summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomasz Wasilczyk <twasilczyk@pidgin.im>2014-06-17 01:10:43 +0200
committerTomasz Wasilczyk <twasilczyk@pidgin.im>2014-06-17 01:10:43 +0200
commitdf6c48952736f861693fb7fb5e55423679ba8efd (patch)
tree41e98805612221d8aa17830d06913b9807834fd1
parent7dbb75ad91fec19ae10cd309302b0e7f599f3d92 (diff)
parenta84cf9d0b0d4739ab8bfadc4828621aa80d4b1ea (diff)
downloadpidgin-df6c48952736f861693fb7fb5e55423679ba8efd.tar.gz
Merge release-2.x.y
-rw-r--r--.hgignore45
-rw-r--r--.tx/config2
-rw-r--r--AUTHORS29
-rw-r--r--COPYRIGHT10
-rw-r--r--ChangeLog68
-rw-r--r--ChangeLog.API510
-rw-r--r--Doxyfile.in1646
-rw-r--r--HACKING2
-rw-r--r--INSTALL2
-rw-r--r--Makefile.am60
-rw-r--r--Makefile.mingw85
-rw-r--r--NEWS6
-rw-r--r--PLUGIN_HOWTO8
-rw-r--r--README5
-rw-r--r--README.hg17
-rw-r--r--README.mingw2
-rwxr-xr-xautogen.sh24
-rw-r--r--config.h.mingw43
-rw-r--r--configure.ac1156
-rw-r--r--doc/Makefile.am33
-rw-r--r--doc/PERL-HOWTO.dox2
-rw-r--r--doc/SIGNAL-HOWTO.dox137
-rw-r--r--doc/TCL-HOWTO.dox4
-rw-r--r--doc/TracFooter.html2
-rw-r--r--doc/TracHeader.html2
-rw-r--r--doc/account-signals.dox252
-rw-r--r--doc/blist-signals.dox154
-rw-r--r--doc/certificate-signals.dox33
-rw-r--r--doc/cipher-signals.dox31
-rw-r--r--doc/cmd-signals.dox29
-rw-r--r--doc/connection-signals.dox80
-rw-r--r--doc/conversation-signals.dox521
-rw-r--r--doc/core-signals.dox32
-rw-r--r--doc/dbus-server-signals.dox34
-rw-r--r--doc/finch.1.in10
-rw-r--r--doc/gtkaccount-signals.dox20
-rw-r--r--doc/gtkblist-signals.dox70
-rw-r--r--doc/gtkconv-signals.dox146
-rw-r--r--doc/gtkimhtml-signals.dox74
-rw-r--r--doc/gtklog-signals.dox22
-rw-r--r--doc/imgstore-signals.dox26
-rw-r--r--doc/jabber-signals.dox138
-rw-r--r--doc/log-signals.dox27
-rw-r--r--doc/notify-signals.dox60
-rw-r--r--doc/pidgin.1.in70
-rw-r--r--doc/plugin-i18n.dox107
-rw-r--r--doc/plugin-ids.dox100
-rw-r--r--doc/plugin-signals.dox31
-rw-r--r--doc/reference/Makefile.am9
-rw-r--r--doc/reference/finch/Makefile.am133
-rw-r--r--doc/reference/finch/finch-docs.xml90
-rw-r--r--doc/reference/finch/version.xml.in1
-rw-r--r--doc/reference/libpurple/Makefile.am179
-rw-r--r--doc/reference/libpurple/libpurple-docs.xml194
-rw-r--r--doc/reference/libpurple/plugin_i18n.xml177
-rw-r--r--doc/reference/libpurple/plugin_ids.xml198
-rw-r--r--doc/reference/libpurple/signals_account.xml521
-rw-r--r--doc/reference/libpurple/signals_blist.xml263
-rw-r--r--doc/reference/libpurple/signals_certificate.xml65
-rw-r--r--doc/reference/libpurple/signals_cmd.xml73
-rw-r--r--doc/reference/libpurple/signals_connection.xml165
-rw-r--r--doc/reference/libpurple/signals_conversation.xml1232
-rw-r--r--doc/reference/libpurple/signals_core.xml68
-rw-r--r--doc/reference/libpurple/signals_dbus_server.xml72
-rw-r--r--doc/reference/libpurple/signals_imgstore.xml44
-rw-r--r--doc/reference/libpurple/signals_jabber.xml311
-rw-r--r--doc/reference/libpurple/signals_log.xml58
-rw-r--r--doc/reference/libpurple/signals_notify.xml133
-rw-r--r--doc/reference/libpurple/signals_plugin.xml63
-rw-r--r--doc/reference/libpurple/signals_savedstatus.xml32
-rw-r--r--doc/reference/libpurple/signals_sound.xml50
-rw-r--r--doc/reference/libpurple/signals_xfer.xml217
-rw-r--r--doc/reference/libpurple/tut_c_plugins.xml (renamed from doc/C-HOWTO.dox)139
-rw-r--r--doc/reference/libpurple/tut_signals.xml203
-rw-r--r--doc/reference/libpurple/ui_ops.xml32
-rw-r--r--doc/reference/libpurple/version.xml.in1
-rw-r--r--doc/reference/pidgin/Makefile.am148
-rw-r--r--doc/reference/pidgin/pidgin-docs.xml91
-rw-r--r--doc/reference/pidgin/signals_gtkaccount.xml41
-rw-r--r--doc/reference/pidgin/signals_gtkblist.xml146
-rw-r--r--doc/reference/pidgin/signals_gtkconv.xml330
-rw-r--r--doc/reference/pidgin/signals_gtklog.xml46
-rw-r--r--doc/reference/pidgin/version.xml.in1
-rw-r--r--doc/savedstatus-signals.dox20
-rw-r--r--doc/sound-signals.dox23
-rw-r--r--doc/ui-ops.dox24
-rw-r--r--doc/xfer-signals.dox114
-rw-r--r--doxy2devhelp.xsl98
-rw-r--r--finch/Makefile.am61
-rw-r--r--finch/finch.c428
-rw-r--r--finch/finch.h22
-rw-r--r--finch/finch.pc.in2
-rw-r--r--finch/finch_winres.rc.in30
-rw-r--r--finch/gntaccount.c114
-rw-r--r--finch/gntaccount.h34
-rw-r--r--finch/gntblist.c632
-rw-r--r--finch/gntblist.h147
-rw-r--r--finch/gntcertmgr.c21
-rw-r--r--finch/gntcertmgr.h12
-rw-r--r--finch/gntconn.c12
-rw-r--r--finch/gntconn.h25
-rw-r--r--finch/gntconv.c566
-rw-r--r--finch/gntconv.h56
-rw-r--r--finch/gntdebug.c10
-rw-r--r--finch/gntdebug.h27
-rw-r--r--finch/gntidle.c1
-rw-r--r--finch/gntidle.h21
-rw-r--r--finch/gntlog.c21
-rw-r--r--finch/gntlog.h49
-rw-r--r--finch/gntmedia.c15
-rw-r--r--finch/gntmedia.h11
-rw-r--r--finch/gntmenuutil.c83
-rw-r--r--finch/gntmenuutil.h49
-rw-r--r--finch/gntnotify.c52
-rw-r--r--finch/gntnotify.h25
-rw-r--r--finch/gntplugin.c151
-rw-r--r--finch/gntplugin.h25
-rw-r--r--finch/gntpounce.c55
-rw-r--r--finch/gntpounce.h37
-rw-r--r--finch/gntprefs.c67
-rw-r--r--finch/gntprefs.h29
-rw-r--r--finch/gntrequest.c209
-rw-r--r--finch/gntrequest.h34
-rw-r--r--finch/gntroomlist.c26
-rw-r--r--finch/gntroomlist.h27
-rw-r--r--finch/gntsound.c84
-rw-r--r--finch/gntsound.h55
-rw-r--r--finch/gntstatus.c20
-rw-r--r--finch/gntstatus.h24
-rw-r--r--finch/gntui.c20
-rw-r--r--finch/gntui.h13
-rw-r--r--finch/gntxfer.c (renamed from finch/gntft.c)58
-rw-r--r--finch/gntxfer.h (renamed from finch/gntft.h)70
-rw-r--r--finch/libfinch.c418
-rw-r--r--finch/libfinch_winres.rc.in30
-rw-r--r--finch/libgnt/Makefile.am17
-rw-r--r--finch/libgnt/configure.ac2
-rw-r--r--finch/libgnt/gnt-skel.c8
-rw-r--r--finch/libgnt/gnt-skel.h25
-rw-r--r--finch/libgnt/gnt.h181
-rw-r--r--finch/libgnt/gntbindable.c16
-rw-r--r--finch/libgnt/gntbindable.h98
-rw-r--r--finch/libgnt/gntbox.c30
-rw-r--r--finch/libgnt/gntbox.h133
-rw-r--r--finch/libgnt/gntbutton.c4
-rw-r--r--finch/libgnt/gntbutton.h35
-rw-r--r--finch/libgnt/gntcheckbox.c4
-rw-r--r--finch/libgnt/gntcheckbox.h40
-rw-r--r--finch/libgnt/gntclipboard.c4
-rw-r--r--finch/libgnt/gntclipboard.h33
-rw-r--r--finch/libgnt/gntcolors.c28
-rw-r--r--finch/libgnt/gntcolors.h58
-rw-r--r--finch/libgnt/gntcombobox.c4
-rw-r--r--finch/libgnt/gntcombobox.h58
-rw-r--r--finch/libgnt/gntentry.c17
-rw-r--r--finch/libgnt/gntentry.h115
-rw-r--r--finch/libgnt/gntfilesel.c145
-rw-r--r--finch/libgnt/gntfilesel.h128
-rw-r--r--finch/libgnt/gntinternal.h6
-rw-r--r--finch/libgnt/gntkeys.c13
-rw-r--r--finch/libgnt/gntkeys.h128
-rw-r--r--finch/libgnt/gntlabel.c8
-rw-r--r--finch/libgnt/gntlabel.h51
-rw-r--r--finch/libgnt/gntline.c6
-rw-r--r--finch/libgnt/gntline.h26
-rw-r--r--finch/libgnt/gntmain.c185
-rw-r--r--finch/libgnt/gntmenu.c48
-rw-r--r--finch/libgnt/gntmenu.h54
-rw-r--r--finch/libgnt/gntmenuitem.c42
-rw-r--r--finch/libgnt/gntmenuitem.h134
-rw-r--r--finch/libgnt/gntmenuitemcheck.c4
-rw-r--r--finch/libgnt/gntmenuitemcheck.h40
-rw-r--r--finch/libgnt/gntprogressbar.c11
-rw-r--r--finch/libgnt/gntprogressbar.h85
-rw-r--r--finch/libgnt/gntslider.c4
-rw-r--r--finch/libgnt/gntslider.h113
-rw-r--r--finch/libgnt/gntstyle.c48
-rw-r--r--finch/libgnt/gntstyle.h118
-rw-r--r--finch/libgnt/gnttextview.c20
-rw-r--r--finch/libgnt/gnttextview.h133
-rw-r--r--finch/libgnt/gnttree.c50
-rw-r--r--finch/libgnt/gnttree.h569
-rw-r--r--finch/libgnt/gntutils.c10
-rw-r--r--finch/libgnt/gntutils.h138
-rw-r--r--finch/libgnt/gntwidget.c12
-rw-r--r--finch/libgnt/gntwidget.h187
-rw-r--r--finch/libgnt/gntwindow.c4
-rw-r--r--finch/libgnt/gntwindow.h68
-rw-r--r--finch/libgnt/gntwm.c55
-rw-r--r--finch/libgnt/gntwm.h202
-rw-r--r--finch/libgnt/gntws.c2
-rw-r--r--finch/libgnt/gntws.h109
-rw-r--r--finch/libgnt/libgnt_winres.rc.in30
-rw-r--r--finch/libgnt/wms/Makefile.am5
-rw-r--r--finch/libgnt/wms/irssi.c9
-rw-r--r--finch/libgnt/wms/s.c11
-rw-r--r--finch/plugins/Makefile.am29
-rw-r--r--finch/plugins/gntclipboard.c7
-rw-r--r--finch/plugins/gntgf.c16
-rw-r--r--finch/plugins/gnthistory.c24
-rw-r--r--finch/plugins/gnttinyurl.c160
-rw-r--r--finch/plugins/grouping.c164
-rw-r--r--finch/plugins/lastlog.c2
-rwxr-xr-xfix-casts.sh18
-rw-r--r--gaim-uninstalled.pc.in14
-rw-r--r--gaim.pc.in14
-rw-r--r--libpurple/Makefile.am195
-rw-r--r--libpurple/Makefile.mingw135
-rw-r--r--libpurple/account.c3476
-rw-r--r--libpurple/account.h1260
-rw-r--r--libpurple/accountopt.c105
-rw-r--r--libpurple/accountopt.h347
-rw-r--r--libpurple/accounts.c1041
-rw-r--r--libpurple/accounts.h254
-rw-r--r--libpurple/blist.c3271
-rw-r--r--libpurple/blist.h1278
-rw-r--r--libpurple/blistnode.c662
-rw-r--r--libpurple/blistnode.h462
-rw-r--r--libpurple/blistnodetypes.c1738
-rw-r--r--libpurple/blistnodetypes.h679
-rw-r--r--libpurple/buddyicon.c555
-rw-r--r--libpurple/buddyicon.h350
-rw-r--r--libpurple/buddylist.c2173
-rw-r--r--libpurple/buddylist.h556
-rw-r--r--libpurple/certificate.c533
-rw-r--r--libpurple/certificate.h858
-rw-r--r--libpurple/cipher.c1004
-rw-r--r--libpurple/cipher.h557
-rw-r--r--libpurple/ciphers/Makefile.am16
-rw-r--r--libpurple/ciphers/aescipher.c725
-rw-r--r--libpurple/ciphers/aescipher.h65
-rw-r--r--libpurple/ciphers/des.c846
-rw-r--r--libpurple/ciphers/des3cipher.c524
-rw-r--r--libpurple/ciphers/des3cipher.h66
-rw-r--r--libpurple/ciphers/descipher.c581
-rw-r--r--libpurple/ciphers/descipher.h67
-rw-r--r--libpurple/ciphers/gchecksum.c144
-rw-r--r--libpurple/ciphers/hmac.c218
-rw-r--r--libpurple/ciphers/hmaccipher.c345
-rw-r--r--libpurple/ciphers/hmaccipher.h67
-rw-r--r--libpurple/ciphers/md4.c297
-rw-r--r--libpurple/ciphers/md4hash.c311
-rw-r--r--libpurple/ciphers/md4hash.h66
-rw-r--r--libpurple/ciphers/md5.c326
-rw-r--r--libpurple/ciphers/md5hash.c191
-rw-r--r--libpurple/ciphers/md5hash.h65
-rw-r--r--libpurple/ciphers/pbkdf2cipher.c420
-rw-r--r--libpurple/ciphers/pbkdf2cipher.h68
-rw-r--r--libpurple/ciphers/rc4.c192
-rw-r--r--libpurple/ciphers/rc4cipher.c262
-rw-r--r--libpurple/ciphers/rc4cipher.h70
-rw-r--r--libpurple/ciphers/sha1.c281
-rw-r--r--libpurple/ciphers/sha1hash.c191
-rw-r--r--libpurple/ciphers/sha1hash.h65
-rw-r--r--libpurple/ciphers/sha256.c283
-rw-r--r--libpurple/ciphers/sha256hash.c191
-rw-r--r--libpurple/ciphers/sha256hash.h65
-rw-r--r--libpurple/circbuffer.c153
-rw-r--r--libpurple/circbuffer.h118
-rw-r--r--libpurple/circularbuffer.c476
-rw-r--r--libpurple/circularbuffer.h172
-rw-r--r--libpurple/cmds.c38
-rw-r--r--libpurple/cmds.h269
-rw-r--r--libpurple/connection.c1049
-rw-r--r--libpurple/connection.h695
-rw-r--r--libpurple/conversation.c2714
-rw-r--r--libpurple/conversation.h1593
-rw-r--r--libpurple/conversations.c468
-rw-r--r--libpurple/conversations.h175
-rw-r--r--libpurple/conversationtypes.c1979
-rw-r--r--libpurple/conversationtypes.h791
-rw-r--r--libpurple/core.c509
-rw-r--r--libpurple/core.h204
-rw-r--r--libpurple/dbus-analyze-functions.py33
-rw-r--r--libpurple/dbus-bindings.h37
-rw-r--r--libpurple/dbus-define-api.h17
-rw-r--r--libpurple/dbus-maybe.h17
-rw-r--r--libpurple/dbus-purple.h12
-rw-r--r--libpurple/dbus-server.c132
-rw-r--r--libpurple/dbus-server.h174
-rw-r--r--libpurple/dbus-useful.c3
-rw-r--r--libpurple/dbus-useful.h16
-rw-r--r--libpurple/debug.c73
-rw-r--r--libpurple/debug.h184
-rw-r--r--libpurple/desktopitem.c11
-rw-r--r--libpurple/desktopitem.h46
-rw-r--r--libpurple/dnsquery.c100
-rw-r--r--libpurple/dnsquery.h131
-rw-r--r--libpurple/dnssrv.c137
-rw-r--r--libpurple/dnssrv.h190
-rw-r--r--libpurple/e2ee.c252
-rw-r--r--libpurple/e2ee.h259
-rw-r--r--libpurple/enums.c.in64
-rw-r--r--libpurple/enums.h.in53
-rw-r--r--libpurple/eventloop.c45
-rw-r--r--libpurple/eventloop.h310
-rw-r--r--libpurple/example/Makefile.am4
-rw-r--r--libpurple/example/defines.h23
-rw-r--r--libpurple/example/nullclient.c92
-rw-r--r--libpurple/ft.c1787
-rw-r--r--libpurple/ft.h785
-rw-r--r--libpurple/gaim-compat.h2317
-rw-r--r--libpurple/gconf/Makefile.am7
-rw-r--r--libpurple/glibcompat.h181
-rw-r--r--libpurple/http.c3219
-rw-r--r--libpurple/http.h959
-rw-r--r--libpurple/idle.c35
-rw-r--r--libpurple/idle.h66
-rw-r--r--libpurple/image-store.c233
-rw-r--r--libpurple/image-store.h166
-rw-r--r--libpurple/image.c622
-rw-r--r--libpurple/image.h305
-rw-r--r--libpurple/imgstore.c231
-rw-r--r--libpurple/imgstore.h230
-rw-r--r--libpurple/internal.h282
-rw-r--r--libpurple/keyring.c1382
-rw-r--r--libpurple/keyring.h647
-rw-r--r--libpurple/log.c111
-rw-r--r--libpurple/log.h473
-rw-r--r--libpurple/marshallers.list1
-rw-r--r--libpurple/media-gst.h164
-rw-r--r--libpurple/media.c47
-rw-r--r--libpurple/media.h406
-rw-r--r--libpurple/media/backend-fs2.c280
-rw-r--r--libpurple/media/backend-fs2.h27
-rw-r--r--libpurple/media/backend-iface.c11
-rw-r--r--libpurple/media/backend-iface.h156
-rw-r--r--libpurple/media/candidate.c29
-rw-r--r--libpurple/media/candidate.h179
-rw-r--r--libpurple/media/codec.c52
-rw-r--r--libpurple/media/codec.h150
-rw-r--r--libpurple/media/enum-types.c5
-rw-r--r--libpurple/media/enum-types.h89
-rw-r--r--libpurple/mediamanager.c107
-rw-r--r--libpurple/mediamanager.h207
-rw-r--r--libpurple/memorypool.c383
-rw-r--r--libpurple/memorypool.h201
-rw-r--r--libpurple/message.c407
-rw-r--r--libpurple/message.h276
-rw-r--r--libpurple/mime.c4
-rw-r--r--libpurple/mime.h144
-rw-r--r--libpurple/nat-pmp.c5
-rw-r--r--libpurple/nat-pmp.h46
-rw-r--r--libpurple/network.c212
-rw-r--r--libpurple/network.h276
-rw-r--r--libpurple/notify.c236
-rw-r--r--libpurple/notify.h741
-rw-r--r--libpurple/ntlm.c94
-rw-r--r--libpurple/ntlm.h53
-rw-r--r--libpurple/plugin.c145
-rw-r--r--libpurple/plugin.h562
-rw-r--r--libpurple/pluginpref.c10
-rw-r--r--libpurple/pluginpref.h211
-rw-r--r--libpurple/plugins/Makefile.am102
-rw-r--r--libpurple/plugins/Makefile.mingw23
-rw-r--r--libpurple/plugins/autoaccept.c57
-rw-r--r--libpurple/plugins/buddynote.c5
-rw-r--r--libpurple/plugins/ciphertest.c394
-rw-r--r--libpurple/plugins/codeinline.c35
-rw-r--r--libpurple/plugins/dbus-example.c17
-rw-r--r--libpurple/plugins/debug_example.c39
-rw-r--r--libpurple/plugins/filectl.c8
-rw-r--r--libpurple/plugins/fortuneprofile.pl2
-rw-r--r--libpurple/plugins/helloworld.c45
-rw-r--r--libpurple/plugins/idle.c16
-rw-r--r--libpurple/plugins/joinpart.c29
-rw-r--r--libpurple/plugins/keyrings/Makefile.am90
-rw-r--r--libpurple/plugins/keyrings/Makefile.mingw82
-rw-r--r--libpurple/plugins/keyrings/gnomekeyring.c475
-rw-r--r--libpurple/plugins/keyrings/internalkeyring.c1055
-rw-r--r--libpurple/plugins/keyrings/kwallet.cpp584
-rw-r--r--libpurple/plugins/keyrings/secretservice.c339
-rw-r--r--libpurple/plugins/keyrings/wincred.c320
-rw-r--r--libpurple/plugins/log_reader.c160
-rw-r--r--libpurple/plugins/mono/Makefile.am2
-rw-r--r--libpurple/plugins/mono/api/Makefile.am2
-rw-r--r--libpurple/plugins/mono/loader/Makefile.am4
-rw-r--r--libpurple/plugins/mono/loader/blist-glue.c4
-rw-r--r--libpurple/plugins/mono/loader/mono-helper.c12
-rw-r--r--libpurple/plugins/mono/loader/mono-helper.h5
-rw-r--r--libpurple/plugins/mono/loader/signal-glue.c38
-rw-r--r--libpurple/plugins/newline.c136
-rw-r--r--libpurple/plugins/notify_example.c56
-rw-r--r--libpurple/plugins/offlinemsg.c53
-rw-r--r--libpurple/plugins/one_time_password.c2
-rw-r--r--libpurple/plugins/perl/Makefile.am24
-rw-r--r--[-rwxr-xr-x]libpurple/plugins/perl/Makefile.mingw10
-rw-r--r--libpurple/plugins/perl/common/Account.xs155
-rw-r--r--libpurple/plugins/perl/common/AccountOpts.xs6
-rw-r--r--libpurple/plugins/perl/common/BuddyIcon.xs4
-rw-r--r--libpurple/plugins/perl/common/BuddyList.xs247
-rw-r--r--libpurple/plugins/perl/common/Certificate.xs22
-rw-r--r--libpurple/plugins/perl/common/Cipher.xs422
-rw-r--r--libpurple/plugins/perl/common/Connection.xs26
-rw-r--r--libpurple/plugins/perl/common/Conversation.xs358
-rw-r--r--libpurple/plugins/perl/common/ImgStore.xs53
-rw-r--r--libpurple/plugins/perl/common/MANIFEST6
-rw-r--r--libpurple/plugins/perl/common/Makefile.PL.in8
-rw-r--r--libpurple/plugins/perl/common/Makefile.mingw21
-rw-r--r--libpurple/plugins/perl/common/Network.xs17
-rw-r--r--libpurple/plugins/perl/common/Notify.xs10
-rw-r--r--libpurple/plugins/perl/common/PluginPref.xs6
-rw-r--r--libpurple/plugins/perl/common/Pounce.xs3
-rw-r--r--libpurple/plugins/perl/common/Prefs.xs10
-rw-r--r--libpurple/plugins/perl/common/Presence.xs102
-rw-r--r--libpurple/plugins/perl/common/Privacy.xs33
-rw-r--r--libpurple/plugins/perl/common/Proxy.xs4
-rw-r--r--libpurple/plugins/perl/common/Prpl.xs2
-rw-r--r--libpurple/plugins/perl/common/Purple.pm8
-rw-r--r--libpurple/plugins/perl/common/Purple.xs10
-rw-r--r--libpurple/plugins/perl/common/Request.xs74
-rw-r--r--libpurple/plugins/perl/common/Roomlist.xs8
-rw-r--r--libpurple/plugins/perl/common/SSLConn.xs7
-rw-r--r--libpurple/plugins/perl/common/SavedStatuses.xs6
-rw-r--r--libpurple/plugins/perl/common/Server.xs79
-rw-r--r--libpurple/plugins/perl/common/Smiley.xs80
-rw-r--r--libpurple/plugins/perl/common/Status.xs206
-rw-r--r--libpurple/plugins/perl/common/Util.xs98
-rw-r--r--libpurple/plugins/perl/common/Whiteboard.xs6
-rw-r--r--libpurple/plugins/perl/common/XMLNode.xs52
-rw-r--r--libpurple/plugins/perl/common/Xfer.xs (renamed from libpurple/plugins/perl/common/FT.xs)16
-rw-r--r--libpurple/plugins/perl/common/module.h78
-rw-r--r--libpurple/plugins/perl/common/typemap39
-rw-r--r--libpurple/plugins/perl/perl-common.c234
-rw-r--r--libpurple/plugins/perl/perl-common.h15
-rw-r--r--libpurple/plugins/perl/perl-handlers.c132
-rw-r--r--libpurple/plugins/perl/perl-handlers.h10
-rw-r--r--libpurple/plugins/perl/perl.c60
-rw-r--r--libpurple/plugins/perl/scripts/account.pl33
-rw-r--r--libpurple/plugins/perl/scripts/count_down.pl2
-rw-r--r--libpurple/plugins/perl/scripts/gtk_frame_test.pl2
-rw-r--r--libpurple/plugins/perl/scripts/signals-test.pl2
-rw-r--r--libpurple/plugins/pluginpref_example.c25
-rw-r--r--libpurple/plugins/psychic.c29
-rw-r--r--libpurple/plugins/signals-test.c207
-rw-r--r--libpurple/plugins/ssl/Makefile.am40
-rw-r--r--libpurple/plugins/ssl/Makefile.mingw40
-rw-r--r--libpurple/plugins/ssl/ssl-gnutls.c118
-rw-r--r--libpurple/plugins/ssl/ssl-nss.c127
-rw-r--r--libpurple/plugins/statenotify.c18
-rw-r--r--libpurple/plugins/tcl/Makefile.am6
-rw-r--r--libpurple/plugins/tcl/Makefile.mingw7
-rw-r--r--libpurple/plugins/tcl/signal-test.tcl2
-rw-r--r--libpurple/plugins/tcl/tcl.c113
-rw-r--r--libpurple/plugins/tcl/tcl_cmds.c198
-rw-r--r--libpurple/plugins/tcl/tcl_glib.c3
-rw-r--r--libpurple/plugins/tcl/tcl_purple.h7
-rw-r--r--libpurple/plugins/tcl/tcl_ref.c4
-rw-r--r--libpurple/plugins/tcl/tcl_signals.c185
-rw-r--r--libpurple/plugins/test.pl2
-rw-r--r--libpurple/pounce.c128
-rw-r--r--libpurple/pounce.h330
-rw-r--r--libpurple/prefs.c110
-rw-r--r--libpurple/prefs.h294
-rw-r--r--libpurple/presence.c1000
-rw-r--r--libpurple/presence.h416
-rw-r--r--libpurple/privacy.c411
-rw-r--r--libpurple/privacy.h194
-rw-r--r--libpurple/protocols/Makefile.am2
-rw-r--r--libpurple/protocols/Makefile.mingw8
-rw-r--r--libpurple/protocols/bonjour/Makefile.am25
-rw-r--r--libpurple/protocols/bonjour/Makefile.mingw1
-rw-r--r--libpurple/protocols/bonjour/bonjour.c136
-rw-r--r--libpurple/protocols/bonjour/bonjour_ft.c364
-rw-r--r--libpurple/protocols/bonjour/bonjour_ft.h6
-rw-r--r--libpurple/protocols/bonjour/buddy.c33
-rw-r--r--libpurple/protocols/bonjour/dns_sd_proxy.c2
-rw-r--r--libpurple/protocols/bonjour/dns_sd_proxy.h100
-rw-r--r--libpurple/protocols/bonjour/jabber.c289
-rw-r--r--libpurple/protocols/bonjour/jabber.h10
-rw-r--r--libpurple/protocols/bonjour/mdns_avahi.c8
-rw-r--r--libpurple/protocols/bonjour/mdns_common.c22
-rw-r--r--libpurple/protocols/bonjour/mdns_types.h2
-rw-r--r--libpurple/protocols/bonjour/mdns_win32.c19
-rw-r--r--libpurple/protocols/bonjour/parser.c18
-rw-r--r--libpurple/protocols/gg/Makefile.am124
-rw-r--r--libpurple/protocols/gg/Makefile.mingw73
-rw-r--r--libpurple/protocols/gg/account.c645
-rw-r--r--libpurple/protocols/gg/account.h62
-rw-r--r--libpurple/protocols/gg/avatar.c396
-rw-r--r--libpurple/protocols/gg/avatar.h53
-rw-r--r--libpurple/protocols/gg/blist.c (renamed from libpurple/protocols/gg/buddylist.c)48
-rw-r--r--libpurple/protocols/gg/blist.h (renamed from libpurple/protocols/gg/buddylist.h)19
-rw-r--r--libpurple/protocols/gg/chat.c623
-rw-r--r--libpurple/protocols/gg/chat.h60
-rw-r--r--libpurple/protocols/gg/confer.c170
-rw-r--r--libpurple/protocols/gg/confer.h93
-rw-r--r--libpurple/protocols/gg/deprecated.c67
-rw-r--r--libpurple/protocols/gg/deprecated.h (renamed from libpurple/protocols/mxit/cipher.h)31
-rw-r--r--libpurple/protocols/gg/edisc.c1225
-rw-r--r--libpurple/protocols/gg/edisc.h49
-rw-r--r--libpurple/protocols/gg/gg-utils.c147
-rw-r--r--libpurple/protocols/gg/gg-utils.h106
-rw-r--r--libpurple/protocols/gg/gg.c2413
-rw-r--r--libpurple/protocols/gg/gg.h71
-rw-r--r--libpurple/protocols/gg/html.c181
-rw-r--r--libpurple/protocols/gg/html.h59
-rw-r--r--libpurple/protocols/gg/image-prpl.c259
-rw-r--r--libpurple/protocols/gg/image-prpl.h68
-rw-r--r--libpurple/protocols/gg/keymapper.c106
-rw-r--r--libpurple/protocols/gg/keymapper.h48
-rw-r--r--libpurple/protocols/gg/lib/config.h2
-rw-r--r--libpurple/protocols/gg/libgadu-events.c124
-rw-r--r--libpurple/protocols/gg/libgadu-events.h45
-rw-r--r--libpurple/protocols/gg/libgaduw.c209
-rw-r--r--libpurple/protocols/gg/libgaduw.h62
-rw-r--r--libpurple/protocols/gg/message-prpl.c686
-rw-r--r--libpurple/protocols/gg/message-prpl.h48
-rw-r--r--libpurple/protocols/gg/multilogon.c255
-rw-r--r--libpurple/protocols/gg/multilogon.h46
-rw-r--r--libpurple/protocols/gg/oauth/oauth-parameter.c159
-rw-r--r--libpurple/protocols/gg/oauth/oauth-parameter.h37
-rw-r--r--libpurple/protocols/gg/oauth/oauth-purple.c264
-rw-r--r--libpurple/protocols/gg/oauth/oauth-purple.h42
-rw-r--r--libpurple/protocols/gg/oauth/oauth.c155
-rw-r--r--libpurple/protocols/gg/oauth/oauth.h (renamed from libpurple/protocols/mxit/http.h)41
-rw-r--r--libpurple/protocols/gg/pubdir-prpl.c967
-rw-r--r--libpurple/protocols/gg/pubdir-prpl.h73
-rw-r--r--libpurple/protocols/gg/purplew.c152
-rw-r--r--libpurple/protocols/gg/purplew.h76
-rw-r--r--libpurple/protocols/gg/resolver-purple.c186
-rw-r--r--libpurple/protocols/gg/resolver-purple.h38
-rw-r--r--libpurple/protocols/gg/roster.c1092
-rw-r--r--libpurple/protocols/gg/roster.h73
-rw-r--r--libpurple/protocols/gg/search.c223
-rw-r--r--libpurple/protocols/gg/search.h152
-rw-r--r--libpurple/protocols/gg/servconn.c105
-rw-r--r--libpurple/protocols/gg/servconn.h43
-rw-r--r--libpurple/protocols/gg/status.c471
-rw-r--r--libpurple/protocols/gg/status.h65
-rw-r--r--libpurple/protocols/gg/tcpsocket.c147
-rw-r--r--libpurple/protocols/gg/tcpsocket.h40
-rw-r--r--libpurple/protocols/gg/utils.c274
-rw-r--r--libpurple/protocols/gg/utils.h111
-rw-r--r--libpurple/protocols/gg/validator.c98
-rw-r--r--libpurple/protocols/gg/validator.h48
-rw-r--r--libpurple/protocols/gg/xml.c157
-rw-r--r--libpurple/protocols/gg/xml.h48
-rw-r--r--libpurple/protocols/irc/Makefile.am6
-rw-r--r--libpurple/protocols/irc/Makefile.mingw1
-rw-r--r--libpurple/protocols/irc/cmds.c76
-rw-r--r--libpurple/protocols/irc/dcc_send.c87
-rw-r--r--libpurple/protocols/irc/irc.c177
-rw-r--r--libpurple/protocols/irc/irc.h9
-rw-r--r--libpurple/protocols/irc/msgs.c450
-rw-r--r--libpurple/protocols/irc/parse.c14
-rw-r--r--libpurple/protocols/jabber/Makefile.am46
-rw-r--r--libpurple/protocols/jabber/Makefile.mingw36
-rw-r--r--libpurple/protocols/jabber/adhoccommands.c117
-rw-r--r--libpurple/protocols/jabber/adhoccommands.h4
-rw-r--r--libpurple/protocols/jabber/auth.c185
-rw-r--r--libpurple/protocols/jabber/auth.h16
-rw-r--r--libpurple/protocols/jabber/auth_cyrus.c98
-rw-r--r--libpurple/protocols/jabber/auth_digest_md5.c58
-rw-r--r--libpurple/protocols/jabber/auth_plain.c26
-rw-r--r--libpurple/protocols/jabber/auth_scram.c109
-rw-r--r--libpurple/protocols/jabber/auth_scram.h4
-rw-r--r--libpurple/protocols/jabber/bosh.c1247
-rw-r--r--libpurple/protocols/jabber/bosh.h35
-rw-r--r--libpurple/protocols/jabber/buddy.c482
-rw-r--r--libpurple/protocols/jabber/buddy.h2
-rw-r--r--libpurple/protocols/jabber/caps.c230
-rw-r--r--libpurple/protocols/jabber/caps.h7
-rw-r--r--libpurple/protocols/jabber/chat.c372
-rw-r--r--libpurple/protocols/jabber/chat.h8
-rw-r--r--libpurple/protocols/jabber/data.c78
-rw-r--r--libpurple/protocols/jabber/data.h10
-rw-r--r--libpurple/protocols/jabber/disco.c184
-rw-r--r--libpurple/protocols/jabber/disco.h4
-rw-r--r--libpurple/protocols/jabber/facebook_roster.c57
-rw-r--r--libpurple/protocols/jabber/facebook_roster.h4
-rw-r--r--libpurple/protocols/jabber/google/gmail.c78
-rw-r--r--libpurple/protocols/jabber/google/gmail.h2
-rw-r--r--libpurple/protocols/jabber/google/google.c2
-rw-r--r--libpurple/protocols/jabber/google/google_p2p.c452
-rw-r--r--libpurple/protocols/jabber/google/google_p2p.h102
-rw-r--r--libpurple/protocols/jabber/google/google_presence.c4
-rw-r--r--libpurple/protocols/jabber/google/google_roster.c112
-rw-r--r--libpurple/protocols/jabber/google/google_roster.h4
-rw-r--r--libpurple/protocols/jabber/google/google_session.c196
-rw-r--r--libpurple/protocols/jabber/google/google_session.h4
-rw-r--r--libpurple/protocols/jabber/google/jingleinfo.c46
-rw-r--r--libpurple/protocols/jabber/google/jingleinfo.h2
-rw-r--r--libpurple/protocols/jabber/google/relay.c72
-rw-r--r--libpurple/protocols/jabber/ibb.c80
-rw-r--r--libpurple/protocols/jabber/ibb.h8
-rw-r--r--libpurple/protocols/jabber/iq.c128
-rw-r--r--libpurple/protocols/jabber/iq.h8
-rw-r--r--libpurple/protocols/jabber/jabber.c1194
-rw-r--r--libpurple/protocols/jabber/jabber.h48
-rw-r--r--libpurple/protocols/jabber/jingle/content.c107
-rw-r--r--libpurple/protocols/jabber/jingle/content.h20
-rw-r--r--libpurple/protocols/jabber/jingle/iceudp.c204
-rw-r--r--libpurple/protocols/jabber/jingle/iceudp.h20
-rw-r--r--libpurple/protocols/jabber/jingle/jingle.c153
-rw-r--r--libpurple/protocols/jabber/jingle/jingle.h10
-rw-r--r--libpurple/protocols/jabber/jingle/rawudp.c120
-rw-r--r--libpurple/protocols/jabber/jingle/rawudp.h14
-rw-r--r--libpurple/protocols/jabber/jingle/rtp.c280
-rw-r--r--libpurple/protocols/jabber/jingle/rtp.h8
-rw-r--r--libpurple/protocols/jabber/jingle/session.c121
-rw-r--r--libpurple/protocols/jabber/jingle/session.h14
-rw-r--r--libpurple/protocols/jabber/jingle/transport.c54
-rw-r--r--libpurple/protocols/jabber/jingle/transport.h20
-rw-r--r--libpurple/protocols/jabber/jutil.c44
-rw-r--r--libpurple/protocols/jabber/libfacebook.c254
-rw-r--r--libpurple/protocols/jabber/libgtalk.c334
-rw-r--r--libpurple/protocols/jabber/libxmpp.c18
-rw-r--r--libpurple/protocols/jabber/message.c654
-rw-r--r--libpurple/protocols/jabber/message.h11
-rw-r--r--libpurple/protocols/jabber/namespaces.h5
-rw-r--r--libpurple/protocols/jabber/oob.c240
-rw-r--r--libpurple/protocols/jabber/oob.h2
-rw-r--r--libpurple/protocols/jabber/parser.c28
-rw-r--r--libpurple/protocols/jabber/pep.c56
-rw-r--r--libpurple/protocols/jabber/pep.h4
-rw-r--r--libpurple/protocols/jabber/ping.c24
-rw-r--r--libpurple/protocols/jabber/ping.h2
-rw-r--r--libpurple/protocols/jabber/presence.c315
-rw-r--r--libpurple/protocols/jabber/presence.h12
-rw-r--r--libpurple/protocols/jabber/roster.c136
-rw-r--r--libpurple/protocols/jabber/roster.h4
-rw-r--r--libpurple/protocols/jabber/si.c569
-rw-r--r--libpurple/protocols/jabber/si.h6
-rw-r--r--libpurple/protocols/jabber/useravatar.c188
-rw-r--r--libpurple/protocols/jabber/useravatar.h4
-rw-r--r--libpurple/protocols/jabber/usermood.c89
-rw-r--r--libpurple/protocols/jabber/usermood.h17
-rw-r--r--libpurple/protocols/jabber/usernick.c36
-rw-r--r--libpurple/protocols/jabber/usertune.c50
-rw-r--r--libpurple/protocols/jabber/xdata.c145
-rw-r--r--libpurple/protocols/jabber/xdata.h12
-rw-r--r--libpurple/protocols/msn/Makefile.am10
-rw-r--r--libpurple/protocols/msn/Makefile.mingw3
-rw-r--r--libpurple/protocols/msn/command.c2
-rw-r--r--libpurple/protocols/msn/command.h2
-rw-r--r--libpurple/protocols/msn/contact.c547
-rw-r--r--libpurple/protocols/msn/contact.h4
-rw-r--r--libpurple/protocols/msn/directconn.c29
-rw-r--r--libpurple/protocols/msn/directconn.h2
-rw-r--r--libpurple/protocols/msn/error.c16
-rw-r--r--libpurple/protocols/msn/ft.c (renamed from libpurple/protocols/msn/xfer.c)61
-rw-r--r--libpurple/protocols/msn/ft.h (renamed from libpurple/protocols/msn/xfer.h)16
-rw-r--r--libpurple/protocols/msn/group.h2
-rw-r--r--libpurple/protocols/msn/history.c2
-rw-r--r--libpurple/protocols/msn/httpconn.c870
-rw-r--r--libpurple/protocols/msn/httpconn.h83
-rw-r--r--libpurple/protocols/msn/msg.c204
-rw-r--r--libpurple/protocols/msn/msn.c503
-rw-r--r--libpurple/protocols/msn/msnutils.c70
-rw-r--r--libpurple/protocols/msn/msnutils.h2
-rw-r--r--libpurple/protocols/msn/nexus.c214
-rw-r--r--libpurple/protocols/msn/nexus.h2
-rw-r--r--libpurple/protocols/msn/notification.c345
-rw-r--r--libpurple/protocols/msn/object.c50
-rw-r--r--libpurple/protocols/msn/object.h15
-rw-r--r--libpurple/protocols/msn/oim.c125
-rw-r--r--libpurple/protocols/msn/p2p.c16
-rw-r--r--libpurple/protocols/msn/sbconn.c2
-rw-r--r--libpurple/protocols/msn/servconn.c32
-rw-r--r--libpurple/protocols/msn/servconn.h4
-rw-r--r--libpurple/protocols/msn/session.c54
-rw-r--r--libpurple/protocols/msn/session.h8
-rw-r--r--libpurple/protocols/msn/slp.c61
-rw-r--r--libpurple/protocols/msn/slp.h2
-rw-r--r--libpurple/protocols/msn/slpcall.c110
-rw-r--r--libpurple/protocols/msn/slplink.c18
-rw-r--r--libpurple/protocols/msn/slpmsg.c20
-rw-r--r--libpurple/protocols/msn/slpmsg.h11
-rw-r--r--libpurple/protocols/msn/slpmsg_part.c2
-rw-r--r--libpurple/protocols/msn/soap.c759
-rw-r--r--libpurple/protocols/msn/soap.h35
-rw-r--r--libpurple/protocols/msn/state.c46
-rw-r--r--libpurple/protocols/msn/state.h4
-rw-r--r--libpurple/protocols/msn/switchboard.c60
-rw-r--r--libpurple/protocols/msn/tlv.c4
-rw-r--r--libpurple/protocols/msn/tlv.h2
-rw-r--r--libpurple/protocols/msn/transaction.c2
-rw-r--r--libpurple/protocols/msn/transaction.h2
-rw-r--r--libpurple/protocols/msn/user.c32
-rw-r--r--libpurple/protocols/msn/user.h5
-rw-r--r--libpurple/protocols/msn/userlist.c72
-rw-r--r--libpurple/protocols/mxit/Makefile.am14
-rw-r--r--libpurple/protocols/mxit/Makefile.mingw5
-rw-r--r--libpurple/protocols/mxit/actions.c33
-rw-r--r--libpurple/protocols/mxit/aes.c405
-rw-r--r--libpurple/protocols/mxit/aes.h39
-rw-r--r--libpurple/protocols/mxit/chunk.c22
-rw-r--r--libpurple/protocols/mxit/chunk.h3
-rw-r--r--libpurple/protocols/mxit/cipher-mxit.c83
-rw-r--r--libpurple/protocols/mxit/cipher-mxit.h (renamed from libpurple/protocols/myspace/markup.h)22
-rw-r--r--libpurple/protocols/mxit/cipher.c245
-rw-r--r--libpurple/protocols/mxit/filexfer.c46
-rw-r--r--libpurple/protocols/mxit/formcmds.c55
-rw-r--r--libpurple/protocols/mxit/http.c341
-rw-r--r--libpurple/protocols/mxit/login.c236
-rw-r--r--libpurple/protocols/mxit/markup.c129
-rw-r--r--libpurple/protocols/mxit/multimx.c76
-rw-r--r--libpurple/protocols/mxit/multimx.h2
-rw-r--r--libpurple/protocols/mxit/mxit.c84
-rw-r--r--libpurple/protocols/mxit/mxit.h8
-rw-r--r--libpurple/protocols/mxit/profile.c66
-rw-r--r--libpurple/protocols/mxit/protocol.c228
-rw-r--r--libpurple/protocols/mxit/roster.c42
-rw-r--r--libpurple/protocols/mxit/roster.h3
-rw-r--r--libpurple/protocols/mxit/splashscreen.c21
-rw-r--r--libpurple/protocols/mxit/voicevideo.c6
-rw-r--r--libpurple/protocols/myspace/Makefile.am44
-rw-r--r--libpurple/protocols/myspace/Makefile.mingw81
-rw-r--r--libpurple/protocols/myspace/README27
-rw-r--r--libpurple/protocols/myspace/markup.c763
-rw-r--r--libpurple/protocols/myspace/message.c1413
-rw-r--r--libpurple/protocols/myspace/message.h109
-rw-r--r--libpurple/protocols/myspace/myspace.c3667
-rw-r--r--libpurple/protocols/myspace/myspace.h203
-rw-r--r--libpurple/protocols/myspace/persist.h92
-rw-r--r--libpurple/protocols/myspace/session.c98
-rw-r--r--libpurple/protocols/myspace/session.h60
-rw-r--r--libpurple/protocols/myspace/user.c892
-rw-r--r--libpurple/protocols/myspace/user.h60
-rw-r--r--libpurple/protocols/myspace/zap.c245
-rw-r--r--libpurple/protocols/myspace/zap.h28
-rw-r--r--libpurple/protocols/novell/Makefile.am6
-rw-r--r--libpurple/protocols/novell/Makefile.mingw1
-rw-r--r--libpurple/protocols/novell/novell.c453
-rw-r--r--libpurple/protocols/null/Makefile.am6
-rw-r--r--libpurple/protocols/null/Makefile.mingw1
-rw-r--r--libpurple/protocols/null/nullprpl.c433
-rw-r--r--libpurple/protocols/oscar/Makefile.am21
-rw-r--r--libpurple/protocols/oscar/Makefile.mingw1
-rw-r--r--libpurple/protocols/oscar/authorization.c27
-rw-r--r--libpurple/protocols/oscar/bstream.c7
-rw-r--r--libpurple/protocols/oscar/clientlogin.c240
-rw-r--r--libpurple/protocols/oscar/family_auth.c71
-rw-r--r--libpurple/protocols/oscar/family_bart.c6
-rw-r--r--libpurple/protocols/oscar/family_buddy.c2
-rw-r--r--libpurple/protocols/oscar/family_chat.c2
-rw-r--r--libpurple/protocols/oscar/family_chatnav.c4
-rw-r--r--libpurple/protocols/oscar/family_feedbag.c391
-rw-r--r--libpurple/protocols/oscar/family_icbm.c26
-rw-r--r--libpurple/protocols/oscar/family_icq.c13
-rw-r--r--libpurple/protocols/oscar/family_locate.c3
-rw-r--r--libpurple/protocols/oscar/family_oservice.c162
-rw-r--r--libpurple/protocols/oscar/flap_connection.c29
-rw-r--r--libpurple/protocols/oscar/libaim.c11
-rw-r--r--libpurple/protocols/oscar/libicq.c19
-rw-r--r--libpurple/protocols/oscar/odc.c77
-rw-r--r--libpurple/protocols/oscar/oft.c46
-rw-r--r--libpurple/protocols/oscar/oscar.c822
-rw-r--r--libpurple/protocols/oscar/oscar.h61
-rw-r--r--libpurple/protocols/oscar/oscar_data.c15
-rw-r--r--libpurple/protocols/oscar/oscarcommon.h9
-rw-r--r--libpurple/protocols/oscar/peer.c115
-rw-r--r--libpurple/protocols/oscar/peer.h6
-rw-r--r--libpurple/protocols/oscar/tlv.c6
-rw-r--r--libpurple/protocols/oscar/userinfo.c89
-rw-r--r--libpurple/protocols/oscar/util.c6
-rw-r--r--libpurple/protocols/oscar/visibility.c6
-rw-r--r--libpurple/protocols/sametime/Makefile.am6
-rw-r--r--libpurple/protocols/sametime/Makefile.mingw3
-rw-r--r--libpurple/protocols/sametime/sametime.c730
-rw-r--r--libpurple/protocols/silc/Makefile.am8
-rw-r--r--libpurple/protocols/silc/Makefile.mingw4
-rw-r--r--libpurple/protocols/silc/buddy.c197
-rw-r--r--libpurple/protocols/silc/chat.c136
-rw-r--r--libpurple/protocols/silc/ft.c94
-rw-r--r--libpurple/protocols/silc/ops.c442
-rw-r--r--libpurple/protocols/silc/pk.c14
-rw-r--r--libpurple/protocols/silc/silc.c282
-rw-r--r--libpurple/protocols/silc/silcpurple.h9
-rw-r--r--libpurple/protocols/silc/util.c79
-rw-r--r--libpurple/protocols/silc/wb.c79
-rw-r--r--libpurple/protocols/silc10/Makefile.am36
-rw-r--r--libpurple/protocols/silc10/Makefile.mingw93
-rw-r--r--libpurple/protocols/silc10/README31
-rw-r--r--libpurple/protocols/silc10/TODO8
-rw-r--r--libpurple/protocols/silc10/buddy.c1748
-rw-r--r--libpurple/protocols/silc10/chat.c1456
-rw-r--r--libpurple/protocols/silc10/ft.c412
-rw-r--r--libpurple/protocols/silc10/ops.c2063
-rw-r--r--libpurple/protocols/silc10/pk.c274
-rw-r--r--libpurple/protocols/silc10/silc.c1965
-rw-r--r--libpurple/protocols/silc10/silcpurple.h173
-rw-r--r--libpurple/protocols/silc10/util.c776
-rw-r--r--libpurple/protocols/silc10/wb.c520
-rw-r--r--libpurple/protocols/silc10/wb.h49
-rw-r--r--libpurple/protocols/simple/Makefile.am6
-rw-r--r--libpurple/protocols/simple/Makefile.mingw1
-rw-r--r--libpurple/protocols/simple/simple.c230
-rw-r--r--libpurple/protocols/simple/simple.h4
-rw-r--r--libpurple/protocols/simple/sipmsg.c2
-rw-r--r--libpurple/protocols/yahoo/Makefile.am19
-rw-r--r--libpurple/protocols/yahoo/Makefile.mingw1
-rw-r--r--libpurple/protocols/yahoo/libyahoo.c30
-rw-r--r--libpurple/protocols/yahoo/libyahoojp.c14
-rw-r--r--libpurple/protocols/yahoo/libymsg.c1442
-rw-r--r--libpurple/protocols/yahoo/libymsg.h41
-rw-r--r--libpurple/protocols/yahoo/util.c92
-rw-r--r--libpurple/protocols/yahoo/yahoo_aliases.c307
-rw-r--r--libpurple/protocols/yahoo/yahoo_aliases.h2
-rw-r--r--libpurple/protocols/yahoo/yahoo_doodle.c72
-rw-r--r--libpurple/protocols/yahoo/yahoo_filexfer.c1539
-rw-r--r--libpurple/protocols/yahoo/yahoo_filexfer.h7
-rw-r--r--libpurple/protocols/yahoo/yahoo_friend.c12
-rw-r--r--libpurple/protocols/yahoo/yahoo_packet.c17
-rw-r--r--libpurple/protocols/yahoo/yahoo_packet.h2
-rw-r--r--libpurple/protocols/yahoo/yahoo_picture.c273
-rw-r--r--libpurple/protocols/yahoo/yahoo_picture.h2
-rw-r--r--libpurple/protocols/yahoo/yahoo_profile.c124
-rw-r--r--libpurple/protocols/yahoo/yahoochat.c508
-rw-r--r--libpurple/protocols/yahoo/yahoochat.h6
-rw-r--r--libpurple/protocols/yahoo/ycht.c67
-rw-r--r--libpurple/protocols/yahoo/ycht.h2
-rw-r--r--libpurple/protocols/zephyr/Makefile.am11
-rw-r--r--libpurple/protocols/zephyr/Makefile.mingw1
-rw-r--r--libpurple/protocols/zephyr/ZReadAscii.c2
-rw-r--r--libpurple/protocols/zephyr/ZRetSubs.c5
-rw-r--r--libpurple/protocols/zephyr/ZVariables.c6
-rw-r--r--libpurple/protocols/zephyr/Zinternal.c4
-rw-r--r--libpurple/protocols/zephyr/internal.h2
-rw-r--r--libpurple/protocols/zephyr/zephyr.c299
-rw-r--r--libpurple/proxy.c273
-rw-r--r--libpurple/proxy.h314
-rw-r--r--libpurple/prpl.c96
-rw-r--r--libpurple/prpl.h926
-rw-r--r--libpurple/purple-3-uninstalled.pc.in (renamed from libpurple/purple-2-uninstalled.pc.in)0
-rw-r--r--libpurple/purple-3.pc.in (renamed from libpurple/purple-2.pc.in)2
-rw-r--r--libpurple/purple-client.c10
-rw-r--r--libpurple/purple-client.h1
-rw-r--r--libpurple/purple-socket.c416
-rw-r--r--libpurple/purple-socket.h217
-rw-r--r--libpurple/purple-uninstalled.pc.in19
-rwxr-xr-xlibpurple/purple-url-handler7
-rw-r--r--libpurple/purple.h.in41
-rw-r--r--libpurple/purple.pc.in16
-rw-r--r--libpurple/request-datasheet.c504
-rw-r--r--libpurple/request-datasheet.h411
-rw-r--r--libpurple/request.c1447
-rw-r--r--libpurple/request.h2278
-rw-r--r--libpurple/roomlist.c546
-rw-r--r--libpurple/roomlist.h440
-rw-r--r--libpurple/savedstatuses.c207
-rw-r--r--libpurple/savedstatuses.h276
-rw-r--r--libpurple/server.c335
-rw-r--r--libpurple/server.h235
-rw-r--r--libpurple/signals.c68
-rw-r--r--libpurple/signals.h271
-rw-r--r--libpurple/smiley-custom.c317
-rw-r--r--libpurple/smiley-custom.h91
-rw-r--r--libpurple/smiley-list.c433
-rw-r--r--libpurple/smiley-list.h193
-rw-r--r--libpurple/smiley-parser.c262
-rw-r--r--libpurple/smiley-parser.h138
-rw-r--r--libpurple/smiley-theme.c97
-rw-r--r--libpurple/smiley-theme.h144
-rw-r--r--libpurple/smiley.c882
-rw-r--r--libpurple/smiley.h272
-rw-r--r--libpurple/sound-theme-loader.c40
-rw-r--r--libpurple/sound-theme-loader.h35
-rw-r--r--libpurple/sound-theme.c6
-rw-r--r--libpurple/sound-theme.h66
-rw-r--r--libpurple/sound.c36
-rw-r--r--libpurple/sound.h126
-rw-r--r--libpurple/sslconn.c25
-rw-r--r--libpurple/sslconn.h318
-rw-r--r--libpurple/status.c1247
-rw-r--r--libpurple/status.h1035
-rw-r--r--libpurple/stringref.c11
-rw-r--r--libpurple/stringref.h85
-rw-r--r--libpurple/stun.c94
-rw-r--r--libpurple/stun.h48
-rwxr-xr-xlibpurple/tag.sh71
-rw-r--r--libpurple/tests/Makefile.am4
-rw-r--r--libpurple/tests/check_libpurple.c8
-rw-r--r--libpurple/tests/test_cipher.c163
-rw-r--r--libpurple/tests/test_jabber_caps.c28
-rw-r--r--libpurple/tests/test_jabber_scram.c3
-rw-r--r--libpurple/tests/test_oscar_util.c2
-rw-r--r--libpurple/tests/test_trie.c364
-rw-r--r--libpurple/tests/test_util.c167
-rw-r--r--libpurple/tests/test_xmlnode.c104
-rw-r--r--libpurple/tests/test_yahoo_util.c12
-rw-r--r--libpurple/tests/tests.h3
-rw-r--r--libpurple/theme-loader.c60
-rw-r--r--libpurple/theme-loader.h74
-rw-r--r--libpurple/theme-manager.c68
-rw-r--r--libpurple/theme-manager.h74
-rw-r--r--libpurple/theme.c78
-rw-r--r--libpurple/theme.h122
-rw-r--r--libpurple/trie.c907
-rw-r--r--libpurple/trie.h306
-rw-r--r--libpurple/upnp.c310
-rw-r--r--libpurple/upnp.h88
-rw-r--r--libpurple/util.c1939
-rw-r--r--libpurple/util.h1433
-rw-r--r--libpurple/value.c488
-rw-r--r--libpurple/value.h504
-rw-r--r--libpurple/version.c5
-rw-r--r--libpurple/version.h.in55
-rw-r--r--libpurple/whiteboard.c396
-rw-r--r--libpurple/whiteboard.h388
-rw-r--r--libpurple/win32/giowin32.c10
-rw-r--r--libpurple/win32/global.mak69
-rw-r--r--libpurple/win32/libc_interface.c28
-rw-r--r--libpurple/win32/libc_interface.h19
-rw-r--r--libpurple/win32/libc_internal.h3
-rw-r--r--libpurple/win32/libpurplerc.rc.in13
-rw-r--r--libpurple/win32/rules.mak11
-rw-r--r--libpurple/win32/targets.mak19
-rw-r--r--libpurple/win32/win32dep.c344
-rw-r--r--libpurple/win32/win32dep.h29
-rw-r--r--libpurple/xfer.c2442
-rw-r--r--libpurple/xfer.h943
-rw-r--r--libpurple/xmlnode.c410
-rw-r--r--libpurple/xmlnode.h440
-rw-r--r--m4macros/Makefile.am2
-rw-r--r--m4macros/as-ac-expand.m449
-rw-r--r--m4macros/purple.m42
-rw-r--r--pidgin.apspec.in3
-rw-r--r--pidgin.spec.in671
-rw-r--r--pidgin/Makefile.am139
-rw-r--r--pidgin/Makefile.mingw70
-rw-r--r--pidgin/gtk3compat.h289
-rw-r--r--pidgin/gtkaccount.c727
-rw-r--r--pidgin/gtkaccount.h49
-rw-r--r--pidgin/gtkblist-theme-loader.c74
-rw-r--r--pidgin/gtkblist-theme-loader.h30
-rw-r--r--pidgin/gtkblist-theme.c354
-rw-r--r--pidgin/gtkblist-theme.h371
-rw-r--r--pidgin/gtkblist.c2345
-rw-r--r--pidgin/gtkblist.h337
-rw-r--r--pidgin/gtkcellrendererexpander.c150
-rw-r--r--pidgin/gtkcellrendererexpander.h31
-rw-r--r--pidgin/gtkcertmgr.c53
-rw-r--r--pidgin/gtkcertmgr.h26
-rw-r--r--pidgin/gtkconn.c20
-rw-r--r--pidgin/gtkconn.h32
-rw-r--r--pidgin/gtkconv-theme-loader.c294
-rw-r--r--pidgin/gtkconv-theme-loader.h76
-rw-r--r--pidgin/gtkconv-theme.c791
-rw-r--r--pidgin/gtkconv-theme.h220
-rw-r--r--pidgin/gtkconv.c5566
-rw-r--r--pidgin/gtkconv.h304
-rw-r--r--pidgin/gtkconvwin.h321
-rw-r--r--pidgin/gtkdebug.c734
-rw-r--r--pidgin/gtkdebug.h32
-rw-r--r--pidgin/gtkdebug.html251
-rw-r--r--pidgin/gtkdialogs.c622
-rw-r--r--pidgin/gtkdialogs.h32
-rw-r--r--pidgin/gtkdnd-hints.c127
-rw-r--r--pidgin/gtkdnd-hints.h88
-rw-r--r--pidgin/gtkdocklet-gtk.c296
-rw-r--r--pidgin/gtkdocklet.c437
-rw-r--r--pidgin/gtkdocklet.h44
-rw-r--r--pidgin/gtkeventloop.c14
-rw-r--r--pidgin/gtkeventloop.h20
-rw-r--r--pidgin/gtkgaim-compat.h412
-rw-r--r--pidgin/gtkicon-theme-loader.c37
-rw-r--r--pidgin/gtkicon-theme-loader.h30
-rw-r--r--pidgin/gtkicon-theme.c7
-rw-r--r--pidgin/gtkicon-theme.h53
-rw-r--r--pidgin/gtkidle.c12
-rw-r--r--pidgin/gtkidle.h23
-rw-r--r--pidgin/gtkimhtml.c5910
-rw-r--r--pidgin/gtkimhtml.h983
-rw-r--r--pidgin/gtkimhtmltoolbar.c1597
-rw-r--r--pidgin/gtkimhtmltoolbar.h103
-rw-r--r--pidgin/gtkinternal.h47
-rw-r--r--pidgin/gtklog.c112
-rw-r--r--pidgin/gtklog.h61
-rw-r--r--pidgin/gtkmedia.c308
-rw-r--r--pidgin/gtkmedia.h11
-rw-r--r--pidgin/gtkmenutray.c71
-rw-r--r--pidgin/gtkmenutray.h75
-rw-r--r--pidgin/gtknickcolors.h14
-rw-r--r--pidgin/gtknotify.c206
-rw-r--r--pidgin/gtknotify.h57
-rw-r--r--pidgin/gtkplugin.c321
-rw-r--r--pidgin/gtkplugin.h35
-rw-r--r--pidgin/gtkpluginpref.c74
-rw-r--r--pidgin/gtkpluginpref.h26
-rw-r--r--pidgin/gtkpounce.c169
-rw-r--r--pidgin/gtkpounce.h35
-rw-r--r--pidgin/gtkprefs.c2123
-rw-r--r--pidgin/gtkprefs.h105
-rw-r--r--pidgin/gtkprivacy.c124
-rw-r--r--pidgin/gtkprivacy.h47
-rw-r--r--pidgin/gtkrequest.c1562
-rw-r--r--pidgin/gtkrequest.h58
-rw-r--r--pidgin/gtkroomlist.c200
-rw-r--r--pidgin/gtkroomlist.h29
-rw-r--r--pidgin/gtksavedstatuses.c93
-rw-r--r--pidgin/gtksavedstatuses.h53
-rw-r--r--pidgin/gtkscrollbook.c14
-rw-r--r--pidgin/gtkscrollbook.h15
-rw-r--r--pidgin/gtksession.c33
-rw-r--r--pidgin/gtksession.h34
-rw-r--r--pidgin/gtksmiley-manager.c831
-rw-r--r--pidgin/gtksmiley-manager.h60
-rw-r--r--pidgin/gtksmiley-theme.c625
-rw-r--r--pidgin/gtksmiley-theme.h174
-rw-r--r--pidgin/gtksmiley.c894
-rw-r--r--pidgin/gtksmiley.h114
-rw-r--r--pidgin/gtksound.c134
-rw-r--r--pidgin/gtksound.h45
-rw-r--r--pidgin/gtksourceiter.c777
-rw-r--r--pidgin/gtksourceiter.h61
-rw-r--r--pidgin/gtksourceundomanager.c1199
-rw-r--r--pidgin/gtksourceundomanager.h83
-rw-r--r--pidgin/gtksourceview-marshal.c95
-rw-r--r--pidgin/gtksourceview-marshal.h32
-rw-r--r--pidgin/gtkstatus-icon-theme.h34
-rw-r--r--pidgin/gtkstatusbox.c748
-rw-r--r--pidgin/gtkstatusbox.h53
-rw-r--r--pidgin/gtkthemes.c475
-rw-r--r--pidgin/gtkthemes.h70
-rw-r--r--pidgin/gtkutils.c1152
-rw-r--r--pidgin/gtkutils.h818
-rw-r--r--pidgin/gtkwebview.c2334
-rw-r--r--pidgin/gtkwebview.h649
-rw-r--r--pidgin/gtkwebviewtoolbar.c1849
-rw-r--r--pidgin/gtkwebviewtoolbar.h131
-rw-r--r--pidgin/gtkwhiteboard.c460
-rw-r--r--pidgin/gtkwhiteboard.h58
-rw-r--r--pidgin/gtkxfer.c (renamed from pidgin/gtkft.c)180
-rw-r--r--pidgin/gtkxfer.h (renamed from pidgin/gtkft.h)109
-rw-r--r--pidgin/libpidgin.c (renamed from pidgin/gtkmain.c)216
-rw-r--r--pidgin/minidialog.c86
-rw-r--r--pidgin/minidialog.h200
-rw-r--r--pidgin/pidgin-3-uninstalled.pc.in (renamed from pidgin/pidgin-2-uninstalled.pc.in)2
-rw-r--r--pidgin/pidgin-3.pc.in (renamed from pidgin/pidgin-2.pc.in)5
-rw-r--r--pidgin/pidgin-uninstalled.pc.in18
-rw-r--r--pidgin/pidgin.c59
-rw-r--r--pidgin/pidgin.h38
-rw-r--r--pidgin/pidgin.pc.in16
-rw-r--r--pidgin/pidginstock.c31
-rw-r--r--pidgin/pidginstock.h57
-rw-r--r--pidgin/pidgintooltip.c55
-rw-r--r--pidgin/pidgintooltip.h98
-rw-r--r--pidgin/pixmaps/Makefile.am15
-rw-r--r--pidgin/pixmaps/Makefile.mingw4
-rw-r--r--pidgin/pixmaps/e2ee/16/finished.pngbin0 -> 829 bytes
-rw-r--r--pidgin/pixmaps/e2ee/16/not-private.pngbin0 -> 704 bytes
-rw-r--r--pidgin/pixmaps/e2ee/16/private.pngbin0 -> 703 bytes
-rw-r--r--pidgin/pixmaps/e2ee/16/unverified.pngbin0 -> 671 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/Makefile.am9
-rw-r--r--pidgin/pixmaps/emotes/default/24/bulgy-eyes.pngbin1500 -> 0 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/default.theme.in132
-rw-r--r--pidgin/pixmaps/emotes/default/24/freaked-out.pngbin1513 -> 0 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/messed.pngbin1515 -> 0 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/mohawk.pngbin1573 -> 0 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/sidefrown.pngbin1506 -> 0 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/sinister.pngbin1414 -> 0 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/smirk.pngbin1482 -> 0 bytes
-rw-r--r--pidgin/pixmaps/emotes/none/Makefile.am17
-rw-r--r--pidgin/pixmaps/emotes/none/Makefile.mingw27
-rw-r--r--pidgin/pixmaps/emotes/none/none.theme.in3
-rw-r--r--pidgin/pixmaps/emotes/small/16/Makefile.am4
-rw-r--r--pidgin/pixmaps/emotes/small/16/small.theme.in54
-rw-r--r--pidgin/pixmaps/protocols/16/myspace.pngbin745 -> 0 bytes
-rw-r--r--pidgin/pixmaps/protocols/22/myspace.pngbin1103 -> 0 bytes
-rw-r--r--pidgin/pixmaps/protocols/22/scalable/myspace.svg93
-rw-r--r--pidgin/pixmaps/protocols/48/google-talk.pngbin0 -> 2865 bytes
-rw-r--r--pidgin/pixmaps/protocols/48/myspace.pngbin2112 -> 0 bytes
-rw-r--r--pidgin/pixmaps/toolbar/16/insert-screenshot.pngbin0 -> 689 bytes
-rw-r--r--pidgin/plugins/Makefile.am111
-rw-r--r--pidgin/plugins/Makefile.mingw41
-rw-r--r--pidgin/plugins/cap/Makefile.am7
-rw-r--r--pidgin/plugins/cap/cap.c78
-rw-r--r--pidgin/plugins/cap/cap.h6
-rw-r--r--pidgin/plugins/cap/cap_statistics.h2
-rw-r--r--pidgin/plugins/contact_priority.c36
-rw-r--r--pidgin/plugins/convcolors.c477
-rw-r--r--pidgin/plugins/crazychat/Makefile.am7
-rw-r--r--pidgin/plugins/crazychat/cc_network.c24
-rw-r--r--pidgin/plugins/crazychat/cc_pidgin_plugin.c13
-rw-r--r--pidgin/plugins/disco/Makefile.am7
-rw-r--r--pidgin/plugins/disco/Makefile.mingw1
-rw-r--r--pidgin/plugins/disco/gtkdisco.c37
-rw-r--r--pidgin/plugins/disco/xmppdisco.c150
-rw-r--r--pidgin/plugins/extplacement.c19
-rw-r--r--pidgin/plugins/gestures/Makefile.am7
-rw-r--r--pidgin/plugins/gestures/gestures.c82
-rw-r--r--pidgin/plugins/gestures/stroke-draw.c94
-rw-r--r--pidgin/plugins/gevolution/Makefile.am7
-rw-r--r--pidgin/plugins/gevolution/add_buddy_dialog.c24
-rw-r--r--pidgin/plugins/gevolution/assoc-buddy.c63
-rw-r--r--pidgin/plugins/gevolution/eds-utils.c114
-rw-r--r--pidgin/plugins/gevolution/gevo-util.c65
-rw-r--r--pidgin/plugins/gevolution/gevolution.c29
-rw-r--r--pidgin/plugins/gevolution/gevolution.h9
-rw-r--r--pidgin/plugins/gevolution/new_person_dialog.c18
-rw-r--r--pidgin/plugins/gtk-signals-test.c28
-rw-r--r--pidgin/plugins/history.c60
-rw-r--r--pidgin/plugins/iconaway.c4
-rw-r--r--pidgin/plugins/imgupload.c483
-rw-r--r--pidgin/plugins/mailchk.c16
-rw-r--r--pidgin/plugins/markerline.c134
-rw-r--r--pidgin/plugins/musicmessaging/Makefile.am11
-rw-r--r--pidgin/plugins/musicmessaging/musicmessaging.c112
-rw-r--r--pidgin/plugins/notify.c134
-rw-r--r--pidgin/plugins/perl/Makefile.am11
-rw-r--r--pidgin/plugins/perl/Makefile.mingw9
-rw-r--r--pidgin/plugins/perl/common/GtkAccount.xs2
-rw-r--r--pidgin/plugins/perl/common/GtkBlist.xs5
-rw-r--r--pidgin/plugins/perl/common/GtkConv.xs19
-rw-r--r--pidgin/plugins/perl/common/GtkConvWin.xs17
-rw-r--r--pidgin/plugins/perl/common/GtkDialogs.xs4
-rw-r--r--pidgin/plugins/perl/common/GtkIMHtml.xs368
-rw-r--r--pidgin/plugins/perl/common/GtkIMHtmlToolbar.xs22
-rw-r--r--pidgin/plugins/perl/common/GtkStatusBox.xs5
-rw-r--r--pidgin/plugins/perl/common/GtkThemes.xs28
-rw-r--r--pidgin/plugins/perl/common/MANIFEST3
-rw-r--r--pidgin/plugins/perl/common/Makefile.PL.in10
-rw-r--r--pidgin/plugins/perl/common/Makefile.mingw11
-rw-r--r--pidgin/plugins/perl/common/Pidgin.pm8
-rw-r--r--pidgin/plugins/perl/common/Pidgin.xs6
-rw-r--r--pidgin/plugins/perl/common/gtkmodule.h20
-rw-r--r--pidgin/plugins/perl/common/typemap7
-rw-r--r--pidgin/plugins/pidgininc.c14
-rw-r--r--pidgin/plugins/pidginrc.c704
-rw-r--r--pidgin/plugins/raw.c3
-rw-r--r--pidgin/plugins/relnot.c47
-rw-r--r--pidgin/plugins/screencap.c1047
-rw-r--r--pidgin/plugins/sendbutton.c4
-rw-r--r--pidgin/plugins/spellchk.c34
-rw-r--r--pidgin/plugins/themeedit-icon.c7
-rw-r--r--pidgin/plugins/themeedit.c127
-rw-r--r--pidgin/plugins/ticker/Makefile.am7
-rw-r--r--pidgin/plugins/ticker/Makefile.mingw1
-rw-r--r--pidgin/plugins/ticker/gtkticker.c200
-rw-r--r--pidgin/plugins/ticker/ticker.c20
-rw-r--r--pidgin/plugins/timestamp.c240
-rw-r--r--pidgin/plugins/timestamp_format.c322
-rw-r--r--pidgin/plugins/unity.c169
-rw-r--r--pidgin/plugins/vvconfig.c807
-rw-r--r--pidgin/plugins/webkit.c85
-rw-r--r--pidgin/plugins/win32/transparency/Makefile.am16
-rw-r--r--pidgin/plugins/win32/transparency/Makefile.mingw1
-rw-r--r--pidgin/plugins/win32/transparency/win2ktrans.c76
-rw-r--r--pidgin/plugins/win32/winprefs/Makefile.am19
-rw-r--r--pidgin/plugins/win32/winprefs/Makefile.mingw1
-rw-r--r--pidgin/plugins/win32/winprefs/gtkappbar.c34
-rw-r--r--pidgin/plugins/win32/winprefs/winprefs.c18
-rw-r--r--pidgin/plugins/xmppconsole.c487
-rw-r--r--pidgin/themes/Contents/Info.plist16
-rw-r--r--pidgin/themes/Contents/Resources/Content.html6
-rw-r--r--pidgin/themes/Contents/Resources/Incoming/Content.html8
-rw-r--r--pidgin/themes/Contents/Resources/Status.html7
-rw-r--r--pidgin/themes/Contents/Resources/Variants/Default.css2
-rw-r--r--pidgin/themes/Contents/Resources/Variants/No-Timestamps.css8
-rw-r--r--pidgin/themes/Contents/Resources/main.css95
-rw-r--r--pidgin/themes/Makefile.am22
-rw-r--r--pidgin/themes/Template.html376
-rw-r--r--pidgin/win32/MinimizeToTray.c2
-rw-r--r--pidgin/win32/gtkdocklet-win32.c20
-rw-r--r--pidgin/win32/gtkwin32dep.c67
-rw-r--r--pidgin/win32/gtkwin32dep.h8
-rwxr-xr-xpidgin/win32/nsis/generate_gtk_zip.sh373
-rw-r--r--pidgin/win32/nsis/nsis_translations.desktop.in6
-rw-r--r--pidgin/win32/nsis/pidgin-installer.nsi20
-rwxr-xr-xpidgin/win32/nsis/rpm2zip.sh65
-rw-r--r--pidgin/win32/pidgin_dll_rc.rc.in15
-rw-r--r--pidgin/win32/pidgin_exe_rc.rc.in15
-rw-r--r--pidgin/win32/prepare-workspace.sh445
-rw-r--r--pidgin/win32/untar.c6
-rw-r--r--pidgin/win32/untar.h2
-rw-r--r--pidgin/win32/winpidgin.c162
-rw-r--r--pidgin/win32/wspell.c110
-rw-r--r--pidgin/win32/wspell.h57
-rw-r--r--po/Makefile.mingw3
-rw-r--r--po/POTFILES.in67
-rw-r--r--po/POTFILES.skip3
-rw-r--r--po/da.po2706
-rw-r--r--po/de.po1371
-rw-r--r--po/mai.po2
-rw-r--r--po/pl.po11361
-rw-r--r--valgrind-suppressions91
1178 files changed, 136584 insertions, 117913 deletions
diff --git a/.hgignore b/.hgignore
index d0c77eadfe..5b0c0a72c9 100644
--- a/.hgignore
+++ b/.hgignore
@@ -1,3 +1,9 @@
+syntax: glob
+.*.swp
+clang_output_*
+pidgin-*
+.dirstamp
+
syntax: regexp
(.*/)?TAGS$
(.*/)?\.svn
@@ -28,7 +34,7 @@ syntax: regexp
.*\.pyo$
.*\.rej$
.*\.so$
-Doxyfile(\.mingw)?$
+.*\.moc$
VERSION$
aclocal.m4
autogen.args
@@ -46,15 +52,26 @@ depcomp
doc/finch.1$
doc/html
doc/pidgin.1$
+doc/reference/.*/.*\.args
+doc/reference/.*/.*\.hierarchy
+doc/reference/.*/.*\.interfaces
+doc/reference/.*/.*\.prerequisites
+doc/reference/.*/.*\.signals
+doc/reference/.*/.*\.stamp
+doc/reference/.*/.*\.txt
+doc/reference/.*/.*\.types
+doc/reference/.*/html/.*
+doc/reference/.*/xml/.*
+doc/reference/.*/version.xml
finch/finch$
-finch/libgnt/gntmarshal.c
-finch/libgnt/gntmarshal.h
+finch/libgnt/gntmarshal.[ch]
+gtk-doc.make
install-sh
intltool-.*
libpurple/dbus-bindings.c
libpurple/dbus-signals.c
-libpurple/dbus-types.c
-libpurple/dbus-types.h
+libpurple/dbus-types.[ch]
+libpurple/enums.[ch]
libpurple/example/nullclient
libpurple/gconf/purple.schemas$
libpurple/marshallers.[ch]
@@ -64,17 +81,24 @@ libpurple/plugins/perl/common/Makefile.old
libpurple/plugins/perl/common/const-c.inc
libpurple/plugins/perl/common/const-xs.inc
libpurple/plugins/perl/common/lib
-libpurple/purple-client-bindings.c
-libpurple/purple-client-bindings.h
+libpurple/purple-client-bindings.[ch]
libpurple/purple-client-example
libpurple/purple.h$
+libpurple/tests/core
libpurple/tests/check_libpurple
libpurple/tests/libpurple..
+^libpurple/tests/test-suite\.log$
libpurple/version.h$
libpurple/win32/libpurplerc.rc$
libtool
local.mak
ltmain.sh
+m4macros/gtk-doc.m4
+m4macros/intltool.m4
+m4macros/ltoptions.m4
+m4macros/ltsugar.m4
+m4macros/ltversion.m4
+m4macros/lt~obsolete.m4
missing
mkinstalldirs
package_revision.h
@@ -89,7 +113,7 @@ pidgin-[0-9a-z.-]+/
pidgin.apspec$
pidgin.desktop$
pidgin.desktop.in$
-pidgin.spec$
+pidgin/.*\.html\.h$
pidgin/pidgin$
pidgin/pixmaps/emotes/default/24/theme
pidgin/pixmaps/emotes/none/theme
@@ -99,14 +123,19 @@ pidgin/plugins/perl/common/Makefile.PL$
pidgin/plugins/perl/common/Makefile.old
pidgin/win32/nsis/gtk-runtime-*.*.*.*.zip
pidgin/win32/nsis/gtk_runtime_stage$
+pidgin/win32/nsis/cacert.pem
pidgin/win32/nsis/langmacros.nsh
pidgin/win32/nsis/nsis_translations.desktop
pidgin/win32/nsis/pidgin-spellcheck-preselect.nsh
pidgin/win32/nsis/pidgin-spellcheck.nsh
pidgin/win32/nsis/pidgin-translations.nsh$
pidgin/win32/nsis/translations
+pidgin/win32/nsis/wget.log
pidgin/win32/pidgin_dll_rc.rc$
pidgin/win32/pidgin_exe_rc.rc$
+finch/finch_winres.rc$
+finch/libfinch_winres.rc$
+finch/libgnt/libgnt_winres.rc$
po/Makefile.in.in
po/POTFILES$
po/missing
diff --git a/.tx/config b/.tx/config
index 3bc687a124..9f516b311f 100644
--- a/.tx/config
+++ b/.tx/config
@@ -1,7 +1,7 @@
[main]
host = https://www.transifex.com
-[pidgin.pidgin_pot-for-2_x_y]
+[pidgin.pidgin_pot-for-development-branch]
file_filter = po/<lang>.po
source_file = po/pidgin.pot
source_lang = en
diff --git a/AUTHORS b/AUTHORS
index 35e7968d6d..52dbbe7673 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -7,15 +7,11 @@ We've got an IRC room now too, #pidgin on irc.freenode.net. Come check us out.
Current Developers:
------------------
-
Daniel 'datallah' Atallah - Developer
Paul 'darkrain42' Aurich - Developer
-John 'rekkanoryo' Bailey - Developer
Ethan 'Paco-Paco' Blanton - Developer
Sadrul Habib Chowdhury - Developer
Mark 'KingAnt' Doliner - Developer
-Casey Harkins - Developer
-Ivan Komarov - Developer
Gary 'grim' Kramlich - Developer
Richard 'rlaager' Laager - Developer
Marcus 'malu' Lundblad - Developer
@@ -30,17 +26,18 @@ Kevin 'SimGuy' Stange - Developer & Webmaster
Will 'resiak' Thompson - Developer
Stu 'nosnilmot' Tomlinson - Developer
Jorge 'Masca' Villaseñor - Developer
+Tomasz Wasilczyk - Developer
Crazy Patch Writers:
-------------------
Jakub 'haakon' Adam
Krzysztof Klinikowski
-Peter 'Fmoo' Ruibal
-Gabriel 'Nix' Schulhof
-Tomasz Wasilczyk
+Eion Robb
+Ankit Vani
Retired Developers:
------------------
+John 'rekkanoryo' Bailey - Developer
Herman Bloggs - Win32 Port
Thomas Butter - Developer
Ka-Hing Cheung - Developer
@@ -49,6 +46,8 @@ Sean Egan - Developer
Rob Flynn <gaim@robflynn.com> - maintainer
Adam Fritzler - libfaim maintainer
Christian 'ChipX86' Hammond - Developer & Webmaster
+Casey Harkins - Developer
+Ivan Komarov - Developer
Syd Logan - hacker and designated driver [lazy bum]
Christopher 'siege' O'Brien - Developer
Bartosz Oler - Developer
@@ -67,6 +66,8 @@ Dennis 'EvilDennisR' Ristuccia - Senior Contributor/QA
Peter 'Bleeter' Lawler
Robert 'Robot101' McQueen
Benjamin Miller
+Peter 'Fmoo' Ruibal
+Gabriel 'Nix' Schulhof
Artists:
-------
@@ -74,8 +75,8 @@ Hylke Bons - Icons
Other Contributions:
-------------------
-Much thanks to Evan Martin <martine@cs.washington.edu> for writing
-GtkSpell <http://gtkspell.sourceforge.net> responsible for the
+Much thanks to Evan Martin <martine@cs.washington.edu> for writing
+GtkSpell <http://gtkspell.sourceforge.net> responsible for the
"Highlight misspelled words" feature and for gtk-nativewin
<http://bunny.darktech.org/cvs/gtk-nativewin/> the default GTK+-2.0
engine originally used in our win32 port.
@@ -83,11 +84,11 @@ engine originally used in our win32 port.
** ORIGINAL LOGO DESIGNED BY: Naru Sundar **
Peter Teichiman <peter@helixcode.com>
-Larry Ewing
+Larry Ewing
Jeramey A. Crawford
Thanks to these boys. Peter and Larry managed to stomp
out a large list of Mem Leaks. Jeramey found the remaining
- onees and pointed me to those. Props to the boys at
+ onees and pointed me to those. Props to the boys at
Helix Code. Thanks guys.
Nathan Walp
@@ -98,15 +99,15 @@ Neil Sanchala
Arkadiusz Miskiewicz
Wrote the Gadu-Gadu plugin
-
-David Prater <IM: dRaven43> draven@tcsx.net
+
+David Prater <IM: dRaven43> draven@tcsx.net
Log and Colour Button Images
Sébastien Carpe <IM: Seb Carpe>
Base HTTP Proxy Support
Ari Pollak <IM: Ari Pollak> compwiz.dhs.org
- Resize conversation window patch
+ Resize conversation window patch
Decklin Foster
Many GUI improvements, other nifty additions and fixes
diff --git a/COPYRIGHT b/COPYRIGHT
index 93ff039941..49c78afada 100644
--- a/COPYRIGHT
+++ b/COPYRIGHT
@@ -15,7 +15,7 @@ If concerns are raised as to the copyright holder of a particular
piece of code, then that code should be traced through our version
control system to see from where it came and who has modified it.
-Copyright (C) 1998-2013 by the following:
+Copyright (C) 1998-2014 by the following:
Mark
Saleem Abdulrasool
@@ -25,6 +25,7 @@ Thijs Alkemade
Manuel Amador
Matt Amato
Josef Andrysek
+Flavius Anton
Geoffrey Antos
Daniel Atallah
Paul Aurich
@@ -54,6 +55,7 @@ Matthew W.S. Bell
Igor Belyi
David Benjamin
Brian Bernas
+Vivien Bernet-Rollande
Paul Betts
Runa Bhattacharjee
Jonas Birmé
@@ -110,6 +112,7 @@ Patrick Cheung
Ka-Hing Cheung
Sadrul Habib Chowdhury
Brian Chu
+Howard Chu
Arturo Cisneros, Jr.
Vincas Ciziunas
Jonathan Clark
@@ -259,6 +262,7 @@ Instant Messaging Freedom, Inc.
Vitaliy Ischenko
Intel Corporation
Andrew Ivanov
+Momchil Ivanov
Scott Jackson
Hans Petter Jansson
David Jedelsky
@@ -425,6 +429,7 @@ Eric Polino <aluink@gmail.com>
Ari Pollak
Stephen Pope
Cristi Posoiu
+Alexei Potashnik
Nathan Poznick
Jory A. Pratt
David Preece
@@ -442,6 +447,7 @@ Etan Reisner
David Reiss
Luoh Ren-Shan
Noa Resare
+Tim Retout
Daniele Ricci
Kristian Rietveld
Pekka Riikonen
@@ -545,6 +551,7 @@ Arun A. Tharuvai
Cestonaro Thilo
Will Thompson
Douglas Thrift (douglaswth)
+Niels Thykier
Mark Tiefenbruck
Andrew Tinney
Jeffery To
@@ -572,6 +579,7 @@ Martijn van Beers
Gideon van Melle
Arjan van de Ven
Philip Van Hoof
+Ankit Vani
Kristof Vansant
James Vega
David Vermeille
diff --git a/ChangeLog b/ChangeLog
index 327a2029ec..55c72985db 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,73 @@
Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
+version 3.0.0 (??/??/????):
+ Pidgin:
+ * Support building with the GTK+ 3.x toolkit. When configuring the
+ build, use --with-gtk=<2|3> to determine which toolkit to use. Using
+ either 2 or 3 will attempt to build with specifically 2.x or 3.x
+ support. The default is 'auto', which will first look for 3.x
+ development headers and then 2.x development headers.
+ * Add email notification in the docklet area. (Alexei) (#3571)
+ * Add a pref to select the type messages in conversation that triggers
+ the docklet notification. (Momchil) (#12598)
+ * Complete support for receiving a limited amount of history when
+ joining a room. (Kha) (#15458)
+ * Fix gevolution plugin to compile with e-d-s >= 3.6, older versions are
+ not supported anymore. (Ed Catmur) (#15353)
+
+ Finch:
+ * Support the conversation-extended signal for extending the
+ conversation menu. (Howard Chu) (#14818)
+
+ AIM and ICQ:
+ * Make buddy list management code more efficient. (Oliver) (#4816)
+
+ Bonjour:
+ * Support file transfers up to ~9 EiB.
+
+ Gadu-Gadu:
+ * Possibility to require encryption. Also, using encryption when
+ available is default option now. (Tomasz Wasilczyk)
+ * Show local time for incoming messages. (Tomasz Wasilczyk) (#4579)
+ * Fixed password change dialog and problems with connecting to accounts
+ with non-ASCII passwords. (Tomasz Wasilczyk) (#14652)
+ * Option to show links from strangers. (Tomasz Wasilczyk) (#10591)
+ * Better handling of "invisible" and "chatty" statuses. (Tomasz
+ Wasilczyk) (#13836)
+
+ MSN:
+ * Fix file transfer with older Mac MSN clients.
+ * Support file transfers up to ~9 EiB.
+ * Fix buddies not going offline.
+
+ MXit:
+ * Remove all reference to Hidden Number.
+ * Fix decoding of font-size changes in the markup of received messages.
+ * Ignore new invites to join a GroupChat if you're already joined, or
+ still have a pending invite.
+ * The buddy's name was not centered vertically in the buddy-list if they
+ did not have a status-message or mood set.
+
+ MySpace:
+ * Remove this protocol because the servers have been taken offline.
+ (Robbie Vehse) (#15356)
+
+ XMPP:
+ * Strip element prefixes from XHTML-IM messages as they're presented
+ to the core (and UIs) as incoming messages (Thijs Alkemade).
+ (#14529)
+ * Support file transfers up to ~9 EiB.
+ * Invalid user moods can no longer be sent to the server.
+
+ Plugins:
+ * The Offline Message Emulation plugin now adds a note that the message
+ was an offline message. (Flavius Anton) (#2497)
+
+ General:
+ * Various core components of libpurple are now GObjects.
+ * Ciphers are now built from the libpurple directory.
+ * Doxygen has been replaced by gtk-doc for generating documentation.
+
version 2.10.10 (?/?/?):
libpurple3 compatibility:
* Encrypted account passwords are preserved until the new one is set.
diff --git a/ChangeLog.API b/ChangeLog.API
index 75569d3a58..644a472ca7 100644
--- a/ChangeLog.API
+++ b/ChangeLog.API
@@ -1,7 +1,513 @@
Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
-version 2.10.10:
- * No changes
+This file intends to list all changes to libpurple, Pidgin, and Finch's public
+API. We sometimes forget to add changes to this file--sorry.
+
+If your plugin fails to build with a new major version (e.g. 3.0.0) we
+suggest checking this list first, in case a function was simply renamed.
+You may still need to consult our API docs or our source code.
+
+If you notice something missing from this list, please let us know and we'll
+add it.
+
+version 3.0.0 (??/??/????):
+ libpurple:
+ Added:
+ * displaying-emails-clear signal (notification signal)
+ * purple_account_is_disconnecting
+ * purple_account_get_ui_data
+ * purple_account_set_ui_data
+ * purple_account_privacy_get_denied
+ * purple_account_privacy_get_permitted
+ * PurpleAccountPresence and PurpleBuddyPresence inherit PurplePresence
+ * purple_account_presence_new
+ * purple_buddy_presence_new
+ * purple_account_register_completed
+ * PurpleAESCipher, PurpleDESCipher, PurpleDES3Cipher, PurpleHMACCipher,
+ PurplePBKDF2Cipher and PurpleRC4Cipher inherit PurpleCipher
+ * purple_aes_cipher_new
+ * purple_des3_cipher_new
+ * purple_des_cipher_new
+ * purple_hmac_cipher_new
+ * purple_pbkdf2_cipher_new
+ * purple_rc4_cipher_new
+ * purple_blist_node_is_transient
+ * purple_blist_node_set_transient
+ * purple_certificate_get_der_data
+ * purple_certificate_get_display_string
+ * purple_chat_user_get_alias
+ * purple_chat_user_get_chat
+ * purple_chat_user_get_flags
+ * purple_chat_user_is_buddy
+ * purple_chat_user_get_ui_data
+ * purple_chat_user_set_ui_data
+ * purple_chat_user_set_chat
+ * purple_connection_get_active_chats
+ * purple_connection_get_error_info
+ * purple_connection_get_flags
+ * purple_connection_set_flags
+ * purple_connection_update_last_received
+ * purple_conversation_get_ui_data
+ * purple_conversation_set_ui_data
+ * purple_conversation_message_get_alias
+ * purple_conversation_message_get_conv
+ * PurpleCountingNode, inherits PurpleBlistNode
+ * purple_counting_node_get_*
+ * purple_counting_node_change_*
+ * purple_counting_node_set_*
+ * PurpleHash and purple_hash_* API
+ * PurpleMD4Hash, PurpleMD5Hash, PurpleSHA1Hash and PurpleSHA265Hash
+ inherit PurpleHash
+ * purple_md4_hash_new
+ * purple_md5_hash_new
+ * purple_sha1_hash_new
+ * purple_sha256_hash_new
+ * PurpleIMConversation and PurpleChatConversation inherit
+ PurpleConversation
+ * purple_notify_emails_pending
+ * purple_notify_emails_present
+ * purple_notify_searchresult_column_get_title
+ * purple_notify_searchresult_column_is_visible
+ * purple_notify_searchresult_column_set_visible
+ * purple_notify_user_info_entry_destroy
+ * purple_notify_user_info_prepend_pair_plaintext
+ * purple_menu_action_get_callback
+ * purple_menu_action_get_children
+ * purple_menu_action_get_data
+ * purple_menu_action_set_label
+ * purple_menu_action_set_data
+ * purple_menu_action_set_callback
+ * purple_menu_action_set_children
+ * purple_request_certificate
+ * purple_request_field_certificate_new
+ * purple_request_field_certificate_get_value
+ * purple_request_field_get_tooltip
+ * purple_request_field_group_get_fields_list
+ * purple_request_field_set_tooltip
+ * purple_request_fields_get_ui_data
+ * purple_request_fields_set_ui_data
+ * purple_roomlist_get_account
+ * purple_roomlist_get_proto_data
+ * purple_roomlist_get_ui_data
+ * purple_roomlist_room_get_expanded_once
+ * purple_roomlist_room_set_expanded_once
+ * purple_roomlist_set_proto_data
+ * purple_roomlist_set_ui_data
+ * purple_whiteboard_get_account
+ * purple_whiteboard_get_draw_list
+ * purple_whiteboard_set_draw_list
+ * purple_whiteboard_get_protocol_data
+ * purple_whiteboard_set_protocol_data
+ * purple_whiteboard_get_state
+ * purple_whiteboard_set_state
+ * purple_whiteboard_get_ui_data
+ * purple_whiteboard_set_ui_data
+ * purple_whiteboard_get_who
+ * purple_xfer_get_fd
+ * purple_xfer_get_message
+ * purple_xfer_get_protocol_data
+ * purple_xfer_get_ui_data
+ * purple_xfer_get_watcher
+ * purple_xfer_set_fd
+ * purple_xfer_set_local_port
+ * purple_xfer_set_protocol_data
+ * purple_xfer_set_remote_user
+ * purple_xfer_set_status
+ * purple_xfer_set_ui_data
+ * purple_xfer_set_watcher
+ * purple_xmlnode_get_default_namespace
+ * purple_xmlnode_strip_prefixes
+
+ Changed:
+ * account.h has been split into account.h (PurpleAccount GObject) and
+ accounts.h (Accounts subsystem)
+ * blist.h has been split into buddylist.h (PurpleBuddyList and
+ subsystem), blistnode.h (PurpleBlistNode and PurpleCountingNode
+ GObjects), blistnodetypes (Buddy, Chat, Contact, Group GObjects)
+ * circbuffer.h renamed to circularbuffer.h
+ * conversation.h has been split into conversation.h
+ (PurpleConversation), conversationtypes.h (PurpleIMConversation and
+ PurpleChatConversation) and conversations.h (Conversations subsystem)
+ * Files in libpurple/ciphers have been renamed. To use a particular
+ cipher, include its header.
+ * Renamed ft.h file to xfer.h
+ * status.h has been split into status.h (Status API) and presence.h
+ (Presence API)
+ * account-authorization-requested signal merged with
+ account-authorization-requested-with-message signal
+ * purple_account_add_buddy now takes an invite message as the last
+ parameter
+ * purple_account_add_buddies now takes an invite message as the last
+ parameter
+ * PurpleAccount is now a GObject. Please see the documentation for
+ details.
+ * purple_account_get_alias renamed to purple_account_get_private_alias
+ * purple_account_set_alias renamed to purple_account_set_private_alias
+ * purple_account_option_get_type renamed to
+ purple_account_option_get_pref_type
+ * PurplePrivacyType renamed to PurpleAccountPrivacyType
+ * purple_privacy_* functions are now purple_account_privacy_*
+ * Removed arguments local and restore from purple_account_privacy_allow
+ and purple_account_privacy_deny
+ * PurpleBlistNode is now a GObject. Please see the documentation for
+ details.
+ * 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_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
+ * purple_blist_server_alias_buddy renamed to
+ * purple_blist_update_buddy_status renamed to purple_buddy_update_status
+ purple_buddy_set_server_alias
+ * purple_buddy_get_local_buddy_alias renamed to
+ purple_buddy_get_local_alias
+ * PurpleContact and PurpleGroup inherit PurpleCountingNode
+ * PurpleBuddyList is now a GObject. Please see the documentation for
+ details.
+ * purple_find_buddies renamed to purple_blist_find_buddies
+ * 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_certificate_check_signature_chain now returns a list of failing
+ PurpleCertificate*s as the second parameter
+ * PurpleConversation is now an abstract type, and is a GObject. Please
+ see the documentation for details.
+ * purple_conv_* functions are now purple_conversation_*
+ * purple_conv_im_* functions are now purple_im_conversation_*
+ * purple_conv_chat_* functions are now purple_chat_conversation_*
+ * purple_chat_conversation_find_user renamed to
+ purple_chat_conversation_has_user
+ * PurpleTypingState renamed to PurpleIMTypingState
+ * PurpleConvChatBuddy changed to PurpleChatUser, is now a GObject.
+ Please see the documentation for details.
+ * purple_conv_chat_cb_* functions are now purple_chat_user_*
+ * Replaced 'chat-buddy' with 'chat-user' in conversation signals
+ * Replaced chatname, buddyname arguments of 'chat-user-flags' (formerly
+ 'chat-buddy-flags') signal with PurpleChatUser *
+ * PurpleCipher split into PurpleCipher and PurpleHash, both are
+ GObjects. Please see the documentation for details.
+ * purple_cipher_context_* functions are now purple_cipher_*
+ * PurpleCircBuffer changed to PurpleCircularBuffer, is now a GObject.
+ Please see the documentation for details.
+ * purple_circ_buffer_* functions are now purple_circular_buffer_*
+ * purple_connection_error now takes a PurpleConnectionError
+ as the second parameter
+ * PurpleConnection is now a GObject. Please see the documentation for
+ details.
+ * PURPLE_CONNECTION_* prefix of PurpleConnectionFlag enum names changed
+ to PURPLE_CONNECTION_FLAG_*
+ * PURPLE_* prefix of PurpleConnectionState enum names changed to
+ PURPLE_CONNECTION_*
+ * purple_conversation_get_gc renamed to
+ purple_conversation_get_connection
+ * purple_dnsquery_a now takes a PurpleAccount as the first parameter
+ * purple_imgstore_add renamed to purple_imgstore_new
+ * purple_imgstore_add_with_id renamed to purple_imgstore_new_with_id
+ * purple_network_listen now takes the protocol family as the second
+ parameter
+ * purple_network_listen now takes a boolean indicating external port
+ mapping as the fourth parameter
+ * purple_network_listen_range now takes a boolean indicating external
+ port mapping as the fifth parameter
+ * purple_network_listen_range now takes the protocol family as the
+ third parameter
+ * purple_notify_user_info_add_pair renamed to
+ purple_notify_user_info_add_pair_html
+ * purple_notify_user_info_get_entries returns a GQueue instead of
+ a GList
+ * purple_notify_user_info_entry_get_type renamed to
+ purple_notify_user_info_entry_get_entry_type
+ * purple_notify_user_info_entry_set_type renamed to
+ purple_notify_user_info_entry_set_entry_type
+ * purple_notify_user_info_prepend_pair renamed to
+ purple_notify_user_info_prepend_pair_html
+ * purple_plugin_pref_get_type renamed to
+ purple_plugin_pref_get_pref_type
+ * purple_plugin_pref_set_type renamed to
+ purple_plugin_pref_set_pref_type
+ * purple_prefs_get_type renamed to purple_prefs_get_pref_type
+ * purple_proxy_info_get_type renamed to purple_proxy_info_get_proxy_type
+ * purple_proxy_info_set_type renamed to purple_proxy_info_set_proxy_type
+ * purple_request_field_get_type renamed to
+ purple_request_field_get_field_type
+ * PurpleRoomlist is now a GObject. Please see the documentation for
+ details.
+ * purple_roomlist_room_get_type is now
+ purple_roomlist_room_get_room_type
+ * purple_roomlist_field_get_type is now
+ purple_roomlist_field_get_field_type
+ * purple_savedstatus_get_type renamed to
+ purple_savedstatus_get_primitive_type
+ * purple_savedstatus_set_type renamed to
+ purple_savedstatus_set_primitive_type
+ * purple_savedstatus_substatus_get_type renamed to
+ purple_savedstatus_substatus_get_status_type
+ * serv_* functions are now purple_serv_*
+ * purple_srv_resolve now takes a PurpleAccount as the first parameter
+ * purple_srv_txt_query_get_type renamed to
+ purple_srv_txt_query_get_query_type
+ * PurpleStatus is now a GObject. Please see the documentation for
+ details.
+ * purple_status_attr_* functions are now purple_status_attribute_*
+ * PurpleStatusAttr renamed to PurpleStatusAttribute
+ * PurplePresence is now an abstract type, and is a GObject. Please see
+ the documentation for details.
+ * update_idle method has been added to PurplePresenceClass to update the
+ idle state of a presence
+ * StunCallback renamed to PurpleStunCallback
+ * purple_str_size_to_units now takes a goffset as the size parameter
+ * PTFunc renamed to PurpleThemeFunc
+ * purple_txt_resolve now takes a PurpleAccount as the first parameter
+ * UPnPMappingAddRemove renamed to PurpleUPnPMappingAddRemove
+ * purple_util_fetch_url_request now takes a PurpleAccount as
+ the first parameter
+ * purple_util_fetch_url_request now takes a length as the eighth
+ parameter
+ * purple_util_fetch_url_len now takes a length as the fifth parameter
+ * PurpleWhiteboard is now a GObject. Please see the documentation for
+ details.
+ * purple_value_destroy is now purple_value_free
+ * purple_value_new, purple_value_dup and purple_value_free now operate
+ on GValue instead of PurpleValue
+ * Signal registration now uses GTypes instead of PurpleValues. See
+ SIGNAL-HOWTO for more information.
+ * purple_whiteboard_create renamed to purple_whiteboard_new
+ * purple_xfer_get_bytes_remaining now returns a goffset
+ * purple_xfer_get_bytes_sent now returns a goffset
+ * purple_xfer_get_size now returns a goffset
+ * purple_xfer_is_canceled renamed to purple_xfer_is_cancelled
+ * PurpleXfer is now a GObject. Please see the documentation for details.
+ * purple_xfer_get_type renamed to purple_xfer_get_xfer_type
+ * PurpleXferStatusType renamed to PurpleXferStatus
+ * PURPLE_XFER_* prefix of PurpleXferType enums changed to
+ PURPLE_XFER_TYPE_*
+ * purple_xfer_set_bytes_sent now takes a goffset as the bytes_sent
+ parameter
+ * purple_xfer_set_size now takes a goffset as the size parameter
+ * PurpleCertificateVerificationStatus enumeration is now merged with
+ internal flags, thus removing PURPLE_CERTIFICATE_INVALID and
+ replacing it with more precise errors.
+ * PurpleConnectionUiOps.report_disconnect now passes a
+ PurpleConnectionError as the second parameter
+ * PurpleXfer.bytes_remaining is now a goffset
+ * PurpleXfer.bytes_sent is now a goffset
+ * PurpleXfer.size is now a goffset
+ * PurpleCertificateScheme.get_times now uses gint64 instead of
+ time_t to represent times
+ * purple_certificate_get_times now uses gint64 instead of
+ time_t to represent times
+ * xmlnode renamed to PurpleXmlNode
+ * XMLNodeType renamed to PurpleXmlNodeType
+ * xmlnode_* functions are now purple_xmlnode_*
+
+ Removed:
+ * buddy-added and buddy-removed blist signals
+ * privacy.h file
+ * value.h file
+ * _PurpleCipherBatchMode
+ * _PurpleCmdFlag
+ * _PurpleCmdPriority
+ * _PurpleCmdRet
+ * _PurpleCmdStatus
+ * _PurplePrefType
+ * _PurplePrivacyType
+ * _PurpleSoundEventID
+ * _XMLNodeType
+ * account-authorization-requested-with-message signal
+ * purple_account_add_buddies_with_invite
+ * purple_account_add_buddy_with_invite
+ * purple_account_set_current_error
+ * purple_blist_load
+ * purple_blist_new
+ * purple_set_blist
+ * purple_blist_update_buddy_icon
+ * purple_buddy_get_local_alias
+ * purple_buddy_icons_has_custom_icon
+ * purple_buddy_icons_find_custom_icon
+ * purple_buddy_icons_set_custom_icon
+ * purple_certificate_check_signature_chain_with_failing. Use
+ purple_certificate_check_signature_chain, instead
+ * purple_certificate_display_x509. Use purple_request_certificate,
+ instead
+ * PurpleCipherContext
+ * purple_cipher_context_destroy
+ * purple_cipher_context_get_data
+ * purple_cipher_context_get_option
+ * purple_cipher_context_new
+ * purple_cipher_context_new_by_name
+ * purple_cipher_context_set_data
+ * purple_cipher_context_set_option
+ * purple_cipher_get_capabilities
+ * purple_cipher_get_name
+ * PurpleCipherOps
+ * purple_ciphers_*
+ * PurpleCipher's init and uninit operations
+ * purple_connection_error_reason
+ * purple_connection_new
+ * purple_connection_new_unregister
+ * purple_connection_destroy
+ * purple_connection_set_account
+ * purple_contact_set_alias
+ * purple_conv_chat_set_users
+ * PurpleConversationType
+ * purple_core_migrate
+ * purple_dnsquery_a_account
+ * purple_network_listen_family. Use purple_network_listen, instead.
+ * purple_network_listen_map_external
+ * purple_network_listen_range_family. Use purple_network_listen,
+ instead.
+ * purple_network_ip_atoi
+ * purple_notify_searchresults_column_get_title
+ * purple_notify_searchresults_get_columns_count
+ * purple_notify_searchresults_get_rows_count
+ * purple_notify_searchresults_row_get
+ * purple_plugins_register_load_notify_cb
+ * purple_plugins_register_probe_notify_cb
+ * purple_plugins_register_unload_notify_cb
+ * purple_plugins_unregister_load_notify_cb
+ * purple_plugins_unregister_probe_notify_cb
+ * purple_plugins_unregister_unload_notify_cb
+ * purple_pounces_load
+ * purple_prefs_set_generic
+ * purple_prefs_update_old
+ * purple_presence_add_status
+ * purple_presence_add_list
+ * PurplePresenceContext
+ * purple_presence_new
+ * purple_presence_new_for_account
+ * purple_presence_new_for_buddy
+ * purple_proxy_connect_socks5
+ * purple_request_field_list_add
+ * purple_srv_cancel
+ * purple_srv_resolve_account
+ * purple_ssl_connect_fd
+ * purple_status_set_attr_boolean
+ * purple_status_set_attr_int
+ * purple_status_set_attr_string
+ * purple_status_type_add_attr
+ * purple_status_type_add_attrs
+ * purple_status_type_add_attrs_vargs
+ * purple_status_type_get_primary_attr
+ * purple_status_type_set_primary_attr
+ * purple_strlcat
+ * purple_strlcpy
+ * purple_txt_cancel
+ * purple_txt_resolve_account
+ * PurpleType, use GType instead.
+ * purple_util_fetch_url_len. Use purple_util_fetch_url, instead.
+ * purple_util_fetch_url_request_len. Use
+ * purple_util_fetch_url_request, instead.
+ * purple_util_fetch_url_request_len_with_account. Use
+ purple_util_fetch_url_request, instead.
+ * purple_util_get_image_checksum. Use
+ g_compute_checksum_for_data(G_CHECKSUM_SHA1, ...), instead.
+ * PurpleCertificateVerificationStatus.PURPLE_CERTIFICATE_INVALID
+ * PurpleConnectionUiOps.report_disconnect_reason
+ * PurplePluginProtocolInfo.add_buddy_with_invite
+ * PurplePluginProtocolInfo.add_buddies_with_invite
+ * PurplePluginProtocolInfo.get_cb_away
+ * PurpleValue, use GValue instead.
+ * serv_got_attention
+ * serv_send_attention
+ * struct _PurpleAttentionType
+ * struct _PurpleCipherCaps
+ * struct _PurpleConversation
+ * struct _PurpleConvChat
+ * struct _PurpleConvChatBuddy
+ * struct _PurpleConvIm
+ * struct _PurpleConvMessage
+ * struct _PurpleMenuAction
+ * struct _PurplePounce
+ * struct _PurpleProxyInfo
+ * struct _PurpleRequestField
+ * struct _PurpleRoomlist
+ * struct _PurpleRoomlistField
+ * struct _PurpleRoomlistRoom
+ * struct _PurpleWhiteboard
+ * struct PurpleAccountOption
+ * struct PurpleAccountUserSplit
+ * struct PurpleNotifySearchColumn
+ * wpurple_g_access
+ * xmlnode_set_attrib_with_namespace
+ * xmlnode_set_attrib_with_prefix
+
+ Pidgin:
+ Added:
+ * pidgin_create_webview
+ * PidginDockletFlag
+ * Various WebKit-related functions in gtkwebview.h
+
+ Changed:
+ * gtkft.h file renamed to gtkxfer.h
+ * pidgin_blist_sort_method renamed to _PidginBlistSortMethod
+ * BRUSH_STATE_DOWN renamed to PIDGIN_BRUSH_STATE_DOWN
+ * BRUSH_STATE_MOTION renamed to PIDGIN_BRUSH_STATE_MOTION
+ * BRUSH_STATE_UP renamed to PIDGIN_BRUSH_STATE_UP
+ * DEFAULT_FONT_FACE renamed to PIDGIN_DEFAULT_FONT_FACE
+ * DndHintPosition renamed to PidginDndHintPosition
+ * DndHintWindowId renamed to PidginDndHintWindowId
+ * dnd_* functions renamed to pidgin_dnd_*
+ * 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_setup_screenname_autocomplete now takes a filter function and
+ its data as final two arguments
+ * smiley_list renamed to PidginSmileyList
+ * smiley_parse_markup renamed to pidgin_smiley_parse_markup
+ * smiley_theme renamed to PidginSmileyTheme
+ * PidginWindow renamed to PidginConvWindow
+
+ Removed:
+ * GtkIMHtml.clipboard_html_string
+ * GtkIMHtml.clipboard_text_string
+ * GtkIMHtmlFontDetail
+ * gtk_imhtml_animation_free
+ * gtk_imhtml_animation_new
+ * gtk_imhtml_image_add_to
+ * gtk_imhtml_image_free
+ * gtk_imhtml_image_new
+ * gtk_imhtml_image_scale
+ * pidgin_blist_update_account_error_state
+ * PidginBuddyList.connection_errors
+ * pidgin_check_if_dir
+ * PidginConversation.sg
+ * PIDGIN_DIALOG
+ * pidgin_dialogs_alias_contact
+ * pidgin_mini_dialog_links_supported
+ * pidgin_set_custom_buddy_icon
+ * pidgin_setup_screenname_autocomplete_with_filter
+ * struct _GtkIMHtmlAnimation
+ * struct _GtkIMHtmlFontDetail
+ * struct _GtkIMHtmlHr
+ * struct _GtkIMHtmlImage
+ * struct _GtkIMHtmlScalable
+ * struct _GtkSmileyTree
+ * struct _PidginChatPane
+ * struct _PidginImPane
+
+ Finch:
+ Changed:
+ * gntft.h file renamed to gntxfer.h
+ * gnt_append_menu_action renamed to finch_append_menu_action
+ * gnt_ui_init renamed to finch_ui_init
+ * gnt_ui_uninit renamed to finch_ui_uninit
+
+ libgnt:
+ Changed:
+ * ENTRY_CHAR renamed to GNT_ENTRY_CHAR
+ * g_hash_table_duplicate renamed to gnt_hash_table_duplicate
+ * GDupFunc renamed to GntDuplicateFunc
+
+ Removed:
+ * _GntFileType
+ * _GntKeyPressMode
+ * _GntMouseEvent
+ * _GntParamFlags
+ * _GntProgressBarOrientation
+ * _GntTreeColumnFlag
+ * _GntWidgetFlags
version 2.10.9:
* No changes
diff --git a/Doxyfile.in b/Doxyfile.in
deleted file mode 100644
index 5373b79e0b..0000000000
--- a/Doxyfile.in
+++ /dev/null
@@ -1,1646 +0,0 @@
-# Doxyfile 1.7.1
-
-# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project
-#
-# All text after a hash (#) is considered a comment and will be ignored
-# The format is:
-# TAG = value [value, ...]
-# For lists items can also be appended using:
-# TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ")
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all
-# text before the first occurrence of this tag. Doxygen uses libiconv (or the
-# iconv built into libc) for the transcoding. See
-# http://www.gnu.org/software/libiconv for the list of possible encodings.
-
-DOXYFILE_ENCODING = UTF-8
-
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
-# by quotes) that should identify the project.
-
-PROJECT_NAME = @PACKAGE@
-
-# The PROJECT_NUMBER tag can be used to enter a project or revision number.
-# This could be handy for archiving the generated documentation or
-# if some version control system is used.
-
-PROJECT_NUMBER = @VERSION@
-
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
-# base path where the generated documentation will be put.
-# If a relative path is entered, it will be relative to the location
-# where doxygen was started. If left blank the current directory will be used.
-
-OUTPUT_DIRECTORY = doc
-
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
-# 4096 sub-directories (in 2 levels) under the output directory of each output
-# format and will distribute the generated files over these directories.
-# Enabling this option can be useful when feeding doxygen a huge amount of
-# source files, where putting all generated files in the same directory would
-# otherwise cause performance problems for the file system.
-
-CREATE_SUBDIRS = NO
-
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all constant output in the proper language.
-# The default language is English, other supported languages are:
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
-# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
-# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
-# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
-# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
-# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
-
-OUTPUT_LANGUAGE = English
-
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
-# include brief member descriptions after the members that are listed in
-# the file and class documentation (similar to JavaDoc).
-# Set to NO to disable this.
-
-BRIEF_MEMBER_DESC = YES
-
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
-# the brief description of a member or function before the detailed description.
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
-# brief descriptions will be completely suppressed.
-
-REPEAT_BRIEF = YES
-
-# This tag implements a quasi-intelligent brief description abbreviator
-# that is used to form the text in various listings. Each string
-# in this list, if found as the leading text of the brief description, will be
-# stripped from the text and the result after processing the whole list, is
-# used as the annotated text. Otherwise, the brief description is used as-is.
-# If left blank, the following values are used ("$name" is automatically
-# replaced with the name of the entity): "The $name class" "The $name widget"
-# "The $name file" "is" "provides" "specifies" "contains"
-# "represents" "a" "an" "the"
-
-ABBREVIATE_BRIEF =
-
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# Doxygen will generate a detailed section even if there is only a brief
-# description.
-
-ALWAYS_DETAILED_SEC = NO
-
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
-# inherited members of a class in the documentation of that class as if those
-# members were ordinary class members. Constructors, destructors and assignment
-# operators of the base classes will not be shown.
-
-INLINE_INHERITED_MEMB = NO
-
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
-# path before files name in the file list and in the header files. If set
-# to NO the shortest path that makes the file name unique will be used.
-
-FULL_PATH_NAMES = NO
-
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
-# can be used to strip a user-defined part of the path. Stripping is
-# only done if one of the specified strings matches the left-hand part of
-# the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the
-# path to strip.
-
-STRIP_FROM_PATH =
-
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
-# the path mentioned in the documentation of a class, which tells
-# the reader which header file to include in order to use a class.
-# If left blank only the name of the header file containing the class
-# definition is used. Otherwise one should specify the include paths that
-# are normally passed to the compiler using the -I flag.
-
-STRIP_FROM_INC_PATH =
-
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
-# (but less readable) file names. This can be useful is your file systems
-# doesn't support long names like on DOS, Mac, or CD-ROM.
-
-SHORT_NAMES = NO
-
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
-# will interpret the first line (until the first dot) of a JavaDoc-style
-# comment as the brief description. If set to NO, the JavaDoc
-# comments will behave just like regular Qt-style comments
-# (thus requiring an explicit @brief command for a brief description.)
-
-JAVADOC_AUTOBRIEF = YES
-
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
-# interpret the first line (until the first dot) of a Qt-style
-# comment as the brief description. If set to NO, the comments
-# will behave just like regular Qt-style comments (thus requiring
-# an explicit \brief command for a brief description.)
-
-QT_AUTOBRIEF = NO
-
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
-# treat a multi-line C++ special comment block (i.e. a block of //! or ///
-# comments) as a brief description. This used to be the default behaviour.
-# The new default is to treat a multi-line C++ comment block as a detailed
-# description. Set this tag to YES if you prefer the old behaviour instead.
-
-MULTILINE_CPP_IS_BRIEF = NO
-
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
-# member inherits the documentation from any documented member that it
-# re-implements.
-
-INHERIT_DOCS = YES
-
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
-# a new page for each member. If set to NO, the documentation of a member will
-# be part of the file/class/namespace that contains it.
-
-SEPARATE_MEMBER_PAGES = NO
-
-# The TAB_SIZE tag can be used to set the number of spaces in a tab.
-# Doxygen uses this value to replace tabs by spaces in code fragments.
-
-TAB_SIZE = 4
-
-# This tag can be used to specify a number of aliases that acts
-# as commands in the documentation. An alias has the form "name=value".
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to
-# put the command \sideeffect (or @sideeffect) in the documentation, which
-# will result in a user-defined paragraph with heading "Side Effects:".
-# You can put \n's in the value part of an alias to insert newlines.
-
-ALIASES = "signal=- @ref " \
- "signaldef=@section " \
- "endsignaldef= " \
- "signalproto=@code " \
- "endsignalproto=@endcode " \
- "signaldesc=@par Description: " \
- "signals=@b Signals: " \
- "endsignals= " \
- "constreturn=@note The return value of this function must not be modified or freed. @return "
-
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
-# sources only. Doxygen will then generate output that is more tailored for C.
-# For instance, some of the names that are used will be different. The list
-# of all members will be omitted, etc.
-
-OPTIMIZE_OUTPUT_FOR_C = YES
-
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
-# sources only. Doxygen will then generate output that is more tailored for
-# Java. For instance, namespaces will be presented as packages, qualified
-# scopes will look different, etc.
-
-OPTIMIZE_OUTPUT_JAVA = NO
-
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources only. Doxygen will then generate output that is more tailored for
-# Fortran.
-
-OPTIMIZE_FOR_FORTRAN = NO
-
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for
-# VHDL.
-
-OPTIMIZE_OUTPUT_VHDL = NO
-
-# Doxygen selects the parser to use depending on the extension of the files it
-# parses. With this tag you can assign which parser to use for a given extension.
-# Doxygen has a built-in mapping, but you can override or extend it using this
-# tag. The format is ext=language, where ext is a file extension, and language
-# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
-# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
-# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
-# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
-# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
-
-EXTENSION_MAPPING =
-
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should
-# set this tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
-# func(std::string) {}). This also make the inheritance and collaboration
-# diagrams that involve STL classes more complete and accurate.
-
-BUILTIN_STL_SUPPORT = NO
-
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
-# enable parsing support.
-
-CPP_CLI_SUPPORT = NO
-
-# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
-# Doxygen will parse them like normal C++ but will assume all classes use public
-# instead of private inheritance when no explicit protection keyword is present.
-
-SIP_SUPPORT = NO
-
-# For Microsoft's IDL there are propget and propput attributes to indicate getter
-# and setter methods for a property. Setting this option to YES (the default)
-# will make doxygen to replace the get and set methods by a property in the
-# documentation. This will only work if the methods are indeed getting or
-# setting a simple type. If this is not the case, or you want to show the
-# methods anyway, you should set this option to NO.
-
-IDL_PROPERTY_SUPPORT = YES
-
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES, then doxygen will reuse the documentation of the first
-# member in the group (if any) for the other members of the group. By default
-# all members of a group must be documented explicitly.
-
-DISTRIBUTE_GROUP_DOC = NO
-
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
-# the same type (for instance a group of public functions) to be put as a
-# subgroup of that type (e.g. under the Public Functions section). Set it to
-# NO to prevent subgrouping. Alternatively, this can be done per class using
-# the \nosubgrouping command.
-
-SUBGROUPING = YES
-
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
-# is documented as struct, union, or enum with the name of the typedef. So
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
-# with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically
-# be useful for C code in case the coding convention dictates that all compound
-# types are typedef'ed and only the typedef is referenced, never the tag name.
-
-TYPEDEF_HIDES_STRUCT = NO
-
-# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
-# determine which symbols to keep in memory and which to flush to disk.
-# When the cache is full, less often used symbols will be written to disk.
-# For small to medium size projects (<1000 input files) the default value is
-# probably good enough. For larger projects a too small cache size can cause
-# doxygen to be busy swapping symbols to and from disk most of the time
-# causing a significant performance penality.
-# If the system has enough physical memory increasing the cache will improve the
-# performance by keeping more symbols in memory. Note that the value works on
-# a logarithmic scale so increasing the size by one will rougly double the
-# memory usage. The cache size is given by this formula:
-# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
-# corresponding to a cache size of 2^16 = 65536 symbols
-
-SYMBOL_CACHE_SIZE = 0
-
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
-
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
-# documentation are documented, even if no documentation was available.
-# Private class members and static file members will be hidden unless
-# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
-
-EXTRACT_ALL = NO
-
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
-# will be included in the documentation.
-
-EXTRACT_PRIVATE = NO
-
-# If the EXTRACT_STATIC tag is set to YES all static members of a file
-# will be included in the documentation.
-
-EXTRACT_STATIC = NO
-
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
-# defined locally in source files will be included in the documentation.
-# If set to NO only classes defined in header files are included.
-
-EXTRACT_LOCAL_CLASSES = YES
-
-# This flag is only useful for Objective-C code. When set to YES local
-# methods, which are defined in the implementation section but not in
-# the interface are included in the documentation.
-# If set to NO (the default) only methods in the interface are included.
-
-EXTRACT_LOCAL_METHODS = YES
-
-# If this flag is set to YES, the members of anonymous namespaces will be
-# extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base
-# name of the file that contains the anonymous namespace. By default
-# anonymous namespace are hidden.
-
-EXTRACT_ANON_NSPACES = NO
-
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
-# undocumented members of documented classes, files or namespaces.
-# If set to NO (the default) these members will be included in the
-# various overviews, but no documentation section is generated.
-# This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_MEMBERS = NO
-
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy.
-# If set to NO (the default) these classes will be included in the various
-# overviews. This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_CLASSES = NO
-
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
-# friend (class|struct|union) declarations.
-# If set to NO (the default) these declarations will be included in the
-# documentation.
-
-HIDE_FRIEND_COMPOUNDS = NO
-
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
-# documentation blocks found inside the body of a function.
-# If set to NO (the default) these blocks will be appended to the
-# function's detailed documentation block.
-
-HIDE_IN_BODY_DOCS = NO
-
-# The INTERNAL_DOCS tag determines if documentation
-# that is typed after a \internal command is included. If the tag is set
-# to NO (the default) then the documentation will be excluded.
-# Set it to YES to include the internal documentation.
-
-INTERNAL_DOCS = NO
-
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
-# file names in lower-case letters. If set to YES upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# and Mac users are advised to set this option to NO.
-
-CASE_SENSE_NAMES = YES
-
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
-# will show members with their full class and namespace scopes in the
-# documentation. If set to YES the scope will be hidden.
-
-HIDE_SCOPE_NAMES = NO
-
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
-# will put a list of the files that are included by a file in the documentation
-# of that file.
-
-SHOW_INCLUDE_FILES = YES
-
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
-# will list include files with double quotes in the documentation
-# rather than with sharp brackets.
-
-FORCE_LOCAL_INCLUDES = NO
-
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
-# is inserted in the documentation for inline members.
-
-INLINE_INFO = YES
-
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
-# will sort the (detailed) documentation of file and class members
-# alphabetically by member name. If set to NO the members will appear in
-# declaration order.
-
-SORT_MEMBER_DOCS = YES
-
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
-# brief documentation of file, namespace and class members alphabetically
-# by member name. If set to NO (the default) the members will appear in
-# declaration order.
-
-SORT_BRIEF_DOCS = NO
-
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
-# will sort the (brief and detailed) documentation of class members so that
-# constructors and destructors are listed first. If set to NO (the default)
-# the constructors will appear in the respective orders defined by
-# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
-# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
-# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
-
-SORT_MEMBERS_CTORS_1ST = NO
-
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
-# hierarchy of group names into alphabetical order. If set to NO (the default)
-# the group names will appear in their defined order.
-
-SORT_GROUP_NAMES = NO
-
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
-# sorted by fully-qualified names, including namespaces. If set to
-# NO (the default), the class list will be sorted only by class name,
-# not including the namespace part.
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the
-# alphabetical list.
-
-SORT_BY_SCOPE_NAME = YES
-
-# The GENERATE_TODOLIST tag can be used to enable (YES) or
-# disable (NO) the todo list. This list is created by putting \todo
-# commands in the documentation.
-
-GENERATE_TODOLIST = YES
-
-# The GENERATE_TESTLIST tag can be used to enable (YES) or
-# disable (NO) the test list. This list is created by putting \test
-# commands in the documentation.
-
-GENERATE_TESTLIST = YES
-
-# The GENERATE_BUGLIST tag can be used to enable (YES) or
-# disable (NO) the bug list. This list is created by putting \bug
-# commands in the documentation.
-
-GENERATE_BUGLIST = YES
-
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
-# disable (NO) the deprecated list. This list is created by putting
-# \deprecated commands in the documentation.
-
-GENERATE_DEPRECATEDLIST= YES
-
-# The ENABLED_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if sectionname ... \endif.
-
-ENABLED_SECTIONS =
-
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
-# the initial value of a variable or define consists of for it to appear in
-# the documentation. If the initializer consists of more lines than specified
-# here it will be hidden. Use a value of 0 to hide initializers completely.
-# The appearance of the initializer of individual variables and defines in the
-# documentation can be controlled using \showinitializer or \hideinitializer
-# command in the documentation regardless of this setting.
-
-MAX_INITIALIZER_LINES = 30
-
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
-# at the bottom of the documentation of classes and structs. If set to YES the
-# list will mention the files that were used to generate the documentation.
-
-SHOW_USED_FILES = YES
-
-# If the sources in your project are distributed over multiple directories
-# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
-# in the documentation. The default is NO.
-
-SHOW_DIRECTORIES = YES
-
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
-# This will remove the Files entry from the Quick Index and from the
-# Folder Tree View (if specified). The default is YES.
-
-SHOW_FILES = YES
-
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
-# Namespaces page.
-# This will remove the Namespaces entry from the Quick Index
-# and from the Folder Tree View (if specified). The default is YES.
-
-SHOW_NAMESPACES = YES
-
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that
-# doxygen should invoke to get the current version for each file (typically from
-# the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command <command> <input-file>, where <command> is the value of
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
-# provided by doxygen. Whatever the program writes to standard output
-# is used as the file version. See the manual for examples.
-
-FILE_VERSION_FILTER =
-
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
-# by doxygen. The layout file controls the global structure of the generated
-# output files in an output format independent way. The create the layout file
-# that represents doxygen's defaults, run doxygen with the -l option.
-# You can optionally specify a file name after the option, if omitted
-# DoxygenLayout.xml will be used as the name of the layout file.
-
-LAYOUT_FILE =
-
-#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-
-# The QUIET tag can be used to turn on/off the messages that are generated
-# by doxygen. Possible values are YES and NO. If left blank NO is used.
-
-QUIET = NO
-
-# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated by doxygen. Possible values are YES and NO. If left blank
-# NO is used.
-
-WARNINGS = YES
-
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
-# automatically be disabled.
-
-WARN_IF_UNDOCUMENTED = NO
-
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some
-# parameters in a documented function, or documenting parameters that
-# don't exist or using markup commands wrongly.
-
-WARN_IF_DOC_ERROR = YES
-
-# This WARN_NO_PARAMDOC option can be abled to get warnings for
-# functions that are documented, but have no documentation for their parameters
-# or return value. If set to NO (the default) doxygen will only warn about
-# wrong or incomplete parameter documentation, but not about the absence of
-# documentation.
-
-WARN_NO_PARAMDOC = YES
-
-# The WARN_FORMAT tag determines the format of the warning messages that
-# doxygen can produce. The string should contain the $file, $line, and $text
-# tags, which will be replaced by the file and line number from which the
-# warning originated and the warning text. Optionally the format may contain
-# $version, which will be replaced by the version of the file (if it could
-# be obtained via FILE_VERSION_FILTER)
-
-WARN_FORMAT = "$file:$line: $text "
-
-# The WARN_LOGFILE tag can be used to specify a file to which warning
-# and error messages should be written. If left blank the output is written
-# to stderr.
-
-WARN_LOGFILE =
-
-#---------------------------------------------------------------------------
-# configuration options related to the input files
-#---------------------------------------------------------------------------
-
-# The INPUT tag can be used to specify the files and/or directories that contain
-# documented source files. You may enter file names like "myfile.cpp" or
-# directories like "/usr/src/myproject". Separate the files or directories
-# with spaces.
-
-INPUT = @top_srcdir@/libpurple \
- @top_srcdir@/finch \
- @top_srcdir@/finch/libgnt \
- @top_srcdir@/pidgin \
- @top_srcdir@/doc
-
-# This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
-# also the default input encoding. Doxygen uses libiconv (or the iconv built
-# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
-# the list of possible encodings.
-
-INPUT_ENCODING = UTF-8
-
-# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
-# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
-
-FILE_PATTERNS = *.h \
- *.dox
-
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories
-# should be searched for input files as well. Possible values are YES and NO.
-# If left blank NO is used.
-
-RECURSIVE = NO
-
-# The EXCLUDE tag can be used to specify files and/or directories that should
-# excluded from the INPUT source files. This way you can easily exclude a
-# subdirectory from a directory tree whose root is specified with the INPUT tag.
-
-EXCLUDE = libpurple/purple-client.h \
- libpurple/purple-client-bindings.h
-
-# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
-# directories that are symbolic links (a Unix filesystem feature) are excluded
-# from the input.
-
-EXCLUDE_SYMLINKS = NO
-
-# If the value of the INPUT tag contains directories, you can use the
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories. Note that the wildcards are matched
-# against the file with absolute path, so to exclude all test directories
-# for example use the pattern */test/*
-
-EXCLUDE_PATTERNS =
-
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
-# (namespaces, classes, functions, etc.) that should be excluded from the
-# output. The symbol name can be a fully qualified name, a word, or if the
-# wildcard * is used, a substring. Examples: ANamespace, AClass,
-# AClass::ANamespace, ANamespace::*Test
-
-EXCLUDE_SYMBOLS =
-
-# The EXAMPLE_PATH tag can be used to specify one or more files or
-# directories that contain example code fragments that are included (see
-# the \include command).
-
-EXAMPLE_PATH =
-
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank all files are included.
-
-EXAMPLE_PATTERNS =
-
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude
-# commands irrespective of the value of the RECURSIVE tag.
-# Possible values are YES and NO. If left blank NO is used.
-
-EXAMPLE_RECURSIVE = NO
-
-# The IMAGE_PATH tag can be used to specify one or more files or
-# directories that contain image that are included in the documentation (see
-# the \image command).
-
-IMAGE_PATH =
-
-# The INPUT_FILTER tag can be used to specify a program that doxygen should
-# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command <filter> <input-file>, where <filter>
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
-# input file. Doxygen will then use the output that the filter program writes
-# to standard output.
-# If FILTER_PATTERNS is specified, this tag will be
-# ignored.
-
-INPUT_FILTER =
-
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis.
-# Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match.
-# The filters are a list of the form:
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
-# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
-# is applied to all files.
-
-FILTER_PATTERNS =
-
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will be used to filter the input files when producing source
-# files to browse (i.e. when SOURCE_BROWSER is set to YES).
-
-FILTER_SOURCE_FILES = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to source browsing
-#---------------------------------------------------------------------------
-
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will
-# be generated. Documented entities will be cross-referenced with these sources.
-# Note: To get rid of all source code in the generated output, make sure also
-# VERBATIM_HEADERS is set to NO.
-
-SOURCE_BROWSER = YES
-
-# Setting the INLINE_SOURCES tag to YES will include the body
-# of functions and classes directly in the documentation.
-
-INLINE_SOURCES = NO
-
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
-# doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C and C++ comments will always remain visible.
-
-STRIP_CODE_COMMENTS = YES
-
-# If the REFERENCED_BY_RELATION tag is set to YES
-# then for each documented function all documented
-# functions referencing it will be listed.
-
-REFERENCED_BY_RELATION = YES
-
-# If the REFERENCES_RELATION tag is set to YES
-# then for each documented function all documented entities
-# called/used by that function will be listed.
-
-REFERENCES_RELATION = YES
-
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
-# link to the source code.
-# Otherwise they will link to the documentation.
-
-REFERENCES_LINK_SOURCE = YES
-
-# If the USE_HTAGS tag is set to YES then the references to source code
-# will point to the HTML generated by the htags(1) tool instead of doxygen
-# built-in source browser. The htags tool is part of GNU's global source
-# tagging system (see http://www.gnu.org/software/global/global.html). You
-# will need version 4.8.6 or higher.
-
-USE_HTAGS = NO
-
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
-# will generate a verbatim copy of the header file for each class for
-# which an include is specified. Set to NO to disable this.
-
-VERBATIM_HEADERS = YES
-
-#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
-# of all compounds will be generated. Enable this if the project
-# contains a lot of classes, structs, unions or interfaces.
-
-ALPHABETICAL_INDEX = YES
-
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
-# in which this list will be split (can be a number in the range [1..20])
-
-COLS_IN_ALPHA_INDEX = 3
-
-# In case all classes in a project start with a common prefix, all
-# classes will be put under the same header in the alphabetical index.
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
-# should be ignored while generating the index headers.
-
-IGNORE_PREFIX = Purple \
- _Purple \
- purple
-
-#---------------------------------------------------------------------------
-# configuration options related to the HTML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
-# generate HTML output.
-
-GENERATE_HTML = YES
-
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `html' will be used as the default path.
-
-HTML_OUTPUT = html
-
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
-# doxygen will generate files with .html extension.
-
-HTML_FILE_EXTENSION = .html
-
-# The HTML_HEADER tag can be used to specify a personal HTML header for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard header.
-
-HTML_HEADER = @top_srcdir@/doc/TracHeader.html
-
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard footer.
-
-HTML_FOOTER = @top_srcdir@/doc/TracFooter.html
-
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
-# style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If the tag is left blank doxygen
-# will generate a default style sheet. Note that doxygen will try to copy
-# the style sheet file to the HTML output directory, so don't put your own
-# stylesheet in the HTML output directory as well, or it will be erased!
-
-HTML_STYLESHEET =
-
-# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
-# Doxygen will adjust the colors in the stylesheet and background images
-# according to this color. Hue is specified as an angle on a colorwheel,
-# see http://en.wikipedia.org/wiki/Hue for more information.
-# For instance the value 0 represents red, 60 is yellow, 120 is green,
-# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
-# The allowed range is 0 to 359.
-
-HTML_COLORSTYLE_HUE = 220
-
-# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
-# the colors in the HTML output. For a value of 0 the output will use
-# grayscales only. A value of 255 will produce the most vivid colors.
-
-HTML_COLORSTYLE_SAT = 100
-
-# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
-# the luminance component of the colors in the HTML output. Values below
-# 100 gradually make the output lighter, whereas values above 100 make
-# the output darker. The value divided by 100 is the actual gamma applied,
-# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
-# and 100 does not change the gamma.
-
-HTML_COLORSTYLE_GAMMA = 80
-
-# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
-# page will contain the date and time when the page was generated. Setting
-# this to NO can help when comparing the output of multiple runs.
-
-HTML_TIMESTAMP = YES
-
-# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
-# files or namespaces will be aligned in HTML using tables. If set to
-# NO a bullet list will be used.
-
-HTML_ALIGN_MEMBERS = YES
-
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
-# documentation will contain sections that can be hidden and shown after the
-# page has loaded. For this to work a browser that supports
-# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
-# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
-
-HTML_DYNAMIC_SECTIONS = NO
-
-# If the GENERATE_DOCSET tag is set to YES, additional index files
-# will be generated that can be used as input for Apple's Xcode 3
-# integrated development environment, introduced with OSX 10.5 (Leopard).
-# To create a documentation set, doxygen will generate a Makefile in the
-# HTML output directory. Running make will produce the docset in that
-# directory and running "make install" will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
-# it at startup.
-# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
-# for more information.
-
-GENERATE_DOCSET = NO
-
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
-# feed. A documentation feed provides an umbrella under which multiple
-# documentation sets from a single provider (such as a company or product suite)
-# can be grouped.
-
-DOCSET_FEEDNAME = "Doxygen generated docs"
-
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
-# should uniquely identify the documentation set bundle. This should be a
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
-# will append .docset to the name.
-
-DOCSET_BUNDLE_ID = org.doxygen.Project
-
-# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
-# the documentation publisher. This should be a reverse domain-name style
-# string, e.g. com.mycompany.MyDocSet.documentation.
-
-DOCSET_PUBLISHER_ID = org.doxygen.Publisher
-
-# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
-
-DOCSET_PUBLISHER_NAME = Publisher
-
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files
-# will be generated that can be used as input for tools like the
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
-# of the generated HTML documentation.
-
-GENERATE_HTMLHELP = YES
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
-# be used to specify the file name of the resulting .chm file. You
-# can add a path in front of the file if the result should not be
-# written to the html output directory.
-
-CHM_FILE =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
-# be used to specify the location (absolute path including file name) of
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
-# the HTML help compiler on the generated index.hhp.
-
-HHC_LOCATION =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
-# controls if a separate .chi index file is generated (YES) or that
-# it should be included in the master .chm file (NO).
-
-GENERATE_CHI = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file
-# content.
-
-CHM_INDEX_ENCODING =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
-# controls whether a binary table of contents is generated (YES) or a
-# normal table of contents (NO) in the .chm file.
-
-BINARY_TOC = NO
-
-# The TOC_EXPAND flag can be set to YES to add extra items for group members
-# to the contents of the HTML help documentation and to the tree view.
-
-TOC_EXPAND = YES
-
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
-# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
-# that can be used as input for Qt's qhelpgenerator to generate a
-# Qt Compressed Help (.qch) of the generated HTML documentation.
-
-GENERATE_QHP = NO
-
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
-# be used to specify the file name of the resulting .qch file.
-# The path specified is relative to the HTML output folder.
-
-QCH_FILE =
-
-# The QHP_NAMESPACE tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#namespace
-
-QHP_NAMESPACE = org.doxygen.Project
-
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#virtual-folders
-
-QHP_VIRTUAL_FOLDER = doc
-
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
-# add. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#custom-filters
-
-QHP_CUST_FILTER_NAME =
-
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
-# custom filter to add. For more information please see
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
-# Qt Help Project / Custom Filters</a>.
-
-QHP_CUST_FILTER_ATTRS =
-
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
-# project's
-# filter section matches.
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
-# Qt Help Project / Filter Attributes</a>.
-
-QHP_SECT_FILTER_ATTRS =
-
-# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
-# be used to specify the location of Qt's qhelpgenerator.
-# If non-empty doxygen will try to run qhelpgenerator on the generated
-# .qhp file.
-
-QHG_LOCATION =
-
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
-# will be generated, which together with the HTML files, form an Eclipse help
-# plugin. To install this plugin and make it available under the help contents
-# menu in Eclipse, the contents of the directory containing the HTML and XML
-# files needs to be copied into the plugins directory of eclipse. The name of
-# the directory within the plugins directory should be the same as
-# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
-# the help appears.
-
-GENERATE_ECLIPSEHELP = NO
-
-# A unique identifier for the eclipse help plugin. When installing the plugin
-# the directory name containing the HTML and XML files should also have
-# this name.
-
-ECLIPSE_DOC_ID = org.doxygen.Project
-
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
-# top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it.
-
-DISABLE_INDEX = NO
-
-# This tag can be used to set the number of enum values (range [1..20])
-# that doxygen will group on one line in the generated HTML documentation.
-
-ENUM_VALUES_PER_LINE = 4
-
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information.
-# If the tag value is set to YES, a side panel will be generated
-# containing a tree-like index structure (just like the one that
-# is generated for HTML Help). For this to work a browser that supports
-# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
-# Windows users are probably better off using the HTML help feature.
-
-GENERATE_TREEVIEW = YES
-
-# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
-# and Class Hierarchy pages using a tree view instead of an ordered list.
-
-USE_INLINE_TREES = NO
-
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
-# used to set the initial width (in pixels) of the frame in which the tree
-# is shown.
-
-TREEVIEW_WIDTH = 250
-
-# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
-# links to external symbols imported via tag files in a separate window.
-
-EXT_LINKS_IN_WINDOW = NO
-
-# Use this tag to change the font size of Latex formulas included
-# as images in the HTML documentation. The default is 10. Note that
-# when you change the font size after a successful doxygen run you need
-# to manually remove any form_*.png images from the HTML output directory
-# to force them to be regenerated.
-
-FORMULA_FONTSIZE = 10
-
-# Use the FORMULA_TRANPARENT tag to determine whether or not the images
-# generated for formulas are transparent PNGs. Transparent PNGs are
-# not supported properly for IE 6.0, but are supported on all modern browsers.
-# Note that when changing this option you need to delete any form_*.png files
-# in the HTML output before the changes have effect.
-
-FORMULA_TRANSPARENT = YES
-
-# When the SEARCHENGINE tag is enabled doxygen will generate a search box
-# for the HTML output. The underlying search engine uses javascript
-# and DHTML and should work on any modern browser. Note that when using
-# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
-# (GENERATE_DOCSET) there is already a search function so this one should
-# typically be disabled. For large projects the javascript based search engine
-# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
-
-SEARCHENGINE = YES
-
-# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a PHP enabled web server instead of at the web client
-# using Javascript. Doxygen will generate the search PHP script and index
-# file to put on the web server. The advantage of the server
-# based approach is that it scales better to large projects and allows
-# full text search. The disadvances is that it is more difficult to setup
-# and does not have live searching capabilities.
-
-SERVER_BASED_SEARCH = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
-# generate Latex output.
-
-GENERATE_LATEX = NO
-
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `latex' will be used as the default path.
-
-LATEX_OUTPUT = latex
-
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked. If left blank `latex' will be used as the default command name.
-# Note that when enabling USE_PDFLATEX this option is only used for
-# generating bitmaps for formulas in the HTML output, but not in the
-# Makefile that is written to the output directory.
-
-LATEX_CMD_NAME = latex
-
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
-# generate index for LaTeX. If left blank `makeindex' will be used as the
-# default command name.
-
-MAKEINDEX_CMD_NAME = makeindex
-
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
-# LaTeX documents. This may be useful for small projects and may help to
-# save some trees in general.
-
-COMPACT_LATEX = NO
-
-# The PAPER_TYPE tag can be used to set the paper type that is used
-# by the printer. Possible values are: a4, a4wide, letter, legal and
-# executive. If left blank a4wide will be used.
-
-PAPER_TYPE = a4wide
-
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
-# packages that should be included in the LaTeX output.
-
-EXTRA_PACKAGES =
-
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
-# the generated latex document. The header should contain everything until
-# the first chapter. If it is left blank doxygen will generate a
-# standard header. Notice: only use this tag if you know what you are doing!
-
-LATEX_HEADER =
-
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will
-# contain links (just like the HTML output) instead of page references
-# This makes the output suitable for online browsing using a pdf viewer.
-
-PDF_HYPERLINKS = NO
-
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
-# plain latex in the generated Makefile. Set this option to YES to get a
-# higher quality PDF documentation.
-
-USE_PDFLATEX = NO
-
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
-# command to the generated LaTeX files. This will instruct LaTeX to keep
-# running if errors occur, instead of asking the user for help.
-# This option is also used when generating formulas in HTML.
-
-LATEX_BATCHMODE = NO
-
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not
-# include the index chapters (such as File Index, Compound Index, etc.)
-# in the output.
-
-LATEX_HIDE_INDICES = NO
-
-# If LATEX_SOURCE_CODE is set to YES then doxygen will include
-# source code with syntax highlighting in the LaTeX output.
-# Note that which sources are shown also depends on other settings
-# such as SOURCE_BROWSER.
-
-LATEX_SOURCE_CODE = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the RTF output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
-# The RTF output is optimized for Word 97 and may not look very pretty with
-# other RTF readers or editors.
-
-GENERATE_RTF = NO
-
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `rtf' will be used as the default path.
-
-RTF_OUTPUT = rtf
-
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
-# RTF documents. This may be useful for small projects and may help to
-# save some trees in general.
-
-COMPACT_RTF = NO
-
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
-# will contain hyperlink fields. The RTF file will
-# contain links (just like the HTML output) instead of page references.
-# This makes the output suitable for online browsing using WORD or other
-# programs which support those fields.
-# Note: wordpad (write) and others do not support links.
-
-RTF_HYPERLINKS = NO
-
-# Load stylesheet definitions from file. Syntax is similar to doxygen's
-# config file, i.e. a series of assignments. You only have to provide
-# replacements, missing definitions are set to their default value.
-
-RTF_STYLESHEET_FILE =
-
-# Set optional variables used in the generation of an rtf document.
-# Syntax is similar to doxygen's config file.
-
-RTF_EXTENSIONS_FILE =
-
-#---------------------------------------------------------------------------
-# configuration options related to the man page output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
-# generate man pages
-
-GENERATE_MAN = NO
-
-# The MAN_OUTPUT tag is used to specify where the man pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `man' will be used as the default path.
-
-MAN_OUTPUT = man
-
-# The MAN_EXTENSION tag determines the extension that is added to
-# the generated man pages (default is the subroutine's section .3)
-
-MAN_EXTENSION = .3
-
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
-# then it will generate one additional man file for each entity
-# documented in the real man page(s). These additional files
-# only source the real man page, but without them the man command
-# would be unable to find the correct page. The default is NO.
-
-MAN_LINKS = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the XML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_XML tag is set to YES Doxygen will
-# generate an XML file that captures the structure of
-# the code including all documentation.
-
-GENERATE_XML = YES
-
-# The XML_OUTPUT tag is used to specify where the XML pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `xml' will be used as the default path.
-
-XML_OUTPUT = xml
-
-# The XML_SCHEMA tag can be used to specify an XML schema,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-XML_SCHEMA =
-
-# The XML_DTD tag can be used to specify an XML DTD,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-XML_DTD =
-
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
-# dump the program listings (including syntax highlighting
-# and cross-referencing information) to the XML output. Note that
-# enabling this will significantly increase the size of the XML output.
-
-XML_PROGRAMLISTING = YES
-
-#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
-# generate an AutoGen Definitions (see autogen.sf.net) file
-# that captures the structure of the code including all
-# documentation. Note that this feature is still experimental
-# and incomplete at the moment.
-
-GENERATE_AUTOGEN_DEF = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will
-# generate a Perl module file that captures the structure of
-# the code including all documentation. Note that this
-# feature is still experimental and incomplete at the
-# moment.
-
-GENERATE_PERLMOD = NO
-
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able
-# to generate PDF and DVI output from the Perl module output.
-
-PERLMOD_LATEX = NO
-
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
-# nicely formatted so it can be parsed by a human reader.
-# This is useful
-# if you want to understand what is going on.
-# On the other hand, if this
-# tag is set to NO the size of the Perl module output will be much smaller
-# and Perl will parse it just the same.
-
-PERLMOD_PRETTY = YES
-
-# The names of the make variables in the generated doxyrules.make file
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
-# This is useful so different doxyrules.make files included by the same
-# Makefile don't overwrite each other's variables.
-
-PERLMOD_MAKEVAR_PREFIX =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor
-#---------------------------------------------------------------------------
-
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
-# evaluate all C-preprocessor directives found in the sources and include
-# files.
-
-ENABLE_PREPROCESSING = YES
-
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
-# names in the source code. If set to NO (the default) only conditional
-# compilation will be performed. Macro expansion can be done in a controlled
-# way by setting EXPAND_ONLY_PREDEF to YES.
-
-MACRO_EXPANSION = NO
-
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
-# then the macro expansion is limited to the macros specified with the
-# PREDEFINED and EXPAND_AS_DEFINED tags.
-
-EXPAND_ONLY_PREDEF = NO
-
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
-# in the INCLUDE_PATH (see below) will be search if a #include is found.
-
-SEARCH_INCLUDES = YES
-
-# The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by
-# the preprocessor.
-
-INCLUDE_PATH =
-
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
-# patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will
-# be used.
-
-INCLUDE_FILE_PATTERNS =
-
-# The PREDEFINED tag can be used to specify one or more macro names that
-# are defined before the preprocessor is started (similar to the -D option of
-# gcc). The argument of the tag is a list of macros of the form: name
-# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed. To prevent a macro definition from being
-# undefined via #undef or recursively expanded use the := operator
-# instead of the = operator.
-
-PREDEFINED =
-
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
-# this tag can be used to specify a list of macro names that should be expanded.
-# The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition.
-
-EXPAND_AS_DEFINED =
-
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all function-like macros that are alone
-# on a line, have an all uppercase name, and do not end with a semicolon. Such
-# function macros are typically used for boiler-plate code, and will confuse
-# the parser if not removed.
-
-SKIP_FUNCTION_MACROS = YES
-
-#---------------------------------------------------------------------------
-# Configuration::additions related to external references
-#---------------------------------------------------------------------------
-
-# The TAGFILES option can be used to specify one or more tagfiles.
-# Optionally an initial location of the external documentation
-# can be added for each tagfile. The format of a tag file without
-# this location is as follows:
-#
-# TAGFILES = file1 file2 ...
-# Adding location for the tag files is done as follows:
-#
-# TAGFILES = file1=loc1 "file2 = loc2" ...
-# where "loc1" and "loc2" can be relative or absolute paths or
-# URLs. If a location is present for each tag, the installdox tool
-# does not have to be run to correct the links.
-# Note that each tag file must have a unique name
-# (where the name does NOT include the path)
-# If a tag file is not located in the directory in which doxygen
-# is run, you must also specify the path to the tagfile here.
-
-TAGFILES =
-
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create
-# a tag file that is based on the input files it reads.
-
-GENERATE_TAGFILE =
-
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed
-# in the class index. If set to NO only the inherited external classes
-# will be listed.
-
-ALLEXTERNALS = NO
-
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will
-# be listed.
-
-EXTERNAL_GROUPS = YES
-
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of `which perl').
-
-PERL_PATH = /usr/bin/perl
-
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool
-#---------------------------------------------------------------------------
-
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
-# or super classes. Setting the tag to NO turns the diagrams off. Note that
-# this option is superseded by the HAVE_DOT option below. This is only a
-# fallback. It is recommended to install and use dot, since it yields more
-# powerful graphs.
-
-CLASS_DIAGRAMS = YES
-
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see
-# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
-# default search path.
-
-MSCGEN_PATH =
-
-# If set to YES, the inheritance and collaboration graphs will hide
-# inheritance and usage relations if the target is undocumented
-# or is not a class.
-
-HIDE_UNDOC_RELATIONS = YES
-
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz, a graph visualization
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section
-# have no effect if this option is set to NO (the default)
-
-HAVE_DOT = @enable_dot@
-
-# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
-# allowed to run in parallel. When set to 0 (the default) doxygen will
-# base this on the number of processors available in the system. You can set it
-# explicitly to a value larger than 0 to get control over the balance
-# between CPU load and processing speed.
-
-DOT_NUM_THREADS = 0
-
-# By default doxygen will write a font called FreeSans.ttf to the output
-# directory and reference it in all dot files that doxygen generates. This
-# font does not include all possible unicode characters however, so when you need
-# these (or just want a differently looking font) you can specify the font name
-# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
-# which can be done by putting it in a standard location or by setting the
-# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
-# containing the font.
-
-DOT_FONTNAME = FreeSans.ttf
-
-# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
-# The default size is 10pt.
-
-DOT_FONTSIZE = 10
-
-# By default doxygen will tell dot to use the output directory to look for the
-# FreeSans.ttf font (which doxygen will put there itself). If you specify a
-# different font using DOT_FONTNAME you can set the path where dot
-# can find it using this tag.
-
-DOT_FONTPATH =
-
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect inheritance relations. Setting this tag to YES will force the
-# the CLASS_DIAGRAMS tag to NO.
-
-CLASS_GRAPH = YES
-
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect implementation dependencies (inheritance, containment, and
-# class references variables) of the class with other documented classes.
-
-COLLABORATION_GRAPH = YES
-
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for groups, showing the direct groups dependencies
-
-GROUP_GRAPHS = YES
-
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
-# collaboration diagrams in a style similar to the OMG's Unified Modeling
-# Language.
-
-UML_LOOK = YES
-
-# If set to YES, the inheritance and collaboration graphs will show the
-# relations between templates and their instances.
-
-TEMPLATE_RELATIONS = YES
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
-# tags are set to YES then doxygen will generate a graph for each documented
-# file showing the direct and indirect include dependencies of the file with
-# other documented files.
-
-INCLUDE_GRAPH = YES
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
-# documented header file showing the documented files that directly or
-# indirectly include this file.
-
-INCLUDED_BY_GRAPH = YES
-
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then
-# doxygen will generate a call dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable call graphs
-# for selected functions only using the \callgraph command.
-
-CALL_GRAPH = YES
-
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
-# doxygen will generate a caller dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable caller
-# graphs for selected functions only using the \callergraph command.
-
-CALLER_GRAPH = NO
-
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
-# will graphical hierarchy of all classes instead of a textual one.
-
-GRAPHICAL_HIERARCHY = YES
-
-# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
-# then doxygen will show the dependencies a directory has on other directories
-# in a graphical way. The dependency relations are determined by the #include
-# relations between the files in the directories.
-
-DIRECTORY_GRAPH = YES
-
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are png, jpg, or gif
-# If left blank png will be used.
-
-DOT_IMAGE_FORMAT = png
-
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
-# found. If left blank, it is assumed the dot tool can be found in the path.
-
-DOT_PATH =
-
-# The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the
-# \dotfile command).
-
-DOTFILE_DIRS =
-
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
-# nodes that will be shown in the graph. If the number of nodes in a graph
-# becomes larger than this value, doxygen will truncate the graph, which is
-# visualized by representing a node as a red box. Note that doxygen if the
-# number of direct children of the root node in a graph is already larger than
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
-# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
-
-DOT_GRAPH_MAX_NODES = 50
-
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
-# graphs generated by dot. A depth value of 3 means that only nodes reachable
-# from the root by following a path via at most 3 edges will be shown. Nodes
-# that lay further from the root node will be omitted. Note that setting this
-# option to 1 or 2 may greatly reduce the computation time needed for large
-# code bases. Also note that the size of a graph can be further restricted by
-# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
-
-MAX_DOT_GRAPH_DEPTH = 2
-
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, because dot on Windows does not
-# seem to support this out of the box. Warning: Depending on the platform used,
-# enabling this option may lead to badly anti-aliased labels on the edges of
-# a graph (i.e. they become hard to read).
-
-DOT_TRANSPARENT = NO
-
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
-# files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10)
-# support this, this feature is disabled by default.
-
-DOT_MULTI_TARGETS = YES
-
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
-# generate a legend page explaining the meaning of the various boxes and
-# arrows in the dot generated graphs.
-
-GENERATE_LEGEND = YES
-
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
-# remove the intermediate dot files that are used to generate
-# the various graphs.
-
-DOT_CLEANUP = YES
diff --git a/HACKING b/HACKING
index 47e8bfd6c5..24dd470379 100644
--- a/HACKING
+++ b/HACKING
@@ -1,2 +1,2 @@
For information on hacking on Pidgin, Finch, or libpurple, see:
- http://developer.pidgin.im
+ https://developer.pidgin.im
diff --git a/INSTALL b/INSTALL
index 3b01a0d17e..0889d57924 100644
--- a/INSTALL
+++ b/INSTALL
@@ -138,8 +138,6 @@ when it should mark accounts idle based on mouse or keyboard usage.
Doing so will remove the ability to have pidgin start up with your window
manager.
- `--disable-gtkspell' will remove the ability to highlight misspelled words.
-
`--disable-gevolution' will cause the evolution integration plugin not to
compile.
diff --git a/Makefile.am b/Makefile.am
index f0e5c6f063..484ba87e8e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,31 +2,49 @@ EXTRA_DIST = \
COPYRIGHT \
ChangeLog.API \
ChangeLog.win32 \
- Doxyfile.in \
HACKING \
Makefile.mingw \
PLUGIN_HOWTO \
README.hg \
README.mingw \
config.h.mingw \
- doxy2devhelp.xsl \
- fix-casts.sh \
- gaim.pc.in \
- gaim-uninstalled.pc.in \
intltool-extract.in \
intltool-merge.in \
intltool-update.in \
package_revision.h \
pidgin.apspec.in \
- pidgin.spec.in \
pidgin.desktop.in \
po/Makefile.mingw \
valgrind-suppressions
noinst_HEADERS = config.h package_revision.h
-dist-hook: pidgin.spec
- cp pidgin.spec $(distdir)
+dist-pre-hook: package_revision.h
+if !ENABLE_GTK_DOC
+ $(AM_V_at)echo "error: gtk-doc must be installed and enabled in order to make dist"
+ $(AM_V_at)echo "re-run ./configure with --enable-gtk-doc switch and try again"
+ $(AM_V_at)false
+endif
+ $(AM_V_at)if [ ! -f "$(top_builddir)libpurple/libpurple.la" ]; then \
+ $(MAKE) -C libpurple libpurple.la; \
+ fi
+if ENABLE_GNT
+ $(AM_V_at)if [ ! -f "$(top_builddir)finch/libgnt/libgnt.la" ]; then \
+ $(MAKE) -C finch/libgnt libgnt.la; \
+ fi
+ $(AM_V_at)if [ ! -f "$(top_builddir)finch/libfinch.la" ]; then \
+ $(MAKE) -C finch libfinch.la; \
+ fi
+endif
+if ENABLE_GTK
+ $(AM_V_at)if [ ! -f "$(top_builddir)pidgin/libpidgin.la" ]; then \
+ $(MAKE) -C pidgin libpidgin.la; \
+ fi
+endif
+
+dist: dist-pre-hook
+
+dist-hook:
rm $(distdir)/config.h
distcheck-hook: libpurple/plugins/perl/common/Purple.pm pidgin/plugins/perl/common/Pidgin.pm
@@ -71,7 +89,11 @@ if ENABLE_GTK
appsdir = $(datadir)/applications
apps_in_files = pidgin.desktop.in
apps_DATA = $(apps_in_files:.desktop.in=.desktop)
-@INTLTOOL_DESKTOP_RULE@
+
+# silenced INTLTOOL_DESKTOP_RULE
+%.desktop: %.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po)
+ $(AM_V_GEN) LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< $@ > /dev/null
+
endif #ENABLE_GTK
endif #INSTALL_I18N
@@ -124,23 +146,9 @@ package_revision.h: package_revision_raw.txt
# line does is tell 'distcheck' to shut up and ignore those two files.
distcleancheck_listfiles = find . -type f -a ! -name package_revision.h
-SUBDIRS = . libpurple doc $(GNT_DIR) $(GTK_DIR) m4macros $(PO_DIR) share/ca-certs share/sounds
-
-docs: Doxyfile
-if HAVE_DOXYGEN
- @echo "Running doxygen..."
- @doxygen
-if HAVE_XSLTPROC
- @echo "Generating devhelp index..."
- @xsltproc $(top_srcdir)/doxy2devhelp.xsl doc/xml/index.xml > doc/html/pidgin.devhelp
- @echo "(Symlink $$(pwd)/doc/html to ~/.local/share/gtk-doc/html/pidgin to make devhelp see the documentation)"
-else
- @echo "Not generating devhelp index: xsltproc was not found by configure"
-endif
-else
- @echo "doxygen was not found during configure. Unable to build documentation."
- @echo;
-endif
+AM_DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc
+
+SUBDIRS = . m4macros libpurple $(GNT_DIR) $(GTK_DIR) $(PO_DIR) share/ca-certs share/sounds doc
# perl's MakeMaker uninstall foo doesn't work well with DESTDIR set, which
# breaks "make distcheck" unless we ignore perl things
diff --git a/Makefile.mingw b/Makefile.mingw
index 2d02062737..e0502bb3b9 100644
--- a/Makefile.mingw
+++ b/Makefile.mingw
@@ -31,7 +31,7 @@ awk 'BEGIN {FS="."} { \
exit; \
}' VERSION)
-GTK_INSTALL_VERSION = 2.16.6.2
+GTK_INSTALL_VERSION = 2.24.18.0
authenticode_sign = $(MONO_SIGNCODE) \
-spc "$(SIGNCODE_SPC)" -v "$(SIGNCODE_PVK)" \
@@ -50,69 +50,42 @@ PIDGIN_INST_DEP_DIR="$(WIN32_DEV_TOP)/pidgin-inst-deps-20130214"
# Any *.dll or *.exe files included in win32-install-dir that we don't compile
# should be included in this list so they don't get stripped
EXTERNAL_DLLS = \
- comerr32.dll \
exchndl.dll \
- freebl3.dll \
- gssapi32.dll \
- k5sprt32.dll \
- krb5_32.dll \
- libenchant.dll \
- libenchant_ispell.dll \
- libenchant_myspell.dll \
- libgtkspell-0.dll \
- libmeanwhile-1.dll \
- libnspr4.dll \
- libplc4.dll \
- libplds4.dll \
libsasl.dll \
- libssp-0.dll \
- libxml2-2.dll \
- nss3.dll \
- nssutil3.dll \
saslANONYMOUS.dll \
saslCRAMMD5.dll \
saslDIGESTMD5.dll \
saslGSSAPI.dll \
saslLOGIN.dll \
- saslPLAIN.dll \
- libsilc-1-1-2.dll \
- libsilcclient-1-1-3.dll \
- smime3.dll \
- softokn3.dll \
- sqlite3.dll \
- ssl3.dll
+ saslPLAIN.dll
#build an expression for `find` to use to ignore the above files
EXTERNAL_DLLS_FIND_EXP = $(patsubst %,-o -name %,$(EXTERNAL_DLLS))
include $(PIDGIN_COMMON_RULES)
-.PHONY: all docs install installer installer_offline installer_zip debug_symbols_zip installers clean uninstall create_release_install_dir generate_installer_includes $(PIDGIN_REVISION_H) $(PIDGIN_REVISION_RAW_TXT) gtk_runtime_zip
+.PHONY: all install installer installer_offline installer_zip debug_symbols_zip installers clean uninstall create_release_install_dir generate_installer_includes $(PIDGIN_REVISION_H) $(PIDGIN_REVISION_RAW_TXT) gtk_runtime_zip
all: $(PIDGIN_CONFIG_H) $(PIDGIN_REVISION_H)
- $(MAKE) -C $(PURPLE_TOP) -f $(MINGW_MAKEFILE)
- $(MAKE) -C $(PIDGIN_TOP) -f $(MINGW_MAKEFILE)
+ $(MAKE_at) $(MAKE) -C $(PURPLE_TOP) -f $(MINGW_MAKEFILE)
+ $(MAKE_at) $(MAKE) -C $(PIDGIN_TOP) -f $(MINGW_MAKEFILE)
ifndef DISABLE_NLS
- $(MAKE) -C $(PURPLE_PO_TOP) -f $(MINGW_MAKEFILE)
+ $(MAKE_at) $(MAKE) -C $(PURPLE_PO_TOP) -f $(MINGW_MAKEFILE)
endif
install: all $(PIDGIN_INSTALL_DIR)
- $(MAKE) -C $(PURPLE_TOP) -f $(MINGW_MAKEFILE) install
- $(MAKE) -C $(PIDGIN_TOP) -f $(MINGW_MAKEFILE) install
+ $(MAKE_at) $(MAKE) -C $(PURPLE_TOP) -f $(MINGW_MAKEFILE) install
+ $(MAKE_at) $(MAKE) -C $(PIDGIN_TOP) -f $(MINGW_MAKEFILE) install
ifndef DISABLE_NLS
- $(MAKE) -C $(PURPLE_PO_TOP) -f $(MINGW_MAKEFILE) install
+ $(MAKE_at) $(MAKE) -C $(PURPLE_PO_TOP) -f $(MINGW_MAKEFILE) install
endif
- $(MAKE) -C share/ca-certs -f $(MINGW_MAKEFILE) install
- $(MAKE) -C share/sounds -f $(MINGW_MAKEFILE) install
- mkdir -p $(PIDGIN_INSTALL_DIR)/spellcheck/lib/enchant
- cp $(GTKSPELL_TOP)/bin/libgtkspell-0.dll $(PIDGIN_INSTALL_DIR)/spellcheck
- cp $(ENCHANT_TOP)/bin/libenchant.dll $(PIDGIN_INSTALL_DIR)/spellcheck
- cp -R $(ENCHANT_TOP)/lib/enchant/*.dll $(PIDGIN_INSTALL_DIR)/spellcheck/lib/enchant
+ $(MAKE_at) $(MAKE) -C share/ca-certs -f $(MINGW_MAKEFILE) install
+ $(MAKE_at) $(MAKE) -C share/sounds -f $(MINGW_MAKEFILE) install
cp $(PIDGIN_INST_DEP_DIR)/exchndl.dll $(PIDGIN_INSTALL_DIR)
- cp $(GCC_SSP_TOP)/libssp-0.dll $(PIDGIN_INSTALL_DIR)
+ chmod -R 777 $(PIDGIN_INSTALL_DIR)
gtk_runtime_zip:
- pidgin/win32/nsis/generate_gtk_zip.sh "`pwd`" "$(GPG_SIGN)"
+ cd pidgin/win32/nsis/ ; ./generate_gtk_zip.sh "$(GPG_SIGN)"
generate_installer_includes: create_release_install_dir gtk_runtime_zip debug_symbols_zip $(PIDGIN_TREE_TOP)/pidgin/win32/nsis/nsis_translations.desktop
rm -f pidgin/win32/nsis/pidgin-translations.nsh pidgin/win32/nsis/pidgin-spellcheck.nsh pidgin/win32/nsis/pidgin-spellcheck-preselect.nsh
@@ -148,7 +121,9 @@ create_release_install_dir: install
find $(STRIPPED_RELEASE_DIR) \( -name '*.dll' -o -name '*.exe' \) \
-not \( -false $(EXTERNAL_DLLS_FIND_EXP) \) \
-exec $(STRIP) --strip-unneeded {} ';'
+ifdef SIGN_EXECUTABLES
$(call authenticode_sign, $(STRIPPED_RELEASE_DIR)/pidgin.exe, "Pidgin $(PIDGIN_VERSION)")
+endif
installer: generate_installer_includes
$(eval $@_DEBUG_SYMBOLS_SHA1SUM := $(shell sha1sum $(DEBUG_SYMBOLS_DIR).zip | sed -e "s/\ .*$$//"))
@@ -157,23 +132,33 @@ installer: generate_installer_includes
-DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" -DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" \
-DDEBUG_SYMBOLS_SHA1SUM="$($@_DEBUG_SYMBOLS_SHA1SUM)" -DGTK_SHA1SUM="$($@_GTK_SHA1SUM)"\
pidgin/win32/nsis/pidgin-installer.nsi
+ifdef SIGN_EXECUTABLES
$(call authenticode_sign, pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION).exe, "Pidgin Installer")
+endif
mv pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION).exe ./
+ifdef SIGN_EXECUTABLES
$(call gpg_sign, pidgin-$(PIDGIN_VERSION).exe)
+endif
installer_offline: generate_installer_includes
$(MAKENSIS) -V3 -DPIDGIN_VERSION="$(PIDGIN_VERSION)" -DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" \
-DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" -DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" \
-DOFFLINE_INSTALLER \
pidgin/win32/nsis/pidgin-installer.nsi
+ifdef SIGN_EXECUTABLES
$(call authenticode_sign, pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION)-offline.exe, "Pidgin Installer")
+endif
mv pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION)-offline.exe ./
+ifdef SIGN_EXECUTABLES
$(call gpg_sign, pidgin-$(PIDGIN_VERSION)-offline.exe)
+endif
installer_zip: create_release_install_dir
rm -f pidgin-$(PIDGIN_VERSION)-win32-bin.zip
zip -9 -r pidgin-$(PIDGIN_VERSION)-win32-bin.zip $(STRIPPED_RELEASE_DIR)
+ifdef SIGN_EXECUTABLES
$(call gpg_sign, pidgin-$(PIDGIN_VERSION)-win32-bin.zip)
+endif
debug_symbols_zip: install
rm -rf $(DEBUG_SYMBOLS_DIR) $(DEBUG_SYMBOLS_DIR).zip
@@ -181,26 +166,20 @@ debug_symbols_zip: install
tar -cf - `find $(PIDGIN_INSTALL_DIR) \( -name '*.dll' -o -name '*.exe' \) \
-not \( -false $(EXTERNAL_DLLS_FIND_EXP) \) -print` \
| tar --strip 2 --xform s/$$/.dbgsym/ -xC $(DEBUG_SYMBOLS_DIR) -f -
- cp $(MEANWHILE_TOP)/bin/libmeanwhile-1.dll.unstripped $(DEBUG_SYMBOLS_DIR)/libmeanwhile-1.dll.dbgsym
+ cp $(MEANWHILE_TOP)/bin/libmeanwhile-1.dll.debug $(DEBUG_SYMBOLS_DIR)/libmeanwhile-1.dll.dbgsym
zip -9 -r $(DEBUG_SYMBOLS_DIR).zip $(DEBUG_SYMBOLS_DIR)
+ifdef SIGN_EXECUTABLES
$(call gpg_sign, $(DEBUG_SYMBOLS_DIR).zip)
+endif
installers: installer installer_offline debug_symbols_zip installer_zip
-Doxyfile.mingw: Doxyfile.in
- sed -e "s/@PACKAGE@/pidgin/" -e "s/@VERSION@/$(PIDGIN_VERSION)/" -e "s/@top_srcdir@/$(PIDGIN_TREE_TOP)/g" -e "s/@enable_dot@/NO/" $< > $@
-
-docs: Doxyfile.mingw
- @echo "Running doxygen..."
- @doxygen Doxyfile.mingw
-
clean:
- $(MAKE) -C $(PURPLE_PO_TOP) -f $(MINGW_MAKEFILE) clean
- $(MAKE) -C $(PIDGIN_TOP) -f $(MINGW_MAKEFILE) clean
- $(MAKE) -C $(PURPLE_TOP) -f $(MINGW_MAKEFILE) clean
- $(MAKE) -C share/ca-certs -f $(MINGW_MAKEFILE) clean
+ $(MAKE_at) $(MAKE) -C $(PURPLE_PO_TOP) -f $(MINGW_MAKEFILE) clean
+ $(MAKE_at) $(MAKE) -C $(PIDGIN_TOP) -f $(MINGW_MAKEFILE) clean
+ $(MAKE_at) $(MAKE) -C $(PURPLE_TOP) -f $(MINGW_MAKEFILE) clean
+ $(MAKE_at) $(MAKE) -C share/ca-certs -f $(MINGW_MAKEFILE) clean
rm -f $(PIDGIN_CONFIG_H) $(PIDGIN_REVISION_H) $(PIDGIN_REVISION_RAW_TXT) ./VERSION pidgin-$(PIDGIN_VERSION)*.exe pidgin-$(PIDGIN_VERSION)-win32-bin.zip $(DEBUG_SYMBOLS_DIR).zip
- rm -rf doc/html Doxyfile.mingw
uninstall:
rm -rf $(PURPLE_INSTALL_PERL_DIR) $(PIDGIN_INSTALL_PLUGINS_DIR) $(PURPLE_INSTALL_PO_DIR) $(PIDGIN_INSTALL_DIR) $(STRIPPED_RELEASE_DIR) $(DEBUG_SYMBOLS_DIR)
diff --git a/NEWS b/NEWS
index e4ba776239..7c2080c499 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,6 @@
Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
-Our development blog is available at: http://planet.pidgin.im
+Our development blog is available at: https://planet.pidgin.im
2.8.0 (06/07/2011):
Paul: I fixed a few things in this release, and committed some
@@ -537,7 +537,7 @@ Our development blog is available at: http://planet.pidgin.im
2.0.1 (5/24/2007):
Sean: 2.0.1! Three weeks later, as scheduled! It is so nice to have
regular, frequent, releases again! This is a bugfix release; We have
- fixed over 100 issues reported to us at http://developer.pidgin.im.
+ fixed over 100 issues reported to us at https://developer.pidgin.im.
Thanks to everyone for their great work, and look for the next release
in another three weeks!
@@ -669,7 +669,7 @@ Our development blog is available at: http://planet.pidgin.im
branding and organizational changes that have been going on for
the last few months, so there are likely to be some snags --
please help us out by trying beta 7, searching for any bugs you
- find in the bug tracker at http://developer.pidgin.im/, and
+ find in the bug tracker at https://developer.pidgin.im/, and
documenting them if they are unknown or you can provide new
information. Help us make 2.0.0 final a release to be remembered
(in a good way)! I'd like to give huge thanks to all of the
diff --git a/PLUGIN_HOWTO b/PLUGIN_HOWTO
index 16874296f5..46a8a097a2 100644
--- a/PLUGIN_HOWTO
+++ b/PLUGIN_HOWTO
@@ -1,7 +1,7 @@
For information on writing a plugin for Purple, Pidgin or Finch, go
-http://developer.pidgin.im and click on API. From there, see the
+https://developer.pidgin.im and click on API. From there, see the
HOWTOs in the "Related Pages" section.
-You can also generate this documentation locally by installing
-doxygen and graphviz dot, then running "make docs" in the
-source tree. The documentation will be in the docs/html directory.
+You can also generate this documentation locally by passing
+"--enable-gtk-doc" to ./configure, then running "make" in the source tree.
+The documentation will be in the doc/reference/*/html directories.
diff --git a/README b/README
index 196300ac2f..c005127a46 100644
--- a/README
+++ b/README
@@ -29,8 +29,7 @@ well as the development files!). The configure script will fail if you
don't. If you don't have GTK+ 2.0 installed, you should install it
using your distribution's package management tools.
-For sound support, you also need gstreamer 0.10 or higher. For
-spellchecking support, you need libgtkspell (http://gtkspell.sf.net/).
+For sound support, you also need gstreamer 0.10 or higher.
Your distro of choice probably already includes these, just be sure to
install the development packages.
@@ -43,7 +42,7 @@ you only need to run 'pidgin' or 'finch'.
To get started, simply add a new account.
-If you come across a bug, please report it at: http://developer.pidgin.im
+If you come across a bug, please report it at: https://developer.pidgin.im
PLUGINS
=======
diff --git a/README.hg b/README.hg
index 3240314e86..cddd924b82 100644
--- a/README.hg
+++ b/README.hg
@@ -16,18 +16,19 @@ shiniest features -- but it will prevent users from having to deal with ugly
development bugs that we already know about but haven't gotten around to fixing.
If you are interested in hacking on Pidgin, Finch, and/or libpurple, please
-check out the information available at: http://developer.pidgin.im
+check out the information available at: https://developer.pidgin.im
-By far the best documentation, however, is the documented code. If you have
-doxygen, you can run "make docs" in the toplevel directory to generate pretty
-documentation. Otherwise (or even if you do!), the header files for each
-subsystem contain documentation for the functions they contain. For instance,
-conversation.h contains documentation for the entire purple_conversation_*
-API, and account.h contains documentation for the purple_account_* API.
+By far the best documentation, however, is the documented code. You can pass
+"--enable-gtk-doc" to ./configure then run "make" in the source tree to
+generate pretty documentation in the doc/reference/*/html directories.
+Otherwise (or even if you do!), the header files for each subsystem contain
+documentation for the functions they contain. For instance, conversation.h
+contains documentation for the entire purple_conversation_* API, and account.h
+contains documentation for the purple_account_* API.
If you have questions, please feel free to contact the Pidgin, Finch, and
libpurple developers by email at devel@pidgin.im or on IRC at irc.freenode.net
in #pidgin. Please do as much homework as you can before contacting us; the
more you know about your question, the faster and more effectively we can help!
-Patches should be posted as Trac tickets at: http://developer.pidgin.im
+Patches should be posted as Trac tickets at: https://developer.pidgin.im
diff --git a/README.mingw b/README.mingw
index 21a2026146..1f9956a1eb 100644
--- a/README.mingw
+++ b/README.mingw
@@ -3,4 +3,4 @@ How to build Pidgin for Windows using MinGW
For the latest build instructions, please refer to:
-http://developer.pidgin.im/wiki/BuildingWinPidgin
+https://developer.pidgin.im/wiki/BuildingWinPidgin
diff --git a/autogen.sh b/autogen.sh
index 13a7f619cd..f2a66ad27d 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -34,6 +34,7 @@
# AUTOMAKE_FLAGS - command line arguments to pass to automake flags
# CONFIGURE_FLAGS - command line arguments to pass to configure
# GLIB_GETTEXTIZE_FLAGS - command line arguments to pass to glib-gettextize
+# GTKDOCIZE_FLAGS - command line arguments to pass to gtkdocize
# INTLTOOLIZE_FLAGS - command line arguments to pass to intltoolize
# LIBTOOLIZE_FLAGS - command line arguments to pass to libtoolize
#
@@ -99,6 +100,22 @@ run_or_die () { # beotch
fi
}
+check_gtkdoc() {
+ printf "checking for gtkdocize... "
+ GTKDOCIZE=`which gtkdocize 2>/dev/null`
+
+ if [ x"${GTKDOCIZE}" = x"" ] ; then
+ echo "not found."
+ echo "EXTRA_DIST =" > gtk-doc.make
+ echo "You don't have gtk-doc installed, and thus won't be able to
+generate the documentation.
+"
+ else
+ echo "${GTKDOCIZE}"
+ run_or_die ${GTKDOCIZE} ${GTKDOCIZE_FLAGS}
+ fi
+}
+
cleanup () {
rm -f autogen-??????
echo
@@ -150,10 +167,13 @@ run_or_die ${LIBTOOLIZE} ${LIBTOOLIZE_FLAGS:-"-c -f --automake"}
run_or_die ${GLIB_GETTEXTIZE} ${GLIB_GETTEXTIZE_FLAGS:-"--force --copy"}
run_or_die ${INTLTOOLIZE} ${INTLTOOLIZE_FLAGS:-"-c -f --automake"}
# This call to sed is needed to work around an annoying bug in intltool 0.40.6
-# See http://developer.pidgin.im/ticket/9520 for details
-run_or_die ${SED} -i.bak -e "s:'\^\$\$lang\$\$':\^\$\$lang\$\$:g" po/Makefile.in.in
+# See https://developer.pidgin.im/ticket/9520 for details
+run_or_die ${SED} -i -e "s:'\^\$\$lang\$\$':\^\$\$lang\$\$:g" po/Makefile.in.in
+# glib-gettextize doesn't seems to use AM_V_GEN macro
+${SED} -i -e "s:\\tfile=\`echo:\\t@echo -e \" GEN\\\\t\$\@\"; file=\`echo:g" po/Makefile.in.in
run_or_die ${ACLOCAL} ${ACLOCAL_FLAGS:-"-I m4macros"}
run_or_die ${AUTOHEADER} ${AUTOHEADER_FLAGS}
+check_gtkdoc
run_or_die ${AUTOMAKE} ${AUTOMAKE_FLAGS:-"-a -c --gnu"}
run_or_die ${AUTOCONF} ${AUTOCONF_FLAGS}
diff --git a/config.h.mingw b/config.h.mingw
index 5576d1e899..e40345761f 100644
--- a/config.h.mingw
+++ b/config.h.mingw
@@ -29,6 +29,7 @@
/* #undef HAVE_CONNECT */
/* Define to 1 if Cyrus SASL is present */
+/* It's defined in global.mak. */
/* #undef HAVE_CYRUS_SASL */
/* Define if you have the external 'daylight' variable. */
@@ -47,12 +48,6 @@
/* Define to 1 if you have the <dlfcn.h> header file. */
/* #undef HAVE_DLFCN_H */
-/* whether or not we have dot */
-/* #undef HAVE_DOT */
-
-/* whether or not we have doxygen */
-/* #undef HAVE_DOXYGEN */
-
/* Define if we're using evolution addressbook. */
/* #undef HAVE_EVOLUTION_ADDRESSBOOK */
@@ -62,9 +57,8 @@
/* Define to 1 if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H 1
-/* Define to 1 if you have the getaddrinfo function. */
-/* TODO: Use this on new enough versions of Windows */
-/* #define HAVE_GETADDRINFO 1 */
+/* Define to 1 if you have the getaddrinfo function (since WinXP). */
+#define HAVE_GETADDRINFO 1
/* Define to 1 if you have the `gethostid' function. */
/* #define HAVE_GETHOSTID 1 */
@@ -76,10 +70,10 @@
/* #define HAVE_GETTEXT 1 */
/* Define if you have GNUTLS */
-/* #define HAVE_GNUTLS 1 */
+#define HAVE_GNUTLS 1
/* Define to 1 if you have the <gnutls/gnutls.h> header file. */
-/* #define HAVE_GNUTLS_GNUTLS_H 1 */
+#define HAVE_GNUTLS_GNUTLS_H 1
/* Define if <inttypes.h> exists and doesn't clash with <sys/types.h>. */
#define HAVE_INTTYPES_H 1
@@ -148,13 +142,13 @@
/* #undef HAVE_MONO_METADATA_OBJECT_H */
/* Define to 1 if you have the <nspr.h> header file. */
-/* #undef HAVE_NSPR_H */
+#define HAVE_NSPR_H 1
/* Define if you have Mozilla NSS */
#define HAVE_NSS 1
/* Define to 1 if you have the <nss.h> header file. */
-/* #undef HAVE_NSS_H */
+#define HAVE_NSS_H 1
/* Define if we have Pango 1.4 or newer. */
#define HAVE_PANGO14 1
@@ -180,9 +174,6 @@
/* Define to 1 if you have the `random' function. */
/* #define HAVE_RANDOM 1 */
-/* Define to 1 if you have the <regex.h> header file. */
-/* #define HAVE_REGEX_H 1 */
-
/* Define to 1 if you have the `setlocale' function. */
#define HAVE_SETLOCALE 1
@@ -365,10 +356,12 @@
/* #undef TM_IN_SYS_TIME */
/* Use GStreamer for playing sounds */
-/* #define USE_GSTREAMER 1 */
+/* It's defined in global.mak. */
+/* #undef USE_GSTREAMER */
-/* Define if we're using GtkSpell */
-#define USE_GTKSPELL 1
+/* Use voice and video */
+/* It's defined in global.mak. */
+/* #undef USE_VV */
/* Define if we're using XScreenSaver. */
#define USE_SCREENSAVER 1
@@ -410,6 +403,16 @@
*/
#define HAVE_VSNPRINTF 1
-#define HAVE_FILENO 1
+#define FINCH_LIBDIR wpurple_lib_dir(NULL)
+#define PIDGIN_LIBDIR wpurple_lib_dir(NULL)
+#define PURPLE_DATADIR wpurple_bin_dir()
+#define PURPLE_LIBDIR wpurple_lib_dir(NULL)
+#define PURPLE_LOCALEDIR wpurple_locale_dir()
+#define PURPLE_SYSCONFDIR wpurple_sysconf_dir()
+#define HAVE_FILENO 1
+#ifndef _WIN32_WINNT
+/* _WIN32_WINNT_WINXP = 0x0501 */
+#define _WIN32_WINNT 0x0501
+#endif
diff --git a/configure.ac b/configure.ac
index 9833b0fb74..5eff393551 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
dnl Process this file with autoconf to produce a configure script.
-AC_PREREQ([2.50])
+AC_PREREQ([2.63])
# UPDATING VERSION NUMBERS FOR RELEASES
#
@@ -43,20 +43,22 @@ AC_PREREQ([2.50])
#
# Make sure to update finch/libgnt/configure.ac with libgnt version changes.
#
-m4_define([purple_lt_current], [10])
-m4_define([purple_major_version], [2])
-m4_define([purple_minor_version], [10])
-m4_define([purple_micro_version], [10])
+m4_define([purple_lt_current], [20])
+m4_define([purple_major_version], [3])
+m4_define([purple_minor_version], [0])
+m4_define([purple_micro_version], [0])
m4_define([purple_version_suffix], [devel])
m4_define([purple_version],
[purple_major_version.purple_minor_version.purple_micro_version])
m4_define([purple_display_version], purple_version[]m4_ifdef([purple_version_suffix],[purple_version_suffix]))
-m4_define([gnt_lt_current], [8])
+# the last version for Finch 2 was 2.8.10,
+# the first version for Finch 3 was 2.9.0
+m4_define([gnt_lt_current], [9])
m4_define([gnt_major_version], [2])
-m4_define([gnt_minor_version], [8])
-m4_define([gnt_micro_version], [10])
-m4_define([gnt_version_suffix], [])
+m4_define([gnt_minor_version], [9])
+m4_define([gnt_micro_version], [0])
+m4_define([gnt_version_suffix], [devel])
m4_define([gnt_version],
[gnt_major_version.gnt_minor_version.gnt_micro_version])
m4_define([gnt_display_version], gnt_version[]m4_ifdef([gnt_version_suffix],[gnt_version_suffix]))
@@ -74,18 +76,20 @@ fi
AC_CANONICAL_HOST
AC_CONFIG_HEADERS([config.h])
-AM_INIT_AUTOMAKE([1.9 -Wno-portability dist-bzip2])
-dnl TODO: Always use AM_SILENT_RULES when we depend on automake >= 1.11
-m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+AC_CONFIG_MACRO_DIR([m4macros])
+AM_INIT_AUTOMAKE([1.11 -Wno-portability dist-bzip2 subdir-objects])
+AM_SILENT_RULES([yes])
PURPLE_MAJOR_VERSION=purple_major_version
PURPLE_MINOR_VERSION=purple_minor_version
PURPLE_MICRO_VERSION=purple_micro_version
-PURPLE_VERSION=[purple_display_version]
+PURPLE_VERSION=purple_display_version
+PURPLE_API_VERSION=$((purple_lt_current - purple_minor_version))
AC_SUBST(PURPLE_MAJOR_VERSION)
AC_SUBST(PURPLE_MINOR_VERSION)
AC_SUBST(PURPLE_MICRO_VERSION)
AC_SUBST(PURPLE_VERSION)
+AC_SUBST(PURPLE_API_VERSION)
PURPLE_LT_VERSION_INFO="purple_lt_current:purple_micro_version:purple_minor_version"
AC_SUBST(PURPLE_LT_VERSION_INFO)
@@ -93,16 +97,19 @@ AC_SUBST(PURPLE_LT_VERSION_INFO)
GNT_MAJOR_VERSION=gnt_major_version
GNT_MINOR_VERSION=gnt_minor_version
GNT_MICRO_VERSION=gnt_micro_version
-GNT_VERSION=[gnt_display_version]
+GNT_VERSION=gnt_display_version
+GNT_API_VERSION=$((gnt_lt_current - gnt_minor_version))
AC_SUBST(GNT_MAJOR_VERSION)
AC_SUBST(GNT_MINOR_VERSION)
AC_SUBST(GNT_MICRO_VERSION)
AC_SUBST(GNT_VERSION)
+AC_SUBST(GNT_API_VERSION)
GNT_LT_VERSION_INFO="gnt_lt_current:gnt_micro_version:gnt_minor_version"
AC_SUBST(GNT_LT_VERSION_INFO)
AC_PATH_PROG(sedpath, sed)
+AC_PATH_PROG(xxdpath, xxd)
dnl Storing configure arguments
AC_DEFINE_UNQUOTED(CONFIG_ARGS, "$ac_configure_args", [configure arguments])
@@ -110,9 +117,9 @@ AC_DEFINE_UNQUOTED(CONFIG_ARGS, "$ac_configure_args", [configure arguments])
dnl Checks for programs.
AC_PROG_CC
AM_PROG_CC_C_O
-AC_DISABLE_STATIC
-AC_PROG_LIBTOOL
-LIBTOOL="$LIBTOOL --silent"
+AC_PROG_CXX
+LT_PREREQ([2.2.6])
+LT_INIT([disable-static])
AC_PROG_INSTALL
PKG_PROG_PKG_CONFIG
AC_FUNC_ALLOCA
@@ -120,10 +127,46 @@ AC_FUNC_ALLOCA
dnl Check for Sun compiler
AC_CHECK_DECL([__SUNPRO_C], [SUNCC="yes"], [SUNCC="no"])
+dnl Check for Win32
+case "$host" in
+ *-*-mingw* | *-*-cygwin*)
+ is_win32="yes"
+ is_not_win32="no"
+ LIBS="$LIBS -lws2_32"
+ DNSAPI_LIBS="-ldnsapi"
+ PLUGIN_LDFLAGS="-avoid-version -no-undefined"
+ AC_SUBST(DNSAPI_LIBS)
+ AC_CHECK_TOOL(WINDRES, windres)
+ AC_DEFINE(IS_WIN32_CROSS_COMPILED, 1,
+ [Define to 1, when building with autotools (not necessarily
+ cross-compiling) for windows. It's a temporary hack to remain
+ non-autotools win32 build working. Drop it when we fully
+ switch our win32 build to autotools.])
+ AC_DEFINE(WIN32_LEAN_AND_MEAN, 1,
+ [Define to 1, to build faster for win32.])
+ ;;
+ *)
+ is_win32="no"
+ is_not_win32="yes"
+ PLUGIN_LDFLAGS="-avoid-version"
+ AC_CHECK_HEADERS(signal.h)
+ ;;
+esac
+AM_CONDITIONAL(IS_WIN32, test "x$is_win32" = "xyes")
+AC_SUBST([PLUGIN_LDFLAGS])
+
+dnl Define *_LIBS
+PURPLE_LIBS="\$(top_builddir)/libpurple/libpurple.la \$(GLIB_LIBS)"
+PIDGIN_LIBS="\$(top_builddir)/pidgin/libpidgin.la \$(GTK_LIBS)"
+FINCH_LIBS="\$(top_builddir)/finch/libfinch.la \$(top_builddir)/finch/libgnt/libgnt.la \$(GLIB_LIBS)"
+AC_SUBST(PURPLE_LIBS)
+AC_SUBST(PIDGIN_LIBS)
+AC_SUBST(FINCH_LIBS)
+
dnl Checks for header files.
AC_HEADER_STDC
AC_HEADER_SYS_WAIT
-AC_CHECK_HEADERS(arpa/nameser_compat.h fcntl.h sys/time.h unistd.h locale.h signal.h stdint.h regex.h)
+AC_CHECK_HEADERS(arpa/nameser_compat.h fcntl.h sys/time.h unistd.h locale.h stdint.h)
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
@@ -134,6 +177,81 @@ AC_CHECK_SIZEOF(time_t, ,[
AC_C_BIGENDIAN
+AC_ARG_WITH(win32-dirs, [AS_HELP_STRING([--with-win32-dirs=<classic|fhs>],
+ [use win32 classic (Program Files-like) or FHS (unix-like) directory structure (default: classic)])], [
+ if test "x$withval" != "xclassic" -a "x$withval" != "xfhs" ; then
+ AC_MSG_ERROR([Unsupported win32-dirs option. Please choose "classic" or "fhs".])
+ fi
+ with_win32_dirs="$withval"
+ ], with_win32_dirs="classic")
+if test "x$is_win32" = "xyes" -a "x$with_win32_dirs" = "xfhs" ; then
+ AC_DEFINE(USE_WIN32_FHS, 1, [Define to 1, to use FHS on win32.])
+fi
+
+dnl Check for directories
+if test "x$is_win32" = "xyes" ; then
+ if test "x$with_win32_dirs" = "xfhs" ; then
+ AS_AC_EXPAND(win32_fhs_bindir, "$bindir")
+ AC_DEFINE_UNQUOTED([WIN32_FHS_BINDIR], ["$win32_fhs_bindir"],
+ [bindir, as defined by configure])
+ AS_AC_EXPAND(win32_fhs_libdir, "$libdir")
+ AC_DEFINE_UNQUOTED([WIN32_FHS_LIBDIR], ["$win32_fhs_libdir"],
+ [libdir, as defined by configure])
+ AS_AC_EXPAND(win32_fhs_datadir, "$datadir")
+ AC_DEFINE_UNQUOTED([WIN32_FHS_DATADIR], ["$win32_fhs_datadir"],
+ [datadir, as defined by configure])
+ AS_AC_EXPAND(win32_fhs_sysconfdir, "$sysconfdir")
+ AC_DEFINE_UNQUOTED([WIN32_FHS_SYSCONFDIR], ["$win32_fhs_sysconfdir"],
+ [sysconfdir, as defined by configure])
+ AS_AC_EXPAND(win32_fhs_localedir, "$localedir")
+ AC_DEFINE_UNQUOTED([WIN32_FHS_LOCALEDIR], ["$win32_fhs_localedir"],
+ [localedir, as defined by configure])
+
+ purple_libdir="wpurple_lib_dir(\"purple-$PURPLE_MAJOR_VERSION\")"
+ pidgin_libdir="wpurple_lib_dir(\"pidgin-$PURPLE_MAJOR_VERSION\")"
+ finch_libdir="wpurple_lib_dir(\"finch-$PURPLE_MAJOR_VERSION\")"
+ else
+ purple_libdir="wpurple_lib_dir(NULL)"
+ pidgin_libdir="wpurple_lib_dir(NULL)"
+ finch_libdir="wpurple_lib_dir(NULL)"
+ fi
+
+ purple_datadir="wpurple_data_dir()"
+ purple_sysconfdir="wpurple_sysconf_dir()"
+ purple_localedir="wpurple_locale_dir()"
+else
+ AS_AC_EXPAND(purple_datadir, "$datadir")
+ purple_datadir="\"$purple_datadir\""
+ AS_AC_EXPAND(purple_sysconfdir, "$sysconfdir")
+ purple_sysconfdir="\"$purple_sysconfdir\""
+ AS_AC_EXPAND(purple_localedir, "$localedir")
+ purple_localedir="\"$purple_localedir\""
+
+ AS_AC_EXPAND(common_libdir, "$libdir")
+ purple_libdir="\"$common_libdir/purple-$PURPLE_MAJOR_VERSION\""
+ pidgin_libdir="\"$common_libdir/pidgin-$PURPLE_MAJOR_VERSION\""
+ finch_libdir="\"$common_libdir/finch-$PURPLE_MAJOR_VERSION\""
+fi
+AC_DEFINE_UNQUOTED([PURPLE_DATADIR], [$purple_datadir],
+ [datadir to use, may expand into a function call that returns const char *])
+AC_DEFINE_UNQUOTED([PURPLE_LIBDIR], [$purple_libdir],
+ [libdir to use for libpurple, may expand into a function call that returns const char *])
+AC_DEFINE_UNQUOTED([PIDGIN_LIBDIR], [$pidgin_libdir],
+ [libdir to use for Pidgin, may expand into a function call that returns const char *])
+AC_DEFINE_UNQUOTED([FINCH_LIBDIR], [$finch_libdir],
+ [libdir to use for Finch, may expand into a function call that returns const char *])
+AC_DEFINE_UNQUOTED([PURPLE_SYSCONFDIR], [$purple_sysconfdir],
+ [sysconfdir to use, may expand into a function call that returns const char *])
+AC_DEFINE_UNQUOTED([PURPLE_LOCALEDIR], [$purple_localedir],
+ [localedir to use, may expand into a function call that returns const char *])
+
+PURPLE_PLUGINDIR="\$(libdir)/purple-$PURPLE_MAJOR_VERSION"
+AC_SUBST([PURPLE_PLUGINDIR])
+PIDGIN_PLUGINDIR="\$(libdir)/pidgin-$PURPLE_MAJOR_VERSION"
+AC_SUBST([PIDGIN_PLUGINDIR])
+FINCH_PLUGINDIR="\$(libdir)/finch-$PURPLE_MAJOR_VERSION"
+AC_SUBST([FINCH_PLUGINDIR])
+
dnl Checks for library functions.
AC_TYPE_SIGNAL
AC_FUNC_STRFTIME
@@ -146,33 +264,48 @@ AC_CHECK_FUNCS(getopt_long,,
])
dnl Check for inet_aton
-AC_CHECK_FUNC(inet_aton, , [AC_CHECK_LIB(resolv, inet_aton, ,
- [AC_MSG_ERROR([inet_aton not found])])])
+if test "x$is_win32" != "xyes" ; then
+ AC_CHECK_FUNC(inet_aton, , [AC_CHECK_LIB(resolv, inet_aton, ,
+ [AC_MSG_ERROR([inet_aton not found])])])
+fi
AC_CHECK_LIB(resolv, __res_query)
AC_CHECK_LIB(nsl, gethostent)
-AC_CHECK_FUNC(socket, ,
- [AC_CHECK_LIB(socket, socket, , [AC_MSG_ERROR([socket not found])])])
-dnl If all goes well, by this point the previous two checks will have
-dnl pulled in -lsocket and -lnsl if we need them.
-AC_CHECK_FUNC(getaddrinfo,
- [AC_DEFINE([HAVE_GETADDRINFO], [1],
- [Define to 1 if you have the getaddrinfo function.])],
- [AC_CHECK_LIB(socket, getaddrinfo,
- [AC_DEFINE([HAVE_GETADDRINFO]) LIBS="-lsocket -lsnl $LIBS"], , , -lnsl)])
-AC_CHECK_FUNCS(inet_ntop)
+if test "x$is_win32" = "xyes" ; then
+ AC_DEFINE(HAVE_GETADDRINFO, 1, [Define to 1 if you have the getaddrinfo function.])
+ AC_DEFINE(HAVE_INET_NTOP, 1, [Define to 1 if you have the `inet_ntop' function.])
+else
+ AC_CHECK_FUNC(socket, , [AC_CHECK_LIB(socket, socket, ,
+ [AC_MSG_ERROR([socket not found])])])
+ dnl If all goes well, by this point the previous two checks will have
+ dnl pulled in -lsocket and -lnsl if we need them.
+ AC_CHECK_FUNC(getaddrinfo,
+ [AC_DEFINE([HAVE_GETADDRINFO], [1],
+ [Define to 1 if you have the getaddrinfo function.])],
+ [AC_CHECK_LIB(socket, getaddrinfo,
+ [AC_DEFINE([HAVE_GETADDRINFO]) LIBS="-lsocket -lnsl $LIBS"], , -lnsl)])
+ AC_CHECK_FUNCS(inet_ntop)
+fi
AC_CHECK_FUNCS(getifaddrs)
dnl Check for socklen_t (in Unix98)
AC_MSG_CHECKING(for socklen_t)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <sys/types.h>
- #include <sys/socket.h>
+ #ifdef _WIN32
+ # include <ws2tcpip.h>
+ #else
+ # include <sys/socket.h>
+ #endif
socklen_t x;
]], [[]])], [
AC_MSG_RESULT(yes)
], [
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <sys/types.h>
- #include <sys/socket.h>
+ #ifdef _WIN32
+ # include <ws2tcpip.h>
+ #else
+ # include <sys/socket.h>
+ #endif
int accept(int, struct sockaddr *, size_t *);
]], [[]])], [
AC_MSG_RESULT(size_t)
@@ -187,13 +320,25 @@ dnl Some systems do not have sa_len field for struct sockaddr.
AC_CHECK_MEMBER([struct sockaddr.sa_len],
[AC_DEFINE([HAVE_STRUCT_SOCKADDR_SA_LEN],[1],
[Define if struct sockaddr has an sa_len member])],[:],
- [#include <sys/socket.h>])
+ [
+ #ifdef _WIN32
+ # include <ws2tcpip.h>
+ #else
+ # include <sys/socket.h>
+ #endif
+ ])
dnl Check for v6-only sockets
AC_CHECK_DECL([IPV6_V6ONLY],
[AC_DEFINE([HAVE_IPV6_V6ONLY],[1],
[Define if the IPV6_V6ONLY setsockopt option exists])],
- [], [#include <netinet/in.h>])
+ [], [
+ #ifdef _WIN32
+ # include <ws2tcpip.h>
+ #else
+ # include <netinet/in.h>
+ #endif
+ ])
dnl to prevent the g_stat()/g_unlink() crash,
dnl (09:50:07) Robot101: LSchiere2: it's easy. +LC_SYS_LARGEFILE somewhere in configure.ac
@@ -202,13 +347,13 @@ AC_SYS_LARGEFILE
dnl FreeBSD doesn't have libdl, dlopen is provided by libc
AC_CHECK_FUNC(dlopen, LIBDL="", [AC_CHECK_LIB(dl, dlopen, LIBDL="-ldl")])
-dnl Haiku does not use libm for the math functions, they are part
+dnl Windows and Haiku do not use libm for the math functions, they are part
dnl of the C library
AC_SEARCH_LIBS([ceil], [m], [], [
- AC_MSG_ERROR([unable to find the floor() function])
+ AC_MSG_ERROR([unable to find the ceil() function])
])
-AC_MSG_CHECKING(for fileno())
+AC_MSG_CHECKING([for fileno()])
AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include <stdio.h>
@@ -232,7 +377,7 @@ int main(int argc, char *argv[])
AC_MSG_RESULT(no)
])
-AC_MSG_CHECKING(for the %z format string in strftime())
+AC_MSG_CHECKING([for the %z format string in strftime()])
AC_RUN_IFELSE([AC_LANG_SOURCE([[
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
@@ -297,10 +442,10 @@ esac
dnl #######################################################################
dnl # Disable creation and installation of translation files
dnl #######################################################################
-AC_ARG_ENABLE(nls, AC_HELP_STRING([--disable-nls], [disable installation of translation files]), enable_i18n="$enableval", enable_i18n=yes)
+AC_ARG_ENABLE(nls, AS_HELP_STRING([--disable-nls], [disable installation of translation files]), enable_i18n="$enableval", enable_i18n=yes)
if test x$enable_i18n = xyes; then
- AC_PROG_INTLTOOL
+ IT_PROG_INTLTOOL
GETTEXT_PACKAGE=pidgin
AC_SUBST(GETTEXT_PACKAGE)
@@ -332,13 +477,16 @@ fi #enable_i18n
AM_CONDITIONAL(INSTALL_I18N, test "x$enable_i18n" = "xyes")
dnl #######################################################################
-dnl # Check for GLib 2.16 (required)
+dnl # Check for GLib 2.20 (required)
dnl #######################################################################
-PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.16.0 gobject-2.0 gmodule-2.0 gthread-2.0], , [
+# TODO: gmodule-2.0 is only needed if enable_plugins is 'yes'. It
+# might be nice to change this check so that it's not required
+# if enable_plugins is 'no'.
+PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.20.0 gobject-2.0 gmodule-2.0 gthread-2.0], , [
AC_MSG_RESULT(no)
AC_MSG_ERROR([
-You must have GLib 2.16.0 or newer development headers installed to build.
+You must have GLib 2.20.0 or newer development headers installed to build.
If you have these installed already you may need to install pkg-config so
I can find them.
@@ -349,8 +497,11 @@ AC_SUBST(GLIB_LIBS)
GLIB_GENMARSHAL=`pkg-config --variable=glib_genmarshal glib-2.0`
AC_SUBST(GLIB_GENMARSHAL)
+GLIB_MKENUMS=`pkg-config --variable=glib_mkenums glib-2.0`
+AC_SUBST(GLIB_MKENUMS)
+
AC_ARG_WITH([extraversion],
- AC_HELP_STRING([--with-extraversion=STRING],
+ AS_HELP_STRING([--with-extraversion=STRING],
[extra version number to be displayed in Help->About and --help (for packagers)]),
EXTRA_VERSION=$withval)
@@ -360,50 +511,60 @@ else
AC_DEFINE_UNQUOTED(DISPLAY_VERSION, "$VERSION", [display version info])
fi
-AC_ARG_ENABLE(missing-dependencies, [AC_HELP_STRING([--disable-missing-dependencies],
+AC_ARG_ENABLE(missing-dependencies, [AS_HELP_STRING([--disable-missing-dependencies],
[skip missing dependencies instead of aborting configure])],
force_deps="$enableval", force_deps="yes")
AC_ARG_WITH(x, [],
- with_x="$withval", with_x="yes")
-AC_ARG_ENABLE(gtkui, [AC_HELP_STRING([--disable-gtkui],
+ with_x="$withval", with_x="$is_not_win32")
+AC_ARG_ENABLE(gtkui, [AS_HELP_STRING([--disable-gtkui],
[compile without GTK+ user interface])],
enable_gtkui="$enableval", enable_gtkui="yes")
-AC_ARG_ENABLE(consoleui, [AC_HELP_STRING([--disable-consoleui],
+AC_ARG_WITH(gtk, [AS_HELP_STRING([--with-gtk=<version>],
+ [compile with GTK+ 2 or 3 user interface (default: auto)])],
+ with_gtk="$withval", with_gtk="auto")
+AC_ARG_ENABLE(consoleui, [AS_HELP_STRING([--disable-consoleui],
[compile without console user interface])],
[enable_consoleui=$enableval force_finch=$enableval], [enable_consoleui=yes force_finch=no])
+AC_ARG_ENABLE(gstreamer,
+ [AS_HELP_STRING([--disable-gstreamer], [compile without GStreamer audio support])],
+ enable_gst="$enableval", enable_gst="yes")
dnl #######################################################################
-dnl # Check for GTK+ 2.10 and other things used by the GTK UI
+dnl # Check for GTK+ 2.18 and other things used by the GTK UI
dnl #######################################################################
AC_ARG_ENABLE(screensaver,
- [AC_HELP_STRING([--disable-screensaver],
+ [AS_HELP_STRING([--disable-screensaver],
[compile without X screensaver extension (used to detect idleness)])],
enable_screensaver="$enableval", enable_screensaver="yes")
AC_ARG_ENABLE(sm,
- [AC_HELP_STRING([--disable-sm],
+ [AS_HELP_STRING([--disable-sm],
[compile without X session management support])],
enable_sm="$enableval", enable_sm="yes")
AC_ARG_ENABLE(startup-notification,
- [AC_HELP_STRING([--disable-startup-notification],
+ [AS_HELP_STRING([--disable-startup-notification],
[compile without startup notification support])],
enable_startup_notification="$enableval", enable_startup_notification="yes")
-AC_ARG_ENABLE(gtkspell,
- [AC_HELP_STRING([--disable-gtkspell],
- [compile without GtkSpell automatic spell checking])],
- enable_gtkspell="$enableval", enable_gtkspell="yes")
+AC_ARG_ENABLE(enchant,
+ [AS_HELP_STRING([--disable-enchant],
+ [compile without Enchant spell checking support])],
+ enable_enchant="$enableval", enable_enchant="yes")
AC_ARG_ENABLE(gevolution,
- [AC_HELP_STRING([--enable-gevolution],
+ [AS_HELP_STRING([--enable-gevolution],
[compile with the Evolution plugin])],
enable_gevolution="$enableval", enable_gevolution="no")
AC_ARG_ENABLE(cap,
- [AC_HELP_STRING([--enable-cap],
+ [AS_HELP_STRING([--enable-cap],
[compile with Contact Availability Prediction plugin])],
enable_cap="$enableval", enable_cap="no")
AC_ARG_ENABLE(gestures,
- [AC_HELP_STRING([--disable-gestures],
+ [AS_HELP_STRING([--disable-gestures],
[compile without the gestures plugin])],
enable_gestures="$enableval", enable_gestures="yes")
+AC_ARG_ENABLE(gcr,
+ [AS_HELP_STRING([--enable-gcr],
+ [compile with GCR certificate widgets])],
+ enable_gcr="$enableval", enable_gcr="no")
AC_PATH_XTRA
# We can't assume that $x_libraries will be set, because autoconf does not
@@ -421,22 +582,98 @@ else
fi
if test "x$enable_gtkui" = "xyes" ; then
- PKG_CHECK_MODULES(GTK, [gtk+-2.0 >= 2.10.0], , [
- AC_MSG_RESULT(no)
- AC_MSG_ERROR([
-
-You must have GTK+ 2.10.0 or newer development headers installed to compile
+ if test "x$with_gtk" = "x3"; then
+ PKG_CHECK_MODULES(GTK, [gtk+-3.0 >= 3.0.0], , [
+ AC_MSG_RESULT(no)
+ AC_MSG_ERROR([
+You must have GTK+ 3.0.0 or newer development headers installed to compile
Pidgin. If you want to build only Finch then specify --disable-gtkui when
running configure.
])])
-
+ elif test "x$with_gtk" = "x2"; then
+ PKG_CHECK_MODULES(GTK, [gtk+-2.0 >= 2.18.0], , [
+ AC_MSG_RESULT(no)
+ AC_MSG_ERROR([
+You must have GTK+ 2.18.0 or newer development headers installed to compile
+Pidgin. If you want to build only Finch then specify --disable-gtkui when
+running configure.
+])])
+ elif test "x$with_gtk" = "xauto"; then
+ if test "x$enable_gst" != "xno"; then
+ gst10_pkg="gstreamer-1.0"
+ gst010_pkg="gstreamer-0.10"
+ fi
+ PKG_CHECK_MODULES(GTK, [gtk+-3.0 >= 3.0.0 $gst10_pkg], [with_gtk=3], [
+ AC_MSG_RESULT(no)
+ PKG_CHECK_MODULES(GTK, [gtk+-2.0 >= 2.18.0 $gst010_pkg], [with_gtk=2], [
+ AC_MSG_RESULT(no)
+ AC_MSG_ERROR([
+You must have GTK+ 2.18.0 or newer and GStreamer development headers installed to compile
+Pidgin. If you want to build only Finch then specify --disable-gtkui when
+running configure. Use --disable-gstreamer if you do not need GStreamer (sound) support.
+])])])
+ else
+ AC_MSG_ERROR([--with-gtk must specify one of 2, 3 or auto.])
+ fi
AC_SUBST(GTK_CFLAGS)
AC_SUBST(GTK_LIBS)
+ GTK_PC_MODULE="gtk+-${with_gtk}.0"
+ AC_SUBST(GTK_PC_MODULE)
+
dnl We only really need Pango >= 1.4 for decent RTL support
PKG_CHECK_MODULES(PANGO, [pango >= 1.4.0],
AC_DEFINE(HAVE_PANGO14, 1, [Define if we have Pango 1.4 or newer.]),:)
+ WEBKIT_VERSION=1.3.7
+ if test "x$with_gtk" = "x3"; then
+ WEBKIT_PC_MODULE="webkitgtk-3.0"
+ PKG_CHECK_MODULES(WEBKIT, [$WEBKIT_PC_MODULE >= $WEBKIT_VERSION], , [
+ AC_MSG_RESULT(no)
+ AC_MSG_ERROR([
+You must have WebKit for GTK+3 $WEBKIT_VERSION or newer development headers
+installed to compile Pidgin. If you want to build only Finch then specify
+--disable-gtkui when running configure.
+])])
+ else
+ WEBKIT_PC_MODULE="webkit-1.0"
+ PKG_CHECK_MODULES(WEBKIT, [$WEBKIT_PC_MODULE >= $WEBKIT_VERSION], , [
+ AC_MSG_RESULT(no)
+ AC_MSG_ERROR([
+You must have WebKit for GTK+2 $WEBKIT_VERSION or newer development headers
+installed to compile Pidgin. If you want to build only Finch then specify
+--disable-gtkui when running configure.
+])])
+ fi
+ AC_SUBST(WEBKIT_CFLAGS)
+ AC_SUBST(WEBKIT_LIBS)
+ AC_SUBST(WEBKIT_PC_MODULE)
+
+ dnl #######################################################################
+ dnl # Check if we should compile with enchant support
+ dnl #######################################################################
+ dnl We need enchant for spell checking dictionary enumeration,
+ dnl because webkit1 doesn't have this.
+ use_enchant=no
+ if test "x$enable_enchant" = "xyes" ; then
+ use_enchant=yes
+ PKG_CHECK_MODULES(ENCHANT, enchant, , [
+ AC_MSG_RESULT(no)
+ use_enchant=no
+ ])
+ if test "x$force_deps" = "xyes" -a "x$use_enchant" = "xno"; then
+ AC_MSG_ERROR([
+Enchant development headers not found.
+Use --disable-enchant if you do not need it.
+])
+ fi
+ if test "x$use_enchant" = "xyes" ; then
+ AC_DEFINE(USE_ENCHANT, 1, [Define if we're using enchant])
+ AC_SUBST(ENCHANT_CFLAGS)
+ AC_SUBST(ENCHANT_LIBS)
+ fi
+ fi
+
dnl #######################################################################
dnl # Check if we should compile with X support
dnl #######################################################################
@@ -495,7 +732,7 @@ Use --without-x if you do not need X11 support.
else
if test "x$force_deps" = "xyes" ; then
AC_MSG_ERROR([
-XScreenSaver extension development headers not found.
+XScreenSaver extension development headers (libXScrnSaver-devel or libxss-dev) not found.
Use --disable-screensaver if you do not need XScreenSaver extension support,
this is required for detecting idle time by mouse and keyboard usage.
])
@@ -546,41 +783,13 @@ Use --disable-sm if you do not need session management support.
fi
dnl #######################################################################
- dnl # Check for GtkSpell
- dnl #######################################################################
- if test "x$enable_gtkspell" = "xyes" ; then
- PKG_CHECK_MODULES(GTKSPELL, gtkspell-2.0 >= 2.0.2, , [
- AC_MSG_RESULT(no)
- enable_gtkspell="no"
- if test "x$force_deps" = "xyes" ; then
- AC_MSG_ERROR([
-GtkSpell development headers not found.
-Use --disable-gtkspell if you do not need it.
-])
- fi])
- if test "x$enable_gtkspell" = "xyes" ; then
- AC_DEFINE(USE_GTKSPELL, 1, [Define if we're using GtkSpell])
- AC_SUBST(GTKSPELL_CFLAGS)
- AC_SUBST(GTKSPELL_LIBS)
- fi
- fi
-
- dnl #######################################################################
dnl # Check for stuff needed by the Evolution integration plugin.
dnl #######################################################################
if test "x$enable_gevolution" = "xyes"; then
- evo_deps="libebook-1.2 libedata-book-1.2 evolution-data-server-1.2 < 3.6"
+ evo_deps="libebook-1.2 libedata-book-1.2 evolution-data-server-1.2 >= 3.6"
PKG_CHECK_MODULES(EVOLUTION_ADDRESSBOOK, $evo_deps, , [
enable_gevolution="no"
])
- if test "x$enable_gevolution" = "xno"; then
- evo_deps="libebook-1.0 libedata-book-1.0"
- PKG_CHECK_MODULES(EVOLUTION_ADDRESSBOOK, $evo_deps, [
- enable_gevolution="yes"
- ], [
- enable_gevolution="no"
- ])
- fi
if test "x$enable_gevolution" = "xyes"; then
AC_DEFINE(HAVE_EVOLUTION_ADDRESSBOOK, 1, [Define if we're using evolution addressbook.])
AC_SUBST(EVOLUTION_ADDRESSBOOK_CFLAGS)
@@ -588,7 +797,7 @@ Use --disable-gtkspell if you do not need it.
else
if test "x$force_deps" = "xyes" ; then
AC_MSG_ERROR([
-Evolution development headers not found (libebook, libedata-book, evolution-data-server < 3.6).
+Evolution development headers not found (libebook, libedata-book, evolution-data-server >= 3.6).
Use --disable-gevolution if you do not need it.
])
fi
@@ -609,12 +818,41 @@ Use --disable-cap if you do not need the Contact Availability Prediction plugin.
])
fi])
fi
-
+
+ dnl #######################################################################
+ dnl # Check for GCR for its certificate widgets
+ dnl #######################################################################
+ if test "x$enable_gcr" = "xyes"; then
+ if test "x$with_gtk" = "x3"; then
+ PKG_CHECK_MODULES(GCR, gcr-3, [
+ AC_DEFINE(ENABLE_GCR, 1, [Define to 1 if GCR is found.])], [
+ AC_MSG_RESULT(no)
+ enable_gcr="no"
+ if test "x$force_deps" = "xyes" ; then
+ AC_MSG_ERROR([
+GCR for GTK+3 development headers not found.
+Use --disable-gcr if you do not need GCR certificate widgets.
+])
+ fi])
+ else
+ PKG_CHECK_MODULES(GCR, gcr-0, [
+ AC_DEFINE(ENABLE_GCR, 1, [Define to 1 if GCR is found.])], [
+ AC_MSG_RESULT(no)
+ enable_gcr="no"
+ if test "x$force_deps" = "xyes" ; then
+ AC_MSG_ERROR([
+GCR for GTK+2 development headers not found.
+Use --disable-gcr if you do not need GCR certificate widgets.
+])
+ fi])
+ fi
+ fi
+
else # GTK
+ enable_gcr=no
enable_cap=no
enable_gevolution=no
- enable_gtkspell=no
enable_screensaver=no
enable_sm=no
enable_startup_notification=no
@@ -624,6 +862,7 @@ AM_CONDITIONAL(ENABLE_GTK, test "x$enable_gtkui" = "xyes")
AM_CONDITIONAL(BUILD_GEVOLUTION, test "x$enable_gevolution" = "xyes")
AM_CONDITIONAL(ENABLE_CAP, test "x$enable_cap" = "xyes")
AM_CONDITIONAL(ENABLE_GESTURES, test "x$enable_gestures" = "xyes")
+AM_CONDITIONAL(ENABLE_GCR, test "x$enable_gcr" = "xyes")
dnl #######################################################################
@@ -631,7 +870,7 @@ dnl # Check for ncurses and other things used by the console UI
dnl #######################################################################
GNT_LIBS=""
GNT_CFLAGS=""
-AC_ARG_WITH(ncurses-headers, [AC_HELP_STRING([--with-ncurses-headers=DIR],
+AC_ARG_WITH(ncurses-headers, [AS_HELP_STRING([--with-ncurses-headers=DIR],
[compile finch against the ncurses includes in DIR])],
[ac_ncurses_includes="$withval"], [ac_ncurses_includes=""])
if test "x$enable_consoleui" = "xyes"; then
@@ -639,10 +878,18 @@ if test "x$enable_consoleui" = "xyes"; then
AC_CHECK_LIB(panelw, update_panels, [GNT_LIBS="$GNT_LIBS -lpanelw"],
[enable_consoleui=no], [$GNT_LIBS])
+ if test "x$is_win32" = "xyes" ; then
+ ncurses_sys_prefix="/usr/$host/sys-root/mingw"
+ else
+ ncurses_sys_prefix="/usr"
+ fi
+
+ ncurses_sys_dirs="$ncurses_sys_prefix/include/ncursesw $ncurses_sys_prefix/include"
+
if test "x$enable_consoleui" = "xyes"; then
dnl # Some distros put the headers in ncursesw/, some don't
found_ncurses_h=no
- for location in $ac_ncurses_includes $NCURSES_HEADERS /usr/include/ncursesw /usr/include
+ for location in $ac_ncurses_includes $NCURSES_HEADERS $ncurses_sys_dirs
do
f="$location/ncurses.h"
orig_CFLAGS="$CFLAGS"
@@ -732,6 +979,30 @@ AC_SUBST(LIBXML_CFLAGS)
AC_SUBST(LIBXML_LIBS)
dnl #######################################################################
+dnl # Check for JSON-GLib (required)
+dnl #######################################################################
+
+PKG_CHECK_MODULES([JSON], [json-glib-1.0 >= 0.14.0], , [
+ AC_MSG_RESULT(no)
+ AC_MSG_ERROR([
+You must have JSON-GLib >= 0.14.0 development headers installed to build.
+])])
+
+AC_SUBST(JSON_CFLAGS)
+AC_SUBST(JSON_LIBS)
+
+dnl #######################################################################
+dnl # Check for zlib (required)
+dnl #######################################################################
+
+PKG_CHECK_MODULES(ZLIB, [zlib >= 1.2.0], , [
+ AC_SEARCH_LIBS([deflate], [z], [], AC_MSG_ERROR([You must have zlib >= 1.2.0 development headers installed to build.]), [])
+])
+
+AC_SUBST(ZLIB_CFLAGS)
+AC_SUBST(ZLIB_LIBS)
+
+dnl #######################################################################
dnl # GConf schemas
dnl #######################################################################
AC_PATH_PROG(GCONFTOOL, gconftool-2, no)
@@ -741,42 +1012,114 @@ AM_GCONF_SOURCE_2
dnl #######################################################################
dnl # Check for GStreamer
dnl #######################################################################
-dnl
-dnl TODO: Depend on gstreamer >= 0.10.10, and remove the conditional use of
-dnl gst_registry_fork_set_enabled.
-AC_ARG_ENABLE(gstreamer,
- [AC_HELP_STRING([--disable-gstreamer], [compile without GStreamer audio support])],
- enable_gst="$enableval", enable_gst="yes")
+
+dnl AC_ARG_ENABLE is declared near GTK2/3 configuration
+AC_ARG_WITH(gstreamer, [AS_HELP_STRING([--with-gstreamer=<version>],
+ [compile with GStreamer 0.10 or 1.0 interface (default: auto)])],
+ with_gstreamer="$withval", with_gstreamer="auto")
if test "x$enable_gst" != "xno"; then
- PKG_CHECK_MODULES(GSTREAMER, [gstreamer-0.10], [
- AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer for playing sounds])
- AC_SUBST(GSTREAMER_CFLAGS)
- AC_SUBST(GSTREAMER_LIBS)
- AC_CHECK_LIB(gstreamer-0.10, gst_registry_fork_set_enabled,
- [ AC_DEFINE(GST_CAN_DISABLE_FORKING, [],
- [Define if gst_registry_fork_set_enabled exists])],
- [], [$GSTREAMER_LIBS])
- ], [
- AC_MSG_RESULT(no)
- enable_gst="no"
- if test "x$force_deps" = "xyes" ; then
- AC_MSG_ERROR([
+ if test "x$with_gstreamer" == "xauto" -a "x$with_gtk" = "x3"; then
+ with_gstreamer="1.0"
+ elif test "x$with_gstreamer" == "xauto" -a "x$with_gtk" = "x2"; then
+ with_gstreamer="0.10"
+ fi
+ if test "x$with_gstreamer" == "xauto"; then
+ PKG_CHECK_MODULES(GSTREAMER, [gstreamer-1.0], [
+ AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer for playing sounds])
+ with_gstreamer="1.0"
+ AC_SUBST(GSTREAMER_CFLAGS)
+ AC_SUBST(GSTREAMER_LIBS)
+ dnl Check whether forking stuff is required for this version.
+ ], [
+ PKG_CHECK_MODULES(GSTREAMER, [gstreamer-0.10], [
+ AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer for playing sounds])
+ with_gstreamer="0.10"
+ AC_SUBST(GSTREAMER_CFLAGS)
+ AC_SUBST(GSTREAMER_LIBS)
+ ], [
+ AC_MSG_RESULT(no)
+ enable_gst="no"
+ if test "x$force_deps" = "xyes" ; then
+ AC_MSG_ERROR([
GStreamer development headers not found.
Use --disable-gstreamer if you do not need GStreamer (sound) support.
])
- fi])
+ fi
+ ])
+ ])
+ elif test "x$with_gstreamer" == "x1.0"; then
+ PKG_CHECK_MODULES(GSTREAMER, [gstreamer-1.0], [
+ AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer 1.0 for playing sounds])
+ AC_SUBST(GSTREAMER_CFLAGS)
+ AC_SUBST(GSTREAMER_LIBS)
+ ], [
+ AC_MSG_RESULT(no)
+ enable_gst="no"
+ if test "x$force_deps" = "xyes" ; then
+ AC_MSG_ERROR([
+GStreamer development headers not found.
+Use --disable-gstreamer if you do not need GStreamer (sound) support.
+])
+ fi
+ ])
+ elif test "x$with_gstreamer" == "x0.10"; then
+ PKG_CHECK_MODULES(GSTREAMER, [gstreamer-0.10], [
+ AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer 0.10 for playing sounds])
+ AC_SUBST(GSTREAMER_CFLAGS)
+ AC_SUBST(GSTREAMER_LIBS)
+ ], [
+ AC_MSG_RESULT(no)
+ enable_gst="no"
+ if test "x$force_deps" = "xyes" ; then
+ AC_MSG_ERROR([
+GStreamer development headers not found.
+Use --disable-gstreamer if you do not need GStreamer (sound) support.
+])
+ fi
+ ])
+ else
+ AC_MSG_ERROR([--with-gstreamer must specify one of 0.10, 1.0 or auto.])
+ fi
+fi
+
+if test "x$with_gtk" == "x3" -a "x$with_gstreamer" == "x0.10"; then
+ AC_MSG_ERROR([WebKitGTK+ 3.0 cannot be mixed with GStreamer 0.10.
+Please switch to WebKitGTK+ 2.0 or GStreamer 1.0.])
+elif test "x$with_gtk" == "x2" -a "x$with_gstreamer" == "x1.0"; then
+ AC_MSG_ERROR([WebKitGTK+ 2.0 cannot be mixed with GStreamer 1.0.
+Please switch to WebKitGTK+ 3.0 or GStreamer 0.10.])
+fi
+
+dnl #######################################################################
+dnl # Check for GStreamer Video
+dnl #######################################################################
+if test "x$enable_gst" != "xno" -a "x$with_gstreamer" == "x1.0"; then
+ AC_ARG_ENABLE(gstreamer-video,
+ [AS_HELP_STRING([--disable-gstreamer-video], [compile without GStreamer 1.0 Video Overlay support])],
+ enable_gstvideo="$enableval", enable_gstvideo="yes")
+ if test "x$enable_gstvideo" != "xno"; then
+ PKG_CHECK_MODULES(GSTVIDEO, [gstreamer-video-1.0], [
+ AC_DEFINE(USE_GSTVIDEO, 1, [Use GStreamer Video Overlay support])
+ AC_SUBST(GSTVIDEO_CFLAGS)
+ AC_SUBST(GSTVIDEO_LIBS)
+ ], [
+ enable_gstvideo="no"
+ ])
+ fi
+else
+ enable_gstvideo="no"
fi
dnl #######################################################################
dnl # Check for GStreamer Interfaces
dnl #######################################################################
-if test "x$enable_gst" != "xno"; then
+if test "x$enable_gst" != "xno" -a "x$with_gstreamer" == "x0.10"; then
AC_ARG_ENABLE(gstreamer-interfaces,
- [AC_HELP_STRING([--disable-gstreamer-interfaces], [compile without GStreamer interface support])],
+ [AS_HELP_STRING([--disable-gstreamer-interfaces], [compile without GStreamer 0.10 interface support])],
enable_gstinterfaces="$enableval", enable_gstinterfaces="yes")
if test "x$enable_gstinterfaces" != "xno"; then
PKG_CHECK_MODULES(GSTINTERFACES, [gstreamer-interfaces-0.10], [
- AC_DEFINE(USE_GSTINTERFACES, 1, [Use GStreamer interfaces for X overlay support])
+ AC_DEFINE(USE_GSTINTERFACES, 1, [Use GStreamer 0.10 interfaces for X overlay support])
AC_SUBST(GSTINTERFACES_CFLAGS)
AC_SUBST(GSTINTERFACES_LIBS)
], [
@@ -791,32 +1134,43 @@ dnl #######################################################################
dnl # Check for Farstream
dnl #######################################################################
AC_ARG_ENABLE(farstream,
- [AC_HELP_STRING([--disable-farstream], [compile without farstream support])],
+ [AS_HELP_STRING([--disable-farstream], [compile without farstream support])],
enable_farstream="$enableval", enable_farstream="yes")
if test "x$enable_farstream" != "xno"; then
- PKG_CHECK_MODULES(FARSTREAM, [farstream-0.1], [
- AC_SUBST(FARSTREAM_CFLAGS)
- AC_SUBST(FARSTREAM_LIBS)
- ], [
- # Try farsight.
- PKG_CHECK_MODULES(FARSTREAM, [farsight2-0.10 >= 0.0.9], [
- AC_DEFINE(HAVE_FARSIGHT, 1, [Use Farsight instead of Farstream])
+ if test "x$with_gstreamer" == "x1.0"; then
+ PKG_CHECK_MODULES(FARSTREAM, [farstream-0.2], [
AC_SUBST(FARSTREAM_CFLAGS)
AC_SUBST(FARSTREAM_LIBS)
], [
enable_farstream="no"
])
- ])
- fi
+ else
+ PKG_CHECK_MODULES(FARSTREAM, [farstream-0.1], [
+ AC_SUBST(FARSTREAM_CFLAGS)
+ AC_SUBST(FARSTREAM_LIBS)
+ ], [
+ # Try farsight.
+ PKG_CHECK_MODULES(FARSTREAM, [farsight2-0.10 >= 0.0.9], [
+ AC_DEFINE(HAVE_FARSIGHT, 1, [Use Farsight instead of Farstream])
+ AC_SUBST(FARSTREAM_CFLAGS)
+ AC_SUBST(FARSTREAM_LIBS)
+ ], [
+ enable_farstream="no"
+ ])
+ ])
+ fi
+fi
dnl #######################################################################
dnl # Check for Voice and Video support
dnl #######################################################################
AC_ARG_ENABLE(vv,
- [AC_HELP_STRING([--disable-vv], [compile without voice and video support])],
+ [AS_HELP_STRING([--disable-vv], [compile without voice and video support])],
enable_vv="$enableval", enable_vv="yes")
if test "x$enable_vv" != "xno"; then
- if test "x$enable_gstreamer" != "xno" -a "x$enable_gstinterfaces" != "xno" -a "x$enable_farstream" != "xno"; then
+ if test "x$enable_gst" != "xno" -a "x$with_gstreamer" == "x1.0" -a "x$enable_gstvideo" != "xno" -a "x$enable_farstream" != "xno"; then
+ AC_DEFINE(USE_VV, 1, [Use voice and video])
+ elif test "x$enable_gst" != "xno" -a "x$with_gstreamer" == "x0.10" -a "x$enable_gstinterfaces" != "xno" -a "x$enable_farstream" != "xno"; then
AC_DEFINE(USE_VV, 1, [Use voice and video])
else
enable_vv="no"
@@ -829,14 +1183,14 @@ Or use --disable-vv if you do not need voice/video support.
fi
fi
fi
-AM_CONDITIONAL(USE_VV, test "x$enable_gstreamer" != "xno" -a "x$enable_gstinterfaces" != "xno" -a "x$enable_farstream" != "xno")
+AM_CONDITIONAL(USE_VV, test "x$enable_vv" != "xno")
dnl #######################################################################
dnl # Check for Internationalized Domain Name support
dnl #######################################################################
AC_ARG_ENABLE(idn,
- [AC_HELP_STRING([--disable-idn], [compile without IDN support])],
+ [AS_HELP_STRING([--disable-idn], [compile without IDN support])],
[enable_idn="$enableval" force_idn=$enableval], [enable_idn="yes" force_idn=no])
if test "x$enable_idn" != "xno"; then
PKG_CHECK_MODULES(IDN, libidn >= 0.0.0, [
@@ -859,7 +1213,7 @@ dnl #######################################################################
dnl # Check for Meanwhile headers (for Sametime)
dnl #######################################################################
AC_ARG_ENABLE(meanwhile,
- [AC_HELP_STRING([--disable-meanwhile],
+ [AS_HELP_STRING([--disable-meanwhile],
[compile without meanwhile (required for Sametime support)])],
enable_meanwhile="$enableval", enable_meanwhile="yes")
if test "x$enable_meanwhile" = "xyes"; then
@@ -881,13 +1235,16 @@ dnl #######################################################################
dnl # Check for Native Avahi headers (for Bonjour)
dnl #######################################################################
AC_ARG_ENABLE(avahi,
- [AC_HELP_STRING([--disable-avahi],
+ [AS_HELP_STRING([--disable-avahi],
[compile without avahi (required for Bonjour support)])],
enable_avahi="$enableval", enable_avahi="yes")
-if test "x$enable_avahi" = "xyes"; then
- AC_ARG_WITH(avahi-client-includes, [AC_HELP_STRING([--with-avahi-client-includes=DIR], [compile the Bonjour plugin against the Avahi Client includes in DIR])], [ac_avahi_client_includes="$withval"], [ac_avahi_client_includes="no"])
- AC_ARG_WITH(avahi-client-libs, [AC_HELP_STRING([--with-avahi-client-libs=DIR], [compile the Bonjour plugin against the Avahi Client libs in DIR])], [ac_avahi_client_libs="$withval"], [ac_avahi_client_libs="no"])
+if test "x$enable_avahi" = "xyes" -a "x$is_win32" = "xyes" ; then
+ avahiincludes="yes"
+ avahilibs="yes"
+elif test "x$enable_avahi" = "xyes"; then
+ AC_ARG_WITH(avahi-client-includes, [AS_HELP_STRING([--with-avahi-client-includes=DIR], [compile the Bonjour plugin against the Avahi Client includes in DIR])], [ac_avahi_client_includes="$withval"], [ac_avahi_client_includes="no"])
+ AC_ARG_WITH(avahi-client-libs, [AS_HELP_STRING([--with-avahi-client-libs=DIR], [compile the Bonjour plugin against the Avahi Client libs in DIR])], [ac_avahi_client_libs="$withval"], [ac_avahi_client_libs="no"])
AVAHI_CFLAGS=""
AVAHI_LIBS=""
@@ -934,8 +1291,8 @@ AC_SUBST(AVAHI_LIBS)
dnl #######################################################################
dnl # Check for SILC client includes and libraries
dnl #######################################################################
-AC_ARG_WITH(silc-includes, [AC_HELP_STRING([--with-silc-includes=DIR], [compile the SILC plugin against includes in DIR])], [ac_silc_includes="$withval"], [ac_silc_includes="no"])
-AC_ARG_WITH(silc-libs, [AC_HELP_STRING([--with-silc-libs=DIR], [compile the SILC plugin against the SILC libs in DIR])], [ac_silc_libs="$withval"], [ac_silc_libs="no"])
+AC_ARG_WITH(silc-includes, [AS_HELP_STRING([--with-silc-includes=DIR], [compile the SILC plugin against includes in DIR])], [ac_silc_includes="$withval"], [ac_silc_includes="no"])
+AC_ARG_WITH(silc-libs, [AS_HELP_STRING([--with-silc-libs=DIR], [compile the SILC plugin against the SILC libs in DIR])], [ac_silc_libs="$withval"], [ac_silc_libs="no"])
SILC_CFLAGS=""
SILC_LIBS=""
have_silc="no"
@@ -952,25 +1309,6 @@ if test "x$silc_manual_check" = "xno"; then
], [
have_silc="no"
])
- if test "x$have_silc" = "xno"; then
- PKG_CHECK_MODULES(SILC, silcclient, [
- have_silc="yes"
- silc10includes="yes"
- silc10client="yes"
- ], [
- have_silc="no"
- ])
- dnl If silcclient.pc wasn't found, check for just silc.pc
- if test "x$have_silc" = "xno"; then
- PKG_CHECK_MODULES(SILC, silc, [
- have_silc="yes"
- silc10includes="yes"
- silc10client="yes"
- ], [
- have_silc="no"
- ])
- fi
- fi
else
if test "$ac_silc_includes" != "no"; then
SILC_CFLAGS="-I$ac_silc_includes"
@@ -988,17 +1326,6 @@ else
if test "x$silcincludes" = "xyes" -a "x$silcclient" = "xyes"; then
have_silc="yes"
- else
- CPPFLAGS_save="$CPPFLAGS"
- CPPFLAGS="$CPPFLAGS $SILC_CFLAGS"
- AC_CHECK_HEADER(silcincludes.h, [silc10includes=yes])
- CPPFLAGS="$CPPFLAGS_save"
-
- SILC_LIBS="$SILC_LIBS -lsilc -lsilcclient -lpthread $LIBDL"
- AC_CHECK_LIB(silcclient, silc_client_init, [silc10client=yes], , $SILC_LIBS)
- if test "x$silc10includes" = "xyes" -a "x$silc10client" = "xyes"; then
- have_silc="yes"
- fi
fi
fi
AC_SUBST(SILC_LIBS)
@@ -1006,20 +1333,6 @@ AC_SUBST(SILC_CFLAGS)
dnl SILC Toolkit >= 1.0.1 has a new MIME API
if test "x$silcclient" = "xyes"; then
AC_DEFINE(HAVE_SILCMIME_H, 1, [Define if we have silcmime.h])
-elif test "x$silc10client" = "xyes"; then
- CPPFLAGS_save="$CPPFLAGS"
- CPPFLAGS="$CPPFLAGS $SILC_CFLAGS"
- AC_MSG_CHECKING(for silcmime.h)
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
-#include <silcincludes.h>
-#include <silcmime.h>
- ]], [[]])], [
- AC_MSG_RESULT(yes)
- AC_DEFINE(HAVE_SILCMIME_H, 1, [Define if we have silcmime.h])
- ], [
- AC_MSG_RESULT(no)
- ])
- CPPFLAGS="$CPPFLAGS_save"
fi
dnl #######################################################################
@@ -1057,13 +1370,13 @@ AC_SUBST(LIBGADU_CFLAGS)
AC_ARG_ENABLE(distrib,,,enable_distrib=no)
AM_CONDITIONAL(DISTRIB, test "x$enable_distrib" = "xyes")
DYNAMIC_PRPLS=all
-AC_ARG_WITH(static-prpls, [AC_HELP_STRING([--with-static-prpls], [Link to certain protocols statically])], [STATIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`], [STATIC_PRPLS=""])
+AC_ARG_WITH(static-prpls, [AS_HELP_STRING([--with-static-prpls], [Link to certain protocols statically])], [STATIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`], [STATIC_PRPLS=""])
if test "x$STATIC_PRPLS" != "x" -a "x$DYNAMIC_PRPLS" = "xall"; then
DYNAMIC_PRPLS=""
fi
if test "x$STATIC_PRPLS" = "xall" ; then
- STATIC_PRPLS="bonjour gg irc jabber msn myspace mxit novell oscar sametime silc simple yahoo zephyr"
+ STATIC_PRPLS="bonjour gg irc jabber msn mxit novell oscar sametime silc simple yahoo zephyr"
fi
if test "x$have_meanwhile" != "xyes" ; then
STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/sametime//'`
@@ -1072,10 +1385,10 @@ if test "x$avahiincludes" != "xyes" -o "x$avahilibs" != "xyes"; then
STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/bonjour//'`
fi
if test "x$silcincludes" != "xyes" -o "x$silcclient" != "xyes"; then
- STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/silc/silc10/'`
+ STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/silc//'`
fi
-if test "x$silc10includes" != "xyes" -o "x$silc10client" != "xyes"; then
- STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/silc10//'`
+if test "x$is_win32" = "xyes" ; then
+ STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/zephyr//'`
fi
AC_SUBST(STATIC_PRPLS)
STATIC_LINK_LIBS=
@@ -1099,8 +1412,6 @@ for i in $STATIC_PRPLS ; do
else
if test "x$i" = "xsilc"; then
STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib${i}purple.la"
- elif test "x$i" = "xsilc10"; then
- STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/libsilcpurple.la"
else
STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib$i.la"
fi
@@ -1113,7 +1424,6 @@ for i in $STATIC_PRPLS ; do
irc) static_irc=yes ;;
jabber) static_jabber=yes ;;
msn) static_msn=yes ;;
- myspace) static_myspace=yes ;;
mxit) static_mxit=yes ;;
novell) static_novell=yes ;;
oscar) static_oscar=yes ;;
@@ -1121,7 +1431,6 @@ for i in $STATIC_PRPLS ; do
icq) static_oscar=yes ;;
sametime) static_sametime=yes ;;
silc) static_silc=yes ;;
- silc10) static_silc=yes ;;
simple) static_simple=yes ;;
yahoo) static_yahoo=yes ;;
zephyr) static_zephyr=yes ;;
@@ -1133,7 +1442,6 @@ AM_CONDITIONAL(STATIC_GG, test "x$static_gg" = "xyes")
AM_CONDITIONAL(STATIC_IRC, test "x$static_irc" = "xyes")
AM_CONDITIONAL(STATIC_JABBER, test "x$static_jabber" = "xyes")
AM_CONDITIONAL(STATIC_MSN, test "x$static_msn" = "xyes")
-AM_CONDITIONAL(STATIC_MYSPACE, test "x$static_myspace" = "xyes")
AM_CONDITIONAL(STATIC_MXIT, test "x$static_mxit" = "xyes")
AM_CONDITIONAL(STATIC_NOVELL, test "x$static_novell" = "xyes")
AM_CONDITIONAL(STATIC_OSCAR, test "x$static_oscar" = "xyes")
@@ -1146,9 +1454,9 @@ AC_SUBST(STATIC_LINK_LIBS)
AC_DEFINE_UNQUOTED(STATIC_PROTO_INIT, $extern_init static void static_proto_init(void) { $load_proto },
[Loads static protocol plugin module initialization functions.])
-AC_ARG_WITH(dynamic_prpls, [AC_HELP_STRING([--with-dynamic-prpls], [specify which protocols to build dynamically])], [DYNAMIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`])
+AC_ARG_WITH(dynamic_prpls, [AS_HELP_STRING([--with-dynamic-prpls], [specify which protocols to build dynamically])], [DYNAMIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`])
if test "x$DYNAMIC_PRPLS" = "xall" ; then
- DYNAMIC_PRPLS="bonjour gg irc jabber msn myspace mxit novell oscar sametime silc simple yahoo zephyr"
+ DYNAMIC_PRPLS="bonjour gg irc jabber msn mxit novell oscar sametime silc simple yahoo zephyr"
fi
if test "x$have_meanwhile" != "xyes"; then
DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/sametime//'`
@@ -1157,10 +1465,10 @@ if test "x$avahiincludes" != "xyes" -o "x$avahilibs" != "xyes"; then
DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/bonjour//'`
fi
if test "x$silcincludes" != "xyes" -o "x$silcclient" != "xyes"; then
- DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/silc/silc10/'`
+ DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/silc//'`
fi
-if test "x$silc10includes" != "xyes" -o "x$silc10client" != "xyes"; then
- DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/silc10//'`
+if test "x$is_win32" = "xyes" ; then
+ DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/zephyr//'`
fi
AC_SUBST(DYNAMIC_PRPLS)
for i in $DYNAMIC_PRPLS ; do
@@ -1170,7 +1478,6 @@ for i in $DYNAMIC_PRPLS ; do
irc) dynamic_irc=yes ;;
jabber) dynamic_jabber=yes ;;
msn) dynamic_msn=yes ;;
- myspace) dynamic_myspace=yes ;;
mxit) dynamic_mxit=yes ;;
novell) dynamic_novell=yes ;;
null) dynamic_null=yes ;;
@@ -1179,7 +1486,6 @@ for i in $DYNAMIC_PRPLS ; do
icq) dynamic_oscar=yes ;;
sametime) dynamic_sametime=yes ;;
silc) dynamic_silc=yes ;;
- silc10) dynamic_silc=yes ;;
simple) dynamic_simple=yes ;;
yahoo) dynamic_yahoo=yes ;;
zephyr) dynamic_zephyr=yes ;;
@@ -1187,15 +1493,15 @@ for i in $DYNAMIC_PRPLS ; do
esac
done
-AC_ARG_ENABLE(plugins, [AC_HELP_STRING([--disable-plugins], [compile without plugin support])], , enable_plugins=yes)
-AC_ARG_WITH(krb4, [AC_HELP_STRING([--with-krb4=PREFIX], [compile Zephyr plugin with Kerberos 4 support])], kerberos="$withval", kerberos="no")
-AC_ARG_WITH(zephyr, [AC_HELP_STRING([--with-zephyr=PREFIX], [compile Zephyr plugin against external libzephyr])], zephyr="$withval", zephyr="no")
+AC_ARG_ENABLE(plugins, [AS_HELP_STRING([--disable-plugins], [compile without plugin support])], , enable_plugins=yes)
+AC_ARG_WITH(krb4, [AS_HELP_STRING([--with-krb4=PREFIX], [compile Zephyr plugin with Kerberos 4 support])], kerberos="$withval", kerberos="no")
+AC_ARG_WITH(zephyr, [AS_HELP_STRING([--with-zephyr=PREFIX], [compile Zephyr plugin against external libzephyr])], zephyr="$withval", zephyr="no")
AM_CONDITIONAL(EXTERNAL_LIBZEPHYR, test "x$zephyr" != "xno")
AC_CHECK_HEADERS(sys/utsname.h)
AC_CHECK_FUNC(uname)
-AC_ARG_ENABLE(fortify, [AC_HELP_STRING([--disable-fortify], [compile without FORTIFY_SOURCE support])], , enable_fortify=yes)
+AC_ARG_ENABLE(fortify, [AS_HELP_STRING([--disable-fortify], [compile without FORTIFY_SOURCE support])], , enable_fortify=yes)
DEBUG_CFLAGS="$DEBUG_CFLAGS -DPURPLE_DISABLE_DEPRECATED -DPIDGIN_DISABLE_DEPRECATED -DFINCH_DISABLE_DEPRECATED -DGNT_DISABLE_DEPRECATED"
if test "x$GCC" = "xyes"; then
@@ -1212,21 +1518,13 @@ if test "x$GCC" = "xyes"; then
dnl This leads to spurious warnings using GPOINTER_TO_INT(), et al. directly on a function call.
dnl We'd need an intermediate variable.
dnl
- dnl Consider adding -Wfloat-equal.
- dnl This leads to warnings with Perl.
- dnl Perhaps we could write ugly configure magic and pass -Wno-float-equal down to that subdirectory.
- dnl On the other hand, it's probably actually broken, so maybe the Perl folks should fix that?
- dnl
- dnl Consider removing -Wno-sign-compare (from the -Wextra set) and fixing all those cases.
- dnl This is likely non-trivial.
- dnl
for newflag in \
"-Waggregate-return" \
"-Wcast-align" \
"-Wdeclaration-after-statement" \
"-Wendif-labels" \
"-Werror-implicit-function-declaration" \
- "-Wextra -Wno-sign-compare -Wno-unused-parameter" \
+ "-Wextra -Wno-unused-parameter" \
"-Wformat-security" \
"-Werror=format-security" \
"-Winit-self" \
@@ -1234,6 +1532,7 @@ if test "x$GCC" = "xyes"; then
"-Wmissing-noreturn" \
"-Wmissing-prototypes" \
"-Wpointer-arith" \
+ "-Wfloat-equal" \
"-Wundef" \
; do
orig_CFLAGS="$CFLAGS"
@@ -1272,9 +1571,10 @@ if test "x$GCC" = "xyes"; then
DEBUG_CFLAGS="-Wall $DEBUG_CFLAGS"
CFLAGS="-g $CFLAGS"
fi
+DEBUG_CPPFLAGS=`echo "$DEBUG_CFLAGS" | $sedpath 's/-Wdeclaration-after-statement//' | $sedpath 's/-Wmissing-prototypes//' | $sedpath 's/-Waggregate-return//'`
if test "x$SUNCC" = "xyes"; then
- CFLAGS="$CFLAGS -features=extensions"
+ CFLAGS="$CFLAGS -features=extensions"
fi
AC_SUBST(CFLAGS)
@@ -1286,17 +1586,48 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [
#endif
])], have_clang=yes, have_clang=no)
+AC_ARG_ENABLE(clang-address-sanitizer,
+ [AS_HELP_STRING([--enable-clang-address-sanitizer],
+ [compile with the Clang's address sanitizer enabled])],
+ enable_clang_address_sanitizer="$enableval", enable_clang_address_sanitizer="no")
+
+if test "x$enable_clang_address_sanitizer" = "xyes" -a "x$have_clang" = "xno"; then
+ AC_MSG_ERROR([
+Clang address sanitizer requested, but we don't compile with Clang.
+Disable the sanitizer or run configure script with CC and CCX set to clang binaries.
+])
+fi
+
if test "x$have_clang" = "xyes"; then
- dnl we don't care about it for 2.x.y
- DEBUG_CFLAGS=`echo $DEBUG_CFLAGS |$sedpath 's/-Wcast-align//'`
+ GLIB_LIBS=`echo $GLIB_LIBS | $sedpath 's/-pthread/-lpthread/'`
+fi
+
+if test "x$enable_clang_address_sanitizer" = "xyes"; then
+ CFLAGS="$CFLAGS -faddress-sanitizer -g -fno-omit-frame-pointer -fno-inline -fno-optimize-sibling-calls"
+fi
+
+
+AC_ARG_ENABLE(glib-errors-trace, [AS_HELP_STRING([--enable-glib-errors-trace], [print backtraces for glib errors])], enable_glibtrace="$enableval", enable_glibtrace="no")
+if test "x$enable_glibtrace" = "xyes"; then
+ if test "x$have_clang" = "xyes"; then
+ AC_MSG_ERROR([--enable-glib-errors-trace doesn't work with clang])
+ fi
+ AC_DEFINE(ENABLE_GLIBTRACE, 1, [Define if backtraces should be printed for glib errors.])
+ dnl CFLAGS="$CFLAGS -rdynamic"
+ CFLAGS="$CFLAGS -rdynamic"
fi
dnl #######################################################################
dnl # Check for D-Bus libraries
dnl #######################################################################
-AC_ARG_ENABLE(dbus, [AC_HELP_STRING([--disable-dbus], [disable D-Bus support])], , enable_dbus=yes)
-AC_ARG_ENABLE(nm, [AC_HELP_STRING([--disable-nm], [disable NetworkManager support (requires D-Bus)])], enable_nm=$enableval, enable_nm=yes)
+# dbus doesn't compile for win32 at the moment
+AC_ARG_ENABLE(dbus,
+ [AS_HELP_STRING([--disable-dbus], [disable D-Bus support])], ,
+ enable_dbus="$is_not_win32")
+AC_ARG_ENABLE(nm,
+ [AS_HELP_STRING([--disable-nm], [disable NetworkManager support (requires D-Bus)])],
+ enable_nm=$enableval, enable_nm="$is_not_win32")
if test "x$enable_dbus" = "xyes" ; then
AC_CHECK_PROG(enable_dbus, dbus-binding-tool, yes, no)
@@ -1358,6 +1689,170 @@ fi
AM_CONDITIONAL(ENABLE_UNITY, [test "x$enable_unity" = "xyes"])
dnl #######################################################################
+dnl # Check for Secret Service headers
+dnl #######################################################################
+
+# disabled - see secretservice.c
+#AC_ARG_ENABLE(libsecret, [AC_HELP_STRING([--disable-libsecret], [enable Secret Service support])], enable_secret_service=no, enable_secret_service=yes)
+
+#if test "x$enable_secret_service" = "xyes" ; then
+# PKG_CHECK_MODULES(SECRETSERVICE, [libsecret-1], [
+# AC_SUBST(SECRETSERVICE_CFLAGS)
+# AC_SUBST(SECRETSERVICE_LIBS)
+# AC_DEFINE(HAVE_SECRETSERVICE, 1, [Define if we have Secret Service.])
+# ])
+#fi
+
+#AM_CONDITIONAL(ENABLE_SECRETSERVICE, test "x$enable_secret_service" = "xyes")
+AM_CONDITIONAL(ENABLE_SECRETSERVICE, test "x1" = "x2")
+
+dnl #######################################################################
+dnl # Check for GNOME Keyring headers
+dnl #######################################################################
+
+AC_ARG_ENABLE(gnome-keyring,
+ [AC_HELP_STRING([--disable-gnome-keyring], [disable GNOME Keyring support])],
+ enable_gnome_keyring="$enableval", enable_gnome_keyring="$is_not_win32")
+
+if test "x$enable_gnome_keyring" = "xyes" ; then
+ PKG_CHECK_MODULES(GNOMEKEYRING, [gnome-keyring-1], [
+ AC_SUBST(GNOMEKEYRING_CFLAGS)
+ AC_SUBST(GNOMEKEYRING_LIBS)
+ AC_DEFINE(HAVE_GNOMEKEYRING, 1, [Define if we have GNOME Keyring.])
+ ], [
+ AC_MSG_RESULT(no)
+ enable_gnome_keyring="no"
+ if test "x$force_deps" = "xyes" ; then
+ AC_MSG_ERROR([
+GNOME Keyring development headers not found
+Use --disable-gnome-keyring if you do not need it.
+])
+ fi
+ ])
+fi
+
+AM_CONDITIONAL(ENABLE_GNOMEKEYRING, test "x$enable_gnome_keyring" = "xyes")
+
+dnl #######################################################################
+dnl # Check for KWallet headers
+dnl #######################################################################
+
+AC_ARG_ENABLE(kwallet,
+ [AC_HELP_STRING([--disable-kwallet], [disable KWallet support])],
+ enable_kwallet="$enableval", enable_kwallet="$is_not_win32")
+AC_ARG_WITH(kwallet-includes, [AC_HELP_STRING([--with-kwallet-includes=DIR], [compile the KWallet plugin against includes in DIR])], [ac_kwallet_includes="$withval"], [ac_kwallet_includes="no"])
+AC_ARG_WITH(kwallet-libs, [AC_HELP_STRING([--with-kwallet-libs=DIR], [compile the KWallet plugin against the KWallet libs in DIR])], [ac_kwallet_libs="$withval"], [ac_kwallet_libs="no"])
+
+if test "x$enable_kwallet" = "xyes"; then
+ dnl Ensure C++ compiler works
+ AC_CHECK_PROG(CXXTEST, [$CXX], [$CXX])
+ if test "x$CXXTEST" = "x"; then
+ enable_kwallet=no
+ if test "x$force_deps" = "xyes"; then
+ AC_MSG_ERROR([
+A C++ compiler was not found.
+Use --disable-kwallet if you do not need KWallet support.
+])
+ fi
+ fi
+fi
+
+AC_LANG_PUSH([C++])
+CPPFLAGS_save="$CPPFLAGS"
+LIBS_save="$LIBS"
+
+if test "x$enable_kwallet" = "xyes"; then
+ PKG_CHECK_MODULES(QT4, [QtCore], [
+ AC_SUBST(QT4_CFLAGS)
+ AC_SUBST(QT4_LIBS)
+ ], [
+ AC_MSG_RESULT(no)
+ enable_kwallet=no
+ if test "x$force_deps" = "xyes"; then
+ AC_MSG_ERROR([
+Qt4 development headers not found.
+Use --disable-kwallet if you do not need KWallet support.
+])
+ fi
+ ])
+fi
+
+if test "x$enable_kwallet" = "xyes"; then
+ 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=no
+ if test "x$force_deps" = "xyes"; then
+ AC_MSG_ERROR([
+kde4-config not found. $KDE4_CONFIG
+Use --disable-kwallet if you do not need KWallet support.
+Use --with-kwallet-includes and --with-kwallet-libs to set up includes manually.
+])
+ fi
+ fi
+ fi
+fi
+
+if test "x$enable_kwallet" = "xyes"; then
+ if test "$ac_kwallet_includes" != "no"; then
+ KWALLET_CXXFLAGS="-I$ac_kwallet_includes"
+ elif 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=no
+ if test "x$force_deps" = "xyes"; then
+ AC_MSG_ERROR([
+KWallet development headers not found.
+Use --disable-kwallet if you do not need KWallet support.
+])
+ fi
+])
+fi
+
+if test "x$enable_kwallet" = "xyes"; then
+ AC_MSG_CHECKING([for KWallet libraries])
+ if test "$ac_kwallet_libs" != "no"; then
+ KWALLET_LIBS="-L$ac_kwallet_libs -lkdeui"
+ elif 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=no
+ if test "x$force_deps" = "xyes"; then
+ AC_MSG_ERROR([
+KWallet development libraries not found.
+Use --disable-kwallet if you do not need KWallet support.
+])
+ fi
+ ])
+fi
+
+CPPFLAGS="$CPPFLAGS_save"
+LIBS="$LIBS_save"
+AC_LANG_POP
+
+AC_SUBST(KWALLET_CXXFLAGS)
+AC_SUBST(KWALLET_LIBS)
+
+AM_CONDITIONAL(ENABLE_KWALLET, test "x$enable_kwallet" = "xyes")
+
+dnl #######################################################################
dnl # Check for Python
dnl #######################################################################
@@ -1374,24 +1869,30 @@ dnl Alternatively, these python scripts could be rewritten
dnl in C (brrrr ...).
AC_ARG_WITH([python],
- AC_HELP_STRING([--with-python=PATH],
+ AS_HELP_STRING([--with-python=PATH],
[which python interpreter to use for dbus code generation]),
PYTHON=$withval)
if test "x$enable_dbus" = "xyes" || test "x$enable_consoleui" = "xyes" ; then
- if test -z "$PYTHON" -o "x$PYTHON" = "xyes"; then
+ if test -z "$PYTHON" -a "x$is_win32" = "xyes" ; then
+ dnl there are problems with include files when cross compiling
+ dnl feel free to fix it, if you want
+ PYTHON=no
+ elif test -z "$PYTHON" -o "x$PYTHON" = "xyes"; then
AC_PATH_PROG([PYTHON], [python], [no])
fi
- if test x"$PYTHON" = x"no" ; then
+ if test x"$PYTHON" = x"no" -a "x$is_win32" != "xyes" ; then
AC_MSG_WARN([python interpreter not found in your path])
enable_dbus=no
fi
+ if test "x$PYTHON" != "xno" ; then
if $PYTHON -c "import sys; sys.exit(sys.version[[:3]] >= '2.4')" ; then
AC_MSG_WARN([python version >= 2.4 required])
enable_dbus=no
fi
+ fi
fi
dnl ###########################################################################
@@ -1408,7 +1909,7 @@ dnl # This is still prone to error if one of the legacy directories exist
dnl # although a newer dbus is installed. But I have tried to order the
dnl # directory searching to keep this situation at a minimum.
dnl ###########################################################################
-AC_ARG_WITH(dbus-services, [AC_HELP_STRING([--with-dbus-services=<dir>], [where the D-Bus services directory is located.])])
+AC_ARG_WITH(dbus-services, [AS_HELP_STRING([--with-dbus-services=<dir>], [where the D-Bus services directory is located.])])
DBUS_SERVICES_DIR=""
@@ -1477,7 +1978,7 @@ AC_SUBST(PY_LIBS)
dnl #######################################################################
dnl # Check for Mono support
dnl #######################################################################
-AC_ARG_ENABLE(mono, [AC_HELP_STRING([--enable-mono], [compile with Mono runtime support (experimental)])], , enable_mono=no)
+AC_ARG_ENABLE(mono, [AS_HELP_STRING([--enable-mono], [compile with Mono runtime support (experimental)])], , enable_mono=no)
if test x"$enable_mono" = x"yes" ; then
PKG_CHECK_MODULES(MONO, mono, [
AC_SUBST(MONO_CFLAGS)
@@ -1518,7 +2019,11 @@ AM_CONDITIONAL(USE_MONO, test x"$enable_mono" = x"yes")
dnl #######################################################################
dnl # Check for Perl support
dnl #######################################################################
-AC_ARG_ENABLE(perl, [AC_HELP_STRING([--disable-perl], [compile without perl scripting])], , enable_perl=yes)
+
+# TODO: we could reconsider this for win32, if gobj.plugins branch fail to complete
+AC_ARG_ENABLE(perl,
+ [AS_HELP_STRING([--disable-perl], [compile without perl scripting])], ,
+ enable_perl="$is_not_win32")
if test "$enable_plugins" = no ; then
enable_perl=no
@@ -1635,13 +2140,37 @@ Use --disable-perl if you do not need Perl scripting support.
])
fi
+if test "$enable_perl" = yes ; then
+ AC_CACHE_CHECK(for new SvUPGRADE in perl API, ac_cv_perl_have_new_svupgrade, [
+ orig_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PERL_CFLAGS"
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+ #include <EXTERN.h>
+ #include <perl.h>
+ ]], [[
+ PerlInterpreter *my_perl;
+ SV *sv;
+ if (!SvUPGRADE(sv, SVt_PV)) {
+ /* SvUPGRADE is an expression, so it doesn't
+ * terminate in case of failure */
+ }
+ ]])],
+ [ac_cv_perl_have_new_svupgrade=no],
+ [ac_cv_perl_have_new_svupgrade=yes])
+ CFLAGS="$orig_CFLAGS"
+ ])
+ if test $ac_cv_perl_have_new_svupgrade = yes; then
+ AC_DEFINE(HAVE_NEW_SVUPGRADE, 1, [Define if you have SvUPGRADE terminating in case of failure.])
+ fi
+fi
+
dnl #######################################################################
dnl # SSL support
dnl #
dnl # Thanks go to Evolution for the checks.
dnl #######################################################################
-AC_ARG_WITH(system-ssl-certs, [AC_HELP_STRING([--with-system-ssl-certs=<dir>], [directory containing system-wide SSL CA certificates])], [ssl_certificates_dir=$withval])
+AC_ARG_WITH(system-ssl-certs, [AS_HELP_STRING([--with-system-ssl-certs=<dir>], [directory containing system-wide SSL CA certificates])], [ssl_certificates_dir=$withval])
SSL_CERTIFICATES_DIR=""
if ! test -z "$ssl_certificates_dir" ; then
@@ -1649,11 +2178,21 @@ if ! test -z "$ssl_certificates_dir" ; then
AC_MSG_ERROR([--with-system-ssl-certs requires that a location is specified, eg. --with-system-ssl-certs=/etc/pki/tls/certs])
fi
if ! test -d "$ssl_certificates_dir" ; then
- AC_MSG_ERROR([$ssl_certificates_dir does not exist, if this is the correct location please make sure that it exists.])
+ if test "x$is_win32" = "xyes" ; then
+ AC_MSG_WARN([$ssl_certificates_dir does not exist. \
+It may be OK when cross-compiling, but please make sure about it.])
+ else
+ AC_MSG_ERROR([$ssl_certificates_dir does not exist, \
+if this is the correct location please make sure that it exists.])
+ fi
fi
SSL_CERTIFICATES_DIR="$ssl_certificates_dir"
fi
AC_SUBST(SSL_CERTIFICATES_DIR)
+if test "x$SSL_CERTIFICATES_DIR" != "x" ; then
+ AC_DEFINE_UNQUOTED([SSL_CERTIFICATES_DIR], ["$SSL_CERTIFICATES_DIR"],
+ [Directory where SSL certificates can be found])
+fi
AM_CONDITIONAL(INSTALL_SSL_CERTIFICATES, test "x$SSL_CERTIFICATES_DIR" = "x")
dnl These two are inverses of each other <-- stolen from evolution!
@@ -1711,7 +2250,7 @@ if test "x$enable_gnutls" != "xno"; then
fi
AC_ARG_WITH(gnutls-libs,
- [AC_HELP_STRING([--with-gnutls-libs=PREFIX], [location of GnuTLS libraries.])],
+ [AS_HELP_STRING([--with-gnutls-libs=PREFIX], [location of GnuTLS libraries.])],
[ with_gnutls_libs="$withval" ])
if test "x$with_gnutls_libs" != "xno" -a \
@@ -1727,7 +2266,7 @@ if test "x$enable_gnutls" != "xno"; then
AC_CACHE_CHECK([for GnuTLS libraries], ac_cv_gnutls_libs,
[
LIBS="$LIBS $with_gnutls_libs -lgnutls"
- AC_TRY_LINK_FUNC(gnutls_init, ac_cv_gnutls_libs="yes", ac_cv_gnutls_libs="no")
+ AC_LINK_IFELSE([AC_LANG_CALL([], [gnutls_init])], ac_cv_gnutls_libs="yes", ac_cv_gnutls_libs="no")
LIBS="$LIBS_save"
])
@@ -1798,19 +2337,19 @@ if test "x$enable_nss" != "xno"; then
looked_for_nss="yes"
AC_ARG_WITH(nspr-includes,
- [AC_HELP_STRING([--with-nspr-includes=PREFIX], [specify location of Mozilla nspr4 includes.])],
+ [AS_HELP_STRING([--with-nspr-includes=PREFIX], [specify location of Mozilla nspr4 includes.])],
[with_nspr_includes="$withval"])
AC_ARG_WITH(nspr-libs,
- [AC_HELP_STRING([--with-nspr-libs=PREFIX], [specify location of Mozilla nspr4 libs.])],
+ [AS_HELP_STRING([--with-nspr-libs=PREFIX], [specify location of Mozilla nspr4 libs.])],
[with_nspr_libs="$withval"])
AC_ARG_WITH(nss-includes,
- [AC_HELP_STRING([--with-nss-includes=PREFIX], [specify location of Mozilla nss3 includes.])],
+ [AS_HELP_STRING([--with-nss-includes=PREFIX], [specify location of Mozilla nss3 includes.])],
[with_nss_includes="$withval"])
AC_ARG_WITH(nss-libs,
- [AC_HELP_STRING([--with-nss-libs=PREFIX], [specify location of Mozilla nss3 libs.])],
+ [AS_HELP_STRING([--with-nss-libs=PREFIX], [specify location of Mozilla nss3 libs.])],
[with_nss_libs="$withval"])
@@ -1930,7 +2469,7 @@ if test "x$enable_nss" != "xno"; then
LDFLAGS="$LDFLAGS"
fi
- AC_TRY_LINK_FUNC(PR_Init,
+ AC_LINK_IFELSE([AC_LANG_CALL([], [PR_Init])],
[ac_cv_moz_nspr_libs="yes"],
[ac_cv_moz_nspr_libs="no"])
@@ -2018,7 +2557,7 @@ if test "x$enable_nss" != "xno"; then
LDFLAGS="$LDFLAGS -L$with_nspr_libs -L$with_nss_libs"
LIBS="$nsslibs $nsprlibs"
- AC_TRY_LINK_FUNC(NSS_Init,
+ AC_LINK_IFELSE([AC_LANG_CALL([], [NSS_Init])],
[ac_cv_moz_nss_libs="yes"],
[ac_cv_moz_nss_libs="no"])
@@ -2026,7 +2565,7 @@ if test "x$enable_nss" != "xno"; then
nsslibs="-lssl3 -lsmime3 -lnss3 -lsoftokn3"
LDFLAGS="$LDFLAGS -L$with_nspr_libs -L$with_nss_libs"
LIBS="$LIBS $nsslibs"
- AC_TRY_LINK_FUNC(NSS_Init,
+ AC_LINK_IFELSE([AC_LANG_CALL([], [NSS_Init])],
[ac_cv_moz_nss_libs="yes"],
[ac_cv_moz_nss_libs="no"])
fi
@@ -2098,9 +2637,9 @@ fi
dnl #######################################################################
dnl # Check for Tcl
dnl #######################################################################
-AC_ARG_ENABLE(tcl, [AC_HELP_STRING([--disable-tcl],
+AC_ARG_ENABLE(tcl, [AS_HELP_STRING([--disable-tcl],
[compile without Tcl scripting])], enable_tcl="$enableval", enable_tcl="yes")
-AC_ARG_WITH(tclconfig, [AC_HELP_STRING([--with-tclconfig=DIR],
+AC_ARG_WITH(tclconfig, [AS_HELP_STRING([--with-tclconfig=DIR],
[directory containing tclConfig.sh])])
if test "$enable_plugins" = no; then
@@ -2177,9 +2716,9 @@ fi
dnl #######################################################################
dnl # Check for Tk
dnl #######################################################################
-AC_ARG_ENABLE(tk, [AC_HELP_STRING([--disable-tk],
+AC_ARG_ENABLE(tk, [AS_HELP_STRING([--disable-tk],
[compile without Tcl support for Tk])], enable_tk="$enableval", enable_tk="yes")
-AC_ARG_WITH(tkconfig, [AC_HELP_STRING([--with-tkconfig=DIR],
+AC_ARG_WITH(tkconfig, [AS_HELP_STRING([--with-tkconfig=DIR],
[directory containing tkConfig.sh])])
if test "$enable_tcl" = yes -a "$enable_tk" = yes; then
@@ -2241,6 +2780,7 @@ if test "$ac_cv_cygwin" = yes ; then
AC_DEFINE(DEBUG, 1, [Define if debugging is enabled.])
fi
+AC_SUBST(DEBUG_CPPFLAGS)
AC_SUBST(DEBUG_CFLAGS)
AC_SUBST(LDADD)
AC_SUBST(LIBS)
@@ -2261,7 +2801,7 @@ dnl #######################################################################
dnl AC_CHECK_SIZEOF(short)
AC_CHECK_FUNCS(snprintf connect)
AC_SUBST(SASL_LIBS)
-AC_ARG_ENABLE(cyrus-sasl, AC_HELP_STRING([--enable-cyrus-sasl], [enable Cyrus SASL support for xmpp/irc]), enable_cyrus_sasl=$enableval, enable_cyrus_sasl=no)
+AC_ARG_ENABLE(cyrus-sasl, AS_HELP_STRING([--enable-cyrus-sasl], [enable Cyrus SASL support for xmpp/irc]), enable_cyrus_sasl=$enableval, enable_cyrus_sasl=no)
if test "x$enable_cyrus_sasl" = "xyes" ; then
AC_CHECK_LIB(sasl2, sasl_client_init, [
AM_CONDITIONAL(USE_CYRUS_SASL, true)
@@ -2412,75 +2952,32 @@ AC_SUBST(CHECK_LIBS)
dnl #######################################################################
dnl # Disable pixmap installation
dnl #######################################################################
-AC_ARG_ENABLE(pixmaps-install, AC_HELP_STRING([--disable-pixmaps-install], [disable installation of pixmap files - Pidgin still needs them!]), enable_pixmaps="$enableval", enable_pixmaps=yes)
+AC_ARG_ENABLE(pixmaps-install, AS_HELP_STRING([--disable-pixmaps-install], [disable installation of pixmap files - Pidgin still needs them!]), enable_pixmaps="$enableval", enable_pixmaps=yes)
AM_CONDITIONAL(INSTALL_PIXMAPS, test "x$enable_pixmaps" = "xyes")
dnl #######################################################################
dnl # Tweak status tray icon installation directory
dnl #######################################################################
-AC_ARG_ENABLE(trayicon-compat, AC_HELP_STRING([--enable-trayicon-compat], [install tray icons in location compatible with older releases of hicolor-icon-theme]), enable_traycompat="$enableval", enable_traycompat=no)
+AC_ARG_ENABLE(trayicon-compat, AS_HELP_STRING([--enable-trayicon-compat], [install tray icons in location compatible with older releases of hicolor-icon-theme]), enable_traycompat="$enableval", enable_traycompat=no)
AM_CONDITIONAL(ENABLE_TRAYCOMPAT, test "x$enable_traycompat" = "xyes")
-dnl #######################################################################
-dnl # Check for Doxygen and dot (part of GraphViz)
-dnl #######################################################################
-AC_ARG_ENABLE(doxygen,
- [AC_HELP_STRING([--disable-doxygen],
- [disable documentation with doxygen])],
- enable_doxygen="$enableval", enable_doxygen="yes")
-AC_ARG_ENABLE(dot,
- [AC_HELP_STRING([--disable-dot],
- [disable graphs in doxygen via 'dot'])],
- enable_dot="$enableval", enable_dot="yes")
-AC_ARG_ENABLE(devhelp,
- [AC_HELP_STRING([--disable-devhelp],
- [disable building index for devhelp documentation browser])],
- enable_devhelp="$enableval", enable_devhelp="yes")
-
-if test "x$enable_doxygen" = xyes; then
- AC_CHECK_PROG(DOXYGEN, doxygen, true, false)
- if test $DOXYGEN = false; then
- AC_MSG_WARN([*** Doxygen not found, docs will not be available])
- enable_doxygen="no"
- else
- AC_DEFINE_UNQUOTED(HAVE_DOXYGEN, 1, [whether or not we have doxygen])
-
- if test "x$enable_dot" = "xyes"; then
- AC_CHECK_PROG(DOT, dot, true, false)
-
- if test $DOT = false; then
- enable_dot="no";
- AC_MSG_WARN([*** GraphViz dot not found, docs will not have graphs])
- else
- AC_DEFINE_UNQUOTED(HAVE_DOT, 1, [whether or not we have dot])
- fi
- fi
-
- if test "x$enable_devhelp" = "xyes"; then
- AC_CHECK_PROG(XSLTPROC, xsltproc, true, false)
-
- if test $XSLTPROC = false; then
- enable_devhelp="no";
- AC_MSG_WARN([*** xsltproc not found; devhelp index will not be created])
- else
- AC_DEFINE_UNQUOTED(HAVE_XSLTPROC, 1, [whether or not we have xsltproc for devhelp index])
- fi
- fi
+# check for gtk-doc
+m4_ifdef([GTK_DOC_CHECK], [
+GTK_DOC_CHECK([1.16],[--flavour no-tmpl])
+],[
+ if test "x$force_deps" = "xyes" -a "x$enable_gtk_doc" = "xyes"; then
+ AC_MSG_ERROR([
+You have requested to generate documentation, but gtk-doc was not found.
+ ])
fi
-else
- enable_dot="no"
- enable_devhelp="no"
-fi
-AC_SUBST(enable_doxygen)
-AC_SUBST(enable_dot)
-AC_SUBST(enable_devhelp)
-AM_CONDITIONAL(HAVE_DOXYGEN, test "x$enable_doxygen" = "xyes")
-AM_CONDITIONAL(HAVE_XSLTPROC, test "x$enable_devhelp" = "xyes")
+ AM_CONDITIONAL([ENABLE_GTK_DOC], false)
+ enable_gtk_doc=no
+])
-AC_ARG_ENABLE(debug, [AC_HELP_STRING([--enable-debug],
+AC_ARG_ENABLE(debug, [AS_HELP_STRING([--enable-debug],
[compile with debugging support])], , enable_debug=no)
if test "x$enable_debug" = "xyes" ; then
@@ -2490,20 +2987,23 @@ fi
AM_CONDITIONAL(PURPLE_AVAILABLE, true)
AC_CONFIG_FILES([Makefile
- Doxyfile
doc/Makefile
doc/pidgin.1
doc/finch.1
+ doc/reference/Makefile
+ doc/reference/finch/Makefile
+ doc/reference/finch/version.xml
+ doc/reference/libpurple/Makefile
+ doc/reference/libpurple/version.xml
+ doc/reference/pidgin/Makefile
+ doc/reference/pidgin/version.xml
m4macros/Makefile
pidgin.apspec
pidgin/Makefile
- pidgin/pidgin.pc
- pidgin/pidgin-uninstalled.pc
- pidgin/pidgin-2.pc
- pidgin/pidgin-2-uninstalled.pc
+ pidgin/pidgin-3.pc
+ pidgin/pidgin-3-uninstalled.pc
pidgin/pixmaps/Makefile
pidgin/pixmaps/emotes/default/24/Makefile
- pidgin/pixmaps/emotes/none/Makefile
pidgin/pixmaps/emotes/small/16/Makefile
pidgin/plugins/Makefile
pidgin/plugins/cap/Makefile
@@ -2514,14 +3014,17 @@ AC_CONFIG_FILES([Makefile
pidgin/plugins/perl/Makefile
pidgin/plugins/perl/common/Makefile.PL
pidgin/plugins/ticker/Makefile
- libpurple/ciphers/Makefile
+ pidgin/plugins/win32/transparency/Makefile
+ pidgin/plugins/win32/winprefs/Makefile
+ pidgin/themes/Makefile
+ pidgin/win32/pidgin_dll_rc.rc
+ pidgin/win32/pidgin_exe_rc.rc
libpurple/example/Makefile
libpurple/gconf/Makefile
- libpurple/purple.pc
- libpurple/purple-uninstalled.pc
- libpurple/purple-2.pc
- libpurple/purple-2-uninstalled.pc
+ libpurple/purple-3.pc
+ libpurple/purple-3-uninstalled.pc
libpurple/plugins/Makefile
+ libpurple/plugins/keyrings/Makefile
libpurple/plugins/mono/Makefile
libpurple/plugins/mono/api/Makefile
libpurple/plugins/mono/loader/Makefile
@@ -2536,31 +3039,32 @@ AC_CONFIG_FILES([Makefile
libpurple/protocols/irc/Makefile
libpurple/protocols/jabber/Makefile
libpurple/protocols/msn/Makefile
- libpurple/protocols/myspace/Makefile
libpurple/protocols/mxit/Makefile
libpurple/protocols/novell/Makefile
libpurple/protocols/null/Makefile
libpurple/protocols/oscar/Makefile
libpurple/protocols/sametime/Makefile
libpurple/protocols/silc/Makefile
- libpurple/protocols/silc10/Makefile
libpurple/protocols/simple/Makefile
libpurple/protocols/yahoo/Makefile
libpurple/protocols/zephyr/Makefile
libpurple/tests/Makefile
libpurple/purple.h
libpurple/version.h
+ libpurple/win32/libpurplerc.rc
share/sounds/Makefile
share/ca-certs/Makefile
finch/finch.pc
+ finch/finch_winres.rc
+ finch/libfinch_winres.rc
finch/Makefile
finch/libgnt/Makefile
finch/libgnt/gnt.pc
+ finch/libgnt/libgnt_winres.rc
finch/libgnt/wms/Makefile
finch/plugins/Makefile
po/Makefile.in
pidgin.desktop.in
- pidgin.spec
])
AC_OUTPUT
@@ -2568,7 +3072,10 @@ echo
echo $PACKAGE $VERSION
echo
-echo Build GTK+ 2.x UI............. : $enable_gtkui
+echo Build GTK+ UI................. : $enable_gtkui
+if test "x$enable_gtkui" = "xyes"; then
+ echo Build for GTK+ version........ : $with_gtk
+fi
echo Build console UI.............. : $enable_consoleui
echo Build for X11................. : $with_x
echo
@@ -2577,6 +3084,7 @@ echo Protocols to build dynamically : $DYNAMIC_PRPLS
echo Protocols to link statically.. : $STATIC_PRPLS
echo
echo Build with GStreamer support.. : $enable_gst
+echo Build for GStreamer version... : $with_gstreamer
echo Build with D-Bus support...... : $enable_dbus
echo Build with voice and video.... : $enable_vv
if test "x$enable_dbus" = "xyes" ; then
@@ -2600,16 +3108,28 @@ echo
echo Use XScreenSaver Extension.... : $enable_screensaver
echo Use X Session Management...... : $enable_sm
echo Use startup notification...... : $enable_startup_notification
-echo Build with GtkSpell support... : $enable_gtkspell
+echo Build with Enchant support.... : $use_enchant
+echo Build with GCR widgets........ : $enable_gcr
echo Build Unity integration plugin.: $enable_unity
echo
+echo Build with GNOME Keyring...... : $enable_gnome_keyring
+echo Build with KWallet............ : $enable_kwallet
+#echo Build with Secret Service..... : $enable_secret_service
+echo
echo Build with plugin support..... : $enable_plugins
echo Build with Mono support....... : $enable_mono
echo Build with Perl support....... : $enable_perl
echo Build with Tcl support........ : $enable_tcl
echo Build with Tk support......... : $enable_tk
+
+if test "x$is_win32" = "xyes" ; then
+ echo
+ echo Win32 directory structure..... : $with_win32_dirs
+fi
+
echo
echo Print debugging messages...... : $enable_debug
+echo Generate documentation........ : $enable_gtk_doc
echo
eval eval echo Pidgin will be installed in $bindir.
if test "x$pidginpath" != "x" ; then
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 4481e645bf..b0ea94ad2a 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -1,3 +1,7 @@
+if ENABLE_GTK_DOC
+SUBDIRS = reference
+endif
+
man_MANS =
if ENABLE_GTK
@@ -9,37 +13,10 @@ man_MANS += finch.1
endif
EXTRA_DIST = \
- C-HOWTO.dox \
PERL-HOWTO.dox \
- SIGNAL-HOWTO.dox \
TCL-HOWTO.dox \
- TracFooter.html \
- TracHeader.html \
- account-signals.dox \
- blist-signals.dox \
- certificate-signals.dox \
- cipher-signals.dox \
- connection-signals.dox \
- conversation-signals.dox \
- core-signals.dox \
- dbus-server-signals.dox \
funniest_home_convos.txt \
finch.1.in \
- gtkaccount-signals.dox \
- gtkblist-signals.dox \
- gtkconv-signals.dox \
- gtklog-signals.dox \
- gtkimhtml-signals.dox \
gtkrc-2.0 \
- imgstore-signals.dox \
- jabber-signals.dox \
- log-signals.dox \
- notify-signals.dox \
pidgin.1.in \
- plugin-i18n.dox \
- plugin-ids.dox \
- plugin-signals.dox \
- savedstatus-signals.dox \
- sound-signals.dox \
- the_penguin.txt \
- xfer-signals.dox
+ the_penguin.txt
diff --git a/doc/PERL-HOWTO.dox b/doc/PERL-HOWTO.dox
index a0d25d291f..51c73aeb2f 100644
--- a/doc/PERL-HOWTO.dox
+++ b/doc/PERL-HOWTO.dox
@@ -19,7 +19,7 @@ use Purple;
summary => "Test plugin for the Perl interpreter.",
description => "Your description here",
author => "John H. Kelm <johnhkelm\@gmail.com",
- url => "http://pidgin.im",
+ url => "https://pidgin.im",
load => "plugin_load",
unload => "plugin_unload"
diff --git a/doc/SIGNAL-HOWTO.dox b/doc/SIGNAL-HOWTO.dox
deleted file mode 100644
index 3007415ca2..0000000000
--- a/doc/SIGNAL-HOWTO.dox
+++ /dev/null
@@ -1,137 +0,0 @@
-/** @page signal-howto Signals HOWTO
-
- @section Introduction
- The libpurple signals interface is used for general event notification, such
- as plugins being loaded or unloaded, allowing the GUI frontend to respond
- appropriately to changing internal data. Unfortunately, its use is not at all
- obvious from the information in the header files. This document uses code
- snippets from the Pidgin/libpurple plugin systems to illustrate the proper
- use of signals.
-
- @section overview Overview of Signals
- Signals in libpurple are very similar to those in GTK+. When certain events
- happen, a named signal is "emitted" from a certain object. Emitting the
- signal triggers a series of callbacks that have been "connected" to that
- signal for that object. These callbacks take appropriate action in response
- to the signal.
-
- @section registering_signal Registering a Signal
- The first step of using a signal is registering it with libpurple so that
- callbacks may be connected to it. This is done using purple_signal_register()
- Here is a slightly modified example from @c purple_plugins_init in
- @c libpurple/plugin.c :
-
- @code
- purple_signal_register( purple_plugins_get_handle(), /* Instance */
- "plugin-load", /* Signal name */
- purple_marshal_VOID__POINTER,/* Marshal function */
- NULL, /* Callback return value type */
- 1, /* Number of callback arguments (not including void *data) */
- purple_value_new(PURPLE_TYPE_SUBTYPE,PURPLE_SUBTYPE_PLUGIN) /* Type of first callback argument */
- );
- @endcode
-
- @subsection Instance
- A reference to the object from which this signal is emitted, and to which
- potential callbacks should be connected. In this case, it will be the entire
- plugin module emitting the signal.
-
- @subsection signalname Signal Name
- Unique identifier for the signal itself.
-
- @subsection therest Callback function definition
- The rest of the arguments specify the form of the callback function.
-
- @subsubsection marshalfunc Marshal Function
- @c purple_marshal_VOID__POINTER represents the callback function prototype,
- not including a "data" argument, explained later. The form is
- @c purple_marshal_RETURNVALUETYPE__ARG1TYPE_ARG2TYPE_ETC. See signals.h for
- more possible types.
-
- In this case, the callback will have the form
- @code
- void cb(void *arg1, void *data)
- @endcode
-
- If @c purple_marshal_BOOLEAN__POINTER_POINTER_POINTER were specified, it
- would be:
- @code
- gboolean cb(void *arg1, void *arg2, void *arg3, void *data)
- @endcode
-
- The @c void @c *data argument at the end of each callback function
- provides the data argument given to purple_signal_connect() .
-
- @subsubsection cb_ret_type Callback return value type
- In our case, this is NULL, meaning "returns void".
- @todo This could be described better.
-
- @subsubsection num_args Number of arguments
- The number of arguments (not including @c data ) that the callback function
- will take.
-
- @subsubsection type_arg Type of argument
- @c purple_value_new(PURPLE_TYPE_SUBTYPE,PURPLE_SUBTYPE_PLUGIN) specifies that
- the first argument given to the callback will be a @c PurplePlugin* . You
- will need as many "type of argument" arguments to purple_signal_register() as
- you specified in "Number of arguments" above.
-
- @todo Describe this more.
-
- @see value.h
-
- @section connect Connecting to the signal
- Once the signal is registered, you can connect callbacks to it. First, you
- must define a callback function, such as this one from gtkplugin.c :
- @code
-static void plugin_load_cb(PurplePlugin *plugin, gpointer data)
-{
- GtkTreeView *view = (GtkTreeView *)data;
- plugin_loading_common(plugin, view, TRUE);
-}
- @endcode
- Note that the callback function prototype matches that specified in the call
- to purple_signal_register() above.
-
- Once the callback function is defined, you can connect it to the signal.
- Again from gtkplugin.c , in @c pidgin_plugin_dialog_show() :
- @code
- purple_signal_connect(purple_plugins_get_handle(), "plugin-load", /* What to connect to */
- plugin_dialog, /* Object receiving the signal */
- PURPLE_CALLBACK(plugin_load_cb), /* Callback function */
- event_view, /* Data to pass to the callback function
- );
- @endcode
-
- The first two arguments ("What to connect to") specify the object emitting
- the signal (the plugin module) and what signal to listen for ("plugin-load").
-
- The object receiving the signal is @c plugin_dialog , the Pidgin plugins
- dialog. When @c plugin_dialog is deleted, then
- @c purple_signals_disconnect_by_handle(plugin_dialog) should be called to
- remove all signal connections it is associated with.
-
- The callback function is given using a helper macro, and finally the
- @c data argument to be passed to @c plugin_load_cb is given as @c event_view,
- a pointer to the GTK widget that @c plugin_load_cb needs to update.
-
- @section emit-signal Emitting a signal
- Connecting callbacks to signals is all well and good, but how do you "fire"
- the signal and trigger the callback? At some point, you must "emit" the
- signal, which immediately calls all connected callbacks.
-
- As seen in @c purple_plugin_load() in plugin.c :
- @code
- purple_signal_emit(purple_plugins_get_handle(), "plugin-load", plugin);
- @endcode
- This causes the signal "plugin-load" to be emitted from the plugin module
- (given by @c purple_plugins_get_handle() ), with the newly loaded plugin as
- the argument to pass to any registered callback functions.
-
- In our example, @c plugin_load_cb is called immediately as
- @code
- plugin_load_cb(plugin, event_view);
- @endcode
- and does whatever it does.
-
- */
diff --git a/doc/TCL-HOWTO.dox b/doc/TCL-HOWTO.dox
index 43ea33aa99..20ac43887b 100644
--- a/doc/TCL-HOWTO.dox
+++ b/doc/TCL-HOWTO.dox
@@ -37,7 +37,7 @@ proc plugin_init { } {
"Example plugin registration" \
"Example of how to register a plugin for the Tcl HOWTO" \
"Ethan Blanton <eblanton@cs.purdue.edu>" \
- "http://pidgin.im/" ]
+ "https://pidgin.im/" ]
}
@endcode
@@ -292,7 +292,7 @@ purple::notify ?type? title primary secondary
purple::send_im gc who text
@endcode
- This sends an IM in the fashion of serv_send_im. @c gc is the GC of
+ This sends an IM in the fashion of purple_serv_send_im. @c gc is the GC of
the connection on which you wish to send (as returned by most event
handlers), @c who is the nick of the buddy to which you wish to send,
and @c text is the text of the message.
diff --git a/doc/TracFooter.html b/doc/TracFooter.html
deleted file mode 100644
index 139597f9cb..0000000000
--- a/doc/TracFooter.html
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/doc/TracHeader.html b/doc/TracHeader.html
deleted file mode 100644
index 139597f9cb..0000000000
--- a/doc/TracHeader.html
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/doc/account-signals.dox b/doc/account-signals.dox
deleted file mode 100644
index 9ef431cced..0000000000
--- a/doc/account-signals.dox
+++ /dev/null
@@ -1,252 +0,0 @@
-/** @page account-signals Account Signals
-
- @signals
- @signal account-created
- @signal account-destroying
- @signal account-added
- @signal account-connecting
- @signal account-removed
- @signal account-disabled
- @signal account-enabled
- @signal account-setting-info
- @signal account-set-info
- @signal account-status-changed
- @signal account-actions-changed
- @signal account-alias-changed
- @signal account-authorization-requested
- @signal account-authorization-requested-with-message
- @signal account-authorization-denied
- @signal account-authorization-granted
- @signal account-error-changed
- @signal account-signed-on
- @signal account-signed-off
- @signal account-connection-error
- @endsignals
-
- @see account.h
-
- <hr>
-
- @signaldef account-created
- @signalproto
-void (*account_created)(PurpleAccount *account);
- @endsignalproto
- @signaldesc
- Emitted when an account is created by calling purple_account_new.
- @param account The account.
- @since 2.6.0
- @endsignaldef
-
- @signaldef account-destroying
- @signalproto
-void (*account_destroying)(PurpleAccount *account);
- @endsignalproto
- @signaldesc
- Emitted when an account is about to be destroyed.
- @param account The account.
- @since 2.6.0
- @endsignaldef
-
- @signaldef account-added
- @signalproto
-void (*account_added)(PurpleAccount *account);
- @endsignalproto
- @signaldesc
- Emitted when an account is added.
- @param account The account that was added.
- @see purple_accounts_add
- @endsignaldef
-
- @signaldef account-connecting
- @signalproto
-void (*account_connecting)(PurpleAccount *account);
- @endsignalproto
- @signaldesc
- This is called when an account is in the process of connecting.
- @param account The account in the process of connecting.
- @endsignaldef
-
- @signaldef account-removed
- @signalproto
-void (*account_removed)(PurpleAccount *account);
- @endsignalproto
- @signaldesc
- Emitted when an account is removed.
- @param account The account that was removed.
- @see purple_accounts_remove
- @endsignaldef
-
- @signaldef account-disabled
- @signalproto
-void (*account_disabled)(PurpleAccount *account);
- @endsignalproto
- @signaldesc
- Emitted when an account is disabled.
- @param account The account that was disabled.
- @endsignaldef
-
- @signaldef account-enabled
- @signalproto
-void (*account_enabled)(PurpleAccount *account);
- @endsignalproto
- @signaldesc
- Emitted when an account is enabled.
- @param account The account that was enabled.
- @endsignaldef
-
- @signaldef account-setting-info
- @signalproto
-void (*account_setting_info)(PurpleAccount *account, const char *new_info);
- @endsignalproto
- @signaldesc
- Emitted when a user is about to send his new user info, or
- profile, to the server.
- @param account The account that the info will be set on.
- @param new_info The new information to set.
- @endsignaldef
-
- @signaldef account-set-info
- @signalproto
-void (*account_set_info)(PurpleAccount *account, const char *new_info);
- @endsignalproto
- @signaldesc
- Emitted when a user sent his new user info, or profile, to the server.
- @param account The account that the info was set on.
- @param new_info The new information set.
- @endsignaldef
-
- @signaldef account-status-changed
- @signalproto
-void (*account_status_changed)(PurpleAccount *account, PurpleStatus *old, PurpleStatus *new);
- @endsignalproto
- @signaldesc
- Emitted when the status of an account changes (after the change).
- @param account The account that changed status.
- @param old The status before change.
- @param new The status after change.
- @endsignaldef
-
- @signaldef account-actions-changed
- @signalproto
-void (*account_actions_changed)(PurpleAccount *account);
- @endsignalproto
- @signaldesc
- Emitted when the account actions are changed after initial connection.
- @param account The account whose actions changed.
- @endsignaldef
-
- @signaldef account-alias-changed
- @signalproto
-void (*account_alias_changed)(PurpleAccount *account, const char *old);
- @endsignalproto
- @signaldesc
- Emitted when the alias of an account changes (after the change).
- @param account The account for which the alias was changed.
- @param old The alias before change.
- @endsignaldef
-
- @signaldef account-authorization-requested
- @signalproto
-int (*account_authorization_requested)(PurpleAccount *account, const char *user);
- @endsignalproto
- @signaldesc
- Emitted when a user requests authorization.
- @param account The account.
- @param user The name of the user requesting authorization.
- @return Less than zero to deny the request without prompting, greater
- than zero if the request should be granted. If zero is returned,
- then the user will be prompted with the request.
- @since 2.3.0
- @endsignaldef
-
- @signaldef account-authorization-requested-with-message
- @signalproto
-int (*account_authorization_requested)(PurpleAccount *account, const char *user, const char *message);
- @endsignalproto
- @signaldesc
- Emitted when a user requests authorization.
- @param account The account.
- @param user The name of the user requesting authorization.
- @param message The authorization request message
- @return PURPLE_ACCOUNT_RESPONSE_IGNORE to silently ignore the request,
- PURPLE_ACCOUNT_RESPONSE_DENY to block the request (the sender might
- get informed, PURPLE_ACCOUNT_RESPONSE_ACCEPT if the request should be
- granted. If PURPLE_ACCOUNT_RESPONSE_PASS is returned, then the user
- will be prompted with the request.
- @since 2.8.0
- @endsignaldef
-
- @signaldef account-authorization-denied
- @signalproto
-void (*account_authorization_denied)(PurpleAccount *account, const char *user);
- @endsignalproto
- @signaldesc
- Emitted when the authorization request for a buddy is denied.
- @param account The account.
- @param user The name of the user requesting authorization.
- @since 2.3.0
- @endsignaldef
-
- @signaldef account-authorization-granted
- @signalproto
-void (*account_authorization_granted)(PurpleAccount *account, const char *user);
- @endsignalproto
- @signaldesc
- Emitted when the authorization request for a buddy is granted.
- @param account The account.
- @param user The name of the user requesting authorization.
- @since 2.3.0
- @endsignaldef
-
- @signaldef account-error-changed
- @signalproto
-void (*account_error_changed)(PurpleAccount *account, const PurpleConnectionErrorInfo *old_error, const PurpleConnectionErrorInfo *current_error);
- @endsignalproto
- @signaldesc
- Emitted when @a account's error changes. You should not call
- purple_account_clear_current_error() while this signal is being emitted.
- @param account The account whose error has changed.
- @param old_error The account's previous error, or @c NULL if it had no
- error. After this signal is emitted, @a old_error is
- not guaranteed to be a valid pointer.
- @param new_error The account's new error, or @c NULL if it has no error.
- If not @c NULL, @a new_error will remain a valid until
- pointer just after the next time this signal is emitted
- for this @a account.
- @see purple_account_get_current_error()
- @since 2.3.0
- @endsignaldef
-
- @signaldef account-signed-on
- @signalproto
-void (*signed_on)(PurpleAccount *account);
- @endsignalproto
- @signaldesc
- Emitted when an account has signed on.
- @param account The account that has signed on.
- @since 2.7.0
- @endsignaldef
-
- @signaldef account-signed-off
- @signalproto
-void (*signed_off)(PurpleAccount *account);
- @endsignalproto
- @signaldesc
- Emitted when an account has signed off.
- @param account The account that has signed off.
- @since 2.7.0
- @endsignaldef
-
- @signaldef account-connection-error
- @signalproto
-void (*connection_error)(PurpleAccount *gc, PurpleConnectionError err, const gchar *desc)
- @endsignalproto
- @signaldesc
- Emitted when a connection error occurs, before @ref signed-off.
- @param account The account on which the error has occurred
- @param err The error that occurred
- @param desc A description of the error, giving more information.
- @since 2.7.0
- @endsignaldef
- */
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doc/blist-signals.dox b/doc/blist-signals.dox
deleted file mode 100644
index 3617a389db..0000000000
--- a/doc/blist-signals.dox
+++ /dev/null
@@ -1,154 +0,0 @@
-/** @page blist-signals Buddy List Signals
-
- @signals
- @signal buddy-status-changed
- @signal buddy-idle-changed
- @signal buddy-signed-on
- @signal buddy-signed-off
- @signal update-idle
- @signal blist-node-extended-menu
- @signal buddy-added
- @signal buddy-removed
- @signal buddy-icon-changed
- @signal blist-node-aliased
- @signal buddy-caps-changed
- @signal ui-caps-changed
- @endsignals
-
- @see blist.h
-
- <hr>
-
- @signaldef buddy-status-changed
- @signalproto
-void (*buddy_away)(PurpleBuddy *buddy, PurpleStatus *old_status, PurpleStatus *status);
- @endsignalproto
- @signaldesc
- Emitted when a buddy on your buddy list goes away.
- @param buddy The buddy whose status changed.
- @param old_status The status that the buddy just changed from.
- @param status The status that the buddy just changed to.
- @endsignaldef
-
- @signaldef buddy-idle-changed
- @signalproto
-void (*buddy_idle)(PurpleBuddy *buddy, gboolean old_idle, gboolean idle);
- @endsignalproto
- @signaldesc
- Emitted when a buddy on your buddy list becomes idle.
- @param buddy The buddy whose idle status changed.
- @param old_idle Whether the buddy was idle.
- @param idle Whether the buddy is currently idle.
- @endsignaldef
-
- @signaldef buddy-signed-on
- @signalproto
-void (*buddy_signed_on)(PurpleBuddy *buddy);
- @endsignalproto
- @signaldesc
- Emitted when a buddy on your buddy list signs on.
- @param buddy The buddy that signed on.
- @endsignaldef
-
- @signaldef buddy-signed-off
- @signalproto
-void (*buddy_signed_off)(PurpleBuddy *buddy);
- @endsignalproto
- @signaldesc
- Emitted when a buddy on your buddy list signs off.
- @param buddy The buddy that signed off.
- @endsignaldef
-
- @signaldef update-idle
- @signalproto
-void (*update_idle)();
- @endsignalproto
- @signaldesc
- Emitted when the buddy list is refreshed and the idle times are updated.
- @endsignaldef
-
- @signaldef blist-node-extended-menu
- @signalproto
-void (*blist_node_extended_menu)(PurpleBlistNode *node, GList **menu)
- @endsignalproto
- @signaldesc
- Emitted when a buddlist menu is being constructed @a menu is a pointer to
- a GList of PurpleBlistNodeAction's allowing a plugin to add menu items
- @endsignaldef
-
- @signaldef blist-node-added
- @signalproto
-void (*blist_node_added)(PurpleBlistNode *node)
- @endsignalproto
- @signaldesc
- Emitted when a new blist node is added to the buddy list.
- @endsignaldef
-
- @signaldef blist-node-removed
- @signalproto
-void (*blist_node_removed)(PurpleBlistNode *node)
- @endsignalproto
- @signaldesc
- Emitted when a blist node is removed from the buddy list.
- @endsignaldef
-
- @signaldef buddy-added
- @signalproto
-void (*buddy_added)(PurpleBuddy *buddy)
- @endsignalproto
- @signaldesc
- Emitted when a new buddy is added to the buddy list.
- @deprecated Use blist-node-added instead.
- @endsignaldef
-
- @signaldef buddy-removed
- @signalproto
-void (*buddy_removed)(PurpleBuddy *buddy)
- @endsignalproto
- @signaldesc
- Emitted when a buddy is removed from the buddy list.
- @deprecated Use blist-node-removed instead.
- @endsignaldef
-
- @signaldef buddy-icon-changed
- @signalproto
-void (*buddy_icon_changed)(PurpleBuddy *buddy)
- @endsignalproto
- @signaldesc
- Emitted when a buddy's icon is set.
- @endsignaldef
-
- @signaldef blist-node-aliased
- @signalproto
-void (*blist_node_aliased)(PurpleBlistNode *node, const char *old_alias)
- @endsignalproto
- @signaldesc
- Emitted when a blist node (buddy, chat, or contact) is aliased.
- @endsignaldef
-
- @signaldef buddy-caps-changed
- @signalproto
-void (*buddy_caps_changed)(PurpleBuddy *buddy, PurpleMediaCaps newcaps,
- PurpleMediaCaps oldcaps)
- @endsignalproto
- @signaldesc
- Emitted when updating a buddy's media capabilities.
- @param buddy The buddy
- @param newcaps
- @param oldcaps
- @since 2.7.0
- @endsignaldef
-
- @signaldef ui-caps-changed
- @signalproto
-void (*ui_caps_changed)(PurpleMediaCaps newcaps, PurpleMediaCaps oldcaps)
- @endsignalproto
- @signaldesc
- Emitted when updating the media capabilities of the UI.
- @param newcaps
- @param oldcaps
- @since 2.7.0
- @endsignaldef
-
- */
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doc/certificate-signals.dox b/doc/certificate-signals.dox
deleted file mode 100644
index fd16674ff0..0000000000
--- a/doc/certificate-signals.dox
+++ /dev/null
@@ -1,33 +0,0 @@
-/** @page certificate-signals Certificate Signals
-
- @signals
- @signal certificate-stored
- @signal certificate-deleted
- @endsignals
-
- @see certificate.h
-
- <hr>
-
- @signaldef certificate-stored
- @signalproto
-void (*certificate_stored)(PurpleCertificatePool *pool, const gchar *id, gpointer data);
- @endsignalproto
- @signaldesc
- Emitted when a pool stores a certificate. Connect to the pool instance.
- @param pool Pool the certificate has been stored into
- @param id Key the certificate was stored under
- @endsignaldef
-
- @signaldef certificate-deleted
- @signalproto
-void (*certificate_deleted)(PurpleCertificatePool *pool, const gchar *id, gpointer data);
- @endsignalproto
- @signaldesc
- Emitted when a pool deletes a certificate. Connect to the pool instance.
- @param pool Pool the certificate was deleted from
- @param id Key that was deleted
- @endsignaldef
-
- */
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doc/cipher-signals.dox b/doc/cipher-signals.dox
deleted file mode 100644
index 30cc56cc26..0000000000
--- a/doc/cipher-signals.dox
+++ /dev/null
@@ -1,31 +0,0 @@
-/** @page cipher-signals Cipher Signals
-
- @signals
- @signal cipher-added
- @signal cipher-removed
- @endsignals
-
- @see cipher.h
-
- <hr>
-
- @signaldef cipher-added
- @signalproto
-void (*cipher_added)(PurpleCipher *cipher);
- @endsignalproto
- @signaldesc
- Emitted when a cipher is added.
- @param cipher The cipher that was added.
- @endsignaldef
-
- @signaldef cipher-removed
- @signalproto
-void (*cipher_removed)(PurpleCipher *cipher);
- @endsignalproto
- @signaldesc
- Emitted when a cipher is removed.
- @param cipher The cipher that was removed.
- @endsignaldef
-
- */
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doc/cmd-signals.dox b/doc/cmd-signals.dox
deleted file mode 100644
index f9bfdd83ca..0000000000
--- a/doc/cmd-signals.dox
+++ /dev/null
@@ -1,29 +0,0 @@
-/** @page cmd-signals Command Signals
- @signals
- @signal cmd-added
- @signal cmd-removed
- @endsignals
-
- @see cmds.h
-
- @signaldef cmd-added
- @signalproto
-void (*cmd_added)(const char *command, PurpleCmdPriority priority,
- PurpleCmdFlag flag);
- @endsignalproto
- @signaldesc
- Emitted when a new command is added.
- @param command The new command.
- @param priority The priority of the new command.
- @param flag The command flags.
- @endsignaldef
-
- @signaldef cmd-removed
- @signalproto
-void (*cmd_removed)(const char *command);
- @endsignalproto
- @signaldesc
- Emitted when a command is removed.
- @param command The removed command.
- @endsignaldef
-*/
diff --git a/doc/connection-signals.dox b/doc/connection-signals.dox
deleted file mode 100644
index ee0d62fba6..0000000000
--- a/doc/connection-signals.dox
+++ /dev/null
@@ -1,80 +0,0 @@
-/** @page connection-signals Connection Signals
-
- @signals
- @signal signing-on
- @signal signed-on
- @signal autojoin
- @signal signing-off
- @signal signed-off
- @signal connection-error
- @endsignals
-
- @see connection.h
-
- <hr>
-
- @signaldef signing-on
- @signalproto
-void (*signing_on)(PurpleConnection *gc);
- @endsignalproto
- @signaldesc
- Emitted when a connection is about to sign on.
- @param gc The connection that is about to sign on.
- @endsignaldef
-
- @signaldef signed-on
- @signalproto
-void (*signed_on)(PurpleConnection *gc);
- @endsignalproto
- @signaldesc
- Emitted when a connection has signed on.
- @param gc The connection that has signed on.
- @endsignaldef
-
- @signaldef autojoin
- @signalproto
-gboolean (*autojoin)(PurpleConnection *gc);
- @endsignalproto
- @signaldesc
- Emitted when a connection has signed on, after the signed-on signal, to
- signal UIs to autojoin chats if they wish. UIs should connect to this
- with @c PURPLE_SIGNAL_PRIORITY_HIGHEST to allow plugins to block this
- signal before the UI sees it and then re-emit it later.
- @param gc The connection that has signed on.
- @return @c TRUE if the signal was handled or @c FALSE otherwise. In
- practice, the return value is irrelevant, as it really only
- exists so plugins can block the UI's autojoin.
- @since 2.7.0
- @endsignaldef
-
- @signaldef signing-off
- @signalproto
-void (*signing_off)(PurpleConnection *gc);
- @endsignalproto
- @signaldesc
- Emitted when a connection is about to sign off.
- @param gc The connection that is about to sign off.
- @endsignaldef
-
- @signaldef signed-off
- @signalproto
-void (*signed_off)(PurpleConnection *gc);
- @endsignalproto
- @signaldesc
- Emitted when a connection has signed off.
- @param gc The connection that has signed off.
- @endsignaldef
-
- @signaldef connection-error
- @signalproto
-void (*connection_error)(PurpleConnection *gc, PurpleConnectionError err, const gchar *desc)
- @endsignalproto
- @signaldesc
- Emitted when a connection error occurs, before @ref signed-off.
- @param gc The connection on which the error has occurred
- @param err The error that occurred
- @param desc A description of the error, giving more information.
- @endsignaldef
-
- */
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doc/conversation-signals.dox b/doc/conversation-signals.dox
deleted file mode 100644
index 30415a9d1c..0000000000
--- a/doc/conversation-signals.dox
+++ /dev/null
@@ -1,521 +0,0 @@
-/** @page conversation-signals Conversation Signals
-
- @signals
- @signal writing-im-msg
- @signal wrote-im-msg
- @signal sending-im-msg
- @signal sent-im-msg
- @signal receiving-im-msg
- @signal received-im-msg
- @signal blocked-im-msg
- @signal writing-chat-msg
- @signal wrote-chat-msg
- @signal sending-chat-msg
- @signal sent-chat-msg
- @signal receiving-chat-msg
- @signal received-chat-msg
- @signal conversation-created
- @signal conversation-updated
- @signal deleting-conversation
- @signal buddy-typing
- @signal buddy-typing-stopped
- @signal chat-buddy-joining
- @signal chat-buddy-joined
- @signal chat-buddy-flags
- @signal chat-buddy-leaving
- @signal chat-buddy-left
- @signal chat-inviting-user
- @signal chat-invited-user
- @signal chat-invited
- @signal chat-invite-blocked
- @signal chat-joined
- @signal chat-join-failed
- @signal chat-left
- @signal chat-topic-changed
- @signal cleared-message-history
- @signal conversation-extended-menu
- @signal sent-attention
- @signal got-attention
- @endsignals
-
- @see conversation.h
-
- @signaldef writing-im-msg
- @signalproto
-gboolean (*writing_im_msg)(PurpleAccount *account, const char *who,
- char **message, PurpleConversation *conv,
- PurpleMessageFlags flags);
- @endsignalproto
- @signaldesc
- Emitted before a message is written in an IM conversation. If the
- message is changed, then the changed message is displayed and logged
- instead of the original message.
- @note
- Make sure to free @a *message before you replace it!
- @param account The account.
- @param who The name of the user.
- @param message A pointer to the message.
- @param conv The conversation.
- @param flags Flags for this message.
- @return @c TRUE if the message should be canceled, or @c FALSE otherwise.
- @endsignaldef
-
- @signaldef wrote-im-msg
- @signalproto
-void (*wrote_im_msg)(PurpleAccount *account, const char *who,
- char *message, PurpleConversation *conv,
- PurpleMessageFlags flags);
- @endsignalproto
- @signaldesc
- Emitted after a message is written and possibly displayed in a conversation.
- @param account The account.
- @param who The name of the user.
- @param message The message.
- @param conv The conversation.
- @param flags Flags for this message.
- @endsignaldef
-
- @signaldef sending-im-msg
- @signalproto
-void (*sending_im_msg)(PurpleAccount *account, const char *receiver,
- char **message);
- @endsignalproto
- @signaldesc
- Emitted before sending an IM to a user. @a message is a pointer to the
- message string, so the plugin can replace the message before being sent.
- @note
- Make sure to free @a *message before you replace it!
- @param account The account the message is being sent on.
- @param receiver The username of the receiver.
- @param message A pointer to the outgoing message. This can be modified.
- @endsignaldef
-
- @signaldef sent-im-msg
- @signalproto
-void (*sent_im_msg)(PurpleAccount *account, const char *receiver,
- const char *message);
- @endsignalproto
- @signaldesc
- Emitted after sending an IM to a user.
- @param account The account the message was sent on.
- @param receiver The username of the receiver.
- @param message The message that was sent.
- @endsignaldef
-
- @signaldef receiving-im-msg
- @signalproto
-gboolean (*receiving_im_msg)(PurpleAccount *account, char **sender,
- char **message, PurpleConversation *conv,
- PurpleMessageFlags *flags);
- @endsignalproto
- @signaldesc
- Emitted when an IM is received. The callback can replace the name of the
- sender, the message, or the flags by modifying the pointer to the
- strings and integer. This can also be used to cancel a message by
- returning @c TRUE.
- @note
- Make sure to free @a *sender and @a *message before you replace them!
- @return @c TRUE if the message should be canceled, or @c FALSE otherwise.
- @param account The account the message was received on.
- @param sender A pointer to the username of the sender.
- @param message A pointer to the message that was sent.
- @param conv The IM conversation.
- @param flags A pointer to the IM message flags.
- @endsignaldef
-
- @signaldef received-im-msg
- @signalproto
-void (*received_im_msg)(PurpleAccount *account, char *sender, char *message,
- PurpleConversation *conv, PurpleMessageFlags flags);
- @endsignalproto
- @signaldesc
- Emitted after an IM is received.
- @param account The account the message was received on.
- @param sender The username of the sender.
- @param message The message that was sent.
- @param conv The IM conversation.
- @param flags The IM message flags.
- @endsignaldef
-
- @signaldef blocked-im-msg
- @signalproto
-void (*blocked_im_msg)(PurpleAccount *account, const char *sender,
- const char *message, PurpleMessageFlags flags, time_t when);
- @endsignalproto
- @signaldesc
- Emitted after an IM is blocked due to privacy settings.
- @param account The account the message was received on.
- @param sender The username of the sender.
- @param message The message that was blocked.
- @param flags The IM message flags.
- @param when The time the message was sent.
- @since 2.5.0
- @endsignaldef
-
- @signaldef writing-chat-msg
- @signalproto
-gboolean (*writing_chat_msg)(PurpleAccount *account, const char *who,
- char **message, PurpleConversation *conv,
- PurpleMessageFlags flags);
- @endsignalproto
- @signaldesc
- Emitted before a message is written in a chat conversation. If the
- message is changed, then the changed message is displayed and logged
- instead of the original message.
- @note
- Make sure to free @a *message before you replace it!
- @param account The account.
- @param who The name of the user.
- @param message A pointer to the message.
- @param conv The conversation.
- @param flags Flags for this message.
- @return @c TRUE if the message should be canceled, or @c FALSE otherwise.
- @endsignaldef
-
- @signaldef wrote-chat-msg
- @signalproto
-void (*wrote_chat_msg)(PurpleAccount *account, const char *who,
- char *message, PurpleConversation *conv,
- PurpleMessageFlags flags);
- @endsignalproto
- @signaldesc
- Emitted after a message is written and possibly displayed in a chat.
- @param account The account.
- @param who The name of the user.
- @param message The message.
- @param conv The conversation.
- @param flags Flags for this message.
- @endsignaldef
-
- @signaldef sending-chat-msg
- @signalproto
-void (*sending_chat_msg)(PurpleAccount *account, char **message, int id);
- @endsignalproto
- @signaldesc
- Emitted before sending a message to a chat. @a message is a pointer to the
- message string, so the plugin can replace the message before being sent.
- @note
- Make sure to free @a *message before you replace it!
- @param account The account the message is being sent on.
- @param message A pointer to the message that will be sent.
- @param id The ID of the chat.
- @endsignaldef
-
- @signaldef sent-chat-msg
- @signalproto
-void (*sent_chat_msg)(PurpleAccount *account, const char *message, int id);
- @endsignalproto
- @signaldesc
- Emitted after sending a message to a chat.
- @param account The account the message was sent on.
- @param message The message that was sent.
- @param id The ID of the chat.
- @endsignaldef
-
- @signaldef receiving-chat-msg
- @signalproto
-gboolean (*receiving_chat_msg)(PurpleAccount *account, char **sender,
- char **message, PurpleConversation *conv, int *flags);
- @endsignalproto
- @signaldesc
- Emitted when a chat message is received. The callback can replace the
- name of the sender, the message, or the flags by modifying the pointer to the
- strings. This can also be used to cancel displaying a message by
- returning @c TRUE.
- @note
- Make sure to free @a *sender and @a *message before you replace them!
- @return @c TRUE if the message should be canceled, or @c FALSE otherwise.
- @param account The account the message was received on.
- @param sender A pointer to the username of the sender.
- @param message A pointer to the message that was sent.
- @param conv The chat conversation.
- @param flags A pointer to the chat message flags
- @endsignaldef
-
- @signaldef received-chat-msg
- @signalproto
-void (*received_chat_msg)(PurpleAccount *account, char *sender, char *message,
- PurpleConversation *conv, PurpleMessageFlags flags);
- @endsignalproto
- @signaldesc
- Emitted after a chat message is received.
- @param account The account the message was received on.
- @param sender The username of the sender.
- @param message The message that was sent.
- @param conv The chat conversation.
- @param flags The chat message flags.
- @endsignaldef
-
- @signaldef conversation-created
- @signalproto
-void (*conversation_created)(PurpleConversation *conv);
- @endsignalproto
- @signaldesc
- Emitted when a new conversation is created.
- @param conv The new conversation.
- @endsignaldef
-
- @signaldef conversation-updated
- @signalproto
-void (*conversation_updated)(PurpleConversation *conv,
- PurpleConvUpdateType type);
- @endsignalproto
- @signaldesc
- Emitted when a conversation is updated.
- @param conv The conversation that was updated.
- @param type The type of update that was made.
- @endsignaldef
-
- @signaldef deleting-conversation
- @signalproto
-void (*deleting_conversation)(PurpleConversation *conv);
- @endsignalproto
- @signaldesc
- Emitted just before a conversation is to be destroyed.
- @param conv The conversation that's about to be destroyed.
- @endsignaldef
-
- @signaldef buddy-typing
- @signalproto
-void (*buddy_typing)(PurpleAccount *account, const char *name);
- @endsignalproto
- @signaldesc
- Emitted when a buddy starts typing in a conversation window.
- @param account The account of the user which is typing.
- @param name The name of the user which is typing.
- @endsignaldef
-
- @signaldef buddy-typing-stopped
- @signalproto
-void (*buddy_typing_stopped)(PurpleAccount *account, const char *name);
- @endsignalproto
- @signaldesc
- Emitted when a buddy stops typing in a conversation window.
- @param account The account of the user which stopped typing.
- @param name The name of the user which stopped typing.
- @endsignaldef
-
- @signaldef chat-buddy-joining
- @signalproto
-gboolean (*chat_buddy_joining)(PurpleConversation *conv, const char *name,
- PurpleConvChatBuddyFlags flags);
- @endsignalproto
- @signaldesc
- Emitted when a buddy is joining a chat, before the list of
- users in the chat updates to include the new user.
- @return @c TRUE if the join should be hidden, or @c FALSE otherwise.
- @param conv The chat conversation.
- @param name The name of the user that is joining the conversation.
- @param flags The flags of the user that is joining the conversation.
- @endsignaldef
-
- @signaldef chat-buddy-joined
- @signalproto
-void (*chat_buddy_joined)(PurpleConversation *conv, const char *name,
- PurpleConvChatBuddyFlags flags,
- gboolean new_arrival);
- @endsignalproto
- @signaldesc
- Emitted when a buddy joined a chat, after the users list is updated.
- @param conv The chat conversation.
- @param name The name of the user that has joined the conversation.
- @param flags The flags of the user that has joined the conversation.
- @param new_arrival If the buddy is a new arrival.
- @endsignaldef
-
- @signaldef chat-join-failed
- @signalproto
-void (*chat_join_failed)(PurpleConnection *gc, GHashTable *components);
- @endsignalproto
- @signaldesc
- Emitted when an account fails to join a chat room
- @param gc The PurpleConnection of the account which failed to join the chat.
- @param data The components passed to serv_join_chat() originally.
- The hash function should be g_str_hash() and the equal
- function should be g_str_equal().
- @endsignaldef
-
- @signaldef chat-buddy-flags
- @signalproto
-void (*chat_buddy_flags)(PurpleConversation *conv, const char *name,
- PurpleConvChatBuddyFlags oldflags,
- PurpleConvChatBuddyFlags newflags);
- @endsignalproto
- @signaldesc
- Emitted when a user in a chat changes flags.
- @param conv The chat conversation.
- @param name The name of the user.
- @param oldflags The old flags.
- @param newflags The new flags.
- @endsignaldef
-
- @signaldef chat-buddy-leaving
- @signalproto
-gboolean (*chat_buddy_leaving)(PurpleConversation *conv, const char *name,
- const char *reason);
- @endsignalproto
- @signaldesc
- Emitted when a user is leaving a chat, before the user list is updated.
- This may include an optional reason why the user is leaving.
- @return @c TRUE if the leave should be hidden, or @c FALSE otherwise.
- @param conv The chat conversation.
- @param name The name of the user that is leaving the chat.
- @param reason The optional reason why the user is leaving.
- @endsignaldef
-
- @signaldef chat-buddy-left
- @signalproto
-void (*chat_buddy_left)(PurpleConversation *conv, const char *name,
- const char *reason);
- @endsignalproto
- @signaldesc
- Emitted when a user leaves a chat, after the user list is updated.
- This may include an optional reason why the user is leaving.
- @param conv The chat conversation.
- @param name The name of the user that left the chat.
- @param reason The optional reason why the user left the chat.
- @endsignaldef
-
- @signaldef chat-inviting-user
- @signalproto
-void (*chat_inviting_user)(PurpleConversation *conv, const char *name,
- char **invite_message);
- @endsignalproto
- @signaldesc
- Emitted when a user is being invited to the chat. The callback can
- replace the invite message to the invitee by modifying the pointer to
- the invite message.
- @note
- Make sure to free @a *invite_message before you replace it!
- @param conv The chat conversation.
- @param name The name of the user being invited.
- @param invite_message A pointer to the reason why a user is being
- invited.
- @endsignaldef
-
- @signaldef chat-invited-user
- @signalproto
-void (*chat_invited_user)(PurpleConversation *conv, const char *name,
- const char *invite_message);
- @endsignalproto
- @signaldesc
- Emitted when a user invited another user to a chat.
- @param conv The chat conversation.
- @param conv The name of the user that was invited.
- @param invite_message The message to be sent to the user when invited.
- @endsignaldef
-
- @signaldef chat-invited
- @signalproto
-gint (*chat_invited)(PurpleAccount *account, const char *inviter,
- const char *chat, const char *invite_message
- const GHashTable *components);
- @endsignalproto
- @signaldesc
- Emitted when an account was invited to a chat.
- @param account The account being invited.
- @param inviter The username of the person inviting the account.
- @param chat The name of the chat you're being invited to.
- @param invite_message The optional invite message.
- @param components The components necessary if you want to call
- serv_join_chat()
- @return Less than zero if the invitation should be rejected, greater than
- zero if the invitation should be accepted. If zero is returned, the
- default behavior will be maintained: the user will be prompted.
- @endsignaldef
-
- @signaldef chat-invite-blocked
- @signalproto
-void (*chat_invite_blocked)(PurpleAccount *account, const char *inviter,
- const char *name, const char *message, GHashTable *data);
- @endsignalproto
- @signaldesc
- Emitted when an invitation to join a chat is blocked.
- @param account The account the invitation was sent to.
- @param inviter The name of the person sending the invitation.
- @param name The name of the chat invited to.
- @param message The invitation message sent.
- @param data Hashtable containing data about the invited chat.
- @since 2.5.0
- @endsignaldef
-
- @signaldef chat-joined
- @signalproto
-void (*chat_joined)(PurpleConversation *conv);
- @endsignalproto
- @signaldesc
- Emitted when an account joins a chat room.
- @param conv The conversation that joined the chat room.
- @endsignaldef
-
- @signaldef chat-left
- @signalproto
-void (*chat_left)(PurpleConversation *conv);
- @endsignalproto
- @signaldesc
- Emitted when an account leaves a chat room.
- @param conv The conversation that left the chat room.
- @endsignaldef
-
- @signaldef chat-topic-changed
- @signalproto
-void (*chat_topic_changed)(PurpleConversation *conv, const char *who, const char *topic);
- @endsignalproto
- @signaldesc
- Emitted when the topic is changed in a chat.
- @param conv The conversation whose topic changed.
- @param who The name of the person that changed the topic.
- @param topic The new topic.
- @endsignaldef
-
- @signaldef conversation-extended-menu
- @signalproto
-void (*conversation_extended_menu)(PurpleConversation *conv, GList **list);
- @endsignalproto
- @signaldesc
- Emitted when the UI requests a list of plugin actions for a
- conversation.
- @param conv The conversation.
- @param list A pointer to the list of actions.
- @since 2.1.0
- @endsignaldef
-
- @signaldef cleared-message-history
- @signalproto
-void (*cleared_message_history)(PurpleConversation *conv);
- @endsignalproto
- @signaldesc
- Emitted when the conversation history is cleared.
- @param conv The conversation.
- @since 2.8.0
- @endsignaldef
-
- @signaldef sent-attention
- @signalproto
-void (*got_attention)(PurpleAccount *account, const char *who,
- PurpleConversation *conv, guint type)
- @endsignalproto
- @signaldesc
- Emitted when receiving an attention message (buzz, nudge, etc.).
- @param account The account
- @param who The name of the person receiving the attention
- @param conv The conversation
- @param type The attention type (an index starting at 0)
- @since 2.7.0
- @endsignaldef
-
- @signaldef got-attention
- @signalproto
-void (*got_attention)(PurpleAccount *account, const char *who,
- PurpleConversation *conv, guint type)
- @endsignalproto
- @signaldesc
- Emitted when receiving an attention message (buzz, nudge, etc.).
- @param account The account
- @param who The name of the person sending the attention
- @param conv The conversation
- @param type The attention type (an index starting at 0)
- @since 2.7.0
- @endsignaldef
-*/
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doc/core-signals.dox b/doc/core-signals.dox
deleted file mode 100644
index a4be04136d..0000000000
--- a/doc/core-signals.dox
+++ /dev/null
@@ -1,32 +0,0 @@
-/** @page core-signals Core Signals
-
- @signals
- @signal quitting
- @signal uri-handler
- @endsignals
-
- @see core.h
-
- <hr>
-
- @signaldef quitting
- @signalproto
-void (*quitting)();
- @endsignalproto
- @signaldesc
- Emitted when libpurple is quitting.
- @endsignaldef
-
- @signaldef uri-handler
- @signalproto
-gboolean (*uri_handler)(const gchar *proto, const gchar *cmd, GHashTable *params);
- @endsignalproto
- @signaldesc
- Emitted when handling a registered URI.
- @param proto The protocol of the URI.
- @param cmd The 'command' of the URI.
- @param params Any key/value parameters from the URI.
- @endsignaldef
-
- */
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doc/dbus-server-signals.dox b/doc/dbus-server-signals.dox
deleted file mode 100644
index f39c91572f..0000000000
--- a/doc/dbus-server-signals.dox
+++ /dev/null
@@ -1,34 +0,0 @@
-/** @page dbus-server-signals DBus Server Signals
-
- @signals
- @signal dbus-method-called
- @signal dbus-introspect
- @endsignals
-
- @see dbus-server.h
-
- <hr>
-
- @signaldef dbus-method-called
- @signalproto
-gboolean (*dbus_method_called)(DBusConnection *connection,
- DBusMessage *message);
- @endsignalproto
- @signaldesc
- Emitted when a dbus method is going to be called.
- @param connection The DBus connection.
- @param message The DBus message.
- @return TRUE if signal handler handled the method. ???
- @endsignaldef
-
- @signaldef dbus-introspect
- @signalproto
-void (*dbus_introspect)(GList **bidings_list);
- @endsignalproto
- @signaldesc
- ???
- @param bindings_list ???
- @endsignaldef
-
- */
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doc/finch.1.in b/doc/finch.1.in
index ad46b4612f..a38ccbc156 100644
--- a/doc/finch.1.in
+++ b/doc/finch.1.in
@@ -566,12 +566,12 @@ for the status window.
.SH FAQ
FAQ for \fBfinch\fR is located at
.br
-\fIhttp://developer.pidgin.im/wiki/Using%20Finch\fR
+\fIhttps://developer.pidgin.im/wiki/Using%20Finch\fR
.SH BUGS
Known bugs are listed at
.br
-\fIhttp://developer.pidgin.im/query?status=new&status=assigned&status=reopened&component=finch+%28gnt%2Fncurses%29&order=priority\fR
+\fIhttps://developer.pidgin.im/query?status=new&status=assigned&status=reopened&component=finch+%28gnt%2Fncurses%29&order=priority\fR
Before sending a bug report, please verify that you have the latest
version of \fBfinch\fR and libpurple. Many bugs (major and minor) are
@@ -581,15 +581,15 @@ been solved.
.SH PATCHES
If you fix a bug in \fBfinch\fR (or otherwise enhance it), please submit a
patch (using \fBmtn diff > my.diff\fR against the latest version from the
-Monotone repository) at \fIhttp://developer.pidgin.im/simpleticket\fR
+Mercurial repository) at \fIhttps://developer.pidgin.im/newticket\fR
You are also encouraged to drop by at \fB#pidgin\fR on \fIirc.freenode.net\fR
to discuss development.
.SH SEE ALSO
-\fIhttp://pidgin.im/\fR
+\fIhttps://pidgin.im/\fR
.br
-\fIhttp://developer.pidgin.im/\fR
+\fIhttps://developer.pidgin.im/\fR
.br
\fBpurple-remote\fR(1)
.br
diff --git a/doc/gtkaccount-signals.dox b/doc/gtkaccount-signals.dox
deleted file mode 100644
index 7f521f759b..0000000000
--- a/doc/gtkaccount-signals.dox
+++ /dev/null
@@ -1,20 +0,0 @@
-/** @page gtkaccount-signals GtkAccount Signals
-
- @signals
- @signal account-modified
- @endsignals
-
- @see gtkaccount.h
-
- <hr>
-
- @signaldef account-modified
- @signalproto
-void (*account-modified)(PurpleAccount *account);
- @endsignalproto
- @signaldesc
- Emitted when the settings for an account have been changed and saved.
- @param account The account that has been modified.
- @endsignaldef
-*/
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doc/gtkblist-signals.dox b/doc/gtkblist-signals.dox
deleted file mode 100644
index 2a6ba955ef..0000000000
--- a/doc/gtkblist-signals.dox
+++ /dev/null
@@ -1,70 +0,0 @@
-/** @page gtkblist-signals GtkBlist Signals
-
- @signals
- @signal gtkblist-hiding
- @signal gtkblist-unhiding
- @signal gtkblist-created
- @signal drawing-tooltip
- @signal drawing-buddy
- @endsignals
-
- @see gtkblist.h
-
- <hr>
-
- @signaldef gtkblist-hiding
- @signalproto
-void (*gtkblist-hiding)(PurpleBuddyList *blist);
- @endsignalproto
- @signaldesc
- Emitted when the buddy list is about to be hidden.
- @param blist The buddy list.
- @endsignaldef
-
- @signaldef gtkblist-unhiding
- @signalproto
-void (*gtkblist-unhiding)(PurpleBuddyList *blist);
- @endsignalproto
- @signaldesc
- Emitted when the buddy list is about to be unhidden.
- @param blist The buddy list.
- @endsignaldef
-
- @signaldef gtkblist-created
- @signalproto
-void (*gtkblist-created)(PurpleBuddyList *blist);
- @endsignalproto
- @signaldesc
- Emitted when the buddy list is created.
- @param blist The buddy list.
- @endsignaldef
-
- @signaldef drawing-tooltip
- @signalproto
-void (*drawing-tooltip)(PurpleBlistNode *node, GString *text, gboolean full);
- @endsignalproto
- @signaldesc
- Emitted just before a tooltip is displayed.
- @a text is a standard GString, so the plugin can modify the text that
- will be displayed.
- @note
- Make sure to free @a *text before you replace it!
- @param node The blist node for the tooltip.
- @param text A pointer to the text that will be displayed.
- @param full Whether we're doing a full tooltip for the priority buddy or
- a compact tooltip for a non-priority buddy.
- @endsignaldef
-
- @signaldef drawing-buddy
- @signalproto
-char *(*drawing-buddy)(PurpleBuddy *buddy);
- @endsignalproto
- @signaldesc
- Emitted to allow plugins to handle markup within a buddy's name or to
- override the default of no formatting for names shown in the buddy list.
- @param buddy A pointer to the PurpleBuddy that will be displayed.
- @return The text to display (must be allocated), or @c NULL if no
- changes to the default behavior are desired.
- @endsignaldef
-*/
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doc/gtkconv-signals.dox b/doc/gtkconv-signals.dox
deleted file mode 100644
index b1df1bbd8a..0000000000
--- a/doc/gtkconv-signals.dox
+++ /dev/null
@@ -1,146 +0,0 @@
-/** @page gtkconv-signals GtkConv Signals
-
- @signals
- @signal conversation-dragging
- @signal conversation-timestamp
- @signal displaying-im-msg
- @signal displayed-im-msg
- @signal displaying-chat-msg
- @signal displayed-chat-msg
- @signal conversation-switched
- @signal conversation-hiding
- @signal conversation-displayed
- @endsignals
-
- @see gtkconv.h
-
- <hr>
-
- @signaldef conversation-dragging
- @signalproto
-void (*conversation_dragging)(PidginWindow *source, PidginWindow *destination);
- @endsignalproto
- @signaldesc
- Emitted when a conversation is being drag and dropped between windows.
- @param source The window where the conversation is.
- @param destination The window where the conversation will be moved to.
- @endsignaldef
-
- @signaldef conversation-timestamp
- @signalproto
-char *(*conversation_timestamp)(PurpleConversation *conv, time_t when,
- gboolean show_date);
- @endsignalproto
- @signaldesc
- Emitted to allow plugins to customize the timestamp on a message.
- @param conv The conversation the message belongs to.
- @param when The time to be converted to a string.
- @param show_date Whether the date should be displayed.
- @return A textual representation of the time, or @c NULL to use a
- default format.
- @endsignaldef
-
-
- @signaldef displaying-im-msg
- @signalproto
-gboolean (*displaying_im_msg)(PurpleAccount *account, const char *who,
- char **message, PurpleConversation *conv,
- PurpleMessageFlags flags);
- @endsignalproto
- @signaldesc
- Emitted just before a message is displayed in an IM conversation.
- @a message is a pointer to a string, so the plugin can replace the
- message that will be displayed. This can also be used to cancel displaying
- a message by returning @c TRUE.
- @note
- Make sure to free @a *message before you replace it!
- @param account The account.
- @param who The name of the user.
- @param message A pointer to the message.
- @param conv The conversation.
- @param flags Flags for this message.
- @return @c TRUE if the message should be canceled, or @c FALSE otherwise.
- @endsignaldef
-
- @signaldef displayed-im-msg
- @signalproto
-void (*displayed_im_msg)(PurpleAccount *account, const char *who,
- char *message, PurpleConversation *conv,
- PurpleMessageFlags flags);
- @endsignalproto
- @signaldesc
- Emitted after a message is displayed in an IM conversation.
- @param account The account.
- @param who The name of the user.
- @param message The message.
- @param conv The conversation.
- @param flags Flags for this message.
- @endsignaldef
-
- @signaldef displaying-chat-msg
- @signalproto
-gboolean (*displaying_chat_msg)(PurpleAccount *account, const char *who,
- char **message, PurpleConversation *conv,
- PurpleMessageFlags flags);
- @endsignalproto
- @signaldesc
- Emitted just before a message is displayed in a chat.
- @a message is a pointer to a string, so the plugin can replace the
- message that will be displayed. This can also be used to cancel displaying
- a message by returning @c TRUE.
- @note
- Make sure to free @a *message before you replace it!
- @param account The account the message is being displayed and sent on.
- @param who The name of the user.
- @param message A pointer to the message that will be displayed and sent.
- @param conv The conversation the message is being displayed and sent on.
- @param flags Flags for this message.
- @return @c TRUE if the message should be canceled, or @c FALSE otherwise.
- @endsignaldef
-
- @signaldef displayed-chat-msg
- @signalproto
-void (*displayed_chat_msg)(PurpleAccount *account, const char *who,
- char *message, PurpleConversation *conv,
- PurpleMessageFlags flags);
- @endsignalproto
- @signaldesc
- Emitted after a message is displayed in a chat conversation.
- @param account The account the message is being displayed and sent on.
- @param who The name of the user.
- @param message A pointer to the message that will be displayed and sent.
- @param conv The conversation the message is being displayed and sent on.
- @param flags Flags for this message.
- @endsignaldef
-
- @signaldef conversation-switched
- @signalproto
-void (*conversation_switched)(PurpleConversation *conv);
- @endsignalproto
- @signaldesc
- Emitted when a window switched from one conversation to another.
- @param new_conv The now active conversation.
- @endsignaldef
-
- @signaldef conversation-hiding
- @signalproto
-void (*conversation_hiding)(PidginConversation *gtkconv);
- @endsignalproto
- @signaldesc
- Emitted immediately before an existing conversation is hidden.
- @param gtkconv The PidginConversation
- @since 2.2.0
- @endsignaldef
-
- @signaldef conversation-displayed
- @signalproto
-void (*conversation_displayed)(PidginConversation *gtkconv);
- @endsignalproto
- @signaldesc
- Emitted right after the Pidgin UI is attached to a new or a hidden conversation.
- @param gtkconv The PidginConversation
- @since 2.2.0
- @endsignaldef
-
-*/
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doc/gtkimhtml-signals.dox b/doc/gtkimhtml-signals.dox
deleted file mode 100644
index a2b9a87a89..0000000000
--- a/doc/gtkimhtml-signals.dox
+++ /dev/null
@@ -1,74 +0,0 @@
-/** @page gtkimhtml-signals GtkIMHtml Signals
-
- @signals
- @signal url_clicked
- @signal format_buttons_update
- @signal format_function_clear
- @signal format_function_toggle
- @signal format_function_update
- @signal paste
- @endsignals
-
- @see gtkimhtml.h
-
- <hr>
-
- @signaldef url_clicked
- @signalproto
-void (*url_clicked)(GtkIMHtml *imhtml, char *uri);
- @endsignalproto
- @signaldesc Emitted when a link is clicked
- @param imhtml The GtkIMHtml emitting the signal.
- @param url The uri.
- @endsignaldef
-
- @signaldef format_buttons_update
- @signalproto
-void (*format_buttons_update)(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons, gpointer data);
- @endsignalproto
- @signaldesc Emitted when allowed formatting has changed.
- @param imhtml The GtkIMHtml emitting the signal.
- @param buttons GtkIMHtmlButtons for the GtkIMHtml.
- @param data User defined data.
- @endsignaldef
-
- @signaldef format_function_clear
- @signalproto
-void (*format_function_clear)(GtkIMHtml *imhtml, gpointer data);
- @endsignalproto
- @signaldesc Emitted when clearing the formatting for the GtkIMHtml.
- @param imhtml The GtkIMHtml emitting the signal.
- @param data User defined data.
- @endsignaldef
-
- @signaldef format_function_toggle
- @signalproto
-void (*format_function_toggle)(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons, gpointer data);
- @endsignalproto
- @signaldesc Emitted when a format has been toggled.
- @param imhtml The GtkIMHtml emitting the signal.
- @param buttons GtkIMHtmlButtons for the GtkIMHtml.
- @param data User defined data.
- @endsignaldef
-
- @signaldef format_function_update
- @signalproto
-void (*format_function_update)(GtkIMHtml *imhtml, gpointer data);
- @endsignalproto
- @signaldesc Emitted when the cursor has moved and formatting has changed
- @param imhtml The GtkIMHtml emitting the signal.
- @param data User defined data.
-
- @signaldef paste
- @signalproto
-void (*paste) (GtkIMHtml *imhtml, char *format)
- @endsignalproto
- @signaldesc Emitted when paste from the clipboard is requested.
- @param imhtml The GtkIMHtml emitting the signal.
- @param format If 'text', then the formatting of the clipboard content
- will be removed before pasting. If empty or 'html', then
- the formatting will not be removed. Any other value for
- this parameter is ignored and nothing is pasted.
- @endsignaldef
-*/
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doc/gtklog-signals.dox b/doc/gtklog-signals.dox
deleted file mode 100644
index e1975e4164..0000000000
--- a/doc/gtklog-signals.dox
+++ /dev/null
@@ -1,22 +0,0 @@
-/** @page gtklog-signals GtkLog Signals
-
- @signals
- @signal log-displaying
- @endsignals
-
- @see gtklog.h
-
- <hr>
-
- @signaldef log-displaying
- @signalproto
-void (*log_displaying)(PidginLogViewer *viewer, PurpleLog *log);
- @endsignalproto
- @signaldesc
- Emitted when a log is being displayed.
- @param viewer The log viewer
- @param log The log being displayed
- @endsignaldef
-
-*/
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doc/imgstore-signals.dox b/doc/imgstore-signals.dox
deleted file mode 100644
index 4445c24f4f..0000000000
--- a/doc/imgstore-signals.dox
+++ /dev/null
@@ -1,26 +0,0 @@
-/** @page imgstore-signals Image Store Signals
-
- @signals
- @signal image-deleting
- @endsignals
-
- @see imgstore.h
-
- <hr>
-
- @signaldef image-deleting
- @signalproto
-char *(*image_deleting)(const PurpleStoredImage *img);
- @endsignalproto
- @signaldesc
- Emitted when a #PurpleStoredImage is about to be destroyed. This allows
- for what amounts to weak references. Code can hold onto a pointer to
- the PurpleStoredImage without actually "holding" a reference. They can
- then use a signal handler to let them know when their img is about to
- be destroyed.
- @param img The image about to be destroyed.
- @note It's not possible to purple_imgstore_ref() img to save it.
- @endsignaldef
-
-*/
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doc/jabber-signals.dox b/doc/jabber-signals.dox
deleted file mode 100644
index 0d022a0424..0000000000
--- a/doc/jabber-signals.dox
+++ /dev/null
@@ -1,138 +0,0 @@
-/** @page jabber-signals Jabber Signals
-
- @signals
- @signal jabber-receiving-iq
- @signal jabber-receiving-message
- @signal jabber-receiving-presence
- @signal jabber-watched-iq
- @signal jabber-register-namespace-watcher
- @signal jabber-unregister-namespace-watcher
- @signal jabber-sending-xmlnode
- @signal jabber-receiving-xmlnode
- @endsignals
-
- <hr>
-
- @signaldef jabber-receiving-iq
- @signalproto
-gboolean (*iq_received)(PurpleConnection *gc, const char *type, const char *id,
- const char *from, xmlnode *iq);
- @endsignalproto
- @signaldesc
- Emitted when an XMPP IQ stanza is received. Allows a plugin to process IQ
- stanzas.
- @param gc The connection on which the stanza is received
- @param type The IQ type ('get', 'set', 'result', or 'error')
- @param id The ID attribute from the stanza. MUST NOT be NULL.
- @param from The originator of the stanza. MAY BE NULL if the stanza
- originated from the user's server.
- @param iq The full stanza received.
- @return TRUE if the plugin processed this stanza and *nobody else* should
- process it. FALSE otherwise.
- @endsignaldef
-
- @signaldef jabber-receiving-message
- @signalproto
-gboolean (*message_received)(PurpleConnection *gc, const char *type,
- const char *id, const char *from, const char *to,
- xmlnode *message);
- @endsignalproto
- @signaldesc
- Emitted when an XMPP message stanza is received. Allows a plugin to
- process message stanzas.
- @param gc The connection on which the stanza is received
- @param type The message type (see rfc3921 or rfc3921bis)
- @param id The ID attribute from the stanza. MAY BE NULL.
- @param from The originator of the stanza. MAY BE NULL if the stanza
- originated from the user's server.
- @param to The destination of the stanza. This is probably either the
- full JID of the receiver or the receiver's bare JID.
- @param message The full stanza received.
- @return TRUE if the plugin processed this stanza and *nobody else* should
- process it. FALSE otherwise.
- @endsignaldef
-
- @signaldef jabber-receiving-presence
- @signalproto
-gboolean (*presence_received)(PurpleConnection *gc, const char *type,
- const char *from, xmlnode *presence);
- @endsignalproto
- @signaldesc
- Emitted when an XMPP presence stanza is received. Allows a plugin to process
- presence stanzas.
- @param gc The connection on which the stanza is received
- @param type The presence type (see rfc3921 or rfc3921bis). NULL indicates
- this is an "available" (i.e. online) presence.
- @param from The originator of the stanza. MAY BE NULL if the stanza
- originated from the user's server.
- @param presence The full stanza received.
- @return TRUE if the plugin processed this stanza and *nobody else* should
- process it. FALSE otherwise.
- @endsignaldef
-
- @signaldef jabber-watched-iq
- @signalproto
-gboolean (*watched_iq)(PurpleConnection *gc, const char *type, const char *id,
- const char *from, xmlnode *child);
- @endsignalproto
- @signaldesc
- Emitted when an IQ with a watched (child, namespace) pair is received. See
- jabber-register-namespace-watcher and jabber-unregister-namespace-watcher.
- @param gc The connection on which the stanza is received
- @param type The IQ type ('get', 'set', 'result', or 'error')
- @param id The ID attribute from the stanza. MUST NOT be NULL.
- @param from The originator of the stanza. MAY BE NULL if the stanza
- originated from the user's server.
- @param child The child node with namespace.
- @return TRUE if the plugin processed this stanza and *nobody else* should
- process it. FALSE otherwise.
- @endsignaldef
-
- @signaldef jabber-register-namespace-watcher
- @signalproto
-void (register_namespace_watcher)(const char *node, const char *namespace);
- @endsignalproto
- @signaldesc
- Emit this signal to register your desire to have specific IQ stanzas to be
- emitted via the jabber-watched-iq signal when received.
- @param node The IQ child name to longer watch.
- @param namespace The IQ child namespace to longer watch.
- @endsignaldef
-
- @signaldef jabber-unregister-namespace-watcher
- @signalproto
-void (unregister_namespace_watcher)(const char *node, const char *namespace);
- @endsignalproto
- @signaldesc
- Emit this signal to unregister your desire to have specific IQ stanzas to be
- emitted via the jabber-watched-iq signal when received.
- @param node The IQ child name to no longer watch.
- @param namespace The IQ child namespace to no longer watch.
- @endsignaldef
-
- @signaldef jabber-sending-xmlnode
- @signalproto
-void (sending_xmlnode)(PurpleConnection *gc, xmlnode **stanza);
- @endsignalproto
- @signaldesc
- Emit this signal (@c purple_signal_emit) to send a stanza. It is preferred
- to use this instead of prpl_info->send_raw.
- @param gc The connection on which to send the stanza.
- @param stanza The stanza to send. If stanza is not NULL after being sent,
- the emitter should free it.
- @endsignaldef
-
- @signaldef jabber-receiving-xmlnode
- @signalproto
-void (receiving_xmlnode)(PurpleConnection *gc, xmlnode **stanza);
- @endsignalproto
- @signaldesc
- Emitted when an XMPP stanza is received. Allows a plugin to process any
- stanza.
- @param gc The connection on which the stanza was received.
- @param stanza The received stanza. Set stanza to NULL (and free it) to
- stop processing the stanza.
- @endsignaldef
-
-*/
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doc/log-signals.dox b/doc/log-signals.dox
deleted file mode 100644
index 62692d3f43..0000000000
--- a/doc/log-signals.dox
+++ /dev/null
@@ -1,27 +0,0 @@
-/** @page log-signals Log Signals
-
- @signals
- @signal log-timestamp
- @endsignals
-
- @see log.h
-
- <hr>
-
- @signaldef log-timestamp
- @signalproto
-char *(*log_timestamp)(PurpleLog *log, time_t when, gboolean show_date);
- @endsignalproto
- @signaldesc
- Emitted to allow plugins to customize the timestamp on a message
- being logged.
- @param log The log the message belongs to.
- @param when The time to be converted to a string.
- @param show_date Whether the date should be displayed.
- @return A textual representation of the time, or @c NULL to use a
- default format.
- @note Plugins must be careful of logs with a type of PURPLE_LOG_SYSTEM.
- @endsignaldef
-
-*/
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doc/notify-signals.dox b/doc/notify-signals.dox
deleted file mode 100644
index 820298c604..0000000000
--- a/doc/notify-signals.dox
+++ /dev/null
@@ -1,60 +0,0 @@
-/** @page notify-signals Notification Signals
-
- @signals
- @signal displaying-userinfo
- @signal displaying-email-notification
- @signal displaying-emails-notification
- @endsignals
-
- @see notify.h
-
- @signaldef displaying-userinfo
- @signalproto
-void (*displaying_userinfo)(PurpleAccount *account, const char *who, PurpleNotifyUserInfo *user_info);
- @endsignalproto
- @signaldesc
- Emitted before userinfo is handed to the UI to display.
- @a user_info can be manipulated via the PurpleNotifyUserInfo API in notify.c.
- @note
- If adding a PurpleNotifyUserInfoEntry, be sure not to free it -- PurpleNotifyUserInfo assumes responsibility for its objects.
- @param account The account on which the info was obtained.
- @param who The name of the buddy whose info is to be displayed.
- @param user_info The information to be displayed, as PurpleNotifyUserInfoEntry objects
- @endsignaldef
-
- @signaldef displaying-email-notification
- @signalproto
-void (*displaying_email_notification)(const char *subject,
- const char *from,
- const char *to,
- const char *url);
- @endsignalproto
- @signaldesc
- Emitted before notification of a single email is handed to the UI to display.
- @param subject Subject of email being notified of.
- @param from Who the email is from.
- @param to Who the email is to.
- @param url A url to view the email.
- @since 2.1.0
- @endsignaldef
-
- @signaldef displaying-emails-notification
- @signalproto
-void (*displaying_emails_notification)(const char **subjects,
- const char **froms,
- const char **tos,
- const char **urls,
- guint count);
- @endsignalproto
- @signaldesc
- Emitted before notification of multiple emails is handed to the UI to display.
- @param subjects Subjects of emails being notified of.
- @param froms Who the emails are from.
- @param tos Who the emails are to.
- @param urls The urls to view the emails.
- @param count Number of emails being notified of.
- @since 2.1.0
- @endsignaldef
-
-*/
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doc/pidgin.1.in b/doc/pidgin.1.in
index 5edbc789fd..81991f34c1 100644
--- a/doc/pidgin.1.in
+++ b/doc/pidgin.1.in
@@ -509,7 +509,7 @@ Smiley...\fR
.SH PLUGINS
Pidgin allows for dynamic loading of plugins to add extra functionality
to Pidgin. See \fIplugins/HOWTO\fR or
-\fIhttp://developer.pidgin.im/wiki/CHowTo\fR for information on writing
+\fIhttps://developer.pidgin.im/wiki/CHowTo\fR for information on writing
plugins.
The plugins dialog can be accessed by selecting \fIPlugins\fR from the
@@ -559,7 +559,7 @@ about this interaction exists.
\fI~/.purple/plugins/\fR: users' local plugins
.SH BUGS
-The bug tracker can be reached by visiting \fIhttp://developer.pidgin.im/query\fR
+The bug tracker can be reached by visiting \fIhttps://developer.pidgin.im/query\fR
Before sending a bug report, please verify that you have the latest
version of Pidgin. Many bugs (major and minor) are fixed
@@ -569,16 +569,16 @@ have been solved.
.SH PATCHES
If you fix a bug in Pidgin (or otherwise enhance it), please submit a
patch (using \fBmtn diff > my.diff\fR against the latest version from the
-Monotone repository) at \fIhttp://developer.pidgin.im/simpleticket\fR
+Mercurial repository) at \fIhttps://developer.pidgin.im/newticket\fR
You are also encouraged to drop by at \fB#pidgin\fR on \fIirc.freenode.net\fR
to discuss development.
.SH SEE ALSO
-\fIhttp://pidgin.im/\fR
+\fIhttps://pidgin.im/\fR
.br
-\fIhttp://developer.pidgin.im/\fR
+\fIhttps://developer.pidgin.im/\fR
.br
\fBpurple-remote\fR(1)
.br
@@ -610,44 +610,28 @@ Pidgin's active developers are:
.br
Ethan 'Paco-Paco' Blanton (developer)
.br
- Thomas Butter (developer)
-.br
- Ka-Hing Cheung (developer)
-.br
Sadrul Habib Chowdhury (developer)
.br
Mark 'KingAnt' Doliner (developer) <\fIthekingant@users.sourceforge.net\fR>
.br
- Sean Egan (developer) <\fIseanegan@gmail.com\fR>
-.br
- Casey Harkins (developer)
-.br
- Ivan Komarov
-.br
Gary 'grim' Kramlich (developer)
.br
Richard 'rlaager' Laager (developer) <\fIrlaager@pidgin.im\fR>
.br
+ Marcus 'malu' Lundblad (developer)
+.br
Sulabh 'sulabh_m' Mahajan (developer)
.br
Richard 'wabz' Nelson (developer)
.br
- Christopher 'siege' O'Brien (developer)
-.br
- Bartosz Oler (developer)
-.br
Etan 'deryni' Reisner (developer)
.br
- Tim 'marv' Ringenbach (developer) <\fImarv_sf@users.sf.net\fR>
-.br
Michael 'Maiku' Ruprecht (developer, voice and video)
.br
Elliott 'QuLogic' Sales de Andrade (developer)
.br
Luke 'LSchiere' Schierer (support)
.br
- Megan 'Cae' Schneider (support/QA)
-.br
Evan Schoenberg (developer)
.br
Kevin 'SimGuy' Stange (developer and webmaster)
@@ -656,21 +640,19 @@ Pidgin's active developers are:
.br
Stu 'nosnilmot' Tomlinson (developer)
.br
- Nathan 'faceprint' Walp (developer)
+ Jorge 'Masca' Villaseñor
+.br
+ Tomasz Wasilczyk
.br
Our crazy patch writers include:
.br
- Marcus 'malu' Lundblad
-.br
- Dennis 'EvilDennisR' Ristuccia
-.br
- Peter 'fmoo' Ruibal
+ Jakub 'haakon' Adam
.br
- Gabriel 'Nix' Schulhof
+ Krzysztof Klinikowski
.br
- Jorge 'Masca' Villaseñor
+ Eion Robb
.br
@@ -684,20 +666,40 @@ Our retired developers are:
.br
Herman Bloggs (win32 port) <\fIherman@bluedigits.com\fR>
.br
+ Thomas Butter (developer)
+.br
+ Ka-Hing Cheung (developer)
+.br
Jim Duchek <\fIjim@linuxpimps.com\fR> (maintainer)
.br
+ Sean Egan (developer) <\fIseanegan@gmail.com\fR>
+.br
Rob Flynn <\fIgaim@robflynn.com\fR> (maintainer)
.br
Adam Fritzler (libfaim maintainer)
.br
Christian 'ChipX86' Hammond (developer & webmaster) <\fIchipx86@chipx86.com\fR>
.br
+ Casey Harkins (developer)
+.br
+ Ivan Komarov
+.br
Syd Logan (hacker and designated driver [lazy bum])
.br
+ Christopher 'siege' O'Brien (developer)
+.br
+ Bartosz Oler (developer)
+.br
+ Tim 'marv' Ringenbach (developer) <\fImarv_sf@users.sf.net\fR>
+.br
+ Megan 'Cae' Schneider (support/QA)
+.br
Jim Seymour (XMPP developer)
.br
Mark Spencer (original author) <\fImarkster@marko.net\fR>
.br
+ Nathan 'faceprint' Walp (developer)
+.br
Eric Warmenhoven (former lead developer) <\fIeric@warmenhoven.org\fR>
.br
@@ -714,6 +716,12 @@ Our retired crazy patch writers include:
.br
Benjamin Miller
.br
+ Dennis 'EvilDennisR' Ristuccia
+.br
+ Peter 'fmoo' Ruibal
+.br
+ Gabriel 'Nix' Schulhof
+.br
This manpage was originally written by Dennis Ristuccia
diff --git a/doc/plugin-i18n.dox b/doc/plugin-i18n.dox
deleted file mode 100644
index 8c9ed9b2f8..0000000000
--- a/doc/plugin-i18n.dox
+++ /dev/null
@@ -1,107 +0,0 @@
-/** @page plugin-i18n Third Party Plugin Translation Support
-
- @section Introduction
- For the purpose of this document we're going to assume that your plugin:
-
- - Is set up to use autotools. It may be possible to add translation support
- without autotools, but we have no idea how. We may not want to know, either ;)
- - Has an autogen.sh. You may have also called this bootstrap.sh or similar.
- - Resides in a source tree that has @c configure.ac and @c Makefile.am in the
- top-level directory as well as a @c src directory in which the plugin's source
- is located. A @c Makefile.am should also exist in the @c src directory.
-
- For a plugin to have translation support there are a few steps that need to
- followed:
-
- - In your autogen.sh, add the following after your other utility checks:
- @code
-(intltoolize --version) < /dev/null > /dev/null 2>&1 || {
- echo;
- echo "You must have intltool installed to compile <YOUR PLUGIN NAME>";
- echo;
- exit;
-}
- @endcode
- Then before your call to aclocal add:
- @code
-intltoolize --force --copy
- @endcode
- - Now edit configure.ac and add the following:
- @code
-AC_PROG_INTLTOOL
-
-GETTEXT_PACKAGE=<YOUR PLUGIN NAME>
-AC_SUBST(GETTEXT_PACKAGE)
-AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, ["$GETTEXT_PACKAGE"], [Define the gettext package to be used])
-
-ALL_LINGUAS=""
-AM_GLIB_GNU_GETTEXT
- @endcode
- The position of these macros in the file don't really matter, but if you
- have issues either play around with it or feel free to ask one of the Pidgin
- developers. Finally add 'po/Makefile.in' to you 'AC_OUTPUT' command.
- - Now create a directory named 'po'.
- - 'cd' into the 'po' directory.
- - Create/edit the file 'POTFILE.in' in your favorite editor. Each line
- should be the name of a file that could or does have strings marked for
- translating (we're getting to that step). These file names should be
- relative to the top directory of your plugin's source tree.
- - 'cd' back to the top directory of your plugin's source tree.
- - Open 'Makefile.am' and add 'po' to your 'SUBDIRS' variable.
- - While still in the top directory of your plugin's source tree, execute
- 'intltool-prepare'. This will setup anything extra that intltool needs.
- - Fire off 'autogen.sh' and when it's completed, verify that you have a
- 'po/POTFILES' (notice the lack of a .in). If you do, everything should be
- set on the autotools side.
- - Take a break, stretch your legs, smoke a cigarette, whatever, because
- we're done with the autotools part.
- - When you're ready, 'cd' into the directory with the source files for your
- plugin.
- - Open the file containing the PurplePluginInfo structure.
- - If you're not already, please make sure that you are including the
- 'config.h' file for you plugin. Note that 'config.h' could be whatever
- you told autohead to use with AM_CONFIG_HEADER. Also add the following:
- @code
-#include <glib/gi18n-lib.h>
- @endcode
- Make sure that this include is after you include of your 'config.h',
- otherwise you will break your build. Also note that if you wish to
- maintain compatibility with older versions of GLib, you will need to
- include additional preprocessor directives, which we won't cover here.
- - This is where things get a bit goofy. libpurple is going to try to
- translate our strings using the libpurple gettext package. So we have to
- convert them before libpurple attempts to.
- - To do this, we're going to change the entries for name, summary, and
- description to NULL.
- - Next, locate your 'init_plugin' function. Your name for this function
- may vary, but it's the second parameter to 'PURPLE_INIT_PLUGIN'.
- - Now add the following within your 'init_plugin' function:
- @code
-#ifdef ENABLE_NLS
- bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
- bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
-#endif /* ENABLE_NLS */
-
- info.name = _("<YOUR PLUGIN NAME>");
- info.summary = _("<YOUR PLUGIN SUMMARY>");
- info.description = _("<YOUR PLUGIN DESCRIPTION>");
- @endcode
- Note that the _() is intentional, and that it is telling intltool that
- this string should be translated. There is also N_() which says that a
- string should only be marked for translation but should not be translated
- yet.
- - Go through the rest of your code and mark all the other strings for
- translation with _().
- - When thats done, feel free to commit your work, create your po template
- (pot file) or whatever.
- - To create you po template, 'cd' to 'po' and execute:
- @code
-intltool-update --pot
- @endcode
- - To add new translations to your plugin, all you have to do is add the
- language code to the 'ALL_LINGUAS' variable in your configure.ac. Take
- note that this list of languages should be separated by a space. After
- you have added the language code to 'ALL_LINGUAS', drop the xx.po file
- into 'po', and re-'autogen.sh'. After a full build you should now be
- able to use the translation.
- */
diff --git a/doc/plugin-ids.dox b/doc/plugin-ids.dox
deleted file mode 100644
index cccd51599c..0000000000
--- a/doc/plugin-ids.dox
+++ /dev/null
@@ -1,100 +0,0 @@
-/** @page plugin-ids Plugin IDs
-
- @section Introduction
- Every plugin contains a unique identifier to prevent duplicate plugin
- loading and conflicts. Third-party plugins (that is, plugins written by
- anyone who is not a libpurple, Pidgin, or Finch developer) are expected
- to use a plugin ID that follows a specific format. This format
- categorizes plugins and makes duplicate IDs highly unlikely.
-
- @section Format
- The basic format of a plugin ID is as follows:
-
- <tt><i>type</i>-<i>username</i>-<i>pluginname</i></tt>
-
- The @em type indicator specifies the type of plugin. This must be one
- of the following:
-
- - core - A core libpurple plugin, capable of being loaded in any
- program using libpurple. Core plugins may not contain any
- UI-specific code.
- - prpl - A protocol plugin. This is a special type of core plugin,
- which provides libpurple the ability to connect to
- another IM or chat network.
- - lopl - A loader plugin, which loads scripts as plugins. Perl and
- Tcl plugins are made possible by loader plugins.
- - gtk - A GTK+ 2.x (a.k.a. Pidgin) plugin. These plugins may use
- GTK+ code, but may not use window toolkit code, such as
- X11, Win32, Cocoa, or Carbon.
- - gtk-x11 - A GTK+ 2.x plugin that uses X11 code. These plugins may
- use both GTK+ code and X11 code, allowing to hook into
- features specific to X11.
- - gtk-win32 - A GTK+ plugin that uses Win32 code. These plugins may use
- both GTK+ code and Win32 code, allowing to hook into
- features available on Windows.
- - gnt - A GNT (a.k.a. Finch) plugin. These plugins may use GNT code.
- - qpe - A plugin for the (now-abandoned) Qutopia user interface.
-
- The @em username must be a unique identifier for you. It
- @em should be your http://developer.pidgin.im Trac user ID. Failing that, you
- could use your SourceForge user ID or your Freenode IRC nickname, if you
- have either. The http://developer.pidgin.im Trac user ID is preferred.
- Do @em not leave this field blank!
-
- The @em pluginname is the name of your plugin. It is usually all
- lowercase letters and matches the static plugin ID (the first argument to
- the PURPLE_INIT_PLUGIN() macro call), although it can be anything you
- like. Do @em not include version information in the plugin ID--the
- #PurplePluginInfo structure already has a field for this.
-
- @section nospaces One Last Rule for Plugin IDs
-
- The last rule of plugin IDs is the most important of all. Plugin IDs may
- @em NOT contain spaces. If you need a space, use another hyphen (-).
-
- @section exceptions Exceptions to the Rule
-
- As with any rule there are exceptions. If you browse through the source
- tree you will see that the plugins we distribute with the Pidgin source
- do not contain a username field. This is because while one developer may
- have written each specific plugin, the plugins are maintained
- collectively by the entire development team. This lack of a username
- field is also an indicator that the plugin is one of our plugins and not
- a third-party plugin.
-
- Another exception to the rule is the <a
- href="http://plugins.guifications.org/trac/wiki/PluginPack">Purple Plugin
- Pack</a>. All plugins whose lives started in the Purple Plugin Pack use
- <tt>"plugin_pack"</tt> for the username field to indicate origination in
- the Purple Plugin Pack.
-
- These two exceptions are mentioned here for completeness. We don't
- encourage breaking the conventions set forth by the rules outlined above.
-
- @section examples Examples of Well-Chosen Plugin IDs
-
- The following is a list of well-chosen Plugin IDs listing a few good examples.
-
- - <tt>"gtk-amc_grim-guifications"</tt> - This is the plugin ID for the
- Guifications 2.x plugin.
- - <tt>"gtk-rlaager-album"</tt> - This is the plugin ID for the Album
- plugin, which is now part of the
- Purple Plugin Pack. Its ID follows the
- rules because its life started prior
- to its inclusion in the Plugin Pack.
- - <tt>"core-rlaager-irchelper"</tt> - This is the plugin ID for the IRC
- Helper plugin, which is now part
- of the Purple Plugin Pack. Its ID
- follows the rules because its
- life started prior to its
- inclusion in the Plugin Pack.
-
- @section plugin-db Plugin Database
- Although it doesn't exist yet, in time there will be a plugin database
- on the Pidgin website, where users can download and install new plugins.
- Plugins will be accessed by your plugin ID, which is one reason why it
- must be unique.
-
- */
-
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doc/plugin-signals.dox b/doc/plugin-signals.dox
deleted file mode 100644
index 447876f415..0000000000
--- a/doc/plugin-signals.dox
+++ /dev/null
@@ -1,31 +0,0 @@
-/** @page plugin-signals Plugin Signals
-
- @signals
- @signal plugin-load
- @signal plugin-unload
- @endsignals
-
- @see plugin.h
-
- <hr>
-
- @signaldef plugin-load
- @signalproto
-void (*plugin_load)(PurplePlugin *plugin);
- @endsignalproto
- @signaldesc
- Emitted when a plugin is loaded.
- @param plugin The plugin that was loaded.
- @endsignaldef
-
- @signaldef plugin-unload
- @signalproto
-void (*plugin_unload)(PurplePlugin *plugin);
- @endsignalproto
- @signaldesc
- Emitted when a plugin is unloaded.
- @param plugin The plugin that was unloaded.
- @endsignaldef
-
- */
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doc/reference/Makefile.am b/doc/reference/Makefile.am
new file mode 100644
index 0000000000..97b1d99077
--- /dev/null
+++ b/doc/reference/Makefile.am
@@ -0,0 +1,9 @@
+if ENABLE_GTK
+GTK_DIR=pidgin
+endif
+
+if ENABLE_GNT
+GNT_DIR=finch
+endif
+
+SUBDIRS = libpurple $(GNT_DIR) $(GTK_DIR)
diff --git a/doc/reference/finch/Makefile.am b/doc/reference/finch/Makefile.am
new file mode 100644
index 0000000000..1c3672455a
--- /dev/null
+++ b/doc/reference/finch/Makefile.am
@@ -0,0 +1,133 @@
+## Process this file with automake to produce Makefile.in
+
+# We require automake 1.6 at least.
+AUTOMAKE_OPTIONS = 1.6
+
+# This is a blank Makefile.am for using gtk-doc.
+# Copy this to your project's API docs directory and modify the variables to
+# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples
+# of using the various options.
+
+# The name of the module, e.g. 'glib'.
+DOC_MODULE=finch
+
+# Uncomment for versioned docs and specify the version of the module, e.g. '2'.
+#DOC_MODULE_VERSION=2
+
+# The top-level XML file (SGML in the past). You can change this if you want to.
+DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml
+
+# The directory containing the source code. Relative to $(srcdir).
+# gtk-doc will search all .c & .h files beneath here for inline comments
+# documenting the functions and macros.
+# e.g. DOC_SOURCE_DIR=../../../gtk
+DOC_SOURCE_DIR=$(top_srcdir)/$(DOC_MODULE)
+
+# Extra options to pass to gtkdoc-scangobj. Not normally needed.
+SCANGOBJ_OPTIONS=
+
+# Extra options to supply to gtkdoc-scan.
+# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED"
+SCAN_OPTIONS=\
+ --deprecated-guards="PURPLE_DISABLE_DEPRECATED|GNT_DISABLE_DEPRECATED|FINCH_DISABLE_DEPRECATED" \
+ --rebuild-types \
+ --rebuild-sections
+
+# Extra options to supply to gtkdoc-mkdb.
+# e.g. MKDB_OPTIONS=--xml-mode --output-format=xml
+MKDB_OPTIONS=--xml-mode --output-format=xml
+
+# Extra options to supply to gtkdoc-mktmpl
+# e.g. MKTMPL_OPTIONS=--only-section-tmpl
+MKTMPL_OPTIONS=
+
+# Extra options to supply to gtkdoc-mkhtml
+MKHTML_OPTIONS=
+
+# Extra options to supply to gtkdoc-fixref. Not normally needed.
+# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html
+FIXXREF_OPTIONS=
+
+# Used for dependencies. The docs will be rebuilt if any of these change.
+# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h
+# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c
+HFILE_GLOB=$(top_srcdir)/$(DOC_MODULE)/*.h
+CFILE_GLOB=$(top_srcdir)/$(DOC_MODULE)/*.c
+
+# Extra header to include when scanning, which are not under DOC_SOURCE_DIR
+# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h
+EXTRA_HFILES=
+
+# Header files or dirs to ignore when scanning. Use base file/dir names
+# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h private_code
+IGNORE_HFILES=\
+ plugins \
+ test \
+ wms \
+ getopt.h \
+ gntinternal.h \
+ gntmarshal.h \
+ gnt-skel.h
+
+# Images to copy into HTML directory.
+# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
+HTML_IMAGES=
+
+# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
+# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
+content_files=version.xml
+
+# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
+# These files must be listed here *and* in content_files
+# e.g. expand_content_files=running.sgml
+expand_content_files=
+
+# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library.
+# Only needed if you are using gtkdoc-scangobj to dynamically query widget
+# signals and properties.
+# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
+# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
+GTKDOC_CFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libpurple \
+ -I$(top_srcdir)/finch \
+ -I$(top_srcdir)/finch/libgnt \
+ $(DEBUG_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(DBUS_CFLAGS) \
+ $(LIBXML_CFLAGS) \
+ $(GSTREAMER_CFLAGS) \
+ $(GNT_CFLAGS)
+
+GTKDOC_LIBS = \
+ $(top_builddir)/finch/libfinch.la \
+ $(DBUS_LIBS) \
+ $(INTLLIBS) \
+ $(GLIB_LIBS) \
+ $(LIBXML_LIBS) \
+ $(GNT_LIBS) \
+ $(GSTREAMER_LIBS)
+
+# This includes the standard gtk-doc make rules, copied by gtkdocize.
+include $(top_srcdir)/gtk-doc.make
+
+# Other files to distribute
+# e.g. EXTRA_DIST += version.xml.in
+EXTRA_DIST += version.xml.in
+
+# Files not to distribute
+# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types
+# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt
+DISTCLEANFILES = $(DOC_MODULE).types $(DOC_MODULE)-sections.txt
+
+dist-hook: html-build.stamp
+
+# Comment this out if you want 'make check' to test you doc status
+# and run some sanity checks
+if ENABLE_GTK_DOC
+TESTS_ENVIRONMENT = \
+ DOC_MODULE=$(DOC_MODULE) DOC_MAIN_SGML_FILE=$(DOC_MAIN_SGML_FILE) \
+ SRCDIR=$(abs_srcdir)
+#TODO: fix and enable
+#TESTS = $(GTKDOC_CHECK)
+endif
diff --git a/doc/reference/finch/finch-docs.xml b/doc/reference/finch/finch-docs.xml
new file mode 100644
index 0000000000..d852fbdc6c
--- /dev/null
+++ b/doc/reference/finch/finch-docs.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+
+<!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
+<!ENTITY version SYSTEM "version.xml">
+]>
+<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude">
+ <bookinfo>
+ <title>Finch Reference Manual</title>
+ <abstract>
+ <title>Finch &version;</title>
+ <para>
+ GNT (GLib Ncurses Toolkit) is an ncurses toolkit for creating text-mode
+ graphical user interfaces in a fast and easy way.
+ </para>
+ <para>
+ Finch is a text-mode frontend to libpurple, built using libgnt.
+ </para>
+ </abstract>
+ </bookinfo>
+
+ <reference label="I">
+ <title>Finch API Reference</title>
+
+ <xi:include href="xml/gntaccount.xml" />
+ <xi:include href="xml/gntblist.xml" />
+ <xi:include href="xml/gntpounce.xml" />
+ <xi:include href="xml/gntcertmgr.xml" />
+ <xi:include href="xml/gntconn.xml" />
+ <xi:include href="xml/gntconv.xml" />
+ <xi:include href="xml/gntdebug.xml" />
+ <xi:include href="xml/gntxfer.xml" />
+ <xi:include href="xml/gntidle.xml" />
+ <xi:include href="xml/gntlog.xml" />
+ <xi:include href="xml/gntmedia.xml" />
+ <xi:include href="xml/gntmenuutil.xml" />
+ <xi:include href="xml/gntnotify.xml" />
+ <xi:include href="xml/gntplugin.xml" />
+ <xi:include href="xml/gntprefs.xml" />
+ <xi:include href="xml/gntrequest.xml" />
+ <xi:include href="xml/gntroomlist.xml" />
+ <xi:include href="xml/gntsound.xml" />
+ <xi:include href="xml/gntstatus.xml" />
+ <xi:include href="xml/gntui.xml" />
+ <xi:include href="xml/finch.xml" />
+ </reference>
+
+ <reference label="II">
+ <title>GNT API Reference</title>
+
+ <xi:include href="xml/gnt.xml" />
+ <xi:include href="xml/gntbindable.xml" />
+ <xi:include href="xml/gntbox.xml" />
+ <xi:include href="xml/gntbutton.xml" />
+ <xi:include href="xml/gntcheckbox.xml" />
+ <xi:include href="xml/gntmenuitemcheck.xml" />
+ <xi:include href="xml/gntclipboard.xml" />
+ <xi:include href="xml/gntcolors.xml" />
+ <xi:include href="xml/gntcombobox.xml" />
+ <xi:include href="xml/gntentry.xml" />
+ <xi:include href="xml/gntfilesel.xml" />
+ <xi:include href="xml/gntkeys.xml" />
+ <xi:include href="xml/gntlabel.xml" />
+ <xi:include href="xml/gntline.xml" />
+ <xi:include href="xml/gntmenu.xml" />
+ <xi:include href="xml/gntmenuitem.xml" />
+ <xi:include href="xml/gntprogressbar.xml" />
+ <xi:include href="xml/gntslider.xml" />
+ <xi:include href="xml/gntstyle.xml" />
+ <xi:include href="xml/gnttextview.xml" />
+ <xi:include href="xml/gnttree.xml" />
+ <xi:include href="xml/gntwidget.xml" />
+ <xi:include href="xml/gntwindow.xml" />
+ <xi:include href="xml/gntwm.xml" />
+ <xi:include href="xml/gntws.xml" />
+ <xi:include href="xml/gntutils.xml" />
+ </reference>
+
+ <index id="api-index-full">
+ <title>Index</title>
+ <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+ </index>
+ <index id="api-index-deprecated" role="deprecated">
+ <title>Index of deprecated symbols</title>
+ <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
+ </index>
+
+ <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
+</book>
diff --git a/doc/reference/finch/version.xml.in b/doc/reference/finch/version.xml.in
new file mode 100644
index 0000000000..405528f9af
--- /dev/null
+++ b/doc/reference/finch/version.xml.in
@@ -0,0 +1 @@
+@PURPLE_VERSION@
diff --git a/doc/reference/libpurple/Makefile.am b/doc/reference/libpurple/Makefile.am
new file mode 100644
index 0000000000..f9a14b65b6
--- /dev/null
+++ b/doc/reference/libpurple/Makefile.am
@@ -0,0 +1,179 @@
+## Process this file with automake to produce Makefile.in
+
+# We require automake 1.6 at least.
+AUTOMAKE_OPTIONS = 1.6
+
+# This is a blank Makefile.am for using gtk-doc.
+# Copy this to your project's API docs directory and modify the variables to
+# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples
+# of using the various options.
+
+# The name of the module, e.g. 'glib'.
+DOC_MODULE=libpurple
+
+# Uncomment for versioned docs and specify the version of the module, e.g. '2'.
+#DOC_MODULE_VERSION=2
+
+# The top-level XML file (SGML in the past). You can change this if you want to.
+DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml
+
+# The directory containing the source code. Relative to $(srcdir).
+# gtk-doc will search all .c & .h files beneath here for inline comments
+# documenting the functions and macros.
+# e.g. DOC_SOURCE_DIR=../../../gtk
+DOC_SOURCE_DIR=$(top_srcdir)/$(DOC_MODULE)
+
+# Extra options to pass to gtkdoc-scangobj. Not normally needed.
+SCANGOBJ_OPTIONS=
+
+# Extra options to supply to gtkdoc-scan.
+# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED"
+SCAN_OPTIONS=\
+ --deprecated-guards="PURPLE_DISABLE_DEPRECATED" \
+ --rebuild-types \
+ --rebuild-sections
+
+# Extra options to supply to gtkdoc-mkdb.
+# e.g. MKDB_OPTIONS=--xml-mode --output-format=xml
+MKDB_OPTIONS=--xml-mode --output-format=xml
+
+# Extra options to supply to gtkdoc-mktmpl
+# e.g. MKTMPL_OPTIONS=--only-section-tmpl
+MKTMPL_OPTIONS=
+
+# Extra options to supply to gtkdoc-mkhtml
+MKHTML_OPTIONS=
+
+# Extra options to supply to gtkdoc-fixref. Not normally needed.
+# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html
+FIXXREF_OPTIONS=
+
+# Used for dependencies. The docs will be rebuilt if any of these change.
+# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h
+# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c
+HFILE_GLOB=$(top_srcdir)/$(DOC_MODULE)/*.h
+CFILE_GLOB=$(top_srcdir)/$(DOC_MODULE)/*.c
+
+# Extra header to include when scanning, which are not under DOC_SOURCE_DIR
+# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h
+# TODO: those files are not properly scanned when building out-of-tree
+EXTRA_HFILES = \
+ $(top_builddir)/$(DOC_MODULE)/enums.h \
+ $(top_builddir)/$(DOC_MODULE)/purple.h \
+ $(top_builddir)/$(DOC_MODULE)/version.h
+
+# Header files or dirs to ignore when scanning. Use base file/dir names
+# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h private_code
+IGNORE_HFILES=\
+ example \
+ gconf \
+ plugins \
+ protocols \
+ tests \
+ win32 \
+ backend-fs2.h \
+ dbus-define-api.h \
+ dbus-types.h \
+ glibcompat.h \
+ internal.h \
+ marshallers.h \
+ purple-client.h \
+ purple-client-bindings.h \
+ valgrind.h
+
+# Images to copy into HTML directory.
+# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
+HTML_IMAGES=
+
+# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
+# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
+content_files = version.xml \
+ plugin_i18n.xml \
+ plugin_ids.xml \
+ signals_account.xml \
+ signals_blist.xml \
+ signals_certificate.xml \
+ signals_cmd.xml \
+ signals_connection.xml \
+ signals_conversation.xml \
+ signals_core.xml \
+ signals_dbus_server.xml \
+ signals_jabber.xml \
+ signals_log.xml \
+ signals_notify.xml \
+ signals_plugin.xml \
+ signals_savedstatus.xml \
+ signals_sound.xml \
+ signals_xfer.xml \
+ tut_c_plugins.xml \
+ tut_signals.xml \
+ ui_ops.xml
+
+# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
+# These files must be listed here *and* in content_files
+# e.g. expand_content_files=running.sgml
+expand_content_files=
+
+# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library.
+# Only needed if you are using gtkdoc-scangobj to dynamically query widget
+# signals and properties.
+# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
+# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
+GTKDOC_CFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libpurple \
+ $(GLIB_CFLAGS) \
+ $(DEBUG_CFLAGS) \
+ $(DBUS_CFLAGS) \
+ $(LIBXML_CFLAGS) \
+ $(FARSTREAM_CFLAGS) \
+ $(GSTREAMER_CFLAGS) \
+ $(GSTVIDEO_CFLAGS) \
+ $(GSTINTERFACES_CFLAGS) \
+ $(IDN_CFLAGS) \
+ $(NETWORKMANAGER_CFLAGS) \
+ $(JSON_CFLAGS) \
+ $(GNUTLS_CFLAGS) \
+ $(NSS_CFLAGS)
+
+GTKDOC_LIBS = \
+ $(top_builddir)/libpurple/libpurple.la \
+ $(STATIC_LINK_LIBS) \
+ $(DBUS_LIBS) \
+ $(GLIB_LIBS) \
+ $(LIBXML_LIBS) \
+ $(NETWORKMANAGER_LIBS) \
+ $(INTLLIBS) \
+ $(FARSTREAM_LIBS) \
+ $(GSTREAMER_LIBS) \
+ $(GSTVIDEO_LIBS) \
+ $(GSTINTERFACES_LIBS) \
+ $(IDN_LIBS) \
+ $(JSON_LIBS) \
+ $(GNUTLS_LIBS) \
+ $(NSS_LIBS) \
+ -lm
+
+# This includes the standard gtk-doc make rules, copied by gtkdocize.
+include $(top_srcdir)/gtk-doc.make
+
+# Other files to distribute
+# e.g. EXTRA_DIST += version.xml.in
+EXTRA_DIST += version.xml.in
+
+# Files not to distribute
+# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types
+# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt
+DISTCLEANFILES = $(DOC_MODULE).types $(DOC_MODULE)-sections.txt
+
+dist-hook: html-build.stamp
+
+# Comment this out if you want 'make check' to test you doc status
+# and run some sanity checks
+if ENABLE_GTK_DOC
+TESTS_ENVIRONMENT = \
+ DOC_MODULE=$(DOC_MODULE) DOC_MAIN_SGML_FILE=$(DOC_MAIN_SGML_FILE) \
+ SRCDIR=$(abs_srcdir)
+#TODO: fix and enable
+#TESTS = $(GTKDOC_CHECK)
+endif
diff --git a/doc/reference/libpurple/libpurple-docs.xml b/doc/reference/libpurple/libpurple-docs.xml
new file mode 100644
index 0000000000..c9e6f0d3d4
--- /dev/null
+++ b/doc/reference/libpurple/libpurple-docs.xml
@@ -0,0 +1,194 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+
+<!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
+<!ENTITY version SYSTEM "version.xml">
+]>
+<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude">
+ <bookinfo>
+ <title>Purple Reference Manual</title>
+ <abstract>
+ <title>Libpurple &version;</title>
+ <para>
+ libpurple is intended to be the core of an IM program. Pidgin is a GTK+
+ frontend to libpurple, and Finch is an ncurses frontend built using
+ libgnt (GLib Ncurses Toolkit).
+ </para>
+ </abstract>
+ </bookinfo>
+
+ <part label="I">
+ <title>Tutorials</title>
+
+ <xi:include href="tut_c_plugins.xml" />
+ <xi:include href="tut_signals.xml" />
+ </part>
+
+ <reference label="II">
+ <title>API Reference</title>
+
+ <xi:include href="xml/account.xml" />
+ <xi:include href="xml/accountopt.xml" />
+ <xi:include href="xml/accounts.xml" />
+ <xi:include href="xml/blistnode.xml" />
+ <xi:include href="xml/blistnodetypes.xml" />
+ <xi:include href="xml/buddylist.xml" />
+ <xi:include href="xml/buddyicon.xml" />
+ <xi:include href="xml/pounce.xml" />
+ <xi:include href="xml/circularbuffer.xml" />
+ <xi:include href="xml/cipher.xml" />
+ <xi:include href="xml/connection.xml" />
+ <xi:include href="xml/conversation.xml" />
+ <xi:include href="xml/conversationtypes.xml" />
+ <xi:include href="xml/conversations.xml" />
+ <xi:include href="xml/dbus-maybe.xml" />
+ <xi:include href="xml/debug.xml" />
+ <xi:include href="xml/dnsquery.xml" />
+ <xi:include href="xml/dnssrv.xml" />
+ <xi:include href="xml/e2ee.xml" />
+ <xi:include href="xml/eventloop.xml" />
+ <xi:include href="xml/xfer.xml" />
+ <xi:include href="xml/purple-socket.xml" />
+ <xi:include href="xml/http.xml" />
+ <xi:include href="xml/idle.xml" />
+ <xi:include href="xml/keyring.xml" />
+ <xi:include href="xml/memorypool.xml" />
+ <xi:include href="xml/desktopitem.xml" />
+ <xi:include href="xml/mime.xml" />
+ <xi:include href="xml/nat-pmp.xml" />
+ <xi:include href="xml/network.xml" />
+ <xi:include href="xml/notify.xml" />
+ <xi:include href="xml/ntlm.xml" />
+ <xi:include href="xml/plugin.xml" />
+ <xi:include href="xml/prefs.xml" />
+ <xi:include href="xml/pluginpref.xml" />
+ <xi:include href="xml/prpl.xml" />
+ <xi:include href="xml/presence.xml" />
+ <xi:include href="xml/proxy.xml" />
+ <xi:include href="xml/certificate.xml" />
+ <xi:include href="xml/signals.xml" />
+ <xi:include href="xml/stringref.xml" />
+ <xi:include href="xml/request.xml" />
+ <xi:include href="xml/request-datasheet.xml" />
+ <xi:include href="xml/roomlist.xml" />
+ <xi:include href="xml/savedstatuses.xml" />
+ <xi:include href="xml/server.xml" />
+ <xi:include href="xml/sound.xml" />
+ <xi:include href="xml/sslconn.xml" />
+ <xi:include href="xml/core.xml" />
+ <xi:include href="xml/status.xml" />
+ <xi:include href="xml/stun.xml" />
+ <xi:include href="xml/theme.xml" />
+ <xi:include href="xml/theme-loader.xml" />
+ <xi:include href="xml/theme-manager.xml" />
+ <xi:include href="xml/trie.xml" />
+ <xi:include href="xml/sound-theme.xml" />
+ <xi:include href="xml/sound-theme-loader.xml" />
+ <xi:include href="xml/upnp.xml" />
+ <xi:include href="xml/xmlnode.xml" />
+ <xi:include href="xml/version.xml" />
+ <xi:include href="xml/util.xml" />
+ <xi:include href="xml/enums.xml" />
+ <xi:include href="xml/purple.xml" />
+ </reference>
+
+ <reference label="">
+ <reference label="a">
+ <title>Messaging</title>
+
+ <xi:include href="xml/message.xml" />
+ <xi:include href="xml/log.xml" />
+ <xi:include href="xml/cmds.xml" />
+ </reference>
+
+ <reference label="b">
+ <title>Ciphers and Hashes</title>
+
+ <xi:include href="xml/aescipher.xml" />
+ <xi:include href="xml/descipher.xml" />
+ <xi:include href="xml/des3cipher.xml" />
+ <xi:include href="xml/hmaccipher.xml" />
+ <xi:include href="xml/pbkdf2cipher.xml" />
+ <xi:include href="xml/rc4cipher.xml" />
+ <xi:include href="xml/md4hash.xml" />
+ <xi:include href="xml/md5hash.xml" />
+ <xi:include href="xml/sha1hash.xml" />
+ <xi:include href="xml/sha256hash.xml" />
+ </reference>
+
+ <reference label="c">
+ <title>Smiley APIs</title>
+
+ <xi:include href="xml/smiley.xml" />
+ <xi:include href="xml/smiley-custom.xml" />
+ <xi:include href="xml/smiley-list.xml" />
+ <xi:include href="xml/smiley-theme.xml" />
+ <xi:include href="xml/smiley-parser.xml" />
+ </reference>
+
+ <reference label="d">
+ <title>Graphics</title>
+
+ <xi:include href="xml/image.xml" />
+ <xi:include href="xml/image-store.xml" />
+ <xi:include href="xml/whiteboard.xml" />
+ </reference>
+
+ <reference label="e">
+ <title>Media APIs</title>
+
+ <xi:include href="xml/media.xml" />
+ <xi:include href="xml/media-gst.xml" />
+ <xi:include href="xml/mediamanager.xml" />
+ <xi:include href="xml/candidate.xml" />
+ <xi:include href="xml/codec.xml" />
+ <xi:include href="xml/backend-iface.xml" />
+ <xi:include href="xml/enum-types.xml" />
+ </reference>
+
+ <reference label="f">
+ <title>D-Bus Server APIs</title>
+
+ <xi:include href="xml/dbus-bindings.xml" />
+ <xi:include href="xml/dbus-purple.xml" />
+ <xi:include href="xml/dbus-server.xml" />
+ <xi:include href="xml/dbus-useful.xml" />
+ </reference>
+ </reference>
+
+ <reference label="III">
+ <title>Purple-signals Reference</title>
+
+ <xi:include href="signals_account.xml" />
+ <xi:include href="signals_blist.xml" />
+ <xi:include href="signals_certificate.xml" />
+ <xi:include href="signals_cmd.xml" />
+ <xi:include href="signals_connection.xml" />
+ <xi:include href="signals_conversation.xml" />
+ <xi:include href="signals_core.xml" />
+ <xi:include href="signals_dbus_server.xml" />
+ <xi:include href="signals_xfer.xml" />
+ <xi:include href="signals_log.xml" />
+ <xi:include href="signals_notify.xml" />
+ <xi:include href="signals_plugin.xml" />
+ <xi:include href="signals_savedstatus.xml" />
+ <xi:include href="signals_sound.xml" />
+ <xi:include href="signals_jabber.xml" />
+ </reference>
+
+ <xi:include href="plugin_ids.xml" />
+ <xi:include href="plugin_i18n.xml" />
+ <xi:include href="ui_ops.xml" />
+
+ <index id="api-index-full">
+ <title>Index</title>
+ <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+ </index>
+ <index id="api-index-deprecated" role="deprecated">
+ <title>Index of deprecated symbols</title>
+ <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
+ </index>
+
+ <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
+</book>
diff --git a/doc/reference/libpurple/plugin_i18n.xml b/doc/reference/libpurple/plugin_i18n.xml
new file mode 100644
index 0000000000..71152686eb
--- /dev/null
+++ b/doc/reference/libpurple/plugin_i18n.xml
@@ -0,0 +1,177 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-plugin-i18n">
+ <title>Third Party Plugin Translation</title>
+
+ <sect2 id="plugin-i18n-introduction">
+ <title>Introduction</title>
+
+ <para>
+ For the purpose of this document we're going to assume that your plugin:
+
+ <itemizedlist>
+ <listitem><para>
+Is set up to use autotools. It may be possible to add translation support
+without autotools, but we have no idea how. We may not want to know, either ;)
+ </para></listitem>
+ <listitem><para>
+Has an autogen.sh. You may have also called this bootstrap.sh or similar.
+ </para></listitem>
+ <listitem><para>
+Resides in a source tree that has <literal>configure.ac</literal> and
+<literal>Makefile.am</literal> in the top-level directory as well as a
+<literal>src</literal> directory in which the plugin's source is located. A
+<literal>Makefile.am</literal> should also exist in the <literal>src</literal>
+directory.
+ </para></listitem>
+ </itemizedlist>
+ </para>
+ </sect2>
+
+ <sect2 id="plugin-i18n-steps">
+ <title>Steps to follow</title>
+
+ <para>
+ For a plugin to have translation support there are a few steps that need to
+ followed:
+
+ <itemizedlist>
+ <listitem><para>
+In your autogen.sh, add the following after your other utility checks:
+<programlisting>
+(intltoolize --version) &lt; /dev/null &gt; /dev/null 2&gt;&amp;1 || {
+ echo;
+ echo "You must have intltool installed to compile &lt;YOUR PLUGIN NAME&gt;";
+ echo;
+ exit;
+}
+</programlisting>
+Then before your call to aclocal add:
+<programlisting>
+intltoolize --force --copy
+</programlisting>
+ </para></listitem>
+ <listitem><para>
+Now edit configure.ac and add the following:
+<programlisting>
+AC_PROG_INTLTOOL
+
+GETTEXT_PACKAGE=&lt;YOUR PLUGIN NAME&gt;
+AC_SUBST(GETTEXT_PACKAGE)
+AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, ["$GETTEXT_PACKAGE"], [Define the gettext package to be used])
+
+ALL_LINGUAS=""
+AM_GLIB_GNU_GETTEXT
+</programlisting>
+The position of these macros in the file don't really matter, but if you
+have issues either play around with it or feel free to ask one of the Pidgin
+developers. Finally add 'po/Makefile.in' to you 'AC_OUTPUT' command.
+ </para></listitem>
+ <listitem><para>
+Now create a directory named 'po'.
+ </para></listitem>
+ <listitem><para>
+'cd' into the 'po' directory.
+ </para></listitem>
+ <listitem><para>
+Create/edit the file 'POTFILE.in' in your favorite editor. Each line
+should be the name of a file that could or does have strings marked for
+translating (we're getting to that step). These file names should be
+relative to the top directory of your plugin's source tree.
+ </para></listitem>
+ <listitem><para>
+'cd' back to the top directory of your plugin's source tree.
+ </para></listitem>
+ <listitem><para>
+Open 'Makefile.am' and add 'po' to your 'SUBDIRS' variable.
+ </para></listitem>
+ <listitem><para>
+While still in the top directory of your plugin's source tree, execute
+'intltool-prepare'. This will setup anything extra that intltool needs.
+ </para></listitem>
+ <listitem><para>
+Fire off 'autogen.sh' and when it's completed, verify that you have a
+'po/POTFILES' (notice the lack of a .in). If you do, everything should be
+set on the autotools side.
+ </para></listitem>
+ <listitem><para>
+Take a break, stretch your legs, smoke a cigarette, whatever, because
+we're done with the autotools part.
+ </para></listitem>
+ <listitem><para>
+When you're ready, 'cd' into the directory with the source files for your
+plugin.
+ </para></listitem>
+ <listitem><para>
+Open the file containing the PurplePluginInfo structure.
+ </para></listitem>
+ <listitem><para>
+If you're not already, please make sure that you are including the
+'config.h' file for you plugin. Note that 'config.h' could be whatever
+you told autohead to use with AM_CONFIG_HEADER. Also add the following:
+<programlisting>
+#include &lt;glib/gi18n-lib.h&gt;
+</programlisting>
+Make sure that this include is after you include of your 'config.h',
+otherwise you will break your build. Also note that if you wish to
+maintain compatibility with older versions of GLib, you will need to
+include additional preprocessor directives, which we won't cover here.
+ </para></listitem>
+ <listitem><para>
+This is where things get a bit goofy. libpurple is going to try to
+translate our strings using the libpurple gettext package. So we have to
+convert them before libpurple attempts to.
+ </para></listitem>
+ <listitem><para>
+To do this, we're going to change the entries for name, summary, and
+description to NULL.
+ </para></listitem>
+ <listitem><para>
+Next, locate your 'init_plugin' function. Your name for this function
+may vary, but it's the second parameter to 'PURPLE_INIT_PLUGIN'.
+ </para></listitem>
+ <listitem><para>
+Now add the following within your 'init_plugin' function:
+<programlisting>
+#ifdef ENABLE_NLS
+ bindtextdomain(GETTEXT_PACKAGE, PURPLE_LOCALEDIR);
+ bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
+#endif /* ENABLE_NLS */
+
+ info.name = _("&lt;YOUR PLUGIN NAME&gt;");
+ info.summary = _("&lt;YOUR PLUGIN SUMMARY&gt;");
+ info.description = _("&lt;YOUR PLUGIN DESCRIPTION&gt;");
+</programlisting>
+Note that the _() is intentional, and that it is telling intltool that
+this string should be translated. There is also N_() which says that a
+string should only be marked for translation but should not be translated
+yet.
+ </para></listitem>
+ <listitem><para>
+Go through the rest of your code and mark all the other strings for
+translation with _().
+ </para></listitem>
+ <listitem><para>
+When thats done, feel free to commit your work, create your po template
+(pot file) or whatever.
+ </para></listitem>
+ <listitem><para>
+To create you po template, 'cd' to 'po' and execute:
+<programlisting>
+intltool-update --pot
+</programlisting>
+ </para></listitem>
+ <listitem><para>
+To add new translations to your plugin, all you have to do is add the
+language code to the 'ALL_LINGUAS' variable in your configure.ac. Take
+note that this list of languages should be separated by a space. After
+you have added the language code to 'ALL_LINGUAS', drop the xx.po file
+into 'po', and re-'autogen.sh'. After a full build you should now be
+able to use the translation.
+ </para></listitem>
+ </itemizedlist>
+ </para>
+ </sect2>
+</chapter>
diff --git a/doc/reference/libpurple/plugin_ids.xml b/doc/reference/libpurple/plugin_ids.xml
new file mode 100644
index 0000000000..a411d96695
--- /dev/null
+++ b/doc/reference/libpurple/plugin_ids.xml
@@ -0,0 +1,198 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-plugin-ids">
+ <title>Plugin IDs</title>
+
+ <sect2 id="plugin-ids-introduction">
+ <title>Introduction</title>
+ <para>
+Every plugin contains a unique identifier. Third-party plugins (that is,
+plugins written by anyone who is not a libpurple, Pidgin, or Finch developer)
+are expected to use a plugin ID that follows a specific format. This format
+categorizes plugins and makes duplicate IDs highly unlikely.
+ </para>
+ </sect2>
+
+ <sect2 id="plugin-ids-format">
+ <title>Format</title>
+ <para>
+The basic format of a plugin ID is as follows:
+
+<programlisting>
+type-username-pluginname
+</programlisting>
+ </para>
+
+ <para>
+The <emphasis>type</emphasis> indicator specifies the type of plugin. This must be one
+of the following:
+
+ <table>
+ <title>Types of plugins</title>
+ <tgroup cols="2">
+ <colspec colwidth="*" colnum="1" align="left"/>
+ <colspec colwidth="*" colnum="2" align="left"/>
+ <thead>
+ <row>
+<entry><emphasis>type</emphasis></entry>
+<entry>description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+<entry><literal>core</literal></entry>
+<entry>
+A core libpurple plugin, capable of being loaded in any program using libpurple.
+Core plugins may not contain any UI-specific code.
+</entry>
+ </row>
+ <row>
+<entry><literal>prpl</literal></entry>
+<entry>
+A protocol plugin. This is a core plugin which provides libpurple the ability to
+connect to another IM or chat network.
+</entry>
+ </row>
+ <row>
+<entry><literal>lopl</literal></entry>
+<entry>
+A loader plugin, which loads scripts as plugins. Perl and
+Tcl plugins are made possible by loader plugins.
+</entry>
+ </row>
+ <row>
+<entry><literal>gtk</literal></entry>
+<entry>
+A GTK+ (a.k.a. Pidgin) plugin. These plugins may use GTK+ code, but may not use
+window toolkit code, such as X11, Win32, Cocoa, or Carbon.
+</entry>
+ </row>
+ <row>
+<entry><literal>gtk-x11</literal></entry>
+<entry>
+A GTK+ plugin that uses X11 code. These plugins may use both GTK+ code and X11
+code, allowing to hook into features specific to X11.
+</entry>
+ </row>
+ <row>
+<entry><literal>gtk-win32</literal></entry>
+<entry>
+A GTK+ plugin that uses Win32 code. These plugins may use both GTK+ code and
+Win32 code, allowing to hook into features available on Windows.
+</entry>
+ </row>
+ <row>
+<entry><literal>gnt</literal></entry>
+<entry>
+A GNT (a.k.a. Finch) plugin. These plugins may use GNT code.
+</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </para>
+
+ <para>
+The <emphasis>username</emphasis> must be a unique identifier for you. It
+<emphasis>should</emphasis> be your https://developer.pidgin.im Trac user ID. Failing that, you
+could use your SourceForge user ID or your Freenode IRC nickname, if you
+have either. The https://developer.pidgin.im Trac user ID is preferred.
+Do <emphasis>not</emphasis> leave this field blank!
+ </para>
+
+ <para>
+The <emphasis>pluginname</emphasis> is the name of your plugin. It is usually all
+lowercase letters and matches the static plugin ID (the first argument to
+the PURPLE_INIT_PLUGIN() macro call), although it can be anything you
+like. Do <emphasis>not</emphasis> include version information in the plugin ID--the
+<literal>PurplePluginInfo</literal> structure already has a field for this.
+ </para>
+ </sect2>
+
+ <sect2 id="plugin-ids-nospaces">
+ <title>One Last Rule for Plugin IDs</title>
+ <para>
+Plugin IDs may <emphasis>NOT</emphasis> contain spaces. If you need a space, use another
+hyphen (-).
+ </para>
+ </sect2>
+
+ <sect2 id="plugin-ids-exceptions">
+ <title>Exceptions to the Rule</title>
+ <para>
+As with any rule there are exceptions. If you browse through the source
+tree you will see that the plugins we distribute with the Pidgin source
+do not contain a username field. This is because while one developer may
+have written each specific plugin, the plugins are maintained
+collectively by the entire development team. This lack of a username
+field is also an indicator that the plugin is one of our plugins and not
+a third-party plugin.
+
+Another exception to the rule is the <ulink
+url="http://plugins.guifications.org/trac/wiki/PluginPack">Purple Plugin
+Pack</ulink>. All plugins whose lives started in the Purple Plugin Pack use
+<literal>"plugin_pack"</literal> for the username field to indicate origination in
+the Purple Plugin Pack.
+
+These two exceptions are mentioned here for completeness. We don't
+encourage breaking the conventions set forth by the rules outlined above.
+ </para>
+ </sect2>
+
+ <sect2 id="plugin-ids-examples">
+ <title>Examples of Well-Chosen Plugin IDs</title>
+ <para>
+The following is a list of well-chosen Plugin IDs listing a few good examples.
+
+ <table>
+ <title>Examples</title>
+ <tgroup cols="2">
+ <colspec colwidth="*" colnum="1" align="left"/>
+ <colspec colwidth="*" colnum="2" align="left"/>
+ <thead>
+ <row>
+<entry>id</entry>
+<entry>description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+<entry><literal>"gtk-amc_grim-guifications"</literal></entry>
+<entry>
+This is the plugin ID for the Guifications 2.x plugin.
+</entry>
+ </row>
+ <row>
+<entry><literal>"gtk-rlaager-album"</literal></entry>
+<entry>
+This is the plugin ID for the Album plugin, which is now part of the
+Purple Plugin Pack. Its ID follows the rules because its life started prior
+to its inclusion in the Plugin Pack.
+</entry>
+ </row>
+ <row>
+<entry><literal>"core-rlaager-irchelper"</literal></entry>
+<entry>
+This is the plugin ID for the IRC Helper plugin, which is now part of the
+Purple Plugin Pack. Its ID follows the rules because its life started prior
+to its inclusion in the Plugin Pack.
+</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </para>
+ </sect2>
+
+ <sect2 id="plugin-ids-plugin-db">
+ <title>Plugin Database</title>
+ <para>
+Although it doesn't exist yet, in time there will be a plugin database
+on the Pidgin website, where users can download and install new plugins.
+Plugins will be accessed by your plugin ID, which is one reason why it
+must be unique.
+ </para>
+ </sect2>
+</chapter>
diff --git a/doc/reference/libpurple/signals_account.xml b/doc/reference/libpurple/signals_account.xml
new file mode 100644
index 0000000000..910efc85bb
--- /dev/null
+++ b/doc/reference/libpurple/signals_account.xml
@@ -0,0 +1,521 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-signals-account">
+<title>Account signals</title>
+
+<refsect1 id="accounts.signals" role="signal_proto">
+<title role="signal_proto.title">List of signals</title>
+<synopsis>
+ &quot;<link linkend="accounts-account-created">account-created</link>&quot;
+ &quot;<link linkend="accounts-account-destroying">account-destroying</link>&quot;
+ &quot;<link linkend="accounts-account-added">account-added</link>&quot;
+ &quot;<link linkend="accounts-account-connecting">account-connecting</link>&quot;
+ &quot;<link linkend="accounts-account-removed">account-removed</link>&quot;
+ &quot;<link linkend="accounts-account-disabled">account-disabled</link>&quot;
+ &quot;<link linkend="accounts-account-enabled">account-enabled</link>&quot;
+ &quot;<link linkend="accounts-account-setting-info">account-setting-info</link>&quot;
+ &quot;<link linkend="accounts-account-set-info">account-set-info</link>&quot;
+ &quot;<link linkend="accounts-account-status-changed">account-status-changed</link>&quot;
+ &quot;<link linkend="accounts-account-actions-changed">account-actions-changed</link>&quot;
+ &quot;<link linkend="accounts-account-alias-changed">account-alias-changed</link>&quot;
+ &quot;<link linkend="accounts-account-authorization-requested">account-authorization-requested</link>&quot;
+ &quot;<link linkend="accounts-account-authorization-denied">account-authorization-denied</link>&quot;
+ &quot;<link linkend="accounts-account-authorization-granted">account-authorization-granted</link>&quot;
+ &quot;<link linkend="accounts-account-error-changed">account-error-changed</link>&quot;
+ &quot;<link linkend="accounts-account-signed-on">account-signed-on</link>&quot;
+ &quot;<link linkend="accounts-account-signed-off">account-signed-off</link>&quot;
+ &quot;<link linkend="accounts-account-connection-error">account-connection-error</link>&quot;
+</synopsis>
+</refsect1>
+
+<refsect1 id="accounts.signal-details" role="signals">
+<title role="signals.title">Signal details</title>
+
+<refsect2 id="accounts-account-created" role="signal">
+ <title>The <literal>&quot;account-created&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when an account is created by calling purple_account_new.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="accounts-account-destroying" role="signal">
+ <title>The <literal>&quot;account-destroying&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when an account is about to be destroyed.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="accounts-account-added" role="signal">
+ <title>The <literal>&quot;account-added&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when an account is added.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account that was added. See <literal>purple_accounts_add</literal>.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="accounts-account-connecting" role="signal">
+ <title>The <literal>&quot;account-connecting&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ gpointer user_data)
+</programlisting>
+ <para>
+This is called when an account is in the process of connecting.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account in the process of connecting.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="accounts-account-removed" role="signal">
+ <title>The <literal>&quot;account-removed&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when an account is removed.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account that was removed. See <literal>purple_accounts_remove</literal>.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="accounts-account-disabled" role="signal">
+ <title>The <literal>&quot;account-disabled&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when an account is disabled.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account that was disabled.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="accounts-account-enabled" role="signal">
+ <title>The <literal>&quot;account-enabled&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when an account is enabled.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account that was enabled.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="accounts-account-setting-info" role="signal">
+ <title>The <literal>&quot;account-setting-info&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ const char *new_info,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a user is about to send his new user info, or profile, to the server.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account that the info will be set on.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>new_info</parameter>&#160;:</term>
+ <listitem><simpara>The new information to set.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="accounts-account-set-info" role="signal">
+ <title>The <literal>&quot;account-set-info&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ const char *new_info,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a user sent his new user info, or profile, to the server.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account that the info was set on.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>new_info</parameter>&#160;:</term>
+ <listitem><simpara>The new information set.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="accounts-account-status-changed" role="signal">
+ <title>The <literal>&quot;account-status-changed&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ PurpleStatus *old,
+ PurpleStatus *new,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when the status of an account changes (after the change).
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account that changed status.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>old</parameter>&#160;:</term>
+ <listitem><simpara>The status before change.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>new</parameter>&#160;:</term>
+ <listitem><simpara>The status after change.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="accounts-account-actions-changed" role="signal">
+ <title>The <literal>&quot;account-actions-changed&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when the account actions are changed after initial connection.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account whose actions changed.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="accounts-account-alias-changed" role="signal">
+ <title>The <literal>&quot;account-alias-changed&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ const char *old,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when the alias of an account changes (after the change).
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account for which the alias was changed.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>old</parameter>&#160;:</term>
+ <listitem><simpara>The alias before change.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="accounts-account-authorization-requested" role="signal">
+ <title>The <literal>&quot;account-authorization-requested&quot;</literal> signal</title>
+<programlisting>
+int user_function (PurpleAccount *account,
+ const char *user,
+ const char *message,
+ char **response,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a user requests authorization.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user</parameter>&#160;:</term>
+ <listitem><simpara>The name of the user requesting authorization.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>The authorization request message.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>response</parameter>&#160;:</term>
+ <listitem><simpara>The message to send in the response.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Returns</emphasis>&#160;:</term>
+ <listitem><simpara>PURPLE_ACCOUNT_RESPONSE_IGNORE to silently ignore the request, PURPLE_ACCOUNT_RESPONSE_DENY to block the request (the sender might get informed), PURPLE_ACCOUNT_RESPONSE_ACCEPT if the request should be granted. If PURPLE_ACCOUNT_RESPONSE_PASS is returned, then the user will be prompted with the request.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="accounts-account-authorization-denied" role="signal">
+ <title>The <literal>&quot;account-authorization-denied&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ const char *user,
+ const char *message,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when the authorization request for a buddy is denied.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user</parameter>&#160;:</term>
+ <listitem><simpara>The name of the user requesting authorization.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>The message to tell the buddy who was denied.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="accounts-account-authorization-granted" role="signal">
+ <title>The <literal>&quot;account-authorization-granted&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ const char *user,
+ const char *message,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when the authorization request for a buddy is granted.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user</parameter>&#160;:</term>
+ <listitem><simpara>The name of the user requesting authorization.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>The message to tell the buddy who was granted authorization.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="accounts-account-error-changed" role="signal">
+ <title>The <literal>&quot;account-error-changed&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ const PurpleConnectionErrorInfo *old_error,
+ const PurpleConnectionErrorInfo *current_error,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when <literal>account</literal>'s error changes. You should not call purple_account_clear_current_error() while this signal is being emitted.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account whose error has changed.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>old_error</parameter>&#160;:</term>
+ <listitem><simpara>The account's previous error, or <literal>NULL</literal> if it had no error. After this signal is emitted, <literal>old_error</literal> is not guaranteed to be a valid pointer.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>new_error</parameter>&#160;:</term>
+ <listitem><simpara>The account's new error, or <literal>NULL</literal> if it has no error. If not <literal>NULL</literal>, <literal>new_error</literal> will remain a valid until pointer just after the next time this signal is emitted for this <literal>account</literal>. See <literal>purple_account_get_current_error</literal>().</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="accounts-account-signed-on" role="signal">
+ <title>The <literal>&quot;account-signed-on&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when an account has signed on.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account that has signed on.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="accounts-account-signed-off" role="signal">
+ <title>The <literal>&quot;account-signed-off&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when an account has signed off.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account that has signed off.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="accounts-account-connection-error" role="signal">
+ <title>The <literal>&quot;account-connection-error&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *gc,
+ PurpleConnectionError err,
+ const gchar *desc,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a connection error occurs, before <literal>"signed"</literal>-off.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account on which the error has occurred.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>err</parameter>&#160;:</term>
+ <listitem><simpara>The error that occurred.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>desc</parameter>&#160;:</term>
+ <listitem><simpara>A description of the error, giving more information.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+</refsect1>
+
+</chapter>
diff --git a/doc/reference/libpurple/signals_blist.xml b/doc/reference/libpurple/signals_blist.xml
new file mode 100644
index 0000000000..5483ca3878
--- /dev/null
+++ b/doc/reference/libpurple/signals_blist.xml
@@ -0,0 +1,263 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-signals-blist">
+<title>Buddy List signals</title>
+
+<refsect1 id="blist.signals" role="signal_proto">
+<title role="signal_proto.title">List of signals</title>
+<synopsis>
+ &quot;<link linkend="blist-buddy-status-changed">buddy-status-changed</link>&quot;
+ &quot;<link linkend="blist-buddy-idle-changed">buddy-idle-changed</link>&quot;
+ &quot;<link linkend="blist-buddy-signed-on">buddy-signed-on</link>&quot;
+ &quot;<link linkend="blist-buddy-signed-off">buddy-signed-off</link>&quot;
+ &quot;<link linkend="blist-update-idle">update-idle</link>&quot;
+ &quot;<link linkend="blist-blist-node-extended-menu">blist-node-extended-menu</link>&quot;
+ &quot;<link linkend="blist-buddy-icon-changed">buddy-icon-changed</link>&quot;
+ &quot;<link linkend="blist-blist-node-aliased">blist-node-aliased</link>&quot;
+ &quot;<link linkend="blist-buddy-caps-changed">buddy-caps-changed</link>&quot;
+ &quot;<link linkend="blist-ui-caps-changed">ui-caps-changed</link>&quot;
+</synopsis>
+</refsect1>
+
+<refsect1 id="blist.signal-details" role="signals">
+<title role="signals.title">Signal details</title>
+
+<refsect2 id="blist-buddy-status-changed" role="signal">
+ <title>The <literal>&quot;buddy-status-changed&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleBuddy *buddy,
+ PurpleStatus *old_status,
+ PurpleStatus *status,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a buddy on your buddy list goes away.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>buddy</parameter>&#160;:</term>
+ <listitem><simpara>The buddy whose status changed.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>old_status</parameter>&#160;:</term>
+ <listitem><simpara>The status that the buddy just changed from.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>status</parameter>&#160;:</term>
+ <listitem><simpara>The status that the buddy just changed to.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="blist-buddy-idle-changed" role="signal">
+ <title>The <literal>&quot;buddy-idle-changed&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleBuddy *buddy,
+ gboolean old_idle,
+ gboolean idle,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a buddy on your buddy list becomes idle.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>buddy</parameter>&#160;:</term>
+ <listitem><simpara>The buddy whose idle status changed.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>old_idle</parameter>&#160;:</term>
+ <listitem><simpara>Whether the buddy was idle.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>idle</parameter>&#160;:</term>
+ <listitem><simpara>Whether the buddy is currently idle.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="blist-buddy-signed-on" role="signal">
+ <title>The <literal>&quot;buddy-signed-on&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleBuddy *buddy,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a buddy on your buddy list signs on.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>buddy</parameter>&#160;:</term>
+ <listitem><simpara>The buddy that signed on.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="blist-buddy-signed-off" role="signal">
+ <title>The <literal>&quot;buddy-signed-off&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleBuddy *buddy,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a buddy on your buddy list signs off.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>buddy</parameter>&#160;:</term>
+ <listitem><simpara>The buddy that signed off.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="blist-update-idle" role="signal">
+ <title>The <literal>&quot;update-idle&quot;</literal> signal</title>
+<programlisting>
+void user_function (gpointer user_data)
+</programlisting>
+ <para>
+Emitted when the buddy list is refreshed and the idle times are updated.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="blist-blist-node-extended-menu" role="signal">
+ <title>The <literal>&quot;blist-node-extended-menu&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleBlistNode *node,
+ GList **menu,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a buddlist menu is being constructed <literal>menu</literal> is a pointer to a GList of PurpleMenuAction's allowing a plugin to add menu items.
+ </para>
+</refsect2>
+
+<refsect2 id="blist-blist-node-added" role="signal">
+ <title>The <literal>&quot;blist-node-added&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleBlistNode *node,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a new blist node is added to the buddy list.
+ </para>
+</refsect2>
+
+<refsect2 id="blist-blist-node-removed" role="signal">
+ <title>The <literal>&quot;blist-node-removed&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleBlistNode *node,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a blist node is removed from the buddy list.
+ </para>
+</refsect2>
+
+<refsect2 id="blist-buddy-icon-changed" role="signal">
+ <title>The <literal>&quot;buddy-icon-changed&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleBuddy *buddy,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a buddy's icon is set.
+ </para>
+</refsect2>
+
+<refsect2 id="blist-blist-node-aliased" role="signal">
+ <title>The <literal>&quot;blist-node-aliased&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleBlistNode *node,
+ const char *old_alias,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a blist node (buddy, chat, or contact) is aliased.
+ </para>
+</refsect2>
+
+<refsect2 id="blist-buddy-caps-changed" role="signal">
+ <title>The <literal>&quot;buddy-caps-changed&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleBuddy *buddy,
+ PurpleMediaCaps newcaps,
+ PurpleMediaCaps oldcaps,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when updating a buddy's media capabilities.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>buddy</parameter>&#160;:</term>
+ <listitem><simpara>The buddy.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>newcaps</parameter>&#160;:</term>
+ <listitem><simpara>.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>oldcaps</parameter>&#160;:</term>
+ <listitem><simpara>.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="blist-ui-caps-changed" role="signal">
+ <title>The <literal>&quot;ui-caps-changed&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleMediaCaps newcaps,
+ PurpleMediaCaps oldcaps,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when updating the media capabilities of the UI.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>newcaps</parameter>&#160;:</term>
+ <listitem><simpara>.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>oldcaps</parameter>&#160;:</term>
+ <listitem><simpara>.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+</refsect1>
+
+</chapter>
diff --git a/doc/reference/libpurple/signals_certificate.xml b/doc/reference/libpurple/signals_certificate.xml
new file mode 100644
index 0000000000..8ba6ea71b4
--- /dev/null
+++ b/doc/reference/libpurple/signals_certificate.xml
@@ -0,0 +1,65 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-signals-certificate">
+<title>Certificate signals</title>
+
+<refsect1 id="certificates.signals" role="signal_proto">
+<title role="signal_proto.title">List of signals</title>
+<synopsis>
+ &quot;<link linkend="certificates-certificate-stored">certificate-stored</link>&quot;
+ &quot;<link linkend="certificates-certificate-deleted">certificate-deleted</link>&quot;
+</synopsis>
+</refsect1>
+
+<refsect1 id="certificates.signal-details" role="signals">
+<title role="signals.title">Signal details</title>
+
+<refsect2 id="certificates-certificate-stored" role="signal">
+ <title>The <literal>&quot;certificate-stored&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleCertificatePool *pool,
+ const gchar *id,
+ gpointer data)
+</programlisting>
+ <para>
+Emitted when a pool stores a certificate. Connect to the pool instance.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>pool</parameter>&#160;:</term>
+ <listitem><simpara>Pool the certificate has been stored into.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>id</parameter>&#160;:</term>
+ <listitem><simpara>Key the certificate was stored under.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="certificates-certificate-deleted" role="signal">
+ <title>The <literal>&quot;certificate-deleted&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleCertificatePool *pool,
+ const gchar *id,
+ gpointer data)
+</programlisting>
+ <para>
+Emitted when a pool deletes a certificate. Connect to the pool instance.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>pool</parameter>&#160;:</term>
+ <listitem><simpara>Pool the certificate was deleted from.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>id</parameter>&#160;:</term>
+ <listitem><simpara>Key that was deleted.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+</refsect1>
+
+</chapter>
diff --git a/doc/reference/libpurple/signals_cmd.xml b/doc/reference/libpurple/signals_cmd.xml
new file mode 100644
index 0000000000..c3fd0d0eab
--- /dev/null
+++ b/doc/reference/libpurple/signals_cmd.xml
@@ -0,0 +1,73 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-signals-cmd">
+<title>Command signals</title>
+
+<refsect1 id="cmds.signals" role="signal_proto">
+<title role="signal_proto.title">List of signals</title>
+<synopsis>
+ &quot;<link linkend="cmds-cmd-added">cmd-added</link>&quot;
+ &quot;<link linkend="cmds-cmd-removed">cmd-removed</link>&quot;
+</synopsis>
+</refsect1>
+
+<refsect1 id="cmds.signal-details" role="signals">
+<title role="signals.title">Signal details</title>
+
+<refsect2 id="cmds-cmd-added" role="signal">
+ <title>The <literal>&quot;cmd-added&quot;</literal> signal</title>
+<programlisting>
+void user_function (const char *command,
+ PurpleCmdPriority priority,
+ PurpleCmdFlag flag,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a new command is added.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>command</parameter>&#160;:</term>
+ <listitem><simpara>The new command.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>priority</parameter>&#160;:</term>
+ <listitem><simpara>The priority of the new command.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>flag</parameter>&#160;:</term>
+ <listitem><simpara>The command flags.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="cmds-cmd-removed" role="signal">
+ <title>The <literal>&quot;cmd-removed&quot;</literal> signal</title>
+<programlisting>
+void user_function (const char *command,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a command is removed.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>command</parameter>&#160;:</term>
+ <listitem><simpara>The removed command.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+</refsect1>
+
+</chapter>
diff --git a/doc/reference/libpurple/signals_connection.xml b/doc/reference/libpurple/signals_connection.xml
new file mode 100644
index 0000000000..436fae253e
--- /dev/null
+++ b/doc/reference/libpurple/signals_connection.xml
@@ -0,0 +1,165 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-signals-connection">
+<title>Connection signals</title>
+
+<refsect1 id="connections.signals" role="signal_proto">
+<title role="signal_proto.title">List of signals</title>
+<synopsis>
+ &quot;<link linkend="connections-signing-on">signing-on</link>&quot;
+ &quot;<link linkend="connections-signed-on">signed-on</link>&quot;
+ &quot;<link linkend="connections-autojoin">autojoin</link>&quot;
+ &quot;<link linkend="connections-signing-off">signing-off</link>&quot;
+ &quot;<link linkend="connections-signed-off">signed-off</link>&quot;
+ &quot;<link linkend="connections-connection-error">connection-error</link>&quot;
+</synopsis>
+</refsect1>
+
+<refsect1 id="connections.signal-details" role="signals">
+<title role="signals.title">Signal details</title>
+
+<refsect2 id="connections-signing-on" role="signal">
+ <title>The <literal>&quot;signing-on&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleConnection *gc,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a connection is about to sign on.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>gc</parameter>&#160;:</term>
+ <listitem><simpara>The connection that is about to sign on.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="connections-signed-on" role="signal">
+ <title>The <literal>&quot;signed-on&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleConnection *gc,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a connection has signed on.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>gc</parameter>&#160;:</term>
+ <listitem><simpara>The connection that has signed on.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="connections-autojoin" role="signal">
+ <title>The <literal>&quot;autojoin&quot;</literal> signal</title>
+<programlisting>
+gboolean user_function (PurpleConnection *gc,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a connection has signed on, after the signed-on signal, to signal UIs to autojoin chats if they wish. UIs should connect to this with <literal>PURPLE_SIGNAL_PRIORITY_HIGHEST</literal> to allow plugins to block this signal before the UI sees it and then re-emit it later.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>gc</parameter>&#160;:</term>
+ <listitem><simpara>The connection that has signed on.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Returns</emphasis>&#160;:</term>
+ <listitem><simpara><literal>TRUE</literal> if the signal was handled or <literal>FALSE</literal> otherwise. In practice, the return value is irrelevant, as it really only exists so plugins can block the UI's autojoin.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="connections-signing-off" role="signal">
+ <title>The <literal>&quot;signing-off&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleConnection *gc,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a connection is about to sign off.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>gc</parameter>&#160;:</term>
+ <listitem><simpara>The connection that is about to sign off.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="connections-signed-off" role="signal">
+ <title>The <literal>&quot;signed-off&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleConnection *gc,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a connection has signed off.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>gc</parameter>&#160;:</term>
+ <listitem><simpara>The connection that has signed off.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="connections-connection-error" role="signal">
+ <title>The <literal>&quot;connection-error&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleConnection *gc,
+ PurpleConnectionError err,
+ const gchar *desc,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a connection error occurs, before <literal>"signed"</literal>-off.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>gc</parameter>&#160;:</term>
+ <listitem><simpara>The connection on which the error has occurred.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>err</parameter>&#160;:</term>
+ <listitem><simpara>The error that occurred.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>desc</parameter>&#160;:</term>
+ <listitem><simpara>A description of the error, giving more information.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+</refsect1>
+
+</chapter>
diff --git a/doc/reference/libpurple/signals_conversation.xml b/doc/reference/libpurple/signals_conversation.xml
new file mode 100644
index 0000000000..b5d5b5ee4e
--- /dev/null
+++ b/doc/reference/libpurple/signals_conversation.xml
@@ -0,0 +1,1232 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-signals-conversation">
+<title>Conversation signals</title>
+
+<refsect1 id="conversations.signals" role="signal_proto">
+<title role="signal_proto.title">List of signals</title>
+<synopsis>
+ &quot;<link linkend="conversations-writing-im-msg">writing-im-msg</link>&quot;
+ &quot;<link linkend="conversations-wrote-im-msg">wrote-im-msg</link>&quot;
+ &quot;<link linkend="conversations-sending-im-msg">sending-im-msg</link>&quot;
+ &quot;<link linkend="conversations-sent-im-msg">sent-im-msg</link>&quot;
+ &quot;<link linkend="conversations-receiving-im-msg">receiving-im-msg</link>&quot;
+ &quot;<link linkend="conversations-received-im-msg">received-im-msg</link>&quot;
+ &quot;<link linkend="conversations-blocked-im-msg">blocked-im-msg</link>&quot;
+ &quot;<link linkend="conversations-writing-chat-msg">writing-chat-msg</link>&quot;
+ &quot;<link linkend="conversations-wrote-chat-msg">wrote-chat-msg</link>&quot;
+ &quot;<link linkend="conversations-sending-chat-msg">sending-chat-msg</link>&quot;
+ &quot;<link linkend="conversations-sent-chat-msg">sent-chat-msg</link>&quot;
+ &quot;<link linkend="conversations-receiving-chat-msg">receiving-chat-msg</link>&quot;
+ &quot;<link linkend="conversations-received-chat-msg">received-chat-msg</link>&quot;
+ &quot;<link linkend="conversations-conversation-created">conversation-created</link>&quot;
+ &quot;<link linkend="conversations-conversation-updated">conversation-updated</link>&quot;
+ &quot;<link linkend="conversations-deleting-conversation">deleting-conversation</link>&quot;
+ &quot;<link linkend="conversations-buddy-typing">buddy-typing</link>&quot;
+ &quot;<link linkend="conversations-buddy-typing-stopped">buddy-typing-stopped</link>&quot;
+ &quot;<link linkend="conversations-chat-user-joining">chat-user-joining</link>&quot;
+ &quot;<link linkend="conversations-chat-user-joined">chat-user-joined</link>&quot;
+ &quot;<link linkend="conversations-chat-user-flags">chat-user-flags</link>&quot;
+ &quot;<link linkend="conversations-chat-user-leaving">chat-user-leaving</link>&quot;
+ &quot;<link linkend="conversations-chat-user-left">chat-user-left</link>&quot;
+ &quot;<link linkend="conversations-chat-inviting-user">chat-inviting-user</link>&quot;
+ &quot;<link linkend="conversations-chat-invited-user">chat-invited-user</link>&quot;
+ &quot;<link linkend="conversations-chat-invited">chat-invited</link>&quot;
+ &quot;<link linkend="conversations-chat-invite-blocked">chat-invite-blocked</link>&quot;
+ &quot;<link linkend="conversations-chat-joined">chat-joined</link>&quot;
+ &quot;<link linkend="conversations-chat-join-failed">chat-join-failed</link>&quot;
+ &quot;<link linkend="conversations-chat-left">chat-left</link>&quot;
+ &quot;<link linkend="conversations-chat-topic-changed">chat-topic-changed</link>&quot;
+ &quot;<link linkend="conversations-cleared-message-history">cleared-message-history</link>&quot;
+ &quot;<link linkend="conversations-conversation-extended-menu">conversation-extended-menu</link>&quot;
+ &quot;<link linkend="conversations-sent-attention">sent-attention</link>&quot;
+ &quot;<link linkend="conversations-got-attention">got-attention</link>&quot;
+</synopsis>
+</refsect1>
+
+<refsect1 id="conversations.signal-details" role="signals">
+<title role="signals.title">Signal details</title>
+
+<refsect2 id="conversations-writing-im-msg" role="signal">
+ <title>The <literal>&quot;writing-im-msg&quot;</literal> signal</title>
+<programlisting>
+gboolean user_function (PurpleAccount *account,
+ const char *who,
+ char **message,
+ PurpleIMConversation *im,
+ PurpleMessageFlags flags,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted before a message is written in an IM conversation. If the message is changed, then the changed message is displayed and logged instead of the original message.
+ </para>
+ <note><para>
+Make sure to free <literal>*message</literal> before you replace it!
+ </para></note>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>who</parameter>&#160;:</term>
+ <listitem><simpara>The name of the user.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>A pointer to the message.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>im</parameter>&#160;:</term>
+ <listitem><simpara>The IM conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>flags</parameter>&#160;:</term>
+ <listitem><simpara>Flags for this message.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Returns</emphasis>&#160;:</term>
+ <listitem><simpara><literal>TRUE</literal> if the message should be canceled, or <literal>FALSE</literal> otherwise.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-wrote-im-msg" role="signal">
+ <title>The <literal>&quot;wrote-im-msg&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ const char *who,
+ char *message,
+ PurpleIMConversation *im,
+ PurpleMessageFlags flags,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted after a message is written and possibly displayed in a conversation.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>who</parameter>&#160;:</term>
+ <listitem><simpara>The name of the user.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>The message.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>im</parameter>&#160;:</term>
+ <listitem><simpara>The IM conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>flags</parameter>&#160;:</term>
+ <listitem><simpara>Flags for this message.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-sending-im-msg" role="signal">
+ <title>The <literal>&quot;sending-im-msg&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ const char *receiver,
+ char **message,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted before sending an IM to a user. <literal>message</literal> is a pointer to the message string, so the plugin can replace the message before being sent.
+ </para>
+ <note><para>
+Make sure to free <literal>*message</literal> before you replace it!
+ </para></note>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account the message is being sent on.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>receiver</parameter>&#160;:</term>
+ <listitem><simpara>The username of the receiver.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>A pointer to the outgoing message. This can be modified.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-sent-im-msg" role="signal">
+ <title>The <literal>&quot;sent-im-msg&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ const char *receiver,
+ const char *message,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted after sending an IM to a user.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account the message was sent on.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>receiver</parameter>&#160;:</term>
+ <listitem><simpara>The username of the receiver.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>The message that was sent.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-receiving-im-msg" role="signal">
+ <title>The <literal>&quot;receiving-im-msg&quot;</literal> signal</title>
+<programlisting>
+gboolean user_function (PurpleAccount *account,
+ char **sender,
+ char **message,
+ PurpleIMConversation *im,
+ PurpleMessageFlags *flags,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when an IM is received. The callback can replace the name of the sender, the message, or the flags by modifying the pointer to the strings and integer. This can also be used to cancel a message by returning <literal>TRUE</literal>.
+ </para>
+ <note><para>
+Make sure to free <literal>*sender</literal> and <literal>*message</literal> before you replace them!
+ </para></note>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account the message was received on.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>sender</parameter>&#160;:</term>
+ <listitem><simpara>A pointer to the username of the sender.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>A pointer to the message that was sent.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>im</parameter>&#160;:</term>
+ <listitem><simpara>The IM conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>flags</parameter>&#160;:</term>
+ <listitem><simpara>A pointer to the IM message flags.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Returns</emphasis>&#160;:</term>
+ <listitem><simpara><literal>TRUE</literal> if the message should be canceled, or <literal>FALSE</literal> otherwise.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-received-im-msg" role="signal">
+ <title>The <literal>&quot;received-im-msg&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ char *sender,
+ char *message,
+ PurpleIMConversation *im,
+ PurpleMessageFlags flags,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted after an IM is received.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account the message was received on.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>sender</parameter>&#160;:</term>
+ <listitem><simpara>The username of the sender.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>The message that was sent.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>im</parameter>&#160;:</term>
+ <listitem><simpara>The IM conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>flags</parameter>&#160;:</term>
+ <listitem><simpara>The IM message flags.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-blocked-im-msg" role="signal">
+ <title>The <literal>&quot;blocked-im-msg&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ const char *sender,
+ const char *message,
+ PurpleMessageFlags flags,
+ time_t when,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted after an IM is blocked due to privacy settings.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account the message was received on.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>sender</parameter>&#160;:</term>
+ <listitem><simpara>The username of the sender.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>The message that was blocked.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>flags</parameter>&#160;:</term>
+ <listitem><simpara>The IM message flags.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>when</parameter>&#160;:</term>
+ <listitem><simpara>The time the message was sent.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-writing-chat-msg" role="signal">
+ <title>The <literal>&quot;writing-chat-msg&quot;</literal> signal</title>
+<programlisting>
+gboolean user_function (PurpleAccount *account,
+ const char *who,
+ char **message,
+ PurpleChatConversation *chat,
+ PurpleMessageFlags flags,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted before a message is written in a chat conversation. If the message is changed, then the changed message is displayed and logged instead of the original message.
+ </para>
+ <note><para>
+Make sure to free <literal>*message</literal> before you replace it!
+ </para></note>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>who</parameter>&#160;:</term>
+ <listitem><simpara>The name of the user.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>A pointer to the message.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>chat</parameter>&#160;:</term>
+ <listitem><simpara>The chat conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>flags</parameter>&#160;:</term>
+ <listitem><simpara>Flags for this message.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Returns</emphasis>&#160;:</term>
+ <listitem><simpara><literal>TRUE</literal> if the message should be canceled, or <literal>FALSE</literal> otherwise.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-wrote-chat-msg" role="signal">
+ <title>The <literal>&quot;wrote-chat-msg&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ const char *who,
+ char *message,
+ PurpleChatConversation *chat,
+ PurpleMessageFlags flags,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted after a message is written and possibly displayed in a chat.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>who</parameter>&#160;:</term>
+ <listitem><simpara>The name of the user.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>The message.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>chat</parameter>&#160;:</term>
+ <listitem><simpara>The chat conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>flags</parameter>&#160;:</term>
+ <listitem><simpara>Flags for this message.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-sending-chat-msg" role="signal">
+ <title>The <literal>&quot;sending-chat-msg&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ char **message,
+ int id,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted before sending a message to a chat. <literal>message</literal> is a pointer to the message string, so the plugin can replace the message before being sent.
+ </para>
+ <note><para>
+Make sure to free <literal>*message</literal> before you replace it!
+ </para></note>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account the message is being sent on.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>A pointer to the message that will be sent.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>id</parameter>&#160;:</term>
+ <listitem><simpara>The ID of the chat.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-sent-chat-msg" role="signal">
+ <title>The <literal>&quot;sent-chat-msg&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ const char *message,
+ int id,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted after sending a message to a chat.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account the message was sent on.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>The message that was sent.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>id</parameter>&#160;:</term>
+ <listitem><simpara>The ID of the chat.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-receiving-chat-msg" role="signal">
+ <title>The <literal>&quot;receiving-chat-msg&quot;</literal> signal</title>
+<programlisting>
+gboolean user_function (PurpleAccount *account,
+ char **sender,
+ char **message,
+ PurpleChatConversation *chat,
+ int *flags,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a chat message is received. The callback can replace the name of the sender, the message, or the flags by modifying the pointer to the strings. This can also be used to cancel displaying a message by returning <literal>TRUE</literal>.
+ </para>
+ <note><para>
+Make sure to free <literal>*sender</literal> and <literal>*message</literal> before you replace them!
+ </para></note>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account the message was received on.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>sender</parameter>&#160;:</term>
+ <listitem><simpara>A pointer to the username of the sender.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>A pointer to the message that was sent.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>chat</parameter>&#160;:</term>
+ <listitem><simpara>The chat conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>flags</parameter>&#160;:</term>
+ <listitem><simpara>A pointer to the chat message flags.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Returns</emphasis>&#160;:</term>
+ <listitem><simpara><literal>TRUE</literal> if the message should be canceled, or <literal>FALSE</literal> otherwise.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-received-chat-msg" role="signal">
+ <title>The <literal>&quot;received-chat-msg&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ char *sender,
+ char *message,
+ PurpleChatConversation *chat,
+ PurpleMessageFlags flags,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted after a chat message is received.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account the message was received on.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>sender</parameter>&#160;:</term>
+ <listitem><simpara>The username of the sender.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>The message that was sent.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>chat</parameter>&#160;:</term>
+ <listitem><simpara>The chat conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>flags</parameter>&#160;:</term>
+ <listitem><simpara>The chat message flags.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-conversation-created" role="signal">
+ <title>The <literal>&quot;conversation-created&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleConversation *conv,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a new conversation is created.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>conv</parameter>&#160;:</term>
+ <listitem><simpara>The new conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-conversation-updated" role="signal">
+ <title>The <literal>&quot;conversation-updated&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleConversation *conv,
+ PurpleConvUpdateType type,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a conversation is updated.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>conv</parameter>&#160;:</term>
+ <listitem><simpara>The conversation that was updated.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>type</parameter>&#160;:</term>
+ <listitem><simpara>The type of update that was made.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-deleting-conversation" role="signal">
+ <title>The <literal>&quot;deleting-conversation&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleConversation *conv,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted just before a conversation is to be destroyed.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>conv</parameter>&#160;:</term>
+ <listitem><simpara>The conversation that's about to be destroyed.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-buddy-typing" role="signal">
+ <title>The <literal>&quot;buddy-typing&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ const char *name,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a buddy starts typing in a conversation window.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account of the user which is typing.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>name</parameter>&#160;:</term>
+ <listitem><simpara>The name of the user which is typing.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-buddy-typing-stopped" role="signal">
+ <title>The <literal>&quot;buddy-typing-stopped&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ const char *name,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a buddy stops typing in a conversation window.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account of the user which stopped typing.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>name</parameter>&#160;:</term>
+ <listitem><simpara>The name of the user which stopped typing.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-chat-user-joining" role="signal">
+ <title>The <literal>&quot;chat-user-joining&quot;</literal> signal</title>
+<programlisting>
+gboolean user_function (PurpleChatConversation *chat,
+ const char *name,
+ PurpleChatUserFlags flags,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a buddy is joining a chat, before the list of users in the chat updates to include the new user.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>chat</parameter>&#160;:</term>
+ <listitem><simpara>The chat conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>name</parameter>&#160;:</term>
+ <listitem><simpara>The name of the user that is joining the conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>flags</parameter>&#160;:</term>
+ <listitem><simpara>The flags of the user that is joining the conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Returns</emphasis>&#160;:</term>
+ <listitem><simpara><literal>TRUE</literal> if the join should be hidden, or <literal>FALSE</literal> otherwise.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-chat-user-joined" role="signal">
+ <title>The <literal>&quot;chat-user-joined&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleChatConversation *chat,
+ const char *name,
+ PurpleChatUserFlags flags,
+ gboolean new_arrival,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a buddy joined a chat, after the users list is updated.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>chat</parameter>&#160;:</term>
+ <listitem><simpara>The chat conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>name</parameter>&#160;:</term>
+ <listitem><simpara>The name of the user that has joined the conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>flags</parameter>&#160;:</term>
+ <listitem><simpara>The flags of the user that has joined the conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>new_arrival</parameter>&#160;:</term>
+ <listitem><simpara>If the buddy is a new arrival.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-chat-join-failed" role="signal">
+ <title>The <literal>&quot;chat-join-failed&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleConnection *gc,
+ GHashTable *components,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when an account fails to join a chat room.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>gc</parameter>&#160;:</term>
+ <listitem><simpara>The PurpleConnection of the account which failed to join the chat.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>data</parameter>&#160;:</term>
+ <listitem><simpara>The components passed to purple_serv_join_chat() originally. The hash function should be g_str_hash() and the equal function should be g_str_equal().</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-chat-user-flags" role="signal">
+ <title>The <literal>&quot;chat-user-flags&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleChatUser *chatuser,
+ PurpleChatUserFlags oldflags,
+ PurpleChatUserFlags newflags,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a user in a chat changes flags.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>chatuser</parameter>&#160;:</term>
+ <listitem><simpara>The chat user whose flags changed.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>oldflags</parameter>&#160;:</term>
+ <listitem><simpara>The old flags.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>newflags</parameter>&#160;:</term>
+ <listitem><simpara>The new flags.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-chat-user-leaving" role="signal">
+ <title>The <literal>&quot;chat-user-leaving&quot;</literal> signal</title>
+<programlisting>
+gboolean user_function (PurpleChatConversation *chat,
+ const char *name,
+ const char *reason,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a user is leaving a chat, before the user list is updated. This may include an optional reason why the user is leaving.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>chat</parameter>&#160;:</term>
+ <listitem><simpara>The chat conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>name</parameter>&#160;:</term>
+ <listitem><simpara>The name of the user that is leaving the chat.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>reason</parameter>&#160;:</term>
+ <listitem><simpara>The optional reason why the user is leaving.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Returns</emphasis>&#160;:</term>
+ <listitem><simpara><literal>TRUE</literal> if the leave should be hidden, or <literal>FALSE</literal> otherwise.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-chat-user-left" role="signal">
+ <title>The <literal>&quot;chat-user-left&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleChatConversation *chat,
+ const char *name,
+ const char *reason,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a user leaves a chat, after the user list is updated. This may include an optional reason why the user is leaving.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>chat</parameter>&#160;:</term>
+ <listitem><simpara>The chat conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>name</parameter>&#160;:</term>
+ <listitem><simpara>The name of the user that left the chat.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>reason</parameter>&#160;:</term>
+ <listitem><simpara>The optional reason why the user left the chat.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-chat-inviting-user" role="signal">
+ <title>The <literal>&quot;chat-inviting-user&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleChatConversation *chat,
+ const char *name,
+ char **invite_message,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a user is being invited to the chat. The callback can replace the invite message to the invitee by modifying the pointer to the invite message.
+ </para>
+ <note><para>
+Make sure to free <literal>*invite_message</literal> before you replace it!
+ </para></note>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>chat</parameter>&#160;:</term>
+ <listitem><simpara>The chat conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>name</parameter>&#160;:</term>
+ <listitem><simpara>The name of the user being invited.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>invite_message</parameter>&#160;:</term>
+ <listitem><simpara>A pointer to the reason why a user is being invited.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-chat-invited-user" role="signal">
+ <title>The <literal>&quot;chat-invited-user&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleChatConversation *conv,
+ const char *name,
+ const char *invite_message,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a user invited another user to a chat.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>chat</parameter>&#160;:</term>
+ <listitem><simpara>The chat conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>name</parameter>&#160;:</term>
+ <listitem><simpara>The name of the user that was invited.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>invite_message</parameter>&#160;:</term>
+ <listitem><simpara>The message to be sent to the user when invited.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-chat-invited" role="signal">
+ <title>The <literal>&quot;chat-invited&quot;</literal> signal</title>
+<programlisting>
+gint user_function (PurpleAccount *account,
+ const char *inviter,
+ const char *chat,
+ const char *invite_messageconst GHashTable *components,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when an account was invited to a chat.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account being invited.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>inviter</parameter>&#160;:</term>
+ <listitem><simpara>The username of the person inviting the account.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>chat</parameter>&#160;:</term>
+ <listitem><simpara>The name of the chat you're being invited to.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>invite_message</parameter>&#160;:</term>
+ <listitem><simpara>The optional invite message.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>components</parameter>&#160;:</term>
+ <listitem><simpara>The components necessary if you want to call purple_serv_join_chat().</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Returns</emphasis>&#160;:</term>
+ <listitem><simpara>Less than zero if the invitation should be rejected, greater than zero if the invitation should be accepted. If zero is returned, the default behavior will be maintained: the user will be prompted.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-chat-invite-blocked" role="signal">
+ <title>The <literal>&quot;chat-invite-blocked&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ const char *inviter,
+ const char *name,
+ const char *message,
+ GHashTable *data)
+</programlisting>
+ <para>
+Emitted when an invitation to join a chat is blocked.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account the invitation was sent to.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>inviter</parameter>&#160;:</term>
+ <listitem><simpara>The name of the person sending the invitation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>name</parameter>&#160;:</term>
+ <listitem><simpara>The name of the chat invited to.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>The invitation message sent.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>data</parameter>&#160;:</term>
+ <listitem><simpara>Hashtable containing data about the invited chat.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-chat-joined" role="signal">
+ <title>The <literal>&quot;chat-joined&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleChatConversation *chat,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when an account joins a chat room.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>chat</parameter>&#160;:</term>
+ <listitem><simpara>The conversation that joined the chat room.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-chat-left" role="signal">
+ <title>The <literal>&quot;chat-left&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleChatConversation *chat,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when an account leaves a chat room.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>chat</parameter>&#160;:</term>
+ <listitem><simpara>The conversation that left the chat room.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-chat-topic-changed" role="signal">
+ <title>The <literal>&quot;chat-topic-changed&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleChatConversation *chat,
+ const char *who,
+ const char *topic,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when the topic is changed in a chat.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>chat</parameter>&#160;:</term>
+ <listitem><simpara>The chat conversation whose topic changed.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>who</parameter>&#160;:</term>
+ <listitem><simpara>The name of the person that changed the topic.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>topic</parameter>&#160;:</term>
+ <listitem><simpara>The new topic.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-conversation-extended-menu" role="signal">
+ <title>The <literal>&quot;conversation-extended-menu&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleConversation *conv,
+ GList **list,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when the UI requests a list of plugin actions for a conversation.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>conv</parameter>&#160;:</term>
+ <listitem><simpara>The conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>list</parameter>&#160;:</term>
+ <listitem><simpara>A pointer to the list of actions.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-cleared-message-history" role="signal">
+ <title>The <literal>&quot;cleared-message-history&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleConversation *conv,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when the conversation history is cleared.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>conv</parameter>&#160;:</term>
+ <listitem><simpara>The conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-sent-attention" role="signal">
+ <title>The <literal>&quot;sent-attention&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ const char *who,
+ PurpleConversation *conv,
+ guint type,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when receiving an attention message (buzz, nudge, etc.).
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>who</parameter>&#160;:</term>
+ <listitem><simpara>The name of the person receiving the attention.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>conv</parameter>&#160;:</term>
+ <listitem><simpara>The conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>type</parameter>&#160;:</term>
+ <listitem><simpara>The attention type (an index starting at 0).</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="conversations-got-attention" role="signal">
+ <title>The <literal>&quot;got-attention&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ const char *who,
+ PurpleConversation *conv,
+ guint type,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when receiving an attention message (buzz, nudge, etc.).
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>who</parameter>&#160;:</term>
+ <listitem><simpara>The name of the person sending the attention.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>conv</parameter>&#160;:</term>
+ <listitem><simpara>The conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>type</parameter>&#160;:</term>
+ <listitem><simpara>The attention type (an index starting at 0).</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+</refsect1>
+
+</chapter>
diff --git a/doc/reference/libpurple/signals_core.xml b/doc/reference/libpurple/signals_core.xml
new file mode 100644
index 0000000000..777fd1ad17
--- /dev/null
+++ b/doc/reference/libpurple/signals_core.xml
@@ -0,0 +1,68 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-signals-core">
+<title>Core signals</title>
+
+<refsect1 id="core.signals" role="signal_proto">
+<title role="signal_proto.title">List of signals</title>
+<synopsis>
+ &quot;<link linkend="core-quitting">quitting</link>&quot;
+ &quot;<link linkend="core-uri-handler">uri-handler</link>&quot;
+</synopsis>
+</refsect1>
+
+<refsect1 id="core.signal-details" role="signals">
+<title role="signals.title">Signal details</title>
+
+<refsect2 id="core-quitting" role="signal">
+ <title>The <literal>&quot;quitting&quot;</literal> signal</title>
+<programlisting>
+void user_function (gpointer user_data)
+</programlisting>
+ <para>
+Emitted when libpurple is quitting.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="core-uri-handler" role="signal">
+ <title>The <literal>&quot;uri-handler&quot;</literal> signal</title>
+<programlisting>
+gboolean user_function (const gchar *proto,
+ const gchar *cmd,
+ GHashTable *params,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when handling a registered URI.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>proto</parameter>&#160;:</term>
+ <listitem><simpara>The protocol of the URI.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>cmd</parameter>&#160;:</term>
+ <listitem><simpara>The 'command' of the URI.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>params</parameter>&#160;:</term>
+ <listitem><simpara>Any key/value parameters from the URI.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+</refsect1>
+
+</chapter>
diff --git a/doc/reference/libpurple/signals_dbus_server.xml b/doc/reference/libpurple/signals_dbus_server.xml
new file mode 100644
index 0000000000..dc6f7b9e82
--- /dev/null
+++ b/doc/reference/libpurple/signals_dbus_server.xml
@@ -0,0 +1,72 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-signals-dbus-server">
+<title>D-Bus Server signals</title>
+
+<refsect1 id="dbus_server.signals" role="signal_proto">
+<title role="signal_proto.title">List of signals</title>
+<synopsis>
+ &quot;<link linkend="dbus_server-dbus-method-called">dbus-method-called</link>&quot;
+ &quot;<link linkend="dbus_server-dbus-introspect">dbus-introspect</link>&quot;
+</synopsis>
+</refsect1>
+
+<refsect1 id="dbus_server.signal-details" role="signals">
+<title role="signals.title">Signal details</title>
+
+<refsect2 id="dbus_server-dbus-method-called" role="signal">
+ <title>The <literal>&quot;dbus-method-called&quot;</literal> signal</title>
+<programlisting>
+gboolean user_function (DBusConnection *connection,
+ DBusMessage *message,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a dbus method is going to be called.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>connection</parameter>&#160;:</term>
+ <listitem><simpara>The DBus connection.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>The DBus message.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Returns</emphasis>&#160;:</term>
+ <listitem><simpara>TRUE if signal handler handled the method. ???.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="dbus_server-dbus-introspect" role="signal">
+ <title>The <literal>&quot;dbus-introspect&quot;</literal> signal</title>
+<programlisting>
+void user_function (GList **bidings_list,
+ gpointer user_data)
+</programlisting>
+ <para>
+???.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>bindings_list</parameter>&#160;:</term>
+ <listitem><simpara>???.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+</refsect1>
+
+</chapter>
diff --git a/doc/reference/libpurple/signals_imgstore.xml b/doc/reference/libpurple/signals_imgstore.xml
new file mode 100644
index 0000000000..debcfb854f
--- /dev/null
+++ b/doc/reference/libpurple/signals_imgstore.xml
@@ -0,0 +1,44 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-signals-imgstore">
+<title>Stored Image signals</title>
+
+<refsect1 id="imgstores.signals" role="signal_proto">
+<title role="signal_proto.title">List of signals</title>
+<synopsis>
+ &quot;<link linkend="imgstores-image-deleting">image-deleting</link>&quot;
+</synopsis>
+</refsect1>
+
+<refsect1 id="imgstores.signal-details" role="signals">
+<title role="signals.title">Signal details</title>
+
+<refsect2 id="imgstores-image-deleting" role="signal">
+ <title>The <literal>&quot;image-deleting&quot;</literal> signal</title>
+<programlisting>
+char * user_function (const PurpleStoredImage *img,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a #PurpleStoredImage is about to be destroyed. This allows for what amounts to weak references. Code can hold onto a pointer to the PurpleStoredImage without actually "holding" a reference. They can then use a signal handler to let them know when their img is about to be destroyed.
+ </para>
+ <note><para>
+It's not possible to purple_imgstore_ref() img to save it.
+ </para></note>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>img</parameter>&#160;:</term>
+ <listitem><simpara>The image about to be destroyed.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+</refsect1>
+
+</chapter>
diff --git a/doc/reference/libpurple/signals_jabber.xml b/doc/reference/libpurple/signals_jabber.xml
new file mode 100644
index 0000000000..413efd929f
--- /dev/null
+++ b/doc/reference/libpurple/signals_jabber.xml
@@ -0,0 +1,311 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-signals-jabber">
+<title>Jabber signals</title>
+
+<refsect1 id="jabber.signals" role="signal_proto">
+<title role="signal_proto.title">List of signals</title>
+<synopsis>
+ &quot;<link linkend="jabber-jabber-receiving-iq">jabber-receiving-iq</link>&quot;
+ &quot;<link linkend="jabber-jabber-receiving-message">jabber-receiving-message</link>&quot;
+ &quot;<link linkend="jabber-jabber-receiving-presence">jabber-receiving-presence</link>&quot;
+ &quot;<link linkend="jabber-jabber-watched-iq">jabber-watched-iq</link>&quot;
+ &quot;<link linkend="jabber-jabber-register-namespace-watcher">jabber-register-namespace-watcher</link>&quot;
+ &quot;<link linkend="jabber-jabber-unregister-namespace-watcher">jabber-unregister-namespace-watcher</link>&quot;
+ &quot;<link linkend="jabber-jabber-sending-xmlnode">jabber-sending-xmlnode</link>&quot;
+ &quot;<link linkend="jabber-jabber-receiving-xmlnode">jabber-receiving-xmlnode</link>&quot;
+</synopsis>
+</refsect1>
+
+<refsect1 id="jabber.signal-details" role="signals">
+<title role="signals.title">Signal details</title>
+
+<refsect2 id="jabber-jabber-receiving-iq" role="signal">
+ <title>The <literal>&quot;jabber-receiving-iq&quot;</literal> signal</title>
+<programlisting>
+gboolean user_function (PurpleConnection *gc,
+ const char *type,
+ const char *id,
+ const char *from,
+ PurpleXmlNode *iq,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when an XMPP IQ stanza is received. Allows a plugin to process IQ stanzas.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>gc</parameter>&#160;:</term>
+ <listitem><simpara>The connection on which the stanza is received.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>type</parameter>&#160;:</term>
+ <listitem><simpara>The IQ type ('get', 'set', 'result', or 'error').</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>id</parameter>&#160;:</term>
+ <listitem><simpara>The ID attribute from the stanza. MUST NOT be NULL.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>from</parameter>&#160;:</term>
+ <listitem><simpara>The originator of the stanza. MAY BE NULL if the stanza originated from the user's server.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>iq</parameter>&#160;:</term>
+ <listitem><simpara>The full stanza received.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Returns</emphasis>&#160;:</term>
+ <listitem><simpara>TRUE if the plugin processed this stanza and *nobody else* should process it. FALSE otherwise.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="jabber-jabber-receiving-message" role="signal">
+ <title>The <literal>&quot;jabber-receiving-message&quot;</literal> signal</title>
+<programlisting>
+gboolean user_function (PurpleConnection *gc,
+ const char *type,
+ const char *id,
+ const char *from,
+ const char *to,
+ PurpleXmlNode *message,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when an XMPP message stanza is received. Allows a plugin to process message stanzas.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>gc</parameter>&#160;:</term>
+ <listitem><simpara>The connection on which the stanza is received.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>type</parameter>&#160;:</term>
+ <listitem><simpara>The message type (see rfc3921 or rfc3921bis).</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>id</parameter>&#160;:</term>
+ <listitem><simpara>The ID attribute from the stanza. MAY BE NULL.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>from</parameter>&#160;:</term>
+ <listitem><simpara>The originator of the stanza. MAY BE NULL if the stanza originated from the user's server.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>to</parameter>&#160;:</term>
+ <listitem><simpara>The destination of the stanza. This is probably either the full JID of the receiver or the receiver's bare JID.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>The full stanza received.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Returns</emphasis>&#160;:</term>
+ <listitem><simpara>TRUE if the plugin processed this stanza and *nobody else* should process it. FALSE otherwise.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="jabber-jabber-receiving-presence" role="signal">
+ <title>The <literal>&quot;jabber-receiving-presence&quot;</literal> signal</title>
+<programlisting>
+gboolean user_function (PurpleConnection *gc,
+ const char *type,
+ const char *from,
+ PurpleXmlNode *presence,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when an XMPP presence stanza is received. Allows a plugin to process presence stanzas.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>gc</parameter>&#160;:</term>
+ <listitem><simpara>The connection on which the stanza is received.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>type</parameter>&#160;:</term>
+ <listitem><simpara>The presence type (see rfc3921 or rfc3921bis). NULL indicates this is an "available" (i.e. online) presence.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>from</parameter>&#160;:</term>
+ <listitem><simpara>The originator of the stanza. MAY BE NULL if the stanza originated from the user's server.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>presence</parameter>&#160;:</term>
+ <listitem><simpara>The full stanza received.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Returns</emphasis>&#160;:</term>
+ <listitem><simpara>TRUE if the plugin processed this stanza and *nobody else* should process it. FALSE otherwise.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="jabber-jabber-watched-iq" role="signal">
+ <title>The <literal>&quot;jabber-watched-iq&quot;</literal> signal</title>
+<programlisting>
+gboolean user_function (PurpleConnection *gc,
+ const char *type,
+ const char *id,
+ const char *from,
+ PurpleXmlNode *child,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when an IQ with a watched (child, namespace) pair is received. See jabber-register-namespace-watcher and jabber-unregister-namespace-watcher.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>gc</parameter>&#160;:</term>
+ <listitem><simpara>The connection on which the stanza is received.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>type</parameter>&#160;:</term>
+ <listitem><simpara>The IQ type ('get', 'set', 'result', or 'error').</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>id</parameter>&#160;:</term>
+ <listitem><simpara>The ID attribute from the stanza. MUST NOT be NULL.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>from</parameter>&#160;:</term>
+ <listitem><simpara>The originator of the stanza. MAY BE NULL if the stanza originated from the user's server.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>child</parameter>&#160;:</term>
+ <listitem><simpara>The child node with namespace.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Returns</emphasis>&#160;:</term>
+ <listitem><simpara>TRUE if the plugin processed this stanza and *nobody else* should process it. FALSE otherwise.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="jabber-jabber-register-namespace-watcher" role="signal">
+ <title>The <literal>&quot;jabber-register-namespace-watcher&quot;</literal> signal</title>
+<programlisting>
+void user_function (const char *node,
+ const char *namespace,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emit this signal to register your desire to have specific IQ stanzas to be emitted via the jabber-watched-iq signal when received.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>node</parameter>&#160;:</term>
+ <listitem><simpara>The IQ child name to longer watch.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>namespace</parameter>&#160;:</term>
+ <listitem><simpara>The IQ child namespace to longer watch.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="jabber-jabber-unregister-namespace-watcher" role="signal">
+ <title>The <literal>&quot;jabber-unregister-namespace-watcher&quot;</literal> signal</title>
+<programlisting>
+void user_function (const char *node,
+ const char *namespace,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emit this signal to unregister your desire to have specific IQ stanzas to be emitted via the jabber-watched-iq signal when received.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>node</parameter>&#160;:</term>
+ <listitem><simpara>The IQ child name to no longer watch.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>namespace</parameter>&#160;:</term>
+ <listitem><simpara>The IQ child namespace to no longer watch.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="jabber-jabber-sending-xmlnode" role="signal">
+ <title>The <literal>&quot;jabber-sending-xmlnode&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleConnection *gc,
+ PurpleXmlNode **stanza,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emit this signal (<literal>purple_signal_emit</literal>) to send a stanza. It is preferred to use this instead of purple_protocol_server_iface_send_raw.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>gc</parameter>&#160;:</term>
+ <listitem><simpara>The connection on which to send the stanza.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>stanza</parameter>&#160;:</term>
+ <listitem><simpara>The stanza to send. If stanza is not NULL after being sent, the emitter should free it.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="jabber-jabber-receiving-xmlnode" role="signal">
+ <title>The <literal>&quot;jabber-receiving-xmlnode&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleConnection *gc,
+ PurpleXmlNode **stanza,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when an XMPP stanza is received. Allows a plugin to process any stanza.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>gc</parameter>&#160;:</term>
+ <listitem><simpara>The connection on which the stanza was received.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>stanza</parameter>&#160;:</term>
+ <listitem><simpara>The received stanza. Set stanza to NULL (and free it) to stop processing the stanza.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+</refsect1>
+
+</chapter>
diff --git a/doc/reference/libpurple/signals_log.xml b/doc/reference/libpurple/signals_log.xml
new file mode 100644
index 0000000000..9a7f1e9062
--- /dev/null
+++ b/doc/reference/libpurple/signals_log.xml
@@ -0,0 +1,58 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-signals-log">
+<title>Log signals</title>
+
+<refsect1 id="logs.signals" role="signal_proto">
+<title role="signal_proto.title">List of signals</title>
+<synopsis>
+ &quot;<link linkend="logs-log-timestamp">log-timestamp</link>&quot;
+</synopsis>
+</refsect1>
+
+<refsect1 id="logs.signal-details" role="signals">
+<title role="signals.title">Signal details</title>
+
+<refsect2 id="logs-log-timestamp" role="signal">
+ <title>The <literal>&quot;log-timestamp&quot;</literal> signal</title>
+<programlisting>
+char * user_function (PurpleLog *log,
+ time_t when,
+ gboolean show_date,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted to allow plugins to customize the timestamp on a message being logged.
+ </para>
+ <note><para>
+Plugins must be careful of logs with a type of PURPLE_LOG_SYSTEM.
+ </para></note>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>log</parameter>&#160;:</term>
+ <listitem><simpara>The log the message belongs to.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>when</parameter>&#160;:</term>
+ <listitem><simpara>The time to be converted to a string.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>show_date</parameter>&#160;:</term>
+ <listitem><simpara>Whether the date should be displayed.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Returns</emphasis>&#160;:</term>
+ <listitem><simpara>A textual representation of the time, or <literal>NULL</literal> to use a default format.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+</refsect1>
+
+</chapter>
diff --git a/doc/reference/libpurple/signals_notify.xml b/doc/reference/libpurple/signals_notify.xml
new file mode 100644
index 0000000000..26f9f65978
--- /dev/null
+++ b/doc/reference/libpurple/signals_notify.xml
@@ -0,0 +1,133 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-signals-notify">
+<title>Notify signals</title>
+
+<refsect1 id="notify.signals" role="signal_proto">
+<title role="signal_proto.title">List of signals</title>
+<synopsis>
+ &quot;<link linkend="notify-displaying-userinfo">displaying-userinfo</link>&quot;
+ &quot;<link linkend="notify-displaying-email-notification">displaying-email-notification</link>&quot;
+ &quot;<link linkend="notify-displaying-emails-notification">displaying-emails-notification</link>&quot;
+</synopsis>
+</refsect1>
+
+<refsect1 id="notify.signal-details" role="signals">
+<title role="signals.title">Signal details</title>
+
+<refsect2 id="notify-displaying-userinfo" role="signal">
+ <title>The <literal>&quot;displaying-userinfo&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ const char *who,
+ PurpleNotifyUserInfo *user_info,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted before userinfo is handed to the UI to display. <literal>user_info</literal> can be manipulated via the PurpleNotifyUserInfo API in notify.c.
+ </para>
+ <note><para>
+If adding a PurpleNotifyUserInfoEntry, be sure not to free it -- PurpleNotifyUserInfo assumes responsibility for its objects.
+ </para></note>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account on which the info was obtained.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>who</parameter>&#160;:</term>
+ <listitem><simpara>The name of the buddy whose info is to be displayed.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_info</parameter>&#160;:</term>
+ <listitem><simpara>The information to be displayed, as PurpleNotifyUserInfoEntry objects.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="notify-displaying-email-notification" role="signal">
+ <title>The <literal>&quot;displaying-email-notification&quot;</literal> signal</title>
+<programlisting>
+void user_function (const char *subject,
+ const char *from,
+ const char *to,
+ const char *url,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted before notification of a single email is handed to the UI to display.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>subject</parameter>&#160;:</term>
+ <listitem><simpara>Subject of email being notified of.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>from</parameter>&#160;:</term>
+ <listitem><simpara>Who the email is from.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>to</parameter>&#160;:</term>
+ <listitem><simpara>Who the email is to.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>url</parameter>&#160;:</term>
+ <listitem><simpara>A url to view the email.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="notify-displaying-emails-notification" role="signal">
+ <title>The <literal>&quot;displaying-emails-notification&quot;</literal> signal</title>
+<programlisting>
+void user_function (const char **subjects,
+ const char **froms,
+ const char **tos,
+ const char **urls,
+ guint count,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted before notification of multiple emails is handed to the UI to display.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>subjects</parameter>&#160;:</term>
+ <listitem><simpara>Subjects of emails being notified of.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>froms</parameter>&#160;:</term>
+ <listitem><simpara>Who the emails are from.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>tos</parameter>&#160;:</term>
+ <listitem><simpara>Who the emails are to.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>urls</parameter>&#160;:</term>
+ <listitem><simpara>The urls to view the emails.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>count</parameter>&#160;:</term>
+ <listitem><simpara>Number of emails being notified of.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+</refsect1>
+
+</chapter>
diff --git a/doc/reference/libpurple/signals_plugin.xml b/doc/reference/libpurple/signals_plugin.xml
new file mode 100644
index 0000000000..d5fe3cb90b
--- /dev/null
+++ b/doc/reference/libpurple/signals_plugin.xml
@@ -0,0 +1,63 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-signals-plugin">
+<title>Plugin signals</title>
+
+<refsect1 id="plugins.signals" role="signal_proto">
+<title role="signal_proto.title">List of signals</title>
+<synopsis>
+ &quot;<link linkend="plugins-plugin-load">plugin-load</link>&quot;
+ &quot;<link linkend="plugins-plugin-unload">plugin-unload</link>&quot;
+</synopsis>
+</refsect1>
+
+<refsect1 id="plugins.signal-details" role="signals">
+<title role="signals.title">Signal details</title>
+
+<refsect2 id="plugins-plugin-load" role="signal">
+ <title>The <literal>&quot;plugin-load&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurplePlugin *plugin,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a plugin is loaded.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>plugin</parameter>&#160;:</term>
+ <listitem><simpara>The plugin that was loaded.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="plugins-plugin-unload" role="signal">
+ <title>The <literal>&quot;plugin-unload&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurplePlugin *plugin,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a plugin is unloaded.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>plugin</parameter>&#160;:</term>
+ <listitem><simpara>The plugin that was unloaded.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+</refsect1>
+
+</chapter>
diff --git a/doc/reference/libpurple/signals_savedstatus.xml b/doc/reference/libpurple/signals_savedstatus.xml
new file mode 100644
index 0000000000..abc0e12f63
--- /dev/null
+++ b/doc/reference/libpurple/signals_savedstatus.xml
@@ -0,0 +1,32 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-signals-savedstatus">
+<title>Saved Status signals</title>
+
+<refsect1 id="savedstatuses.signals" role="signal_proto">
+<title role="signal_proto.title">List of signals</title>
+<synopsis>
+ &quot;<link linkend="savedstatuses-savedstatus-changed">savedstatus-changed</link>&quot;
+</synopsis>
+</refsect1>
+
+<refsect1 id="savedstatuses.signal-details" role="signals">
+<title role="signals.title">Signal details</title>
+
+<refsect2 id="savedstatuses-savedstatus-changed" role="signal">
+ <title>The <literal>&quot;savedstatus-changed&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleSavedStatus *new,
+ PurpleSavedStatus *old,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a new saved status is activated.
+ </para>
+</refsect2>
+
+</refsect1>
+
+</chapter>
diff --git a/doc/reference/libpurple/signals_sound.xml b/doc/reference/libpurple/signals_sound.xml
new file mode 100644
index 0000000000..94c11ba4ce
--- /dev/null
+++ b/doc/reference/libpurple/signals_sound.xml
@@ -0,0 +1,50 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-signals-sound">
+<title>Sound signals</title>
+
+<refsect1 id="sound.signals" role="signal_proto">
+<title role="signal_proto.title">List of signals</title>
+<synopsis>
+ &quot;<link linkend="sound-playing-sound-event">playing-sound-event</link>&quot;
+</synopsis>
+</refsect1>
+
+<refsect1 id="sound.signal-details" role="signals">
+<title role="signals.title">Signal details</title>
+
+<refsect2 id="sound-playing-sound-event" role="signal">
+ <title>The <literal>&quot;playing-sound-event&quot;</literal> signal</title>
+<programlisting>
+gboolean user_function (PurpleSoundEventID event,
+ PurpleAccount *account,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when libpurple is going to play a sound event. This can be used to cancel playing sound by returning TRUE.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>event</parameter>&#160;:</term>
+ <listitem><simpara>The event this sound represents.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account the sound is being played for.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Returns</emphasis>&#160;:</term>
+ <listitem><simpara><literal>TRUE</literal> if the sound should not be played, or <literal>FALSE</literal> otherwise.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+</refsect1>
+
+</chapter>
diff --git a/doc/reference/libpurple/signals_xfer.xml b/doc/reference/libpurple/signals_xfer.xml
new file mode 100644
index 0000000000..323d936729
--- /dev/null
+++ b/doc/reference/libpurple/signals_xfer.xml
@@ -0,0 +1,217 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-signals-xfer">
+<title>File Transfer signals</title>
+
+<refsect1 id="xfers.signals" role="signal_proto">
+<title role="signal_proto.title">List of signals</title>
+<synopsis>
+ &quot;<link linkend="xfers-file-recv-accept">file-recv-accept</link>&quot;
+ &quot;<link linkend="xfers-file-recv-start">file-recv-start</link>&quot;
+ &quot;<link linkend="xfers-file-recv-cancel">file-recv-cancel</link>&quot;
+ &quot;<link linkend="xfers-file-recv-complete">file-recv-complete</link>&quot;
+ &quot;<link linkend="xfers-file-recv-request">file-recv-request</link>&quot;
+ &quot;<link linkend="xfers-file-send-accept">file-send-accept</link>&quot;
+ &quot;<link linkend="xfers-file-send-start">file-send-start</link>&quot;
+ &quot;<link linkend="xfers-file-send-cancel">file-send-cancel</link>&quot;
+ &quot;<link linkend="xfers-file-send-complete">file-send-complete</link>&quot;
+</synopsis>
+</refsect1>
+
+<refsect1 id="xfers.signal-details" role="signals">
+<title role="signals.title">Signal details</title>
+
+<refsect2 id="xfers-file-recv-accept" role="signal">
+ <title>The <literal>&quot;file-recv-accept&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleXfer *xfer,
+ gpointer data)
+</programlisting>
+ <para>
+Emitted when an incoming file transfer has been accepted.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>xfer</parameter>&#160;:</term>
+ <listitem><simpara>The file transfer.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>data</parameter>&#160;:</term>
+ <listitem><simpara>User data.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="xfers-file-recv-start" role="signal">
+ <title>The <literal>&quot;file-recv-start&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleXfer *xfer,
+ gpointer data)
+</programlisting>
+ <para>
+Emitted when an incoming file transfer has been started.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>xfer</parameter>&#160;:</term>
+ <listitem><simpara>The file transfer.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>data</parameter>&#160;:</term>
+ <listitem><simpara>User data.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="xfers-file-recv-cancel" role="signal">
+ <title>The <literal>&quot;file-recv-cancel&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleXfer *xfer,
+ gpointer data)
+</programlisting>
+ <para>
+Emitted when an incoming file transfer has been canceled.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>xfer</parameter>&#160;:</term>
+ <listitem><simpara>The file transfer.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>data</parameter>&#160;:</term>
+ <listitem><simpara>User data.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="xfers-file-recv-complete" role="signal">
+ <title>The <literal>&quot;file-recv-complete&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleXfer *xfer,
+ gpointer data)
+</programlisting>
+ <para>
+Emitted when an incoming file transfer has been completed.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>xfer</parameter>&#160;:</term>
+ <listitem><simpara>The file transfer.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>data</parameter>&#160;:</term>
+ <listitem><simpara>User data.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="xfers-file-recv-request" role="signal">
+ <title>The <literal>&quot;file-recv-request&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleXfer *xfer,
+ gpointer data)
+</programlisting>
+ <para>
+Emitted before the user is prompted for an incoming file-transfer. Plugins can intercept the signal to auto-accept/auto-reject the requests. To auto-accept the file transfer, use purple_xfer_request_accepted(). To auto-reject, set the status of the xfer to PURPLE_XFER_STATUS_CANCEL_LOCAL.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>xfer</parameter>&#160;:</term>
+ <listitem><simpara>The file transfer.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>data</parameter>&#160;:</term>
+ <listitem><simpara>User data.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="xfers-file-send-accept" role="signal">
+ <title>The <literal>&quot;file-send-accept&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleXfer *xfer,
+ gpointer data)
+</programlisting>
+ <para>
+Emitted when an outgoing file transfer has been accepted.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>xfer</parameter>&#160;:</term>
+ <listitem><simpara>The file transfer.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>data</parameter>&#160;:</term>
+ <listitem><simpara>User data.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="xfers-file-send-start" role="signal">
+ <title>The <literal>&quot;file-send-start&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleXfer *xfer,
+ gpointer data)
+</programlisting>
+ <para>
+Emitted when an outgoing file transfer has started.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>xfer</parameter>&#160;:</term>
+ <listitem><simpara>The file transfer.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>data</parameter>&#160;:</term>
+ <listitem><simpara>User data.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="xfers-file-send-cancel" role="signal">
+ <title>The <literal>&quot;file-send-cancel&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleXfer *xfer,
+ gpointer data)
+</programlisting>
+ <para>
+Emitted when an outgoing file transfer has been canceled.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>xfer</parameter>&#160;:</term>
+ <listitem><simpara>The file transfer.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>data</parameter>&#160;:</term>
+ <listitem><simpara>User data.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="xfers-file-send-complete" role="signal">
+ <title>The <literal>&quot;file-send-complete&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleXfer *xfer,
+ gpointer data)
+</programlisting>
+ <para>
+Emitted when an outgoing file transfer has been completed.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>xfer</parameter>&#160;:</term>
+ <listitem><simpara>The file transfer.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>data</parameter>&#160;:</term>
+ <listitem><simpara>User data.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+</refsect1>
+
+</chapter>
diff --git a/doc/C-HOWTO.dox b/doc/reference/libpurple/tut_c_plugins.xml
index 0c8774fc5c..c9d3cd88ee 100644
--- a/doc/C-HOWTO.dox
+++ b/doc/reference/libpurple/tut_c_plugins.xml
@@ -1,38 +1,53 @@
-/** @page c-howto C Plugin HOWTO
-
- @section Introduction
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-tut-c-plugins">
+ <title>C Plugins tutorial</title>
+
+ <sect2 id="tut-c-plugins-introduction">
+ <title>Introduction</title>
+ <para>
C plugins are native plugins. They have complete access to all of the API,
- and can do basically whatever they want. All of the protocol plugins, as
- well as the Mono, Perl, and Tcl loader plugins are written in C.
-
- @section getting_started Getting Started
+ and can do basically whatever they want. All of the protocol plugins are
+ also written in C.
+ </para>
+ </sect2>
+
+ <sect2 id="tut-c-plugins-getting-started">
+ <title>Getting Started</title>
+ <para>
To develop a plugin you need to have the libpurple and (for UI plugins) the
Pidgin/Finch source code or development headers. It is generally a good idea
to compile against the same version of Pidgin that you are running. You may
- also want to develop against the code in our Monotone repository if you need
- to use a new feature. Please do not abuse our Monotone repository, however.
-
- All plugins must have @c PURPLE_PLUGINS defined and the definition must be
- before including any libpurple, Pidgin, or Finch header files. Failure to do
- so can lead to strange errors that are hard to diagnose. Just don't forget!
-
- @section hello_world Hello World!
+ also want to develop against the code in our Mercurial repository if you need
+ to use a new feature. Please do not abuse our Mercurial repository, however.
+ </para>
+
+ <para>
+ All plugins must have <literal>PURPLE_PLUGINS</literal> defined and the
+ definition must be before including any libpurple, Pidgin, or Finch header
+ files. Failure to do so can lead to strange errors that are hard to diagnose.
+ Including <literal>purple.h</literal> will define this for you.
+ </para>
+ </sect2>
+
+ <sect2 id="tut-c-plugins-hello-world">
+ <title>An Example</title>
+ <para>
I know every tutorial has a hello world, so why should libpurple be any
different?
- @code
-#define PURPLE_PLUGINS
-
-#include <glib.h>
-
-#include "notify.h"
-#include "plugin.h"
-#include "version.h"
+<example>
+<title>Hello World!</title>
+<programlisting>
+#include &lt;purple.h&gt;
static gboolean
plugin_load(PurplePlugin *plugin) {
purple_notify_message(plugin, PURPLE_NOTIFY_MSG_INFO, "Hello World!",
- "This is the Hello World! plugin :)", NULL, NULL, NULL);
+ "This is the Hello World! plugin :)",
+ NULL, NULL, NULL, NULL);
return TRUE;
}
@@ -76,31 +91,32 @@ init_plugin(PurplePlugin *plugin)
}
PURPLE_INIT_PLUGIN(hello_world, init_plugin, info);
-
- @endcode
-
- Okay, so what does all this mean? We start off by defining @c PURPLE_PLUGINS
- like described before. Next we include glib.h, mainly for gboolean and the
- glib wrappers of the standard C types.
-
- Next, we include plugin.h which has all the plugin specific stuff that we
- need. For example: @c PurplePlugin, @c PurplePluginInfo,
- @c PURPLE_PLUGIN_MAGIC, and @c PURPLE_INIT_PLUGIN().
-
- Our last include is version.h which defines @c PURPLE_MAJOR_VERSION, and
- @c PURPLE_MINOR_VERSION. There is not much you need to know about these,
- except that they are required and will stop your plugin from crashing Pidgin
- when something has changed that your plugin does not know about yet.
-
- @c plugin_load is not required. It is called when the plugin is loaded so
- that you can initialize any variables and so on. In this plugin we'll just
- use it to display a message.
-
- Next we have the @c PurplePluginInfo structure. Every plugin MUST have one of
- these. Below is a code snipet of the same struct used in @c hello_world with
- comments describing what each is.
-
- @code
+</programlisting>
+</example>
+ </para>
+
+ <para>
+ Okay, so what does all this mean? We start off by including purple.h. This
+ file defines <literal>PURPLE_PLUGINS</literal> as described before so that we
+ don't have to manually define it. It also includes all the libpurple header
+ files.
+ </para>
+
+ <para>
+ <literal>plugin_load</literal> is not required. It is called when the plugin
+ is loaded so that you can initialize any variables and so on. In this plugin
+ we'll just use it to display a message.
+ </para>
+
+ <para>
+ Next we have the <literal>PurplePluginInfo</literal> structure. Every plugin
+ <emphasis>MUST</emphasis> have one of these. Below is a code snipet of the
+ same struct used in <literal>hello_world</literal> with comments describing
+ what each is.
+ </para>
+
+ <para>
+<programlisting>
static PurplePluginInfo info = {
PURPLE_PLUGIN_MAGIC, /* Plugin magic, this must always be
PURPLE_PLUGIN_MAGIC.
@@ -257,16 +273,21 @@ static PurplePluginInfo info = {
need it.
*/
};
- @endcode
+</programlisting>
+ </para>
- Finally we have @c init_plugin and @c PURPLE_INIT_PLUGIN. @c init_plugin is
+ <para>
+ Finally we have <literal>init_plugin</literal> and
+ <literal>PURPLE_INIT_PLUGIN</literal>. <literal>init_plugin</literal> is
a function that gets called when libpurple probes the plugin. Most plugins
will add their preferences to the pref tree here--more about that later.
- @c PURPLE_INIT_PLUGIN is a macro that EVERY plugin MUST have.
- @c PURPLE_INIT_PLUGIN tells libpurple some very basic things about your
- plugin, like what name to use if the plugin is compiled staticly, the
- @c init_plugin function, and the name of the PurplePluginInfo structure. As
- you may have guessed, this also gets read when libpurple is probing your
- plugin. If this is missing, the plugin will not load.
- */
-// vim: syntax=c.doxygen
+ <literal>PURPLE_INIT_PLUGIN</literal> is a macro that EVERY plugin MUST have.
+ <literal>PURPLE_INIT_PLUGIN</literal> tells libpurple some very basic things
+ about your plugin, like what name to use if the plugin is compiled staticly,
+ the <literal>init_plugin</literal> function, and the name of the
+ <literal>PurplePluginInfo</literal> structure. As you may have guessed,
+ this also gets read when libpurple is probing your plugin. If this is
+ missing, the plugin will not load.
+ </para>
+ </sect2>
+</chapter>
diff --git a/doc/reference/libpurple/tut_signals.xml b/doc/reference/libpurple/tut_signals.xml
new file mode 100644
index 0000000000..2e2a96e036
--- /dev/null
+++ b/doc/reference/libpurple/tut_signals.xml
@@ -0,0 +1,203 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-tut-signals">
+ <title>Signals tutorial</title>
+
+ <sect2 id="tut-signals-introduction">
+ <title>Introduction</title>
+ <para>
+ The libpurple signals interface is used for general event notification, such
+ as plugins being loaded or unloaded, allowing the GUI frontend to respond
+ appropriately to changing internal data. Unfortunately, its use is not at all
+ obvious from the information in the header files. This document uses code
+ snippets from the Pidgin/libpurple plugin systems to illustrate the proper
+ use of signals.
+ </para>
+ </sect2>
+
+ <sect2 id="tut-signals-overview">
+ <title>Overview of Purple-signals</title>
+ <para>
+ Signals in libpurple are very similar to those in GTK+. When certain events
+ happen, a named signal is "emitted" from a certain object. Emitting the
+ signal triggers a series of callbacks that have been "connected" to that
+ signal for that object. These callbacks take appropriate action in response
+ to the signal.
+ </para>
+ </sect2>
+
+ <sect2 id="tut-signals-registering">
+ <title>Registering a Signal</title>
+ <para>
+ The first step of using a signal is registering it with libpurple so that
+ callbacks may be connected to it. This is done using
+ <link linkend="purple-signal-register"><function>purple_signal_register()</function></link>.
+ Here is a slightly modified example from
+ <link linkend="purple-plugins-init"><function>purple_plugins_init()</function></link>
+ in <literal>plugin.c</literal>:
+
+<programlisting>
+purple_signal_register(purple_plugins_get_handle(), /* Instance */
+ "plugin-load", /* Signal name */
+ purple_marshal_VOID__POINTER,/* Marshal function */
+ G_TYPE_NONE, /* Callback return type */
+ 1, /* Number of callback arguments (not including void *data) */
+ PURPLE_TYPE_PLUGIN /* Type of first callback argument */
+);
+</programlisting>
+ </para>
+
+ <sect3>
+ <title>Instance</title>
+ <para>
+ A reference to the object from which this signal is emitted, and to which
+ potential callbacks should be connected. In this case, it will be the entire
+ plugin module emitting the signal.
+ </para>
+ </sect3>
+
+ <sect3>
+ <title>Signal Name</title>
+ <para>
+ Unique identifier for the signal itself.
+ </para>
+ </sect3>
+
+ <sect3>
+ <title>Callback function definition</title>
+ <para>
+ The rest of the arguments specify the form of the callback function.
+
+ <itemizedlist>
+ <listitem><para><emphasis>Marshal function</emphasis></para><para>
+ <literal>purple_marshal_VOID__POINTER</literal> represents the callback
+ function prototype, not including a "data" argument, explained later. The form
+ is <literal>purple_marshal_RETURNVALUETYPE__ARG1TYPE_ARG2TYPE_ETC</literal>.
+ See <link linkend="libpurple-signals">signals.h</link> for more possible types.
+ </para>
+
+ <para>
+ In this case, the callback will have the form
+<programlisting>
+void cb(void *arg1, void *data)
+</programlisting>
+ </para>
+
+ <para>
+ If <literal>purple_marshal_BOOLEAN__POINTER_POINTER_POINTER</literal> were
+ specified, it would be:
+<programlisting>
+gboolean cb(void *arg1, void *arg2, void *arg3, void *data)
+</programlisting>
+
+ The <literal>void *data</literal> argument at the end of each callback function
+ provides the data argument given to
+ <link linkend="purple-signal-connect"><function>purple_signal_connect()</function></link>.
+ </para></listitem>
+
+ <listitem><para><emphasis>Callback return type</emphasis></para><para>
+ In our case, this is G_TYPE_NONE, meaning "returns void".
+<!-- TODO This could be described better. -->
+ </para></listitem>
+
+ <listitem><para><emphasis>Number of callback arguments</emphasis></para><para>
+ The number of arguments (not including <literal>data</literal>) that the callback function
+ will take.
+ </para></listitem>
+
+ <listitem><para><emphasis>Type of argument</emphasis></para><para>
+ <literal>PURPLE_TYPE_PLUGIN</literal> specifies that the first argument given to the callback
+ will be a <literal>PurplePlugin*</literal>. You will need as many "type of argument"
+ arguments to
+ <link linkend="purple-signal-register"><function>purple_signal_register()</function></link>
+ as you specified in
+ "Number of arguments" above.
+
+<!-- TODO Describe this more. -->
+ </para></listitem>
+ </itemizedlist>
+ </para>
+ </sect3>
+ </sect2>
+
+ <sect2 id="tut-signals-connect">
+ <title>Connecting to the signal</title>
+ <para>
+ Once the signal is registered, you can connect callbacks to it. First, you
+ must define a callback function, such as this one from gtkplugin.c :
+<programlisting>
+static void plugin_load_cb(PurplePlugin *plugin, gpointer data)
+{
+ GtkTreeView *view = (GtkTreeView *)data;
+ plugin_loading_common(plugin, view, TRUE);
+}
+</programlisting>
+ Note that the callback function prototype matches that specified in the call
+ to <link linkend="purple-signal-register"><function>purple_signal_register()</function></link>
+ above.
+ </para>
+
+ <para>
+ Once the callback function is defined, you can connect it to the signal.
+ Again from gtkplugin.c , in <function>pidgin_plugin_dialog_show()</function>:
+<programlisting>
+purple_signal_connect(purple_plugins_get_handle(), "plugin-load", /* What to connect to */
+ plugin_dialog, /* Object receiving the signal */
+ PURPLE_CALLBACK(plugin_load_cb), /* Callback function */
+ event_view, /* Data to pass to the callback function
+);
+</programlisting>
+ </para>
+
+ <para>
+ The first two arguments ("What to connect to") specify the object emitting
+ the signal (the plugin module) and what signal to listen for ("plugin-load").
+ </para>
+
+ <para>
+ The object receiving the signal is <literal>plugin_dialog</literal> , the Pidgin plugins
+ dialog. When <literal>plugin_dialog</literal> is deleted, then
+ <literal>purple_signals_disconnect_by_handle(plugin_dialog)</literal> should be called to
+ remove all signal connections it is associated with.
+ </para>
+
+ <para>
+ The callback function is given using a helper macro, and finally the
+ <literal>data</literal> argument to be passed to
+ <literal>plugin_load_cb</literal> is given as <literal>event_view</literal>,
+ a pointer to the GTK widget that <literal>plugin_load_cb</literal> needs to update.
+ </para>
+ </sect2>
+
+ <sect2 id="tut-signals-emit-signal">
+ <title>Emitting a signal</title>
+ <para>
+ Connecting callbacks to signals is all well and good, but how do you "fire"
+ the signal and trigger the callback? At some point, you must "emit" the
+ signal, which immediately calls all connected callbacks.
+ </para>
+
+ <para>
+ As seen in <link linkend="purple-plugin-load"><function>purple_plugin_load()</function></link>
+ in plugin.c:
+<programlisting>
+purple_signal_emit(purple_plugins_get_handle(), "plugin-load", plugin);
+</programlisting>
+ This causes the signal "plugin-load" to be emitted from the plugin module
+ (given by
+ <link linkend="purple-plugins-get-handle"><function>purple_plugins_get_handle()</function></link>),
+ with the newly loaded plugin as
+ the argument to pass to any registered callback functions.
+ </para>
+
+ <para>
+ In our example, <literal>plugin_load_cb</literal> is called immediately as
+<programlisting>
+plugin_load_cb(plugin, event_view);
+</programlisting>
+ and does whatever it does.
+ </para>
+ </sect2>
+</chapter>
diff --git a/doc/reference/libpurple/ui_ops.xml b/doc/reference/libpurple/ui_ops.xml
new file mode 100644
index 0000000000..06e23edcf1
--- /dev/null
+++ b/doc/reference/libpurple/ui_ops.xml
@@ -0,0 +1,32 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-ui-ops">
+ <title>List of <literal>UiOps</literal> Structures</title>
+
+ <para>
+ When implementing a UI for libpurple, you need to fill in various UiOps
+ structures:
+
+ <itemizedlist>
+<listitem><link linkend="PurpleAccountUiOps"><literal>PurpleAccountUiOps</literal></link></listitem>
+<listitem><link linkend="PurpleBlistUiOps"><literal>PurpleBlistUiOps</literal></link></listitem>
+<listitem><link linkend="PurpleConnectionUiOps"><literal>PurpleConnectionUiOps</literal></link></listitem>
+<listitem><link linkend="PurpleConversationUiOps"><literal>PurpleConversationUiOps</literal></link></listitem>
+<listitem><link linkend="PurpleCoreUiOps"><literal>PurpleCoreUiOps</literal></link></listitem>
+<listitem><link linkend="PurpleDebugUiOps"><literal>PurpleDebugUiOps</literal></link></listitem>
+<listitem><link linkend="PurpleDnsQueryUiOps"><literal>PurpleDnsQueryUiOps</literal></link></listitem>
+<listitem><link linkend="PurpleEventLoopUiOps"><literal>PurpleEventLoopUiOps</literal></link> (without this, nothing will work and you will cry)</listitem>
+<listitem><link linkend="PurpleIdleUiOps"><literal>PurpleIdleUiOps</literal></link></listitem>
+<listitem><link linkend="PurpleNotifyUiOps"><literal>PurpleNotifyUiOps</literal></link></listitem>
+<listitem><link linkend="PurpleRequestUiOps"><literal>PurpleRequestUiOps</literal></link></listitem>
+<listitem><link linkend="PurpleRoomlistUiOps"><literal>PurpleRoomlistUiOps</literal></link></listitem>
+<listitem><link linkend="PurpleSoundUiOps"><literal>PurpleSoundUiOps</literal></link></listitem>
+<listitem><link linkend="PurpleSrvTxtQueryUiOps"><literal>PurpleSrvTxtQueryUiOps</literal></link></listitem>
+<listitem><link linkend="PurpleWhiteboardUiOps"><literal>PurpleWhiteboardUiOps</literal></link></listitem>
+<listitem><link linkend="PurpleXferUiOps"><literal>PurpleXferUiOps</literal></link></listitem>
+ </itemizedlist>
+ </para>
+
+</chapter>
diff --git a/doc/reference/libpurple/version.xml.in b/doc/reference/libpurple/version.xml.in
new file mode 100644
index 0000000000..405528f9af
--- /dev/null
+++ b/doc/reference/libpurple/version.xml.in
@@ -0,0 +1 @@
+@PURPLE_VERSION@
diff --git a/doc/reference/pidgin/Makefile.am b/doc/reference/pidgin/Makefile.am
new file mode 100644
index 0000000000..837cc49298
--- /dev/null
+++ b/doc/reference/pidgin/Makefile.am
@@ -0,0 +1,148 @@
+## Process this file with automake to produce Makefile.in
+
+# We require automake 1.6 at least.
+AUTOMAKE_OPTIONS = 1.6
+
+# This is a blank Makefile.am for using gtk-doc.
+# Copy this to your project's API docs directory and modify the variables to
+# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples
+# of using the various options.
+
+# The name of the module, e.g. 'glib'.
+DOC_MODULE=pidgin
+
+# Uncomment for versioned docs and specify the version of the module, e.g. '2'.
+#DOC_MODULE_VERSION=2
+
+# The top-level XML file (SGML in the past). You can change this if you want to.
+DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml
+
+# The directory containing the source code. Relative to $(srcdir).
+# gtk-doc will search all .c & .h files beneath here for inline comments
+# documenting the functions and macros.
+# e.g. DOC_SOURCE_DIR=../../../gtk
+DOC_SOURCE_DIR=$(top_srcdir)/$(DOC_MODULE)
+
+# Extra options to pass to gtkdoc-scangobj. Not normally needed.
+SCANGOBJ_OPTIONS=
+
+# Extra options to supply to gtkdoc-scan.
+# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED"
+SCAN_OPTIONS=\
+ --deprecated-guards="PURPLE_DISABLE_DEPRECATED|PIDGIN_DISABLE_DEPRECATED" \
+ --rebuild-types \
+ --rebuild-sections
+
+# Extra options to supply to gtkdoc-mkdb.
+# e.g. MKDB_OPTIONS=--xml-mode --output-format=xml
+MKDB_OPTIONS=--xml-mode --output-format=xml
+
+# Extra options to supply to gtkdoc-mktmpl
+# e.g. MKTMPL_OPTIONS=--only-section-tmpl
+MKTMPL_OPTIONS=
+
+# Extra options to supply to gtkdoc-mkhtml
+MKHTML_OPTIONS=
+
+# Extra options to supply to gtkdoc-fixref. Not normally needed.
+# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html
+FIXXREF_OPTIONS=
+
+# Used for dependencies. The docs will be rebuilt if any of these change.
+# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h
+# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c
+HFILE_GLOB=$(top_srcdir)/$(DOC_MODULE)/*.h
+CFILE_GLOB=$(top_srcdir)/$(DOC_MODULE)/*.c
+
+# Extra header to include when scanning, which are not under DOC_SOURCE_DIR
+# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h
+EXTRA_HFILES=
+
+# Header files or dirs to ignore when scanning. Use base file/dir names
+# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h private_code
+IGNORE_HFILES=\
+ pixmaps \
+ plugins \
+ themes \
+ win32 \
+ getopt.h \
+ gtk3compat.h \
+ gtkinternal.h
+
+# Images to copy into HTML directory.
+# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
+HTML_IMAGES=
+
+# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
+# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
+content_files = version.xml \
+ signals_gtkaccount.xml \
+ signals_gtkblist.xml \
+ signals_gtkconv.xml \
+ signals_gtklog.xml
+
+# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
+# These files must be listed here *and* in content_files
+# e.g. expand_content_files=running.sgml
+expand_content_files=
+
+# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library.
+# Only needed if you are using gtkdoc-scangobj to dynamically query widget
+# signals and properties.
+# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
+# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
+GTKDOC_CFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libpurple \
+ -I$(top_srcdir)/pidgin \
+ $(GLIB_CFLAGS) \
+ $(GCR_CFLAGS) \
+ $(GSTREAMER_CFLAGS) \
+ $(GSTVIDEO_CFLAGS) \
+ $(GSTINTERFACES_CFLAGS) \
+ $(DEBUG_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(X11_CFLAGS) \
+ $(DBUS_CFLAGS) \
+ $(LIBXML_CFLAGS) \
+ $(WEBKIT_CFLAGS)
+
+GTKDOC_LIBS = \
+ $(top_builddir)/pidgin/libpidgin.la \
+ $(GLIB_LIBS) \
+ $(GCR_LIBS) \
+ $(DBUS_LIBS) \
+ $(GSTREAMER_LIBS) \
+ $(GSTVIDEO_LIBS) \
+ $(GSTINTERFACES_LIBS) \
+ $(XSS_LIBS) \
+ $(SM_LIBS) \
+ $(INTLLIBS) \
+ $(LIBXML_LIBS) \
+ $(WEBKIT_LIBS) \
+ $(GTK_LIBS) \
+ $(X11_LIBS)
+
+# This includes the standard gtk-doc make rules, copied by gtkdocize.
+include $(top_srcdir)/gtk-doc.make
+
+# Other files to distribute
+# e.g. EXTRA_DIST += version.xml.in
+EXTRA_DIST += version.xml.in
+
+# Files not to distribute
+# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types
+# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt
+DISTCLEANFILES = $(DOC_MODULE).types $(DOC_MODULE)-sections.txt
+
+dist-hook: html-build.stamp
+
+# Comment this out if you want 'make check' to test you doc status
+# and run some sanity checks
+if ENABLE_GTK_DOC
+TESTS_ENVIRONMENT = \
+ DOC_MODULE=$(DOC_MODULE) DOC_MAIN_SGML_FILE=$(DOC_MAIN_SGML_FILE) \
+ SRCDIR=$(abs_srcdir)
+#TODO: fix and enable
+#TESTS = $(GTKDOC_CHECK)
+endif
diff --git a/doc/reference/pidgin/pidgin-docs.xml b/doc/reference/pidgin/pidgin-docs.xml
new file mode 100644
index 0000000000..fa53a6f3e1
--- /dev/null
+++ b/doc/reference/pidgin/pidgin-docs.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+
+<!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
+<!ENTITY version SYSTEM "version.xml">
+]>
+<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude">
+ <bookinfo>
+ <title>Pidgin Reference Manual</title>
+ <abstract>
+ <title>Pidgin &version;</title>
+ <para>
+ Pidgin is a GTK+ frontend to libpurple.
+ </para>
+ </abstract>
+ </bookinfo>
+
+ <reference label="I">
+ <title>API Reference</title>
+
+ <xi:include href="xml/gtkaccount.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/gtkcertmgr.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/gtkdebug.xml" />
+ <xi:include href="xml/gtkdocklet.xml" />
+ <xi:include href="xml/gtkdnd-hints.xml" />
+ <xi:include href="xml/gtkeventloop.xml" />
+ <xi:include href="xml/gtkxfer.xml" />
+ <xi:include href="xml/gtkicon-theme.xml" />
+ <xi:include href="xml/gtkidle.xml" />
+ <xi:include href="xml/gtklog.xml" />
+ <xi:include href="xml/gtkmedia.xml" />
+ <xi:include href="xml/minidialog.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/gtkprefs.xml" />
+ <xi:include href="xml/gtkprivacy.xml" />
+ <xi:include href="xml/gtkrequest.xml" />
+ <xi:include href="xml/gtkroomlist.xml" />
+ <xi:include href="xml/gtksavedstatuses.xml" />
+ <xi:include href="xml/gtkscrollbook.xml" />
+ <xi:include href="xml/gtksmiley-manager.xml" />
+ <xi:include href="xml/gtksmiley-theme.xml" />
+ <xi:include href="xml/gtksound.xml" />
+ <xi:include href="xml/gtkstatusbox.xml" />
+ <xi:include href="xml/pidginstock.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/gtksession.xml" />
+ <xi:include href="xml/pidgin.xml" />
+ </reference>
+
+ <reference label="II">
+ <title>Purple-signals Reference</title>
+
+ <xi:include href="signals_gtkaccount.xml" />
+ <xi:include href="signals_gtkblist.xml" />
+ <xi:include href="signals_gtkconv.xml" />
+ <xi:include href="signals_gtklog.xml" />
+ </reference>
+
+ <index id="api-index-full">
+ <title>Index</title>
+ <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+ </index>
+ <index id="api-index-deprecated" role="deprecated">
+ <title>Index of deprecated symbols</title>
+ <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
+ </index>
+
+ <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
+</book>
diff --git a/doc/reference/pidgin/signals_gtkaccount.xml b/doc/reference/pidgin/signals_gtkaccount.xml
new file mode 100644
index 0000000000..fb0fc22a93
--- /dev/null
+++ b/doc/reference/pidgin/signals_gtkaccount.xml
@@ -0,0 +1,41 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-signals-gtkaccount">
+<title>Account signals</title>
+
+<refsect1 id="gtkaccounts.signals" role="signal_proto">
+<title role="signal_proto.title">List of signals</title>
+<synopsis>
+ &quot;<link linkend="gtkaccounts-account-modified">account-modified</link>&quot;
+</synopsis>
+</refsect1>
+
+<refsect1 id="gtkaccounts.signal-details" role="signals">
+<title role="signals.title">Signal details</title>
+
+<refsect2 id="gtkaccounts-account-modified" role="signal">
+ <title>The <literal>&quot;account-modified&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when the settings for an account have been changed and saved.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account that has been modified.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+</refsect1>
+
+</chapter>
diff --git a/doc/reference/pidgin/signals_gtkblist.xml b/doc/reference/pidgin/signals_gtkblist.xml
new file mode 100644
index 0000000000..100cdf8b9b
--- /dev/null
+++ b/doc/reference/pidgin/signals_gtkblist.xml
@@ -0,0 +1,146 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-signals-gtkblist">
+<title>Buddy List signals</title>
+
+<refsect1 id="gtkblist.signals" role="signal_proto">
+<title role="signal_proto.title">List of signals</title>
+<synopsis>
+ &quot;<link linkend="gtkblist-gtkblist-hiding">gtkblist-hiding</link>&quot;
+ &quot;<link linkend="gtkblist-gtkblist-unhiding">gtkblist-unhiding</link>&quot;
+ &quot;<link linkend="gtkblist-gtkblist-created">gtkblist-created</link>&quot;
+ &quot;<link linkend="gtkblist-drawing-tooltip">drawing-tooltip</link>&quot;
+ &quot;<link linkend="gtkblist-drawing-buddy">drawing-buddy</link>&quot;
+</synopsis>
+</refsect1>
+
+<refsect1 id="gtkblist.signal-details" role="signals">
+<title role="signals.title">Signal details</title>
+
+<refsect2 id="gtkblist-gtkblist-hiding" role="signal">
+ <title>The <literal>&quot;gtkblist-hiding&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleBuddyList *blist,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when the buddy list is about to be hidden.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>blist</parameter>&#160;:</term>
+ <listitem><simpara>The buddy list.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="gtkblist-gtkblist-unhiding" role="signal">
+ <title>The <literal>&quot;gtkblist-unhiding&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleBuddyList *blist,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when the buddy list is about to be unhidden.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>blist</parameter>&#160;:</term>
+ <listitem><simpara>The buddy list.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="gtkblist-gtkblist-created" role="signal">
+ <title>The <literal>&quot;gtkblist-created&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleBuddyList *blist,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when the buddy list is created.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>blist</parameter>&#160;:</term>
+ <listitem><simpara>The buddy list.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="gtkblist-drawing-tooltip" role="signal">
+ <title>The <literal>&quot;drawing-tooltip&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleBlistNode *node,
+ GString *text,
+ gboolean full,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted just before a tooltip is displayed. <literal>text</literal> is a standard GString, so the plugin can modify the text that will be displayed.
+ </para>
+ <note><para>
+Make sure to free <literal>*text</literal> before you replace it!
+ </para></note>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>node</parameter>&#160;:</term>
+ <listitem><simpara>The blist node for the tooltip.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>text</parameter>&#160;:</term>
+ <listitem><simpara>A pointer to the text that will be displayed.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>full</parameter>&#160;:</term>
+ <listitem><simpara>Whether we're doing a full tooltip for the priority buddy or a compact tooltip for a non-priority buddy.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="gtkblist-drawing-buddy" role="signal">
+ <title>The <literal>&quot;drawing-buddy&quot;</literal> signal</title>
+<programlisting>
+char * user_function (PurpleBuddy *buddy,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted to allow plugins to handle markup within a buddy's name or to override the default of no formatting for names shown in the buddy list.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>buddy</parameter>&#160;:</term>
+ <listitem><simpara>A pointer to the PurpleBuddy that will be displayed.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Returns</emphasis>&#160;:</term>
+ <listitem><simpara>The text to display (must be allocated), or <literal>NULL</literal> if no changes to the default behavior are desired.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+</refsect1>
+
+</chapter>
diff --git a/doc/reference/pidgin/signals_gtkconv.xml b/doc/reference/pidgin/signals_gtkconv.xml
new file mode 100644
index 0000000000..da786e2ecf
--- /dev/null
+++ b/doc/reference/pidgin/signals_gtkconv.xml
@@ -0,0 +1,330 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-signals-gtkconv">
+<title>Conversation signals</title>
+
+<refsect1 id="gtkconvs.signals" role="signal_proto">
+<title role="signal_proto.title">List of signals</title>
+<synopsis>
+ &quot;<link linkend="gtkconvs-conversation-dragging">conversation-dragging</link>&quot;
+ &quot;<link linkend="gtkconvs-conversation-timestamp">conversation-timestamp</link>&quot;
+ &quot;<link linkend="gtkconvs-displaying-im-msg">displaying-im-msg</link>&quot;
+ &quot;<link linkend="gtkconvs-displayed-im-msg">displayed-im-msg</link>&quot;
+ &quot;<link linkend="gtkconvs-displaying-chat-msg">displaying-chat-msg</link>&quot;
+ &quot;<link linkend="gtkconvs-displayed-chat-msg">displayed-chat-msg</link>&quot;
+ &quot;<link linkend="gtkconvs-conversation-switched">conversation-switched</link>&quot;
+ &quot;<link linkend="gtkconvs-conversation-hiding">conversation-hiding</link>&quot;
+ &quot;<link linkend="gtkconvs-conversation-displayed">conversation-displayed</link>&quot;
+</synopsis>
+</refsect1>
+
+<refsect1 id="gtkconvs.signal-details" role="signals">
+<title role="signals.title">Signal details</title>
+
+<refsect2 id="gtkconvs-conversation-dragging" role="signal">
+ <title>The <literal>&quot;conversation-dragging&quot;</literal> signal</title>
+<programlisting>
+void user_function (PidginWindow *source,
+ PidginWindow *destination,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a conversation is being drag and dropped between windows.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>source</parameter>&#160;:</term>
+ <listitem><simpara>The window where the conversation is.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>destination</parameter>&#160;:</term>
+ <listitem><simpara>The window where the conversation will be moved to.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="gtkconvs-conversation-timestamp" role="signal">
+ <title>The <literal>&quot;conversation-timestamp&quot;</literal> signal</title>
+<programlisting>
+char * user_function (PurpleConversation *conv,
+ time_t when,
+ gboolean show_date,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted to allow plugins to customize the timestamp on a message.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>conv</parameter>&#160;:</term>
+ <listitem><simpara>The conversation the message belongs to.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>when</parameter>&#160;:</term>
+ <listitem><simpara>The time to be converted to a string.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>show_date</parameter>&#160;:</term>
+ <listitem><simpara>Whether the date should be displayed.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Returns</emphasis>&#160;:</term>
+ <listitem><simpara>A textual representation of the time, or <literal>NULL</literal> to use a default format.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="gtkconvs-displaying-im-msg" role="signal">
+ <title>The <literal>&quot;displaying-im-msg&quot;</literal> signal</title>
+<programlisting>
+gboolean user_function (PurpleAccount *account,
+ const char *who,
+ char **message,
+ PurpleConversation *conv,
+ PurpleMessageFlags flags,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted just before a message is displayed in an IM conversation. <literal>message</literal> is a pointer to a string, so the plugin can replace the message that will be displayed. This can also be used to cancel displaying a message by returning <literal>TRUE</literal>.
+ </para>
+ <note><para>
+Make sure to free <literal>*message</literal> before you replace it!
+ </para></note>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>who</parameter>&#160;:</term>
+ <listitem><simpara>The name of the user.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>A pointer to the message.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>conv</parameter>&#160;:</term>
+ <listitem><simpara>The conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>flags</parameter>&#160;:</term>
+ <listitem><simpara>Flags for this message.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Returns</emphasis>&#160;:</term>
+ <listitem><simpara><literal>TRUE</literal> if the message should be canceled, or <literal>FALSE</literal> otherwise.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="gtkconvs-displayed-im-msg" role="signal">
+ <title>The <literal>&quot;displayed-im-msg&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ const char *who,
+ char *message,
+ PurpleConversation *conv,
+ PurpleMessageFlags flags,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted after a message is displayed in an IM conversation.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>who</parameter>&#160;:</term>
+ <listitem><simpara>The name of the user.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>The message.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>conv</parameter>&#160;:</term>
+ <listitem><simpara>The conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>flags</parameter>&#160;:</term>
+ <listitem><simpara>Flags for this message.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="gtkconvs-displaying-chat-msg" role="signal">
+ <title>The <literal>&quot;displaying-chat-msg&quot;</literal> signal</title>
+<programlisting>
+gboolean user_function (PurpleAccount *account,
+ const char *who,
+ char **message,
+ PurpleConversation *conv,
+ PurpleMessageFlags flags,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted just before a message is displayed in a chat. <literal>message</literal> is a pointer to a string, so the plugin can replace the message that will be displayed. This can also be used to cancel displaying a message by returning <literal>TRUE</literal>.
+ </para>
+ <note><para>
+Make sure to free <literal>*message</literal> before you replace it!
+ </para></note>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account the message is being displayed and sent on.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>who</parameter>&#160;:</term>
+ <listitem><simpara>The name of the user.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>A pointer to the message that will be displayed and sent.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>conv</parameter>&#160;:</term>
+ <listitem><simpara>The conversation the message is being displayed and sent on.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>flags</parameter>&#160;:</term>
+ <listitem><simpara>Flags for this message.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Returns</emphasis>&#160;:</term>
+ <listitem><simpara><literal>TRUE</literal> if the message should be canceled, or <literal>FALSE</literal> otherwise.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="gtkconvs-displayed-chat-msg" role="signal">
+ <title>The <literal>&quot;displayed-chat-msg&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleAccount *account,
+ const char *who,
+ char *message,
+ PurpleConversation *conv,
+ PurpleMessageFlags flags,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted after a message is displayed in a chat conversation.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>account</parameter>&#160;:</term>
+ <listitem><simpara>The account the message is being displayed and sent on.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>who</parameter>&#160;:</term>
+ <listitem><simpara>The name of the user.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>message</parameter>&#160;:</term>
+ <listitem><simpara>A pointer to the message that will be displayed and sent.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>conv</parameter>&#160;:</term>
+ <listitem><simpara>The conversation the message is being displayed and sent on.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>flags</parameter>&#160;:</term>
+ <listitem><simpara>Flags for this message.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="gtkconvs-conversation-switched" role="signal">
+ <title>The <literal>&quot;conversation-switched&quot;</literal> signal</title>
+<programlisting>
+void user_function (PurpleConversation *conv,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a window switched from one conversation to another.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>new_conv</parameter>&#160;:</term>
+ <listitem><simpara>The now active conversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="gtkconvs-conversation-hiding" role="signal">
+ <title>The <literal>&quot;conversation-hiding&quot;</literal> signal</title>
+<programlisting>
+void user_function (PidginConversation *gtkconv,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted immediately before an existing conversation is hidden.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>gtkconv</parameter>&#160;:</term>
+ <listitem><simpara>The PidginConversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="gtkconvs-conversation-displayed" role="signal">
+ <title>The <literal>&quot;conversation-displayed&quot;</literal> signal</title>
+<programlisting>
+void user_function (PidginConversation *gtkconv,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted right after the Pidgin UI is attached to a new or a hidden conversation.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>gtkconv</parameter>&#160;:</term>
+ <listitem><simpara>The PidginConversation.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+</refsect1>
+
+</chapter>
diff --git a/doc/reference/pidgin/signals_gtklog.xml b/doc/reference/pidgin/signals_gtklog.xml
new file mode 100644
index 0000000000..e8201a7341
--- /dev/null
+++ b/doc/reference/pidgin/signals_gtklog.xml
@@ -0,0 +1,46 @@
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<chapter id="chapter-signals-gtklog">
+<title>Log signals</title>
+
+<refsect1 id="gtklogs.signals" role="signal_proto">
+<title role="signal_proto.title">List of signals</title>
+<synopsis>
+ &quot;<link linkend="gtklogs-log-displaying">log-displaying</link>&quot;
+</synopsis>
+</refsect1>
+
+<refsect1 id="gtklogs.signal-details" role="signals">
+<title role="signals.title">Signal details</title>
+
+<refsect2 id="gtklogs-log-displaying" role="signal">
+ <title>The <literal>&quot;log-displaying&quot;</literal> signal</title>
+<programlisting>
+void user_function (PidginLogViewer *viewer,
+ PurpleLog *log,
+ gpointer user_data)
+</programlisting>
+ <para>
+Emitted when a log is being displayed.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>viewer</parameter>&#160;:</term>
+ <listitem><simpara>The log viewer.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>log</parameter>&#160;:</term>
+ <listitem><simpara>The log being displayed.</simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+</refsect1>
+
+</chapter>
diff --git a/doc/reference/pidgin/version.xml.in b/doc/reference/pidgin/version.xml.in
new file mode 100644
index 0000000000..405528f9af
--- /dev/null
+++ b/doc/reference/pidgin/version.xml.in
@@ -0,0 +1 @@
+@PURPLE_VERSION@
diff --git a/doc/savedstatus-signals.dox b/doc/savedstatus-signals.dox
deleted file mode 100644
index 20d8a9ca20..0000000000
--- a/doc/savedstatus-signals.dox
+++ /dev/null
@@ -1,20 +0,0 @@
-/** @page savedstatus-signals Saved Status Signals
-
- @signals
- @signal savedstatus-changed
- @endsignals
-
- @see savedstatus.h
-
- <hr>
-
- @signaldef savedstatus-changed
- @signalproto
-void (*savedstatus_changed)(PurpleSavedStatus *new, PurpleSavedStatus *old);
- @endsignalproto
- @signaldesc
- Emitted when a new saved status is activated.
- @endsignaldef
-
- */
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doc/sound-signals.dox b/doc/sound-signals.dox
deleted file mode 100644
index 66e26620b7..0000000000
--- a/doc/sound-signals.dox
+++ /dev/null
@@ -1,23 +0,0 @@
-/** @page sound-signals Sound Signals
-
- @signals
- @signal playing-sound-event
- @endsignals
-
- @see sound.h
-
- <hr>
-
- @signaldef playing-sound-event
- @signalproto
-gboolean (*playing_sound_event)(PurpleSoundEventID event, PurpleAccount *account);
- @endsignalproto
- @signaldesc
- Emitted when libpurple is going to play a sound event. This can be used to cancel playing sound by returning TRUE.
- @param event The event this sound represents.
- @param account The account the sound is being played for.
- @return @c TRUE if the sound should not be played, or @c FALSE otherwise.
- @endsignaldef
-
- */
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doc/ui-ops.dox b/doc/ui-ops.dox
deleted file mode 100644
index 483eea0593..0000000000
--- a/doc/ui-ops.dox
+++ /dev/null
@@ -1,24 +0,0 @@
-/** @page ui-ops UiOps structures
-
- When implementing a UI for libpurple, you need to fill in various UiOps
- structures:
-
- - #PurpleAccountUiOps
- - #PurpleBlistUiOps
- - #PurpleConnectionUiOps
- - #PurpleConversationUiOps
- - #PurpleCoreUiOps
- - #PurpleDebugUiOps
- - #PurpleDnsQueryUiOps
- - #PurpleEventLoopUiOps (without this, nothing will work and you will cry)
- - #PurpleIdleUiOps
- - #PurpleNotifyUiOps
- - #PurplePrivacyUiOps
- - #PurpleRequestUiOps
- - #PurpleRoomlistUiOps
- - #PurpleSoundUiOps
- - #PurpleWhiteboardUiOps
- - #PurpleXferUiOps
-
- */
-// vim: ft=c.doxygen
diff --git a/doc/xfer-signals.dox b/doc/xfer-signals.dox
deleted file mode 100644
index 6e64c17664..0000000000
--- a/doc/xfer-signals.dox
+++ /dev/null
@@ -1,114 +0,0 @@
-/** @page xfer-signals File Transfer Signals
-
- @signals
- @signal file-recv-accept
- @signal file-recv-start
- @signal file-recv-cancel
- @signal file-recv-complete
- @signal file-recv-request
- @signal file-send-accept
- @signal file-send-start
- @signal file-send-cancel
- @signal file-send-complete
- @endsignals
-
- @see ft.h
-
- <hr>
-
- @signaldef file-recv-accept
- @signalproto
-void (*file_recv_accept)(PurpleXfer *xfer, gpointer data);
- @endsignalproto
- @signaldesc
- Emitted when an incoming file transfer has been accepted.
- @param xfer The file transfer
- @param data User data
- @endsignaldef
-
- @signaldef file-recv-start
- @signalproto
-void (*file_recv_start)(PurpleXfer *xfer, gpointer data);
- @endsignalproto
- @signaldesc
- Emitted when an incoming file transfer has been started.
- @param xfer The file transfer
- @param data User data
- @endsignaldef
-
- @signaldef file-recv-cancel
- @signalproto
-void (*file_recv_cancel)(PurpleXfer *xfer, gpointer data);
- @endsignalproto
- @signaldesc
- Emitted when an incoming file transfer has been canceled.
- @param xfer The file transfer
- @param data User data
- @endsignaldef
-
- @signaldef file-recv-complete
- @signalproto
-void (*file_recv_complete)(PurpleXfer *xfer, gpointer data);
- @endsignalproto
- @signaldesc
- Emitted when an incoming file transfer has been completed.
- @param xfer The file transfer
- @param data User data
- @endsignaldef
-
- @signaldef file-recv-request
- @signalproto
-void (*file_recv_request)(PurpleXfer *xfer, gpointer data);
- @endsignalproto
- @signaldesc
- Emitted before the user is prompted for an incoming file-transfer.
- Plugins can intercept the signal to auto-accept/auto-reject the
- requests. To auto-accept the file transfer, use
- purple_xfer_request_accepted(). To auto-reject, set the status of the
- xfer to PURPLE_XFER_STATUS_CANCEL_LOCAL.
- @param xfer The file transfer
- @param data User data
- @endsignaldef
-
- @signaldef file-send-accept
- @signalproto
-void (*file_send_accept)(PurpleXfer *xfer, gpointer data);
- @endsignalproto
- @signaldesc
- Emitted when an outgoing file transfer has been accepted.
- @param xfer The file transfer
- @param data User data
- @endsignaldef
-
- @signaldef file-send-start
- @signalproto
-void (*file_send_start)(PurpleXfer *xfer, gpointer data);
- @endsignalproto
- @signaldesc
- Emitted when an outgoing file transfer has started.
- @param xfer The file transfer
- @param data User data
- @endsignaldef
-
- @signaldef file-send-cancel
- @signalproto
-void (*file_send_cancel)(PurpleXfer *xfer, gpointer data);
- @endsignalproto
- @signaldesc
- Emitted when an outgoing file transfer has been canceled.
- @param xfer The file transfer
- @param data User data
- @endsignaldef
-
- @signaldef file-send-complete
- @signalproto
-void (*file_send_complete)(PurpleXfer *xfer, gpointer data);
- @endsignalproto
- @signaldesc
- Emitted when an outgoing file transfer has been completed.
- @param xfer The file transfer
- @param data User data
- @endsignaldef
-
- */
-// vim: syntax=c.doxygen tw=75 et
diff --git a/doxy2devhelp.xsl b/doxy2devhelp.xsl
deleted file mode 100644
index 4ea5a4d7a5..0000000000
--- a/doxy2devhelp.xsl
+++ /dev/null
@@ -1,98 +0,0 @@
-<xsl:stylesheet
- xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- xmlns:fo="http://www.w3.org/1999/XSL/Format"
- version="1.0">
-
-<!-- Based on http://bur.st/~eleusis/devhelp/doxy2devhelp.xsl
- (http://bur.st/~eleusis/devhelp/README)
- which is based on http://bugzilla.gnome.org/show_bug.cgi?id=122450
--->
-
-<xsl:output method="xml" version="1.0" indent="yes"/>
-
-<xsl:param name="reference_prefix"></xsl:param>
-
-<xsl:template match="/">
- <book title="Pidgin Documentation"
- name="pidgin"
- link="{$reference_prefix}main.html">
- <chapters>
- <sub name="Modules" link="{$reference_prefix}modules.html">
- <xsl:apply-templates select="doxygenindex/compound[@kind='group']">
- <xsl:sort select="."/>
- </xsl:apply-templates>
- </sub>
- <!-- annotated.html has the short descriptions beside each struct. is
- that more useful than being grouped alphabetically?
- -->
- <sub name="Structs" link="{$reference_prefix}classes.html">
- <xsl:apply-templates select="doxygenindex/compound[@kind='struct']">
- <xsl:sort select="."/>
- </xsl:apply-templates>
- </sub>
- <!-- This is redundant given Modules -->
- <!--
- <sub name="Directories" link="{$reference_prefix}dirs.html">
- <xsl:apply-templates select="doxygenindex/compound[@kind='dir']">
- <xsl:sort select="."/>
- </xsl:apply-templates>
- </sub>
- -->
- <!-- FIXME: Some files show up here but are broken links; mostly
- files that are under pages...
- -->
- <sub name="Files" link="{$reference_prefix}files.html">
- <xsl:apply-templates select="doxygenindex/compound[@kind='file']">
- <xsl:sort select="."/>
- </xsl:apply-templates>
- </sub>
- <sub name="Signals, HOWTOs, Other" link="{$reference_prefix}pages.html">
- <xsl:apply-templates select="doxygenindex/compound[@kind='page']">
- <xsl:sort select="."/>
- </xsl:apply-templates>
- </sub>
- </chapters>
-
- <functions>
- <!-- @todo: maybe select only the real functions, ie those with kind=="function"? -->
- <xsl:apply-templates select="doxygenindex/compound/member" mode="as-function"/>
- </functions>
- </book>
-</xsl:template>
-
-<xsl:template match="compound">
- <xsl:param name="name"><xsl:value-of select="name"/></xsl:param>
- <xsl:param name="link"><xsl:value-of select="@refid"/>.html</xsl:param>
- <sub name="{$name}" link="{$reference_prefix}{$link}">
- <xsl:apply-templates select="member" mode="as-sub">
- <xsl:sort select="."/>
- </xsl:apply-templates>
- </sub>
-</xsl:template>
-
-<xsl:template match="member" mode="as-function">
- <!--
- <function name="atk_set_value" link="atk-atkvalue.html#ATK-SET-VALUE"/>
- -->
- <xsl:param name="name"><xsl:value-of select="name"/></xsl:param>
- <!-- Link is refid attribute of parent element + "#" + diff between refid of parent and own refid -->
- <xsl:param name="refid_parent"><xsl:value-of select="parent::node()/@refid"/></xsl:param>
- <xsl:param name="own_refid"><xsl:value-of select="@refid"/></xsl:param>
- <xsl:param name="offset"><xsl:value-of select="string-length($refid_parent) + 3"/></xsl:param>
- <xsl:param name="ref_diff"><xsl:value-of select="substring($own_refid, $offset, 33)"/></xsl:param>
- <xsl:param name="link"><xsl:value-of select="$refid_parent"/>.html#<xsl:value-of select="$ref_diff"/></xsl:param>
- <function name="{$name}" link="{$reference_prefix}{$link}"/>
-</xsl:template>
-
-<xsl:template match="member" mode="as-sub">
- <xsl:param name="name"><xsl:value-of select="name"/></xsl:param>
- <!-- Link is refid attribute of parent element + "#" + diff between refid of parent and own refid -->
- <xsl:param name="refid_parent"><xsl:value-of select="parent::node()/@refid"/></xsl:param>
- <xsl:param name="own_refid"><xsl:value-of select="@refid"/></xsl:param>
- <xsl:param name="offset"><xsl:value-of select="string-length($refid_parent) + 3"/></xsl:param>
- <xsl:param name="ref_diff"><xsl:value-of select="substring($own_refid, $offset, 33)"/></xsl:param>
- <xsl:param name="link"><xsl:value-of select="$refid_parent"/>.html#<xsl:value-of select="$ref_diff"/></xsl:param>
- <sub name="{$name}" link="{$reference_prefix}{$link}"/>
-</xsl:template>
-
-</xsl:stylesheet>
diff --git a/finch/Makefile.am b/finch/Makefile.am
index 85502d3015..2c740c52ca 100644
--- a/finch/Makefile.am
+++ b/finch/Makefile.am
@@ -7,26 +7,27 @@ EXTRA_DIST = \
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = finch.pc
-SUBDIRS = libgnt plugins
+SUBDIRS = libgnt . plugins
if ENABLE_GNT
+# XXX: should this be lib_, or noinst_?
+lib_LTLIBRARIES = libfinch.la
bin_PROGRAMS = finch
endif
-finch_SOURCES = \
+libfinch_la_SOURCES = \
gntaccount.c \
gntblist.c \
gntcertmgr.c \
gntconn.c \
gntconv.c \
gntdebug.c \
- gntft.c \
- finch.c \
gntidle.c \
gntlog.c \
gntmedia.c \
+ gntmenuutil.c \
gntnotify.c \
gntplugin.c \
gntpounce.c \
@@ -35,20 +36,22 @@ finch_SOURCES = \
gntroomlist.c \
gntsound.c \
gntstatus.c \
- gntui.c
+ gntui.c \
+ gntxfer.c \
+ libfinch.c
-finch_headers = \
+libfinch_la_headers = \
gntaccount.h \
gntblist.h \
gntcertmgr.h \
gntconn.h \
gntconv.h \
gntdebug.h \
- gntft.h \
finch.h \
gntidle.h \
gntlog.h \
gntmedia.h \
+ gntmenuutil.h \
gntnotify.h \
gntplugin.h \
gntpounce.h \
@@ -57,15 +60,20 @@ finch_headers = \
gntroomlist.h \
gntsound.h \
gntstatus.h \
- gntui.h
+ gntui.h \
+ gntxfer.h
-finchincludedir=$(includedir)/finch
-finchinclude_HEADERS = \
- $(finch_headers)
+finch_SOURCES = \
+ finch.c
-finch_DEPENDENCIES = @LIBOBJS@
-finch_LDFLAGS = -export-dynamic
-finch_LDADD = \
+libfinchincludedir=$(includedir)/finch
+libfinchinclude_HEADERS = \
+ $(libfinch_la_headers)
+
+libfinch_la_DEPENDENCIES = @LIBOBJS@
+libfinch_la_LDFLAGS = -export-dynamic -no-undefined \
+ -version-info $(PURPLE_LT_VERSION_INFO)
+libfinch_la_LIBADD = \
@LIBOBJS@ \
$(DBUS_LIBS) \
$(INTLLIBS) \
@@ -76,12 +84,29 @@ finch_LDADD = \
./libgnt/libgnt.la \
$(top_builddir)/libpurple/libpurple.la
+finch_DEPENDENCIES = $(builddir)/libfinch.la
+finch_LDFLAGS = -export-dynamic
+finch_LDADD = $(builddir)/libfinch.la $(libfinch_la_LIBADD)
+
+if IS_WIN32
+libfinch_la_LIBADD += \
+ -lwinmm
+
+.rc.o: %.rc
+ $(AM_V_GEN)$(WINDRES) -i $< -o $@
+
+FINCH_WIN32RES = finch_winres.o
+finch_DEPENDENCIES += $(FINCH_WIN32RES)
+finch_LDFLAGS += -Wl,$(FINCH_WIN32RES)
+
+LIBFINCH_WIN32RES = libfinch_winres.o
+libfinch_la_DEPENDENCIES += $(LIBFINCH_WIN32RES)
+libfinch_la_LDFLAGS += -Wl,$(LIBFINCH_WIN32RES)
+
+endif
+
AM_CPPFLAGS = \
-DSTANDALONE \
- -DDATADIR=\"$(datadir)\" \
- -DLIBDIR=\"$(libdir)/finch/\" \
- -DLOCALEDIR=\"$(datadir)/locale\" \
- -DSYSCONFDIR=\"$(sysconfdir)\" \
-I$(top_srcdir)/libpurple/ \
-I$(top_srcdir) \
-I$(srcdir)/libgnt/ \
diff --git a/finch/finch.c b/finch/finch.c
index 659b2d40dc..bd90170a0b 100644
--- a/finch/finch.c
+++ b/finch/finch.c
@@ -19,433 +19,18 @@
* 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"
-#include "account.h"
-#include "conversation.h"
+#include "internal.h"
#include "core.h"
-#include "debug.h"
-#include "eventloop.h"
-#include "ft.h"
-#include "log.h"
-#include "notify.h"
-#include "prefs.h"
-#include "prpl.h"
-#include "pounce.h"
-#include "savedstatuses.h"
-#include "sound.h"
-#include "status.h"
-#include "util.h"
-#include "whiteboard.h"
-
-#include "gntdebug.h"
-#include "gntprefs.h"
-#include "gntui.h"
-#include "gntidle.h"
-
-#define _GNU_SOURCE
-#include <getopt.h>
-
-#include "config.h"
-#include "package_revision.h"
-
-static void
-debug_init(void)
-{
- finch_debug_init();
- purple_debug_set_ui_ops(finch_debug_get_ui_ops());
-}
-
-static GHashTable *ui_info = NULL;
-static GHashTable *finch_ui_get_info(void)
-{
- if (ui_info == NULL) {
- ui_info = g_hash_table_new(g_str_hash, g_str_equal);
-
- g_hash_table_insert(ui_info, "name", (char*)_("Finch"));
- g_hash_table_insert(ui_info, "version", VERSION);
- g_hash_table_insert(ui_info, "website", "http://pidgin.im");
- g_hash_table_insert(ui_info, "dev_website", "http://developer.pidgin.im");
- g_hash_table_insert(ui_info, "client_type", "console");
-
- /*
- * This is the client key for "Finch." It is owned by the AIM
- * account "markdoliner." Please don't use this key for other
- * applications. You can either not specify a client key, in
- * which case the default "libpurple" key will be used, or you
- * can try to register your own at the AIM or ICQ web sites
- * (although this functionality was removed at some point, it's
- * possible it has been re-added). AOL's old key management
- * page is http://developer.aim.com/manageKeys.jsp
- */
- g_hash_table_insert(ui_info, "prpl-aim-clientkey", "ma19sqWV9ymU6UYc");
-
- /*
- * This is the client key for "Pidgin." It is owned by the AIM
- * account "markdoliner." Please don't use this key for other
- * applications. You can either not specify a client key, in
- * which case the default "libpurple" key will be used, or you
- * can try to register your own at the AIM or ICQ web sites
- * (although this functionality was removed at some point, it's
- * possible it has been re-added). AOL's old key management
- * page is http://developer.aim.com/manageKeys.jsp
- *
- * We used to have a Finch-specific devId/clientkey
- * (ma19sqWV9ymU6UYc), but it stopped working, so we switched
- * to this one.
- */
- g_hash_table_insert(ui_info, "prpl-icq-clientkey", "ma1cSASNCKFtrdv9");
-
- /*
- * This is the distid for Finch, given to us by AOL. Please
- * don't use this for other applications. You can just not
- * specify a distid and libpurple will use a default.
- */
- g_hash_table_insert(ui_info, "prpl-aim-distid", GINT_TO_POINTER(1552));
- g_hash_table_insert(ui_info, "prpl-icq-distid", GINT_TO_POINTER(1552));
- }
-
- return ui_info;
-}
-
-static void
-finch_quit(void)
-{
- gnt_ui_uninit();
- if (ui_info)
- g_hash_table_destroy(ui_info);
-}
-
-static PurpleCoreUiOps core_ops =
-{
- finch_prefs_init,
- debug_init,
- gnt_ui_init,
- finch_quit,
- finch_ui_get_info,
-
- /* padding */
- NULL,
- NULL,
- NULL
-};
-
-static PurpleCoreUiOps *
-gnt_core_get_ui_ops(void)
-{
- return &core_ops;
-}
-
-/* Anything IO-related is directly copied from gtkpurple's source tree */
-
-#define FINCH_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR)
-#define FINCH_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL)
-
-typedef struct _PurpleGntIOClosure {
- PurpleInputFunction function;
- guint result;
- gpointer data;
-} PurpleGntIOClosure;
-
-static void purple_gnt_io_destroy(gpointer data)
-{
- g_free(data);
-}
-
-static gboolean purple_gnt_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data)
-{
- PurpleGntIOClosure *closure = data;
- PurpleInputCondition purple_cond = 0;
-
- if (condition & FINCH_READ_COND)
- purple_cond |= PURPLE_INPUT_READ;
- if (condition & FINCH_WRITE_COND)
- purple_cond |= PURPLE_INPUT_WRITE;
-
-#if 0
- purple_debug(PURPLE_DEBUG_MISC, "gtk_eventloop",
- "CLOSURE: callback for %d, fd is %d\n",
- closure->result, g_io_channel_unix_get_fd(source));
-#endif
-
-#ifdef _WIN32
- if(! purple_cond) {
-#if DEBUG
- purple_debug_misc("gnt_eventloop",
- "CLOSURE received GIOCondition of 0x%x, which does not"
- " match 0x%x (READ) or 0x%x (WRITE)\n",
- condition, FINCH_READ_COND, FINCH_WRITE_COND);
-#endif /* DEBUG */
-
- return TRUE;
- }
-#endif /* _WIN32 */
-
- closure->function(closure->data, g_io_channel_unix_get_fd(source),
- purple_cond);
-
- return TRUE;
-}
-
-static guint gnt_input_add(gint fd, PurpleInputCondition condition, PurpleInputFunction function,
- gpointer data)
-{
- PurpleGntIOClosure *closure = g_new0(PurpleGntIOClosure, 1);
- GIOChannel *channel;
- GIOCondition cond = 0;
-
- closure->function = function;
- closure->data = data;
-
- if (condition & PURPLE_INPUT_READ)
- cond |= FINCH_READ_COND;
- if (condition & PURPLE_INPUT_WRITE)
- cond |= FINCH_WRITE_COND;
-
- channel = g_io_channel_unix_new(fd);
- closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond,
- purple_gnt_io_invoke, closure, purple_gnt_io_destroy);
-
- g_io_channel_unref(channel);
- return closure->result;
-}
-
-static PurpleEventLoopUiOps eventloop_ops =
-{
- g_timeout_add,
- g_source_remove,
- gnt_input_add,
- g_source_remove,
- NULL, /* input_get_error */
-#if GLIB_CHECK_VERSION(2,14,0)
- g_timeout_add_seconds,
-#else
- NULL,
-#endif
-
- /* padding */
- NULL,
- NULL,
- NULL
-};
-
-static PurpleEventLoopUiOps *
-gnt_eventloop_get_ui_ops(void)
-{
- return &eventloop_ops;
-}
-
-/* This is mostly copied from gtkpurple's source tree */
-static void
-show_usage(const char *name, gboolean terse)
-{
- char *text;
-
- if (terse) {
- text = g_strdup_printf(_("%s. Try `%s -h' for more information.\n"), DISPLAY_VERSION, name);
- } else {
- text = g_strdup_printf(_("%s\n"
- "Usage: %s [OPTION]...\n\n"
- " -c, --config=DIR use DIR for config files\n"
- " -d, --debug print debugging messages to stderr\n"
- " -h, --help display this help and exit\n"
- " -n, --nologin don't automatically login\n"
- " -v, --version display the current version and exit\n"), DISPLAY_VERSION, name);
- }
-
- purple_print_utf8_to_console(stdout, text);
- g_free(text);
-}
-
-static int
-init_libpurple(int argc, char **argv)
-{
- char *path;
- int opt;
- gboolean opt_help = FALSE;
- gboolean opt_nologin = FALSE;
- gboolean opt_version = FALSE;
- char *opt_config_dir_arg = NULL;
- gboolean debug_enabled = FALSE;
-
- struct option long_options[] = {
- {"config", required_argument, NULL, 'c'},
- {"debug", no_argument, NULL, 'd'},
- {"help", no_argument, NULL, 'h'},
- {"nologin", no_argument, NULL, 'n'},
- {"version", no_argument, NULL, 'v'},
- {0, 0, 0, 0}
- };
-
-#ifdef ENABLE_NLS
- bindtextdomain(PACKAGE, LOCALEDIR);
- bind_textdomain_codeset(PACKAGE, "UTF-8");
- textdomain(PACKAGE);
-#endif
-
-#ifdef HAVE_SETLOCALE
- setlocale(LC_ALL, "");
-#endif
-
- /* scan command-line options */
- opterr = 1;
- while ((opt = getopt_long(argc, argv,
-#ifndef _WIN32
- "c:dhn::v",
-#else
- "c:dhn::v",
-#endif
- long_options, NULL)) != -1) {
- switch (opt) {
- case 'c': /* config dir */
- g_free(opt_config_dir_arg);
- opt_config_dir_arg = g_strdup(optarg);
- break;
- case 'd': /* debug */
- debug_enabled = TRUE;
- break;
- case 'h': /* help */
- opt_help = TRUE;
- break;
- case 'n': /* no autologin */
- opt_nologin = TRUE;
- break;
- case 'v': /* version */
- opt_version = TRUE;
- break;
- case '?': /* show terse help */
- default:
- show_usage(argv[0], TRUE);
- return 0;
- break;
- }
- }
-
- /* show help message */
- if (opt_help) {
- show_usage(argv[0], FALSE);
- return 0;
- }
- /* show version message */
- if (opt_version) {
- /* Translators may want to transliterate the name.
- It is not to be translated. */
- printf("%s %s (%s)\n", _("Finch"), DISPLAY_VERSION, REVISION);
- return 0;
- }
-
- /* set a user-specified config directory */
- if (opt_config_dir_arg != NULL) {
- purple_util_set_user_dir(opt_config_dir_arg);
- g_free(opt_config_dir_arg);
- }
-
- /*
- * We're done piddling around with command line arguments.
- * Fire up this baby.
- */
-
- /* We don't want debug-messages to show up and corrupt the display */
- purple_debug_set_enabled(debug_enabled);
-
- /* If we're using a custom configuration directory, we
- * do NOT want to migrate, or weird things will happen. */
- if (opt_config_dir_arg == NULL)
- {
- if (!purple_core_migrate())
- {
- char *old = g_strconcat(purple_home_dir(),
- G_DIR_SEPARATOR_S ".gaim", NULL);
- char *text = g_strdup_printf(_(
- "%s encountered errors migrating your settings "
- "from %s to %s. Please investigate and complete the "
- "migration by hand. Please report this error at http://developer.pidgin.im"), _("Finch"),
- old, purple_user_dir());
-
- g_free(old);
-
- purple_print_utf8_to_console(stderr, text);
- g_free(text);
-
- return 0;
- }
- }
-
- purple_core_set_ui_ops(gnt_core_get_ui_ops());
- purple_eventloop_set_ui_ops(gnt_eventloop_get_ui_ops());
- purple_idle_set_ui_ops(finch_idle_get_ui_ops());
-
- path = g_build_filename(purple_user_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);
- g_free(path);
-
- purple_plugins_add_search_path(LIBDIR);
-
- if (!purple_core_init(FINCH_UI))
- {
- fprintf(stderr,
- "Initialization of the Purple core failed. Dumping core.\n"
- "Please report this!\n");
- abort();
- }
-
- /* TODO: Move blist loading into purple_blist_init() */
- purple_set_blist(purple_blist_new());
- purple_blist_load();
-
- /* TODO: should this be moved into finch_prefs_init() ? */
- finch_prefs_update_old();
-
- /* load plugins we had when we quit */
- purple_plugins_load_saved("/finch/plugins/loaded");
-
- /* TODO: Move pounces loading into purple_pounces_init() */
- purple_pounces_load();
-
- if (opt_nologin)
- {
- /* Set all accounts to "offline" */
- PurpleSavedStatus *saved_status;
-
- /* If we've used this type+message before, lookup the transient status */
- saved_status = purple_savedstatus_find_transient_by_type_and_message(
- PURPLE_STATUS_OFFLINE, NULL);
-
- /* If this type+message is unique then create a new transient saved status */
- if (saved_status == NULL)
- saved_status = purple_savedstatus_new(NULL, PURPLE_STATUS_OFFLINE);
-
- /* Set the status for each account */
- purple_savedstatus_activate(saved_status);
- }
- else
- {
- /* Everything is good to go--sign on already */
- if (!purple_prefs_get_bool("/purple/savedstatus/startup_current_status"))
- purple_savedstatus_activate(purple_savedstatus_get_startup());
- purple_accounts_restore_current_statuses();
- }
-
- return 1;
-}
-
-static gboolean gnt_start(int *argc, char ***argv)
-{
- /* Initialize the libpurple stuff */
- if (!init_libpurple(*argc, *argv))
- return FALSE;
-
- purple_blist_show();
- return TRUE;
-}
+#include "finch.h"
+#include "gnt.h"
int main(int argc, char *argv[])
{
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
+#endif
#if !GLIB_CHECK_VERSION(2, 32, 0)
/* GLib threading system is automaticaly initialized since 2.32.
@@ -458,7 +43,7 @@ int main(int argc, char *argv[])
g_set_prgname("Finch");
g_set_application_name(_("Finch"));
- if (gnt_start(&argc, &argv)) {
+ if (finch_start(&argc, &argv)) {
gnt_main();
#ifdef STANDALONE
@@ -468,4 +53,3 @@ int main(int argc, char *argv[])
return 0;
}
-
diff --git a/finch/finch.h b/finch/finch.h
index 6299a1ea98..89e6df00fe 100644
--- a/finch/finch.h
+++ b/finch/finch.h
@@ -1,7 +1,3 @@
-/**
- * @defgroup finch Finch (GNT User Interface)
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -22,6 +18,16 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
+#ifndef _FINCH_H_
+#define _FINCH_H_
+/**
+ * SECTION:finch
+ * @section_id: finch-finch
+ * @short_description: <filename>finch.h</filename>
+ * @title: UI Definitions and Includes
+ */
+
#include <glib.h>
#define FINCH_UI "gnt-purple"
@@ -31,3 +37,11 @@
#define FINCH_GET_DATA(obj) (obj)->ui_data
#define FINCH_SET_DATA(obj, data) (obj)->ui_data = data
+/**
+ * finch_start:
+ *
+ * Start finch with the given command line arguments.
+ */
+gboolean finch_start(int *argc, char ***argv);
+
+#endif
diff --git a/finch/finch.pc.in b/finch/finch.pc.in
index 6deccbc45a..080de731f9 100644
--- a/finch/finch.pc.in
+++ b/finch/finch.pc.in
@@ -11,4 +11,4 @@ Description: Finch is an instant messenger application that uses libpurple for p
Version: @VERSION@
Requires: gnt purple
Cflags: -I${includedir}/finch
-
+Libs: -L${libdir} -lfinch
diff --git a/finch/finch_winres.rc.in b/finch/finch_winres.rc.in
new file mode 100644
index 0000000000..9ecf482901
--- /dev/null
+++ b/finch/finch_winres.rc.in
@@ -0,0 +1,30 @@
+#include <winver.h>
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION @PURPLE_MAJOR_VERSION@,@PURPLE_MINOR_VERSION@,@PURPLE_MICRO_VERSION@,0
+ PRODUCTVERSION @PURPLE_MAJOR_VERSION@,@PURPLE_MINOR_VERSION@,@PURPLE_MICRO_VERSION@,0
+ FILEFLAGSMASK 0
+ FILEFLAGS 0
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_APP
+ FILESUBTYPE VFT2_UNKNOWN
+ BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904B0"
+ BEGIN
+ VALUE "CompanyName", "The Pidgin developer community"
+ VALUE "FileDescription", "Finch instant messenger"
+ VALUE "FileVersion", "@PURPLE_VERSION@"
+ VALUE "InternalName", "finch"
+ VALUE "LegalCopyright", "Copyright (C) 1998-2014 The Pidgin developer community (See the COPYRIGHT file in the source distribution)."
+ VALUE "OriginalFilename", "finch.exe"
+ VALUE "ProductName", "Finch"
+ VALUE "ProductVersion", "@PURPLE_VERSION@"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+ END
diff --git a/finch/gntaccount.c b/finch/gntaccount.c
index 4464b548c6..a150a81a75 100644
--- a/finch/gntaccount.c
+++ b/finch/gntaccount.c
@@ -1,8 +1,3 @@
-/**
- * @file gntaccount.c GNT Account API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -126,8 +121,10 @@ save_account_cb(AccountEditDialog *dialog)
if (value == NULL || *value == '\0')
{
purple_notify_error(NULL, _("Error"),
- dialog->account ? _("Account was not modified") : _("Account was not added"),
- _("Username of an account must be non-empty."));
+ dialog->account ? _("Account was not modified") :
+ _("Account was not added"),
+ _("Username of an account must be non-empty."),
+ purple_request_cpar_from_account(dialog->account));
return;
}
@@ -142,7 +139,7 @@ save_account_cb(AccountEditDialog *dialog)
PurpleAccountUserSplit *split = iter->data;
GntWidget *entry = entries->data;
- value = gnt_entry_get_text(GNT_ENTRY(entry));
+ value = entry ? gnt_entry_get_text(GNT_ENTRY(entry)) : NULL;
if (value == NULL || *value == '\0')
value = purple_account_user_split_get_default_value(split);
g_string_append_printf(username, "%c%s",
@@ -168,15 +165,25 @@ save_account_cb(AccountEditDialog *dialog)
const char *old = purple_account_get_protocol_id(account);
char *oldprpl;
if (strcmp(old, purple_plugin_get_id(plugin))) {
- purple_notify_error(NULL, _("Error"), _("Account was not modified"),
- _("The account's protocol cannot be changed while it is connected to the server."));
+ purple_notify_error(NULL, _("Error"),
+ _("Account was not modified"),
+ _("The account's protocol cannot be "
+ "changed while it is connected to the "
+ "server."),
+ purple_request_cpar_from_account(
+ account));
return;
}
oldprpl = g_strdup(purple_normalize(account, purple_account_get_username(account)));
if (g_utf8_collate(oldprpl, purple_normalize(account, username->str))) {
- purple_notify_error(NULL, _("Error"), _("Account was not modified"),
- _("The account's username cannot be changed while it is connected to the server."));
+ purple_notify_error(NULL, _("Error"),
+ _("Account was not modified"),
+ _("The account's username cannot be "
+ "changed while it is connected to the "
+ "server."),
+ purple_request_cpar_from_account(
+ account));
g_free(oldprpl);
return;
}
@@ -188,16 +195,16 @@ save_account_cb(AccountEditDialog *dialog)
/* Alias */
value = gnt_entry_get_text(GNT_ENTRY(dialog->alias));
- purple_account_set_alias(account, value);
+ purple_account_set_private_alias(account, value);
/* Remember password and password */
purple_account_set_remember_password(account,
gnt_check_box_get_checked(GNT_CHECK_BOX(dialog->remember)));
value = gnt_entry_get_text(GNT_ENTRY(dialog->password));
if (value && *value)
- purple_account_set_password(account, value);
+ purple_account_set_password(account, value, NULL, NULL);
else
- purple_account_set_password(account, NULL);
+ purple_account_set_password(account, NULL, NULL, NULL);
/* Mail notification */
purple_account_set_check_mail(account,
@@ -213,7 +220,7 @@ save_account_cb(AccountEditDialog *dialog)
{
PurpleAccountOption *option = iter->data;
GntWidget *entry = entries->data;
- PurplePrefType type = purple_account_option_get_type(option);
+ PurplePrefType type = purple_account_option_get_pref_type(option);
const char *setting = purple_account_option_get_setting(option);
if (type == PURPLE_PREF_STRING)
@@ -314,17 +321,19 @@ update_user_splits(AccountEditDialog *dialog)
for (iter = prplinfo->user_splits; iter; iter = iter->next)
{
PurpleAccountUserSplit *split = iter->data;
- GntWidget *entry;
- char *buf;
+ GntWidget *entry = NULL;
+ char *buf = NULL;
- hbox = gnt_hbox_new(TRUE);
- gnt_box_add_widget(GNT_BOX(dialog->splits), hbox);
+ if (!purple_account_user_split_is_constant(split)) {
+ hbox = gnt_hbox_new(TRUE);
+ gnt_box_add_widget(GNT_BOX(dialog->splits), hbox);
- buf = g_strdup_printf("%s:", purple_account_user_split_get_text(split));
- gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(buf));
+ buf = g_strdup_printf("%s:", purple_account_user_split_get_text(split));
+ gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(buf));
- entry = gnt_entry_new(NULL);
- gnt_box_add_widget(GNT_BOX(hbox), entry);
+ entry = gnt_entry_new(NULL);
+ gnt_box_add_widget(GNT_BOX(hbox), entry);
+ }
dialog->split_entries = g_list_append(dialog->split_entries, entry);
g_free(buf);
@@ -355,7 +364,7 @@ update_user_splits(AccountEditDialog *dialog)
if (value == NULL)
value = purple_account_user_split_get_default_value(split);
- if (value != NULL)
+ if (value != NULL && entry != NULL)
gnt_entry_set_text(GNT_ENTRY(entry), value);
}
@@ -403,7 +412,7 @@ add_protocol_options(AccountEditDialog *dialog)
for (iter = prplinfo->protocol_options; iter; iter = iter->next)
{
PurpleAccountOption *option = iter->data;
- PurplePrefType type = purple_account_option_get_type(option);
+ PurplePrefType type = purple_account_option_get_pref_type(option);
box = gnt_hbox_new(TRUE);
gnt_box_set_pad(GNT_BOX(box), 0);
@@ -534,7 +543,8 @@ prpl_changed_cb(GntWidget *combo, PurplePlugin *old, PurplePlugin *new, AccountE
}
static void
-edit_account(PurpleAccount *account)
+edit_account_continue(PurpleAccount *account,
+ const gchar *password, GError *error, gpointer user_data)
{
GntWidget *window, *hbox;
GntWidget *combo, *button, *entry;
@@ -556,8 +566,9 @@ edit_account(PurpleAccount *account)
list = purple_plugins_get_protocols();
if (list == NULL) {
purple_notify_error(NULL, _("Error"),
- _("There are no protocol plugins installed."),
- _("(You probably forgot to 'make install'.)"));
+ _("There are no protocol plugins installed."),
+ _("(You probably forgot to 'make install'.)"),
+ purple_request_cpar_from_account(account));
return;
}
@@ -617,7 +628,7 @@ edit_account(PurpleAccount *account)
gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Password:")));
gnt_box_add_widget(GNT_BOX(hbox), entry);
if (account)
- gnt_entry_set_text(GNT_ENTRY(entry), purple_account_get_password(account));
+ gnt_entry_set_text(GNT_ENTRY(entry), password);
hbox = gnt_hbox_new(TRUE);
gnt_box_set_pad(GNT_BOX(hbox), 0);
@@ -627,7 +638,7 @@ edit_account(PurpleAccount *account)
gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Alias:")));
gnt_box_add_widget(GNT_BOX(hbox), entry);
if (account)
- gnt_entry_set_text(GNT_ENTRY(entry), purple_account_get_alias(account));
+ gnt_entry_set_text(GNT_ENTRY(entry), purple_account_get_private_alias(account));
/* User options */
update_user_options(dialog);
@@ -667,6 +678,12 @@ edit_account(PurpleAccount *account)
}
static void
+edit_account(PurpleAccount *account)
+{
+ purple_account_get_password(account, edit_account_continue, account);
+}
+
+static void
add_account_cb(GntWidget *widget, gpointer null)
{
edit_account(NULL);
@@ -712,10 +729,9 @@ delete_account_cb(GntWidget *widget, GntTree *tree)
purple_account_get_username(account));
purple_request_action(account, _("Delete Account"), prompt, NULL,
- PURPLE_DEFAULT_ACTION_NONE,
- account, NULL, NULL, account, 2,
- _("Delete"), really_delete_account,
- _("Cancel"), NULL);
+ PURPLE_DEFAULT_ACTION_NONE,
+ purple_request_cpar_from_account(account), account, 2,
+ _("Delete"), really_delete_account, _("Cancel"), NULL);
g_free(prompt);
}
@@ -758,7 +774,8 @@ account_list_key_pressed_cb(GntWidget *widget, const char *text, gpointer null)
count = g_list_length(accounts);
pos = g_list_index(accounts, account);
pos = (move + pos + count + 1) % (count + 1);
- purple_accounts_reorder(account, pos);
+ if (pos >= 0)
+ purple_accounts_reorder(account, pos);
/* I don't like this, but recreating the entire list seems to be
* the easiest way of doing it */
@@ -959,7 +976,8 @@ notify_added(PurpleAccount *account, const char *remote_user,
buffer = make_info(account, gc, remote_user, id, alias, msg);
- purple_notify_info(NULL, NULL, buffer, NULL);
+ purple_notify_info(NULL, NULL, buffer, NULL,
+ purple_request_cpar_from_connection(gc));
g_free(buffer);
}
@@ -1006,12 +1024,11 @@ request_add(PurpleAccount *account, const char *remote_user,
data->alias = (alias != NULL ? g_strdup(alias) : NULL);
buffer = make_info(account, gc, remote_user, id, alias, msg);
- purple_request_action(NULL, NULL, _("Add buddy to your list?"),
- buffer, PURPLE_DEFAULT_ACTION_NONE,
- account, remote_user, NULL,
- data, 2,
- _("Add"), G_CALLBACK(add_user_cb),
- _("Cancel"), G_CALLBACK(free_add_user_data));
+ purple_request_action(NULL, NULL, _("Add buddy to your list?"), buffer,
+ PURPLE_DEFAULT_ACTION_NONE,
+ purple_request_cpar_from_account(account), data, 2,
+ _("Add"), G_CALLBACK(add_user_cb),
+ _("Cancel"), G_CALLBACK(free_add_user_data));
g_free(buffer);
}
@@ -1036,7 +1053,7 @@ free_auth_and_add(auth_and_add *aa)
static void
authorize_and_add_cb(auth_and_add *aa)
{
- aa->auth_cb(aa->data);
+ aa->auth_cb(NULL, aa->data);
purple_blist_request_add_buddy(aa->account, aa->username,
NULL, aa->alias);
}
@@ -1044,7 +1061,7 @@ authorize_and_add_cb(auth_and_add *aa)
static void
deny_no_add_cb(auth_and_add *aa)
{
- aa->deny_cb(aa->data);
+ aa->deny_cb(NULL, aa->data);
}
static void *
@@ -1096,7 +1113,7 @@ finch_request_authorize(PurpleAccount *account,
widget = purple_request_action(NULL, _("Authorize buddy?"), buffer, NULL,
PURPLE_DEFAULT_ACTION_NONE,
- account, remote_user, NULL,
+ purple_request_cpar_from_account(account),
aa, 2,
_("Authorize"), authorize_and_add_cb,
_("Deny"), deny_no_add_cb);
@@ -1123,7 +1140,7 @@ finch_request_authorize(PurpleAccount *account,
} else {
uihandle = purple_request_action(NULL, _("Authorize buddy?"), buffer, NULL,
PURPLE_DEFAULT_ACTION_NONE,
- account, remote_user, NULL,
+ purple_request_cpar_from_account(account),
user_data, 2,
_("Authorize"), auth_cb,
_("Deny"), deny_cb);
@@ -1150,7 +1167,8 @@ static PurpleAccountUiOps ui_ops =
NULL,
NULL,
NULL,
- NULL
+ NULL,
+ NULL, NULL, NULL, NULL
};
PurpleAccountUiOps *finch_accounts_get_ui_ops()
diff --git a/finch/gntaccount.h b/finch/gntaccount.h
index 2cf7d8f9e7..40d4cde31f 100644
--- a/finch/gntaccount.h
+++ b/finch/gntaccount.h
@@ -1,8 +1,3 @@
-/**
- * @file gntaccount.h GNT Account API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -23,47 +18,58 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _GNT_ACCOUNT_H
#define _GNT_ACCOUNT_H
+/**
+ * SECTION:gntaccount
+ * @section_id: finch-gntaccount
+ * @short_description: <filename>gntaccount.h</filename>
+ * @title: Account API
+ */
#include "account.h"
/**********************************************************************
- * @name GNT Account API
+ * GNT Account API
**********************************************************************/
-/*@{*/
/**
+ * finch_accounts_get_ui_ops:
+ *
* Get the ui-functions.
*
- * @return The PurpleAccountUiOps structure populated with the appropriate functions.
+ * Returns: The PurpleAccountUiOps structure populated with the appropriate functions.
*/
PurpleAccountUiOps *finch_accounts_get_ui_ops(void);
/**
+ * finch_accounts_init:
+ *
* Perform necessary initializations.
*/
void finch_accounts_init(void);
/**
+ * finch_accounts_uninit:
+ *
* Perform necessary uninitializations.
*/
void finch_accounts_uninit(void);
/**
+ * finch_accounts_show_all:
+ *
* Show the account-manager dialog.
*/
void finch_accounts_show_all(void);
/**
- * Show the edit dialog for an account.
+ * finch_account_dialog_show:
+ * @account: The account to edit, or %NULL to create a new account.
*
- * @param account The account to edit, or @c NULL to create a new account.
- *
- * @since 2.2.0
+ * Show the edit dialog for an account.
*/
void finch_account_dialog_show(PurpleAccount *account);
-/*@}*/
-
#endif
diff --git a/finch/gntblist.c b/finch/gntblist.c
index b2c62c55d8..5ed4890719 100644
--- a/finch/gntblist.c
+++ b/finch/gntblist.c
@@ -1,8 +1,3 @@
-/**
- * @file gntblist.c GNT BuddyList API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -27,10 +22,9 @@
#include "finch.h"
#include <account.h>
-#include <blist.h>
+#include <buddylist.h>
#include <log.h>
#include <notify.h>
-#include <privacy.h>
#include <request.h>
#include <savedstatuses.h>
#include <server.h>
@@ -43,13 +37,14 @@
#include "gntcolors.h"
#include "gntcombobox.h"
#include "gntentry.h"
-#include "gntft.h"
+#include "gntxfer.h"
#include "gntlabel.h"
#include "gntline.h"
#include "gntlog.h"
#include "gntmenu.h"
#include "gntmenuitem.h"
#include "gntmenuitemcheck.h"
+#include "gntmenuutil.h"
#include "gntpounce.h"
#include "gntstyle.h"
#include "gnttree.h"
@@ -72,15 +67,15 @@ typedef struct
GntWidget *tree;
GntWidget *tooltip;
- PurpleBlistNode *tnode; /* Who is the tooltip being displayed for? */
- GList *tagged; /* A list of tagged blistnodes */
+ PurpleBlistNode *tnode; /* Who is the tooltip being displayed for? */
+ GList *tagged; /* A list of tagged blistnodes */
GntWidget *context;
PurpleBlistNode *cnode;
/* XXX: I am KISSing */
- GntWidget *status; /* Dropdown with the statuses */
- GntWidget *statustext; /* Status message */
+ GntWidget *status; /* Dropdown with the statuses */
+ GntWidget *statustext; /* Status message */
int typing;
GntWidget *menu;
@@ -101,8 +96,8 @@ typedef struct
typedef struct
{
- gpointer row; /* the row in the GntTree */
- guint signed_timer; /* used when 'recently' signed on/off */
+ gpointer row; /* the row in the GntTree */
+ guint signed_timer; /* used when 'recently' signed on/off */
} FinchBlistNode;
typedef enum
@@ -159,7 +154,7 @@ static int color_away;
static int color_offline;
static int color_idle;
-/**
+/*
* Buddy List Manager functions.
*/
@@ -167,9 +162,9 @@ static gboolean default_can_add_node(PurpleBlistNode *node)
{
gboolean offline = purple_prefs_get_bool(PREF_ROOT "/showoffline");
- if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ if (PURPLE_IS_BUDDY(node)) {
PurpleBuddy *buddy = (PurpleBuddy*)node;
- FinchBlistNode *fnode = FINCH_GET_DATA(node);
+ FinchBlistNode *fnode = purple_blist_node_get_ui_data(node);
if (!purple_buddy_get_contact(buddy))
return FALSE; /* When a new buddy is added and show-offline is set */
if (PURPLE_BUDDY_IS_ONLINE(buddy))
@@ -182,18 +177,18 @@ static gboolean default_can_add_node(PurpleBlistNode *node)
return TRUE; /* Show if the buddy just signed off */
if (purple_blist_node_get_bool(node, "show_offline"))
return TRUE;
- } else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ } else if (PURPLE_IS_CONTACT(node)) {
PurpleBlistNode *nd;
for (nd = purple_blist_node_get_first_child(node);
nd; nd = purple_blist_node_get_sibling_next(nd)) {
if (default_can_add_node(nd))
return TRUE;
}
- } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if (PURPLE_IS_CHAT(node)) {
PurpleChat *chat = (PurpleChat*)node;
if (purple_account_is_connected(purple_chat_get_account(chat)))
return TRUE; /* Show whenever the account is online */
- } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ } else if (PURPLE_IS_GROUP(node)) {
PurpleBlistNode *nd;
gboolean empty = purple_prefs_get_bool(PREF_ROOT "/emptygroups");
if (empty)
@@ -215,17 +210,13 @@ static gboolean default_can_add_node(PurpleBlistNode *node)
static gpointer default_find_parent(PurpleBlistNode *node)
{
gpointer ret = NULL;
- switch (purple_blist_node_get_type(node)) {
- case PURPLE_BLIST_BUDDY_NODE:
- case PURPLE_BLIST_CONTACT_NODE:
- case PURPLE_BLIST_CHAT_NODE:
- ret = purple_blist_node_get_parent(node);
- break;
- default:
- break;
- }
+
+ if (PURPLE_IS_BUDDY(node) || PURPLE_IS_CONTACT(node) || PURPLE_IS_CHAT(node))
+ ret = purple_blist_node_get_parent(node);
+
if (ret)
add_node(ret, ggblist);
+
return ret;
}
@@ -236,13 +227,13 @@ static gboolean default_create_tooltip(gpointer selected_row, GString **body, ch
int lastseen = 0;
char *title;
- if (!node ||
- purple_blist_node_get_type(node) == PURPLE_BLIST_OTHER_NODE)
+ 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_BLIST_NODE_IS_CONTACT(node)) {
+ if (PURPLE_IS_CONTACT(node)) {
PurpleBuddy *pr = purple_contact_get_priority_buddy((PurpleContact*)node);
gboolean offline = !PURPLE_BUDDY_IS_ONLINE(pr);
gboolean showoffline = purple_prefs_get_bool(PREF_ROOT "/showoffline");
@@ -266,21 +257,21 @@ static gboolean default_create_tooltip(gpointer selected_row, GString **body, ch
str = g_string_append(str, "\n----------\n");
tooltip_for_buddy(buddy, str, FALSE);
}
- } else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ } else if (PURPLE_IS_BUDDY(node)) {
PurpleBuddy *buddy = (PurpleBuddy *)node;
tooltip_for_buddy(buddy, str, TRUE);
title = g_strdup(purple_buddy_get_name(buddy));
if (!PURPLE_BUDDY_IS_ONLINE((PurpleBuddy*)node))
lastseen = purple_blist_node_get_int(node, "last_seen");
- } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ } else if (PURPLE_IS_GROUP(node)) {
PurpleGroup *group = (PurpleGroup *)node;
g_string_append_printf(str, _("Online: %d\nTotal: %d"),
- purple_blist_get_group_online_count(group),
- purple_blist_get_group_size(group, FALSE));
+ purple_counting_node_get_online_count(PURPLE_COUNTING_NODE(group)),
+ purple_counting_node_get_current_size(PURPLE_COUNTING_NODE(group)));
title = g_strdup(purple_group_get_name(group));
- } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if (PURPLE_IS_CHAT(node)) {
PurpleChat *chat = (PurpleChat *)node;
PurpleAccount *account = purple_chat_get_account(chat);
@@ -329,11 +320,11 @@ static GList *managers;
static FinchBlistNode *
create_finch_blist_node(PurpleBlistNode *node, gpointer row)
{
- FinchBlistNode *fnode = FINCH_GET_DATA(node);
+ FinchBlistNode *fnode = purple_blist_node_get_ui_data(node);
if (!fnode) {
fnode = g_new0(FinchBlistNode, 1);
fnode->signed_timer = 0;
- FINCH_SET_DATA(node, fnode);
+ purple_blist_node_set_ui_data(node, fnode);
}
fnode->row = row;
return fnode;
@@ -342,13 +333,13 @@ create_finch_blist_node(PurpleBlistNode *node, gpointer row)
static void
reset_blist_node_ui_data(PurpleBlistNode *node)
{
- FinchBlistNode *fnode = FINCH_GET_DATA(node);
+ FinchBlistNode *fnode = purple_blist_node_get_ui_data(node);
if (fnode == NULL)
return;
if (fnode->signed_timer)
purple_timeout_remove(fnode->signed_timer);
g_free(fnode);
- FINCH_SET_DATA(node, NULL);
+ purple_blist_node_set_ui_data(node, NULL);
}
static int
@@ -357,9 +348,9 @@ get_display_color(PurpleBlistNode *node)
PurpleBuddy *buddy;
int color = 0;
- if (PURPLE_BLIST_NODE_IS_CONTACT(node))
+ if (PURPLE_IS_CONTACT(node))
node = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node)));
- if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if (!PURPLE_IS_BUDDY(node))
return 0;
buddy = (PurpleBuddy*)node;
@@ -381,19 +372,19 @@ static GntTextFormatFlags
get_blist_node_flag(PurpleBlistNode *node)
{
GntTextFormatFlags flag = 0;
- FinchBlistNode *fnode = FINCH_GET_DATA(node);
+ FinchBlistNode *fnode = purple_blist_node_get_ui_data(node);
if (ggblist->tagged && g_list_find(ggblist->tagged, node))
flag |= GNT_TEXT_FLAG_BOLD;
if (fnode && fnode->signed_timer)
flag |= GNT_TEXT_FLAG_BLINK;
- else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ else if (PURPLE_IS_CONTACT(node)) {
node = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node)));
- fnode = FINCH_GET_DATA(node);
+ fnode = purple_blist_node_get_ui_data(node);
if (fnode && fnode->signed_timer)
flag |= GNT_TEXT_FLAG_BLINK;
- } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ } else if (PURPLE_IS_GROUP(node)) {
/* If the node is collapsed, then check to see if any of the priority buddies of
* any of the contacts within this group recently signed on/off, and set the blink
* flag appropriately. */
@@ -406,7 +397,7 @@ get_blist_node_flag(PurpleBlistNode *node)
node = purple_blist_node_get_sibling_next(node)) {
PurpleBlistNode *pnode;
pnode = purple_contact_get_priority_buddy((PurpleContact*)node);
- fnode = FINCH_GET_DATA(node);
+ fnode = purple_blist_node_get_ui_data(node);
if (fnode && fnode->signed_timer) {
flag |= GNT_TEXT_FLAG_BLINK;
break;
@@ -433,7 +424,7 @@ is_contact_online(PurpleContact *contact)
PurpleBlistNode *node;
for (node = purple_blist_node_get_first_child(((PurpleBlistNode*)contact)); node;
node = purple_blist_node_get_sibling_next(node)) {
- FinchBlistNode *fnode = FINCH_GET_DATA(node);
+ FinchBlistNode *fnode = purple_blist_node_get_ui_data(node);
if (PURPLE_BUDDY_IS_ONLINE((PurpleBuddy*)node) ||
(fnode && fnode->signed_timer))
return TRUE;
@@ -447,7 +438,7 @@ is_group_online(PurpleGroup *group)
PurpleBlistNode *node;
for (node = purple_blist_node_get_first_child(((PurpleBlistNode*)group)); node;
node = purple_blist_node_get_sibling_next(node)) {
- if (PURPLE_BLIST_NODE_IS_CHAT(node) &&
+ if (PURPLE_IS_CHAT(node) &&
purple_account_is_connected(((PurpleChat *)node)->account))
return TRUE;
else if (is_contact_online((PurpleContact*)node))
@@ -465,19 +456,19 @@ new_node(PurpleBlistNode *node)
static void
add_node(PurpleBlistNode *node, FinchBlist *ggblist)
{
- if (FINCH_GET_DATA(node))
+ if (purple_blist_node_get_ui_data(node))
return;
if (!ggblist->manager->can_add_node(node))
return;
- if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if (PURPLE_IS_BUDDY(node))
add_buddy((PurpleBuddy*)node, ggblist);
- else if (PURPLE_BLIST_NODE_IS_CONTACT(node))
+ else if (PURPLE_IS_CONTACT(node))
add_contact((PurpleContact*)node, ggblist);
- else if (PURPLE_BLIST_NODE_IS_GROUP(node))
+ else if (PURPLE_IS_GROUP(node))
add_group((PurpleGroup*)node, ggblist);
- else if (PURPLE_BLIST_NODE_IS_CHAT(node))
+ else if (PURPLE_IS_CHAT(node))
add_chat((PurpleChat *)node, ggblist);
draw_tooltip(ggblist);
@@ -502,10 +493,10 @@ node_remove(PurpleBuddyList *list, PurpleBlistNode *node)
FinchBlist *ggblist = FINCH_GET_DATA(list);
PurpleBlistNode *parent;
- if (ggblist == NULL || FINCH_GET_DATA(node) == NULL)
+ if (ggblist == NULL || purple_blist_node_get_ui_data(node) == NULL)
return;
- if (PURPLE_BLIST_NODE_IS_GROUP(node) && ggblist->new_group) {
+ if (PURPLE_IS_GROUP(node) && ggblist->new_group) {
ggblist->new_group = g_list_remove(ggblist->new_group, node);
}
@@ -543,7 +534,7 @@ node_update(PurpleBuddyList *list, PurpleBlistNode *node)
if (ggblist->window == NULL)
return;
- if (FINCH_GET_DATA(node)!= NULL) {
+ if (purple_blist_node_get_ui_data(node)!= NULL) {
gnt_tree_change_text(GNT_TREE(ggblist->tree), node,
0, get_display_name(node));
gnt_tree_sort_row(GNT_TREE(ggblist->tree), node);
@@ -553,19 +544,19 @@ node_update(PurpleBuddyList *list, PurpleBlistNode *node)
node_remove(list, node);
}
- if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ if (PURPLE_IS_BUDDY(node)) {
PurpleBuddy *buddy = (PurpleBuddy*)node;
add_node((PurpleBlistNode*)buddy, FINCH_GET_DATA(list));
node_update(list, purple_blist_node_get_parent(node));
- } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if (PURPLE_IS_CHAT(node)) {
add_node(node, FINCH_GET_DATA(list));
- } else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
- if (FINCH_GET_DATA(node)== NULL) {
+ } 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));
}
- } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ } else if (PURPLE_IS_GROUP(node)) {
if (!ggblist->manager->can_add_node(node))
node_remove(list, node);
else
@@ -607,7 +598,7 @@ remove_new_empty_group(gpointer data)
if (!ggblist)
return FALSE;
- list = purple_get_blist();
+ list = purple_blist_get_buddy_list();
g_return_val_if_fail(list, FALSE);
ggblist->new_group_timeout = 0;
@@ -644,11 +635,12 @@ add_buddy_cb(void *data, PurpleRequestFields *allfields)
if (error)
{
finch_request_add_buddy(account, username, group, alias);
- purple_notify_error(NULL, _("Error"), _("Error adding buddy"), error);
+ purple_notify_error(NULL, _("Error"), _("Error adding buddy"),
+ error, purple_request_cpar_from_account(account));
return;
}
- grp = purple_find_group(group);
+ grp = purple_blist_find_group(group);
if (!grp)
{
grp = purple_group_new(group);
@@ -657,13 +649,13 @@ add_buddy_cb(void *data, PurpleRequestFields *allfields)
/* XXX: Ask to merge if there's already a buddy with the same alias in the same group (#4553) */
- if ((buddy = purple_find_buddy_in_group(account, username, grp)) == NULL)
+ if ((buddy = purple_blist_find_buddy_in_group(account, username, grp)) == NULL)
{
buddy = purple_buddy_new(account, username, alias);
purple_blist_add_buddy(buddy, NULL, grp, NULL);
}
- purple_account_add_buddy_with_invite(account, buddy, invite);
+ purple_account_add_buddy(account, buddy, invite);
}
static void
@@ -698,7 +690,7 @@ finch_request_add_buddy(PurpleAccount *account, const char *username, const char
fields,
_("Add"), G_CALLBACK(add_buddy_cb),
_("Cancel"), NULL,
- account, NULL, NULL,
+ purple_request_cpar_from_account(account),
NULL);
}
@@ -707,24 +699,16 @@ join_chat(PurpleChat *chat)
{
PurpleAccount *account = purple_chat_get_account(chat);
const char *name;
- PurpleConversation *conv;
- const char *alias;
-
- /* This hack here is to work around the fact that there's no good way of
- * getting the actual name of a chat. I don't understand why we return
- * the alias for a chat when all we want is the name. */
- alias = chat->alias;
- chat->alias = NULL;
- name = purple_chat_get_name(chat);
- conv = purple_find_conversation_with_account(
- PURPLE_CONV_TYPE_CHAT, name, account);
- chat->alias = (char *)alias;
-
- if (!conv || purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv))) {
- serv_join_chat(purple_account_get_connection(account),
+ PurpleChatConversation *conv;
+
+ name = purple_chat_get_name_only(chat);
+ conv = purple_conversations_find_chat_with_account(name, account);
+
+ if (!conv || purple_chat_conversation_has_left(conv)) {
+ purple_serv_join_chat(purple_account_get_connection(account),
purple_chat_get_components(chat));
} else if (conv) {
- purple_conversation_present(conv);
+ purple_conversation_present(PURPLE_CONVERSATION(conv));
}
}
@@ -760,12 +744,12 @@ add_chat_cb(void *data, PurpleRequestFields *allfields)
chat = purple_chat_new(account, name, hash);
if (chat != NULL) {
- if ((grp = purple_find_group(group)) == NULL) {
+ if ((grp = purple_blist_find_group(group)) == NULL) {
grp = purple_group_new(group);
purple_blist_add_group(grp, NULL);
}
purple_blist_add_chat(chat, grp, NULL);
- purple_blist_alias_chat(chat, alias);
+ purple_chat_set_alias(chat, alias);
purple_blist_node_set_bool((PurpleBlistNode*)chat, "gnt-autojoin", autojoin);
if (autojoin) {
join_chat(chat);
@@ -804,8 +788,7 @@ finch_request_add_chat(PurpleAccount *account, PurpleGroup *grp, const char *ali
purple_request_fields(NULL, _("Add Chat"), NULL,
_("You can edit more information from the context menu later."),
fields, _("Add"), G_CALLBACK(add_chat_cb), _("Cancel"), NULL,
- NULL, NULL, NULL,
- NULL);
+ NULL, NULL);
}
static void
@@ -815,11 +798,11 @@ add_group_cb(gpointer null, const char *group)
if (!group || !*group) {
purple_notify_error(NULL, _("Error"), _("Error adding group"),
- _("You must give a name for the group to add."));
+ _("You must give a name for the group to add."), NULL);
return;
}
- grp = purple_find_group(group);
+ grp = purple_blist_find_group(group);
if (!grp) {
grp = purple_group_new(group);
purple_blist_add_group(grp, NULL);
@@ -839,7 +822,7 @@ add_group_cb(gpointer null, const char *group)
/* Select the group */
if (ggblist->tree) {
- FinchBlistNode *fnode = FINCH_GET_DATA((PurpleBlistNode*)grp);
+ FinchBlistNode *fnode = purple_blist_node_get_ui_data((PurpleBlistNode*)grp);
if (!fnode)
add_node((PurpleBlistNode*)grp, ggblist);
gnt_tree_set_selected(GNT_TREE(ggblist->tree), grp);
@@ -852,8 +835,7 @@ 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, NULL,
- NULL);
+ NULL, NULL);
}
static PurpleBlistUiOps blist_ui_ops =
@@ -871,7 +853,7 @@ static PurpleBlistUiOps blist_ui_ops =
NULL,
NULL,
NULL,
- NULL
+ NULL, NULL, NULL, NULL
};
static gpointer
@@ -887,7 +869,7 @@ add_group(PurpleGroup *group, FinchBlist *ggblist)
{
gpointer parent;
PurpleBlistNode *node = (PurpleBlistNode *)group;
- if (FINCH_GET_DATA(node))
+ if (purple_blist_node_get_ui_data(node))
return;
parent = ggblist->manager->find_parent((PurpleBlistNode*)group);
create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), group,
@@ -904,13 +886,13 @@ get_display_name(PurpleBlistNode *node)
char status[8] = " ";
const char *name = NULL;
- if (PURPLE_BLIST_NODE_IS_CONTACT(node))
+ if (PURPLE_IS_CONTACT(node))
node = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node))); /* XXX: this can return NULL?! */
if (node == NULL)
return NULL;
- if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if (PURPLE_IS_BUDDY(node))
{
PurpleBuddy *buddy = (PurpleBuddy *)node;
PurpleStatusPrimitive prim;
@@ -924,7 +906,7 @@ get_display_name(PurpleBlistNode *node)
else {
now = purple_presence_get_active_status(presence);
- prim = purple_status_type_get_primitive(purple_status_get_type(now));
+ prim = purple_status_type_get_primitive(purple_status_get_status_type(now));
switch(prim) {
case PURPLE_STATUS_OFFLINE:
@@ -940,14 +922,14 @@ get_display_name(PurpleBlistNode *node)
}
name = purple_buddy_get_alias(buddy);
}
- else if (PURPLE_BLIST_NODE_IS_CHAT(node))
+ else if (PURPLE_IS_CHAT(node))
{
PurpleChat *chat = (PurpleChat*)node;
name = purple_chat_get_name(chat);
strncpy(status, "~", sizeof(status) - 1);
}
- else if (PURPLE_BLIST_NODE_IS_GROUP(node))
+ else if (PURPLE_IS_GROUP(node))
return purple_group_get_name((PurpleGroup*)node);
g_snprintf(text, sizeof(text) - 1, "%s %s", status, name);
@@ -960,7 +942,7 @@ add_chat(PurpleChat *chat, FinchBlist *ggblist)
{
gpointer parent;
PurpleBlistNode *node = (PurpleBlistNode *)chat;
- if (FINCH_GET_DATA(node))
+ if (purple_blist_node_get_ui_data(node))
return;
if (!purple_account_is_connected(purple_chat_get_account(chat)))
return;
@@ -979,7 +961,7 @@ add_contact(PurpleContact *contact, FinchBlist *ggblist)
PurpleBlistNode *node = (PurpleBlistNode*)contact;
const char *name;
- if (FINCH_GET_DATA(node))
+ if (purple_blist_node_get_ui_data(node))
return;
name = get_display_name(node);
@@ -1002,7 +984,7 @@ add_buddy(PurpleBuddy *buddy, FinchBlist *ggblist)
PurpleBlistNode *node = (PurpleBlistNode *)buddy;
PurpleContact *contact;
- if (FINCH_GET_DATA(node))
+ if (purple_blist_node_get_ui_data(node))
return;
contact = purple_buddy_get_contact(buddy);
@@ -1027,7 +1009,7 @@ buddy_signed_on(PurpleBuddy *buddy, FinchBlist *ggblist)
static void
buddy_signed_off(PurpleBuddy *buddy, FinchBlist *ggblist)
{
- node_remove(purple_get_blist(), (PurpleBlistNode*)buddy);
+ node_remove(purple_blist_get_buddy_list(), (PurpleBlistNode*)buddy);
}
#endif
@@ -1045,70 +1027,31 @@ selection_activate(GntWidget *widget, FinchBlist *ggblist)
if (!node)
return;
- if (PURPLE_BLIST_NODE_IS_CONTACT(node))
+ if (PURPLE_IS_CONTACT(node))
node = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node)));
- if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if (PURPLE_IS_BUDDY(node))
{
PurpleBuddy *buddy = (PurpleBuddy *)node;
- PurpleConversation *conv;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
- purple_buddy_get_name(buddy),
+ PurpleIMConversation *im;
+ im = purple_conversations_find_im_with_account(purple_buddy_get_name(buddy),
purple_buddy_get_account(buddy));
- if (!conv) {
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM,
- purple_buddy_get_account(buddy),
+ if (!im) {
+ im = purple_im_conversation_new(purple_buddy_get_account(buddy),
purple_buddy_get_name(buddy));
} else {
- FinchConv *ggconv = FINCH_GET_DATA(conv);
+ FinchConv *ggconv = FINCH_CONV(PURPLE_CONVERSATION(im));
gnt_window_present(ggconv->window);
}
- finch_conversation_set_active(conv);
+ finch_conversation_set_active(PURPLE_CONVERSATION(im));
}
- else if (PURPLE_BLIST_NODE_IS_CHAT(node))
+ else if (PURPLE_IS_CHAT(node))
{
join_chat((PurpleChat*)node);
}
}
static void
-context_menu_callback(GntMenuItem *item, gpointer data)
-{
- PurpleMenuAction *action = data;
- PurpleBlistNode *node = ggblist->cnode;
- if (action) {
- void (*callback)(PurpleBlistNode *, gpointer);
- callback = (void (*)(PurpleBlistNode *, gpointer))action->callback;
- if (callback)
- callback(node, action->data);
- else
- return;
- }
-}
-
-static void
-gnt_append_menu_action(GntMenu *menu, PurpleMenuAction *action, gpointer parent)
-{
- GList *list;
- GntMenuItem *item;
-
- if (action == NULL)
- return;
-
- item = gnt_menuitem_new(action->label);
- if (action->callback)
- gnt_menuitem_set_callback(GNT_MENU_ITEM(item), context_menu_callback, action);
- gnt_menu_add_item(menu, GNT_MENU_ITEM(item));
-
- if (action->children) {
- GntWidget *sub = gnt_menu_new(GNT_MENU_POPUP);
- gnt_menuitem_set_submenu(item, GNT_MENU(sub));
- for (list = action->children; list; list = list->next)
- gnt_append_menu_action(GNT_MENU(sub), list->data, action);
- }
-}
-
-static void
append_proto_menu(GntMenu *menu, PurpleConnection *gc, PurpleBlistNode *node)
{
GList *list;
@@ -1123,10 +1066,8 @@ append_proto_menu(GntMenu *menu, PurpleConnection *gc, PurpleBlistNode *node)
PurpleMenuAction *act = (PurpleMenuAction *) list->data;
if (!act)
continue;
- act->data = node;
- gnt_append_menu_action(menu, act, NULL);
- g_signal_connect_swapped(G_OBJECT(menu), "destroy",
- G_CALLBACK(purple_menu_action_free), act);
+ purple_menu_action_set_data(act, node);
+ finch_append_menu_action(menu, act, node);
}
}
@@ -1135,9 +1076,7 @@ add_custom_action(GntMenu *menu, const char *label, PurpleCallback callback,
gpointer data)
{
PurpleMenuAction *action = purple_menu_action_new(label, callback, data, NULL);
- gnt_append_menu_action(menu, action, NULL);
- g_signal_connect_swapped(G_OBJECT(menu), "destroy",
- G_CALLBACK(purple_menu_action_free), action);
+ finch_append_menu_action(menu, action, NULL);
}
static void
@@ -1153,7 +1092,7 @@ chat_components_edit_ok(PurpleChat *chat, PurpleRequestFields *allfields)
char *val;
id = purple_request_field_get_id(field);
- if (purple_request_field_get_type(field) == PURPLE_REQUEST_FIELD_INTEGER)
+ if (purple_request_field_get_field_type(field) == PURPLE_REQUEST_FIELD_INTEGER)
val = g_strdup_printf("%d", purple_request_field_int_get_value(field));
else
val = g_strdup(purple_request_field_string_get_value(field));
@@ -1189,7 +1128,7 @@ chat_components_edit(PurpleBlistNode *selected, PurpleChat *chat)
const char *str = g_hash_table_lookup(purple_chat_get_components(chat), pce->identifier);
if (!str || sscanf(str, "%d", &val) != 1)
val = pce->min;
- field = purple_request_field_int_new(pce->identifier, pce->label, val);
+ field = purple_request_field_int_new(pce->identifier, pce->label, val, INT_MIN, INT_MAX);
} else {
field = purple_request_field_string_new(pce->identifier, pce->label,
g_hash_table_lookup(purple_chat_get_components(chat), pce->identifier), FALSE);
@@ -1208,15 +1147,14 @@ chat_components_edit(PurpleBlistNode *selected, PurpleChat *chat)
purple_request_fields(NULL, _("Edit Chat"), NULL, _("Please Update the necessary fields."),
fields, _("Edit"), G_CALLBACK(chat_components_edit_ok), _("Cancel"), NULL,
- NULL, NULL, NULL,
- chat);
+ NULL, chat);
}
static void
autojoin_toggled(GntMenuItem *item, gpointer data)
{
PurpleMenuAction *action = data;
- purple_blist_node_set_bool(action->data, "gnt-autojoin",
+ purple_blist_node_set_bool(purple_menu_action_get_data(action), "gnt-autojoin",
gnt_menuitem_check_get_checked(GNT_MENU_ITEM_CHECK(item)));
}
@@ -1224,7 +1162,8 @@ static void
create_chat_menu(GntMenu *menu, PurpleChat *chat)
{
PurpleMenuAction *action = purple_menu_action_new(_("Auto-join"), NULL, chat, NULL);
- GntMenuItem *check = gnt_menuitem_check_new(action->label);
+ GntMenuItem *check = gnt_menuitem_check_new(
+ purple_menu_action_get_label(action));
gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(check),
purple_blist_node_get_bool((PurpleBlistNode*)chat, "gnt-autojoin"));
gnt_menu_add_item(menu, check);
@@ -1268,11 +1207,11 @@ gpointer finch_retrieve_user_info(PurpleConnection *conn, const char *name)
{
PurpleNotifyUserInfo *info = purple_notify_user_info_new();
gpointer uihandle;
- purple_notify_user_info_add_pair(info, _("Information"), _("Retrieving..."));
+ purple_notify_user_info_add_pair_plaintext(info, _("Information"), _("Retrieving..."));
uihandle = purple_notify_userinfo(conn, name, info, NULL, NULL);
purple_notify_user_info_destroy(info);
- serv_get_info(conn, name);
+ purple_serv_get_info(conn, name);
return uihandle;
}
@@ -1285,14 +1224,14 @@ finch_blist_get_buddy_info_cb(PurpleBlistNode *selected, PurpleBuddy *buddy)
static void
finch_blist_menu_send_file_cb(PurpleBlistNode *selected, PurpleBuddy *buddy)
{
- serv_send_file(purple_account_get_connection(purple_buddy_get_account(buddy)), purple_buddy_get_name(buddy), NULL);
+ purple_serv_send_file(purple_account_get_connection(purple_buddy_get_account(buddy)), purple_buddy_get_name(buddy), NULL);
}
static void
finch_blist_pounce_node_cb(PurpleBlistNode *selected, PurpleBlistNode *node)
{
PurpleBuddy *b;
- if (PURPLE_BLIST_NODE_IS_CONTACT(node))
+ if (PURPLE_IS_CONTACT(node))
b = purple_contact_get_priority_buddy((PurpleContact *)node);
else
b = (PurpleBuddy *)node;
@@ -1306,8 +1245,8 @@ toggle_block_buddy(GntMenuItem *item, gpointer buddy)
PurpleAccount *account = purple_buddy_get_account(buddy);
const char *name = purple_buddy_get_name(buddy);
- block ? purple_privacy_deny(account, name, FALSE, FALSE) :
- purple_privacy_allow(account, name, FALSE, FALSE);
+ block ? purple_account_privacy_deny(account, name) :
+ purple_account_privacy_allow(account, name);
}
static void
@@ -1316,9 +1255,9 @@ toggle_show_offline(GntMenuItem *item, gpointer buddy)
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_get_blist(), buddy);
+ node_remove(purple_blist_get_buddy_list(), buddy);
else
- node_update(purple_get_blist(), buddy);
+ node_update(purple_blist_get_buddy_list(), buddy);
}
static void
@@ -1349,7 +1288,7 @@ create_buddy_menu(GntMenu *menu, PurpleBuddy *buddy)
}
account = purple_buddy_get_account(buddy);
- permitted = purple_privacy_check(account, purple_buddy_get_name(buddy));
+ permitted = purple_account_privacy_check(account, purple_buddy_get_name(buddy));
item = gnt_menuitem_check_new(_("Blocked"));
gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item), !permitted);
@@ -1375,9 +1314,7 @@ append_extended_menu(GntMenu *menu, PurpleBlistNode *node)
for (iter = purple_blist_node_get_extended_menu(node);
iter; iter = g_list_delete_link(iter, iter))
{
- gnt_append_menu_action(menu, iter->data, NULL);
- g_signal_connect_swapped(G_OBJECT(menu), "destroy",
- G_CALLBACK(purple_menu_action_free), iter->data);
+ finch_append_menu_action(menu, iter->data, node);
}
}
@@ -1406,19 +1343,19 @@ rename_blist_node(PurpleBlistNode *node, const char *newname)
if (name && !*name)
name = NULL;
- if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ if (PURPLE_IS_CONTACT(node)) {
PurpleContact *contact = (PurpleContact*)node;
PurpleBuddy *buddy = purple_contact_get_priority_buddy(contact);
- purple_blist_alias_contact(contact, name);
- purple_blist_alias_buddy(buddy, name);
- serv_alias_buddy(buddy);
- } else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
- purple_blist_alias_buddy((PurpleBuddy*)node, name);
- serv_alias_buddy((PurpleBuddy*)node);
- } else if (PURPLE_BLIST_NODE_IS_CHAT(node))
- purple_blist_alias_chat((PurpleChat*)node, name);
- else if (PURPLE_BLIST_NODE_IS_GROUP(node) && (name != NULL))
- purple_blist_rename_group((PurpleGroup*)node, name);
+ purple_contact_set_alias(contact, name);
+ purple_buddy_set_local_alias(buddy, name);
+ purple_serv_alias_buddy(buddy);
+ } else if (PURPLE_IS_BUDDY(node)) {
+ purple_buddy_set_local_alias((PurpleBuddy*)node, name);
+ purple_serv_alias_buddy((PurpleBuddy*)node);
+ } else if (PURPLE_IS_CHAT(node))
+ purple_chat_set_alias((PurpleChat*)node, name);
+ else if (PURPLE_IS_GROUP(node) && (name != NULL))
+ purple_group_set_name((PurpleGroup*)node, name);
else
g_return_if_reached();
}
@@ -1430,25 +1367,24 @@ finch_blist_rename_node_cb(PurpleBlistNode *selected, PurpleBlistNode *node)
char *prompt;
const char *text;
- if (PURPLE_BLIST_NODE_IS_CONTACT(node))
+ if (PURPLE_IS_CONTACT(node))
name = purple_contact_get_alias((PurpleContact*)node);
- else if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+ else if (PURPLE_IS_BUDDY(node))
name = purple_buddy_get_contact_alias((PurpleBuddy*)node);
- else if (PURPLE_BLIST_NODE_IS_CHAT(node))
+ else if (PURPLE_IS_CHAT(node))
name = purple_chat_get_name((PurpleChat*)node);
- else if (PURPLE_BLIST_NODE_IS_GROUP(node))
+ else if (PURPLE_IS_GROUP(node))
name = purple_group_get_name((PurpleGroup*)node);
else
g_return_if_reached();
prompt = g_strdup_printf(_("Please enter the new name for %s"), name);
- text = PURPLE_BLIST_NODE_IS_GROUP(node) ? _("Rename") : _("Set Alias");
+ text = PURPLE_IS_GROUP(node) ? _("Rename") : _("Set Alias");
purple_request_input(node, text, prompt, _("Enter empty string to reset the name."),
name, FALSE, FALSE, NULL, text, G_CALLBACK(rename_blist_node),
_("Cancel"), NULL,
- NULL, NULL, NULL,
- node);
+ NULL, node);
g_free(prompt);
}
@@ -1460,12 +1396,12 @@ static void showlog_cb(PurpleBlistNode *sel, PurpleBlistNode *node)
PurpleAccount *account;
char *name = NULL;
- if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ if (PURPLE_IS_BUDDY(node)) {
PurpleBuddy *b = (PurpleBuddy*) node;
type = PURPLE_LOG_IM;
name = g_strdup(purple_buddy_get_name(b));
account = purple_buddy_get_account(b);
- } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if (PURPLE_IS_CHAT(node)) {
PurpleChat *c = (PurpleChat*) node;
PurplePluginProtocolInfo *prpl_info = NULL;
type = PURPLE_LOG_CHAT;
@@ -1474,7 +1410,7 @@ static void showlog_cb(PurpleBlistNode *sel, PurpleBlistNode *node)
if (prpl_info && prpl_info->get_chat_name) {
name = prpl_info->get_chat_name(purple_chat_get_components(c));
}
- } else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ } else if (PURPLE_IS_CONTACT(node)) {
finch_log_show_contact((PurpleContact *)node);
return;
} else {
@@ -1499,12 +1435,12 @@ remove_group(PurpleGroup *group)
cnode = purple_blist_node_get_first_child(((PurpleBlistNode*)group));
while (cnode) {
- if (PURPLE_BLIST_NODE_IS_CONTACT(cnode)) {
+ if (PURPLE_IS_CONTACT(cnode)) {
bnode = purple_blist_node_get_first_child(cnode);
cnode = purple_blist_node_get_sibling_next(cnode);
while (bnode) {
PurpleBuddy *buddy;
- if (PURPLE_BLIST_NODE_IS_BUDDY(bnode)) {
+ if (PURPLE_IS_BUDDY(bnode)) {
PurpleAccount *account;
buddy = (PurpleBuddy*)bnode;
bnode = purple_blist_node_get_sibling_next(bnode);
@@ -1517,7 +1453,7 @@ remove_group(PurpleGroup *group)
bnode = purple_blist_node_get_sibling_next(bnode);
}
}
- } else if (PURPLE_BLIST_NODE_IS_CHAT(cnode)) {
+ } else if (PURPLE_IS_CHAT(cnode)) {
PurpleChat *chat = (PurpleChat *)cnode;
cnode = purple_blist_node_get_sibling_next(cnode);
if (purple_account_is_connected(purple_chat_get_account(chat)))
@@ -1533,16 +1469,16 @@ remove_group(PurpleGroup *group)
static void
finch_blist_remove_node(PurpleBlistNode *node)
{
- if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ if (PURPLE_IS_CONTACT(node)) {
remove_contact((PurpleContact*)node);
- } else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ } else if (PURPLE_IS_BUDDY(node)) {
PurpleBuddy *buddy = (PurpleBuddy*)node;
PurpleGroup *group = purple_buddy_get_group(buddy);
purple_account_remove_buddy(purple_buddy_get_account(buddy), buddy, group);
purple_blist_remove_buddy(buddy);
- } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if (PURPLE_IS_CHAT(node)) {
purple_blist_remove_chat((PurpleChat*)node);
- } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ } else if (PURPLE_IS_GROUP(node)) {
remove_group((PurpleGroup*)node);
}
}
@@ -1554,17 +1490,17 @@ finch_blist_remove_node_cb(PurpleBlistNode *selected, PurpleBlistNode *node)
char *primary;
const char *name, *sec = NULL;
- if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ if (PURPLE_IS_CONTACT(node)) {
PurpleContact *c = (PurpleContact*)node;
name = purple_contact_get_alias(c);
- if (c->totalsize > 1)
+ if (purple_counting_node_get_total_size(PURPLE_COUNTING_NODE(c)) > 1)
sec = _("Removing this contact will also remove all the buddies in the contact");
- } else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ } else if (PURPLE_IS_BUDDY(node)) {
name = purple_buddy_get_name((PurpleBuddy*)node);
account = purple_buddy_get_account((PurpleBuddy*)node);
- } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if (PURPLE_IS_CHAT(node)) {
name = purple_chat_get_name((PurpleChat*)node);
- } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ } else if (PURPLE_IS_GROUP(node)) {
name = purple_group_get_name((PurpleGroup*)node);
sec = _("Removing this group will also remove all the buddies in the group");
}
@@ -1577,7 +1513,7 @@ finch_blist_remove_node_cb(PurpleBlistNode *selected, PurpleBlistNode *node)
purple_request_action(node, _("Confirm Remove"),
primary, sec,
1,
- account, name, NULL,
+ purple_request_cpar_from_account(account),
node, 2,
_("Remove"), finch_blist_remove_node,
_("Cancel"), NULL);
@@ -1595,9 +1531,9 @@ finch_blist_toggle_tag_buddy(PurpleBlistNode *node)
} else {
ggblist->tagged = g_list_prepend(ggblist->tagged, node);
}
- if (PURPLE_BLIST_NODE_IS_CONTACT(node))
+ if (PURPLE_IS_CONTACT(node))
update_buddy_display(purple_contact_get_priority_buddy(PURPLE_CONTACT(node)), ggblist);
- else if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+ else if (PURPLE_IS_BUDDY(node))
update_buddy_display((PurpleBuddy*)node, ggblist);
else
update_node_display(node, ggblist);
@@ -1609,17 +1545,17 @@ finch_blist_place_tagged(PurpleBlistNode *target)
PurpleGroup *tg = NULL;
PurpleContact *tc = NULL;
- if (target == NULL ||
- purple_blist_node_get_type(target) == PURPLE_BLIST_OTHER_NODE)
+ if (target == NULL || !(PURPLE_IS_BUDDY(target) || PURPLE_IS_CONTACT(target) ||
+ PURPLE_IS_GROUP(target) || PURPLE_IS_CHAT(target)))
return;
- if (PURPLE_BLIST_NODE_IS_GROUP(target))
+ if (PURPLE_IS_GROUP(target))
tg = (PurpleGroup*)target;
- else if (PURPLE_BLIST_NODE_IS_BUDDY(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_BLIST_NODE_IS_CONTACT(target))
+ if (PURPLE_IS_CONTACT(target))
tc = (PurpleContact*)target;
tg = (PurpleGroup*)purple_blist_node_get_parent(target);
}
@@ -1631,38 +1567,38 @@ finch_blist_place_tagged(PurpleBlistNode *target)
PurpleBlistNode *node = list->data;
list = g_list_delete_link(list, list);
- if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ if (PURPLE_IS_GROUP(node)) {
update_node_display(node, ggblist);
/* Add the group after the current group */
purple_blist_add_group((PurpleGroup*)node, (PurpleBlistNode*)tg);
- } else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ } else if (PURPLE_IS_CONTACT(node)) {
update_buddy_display(purple_contact_get_priority_buddy((PurpleContact*)node), ggblist);
if (PURPLE_BLIST_NODE(tg) == target) {
/* The target is a group, just add the contact to the group. */
purple_blist_add_contact((PurpleContact*)node, tg, NULL);
} else if (tc) {
/* The target is either a buddy, or a contact. Merge with that contact. */
- purple_blist_merge_contact((PurpleContact*)node, (PurpleBlistNode*)tc);
+ purple_contact_merge((PurpleContact*)node, (PurpleBlistNode*)tc);
} else {
/* The target is a chat. Add the contact to the group after this chat. */
purple_blist_add_contact((PurpleContact*)node, NULL, target);
}
- } else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ } else if (PURPLE_IS_BUDDY(node)) {
update_buddy_display((PurpleBuddy*)node, ggblist);
if (PURPLE_BLIST_NODE(tg) == target) {
/* The target is a group. Add this buddy in a new contact under this group. */
purple_blist_add_buddy((PurpleBuddy*)node, NULL, tg, NULL);
- } else if (PURPLE_BLIST_NODE_IS_CONTACT(target)) {
+ } else if (PURPLE_IS_CONTACT(target)) {
/* Add to the contact. */
purple_blist_add_buddy((PurpleBuddy*)node, tc, NULL, NULL);
- } else if (PURPLE_BLIST_NODE_IS_BUDDY(target)) {
+ } else if (PURPLE_IS_BUDDY(target)) {
/* Add to the contact after the selected buddy. */
purple_blist_add_buddy((PurpleBuddy*)node, NULL, NULL, target);
- } else if (PURPLE_BLIST_NODE_IS_CHAT(target)) {
+ } else if (PURPLE_IS_CHAT(target)) {
/* Add to the selected chat's group. */
purple_blist_add_buddy((PurpleBuddy*)node, NULL, tg, NULL);
}
- } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if (PURPLE_IS_CHAT(node)) {
update_node_display(node, ggblist);
if (PURPLE_BLIST_NODE(tg) == target)
purple_blist_add_chat((PurpleChat*)node, tg, NULL);
@@ -1694,7 +1630,8 @@ draw_context_menu(FinchBlist *ggblist)
tree = GNT_TREE(ggblist->tree);
node = gnt_tree_get_selection_data(tree);
- if (node && purple_blist_node_get_type(node) == PURPLE_BLIST_OTHER_NODE)
+ if (node && !(PURPLE_IS_BUDDY(node) || PURPLE_IS_CONTACT(node) ||
+ PURPLE_IS_GROUP(node) || PURPLE_IS_CHAT(node)))
return;
if (ggblist->tooltip)
@@ -1709,19 +1646,19 @@ draw_context_menu(FinchBlist *ggblist)
if (!node) {
create_group_menu(GNT_MENU(context), NULL);
title = g_strdup(_("Buddy List"));
- } else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ } else if (PURPLE_IS_CONTACT(node)) {
ggblist->cnode = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node)));
create_buddy_menu(GNT_MENU(context), (PurpleBuddy*)ggblist->cnode);
title = g_strdup(purple_contact_get_alias((PurpleContact*)node));
- } else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ } else if (PURPLE_IS_BUDDY(node)) {
PurpleBuddy *buddy = (PurpleBuddy *)node;
create_buddy_menu(GNT_MENU(context), buddy);
title = g_strdup(purple_buddy_get_name(buddy));
- } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if (PURPLE_IS_CHAT(node)) {
PurpleChat *chat = (PurpleChat*)node;
create_chat_menu(GNT_MENU(context), chat);
title = g_strdup(purple_chat_get_name(chat));
- } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ } else if (PURPLE_IS_GROUP(node)) {
PurpleGroup *group = (PurpleGroup *)node;
create_group_menu(GNT_MENU(context), group);
title = g_strdup(purple_group_get_name(group));
@@ -1732,22 +1669,22 @@ draw_context_menu(FinchBlist *ggblist)
/* These are common for everything */
if (node) {
add_custom_action(GNT_MENU(context),
- PURPLE_BLIST_NODE_IS_GROUP(node) ? _("Rename") : _("Alias"),
+ PURPLE_IS_GROUP(node) ? _("Rename") : _("Alias"),
PURPLE_CALLBACK(finch_blist_rename_node_cb), node);
add_custom_action(GNT_MENU(context), _("Remove"),
PURPLE_CALLBACK(finch_blist_remove_node_cb), node);
- if (ggblist->tagged && (PURPLE_BLIST_NODE_IS_CONTACT(node)
- || PURPLE_BLIST_NODE_IS_GROUP(node))) {
+ if (ggblist->tagged && (PURPLE_IS_CONTACT(node)
+ || PURPLE_IS_GROUP(node))) {
add_custom_action(GNT_MENU(context), _("Place tagged"),
PURPLE_CALLBACK(finch_blist_place_tagged), node);
}
- if (PURPLE_BLIST_NODE_IS_BUDDY(node) || PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ if (PURPLE_IS_BUDDY(node) || PURPLE_IS_CONTACT(node)) {
add_custom_action(GNT_MENU(context), _("Toggle Tag"),
PURPLE_CALLBACK(finch_blist_toggle_tag_buddy), node);
}
- if (!PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ if (!PURPLE_IS_GROUP(node)) {
add_custom_action(GNT_MENU(context), _("View Log"),
PURPLE_CALLBACK(showlog_cb), node);
}
@@ -1783,15 +1720,13 @@ tooltip_for_buddy(PurpleBuddy *buddy, GString *str, gboolean full)
presence = purple_buddy_get_presence(buddy);
if (!full || g_utf8_collate(purple_buddy_get_name(buddy), alias)) {
- char *esc = g_markup_escape_text(alias, -1);
- purple_notify_user_info_add_pair(user_info, _("Nickname"), esc);
- g_free(esc);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Nickname"), alias);
}
tmp = g_strdup_printf("%s (%s)",
purple_account_get_username(account),
purple_account_get_protocol_name(account));
- purple_notify_user_info_add_pair(user_info, _("Account"), tmp);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Account"), tmp);
g_free(tmp);
prpl = purple_find_prpl(purple_account_get_protocol_id(account));
@@ -1806,7 +1741,7 @@ tooltip_for_buddy(PurpleBuddy *buddy, GString *str, gboolean full)
time_t idle = purple_presence_get_idle_time(pre);
if (idle > 0) {
char *st = purple_str_seconds_to_string(time(NULL) - idle);
- purple_notify_user_info_add_pair(user_info, _("Idle"), st);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Idle"), st);
g_free(st);
}
}
@@ -1945,7 +1880,7 @@ key_pressed(GntWidget *widget, const char *text, FinchBlist *ggblist)
} else if (strcmp(text, GNT_KEY_INS) == 0) {
PurpleBlistNode *node = gnt_tree_get_selection_data(GNT_TREE(ggblist->tree));
purple_blist_request_add_buddy(NULL, NULL,
- node && PURPLE_BLIST_NODE_IS_GROUP(node) ? purple_group_get_name(PURPLE_GROUP(node)) : NULL,
+ node && PURPLE_IS_GROUP(node) ? purple_group_get_name(PURPLE_GROUP(node)) : NULL,
NULL);
} else if (!gnt_tree_is_searching(GNT_TREE(ggblist->tree))) {
if (strcmp(text, "t") == 0) {
@@ -2028,7 +1963,7 @@ reset_blist_window(GntWidget *window, gpointer null)
{
PurpleBlistNode *node;
purple_signals_disconnect_by_handle(finch_blist_get_handle());
- FINCH_SET_DATA(purple_get_blist(), NULL);
+ FINCH_SET_DATA(purple_blist_get_buddy_list(), NULL);
node = purple_blist_get_root();
while (node) {
@@ -2071,7 +2006,7 @@ populate_buddylist(void)
(GCompareFunc)blist_node_compare_log);
}
- list = purple_get_blist();
+ list = purple_blist_get_buddy_list();
node = purple_blist_get_root();
while (node)
{
@@ -2240,7 +2175,7 @@ remove_typing_cb(gpointer null)
current = purple_savedstatus_get_current();
message = purple_savedstatus_get_message(current);
- prim = purple_savedstatus_get_type(current);
+ prim = purple_savedstatus_get_primitive_type(current);
newmessage = gnt_entry_get_text(GNT_ENTRY(ggblist->statustext));
item = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(ggblist->status));
@@ -2251,7 +2186,7 @@ remove_typing_cb(gpointer null)
newprim = item->u.prim;
break;
case STATUS_SAVED_POPULAR:
- newprim = purple_savedstatus_get_type(item->u.saved);
+ newprim = purple_savedstatus_get_primitive_type(item->u.saved);
break;
default:
goto end; /* 'New' or 'Saved' is selected, but this should never happen. */
@@ -2352,7 +2287,7 @@ savedstatus_changed(PurpleSavedStatus *now, PurpleSavedStatus *old)
g_signal_handlers_block_matched(ggblist->statustext, G_SIGNAL_MATCH_FUNC,
0, 0, NULL, status_text_changed, NULL);
- prim = purple_savedstatus_get_type(now);
+ prim = purple_savedstatus_get_primitive_type(now);
message = purple_savedstatus_get_message(now);
/* Rebuild the status dropdown */
@@ -2402,25 +2337,21 @@ blist_node_compare_text(PurpleBlistNode *n1, PurpleBlistNode *n2)
char *us1, *us2;
int ret;
- if (purple_blist_node_get_type(n1) != purple_blist_node_get_type(n2))
+ if (G_OBJECT_TYPE(n1) != G_OBJECT_TYPE(n2))
return blist_node_compare_position(n1, n2);
- switch (purple_blist_node_get_type(n1))
- {
- case PURPLE_BLIST_CHAT_NODE:
- s1 = purple_chat_get_name((PurpleChat*)n1);
- s2 = purple_chat_get_name((PurpleChat*)n2);
- break;
- case PURPLE_BLIST_BUDDY_NODE:
- return purple_presence_compare(purple_buddy_get_presence((PurpleBuddy*)n1),
- purple_buddy_get_presence((PurpleBuddy*)n2));
- break;
- case PURPLE_BLIST_CONTACT_NODE:
- s1 = purple_contact_get_alias((PurpleContact*)n1);
- s2 = purple_contact_get_alias((PurpleContact*)n2);
- break;
- default:
- return blist_node_compare_position(n1, n2);
+ if (PURPLE_IS_CHAT(n1)) {
+ s1 = purple_chat_get_name((PurpleChat*)n1);
+ s2 = purple_chat_get_name((PurpleChat*)n2);
+ } else if (PURPLE_IS_BUDDY(n1)) {
+ return purple_buddy_presence_compare(
+ PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(PURPLE_BUDDY(n1))),
+ PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(PURPLE_BUDDY(n2))));
+ } else if (PURPLE_IS_CONTACT(n1)) {
+ s1 = purple_contact_get_alias((PurpleContact*)n1);
+ s2 = purple_contact_get_alias((PurpleContact*)n2);
+ } else {
+ return blist_node_compare_position(n1, n2);
}
us1 = g_utf8_strup(s1, -1);
@@ -2437,23 +2368,22 @@ blist_node_compare_status(PurpleBlistNode *n1, PurpleBlistNode *n2)
{
int ret;
- if (purple_blist_node_get_type(n1) != purple_blist_node_get_type(n2))
+ if (G_OBJECT_TYPE(n1) != G_OBJECT_TYPE(n2))
return blist_node_compare_position(n1, n2);
- switch (purple_blist_node_get_type(n1)) {
- case PURPLE_BLIST_CONTACT_NODE:
- n1 = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(n1)));
- n2 = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(n2)));
- /* now compare the presence of the priority buddies */
- case PURPLE_BLIST_BUDDY_NODE:
- ret = purple_presence_compare(purple_buddy_get_presence((PurpleBuddy*)n1),
- purple_buddy_get_presence((PurpleBuddy*)n2));
- if (ret != 0)
- return ret;
- break;
- default:
- return blist_node_compare_position(n1, n2);
- break;
+ if (PURPLE_IS_CONTACT(n1))
+ n1 = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(n1)));
+ if (PURPLE_IS_CONTACT(n2))
+ n2 = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(n2)));
+
+ if (PURPLE_IS_BUDDY(n1) && PURPLE_IS_BUDDY(n2)) {
+ ret = purple_buddy_presence_compare(
+ PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(PURPLE_BUDDY(n1))),
+ PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(PURPLE_BUDDY(n2))));
+ if (ret != 0)
+ return ret;
+ } else {
+ return blist_node_compare_position(n1, n2);
}
/* Sort alphabetically if presence is not comparable */
@@ -2483,26 +2413,24 @@ blist_node_compare_log(PurpleBlistNode *n1, PurpleBlistNode *n2)
int ret;
PurpleBuddy *b1, *b2;
- if (purple_blist_node_get_type(n1) != purple_blist_node_get_type(n2))
+ if (G_OBJECT_TYPE(n1) != G_OBJECT_TYPE(n2))
return blist_node_compare_position(n1, n2);
- switch (purple_blist_node_get_type(n1)) {
- case PURPLE_BLIST_BUDDY_NODE:
- b1 = (PurpleBuddy*)n1;
- b2 = (PurpleBuddy*)n2;
- ret = purple_log_get_total_size(PURPLE_LOG_IM, purple_buddy_get_name(b2), purple_buddy_get_account(b2)) -
- purple_log_get_total_size(PURPLE_LOG_IM, purple_buddy_get_name(b1), purple_buddy_get_account(b1));
- if (ret != 0)
- return ret;
- break;
- case PURPLE_BLIST_CONTACT_NODE:
- ret = get_contact_log_size(n2) - get_contact_log_size(n1);
- if (ret != 0)
- return ret;
- break;
- default:
- return blist_node_compare_position(n1, n2);
+ if (PURPLE_IS_BUDDY(n1)) {
+ b1 = (PurpleBuddy*)n1;
+ b2 = (PurpleBuddy*)n2;
+ ret = purple_log_get_total_size(PURPLE_LOG_IM, purple_buddy_get_name(b2), purple_buddy_get_account(b2)) -
+ purple_log_get_total_size(PURPLE_LOG_IM, purple_buddy_get_name(b1), purple_buddy_get_account(b1));
+ if (ret != 0)
+ return ret;
+ } else if (PURPLE_IS_CONTACT(n1)) {
+ ret = get_contact_log_size(n2) - get_contact_log_size(n1);
+ if (ret != 0)
+ return ret;
+ } else {
+ return blist_node_compare_position(n1, n2);
}
+
ret = blist_node_compare_text(n1, n2);
return ret;
}
@@ -2543,19 +2471,20 @@ static gboolean
buddy_recent_signed_on_off(gpointer data)
{
PurpleBlistNode *node = data;
- FinchBlistNode *fnode = FINCH_GET_DATA(node);
+ FinchBlistNode *fnode = purple_blist_node_get_ui_data(node);
purple_timeout_remove(fnode->signed_timer);
fnode->signed_timer = 0;
if (!ggblist->manager->can_add_node(node)) {
- node_remove(purple_get_blist(), node);
+ node_remove(purple_blist_get_buddy_list(), node);
} else {
update_node_display(node, ggblist);
- if (purple_blist_node_get_parent(node) && PURPLE_BLIST_NODE_IS_CONTACT(purple_blist_node_get_parent(node)))
+ if (purple_blist_node_get_parent(node) && PURPLE_IS_CONTACT(purple_blist_node_get_parent(node)))
update_node_display(purple_blist_node_get_parent(node), ggblist);
}
+ g_object_unref(node);
return FALSE;
}
@@ -2563,15 +2492,17 @@ static gboolean
buddy_signed_on_off_cb(gpointer data)
{
PurpleBlistNode *node = data;
- FinchBlistNode *fnode = FINCH_GET_DATA(node);
+ FinchBlistNode *fnode = purple_blist_node_get_ui_data(node);
if (!ggblist || !fnode)
return FALSE;
if (fnode->signed_timer)
purple_timeout_remove(fnode->signed_timer);
+
+ g_object_ref(node);
fnode->signed_timer = purple_timeout_add_seconds(6, (GSourceFunc)buddy_recent_signed_on_off, data);
update_node_display(node, ggblist);
- if (purple_blist_node_get_parent(node) && PURPLE_BLIST_NODE_IS_CONTACT(purple_blist_node_get_parent(node)))
+ if (purple_blist_node_get_parent(node) && PURPLE_IS_CONTACT(purple_blist_node_get_parent(node)))
update_node_display(purple_blist_node_get_parent(node), ggblist);
return FALSE;
}
@@ -2682,11 +2613,11 @@ auto_join_chats(gpointer data)
for (node = purple_blist_get_root(); node;
node = purple_blist_node_next(node, FALSE)) {
- if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ if (PURPLE_IS_CHAT(node)) {
PurpleChat *chat = (PurpleChat*)node;
if (purple_chat_get_account(chat) == account &&
purple_blist_node_get_bool(node, "gnt-autojoin"))
- serv_join_chat(purple_account_get_connection(account), purple_chat_get_components(chat));
+ purple_serv_join_chat(purple_account_get_connection(account), purple_chat_get_components(chat));
}
}
return FALSE;
@@ -2715,10 +2646,10 @@ block_select_cb(gpointer data, PurpleRequestFields *fields)
PurpleAccount *account = purple_request_fields_get_account(fields, "account");
const char *name = purple_request_fields_get_string(fields, "screenname");
if (account && name && *name != '\0') {
- if (purple_request_fields_get_choice(fields, "block") == 1) {
- purple_privacy_deny(account, name, FALSE, FALSE);
+ if (GPOINTER_TO_INT(purple_request_fields_get_choice(fields, "block")) == 1) {
+ purple_account_privacy_deny(account, name);
} else {
- purple_privacy_allow(account, name, FALSE, FALSE);
+ purple_account_privacy_allow(account, name);
}
}
}
@@ -2748,20 +2679,19 @@ block_select(GntMenuItem *item, gpointer n)
purple_request_field_set_required(field, TRUE);
purple_request_field_group_add_field(group, field);
- field = purple_request_field_choice_new("block", _("Block/Unblock"), 1);
- purple_request_field_choice_add(field, _("Block"));
- purple_request_field_choice_add(field, _("Unblock"));
+ field = purple_request_field_choice_new("block", _("Block/Unblock"), GINT_TO_POINTER(1));
+ purple_request_field_choice_add(field, _("Block"), GINT_TO_POINTER(1));
+ purple_request_field_choice_add(field, _("Unblock"), GINT_TO_POINTER(2));
purple_request_field_group_add_field(group, field);
- purple_request_fields(purple_get_blist(), _("Block/Unblock"),
+ 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, NULL,
- NULL);
+ NULL, NULL);
}
/* send_im_select* -- Xerox */
@@ -2770,13 +2700,13 @@ send_im_select_cb(gpointer data, PurpleRequestFields *fields)
{
PurpleAccount *account;
const char *username;
- PurpleConversation *conv;
+ PurpleIMConversation *im;
account = purple_request_fields_get_account(fields, "account");
username = purple_request_fields_get_string(fields, "screenname");
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, username);
- purple_conversation_present(conv);
+ im = purple_im_conversation_new(account, username);
+ purple_conversation_present(PURPLE_CONVERSATION(im));
}
static void
@@ -2804,15 +2734,14 @@ send_im_select(GntMenuItem *item, gpointer n)
purple_request_field_set_required(field, TRUE);
purple_request_field_group_add_field(group, field);
- purple_request_fields(purple_get_blist(), _("New Instant Message"),
+ 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, NULL,
- NULL);
+ NULL, NULL);
}
static void
@@ -2823,7 +2752,7 @@ join_chat_select_cb(gpointer data, PurpleRequestFields *fields)
PurpleConnection *gc;
PurpleChat *chat;
GHashTable *hash = NULL;
- PurpleConversation *conv;
+ PurpleChatConversation *conv;
account = purple_request_fields_get_account(fields, "account");
name = purple_request_fields_get_string(fields, "chat");
@@ -2835,11 +2764,11 @@ join_chat_select_cb(gpointer data, PurpleRequestFields *fields)
/* Create a new conversation now. This will give focus to the new window.
* But it's necessary to pretend that we left the chat, because otherwise
* a new conversation window will pop up when we finally join the chat. */
- if (!(conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, name, account))) {
- conv = purple_conversation_new(PURPLE_CONV_TYPE_CHAT, account, name);
- purple_conv_chat_left(PURPLE_CONV_CHAT(conv));
+ if (!(conv = purple_conversations_find_chat_with_account(name, account))) {
+ conv = purple_chat_conversation_new(account, name);
+ purple_chat_conversation_leave(conv);
} else {
- purple_conversation_present(conv);
+ purple_conversation_present(PURPLE_CONVERSATION(conv));
}
chat = purple_blist_find_chat(account, name);
@@ -2850,7 +2779,7 @@ join_chat_select_cb(gpointer data, PurpleRequestFields *fields)
} else {
hash = purple_chat_get_components(chat);
}
- serv_join_chat(gc, hash);
+ purple_serv_join_chat(gc, hash);
if (chat == NULL && hash != NULL)
g_hash_table_destroy(hash);
}
@@ -2879,14 +2808,13 @@ join_chat_select(GntMenuItem *item, gpointer n)
purple_request_field_set_required(field, TRUE);
purple_request_field_group_add_field(group, field);
- purple_request_fields(purple_get_blist(), _("Join a Chat"),
+ 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, NULL,
- NULL);
+ NULL, NULL);
}
static void
@@ -2900,7 +2828,7 @@ view_log_select_cb(gpointer data, PurpleRequestFields *fields)
account = purple_request_fields_get_account(fields, "account");
name = purple_request_fields_get_string(fields, "screenname");
- buddy = purple_find_buddy(account, name);
+ buddy = purple_blist_find_buddy(account, name);
if (buddy) {
contact = purple_buddy_get_contact(buddy);
} else {
@@ -2940,15 +2868,14 @@ view_log_cb(GntMenuItem *item, gpointer n)
purple_request_field_group_add_field(group, field);
purple_request_field_account_set_show_all(field, TRUE);
- purple_request_fields(purple_get_blist(), _("View Log"),
+ 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, NULL,
- NULL);
+ NULL, NULL);
}
static void
@@ -3100,13 +3027,13 @@ create_menu(void)
void finch_blist_show()
{
- blist_show(purple_get_blist());
+ blist_show(purple_blist_get_buddy_list());
}
static void
group_collapsed(GntWidget *widget, PurpleBlistNode *node, gboolean collapsed, gpointer null)
{
- if (PURPLE_BLIST_NODE_IS_GROUP(node))
+ if (PURPLE_IS_GROUP(node))
purple_blist_node_set_bool(node, "collapsed", collapsed);
}
@@ -3274,3 +3201,40 @@ GntTree * finch_blist_get_tree(void)
return ggblist ? GNT_TREE(ggblist->tree) : NULL;
}
+/**************************************************************************
+ * GBoxed code
+ **************************************************************************/
+static FinchBlistManager *
+finch_blist_manager_copy(FinchBlistManager *manager)
+{
+ FinchBlistManager *manager_new;
+
+ g_return_val_if_fail(manager != NULL, NULL);
+
+ manager_new = g_new(FinchBlistManager, 1);
+ *manager_new = *manager;
+
+ return manager_new;
+}
+
+static void
+finch_blist_manager_free(FinchBlistManager *manager)
+{
+ g_return_if_fail(manager != NULL);
+
+ g_free(manager);
+}
+
+GType
+finch_blist_manager_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("FinchBlistManager",
+ (GBoxedCopyFunc)finch_blist_manager_copy,
+ (GBoxedFreeFunc)finch_blist_manager_free);
+ }
+
+ return type;
+}
diff --git a/finch/gntblist.h b/finch/gntblist.h
index a8037d597d..81f099fa2a 100644
--- a/finch/gntblist.h
+++ b/finch/gntblist.h
@@ -1,8 +1,3 @@
-/**
- * @file gntblist.h GNT BuddyList API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -23,144 +18,186 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _GNT_BLIST_H
#define _GNT_BLIST_H
+/**
+ * SECTION:gntblist
+ * @section_id: finch-gntblist
+ * @short_description: <filename>gntblist.h</filename>
+ * @title: Buddy List API
+ */
-#include "blist.h"
+#include "buddylist.h"
+#include "gnt.h"
#include "gnttree.h"
+#define FINCH_TYPE_BLIST_MANAGER (finch_blist_manager_get_type())
+
/**********************************************************************
- * @name GNT BuddyList API
+ * GNT BuddyList API
**********************************************************************/
-/*@{*/
+
+typedef struct _FinchBlistManager FinchBlistManager;
/**
+ * FinchBlistManager:
+ * @id: An identifier for the manager.
+ * @name: Displayable name for the manager.
+ * @init: Called right before it's being used.
+ * @uninit: Called right after it's not being used any more.
+ * @can_add_node: Whether a node should be added to the view.
+ * @find_parent: Find the parent row for a node.
+ * @create_tooltip: Create tooltip for a selected row.
+ *
* Buddylist manager for finch. This decides the visility, ordering and hierarchy
* of the buddylist nodes. This also manages the creation of tooltips.
*/
-typedef struct
+struct _FinchBlistManager
{
- const char *id; /**< An identifier for the manager. */
- const char *name; /**< Displayable name for the manager. */
- gboolean (*init)(void); /**< Called right before it's being used. */
- gboolean (*uninit)(void); /**< Called right after it's not being used any more. */
- gboolean (*can_add_node)(PurpleBlistNode *node); /**< Whether a node should be added to the view. */
- gpointer (*find_parent)(PurpleBlistNode *node); /**< Find the parent row for a node. */
- gboolean (*create_tooltip)(gpointer selected_row, GString **body, char **title); /**< Create tooltip for a selected row. */
+ const char *id;
+ const char *name;
+ gboolean (*init)(void);
+ gboolean (*uninit)(void);
+ gboolean (*can_add_node)(PurpleBlistNode *node);
+ gpointer (*find_parent)(PurpleBlistNode *node);
+ gboolean (*create_tooltip)(gpointer selected_row, GString **body, char **title);
+
+ /*< private >*/
gpointer reserved[4];
-} FinchBlistManager;
+};
+
+/**
+ * finch_blist_manager_get_type:
+ *
+ * Returns: The #GType for the #FinchBlistManager boxed structure.
+ */
+GType finch_blist_manager_get_type(void);
/**
+ * finch_blist_get_ui_ops:
+ *
* Get the ui-functions.
*
- * @return The PurpleBlistUiOps structure populated with the appropriate functions.
+ * Returns: The PurpleBlistUiOps structure populated with the appropriate functions.
*/
PurpleBlistUiOps * finch_blist_get_ui_ops(void);
/**
+ * finch_blist_init:
+ *
* Perform necessary initializations.
*/
void finch_blist_init(void);
/**
+ * finch_blist_uninit:
+ *
* Perform necessary uninitializations.
*/
void finch_blist_uninit(void);
/**
+ * finch_blist_show:
+ *
* Show the buddy list.
*/
void finch_blist_show(void);
/**
- * Get the position of the buddy list.
+ * finch_blist_get_position:
+ * @x: The x-coordinate is set here if not %NULL.
+ * @y: The y-coordinate is set here if not %NULL.
*
- * @param x The x-coordinate is set here if not @ NULL.
- * @param y The y-coordinate is set here if not @c NULL.
+ * Get the position of the buddy list.
*
- * @return Returns @c TRUE if the values were set, @c FALSE otherwise.
+ * Returns: Returns %TRUE if the values were set, %FALSE otherwise.
*/
gboolean finch_blist_get_position(int *x, int *y);
/**
- * Set the position of the buddy list.
+ * finch_blist_set_position:
+ * @x: The x-coordinate of the buddy list.
+ * @y: The y-coordinate of the buddy list.
*
- * @param x The x-coordinate of the buddy list.
- * @param y The y-coordinate of the buddy list.
+ * Set the position of the buddy list.
*/
void finch_blist_set_position(int x, int y);
/**
- * Get the size of the buddy list.
+ * finch_blist_get_size:
+ * @width: The width is set here if not %NULL.
+ * @height: The height is set here if not %NULL.
*
- * @param width The width is set here if not @ NULL.
- * @param height The height is set here if not @c NULL.
+ * Get the size of the buddy list.
*
- * @return Returns @c TRUE if the values were set, @c FALSE otherwise.
+ * Returns: Returns %TRUE if the values were set, %FALSE otherwise.
*/
gboolean finch_blist_get_size(int *width, int *height);
/**
- * Set the size of the buddy list.
+ * finch_blist_set_size:
+ * @width: The width of the buddy list.
+ * @height: The height of the buddy list.
*
- * @param width The width of the buddy list.
- * @param height The height of the buddy list.
+ * Set the size of the buddy list.
*/
void finch_blist_set_size(int width, int height);
/**
- * Get information about a user. Show immediate feedback.
- *
- * @param conn The connection to get information fro
- * @param name The user to get information about.
+ * finch_retrieve_user_info:
+ * @conn: The connection to get information from
+ * @name: The user to get information about.
*
- * @return Returns the ui-handle for the userinfo notification.
+ * Get information about a user. Show immediate feedback.
*
- * @since 2.1.0
+ * Returns: (transfer none): Returns the ui-handle for the userinfo
+ * notification.
*/
gpointer finch_retrieve_user_info(PurpleConnection *conn, const char *name);
/**
+ * finch_blist_get_tree:
+ *
* Get the tree list of the buddy list.
- * @return The GntTree widget.
- * @since 2.4.0
+ *
+ * Returns: (transfer none): The GntTree widget.
*/
GntTree * finch_blist_get_tree(void);
/**
- * Add an alternate buddy list manager.
+ * finch_blist_install_manager:
+ * @manager: The alternate buddylist manager.
*
- * @param manager The alternate buddylist manager.
- * @since 2.4.0
+ * Add an alternate buddy list manager.
*/
void finch_blist_install_manager(const FinchBlistManager *manager);
/**
- * Remove an alternate buddy list manager.
+ * finch_blist_uninstall_manager:
+ * @manager: The buddy list manager to remove.
*
- * @param manager The buddy list manager to remove.
- * @since 2.4.0
+ * Remove an alternate buddy list manager.
*/
void finch_blist_uninstall_manager(const FinchBlistManager *manager);
/**
- * Find a buddy list manager.
+ * finch_blist_manager_find:
+ * @id: The identifier for the desired buddy list manager.
*
- * @param id The identifier for the desired buddy list manager.
+ * Find a buddy list manager.
*
- * @return The manager with the requested identifier, if available. @c NULL otherwise.
- * @since 2.4.0
+ * Returns: The manager with the requested identifier, if available. %NULL
+ * otherwise.
*/
FinchBlistManager * finch_blist_manager_find(const char *id);
/**
- * Request the active buddy list manager to add a node.
+ * finch_blist_manager_add_node:
+ * @node: The node to add
*
- * @param node The node to add
- * @since 2.4.0
+ * Request the active buddy list manager to add a node.
*/
void finch_blist_manager_add_node(PurpleBlistNode *node);
-/*@}*/
-
#endif
diff --git a/finch/gntcertmgr.c b/finch/gntcertmgr.c
index 7d97b92f29..a6121e41b3 100644
--- a/finch/gntcertmgr.c
+++ b/finch/gntcertmgr.c
@@ -1,8 +1,3 @@
-/**
- * @file gntcertmgr.c GNT Certificate Manager API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -89,8 +84,7 @@ tls_peers_mgmt_import_ok_cb(gpointer data, const char *filename)
default_hostname, FALSE, FALSE, NULL,
_("OK"), G_CALLBACK(tls_peers_mgmt_import_ok2_cb),
_("Cancel"), G_CALLBACK(tls_peers_mgmt_import_cancel2_cb),
- NULL, NULL, NULL,
- crt);
+ NULL, crt);
g_free(default_hostname);
} else {
gchar * secondary;
@@ -98,7 +92,7 @@ tls_peers_mgmt_import_ok_cb(gpointer data, const char *filename)
purple_notify_error(NULL,
_("Certificate Import Error"),
_("X.509 certificate import failed"),
- secondary);
+ secondary, NULL);
g_free(secondary);
}
}
@@ -112,7 +106,7 @@ add_cert_cb(GntWidget *button, gpointer null)
FALSE,
G_CALLBACK(tls_peers_mgmt_import_ok_cb),
NULL,
- NULL, NULL, NULL, NULL );
+ NULL, NULL );
}
/* Save certs in some file */
@@ -128,7 +122,7 @@ tls_peers_mgmt_export_ok_cb(gpointer data, const char *filename)
purple_notify_error(NULL,
_("Certificate Export Error"),
_("X.509 certificate export failed"),
- secondary);
+ secondary, NULL);
g_free(secondary);
}
@@ -160,8 +154,7 @@ save_cert_cb(GntWidget *button, gpointer null)
"certificate.pem", TRUE,
G_CALLBACK(tls_peers_mgmt_export_ok_cb),
G_CALLBACK(purple_certificate_destroy),
- NULL, NULL, NULL,
- crt);
+ NULL, crt);
}
/* Show information about a cert */
@@ -195,7 +188,7 @@ info_cert_cb(GntWidget *button, gpointer null)
secondary = g_strdup_printf(_("Common name: %s\n\nSHA1 fingerprint:\n%s"), subject, fpr_sha1_asc);
purple_notify_info(NULL,
- _("SSL Host Certificate"), primary, secondary);
+ _("SSL Host Certificate"), primary, secondary, NULL);
g_free(primary);
g_free(secondary);
@@ -236,7 +229,7 @@ delete_cert_cb(GntWidget *button, gpointer null)
purple_request_yes_no((void *)key, _("Confirm certificate delete"),
primary, NULL,
0,
- NULL, NULL, NULL,
+ NULL,
g_strdup(key),
tls_peers_mgmt_delete_confirm_cb,
g_free);
diff --git a/finch/gntcertmgr.h b/finch/gntcertmgr.h
index bce818d454..d5ed04d9d9 100644
--- a/finch/gntcertmgr.h
+++ b/finch/gntcertmgr.h
@@ -1,8 +1,3 @@
-/**
- * @file gntcertmgr.h GNT Certificate Manager API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -24,8 +19,15 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
*/
+
#ifndef _GNT_CERTMGR_H
#define _GNT_CERTMGR_H
+/**
+ * SECTION:gntcertmgr
+ * @section_id: finch-gntcertmgr
+ * @short_description: <filename>gntcertmgr.h</filename>
+ * @title: Certificate Manager API
+ */
void finch_certmgr_show(void);
diff --git a/finch/gntconn.c b/finch/gntconn.c
index 2c3b8afa43..cd0026a531 100644
--- a/finch/gntconn.c
+++ b/finch/gntconn.c
@@ -1,8 +1,3 @@
-/**
- * @file gntconn.c GNT Connection API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -45,7 +40,7 @@ typedef struct {
guint timeout;
} FinchAutoRecon;
-/**
+/*
* Contains accounts that are auto-reconnecting.
* The key is a pointer to the PurpleAccount and the
* value is a pointer to a FinchAutoRecon.
@@ -132,8 +127,7 @@ finch_connection_report_disconnect(PurpleConnection *gc, PurpleConnectionError r
"correct the error and re-enable the account."), text);
purple_request_action(account, NULL, primary, secondary, 2,
- account, NULL, NULL,
- account, 3,
+ purple_request_cpar_from_account(account), account, 3,
_("OK"), NULL,
_("Modify Account"), PURPLE_CALLBACK(ce_modify_account_cb),
_("Re-enable Account"), PURPLE_CALLBACK(ce_enable_account_cb));
@@ -165,12 +159,12 @@ static PurpleConnectionUiOps ops =
NULL, /* connected */
NULL, /* disconnected */
NULL, /* notice */
- NULL,
NULL, /* network_connected */
NULL, /* network_disconnected */
finch_connection_report_disconnect,
NULL,
NULL,
+ NULL,
NULL
};
diff --git a/finch/gntconn.h b/finch/gntconn.h
index 6d72791500..92542ac5a8 100644
--- a/finch/gntconn.h
+++ b/finch/gntconn.h
@@ -1,8 +1,3 @@
-/**
- * @file gntconn.h GNT Connection API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -23,33 +18,43 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _GNT_CONN_H
#define _GNT_CONN_H
+/**
+ * SECTION:gntconn
+ * @section_id: finch-gntconn
+ * @short_description: <filename>gntconn.h</filename>
+ * @title: Connection API
+ */
#include "connection.h"
/**********************************************************************
- * @name GNT Connection API
+ * GNT Connection API
**********************************************************************/
-/*@{*/
/**
+ * finch_connections_get_ui_ops:
+ *
* Get the ui-functions.
*
- * @return The PurpleConnectionUiOps structure populated with the appropriate functions.
+ * Returns: The PurpleConnectionUiOps structure populated with the appropriate functions.
*/
PurpleConnectionUiOps *finch_connections_get_ui_ops(void);
/**
+ * finch_connections_init:
+ *
* Perform necessary initializations.
*/
void finch_connections_init(void);
/**
+ * finch_connections_uninit:
+ *
* Perform necessary uninitializations.
*/
void finch_connections_uninit(void);
-/*@}*/
-
#endif
diff --git a/finch/gntconv.c b/finch/gntconv.c
index 04c882857a..8d59f30c0f 100644
--- a/finch/gntconv.c
+++ b/finch/gntconv.c
@@ -1,8 +1,3 @@
-/**
- * @file gntconv.c GNT Conversation API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -52,6 +47,7 @@
#include "gntmenu.h"
#include "gntmenuitem.h"
#include "gntmenuitemcheck.h"
+#include "gntmenuutil.h"
#include "gntstyle.h"
#include "gnttextview.h"
#include "gnttree.h"
@@ -64,9 +60,8 @@
#include "config.h"
-static void finch_write_common(PurpleConversation *conv, const char *who,
- const char *message, PurpleMessageFlags flags, time_t mtime);
static void generate_send_to_menu(FinchConv *ggc);
+static void generate_e2ee_menu(FinchConv *ggc);
static int color_message_receive;
static int color_message_send;
@@ -77,7 +72,7 @@ static int color_timestamp;
static PurpleBuddy *
find_buddy_for_conversation(PurpleConversation *conv)
{
- return purple_find_buddy(purple_conversation_get_account(conv),
+ return purple_blist_find_buddy(purple_conversation_get_account(conv),
purple_conversation_get_name(conv));
}
@@ -93,17 +88,13 @@ get_conversation_blist_node(PurpleConversation *conv)
{
PurpleBlistNode *node = NULL;
- switch (purple_conversation_get_type(conv)) {
- case PURPLE_CONV_TYPE_IM:
- node = (PurpleBlistNode*)find_buddy_for_conversation(conv);
- node = node ? purple_blist_node_get_parent(node) : NULL;
- break;
- case PURPLE_CONV_TYPE_CHAT:
- node = (PurpleBlistNode*)find_chat_for_conversation(conv);
- break;
- default:
- break;
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
+ node = (PurpleBlistNode*)find_buddy_for_conversation(conv);
+ node = node ? purple_blist_node_get_parent(node) : NULL;
+ } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
+ node = (PurpleBlistNode*)find_chat_for_conversation(conv);
}
+
return node;
}
@@ -114,26 +105,26 @@ send_typing_notification(GntWidget *w, FinchConv *ggconv)
gboolean empty = (!text || !*text || (*text == '/'));
if (purple_prefs_get_bool("/finch/conversations/notify_typing")) {
PurpleConversation *conv = ggconv->active_conv;
- PurpleConvIm *im = PURPLE_CONV_IM(conv);
+ PurpleIMConversation *im = PURPLE_IM_CONVERSATION(conv);
if (!empty) {
- gboolean send = (purple_conv_im_get_send_typed_timeout(im) == 0);
+ gboolean send = (purple_im_conversation_get_send_typed_timeout(im) == 0);
- purple_conv_im_stop_send_typed_timeout(im);
- purple_conv_im_start_send_typed_timeout(im);
- if (send || (purple_conv_im_get_type_again(im) != 0 &&
- time(NULL) > purple_conv_im_get_type_again(im))) {
+ purple_im_conversation_stop_send_typed_timeout(im);
+ purple_im_conversation_start_send_typed_timeout(im);
+ if (send || (purple_im_conversation_get_type_again(im) != 0 &&
+ time(NULL) > purple_im_conversation_get_type_again(im))) {
unsigned int timeout;
- timeout = serv_send_typing(purple_conversation_get_gc(conv),
+ timeout = purple_serv_send_typing(purple_conversation_get_connection(conv),
purple_conversation_get_name(conv),
- PURPLE_TYPING);
- purple_conv_im_set_type_again(im, timeout);
+ PURPLE_IM_TYPING);
+ purple_im_conversation_set_type_again(im, timeout);
}
} else {
- purple_conv_im_stop_send_typed_timeout(im);
+ purple_im_conversation_stop_send_typed_timeout(im);
- serv_send_typing(purple_conversation_get_gc(conv),
+ purple_serv_send_typing(purple_conversation_get_connection(conv),
purple_conversation_get_name(conv),
- PURPLE_NOT_TYPING);
+ PURPLE_IM_NOT_TYPING);
}
}
}
@@ -158,53 +149,48 @@ entry_key_pressed(GntWidget *w, FinchConv *ggconv)
case PURPLE_CMD_STATUS_OK:
break;
case PURPLE_CMD_STATUS_NOT_FOUND:
- purple_conversation_write(conv, "", _("No such command."),
- PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("No such command."), PURPLE_MESSAGE_NO_LOG);
break;
case PURPLE_CMD_STATUS_WRONG_ARGS:
- purple_conversation_write(conv, "", _("Syntax Error: You typed the wrong number of arguments "
- "to that command."),
- PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("Syntax Error: You typed the wrong "
+ "number of arguments to that command."),
+ PURPLE_MESSAGE_NO_LOG);
break;
case PURPLE_CMD_STATUS_FAILED:
- purple_conversation_write(conv, "", error ? error : _("Your command failed for an unknown reason."),
- PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(conv,
+ error ? error : _("Your command failed for an unknown reason."),
+ PURPLE_MESSAGE_NO_LOG);
break;
case PURPLE_CMD_STATUS_WRONG_TYPE:
- if(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
- purple_conversation_write(conv, "", _("That command only works in chats, not IMs."),
- PURPLE_MESSAGE_NO_LOG, time(NULL));
+ if(PURPLE_IS_IM_CONVERSATION(conv))
+ purple_conversation_write_system_message(conv,
+ _("That command only works in chats, not IMs."),
+ PURPLE_MESSAGE_NO_LOG);
else
- purple_conversation_write(conv, "", _("That command only works in IMs, not chats."),
- PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("That command only works in IMs, not chats."),
+ PURPLE_MESSAGE_NO_LOG);
break;
case PURPLE_CMD_STATUS_WRONG_PRPL:
- purple_conversation_write(conv, "", _("That command doesn't work on this protocol."),
- PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("That command doesn't work on this protocol."),
+ PURPLE_MESSAGE_NO_LOG);
break;
}
g_free(error);
}
else if (!purple_account_is_connected(purple_conversation_get_account(ggconv->active_conv)))
{
- purple_conversation_write(ggconv->active_conv, "", _("Message was not sent, because you are not signed on."),
- PURPLE_MESSAGE_ERROR | PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(ggconv->active_conv,
+ _("Message was not sent, because you are not signed on."),
+ PURPLE_MESSAGE_ERROR | PURPLE_MESSAGE_NO_LOG);
}
else
{
char *escape = purple_markup_escape_text((*text == '/' ? text + 1 : text), -1);
- switch (purple_conversation_get_type(ggconv->active_conv))
- {
- case PURPLE_CONV_TYPE_IM:
- purple_conv_im_send_with_flags(PURPLE_CONV_IM(ggconv->active_conv), escape, PURPLE_MESSAGE_SEND);
- break;
- case PURPLE_CONV_TYPE_CHAT:
- purple_conv_chat_send(PURPLE_CONV_CHAT(ggconv->active_conv), escape);
- break;
- default:
- g_free(escape);
- g_return_if_reached();
- }
+ purple_conversation_send(ggconv->active_conv, escape);
g_free(escape);
purple_idle_touch();
}
@@ -220,7 +206,7 @@ closing_window(GntWidget *window, FinchConv *ggconv)
while (list) {
PurpleConversation *conv = list->data;
list = list->next;
- purple_conversation_destroy(conv);
+ g_object_unref(conv);
}
}
@@ -240,12 +226,12 @@ save_position_cb(GntWidget *w, int x, int y)
purple_prefs_set_int(PREF_ROOT "/position/y", y);
}
-static PurpleConversation *
-find_conv_with_contact(PurpleAccount *account, const char *name)
+static PurpleIMConversation *
+find_im_with_contact(PurpleAccount *account, const char *name)
{
PurpleBlistNode *node;
- PurpleBuddy *buddy = purple_find_buddy(account, name);
- PurpleConversation *ret = NULL;
+ PurpleBuddy *buddy = purple_blist_find_buddy(account, name);
+ PurpleIMConversation *im = NULL;
if (!buddy)
return NULL;
@@ -254,37 +240,47 @@ find_conv_with_contact(PurpleAccount *account, const char *name)
node; node = purple_blist_node_get_sibling_next(node)) {
if (node == (PurpleBlistNode*)buddy)
continue;
- if ((ret = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+ if ((im = purple_conversations_find_im_with_account(
purple_buddy_get_name((PurpleBuddy*)node), purple_buddy_get_account((PurpleBuddy*)node))) != NULL)
break;
}
- return ret;
+ return im;
}
static char *
get_conversation_title(PurpleConversation *conv, PurpleAccount *account)
{
- return g_strdup_printf(_("%s (%s -- %s)"), purple_conversation_get_title(conv),
- purple_account_get_username(account), purple_account_get_protocol_name(account));
+ PurpleE2eeState *e2ee;
+
+ e2ee = purple_conversation_get_e2ee_state(conv);
+
+ return g_strdup_printf(_("%s (%s -- %s)%s%s%s%s"),
+ purple_conversation_get_title(conv),
+ purple_account_get_username(account),
+ purple_account_get_protocol_name(account),
+ e2ee ? " | " : "",
+ e2ee ? purple_e2ee_provider_get_name(purple_e2ee_state_get_provider(e2ee)) : "",
+ e2ee ? ": " : "",
+ e2ee ? purple_e2ee_state_get_name(e2ee) : "");
}
static void
update_buddy_typing(PurpleAccount *account, const char *who, gpointer null)
{
- PurpleConversation *conv;
FinchConv *ggc;
- PurpleConvIm *im = NULL;
+ PurpleIMConversation *im;
+ PurpleConversation *conv;
char *title, *str;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, account);
+ im = purple_conversations_find_im_with_account(who, account);
- if (!conv)
+ if (!im)
return;
- im = PURPLE_CONV_IM(conv);
- ggc = FINCH_GET_DATA(conv);
+ conv = PURPLE_CONVERSATION(im);
+ ggc = FINCH_CONV(conv);
- if (purple_conv_im_get_typing_state(im) == PURPLE_TYPING) {
+ if (purple_im_conversation_get_typing_state(im) == PURPLE_IM_TYPING) {
int scroll;
str = get_conversation_title(conv, account);
title = g_strdup_printf(_("%s [%s]"), str,
@@ -311,36 +307,36 @@ update_buddy_typing(PurpleAccount *account, const char *who, gpointer null)
static void
chat_left_cb(PurpleConversation *conv, gpointer null)
{
- finch_write_common(conv, NULL, _("You have left this chat."),
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("You have left this chat."), 0);
}
static void
buddy_signed_on_off(PurpleBuddy *buddy, gpointer null)
{
- PurpleConversation *conv = find_conv_with_contact(purple_buddy_get_account(buddy), purple_buddy_get_name(buddy));
- if (conv == NULL)
+ PurpleIMConversation *im = find_im_with_contact(purple_buddy_get_account(buddy), purple_buddy_get_name(buddy));
+ if (im == NULL)
return;
- generate_send_to_menu(FINCH_GET_DATA(conv));
+ generate_send_to_menu(FINCH_CONV(PURPLE_CONVERSATION(im)));
}
static void
account_signed_on_off(PurpleConnection *gc, gpointer null)
{
- GList *list = purple_get_ims();
+ GList *list = purple_conversations_get_ims();
while (list) {
PurpleConversation *conv = list->data;
- PurpleConversation *cc = find_conv_with_contact(
+ PurpleIMConversation *cc = find_im_with_contact(
purple_conversation_get_account(conv), purple_conversation_get_name(conv));
if (cc)
- generate_send_to_menu(FINCH_GET_DATA(cc));
+ generate_send_to_menu(FINCH_CONV(PURPLE_CONVERSATION(cc)));
list = list->next;
}
if (PURPLE_CONNECTION_IS_CONNECTED(gc)) {
/* We just signed on. Let's see if there's any chat that we have open,
* and hadn't left before the disconnect. */
- list = purple_get_chats();
+ list = purple_conversations_get_chats();
while (list) {
PurpleConversation *conv = list->data;
PurpleChat *chat;
@@ -348,7 +344,7 @@ account_signed_on_off(PurpleConnection *gc, gpointer null)
list = list->next;
if (purple_conversation_get_account(conv) != purple_connection_get_account(gc) ||
- !purple_conversation_get_data(conv, "want-to-rejoin"))
+ !g_object_get_data(G_OBJECT(conv), "want-to-rejoin"))
continue;
chat = find_chat_for_conversation(conv);
@@ -359,7 +355,7 @@ account_signed_on_off(PurpleConnection *gc, gpointer null)
} else {
comps = purple_chat_get_components(chat);
}
- serv_join_chat(gc, comps);
+ purple_serv_join_chat(gc, comps);
if (chat == NULL && comps != NULL)
g_hash_table_destroy(comps);
}
@@ -369,20 +365,21 @@ account_signed_on_off(PurpleConnection *gc, gpointer null)
static void
account_signing_off(PurpleConnection *gc)
{
- GList *list = purple_get_chats();
+ GList *list = purple_conversations_get_chats();
PurpleAccount *account = purple_connection_get_account(gc);
/* We are about to sign off. See which chats we are currently in, and mark
* them for rejoin on reconnect. */
while (list) {
PurpleConversation *conv = list->data;
- if (!purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)) &&
+ if (!purple_chat_conversation_has_left(PURPLE_CHAT_CONVERSATION(conv)) &&
purple_conversation_get_account(conv) == account) {
- purple_conversation_set_data(conv, "want-to-rejoin", GINT_TO_POINTER(TRUE));
- purple_conversation_write(conv, NULL, _("The account has disconnected and you are no "
- "longer in this chat. You will be automatically rejoined in the chat when "
- "the account reconnects."),
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ g_object_set_data(G_OBJECT(conv), "want-to-rejoin", GINT_TO_POINTER(TRUE));
+ purple_conversation_write_system_message(conv,
+ _("The account has disconnected and you are no "
+ "longer in this chat. You will be automatically rejoined in the chat when "
+ "the account reconnects."),
+ PURPLE_MESSAGE_NO_LOG);
}
list = list->next;
}
@@ -398,12 +395,58 @@ finch_conv_get_handle(void)
static void
cleared_message_history_cb(PurpleConversation *conv, gpointer data)
{
- FinchConv *ggc = FINCH_GET_DATA(conv);
+ FinchConv *ggc = FINCH_CONV(conv);
if (ggc)
gnt_text_view_clear(GNT_TEXT_VIEW(ggc->tv));
}
static void
+gg_extended_menu(FinchConv *ggc)
+{
+ GntMenu *sub;
+ GList *list;
+ gboolean is_empty = TRUE;
+
+ g_return_if_fail(ggc != NULL);
+
+ sub = GNT_MENU(gnt_menu_new(GNT_MENU_POPUP));
+ gnt_menuitem_set_submenu(ggc->plugins, sub);
+
+ for (list = purple_conversation_get_extended_menu(ggc->active_conv);
+ list; list = g_list_delete_link(list, list))
+ {
+ finch_append_menu_action(sub, list->data, ggc->active_conv);
+ is_empty = FALSE;
+ }
+ gnt_menuitem_set_visible(ggc->plugins, !is_empty);
+}
+
+static void
+conv_updated(PurpleConversation *conv, PurpleConversationUpdateType type)
+{
+ if (purple_conversation_get_ui_data(conv) == NULL)
+ return;
+
+ if (type == PURPLE_CONVERSATION_UPDATE_FEATURES) {
+ gg_extended_menu(purple_conversation_get_ui_data(conv));
+ return;
+ }
+ if (type == PURPLE_CONVERSATION_UPDATE_E2EE) {
+ FinchConv *ggconv = FINCH_CONV(conv);
+ gchar *title;
+
+ title = get_conversation_title(conv,
+ purple_conversation_get_account(conv));
+ gnt_screen_rename_widget(ggconv->window, title);
+ g_free(title);
+
+ generate_e2ee_menu(ggconv);
+
+ return;
+ }
+}
+
+static void
clear_scrollback_cb(GntMenuItem *item, gpointer ggconv)
{
FinchConv *ggc = ggconv;
@@ -414,7 +457,7 @@ static void
send_file_cb(GntMenuItem *item, gpointer ggconv)
{
FinchConv *ggc = ggconv;
- serv_send_file(purple_conversation_get_gc(ggc->active_conv),
+ purple_serv_send_file(purple_conversation_get_connection(ggc->active_conv),
purple_conversation_get_name(ggc->active_conv), NULL);
}
@@ -431,7 +474,7 @@ static void
get_info_cb(GntMenuItem *item, gpointer ggconv)
{
FinchConv *ggc = ggconv;
- finch_retrieve_user_info(purple_conversation_get_gc(ggc->active_conv),
+ finch_retrieve_user_info(purple_conversation_get_connection(ggc->active_conv),
purple_conversation_get_name(ggc->active_conv));
}
@@ -458,13 +501,13 @@ toggle_logging_cb(GntMenuItem *item, gpointer ggconv)
/* Enable logging first so the message below can be logged. */
purple_conversation_set_logging(conv, TRUE);
- purple_conversation_write(conv, NULL,
- _("Logging started. Future messages in this conversation will be logged."),
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("Logging started. Future messages in this "
+ "conversation will be logged."), 0);
} else {
- purple_conversation_write(conv, NULL,
- _("Logging stopped. Future messages in this conversation will not be logged."),
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("Logging stopped. Future messages in this "
+ "conversation will not be logged."), 0);
/* Disable the logging second, so that the above message can be logged. */
purple_conversation_set_logging(conv, FALSE);
@@ -493,8 +536,8 @@ send_to_cb(GntMenuItem *m, gpointer n)
{
PurpleAccount *account = g_object_get_data(G_OBJECT(m), "purple_account");
gchar *buddy = g_object_get_data(G_OBJECT(m), "purple_buddy_name");
- PurpleConversation *conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, buddy);
- finch_conversation_set_active(conv);
+ PurpleIMConversation *im = purple_im_conversation_new(account, buddy);
+ finch_conversation_set_active(PURPLE_CONVERSATION(im));
}
static void
@@ -511,9 +554,9 @@ view_log_cb(GntMenuItem *n, gpointer ggc)
fc = ggc;
conv = fc->active_conv;
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
+ if (PURPLE_IS_IM_CONVERSATION(conv))
type = PURPLE_LOG_IM;
- else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
+ else if (PURPLE_IS_CHAT_CONVERSATION(conv))
type = PURPLE_LOG_CHAT;
else
return;
@@ -521,7 +564,7 @@ view_log_cb(GntMenuItem *n, gpointer ggc)
name = purple_conversation_get_name(conv);
account = purple_conversation_get_account(conv);
- buddies = purple_find_buddies(account, name);
+ buddies = purple_blist_find_buddies(account, name);
for (cur = buddies; cur != NULL; cur = cur->next) {
PurpleBlistNode *node = cur->data;
if ((node != NULL) &&
@@ -544,7 +587,7 @@ generate_send_to_menu(FinchConv *ggc)
GSList *buds;
GList *list = NULL;
- buds = purple_find_buddies(purple_conversation_get_account(ggc->active_conv),
+ buds = purple_blist_find_buddies(purple_conversation_get_account(ggc->active_conv),
purple_conversation_get_name(ggc->active_conv));
if (!buds)
return;
@@ -573,7 +616,7 @@ generate_send_to_menu(FinchConv *ggc)
}
for (list = g_list_reverse(list); list != NULL; list = g_list_delete_link(list, list)) {
PurplePresence *pre = list->data;
- PurpleBuddy *buddy = purple_presence_get_buddy(pre);
+ PurpleBuddy *buddy = purple_buddy_presence_get_buddy(PURPLE_BUDDY_PRESENCE(pre));
PurpleAccount *account = purple_buddy_get_account(buddy);
gchar *name = g_strdup(purple_buddy_get_name(buddy));
gchar *text = g_strdup_printf("%s (%s)", purple_buddy_get_name(buddy), purple_account_get_username(account));
@@ -587,11 +630,52 @@ generate_send_to_menu(FinchConv *ggc)
}
static void
+generate_e2ee_menu(FinchConv *ggc)
+{
+ GntMenu *sub;
+ GntWidget *menu = ggc->menu;
+ PurpleConversation *conv = ggc->active_conv;
+ GntMenuItem *item;
+ PurpleE2eeProvider *eprov;
+ GList *menu_actions, *it;
+
+ eprov = purple_e2ee_provider_get_main();
+
+ item = ggc->u.im->e2ee_menu;
+ if (item == NULL) {
+ item = gnt_menuitem_new(NULL);
+ gnt_menu_add_item(GNT_MENU(menu), item);
+ ggc->u.im->e2ee_menu = item;
+ }
+ sub = GNT_MENU(gnt_menu_new(GNT_MENU_POPUP));
+ gnt_menuitem_set_submenu(item, GNT_MENU(sub));
+
+ gnt_menuitem_set_visible(item, (eprov != NULL));
+ if (eprov == NULL)
+ return;
+ gnt_menuitem_set_text(item, purple_e2ee_provider_get_name(eprov));
+
+ menu_actions = purple_e2ee_provider_get_conv_menu_actions(eprov, conv);
+ for (it = menu_actions; it; it = g_list_next(it)) {
+ PurpleMenuAction *action = it->data;
+
+ finch_append_menu_action(sub, action, conv);
+ }
+ g_list_free(menu_actions);
+}
+
+static void
invite_cb(GntMenuItem *item, gpointer ggconv)
{
FinchConv *fc = ggconv;
- PurpleConversation *conv = fc->active_conv;
- purple_conv_chat_invite_user(PURPLE_CONV_CHAT(conv), NULL, NULL, TRUE);
+ PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(fc->active_conv);
+ purple_chat_conversation_invite_user(chat, NULL, NULL, TRUE);
+}
+
+static void
+plugin_changed_cb(PurplePlugin *p, gpointer data)
+{
+ gg_extended_menu(data);
}
static void
@@ -619,7 +703,7 @@ gg_create_menu(FinchConv *ggc)
gnt_menu_add_item(GNT_MENU(sub), item);
gnt_menuitem_set_callback(item, toggle_timestamps_cb, ggc);
- if (purple_conversation_get_type(ggc->active_conv) == PURPLE_CONV_TYPE_IM) {
+ if (PURPLE_IS_IM_CONVERSATION(ggc->active_conv)) {
PurpleAccount *account = purple_conversation_get_account(ggc->active_conv);
PurpleConnection *gc = purple_account_get_connection(account);
PurplePluginProtocolInfo *pinfo =
@@ -644,12 +728,14 @@ gg_create_menu(FinchConv *ggc)
}
generate_send_to_menu(ggc);
- } else if (purple_conversation_get_type(ggc->active_conv) == PURPLE_CONV_TYPE_CHAT) {
+ } else if (PURPLE_IS_CHAT_CONVERSATION(ggc->active_conv)) {
item = gnt_menuitem_new(_("Invite..."));
gnt_menu_add_item(GNT_MENU(sub), item);
gnt_menuitem_set_callback(item, invite_cb, ggc);
}
+ generate_e2ee_menu(ggc);
+
item = gnt_menuitem_new(_("View Log..."));
gnt_menu_add_item(GNT_MENU(sub), item);
gnt_menuitem_set_callback(item, view_log_cb, ggc);
@@ -665,6 +751,12 @@ gg_create_menu(FinchConv *ggc)
!(ggc->flags & FINCH_CONV_NO_SOUND));
gnt_menu_add_item(GNT_MENU(sub), item);
gnt_menuitem_set_callback(item, toggle_sound_cb, ggc);
+
+ item = gnt_menuitem_new(_("Plugins"));
+ gnt_menu_add_item(GNT_MENU(menu), item);
+ ggc->plugins = item;
+
+ gg_extended_menu(ggc);
}
static void
@@ -676,19 +768,20 @@ create_conv_from_userlist(GntWidget *widget, FinchConv *fc)
char *name, *realname;
if (!gc) {
- purple_conversation_write(fc->active_conv, NULL, _("You are not connected."),
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(fc->active_conv,
+ _("You are not connected."), 0);
return;
}
name = gnt_tree_get_selection_data(GNT_TREE(widget));
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_cb_real_name))
- realname = prpl_info->get_cb_real_name(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(fc->active_conv)), name);
+ realname = prpl_info->get_cb_real_name(gc, purple_chat_conversation_get_id(
+ PURPLE_CHAT_CONVERSATION(fc->active_conv)), name);
else
realname = NULL;
- purple_conversation_new(PURPLE_CONV_TYPE_IM, account, realname ? realname : name);
+ purple_im_conversation_new(account, realname ? realname : name);
g_free(realname);
}
@@ -697,8 +790,8 @@ gained_focus_cb(GntWindow *window, FinchConv *fc)
{
GList *iter;
for (iter = fc->list; iter; iter = iter->next) {
- purple_conversation_set_data(iter->data, "unseen-count", 0);
- purple_conversation_update(iter->data, PURPLE_CONV_UPDATE_UNSEEN);
+ g_object_set_data(G_OBJECT(iter->data), "unseen-count", 0);
+ purple_conversation_update(iter->data, PURPLE_CONVERSATION_UPDATE_UNSEEN);
}
}
@@ -749,9 +842,8 @@ cmd_removed_cb(const char *cmd, FinchConv *fconv)
static void
finch_create_conversation(PurpleConversation *conv)
{
- FinchConv *ggc = FINCH_GET_DATA(conv);
+ FinchConv *ggc = FINCH_CONV(conv);
char *title;
- PurpleConversationType type;
PurpleConversation *cc;
PurpleAccount *account;
PurpleBlistNode *convnode = NULL;
@@ -762,9 +854,9 @@ finch_create_conversation(PurpleConversation *conv)
}
account = purple_conversation_get_account(conv);
- cc = find_conv_with_contact(account, purple_conversation_get_name(conv));
- if (cc && FINCH_GET_DATA(cc))
- ggc = FINCH_GET_DATA(cc);
+ cc = PURPLE_CONVERSATION(find_im_with_contact(account, purple_conversation_get_name(conv)));
+ if (cc && FINCH_CONV(cc))
+ ggc = FINCH_CONV(cc);
else
ggc = g_new0(FinchConv, 1);
@@ -776,14 +868,13 @@ finch_create_conversation(PurpleConversation *conv)
ggc->list = g_list_prepend(ggc->list, conv);
ggc->active_conv = conv;
- FINCH_SET_DATA(conv, ggc);
+ purple_conversation_set_ui_data(conv, ggc);
- if (cc && FINCH_GET_DATA(cc) && cc != conv) {
+ if (cc && FINCH_CONV(cc) && cc != conv) {
finch_conversation_set_active(conv);
return;
}
- type = purple_conversation_get_type(conv);
title = get_conversation_title(conv, account);
ggc->window = gnt_vwindow_new(FALSE);
@@ -791,30 +882,19 @@ finch_create_conversation(PurpleConversation *conv)
gnt_box_set_toplevel(GNT_BOX(ggc->window), TRUE);
gnt_box_set_pad(GNT_BOX(ggc->window), 0);
- switch (purple_conversation_get_type(conv)) {
- case PURPLE_CONV_TYPE_UNKNOWN:
- gnt_widget_set_name(ggc->window, "conversation-window-unknown" );
- break;
- case PURPLE_CONV_TYPE_IM:
- gnt_widget_set_name(ggc->window, "conversation-window-im" );
- break;
- case PURPLE_CONV_TYPE_CHAT:
- gnt_widget_set_name(ggc->window, "conversation-window-chat" );
- break;
- case PURPLE_CONV_TYPE_MISC:
- gnt_widget_set_name(ggc->window, "conversation-window-misc" );
- break;
- case PURPLE_CONV_TYPE_ANY:
- gnt_widget_set_name(ggc->window, "conversation-window-any" );
- break;
- }
+ if (PURPLE_IS_IM_CONVERSATION(conv))
+ gnt_widget_set_name(ggc->window, "conversation-window-im");
+ else if (PURPLE_IS_CHAT_CONVERSATION(conv))
+ gnt_widget_set_name(ggc->window, "conversation-window-chat");
+ else
+ gnt_widget_set_name(ggc->window, "conversation-window-other");
ggc->tv = gnt_text_view_new();
gnt_widget_set_name(ggc->tv, "conversation-window-textview");
gnt_widget_set_size(ggc->tv, purple_prefs_get_int(PREF_ROOT "/size/width"),
purple_prefs_get_int(PREF_ROOT "/size/height"));
- if (type == PURPLE_CONV_TYPE_CHAT) {
+ if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
GntWidget *hbox, *tree;
FinchConvChat *fc = ggc->u.chat = g_new0(FinchConvChat, 1);
hbox = gnt_hbox_new(FALSE);
@@ -859,9 +939,8 @@ finch_create_conversation(PurpleConversation *conv)
g_signal_connect(G_OBJECT(ggc->tv), "size_changed", G_CALLBACK(size_changed_cb), NULL);
g_signal_connect(G_OBJECT(ggc->window), "position_set", G_CALLBACK(save_position_cb), NULL);
- if (type == PURPLE_CONV_TYPE_IM) {
+ if (PURPLE_IS_IM_CONVERSATION(conv))
g_signal_connect(G_OBJECT(ggc->entry), "text_changed", G_CALLBACK(send_typing_notification), ggc);
- }
convnode = get_conversation_blist_node(conv);
if ((convnode && purple_blist_node_get_bool(convnode, "gnt-mute-sound")) ||
@@ -876,6 +955,11 @@ finch_create_conversation(PurpleConversation *conv)
purple_signal_connect(purple_cmds_get_handle(), "cmd-removed", ggc,
G_CALLBACK(cmd_removed_cb), ggc);
+ purple_signal_connect(purple_plugins_get_handle(), "plugin-load", ggc,
+ PURPLE_CALLBACK(plugin_changed_cb), ggc);
+ purple_signal_connect(purple_plugins_get_handle(), "plugin-unload", ggc,
+ PURPLE_CALLBACK(plugin_changed_cb), ggc);
+
g_free(title);
gnt_box_give_focus_to_child(GNT_BOX(ggc->window), ggc->entry);
g_signal_connect(G_OBJECT(ggc->window), "gained-focus", G_CALLBACK(gained_focus_cb), ggc);
@@ -885,12 +969,10 @@ static void
finch_destroy_conversation(PurpleConversation *conv)
{
/* do stuff here */
- FinchConv *ggc = FINCH_GET_DATA(conv);
+ FinchConv *ggc = FINCH_CONV(conv);
ggc->list = g_list_remove(ggc->list, conv);
- if (ggc->list && conv == ggc->active_conv) {
- ggc->active_conv = ggc->list->data;
- gg_setup_commands(ggc, TRUE);
- }
+ if (ggc->list && conv == ggc->active_conv)
+ finch_conversation_set_active(ggc->list->data);
if (ggc->list == NULL) {
g_free(ggc->u.chat);
@@ -902,13 +984,13 @@ finch_destroy_conversation(PurpleConversation *conv)
}
static void
-finch_write_common(PurpleConversation *conv, const char *who, const char *message,
- PurpleMessageFlags flags, time_t mtime)
+finch_write_conv(PurpleConversation *conv, PurpleMessage *msg)
{
- FinchConv *ggconv = FINCH_GET_DATA(conv);
+ FinchConv *ggconv = FINCH_CONV(conv);
char *strip, *newline;
GntTextFormatFlags fl = 0;
int pos;
+ PurpleMessageFlags flags = purple_message_get_flags(msg);
g_return_if_fail(ggconv != NULL);
@@ -930,6 +1012,7 @@ finch_write_common(PurpleConversation *conv, const char *who, const char *messag
/* Unnecessary to print the timestamp for delayed message */
if (purple_prefs_get_bool("/finch/conversations/timestamps")) {
+ time_t mtime = purple_message_get_time(msg);
if (!mtime)
time(&mtime);
gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv),
@@ -942,15 +1025,16 @@ finch_write_common(PurpleConversation *conv, const char *who, const char *messag
gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv),
_("<AUTO-REPLY> "), GNT_TEXT_FLAG_BOLD);
- if (who && *who && (flags & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV)) &&
+ if (purple_message_get_author(msg) && (flags & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV)) &&
!(flags & PURPLE_MESSAGE_NOTIFY))
{
char * name = NULL;
GntTextFormatFlags msgflags = GNT_TEXT_FLAG_NORMAL;
gboolean me = FALSE;
+ gchar *msg_text = g_strdup(purple_message_get_contents(msg));
- if (purple_message_meify((char*)message, -1)) {
- name = g_strdup_printf("*** %s", who);
+ if (purple_message_meify(msg_text, -1)) {
+ name = g_strdup_printf("*** %s", purple_message_get_author(msg));
if (!(flags & PURPLE_MESSAGE_SEND) &&
(flags & PURPLE_MESSAGE_NICK))
msgflags = gnt_color_pair(color_message_highlight);
@@ -958,7 +1042,7 @@ finch_write_common(PurpleConversation *conv, const char *who, const char *messag
msgflags = gnt_color_pair(color_message_action);
me = TRUE;
} else {
- name = g_strdup_printf("%s", who);
+ name = g_strdup_printf("%s", purple_message_get_author(msg));
if (flags & PURPLE_MESSAGE_SEND)
msgflags = gnt_color_pair(color_message_send);
else if (flags & PURPLE_MESSAGE_NICK)
@@ -966,6 +1050,7 @@ finch_write_common(PurpleConversation *conv, const char *who, const char *messag
else
msgflags = gnt_color_pair(color_message_receive);
}
+ purple_message_set_contents(msg, msg_text); /* might be "meified" */
gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv),
name, msgflags);
gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), me ? " " : ": ", GNT_TEXT_FLAG_NORMAL);
@@ -977,7 +1062,7 @@ finch_write_common(PurpleConversation *conv, const char *who, const char *messag
fl |= GNT_TEXT_FLAG_BOLD;
/* XXX: Remove this workaround when textview can parse messages. */
- newline = purple_strdup_withhtml(message);
+ newline = purple_strdup_withhtml(purple_message_get_contents(msg));
strip = purple_markup_strip_html(newline);
gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv),
strip, fl);
@@ -985,8 +1070,8 @@ finch_write_common(PurpleConversation *conv, const char *who, const char *messag
g_free(newline);
g_free(strip);
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM &&
- purple_conv_im_get_typing_state(PURPLE_CONV_IM(conv)) == PURPLE_TYPING) {
+ if (PURPLE_IS_IM_CONVERSATION(conv) && purple_im_conversation_get_typing_state(
+ PURPLE_IM_CONVERSATION(conv)) == PURPLE_IM_TYPING) {
strip = g_strdup_printf(_("\n%s is typing..."), purple_conversation_get_title(conv));
gnt_text_view_append_text_with_tag(GNT_TEXT_VIEW(ggconv->tv),
strip, GNT_TEXT_FLAG_DIM, "typing");
@@ -999,77 +1084,31 @@ finch_write_common(PurpleConversation *conv, const char *who, const char *messag
if (flags & (PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_NICK | PURPLE_MESSAGE_ERROR))
gnt_widget_set_urgent(ggconv->tv);
if (flags & PURPLE_MESSAGE_RECV && !gnt_widget_has_focus(ggconv->window)) {
- int count = GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count"));
- purple_conversation_set_data(conv, "unseen-count", GINT_TO_POINTER(count + 1));
- purple_conversation_update(conv, PURPLE_CONV_UPDATE_UNSEEN);
+ int count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "unseen-count"));
+ g_object_set_data(G_OBJECT(conv), "unseen-count", GINT_TO_POINTER(count + 1));
+ purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_UNSEEN);
}
}
-static void
-finch_write_chat(PurpleConversation *conv, const char *who, const char *message,
- PurpleMessageFlags flags, time_t mtime)
-{
- purple_conversation_write(conv, who, message, flags, mtime);
-}
-
-static void
-finch_write_im(PurpleConversation *conv, const char *who, const char *message,
- PurpleMessageFlags flags, time_t mtime)
-{
- PurpleAccount *account = purple_conversation_get_account(conv);
- if (flags & PURPLE_MESSAGE_SEND)
- {
- who = purple_connection_get_display_name(purple_account_get_connection(account));
- if (!who)
- who = purple_account_get_alias(account);
- if (!who)
- who = purple_account_get_username(account);
- }
- else if (flags & PURPLE_MESSAGE_RECV)
- {
- PurpleBuddy *buddy;
- who = purple_conversation_get_name(conv);
- buddy = purple_find_buddy(account, who);
- if (buddy)
- who = purple_buddy_get_contact_alias(buddy);
- }
-
- purple_conversation_write(conv, who, message, flags, mtime);
-}
-
-static void
-finch_write_conv(PurpleConversation *conv, const char *who, const char *alias,
- const char *message, PurpleMessageFlags flags, time_t mtime)
-{
- const char *name;
- if (alias && *alias)
- name = alias;
- else if (who && *who)
- name = who;
- else
- name = NULL;
-
- finch_write_common(conv, name, message, flags, mtime);
-}
-
static const char *
-chat_flag_text(PurpleConvChatBuddyFlags flags)
+chat_flag_text(PurpleChatUserFlags flags)
{
- if (flags & PURPLE_CBFLAGS_FOUNDER)
+ if (flags & PURPLE_CHAT_USER_FOUNDER)
return "~";
- if (flags & PURPLE_CBFLAGS_OP)
+ if (flags & PURPLE_CHAT_USER_OP)
return "@";
- if (flags & PURPLE_CBFLAGS_HALFOP)
+ if (flags & PURPLE_CHAT_USER_HALFOP)
return "%";
- if (flags & PURPLE_CBFLAGS_VOICE)
+ if (flags & PURPLE_CHAT_USER_VOICE)
return "+";
return " ";
}
static void
-finch_chat_add_users(PurpleConversation *conv, GList *users, gboolean new_arrivals)
+finch_chat_add_users(PurpleChatConversation *chat, GList *users, gboolean new_arrivals)
{
- FinchConv *ggc = FINCH_GET_DATA(conv);
+ PurpleConversation *conv = PURPLE_CONVERSATION(chat);
+ FinchConv *ggc = FINCH_CONV(conv);
GntEntry *entry = GNT_ENTRY(ggc->entry);
if (!new_arrivals)
@@ -1083,38 +1122,38 @@ finch_chat_add_users(PurpleConversation *conv, GList *users, gboolean new_arriva
ngettext("List of %d user:\n", "List of %d users:\n", count), count);
for (iter = users; iter; iter = iter->next)
{
- PurpleConvChatBuddy *cbuddy = iter->data;
- char *str;
+ PurpleChatUser *chatuser = iter->data;
+ const char *str;
- if ((str = cbuddy->alias) == NULL)
- str = cbuddy->name;
+ if ((str = purple_chat_user_get_alias(chatuser)) == NULL)
+ str = purple_chat_user_get_name(chatuser);
g_string_append_printf(string, "[ %s ]", str);
}
- purple_conversation_write(conv, NULL, string->str,
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(
+ conv, string->str, 0);
g_string_free(string, TRUE);
}
for (; users; users = users->next)
{
- PurpleConvChatBuddy *cbuddy = users->data;
+ PurpleChatUser *chatuser = users->data;
GntTree *tree = GNT_TREE(ggc->u.chat->userlist);
- gnt_entry_add_suggest(entry, cbuddy->name);
- gnt_entry_add_suggest(entry, cbuddy->alias);
- gnt_tree_add_row_after(tree, g_strdup(cbuddy->name),
- gnt_tree_create_row(tree, chat_flag_text(cbuddy->flags), cbuddy->alias), NULL, NULL);
+ gnt_entry_add_suggest(entry, purple_chat_user_get_name(chatuser));
+ gnt_entry_add_suggest(entry, purple_chat_user_get_alias(chatuser));
+ gnt_tree_add_row_after(tree, g_strdup(purple_chat_user_get_name(chatuser)),
+ gnt_tree_create_row(tree, chat_flag_text(purple_chat_user_get_flags(chatuser)), purple_chat_user_get_alias(chatuser)), NULL, NULL);
}
}
static void
-finch_chat_rename_user(PurpleConversation *conv, const char *old, const char *new_n, const char *new_a)
+finch_chat_rename_user(PurpleChatConversation *chat, const char *old, const char *new_n, const char *new_a)
{
/* Update the name for string completion */
- FinchConv *ggc = FINCH_GET_DATA(conv);
+ FinchConv *ggc = FINCH_CONV(PURPLE_CONVERSATION(chat));
GntEntry *entry = GNT_ENTRY(ggc->entry);
GntTree *tree = GNT_TREE(ggc->u.chat->userlist);
- PurpleConvChatBuddy *cb = purple_conv_chat_cb_find(PURPLE_CONV_CHAT(conv), new_n);
+ PurpleChatUser *cb = purple_chat_conversation_find_user(chat, new_n);
gnt_entry_remove_suggest(entry, old);
gnt_tree_remove(tree, (gpointer)old);
@@ -1122,14 +1161,14 @@ finch_chat_rename_user(PurpleConversation *conv, const char *old, const char *ne
gnt_entry_add_suggest(entry, new_n);
gnt_entry_add_suggest(entry, new_a);
gnt_tree_add_row_after(tree, g_strdup(new_n),
- gnt_tree_create_row(tree, chat_flag_text(cb->flags), new_a), NULL, NULL);
+ gnt_tree_create_row(tree, chat_flag_text(purple_chat_user_get_flags(cb)), new_a), NULL, NULL);
}
static void
-finch_chat_remove_users(PurpleConversation *conv, GList *list)
+finch_chat_remove_users(PurpleChatConversation *chat, GList *list)
{
/* Remove the name from string completion */
- FinchConv *ggc = FINCH_GET_DATA(conv);
+ FinchConv *ggc = FINCH_CONV(PURPLE_CONVERSATION(chat));
GntEntry *entry = GNT_ENTRY(ggc->entry);
for (; list; list = list->next) {
GntTree *tree = GNT_TREE(ggc->u.chat->userlist);
@@ -1139,11 +1178,18 @@ finch_chat_remove_users(PurpleConversation *conv, GList *list)
}
static void
-finch_chat_update_user(PurpleConversation *conv, const char *user)
+finch_chat_update_user(PurpleChatUser *cb)
{
- PurpleConvChatBuddy *cb = purple_conv_chat_cb_find(PURPLE_CONV_CHAT(conv), user);
- FinchConv *ggc = FINCH_GET_DATA(conv);
- gnt_tree_change_text(GNT_TREE(ggc->u.chat->userlist), (gpointer)user, 0, chat_flag_text(cb->flags));
+ PurpleChatConversation *chat;
+ FinchConv *ggc;
+ if (!cb)
+ return;
+
+ chat = purple_chat_user_get_chat(cb);
+ ggc = FINCH_CONV(PURPLE_CONVERSATION(chat));
+ gnt_tree_change_text(GNT_TREE(ggc->u.chat->userlist),
+ (gpointer)purple_chat_user_get_name(cb), 0,
+ chat_flag_text(purple_chat_user_get_flags(cb)));
}
static void
@@ -1167,8 +1213,8 @@ static PurpleConversationUiOps conv_ui_ops =
{
finch_create_conversation,
finch_destroy_conversation,
- finch_write_chat,
- finch_write_im,
+ NULL, /* write_chat */
+ NULL, /* write_im */
finch_write_conv,
finch_chat_add_users,
finch_chat_rename_user,
@@ -1176,9 +1222,6 @@ static PurpleConversationUiOps conv_ui_ops =
finch_chat_update_user,
finch_conv_present, /* present */
finch_conv_has_focus, /* has_focus */
- NULL, /* custom_smiley_add */
- NULL, /* custom_smiley_write */
- NULL, /* custom_smiley_close */
NULL, /* send_confirm */
NULL,
NULL,
@@ -1196,10 +1239,7 @@ static PurpleCmdRet
say_command_cb(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
- purple_conv_im_send(PURPLE_CONV_IM(conv), args[0]);
- else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
- purple_conv_chat_send(PURPLE_CONV_CHAT(conv), args[0]);
+ purple_conversation_send(conv, args[0]);
return PURPLE_CMD_RET_OK;
}
@@ -1212,11 +1252,7 @@ me_command_cb(PurpleConversation *conv,
char *tmp;
tmp = g_strdup_printf("/me %s", args[0]);
-
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
- purple_conv_im_send(PURPLE_CONV_IM(conv), tmp);
- else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
- purple_conv_chat_send(PURPLE_CONV_CHAT(conv), tmp);
+ purple_conversation_send(conv, tmp);
g_free(tmp);
return PURPLE_CMD_RET_OK;
@@ -1251,16 +1287,14 @@ debug_command_cb(PurpleConversation *conv,
tmp = g_string_free(str, FALSE);
} else {
- purple_conversation_write(conv, NULL, _("Supported debug options are: plugins version"),
- PURPLE_MESSAGE_NO_LOG|PURPLE_MESSAGE_ERROR, time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("Supported debug options are: plugins version"),
+ PURPLE_MESSAGE_NO_LOG | PURPLE_MESSAGE_ERROR);
return PURPLE_CMD_RET_OK;
}
markup = g_markup_escape_text(tmp, -1);
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
- purple_conv_im_send(PURPLE_CONV_IM(conv), markup);
- else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
- purple_conv_chat_send(PURPLE_CONV_CHAT(conv), markup);
+ purple_conversation_send(conv, markup);
g_free(tmp);
g_free(markup);
@@ -1310,7 +1344,7 @@ help_command_cb(PurpleConversation *conv,
g_list_free(text);
}
- purple_conversation_write(conv, NULL, s->str, PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(conv, s->str, PURPLE_MESSAGE_NO_LOG);
g_string_free(s, TRUE);
return PURPLE_CMD_RET_OK;
@@ -1368,7 +1402,7 @@ cmd_message_color(PurpleConversation *conv, const char *cmd, char **args, char *
static PurpleCmdRet
users_command_cb(PurpleConversation *conv, const char *cmd, char **args, char **error, gpointer data)
{
- FinchConv *fc = FINCH_GET_DATA(conv);
+ FinchConv *fc = FINCH_CONV(conv);
FinchConvChat *ch;
if (!fc)
return PURPLE_CMD_RET_FAILED;
@@ -1475,6 +1509,8 @@ void finch_conversation_init()
PURPLE_CALLBACK(chat_left_cb), NULL);
purple_signal_connect(purple_conversations_get_handle(), "cleared-message-history", finch_conv_get_handle(),
PURPLE_CALLBACK(cleared_message_history_cb), NULL);
+ purple_signal_connect(purple_conversations_get_handle(), "conversation-updated", finch_conv_get_handle(),
+ PURPLE_CALLBACK(conv_updated), NULL);
purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", finch_conv_get_handle(),
PURPLE_CALLBACK(buddy_signed_on_off), NULL);
purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", finch_conv_get_handle(),
@@ -1494,7 +1530,7 @@ void finch_conversation_uninit()
void finch_conversation_set_active(PurpleConversation *conv)
{
- FinchConv *ggconv = FINCH_GET_DATA(conv);
+ FinchConv *ggconv = FINCH_CONV(conv);
PurpleAccount *account;
char *title;
@@ -1509,11 +1545,13 @@ void finch_conversation_set_active(PurpleConversation *conv)
title = get_conversation_title(conv, account);
gnt_screen_rename_widget(ggconv->window, title);
g_free(title);
+
+ generate_e2ee_menu(ggconv);
}
void finch_conversation_set_info_widget(PurpleConversation *conv, GntWidget *widget)
{
- FinchConv *fc = FINCH_GET_DATA(conv);
+ FinchConv *fc = FINCH_CONV(conv);
int height, width;
gnt_box_remove_all(GNT_BOX(fc->info));
diff --git a/finch/gntconv.h b/finch/gntconv.h
index b188469bf5..4ef4367daa 100644
--- a/finch/gntconv.h
+++ b/finch/gntconv.h
@@ -1,8 +1,3 @@
-/**
- * @file gntconv.h GNT Conversation API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -23,8 +18,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _GNT_CONV_H
#define _GNT_CONV_H
+/**
+ * SECTION:gntconv
+ * @section_id: finch-gntconv
+ * @short_description: <filename>gntconv.h</filename>
+ * @title: Conversation API
+ */
#include <gnt.h>
#include <gntwidget.h>
@@ -33,12 +35,11 @@
#include "conversation.h"
/* Grabs the conv out of a PurpleConverstation */
-#define FINCH_CONV(conv) ((FinchConv *)(conv)->ui_data)
+#define FINCH_CONV(conv) ((FinchConv *)purple_conversation_get_ui_data(conv))
/***************************************************************************
- * @name GNT Conversations API
+ * GNT Conversations API
***************************************************************************/
-/*@{*/
typedef struct _FinchConv FinchConv;
typedef struct _FinchConvChat FinchConvChat;
@@ -59,6 +60,7 @@ struct _FinchConv
GntWidget *tv; /* text-view */
GntWidget *menu;
GntWidget *info;
+ GntMenuItem *plugins;
FinchConversationFlag flags;
union
@@ -71,49 +73,63 @@ struct _FinchConv
struct _FinchConvChat
{
GntWidget *userlist; /* the userlist */
- void *pad1;
- void *pad2;
+
+ void *finch_reserved1;
+ void *finch_reserved2;
+ void *finch_reserved3;
+ void *finch_reserved4;
};
struct _FinchConvIm
{
GntMenuItem *sendto;
- void *something_for_later;
+ GntMenuItem *e2ee_menu;
+
+ void *finch_reserved1;
+ void *finch_reserved2;
+ void *finch_reserved3;
+ void *finch_reserved4;
};
/**
+ * finch_conv_get_ui_ops:
+ *
* Get the ui-functions.
*
- * @return The PurpleConversationUiOps populated with the appropriate functions.
+ * Returns: The PurpleConversationUiOps populated with the appropriate functions.
*/
PurpleConversationUiOps *finch_conv_get_ui_ops(void);
/**
+ * finch_conversation_init:
+ *
* Perform the necessary initializations.
*/
void finch_conversation_init(void);
/**
+ * finch_conversation_uninit:
+ *
* Perform the necessary uninitializations.
*/
void finch_conversation_uninit(void);
/**
- * Set a conversation as active in a contactized conversation
+ * finch_conversation_set_active:
+ * @conv: The conversation to make active.
*
- * @param conv The conversation to make active.
+ * Set a conversation as active in a contactized conversation
*/
void finch_conversation_set_active(PurpleConversation *conv);
/**
- * Sets the information widget for the conversation window.
- *
- * @param conv The conversation.
- * @param widget The widget containing the information. If @c NULL,
+ * finch_conversation_set_info_widget:
+ * @conv: The conversation.
+ * @widget: The widget containing the information. If %NULL,
* the current information widget is removed.
+ *
+ * Sets the information widget for the conversation window.
*/
void finch_conversation_set_info_widget(PurpleConversation *conv, GntWidget *widget);
-/*@}*/
-
#endif
diff --git a/finch/gntdebug.c b/finch/gntdebug.c
index 7c95bea9e5..1c2c75ef46 100644
--- a/finch/gntdebug.c
+++ b/finch/gntdebug.c
@@ -1,8 +1,3 @@
-/**
- * @file gntdebug.c GNT Debug API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -77,7 +72,7 @@ handle_fprintf_stderr(gboolean stop)
}
return;
}
- if (pipe(pipes)) {
+ if (purple_input_pipe(pipes)) {
readhandle = -1;
return;
};
@@ -145,6 +140,7 @@ finch_debug_print(PurpleDebugLevel level, const char *category,
}
gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(debug.tview), args, flag);
+ gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(debug.tview), "\n", GNT_TEXT_FLAG_NORMAL);
if (pos <= 1)
gnt_text_view_scroll(GNT_TEXT_VIEW(debug.tview), 0);
}
@@ -273,7 +269,7 @@ file_save(GntFileSel *fs, const char *path, const char *file, GntTextView *tv)
FILE *fp;
if ((fp = g_fopen(path, "w+")) == NULL) {
- purple_notify_error(NULL, NULL, _("Unable to open file."), NULL);
+ purple_notify_error(NULL, NULL, _("Unable to open file."), NULL, NULL);
return;
}
diff --git a/finch/gntdebug.h b/finch/gntdebug.h
index d6bbfd656d..f9901f0e3f 100644
--- a/finch/gntdebug.h
+++ b/finch/gntdebug.h
@@ -1,8 +1,3 @@
-/**
- * @file gntdebug.h GNT Debug API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -23,38 +18,50 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _GNT_DEBUG_H
#define _GNT_DEBUG_H
+/**
+ * SECTION:gntdebug
+ * @section_id: finch-gntdebug
+ * @short_description: <filename>gntdebug.h</filename>
+ * @title: Debug API
+ */
#include "debug.h"
/**********************************************************************
- * @name GNT Debug API
+ * GNT Debug API
**********************************************************************/
-/*@{*/
/**
+ * finch_debug_get_ui_ops:
+ *
* Get the ui-functions.
*
- * @return The PurpleDebugUiOps structure populated with the appropriate functions.
+ * Returns: The PurpleDebugUiOps structure populated with the appropriate functions.
*/
PurpleDebugUiOps *finch_debug_get_ui_ops(void);
/**
+ * finch_debug_init:
+ *
* Perform necessary initializations.
*/
void finch_debug_init(void);
/**
+ * finch_debug_uninit:
+ *
* Perform necessary uninitializations.
*/
void finch_debug_uninit(void);
/**
+ * finch_debug_window_show:
+ *
* Show the debug window.
*/
void finch_debug_window_show(void);
-/*@}*/
-
#endif
diff --git a/finch/gntidle.c b/finch/gntidle.c
index d635bc3eff..a95f09bab6 100644
--- a/finch/gntidle.c
+++ b/finch/gntidle.c
@@ -25,6 +25,7 @@
#include "finch.h"
#include "gntidle.h"
+#include "gnt.h"
#include "gntwm.h"
#include "idle.h"
diff --git a/finch/gntidle.h b/finch/gntidle.h
index d1fa11dda0..ab02f9cd00 100644
--- a/finch/gntidle.h
+++ b/finch/gntidle.h
@@ -1,8 +1,3 @@
-/**
- * @file gntidle.h GNT Idle API
- * @ingroup finch
- */
-
/* finch
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -23,23 +18,29 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _GNT_IDLE_H_
#define _GNT_IDLE_H_
+/**
+ * SECTION:gntidle
+ * @section_id: finch-gntidle
+ * @short_description: <filename>gntidle.h</filename>
+ * @title: Idle API
+ */
#include "idle.h"
/**************************************************************************/
-/** @name GNT Idle API */
+/* GNT Idle API */
/**************************************************************************/
-/*@{*/
/**
+ * finch_idle_get_ui_ops:
+ *
* Returns the GNT idle UI ops.
*
- * @return The UI operations structure.
+ * Returns: The UI operations structure.
*/
PurpleIdleUiOps *finch_idle_get_ui_ops(void);
-/*@}*/
-
#endif /* _Finch_IDLE_H_ */
diff --git a/finch/gntlog.c b/finch/gntlog.c
index c946c285c6..e787df5909 100644
--- a/finch/gntlog.c
+++ b/finch/gntlog.c
@@ -1,8 +1,3 @@
-/**
- * @file gntlog.c GNT Log viewer
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -289,7 +284,7 @@ static FinchLogViewer *display_log_viewer(struct log_viewer_hash_t *ht, GList *l
g_free(ht);
}
- purple_notify_info(NULL, title, _("No logs were found"), log_preferences);
+ purple_notify_info(NULL, title, _("No logs were found"), log_preferences, NULL);
return NULL;
}
@@ -407,7 +402,7 @@ void finch_log_show(PurpleLogType type, const char *username, PurpleAccount *acc
PurpleBuddy *buddy;
if (username) {
- buddy = purple_find_buddy(account, username);
+ buddy = purple_blist_find_buddy(account, username);
if (buddy != NULL)
name = purple_buddy_get_contact_alias(buddy);
title = g_strdup_printf(_("Conversations with %s"), name);
@@ -461,7 +456,7 @@ void finch_log_show_contact(PurpleContact *contact)
child = purple_blist_node_get_sibling_next(child)) {
const char *name;
PurpleAccount *account;
- if (!PURPLE_BLIST_NODE_IS_BUDDY(child))
+ if (!PURPLE_IS_BUDDY(child))
continue;
name = purple_buddy_get_name((PurpleBuddy *)child);
@@ -481,7 +476,7 @@ void finch_log_show_contact(PurpleContact *contact)
* 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_BLIST_NODE_IS_BUDDY(child))
+ if (child != NULL && PURPLE_IS_BUDDY(child))
name = purple_buddy_get_contact_alias((PurpleBuddy *)child);
if (name == NULL)
name = "";
@@ -533,11 +528,9 @@ void finch_log_init(void)
purple_signal_register(handle, "log-displaying",
purple_marshal_VOID__POINTER_POINTER,
- NULL, 2,
- purple_value_new(PURPLE_TYPE_BOXED,
- "FinchLogViewer *"),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_LOG));
+ G_TYPE_NONE, 2,
+ G_TYPE_POINTER, /* (FinchLogViewer *) */
+ PURPLE_TYPE_LOG);
}
void
diff --git a/finch/gntlog.h b/finch/gntlog.h
index e089b62456..fe4589498d 100644
--- a/finch/gntlog.h
+++ b/finch/gntlog.h
@@ -1,8 +1,3 @@
-/**
- * @file gntlog.h GNT Log viewer
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -23,8 +18,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _FINCHLOG_H_
#define _FINCHLOG_H_
+/**
+ * SECTION:gntlog
+ * @section_id: finch-gntlog
+ * @short_description: <filename>gntlog.h</filename>
+ * @title: Log Viewer
+ */
#include "log.h"
#include "account.h"
@@ -33,19 +35,27 @@
typedef struct _FinchLogViewer FinchLogViewer;
/**
+ * FinchLogViewer:
+ * @logs: The list of logs viewed in this viewer
+ * @window: The viewer's window
+ * @tree: The tree representing said treestore
+ * @text: The text to display said logs
+ * @entry: The search entry, in which search terms are entered
+ * @flags: The most recently used log flags
+ * @search: The string currently being searched for
+ *
* A GNT Log Viewer. You can look at logs with it.
*/
struct _FinchLogViewer {
- GList *logs; /**< The list of logs viewed in this viewer */
+ GList *logs;
- GntWidget *window; /**< The viewer's window */
- GntWidget *tree; /**< The tree representing said treestore */
- GntWidget *text; /**< The text to display said logs */
- GntWidget *entry; /**< The search entry, in which search terms
- * are entered */
+ GntWidget *window;
+ GntWidget *tree;
+ GntWidget *text;
+ GntWidget *entry;
GntWidget *label;
- PurpleLogReadFlags flags; /**< The most recently used log flags */
- char *search; /**< The string currently being searched for */
+ PurpleLogReadFlags flags;
+ char *search;
};
@@ -56,27 +66,30 @@ void finch_log_show_contact(PurpleContact *contact);
void finch_syslog_show(void);
/**************************************************************************/
-/** @name GNT Log Subsystem */
+/* GNT Log Subsystem */
/**************************************************************************/
-/*@{*/
/**
+ * finch_log_init:
+ *
* Initializes the GNT log subsystem.
*/
void finch_log_init(void);
/**
+ * finch_log_get_handle:
+ *
* Returns the GNT log subsystem handle.
*
- * @return The GNT log subsystem handle.
+ * Returns: (transfer none): The GNT log subsystem handle.
*/
void *finch_log_get_handle(void);
/**
+ * finch_log_uninit:
+ *
* Uninitializes the GNT log subsystem.
*/
void finch_log_uninit(void);
-/*@}*/
-
#endif
diff --git a/finch/gntmedia.c b/finch/gntmedia.c
index 00d0a912c2..f719d6046a 100644
--- a/finch/gntmedia.c
+++ b/finch/gntmedia.c
@@ -1,8 +1,3 @@
-/**
- * @file gntmedia.c GNT Media API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -140,7 +135,7 @@ finch_media_class_init (FinchMediaClass *klass)
"PurpleMedia",
"The PurpleMedia associated with this media.",
PURPLE_TYPE_MEDIA,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
finch_media_signals[MESSAGE] = g_signal_new("message", G_TYPE_FROM_CLASS(klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL,
@@ -262,7 +257,7 @@ finch_media_state_changed_cb(PurpleMedia *media, PurpleMediaState state,
gchar *message = NULL;
account = purple_media_get_account(gntmedia->priv->media);
- buddy = purple_find_buddy(account, name);
+ buddy = purple_blist_find_buddy(account, name);
alias = buddy ? purple_buddy_get_contact_alias(buddy) : name;
if (type & PURPLE_MEDIA_AUDIO) {
@@ -378,8 +373,8 @@ finch_media_new(PurpleMedia *media)
static void
gntmedia_message_cb(FinchMedia *gntmedia, const char *msg, PurpleConversation *conv)
{
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
- purple_conv_im_write(PURPLE_CONV_IM(conv), NULL, msg, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
+ purple_conversation_write_system_message(conv, msg, 0);
}
}
@@ -390,7 +385,7 @@ finch_new_media(PurpleMediaManager *manager, PurpleMedia *media,
GntWidget *gntmedia;
PurpleConversation *conv;
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, name);
+ conv = PURPLE_CONVERSATION(purple_im_conversation_new(account, name));
gntmedia = finch_media_new(media);
g_signal_connect(G_OBJECT(gntmedia), "message", G_CALLBACK(gntmedia_message_cb), conv);
diff --git a/finch/gntmedia.h b/finch/gntmedia.h
index 95842d477d..278b3d2148 100644
--- a/finch/gntmedia.h
+++ b/finch/gntmedia.h
@@ -1,8 +1,3 @@
-/**
- * @file gntmedia.h GNT Media API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -26,6 +21,12 @@
#ifndef GNT_MEDIA_H
#define GNT_MEDIA_H
+/**
+ * SECTION:gntmedia
+ * @section_id: finch-gntmedia
+ * @short_description: <filename>gntmedia.h</filename>
+ * @title: Media API
+ */
#ifdef HAVE_CONFIG_H
#include "config.h"
diff --git a/finch/gntmenuutil.c b/finch/gntmenuutil.c
new file mode 100644
index 0000000000..0cebbf9fe5
--- /dev/null
+++ b/finch/gntmenuutil.c
@@ -0,0 +1,83 @@
+/* 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
+ * 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 "finch.h"
+
+#include "gnt.h"
+#include "gntmenu.h"
+#include "gntmenuitem.h"
+#include "gntmenuutil.h"
+
+static void
+context_menu_callback(GntMenuItem *item, gpointer data)
+{
+ PurpleMenuAction *action = data;
+ if (action) {
+ void (*callback)(gpointer, gpointer);
+ callback = (void (*)(gpointer, gpointer))
+ purple_menu_action_get_callback(action);
+ if (callback) {
+ gpointer ctx = g_object_get_data(G_OBJECT(item), "menuctx");
+ callback(ctx, purple_menu_action_get_data(action));
+ }
+ }
+}
+
+void
+finch_append_menu_action(GntMenu *menu, PurpleMenuAction *action, gpointer ctx)
+{
+ GList *list;
+ GntMenuItem *item;
+ const gchar *label;
+ gchar *clean_label = NULL;
+
+ if (action == NULL)
+ return;
+ label = purple_menu_action_get_label(action);
+
+ if (strchr(label, '_') != NULL) {
+ clean_label = g_strdup(label);
+ purple_str_strip_char(clean_label, '_');
+ label = clean_label;
+ }
+ item = gnt_menuitem_new(label);
+ g_free(clean_label);
+
+ if (purple_menu_action_get_callback(action)) {
+ gnt_menuitem_set_callback(item, context_menu_callback, action);
+ g_object_set_data(G_OBJECT(item), "menuctx", ctx);
+ }
+ gnt_menu_add_item(menu, item);
+
+ list = purple_menu_action_get_children(action);
+
+ if (list) {
+ GntWidget *sub = gnt_menu_new(GNT_MENU_POPUP);
+ gnt_menuitem_set_submenu(item, GNT_MENU(sub));
+ for (; list; list = g_list_delete_link(list, list))
+ finch_append_menu_action(GNT_MENU(sub), list->data, action);
+ purple_menu_action_set_children(action, NULL);
+ }
+
+ g_signal_connect_swapped(G_OBJECT(menu), "destroy",
+ G_CALLBACK(purple_menu_action_free), action);
+}
diff --git a/finch/gntmenuutil.h b/finch/gntmenuutil.h
new file mode 100644
index 0000000000..ba2e0db501
--- /dev/null
+++ b/finch/gntmenuutil.h
@@ -0,0 +1,49 @@
+/* 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
+ * 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 _GNT_MENUUTIL_H
+#define _GNT_MENUUTIL_H
+/**
+ * SECTION:gntmenuutil
+ * @section_id: finch-gntmenuutil
+ * @short_description: <filename>gntmenuutil.h</filename>
+ * @title: Menu Utility functions
+ */
+
+#include <gnt.h>
+#include <gntmenu.h>
+
+/***************************************************************************
+ * GNT Menu Utility Functions
+ ***************************************************************************/
+
+/**
+ * finch_append_menu_action:
+ * @menu: the GntMenu to add to
+ * @action: the PurpleMenuAction to add
+ * @ctx: the callback context, passed as the first argument to
+ * the PurpleMenuAction's PurpleCallback function.
+ *
+ * Add a PurpleMenuAction to a GntMenu.
+ */
+void finch_append_menu_action(GntMenu *menu, PurpleMenuAction *action, gpointer ctx);
+
+#endif
diff --git a/finch/gntnotify.c b/finch/gntnotify.c
index f32650d2b5..e497627ffd 100644
--- a/finch/gntnotify.c
+++ b/finch/gntnotify.c
@@ -1,8 +1,3 @@
-/**
- * @file gntnotify.c GNT Notify API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -54,7 +49,8 @@ notify_msg_window_destroy_cb(GntWidget *window, PurpleNotifyType type)
static void *
finch_notify_common(PurpleNotifyType ntype, PurpleNotifyMsgType msgtype,
- const char *title, const char *primary, const char *secondary)
+ const char *title, const char *primary, const char *secondary,
+ PurpleRequestCommonParameters *cpar)
{
GntWidget *window, *button;
GntTextFormatFlags pf = 0, sf = 0;
@@ -118,14 +114,6 @@ finch_notify_common(PurpleNotifyType ntype, PurpleNotifyMsgType msgtype,
return window;
}
-static void *
-finch_notify_message(PurpleNotifyMsgType type, const char *title,
- const char *primary, const char *secondary)
-{
- return finch_notify_common(PURPLE_NOTIFY_MESSAGE, type, title, primary,
- secondary);
-}
-
/* handle is, in all/most occasions, a GntWidget * */
static void finch_close_notify(PurpleNotifyType type, void *handle)
{
@@ -147,6 +135,15 @@ static void finch_close_notify(PurpleNotifyType type, void *handle)
gnt_widget_destroy(widget);
}
+static void *
+finch_notify_message(PurpleNotifyMsgType type, const char *title,
+ const char *primary, const char *secondary,
+ PurpleRequestCommonParameters *cpar)
+{
+ return finch_notify_common(PURPLE_NOTIFY_MESSAGE, type, title, primary,
+ secondary, cpar);
+}
+
static void *finch_notify_formatted(const char *title, const char *primary,
const char *secondary, const char *text)
{
@@ -159,7 +156,7 @@ static void *finch_notify_formatted(const char *title, const char *primary,
purple_markup_html_to_xhtml(t, &xhtml, NULL);
ret = finch_notify_common(PURPLE_NOTIFY_FORMATTED,
- PURPLE_NOTIFY_MSG_INFO, title, primary, xhtml);
+ PURPLE_NOTIFY_MSG_INFO, title, primary, xhtml, NULL);
g_free(t);
g_free(xhtml);
@@ -253,7 +250,7 @@ finch_notify_emails(PurpleConnection *gc, size_t count, gboolean detailed,
}
ret = finch_notify_common(PURPLE_NOTIFY_EMAIL, PURPLE_NOTIFY_MSG_INFO,
- _("New Mail"), _("You have mail!"), message->str);
+ _("New Mail"), _("You have mail!"), message->str, NULL);
g_string_free(message, TRUE);
return ret;
}
@@ -294,10 +291,10 @@ purple_notify_user_info_get_xhtml(PurpleNotifyUserInfo *user_info)
text = g_string_new("<span>");
- for (l = purple_notify_user_info_get_entries(user_info); l != NULL;
+ for (l = purple_notify_user_info_get_entries(user_info)->head; l != NULL;
l = l->next) {
PurpleNotifyUserInfoEntry *user_info_entry = l->data;
- PurpleNotifyUserInfoEntryType type = purple_notify_user_info_entry_get_type(user_info_entry);
+ PurpleNotifyUserInfoEntryType type = purple_notify_user_info_entry_get_entry_type(user_info_entry);
const char *label = purple_notify_user_info_entry_get_label(user_info_entry);
const char *value = purple_notify_user_info_entry_get_value(user_info_entry);
@@ -390,6 +387,7 @@ finch_notify_sr_new_rows(PurpleConnection *gc,
{
GntTree *tree = GNT_TREE(data);
GList *o;
+ GntTreeRow *prev = NULL;
/* XXX: Do I need to empty the tree here? */
@@ -397,10 +395,17 @@ finch_notify_sr_new_rows(PurpleConnection *gc,
{
gnt_tree_add_row_after(GNT_TREE(tree), o->data,
gnt_tree_create_row_from_list(GNT_TREE(tree), o->data),
- NULL, NULL);
+ NULL, prev);
+ prev = o->data;
}
}
+static void
+notify_sr_destroy_cb(GntWidget *window, void *data)
+{
+ purple_notify_close(PURPLE_NOTIFY_SEARCHRESULTS, window);
+}
+
static void *
finch_notify_searchresults(PurpleConnection *gc, const char *title,
const char *primary, const char *secondary,
@@ -433,7 +438,10 @@ finch_notify_searchresults(PurpleConnection *gc, const char *title,
for (iter = results->columns; iter; iter = iter->next)
{
PurpleNotifySearchColumn *column = iter->data;
- gnt_tree_set_column_title(GNT_TREE(tree), i, column->title);
+ gnt_tree_set_column_title(GNT_TREE(tree), i, purple_notify_searchresult_column_get_title(column));
+
+ if (!purple_notify_searchresult_column_is_visible(column))
+ gnt_tree_set_column_visible(GNT_TREE(tree), i, FALSE);
i++;
}
@@ -482,6 +490,8 @@ finch_notify_searchresults(PurpleConnection *gc, const char *title,
}
gnt_box_add_widget(GNT_BOX(window), box);
+ g_signal_connect(G_OBJECT(tree), "destroy",
+ G_CALLBACK(notify_sr_destroy_cb), NULL);
finch_notify_sr_new_rows(gc, results, tree);
@@ -495,7 +505,7 @@ static void *
finch_notify_uri(const char *url)
{
return finch_notify_common(PURPLE_NOTIFY_URI, PURPLE_NOTIFY_MSG_INFO,
- _("URI"), url, NULL);
+ _("URI"), url, NULL, NULL);
}
static PurpleNotifyUiOps ops =
diff --git a/finch/gntnotify.h b/finch/gntnotify.h
index 8d231d7aff..950acac759 100644
--- a/finch/gntnotify.h
+++ b/finch/gntnotify.h
@@ -1,8 +1,3 @@
-/**
- * @file gntnotify.h GNT Notify API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -23,34 +18,44 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _GNT_NOTIFY_H
#define _GNT_NOTIFY_H
+/**
+ * SECTION:gntnotify
+ * @section_id: finch-gntnotify
+ * @short_description: <filename>gntnotify.h</filename>
+ * @title: Notify API
+ */
#include "notify.h"
/**********************************************************************
- * @name GNT Notify API
+ * GNT Notify API
**********************************************************************/
-/*@{*/
/**
+ * finch_notify_get_ui_ops:
+ *
* Get the ui-functions.
*
- * @return The PurpleNotifyUiOps structure populated with the appropriate functions.
+ * Returns: The PurpleNotifyUiOps structure populated with the appropriate functions.
*/
PurpleNotifyUiOps *finch_notify_get_ui_ops(void);
/**
+ * finch_notify_init:
+ *
* Perform necessary initializations.
*/
void finch_notify_init(void);
/**
+ * finch_notify_uninit:
+ *
* Perform necessary uninitializations.
*/
void finch_notify_uninit(void);
-/*@}*/
-
#endif
diff --git a/finch/gntplugin.c b/finch/gntplugin.c
index cf863507a5..891f0cf3d3 100644
--- a/finch/gntplugin.c
+++ b/finch/gntplugin.c
@@ -1,8 +1,3 @@
-/**
- * @file gntplugin.c GNT Plugins API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -50,7 +45,20 @@ static struct
GntWidget *conf;
} plugins;
-static GHashTable *confwins;
+typedef struct
+{
+ enum
+ {
+ FINCH_PLUGIN_UI_DATA_TYPE_WINDOW,
+ FINCH_PLUGIN_UI_DATA_TYPE_REQUEST
+ } type;
+
+ union
+ {
+ GntWidget *window;
+ gpointer request_handle;
+ } u;
+} FinchPluginUiData;
static GntWidget *process_pref_frame(PurplePluginPrefFrame *frame);
@@ -61,14 +69,34 @@ free_stringlist(GList *list)
g_list_free(list);
}
+static gboolean
+has_prefs(PurplePlugin *plugin)
+{
+ PurplePluginUiInfo *pinfo;
+
+ if (!purple_plugin_is_loaded(plugin))
+ return FALSE;
+
+ if (PURPLE_IS_GNT_PLUGIN(plugin) &&
+ FINCH_PLUGIN_UI_INFO(plugin) != NULL)
+ {
+ return TRUE;
+ }
+
+ if (!plugin->info)
+ return FALSE;
+
+ pinfo = plugin->info->prefs_info;
+ if (!pinfo)
+ return FALSE;
+
+ return (pinfo->get_plugin_pref_frame || pinfo->get_plugin_pref_request);
+}
+
static void
decide_conf_button(PurplePlugin *plugin)
{
- if (purple_plugin_is_loaded(plugin) &&
- ((PURPLE_IS_GNT_PLUGIN(plugin) &&
- FINCH_PLUGIN_UI_INFO(plugin) != NULL) ||
- (plugin->info->prefs_info &&
- plugin->info->prefs_info->get_plugin_pref_frame)))
+ if (has_prefs(plugin))
gnt_widget_set_visible(plugins.conf, TRUE);
else
gnt_widget_set_visible(plugins.conf, FALSE);
@@ -78,29 +106,49 @@ decide_conf_button(PurplePlugin *plugin)
}
static void
+finch_plugin_pref_close(PurplePlugin *plugin)
+{
+ FinchPluginUiData *ui_data;
+
+ g_return_if_fail(plugin != NULL);
+
+ if (plugin->ui_data == NULL)
+ return;
+ ui_data = plugin->ui_data;
+
+ if (ui_data->type == FINCH_PLUGIN_UI_DATA_TYPE_REQUEST) {
+ purple_request_close(PURPLE_REQUEST_FIELDS,
+ ui_data->u.request_handle);
+ return;
+ }
+
+ g_return_if_fail(ui_data->type == FINCH_PLUGIN_UI_DATA_TYPE_WINDOW);
+
+ gnt_widget_destroy(ui_data->u.window);
+
+ g_free(ui_data);
+ plugin->ui_data = NULL;
+}
+
+static void
plugin_toggled_cb(GntWidget *tree, PurplePlugin *plugin, gpointer null)
{
if (gnt_tree_get_choice(GNT_TREE(tree), plugin))
{
if (!purple_plugin_load(plugin)) {
- purple_notify_error(NULL, _("ERROR"), _("loading plugin failed"), NULL);
+ purple_notify_error(NULL, _("ERROR"), _("loading plugin failed"), NULL, NULL);
gnt_tree_set_choice(GNT_TREE(tree), plugin, FALSE);
}
}
else
{
- GntWidget *win;
-
if (!purple_plugin_unload(plugin)) {
- purple_notify_error(NULL, _("ERROR"), _("unloading plugin failed"), NULL);
+ purple_notify_error(NULL, _("ERROR"), _("unloading plugin failed"), NULL, NULL);
purple_plugin_disable(plugin);
gnt_tree_set_choice(GNT_TREE(tree), plugin, TRUE);
}
- if (confwins && (win = g_hash_table_lookup(confwins, plugin)) != NULL)
- {
- gnt_widget_destroy(win);
- }
+ finch_plugin_pref_close(plugin);
}
decide_conf_button(plugin);
finch_plugins_save_loaded();
@@ -173,15 +221,12 @@ plugin_compare(PurplePlugin *p1, PurplePlugin *p2)
}
static void
-confwin_init(void)
+remove_confwin(GntWidget *window, gpointer _plugin)
{
- confwins = g_hash_table_new(g_direct_hash, g_direct_equal);
-}
+ PurplePlugin *plugin = _plugin;
-static void
-remove_confwin(GntWidget *window, gpointer plugin)
-{
- g_hash_table_remove(confwins, plugin);
+ g_free(plugin->ui_data);
+ plugin->ui_data = NULL;
}
static void
@@ -189,6 +234,7 @@ configure_plugin_cb(GntWidget *button, gpointer null)
{
PurplePlugin *plugin;
FinchPluginFrame callback;
+ FinchPluginUiData *ui_data;
g_return_if_fail(plugins.tree != NULL);
@@ -196,12 +242,15 @@ configure_plugin_cb(GntWidget *button, gpointer null)
if (!purple_plugin_is_loaded(plugin))
{
purple_notify_error(plugin, _("Error"),
- _("Plugin need to be loaded before you can configure it."), NULL);
+ _("Plugin need to be loaded before you can configure it."), NULL, NULL);
return;
}
- if (confwins && g_hash_table_lookup(confwins, plugin))
+ if (plugin->ui_data != NULL)
return;
+ plugin->ui_data = ui_data = g_new0(FinchPluginUiData, 1);
+
+ g_return_if_fail(plugin->info != NULL);
if (PURPLE_IS_GNT_PLUGIN(plugin) &&
(callback = FINCH_PLUGIN_UI_INFO(plugin)) != NULL)
@@ -227,25 +276,36 @@ configure_plugin_cb(GntWidget *button, gpointer null)
gnt_widget_show(window);
- if (confwins == NULL)
- confwin_init();
- g_hash_table_insert(confwins, plugin, window);
+ ui_data->type = FINCH_PLUGIN_UI_DATA_TYPE_WINDOW;
+ ui_data->u.window = window;
+ }
+ else if (plugin->info->prefs_info &&
+ plugin->info->prefs_info->get_plugin_pref_request)
+ {
+ gpointer handle;
+
+ ui_data->type = FINCH_PLUGIN_UI_DATA_TYPE_REQUEST;
+ ui_data->u.request_handle = handle = plugin->info->prefs_info->
+ get_plugin_pref_request(plugin);
+ purple_request_add_close_notify(handle,
+ purple_callback_set_zero, &plugin->ui_data);
+ purple_request_add_close_notify(handle, g_free, ui_data);
}
else if (plugin->info->prefs_info &&
plugin->info->prefs_info->get_plugin_pref_frame)
{
GntWidget *win = process_pref_frame(plugin->info->prefs_info->get_plugin_pref_frame(plugin));
- if (confwins == NULL)
- confwin_init();
g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(remove_confwin), plugin);
- g_hash_table_insert(confwins, plugin, win);
- return;
+
+ ui_data->type = FINCH_PLUGIN_UI_DATA_TYPE_WINDOW;
+ ui_data->u.window = win;
}
else
{
- purple_notify_info(plugin, _("Error"),
- _("No configuration options for this plugin."), NULL);
- return;
+ purple_notify_info(plugin, _("Error"), _("No configuration "
+ "options for this plugin."), NULL, NULL);
+ g_free(ui_data);
+ plugin->ui_data = NULL;
}
}
@@ -267,7 +327,7 @@ install_selected_file_cb(gpointer handle, const char *filename)
if (!plugin) {
purple_notify_error(handle, _("Error loading plugin"),
_("The selected file is not a valid plugin."),
- _("Please open the debug window and try again to see the exact error message."));
+ _("Please open the debug window and try again to see the exact error message."), NULL);
return;
}
if (g_list_find(gnt_tree_get_rows(GNT_TREE(plugins.tree)), plugin)) {
@@ -332,7 +392,7 @@ install_plugin_cb(GntWidget *w, gpointer null)
purple_request_close_with_handle(&handle);
purple_request_file(&handle, _("Select plugin to install"), NULL,
FALSE, G_CALLBACK(install_selected_file_cb), NULL,
- NULL, NULL, NULL, &handle);
+ NULL, &handle);
g_signal_connect_swapped(G_OBJECT(w), "destroy", G_CALLBACK(purple_request_close_with_handle), &handle);
}
@@ -449,7 +509,7 @@ process_pref_frame(PurplePluginPrefFrame *frame)
if(label == NULL)
continue;
- if(purple_plugin_pref_get_type(pref) == PURPLE_PLUGIN_PREF_INFO) {
+ if(purple_plugin_pref_get_pref_type(pref) == PURPLE_PLUGIN_PREF_INFO) {
field = purple_request_field_label_new("*", purple_plugin_pref_get_label(pref));
purple_request_field_group_add_field(group, field);
} else {
@@ -460,8 +520,8 @@ process_pref_frame(PurplePluginPrefFrame *frame)
}
field = NULL;
- type = purple_prefs_get_type(name);
- if(purple_plugin_pref_get_type(pref) == PURPLE_PLUGIN_PREF_CHOICE) {
+ type = purple_prefs_get_pref_type(name);
+ if(purple_plugin_pref_get_pref_type(pref) == PURPLE_PLUGIN_PREF_CHOICE) {
GList *list = purple_plugin_pref_get_choices(pref);
gpointer current_value = NULL;
@@ -510,7 +570,7 @@ process_pref_frame(PurplePluginPrefFrame *frame)
field = purple_request_field_bool_new(name, label, purple_prefs_get_bool(name));
break;
case PURPLE_PREF_INT:
- field = purple_request_field_int_new(name, label, purple_prefs_get_int(name));
+ field = purple_request_field_int_new(name, label, purple_prefs_get_int(name), INT_MIN, INT_MAX);
break;
case PURPLE_PREF_STRING:
field = purple_request_field_string_new(name, label, purple_prefs_get_string(name),
@@ -532,8 +592,7 @@ process_pref_frame(PurplePluginPrefFrame *frame)
ret = purple_request_fields(NULL, _("Preferences"), NULL, NULL, fields,
_("Save"), G_CALLBACK(finch_request_save_in_prefs), _("Cancel"), NULL,
- NULL, NULL, NULL,
- NULL);
+ NULL, NULL);
g_signal_connect_swapped(G_OBJECT(ret), "destroy", G_CALLBACK(free_stringlist), stringlist);
return ret;
}
diff --git a/finch/gntplugin.h b/finch/gntplugin.h
index b97b54d74c..39969b8903 100644
--- a/finch/gntplugin.h
+++ b/finch/gntplugin.h
@@ -1,8 +1,3 @@
-/**
- * @file gntplugin.h GNT Plugins API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -23,8 +18,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _GNT_PLUGIN_H
#define _GNT_PLUGIN_H
+/**
+ * SECTION:gntplugin
+ * @section_id: finch-gntplugin
+ * @short_description: <filename>gntplugin.h</filename>
+ * @title: Plugin API
+ */
#include <gnt.h>
@@ -36,9 +38,8 @@
#include "finch.h"
/**********************************************************************
- * @name GNT Plugins API
+ * GNT Plugins API
**********************************************************************/
-/*@{*/
typedef GntWidget* (*FinchPluginFrame) (void);
@@ -46,6 +47,8 @@ typedef GntWidget* (*FinchPluginFrame) (void);
#define FINCH_PLUGIN_TYPE FINCH_UI
/**
+ * PURPLE_IS_GNT_PLUGIN:
+ *
* Decide whether a plugin is a GNT-plugin.
*/
#define PURPLE_IS_GNT_PLUGIN(plugin) \
@@ -53,21 +56,25 @@ typedef GntWidget* (*FinchPluginFrame) (void);
!strcmp((plugin)->info->ui_requirement, FINCH_PLUGIN_TYPE))
/**
+ * FINCH_PLUGIN_UI_INFO:
+ *
* Get the ui-info from GNT-plugins.
*/
#define FINCH_PLUGIN_UI_INFO(plugin) \
(FinchPluginFrame)((plugin)->info->ui_info)
/**
+ * finch_plugins_show_all:
+ *
* Show a list of plugins.
*/
void finch_plugins_show_all(void);
/**
+ * finch_plugins_save_loaded:
+ *
* Save the list of loaded plugins.
*/
void finch_plugins_save_loaded(void);
-/*@}*/
-
#endif
diff --git a/finch/gntpounce.c b/finch/gntpounce.c
index c5fc691a54..6973359571 100644
--- a/finch/gntpounce.c
+++ b/finch/gntpounce.c
@@ -1,8 +1,3 @@
-/**
- * @file gntpounce.c GNT Buddy Pounce API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -173,7 +168,7 @@ setup_buddy_list_suggestion(GntEntry *entry, gboolean offline)
{
PurpleBlistNode *node = purple_blist_get_root();
for (; node; node = purple_blist_node_next(node, offline)) {
- if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if (!PURPLE_IS_BUDDY(node))
continue;
gnt_entry_add_suggest(entry, purple_buddy_get_name((PurpleBuddy*)node));
}
@@ -192,7 +187,7 @@ save_pounce_cb(GntWidget *w, PurpleGntPounceDialog *dialog)
if (*name == '\0')
{
purple_notify_error(NULL, NULL,
- _("Please enter a buddy to pounce."), NULL);
+ _("Please enter a buddy to pounce."), NULL, NULL);
return;
}
@@ -558,7 +553,7 @@ finch_pounce_editor_show(PurpleAccount *account, const char *name,
PurpleBuddy *buddy = NULL;
if (name != NULL)
- buddy = purple_find_buddy(account, name);
+ buddy = purple_blist_find_buddy(account, name);
/* Set some defaults */
if (buddy == NULL) {
@@ -631,7 +626,7 @@ pounces_manager_add_cb(GntButton *button, gpointer user_data)
if (purple_accounts_get_all() == NULL) {
purple_notify_error(NULL, _("Cannot create pounce"),
_("You do not have any accounts."),
- _("You must create an account first before you can create a pounce."));
+ _("You must create an account first before you can create a pounce."), NULL);
return;
}
finch_pounce_editor_show(NULL, NULL, NULL);
@@ -675,8 +670,7 @@ pounces_manager_delete_cb(GntButton *button, gpointer user_data)
pouncee = purple_pounce_get_pouncee(pounce);
buf = g_strdup_printf(_("Are you sure you want to delete the pounce on %s for %s?"), pouncee, pouncer);
purple_request_action(pounce, NULL, buf, NULL, 0,
- account, pouncee, NULL,
- pounce, 2,
+ purple_request_cpar_from_account(account), pounce, 2,
_("Delete"), pounces_manager_delete_confirm_cb,
_("Cancel"), NULL);
g_free(buf);
@@ -780,7 +774,7 @@ finch_pounces_manager_hide(void)
static void
pounce_cb(PurplePounce *pounce, PurplePounceEvent events, void *data)
{
- PurpleConversation *conv;
+ PurpleIMConversation *im;
PurpleAccount *account;
PurpleBuddy *buddy;
const char *pouncee;
@@ -789,7 +783,7 @@ pounce_cb(PurplePounce *pounce, PurplePounceEvent events, void *data)
pouncee = purple_pounce_get_pouncee(pounce);
account = purple_pounce_get_pouncer(pounce);
- buddy = purple_find_buddy(account, pouncee);
+ buddy = purple_blist_find_buddy(account, pouncee);
if (buddy != NULL)
{
alias = purple_buddy_get_alias(buddy);
@@ -801,8 +795,8 @@ pounce_cb(PurplePounce *pounce, PurplePounceEvent events, void *data)
if (purple_pounce_action_is_enabled(pounce, "open-window"))
{
- if (!purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, pouncee, account))
- purple_conversation_new(PURPLE_CONV_TYPE_IM, account, pouncee);
+ if (!purple_conversations_find_im_with_account(pouncee, account))
+ purple_im_conversation_new(account, pouncee);
}
if (purple_pounce_action_is_enabled(pounce, "popup-notify"))
@@ -849,17 +843,17 @@ pounce_cb(PurplePounce *pounce, PurplePounceEvent events, void *data)
* NULL to the account alias if we have it or the account
* name if that's all we have
*/
- if ((name_shown = purple_account_get_alias(account)) == NULL)
+ if ((name_shown = purple_account_get_private_alias(account)) == NULL)
name_shown = purple_account_get_username(account);
if (reason == NULL)
{
- purple_notify_info(NULL, name_shown, tmp, purple_date_format_full(NULL));
+ purple_notify_info(NULL, name_shown, tmp, purple_date_format_full(NULL), NULL);
}
else
{
char *tmp2 = g_strdup_printf("%s\n\n%s", reason, purple_date_format_full(NULL));
- purple_notify_info(NULL, name_shown, tmp, tmp2);
+ purple_notify_info(NULL, name_shown, tmp, tmp2, NULL);
g_free(tmp2);
}
g_free(tmp);
@@ -874,20 +868,24 @@ pounce_cb(PurplePounce *pounce, PurplePounceEvent events, void *data)
if (message != NULL)
{
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, pouncee, account);
+ PurpleMessage *pmsg;
- if (conv == NULL)
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, pouncee);
+ im = purple_conversations_find_im_with_account(pouncee, account);
- purple_conversation_write(conv, NULL, message,
- PURPLE_MESSAGE_SEND, time(NULL));
+ if (im == NULL)
+ im = purple_im_conversation_new(account, pouncee);
- serv_send_im(purple_account_get_connection(account), (char *)pouncee, (char *)message, 0);
+ pmsg = purple_message_new_outgoing(pouncee, message, 0);
+ purple_serv_send_im(purple_account_get_connection(account), pmsg);
+ purple_conversation_write_message(PURPLE_CONVERSATION(im), pmsg);
}
}
if (purple_pounce_action_is_enabled(pounce, "execute-command"))
{
+#ifdef _WIN32
+ purple_debug_error("gntpounce", "execute-command is not supported on this OS");
+#else
const char *command;
command = purple_pounce_action_get_attribute(pounce,
@@ -917,6 +915,7 @@ pounce_cb(PurplePounce *pounce, PurplePounceEvent events, void *data)
g_free(localecmd);
}
}
+#endif
}
if (purple_pounce_action_is_enabled(pounce, "play-beep"))
@@ -979,11 +978,11 @@ finch_pounces_init(void)
PURPLE_CALLBACK(signed_on_off_cb), NULL);
}
-/* XXX: There's no such thing in pidgin. Perhaps there should be? */
+/* XXX: There's no such thing in pidgin. Perhaps there should be?
+ * For sure, we don't need purple_pounces_unregister_handler -
+ * it's wiped in purple_pounces_uninit.
+ */
void finch_pounces_uninit()
{
- purple_pounces_unregister_handler(FINCH_UI);
-
purple_signals_disconnect_by_handle(finch_pounces_get_handle());
}
-
diff --git a/finch/gntpounce.h b/finch/gntpounce.h
index 6b72abe0ca..79618f9579 100644
--- a/finch/gntpounce.h
+++ b/finch/gntpounce.h
@@ -1,8 +1,3 @@
-/**
- * @file gntpounce.h GNT Buddy Pounce API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -23,46 +18,64 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _FINCHPOUNCE_H_
#define _FINCHPOUNCE_H_
+/**
+ * SECTION:gntpounce
+ * @section_id: finch-gntpounce
+ * @short_description: <filename>gntpounce.h</filename>
+ * @title: Buddy Pounce API
+ */
#include "pounce.h"
/**
- * Displays a New Buddy Pounce or Edit Buddy Pounce dialog.
+ * finch_pounce_editor_show:
+ * @account: The optional account to use.
+ * @name: The optional name to pounce on.
+ * @cur_pounce: The current buddy pounce, if editing an existing one.
*
- * @param account The optional account to use.
- * @param name The optional name to pounce on.
- * @param cur_pounce The current buddy pounce, if editing an existing one.
+ * Displays a New Buddy Pounce or Edit Buddy Pounce dialog.
*/
void finch_pounce_editor_show(PurpleAccount *account, const char *name,
PurplePounce *cur_pounce);
/**
+ * finch_pounces_manager_show:
+ *
* Shows the pounces manager window.
*/
void finch_pounces_manager_show(void);
/**
+ * finch_pounces_manager_hide:
+ *
* Hides the pounces manager window.
*/
void finch_pounces_manager_hide(void);
/**
- * Returns the gtkpounces handle
+ * finch_pounces_get_handle:
*
- * @return The handle to the GTK+ pounces system
+ * Returns the GNT pounces handle
+ *
+ * Returns: (transfer none): The handle to the GNT pounces system
*/
void *finch_pounces_get_handle(void);
/**
+ * finch_pounces_init:
+ *
* Initializes the GNT pounces subsystem.
*/
void finch_pounces_init(void);
/**
+ * finch_pounces_uninit:
+ *
* Uninitializes the GNT pounces subsystem.
*/
void finch_pounces_uninit(void);
-#endif /* _PURPLE_GTKPOUNCE_H_ */
+#endif /* _PURPLE_FINCHPOUNCE_H_ */
diff --git a/finch/gntprefs.c b/finch/gntprefs.c
index 308962d895..17a80c6dc8 100644
--- a/finch/gntprefs.c
+++ b/finch/gntprefs.c
@@ -1,8 +1,3 @@
-/**
- * @file gntprefs.c GNT Preferences API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -41,6 +36,7 @@ static struct {
GList *freestrings; /* strings to be freed when the pref-window is closed */
gboolean showing;
GntWidget *window;
+ GntWidget *keyring_window;
} pref_request;
void finch_prefs_init()
@@ -62,14 +58,6 @@ void finch_prefs_init()
void finch_prefs_update_old()
{
- const char *str = NULL;
-
- purple_prefs_rename("/gaim/gnt", "/finch");
- purple_prefs_rename("/purple/gnt", "/finch");
-
- if ((str = purple_prefs_get_string("/purple/away/idle_reporting")) &&
- strcmp(str, "gaim") == 0)
- purple_prefs_set_string("/purple/away/idle_reporting", "purple");
}
typedef struct
@@ -131,7 +119,7 @@ get_pref_field(Prefs *prefs)
break;
case PURPLE_PREF_INT:
field = purple_request_field_int_new(prefs->pref, _(prefs->label),
- purple_prefs_get_int(prefs->pref));
+ purple_prefs_get_int(prefs->pref), INT_MIN, INT_MAX);
break;
case PURPLE_PREF_STRING:
field = purple_request_field_string_new(prefs->pref, _(prefs->label),
@@ -205,6 +193,12 @@ static Prefs logging[] =
{PURPLE_PREF_NONE, NULL, NULL, NULL},
};
+static Prefs keyring[] =
+{
+ {PURPLE_PREF_STRING, "/purple/keyring/active", N_("Active keyring"), purple_keyring_get_options},
+ {PURPLE_PREF_NONE, NULL, NULL, NULL}
+};
+
static Prefs idle[] =
{
{PURPLE_PREF_STRING, "/purple/away/idle_reporting", N_("Report Idle time"), get_idle_options},
@@ -256,17 +250,58 @@ void finch_prefs_show_all()
return;
}
+ if (pref_request.keyring_window != NULL)
+ purple_request_close(PURPLE_REQUEST_FIELDS,
+ pref_request.keyring_window);
+
fields = purple_request_fields_new();
add_pref_group(fields, _("Buddy List"), blist);
add_pref_group(fields, _("Conversations"), convs);
+ add_pref_group(fields, _("Keyring"), keyring);
add_pref_group(fields, _("Logging"), logging);
add_pref_group(fields, _("Idle"), idle);
pref_request.showing = TRUE;
pref_request.window = purple_request_fields(NULL, _("Preferences"), NULL, NULL, fields,
_("Save"), G_CALLBACK(save_cb), _("Cancel"), free_strings,
- NULL, NULL, NULL,
- NULL);
+ NULL, NULL);
+}
+
+static void
+finch_prefs_keyring_save(void *data, PurpleRequestFields *fields)
+{
+ pref_request.keyring_window = NULL;
+
+ purple_keyring_apply_settings(NULL, fields);
}
+static void
+finch_prefs_keyring_cancel(void)
+{
+ pref_request.keyring_window = NULL;
+}
+
+void finch_prefs_show_keyring(void)
+{
+ PurpleRequestFields *fields;
+
+ if (pref_request.keyring_window != NULL) {
+ gnt_window_present(pref_request.keyring_window);
+ return;
+ }
+
+ fields = purple_keyring_read_settings();
+ if (fields == NULL) {
+ purple_notify_info(NULL, _("Keyring settings"),
+ _("Selected keyring doesn't allow any configuration"),
+ NULL, NULL);
+ return;
+ }
+
+ pref_request.keyring_window = purple_request_fields(NULL,
+ _("Keyring settings"), NULL, NULL, fields,
+ _("Save"), G_CALLBACK(finch_prefs_keyring_save),
+ _("Cancel"), G_CALLBACK(finch_prefs_keyring_cancel),
+ NULL, NULL);
+}
diff --git a/finch/gntprefs.h b/finch/gntprefs.h
index b11a3141f9..8303b24ee5 100644
--- a/finch/gntprefs.h
+++ b/finch/gntprefs.h
@@ -1,8 +1,3 @@
-/**
- * @file gntprefs.h GNT Preferences API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -23,28 +18,46 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _GNT_PREFS_H
#define _GNT_PREFS_H
+/**
+ * SECTION:gntprefs
+ * @section_id: finch-gntprefs
+ * @short_description: <filename>gntprefs.h</filename>
+ * @title: Preferences API
+ */
/**********************************************************************
- * @name GNT Preferences API
+ * GNT Preferences API
**********************************************************************/
-/*@{*/
/**
+ * finch_prefs_init:
+ *
* Perform necessary initializations.
*/
void finch_prefs_init(void);
/**
+ * finch_prefs_show_all:
+ *
* Show the preferences dialog.
*/
void finch_prefs_show_all(void);
/**
+ * finch_prefs_show_keyring:
+ *
+ * Show the preferences dialog for the selected keyring.
+ */
+void finch_prefs_show_keyring(void);
+
+/**
+ * finch_prefs_update_old:
+ *
* You don't need to know about this.
*/
void finch_prefs_update_old(void);
-/*@}*/
#endif
diff --git a/finch/gntrequest.c b/finch/gntrequest.c
index 851db7e240..f8e5f37a82 100644
--- a/finch/gntrequest.c
+++ b/finch/gntrequest.c
@@ -1,8 +1,3 @@
-/**
- * @file gntrequest.c GNT Request API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -41,12 +36,6 @@
#include "debug.h"
#include "util.h"
-/* XXX: Until gobjectification ... */
-#undef FINCH_GET_DATA
-#undef FINCH_SET_DATA
-#define FINCH_GET_DATA(obj) purple_request_field_get_ui_data(obj)
-#define FINCH_SET_DATA(obj, data) purple_request_field_set_ui_data(obj, data)
-
typedef struct
{
void *user_data;
@@ -78,7 +67,7 @@ setup_request_window(const char *title, const char *primary,
return window;
}
-/**
+/*
* If the window is closed by the wm (ie, without triggering any of
* the buttons, then do some default callback.
*/
@@ -100,12 +89,13 @@ action_performed(GntWidget *button, gpointer data)
NULL);
}
-/**
- * window: this is the window
- * userdata: the userdata to pass to the primary callbacks
- * cb: the callback
- * data: data for the callback
- * (text, primary-callback) pairs, ended by a NULL
+/*
+ * setup_button_box:
+ * @win: this is the window
+ * @userdata: the userdata to pass to the primary callbacks
+ * @cb: the callback
+ * @data: data for the callback
+ * @...: (text, primary-callback) pairs, ended by a NULL
*
* The cancellation callback should be the last callback sent.
*/
@@ -162,7 +152,7 @@ finch_request_input(const char *title, const char *primary,
gboolean multiline, gboolean masked, gchar *hint,
const char *ok_text, GCallback ok_cb,
const char *cancel_text, GCallback cancel_cb,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
+ PurpleRequestCommonParameters *cpar,
void *user_data)
{
GntWidget *window, *box, *entry;
@@ -203,7 +193,7 @@ request_choice_cb(GntWidget *button, GntComboBox *combo)
{
PurpleRequestChoiceCb callback = g_object_get_data(G_OBJECT(button), "activate-callback");
gpointer data = g_object_get_data(G_OBJECT(button), "activate-userdata");
- int choice = GPOINTER_TO_INT(gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo))) - 1;
+ gpointer choice = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo));
if (callback)
callback(data, choice);
@@ -216,10 +206,10 @@ request_choice_cb(GntWidget *button, GntComboBox *combo)
static void *
finch_request_choice(const char *title, const char *primary,
- const char *secondary, int default_value,
+ const char *secondary, gpointer default_value,
const char *ok_text, GCallback ok_cb,
const char *cancel_text, GCallback cancel_cb,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
+ PurpleRequestCommonParameters *cpar,
void *user_data, va_list choices)
{
GntWidget *window, *combo, *box;
@@ -235,7 +225,7 @@ finch_request_choice(const char *title, const char *primary,
val = va_arg(choices, int);
gnt_combo_box_add_data(GNT_COMBO_BOX(combo), GINT_TO_POINTER(val + 1), text);
}
- gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), GINT_TO_POINTER(default_value + 1));
+ gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), default_value);
box = setup_button_box(window, user_data, request_choice_cb, combo,
ok_text, ok_cb, cancel_text, cancel_cb, NULL);
@@ -263,12 +253,12 @@ request_action_cb(GntWidget *button, GntWidget *window)
static void*
finch_request_action(const char *title, const char *primary,
const char *secondary, int default_value,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
+ PurpleRequestCommonParameters *cpar,
void *user_data, size_t actioncount,
va_list actions)
{
GntWidget *window, *box, *button, *focus = NULL;
- int i;
+ gsize i;
window = setup_request_window(title, primary, secondary, PURPLE_REQUEST_ACTION);
@@ -287,7 +277,7 @@ finch_request_action(const char *title, const char *primary,
g_object_set_data(G_OBJECT(button), "activate-id", GINT_TO_POINTER(i));
g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(request_action_cb), window);
- if (i == default_value)
+ if (default_value >= 0 && i == (gsize)default_value)
focus = button;
}
@@ -318,41 +308,40 @@ request_fields_cb(GntWidget *button, PurpleRequestFields *fields)
for (; fields ; fields = fields->next)
{
PurpleRequestField *field = fields->data;
- PurpleRequestFieldType type = purple_request_field_get_type(field);
+ PurpleRequestFieldType type = purple_request_field_get_field_type(field);
if (!purple_request_field_is_visible(field))
continue;
if (type == PURPLE_REQUEST_FIELD_BOOLEAN)
{
- GntWidget *check = FINCH_GET_DATA(field);
+ GntWidget *check = purple_request_field_get_ui_data(field);
gboolean value = gnt_check_box_get_checked(GNT_CHECK_BOX(check));
purple_request_field_bool_set_value(field, value);
}
else if (type == PURPLE_REQUEST_FIELD_STRING)
{
- GntWidget *entry = FINCH_GET_DATA(field);
+ GntWidget *entry = purple_request_field_get_ui_data(field);
const char *text = gnt_entry_get_text(GNT_ENTRY(entry));
purple_request_field_string_set_value(field, (text && *text) ? text : NULL);
}
else if (type == PURPLE_REQUEST_FIELD_INTEGER)
{
- GntWidget *entry = FINCH_GET_DATA(field);
+ GntWidget *entry = purple_request_field_get_ui_data(field);
const char *text = gnt_entry_get_text(GNT_ENTRY(entry));
int value = (text && *text) ? atoi(text) : 0;
purple_request_field_int_set_value(field, value);
}
else if (type == PURPLE_REQUEST_FIELD_CHOICE)
{
- GntWidget *combo = FINCH_GET_DATA(field);
- int id;
- id = GPOINTER_TO_INT(gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo)));
- purple_request_field_choice_set_value(field, id);
+ GntWidget *combo = purple_request_field_get_ui_data(field);
+ gpointer value = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo));
+ purple_request_field_choice_set_value(field, value);
}
else if (type == PURPLE_REQUEST_FIELD_LIST)
{
GList *list = NULL, *iter;
if (purple_request_field_list_get_multi_select(field))
{
- GntWidget *tree = FINCH_GET_DATA(field);
+ GntWidget *tree = purple_request_field_get_ui_data(field);
iter = purple_request_field_list_get_items(field);
for (; iter; iter = iter->next)
@@ -365,7 +354,7 @@ request_fields_cb(GntWidget *button, PurpleRequestFields *fields)
}
else
{
- GntWidget *combo = FINCH_GET_DATA(field);
+ GntWidget *combo = purple_request_field_get_ui_data(field);
gpointer data = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo));
iter = purple_request_field_list_get_items(field);
@@ -384,7 +373,7 @@ request_fields_cb(GntWidget *button, PurpleRequestFields *fields)
}
else if (type == PURPLE_REQUEST_FIELD_ACCOUNT)
{
- GntWidget *combo = FINCH_GET_DATA(field);
+ GntWidget *combo = purple_request_field_get_ui_data(field);
PurpleAccount *acc = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo));
purple_request_field_account_set_value(field, acc);
}
@@ -394,10 +383,11 @@ request_fields_cb(GntWidget *button, PurpleRequestFields *fields)
purple_notify_close_with_handle(button);
if (!g_object_get_data(G_OBJECT(button), "cancellation-function") &&
- !purple_request_fields_all_required_filled(fields)) {
+ (!purple_request_fields_all_required_filled(fields) ||
+ !purple_request_fields_all_valid(fields))) {
purple_notify_error(button, _("Error"),
- _("You must fill all the required fields."),
- _("The required fields are underlined."));
+ _("You must properly fill all the required fields."),
+ _("The required fields are underlined."), NULL);
return;
}
@@ -417,7 +407,7 @@ update_selected_account(GntEntry *username, const char *start, const char *end,
GList *accounts = gnt_tree_get_rows(GNT_TREE(accountlist->dropdown));
const char *name = gnt_entry_get_text(username);
while (accounts) {
- if (purple_find_buddy(accounts->data, name)) {
+ if (purple_blist_find_buddy(accounts->data, name)) {
gnt_combo_box_set_selected(accountlist, accounts->data);
gnt_widget_draw(GNT_WIDGET(accountlist));
break;
@@ -439,7 +429,7 @@ create_boolean_field(PurpleRequestField *field)
static GntWidget*
create_string_field(PurpleRequestField *field, GntWidget **username)
{
- const char *hint = purple_request_field_get_type_hint(field);
+ const char *hint = purple_request_field_get_field_type_hint(field);
GntWidget *entry = gnt_entry_new(
purple_request_field_string_get_default_value(field));
gnt_entry_set_masked(GNT_ENTRY(entry),
@@ -448,7 +438,7 @@ create_string_field(PurpleRequestField *field, GntWidget **username)
PurpleBlistNode *node = purple_blist_get_root();
gboolean offline = purple_str_has_suffix(hint, "all");
for (; node; node = purple_blist_node_next(node, offline)) {
- if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if (!PURPLE_IS_BUDDY(node))
continue;
gnt_entry_add_suggest(GNT_ENTRY(entry), purple_buddy_get_name((PurpleBuddy*)node));
}
@@ -459,7 +449,7 @@ create_string_field(PurpleRequestField *field, GntWidget **username)
PurpleBlistNode *node;
for (node = purple_blist_get_root(); node;
node = purple_blist_node_get_sibling_next(node)) {
- if (PURPLE_BLIST_NODE_IS_GROUP(node))
+ if (PURPLE_IS_GROUP(node))
gnt_entry_add_suggest(GNT_ENTRY(entry), purple_group_get_name((PurpleGroup *)node));
}
}
@@ -482,18 +472,25 @@ create_integer_field(PurpleRequestField *field)
static GntWidget*
create_choice_field(PurpleRequestField *field)
{
- int id;
- GList *list;
+ GList *it;
GntWidget *combo = gnt_combo_box_new();
- list = purple_request_field_choice_get_labels(field);
- for (id = 1; list; list = list->next, id++)
+ it = purple_request_field_choice_get_elements(field);
+ while (it != NULL)
{
- gnt_combo_box_add_data(GNT_COMBO_BOX(combo),
- GINT_TO_POINTER(id), list->data);
+ const gchar *text;
+ gpointer value;
+
+ text = it->data;
+ it = g_list_next(it);
+ g_assert(it != NULL);
+ value = it->data;
+ it = g_list_next(it);
+
+ gnt_combo_box_add_data(GNT_COMBO_BOX(combo), value, text);
}
gnt_combo_box_set_selected(GNT_COMBO_BOX(combo),
- GINT_TO_POINTER(purple_request_field_choice_get_default_value(field)));
+ purple_request_field_choice_get_default_value(field));
return combo;
}
@@ -577,17 +574,54 @@ create_account_field(PurpleRequestField *field)
return combo;
}
+static GntWidget*
+create_certificate_field(PurpleRequestField *field)
+{
+ GntWidget *w;
+ PurpleCertificate *cert;
+ char *str;
+
+ cert = purple_request_field_certificate_get_value(field);
+ str = purple_certificate_get_display_string(cert);
+ w = gnt_label_new(str);
+
+ g_free(str);
+
+ return w;
+}
+
+static void
+multifield_extra_cb(GntWidget *button, PurpleRequestFields *allfields)
+{
+ PurpleRequestFieldsCb cb;
+ gpointer cb_data;
+ gpointer handle;
+
+ handle = g_object_get_data(G_OBJECT(button), "ui-handle");
+ cb = g_object_get_data(G_OBJECT(button), "extra-cb");
+ cb_data = g_object_get_data(G_OBJECT(button), "extra-cb-data");
+
+ if (cb != NULL)
+ cb(cb_data, allfields);
+
+ action_performed(button, handle);
+ purple_request_close(PURPLE_REQUEST_FIELDS, handle);
+}
+
static void *
finch_request_fields(const char *title, const char *primary,
const char *secondary, PurpleRequestFields *allfields,
const char *ok, GCallback ok_cb,
const char *cancel, GCallback cancel_cb,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
+ PurpleRequestCommonParameters *cpar,
void *userdata)
{
GntWidget *window, *box;
GList *grlist;
GntWidget *username = NULL, *accountlist = NULL;
+ PurpleRequestHelpCb help_cb;
+ gpointer help_data;
+ GSList *extra_actions, *it;
window = setup_request_window(title, primary, secondary, PURPLE_REQUEST_FIELDS);
@@ -611,7 +645,7 @@ finch_request_fields(const char *title, const char *primary,
for (; fields ; fields = fields->next)
{
PurpleRequestField *field = fields->data;
- PurpleRequestFieldType type = purple_request_field_get_type(field);
+ PurpleRequestFieldType type = purple_request_field_get_field_type(field);
const char *label = purple_request_field_get_label(field);
if (!purple_request_field_is_visible(field))
@@ -633,36 +667,40 @@ finch_request_fields(const char *title, const char *primary,
if (type == PURPLE_REQUEST_FIELD_BOOLEAN)
{
- FINCH_SET_DATA(field, create_boolean_field(field));
+ purple_request_field_set_ui_data(field, create_boolean_field(field));
}
else if (type == PURPLE_REQUEST_FIELD_STRING)
{
- FINCH_SET_DATA(field, create_string_field(field, &username));
+ purple_request_field_set_ui_data(field, create_string_field(field, &username));
}
else if (type == PURPLE_REQUEST_FIELD_INTEGER)
{
- FINCH_SET_DATA(field, create_integer_field(field));
+ purple_request_field_set_ui_data(field, create_integer_field(field));
}
else if (type == PURPLE_REQUEST_FIELD_CHOICE)
{
- FINCH_SET_DATA(field, create_choice_field(field));
+ purple_request_field_set_ui_data(field, create_choice_field(field));
}
else if (type == PURPLE_REQUEST_FIELD_LIST)
{
- FINCH_SET_DATA(field, create_list_field(field));
+ purple_request_field_set_ui_data(field, create_list_field(field));
}
else if (type == PURPLE_REQUEST_FIELD_ACCOUNT)
{
accountlist = create_account_field(field);
- FINCH_SET_DATA(field, accountlist);
+ purple_request_field_set_ui_data(field, accountlist);
+ }
+ else if (type == PURPLE_REQUEST_FIELD_CERTIFICATE)
+ {
+ purple_request_field_set_ui_data(field, create_certificate_field(field));
}
else
{
- FINCH_SET_DATA(field, gnt_label_new_with_format(_("Not implemented yet."),
+ purple_request_field_set_ui_data(field, gnt_label_new_with_format(_("Not implemented yet."),
GNT_TEXT_FLAG_BOLD));
}
gnt_box_set_alignment(GNT_BOX(hbox), GNT_ALIGN_MID);
- gnt_box_add_widget(GNT_BOX(hbox), GNT_WIDGET(FINCH_GET_DATA(field)));
+ gnt_box_add_widget(GNT_BOX(hbox), GNT_WIDGET(purple_request_field_get_ui_data(field)));
}
if (grlist->next)
gnt_box_add_widget(GNT_BOX(box), gnt_hline_new());
@@ -671,6 +709,29 @@ finch_request_fields(const char *title, const char *primary,
box = setup_button_box(window, userdata, request_fields_cb, allfields,
ok, ok_cb, cancel, cancel_cb, NULL);
+
+ extra_actions = purple_request_cpar_get_extra_actions(cpar);
+ for (it = extra_actions; it; it = it->next->next) {
+ const gchar *label = it->data;
+ PurpleRequestFieldsCb *cb = it->next->data;
+
+ GntWidget *button = gnt_button_new(label);
+ gnt_box_add_widget_in_front(GNT_BOX(box), button);
+ g_object_set_data(G_OBJECT(button), "ui-handle", window);
+ g_object_set_data(G_OBJECT(button), "extra-cb", cb);
+ g_object_set_data(G_OBJECT(button), "extra-cb-data", userdata);
+ g_signal_connect(G_OBJECT(button), "activate",
+ G_CALLBACK(multifield_extra_cb), allfields);
+ }
+
+ help_cb = purple_request_cpar_get_help_cb(cpar, &help_data);
+ if (help_cb) {
+ GntWidget *button = gnt_button_new(_("Help"));
+ gnt_box_add_widget_in_front(GNT_BOX(box), button);
+ g_signal_connect_swapped(G_OBJECT(button), "activate",
+ G_CALLBACK(help_cb), help_data);
+ }
+
gnt_box_add_widget(GNT_BOX(window), box);
setup_default_callback(window, cancel_cb, userdata);
@@ -753,11 +814,9 @@ finch_file_request_window(const char *title, const char *path,
}
static void *
-finch_request_file(const char *title, const char *filename,
- gboolean savedialog,
- GCallback ok_cb, GCallback cancel_cb,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
- void *user_data)
+finch_request_file(const char *title, const char *filename, gboolean savedialog,
+ GCallback ok_cb, GCallback cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data)
{
FinchFileRequest *data;
const char *path;
@@ -775,8 +834,8 @@ finch_request_file(const char *title, const char *filename,
static void *
finch_request_folder(const char *title, const char *dirname, GCallback ok_cb,
- GCallback cancel_cb, PurpleAccount *account, const char *who, PurpleConversation *conv,
- void *user_data)
+ GCallback cancel_cb, PurpleRequestCommonParameters *cpar,
+ void *user_data)
{
FinchFileRequest *data;
@@ -791,13 +850,16 @@ finch_request_folder(const char *title, const char *dirname, GCallback ok_cb,
static PurpleRequestUiOps uiops =
{
+ 0,
finch_request_input,
finch_request_choice,
finch_request_action,
+ NULL,
+ NULL,
finch_request_fields,
finch_request_file,
- finch_close_request,
finch_request_folder,
+ finch_close_request,
NULL,
NULL,
NULL,
@@ -826,7 +888,7 @@ void finch_request_save_in_prefs(gpointer null, PurpleRequestFields *allfields)
for (; fields ; fields = fields->next) {
PurpleRequestField *field = fields->data;
- PurpleRequestFieldType type = purple_request_field_get_type(field);
+ PurpleRequestFieldType type = purple_request_field_get_field_type(field);
PurplePrefType pt;
gpointer val = NULL;
const char *id = purple_request_field_get_id(field);
@@ -849,7 +911,7 @@ void finch_request_save_in_prefs(gpointer null, PurpleRequestFields *allfields)
break;
}
- pt = purple_prefs_get_type(id);
+ pt = purple_prefs_get_pref_type(id);
switch (pt) {
case PURPLE_PREF_INT:
{
@@ -878,7 +940,7 @@ void finch_request_save_in_prefs(gpointer null, PurpleRequestFields *allfields)
GntWidget *finch_request_field_get_widget(PurpleRequestField *field)
{
GntWidget *ret = NULL;
- switch (purple_request_field_get_type(field)) {
+ switch (purple_request_field_get_field_type(field)) {
case PURPLE_REQUEST_FIELD_BOOLEAN:
ret = create_boolean_field(field);
break;
@@ -898,7 +960,8 @@ GntWidget *finch_request_field_get_widget(PurpleRequestField *field)
ret = create_account_field(field);
break;
default:
- purple_debug_error("GntRequest", "Unimplemented request-field %d\n", purple_request_field_get_type(field));
+ purple_debug_error("GntRequest", "Unimplemented request-field %d\n",
+ purple_request_field_get_field_type(field));
break;
}
return ret;
diff --git a/finch/gntrequest.h b/finch/gntrequest.h
index 2e1c33fde5..2c0bbabdbc 100644
--- a/finch/gntrequest.h
+++ b/finch/gntrequest.h
@@ -1,8 +1,3 @@
-/**
- * @file gntrequest.h GNT Request API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -23,49 +18,62 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _GNT_REQUEST_H
#define _GNT_REQUEST_H
+/**
+ * SECTION:gntrequest
+ * @section_id: finch-gntrequest
+ * @short_description: <filename>gntrequest.h</filename>
+ * @title: Request API
+ */
#include "request.h"
#include "gnt.h"
/**********************************************************************
- * @name GNT Request API
+ * GNT Request API
**********************************************************************/
-/*@{*/
/**
+ * finch_request_get_ui_ops:
+ *
* Get the ui-functions.
*
- * @return The PurpleRequestUiOps structure populated with the appropriate functions.
+ * Returns: The PurpleRequestUiOps structure populated with the appropriate functions.
*/
PurpleRequestUiOps *finch_request_get_ui_ops(void);
/**
+ * finch_request_init:
+ *
* Perform necessary initializations.
*/
void finch_request_init(void);
/**
+ * finch_request_uninit:
+ *
* Perform necessary uninitializations.
*/
void finch_request_uninit(void);
/**
+ * finch_request_save_in_prefs:
+ *
* 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);
/**
- * Create a widget field for a request-field.
+ * finch_request_field_get_widget:
+ * @field: The request field.
*
- * @param field The request field.
+ * Create a widget field for a request-field.
*
- * @return A GntWidget for the request field.
- * @since 2.4.0
+ * Returns: (transfer full): A GntWidget for the request field.
*/
GntWidget *finch_request_field_get_widget(PurpleRequestField *field);
-/*@}*/
#endif
diff --git a/finch/gntroomlist.c b/finch/gntroomlist.c
index 92299d3a5c..d2ab14afa2 100644
--- a/finch/gntroomlist.c
+++ b/finch/gntroomlist.c
@@ -1,8 +1,3 @@
-/**
- * @file gntroomlist.c GNT Room List API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -41,6 +36,7 @@
#define PREF_ROOT "/finch/roomlist"
+
/* Yes, just one roomlist at a time. Let's not get greedy. Aight? */
struct _FinchRoomlist
{
@@ -67,7 +63,7 @@ unset_roomlist(gpointer null)
{
froomlist.window = NULL;
if (froomlist.roomlist) {
- purple_roomlist_unref(froomlist.roomlist);
+ g_object_unref(froomlist.roomlist);
froomlist.roomlist = NULL;
}
froomlist.account = NULL;
@@ -81,10 +77,10 @@ update_roomlist(PurpleRoomlist *list)
return;
if (froomlist.roomlist)
- purple_roomlist_unref(froomlist.roomlist);
+ g_object_unref(froomlist.roomlist);
if ((froomlist.roomlist = list) != NULL)
- purple_roomlist_ref(list);
+ g_object_ref(list);
}
static void fl_stop(GntWidget *button, gpointer null)
@@ -141,14 +137,14 @@ roomlist_activated(GntWidget *widget)
if (!room)
return;
- switch (purple_roomlist_room_get_type(room)) {
+ switch (purple_roomlist_room_get_room_type(room)) {
case PURPLE_ROOMLIST_ROOMTYPE_ROOM:
purple_roomlist_room_join(froomlist.roomlist, room);
break;
case PURPLE_ROOMLIST_ROOMTYPE_CATEGORY:
- if (!room->expanded_once) {
+ if (!purple_roomlist_room_get_expanded_once(room)) {
purple_roomlist_expand_category(froomlist.roomlist, room);
- room->expanded_once = TRUE;
+ purple_roomlist_room_set_expanded_once(room, TRUE);
}
break;
}
@@ -186,7 +182,7 @@ roomlist_selection_changed(GntWidget *widget, gpointer old, gpointer current, gp
purple_roomlist_field_get_label(f), GNT_TEXT_FLAG_BOLD);
gnt_text_view_append_text_with_flags(tv, ": ", GNT_TEXT_FLAG_BOLD);
- switch (purple_roomlist_field_get_type(f)) {
+ switch (purple_roomlist_field_get_field_type(f)) {
case PURPLE_ROOMLIST_FIELD_BOOL:
label = g_strdup(iter->data ? "True" : "False");
break;
@@ -202,7 +198,7 @@ roomlist_selection_changed(GntWidget *widget, gpointer old, gpointer current, gp
first = FALSE;
}
- if (purple_roomlist_room_get_type(room) == PURPLE_ROOMLIST_ROOMTYPE_CATEGORY) {
+ if (purple_roomlist_room_get_room_type(room) == PURPLE_ROOMLIST_ROOMTYPE_CATEGORY) {
if (!first)
gnt_text_view_append_text_with_flags(tv, "\n", GNT_TEXT_FLAG_NORMAL);
gnt_text_view_append_text_with_flags(tv,
@@ -343,7 +339,7 @@ fl_show_with_account(PurpleAccount *account)
static void
fl_create(PurpleRoomlist *list)
{
- FINCH_SET_DATA(list, &froomlist);
+ purple_roomlist_set_ui_data(list, &froomlist);
setup_roomlist(NULL);
update_roomlist(list);
}
@@ -360,7 +356,7 @@ fl_add_room(PurpleRoomlist *roomlist, PurpleRoomlistRoom *room)
if (froomlist.roomlist != roomlist)
return;
- category = (purple_roomlist_room_get_type(room) == PURPLE_ROOMLIST_ROOMTYPE_CATEGORY);
+ category = (purple_roomlist_room_get_room_type(room) == PURPLE_ROOMLIST_ROOMTYPE_CATEGORY);
gnt_tree_remove(GNT_TREE(froomlist.tree), room);
gnt_tree_add_row_after(GNT_TREE(froomlist.tree), room,
gnt_tree_create_row(GNT_TREE(froomlist.tree),
diff --git a/finch/gntroomlist.h b/finch/gntroomlist.h
index ac96bc8e44..a2e2ae7a79 100644
--- a/finch/gntroomlist.h
+++ b/finch/gntroomlist.h
@@ -1,8 +1,3 @@
-/**
- * @file gntroomlist.h GNT Room List API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -23,39 +18,51 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _GNT_ROOMLIST_H
#define _GNT_ROOMLIST_H
+/**
+ * SECTION:gntroomlist
+ * @section_id: finch-gntroomlist
+ * @short_description: <filename>gntroomlist.h</filename>
+ * @title: Room List API
+ */
#include "roomlist.h"
/**********************************************************************
- * @name GNT Room List API
+ * GNT Room List API
**********************************************************************/
-/*@{*/
/**
+ * finch_roomlist_init:
+ *
* Initialize the roomlist subsystem.
*/
void finch_roomlist_init(void);
/**
+ * finch_roomlist_get_ui_ops:
+ *
* Get the ui-functions.
*
- * @return The PurpleRoomlistUiOps structure populated with the appropriate functions.
+ * Returns: The PurpleRoomlistUiOps structure populated with the appropriate functions.
*/
PurpleRoomlistUiOps *finch_roomlist_get_ui_ops(void);
/**
+ * finch_roomlist_show_all:
+ *
* Show the roomlist dialog.
*/
void finch_roomlist_show_all(void);
/**
+ * finch_roomlist_uninit:
+ *
* Uninitialize the roomlist subsystem.
*/
void finch_roomlist_uninit(void);
-/*@}*/
-
#endif
diff --git a/finch/gntsound.c b/finch/gntsound.c
index 296fee1ef3..999c86c5fa 100644
--- a/finch/gntsound.c
+++ b/finch/gntsound.c
@@ -1,8 +1,3 @@
-/**
- * @file gntsound.c GNT Sound API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -137,20 +132,18 @@ unmute_login_sounds_cb(gpointer data)
}
static gboolean
-chat_nick_matches_name(PurpleConversation *conv, const char *aname)
+chat_nick_matches_name(PurpleChatConversation *chat, const char *aname)
{
- PurpleConvChat *chat = NULL;
char *nick = NULL;
char *name = NULL;
gboolean ret = FALSE;
PurpleAccount *account;
- chat = purple_conversation_get_chat_data(conv);
if (chat == NULL)
return ret;
- account = purple_conversation_get_account(conv);
- nick = g_strdup(purple_normalize(account, chat->nick));
+ account = purple_conversation_get_account(PURPLE_CONVERSATION(chat));
+ nick = g_strdup(purple_normalize(account, purple_chat_conversation_get_nick(chat)));
name = g_strdup(purple_normalize(account, aname));
if (g_utf8_collate(nick, name) == 0)
@@ -211,67 +204,64 @@ im_msg_received_cb(PurpleAccount *account, char *sender,
}
static void
-im_msg_sent_cb(PurpleAccount *account, const char *receiver,
- const char *message, PurpleSoundEventID event)
+im_msg_sent_cb(PurpleAccount *account, PurpleMessage *msg,
+ PurpleSoundEventID event)
{
- PurpleConversation *conv = purple_find_conversation_with_account(
- PURPLE_CONV_TYPE_IM, receiver, account);
- play_conv_event(conv, event);
+ PurpleIMConversation *im = purple_conversations_find_im_with_account(
+ purple_message_get_recipient(msg), account);
+ play_conv_event(PURPLE_CONVERSATION(im), event);
}
static void
-chat_buddy_join_cb(PurpleConversation *conv, const char *name,
- PurpleConvChatBuddyFlags flags, gboolean new_arrival,
+chat_user_join_cb(PurpleChatConversation *chat, const char *name,
+ PurpleChatUserFlags flags, gboolean new_arrival,
PurpleSoundEventID event)
{
- if (new_arrival && !chat_nick_matches_name(conv, name))
- play_conv_event(conv, event);
+ if (new_arrival && !chat_nick_matches_name(chat, name))
+ play_conv_event(PURPLE_CONVERSATION(chat), event);
}
static void
-chat_buddy_left_cb(PurpleConversation *conv, const char *name,
+chat_user_left_cb(PurpleChatConversation *chat, const char *name,
const char *reason, PurpleSoundEventID event)
{
- if (!chat_nick_matches_name(conv, name))
- play_conv_event(conv, event);
+ if (!chat_nick_matches_name(chat, name))
+ play_conv_event(PURPLE_CONVERSATION(chat), event);
}
static void
-chat_msg_sent_cb(PurpleAccount *account, const char *message,
- int id, PurpleSoundEventID event)
+chat_msg_sent_cb(PurpleAccount *account, PurpleMessage *msg, int id,
+ PurpleSoundEventID event)
{
PurpleConnection *conn = purple_account_get_connection(account);
- PurpleConversation *conv = NULL;
+ PurpleChatConversation *chat = NULL;
if (conn!=NULL)
- conv = purple_find_chat(conn, id);
+ chat = purple_conversations_find_chat(conn, id);
- play_conv_event(conv, event);
+ play_conv_event(PURPLE_CONVERSATION(chat), event);
}
static void
chat_msg_received_cb(PurpleAccount *account, char *sender,
- char *message, PurpleConversation *conv,
+ char *message, PurpleChatConversation *chat,
PurpleMessageFlags flags, PurpleSoundEventID event)
{
- PurpleConvChat *chat;
-
if (flags & PURPLE_MESSAGE_DELAYED)
return;
- chat = purple_conversation_get_chat_data(conv);
g_return_if_fail(chat != NULL);
- if (purple_conv_chat_is_user_ignored(chat, sender))
+ if (purple_chat_conversation_is_ignored_user(chat, sender))
return;
- if (chat_nick_matches_name(conv, sender))
+ if (chat_nick_matches_name(chat, sender))
return;
- if (flags & PURPLE_MESSAGE_NICK || purple_utf8_has_word(message, chat->nick))
- play_conv_event(conv, PURPLE_SOUND_CHAT_NICK);
+ if (flags & PURPLE_MESSAGE_NICK || purple_utf8_has_word(message, purple_chat_conversation_get_nick(chat)))
+ play_conv_event(PURPLE_CONVERSATION(chat), PURPLE_SOUND_CHAT_NICK);
else
- play_conv_event(conv, event);
+ play_conv_event(PURPLE_CONVERSATION(chat), event);
}
static void
@@ -383,7 +373,7 @@ finch_sound_init(void)
if ((gst_init_failed = !gst_init_check(NULL, NULL, &error))) {
purple_notify_error(NULL, _("GStreamer Failure"),
_("GStreamer failed to initialize."),
- error ? error->message : "");
+ error ? error->message : "", NULL);
if (error) {
g_error_free(error);
error = NULL;
@@ -403,11 +393,11 @@ finch_sound_init(void)
purple_signal_connect(conv_handle, "sent-im-msg",
gnt_sound_handle, PURPLE_CALLBACK(im_msg_sent_cb),
GINT_TO_POINTER(PURPLE_SOUND_SEND));
- purple_signal_connect(conv_handle, "chat-buddy-joined",
- gnt_sound_handle, PURPLE_CALLBACK(chat_buddy_join_cb),
+ purple_signal_connect(conv_handle, "chat-user-joined",
+ gnt_sound_handle, PURPLE_CALLBACK(chat_user_join_cb),
GINT_TO_POINTER(PURPLE_SOUND_CHAT_JOIN));
- purple_signal_connect(conv_handle, "chat-buddy-left",
- gnt_sound_handle, PURPLE_CALLBACK(chat_buddy_left_cb),
+ purple_signal_connect(conv_handle, "chat-user-left",
+ gnt_sound_handle, PURPLE_CALLBACK(chat_user_left_cb),
GINT_TO_POINTER(PURPLE_SOUND_CHAT_LEAVE));
purple_signal_connect(conv_handle, "sent-chat-msg",
gnt_sound_handle, PURPLE_CALLBACK(chat_msg_sent_cb),
@@ -433,7 +423,7 @@ finch_sound_uninit(void)
purple_signals_disconnect_by_handle(finch_sound_get_handle());
}
-#ifdef USE_GSTREAMER
+#if defined(USE_GSTREAMER) && !defined(_WIN32)
static gboolean
bus_call (GstBus *bus, GstMessage *msg, gpointer data)
{
@@ -466,7 +456,7 @@ static void
finch_sound_play_file(const char *filename)
{
const char *method;
-#ifdef USE_GSTREAMER
+#if defined(USE_GSTREAMER) && !defined(_WIN32)
float volume;
char *uri;
GstElement *sink = NULL;
@@ -553,7 +543,11 @@ finch_sound_play_file(const char *filename)
return;
}
+#if GST_CHECK_VERSION(1,0,0)
play = gst_element_factory_make("playbin", "play");
+#else
+ play = gst_element_factory_make("playbin2", "play");
+#endif
if (play == NULL) {
return;
@@ -614,8 +608,8 @@ finch_sound_play_event(PurpleSoundEventID event)
char *filename = g_strdup(purple_prefs_get_path(file_pref));
if (!filename || !strlen(filename)) {
g_free(filename);
- /* XXX Consider creating a constant for "sounds/purple" to be shared with Pidgin */
- filename = g_build_filename(DATADIR, "sounds", "purple", sounds[event].def, NULL);
+ filename = g_build_filename(PURPLE_DATADIR,
+ "sounds", "purple", sounds[event].def, NULL);
}
purple_sound_play_file(filename, NULL);
diff --git a/finch/gntsound.h b/finch/gntsound.h
index c1edfaac6f..836b191992 100644
--- a/finch/gntsound.h
+++ b/finch/gntsound.h
@@ -1,8 +1,3 @@
-/**
- * @file gntsound.h GNT Sound API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -23,70 +18,74 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _GNT_SOUND_H
#define _GNT_SOUND_H
+/**
+ * SECTION:gntsound
+ * @section_id: finch-gntsound
+ * @short_description: <filename>gntsound.h</filename>
+ * @title: Sound API
+ */
#include "sound.h"
/**********************************************************************/
-/** @name GNT Sound API */
+/* GNT Sound API */
/**********************************************************************/
-/*@{*/
/**
- * Get the name of the active sound profile.
+ * finch_sound_get_active_profile:
*
- * @return The name of the profile
+ * Get the name of the active sound profile.
*
- * @since 2.1.0
+ * Returns: The name of the profile
*/
const char *finch_sound_get_active_profile(void);
/**
- * Set the active profile. If the profile doesn't exist, nothing is changed.
- *
- * @param name The name of the profile
+ * finch_sound_set_active_profile:
+ * @name: The name of the profile
*
- * @since 2.1.0
+ * Set the active profile. If the profile doesn't exist, nothing is changed.
*/
void finch_sound_set_active_profile(const char *name);
/**
- * Get a list of available sound profiles.
+ * finch_sound_get_profiles:
*
- * @return A list of strings denoting sound profile names.
- * Caller must free the list (but not the data).
+ * Get a list of available sound profiles.
*
- * @since 2.1.0
+ * Returns: (transfer container) (element-type utf8): A list of strings
+ * denoting sound profile names. Caller must free the list
+ * (but not the data).
*/
GList *finch_sound_get_profiles(void);
/**
+ * finch_sound_is_enabled:
+ *
* Determine whether any sound will be played or not.
*
- * @return Returns FALSE if preference is set to 'No sound', or if volume is
+ * Returns: Returns FALSE if preference is set to 'No sound', or if volume is
* set to zero.
- *
- * @since 2.2.0
*/
gboolean finch_sound_is_enabled(void);
/**
- * Gets GNT sound UI ops.
+ * finch_sound_get_ui_ops:
*
- * @return The UI operations structure.
+ * Gets GNT sound UI ops.
*
- * @since 2.1.0
+ * Returns: The UI operations structure.
*/
PurpleSoundUiOps *finch_sound_get_ui_ops(void);
/**
- * Show the sound settings dialog.
+ * finch_sounds_show_all:
*
- * @since 2.1.0
+ * Show the sound settings dialog.
*/
void finch_sounds_show_all(void);
-/*@}*/
-
#endif
diff --git a/finch/gntstatus.c b/finch/gntstatus.c
index c49d05df7b..153a850c00 100644
--- a/finch/gntstatus.c
+++ b/finch/gntstatus.c
@@ -1,8 +1,3 @@
-/**
- * @file gntstatus.c GNT Status API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -99,7 +94,7 @@ populate_statuses(GntTree *tree)
continue;
title = purple_savedstatus_get_title(saved);
- type = purple_primitive_get_name_from_type(purple_savedstatus_get_type(saved));
+ type = purple_primitive_get_name_from_type(purple_savedstatus_get_primitive_type(saved));
message = purple_savedstatus_get_message(saved); /* XXX: Strip possible markups */
gnt_tree_add_row_last(tree, saved,
@@ -141,8 +136,7 @@ ask_before_delete(GntWidget *button, gpointer null)
purple_savedstatus_get_title(saved));
purple_request_action(saved, _("Delete Status"), ask, NULL, 0,
- NULL, NULL, NULL,
- saved, 2,
+ NULL, saved, 2,
_("Delete"), really_delete_status,
_("Cancel"), NULL);
g_free(ask);
@@ -301,7 +295,7 @@ save_savedstatus_cb(GntWidget *button, EditStatus *edit)
if (!title || !*title)
{
purple_notify_error(edit, _("Error"), _("Invalid title"),
- _("Please enter a non-empty title for the status."));
+ _("Please enter a non-empty title for the status."), NULL);
gnt_box_give_focus_to_child(GNT_BOX(edit->window), edit->title);
return;
}
@@ -310,7 +304,7 @@ save_savedstatus_cb(GntWidget *button, EditStatus *edit)
if (find && find != edit->saved)
{
purple_notify_error(edit, _("Error"), _("Duplicate title"),
- _("Please enter a different title for the status."));
+ _("Please enter a different title for the status."), NULL);
gnt_box_give_focus_to_child(GNT_BOX(edit->window), edit->title);
return;
}
@@ -328,7 +322,7 @@ save_savedstatus_cb(GntWidget *button, EditStatus *edit)
else
{
purple_savedstatus_set_title(edit->saved, title);
- purple_savedstatus_set_type(edit->saved, prim);
+ purple_savedstatus_set_primitive_type(edit->saved, prim);
purple_savedstatus_set_message(edit->saved, message);
if (statuses.tree)
{
@@ -364,7 +358,7 @@ add_substatus(EditStatus *edit, PurpleAccount *account)
if (sub)
{
- key->type = purple_savedstatus_substatus_get_type(sub);
+ key->type = purple_savedstatus_substatus_get_status_type(sub);
type = purple_status_type_get_name(key->type);
message = purple_savedstatus_substatus_get_message(sub);
key->message = g_strdup(message);
@@ -551,7 +545,7 @@ void finch_savedstatus_edit(PurpleSavedStatus *saved)
edit->type = combo = gnt_combo_box_new();
gnt_box_add_widget(GNT_BOX(box), combo);
- current = saved ? purple_savedstatus_get_type(saved) : PURPLE_STATUS_UNSET;
+ current = saved ? purple_savedstatus_get_primitive_type(saved) : PURPLE_STATUS_UNSET;
for (i = 0; prims[i] != PURPLE_STATUS_UNSET; i++)
{
gnt_combo_box_add_data(GNT_COMBO_BOX(combo), GINT_TO_POINTER(prims[i]),
diff --git a/finch/gntstatus.h b/finch/gntstatus.h
index 71d9da8d9f..8fc3cfbb4d 100644
--- a/finch/gntstatus.h
+++ b/finch/gntstatus.h
@@ -1,8 +1,3 @@
-/**
- * @file gntstatus.h GNT Status API
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -23,29 +18,36 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _GNT_STATUS_H
#define _GNT_STATUS_H
+/**
+ * SECTION:gntstatus
+ * @section_id: finch-gntstatus
+ * @short_description: <filename>gntstatus.h</filename>
+ * @title: Status API
+ */
#include <status.h>
#include <savedstatuses.h>
/**********************************************************************
- * @name GNT BuddyList API
+ * GNT BuddyList API
**********************************************************************/
-/*@{*/
/**
+ * finch_savedstatus_show_all:
+ *
* Show a dialog with all the saved statuses.
*/
void finch_savedstatus_show_all(void);
/**
- * Show a dialog to edit a status.
+ * finch_savedstatus_edit:
+ * @saved: The saved status to edit. Set it to %NULL to create a new status.
*
- * @param saved The saved status to edit. Set it to @c NULL to create a new status.
+ * Show a dialog to edit a status.
*/
void finch_savedstatus_edit(PurpleSavedStatus *saved);
-/*@}*/
-
#endif
diff --git a/finch/gntui.c b/finch/gntui.c
index bac9ec0912..deadfde50f 100644
--- a/finch/gntui.c
+++ b/finch/gntui.c
@@ -30,7 +30,7 @@
#include "gntconn.h"
#include "gntconv.h"
#include "gntdebug.h"
-#include "gntft.h"
+#include "gntxfer.h"
#include "gntlog.h"
#include "gntmedia.h"
#include "gntnotify.h"
@@ -44,11 +44,15 @@
#include <prefs.h>
-void gnt_ui_init()
+void finch_ui_init()
{
#ifdef STANDALONE
+#ifdef _WIN32 /* TODO: don't change it when using FHS under win32 */
+ gnt_set_config_dir(purple_user_dir());
+#endif /* _WIN32 */
+
gnt_init();
-#endif
+#endif /* STANDALONE */
purple_prefs_add_none("/purple/gnt");
@@ -106,12 +110,13 @@ void gnt_ui_init()
gnt_register_action(_("Room List"), finch_roomlist_show_all);
gnt_register_action(_("Sounds"), finch_sounds_show_all);
gnt_register_action(_("Preferences"), finch_prefs_show_all);
+ gnt_register_action(_("Keyring settings"), finch_prefs_show_keyring);
gnt_register_action(_("Statuses"), finch_savedstatus_show_all);
#ifdef STANDALONE
}
-void gnt_ui_uninit()
+void finch_ui_uninit()
{
purple_accounts_set_ui_ops(NULL);
finch_accounts_uninit();
@@ -146,6 +151,9 @@ void gnt_ui_uninit()
#endif
gnt_quit();
-#endif
-}
+#ifdef _WIN32
+ gnt_set_config_dir(NULL);
+#endif /* _WIN32 */
+#endif /* STANDALONE */
+}
diff --git a/finch/gntui.h b/finch/gntui.h
index a4eb567dd4..ea849ff1d1 100644
--- a/finch/gntui.h
+++ b/finch/gntui.h
@@ -1,4 +1,4 @@
-/**
+/*
* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -19,12 +19,19 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _GNT_UI_H
#define _GNT_UI_H
+/**
+ * SECTION:gntui
+ * @section_id: finch-gntui
+ * @short_description: <filename>gntui.h</filename>
+ * @title: Startup and Shutdown UI
+ */
#include "gnt.h"
-void gnt_ui_init(void);
-void gnt_ui_uninit(void);
+void finch_ui_init(void);
+void finch_ui_uninit(void);
#endif
diff --git a/finch/gntft.c b/finch/gntxfer.c
index 262140a7b6..df2d079d29 100644
--- a/finch/gntft.c
+++ b/finch/gntxfer.c
@@ -1,8 +1,3 @@
-/**
- * @file gntft.c GNT File Transfer UI
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -35,16 +30,13 @@
#include "debug.h"
#include "notify.h"
-#include "ft.h"
+#include "xfer.h"
#include "prpl.h"
#include "util.h"
-#include "gntft.h"
+#include "gntxfer.h"
#include "prefs.h"
-#define FINCHXFER(xfer) \
- (PurpleGntXferUiData *)FINCH_GET_DATA(xfer)
-
typedef struct
{
gboolean keep_open;
@@ -152,7 +144,7 @@ toggle_clear_finished_cb(GntWidget *w)
while (iter) {
PurpleXfer *xfer = iter->data;
iter = iter->next;
- if (purple_xfer_is_completed(xfer) || purple_xfer_is_canceled(xfer))
+ if (purple_xfer_is_completed(xfer) || purple_xfer_is_cancelled(xfer))
finch_xfer_dialog_remove_xfer(xfer);
}
}
@@ -163,7 +155,7 @@ remove_button_cb(GntButton *button)
{
PurpleXfer *selected_xfer = gnt_tree_get_selection_data(GNT_TREE(xfer_dialog->tree));
if (selected_xfer && (purple_xfer_is_completed(selected_xfer) ||
- purple_xfer_is_canceled(selected_xfer))) {
+ purple_xfer_is_cancelled(selected_xfer))) {
finch_xfer_dialog_remove_xfer(selected_xfer);
}
}
@@ -172,7 +164,7 @@ static void
stop_button_cb(GntButton *button)
{
PurpleXfer *selected_xfer = gnt_tree_get_selection_data(GNT_TREE(xfer_dialog->tree));
- PurpleXferStatusType status;
+ PurpleXferStatus status;
if (!selected_xfer)
return;
@@ -262,7 +254,7 @@ finch_xfer_dialog_new(void)
for (iter = purple_xfers_get_all(); iter; iter = iter->next) {
PurpleXfer *xfer = (PurpleXfer *)iter->data;
- PurpleGntXferUiData *data = FINCHXFER(xfer);
+ PurpleGntXferUiData *data = purple_xfer_get_ui_data(xfer);
if (data->in_list) {
finch_xfer_dialog_add_xfer(xfer);
finch_xfer_dialog_update_xfer(xfer);
@@ -300,16 +292,16 @@ finch_xfer_dialog_add_xfer(PurpleXfer *xfer)
g_return_if_fail(xfer_dialog != NULL);
g_return_if_fail(xfer != NULL);
- purple_xfer_ref(xfer);
+ g_object_ref(xfer);
- data = FINCHXFER(xfer);
+ data = purple_xfer_get_ui_data(xfer);
data->in_list = TRUE;
finch_xfer_dialog_show();
data->last_updated_time = 0;
- type = purple_xfer_get_type(xfer);
+ 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));
@@ -320,7 +312,7 @@ finch_xfer_dialog_add_xfer(PurpleXfer *xfer)
lfilename = utf8;
gnt_tree_add_row_last(GNT_TREE(xfer_dialog->tree), xfer,
gnt_tree_create_row(GNT_TREE(xfer_dialog->tree),
- "0.0", (type == PURPLE_XFER_RECEIVE) ? purple_xfer_get_filename(xfer) : lfilename,
+ "0.0", (type == PURPLE_XFER_TYPE_RECEIVE) ? purple_xfer_get_filename(xfer) : lfilename,
size_str, "0.0", "",_("Waiting for transfer to begin")), NULL);
g_free(lfilename);
@@ -340,7 +332,7 @@ finch_xfer_dialog_remove_xfer(PurpleXfer *xfer)
g_return_if_fail(xfer_dialog != NULL);
g_return_if_fail(xfer != NULL);
- data = FINCHXFER(xfer);
+ data = purple_xfer_get_ui_data(xfer);
if (data == NULL)
return;
@@ -358,7 +350,7 @@ finch_xfer_dialog_remove_xfer(PurpleXfer *xfer)
finch_xfer_dialog_destroy();
else
update_title_progress();
- purple_xfer_unref(xfer);
+ g_object_unref(xfer);
}
void
@@ -370,7 +362,7 @@ finch_xfer_dialog_cancel_xfer(PurpleXfer *xfer)
g_return_if_fail(xfer_dialog != NULL);
g_return_if_fail(xfer != NULL);
- data = FINCHXFER(xfer);
+ data = purple_xfer_get_ui_data(xfer);
if (data == NULL)
return;
@@ -385,7 +377,7 @@ finch_xfer_dialog_cancel_xfer(PurpleXfer *xfer)
update_title_progress();
- if (purple_xfer_is_canceled(xfer))
+ if (purple_xfer_is_cancelled(xfer))
status = _("Cancelled");
else
status = _("Failed");
@@ -416,7 +408,7 @@ finch_xfer_dialog_update_xfer(PurpleXfer *xfer)
g_return_if_fail(xfer_dialog != NULL);
g_return_if_fail(xfer != NULL);
- if ((data = FINCHXFER(xfer)) == NULL)
+ if ((data = purple_xfer_get_ui_data(xfer)) == NULL)
return;
if (data->in_list == FALSE || data->notified)
@@ -430,7 +422,7 @@ finch_xfer_dialog_update_xfer(PurpleXfer *xfer)
}
data->last_updated_time = current_time;
- send = (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND);
+ 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);
@@ -471,9 +463,9 @@ finch_xfer_new_xfer(PurpleXfer *xfer)
{
PurpleGntXferUiData *data;
- /* This is where we're setting xfer->ui_data for the first time. */
+ /* This is where we're setting xfer's "ui_data" for the first time. */
data = g_new0(PurpleGntXferUiData, 1);
- FINCH_SET_DATA(xfer, data);
+ purple_xfer_set_ui_data(xfer, data);
}
static void
@@ -481,11 +473,11 @@ finch_xfer_destroy(PurpleXfer *xfer)
{
PurpleGntXferUiData *data;
- data = FINCHXFER(xfer);
+ data = purple_xfer_get_ui_data(xfer);
if (data) {
g_free(data->name);
g_free(data);
- FINCH_SET_DATA(xfer, NULL);
+ purple_xfer_set_ui_data(xfer, NULL);
}
}
@@ -528,12 +520,10 @@ static PurpleXferUiOps ops =
finch_xfer_update_progress,
finch_xfer_cancel_local,
finch_xfer_cancel_remote,
-
- /* padding */
- NULL,
- NULL,
- NULL,
- NULL
+ NULL, /* ui_write */
+ NULL, /* ui_read */
+ NULL, /* data_not_sent */
+ NULL /* add_thumbnail */
};
/**************************************************************************
diff --git a/finch/gntft.h b/finch/gntxfer.h
index 75e69d0bba..02139719b7 100644
--- a/finch/gntft.h
+++ b/finch/gntxfer.h
@@ -1,8 +1,3 @@
-/**
- * @file gntft.h GNT File Transfer UI
- * @ingroup finch
- */
-
/* finch
*
* Finch is the legal property of its developers, whose names are too numerous
@@ -23,92 +18,111 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#ifndef _FINCHFT_H_
-#define _FINCHFT_H_
-#include "ft.h"
+#ifndef _GNT_XFER_H_
+#define _GNT_XFER_H_
+/**
+ * SECTION:gntxfer
+ * @section_id: finch-gntxfer
+ * @short_description: <filename>gntxfer.h</filename>
+ * @title: File Transfer UI
+ */
+
+#include "xfer.h"
/**************************************************************************/
-/** @name GNT File Transfer Dialog API */
+/* GNT File Transfer Dialog API */
/**************************************************************************/
-/*@{*/
/**
+ * finch_xfer_dialog_new:
+ *
* Creates a new file transfer dialog.
*
- * @return The new dialog.
+ * Returns: The new dialog.
*/
void finch_xfer_dialog_new(void);
/**
+ * finch_xfer_dialog_destroy:
+ *
* Destroys a file transfer dialog.
*/
void finch_xfer_dialog_destroy(void);
/**
+ * finch_xfer_dialog_show:
+ *
* Displays the file transfer dialog given.
- * If dialog is @c NULL, displays the default dialog, creating one if necessary
+ * If dialog is %NULL, displays the default dialog, creating one if necessary
*/
void finch_xfer_dialog_show(void);
/**
+ * finch_xfer_dialog_hide:
+ *
* Hides the file transfer dialog.
*/
void finch_xfer_dialog_hide(void);
/**
- * Adds a file transfer to the dialog.
+ * finch_xfer_dialog_add_xfer:
+ * @xfer: The file transfer.
*
- * @param xfer The file transfer.
+ * Adds a file transfer to the dialog.
*/
void finch_xfer_dialog_add_xfer(PurpleXfer *xfer);
/**
- * Removes a file transfer from the dialog.
+ * finch_xfer_dialog_remove_xfer:
+ * @xfer: The file transfer.
*
- * @param xfer The file transfer.
+ * Removes a file transfer from the dialog.
*/
void finch_xfer_dialog_remove_xfer(PurpleXfer *xfer);
/**
- * Indicate in a file transfer dialog that a transfer was cancelled.
+ * finch_xfer_dialog_cancel_xfer:
+ * @xfer: The file transfer that was cancelled.
*
- * @param xfer The file transfer that was cancelled.
+ * Indicate in a file transfer dialog that a transfer was cancelled.
*/
void finch_xfer_dialog_cancel_xfer(PurpleXfer *xfer);
/**
- * Updates the information for a transfer in the dialog.
+ * finch_xfer_dialog_update_xfer:
+ * @xfer: The file transfer.
*
- * @param xfer The file transfer.
+ * Updates the information for a transfer in the dialog.
*/
void finch_xfer_dialog_update_xfer(PurpleXfer *xfer);
-/*@}*/
-
/**************************************************************************/
-/** @name GNT File Transfer API */
+/* GNT File Transfer API */
/**************************************************************************/
-/*@{*/
/**
+ * finch_xfers_init:
+ *
* Initializes the GNT file transfer system.
*/
void finch_xfers_init(void);
/**
+ * finch_xfers_uninit:
+ *
* Uninitializes the GNT file transfer system.
*/
void finch_xfers_uninit(void);
/**
+ * finch_xfers_get_ui_ops:
+ *
* Returns the UI operations structure for the GNT file transfer UI.
*
- * @return The GNT file transfer UI operations structure.
+ * Returns: The GNT file transfer UI operations structure.
*/
PurpleXferUiOps *finch_xfers_get_ui_ops(void);
-/*@}*/
-
-#endif /* _FINCHFT_H_ */
+#endif /* _GNT_XFER_H_ */
diff --git a/finch/libfinch.c b/finch/libfinch.c
new file mode 100644
index 0000000000..4fce785b55
--- /dev/null
+++ b/finch/libfinch.c
@@ -0,0 +1,418 @@
+/**
+ * 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
+ * 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 "finch.h"
+
+#include "account.h"
+#include "conversation.h"
+#include "core.h"
+#include "debug.h"
+#include "eventloop.h"
+#include "glibcompat.h"
+#include "log.h"
+#include "notify.h"
+#include "prefs.h"
+#include "prpl.h"
+#include "pounce.h"
+#include "savedstatuses.h"
+#include "sound.h"
+#include "status.h"
+#include "util.h"
+#include "whiteboard.h"
+#include "xfer.h"
+
+#include "gntdebug.h"
+#include "gntprefs.h"
+#include "gntui.h"
+#include "gntidle.h"
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include "config.h"
+#include "package_revision.h"
+
+static void
+debug_init(void)
+{
+ finch_debug_init();
+ purple_debug_set_ui_ops(finch_debug_get_ui_ops());
+}
+
+static GHashTable *ui_info = NULL;
+static GHashTable *finch_ui_get_info(void)
+{
+ if (ui_info == NULL) {
+ ui_info = g_hash_table_new(g_str_hash, g_str_equal);
+
+ g_hash_table_insert(ui_info, "name", (char*)_("Finch"));
+ g_hash_table_insert(ui_info, "version", VERSION);
+ g_hash_table_insert(ui_info, "website", "https://pidgin.im");
+ g_hash_table_insert(ui_info, "dev_website", "https://developer.pidgin.im");
+ g_hash_table_insert(ui_info, "client_type", "console");
+
+ /*
+ * This is the client key for "Finch." It is owned by the AIM
+ * account "markdoliner." Please don't use this key for other
+ * applications. You can either not specify a client key, in
+ * which case the default "libpurple" key will be used, or you
+ * can try to register your own at the AIM or ICQ web sites
+ * (although this functionality was removed at some point, it's
+ * possible it has been re-added). AOL's old key management
+ * page is http://developer.aim.com/manageKeys.jsp
+ */
+ g_hash_table_insert(ui_info, "prpl-aim-clientkey", "ma19sqWV9ymU6UYc");
+
+ /*
+ * This is the client key for "Pidgin." It is owned by the AIM
+ * account "markdoliner." Please don't use this key for other
+ * applications. You can either not specify a client key, in
+ * which case the default "libpurple" key will be used, or you
+ * can try to register your own at the AIM or ICQ web sites
+ * (although this functionality was removed at some point, it's
+ * possible it has been re-added). AOL's old key management
+ * page is http://developer.aim.com/manageKeys.jsp
+ *
+ * We used to have a Finch-specific devId/clientkey
+ * (ma19sqWV9ymU6UYc), but it stopped working, so we switched
+ * to this one.
+ */
+ g_hash_table_insert(ui_info, "prpl-icq-clientkey", "ma1cSASNCKFtrdv9");
+
+ /*
+ * This is the distid for Finch, given to us by AOL. Please
+ * don't use this for other applications. You can just not
+ * specify a distid and libpurple will use a default.
+ */
+ g_hash_table_insert(ui_info, "prpl-aim-distid", GINT_TO_POINTER(1552));
+ g_hash_table_insert(ui_info, "prpl-icq-distid", GINT_TO_POINTER(1552));
+ }
+
+ return ui_info;
+}
+
+static void
+finch_quit(void)
+{
+ finch_ui_uninit();
+ if (ui_info)
+ g_hash_table_destroy(ui_info);
+}
+
+static PurpleCoreUiOps core_ops =
+{
+ finch_prefs_init,
+ debug_init,
+ finch_ui_init,
+ finch_quit,
+ finch_ui_get_info,
+
+ /* padding */
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static PurpleCoreUiOps *
+gnt_core_get_ui_ops(void)
+{
+ return &core_ops;
+}
+
+/* Anything IO-related is directly copied from gtkpurple's source tree */
+
+#define FINCH_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR)
+#define FINCH_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL)
+
+typedef struct _PurpleGntIOClosure {
+ PurpleInputFunction function;
+ guint result;
+ gpointer data;
+
+} PurpleGntIOClosure;
+
+static void purple_gnt_io_destroy(gpointer data)
+{
+ g_free(data);
+}
+
+static gboolean purple_gnt_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data)
+{
+ PurpleGntIOClosure *closure = data;
+ PurpleInputCondition purple_cond = 0;
+
+ if (condition & FINCH_READ_COND)
+ purple_cond |= PURPLE_INPUT_READ;
+ if (condition & FINCH_WRITE_COND)
+ purple_cond |= PURPLE_INPUT_WRITE;
+
+#if 0
+ purple_debug(PURPLE_DEBUG_MISC, "gtk_eventloop",
+ "CLOSURE: callback for %d, fd is %d\n",
+ closure->result, g_io_channel_unix_get_fd(source));
+#endif
+
+#ifdef _WIN32
+ if(! purple_cond) {
+#if 0
+ purple_debug_misc("gnt_eventloop",
+ "CLOSURE received GIOCondition of 0x%x, which does not"
+ " match 0x%x (READ) or 0x%x (WRITE)\n",
+ condition, FINCH_READ_COND, FINCH_WRITE_COND);
+#endif /* DEBUG */
+
+ return TRUE;
+ }
+#endif /* _WIN32 */
+
+ closure->function(closure->data, g_io_channel_unix_get_fd(source),
+ purple_cond);
+
+ return TRUE;
+}
+
+static guint gnt_input_add(gint fd, PurpleInputCondition condition, PurpleInputFunction function,
+ gpointer data)
+{
+ PurpleGntIOClosure *closure = g_new0(PurpleGntIOClosure, 1);
+ GIOChannel *channel;
+ GIOCondition cond = 0;
+
+ closure->function = function;
+ closure->data = data;
+
+ if (condition & PURPLE_INPUT_READ)
+ cond |= FINCH_READ_COND;
+ if (condition & PURPLE_INPUT_WRITE)
+ cond |= FINCH_WRITE_COND;
+
+ channel = g_io_channel_unix_new(fd);
+ closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond,
+ purple_gnt_io_invoke, closure, purple_gnt_io_destroy);
+
+ g_io_channel_unref(channel);
+ return closure->result;
+}
+
+static PurpleEventLoopUiOps eventloop_ops =
+{
+ g_timeout_add,
+ g_source_remove,
+ gnt_input_add,
+ g_source_remove,
+ NULL, /* input_get_error */
+ g_timeout_add_seconds,
+
+ /* padding */
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static PurpleEventLoopUiOps *
+gnt_eventloop_get_ui_ops(void)
+{
+ return &eventloop_ops;
+}
+
+/* This is mostly copied from gtkpurple's source tree */
+static void
+show_usage(const char *name, gboolean terse)
+{
+ char *text;
+
+ if (terse) {
+ text = g_strdup_printf(_("%s. Try `%s -h' for more information.\n"), DISPLAY_VERSION, name);
+ } else {
+ text = g_strdup_printf(_("%s\n"
+ "Usage: %s [OPTION]...\n\n"
+ " -c, --config=DIR use DIR for config files\n"
+ " -d, --debug print debugging messages to stderr\n"
+ " -h, --help display this help and exit\n"
+ " -n, --nologin don't automatically login\n"
+ " -v, --version display the current version and exit\n"), DISPLAY_VERSION, name);
+ }
+
+ purple_print_utf8_to_console(stdout, text);
+ g_free(text);
+}
+
+static int
+init_libpurple(int argc, char **argv)
+{
+ char *path;
+ int opt;
+ gboolean opt_help = FALSE;
+ gboolean opt_nologin = FALSE;
+ gboolean opt_version = FALSE;
+ char *opt_config_dir_arg = NULL;
+ gboolean debug_enabled = FALSE;
+
+ struct option long_options[] = {
+ {"config", required_argument, NULL, 'c'},
+ {"debug", no_argument, NULL, 'd'},
+ {"help", no_argument, NULL, 'h'},
+ {"nologin", no_argument, NULL, 'n'},
+ {"version", no_argument, NULL, 'v'},
+ {0, 0, 0, 0}
+ };
+
+#ifdef ENABLE_NLS
+ bindtextdomain(PACKAGE, PURPLE_LOCALEDIR);
+ bind_textdomain_codeset(PACKAGE, "UTF-8");
+ textdomain(PACKAGE);
+#endif
+
+#ifdef HAVE_SETLOCALE
+ setlocale(LC_ALL, "");
+#endif
+
+ /* scan command-line options */
+ opterr = 1;
+ while ((opt = getopt_long(argc, argv, "c:dhn::v",
+ long_options, NULL)) != -1) {
+ switch (opt) {
+ case 'c': /* config dir */
+ g_free(opt_config_dir_arg);
+ opt_config_dir_arg = g_strdup(optarg);
+ break;
+ case 'd': /* debug */
+ debug_enabled = TRUE;
+ break;
+ case 'h': /* help */
+ opt_help = TRUE;
+ break;
+ case 'n': /* no autologin */
+ opt_nologin = TRUE;
+ break;
+ case 'v': /* version */
+ opt_version = TRUE;
+ break;
+ case '?': /* show terse help */
+ default:
+ show_usage(argv[0], TRUE);
+ return 0;
+ break;
+ }
+ }
+
+ /* show help message */
+ if (opt_help) {
+ show_usage(argv[0], FALSE);
+ return 0;
+ }
+ /* show version message */
+ if (opt_version) {
+ /* Translators may want to transliterate the name.
+ It is not to be translated. */
+ printf("%s %s (%s)\n", _("Finch"), DISPLAY_VERSION, REVISION);
+ return 0;
+ }
+
+ /* set a user-specified config directory */
+ if (opt_config_dir_arg != NULL) {
+ if (g_path_is_absolute(opt_config_dir_arg)) {
+ purple_util_set_user_dir(opt_config_dir_arg);
+ } else {
+ /* Make an absolute (if not canonical) path */
+ char *cwd = g_get_current_dir();
+ char *path = g_build_path(G_DIR_SEPARATOR_S, cwd, opt_config_dir_arg, NULL);
+ purple_util_set_user_dir(path);
+ g_free(path);
+ g_free(cwd);
+ }
+
+ g_free(opt_config_dir_arg);
+ }
+
+ /*
+ * We're done piddling around with command line arguments.
+ * Fire up this baby.
+ */
+
+ /* We don't want debug-messages to show up and corrupt the display */
+ purple_debug_set_enabled(debug_enabled);
+
+ purple_core_set_ui_ops(gnt_core_get_ui_ops());
+ purple_eventloop_set_ui_ops(gnt_eventloop_get_ui_ops());
+ purple_idle_set_ui_ops(finch_idle_get_ui_ops());
+
+ path = g_build_filename(purple_user_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);
+ g_free(path);
+
+ purple_plugins_add_search_path(FINCH_LIBDIR);
+
+ if (!purple_core_init(FINCH_UI))
+ {
+ fprintf(stderr,
+ "Initialization of the Purple core failed. Dumping core.\n"
+ "Please report this!\n");
+ abort();
+ }
+
+ /* TODO: should this be moved into finch_prefs_init() ? */
+ finch_prefs_update_old();
+
+ /* load plugins we had when we quit */
+ purple_plugins_load_saved("/finch/plugins/loaded");
+
+ if (opt_nologin)
+ {
+ /* Set all accounts to "offline" */
+ PurpleSavedStatus *saved_status;
+
+ /* If we've used this type+message before, lookup the transient status */
+ saved_status = purple_savedstatus_find_transient_by_type_and_message(
+ PURPLE_STATUS_OFFLINE, NULL);
+
+ /* If this type+message is unique then create a new transient saved status */
+ if (saved_status == NULL)
+ saved_status = purple_savedstatus_new(NULL, PURPLE_STATUS_OFFLINE);
+
+ /* Set the status for each account */
+ purple_savedstatus_activate(saved_status);
+ }
+ else
+ {
+ /* Everything is good to go--sign on already */
+ if (!purple_prefs_get_bool("/purple/savedstatus/startup_current_status"))
+ purple_savedstatus_activate(purple_savedstatus_get_startup());
+ purple_accounts_restore_current_statuses();
+ }
+
+ return 1;
+}
+
+gboolean finch_start(int *argc, char ***argv)
+{
+ /* Initialize the libpurple stuff */
+ if (!init_libpurple(*argc, *argv))
+ return FALSE;
+
+ purple_blist_show();
+ return TRUE;
+}
diff --git a/finch/libfinch_winres.rc.in b/finch/libfinch_winres.rc.in
new file mode 100644
index 0000000000..04af8966f6
--- /dev/null
+++ b/finch/libfinch_winres.rc.in
@@ -0,0 +1,30 @@
+#include <winver.h>
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION @PURPLE_MAJOR_VERSION@,@PURPLE_MINOR_VERSION@,@PURPLE_MICRO_VERSION@,0
+ PRODUCTVERSION @PURPLE_MAJOR_VERSION@,@PURPLE_MINOR_VERSION@,@PURPLE_MICRO_VERSION@,0
+ FILEFLAGSMASK 0
+ FILEFLAGS 0
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_APP
+ FILESUBTYPE VFT2_UNKNOWN
+ BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904B0"
+ BEGIN
+ VALUE "CompanyName", "The Pidgin developer community"
+ VALUE "FileDescription", "Finch UI library"
+ VALUE "FileVersion", "@PURPLE_VERSION@"
+ VALUE "InternalName", "libfinch"
+ VALUE "LegalCopyright", "Copyright (C) 1998-2014 The Pidgin developer community (See the COPYRIGHT file in the source distribution)."
+ VALUE "OriginalFilename", "libfinch-@PURPLE_API_VERSION@.dll"
+ VALUE "ProductName", "Finch"
+ VALUE "ProductVersion", "@PURPLE_VERSION@"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+ END
diff --git a/finch/libgnt/Makefile.am b/finch/libgnt/Makefile.am
index 07533e2474..00fd3b0c7f 100644
--- a/finch/libgnt/Makefile.am
+++ b/finch/libgnt/Makefile.am
@@ -73,11 +73,11 @@ CLEANFILES = \
gntmarshal.c
gntmarshal.c: $(srcdir)/genmarshal gntmarshal.h
- echo "#include \"gntmarshal.h\"" > $@
- glib-genmarshal --prefix=gnt_closure_marshal --body $(srcdir)/genmarshal >> $@
+ $(AM_V_GEN)echo "#include \"gntmarshal.h\"" > $@
+ $(AM_V_at)glib-genmarshal --prefix=gnt_closure_marshal --body $(srcdir)/genmarshal >> $@
gntmarshal.h: $(srcdir)/genmarshal
- glib-genmarshal --prefix=gnt_closure_marshal --header $(srcdir)/genmarshal > $@
+ $(AM_V_GEN)glib-genmarshal --prefix=gnt_closure_marshal --header $(srcdir)/genmarshal > $@
libgnt_laincludedir=$(includedir)/gnt
libgnt_lainclude_HEADERS = \
@@ -91,6 +91,17 @@ libgnt_la_LIBADD = \
$(LIBXML_LIBS) \
$(PY_LIBS)
+if IS_WIN32
+
+.rc.o: %.rc
+ $(AM_V_GEN)$(WINDRES) -i $< -o $@
+
+LIBGNT_WIN32RES = libgnt_winres.o
+libgnt_la_DEPENDENCIES += $(LIBGNT_WIN32RES)
+libgnt_la_LDFLAGS += -Wl,$(LIBGNT_WIN32RES)
+
+endif
+
AM_CPPFLAGS = \
$(GLIB_CFLAGS) \
$(GNT_CFLAGS) \
diff --git a/finch/libgnt/configure.ac b/finch/libgnt/configure.ac
index 5775f27f53..0ca20f204b 100644
--- a/finch/libgnt/configure.ac
+++ b/finch/libgnt/configure.ac
@@ -42,7 +42,7 @@ AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
GNT_MAJOR_VERSION=gnt_major_version
GNT_MINOR_VERSION=gnt_minor_version
GNT_MICRO_VERSION=gnt_micro_version
-GNT_VERSION=[gnt_display_version]
+GNT_VERSION=gnt_display_version
AC_SUBST(GNT_MAJOR_VERSION)
AC_SUBST(GNT_MINOR_VERSION)
AC_SUBST(GNT_MICRO_VERSION)
diff --git a/finch/libgnt/gnt-skel.c b/finch/libgnt/gnt-skel.c
index afa91bf02e..2a775debfa 100644
--- a/finch/libgnt/gnt-skel.c
+++ b/finch/libgnt/gnt-skel.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -72,9 +72,9 @@ gnt_skel_class_init(GntSkelClass *klass)
parent_class->size_request = gnt_skel_size_request;
parent_class->key_pressed = gnt_skel_key_pressed;
- parent_class->actions = g_hash_table_duplicate(parent_class->actions, g_str_hash,
+ parent_class->actions = gnt_hash_table_duplicate(parent_class->actions, g_str_hash,
g_str_equal, NULL, (GDestroyNotify)gnt_widget_action_free);
- parent_class->bindings = g_hash_table_duplicate(parent_class->bindings, g_str_hash,
+ parent_class->bindings = gnt_hash_table_duplicate(parent_class->bindings, g_str_hash,
g_str_equal, NULL, (GDestroyNotify)gnt_widget_action_param_free);
gnt_widget_actions_read(G_OBJECT_CLASS_TYPE(klass), klass);
@@ -92,7 +92,7 @@ gnt_skel_init(GTypeInstance *instance, gpointer class)
* GntSkel API
*****************************************************************************/
GType
-gnt_skel_get_gtype(void)
+gnt_skel_get_type(void)
{
static GType type = 0;
diff --git a/finch/libgnt/gnt-skel.h b/finch/libgnt/gnt-skel.h
index 98ba9e4710..978b05ff18 100644
--- a/finch/libgnt/gnt-skel.h
+++ b/finch/libgnt/gnt-skel.h
@@ -1,7 +1,3 @@
-/**
- * @file gnt-skel.h -skel API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,13 +22,19 @@
#ifndef GNT_SKEL_H
#define GNT_SKEL_H
+/*
+ * SECTION:gnt-skel
+ * @section_id: libgnt-gnt-skel
+ * @short_description: <filename>gnt-skel.h</filename>
+ * @title: Skel API
+ */
#include "gntwidget.h"
#include "gnt.h"
#include "gntcolors.h"
#include "gntkeys.h"
-#define GNT_TYPE_SKEL (gnt_skel_get_gtype())
+#define GNT_TYPE_SKEL (gnt_skel_get_type())
#define GNT_SKEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_SKEL, GntSkel))
#define GNT_SKEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_SKEL, GntSkelClass))
#define GNT_IS_SKEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_SKEL))
@@ -52,6 +54,7 @@ struct _GntSkelClass
{
GntWidgetClass parent;
+ /*< private >*/
void (*gnt_reserved1)(void);
void (*gnt_reserved2)(void);
void (*gnt_reserved3)(void);
@@ -60,18 +63,8 @@ struct _GntSkelClass
G_BEGIN_DECLS
-/**
- *
- *
- * @return
- */
-GType gnt_skel_get_gtype(void);
+GType gnt_skel_get_type(void);
-/**
- *
- *
- * @return
- */
GntWidget * gnt_skel_new();
G_END_DECLS
diff --git a/finch/libgnt/gnt.h b/finch/libgnt/gnt.h
index 0c1a0bc024..a52133f799 100644
--- a/finch/libgnt/gnt.h
+++ b/finch/libgnt/gnt.h
@@ -1,13 +1,3 @@
-/**
- * @defgroup gnt GNT (GLib Ncurses Toolkit)
- *
- * GNT is an ncurses toolkit for creating text-mode graphical user interfaces
- * in a fast and easy way.
- */
-/**
- * @file gnt.h GNT API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -32,179 +22,230 @@
#ifndef GNT_H
#define GNT_H
+/**
+ * SECTION:gnt
+ * @section_id: libgnt-gnt
+ * @short_description: <filename>gnt.h</filename>
+ * @title: GNT API
+ */
#include <glib.h>
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#ifdef _WIN32
+# undef KEY_EVENT
+#endif
+#ifdef NO_WIDECHAR
+# define NCURSES_WIDECHAR 0
+#else
+# define NCURSES_WIDECHAR 1
+#endif
+#include <ncurses.h>
+
#include "gntwidget.h"
#include "gntclipboard.h"
#include "gntcolors.h"
#include "gntkeys.h"
/**
- * Get things to compile in Glib < 2.8
+ * gnt_init:
+ *
+ * Initialize GNT.
*/
-#if !GLIB_CHECK_VERSION(2,8,0)
- #define G_PARAM_STATIC_NAME G_PARAM_PRIVATE
- #define G_PARAM_STATIC_NICK G_PARAM_PRIVATE
- #define G_PARAM_STATIC_BLURB G_PARAM_PRIVATE
-#endif
-
-#if !GLIB_CHECK_VERSION(2,14,0)
- #define g_timeout_add_seconds(time, callback, data) g_timeout_add(time * 1000, callback, data)
-#endif
+void gnt_init(void);
/**
- * Initialize GNT.
+ * gnt_set_config_dir:
+ * @config_dir: the path to a configuration directory, may be %NULL.
+ *
+ * Change directory to store gnt configuration files (default is ~).
+ *
+ * You have to call this before #gnt_init. You might want to call this
+ * with %NULL, to free the resources, but not before a call to #gnt_quit.
+ *
+ * Since: 2.8.0
*/
-void gnt_init(void);
+void gnt_set_config_dir(const gchar *config_dir);
/**
+ * gnt_main:
+ *
* Start running the mainloop for gnt.
*/
void gnt_main(void);
/**
+ * gnt_ascii_only:
+ *
* Check whether the terminal is capable of UTF8 display.
*
- * @return @c FALSE if the terminal is capable of drawing UTF-8, @c TRUE otherwise.
+ * Returns: %FALSE if the terminal is capable of drawing UTF-8, %TRUE otherwise.
*/
gboolean gnt_ascii_only(void);
/**
+ * gnt_window_present:
+ * @window: The window the present.
+ *
* Present a window. If the event was triggered because of user interaction,
* the window is moved to the foreground. Otherwise, the Urgent hint is set.
*
- * @param window The window the present.
- *
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
void gnt_window_present(GntWidget *window);
/**
- * @internal
- * Use #gnt_widget_show instead.
+ * gnt_screen_occupy:
+ *
+ * Internal function -- do not use.
+ * Use gnt_widget_show() instead.
*/
void gnt_screen_occupy(GntWidget *widget);
/**
- * @internal
- * Use #gnt_widget_hide instead.
+ * gnt_screen_release:
+ *
+ * Internal function -- do not use.
+ * Use gnt_widget_hide() instead.
*/
void gnt_screen_release(GntWidget *widget);
/**
- * @internal
- * Use #gnt_widget_draw instead.
+ * gnt_screen_update:
+ *
+ * Internal function -- do not use.
+ * Use gnt_widget_draw() instead.
*/
void gnt_screen_update(GntWidget *widget);
/**
- * Resize a widget.
+ * gnt_screen_resize_widget:
+ * @widget: The widget to resize.
+ * @width: The desired width.
+ * @height: The desired height.
*
- * @param widget The widget to resize.
- * @param width The desired width.
- * @param height The desired height.
+ * Resize a widget.
*/
void gnt_screen_resize_widget(GntWidget *widget, int width, int height);
/**
- * Move a widget.
+ * gnt_screen_move_widget:
+ * @widget: The widget to move.
+ * @x: The desired x-coordinate.
+ * @y: The desired y-coordinate.
*
- * @param widget The widget to move.
- * @param x The desired x-coordinate.
- * @param y The desired y-coordinate.
+ * Move a widget.
*/
void gnt_screen_move_widget(GntWidget *widget, int x, int y);
/**
- * Rename a widget.
+ * gnt_screen_rename_widget:
+ * @widget: The widget to rename.
+ * @text: The new name for the widget.
*
- * @param widget The widget to rename.
- * @param text The new name for the widget.
+ * Rename a widget.
*/
void gnt_screen_rename_widget(GntWidget *widget, const char *text);
/**
- * Check whether a widget has focus.
+ * gnt_widget_has_focus:
+ * @widget: The widget.
*
- * @param widget The widget.
+ * Check whether a widget has focus.
*
- * @return @c TRUE if the widget has the current focus, @c FALSE otherwise.
+ * Returns: %TRUE if the widget has the current focus, %FALSE otherwise.
*/
gboolean gnt_widget_has_focus(GntWidget *widget);
/**
- * Set the URGENT hint for a widget.
+ * gnt_widget_set_urgent:
+ * @widget: The widget to set the URGENT hint for.
*
- * @param widget The widget to set the URGENT hint for.
+ * Set the URGENT hint for a widget.
*/
void gnt_widget_set_urgent(GntWidget *widget);
/**
- * Register a global action.
+ * gnt_register_action:
+ * @label: The user-visible label for the action.
+ * @callback: The callback function for the action.
*
- * @param label The user-visible label for the action.
- * @param callback The callback function for the action.
+ * Register a global action.
*/
void gnt_register_action(const char *label, void (*callback)(void));
/**
- * Show a menu.
+ * gnt_screen_menu_show:
+ * @menu: The menu to display.
*
- * @param menu The menu to display.
+ * Show a menu.
*
- * @return @c TRUE if the menu is displayed, @c FALSE otherwise (e.g., if another menu is currently displayed).
+ * Returns: %TRUE if the menu is displayed, %FALSE otherwise (e.g., if another menu is currently displayed).
*/
gboolean gnt_screen_menu_show(gpointer menu);
/**
+ * gnt_quit:
+ *
* Terminate the mainloop of gnt.
*/
void gnt_quit(void);
/**
+ * gnt_get_clipboard:
+ *
* Get the global clipboard.
*
- * @return The clipboard.
+ * Returns: (transfer none): The clipboard.
*/
GntClipboard * gnt_get_clipboard(void);
/**
+ * gnt_get_clipboard_string:
+ *
* Get the string in the clipboard.
*
- * @return A copy of the string in the clipboard. The caller must @c g_free the string.
+ * Returns: A copy of the string in the clipboard. The caller
+ * must g_free() the string.
*/
gchar * gnt_get_clipboard_string(void);
/**
- * Set the contents of the global clipboard.
+ * gnt_set_clipboard_string:
+ * @string: The new content of the new clipboard.
*
- * @param string The new content of the new clipboard.
+ * Set the contents of the global clipboard.
*/
void gnt_set_clipboard_string(const gchar *string);
/**
- * Spawn a different application that will consume the console.
+ * gnt_giveup_console:
+ * @wd: The working directory for the new application.
+ * @argv: The argument vector.
+ * @envp: The environment, or %NULL.
+ * @stin: Location to store the child's stdin, or %NULL.
+ * @stout: Location to store the child's stdout, or %NULL.
+ * @sterr: Location to store the child's stderr, or %NULL.
+ * @callback: The callback to call after the child exits.
+ * @data: The data to pass to the callback.
*
- * @param wd The working directory for the new application.
- * @param argv The argument vector.
- * @param envp The environment, or @c NULL.
- * @param stin Location to store the child's stdin, or @c NULL.
- * @param stout Location to store the child's stdout, or @c NULL.
- * @param sterr Location to store the child's stderr, or @c NULL.
- * @param callback The callback to call after the child exits.
- * @param data The data to pass to the callback.
+ * Spawn a different application that will consume the console.
*
- * @return @c TRUE if the child was successfully spawned, @c FALSE otherwise.
+ * Returns: %TRUE if the child was successfully spawned, %FALSE otherwise.
*/
gboolean gnt_giveup_console(const char *wd, char **argv, char **envp,
gint *stin, gint *stout, gint *sterr,
void (*callback)(int status, gpointer data), gpointer data);
/**
+ * gnt_is_refugee:
+ *
* Check whether a child process is in control of the current terminal.
*
- * @return @c TRUE if a child process (eg., PAGER) is occupying the current
- * terminal, @c FALSE otherwise.
+ * Returns: %TRUE if a child process (eg., PAGER) is occupying the current
+ * terminal, %FALSE otherwise.
*/
gboolean gnt_is_refugee(void);
diff --git a/finch/libgnt/gntbindable.c b/finch/libgnt/gntbindable.c
index 33082a4612..f6d691d325 100644
--- a/finch/libgnt/gntbindable.c
+++ b/finch/libgnt/gntbindable.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -237,12 +237,12 @@ duplicate_hashes(GntBindableClass *klass)
{
/* Duplicate the bindings from parent class */
if (klass->actions) {
- klass->actions = g_hash_table_duplicate(klass->actions, g_str_hash,
+ klass->actions = gnt_hash_table_duplicate(klass->actions, g_str_hash,
g_str_equal, g_free, (GDestroyNotify)gnt_bindable_action_free,
- (GDupFunc)g_strdup, (GDupFunc)bindable_clone);
- klass->bindings = g_hash_table_duplicate(klass->bindings, g_str_hash,
+ (GntDuplicateFunc)g_strdup, (GntDuplicateFunc)bindable_clone);
+ klass->bindings = gnt_hash_table_duplicate(klass->bindings, g_str_hash,
g_str_equal, g_free, (GDestroyNotify)gnt_bindable_action_param_free,
- (GDupFunc)g_strdup, (GDupFunc)binding_clone);
+ (GntDuplicateFunc)g_strdup, (GntDuplicateFunc)binding_clone);
} else {
klass->actions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
(GDestroyNotify)gnt_bindable_action_free);
@@ -257,7 +257,7 @@ duplicate_hashes(GntBindableClass *klass)
* GntBindable API
*****************************************************************************/
GType
-gnt_bindable_get_gtype(void)
+gnt_bindable_get_type(void)
{
static GType type = 0;
@@ -283,7 +283,7 @@ gnt_bindable_get_gtype(void)
return type;
}
-/**
+/*
* Key Remaps
*/
const char *
@@ -304,7 +304,7 @@ gnt_bindable_remap_keys(GntBindable *bindable, const char *text)
return (remap ? remap : text);
}
-/**
+/*
* Actions and Bindings
*/
gboolean
diff --git a/finch/libgnt/gntbindable.h b/finch/libgnt/gntbindable.h
index ff39d561c7..064aadb13e 100644
--- a/finch/libgnt/gntbindable.h
+++ b/finch/libgnt/gntbindable.h
@@ -1,7 +1,3 @@
-/**
- * @file gntbindable.h Bindable API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,13 +22,18 @@
#ifndef GNT_BINDABLE_H
#define GNT_BINDABLE_H
+/**
+ * SECTION:gntbindable
+ * @section_id: libgnt-gntbindable
+ * @short_description: <filename>gntbindable.h</filename>
+ * @title: Bindable
+ */
#include <stdio.h>
#include <glib.h>
#include <glib-object.h>
-#include <ncurses.h>
-#define GNT_TYPE_BINDABLE (gnt_bindable_get_gtype())
+#define GNT_TYPE_BINDABLE (gnt_bindable_get_type())
#define GNT_BINDABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_BINDABLE, GntBindable))
#define GNT_BINDABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_BINDABLE, GntBindableClass))
#define GNT_IS_BINDABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_BINDABLE))
@@ -59,6 +60,7 @@ struct _GntBindableClass
GntBindable * help_window;
+ /*< private >*/
void (*gnt_reserved2)(void);
void (*gnt_reserved3)(void);
void (*gnt_reserved4)(void);
@@ -66,12 +68,7 @@ struct _GntBindableClass
G_BEGIN_DECLS
-/**
- *
- *
- * @return
- */
-GType gnt_bindable_get_gtype(void);
+GType gnt_bindable_get_type(void);
/******************/
/* Key Remaps */
@@ -91,8 +88,8 @@ struct _GntBindableAction
{
char *name; /* The name of the action */
union {
- gboolean (*action)(GntBindable *bindable, GList *params);
- gboolean (*action_noparam)(GntBindable *bindable);
+ GntBindableActionCallback action;
+ GntBindableActionCallbackNoParam action_noparam;
} u;
};
@@ -105,85 +102,94 @@ struct _GntBindableActionParam
/*GntBindableAction *gnt_bindable_action_parse(const char *name);*/
/**
- * Free a bindable action.
+ * gnt_bindable_action_free:
+ * @action: The bindable action.
*
- * @param action The bindable action.
+ * Free a bindable action.
*/
void gnt_bindable_action_free(GntBindableAction *action);
/**
- * Free a GntBindableActionParam.
+ * gnt_bindable_action_param_free:
+ * @param: The GntBindableActionParam to free.
*
- * @param param The GntBindableActionParam to free.
+ * Free a GntBindableActionParam.
*/
void gnt_bindable_action_param_free(GntBindableActionParam *param);
/**
- * Register a bindable action for a class.
+ * gnt_bindable_class_register_action:
+ * @klass: The class the binding is for.
+ * @name: The name of the binding.
+ * @callback: (scope call): The callback for the binding.
+ * @trigger: The default trigger for the binding, or %NULL, followed by a
+ * %NULL-terminated list of default parameters.
*
- * @param klass The class the binding is for.
- * @param name The name of the binding.
- * @param callback The callback for the binding.
- * @param trigger The default trigger for the binding, or @c NULL, followed by a NULL-terminated
- * list of default parameters.
+ * Register a bindable action for a class.
*/
void gnt_bindable_class_register_action(GntBindableClass *klass, const char *name, GntBindableActionCallback callback, const char *trigger, ...);
/**
- * Register a key-binding to an existing action.
+ * gnt_bindable_register_binding:
+ * @klass: The class the binding is for.
+ * @name: The name of the binding.
+ * @trigger: A new trigger for the binding, followed by a %NULL-terminated list of parameters for the callback.
*
- * @param klass The class the binding is for.
- * @param name The name of the binding.
- * @param trigger A new trigger for the binding, followed by a @c NULL-terminated list of parameters for the callback.
+ * Register a key-binding to an existing action.
*/
void gnt_bindable_register_binding(GntBindableClass *klass, const char *name, const char *trigger, ...);
/**
- * Perform an action from a keybinding.
+ * gnt_bindable_perform_action_key:
+ * @bindable: The bindable object.
+ * @keys: The key to trigger the action.
*
- * @param bindable The bindable object.
- * @param keys The key to trigger the action.
+ * Perform an action from a keybinding.
*
- * @return @c TRUE if the action was performed successfully, @c FALSE otherwise.
+ * Returns: %TRUE if the action was performed successfully, %FALSE otherwise.
*/
gboolean gnt_bindable_perform_action_key(GntBindable *bindable, const char *keys);
/**
- * Discover if a key is bound.
+ * gnt_bindable_check_key:
+ * @bindable: The bindable object.
+ * @keys: The key to check for.
*
- * @param bindable The bindable object.
- * @param keys The key to check for.
+ * Discover if a key is bound.
*
- * @return @c TRUE if the the key has an action associated with it.
+ * Returns: %TRUE if the the key has an action associated with it.
*/
gboolean gnt_bindable_check_key(GntBindable *bindable, const char *keys);
/**
- * Perform an action on a bindable object.
+ * gnt_bindable_perform_action_named:
+ * @bindable: The bindable object.
+ * @name: The action to perform, followed by a %NULL-terminated list of parameters.
*
- * @param bindable The bindable object.
- * @param name The action to perform, followed by a @c NULL-terminated list of parameters.
+ * Perform an action on a bindable object.
*
- * @return @c TRUE if the action was performed successfully, @c FALSE otherwise.
+ * Returns: %TRUE if the action was performed successfully, %FALSE otherwise.
*/
gboolean gnt_bindable_perform_action_named(GntBindable *bindable, const char *name, ...) G_GNUC_NULL_TERMINATED;
/**
- * Returns a GntTree populated with "key" -> "binding" for the widget.
+ * gnt_bindable_bindings_view:
+ * @bind: The object to list the bindings for.
*
- * @param bind The object to list the bindings for.
+ * Returns a GntTree populated with "key" -> "binding" for the widget.
*
- * @return The GntTree.
+ * Returns: (transfer full): The GntTree.
*/
GntBindable * gnt_bindable_bindings_view(GntBindable *bind);
/**
+ * gnt_bindable_build_help_window:
+ * @bindable: The object to list the bindings for.
+ *
* Builds a window that list the key bindings for a GntBindable object.
* From this window a user can select a listing to rebind a new key for the given action.
*
- * @param bindable The object to list the bindings for.
- *
- * @return @c TRUE
+ * Returns: %TRUE
*/
gboolean gnt_bindable_build_help_window(GntBindable *bindable);
diff --git a/finch/libgnt/gntbox.c b/finch/libgnt/gntbox.c
index 2910b8c3da..94e68bbef7 100644
--- a/finch/libgnt/gntbox.c
+++ b/finch/libgnt/gntbox.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -592,7 +592,7 @@ gnt_box_class_init(GntBoxClass *klass)
g_param_spec_boolean("vertical", "Vertical",
"Whether the child widgets in the box should be stacked vertically.",
TRUE,
- G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS
)
);
g_object_class_install_property(gclass,
@@ -600,7 +600,7 @@ gnt_box_class_init(GntBoxClass *klass)
g_param_spec_boolean("homogeneous", "Homogeneous",
"Whether the child widgets in the box should have the same size.",
TRUE,
- G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS
)
);
@@ -633,7 +633,7 @@ gnt_box_init(GTypeInstance *instance, gpointer class)
* GntBox API
*****************************************************************************/
GType
-gnt_box_get_gtype(void)
+gnt_box_get_type(void)
{
static GType type = 0;
@@ -662,11 +662,10 @@ gnt_box_get_gtype(void)
GntWidget *gnt_box_new(gboolean homo, gboolean vert)
{
- GntWidget *widget = g_object_new(GNT_TYPE_BOX, NULL);
+ GntWidget *widget = g_object_new(GNT_TYPE_BOX, "homogeneous", homo,
+ "vertical", vert, NULL);
GntBox *box = GNT_BOX(widget);
- box->homogeneous = homo;
- box->vertical = vert;
box->alignment = vert ? GNT_ALIGN_LEFT : GNT_ALIGN_MID;
return widget;
@@ -678,6 +677,12 @@ void gnt_box_add_widget(GntBox *b, GntWidget *widget)
widget->parent = GNT_WIDGET(b);
}
+void gnt_box_add_widget_in_front(GntBox *b, GntWidget *widget)
+{
+ b->list = g_list_prepend(b->list, widget);
+ widget->parent = GNT_WIDGET(b);
+}
+
void gnt_box_set_title(GntBox *b, const char *title)
{
char *prev = b->title;
@@ -732,6 +737,11 @@ void gnt_box_sync_children(GntBox *box)
int height, width;
int x, y;
+ if (G_UNLIKELY(w == NULL)) {
+ g_warn_if_reached();
+ continue;
+ }
+
if (GNT_WIDGET_IS_FLAG_SET(w, GNT_WIDGET_INVISIBLE))
continue;
@@ -820,6 +830,12 @@ void gnt_box_readjust(GntBox *box)
for (iter = box->list; iter; iter = iter->next)
{
GntWidget *w = iter->data;
+
+ if (G_UNLIKELY(w == NULL)) {
+ g_warn_if_reached();
+ continue;
+ }
+
if (GNT_IS_BOX(w))
gnt_box_readjust(GNT_BOX(w));
else
diff --git a/finch/libgnt/gntbox.h b/finch/libgnt/gntbox.h
index f35c691b55..1aefa9b32a 100644
--- a/finch/libgnt/gntbox.h
+++ b/finch/libgnt/gntbox.h
@@ -1,7 +1,3 @@
-/**
- * @file gntbox.h Box API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,11 +22,17 @@
#ifndef GNT_BOX_H
#define GNT_BOX_H
+/**
+ * SECTION:gntbox
+ * @section_id: libgnt-gntbox
+ * @short_description: <filename>gntbox.h</filename>
+ * @title: Box
+ */
#include "gnt.h"
#include "gntwidget.h"
-#define GNT_TYPE_BOX (gnt_box_get_gtype())
+#define GNT_TYPE_BOX (gnt_box_get_type())
#define GNT_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_BOX, GntBox))
#define GNT_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_BOX, GntBoxClass))
#define GNT_IS_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_BOX))
@@ -69,16 +71,18 @@ struct _GntBox
char *title;
GList *focus; /* List of widgets to cycle focus (only valid for parent boxes) */
- void (*gnt_reserved1)(void);
- void (*gnt_reserved2)(void);
- void (*gnt_reserved3)(void);
- void (*gnt_reserved4)(void);
+ /*< private >*/
+ void *res1;
+ void *res2;
+ void *res3;
+ void *res4;
};
struct _GntBoxClass
{
GntWidgetClass parent;
+ /*< private >*/
void (*gnt_reserved1)(void);
void (*gnt_reserved2)(void);
void (*gnt_reserved3)(void);
@@ -88,119 +92,146 @@ struct _GntBoxClass
G_BEGIN_DECLS
/**
+ * gnt_box_get_type:
+ *
* The GType for GntBox.
- * @return The GType.
+ *
+ * Returns: The GType.
*/
-GType gnt_box_get_gtype(void);
+GType gnt_box_get_type(void);
#define gnt_vbox_new(homo) gnt_box_new(homo, TRUE)
#define gnt_hbox_new(homo) gnt_box_new(homo, FALSE)
/**
- * Create a new GntBox.
+ * gnt_box_new:
+ * @homo: If %TRUE, all the widgets in it will have the same width (or height)
+ * @vert: Whether the widgets in it should be stacked vertically (if %TRUE)
+ * or horizontally (if %FALSE).
*
- * @param homo If @c TRUE, all the widgets in it will have the same width (or height)
- * @param vert Whether the widgets in it should be stacked vertically (if @c TRUE)
- * or horizontally (if @c FALSE).
+ * Create a new GntBox.
*
- * @return The new GntBox.
+ * Returns: The new GntBox.
*/
GntWidget * gnt_box_new(gboolean homo, gboolean vert);
/**
- * Add a widget in the box.
+ * gnt_box_add_widget:
+ * @box: The box
+ * @widget: The widget to add
*
- * @param box The box
- * @param widget The widget to add
+ * Add a widget in the box.
*/
void gnt_box_add_widget(GntBox *box, GntWidget *widget);
/**
- * Set a title for the box.
+ * gnt_box_add_widget_in_front:
+ * @box: The box
+ * @widget: The widget to add
+ *
+ * Add a widget in the box at its front.
+ *
+ * Since: 2.8.0
+ */
+void gnt_box_add_widget_in_front(GntBox *b, GntWidget *widget);
+
+/**
+ * gnt_box_set_title:
+ * @box: The box
+ * @title: The title to set
*
- * @param box The box
- * @param title The title to set
+ * Set a title for the box.
*/
void gnt_box_set_title(GntBox *box, const char *title);
/**
- * Set the padding to use between the widgets in the box.
+ * gnt_box_set_pad:
+ * @box: The box
+ * @pad: The padding to use
*
- * @param box The box
- * @param pad The padding to use
+ * Set the padding to use between the widgets in the box.
*/
void gnt_box_set_pad(GntBox *box, int pad);
/**
+ * gnt_box_set_toplevel:
+ * @box: The box
+ * @set: %TRUE if it's a toplevel box, %FALSE otherwise.
+ *
* Set whether it's a toplevel box (ie, a window) or not. If a box is toplevel,
* then it will show borders, the title (if set) and shadow (if enabled in
- * @e .gntrc)
- *
- * @param box The box
- * @param set @c TRUE if it's a toplevel box, @c FALSE otherwise.
+ * <filename>.gntrc</filename>)
*/
void gnt_box_set_toplevel(GntBox *box, gboolean set);
/**
- * Reposition and refresh the widgets in the box.
+ * gnt_box_sync_children:
+ * @box: The box
*
- * @param box The box
+ * Reposition and refresh the widgets in the box.
*/
void gnt_box_sync_children(GntBox *box);
/**
- * Set the alignment for the widgets in the box.
+ * gnt_box_set_alignment:
+ * @box: The box
+ * @alignment: The alignment to use
*
- * @param box The box
- * @param alignment The alignment to use
+ * Set the alignment for the widgets in the box.
*/
void gnt_box_set_alignment(GntBox *box, GntAlignment alignment);
/**
- * Remove a widget from the box. Calling this does NOT destroy the removed widget.
+ * gnt_box_remove:
+ * @box: The box
+ * @widget: The widget to remove
*
- * @param box The box
- * @param widget The widget to remove
+ * Remove a widget from the box. Calling this does NOT destroy the removed widget.
*/
void gnt_box_remove(GntBox *box, GntWidget *widget);
/**
- * Remove all widgets from the box. This DOES destroy all widgets in the box.
+ * gnt_box_remove_all:
+ * @box: The box
*
- * @param box The box
+ * Remove all widgets from the box. This DOES destroy all widgets in the box.
*/
void gnt_box_remove_all(GntBox *box);
/**
+ * gnt_box_readjust:
+ * @box: The box
+ *
* Readjust the size of each child widget, reposition the child widgets and
* recalculate the size of the box.
- *
- * @param box The box
*/
void gnt_box_readjust(GntBox *box);
/**
- * Set whether the widgets in the box should fill the empty spaces.
+ * gnt_box_set_fill:
+ * @box: The box
+ * @fill: Whether the child widgets should fill the empty space
*
- * @param box The box
- * @param fill Whether the child widgets should fill the empty space
+ * Set whether the widgets in the box should fill the empty spaces.
*/
void gnt_box_set_fill(GntBox *box, gboolean fill);
/**
- * Move the focus from one widget to the other.
- *
- * @param box The box
- * @param dir The direction. If it's 1, then the focus is moved forwards, if it's
+ * gnt_box_move_focus:
+ * @box: The box
+ * @dir: The direction. If it's 1, then the focus is moved forwards, if it's
* -1, the focus is moved backwards.
+ *
+ * Move the focus from one widget to the other.
*/
void gnt_box_move_focus(GntBox *box, int dir);
/**
- * Give focus to a specific child widget.
+ * gnt_box_give_focus_to_child:
+ * @box: The box
+ * @widget: The child widget to give focus
*
- * @param box The box
- * @param widget The child widget to give focus
+ * Give focus to a specific child widget.
*/
void gnt_box_give_focus_to_child(GntBox *box, GntWidget *widget);
diff --git a/finch/libgnt/gntbutton.c b/finch/libgnt/gntbutton.c
index 1857a7b4f9..02e6a7aae5 100644
--- a/finch/libgnt/gntbutton.c
+++ b/finch/libgnt/gntbutton.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -143,7 +143,7 @@ gnt_button_init(GTypeInstance *instance, gpointer class)
* GntButton API
*****************************************************************************/
GType
-gnt_button_get_gtype(void) {
+gnt_button_get_type(void) {
static GType type = 0;
if(type == 0) {
diff --git a/finch/libgnt/gntbutton.h b/finch/libgnt/gntbutton.h
index c285cb7fcd..97ed005a25 100644
--- a/finch/libgnt/gntbutton.h
+++ b/finch/libgnt/gntbutton.h
@@ -1,7 +1,3 @@
-/**
- * @file gntbutton.h Button API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,13 +22,19 @@
#ifndef GNT_BUTTON_H
#define GNT_BUTTON_H
+/**
+ * SECTION:gntbutton
+ * @section_id: libgnt-gntbutton
+ * @short_description: <filename>gntbutton.h</filename>
+ * @title: Button
+ */
#include <glib.h>
#include <glib-object.h>
#include "gnt.h"
#include "gntwidget.h"
-#define GNT_TYPE_BUTTON (gnt_button_get_gtype())
+#define GNT_TYPE_BUTTON (gnt_button_get_type())
#define GNT_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_BUTTON, GntButton))
#define GNT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_BUTTON, GntButtonClass))
#define GNT_IS_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_BUTTON))
@@ -54,16 +56,18 @@ struct _GntButton
GntButtonPriv *priv;
- void (*gnt_reserved1)(void);
- void (*gnt_reserved2)(void);
- void (*gnt_reserved3)(void);
- void (*gnt_reserved4)(void);
+ /*< private >*/
+ void *res1;
+ void *res2;
+ void *res3;
+ void *res4;
};
struct _GntButtonClass
{
GntWidgetClass parent;
+ /*< private >*/
void (*gnt_reserved1)(void);
void (*gnt_reserved2)(void);
void (*gnt_reserved3)(void);
@@ -73,16 +77,19 @@ struct _GntButtonClass
G_BEGIN_DECLS
/**
- * @return GType for Gntbutton
+ * gnt_button_get_type:
+ *
+ * Returns: GType for Gntbutton
*/
-GType gnt_button_get_gtype(void);
+GType gnt_button_get_type(void);
/**
- * Create a new button.
+ * gnt_button_new:
+ * @text: The text for the button.
*
- * @param text The text for the button.
+ * Create a new button.
*
- * @return The newly created button.
+ * Returns: The newly created button.
*/
GntWidget * gnt_button_new(const char *text);
diff --git a/finch/libgnt/gntcheckbox.c b/finch/libgnt/gntcheckbox.c
index 7b48eedaea..698c561151 100644
--- a/finch/libgnt/gntcheckbox.c
+++ b/finch/libgnt/gntcheckbox.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -125,7 +125,7 @@ gnt_check_box_init(GTypeInstance *instance, gpointer class)
* GntCheckBox API
*****************************************************************************/
GType
-gnt_check_box_get_gtype(void)
+gnt_check_box_get_type(void)
{
static GType type = 0;
diff --git a/finch/libgnt/gntcheckbox.h b/finch/libgnt/gntcheckbox.h
index 559652de1c..f42b5ee135 100644
--- a/finch/libgnt/gntcheckbox.h
+++ b/finch/libgnt/gntcheckbox.h
@@ -1,7 +1,3 @@
-/**
- * @file gntcheckbox.h Checkbox API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,13 +22,19 @@
#ifndef GNT_CHECK_BOX_H
#define GNT_CHECK_BOX_H
+/**
+ * SECTION:gntcheckbox
+ * @section_id: libgnt-gntcheckbox
+ * @short_description: <filename>gntcheckbox.h</filename>
+ * @title: Checkbox
+ */
#include "gntbutton.h"
#include "gnt.h"
#include "gntcolors.h"
#include "gntkeys.h"
-#define GNT_TYPE_CHECK_BOX (gnt_check_box_get_gtype())
+#define GNT_TYPE_CHECK_BOX (gnt_check_box_get_type())
#define GNT_CHECK_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_CHECK_BOX, GntCheckBox))
#define GNT_CHECK_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_CHECK_BOX, GntCheckBoxClass))
#define GNT_IS_CHECK_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_CHECK_BOX))
@@ -59,6 +61,7 @@ struct _GntCheckBoxClass
void (*toggled)(void);
+ /*< private >*/
void (*gnt_reserved1)(void);
void (*gnt_reserved2)(void);
void (*gnt_reserved3)(void);
@@ -68,33 +71,38 @@ struct _GntCheckBoxClass
G_BEGIN_DECLS
/**
- * @return GType for GntCheckBox
+ * gnt_check_box_get_type:
+ *
+ * Returns: GType for GntCheckBox
*/
-GType gnt_check_box_get_gtype(void);
+GType gnt_check_box_get_type(void);
/**
- * Create a new checkbox.
+ * gnt_check_box_new:
+ * @text: The text for the checkbox.
*
- * @param text The text for the checkbox.
+ * Create a new checkbox.
*
- * @return The newly created checkbox.
+ * Returns: The newly created checkbox.
*/
GntWidget * gnt_check_box_new(const char *text);
/**
- * Set whether the checkbox should be checked or not.
+ * gnt_check_box_set_checked:
+ * @box: The checkbox.
+ * @set: %TRUE if the checkbox should be selected, %FALSE otherwise.
*
- * @param box The checkbox.
- * @param set @c TRUE if the checkbox should be selected, @c FALSE otherwise.
+ * Set whether the checkbox should be checked or not.
*/
void gnt_check_box_set_checked(GntCheckBox *box, gboolean set);
/**
- * Return the checked state of the checkbox.
+ * gnt_check_box_get_checked:
+ * @box: The checkbox.
*
- * @param box The checkbox.
+ * Return the checked state of the checkbox.
*
- * @return @c TRUE if the checkbox is selected, @c FALSE otherwise.
+ * Returns: %TRUE if the checkbox is selected, %FALSE otherwise.
*/
gboolean gnt_check_box_get_checked(GntCheckBox *box);
diff --git a/finch/libgnt/gntclipboard.c b/finch/libgnt/gntclipboard.c
index 7bef9c96e3..1be202834f 100644
--- a/finch/libgnt/gntclipboard.c
+++ b/finch/libgnt/gntclipboard.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -67,7 +67,7 @@ static void gnt_clipboard_init(GTypeInstance *instance, gpointer class) {
}
GType
-gnt_clipboard_get_gtype(void)
+gnt_clipboard_get_type(void)
{
static GType type = 0;
diff --git a/finch/libgnt/gntclipboard.h b/finch/libgnt/gntclipboard.h
index d4c1771d1d..d1b5f31770 100644
--- a/finch/libgnt/gntclipboard.h
+++ b/finch/libgnt/gntclipboard.h
@@ -1,7 +1,3 @@
-/**
- * @file gntclipboard.h Clipboard API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,12 +22,18 @@
#ifndef GNT_CLIPBOARD_H
#define GNT_CLIPBOARD_H
+/**
+ * SECTION:gntclipboard
+ * @section_id: libgnt-gntclipboard
+ * @short_description: <filename>gntclipboard.h</filename>
+ * @title: Clipboard
+ */
#include <stdio.h>
#include <glib.h>
#include <glib-object.h>
-#define GNT_TYPE_CLIPBOARD (gnt_clipboard_get_gtype())
+#define GNT_TYPE_CLIPBOARD (gnt_clipboard_get_type())
#define GNT_CLIPBOARD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_CLIPBOARD, GntClipboard))
#define GNT_CLIPBOARD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_CLIPBOARD, GntClipboardClass))
#define GNT_IS_CLIPBOARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_CLIPBOARD))
@@ -51,6 +53,7 @@ struct _GntClipboardClass
{
GObjectClass parent;
+ /*< private >*/
void (*gnt_reserved1)(void);
void (*gnt_reserved2)(void);
void (*gnt_reserved3)(void);
@@ -60,25 +63,29 @@ struct _GntClipboardClass
G_BEGIN_DECLS
/**
- * @return GType for GntClipboard.
+ * gnt_clipboard_get_type:
+ *
+ * Returns: GType for GntClipboard.
*/
-GType gnt_clipboard_get_gtype(void);
+GType gnt_clipboard_get_type(void);
/**
- * Get the current text from the clipboard.
+ * gnt_clipboard_get_string:
+ * @clip: The clipboard.
*
- * @param clip The clipboard.
+ * Get the current text from the clipboard.
*
- * @return A copy of the string in the clipboard. The caller should free the
+ * Returns: A copy of the string in the clipboard. The caller should free the
* returned value.
*/
gchar * gnt_clipboard_get_string(GntClipboard *clip);
/**
- * Set the text in the clipboard.
+ * gnt_clipboard_set_string:
+ * @clip: The clipboard.
+ * @string: New string for the clipboard.
*
- * @param clip The clipboard.
- * @param string New string for the clipboard.
+ * Set the text in the clipboard.
*/
void gnt_clipboard_set_string(GntClipboard *clip, const gchar *string);
diff --git a/finch/libgnt/gntcolors.c b/finch/libgnt/gntcolors.c
index 082be1dd48..760504e5aa 100644
--- a/finch/libgnt/gntcolors.c
+++ b/finch/libgnt/gntcolors.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -20,10 +20,6 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#include "config.h"
-
-#include <ncurses.h>
-
#include "gntinternal.h"
#undef GNT_LOG_DOMAIN
#define GNT_LOG_DOMAIN "Colors"
@@ -142,7 +138,6 @@ gnt_uninit_colors()
restore_colors();
}
-#if GLIB_CHECK_VERSION(2,6,0)
int
gnt_colors_get_color(char *key)
{
@@ -182,10 +177,15 @@ void gnt_colors_parse(GKeyFile *kfile)
{
GError *error = NULL;
gsize nkeys;
- char **keys = g_key_file_get_keys(kfile, "colors", &nkeys, &error);
+ char **keys;
- if (error)
- {
+ if (!g_key_file_has_group(kfile, "colors")) {
+ gnt_color_pairs_parse(kfile);
+ return;
+ }
+
+ keys = g_key_file_get_keys(kfile, "colors", &nkeys, &error);
+ if (error) {
gnt_warning("%s", error->message);
g_error_free(error);
error = NULL;
@@ -228,8 +228,12 @@ void gnt_color_pairs_parse(GKeyFile *kfile)
{
GError *error = NULL;
gsize nkeys;
- char **keys = g_key_file_get_keys(kfile, "colorpairs", &nkeys, &error);
+ char **keys;
+ if (!g_key_file_has_group(kfile, "colorpairs"))
+ return;
+
+ keys = g_key_file_get_keys(kfile, "colorpairs", &nkeys, &error);
if (error)
{
gnt_warning("%s", error->message);
@@ -293,13 +297,11 @@ void gnt_color_pairs_parse(GKeyFile *kfile)
g_strfreev(keys);
}
-#endif /* GKeyFile */
-
int gnt_color_pair(int pair)
{
return (hascolors ? COLOR_PAIR(pair) :
((pair == GNT_COLOR_NORMAL || pair == GNT_COLOR_HIGHLIGHT_D ||
- pair == GNT_COLOR_TITLE_D || pair == GNT_COLOR_DISABLED) ? 0 : A_STANDOUT));
+ pair == GNT_COLOR_TITLE_D || pair == GNT_COLOR_DISABLED) ? 0 : (int)A_STANDOUT));
}
int gnt_color_add_pair(int fg, int bg)
diff --git a/finch/libgnt/gntcolors.h b/finch/libgnt/gntcolors.h
index 6904b5e797..3abb147466 100644
--- a/finch/libgnt/gntcolors.h
+++ b/finch/libgnt/gntcolors.h
@@ -1,7 +1,3 @@
-/**
- * @file gntcolors.h Colors API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,10 +22,18 @@
#ifndef GNT_COLORS_H
#define GNT_COLORS_H
+/**
+ * SECTION:gntcolors
+ * @section_id: libgnt-gntcolors
+ * @short_description: <filename>gntcolors.h</filename>
+ * @title: Colors
+ */
#include <glib.h>
/**
+ * GntColorType:
+ *
* Different classes of colors.
*/
typedef enum
@@ -62,64 +66,72 @@ enum
};
/**
+ * gnt_init_colors:
+ *
* Initialize the colors.
*/
void gnt_init_colors(void);
/**
+ * gnt_uninit_colors:
+ *
* Uninitialize the colors.
*/
void gnt_uninit_colors(void);
-#if GLIB_CHECK_VERSION(2,6,0)
/**
- * Parse color information from a file.
+ * gnt_colors_parse:
+ * @kfile: The file containing color information.
*
- * @param kfile The file containing color information.
+ * Parse color information from a file.
*/
void gnt_colors_parse(GKeyFile *kfile);
/**
- * Parse color-pair information from a file.
+ * gnt_color_pairs_parse:
+ * @kfile: The file containing the color-pair information.
*
- * @param kfile The file containing the color-pair information.
+ * Parse color-pair information from a file.
*/
void gnt_color_pairs_parse(GKeyFile *kfile);
/**
- * Parse a string color
+ * gnt_colors_get_color:
+ * @key: The string value
*
- * @param kfile The string value
+ * Parse a string color
*
- * @return A color. For an unknown color name, returns -EINVAL.
+ * Returns: A color. For an unknown color name, returns -EINVAL.
*
- * @since 2.4.0
+ * Since: 2.4.0
*/
int gnt_colors_get_color(char *key);
-#endif
/**
+ * gnt_color_pair:
+ * @color: The color code.
+ *
* Return the appropriate character attribute for a specified color.
* If the terminal doesn't have color support, this returns A_STANDOUT
* when deemed appropriate.
*
- * @param color The color code.
+ * Returns: A character attribute.
*
- * @return A character attribute.
- *
- * @since 2.3.0
+ * Since: 2.3.0
*/
int gnt_color_pair(int color);
/**
- * Adds a color definition
+ * gnt_color_add_pair:
+ * @fg: Foreground
+ * @bg: Background
*
- * @param fg Foreground
- * @param bg Background
+ * Adds a color definition
*
- * @return A color pair
+ * Returns: A color pair
*
- * @since 2.4.0
+ * Since: 2.4.0
*/
int gnt_color_add_pair(int fg, int bg);
+
#endif
diff --git a/finch/libgnt/gntcombobox.c b/finch/libgnt/gntcombobox.c
index 2d8fa27c42..ed0b38a37b 100644
--- a/finch/libgnt/gntcombobox.c
+++ b/finch/libgnt/gntcombobox.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -321,7 +321,7 @@ gnt_combo_box_init(GTypeInstance *instance, gpointer class)
* GntComboBox API
*****************************************************************************/
GType
-gnt_combo_box_get_gtype(void)
+gnt_combo_box_get_type(void)
{
static GType type = 0;
diff --git a/finch/libgnt/gntcombobox.h b/finch/libgnt/gntcombobox.h
index a90963d6f2..0dfba60cf0 100644
--- a/finch/libgnt/gntcombobox.h
+++ b/finch/libgnt/gntcombobox.h
@@ -1,7 +1,3 @@
-/**
- * @file gntcombobox.h Combobox API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,13 +22,19 @@
#ifndef GNT_COMBO_BOX_H
#define GNT_COMBO_BOX_H
+/**
+ * SECTION:gntcombobox
+ * @section_id: libgnt-gntcombobox
+ * @short_description: <filename>gntcombobox.h</filename>
+ * @title: Combobox
+ */
#include "gnt.h"
#include "gntcolors.h"
#include "gntkeys.h"
#include "gntwidget.h"
-#define GNT_TYPE_COMBO_BOX (gnt_combo_box_get_gtype())
+#define GNT_TYPE_COMBO_BOX (gnt_combo_box_get_type())
#define GNT_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_COMBO_BOX, GntComboBox))
#define GNT_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_COMBO_BOX, GntComboBoxClass))
#define GNT_IS_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_COMBO_BOX))
@@ -60,6 +62,7 @@ struct _GntComboBoxClass
{
GntWidgetClass parent;
+ /*< private >*/
void (*gnt_reserved1)(void);
void (*gnt_reserved2)(void);
void (*gnt_reserved3)(void);
@@ -69,55 +72,64 @@ struct _GntComboBoxClass
G_BEGIN_DECLS
/**
- * @return Get the GType for GntComboBox
+ * gnt_combo_box_get_type:
+ *
+ * Returns: Get the GType for GntComboBox
*/
-GType gnt_combo_box_get_gtype(void);
+GType gnt_combo_box_get_type(void);
/**
+ * gnt_combo_box_new:
+ *
* Create a new GntComboBox
*
- * @return A new GntComboBox
+ * Returns: A new GntComboBox
*/
GntWidget * gnt_combo_box_new(void);
/**
- * Add an entry
+ * gnt_combo_box_add_data:
+ * @box: The GntComboBox
+ * @key: The data
+ * @text: The text to display
*
- * @param box The GntComboBox
- * @param key The data
- * @param text The text to display
+ * Add an entry
*/
void gnt_combo_box_add_data(GntComboBox *box, gpointer key, const char *text);
/**
- * Remove an entry
+ * gnt_combo_box_remove:
+ * @box: The GntComboBox
+ * @key: The data to be removed
*
- * @param box The GntComboBox
- * @param key The data to be removed
+ * Remove an entry
*/
void gnt_combo_box_remove(GntComboBox *box, gpointer key);
/**
- * Remove all entries
+ * gnt_combo_box_remove_all:
+ * @box: The GntComboBox
*
- * @param box The GntComboBox
+ * Remove all entries
*/
void gnt_combo_box_remove_all(GntComboBox *box);
/**
- * Get the data that is currently selected
+ * gnt_combo_box_get_selected_data:
+ * @box: The GntComboBox
*
- * @param box The GntComboBox
+ * Get the data that is currently selected
*
- * @return The data of the currently selected entry
+ * Returns: (transfer none): The data of the currently selected entry
*/
gpointer gnt_combo_box_get_selected_data(GntComboBox *box);
/**
- * Set the current selection to a specific entry
+ * gnt_combo_box_set_selected:
+ * @box: The GntComboBox
+ * @key: The data to be set to
*
- * @param box The GntComboBox
- * @param key The data to be set to
+ * Set the current selection to a specific entry
*/
void gnt_combo_box_set_selected(GntComboBox *box, gpointer key);
diff --git a/finch/libgnt/gntentry.c b/finch/libgnt/gntentry.c
index b6399c6ae8..836fd5b6fc 100644
--- a/finch/libgnt/gntentry.c
+++ b/finch/libgnt/gntentry.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -184,7 +184,7 @@ static gboolean
show_suggest_dropdown(GntEntry *entry)
{
char *suggest = NULL;
- int len;
+ gsize len;
int offset = 0, x, y;
int count = 0;
GList *iter;
@@ -294,7 +294,7 @@ gnt_entry_draw(GntWidget *widget)
stop = gnt_util_onscreen_width(entry->scroll, entry->end);
if (stop < widget->priv.width)
- mvwhline(widget->window, 0, stop, ENTRY_CHAR, widget->priv.width - stop);
+ mvwhline(widget->window, 0, stop, GNT_ENTRY_CHAR, widget->priv.width - stop);
curpos = gnt_util_onscreen_width(entry->scroll, entry->cursor);
if (focus)
@@ -815,7 +815,7 @@ gnt_entry_key_pressed(GntWidget *widget, const char *text)
for (str = text; *str; str = next)
{
- int len;
+ gsize len;
next = g_utf8_find_next_char(str, NULL);
len = next - str;
@@ -834,7 +834,7 @@ gnt_entry_key_pressed(GntWidget *widget, const char *text)
if (entry->max && g_utf8_pointer_to_offset(entry->start, entry->end) >= entry->max)
continue;
- if (entry->end + len - entry->start >= entry->buffer)
+ if ((gsize)(entry->end + len - entry->start) >= entry->buffer)
{
/* This will cause the buffer to grow */
char *tmp = g_strdup(entry->start);
@@ -1064,7 +1064,7 @@ gnt_entry_init(GTypeInstance *instance, gpointer class)
* GntEntry API
*****************************************************************************/
GType
-gnt_entry_get_gtype(void)
+gnt_entry_get_type(void)
{
static GType type = 0;
@@ -1184,8 +1184,11 @@ void gnt_entry_add_to_history(GntEntry *entry, const char *text)
{
g_return_if_fail(entry->history != NULL); /* Need to set_history_length first */
- if (g_list_length(entry->history) >= entry->histlength)
+ if (entry->histlength >= 0 &&
+ g_list_length(entry->history) >= (gsize)entry->histlength)
+ {
return;
+ }
entry->history = g_list_first(entry->history);
g_free(entry->history->data);
diff --git a/finch/libgnt/gntentry.h b/finch/libgnt/gntentry.h
index 1b0f260903..06fa9911e7 100644
--- a/finch/libgnt/gntentry.h
+++ b/finch/libgnt/gntentry.h
@@ -1,7 +1,3 @@
-/**
- * @file gntentry.h Entry API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,13 +22,19 @@
#ifndef GNT_ENTRY_H
#define GNT_ENTRY_H
+/**
+ * SECTION:gntentry
+ * @section_id: libgnt-gntentry
+ * @short_description: <filename>gntentry.h</filename>
+ * @title: Entry
+ */
#include "gntwidget.h"
#include "gnt.h"
#include "gntcolors.h"
#include "gntkeys.h"
-#define GNT_TYPE_ENTRY (gnt_entry_get_gtype())
+#define GNT_TYPE_ENTRY (gnt_entry_get_type())
#define GNT_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_ENTRY, GntEntry))
#define GNT_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_ENTRY, GntEntryClass))
#define GNT_IS_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_ENTRY))
@@ -43,7 +45,7 @@
#define GNT_ENTRY_SET_FLAGS(obj, flags) (GNT_ENTRY_FLAGS(obj) |= flags)
#define GNT_ENTRY_UNSET_FLAGS(obj, flags) (GNT_ENTRY_FLAGS(obj) &= ~(flags))
-#define ENTRY_CHAR '_' /* The character to use to fill in the blank places */
+#define GNT_ENTRY_CHAR '_' /* The character to use to fill in the blank places */
typedef struct _GntEntry GntEntry;
typedef struct _GntEntryPriv GntEntryPriv;
@@ -86,8 +88,8 @@ struct _GntEntry
gboolean word; /* Are the suggestions for only a word, or for the whole thing? */
gboolean always; /* Should the list of suggestions show at all times, or only on tab-press? */
GntWidget *ddown; /* The dropdown with the suggested list */
- GntEntryKillRing *killring; /**< @since 2.3.0 */
- GntEntrySearch *search; /**< @since 2.7.0 */
+ GntEntryKillRing *killring; /* Since: 2.3.0 */
+ GntEntrySearch *search; /* Since: 2.7.0 */
};
struct _GntEntryClass
@@ -95,6 +97,8 @@ struct _GntEntryClass
GntWidgetClass parent;
void (*text_changed)(GntEntry *entry);
+
+ /*< private >*/
void (*gnt_reserved1)(void);
void (*gnt_reserved2)(void);
void (*gnt_reserved3)(void);
@@ -104,115 +108,130 @@ struct _GntEntryClass
G_BEGIN_DECLS
/**
- * @return GType for GntEntry.
+ * gnt_entry_get_type:
+ *
+ * Returns: GType for GntEntry.
*/
-GType gnt_entry_get_gtype(void);
+GType gnt_entry_get_type(void);
/**
- * Create a new GntEntry.
+ * gnt_entry_new:
+ * @text: The text in the new entry box.
*
- * @param text The text in the new entry box.
+ * Create a new GntEntry.
*
- * @return The newly created entry box.
+ * Returns: The newly created entry box.
*/
GntWidget * gnt_entry_new(const char *text);
/**
- * Set the maximum length of the text in the entry box.
+ * gnt_entry_set_max:
+ * @entry: The entry box.
+ * @max: The maximum length for text. A value of 0 means infinite length.
*
- * @param entry The entry box.
- * @param max The maximum length for text. A value of 0 means infinite length.
+ * Set the maximum length of the text in the entry box.
*/
void gnt_entry_set_max(GntEntry *entry, int max);
/**
- * Set the text in an entry box.
+ * gnt_entry_set_text:
+ * @entry: The entry box.
+ * @text: The text to set in the box.
*
- * @param entry The entry box.
- * @param text The text to set in the box.
+ * Set the text in an entry box.
*/
void gnt_entry_set_text(GntEntry *entry, const char *text);
/**
- * Set flags an entry box.
+ * gnt_entry_set_flag:
+ * @entry: The entry box.
+ * @flag: The flags to set for the entry box.
*
- * @param entry The entry box.
- * @param flag The flags to set for the entry box.
+ * Set flags an entry box.
*/
void gnt_entry_set_flag(GntEntry *entry, GntEntryFlag flag);
/**
- * Get the text in an entry box.
+ * gnt_entry_get_text:
+ * @entry: The entry box.
*
- * @param entry The entry box.
+ * Get the text in an entry box.
*
- * @return The current text in the entry box.
+ * Returns: The current text in the entry box.
*/
const char *gnt_entry_get_text(GntEntry *entry);
/**
- * Clear the text in the entry box.
+ * gnt_entry_clear:
+ * @entry: The entry box.
*
- * @param entry The entry box.
+ * Clear the text in the entry box.
*/
void gnt_entry_clear(GntEntry *entry);
/**
- * Set whether the text in the entry box should be masked for display.
+ * gnt_entry_set_masked:
+ * @entry: The entry box.
+ * @set: %TRUE if the text should be masked, %FALSE otherwise.
*
- * @param entry The entry box.
- * @param set @c TRUE if the text should be masked, @c FALSE otherwise.
+ * Set whether the text in the entry box should be masked for display.
*/
void gnt_entry_set_masked(GntEntry *entry, gboolean set);
/**
+ * gnt_entry_add_to_history:
+ * @entry: The entry box.
+ * @text: A new entry for the history list.
+ *
* Add a text to the history list for the text. The history length for the
* entry box needs to be set first by gnt_entry_set_history_length.
- *
- * @param entry The entry box.
- * @param text A new entry for the history list.
*/
void gnt_entry_add_to_history(GntEntry *entry, const char *text);
/**
- * Set the length of history for the entry box.
+ * gnt_entry_set_history_length:
+ * @entry: The entry box.
+ * @num: The maximum length of the history, -1 for unlimited.
*
- * @param entry The entry box.
- * @param num The maximum length of the history.
+ * Set the length of history for the entry box.
*/
void gnt_entry_set_history_length(GntEntry *entry, int num);
/**
+ * gnt_entry_set_word_suggest:
+ * @entry: The entry box.
+ * @word: %TRUE if the suggestions are for individual words, %FALSE otherwise.
+ *
* Set whether the suggestions are for the entire entry box, or for each
* individual word in the entry box.
- *
- * @param entry The entry box.
- * @param word @c TRUE if the suggestions are for individual words, @c FALSE otherwise.
*/
void gnt_entry_set_word_suggest(GntEntry *entry, gboolean word);
/**
+ * gnt_entry_set_always_suggest:
+ * @entry: The entry box.
+ * @always: %TRUE if the suggestion list should always be displayed.
+ *
* Set whether to always display the suggestions list, or only when the
* tab-completion key is pressed (the TAB key, by default).
- *
- * @param entry The entry box.
- * @param always @c TRUE if the suggestion list should always be displayed.
*/
void gnt_entry_set_always_suggest(GntEntry *entry, gboolean always);
/**
- * Add an item to the suggestion list.
+ * gnt_entry_add_suggest:
+ * @entry: The entry box.
+ * @text: An item to add to the suggestion list.
*
- * @param entry The entry box.
- * @param text An item to add to the suggestion list.
+ * Add an item to the suggestion list.
*/
void gnt_entry_add_suggest(GntEntry *entry, const char *text);
/**
- * Remove an entry from the suggestion list.
+ * gnt_entry_remove_suggest:
+ * @entry: The entry box.
+ * @text: The item to remove from the suggestion list.
*
- * @param entry The entry box.
- * @param text The item to remove from the suggestion list.
+ * Remove an entry from the suggestion list.
*/
void gnt_entry_remove_suggest(GntEntry *entry, const char *text);
diff --git a/finch/libgnt/gntfilesel.c b/finch/libgnt/gntfilesel.c
index b597bc5a6c..f0ec793dd5 100644
--- a/finch/libgnt/gntfilesel.c
+++ b/finch/libgnt/gntfilesel.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -37,6 +37,8 @@
#include <sys/stat.h>
#include <unistd.h>
+#include <glib/gstdio.h>
+
#if 0
#include <glob.h>
#endif
@@ -66,106 +68,6 @@ gnt_file_sel_destroy(GntWidget *widget)
}
}
-#if !GLIB_CHECK_VERSION(2,8,0)
-/* ripped from glib/gfileutils.c */
-static gchar *
-g_build_path_va (const gchar *separator,
- gchar **str_array)
-{
- GString *result;
- gint separator_len = strlen (separator);
- gboolean is_first = TRUE;
- gboolean have_leading = FALSE;
- const gchar *single_element = NULL;
- const gchar *next_element;
- const gchar *last_trailing = NULL;
- gint i = 0;
-
- result = g_string_new (NULL);
-
- next_element = str_array[i++];
-
- while (TRUE) {
- const gchar *element;
- const gchar *start;
- const gchar *end;
-
- if (next_element) {
- element = next_element;
- next_element = str_array[i++];
- } else
- break;
-
- /* Ignore empty elements */
- if (!*element)
- continue;
-
- start = element;
-
- if (separator_len) {
- while (start &&
- strncmp (start, separator, separator_len) == 0)
- start += separator_len;
- }
-
- end = start + strlen (start);
-
- if (separator_len) {
- while (end >= start + separator_len &&
- strncmp (end - separator_len, separator, separator_len) == 0)
- end -= separator_len;
-
- last_trailing = end;
- while (last_trailing >= element + separator_len &&
- strncmp (last_trailing - separator_len, separator, separator_len) == 0)
- last_trailing -= separator_len;
-
- if (!have_leading) {
- /* If the leading and trailing separator strings are in the
- * same element and overlap, the result is exactly that element
- */
- if (last_trailing <= start)
- single_element = element;
-
- g_string_append_len (result, element, start - element);
- have_leading = TRUE;
- } else
- single_element = NULL;
- }
-
- if (end == start)
- continue;
-
- if (!is_first)
- g_string_append (result, separator);
-
- g_string_append_len (result, start, end - start);
- is_first = FALSE;
- }
-
- if (single_element) {
- g_string_free (result, TRUE);
- return g_strdup (single_element);
- } else {
- if (last_trailing)
- g_string_append (result, last_trailing);
-
- return g_string_free (result, FALSE);
- }
-}
-
-static gchar *
-g_build_pathv (const gchar *separator,
- gchar **args)
-{
- if (!args)
- return NULL;
-
- return g_build_path_va (separator, args);
-}
-
-#endif
-
static char *
process_path(const char *path)
{
@@ -261,7 +163,7 @@ local_read_fn(const char *path, GList **files, GError **error)
char *fp = g_build_filename(path, str, NULL);
struct stat st;
- if (stat(fp, &st)) {
+ if (g_stat(fp, &st)) {
gnt_warning("Error stating location %s", fp);
} else {
if (S_ISDIR(st.st_mode)) {
@@ -282,6 +184,8 @@ local_read_fn(const char *path, GList **files, GError **error)
static void
gnt_file_free(GntFile *file)
{
+ g_return_if_fail(file != NULL);
+
g_free(file->fullpath);
g_free(file->basename);
g_free(file);
@@ -381,7 +285,7 @@ location_key_pressed(GntTree *tree, const char *key, GntFileSel *sel)
#if 0
int count;
glob_t gl;
- struct stat st;
+ GStatBuf st;
int glob_ret;
#endif
if (strcmp(key, "\r") && strcmp(key, "\n"))
@@ -413,7 +317,7 @@ location_key_pressed(GntTree *tree, const char *key, GntFileSel *sel)
if (!glob_ret) { /* XXX: do something with the return value */
char *loc = g_path_get_dirname(gl.gl_pathv[0]);
- stat(gl.gl_pathv[0], &st);
+ g_stat(gl.gl_pathv[0], &st);
gnt_file_sel_set_current_location(sel, loc); /* XXX: check the return value */
g_free(loc);
if (!S_ISDIR(st.st_mode) && !sel->dirsonly) {
@@ -652,7 +556,7 @@ gnt_file_sel_init(GTypeInstance *instance, gpointer class)
* GntFileSel API
*****************************************************************************/
GType
-gnt_file_sel_get_gtype(void)
+gnt_file_sel_get_type(void)
{
static GType type = 0;
@@ -783,4 +687,35 @@ void gnt_file_sel_set_read_fn(GntFileSel *sel, gboolean (*read_fn)(const char *p
sel->read_fn = read_fn;
}
+/**************************************************************************
+ * GntFile GBoxed API
+ **************************************************************************/
+static GntFile *
+gnt_file_copy(GntFile *file)
+{
+ GntFile *file_new;
+
+ g_return_val_if_fail(file != NULL, NULL);
+
+ file_new = g_new(GntFile, 1);
+ *file_new = *file;
+
+ file_new->fullpath = g_strdup(file->fullpath);
+ file_new->basename = g_strdup(file->basename);
+ return file_new;
+}
+
+GType
+gnt_file_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("GntFile",
+ (GBoxedCopyFunc)gnt_file_copy,
+ (GBoxedFreeFunc)gnt_file_free);
+ }
+
+ return type;
+}
diff --git a/finch/libgnt/gntfilesel.h b/finch/libgnt/gntfilesel.h
index bb6b1d6cf9..4728d54e82 100644
--- a/finch/libgnt/gntfilesel.h
+++ b/finch/libgnt/gntfilesel.h
@@ -1,7 +1,3 @@
-/**
- * @file gntfilesel.h File selector API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,13 +22,19 @@
#ifndef GNT_FILE_SEL_H
#define GNT_FILE_SEL_H
+/**
+ * SECTION:gntfilesel
+ * @section_id: libgnt-gntfilesel
+ * @short_description: <filename>gntfilesel.h</filename>
+ * @title: File Selector
+ */
#include "gntwindow.h"
#include "gnt.h"
#include "gntcolors.h"
#include "gntkeys.h"
-#define GNT_TYPE_FILE_SEL (gnt_file_sel_get_gtype())
+#define GNT_TYPE_FILE_SEL (gnt_file_sel_get_type())
#define GNT_FILE_SEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_FILE_SEL, GntFileSel))
#define GNT_FILE_SEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_FILE_SEL, GntFileSelClass))
#define GNT_IS_FILE_SEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_FILE_SEL))
@@ -43,6 +45,8 @@
#define GNT_FILE_SEL_SET_FLAGS(obj, flags) (GNT_FILE_SEL_FLAGS(obj) |= flags)
#define GNT_FILE_SEL_UNSET_FLAGS(obj, flags) (GNT_FILE_SEL_FLAGS(obj) &= ~(flags))
+#define GNT_TYPE_FILE (gnt_file_get_type())
+
typedef struct _GntFileSel GntFileSel;
typedef struct _GntFileSelPriv GntFileSelPriv;
typedef struct _GntFileSelClass GntFileSelClass;
@@ -75,13 +79,15 @@ struct _GntFileSelClass
GntWindowClass parent;
void (*file_selected)(GntFileSel *sel, const char *path, const char *filename);
+
+ /*< private >*/
void (*gnt_reserved1)(void);
void (*gnt_reserved2)(void);
void (*gnt_reserved3)(void);
void (*gnt_reserved4)(void);
};
-typedef enum _GntFileType
+typedef enum
{
GNT_FILE_REGULAR,
GNT_FILE_DIR
@@ -98,123 +104,147 @@ struct _GntFile
G_BEGIN_DECLS
/**
- * @return GType for GntFileSel.
+ * gnt_file_sel_get_type:
+ *
+ * Returns: GType for GntFileSel.
*/
-GType gnt_file_sel_get_gtype(void);
+GType gnt_file_sel_get_type(void);
/**
+ * gnt_file_get_type:
+ *
+ * Returns: The #GType for the #GntFile boxed structure.
+ */
+GType gnt_file_get_type(void);
+
+/**
+ * gnt_file_sel_new:
+ *
* Create a new file selector.
*
- * @return The newly created file selector.
+ * Returns: The newly created file selector.
*/
GntWidget * gnt_file_sel_new(void);
/**
- * Set the current location of the file selector.
+ * gnt_file_sel_set_current_location:
+ * @sel: The file selector.
+ * @path: The current path of the selector.
*
- * @param sel The file selector.
- * @param path The current path of the selector.
+ * Set the current location of the file selector.
*
- * @return @c TRUE if the current location was successfully changed, @c FALSE otherwise.
+ * Returns: %TRUE if the current location was successfully changed, %FALSE otherwise.
*/
gboolean gnt_file_sel_set_current_location(GntFileSel *sel, const char *path);
/**
- * Set wheter to only allow selecting directories.
- *
- * @param sel The file selector.
- * @param dirs @c TRUE if only directories can be selected, @c FALSE if files
+ * gnt_file_sel_set_dirs_only:
+ * @sel: The file selector.
+ * @dirs: %TRUE if only directories can be selected, %FALSE if files
* can also be selected.
+ *
+ * Set wheter to only allow selecting directories.
*/
void gnt_file_sel_set_dirs_only(GntFileSel *sel, gboolean dirs);
/**
- * Check whether the file selector allows only selecting directories.
+ * gnt_file_sel_get_dirs_only:
+ * @sel: The file selector.
*
- * @param sel The file selector.
+ * Check whether the file selector allows only selecting directories.
*
- * @return @c TRUE if only directories can be selected.
+ * Returns: %TRUE if only directories can be selected.
*/
gboolean gnt_file_sel_get_dirs_only(GntFileSel *sel);
/**
- * Set whether a selected file must exist.
+ * gnt_file_sel_set_must_exist:
+ * @sel: The file selector.
+ * @must: %TRUE if the selected file must exist.
*
- * @param sel The file selector.
- * @param must @c TRUE if the selected file must exist.
+ * Set whether a selected file must exist.
*/
void gnt_file_sel_set_must_exist(GntFileSel *sel, gboolean must);
/**
- * Check whether the selector allows selecting non-existent files.
+ * gnt_file_sel_get_must_exist:
+ * @sel: The file selector.
*
- * @param sel The file selector.
+ * Check whether the selector allows selecting non-existent files.
*
- * @return @c TRUE if the selected file must exist, @c FALSE if a non-existent
+ * Returns: %TRUE if the selected file must exist, %FALSE if a non-existent
* file can be selected.
*/
gboolean gnt_file_sel_get_must_exist(GntFileSel *sel);
/**
- * Get the selected file in the selector.
+ * gnt_file_sel_get_selected_file:
+ * @sel: The file selector.
*
- * @param sel The file selector.
+ * Get the selected file in the selector.
*
- * @return The path of the selected file. The caller should g_free the returned
+ * Returns: The path of the selected file. The caller should g_free the returned
* string.
*/
char * gnt_file_sel_get_selected_file(GntFileSel *sel);
/**
- * Get the list of selected files in the selector.
+ * gnt_file_sel_get_selected_multi_files:
+ * @sel: The file selector.
*
- * @param sel The file selector.
+ * Get the list of selected files in the selector.
*
- * @return A list of paths for the selected files. The caller must g_free the
- * contents of the list, and g_list_free the list.
+ * Returns: (transfer full) (element-type filename): A list of paths for the
+ * selected files. The caller must g_free() the contents of the list,
+ * and g_list_free() the list.
*/
GList * gnt_file_sel_get_selected_multi_files(GntFileSel *sel);
/**
- * Allow selecting multiple files.
+ * gnt_file_sel_set_multi_select:
+ * @sel: The file selector.
+ * @set: %TRUE if selecting multiple files should be allowed.
*
- * @param sel The file selector.
- * @param set @c TRUE if selecting multiple files should be allowed.
+ * Allow selecting multiple files.
*/
void gnt_file_sel_set_multi_select(GntFileSel *sel, gboolean set);
/**
- * Set the suggested file to have selected at startup.
+ * gnt_file_sel_set_suggested_filename:
+ * @sel: The file selector.
+ * @suggest: The suggested filename.
*
- * @param sel The file selector.
- * @param suggest The suggested filename.
+ * Set the suggested file to have selected at startup.
*/
void gnt_file_sel_set_suggested_filename(GntFileSel *sel, const char *suggest);
/**
- * Set custom functions to read the names of files.
+ * gnt_file_sel_set_read_fn:
+ * @sel: The file selector.
+ * @read_fn: The custom read function.
*
- * @param sel The file selector.
- * @param read_fn The custom read function.
+ * Set custom functions to read the names of files.
*/
void gnt_file_sel_set_read_fn(GntFileSel *sel, gboolean (*read_fn)(const char *path, GList **files, GError **error));
/**
- * Create a new GntFile.
+ * gnt_file_new:
+ * @name: The name of the file.
+ * @size: The size of the file.
*
- * @param name The name of the file.
- * @param size The size of the file.
+ * Create a new GntFile.
*
- * @return The newly created GntFile.
+ * Returns: The newly created GntFile.
*/
GntFile* gnt_file_new(const char *name, unsigned long size);
/**
- * Create a new GntFile for a directory.
+ * gnt_file_new_dir:
+ * @name: The name of the directory.
*
- * @param name The name of the directory.
+ * Create a new GntFile for a directory.
*
- * @return The newly created GntFile.
+ * Returns: The newly created GntFile.
*/
GntFile* gnt_file_new_dir(const char *name);
diff --git a/finch/libgnt/gntinternal.h b/finch/libgnt/gntinternal.h
index 79c8836346..7f65a65d47 100644
--- a/finch/libgnt/gntinternal.h
+++ b/finch/libgnt/gntinternal.h
@@ -19,7 +19,9 @@
* 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 "gnt.h"
+
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "Gnt"
@@ -42,4 +44,4 @@
extern int gnt_need_conversation_to_locale;
extern const char *C_(const char *x);
-
+const gchar *gnt_get_config_dir(void);
diff --git a/finch/libgnt/gntkeys.c b/finch/libgnt/gntkeys.c
index 01523f8f0b..c2607f8550 100644
--- a/finch/libgnt/gntkeys.c
+++ b/finch/libgnt/gntkeys.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -51,6 +51,12 @@ void gnt_init_keys()
term = ""; /* Just in case */
}
+#ifdef _WIN32
+ gnt_key_cup = GNT_KEY_CTRL_UP;
+ gnt_key_cdown = GNT_KEY_CTRL_DOWN;
+ gnt_key_cright = GNT_KEY_CTRL_RIGHT;
+ gnt_key_cleft = GNT_KEY_CTRL_LEFT;
+#else
if (strstr(term, "xterm") == term || strcmp(term, "rxvt") == 0) {
gnt_key_cup = "\033" "[1;5A";
gnt_key_cdown = "\033" "[1;5B";
@@ -62,6 +68,7 @@ void gnt_init_keys()
gnt_key_cright = "\033" "Oc";
gnt_key_cleft = "\033" "Od";
}
+#endif
specials = g_hash_table_new(g_str_hash, g_str_equal);
@@ -141,7 +148,7 @@ void gnt_init_keys()
}
}
c = 0;
- for (a = 0; alts[a]; a++) {
+ for (a = 0; alts[a]; a++) { /* XXX: is that loop necessary? */
/* Upper-case alphabets */
for (ch = 0; ch < 26; ch++) {
char str[2] = {'A' + ch, 0}, code[] = {'\033', 'A' + ch, 0};
@@ -201,7 +208,7 @@ const char *gnt_key_lookup(const char *key)
return k.name;
}
-/**
+/*
* The key-bindings will be saved in a tree. When a keystroke happens, GNT will
* find the sequence that matches a binding and return the length.
* A sequence should not be a prefix of another sequence. If it is, then only
diff --git a/finch/libgnt/gntkeys.h b/finch/libgnt/gntkeys.h
index 1a63c1a663..855cd879b9 100644
--- a/finch/libgnt/gntkeys.h
+++ b/finch/libgnt/gntkeys.h
@@ -1,7 +1,3 @@
-/**
- * @file gntkeys.h Keys API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,11 +22,16 @@
#ifndef GNT_KEYS_H
#define GNT_KEYS_H
+/**
+ * SECTION:gntkeys
+ * @section_id: libgnt-gntkeys
+ * @short_description: <filename>gntkeys.h</filename>
+ * @title: Keys API
+ */
-#include <curses.h>
#include <term.h>
-/**
+/*
* terminfo/termcap doesn't provide all the information that I want to use, eg.
* ctrl-up, ctrl-down etc. So I am going to hard-code some of the information
* for some popular $TERMs
@@ -40,7 +41,50 @@ extern char *gnt_key_cdown;
extern char *gnt_key_cleft;
extern char *gnt_key_cright;
-#define SAFE(x) ((x) ? (x) : "")
+#define SAFE(x) ((cur_term && (x)) ? (x) : "")
+
+#ifdef _WIN32
+
+/* XXX: \xe1 is a hacky alias for \x00 key code */
+
+#define GNT_KEY_POPUP "" /* not supported? */
+
+#define GNT_KEY_UP "\033\xe0\x48"
+#define GNT_KEY_DOWN "\033\xe0\x50"
+#define GNT_KEY_LEFT "\033\xe0\x4B"
+#define GNT_KEY_RIGHT "\033\xe0\x4D"
+
+#define GNT_KEY_CTRL_UP "\033\xe0\x8d"
+#define GNT_KEY_CTRL_DOWN "\033\xe0\x91"
+#define GNT_KEY_CTRL_LEFT "\033\xe0\x73"
+#define GNT_KEY_CTRL_RIGHT "\033\xe0\x74"
+
+#define GNT_KEY_PGUP "\033\xe0\x49"
+#define GNT_KEY_PGDOWN "\033\xe0\x51"
+#define GNT_KEY_HOME "\033\xe0\x47"
+#define GNT_KEY_END "\033\xe0\x4f"
+
+#define GNT_KEY_ENTER "\x0d"
+
+#define GNT_KEY_BACKSPACE "\x08"
+#define GNT_KEY_DEL "\033\xe0\x53"
+#define GNT_KEY_INS "\033\xe0\x52"
+#define GNT_KEY_BACK_TAB "\033\xe1\x94"
+
+#define GNT_KEY_F1 "\033\xe1\x3b"
+#define GNT_KEY_F2 "\033\xe1\x3c"
+#define GNT_KEY_F3 "\033\xe1\x3d"
+#define GNT_KEY_F4 "\033\xe1\x3e"
+#define GNT_KEY_F5 "\033\xe1\x3f"
+#define GNT_KEY_F6 "\033\xe1\x40"
+#define GNT_KEY_F7 "\033\xe1\x41"
+#define GNT_KEY_F8 "\033\xe1\x42"
+#define GNT_KEY_F9 "\033\xe1\x43"
+#define GNT_KEY_F10 "\033\xe1\x44"
+#define GNT_KEY_F11 "\033\xe0\x85"
+#define GNT_KEY_F12 "\033\xe0\x86"
+
+#else
#define GNT_KEY_POPUP SAFE(key_f16) /* Apparently */
@@ -60,12 +104,27 @@ extern char *gnt_key_cright;
#define GNT_KEY_HOME SAFE(key_home)
#define GNT_KEY_END SAFE(key_end)
-#define GNT_KEY_ENTER carriage_return
+#define GNT_KEY_ENTER SAFE(carriage_return)
#define GNT_KEY_BACKSPACE SAFE(key_backspace)
#define GNT_KEY_DEL SAFE(key_dc)
#define GNT_KEY_INS SAFE(key_ic)
-#define GNT_KEY_BACK_TAB (back_tab ? back_tab : SAFE(key_btab))
+#define GNT_KEY_BACK_TAB ((cur_term && back_tab) ? back_tab : SAFE(key_btab))
+
+#define GNT_KEY_F1 SAFE(key_f1)
+#define GNT_KEY_F2 SAFE(key_f2)
+#define GNT_KEY_F3 SAFE(key_f3)
+#define GNT_KEY_F4 SAFE(key_f4)
+#define GNT_KEY_F5 SAFE(key_f5)
+#define GNT_KEY_F6 SAFE(key_f6)
+#define GNT_KEY_F7 SAFE(key_f7)
+#define GNT_KEY_F8 SAFE(key_f8)
+#define GNT_KEY_F9 SAFE(key_f9)
+#define GNT_KEY_F10 SAFE(key_f10)
+#define GNT_KEY_F11 SAFE(key_f11)
+#define GNT_KEY_F12 SAFE(key_f12)
+
+#endif
#define GNT_KEY_CTRL_A "\001"
#define GNT_KEY_CTRL_B "\002"
@@ -90,70 +149,65 @@ extern char *gnt_key_cright;
#define GNT_KEY_CTRL_X "\030"
#define GNT_KEY_CTRL_Y "\031"
-#define GNT_KEY_F1 SAFE(key_f1)
-#define GNT_KEY_F2 SAFE(key_f2)
-#define GNT_KEY_F3 SAFE(key_f3)
-#define GNT_KEY_F4 SAFE(key_f4)
-#define GNT_KEY_F5 SAFE(key_f5)
-#define GNT_KEY_F6 SAFE(key_f6)
-#define GNT_KEY_F7 SAFE(key_f7)
-#define GNT_KEY_F8 SAFE(key_f8)
-#define GNT_KEY_F9 SAFE(key_f9)
-#define GNT_KEY_F10 SAFE(key_f10)
-#define GNT_KEY_F11 SAFE(key_f11)
-#define GNT_KEY_F12 SAFE(key_f12)
-
/**
+ * gnt_init_keys:
+ *
* Initialize the keys.
*/
void gnt_init_keys(void);
/**
+ * gnt_keys_refine:
+ * @text: The input text to refine.
+ *
* Refine input text. This usually looks at what the terminal claims it is,
* and tries to change the text to work around some oft-broken terminfo entries.
- *
- * @param text The input text to refine.
*/
void gnt_keys_refine(char *text);
/**
- * Translate a user-readable representation of an input to a machine-readable representation.
+ * gnt_key_translate:
+ * @name: The user-readable representation of an input (eg.: c-t)
*
- * @param name The user-readable representation of an input (eg.: c-t)
+ * Translate a user-readable representation of an input to a machine-readable representation.
*
- * @return A machine-readable representation of the input.
+ * Returns: A machine-readable representation of the input.
*/
const char *gnt_key_translate(const char *name);
/**
- * Translate a machine-readable representation of an input to a user-readable representation.
+ * gnt_key_lookup:
+ * @key: The machine-readable representation of an input.
*
- * @param key The machine-readable representation of an input.
+ * Translate a machine-readable representation of an input to a user-readable representation.
*
- * @return A user-readable representation of the input (eg.: c-t).
+ * Returns: A user-readable representation of the input (eg.: c-t).
*/
const char *gnt_key_lookup(const char *key);
/**
- * Add a key combination to the internal key-tree.
+ * gnt_keys_add_combination:
+ * @key: The key to add
*
- * @param key The key to add
+ * Add a key combination to the internal key-tree.
*/
void gnt_keys_add_combination(const char *key);
/**
- * Remove a key combination from the internal key-tree.
+ * gnt_keys_del_combination:
+ * @key: The key to remove.
*
- * @param key The key to remove.
+ * Remove a key combination from the internal key-tree.
*/
void gnt_keys_del_combination(const char *key);
/**
- * Find a combination from the given string.
+ * gnt_keys_find_combination:
+ * @key: The input string.
*
- * @param key The input string.
+ * Find a combination from the given string.
*
- * @return The number of bytes in the combination that starts at the beginning
+ * Returns: The number of bytes in the combination that starts at the beginning
* of key (can be 0).
*/
int gnt_keys_find_combination(const char *key);
diff --git a/finch/libgnt/gntlabel.c b/finch/libgnt/gntlabel.c
index 2d4196991e..cf637e2a24 100644
--- a/finch/libgnt/gntlabel.c
+++ b/finch/libgnt/gntlabel.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -123,7 +123,7 @@ gnt_label_class_init(GntLabelClass *klass)
g_param_spec_string("text", "Text",
"The text for the label.",
NULL,
- G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+ G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS
)
);
@@ -135,7 +135,7 @@ gnt_label_class_init(GntLabelClass *klass)
GNT_TEXT_FLAG_NORMAL|GNT_TEXT_FLAG_BOLD|GNT_TEXT_FLAG_UNDERLINE|
GNT_TEXT_FLAG_BLINK|GNT_TEXT_FLAG_DIM|GNT_TEXT_FLAG_HIGHLIGHT,
GNT_TEXT_FLAG_NORMAL,
- G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+ G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS
)
);
GNTDEBUG;
@@ -157,7 +157,7 @@ gnt_label_init(GTypeInstance *instance, gpointer class)
* GntLabel API
*****************************************************************************/
GType
-gnt_label_get_gtype(void)
+gnt_label_get_type(void)
{
static GType type = 0;
diff --git a/finch/libgnt/gntlabel.h b/finch/libgnt/gntlabel.h
index 83dfbdc58d..d795730f5f 100644
--- a/finch/libgnt/gntlabel.h
+++ b/finch/libgnt/gntlabel.h
@@ -1,7 +1,3 @@
-/**
- * @file gntlabel.h Label API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,12 +22,18 @@
#ifndef GNT_LABEL_H
#define GNT_LABEL_H
+/**
+ * SECTION:gntlabel
+ * @section_id: libgnt-gntlabel
+ * @short_description: <filename>gntlabel.h</filename>
+ * @title: Label
+ */
#include "gnt.h"
#include "gntwidget.h"
#include "gnttextview.h"
-#define GNT_TYPE_LABEL (gnt_label_get_gtype())
+#define GNT_TYPE_LABEL (gnt_label_get_type())
#define GNT_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_LABEL, GntLabel))
#define GNT_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_LABEL, GntLabelClass))
#define GNT_IS_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_LABEL))
@@ -48,16 +50,18 @@ struct _GntLabel
char *text;
GntTextFormatFlags flags;
- void (*gnt_reserved1)(void);
- void (*gnt_reserved2)(void);
- void (*gnt_reserved3)(void);
- void (*gnt_reserved4)(void);
+ /*< private >*/
+ void *res1;
+ void *res2;
+ void *res3;
+ void *res4;
};
struct _GntLabelClass
{
GntWidgetClass parent;
+ /*< private >*/
void (*gnt_reserved1)(void);
void (*gnt_reserved2)(void);
void (*gnt_reserved3)(void);
@@ -67,34 +71,39 @@ struct _GntLabelClass
G_BEGIN_DECLS
/**
- * @return GType for GntLabel.
+ * gnt_label_get_type:
+ *
+ * Returns: GType for GntLabel.
*/
-GType gnt_label_get_gtype(void);
+GType gnt_label_get_type(void);
/**
- * Create a new GntLabel.
+ * gnt_label_new:
+ * @text: The text of the label.
*
- * @param text The text of the label.
+ * Create a new GntLabel.
*
- * @return The newly created label.
+ * Returns: The newly created label.
*/
GntWidget * gnt_label_new(const char *text);
/**
- * Create a new label with specified text attributes.
+ * gnt_label_new_with_format:
+ * @text: The text.
+ * @flags: Text attributes for the text.
*
- * @param text The text.
- * @param flags Text attributes for the text.
+ * Create a new label with specified text attributes.
*
- * @return The newly created label.
+ * Returns: The newly created label.
*/
GntWidget * gnt_label_new_with_format(const char *text, GntTextFormatFlags flags);
/**
- * Change the text of a label.
+ * gnt_label_set_text:
+ * @label: The label.
+ * @text: The new text to set in the label.
*
- * @param label The label.
- * @param text The new text to set in the label.
+ * Change the text of a label.
*/
void gnt_label_set_text(GntLabel *label, const char *text);
diff --git a/finch/libgnt/gntline.c b/finch/libgnt/gntline.c
index 50418fdda1..974da45137 100644
--- a/finch/libgnt/gntline.c
+++ b/finch/libgnt/gntline.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -120,7 +120,7 @@ gnt_line_class_init(GntLineClass *klass)
g_param_spec_boolean("vertical", "Vertical",
"Whether it's a vertical line or a horizontal one.",
TRUE,
- G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+ G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS
)
);
}
@@ -139,7 +139,7 @@ gnt_line_init(GTypeInstance *instance, gpointer class)
* GntLine API
*****************************************************************************/
GType
-gnt_line_get_gtype(void)
+gnt_line_get_type(void)
{
static GType type = 0;
diff --git a/finch/libgnt/gntline.h b/finch/libgnt/gntline.h
index faf8e2c8a7..4fb83f0512 100644
--- a/finch/libgnt/gntline.h
+++ b/finch/libgnt/gntline.h
@@ -1,7 +1,3 @@
-/**
- * @file gntline.h Line API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,13 +22,19 @@
#ifndef GNT_LINE_H
#define GNT_LINE_H
+/**
+ * SECTION:gntline
+ * @section_id: libgnt-gntline
+ * @short_description: <filename>gntline.h</filename>
+ * @title: Line
+ */
#include "gntwidget.h"
#include "gnt.h"
#include "gntcolors.h"
#include "gntkeys.h"
-#define GNT_TYPE_LINE (gnt_line_get_gtype())
+#define GNT_TYPE_LINE (gnt_line_get_type())
#define GNT_LINE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_LINE, GntLine))
#define GNT_LINE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_LINE, GntLineClass))
#define GNT_IS_LINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_LINE))
@@ -58,6 +60,7 @@ struct _GntLineClass
{
GntWidgetClass parent;
+ /*< private >*/
void (*gnt_reserved1)(void);
void (*gnt_reserved2)(void);
void (*gnt_reserved3)(void);
@@ -67,19 +70,22 @@ struct _GntLineClass
G_BEGIN_DECLS
/**
- * @return GType for GntLine.
+ * gnt_line_get_type:
+ *
+ * Returns: GType for GntLine.
*/
-GType gnt_line_get_gtype(void);
+GType gnt_line_get_type(void);
#define gnt_hline_new() gnt_line_new(FALSE)
#define gnt_vline_new() gnt_line_new(TRUE)
/**
- * Create new line
+ * gnt_line_new:
+ * @vertical: %TRUE if the line should be vertical, %FALSE for a horizontal line.
*
- * @param vertical @c TRUE if the line should be vertical, @c FALSE for a horizontal line.
+ * Create new line
*
- * @return The newly created line.
+ * Returns: The newly created line.
*/
GntWidget * gnt_line_new(gboolean vertical);
diff --git a/finch/libgnt/gntmain.c b/finch/libgnt/gntmain.c
index 71548df9b5..4ad548e92d 100644
--- a/finch/libgnt/gntmain.c
+++ b/finch/libgnt/gntmain.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -30,7 +30,9 @@
#include <gmodule.h>
#include <sys/types.h>
+#ifndef _WIN32
#include <sys/wait.h>
+#endif
#include "gntinternal.h"
#undef GNT_LOG_DOMAIN
@@ -61,11 +63,18 @@
#include <ctype.h>
#include <errno.h>
-/**
+#ifdef _WIN32
+#undef _getch
+#undef getch
+#include <windows.h>
+#include <conio.h>
+#endif
+
+/*
* Notes: Interesting functions to look at:
- * scr_dump, scr_init, scr_restore: for workspaces
+ * scr_dump, scr_init, scr_restore: for workspaces
*
- * Need to wattrset for colors to use with PDCurses.
+ * Need to wattrset for colors to use with PDCurses.
*/
static GIOChannel *channel = NULL;
@@ -84,6 +93,8 @@ static GntClipboard *clipboard;
int gnt_need_conversation_to_locale;
+static gchar *custom_config_dir = NULL;
+
#define HOLDING_ESCAPE (escape_stuff.timer != 0)
static struct {
@@ -98,8 +109,31 @@ escape_timeout(gpointer data)
return FALSE;
}
+void
+gnt_set_config_dir(const gchar *config_dir)
+{
+ if (channel) {
+ gnt_warning("gnt_set_config_dir failed: %s",
+ "gnt already initialized");
+ }
+ free(custom_config_dir);
+ custom_config_dir = g_strdup(config_dir);
+}
+
+const gchar *
+gnt_get_config_dir(void)
+{
+ if (custom_config_dir)
+ return custom_config_dir;
+ return g_get_home_dir();
+}
+
+#ifndef _WIN32
/**
+ * detect_mouse_action:
+ *
* Mouse support:
+ *
* - bring a window on top if you click on its taskbar
* - click on the top-bar of the active window and drag+drop to move a window
* - click on a window to bring it to focus
@@ -211,22 +245,118 @@ detect_mouse_action(const char *buffer)
gnt_widget_clicked(widget, event, x, y);
return TRUE;
}
+#endif
static gboolean
io_invoke_error(GIOChannel *source, GIOCondition cond, gpointer data)
{
+ /* XXX: it throws an error after evey io_invoke, I have no idea why */
+#ifndef _WIN32
int id = GPOINTER_TO_INT(data);
+
g_source_remove(id);
g_io_channel_unref(source);
channel = NULL;
setup_io();
+#endif
+
return TRUE;
}
+
static gboolean
io_invoke(GIOChannel *source, GIOCondition cond, gpointer null)
{
+#ifdef _WIN32
+ /* We need:
+ * - 1 for escape prefix
+ * - 6 for gunichar-to-gchar conversion (see g_unichar_to_utf8)
+ * - 1 for the terminating NUL
+ * or:
+ * - 1 for escape prefix
+ * - 1 for special key prefix
+ * - 1 for the key
+ * - 1 for the terminating NUL
+ */
+ gchar keys[8];
+ gchar *k = keys;
+ int ch;
+ gboolean is_special = FALSE;
+ gboolean is_escape = FALSE;
+
+ if (wm->mode == GNT_KP_MODE_WAIT_ON_CHILD)
+ return FALSE;
+
+ if (HOLDING_ESCAPE) {
+ is_escape = TRUE;
+ g_source_remove(escape_stuff.timer);
+ escape_stuff.timer = 0;
+ } else if (GetAsyncKeyState(VK_LMENU)) { /* left-ALT key */
+ is_escape = TRUE;
+ }
+ if (is_escape) {
+ *k = '\033';
+ k++;
+ }
+
+ ch = _getwch(); /* we could use _getwch_nolock */
+
+ /* a small hack - we don't want to put NUL anywhere */
+ if (ch == 0x00)
+ ch = 0xE1;
+
+ if (ch == 0xE0 || ch == 0xE1) {
+ is_special = TRUE;
+ if (!is_escape) {
+ *k = '\033';
+ k++;
+ }
+ *k = ch;
+ k++;
+ ch = _getwch();
+ }
+
+ if (ch == 0x1B && !is_special) { /* ESC key */
+ escape_stuff.timer = g_timeout_add(250, escape_timeout, NULL);
+ return TRUE;
+ }
+
+ if (wm)
+ gnt_wm_set_event_stack(wm, TRUE);
+
+ if (is_special) {
+ if (ch > 0xFF) {
+ gnt_warning("a special key out of gchar range (%d)", ch);
+ return TRUE;
+ }
+ *k = ch;
+ k++;
+ } else {
+ gint result_len;
+
+ result_len = g_unichar_to_utf8(ch, k);
+ k += result_len;
+ }
+ *k = '\0';
+
+#if 0
+ gnt_warning("a key: [%s] %#x %#x %#x %#x %#x %#x", keys,
+ (guchar)keys[0], (guchar)keys[1], (guchar)keys[2],
+ (guchar)keys[3], (guchar)keys[4], (guchar)keys[5]);
+#endif
+
+ /* TODO: we could call detect_mouse_action here, but no
+ * events are triggered (yet?) for mouse on win32.
+ */
+
+ gnt_wm_process_input(wm, keys);
+
+ if (wm)
+ gnt_wm_set_event_stack(wm, FALSE);
+
+ return TRUE;
+#else
char keys[256];
gssize rd;
char *k;
@@ -299,13 +429,25 @@ end:
gnt_wm_set_event_stack(wm, FALSE);
g_free(cvrt);
return TRUE;
+#endif
}
static void
setup_io()
{
int result;
+
+#ifdef _WIN32
+ channel = g_io_channel_win32_new_fd(STDIN_FILENO);
+#else
channel = g_io_channel_unix_new(STDIN_FILENO);
+#endif
+
+ if (channel == NULL) {
+ gnt_warning("failed creating new channel%s", "");
+ return;
+ }
+
g_io_channel_set_close_on_unref(channel, TRUE);
#if 0
@@ -322,11 +464,11 @@ setup_io()
(G_IO_NVAL),
io_invoke_error, GINT_TO_POINTER(result), NULL);
- g_io_channel_unref(channel); /* Apparently this caused crashes for some people.
- But irssi does this, so I am going to assume the
- crashes were caused by some other stuff. */
+ g_io_channel_unref(channel);
+#if 0
gnt_warning("setting up IO (%d)", channel_read_callback);
+#endif
}
static gboolean
@@ -336,6 +478,7 @@ refresh_screen(void)
return FALSE;
}
+#ifndef _WIN32
/* Xerox */
static void
clean_pid(void)
@@ -353,6 +496,7 @@ clean_pid(void)
perror(errmsg);
}
}
+#endif
static void
exit_confirmed(gpointer null)
@@ -422,10 +566,12 @@ sighandler(int sig)
signal(SIGWINCH, sighandler);
break;
#endif
+#ifndef _WIN32
case SIGCHLD:
clean_pid();
signal(SIGCHLD, sighandler);
break;
+#endif
case SIGINT:
ask_before_exit();
signal(SIGINT, sighandler);
@@ -460,12 +606,19 @@ void gnt_init()
if (channel)
return;
+#ifdef _WIN32
+ /* UTF-8 for input */
+ /* TODO: check it with NO_WIDECHAR. */
+ SetConsoleCP(65001);
+#endif
+
locale = setlocale(LC_ALL, "");
setup_io();
#ifdef NO_WIDECHAR
ascii_only = TRUE;
+ (void)locale;
#else
if (locale && (strstr(locale, "UTF") || strstr(locale, "utf"))) {
ascii_only = FALSE;
@@ -483,7 +636,7 @@ void gnt_init()
gnt_init_keys();
gnt_init_styles();
- filename = g_build_filename(g_get_home_dir(), ".gntrc", NULL);
+ filename = g_build_filename(gnt_get_config_dir(), ".gntrc", NULL);
gnt_style_read_configure_file(filename);
g_free(filename);
@@ -504,9 +657,11 @@ void gnt_init()
#ifdef SIGWINCH
org_winch_handler = signal(SIGWINCH, sighandler);
#endif
+#ifndef _WIN32
signal(SIGCHLD, sighandler);
- signal(SIGINT, sighandler);
signal(SIGPIPE, SIG_IGN);
+#endif
+ signal(SIGINT, sighandler);
#if !GLIB_CHECK_VERSION(2, 36, 0)
/* GLib type system is automaticaly initialized since 2.36. */
@@ -678,7 +833,6 @@ gchar *gnt_get_clipboard_string()
return gnt_clipboard_get_string(clipboard);
}
-#if GLIB_CHECK_VERSION(2,4,0)
typedef struct
{
void (*callback)(int status, gpointer data);
@@ -693,20 +847,20 @@ reap_child(GPid pid, gint status, gpointer data)
cp->callback(status, cp->data);
}
g_free(cp);
+#ifndef _WIN32
clean_pid();
+#endif
wm->mode = GNT_KP_MODE_NORMAL;
endwin();
setup_io();
refresh();
refresh_screen();
}
-#endif
gboolean gnt_giveup_console(const char *wd, char **argv, char **envp,
gint *stin, gint *stout, gint *sterr,
void (*callback)(int status, gpointer data), gpointer data)
{
-#if GLIB_CHECK_VERSION(2,4,0)
GPid pid = 0;
ChildProcess *cp = NULL;
@@ -724,18 +878,11 @@ gboolean gnt_giveup_console(const char *wd, char **argv, char **envp,
g_child_watch_add(pid, reap_child, cp);
return TRUE;
-#else
- return FALSE;
-#endif
}
gboolean gnt_is_refugee()
{
-#if GLIB_CHECK_VERSION(2,4,0)
return (wm && wm->mode == GNT_KP_MODE_WAIT_ON_CHILD);
-#else
- return FALSE;
-#endif
}
const char *C_(const char *x)
diff --git a/finch/libgnt/gntmenu.c b/finch/libgnt/gntmenu.c
index 920ad5ec43..8ec158fb8d 100644
--- a/finch/libgnt/gntmenu.c
+++ b/finch/libgnt/gntmenu.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -79,7 +79,7 @@ gnt_menu_draw(GntWidget *widget)
GntMenu *menu = GNT_MENU(widget);
GList *iter;
chtype type;
- int i;
+ guint i;
if (menu->type == GNT_MENU_TOPLEVEL) {
wbkgdset(widget->window, '\0' | gnt_color_pair(GNT_COLOR_HIGHLIGHT));
@@ -87,7 +87,13 @@ gnt_menu_draw(GntWidget *widget)
for (i = 0, iter = menu->list; iter; iter = iter->next, i++) {
GntMenuItem *item = GNT_MENU_ITEM(iter->data);
- type = ' ' | gnt_color_pair(GNT_COLOR_HIGHLIGHT);
+ if (!gnt_menuitem_is_visible(item))
+ continue;
+ type = ' ';
+ if (item->callback || item->submenu)
+ type |= gnt_color_pair(GNT_COLOR_HIGHLIGHT);
+ else
+ type |= gnt_color_pair(GNT_COLOR_DISABLED);
if (i == menu->selected)
type |= A_REVERSE;
item->priv.x = getcurx(widget->window) + widget->priv.x;
@@ -119,7 +125,9 @@ gnt_menu_size_request(GntWidget *widget)
static void
menu_tree_add(GntMenu *menu, GntMenuItem *item, GntMenuItem *parent)
{
- char trigger[4] = "\0 )\0";
+ char trigger[] = "\0 )\0";
+
+ g_return_if_fail(item != NULL);
if ((trigger[1] = gnt_menuitem_get_trigger(item)) && trigger[1] != ' ')
trigger[0] = '(';
@@ -128,9 +136,12 @@ menu_tree_add(GntMenu *menu, GntMenuItem *item, GntMenuItem *parent)
gnt_tree_add_choice(GNT_TREE(menu), item,
gnt_tree_create_row(GNT_TREE(menu), item->text, trigger, " "), parent, NULL);
gnt_tree_set_choice(GNT_TREE(menu), item, gnt_menuitem_check_get_checked(GNT_MENU_ITEM_CHECK(item)));
- } else
+ } else {
gnt_tree_add_row_last(GNT_TREE(menu), item,
gnt_tree_create_row(GNT_TREE(menu), item->text, trigger, item->submenu ? ">" : " "), parent);
+ if (!item->callback && !item->submenu)
+ gnt_tree_set_row_color(GNT_TREE(menu), item, GNT_COLOR_DISABLED);
+ }
if (0 && item->submenu) {
GntMenu *sub = GNT_MENU(item->submenu);
@@ -277,7 +288,7 @@ static gboolean
gnt_menu_key_pressed(GntWidget *widget, const char *text)
{
GntMenu *menu = GNT_MENU(widget);
- int current = menu->selected;
+ guint current = menu->selected;
if (menu->submenu) {
GntMenu *sub = menu;
@@ -303,14 +314,25 @@ gnt_menu_key_pressed(GntWidget *widget, const char *text)
}
if (menu->type == GNT_MENU_TOPLEVEL) {
+ GntMenuItem *item;
+ GList *it;
if (strcmp(text, GNT_KEY_LEFT) == 0) {
- menu->selected--;
- if (menu->selected < 0)
- menu->selected = g_list_length(menu->list) - 1;
+ do {
+ if (menu->selected == 0)
+ menu->selected = g_list_length(menu->list) - 1;
+ else
+ menu->selected--;
+ it = g_list_nth(menu->list, menu->selected);
+ item = it ? it->data : NULL;
+ } while (!gnt_menuitem_is_visible(item));
} else if (strcmp(text, GNT_KEY_RIGHT) == 0) {
- menu->selected++;
- if (menu->selected >= g_list_length(menu->list))
- menu->selected = 0;
+ do {
+ menu->selected++;
+ if (menu->selected >= g_list_length(menu->list))
+ menu->selected = 0;
+ it = g_list_nth(menu->list, menu->selected);
+ item = it ? it->data : NULL;
+ } while (!gnt_menuitem_is_visible(item));
} else if (strcmp(text, GNT_KEY_ENTER) == 0 ||
strcmp(text, GNT_KEY_DOWN) == 0) {
gnt_widget_activate(widget);
@@ -449,7 +471,7 @@ gnt_menu_init(GTypeInstance *instance, gpointer class)
* GntMenu API
*****************************************************************************/
GType
-gnt_menu_get_gtype(void)
+gnt_menu_get_type(void)
{
static GType type = 0;
diff --git a/finch/libgnt/gntmenu.h b/finch/libgnt/gntmenu.h
index 53b75eead8..5a71377336 100644
--- a/finch/libgnt/gntmenu.h
+++ b/finch/libgnt/gntmenu.h
@@ -1,7 +1,3 @@
-/**
- * @file gntmenu.h Menu API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,12 +22,18 @@
#ifndef GNT_MENU_H
#define GNT_MENU_H
+/**
+ * SECTION:gntmenu
+ * @section_id: libgnt-gntmenu
+ * @short_description: <filename>gntmenu.h</filename>
+ * @title: Menu
+ */
#include "gnttree.h"
#include "gntcolors.h"
#include "gntkeys.h"
-#define GNT_TYPE_MENU (gnt_menu_get_gtype())
+#define GNT_TYPE_MENU (gnt_menu_get_type())
#define GNT_MENU(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_MENU, GntMenu))
#define GNT_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_MENU, GntMenuClass))
#define GNT_IS_MENU(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_MENU))
@@ -49,14 +51,18 @@ typedef struct _GntMenuClass GntMenuClass;
#include "gntmenuitem.h"
/**
+ * GntMenuType:
+ * @GNT_MENU_TOPLEVEL: Menu for a toplevel window
+ * @GNT_MENU_POPUP: A popup menu
+ *
* A toplevel-menu is displayed at the top of the screen, and it spans accross
* the entire width of the screen.
* A popup-menu could be displayed, for example, as a context menu for widgets.
*/
typedef enum
{
- GNT_MENU_TOPLEVEL = 1, /* Menu for a toplevel window */
- GNT_MENU_POPUP, /* A popup menu */
+ GNT_MENU_TOPLEVEL = 1,
+ GNT_MENU_POPUP,
} GntMenuType;
struct _GntMenu
@@ -65,7 +71,7 @@ struct _GntMenu
GntMenuType type;
GList *list;
- int selected;
+ guint selected;
/* This will keep track of its immediate submenu which is visible so that
* keystrokes can be passed to it. */
@@ -77,6 +83,7 @@ struct _GntMenuClass
{
GntTreeClass parent;
+ /*< private >*/
void (*gnt_reserved1)(void);
void (*gnt_reserved2)(void);
void (*gnt_reserved3)(void);
@@ -86,36 +93,41 @@ struct _GntMenuClass
G_BEGIN_DECLS
/**
- * @return The GType for GntMenu.
+ * gnt_menu_get_type:
+ *
+ * Returns: The GType for GntMenu.
*/
-GType gnt_menu_get_gtype(void);
+GType gnt_menu_get_type(void);
/**
- * Create a new menu.
+ * gnt_menu_new:
+ * @type: The type of the menu, whether it's a toplevel menu or a popup menu.
*
- * @param type The type of the menu, whether it's a toplevel menu or a popup menu.
+ * Create a new menu.
*
- * @return The newly created menu.
+ * Returns: The newly created menu.
*/
GntWidget * gnt_menu_new(GntMenuType type);
/**
- * Add an item to the menu.
+ * gnt_menu_add_item:
+ * @menu: The menu.
+ * @item: The item to add to the menu.
*
- * @param menu The menu.
- * @param item The item to add to the menu.
+ * Add an item to the menu.
*/
void gnt_menu_add_item(GntMenu *menu, GntMenuItem *item);
/**
- * Return the GntMenuItem with the given ID.
+ * gnt_menu_get_item:
+ * @menu: The menu.
+ * @id: The ID for an item.
*
- * @param menu The menu.
- * @param id The ID for an item.
+ * Return the GntMenuItem with the given ID.
*
- * @return The menuitem with the given ID, or @c NULL.
+ * Returns: (transfer none): The menuitem with the given ID, or %NULL.
*
- * @since 2.3.0
+ * Since: 2.3.0
*/
GntMenuItem *gnt_menu_get_item(GntMenu *menu, const char *id);
diff --git a/finch/libgnt/gntmenuitem.c b/finch/libgnt/gntmenuitem.c
index 306ef55d52..1c0671b61f 100644
--- a/finch/libgnt/gntmenuitem.c
+++ b/finch/libgnt/gntmenuitem.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -65,13 +65,21 @@ gnt_menuitem_class_init(GntMenuItemClass *klass)
static void
gnt_menuitem_init(GTypeInstance *instance, gpointer klass)
{
+ GntMenuItem *item = GNT_MENU_ITEM(instance);
+#if 0
+ GntMenuItemPriv *priv = &item->priv;
+
+ priv->visible = TRUE;
+#else
+ item->visible = TRUE;
+#endif
}
/******************************************************************************
* GntMenuItem API
*****************************************************************************/
GType
-gnt_menuitem_get_gtype(void)
+gnt_menuitem_get_type(void)
{
static GType type = 0;
@@ -157,3 +165,33 @@ gboolean gnt_menuitem_activate(GntMenuItem *item)
return FALSE;
}
+void
+gnt_menuitem_set_visible(GntMenuItem *item, gboolean visible)
+{
+#if 0
+ GntMenuItemPriv *priv = &item->priv;
+
+ priv->visible = visible;
+#else
+ item->visible = visible;
+#endif
+}
+
+gboolean
+gnt_menuitem_is_visible(GntMenuItem *item)
+{
+ g_return_val_if_fail(GNT_IS_MENU_ITEM(item), FALSE);
+
+#if 0
+ return item->priv.visible;
+#else
+ return item->visible;
+#endif
+}
+
+void
+gnt_menuitem_set_text(GntMenuItem *item, const gchar *text)
+{
+ g_free(item->text);
+ item->text = g_strdup(text);
+}
diff --git a/finch/libgnt/gntmenuitem.h b/finch/libgnt/gntmenuitem.h
index 3517703511..3c7c25525f 100644
--- a/finch/libgnt/gntmenuitem.h
+++ b/finch/libgnt/gntmenuitem.h
@@ -1,7 +1,3 @@
-/**
- * @file gntmenuitem.h Menuitem API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,11 +22,17 @@
#ifndef GNT_MENUITEM_H
#define GNT_MENUITEM_H
+/**
+ * SECTION:gntmenuitem
+ * @section_id: libgnt-gntmenuitem
+ * @short_description: <filename>gntmenuitem.h</filename>
+ * @title: Menuitem
+ */
#include <glib.h>
#include <glib-object.h>
-#define GNT_TYPE_MENU_ITEM (gnt_menuitem_get_gtype())
+#define GNT_TYPE_MENU_ITEM (gnt_menuitem_get_type())
#define GNT_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_MENU_ITEM, GntMenuItem))
#define GNT_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_MENU_ITEM, GntMenuItemClass))
#define GNT_IS_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_MENU_ITEM))
@@ -72,12 +74,16 @@ struct _GntMenuItem
GntMenuItemCallback callback;
GntMenu *submenu;
+
+ /*< private >*/
+ gboolean visible;
};
struct _GntMenuItemClass
{
GObjectClass parent;
+ /*< private >*/
void (*gnt_reserved1)(void);
void (*gnt_reserved2)(void);
void (*gnt_reserved3)(void);
@@ -87,100 +93,148 @@ struct _GntMenuItemClass
G_BEGIN_DECLS
/**
- * @return GType for GntMenuItem.
+ * gnt_menuitem_get_type:
+ *
+ * Returns: GType for GntMenuItem.
*/
-GType gnt_menuitem_get_gtype(void);
+GType gnt_menuitem_get_type(void);
/**
- * Create a new menuitem.
+ * gnt_menuitem_new:
+ * @text: Label for the menuitem.
*
- * @param text Label for the menuitem.
+ * Create a new menuitem.
*
- * @return The newly created menuitem.
+ * Returns: The newly created menuitem.
*/
GntMenuItem * gnt_menuitem_new(const char *text);
/**
- * Set a callback function for a menuitem.
+ * gnt_menuitem_set_callback:
+ * @item: The menuitem.
+ * @callback: (scope call): The callback function.
+ * @data: Data to send to the callback function.
*
- * @param item The menuitem.
- * @param callback The callback function.
- * @param data Data to send to the callback function.
+ * Set a callback function for a menuitem.
*/
void gnt_menuitem_set_callback(GntMenuItem *item, GntMenuItemCallback callback, gpointer data);
/**
- * Set a submenu for a menuitem. A menuitem with a submenu cannot have a callback.
+ * gnt_menuitem_set_submenu:
+ * @item: The menuitem.
+ * @menu: The submenu.
*
- * @param item The menuitem.
- * @param menu The submenu.
+ * Set a submenu for a menuitem. A menuitem with a submenu cannot have a callback.
*/
void gnt_menuitem_set_submenu(GntMenuItem *item, GntMenu *menu);
/**
- * Get the submenu for a menuitem.
+ * gnt_menuitem_get_submenu:
+ * @item: The menuitem.
*
- * @param item The menuitem.
+ * Get the submenu for a menuitem.
*
- * @return The submenu, or @c NULL.
+ * Returns: (transfer none): The submenu, or %NULL.
*
- * @since 2.3.0
+ * Since: 2.3.0
*/
GntMenu *gnt_menuitem_get_submenu(GntMenuItem *item);
/**
- * Set a trigger key for the item.
+ * gnt_menuitem_set_trigger:
+ * @item: The menuitem
+ * @trigger: The key that will trigger the item when the parent manu is visible
*
- * @param item The menuitem
- * @param trigger The key that will trigger the item when the parent manu is visible
+ * Set a trigger key for the item.
*/
void gnt_menuitem_set_trigger(GntMenuItem *item, char trigger);
/**
- * Get the trigger key for a menuitem.
+ * gnt_menuitem_get_trigger:
+ * @item: The menuitem
*
- * @param item The menuitem
+ * Get the trigger key for a menuitem.
*
- * @return The trigger key for the menuitem.
+ * See gnt_menuitem_set_trigger().
*
- * @see gnt_menuitem_set_trigger
+ * Returns: The trigger key for the menuitem.
*/
char gnt_menuitem_get_trigger(GntMenuItem *item);
/**
- * Set an ID for the menuitem.
+ * gnt_menuitem_set_id:
+ * @item: The menuitem.
+ * @id: The ID for the menuitem.
*
- * @param item The menuitem.
- * @param id The ID for the menuitem.
+ * Set an ID for the menuitem.
*
- * @since 2.3.0
+ * Since: 2.3.0
*/
void gnt_menuitem_set_id(GntMenuItem *item, const char *id);
/**
- * Get the ID of the menuitem.
+ * gnt_menuitem_get_id:
+ * @item: The menuitem.
*
- * @param item The menuitem.
+ * Get the ID of the menuitem.
*
- * @return The ID for the menuitem.
+ * Returns: The ID for the menuitem.
*
- * @since 2.3.0
+ * Since: 2.3.0
*/
const char * gnt_menuitem_get_id(GntMenuItem *item);
/**
+ * gnt_menuitem_activate:
+ * @item: The menuitem.
+ *
* Activate a menuitem.
* Activating the menuitem will first trigger the 'activate' signal for the
* menuitem. Then the callback for the menuitem is triggered, if there is one.
*
- * @param item The menuitem.
+ * Returns: Whether the callback for the menuitem was called.
*
- * @return Whether the callback for the menuitem was called.
- *
- * @since 2.3.0
+ * Since: 2.3.0
*/
gboolean gnt_menuitem_activate(GntMenuItem *item);
+/**
+ * gnt_menuitem_set_visible:
+ * @item: The menuitem.
+ * @visible: %TRUE to make @item visible, %FALSE to hide it.
+ *
+ * Sets @item visible or not.
+ *
+ * Since: 2.8.0
+ */
+void
+gnt_menuitem_set_visible(GntMenuItem *item, gboolean visible);
+
+/**
+ * gnt_menuitem_is_visible:
+ * @item: The menuitem.
+ *
+ * Checks, if the @item is visible.
+ *
+ * Returns: %TRUE, if the @item is visible.
+ *
+ * Since: 2.8.0
+ */
+gboolean
+gnt_menuitem_is_visible(GntMenuItem *item);
+
+/**
+ * gnt_menuitem_set_text:
+ * @item: The menuitem.
+ * @text: The new text.
+ *
+ * Changes the text for an @item.
+ *
+ * Since: 2.8.0
+ */
+void
+gnt_menuitem_set_text(GntMenuItem *item, const gchar *text);
+
G_END_DECLS
#endif /* GNT_MENUITEM_H */
diff --git a/finch/libgnt/gntmenuitemcheck.c b/finch/libgnt/gntmenuitemcheck.c
index 7a4dd9f779..7505db0f07 100644
--- a/finch/libgnt/gntmenuitemcheck.c
+++ b/finch/libgnt/gntmenuitemcheck.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -43,7 +43,7 @@ gnt_menuitem_check_init(GTypeInstance *instance, gpointer class)
* GntMenuItemCheck API
*****************************************************************************/
GType
-gnt_menuitem_check_get_gtype(void)
+gnt_menuitem_check_get_type(void)
{
static GType type = 0;
diff --git a/finch/libgnt/gntmenuitemcheck.h b/finch/libgnt/gntmenuitemcheck.h
index 289d123f22..12f39e579c 100644
--- a/finch/libgnt/gntmenuitemcheck.h
+++ b/finch/libgnt/gntmenuitemcheck.h
@@ -1,7 +1,3 @@
-/**
- * @file gntmenuitemcheck.h Check Menuitem API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,13 +22,19 @@
#ifndef GNT_MENU_ITEM_CHECK_H
#define GNT_MENU_ITEM_CHECK_H
+/**
+ * SECTION:gntmenuitemcheck
+ * @section_id: libgnt-gntmenuitemcheck
+ * @short_description: <filename>gntmenuitemcheck.h</filename>
+ * @title: Check Menuitem
+ */
#include "gnt.h"
#include "gntcolors.h"
#include "gntkeys.h"
#include "gntmenuitem.h"
-#define GNT_TYPE_MENU_ITEM_CHECK (gnt_menuitem_check_get_gtype())
+#define GNT_TYPE_MENU_ITEM_CHECK (gnt_menuitem_check_get_type())
#define GNT_MENU_ITEM_CHECK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_MENU_ITEM_CHECK, GntMenuItemCheck))
#define GNT_MENU_ITEM_CHECK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_MENU_ITEM_CHECK, GntMenuItemCheckClass))
#define GNT_IS_MENU_ITEM_CHECK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_MENU_ITEM_CHECK))
@@ -57,6 +59,7 @@ struct _GntMenuItemCheckClass
{
GntMenuItemClass parent;
+ /*< private >*/
void (*gnt_reserved1)(void);
void (*gnt_reserved2)(void);
void (*gnt_reserved3)(void);
@@ -66,33 +69,38 @@ struct _GntMenuItemCheckClass
G_BEGIN_DECLS
/**
- * @return GType for GntMenuItemCheck.
+ * gnt_menuitem_check_get_type:
+ *
+ * Returns: GType for GntMenuItemCheck.
*/
-GType gnt_menuitem_check_get_gtype(void);
+GType gnt_menuitem_check_get_type(void);
/**
- * Create a new menuitem.
+ * gnt_menuitem_check_new:
+ * @text: The text for the menuitem.
*
- * @param text The text for the menuitem.
+ * Create a new menuitem.
*
- * @return The newly created menuitem.
+ * Returns: The newly created menuitem.
*/
GntMenuItem * gnt_menuitem_check_new(const char *text);
/**
- * Check whether the menuitem is checked or not.
+ * gnt_menuitem_check_get_checked:
+ * @item: The menuitem.
*
- * @param item The menuitem.
+ * Check whether the menuitem is checked or not.
*
- * @return @c TRUE if the item is checked, @c FALSE otherwise.
+ * Returns: %TRUE if the item is checked, %FALSE otherwise.
*/
gboolean gnt_menuitem_check_get_checked(GntMenuItemCheck *item);
/**
- * Set whether the menuitem is checked or not.
+ * gnt_menuitem_check_set_checked:
+ * @item: The menuitem.
+ * @set: %TRUE if the item should be checked, %FALSE otherwise.
*
- * @param item The menuitem.
- * @param set @c TRUE if the item should be checked, @c FALSE otherwise.
+ * Set whether the menuitem is checked or not.
*/
void gnt_menuitem_check_set_checked(GntMenuItemCheck *item, gboolean set);
diff --git a/finch/libgnt/gntprogressbar.c b/finch/libgnt/gntprogressbar.c
index c2da8b9b43..4aa265c2ee 100644
--- a/finch/libgnt/gntprogressbar.c
+++ b/finch/libgnt/gntprogressbar.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -36,16 +36,9 @@ typedef struct _GntProgressBarPrivate
struct _GntProgressBar
{
GntWidget parent;
-#if !GLIB_CHECK_VERSION(2,4,0)
- GntProgressBarPrivate priv;
-#endif
};
-#if GLIB_CHECK_VERSION(2,4,0)
#define GNT_PROGRESS_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNT_TYPE_PROGRESS_BAR, GntProgressBarPrivate))
-#else
-#define GNT_PROGRESS_BAR_GET_PRIVATE(o) &(GNT_PROGRESS_BAR(o)->priv)
-#endif
static GntWidgetClass *parent_class = NULL;
@@ -128,9 +121,7 @@ gnt_progress_bar_class_init (gpointer klass, gpointer class_data)
parent_class = GNT_WIDGET_CLASS (klass);
-#if GLIB_CHECK_VERSION(2,4,0)
g_type_class_add_private (g_class, sizeof (GntProgressBarPrivate));
-#endif
parent_class->draw = gnt_progress_bar_draw;
parent_class->size_request = gnt_progress_bar_size_request;
diff --git a/finch/libgnt/gntprogressbar.h b/finch/libgnt/gntprogressbar.h
index fbfd788992..b906a5a115 100644
--- a/finch/libgnt/gntprogressbar.h
+++ b/finch/libgnt/gntprogressbar.h
@@ -1,7 +1,3 @@
-/**
- * @file gntprogressbar.h Progress Bar API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,6 +22,12 @@
#ifndef GNT_PROGRESS_BAR_H
#define GNT_PROGRESS_BAR_H
+/**
+ * SECTION:gntprogressbar
+ * @section_id: libgnt-gntprogressbar
+ * @short_description: <filename>gntprogressbar.h</filename>
+ * @title: Progress Bar
+ */
#include "gnt.h"
#include "gntwidget.h"
@@ -37,7 +39,7 @@
#define GNT_IS_PROGRESS_BAR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GNT_TYPE_PROGRESS_BAR))
#define GNT_PROGRESS_BAR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GNT_TYPE_PROGRESS_BAR, GntProgressBarClass))
-typedef enum _GntProgressBarOrientation
+typedef enum
{
GNT_PROGRESS_LEFT_TO_RIGHT,
GNT_PROGRESS_RIGHT_TO_LEFT,
@@ -46,84 +48,101 @@ typedef enum _GntProgressBarOrientation
} GntProgressBarOrientation;
typedef struct _GntProgressBar GntProgressBar;
+typedef struct _GntProgressBarClass GntProgressBarClass;
-typedef struct _GntProgressBarClass
+struct _GntProgressBarClass
{
GntWidgetClass parent;
+ /*< private >*/
void (*gnt_reserved1)(void);
void (*gnt_reserved2)(void);
void (*gnt_reserved3)(void);
void (*gnt_reserved4)(void);
-} GntProgressBarClass;
+};
G_BEGIN_DECLS
/**
- * Get the GType for GntProgressBar
- * @return The GType for GntProrgressBar
- **/
+ * gnt_progress_bar_get_type:
+ *
+ * Get the #GType for GntProgressBar
+ *
+ * Returns: The GType for GntProrgressBar
+ */
GType
gnt_progress_bar_get_type (void);
/**
+ * gnt_progress_bar_new:
+ *
* Create a new GntProgressBar
- * @return The new GntProgressBar
- **/
+ *
+ * Returns: The new GntProgressBar
+ */
GntWidget *
gnt_progress_bar_new (void);
/**
- * Set the progress for a progress bar
+ * gnt_progress_bar_set_fraction:
+ * @pbar: The GntProgressBar
+ * @fraction: The value between 0 and 1 to display
*
- * @param pbar The GntProgressBar
- * @param fraction The value between 0 and 1 to display
- **/
+ * Set the progress for a progress bar
+ */
void
gnt_progress_bar_set_fraction (GntProgressBar *pbar, gdouble fraction);
/**
- * Set the orientation for a progress bar
+ * gnt_progress_bar_set_orientation:
+ * @pbar: The GntProgressBar
+ * @orientation: The orientation to use
*
- * @param pbar The GntProgressBar
- * @param orientation The orientation to use
- **/
+ * Set the orientation for a progress bar
+ */
void
gnt_progress_bar_set_orientation (GntProgressBar *pbar, GntProgressBarOrientation orientation);
/**
- * Controls whether the progress value is shown
+ * gnt_progress_bar_set_show_progress:
+ * @pbar: The GntProgressBar
+ * @show: A boolean indicating if the value is shown
*
- * @param pbar The GntProgressBar
- * @param show A boolean indicating if the value is shown
- **/
+ * Controls whether the progress value is shown
+ */
void
gnt_progress_bar_set_show_progress (GntProgressBar *pbar, gboolean show);
/**
+ * gnt_progress_bar_get_fraction:
+ * @pbar: The GntProgressBar
+ *
* Get the progress that is displayed
*
- * @param pbar The GntProgressBar
- * @return The progress displayed as a value between 0 and 1
- **/
+ * Returns: The progress displayed as a value between 0 and 1
+ */
gdouble
gnt_progress_bar_get_fraction (GntProgressBar *pbar);
/**
+ * gnt_progress_bar_get_orientation:
+ * @pbar: The GntProgressBar
+ *
* Get the orientation for the progress bar
*
- * @param pbar The GntProgressBar
- * @return The current orientation of the progress bar
- **/
+ * Returns: The current orientation of the progress bar
+ */
GntProgressBarOrientation
gnt_progress_bar_get_orientation (GntProgressBar *pbar);
/**
+ * gnt_progress_bar_get_show_progress:
+ * @pbar: The GntProgressBar
+ *
* Get a boolean describing if the progress value is shown
*
- * @param pbar The GntProgressBar
- * @return A boolean @c true if the progress value is shown, @c false otherwise.
- **/
+ * Returns: %TRUE if the progress value is shown, %FALSE otherwise.
+ */
gboolean
gnt_progress_bar_get_show_progress (GntProgressBar *pbar);
diff --git a/finch/libgnt/gntslider.c b/finch/libgnt/gntslider.c
index 63e5d694bf..15a7dfdf1f 100644
--- a/finch/libgnt/gntslider.c
+++ b/finch/libgnt/gntslider.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -236,7 +236,7 @@ gnt_slider_init(GTypeInstance *instance, gpointer class)
* GntSlider API
*****************************************************************************/
GType
-gnt_slider_get_gtype(void)
+gnt_slider_get_type(void)
{
static GType type = 0;
diff --git a/finch/libgnt/gntslider.h b/finch/libgnt/gntslider.h
index a1448f7165..7ee5bf999e 100644
--- a/finch/libgnt/gntslider.h
+++ b/finch/libgnt/gntslider.h
@@ -1,7 +1,3 @@
-/**
- * @file gntslider.h Slider API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,12 +22,18 @@
#ifndef GNT_SLIDER_H
#define GNT_SLIDER_H
+/**
+ * SECTION:gntslider
+ * @section_id: libgnt-gntslider
+ * @short_description: <filename>gntslider.h</filename>
+ * @title: Slider
+ */
#include "gntwidget.h"
#include "gnt.h"
#include "gntlabel.h"
-#define GNT_TYPE_SLIDER (gnt_slider_get_gtype())
+#define GNT_TYPE_SLIDER (gnt_slider_get_type())
#define GNT_SLIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_SLIDER, GntSlider))
#define GNT_SLIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_SLIDER, GntSliderClass))
#define GNT_IS_SLIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_SLIDER))
@@ -65,6 +67,8 @@ struct _GntSliderClass
GntWidgetClass parent;
void (*changed)(GntSlider *slider, int);
+
+ /*< private >*/
void (*gnt_reserved1)(void);
void (*gnt_reserved2)(void);
void (*gnt_reserved3)(void);
@@ -74,113 +78,122 @@ struct _GntSliderClass
G_BEGIN_DECLS
/**
- * @return The GType for GntSlider
+ * gnt_slider_get_type:
+ *
+ * Returns: The GType for GntSlider
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
-GType gnt_slider_get_gtype(void);
+GType gnt_slider_get_type(void);
#define gnt_hslider_new(max, min) gnt_slider_new(FALSE, max, min)
#define gnt_vslider_new(max, min) gnt_slider_new(TRUE, max, min)
/**
- * Create a new slider.
+ * gnt_slider_new:
+ * @orient: A vertical slider is created if %TRUE, otherwise the slider is horizontal.
+ * @max: The maximum value for the slider
+ * @min: The minimum value for the slider
*
- * @param orient A vertical slider is created if @c TRUE, otherwise the slider is horizontal.
- * @param max The maximum value for the slider
- * @param min The minimum value for the slider
+ * Create a new slider.
*
- * @return The newly created slider
+ * Returns: The newly created slider
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
GntWidget * gnt_slider_new(gboolean orient, int max, int min);
/**
- * Set the range of the slider.
+ * gnt_slider_set_range:
+ * @slider: The slider
+ * @max: The maximum value
+ * @min: The minimum value
*
- * @param slider The slider
- * @param max The maximum value
- * @param min The minimum value
+ * Set the range of the slider.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
void gnt_slider_set_range(GntSlider *slider, int max, int min);
/**
- * Sets the amount of change at each step.
+ * gnt_slider_set_step:
+ * @slider: The slider
+ * @step: The amount for each step
*
- * @param slider The slider
- * @param step The amount for each step
+ * Sets the amount of change at each step.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
void gnt_slider_set_step(GntSlider *slider, int step);
/**
- * Sets the amount of change a small step.
+ * gnt_slider_set_small_step:
+ * @slider: The slider
+ * @step: The amount for a small step (for the slider)
*
- * @param slider The slider
- * @param step The amount for a small step (for the slider)
+ * Sets the amount of change a small step.
*
- * @since 2.2.0
+ * Since: 2.2.0
*/
void gnt_slider_set_small_step(GntSlider *slider, int step);
/**
- * Sets the amount of change a large step.
+ * gnt_slider_set_large_step:
+ * @slider: The slider
+ * @step: The amount for a large step (for the slider)
*
- * @param slider The slider
- * @param step The amount for a large step (for the slider)
+ * Sets the amount of change a large step.
*
- * @since 2.2.0
+ * Since: 2.2.0
*/
void gnt_slider_set_large_step(GntSlider *slider, int step);
/**
- * Advance the slider forward or backward.
- *
- * @param slider The slider
- * @param steps The number of amounts to change, positive to change
+ * gnt_slider_advance_step:
+ * @slider: The slider
+ * @steps: The number of amounts to change, positive to change
* forward, negative to change backward
*
- * @return The value of the slider after the change
+ * Advance the slider forward or backward.
+ *
+ * Returns: The value of the slider after the change
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
int gnt_slider_advance_step(GntSlider *slider, int steps);
/**
- * Set the current value for the slider.
+ * gnt_slider_set_value:
+ * @slider: The slider
+ * @value: The current value
*
- * @param slider The slider
- * @param value The current value
+ * Set the current value for the slider.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
void gnt_slider_set_value(GntSlider *slider, int value);
/**
- * Get the current value for the slider.
- *
- * @param slider The slider
+ * gnt_slider_get_value:
+ * @slider: The slider
*
+ * Get the current value for the slider.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
int gnt_slider_get_value(GntSlider *slider);
/**
- * Update a label with the value of the slider whenever the value changes.
+ * gnt_slider_reflect_label:
+ * @slider: The slider
+ * @label: The label to update
*
- * @param slider The slider
- * @param label The label to update
+ * Update a label with the value of the slider whenever the value changes.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
void gnt_slider_reflect_label(GntSlider *slider, GntLabel *label);
-
G_END_DECLS
#endif /* GNT_SLIDER_H */
diff --git a/finch/libgnt/gntstyle.c b/finch/libgnt/gntstyle.c
index 3aa7964f96..6b1922c994 100644
--- a/finch/libgnt/gntstyle.c
+++ b/finch/libgnt/gntstyle.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -35,9 +35,7 @@
#define MAX_WORKSPACES 99
-#if GLIB_CHECK_VERSION(2,6,0)
static GKeyFile *gkfile;
-#endif
static char * str_styles[GNT_STYLES];
static int int_styles[GNT_STYLES];
@@ -50,23 +48,24 @@ const char *gnt_style_get(GntStyle style)
char *gnt_style_get_from_name(const char *group, const char *key)
{
-#if GLIB_CHECK_VERSION(2,6,0)
- const char *prg = g_get_prgname();
+ const char *prg;
+
+ /* gkfile is NULL when run by gtkdoc-scanobj or g-ir-scanner */
+ if (!gkfile)
+ return NULL;
+
+ prg = g_get_prgname();
if ((group == NULL || *group == '\0') && prg &&
g_key_file_has_group(gkfile, prg))
group = prg;
if (!group)
group = "general";
return g_key_file_get_value(gkfile, group, key, NULL);
-#else
- return NULL;
-#endif
}
int
gnt_style_get_color(char *group, char *key)
{
-#if GLIB_CHECK_VERSION(2,6,0)
int fg = 0, bg = 0;
gsize n;
char **vals;
@@ -79,14 +78,10 @@ gnt_style_get_color(char *group, char *key)
}
g_strfreev(vals);
return ret;
-#else
- return 0;
-#endif
}
char **gnt_style_get_string_list(const char *group, const char *key, gsize *length)
{
-#if GLIB_CHECK_VERSION(2,6,0)
const char *prg = g_get_prgname();
if ((group == NULL || *group == '\0') && prg &&
g_key_file_has_group(gkfile, prg))
@@ -94,9 +89,6 @@ char **gnt_style_get_string_list(const char *group, const char *key, gsize *leng
if (!group)
group = "general";
return g_key_file_get_string_list(gkfile, group, key, length, NULL);
-#else
- return NULL;
-#endif
}
gboolean gnt_style_get_bool(GntStyle style, gboolean def)
@@ -134,7 +126,6 @@ gboolean gnt_style_parse_bool(const char *str)
return def;
}
-#if GLIB_CHECK_VERSION(2,6,0)
static void
refine(char *text)
{
@@ -175,17 +166,15 @@ parse_key(const char *key)
{
return (char *)gnt_key_translate(key);
}
-#endif
void gnt_style_read_workspaces(GntWM *wm)
{
-#if GLIB_CHECK_VERSION(2,6,0)
int i;
gchar *name;
gsize c;
for (i = 1; i < MAX_WORKSPACES; ++i) {
- int j;
+ gsize j;
GntWS *ws;
gchar **titles;
char group[32];
@@ -212,14 +201,17 @@ void gnt_style_read_workspaces(GntWM *wm)
g_strfreev(titles);
}
}
-#endif
}
+
void gnt_style_read_actions(GType type, GntBindableClass *klass)
{
-#if GLIB_CHECK_VERSION(2,6,0)
char *name;
GError *error = NULL;
+ /* gkfile is NULL when run by gtkdoc-scanobj or g-ir-scanner */
+ if (!gkfile)
+ return;
+
name = g_strdup_printf("%s::binding", g_type_name(type));
if (g_key_file_has_group(gkfile, name))
@@ -264,12 +256,10 @@ void gnt_style_read_actions(GType type, GntBindableClass *klass)
g_strfreev(keys);
}
g_free(name);
-#endif
}
gboolean gnt_style_read_menu_accels(const char *name, GHashTable *table)
{
-#if GLIB_CHECK_VERSION(2,6,0)
char *kname;
GError *error = NULL;
gboolean ret = FALSE;
@@ -322,13 +312,10 @@ gboolean gnt_style_read_menu_accels(const char *name, GHashTable *table)
g_free(kname);
return ret;
-#endif
- return FALSE;
}
void gnt_styles_get_keyremaps(GType type, GHashTable *hash)
{
-#if GLIB_CHECK_VERSION(2,6,0)
char *name;
GError *error = NULL;
@@ -373,10 +360,8 @@ void gnt_styles_get_keyremaps(GType type, GHashTable *hash)
}
g_free(name);
-#endif
}
-#if GLIB_CHECK_VERSION(2,6,0)
static void
read_general_style(GKeyFile *kfile)
{
@@ -419,11 +404,9 @@ read_general_style(GKeyFile *kfile)
}
g_strfreev(keys);
}
-#endif
void gnt_style_read_configure_file(const char *filename)
{
-#if GLIB_CHECK_VERSION(2,6,0)
GError *error = NULL;
gkfile = g_key_file_new();
@@ -436,7 +419,6 @@ void gnt_style_read_configure_file(const char *filename)
}
gnt_colors_parse(gkfile);
read_general_style(gkfile);
-#endif
}
void gnt_init_styles()
@@ -458,9 +440,7 @@ void gnt_uninit_styles()
str_styles[i] = NULL;
}
-#if GLIB_CHECK_VERSION(2,6,0)
g_key_file_free(gkfile);
gkfile = NULL;
-#endif
}
diff --git a/finch/libgnt/gntstyle.h b/finch/libgnt/gntstyle.h
index 7116922db3..ad326741a0 100644
--- a/finch/libgnt/gntstyle.h
+++ b/finch/libgnt/gntstyle.h
@@ -1,7 +1,3 @@
-/**
- * @file gntstyle.h Style API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -24,6 +20,15 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+#ifndef GNT_STYLE_H
+#define GNT_STYLE_H
+/**
+ * SECTION:gntstyle
+ * @section_id: libgnt-gntstyle
+ * @short_description: <filename>gntstyle.h</filename>
+ * @title: Style API
+ */
+
#include "gnt.h"
#include "gntwm.h"
@@ -38,115 +43,138 @@ typedef enum
} GntStyle;
/**
- * Read configuration from a file.
+ * gnt_style_read_configure_file:
+ * @filename: The filename to read configuration from.
*
- * @param filename The filename to read configuration from.
+ * Read configuration from a file.
*/
void gnt_style_read_configure_file(const char *filename);
/**
+ * gnt_style_get:
+ * @style: The style.
+ *
* Get the user-setting for a style.
- * @param style The style.
- * @return The user-setting, or @c NULL.
+ *
+ * Returns: The user-setting, or %NULL.
*/
const char *gnt_style_get(GntStyle style);
/**
- * Get the value of a preference in ~/.gntrc.
- *
- * @param group The name of the group in the keyfile. If @c NULL, the prgname
+ * gnt_style_get_from_name:
+ * @group: The name of the group in the keyfile. If %NULL, the prgname
* will be used first, if available. Otherwise, "general" will be used.
- * @param key The key
+ * @key: The key
+ *
+ * Get the value of a preference in ~/.gntrc.
*
- * @return The value of the setting as a string, or @c NULL
+ * Returns: The value of the setting as a string, or %NULL
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
char *gnt_style_get_from_name(const char *group, const char *key);
/**
- * Get the value of a preference in ~/.gntrc.
+ * gnt_style_get_string_list:
+ * @group: The name of the group in the keyfile. If %NULL, the prgname
+ * will be used first, if available. Otherwise, "general" will be used.
+ * @key: The key
+ * @length: Return location for the number of strings returned, or NULL
*
- * @param group The name of the group in the keyfile. If @c NULL, the prgname
- * will be used first, if available. Otherwise, "general" will be used.
- * @param key The key
- * @param length Return location for the number of strings returned, or NULL
+ * Get the value of a preference in ~/.gntrc.
*
- * @return NULL terminated string array. The array should be freed with g_strfreev().
+ * Returns: (transfer full): %NULL terminated string array. The array should be
+ * freed with g_strfreev().
*
- * @since 2.4.0
+ * Since: 2.4.0
*/
char **gnt_style_get_string_list(const char *group, const char *key, gsize *length);
/**
- * Get the value of a color pair in ~/.gntrc.
- *
- * @param group The name of the group in the keyfile. If @c NULL, the prgname
+ * gnt_style_get_color:
+ * @group: The name of the group in the keyfile. If %NULL, the prgname
* will be used first, if available. Otherwise, "general" will be used.
- * @param key The key
+ * @key: The key
+ *
+ * Get the value of a color pair in ~/.gntrc.
*
- * @return The value of the color as an int, or 0 on error.
+ * Returns: The value of the color as an int, or 0 on error.
*
- * @since 2.4.0
+ * Since: 2.4.0
*/
int gnt_style_get_color(char *group, char *key);
/**
+ * gnt_style_parse_bool:
+ * @value: The value of the boolean setting as a string
+ *
* Parse a boolean preference. For example, if 'value' is "false" (ignoring case)
- * or "0", the return value will be @c FALSE, otherwise @c TRUE.
+ * or "0", the return value will be %FALSE, otherwise %TRUE.
*
- * @param value The value of the boolean setting as a string
- * @return The boolean value
+ * Returns: The boolean value
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
gboolean gnt_style_parse_bool(const char *value);
/**
- * Get the boolean value for a user-setting.
- *
- * @param style The style.
- * @param def The default value (i.e, the value if the user didn't define
+ * gnt_style_get_bool:
+ * @style: The style.
+ * @def: The default value (i.e, the value if the user didn't define
* any value)
*
- * @return The value of the setting.
+ * Get the boolean value for a user-setting.
+ *
+ * Returns: The value of the setting.
*/
gboolean gnt_style_get_bool(GntStyle style, gboolean def);
/**
- * @internal
+ * gnt_styles_get_keyremaps:
+ *
+ * Internal function -- do not use.
*/
void gnt_styles_get_keyremaps(GType type, GHashTable *hash);
/**
- * @internal
+ * gnt_style_read_actions:
+ *
+ * Internal function -- do not use.
*/
void gnt_style_read_actions(GType type, GntBindableClass *klass);
/**
- * Read menu-accels from ~/.gntrc
+ * gnt_style_read_menu_accels:
+ * @name: The name of the window.
+ * @table: The hastable to store the accel information.
*
- * @param name The name of the window.
- * @param table The hastable to store the accel information.
+ * Read menu-accels from ~/.gntrc
*
- * @return @c TRUE if some accels were read, @c FALSE otherwise.
+ * Returns: %TRUE if some accels were read, %FALSE otherwise.
*/
gboolean gnt_style_read_menu_accels(const char *name, GHashTable *table);
/**
- * @internal
- * Read workspace information.
+ * gnt_style_read_workspaces:
+ *
+ * Reads workspace information.
+ *
+ * Internal function -- do not use.
*/
void gnt_style_read_workspaces(GntWM *wm);
/**
+ * gnt_init_styles:
+ *
* Initialize style settings.
*/
void gnt_init_styles(void);
/**
+ * gnt_uninit_styles:
+ *
* Uninitialize style settings.
*/
void gnt_uninit_styles(void);
-
+#endif /* GNT_STYLE_H */
diff --git a/finch/libgnt/gnttextview.c b/finch/libgnt/gnttextview.c
index 2cb6fd1460..c9cd80c0ea 100644
--- a/finch/libgnt/gnttextview.c
+++ b/finch/libgnt/gnttextview.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -335,7 +335,13 @@ gnt_text_view_clicked(GntWidget *widget, GntMouseEvent event, int x, int y)
GntTextView *view = GNT_TEXT_VIEW(widget);
if (text_view_contains(view, select_start)) {
GString *clip;
+
+ g_return_val_if_fail(select_start != NULL, TRUE);
+
select_end = gnt_text_view_get_p(view, x - widget->priv.x, y - widget->priv.y);
+
+ g_return_val_if_fail(select_end != NULL, TRUE);
+
if (select_end < select_start) {
gchar *t = select_start;
select_start = select_end;
@@ -470,7 +476,7 @@ gnt_text_view_init(GTypeInstance *instance, gpointer class)
* GntTextView API
*****************************************************************************/
GType
-gnt_text_view_get_gtype(void)
+gnt_text_view_get_type(void)
{
static GType type = 0;
@@ -513,7 +519,7 @@ void gnt_text_view_append_text_with_tag(GntTextView *view, const char *text,
GntTextFormatFlags flags, const char *tagname)
{
GntWidget *widget = GNT_WIDGET(view);
- int fl = 0;
+ chtype fl = 0;
const char *start, *end;
GList *list = view->list;
GntTextLine *line;
@@ -720,7 +726,7 @@ int gnt_text_view_get_lines_above(GntTextView *view)
return above;
}
-/**
+/*
* XXX: There are quite possibly more than a few bugs here.
*/
int gnt_text_view_tag_change(GntTextView *view, const char *name, const char *text, gboolean all)
@@ -759,7 +765,7 @@ int gnt_text_view_tag_change(GntTextView *view, const char *name, const char *te
GntTextLine *line = iter->data;
inext = iter->next;
- if (!line) {
+ if (G_UNLIKELY(line == NULL)) {
g_warn_if_reached();
continue;
}
@@ -781,6 +787,10 @@ int gnt_text_view_tag_change(GntTextView *view, const char *name, const char *te
/* This segment starts in the middle of the tag */
if (text == NULL) {
free_text_segment(seg, NULL);
+ if (G_UNLIKELY(line == NULL)) {
+ g_warn_if_reached();
+ break;
+ }
line->segments = g_list_delete_link(line->segments, segs);
if (line->segments == NULL) {
free_text_line(line, NULL);
diff --git a/finch/libgnt/gnttextview.h b/finch/libgnt/gnttextview.h
index 341d25bc34..2f0846193b 100644
--- a/finch/libgnt/gnttextview.h
+++ b/finch/libgnt/gnttextview.h
@@ -1,7 +1,3 @@
-/**
- * @file gnttextview.h Textview API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,13 +22,19 @@
#ifndef GNT_TEXT_VIEW_H
#define GNT_TEXT_VIEW_H
+/**
+ * SECTION:gnttextview
+ * @section_id: libgnt-gnttextview
+ * @short_description: <filename>gnttextview.h</filename>
+ * @title: Textview
+ */
#include "gntwidget.h"
#include "gnt.h"
#include "gntcolors.h"
#include "gntkeys.h"
-#define GNT_TYPE_TEXT_VIEW (gnt_text_view_get_gtype())
+#define GNT_TYPE_TEXT_VIEW (gnt_text_view_get_type())
#define GNT_TEXT_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_TEXT_VIEW, GntTextView))
#define GNT_TEXT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_TEXT_VIEW, GntTextViewClass))
#define GNT_IS_TEXT_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_TEXT_VIEW))
@@ -79,6 +81,7 @@ struct _GntTextViewClass
{
GntWidgetClass parent;
+ /*< private >*/
void (*gnt_reserved1)(void);
void (*gnt_reserved2)(void);
void (*gnt_reserved3)(void);
@@ -88,108 +91,127 @@ struct _GntTextViewClass
G_BEGIN_DECLS
/**
- * @return GType for GntTextView.
+ * gnt_text_view_get_type:
+ *
+ * Returns: GType for GntTextView.
*/
-GType gnt_text_view_get_gtype(void);
+GType gnt_text_view_get_type(void);
/**
+ * gnt_text_view_new:
+ *
* Create a new textview.
*
- * @return The newly created textview.
+ * Returns: The newly created textview.
*/
GntWidget * gnt_text_view_new(void);
/**
+ * gnt_text_view_scroll:
+ * @view: The textview to scroll.
+ * @scroll: scroll > 0 means scroll up, < 0 means scroll down, == 0 means scroll to the end.
+ *
* Scroll the textview.
- * @param view The textview to scroll.
- * @param scroll scroll > 0 means scroll up, < 0 means scroll down, == 0 means scroll to the end.
*/
void gnt_text_view_scroll(GntTextView *view, int scroll);
/**
- * Append new text in a textview.
+ * gnt_text_view_append_text_with_flags:
+ * @view: The textview.
+ * @text: The text to append to the textview.
+ * @flags: The text-flags to apply to the new text.
*
- * @param view The textview.
- * @param text The text to append to the textview.
- * @param flags The text-flags to apply to the new text.
+ * Append new text in a textview.
*/
void gnt_text_view_append_text_with_flags(GntTextView *view, const char *text, GntTextFormatFlags flags);
/**
- * Append text in the textview, with some identifier (tag) for the added text.
+ * gnt_text_view_append_text_with_tag:
+ * @view: The textview.
+ * @text: The text to append.
+ * @flags: The text-flags to apply to the new text.
+ * @tag: The tag for the appended text, so it can be changed later (see gnt_text_view_tag_change())
*
- * @param view The textview.
- * @param text The text to append.
- * @param flags The text-flags to apply to the new text.
- * @param tag The tag for the appended text, so it can be changed later (@see gnt_text_view_tag_change)
+ * Append text in the textview, with some identifier (tag) for the added text.
*/
void gnt_text_view_append_text_with_tag(GntTextView *view, const char *text, GntTextFormatFlags flags, const char *tag);
/**
+ * gnt_text_view_next_line:
+ * @view: The textview.
+ *
* Move the cursor to the beginning of the next line and resets text-attributes.
* It first completes the current line with the current text-attributes.
- *
- * @param view The textview.
*/
void gnt_text_view_next_line(GntTextView *view);
/**
- * Convert GNT-text formats to ncurses-text attributes.
+ * gnt_text_format_flag_to_chtype:
+ * @flags: The GNT text format.
*
- * @param flags The GNT text format.
+ * Convert GNT-text formats to ncurses-text attributes.
*
- * @return Nucrses text attribute.
+ * Returns: Nucrses text attribute.
*/
chtype gnt_text_format_flag_to_chtype(GntTextFormatFlags flags);
/**
- * Clear the contents of the textview.
+ * gnt_text_view_clear:
+ * @view: The textview.
*
- * @param view The textview.
+ * Clear the contents of the textview.
*/
void gnt_text_view_clear(GntTextView *view);
/**
- * The number of lines below the bottom-most visible line.
+ * gnt_text_view_get_lines_below:
+ * @view: The textview.
*
- * @param view The textview.
+ * The number of lines below the bottom-most visible line.
*
- * @return Number of lines below the bottom-most visible line.
+ * Returns: Number of lines below the bottom-most visible line.
*/
int gnt_text_view_get_lines_below(GntTextView *view);
/**
- * The number of lines above the topmost visible line.
+ * gnt_text_view_get_lines_above:
+ * @view: The textview.
*
- * @param view The textview.
+ * The number of lines above the topmost visible line.
*
- * @return Number of lines above the topmost visible line.
+ * Returns: Number of lines above the topmost visible line.
*/
int gnt_text_view_get_lines_above(GntTextView *view);
/**
- * Change the text of a tag.
- *
- * @param view The textview.
- * @param name The name of the tag.
- * @param text The new text for the text. If 'text' is @c NULL, the tag is removed.
- * @param all @c TRUE if all of the instancess of the tag should be changed, @c FALSE if
+ * gnt_text_view_tag_change:
+ * @view: The textview.
+ * @name: The name of the tag.
+ * @text: The new text for the text. If 'text' is %NULL, the tag is removed.
+ * @all: %TRUE if all of the instancess of the tag should be changed, %FALSE if
* only the first instance should be changed.
*
- * @return The number of instances changed.
+ * Change the text of a tag.
+ *
+ * Returns: The number of instances changed.
*/
int gnt_text_view_tag_change(GntTextView *view, const char *name, const char *text, gboolean all);
/**
+ * gnt_text_view_attach_scroll_widget:
+ * @view: The textview.
+ * @widget: The trigger widget.
+ *
* Setup hooks so that pressing up/down/page-up/page-down keys when 'widget' is
* in focus scrolls the textview.
- *
- * @param view The textview.
- * @param widget The trigger widget.
*/
void gnt_text_view_attach_scroll_widget(GntTextView *view, GntWidget *widget);
/**
+ * gnt_text_view_attach_pager_widget:
+ * @view: The textview.
+ * @pager: The widget to trigger the PAGER.
+ *
* Setup appropriate hooks so that pressing some keys when the 'pager' widget
* is in focus triggers the PAGER to popup with the contents of the textview
* in it.
@@ -197,18 +219,19 @@ void gnt_text_view_attach_scroll_widget(GntTextView *view, GntWidget *widget);
* The default key-combination to trigger the pager is a-v, and the default
* PAGER application is $PAGER. Both can be changed in ~/.gntrc like this:
*
- * @code
+ * <programlisting>
* [pager]
* key = a-v
* path = /path/to/pager
- * @endcode
- *
- * @param view The textview.
- * @param pager The widget to trigger the PAGER.
+ * </programlisting>
*/
void gnt_text_view_attach_pager_widget(GntTextView *view, GntWidget *pager);
/**
+ * gnt_text_view_attach_editor_widget:
+ * @view: The textview.
+ * @widget: The widget to trigger the EDITOR.
+ *
* Setup appropriate hooks so that pressing some keys when 'widget'
* is in focus triggers the EDITOR to popup with the contents of the textview
* in it.
@@ -216,24 +239,22 @@ void gnt_text_view_attach_pager_widget(GntTextView *view, GntWidget *pager);
* The default key-combination to trigger the pager is a-e, and the default
* EDITOR application is $EDITOR. Both can be changed in ~/.gntrc like this:
*
- * @code
+ * <programlisting>
* [editor]
* key = a-e
* path = /path/to/editor
- * @endcode
- *
- * @param view The textview.
- * @param widget The widget to trigger the EDITOR.
+ * </programlisting>
*/
void gnt_text_view_attach_editor_widget(GntTextView *view, GntWidget *widget);
/**
- * Set a GntTextViewFlag for the textview widget.
+ * gnt_text_view_set_flag:
+ * @view: The textview widget
+ * @flag: The flag to set
*
- * @param view The textview widget
- * @param flag The flag to set
+ * Set a GntTextViewFlag for the textview widget.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
void gnt_text_view_set_flag(GntTextView *view, GntTextViewFlag flag);
diff --git a/finch/libgnt/gnttree.c b/finch/libgnt/gnttree.c
index 2980f1cacb..239f366d94 100644
--- a/finch/libgnt/gnttree.c
+++ b/finch/libgnt/gnttree.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -70,6 +70,8 @@ struct _GntTreePriv
* ... Probably not */
struct _GntTreeRow
{
+ int box_count;
+
void *key;
void *data; /* XXX: unused */
@@ -1009,7 +1011,6 @@ gnt_tree_set_property(GObject *obj, guint prop_id, const GValue *value,
if (tree->priv->expander_level == g_value_get_int(value))
break;
tree->priv->expander_level = g_value_get_int(value);
- g_object_notify(obj, "expander-level");
default:
break;
}
@@ -1054,7 +1055,7 @@ gnt_tree_class_init(GntTreeClass *klass)
g_param_spec_int("columns", "Columns",
"Number of columns in the tree.",
1, G_MAXINT, 1,
- G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+ G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS
)
);
g_object_class_install_property(gclass,
@@ -1062,7 +1063,7 @@ gnt_tree_class_init(GntTreeClass *klass)
g_param_spec_int("expander-level", "Expander level",
"Number of levels to show expander in the tree.",
0, G_MAXINT, 1,
- G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+ G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS
)
);
@@ -1143,7 +1144,7 @@ gnt_tree_init(GTypeInstance *instance, gpointer class)
* GntTree API
*****************************************************************************/
GType
-gnt_tree_get_gtype(void)
+gnt_tree_get_type(void)
{
static GType type = 0;
@@ -1313,7 +1314,8 @@ void gnt_tree_sort_row(GntTree *tree, gpointer key)
else
tree->root = row;
row->next = s;
- s->prev = row; /* s cannot be NULL */
+ g_return_if_fail(s != NULL); /* s cannot be NULL */
+ s->prev = row;
row->prev = NULL;
newp = g_list_index(tree->list, s) - 1;
} else {
@@ -1956,3 +1958,39 @@ GntTreeRow * gnt_tree_row_get_parent(GntTree *tree, GntTreeRow *row)
return row->parent;
}
+/**************************************************************************
+ * GntTreeRow GBoxed API
+ **************************************************************************/
+static GntTreeRow *
+gnt_tree_row_ref(GntTreeRow *row)
+{
+ g_return_val_if_fail(row != NULL, NULL);
+
+ row->box_count++;
+
+ return row;
+}
+
+static void
+gnt_tree_row_unref(GntTreeRow *row)
+{
+ g_return_if_fail(row != NULL);
+ g_return_if_fail(row->box_count >= 0);
+
+ if (!row->box_count--)
+ free_tree_row(row);
+}
+
+GType
+gnt_tree_row_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("GntTreeRow",
+ (GBoxedCopyFunc)gnt_tree_row_ref,
+ (GBoxedFreeFunc)gnt_tree_row_unref);
+ }
+
+ return type;
+}
diff --git a/finch/libgnt/gnttree.h b/finch/libgnt/gnttree.h
index 2be31b2cce..dbbc860254 100644
--- a/finch/libgnt/gnttree.h
+++ b/finch/libgnt/gnttree.h
@@ -1,7 +1,3 @@
-/**
- * @file gnttree.h Tree API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,6 +22,12 @@
#ifndef GNT_TREE_H
#define GNT_TREE_H
+/**
+ * SECTION:gnttree
+ * @section_id: libgnt-gnttree
+ * @short_description: <filename>gnttree.h</filename>
+ * @title: Tree
+ */
#include "gntwidget.h"
#include "gnt.h"
@@ -33,27 +35,41 @@
#include "gntkeys.h"
#include "gnttextview.h"
-#define GNT_TYPE_TREE (gnt_tree_get_gtype())
+#define GNT_TYPE_TREE (gnt_tree_get_type())
#define GNT_TREE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_TREE, GntTree))
#define GNT_TREE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_TREE, GntTreeClass))
#define GNT_IS_TREE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_TREE))
#define GNT_IS_TREE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_TREE))
#define GNT_TREE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_TREE, GntTreeClass))
+#define GNT_TYPE_TREE_ROW (gnt_tree_row_get_type())
+
+typedef guint (*GntTreeHashFunc)(gconstpointer);
+typedef gboolean (*GntTreeHashEqualityFunc)(gconstpointer, gconstpointer);
+
typedef struct _GntTree GntTree;
typedef struct _GntTreePriv GntTreePriv;
typedef struct _GntTreeClass GntTreeClass;
typedef struct _GntTreeRow GntTreeRow;
typedef struct _GntTreeCol GntTreeCol;
+typedef struct _GntTreeColInfo GntTreeColInfo;
-typedef enum _GntTreeColumnFlag {
+typedef enum {
GNT_TREE_COLUMN_INVISIBLE = 1 << 0,
GNT_TREE_COLUMN_FIXED_SIZE = 1 << 1,
GNT_TREE_COLUMN_BINARY_DATA = 1 << 2,
GNT_TREE_COLUMN_RIGHT_ALIGNED = 1 << 3,
} GntTreeColumnFlag;
+struct _GntTreeColInfo
+{
+ int width;
+ char *title;
+ int width_ratio;
+ GntTreeColumnFlag flags;
+};
+
struct _GntTree
{
GntWidget parent;
@@ -67,19 +83,13 @@ struct _GntTree
GList *list; /* List of GntTreeRow s */
GHashTable *hash; /* We need this for quickly referencing the rows */
- guint (*hash_func)(gconstpointer);
- gboolean (*hash_eq_func)(gconstpointer, gconstpointer);
+ GntTreeHashFunc hash_func;
+ GntTreeHashEqualityFunc hash_eq_func;
GDestroyNotify key_destroy;
GDestroyNotify value_destroy;
int ncol; /* No. of columns */
- struct _GntTreeColInfo
- {
- int width;
- char *title;
- int width_ratio;
- GntTreeColumnFlag flags;
- } *columns; /* Would a GList be better? */
+ GntTreeColInfo *columns; /* Would a GList be better? */
gboolean show_title;
gboolean show_separator; /* Whether to show column separators */
@@ -93,6 +103,7 @@ struct _GntTreeClass
void (*selection_changed)(GntTreeRow *old, GntTreeRow * current);
void (*toggled)(GntTree *tree, gpointer key);
+ /*< private >*/
void (*gnt_reserved1)(void);
void (*gnt_reserved2)(void);
void (*gnt_reserved3)(void);
@@ -102,542 +113,600 @@ struct _GntTreeClass
G_BEGIN_DECLS
/**
- * @return The GType for GntTree
+ * gnt_tree_get_type:
+ *
+ * Returns: The GType for GntTree
+ */
+GType gnt_tree_get_type(void);
+
+/**
+ * gnt_tree_row_get_type:
+ *
+ * Returns: The #GType for the #GntTreeRow boxed structure.
*/
-GType gnt_tree_get_gtype(void);
+GType gnt_tree_row_get_type(void);
/**
+ * gnt_tree_new:
+ *
* Create a tree with one column.
*
- * @return The newly created tree
+ * See gnt_tree_new_with_columns().
*
- * @see gnt_tree_new_with_columns
+ * Returns: The newly created tree
*/
GntWidget * gnt_tree_new(void);
/**
- * Create a tree with a specified number of columns.
+ * gnt_tree_new_with_columns:
+ * @columns: Number of columns
*
- * @param columns Number of columns
+ * Create a tree with a specified number of columns.
*
- * @return The newly created tree
+ * See gnt_tree_new().
*
- * @see gnt_tree_new
+ * Returns: The newly created tree
*/
GntWidget * gnt_tree_new_with_columns(int columns);
/**
- * The number of rows the tree should display at a time.
+ * gnt_tree_set_visible_rows:
+ * @tree: The tree
+ * @rows: The number of rows
*
- * @param tree The tree
- * @param rows The number of rows
+ * The number of rows the tree should display at a time.
*/
void gnt_tree_set_visible_rows(GntTree *tree, int rows);
/**
- * Get the number visible rows.
+ * gnt_tree_get_visible_rows:
+ * @tree: The tree
*
- * @param tree The tree
+ * Get the number visible rows.
*
- * @return The number of visible rows
+ * Returns: The number of visible rows
*/
int gnt_tree_get_visible_rows(GntTree *tree);
/**
- * Scroll the contents of the tree.
- *
- * @param tree The tree
- * @param count If positive, the tree will be scrolled down by count rows,
+ * gnt_tree_scroll:
+ * @tree: The tree
+ * @count: If positive, the tree will be scrolled down by count rows,
* otherwise, it will be scrolled up by count rows.
+ *
+ * Scroll the contents of the tree.
*/
void gnt_tree_scroll(GntTree *tree, int count);
/**
- * Insert a row in the tree.
+ * gnt_tree_add_row_after:
+ * @tree: The tree
+ * @key: The key for the row
+ * @row: The row to insert
+ * @parent: The key for the parent row
+ * @bigbro: The key for the row to insert the new row after.
*
- * @param tree The tree
- * @param key The key for the row
- * @param row The row to insert
- * @param parent The key for the parent row
- * @param bigbro The key for the row to insert the new row after.
+ * Insert a row in the tree.
*
- * @return The inserted row
+ * See gnt_tree_create_row(), gnt_tree_add_row_last(), gnt_tree_add_choice().
*
- * @see gnt_tree_create_row
- * @see gnt_tree_add_row_last
- * @see gnt_tree_add_choice
+ * Returns: The inserted row
*/
GntTreeRow * gnt_tree_add_row_after(GntTree *tree, void *key, GntTreeRow *row, void *parent, void *bigbro);
/**
- * Insert a row at the end of the tree.
+ * gnt_tree_add_row_last:
+ * @tree: The tree
+ * @key: The key for the row
+ * @row: The row to insert
+ * @parent: The key for the parent row
*
- * @param tree The tree
- * @param key The key for the row
- * @param row The row to insert
- * @param parent The key for the parent row
+ * Insert a row at the end of the tree.
*
- * @return The inserted row
+ * See gnt_tree_create_row(), gnt_tree_add_row_after(), gnt_tree_add_choice().
*
- * @see gnt_tree_create_row
- * @see gnt_tree_add_row_after
- * @see gnt_tree_add_choice
+ * Returns: The inserted row
*/
GntTreeRow * gnt_tree_add_row_last(GntTree *tree, void *key, GntTreeRow *row, void *parent);
/**
- * Get the key for the selected row.
+ * gnt_tree_get_selection_data:
+ * @tree: The tree
*
- * @param tree The tree
+ * Get the key for the selected row.
*
- * @return The key for the selected row
+ * Returns: (transfer none): The key for the selected row
*/
gpointer gnt_tree_get_selection_data(GntTree *tree);
/**
+ * gnt_tree_get_selection_text:
+ * @tree: The tree
+ *
* Get the text displayed for the selected row.
*
- * @param tree The tree
+ * See gnt_tree_get_row_text_list(), gnt_tree_get_selection_text_list().
*
- * @return The text, which needs to be freed by the caller
- * @see gnt_tree_get_row_text_list
- * @see gnt_tree_get_selection_text_list
+ * Returns: The text, which needs to be freed by the caller
*/
char * gnt_tree_get_selection_text(GntTree *tree);
/**
+ * gnt_tree_get_row_text_list:
+ * @tree: The tree
+ * @key: A key corresponding to the row in question. If key
+ * is %NULL, the text list for the selected row will
+ * be returned.
+ *
* Get a list of text for a row.
*
- * @param tree The tree
- * @param key A key corresponding to the row in question. If key
- * is @c NULL, the text list for the selected row will
- * be returned.
+ * See gnt_tree_get_selection_text_list(), gnt_tree_get_selection_text().
*
- * @return A list of texts of a row. The list and its data should be
- * freed by the caller. The caller should make sure that if
- * any column of the tree contains binary data, it's not freed.
- * @see gnt_tree_get_selection_text_list
- * @see gnt_tree_get_selection_text
+ * Returns: (transfer container) (element-type utf8): A list of texts of a row.
+ * The list and its data should be freed by the caller. The caller
+ * should make sure that if any column of the tree contains binary
+ * data, it's not freed.
*/
+/* TODO This leaks when used from introspection. The transfer mode for the
+ return type here should be 'full', but that would free binary data as
+ well. */
GList * gnt_tree_get_row_text_list(GntTree *tree, gpointer key);
/**
+ * gnt_tree_row_get_key:
+ * @tree: The tree
+ * @row: The GntTreeRow object
+ *
* Get the key of a row.
*
- * @param tree The tree
- * @param row The GntTreeRow object
+ * Returns: (transfer none): The key of the row.
*
- * @return The key of the row.
- * @since 2.8.0 (gnt), 2.7.2 (pidgin)
+ * Since: 2.8.0 (gnt), 2.7.2 (pidgin)
*/
gpointer gnt_tree_row_get_key(GntTree *tree, GntTreeRow *row);
/**
+ * gnt_tree_row_get_next:
+ * @tree: The tree
+ * @row: The GntTreeRow object
+ *
* Get the next row.
*
- * @param tree The tree
- * @param row The GntTreeRow object
+ * Returns: The next row.
*
- * @return The next row.
- * @since 2.8.0 (gnt), 2.7.2 (pidgin)
+ * Since: 2.8.0 (gnt), 2.7.2 (pidgin)
*/
GntTreeRow * gnt_tree_row_get_next(GntTree *tree, GntTreeRow *row);
/**
+ * gnt_tree_row_get_prev:
+ * @tree: The tree
+ * @row: The GntTreeRow object
+ *
* Get the previous row.
*
- * @param tree The tree
- * @param row The GntTreeRow object
+ * Returns: The previous row.
*
- * @return The previous row.
- * @since 2.8.0 (gnt), 2.7.2 (pidgin)
+ * Since: 2.8.0 (gnt), 2.7.2 (pidgin)
*/
GntTreeRow * gnt_tree_row_get_prev(GntTree *tree, GntTreeRow *row);
/**
+ * gnt_tree_row_get_child:
+ * @tree: The tree
+ * @row: The GntTreeRow object
+ *
* Get the child row.
*
- * @param tree The tree
- * @param row The GntTreeRow object
+ * Returns: The child row.
*
- * @return The child row.
- * @since 2.8.0 (gnt), 2.7.2 (pidgin)
+ * Since: 2.8.0 (gnt), 2.7.2 (pidgin)
*/
GntTreeRow * gnt_tree_row_get_child(GntTree *tree, GntTreeRow *row);
/**
+ * gnt_tree_row_get_parent:
+ * @tree: The tree
+ * @row: The GntTreeRow object
+ *
* Get the parent row.
*
- * @param tree The tree
- * @param row The GntTreeRow object
+ * Returns: The parent row.
*
- * @return The parent row.
- * @since 2.8.0 (gnt), 2.7.2 (pidgin)
+ * Since: 2.8.0 (gnt), 2.7.2 (pidgin)
*/
GntTreeRow * gnt_tree_row_get_parent(GntTree *tree, GntTreeRow *row);
/**
+ * gnt_tree_get_selection_text_list:
+ * @tree: The tree
+ *
* Get a list of text of the current row.
*
- * @param tree The tree
+ * See gnt_tree_get_row_text_list(), gnt_tree_get_selection_text().
*
- * @return A list of texts of the currently selected row. The list
- * and its data should be freed by the caller. The caller
- * should make sure that if any column of the tree contains
- * binary data, it's not freed.
- * @see gnt_tree_get_row_text_list
- * @see gnt_tree_get_selection_text
+ * Returns: (transfer container) (element-type utf8): A list of texts of the
+ * currently selected row. The list and its data should be freed by
+ * the caller. The caller should make sure that if any column of the
+ * tree contains binary data, it's not freed.
*/
+/* TODO This leaks when used from introspection. The transfer mode for the
+ return type here should be 'full', but that would free binary data as
+ well. */
GList * gnt_tree_get_selection_text_list(GntTree *tree);
/**
- * Returns the list of rows in the tree.
+ * gnt_tree_get_rows:
+ * @tree: The tree
*
- * @param tree The tree
+ * Returns the list of rows in the tree.
*
- * @return The list of the rows. The list should not be modified by the caller.
+ * Returns: (transfer none) (element-type Gnt.TreeRow): The list of the rows.
+ * The list should not be modified by the caller.
*/
GList *gnt_tree_get_rows(GntTree *tree);
/**
- * Remove a row from the tree.
+ * gnt_tree_remove:
+ * @tree: The tree
+ * @key: The key for the row to remove
*
- * @param tree The tree
- * @param key The key for the row to remove
+ * Remove a row from the tree.
*/
void gnt_tree_remove(GntTree *tree, gpointer key);
/**
- * Remove all the item from the tree.
+ * gnt_tree_remove_all:
+ * @tree: The tree
*
- * @param tree The tree
+ * Remove all the item from the tree.
*/
void gnt_tree_remove_all(GntTree *tree);
/**
- * Get the visible line number of the selected row.
+ * gnt_tree_get_selection_visible_line:
+ * @tree: The tree
*
- * @param tree The tree
+ * Get the visible line number of the selected row.
*
- * @return The line number of the currently selected row
+ * Returns: The line number of the currently selected row
*/
int gnt_tree_get_selection_visible_line(GntTree *tree);
/**
- * Change the text of a column in a row.
+ * gnt_tree_change_text:
+ * @tree: The tree
+ * @key: The key for the row
+ * @colno: The index of the column
+ * @text: The new text
*
- * @param tree The tree
- * @param key The key for the row
- * @param colno The index of the column
- * @param text The new text
+ * Change the text of a column in a row.
*/
void gnt_tree_change_text(GntTree *tree, gpointer key, int colno, const char *text);
/**
- * Add a checkable item in the tree.
+ * gnt_tree_add_choice:
+ * @tree: The tree
+ * @key: The key for the row
+ * @row: The row to add
+ * @parent: The parent of the row, or %NULL
+ * @bigbro: The row to insert after, or %NULL
*
- * @param tree The tree
- * @param key The key for the row
- * @param row The row to add
- * @param parent The parent of the row, or @c NULL
- * @param bigbro The row to insert after, or @c NULL
+ * Add a checkable item in the tree.
*
- * @return The row inserted.
+ * See gnt_tree_create_row(), gnt_tree_create_row_from_list(),
+ * gnt_tree_add_row_last(), gnt_tree_add_row_after().
*
- * @see gnt_tree_create_row
- * @see gnt_tree_create_row_from_list
- * @see gnt_tree_add_row_last
- * @see gnt_tree_add_row_after
+ * Returns: The row inserted.
*/
GntTreeRow * gnt_tree_add_choice(GntTree *tree, void *key, GntTreeRow *row, void *parent, void *bigbro);
/**
- * Set whether a checkable item is checked or not.
+ * gnt_tree_set_choice:
+ * @tree: The tree
+ * @key: The key for the row
+ * @set: %TRUE if the item should be checked, %FALSE if not
*
- * @param tree The tree
- * @param key The key for the row
- * @param set @c TRUE if the item should be checked, @c FALSE if not
+ * Set whether a checkable item is checked or not.
*/
void gnt_tree_set_choice(GntTree *tree, void *key, gboolean set);
/**
- * Return whether a row is selected or not, where the row is a checkable item.
+ * gnt_tree_get_choice:
+ * @tree: The tree
+ * @key: The key for the row
*
- * @param tree The tree
- * @param key The key for the row
+ * Return whether a row is selected or not, where the row is a checkable item.
*
- * @return @c TRUE if the row is checked, @c FALSE otherwise.
+ * Returns: %TRUE if the row is checked, %FALSE otherwise.
*/
gboolean gnt_tree_get_choice(GntTree *tree, void *key);
/**
- * Set flags for the text in a row in the tree.
+ * gnt_tree_set_row_flags:
+ * @tree: The tree
+ * @key: The key for the row
+ * @flags: The flags to set
*
- * @param tree The tree
- * @param key The key for the row
- * @param flags The flags to set
+ * Set flags for the text in a row in the tree.
*/
void gnt_tree_set_row_flags(GntTree *tree, void *key, GntTextFormatFlags flags);
/**
+ * gnt_tree_set_row_color:
+ * @tree: The tree
+ * @key: The key for the row
+ * @color: The color
+ *
* Set color for the text in a row in the tree.
*
- * @param tree The tree
- * @param key The key for the row
- * @param color The color
- * @since 2.4.0
+ * Since: 2.4.0
*/
void gnt_tree_set_row_color(GntTree *tree, void *key, int color);
/**
- * Select a row.
+ * gnt_tree_set_selected:
+ * @tree: The tree
+ * @key: The key of the row to select
*
- * @param tree The tree
- * @param key The key of the row to select
+ * Select a row.
*/
void gnt_tree_set_selected(GntTree *tree , void *key);
/**
- * Create a row to insert in the tree.
+ * gnt_tree_create_row:
+ * @tree: The tree
+ * @...: A string for each column in the tree
*
- * @param tree The tree
- * @param ... A string for each column in the tree
+ * Create a row to insert in the tree.
*
- * @return The row
+ * See gnt_tree_create_row_from_list(), gnt_tree_add_row_after(),
+ * gnt_tree_add_row_last(), gnt_tree_add_choice().
*
- * @see gnt_tree_create_row_from_list
- * @see gnt_tree_add_row_after
- * @see gnt_tree_add_row_last
- * @see gnt_tree_add_choice
+ * Returns: The row
*/
GntTreeRow * gnt_tree_create_row(GntTree *tree, ...);
/**
- * Create a row from a list of text.
+ * gnt_tree_create_row_from_list:
+ * @tree: The tree
+ * @list: (element-type utf8): The list containing the text for each column
*
- * @param tree The tree
- * @param list The list containing the text for each column
+ * Create a row from a list of text.
*
- * @return The row
+ * See gnt_tree_create_row(), gnt_tree_add_row_after(), gnt_tree_add_row_last(),
+ * gnt_tree_add_choice().
*
- * @see gnt_tree_create_row
- * @see gnt_tree_add_row_after
- * @see gnt_tree_add_row_last
- * @see gnt_tree_add_choice
+ * Returns: (transfer full): The row
*/
GntTreeRow * gnt_tree_create_row_from_list(GntTree *tree, GList *list);
/**
- * Set the width of a column in the tree.
+ * gnt_tree_set_col_width:
+ * @tree: The tree
+ * @col: The index of the column
+ * @width: The width for the column
*
- * @param tree The tree
- * @param col The index of the column
- * @param width The width for the column
+ * Set the width of a column in the tree.
*
- * @see gnt_tree_set_column_width_ratio
- * @see gnt_tree_set_column_resizable
+ * See gnt_tree_set_column_width_ratio(), gnt_tree_set_column_resizable()
*/
void gnt_tree_set_col_width(GntTree *tree, int col, int width);
/**
- * Set the title for a column.
+ * gnt_tree_set_column_title:
+ * @tree: The tree
+ * @index: The index of the column
+ * @title: The title for the column
*
- * @param tree The tree
- * @param index The index of the column
- * @param title The title for the column
+ * Set the title for a column.
*
- * @see gnt_tree_set_column_titles
- * @see gnt_tree_set_show_title
+ * See gnt_tree_set_column_titles(), gnt_tree_set_show_title().
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
void gnt_tree_set_column_title(GntTree *tree, int index, const char *title);
/**
- * Set the titles of the columns
+ * gnt_tree_set_column_titles:
+ * @tree: The tree
+ * @...: One title for each column in the tree
*
- * @param tree The tree
- * @param ... One title for each column in the tree
+ * Set the titles of the columns
*
- * @see gnt_tree_set_column_title
- * @see gnt_tree_set_show_title
+ * See gnt_tree_set_column_title(), gnt_tree_set_show_title().
*/
void gnt_tree_set_column_titles(GntTree *tree, ...);
/**
- * Set whether to display the title of the columns.
+ * gnt_tree_set_show_title:
+ * @tree: The tree
+ * @set: If %TRUE, the column titles are displayed
*
- * @param tree The tree
- * @param set If @c TRUE, the column titles are displayed
+ * Set whether to display the title of the columns.
*
- * @see gnt_tree_set_column_title
- * @see gnt_tree_set_column_titles
+ * See gnt_tree_set_column_title(), gnt_tree_set_column_titles().
*/
void gnt_tree_set_show_title(GntTree *tree, gboolean set);
/**
- * Set the compare function for sorting the data.
+ * gnt_tree_set_compare_func:
+ * @tree: The tree
+ * @func: (scope call): The comparison function, which is used to compare
+ * the keys
*
- * @param tree The tree
- * @param func The comparison function, which is used to compare
- * the keys
+ * Set the compare function for sorting the data.
*
- * @see gnt_tree_sort_row
+ * See gnt_tree_sort_row().
*/
void gnt_tree_set_compare_func(GntTree *tree, GCompareFunc func);
/**
- * Set whether a row, which has child rows, should be expanded.
+ * gnt_tree_set_expanded:
+ * @tree: The tree
+ * @key: The key of the row
+ * @expanded: Whether to expand the child rows
*
- * @param tree The tree
- * @param key The key of the row
- * @param expanded Whether to expand the child rows
+ * Set whether a row, which has child rows, should be expanded.
*/
void gnt_tree_set_expanded(GntTree *tree, void *key, gboolean expanded);
/**
- * Set whether to show column separators.
+ * gnt_tree_set_show_separator:
+ * @tree: The tree
+ * @set: If %TRUE, the column separators are displayed
*
- * @param tree The tree
- * @param set If @c TRUE, the column separators are displayed
+ * Set whether to show column separators.
*/
void gnt_tree_set_show_separator(GntTree *tree, gboolean set);
/**
- * Sort a row in the tree.
+ * gnt_tree_sort_row:
+ * @tree: The tree
+ * @row: The row to sort
*
- * @param tree The tree
- * @param row The row to sort
+ * Sort a row in the tree.
*
- * @see gnt_tree_set_compare_func
+ * See gnt_tree_set_compare_func().
*/
void gnt_tree_sort_row(GntTree *tree, void *row);
/**
- * Automatically adjust the width of the columns in the tree.
+ * gnt_tree_adjust_columns:
+ * @tree: The tree
*
- * @param tree The tree
+ * Automatically adjust the width of the columns in the tree.
*/
void gnt_tree_adjust_columns(GntTree *tree);
/**
- * Set the hash functions to use to hash, compare and free the keys.
- *
- * @param tree The tree
- * @param hash The hashing function
- * @param eq The function to compare keys
- * @param kd The function to use to free the keys when a row is removed
+ * gnt_tree_set_hash_fns:
+ * @tree: The tree
+ * @hash: The hashing function
+ * @eq: The function to compare keys
+ * @kd: The function to use to free the keys when a row is removed
* from the tree
+ *
+ * Set the hash functions to use to hash, compare and free the keys.
*/
void gnt_tree_set_hash_fns(GntTree *tree, gpointer hash, gpointer eq, gpointer kd);
/**
+ * gnt_tree_set_column_visible:
+ * @tree: The tree
+ * @col: The index of the column
+ * @vis: If %FALSE, the column will not be displayed
+ *
* Set whether a column is visible or not.
* This can be useful when, for example, we want to store some data
* which we don't want/need to display.
- *
- * @param tree The tree
- * @param col The index of the column
- * @param vis If @c FALSE, the column will not be displayed
*/
void gnt_tree_set_column_visible(GntTree *tree, int col, gboolean vis);
/**
+ * gnt_tree_set_column_resizable:
+ * @tree: The tree
+ * @col: The index of the column
+ * @res: If %FALSE, the column will not be resized when the
+ * tree is resized
+ *
* Set whether a column can be resized to keep the same ratio when the
* tree is resized.
*
- * @param tree The tree
- * @param col The index of the column
- * @param res If @c FALSE, the column will not be resized when the
- * tree is resized
- *
- * @see gnt_tree_set_col_width
- * @see gnt_tree_set_column_width_ratio
+ * See gnt_tree_set_col_width(), gnt_tree_set_column_width_ratio().
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
void gnt_tree_set_column_resizable(GntTree *tree, int col, gboolean res);
/**
+ * gnt_tree_set_column_is_binary:
+ * @tree: The tree
+ * @col: The index of the column
+ * @bin: %TRUE if the data for the column is binary
+ *
* Set whether data in a column should be considered as binary data, and
* not as strings. A column containing binary data will be display empty text.
- *
- * @param tree The tree
- * @param col The index of the column
- * @param bin @c TRUE if the data for the column is binary
*/
void gnt_tree_set_column_is_binary(GntTree *tree, int col, gboolean bin);
/**
- * Set whether text in a column should be right-aligned.
+ * gnt_tree_set_column_is_right_aligned:
+ * @tree: The tree
+ * @col: The index of the column
+ * @right: %TRUE if the text in the column should be right aligned
*
- * @param tree The tree
- * @param col The index of the column
- * @param right @c TRUE if the text in the column should be right aligned
+ * Set whether text in a column should be right-aligned.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
void gnt_tree_set_column_is_right_aligned(GntTree *tree, int col, gboolean right);
/**
- * Set column widths to use when calculating column widths after a tree
- * is resized.
- *
- * @param tree The tree
- * @param cols Array of widths. The width must have the same number
+ * gnt_tree_set_column_width_ratio:
+ * @tree: The tree
+ * @cols: Array of widths. The width must have the same number
* of entries as the number of columns in the tree, or
* end with a negative value for a column-width.
*
- * @see gnt_tree_set_col_width
- * @see gnt_tree_set_column_resizable
+ * Set column widths to use when calculating column widths after a tree
+ * is resized.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * See gnt_tree_set_col_width(), gnt_tree_set_column_resizable().
+ *
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
void gnt_tree_set_column_width_ratio(GntTree *tree, int cols[]);
/**
- * Set the column to use for typeahead searching.
+ * gnt_tree_set_search_column:
+ * @tree: The tree
+ * @col: The index of the column
*
- * @param tree The tree
- * @param col The index of the column
+ * Set the column to use for typeahead searching.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
void gnt_tree_set_search_column(GntTree *tree, int col);
/**
+ * gnt_tree_is_searching:
+ * @tree: The tree
+ *
* Check whether the user is currently in the middle of a search.
*
- * @param tree The tree
- * @return @c TRUE if the user is searching, @c FALSE otherwise.
+ * Returns: %TRUE if the user is searching, %FALSE otherwise.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
gboolean gnt_tree_is_searching(GntTree *tree);
/**
- * Set a custom search function.
- *
- * @param tree The tree
- * @param func The custom search function. The search function is
+ * gnt_tree_set_search_function:
+ * @tree: The tree
+ * @func: The custom search function. The search function is
* sent the tree itself, the key of a row, the search
* string and the content of row in the search column.
- * If the function returns @c TRUE, the row is dislayed,
+ * If the function returns %TRUE, the row is dislayed,
* otherwise it's not.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Set a custom search function.
+ *
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
void gnt_tree_set_search_function(GntTree *tree,
gboolean (*func)(GntTree *tree, gpointer key, const char *search, const char *current));
/**
+ * gnt_tree_get_parent_key:
+ * @tree: The tree
+ * @key: The key for the row.
+ *
* Get the parent key for a row.
*
- * @param tree The tree
- * @param key The key for the row.
+ * Returns: (transfer none): The key of the parent row.
*
- * @return The key of the parent row.
- * @since 2.4.0
+ * Since: 2.4.0
*/
gpointer gnt_tree_get_parent_key(GntTree *tree, gpointer key);
diff --git a/finch/libgnt/gntutils.c b/finch/libgnt/gntutils.c
index 1e41c363c9..ef32396579 100644
--- a/finch/libgnt/gntutils.c
+++ b/finch/libgnt/gntutils.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -148,8 +148,8 @@ char *gnt_util_onscreen_fit_string(const char *string, int maxw)
struct duplicate_fns
{
- GDupFunc key_dup;
- GDupFunc value_dup;
+ GntDuplicateFunc key_dup;
+ GntDuplicateFunc value_dup;
GHashTable *table;
};
@@ -161,9 +161,9 @@ duplicate_values(gpointer key, gpointer value, gpointer data)
fns->value_dup ? fns->value_dup(value) : value);
}
-GHashTable *g_hash_table_duplicate(GHashTable *src, GHashFunc hash,
+GHashTable *gnt_hash_table_duplicate(GHashTable *src, GHashFunc hash,
GEqualFunc equal, GDestroyNotify key_d, GDestroyNotify value_d,
- GDupFunc key_dup, GDupFunc value_dup)
+ GntDuplicateFunc key_dup, GntDuplicateFunc value_dup)
{
GHashTable *dest = g_hash_table_new_full(hash, equal, key_d, value_d);
struct duplicate_fns fns = {key_dup, value_dup, dest};
diff --git a/finch/libgnt/gntutils.h b/finch/libgnt/gntutils.h
index 7517d7420c..f2d16769cb 100644
--- a/finch/libgnt/gntutils.h
+++ b/finch/libgnt/gntutils.h
@@ -1,7 +1,3 @@
-/**
- * @file gntutils.h Some utility functions
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -24,134 +20,150 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+#ifndef GNT_UTILS_H
+#define GNT_UTILS_H
+/**
+ * SECTION:gntutils
+ * @section_id: libgnt-gntutils
+ * @short_description: <filename>gntutils.h</filename>
+ * @title: Utility functions
+ */
+
#include <glib.h>
#include "gnt.h"
#include "gnttextview.h"
#include "gntwidget.h"
-typedef gpointer (*GDupFunc)(gconstpointer data);
+typedef gpointer (*GntDuplicateFunc)(gconstpointer data);
/**
- * Compute the width and height required to view the text on the screen.
+ * gnt_util_get_text_bound:
+ * @text: The text to be displayed.
+ * @width: The width required is set here, if not %NULL.
+ * @height: The height required is set here, if not %NULL.
*
- * @param text The text to be displayed.
- * @param width The width required is set here, if not @c NULL.
- * @param height The height required is set here, if not @c NULL.
+ * Compute the width and height required to view the text on the screen.
*/
void gnt_util_get_text_bound(const char *text, int *width, int *height);
/* excluding *end */
/**
- * Get the onscreen width of a string, or a substring.
- *
- * @param start The beginning of the string.
- * @param end The end of the string. The width returned is the width
+ * gnt_util_onscreen_width:
+ * @start: The beginning of the string.
+ * @end: The end of the string. The width returned is the width
* upto (but not including) end. If end is NULL, then start
- * is considered as a @c NULL-terminated string.
+ * is considered as a %NULL-terminated string.
+ *
+ * Get the onscreen width of a string, or a substring.
*
- * @return The on-screen width of the string.
+ * Returns: The on-screen width of the string.
*/
int gnt_util_onscreen_width(const char *start, const char *end);
/**
- * Computes and returns the string after a specific number of onscreen characters.
+ * gnt_util_onscreen_width_to_pointer:
+ * @str: The string.
+ * @len: The length to consider. If non-positive, the entire screenlength is used.
+ * @w: The actual width of the string upto the returned offset, if not %NULL.
*
- * @param str The string.
- * @param len The length to consider. If non-positive, the entire screenlength is used.
- * @param w The actual width of the string upto the returned offset, if not @c NULL.
+ * Computes and returns the string after a specific number of onscreen characters.
*
- * @return The string after len offset.
+ * Returns: The string after len offset.
*/
const char *gnt_util_onscreen_width_to_pointer(const char *str, int len, int *w);
/**
+ * gnt_util_onscreen_fit_string:
+ * @string: The string.
+ * @maxw: The width that the string should fit into. If maxw is <= 0,
+ * then the available maximum width is used.
+ *
* Inserts newlines in 'string' where necessary so that its onscreen width is
* no more than 'maxw'.
*
- * @param string The string.
- * @param maxw The width that the string should fit into. If maxw is <= 0,
- * then the available maximum width is used.
- *
- * @return A newly allocated string that needs to be freed by the caller.
+ * Returns: A newly allocated string that needs to be freed by the caller.
*/
char * gnt_util_onscreen_fit_string(const char *string, int maxw);
/**
- * Duplicate the contents of a hastable.
+ * gnt_hash_table_duplicate:
+ * @src: The source hashtable.
+ * @hash: (scope call): The hash-function to use.
+ * @equal: The hash-equal function to use.
+ * @key_d: The key-destroy function to use.
+ * @value_d: The value-destroy function to use.
+ * @key_dup: (scope call): The function to use to duplicate the key.
+ * @value_dup: (scope call): The function to use to duplicate the value.
*
- * @param src The source hashtable.
- * @param hash The hash-function to use.
- * @param equal The hash-equal function to use.
- * @param key_d The key-destroy function to use.
- * @param value_d The value-destroy function to use.
- * @param key_dup The function to use to duplicate the key.
- * @param value_dup The function to use to duplicate the value.
+ * Duplicate the contents of a hastable.
*
- * @return The new hashtable.
+ * Returns: (transfer full): The new hashtable.
*/
-GHashTable * g_hash_table_duplicate(GHashTable *src, GHashFunc hash, GEqualFunc equal, GDestroyNotify key_d, GDestroyNotify value_d, GDupFunc key_dup, GDupFunc value_dup);
+GHashTable * gnt_hash_table_duplicate(GHashTable *src, GHashFunc hash, GEqualFunc equal, GDestroyNotify key_d, GDestroyNotify value_d, GntDuplicateFunc key_dup, GntDuplicateFunc value_dup);
/**
+ * gnt_boolean_handled_accumulator:
+ *
* To be used with g_signal_new. Look in the key_pressed signal-definition in
* gntwidget.c for usage.
- *
- * @param ihint NA
- * @param return_accu NA
- * @param handler_return NA
- * @param dummy NA
- *
- * @return NA
*/
gboolean gnt_boolean_handled_accumulator(GSignalInvocationHint *ihint, GValue *return_accu, const GValue *handler_return, gpointer dummy);
/**
- * Get a helpful display about the bindings of a widget.
+ * gnt_widget_bindings_view:
+ * @widget: The widget to get bindings for.
*
- * @param widget The widget to get bindings for.
+ * Get a helpful display about the bindings of a widget.
*
- * @return Returns a GntTree populated with "key" -> "binding" for the widget.
+ * Returns: (transfer full): Returns a GntTree populated with "key" -> "binding"
+ * for the widget.
*/
GntWidget * gnt_widget_bindings_view(GntWidget *widget);
/**
+ * gnt_util_parse_widgets:
+ * @string: The XML string.
+ * @num: The number of widgets to return, followed by 'num' GntWidget **
+ *
* Parse widgets from an XML description. For example,
*
- * @code
+ * <programlisting>
* GntWidget *win, *button;
* gnt_util_parse_widgets("\
- * <vwindow id='0' fill='0' align='2'> \
- * <label>This is a test</label> \
- * <button id='1'>OK</button> \
- * </vwindow>",
+ * &lt;vwindow id='0' fill='0' align='2'&gt; \
+ * &lt;label&gt;This is a test&lt;/label&gt; \
+ * &lt;button id='1'&gt;OK&lt;/button&gt; \
+ * &lt;/vwindow&gt;",
* 2, &win, &button);
- * @endcode
- *
- * @param string The XML string.
- * @param num The number of widgets to return, followed by 'num' GntWidget **
+ * </programlisting>
*/
void gnt_util_parse_widgets(const char *string, int num, ...);
/**
+ * gnt_util_parse_xhtml_to_textview:
+ * @string: The XHTML string
+ * @tv: The GntTextView
+ *
* Parse an XHTML string and add it in a GntTextView with
* appropriate text flags.
*
- * @param string The XHTML string
- * @param tv The GntTextView
- * @return @c TRUE if the string was added to the textview properly, @c FALSE otherwise.
+ * Returns: %TRUE if the string was added to the textview properly, %FALSE otherwise.
*
- * @since 2.2.0
+ * Since: 2.2.0
*/
gboolean gnt_util_parse_xhtml_to_textview(const char *string, GntTextView *tv);
/**
- * Make some keypress activate a button when some key is pressed with 'wid' in focus.
+ * gnt_util_set_trigger_widget:
+ * @widget: The widget
+ * @key: The key to trigger the button
+ * @button: The button to trigger
*
- * @param widget The widget
- * @param key The key to trigger the button
- * @param button The button to trigger
+ * Make some keypress activate a button when some key is pressed with 'wid' in focus.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
void gnt_util_set_trigger_widget(GntWidget *widget, const char *key, GntWidget *button);
+#endif /* GNT_UTILS_H */
diff --git a/finch/libgnt/gntwidget.c b/finch/libgnt/gntwidget.c
index fb2d3d8646..89ba57d748 100644
--- a/finch/libgnt/gntwidget.c
+++ b/finch/libgnt/gntwidget.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -27,7 +27,6 @@
#include "gntstyle.h"
#include "gntmarshal.h"
#include "gntutils.h"
-#include "gnt.h"
enum
{
@@ -268,7 +267,7 @@ gnt_widget_class_init(GntWidgetClass *klass)
* GntWidget API
*****************************************************************************/
GType
-gnt_widget_get_gtype(void)
+gnt_widget_get_type(void)
{
static GType type = 0;
@@ -302,13 +301,6 @@ void gnt_widget_set_take_focus(GntWidget *widget, gboolean can)
GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_CAN_TAKE_FOCUS);
}
-/**
- * gnt_widget_destroy:
- * @obj: The #GntWidget instance.
- *
- * Emits the "destroy" signal notifying all reference holders that they
- * should release @obj.
- */
void
gnt_widget_destroy(GntWidget *obj)
{
diff --git a/finch/libgnt/gntwidget.h b/finch/libgnt/gntwidget.h
index e25d58f711..85bdd54214 100644
--- a/finch/libgnt/gntwidget.h
+++ b/finch/libgnt/gntwidget.h
@@ -1,7 +1,3 @@
-/**
- * @file gntwidget.h Widget API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,14 +22,19 @@
#ifndef GNT_WIDGET_H
#define GNT_WIDGET_H
+/**
+ * SECTION:gntwidget
+ * @section_id: libgnt-gntwidget
+ * @short_description: <filename>gntwidget.h</filename>
+ * @title: Widget
+ */
#include <stdio.h>
#include <glib.h>
-#include <ncurses.h>
#include "gntbindable.h"
-#define GNT_TYPE_WIDGET (gnt_widget_get_gtype())
+#define GNT_TYPE_WIDGET (gnt_widget_get_type())
#define GNT_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_WIDGET, GntWidget))
#define GNT_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_WIDGET, GntWidgetClass))
#define GNT_IS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_WIDGET))
@@ -49,7 +50,7 @@ typedef struct _GntWidget GntWidget;
typedef struct _GntWidgetPriv GntWidgetPriv;
typedef struct _GntWidgetClass GntWidgetClass;
-typedef enum _GntWidgetFlags
+typedef enum
{
GNT_WIDGET_DESTROYING = 1 << 0,
GNT_WIDGET_CAN_TAKE_FOCUS = 1 << 1,
@@ -69,7 +70,7 @@ typedef enum _GntWidgetFlags
} GntWidgetFlags;
/* XXX: This will probably move elsewhere */
-typedef enum _GntMouseEvent
+typedef enum
{
GNT_LEFT_MOUSE_DOWN = 1,
GNT_RIGHT_MOUSE_DOWN,
@@ -80,7 +81,7 @@ typedef enum _GntMouseEvent
} GntMouseEvent;
/* XXX: I'll have to ask grim what he's using this for in guifications. */
-typedef enum _GntParamFlags
+typedef enum
{
GNT_PARAM_SERIALIZABLE = 1 << G_PARAM_USER_SHIFT
} GntParamFlags;
@@ -104,10 +105,11 @@ struct _GntWidget
GntWidgetPriv priv;
WINDOW *window;
- void (*gnt_reserved1)(void);
- void (*gnt_reserved2)(void);
- void (*gnt_reserved3)(void);
- void (*gnt_reserved4)(void);
+ /*< private >*/
+ void *res1;
+ void *res2;
+ void *res3;
+ void *res4;
};
struct _GntWidgetClass
@@ -131,6 +133,7 @@ struct _GntWidgetClass
void (*activate)(GntWidget *widget);
gboolean (*clicked)(GntWidget *widget, GntMouseEvent event, int x, int y);
+ /*< private >*/
void (*gnt_reserved1)(void);
void (*gnt_reserved2)(void);
void (*gnt_reserved3)(void);
@@ -140,175 +143,215 @@ struct _GntWidgetClass
G_BEGIN_DECLS
/**
- * @return GType for GntWidget.
+ * gnt_widget_get_type:
+ *
+ * Returns: GType for GntWidget.
*/
-GType gnt_widget_get_gtype(void);
+GType gnt_widget_get_type(void);
/**
+ * gnt_widget_destroy:
+ * @widget: The widget to destroy.
+ *
* Destroy a widget.
- * @param widget The widget to destroy.
+ *
+ * Emits the "destroy" signal notifying all reference holders that they
+ * should release @widget.
*/
void gnt_widget_destroy(GntWidget *widget);
/**
+ * gnt_widget_show:
+ * @widget: The widget to show.
+ *
* Show a widget. This should only be used for toplevel widgets. For the rest
* of the widgets, use #gnt_widget_draw instead.
- *
- * @param widget The widget to show.
*/
void gnt_widget_show(GntWidget *widget);
/**
+ * gnt_widget_draw:
+ * @widget: The widget to draw.
+ *
* Draw a widget.
- * @param widget The widget to draw.
*/
void gnt_widget_draw(GntWidget *widget);
/**
- * @internal
+ * gnt_widget_expose:
+ *
* Expose part of a widget.
+ *
+ * Internal function -- do not use.
*/
void gnt_widget_expose(GntWidget *widget, int x, int y, int width, int height);
/**
+ * gnt_widget_hide:
+ * @widget: The widget to hide.
+ *
* Hide a widget.
- * @param widget The widget to hide.
*/
void gnt_widget_hide(GntWidget *widget);
/**
- * Get the position of a widget.
+ * gnt_widget_get_position:
+ * @widget: The widget.
+ * @x: Location to store the x-coordinate of the widget.
+ * @y: Location to store the y-coordinate of the widget.
*
- * @param widget The widget.
- * @param x Location to store the x-coordinate of the widget.
- * @param y Location to store the y-coordinate of the widget.
+ * Get the position of a widget.
*/
void gnt_widget_get_position(GntWidget *widget, int *x, int *y);
/**
+ * gnt_widget_set_position:
+ * @widget: The widget to reposition.
+ * @x: The x-coordinate of the widget.
+ * @y: The x-coordinate of the widget.
+ *
* Set the position of a widget.
- * @param widget The widget to reposition.
- * @param x The x-coordinate of the widget.
- * @param y The x-coordinate of the widget.
*/
void gnt_widget_set_position(GntWidget *widget, int x, int y);
/**
+ * gnt_widget_size_request:
+ * @widget: The widget.
+ *
* Request a widget to calculate its desired size.
- * @param widget The widget.
*/
void gnt_widget_size_request(GntWidget *widget);
/**
+ * gnt_widget_get_size:
+ * @widget: The widget.
+ * @width: Location to store the width of the widget.
+ * @height: Location to store the height of the widget.
+ *
* Get the size of a widget.
- * @param widget The widget.
- * @param width Location to store the width of the widget.
- * @param height Location to store the height of the widget.
*/
void gnt_widget_get_size(GntWidget *widget, int *width, int *height);
/**
- * Set the size of a widget.
+ * gnt_widget_set_size:
+ * @widget: The widget to resize.
+ * @width: The width of the widget.
+ * @height: The height of the widget.
*
- * @param widget The widget to resize.
- * @param width The width of the widget.
- * @param height The height of the widget.
+ * Set the size of a widget.
*
- * @return If the widget was resized to the new size.
+ * Returns: If the widget was resized to the new size.
*/
gboolean gnt_widget_set_size(GntWidget *widget, int width, int height);
/**
- * Confirm a requested a size for a widget.
+ * gnt_widget_confirm_size:
+ * @widget: The widget.
+ * @width: The requested width.
+ * @height: The requested height.
*
- * @param widget The widget.
- * @param width The requested width.
- * @param height The requested height.
+ * Confirm a requested a size for a widget.
*
- * @return @c TRUE if the new size was confirmed, @c FALSE otherwise.
+ * Returns: %TRUE if the new size was confirmed, %FALSE otherwise.
*/
gboolean gnt_widget_confirm_size(GntWidget *widget, int width, int height);
/**
- * Trigger the key-press callbacks for a widget.
+ * gnt_widget_key_pressed:
+ * @widget: The widget.
+ * @keys: The keypress on the widget.
*
- * @param widget The widget.
- * @param keys The keypress on the widget.
+ * Trigger the key-press callbacks for a widget.
*
- * @return @c TRUE if the key-press was handled, @c FALSE otherwise.
+ * Returns: %TRUE if the key-press was handled, %FALSE otherwise.
*/
gboolean gnt_widget_key_pressed(GntWidget *widget, const char *keys);
/**
- * Trigger the 'click' callback of a widget.
+ * gnt_widget_clicked:
+ * @widget: The widget.
+ * @event: The mouseevent.
+ * @x: The x-coordinate of the mouse.
+ * @y: The y-coordinate of the mouse.
*
- * @param widget The widget.
- * @param event The mouseevent.
- * @param x The x-coordinate of the mouse.
- * @param y The y-coordinate of the mouse.
+ * Trigger the 'click' callback of a widget.
*
- * @return @c TRUE if the event was handled, @c FALSE otherwise.
+ * Returns: %TRUE if the event was handled, %FALSE otherwise.
*/
gboolean gnt_widget_clicked(GntWidget *widget, GntMouseEvent event, int x, int y);
/**
- * Give or remove focus to a widget.
- * @param widget The widget.
- * @param set @c TRUE of focus should be given to the widget, @c FALSE if
+ * gnt_widget_set_focus:
+ * @widget: The widget.
+ * @set: %TRUE of focus should be given to the widget, %FALSE if
* focus should be removed.
*
- * @return @c TRUE if the focus has been changed, @c FALSE otherwise.
+ * Give or remove focus to a widget.
+ *
+ * Returns: %TRUE if the focus has been changed, %FALSE otherwise.
*/
gboolean gnt_widget_set_focus(GntWidget *widget, gboolean set);
/**
+ * gnt_widget_activate:
+ * @widget: The widget to activate.
+ *
* Activate a widget. This only applies to widgets that can be activated (eg. GntButton)
- * @param widget The widget to activate.
*/
void gnt_widget_activate(GntWidget *widget);
/**
+ * gnt_widget_set_name:
+ * @widget: The widget.
+ * @name: A new name for the widget.
+ *
* Set the name of a widget.
- * @param widget The widget.
- * @param name A new name for the widget.
*/
void gnt_widget_set_name(GntWidget *widget, const char *name);
/**
+ * gnt_widget_get_name:
+ * @widget: The widget.
+ *
* Get the name of a widget.
- * @param widget The widget.
- * @return The name of the widget.
+ *
+ * Returns: The name of the widget.
*/
const char *gnt_widget_get_name(GntWidget *widget);
/**
- * @internal
- * Use #gnt_widget_draw instead.
+ * gnt_widget_queue_update:
+ *
+ * Internal function -- do not use.
+ * Use gnt_widget_draw() instead.
*/
void gnt_widget_queue_update(GntWidget *widget);
/**
- * Set whether a widget can take focus or not.
+ * gnt_widget_set_take_focus:
+ * @widget: The widget.
+ * @set: %TRUE if the widget can take focus.
*
- * @param widget The widget.
- * @param set @c TRUE if the widget can take focus.
+ * Set whether a widget can take focus or not.
*/
void gnt_widget_set_take_focus(GntWidget *widget, gboolean set);
/**
- * Set the visibility of a widget.
+ * gnt_widget_set_visible:
+ * @widget: The widget.
+ * @set: Whether the widget is visible or not.
*
- * @param widget The widget.
- * @param set Whether the widget is visible or not.
+ * Set the visibility of a widget.
*/
void gnt_widget_set_visible(GntWidget *widget, gboolean set);
/**
- * Check whether the widget has shadows.
+ * gnt_widget_has_shadow:
+ * @widget: The widget.
*
- * @param widget The widget.
+ * Check whether the widget has shadows.
*
- * @return @c TRUE if the widget has shadows. This checks both the user-setting
+ * Returns: %TRUE if the widget has shadows. This checks both the user-setting
* and whether the widget can have shadows at all.
*/
gboolean gnt_widget_has_shadow(GntWidget *widget);
diff --git a/finch/libgnt/gntwindow.c b/finch/libgnt/gntwindow.c
index f4ea3c6e70..eaeab91c71 100644
--- a/finch/libgnt/gntwindow.c
+++ b/finch/libgnt/gntwindow.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -131,7 +131,7 @@ gnt_window_init(GTypeInstance *instance, gpointer class)
* GntWindow API
*****************************************************************************/
GType
-gnt_window_get_gtype(void)
+gnt_window_get_type(void)
{
static GType type = 0;
diff --git a/finch/libgnt/gntwindow.h b/finch/libgnt/gntwindow.h
index 3a9c57db7a..c83eedff7e 100644
--- a/finch/libgnt/gntwindow.h
+++ b/finch/libgnt/gntwindow.h
@@ -1,7 +1,3 @@
-/**
- * @file gntwindow.h Window API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,6 +22,12 @@
#ifndef GNT_WINDOW_H
#define GNT_WINDOW_H
+/**
+ * SECTION:gntwindow
+ * @section_id: libgnt-gntwindow
+ * @short_description: <filename>gntwindow.h</filename>
+ * @title: Window
+ */
#include "gnt.h"
#include "gntbox.h"
@@ -33,7 +35,7 @@
#include "gntkeys.h"
#include "gntmenu.h"
-#define GNT_TYPE_WINDOW (gnt_window_get_gtype())
+#define GNT_TYPE_WINDOW (gnt_window_get_type())
#define GNT_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_WINDOW, GntWindow))
#define GNT_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_WINDOW, GntWindowClass))
#define GNT_IS_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_WINDOW))
@@ -65,6 +67,7 @@ struct _GntWindowClass
{
GntBoxClass parent;
+ /*< private >*/
void (*gnt_reserved1)(void);
void (*gnt_reserved2)(void);
void (*gnt_reserved3)(void);
@@ -74,68 +77,77 @@ struct _GntWindowClass
G_BEGIN_DECLS
/**
- * @return GType for GntWindow.
+ * gnt_window_get_type:
+ *
+ * Returns: GType for GntWindow.
*/
-GType gnt_window_get_gtype(void);
+GType gnt_window_get_type(void);
#define gnt_vwindow_new(homo) gnt_window_box_new(homo, TRUE)
#define gnt_hwindow_new(homo) gnt_window_box_new(homo, FALSE)
/**
+ * gnt_window_new:
+ *
* Create a new window.
*
- * @return The newly created window.
+ * Returns: The newly created window.
*/
GntWidget * gnt_window_new(void);
/**
- * Create a new window.
+ * gnt_window_box_new:
+ * @homo: %TRUE if the widgets inside the window should have the same dimensions.
+ * @vert: %TRUE if the widgets inside the window should be stacked vertically.
*
- * @param homo @c TRUE if the widgets inside the window should have the same dimensions.
- * @param vert @c TRUE if the widgets inside the window should be stacked vertically.
+ * Create a new window.
*
- * @return The newly created window.
+ * Returns: The newly created window.
*/
GntWidget * gnt_window_box_new(gboolean homo, gboolean vert);
/**
- * Set the menu for a window.
+ * gnt_window_set_menu:
+ * @window: The window.
+ * @menu: The menu for the window.
*
- * @param window The window.
- * @param menu The menu for the window.
+ * Set the menu for a window.
*/
void gnt_window_set_menu(GntWindow *window, GntMenu *menu);
/**
- * Return the id of a menuitem specified to a keystroke.
+ * gnt_window_get_accel_item:
+ * @window: The window.
+ * @key: The keystroke.
*
- * @param window The window.
- * @param key The keystroke.
+ * Return the id of a menuitem specified to a keystroke.
*
- * @return The id of the menuitem bound to the keystroke, or @c NULL.
+ * Returns: The id of the menuitem bound to the keystroke, or %NULL.
*
- * @since 2.3.0
+ * Since: 2.3.0
*/
const char * gnt_window_get_accel_item(GntWindow *window, const char *key);
/**
- * Maximize a window, either horizontally or vertically, or both.
+ * gnt_window_set_maximize:
+ * @window: The window to maximize.
+ * @maximize: The maximization state of the window.
*
- * @param window The window to maximize.
- * @param maximize The maximization state of the window.
+ * Maximize a window, either horizontally or vertically, or both.
*
- * @since 2.3.0
+ * Since: 2.3.0
*/
void gnt_window_set_maximize(GntWindow *window, GntWindowFlags maximize);
/**
- * Get the maximization state of a window.
+ * gnt_window_get_maximize:
+ * @window: The window.
*
- * @param window The window.
+ * Get the maximization state of a window.
*
- * @return The maximization state of the window.
+ * Returns: The maximization state of the window.
*
- * @since 2.3.0
+ * Since: 2.3.0
*/
GntWindowFlags gnt_window_get_maximize(GntWindow *window);
diff --git a/finch/libgnt/gntwm.c b/finch/libgnt/gntwm.c
index 19c3696e5e..8d51fdfe8a 100644
--- a/finch/libgnt/gntwm.c
+++ b/finch/libgnt/gntwm.c
@@ -1,4 +1,4 @@
-/**
+/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
@@ -37,14 +37,7 @@
#endif
#include <glib.h>
-#if GLIB_CHECK_VERSION(2,6,0)
-# include <glib/gstdio.h>
-#else
-# include <sys/types.h>
-# include <sys/stat.h>
-# include <fcntl.h>
-# define g_fopen open
-#endif
+#include <glib/gstdio.h>
#include <ctype.h>
#include <gmodule.h>
#include <stdlib.h>
@@ -153,7 +146,7 @@ gnt_wm_copy_win(GntWidget *widget, GntNode *node)
}
}
-/**
+/*
* The following is a workaround for a bug in most versions of ncursesw.
* Read about it in: http://article.gmane.org/gmane.comp.lib.ncurses.bugs/2751
*
@@ -337,9 +330,8 @@ refresh_node(GntWidget *widget, GntNode *node, gpointer m)
static void
read_window_positions(GntWM *wm)
{
-#if GLIB_CHECK_VERSION(2,6,0)
GKeyFile *gfile = g_key_file_new();
- char *filename = g_build_filename(g_get_home_dir(), ".gntpositions", NULL);
+ char *filename = g_build_filename(gnt_get_config_dir(), ".gntpositions", NULL);
GError *error = NULL;
char **keys;
gsize nk;
@@ -379,7 +371,6 @@ read_window_positions(GntWM *wm)
g_free(filename);
g_key_file_free(gfile);
-#endif
}
static gboolean check_idle(gpointer n)
@@ -436,6 +427,7 @@ switch_window(GntWM *wm, int direction, gboolean urgent)
w = wm->cws->ordered->data;
orgpos = pos = g_list_index(wm->cws->list, w);
+ g_return_if_fail(pos >= 0);
do {
pos += direction;
@@ -443,7 +435,7 @@ switch_window(GntWM *wm, int direction, gboolean urgent)
if (pos < 0) {
wid = g_list_last(wm->cws->list)->data;
pos = g_list_length(wm->cws->list) - 1;
- } else if (pos >= g_list_length(wm->cws->list)) {
+ } else if ((guint)pos >= g_list_length(wm->cws->list)) {
wid = wm->cws->list->data;
pos = 0;
} else
@@ -1613,7 +1605,7 @@ gnt_wm_class_init(GntWMClass *klass)
* GntWM API
*****************************************************************************/
GType
-gnt_wm_get_gtype(void)
+gnt_wm_get_type(void)
{
static GType type = 0;
@@ -1776,31 +1768,6 @@ match_title(gpointer title, gpointer n, gpointer wid_title)
return FALSE;
}
-#if !GLIB_CHECK_VERSION(2,4,0)
-struct
-{
- gpointer data;
- gpointer value;
-} table_find_data;
-
-static void
-table_find_helper(gpointer key, gpointer value, gpointer data)
-{
- GHRFunc func = data;
- if (func(key, value, table_find_data.data))
- table_find_data.value = value;
-}
-
-static gpointer
-g_hash_table_find(GHashTable * table, GHRFunc func, gpointer data)
-{
- table_find_data.data = data;
- table_find_data.value = NULL;
- g_hash_table_foreach(table, table_find_helper, func);
- return table_find_data.value;
-}
-#endif
-
static GntWS *
new_widget_find_workspace(GntWM *wm, GntWidget *widget)
{
@@ -2155,7 +2122,7 @@ write_already(gpointer data)
FILE *file;
char *filename;
- filename = g_build_filename(g_get_home_dir(), ".gntpositions", NULL);
+ filename = g_build_filename(gnt_get_config_dir(), ".gntpositions", NULL);
file = fopen(filename, "wb");
if (file == NULL) {
@@ -2286,8 +2253,14 @@ gboolean gnt_wm_process_click(GntWM *wm, GntMouseEvent event, int x, int y, GntW
void gnt_wm_raise_window(GntWM *wm, GntWidget *widget)
{
GntWS *ws = gnt_wm_widget_find_workspace(wm, widget);
+
+ g_return_if_fail(wm != NULL);
+
if (wm->cws != ws)
gnt_wm_switch_workspace(wm, g_list_index(wm->workspaces, ws));
+
+ g_return_if_fail(wm->cws != NULL);
+
if (widget != wm->cws->ordered->data) {
GntWidget *wid = wm->cws->ordered->data;
wm->cws->ordered = g_list_bring_to_front(wm->cws->ordered, widget);
diff --git a/finch/libgnt/gntwm.h b/finch/libgnt/gntwm.h
index 4d8ace2428..cc97097dda 100644
--- a/finch/libgnt/gntwm.h
+++ b/finch/libgnt/gntwm.h
@@ -1,7 +1,3 @@
-/**
- * @file gntwm.h Window-manager API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,6 +22,12 @@
#ifndef GNTWM_H
#define GNTWM_H
+/**
+ * SECTION:gntwm
+ * @section_id: libgnt-gntwm
+ * @short_description: <filename>gntwm.h</filename>
+ * @title: Window-manager API
+ */
#include "gntwidget.h"
#include "gntmenu.h"
@@ -34,14 +36,14 @@
#include <panel.h>
#include <time.h>
-#define GNT_TYPE_WM (gnt_wm_get_gtype())
+#define GNT_TYPE_WM (gnt_wm_get_type())
#define GNT_WM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_WM, GntWM))
#define GNT_WM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_WM, GntWMClass))
#define GNT_IS_WM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_WM))
#define GNT_IS_WM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_WM))
#define GNT_WM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_WM, GntWMClass))
-typedef enum _GntKeyPressMode
+typedef enum
{
GNT_KP_MODE_NORMAL,
GNT_KP_MODE_RESIZE,
@@ -49,7 +51,9 @@ typedef enum _GntKeyPressMode
GNT_KP_MODE_WAIT_ON_CHILD
} GntKeyPressMode;
-typedef struct _GntNode
+typedef struct _GntNode GntNode;
+
+struct _GntNode
{
GntWidget *me;
@@ -57,7 +61,7 @@ typedef struct _GntNode
int scroll;
PANEL *panel;
GntWS *ws;
-} GntNode;
+};
typedef struct _GntWM GntWM;
@@ -68,6 +72,8 @@ typedef struct _GntPosition
} GntPosition;
/**
+ * GntAction:
+ *
* An application can register actions which will show up in a 'start-menu' like popup
*/
typedef struct _GntAction
@@ -76,48 +82,52 @@ typedef struct _GntAction
void (*callback)(void);
} GntAction;
+typedef struct _GntListWindow {
+ GntWidget *window;
+ GntWidget *tree;
+} GntListWindow;
+
+/**
+ * GntWM:
+ * @acts: List of actions
+ * @menu: Currently active menu. There can be at most one menu at a time on the
+ * screen. If there is a menu being displayed, then all the keystrokes
+ * will be sent to the menu until it is closed, either when the user
+ * activates a menuitem, or presses Escape to cancel the menu.
+ * @event_stack: Will be set to %TRUE when a user-event, ie. a mouse-click or a
+ * key-press is being processed. This variable will be used to
+ * determine whether to give focus to a new window.
+ */
struct _GntWM
{
GntBindable inherit;
+ /*< public >*/
GMainLoop *loop;
GList *workspaces;
GList *tagged; /* tagged windows */
GntWS *cws;
- struct {
- GntWidget *window;
- GntWidget *tree;
- } _list,
- *windows, /* Window-list window */
- *actions; /* Action-list window */
+ GntListWindow _list;
+ GntListWindow *windows; /* Window-list window */
+ GntListWindow *actions; /* Action-list window */
GHashTable *nodes; /* GntWidget -> GntNode */
GHashTable *name_places; /* window name -> ws*/
GHashTable *title_places; /* window title -> ws */
- GList *acts; /* List of actions */
+ GList *acts;
- /**
- * There can be at most one menu at a time on the screen.
- * If there is a menu being displayed, then all the keystrokes will be sent to
- * the menu until it is closed, either when the user activates a menuitem, or
- * presses Escape to cancel the menu.
- */
- GntMenu *menu; /* Currently active menu */
+ GntMenu *menu;
- /**
- * 'event_stack' will be set to TRUE when a user-event, ie. a mouse-click
- * or a key-press is being processed. This variable will be used to determine
- * whether to give focus to a new window.
- */
gboolean event_stack;
GntKeyPressMode mode;
GHashTable *positions;
+ /*< private >*/
void *res1;
void *res2;
void *res3;
@@ -184,151 +194,191 @@ struct _GntWMClass
G_BEGIN_DECLS
/**
- * @return GType for GntWM.
+ * gnt_wm_get_type:
+ *
+ * Returns: GType for GntWM.
*/
-GType gnt_wm_get_gtype(void);
+GType gnt_wm_get_type(void);
/**
+ * gnt_wm_add_workspace:
+ * @wm: The window-manager.
+ * @ws: The workspace to add.
+ *
* Add a workspace.
- * @param wm The window-manager.
- * @param ws The workspace to add.
*/
void gnt_wm_add_workspace(GntWM *wm, GntWS *ws);
/**
+ * gnt_wm_switch_workspace:
+ * @wm: The window-manager.
+ * @n: Index of the workspace to switch to.
+ *
* Switch to a workspace.
- * @param wm The window-manager.
- * @param n Index of the workspace to switch to.
*
- * @return @c TRUE if the switch was successful.
+ * Returns: %TRUE if the switch was successful.
*/
gboolean gnt_wm_switch_workspace(GntWM *wm, gint n);
/**
+ * gnt_wm_switch_workspace_prev:
+ * @wm: The window-manager.
+ *
* Switch to the previous workspace from the current one.
- * @param wm The window-manager.
*/
gboolean gnt_wm_switch_workspace_prev(GntWM *wm);
/**
+ * gnt_wm_switch_workspace_next:
+ * @wm: The window-manager.
+ *
* Switch to the next workspace from the current one.
- * @param wm The window-manager.
*/
gboolean gnt_wm_switch_workspace_next(GntWM *wm);
/**
+ * gnt_wm_widget_move_workspace:
+ * @wm: The window manager.
+ * @neww: The new workspace.
+ * @widget: The widget to move.
+ *
* Move a window to a specific workspace.
- * @param wm The window manager.
- * @param neww The new workspace.
- * @param widget The widget to move.
*/
void gnt_wm_widget_move_workspace(GntWM *wm, GntWS *neww, GntWidget *widget);
/**
+ * gnt_wm_set_workspaces:
+ * @wm: The window manager.
+ * @workspaces: (element-type Gnt.WS): The list of workspaces.
+ *
* Set the list of workspaces .
- * @param wm The window manager.
- * @param workspaces The list of workspaces.
*/
void gnt_wm_set_workspaces(GntWM *wm, GList *workspaces);
/**
+ * gnt_wm_widget_find_workspace:
+ * @wm: The window-manager.
+ * @widget: The widget to find.
+ *
* Find the workspace that contains a specific widget.
- * @param wm The window-manager.
- * @param widget The widget to find.
- * @return The workspace that has the widget.
+ *
+ * Returns: (transfer none): The workspace that has the widget.
*/
GntWS *gnt_wm_widget_find_workspace(GntWM *wm, GntWidget *widget);
/**
- * Process a new window.
+ * gnt_wm_new_window:
+ * @wm: The window-manager.
+ * @widget: The new window.
*
- * @param wm The window-manager.
- * @param widget The new window.
+ * Process a new window.
*/
void gnt_wm_new_window(GntWM *wm, GntWidget *widget);
/**
+ * gnt_wm_window_decorate:
+ * @wm: The window-manager.
+ * @widget: The widget to decorate.
+ *
* Decorate a window.
- * @param wm The window-manager.
- * @param widget The widget to decorate.
*/
void gnt_wm_window_decorate(GntWM *wm, GntWidget *widget);
/**
+ * gnt_wm_window_close:
+ * @wm: The window-manager.
+ * @widget: The window to close.
+ *
* Close a window.
- * @param wm The window-manager.
- * @param widget The window to close.
*/
void gnt_wm_window_close(GntWM *wm, GntWidget *widget);
/**
- * Process input.
+ * gnt_wm_process_input:
+ * @wm: The window-manager.
+ * @string: The input string to process.
*
- * @param wm The window-manager.
- * @param string The input string to process.
+ * Process input.
*
- * @return @c TRUE of the string was processed, @c FALSE otherwise.
+ * Returns: %TRUE of the string was processed, %FALSE otherwise.
*/
gboolean gnt_wm_process_input(GntWM *wm, const char *string);
/**
+ * gnt_wm_process_click:
+ * @wm: The window manager.
+ * @event: The mouse event.
+ * @x: The x-coordinate of the mouse.
+ * @y: The y-coordinate of the mouse.
+ * @widget: The widget under the mouse.
+ *
* Process a click event.
- * @param wm The window manager.
- * @param event The mouse event.
- * @param x The x-coordinate of the mouse.
- * @param y The y-coordinate of the mouse.
- * @param widget The widget under the mouse.
*
- * @return @c TRUE if the event was handled, @c FALSE otherwise.
+ * Returns: %TRUE if the event was handled, %FALSE otherwise.
*/
gboolean gnt_wm_process_click(GntWM *wm, GntMouseEvent event, int x, int y, GntWidget *widget);
/**
+ * gnt_wm_resize_window:
+ * @wm: The window manager.
+ * @widget: The window to resize.
+ * @width: The desired width of the window.
+ * @height: The desired height of the window.
+ *
* Resize a window.
- * @param wm The window manager.
- * @param widget The window to resize.
- * @param width The desired width of the window.
- * @param height The desired height of the window.
*/
void gnt_wm_resize_window(GntWM *wm, GntWidget *widget, int width, int height);
/**
+ * gnt_wm_move_window:
+ * @wm: The window manager.
+ * @widget: The window to move.
+ * @x: The desired x-coordinate of the window.
+ * @y: The desired y-coordinate of the window.
+ *
* Move a window.
- * @param wm The window manager.
- * @param widget The window to move.
- * @param x The desired x-coordinate of the window.
- * @param y The desired y-coordinate of the window.
*/
void gnt_wm_move_window(GntWM *wm, GntWidget *widget, int x, int y);
/**
+ * gnt_wm_update_window:
+ * @wm: The window-manager.
+ * @widget: The window to update.
+ *
* Update a window.
- * @param wm The window-manager.
- * @param widget The window to update.
*/
void gnt_wm_update_window(GntWM *wm, GntWidget *widget);
/**
+ * gnt_wm_raise_window:
+ * @wm: The window-manager.
+ * @widget: The window to raise.
+ *
* Raise a window.
- * @param wm The window-manager.
- * @param widget The window to raise.
*/
void gnt_wm_raise_window(GntWM *wm, GntWidget *widget);
/**
- * @internal
+ * gnt_wm_set_event_stack:
+ *
+ * Internal function -- do not use.
*/
void gnt_wm_set_event_stack(GntWM *wm, gboolean set);
/**
- * @internal
+ * gnt_wm_copy_win:
+ *
+ * Internal function -- do not use.
*/
void gnt_wm_copy_win(GntWidget *widget, GntNode *node);
/**
- * @return The idle time of the user.
+ * gnt_wm_get_idle_time:
+ *
+ * Returns: The idle time of the user.
*/
time_t gnt_wm_get_idle_time(void);
G_END_DECLS
+
#endif
diff --git a/finch/libgnt/gntws.c b/finch/libgnt/gntws.c
index d8d35c1ed0..91298d3a7b 100644
--- a/finch/libgnt/gntws.c
+++ b/finch/libgnt/gntws.c
@@ -162,7 +162,7 @@ gnt_ws_show(GntWS *ws, GHashTable *nodes)
}
GType
-gnt_ws_get_gtype(void)
+gnt_ws_get_type(void)
{
static GType type = 0;
diff --git a/finch/libgnt/gntws.h b/finch/libgnt/gntws.h
index 795921b196..d78aef6502 100644
--- a/finch/libgnt/gntws.h
+++ b/finch/libgnt/gntws.h
@@ -1,7 +1,3 @@
-/**
- * @file gntws.h Workspace API
- * @ingroup gnt
- */
/*
* GNT - The GLib Ncurses Toolkit
*
@@ -26,12 +22,18 @@
#ifndef GNTWS_H
#define GNTWS_H
+/**
+ * SECTION:gntws
+ * @section_id: libgnt-gntws
+ * @short_description: <filename>gntws.h</filename>
+ * @title: Workspace API
+ */
#include "gntwidget.h"
#include <panel.h>
-#define GNT_TYPE_WS (gnt_ws_get_gtype())
+#define GNT_TYPE_WS (gnt_ws_get_type())
#define GNT_WS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_WS, GntWS))
#define GNT_IS_WS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_WS))
#define GNT_IS_WS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_WS))
@@ -70,110 +72,123 @@ struct _GntWSClass
G_BEGIN_DECLS
/**
- * @return The GType for GntWS.
+ * gnt_ws_get_type:
+ *
+ * Returns: The GType for GntWS.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
-GType gnt_ws_get_gtype(void);
+GType gnt_ws_get_type(void);
/**
- * Create a new workspace with the specified name.
+ * gnt_ws_new:
+ * @name: The desired name of the workspace, or %NULL.
*
- * @param name The desired name of the workspace, or @c NULL.
+ * Create a new workspace with the specified name.
*
- * @return The newly created workspace.
+ * Returns: The newly created workspace.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
GntWS *gnt_ws_new(const char *name);
/**
- * Set the name of a workspace.
+ * gnt_ws_set_name:
+ * @ws: The workspace to rename.
+ * @name: The new name of the workspace.
*
- * @param ws The workspace to rename.
- * @param name The new name of the workspace.
+ * Set the name of a workspace.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
void gnt_ws_set_name(GntWS *ws, const gchar *name);
/**
- * Add a widget to a workspace.
+ * gnt_ws_add_widget:
+ * @ws: The workspace.
+ * @widget: The widget to add.
*
- * @param ws The workspace.
- * @param widget The widget to add.
+ * Add a widget to a workspace.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
void gnt_ws_add_widget(GntWS *ws, GntWidget *widget);
/**
- * Remove a widget from a workspace.
+ * gnt_ws_remove_widget:
+ * @ws: The workspace
+ * @widget: The widget to remove from the workspace.
*
- * @param ws The workspace
- * @param widget The widget to remove from the workspace.
+ * Remove a widget from a workspace.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
void gnt_ws_remove_widget(GntWS *ws, GntWidget *widget);
/**
- * Hide a widget in a workspace.
+ * gnt_ws_widget_hide:
+ * @widget: The widget to hide.
+ * @nodes: A hashtable containing information about the widgets.
*
- * @param widget The widget to hide.
- * @param nodes A hashtable containing information about the widgets.
+ * Hide a widget in a workspace.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
void gnt_ws_widget_hide(GntWidget *widget, GHashTable *nodes);
/**
- * Show a widget in a workspace.
+ * gnt_ws_widget_show:
+ * @widget: The widget to show.
+ * @nodes: A hashtable containing information about the widgets.
*
- * @param widget The widget to show.
- * @param nodes A hashtable containing information about the widgets.
+ * Show a widget in a workspace.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
void gnt_ws_widget_show(GntWidget *widget, GHashTable *nodes);
/**
- * Draw the taskbar in a workspace.
+ * gnt_ws_draw_taskbar:
+ * @ws: The workspace.
+ * @reposition: Whether the workspace should reposition the taskbar.
*
- * @param ws The workspace.
- * @param reposition Whether the workspace should reposition the taskbar.
+ * Draw the taskbar in a workspace.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
void gnt_ws_draw_taskbar(GntWS *ws, gboolean reposition);
/**
- * Hide a workspace.
+ * gnt_ws_hide:
+ * @ws: The workspace to hide.
+ * @table: A hashtable containing information about the widgets.
*
- * @param ws The workspace to hide.
- * @param table A hashtable containing information about the widgets.
+ * Hide a workspace.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
void gnt_ws_hide(GntWS *ws, GHashTable *table);
/**
- * Show a workspace.
+ * gnt_ws_show:
+ * @ws: The workspace to hide.
+ * @table: A hashtable containing information about the widgets.
*
- * @param ws The workspace to hide.
- * @param table A hashtable containing information about the widgets.
+ * Show a workspace.
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
void gnt_ws_show(GntWS *ws, GHashTable *table);
/**
+ * gnt_ws_get_name:
+ * @ws: The workspace.
+ *
* Get the name of a workspace.
*
- * @param ws The workspace.
- * @return The name of the workspace (can be @c NULL).
+ * Returns: The name of the workspace (can be %NULL).
*
- * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ * Since: 2.0.0 (gnt), 2.1.0 (pidgin)
*/
const char * gnt_ws_get_name(GntWS *ws);
diff --git a/finch/libgnt/libgnt_winres.rc.in b/finch/libgnt/libgnt_winres.rc.in
new file mode 100644
index 0000000000..55b831b947
--- /dev/null
+++ b/finch/libgnt/libgnt_winres.rc.in
@@ -0,0 +1,30 @@
+#include <winver.h>
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION @GNT_MAJOR_VERSION@,@GNT_MINOR_VERSION@,@GNT_MICRO_VERSION@,0
+ PRODUCTVERSION @GNT_MAJOR_VERSION@,@GNT_MINOR_VERSION@,@GNT_MICRO_VERSION@,0
+ FILEFLAGSMASK 0
+ FILEFLAGS 0
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_APP
+ FILESUBTYPE VFT2_UNKNOWN
+ BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904B0"
+ BEGIN
+ VALUE "CompanyName", "The Pidgin developer community"
+ VALUE "FileDescription", "Glib ncurses toolkit"
+ VALUE "FileVersion", "@GNT_VERSION@"
+ VALUE "InternalName", "libgnt"
+ VALUE "LegalCopyright", "Copyright (C) 1998-2014 The Pidgin developer community (See the COPYRIGHT file in the source distribution)."
+ VALUE "OriginalFilename", "libgnt-@GNT_API_VERSION@.dll"
+ VALUE "ProductName", "libgnt"
+ VALUE "ProductVersion", "@GNT_VERSION@"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+ END
diff --git a/finch/libgnt/wms/Makefile.am b/finch/libgnt/wms/Makefile.am
index b72069a3d8..9c4dba87a9 100644
--- a/finch/libgnt/wms/Makefile.am
+++ b/finch/libgnt/wms/Makefile.am
@@ -6,8 +6,8 @@ else
purple_wms =
endif
-s_la_LDFLAGS = -module -avoid-version
-irssi_la_LDFLAGS = -module -avoid-version
+s_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+irssi_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
plugin_LTLIBRARIES = \
$(wms) \
@@ -29,7 +29,6 @@ s_la_LIBADD = \
EXTRA_DIST =
AM_CPPFLAGS = \
- -DDATADIR=\"$(datadir)\" \
-I$(top_srcdir)/libpurple \
-I$(top_srcdir)/finch \
-I$(top_srcdir)/finch/libgnt \
diff --git a/finch/libgnt/wms/irssi.c b/finch/libgnt/wms/irssi.c
index 22d74b3207..819e8b872b 100644
--- a/finch/libgnt/wms/irssi.c
+++ b/finch/libgnt/wms/irssi.c
@@ -43,7 +43,7 @@
#include "gntwindow.h"
#include "gntlabel.h"
-#define TYPE_IRSSI (irssi_get_gtype())
+#define TYPE_IRSSI (irssi_get_type())
typedef struct _Irssi
{
@@ -60,7 +60,7 @@ typedef struct _IrssiClass
GntWMClass inherit;
} IrssiClass;
-GType irssi_get_gtype(void);
+GType irssi_get_type(void);
void gntwm_init(GntWM **wm);
static void (*org_new_window)(GntWM *wm, GntWidget *win);
@@ -155,6 +155,9 @@ irssi_new_window(GntWM *wm, GntWidget *win)
const char *name;
int x, y, w, h;
+ g_return_if_fail(win != NULL);
+ g_return_if_fail(wm != NULL);
+
name = gnt_widget_get_name(win);
if (!name || !strstr(name, "conversation-window")) {
if (!GNT_IS_MENU(win) && !GNT_WIDGET_IS_FLAG_SET(win, GNT_WIDGET_TRANSIENT)) {
@@ -367,7 +370,7 @@ void gntwm_init(GntWM **wm)
irssi->buddylistwidth = 0;
}
-GType irssi_get_gtype(void)
+GType irssi_get_type(void)
{
static GType type = 0;
diff --git a/finch/libgnt/wms/s.c b/finch/libgnt/wms/s.c
index a326815bab..8ab8ebabd8 100644
--- a/finch/libgnt/wms/s.c
+++ b/finch/libgnt/wms/s.c
@@ -11,9 +11,9 @@
#include "gntwindow.h"
#include "gntlabel.h"
-#include "blist.h"
+#include "buddylist.h"
-#define TYPE_S (s_get_gtype())
+#define TYPE_S (s_get_type())
#ifdef _S
#undef _S
@@ -29,7 +29,7 @@ typedef struct _SClass
GntWMClass inherit;
} SClass;
-GType s_get_gtype(void);
+GType s_get_type(void);
void gntwm_init(GntWM **wm);
static void (*org_new_window)(GntWM *wm, GntWidget *win);
@@ -84,6 +84,9 @@ s_new_window(GntWM *wm, GntWidget *win)
const char *name;
gboolean blist = FALSE;
+ g_return_if_fail(win != NULL);
+ g_return_if_fail(wm != NULL);
+
if (!GNT_IS_MENU(win)) {
getmaxyx(stdscr, maxy, maxx);
@@ -197,7 +200,7 @@ void gntwm_init(GntWM **wm)
*wm = g_object_new(TYPE_S, NULL);
}
-GType s_get_gtype(void)
+GType s_get_type(void)
{
static GType type = 0;
diff --git a/finch/plugins/Makefile.am b/finch/plugins/Makefile.am
index 9258ae1706..cc4fb0e6ae 100644
--- a/finch/plugins/Makefile.am
+++ b/finch/plugins/Makefile.am
@@ -1,9 +1,9 @@
-gntclipboard_la_LDFLAGS = -module -avoid-version
-gntgf_la_LDFLAGS = -module -avoid-version
-gnthistory_la_LDFLAGS = -module -avoid-version
-gntlastlog_la_LDFLAGS = -module -avoid-version
-gnttinyurl_la_LDFLAGS = -module -avoid-version
-grouping_la_LDFLAGS = -module -avoid-version
+gntclipboard_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+gntgf_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+gnthistory_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+gntlastlog_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+gnttinyurl_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+grouping_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
if PLUGINS
@@ -15,7 +15,7 @@ plugin_LTLIBRARIES = \
gnttinyurl.la \
grouping.la
-plugindir = $(libdir)/finch
+plugindir = @FINCH_PLUGINDIR@
gntclipboard_la_SOURCES = gntclipboard.c
gntgf_la_SOURCES = gntgf.c
@@ -27,19 +27,18 @@ grouping_la_SOURCES = grouping.c
gntclipboard_la_CFLAGS = $(X11_CFLAGS)
gntgf_la_CFLAGS = $(X11_CFLAGS)
-gntclipboard_la_LIBADD = $(GLIB_LIBS) $(X11_LIBS)
-gntgf_la_LIBADD = $(GLIB_LIBS) $(X11_LIBS) $(top_builddir)/finch/libgnt/libgnt.la
-gnthistory_la_LIBADD = $(GLIB_LIBS)
-gntlastlog_la_LIBADD = $(GLIB_LIBS)
-gnttinyurl_la_LIBADD = $(GLIB_LIBS)
-grouping_la_LIBADD = $(GLIB_LIBS) $(top_builddir)/finch/libgnt/libgnt.la
+gntclipboard_la_LIBADD = @FINCH_LIBS@ $(X11_LIBS)
+gntgf_la_LIBADD = @FINCH_LIBS@ $(X11_LIBS)
+gnthistory_la_LIBADD = @FINCH_LIBS@
+gntlastlog_la_LIBADD = @FINCH_LIBS@
+gnttinyurl_la_LIBADD = @FINCH_LIBS@
+grouping_la_LIBADD = @FINCH_LIBS@
endif # PLUGINS
EXTRA_DIST = pietray.py
AM_CPPFLAGS = \
- -DDATADIR=\"$(datadir)\" \
-I$(top_builddir)/libpurple \
-I$(top_srcdir)/libpurple \
-I$(top_srcdir) \
@@ -57,7 +56,7 @@ AM_CPPFLAGS = \
SUFFIXES = .c .so
.c.so:
$(LIBTOOL) --mode=compile $(CC) -DHAVE_CONFIG_H -I$(top_builddir) $(AM_CPPFLAGS) $(CFLAGS) -c $< -o tmp$@.lo $(PLUGIN_CFLAGS)
- $(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o libtmp$@.la -rpath $(plugindir) tmp$@.lo $(LIBS) $(LDFLAGS) -module -avoid-version $(PLUGIN_LIBS)
+ $(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o libtmp$@.la -rpath $(plugindir) tmp$@.lo $(LIBS) $(LDFLAGS) -module @PLUGIN_LDFLAGS@ $(PLUGIN_LIBS)
@rm -f tmp$@.lo tmp$@.o libtmp$@.la
@cp .libs/libtmp$@.so.so $@
@rm -rf .libs/libtmp$@.*
diff --git a/finch/plugins/gntclipboard.c b/finch/plugins/gntclipboard.c
index fea6289cca..c6003a5886 100644
--- a/finch/plugins/gntclipboard.c
+++ b/finch/plugins/gntclipboard.c
@@ -39,6 +39,7 @@
#include <version.h>
#include <debug.h>
#include <notify.h>
+#include <gnt.h>
#include <gntwm.h>
#include <gntplugin.h>
@@ -113,20 +114,20 @@ plugin_load(PurplePlugin *plugin)
if (!XOpenDisplay(NULL)) {
purple_debug_warning("gntclipboard", "Couldn't find X display\n");
purple_notify_error(NULL, _("Error"), _("Error loading the plugin."),
- _("Couldn't find X display"));
+ _("Couldn't find X display"), NULL);
return FALSE;
}
if (!getenv("WINDOWID")) {
purple_debug_warning("gntclipboard", "Couldn't find window\n");
purple_notify_error(NULL, _("Error"), _("Error loading the plugin."),
- _("Couldn't find window"));
+ _("Couldn't find window"), NULL);
return FALSE;
}
sig_handle = g_signal_connect(G_OBJECT(gnt_get_clipboard()), "clipboard_changed", G_CALLBACK(clipboard_changed), NULL);
return TRUE;
#else
purple_notify_error(NULL, _("Error"), _("Error loading the plugin."),
- _("This plugin cannot be loaded because it was not built with X11 support."));
+ _("This plugin cannot be loaded because it was not built with X11 support."), NULL);
return FALSE;
#endif
}
diff --git a/finch/plugins/gntgf.c b/finch/plugins/gntgf.c
index 9d7d726d86..229d0a9bf2 100644
--- a/finch/plugins/gntgf.c
+++ b/finch/plugins/gntgf.c
@@ -44,7 +44,7 @@
#include <plugin.h>
#include <version.h>
-#include <blist.h>
+#include <buddylist.h>
#include <conversation.h>
#include <debug.h>
#include <eventloop.h>
@@ -168,7 +168,7 @@ notify(PurpleConversation *conv, const char *fmt, ...)
beep();
if (conv != NULL) {
- FinchConv *fc = conv->ui_data;
+ FinchConv *fc = FINCH_CONV(conv);
if (gnt_widget_has_focus(fc->window))
return;
}
@@ -241,22 +241,20 @@ buddy_signed_off(PurpleBuddy *buddy, gpointer null)
static void
received_im_msg(PurpleAccount *account, const char *sender, const char *msg,
- PurpleConversation *conv, PurpleMessageFlags flags, gpointer null)
+ PurpleIMConversation *im, PurpleMessageFlags flags, gpointer null)
{
if (purple_prefs_get_bool(PREFS_EVENT_IM_MSG))
- notify(conv, _("%s sent you a message"), sender);
+ notify(PURPLE_CONVERSATION(im), _("%s sent you a message"), sender);
}
static void
received_chat_msg(PurpleAccount *account, const char *sender, const char *msg,
- PurpleConversation *conv, PurpleMessageFlags flags, gpointer null)
+ PurpleChatConversation *chat, PurpleMessageFlags flags, gpointer null)
{
const char *nick;
+ PurpleConversation *conv = PURPLE_CONVERSATION(chat);
- if (flags & PURPLE_MESSAGE_WHISPER)
- return;
-
- nick = PURPLE_CONV_CHAT(conv)->nick;
+ nick = purple_chat_conversation_get_nick(chat);
if (g_utf8_collate(sender, nick) == 0)
return;
diff --git a/finch/plugins/gnthistory.c b/finch/plugins/gnthistory.c
index b5514f0b26..687e6fdbb0 100644
--- a/finch/plugins/gnthistory.c
+++ b/finch/plugins/gnthistory.c
@@ -43,7 +43,6 @@ static void historize(PurpleConversation *c)
{
PurpleAccount *account = purple_conversation_get_account(c);
const char *name = purple_conversation_get_name(c);
- PurpleConversationType convtype;
GList *logs = NULL;
const char *alias = name;
PurpleLogReadFlags flags;
@@ -51,8 +50,7 @@ static void historize(PurpleConversation *c)
char *header;
PurpleMessageFlags mflag;
- convtype = purple_conversation_get_type(c);
- if (convtype == PURPLE_CONV_TYPE_IM) {
+ if (PURPLE_IS_IM_CONVERSATION(c)) {
GSList *buddies;
GSList *cur;
FinchConv *fc = FINCH_CONV(c);
@@ -65,11 +63,11 @@ static void historize(PurpleConversation *c)
return;
/* Find buddies for this conversation. */
- buddies = purple_find_buddies(account, name);
+ buddies = purple_blist_find_buddies(account, name);
/* If we found at least one buddy, save the first buddy's alias. */
if (buddies != NULL)
- alias = purple_buddy_get_contact_alias((PurpleBuddy *)buddies->data);
+ alias = purple_buddy_get_contact_alias(PURPLE_BUDDY(buddies->data));
for (cur = buddies; cur != NULL; cur = cur->next) {
PurpleBlistNode *node = cur->data;
@@ -78,7 +76,7 @@ static void historize(PurpleConversation *c)
(purple_blist_node_get_sibling_next(node) != NULL))) {
PurpleBlistNode *node2;
- alias = purple_buddy_get_contact_alias((PurpleBuddy *)node);
+ alias = purple_buddy_get_contact_alias(PURPLE_BUDDY(node));
/* We've found a buddy that matches this conversation. It's part of a
* PurpleContact with more than one PurpleBuddy. Loop through the PurpleBuddies
@@ -87,8 +85,8 @@ static void historize(PurpleConversation *c)
node2 != NULL ; node2 = purple_blist_node_get_sibling_next(node2)) {
logs = g_list_concat(
purple_log_get_logs(PURPLE_LOG_IM,
- purple_buddy_get_name((PurpleBuddy *)node2),
- purple_buddy_get_account((PurpleBuddy *)node2)),
+ purple_buddy_get_name(PURPLE_BUDDY(node2)),
+ purple_buddy_get_account(PURPLE_BUDDY(node2))),
logs);
}
break;
@@ -100,7 +98,7 @@ static void historize(PurpleConversation *c)
logs = purple_log_get_logs(PURPLE_LOG_IM, name, account);
else
logs = g_list_sort(logs, purple_log_compare);
- } else if (convtype == PURPLE_CONV_TYPE_CHAT) {
+ } else if (PURPLE_IS_CHAT_CONVERSATION(c)) {
/* If we're not logging, don't show anything.
* Otherwise, we might show a very old log. */
if (!purple_prefs_get_bool("/purple/logging/log_chats"))
@@ -117,15 +115,15 @@ static void historize(PurpleConversation *c)
header = g_strdup_printf(_("<b>Conversation with %s on %s:</b><br>"), alias,
purple_date_format_full(localtime(&((PurpleLog *)logs->data)->time)));
- purple_conversation_write(c, "", header, mflag, time(NULL));
+ purple_conversation_write_system_message(c, header, mflag);
g_free(header);
if (flags & PURPLE_LOG_READ_NO_NEWLINE)
purple_str_strip_char(history, '\n');
- purple_conversation_write(c, "", history, mflag, time(NULL));
+ purple_conversation_write_system_message(c, history, mflag);
g_free(history);
- purple_conversation_write(c, "", "<hr>", mflag, time(NULL));
+ purple_conversation_write_system_message(c, "<hr>", mflag);
g_list_foreach(logs, (GFunc)purple_log_free, NULL);
g_list_free(logs);
@@ -180,7 +178,7 @@ history_prefs_check(PurplePlugin *plugin)
fields,
_("OK"), G_CALLBACK(finch_request_save_in_prefs),
_("Cancel"), NULL,
- NULL, NULL, NULL, plugin);
+ NULL, plugin);
}
}
diff --git a/finch/plugins/gnttinyurl.c b/finch/plugins/gnttinyurl.c
index 7e8a5b9ec7..c9c56c8dd2 100644
--- a/finch/plugins/gnttinyurl.c
+++ b/finch/plugins/gnttinyurl.c
@@ -29,6 +29,7 @@
#include <conversation.h>
+#include <http.h>
#include <signals.h>
#include <glib.h>
@@ -47,9 +48,11 @@
#include <gntwindow.h>
static int tag_num = 0;
+static GHashTable *tinyurl_cache = NULL;
typedef struct
{
+ gchar *original_url;
PurpleConversation *conv;
gchar *tag;
int num;
@@ -202,17 +205,29 @@ static GList *extract_urls(const char *text)
return ret;
}
-static void url_fetched(PurpleUtilFetchUrlData *url_data, gpointer cb_data,
- const gchar *url_text, gsize len, const gchar *error_message)
+
+
+static void url_fetched(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _data)
{
- CbInfo *data = (CbInfo *)cb_data;
+ CbInfo *data = (CbInfo *)_data;
PurpleConversation *conv = data->conv;
- GList *convs = purple_get_conversations();
+ GList *convs = purple_conversations_get_all();
+ const gchar *url;
+
+ if (purple_http_response_is_successful(response)) {
+ url = purple_http_response_get_data(response, NULL);
+ g_hash_table_insert(tinyurl_cache, data->original_url, g_strdup(url));
+ } else {
+ url = _("Error while querying TinyURL");
+ g_free(data->original_url);
+ }
+
/* ensure the conversation still exists */
for (; convs; convs = convs->next) {
if ((PurpleConversation *)(convs->data) == conv) {
FinchConv *fconv = FINCH_CONV(conv);
- gchar *str = g_strdup_printf("[%d] %s", data->num, url_text);
+ gchar *str = g_strdup_printf("[%d] %s", data->num, url);
GntTextView *tv = GNT_TEXT_VIEW(fconv->tv);
gnt_text_view_tag_change(tv, data->tag, str, FALSE);
g_free(str);
@@ -231,26 +246,24 @@ static void free_urls(gpointer data, gpointer null)
g_free(data);
}
-static gboolean writing_msg(PurpleAccount *account, char *sender, char **message,
- PurpleConversation *conv, PurpleMessageFlags flags)
+static gboolean writing_msg(PurpleConversation *conv, PurpleMessage *msg, gpointer _unused)
{
GString *t;
GList *iter, *urls, *next;
int c = 0;
- if ((flags & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_INVISIBLE)))
+ if (purple_message_get_flags(msg) & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_INVISIBLE))
return FALSE;
- urls = purple_conversation_get_data(conv, "TinyURLs");
+ 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);
- urls = extract_urls(*message);
+ urls = extract_urls(purple_message_get_contents(msg));
if (!urls)
return FALSE;
- t = g_string_new(*message);
- g_free(*message);
+ t = g_string_new(g_strdup(purple_message_get_contents(msg)));
for (iter = urls; iter; iter = next) {
next = iter->next;
if (g_utf8_strlen((char *)iter->data, -1) >= purple_prefs_get_int(PREF_LENGTH)) {
@@ -274,25 +287,27 @@ static gboolean writing_msg(PurpleAccount *account, char *sender, char **message
urls = g_list_delete_link(urls, iter);
}
}
- *message = t->str;
- g_string_free(t, FALSE);
- if (conv == NULL)
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender);
- purple_conversation_set_data(conv, "TinyURLs", urls);
+ purple_message_set_contents(msg, t->str);
+ g_string_free(t, TRUE);
+ if (conv != NULL)
+ g_object_set_data(G_OBJECT(conv), "TinyURLs", urls);
return FALSE;
}
-static void wrote_msg(PurpleAccount *account, char *sender, char *message,
- PurpleConversation *conv, PurpleMessageFlags flags)
+static void wrote_msg(PurpleConversation *conv, PurpleMessage *pmsg,
+ gpointer _unused)
{
GList *urls;
- urls = purple_conversation_get_data(conv, "TinyURLs");
- if ((flags & PURPLE_MESSAGE_SEND) || urls == NULL)
+ if (purple_message_get_flags(pmsg) & PURPLE_MESSAGE_SEND)
+ return;
+
+ urls = g_object_get_data(G_OBJECT(conv), "TinyURLs");
+ if (urls == NULL)
return;
process_urls(conv, urls);
- purple_conversation_set_data(conv, "TinyURLs", NULL);
+ g_object_set_data(G_OBJECT(conv), "TinyURLs", NULL);
}
/* Frees 'urls' */
@@ -304,23 +319,38 @@ process_urls(PurpleConversation *conv, GList *urls)
FinchConv *fconv = FINCH_CONV(conv);
GntTextView *tv = GNT_TEXT_VIEW(fconv->tv);
- for (iter = urls, c = 0; iter; iter = iter->next) {
+ for (iter = urls, c = 1; iter; iter = iter->next, c++) {
int i;
CbInfo *cbdata;
- gchar *url, *str, *tmp;
+ gchar *url, *str;
+ gchar *original_url;
+ const gchar *tiny_url;
+
+ i = gnt_text_view_get_lines_below(tv);
+
+ original_url = purple_unescape_html((char *)iter->data);
+ tiny_url = g_hash_table_lookup(tinyurl_cache, original_url);
+ if (tiny_url) {
+ gchar *str = g_strdup_printf("\n[%d] %s", c, tiny_url);
+
+ g_free(original_url);
+ gnt_text_view_append_text_with_flags(tv, str, GNT_TEXT_FLAG_DIM);
+ if (i == 0)
+ gnt_text_view_scroll(tv, 0);
+ g_free(str);
+ continue;
+ }
cbdata = g_new(CbInfo, 1);
- cbdata->num = ++c;
+ cbdata->num = c;
+ cbdata->original_url = original_url;
cbdata->tag = g_strdup_printf("%s%d", "tiny_", tag_num++);
cbdata->conv = conv;
- tmp = purple_unescape_html((char *)iter->data);
- if (g_ascii_strncasecmp(tmp, "http://", 7) && g_ascii_strncasecmp(tmp, "https://", 8)) {
- url = g_strdup_printf("%shttp%%3A%%2F%%2F%s", purple_prefs_get_string(PREF_URL), purple_url_encode(tmp));
+ if (g_ascii_strncasecmp(original_url, "http://", 7) && g_ascii_strncasecmp(original_url, "https://", 8)) {
+ url = g_strdup_printf("%shttp%%3A%%2F%%2F%s", purple_prefs_get_string(PREF_URL), purple_url_encode(original_url));
} else {
- url = g_strdup_printf("%s%s", purple_prefs_get_string(PREF_URL), purple_url_encode(tmp));
+ url = g_strdup_printf("%s%s", purple_prefs_get_string(PREF_URL), purple_url_encode(original_url));
}
- g_free(tmp);
- purple_util_fetch_url(url, TRUE, "finch", FALSE, url_fetched, cbdata);
- i = gnt_text_view_get_lines_below(tv);
+ 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);
@@ -335,26 +365,44 @@ process_urls(PurpleConversation *conv, GList *urls)
static void
free_conv_urls(PurpleConversation *conv)
{
- GList *urls = purple_conversation_get_data(conv, "TinyURLs");
+ GList *urls = g_object_get_data(G_OBJECT(conv), "TinyURLs");
if (urls)
g_list_foreach(urls, free_urls, NULL);
g_list_free(urls);
}
-static void tinyurl_notify_fetch_cb(PurpleUtilFetchUrlData *urldata, gpointer cbdata,
- const gchar *urltext, gsize len, const gchar *error)
+static void
+tinyurl_notify_tinyuri(GntWidget *win, const gchar *url)
{
- GntWidget *win = cbdata;
+ gchar *message;
GntWidget *label = g_object_get_data(G_OBJECT(win), "info-widget");
- char *message;
- message = g_strdup_printf(_("TinyURL for above: %s"), urltext);
+ message = g_strdup_printf(_("TinyURL for above: %s"), url);
gnt_label_set_text(GNT_LABEL(label), message);
g_free(message);
+}
+
+static void
+tinyurl_notify_fetch_cb(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _win)
+{
+ GntWidget *win = _win;
+ const gchar *url;
+ const gchar *original_url;
+
+ if (!purple_http_response_is_successful(response))
+ return;
+
+ original_url = g_object_get_data(G_OBJECT(win), "gnttinyurl-original");
+ url = purple_http_response_get_data(response, NULL);
+ g_hash_table_insert(tinyurl_cache,
+ g_strdup(original_url), g_strdup(url));
+
+ tinyurl_notify_tinyuri(win, url);
g_signal_handlers_disconnect_matched(G_OBJECT(win), G_SIGNAL_MATCH_FUNC,
0, 0, NULL,
- G_CALLBACK(purple_util_fetch_url_cancel), NULL);
+ G_CALLBACK(purple_http_conn_cancel), NULL);
}
static void *
@@ -362,16 +410,23 @@ tinyurl_notify_uri(const char *uri)
{
char *fullurl = NULL;
GntWidget *win;
- PurpleUtilFetchUrlData *urlcb;
+ PurpleHttpConnection *hc;
+ const gchar *tiny_url;
/* XXX: The following expects that finch_notify_message gets called. This
* may not always happen, e.g. when another plugin sets its own
* notify_message. So tread carefully. */
win = purple_notify_message(NULL, PURPLE_NOTIFY_MSG_INFO, _("URI"), uri,
- _("Please wait while TinyURL fetches a shorter URL ..."), NULL, NULL);
+ _("Please wait while TinyURL fetches a shorter URL ..."), NULL, NULL, NULL);
if (!GNT_IS_WINDOW(win) || !g_object_get_data(G_OBJECT(win), "info-widget"))
return win;
+ tiny_url = g_hash_table_lookup(tinyurl_cache, uri);
+ if (tiny_url) {
+ tinyurl_notify_tinyuri(win, tiny_url);
+ return win;
+ }
+
if (g_ascii_strncasecmp(uri, "http://", 7) && g_ascii_strncasecmp(uri, "https://", 8)) {
fullurl = g_strdup_printf("%shttp%%3A%%2F%%2F%s",
purple_prefs_get_string(PREF_URL), purple_url_encode(uri));
@@ -380,13 +435,16 @@ tinyurl_notify_uri(const char *uri)
purple_url_encode(uri));
}
- /* Store the return value of _fetch_url and destroy that when win is
- destroyed, so that the callback for _fetch_url does not try to molest a
- non-existent window */
- urlcb = purple_util_fetch_url(fullurl, TRUE, "finch", FALSE, tinyurl_notify_fetch_cb, win);
+ g_object_set_data_full(G_OBJECT(win), "gnttinyurl-original", g_strdup(uri), g_free);
+
+ /* Store the return value of purple_http_get and destroy that when win
+ * is destroyed, so that the callback for purple_http_get does not try
+ * to molest a non-existent window
+ */
+ hc = purple_http_get(NULL, tinyurl_notify_fetch_cb, win, fullurl);
g_free(fullurl);
g_signal_connect_swapped(G_OBJECT(win), "destroy",
- G_CALLBACK(purple_util_fetch_url_cancel), urlcb);
+ G_CALLBACK(purple_http_conn_cancel), hc);
return win;
}
@@ -398,6 +456,9 @@ plugin_load(PurplePlugin *plugin)
plugin->extra = ops->notify_uri;
ops->notify_uri = tinyurl_notify_uri;
+ tinyurl_cache = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, g_free);
+
purple_signal_connect(purple_conversations_get_handle(),
"wrote-im-msg",
plugin, PURPLE_CALLBACK(wrote_msg), NULL);
@@ -423,6 +484,10 @@ plugin_unload(PurplePlugin *plugin)
PurpleNotifyUiOps *ops = purple_notify_get_ui_ops();
if (ops->notify_uri == tinyurl_notify_uri)
ops->notify_uri = plugin->extra;
+
+ g_hash_table_destroy(tinyurl_cache);
+ tinyurl_cache = NULL;
+
return TRUE;
}
@@ -447,8 +512,7 @@ get_plugin_pref_frame(PurplePlugin *plugin) {
static PurplePluginUiInfo prefs_info = {
get_plugin_pref_frame,
- 0, /* page_num (Reserved) */
- NULL, /* frame (Reserved) */
+ NULL,
/* padding */
NULL,
diff --git a/finch/plugins/grouping.c b/finch/plugins/grouping.c
index b312c7dc3b..b54584eea4 100644
--- a/finch/plugins/grouping.c
+++ b/finch/plugins/grouping.c
@@ -28,78 +28,104 @@
#include "gnttree.h"
+#define FINCH_TYPE_GROUPING_NODE (finch_grouping_node_get_type())
+#define FINCH_GROUPING_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), FINCH_TYPE_GROUPING_NODE, FinchGroupingNode))
+#define FINCH_IS_GROUPING_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), FINCH_TYPE_GROUPING_NODE))
+
+typedef struct {
+ PurpleBlistNode node;
+} FinchGroupingNode;
+
+typedef struct {
+ PurpleBlistNodeClass node_class;
+} FinchGroupingNodeClass;
+
static FinchBlistManager *default_manager;
/**
+ * GObject code
+ */
+static GType
+finch_grouping_node_get_type(void)
+{
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(FinchGroupingNodeClass),
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ sizeof(FinchGroupingNode),
+ 0,
+ NULL,
+ NULL,
+ };
+
+ type = g_type_register_static(PURPLE_TYPE_BLIST_NODE,
+ "FinchGroupingNode",
+ &info, 0);
+ }
+
+ return type;
+}
+
+/**
* Online/Offline
*/
-static PurpleBlistNode online = {.type = PURPLE_BLIST_OTHER_NODE},
- offline = {.type = PURPLE_BLIST_OTHER_NODE};
+static FinchGroupingNode *online, *offline;
static gboolean on_offline_init()
{
GntTree *tree = finch_blist_get_tree();
- gnt_tree_add_row_after(tree, &online,
+ gnt_tree_add_row_after(tree, online,
gnt_tree_create_row(tree, _("Online")), NULL, NULL);
- gnt_tree_add_row_after(tree, &offline,
- gnt_tree_create_row(tree, _("Offline")), NULL, &online);
+ gnt_tree_add_row_after(tree, offline,
+ gnt_tree_create_row(tree, _("Offline")), NULL, online);
return TRUE;
}
static gboolean on_offline_can_add_node(PurpleBlistNode *node)
{
- switch (purple_blist_node_get_type(node)) {
- case PURPLE_BLIST_CONTACT_NODE:
- {
- PurpleContact *contact = (PurpleContact*)node;
- if (contact->currentsize > 0)
- return TRUE;
- return FALSE;
- }
- break;
- case PURPLE_BLIST_BUDDY_NODE:
- {
- PurpleBuddy *buddy = (PurpleBuddy*)node;
- if (PURPLE_BUDDY_IS_ONLINE(buddy))
- return TRUE;
- if (purple_prefs_get_bool("/finch/blist/showoffline") &&
- purple_account_is_connected(purple_buddy_get_account(buddy)))
- return TRUE;
- return FALSE;
- }
- break;
- case PURPLE_BLIST_CHAT_NODE:
- {
- PurpleChat *chat = (PurpleChat*)node;
- return purple_account_is_connected(purple_chat_get_account(chat));
- }
- break;
- default:
- return FALSE;
+ if (PURPLE_IS_CONTACT(node)) {
+ PurpleContact *contact = PURPLE_CONTACT(node);
+ if (purple_counting_node_get_current_size(PURPLE_COUNTING_NODE(contact)) > 0)
+ return TRUE;
+ return FALSE;
+ } else if (PURPLE_IS_BUDDY(node)) {
+ PurpleBuddy *buddy = PURPLE_BUDDY(node);
+ if (PURPLE_BUDDY_IS_ONLINE(buddy))
+ return TRUE;
+ if (purple_prefs_get_bool("/finch/blist/showoffline") &&
+ purple_account_is_connected(purple_buddy_get_account(buddy)))
+ return TRUE;
+ return FALSE;
+ } else if (PURPLE_IS_CHAT(node)) {
+ PurpleChat *chat = PURPLE_CHAT(node);
+ return purple_account_is_connected(purple_chat_get_account(chat));
}
+
+ return FALSE;
}
static gpointer on_offline_find_parent(PurpleBlistNode *node)
{
gpointer ret = NULL;
- switch (purple_blist_node_get_type(node)) {
- case PURPLE_BLIST_CONTACT_NODE:
- node = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node)));
- ret = PURPLE_BUDDY_IS_ONLINE((PurpleBuddy*)node) ? &online : &offline;
- break;
- case PURPLE_BLIST_BUDDY_NODE:
- ret = purple_blist_node_get_parent(node);
- finch_blist_manager_add_node(ret);
- break;
- case PURPLE_BLIST_CHAT_NODE:
- ret = &online;
- break;
- default:
- break;
+ if (PURPLE_IS_CONTACT(node)) {
+ node = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node)));
+ ret = PURPLE_BUDDY_IS_ONLINE(PURPLE_BUDDY(node)) ? online : offline;
+ } else if (PURPLE_IS_BUDDY(node)) {
+ ret = purple_blist_node_get_parent(node);
+ finch_blist_manager_add_node(ret);
+ } else if (PURPLE_IS_CHAT(node)) {
+ ret = online;
}
+
return ret;
}
@@ -107,12 +133,13 @@ static gboolean on_offline_create_tooltip(gpointer selected_row, GString **body,
{
PurpleBlistNode *node = selected_row;
- if (purple_blist_node_get_type(node) == PURPLE_BLIST_OTHER_NODE) {
+ if (FINCH_IS_GROUPING_NODE(node)) {
/* There should be some easy way of getting the total online count,
* or total number of chats. Doing a loop here will probably be pretty
* expensive. */
if (body)
- *body = g_string_new(node == &online ? _("Online Buddies") : _("Offline Buddies"));
+ *body = g_string_new(FINCH_GROUPING_NODE(node) == online ?
+ _("Online Buddies") : _("Offline Buddies"));
return TRUE;
} else {
return default_manager ? default_manager->create_tooltip(selected_row, body, tool_title) : FALSE;
@@ -134,7 +161,7 @@ static FinchBlistManager on_offline =
/**
* Meebo-like Grouping.
*/
-static PurpleBlistNode meebo = {.type = PURPLE_BLIST_OTHER_NODE};
+static FinchGroupingNode meebo;
static gboolean meebo_init()
{
GntTree *tree = finch_blist_get_tree();
@@ -147,8 +174,8 @@ static gboolean meebo_init()
static gpointer meebo_find_parent(PurpleBlistNode *node)
{
- if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
- PurpleBuddy *buddy = purple_contact_get_priority_buddy((PurpleContact*)node);
+ if (PURPLE_IS_CONTACT(node)) {
+ PurpleBuddy *buddy = purple_contact_get_priority_buddy(PURPLE_CONTACT(node));
if (buddy && !PURPLE_BUDDY_IS_ONLINE(buddy)) {
return &meebo;
}
@@ -194,14 +221,11 @@ static gpointer no_group_find_parent(PurpleBlistNode *node)
{
gpointer ret = NULL;
- switch (purple_blist_node_get_type(node)) {
- case PURPLE_BLIST_BUDDY_NODE:
- ret = purple_blist_node_get_parent(node);
- finch_blist_manager_add_node(ret);
- break;
- default:
- break;
+ if (PURPLE_IS_BUDDY(node)) {
+ ret = purple_blist_node_get_parent(node);
+ finch_blist_manager_add_node(ret);
}
+
return ret;
}
@@ -247,10 +271,10 @@ nested_group_find_parent(PurpleBlistNode *node)
PurpleBlistNode *ret, *parent;
GntTree *tree;
- if (!PURPLE_BLIST_NODE_IS_GROUP(node))
+ if (!PURPLE_IS_GROUP(node))
return default_manager->find_parent(node);
- group = (PurpleGroup *)node;
+ group = PURPLE_GROUP(node);
name = g_strdup(purple_group_get_name(group));
if (!(sep = strchr(name, '/'))) {
g_free(name);
@@ -262,13 +286,12 @@ nested_group_find_parent(PurpleBlistNode *node)
while (sep) {
*sep = 0;
- if (*(sep + 1) && (ret = (PurpleBlistNode *)purple_find_group(name))) {
+ if (*(sep + 1) && (ret = PURPLE_BLIST_NODE(purple_blist_find_group(name)))) {
finch_blist_manager_add_node(ret);
parent = ret;
} else if (!(ret = g_hash_table_lookup(groups, name))) {
- ret = g_new0(PurpleBlistNode, 1);
+ ret = g_object_new(FINCH_TYPE_GROUPING_NODE, NULL);
g_hash_table_insert(groups, g_strdup(name), ret);
- ret->type = PURPLE_BLIST_OTHER_NODE;
gnt_tree_add_row_last(tree, ret,
gnt_tree_create_row(tree, name), parent);
parent = ret;
@@ -285,8 +308,7 @@ static gboolean
nested_group_create_tooltip(gpointer selected_row, GString **body, char **title)
{
PurpleBlistNode *node = selected_row;
- if (!node ||
- purple_blist_node_get_type(node) != PURPLE_BLIST_OTHER_NODE)
+ if (!node || !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? */
@@ -299,19 +321,19 @@ nested_group_can_add_node(PurpleBlistNode *node)
PurpleBlistNode *group;
int len;
- if (!PURPLE_BLIST_NODE_IS_GROUP(node))
+ if (!PURPLE_IS_GROUP(node))
return default_manager->can_add_node(node);
if (default_manager->can_add_node(node))
return TRUE;
- len = strlen(purple_group_get_name((PurpleGroup*)node));
+ len = strlen(purple_group_get_name(PURPLE_GROUP(node)));
group = purple_blist_get_root();
for (; group; group = purple_blist_node_get_sibling_next(group)) {
if (group == node)
continue;
- if (strncmp(purple_group_get_name((PurpleGroup *)node),
- purple_group_get_name((PurpleGroup *)group), len) == 0 &&
+ if (strncmp(purple_group_get_name(PURPLE_GROUP(node)),
+ purple_group_get_name(PURPLE_GROUP(group)), len) == 0 &&
default_manager->can_add_node(group))
return TRUE;
}
@@ -381,6 +403,8 @@ static PurplePluginInfo info =
static void
init_plugin(PurplePlugin *plugin)
{
+ online = g_object_new(FINCH_TYPE_GROUPING_NODE, NULL);
+ offline = g_object_new(FINCH_TYPE_GROUPING_NODE, NULL);
}
PURPLE_INIT_PLUGIN(grouping, init_plugin, info)
diff --git a/finch/plugins/lastlog.c b/finch/plugins/lastlog.c
index 7e7ef0e0c8..8832b5c98d 100644
--- a/finch/plugins/lastlog.c
+++ b/finch/plugins/lastlog.c
@@ -60,7 +60,7 @@ window_kpress_cb(GntWidget *wid, const char *key, GntTextView *view)
static PurpleCmdRet
lastlog_cb(PurpleConversation *conv, const char *cmd, char **args, char **error, gpointer null)
{
- FinchConv *ggconv = conv->ui_data;
+ FinchConv *ggconv = FINCH_CONV(conv);
char **strings = g_strsplit(GNT_TEXT_VIEW(ggconv->tv)->string->str, "\n", 0);
GntWidget *win, *tv;
int i, j;
diff --git a/fix-casts.sh b/fix-casts.sh
deleted file mode 100755
index 6556f6935e..0000000000
--- a/fix-casts.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/sh
-
-if [ $# -eq 0 ]; then
- echo "Usage: `basename "$0"` PurpleFoo..."
- echo
- echo "This script searches the *current working directory* and replaces casts"
- echo "with GObject-style type checking and casting macros."
- echo 'For example, "(PurpleBuddy *)b" becomes "PURPLE_BUDDY(b)".'
- exit 0
-fi
-
-for struct in $* ; do
- cast=`echo $struct | sed "s|[A-Z]|_\0|g" | tr "a-z" "A-Z" | sed "s|^_||"`
- for file in `grep -rl "([[:space:]]*$struct[[:space:]]*\*[[:space:]]*)" . --include=*.c --exclude=purple-client-bindings.c` ; do
- sed -i "s|([[:space:]]*$struct[[:space:]]*\*[[:space:]]*)[[:space:]]*(|$cast(|g" $file
- sed -i "s|([[:space:]]*$struct[[:space:]]*\*[[:space:]]*)[[:space:]]*\([^(][^,);]*\)|$cast(\1)|g" $file
- done
-done
diff --git a/gaim-uninstalled.pc.in b/gaim-uninstalled.pc.in
deleted file mode 100644
index b9d7d0e193..0000000000
--- a/gaim-uninstalled.pc.in
+++ /dev/null
@@ -1,14 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-datarootdir=@datarootdir@
-datadir=@datadir@
-sysconfdir=@sysconfdir@
-
-Name: Pidgin (Gaim compatibility)
-Description: Pidgin is a GTK2-based instant messenger application.
-Version: @VERSION@
-Requires: glib-2.0
-Cflags: -I${pc_top_builddir}/${pcfiledir}/libpurple -I${pc_top_builddir}/${pcfiledir}/pidgin
-Libs: ${pc_top_builddir}/${pcfiledir}/libpurple/libpurple.la
diff --git a/gaim.pc.in b/gaim.pc.in
deleted file mode 100644
index bc8f5da073..0000000000
--- a/gaim.pc.in
+++ /dev/null
@@ -1,14 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-datarootdir=@datarootdir@
-datadir=@datadir@
-sysconfdir=@sysconfdir@
-
-Name: Pidgin (Gaim compatibility)
-Description: Pidgin is a GTK2-based instant messenger application.
-Version: @VERSION@
-Requires: glib-2.0
-Cflags: -I${includedir}/libpurple
-Libs: -L${libdir} -lpurple
diff --git a/libpurple/Makefile.am b/libpurple/Makefile.am
index 1447d5c052..114dc60978 100644
--- a/libpurple/Makefile.am
+++ b/libpurple/Makefile.am
@@ -2,6 +2,9 @@ EXTRA_DIST = \
dbus-analyze-functions.py \
dbus-analyze-signals.py \
dbus-analyze-types.py \
+ enums.c.in \
+ enums.h.in \
+ glibcompat.h \
marshallers.list \
purple-notifications-example \
purple-remote \
@@ -9,8 +12,9 @@ EXTRA_DIST = \
purple-send-async \
purple-url-handler \
purple.h.in \
- purple.pc.in \
- purple-uninstalled.pc.in \
+ purple-3.pc.in \
+ purple-3-uninstalled.pc.in \
+ tag.sh \
version.h.in \
Makefile.mingw \
win32/global.mak \
@@ -30,28 +34,46 @@ GCONF_DIR=gconf
endif
pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = purple.pc
+pkgconfig_DATA = purple-3.pc
-SUBDIRS = $(GCONF_DIR) plugins protocols ciphers . tests example
+SUBDIRS = $(GCONF_DIR) . plugins protocols tests example
purple_coresources = \
account.c \
+ accounts.c \
accountopt.c \
- blist.c \
+ blistnode.c \
+ blistnodetypes.c \
+ buddylist.c \
buddyicon.c \
certificate.c \
+ ciphers/aescipher.c \
+ ciphers/descipher.c \
+ ciphers/des3cipher.c \
+ ciphers/hmaccipher.c \
+ ciphers/md4hash.c \
+ ciphers/md5hash.c \
+ ciphers/pbkdf2cipher.c \
+ ciphers/rc4cipher.c \
+ ciphers/sha1hash.c \
+ ciphers/sha256hash.c \
cipher.c \
- circbuffer.c \
+ circularbuffer.c \
cmds.c \
connection.c \
conversation.c \
+ conversationtypes.c \
+ conversations.c \
core.c \
debug.c \
desktopitem.c \
+ e2ee.c \
eventloop.c \
- ft.c \
+ http.c \
idle.c \
- imgstore.c \
+ image.c \
+ image-store.c \
+ keyring.c \
log.c \
media/backend-fs2.c \
media/backend-iface.c \
@@ -60,6 +82,8 @@ purple_coresources = \
media/enum-types.c \
media.c \
mediamanager.c \
+ memorypool.c \
+ message.c \
mime.c \
nat-pmp.c \
network.c \
@@ -69,14 +93,20 @@ purple_coresources = \
pluginpref.c \
pounce.c \
prefs.c \
- privacy.c \
+ presence.c \
proxy.c \
prpl.c \
+ purple-socket.c \
request.c \
+ request-datasheet.c \
roomlist.c \
savedstatuses.c \
server.c \
signals.c \
+ smiley-custom.c \
+ smiley-list.c \
+ smiley-parser.c \
+ smiley-theme.c \
smiley.c \
dnsquery.c \
dnssrv.c\
@@ -90,40 +120,51 @@ purple_coresources = \
theme.c \
theme-loader.c \
theme-manager.c \
+ trie.c \
upnp.c \
util.c \
- value.c \
version.c \
- xmlnode.c \
- whiteboard.c
+ whiteboard.c \
+ xfer.c \
+ xmlnode.c
purple_builtsources = \
+ enums.c \
marshallers.c
purple_coreheaders = \
account.h \
+ accounts.h \
accountopt.h \
- blist.h \
+ blistnode.h \
+ blistnodetypes.h \
+ buddylist.h \
buddyicon.h \
certificate.h \
cipher.h \
- circbuffer.h \
+ circularbuffer.h \
cmds.h \
connection.h \
conversation.h \
+ conversationtypes.h \
+ conversations.h \
core.h \
dbus-maybe.h \
debug.h \
desktopitem.h \
+ e2ee.h \
eventloop.h \
- ft.h \
- gaim-compat.h \
+ http.h \
idle.h \
- imgstore.h \
+ image.h \
+ image-store.h \
+ keyring.h \
log.h \
media.h \
media-gst.h \
mediamanager.h \
+ memorypool.h \
+ message.h \
mime.h \
nat-pmp.h \
network.h \
@@ -133,14 +174,20 @@ purple_coreheaders = \
pluginpref.h \
pounce.h \
prefs.h \
- privacy.h \
+ presence.h \
proxy.h \
prpl.h \
+ purple-socket.h \
request.h \
+ request-datasheet.h \
roomlist.h \
savedstatuses.h \
server.h \
signals.h \
+ smiley-custom.h \
+ smiley-list.h \
+ smiley-parser.h \
+ smiley-theme.h \
smiley.h \
dnsquery.h \
dnssrv.h \
@@ -154,11 +201,31 @@ purple_coreheaders = \
theme.h \
theme-loader.h \
theme-manager.h \
+ trie.h \
upnp.h \
util.h \
- value.h \
- xmlnode.h \
- whiteboard.h
+ whiteboard.h \
+ xfer.h \
+ xmlnode.h
+
+if IS_WIN32
+purple_coresources += \
+ win32/giowin32.c \
+ win32/libc_interface.c \
+ win32/win32dep.c
+
+purple_coreheaders += \
+ win32/libc_interface.h \
+ win32/libc_internal.h \
+ win32/win32dep.h \
+ win32/wpurpleerror.h
+
+.rc.o: %.rc
+ $(AM_V_GEN)$(WINDRES) -i $< -o $@
+
+LIBPURPLE_WIN32RES = win32/libpurplerc.o
+LIBPURPLE_WIN32RES_LDFLAGS = -Wl,$(LIBPURPLE_WIN32RES)
+endif
purple_mediaheaders = \
backend-iface.h \
@@ -166,7 +233,29 @@ purple_mediaheaders = \
codec.h \
enum-types.h
-purple_builtheaders = purple.h version.h marshallers.h
+purple_cipherheaders = \
+ aescipher.h \
+ descipher.h \
+ des3cipher.h \
+ hmaccipher.h \
+ md4hash.h \
+ md5hash.h \
+ pbkdf2cipher.h \
+ rc4cipher.h \
+ sha1hash.h \
+ sha256hash.h
+
+purple_builtheaders = purple.h version.h enums.h marshallers.h
+
+purple_enumheaders = \
+ $(srcdir)/account.h \
+ $(srcdir)/cipher.h \
+ $(srcdir)/connection.h \
+ $(srcdir)/conversation.h \
+ $(srcdir)/conversationtypes.h \
+ $(srcdir)/roomlist.h \
+ $(srcdir)/status.h \
+ $(srcdir)/xfer.h
marshallers.h: marshallers.list
$(AM_V_GEN)$(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --header > marshallers.h
@@ -175,17 +264,27 @@ marshallers.c: marshallers.list marshallers.h
$(AM_V_GEN)echo "#include \"marshallers.h\"" > marshallers.c
$(AM_V_at)$(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --body >> marshallers.c
-if ENABLE_DBUS
+enums.h: $(srcdir)/enums.h.in $(purple_enumheaders)
+ $(AM_V_GEN)$(GLIB_MKENUMS) --template $(srcdir)/enums.h.in $(purple_enumheaders) > $@
+
+enums.c: $(srcdir)/enums.c.in $(purple_enumheaders)
+ $(AM_V_GEN)$(GLIB_MKENUMS) --template $(srcdir)/enums.c.in $(purple_enumheaders) > $@
CLEANFILES = \
+ enums.c \
+ enums.h \
+ marshallers.c \
+ marshallers.h
+
+if ENABLE_DBUS
+
+CLEANFILES += \
dbus-bindings.c \
dbus-client-binding.c \
dbus-client-binding.h \
dbus-signals.c \
dbus-types.c \
dbus-types.h \
- marshallers.c \
- marshallers.h \
purple-client-bindings.c \
purple-client-bindings.h \
purple.service
@@ -195,11 +294,14 @@ CLEANFILES = \
dbus_sources = dbus-server.c dbus-useful.c
dbus_headers = dbus-bindings.h dbus-purple.h dbus-server.h dbus-useful.h dbus-define-api.h dbus-types.h
-dbus_exported = dbus-useful.h dbus-define-api.h account.h blist.h buddyicon.h \
- connection.h conversation.h core.h ft.h log.h notify.h prefs.h roomlist.h \
- savedstatuses.h smiley.h status.h server.h util.h xmlnode.h prpl.h
+dbus_exported = dbus-useful.h dbus-define-api.h account.h accounts.h blistnode.h \
+ blistnodetypes.h buddylist.h buddyicon.h connection.h conversation.h \
+ conversationtypes.h conversations.h core.h xfer.h log.h notify.h \
+ prefs.h presence.h roomlist.h savedstatuses.h smiley.h smiley-list.h \
+ status.h server.h util.h xmlnode.h prpl.h
purple_build_coreheaders = $(addprefix $(srcdir)/, $(purple_coreheaders)) \
+ $(addprefix $(srcdir)/ciphers/, $(purple_cipherheaders)) \
$(addprefix $(srcdir)/media/, $(purple_mediaheaders)) \
$(purple_builtheaders)
dbus_build_exported = $(addprefix $(srcdir)/, $(dbus_exported))
@@ -235,7 +337,7 @@ libpurple_client_la_LIBADD = $(DBUS_LIBS)
purple-client-bindings.c: dbus-analyze-functions.py $(dbus_exported)
$(AM_V_GEN)cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py --client > $@
-purple-client-bindings.h: dbus-analyze-types.py dbus-analyze-functions.py $(purple_coreheaders) $(addprefix media/, $(purple_mediaheaders)) $(purple_builtheaders) $(dbus_exported)
+purple-client-bindings.h: dbus-analyze-types.py dbus-analyze-functions.py $(purple_coreheaders) $(addprefix ciphers/, $(purple_cipherheaders)) $(addprefix media/, $(purple_mediaheaders)) $(purple_builtheaders) $(dbus_exported)
$(AM_V_GEN)cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --keyword=enum --verbatim > $@
$(AM_V_at)cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py --client --headers >> $@
@@ -293,43 +395,50 @@ libpurpleinclude_HEADERS = \
$(purple_builtheaders) \
$(dbus_headers)
+cipherincludedir=$(includedir)/libpurple/ciphers
+cipherinclude_HEADERS = \
+ $(addprefix $(srcdir)/ciphers/, $(purple_cipherheaders))
+
mediaincludedir=$(includedir)/libpurple/media
mediainclude_HEADERS = \
$(addprefix $(srcdir)/media/, $(purple_mediaheaders))
-libpurple_la_DEPENDENCIES = $(STATIC_LINK_LIBS)
-libpurple_la_LDFLAGS = -export-dynamic -version-info $(PURPLE_LT_VERSION_INFO) -no-undefined
+libpurple_la_DEPENDENCIES = \
+ $(STATIC_LINK_LIBS) $(LIBPURPLE_WIN32RES)
+
+libpurple_la_LDFLAGS = -export-dynamic -version-info $(PURPLE_LT_VERSION_INFO) \
+ -no-undefined $(LIBPURPLE_WIN32RES_LDFLAGS)
libpurple_la_LIBADD = \
$(STATIC_LINK_LIBS) \
$(DBUS_LIBS) \
+ $(DNSAPI_LIBS) \
$(GLIB_LIBS) \
$(LIBXML_LIBS) \
$(NETWORKMANAGER_LIBS) \
$(INTLLIBS) \
$(FARSTREAM_LIBS) \
$(GSTREAMER_LIBS) \
+ $(GSTVIDEO_LIBS) \
$(GSTINTERFACES_LIBS) \
$(IDN_LIBS) \
- ciphers/libpurple-ciphers.la \
+ $(JSON_LIBS) \
+ $(GNUTLS_LIBS) \
+ $(NSS_LIBS) \
+ $(ZLIB_LIBS) \
-lm
AM_CPPFLAGS = \
- -DDATADIR=\"$(datadir)\" \
- -DLIBDIR=\"$(libdir)/purple-$(PURPLE_MAJOR_VERSION)/\" \
- -DLOCALEDIR=\"$(datadir)/locale\" \
- -DSYSCONFDIR=\"$(sysconfdir)\" \
$(GLIB_CFLAGS) \
$(DEBUG_CFLAGS) \
$(DBUS_CFLAGS) \
$(LIBXML_CFLAGS) \
$(FARSTREAM_CFLAGS) \
$(GSTREAMER_CFLAGS) \
+ $(GSTVIDEO_CFLAGS) \
$(GSTINTERFACES_CFLAGS) \
$(IDN_CFLAGS) \
- $(NETWORKMANAGER_CFLAGS)
-
-# INSTALL_SSL_CERTIFICATES is true when SSL_CERTIFICATES_DIR is empty.
-# We want to use SSL_CERTIFICATES_DIR when it's not empty.
-if ! INSTALL_SSL_CERTIFICATES
-AM_CPPFLAGS += -DSSL_CERTIFICATES_DIR=\"$(SSL_CERTIFICATES_DIR)\"
-endif
+ $(NETWORKMANAGER_CFLAGS) \
+ $(JSON_CFLAGS) \
+ $(GNUTLS_CFLAGS) \
+ $(NSS_CFLAGS) \
+ $(ZLIB_CFLAGS)
diff --git a/libpurple/Makefile.mingw b/libpurple/Makefile.mingw
index 2d9ebab368..6f916165f8 100644
--- a/libpurple/Makefile.mingw
+++ b/libpurple/Makefile.mingw
@@ -8,10 +8,9 @@ PIDGIN_TREE_TOP := ..
include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
TARGET = libpurple
-NEEDED_DLLS = $(LIBXML2_TOP)/bin/libxml2-2.dll
ifeq ($(CYRUS_SASL), 1)
-NEEDED_DLLS += $(CYRUS_SASL_TOP)/bin/libsasl.dll
+NEEDED_DLLS = $(CYRUS_SASL_TOP)/bin/libsasl.dll
CYRUS_SASL_PLUGINS = \
$(CYRUS_SASL_TOP)/bin/sasl2/saslANONYMOUS.dll \
@@ -33,44 +32,78 @@ INCLUDE_PATHS += \
-I$(GTK_TOP)/include \
-I$(GTK_TOP)/include/glib-2.0 \
-I$(GTK_TOP)/lib/glib-2.0/include \
- -I$(LIBXML2_TOP)/include/libxml2
+ -I$(JSON_GLIB_TOP)/include/json-glib-1.0 \
+ -I$(LIBXML2_TOP)/include/libxml2 \
+ -I$(NSS_TOP)/include/nspr4 \
+ -I$(NSS_TOP)/include/nss3 \
+ -I$(GNUTLS_TOP)/include \
+ $(VV_INCLUDE_PATHS)
LIB_PATHS += -L$(GTK_TOP)/lib \
- -L$(LIBXML2_TOP)/lib
+ -L$(JSON_GLIB_TOP)/lib \
+ -L$(LIBXML2_TOP)/lib \
+ -L$(NSS_TOP)/lib \
+ -L$(GNUTLS_TOP)/lib \
+ $(VV_LIB_PATHS)
##
## SOURCES, OBJECTS
##
+ifeq "$(USE_VV)" "1"
+VV_SRC = \
+ media/backend-fs2.c \
+ media/backend-iface.c \
+ media/codec.c
+else
+VV_SRC =
+endif
+
+BUILT_SRC = marshallers.c enums.c
+
C_SRC = \
+ $(BUILT_SRC) \
account.c \
+ accounts.c \
accountopt.c \
- blist.c \
+ blistnode.c \
+ blistnodetypes.c \
+ buddylist.c \
buddyicon.c \
certificate.c \
+ ciphers/aescipher.c \
+ ciphers/descipher.c \
+ ciphers/des3cipher.c \
+ ciphers/hmaccipher.c \
+ ciphers/md4hash.c \
+ ciphers/md5hash.c \
+ ciphers/pbkdf2cipher.c \
+ ciphers/rc4cipher.c \
+ ciphers/sha1hash.c \
+ ciphers/sha256hash.c \
cipher.c \
- ciphers/des.c \
- ciphers/gchecksum.c \
- ciphers/hmac.c \
- ciphers/md4.c \
- ciphers/md5.c \
- ciphers/rc4.c \
- ciphers/sha1.c \
- ciphers/sha256.c \
- circbuffer.c \
+ circularbuffer.c \
cmds.c \
connection.c \
conversation.c \
+ conversationtypes.c \
+ conversations.c \
core.c \
debug.c \
dnsquery.c \
dnssrv.c \
+ e2ee.c \
eventloop.c \
- ft.c \
+ http.c \
idle.c \
- imgstore.c \
+ image.c \
+ image-store.c \
+ keyring.c \
log.c \
+ media/candidate.c \
+ media/enum-types.c \
mediamanager.c \
media.c \
+ memorypool.c \
mime.c \
nat-pmp.c \
network.c \
@@ -80,15 +113,21 @@ C_SRC = \
pluginpref.c \
pounce.c \
prefs.c \
- privacy.c \
+ presence.c \
proxy.c \
prpl.c \
+ purple-socket.c \
request.c \
+ request-datasheet.c \
roomlist.c \
savedstatuses.c \
server.c \
signals.c \
smiley.c \
+ smiley-custom.c \
+ smiley-list.c \
+ smiley-parser.c \
+ smiley-theme.c \
sound-theme-loader.c \
sound-theme.c \
sound.c \
@@ -99,18 +138,30 @@ C_SRC = \
theme-loader.c \
theme-manager.c \
theme.c \
+ trie.c \
upnp.c \
util.c \
- value.c \
version.c \
whiteboard.c \
+ xfer.c \
xmlnode.c \
win32/giowin32.c \
win32/libc_interface.c \
- win32/win32dep.c
+ win32/win32dep.c \
+ $(VV_SRC)
RC_SRC = win32/libpurplerc.rc
+ENUM_HEADERS = \
+ account.h \
+ cipher.h \
+ connection.h \
+ conversation.h \
+ conversationtypes.h \
+ roomlist.h \
+ status.h \
+ xfer.h
+
OBJECTS = $(C_SRC:%.c=%.o) $(RC_SRC:%.rc=%.o)
##
@@ -124,7 +175,13 @@ LIBS = \
-lgmodule-2.0 \
-lintl \
-lws2_32 \
- -lxml2
+ -lxml2 \
+ -ljson-glib-1.0 \
+ -lnss3 \
+ -lnspr4 \
+ -lgnutls \
+ -lz \
+ $(VV_LIBS)
include $(PIDGIN_COMMON_RULES)
@@ -134,8 +191,8 @@ include $(PIDGIN_COMMON_RULES)
.PHONY: all install install_shallow clean
all: $(TARGET).dll
- $(MAKE) -C $(PURPLE_PROTOS_TOP) -f $(MINGW_MAKEFILE)
- $(MAKE) -C $(PURPLE_PLUGINS_TOP) -f $(MINGW_MAKEFILE)
+ $(MAKE_at) $(MAKE) -C $(PURPLE_PROTOS_TOP) -f $(MINGW_MAKEFILE)
+ $(MAKE_at) $(MAKE) -C $(PURPLE_PLUGINS_TOP) -f $(MINGW_MAKEFILE)
install_shallow: $(PURPLE_INSTALL_DIR) $(TARGET).dll
cp $(TARGET).dll $(PURPLE_INSTALL_DIR)
@@ -146,15 +203,37 @@ ifeq ($(CYRUS_SASL), 1)
endif
install: install_shallow all
- $(MAKE) -C $(PURPLE_PROTOS_TOP) -f $(MINGW_MAKEFILE) install
- $(MAKE) -C $(PURPLE_PLUGINS_TOP) -f $(MINGW_MAKEFILE) install
+ $(MAKE_at) $(MAKE) -C $(PURPLE_PROTOS_TOP) -f $(MINGW_MAKEFILE) install
+ $(MAKE_at) $(MAKE) -C $(PURPLE_PLUGINS_TOP) -f $(MINGW_MAKEFILE) install
./win32/libpurplerc.rc: ./win32/libpurplerc.rc.in $(PIDGIN_TREE_TOP)/VERSION
- sed -e 's/@PURPLE_VERSION@/$(PURPLE_VERSION)/g' \
+ @echo -e " GEN\t$@"
+ @sed -e 's/@PURPLE_VERSION@/$(PURPLE_VERSION)/g' \
+ -e 's/@PURPLE_MAJOR_VERSION@/$(PURPLE_MAJOR_VERSION)/g' \
+ -e 's/@PURPLE_MINOR_VERSION@/$(PURPLE_MINOR_VERSION)/g' \
+ -e 's/@PURPLE_MICRO_VERSION@/$(PURPLE_MICRO_VERSION)/g' \
+ -e 's/@PURPLE_API_VERSION@/$(PURPLE_API_VERSION)/g' \
$@.in > $@
$(OBJECTS): $(PURPLE_CONFIG_H) $(PURPLE_VERSION_H) $(PURPLE_PURPLE_H)
+marshallers.h: marshallers.list
+ @echo -e " GEN\t$@"
+ @$(GLIB_GENMARSHAL) --prefix=purple_smarshal $< --header > marshallers.h
+
+marshallers.c: marshallers.list marshallers.h
+ @echo -e " GEN\t$@"
+ @echo "#include \"marshallers.h\"" > marshallers.c
+ @$(GLIB_GENMARSHAL) --prefix=purple_smarshal $< --body >> marshallers.c
+
+enums.h: enums.h.in $(ENUM_HEADERS)
+ @echo -e " GEN\t$@"
+ @$(GLIB_MKENUMS) --template enums.h.in $(ENUM_HEADERS) > enums.h
+
+enums.c: enums.c.in enums.h $(ENUM_HEADERS)
+ @echo -e " GEN\t$@"
+ @$(GLIB_MKENUMS) --template enums.c.in $(ENUM_HEADERS) > enums.c
+
$(TARGET).dll $(TARGET).dll.a: $(OBJECTS)
$(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -Wl,--output-def,$(TARGET).def,--out-implib,$(TARGET).dll.a -o $(TARGET).dll
@@ -162,9 +241,9 @@ $(TARGET).dll $(TARGET).dll.a: $(OBJECTS)
## CLEAN RULES
##
clean:
- rm -f $(OBJECTS) $(RC_SRC) $(PURPLE_VERSION_H) $(PURPLE_PURPLE_H)
+ rm -f $(OBJECTS) $(RC_SRC) $(PURPLE_VERSION_H) $(PURPLE_PURPLE_H) marshallers.h marshallers.c enums.h enums.c
rm -f $(TARGET).dll $(TARGET).dll.a $(TARGET).def
- $(MAKE) -C $(PURPLE_PROTOS_TOP) -f $(MINGW_MAKEFILE) clean
- $(MAKE) -C $(PURPLE_PLUGINS_TOP) -f $(MINGW_MAKEFILE) clean
+ $(MAKE_at) $(MAKE) -C $(PURPLE_PROTOS_TOP) -f $(MINGW_MAKEFILE) clean
+ $(MAKE_at) $(MAKE) -C $(PURPLE_PLUGINS_TOP) -f $(MINGW_MAKEFILE) clean
include $(PIDGIN_COMMON_TARGETS)
diff --git a/libpurple/account.c b/libpurple/account.c
index bef2620532..8277d27882 100644
--- a/libpurple/account.c
+++ b/libpurple/account.c
@@ -1,8 +1,3 @@
-/**
- * @file account.c Account API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -24,7 +19,9 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#include "internal.h"
-#include "account.h"
+#include "glibcompat.h"
+
+#include "accounts.h"
#include "core.h"
#include "dbus-maybe.h"
#include "debug.h"
@@ -32,1184 +29,213 @@
#include "notify.h"
#include "pounce.h"
#include "prefs.h"
-#include "privacy.h"
-#include "prpl.h"
#include "request.h"
#include "server.h"
#include "signals.h"
-#include "status.h"
#include "util.h"
-#include "xmlnode.h"
-typedef struct
-{
- PurpleConnectionErrorInfo *current_error;
+#define PURPLE_ACCOUNT_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_ACCOUNT, PurpleAccountPrivate))
- /* libpurple 3.0.0 compatibility */
- char *password_keyring;
- char *password_mode;
- char *password_ciphertext;
-} PurpleAccountPrivate;
-
-#define PURPLE_ACCOUNT_GET_PRIVATE(account) \
- ((PurpleAccountPrivate *) (account->priv))
-
-/* TODO: Should use PurpleValue instead of this? What about "ui"? */
typedef struct
{
- PurplePrefType type;
-
- char *ui;
-
- union
- {
- int integer;
- char *string;
- gboolean boolean;
-
- } value;
-
-} PurpleAccountSetting;
-
-typedef struct
-{
- PurpleAccountRequestType type;
- PurpleAccount *account;
- void *ui_handle;
- char *user;
- gpointer userdata;
- PurpleAccountRequestAuthorizationCb auth_cb;
- PurpleAccountRequestAuthorizationCb deny_cb;
- guint ref;
-} PurpleAccountRequestInfo;
-
-static PurpleAccountUiOps *account_ui_ops = NULL;
-
-static GList *accounts = NULL;
-static guint save_timer = 0;
-static gboolean accounts_loaded = FALSE;
-
-static GList *handles = NULL;
-
-static void set_current_error(PurpleAccount *account,
- PurpleConnectionErrorInfo *new_err);
-
-static void
-_purple_account_set_encrypted_password(PurpleAccount *account, const char *keyring,
- const char *mode, const char *ciphertext);
-static gboolean
-_purple_account_get_encrypted_password(PurpleAccount *account, const char **keyring,
- const char **mode, const char **ciphertext);
-static gboolean
-_purple_account_is_password_encrypted(PurpleAccount *account);
-
-/*********************************************************************
- * Writing to disk *
- *********************************************************************/
-
-static void
-setting_to_xmlnode(gpointer key, gpointer value, gpointer user_data)
-{
- const char *name;
- PurpleAccountSetting *setting;
- xmlnode *node, *child;
- char buf[21];
-
- name = (const char *)key;
- setting = (PurpleAccountSetting *)value;
- node = (xmlnode *)user_data;
-
- child = xmlnode_new_child(node, "setting");
- xmlnode_set_attrib(child, "name", name);
-
- if (setting->type == PURPLE_PREF_INT) {
- xmlnode_set_attrib(child, "type", "int");
- g_snprintf(buf, sizeof(buf), "%d", setting->value.integer);
- xmlnode_insert_data(child, buf, -1);
- }
- else if (setting->type == PURPLE_PREF_STRING && setting->value.string != NULL) {
- xmlnode_set_attrib(child, "type", "string");
- xmlnode_insert_data(child, setting->value.string, -1);
- }
- else if (setting->type == PURPLE_PREF_BOOLEAN) {
- xmlnode_set_attrib(child, "type", "bool");
- g_snprintf(buf, sizeof(buf), "%d", setting->value.boolean);
- xmlnode_insert_data(child, buf, -1);
- }
-}
-
-static void
-ui_setting_to_xmlnode(gpointer key, gpointer value, gpointer user_data)
-{
- const char *ui;
- GHashTable *table;
- xmlnode *node, *child;
-
- ui = (const char *)key;
- table = (GHashTable *)value;
- node = (xmlnode *)user_data;
-
- if (g_hash_table_size(table) > 0)
- {
- child = xmlnode_new_child(node, "settings");
- xmlnode_set_attrib(child, "ui", ui);
- g_hash_table_foreach(table, setting_to_xmlnode, child);
- }
-}
-
-static xmlnode *
-status_attr_to_xmlnode(const PurpleStatus *status, const PurpleStatusType *type, const PurpleStatusAttr *attr)
-{
- xmlnode *node;
- const char *id;
- char *value = NULL;
- PurpleStatusAttr *default_attr;
- PurpleValue *default_value;
- PurpleType attr_type;
- PurpleValue *attr_value;
+ char *username; /* The username. */
+ char *alias; /* How you appear to yourself. */
+ char *password; /* The account password. */
+ char *user_info; /* User information. */
- id = purple_status_attr_get_id(attr);
- g_return_val_if_fail(id, NULL);
+ char *buddy_icon_path; /* The buddy icon's non-cached path. */
- attr_value = purple_status_get_attr_value(status, id);
- g_return_val_if_fail(attr_value, NULL);
- attr_type = purple_value_get_type(attr_value);
+ gboolean remember_pass; /* Remember the password. */
/*
- * If attr_value is a different type than it should be
- * then don't write it to the file.
- */
- default_attr = purple_status_type_get_attr(type, id);
- default_value = purple_status_attr_get_value(default_attr);
- if (attr_type != purple_value_get_type(default_value))
- return NULL;
-
- /*
- * If attr_value is the same as the default for this status
- * then there is no need to write it to the file.
+ * TODO: After a GObject representing a protocol is ready, use it
+ * here instead of the protocol ID.
*/
- if (attr_type == PURPLE_TYPE_STRING)
- {
- const char *string_value = purple_value_get_string(attr_value);
- const char *default_string_value = purple_value_get_string(default_value);
- if (purple_strequal(string_value, default_string_value))
- return NULL;
- value = g_strdup(purple_value_get_string(attr_value));
- }
- else if (attr_type == PURPLE_TYPE_INT)
- {
- int int_value = purple_value_get_int(attr_value);
- if (int_value == purple_value_get_int(default_value))
- return NULL;
- value = g_strdup_printf("%d", int_value);
- }
- else if (attr_type == PURPLE_TYPE_BOOLEAN)
- {
- gboolean boolean_value = purple_value_get_boolean(attr_value);
- if (boolean_value == purple_value_get_boolean(default_value))
- return NULL;
- value = g_strdup(boolean_value ?
- "true" : "false");
- }
- else
- {
- return NULL;
- }
-
- g_return_val_if_fail(value, NULL);
-
- node = xmlnode_new("attribute");
-
- xmlnode_set_attrib(node, "id", id);
- xmlnode_set_attrib(node, "value", value);
-
- g_free(value);
-
- return node;
-}
-
-static xmlnode *
-status_attrs_to_xmlnode(const PurpleStatus *status)
-{
- PurpleStatusType *type = purple_status_get_type(status);
- xmlnode *node, *child;
- GList *attrs, *attr;
-
- node = xmlnode_new("attributes");
-
- attrs = purple_status_type_get_attrs(type);
- for (attr = attrs; attr != NULL; attr = attr->next)
- {
- child = status_attr_to_xmlnode(status, type, (const PurpleStatusAttr *)attr->data);
- if (child)
- xmlnode_insert_child(node, child);
- }
-
- return node;
-}
-
-static xmlnode *
-status_to_xmlnode(const PurpleStatus *status)
-{
- xmlnode *node, *child;
+ char *protocol_id; /* The ID of the protocol. */
- node = xmlnode_new("status");
- xmlnode_set_attrib(node, "type", purple_status_get_id(status));
- if (purple_status_get_name(status) != NULL)
- xmlnode_set_attrib(node, "name", purple_status_get_name(status));
- xmlnode_set_attrib(node, "active", purple_status_is_active(status) ? "true" : "false");
-
- child = status_attrs_to_xmlnode(status);
- xmlnode_insert_child(node, child);
-
- return node;
-}
-
-static xmlnode *
-statuses_to_xmlnode(const PurplePresence *presence)
-{
- xmlnode *node, *child;
- GList *statuses;
- PurpleStatus *status;
-
- node = xmlnode_new("statuses");
-
- statuses = purple_presence_get_statuses(presence);
- for (; statuses != NULL; statuses = statuses->next)
- {
- status = statuses->data;
- if (purple_status_type_is_saveable(purple_status_get_type(status)))
- {
- child = status_to_xmlnode(status);
- xmlnode_insert_child(node, child);
- }
- }
+ PurpleConnection *gc; /* The connection handle. */
+ gboolean disconnecting; /* The account is currently disconnecting */
- return node;
-}
+ GHashTable *settings; /* Protocol-specific settings. */
+ GHashTable *ui_settings; /* UI-specific settings. */
-static xmlnode *
-proxy_settings_to_xmlnode(PurpleProxyInfo *proxy_info)
-{
- xmlnode *node, *child;
- PurpleProxyType proxy_type;
- const char *value;
- int int_value;
- char buf[21];
-
- proxy_type = purple_proxy_info_get_type(proxy_info);
+ PurpleProxyInfo *proxy_info; /* Proxy information. This will be set */
+ /* to NULL when the account inherits */
+ /* proxy settings from global prefs. */
- node = xmlnode_new("proxy");
-
- child = xmlnode_new_child(node, "type");
- xmlnode_insert_data(child,
- (proxy_type == PURPLE_PROXY_USE_GLOBAL ? "global" :
- proxy_type == PURPLE_PROXY_NONE ? "none" :
- proxy_type == PURPLE_PROXY_HTTP ? "http" :
- proxy_type == PURPLE_PROXY_SOCKS4 ? "socks4" :
- proxy_type == PURPLE_PROXY_SOCKS5 ? "socks5" :
- proxy_type == PURPLE_PROXY_TOR ? "tor" :
- proxy_type == PURPLE_PROXY_USE_ENVVAR ? "envvar" : "unknown"), -1);
-
- if ((value = purple_proxy_info_get_host(proxy_info)) != NULL)
- {
- child = xmlnode_new_child(node, "host");
- xmlnode_insert_data(child, value, -1);
- }
-
- if ((int_value = purple_proxy_info_get_port(proxy_info)) != 0)
- {
- g_snprintf(buf, sizeof(buf), "%d", int_value);
- child = xmlnode_new_child(node, "port");
- xmlnode_insert_data(child, buf, -1);
- }
-
- if ((value = purple_proxy_info_get_username(proxy_info)) != NULL)
- {
- child = xmlnode_new_child(node, "username");
- xmlnode_insert_data(child, value, -1);
- }
-
- if ((value = purple_proxy_info_get_password(proxy_info)) != NULL)
- {
- child = xmlnode_new_child(node, "password");
- xmlnode_insert_data(child, value, -1);
- }
-
- return node;
-}
-
-static xmlnode *
-current_error_to_xmlnode(PurpleConnectionErrorInfo *err)
-{
- xmlnode *node, *child;
- char type_str[3];
-
- node = xmlnode_new("current_error");
-
- if(err == NULL)
- return node;
-
- /* It doesn't make sense to have transient errors persist across a
- * restart.
+ /*
+ * TODO: Instead of linked lists for permit and deny, use a data
+ * structure that allows fast lookups AND decent performance when
+ * iterating through all items. Fast lookups should help performance
+ * for protocols like MSN, where all your buddies exist in your permit
+ * list therefore the permit list is large. Possibly GTree or
+ * GHashTable.
*/
- if(!purple_connection_error_is_fatal (err->type))
- return node;
-
- child = xmlnode_new_child(node, "type");
- g_snprintf(type_str, sizeof(type_str), "%u", err->type);
- xmlnode_insert_data(child, type_str, -1);
-
- child = xmlnode_new_child(node, "description");
- if(err->description) {
- char *utf8ized = purple_utf8_try_convert(err->description);
- if(utf8ized == NULL)
- utf8ized = purple_utf8_salvage(err->description);
- xmlnode_insert_data(child, utf8ized, -1);
- g_free(utf8ized);
- }
-
- return node;
-}
+ GSList *permit; /* Permit list. */
+ GSList *deny; /* Deny list. */
+ PurpleAccountPrivacyType privacy_type; /* The permit/deny setting. */
-static xmlnode *
-account_to_xmlnode(PurpleAccount *account)
-{
- PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
-
- xmlnode *node, *child;
- const char *tmp;
- PurplePresence *presence;
- PurpleProxyInfo *proxy_info;
-
- node = xmlnode_new("account");
-
- child = xmlnode_new_child(node, "protocol");
- xmlnode_insert_data(child, purple_account_get_protocol_id(account), -1);
-
- child = xmlnode_new_child(node, "name");
- xmlnode_insert_data(child, purple_account_get_username(account), -1);
-
- if (purple_account_get_remember_password(account) &&
- ((tmp = purple_account_get_password(account)) != NULL))
- {
- child = xmlnode_new_child(node, "password");
- xmlnode_insert_data(child, tmp, -1);
- } else if (_purple_account_is_password_encrypted(account)) {
- const char *keyring = NULL;
- const char *mode = NULL;
- const char *ciphertext = NULL;
- gboolean success;
-
- purple_debug_warning("account", "saving libpurple3-compatible "
- "encrypted password\n");
-
- success = _purple_account_get_encrypted_password(account,
- &keyring, &mode, &ciphertext);
- g_warn_if_fail(success);
-
- child = xmlnode_new_child(node, "password");
- if (keyring != NULL)
- xmlnode_set_attrib(child, "keyring_id", keyring);
- if (mode != NULL)
- xmlnode_set_attrib(child, "mode", mode);
- if (ciphertext != NULL)
- xmlnode_insert_data(child, ciphertext, -1);
- }
-
- if ((tmp = purple_account_get_alias(account)) != NULL)
- {
- child = xmlnode_new_child(node, "alias");
- xmlnode_insert_data(child, tmp, -1);
- }
-
- if ((presence = purple_account_get_presence(account)) != NULL)
- {
- child = statuses_to_xmlnode(presence);
- xmlnode_insert_child(node, child);
- }
+ GList *status_types; /* Status types. */
- if ((tmp = purple_account_get_user_info(account)) != NULL)
- {
- /* TODO: Do we need to call purple_str_strip_char(tmp, '\r') here? */
- child = xmlnode_new_child(node, "userinfo");
- xmlnode_insert_data(child, tmp, -1);
- }
+ PurplePresence *presence; /* Presence. */
+ PurpleLog *system_log; /* The system log */
- if (g_hash_table_size(account->settings) > 0)
- {
- child = xmlnode_new_child(node, "settings");
- g_hash_table_foreach(account->settings, setting_to_xmlnode, child);
- }
+ PurpleAccountRegistrationCb registration_cb;
+ void *registration_cb_user_data;
- if (g_hash_table_size(account->ui_settings) > 0)
- {
- g_hash_table_foreach(account->ui_settings, ui_setting_to_xmlnode, node);
- }
-
- if ((proxy_info = purple_account_get_proxy_info(account)) != NULL)
- {
- child = proxy_settings_to_xmlnode(proxy_info);
- xmlnode_insert_child(node, child);
- }
-
- child = current_error_to_xmlnode(priv->current_error);
- xmlnode_insert_child(node, child);
-
- return node;
-}
-
-static xmlnode *
-accounts_to_xmlnode(void)
-{
- xmlnode *node, *child;
- GList *cur;
-
- node = xmlnode_new("account");
- xmlnode_set_attrib(node, "version", "1.0");
-
- for (cur = purple_accounts_get_all(); cur != NULL; cur = cur->next)
- {
- child = account_to_xmlnode(cur->data);
- xmlnode_insert_child(node, child);
- }
-
- return node;
-}
-
-static void
-sync_accounts(void)
-{
- xmlnode *node;
- char *data;
-
- if (!accounts_loaded)
- {
- purple_debug_error("account", "Attempted to save accounts before "
- "they were read!\n");
- return;
- }
-
- node = accounts_to_xmlnode();
- data = xmlnode_to_formatted_str(node, NULL);
- purple_util_write_data_to_file("accounts.xml", data, -1);
- g_free(data);
- xmlnode_free(node);
-}
-
-static gboolean
-save_cb(gpointer data)
-{
- sync_accounts();
- save_timer = 0;
- return FALSE;
-}
-
-static void
-schedule_accounts_save(void)
-{
- if (save_timer == 0)
- save_timer = purple_timeout_add_seconds(5, save_cb, NULL);
-}
-
-
-/*********************************************************************
- * Reading from disk *
- *********************************************************************/
-static void
-migrate_yahoo_japan(PurpleAccount *account)
-{
- /* detect a Yahoo! JAPAN account that existed prior to 2.6.0 and convert it
- * to use the new prpl-yahoojp. Also remove the account-specific settings
- * we no longer need */
-
- if(purple_strequal(purple_account_get_protocol_id(account), "prpl-yahoo")) {
- if(purple_account_get_bool(account, "yahoojp", FALSE)) {
- const char *serverjp = purple_account_get_string(account, "serverjp", NULL);
- const char *xferjp_host = purple_account_get_string(account, "xferjp_host", NULL);
-
- g_return_if_fail(serverjp != NULL);
- g_return_if_fail(xferjp_host != NULL);
-
- purple_account_set_string(account, "server", serverjp);
- purple_account_set_string(account, "xfer_host", xferjp_host);
-
- purple_account_set_protocol_id(account, "prpl-yahoojp");
- }
-
- /* these should always be nuked */
- purple_account_remove_setting(account, "yahoojp");
- purple_account_remove_setting(account, "serverjp");
- purple_account_remove_setting(account, "xferjp_host");
-
- }
-}
-
-static void
-migrate_icq_server(PurpleAccount *account)
-{
- /* Migrate the login server setting for ICQ accounts. See
- * 'mtn log --last 1 --no-graph --from b6d7712e90b68610df3bd2d8cbaf46d94c8b3794'
- * for details on the change. */
-
- if(purple_strequal(purple_account_get_protocol_id(account), "prpl-icq")) {
- const char *tmp = purple_account_get_string(account, "server", NULL);
-
- /* Non-secure server */
- if(purple_strequal(tmp, "login.messaging.aol.com") ||
- purple_strequal(tmp, "login.oscar.aol.com"))
- purple_account_set_string(account, "server", "login.icq.com");
-
- /* Secure server */
- if(purple_strequal(tmp, "slogin.oscar.aol.com"))
- purple_account_set_string(account, "server", "slogin.icq.com");
- }
-}
-
-static void
-migrate_xmpp_encryption(PurpleAccount *account)
-{
- /* When this is removed, nuke the "old_ssl" and "require_tls" settings */
- if (g_str_equal(purple_account_get_protocol_id(account), "prpl-jabber")) {
- const char *sec = purple_account_get_string(account, "connection_security", "");
-
- if (g_str_equal("", sec)) {
- const char *val = "require_tls";
- if (purple_account_get_bool(account, "old_ssl", FALSE))
- val = "old_ssl";
- else if (!purple_account_get_bool(account, "require_tls", TRUE))
- val = "opportunistic_tls";
-
- purple_account_set_string(account, "connection_security", val);
- }
- }
-}
-
-static void
-parse_settings(xmlnode *node, PurpleAccount *account)
-{
- const char *ui;
- xmlnode *child;
-
- /* Get the UI string, if these are UI settings */
- ui = xmlnode_get_attrib(node, "ui");
-
- /* Read settings, one by one */
- for (child = xmlnode_get_child(node, "setting"); child != NULL;
- child = xmlnode_get_next_twin(child))
- {
- const char *name, *str_type;
- PurplePrefType type;
- char *data;
-
- name = xmlnode_get_attrib(child, "name");
- if (name == NULL)
- /* Ignore this setting */
- continue;
-
- str_type = xmlnode_get_attrib(child, "type");
- if (str_type == NULL)
- /* Ignore this setting */
- continue;
-
- if (purple_strequal(str_type, "string"))
- type = PURPLE_PREF_STRING;
- else if (purple_strequal(str_type, "int"))
- type = PURPLE_PREF_INT;
- else if (purple_strequal(str_type, "bool"))
- type = PURPLE_PREF_BOOLEAN;
- else
- /* Ignore this setting */
- continue;
-
- data = xmlnode_get_data(child);
- if (data == NULL)
- /* Ignore this setting */
- continue;
-
- if (ui == NULL)
- {
- if (type == PURPLE_PREF_STRING)
- purple_account_set_string(account, name, data);
- else if (type == PURPLE_PREF_INT)
- purple_account_set_int(account, name, atoi(data));
- else if (type == PURPLE_PREF_BOOLEAN)
- purple_account_set_bool(account, name,
- (*data == '0' ? FALSE : TRUE));
- } else {
- if (type == PURPLE_PREF_STRING)
- purple_account_set_ui_string(account, ui, name, data);
- else if (type == PURPLE_PREF_INT)
- purple_account_set_ui_int(account, ui, name, atoi(data));
- else if (type == PURPLE_PREF_BOOLEAN)
- purple_account_set_ui_bool(account, ui, name,
- (*data == '0' ? FALSE : TRUE));
- }
-
- g_free(data);
- }
-
- /* we do this here because we need access to account settings to determine
- * if we can/should migrate an old Yahoo! JAPAN account */
- migrate_yahoo_japan(account);
- /* we do this here because we need access to account settings to determine
- * if we can/should migrate an ICQ account's server setting */
- migrate_icq_server(account);
- /* we do this here because we need to do it before the user views the
- * Edit Account dialog. */
- migrate_xmpp_encryption(account);
-}
+ PurpleConnectionErrorInfo *current_error; /* Errors */
+} PurpleAccountPrivate;
-static GList *
-parse_status_attrs(xmlnode *node, PurpleStatus *status)
+typedef struct
{
- GList *list = NULL;
- xmlnode *child;
- PurpleValue *attr_value;
-
- for (child = xmlnode_get_child(node, "attribute"); child != NULL;
- child = xmlnode_get_next_twin(child))
- {
- const char *id = xmlnode_get_attrib(child, "id");
- const char *value = xmlnode_get_attrib(child, "value");
-
- if (!id || !*id || !value || !*value)
- continue;
-
- attr_value = purple_status_get_attr_value(status, id);
- if (!attr_value)
- continue;
-
- list = g_list_append(list, (char *)id);
-
- switch (purple_value_get_type(attr_value))
- {
- case PURPLE_TYPE_STRING:
- list = g_list_append(list, (char *)value);
- break;
- case PURPLE_TYPE_INT:
- case PURPLE_TYPE_BOOLEAN:
- {
- int v;
- if (sscanf(value, "%d", &v) == 1)
- list = g_list_append(list, GINT_TO_POINTER(v));
- else
- list = g_list_remove(list, id);
- break;
- }
- default:
- break;
- }
- }
+ char *ui;
+ GValue value;
- return list;
-}
+} PurpleAccountSetting;
-static void
-parse_status(xmlnode *node, PurpleAccount *account)
+typedef struct
{
- gboolean active = FALSE;
- const char *data;
- const char *type;
- xmlnode *child;
- GList *attrs = NULL;
-
- /* Get the active/inactive state */
- data = xmlnode_get_attrib(node, "active");
- if (data == NULL)
- return;
- if (g_ascii_strcasecmp(data, "true") == 0)
- active = TRUE;
- else if (g_ascii_strcasecmp(data, "false") == 0)
- active = FALSE;
- else
- return;
-
- /* Get the type of the status */
- type = xmlnode_get_attrib(node, "type");
- if (type == NULL)
- return;
-
- /* Read attributes into a GList */
- child = xmlnode_get_child(node, "attributes");
- if (child != NULL)
- {
- attrs = parse_status_attrs(child,
- purple_account_get_status(account, type));
- }
-
- purple_account_set_status_list(account, type, active, attrs);
-
- g_list_free(attrs);
-}
+ PurpleAccountRequestType type;
+ PurpleAccount *account;
+ void *ui_handle;
+ char *user;
+ gpointer userdata;
+ PurpleAccountRequestAuthorizationCb auth_cb;
+ PurpleAccountRequestAuthorizationCb deny_cb;
+ guint ref;
+} PurpleAccountRequestInfo;
-static void
-parse_statuses(xmlnode *node, PurpleAccount *account)
+typedef struct
{
- xmlnode *child;
+ PurpleCallback cb;
+ gpointer data;
+} PurpleCallbackBundle;
+
+/* GObject Property enums */
+enum
+{
+ PROP_0,
+ PROP_USERNAME,
+ PROP_PRIVATE_ALIAS,
+ PROP_ENABLED,
+ PROP_CONNECTION,
+ PROP_PROTOCOL_ID,
+ PROP_USER_INFO,
+ PROP_BUDDY_ICON_PATH,
+ PROP_REMEMBER_PASSWORD,
+ PROP_CHECK_MAIL,
+ PROP_LAST
+};
- for (child = xmlnode_get_child(node, "status"); child != NULL;
- child = xmlnode_get_next_twin(child))
- {
- parse_status(child, account);
- }
-}
+static GObjectClass *parent_class = NULL;
+static GParamSpec *properties[PROP_LAST];
+static GList *handles = NULL;
-static void
-parse_proxy_info(xmlnode *node, PurpleAccount *account)
+/***************
+ * Account API *
+ ***************/
+void
+purple_account_set_register_callback(PurpleAccount *account, PurpleAccountRegistrationCb cb, void *user_data)
{
- PurpleProxyInfo *proxy_info;
- xmlnode *child;
- char *data;
-
- proxy_info = purple_proxy_info_new();
-
- /* Use the global proxy settings, by default */
- purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_USE_GLOBAL);
-
- /* Read proxy type */
- child = xmlnode_get_child(node, "type");
- if ((child != NULL) && ((data = xmlnode_get_data(child)) != NULL))
- {
- if (purple_strequal(data, "global"))
- purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_USE_GLOBAL);
- else if (purple_strequal(data, "none"))
- purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_NONE);
- else if (purple_strequal(data, "http"))
- purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_HTTP);
- else if (purple_strequal(data, "socks4"))
- purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_SOCKS4);
- else if (purple_strequal(data, "socks5"))
- purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_SOCKS5);
- else if (purple_strequal(data, "tor"))
- purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_TOR);
- else if (purple_strequal(data, "envvar"))
- purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_USE_ENVVAR);
- else
- {
- purple_debug_error("account", "Invalid proxy type found when "
- "loading account information for %s\n",
- purple_account_get_username(account));
- }
- g_free(data);
- }
-
- /* Read proxy host */
- child = xmlnode_get_child(node, "host");
- if ((child != NULL) && ((data = xmlnode_get_data(child)) != NULL))
- {
- purple_proxy_info_set_host(proxy_info, data);
- g_free(data);
- }
-
- /* Read proxy port */
- child = xmlnode_get_child(node, "port");
- if ((child != NULL) && ((data = xmlnode_get_data(child)) != NULL))
- {
- purple_proxy_info_set_port(proxy_info, atoi(data));
- g_free(data);
- }
-
- /* Read proxy username */
- child = xmlnode_get_child(node, "username");
- if ((child != NULL) && ((data = xmlnode_get_data(child)) != NULL))
- {
- purple_proxy_info_set_username(proxy_info, data);
- g_free(data);
- }
+ PurpleAccountPrivate *priv;
- /* Read proxy password */
- child = xmlnode_get_child(node, "password");
- if ((child != NULL) && ((data = xmlnode_get_data(child)) != NULL))
- {
- purple_proxy_info_set_password(proxy_info, data);
- g_free(data);
- }
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
- /* If there are no values set then proxy_info NULL */
- if ((purple_proxy_info_get_type(proxy_info) == PURPLE_PROXY_USE_GLOBAL) &&
- (purple_proxy_info_get_host(proxy_info) == NULL) &&
- (purple_proxy_info_get_port(proxy_info) == 0) &&
- (purple_proxy_info_get_username(proxy_info) == NULL) &&
- (purple_proxy_info_get_password(proxy_info) == NULL))
- {
- purple_proxy_info_destroy(proxy_info);
- return;
- }
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
- purple_account_set_proxy_info(account, proxy_info);
+ priv->registration_cb = cb;
+ priv->registration_cb_user_data = user_data;
}
static void
-parse_current_error(xmlnode *node, PurpleAccount *account)
+purple_account_register_got_password_cb(PurpleAccount *account,
+ const gchar *password, GError *error, gpointer data)
{
- guint type;
- char *type_str = NULL, *description = NULL;
- xmlnode *child;
- PurpleConnectionErrorInfo *current_error = NULL;
-
- child = xmlnode_get_child(node, "type");
- if (child == NULL || (type_str = xmlnode_get_data(child)) == NULL)
- return;
- type = atoi(type_str);
- g_free(type_str);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
- if (type > PURPLE_CONNECTION_ERROR_OTHER_ERROR)
- {
- purple_debug_error("account",
- "Invalid PurpleConnectionError value %d found when "
- "loading account information for %s\n",
- type, purple_account_get_username(account));
- type = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
- }
-
- child = xmlnode_get_child(node, "description");
- if (child)
- description = xmlnode_get_data(child);
- if (description == NULL)
- description = g_strdup("");
-
- current_error = g_new0(PurpleConnectionErrorInfo, 1);
- PURPLE_DBUS_REGISTER_POINTER(current_error, PurpleConnectionErrorInfo);
- current_error->type = type;
- current_error->description = description;
-
- set_current_error(account, current_error);
+ _purple_connection_new(account, TRUE, password);
}
-static PurpleAccount *
-parse_account(xmlnode *node)
-{
- PurpleAccount *ret;
- xmlnode *child;
- char *protocol_id = NULL;
- char *name = NULL;
- char *data;
-
- child = xmlnode_get_child(node, "protocol");
- if (child != NULL)
- protocol_id = xmlnode_get_data(child);
-
- child = xmlnode_get_child(node, "name");
- if (child != NULL)
- name = xmlnode_get_data(child);
- if (name == NULL)
- {
- /* Do we really need to do this? */
- child = xmlnode_get_child(node, "username");
- if (child != NULL)
- name = xmlnode_get_data(child);
- }
-
- if ((protocol_id == NULL) || (name == NULL))
- {
- g_free(protocol_id);
- g_free(name);
- return NULL;
- }
-
- ret = purple_account_new(name, _purple_oscar_convert(name, protocol_id)); /* XXX: */
- g_free(name);
- g_free(protocol_id);
-
- /* Read the password */
- child = xmlnode_get_child(node, "password");
- if (child != NULL) {
- const char *keyring_id = xmlnode_get_attrib(child, "keyring_id");
- const char *mode = xmlnode_get_attrib(child, "mode");
- gboolean is_plaintext;
-
- data = xmlnode_get_data(child);
-
- if (keyring_id == NULL || keyring_id[0] == '\0')
- is_plaintext = TRUE;
- else if (g_strcmp0(keyring_id, "keyring-internal") != 0)
- is_plaintext = FALSE;
- else if (mode == NULL || mode[0] == '\0' || g_strcmp0(mode, "cleartext") == 0)
- is_plaintext = TRUE;
- else
- is_plaintext = FALSE;
-
- if (is_plaintext) {
- purple_account_set_remember_password(ret, TRUE);
- purple_account_set_password(ret, data);
- } else {
- purple_debug_warning("account", "found encrypted password, "
- "but it's not supported in 2.x.y\n");
- _purple_account_set_encrypted_password(ret, keyring_id, mode, data);
- }
- g_free(data);
- }
-
- /* Read the alias */
- child = xmlnode_get_child(node, "alias");
- if ((child != NULL) && ((data = xmlnode_get_data(child)) != NULL))
- {
- if (*data != '\0')
- purple_account_set_alias(ret, data);
- g_free(data);
- }
-
- /* Read the statuses */
- child = xmlnode_get_child(node, "statuses");
- if (child != NULL)
- {
- parse_statuses(child, ret);
- }
-
- /* Read the userinfo */
- child = xmlnode_get_child(node, "userinfo");
- if ((child != NULL) && ((data = xmlnode_get_data(child)) != NULL))
- {
- purple_account_set_user_info(ret, data);
- g_free(data);
- }
-
- /* Read an old buddyicon */
- child = xmlnode_get_child(node, "buddyicon");
- if ((child != NULL) && ((data = xmlnode_get_data(child)) != NULL))
- {
- const char *dirname = purple_buddy_icons_get_cache_dir();
- char *filename = g_build_filename(dirname, data, NULL);
- gchar *contents;
- gsize len;
-
- if (g_file_get_contents(filename, &contents, &len, NULL))
- {
- purple_buddy_icons_set_account_icon(ret, (guchar *)contents, len);
- }
- else
- {
- /* Try to see if the icon got left behind in the old cache. */
- g_free(filename);
- filename = g_build_filename(g_get_home_dir(), ".gaim", "icons", data, NULL);
- if (g_file_get_contents(filename, &contents, &len, NULL)) {
- purple_buddy_icons_set_account_icon(ret, (guchar*)contents, len);
- }
- }
-
- g_free(filename);
- g_free(data);
- }
-
- /* Read settings (both core and UI) */
- for (child = xmlnode_get_child(node, "settings"); child != NULL;
- child = xmlnode_get_next_twin(child))
- {
- parse_settings(child, ret);
- }
-
- /* Read proxy */
- child = xmlnode_get_child(node, "proxy");
- if (child != NULL)
- {
- parse_proxy_info(child, ret);
- }
-
- /* Read current error */
- child = xmlnode_get_child(node, "current_error");
- if (child != NULL)
- {
- parse_current_error(child, ret);
- }
-
- return ret;
-}
-
-static void
-load_accounts(void)
+void
+purple_account_register(PurpleAccount *account)
{
- xmlnode *node, *child;
-
- accounts_loaded = TRUE;
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
- node = purple_util_read_xml_from_file("accounts.xml", _("accounts"));
-
- if (node == NULL)
- return;
-
- for (child = xmlnode_get_child(node, "account"); child != NULL;
- child = xmlnode_get_next_twin(child))
- {
- PurpleAccount *new_acct;
- new_acct = parse_account(child);
- purple_accounts_add(new_acct);
- }
-
- xmlnode_free(node);
+ purple_debug_info("account", "Registering account %s\n",
+ purple_account_get_username(account));
- _purple_buddy_icons_account_loaded_cb();
+ purple_keyring_get_password(account,
+ purple_account_register_got_password_cb, NULL);
}
-
static void
-delete_setting(void *data)
+purple_account_unregister_got_password_cb(PurpleAccount *account,
+ const gchar *password, GError *error, gpointer data)
{
- PurpleAccountSetting *setting = (PurpleAccountSetting *)data;
+ PurpleCallbackBundle *cbb = data;
+ PurpleAccountUnregistrationCb cb;
- g_free(setting->ui);
-
- if (setting->type == PURPLE_PREF_STRING)
- g_free(setting->value.string);
+ cb = (PurpleAccountUnregistrationCb)cbb->cb;
+ _purple_connection_new_unregister(account, password, cb, cbb->data);
- g_free(setting);
+ g_free(cbb);
}
-PurpleAccount *
-purple_account_new(const char *username, const char *protocol_id)
+struct register_completed_closure
{
- PurpleAccount *account = NULL;
- PurpleAccountPrivate *priv = NULL;
- PurplePlugin *prpl = NULL;
- PurplePluginProtocolInfo *prpl_info = NULL;
- PurpleStatusType *status_type;
-
- g_return_val_if_fail(username != NULL, NULL);
- g_return_val_if_fail(protocol_id != NULL, NULL);
-
- account = purple_accounts_find(username, protocol_id);
-
- if (account != NULL)
- return account;
-
- account = g_new0(PurpleAccount, 1);
- PURPLE_DBUS_REGISTER_POINTER(account, PurpleAccount);
- priv = g_new0(PurpleAccountPrivate, 1);
- account->priv = priv;
-
- purple_account_set_username(account, username);
-
- purple_account_set_protocol_id(account, protocol_id);
-
- account->settings = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, delete_setting);
- account->ui_settings = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, (GDestroyNotify)g_hash_table_destroy);
- account->system_log = NULL;
- /* 0 is not a valid privacy setting */
- account->perm_deny = PURPLE_PRIVACY_ALLOW_ALL;
-
- purple_signal_emit(purple_accounts_get_handle(), "account-created", account);
-
- prpl = purple_find_prpl(protocol_id);
-
- if (prpl == NULL)
- return account;
-
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
- if (prpl_info != NULL && prpl_info->status_types != NULL)
- purple_account_set_status_types(account, prpl_info->status_types(account));
-
- account->presence = purple_presence_new_for_account(account);
-
- status_type = purple_account_get_status_type_with_primitive(account, PURPLE_STATUS_AVAILABLE);
- if (status_type != NULL)
- purple_presence_set_status_active(account->presence,
- purple_status_type_get_id(status_type),
- TRUE);
- else
- purple_presence_set_status_active(account->presence,
- "offline",
- TRUE);
-
- return account;
-}
+ PurpleAccount *account;
+ gboolean succeeded;
+};
-void
-purple_account_destroy(PurpleAccount *account)
+static gboolean
+purple_account_register_completed_cb(gpointer data)
{
- PurpleAccountPrivate *priv = NULL;
- GList *l;
-
- g_return_if_fail(account != NULL);
-
- purple_debug_info("account", "Destroying account %p\n", account);
- purple_signal_emit(purple_accounts_get_handle(), "account-destroying", account);
-
- for (l = purple_get_conversations(); l != NULL; l = l->next)
- {
- PurpleConversation *conv = (PurpleConversation *)l->data;
-
- if (purple_conversation_get_account(conv) == account)
- purple_conversation_set_account(conv, NULL);
- }
-
- g_free(account->username);
- g_free(account->alias);
- g_free(account->password);
- g_free(account->user_info);
- g_free(account->buddy_icon_path);
- g_free(account->protocol_id);
-
- g_hash_table_destroy(account->settings);
- g_hash_table_destroy(account->ui_settings);
-
- if (account->proxy_info)
- purple_proxy_info_destroy(account->proxy_info);
-
- purple_account_set_status_types(account, NULL);
-
- purple_presence_destroy(account->presence);
-
- if(account->system_log)
- purple_log_free(account->system_log);
-
- while (account->deny) {
- g_free(account->deny->data);
- account->deny = g_slist_delete_link(account->deny, account->deny);
- }
-
- while (account->permit) {
- g_free(account->permit->data);
- account->permit = g_slist_delete_link(account->permit, account->permit);
- }
+ struct register_completed_closure *closure = data;
+ PurpleAccountPrivate *priv;
- priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
- PURPLE_DBUS_UNREGISTER_POINTER(priv->current_error);
- if (priv->current_error) {
- g_free(priv->current_error->description);
- g_free(priv->current_error);
- }
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(closure->account);
- g_free(priv->password_keyring);
- g_free(priv->password_mode);
- g_free(priv->password_ciphertext);
+ if (priv->registration_cb)
+ (priv->registration_cb)(closure->account, closure->succeeded,
+ priv->registration_cb_user_data);
- g_free(priv);
+ g_object_unref(closure->account);
+ g_free(closure);
- PURPLE_DBUS_UNREGISTER_POINTER(account);
- g_free(account);
+ return FALSE;
}
void
-purple_account_set_register_callback(PurpleAccount *account, PurpleAccountRegistrationCb cb, void *user_data)
+purple_account_register_completed(PurpleAccount *account, gboolean succeeded)
{
- g_return_if_fail(account != NULL);
+ struct register_completed_closure *closure;
- account->registration_cb = cb;
- account->registration_cb_user_data = user_data;
-}
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
-void
-purple_account_register(PurpleAccount *account)
-{
- g_return_if_fail(account != NULL);
+ closure = g_new0(struct register_completed_closure, 1);
+ closure->account = g_object_ref(account);
+ closure->succeeded = succeeded;
- purple_debug_info("account", "Registering account %s\n",
- purple_account_get_username(account));
-
- _purple_connection_new(account, TRUE, purple_account_get_password(account));
+ purple_timeout_add(0, purple_account_register_completed_cb, closure);
}
void
purple_account_unregister(PurpleAccount *account, PurpleAccountUnregistrationCb cb, void *user_data)
{
- g_return_if_fail(account != NULL);
+ PurpleCallbackBundle *cbb;
+
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
purple_debug_info("account", "Unregistering account %s\n",
purple_account_get_username(account));
- _purple_connection_new_unregister(account, purple_account_get_password(account), cb, user_data);
+ cbb = g_new0(PurpleCallbackBundle, 1);
+ cbb->cb = PURPLE_CALLBACK(cb);
+ cbb->data = user_data;
+
+ purple_keyring_get_password(account,
+ purple_account_unregister_got_password_cb, cbb);
}
static void
@@ -1223,15 +249,15 @@ request_password_ok_cb(PurpleAccount *account, PurpleRequestFields *fields)
if (!entry || !*entry)
{
- purple_notify_error(account, NULL, _("Password is required to sign on."), NULL);
+ purple_notify_error(account, NULL,
+ _("Password is required to sign on."), NULL,
+ purple_request_cpar_from_account(account));
return;
}
- if(remember)
- purple_account_set_remember_password(account, TRUE);
-
- purple_account_set_password(account, entry);
+ purple_account_set_remember_password(account, remember);
+ purple_account_set_password(account, entry, NULL, NULL);
_purple_connection_new(account, FALSE, entry);
}
@@ -1272,26 +298,37 @@ purple_account_request_password(PurpleAccount *account, GCallback ok_cb,
field = purple_request_field_bool_new("remember", _("Save password"), FALSE);
purple_request_field_group_add_field(group, field);
- purple_request_fields(account,
- NULL,
- primary,
- NULL,
- fields,
- _("OK"), ok_cb,
- _("Cancel"), cancel_cb,
- account, NULL, NULL,
- user_data);
+ purple_request_fields(account, NULL, primary, NULL, fields, _("OK"),
+ ok_cb, _("Cancel"), cancel_cb,
+ purple_request_cpar_from_account(account), user_data);
g_free(primary);
}
+static void
+purple_account_connect_got_password_cb(PurpleAccount *account,
+ const gchar *password, GError *error, gpointer data)
+{
+ PurplePluginProtocolInfo *prpl_info = data;
+
+ if ((password == NULL || *password == '\0') &&
+ !(prpl_info->options & OPT_PROTO_NO_PASSWORD) &&
+ !(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL))
+ purple_account_request_password(account,
+ G_CALLBACK(request_password_ok_cb),
+ G_CALLBACK(request_password_cancel_cb), account);
+ else
+ _purple_connection_new(account, FALSE, password);
+}
+
void
purple_account_connect(PurpleAccount *account)
{
PurplePlugin *prpl;
- const char *password, *username;
+ const char *username;
PurplePluginProtocolInfo *prpl_info;
+ PurpleAccountPrivate *priv;
- g_return_if_fail(account != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
username = purple_account_get_username(account);
@@ -1307,45 +344,61 @@ purple_account_connect(PurpleAccount *account)
gchar *message;
message = g_strdup_printf(_("Missing protocol plugin for %s"), username);
- purple_notify_error(account, _("Connection Error"), message, NULL);
+ purple_notify_error(account, _("Connection Error"), message,
+ NULL, purple_request_cpar_from_account(account));
g_free(message);
return;
}
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
purple_debug_info("account", "Connecting to account %s.\n", username);
prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
- password = purple_account_get_password(account);
- if ((password == NULL) &&
- !(prpl_info->options & OPT_PROTO_NO_PASSWORD) &&
- !(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL))
- purple_account_request_password(account, G_CALLBACK(request_password_ok_cb), G_CALLBACK(request_password_cancel_cb), account);
- else
- _purple_connection_new(account, FALSE, password);
+ if (priv->password != NULL) {
+ purple_account_connect_got_password_cb(account,
+ priv->password, NULL, prpl_info);
+ } else {
+ purple_keyring_get_password(account,
+ purple_account_connect_got_password_cb, prpl_info);
+ }
}
void
purple_account_disconnect(PurpleAccount *account)
{
PurpleConnection *gc;
+ PurpleAccountPrivate *priv;
const char *username;
- g_return_if_fail(account != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+ g_return_if_fail(!purple_account_is_disconnecting(account));
g_return_if_fail(!purple_account_is_disconnected(account));
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
username = purple_account_get_username(account);
purple_debug_info("account", "Disconnecting account %s (%p)\n",
username ? username : "(null)", account);
- account->disconnecting = TRUE;
+ priv->disconnecting = TRUE;
gc = purple_account_get_connection(account);
- _purple_connection_destroy(gc);
- if (!purple_account_get_remember_password(account))
- purple_account_set_password(account, NULL);
+ g_object_unref(gc);
purple_account_set_connection(account, NULL);
- account->disconnecting = FALSE;
+ priv->disconnecting = FALSE;
+}
+
+gboolean
+purple_account_is_disconnecting(const PurpleAccount *account)
+{
+ PurpleAccountPrivate *priv;
+
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), TRUE);
+
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ return priv->disconnecting;
}
void
@@ -1355,7 +408,7 @@ purple_account_notify_added(PurpleAccount *account, const char *remote_user,
{
PurpleAccountUiOps *ui_ops;
- g_return_if_fail(account != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(remote_user != NULL);
ui_ops = purple_accounts_get_ui_ops();
@@ -1371,7 +424,7 @@ purple_account_request_add(PurpleAccount *account, const char *remote_user,
{
PurpleAccountUiOps *ui_ops;
- g_return_if_fail(account != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(remote_user != NULL);
ui_ops = purple_accounts_get_ui_ops();
@@ -1410,7 +463,7 @@ purple_account_request_close_with_account(PurpleAccount *account)
{
GList *l, *l_next;
- g_return_if_fail(account != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
for (l = handles; l != NULL; l = l_next) {
PurpleAccountRequestInfo *info = l->data;
@@ -1444,33 +497,33 @@ purple_account_request_close(void *ui_handle)
}
static void
-request_auth_cb(void *data)
+request_auth_cb(const char *message, void *data)
{
PurpleAccountRequestInfo *info = data;
handles = g_list_remove(handles, info);
if (info->auth_cb != NULL)
- info->auth_cb(info->userdata);
+ info->auth_cb(message, info->userdata);
purple_signal_emit(purple_accounts_get_handle(),
- "account-authorization-granted", info->account, info->user);
+ "account-authorization-granted", info->account, info->user, message);
purple_account_request_info_unref(info);
}
static void
-request_deny_cb(void *data)
+request_deny_cb(const char *message, void *data)
{
PurpleAccountRequestInfo *info = data;
handles = g_list_remove(handles, info);
if (info->deny_cb != NULL)
- info->deny_cb(info->userdata);
+ info->deny_cb(message, info->userdata);
purple_signal_emit(purple_accounts_get_handle(),
- "account-authorization-denied", info->account, info->user);
+ "account-authorization-denied", info->account, info->user, message);
purple_account_request_info_unref(info);
}
@@ -1483,47 +536,39 @@ purple_account_request_authorization(PurpleAccount *account, const char *remote_
PurpleAccountUiOps *ui_ops;
PurpleAccountRequestInfo *info;
int plugin_return;
+ char *response = NULL;
- g_return_val_if_fail(account != NULL, NULL);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
g_return_val_if_fail(remote_user != NULL, NULL);
ui_ops = purple_accounts_get_ui_ops();
plugin_return = GPOINTER_TO_INT(
- purple_signal_emit_return_1(purple_accounts_get_handle(),
- "account-authorization-requested", account, remote_user));
-
- if (plugin_return > 0) {
- if (auth_cb != NULL)
- auth_cb(user_data);
- return NULL;
- } else if (plugin_return < 0) {
- if (deny_cb != NULL)
- deny_cb(user_data);
- return NULL;
- }
-
- plugin_return = GPOINTER_TO_INT(
purple_signal_emit_return_1(
purple_accounts_get_handle(),
- "account-authorization-requested-with-message",
- account, remote_user, message
+ "account-authorization-requested",
+ account, remote_user, message, &response
));
switch (plugin_return)
{
case PURPLE_ACCOUNT_RESPONSE_IGNORE:
+ g_free(response);
return NULL;
case PURPLE_ACCOUNT_RESPONSE_ACCEPT:
if (auth_cb != NULL)
- auth_cb(user_data);
+ auth_cb(response, user_data);
+ g_free(response);
return NULL;
case PURPLE_ACCOUNT_RESPONSE_DENY:
if (deny_cb != NULL)
- deny_cb(user_data);
+ deny_cb(response, user_data);
+ g_free(response);
return NULL;
}
+ g_free(response);
+
if (ui_ops != NULL && ui_ops->request_authorize != NULL) {
info = g_new0(PurpleAccountRequestInfo, 1);
info->type = PURPLE_ACCOUNT_REQUEST_AUTHORIZATION;
@@ -1561,7 +606,8 @@ change_password_cb(PurpleAccount *account, PurpleRequestFields *fields)
if (g_utf8_collate(new_pass_1, new_pass_2))
{
purple_notify_error(account, NULL,
- _("New passwords do not match."), NULL);
+ _("New passwords do not match."), NULL,
+ purple_request_cpar_from_account(account));
return;
}
@@ -1574,7 +620,8 @@ change_password_cb(PurpleAccount *account, PurpleRequestFields *fields)
(new_pass_2 == NULL || *new_pass_2 == '\0')))
{
purple_notify_error(account, NULL,
- _("Fill out all fields completely."), NULL);
+ _("Fill out all fields completely."), NULL,
+ purple_request_cpar_from_account(account));
return;
}
@@ -1592,7 +639,7 @@ purple_account_request_change_password(PurpleAccount *account)
PurplePluginProtocolInfo *prpl_info = NULL;
char primary[256];
- g_return_if_fail(account != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(purple_account_is_connected(account));
gc = purple_account_get_connection(account);
@@ -1634,16 +681,11 @@ purple_account_request_change_password(PurpleAccount *account)
/* I'm sticking this somewhere in the code: bologna */
- purple_request_fields(purple_account_get_connection(account),
- NULL,
- primary,
- _("Please enter your current password and your "
- "new password."),
- fields,
- _("OK"), G_CALLBACK(change_password_cb),
- _("Cancel"), NULL,
- account, NULL, NULL,
- account);
+ purple_request_fields(purple_account_get_connection(account), NULL,
+ primary, _("Please enter your current password and your new "
+ "password."), fields, _("OK"), G_CALLBACK(change_password_cb),
+ _("Cancel"), NULL, purple_request_cpar_from_account(account),
+ account);
}
static void
@@ -1653,7 +695,7 @@ set_user_info_cb(PurpleAccount *account, const char *user_info)
purple_account_set_user_info(account, user_info);
gc = purple_account_get_connection(account);
- serv_set_info(gc, user_info);
+ purple_serv_set_info(gc, user_info);
}
void
@@ -1662,7 +704,7 @@ purple_account_request_change_user_info(PurpleAccount *account)
PurpleConnection *gc;
char primary[256];
- g_return_if_fail(account != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(purple_account_is_connected(account));
gc = purple_account_get_connection(account);
@@ -1674,10 +716,10 @@ purple_account_request_change_user_info(PurpleAccount *account)
purple_request_input(gc, _("Set User Info"), primary, NULL,
purple_account_get_user_info(account),
TRUE, FALSE, ((gc != NULL) &&
- (gc->flags & PURPLE_CONNECTION_HTML) ? "html" : NULL),
+ (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_HTML) ? "html" : NULL),
_("Save"), G_CALLBACK(set_user_info_cb),
_("Cancel"), NULL,
- account, NULL, NULL,
+ purple_request_cpar_from_account(account),
account);
}
@@ -1685,13 +727,18 @@ void
purple_account_set_username(PurpleAccount *account, const char *username)
{
PurpleBlistUiOps *blist_ops;
+ PurpleAccountPrivate *priv;
+
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
- g_return_if_fail(account != NULL);
+ g_free(priv->username);
+ priv->username = g_strdup(username);
- g_free(account->username);
- account->username = g_strdup(username);
+ g_object_notify_by_pspec(G_OBJECT(account), properties[PROP_USERNAME]);
- schedule_accounts_save();
+ purple_accounts_schedule_save();
/* if the name changes, we should re-write the buddy list
* to disk with the new name */
@@ -1700,100 +747,154 @@ purple_account_set_username(PurpleAccount *account, const char *username)
blist_ops->save_account(account);
}
-void
-purple_account_set_password(PurpleAccount *account, const char *password)
+void
+purple_account_set_password(PurpleAccount *account, const gchar *password,
+ PurpleKeyringSaveCallback cb, gpointer data)
{
- g_return_if_fail(account != NULL);
+ PurpleAccountPrivate *priv;
+
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
- g_free(account->password);
- account->password = g_strdup(password);
+ purple_str_wipe(priv->password);
+ priv->password = g_strdup(password);
- schedule_accounts_save();
+ purple_accounts_schedule_save();
+
+ if (!purple_account_get_remember_password(account)) {
+ purple_debug_info("account",
+ "Password for %s set, not sent to keyring.\n",
+ purple_account_get_username(account));
+
+ if (cb != NULL)
+ cb(account, NULL, data);
+ } else {
+ purple_keyring_set_password(account, password, cb, data);
+ }
}
void
-purple_account_set_alias(PurpleAccount *account, const char *alias)
+purple_account_set_private_alias(PurpleAccount *account, const char *alias)
{
- g_return_if_fail(account != NULL);
+ PurpleAccountPrivate *priv;
+
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
/*
- * Do nothing if alias and account->alias are both NULL. Or if
+ * Do nothing if alias and priv->alias are both NULL. Or if
* they're the exact same string.
*/
- if (alias == account->alias)
+ if (alias == priv->alias)
return;
- if ((!alias && account->alias) || (alias && !account->alias) ||
- g_utf8_collate(account->alias, alias))
+ if ((!alias && priv->alias) || (alias && !priv->alias) ||
+ g_utf8_collate(priv->alias, alias))
{
- char *old = account->alias;
+ char *old = priv->alias;
- account->alias = g_strdup(alias);
+ priv->alias = g_strdup(alias);
+ g_object_notify_by_pspec(G_OBJECT(account),
+ properties[PROP_PRIVATE_ALIAS]);
purple_signal_emit(purple_accounts_get_handle(), "account-alias-changed",
account, old);
g_free(old);
- schedule_accounts_save();
+ purple_accounts_schedule_save();
}
}
void
purple_account_set_user_info(PurpleAccount *account, const char *user_info)
{
- g_return_if_fail(account != NULL);
+ PurpleAccountPrivate *priv;
+
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
- g_free(account->user_info);
- account->user_info = g_strdup(user_info);
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
- schedule_accounts_save();
+ g_free(priv->user_info);
+ priv->user_info = g_strdup(user_info);
+
+ g_object_notify_by_pspec(G_OBJECT(account), properties[PROP_USER_INFO]);
+
+ purple_accounts_schedule_save();
}
void purple_account_set_buddy_icon_path(PurpleAccount *account, const char *path)
{
- g_return_if_fail(account != NULL);
+ PurpleAccountPrivate *priv;
- g_free(account->buddy_icon_path);
- account->buddy_icon_path = g_strdup(path);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
- schedule_accounts_save();
+ g_free(priv->buddy_icon_path);
+ priv->buddy_icon_path = g_strdup(path);
+
+ g_object_notify_by_pspec(G_OBJECT(account),
+ properties[PROP_BUDDY_ICON_PATH]);
+
+ purple_accounts_schedule_save();
}
void
purple_account_set_protocol_id(PurpleAccount *account, const char *protocol_id)
{
- g_return_if_fail(account != NULL);
+ PurpleAccountPrivate *priv;
+
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(protocol_id != NULL);
- g_free(account->protocol_id);
- account->protocol_id = g_strdup(protocol_id);
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ g_free(priv->protocol_id);
+ priv->protocol_id = g_strdup(protocol_id);
+
+ g_object_notify_by_pspec(G_OBJECT(account), properties[PROP_PROTOCOL_ID]);
- schedule_accounts_save();
+ purple_accounts_schedule_save();
}
void
purple_account_set_connection(PurpleAccount *account, PurpleConnection *gc)
{
- g_return_if_fail(account != NULL);
+ PurpleAccountPrivate *priv;
- account->gc = gc;
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ priv->gc = gc;
+
+ g_object_notify_by_pspec(G_OBJECT(account), properties[PROP_CONNECTION]);
}
void
purple_account_set_remember_password(PurpleAccount *account, gboolean value)
{
- g_return_if_fail(account != NULL);
+ PurpleAccountPrivate *priv;
+
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
- account->remember_pass = value;
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ priv->remember_pass = value;
+
+ g_object_notify_by_pspec(G_OBJECT(account),
+ properties[PROP_REMEMBER_PASSWORD]);
- schedule_accounts_save();
+ purple_accounts_schedule_save();
}
void
purple_account_set_check_mail(PurpleAccount *account, gboolean value)
{
- g_return_if_fail(account != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
purple_account_set_bool(account, "check-mail", value);
+
+ g_object_notify_by_pspec(G_OBJECT(account), properties[PROP_CHECK_MAIL]);
}
void
@@ -1801,10 +902,11 @@ purple_account_set_enabled(PurpleAccount *account, const char *ui,
gboolean value)
{
PurpleConnection *gc;
+ PurpleAccountPrivate *priv;
gboolean was_enabled = FALSE;
- g_return_if_fail(account != NULL);
- g_return_if_fail(ui != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+ g_return_if_fail(ui != NULL);
was_enabled = purple_account_get_enabled(account, ui);
@@ -1816,10 +918,14 @@ purple_account_set_enabled(PurpleAccount *account, const char *ui,
else if(!was_enabled && value)
purple_signal_emit(purple_accounts_get_handle(), "account-enabled", account);
- if ((gc != NULL) && (gc->wants_to_die == TRUE))
+ g_object_notify_by_pspec(G_OBJECT(account), properties[PROP_ENABLED]);
+
+ if ((gc != NULL) && (_purple_connection_wants_to_die(gc)))
return;
- if (value && purple_presence_is_online(account->presence))
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ if (value && purple_presence_is_online(priv->presence))
purple_account_connect(account);
else if (!value && !purple_account_is_disconnected(account))
purple_account_disconnect(account);
@@ -1828,38 +934,49 @@ purple_account_set_enabled(PurpleAccount *account, const char *ui,
void
purple_account_set_proxy_info(PurpleAccount *account, PurpleProxyInfo *info)
{
- g_return_if_fail(account != NULL);
+ PurpleAccountPrivate *priv;
+
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
- if (account->proxy_info != NULL)
- purple_proxy_info_destroy(account->proxy_info);
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
- account->proxy_info = info;
+ if (priv->proxy_info != NULL)
+ purple_proxy_info_destroy(priv->proxy_info);
- schedule_accounts_save();
+ priv->proxy_info = info;
+
+ purple_accounts_schedule_save();
}
void
-purple_account_set_privacy_type(PurpleAccount *account, PurplePrivacyType privacy_type)
+purple_account_set_privacy_type(PurpleAccount *account, PurpleAccountPrivacyType privacy_type)
{
- g_return_if_fail(account != NULL);
+ PurpleAccountPrivate *priv;
- account->perm_deny = privacy_type;
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ priv->privacy_type = privacy_type;
}
void
purple_account_set_status_types(PurpleAccount *account, GList *status_types)
{
- g_return_if_fail(account != NULL);
+ PurpleAccountPrivate *priv;
+
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
/* Out with the old... */
- if (account->status_types != NULL)
+ if (priv->status_types != NULL)
{
- g_list_foreach(account->status_types, (GFunc)purple_status_type_destroy, NULL);
- g_list_free(account->status_types);
+ g_list_foreach(priv->status_types, (GFunc)purple_status_type_destroy, NULL);
+ g_list_free(priv->status_types);
}
/* In with the new... */
- account->status_types = status_types;
+ priv->status_types = status_types;
}
void
@@ -1889,7 +1006,7 @@ purple_account_set_status_list(PurpleAccount *account, const char *status_id,
{
PurpleStatus *status;
- g_return_if_fail(account != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(status_id != NULL);
status = purple_account_get_status(account, status_id);
@@ -1909,7 +1026,7 @@ purple_account_set_status_list(PurpleAccount *account, const char *status_id,
* Our current statuses are saved to accounts.xml (so that when we
* reconnect, we go back to the previous status).
*/
- schedule_accounts_save();
+ purple_accounts_schedule_save();
}
struct public_alias_closure
@@ -1926,6 +1043,8 @@ set_public_alias_unsupported(gpointer data)
failure_cb(closure->account,
_("This protocol does not support setting a public alias."));
+
+ g_object_unref(closure->account);
g_free(closure);
return FALSE;
@@ -1940,7 +1059,7 @@ purple_account_set_public_alias(PurpleAccount *account,
PurplePlugin *prpl = NULL;
PurplePluginProtocolInfo *prpl_info = NULL;
- g_return_if_fail(account != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(purple_account_is_connected(account));
gc = purple_account_get_connection(account);
@@ -1952,7 +1071,7 @@ purple_account_set_public_alias(PurpleAccount *account,
else if (failure_cb) {
struct public_alias_closure *closure =
g_new0(struct public_alias_closure, 1);
- closure->account = account;
+ closure->account = g_object_ref(account);
closure->failure_cb = failure_cb;
purple_timeout_add(0, set_public_alias_unsupported, closure);
}
@@ -1966,6 +1085,8 @@ get_public_alias_unsupported(gpointer data)
failure_cb(closure->account,
_("This protocol does not support fetching the public alias."));
+
+ g_object_unref(closure->account);
g_free(closure);
return FALSE;
@@ -1980,7 +1101,7 @@ purple_account_get_public_alias(PurpleAccount *account,
PurplePlugin *prpl = NULL;
PurplePluginProtocolInfo *prpl_info = NULL;
- g_return_if_fail(account != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(purple_account_is_connected(account));
gc = purple_account_get_connection(account);
@@ -1992,7 +1113,7 @@ purple_account_get_public_alias(PurpleAccount *account,
else if (failure_cb) {
struct public_alias_closure *closure =
g_new0(struct public_alias_closure, 1);
- closure->account = account;
+ closure->account = g_object_ref(account);
closure->failure_cb = failure_cb;
purple_timeout_add(0, get_public_alias_unsupported, closure);
}
@@ -2007,47 +1128,68 @@ purple_account_get_silence_suppression(const PurpleAccount *account)
void
purple_account_set_silence_suppression(PurpleAccount *account, gboolean value)
{
- g_return_if_fail(account != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
purple_account_set_bool(account, "silence-suppression", value);
}
+static void
+delete_setting(void *data)
+{
+ PurpleAccountSetting *setting = (PurpleAccountSetting *)data;
+
+ g_free(setting->ui);
+ g_value_unset(&setting->value);
+
+ g_free(setting);
+}
+
void
purple_account_clear_settings(PurpleAccount *account)
{
- g_return_if_fail(account != NULL);
+ PurpleAccountPrivate *priv;
+
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
- g_hash_table_destroy(account->settings);
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ g_hash_table_destroy(priv->settings);
- account->settings = g_hash_table_new_full(g_str_hash, g_str_equal,
+ priv->settings = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, delete_setting);
}
void
purple_account_remove_setting(PurpleAccount *account, const char *setting)
{
- g_return_if_fail(account != NULL);
+ PurpleAccountPrivate *priv;
+
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(setting != NULL);
- g_hash_table_remove(account->settings, setting);
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ g_hash_table_remove(priv->settings, setting);
}
void
purple_account_set_int(PurpleAccount *account, const char *name, int value)
{
PurpleAccountSetting *setting;
+ PurpleAccountPrivate *priv;
- g_return_if_fail(account != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(name != NULL);
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
setting = g_new0(PurpleAccountSetting, 1);
- setting->type = PURPLE_PREF_INT;
- setting->value.integer = value;
+ g_value_init(&setting->value, G_TYPE_INT);
+ g_value_set_int(&setting->value, value);
- g_hash_table_insert(account->settings, g_strdup(name), setting);
+ g_hash_table_insert(priv->settings, g_strdup(name), setting);
- schedule_accounts_save();
+ purple_accounts_schedule_save();
}
void
@@ -2055,49 +1197,56 @@ purple_account_set_string(PurpleAccount *account, const char *name,
const char *value)
{
PurpleAccountSetting *setting;
+ PurpleAccountPrivate *priv;
- g_return_if_fail(account != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(name != NULL);
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
setting = g_new0(PurpleAccountSetting, 1);
- setting->type = PURPLE_PREF_STRING;
- setting->value.string = g_strdup(value);
+ g_value_init(&setting->value, G_TYPE_STRING);
+ g_value_set_string(&setting->value, value);
- g_hash_table_insert(account->settings, g_strdup(name), setting);
+ g_hash_table_insert(priv->settings, g_strdup(name), setting);
- schedule_accounts_save();
+ purple_accounts_schedule_save();
}
void
purple_account_set_bool(PurpleAccount *account, const char *name, gboolean value)
{
PurpleAccountSetting *setting;
+ PurpleAccountPrivate *priv;
- g_return_if_fail(account != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(name != NULL);
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
setting = g_new0(PurpleAccountSetting, 1);
- setting->type = PURPLE_PREF_BOOLEAN;
- setting->value.boolean = value;
+ g_value_init(&setting->value, G_TYPE_BOOLEAN);
+ g_value_set_boolean(&setting->value, value);
- g_hash_table_insert(account->settings, g_strdup(name), setting);
+ g_hash_table_insert(priv->settings, g_strdup(name), setting);
- schedule_accounts_save();
+ purple_accounts_schedule_save();
}
static GHashTable *
get_ui_settings_table(PurpleAccount *account, const char *ui)
{
GHashTable *table;
+ PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
- table = g_hash_table_lookup(account->ui_settings, ui);
+ table = g_hash_table_lookup(priv->ui_settings, ui);
if (table == NULL) {
table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
delete_setting);
- g_hash_table_insert(account->ui_settings, g_strdup(ui), table);
+ g_hash_table_insert(priv->ui_settings, g_strdup(ui), table);
}
return table;
@@ -2110,21 +1259,21 @@ purple_account_set_ui_int(PurpleAccount *account, const char *ui,
PurpleAccountSetting *setting;
GHashTable *table;
- g_return_if_fail(account != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(ui != NULL);
g_return_if_fail(name != NULL);
setting = g_new0(PurpleAccountSetting, 1);
- setting->type = PURPLE_PREF_INT;
setting->ui = g_strdup(ui);
- setting->value.integer = value;
+ g_value_init(&setting->value, G_TYPE_INT);
+ g_value_set_int(&setting->value, value);
table = get_ui_settings_table(account, ui);
g_hash_table_insert(table, g_strdup(name), setting);
- schedule_accounts_save();
+ purple_accounts_schedule_save();
}
void
@@ -2134,21 +1283,21 @@ purple_account_set_ui_string(PurpleAccount *account, const char *ui,
PurpleAccountSetting *setting;
GHashTable *table;
- g_return_if_fail(account != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(ui != NULL);
g_return_if_fail(name != NULL);
setting = g_new0(PurpleAccountSetting, 1);
- setting->type = PURPLE_PREF_STRING;
setting->ui = g_strdup(ui);
- setting->value.string = g_strdup(value);
+ g_value_init(&setting->value, G_TYPE_STRING);
+ g_value_set_string(&setting->value, value);
table = get_ui_settings_table(account, ui);
g_hash_table_insert(table, g_strdup(name), setting);
- schedule_accounts_save();
+ purple_accounts_schedule_save();
}
void
@@ -2158,21 +1307,21 @@ purple_account_set_ui_bool(PurpleAccount *account, const char *ui,
PurpleAccountSetting *setting;
GHashTable *table;
- g_return_if_fail(account != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(ui != NULL);
g_return_if_fail(name != NULL);
setting = g_new0(PurpleAccountSetting, 1);
- setting->type = PURPLE_PREF_BOOLEAN;
setting->ui = g_strdup(ui);
- setting->value.boolean = value;
+ g_value_init(&setting->value, G_TYPE_BOOLEAN);
+ g_value_set_boolean(&setting->value, value);
table = get_ui_settings_table(account, ui);
g_hash_table_insert(table, g_strdup(name), setting);
- schedule_accounts_save();
+ purple_accounts_schedule_save();
}
static PurpleConnectionState
@@ -2180,11 +1329,11 @@ purple_account_get_state(const PurpleAccount *account)
{
PurpleConnection *gc;
- g_return_val_if_fail(account != NULL, PURPLE_DISCONNECTED);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), PURPLE_CONNECTION_DISCONNECTED);
gc = purple_account_get_connection(account);
if (!gc)
- return PURPLE_DISCONNECTED;
+ return PURPLE_CONNECTION_DISCONNECTED;
return purple_connection_get_state(gc);
}
@@ -2192,66 +1341,127 @@ purple_account_get_state(const PurpleAccount *account)
gboolean
purple_account_is_connected(const PurpleAccount *account)
{
- return (purple_account_get_state(account) == PURPLE_CONNECTED);
+ return (purple_account_get_state(account) == PURPLE_CONNECTION_CONNECTED);
}
gboolean
purple_account_is_connecting(const PurpleAccount *account)
{
- return (purple_account_get_state(account) == PURPLE_CONNECTING);
+ return (purple_account_get_state(account) == PURPLE_CONNECTION_CONNECTING);
}
gboolean
purple_account_is_disconnected(const PurpleAccount *account)
{
- return (purple_account_get_state(account) == PURPLE_DISCONNECTED);
+ return (purple_account_get_state(account) == PURPLE_CONNECTION_DISCONNECTED);
}
const char *
purple_account_get_username(const PurpleAccount *account)
{
- g_return_val_if_fail(account != NULL, NULL);
+ PurpleAccountPrivate *priv;
- return account->username;
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ return priv->username;
}
-const char *
-purple_account_get_password(const PurpleAccount *account)
+static void
+purple_account_get_password_got(PurpleAccount *account,
+ const gchar *password, GError *error, gpointer data)
+{
+ PurpleCallbackBundle *cbb = data;
+ PurpleKeyringReadCallback cb;
+ PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ purple_debug_info("account",
+ "Read password for account %s from async keyring.\n",
+ purple_account_get_username(account));
+
+ purple_str_wipe(priv->password);
+ priv->password = g_strdup(password);
+
+ cb = (PurpleKeyringReadCallback)cbb->cb;
+ if (cb != NULL)
+ cb(account, password, error, cbb->data);
+
+ g_free(cbb);
+}
+
+void
+purple_account_get_password(PurpleAccount *account,
+ PurpleKeyringReadCallback cb, gpointer data)
{
- g_return_val_if_fail(account != NULL, NULL);
+ PurpleAccountPrivate *priv;
+
+ if (account == NULL) {
+ cb(NULL, NULL, NULL, data);
+ return;
+ }
+
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ if (priv->password != NULL) {
+ purple_debug_info("account",
+ "Reading password for account %s from cache.\n",
+ purple_account_get_username(account));
+ cb(account, priv->password, NULL, data);
+ } else {
+ PurpleCallbackBundle *cbb = g_new0(PurpleCallbackBundle, 1);
+ cbb->cb = PURPLE_CALLBACK(cb);
+ cbb->data = data;
- return account->password;
+ purple_debug_info("account",
+ "Reading password for account %s from async keyring.\n",
+ purple_account_get_username(account));
+ purple_keyring_get_password(account,
+ purple_account_get_password_got, cbb);
+ }
}
const char *
-purple_account_get_alias(const PurpleAccount *account)
+purple_account_get_private_alias(const PurpleAccount *account)
{
- g_return_val_if_fail(account != NULL, NULL);
+ PurpleAccountPrivate *priv;
- return account->alias;
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ return priv->alias;
}
const char *
purple_account_get_user_info(const PurpleAccount *account)
{
- g_return_val_if_fail(account != NULL, NULL);
+ PurpleAccountPrivate *priv;
+
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
- return account->user_info;
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ return priv->user_info;
}
const char *
purple_account_get_buddy_icon_path(const PurpleAccount *account)
{
- g_return_val_if_fail(account != NULL, NULL);
+ PurpleAccountPrivate *priv;
+
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
- return account->buddy_icon_path;
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ return priv->buddy_icon_path;
}
const char *
purple_account_get_protocol_id(const PurpleAccount *account)
{
- g_return_val_if_fail(account != NULL, NULL);
- return account->protocol_id;
+ PurpleAccountPrivate *priv;
+
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ return priv->protocol_id;
}
const char *
@@ -2259,7 +1469,7 @@ purple_account_get_protocol_name(const PurpleAccount *account)
{
PurplePlugin *p;
- g_return_val_if_fail(account != NULL, NULL);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
p = purple_find_prpl(purple_account_get_protocol_id(account));
@@ -2269,9 +1479,12 @@ purple_account_get_protocol_name(const PurpleAccount *account)
PurpleConnection *
purple_account_get_connection(const PurpleAccount *account)
{
- g_return_val_if_fail(account != NULL, NULL);
+ PurpleAccountPrivate *priv;
- return account->gc;
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ return priv->gc;
}
const gchar *
@@ -2281,14 +1494,14 @@ purple_account_get_name_for_display(const PurpleAccount *account)
PurpleConnection *gc = NULL;
const gchar *name = NULL, *username = NULL, *displayname = NULL;
- name = purple_account_get_alias(account);
+ name = purple_account_get_private_alias(account);
if (name) {
return name;
}
username = purple_account_get_username(account);
- self = purple_find_buddy((PurpleAccount *)account, username);
+ self = purple_blist_find_buddy((PurpleAccount *)account, username);
if (self) {
const gchar *calias= purple_buddy_get_contact_alias(self);
@@ -2313,15 +1526,18 @@ purple_account_get_name_for_display(const PurpleAccount *account)
gboolean
purple_account_get_remember_password(const PurpleAccount *account)
{
- g_return_val_if_fail(account != NULL, FALSE);
+ PurpleAccountPrivate *priv;
- return account->remember_pass;
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
+
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ return priv->remember_pass;
}
gboolean
purple_account_get_check_mail(const PurpleAccount *account)
{
- g_return_val_if_fail(account != NULL, FALSE);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
return purple_account_get_bool(account, "check-mail", FALSE);
}
@@ -2329,7 +1545,7 @@ purple_account_get_check_mail(const PurpleAccount *account)
gboolean
purple_account_get_enabled(const PurpleAccount *account, const char *ui)
{
- g_return_val_if_fail(account != NULL, FALSE);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
g_return_val_if_fail(ui != NULL, FALSE);
return purple_account_get_ui_bool(account, ui, "auto-login", FALSE);
@@ -2338,34 +1554,434 @@ purple_account_get_enabled(const PurpleAccount *account, const char *ui)
PurpleProxyInfo *
purple_account_get_proxy_info(const PurpleAccount *account)
{
- g_return_val_if_fail(account != NULL, NULL);
+ PurpleAccountPrivate *priv;
+
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
- return account->proxy_info;
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ return priv->proxy_info;
}
-PurplePrivacyType
+PurpleAccountPrivacyType
purple_account_get_privacy_type(const PurpleAccount *account)
{
- g_return_val_if_fail(account != NULL, PURPLE_PRIVACY_ALLOW_ALL);
+ PurpleAccountPrivate *priv;
+
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL);
+
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ return priv->privacy_type;
+}
+
+gboolean
+purple_account_privacy_permit_add(PurpleAccount *account, const char *who,
+ gboolean local_only)
+{
+ GSList *l;
+ char *name;
+ PurpleBuddy *buddy;
+ PurpleBlistUiOps *blist_ops;
+ PurpleAccountPrivate *priv;
+ PurpleAccountUiOps *ui_ops = purple_accounts_get_ui_ops();
+
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
+ g_return_val_if_fail(who != NULL, FALSE);
+
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ name = g_strdup(purple_normalize(account, who));
+
+ for (l = priv->permit; l != NULL; l = l->next) {
+ if (g_str_equal(name, l->data))
+ /* This buddy already exists */
+ break;
+ }
+
+ if (l != NULL)
+ {
+ /* This buddy already exists, so bail out */
+ g_free(name);
+ return FALSE;
+ }
+
+ priv->permit = g_slist_append(priv->permit, name);
+
+ if (!local_only && purple_account_is_connected(account))
+ purple_serv_add_permit(purple_account_get_connection(account), who);
+
+ 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);
+
+ /* This lets the UI know a buddy has had its privacy setting changed */
+ buddy = purple_blist_find_buddy(account, name);
+ if (buddy != NULL) {
+ purple_signal_emit(purple_blist_get_handle(),
+ "buddy-privacy-changed", buddy);
+ }
+ return TRUE;
+}
+
+gboolean
+purple_account_privacy_permit_remove(PurpleAccount *account, const char *who,
+ gboolean local_only)
+{
+ GSList *l;
+ const char *name;
+ PurpleBuddy *buddy;
+ char *del;
+ PurpleBlistUiOps *blist_ops;
+ PurpleAccountPrivate *priv;
+ PurpleAccountUiOps *ui_ops = purple_accounts_get_ui_ops();
+
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
+ g_return_val_if_fail(who != NULL, FALSE);
+
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ name = purple_normalize(account, who);
+
+ for (l = priv->permit; l != NULL; l = l->next) {
+ if (g_str_equal(name, l->data))
+ /* We found the buddy we were looking for */
+ break;
+ }
+
+ if (l == NULL)
+ /* We didn't find the buddy we were looking for, so bail out */
+ return FALSE;
+
+ /* We should not free l->data just yet. There can be occasions where
+ * l->data == who. In such cases, freeing l->data here can cause crashes
+ * later when who is used. */
+ del = l->data;
+ priv->permit = g_slist_delete_link(priv->permit, l);
+
+ if (!local_only && purple_account_is_connected(account))
+ purple_serv_rem_permit(purple_account_get_connection(account), who);
+
+ 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);
+
+ buddy = purple_blist_find_buddy(account, name);
+ if (buddy != NULL) {
+ purple_signal_emit(purple_blist_get_handle(),
+ "buddy-privacy-changed", buddy);
+ }
+ g_free(del);
+ return TRUE;
+}
+
+gboolean
+purple_account_privacy_deny_add(PurpleAccount *account, const char *who,
+ gboolean local_only)
+{
+ GSList *l;
+ char *name;
+ PurpleBuddy *buddy;
+ PurpleBlistUiOps *blist_ops;
+ PurpleAccountPrivate *priv;
+ PurpleAccountUiOps *ui_ops = purple_accounts_get_ui_ops();
+
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
+ g_return_val_if_fail(who != NULL, FALSE);
+
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ name = g_strdup(purple_normalize(account, who));
+
+ for (l = priv->deny; l != NULL; l = l->next) {
+ if (g_str_equal(name, l->data))
+ /* This buddy already exists */
+ break;
+ }
+
+ if (l != NULL)
+ {
+ /* This buddy already exists, so bail out */
+ g_free(name);
+ return FALSE;
+ }
+
+ priv->deny = g_slist_append(priv->deny, name);
+
+ if (!local_only && purple_account_is_connected(account))
+ purple_serv_add_deny(purple_account_get_connection(account), who);
+
+ 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);
+
+ buddy = purple_blist_find_buddy(account, name);
+ if (buddy != NULL) {
+ purple_signal_emit(purple_blist_get_handle(),
+ "buddy-privacy-changed", buddy);
+ }
+ return TRUE;
+}
+
+gboolean
+purple_account_privacy_deny_remove(PurpleAccount *account, const char *who,
+ gboolean local_only)
+{
+ GSList *l;
+ const char *normalized;
+ char *name;
+ PurpleBuddy *buddy;
+ PurpleBlistUiOps *blist_ops;
+ PurpleAccountPrivate *priv;
+ PurpleAccountUiOps *ui_ops = purple_accounts_get_ui_ops();
+
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
+ g_return_val_if_fail(who != NULL, FALSE);
+
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ normalized = purple_normalize(account, who);
+
+ for (l = priv->deny; l != NULL; l = l->next) {
+ if (g_str_equal(normalized, l->data))
+ /* We found the buddy we were looking for */
+ break;
+ }
- return account->perm_deny;
+ if (l == NULL)
+ /* We didn't find the buddy we were looking for, so bail out */
+ return FALSE;
+
+ buddy = purple_blist_find_buddy(account, normalized);
+
+ name = l->data;
+ priv->deny = g_slist_delete_link(priv->deny, l);
+
+ if (!local_only && purple_account_is_connected(account))
+ purple_serv_rem_deny(purple_account_get_connection(account), name);
+
+ if (ui_ops != NULL && ui_ops->deny_removed != NULL)
+ ui_ops->deny_removed(account, who);
+
+ if (buddy != NULL) {
+ purple_signal_emit(purple_blist_get_handle(),
+ "buddy-privacy-changed", buddy);
+ }
+
+ g_free(name);
+
+ blist_ops = purple_blist_get_ui_ops();
+ if (blist_ops != NULL && blist_ops->save_account != NULL)
+ blist_ops->save_account(account);
+
+ return TRUE;
+}
+
+/*
+ * This makes sure your permit list contains all buddies from your
+ * buddy list and ONLY buddies from your buddy list.
+ */
+static void
+add_all_buddies_to_permit_list(PurpleAccount *account, gboolean local)
+{
+ GSList *list;
+ PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ /* Remove anyone in the permit list who is not in the buddylist */
+ for (list = priv->permit; list != NULL; ) {
+ char *person = list->data;
+ list = list->next;
+ if (!purple_blist_find_buddy(account, person))
+ purple_account_privacy_permit_remove(account, person, local);
+ }
+
+ /* Now make sure everyone in the buddylist is in the permit list */
+ list = purple_blist_find_buddies(account, NULL);
+ while (list != NULL)
+ {
+ PurpleBuddy *buddy = list->data;
+ const gchar *name = purple_buddy_get_name(buddy);
+
+ if (!g_slist_find_custom(priv->permit, name, (GCompareFunc)g_utf8_collate))
+ purple_account_privacy_permit_add(account, name, local);
+ list = g_slist_delete_link(list, list);
+ }
+}
+
+void
+purple_account_privacy_allow(PurpleAccount *account, const char *who)
+{
+ GSList *list;
+ PurpleAccountPrivacyType type = purple_account_get_privacy_type(account);
+ PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ switch (type) {
+ case PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL:
+ return;
+ case PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS:
+ purple_account_privacy_permit_add(account, who, FALSE);
+ break;
+ case PURPLE_ACCOUNT_PRIVACY_DENY_USERS:
+ purple_account_privacy_deny_remove(account, who, FALSE);
+ break;
+ case PURPLE_ACCOUNT_PRIVACY_DENY_ALL:
+ {
+ /* Empty the allow-list. */
+ const char *norm = purple_normalize(account, who);
+ for (list = priv->permit; list != NULL;) {
+ char *person = list->data;
+ list = list->next;
+ if (!purple_strequal(norm, person))
+ purple_account_privacy_permit_remove(account, person, FALSE);
+ }
+ purple_account_privacy_permit_add(account, who, FALSE);
+ purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS);
+ }
+ break;
+ case PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST:
+ if (!purple_blist_find_buddy(account, who)) {
+ add_all_buddies_to_permit_list(account, FALSE);
+ purple_account_privacy_permit_add(account, who, FALSE);
+ purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS);
+ }
+ break;
+ default:
+ g_return_if_reached();
+ }
+
+ /* Notify the server if the privacy setting was changed */
+ if (type != purple_account_get_privacy_type(account) && purple_account_is_connected(account))
+ purple_serv_set_permit_deny(purple_account_get_connection(account));
+}
+
+void
+purple_account_privacy_deny(PurpleAccount *account, const char *who)
+{
+ GSList *list;
+ PurpleAccountPrivacyType type = purple_account_get_privacy_type(account);
+ PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ switch (type) {
+ case PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL:
+ {
+ /* Empty the deny-list. */
+ const char *norm = purple_normalize(account, who);
+ for (list = priv->deny; list != NULL; ) {
+ char *person = list->data;
+ list = list->next;
+ if (!purple_strequal(norm, person))
+ purple_account_privacy_deny_remove(account, person, FALSE);
+ }
+ purple_account_privacy_deny_add(account, who, FALSE);
+ purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_DENY_USERS);
+ }
+ break;
+ case PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS:
+ purple_account_privacy_permit_remove(account, who, FALSE);
+ break;
+ case PURPLE_ACCOUNT_PRIVACY_DENY_USERS:
+ purple_account_privacy_deny_add(account, who, FALSE);
+ break;
+ case PURPLE_ACCOUNT_PRIVACY_DENY_ALL:
+ break;
+ case PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST:
+ if (purple_blist_find_buddy(account, who)) {
+ add_all_buddies_to_permit_list(account, FALSE);
+ purple_account_privacy_permit_remove(account, who, FALSE);
+ purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS);
+ }
+ break;
+ default:
+ g_return_if_reached();
+ }
+
+ /* Notify the server if the privacy setting was changed */
+ if (type != purple_account_get_privacy_type(account) && purple_account_is_connected(account))
+ purple_serv_set_permit_deny(purple_account_get_connection(account));
+}
+
+GSList *
+purple_account_privacy_get_permitted(PurpleAccount *account)
+{
+ PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->permit;
+}
+
+GSList *
+purple_account_privacy_get_denied(PurpleAccount *account)
+{
+ PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->deny;
+}
+
+gboolean
+purple_account_privacy_check(PurpleAccount *account, const char *who)
+{
+ GSList *list;
+ PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ switch (purple_account_get_privacy_type(account)) {
+ case PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL:
+ return TRUE;
+
+ case PURPLE_ACCOUNT_PRIVACY_DENY_ALL:
+ return FALSE;
+
+ case PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS:
+ who = purple_normalize(account, who);
+ for (list=priv->permit; list!=NULL; list=list->next) {
+ if (g_str_equal(who, list->data))
+ return TRUE;
+ }
+ return FALSE;
+
+ case PURPLE_ACCOUNT_PRIVACY_DENY_USERS:
+ who = purple_normalize(account, who);
+ for (list=priv->deny; list!=NULL; list=list->next) {
+ if (g_str_equal(who, list->data))
+ return FALSE;
+ }
+ return TRUE;
+
+ case PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST:
+ return (purple_blist_find_buddy(account, who) != NULL);
+
+ default:
+ g_return_val_if_reached(TRUE);
+ }
}
PurpleStatus *
purple_account_get_active_status(const PurpleAccount *account)
{
- g_return_val_if_fail(account != NULL, NULL);
+ PurpleAccountPrivate *priv;
+
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
- return purple_presence_get_active_status(account->presence);
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ return purple_presence_get_active_status(priv->presence);
}
PurpleStatus *
purple_account_get_status(const PurpleAccount *account, const char *status_id)
{
- g_return_val_if_fail(account != NULL, NULL);
+ PurpleAccountPrivate *priv;
+
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
g_return_val_if_fail(status_id != NULL, NULL);
- return purple_presence_get_status(account->presence, status_id);
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ return purple_presence_get_status(priv->presence, status_id);
}
PurpleStatusType *
@@ -2373,8 +1989,8 @@ purple_account_get_status_type(const PurpleAccount *account, const char *id)
{
GList *l;
- g_return_val_if_fail(account != NULL, NULL);
- g_return_val_if_fail(id != NULL, NULL);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+ g_return_val_if_fail(id != NULL, NULL);
for (l = purple_account_get_status_types(account); l != NULL; l = l->next)
{
@@ -2392,7 +2008,7 @@ purple_account_get_status_type_with_primitive(const PurpleAccount *account, Purp
{
GList *l;
- g_return_val_if_fail(account != NULL, NULL);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
for (l = purple_account_get_status_types(account); l != NULL; l = l->next)
{
@@ -2408,27 +2024,37 @@ purple_account_get_status_type_with_primitive(const PurpleAccount *account, Purp
PurplePresence *
purple_account_get_presence(const PurpleAccount *account)
{
- g_return_val_if_fail(account != NULL, NULL);
+ PurpleAccountPrivate *priv;
+
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
- return account->presence;
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ return priv->presence;
}
gboolean
purple_account_is_status_active(const PurpleAccount *account,
const char *status_id)
{
- g_return_val_if_fail(account != NULL, FALSE);
+ PurpleAccountPrivate *priv;
+
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
g_return_val_if_fail(status_id != NULL, FALSE);
- return purple_presence_is_status_active(account->presence, status_id);
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ return purple_presence_is_status_active(priv->presence, status_id);
}
GList *
purple_account_get_status_types(const PurpleAccount *account)
{
- g_return_val_if_fail(account != NULL, NULL);
+ PurpleAccountPrivate *priv;
+
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
- return account->status_types;
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ return priv->status_types;
}
int
@@ -2436,18 +2062,21 @@ purple_account_get_int(const PurpleAccount *account, const char *name,
int default_value)
{
PurpleAccountSetting *setting;
+ PurpleAccountPrivate *priv;
- g_return_val_if_fail(account != NULL, default_value);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), default_value);
g_return_val_if_fail(name != NULL, default_value);
- setting = g_hash_table_lookup(account->settings, name);
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ setting = g_hash_table_lookup(priv->settings, name);
if (setting == NULL)
return default_value;
- g_return_val_if_fail(setting->type == PURPLE_PREF_INT, default_value);
+ g_return_val_if_fail(G_VALUE_HOLDS_INT(&setting->value), default_value);
- return setting->value.integer;
+ return g_value_get_int(&setting->value);
}
const char *
@@ -2455,18 +2084,21 @@ purple_account_get_string(const PurpleAccount *account, const char *name,
const char *default_value)
{
PurpleAccountSetting *setting;
+ PurpleAccountPrivate *priv;
- g_return_val_if_fail(account != NULL, default_value);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), default_value);
g_return_val_if_fail(name != NULL, default_value);
- setting = g_hash_table_lookup(account->settings, name);
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ setting = g_hash_table_lookup(priv->settings, name);
if (setting == NULL)
return default_value;
- g_return_val_if_fail(setting->type == PURPLE_PREF_STRING, default_value);
+ g_return_val_if_fail(G_VALUE_HOLDS_STRING(&setting->value), default_value);
- return setting->value.string;
+ return g_value_get_string(&setting->value);
}
gboolean
@@ -2474,18 +2106,21 @@ purple_account_get_bool(const PurpleAccount *account, const char *name,
gboolean default_value)
{
PurpleAccountSetting *setting;
+ PurpleAccountPrivate *priv;
- g_return_val_if_fail(account != NULL, default_value);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), default_value);
g_return_val_if_fail(name != NULL, default_value);
- setting = g_hash_table_lookup(account->settings, name);
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ setting = g_hash_table_lookup(priv->settings, name);
if (setting == NULL)
return default_value;
- g_return_val_if_fail(setting->type == PURPLE_PREF_BOOLEAN, default_value);
+ g_return_val_if_fail(G_VALUE_HOLDS_BOOLEAN(&setting->value), default_value);
- return setting->value.boolean;
+ return g_value_get_boolean(&setting->value);
}
int
@@ -2493,21 +2128,24 @@ purple_account_get_ui_int(const PurpleAccount *account, const char *ui,
const char *name, int default_value)
{
PurpleAccountSetting *setting;
+ PurpleAccountPrivate *priv;
GHashTable *table;
- g_return_val_if_fail(account != NULL, default_value);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), default_value);
g_return_val_if_fail(ui != NULL, default_value);
g_return_val_if_fail(name != NULL, default_value);
- if ((table = g_hash_table_lookup(account->ui_settings, ui)) == NULL)
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ if ((table = g_hash_table_lookup(priv->ui_settings, ui)) == NULL)
return default_value;
if ((setting = g_hash_table_lookup(table, name)) == NULL)
return default_value;
- g_return_val_if_fail(setting->type == PURPLE_PREF_INT, default_value);
+ g_return_val_if_fail(G_VALUE_HOLDS_INT(&setting->value), default_value);
- return setting->value.integer;
+ return g_value_get_int(&setting->value);
}
const char *
@@ -2515,21 +2153,24 @@ purple_account_get_ui_string(const PurpleAccount *account, const char *ui,
const char *name, const char *default_value)
{
PurpleAccountSetting *setting;
+ PurpleAccountPrivate *priv;
GHashTable *table;
- g_return_val_if_fail(account != NULL, default_value);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), default_value);
g_return_val_if_fail(ui != NULL, default_value);
g_return_val_if_fail(name != NULL, default_value);
- if ((table = g_hash_table_lookup(account->ui_settings, ui)) == NULL)
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ if ((table = g_hash_table_lookup(priv->ui_settings, ui)) == NULL)
return default_value;
if ((setting = g_hash_table_lookup(table, name)) == NULL)
return default_value;
- g_return_val_if_fail(setting->type == PURPLE_PREF_STRING, default_value);
+ g_return_val_if_fail(G_VALUE_HOLDS_STRING(&setting->value), default_value);
- return setting->value.string;
+ return g_value_get_string(&setting->value);
}
gboolean
@@ -2537,88 +2178,90 @@ purple_account_get_ui_bool(const PurpleAccount *account, const char *ui,
const char *name, gboolean default_value)
{
PurpleAccountSetting *setting;
+ PurpleAccountPrivate *priv;
GHashTable *table;
- g_return_val_if_fail(account != NULL, default_value);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), default_value);
g_return_val_if_fail(ui != NULL, default_value);
g_return_val_if_fail(name != NULL, default_value);
- if ((table = g_hash_table_lookup(account->ui_settings, ui)) == NULL)
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ if ((table = g_hash_table_lookup(priv->ui_settings, ui)) == NULL)
return default_value;
if ((setting = g_hash_table_lookup(table, name)) == NULL)
return default_value;
- g_return_val_if_fail(setting->type == PURPLE_PREF_BOOLEAN, default_value);
+ g_return_val_if_fail(G_VALUE_HOLDS_BOOLEAN(&setting->value), default_value);
+
+ return g_value_get_boolean(&setting->value);
+}
+
+gpointer
+purple_account_get_ui_data(const PurpleAccount *account)
+{
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
- return setting->value.boolean;
+ return account->ui_data;
+}
+
+void
+purple_account_set_ui_data(PurpleAccount *account, gpointer ui_data)
+{
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+
+ account->ui_data = ui_data;
}
PurpleLog *
purple_account_get_log(PurpleAccount *account, gboolean create)
{
- g_return_val_if_fail(account != NULL, NULL);
+ PurpleAccountPrivate *priv;
+
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
- if(!account->system_log && create){
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ if(!priv->system_log && create){
PurplePresence *presence;
int login_time;
presence = purple_account_get_presence(account);
login_time = purple_presence_get_login_time(presence);
- account->system_log = purple_log_new(PURPLE_LOG_SYSTEM,
+ priv->system_log = purple_log_new(PURPLE_LOG_SYSTEM,
purple_account_get_username(account), account, NULL,
(login_time != 0) ? login_time : time(NULL), NULL);
}
- return account->system_log;
+ return priv->system_log;
}
void
purple_account_destroy_log(PurpleAccount *account)
{
- g_return_if_fail(account != NULL);
-
- if(account->system_log){
- purple_log_free(account->system_log);
- account->system_log = NULL;
- }
-}
-
-void
-purple_account_add_buddy(PurpleAccount *account, PurpleBuddy *buddy)
-{
- PurplePluginProtocolInfo *prpl_info = NULL;
- PurpleConnection *gc;
- PurplePlugin *prpl = NULL;
+ PurpleAccountPrivate *priv;
- g_return_if_fail(account != NULL);
- g_return_if_fail(buddy != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
- gc = purple_account_get_connection(account);
- if (gc != NULL)
- prpl = purple_connection_get_prpl(gc);
-
- if (prpl != NULL)
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+ priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
- if (prpl_info != NULL) {
- if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy_with_invite))
- prpl_info->add_buddy_with_invite(gc, buddy, purple_buddy_get_group(buddy), NULL);
- else if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy))
- prpl_info->add_buddy(gc, buddy, purple_buddy_get_group(buddy));
+ if(priv->system_log){
+ purple_log_free(priv->system_log);
+ priv->system_log = NULL;
}
}
void
-purple_account_add_buddy_with_invite(PurpleAccount *account, PurpleBuddy *buddy, const char *message)
+purple_account_add_buddy(PurpleAccount *account, PurpleBuddy *buddy, const char *message)
{
PurplePluginProtocolInfo *prpl_info = NULL;
PurpleConnection *gc;
PurplePlugin *prpl = NULL;
- g_return_if_fail(account != NULL);
- g_return_if_fail(buddy != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+ g_return_if_fail(PURPLE_IS_BUDDY(buddy));
gc = purple_account_get_connection(account);
if (gc != NULL)
@@ -2628,15 +2271,13 @@ purple_account_add_buddy_with_invite(PurpleAccount *account, PurpleBuddy *buddy,
prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
if (prpl_info != NULL) {
- if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy_with_invite))
- prpl_info->add_buddy_with_invite(gc, buddy, purple_buddy_get_group(buddy), message);
- else if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy))
- prpl_info->add_buddy(gc, buddy, purple_buddy_get_group(buddy));
+ if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy))
+ prpl_info->add_buddy(gc, buddy, purple_buddy_get_group(buddy), message);
}
}
void
-purple_account_add_buddies(PurpleAccount *account, GList *buddies)
+purple_account_add_buddies(PurpleAccount *account, GList *buddies, const char *message)
{
PurplePluginProtocolInfo *prpl_info = NULL;
PurpleConnection *gc = purple_account_get_connection(account);
@@ -2657,73 +2298,13 @@ purple_account_add_buddies(PurpleAccount *account, GList *buddies)
groups = g_list_append(groups, purple_buddy_get_group(buddy));
}
- if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddies_with_invite))
- prpl_info->add_buddies_with_invite(gc, buddies, groups, NULL);
- else if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddies))
- prpl_info->add_buddies(gc, buddies, groups);
- else if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy_with_invite)) {
- GList *curb = buddies, *curg = groups;
-
- while ((curb != NULL) && (curg != NULL)) {
- prpl_info->add_buddy_with_invite(gc, curb->data, curg->data, NULL);
- curb = curb->next;
- curg = curg->next;
- }
- }
+ if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddies))
+ prpl_info->add_buddies(gc, buddies, groups, message);
else if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy)) {
GList *curb = buddies, *curg = groups;
while ((curb != NULL) && (curg != NULL)) {
- prpl_info->add_buddy(gc, curb->data, curg->data);
- curb = curb->next;
- curg = curg->next;
- }
- }
-
- g_list_free(groups);
- }
-}
-
-void
-purple_account_add_buddies_with_invite(PurpleAccount *account, GList *buddies, const char *message)
-{
- PurplePluginProtocolInfo *prpl_info = NULL;
- PurpleConnection *gc = purple_account_get_connection(account);
- PurplePlugin *prpl = NULL;
-
- if (gc != NULL)
- prpl = purple_connection_get_prpl(gc);
-
- if (prpl != NULL)
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-
- if (prpl_info) {
- GList *cur, *groups = NULL;
-
- /* Make a list of what group each buddy is in */
- for (cur = buddies; cur != NULL; cur = cur->next) {
- PurpleBuddy *buddy = cur->data;
- groups = g_list_append(groups, purple_buddy_get_group(buddy));
- }
-
- if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddies_with_invite))
- prpl_info->add_buddies_with_invite(gc, buddies, groups, message);
- else if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy_with_invite)) {
- GList *curb = buddies, *curg = groups;
-
- while ((curb != NULL) && (curg != NULL)) {
- prpl_info->add_buddy_with_invite(gc, curb->data, curg->data, message);
- curb = curb->next;
- curg = curg->next;
- }
- }
- else if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddies))
- prpl_info->add_buddies(gc, buddies, groups);
- else if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy)) {
- GList *curb = buddies, *curg = groups;
-
- while ((curb != NULL) && (curg != NULL)) {
- prpl_info->add_buddy(gc, curb->data, curg->data);
+ prpl_info->add_buddy(gc, curb->data, curg->data, message);
curb = curb->next;
curg = curg->next;
}
@@ -2804,7 +2385,7 @@ purple_account_change_password(PurpleAccount *account, const char *orig_pw,
PurpleConnection *gc = purple_account_get_connection(account);
PurplePlugin *prpl = NULL;
- purple_account_set_password(account, new_pw);
+ purple_account_set_password(account, new_pw, NULL, NULL);
if (gc != NULL)
prpl = purple_connection_get_prpl(gc);
@@ -2822,8 +2403,8 @@ gboolean purple_account_supports_offline_message(PurpleAccount *account, PurpleB
PurplePluginProtocolInfo *prpl_info = NULL;
PurplePlugin *prpl = NULL;
- g_return_val_if_fail(account, FALSE);
- g_return_val_if_fail(buddy, FALSE);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
+ g_return_val_if_fail(PURPLE_IS_BUDDY(buddy), FALSE);
gc = purple_account_get_connection(account);
if (gc == NULL)
@@ -2839,36 +2420,16 @@ gboolean purple_account_supports_offline_message(PurpleAccount *account, PurpleB
return prpl_info->offline_message(buddy);
}
-static void
-signed_on_cb(PurpleConnection *gc,
- gpointer unused)
-{
- PurpleAccount *account = purple_connection_get_account(gc);
- purple_account_clear_current_error(account);
-
- purple_signal_emit(purple_accounts_get_handle(), "account-signed-on",
- account);
-}
-
-static void
-signed_off_cb(PurpleConnection *gc,
- gpointer unused)
-{
- PurpleAccount *account = purple_connection_get_account(gc);
-
- purple_signal_emit(purple_accounts_get_handle(), "account-signed-off",
- account);
-}
-
-static void
-set_current_error(PurpleAccount *account, PurpleConnectionErrorInfo *new_err)
+void
+_purple_account_set_current_error(PurpleAccount *account,
+ PurpleConnectionErrorInfo *new_err)
{
- PurpleAccountPrivate *priv;
PurpleConnectionErrorInfo *old_err;
+ PurpleAccountPrivate *priv;
- g_return_if_fail(account != NULL);
-
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
old_err = priv->current_error;
if(new_err == old_err)
@@ -2879,7 +2440,7 @@ set_current_error(PurpleAccount *account, PurpleConnectionErrorInfo *new_err)
purple_signal_emit(purple_accounts_get_handle(),
"account-error-changed",
account, old_err, new_err);
- schedule_accounts_save();
+ purple_accounts_schedule_save();
if(old_err)
g_free(old_err->description);
@@ -2888,464 +2449,719 @@ set_current_error(PurpleAccount *account, PurpleConnectionErrorInfo *new_err)
g_free(old_err);
}
-static void
-connection_error_cb(PurpleConnection *gc,
- PurpleConnectionError type,
- const gchar *description,
- gpointer unused)
-{
- PurpleAccount *account;
- PurpleConnectionErrorInfo *err;
-
- account = purple_connection_get_account(gc);
-
- g_return_if_fail(account != NULL);
-
- err = g_new0(PurpleConnectionErrorInfo, 1);
- PURPLE_DBUS_REGISTER_POINTER(err, PurpleConnectionErrorInfo);
-
- err->type = type;
- err->description = g_strdup(description);
-
- set_current_error(account, err);
-
- purple_signal_emit(purple_accounts_get_handle(), "account-connection-error",
- account, type, description);
-}
-
const PurpleConnectionErrorInfo *
purple_account_get_current_error(PurpleAccount *account)
{
PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
return priv->current_error;
}
void
purple_account_clear_current_error(PurpleAccount *account)
{
- set_current_error(account, NULL);
+ _purple_account_set_current_error(account, NULL);
}
-void
-purple_accounts_add(PurpleAccount *account)
+static PurpleXmlNode *
+status_attribute_to_xmlnode(const PurpleStatus *status, const PurpleStatusType *type,
+ const PurpleStatusAttribute *attr)
{
- g_return_if_fail(account != NULL);
+ PurpleXmlNode *node;
+ const char *id;
+ char *value = NULL;
+ PurpleStatusAttribute *default_attr;
+ GValue *default_value;
+ GType attr_type;
+ GValue *attr_value;
- if (g_list_find(accounts, account) != NULL)
- return;
+ id = purple_status_attribute_get_id(attr);
+ g_return_val_if_fail(id, NULL);
+
+ attr_value = purple_status_get_attr_value(status, id);
+ g_return_val_if_fail(attr_value, NULL);
+ attr_type = G_VALUE_TYPE(attr_value);
+
+ /*
+ * If attr_value is a different type than it should be
+ * then don't write it to the file.
+ */
+ default_attr = purple_status_type_get_attr(type, id);
+ default_value = purple_status_attribute_get_value(default_attr);
+ if (attr_type != G_VALUE_TYPE(default_value))
+ return NULL;
+
+ /*
+ * If attr_value is the same as the default for this status
+ * then there is no need to write it to the file.
+ */
+ if (attr_type == G_TYPE_STRING)
+ {
+ const char *string_value = g_value_get_string(attr_value);
+ const char *default_string_value = g_value_get_string(default_value);
+ if (purple_strequal(string_value, default_string_value))
+ return NULL;
+ value = g_strdup(g_value_get_string(attr_value));
+ }
+ else if (attr_type == G_TYPE_INT)
+ {
+ int int_value = g_value_get_int(attr_value);
+ if (int_value == g_value_get_int(default_value))
+ return NULL;
+ value = g_strdup_printf("%d", int_value);
+ }
+ else if (attr_type == G_TYPE_BOOLEAN)
+ {
+ gboolean boolean_value = g_value_get_boolean(attr_value);
+ if (boolean_value == g_value_get_boolean(default_value))
+ return NULL;
+ value = g_strdup(boolean_value ?
+ "true" : "false");
+ }
+ else
+ {
+ return NULL;
+ }
- accounts = g_list_append(accounts, account);
+ g_return_val_if_fail(value, NULL);
- schedule_accounts_save();
+ node = purple_xmlnode_new("attribute");
- purple_signal_emit(purple_accounts_get_handle(), "account-added", account);
+ purple_xmlnode_set_attrib(node, "id", id);
+ purple_xmlnode_set_attrib(node, "value", value);
+
+ g_free(value);
+
+ return node;
}
-void
-purple_accounts_remove(PurpleAccount *account)
+static PurpleXmlNode *
+status_attrs_to_xmlnode(const PurpleStatus *status)
{
- g_return_if_fail(account != NULL);
+ PurpleStatusType *type = purple_status_get_status_type(status);
+ PurpleXmlNode *node, *child;
+ GList *attrs, *attr;
- accounts = g_list_remove(accounts, account);
+ node = purple_xmlnode_new("attributes");
- schedule_accounts_save();
+ attrs = purple_status_type_get_attrs(type);
+ for (attr = attrs; attr != NULL; attr = attr->next)
+ {
+ child = status_attribute_to_xmlnode(status, type, (const PurpleStatusAttribute *)attr->data);
+ if (child)
+ purple_xmlnode_insert_child(node, child);
+ }
- /* Clearing the error ensures that account-error-changed is emitted,
- * which is the end of the guarantee that the the error's pointer is
- * valid.
- */
- purple_account_clear_current_error(account);
- purple_signal_emit(purple_accounts_get_handle(), "account-removed", account);
+ return node;
}
-void
-purple_accounts_delete(PurpleAccount *account)
+static PurpleXmlNode *
+status_to_xmlnode(const PurpleStatus *status)
{
- PurpleBlistNode *gnode, *cnode, *bnode;
- GList *iter;
+ PurpleXmlNode *node, *child;
- g_return_if_fail(account != NULL);
+ node = purple_xmlnode_new("status");
+ purple_xmlnode_set_attrib(node, "type", purple_status_get_id(status));
+ if (purple_status_get_name(status) != NULL)
+ purple_xmlnode_set_attrib(node, "name", purple_status_get_name(status));
+ purple_xmlnode_set_attrib(node, "active", purple_status_is_active(status) ? "true" : "false");
- /*
- * Disable the account before blowing it out of the water.
- * Conceptually it probably makes more sense to disable the
- * account for all UIs rather than the just the current UI,
- * but it doesn't really matter.
- */
- purple_account_set_enabled(account, purple_core_get_ui(), FALSE);
+ child = status_attrs_to_xmlnode(status);
+ purple_xmlnode_insert_child(node, child);
- purple_notify_close_with_handle(account);
- purple_request_close_with_handle(account);
+ return node;
+}
- purple_accounts_remove(account);
+static PurpleXmlNode *
+statuses_to_xmlnode(const PurplePresence *presence)
+{
+ PurpleXmlNode *node, *child;
+ GList *statuses;
+ PurpleStatus *status;
+
+ node = purple_xmlnode_new("statuses");
- /* Remove this account's buddies */
- for (gnode = purple_blist_get_root();
- gnode != NULL;
- gnode = purple_blist_node_get_sibling_next(gnode))
+ statuses = purple_presence_get_statuses(presence);
+ for (; statuses != NULL; statuses = statuses->next)
{
- if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
- continue;
+ status = statuses->data;
+ if (purple_status_type_is_saveable(purple_status_get_status_type(status)))
+ {
+ child = status_to_xmlnode(status);
+ purple_xmlnode_insert_child(node, child);
+ }
+ }
- cnode = purple_blist_node_get_first_child(gnode);
- while (cnode) {
- PurpleBlistNode *cnode_next = purple_blist_node_get_sibling_next(cnode);
+ return node;
+}
- if(PURPLE_BLIST_NODE_IS_CONTACT(cnode)) {
- bnode = purple_blist_node_get_first_child(cnode);
- while (bnode) {
- PurpleBlistNode *bnode_next = purple_blist_node_get_sibling_next(bnode);
+static PurpleXmlNode *
+proxy_settings_to_xmlnode(PurpleProxyInfo *proxy_info)
+{
+ PurpleXmlNode *node, *child;
+ PurpleProxyType proxy_type;
+ const char *value;
+ int int_value;
+ char buf[21];
- if (PURPLE_BLIST_NODE_IS_BUDDY(bnode)) {
- PurpleBuddy *b = (PurpleBuddy *)bnode;
+ proxy_type = purple_proxy_info_get_proxy_type(proxy_info);
- if (purple_buddy_get_account(b) == account)
- purple_blist_remove_buddy(b);
- }
- bnode = bnode_next;
- }
- } else if (PURPLE_BLIST_NODE_IS_CHAT(cnode)) {
- PurpleChat *c = (PurpleChat *)cnode;
+ node = purple_xmlnode_new("proxy");
- if (purple_chat_get_account(c) == account)
- purple_blist_remove_chat(c);
- }
- cnode = cnode_next;
- }
+ child = purple_xmlnode_new_child(node, "type");
+ purple_xmlnode_insert_data(child,
+ (proxy_type == PURPLE_PROXY_USE_GLOBAL ? "global" :
+ proxy_type == PURPLE_PROXY_NONE ? "none" :
+ proxy_type == PURPLE_PROXY_HTTP ? "http" :
+ proxy_type == PURPLE_PROXY_SOCKS4 ? "socks4" :
+ proxy_type == PURPLE_PROXY_SOCKS5 ? "socks5" :
+ proxy_type == PURPLE_PROXY_TOR ? "tor" :
+ proxy_type == PURPLE_PROXY_USE_ENVVAR ? "envvar" : "unknown"), -1);
+
+ if ((value = purple_proxy_info_get_host(proxy_info)) != NULL)
+ {
+ child = purple_xmlnode_new_child(node, "host");
+ purple_xmlnode_insert_data(child, value, -1);
}
- /* Remove any open conversation for this account */
- for (iter = purple_get_conversations(); iter; ) {
- PurpleConversation *conv = iter->data;
- iter = iter->next;
- if (purple_conversation_get_account(conv) == account)
- purple_conversation_destroy(conv);
+ if ((int_value = purple_proxy_info_get_port(proxy_info)) != 0)
+ {
+ g_snprintf(buf, sizeof(buf), "%d", int_value);
+ child = purple_xmlnode_new_child(node, "port");
+ purple_xmlnode_insert_data(child, buf, -1);
}
- /* Remove this account's pounces */
- purple_pounce_destroy_all_by_account(account);
+ if ((value = purple_proxy_info_get_username(proxy_info)) != NULL)
+ {
+ child = purple_xmlnode_new_child(node, "username");
+ purple_xmlnode_insert_data(child, value, -1);
+ }
- /* This will cause the deletion of an old buddy icon. */
- purple_buddy_icons_set_account_icon(account, NULL, 0);
+ if ((value = purple_proxy_info_get_password(proxy_info)) != NULL)
+ {
+ child = purple_xmlnode_new_child(node, "password");
+ purple_xmlnode_insert_data(child, value, -1);
+ }
- purple_account_destroy(account);
+ return node;
}
-void
-purple_accounts_reorder(PurpleAccount *account, gint new_index)
+static PurpleXmlNode *
+current_error_to_xmlnode(PurpleConnectionErrorInfo *err)
{
- gint index;
- GList *l;
-
- g_return_if_fail(account != NULL);
- g_return_if_fail(new_index <= g_list_length(accounts));
-
- index = g_list_index(accounts, account);
+ PurpleXmlNode *node, *child;
+ char type_str[3];
- if (index == -1) {
- purple_debug_error("account",
- "Unregistered account (%s) discovered during reorder!\n",
- purple_account_get_username(account));
- return;
- }
+ node = purple_xmlnode_new("current_error");
- l = g_list_nth(accounts, index);
+ if(err == NULL)
+ return node;
- if (new_index > index)
- new_index--;
+ /* It doesn't make sense to have transient errors persist across a
+ * restart.
+ */
+ if(!purple_connection_error_is_fatal (err->type))
+ return node;
- /* Remove the old one. */
- accounts = g_list_delete_link(accounts, l);
+ child = purple_xmlnode_new_child(node, "type");
+ g_snprintf(type_str, sizeof(type_str), "%u", err->type);
+ purple_xmlnode_insert_data(child, type_str, -1);
- /* Insert it where it should go. */
- accounts = g_list_insert(accounts, account, new_index);
+ child = purple_xmlnode_new_child(node, "description");
+ if(err->description) {
+ char *utf8ized = purple_utf8_try_convert(err->description);
+ if(utf8ized == NULL)
+ utf8ized = purple_utf8_salvage(err->description);
+ purple_xmlnode_insert_data(child, utf8ized, -1);
+ g_free(utf8ized);
+ }
- schedule_accounts_save();
+ return node;
}
-GList *
-purple_accounts_get_all(void)
+static void
+setting_to_xmlnode(gpointer key, gpointer value, gpointer user_data)
{
- return accounts;
+ const char *name;
+ PurpleAccountSetting *setting;
+ PurpleXmlNode *node, *child;
+ char buf[21];
+
+ name = (const char *)key;
+ setting = (PurpleAccountSetting *)value;
+ node = (PurpleXmlNode *)user_data;
+
+ child = purple_xmlnode_new_child(node, "setting");
+ purple_xmlnode_set_attrib(child, "name", name);
+
+ if (G_VALUE_HOLDS_INT(&setting->value)) {
+ purple_xmlnode_set_attrib(child, "type", "int");
+ g_snprintf(buf, sizeof(buf), "%d", g_value_get_int(&setting->value));
+ purple_xmlnode_insert_data(child, buf, -1);
+ }
+ else if (G_VALUE_HOLDS_STRING(&setting->value) && g_value_get_string(&setting->value) != NULL) {
+ purple_xmlnode_set_attrib(child, "type", "string");
+ purple_xmlnode_insert_data(child, g_value_get_string(&setting->value), -1);
+ }
+ else if (G_VALUE_HOLDS_BOOLEAN(&setting->value)) {
+ purple_xmlnode_set_attrib(child, "type", "bool");
+ g_snprintf(buf, sizeof(buf), "%d", g_value_get_boolean(&setting->value));
+ purple_xmlnode_insert_data(child, buf, -1);
+ }
}
-GList *
-purple_accounts_get_all_active(void)
+static void
+ui_setting_to_xmlnode(gpointer key, gpointer value, gpointer user_data)
{
- GList *list = NULL;
- GList *all = purple_accounts_get_all();
-
- while (all != NULL) {
- PurpleAccount *account = all->data;
+ const char *ui;
+ GHashTable *table;
+ PurpleXmlNode *node, *child;
- if (purple_account_get_enabled(account, purple_core_get_ui()))
- list = g_list_append(list, account);
+ ui = (const char *)key;
+ table = (GHashTable *)value;
+ node = (PurpleXmlNode *)user_data;
- all = all->next;
+ if (g_hash_table_size(table) > 0)
+ {
+ child = purple_xmlnode_new_child(node, "settings");
+ purple_xmlnode_set_attrib(child, "ui", ui);
+ g_hash_table_foreach(table, setting_to_xmlnode, child);
}
-
- return list;
}
-PurpleAccount *
-purple_accounts_find(const char *name, const char *protocol_id)
+PurpleXmlNode *
+_purple_account_to_xmlnode(PurpleAccount *account)
{
- PurpleAccount *account = NULL;
- GList *l;
- char *who;
+ PurpleXmlNode *node, *child;
+ const char *tmp;
+ PurplePresence *presence;
+ PurpleProxyInfo *proxy_info;
+ PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
- g_return_val_if_fail(name != NULL, NULL);
- g_return_val_if_fail(protocol_id != NULL, NULL);
+ node = purple_xmlnode_new("account");
- for (l = purple_accounts_get_all(); l != NULL; l = l->next) {
- account = (PurpleAccount *)l->data;
- if (!purple_strequal(account->protocol_id, protocol_id))
- continue;
+ child = purple_xmlnode_new_child(node, "protocol");
+ purple_xmlnode_insert_data(child, purple_account_get_protocol_id(account), -1);
- who = g_strdup(purple_normalize(account, name));
- if (purple_strequal(purple_normalize(account, purple_account_get_username(account)), who)) {
- g_free(who);
- return account;
+ child = purple_xmlnode_new_child(node, "name");
+ purple_xmlnode_insert_data(child, purple_account_get_username(account), -1);
+
+ if (purple_account_get_remember_password(account))
+ {
+ const char *keyring_id = NULL;
+ const char *mode = NULL;
+ char *data = NULL;
+ GError *error = NULL;
+ GDestroyNotify destroy = NULL;
+ gboolean exported = purple_keyring_export_password(account,
+ &keyring_id, &mode, &data, &error, &destroy);
+
+ if (error != NULL) {
+ purple_debug_error("account",
+ "Failed to export password for account %s: %s.\n",
+ purple_account_get_username(account),
+ error->message);
+ } else if (exported) {
+ child = purple_xmlnode_new_child(node, "password");
+ if (keyring_id != NULL)
+ purple_xmlnode_set_attrib(child, "keyring_id", keyring_id);
+ if (mode != NULL)
+ purple_xmlnode_set_attrib(child, "mode", mode);
+ if (data != NULL)
+ purple_xmlnode_insert_data(child, data, -1);
+
+ if (destroy != NULL)
+ destroy(data);
}
- g_free(who);
}
- return NULL;
-}
+ if ((tmp = purple_account_get_private_alias(account)) != NULL)
+ {
+ child = purple_xmlnode_new_child(node, "alias");
+ purple_xmlnode_insert_data(child, tmp, -1);
+ }
-void
-purple_accounts_restore_current_statuses()
-{
- GList *l;
- PurpleAccount *account;
+ if ((presence = purple_account_get_presence(account)) != NULL)
+ {
+ child = statuses_to_xmlnode(presence);
+ purple_xmlnode_insert_child(node, child);
+ }
- /* If we're not connected to the Internet right now, we bail on this */
- if (!purple_network_is_available())
+ if ((tmp = purple_account_get_user_info(account)) != NULL)
{
- purple_debug_warning("account", "Network not connected; skipping reconnect\n");
- return;
+ /* TODO: Do we need to call purple_str_strip_char(tmp, '\r') here? */
+ child = purple_xmlnode_new_child(node, "user-info");
+ purple_xmlnode_insert_data(child, tmp, -1);
}
- for (l = purple_accounts_get_all(); l != NULL; l = l->next)
+ if (g_hash_table_size(priv->settings) > 0)
{
- account = (PurpleAccount *)l->data;
- if (purple_account_get_enabled(account, purple_core_get_ui()) &&
- (purple_presence_is_online(account->presence)))
- {
- purple_account_connect(account);
- }
+ child = purple_xmlnode_new_child(node, "settings");
+ g_hash_table_foreach(priv->settings, setting_to_xmlnode, child);
}
+
+ if (g_hash_table_size(priv->ui_settings) > 0)
+ {
+ g_hash_table_foreach(priv->ui_settings, ui_setting_to_xmlnode, node);
+ }
+
+ if ((proxy_info = purple_account_get_proxy_info(account)) != NULL)
+ {
+ child = proxy_settings_to_xmlnode(proxy_info);
+ purple_xmlnode_insert_child(node, child);
+ }
+
+ child = current_error_to_xmlnode(priv->current_error);
+ purple_xmlnode_insert_child(node, child);
+
+ return node;
}
-void
-purple_accounts_set_ui_ops(PurpleAccountUiOps *ops)
-{
- account_ui_ops = ops;
+/****************
+ * GObject Code *
+ ****************/
+
+/* Set method for GObject properties */
+static void
+purple_account_set_property(GObject *obj, guint param_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleAccount *account = PURPLE_ACCOUNT(obj);
+
+ switch (param_id) {
+ case PROP_USERNAME:
+ purple_account_set_username(account, g_value_get_string(value));
+ break;
+ case PROP_PRIVATE_ALIAS:
+ purple_account_set_private_alias(account, g_value_get_string(value));
+ break;
+ case PROP_ENABLED:
+ purple_account_set_enabled(account, purple_core_get_ui(),
+ g_value_get_boolean(value));
+ break;
+ case PROP_CONNECTION:
+ purple_account_set_connection(account, g_value_get_object(value));
+ break;
+ case PROP_PROTOCOL_ID:
+ purple_account_set_protocol_id(account, g_value_get_string(value));
+ break;
+ case PROP_USER_INFO:
+ purple_account_set_user_info(account, g_value_get_string(value));
+ break;
+ case PROP_BUDDY_ICON_PATH:
+ purple_account_set_buddy_icon_path(account,
+ g_value_get_string(value));
+ break;
+ case PROP_REMEMBER_PASSWORD:
+ purple_account_set_remember_password(account,
+ g_value_get_boolean(value));
+ break;
+ case PROP_CHECK_MAIL:
+ purple_account_set_check_mail(account, g_value_get_boolean(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* Get method for GObject properties */
+static void
+purple_account_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleAccount *account = PURPLE_ACCOUNT(obj);
+
+ switch (param_id) {
+ case PROP_USERNAME:
+ g_value_set_string(value, purple_account_get_username(account));
+ break;
+ case PROP_PRIVATE_ALIAS:
+ g_value_set_string(value, purple_account_get_private_alias(account));
+ break;
+ case PROP_ENABLED:
+ g_value_set_boolean(value, purple_account_get_enabled(account,
+ purple_core_get_ui()));
+ break;
+ case PROP_CONNECTION:
+ g_value_set_object(value, purple_account_get_connection(account));
+ break;
+ case PROP_PROTOCOL_ID:
+ g_value_set_string(value, purple_account_get_protocol_id(account));
+ break;
+ case PROP_USER_INFO:
+ g_value_set_string(value, purple_account_get_user_info(account));
+ break;
+ case PROP_BUDDY_ICON_PATH:
+ g_value_set_string(value,
+ purple_account_get_buddy_icon_path(account));
+ break;
+ case PROP_REMEMBER_PASSWORD:
+ g_value_set_boolean(value,
+ purple_account_get_remember_password(account));
+ break;
+ case PROP_CHECK_MAIL:
+ g_value_set_boolean(value, purple_account_get_check_mail(account));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* GObject initialization function */
+static void purple_account_init(GTypeInstance *instance, gpointer klass)
+{
+ PurpleAccount *account = PURPLE_ACCOUNT(instance);
+ PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ priv->settings = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, delete_setting);
+ priv->ui_settings = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, (GDestroyNotify)g_hash_table_destroy);
+ priv->system_log = NULL;
+
+ priv->privacy_type = PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL;
+
+ PURPLE_DBUS_REGISTER_POINTER(account, PurpleAccount);
}
-PurpleAccountUiOps *
-purple_accounts_get_ui_ops(void)
+/* Called when done constructing */
+static void
+purple_account_constructed(GObject *object)
{
- return account_ui_ops;
+ PurpleAccount *account = PURPLE_ACCOUNT(object);
+ PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ gchar *username, *protocol_id;
+ PurplePlugin *prpl = NULL;
+ PurplePluginProtocolInfo *prpl_info = NULL;
+ PurpleStatusType *status_type;
+
+ parent_class->constructed(object);
+
+ g_object_get(object,
+ "username", &username,
+ "protocol-id", &protocol_id,
+ NULL);
+
+ purple_signal_emit(purple_accounts_get_handle(), "account-created",
+ account);
+
+ prpl = purple_find_prpl(protocol_id);
+ if (prpl == NULL) {
+ g_free(username);
+ g_free(protocol_id);
+ return;
+ }
+
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+ if (prpl_info != NULL && prpl_info->status_types != NULL)
+ purple_account_set_status_types(account,
+ prpl_info->status_types(account));
+
+ priv->presence = PURPLE_PRESENCE(purple_account_presence_new(account));
+
+ status_type = purple_account_get_status_type_with_primitive(account,
+ PURPLE_STATUS_AVAILABLE);
+ if (status_type != NULL)
+ purple_presence_set_status_active(priv->presence,
+ purple_status_type_get_id(status_type),
+ TRUE);
+ else
+ purple_presence_set_status_active(priv->presence,
+ "offline",
+ TRUE);
+
+ g_free(username);
+ g_free(protocol_id);
}
-void *
-purple_accounts_get_handle(void)
+/* GObject dispose function */
+static void
+purple_account_dispose(GObject *object)
{
- static int handle;
+ PurpleAccount *account = PURPLE_ACCOUNT(object);
+ PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
- return &handle;
-}
+ if (!purple_account_is_disconnected(account))
+ purple_account_disconnect(account);
-void
-purple_accounts_init(void)
-{
- void *handle = purple_accounts_get_handle();
- void *conn_handle = purple_connections_get_handle();
-
- purple_signal_register(handle, "account-connecting",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT));
-
- purple_signal_register(handle, "account-disabled",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT));
-
- purple_signal_register(handle, "account-enabled",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT));
-
- purple_signal_register(handle, "account-setting-info",
- purple_marshal_VOID__POINTER_POINTER, NULL, 2,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING));
-
- purple_signal_register(handle, "account-set-info",
- purple_marshal_VOID__POINTER_POINTER, NULL, 2,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING));
-
- purple_signal_register(handle, "account-created",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT));
-
- purple_signal_register(handle, "account-destroying",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT));
-
- purple_signal_register(handle, "account-added",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT));
-
- purple_signal_register(handle, "account-removed",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT));
-
- purple_signal_register(handle, "account-status-changed",
- purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_STATUS),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_STATUS));
-
- purple_signal_register(handle, "account-actions-changed",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT));
-
- purple_signal_register(handle, "account-alias-changed",
- purple_marshal_VOID__POINTER_POINTER, NULL, 2,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING));
-
- purple_signal_register(handle, "account-authorization-requested",
- purple_marshal_INT__POINTER_POINTER,
- purple_value_new(PURPLE_TYPE_INT), 2,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING));
-
- purple_signal_register(handle, "account-authorization-requested-with-message",
- purple_marshal_INT__POINTER_POINTER_POINTER,
- purple_value_new(PURPLE_TYPE_INT), 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_STRING));
- purple_signal_register(handle, "account-authorization-denied",
- purple_marshal_VOID__POINTER_POINTER, NULL, 2,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING));
-
- purple_signal_register(handle, "account-authorization-granted",
- purple_marshal_VOID__POINTER_POINTER, NULL, 2,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING));
-
- purple_signal_register(handle, "account-error-changed",
- purple_marshal_VOID__POINTER_POINTER_POINTER,
- NULL, 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_POINTER),
- purple_value_new(PURPLE_TYPE_POINTER));
-
- purple_signal_register(handle, "account-signed-on",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT));
-
- purple_signal_register(handle, "account-signed-off",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT));
-
- purple_signal_register(handle, "account-connection-error",
- purple_marshal_VOID__POINTER_INT_POINTER, NULL, 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_ENUM),
- purple_value_new(PURPLE_TYPE_STRING));
-
- purple_signal_connect(conn_handle, "signed-on", handle,
- PURPLE_CALLBACK(signed_on_cb), NULL);
- purple_signal_connect(conn_handle, "signed-off", handle,
- PURPLE_CALLBACK(signed_off_cb), NULL);
- purple_signal_connect(conn_handle, "connection-error", handle,
- PURPLE_CALLBACK(connection_error_cb), NULL);
-
- load_accounts();
+ if (priv->presence) {
+ g_object_unref(priv->presence);
+ priv->presence = NULL;
+ }
+ parent_class->dispose(object);
}
-void
-purple_accounts_uninit(void)
+/* GObject finalize function */
+static void
+purple_account_finalize(GObject *object)
{
- gpointer handle = purple_accounts_get_handle();
- if (save_timer != 0)
+ GList *l;
+ PurpleAccount *account = PURPLE_ACCOUNT(object);
+ PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+ purple_debug_info("account", "Destroying account %p\n", account);
+ purple_signal_emit(purple_accounts_get_handle(), "account-destroying",
+ account);
+
+ for (l = purple_conversations_get_all(); l != NULL; l = l->next)
{
- purple_timeout_remove(save_timer);
- save_timer = 0;
- sync_accounts();
+ PurpleConversation *conv = (PurpleConversation *)l->data;
+
+ if (purple_conversation_get_account(conv) == account)
+ purple_conversation_set_account(conv, NULL);
}
- for (; accounts; accounts = g_list_delete_link(accounts, accounts))
- purple_account_destroy(accounts->data);
+ purple_account_set_status_types(account, NULL);
- purple_signals_disconnect_by_handle(handle);
- purple_signals_unregister_by_instance(handle);
-}
+ if (priv->proxy_info)
+ purple_proxy_info_destroy(priv->proxy_info);
+
+ if(priv->system_log)
+ purple_log_free(priv->system_log);
+
+ PURPLE_DBUS_UNREGISTER_POINTER(priv->current_error);
+ if (priv->current_error) {
+ g_free(priv->current_error->description);
+ g_free(priv->current_error);
+ }
-/* libpurple 3.0.0 compatibility */
+ g_free(priv->username);
+ g_free(priv->alias);
+ purple_str_wipe(priv->password);
+ g_free(priv->user_info);
+ g_free(priv->buddy_icon_path);
+ g_free(priv->protocol_id);
+ g_hash_table_destroy(priv->settings);
+ g_hash_table_destroy(priv->ui_settings);
+
+ while (priv->deny) {
+ g_free(priv->deny->data);
+ priv->deny = g_slist_delete_link(priv->deny, priv->deny);
+ }
+
+ while (priv->permit) {
+ g_free(priv->permit->data);
+ priv->permit = g_slist_delete_link(priv->permit, priv->permit);
+ }
+
+ PURPLE_DBUS_UNREGISTER_POINTER(account);
+
+ parent_class->finalize(object);
+}
+
+/* Class initializer function */
static void
-_purple_account_set_encrypted_password(PurpleAccount *account, const char *keyring,
- const char *mode, const char *ciphertext)
+purple_account_class_init(PurpleAccountClass *klass)
{
- PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ obj_class->dispose = purple_account_dispose;
+ obj_class->finalize = purple_account_finalize;
+ obj_class->constructed = purple_account_constructed;
+
+ /* Setup properties */
+ obj_class->get_property = purple_account_get_property;
+ obj_class->set_property = purple_account_set_property;
- g_free(priv->password_keyring);
- g_free(priv->password_mode);
- g_free(priv->password_ciphertext);
+ g_type_class_add_private(klass, sizeof(PurpleAccountPrivate));
- priv->password_keyring = g_strdup(keyring);
- priv->password_mode = g_strdup(mode);
- priv->password_ciphertext = g_strdup(ciphertext);
+ properties[PROP_USERNAME] = g_param_spec_string("username", "Username",
+ "The username for the account.", NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_PRIVATE_ALIAS] = g_param_spec_string("private-alias",
+ "Private Alias",
+ "The private alias for the account.", NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_USER_INFO] = g_param_spec_string("user-info",
+ "User information",
+ "Detailed user information for the account.", NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_BUDDY_ICON_PATH] = g_param_spec_string("buddy-icon-path",
+ "Buddy icon path",
+ "Path to the buddyicon for the account.", NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_ENABLED] = g_param_spec_boolean("enabled", "Enabled",
+ "Whether the account is enabled or not.", FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_REMEMBER_PASSWORD] = g_param_spec_boolean(
+ "remember-password", "Remember password",
+ "Whether to remember and store the password for this account.",
+ FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_CHECK_MAIL] = g_param_spec_boolean("check-mail",
+ "Check mail",
+ "Whether to check mails for this account.", FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_CONNECTION] = g_param_spec_object("connection",
+ "Connection",
+ "The connection for the account.", PURPLE_TYPE_CONNECTION,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_PROTOCOL_ID] = g_param_spec_string("protocol-id",
+ "Protocol ID",
+ "ID of the protocol that is responsible for the account.", NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, PROP_LAST, properties);
}
-static gboolean
-_purple_account_get_encrypted_password(PurpleAccount *account, const char **keyring,
- const char **mode, const char **ciphertext)
+GType
+purple_account_get_type(void)
{
- PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
-
- g_return_val_if_fail(keyring != NULL, FALSE);
- g_return_val_if_fail(mode != NULL, FALSE);
- g_return_val_if_fail(ciphertext != NULL, FALSE);
+ static GType type = 0;
- if (!_purple_account_is_password_encrypted(account))
- return FALSE;
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleAccountClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_account_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleAccount),
+ 0,
+ (GInstanceInitFunc)purple_account_init,
+ NULL,
+ };
- *keyring = priv->password_keyring;
- *mode = priv->password_mode;
- *ciphertext = priv->password_ciphertext;
+ type = g_type_register_static(G_TYPE_OBJECT,
+ "PurpleAccount",
+ &info, 0);
+ }
- return TRUE;
+ return type;
}
-static gboolean
-_purple_account_is_password_encrypted(PurpleAccount *account)
+PurpleAccount *
+purple_account_new(const char *username, const char *protocol_id)
{
- PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+ PurpleAccount *account;
- return (priv->password_keyring != NULL);
+ g_return_val_if_fail(username != NULL, NULL);
+ g_return_val_if_fail(protocol_id != NULL, NULL);
+
+ account = purple_accounts_find(username, protocol_id);
+
+ if (account != NULL)
+ return account;
+
+ account = g_object_new(PURPLE_TYPE_ACCOUNT,
+ "username", username,
+ "protocol-id", protocol_id,
+ NULL);
+
+ return account;
}
diff --git a/libpurple/account.h b/libpurple/account.h
index 80a153b13f..6827891eed 100644
--- a/libpurple/account.h
+++ b/libpurple/account.h
@@ -1,9 +1,3 @@
-/**
- * @file account.h Account API
- * @ingroup core
- * @see @ref account-signals
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -24,19 +18,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_ACCOUNT_H_
#define _PURPLE_ACCOUNT_H_
+/**
+ * SECTION:account
+ * @section_id: libpurple-account
+ * @short_description: <filename>account.h</filename>
+ * @title: Account Object
+ */
#include <glib.h>
#include <glib-object.h>
-/** @copydoc _PurpleAccountUiOps */
-typedef struct _PurpleAccountUiOps PurpleAccountUiOps;
-/** @copydoc _PurpleAccount */
-typedef struct _PurpleAccount PurpleAccount;
+#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)(void *);
+typedef void (*PurpleAccountRequestAuthorizationCb)(const char *, void *);
typedef void (*PurpleAccountRegistrationCb)(PurpleAccount *account, gboolean succeeded, void *user_data);
typedef void (*PurpleAccountUnregistrationCb)(PurpleAccount *account, gboolean succeeded, void *user_data);
typedef void (*PurpleSetPublicAliasSuccessCallback)(PurpleAccount *account, const char *new_alias);
@@ -46,20 +52,26 @@ typedef void (*PurpleGetPublicAliasFailureCallback)(PurpleAccount *account, cons
#include "connection.h"
#include "log.h"
-#include "privacy.h"
#include "proxy.h"
#include "prpl.h"
#include "status.h"
+#include "keyring.h"
+#include "xmlnode.h"
/**
+ * PurpleAccountRequestType:
+ * @PURPLE_ACCOUNT_REQUEST_AUTHORIZATION: Account authorization request
+ *
* Account request types.
*/
typedef enum
{
- PURPLE_ACCOUNT_REQUEST_AUTHORIZATION = 0 /* Account authorization request */
+ PURPLE_ACCOUNT_REQUEST_AUTHORIZATION = 0
} PurpleAccountRequestType;
/**
+ * PurpleAccountRequestResponse:
+ *
* Account request response types
*/
typedef enum
@@ -70,190 +82,165 @@ typedef enum
PURPLE_ACCOUNT_RESPONSE_ACCEPT = 1
} PurpleAccountRequestResponse;
-/** Account UI operations, used to notify the user of status changes and when
- * buddies add this account to their buddy lists.
+/**
+ * PurpleAccountPrivacyType:
+ *
+ * Privacy data types.
*/
-struct _PurpleAccountUiOps
+typedef enum
{
- /** A buddy who is already on this account's buddy list added this account
- * to their buddy list.
- */
- void (*notify_added)(PurpleAccount *account,
- const char *remote_user,
- const char *id,
- const char *alias,
- const char *message);
-
- /** This account's status changed. */
- void (*status_changed)(PurpleAccount *account,
- PurpleStatus *status);
-
- /** Someone we don't have on our list added us; prompt to add them. */
- void (*request_add)(PurpleAccount *account,
- const char *remote_user,
- const char *id,
- const char *alias,
- const char *message);
-
- /** Prompt for authorization when someone adds this account to their buddy
- * list. To authorize them to see this account's presence, call \a
- * authorize_cb (\a user_data); otherwise call \a deny_cb (\a user_data);
- * @return a UI-specific handle, as passed to #close_account_request.
- */
- void *(*request_authorize)(PurpleAccount *account,
- const char *remote_user,
- const char *id,
- const char *alias,
- const char *message,
- gboolean on_list,
- PurpleAccountRequestAuthorizationCb authorize_cb,
- PurpleAccountRequestAuthorizationCb deny_cb,
- void *user_data);
-
- /** Close a pending request for authorization. \a ui_handle is a handle
- * as returned by #request_authorize.
- */
- void (*close_account_request)(void *ui_handle);
-
- void (*_purple_reserved1)(void);
- void (*_purple_reserved2)(void);
- void (*_purple_reserved3)(void);
- void (*_purple_reserved4)(void);
-};
+ PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL = 1,
+ PURPLE_ACCOUNT_PRIVACY_DENY_ALL,
+ PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS,
+ PURPLE_ACCOUNT_PRIVACY_DENY_USERS,
+ PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST
+} PurpleAccountPrivacyType;
-/** Structure representing an account.
+/**
+ * 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
{
- char *username; /**< The username. */
- char *alias; /**< How you appear to yourself. */
- char *password; /**< The account password. */
- char *user_info; /**< User information. */
-
- char *buddy_icon_path; /**< The buddy icon's non-cached path. */
+ GObject gparent;
- gboolean remember_pass; /**< Remember the password. */
-
- char *protocol_id; /**< The ID of the protocol. */
-
- PurpleConnection *gc; /**< The connection handle. */
- gboolean disconnecting; /**< The account is currently disconnecting */
-
- GHashTable *settings; /**< Protocol-specific settings. */
- GHashTable *ui_settings; /**< UI-specific settings. */
-
- PurpleProxyInfo *proxy_info; /**< Proxy information. This will be set */
- /* to NULL when the account inherits */
- /* proxy settings from global prefs. */
-
- /*
- * TODO: Supplementing the next two linked lists with hash tables
- * should help performance a lot when these lists are long. This
- * matters quite a bit for protocols like MSN, where all your
- * buddies are added to your permit list. Currently we have to
- * iterate through the entire list if we want to check if someone
- * is permitted or denied. We should do this for 3.0.0.
- * Or maybe use a GTree.
- */
- GSList *permit; /**< Permit list. */
- GSList *deny; /**< Deny list. */
- PurplePrivacyType perm_deny; /**< The permit/deny setting. */
-
- GList *status_types; /**< Status types. */
-
- PurplePresence *presence; /**< Presence. */
- PurpleLog *system_log; /**< The system log */
+ /*< public >*/
+ gpointer ui_data;
+};
- void *ui_data; /**< The UI can put data here. */
- PurpleAccountRegistrationCb registration_cb;
- void *registration_cb_user_data;
+/**
+ * PurpleAccountClass:
+ *
+ * The base class for all #PurpleAccount's.
+ */
+struct _PurpleAccountClass {
+ GObjectClass parent_class;
- gpointer priv; /**< Pointer to opaque private data. */
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
};
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name Account API */
+/* Account API */
/**************************************************************************/
-/*@{*/
/**
- * Creates a new account.
+ * purple_account_get_type:
*
- * @param username The username.
- * @param protocol_id The protocol ID.
- *
- * @return The new account.
+ * Returns: The #GType for the Account object.
*/
-PurpleAccount *purple_account_new(const char *username, const char *protocol_id);
+GType purple_account_get_type(void);
/**
- * Destroys an account.
+ * purple_account_new:
+ * @username: The username.
+ * @protocol_id: The protocol ID.
*
- * @param account The account to destroy.
+ * Creates a new account.
+ *
+ * Returns: The new account.
*/
-void purple_account_destroy(PurpleAccount *account);
+PurpleAccount *purple_account_new(const char *username, const char *protocol_id);
/**
- * Connects to an account.
+ * purple_account_connect:
+ * @account: The account to connect to.
*
- * @param account The account to connect to.
+ * Connects to an account.
*/
void purple_account_connect(PurpleAccount *account);
/**
- * Sets the callback for successful registration.
+ * purple_account_set_register_callback:
+ * @account: The account for which this callback should be used
+ * @cb: (scope call): The callback
+ * @user_data: The user data passed to the callback
*
- * @param account The account for which this callback should be used
- * @param cb The callback
- * @param user_data The user data passed to the callback
+ * Sets the callback for successful registration.
*/
void purple_account_set_register_callback(PurpleAccount *account, PurpleAccountRegistrationCb cb, void *user_data);
/**
- * Registers an account.
+ * purple_account_register:
+ * @account: The account to register.
*
- * @param account The account to register.
+ * Registers an account.
*/
void purple_account_register(PurpleAccount *account);
/**
- * Unregisters an account (deleting it from the server).
+ * purple_account_register_completed:
+ * @account: The account being registered.
+ * @succeeded: Was the account registration successful?
*
- * @param account The account to unregister.
- * @param cb Optional callback to be called when unregistration is complete
- * @param user_data user data to pass to the callback
+ * Registration of the account was completed.
+ * Calls the registration call-back set with purple_account_set_register_callback().
+ */
+void purple_account_register_completed(PurpleAccount *account, gboolean succeeded);
+
+/**
+ * purple_account_unregister:
+ * @account: The account to unregister.
+ * @cb: (scope call): Optional callback to be called when unregistration is
+ * complete
+ * @user_data: user data to pass to the callback
+ *
+ * Unregisters an account (deleting it from the server).
*/
void purple_account_unregister(PurpleAccount *account, PurpleAccountUnregistrationCb cb, void *user_data);
/**
- * Disconnects from an account.
+ * purple_account_disconnect:
+ * @account: The account to disconnect from.
*
- * @param account The account to disconnect from.
+ * Disconnects from an account.
*/
void purple_account_disconnect(PurpleAccount *account);
/**
+ * purple_account_is_disconnecting:
+ * @account: The account
+ *
+ * Indicates if the account is currently being disconnected.
+ *
+ * Returns: TRUE if the account is being disconnected.
+ */
+gboolean purple_account_is_disconnecting(const PurpleAccount *account);
+
+/**
+ * purple_account_notify_added:
+ * @account: The account that was added.
+ * @remote_user: The name of the user that added this account.
+ * @id: The optional ID of the local account. Rarely used.
+ * @alias: The optional alias of the user.
+ * @message: The optional message sent from the user adding you.
+ *
* Notifies the user that the account was added to a remote user's
* buddy list.
*
* This will present a dialog informing the user that he was added to the
* remote user's buddy list.
- *
- * @param account The account that was added.
- * @param remote_user The name of the user that added this account.
- * @param id The optional ID of the local account. Rarely used.
- * @param alias The optional alias of the user.
- * @param message The optional message sent from the user adding you.
*/
void purple_account_notify_added(PurpleAccount *account, const char *remote_user,
const char *id, const char *alias,
const char *message);
/**
+ * purple_account_request_add:
+ * @account: The account that was added.
+ * @remote_user: The name of the user that added this account.
+ * @id: The optional ID of the local account. Rarely used.
+ * @alias: The optional alias of the user.
+ * @message: The optional message sent from the user adding you.
+ *
* Notifies the user that the account was addded to a remote user's buddy
* list and asks ther user if they want to add the remote user to their buddy
* list.
@@ -261,934 +248,1021 @@ void purple_account_notify_added(PurpleAccount *account, const char *remote_user
* This will present a dialog informing the local user that the remote user
* added them to the remote user's buddy list and will ask if they want to add
* the remote user to the buddy list.
- *
- * @param account The account that was added.
- * @param remote_user The name of the user that added this account.
- * @param id The optional ID of the local account. Rarely used.
- * @param alias The optional alias of the user.
- * @param message The optional message sent from the user adding you.
*/
void purple_account_request_add(PurpleAccount *account, const char *remote_user,
const char *id, const char *alias,
const char *message);
/**
+ * purple_account_request_authorization:
+ * @account: The account that was added
+ * @remote_user: The name of the user that added this account.
+ * @id: The optional ID of the local account. Rarely used.
+ * @alias: The optional alias of the remote user.
+ * @message: The optional message sent by the user wanting to add you.
+ * @on_list: Is the remote user already on the buddy list?
+ * @auth_cb: (scope call): The callback called when the local user accepts
+ * @deny_cb: (scope call): The callback called when the local user rejects
+ * @user_data: Data to be passed back to the above callbacks
+ *
* Notifies the user that a remote user has wants to add the local user
* to his or her buddy list and requires authorization to do so.
*
* This will present a dialog informing the user of this and ask if the
* user authorizes or denies the remote user from adding him.
*
- * @param account The account that was added
- * @param remote_user The name of the user that added this account.
- * @param id The optional ID of the local account. Rarely used.
- * @param alias The optional alias of the remote user.
- * @param message The optional message sent by the user wanting to add you.
- * @param on_list Is the remote user already on the buddy list?
- * @param auth_cb The callback called when the local user accepts
- * @param deny_cb The callback called when the local user rejects
- * @param user_data Data to be passed back to the above callbacks
- *
- * @return A UI-specific handle.
+ * Returns: A UI-specific handle.
*/
void *purple_account_request_authorization(PurpleAccount *account, const char *remote_user,
const char *id, const char *alias, const char *message, gboolean on_list,
PurpleAccountRequestAuthorizationCb auth_cb, PurpleAccountRequestAuthorizationCb deny_cb, void *user_data);
/**
- * Close account requests registered for the given PurpleAccount
+ * purple_account_request_close_with_account:
+ * @account: The account for which requests should be closed
*
- * @param account The account for which requests should be closed
+ * Close account requests registered for the given PurpleAccount
*/
void purple_account_request_close_with_account(PurpleAccount *account);
/**
- * Close the account request for the given ui handle
+ * purple_account_request_close:
+ * @ui_handle: The ui specific handle for which requests should be closed
*
- * @param ui_handle The ui specific handle for which requests should be closed
+ * Close the account request for the given ui handle
*/
void purple_account_request_close(void *ui_handle);
/**
+ * purple_account_request_password:
+ * @account: The account to request the password for.
+ * @ok_cb: (scope call): The callback for the OK button.
+ * @cancel_cb: (scope call): The callback for the cancel button.
+ * @user_data: User data to be passed into callbacks.
+ *
* Requests a password from the user for the account. Does not set the
* account password on success; do that in ok_cb if desired.
- *
- * @param account The account to request the password for.
- * @param ok_cb The callback for the OK button.
- * @param cancel_cb The callback for the cancel button.
- * @param user_data User data to be passed into callbacks.
*/
void purple_account_request_password(PurpleAccount *account, GCallback ok_cb,
GCallback cancel_cb, void *user_data);
/**
- * Requests information from the user to change the account's password.
+ * purple_account_request_change_password:
+ * @account: The account to change the password on.
*
- * @param account The account to change the password on.
+ * Requests information from the user to change the account's password.
*/
void purple_account_request_change_password(PurpleAccount *account);
/**
+ * purple_account_request_change_user_info:
+ * @account: The account to change the user information on.
+ *
* Requests information from the user to change the account's
* user information.
- *
- * @param account The account to change the user information on.
*/
void purple_account_request_change_user_info(PurpleAccount *account);
/**
- * Sets the account's username.
+ * purple_account_set_username:
+ * @account: The account.
+ * @username: The username.
*
- * @param account The account.
- * @param username The username.
+ * Sets the account's username.
*/
void purple_account_set_username(PurpleAccount *account, const char *username);
/**
+ * purple_account_set_password:
+ * @account: The account.
+ * @password: The password.
+ * @cb: (scope call): A callback for once the password is saved.
+ * @data: A pointer to be passed to the callback.
+ *
* Sets the account's password.
*
- * @param account The account.
- * @param password The password.
+ * The password in the keyring might not be immediately updated, but the cached
+ * version will be, and it is therefore safe to read the password back before
+ * the callback has been triggered. One can also set a %NULL callback if
+ * notification of saving to the keyring is not required.
*/
-void purple_account_set_password(PurpleAccount *account, const char *password);
+void purple_account_set_password(PurpleAccount *account, const gchar *password,
+ PurpleKeyringSaveCallback cb, gpointer data);
/**
- * Sets the account's alias.
+ * purple_account_set_private_alias:
+ * @account: The account.
+ * @alias: The alias.
*
- * @param account The account.
- * @param alias The alias.
+ * Sets the account's private alias.
*/
-void purple_account_set_alias(PurpleAccount *account, const char *alias);
+void purple_account_set_private_alias(PurpleAccount *account, const char *alias);
/**
- * Sets the account's user information
+ * purple_account_set_user_info:
+ * @account: The account.
+ * @user_info: The user information.
*
- * @param account The account.
- * @param user_info The user information.
+ * Sets the account's user information
*/
void purple_account_set_user_info(PurpleAccount *account, const char *user_info);
/**
- * Sets the account's buddy icon path.
+ * purple_account_set_buddy_icon_path:
+ * @account: The account.
+ * @path: The buddy icon non-cached path.
*
- * @param account The account.
- * @param path The buddy icon non-cached path.
+ * Sets the account's buddy icon path.
*/
void purple_account_set_buddy_icon_path(PurpleAccount *account, const char *path);
/**
- * Sets the account's protocol ID.
+ * purple_account_set_protocol_id:
+ * @account: The account.
+ * @protocol_id: The protocol ID.
*
- * @param account The account.
- * @param protocol_id The protocol ID.
+ * Sets the account's protocol ID.
*/
void purple_account_set_protocol_id(PurpleAccount *account,
const char *protocol_id);
/**
- * Sets the account's connection.
+ * purple_account_set_connection:
+ * @account: The account.
+ * @gc: The connection.
*
- * @param account The account.
- * @param gc The connection.
+ * Sets the account's connection.
*/
void purple_account_set_connection(PurpleAccount *account, PurpleConnection *gc);
/**
- * Sets whether or not this account should save its password.
+ * purple_account_set_remember_password:
+ * @account: The account.
+ * @value: %TRUE if it should remember the password.
*
- * @param account The account.
- * @param value @c TRUE if it should remember the password.
+ * Sets whether or not this account should save its password.
*/
void purple_account_set_remember_password(PurpleAccount *account, gboolean value);
/**
- * Sets whether or not this account should check for mail.
+ * purple_account_set_check_mail:
+ * @account: The account.
+ * @value: %TRUE if it should check for mail.
*
- * @param account The account.
- * @param value @c TRUE if it should check for mail.
+ * Sets whether or not this account should check for mail.
*/
void purple_account_set_check_mail(PurpleAccount *account, gboolean value);
/**
+ * purple_account_set_enabled:
+ * @account: The account.
+ * @ui: The UI.
+ * @value: %TRUE if it is enabled.
+ *
* Sets whether or not this account is enabled for the specified
* UI.
- *
- * @param account The account.
- * @param ui The UI.
- * @param value @c TRUE if it is enabled.
*/
void purple_account_set_enabled(PurpleAccount *account, const char *ui,
gboolean value);
/**
- * Sets the account's proxy information.
+ * purple_account_set_proxy_info:
+ * @account: The account.
+ * @info: The proxy information.
*
- * @param account The account.
- * @param info The proxy information.
+ * Sets the account's proxy information.
*/
void purple_account_set_proxy_info(PurpleAccount *account, PurpleProxyInfo *info);
/**
- * Sets the account's privacy type.
- *
- * @param account The account.
- * @param privacy_type The privacy type.
+ * purple_account_set_privacy_type:
+ * @account: The account.
+ * @privacy_type: The privacy type.
*
- * @since 2.7.0
+ * Sets the account's privacy type.
*/
-void purple_account_set_privacy_type(PurpleAccount *account, PurplePrivacyType privacy_type);
+void purple_account_set_privacy_type(PurpleAccount *account, PurpleAccountPrivacyType privacy_type);
/**
- * Sets the account's status types.
+ * purple_account_set_status_types:
+ * @account: The account.
+ * @status_types: The list of status types.
*
- * @param account The account.
- * @param status_types The list of status types.
+ * Sets the account's status types.
*/
void purple_account_set_status_types(PurpleAccount *account, GList *status_types);
/**
- * Variadic version of purple_account_set_status_list(); the variadic list
- * replaces @a attrs, and should be <tt>NULL</tt>-terminated.
+ * purple_account_set_status:
+ * @account: The account.
+ * @status_id: The ID of the status.
+ * @active: Whether @a status_id is to be activated (%TRUE) or
+ * deactivated (%FALSE).
+ * @...: A %NULL-terminated list of pairs of <type>const char *</type>
+ * attribute name followed by <type>const char *</type> attribute
+ * value for the status. (For example, one pair might be
+ * <literal>"message"</literal> followed by
+ * <literal>"hello, talk to me!"</literal>.)
*
- * @copydoc purple_account_set_status_list()
+ * Variadic version of purple_account_set_status_list().
*/
void purple_account_set_status(PurpleAccount *account, const char *status_id,
gboolean active, ...) G_GNUC_NULL_TERMINATED;
/**
+ * purple_account_set_status_list:
+ * @account: The account.
+ * @status_id: The ID of the status.
+ * @active: Whether @a status_id is to be activated (%TRUE) or
+ * deactivated (%FALSE).
+ * @attrs: A list of <type>const char *</type> attribute names followed by
+ * <type>const char *</type> attribute values for the status.
+ * (For example, one pair might be <literal>"message"</literal>
+ * followed by <literal>"hello, talk to me!"</literal>.)
+ *
* Activates or deactivates a status. All changes to the statuses of
* an account go through this function or purple_account_set_status().
*
* You can only deactivate an exclusive status by activating another exclusive
* status. So, if @a status_id is an exclusive status and @a active is @c
* FALSE, this function does nothing.
- *
- * @param account The account.
- * @param status_id The ID of the status.
- * @param active Whether @a status_id is to be activated (<tt>TRUE</tt>) or
- * deactivated (<tt>FALSE</tt>).
- * @param attrs A list of <tt>const char *</tt> attribute names followed by
- * <tt>const char *</tt> attribute values for the status.
- * (For example, one pair might be <tt>"message"</tt> followed
- * by <tt>"hello, talk to me!"</tt>.)
*/
void purple_account_set_status_list(PurpleAccount *account,
const char *status_id, gboolean active, GList *attrs);
/**
+ * purple_account_set_public_alias:
+ * @account: The account
+ * @alias: The new public alias for this account or %NULL
+ * to unset the alias/nickname (or return it to
+ * a protocol-specific "default", like the username)
+ * @success_cb: (scope call): A callback which will be called if the alias
+ * is successfully set on the server (or %NULL).
+ * @failure_cb: (scope call): A callback which will be called if the alias
+ * is not successfully set on the server (or %NULL).
+ *
* Set a server-side (public) alias for this account. The account
* must already be connected.
*
* Currently, the public alias is not stored locally, although this
* may change in a later version.
- *
- * @param account The account
- * @param alias The new public alias for this account or NULL
- * to unset the alias/nickname (or return it to
- * a protocol-specific "default", like the username)
- * @param success_cb A callback which will be called if the alias
- * is successfully set on the server (or NULL).
- * @param failure_cb A callback which will be called if the alias
- * is not successfully set on the server (or NULL).
- *
- * @since 2.7.0
*/
void purple_account_set_public_alias(PurpleAccount *account,
const char *alias, PurpleSetPublicAliasSuccessCallback success_cb,
PurpleSetPublicAliasFailureCallback failure_cb);
/**
+ * purple_account_get_public_alias:
+ * @account: The account
+ * @success_cb: (scope call): A callback which will be called with the alias
+ * @failure_cb: (scope call): A callback which will be called if the protocol is
+ * unable to retrieve the server-side alias.
+ *
* Fetch the server-side (public) alias for this account. The account
* must already be connected.
- *
- * @param account The account
- * @param success_cb A callback which will be called with the alias
- * @param failure_cb A callback which will be called if the prpl is
- * unable to retrieve the server-side alias.
- * @since 2.7.0
*/
void purple_account_get_public_alias(PurpleAccount *account,
PurpleGetPublicAliasSuccessCallback success_cb,
PurpleGetPublicAliasFailureCallback failure_cb);
/**
- * Return whether silence suppression is used during voice call.
+ * purple_account_get_silence_suppression:
+ * @account: The account.
*
- * @param account The account.
+ * Return whether silence suppression is used during voice call.
*
- * @return @c TRUE if suppression is used, or @c FALSE if not.
+ * Returns: %TRUE if suppression is used, or %FALSE if not.
*/
gboolean purple_account_get_silence_suppression(const PurpleAccount *account);
/**
- * Sets whether silence suppression is used during voice call.
+ * purple_account_set_silence_suppression:
+ * @account: The account.
+ * @value: %TRUE if suppression should be used.
*
- * @param account The account.
- * @param value @c TRUE if suppression should be used.
+ * Sets whether silence suppression is used during voice call.
*/
void purple_account_set_silence_suppression(PurpleAccount *account,
gboolean value);
/**
- * Clears all protocol-specific settings on an account.
+ * purple_account_clear_settings:
+ * @account: The account.
*
- * @param account The account.
+ * Clears all protocol-specific settings on an account.
*/
void purple_account_clear_settings(PurpleAccount *account);
/**
- * Removes an account-specific setting by name.
- *
- * @param account The account.
- * @param setting The setting to remove.
+ * purple_account_remove_setting:
+ * @account: The account.
+ * @setting: The setting to remove.
*
- * @since 2.6.0
+ * Removes an account-specific setting by name.
*/
void purple_account_remove_setting(PurpleAccount *account, const char *setting);
/**
- * Sets a protocol-specific integer setting for an account.
+ * purple_account_set_int:
+ * @account: The account.
+ * @name: The name of the setting.
+ * @value: The setting's value.
*
- * @param account The account.
- * @param name The name of the setting.
- * @param value The setting's value.
+ * Sets a protocol-specific integer setting for an account.
*/
void purple_account_set_int(PurpleAccount *account, const char *name, int value);
/**
- * Sets a protocol-specific string setting for an account.
+ * purple_account_set_string:
+ * @account: The account.
+ * @name: The name of the setting.
+ * @value: The setting's value.
*
- * @param account The account.
- * @param name The name of the setting.
- * @param value The setting's value.
+ * Sets a protocol-specific string setting for an account.
*/
void purple_account_set_string(PurpleAccount *account, const char *name,
const char *value);
/**
- * Sets a protocol-specific boolean setting for an account.
+ * purple_account_set_bool:
+ * @account: The account.
+ * @name: The name of the setting.
+ * @value: The setting's value.
*
- * @param account The account.
- * @param name The name of the setting.
- * @param value The setting's value.
+ * Sets a protocol-specific boolean setting for an account.
*/
void purple_account_set_bool(PurpleAccount *account, const char *name,
gboolean value);
/**
- * Sets a UI-specific integer setting for an account.
+ * purple_account_set_ui_int:
+ * @account: The account.
+ * @ui: The UI name.
+ * @name: The name of the setting.
+ * @value: The setting's value.
*
- * @param account The account.
- * @param ui The UI name.
- * @param name The name of the setting.
- * @param value The setting's value.
+ * Sets a UI-specific integer setting for an account.
*/
void purple_account_set_ui_int(PurpleAccount *account, const char *ui,
const char *name, int value);
/**
- * Sets a UI-specific string setting for an account.
+ * purple_account_set_ui_string:
+ * @account: The account.
+ * @ui: The UI name.
+ * @name: The name of the setting.
+ * @value: The setting's value.
*
- * @param account The account.
- * @param ui The UI name.
- * @param name The name of the setting.
- * @param value The setting's value.
+ * Sets a UI-specific string setting for an account.
*/
void purple_account_set_ui_string(PurpleAccount *account, const char *ui,
const char *name, const char *value);
/**
- * Sets a UI-specific boolean setting for an account.
+ * purple_account_set_ui_bool:
+ * @account: The account.
+ * @ui: The UI name.
+ * @name: The name of the setting.
+ * @value: The setting's value.
*
- * @param account The account.
- * @param ui The UI name.
- * @param name The name of the setting.
- * @param value The setting's value.
+ * Sets a UI-specific boolean setting for an account.
*/
void purple_account_set_ui_bool(PurpleAccount *account, const char *ui,
const char *name, gboolean value);
/**
- * Returns whether or not the account is connected.
+ * purple_account_set_ui_data:
+ * @account: The account.
+ * @ui_data: A pointer to associate with this object.
*
- * @param account The account.
+ * 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.
*
- * @return @c TRUE if connected, or @c FALSE otherwise.
+ * 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(const PurpleAccount *account);
+
+/**
+ * purple_account_is_connected:
+ * @account: The account.
+ *
+ * Returns whether or not the account is connected.
+ *
+ * Returns: %TRUE if connected, or %FALSE otherwise.
*/
gboolean purple_account_is_connected(const PurpleAccount *account);
/**
- * Returns whether or not the account is connecting.
+ * purple_account_is_connecting:
+ * @account: The account.
*
- * @param account The account.
+ * Returns whether or not the account is connecting.
*
- * @return @c TRUE if connecting, or @c FALSE otherwise.
+ * Returns: %TRUE if connecting, or %FALSE otherwise.
*/
gboolean purple_account_is_connecting(const PurpleAccount *account);
/**
- * Returns whether or not the account is disconnected.
+ * purple_account_is_disconnected:
+ * @account: The account.
*
- * @param account The account.
+ * Returns whether or not the account is disconnected.
*
- * @return @c TRUE if disconnected, or @c FALSE otherwise.
+ * Returns: %TRUE if disconnected, or %FALSE otherwise.
*/
gboolean purple_account_is_disconnected(const PurpleAccount *account);
/**
- * Returns the account's username.
+ * purple_account_get_username:
+ * @account: The account.
*
- * @param account The account.
+ * Returns the account's username.
*
- * @return The username.
+ * Returns: The username.
*/
const char *purple_account_get_username(const PurpleAccount *account);
/**
- * Returns the account's password.
+ * purple_account_get_password:
+ * @account: The account.
+ * @cb: (scope call): The callback to give the password.
+ * @data: A pointer passed to the callback.
*
- * @param account The account.
+ * Reads the password for the account.
*
- * @return The password.
+ * This is an asynchronous call, that will return the password in a callback
+ * once it has been read from the keyring. If the account is connected, and you
+ * require the password immediately, then consider using @ref
+ * purple_connection_get_password instead.
*/
-const char *purple_account_get_password(const PurpleAccount *account);
+void purple_account_get_password(PurpleAccount *account,
+ PurpleKeyringReadCallback cb, gpointer data);
/**
- * Returns the account's alias.
+ * purple_account_get_private_alias:
+ * @account: The account.
*
- * @param account The account.
+ * Returns the account's private alias.
*
- * @return The alias.
+ * Returns: The alias.
*/
-const char *purple_account_get_alias(const PurpleAccount *account);
+const char *purple_account_get_private_alias(const PurpleAccount *account);
/**
- * Returns the account's user information.
+ * purple_account_get_user_info:
+ * @account: The account.
*
- * @param account The account.
+ * Returns the account's user information.
*
- * @return The user information.
+ * Returns: The user information.
*/
const char *purple_account_get_user_info(const PurpleAccount *account);
/**
- * Gets the account's buddy icon path.
+ * purple_account_get_buddy_icon_path:
+ * @account: The account.
*
- * @param account The account.
+ * Gets the account's buddy icon path.
*
- * @return The buddy icon's non-cached path.
+ * Returns: The buddy icon's non-cached path.
*/
const char *purple_account_get_buddy_icon_path(const PurpleAccount *account);
/**
- * Returns the account's protocol ID.
+ * purple_account_get_protocol_id:
+ * @account: The account.
*
- * @param account The account.
+ * Returns the account's protocol ID.
*
- * @return The protocol ID.
+ * Returns: The protocol ID.
*/
const char *purple_account_get_protocol_id(const PurpleAccount *account);
/**
- * Returns the account's protocol name.
+ * purple_account_get_protocol_name:
+ * @account: The account.
*
- * @param account The account.
+ * Returns the account's protocol name.
*
- * @return The protocol name.
+ * Returns: The protocol name.
*/
const char *purple_account_get_protocol_name(const PurpleAccount *account);
/**
- * Returns the account's connection.
+ * purple_account_get_connection:
+ * @account: The account.
*
- * @param account The account.
+ * Returns the account's connection.
*
- * @return The connection.
+ * Returns: The connection.
*/
PurpleConnection *purple_account_get_connection(const PurpleAccount *account);
/**
+ * purple_account_get_name_for_display:
+ * @account: The account.
+ *
* Returns a name for this account appropriate for display to the user. In
* order of preference: the account's alias; the contact or buddy alias (if
* the account exists on its own buddy list); the connection's display name;
* the account's username.
*
- * @param account The account.
- *
- * @return The name to display.
- *
- * @since 2.7.0
+ * Returns: The name to display.
*/
const gchar *purple_account_get_name_for_display(const PurpleAccount *account);
/**
- * Returns whether or not this account should save its password.
+ * purple_account_get_remember_password:
+ * @account: The account.
*
- * @param account The account.
+ * Returns whether or not this account should save its password.
*
- * @return @c TRUE if it should remember the password.
+ * Returns: %TRUE if it should remember the password.
*/
gboolean purple_account_get_remember_password(const PurpleAccount *account);
/**
- * Returns whether or not this account should check for mail.
+ * purple_account_get_check_mail:
+ * @account: The account.
*
- * @param account The account.
+ * Returns whether or not this account should check for mail.
*
- * @return @c TRUE if it should check for mail.
+ * Returns: %TRUE if it should check for mail.
*/
gboolean purple_account_get_check_mail(const PurpleAccount *account);
/**
+ * purple_account_get_enabled:
+ * @account: The account.
+ * @ui: The UI.
+ *
* Returns whether or not this account is enabled for the
* specified UI.
*
- * @param account The account.
- * @param ui The UI.
- *
- * @return @c TRUE if it enabled on this UI.
+ * Returns: %TRUE if it enabled on this UI.
*/
gboolean purple_account_get_enabled(const PurpleAccount *account,
const char *ui);
/**
- * Returns the account's proxy information.
+ * purple_account_get_proxy_info:
+ * @account: The account.
*
- * @param account The account.
+ * Returns the account's proxy information.
*
- * @return The proxy information.
+ * Returns: The proxy information.
*/
PurpleProxyInfo *purple_account_get_proxy_info(const PurpleAccount *account);
/**
+ * purple_account_get_privacy_type:
+ * @account: The account.
+ *
* Returns the account's privacy type.
*
- * @param account The account.
+ * Returns: The privacy type.
+ */
+PurpleAccountPrivacyType purple_account_get_privacy_type(const PurpleAccount *account);
+
+/**
+ * purple_account_privacy_permit_add:
+ * @account: The account.
+ * @name: The name of the user to add to the list.
+ * @local_only: If TRUE, only the local list is updated, and not
+ * the server.
+ *
+ * Adds a user to the account's permit list.
+ *
+ * Returns: TRUE if the user was added successfully, or %FALSE otherwise.
+ */
+gboolean purple_account_privacy_permit_add(PurpleAccount *account,
+ const char *name, gboolean local_only);
+
+/**
+ * purple_account_privacy_permit_remove:
+ * @account: The account.
+ * @name: The name of the user to add to the list.
+ * @local_only: If TRUE, only the local list is updated, and not
+ * the server.
+ *
+ * Removes a user from the account's permit list.
+ *
+ * Returns: TRUE if the user was removed successfully, or %FALSE otherwise.
+ */
+gboolean purple_account_privacy_permit_remove(PurpleAccount *account,
+ const char *name, gboolean local_only);
+
+/**
+ * purple_account_privacy_deny_add:
+ * @account: The account.
+ * @name: The name of the user to add to the list.
+ * @local_only: If TRUE, only the local list is updated, and not
+ * the server.
+ *
+ * Adds a user to the account's deny list.
+ *
+ * Returns: TRUE if the user was added successfully, or %FALSE otherwise.
+ */
+gboolean purple_account_privacy_deny_add(PurpleAccount *account,
+ const char *name, gboolean local_only);
+
+/**
+ * purple_account_privacy_deny_remove:
+ * @account: The account.
+ * @name: The name of the user to add to the list.
+ * @local_only: If TRUE, only the local list is updated, and not
+ * the server.
+ *
+ * Removes a user from the account's deny list.
+ *
+ * Returns: TRUE if the user was removed successfully, or %FALSE otherwise.
+ */
+gboolean purple_account_privacy_deny_remove(PurpleAccount *account,
+ const char *name, gboolean local_only);
+
+/**
+ * purple_account_privacy_allow:
+ * @account: The account.
+ * @who: The name of the user.
+ *
+ * Allow a user to send messages. If current privacy setting for the account is:
+ * PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS: The user is added to the allow-list.
+ * PURPLE_ACCOUNT_PRIVACY_DENY_USERS : The user is removed from the
+ * deny-list.
+ * PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL : No changes made.
+ * PURPLE_ACCOUNT_PRIVACY_DENY_ALL : The privacy setting is changed to
+ * PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS and the
+ * user is added to the allow-list.
+ * PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST: No changes made if the user is
+ * already in the buddy-list. Otherwise the
+ * setting is changed to
+ * PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS, all the buddies are added to the
+ * allow-list, and the user is also added to
+ * the allow-list.
+ *
+ * The changes are reflected on the server. The previous allow/deny list is not
+ * restored if the privacy setting is changed.
+ */
+void purple_account_privacy_allow(PurpleAccount *account, const char *who);
+
+/**
+ * purple_account_privacy_deny:
+ * @account: The account.
+ * @who: The name of the user.
+ *
+ * Block messages from a user. If current privacy setting for the account is:
+ * PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS: The user is removed from the
+ * allow-list.
+ * PURPLE_ACCOUNT_PRIVACY_DENY_USERS: The user is added to the deny-list.
+ * PURPLE_ACCOUNT_PRIVACY_DENY_ALL: No changes made.
+ * PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL: The privacy setting is changed to
+ * PURPLE_ACCOUNT_PRIVACY_DENY_USERS and the
+ * user is added to the deny-list.
+ * PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST: If the user is not in the
+ * buddy-list, then no changes made. Otherwise,
+ * the setting is changed to
+ * PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS, all
+ * the buddies are added to the allow-list, and
+ * this user is removed from the list.
+ *
+ * The changes are reflected on the server. The previous allow/deny list is not
+ * restored if the privacy setting is changed.
+ */
+void purple_account_privacy_deny(PurpleAccount *account, const char *who);
+
+/**
+ * purple_account_privacy_get_permitted:
+ * @account: The account.
+ *
+ * Returns the account's permit list.
+ *
+ * Returns: (transfer none): A list of the permitted users
+ */
+GSList *purple_account_privacy_get_permitted(PurpleAccount *account);
+
+/**
+ * purple_account_privacy_get_denied:
+ * @account: The account.
+ *
+ * Returns the account's deny list.
+ *
+ * Returns: (transfer none): A list of the denied users
+ */
+GSList *purple_account_privacy_get_denied(PurpleAccount *account);
+
+/**
+ * purple_account_privacy_check:
+ * @account: The account.
+ * @who: The name of the user.
*
- * @return The privacy type.
+ * Check the privacy-setting for a user.
*
- * @since 2.7.0
+ * Returns: %FALSE if the specified account's privacy settings block the user
+ * or %TRUE otherwise. The meaning of "block" is protocol-dependent and
+ * generally relates to status and/or sending of messages.
*/
-PurplePrivacyType purple_account_get_privacy_type(const PurpleAccount *account);
+gboolean purple_account_privacy_check(PurpleAccount *account, const char *who);
/**
+ * purple_account_get_active_status:
+ * @account: The account.
+ *
* Returns the active status for this account. This looks through
* the PurplePresence associated with this account and returns the
* PurpleStatus that has its active flag set to "TRUE." There can be
* only one active PurpleStatus in a PurplePresence.
*
- * @param account The account.
- *
- * @return The active status.
+ * Returns: The active status.
*/
PurpleStatus *purple_account_get_active_status(const PurpleAccount *account);
/**
- * Returns the account status with the specified ID.
- *
- * Note that this works differently than purple_buddy_get_status() in that
- * it will only return NULL if the status was not registered.
+ * purple_account_get_status:
+ * @account: The account.
+ * @status_id: The status ID.
*
- * @param account The account.
- * @param status_id The status ID.
+ * Returns the account status with the specified ID.
*
- * @return The status, or NULL if it was never registered.
+ * Returns: The status, or %NULL if it was never registered.
*/
PurpleStatus *purple_account_get_status(const PurpleAccount *account,
const char *status_id);
/**
- * Returns the account status type with the specified ID.
+ * purple_account_get_status_type:
+ * @account: The account.
+ * @id: The ID of the status type to find.
*
- * @param account The account.
- * @param id The ID of the status type to find.
+ * Returns the account status type with the specified ID.
*
- * @return The status type if found, or NULL.
+ * Returns: The status type if found, or %NULL.
*/
PurpleStatusType *purple_account_get_status_type(const PurpleAccount *account,
const char *id);
/**
+ * purple_account_get_status_type_with_primitive:
+ * @account: The account.
+ * @primitive: The type of the status type to find.
+ *
* Returns the account status type with the specified primitive.
* Note: It is possible for an account to have more than one
* PurpleStatusType with the same primitive. In this case, the
* first PurpleStatusType is returned.
*
- * @param account The account.
- * @param primitive The type of the status type to find.
- *
- * @return The status if found, or NULL.
+ * Returns: The status if found, or %NULL.
*/
PurpleStatusType *purple_account_get_status_type_with_primitive(
const PurpleAccount *account,
PurpleStatusPrimitive primitive);
/**
- * Returns the account's presence.
+ * purple_account_get_presence:
+ * @account: The account.
*
- * @param account The account.
+ * Returns the account's presence.
*
- * @return The account's presence.
+ * Returns: The account's presence.
*/
PurplePresence *purple_account_get_presence(const PurpleAccount *account);
/**
- * Returns whether or not an account status is active.
+ * purple_account_is_status_active:
+ * @account: The account.
+ * @status_id: The status ID.
*
- * @param account The account.
- * @param status_id The status ID.
+ * Returns whether or not an account status is active.
*
- * @return TRUE if active, or FALSE if not.
+ * Returns: TRUE if active, or FALSE if not.
*/
gboolean purple_account_is_status_active(const PurpleAccount *account,
const char *status_id);
/**
- * Returns the account's status types.
+ * purple_account_get_status_types:
+ * @account: The account.
*
- * @param account The account.
+ * Returns the account's status types.
*
- * @constreturn The account's status types.
+ * Returns: (transfer none): The account's status types.
*/
GList *purple_account_get_status_types(const PurpleAccount *account);
/**
- * Returns a protocol-specific integer setting for an account.
+ * purple_account_get_int:
+ * @account: The account.
+ * @name: The name of the setting.
+ * @default_value: The default value.
*
- * @param account The account.
- * @param name The name of the setting.
- * @param default_value The default value.
+ * Returns a protocol-specific integer setting for an account.
*
- * @return The value.
+ * Returns: The value.
*/
int purple_account_get_int(const PurpleAccount *account, const char *name,
int default_value);
/**
- * Returns a protocol-specific string setting for an account.
+ * purple_account_get_string:
+ * @account: The account.
+ * @name: The name of the setting.
+ * @default_value: The default value.
*
- * @param account The account.
- * @param name The name of the setting.
- * @param default_value The default value.
+ * Returns a protocol-specific string setting for an account.
*
- * @return The value.
+ * Returns: The value.
*/
const char *purple_account_get_string(const PurpleAccount *account,
const char *name,
const char *default_value);
/**
- * Returns a protocol-specific boolean setting for an account.
+ * purple_account_get_bool:
+ * @account: The account.
+ * @name: The name of the setting.
+ * @default_value: The default value.
*
- * @param account The account.
- * @param name The name of the setting.
- * @param default_value The default value.
+ * Returns a protocol-specific boolean setting for an account.
*
- * @return The value.
+ * Returns: The value.
*/
gboolean purple_account_get_bool(const PurpleAccount *account, const char *name,
gboolean default_value);
/**
- * Returns a UI-specific integer setting for an account.
+ * purple_account_get_ui_int:
+ * @account: The account.
+ * @ui: The UI name.
+ * @name: The name of the setting.
+ * @default_value: The default value.
*
- * @param account The account.
- * @param ui The UI name.
- * @param name The name of the setting.
- * @param default_value The default value.
+ * Returns a UI-specific integer setting for an account.
*
- * @return The value.
+ * Returns: The value.
*/
int purple_account_get_ui_int(const PurpleAccount *account, const char *ui,
const char *name, int default_value);
/**
- * Returns a UI-specific string setting for an account.
+ * purple_account_get_ui_string:
+ * @account: The account.
+ * @ui: The UI name.
+ * @name: The name of the setting.
+ * @default_value: The default value.
*
- * @param account The account.
- * @param ui The UI name.
- * @param name The name of the setting.
- * @param default_value The default value.
+ * Returns a UI-specific string setting for an account.
*
- * @return The value.
+ * Returns: The value.
*/
const char *purple_account_get_ui_string(const PurpleAccount *account,
const char *ui, const char *name,
const char *default_value);
/**
- * Returns a UI-specific boolean setting for an account.
+ * purple_account_get_ui_bool:
+ * @account: The account.
+ * @ui: The UI name.
+ * @name: The name of the setting.
+ * @default_value: The default value.
*
- * @param account The account.
- * @param ui The UI name.
- * @param name The name of the setting.
- * @param default_value The default value.
+ * Returns a UI-specific boolean setting for an account.
*
- * @return The value.
+ * Returns: The value.
*/
gboolean purple_account_get_ui_bool(const PurpleAccount *account, const char *ui,
const char *name, gboolean default_value);
/**
- * Returns the system log for an account.
- *
- * @param account The account.
- * @param create Should it be created if it doesn't exist?
+ * purple_account_get_log:
+ * @account: The account.
+ * @create: Should it be created if it doesn't exist?
*
- * @return The log.
+ * Returns the system log for an account.
*
- * @note Callers should almost always pass @c FALSE for @a create.
- * Passing @c TRUE could result in an existing log being reopened,
+ * Note: Callers should almost always pass %FALSE for @a create.
+ * Passing %TRUE could result in an existing log being reopened,
* if the log has already been closed, which not all loggers deal
* with appropriately.
+ *
+ * Returns: The log.
*/
PurpleLog *purple_account_get_log(PurpleAccount *account, gboolean create);
/**
- * Frees the system log of an account
+ * purple_account_destroy_log:
+ * @account: The account.
*
- * @param account The account.
+ * Frees the system log of an account
*/
void purple_account_destroy_log(PurpleAccount *account);
/**
- * Adds a buddy to the server-side buddy list for the specified account.
+ * purple_account_add_buddy:
+ * @account: The account.
+ * @buddy: The buddy to add.
+ * @message: The invite message. This may be ignored by a protocol.
*
- * @param account The account.
- * @param buddy The buddy to add.
- *
- * @deprecated Use purple_account_add_buddy_with_invite and \c NULL message.
- */
-void purple_account_add_buddy(PurpleAccount *account, PurpleBuddy *buddy);
-/**
* Adds a buddy to the server-side buddy list for the specified account.
- *
- * @param account The account.
- * @param buddy The buddy to add.
- * @param message The invite message. This may be ignored by a prpl.
- *
- * @since 2.8.0
*/
-void purple_account_add_buddy_with_invite(PurpleAccount *account, PurpleBuddy *buddy, const char *message);
+void purple_account_add_buddy(PurpleAccount *account, PurpleBuddy *buddy, const char *message);
/**
- * Adds a list of buddies to the server-side buddy list.
- *
- * @param account The account.
- * @param buddies The list of PurpleBlistNodes representing the buddies to add.
+ * purple_account_add_buddies:
+ * @account: The account.
+ * @buddies: The list of PurpleBlistNodes representing the buddies to add.
+ * @message: The invite message. This may be ignored by a protocol.
*
- * @deprecated Use purple_account_add_buddies_with_invite and \c NULL message.
- */
-void purple_account_add_buddies(PurpleAccount *account, GList *buddies);
-/**
* Adds a list of buddies to the server-side buddy list.
- *
- * @param account The account.
- * @param buddies The list of PurpleBlistNodes representing the buddies to add.
- * @param message The invite message. This may be ignored by a prpl.
- *
- * @since 2.8.0
*/
-void purple_account_add_buddies_with_invite(PurpleAccount *account, GList *buddies, const char *message);
+void purple_account_add_buddies(PurpleAccount *account, GList *buddies, const char *message);
/**
- * Removes a buddy from the server-side buddy list.
+ * purple_account_remove_buddy:
+ * @account: The account.
+ * @buddy: The buddy to remove.
+ * @group: The group to remove the buddy from.
*
- * @param account The account.
- * @param buddy The buddy to remove.
- * @param group The group to remove the buddy from.
+ * Removes a buddy from the server-side buddy list.
*/
void purple_account_remove_buddy(PurpleAccount *account, PurpleBuddy *buddy,
PurpleGroup *group);
/**
+ * purple_account_remove_buddies:
+ * @account: The account.
+ * @buddies: The list of buddies to remove.
+ * @groups: The list of groups to remove buddies from. Each node of this
+ * list should match the corresponding node of buddies.
+ *
* Removes a list of buddies from the server-side buddy list.
*
- * @note The lists buddies and groups are parallel lists. Be sure that node n of
+ * Note: The lists buddies and groups are parallel lists. Be sure that node n of
* groups matches node n of buddies.
- *
- * @param account The account.
- * @param buddies The list of buddies to remove.
- * @param groups The list of groups to remove buddies from. Each node of this
- * list should match the corresponding node of buddies.
*/
void purple_account_remove_buddies(PurpleAccount *account, GList *buddies,
GList *groups);
/**
- * Removes a group from the server-side buddy list.
+ * purple_account_remove_group:
+ * @account: The account.
+ * @group: The group to remove.
*
- * @param account The account.
- * @param group The group to remove.
+ * Removes a group from the server-side buddy list.
*/
void purple_account_remove_group(PurpleAccount *account, PurpleGroup *group);
/**
- * Changes the password on the specified account.
+ * purple_account_change_password:
+ * @account: The account.
+ * @orig_pw: The old password.
+ * @new_pw: The new password.
*
- * @param account The account.
- * @param orig_pw The old password.
- * @param new_pw The new password.
+ * Changes the password on the specified account.
*/
void purple_account_change_password(PurpleAccount *account, const char *orig_pw,
const char *new_pw);
/**
- * Whether the account supports sending offline messages to buddy.
+ * purple_account_supports_offline_message:
+ * @account: The account
+ * @buddy: The buddy
*
- * @param account The account
- * @param buddy The buddy
+ * Whether the account supports sending offline messages to buddy.
*/
gboolean purple_account_supports_offline_message(PurpleAccount *account, PurpleBuddy *buddy);
/**
- * Get the error that caused the account to be disconnected, or @c NULL if the
+ * purple_account_get_current_error:
+ * @account: The account whose error should be retrieved.
+ *
+ * Get the error that caused the account to be disconnected, or %NULL if the
* account is happily connected or disconnected without an error.
*
- * @param account The account whose error should be retrieved.
- * @constreturn The type of error and a human-readable description of the
- * current error, or @c NULL if there is no current error. This
- * pointer is guaranteed to remain valid until the @ref
- * account-error-changed signal is emitted for @a account.
+ * Returns: (transfer none): The type of error and a human-readable description
+ * of the current error, or %NULL if there is no current error. This
+ * pointer is guaranteed to remain valid until the @ref
+ * account-error-changed signal is emitted for @a account.
*/
const PurpleConnectionErrorInfo *purple_account_get_current_error(PurpleAccount *account);
/**
- * Clear an account's current error state, resetting it to @c NULL.
+ * purple_account_clear_current_error:
+ * @account: The account whose error state should be cleared.
*
- * @param account The account whose error state should be cleared.
+ * Clear an account's current error state, resetting it to %NULL.
*/
void purple_account_clear_current_error(PurpleAccount *account);
-/*@}*/
-
-/**************************************************************************/
-/** @name Accounts API */
-/**************************************************************************/
-/*@{*/
-
-/**
- * Adds an account to the list of accounts.
- *
- * @param account The account.
- */
-void purple_accounts_add(PurpleAccount *account);
-
-/**
- * Removes an account from the list of accounts.
- *
- * @param account The account.
- */
-void purple_accounts_remove(PurpleAccount *account);
-
-/**
- * Deletes an account.
- *
- * This will remove any buddies from the buddy list that belong to this
- * account, buddy pounces that belong to this account, and will also
- * destroy @a account.
- *
- * @param account The account.
- */
-void purple_accounts_delete(PurpleAccount *account);
-
-/**
- * Reorders an account.
- *
- * @param account The account to reorder.
- * @param new_index The new index for the account.
- */
-void purple_accounts_reorder(PurpleAccount *account, gint new_index);
-
-/**
- * Returns a list of all accounts.
- *
- * @constreturn A list of all accounts.
- */
-GList *purple_accounts_get_all(void);
-
-/**
- * Returns a list of all enabled accounts
- *
- * @return A list of all enabled accounts. The list is owned
- * by the caller, and must be g_list_free()d to avoid
- * leaking the nodes.
- */
-GList *purple_accounts_get_all_active(void);
-
-/**
- * Finds an account with the specified name and protocol id.
- *
- * @param name The account username.
- * @param protocol The account protocol ID.
- *
- * @return The account, if found, or @c FALSE otherwise.
- */
-PurpleAccount *purple_accounts_find(const char *name, const char *protocol);
-
-/**
- * This is called by the core after all subsystems and what
- * not have been initialized. It sets all enabled accounts
- * to their startup status by signing them on, setting them
- * away, etc.
- *
- * You probably shouldn't call this unless you really know
- * what you're doing.
- */
-void purple_accounts_restore_current_statuses(void);
-
-/*@}*/
-
-
-/**************************************************************************/
-/** @name UI Registration Functions */
-/**************************************************************************/
-/*@{*/
-/**
- * Sets the UI operations structure to be used for accounts.
- *
- * @param ops The UI operations structure.
- */
-void purple_accounts_set_ui_ops(PurpleAccountUiOps *ops);
-
-/**
- * Returns the UI operations structure used for accounts.
- *
- * @return The UI operations structure in use.
- */
-PurpleAccountUiOps *purple_accounts_get_ui_ops(void);
-
-/*@}*/
-
-
-/**************************************************************************/
-/** @name Accounts Subsystem */
-/**************************************************************************/
-/*@{*/
-
-/**
- * Returns the accounts subsystem handle.
- *
- * @return The accounts subsystem handle.
- */
-void *purple_accounts_get_handle(void);
-
-/**
- * Initializes the accounts subsystem.
- */
-void purple_accounts_init(void);
-
-/**
- * Uninitializes the accounts subsystem.
- */
-void purple_accounts_uninit(void);
-
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_ACCOUNT_H_ */
diff --git a/libpurple/accountopt.c b/libpurple/accountopt.c
index 726f385f5e..7830cccb38 100644
--- a/libpurple/accountopt.c
+++ b/libpurple/accountopt.c
@@ -1,8 +1,3 @@
-/**
- * @file accountopt.c Account Options API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -27,6 +22,60 @@
#include "accountopt.h"
#include "util.h"
+#include "glibcompat.h"
+
+/*
+ * An option for an account.
+ *
+ * This is set by protocol plugins, and appears in the account settings
+ * dialogs.
+ */
+struct _PurpleAccountOption
+{
+ PurplePrefType type; /* The type of value. */
+
+ char *text; /* The text that will appear to the user. */
+ char *pref_name; /* The name of the associated preference. */
+
+ union
+ {
+ gboolean boolean; /* The default boolean value. */
+ int integer; /* The default integer value. */
+ char *string; /* The default string value. */
+ GList *list; /* The default list value. */
+
+ } default_value;
+
+ union
+ {
+ struct
+ {
+ gboolean masked; /* Whether the value entered should
+ * be obscured from view (for
+ * passwords and similar options)
+ */
+ GSList *hints; /* List of hinted values */
+ } string;
+ } params;
+};
+
+/*
+ * A username split.
+ *
+ * This is used by some protocols to separate the fields of the username
+ * into more human-readable components.
+ */
+struct _PurpleAccountUserSplit
+{
+ char *text; /* The text that will appear to the user. */
+ char *default_value; /* The default value. */
+ char field_sep; /* The field separator. */
+ gboolean reverse; /* TRUE if the separator should be found
+ starting a the end of the string, FALSE
+ otherwise */
+ gboolean constant;
+};
+
PurpleAccountOption *
purple_account_option_new(PurplePrefType type, const char *text,
@@ -132,6 +181,7 @@ purple_account_option_destroy(PurpleAccountOption *option)
if (option->type == PURPLE_PREF_STRING)
{
g_free(option->default_value.string);
+ g_slist_free_full(option->params.string.hints, &g_free);
}
else if (option->type == PURPLE_PREF_STRING_LIST)
{
@@ -176,14 +226,23 @@ purple_account_option_set_default_string(PurpleAccountOption *option,
}
void
-purple_account_option_set_masked(PurpleAccountOption *option, gboolean masked)
+purple_account_option_string_set_masked(PurpleAccountOption *option, gboolean masked)
{
g_return_if_fail(option != NULL);
g_return_if_fail(option->type == PURPLE_PREF_STRING);
- option->masked = masked;
+ option->params.string.masked = masked;
}
+void
+purple_account_option_string_set_hints(PurpleAccountOption *option, GSList *hints)
+{
+ g_return_if_fail(option != NULL);
+ g_return_if_fail(option->type == PURPLE_PREF_STRING);
+
+ g_slist_free_full(option->params.string.hints, &g_free);
+ option->params.string.hints = hints;
+}
void
purple_account_option_set_list(PurpleAccountOption *option, GList *values)
@@ -220,7 +279,7 @@ purple_account_option_add_list_item(PurpleAccountOption *option,
}
PurplePrefType
-purple_account_option_get_type(const PurpleAccountOption *option)
+purple_account_option_get_pref_type(const PurpleAccountOption *option)
{
g_return_val_if_fail(option != NULL, PURPLE_PREF_NONE);
@@ -287,12 +346,21 @@ purple_account_option_get_default_list_value(const PurpleAccountOption *option)
}
gboolean
-purple_account_option_get_masked(const PurpleAccountOption *option)
+purple_account_option_string_get_masked(const PurpleAccountOption *option)
+{
+ g_return_val_if_fail(option != NULL, FALSE);
+ g_return_val_if_fail(option->type == PURPLE_PREF_STRING, FALSE);
+
+ return option->params.string.masked;
+}
+
+const GSList *
+purple_account_option_string_get_hints(const PurpleAccountOption *option)
{
g_return_val_if_fail(option != NULL, FALSE);
g_return_val_if_fail(option->type == PURPLE_PREF_STRING, FALSE);
- return option->masked;
+ return option->params.string.hints;
}
GList *
@@ -375,3 +443,20 @@ purple_account_user_split_set_reverse(PurpleAccountUserSplit *split, gboolean re
split->reverse = reverse;
}
+
+gboolean
+purple_account_user_split_is_constant(const PurpleAccountUserSplit *split)
+{
+ g_return_val_if_fail(split != NULL, FALSE);
+
+ return split->constant;
+}
+
+void
+purple_account_user_split_set_constant(PurpleAccountUserSplit *split,
+ gboolean constant)
+{
+ g_return_if_fail(split != NULL);
+
+ split->constant = constant;
+}
diff --git a/libpurple/accountopt.h b/libpurple/accountopt.h
index 85aa181149..ba66119633 100644
--- a/libpurple/accountopt.h
+++ b/libpurple/accountopt.h
@@ -1,8 +1,3 @@
-/**
- * @file accountopt.h Account Options API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -23,182 +18,192 @@
* 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_ACCOUNTOPT_H_
#define _PURPLE_ACCOUNTOPT_H_
+/**
+ * SECTION:accountopt
+ * @section_id: libpurple-accountopt
+ * @short_description: <filename>accountopt.h</filename>
+ * @title: Account Options API
+ */
#include "prefs.h"
+/**************************************************************************/
+/** Data Structures */
+/**************************************************************************/
+
/**
+ * PurpleAccountOption:
+ *
* An option for an account.
*
- * This is set by protocol plugins, and appears in the account settings
+ * This is set by protocols, and appears in the account settings
* dialogs.
*/
-typedef struct
-{
- PurplePrefType type; /**< The type of value. */
-
- char *text; /**< The text that will appear to the user. */
- char *pref_name; /**< The name of the associated preference. */
-
- union
- {
- gboolean boolean; /**< The default boolean value. */
- int integer; /**< The default integer value. */
- char *string; /**< The default string value. */
- GList *list; /**< The default list value. */
-
- } default_value;
-
- gboolean masked; /**< Whether the value entered should be
- * obscured from view (for passwords and
- * similar options)
- */
-} PurpleAccountOption;
+typedef struct _PurpleAccountOption PurpleAccountOption;
/**
+ * PurpleAccountUserSplit:
+ *
* A username split.
*
* This is used by some protocols to separate the fields of the username
* into more human-readable components.
*/
-typedef struct
-{
- char *text; /**< The text that will appear to the user. */
- char *default_value; /**< The default value. */
- char field_sep; /**< The field separator. */
- gboolean reverse; /**< TRUE if the separator should be found
- starting a the end of the string, FALSE
- otherwise */
+typedef struct _PurpleAccountUserSplit PurpleAccountUserSplit;
-} PurpleAccountUserSplit;
-
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name Account Option API */
+/* Account Option API */
/**************************************************************************/
-/*@{*/
/**
+ * purple_account_option_new:
+ * @type: The type of option.
+ * @text: The text of the option.
+ * @pref_name: The account preference name for the option.
+ *
* Creates a new account option. If you know what @a type will be in advance,
* consider using purple_account_option_bool_new(),
* purple_account_option_int_new(), purple_account_option_string_new() or
* purple_account_option_list_new() (as appropriate) instead.
*
- * @param type The type of option.
- * @param text The text of the option.
- * @param pref_name The account preference name for the option.
- *
- * @return The account option.
+ * Returns: The account option.
*/
PurpleAccountOption *purple_account_option_new(PurplePrefType type,
const char *text, const char *pref_name);
/**
- * Creates a new boolean account option.
+ * purple_account_option_bool_new:
+ * @text: The text of the option.
+ * @pref_name: The account preference name for the option.
+ * @default_value: The default value.
*
- * @param text The text of the option.
- * @param pref_name The account preference name for the option.
- * @param default_value The default value.
+ * Creates a new boolean account option.
*
- * @return The account option.
+ * Returns: The account option.
*/
PurpleAccountOption *purple_account_option_bool_new(const char *text,
const char *pref_name, gboolean default_value);
/**
- * Creates a new integer account option.
+ * purple_account_option_int_new:
+ * @text: The text of the option.
+ * @pref_name: The account preference name for the option.
+ * @default_value: The default value.
*
- * @param text The text of the option.
- * @param pref_name The account preference name for the option.
- * @param default_value The default value.
+ * Creates a new integer account option.
*
- * @return The account option.
+ * Returns: The account option.
*/
PurpleAccountOption *purple_account_option_int_new(const char *text,
const char *pref_name, int default_value);
/**
- * Creates a new string account option.
+ * purple_account_option_string_new:
+ * @text: The text of the option.
+ * @pref_name: The account preference name for the option.
+ * @default_value: The default value.
*
- * @param text The text of the option.
- * @param pref_name The account preference name for the option.
- * @param default_value The default value.
+ * Creates a new string account option.
*
- * @return The account option.
+ * Returns: The account option.
*/
PurpleAccountOption *purple_account_option_string_new(const char *text,
const char *pref_name, const char *default_value);
/**
+ * purple_account_option_list_new:
+ * @text: The text of the option.
+ * @pref_name: The account preference name for the option.
+ * @list: The key, value list.
+ *
* Creates a new list account option.
*
* The list passed will be owned by the account option, and the
* strings inside will be freed automatically.
*
* The list is a list of #PurpleKeyValuePair items. The key is the label that
- * should be displayed to the user, and the <tt>(const char *)</tt> value is
+ * should be displayed to the user, and the <type>(const char *)</type> value is
* the internal ID that should be passed to purple_account_set_string() to
* choose that value.
*
- * @param text The text of the option.
- * @param pref_name The account preference name for the option.
- * @param list The key, value list.
- *
- * @return The account option.
+ * Returns: The account option.
*/
PurpleAccountOption *purple_account_option_list_new(const char *text,
const char *pref_name, GList *list);
/**
- * Destroys an account option.
+ * purple_account_option_destroy:
+ * @option: The option to destroy.
*
- * @param option The option to destroy.
+ * Destroys an account option.
*/
void purple_account_option_destroy(PurpleAccountOption *option);
/**
- * Sets the default boolean value for an account option.
+ * purple_account_option_set_default_bool:
+ * @option: The account option.
+ * @value: The default boolean value.
*
- * @param option The account option.
- * @param value The default boolean value.
+ * Sets the default boolean value for an account option.
*/
void purple_account_option_set_default_bool(PurpleAccountOption *option,
gboolean value);
/**
- * Sets the default integer value for an account option.
+ * purple_account_option_set_default_int:
+ * @option: The account option.
+ * @value: The default integer value.
*
- * @param option The account option.
- * @param value The default integer value.
+ * Sets the default integer value for an account option.
*/
void purple_account_option_set_default_int(PurpleAccountOption *option,
int value);
/**
- * Sets the default string value for an account option.
+ * purple_account_option_set_default_string:
+ * @option: The account option.
+ * @value: The default string value.
*
- * @param option The account option.
- * @param value The default string value.
+ * Sets the default string value for an account option.
*/
void purple_account_option_set_default_string(PurpleAccountOption *option,
const char *value);
/**
+ * purple_account_option_string_set_masked:
+ * @option: The account option.
+ * @masked: The masking.
+ *
* Sets the masking for an account option. Setting this to %TRUE acts
* as a hint to the UI that the option's value should be obscured from
* view, like a password.
- *
- * @param option The account option.
- * @param masked The masking.
*/
void
-purple_account_option_set_masked(PurpleAccountOption *option, gboolean masked);
+purple_account_option_string_set_masked(PurpleAccountOption *option, gboolean masked);
/**
+ * purple_account_option_string_set_hints:
+ * @option: The account option.
+ * @hints: The list of hints, stored as strings.
+ *
+ * Sets the hint list for an account option.
+ *
+ * The list passed will be owned by the account option, and the
+ * strings inside will be freed automatically.
+ */
+void purple_account_option_string_set_hints(PurpleAccountOption *option,
+ GSList *hints);
+
+/**
+ * purple_account_option_set_list:
+ * @option: The account option.
+ * @values: The default list value.
+ *
* Sets the list values for an account option.
*
* The list passed will be owned by the account option, and the
@@ -206,190 +211,232 @@ purple_account_option_set_masked(PurpleAccountOption *option, gboolean masked);
*
* The list is in key, value pairs. The key is the ID stored and used
* internally, and the value is the label displayed.
- *
- * @param option The account option.
- * @param values The default list value.
*/
void purple_account_option_set_list(PurpleAccountOption *option, GList *values);
/**
- * Adds an item to a list account option.
+ * purple_account_option_add_list_item:
+ * @option: The account option.
+ * @key: The key.
+ * @value: The value.
*
- * @param option The account option.
- * @param key The key.
- * @param value The value.
+ * Adds an item to a list account option.
*/
void purple_account_option_add_list_item(PurpleAccountOption *option,
const char *key, const char *value);
/**
- * Returns the specified account option's type.
+ * purple_account_option_get_pref_type:
+ * @option: The account option.
*
- * @param option The account option.
+ * Returns the specified account option's type.
*
- * @return The account option's type.
+ * Returns: The account option's type.
*/
-PurplePrefType purple_account_option_get_type(const PurpleAccountOption *option);
+PurplePrefType purple_account_option_get_pref_type(const PurpleAccountOption *option);
/**
- * Returns the text for an account option.
+ * purple_account_option_get_text:
+ * @option: The account option.
*
- * @param option The account option.
+ * Returns the text for an account option.
*
- * @return The account option's text.
+ * Returns: The account option's text.
*/
const char *purple_account_option_get_text(const PurpleAccountOption *option);
/**
+ * purple_account_option_get_setting:
+ * @option: The account option.
+ *
* Returns the name of an account option. This corresponds to the @c pref_name
* parameter supplied to purple_account_option_new() or one of the
* type-specific constructors.
*
- * @param option The account option.
- *
- * @return The option's name.
+ * Returns: The option's name.
*/
const char *purple_account_option_get_setting(const PurpleAccountOption *option);
/**
- * Returns the default boolean value for an account option.
+ * purple_account_option_get_default_bool:
+ * @option: The account option.
*
- * @param option The account option.
+ * Returns the default boolean value for an account option.
*
- * @return The default boolean value.
+ * Returns: The default boolean value.
*/
gboolean purple_account_option_get_default_bool(const PurpleAccountOption *option);
/**
- * Returns the default integer value for an account option.
+ * purple_account_option_get_default_int:
+ * @option: The account option.
*
- * @param option The account option.
+ * Returns the default integer value for an account option.
*
- * @return The default integer value.
+ * Returns: The default integer value.
*/
int purple_account_option_get_default_int(const PurpleAccountOption *option);
/**
- * Returns the default string value for an account option.
+ * purple_account_option_get_default_string:
+ * @option: The account option.
*
- * @param option The account option.
+ * Returns the default string value for an account option.
*
- * @return The default string value.
+ * Returns: The default string value.
*/
const char *purple_account_option_get_default_string(
const PurpleAccountOption *option);
/**
- * Returns the default string value for a list account option.
+ * purple_account_option_get_default_list_value:
+ * @option: The account option.
*
- * @param option The account option.
+ * Returns the default string value for a list account option.
*
- * @return The default list string value.
+ * Returns: The default list string value.
*/
const char *purple_account_option_get_default_list_value(
const PurpleAccountOption *option);
/**
+ * purple_account_option_string_get_masked:
+ * @option: The account option.
+ *
* Returns whether an option's value should be masked from view, like a
* password. If so, the UI might display each character of the option
* as a '*' (for example).
*
- * @param option The account option.
- *
- * @return %TRUE if the option's value should be obscured.
+ * Returns: %TRUE if the option's value should be obscured.
*/
gboolean
-purple_account_option_get_masked(const PurpleAccountOption *option);
+purple_account_option_string_get_masked(const PurpleAccountOption *option);
/**
- * Returns the list values for an account option.
+ * purple_account_option_string_get_hints:
+ * @option: The account option.
+ *
+ * Returns the list of hints for an account option.
+ *
+ * Returns: (transfer none): A list of hints, stored as strings.
+ */
+const GSList * purple_account_option_string_get_hints(const PurpleAccountOption *option);
+
+/**
+ * purple_account_option_get_list:
+ * @option: The account option.
*
- * @param option The account option.
+ * Returns the list values for an account option.
*
- * @constreturn A list of #PurpleKeyValuePair, mapping the human-readable
- * description of the value to the <tt>(const char *)</tt> that
+ * Returns: (transfer none): A list of #PurpleKeyValuePair, mapping the human-readable
+ * description of the value to the <type>(const char *)</type> that
* should be passed to purple_account_set_string() to set the
* option.
*/
GList *purple_account_option_get_list(const PurpleAccountOption *option);
-/*@}*/
-
/**************************************************************************/
-/** @name Account User Split API */
+/* Account User Split API */
/**************************************************************************/
-/*@{*/
/**
- * Creates a new account username split.
+ * purple_account_user_split_new:
+ * @text: The text of the option.
+ * @default_value: The default value.
+ * @sep: The field separator.
*
- * @param text The text of the option.
- * @param default_value The default value.
- * @param sep The field separator.
+ * Creates a new account username split.
*
- * @return The new user split.
+ * Returns: The new user split.
*/
PurpleAccountUserSplit *purple_account_user_split_new(const char *text,
const char *default_value,
char sep);
/**
- * Destroys an account username split.
+ * purple_account_user_split_destroy:
+ * @split: The split to destroy.
*
- * @param split The split to destroy.
+ * Destroys an account username split.
*/
void purple_account_user_split_destroy(PurpleAccountUserSplit *split);
/**
- * Returns the text for an account username split.
+ * purple_account_user_split_get_text:
+ * @split: The account username split.
*
- * @param split The account username split.
+ * Returns the text for an account username split.
*
- * @return The account username split's text.
+ * Returns: The account username split's text.
*/
const char *purple_account_user_split_get_text(const PurpleAccountUserSplit *split);
/**
- * Returns the default string value for an account split.
+ * purple_account_user_split_get_default_value:
+ * @split: The account username split.
*
- * @param split The account username split.
+ * Returns the default string value for an account split.
*
- * @return The default string.
+ * Returns: The default string.
*/
const char *purple_account_user_split_get_default_value(
const PurpleAccountUserSplit *split);
/**
- * Returns the field separator for an account split.
+ * purple_account_user_split_get_separator:
+ * @split: The account username split.
*
- * @param split The account username split.
+ * Returns the field separator for an account split.
*
- * @return The field separator.
+ * Returns: The field separator.
*/
char purple_account_user_split_get_separator(const PurpleAccountUserSplit *split);
/**
- * Returns the 'reverse' value for an account split.
+ * purple_account_user_split_get_reverse:
+ * @split: The account username split.
*
- * @param split The account username split.
+ * Returns the 'reverse' value for an account split.
*
- * @return The 'reverse' value.
+ * Returns: The 'reverse' value.
*/
gboolean purple_account_user_split_get_reverse(const PurpleAccountUserSplit *split);
/**
- * Sets the 'reverse' value for an account split.
+ * purple_account_user_split_set_reverse:
+ * @split: The account username split.
+ * @reverse: The 'reverse' value
*
- * @param split The account username split.
- * @param reverse The 'reverse' value
+ * Sets the 'reverse' value for an account split.
*/
void purple_account_user_split_set_reverse(PurpleAccountUserSplit *split, gboolean reverse);
-/*@}*/
+/**
+ * purple_account_user_split_is_constant:
+ * @split: The account username split.
+ *
+ * Returns the constant parameter for an account split.
+ *
+ * When split is constant, it does not need to be displayed
+ * in configuration dialog.
+ *
+ * Returns: %TRUE, if the split is constant.
+ */
+gboolean
+purple_account_user_split_is_constant(const PurpleAccountUserSplit *split);
+
+/**
+ * purple_account_user_split_set_constant:
+ * @split: The account username split.
+ * @constant: %TRUE, if the split is a constant part.
+ *
+ * Sets the constant parameter of account split.
+ */
+void
+purple_account_user_split_set_constant(PurpleAccountUserSplit *split,
+ gboolean constant);
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_ACCOUNTOPT_H_ */
diff --git a/libpurple/accounts.c b/libpurple/accounts.c
new file mode 100644
index 0000000000..c0499202ef
--- /dev/null
+++ b/libpurple/accounts.c
@@ -0,0 +1,1041 @@
+/* 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 "accounts.h"
+#include "core.h"
+#include "dbus-maybe.h"
+#include "debug.h"
+#include "enums.h"
+#include "network.h"
+#include "pounce.h"
+
+static PurpleAccountUiOps *account_ui_ops = NULL;
+
+static GList *accounts = NULL;
+static guint save_timer = 0;
+static gboolean accounts_loaded = FALSE;
+
+/*********************************************************************
+ * Writing to disk *
+ *********************************************************************/
+static PurpleXmlNode *
+accounts_to_xmlnode(void)
+{
+ PurpleXmlNode *node, *child;
+ GList *cur;
+
+ node = purple_xmlnode_new("account");
+ purple_xmlnode_set_attrib(node, "version", "1.0");
+
+ for (cur = purple_accounts_get_all(); cur != NULL; cur = cur->next)
+ {
+ child = _purple_account_to_xmlnode(cur->data);
+ purple_xmlnode_insert_child(node, child);
+ }
+
+ return node;
+}
+
+static void
+sync_accounts(void)
+{
+ PurpleXmlNode *node;
+ char *data;
+
+ if (!accounts_loaded)
+ {
+ purple_debug_error("accounts", "Attempted to save accounts before "
+ "they were read!\n");
+ return;
+ }
+
+ node = accounts_to_xmlnode();
+ data = purple_xmlnode_to_formatted_str(node, NULL);
+ purple_util_write_data_to_file("accounts.xml", data, -1);
+ g_free(data);
+ purple_xmlnode_free(node);
+}
+
+static gboolean
+save_cb(gpointer data)
+{
+ sync_accounts();
+ save_timer = 0;
+ return FALSE;
+}
+
+void
+purple_accounts_schedule_save(void)
+{
+ if (save_timer == 0)
+ save_timer = purple_timeout_add_seconds(5, save_cb, NULL);
+}
+
+/*********************************************************************
+ * Reading from disk *
+ *********************************************************************/
+static void
+migrate_yahoo_japan(PurpleAccount *account)
+{
+ /* detect a Yahoo! JAPAN account that existed prior to 2.6.0 and convert it
+ * to use the new prpl-yahoojp. Also remove the account-specific settings
+ * we no longer need */
+
+ if(purple_strequal(purple_account_get_protocol_id(account), "prpl-yahoo")) {
+ if(purple_account_get_bool(account, "yahoojp", FALSE)) {
+ const char *serverjp = purple_account_get_string(account, "serverjp", NULL);
+ const char *xferjp_host = purple_account_get_string(account, "xferjp_host", NULL);
+
+ g_return_if_fail(serverjp != NULL);
+ g_return_if_fail(xferjp_host != NULL);
+
+ purple_account_set_string(account, "server", serverjp);
+ purple_account_set_string(account, "xfer_host", xferjp_host);
+
+ purple_account_set_protocol_id(account, "prpl-yahoojp");
+ }
+
+ /* these should always be nuked */
+ purple_account_remove_setting(account, "yahoojp");
+ purple_account_remove_setting(account, "serverjp");
+ purple_account_remove_setting(account, "xferjp_host");
+
+ }
+}
+
+static void
+migrate_icq_server(PurpleAccount *account)
+{
+ /* Migrate the login server setting for ICQ accounts. See
+ * 'mtn log --last 1 --no-graph --from b6d7712e90b68610df3bd2d8cbaf46d94c8b3794'
+ * for details on the change. */
+
+ if(purple_strequal(purple_account_get_protocol_id(account), "prpl-icq")) {
+ const char *tmp = purple_account_get_string(account, "server", NULL);
+
+ /* Non-secure server */
+ if(purple_strequal(tmp, "login.messaging.aol.com") ||
+ purple_strequal(tmp, "login.oscar.aol.com"))
+ purple_account_set_string(account, "server", "login.icq.com");
+
+ /* Secure server */
+ if(purple_strequal(tmp, "slogin.oscar.aol.com"))
+ purple_account_set_string(account, "server", "slogin.icq.com");
+ }
+}
+
+static void
+migrate_xmpp_encryption(PurpleAccount *account)
+{
+ /* When this is removed, nuke the "old_ssl" and "require_tls" settings */
+ if (g_str_equal(purple_account_get_protocol_id(account), "prpl-jabber")) {
+ const char *sec = purple_account_get_string(account, "connection_security", "");
+
+ if (g_str_equal("", sec)) {
+ const char *val = "require_tls";
+ if (purple_account_get_bool(account, "old_ssl", FALSE))
+ val = "old_ssl";
+ else if (!purple_account_get_bool(account, "require_tls", TRUE))
+ val = "opportunistic_tls";
+
+ purple_account_set_string(account, "connection_security", val);
+ }
+ }
+}
+
+static void
+parse_settings(PurpleXmlNode *node, PurpleAccount *account)
+{
+ const char *ui;
+ PurpleXmlNode *child;
+
+ /* Get the UI string, if these are UI settings */
+ ui = purple_xmlnode_get_attrib(node, "ui");
+
+ /* Read settings, one by one */
+ for (child = purple_xmlnode_get_child(node, "setting"); child != NULL;
+ child = purple_xmlnode_get_next_twin(child))
+ {
+ const char *name, *str_type;
+ PurplePrefType type;
+ char *data;
+
+ name = purple_xmlnode_get_attrib(child, "name");
+ if (name == NULL)
+ /* Ignore this setting */
+ continue;
+
+ str_type = purple_xmlnode_get_attrib(child, "type");
+ if (str_type == NULL)
+ /* Ignore this setting */
+ continue;
+
+ if (purple_strequal(str_type, "string"))
+ type = PURPLE_PREF_STRING;
+ else if (purple_strequal(str_type, "int"))
+ type = PURPLE_PREF_INT;
+ else if (purple_strequal(str_type, "bool"))
+ type = PURPLE_PREF_BOOLEAN;
+ else
+ /* Ignore this setting */
+ continue;
+
+ data = purple_xmlnode_get_data(child);
+ if (data == NULL)
+ /* Ignore this setting */
+ continue;
+
+ if (ui == NULL)
+ {
+ if (type == PURPLE_PREF_STRING)
+ purple_account_set_string(account, name, data);
+ else if (type == PURPLE_PREF_INT)
+ purple_account_set_int(account, name, atoi(data));
+ else if (type == PURPLE_PREF_BOOLEAN)
+ purple_account_set_bool(account, name,
+ (*data == '0' ? FALSE : TRUE));
+ } else {
+ if (type == PURPLE_PREF_STRING)
+ purple_account_set_ui_string(account, ui, name, data);
+ else if (type == PURPLE_PREF_INT)
+ purple_account_set_ui_int(account, ui, name, atoi(data));
+ else if (type == PURPLE_PREF_BOOLEAN)
+ purple_account_set_ui_bool(account, ui, name,
+ (*data == '0' ? FALSE : TRUE));
+ }
+
+ g_free(data);
+ }
+
+ /* we do this here because we need access to account settings to determine
+ * if we can/should migrate an old Yahoo! JAPAN account */
+ migrate_yahoo_japan(account);
+ /* we do this here because we need access to account settings to determine
+ * if we can/should migrate an ICQ account's server setting */
+ migrate_icq_server(account);
+ /* we do this here because we need to do it before the user views the
+ * Edit Account dialog. */
+ migrate_xmpp_encryption(account);
+}
+
+static GList *
+parse_status_attrs(PurpleXmlNode *node, PurpleStatus *status)
+{
+ GList *list = NULL;
+ PurpleXmlNode *child;
+ GValue *attr_value;
+
+ for (child = purple_xmlnode_get_child(node, "attribute"); child != NULL;
+ child = purple_xmlnode_get_next_twin(child))
+ {
+ const char *id = purple_xmlnode_get_attrib(child, "id");
+ const char *value = purple_xmlnode_get_attrib(child, "value");
+
+ if (!id || !*id || !value || !*value)
+ continue;
+
+ attr_value = purple_status_get_attr_value(status, id);
+ if (!attr_value)
+ continue;
+
+ list = g_list_append(list, (char *)id);
+
+ switch (G_VALUE_TYPE(attr_value))
+ {
+ case G_TYPE_STRING:
+ list = g_list_append(list, (char *)value);
+ break;
+ case G_TYPE_INT:
+ case G_TYPE_BOOLEAN:
+ {
+ int v;
+ if (sscanf(value, "%d", &v) == 1)
+ list = g_list_append(list, GINT_TO_POINTER(v));
+ else
+ list = g_list_remove(list, id);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return list;
+}
+
+static void
+parse_status(PurpleXmlNode *node, PurpleAccount *account)
+{
+ gboolean active = FALSE;
+ const char *data;
+ const char *type;
+ PurpleXmlNode *child;
+ GList *attrs = NULL;
+
+ /* Get the active/inactive state */
+ data = purple_xmlnode_get_attrib(node, "active");
+ if (data == NULL)
+ return;
+ if (g_ascii_strcasecmp(data, "true") == 0)
+ active = TRUE;
+ else if (g_ascii_strcasecmp(data, "false") == 0)
+ active = FALSE;
+ else
+ return;
+
+ /* Get the type of the status */
+ type = purple_xmlnode_get_attrib(node, "type");
+ if (type == NULL)
+ return;
+
+ /* Read attributes into a GList */
+ child = purple_xmlnode_get_child(node, "attributes");
+ if (child != NULL)
+ {
+ attrs = parse_status_attrs(child,
+ purple_account_get_status(account, type));
+ }
+
+ purple_account_set_status_list(account, type, active, attrs);
+
+ g_list_free(attrs);
+}
+
+static void
+parse_statuses(PurpleXmlNode *node, PurpleAccount *account)
+{
+ PurpleXmlNode *child;
+
+ for (child = purple_xmlnode_get_child(node, "status"); child != NULL;
+ child = purple_xmlnode_get_next_twin(child))
+ {
+ parse_status(child, account);
+ }
+}
+
+static void
+parse_proxy_info(PurpleXmlNode *node, PurpleAccount *account)
+{
+ PurpleProxyInfo *proxy_info;
+ PurpleXmlNode *child;
+ char *data;
+
+ proxy_info = purple_proxy_info_new();
+
+ /* Use the global proxy settings, by default */
+ purple_proxy_info_set_proxy_type(proxy_info, PURPLE_PROXY_USE_GLOBAL);
+
+ /* Read proxy type */
+ child = purple_xmlnode_get_child(node, "type");
+ if ((child != NULL) && ((data = purple_xmlnode_get_data(child)) != NULL))
+ {
+ if (purple_strequal(data, "global"))
+ purple_proxy_info_set_proxy_type(proxy_info, PURPLE_PROXY_USE_GLOBAL);
+ else if (purple_strequal(data, "none"))
+ purple_proxy_info_set_proxy_type(proxy_info, PURPLE_PROXY_NONE);
+ else if (purple_strequal(data, "http"))
+ purple_proxy_info_set_proxy_type(proxy_info, PURPLE_PROXY_HTTP);
+ else if (purple_strequal(data, "socks4"))
+ purple_proxy_info_set_proxy_type(proxy_info, PURPLE_PROXY_SOCKS4);
+ else if (purple_strequal(data, "socks5"))
+ purple_proxy_info_set_proxy_type(proxy_info, PURPLE_PROXY_SOCKS5);
+ else if (purple_strequal(data, "tor"))
+ purple_proxy_info_set_proxy_type(proxy_info, PURPLE_PROXY_TOR);
+ else if (purple_strequal(data, "envvar"))
+ purple_proxy_info_set_proxy_type(proxy_info, PURPLE_PROXY_USE_ENVVAR);
+ else
+ {
+ purple_debug_error("accounts", "Invalid proxy type found when "
+ "loading account information for %s\n",
+ purple_account_get_username(account));
+ }
+ g_free(data);
+ }
+
+ /* Read proxy host */
+ child = purple_xmlnode_get_child(node, "host");
+ if ((child != NULL) && ((data = purple_xmlnode_get_data(child)) != NULL))
+ {
+ purple_proxy_info_set_host(proxy_info, data);
+ g_free(data);
+ }
+
+ /* Read proxy port */
+ child = purple_xmlnode_get_child(node, "port");
+ if ((child != NULL) && ((data = purple_xmlnode_get_data(child)) != NULL))
+ {
+ purple_proxy_info_set_port(proxy_info, atoi(data));
+ g_free(data);
+ }
+
+ /* Read proxy username */
+ child = purple_xmlnode_get_child(node, "username");
+ if ((child != NULL) && ((data = purple_xmlnode_get_data(child)) != NULL))
+ {
+ purple_proxy_info_set_username(proxy_info, data);
+ g_free(data);
+ }
+
+ /* Read proxy password */
+ child = purple_xmlnode_get_child(node, "password");
+ if ((child != NULL) && ((data = purple_xmlnode_get_data(child)) != NULL))
+ {
+ purple_proxy_info_set_password(proxy_info, data);
+ g_free(data);
+ }
+
+ /* If there are no values set then proxy_info NULL */
+ if ((purple_proxy_info_get_proxy_type(proxy_info) == PURPLE_PROXY_USE_GLOBAL) &&
+ (purple_proxy_info_get_host(proxy_info) == NULL) &&
+ (purple_proxy_info_get_port(proxy_info) == 0) &&
+ (purple_proxy_info_get_username(proxy_info) == NULL) &&
+ (purple_proxy_info_get_password(proxy_info) == NULL))
+ {
+ purple_proxy_info_destroy(proxy_info);
+ return;
+ }
+
+ purple_account_set_proxy_info(account, proxy_info);
+}
+
+static void
+parse_current_error(PurpleXmlNode *node, PurpleAccount *account)
+{
+ guint type;
+ char *type_str = NULL, *description = NULL;
+ PurpleXmlNode *child;
+ PurpleConnectionErrorInfo *current_error = NULL;
+
+ child = purple_xmlnode_get_child(node, "type");
+ if (child == NULL || (type_str = purple_xmlnode_get_data(child)) == NULL)
+ return;
+ type = atoi(type_str);
+ g_free(type_str);
+
+ if (type > PURPLE_CONNECTION_ERROR_OTHER_ERROR)
+ {
+ purple_debug_error("accounts",
+ "Invalid PurpleConnectionError value %d found when "
+ "loading account information for %s\n",
+ type, purple_account_get_username(account));
+ type = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
+ }
+
+ child = purple_xmlnode_get_child(node, "description");
+ if (child)
+ description = purple_xmlnode_get_data(child);
+ if (description == NULL)
+ description = g_strdup("");
+
+ current_error = g_new0(PurpleConnectionErrorInfo, 1);
+ PURPLE_DBUS_REGISTER_POINTER(current_error, PurpleConnectionErrorInfo);
+ current_error->type = type;
+ current_error->description = description;
+
+ _purple_account_set_current_error(account, current_error);
+}
+
+static PurpleAccount *
+parse_account(PurpleXmlNode *node)
+{
+ PurpleAccount *ret;
+ PurpleXmlNode *child;
+ char *protocol_id = NULL;
+ char *name = NULL;
+ char *data;
+
+ child = purple_xmlnode_get_child(node, "protocol");
+ if (child != NULL)
+ protocol_id = purple_xmlnode_get_data(child);
+
+ child = purple_xmlnode_get_child(node, "name");
+ if (child != NULL)
+ name = purple_xmlnode_get_data(child);
+ if (name == NULL)
+ {
+ /* Do we really need to do this? */
+ child = purple_xmlnode_get_child(node, "username");
+ if (child != NULL)
+ name = purple_xmlnode_get_data(child);
+ }
+
+ if ((protocol_id == NULL) || (name == NULL))
+ {
+ g_free(protocol_id);
+ g_free(name);
+ return NULL;
+ }
+
+ ret = purple_account_new(name, protocol_id);
+ g_free(name);
+ g_free(protocol_id);
+
+ /* Read the alias */
+ child = purple_xmlnode_get_child(node, "alias");
+ if ((child != NULL) && ((data = purple_xmlnode_get_data(child)) != NULL))
+ {
+ if (*data != '\0')
+ purple_account_set_private_alias(ret, data);
+ g_free(data);
+ }
+
+ /* Read the statuses */
+ child = purple_xmlnode_get_child(node, "statuses");
+ if (child != NULL)
+ {
+ parse_statuses(child, ret);
+ }
+
+ /* Read the userinfo */
+ child = purple_xmlnode_get_child(node, "userinfo");
+ if ((child != NULL) && ((data = purple_xmlnode_get_data(child)) != NULL))
+ {
+ purple_account_set_user_info(ret, data);
+ g_free(data);
+ }
+
+ /* Read an old buddyicon */
+ child = purple_xmlnode_get_child(node, "buddyicon");
+ if ((child != NULL) && ((data = purple_xmlnode_get_data(child)) != NULL))
+ {
+ const char *dirname = purple_buddy_icons_get_cache_dir();
+ char *filename = g_build_filename(dirname, data, NULL);
+ gchar *contents;
+ gsize len;
+
+ if (g_file_get_contents(filename, &contents, &len, NULL))
+ {
+ purple_buddy_icons_set_account_icon(ret, (guchar *)contents, len);
+ }
+
+ g_free(filename);
+ g_free(data);
+ }
+
+ /* Read settings (both core and UI) */
+ for (child = purple_xmlnode_get_child(node, "settings"); child != NULL;
+ child = purple_xmlnode_get_next_twin(child))
+ {
+ parse_settings(child, ret);
+ }
+
+ /* Read proxy */
+ child = purple_xmlnode_get_child(node, "proxy");
+ if (child != NULL)
+ {
+ parse_proxy_info(child, ret);
+ }
+
+ /* Read current error */
+ child = purple_xmlnode_get_child(node, "current_error");
+ if (child != NULL)
+ {
+ parse_current_error(child, ret);
+ }
+
+ /* Read the password */
+ child = purple_xmlnode_get_child(node, "password");
+ if (child != NULL)
+ {
+ const char *keyring_id = purple_xmlnode_get_attrib(child, "keyring_id");
+ const char *mode = purple_xmlnode_get_attrib(child, "mode");
+ gboolean result;
+
+ data = purple_xmlnode_get_data(child);
+ result = purple_keyring_import_password(ret, keyring_id, mode, data, NULL);
+
+ if (result == TRUE || purple_keyring_get_inuse() == NULL) {
+ purple_account_set_remember_password(ret, TRUE);
+ } else {
+ purple_debug_error("accounts", "Failed to import password.\n");
+ }
+ purple_str_wipe(data);
+ }
+
+ return ret;
+}
+
+static void
+load_accounts(void)
+{
+ PurpleXmlNode *node, *child;
+
+ accounts_loaded = TRUE;
+
+ node = purple_util_read_xml_from_file("accounts.xml", _("accounts"));
+
+ if (node == NULL)
+ return;
+
+ for (child = purple_xmlnode_get_child(node, "account"); child != NULL;
+ child = purple_xmlnode_get_next_twin(child))
+ {
+ PurpleAccount *new_acct;
+ new_acct = parse_account(child);
+ purple_accounts_add(new_acct);
+ }
+
+ purple_xmlnode_free(node);
+
+ _purple_buddy_icons_account_loaded_cb();
+}
+
+void
+purple_accounts_add(PurpleAccount *account)
+{
+ g_return_if_fail(account != NULL);
+
+ if (g_list_find(accounts, account) != NULL)
+ return;
+
+ accounts = g_list_append(accounts, account);
+
+ purple_accounts_schedule_save();
+
+ purple_signal_emit(purple_accounts_get_handle(), "account-added", account);
+}
+
+void
+purple_accounts_remove(PurpleAccount *account)
+{
+ g_return_if_fail(account != NULL);
+
+ accounts = g_list_remove(accounts, account);
+
+ purple_accounts_schedule_save();
+
+ /* Clearing the error ensures that account-error-changed is emitted,
+ * which is the end of the guarantee that the the error's pointer is
+ * valid.
+ */
+ purple_account_clear_current_error(account);
+ purple_signal_emit(purple_accounts_get_handle(), "account-removed", account);
+}
+
+static void
+purple_accounts_delete_set(PurpleAccount *account, GError *error, gpointer data)
+{
+ g_object_unref(G_OBJECT(account));
+}
+
+void
+purple_accounts_delete(PurpleAccount *account)
+{
+ PurpleBlistNode *gnode, *cnode, *bnode;
+ GList *iter;
+
+ g_return_if_fail(account != NULL);
+
+ /*
+ * Disable the account before blowing it out of the water.
+ * Conceptually it probably makes more sense to disable the
+ * account for all UIs rather than the just the current UI,
+ * but it doesn't really matter.
+ */
+ purple_account_set_enabled(account, purple_core_get_ui(), FALSE);
+
+ purple_notify_close_with_handle(account);
+ purple_request_close_with_handle(account);
+
+ 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))
+ {
+ if (!PURPLE_IS_GROUP(gnode))
+ continue;
+
+ cnode = purple_blist_node_get_first_child(gnode);
+ while (cnode) {
+ PurpleBlistNode *cnode_next = purple_blist_node_get_sibling_next(cnode);
+
+ if(PURPLE_IS_CONTACT(cnode)) {
+ bnode = purple_blist_node_get_first_child(cnode);
+ while (bnode) {
+ PurpleBlistNode *bnode_next = purple_blist_node_get_sibling_next(bnode);
+
+ if (PURPLE_IS_BUDDY(bnode)) {
+ PurpleBuddy *b = (PurpleBuddy *)bnode;
+
+ if (purple_buddy_get_account(b) == account)
+ purple_blist_remove_buddy(b);
+ }
+ bnode = bnode_next;
+ }
+ } else if (PURPLE_IS_CHAT(cnode)) {
+ PurpleChat *c = (PurpleChat *)cnode;
+
+ if (purple_chat_get_account(c) == account)
+ purple_blist_remove_chat(c);
+ }
+ cnode = cnode_next;
+ }
+ }
+
+ /* Remove any open conversation for this account */
+ for (iter = purple_conversations_get_all(); iter; ) {
+ PurpleConversation *conv = iter->data;
+ iter = iter->next;
+ if (purple_conversation_get_account(conv) == account)
+ g_object_unref(conv);
+ }
+
+ /* Remove this account's pounces */
+ purple_pounce_destroy_all_by_account(account);
+
+ /* This will cause the deletion of an old buddy icon. */
+ purple_buddy_icons_set_account_icon(account, NULL, 0);
+
+ /* This is async because we do not want the
+ * account being overwritten before we are done.
+ */
+ purple_keyring_set_password(account, NULL,
+ purple_accounts_delete_set, NULL);
+}
+
+void
+purple_accounts_reorder(PurpleAccount *account, guint new_index)
+{
+ gint index;
+ GList *l;
+
+ g_return_if_fail(account != NULL);
+ g_return_if_fail(new_index <= g_list_length(accounts));
+
+ index = g_list_index(accounts, account);
+
+ if (index < 0) {
+ purple_debug_error("accounts",
+ "Unregistered account (%s) discovered during reorder!\n",
+ purple_account_get_username(account));
+ return;
+ }
+
+ l = g_list_nth(accounts, index);
+
+ if (new_index > (guint)index)
+ new_index--;
+
+ /* Remove the old one. */
+ accounts = g_list_delete_link(accounts, l);
+
+ /* Insert it where it should go. */
+ accounts = g_list_insert(accounts, account, new_index);
+
+ purple_accounts_schedule_save();
+}
+
+GList *
+purple_accounts_get_all(void)
+{
+ return accounts;
+}
+
+GList *
+purple_accounts_get_all_active(void)
+{
+ GList *list = NULL;
+ GList *all = purple_accounts_get_all();
+
+ while (all != NULL) {
+ PurpleAccount *account = all->data;
+
+ if (purple_account_get_enabled(account, purple_core_get_ui()))
+ list = g_list_append(list, account);
+
+ all = all->next;
+ }
+
+ return list;
+}
+
+PurpleAccount *
+purple_accounts_find(const char *name, const char *protocol_id)
+{
+ PurpleAccount *account = NULL;
+ GList *l;
+ char *who;
+
+ g_return_val_if_fail(name != NULL, NULL);
+ g_return_val_if_fail(protocol_id != NULL, NULL);
+
+ for (l = purple_accounts_get_all(); l != NULL; l = l->next) {
+ account = (PurpleAccount *)l->data;
+
+ if (!purple_strequal(purple_account_get_protocol_id(account), protocol_id))
+ continue;
+
+ who = g_strdup(purple_normalize(account, name));
+ if (purple_strequal(purple_normalize(account, purple_account_get_username(account)), who)) {
+ g_free(who);
+ return account;
+ }
+ g_free(who);
+ }
+
+ return NULL;
+}
+
+void
+purple_accounts_restore_current_statuses()
+{
+ GList *l;
+ PurpleAccount *account;
+
+ /* If we're not connected to the Internet right now, we bail on this */
+ if (!purple_network_is_available())
+ {
+ purple_debug_warning("accounts", "Network not connected; skipping reconnect\n");
+ return;
+ }
+
+ for (l = purple_accounts_get_all(); l != NULL; l = l->next)
+ {
+ account = (PurpleAccount *)l->data;
+
+ if (purple_account_get_enabled(account, purple_core_get_ui()) &&
+ (purple_presence_is_online(purple_account_get_presence(account))))
+ {
+ purple_account_connect(account);
+ }
+ }
+}
+
+static PurpleAccountUiOps *
+purple_account_ui_ops_copy(PurpleAccountUiOps *ops)
+{
+ PurpleAccountUiOps *ops_new;
+
+ g_return_val_if_fail(ops != NULL, NULL);
+
+ ops_new = g_new(PurpleAccountUiOps, 1);
+ *ops_new = *ops;
+
+ return ops_new;
+}
+
+GType
+purple_account_ui_ops_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleAccountUiOps",
+ (GBoxedCopyFunc)purple_account_ui_ops_copy,
+ (GBoxedFreeFunc)g_free);
+ }
+
+ return type;
+}
+
+void
+purple_accounts_set_ui_ops(PurpleAccountUiOps *ops)
+{
+ account_ui_ops = ops;
+}
+
+PurpleAccountUiOps *
+purple_accounts_get_ui_ops(void)
+{
+ return account_ui_ops;
+}
+
+void *
+purple_accounts_get_handle(void)
+{
+ static int handle;
+
+ return &handle;
+}
+
+static void
+signed_on_cb(PurpleConnection *gc,
+ gpointer unused)
+{
+ PurpleAccount *account = purple_connection_get_account(gc);
+ purple_account_clear_current_error(account);
+
+ purple_signal_emit(purple_accounts_get_handle(), "account-signed-on",
+ account);
+}
+
+static void
+signed_off_cb(PurpleConnection *gc,
+ gpointer unused)
+{
+ PurpleAccount *account = purple_connection_get_account(gc);
+
+ purple_signal_emit(purple_accounts_get_handle(), "account-signed-off",
+ account);
+}
+
+static void
+connection_error_cb(PurpleConnection *gc,
+ PurpleConnectionError type,
+ const gchar *description,
+ gpointer unused)
+{
+ PurpleAccount *account;
+ PurpleConnectionErrorInfo *err;
+
+ account = purple_connection_get_account(gc);
+
+ g_return_if_fail(account != NULL);
+
+ err = g_new0(PurpleConnectionErrorInfo, 1);
+ PURPLE_DBUS_REGISTER_POINTER(err, PurpleConnectionErrorInfo);
+
+ err->type = type;
+ err->description = g_strdup(description);
+
+ _purple_account_set_current_error(account, err);
+
+ purple_signal_emit(purple_accounts_get_handle(), "account-connection-error",
+ account, type, description);
+}
+
+static void
+password_migration_cb(PurpleAccount *account)
+{
+ /* account may be NULL (means: all) */
+
+ purple_accounts_schedule_save();
+}
+
+void
+purple_accounts_init(void)
+{
+ void *handle = purple_accounts_get_handle();
+ void *conn_handle = purple_connections_get_handle();
+
+ purple_signal_register(handle, "account-connecting",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_ACCOUNT);
+
+ purple_signal_register(handle, "account-disabled",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_ACCOUNT);
+
+ purple_signal_register(handle, "account-enabled",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_ACCOUNT);
+
+ purple_signal_register(handle, "account-setting-info",
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ PURPLE_TYPE_ACCOUNT, G_TYPE_STRING);
+
+ purple_signal_register(handle, "account-set-info",
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ PURPLE_TYPE_ACCOUNT, G_TYPE_STRING);
+
+ purple_signal_register(handle, "account-created",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_ACCOUNT);
+
+ purple_signal_register(handle, "account-destroying",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_ACCOUNT);
+
+ purple_signal_register(handle, "account-added",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_ACCOUNT);
+
+ purple_signal_register(handle, "account-removed",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_ACCOUNT);
+
+ purple_signal_register(handle, "account-status-changed",
+ purple_marshal_VOID__POINTER_POINTER_POINTER,
+ G_TYPE_NONE, 3, PURPLE_TYPE_ACCOUNT,
+ PURPLE_TYPE_STATUS, PURPLE_TYPE_STATUS);
+
+ purple_signal_register(handle, "account-actions-changed",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_ACCOUNT);
+
+ purple_signal_register(handle, "account-alias-changed",
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ PURPLE_TYPE_ACCOUNT, G_TYPE_STRING);
+
+ purple_signal_register(handle, "account-authorization-requested",
+ purple_marshal_INT__POINTER_POINTER_POINTER,
+ G_TYPE_INT, 4, PURPLE_TYPE_ACCOUNT, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING);
+
+ purple_signal_register(handle, "account-authorization-denied",
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 3,
+ PURPLE_TYPE_ACCOUNT, G_TYPE_STRING, G_TYPE_STRING);
+
+ purple_signal_register(handle, "account-authorization-granted",
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 3,
+ PURPLE_TYPE_ACCOUNT, G_TYPE_STRING, G_TYPE_STRING);
+
+ purple_signal_register(handle, "account-error-changed",
+ purple_marshal_VOID__POINTER_POINTER_POINTER,
+ G_TYPE_NONE, 3, PURPLE_TYPE_ACCOUNT,
+ PURPLE_TYPE_CONNECTION_ERROR_INFO,
+ PURPLE_TYPE_CONNECTION_ERROR_INFO);
+
+ purple_signal_register(handle, "account-signed-on",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_ACCOUNT);
+
+ purple_signal_register(handle, "account-signed-off",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_ACCOUNT);
+
+ purple_signal_register(handle, "account-connection-error",
+ purple_marshal_VOID__POINTER_INT_POINTER,
+ G_TYPE_NONE, 3, PURPLE_TYPE_ACCOUNT,
+ PURPLE_TYPE_CONNECTION_ERROR, G_TYPE_STRING);
+
+ purple_signal_connect(conn_handle, "signed-on", handle,
+ PURPLE_CALLBACK(signed_on_cb), NULL);
+ purple_signal_connect(conn_handle, "signed-off", handle,
+ PURPLE_CALLBACK(signed_off_cb), NULL);
+ purple_signal_connect(conn_handle, "connection-error", handle,
+ PURPLE_CALLBACK(connection_error_cb), NULL);
+ purple_signal_connect(purple_keyring_get_handle(), "password-migration", handle,
+ PURPLE_CALLBACK(password_migration_cb), NULL);
+
+ load_accounts();
+
+}
+
+void
+purple_accounts_uninit(void)
+{
+ gpointer handle = purple_accounts_get_handle();
+ if (save_timer != 0)
+ {
+ purple_timeout_remove(save_timer);
+ save_timer = 0;
+ sync_accounts();
+ }
+
+ for (; accounts; accounts = g_list_delete_link(accounts, accounts))
+ g_object_unref(G_OBJECT(accounts->data));
+
+ purple_signals_disconnect_by_handle(handle);
+ purple_signals_unregister_by_instance(handle);
+}
diff --git a/libpurple/accounts.h b/libpurple/accounts.h
new file mode 100644
index 0000000000..cceb4312d2
--- /dev/null
+++ b/libpurple/accounts.h
@@ -0,0 +1,254 @@
+/* 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 _PURPLE_ACCOUNTS_H_
+#define _PURPLE_ACCOUNTS_H_
+/**
+ * SECTION:accounts
+ * @section_id: libpurple-accounts
+ * @short_description: <filename>accounts.h</filename>
+ * @title: Accounts Subsystem API
+ * @see_also: <link linkend="chapter-signals-account">Account signals</link>
+ */
+
+#include "account.h"
+#include "status.h"
+
+#define PURPLE_TYPE_ACCOUNT_UI_OPS (purple_account_ui_ops_get_type())
+
+typedef struct _PurpleAccountUiOps PurpleAccountUiOps;
+
+/**
+ * PurpleAccountUiOps:
+ * @notify_added: A buddy who is already on this account's buddy list
+ * added this account to their buddy list.
+ * @status_changed: This account's status changed.
+ * @request_add: Someone we don't have on our list added us; prompt
+ * to add them.
+ * @request_authorize: Prompt for authorization when someone adds this
+ * account to their buddy list. To authorize them to
+ * see this account's presence, call
+ * @authorize_cb (@message, @user_data) otherwise call
+ * @deny_cb (@message, @user_data).
+ * <sbr/>Returns: A UI-specific handle, as passed to
+ * @close_account_request.
+ * @close_account_request: Close a pending request for authorization.
+ * @ui_handle is a handle as returned by
+ * @request_authorize.
+ *
+ * Account UI operations, used to notify the user of status changes and when
+ * buddies add this account to their buddy lists.
+ */
+struct _PurpleAccountUiOps
+{
+ void (*notify_added)(PurpleAccount *account,
+ const char *remote_user,
+ const char *id,
+ const char *alias,
+ const char *message);
+
+ void (*status_changed)(PurpleAccount *account,
+ PurpleStatus *status);
+
+ void (*request_add)(PurpleAccount *account,
+ const char *remote_user,
+ const char *id,
+ const char *alias,
+ const char *message);
+
+ void *(*request_authorize)(PurpleAccount *account,
+ const char *remote_user,
+ const char *id,
+ const char *alias,
+ const char *message,
+ gboolean on_list,
+ PurpleAccountRequestAuthorizationCb authorize_cb,
+ PurpleAccountRequestAuthorizationCb deny_cb,
+ void *user_data);
+
+ void (*close_account_request)(void *ui_handle);
+
+ void (*permit_added)(PurpleAccount *account, const char *name);
+ void (*permit_removed)(PurpleAccount *account, const char *name);
+ void (*deny_added)(PurpleAccount *account, const char *name);
+ void (*deny_removed)(PurpleAccount *account, const char *name);
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+/**************************************************************************/
+/* Accounts API */
+/**************************************************************************/
+
+/**
+ * purple_accounts_add:
+ * @account: The account.
+ *
+ * Adds an account to the list of accounts.
+ */
+void purple_accounts_add(PurpleAccount *account);
+
+/**
+ * purple_accounts_remove:
+ * @account: The account.
+ *
+ * Removes an account from the list of accounts.
+ */
+void purple_accounts_remove(PurpleAccount *account);
+
+/**
+ * purple_accounts_delete:
+ * @account: The account.
+ *
+ * Deletes an account.
+ *
+ * This will remove any buddies from the buddy list that belong to this
+ * account, buddy pounces that belong to this account, and will also
+ * destroy @account.
+ */
+void purple_accounts_delete(PurpleAccount *account);
+
+/**
+ * purple_accounts_reorder:
+ * @account: The account to reorder.
+ * @new_index: The new index for the account.
+ *
+ * Reorders an account.
+ */
+void purple_accounts_reorder(PurpleAccount *account, guint new_index);
+
+/**
+ * purple_accounts_get_all:
+ *
+ * Returns a list of all accounts.
+ *
+ * Returns: (transfer none): A list of all accounts.
+ */
+GList *purple_accounts_get_all(void);
+
+/**
+ * purple_accounts_get_all_active:
+ *
+ * Returns a list of all enabled accounts
+ *
+ * Returns: A list of all enabled accounts. The list is owned
+ * by the caller, and must be g_list_free()d to avoid
+ * leaking the nodes.
+ */
+GList *purple_accounts_get_all_active(void);
+
+/**
+ * purple_accounts_find:
+ * @name: The account username.
+ * @protocol: The account protocol ID.
+ *
+ * Finds an account with the specified name and protocol id.
+ *
+ * Returns: The account, if found, or %FALSE otherwise.
+ */
+PurpleAccount *purple_accounts_find(const char *name, const char *protocol);
+
+/**
+ * purple_accounts_restore_current_statuses:
+ *
+ * This is called by the core after all subsystems and what
+ * not have been initialized. It sets all enabled accounts
+ * to their startup status by signing them on, setting them
+ * away, etc.
+ *
+ * You probably shouldn't call this unless you really know
+ * what you're doing.
+ */
+void purple_accounts_restore_current_statuses(void);
+
+
+/**************************************************************************/
+/* UI Registration Functions */
+/**************************************************************************/
+
+/**
+ * purple_account_ui_ops_get_type:
+ *
+ * Returns: The #GType for the #PurpleAccountUiOps boxed structure.
+ */
+GType purple_account_ui_ops_get_type(void);
+
+/**
+ * purple_accounts_set_ui_ops:
+ * @ops: The UI operations structure.
+ *
+ * Sets the UI operations structure to be used for accounts.
+ */
+void purple_accounts_set_ui_ops(PurpleAccountUiOps *ops);
+
+/**
+ * purple_accounts_get_ui_ops:
+ *
+ * Returns the UI operations structure used for accounts.
+ *
+ * Returns: The UI operations structure in use.
+ */
+PurpleAccountUiOps *purple_accounts_get_ui_ops(void);
+
+
+/**************************************************************************/
+/* Accounts Subsystem */
+/**************************************************************************/
+
+/**
+ * purple_accounts_get_handle:
+ *
+ * Returns the accounts subsystem handle.
+ *
+ * Returns: The accounts subsystem handle.
+ */
+void *purple_accounts_get_handle(void);
+
+/**
+ * purple_accounts_init:
+ *
+ * Initializes the accounts subsystem.
+ */
+void purple_accounts_init(void);
+
+/**
+ * purple_accounts_uninit:
+ *
+ * Uninitializes the accounts subsystem.
+ */
+void purple_accounts_uninit(void);
+
+/**
+ * purple_accounts_schedule_save:
+ *
+ * Schedules saving of accounts
+ */
+void purple_accounts_schedule_save(void);
+
+G_END_DECLS
+
+#endif /* _PURPLE_ACCOUNTS_H_ */
diff --git a/libpurple/blist.c b/libpurple/blist.c
deleted file mode 100644
index a34558df38..0000000000
--- a/libpurple/blist.c
+++ /dev/null
@@ -1,3271 +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
- *
- */
-#define _PURPLE_BLIST_C_
-
-#include "internal.h"
-#include "blist.h"
-#include "conversation.h"
-#include "dbus-maybe.h"
-#include "debug.h"
-#include "notify.h"
-#include "pounce.h"
-#include "prefs.h"
-#include "privacy.h"
-#include "prpl.h"
-#include "server.h"
-#include "signals.h"
-#include "util.h"
-#include "value.h"
-#include "xmlnode.h"
-
-static PurpleBlistUiOps *blist_ui_ops = NULL;
-
-static PurpleBuddyList *purplebuddylist = NULL;
-
-/**
- * A hash table used for efficient lookups of buddies by name.
- * PurpleAccount* => GHashTable*, with the inner hash table being
- * struct _purple_hbuddy => PurpleBuddy*
- */
-static GHashTable *buddies_cache = NULL;
-
-/**
- * A hash table used for efficient lookups of groups by name.
- * UTF-8 collate-key => PurpleGroup*.
- */
-static GHashTable *groups_cache = NULL;
-
-static guint save_timer = 0;
-static gboolean blist_loaded = FALSE;
-
-/*********************************************************************
- * Private utility functions *
- *********************************************************************/
-
-static PurpleBlistNode *purple_blist_get_last_sibling(PurpleBlistNode *node)
-{
- PurpleBlistNode *n = node;
- if (!n)
- return NULL;
- while (n->next)
- n = n->next;
- return n;
-}
-
-static PurpleBlistNode *purple_blist_get_last_child(PurpleBlistNode *node)
-{
- if (!node)
- return NULL;
- return purple_blist_get_last_sibling(node->child);
-}
-
-struct _list_account_buddies {
- GSList *list;
- PurpleAccount *account;
-};
-
-struct _purple_hbuddy {
- char *name;
- PurpleAccount *account;
- PurpleBlistNode *group;
-};
-
-/* This function must not use purple_normalize */
-static guint _purple_blist_hbuddy_hash(struct _purple_hbuddy *hb)
-{
- return g_str_hash(hb->name) ^ g_direct_hash(hb->group) ^ g_direct_hash(hb->account);
-}
-
-/* This function must not use purple_normalize */
-static guint _purple_blist_hbuddy_equal(struct _purple_hbuddy *hb1, struct _purple_hbuddy *hb2)
-{
- return (hb1->group == hb2->group &&
- hb1->account == hb2->account &&
- g_str_equal(hb1->name, hb2->name));
-}
-
-static void _purple_blist_hbuddy_free_key(struct _purple_hbuddy *hb)
-{
- g_free(hb->name);
- g_free(hb);
-}
-
-static void
-purple_blist_buddies_cache_add_account(PurpleAccount *account)
-{
- GHashTable *account_buddies = g_hash_table_new_full((GHashFunc)_purple_blist_hbuddy_hash,
- (GEqualFunc)_purple_blist_hbuddy_equal,
- (GDestroyNotify)_purple_blist_hbuddy_free_key, NULL);
- g_hash_table_insert(buddies_cache, account, account_buddies);
-}
-
-static void
-purple_blist_buddies_cache_remove_account(const PurpleAccount *account)
-{
- g_hash_table_remove(buddies_cache, account);
-}
-
-
-/*********************************************************************
- * Writing to disk *
- *********************************************************************/
-
-static void
-value_to_xmlnode(gpointer key, gpointer hvalue, gpointer user_data)
-{
- const char *name;
- PurpleValue *value;
- xmlnode *node, *child;
- char buf[21];
-
- name = (const char *)key;
- value = (PurpleValue *)hvalue;
- node = (xmlnode *)user_data;
-
- g_return_if_fail(value != NULL);
-
- child = xmlnode_new_child(node, "setting");
- xmlnode_set_attrib(child, "name", name);
-
- if (purple_value_get_type(value) == PURPLE_TYPE_INT) {
- xmlnode_set_attrib(child, "type", "int");
- g_snprintf(buf, sizeof(buf), "%d", purple_value_get_int(value));
- xmlnode_insert_data(child, buf, -1);
- }
- else if (purple_value_get_type(value) == PURPLE_TYPE_STRING) {
- xmlnode_set_attrib(child, "type", "string");
- xmlnode_insert_data(child, purple_value_get_string(value), -1);
- }
- else if (purple_value_get_type(value) == PURPLE_TYPE_BOOLEAN) {
- xmlnode_set_attrib(child, "type", "bool");
- g_snprintf(buf, sizeof(buf), "%d", purple_value_get_boolean(value));
- xmlnode_insert_data(child, buf, -1);
- }
-}
-
-static void
-chat_component_to_xmlnode(gpointer key, gpointer value, gpointer user_data)
-{
- const char *name;
- const char *data;
- xmlnode *node, *child;
-
- name = (const char *)key;
- data = (const char *)value;
- node = (xmlnode *)user_data;
-
- g_return_if_fail(data != NULL);
-
- child = xmlnode_new_child(node, "component");
- xmlnode_set_attrib(child, "name", name);
- xmlnode_insert_data(child, data, -1);
-}
-
-static xmlnode *
-buddy_to_xmlnode(PurpleBlistNode *bnode)
-{
- xmlnode *node, *child;
- PurpleBuddy *buddy;
-
- buddy = (PurpleBuddy *)bnode;
-
- node = xmlnode_new("buddy");
- xmlnode_set_attrib(node, "account", purple_account_get_username(buddy->account));
- xmlnode_set_attrib(node, "proto", purple_account_get_protocol_id(buddy->account));
-
- child = xmlnode_new_child(node, "name");
- xmlnode_insert_data(child, buddy->name, -1);
-
- if (buddy->alias != NULL)
- {
- child = xmlnode_new_child(node, "alias");
- xmlnode_insert_data(child, buddy->alias, -1);
- }
-
- /* Write buddy settings */
- g_hash_table_foreach(buddy->node.settings, value_to_xmlnode, node);
-
- return node;
-}
-
-static xmlnode *
-contact_to_xmlnode(PurpleBlistNode *cnode)
-{
- xmlnode *node, *child;
- PurpleContact *contact;
- PurpleBlistNode *bnode;
-
- contact = (PurpleContact *)cnode;
-
- node = xmlnode_new("contact");
-
- if (contact->alias != NULL)
- {
- xmlnode_set_attrib(node, "alias", contact->alias);
- }
-
- /* Write buddies */
- for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
- {
- if (!PURPLE_BLIST_NODE_SHOULD_SAVE(bnode))
- continue;
- if (PURPLE_BLIST_NODE_IS_BUDDY(bnode))
- {
- child = buddy_to_xmlnode(bnode);
- xmlnode_insert_child(node, child);
- }
- }
-
- /* Write contact settings */
- g_hash_table_foreach(cnode->settings, value_to_xmlnode, node);
-
- return node;
-}
-
-static xmlnode *
-chat_to_xmlnode(PurpleBlistNode *cnode)
-{
- xmlnode *node, *child;
- PurpleChat *chat;
-
- chat = (PurpleChat *)cnode;
-
- node = xmlnode_new("chat");
- xmlnode_set_attrib(node, "proto", purple_account_get_protocol_id(chat->account));
- xmlnode_set_attrib(node, "account", purple_account_get_username(chat->account));
-
- if (chat->alias != NULL)
- {
- child = xmlnode_new_child(node, "alias");
- xmlnode_insert_data(child, chat->alias, -1);
- }
-
- /* Write chat components */
- g_hash_table_foreach(chat->components, chat_component_to_xmlnode, node);
-
- /* Write chat settings */
- g_hash_table_foreach(chat->node.settings, value_to_xmlnode, node);
-
- return node;
-}
-
-static xmlnode *
-group_to_xmlnode(PurpleBlistNode *gnode)
-{
- xmlnode *node, *child;
- PurpleGroup *group;
- PurpleBlistNode *cnode;
-
- group = (PurpleGroup *)gnode;
-
- node = xmlnode_new("group");
- if (g_strcmp0(group->name, _("Buddies")) != 0)
- xmlnode_set_attrib(node, "name", group->name);
-
- /* Write settings */
- g_hash_table_foreach(group->node.settings, value_to_xmlnode, node);
-
- /* Write contacts and chats */
- for (cnode = gnode->child; cnode != NULL; cnode = cnode->next)
- {
- if (!PURPLE_BLIST_NODE_SHOULD_SAVE(cnode))
- continue;
- if (PURPLE_BLIST_NODE_IS_CONTACT(cnode))
- {
- child = contact_to_xmlnode(cnode);
- xmlnode_insert_child(node, child);
- }
- else if (PURPLE_BLIST_NODE_IS_CHAT(cnode))
- {
- child = chat_to_xmlnode(cnode);
- xmlnode_insert_child(node, child);
- }
- }
-
- return node;
-}
-
-static xmlnode *
-accountprivacy_to_xmlnode(PurpleAccount *account)
-{
- xmlnode *node, *child;
- GSList *cur;
- char buf[10];
-
- node = xmlnode_new("account");
- xmlnode_set_attrib(node, "proto", purple_account_get_protocol_id(account));
- xmlnode_set_attrib(node, "name", purple_account_get_username(account));
- g_snprintf(buf, sizeof(buf), "%d", account->perm_deny);
- xmlnode_set_attrib(node, "mode", buf);
-
- for (cur = account->permit; cur; cur = cur->next)
- {
- child = xmlnode_new_child(node, "permit");
- xmlnode_insert_data(child, cur->data, -1);
- }
-
- for (cur = account->deny; cur; cur = cur->next)
- {
- child = xmlnode_new_child(node, "block");
- xmlnode_insert_data(child, cur->data, -1);
- }
-
- return node;
-}
-
-static xmlnode *
-blist_to_xmlnode(void)
-{
- xmlnode *node, *child, *grandchild;
- PurpleBlistNode *gnode;
- GList *cur;
-
- node = xmlnode_new("purple");
- xmlnode_set_attrib(node, "version", "1.0");
-
- /* Write groups */
- child = xmlnode_new_child(node, "blist");
- for (gnode = purplebuddylist->root; gnode != NULL; gnode = gnode->next)
- {
- if (!PURPLE_BLIST_NODE_SHOULD_SAVE(gnode))
- continue;
- if (PURPLE_BLIST_NODE_IS_GROUP(gnode))
- {
- grandchild = group_to_xmlnode(gnode);
- xmlnode_insert_child(child, grandchild);
- }
- }
-
- /* Write privacy settings */
- child = xmlnode_new_child(node, "privacy");
- for (cur = purple_accounts_get_all(); cur != NULL; cur = cur->next)
- {
- grandchild = accountprivacy_to_xmlnode(cur->data);
- xmlnode_insert_child(child, grandchild);
- }
-
- return node;
-}
-
-static void
-purple_blist_sync(void)
-{
- xmlnode *node;
- char *data;
-
- if (!blist_loaded)
- {
- purple_debug_error("blist", "Attempted to save buddy list before it "
- "was read!\n");
- return;
- }
-
- node = blist_to_xmlnode();
- data = xmlnode_to_formatted_str(node, NULL);
- purple_util_write_data_to_file("blist.xml", data, -1);
- g_free(data);
- xmlnode_free(node);
-}
-
-static gboolean
-save_cb(gpointer data)
-{
- purple_blist_sync();
- save_timer = 0;
- return FALSE;
-}
-
-static void
-_purple_blist_schedule_save()
-{
- if (save_timer == 0)
- save_timer = purple_timeout_add_seconds(5, save_cb, NULL);
-}
-
-static void
-purple_blist_save_account(PurpleAccount *account)
-{
-#if 1
- _purple_blist_schedule_save();
-#else
- if (account != NULL) {
- /* Save the buddies and privacy data for this account */
- } else {
- /* Save all buddies and privacy data */
- }
-#endif
-}
-
-static void
-purple_blist_save_node(PurpleBlistNode *node)
-{
- _purple_blist_schedule_save();
-}
-
-void purple_blist_schedule_save()
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-
- /* Save everything */
- if (ops && ops->save_account)
- ops->save_account(NULL);
-}
-
-
-/*********************************************************************
- * Reading from disk *
- *********************************************************************/
-
-static void
-parse_setting(PurpleBlistNode *node, xmlnode *setting)
-{
- const char *name = xmlnode_get_attrib(setting, "name");
- const char *type = xmlnode_get_attrib(setting, "type");
- char *value = xmlnode_get_data(setting);
-
- if (!value)
- return;
-
- if (!type || purple_strequal(type, "string"))
- purple_blist_node_set_string(node, name, value);
- else if (purple_strequal(type, "bool"))
- purple_blist_node_set_bool(node, name, atoi(value));
- else if (purple_strequal(type, "int"))
- purple_blist_node_set_int(node, name, atoi(value));
-
- g_free(value);
-}
-
-static void
-parse_buddy(PurpleGroup *group, PurpleContact *contact, xmlnode *bnode)
-{
- PurpleAccount *account;
- PurpleBuddy *buddy;
- char *name = NULL, *alias = NULL;
- const char *acct_name, *proto, *protocol;
- xmlnode *x;
-
- acct_name = xmlnode_get_attrib(bnode, "account");
- protocol = xmlnode_get_attrib(bnode, "protocol");
- protocol = _purple_oscar_convert(acct_name, protocol); /* XXX: Remove */
- proto = xmlnode_get_attrib(bnode, "proto");
- proto = _purple_oscar_convert(acct_name, proto); /* XXX: Remove */
-
- if (!acct_name || (!proto && !protocol))
- return;
-
- account = purple_accounts_find(acct_name, proto ? proto : protocol);
-
- if (!account)
- return;
-
- if ((x = xmlnode_get_child(bnode, "name")))
- name = xmlnode_get_data(x);
-
- if (!name)
- return;
-
- if ((x = xmlnode_get_child(bnode, "alias")))
- alias = xmlnode_get_data(x);
-
- buddy = purple_buddy_new(account, name, alias);
- purple_blist_add_buddy(buddy, contact, group,
- purple_blist_get_last_child((PurpleBlistNode*)contact));
-
- for (x = xmlnode_get_child(bnode, "setting"); x; x = xmlnode_get_next_twin(x)) {
- parse_setting((PurpleBlistNode*)buddy, x);
- }
-
- g_free(name);
- g_free(alias);
-}
-
-static void
-parse_contact(PurpleGroup *group, xmlnode *cnode)
-{
- PurpleContact *contact = purple_contact_new();
- xmlnode *x;
- const char *alias;
-
- purple_blist_add_contact(contact, group,
- purple_blist_get_last_child((PurpleBlistNode*)group));
-
- if ((alias = xmlnode_get_attrib(cnode, "alias"))) {
- purple_blist_alias_contact(contact, alias);
- }
-
- for (x = cnode->child; x; x = x->next) {
- if (x->type != XMLNODE_TYPE_TAG)
- continue;
- if (purple_strequal(x->name, "buddy"))
- parse_buddy(group, contact, x);
- else if (purple_strequal(x->name, "setting"))
- parse_setting((PurpleBlistNode*)contact, x);
- }
-
- /* if the contact is empty, don't keep it around. it causes problems */
- if (!((PurpleBlistNode*)contact)->child)
- purple_blist_remove_contact(contact);
-}
-
-static void
-parse_chat(PurpleGroup *group, xmlnode *cnode)
-{
- PurpleChat *chat;
- PurpleAccount *account;
- const char *acct_name, *proto, *protocol;
- xmlnode *x;
- char *alias = NULL;
- GHashTable *components;
-
- acct_name = xmlnode_get_attrib(cnode, "account");
- protocol = xmlnode_get_attrib(cnode, "protocol");
- proto = xmlnode_get_attrib(cnode, "proto");
-
- if (!acct_name || (!proto && !protocol))
- return;
-
- account = purple_accounts_find(acct_name, proto ? proto : protocol);
-
- if (!account)
- return;
-
- if ((x = xmlnode_get_child(cnode, "alias")))
- alias = xmlnode_get_data(x);
-
- components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
-
- for (x = xmlnode_get_child(cnode, "component"); x; x = xmlnode_get_next_twin(x)) {
- const char *name;
- char *value;
-
- name = xmlnode_get_attrib(x, "name");
- value = xmlnode_get_data(x);
- g_hash_table_replace(components, g_strdup(name), value);
- }
-
- chat = purple_chat_new(account, alias, components);
- purple_blist_add_chat(chat, group,
- purple_blist_get_last_child((PurpleBlistNode*)group));
-
- for (x = xmlnode_get_child(cnode, "setting"); x; x = xmlnode_get_next_twin(x)) {
- parse_setting((PurpleBlistNode*)chat, x);
- }
-
- g_free(alias);
-}
-
-static void
-parse_group(xmlnode *groupnode)
-{
- const char *name = xmlnode_get_attrib(groupnode, "name");
- PurpleGroup *group;
- xmlnode *cnode;
-
- if (!name)
- name = _("Buddies");
-
- group = purple_group_new(name);
- purple_blist_add_group(group,
- purple_blist_get_last_sibling(purplebuddylist->root));
-
- for (cnode = groupnode->child; cnode; cnode = cnode->next) {
- if (cnode->type != XMLNODE_TYPE_TAG)
- continue;
- if (purple_strequal(cnode->name, "setting"))
- parse_setting((PurpleBlistNode*)group, cnode);
- else if (purple_strequal(cnode->name, "contact") ||
- purple_strequal(cnode->name, "person"))
- parse_contact(group, cnode);
- else if (purple_strequal(cnode->name, "chat"))
- parse_chat(group, cnode);
- }
-}
-
-/* TODO: Make static and rename to load_blist */
-void
-purple_blist_load()
-{
- xmlnode *purple, *blist, *privacy;
-
- blist_loaded = TRUE;
-
- purple = purple_util_read_xml_from_file("blist.xml", _("buddy list"));
-
- if (purple == NULL)
- return;
-
- blist = xmlnode_get_child(purple, "blist");
- if (blist) {
- xmlnode *groupnode;
- for (groupnode = xmlnode_get_child(blist, "group"); groupnode != NULL;
- groupnode = xmlnode_get_next_twin(groupnode)) {
- parse_group(groupnode);
- }
- }
-
- privacy = xmlnode_get_child(purple, "privacy");
- if (privacy) {
- xmlnode *anode;
- for (anode = privacy->child; anode; anode = anode->next) {
- xmlnode *x;
- PurpleAccount *account;
- int imode;
- const char *acct_name, *proto, *mode, *protocol;
-
- acct_name = xmlnode_get_attrib(anode, "name");
- protocol = xmlnode_get_attrib(anode, "protocol");
- proto = xmlnode_get_attrib(anode, "proto");
- mode = xmlnode_get_attrib(anode, "mode");
-
- if (!acct_name || (!proto && !protocol) || !mode)
- continue;
-
- account = purple_accounts_find(acct_name, proto ? proto : protocol);
-
- if (!account)
- continue;
-
- imode = atoi(mode);
- account->perm_deny = (imode != 0 ? imode : PURPLE_PRIVACY_ALLOW_ALL);
-
- for (x = anode->child; x; x = x->next) {
- char *name;
- if (x->type != XMLNODE_TYPE_TAG)
- continue;
-
- if (purple_strequal(x->name, "permit")) {
- name = xmlnode_get_data(x);
- purple_privacy_permit_add(account, name, TRUE);
- g_free(name);
- } else if (purple_strequal(x->name, "block")) {
- name = xmlnode_get_data(x);
- purple_privacy_deny_add(account, name, TRUE);
- g_free(name);
- }
- }
- }
- }
-
- xmlnode_free(purple);
-
- /* This tells the buddy icon code to do its thing. */
- _purple_buddy_icons_blist_loaded_cb();
-}
-
-
-/*********************************************************************
- * Stuff *
- *********************************************************************/
-
-static void
-purple_contact_compute_priority_buddy(PurpleContact *contact)
-{
- PurpleBlistNode *bnode;
- PurpleBuddy *new_priority = NULL;
-
- g_return_if_fail(contact != NULL);
-
- contact->priority = NULL;
- for (bnode = ((PurpleBlistNode*)contact)->child;
- bnode != NULL;
- bnode = bnode->next)
- {
- PurpleBuddy *buddy;
-
- if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
- continue;
-
- buddy = (PurpleBuddy*)bnode;
- if (new_priority == NULL)
- {
- new_priority = buddy;
- continue;
- }
-
- if (purple_account_is_connected(buddy->account))
- {
- int cmp = 1;
- if (purple_account_is_connected(new_priority->account))
- cmp = purple_presence_compare(purple_buddy_get_presence(new_priority),
- purple_buddy_get_presence(buddy));
-
- if (cmp > 0 || (cmp == 0 &&
- purple_prefs_get_bool("/purple/contact/last_match")))
- {
- new_priority = buddy;
- }
- }
- }
-
- contact->priority = new_priority;
- contact->priority_valid = TRUE;
-}
-
-
-/*****************************************************************************
- * Public API functions *
- *****************************************************************************/
-
-PurpleBuddyList *purple_blist_new()
-{
- PurpleBlistUiOps *ui_ops;
- GList *account;
- PurpleBuddyList *gbl = g_new0(PurpleBuddyList, 1);
- PURPLE_DBUS_REGISTER_POINTER(gbl, PurpleBuddyList);
-
- ui_ops = purple_blist_get_ui_ops();
-
- gbl->buddies = g_hash_table_new_full((GHashFunc)_purple_blist_hbuddy_hash,
- (GEqualFunc)_purple_blist_hbuddy_equal,
- (GDestroyNotify)_purple_blist_hbuddy_free_key, NULL);
-
- buddies_cache = g_hash_table_new_full(g_direct_hash, g_direct_equal,
- NULL, (GDestroyNotify)g_hash_table_destroy);
-
- groups_cache = g_hash_table_new_full((GHashFunc)g_str_hash,
- (GEqualFunc)g_str_equal,
- (GDestroyNotify)g_free, NULL);
-
- for (account = purple_accounts_get_all(); account != NULL; account = account->next)
- {
- purple_blist_buddies_cache_add_account(account->data);
- }
-
- if (ui_ops != NULL && ui_ops->new_list != NULL)
- ui_ops->new_list(gbl);
-
- return gbl;
-}
-
-void
-purple_set_blist(PurpleBuddyList *list)
-{
- purplebuddylist = list;
-}
-
-PurpleBuddyList *
-purple_get_blist()
-{
- return purplebuddylist;
-}
-
-PurpleBlistNode *
-purple_blist_get_root()
-{
- return purplebuddylist ? purplebuddylist->root : NULL;
-}
-
-static void
-append_buddy(gpointer key, gpointer value, gpointer user_data)
-{
- GSList **list = user_data;
- *list = g_slist_prepend(*list, value);
-}
-
-GSList *
-purple_blist_get_buddies()
-{
- GSList *buddies = NULL;
-
- if (!purplebuddylist)
- return NULL;
-
- g_hash_table_foreach(purplebuddylist->buddies, append_buddy, &buddies);
- 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();
-
- if (ops && ops->show)
- ops->show(purplebuddylist);
-}
-
-void purple_blist_destroy()
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-
- purple_debug(PURPLE_DEBUG_INFO, "blist", "Destroying\n");
-
- if (ops && ops->destroy)
- ops->destroy(purplebuddylist);
-}
-
-void purple_blist_set_visible(gboolean show)
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-
- if (ops && ops->set_visible)
- ops->set_visible(purplebuddylist, show);
-}
-
-static PurpleBlistNode *get_next_node(PurpleBlistNode *node, gboolean godeep)
-{
- if (node == NULL)
- return NULL;
-
- if (godeep && node->child)
- return node->child;
-
- if (node->next)
- return node->next;
-
- return get_next_node(node->parent, FALSE);
-}
-
-PurpleBlistNode *purple_blist_node_next(PurpleBlistNode *node, gboolean offline)
-{
- PurpleBlistNode *ret = node;
-
- if (offline)
- return get_next_node(ret, TRUE);
- do
- {
- ret = get_next_node(ret, TRUE);
- } while (ret && PURPLE_BLIST_NODE_IS_BUDDY(ret) &&
- !purple_account_is_connected(purple_buddy_get_account((PurpleBuddy *)ret)));
-
- return ret;
-}
-
-PurpleBlistNode *purple_blist_node_get_parent(PurpleBlistNode *node)
-{
- return node ? node->parent : NULL;
-}
-
-PurpleBlistNode *purple_blist_node_get_first_child(PurpleBlistNode *node)
-{
- return node ? node->child : NULL;
-}
-
-PurpleBlistNode *purple_blist_node_get_sibling_next(PurpleBlistNode *node)
-{
- return node? node->next : NULL;
-}
-
-PurpleBlistNode *purple_blist_node_get_sibling_prev(PurpleBlistNode *node)
-{
- return node? node->prev : NULL;
-}
-
-void *
-purple_blist_node_get_ui_data(const PurpleBlistNode *node)
-{
- g_return_val_if_fail(node, NULL);
-
- return node->ui_data;
-}
-
-void
-purple_blist_node_set_ui_data(PurpleBlistNode *node, void *ui_data) {
- g_return_if_fail(node);
-
- node->ui_data = ui_data;
-}
-
-void
-purple_blist_update_buddy_status(PurpleBuddy *buddy, PurpleStatus *old_status)
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
- PurplePresence *presence;
- PurpleStatus *status;
- PurpleBlistNode *cnode;
-
- g_return_if_fail(buddy != NULL);
-
- presence = purple_buddy_get_presence(buddy);
- status = purple_presence_get_active_status(presence);
-
- purple_debug_info("blist", "Updating buddy status for %s (%s)\n",
- buddy->name, purple_account_get_protocol_name(buddy->account));
-
- if (purple_status_is_online(status) &&
- !purple_status_is_online(old_status)) {
-
- purple_signal_emit(purple_blist_get_handle(), "buddy-signed-on", buddy);
-
- cnode = buddy->node.parent;
- if (++(PURPLE_CONTACT(cnode)->online) == 1)
- PURPLE_GROUP(cnode->parent)->online++;
- } else if (!purple_status_is_online(status) &&
- purple_status_is_online(old_status)) {
-
- purple_blist_node_set_int(&buddy->node, "last_seen", time(NULL));
- purple_signal_emit(purple_blist_get_handle(), "buddy-signed-off", buddy);
-
- cnode = buddy->node.parent;
- if (--(PURPLE_CONTACT(cnode)->online) == 0)
- PURPLE_GROUP(cnode->parent)->online--;
- } else {
- purple_signal_emit(purple_blist_get_handle(),
- "buddy-status-changed", buddy, old_status,
- status);
- }
-
- /*
- * This function used to only call the following two functions if one of
- * the above signals had been triggered, but that's not good, because
- * if someone's away message changes and they don't go from away to back
- * to away then no signal is triggered.
- *
- * It's a safe assumption that SOMETHING called this function. PROBABLY
- * because something, somewhere changed. Calling the stuff below
- * certainly won't hurt anything. Unless you're on a K6-2 300.
- */
- purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy));
- if (ops && ops->update)
- ops->update(purplebuddylist, (PurpleBlistNode *)buddy);
-}
-
-void
-purple_blist_update_node_icon(PurpleBlistNode *node)
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-
- g_return_if_fail(node != NULL);
-
- if (ops && ops->update)
- ops->update(purplebuddylist, node);
-}
-
-void
-purple_blist_update_buddy_icon(PurpleBuddy *buddy)
-{
- purple_blist_update_node_icon((PurpleBlistNode *)buddy);
-}
-
-/*
- * TODO: Maybe remove the call to this from server.c and call it
- * from oscar.c and toc.c instead?
- */
-void purple_blist_rename_buddy(PurpleBuddy *buddy, const char *name)
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
- struct _purple_hbuddy *hb, *hb2;
- GHashTable *account_buddies;
-
- g_return_if_fail(buddy != NULL);
-
- hb = g_new(struct _purple_hbuddy, 1);
- hb->name = (gchar *)purple_normalize(buddy->account, buddy->name);
- hb->account = buddy->account;
- hb->group = ((PurpleBlistNode *)buddy)->parent->parent;
- g_hash_table_remove(purplebuddylist->buddies, hb);
-
- account_buddies = g_hash_table_lookup(buddies_cache, buddy->account);
- g_hash_table_remove(account_buddies, hb);
-
- hb->name = g_strdup(purple_normalize(buddy->account, name));
- g_hash_table_replace(purplebuddylist->buddies, hb, buddy);
-
- hb2 = g_new(struct _purple_hbuddy, 1);
- hb2->name = g_strdup(hb->name);
- hb2->account = buddy->account;
- hb2->group = ((PurpleBlistNode *)buddy)->parent->parent;
-
- g_hash_table_replace(account_buddies, hb2, buddy);
-
- g_free(buddy->name);
- buddy->name = g_strdup(name);
-
- if (ops && ops->save_node)
- ops->save_node((PurpleBlistNode *) buddy);
-
- if (ops && ops->update)
- ops->update(purplebuddylist, (PurpleBlistNode *)buddy);
-}
-
-static gboolean
-purple_strings_are_different(const char *one, const char *two)
-{
- return !((one && two && g_utf8_collate(one, two) == 0) ||
- ((one == NULL || *one == '\0') && (two == NULL || *two == '\0')));
-}
-
-void purple_blist_alias_contact(PurpleContact *contact, const char *alias)
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
- PurpleConversation *conv;
- PurpleBlistNode *bnode;
- char *old_alias;
- char *new_alias = NULL;
-
- g_return_if_fail(contact != NULL);
-
- if ((alias != NULL) && (*alias != '\0'))
- new_alias = purple_utf8_strip_unprintables(alias);
-
- if (!purple_strings_are_different(contact->alias, new_alias)) {
- g_free(new_alias);
- return;
- }
-
- old_alias = contact->alias;
-
- if ((new_alias != NULL) && (*new_alias != '\0'))
- contact->alias = new_alias;
- else {
- contact->alias = NULL;
- g_free(new_alias); /* could be "\0" */
- }
-
- if (ops && ops->save_node)
- ops->save_node((PurpleBlistNode*) contact);
-
- if (ops && ops->update)
- ops->update(purplebuddylist, (PurpleBlistNode *)contact);
-
- for(bnode = ((PurpleBlistNode *)contact)->child; bnode != NULL; bnode = bnode->next)
- {
- PurpleBuddy *buddy = (PurpleBuddy *)bnode;
-
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy->name,
- buddy->account);
- if (conv)
- purple_conversation_autoset_title(conv);
- }
-
- purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
- contact, old_alias);
- g_free(old_alias);
-}
-
-void purple_blist_alias_chat(PurpleChat *chat, const char *alias)
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
- char *old_alias;
- char *new_alias = NULL;
-
- g_return_if_fail(chat != NULL);
-
- if ((alias != NULL) && (*alias != '\0'))
- new_alias = purple_utf8_strip_unprintables(alias);
-
- if (!purple_strings_are_different(chat->alias, new_alias)) {
- g_free(new_alias);
- return;
- }
-
- old_alias = chat->alias;
-
- if ((new_alias != NULL) && (*new_alias != '\0'))
- chat->alias = new_alias;
- else {
- chat->alias = NULL;
- g_free(new_alias); /* could be "\0" */
- }
-
- if (ops && ops->save_node)
- ops->save_node((PurpleBlistNode*) chat);
-
- if (ops && ops->update)
- ops->update(purplebuddylist, (PurpleBlistNode *)chat);
-
- purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
- chat, old_alias);
- g_free(old_alias);
-}
-
-void purple_blist_alias_buddy(PurpleBuddy *buddy, const char *alias)
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
- PurpleConversation *conv;
- char *old_alias;
- char *new_alias = NULL;
-
- g_return_if_fail(buddy != NULL);
-
- if ((alias != NULL) && (*alias != '\0'))
- new_alias = purple_utf8_strip_unprintables(alias);
-
- if (!purple_strings_are_different(buddy->alias, new_alias)) {
- g_free(new_alias);
- return;
- }
-
- old_alias = buddy->alias;
-
- if ((new_alias != NULL) && (*new_alias != '\0'))
- buddy->alias = new_alias;
- else {
- buddy->alias = NULL;
- g_free(new_alias); /* could be "\0" */
- }
-
- if (ops && ops->save_node)
- ops->save_node((PurpleBlistNode*) buddy);
-
- if (ops && ops->update)
- ops->update(purplebuddylist, (PurpleBlistNode *)buddy);
-
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy->name,
- buddy->account);
- if (conv)
- purple_conversation_autoset_title(conv);
-
- purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
- buddy, old_alias);
- g_free(old_alias);
-}
-
-void purple_blist_server_alias_buddy(PurpleBuddy *buddy, const char *alias)
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
- PurpleConversation *conv;
- char *old_alias;
- char *new_alias = NULL;
-
- g_return_if_fail(buddy != NULL);
-
- if ((alias != NULL) && (*alias != '\0') && g_utf8_validate(alias, -1, NULL))
- new_alias = purple_utf8_strip_unprintables(alias);
-
- if (!purple_strings_are_different(buddy->server_alias, new_alias)) {
- g_free(new_alias);
- return;
- }
-
- old_alias = buddy->server_alias;
-
- if ((new_alias != NULL) && (*new_alias != '\0'))
- buddy->server_alias = new_alias;
- else {
- buddy->server_alias = NULL;
- g_free(new_alias); /* could be "\0"; */
- }
-
- if (ops && ops->save_node)
- ops->save_node((PurpleBlistNode*) buddy);
-
- if (ops && ops->update)
- ops->update(purplebuddylist, (PurpleBlistNode *)buddy);
-
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy->name,
- buddy->account);
- if (conv)
- purple_conversation_autoset_title(conv);
-
- purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
- buddy, old_alias);
- g_free(old_alias);
-}
-
-/*
- * TODO: If merging, prompt the user if they want to merge.
- */
-void purple_blist_rename_group(PurpleGroup *source, const char *name)
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
- PurpleGroup *dest;
- gchar *old_name;
- gchar *new_name;
- GList *moved_buddies = NULL;
- GSList *accts;
-
- g_return_if_fail(source != NULL);
- g_return_if_fail(name != NULL);
-
- new_name = purple_utf8_strip_unprintables(name);
-
- if (*new_name == '\0' || purple_strequal(new_name, source->name)) {
- g_free(new_name);
- return;
- }
-
- dest = purple_find_group(new_name);
- if (dest != NULL && purple_utf8_strcasecmp(source->name, dest->name) != 0) {
- /* We're merging two groups */
- PurpleBlistNode *prev, *child, *next;
-
- prev = purple_blist_get_last_child((PurpleBlistNode*)dest);
- child = ((PurpleBlistNode*)source)->child;
-
- /*
- * TODO: This seems like a dumb way to do this... why not just
- * append all children from the old group to the end of the new
- * one? PRPLs might be expecting to receive an add_buddy() for
- * each moved buddy...
- */
- while (child)
- {
- next = child->next;
- if (PURPLE_BLIST_NODE_IS_CONTACT(child)) {
- PurpleBlistNode *bnode;
- purple_blist_add_contact((PurpleContact *)child, dest, prev);
- for (bnode = child->child; bnode != NULL; bnode = bnode->next) {
- purple_blist_add_buddy((PurpleBuddy *)bnode, (PurpleContact *)child,
- NULL, bnode->prev);
- moved_buddies = g_list_append(moved_buddies, bnode);
- }
- prev = child;
- } else if (PURPLE_BLIST_NODE_IS_CHAT(child)) {
- purple_blist_add_chat((PurpleChat *)child, dest, prev);
- prev = child;
- } else {
- purple_debug(PURPLE_DEBUG_ERROR, "blist",
- "Unknown child type in group %s\n", source->name);
- }
- child = next;
- }
-
- /* Make a copy of the old group name and then delete the old group */
- old_name = g_strdup(source->name);
- purple_blist_remove_group(source);
- source = dest;
- g_free(new_name);
- } else {
- /* A simple rename */
- PurpleBlistNode *cnode, *bnode;
- gchar* key;
-
- /* Build a GList of all buddies in this group */
- for (cnode = ((PurpleBlistNode *)source)->child; cnode != NULL; cnode = cnode->next) {
- if (PURPLE_BLIST_NODE_IS_CONTACT(cnode))
- for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
- moved_buddies = g_list_append(moved_buddies, bnode);
- }
-
- old_name = source->name;
- source->name = new_name;
-
- key = g_utf8_collate_key(old_name, -1);
- g_hash_table_remove(groups_cache, key);
- g_free(key);
-
- key = g_utf8_collate_key(new_name, -1);
- g_hash_table_insert(groups_cache, key, source);
- }
-
- /* Save our changes */
- if (ops && ops->save_node)
- ops->save_node((PurpleBlistNode*) source);
-
- /* Update the UI */
- if (ops && ops->update)
- ops->update(purplebuddylist, (PurpleBlistNode*)source);
-
- /* Notify all PRPLs */
- /* TODO: Is this condition needed? Seems like it would always be TRUE */
- if(old_name && !purple_strequal(source->name, old_name)) {
- for (accts = purple_group_get_accounts(source); accts; accts = g_slist_remove(accts, accts->data)) {
- PurpleAccount *account = accts->data;
- PurpleConnection *gc = NULL;
- PurplePlugin *prpl = NULL;
- PurplePluginProtocolInfo *prpl_info = NULL;
- GList *l = NULL, *buddies = NULL;
-
- gc = purple_account_get_connection(account);
-
- if(gc)
- prpl = purple_connection_get_prpl(gc);
-
- if(gc && prpl)
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-
- if(!prpl_info)
- continue;
-
- for(l = moved_buddies; l; l = l->next) {
- PurpleBuddy *buddy = (PurpleBuddy *)l->data;
-
- if(buddy && buddy->account == account)
- buddies = g_list_append(buddies, (PurpleBlistNode *)buddy);
- }
-
- if(prpl_info->rename_group) {
- prpl_info->rename_group(gc, old_name, source, buddies);
- } else {
- GList *cur, *groups = NULL;
-
- /* Make a list of what the groups each buddy is in */
- for(cur = buddies; cur; cur = cur->next) {
- PurpleBlistNode *node = (PurpleBlistNode *)cur->data;
- groups = g_list_prepend(groups, node->parent->parent);
- }
-
- purple_account_remove_buddies(account, buddies, groups);
- g_list_free(groups);
- purple_account_add_buddies(account, buddies);
- }
-
- g_list_free(buddies);
- }
- }
- g_list_free(moved_buddies);
- g_free(old_name);
-}
-
-static void purple_blist_node_initialize_settings(PurpleBlistNode *node);
-
-PurpleChat *purple_chat_new(PurpleAccount *account, const char *alias, GHashTable *components)
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
- PurpleChat *chat;
-
- g_return_val_if_fail(account != NULL, NULL);
- g_return_val_if_fail(components != NULL, NULL);
-
- chat = g_new0(PurpleChat, 1);
- chat->account = account;
- if ((alias != NULL) && (*alias != '\0'))
- chat->alias = purple_utf8_strip_unprintables(alias);
- chat->components = components;
- purple_blist_node_initialize_settings((PurpleBlistNode *)chat);
- ((PurpleBlistNode *)chat)->type = PURPLE_BLIST_CHAT_NODE;
-
- if (ops != NULL && ops->new_node != NULL)
- ops->new_node((PurpleBlistNode *)chat);
-
- PURPLE_DBUS_REGISTER_POINTER(chat, PurpleChat);
- return chat;
-}
-
-void
-purple_chat_destroy(PurpleChat *chat)
-{
- g_hash_table_destroy(chat->components);
- g_hash_table_destroy(chat->node.settings);
- g_free(chat->alias);
- PURPLE_DBUS_UNREGISTER_POINTER(chat);
- g_free(chat);
-}
-
-PurpleBuddy *purple_buddy_new(PurpleAccount *account, const char *name, const char *alias)
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
- PurpleBuddy *buddy;
-
- g_return_val_if_fail(account != NULL, NULL);
- g_return_val_if_fail(name != NULL, NULL);
-
- buddy = g_new0(PurpleBuddy, 1);
- buddy->account = account;
- buddy->name = purple_utf8_strip_unprintables(name);
- buddy->alias = purple_utf8_strip_unprintables(alias);
- buddy->presence = purple_presence_new_for_buddy(buddy);
- ((PurpleBlistNode *)buddy)->type = PURPLE_BLIST_BUDDY_NODE;
-
- purple_presence_set_status_active(buddy->presence, "offline", TRUE);
-
- purple_blist_node_initialize_settings((PurpleBlistNode *)buddy);
-
- if (ops && ops->new_node)
- ops->new_node((PurpleBlistNode *)buddy);
-
- PURPLE_DBUS_REGISTER_POINTER(buddy, PurpleBuddy);
- return buddy;
-}
-
-void
-purple_buddy_destroy(PurpleBuddy *buddy)
-{
- PurplePlugin *prpl;
- PurplePluginProtocolInfo *prpl_info;
-
- /*
- * Tell the owner PRPL that we're about to free the buddy so it
- * can free proto_data
- */
- prpl = purple_find_prpl(purple_account_get_protocol_id(buddy->account));
- if (prpl) {
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
- if (prpl_info && prpl_info->buddy_free)
- prpl_info->buddy_free(buddy);
- }
-
- /* Delete the node */
- purple_buddy_icon_unref(buddy->icon);
- g_hash_table_destroy(buddy->node.settings);
- purple_presence_destroy(buddy->presence);
- g_free(buddy->name);
- g_free(buddy->alias);
- g_free(buddy->server_alias);
-
- PURPLE_DBUS_UNREGISTER_POINTER(buddy);
- g_free(buddy);
-
- /* FIXME: Once PurpleBuddy is a GObject, timeout callbacks can
- * g_object_ref() it when connecting the callback and
- * g_object_unref() it in the handler. That way, it won't
- * get freed while the timeout is pending and this line can
- * be removed. */
- while (g_source_remove_by_user_data((gpointer *)buddy));
-}
-
-void
-purple_buddy_set_icon(PurpleBuddy *buddy, PurpleBuddyIcon *icon)
-{
- g_return_if_fail(buddy != NULL);
-
- if (buddy->icon != icon)
- {
- purple_buddy_icon_unref(buddy->icon);
- buddy->icon = (icon != NULL ? purple_buddy_icon_ref(icon) : NULL);
- }
-
- purple_signal_emit(purple_blist_get_handle(), "buddy-icon-changed", buddy);
-
- purple_blist_update_node_icon((PurpleBlistNode*)buddy);
-}
-
-PurpleAccount *
-purple_buddy_get_account(const PurpleBuddy *buddy)
-{
- g_return_val_if_fail(buddy != NULL, NULL);
-
- return buddy->account;
-}
-
-const char *
-purple_buddy_get_name(const PurpleBuddy *buddy)
-{
- g_return_val_if_fail(buddy != NULL, NULL);
-
- return buddy->name;
-}
-
-PurpleBuddyIcon *
-purple_buddy_get_icon(const PurpleBuddy *buddy)
-{
- g_return_val_if_fail(buddy != NULL, NULL);
-
- return buddy->icon;
-}
-
-gpointer
-purple_buddy_get_protocol_data(const PurpleBuddy *buddy)
-{
- g_return_val_if_fail(buddy != NULL, NULL);
-
- return buddy->proto_data;
-}
-
-void
-purple_buddy_set_protocol_data(PurpleBuddy *buddy, gpointer data)
-{
- g_return_if_fail(buddy != NULL);
-
- buddy->proto_data = data;
-}
-
-
-void purple_blist_add_chat(PurpleChat *chat, PurpleGroup *group, PurpleBlistNode *node)
-{
- PurpleBlistNode *cnode = (PurpleBlistNode*)chat;
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-
- g_return_if_fail(chat != NULL);
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT((PurpleBlistNode *)chat));
-
- if (node == NULL) {
- if (group == NULL)
- group = purple_group_new(_("Chats"));
-
- /* Add group to blist if isn't already on it. Fixes #2752. */
- if (!purple_find_group(group->name)) {
- purple_blist_add_group(group,
- purple_blist_get_last_sibling(purplebuddylist->root));
- }
- } else {
- group = (PurpleGroup*)node->parent;
- }
-
- /* if we're moving to overtop of ourselves, do nothing */
- if (cnode == node)
- return;
-
- if (cnode->parent) {
- /* This chat was already in the list and is
- * being moved.
- */
- ((PurpleGroup *)cnode->parent)->totalsize--;
- if (purple_account_is_connected(chat->account)) {
- ((PurpleGroup *)cnode->parent)->online--;
- ((PurpleGroup *)cnode->parent)->currentsize--;
- }
- if (cnode->next)
- cnode->next->prev = cnode->prev;
- if (cnode->prev)
- cnode->prev->next = cnode->next;
- if (cnode->parent->child == cnode)
- cnode->parent->child = cnode->next;
-
- if (ops && ops->remove)
- ops->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 (node != NULL) {
- if (node->next)
- node->next->prev = cnode;
- cnode->next = node->next;
- cnode->prev = node;
- cnode->parent = node->parent;
- node->next = cnode;
- ((PurpleGroup *)node->parent)->totalsize++;
- if (purple_account_is_connected(chat->account)) {
- ((PurpleGroup *)node->parent)->online++;
- ((PurpleGroup *)node->parent)->currentsize++;
- }
- } else {
- if (((PurpleBlistNode *)group)->child)
- ((PurpleBlistNode *)group)->child->prev = cnode;
- cnode->next = ((PurpleBlistNode *)group)->child;
- cnode->prev = NULL;
- ((PurpleBlistNode *)group)->child = cnode;
- cnode->parent = (PurpleBlistNode *)group;
- group->totalsize++;
- if (purple_account_is_connected(chat->account)) {
- group->online++;
- group->currentsize++;
- }
- }
-
- if (ops && ops->save_node)
- ops->save_node(cnode);
-
- if (ops && ops->update)
- ops->update(purplebuddylist, (PurpleBlistNode *)cnode);
-
- purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
- cnode);
-}
-
-void purple_blist_add_buddy(PurpleBuddy *buddy, PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node)
-{
- PurpleBlistNode *cnode, *bnode;
- PurpleGroup *g;
- PurpleContact *c;
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
- struct _purple_hbuddy *hb, *hb2;
- GHashTable *account_buddies;
-
- g_return_if_fail(buddy != NULL);
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY((PurpleBlistNode*)buddy));
-
- bnode = (PurpleBlistNode *)buddy;
-
- /* if we're moving to overtop of ourselves, do nothing */
- if (bnode == node || (!node && bnode->parent &&
- contact && bnode->parent == (PurpleBlistNode*)contact
- && bnode == bnode->parent->child))
- return;
-
- if (node && PURPLE_BLIST_NODE_IS_BUDDY(node)) {
- c = (PurpleContact*)node->parent;
- g = (PurpleGroup*)node->parent->parent;
- } else if (contact) {
- c = contact;
- g = PURPLE_GROUP(PURPLE_BLIST_NODE(c)->parent);
- } else {
- g = group;
- if (g == NULL)
- g = purple_group_new(_("Buddies"));
- /* Add group to blist if isn't already on it. Fixes #2752. */
- if (!purple_find_group(g->name)) {
- purple_blist_add_group(g,
- purple_blist_get_last_sibling(purplebuddylist->root));
- }
- c = purple_contact_new();
- purple_blist_add_contact(c, g,
- purple_blist_get_last_child((PurpleBlistNode*)g));
- }
-
- cnode = (PurpleBlistNode *)c;
-
- if (bnode->parent) {
- if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
- ((PurpleContact*)bnode->parent)->online--;
- if (((PurpleContact*)bnode->parent)->online == 0)
- ((PurpleGroup*)bnode->parent->parent)->online--;
- }
- if (purple_account_is_connected(buddy->account)) {
- ((PurpleContact*)bnode->parent)->currentsize--;
- if (((PurpleContact*)bnode->parent)->currentsize == 0)
- ((PurpleGroup*)bnode->parent->parent)->currentsize--;
- }
- ((PurpleContact*)bnode->parent)->totalsize--;
- /* the group totalsize will be taken care of by remove_contact below */
-
- if (bnode->parent->parent != (PurpleBlistNode*)g)
- serv_move_buddy(buddy, (PurpleGroup *)bnode->parent->parent, g);
-
- if (bnode->next)
- bnode->next->prev = bnode->prev;
- if (bnode->prev)
- bnode->prev->next = bnode->next;
- if (bnode->parent->child == bnode)
- bnode->parent->child = bnode->next;
-
- if (ops && ops->remove)
- ops->remove(purplebuddylist, bnode);
-
- if (bnode->parent->parent != (PurpleBlistNode*)g) {
- struct _purple_hbuddy hb;
- hb.name = (gchar *)purple_normalize(buddy->account, buddy->name);
- hb.account = buddy->account;
- hb.group = bnode->parent->parent;
- g_hash_table_remove(purplebuddylist->buddies, &hb);
-
- account_buddies = g_hash_table_lookup(buddies_cache, buddy->account);
- g_hash_table_remove(account_buddies, &hb);
- }
-
- if (!bnode->parent->child) {
- purple_blist_remove_contact((PurpleContact*)bnode->parent);
- } else {
- purple_contact_invalidate_priority_buddy((PurpleContact*)bnode->parent);
- if (ops && ops->update)
- ops->update(purplebuddylist, bnode->parent);
- }
- }
-
- if (node && PURPLE_BLIST_NODE_IS_BUDDY(node)) {
- if (node->next)
- node->next->prev = bnode;
- bnode->next = node->next;
- bnode->prev = node;
- bnode->parent = node->parent;
- node->next = bnode;
- } else {
- if (cnode->child)
- cnode->child->prev = bnode;
- bnode->prev = NULL;
- bnode->next = cnode->child;
- cnode->child = bnode;
- bnode->parent = cnode;
- }
-
- if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
- if (++(PURPLE_CONTACT(bnode->parent)->online) == 1)
- PURPLE_GROUP(bnode->parent->parent)->online++;
- }
- if (purple_account_is_connected(buddy->account)) {
- if (++(PURPLE_CONTACT(bnode->parent)->currentsize) == 1)
- PURPLE_GROUP(bnode->parent->parent)->currentsize++;
- }
- PURPLE_CONTACT(bnode->parent)->totalsize++;
-
- hb = g_new(struct _purple_hbuddy, 1);
- hb->name = g_strdup(purple_normalize(buddy->account, buddy->name));
- hb->account = buddy->account;
- hb->group = ((PurpleBlistNode*)buddy)->parent->parent;
-
- g_hash_table_replace(purplebuddylist->buddies, hb, buddy);
-
- account_buddies = g_hash_table_lookup(buddies_cache, buddy->account);
-
- hb2 = g_new(struct _purple_hbuddy, 1);
- hb2->name = g_strdup(hb->name);
- hb2->account = buddy->account;
- hb2->group = ((PurpleBlistNode*)buddy)->parent->parent;
-
- g_hash_table_replace(account_buddies, hb2, buddy);
-
- purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy));
-
- if (ops && ops->save_node)
- ops->save_node((PurpleBlistNode*) buddy);
-
- if (ops && ops->update)
- ops->update(purplebuddylist, (PurpleBlistNode*)buddy);
-
- /* Signal that the buddy has been added */
- purple_signal_emit(purple_blist_get_handle(), "buddy-added", buddy);
-
- purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
- PURPLE_BLIST_NODE(buddy));
-}
-
-PurpleContact *purple_contact_new()
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-
- PurpleContact *contact = g_new0(PurpleContact, 1);
- contact->totalsize = 0;
- contact->currentsize = 0;
- contact->online = 0;
- purple_blist_node_initialize_settings((PurpleBlistNode *)contact);
- ((PurpleBlistNode *)contact)->type = PURPLE_BLIST_CONTACT_NODE;
-
- if (ops && ops->new_node)
- ops->new_node((PurpleBlistNode *)contact);
-
- PURPLE_DBUS_REGISTER_POINTER(contact, PurpleContact);
- return contact;
-}
-
-void
-purple_contact_destroy(PurpleContact *contact)
-{
- g_hash_table_destroy(contact->node.settings);
- g_free(contact->alias);
- PURPLE_DBUS_UNREGISTER_POINTER(contact);
- g_free(contact);
-}
-
-PurpleGroup *
-purple_contact_get_group(const PurpleContact *contact)
-{
- g_return_val_if_fail(contact, NULL);
-
- return (PurpleGroup *)(((PurpleBlistNode *)contact)->parent);
-}
-
-void purple_contact_set_alias(PurpleContact *contact, const char *alias)
-{
- purple_blist_alias_contact(contact,alias);
-}
-
-const char *purple_contact_get_alias(PurpleContact* contact)
-{
- g_return_val_if_fail(contact != NULL, NULL);
-
- if (contact->alias)
- return contact->alias;
-
- return purple_buddy_get_alias(purple_contact_get_priority_buddy(contact));
-}
-
-gboolean purple_contact_on_account(PurpleContact *c, PurpleAccount *account)
-{
- PurpleBlistNode *bnode, *cnode = (PurpleBlistNode *) c;
-
- g_return_val_if_fail(c != NULL, FALSE);
- g_return_val_if_fail(account != NULL, FALSE);
-
- for (bnode = cnode->child; bnode; bnode = bnode->next) {
- PurpleBuddy *buddy;
-
- if (! PURPLE_BLIST_NODE_IS_BUDDY(bnode))
- continue;
-
- buddy = (PurpleBuddy *)bnode;
- if (buddy->account == account)
- return TRUE;
- }
- return FALSE;
-}
-
-void purple_contact_invalidate_priority_buddy(PurpleContact *contact)
-{
- g_return_if_fail(contact != NULL);
-
- contact->priority_valid = FALSE;
-}
-
-PurpleGroup *purple_group_new(const char *name)
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
- PurpleGroup *group;
-
- g_return_val_if_fail(name != NULL, NULL);
- g_return_val_if_fail(*name != '\0', NULL);
-
- group = purple_find_group(name);
- if (group != NULL)
- return group;
-
- group = g_new0(PurpleGroup, 1);
- group->name = purple_utf8_strip_unprintables(name);
- group->totalsize = 0;
- group->currentsize = 0;
- group->online = 0;
- purple_blist_node_initialize_settings((PurpleBlistNode *)group);
- ((PurpleBlistNode *)group)->type = PURPLE_BLIST_GROUP_NODE;
-
- if (ops && ops->new_node)
- ops->new_node((PurpleBlistNode *)group);
-
- PURPLE_DBUS_REGISTER_POINTER(group, PurpleGroup);
- return group;
-}
-
-void
-purple_group_destroy(PurpleGroup *group)
-{
- g_hash_table_destroy(group->node.settings);
- g_free(group->name);
- PURPLE_DBUS_UNREGISTER_POINTER(group);
- g_free(group);
-}
-
-void purple_blist_add_contact(PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node)
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
- PurpleGroup *g;
- PurpleBlistNode *gnode, *cnode, *bnode;
-
- g_return_if_fail(contact != NULL);
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CONTACT((PurpleBlistNode*)contact));
-
- if (PURPLE_BLIST_NODE(contact) == node)
- return;
-
- if (node && (PURPLE_BLIST_NODE_IS_CONTACT(node) ||
- PURPLE_BLIST_NODE_IS_CHAT(node)))
- g = (PurpleGroup*)node->parent;
- else if (group)
- g = group;
- else {
- g = purple_find_group(_("Buddies"));
- if (g == NULL) {
- g = purple_group_new(_("Buddies"));
- purple_blist_add_group(g,
- purple_blist_get_last_sibling(purplebuddylist->root));
- }
- }
-
- gnode = (PurpleBlistNode*)g;
- cnode = (PurpleBlistNode*)contact;
-
- if (cnode->parent) {
- if (cnode->parent->child == cnode)
- cnode->parent->child = cnode->next;
- if (cnode->prev)
- cnode->prev->next = cnode->next;
- if (cnode->next)
- cnode->next->prev = cnode->prev;
-
- if (cnode->parent != gnode) {
- bnode = cnode->child;
- while (bnode) {
- PurpleBlistNode *next_bnode = bnode->next;
- PurpleBuddy *b = (PurpleBuddy*)bnode;
- GHashTable *account_buddies;
-
- struct _purple_hbuddy *hb, *hb2;
-
- hb = g_new(struct _purple_hbuddy, 1);
- hb->name = g_strdup(purple_normalize(b->account, b->name));
- hb->account = b->account;
- hb->group = cnode->parent;
-
- g_hash_table_remove(purplebuddylist->buddies, hb);
-
- account_buddies = g_hash_table_lookup(buddies_cache, b->account);
- g_hash_table_remove(account_buddies, hb);
-
- if (!purple_find_buddy_in_group(b->account, b->name, g)) {
- hb->group = gnode;
- g_hash_table_replace(purplebuddylist->buddies, hb, b);
-
- hb2 = g_new(struct _purple_hbuddy, 1);
- hb2->name = g_strdup(hb->name);
- hb2->account = b->account;
- hb2->group = gnode;
-
- g_hash_table_replace(account_buddies, hb2, b);
-
- if (purple_account_get_connection(b->account))
- serv_move_buddy(b, (PurpleGroup *)cnode->parent, g);
- } else {
- gboolean empty_contact = FALSE;
-
- /* this buddy already exists in the group, so we're
- * gonna delete it instead */
- g_free(hb->name);
- g_free(hb);
- if (purple_account_get_connection(b->account))
- purple_account_remove_buddy(b->account, b, (PurpleGroup *)cnode->parent);
-
- if (!cnode->child->next)
- empty_contact = TRUE;
- purple_blist_remove_buddy(b);
-
- /** in purple_blist_remove_buddy(), if the last buddy in a
- * contact is removed, the contact is cleaned up and
- * g_free'd, so we mustn't try to reference bnode->next */
- if (empty_contact)
- return;
- }
- bnode = next_bnode;
- }
- }
-
- if (contact->online > 0)
- ((PurpleGroup*)cnode->parent)->online--;
- if (contact->currentsize > 0)
- ((PurpleGroup*)cnode->parent)->currentsize--;
- ((PurpleGroup*)cnode->parent)->totalsize--;
-
- if (ops && ops->remove)
- ops->remove(purplebuddylist, cnode);
-
- if (ops && ops->remove_node)
- ops->remove_node(cnode);
- }
-
- if (node && (PURPLE_BLIST_NODE_IS_CONTACT(node) ||
- PURPLE_BLIST_NODE_IS_CHAT(node))) {
- if (node->next)
- node->next->prev = cnode;
- cnode->next = node->next;
- cnode->prev = node;
- cnode->parent = node->parent;
- node->next = cnode;
- } else {
- if (gnode->child)
- gnode->child->prev = cnode;
- cnode->prev = NULL;
- cnode->next = gnode->child;
- gnode->child = cnode;
- cnode->parent = gnode;
- }
-
- if (contact->online > 0)
- g->online++;
- if (contact->currentsize > 0)
- g->currentsize++;
- g->totalsize++;
-
- 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 (ops && ops->update)
- {
- if (cnode->child)
- ops->update(purplebuddylist, cnode);
-
- for (bnode = cnode->child; bnode; bnode = bnode->next)
- ops->update(purplebuddylist, bnode);
- }
-}
-
-void purple_blist_merge_contact(PurpleContact *source, PurpleBlistNode *node)
-{
- PurpleBlistNode *sourcenode = (PurpleBlistNode*)source;
- PurpleBlistNode *prev, *cur, *next;
- PurpleContact *target;
-
- g_return_if_fail(source != NULL);
- g_return_if_fail(node != NULL);
-
- if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
- target = (PurpleContact *)node;
- prev = purple_blist_get_last_child(node);
- } else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
- target = (PurpleContact *)node->parent;
- prev = node;
- } else {
- return;
- }
-
- if (source == target || !target)
- return;
-
- next = sourcenode->child;
-
- while (next) {
- cur = next;
- next = cur->next;
- if (PURPLE_BLIST_NODE_IS_BUDDY(cur)) {
- purple_blist_add_buddy((PurpleBuddy *)cur, target, NULL, prev);
- prev = cur;
- }
- }
-}
-
-void purple_blist_add_group(PurpleGroup *group, PurpleBlistNode *node)
-{
- PurpleBlistUiOps *ops;
- PurpleBlistNode *gnode = (PurpleBlistNode*)group;
- gchar* key;
-
- g_return_if_fail(group != NULL);
- g_return_if_fail(PURPLE_BLIST_NODE_IS_GROUP((PurpleBlistNode *)group));
-
- ops = purple_blist_get_ui_ops();
-
- /* if we're moving to overtop of ourselves, do nothing */
- if (gnode == node) {
- if (!purplebuddylist->root)
- node = NULL;
- else
- return;
- }
-
- if (purple_find_group(group->name)) {
- /* This is just being moved */
-
- if (ops && ops->remove)
- ops->remove(purplebuddylist, (PurpleBlistNode *)group);
-
- if (gnode == purplebuddylist->root)
- purplebuddylist->root = gnode->next;
- if (gnode->prev)
- gnode->prev->next = gnode->next;
- if (gnode->next)
- gnode->next->prev = gnode->prev;
- } else {
- key = g_utf8_collate_key(group->name, -1);
- g_hash_table_insert(groups_cache, key, group);
- }
-
- if (node && PURPLE_BLIST_NODE_IS_GROUP(node)) {
- gnode->next = node->next;
- gnode->prev = node;
- if (node->next)
- node->next->prev = gnode;
- node->next = gnode;
- } else {
- if (purplebuddylist->root)
- purplebuddylist->root->prev = gnode;
- gnode->next = purplebuddylist->root;
- gnode->prev = NULL;
- purplebuddylist->root = gnode;
- }
-
- if (ops && ops->save_node) {
- ops->save_node(gnode);
- for (node = gnode->child; node; node = node->next)
- ops->save_node(node);
- }
-
- if (ops && ops->update) {
- ops->update(purplebuddylist, gnode);
- for (node = gnode->child; node; node = node->next)
- ops->update(purplebuddylist, node);
- }
-
- purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
- gnode);
-}
-
-void purple_blist_remove_contact(PurpleContact *contact)
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
- PurpleBlistNode *node, *gnode;
-
- g_return_if_fail(contact != NULL);
-
- node = (PurpleBlistNode *)contact;
- gnode = node->parent;
-
- if (node->child) {
- /*
- * If this contact has children then remove them. When the last
- * buddy is removed from the contact, the contact is automatically
- * deleted.
- */
- while (node->child->next) {
- purple_blist_remove_buddy((PurpleBuddy*)node->child);
- }
- /*
- * Remove the last buddy and trigger the deletion of the contact.
- * It would probably be cleaner if contact-deletion was done after
- * a timeout? Or if it had to be done manually, like below?
- */
- purple_blist_remove_buddy((PurpleBuddy*)node->child);
- } else {
- /* Remove the node from its parent */
- if (gnode->child == node)
- gnode->child = node->next;
- if (node->prev)
- node->prev->next = node->next;
- if (node->next)
- node->next->prev = node->prev;
-
- /* Update the UI */
- if (ops && ops->remove)
- ops->remove(purplebuddylist, node);
-
- if (ops && ops->remove_node)
- ops->remove_node(node);
-
- purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
- PURPLE_BLIST_NODE(contact));
-
- /* Delete the node */
- purple_contact_destroy(contact);
- }
-}
-
-void purple_blist_remove_buddy(PurpleBuddy *buddy)
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
- PurpleBlistNode *node, *cnode, *gnode;
- PurpleContact *contact;
- PurpleGroup *group;
- struct _purple_hbuddy hb;
- GHashTable *account_buddies;
-
- g_return_if_fail(buddy != NULL);
-
- node = (PurpleBlistNode *)buddy;
- cnode = node->parent;
- gnode = (cnode != NULL) ? cnode->parent : NULL;
- contact = (PurpleContact *)cnode;
- group = (PurpleGroup *)gnode;
-
- /* Remove the node from its parent */
- if (node->prev)
- node->prev->next = node->next;
- if (node->next)
- node->next->prev = node->prev;
- if ((cnode != NULL) && (cnode->child == node))
- cnode->child = node->next;
-
- /* Adjust size counts */
- if (contact != NULL) {
- if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
- contact->online--;
- if (contact->online == 0)
- group->online--;
- }
- if (purple_account_is_connected(buddy->account)) {
- contact->currentsize--;
- if (contact->currentsize == 0)
- group->currentsize--;
- }
- contact->totalsize--;
-
- /* Re-sort the contact */
- if (cnode->child && contact->priority == buddy) {
- purple_contact_invalidate_priority_buddy(contact);
- if (ops && ops->update)
- ops->update(purplebuddylist, cnode);
- }
- }
-
- /* Remove this buddy from the buddies hash table */
- hb.name = (gchar *)purple_normalize(buddy->account, buddy->name);
- hb.account = buddy->account;
- hb.group = gnode;
- g_hash_table_remove(purplebuddylist->buddies, &hb);
-
- account_buddies = g_hash_table_lookup(buddies_cache, buddy->account);
- g_hash_table_remove(account_buddies, &hb);
-
- /* Update the UI */
- if (ops && ops->remove)
- ops->remove(purplebuddylist, node);
-
- if (ops && ops->remove_node)
- ops->remove_node(node);
-
- /* Remove this buddy's pounces */
- purple_pounce_destroy_all_by_buddy(buddy);
-
- /* Signal that the buddy has been removed before freeing the memory for it */
- purple_signal_emit(purple_blist_get_handle(), "buddy-removed", buddy);
-
- purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
- PURPLE_BLIST_NODE(buddy));
-
- purple_buddy_destroy(buddy);
-
- /* If the contact is empty then remove it */
- if ((contact != NULL) && !cnode->child)
- purple_blist_remove_contact(contact);
-}
-
-void purple_blist_remove_chat(PurpleChat *chat)
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
- PurpleBlistNode *node, *gnode;
- PurpleGroup *group;
-
- g_return_if_fail(chat != NULL);
-
- node = (PurpleBlistNode *)chat;
- gnode = node->parent;
- group = (PurpleGroup *)gnode;
-
- if (gnode != NULL)
- {
- /* Remove the node from its parent */
- if (gnode->child == node)
- gnode->child = node->next;
- if (node->prev)
- node->prev->next = node->next;
- if (node->next)
- node->next->prev = node->prev;
-
- /* Adjust size counts */
- if (purple_account_is_connected(chat->account)) {
- group->online--;
- group->currentsize--;
- }
- group->totalsize--;
-
- }
-
- /* Update the UI */
- if (ops && ops->remove)
- ops->remove(purplebuddylist, node);
-
- if (ops && ops->remove_node)
- ops->remove_node(node);
-
- purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
- PURPLE_BLIST_NODE(chat));
-
- /* Delete the node */
- purple_chat_destroy(chat);
-}
-
-void purple_blist_remove_group(PurpleGroup *group)
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
- PurpleBlistNode *node;
- GList *l;
- gchar* key;
-
- g_return_if_fail(group != NULL);
-
- node = (PurpleBlistNode *)group;
-
- /* Make sure the group is empty */
- if (node->child)
- return;
-
- /* Remove the node from its parent */
- if (purplebuddylist->root == node)
- purplebuddylist->root = node->next;
- if (node->prev)
- node->prev->next = node->next;
- if (node->next)
- node->next->prev = node->prev;
-
- key = g_utf8_collate_key(group->name, -1);
- g_hash_table_remove(groups_cache, key);
- g_free(key);
-
- /* Update the UI */
- if (ops && ops->remove)
- ops->remove(purplebuddylist, node);
-
- if (ops && ops->remove_node)
- ops->remove_node(node);
-
- purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
- PURPLE_BLIST_NODE(group));
-
- /* Remove the group from all accounts that are online */
- for (l = purple_connections_get_all(); l != NULL; l = l->next)
- {
- PurpleConnection *gc = (PurpleConnection *)l->data;
-
- if (purple_connection_get_state(gc) == PURPLE_CONNECTED)
- purple_account_remove_group(purple_connection_get_account(gc), group);
- }
-
- /* Delete the node */
- purple_group_destroy(group);
-}
-
-PurpleBuddy *purple_contact_get_priority_buddy(PurpleContact *contact)
-{
- g_return_val_if_fail(contact != NULL, NULL);
-
- if (!contact->priority_valid)
- purple_contact_compute_priority_buddy(contact);
-
- return contact->priority;
-}
-
-const char *purple_buddy_get_alias_only(PurpleBuddy *buddy)
-{
- g_return_val_if_fail(buddy != NULL, NULL);
-
- if ((buddy->alias != NULL) && (*buddy->alias != '\0')) {
- return buddy->alias;
- } else if ((buddy->server_alias != NULL) &&
- (*buddy->server_alias != '\0')) {
-
- return buddy->server_alias;
- }
-
- return NULL;
-}
-
-
-const char *purple_buddy_get_contact_alias(PurpleBuddy *buddy)
-{
- PurpleContact *c;
-
- g_return_val_if_fail(buddy != NULL, NULL);
-
- /* Search for an alias for the buddy. In order of precedence: */
- /* The buddy alias */
- if (buddy->alias != NULL)
- return buddy->alias;
-
- /* The contact alias */
- c = purple_buddy_get_contact(buddy);
- if ((c != NULL) && (c->alias != NULL))
- return c->alias;
-
- /* The server alias */
- if ((buddy->server_alias) && (*buddy->server_alias))
- return buddy->server_alias;
-
- /* The buddy's user name (i.e. no alias) */
- return buddy->name;
-}
-
-
-const char *purple_buddy_get_alias(PurpleBuddy *buddy)
-{
- g_return_val_if_fail(buddy != NULL, NULL);
-
- /* Search for an alias for the buddy. In order of precedence: */
- /* The buddy alias */
- if (buddy->alias != NULL)
- return buddy->alias;
-
- /* The server alias */
- if ((buddy->server_alias) && (*buddy->server_alias))
- return buddy->server_alias;
-
- /* The buddy's user name (i.e. no alias) */
- return buddy->name;
-}
-
-const char *purple_buddy_get_local_buddy_alias(PurpleBuddy *buddy)
-{
- g_return_val_if_fail(buddy, NULL);
- return buddy->alias;
-}
-
-const char *purple_buddy_get_server_alias(PurpleBuddy *buddy)
-{
- g_return_val_if_fail(buddy != NULL, NULL);
-
- if ((buddy->server_alias) && (*buddy->server_alias))
- return buddy->server_alias;
-
- return NULL;
-}
-
-const char *purple_buddy_get_local_alias(PurpleBuddy *buddy)
-{
- PurpleContact *c;
-
- g_return_val_if_fail(buddy != NULL, NULL);
-
- /* Search for an alias for the buddy. In order of precedence: */
- /* The buddy alias */
- if (buddy->alias != NULL)
- return buddy->alias;
-
- /* The contact alias */
- c = purple_buddy_get_contact(buddy);
- if ((c != NULL) && (c->alias != NULL))
- return c->alias;
-
- /* The buddy's user name (i.e. no alias) */
- return buddy->name;
-}
-
-const char *purple_chat_get_name(PurpleChat *chat)
-{
- char *ret = NULL;
- PurplePlugin *prpl;
- PurplePluginProtocolInfo *prpl_info = NULL;
-
- g_return_val_if_fail(chat != NULL, NULL);
-
- if ((chat->alias != NULL) && (*chat->alias != '\0'))
- return chat->alias;
-
- prpl = purple_find_prpl(purple_account_get_protocol_id(chat->account));
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-
- if (prpl_info->chat_info) {
- struct proto_chat_entry *pce;
- GList *parts = prpl_info->chat_info(purple_account_get_connection(chat->account));
- pce = parts->data;
- ret = g_hash_table_lookup(chat->components, pce->identifier);
- g_list_foreach(parts, (GFunc)g_free, NULL);
- g_list_free(parts);
- }
-
- return ret;
-}
-
-PurpleBuddy *purple_find_buddy(PurpleAccount *account, const char *name)
-{
- PurpleBuddy *buddy;
- struct _purple_hbuddy hb;
- PurpleBlistNode *group;
-
- g_return_val_if_fail(purplebuddylist != NULL, NULL);
- g_return_val_if_fail(account != NULL, NULL);
- g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL);
-
- hb.account = account;
- hb.name = (gchar *)purple_normalize(account, name);
-
- for (group = purplebuddylist->root; group; group = group->next) {
- if (!group->child)
- continue;
-
- hb.group = group;
- if ((buddy = g_hash_table_lookup(purplebuddylist->buddies, &hb))) {
- return buddy;
- }
- }
-
- return NULL;
-}
-
-PurpleBuddy *purple_find_buddy_in_group(PurpleAccount *account, const char *name,
- PurpleGroup *group)
-{
- struct _purple_hbuddy hb;
-
- g_return_val_if_fail(purplebuddylist != NULL, NULL);
- g_return_val_if_fail(account != NULL, NULL);
- g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL);
-
- hb.name = (gchar *)purple_normalize(account, name);
- hb.account = account;
- hb.group = (PurpleBlistNode*)group;
-
- return g_hash_table_lookup(purplebuddylist->buddies, &hb);
-}
-
-static void find_acct_buddies(gpointer key, gpointer value, gpointer data)
-{
- PurpleBuddy *buddy = value;
- GSList **list = data;
-
- *list = g_slist_prepend(*list, buddy);
-}
-
-GSList *purple_find_buddies(PurpleAccount *account, const char *name)
-{
- PurpleBuddy *buddy;
- PurpleBlistNode *node;
- GSList *ret = NULL;
-
- g_return_val_if_fail(purplebuddylist != NULL, NULL);
- g_return_val_if_fail(account != NULL, NULL);
-
- if ((name != NULL) && (*name != '\0')) {
- struct _purple_hbuddy hb;
-
- hb.name = (gchar *)purple_normalize(account, name);
- hb.account = account;
-
- for (node = purplebuddylist->root; node != NULL; node = node->next) {
- if (!node->child)
- continue;
-
- hb.group = node;
- if ((buddy = g_hash_table_lookup(purplebuddylist->buddies, &hb)) != NULL)
- ret = g_slist_prepend(ret, buddy);
- }
- } else {
- GSList *list = NULL;
- GHashTable *buddies = g_hash_table_lookup(buddies_cache, account);
- g_hash_table_foreach(buddies, find_acct_buddies, &list);
- ret = list;
- }
-
- return ret;
-}
-
-PurpleGroup *purple_find_group(const char *name)
-{
- gchar* key;
- PurpleGroup *group;
-
- g_return_val_if_fail(purplebuddylist != NULL, NULL);
- g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL);
-
- key = g_utf8_collate_key(name, -1);
- group = g_hash_table_lookup(groups_cache, key);
- g_free(key);
-
- return group;
-}
-
-PurpleChat *
-purple_blist_find_chat(PurpleAccount *account, const char *name)
-{
- char *chat_name;
- PurpleChat *chat;
- PurplePlugin *prpl;
- PurplePluginProtocolInfo *prpl_info = NULL;
- struct proto_chat_entry *pce;
- PurpleBlistNode *node, *group;
- GList *parts;
- char *normname;
-
- g_return_val_if_fail(purplebuddylist != NULL, NULL);
- g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL);
-
- if (!purple_account_is_connected(account))
- return NULL;
-
- prpl = purple_find_prpl(purple_account_get_protocol_id(account));
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-
- if (prpl_info->find_blist_chat != NULL)
- return prpl_info->find_blist_chat(account, name);
-
- normname = g_strdup(purple_normalize(account, name));
- for (group = purplebuddylist->root; group != NULL; group = group->next) {
- for (node = group->child; node != NULL; node = node->next) {
- if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
-
- chat = (PurpleChat*)node;
-
- if (account != chat->account)
- continue;
-
- parts = prpl_info->chat_info(
- purple_account_get_connection(chat->account));
-
- pce = parts->data;
- chat_name = g_hash_table_lookup(chat->components,
- pce->identifier);
- g_list_foreach(parts, (GFunc)g_free, NULL);
- g_list_free(parts);
-
- if (chat->account == account && chat_name != NULL &&
- normname != NULL && !strcmp(purple_normalize(account, chat_name), normname)) {
- g_free(normname);
- return chat;
- }
- }
- }
- }
-
- g_free(normname);
- return NULL;
-}
-
-PurpleGroup *
-purple_chat_get_group(PurpleChat *chat)
-{
- g_return_val_if_fail(chat != NULL, NULL);
-
- return (PurpleGroup *)(((PurpleBlistNode *)chat)->parent);
-}
-
-PurpleAccount *
-purple_chat_get_account(PurpleChat *chat)
-{
- g_return_val_if_fail(chat != NULL, NULL);
-
- return chat->account;
-}
-
-GHashTable *
-purple_chat_get_components(PurpleChat *chat)
-{
- g_return_val_if_fail(chat != NULL, NULL);
-
- return chat->components;
-}
-
-PurpleContact *purple_buddy_get_contact(PurpleBuddy *buddy)
-{
- g_return_val_if_fail(buddy != NULL, NULL);
-
- return PURPLE_CONTACT(PURPLE_BLIST_NODE(buddy)->parent);
-}
-
-PurplePresence *purple_buddy_get_presence(const PurpleBuddy *buddy)
-{
- g_return_val_if_fail(buddy != NULL, NULL);
- return buddy->presence;
-}
-
-PurpleMediaCaps purple_buddy_get_media_caps(const PurpleBuddy *buddy)
-{
- g_return_val_if_fail(buddy != NULL, 0);
- return buddy->media_caps;
-}
-
-void purple_buddy_set_media_caps(PurpleBuddy *buddy, PurpleMediaCaps media_caps)
-{
- g_return_if_fail(buddy != NULL);
- buddy->media_caps = media_caps;
-}
-
-PurpleGroup *purple_buddy_get_group(PurpleBuddy *buddy)
-{
- g_return_val_if_fail(buddy != NULL, NULL);
-
- if (((PurpleBlistNode *)buddy)->parent == NULL)
- return NULL;
-
- return (PurpleGroup *)(((PurpleBlistNode*)buddy)->parent->parent);
-}
-
-GSList *purple_group_get_accounts(PurpleGroup *group)
-{
- GSList *l = NULL;
- PurpleBlistNode *gnode, *cnode, *bnode;
-
- gnode = (PurpleBlistNode *)group;
-
- for (cnode = gnode->child; cnode; cnode = cnode->next) {
- if (PURPLE_BLIST_NODE_IS_CHAT(cnode)) {
- if (!g_slist_find(l, ((PurpleChat *)cnode)->account))
- l = g_slist_append(l, ((PurpleChat *)cnode)->account);
- } else if (PURPLE_BLIST_NODE_IS_CONTACT(cnode)) {
- for (bnode = cnode->child; bnode; bnode = bnode->next) {
- if (PURPLE_BLIST_NODE_IS_BUDDY(bnode)) {
- if (!g_slist_find(l, ((PurpleBuddy *)bnode)->account))
- l = g_slist_append(l, ((PurpleBuddy *)bnode)->account);
- }
- }
- }
- }
-
- return l;
-}
-
-void purple_blist_add_account(PurpleAccount *account)
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
- PurpleBlistNode *gnode, *cnode, *bnode;
-
- g_return_if_fail(purplebuddylist != NULL);
-
- if (!ops || !ops->update)
- return;
-
- for (gnode = purplebuddylist->root; gnode; gnode = gnode->next) {
- if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
- continue;
- for (cnode = gnode->child; cnode; cnode = cnode->next) {
- if (PURPLE_BLIST_NODE_IS_CONTACT(cnode)) {
- gboolean recompute = FALSE;
- for (bnode = cnode->child; bnode; bnode = bnode->next) {
- if (PURPLE_BLIST_NODE_IS_BUDDY(bnode) &&
- ((PurpleBuddy*)bnode)->account == account) {
- recompute = TRUE;
- ((PurpleContact*)cnode)->currentsize++;
- if (((PurpleContact*)cnode)->currentsize == 1)
- ((PurpleGroup*)gnode)->currentsize++;
- 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);
- }
- } else if (PURPLE_BLIST_NODE_IS_CHAT(cnode) &&
- ((PurpleChat*)cnode)->account == account) {
- ((PurpleGroup *)gnode)->online++;
- ((PurpleGroup *)gnode)->currentsize++;
- ops->update(purplebuddylist, cnode);
- }
- }
- ops->update(purplebuddylist, gnode);
- }
-}
-
-void purple_blist_remove_account(PurpleAccount *account)
-{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
- PurpleBlistNode *gnode, *cnode, *bnode;
- PurpleBuddy *buddy;
- PurpleChat *chat;
- PurpleContact *contact;
- PurpleGroup *group;
- GList *list = NULL, *iter = NULL;
-
- g_return_if_fail(purplebuddylist != NULL);
-
- for (gnode = purplebuddylist->root; gnode; gnode = gnode->next) {
- if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
- continue;
-
- group = (PurpleGroup *)gnode;
-
- for (cnode = gnode->child; cnode; cnode = cnode->next) {
- if (PURPLE_BLIST_NODE_IS_CONTACT(cnode)) {
- gboolean recompute = FALSE;
- contact = (PurpleContact *)cnode;
-
- for (bnode = cnode->child; bnode; bnode = bnode->next) {
- if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
- continue;
-
- buddy = (PurpleBuddy *)bnode;
- if (account == buddy->account) {
- PurplePresence *presence;
-
- presence = purple_buddy_get_presence(buddy);
-
- if(purple_presence_is_online(presence)) {
- contact->online--;
- if (contact->online == 0)
- group->online--;
-
- purple_blist_node_set_int(&buddy->node,
- "last_seen", time(NULL));
- }
-
- contact->currentsize--;
- if (contact->currentsize == 0)
- group->currentsize--;
-
- if (!g_list_find(list, presence))
- list = g_list_prepend(list, presence);
-
- if (contact->priority == buddy)
- purple_contact_invalidate_priority_buddy(contact);
- else
- recompute = TRUE;
-
- if (ops && ops->remove) {
- ops->remove(purplebuddylist, bnode);
- }
- }
- }
- if (recompute) {
- purple_contact_invalidate_priority_buddy(contact);
- if (ops && ops->update)
- ops->update(purplebuddylist, cnode);
- }
- } else if (PURPLE_BLIST_NODE_IS_CHAT(cnode)) {
- chat = (PurpleChat *)cnode;
-
- if(chat->account == account) {
- group->currentsize--;
- group->online--;
-
- if (ops && ops->remove)
- ops->remove(purplebuddylist, cnode);
- }
- }
- }
- }
-
- for (iter = list; iter; iter = iter->next)
- {
- purple_presence_set_status_active(iter->data, "offline", TRUE);
- }
- g_list_free(list);
-}
-
-gboolean purple_group_on_account(PurpleGroup *g, PurpleAccount *account)
-{
- PurpleBlistNode *cnode;
- for (cnode = ((PurpleBlistNode *)g)->child; cnode; cnode = cnode->next) {
- if (PURPLE_BLIST_NODE_IS_CONTACT(cnode)) {
- if(purple_contact_on_account((PurpleContact *) cnode, account))
- return TRUE;
- } else if (PURPLE_BLIST_NODE_IS_CHAT(cnode)) {
- PurpleChat *chat = (PurpleChat *)cnode;
- if ((!account && purple_account_is_connected(chat->account))
- || chat->account == account)
- return TRUE;
- }
- }
- return FALSE;
-}
-
-const char *purple_group_get_name(PurpleGroup *group)
-{
- g_return_val_if_fail(group != NULL, NULL);
-
- return group->name;
-}
-
-void
-purple_blist_request_add_buddy(PurpleAccount *account, const char *username,
- const char *group, const char *alias)
-{
- PurpleBlistUiOps *ui_ops;
-
- 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);
-}
-
-void
-purple_blist_request_add_chat(PurpleAccount *account, PurpleGroup *group,
- const char *alias, const char *name)
-{
- PurpleBlistUiOps *ui_ops;
-
- 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);
-}
-
-void
-purple_blist_request_add_group(void)
-{
- PurpleBlistUiOps *ui_ops;
-
- ui_ops = purple_blist_get_ui_ops();
-
- if (ui_ops != NULL && ui_ops->request_add_group != NULL)
- ui_ops->request_add_group();
-}
-
-static void
-purple_blist_node_destroy(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;
- purple_blist_node_destroy(child);
- child = next_child;
- }
-
- /* Allow the UI to free data */
- node->parent = NULL;
- node->child = NULL;
- node->next = NULL;
- node->prev = NULL;
- if (ui_ops && ui_ops->remove)
- ui_ops->remove(purplebuddylist, node);
-
- if (PURPLE_BLIST_NODE_IS_BUDDY(node))
- purple_buddy_destroy((PurpleBuddy*)node);
- else if (PURPLE_BLIST_NODE_IS_CHAT(node))
- purple_chat_destroy((PurpleChat*)node);
- else if (PURPLE_BLIST_NODE_IS_CONTACT(node))
- purple_contact_destroy((PurpleContact*)node);
- else if (PURPLE_BLIST_NODE_IS_GROUP(node))
- purple_group_destroy((PurpleGroup*)node);
-}
-
-static void
-purple_blist_node_setting_free(gpointer data)
-{
- PurpleValue *value;
-
- value = (PurpleValue *)data;
-
- purple_value_destroy(value);
-}
-
-static void purple_blist_node_initialize_settings(PurpleBlistNode *node)
-{
- if (node->settings)
- return;
-
- node->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
- (GDestroyNotify)purple_blist_node_setting_free);
-}
-
-void purple_blist_node_remove_setting(PurpleBlistNode *node, const char *key)
-{
- PurpleBlistUiOps *ops;
- g_return_if_fail(node != NULL);
- g_return_if_fail(node->settings != NULL);
- g_return_if_fail(key != NULL);
-
- g_hash_table_remove(node->settings, key);
-
- ops = purple_blist_get_ui_ops();
- if (ops && ops->save_node)
- ops->save_node(node);
-}
-
-void
-purple_blist_node_set_flags(PurpleBlistNode *node, PurpleBlistNodeFlags flags)
-{
- g_return_if_fail(node != NULL);
-
- node->flags = flags;
-}
-
-PurpleBlistNodeFlags
-purple_blist_node_get_flags(PurpleBlistNode *node)
-{
- g_return_val_if_fail(node != NULL, 0);
-
- return node->flags;
-}
-
-PurpleBlistNodeType
-purple_blist_node_get_type(PurpleBlistNode *node)
-{
- g_return_val_if_fail(node != NULL, PURPLE_BLIST_OTHER_NODE);
- return node->type;
-}
-
-void
-purple_blist_node_set_bool(PurpleBlistNode* node, const char *key, gboolean data)
-{
- PurpleValue *value;
- PurpleBlistUiOps *ops;
-
- g_return_if_fail(node != NULL);
- g_return_if_fail(node->settings != NULL);
- g_return_if_fail(key != NULL);
-
- value = purple_value_new(PURPLE_TYPE_BOOLEAN);
- purple_value_set_boolean(value, data);
-
- g_hash_table_replace(node->settings, g_strdup(key), value);
-
- ops = purple_blist_get_ui_ops();
- if (ops && ops->save_node)
- ops->save_node(node);
-}
-
-gboolean
-purple_blist_node_get_bool(PurpleBlistNode* node, const char *key)
-{
- PurpleValue *value;
-
- g_return_val_if_fail(node != NULL, FALSE);
- g_return_val_if_fail(node->settings != NULL, FALSE);
- g_return_val_if_fail(key != NULL, FALSE);
-
- value = g_hash_table_lookup(node->settings, key);
-
- if (value == NULL)
- return FALSE;
-
- g_return_val_if_fail(purple_value_get_type(value) == PURPLE_TYPE_BOOLEAN, FALSE);
-
- return purple_value_get_boolean(value);
-}
-
-void
-purple_blist_node_set_int(PurpleBlistNode* node, const char *key, int data)
-{
- PurpleValue *value;
- PurpleBlistUiOps *ops;
-
- g_return_if_fail(node != NULL);
- g_return_if_fail(node->settings != NULL);
- g_return_if_fail(key != NULL);
-
- value = purple_value_new(PURPLE_TYPE_INT);
- purple_value_set_int(value, data);
-
- g_hash_table_replace(node->settings, g_strdup(key), value);
-
- ops = purple_blist_get_ui_ops();
- if (ops && ops->save_node)
- ops->save_node(node);
-}
-
-int
-purple_blist_node_get_int(PurpleBlistNode* node, const char *key)
-{
- PurpleValue *value;
-
- g_return_val_if_fail(node != NULL, 0);
- g_return_val_if_fail(node->settings != NULL, 0);
- g_return_val_if_fail(key != NULL, 0);
-
- value = g_hash_table_lookup(node->settings, key);
-
- if (value == NULL)
- return 0;
-
- g_return_val_if_fail(purple_value_get_type(value) == PURPLE_TYPE_INT, 0);
-
- return purple_value_get_int(value);
-}
-
-void
-purple_blist_node_set_string(PurpleBlistNode* node, const char *key, const char *data)
-{
- PurpleValue *value;
- PurpleBlistUiOps *ops;
-
- g_return_if_fail(node != NULL);
- g_return_if_fail(node->settings != NULL);
- g_return_if_fail(key != NULL);
-
- value = purple_value_new(PURPLE_TYPE_STRING);
- purple_value_set_string(value, data);
-
- g_hash_table_replace(node->settings, g_strdup(key), value);
-
- ops = purple_blist_get_ui_ops();
- if (ops && ops->save_node)
- ops->save_node(node);
-}
-
-const char *
-purple_blist_node_get_string(PurpleBlistNode* node, const char *key)
-{
- PurpleValue *value;
-
- g_return_val_if_fail(node != NULL, NULL);
- g_return_val_if_fail(node->settings != NULL, NULL);
- g_return_val_if_fail(key != NULL, NULL);
-
- value = g_hash_table_lookup(node->settings, key);
-
- if (value == NULL)
- return NULL;
-
- g_return_val_if_fail(purple_value_get_type(value) == PURPLE_TYPE_STRING, NULL);
-
- return purple_value_get_string(value);
-}
-
-GList *
-purple_blist_node_get_extended_menu(PurpleBlistNode *n)
-{
- GList *menu = NULL;
-
- g_return_val_if_fail(n != NULL, NULL);
-
- purple_signal_emit(purple_blist_get_handle(),
- "blist-node-extended-menu",
- n, &menu);
- return menu;
-}
-
-int purple_blist_get_group_size(PurpleGroup *group, gboolean offline)
-{
- if (!group)
- return 0;
-
- return offline ? group->totalsize : group->currentsize;
-}
-
-int purple_blist_get_group_online_count(PurpleGroup *group)
-{
- if (!group)
- return 0;
-
- return group->online;
-}
-
-void
-purple_blist_set_ui_ops(PurpleBlistUiOps *ops)
-{
- gboolean overrode = FALSE;
- blist_ui_ops = ops;
-
- if (!ops)
- return;
-
- 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;
- }
-
- 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("blist", "Only some of the blist saving UI ops "
- "were overridden. This probably is not what you want!\n");
- }
-}
-
-PurpleBlistUiOps *
-purple_blist_get_ui_ops(void)
-{
- return blist_ui_ops;
-}
-
-
-void *
-purple_blist_get_handle(void)
-{
- static int handle;
-
- return &handle;
-}
-
-void
-purple_blist_init(void)
-{
- void *handle = purple_blist_get_handle();
-
- purple_signal_register(handle, "buddy-status-changed",
- purple_marshal_VOID__POINTER_POINTER_POINTER, NULL,
- 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_BLIST_BUDDY),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_STATUS),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_STATUS));
- purple_signal_register(handle, "buddy-privacy-changed",
- purple_marshal_VOID__POINTER, NULL,
- 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_BLIST_BUDDY));
-
- purple_signal_register(handle, "buddy-idle-changed",
- purple_marshal_VOID__POINTER_INT_INT, NULL,
- 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_BLIST_BUDDY),
- purple_value_new(PURPLE_TYPE_INT),
- purple_value_new(PURPLE_TYPE_INT));
-
-
- purple_signal_register(handle, "buddy-signed-on",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_BLIST_BUDDY));
-
- purple_signal_register(handle, "buddy-signed-off",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_BLIST_BUDDY));
-
- purple_signal_register(handle, "buddy-got-login-time",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_BLIST_BUDDY));
-
- purple_signal_register(handle, "blist-node-added",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_BLIST_NODE));
-
- purple_signal_register(handle, "blist-node-removed",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_BLIST_NODE));
-
- purple_signal_register(handle, "buddy-added",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_BLIST_BUDDY));
-
- purple_signal_register(handle, "buddy-removed",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_BLIST_BUDDY));
-
- purple_signal_register(handle, "buddy-icon-changed",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_BLIST_BUDDY));
-
- purple_signal_register(handle, "update-idle", purple_marshal_VOID, NULL, 0);
-
- purple_signal_register(handle, "blist-node-extended-menu",
- purple_marshal_VOID__POINTER_POINTER, NULL, 2,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_BLIST_NODE),
- purple_value_new(PURPLE_TYPE_BOXED, "GList **"));
-
- purple_signal_register(handle, "blist-node-aliased",
- purple_marshal_VOID__POINTER_POINTER, NULL, 2,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_BLIST_NODE),
- purple_value_new(PURPLE_TYPE_STRING));
-
- purple_signal_register(handle, "buddy-caps-changed",
- purple_marshal_VOID__POINTER_INT_INT, NULL,
- 3, purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_BLIST_BUDDY),
- purple_value_new(PURPLE_TYPE_INT),
- purple_value_new(PURPLE_TYPE_INT));
-
- purple_signal_connect(purple_accounts_get_handle(), "account-created",
- handle,
- PURPLE_CALLBACK(purple_blist_buddies_cache_add_account),
- NULL);
-
- purple_signal_connect(purple_accounts_get_handle(), "account-destroying",
- handle,
- PURPLE_CALLBACK(purple_blist_buddies_cache_remove_account),
- NULL);
-}
-
-void
-purple_blist_uninit(void)
-{
- PurpleBlistNode *node, *next_node;
-
- /* This happens if we quit before purple_set_blist is called. */
- if (purplebuddylist == NULL)
- return;
-
- if (save_timer != 0) {
- purple_timeout_remove(save_timer);
- save_timer = 0;
- purple_blist_sync();
- }
-
- purple_blist_destroy();
-
- node = purple_blist_get_root();
- while (node) {
- next_node = node->next;
- purple_blist_node_destroy(node);
- node = next_node;
- }
- purplebuddylist->root = NULL;
-
- g_hash_table_destroy(purplebuddylist->buddies);
- g_hash_table_destroy(buddies_cache);
- g_hash_table_destroy(groups_cache);
-
- buddies_cache = NULL;
- groups_cache = NULL;
-
- PURPLE_DBUS_UNREGISTER_POINTER(purplebuddylist);
- g_free(purplebuddylist);
- purplebuddylist = NULL;
-
- purple_signals_disconnect_by_handle(purple_blist_get_handle());
- purple_signals_unregister_by_instance(purple_blist_get_handle());
-}
diff --git a/libpurple/blist.h b/libpurple/blist.h
deleted file mode 100644
index a053d081c6..0000000000
--- a/libpurple/blist.h
+++ /dev/null
@@ -1,1278 +0,0 @@
-/**
- * @file blist.h Buddy List API
- * @ingroup core
- * @see @ref blist-signals
- */
-
-/* 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 _PURPLE_BLIST_H_
-#define _PURPLE_BLIST_H_
-
-/* I can't believe I let ChipX86 inspire me to write good code. -Sean */
-
-#include <glib.h>
-
-/** @copydoc _PurpleBuddyList */
-typedef struct _PurpleBuddyList PurpleBuddyList;
-/** @copydoc _PurpleBlistUiOps */
-typedef struct _PurpleBlistUiOps PurpleBlistUiOps;
-/** @copydoc _PurpleBlistNode */
-typedef struct _PurpleBlistNode PurpleBlistNode;
-
-/** @copydoc _PurpleChat */
-typedef struct _PurpleChat PurpleChat;
-/** @copydoc _PurpleGroup */
-typedef struct _PurpleGroup PurpleGroup;
-/** @copydoc _PurpleContact */
-typedef struct _PurpleContact PurpleContact;
-/** @copydoc _PurpleBuddy */
-typedef struct _PurpleBuddy PurpleBuddy;
-
-/**************************************************************************/
-/* Enumerations */
-/**************************************************************************/
-typedef enum
-{
- PURPLE_BLIST_GROUP_NODE,
- PURPLE_BLIST_CONTACT_NODE,
- PURPLE_BLIST_BUDDY_NODE,
- PURPLE_BLIST_CHAT_NODE,
- PURPLE_BLIST_OTHER_NODE
-
-} PurpleBlistNodeType;
-
-#define PURPLE_BLIST_NODE_IS_CHAT(n) (purple_blist_node_get_type(n) == PURPLE_BLIST_CHAT_NODE)
-#define PURPLE_BLIST_NODE_IS_BUDDY(n) (purple_blist_node_get_type(n) == PURPLE_BLIST_BUDDY_NODE)
-#define PURPLE_BLIST_NODE_IS_CONTACT(n) (purple_blist_node_get_type(n) == PURPLE_BLIST_CONTACT_NODE)
-#define PURPLE_BLIST_NODE_IS_GROUP(n) (purple_blist_node_get_type(n) == PURPLE_BLIST_GROUP_NODE)
-
-#define PURPLE_BUDDY_IS_ONLINE(b) \
- ((b) != NULL && purple_account_is_connected(purple_buddy_get_account(b)) && \
- purple_presence_is_online(purple_buddy_get_presence(b)))
-
-typedef enum
-{
- PURPLE_BLIST_NODE_FLAG_NO_SAVE = 1 << 0 /**< node should not be saved with the buddy list */
-
-} PurpleBlistNodeFlags;
-
-/**
- * @since 2.6.0
- */
-#define PURPLE_BLIST_NODE(obj) ((PurpleBlistNode *)(obj))
-
-#define PURPLE_BLIST_NODE_HAS_FLAG(b, f) (purple_blist_node_get_flags((PurpleBlistNode*)(b)) & (f))
-#define PURPLE_BLIST_NODE_SHOULD_SAVE(b) (! PURPLE_BLIST_NODE_HAS_FLAG(b, PURPLE_BLIST_NODE_FLAG_NO_SAVE))
-
-#define PURPLE_BLIST_NODE_NAME(n) (purple_blist_node_get_type(n) == PURPLE_BLIST_CHAT_NODE ? purple_chat_get_name((PurpleChat*)n) : \
- purple_blist_node_get_type(n) == PURPLE_BLIST_BUDDY_NODE ? purple_buddy_get_name((PurpleBuddy*)n) : NULL)
-
-/**
- * @since 2.6.0
- */
-#define PURPLE_GROUP(obj) ((PurpleGroup *)(obj))
-
-/**
- * @since 2.6.0
- */
-#define PURPLE_CONTACT(obj) ((PurpleContact *)(obj))
-
-/**
- * @since 2.6.0
- */
-#define PURPLE_BUDDY(obj) ((PurpleBuddy *)(obj))
-
-/**
- * @since 2.6.0
- */
-#define PURPLE_CHAT(obj) ((PurpleChat *)(obj))
-
-#include "account.h"
-#include "buddyicon.h"
-#include "media.h"
-#include "status.h"
-
-/**************************************************************************/
-/* Data Structures */
-/**************************************************************************/
-
-#if !(defined PURPLE_HIDE_STRUCTS) || (defined _PURPLE_BLIST_C_)
-
-/**
- * A Buddy list node. This can represent a group, a buddy, or anything else.
- * This is a base class for PurpleBuddy, PurpleContact, PurpleGroup, and for
- * anything else that wants to put itself in the buddy list. */
-struct _PurpleBlistNode {
- PurpleBlistNodeType type; /**< The type of node this is */
- PurpleBlistNode *prev; /**< The sibling before this buddy. */
- PurpleBlistNode *next; /**< The sibling after this buddy. */
- PurpleBlistNode *parent; /**< The parent of this node */
- PurpleBlistNode *child; /**< The child of this node */
- GHashTable *settings; /**< per-node settings */
- void *ui_data; /**< The UI can put data here. */
- PurpleBlistNodeFlags flags; /**< The buddy flags */
-};
-
-/**
- * A buddy. This contains everything Purple will ever need to know about someone on the buddy list. Everything.
- */
-struct _PurpleBuddy {
- PurpleBlistNode node; /**< The node that this buddy inherits from */
- char *name; /**< The name of the buddy. */
- char *alias; /**< The user-set alias of the buddy */
- char *server_alias; /**< The server-specified alias of the buddy. (i.e. MSN "Friendly Names") */
- void *proto_data; /**< This allows the prpl to associate whatever data it wants with a buddy */
- PurpleBuddyIcon *icon; /**< The buddy icon. */
- PurpleAccount *account; /**< the account this buddy belongs to */
- PurplePresence *presence;
- PurpleMediaCaps media_caps; /**< The media capabilities of the buddy. */
-};
-
-/**
- * A contact. This contains everything Purple will ever need to know about a contact.
- */
-struct _PurpleContact {
- PurpleBlistNode node; /**< The node that this contact inherits from. */
- char *alias; /**< The user-set alias of the contact */
- int totalsize; /**< The number of buddies in this contact */
- int currentsize; /**< The number of buddies in this contact corresponding to online accounts */
- int online; /**< The number of buddies in this contact who are currently online */
- PurpleBuddy *priority; /**< The "top" buddy for this contact */
- gboolean priority_valid; /**< Is priority valid? */
-};
-
-
-/**
- * A group. This contains everything Purple will ever need to know about a group.
- */
-struct _PurpleGroup {
- PurpleBlistNode node; /**< The node that this group inherits from */
- char *name; /**< The name of this group. */
- int totalsize; /**< The number of chats and contacts in this group */
- int currentsize; /**< The number of chats and contacts in this group corresponding to online accounts */
- int online; /**< The number of chats and contacts in this group who are currently online */
-};
-
-/**
- * A chat. This contains everything Purple needs to put a chat room in the
- * buddy list.
- */
-struct _PurpleChat {
- PurpleBlistNode node; /**< The node that this chat inherits from */
- char *alias; /**< The display name of this chat. */
- GHashTable *components; /**< the stuff the protocol needs to know to join the chat */
- PurpleAccount *account; /**< The account this chat is attached to */
-};
-
-/**
- * The Buddy List
- */
-struct _PurpleBuddyList {
- PurpleBlistNode *root; /**< The first node in the buddy list */
- GHashTable *buddies; /**< Every buddy in this list */
- void *ui_data; /**< UI-specific data. */
-};
-
-#endif /* PURPLE_HIDE_STRUCTS && PURPLE_BLIST_STRUCTS */
-
-/**
- * Buddy list UI operations.
- *
- * Any UI representing a buddy list must assign a filled-out PurpleBlistUiOps
- * structure to the buddy list core.
- */
-struct _PurpleBlistUiOps
-{
- void (*new_list)(PurpleBuddyList *list); /**< Sets UI-specific data on a buddy list. */
- void (*new_node)(PurpleBlistNode *node); /**< Sets UI-specific data on a node. */
- void (*show)(PurpleBuddyList *list); /**< The core will call this when it's finished doing its core stuff */
- void (*update)(PurpleBuddyList *list,
- PurpleBlistNode *node); /**< This will update a node in the buddy list. */
- void (*remove)(PurpleBuddyList *list,
- PurpleBlistNode *node); /**< This removes a node from the list */
- void (*destroy)(PurpleBuddyList *list); /**< When the list is destroyed, this is called to destroy the UI. */
- void (*set_visible)(PurpleBuddyList *list,
- gboolean show); /**< Hides or unhides the buddy list */
- 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_group)(void);
-
- /**
- * This is called when a node has been modified and should be saved.
- *
- * Implementation of this UI op is OPTIONAL. If not implemented, it will
- * be set to a fallback function that saves data to blist.xml like in
- * previous libpurple versions.
- *
- * @param node The node which has been modified.
- *
- * @since 2.6.0.
- */
- void (*save_node)(PurpleBlistNode *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).
- *
- * Implementation of this UI op is OPTIONAL. If not implemented, it will
- * be set to a fallback function that saves data to blist.xml like in
- * previous libpurple versions.
- *
- * @param node The node which has been modified.
- * @since 2.6.0.
- */
- void (*remove_node)(PurpleBlistNode *node);
-
- /**
- * Called to save all the data for an account. If the UI sets 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.
- *
- * Implementation of this UI op is OPTIONAL. If not implemented, it will
- * be set to a fallback function that saves data to blist.xml like in
- * previous libpurple versions.
- *
- * @param account The account whose data to save. If NULL, save all data
- * for all accounts.
- * @since 2.6.0.
- */
- void (*save_account)(PurpleAccount *account);
-
- void (*_purple_reserved1)(void);
-};
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**************************************************************************/
-/** @name Buddy List API */
-/**************************************************************************/
-/*@{*/
-
-/**
- * Creates a new buddy list
- *
- * @return The new buddy list.
- * @deprecated In 3.0.0, this will be handled by purple_blist_init()
- */
-PurpleBuddyList *purple_blist_new(void);
-
-/**
- * Sets the main buddy list.
- *
- * @param blist The buddy list you want to use.
- * @deprecated In 3.0.0, this will be handled by purple_blist_init()
- */
-void purple_set_blist(PurpleBuddyList *blist);
-
-/**
- * Returns the main buddy list.
- *
- * @return The main buddy list.
- */
-PurpleBuddyList *purple_get_blist(void);
-
-/**
- * Returns the root node of the main buddy list.
- *
- * @return The root node.
- */
-PurpleBlistNode *purple_blist_get_root(void);
-
-/**
- * Returns a list of every buddy in the list. Use of this function is
- * discouraged if you do not actually need every buddy in the list. Use
- * purple_find_buddies instead.
- *
- * @return A list of every buddy in the list. Caller is responsible for
- * freeing the list.
- *
- * @see purple_find_buddies
- * @since 2.6.0
- */
-GSList *purple_blist_get_buddies(void);
-
-/**
- * Returns the UI data for the list.
- *
- * @return The UI data for the list.
- *
- * @since 2.6.0
- */
-gpointer purple_blist_get_ui_data(void);
-
-/**
- * Sets the UI data for the list.
- *
- * @param ui_data The UI data for the list.
- *
- * @since 2.6.0
- */
-void purple_blist_set_ui_data(gpointer ui_data);
-
-/**
- * Returns the next node of a given node. This function is to be used to iterate
- * over the tree returned by purple_get_blist.
- *
- * @param node A node.
- * @param offline Whether to include nodes for offline accounts
- * @return The next node
- * @see purple_blist_node_get_parent
- * @see purple_blist_node_get_first_child
- * @see purple_blist_node_get_sibling_next
- * @see purple_blist_node_get_sibling_prev
- */
-PurpleBlistNode *purple_blist_node_next(PurpleBlistNode *node, gboolean offline);
-
-/**
- * Returns the parent node of a given node.
- *
- * @param node A node.
- * @return The parent node.
- * @since 2.4.0
- * @see purple_blist_node_get_first_child
- * @see purple_blist_node_get_sibling_next
- * @see purple_blist_node_get_sibling_prev
- * @see purple_blist_node_next
- */
-PurpleBlistNode *purple_blist_node_get_parent(PurpleBlistNode *node);
-
-/**
- * Returns the the first child node of a given node.
- *
- * @param node A node.
- * @return The child node.
- * @since 2.4.0
- * @see purple_blist_node_get_parent
- * @see purple_blist_node_get_sibling_next
- * @see purple_blist_node_get_sibling_prev
- * @see purple_blist_node_next
- */
-PurpleBlistNode *purple_blist_node_get_first_child(PurpleBlistNode *node);
-
-/**
- * Returns the sibling node of a given node.
- *
- * @param node A node.
- * @return The sibling node.
- * @since 2.4.0
- * @see purple_blist_node_get_parent
- * @see purple_blist_node_get_first_child
- * @see purple_blist_node_get_sibling_prev
- * @see purple_blist_node_next
- */
-PurpleBlistNode *purple_blist_node_get_sibling_next(PurpleBlistNode *node);
-
-/**
- * Returns the previous sibling node of a given node.
- *
- * @param node A node.
- * @return The sibling node.
- * @since 2.4.0
- * @see purple_blist_node_get_parent
- * @see purple_blist_node_get_first_child
- * @see purple_blist_node_get_sibling_next
- * @see purple_blist_node_next
- */
-PurpleBlistNode *purple_blist_node_get_sibling_prev(PurpleBlistNode *node);
-
-/**
- * Returns the UI data of a given node.
- *
- * @param node The node.
- * @return The UI data.
- * @since 2.6.0
- */
-gpointer purple_blist_node_get_ui_data(const PurpleBlistNode *node);
-
-/**
- * Sets the UI data of a given node.
- *
- * @param node The node.
- * @param ui_data The UI data.
- *
- * @since 2.6.0
- */
-void purple_blist_node_set_ui_data(PurpleBlistNode *node, gpointer ui_data);
-
-/**
- * Shows the buddy list, creating a new one if necessary.
- */
-void purple_blist_show(void);
-
-
-/**
- * Destroys the buddy list window.
- *
- * @deprecated The UI is responsible for cleaning up the
- * PurpleBuddyList->ui_data. purple_blist_uninit() will free the
- * PurpleBuddyList* itself.
- */
-void purple_blist_destroy(void);
-
-/**
- * Hides or unhides the buddy list.
- *
- * @param show Whether or not to show the buddy list
- */
-void purple_blist_set_visible(gboolean show);
-
-/**
- * Updates a buddy's status.
- *
- * This should only be called from within Purple.
- *
- * @param buddy The buddy whose status has changed.
- * @param old_status The status from which we are changing.
- */
-void purple_blist_update_buddy_status(PurpleBuddy *buddy, PurpleStatus *old_status);
-
-/**
- * Updates a node's custom icon.
- *
- * @param node The PurpleBlistNode whose custom icon has changed.
- *
- * @since 2.5.0
- */
-void purple_blist_update_node_icon(PurpleBlistNode *node);
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_BLIST_C_)
-/**
- * Updates a buddy's icon.
- *
- * @param buddy The buddy whose buddy icon has changed
- * @deprecated Use purple_blist_update_node_icon() instead.
- */
-void purple_blist_update_buddy_icon(PurpleBuddy *buddy);
-#endif
-
-/**
- * Renames a buddy in the buddy list.
- *
- * @param buddy The buddy whose name will be changed.
- * @param name The new name of the buddy.
- */
-void purple_blist_rename_buddy(PurpleBuddy *buddy, const char *name);
-
-/**
- * Aliases a contact in the buddy list.
- *
- * @param contact The contact whose alias will be changed.
- * @param alias The contact's alias.
- */
-void purple_blist_alias_contact(PurpleContact *contact, const char *alias);
-
-/**
- * Aliases a buddy in the buddy list.
- *
- * @param buddy The buddy whose alias will be changed.
- * @param alias The buddy's alias.
- */
-void purple_blist_alias_buddy(PurpleBuddy *buddy, const char *alias);
-
-/**
- * Sets the server-sent alias of a buddy in the buddy list.
- * PRPLs should call serv_got_alias() instead of this.
- *
- * @param buddy The buddy whose alias will be changed.
- * @param alias The buddy's "official" alias.
- */
-void purple_blist_server_alias_buddy(PurpleBuddy *buddy, const char *alias);
-
-/**
- * Aliases a chat in the buddy list.
- *
- * @param chat The chat whose alias will be changed.
- * @param alias The chat's new alias.
- */
-void purple_blist_alias_chat(PurpleChat *chat, const char *alias);
-
-/**
- * Renames a group
- *
- * @param group The group to rename
- * @param name The new name
- */
-void purple_blist_rename_group(PurpleGroup *group, const char *name);
-
-/**
- * Creates a new chat for the buddy list
- *
- * @param account The account this chat will get added to
- * @param alias The alias of the new chat
- * @param components The info the prpl needs to join the chat. The
- * hash function should be g_str_hash() and the
- * equal function should be g_str_equal().
- * @return A newly allocated chat
- */
-PurpleChat *purple_chat_new(PurpleAccount *account, const char *alias, GHashTable *components);
-
-/**
- * Destroys a chat
- *
- * @param chat The chat to destroy
- */
-void purple_chat_destroy(PurpleChat *chat);
-
-/**
- * Adds a new chat to the buddy list.
- *
- * The chat will be inserted right after node or appended to the end
- * of group if node is NULL. If both are NULL, the buddy will be added to
- * the "Chats" group.
- *
- * @param chat The new chat who gets added
- * @param group The group to add the new chat to.
- * @param node The insertion point
- */
-void purple_blist_add_chat(PurpleChat *chat, PurpleGroup *group, PurpleBlistNode *node);
-
-/**
- * Creates a new buddy.
- *
- * This function only creates the PurpleBuddy. Use purple_blist_add_buddy
- * to add the buddy to the list and purple_account_add_buddy to sync up
- * with the server.
- *
- * @param account The account this buddy will get added to
- * @param name The name of the new buddy
- * @param alias The alias of the new buddy (or NULL if unaliased)
- * @return A newly allocated buddy
- *
- * @see purple_account_add_buddy
- * @see purple_blist_add_buddy
- */
-PurpleBuddy *purple_buddy_new(PurpleAccount *account, const char *name, const char *alias);
-
-/**
- * Destroys a buddy
- *
- * @param buddy The buddy to destroy
- */
-void purple_buddy_destroy(PurpleBuddy *buddy);
-
-/**
- * Sets a buddy's icon.
- *
- * This should only be called from within Purple. You probably want to
- * call purple_buddy_icon_set_data().
- *
- * @param buddy The buddy.
- * @param icon The buddy icon.
- *
- * @see purple_buddy_icon_set_data()
- */
-void purple_buddy_set_icon(PurpleBuddy *buddy, PurpleBuddyIcon *icon);
-
-/**
- * Returns a buddy's account.
- *
- * @param buddy The buddy.
- *
- * @return The account
- */
-PurpleAccount *purple_buddy_get_account(const PurpleBuddy *buddy);
-
-/**
- * Returns a buddy's name
- *
- * @param buddy The buddy.
- *
- * @return The name.
- */
-const char *purple_buddy_get_name(const PurpleBuddy *buddy);
-
-/**
- * Returns a buddy's icon.
- *
- * @param buddy The buddy.
- *
- * @return The buddy icon.
- */
-PurpleBuddyIcon *purple_buddy_get_icon(const PurpleBuddy *buddy);
-
-/**
- * Returns a buddy's protocol-specific data.
- *
- * This should only be called from the associated prpl.
- *
- * @param buddy The buddy.
- * @return The protocol data.
- *
- * @see purple_buddy_set_protocol_data()
- * @since 2.6.0
- */
-gpointer purple_buddy_get_protocol_data(const PurpleBuddy *buddy);
-
-/**
- * Sets a buddy's protocol-specific data.
- *
- * This should only be called from the associated prpl.
- *
- * @param buddy The buddy.
- * @param data The data.
- *
- * @see purple_buddy_get_protocol_data()
- * @since 2.6.0
- */
-void purple_buddy_set_protocol_data(PurpleBuddy *buddy, gpointer data);
-
-/**
- * Returns a buddy's contact.
- *
- * @param buddy The buddy.
- *
- * @return The buddy's contact.
- */
-PurpleContact *purple_buddy_get_contact(PurpleBuddy *buddy);
-
-/**
- * Returns a buddy's presence.
- *
- * @param buddy The buddy.
- *
- * @return The buddy's presence.
- */
-PurplePresence *purple_buddy_get_presence(const PurpleBuddy *buddy);
-
-/**
- * Gets the media caps from a buddy.
- *
- * @param buddy The buddy.
- * @return The media caps.
- *
- * @since 2.7.0
- */
-PurpleMediaCaps purple_buddy_get_media_caps(const PurpleBuddy *buddy);
-
-/**
- * Sets the media caps for a buddy.
- *
- * @param buddy The PurpleBuddy.
- * @param media_caps The PurpleMediaCaps.
- */
-void purple_buddy_set_media_caps(PurpleBuddy *buddy, PurpleMediaCaps media_caps);
-
-/**
- * Adds a new buddy to the buddy list.
- *
- * The buddy will be inserted right after node or prepended to the
- * group if node is NULL. If both are NULL, the buddy will be added to
- * the "Buddies" group.
- *
- * @param buddy The new buddy who gets added
- * @param contact The optional contact to place the buddy in.
- * @param group The group to add the new buddy to.
- * @param node The insertion point. Pass in NULL to add the node as
- * the first child in the given group.
- */
-void purple_blist_add_buddy(PurpleBuddy *buddy, PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node);
-
-/**
- * Creates a new group
- *
- * You can't have more than one group with the same name. Sorry. If you pass
- * this the name of a group that already exists, it will return that group.
- *
- * @param name The name of the new group
- * @return A new group struct
-*/
-PurpleGroup *purple_group_new(const char *name);
-
-/**
- * Destroys a group
- *
- * @param group The group to destroy
-*/
-void purple_group_destroy(PurpleGroup *group);
-
-/**
- * Adds a new group to the buddy list.
- *
- * The new group will be inserted after insert or prepended to the list if
- * node is NULL.
- *
- * @param group The group
- * @param node The insertion point
- */
-void purple_blist_add_group(PurpleGroup *group, PurpleBlistNode *node);
-
-/**
- * Creates a new contact
- *
- * @return A new contact struct
- */
-PurpleContact *purple_contact_new(void);
-
-/**
- * Destroys a contact
- *
- * @param contact The contact to destroy
- */
-void purple_contact_destroy(PurpleContact *contact);
-
-/**
- * Gets the PurpleGroup from a PurpleContact
- *
- * @param contact The contact
- * @return The group
- *
- * @since 2.7.0
- */
-PurpleGroup *purple_contact_get_group(const PurpleContact *contact);
-
-/**
- * Adds a new contact to the buddy list.
- *
- * The new contact will be inserted after insert or prepended to the list if
- * node is NULL.
- *
- * @param contact The contact
- * @param group The group to add the contact to
- * @param node The insertion point
- */
-void purple_blist_add_contact(PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node);
-
-/**
- * Merges two contacts
- *
- * All of the buddies from source will be moved to target
- *
- * @param source The contact to merge
- * @param node The place to merge to (a buddy or contact)
- */
-void purple_blist_merge_contact(PurpleContact *source, PurpleBlistNode *node);
-
-/**
- * Returns the highest priority buddy for a given contact.
- *
- * @param contact The contact
- * @return The highest priority buddy
- */
-PurpleBuddy *purple_contact_get_priority_buddy(PurpleContact *contact);
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_BLIST_C_)
-/**
- * Sets the alias for a contact.
- *
- * @param contact The contact
- * @param alias The alias to set, or NULL to unset
- *
- * @deprecated Use purple_blist_alias_contact() instead.
- */
-void purple_contact_set_alias(PurpleContact *contact, const char *alias);
-#endif
-
-/**
- * Gets the alias for a contact.
- *
- * @param contact The contact
- * @return The alias, or NULL if it is not set.
- */
-const char *purple_contact_get_alias(PurpleContact *contact);
-
-/**
- * Determines whether an account owns any buddies in a given contact
- *
- * @param contact The contact to search through.
- * @param account The account.
- *
- * @return TRUE if there are any buddies from account in the contact, or FALSE otherwise.
- */
-gboolean purple_contact_on_account(PurpleContact *contact, PurpleAccount *account);
-
-/**
- * Invalidates the priority buddy so that the next call to
- * purple_contact_get_priority_buddy recomputes it.
- *
- * @param contact The contact
- */
-void purple_contact_invalidate_priority_buddy(PurpleContact *contact);
-
-/**
- * Removes a buddy from the buddy list and frees the memory allocated to it.
- * This doesn't actually try to remove the buddy from the server list.
- *
- * @param buddy The buddy to be removed
- *
- * @see purple_account_remove_buddy
- */
-void purple_blist_remove_buddy(PurpleBuddy *buddy);
-
-/**
- * Removes a contact, and any buddies it contains, and frees the memory
- * allocated to it. This calls purple_blist_remove_buddy and therefore
- * doesn't remove the buddies from the server list.
- *
- * @param contact The contact to be removed
- *
- * @see purple_blist_remove_buddy
- */
-void purple_blist_remove_contact(PurpleContact *contact);
-
-/**
- * Removes a chat from the buddy list and frees the memory allocated to it.
- *
- * @param chat The chat to be removed
- */
-void purple_blist_remove_chat(PurpleChat *chat);
-
-/**
- * Removes a group from the buddy list and frees the memory allocated to it and to
- * its children
- *
- * @param group The group to be removed
- */
-void purple_blist_remove_group(PurpleGroup *group);
-
-/**
- * Returns the alias of a buddy.
- *
- * @param buddy The buddy whose name will be returned.
- * @return The alias (if set), server alias (if set),
- * or NULL.
- */
-const char *purple_buddy_get_alias_only(PurpleBuddy *buddy);
-
-/**
- * Gets the server alias for a buddy.
- *
- * @param buddy The buddy whose name will be returned
- * @return The server alias, or NULL if it is not set.
- */
-const char *purple_buddy_get_server_alias(PurpleBuddy *buddy);
-
-/**
- * Returns the correct name to display for a buddy, taking the contact alias
- * into account. In order of precedence: the buddy's alias; the buddy's
- * contact alias; the buddy's server alias; the buddy's user name.
- *
- * @param buddy The buddy whose name will be returned
- * @return The appropriate name or alias, or NULL.
- *
- */
-const char *purple_buddy_get_contact_alias(PurpleBuddy *buddy);
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_BLIST_C_)
-/**
- * Returns the correct alias for this user, ignoring server aliases. Used
- * when a user-recognizable name is required. In order: buddy's alias; buddy's
- * contact alias; buddy's user name.
- *
- * @param buddy The buddy whose alias will be returned.
- * @return The appropriate name or alias.
- * @deprecated Try purple_buddy_get_alias(), if server aliases are okay.
- */
-const char *purple_buddy_get_local_alias(PurpleBuddy *buddy);
-#endif
-
-/**
- * Returns the correct name to display for a buddy. In order of precedence:
- * the buddy's alias; the buddy's server alias; the buddy's contact alias;
- * the buddy's user name.
- *
- * @param buddy The buddy whose name will be returned.
- * @return The appropriate name or alias, or NULL
- */
-const char *purple_buddy_get_alias(PurpleBuddy *buddy);
-
-/**
- * Returns the local alias for the buddy, or @c NULL if none exists.
- *
- * @param buddy The buddy
- * @return The local alias for the buddy
- *
- * @since 2.6.0
- */
-const char *purple_buddy_get_local_buddy_alias(PurpleBuddy *buddy);
-
-/**
- * Returns the correct name to display for a blist chat.
- *
- * @param chat The chat whose name will be returned.
- * @return The alias (if set), or first component value.
- */
-const char *purple_chat_get_name(PurpleChat *chat);
-
-/**
- * Finds the buddy struct given a name and an account
- *
- * @param account The account this buddy belongs to
- * @param name The buddy's name
- * @return The buddy or NULL if the buddy does not exist
- */
-PurpleBuddy *purple_find_buddy(PurpleAccount *account, const char *name);
-
-/**
- * Finds the buddy struct given a name, an account, and a group
- *
- * @param account The account this buddy belongs to
- * @param name The buddy's name
- * @param group The group to look in
- * @return The buddy or NULL if the buddy does not exist in the group
- */
-PurpleBuddy *purple_find_buddy_in_group(PurpleAccount *account, const char *name,
- PurpleGroup *group);
-
-/**
- * Finds all PurpleBuddy structs given a name and an account
- *
- * @param account The account this buddy belongs to
- * @param name The buddy's name (or NULL to return all buddies for the account)
- *
- * @return A GSList of buddies (which must be freed), or NULL if the buddy doesn't exist
- */
-GSList *purple_find_buddies(PurpleAccount *account, const char *name);
-
-
-/**
- * Finds a group by name
- *
- * @param name The group's name
- * @return The group or NULL if the group does not exist
- */
-PurpleGroup *purple_find_group(const char *name);
-
-/**
- * Finds a chat by name.
- *
- * @param account The chat's account.
- * @param name The chat's name.
- *
- * @return The chat, or @c NULL if the chat does not exist.
- */
-PurpleChat *purple_blist_find_chat(PurpleAccount *account, const char *name);
-
-/**
- * Returns the group of which the chat is a member.
- *
- * @param chat The chat.
- *
- * @return The parent group, or @c NULL if the chat is not in a group.
- */
-PurpleGroup *purple_chat_get_group(PurpleChat *chat);
-
-/**
- * Returns the account the chat belongs to.
- *
- * @param chat The chat.
- *
- * @return The account the chat belongs to.
- *
- * @since 2.4.0
- */
-PurpleAccount *purple_chat_get_account(PurpleChat *chat);
-
-/**
- * Get a hashtable containing information about a chat.
- *
- * @param chat The chat.
- *
- * @constreturn The hashtable.
- *
- * @since 2.4.0
- */
-GHashTable *purple_chat_get_components(PurpleChat *chat);
-
-/**
- * Returns the group of which the buddy is a member.
- *
- * @param buddy The buddy
- * @return The group or NULL if the buddy is not in a group
- */
-PurpleGroup *purple_buddy_get_group(PurpleBuddy *buddy);
-
-
-/**
- * Returns a list of accounts that have buddies in this group
- *
- * @param g The group
- *
- * @return A GSList of accounts (which must be freed), or NULL if the group
- * has no accounts.
- */
-GSList *purple_group_get_accounts(PurpleGroup *g);
-
-/**
- * Determines whether an account owns any buddies in a given group
- *
- * @param g The group to search through.
- * @param account The account.
- *
- * @return TRUE if there are any buddies in the group, or FALSE otherwise.
- */
-gboolean purple_group_on_account(PurpleGroup *g, PurpleAccount *account);
-
-/**
- * Returns the name of a group.
- *
- * @param group The group.
- *
- * @return The name of the group.
- */
-const char *purple_group_get_name(PurpleGroup *group);
-
-/**
- * Called when an account connects. Tells the UI to update all the
- * buddies.
- *
- * @param account The account
- */
-void purple_blist_add_account(PurpleAccount *account);
-
-
-/**
- * Called when an account disconnects. Sets the presence of all the buddies to 0
- * and tells the UI to update them.
- *
- * @param account The account
- */
-void purple_blist_remove_account(PurpleAccount *account);
-
-
-/**
- * Determines the total size of a group
- *
- * @param group The group
- * @param offline Count buddies in offline accounts
- * @return The number of buddies in the group
- */
-int purple_blist_get_group_size(PurpleGroup *group, gboolean offline);
-
-/**
- * Determines the number of online buddies in a group
- *
- * @param group The group
- * @return The number of online buddies in the group, or 0 if the group is NULL
- */
-int purple_blist_get_group_online_count(PurpleGroup *group);
-
-/*@}*/
-
-/****************************************************************************************/
-/** @name Buddy list file management API */
-/****************************************************************************************/
-
-/**
- * Loads the buddy list from ~/.purple/blist.xml.
- */
-void purple_blist_load(void);
-
-/**
- * Schedule a save of the blist.xml file. This is used by the privacy
- * API whenever the privacy settings are changed. If you make a change
- * to blist.xml using one of the functions in the buddy list API, then
- * the buddy list is saved automatically, so you should not need to
- * call this.
- */
-void purple_blist_schedule_save(void);
-
-/**
- * Requests from the user information needed to add a buddy to the
- * buddy list.
- *
- * @param account The account the buddy is added to.
- * @param username The username of the buddy.
- * @param group The name of the group to place the buddy in.
- * @param alias The optional alias for the buddy.
- */
-void purple_blist_request_add_buddy(PurpleAccount *account, const char *username,
- const char *group, const char *alias);
-
-/**
- * Requests from the user information needed to add a chat to the
- * buddy list.
- *
- * @param account The account the buddy is added to.
- * @param group The optional group to add the chat to.
- * @param alias The optional alias for the chat.
- * @param name The required chat name.
- */
-void purple_blist_request_add_chat(PurpleAccount *account, PurpleGroup *group,
- const char *alias, const char *name);
-
-/**
- * Requests from the user information needed to add a group to the
- * buddy list.
- */
-void purple_blist_request_add_group(void);
-
-/**
- * Associates a boolean with a node in the buddy list
- *
- * @param node The node to associate the data with
- * @param key The identifier for the data
- * @param value The value to set
- */
-void purple_blist_node_set_bool(PurpleBlistNode *node, const char *key, gboolean value);
-
-/**
- * Retrieves a named boolean setting from a node in the buddy list
- *
- * @param node The node to retrieve the data from
- * @param key The identifier of the data
- *
- * @return The value, or FALSE if there is no setting
- */
-gboolean purple_blist_node_get_bool(PurpleBlistNode *node, const char *key);
-
-/**
- * Associates an integer with a node in the buddy list
- *
- * @param node The node to associate the data with
- * @param key The identifier for the data
- * @param value The value to set
- */
-void purple_blist_node_set_int(PurpleBlistNode *node, const char *key, int value);
-
-/**
- * Retrieves a named integer setting from a node in the buddy list
- *
- * @param node The node to retrieve the data from
- * @param key The identifier of the data
- *
- * @return The value, or 0 if there is no setting
- */
-int purple_blist_node_get_int(PurpleBlistNode *node, const char *key);
-
-/**
- * Associates a string with a node in the buddy list
- *
- * @param node The node to associate the data with
- * @param key The identifier for the data
- * @param value The value to set
- */
-void purple_blist_node_set_string(PurpleBlistNode *node, const char *key,
- const char *value);
-
-/**
- * Retrieves a named string setting from a node in the buddy list
- *
- * @param node The node to retrieve the data from
- * @param key The identifier of the data
- *
- * @return The value, or NULL if there is no setting
- */
-const char *purple_blist_node_get_string(PurpleBlistNode *node, const char *key);
-
-/**
- * Removes a named setting from a blist node
- *
- * @param node The node from which to remove the setting
- * @param key The name of the setting
- */
-void purple_blist_node_remove_setting(PurpleBlistNode *node, const char *key);
-
-/**
- * Set the flags for the given node. Setting a node's flags will overwrite
- * the old flags, so if you want to save them, you must first call
- * purple_blist_node_get_flags and modify that appropriately.
- *
- * @param node The node on which to set the flags.
- * @param flags The flags to set. This is a bitmask.
- */
-void purple_blist_node_set_flags(PurpleBlistNode *node, PurpleBlistNodeFlags flags);
-
-/**
- * Get the current flags on a given node.
- *
- * @param node The node from which to get the flags.
- *
- * @return The flags on the node. This is a bitmask.
- */
-PurpleBlistNodeFlags purple_blist_node_get_flags(PurpleBlistNode *node);
-
-/**
- * Get the type of a given node.
- *
- * @param node The node.
- *
- * @return The type of the node.
- *
- * @since 2.1.0
- */
-PurpleBlistNodeType purple_blist_node_get_type(PurpleBlistNode *node);
-
-/*@}*/
-
-/**
- * Retrieves the extended menu items for a buddy list node.
- * @param n The blist node for which to obtain the extended menu items.
- * @return A list of PurpleMenuAction items, as harvested by the
- * blist-node-extended-menu signal.
- */
-GList *purple_blist_node_get_extended_menu(PurpleBlistNode *n);
-
-/**************************************************************************/
-/** @name UI Registration Functions */
-/**************************************************************************/
-/*@{*/
-
-/**
- * Sets the UI operations structure to be used for the buddy list.
- *
- * @param ops The ops struct.
- */
-void purple_blist_set_ui_ops(PurpleBlistUiOps *ops);
-
-/**
- * Returns the UI operations structure to be used for the buddy list.
- *
- * @return The UI operations structure.
- */
-PurpleBlistUiOps *purple_blist_get_ui_ops(void);
-
-/*@}*/
-
-/**************************************************************************/
-/** @name Buddy List Subsystem */
-/**************************************************************************/
-/*@{*/
-
-/**
- * Returns the handle for the buddy list subsystem.
- *
- * @return The buddy list subsystem handle.
- */
-void *purple_blist_get_handle(void);
-
-/**
- * Initializes the buddy list subsystem.
- */
-void purple_blist_init(void);
-
-/**
- * Uninitializes the buddy list subsystem.
- */
-void purple_blist_uninit(void);
-
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _PURPLE_BLIST_H_ */
diff --git a/libpurple/blistnode.c b/libpurple/blistnode.c
new file mode 100644
index 0000000000..6c4568870d
--- /dev/null
+++ b/libpurple/blistnode.c
@@ -0,0 +1,662 @@
+/*
+ * 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 "glibcompat.h"
+#include "blistnodetypes.h"
+
+#define PURPLE_BLIST_NODE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_BLIST_NODE, PurpleBlistNodePrivate))
+
+typedef struct _PurpleBlistNodePrivate PurpleBlistNodePrivate;
+
+#define PURPLE_COUNTING_NODE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_COUNTING_NODE, PurpleCountingNodePrivate))
+
+typedef struct _PurpleCountingNodePrivate PurpleCountingNodePrivate;
+
+/* Private data of a buddy list node */
+struct _PurpleBlistNodePrivate {
+ GHashTable *settings; /* per-node settings */
+ gboolean transient; /* node should not be saved with the buddy list */
+};
+
+/* Blist node property enums */
+enum
+{
+ BLNODE_PROP_0,
+ BLNODE_PROP_TRANSIENT,
+ BLNODE_PROP_LAST
+};
+
+/* Private data of a counting node */
+struct _PurpleCountingNodePrivate {
+ int totalsize; /* The number of children under this node */
+ int currentsize; /* The number of children under this node
+ corresponding to online accounts */
+ int onlinecount; /* The number of children under this contact who are
+ currently online */
+};
+
+/* Counting node property enums */
+enum
+{
+ CNODE_PROP_0,
+ CNODE_PROP_TOTAL_SIZE,
+ CNODE_PROP_CURRENT_SIZE,
+ CNODE_PROP_ONLINE_COUNT,
+ CNODE_PROP_LAST
+};
+
+static GObjectClass *parent_class;
+
+static GParamSpec *bn_properties[BLNODE_PROP_LAST];
+static GParamSpec *cn_properties[CNODE_PROP_LAST];
+
+/**************************************************************************/
+/* Buddy list node API */
+/**************************************************************************/
+
+static PurpleBlistNode *get_next_node(PurpleBlistNode *node, gboolean godeep)
+{
+ if (node == NULL)
+ return NULL;
+
+ if (godeep && node->child)
+ return node->child;
+
+ if (node->next)
+ return node->next;
+
+ return get_next_node(node->parent, FALSE);
+}
+
+PurpleBlistNode *purple_blist_node_next(PurpleBlistNode *node, gboolean offline)
+{
+ PurpleBlistNode *ret = node;
+
+ if (offline)
+ return get_next_node(ret, TRUE);
+ do
+ {
+ ret = get_next_node(ret, TRUE);
+ } while (ret && PURPLE_IS_BUDDY(ret) &&
+ !purple_account_is_connected(purple_buddy_get_account((PurpleBuddy *)ret)));
+
+ return ret;
+}
+
+PurpleBlistNode *purple_blist_node_get_parent(PurpleBlistNode *node)
+{
+ return node ? node->parent : NULL;
+}
+
+PurpleBlistNode *purple_blist_node_get_first_child(PurpleBlistNode *node)
+{
+ return node ? node->child : NULL;
+}
+
+PurpleBlistNode *purple_blist_node_get_sibling_next(PurpleBlistNode *node)
+{
+ return node? node->next : NULL;
+}
+
+PurpleBlistNode *purple_blist_node_get_sibling_prev(PurpleBlistNode *node)
+{
+ return node? node->prev : NULL;
+}
+
+void *
+purple_blist_node_get_ui_data(const PurpleBlistNode *node)
+{
+ g_return_val_if_fail(PURPLE_IS_BLIST_NODE(node), NULL);
+
+ return node->ui_data;
+}
+
+void
+purple_blist_node_set_ui_data(PurpleBlistNode *node, void *ui_data) {
+ g_return_if_fail(PURPLE_IS_BLIST_NODE(node));
+
+ node->ui_data = ui_data;
+}
+
+void purple_blist_node_remove_setting(PurpleBlistNode *node, const char *key)
+{
+ PurpleBlistUiOps *ops;
+ PurpleBlistNodePrivate *priv = PURPLE_BLIST_NODE_GET_PRIVATE(node);
+
+ g_return_if_fail(priv != NULL);
+ 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);
+}
+
+void
+purple_blist_node_set_transient(PurpleBlistNode *node, gboolean transient)
+{
+ PurpleBlistNodePrivate *priv = PURPLE_BLIST_NODE_GET_PRIVATE(node);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->transient = transient;
+
+ g_object_notify_by_pspec(G_OBJECT(node),
+ bn_properties[BLNODE_PROP_TRANSIENT]);
+}
+
+gboolean
+purple_blist_node_is_transient(PurpleBlistNode *node)
+{
+ PurpleBlistNodePrivate *priv = PURPLE_BLIST_NODE_GET_PRIVATE(node);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return priv->transient;
+}
+
+GHashTable *
+purple_blist_node_get_settings(PurpleBlistNode *node)
+{
+ PurpleBlistNodePrivate *priv = PURPLE_BLIST_NODE_GET_PRIVATE(node);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->settings;
+}
+
+gboolean
+purple_blist_node_has_setting(PurpleBlistNode* node, const char *key)
+{
+ PurpleBlistNodePrivate *priv = PURPLE_BLIST_NODE_GET_PRIVATE(node);
+
+ g_return_val_if_fail(priv != NULL, FALSE);
+ 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);
+}
+
+void
+purple_blist_node_set_bool(PurpleBlistNode* node, const char *key, gboolean data)
+{
+ GValue *value;
+ PurpleBlistUiOps *ops;
+ PurpleBlistNodePrivate *priv = PURPLE_BLIST_NODE_GET_PRIVATE(node);
+
+ g_return_if_fail(priv != NULL);
+ 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);
+}
+
+gboolean
+purple_blist_node_get_bool(PurpleBlistNode* node, const char *key)
+{
+ GValue *value;
+ PurpleBlistNodePrivate *priv = PURPLE_BLIST_NODE_GET_PRIVATE(node);
+
+ g_return_val_if_fail(priv != NULL, FALSE);
+ 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);
+
+ if (value == NULL)
+ return FALSE;
+
+ g_return_val_if_fail(G_VALUE_HOLDS_BOOLEAN(value), FALSE);
+
+ return g_value_get_boolean(value);
+}
+
+void
+purple_blist_node_set_int(PurpleBlistNode* node, const char *key, int data)
+{
+ GValue *value;
+ PurpleBlistUiOps *ops;
+ PurpleBlistNodePrivate *priv = PURPLE_BLIST_NODE_GET_PRIVATE(node);
+
+ g_return_if_fail(priv != NULL);
+ 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);
+}
+
+int
+purple_blist_node_get_int(PurpleBlistNode* node, const char *key)
+{
+ GValue *value;
+ PurpleBlistNodePrivate *priv = PURPLE_BLIST_NODE_GET_PRIVATE(node);
+
+ g_return_val_if_fail(priv != NULL, 0);
+ 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);
+
+ if (value == NULL)
+ return 0;
+
+ g_return_val_if_fail(G_VALUE_HOLDS_INT(value), 0);
+
+ return g_value_get_int(value);
+}
+
+void
+purple_blist_node_set_string(PurpleBlistNode* node, const char *key, const char *data)
+{
+ GValue *value;
+ PurpleBlistUiOps *ops;
+ PurpleBlistNodePrivate *priv = PURPLE_BLIST_NODE_GET_PRIVATE(node);
+
+ g_return_if_fail(priv != NULL);
+ 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);
+}
+
+const char *
+purple_blist_node_get_string(PurpleBlistNode* node, const char *key)
+{
+ GValue *value;
+ PurpleBlistNodePrivate *priv = PURPLE_BLIST_NODE_GET_PRIVATE(node);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+ 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);
+
+ if (value == NULL)
+ return NULL;
+
+ g_return_val_if_fail(G_VALUE_HOLDS_STRING(value), NULL);
+
+ return g_value_get_string(value);
+}
+
+GList *
+purple_blist_node_get_extended_menu(PurpleBlistNode *n)
+{
+ GList *menu = NULL;
+
+ g_return_val_if_fail(n != NULL, NULL);
+
+ purple_signal_emit(purple_blist_get_handle(), "blist-node-extended-menu",
+ n, &menu);
+ return menu;
+}
+
+/**************************************************************************
+ * GObject code for PurpleBlistNode
+ **************************************************************************/
+
+/* Set method for GObject properties */
+static void
+purple_blist_node_set_property(GObject *obj, guint param_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleBlistNode *node = PURPLE_BLIST_NODE(obj);
+
+ switch (param_id) {
+ case BLNODE_PROP_TRANSIENT:
+ purple_blist_node_set_transient(node, g_value_get_boolean(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* Get method for GObject properties */
+static void
+purple_blist_node_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleBlistNode *node = PURPLE_BLIST_NODE(obj);
+
+ switch (param_id) {
+ case BLNODE_PROP_TRANSIENT:
+ g_value_set_boolean(value, purple_blist_node_is_transient(node));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* GObject initialization function */
+static void
+purple_blist_node_init(GTypeInstance *instance, gpointer klass)
+{
+ PurpleBlistNodePrivate *priv = PURPLE_BLIST_NODE_GET_PRIVATE(instance);
+
+ priv->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+ (GDestroyNotify)purple_value_free);
+}
+
+/* GObject finalize function */
+static void
+purple_blist_node_finalize(GObject *object)
+{
+ PurpleBlistNodePrivate *priv = PURPLE_BLIST_NODE_GET_PRIVATE(object);
+
+ g_hash_table_destroy(priv->settings);
+
+ parent_class->finalize(object);
+}
+
+/* Class initializer function */
+static void
+purple_blist_node_class_init(PurpleBlistNodeClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ obj_class->finalize = purple_blist_node_finalize;
+
+ /* Setup properties */
+ obj_class->get_property = purple_blist_node_get_property;
+ obj_class->set_property = purple_blist_node_set_property;
+
+ g_type_class_add_private(klass, sizeof(PurpleBlistNodePrivate));
+
+ bn_properties[BLNODE_PROP_TRANSIENT] = g_param_spec_boolean("transient",
+ "Transient",
+ "Whether node should not be saved with the buddy list.",
+ FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, BLNODE_PROP_LAST,
+ bn_properties);
+}
+
+GType
+purple_blist_node_get_type(void)
+{
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleBlistNodeClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_blist_node_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleBlistNode),
+ 0,
+ (GInstanceInitFunc)purple_blist_node_init,
+ NULL,
+ };
+
+ type = g_type_register_static(G_TYPE_OBJECT, "PurpleBlistNode",
+ &info, G_TYPE_FLAG_ABSTRACT);
+ }
+
+ return type;
+}
+
+/**************************************************************************/
+/* Counting node API */
+/**************************************************************************/
+
+int
+purple_counting_node_get_total_size(PurpleCountingNode *counter)
+{
+ PurpleCountingNodePrivate *priv = PURPLE_COUNTING_NODE_GET_PRIVATE(counter);
+
+ g_return_val_if_fail(priv != NULL, -1);
+
+ return priv->totalsize;
+}
+
+int
+purple_counting_node_get_current_size(PurpleCountingNode *counter)
+{
+ PurpleCountingNodePrivate *priv = PURPLE_COUNTING_NODE_GET_PRIVATE(counter);
+
+ g_return_val_if_fail(priv != NULL, -1);
+
+ return priv->currentsize;
+}
+
+int
+purple_counting_node_get_online_count(PurpleCountingNode *counter)
+{
+ PurpleCountingNodePrivate *priv = PURPLE_COUNTING_NODE_GET_PRIVATE(counter);
+
+ g_return_val_if_fail(priv != NULL, -1);
+
+ return priv->onlinecount;
+}
+
+void
+purple_counting_node_change_total_size(PurpleCountingNode *counter, int delta)
+{
+ PurpleCountingNodePrivate *priv = PURPLE_COUNTING_NODE_GET_PRIVATE(counter);
+
+ g_return_if_fail(priv != NULL);
+
+ 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_PRIVATE(counter);
+
+ g_return_if_fail(priv != NULL);
+
+ 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_PRIVATE(counter);
+
+ g_return_if_fail(priv != NULL);
+
+ 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_PRIVATE(counter);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->totalsize = totalsize;
+
+ g_object_notify_by_pspec(G_OBJECT(counter),
+ cn_properties[CNODE_PROP_TOTAL_SIZE]);
+}
+
+void
+purple_counting_node_set_current_size(PurpleCountingNode *counter, int currentsize)
+{
+ PurpleCountingNodePrivate *priv = PURPLE_COUNTING_NODE_GET_PRIVATE(counter);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->currentsize = currentsize;
+
+ g_object_notify_by_pspec(G_OBJECT(counter),
+ cn_properties[CNODE_PROP_CURRENT_SIZE]);
+}
+
+void
+purple_counting_node_set_online_count(PurpleCountingNode *counter, int onlinecount)
+{
+ PurpleCountingNodePrivate *priv = PURPLE_COUNTING_NODE_GET_PRIVATE(counter);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->onlinecount = onlinecount;
+
+ g_object_notify_by_pspec(G_OBJECT(counter),
+ cn_properties[CNODE_PROP_ONLINE_COUNT]);
+}
+
+/**************************************************************************
+ * GObject code for PurpleCountingNode
+ **************************************************************************/
+
+/* Set method for GObject properties */
+static void
+purple_counting_node_set_property(GObject *obj, guint param_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleCountingNode *node = PURPLE_COUNTING_NODE(obj);
+
+ switch (param_id) {
+ case CNODE_PROP_TOTAL_SIZE:
+ purple_counting_node_set_total_size(node, g_value_get_int(value));
+ break;
+ case CNODE_PROP_CURRENT_SIZE:
+ purple_counting_node_set_current_size(node, g_value_get_int(value));
+ break;
+ case CNODE_PROP_ONLINE_COUNT:
+ purple_counting_node_set_online_count(node, g_value_get_int(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* Get method for GObject properties */
+static void
+purple_counting_node_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleCountingNode *node = PURPLE_COUNTING_NODE(obj);
+
+ switch (param_id) {
+ case CNODE_PROP_TOTAL_SIZE:
+ g_value_set_int(value, purple_counting_node_get_total_size(node));
+ break;
+ case CNODE_PROP_CURRENT_SIZE:
+ g_value_set_int(value, purple_counting_node_get_current_size(node));
+ break;
+ case CNODE_PROP_ONLINE_COUNT:
+ g_value_set_int(value, purple_counting_node_get_online_count(node));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* Class initializer function */
+static void
+purple_counting_node_class_init(PurpleCountingNodeClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ /* Setup properties */
+ obj_class->get_property = purple_counting_node_get_property;
+ obj_class->set_property = purple_counting_node_set_property;
+
+ g_type_class_add_private(klass, sizeof(PurpleCountingNodePrivate));
+
+ cn_properties[CNODE_PROP_TOTAL_SIZE] = g_param_spec_int("total-size",
+ "Total size",
+ "The number of children under this node.",
+ G_MININT, G_MAXINT, 0, G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
+
+ cn_properties[CNODE_PROP_CURRENT_SIZE] = g_param_spec_int("current-size",
+ "Current size",
+ "The number of children with online accounts.",
+ G_MININT, G_MAXINT, 0, G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
+
+ cn_properties[CNODE_PROP_ONLINE_COUNT] = g_param_spec_int("online-count",
+ "Online count",
+ "The number of children that are online.",
+ G_MININT, G_MAXINT, 0, G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, CNODE_PROP_LAST,
+ cn_properties);
+}
+
+GType
+purple_counting_node_get_type(void)
+{
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleCountingNodeClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_counting_node_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleCountingNode),
+ 0,
+ NULL,
+ NULL,
+ };
+
+ type = g_type_register_static(PURPLE_TYPE_BLIST_NODE,
+ "PurpleCountingNode",
+ &info, G_TYPE_FLAG_ABSTRACT);
+ }
+
+ return type;
+}
diff --git a/libpurple/blistnode.h b/libpurple/blistnode.h
new file mode 100644
index 0000000000..4b55f75839
--- /dev/null
+++ b/libpurple/blistnode.h
@@ -0,0 +1,462 @@
+/* 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 _PURPLE_BLIST_NODE_H_
+#define _PURPLE_BLIST_NODE_H_
+/**
+ * SECTION:blistnode
+ * @section_id: libpurple-blistnode
+ * @short_description: <filename>blistnode.h</filename>
+ * @title: Buddy List Node and Counting Node types
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define PURPLE_TYPE_BLIST_NODE (purple_blist_node_get_type())
+#define PURPLE_BLIST_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_BLIST_NODE, PurpleBlistNode))
+#define PURPLE_BLIST_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_BLIST_NODE, PurpleBlistNodeClass))
+#define PURPLE_IS_BLIST_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_BLIST_NODE))
+#define PURPLE_IS_BLIST_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_BLIST_NODE))
+#define PURPLE_BLIST_NODE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_BLIST_NODE, PurpleBlistNodeClass))
+
+typedef struct _PurpleBlistNode PurpleBlistNode;
+typedef struct _PurpleBlistNodeClass PurpleBlistNodeClass;
+
+#define PURPLE_TYPE_COUNTING_NODE (purple_counting_node_get_type())
+#define PURPLE_COUNTING_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_COUNTING_NODE, PurpleCountingNode))
+#define PURPLE_COUNTING_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_COUNTING_NODE, PurpleCountingNodeClass))
+#define PURPLE_IS_COUNTING_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_COUNTING_NODE))
+#define PURPLE_IS_COUNTING_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_COUNTING_NODE))
+#define PURPLE_COUNTING_NODE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_COUNTING_NODE, PurpleCountingNodeClass))
+
+typedef struct _PurpleCountingNode PurpleCountingNode;
+typedef struct _PurpleCountingNodeClass PurpleCountingNodeClass;
+
+/**************************************************************************/
+/* Data Structures */
+/**************************************************************************/
+
+/**
+ * PurpleBlistNode:
+ * @prev: The sibling before this buddy.
+ * @next: The sibling after this buddy.
+ * @parent: The parent of this node.
+ * @child: The child of this node.
+ * @ui_data: The UI data associated with this node. This is a convenience
+ * field provided to the UIs -- it is not used by the libpurple core.
+ *
+ * A Buddy list node. This can represent a group, a buddy, or anything else.
+ * This is a base class for PurpleBuddy, PurpleContact, PurpleGroup, and for
+ * anything else that wants to put itself in the buddy list.
+ */
+struct _PurpleBlistNode {
+ GObject gparent;
+
+ /*< public >*/
+ PurpleBlistNode *prev;
+ PurpleBlistNode *next;
+ PurpleBlistNode *parent;
+ PurpleBlistNode *child;
+
+ gpointer ui_data;
+};
+
+/**
+ * PurpleBlistNodeClass:
+ *
+ * The base class for all #PurpleBlistNode's.
+ */
+struct _PurpleBlistNodeClass {
+ GObjectClass gparent_class;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+/**
+ * PurpleCountingNode:
+ *
+ * A node that keeps count of the number of children that it has. It tracks the
+ * total number of children, the number of children corresponding to online
+ * accounts, and the number of online children.
+ *
+ * The two types of counting nodes are:
+ * <orderedlist>
+ * <listitem>Contact: Keeps track of the number of buddies under it.</listitem>
+ * <listitem>Group: Keeps track of the number of chats and contacts under it.
+ * </listitem>
+ * </orderedlist>
+ *
+ * See #PurpleContact, #PurpleGroup
+ */
+struct _PurpleCountingNode {
+ PurpleBlistNode node;
+};
+
+/**
+ * PurpleCountingNodeClass:
+ *
+ * The base class for all #PurpleCountingNode's.
+ */
+struct _PurpleCountingNodeClass {
+ PurpleBlistNodeClass node_class;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+/**************************************************************************/
+/* Buddy list node API */
+/**************************************************************************/
+
+/**
+ * purple_blist_node_get_type:
+ *
+ * Returns: The #GType for the #PurpleBlistNode object.
+ */
+GType purple_blist_node_get_type(void);
+
+/**
+ * purple_blist_node_next:
+ * @node: A node.
+ * @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.
+ *
+ * See purple_blist_node_get_parent(), purple_blist_node_get_first_child(),
+ * purple_blist_node_get_sibling_next(), purple_blist_node_get_sibling_prev().
+ *
+ * Returns: The next node
+ */
+PurpleBlistNode *purple_blist_node_next(PurpleBlistNode *node, gboolean offline);
+
+/**
+ * purple_blist_node_get_parent:
+ * @node: A node.
+ *
+ * Returns the parent node of a given node.
+ *
+ * See purple_blist_node_get_first_child(), purple_blist_node_get_sibling_next(),
+ * purple_blist_node_get_sibling_prev(), purple_blist_node_next().
+ *
+ * Returns: The parent node.
+ */
+PurpleBlistNode *purple_blist_node_get_parent(PurpleBlistNode *node);
+
+/**
+ * purple_blist_node_get_first_child:
+ * @node: A node.
+ *
+ * Returns the the first child node of a given node.
+ *
+ * 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.
+ */
+PurpleBlistNode *purple_blist_node_get_first_child(PurpleBlistNode *node);
+
+/**
+ * purple_blist_node_get_sibling_next:
+ * @node: A node.
+ *
+ * Returns the sibling node of a given node.
+ *
+ * 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.
+ */
+PurpleBlistNode *purple_blist_node_get_sibling_next(PurpleBlistNode *node);
+
+/**
+ * purple_blist_node_get_sibling_prev:
+ * @node: A node.
+ *
+ * Returns the previous sibling node of a given node.
+ *
+ * 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.
+ */
+PurpleBlistNode *purple_blist_node_get_sibling_prev(PurpleBlistNode *node);
+
+/**
+ * purple_blist_node_get_ui_data:
+ * @node: The node.
+ *
+ * Returns the UI data of a given node.
+ *
+ * Returns: The UI data.
+ */
+gpointer purple_blist_node_get_ui_data(const PurpleBlistNode *node);
+
+/**
+ * purple_blist_node_set_ui_data:
+ * @node: The node.
+ * @ui_data: The UI data.
+ *
+ * Sets the UI data of a given node.
+ */
+void purple_blist_node_set_ui_data(PurpleBlistNode *node, gpointer ui_data);
+
+/**
+ * purple_blist_node_get_settings:
+ * @node: The node to from which to get settings
+ *
+ * Returns a node's settings
+ *
+ * Returns: The hash table with the node's settings
+ */
+GHashTable *purple_blist_node_get_settings(PurpleBlistNode *node);
+
+/**
+ * purple_blist_node_has_setting:
+ * @node: The node to check from which to check settings
+ * @key: The identifier of the data
+ *
+ * Checks whether a named setting exists for a node in the buddy list
+ *
+ * Returns: TRUE if a value exists, or FALSE if there is no setting
+ */
+gboolean purple_blist_node_has_setting(PurpleBlistNode *node, const char *key);
+
+/**
+ * purple_blist_node_set_bool:
+ * @node: The node to associate the data with
+ * @key: The identifier for the data
+ * @value: The value to set
+ *
+ * Associates a boolean with a node in the buddy list
+ */
+void purple_blist_node_set_bool(PurpleBlistNode *node, const char *key, gboolean value);
+
+/**
+ * purple_blist_node_get_bool:
+ * @node: The node to retrieve the data from
+ * @key: The identifier of the data
+ *
+ * Retrieves a named boolean setting from a node in the buddy list
+ *
+ * Returns: The value, or FALSE if there is no setting
+ */
+gboolean purple_blist_node_get_bool(PurpleBlistNode *node, const char *key);
+
+/**
+ * purple_blist_node_set_int:
+ * @node: The node to associate the data with
+ * @key: The identifier for the data
+ * @value: The value to set
+ *
+ * Associates an integer with a node in the buddy list
+ */
+void purple_blist_node_set_int(PurpleBlistNode *node, const char *key, int value);
+
+/**
+ * purple_blist_node_get_int:
+ * @node: The node to retrieve the data from
+ * @key: The identifier of the data
+ *
+ * Retrieves a named integer setting from a node in the buddy list
+ *
+ * Returns: The value, or 0 if there is no setting
+ */
+int purple_blist_node_get_int(PurpleBlistNode *node, const char *key);
+
+/**
+ * purple_blist_node_set_string:
+ * @node: The node to associate the data with
+ * @key: The identifier for the data
+ * @value: The value to set
+ *
+ * Associates a string with a node in the buddy list
+ */
+void purple_blist_node_set_string(PurpleBlistNode *node, const char *key,
+ const char *value);
+
+/**
+ * purple_blist_node_get_string:
+ * @node: The node to retrieve the data from
+ * @key: The identifier of the data
+ *
+ * Retrieves a named string setting from a node in the buddy list
+ *
+ * Returns: The value, or NULL if there is no setting
+ */
+const char *purple_blist_node_get_string(PurpleBlistNode *node, const char *key);
+
+/**
+ * purple_blist_node_remove_setting:
+ * @node: The node from which to remove the setting
+ * @key: The name of the setting
+ *
+ * Removes a named setting from a blist node
+ */
+void purple_blist_node_remove_setting(PurpleBlistNode *node, const char *key);
+
+/**
+ * purple_blist_node_set_transient:
+ * @node: The node
+ * @transient: TRUE if the node should NOT be saved, FALSE if node should
+ * be saved
+ *
+ * Sets whether the node should be saved with the buddy list or not
+ */
+void purple_blist_node_set_transient(PurpleBlistNode *node, gboolean transient);
+
+/**
+ * purple_blist_node_is_transient:
+ * @node: The node
+ *
+ * Gets whether the node should be saved with the buddy list or not
+ *
+ * Returns: TRUE if the node should NOT be saved, FALSE if node should be saved
+ */
+gboolean purple_blist_node_is_transient(PurpleBlistNode *node);
+
+/**
+ * purple_blist_node_get_extended_menu:
+ * @n: The blist node for which to obtain the extended menu items.
+ *
+ * Retrieves the extended menu items for a buddy list node.
+ *
+ * Returns: A list of PurpleMenuAction items, as harvested by the
+ * blist-node-extended-menu signal.
+ */
+GList *purple_blist_node_get_extended_menu(PurpleBlistNode *n);
+
+/**************************************************************************/
+/* Counting node API */
+/**************************************************************************/
+
+/**
+ * purple_counting_node_get_type:
+ *
+ * Returns: The #GType for the #PurpleCountingNode object.
+ */
+GType purple_counting_node_get_type(void);
+
+/**
+ * purple_counting_node_get_total_size:
+ * @counter: The node
+ *
+ * Returns the total number of children of the counting node.
+ *
+ * Returns: The total number of children of the node
+ */
+int purple_counting_node_get_total_size(PurpleCountingNode *counter);
+
+/**
+ * purple_counting_node_get_current_size:
+ * @counter: The node
+ *
+ * Returns the number of children of the counting node corresponding to online
+ * accounts.
+ *
+ * Returns: The number of children with online accounts
+ */
+int purple_counting_node_get_current_size(PurpleCountingNode *counter);
+
+/**
+ * purple_counting_node_get_online_count:
+ * @counter: The node
+ *
+ * Returns the number of children of the counting node that are online.
+ *
+ * Returns: The total number of online children
+ */
+int purple_counting_node_get_online_count(PurpleCountingNode *counter);
+
+/**
+ * purple_counting_node_change_total_size:
+ * @counter: The node
+ * @delta: The value to change the total size by
+ *
+ * Changes the total number of children of the counting node. The provided
+ * delta value is added to the count, or if it's negative, the count is
+ * decreased.
+ */
+void purple_counting_node_change_total_size(PurpleCountingNode *counter, int delta);
+
+/**
+ * purple_counting_node_change_current_size:
+ * @counter: The node
+ * @delta: The value to change the current size by
+ *
+ * Changes the number of children of the counting node corresponding to online
+ * accounts. The provided delta value is added to the count, or if it's
+ * negative, the count is decreased.
+ */
+void purple_counting_node_change_current_size(PurpleCountingNode *counter, int delta);
+
+/**
+ * purple_counting_node_change_online_count:
+ * @counter: The node
+ * @delta: The value to change the online count by
+ *
+ * Changes the number of children of the counting node that are online. The
+ * provided delta value is added to the count, or if it's negative, the count is
+ * decreased.
+ */
+void purple_counting_node_change_online_count(PurpleCountingNode *counter, int delta);
+
+/**
+ * purple_counting_node_set_total_size:
+ * @counter: The node
+ * @totalsize: The total number of children of the node
+ *
+ * Sets the total number of children of the counting node.
+ */
+void purple_counting_node_set_total_size(PurpleCountingNode *counter, int totalsize);
+
+/**
+ * purple_counting_node_set_current_size:
+ * @counter: The node
+ * @currentsize: The number of children with online accounts
+ *
+ * Sets the number of children of the counting node corresponding to online
+ * accounts.
+ */
+void purple_counting_node_set_current_size(PurpleCountingNode *counter, int currentsize);
+
+/**
+ * purple_counting_node_set_online_count:
+ * @counter: The node
+ * @onlinecount: The total number of online children
+ *
+ * Sets the number of children of the counting node that are online.
+ */
+void purple_counting_node_set_online_count(PurpleCountingNode *counter, int onlinecount);
+
+G_END_DECLS
+
+#endif /* _PURPLE_BLIST_NODE_H_ */
diff --git a/libpurple/blistnodetypes.c b/libpurple/blistnodetypes.c
new file mode 100644
index 0000000000..f1ac266485
--- /dev/null
+++ b/libpurple/blistnodetypes.c
@@ -0,0 +1,1738 @@
+/*
+ * 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 "glibcompat.h"
+#include "dbus-maybe.h"
+#include "debug.h"
+
+#define PURPLE_BUDDY_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_BUDDY, PurpleBuddyPrivate))
+
+typedef struct _PurpleBuddyPrivate PurpleBuddyPrivate;
+
+#define PURPLE_CONTACT_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_CONTACT, PurpleContactPrivate))
+
+typedef struct _PurpleContactPrivate PurpleContactPrivate;
+
+#define PURPLE_GROUP_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_GROUP, PurpleGroupPrivate))
+
+typedef struct _PurpleGroupPrivate PurpleGroupPrivate;
+
+#define PURPLE_CHAT_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_CHAT, PurpleChatPrivate))
+
+typedef struct _PurpleChatPrivate PurpleChatPrivate;
+
+/**************************************************************************/
+/* Private data */
+/**************************************************************************/
+/* Private data for a buddy. */
+struct _PurpleBuddyPrivate {
+ char *name; /* The name of the buddy. */
+ char *local_alias; /* The user-set alias of the buddy */
+ char *server_alias; /* The server-specified alias of the buddy.
+ (i.e. MSN "Friendly Names") */
+ void *proto_data; /* This allows the prpl to associate
+ whatever data it wants with a buddy. */
+ PurpleBuddyIcon *icon; /* The buddy icon. */
+ PurpleAccount *account; /* the account this buddy belongs to */
+ PurplePresence *presence; /* Presense information of the buddy */
+ PurpleMediaCaps media_caps; /* The media capabilities of the buddy. */
+
+ gboolean is_constructed; /* Indicates if the buddy has finished
+ being constructed. */
+};
+
+/* Buddy property enums */
+enum
+{
+ BUDDY_PROP_0,
+ BUDDY_PROP_NAME,
+ BUDDY_PROP_LOCAL_ALIAS,
+ BUDDY_PROP_SERVER_ALIAS,
+ BUDDY_PROP_ICON,
+ BUDDY_PROP_ACCOUNT,
+ BUDDY_PROP_PRESENCE,
+ BUDDY_PROP_MEDIA_CAPS,
+ BUDDY_PROP_LAST
+};
+
+/* Private data for a contact */
+struct _PurpleContactPrivate {
+ char *alias; /* The user-set alias of the contact */
+ PurpleBuddy *priority_buddy; /* The "top" buddy for this contact */
+ gboolean priority_valid; /* Is priority valid? */
+};
+
+/* Contact property enums */
+enum
+{
+ CONTACT_PROP_0,
+ CONTACT_PROP_ALIAS,
+ CONTACT_PROP_PRIORITY_BUDDY,
+ CONTACT_PROP_LAST
+};
+
+/* Private data for a group */
+struct _PurpleGroupPrivate {
+ char *name; /* The name of this group. */
+ gboolean is_constructed; /* Indicates if the group has finished being
+ constructed. */
+};
+
+/* Group property enums */
+enum
+{
+ GROUP_PROP_0,
+ GROUP_PROP_NAME,
+ GROUP_PROP_LAST
+};
+
+/* Private data for a chat node */
+struct _PurpleChatPrivate {
+ char *alias; /* The display name of this chat. */
+ PurpleAccount *account; /* The account this chat is attached to */
+ GHashTable *components; /* the stuff the protocol needs to know to
+ join the chat */
+
+ gboolean is_constructed; /* Indicates if the chat has finished being
+ constructed. */
+};
+
+/* Chat property enums */
+enum
+{
+ CHAT_PROP_0,
+ CHAT_PROP_ALIAS,
+ CHAT_PROP_ACCOUNT,
+ CHAT_PROP_COMPONENTS,
+ CHAT_PROP_LAST
+};
+
+static PurpleBlistNode *blistnode_parent_class;
+static PurpleCountingNode *counting_parent_class;
+
+static GParamSpec *bd_properties[BUDDY_PROP_LAST];
+static GParamSpec *co_properties[CONTACT_PROP_LAST];
+static GParamSpec *gr_properties[GROUP_PROP_LAST];
+static GParamSpec *ch_properties[CHAT_PROP_LAST];
+
+static gboolean
+purple_strings_are_different(const char *one, const char *two)
+{
+ return !((one && two && g_utf8_collate(one, two) == 0) ||
+ ((one == NULL || *one == '\0') && (two == NULL || *two == '\0')));
+}
+
+/**************************************************************************/
+/* Buddy API */
+/**************************************************************************/
+
+void
+purple_buddy_set_icon(PurpleBuddy *buddy, PurpleBuddyIcon *icon)
+{
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy);
+
+ g_return_if_fail(priv != NULL);
+
+ if (priv->icon != icon)
+ {
+ purple_buddy_icon_unref(priv->icon);
+ priv->icon = (icon != NULL ? purple_buddy_icon_ref(icon) : NULL);
+
+ g_object_notify_by_pspec(G_OBJECT(buddy),
+ bd_properties[BUDDY_PROP_ICON]);
+ }
+
+ 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));
+}
+
+PurpleBuddyIcon *
+purple_buddy_get_icon(const PurpleBuddy *buddy)
+{
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->icon;
+}
+
+PurpleAccount *
+purple_buddy_get_account(const PurpleBuddy *buddy)
+{
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->account;
+}
+
+void
+purple_buddy_set_name(PurpleBuddy *buddy, const char *name)
+{
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy);
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+
+ g_return_if_fail(priv != NULL);
+
+ purple_blist_update_buddies_cache(buddy, name);
+
+ g_free(priv->name);
+ priv->name = purple_utf8_strip_unprintables(name);
+
+ g_object_notify_by_pspec(G_OBJECT(buddy), bd_properties[BUDDY_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));
+ }
+}
+
+const char *
+purple_buddy_get_name(const PurpleBuddy *buddy)
+{
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->name;
+}
+
+gpointer
+purple_buddy_get_protocol_data(const PurpleBuddy *buddy)
+{
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->proto_data;
+}
+
+void
+purple_buddy_set_protocol_data(PurpleBuddy *buddy, gpointer data)
+{
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->proto_data = data;
+}
+
+const char *purple_buddy_get_alias_only(PurpleBuddy *buddy)
+{
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ if ((priv->local_alias != NULL) && (*priv->local_alias != '\0')) {
+ return priv->local_alias;
+ } else if ((priv->server_alias != NULL) &&
+ (*priv->server_alias != '\0')) {
+
+ return priv->server_alias;
+ }
+
+ return NULL;
+}
+
+const char *purple_buddy_get_contact_alias(PurpleBuddy *buddy)
+{
+ PurpleContact *c;
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ /* Search for an alias for the buddy. In order of precedence: */
+ /* The local buddy alias */
+ if (priv->local_alias != NULL)
+ return priv->local_alias;
+
+ /* The contact alias */
+ c = purple_buddy_get_contact(buddy);
+ if ((c != NULL) && (purple_contact_get_alias(c) != NULL))
+ return purple_contact_get_alias(c);
+
+ /* The server alias */
+ if ((priv->server_alias) && (*priv->server_alias))
+ return priv->server_alias;
+
+ /* The buddy's user name (i.e. no alias) */
+ return priv->name;
+}
+
+const char *purple_buddy_get_alias(PurpleBuddy *buddy)
+{
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ /* Search for an alias for the buddy. In order of precedence: */
+ /* The buddy alias */
+ if (priv->local_alias != NULL)
+ return priv->local_alias;
+
+ /* The server alias */
+ if ((priv->server_alias) && (*priv->server_alias))
+ return priv->server_alias;
+
+ /* The buddy's user name (i.e. no alias) */
+ return priv->name;
+}
+
+void
+purple_buddy_set_local_alias(PurpleBuddy *buddy, const char *alias)
+{
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+ PurpleIMConversation *im;
+ char *old_alias;
+ char *new_alias = NULL;
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy);
+
+ g_return_if_fail(priv != NULL);
+
+ if ((alias != NULL) && (*alias != '\0'))
+ new_alias = purple_utf8_strip_unprintables(alias);
+
+ if (!purple_strings_are_different(priv->local_alias, new_alias)) {
+ g_free(new_alias);
+ return;
+ }
+
+ old_alias = priv->local_alias;
+
+ if ((new_alias != NULL) && (*new_alias != '\0'))
+ priv->local_alias = new_alias;
+ else {
+ priv->local_alias = NULL;
+ g_free(new_alias); /* could be "\0" */
+ }
+
+ g_object_notify_by_pspec(G_OBJECT(buddy),
+ bd_properties[BUDDY_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));
+
+ im = purple_conversations_find_im_with_account(priv->name,
+ priv->account);
+ if (im)
+ purple_conversation_autoset_title(PURPLE_CONVERSATION(im));
+
+ purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
+ buddy, old_alias);
+ g_free(old_alias);
+}
+
+const char *purple_buddy_get_local_alias(PurpleBuddy *buddy)
+{
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->local_alias;
+}
+
+void
+purple_buddy_set_server_alias(PurpleBuddy *buddy, const char *alias)
+{
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+ PurpleIMConversation *im;
+ char *old_alias;
+ char *new_alias = NULL;
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy);
+
+ g_return_if_fail(priv != NULL);
+
+ if ((alias != NULL) && (*alias != '\0') && g_utf8_validate(alias, -1, NULL))
+ new_alias = purple_utf8_strip_unprintables(alias);
+
+ if (!purple_strings_are_different(priv->server_alias, new_alias)) {
+ g_free(new_alias);
+ return;
+ }
+
+ old_alias = priv->server_alias;
+
+ if ((new_alias != NULL) && (*new_alias != '\0'))
+ priv->server_alias = new_alias;
+ else {
+ priv->server_alias = NULL;
+ g_free(new_alias); /* could be "\0"; */
+ }
+
+ g_object_notify_by_pspec(G_OBJECT(buddy),
+ bd_properties[BUDDY_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));
+ }
+
+ im = purple_conversations_find_im_with_account(priv->name,
+ priv->account);
+ if (im)
+ purple_conversation_autoset_title(PURPLE_CONVERSATION(im));
+
+ purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
+ buddy, old_alias);
+ g_free(old_alias);
+}
+
+const char *purple_buddy_get_server_alias(PurpleBuddy *buddy)
+{
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ if ((priv->server_alias) && (*priv->server_alias))
+ return priv->server_alias;
+
+ return NULL;
+}
+
+PurpleContact *purple_buddy_get_contact(PurpleBuddy *buddy)
+{
+ g_return_val_if_fail(PURPLE_IS_BUDDY(buddy), NULL);
+
+ return PURPLE_CONTACT(PURPLE_BLIST_NODE(buddy)->parent);
+}
+
+PurplePresence *purple_buddy_get_presence(const PurpleBuddy *buddy)
+{
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->presence;
+}
+
+void
+purple_buddy_update_status(PurpleBuddy *buddy, PurpleStatus *old_status)
+{
+ PurpleStatus *status;
+ PurpleBlistNode *cnode;
+ PurpleContact *contact;
+ PurpleCountingNode *contact_counter, *group_counter;
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy);
+
+ g_return_if_fail(priv != NULL);
+
+ status = purple_presence_get_active_status(priv->presence);
+
+ purple_debug_info("blistnodetypes", "Updating buddy status for %s (%s)\n",
+ priv->name, purple_account_get_protocol_name(priv->account));
+
+ if (purple_status_is_online(status) &&
+ !purple_status_is_online(old_status)) {
+
+ purple_signal_emit(purple_blist_get_handle(), "buddy-signed-on", buddy);
+
+ cnode = PURPLE_BLIST_NODE(buddy)->parent;
+ contact = PURPLE_CONTACT(cnode);
+ contact_counter = PURPLE_COUNTING_NODE(contact);
+ group_counter = PURPLE_COUNTING_NODE(cnode->parent);
+
+ purple_counting_node_change_online_count(contact_counter, +1);
+ if (purple_counting_node_get_online_count(contact_counter) == 1)
+ purple_counting_node_change_online_count(group_counter, +1);
+ } else if (!purple_status_is_online(status) &&
+ purple_status_is_online(old_status)) {
+
+ purple_blist_node_set_int(PURPLE_BLIST_NODE(buddy), "last_seen", time(NULL));
+ purple_signal_emit(purple_blist_get_handle(), "buddy-signed-off", buddy);
+
+ cnode = PURPLE_BLIST_NODE(buddy)->parent;
+ contact = PURPLE_CONTACT(cnode);
+ contact_counter = PURPLE_COUNTING_NODE(contact);
+ group_counter = PURPLE_COUNTING_NODE(cnode->parent);
+
+ purple_counting_node_change_online_count(contact_counter, -1);
+ if (purple_counting_node_get_online_count(contact_counter) == 0)
+ purple_counting_node_change_online_count(group_counter, -1);
+ } else {
+ purple_signal_emit(purple_blist_get_handle(),
+ "buddy-status-changed", buddy, old_status,
+ status);
+ }
+
+ /*
+ * This function used to only call the following two functions if one of
+ * the above signals had been triggered, but that's not good, because
+ * if someone's away message changes and they don't go from away to back
+ * to away then no signal is triggered.
+ *
+ * It's a safe assumption that SOMETHING called this function. PROBABLY
+ * because something, somewhere changed. Calling the stuff below
+ * certainly won't hurt anything. Unless you're on a K6-2 300.
+ */
+ 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));
+}
+
+PurpleMediaCaps purple_buddy_get_media_caps(const PurpleBuddy *buddy)
+{
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return priv->media_caps;
+}
+
+void purple_buddy_set_media_caps(PurpleBuddy *buddy, PurpleMediaCaps media_caps)
+{
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->media_caps = media_caps;
+
+ g_object_notify_by_pspec(G_OBJECT(buddy),
+ bd_properties[BUDDY_PROP_MEDIA_CAPS]);
+}
+
+PurpleGroup *purple_buddy_get_group(PurpleBuddy *buddy)
+{
+ g_return_val_if_fail(PURPLE_IS_BUDDY(buddy), NULL);
+
+ if (PURPLE_BLIST_NODE(buddy)->parent == NULL)
+ return purple_blist_get_default_group();
+
+ return PURPLE_GROUP(PURPLE_BLIST_NODE(buddy)->parent->parent);
+}
+
+/**************************************************************************
+ * GObject code for PurpleBuddy
+ **************************************************************************/
+
+/* Set method for GObject properties */
+static void
+purple_buddy_set_property(GObject *obj, guint param_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleBuddy *buddy = PURPLE_BUDDY(obj);
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy);
+
+ switch (param_id) {
+ case BUDDY_PROP_NAME:
+ if (priv->is_constructed)
+ purple_buddy_set_name(buddy, g_value_get_string(value));
+ else
+ priv->name =
+ purple_utf8_strip_unprintables(g_value_get_string(value));
+ break;
+ case BUDDY_PROP_LOCAL_ALIAS:
+ if (priv->is_constructed)
+ purple_buddy_set_local_alias(buddy, g_value_get_string(value));
+ else
+ priv->local_alias =
+ purple_utf8_strip_unprintables(g_value_get_string(value));
+ break;
+ case BUDDY_PROP_SERVER_ALIAS:
+ purple_buddy_set_server_alias(buddy, g_value_get_string(value));
+ break;
+ case BUDDY_PROP_ICON:
+ purple_buddy_set_icon(buddy, g_value_get_pointer(value));
+ break;
+ case BUDDY_PROP_ACCOUNT:
+ priv->account = g_value_get_object(value);
+ break;
+ case BUDDY_PROP_MEDIA_CAPS:
+ purple_buddy_set_media_caps(buddy, g_value_get_enum(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* Get method for GObject properties */
+static void
+purple_buddy_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleBuddy *buddy = PURPLE_BUDDY(obj);
+
+ switch (param_id) {
+ case BUDDY_PROP_NAME:
+ g_value_set_string(value, purple_buddy_get_name(buddy));
+ break;
+ case BUDDY_PROP_LOCAL_ALIAS:
+ g_value_set_string(value, purple_buddy_get_local_alias(buddy));
+ break;
+ case BUDDY_PROP_SERVER_ALIAS:
+ g_value_set_string(value, purple_buddy_get_server_alias(buddy));
+ break;
+ case BUDDY_PROP_ICON:
+ g_value_set_pointer(value, purple_buddy_get_icon(buddy));
+ break;
+ case BUDDY_PROP_ACCOUNT:
+ g_value_set_object(value, purple_buddy_get_account(buddy));
+ break;
+ case BUDDY_PROP_PRESENCE:
+ g_value_set_object(value, purple_buddy_get_presence(buddy));
+ break;
+ case BUDDY_PROP_MEDIA_CAPS:
+ g_value_set_enum(value, purple_buddy_get_media_caps(buddy));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* GObject initialization function */
+static void
+purple_buddy_init(GTypeInstance *instance, gpointer klass)
+{
+ PURPLE_DBUS_REGISTER_POINTER(PURPLE_BUDDY(instance), PurpleBuddy);
+}
+
+/* Called when done constructing */
+static void
+purple_buddy_constructed(GObject *object)
+{
+ PurpleBuddy *buddy = PURPLE_BUDDY(object);
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy);
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+
+ G_OBJECT_CLASS(blistnode_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);
+
+ priv->is_constructed = TRUE;
+}
+
+/* GObject dispose function */
+static void
+purple_buddy_dispose(GObject *object)
+{
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(object);
+
+ if (priv->icon) {
+ purple_buddy_icon_unref(priv->icon);
+ priv->icon = NULL;
+ }
+
+ if (priv->presence) {
+ g_object_unref(priv->presence);
+ priv->presence = NULL;
+ }
+
+ G_OBJECT_CLASS(blistnode_parent_class)->dispose(object);
+}
+
+/* GObject finalize function */
+static void
+purple_buddy_finalize(GObject *object)
+{
+ PurpleBuddy *buddy = PURPLE_BUDDY(object);
+ PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy);
+ PurplePlugin *prpl;
+ PurplePluginProtocolInfo *prpl_info;
+
+ /*
+ * Tell the owner PRPL that we're about to free the buddy so it
+ * can free proto_data
+ */
+ prpl = purple_find_prpl(purple_account_get_protocol_id(priv->account));
+ if (prpl) {
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+ if (prpl_info && prpl_info->buddy_free)
+ prpl_info->buddy_free(buddy);
+ }
+
+ g_free(priv->name);
+ g_free(priv->local_alias);
+ g_free(priv->server_alias);
+
+ PURPLE_DBUS_UNREGISTER_POINTER(buddy);
+
+ G_OBJECT_CLASS(blistnode_parent_class)->finalize(object);
+}
+
+/* Class initializer function */
+static void purple_buddy_class_init(PurpleBuddyClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ blistnode_parent_class = g_type_class_peek_parent(klass);
+
+ obj_class->dispose = purple_buddy_dispose;
+ obj_class->finalize = purple_buddy_finalize;
+
+ /* Setup properties */
+ obj_class->get_property = purple_buddy_get_property;
+ obj_class->set_property = purple_buddy_set_property;
+ obj_class->constructed = purple_buddy_constructed;
+
+ g_type_class_add_private(klass, sizeof(PurpleBuddyPrivate));
+
+ bd_properties[BUDDY_PROP_NAME] = g_param_spec_string("name", "Name",
+ "The name of the buddy.", NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+ bd_properties[BUDDY_PROP_LOCAL_ALIAS] = g_param_spec_string("local-alias",
+ "Local alias",
+ "Local alias of thee buddy.", NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+ bd_properties[BUDDY_PROP_SERVER_ALIAS] = g_param_spec_string("server-alias",
+ "Server alias",
+ "Server-side alias of the buddy.", NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ bd_properties[BUDDY_PROP_ICON] = g_param_spec_pointer("icon", "Buddy icon",
+ "The icon for the buddy.",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ bd_properties[BUDDY_PROP_ACCOUNT] = g_param_spec_object("account",
+ "Account",
+ "The account for the buddy.", PURPLE_TYPE_ACCOUNT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ bd_properties[BUDDY_PROP_PRESENCE] = g_param_spec_object("presence",
+ "Presence",
+ "The status information for the buddy.", PURPLE_TYPE_PRESENCE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ bd_properties[BUDDY_PROP_MEDIA_CAPS] = g_param_spec_enum("media-caps",
+ "Media capabilities",
+ "The media capabilities of the buddy.",
+ PURPLE_MEDIA_TYPE_CAPS, PURPLE_MEDIA_CAPS_NONE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, BUDDY_PROP_LAST,
+ bd_properties);
+}
+
+GType
+purple_buddy_get_type(void)
+{
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleBuddyClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_buddy_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleBuddy),
+ 0,
+ (GInstanceInitFunc)purple_buddy_init,
+ NULL,
+ };
+
+ type = g_type_register_static(PURPLE_TYPE_BLIST_NODE,
+ "PurpleBuddy",
+ &info, 0);
+ }
+
+ return type;
+}
+
+PurpleBuddy *
+purple_buddy_new(PurpleAccount *account, const char *name, const char *alias)
+{
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ return g_object_new(PURPLE_TYPE_BUDDY,
+ "account", account,
+ "name", name,
+ "local-alias", alias,
+ NULL);
+}
+
+/**************************************************************************/
+/* Contact API */
+/**************************************************************************/
+
+static void
+purple_contact_compute_priority_buddy(PurpleContact *contact)
+{
+ PurpleBlistNode *bnode;
+ PurpleBuddy *new_priority = NULL;
+ PurpleContactPrivate *priv = PURPLE_CONTACT_GET_PRIVATE(contact);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->priority_buddy = NULL;
+ for (bnode = PURPLE_BLIST_NODE(contact)->child;
+ bnode != NULL;
+ bnode = bnode->next)
+ {
+ PurpleBuddy *buddy;
+
+ if (!PURPLE_IS_BUDDY(bnode))
+ continue;
+
+ buddy = PURPLE_BUDDY(bnode);
+ if (new_priority == NULL)
+ {
+ new_priority = buddy;
+ continue;
+ }
+
+ if (purple_account_is_connected(purple_buddy_get_account(buddy)))
+ {
+ int cmp = 1;
+ if (purple_account_is_connected(purple_buddy_get_account(new_priority)))
+ cmp = purple_buddy_presence_compare(
+ PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(new_priority)),
+ PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(buddy)));
+
+ if (cmp > 0 || (cmp == 0 &&
+ purple_prefs_get_bool("/purple/contact/last_match")))
+ {
+ new_priority = buddy;
+ }
+ }
+ }
+
+ priv->priority_buddy = new_priority;
+ priv->priority_valid = TRUE;
+
+ g_object_notify_by_pspec(G_OBJECT(contact),
+ co_properties[CONTACT_PROP_PRIORITY_BUDDY]);
+}
+
+PurpleGroup *
+purple_contact_get_group(const PurpleContact *contact)
+{
+ g_return_val_if_fail(PURPLE_IS_CONTACT(contact), NULL);
+
+ return PURPLE_GROUP(PURPLE_BLIST_NODE(contact)->parent);
+}
+
+void
+purple_contact_set_alias(PurpleContact *contact, const char *alias)
+{
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+ PurpleIMConversation *im;
+ PurpleBlistNode *bnode;
+ char *old_alias;
+ char *new_alias = NULL;
+ PurpleContactPrivate *priv = PURPLE_CONTACT_GET_PRIVATE(contact);
+
+ g_return_if_fail(priv != NULL);
+
+ if ((alias != NULL) && (*alias != '\0'))
+ new_alias = purple_utf8_strip_unprintables(alias);
+
+ if (!purple_strings_are_different(priv->alias, new_alias)) {
+ g_free(new_alias);
+ return;
+ }
+
+ old_alias = priv->alias;
+
+ if ((new_alias != NULL) && (*new_alias != '\0'))
+ priv->alias = new_alias;
+ else {
+ priv->alias = NULL;
+ g_free(new_alias); /* could be "\0" */
+ }
+
+ g_object_notify_by_pspec(G_OBJECT(contact),
+ co_properties[CONTACT_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));
+ }
+
+ for(bnode = PURPLE_BLIST_NODE(contact)->child; bnode != NULL; bnode = bnode->next)
+ {
+ PurpleBuddy *buddy = PURPLE_BUDDY(bnode);
+
+ im = purple_conversations_find_im_with_account(purple_buddy_get_name(buddy),
+ purple_buddy_get_account(buddy));
+ if (im)
+ purple_conversation_autoset_title(PURPLE_CONVERSATION(im));
+ }
+
+ purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
+ contact, old_alias);
+ g_free(old_alias);
+}
+
+const char *purple_contact_get_alias(PurpleContact* contact)
+{
+ PurpleContactPrivate *priv = PURPLE_CONTACT_GET_PRIVATE(contact);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ if (priv->alias)
+ return priv->alias;
+
+ return purple_buddy_get_alias(purple_contact_get_priority_buddy(contact));
+}
+
+gboolean purple_contact_on_account(PurpleContact *c, PurpleAccount *account)
+{
+ PurpleBlistNode *bnode, *cnode = (PurpleBlistNode *) c;
+
+ g_return_val_if_fail(PURPLE_IS_CONTACT(c), FALSE);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
+
+ for (bnode = cnode->child; bnode; bnode = bnode->next) {
+ PurpleBuddy *buddy;
+
+ if (! PURPLE_IS_BUDDY(bnode))
+ continue;
+
+ buddy = (PurpleBuddy *)bnode;
+ if (purple_buddy_get_account(buddy) == account)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void purple_contact_invalidate_priority_buddy(PurpleContact *contact)
+{
+ PurpleContactPrivate *priv = PURPLE_CONTACT_GET_PRIVATE(contact);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->priority_valid = FALSE;
+}
+
+PurpleBuddy *purple_contact_get_priority_buddy(PurpleContact *contact)
+{
+ PurpleContactPrivate *priv = PURPLE_CONTACT_GET_PRIVATE(contact);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ if (!priv->priority_valid)
+ purple_contact_compute_priority_buddy(contact);
+
+ return priv->priority_buddy;
+}
+
+void purple_contact_merge(PurpleContact *source, PurpleBlistNode *node)
+{
+ PurpleBlistNode *sourcenode = (PurpleBlistNode*)source;
+ PurpleBlistNode *prev, *cur, *next;
+ PurpleContact *target;
+
+ g_return_if_fail(PURPLE_IS_CONTACT(source));
+ g_return_if_fail(PURPLE_IS_BLIST_NODE(node));
+
+ if (PURPLE_IS_CONTACT(node)) {
+ target = (PurpleContact *)node;
+ prev = _purple_blist_get_last_child(node);
+ } else if (PURPLE_IS_BUDDY(node)) {
+ target = (PurpleContact *)node->parent;
+ prev = node;
+ } else {
+ return;
+ }
+
+ if (source == target || !target)
+ return;
+
+ next = sourcenode->child;
+
+ while (next) {
+ cur = next;
+ next = cur->next;
+ if (PURPLE_IS_BUDDY(cur)) {
+ purple_blist_add_buddy((PurpleBuddy *)cur, target, NULL, prev);
+ prev = cur;
+ }
+ }
+}
+
+/**************************************************************************
+ * GObject code for PurpleContact
+ **************************************************************************/
+
+/* Set method for GObject properties */
+static void
+purple_contact_set_property(GObject *obj, guint param_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleContact *contact = PURPLE_CONTACT(obj);
+
+ switch (param_id) {
+ case CONTACT_PROP_ALIAS:
+ purple_contact_set_alias(contact, g_value_get_string(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* Get method for GObject properties */
+static void
+purple_contact_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleContact *contact = PURPLE_CONTACT(obj);
+ PurpleContactPrivate *priv = PURPLE_CONTACT_GET_PRIVATE(contact);
+
+ switch (param_id) {
+ case CONTACT_PROP_ALIAS:
+ g_value_set_string(value, priv->alias);
+ break;
+ case CONTACT_PROP_PRIORITY_BUDDY:
+ g_value_set_object(value, purple_contact_get_priority_buddy(contact));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* GObject initialization function */
+static void
+purple_contact_init(GTypeInstance *instance, gpointer klass)
+{
+ PurpleContact *contact = PURPLE_CONTACT(instance);
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+
+ if (ops && ops->new_node)
+ ops->new_node(PURPLE_BLIST_NODE(contact));
+
+ PURPLE_DBUS_REGISTER_POINTER(contact, PurpleContact);
+}
+
+/* GObject finalize function */
+static void
+purple_contact_finalize(GObject *object)
+{
+ g_free(PURPLE_CONTACT_GET_PRIVATE(object)->alias);
+
+ PURPLE_DBUS_UNREGISTER_POINTER(object);
+
+ G_OBJECT_CLASS(counting_parent_class)->finalize(object);
+}
+
+/* Class initializer function */
+static void purple_contact_class_init(PurpleContactClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ counting_parent_class = g_type_class_peek_parent(klass);
+
+ obj_class->finalize = purple_contact_finalize;
+
+ /* Setup properties */
+ obj_class->get_property = purple_contact_get_property;
+ obj_class->set_property = purple_contact_set_property;
+
+ g_type_class_add_private(klass, sizeof(PurpleContactPrivate));
+
+ co_properties[CONTACT_PROP_ALIAS] = g_param_spec_string("alias", "Alias",
+ "The alias for the contact.", NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ co_properties[CONTACT_PROP_PRIORITY_BUDDY] = g_param_spec_object(
+ "priority-buddy",
+ "Priority buddy", "The priority buddy of the contact.",
+ PURPLE_TYPE_BUDDY, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, CONTACT_PROP_LAST,
+ co_properties);
+}
+
+GType
+purple_contact_get_type(void)
+{
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleContactClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_contact_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleContact),
+ 0,
+ (GInstanceInitFunc)purple_contact_init,
+ NULL,
+ };
+
+ type = g_type_register_static(PURPLE_TYPE_COUNTING_NODE,
+ "PurpleContact",
+ &info, 0);
+ }
+
+ return type;
+}
+
+PurpleContact *
+purple_contact_new(void)
+{
+ return g_object_new(PURPLE_TYPE_CONTACT, NULL);
+}
+
+/**************************************************************************/
+/* Chat API */
+/**************************************************************************/
+
+const char *purple_chat_get_name(PurpleChat *chat)
+{
+ PurpleChatPrivate *priv = PURPLE_CHAT_GET_PRIVATE(chat);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ if ((priv->alias != NULL) && (*priv->alias != '\0'))
+ return priv->alias;
+
+ return purple_chat_get_name_only(chat);
+}
+
+const char *purple_chat_get_name_only(PurpleChat *chat)
+{
+ char *ret = NULL;
+ PurplePlugin *prpl;
+ PurplePluginProtocolInfo *prpl_info = NULL;
+ PurpleChatPrivate *priv = PURPLE_CHAT_GET_PRIVATE(chat);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ prpl = purple_find_prpl(purple_account_get_protocol_id(priv->account));
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+
+ if (prpl_info->chat_info) {
+ struct proto_chat_entry *pce;
+ GList *parts = prpl_info->chat_info(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);
+ }
+
+ return ret;
+}
+
+void
+purple_chat_set_alias(PurpleChat *chat, const char *alias)
+{
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+ char *old_alias;
+ char *new_alias = NULL;
+ PurpleChatPrivate *priv = PURPLE_CHAT_GET_PRIVATE(chat);
+
+ g_return_if_fail(priv != NULL);
+
+ if ((alias != NULL) && (*alias != '\0'))
+ new_alias = purple_utf8_strip_unprintables(alias);
+
+ if (!purple_strings_are_different(priv->alias, new_alias)) {
+ g_free(new_alias);
+ return;
+ }
+
+ old_alias = priv->alias;
+
+ if ((new_alias != NULL) && (*new_alias != '\0'))
+ priv->alias = new_alias;
+ else {
+ priv->alias = NULL;
+ g_free(new_alias); /* could be "\0" */
+ }
+
+ g_object_notify_by_pspec(G_OBJECT(chat), ch_properties[CHAT_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_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
+ chat, old_alias);
+ g_free(old_alias);
+}
+
+PurpleGroup *
+purple_chat_get_group(PurpleChat *chat)
+{
+ g_return_val_if_fail(PURPLE_IS_CHAT(chat), NULL);
+
+ return PURPLE_GROUP(PURPLE_BLIST_NODE(chat)->parent);
+}
+
+PurpleAccount *
+purple_chat_get_account(PurpleChat *chat)
+{
+ PurpleChatPrivate *priv = PURPLE_CHAT_GET_PRIVATE(chat);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->account;
+}
+
+GHashTable *
+purple_chat_get_components(PurpleChat *chat)
+{
+ PurpleChatPrivate *priv = PURPLE_CHAT_GET_PRIVATE(chat);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->components;
+}
+
+/**************************************************************************
+ * GObject code for PurpleChat
+ **************************************************************************/
+
+/* Set method for GObject properties */
+static void
+purple_chat_set_property(GObject *obj, guint param_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleChat *chat = PURPLE_CHAT(obj);
+ PurpleChatPrivate *priv = PURPLE_CHAT_GET_PRIVATE(chat);
+
+ switch (param_id) {
+ case CHAT_PROP_ALIAS:
+ if (priv->is_constructed)
+ purple_chat_set_alias(chat, g_value_get_string(value));
+ else
+ priv->alias =
+ purple_utf8_strip_unprintables(g_value_get_string(value));
+ break;
+ case CHAT_PROP_ACCOUNT:
+ priv->account = g_value_get_object(value);
+ break;
+ case CHAT_PROP_COMPONENTS:
+ priv->components = 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_chat_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleChat *chat = PURPLE_CHAT(obj);
+ PurpleChatPrivate *priv = PURPLE_CHAT_GET_PRIVATE(chat);
+
+ switch (param_id) {
+ case CHAT_PROP_ALIAS:
+ g_value_set_string(value, priv->alias);
+ break;
+ case CHAT_PROP_ACCOUNT:
+ g_value_set_object(value, purple_chat_get_account(chat));
+ break;
+ case CHAT_PROP_COMPONENTS:
+ g_value_set_pointer(value, purple_chat_get_components(chat));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* GObject initialization function */
+static void
+purple_chat_init(GTypeInstance *instance, gpointer klass)
+{
+ PURPLE_DBUS_REGISTER_POINTER(PURPLE_CHAT(instance), PurpleChat);
+}
+
+/* Called when done constructing */
+static void
+purple_chat_constructed(GObject *object)
+{
+ PurpleChat *chat = PURPLE_CHAT(object);
+ PurpleChatPrivate *priv = PURPLE_CHAT_GET_PRIVATE(chat);
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+
+ G_OBJECT_CLASS(blistnode_parent_class)->constructed(object);
+
+ if (ops != NULL && ops->new_node != NULL)
+ ops->new_node(PURPLE_BLIST_NODE(chat));
+
+ priv->is_constructed = TRUE;
+}
+
+/* GObject finalize function */
+static void
+purple_chat_finalize(GObject *object)
+{
+ PurpleChatPrivate *priv = PURPLE_CHAT_GET_PRIVATE(object);
+
+ g_free(priv->alias);
+ g_hash_table_destroy(priv->components);
+
+ PURPLE_DBUS_UNREGISTER_POINTER(object);
+
+ G_OBJECT_CLASS(blistnode_parent_class)->finalize(object);
+}
+
+/* Class initializer function */
+static void purple_chat_class_init(PurpleChatClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ blistnode_parent_class = g_type_class_peek_parent(klass);
+
+ obj_class->finalize = purple_chat_finalize;
+
+ /* Setup properties */
+ obj_class->get_property = purple_chat_get_property;
+ obj_class->set_property = purple_chat_set_property;
+ obj_class->constructed = purple_chat_constructed;
+
+ g_type_class_add_private(klass, sizeof(PurpleChatPrivate));
+
+ ch_properties[CHAT_PROP_ALIAS] = g_param_spec_string("alias", "Alias",
+ "The alias for the chat.", NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+ ch_properties[CHAT_PROP_ACCOUNT] = g_param_spec_object("account", "Account",
+ "The account that the chat belongs to.", PURPLE_TYPE_ACCOUNT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ ch_properties[CHAT_PROP_COMPONENTS] = g_param_spec_pointer("components",
+ "Components",
+ "The protocol components of the chat.",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, CHAT_PROP_LAST, ch_properties);
+}
+
+GType
+purple_chat_get_type(void)
+{
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleChatClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_chat_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleChat),
+ 0,
+ (GInstanceInitFunc)purple_chat_init,
+ NULL,
+ };
+
+ type = g_type_register_static(PURPLE_TYPE_BLIST_NODE,
+ "PurpleChat",
+ &info, 0);
+ }
+
+ return type;
+}
+
+PurpleChat *
+purple_chat_new(PurpleAccount *account, const char *alias, GHashTable *components)
+{
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+ g_return_val_if_fail(components != NULL, NULL);
+
+ return g_object_new(PURPLE_TYPE_CHAT,
+ "account", account,
+ "alias", alias,
+ "components", components,
+ NULL);
+}
+
+/**************************************************************************/
+/* Group API */
+/**************************************************************************/
+
+GSList *purple_group_get_accounts(PurpleGroup *group)
+{
+ GSList *l = NULL;
+ PurpleBlistNode *gnode, *cnode, *bnode;
+
+ gnode = (PurpleBlistNode *)group;
+
+ for (cnode = gnode->child; cnode; cnode = cnode->next) {
+ if (PURPLE_IS_CHAT(cnode)) {
+ if (!g_slist_find(l, purple_chat_get_account(PURPLE_CHAT(cnode))))
+ l = g_slist_append(l, purple_chat_get_account(PURPLE_CHAT(cnode)));
+ } else if (PURPLE_IS_CONTACT(cnode)) {
+ for (bnode = cnode->child; bnode; bnode = bnode->next) {
+ if (PURPLE_IS_BUDDY(bnode)) {
+ if (!g_slist_find(l, purple_buddy_get_account(PURPLE_BUDDY(bnode))))
+ l = g_slist_append(l, purple_buddy_get_account(PURPLE_BUDDY(bnode)));
+ }
+ }
+ }
+ }
+
+ return l;
+}
+
+gboolean purple_group_on_account(PurpleGroup *g, PurpleAccount *account)
+{
+ PurpleBlistNode *cnode;
+ for (cnode = ((PurpleBlistNode *)g)->child; cnode; cnode = cnode->next) {
+ if (PURPLE_IS_CONTACT(cnode)) {
+ if(purple_contact_on_account((PurpleContact *) cnode, account))
+ return TRUE;
+ } else if (PURPLE_IS_CHAT(cnode)) {
+ PurpleChat *chat = (PurpleChat *)cnode;
+ if ((!account && purple_account_is_connected(purple_chat_get_account(chat)))
+ || purple_chat_get_account(chat) == account)
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * 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();
+ PurpleGroup *dest;
+ gchar *old_name;
+ gchar *new_name;
+ GList *moved_buddies = NULL;
+ GSList *accts;
+ PurpleGroupPrivate *priv = PURPLE_GROUP_GET_PRIVATE(source);
+
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(name != NULL);
+
+ new_name = purple_utf8_strip_unprintables(name);
+
+ if (*new_name == '\0' || purple_strequal(new_name, priv->name)) {
+ g_free(new_name);
+ return;
+ }
+
+ dest = purple_blist_find_group(new_name);
+ if (dest != NULL && purple_utf8_strcasecmp(priv->name,
+ PURPLE_GROUP_GET_PRIVATE(dest)->name) != 0) {
+ /* We're merging two groups */
+ PurpleBlistNode *prev, *child, *next;
+
+ prev = _purple_blist_get_last_child((PurpleBlistNode*)dest);
+ child = PURPLE_BLIST_NODE(source)->child;
+
+ /*
+ * TODO: This seems like a dumb way to do this... why not just
+ * append all children from the old group to the end of the new
+ * one? PRPLs might be expecting to receive an add_buddy() for
+ * each moved buddy...
+ */
+ while (child)
+ {
+ next = child->next;
+ if (PURPLE_IS_CONTACT(child)) {
+ PurpleBlistNode *bnode;
+ purple_blist_add_contact((PurpleContact *)child, dest, prev);
+ for (bnode = child->child; bnode != NULL; bnode = bnode->next) {
+ purple_blist_add_buddy((PurpleBuddy *)bnode, (PurpleContact *)child,
+ NULL, bnode->prev);
+ moved_buddies = g_list_append(moved_buddies, bnode);
+ }
+ prev = child;
+ } else if (PURPLE_IS_CHAT(child)) {
+ purple_blist_add_chat((PurpleChat *)child, dest, prev);
+ prev = child;
+ } else {
+ purple_debug(PURPLE_DEBUG_ERROR, "blistnodetypes",
+ "Unknown child type in group %s\n", priv->name);
+ }
+ child = next;
+ }
+
+ /* Make a copy of the old group name and then delete the old group */
+ old_name = g_strdup(priv->name);
+ purple_blist_remove_group(source);
+ source = dest;
+ g_free(new_name);
+ } else {
+ /* A simple rename */
+ PurpleBlistNode *cnode, *bnode;
+
+ /* Build a GList of all buddies in this group */
+ for (cnode = PURPLE_BLIST_NODE(source)->child; cnode != NULL; cnode = cnode->next) {
+ if (PURPLE_IS_CONTACT(cnode))
+ for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
+ moved_buddies = g_list_append(moved_buddies, bnode);
+ }
+
+ purple_blist_update_groups_cache(source, new_name);
+
+ old_name = priv->name;
+ priv->name = new_name;
+
+ g_object_notify_by_pspec(G_OBJECT(source), gr_properties[GROUP_PROP_NAME]);
+ }
+
+ /* Save our changes */
+ if (ops && ops->save_node)
+ ops->save_node(PURPLE_BLIST_NODE(source));
+
+ /* Update the UI */
+ if (ops && ops->update)
+ ops->update(purple_blist_get_buddy_list(), PURPLE_BLIST_NODE(source));
+
+ /* Notify all PRPLs */
+ /* TODO: Is this condition needed? Seems like it would always be TRUE */
+ if(old_name && !purple_strequal(priv->name, old_name)) {
+ for (accts = purple_group_get_accounts(source); accts; accts = g_slist_remove(accts, accts->data)) {
+ PurpleAccount *account = accts->data;
+ PurpleConnection *gc = NULL;
+ PurplePlugin *prpl = NULL;
+ PurplePluginProtocolInfo *prpl_info = NULL;
+ GList *l = NULL, *buddies = NULL;
+
+ gc = purple_account_get_connection(account);
+
+ if(gc)
+ prpl = purple_connection_get_prpl(gc);
+
+ if(gc && prpl)
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+
+ if(!prpl_info)
+ continue;
+
+ for(l = moved_buddies; l; l = l->next) {
+ PurpleBuddy *buddy = PURPLE_BUDDY(l->data);
+
+ if(buddy && purple_buddy_get_account(buddy) == account)
+ buddies = g_list_append(buddies, (PurpleBlistNode *)buddy);
+ }
+
+ if(prpl_info->rename_group) {
+ prpl_info->rename_group(gc, old_name, source, buddies);
+ } else {
+ GList *cur, *groups = NULL;
+
+ /* Make a list of what the groups each buddy is in */
+ for(cur = buddies; cur; cur = cur->next) {
+ PurpleBlistNode *node = (PurpleBlistNode *)cur->data;
+ groups = g_list_prepend(groups, node->parent->parent);
+ }
+
+ purple_account_remove_buddies(account, buddies, groups);
+ g_list_free(groups);
+ purple_account_add_buddies(account, buddies, NULL);
+ }
+
+ g_list_free(buddies);
+ }
+ }
+ g_list_free(moved_buddies);
+ g_free(old_name);
+
+ g_object_notify_by_pspec(G_OBJECT(source), gr_properties[GROUP_PROP_NAME]);
+}
+
+const char *purple_group_get_name(PurpleGroup *group)
+{
+ PurpleGroupPrivate *priv = PURPLE_GROUP_GET_PRIVATE(group);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->name;
+}
+
+/**************************************************************************
+ * GObject code for PurpleGroup
+ **************************************************************************/
+
+/* Set method for GObject properties */
+static void
+purple_group_set_property(GObject *obj, guint param_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleGroup *group = PURPLE_GROUP(obj);
+ PurpleGroupPrivate *priv = PURPLE_GROUP_GET_PRIVATE(group);
+
+ switch (param_id) {
+ case GROUP_PROP_NAME:
+ if (priv->is_constructed)
+ purple_group_set_name(group, g_value_get_string(value));
+ else
+ priv->name =
+ purple_utf8_strip_unprintables(g_value_get_string(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* Get method for GObject properties */
+static void
+purple_group_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleGroup *group = PURPLE_GROUP(obj);
+
+ switch (param_id) {
+ case GROUP_PROP_NAME:
+ g_value_set_string(value, purple_group_get_name(group));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* GObject initialization function */
+static void
+purple_group_init(GTypeInstance *instance, gpointer klass)
+{
+ PURPLE_DBUS_REGISTER_POINTER(PURPLE_GROUP(instance), PurpleGroup);
+}
+
+/* Called when done constructing */
+static void
+purple_group_constructed(GObject *object)
+{
+ PurpleGroup *group = PURPLE_GROUP(object);
+ PurpleGroupPrivate *priv = PURPLE_GROUP_GET_PRIVATE(group);
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+
+ G_OBJECT_CLASS(counting_parent_class)->constructed(object);
+
+ if (ops && ops->new_node)
+ ops->new_node(PURPLE_BLIST_NODE(group));
+
+ priv->is_constructed = TRUE;
+}
+
+/* GObject finalize function */
+static void
+purple_group_finalize(GObject *object)
+{
+ g_free(PURPLE_GROUP_GET_PRIVATE(object)->name);
+
+ PURPLE_DBUS_UNREGISTER_POINTER(object);
+
+ G_OBJECT_CLASS(counting_parent_class)->finalize(object);
+}
+
+/* Class initializer function */
+static void purple_group_class_init(PurpleGroupClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ counting_parent_class = g_type_class_peek_parent(klass);
+
+ obj_class->finalize = purple_group_finalize;
+ obj_class->constructed = purple_group_constructed;
+
+ /* Setup properties */
+ obj_class->get_property = purple_group_get_property;
+ obj_class->set_property = purple_group_set_property;
+
+ g_type_class_add_private(klass, sizeof(PurpleGroupPrivate));
+
+ gr_properties[GROUP_PROP_NAME] = g_param_spec_string("name", "Name",
+ "Name of the group.", NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, GROUP_PROP_LAST,
+ gr_properties);
+}
+
+GType
+purple_group_get_type(void)
+{
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleGroupClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_group_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleGroup),
+ 0,
+ (GInstanceInitFunc)purple_group_init,
+ NULL,
+ };
+
+ type = g_type_register_static(PURPLE_TYPE_COUNTING_NODE,
+ "PurpleGroup",
+ &info, 0);
+ }
+
+ return type;
+}
+
+PurpleGroup *
+purple_group_new(const char *name)
+{
+ PurpleGroup *group;
+
+ if (name == NULL || name[0] == '\0')
+ name = PURPLE_BLIST_DEFAULT_GROUP_NAME;
+ if (g_strcmp0(name, "Buddies") == 0)
+ name = PURPLE_BLIST_DEFAULT_GROUP_NAME;
+ if (g_strcmp0(name, _purple_blist_get_localized_default_group_name()) == 0)
+ name = PURPLE_BLIST_DEFAULT_GROUP_NAME;
+
+ group = purple_blist_find_group(name);
+ if (group != NULL)
+ return group;
+
+ return g_object_new(PURPLE_TYPE_GROUP, "name", name, NULL);
+}
diff --git a/libpurple/blistnodetypes.h b/libpurple/blistnodetypes.h
new file mode 100644
index 0000000000..cbd0709e5a
--- /dev/null
+++ b/libpurple/blistnodetypes.h
@@ -0,0 +1,679 @@
+/* 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 _PURPLE_BLISTNODE_TYPES_H_
+#define _PURPLE_BLISTNODE_TYPES_H_
+/**
+ * SECTION:blistnodetypes
+ * @section_id: libpurple-blistnodetypes
+ * @short_description: <filename>blistnodetypes.h</filename>
+ * @title: Buddy, Chat, Contact and Group node Objects
+ */
+
+#include "blistnode.h"
+
+#define PURPLE_TYPE_BUDDY (purple_buddy_get_type())
+#define PURPLE_BUDDY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_BUDDY, PurpleBuddy))
+#define PURPLE_BUDDY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_BUDDY, PurpleBuddyClass))
+#define PURPLE_IS_BUDDY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_BUDDY))
+#define PURPLE_IS_BUDDY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_BUDDY))
+#define PURPLE_BUDDY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_BUDDY, PurpleBuddyClass))
+
+typedef struct _PurpleBuddy PurpleBuddy;
+typedef struct _PurpleBuddyClass PurpleBuddyClass;
+
+#define PURPLE_TYPE_CONTACT (purple_contact_get_type())
+#define PURPLE_CONTACT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_CONTACT, PurpleContact))
+#define PURPLE_CONTACT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_CONTACT, PurpleContactClass))
+#define PURPLE_IS_CONTACT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_CONTACT))
+#define PURPLE_IS_CONTACT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_CONTACT))
+#define PURPLE_CONTACT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_CONTACT, PurpleContactClass))
+
+typedef struct _PurpleContact PurpleContact;
+typedef struct _PurpleContactClass PurpleContactClass;
+
+#define PURPLE_TYPE_GROUP (purple_group_get_type())
+#define PURPLE_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_GROUP, PurpleGroup))
+#define PURPLE_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_GROUP, PurpleGroupClass))
+#define PURPLE_IS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_GROUP))
+#define PURPLE_IS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_GROUP))
+#define PURPLE_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_GROUP, PurpleGroupClass))
+
+typedef struct _PurpleGroup PurpleGroup;
+typedef struct _PurpleGroupClass PurpleGroupClass;
+
+#define PURPLE_TYPE_CHAT (purple_chat_get_type())
+#define PURPLE_CHAT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_CHAT, PurpleChat))
+#define PURPLE_CHAT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_CHAT, PurpleChatClass))
+#define PURPLE_IS_CHAT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_CHAT))
+#define PURPLE_IS_CHAT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_CHAT))
+#define PURPLE_CHAT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_CHAT, PurpleChatClass))
+
+typedef struct _PurpleChat PurpleChat;
+typedef struct _PurpleChatClass PurpleChatClass;
+
+#include "account.h"
+#include "buddyicon.h"
+#include "media.h"
+#include "presence.h"
+#include "status.h"
+
+#define PURPLE_BUDDY_IS_ONLINE(b) \
+ (PURPLE_IS_BUDDY(b) \
+ && purple_account_is_connected(purple_buddy_get_account(PURPLE_BUDDY(b))) \
+ && purple_presence_is_online(purple_buddy_get_presence(PURPLE_BUDDY(b))))
+
+#define PURPLE_BLIST_NODE_NAME(n) \
+ (PURPLE_IS_CHAT(n) ? purple_chat_get_name(PURPLE_CHAT(n)) : \
+ PURPLE_IS_BUDDY(n) ? purple_buddy_get_name(PURPLE_BUDDY(n)) : NULL)
+
+/**************************************************************************/
+/* Data Structures */
+/**************************************************************************/
+/**
+ * PurpleBuddy:
+ *
+ * A buddy on the buddy list.
+ */
+struct _PurpleBuddy {
+ PurpleBlistNode node;
+};
+
+/**
+ * PurpleBuddyClass:
+ *
+ * The base class for all #PurpleBuddy's.
+ */
+struct _PurpleBuddyClass {
+ PurpleBlistNodeClass node_class;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+/**
+ * PurpleContact:
+ *
+ * A contact on the buddy list.
+ *
+ * A contact is a counting node, which means it keeps track of the counts of
+ * the buddies under this contact.
+ */
+struct _PurpleContact {
+ PurpleCountingNode counting;
+};
+
+/**
+ * PurpleContactClass:
+ *
+ * The base class for all #PurpleContact's.
+ */
+struct _PurpleContactClass {
+ PurpleCountingNodeClass counting_class;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+/**
+ * PurpleGroup:
+ *
+ * A group on the buddy list.
+ *
+ * A group is a counting node, which means it keeps track of the counts of the
+ * chats and contacts under this group.
+ */
+struct _PurpleGroup {
+ PurpleCountingNode counting;
+};
+
+/**
+ * PurpleGroupClass:
+ *
+ * The base class for all #PurpleGroup's.
+ */
+struct _PurpleGroupClass {
+ PurpleCountingNodeClass counting_class;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+/**
+ * PurpleChat:
+ *
+ * A chat on the buddy list.
+ */
+struct _PurpleChat {
+ PurpleBlistNode node;
+};
+
+/**
+ * PurpleChatClass:
+ *
+ * The base class for all #PurpleChat's.
+ */
+struct _PurpleChatClass {
+ PurpleBlistNodeClass node_class;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+/**************************************************************************/
+/* Buddy API */
+/**************************************************************************/
+
+/**
+ * purple_buddy_get_type:
+ *
+ * Returns: The #GType for the #PurpleBuddy object.
+ */
+GType purple_buddy_get_type(void);
+
+/**
+ * purple_buddy_new:
+ * @account: The account this buddy will get added to
+ * @name: The name of the new buddy
+ * @alias: The alias of the new buddy (or NULL if unaliased)
+ *
+ * Creates a new buddy.
+ *
+ * This function only creates the #PurpleBuddy. Use purple_blist_add_buddy()
+ * to add the buddy to the list and purple_account_add_buddy() to sync up
+ * with the server.
+ *
+ * See purple_account_add_buddy(), purple_blist_add_buddy().
+ *
+ * Returns: A newly allocated buddy
+ */
+PurpleBuddy *purple_buddy_new(PurpleAccount *account, const char *name, const char *alias);
+
+/**
+ * purple_buddy_set_icon:
+ * @buddy: The buddy.
+ * @icon: The buddy icon.
+ *
+ * Sets a buddy's icon.
+ *
+ * This should only be called from within Purple. You probably want to
+ * call purple_buddy_icon_set_data().
+ *
+ * See purple_buddy_icon_set_data().
+ */
+void purple_buddy_set_icon(PurpleBuddy *buddy, PurpleBuddyIcon *icon);
+
+/**
+ * purple_buddy_get_icon:
+ * @buddy: The buddy.
+ *
+ * Returns a buddy's icon.
+ *
+ * Returns: The buddy icon.
+ */
+PurpleBuddyIcon *purple_buddy_get_icon(const PurpleBuddy *buddy);
+
+/**
+ * purple_buddy_get_account:
+ * @buddy: The buddy.
+ *
+ * Returns a buddy's account.
+ *
+ * Returns: The account
+ */
+PurpleAccount *purple_buddy_get_account(const PurpleBuddy *buddy);
+
+/**
+ * purple_buddy_set_name:
+ * @buddy: The buddy.
+ * @name: The name.
+ *
+ * Sets a buddy's name
+ */
+void purple_buddy_set_name(PurpleBuddy *buddy, const char *name);
+
+/**
+ * purple_buddy_get_name:
+ * @buddy: The buddy.
+ *
+ * Returns a buddy's name
+ *
+ * Returns: The name.
+ */
+const char *purple_buddy_get_name(const PurpleBuddy *buddy);
+
+/**
+ * purple_buddy_get_protocol_data:
+ * @buddy: The buddy.
+ *
+ * Returns a buddy's protocol-specific data.
+ *
+ * This should only be called from the associated protocol.
+ *
+ * See purple_buddy_set_protocol_data().
+ *
+ * Returns: The protocol data.
+ */
+gpointer purple_buddy_get_protocol_data(const PurpleBuddy *buddy);
+
+/**
+ * purple_buddy_set_protocol_data:
+ * @buddy: The buddy.
+ * @data: The data.
+ *
+ * Sets a buddy's protocol-specific data.
+ *
+ * This should only be called from the associated protocol.
+ *
+ * See purple_buddy_get_protocol_data().
+ */
+void purple_buddy_set_protocol_data(PurpleBuddy *buddy, gpointer data);
+
+/**
+ * purple_buddy_get_contact:
+ * @buddy: The buddy.
+ *
+ * Returns a buddy's contact.
+ *
+ * Returns: The buddy's contact.
+ */
+PurpleContact *purple_buddy_get_contact(PurpleBuddy *buddy);
+
+/**
+ * purple_buddy_get_presence:
+ * @buddy: The buddy.
+ *
+ * Returns a buddy's presence.
+ *
+ * Returns: The buddy's presence.
+ */
+PurplePresence *purple_buddy_get_presence(const PurpleBuddy *buddy);
+
+/**
+ * purple_buddy_update_status:
+ * @buddy: The buddy whose status has changed.
+ * @old_status: The status from which we are changing.
+ *
+ * Updates a buddy's status.
+ *
+ * This should only be called from within Purple.
+ */
+void purple_buddy_update_status(PurpleBuddy *buddy, PurpleStatus *old_status);
+
+/**
+ * purple_buddy_get_media_caps:
+ * @buddy: The buddy.
+ *
+ * Gets the media caps from a buddy.
+ *
+ * Returns: The media caps.
+ */
+PurpleMediaCaps purple_buddy_get_media_caps(const PurpleBuddy *buddy);
+
+/**
+ * purple_buddy_set_media_caps:
+ * @buddy: The PurpleBuddy.
+ * @media_caps: The PurpleMediaCaps.
+ *
+ * Sets the media caps for a buddy.
+ */
+void purple_buddy_set_media_caps(PurpleBuddy *buddy, PurpleMediaCaps media_caps);
+
+/**
+ * purple_buddy_get_alias_only:
+ * @buddy: The buddy whose alias will be returned.
+ *
+ * Returns the alias of a buddy.
+ *
+ * Returns: The alias (if set), server alias (if set),
+ * or NULL.
+ */
+const char *purple_buddy_get_alias_only(PurpleBuddy *buddy);
+
+/**
+ * purple_buddy_set_server_alias:
+ * @buddy: The buddy.
+ * @alias: The server alias to be set.
+ *
+ * Sets the server alias for a buddy.
+ */
+void purple_buddy_set_server_alias(PurpleBuddy *buddy, const char *alias);
+
+/**
+ * purple_buddy_get_server_alias:
+ * @buddy: The buddy whose server alias will be returned
+ *
+ * Gets the server alias for a buddy.
+ *
+ * Returns: The server alias, or NULL if it is not set.
+ */
+const char *purple_buddy_get_server_alias(PurpleBuddy *buddy);
+
+/**
+ * purple_buddy_get_contact_alias:
+ * @buddy: The buddy whose alias will be returned
+ *
+ * Returns the correct name to display for a buddy, taking the contact alias
+ * into account. In order of precedence: the buddy's alias; the buddy's
+ * contact alias; the buddy's server alias; the buddy's user name.
+ *
+ * Returns: The appropriate name or alias, or NULL.
+ *
+ */
+const char *purple_buddy_get_contact_alias(PurpleBuddy *buddy);
+
+/**
+ * purple_buddy_get_alias:
+ * @buddy: The buddy whose alias will be returned.
+ *
+ * Returns the correct name to display for a buddy. In order of precedence:
+ * the buddy's local alias; the buddy's server alias; the buddy's contact alias;
+ * the buddy's user name.
+ *
+ * Returns: The appropriate name or alias, or NULL
+ */
+const char *purple_buddy_get_alias(PurpleBuddy *buddy);
+
+/**
+ * purple_buddy_set_local_alias:
+ * @buddy: The buddy
+ * @alias: The local alias for the buddy
+ *
+ * Sets the local alias for the buddy.
+ */
+void purple_buddy_set_local_alias(PurpleBuddy *buddy, const char *alias);
+
+/**
+ * purple_buddy_get_local_alias:
+ * @buddy: The buddy
+ *
+ * Returns the local alias for the buddy, or %NULL if none exists.
+ *
+ * Returns: The local alias for the buddy
+ */
+const char *purple_buddy_get_local_alias(PurpleBuddy *buddy);
+
+/**
+ * purple_buddy_get_group:
+ * @buddy: The buddy
+ *
+ * Returns the group of which the buddy is a member.
+ *
+ * Returns: The group or NULL if the buddy is not in a group
+ */
+PurpleGroup *purple_buddy_get_group(PurpleBuddy *buddy);
+
+/**************************************************************************/
+/* Contact API */
+/**************************************************************************/
+
+/**
+ * purple_contact_get_type:
+ *
+ * Returns: The #GType for the #PurpleContact object.
+ */
+GType purple_contact_get_type(void);
+
+/**
+ * purple_contact_new:
+ *
+ * Creates a new contact
+ *
+ * Returns: A new contact struct
+ */
+PurpleContact *purple_contact_new(void);
+
+/**
+ * purple_contact_get_group:
+ * @contact: The contact
+ *
+ * Gets the PurpleGroup from a PurpleContact
+ *
+ * Returns: The group
+ */
+PurpleGroup *purple_contact_get_group(const PurpleContact *contact);
+
+/**
+ * purple_contact_get_priority_buddy:
+ * @contact: The contact
+ *
+ * Returns the highest priority buddy for a given contact.
+ *
+ * Returns: The highest priority buddy
+ */
+PurpleBuddy *purple_contact_get_priority_buddy(PurpleContact *contact);
+
+/**
+ * purple_contact_set_alias:
+ * @contact: The contact
+ * @alias: The alias
+ *
+ * Sets the alias for a contact.
+ */
+void purple_contact_set_alias(PurpleContact *contact, const char *alias);
+
+/**
+ * purple_contact_get_alias:
+ * @contact: The contact
+ *
+ * Gets the alias for a contact.
+ *
+ * Returns: The alias, or NULL if it is not set.
+ */
+const char *purple_contact_get_alias(PurpleContact *contact);
+
+/**
+ * purple_contact_on_account:
+ * @contact: The contact to search through.
+ * @account: The account.
+ *
+ * Determines whether an account owns any buddies in a given contact
+ *
+ * Returns: TRUE if there are any buddies from account in the contact, or FALSE otherwise.
+ */
+gboolean purple_contact_on_account(PurpleContact *contact, PurpleAccount *account);
+
+/**
+ * purple_contact_invalidate_priority_buddy:
+ * @contact: The contact
+ *
+ * Invalidates the priority buddy so that the next call to
+ * purple_contact_get_priority_buddy recomputes it.
+ */
+void purple_contact_invalidate_priority_buddy(PurpleContact *contact);
+
+/**
+ * purple_contact_merge:
+ * @source: The contact to merge
+ * @node: The place to merge to (a buddy or contact)
+ *
+ * Merges two contacts
+ *
+ * All of the buddies from source will be moved to target
+ */
+void purple_contact_merge(PurpleContact *source, PurpleBlistNode *node);
+
+/**************************************************************************/
+/* Chat API */
+/**************************************************************************/
+
+/**
+ * purple_chat_get_type:
+ *
+ * Returns: The #GType for the #PurpleChat object.
+ */
+GType purple_chat_get_type(void);
+
+/**
+ * purple_chat_new:
+ * @account: The account this chat will get added to
+ * @alias: The alias of the new chat
+ * @components: The info the protocol needs to join the chat. The
+ * hash function should be g_str_hash() and the
+ * equal function should be g_str_equal().
+ *
+ * Creates a new chat for the buddy list
+ *
+ * Returns: A newly allocated chat
+ */
+PurpleChat *purple_chat_new(PurpleAccount *account, const char *alias, GHashTable *components);
+
+/**
+ * purple_chat_get_name:
+ * @chat: The chat whose name will be returned.
+ *
+ * Returns the correct name to display for a blist chat.
+ *
+ * Returns: The alias (if set), or first component value.
+ */
+const char *purple_chat_get_name(PurpleChat *chat);
+
+/**
+ * purple_chat_get_name_only:
+ * @chat: The chat whose name will be returned.
+ *
+ * Returns the name of the chat
+ *
+ * Returns: The first component value.
+ */
+const char *purple_chat_get_name_only(PurpleChat *chat);
+
+/**
+ * purple_chat_set_alias:
+ * @chat: The chat
+ * @alias: The alias
+ *
+ * Sets the alias for a blist chat.
+ */
+void purple_chat_set_alias(PurpleChat *chat, const char *alias);
+
+/**
+ * purple_chat_get_group:
+ * @chat: The chat.
+ *
+ * Returns the group of which the chat is a member.
+ *
+ * Returns: The parent group, or %NULL if the chat is not in a group.
+ */
+PurpleGroup *purple_chat_get_group(PurpleChat *chat);
+
+/**
+ * purple_chat_get_account:
+ * @chat: The chat.
+ *
+ * Returns the account the chat belongs to.
+ *
+ * Returns: The account the chat belongs to.
+ */
+PurpleAccount *purple_chat_get_account(PurpleChat *chat);
+
+/**
+ * purple_chat_get_components:
+ * @chat: The chat.
+ *
+ * Get a hashtable containing information about a chat.
+ *
+ * Returns: (transfer none): The hashtable.
+ */
+GHashTable *purple_chat_get_components(PurpleChat *chat);
+
+/**************************************************************************/
+/* Group API */
+/**************************************************************************/
+
+/**
+ * purple_group_get_type:
+ *
+ * Returns: The #GType for the #PurpleGroup object.
+ */
+GType purple_group_get_type(void);
+
+/**
+ * purple_group_new:
+ * @name: The name of the new group
+ *
+ * Creates a new group
+ *
+ * You can't have more than one group with the same name. Sorry. If you pass
+ * this the name of a group that already exists, it will return that group.
+ *
+ * Returns: A new group struct
+*/
+PurpleGroup *purple_group_new(const char *name);
+
+/**
+ * purple_group_get_accounts:
+ * @g: The group
+ *
+ * Returns a list of accounts that have buddies in this group
+ *
+ * Returns: A GSList of accounts (which must be freed), or NULL if the group
+ * has no accounts.
+ */
+GSList *purple_group_get_accounts(PurpleGroup *g);
+
+/**
+ * purple_group_on_account:
+ * @g: The group to search through.
+ * @account: The account.
+ *
+ * Determines whether an account owns any buddies in a given group
+ *
+ * Returns: TRUE if there are any buddies in the group, or FALSE otherwise.
+ */
+gboolean purple_group_on_account(PurpleGroup *g, PurpleAccount *account);
+
+/**
+ * purple_group_set_name:
+ * @group: The group.
+ * @name: The name of the group.
+ *
+ * Sets the name of a group.
+ */
+void purple_group_set_name(PurpleGroup *group, const char *name);
+
+/**
+ * purple_group_get_name:
+ * @group: The group.
+ *
+ * Returns the name of a group.
+ *
+ * Returns: The name of the group.
+ */
+const char *purple_group_get_name(PurpleGroup *group);
+
+G_END_DECLS
+
+#endif /* _PURPLE_BLISTNODE_TYPES_H_ */
diff --git a/libpurple/buddyicon.c b/libpurple/buddyicon.c
index 76388c8521..03a425a309 100644
--- a/libpurple/buddyicon.c
+++ b/libpurple/buddyicon.c
@@ -1,8 +1,3 @@
-/**
- * @file buddyicon.c Buddy Icon API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -30,22 +25,22 @@
#include "conversation.h"
#include "dbus-maybe.h"
#include "debug.h"
-#include "imgstore.h"
+#include "image.h"
#include "util.h"
/* NOTE: Instances of this struct are allocated without zeroing the memory, so
* NOTE: be sure to update purple_buddy_icon_new() if you add members. */
struct _PurpleBuddyIcon
{
- PurpleAccount *account; /**< The account the user is on. */
- PurpleStoredImage *img; /**< The stored image containing
- the icon data. */
- char *username; /**< The username the icon belongs to. */
- char *checksum; /**< The protocol checksum. */
- int ref_count; /**< The buddy icon reference count. */
+ PurpleAccount *account; /* The account the user is on. */
+ PurpleImage *img; /* The image containing
+ the icon data. */
+ char *username; /* The username the icon belongs to. */
+ char *checksum; /* The protocol checksum. */
+ unsigned int ref_count; /* The buddy icon reference count. */
};
-/**
+/*
* This is the big grand daddy hash table that contains references to
* everybody's buddy icons.
*
@@ -57,32 +52,31 @@ struct _PurpleBuddyIcon
*/
static GHashTable *account_cache = NULL;
-/**
- * This hash table contains a bunch of PurpleStoredImages that are
+/*
+ * This hash table contains a bunch of PurpleImages that are
* shared across all accounts.
*
* Key is the filename for this image as constructed by
- * purple_util_get_image_filename(). So it is the base16 encoded
+ * purple_image_generate_filename(). So it is the base16 encoded
* sha-1 hash plus an appropriate file extension. For example:
* "0f4972d17d1e70e751c43c90c948e72efbff9796.gif"
*
- * The value is a PurpleStoredImage containing the icon data. These
- * images are reference counted, and when the count reaches 0
- * imgstore.c emits the image-deleting signal and we remove the image
- * from the hash table (but it might still be saved on disk, if the
- * icon is being used by offline accounts or some such).
+ * The value is a PurpleImage containing the icon data. These images are
+ * reference counted, and when the count reaches 0 we remove the image from
+ * the hash table (but it might still be saved on disk, if the icon is being
+ * used by offline accounts or some such).
*/
static GHashTable *icon_data_cache = NULL;
-/**
- * This hash table contains references counts for how many times each
+/*
+ * This hash table contains reference counts for how many times each
* icon in the ~/.purple/icons/ directory is being used. It's pretty
* crazy. It maintains the reference count across sessions, too, so
* if you exit Pidgin then this hash table is reconstructed the next
* time Pidgin starts.
*
* Key is the filename for this image as constructed by
- * purple_util_get_image_filename(). So it is the base16 encoded
+ * purple_image_generate_filename(). So it is the base16 encoded
* sha-1 hash plus an appropriate file extension. For example:
* "0f4972d17d1e70e751c43c90c948e72efbff9796.gif"
*
@@ -94,7 +88,7 @@ static GHashTable *icon_data_cache = NULL;
*/
static GHashTable *icon_file_cache = NULL;
-/**
+/*
* This hash table is used for both custom buddy icons on PurpleBlistNodes and
* account icons.
*/
@@ -102,12 +96,9 @@ static GHashTable *pointer_icon_cache = NULL;
static char *cache_dir = NULL;
-/** "Should icons be cached to disk?" */
+/* "Should icons be cached to disk?" */
static gboolean icon_caching = TRUE;
-/* For ~/.gaim to ~/.purple migration. */
-static char *old_icons_dir = NULL;
-
static void delete_buddy_icon_settings(PurpleBlistNode *node, const char *setting_name);
/*
@@ -148,34 +139,43 @@ unref_filename(const char *filename)
}
}
+static const gchar *
+image_get_filename(PurpleImage *img)
+{
+ return g_object_get_data(G_OBJECT(img), "purple-buddyicon-filename");
+}
+
static void
-purple_buddy_icon_data_cache(PurpleStoredImage *img)
+purple_buddy_icon_data_cache(PurpleImage *img)
{
- const char *dirname;
- char *path;
+ const gchar *dirname, *filename;
+ gchar *path;
- g_return_if_fail(img != NULL);
+ g_return_if_fail(PURPLE_IS_IMAGE(img));
if (!purple_buddy_icons_is_caching())
return;
- dirname = purple_buddy_icons_get_cache_dir();
- path = g_build_filename(dirname, purple_imgstore_get_filename(img), NULL);
+ dirname = purple_buddy_icons_get_cache_dir();
+ filename = image_get_filename(img);
+ g_return_if_fail(filename != NULL);
+ path = g_build_filename(dirname, filename, NULL);
if (!g_file_test(dirname, G_FILE_TEST_IS_DIR))
{
- purple_debug_info("buddyicon", "Creating icon cache directory.\n");
+ purple_debug_info("buddyicon", "creating icon cache directory");
if (g_mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR) < 0)
{
purple_debug_error("buddyicon",
- "Unable to create directory %s: %s\n",
- dirname, g_strerror(errno));
+ "unable to create directory %s: %s",
+ dirname, g_strerror(errno));
+ return;
}
}
- purple_util_write_data_to_file_absolute(path, purple_imgstore_get_data(img),
- purple_imgstore_get_size(img));
+ if (!purple_image_save(img, path))
+ purple_debug_error("buddyicon", "failed to save icon %s", path);
g_free(path);
}
@@ -192,7 +192,7 @@ purple_buddy_icon_data_uncache_file(const char *filename)
if (GPOINTER_TO_INT(g_hash_table_lookup(icon_file_cache, filename)))
return;
- dirname = purple_buddy_icons_get_cache_dir();
+ dirname = purple_buddy_icons_get_cache_dir();
path = g_build_filename(dirname, filename, NULL);
if (g_file_test(path, G_FILE_TEST_EXISTS))
@@ -226,61 +226,50 @@ value_equals(gpointer key, gpointer value, gpointer user_data)
}
static void
-image_deleting_cb(const PurpleStoredImage *img, gpointer data)
+image_deleting_cb(gpointer _filename)
{
- const char *filename = purple_imgstore_get_filename(img);
+ PurpleImage *img;
+ gchar *filename = _filename;
- /* If there's no filename, it can't be one of our images. */
- if (filename == NULL)
- return;
+ img = g_hash_table_lookup(icon_data_cache, filename);
+ purple_buddy_icon_data_uncache_file(filename);
+ g_hash_table_remove(icon_data_cache, filename);
- if (img == g_hash_table_lookup(icon_data_cache, filename))
- {
- purple_buddy_icon_data_uncache_file(filename);
- g_hash_table_remove(icon_data_cache, filename);
+ /* We could make this O(1) by using another hash table, but
+ * this is probably good enough. */
+ g_hash_table_foreach_remove(pointer_icon_cache, value_equals, (gpointer)img);
- /* We could make this O(1) by using another hash table, but
- * this is probably good enough. */
- g_hash_table_foreach_remove(pointer_icon_cache, value_equals, (gpointer)img);
- }
+ g_free(filename);
}
-static PurpleStoredImage *
-purple_buddy_icon_data_new(guchar *icon_data, size_t icon_len, const char *filename)
+static PurpleImage *
+purple_buddy_icon_data_new(guchar *icon_data, size_t icon_len)
{
- char *file;
- PurpleStoredImage *img;
+ PurpleImage *newimg, *oldimg;
+ const gchar *filename;
g_return_val_if_fail(icon_data != NULL, NULL);
- g_return_val_if_fail(icon_len > 0, NULL);
+ g_return_val_if_fail(icon_len > 0, NULL);
- if (filename == NULL)
- {
- file = purple_util_get_image_filename(icon_data, icon_len);
- if (file == NULL)
- {
- g_free(icon_data);
- return NULL;
- }
- }
- else
- file = g_strdup(filename);
+ newimg = purple_image_new_from_data(icon_data, icon_len);
+ filename = purple_image_generate_filename(newimg);
- if ((img = g_hash_table_lookup(icon_data_cache, file)))
- {
- g_free(file);
- g_free(icon_data);
- return purple_imgstore_ref(img);
+ oldimg = g_hash_table_lookup(icon_data_cache, filename);
+ if (oldimg) {
+ g_warn_if_fail(PURPLE_IS_IMAGE(oldimg));
+ g_object_unref(newimg);
+ g_object_ref(oldimg);
+ return oldimg;
}
- img = purple_imgstore_add(icon_data, icon_len, file);
+ /* This will take ownership of file and free it as needed */
+ g_hash_table_insert(icon_data_cache, g_strdup(filename), newimg);
+ g_object_set_data_full(G_OBJECT(newimg), "purple-buddyicon-filename",
+ g_strdup(filename), image_deleting_cb);
- /* This will take ownership of file and g_free it either now or later. */
- g_hash_table_insert(icon_data_cache, file, img);
+ purple_buddy_icon_data_cache(newimg);
- purple_buddy_icon_data_cache(img);
-
- return img;
+ return newimg;
}
/*
@@ -355,13 +344,13 @@ purple_buddy_icon_ref(PurpleBuddyIcon *icon)
return icon;
}
-PurpleBuddyIcon *
+void
purple_buddy_icon_unref(PurpleBuddyIcon *icon)
{
if (icon == NULL)
- return NULL;
+ return;
- g_return_val_if_fail(icon->ref_count > 0, NULL);
+ g_return_if_fail(icon->ref_count > 0);
icon->ref_count--;
@@ -374,21 +363,17 @@ purple_buddy_icon_unref(PurpleBuddyIcon *icon)
g_free(icon->username);
g_free(icon->checksum);
- purple_imgstore_unref(icon->img);
+ g_object_unref(icon->img);
PURPLE_DBUS_UNREGISTER_POINTER(icon);
g_slice_free(PurpleBuddyIcon, icon);
-
- return NULL;
}
-
- return icon;
}
void
purple_buddy_icon_update(PurpleBuddyIcon *icon)
{
- PurpleConversation *conv;
+ PurpleIMConversation *im;
PurpleAccount *account;
const char *username;
PurpleBuddyIcon *icon_to_set;
@@ -409,7 +394,7 @@ purple_buddy_icon_update(PurpleBuddyIcon *icon)
/* Ensure that icon remains valid throughout */
purple_buddy_icon_ref(icon);
- buddies = purple_find_buddies(account, username);
+ buddies = purple_blist_find_buddies(account, username);
while (buddies != NULL)
{
PurpleBuddy *buddy = (PurpleBuddy *)buddies->data;
@@ -420,7 +405,8 @@ purple_buddy_icon_update(PurpleBuddyIcon *icon)
"buddy_icon"));
if (icon->img && purple_buddy_icons_is_caching())
{
- const char *filename = purple_imgstore_get_filename(icon->img);
+ const char *filename = image_get_filename(icon->img);
+ g_warn_if_fail(filename != NULL);
purple_blist_node_set_string((PurpleBlistNode *)buddy,
"buddy_icon",
filename);
@@ -449,10 +435,10 @@ purple_buddy_icon_update(PurpleBuddyIcon *icon)
buddies = g_slist_delete_link(buddies, buddies);
}
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, username, account);
+ im = purple_conversations_find_im_with_account(username, account);
- if (conv != NULL)
- purple_conv_im_set_icon(PURPLE_CONV_IM(conv), icon_to_set);
+ if (im != NULL)
+ purple_im_conversation_set_icon(im, icon_to_set);
/* icon's refcount was incremented above */
purple_buddy_icon_unref(icon);
@@ -462,7 +448,7 @@ void
purple_buddy_icon_set_data(PurpleBuddyIcon *icon, guchar *data,
size_t len, const char *checksum)
{
- PurpleStoredImage *old_img;
+ PurpleImage *old_img;
g_return_if_fail(icon != NULL);
@@ -472,7 +458,7 @@ purple_buddy_icon_set_data(PurpleBuddyIcon *icon, guchar *data,
if (data != NULL)
{
if (len > 0)
- icon->img = purple_buddy_icon_data_new(data, len, NULL);
+ icon->img = purple_buddy_icon_data_new(data, len);
else
g_free(data);
}
@@ -482,7 +468,8 @@ purple_buddy_icon_set_data(PurpleBuddyIcon *icon, guchar *data,
purple_buddy_icon_update(icon);
- purple_imgstore_unref(old_img);
+ if (old_img)
+ g_object_unref(old_img);
}
PurpleAccount *
@@ -517,9 +504,9 @@ purple_buddy_icon_get_data(const PurpleBuddyIcon *icon, size_t *len)
if (icon->img)
{
if (len != NULL)
- *len = purple_imgstore_get_size(icon->img);
+ *len = purple_image_get_size(icon->img);
- return purple_imgstore_get_data(icon->img);
+ return purple_image_get_data(icon->img);
}
return NULL;
@@ -529,7 +516,7 @@ const char *
purple_buddy_icon_get_extension(const PurpleBuddyIcon *icon)
{
if (icon->img != NULL)
- return purple_imgstore_get_extension(icon->img);
+ return purple_image_get_extension(icon->img);
return NULL;
}
@@ -571,7 +558,7 @@ purple_buddy_icons_set_for_user(PurpleAccount *account, const char *username,
* Since we know we're deleting the icon, we only
* need a subset of purple_buddy_icon_update(). */
- GSList *buddies = purple_find_buddies(account, username);
+ GSList *buddies = purple_blist_find_buddies(account, username);
while (buddies != NULL)
{
PurpleBuddy *buddy = (PurpleBuddy *)buddies->data;
@@ -585,20 +572,19 @@ purple_buddy_icons_set_for_user(PurpleAccount *account, const char *username,
}
}
-char *purple_buddy_icon_get_full_path(PurpleBuddyIcon *icon)
+const gchar *
+purple_buddy_icon_get_full_path(PurpleBuddyIcon *icon)
{
- char *path;
+ const gchar *path;
g_return_val_if_fail(icon != NULL, NULL);
if (icon->img == NULL)
return NULL;
- path = g_build_filename(purple_buddy_icons_get_cache_dir(),
- purple_imgstore_get_filename(icon->img), NULL);
+ path = purple_image_get_path(icon->img);
if (!g_file_test(path, G_FILE_TEST_EXISTS))
{
- g_free(path);
return NULL;
}
return path;
@@ -641,7 +627,8 @@ purple_buddy_icons_find(PurpleAccount *account, const char *username)
if ((icon_cache == NULL) || ((icon = g_hash_table_lookup(icon_cache, username)) == NULL))
{
- PurpleBuddy *b = purple_find_buddy(account, username);
+ /* The icon is not currently cached in memory--try reading from disk */
+ PurpleBuddy *b = purple_blist_find_buddy(account, username);
const char *protocol_icon_file;
const char *dirname;
gboolean caching;
@@ -688,10 +675,10 @@ purple_buddy_icons_find(PurpleAccount *account, const char *username)
return (icon ? purple_buddy_icon_ref(icon) : NULL);
}
-PurpleStoredImage *
+PurpleImage *
purple_buddy_icons_find_account_icon(PurpleAccount *account)
{
- PurpleStoredImage *img;
+ PurpleImage *img;
const char *account_icon_file;
const char *dirname;
char *path;
@@ -700,9 +687,10 @@ purple_buddy_icons_find_account_icon(PurpleAccount *account)
g_return_val_if_fail(account != NULL, NULL);
- if ((img = g_hash_table_lookup(pointer_icon_cache, account)))
- {
- return purple_imgstore_ref(img);
+ img = g_hash_table_lookup(pointer_icon_cache, account);
+ if (img) {
+ g_object_ref(img);
+ return img;
}
account_icon_file = purple_account_get_string(account, "buddy_icon", NULL);
@@ -713,34 +701,34 @@ purple_buddy_icons_find_account_icon(PurpleAccount *account)
dirname = purple_buddy_icons_get_cache_dir();
path = g_build_filename(dirname, account_icon_file, NULL);
- if (read_icon_file(path, &data, &len))
- {
+ if (read_icon_file(path, &data, &len)) {
g_free(path);
img = purple_buddy_icons_set_account_icon(account, data, len);
- return purple_imgstore_ref(img);
+ g_object_ref(img);
+ return img;
}
g_free(path);
return NULL;
}
-PurpleStoredImage *
+PurpleImage *
purple_buddy_icons_set_account_icon(PurpleAccount *account,
guchar *icon_data, size_t icon_len)
{
- PurpleStoredImage *old_img;
- PurpleStoredImage *img = NULL;
+ PurpleImage *old_img;
+ PurpleImage *img = NULL;
char *old_icon;
- if (icon_data != NULL && icon_len > 0)
- {
- img = purple_buddy_icon_data_new(icon_data, icon_len, NULL);
+ if (icon_data != NULL && icon_len > 0) {
+ img = purple_buddy_icon_data_new(icon_data, icon_len);
}
old_icon = g_strdup(purple_account_get_string(account, "buddy_icon", NULL));
if (img && purple_buddy_icons_is_caching())
{
- const char *filename = purple_imgstore_get_filename(img);
+ const char *filename = image_get_filename(img);
+ g_warn_if_fail(filename != NULL);
purple_account_set_string(account, "buddy_icon", filename);
purple_account_set_int(account, "buddy_icon_timestamp", time(NULL));
ref_filename(filename);
@@ -759,7 +747,7 @@ purple_buddy_icons_set_account_icon(PurpleAccount *account,
else
g_hash_table_remove(pointer_icon_cache, account);
- if (purple_account_is_connected(account))
+ if (!purple_account_is_disconnected(account))
{
PurpleConnection *gc;
PurplePluginProtocolInfo *prpl_info;
@@ -772,7 +760,7 @@ purple_buddy_icons_set_account_icon(PurpleAccount *account,
}
if (old_img)
- purple_imgstore_unref(old_img);
+ g_object_unref(old_img);
else if (old_icon)
{
/* The old icon may not have been loaded into memory. In that
@@ -812,20 +800,21 @@ purple_buddy_icons_node_has_custom_icon(PurpleBlistNode *node)
return (purple_blist_node_get_string(node, "custom_buddy_icon") != NULL);
}
-PurpleStoredImage *
+PurpleImage *
purple_buddy_icons_node_find_custom_icon(PurpleBlistNode *node)
{
char *path;
size_t len;
guchar *data;
- PurpleStoredImage *img;
+ PurpleImage *img;
const char *custom_icon_file, *dirname;
g_return_val_if_fail(node != NULL, NULL);
- if ((img = g_hash_table_lookup(pointer_icon_cache, node)))
- {
- return purple_imgstore_ref(img);
+ img = g_hash_table_lookup(pointer_icon_cache, node);
+ if (img) {
+ g_object_ref(img);
+ return img;
}
custom_icon_file = purple_blist_node_get_string(node,
@@ -837,43 +826,45 @@ purple_buddy_icons_node_find_custom_icon(PurpleBlistNode *node)
dirname = purple_buddy_icons_get_cache_dir();
path = g_build_filename(dirname, custom_icon_file, NULL);
- if (read_icon_file(path, &data, &len))
- {
+ if (read_icon_file(path, &data, &len)) {
g_free(path);
img = purple_buddy_icons_node_set_custom_icon(node, data, len);
- return purple_imgstore_ref(img);
+ g_object_ref(img);
+ return img;
}
g_free(path);
return NULL;
}
-PurpleStoredImage *
+PurpleImage *
purple_buddy_icons_node_set_custom_icon(PurpleBlistNode *node,
guchar *icon_data, size_t icon_len)
{
char *old_icon;
- PurpleStoredImage *old_img;
- PurpleStoredImage *img = NULL;
+ PurpleImage *old_img;
+ PurpleImage *img = NULL;
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
g_return_val_if_fail(node != NULL, NULL);
- if (!PURPLE_BLIST_NODE_IS_CONTACT(node) &&
- !PURPLE_BLIST_NODE_IS_CHAT(node) &&
- !PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ if (!PURPLE_IS_CONTACT(node) &&
+ !PURPLE_IS_CHAT(node) &&
+ !PURPLE_IS_GROUP(node)) {
return NULL;
}
old_img = g_hash_table_lookup(pointer_icon_cache, node);
if (icon_data != NULL && icon_len > 0) {
- img = purple_buddy_icon_data_new(icon_data, icon_len, NULL);
+ img = purple_buddy_icon_data_new(icon_data, icon_len);
}
old_icon = g_strdup(purple_blist_node_get_string(node,
"custom_buddy_icon"));
if (img && purple_buddy_icons_is_caching()) {
- const char *filename = purple_imgstore_get_filename(img);
+ const char *filename = image_get_filename(img);
+ g_warn_if_fail(filename);
purple_blist_node_set_string(node, "custom_buddy_icon",
filename);
ref_filename(filename);
@@ -887,42 +878,44 @@ purple_buddy_icons_node_set_custom_icon(PurpleBlistNode *node,
else
g_hash_table_remove(pointer_icon_cache, node);
- if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ if (PURPLE_IS_CONTACT(node)) {
PurpleBlistNode *child;
for (child = purple_blist_node_get_first_child(node);
child;
child = purple_blist_node_get_sibling_next(child))
{
PurpleBuddy *buddy;
- PurpleConversation *conv;
+ PurpleIMConversation *im;
- if (!PURPLE_BLIST_NODE_IS_BUDDY(child))
+ if (!PURPLE_IS_BUDDY(child))
continue;
buddy = (PurpleBuddy *)child;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, purple_buddy_get_name(buddy), purple_buddy_get_account(buddy));
- if (conv)
- purple_conversation_update(conv, PURPLE_CONV_UPDATE_ICON);
+ im = purple_conversations_find_im_with_account(purple_buddy_get_name(buddy), purple_buddy_get_account(buddy));
+ if (im)
+ purple_conversation_update(PURPLE_CONVERSATION(im), PURPLE_CONVERSATION_UPDATE_ICON);
/* Is this call necessary anymore? Can the buddies
* themselves need updating when the custom buddy
* icon changes? */
- purple_blist_update_node_icon((PurpleBlistNode*)buddy);
+ if (ops && ops->update)
+ ops->update(purple_blist_get_buddy_list(), PURPLE_BLIST_NODE(buddy));
}
- } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
- PurpleConversation *conv = NULL;
+ } else if (PURPLE_IS_CHAT(node)) {
+ PurpleChatConversation *chat = NULL;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, purple_chat_get_name((PurpleChat*)node), purple_chat_get_account((PurpleChat*)node));
- if (conv) {
- purple_conversation_update(conv, PURPLE_CONV_UPDATE_ICON);
+ chat = purple_conversations_find_chat_with_account(purple_chat_get_name((PurpleChat*)node), purple_chat_get_account((PurpleChat*)node));
+ if (chat) {
+ purple_conversation_update(PURPLE_CONVERSATION(chat), PURPLE_CONVERSATION_UPDATE_ICON);
}
}
- purple_blist_update_node_icon(node);
+ if (ops && ops->update)
+ ops->update(purple_blist_get_buddy_list(), node);
if (old_img) {
- purple_imgstore_unref(old_img);
+ g_object_unref(old_img);
} else if (old_icon) {
/* The old icon may not have been loaded into memory. In that
* case, we'll need to uncache the filename. The filenames
@@ -934,7 +927,7 @@ purple_buddy_icons_node_set_custom_icon(PurpleBlistNode *node,
return img;
}
-PurpleStoredImage *
+PurpleImage *
purple_buddy_icons_node_set_custom_icon_from_file(PurpleBlistNode *node,
const gchar *filename)
{
@@ -943,9 +936,9 @@ purple_buddy_icons_node_set_custom_icon_from_file(PurpleBlistNode *node,
g_return_val_if_fail(node != NULL, NULL);
- if (!PURPLE_BLIST_NODE_IS_CONTACT(node) &&
- !PURPLE_BLIST_NODE_IS_CHAT(node) &&
- !PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ if (!PURPLE_IS_CONTACT(node) &&
+ !PURPLE_IS_CHAT(node) &&
+ !PURPLE_IS_GROUP(node)) {
return NULL;
}
@@ -958,31 +951,6 @@ purple_buddy_icons_node_set_custom_icon_from_file(PurpleBlistNode *node,
return purple_buddy_icons_node_set_custom_icon(node, data, len);
}
-gboolean
-purple_buddy_icons_has_custom_icon(PurpleContact *contact)
-{
- return purple_buddy_icons_node_has_custom_icon((PurpleBlistNode*)contact);
-}
-
-PurpleStoredImage *
-purple_buddy_icons_find_custom_icon(PurpleContact *contact)
-{
- return purple_buddy_icons_node_find_custom_icon((PurpleBlistNode*)contact);
-}
-
-PurpleStoredImage *
-purple_buddy_icons_set_custom_icon(PurpleContact *contact, guchar *icon_data,
- size_t icon_len)
-{
- return purple_buddy_icons_node_set_custom_icon((PurpleBlistNode*)contact, icon_data, icon_len);
-}
-
-void
-_purple_buddy_icon_set_old_icons_dir(const char *dirname)
-{
- old_icons_dir = g_strdup(dirname);
-}
-
static void
delete_buddy_icon_settings(PurpleBlistNode *node, const char *setting_name)
{
@@ -995,133 +963,6 @@ delete_buddy_icon_settings(PurpleBlistNode *node, const char *setting_name)
}
}
-static void
-migrate_buddy_icon(PurpleBlistNode *node, const char *setting_name,
- const char *dirname, const char *filename)
-{
- char *path;
-
- if (filename[0] != '/')
- {
- path = g_build_filename(dirname, filename, NULL);
- if (g_file_test(path, G_FILE_TEST_EXISTS))
- {
- g_free(path);
- return;
- }
- g_free(path);
-
- path = g_build_filename(old_icons_dir, filename, NULL);
- }
- else
- path = g_strdup(filename);
-
- if (g_file_test(path, G_FILE_TEST_EXISTS))
- {
- guchar *icon_data;
- size_t icon_len;
- FILE *file;
- char *new_filename;
-
- if (!read_icon_file(path, &icon_data, &icon_len))
- {
- g_free(path);
- delete_buddy_icon_settings(node, setting_name);
- return;
- }
-
- if (icon_data == NULL || icon_len <= 0)
- {
- /* This really applies to the icon_len check.
- * icon_data should never be NULL if
- * read_icon_file() returns TRUE. */
- purple_debug_error("buddyicon", "Empty buddy icon file: %s\n", path);
- delete_buddy_icon_settings(node, setting_name);
- g_free(path);
- return;
- }
-
- g_free(path);
-
- new_filename = purple_util_get_image_filename(icon_data, icon_len);
- if (new_filename == NULL)
- {
- purple_debug_error("buddyicon",
- "New icon filename is NULL. This should never happen! "
- "The old filename was: %s\n", path);
- delete_buddy_icon_settings(node, setting_name);
- g_return_if_reached();
- }
-
- path = g_build_filename(dirname, new_filename, NULL);
- if ((file = g_fopen(path, "wb")) != NULL)
- {
- if (!fwrite(icon_data, icon_len, 1, file))
- {
- purple_debug_error("buddyicon", "Error writing %s: %s\n",
- path, g_strerror(errno));
- }
- else
- purple_debug_info("buddyicon", "Wrote migrated cache file: %s\n", path);
-
- fclose(file);
- }
- else
- {
- purple_debug_error("buddyicon", "Unable to create file %s: %s\n",
- path, g_strerror(errno));
- g_free(new_filename);
- g_free(path);
-
- delete_buddy_icon_settings(node, setting_name);
- return;
- }
- g_free(path);
-
- purple_blist_node_set_string(node,
- setting_name,
- new_filename);
- ref_filename(new_filename);
-
- g_free(new_filename);
-
- if (purple_strequal(setting_name, "buddy_icon"))
- {
- const char *hash;
-
- hash = purple_blist_node_get_string(node, "avatar_hash");
- if (hash != NULL)
- {
- purple_blist_node_set_string(node, "icon_checksum", hash);
- purple_blist_node_remove_setting(node, "avatar_hash");
- }
- else
- {
- PurpleAccount *account = purple_buddy_get_account((PurpleBuddy *)node);
- const char *prpl_id = purple_account_get_protocol_id(account);
-
- if (g_str_equal(prpl_id, "prpl-yahoo") || g_str_equal(prpl_id, "prpl-yahoojp"))
- {
- int checksum = purple_blist_node_get_int(node, "icon_checksum");
- if (checksum != 0)
- {
- char *checksum_str = g_strdup_printf("%i", checksum);
- purple_blist_node_remove_setting(node, "icon_checksum");
- purple_blist_node_set_string(node, "icon_checksum", checksum_str);
- g_free(checksum_str);
- }
- }
- }
- }
- }
- else
- {
- purple_debug_error("buddyicon", "Old icon file doesn't exist: %s\n", path);
- delete_buddy_icon_settings(node, setting_name);
- g_free(path);
- }
-}
-
void
_purple_buddy_icons_account_loaded_cb()
{
@@ -1153,80 +994,46 @@ _purple_buddy_icons_blist_loaded_cb()
PurpleBlistNode *node = purple_blist_get_root();
const char *dirname = purple_buddy_icons_get_cache_dir();
- /* Doing this once here saves having to check it inside a loop. */
- if (old_icons_dir != NULL)
- {
- if (!g_file_test(dirname, G_FILE_TEST_IS_DIR))
- {
- purple_debug_info("buddyicon", "Creating icon cache directory.\n");
-
- if (g_mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR) < 0)
- {
- purple_debug_error("buddyicon",
- "Unable to create directory %s: %s\n",
- dirname, g_strerror(errno));
- }
- }
- }
-
while (node != NULL)
{
- if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if (PURPLE_IS_BUDDY(node))
{
const char *filename;
filename = purple_blist_node_get_string(node, "buddy_icon");
if (filename != NULL)
{
- if (old_icons_dir != NULL)
+ char *path = g_build_filename(dirname, filename, NULL);
+ if (!g_file_test(path, G_FILE_TEST_EXISTS))
{
- migrate_buddy_icon(node,
- "buddy_icon",
- dirname, filename);
+ purple_blist_node_remove_setting(node,
+ "buddy_icon");
+ purple_blist_node_remove_setting(node,
+ "icon_checksum");
}
else
- {
- char *path = g_build_filename(dirname, filename, NULL);
- if (!g_file_test(path, G_FILE_TEST_EXISTS))
- {
- purple_blist_node_remove_setting(node,
- "buddy_icon");
- purple_blist_node_remove_setting(node,
- "icon_checksum");
- }
- else
- ref_filename(filename);
- g_free(path);
- }
+ ref_filename(filename);
+ g_free(path);
}
}
- else if (PURPLE_BLIST_NODE_IS_CONTACT(node) ||
- PURPLE_BLIST_NODE_IS_CHAT(node) ||
- PURPLE_BLIST_NODE_IS_GROUP(node))
+ else if (PURPLE_IS_CONTACT(node) ||
+ PURPLE_IS_CHAT(node) ||
+ PURPLE_IS_GROUP(node))
{
const char *filename;
filename = purple_blist_node_get_string(node, "custom_buddy_icon");
if (filename != NULL)
{
- if (old_icons_dir != NULL)
+ char *path = g_build_filename(dirname, filename, NULL);
+ if (!g_file_test(path, G_FILE_TEST_EXISTS))
{
- migrate_buddy_icon(node,
- "custom_buddy_icon",
- dirname, filename);
+ purple_blist_node_remove_setting(node,
+ "custom_buddy_icon");
}
else
- {
- char *path = g_build_filename(dirname, filename, NULL);
- if (!g_file_test(path, G_FILE_TEST_EXISTS))
- {
- purple_blist_node_remove_setting(node,
- "custom_buddy_icon");
- }
- else
- ref_filename(filename);
- g_free(path);
- }
+ ref_filename(filename);
+ g_free(path);
}
}
node = purple_blist_node_next(node, TRUE);
@@ -1282,11 +1089,7 @@ purple_buddy_icons_init()
pointer_icon_cache = g_hash_table_new(g_direct_hash, g_direct_equal);
if (!cache_dir)
- cache_dir = g_build_filename(purple_user_dir(), "icons", NULL);
-
- purple_signal_connect(purple_imgstore_get_handle(), "image-deleting",
- purple_buddy_icons_get_handle(),
- G_CALLBACK(image_deleting_cb), NULL);
+ cache_dir = g_build_filename(purple_user_dir(), "icons", NULL);
}
void
@@ -1298,11 +1101,9 @@ purple_buddy_icons_uninit()
g_hash_table_destroy(icon_data_cache);
g_hash_table_destroy(icon_file_cache);
g_hash_table_destroy(pointer_icon_cache);
- g_free(old_icons_dir);
g_free(cache_dir);
cache_dir = NULL;
- old_icons_dir = NULL;
}
void purple_buddy_icon_get_scale_size(PurpleBuddyIconSpec *spec, int *width, int *height)
@@ -1333,3 +1134,17 @@ void purple_buddy_icon_get_scale_size(PurpleBuddyIconSpec *spec, int *width, int
*width = new_width;
*height = new_height;
}
+
+GType
+purple_buddy_icon_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleBuddyIcon",
+ (GBoxedCopyFunc)purple_buddy_icon_ref,
+ (GBoxedFreeFunc)purple_buddy_icon_unref);
+ }
+
+ return type;
+}
diff --git a/libpurple/buddyicon.h b/libpurple/buddyicon.h
index 7fbecbb9a8..e46d1e8069 100644
--- a/libpurple/buddyicon.h
+++ b/libpurple/buddyicon.h
@@ -1,8 +1,3 @@
-/**
- * @file buddyicon.h Buddy Icon API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -23,141 +18,166 @@
* 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_BUDDYICON_H_
#define _PURPLE_BUDDYICON_H_
+/**
+ * SECTION:buddyicon
+ * @section_id: libpurple-buddyicon
+ * @short_description: <filename>buddyicon.h</filename>
+ * @title: Buddy Icon API
+ */
+
+#define PURPLE_TYPE_BUDDY_ICON (purple_buddy_icon_get_type())
-/** An opaque structure representing a buddy icon for a particular user on a
- * particular #PurpleAccount. Instances are reference-counted; use
- * purple_buddy_icon_ref() and purple_buddy_icon_unref() to take and release
- * references.
+/**
+ * PurpleBuddyIcon:
+ *
+ * An opaque structure representing a buddy icon for a particular user on a
+ * particular #PurpleAccount. Instances are reference-counted; use
+ * purple_buddy_icon_ref() and purple_buddy_icon_unref() to take and release
+ * references.
*/
typedef struct _PurpleBuddyIcon PurpleBuddyIcon;
#include "account.h"
-#include "blist.h"
-#include "imgstore.h"
+#include "buddylist.h"
#include "prpl.h"
#include "util.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name Buddy Icon API */
+/* Buddy Icon API */
/**************************************************************************/
-/*@{*/
/**
- * Creates a new buddy icon structure and populates it.
+ * purple_buddy_icon_get_type:
*
- * If the buddy icon already exists, you'll get a reference to that structure,
- * which will have been updated with the data supplied.
+ * Returns: The #GType for the #PurpleBuddyIcon boxed structure.
+ */
+GType purple_buddy_icon_get_type(void);
+
+/**
+ * purple_buddy_icon_new:
+ * @account: The account the user is on.
+ * @username: The username the icon belongs to.
+ * @icon_data: The buddy icon data.
+ * @icon_len: The buddy icon length.
+ * @checksum: A protocol checksum from the protocol or %NULL.
+ *
+ * Creates a new buddy icon structure and populates it.
*
- * @param account The account the user is on.
- * @param username The username the icon belongs to.
- * @param icon_data The buddy icon data.
- * @param icon_len The buddy icon length.
- * @param checksum A protocol checksum from the prpl or @c NULL.
+ * If an icon for this account+username already exists, you'll get a reference
+ * to that structure, which will have been updated with the data supplied.
*
- * @return The buddy icon structure, with a reference for the caller.
+ * Returns: The buddy icon structure, with a reference for the caller.
*/
PurpleBuddyIcon *purple_buddy_icon_new(PurpleAccount *account, const char *username,
void *icon_data, size_t icon_len,
const char *checksum);
/**
- * Increments the reference count on a buddy icon.
+ * purple_buddy_icon_ref:
+ * @icon: The buddy icon.
*
- * @param icon The buddy icon.
+ * Increments the reference count on a buddy icon.
*
- * @return @a icon.
+ * Returns: @icon.
*/
PurpleBuddyIcon *purple_buddy_icon_ref(PurpleBuddyIcon *icon);
/**
+ * purple_buddy_icon_unref:
+ * @icon: The buddy icon.
+ *
* Decrements the reference count on a buddy icon.
*
* If the reference count reaches 0, the icon will be destroyed.
- *
- * @param icon The buddy icon.
- *
- * @return @a icon, or @c NULL if the reference count reached 0.
*/
-PurpleBuddyIcon *purple_buddy_icon_unref(PurpleBuddyIcon *icon);
+void purple_buddy_icon_unref(PurpleBuddyIcon *icon);
/**
- * Updates every instance of this icon.
+ * purple_buddy_icon_update:
+ * @icon: The buddy icon.
*
- * @param icon The buddy icon.
+ * Updates every instance of this icon.
*/
void purple_buddy_icon_update(PurpleBuddyIcon *icon);
/**
- * Sets the buddy icon's data.
- *
- * @param icon The buddy icon.
- * @param data The buddy icon data, which the buddy icon code
+ * purple_buddy_icon_set_data:
+ * @icon: The buddy icon.
+ * @data: The buddy icon data, which the buddy icon code
* takes ownership of and will free.
- * @param len The length of the data in @a data.
- * @param checksum A protocol checksum from the prpl or @c NULL.
+ * @len: The length of the data in @a data.
+ * @checksum: A protocol checksum from the protocol or %NULL.
+ *
+ * Sets the buddy icon's data.
*/
void
purple_buddy_icon_set_data(PurpleBuddyIcon *icon, guchar *data,
size_t len, const char *checksum);
/**
- * Returns the buddy icon's account.
+ * purple_buddy_icon_get_account:
+ * @icon: The buddy icon.
*
- * @param icon The buddy icon.
+ * Returns the buddy icon's account.
*
- * @return The account.
+ * Returns: The account.
*/
PurpleAccount *purple_buddy_icon_get_account(const PurpleBuddyIcon *icon);
/**
- * Returns the buddy icon's username.
+ * purple_buddy_icon_get_username:
+ * @icon: The buddy icon.
*
- * @param icon The buddy icon.
+ * Returns the buddy icon's username.
*
- * @return The username.
+ * Returns: The username.
*/
const char *purple_buddy_icon_get_username(const PurpleBuddyIcon *icon);
/**
- * Returns the buddy icon's checksum.
+ * purple_buddy_icon_get_checksum:
+ * @icon: The buddy icon.
*
- * This function is really only for prpl use.
+ * Returns the buddy icon's checksum.
*
- * @param icon The buddy icon.
+ * This function is really only for protocol use.
*
- * @return The checksum.
+ * Returns: The checksum.
*/
const char *purple_buddy_icon_get_checksum(const PurpleBuddyIcon *icon);
/**
- * Returns the buddy icon's data.
- *
- * @param icon The buddy icon.
- * @param len If not @c NULL, the length of the icon data returned will be
+ * purple_buddy_icon_get_data:
+ * @icon: The buddy icon.
+ * @len: If not %NULL, the length of the icon data returned will be
* set in the location pointed to by this.
*
- * @return A pointer to the icon data.
+ * Returns the buddy icon's data.
+ *
+ * Returns: A pointer to the icon data.
*/
gconstpointer purple_buddy_icon_get_data(const PurpleBuddyIcon *icon, size_t *len);
/**
- * Returns an extension corresponding to the buddy icon's file type.
+ * purple_buddy_icon_get_extension:
+ * @icon: The buddy icon.
*
- * @param icon The buddy icon.
+ * Returns an extension corresponding to the buddy icon's file type.
*
- * @return The icon's extension, "icon" if unknown, or @c NULL if
+ * Returns: The icon's extension, "icon" if unknown, or %NULL if
* the image data has disappeared.
*/
const char *purple_buddy_icon_get_extension(const PurpleBuddyIcon *icon);
/**
+ * purple_buddy_icon_get_full_path:
+ * @icon: The buddy icon
+ *
* Returns a full path to an icon.
*
* If the icon has data and the file exists in the cache, this will return
@@ -167,28 +187,25 @@ const char *purple_buddy_icon_get_extension(const PurpleBuddyIcon *icon);
* directly. If you find yourself wanting to use this function, think
* very long and hard about it, and then don't.
*
- * @param icon The buddy icon
- *
- * @return A full path to the file, or @c NULL under various conditions.
+ * Returns: (transfer none): A full path to the file, or %NULL under various conditions.
*/
-char *purple_buddy_icon_get_full_path(PurpleBuddyIcon *icon);
-
-/*@}*/
+const gchar *
+purple_buddy_icon_get_full_path(PurpleBuddyIcon *icon);
/**************************************************************************/
-/** @name Buddy Icon Subsystem API */
+/* Buddy Icon Subsystem API */
/**************************************************************************/
-/*@{*/
/**
- * Sets a buddy icon for a user.
- *
- * @param account The account the user is on.
- * @param username The username of the user.
- * @param icon_data The buddy icon data, which the buddy icon code
+ * purple_buddy_icons_set_for_user:
+ * @account: The account the user is on.
+ * @username: The username of the user.
+ * @icon_data: The buddy icon data, which the buddy icon code
* takes ownership of and will free.
- * @param icon_len The length of the icon data.
- * @param checksum A protocol checksum from the prpl or @c NULL.
+ * @icon_len: The length of the icon data.
+ * @checksum: A protocol checksum from the protocol or %NULL.
+ *
+ * Sets a buddy icon for a user.
*/
void
purple_buddy_icons_set_for_user(PurpleAccount *account, const char *username,
@@ -196,246 +213,225 @@ purple_buddy_icons_set_for_user(PurpleAccount *account, const char *username,
const char *checksum);
/**
+ * purple_buddy_icons_get_checksum_for_user:
+ * @buddy: The buddy
+ *
* Returns the checksum for the buddy icon of a specified buddy.
*
* This avoids loading the icon image data from the cache if it's
* not already loaded for some other reason.
*
- * @param buddy The buddy
- *
- * @return The checksum.
+ * Returns: The checksum.
*/
const char *
purple_buddy_icons_get_checksum_for_user(PurpleBuddy *buddy);
/**
- * Returns the buddy icon information for a user.
+ * purple_buddy_icons_find:
+ * @account: The account the user is on.
+ * @username: The username of the user.
*
- * @param account The account the user is on.
- * @param username The username of the user.
+ * Returns the buddy icon information for a user.
*
- * @return The icon (with a reference for the caller) if found, or @c NULL if
+ * Returns: The icon (with a reference for the caller) if found, or %NULL if
* not found.
*/
PurpleBuddyIcon *
purple_buddy_icons_find(PurpleAccount *account, const char *username);
/**
+ * purple_buddy_icons_find_account_icon:
+ * @account: The account
+ *
* Returns the buddy icon image for an account.
*
- * The caller owns a reference to the image in the store, and must dereference
- * the image with purple_imgstore_unref() for it to be freed.
+ * 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.
*
- * @param account The account
- *
- * @return The account's buddy icon image.
+ * Returns: The account's buddy icon image.
*/
-PurpleStoredImage *
+PurpleImage *
purple_buddy_icons_find_account_icon(PurpleAccount *account);
/**
+ * purple_buddy_icons_set_account_icon:
+ * @account: The account for which to set a custom icon.
+ * @icon_data: The image data of the icon, which the
+ * buddy icon code will free.
+ * @icon_len: The length of the data in @icon_data.
+ *
* Sets a buddy icon for an account.
*
* This function will deal with saving a record of the icon,
* caching the data, etc.
*
- * @param account The account for which to set a custom icon.
- * @param icon_data The image data of the icon, which the
- * buddy icon code will free.
- * @param icon_len The length of the data in @a icon_data.
- *
- * @return The icon that was set. The caller does NOT own
- * a reference to this, and must call purple_imgstore_ref()
+ * 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.
*/
-PurpleStoredImage *
+PurpleImage *
purple_buddy_icons_set_account_icon(PurpleAccount *account,
guchar *icon_data, size_t icon_len);
/**
+ * purple_buddy_icons_get_account_icon_timestamp:
+ * @account: The account
+ *
* Returns the timestamp of when the icon was set.
*
* This is intended for use in protocols that require a timestamp for
* buddy icon update reasons.
*
- * @param account The account
- *
- * @return The time the icon was set, or 0 if an error occurred.
+ * Returns: The time the icon was set, or 0 if an error occurred.
*/
time_t
purple_buddy_icons_get_account_icon_timestamp(PurpleAccount *account);
/**
- * Returns a boolean indicating if a given blist node has a custom buddy icon.
+ * purple_buddy_icons_node_has_custom_icon:
+ * @node: The blist node.
*
- * @param node The blist node.
+ * Returns a boolean indicating if a given blist node has a custom buddy icon.
*
- * @return A boolean indicating if @a node has a custom buddy icon.
- * @since 2.5.0
+ * Returns: A boolean indicating if @node has a custom buddy icon.
*/
gboolean
purple_buddy_icons_node_has_custom_icon(PurpleBlistNode *node);
/**
+ * purple_buddy_icons_node_find_custom_icon:
+ * @node: The node.
+ *
* Returns the custom buddy icon image for a blist node.
*
- * The caller owns a reference to the image in the store, and must dereference
- * the image with purple_imgstore_unref() for it to be freed.
+ * 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.
*
- * @param node The node.
- *
- * @return The custom buddy icon.
- * @since 2.5.0
+ * Returns: The custom buddy icon.
*/
-PurpleStoredImage *
+PurpleImage *
purple_buddy_icons_node_find_custom_icon(PurpleBlistNode *node);
/**
+ * purple_buddy_icons_node_set_custom_icon:
+ * @node: The blist node for which to set a custom icon.
+ * @icon_data: The image data of the icon, which the buddy icon code will
+ * free. Use NULL to unset the icon.
+ * @icon_len: The length of the data in @icon_data.
+ *
* Sets a custom buddy icon for a blist node.
*
* This function will deal with saving a record of the icon, caching the data,
* etc.
*
- * @param node The blist node for which to set a custom icon.
- * @param icon_data The image data of the icon, which the buddy icon code will
- * free. Use NULL to unset the icon.
- * @param icon_len The length of the data in @a icon_data.
- *
- * @return The icon that was set. The caller does NOT own a reference to this,
- * and must call purple_imgstore_ref() if it wants one.
- * @since 2.5.0
+ * 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.
*/
-PurpleStoredImage *
+PurpleImage *
purple_buddy_icons_node_set_custom_icon(PurpleBlistNode *node,
guchar *icon_data, size_t icon_len);
/**
+ * purple_buddy_icons_node_set_custom_icon_from_file:
+ * @node: The blist node for which to set a custom icon.
+ * @filename: The path to the icon to set for the blist node. Use NULL
+ * to unset the custom icon.
+ *
* Sets a custom buddy icon for a blist node.
*
* Convenience wrapper around purple_buddy_icons_node_set_custom_icon.
- * @see purple_buddy_icons_node_set_custom_icon()
+ * See purple_buddy_icons_node_set_custom_icon().
*
- * @param node The blist node for which to set a custom icon.
- * @param filename The path to the icon to set for the blist node. Use NULL
- * to unset the custom icon.
- *
- * @return The icon that was set. The caller does NOT own a reference to this,
- * and must call purple_imgstore_ref() if it wants one.
- * @since 2.5.0
+ * 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.
*/
-PurpleStoredImage *
+PurpleImage *
purple_buddy_icons_node_set_custom_icon_from_file(PurpleBlistNode *node,
const gchar *filename);
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_BUDDYICON_C_)
/**
- * PurpleContact version of purple_buddy_icons_node_has_custom_icon.
- *
- * @copydoc purple_buddy_icons_node_has_custom_icon()
- *
- * @deprecated Use purple_buddy_icons_node_has_custom_icon instead.
- */
-gboolean
-purple_buddy_icons_has_custom_icon(PurpleContact *contact);
-
-/**
- * PurpleContact version of purple_buddy_icons_node_find_custom_icon.
- *
- * @copydoc purple_buddy_icons_node_find_custom_icon()
- *
- * @deprecated Use purple_buddy_icons_node_find_custom_icon instead.
- */
-PurpleStoredImage *
-purple_buddy_icons_find_custom_icon(PurpleContact *contact);
-
-/**
- * PurpleContact version of purple_buddy_icons_node_set_custom_icon.
- *
- * @copydoc purple_buddy_icons_node_set_custom_icon()
+ * purple_buddy_icons_set_caching:
+ * @caching: TRUE if buddy icon caching should be enabled, or
+ * FALSE otherwise.
*
- * @deprecated Use purple_buddy_icons_node_set_custom_icon instead.
- */
-PurpleStoredImage *
-purple_buddy_icons_set_custom_icon(PurpleContact *contact,
- guchar *icon_data, size_t icon_len);
-#endif
-
-/**
* Sets whether or not buddy icon caching is enabled.
- *
- * @param caching TRUE of buddy icon caching should be enabled, or
- * FALSE otherwise.
*/
void purple_buddy_icons_set_caching(gboolean caching);
/**
+ * purple_buddy_icons_is_caching:
+ *
* Returns whether or not buddy icon caching should be enabled.
*
* The default is TRUE, unless otherwise specified by
* purple_buddy_icons_set_caching().
*
- * @return TRUE if buddy icon caching is enabled, or FALSE otherwise.
+ * Returns: TRUE if buddy icon caching is enabled, or FALSE otherwise.
*/
gboolean purple_buddy_icons_is_caching(void);
/**
- * Sets the directory used to store buddy icon cache files.
+ * purple_buddy_icons_set_cache_dir:
+ * @cache_dir: The directory to store buddy icon cache files to.
*
- * @param cache_dir The directory to store buddy icon cache files to.
+ * Sets the directory used to store buddy icon cache files.
*/
void purple_buddy_icons_set_cache_dir(const char *cache_dir);
/**
+ * purple_buddy_icons_get_cache_dir:
+ *
* Returns the directory used to store buddy icon cache files.
*
* The default directory is PURPLEDIR/icons, unless otherwise specified
* by purple_buddy_icons_set_cache_dir().
*
- * @return The directory to store buddy icon cache files to.
+ * Returns: The directory to store buddy icon cache files to.
*/
const char *purple_buddy_icons_get_cache_dir(void);
/**
+ * purple_buddy_icons_get_handle:
+ *
* Returns the buddy icon subsystem handle.
*
- * @return The subsystem handle.
+ * Returns: The subsystem handle.
*/
void *purple_buddy_icons_get_handle(void);
/**
+ * purple_buddy_icons_init:
+ *
* Initializes the buddy icon subsystem.
*/
void purple_buddy_icons_init(void);
/**
+ * purple_buddy_icons_uninit:
+ *
* Uninitializes the buddy icon subsystem.
*/
void purple_buddy_icons_uninit(void);
-/*@}*/
-
/**************************************************************************/
-/** @name Buddy Icon Helper API */
+/* Buddy Icon Helper API */
/**************************************************************************/
-/*@{*/
/**
+ * purple_buddy_icon_get_scale_size:
+ *
* Gets display size for a buddy icon
*/
void purple_buddy_icon_get_scale_size(PurpleBuddyIconSpec *spec, int *width, int *height);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_BUDDYICON_H_ */
diff --git a/libpurple/buddylist.c b/libpurple/buddylist.c
new file mode 100644
index 0000000000..ae779540e7
--- /dev/null
+++ b/libpurple/buddylist.c
@@ -0,0 +1,2173 @@
+/*
+ * 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 "buddylist.h"
+#include "conversation.h"
+#include "dbus-maybe.h"
+#include "debug.h"
+#include "notify.h"
+#include "pounce.h"
+#include "prefs.h"
+#include "prpl.h"
+#include "server.h"
+#include "signals.h"
+#include "util.h"
+#include "xmlnode.h"
+
+#define PURPLE_BUDDY_LIST_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_BUDDY_LIST, PurpleBuddyListPrivate))
+
+/* Private data for a buddy list. */
+typedef struct {
+ GHashTable *buddies; /* Every buddy in this list */
+} PurpleBuddyListPrivate;
+
+static PurpleBlistUiOps *blist_ui_ops = NULL;
+
+static PurpleBuddyList *purplebuddylist = NULL;
+
+static GObjectClass *parent_class;
+
+/*
+ * A hash table used for efficient lookups of buddies by name.
+ * PurpleAccount* => GHashTable*, with the inner hash table being
+ * struct _purple_hbuddy => PurpleBuddy*
+ */
+static GHashTable *buddies_cache = NULL;
+
+/*
+ * A hash table used for efficient lookups of groups by name.
+ * UTF-8 collate-key => PurpleGroup*.
+ */
+static GHashTable *groups_cache = NULL;
+
+static guint save_timer = 0;
+static gboolean blist_loaded = FALSE;
+static gchar *localized_default_group_name = NULL;
+
+/*********************************************************************
+ * Private utility functions *
+ *********************************************************************/
+
+static gchar *
+purple_blist_fold_name(const gchar *name)
+{
+ gchar *res, *tmp;
+
+ if (name == NULL)
+ return NULL;
+
+ tmp = g_utf8_casefold(name, -1);
+ res = g_utf8_collate_key(tmp, -1);
+ g_free(tmp);
+
+ return res;
+}
+
+static PurpleBlistNode *purple_blist_get_last_sibling(PurpleBlistNode *node)
+{
+ PurpleBlistNode *n = node;
+ if (!n)
+ return NULL;
+ while (n->next)
+ n = n->next;
+ return n;
+}
+
+PurpleBlistNode *_purple_blist_get_last_child(PurpleBlistNode *node)
+{
+ if (!node)
+ return NULL;
+ return purple_blist_get_last_sibling(node->child);
+}
+
+struct _list_account_buddies {
+ GSList *list;
+ PurpleAccount *account;
+};
+
+struct _purple_hbuddy {
+ char *name;
+ PurpleAccount *account;
+ PurpleBlistNode *group;
+};
+
+/* This function must not use purple_normalize */
+static guint _purple_blist_hbuddy_hash(struct _purple_hbuddy *hb)
+{
+ return g_str_hash(hb->name) ^ g_direct_hash(hb->group) ^ g_direct_hash(hb->account);
+}
+
+/* This function must not use purple_normalize */
+static guint _purple_blist_hbuddy_equal(struct _purple_hbuddy *hb1, struct _purple_hbuddy *hb2)
+{
+ return (hb1->group == hb2->group &&
+ hb1->account == hb2->account &&
+ g_str_equal(hb1->name, hb2->name));
+}
+
+static void _purple_blist_hbuddy_free_key(struct _purple_hbuddy *hb)
+{
+ g_free(hb->name);
+ g_free(hb);
+}
+
+static void
+purple_blist_buddies_cache_add_account(PurpleAccount *account)
+{
+ GHashTable *account_buddies = g_hash_table_new_full((GHashFunc)_purple_blist_hbuddy_hash,
+ (GEqualFunc)_purple_blist_hbuddy_equal,
+ (GDestroyNotify)_purple_blist_hbuddy_free_key, NULL);
+ g_hash_table_insert(buddies_cache, account, account_buddies);
+}
+
+static void
+purple_blist_buddies_cache_remove_account(const PurpleAccount *account)
+{
+ g_hash_table_remove(buddies_cache, account);
+}
+
+/*********************************************************************
+ * Writing to disk *
+ *********************************************************************/
+
+static void
+value_to_xmlnode(gpointer key, gpointer hvalue, gpointer user_data)
+{
+ const char *name;
+ GValue *value;
+ PurpleXmlNode *node, *child;
+ char buf[21];
+
+ name = (const char *)key;
+ value = (GValue *)hvalue;
+ node = (PurpleXmlNode *)user_data;
+
+ g_return_if_fail(value != NULL);
+
+ child = purple_xmlnode_new_child(node, "setting");
+ purple_xmlnode_set_attrib(child, "name", name);
+
+ if (G_VALUE_HOLDS_INT(value)) {
+ purple_xmlnode_set_attrib(child, "type", "int");
+ g_snprintf(buf, sizeof(buf), "%d", g_value_get_int(value));
+ purple_xmlnode_insert_data(child, buf, -1);
+ }
+ else if (G_VALUE_HOLDS_STRING(value)) {
+ purple_xmlnode_set_attrib(child, "type", "string");
+ purple_xmlnode_insert_data(child, g_value_get_string(value), -1);
+ }
+ else if (G_VALUE_HOLDS_BOOLEAN(value)) {
+ purple_xmlnode_set_attrib(child, "type", "bool");
+ g_snprintf(buf, sizeof(buf), "%d", g_value_get_boolean(value));
+ purple_xmlnode_insert_data(child, buf, -1);
+ }
+}
+
+static void
+chat_component_to_xmlnode(gpointer key, gpointer value, gpointer user_data)
+{
+ const char *name;
+ const char *data;
+ PurpleXmlNode *node, *child;
+
+ name = (const char *)key;
+ data = (const char *)value;
+ node = (PurpleXmlNode *)user_data;
+
+ g_return_if_fail(data != NULL);
+
+ child = purple_xmlnode_new_child(node, "component");
+ purple_xmlnode_set_attrib(child, "name", name);
+ purple_xmlnode_insert_data(child, data, -1);
+}
+
+static PurpleXmlNode *
+buddy_to_xmlnode(PurpleBuddy *buddy)
+{
+ PurpleXmlNode *node, *child;
+ PurpleAccount *account = purple_buddy_get_account(buddy);
+ const char *alias = purple_buddy_get_local_alias(buddy);
+
+ node = purple_xmlnode_new("buddy");
+ purple_xmlnode_set_attrib(node, "account", purple_account_get_username(account));
+ purple_xmlnode_set_attrib(node, "proto", purple_account_get_protocol_id(account));
+
+ child = purple_xmlnode_new_child(node, "name");
+ purple_xmlnode_insert_data(child, purple_buddy_get_name(buddy), -1);
+
+ if (alias != NULL)
+ {
+ child = purple_xmlnode_new_child(node, "alias");
+ purple_xmlnode_insert_data(child, alias, -1);
+ }
+
+ /* Write buddy settings */
+ g_hash_table_foreach(purple_blist_node_get_settings(PURPLE_BLIST_NODE(buddy)),
+ value_to_xmlnode, node);
+
+ return node;
+}
+
+static PurpleXmlNode *
+contact_to_xmlnode(PurpleContact *contact)
+{
+ PurpleXmlNode *node, *child;
+ PurpleBlistNode *bnode;
+ gchar *alias;
+
+ node = purple_xmlnode_new("contact");
+ g_object_get(contact, "alias", &alias, NULL);
+
+ if (alias != NULL)
+ {
+ purple_xmlnode_set_attrib(node, "alias", alias);
+ }
+
+ /* Write buddies */
+ for (bnode = PURPLE_BLIST_NODE(contact)->child; bnode != NULL; bnode = bnode->next)
+ {
+ if (purple_blist_node_is_transient(bnode))
+ continue;
+ if (PURPLE_IS_BUDDY(bnode))
+ {
+ child = buddy_to_xmlnode(PURPLE_BUDDY(bnode));
+ purple_xmlnode_insert_child(node, child);
+ }
+ }
+
+ /* Write contact settings */
+ g_hash_table_foreach(purple_blist_node_get_settings(PURPLE_BLIST_NODE(contact)),
+ value_to_xmlnode, node);
+
+ g_free(alias);
+ return node;
+}
+
+static PurpleXmlNode *
+chat_to_xmlnode(PurpleChat *chat)
+{
+ PurpleXmlNode *node, *child;
+ PurpleAccount *account = purple_chat_get_account(chat);
+ gchar *alias;
+
+ g_object_get(chat, "alias", &alias, NULL);
+
+ node = purple_xmlnode_new("chat");
+ purple_xmlnode_set_attrib(node, "proto", purple_account_get_protocol_id(account));
+ purple_xmlnode_set_attrib(node, "account", purple_account_get_username(account));
+
+ if (alias != NULL)
+ {
+ child = purple_xmlnode_new_child(node, "alias");
+ purple_xmlnode_insert_data(child, alias, -1);
+ }
+
+ /* Write chat components */
+ g_hash_table_foreach(purple_chat_get_components(chat),
+ chat_component_to_xmlnode, node);
+
+ /* Write chat settings */
+ g_hash_table_foreach(purple_blist_node_get_settings(PURPLE_BLIST_NODE(chat)),
+ value_to_xmlnode, node);
+
+ g_free(alias);
+ return node;
+}
+
+static PurpleXmlNode *
+group_to_xmlnode(PurpleGroup *group)
+{
+ PurpleXmlNode *node, *child;
+ PurpleBlistNode *cnode;
+
+ node = purple_xmlnode_new("group");
+ if (group != purple_blist_get_default_group())
+ purple_xmlnode_set_attrib(node, "name", purple_group_get_name(group));
+
+ /* Write settings */
+ g_hash_table_foreach(purple_blist_node_get_settings(PURPLE_BLIST_NODE(group)),
+ value_to_xmlnode, node);
+
+ /* Write contacts and chats */
+ for (cnode = PURPLE_BLIST_NODE(group)->child; cnode != NULL; cnode = cnode->next)
+ {
+ if (purple_blist_node_is_transient(cnode))
+ continue;
+ if (PURPLE_IS_CONTACT(cnode))
+ {
+ child = contact_to_xmlnode(PURPLE_CONTACT(cnode));
+ purple_xmlnode_insert_child(node, child);
+ }
+ else if (PURPLE_IS_CHAT(cnode))
+ {
+ child = chat_to_xmlnode(PURPLE_CHAT(cnode));
+ purple_xmlnode_insert_child(node, child);
+ }
+ }
+
+ return node;
+}
+
+static PurpleXmlNode *
+accountprivacy_to_xmlnode(PurpleAccount *account)
+{
+ PurpleXmlNode *node, *child;
+ GSList *cur;
+ char buf[10];
+
+ node = purple_xmlnode_new("account");
+ purple_xmlnode_set_attrib(node, "proto", purple_account_get_protocol_id(account));
+ purple_xmlnode_set_attrib(node, "name", purple_account_get_username(account));
+ g_snprintf(buf, sizeof(buf), "%d", purple_account_get_privacy_type(account));
+ purple_xmlnode_set_attrib(node, "mode", buf);
+
+ for (cur = purple_account_privacy_get_permitted(account); cur; cur = cur->next)
+ {
+ child = purple_xmlnode_new_child(node, "permit");
+ purple_xmlnode_insert_data(child, cur->data, -1);
+ }
+
+ for (cur = purple_account_privacy_get_denied(account); cur; cur = cur->next)
+ {
+ child = purple_xmlnode_new_child(node, "block");
+ purple_xmlnode_insert_data(child, cur->data, -1);
+ }
+
+ return node;
+}
+
+static PurpleXmlNode *
+blist_to_xmlnode(void)
+{
+ PurpleXmlNode *node, *child, *grandchild;
+ PurpleBlistNode *gnode;
+ GList *cur;
+ const gchar *localized_default;
+
+ node = purple_xmlnode_new("purple");
+ purple_xmlnode_set_attrib(node, "version", "1.0");
+
+ /* Write groups */
+ child = purple_xmlnode_new_child(node, "blist");
+
+ localized_default = localized_default_group_name;
+ if (g_strcmp0(_("Buddies"), "Buddies") != 0)
+ localized_default = _("Buddies");
+ if (localized_default != NULL) {
+ purple_xmlnode_set_attrib(child,
+ "localized-default-group", localized_default);
+ }
+
+ for (gnode = purplebuddylist->root; gnode != NULL; gnode = gnode->next)
+ {
+ if (purple_blist_node_is_transient(gnode))
+ continue;
+ if (PURPLE_IS_GROUP(gnode))
+ {
+ grandchild = group_to_xmlnode(PURPLE_GROUP(gnode));
+ purple_xmlnode_insert_child(child, grandchild);
+ }
+ }
+
+ /* Write privacy settings */
+ child = purple_xmlnode_new_child(node, "privacy");
+ for (cur = purple_accounts_get_all(); cur != NULL; cur = cur->next)
+ {
+ grandchild = accountprivacy_to_xmlnode(cur->data);
+ purple_xmlnode_insert_child(child, grandchild);
+ }
+
+ return node;
+}
+
+static void
+purple_blist_sync(void)
+{
+ PurpleXmlNode *node;
+ char *data;
+
+ if (!blist_loaded)
+ {
+ purple_debug_error("buddylist", "Attempted to save buddy list before it "
+ "was read!\n");
+ return;
+ }
+
+ node = blist_to_xmlnode();
+ data = purple_xmlnode_to_formatted_str(node, NULL);
+ purple_util_write_data_to_file("blist.xml", data, -1);
+ g_free(data);
+ purple_xmlnode_free(node);
+}
+
+static gboolean
+save_cb(gpointer data)
+{
+ purple_blist_sync();
+ save_timer = 0;
+ return FALSE;
+}
+
+static void
+_purple_blist_schedule_save()
+{
+ if (save_timer == 0)
+ save_timer = purple_timeout_add_seconds(5, save_cb, NULL);
+}
+
+static void
+purple_blist_save_account(PurpleAccount *account)
+{
+#if 1
+ _purple_blist_schedule_save();
+#else
+ if (account != NULL) {
+ /* Save the buddies and privacy data for this account */
+ } else {
+ /* Save all buddies and privacy data */
+ }
+#endif
+}
+
+static void
+purple_blist_save_node(PurpleBlistNode *node)
+{
+ _purple_blist_schedule_save();
+}
+
+void purple_blist_schedule_save()
+{
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+
+ /* Save everything */
+ if (ops && ops->save_account)
+ ops->save_account(NULL);
+}
+
+/*********************************************************************
+ * Reading from disk *
+ *********************************************************************/
+
+static void
+parse_setting(PurpleBlistNode *node, PurpleXmlNode *setting)
+{
+ const char *name = purple_xmlnode_get_attrib(setting, "name");
+ const char *type = purple_xmlnode_get_attrib(setting, "type");
+ char *value = purple_xmlnode_get_data(setting);
+
+ if (!value)
+ return;
+
+ if (!type || purple_strequal(type, "string"))
+ purple_blist_node_set_string(node, name, value);
+ else if (purple_strequal(type, "bool"))
+ purple_blist_node_set_bool(node, name, atoi(value));
+ else if (purple_strequal(type, "int"))
+ purple_blist_node_set_int(node, name, atoi(value));
+
+ g_free(value);
+}
+
+static void
+parse_buddy(PurpleGroup *group, PurpleContact *contact, PurpleXmlNode *bnode)
+{
+ PurpleAccount *account;
+ PurpleBuddy *buddy;
+ char *name = NULL, *alias = NULL;
+ const char *acct_name, *proto;
+ PurpleXmlNode *x;
+
+ acct_name = purple_xmlnode_get_attrib(bnode, "account");
+ proto = purple_xmlnode_get_attrib(bnode, "proto");
+
+ if (!acct_name || !proto)
+ return;
+
+ account = purple_accounts_find(acct_name, proto);
+
+ if (!account)
+ return;
+
+ if ((x = purple_xmlnode_get_child(bnode, "name")))
+ name = purple_xmlnode_get_data(x);
+
+ if (!name)
+ return;
+
+ if ((x = purple_xmlnode_get_child(bnode, "alias")))
+ alias = purple_xmlnode_get_data(x);
+
+ buddy = purple_buddy_new(account, name, alias);
+ purple_blist_add_buddy(buddy, contact, group,
+ _purple_blist_get_last_child((PurpleBlistNode*)contact));
+
+ for (x = purple_xmlnode_get_child(bnode, "setting"); x; x = purple_xmlnode_get_next_twin(x)) {
+ parse_setting((PurpleBlistNode*)buddy, x);
+ }
+
+ g_free(name);
+ g_free(alias);
+}
+
+static void
+parse_contact(PurpleGroup *group, PurpleXmlNode *cnode)
+{
+ PurpleContact *contact = purple_contact_new();
+ PurpleXmlNode *x;
+ const char *alias;
+
+ purple_blist_add_contact(contact, group,
+ _purple_blist_get_last_child((PurpleBlistNode*)group));
+
+ if ((alias = purple_xmlnode_get_attrib(cnode, "alias"))) {
+ purple_contact_set_alias(contact, alias);
+ }
+
+ for (x = cnode->child; x; x = x->next) {
+ if (x->type != PURPLE_XMLNODE_TYPE_TAG)
+ continue;
+ if (purple_strequal(x->name, "buddy"))
+ parse_buddy(group, contact, x);
+ else if (purple_strequal(x->name, "setting"))
+ parse_setting(PURPLE_BLIST_NODE(contact), x);
+ }
+
+ /* if the contact is empty, don't keep it around. it causes problems */
+ if (!PURPLE_BLIST_NODE(contact)->child)
+ purple_blist_remove_contact(contact);
+}
+
+static void
+parse_chat(PurpleGroup *group, PurpleXmlNode *cnode)
+{
+ PurpleChat *chat;
+ PurpleAccount *account;
+ const char *acct_name, *proto;
+ PurpleXmlNode *x;
+ char *alias = NULL;
+ GHashTable *components;
+
+ acct_name = purple_xmlnode_get_attrib(cnode, "account");
+ proto = purple_xmlnode_get_attrib(cnode, "proto");
+
+ if (!acct_name || !proto)
+ return;
+
+ account = purple_accounts_find(acct_name, proto);
+
+ if (!account)
+ return;
+
+ if ((x = purple_xmlnode_get_child(cnode, "alias")))
+ alias = purple_xmlnode_get_data(x);
+
+ components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+ for (x = purple_xmlnode_get_child(cnode, "component"); x; x = purple_xmlnode_get_next_twin(x)) {
+ const char *name;
+ char *value;
+
+ name = purple_xmlnode_get_attrib(x, "name");
+ value = purple_xmlnode_get_data(x);
+ g_hash_table_replace(components, g_strdup(name), value);
+ }
+
+ chat = purple_chat_new(account, alias, components);
+ purple_blist_add_chat(chat, group,
+ _purple_blist_get_last_child((PurpleBlistNode*)group));
+
+ for (x = purple_xmlnode_get_child(cnode, "setting"); x; x = purple_xmlnode_get_next_twin(x)) {
+ parse_setting((PurpleBlistNode*)chat, x);
+ }
+
+ g_free(alias);
+}
+
+static void
+parse_group(PurpleXmlNode *groupnode)
+{
+ const char *name = purple_xmlnode_get_attrib(groupnode, "name");
+ PurpleGroup *group;
+ PurpleXmlNode *cnode;
+
+ group = purple_group_new(name);
+ purple_blist_add_group(group,
+ purple_blist_get_last_sibling(purplebuddylist->root));
+
+ for (cnode = groupnode->child; cnode; cnode = cnode->next) {
+ if (cnode->type != PURPLE_XMLNODE_TYPE_TAG)
+ continue;
+ if (purple_strequal(cnode->name, "setting"))
+ parse_setting((PurpleBlistNode*)group, cnode);
+ else if (purple_strequal(cnode->name, "contact") ||
+ purple_strequal(cnode->name, "person"))
+ parse_contact(group, cnode);
+ else if (purple_strequal(cnode->name, "chat"))
+ parse_chat(group, cnode);
+ }
+}
+
+static void
+load_blist(void)
+{
+ PurpleXmlNode *purple, *blist, *privacy;
+
+ blist_loaded = TRUE;
+
+ purple = purple_util_read_xml_from_file("blist.xml", _("buddy list"));
+
+ if (purple == NULL)
+ return;
+
+ blist = purple_xmlnode_get_child(purple, "blist");
+ if (blist) {
+ PurpleXmlNode *groupnode;
+
+ localized_default_group_name = g_strdup(
+ purple_xmlnode_get_attrib(blist,
+ "localized-default-group"));
+
+ for (groupnode = purple_xmlnode_get_child(blist, "group"); groupnode != NULL;
+ groupnode = purple_xmlnode_get_next_twin(groupnode)) {
+ parse_group(groupnode);
+ }
+ } else {
+ g_free(localized_default_group_name);
+ localized_default_group_name = NULL;
+ }
+
+ privacy = purple_xmlnode_get_child(purple, "privacy");
+ if (privacy) {
+ PurpleXmlNode *anode;
+ for (anode = privacy->child; anode; anode = anode->next) {
+ PurpleXmlNode *x;
+ PurpleAccount *account;
+ int imode;
+ const char *acct_name, *proto, *mode;
+
+ acct_name = purple_xmlnode_get_attrib(anode, "name");
+ proto = purple_xmlnode_get_attrib(anode, "proto");
+ mode = purple_xmlnode_get_attrib(anode, "mode");
+
+ if (!acct_name || !proto || !mode)
+ continue;
+
+ account = purple_accounts_find(acct_name, proto);
+
+ if (!account)
+ continue;
+
+ imode = atoi(mode);
+ purple_account_set_privacy_type(account, (imode != 0 ? imode : PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL));
+
+ for (x = anode->child; x; x = x->next) {
+ char *name;
+ if (x->type != PURPLE_XMLNODE_TYPE_TAG)
+ continue;
+
+ if (purple_strequal(x->name, "permit")) {
+ name = purple_xmlnode_get_data(x);
+ purple_account_privacy_permit_add(account, name, TRUE);
+ g_free(name);
+ } else if (purple_strequal(x->name, "block")) {
+ name = purple_xmlnode_get_data(x);
+ purple_account_privacy_deny_add(account, name, TRUE);
+ g_free(name);
+ }
+ }
+ }
+ }
+
+ purple_xmlnode_free(purple);
+
+ /* This tells the buddy icon code to do its thing. */
+ _purple_buddy_icons_blist_loaded_cb();
+}
+
+/*****************************************************************************
+ * Public API functions *
+ *****************************************************************************/
+
+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();
+
+ buddies_cache = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify)g_hash_table_destroy);
+
+ groups_cache = g_hash_table_new_full((GHashFunc)g_str_hash,
+ (GEqualFunc)g_str_equal,
+ (GDestroyNotify)g_free, NULL);
+
+ for (account = purple_accounts_get_all(); account != NULL; account = account->next)
+ {
+ 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()
+{
+ return purplebuddylist;
+}
+
+PurpleBlistNode *
+purple_blist_get_root()
+{
+ return purplebuddylist ? purplebuddylist->root : NULL;
+}
+
+static void
+append_buddy(gpointer key, gpointer value, gpointer user_data)
+{
+ GSList **list = user_data;
+ *list = g_slist_prepend(*list, value);
+}
+
+GSList *
+purple_blist_get_buddies()
+{
+ GSList *buddies = NULL;
+
+ if (!purplebuddylist)
+ return NULL;
+
+ g_hash_table_foreach(PURPLE_BUDDY_LIST_GET_PRIVATE(purplebuddylist)->buddies,
+ append_buddy, &buddies);
+ 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();
+
+ if (ops && ops->show)
+ ops->show(purplebuddylist);
+}
+
+void purple_blist_set_visible(gboolean show)
+{
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+
+ if (ops && ops->set_visible)
+ ops->set_visible(purplebuddylist, show);
+}
+
+void purple_blist_update_buddies_cache(PurpleBuddy *buddy, const char *new_name)
+{
+ struct _purple_hbuddy *hb, *hb2;
+ GHashTable *account_buddies;
+ PurpleAccount *account;
+ gchar *name;
+ PurpleBuddyListPrivate *priv = PURPLE_BUDDY_LIST_GET_PRIVATE(purplebuddylist);
+
+ g_return_if_fail(PURPLE_IS_BUDDY(buddy));
+
+ account = purple_buddy_get_account(buddy);
+ name = (gchar *)purple_buddy_get_name(buddy);
+
+ hb = g_new(struct _purple_hbuddy, 1);
+ hb->name = (gchar *)purple_normalize(account, name);
+ hb->account = account;
+ hb->group = PURPLE_BLIST_NODE(buddy)->parent->parent;
+ g_hash_table_remove(priv->buddies, hb);
+
+ account_buddies = g_hash_table_lookup(buddies_cache, account);
+ g_hash_table_remove(account_buddies, hb);
+
+ hb->name = g_strdup(purple_normalize(account, new_name));
+ g_hash_table_replace(priv->buddies, hb, buddy);
+
+ hb2 = g_new(struct _purple_hbuddy, 1);
+ hb2->name = g_strdup(hb->name);
+ hb2->account = account;
+ hb2->group = PURPLE_BLIST_NODE(buddy)->parent->parent;
+
+ g_hash_table_replace(account_buddies, hb2, buddy);
+}
+
+void purple_blist_update_groups_cache(PurpleGroup *group, const char *new_name)
+{
+ gchar* key;
+
+ key = purple_blist_fold_name(purple_group_get_name(group));
+ g_hash_table_remove(groups_cache, key);
+ g_free(key);
+
+ g_hash_table_insert(groups_cache,
+ purple_blist_fold_name(new_name), group);
+}
+
+void purple_blist_add_chat(PurpleChat *chat, PurpleGroup *group, PurpleBlistNode *node)
+{
+ PurpleBlistNode *cnode = PURPLE_BLIST_NODE(chat);
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+ PurpleCountingNode *group_counter;
+
+ g_return_if_fail(PURPLE_IS_CHAT(chat));
+
+ if (node == NULL) {
+ if (group == NULL)
+ group = purple_group_new(_("Chats"));
+
+ /* 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));
+ }
+ } else {
+ group = PURPLE_GROUP(node->parent);
+ }
+
+ /* if we're moving to overtop of ourselves, do nothing */
+ if (cnode == node)
+ return;
+
+ if (cnode->parent) {
+ /* This chat was already in the list and is
+ * being moved.
+ */
+ group_counter = PURPLE_COUNTING_NODE(cnode->parent);
+ purple_counting_node_change_total_size(group_counter, -1);
+ if (purple_account_is_connected(purple_chat_get_account(chat))) {
+ purple_counting_node_change_online_count(group_counter, -1);
+ purple_counting_node_change_current_size(group_counter, -1);
+ }
+ if (cnode->next)
+ cnode->next->prev = cnode->prev;
+ if (cnode->prev)
+ cnode->prev->next = cnode->next;
+ if (cnode->parent->child == cnode)
+ cnode->parent->child = cnode->next;
+
+ if (ops && ops->remove)
+ ops->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 (node != NULL) {
+ if (node->next)
+ node->next->prev = cnode;
+ cnode->next = node->next;
+ cnode->prev = node;
+ cnode->parent = node->parent;
+ node->next = cnode;
+ group_counter = PURPLE_COUNTING_NODE(node->parent);
+ purple_counting_node_change_total_size(group_counter, +1);
+ if (purple_account_is_connected(purple_chat_get_account(chat))) {
+ purple_counting_node_change_online_count(group_counter, +1);
+ purple_counting_node_change_current_size(group_counter, +1);
+ }
+ } else {
+ if (((PurpleBlistNode *)group)->child)
+ ((PurpleBlistNode *)group)->child->prev = cnode;
+ cnode->next = ((PurpleBlistNode *)group)->child;
+ cnode->prev = NULL;
+ ((PurpleBlistNode *)group)->child = cnode;
+ cnode->parent = PURPLE_BLIST_NODE(group);
+ group_counter = PURPLE_COUNTING_NODE(group);
+ purple_counting_node_change_total_size(group_counter, +1);
+ if (purple_account_is_connected(purple_chat_get_account(chat))) {
+ purple_counting_node_change_online_count(group_counter, +1);
+ purple_counting_node_change_current_size(group_counter, +1);
+ }
+ }
+
+ if (ops) {
+ if (ops->save_node)
+ ops->save_node(cnode);
+ if (ops->update)
+ ops->update(purplebuddylist, PURPLE_BLIST_NODE(cnode));
+ }
+
+ purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
+ cnode);
+}
+
+void purple_blist_add_buddy(PurpleBuddy *buddy, PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node)
+{
+ 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_PRIVATE(purplebuddylist);
+
+ g_return_if_fail(PURPLE_IS_BUDDY(buddy));
+
+ bnode = PURPLE_BLIST_NODE(buddy);
+ account = purple_buddy_get_account(buddy);
+
+ /* if we're moving to overtop of ourselves, do nothing */
+ if (bnode == node || (!node && bnode->parent &&
+ contact && bnode->parent == (PurpleBlistNode*)contact
+ && bnode == bnode->parent->child))
+ return;
+
+ if (node && PURPLE_IS_BUDDY(node)) {
+ c = (PurpleContact*)node->parent;
+ g = (PurpleGroup*)node->parent->parent;
+ } else if (contact) {
+ c = contact;
+ g = PURPLE_GROUP(PURPLE_BLIST_NODE(c)->parent);
+ } else {
+ g = group;
+ if (g == NULL)
+ 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));
+ }
+ c = purple_contact_new();
+ purple_blist_add_contact(c, g,
+ _purple_blist_get_last_child((PurpleBlistNode*)g));
+ }
+
+ cnode = PURPLE_BLIST_NODE(c);
+
+ if (bnode->parent) {
+ contact_counter = PURPLE_COUNTING_NODE(bnode->parent);
+ group_counter = PURPLE_COUNTING_NODE(bnode->parent->parent);
+
+ if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
+ purple_counting_node_change_online_count(contact_counter, -1);
+ if (purple_counting_node_get_online_count(contact_counter) == 0)
+ purple_counting_node_change_online_count(group_counter, -1);
+ }
+ if (purple_account_is_connected(account)) {
+ purple_counting_node_change_current_size(contact_counter, -1);
+ if (purple_counting_node_get_current_size(contact_counter) == 0)
+ purple_counting_node_change_current_size(group_counter, -1);
+ }
+ purple_counting_node_change_total_size(contact_counter, -1);
+ /* the group totalsize will be taken care of by remove_contact below */
+
+ if (bnode->parent->parent != (PurpleBlistNode*)g)
+ purple_serv_move_buddy(buddy, (PurpleGroup *)bnode->parent->parent, g);
+
+ if (bnode->next)
+ bnode->next->prev = bnode->prev;
+ if (bnode->prev)
+ bnode->prev->next = bnode->next;
+ if (bnode->parent->child == bnode)
+ bnode->parent->child = bnode->next;
+
+ if (ops && ops->remove)
+ ops->remove(purplebuddylist, bnode);
+
+ if (bnode->parent->parent != (PurpleBlistNode*)g) {
+ struct _purple_hbuddy hb;
+ hb.name = (gchar *)purple_normalize(account,
+ purple_buddy_get_name(buddy));
+ hb.account = account;
+ hb.group = bnode->parent->parent;
+ g_hash_table_remove(priv->buddies, &hb);
+
+ account_buddies = g_hash_table_lookup(buddies_cache, account);
+ g_hash_table_remove(account_buddies, &hb);
+ }
+
+ if (!bnode->parent->child) {
+ purple_blist_remove_contact((PurpleContact*)bnode->parent);
+ } else {
+ purple_contact_invalidate_priority_buddy((PurpleContact*)bnode->parent);
+
+ if (ops && ops->update)
+ ops->update(purplebuddylist, bnode->parent);
+ }
+ }
+
+ if (node && PURPLE_IS_BUDDY(node)) {
+ if (node->next)
+ node->next->prev = bnode;
+ bnode->next = node->next;
+ bnode->prev = node;
+ bnode->parent = node->parent;
+ node->next = bnode;
+ } else {
+ if (cnode->child)
+ cnode->child->prev = bnode;
+ bnode->prev = NULL;
+ bnode->next = cnode->child;
+ cnode->child = bnode;
+ bnode->parent = cnode;
+ }
+
+ contact_counter = PURPLE_COUNTING_NODE(bnode->parent);
+ group_counter = PURPLE_COUNTING_NODE(bnode->parent->parent);
+
+ if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
+ purple_counting_node_change_online_count(contact_counter, +1);
+ if (purple_counting_node_get_online_count(contact_counter) == 1)
+ purple_counting_node_change_online_count(group_counter, +1);
+ }
+ if (purple_account_is_connected(account)) {
+ 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);
+ }
+ purple_counting_node_change_total_size(contact_counter, +1);
+
+ hb = g_new(struct _purple_hbuddy, 1);
+ hb->name = g_strdup(purple_normalize(account, purple_buddy_get_name(buddy)));
+ hb->account = account;
+ hb->group = PURPLE_BLIST_NODE(buddy)->parent->parent;
+
+ g_hash_table_replace(priv->buddies, hb, buddy);
+
+ account_buddies = g_hash_table_lookup(buddies_cache, account);
+
+ hb2 = g_new(struct _purple_hbuddy, 1);
+ hb2->name = g_strdup(hb->name);
+ hb2->account = account;
+ hb2->group = ((PurpleBlistNode*)buddy)->parent->parent;
+
+ g_hash_table_replace(account_buddies, hb2, buddy);
+
+ 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));
+ }
+
+ /* Signal that the buddy has been added */
+ purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
+ PURPLE_BLIST_NODE(buddy));
+}
+
+void purple_blist_add_contact(PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node)
+{
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+ PurpleGroup *g;
+ PurpleBlistNode *gnode, *cnode, *bnode;
+ PurpleCountingNode *contact_counter, *group_counter;
+ PurpleBuddyListPrivate *priv = PURPLE_BUDDY_LIST_GET_PRIVATE(purplebuddylist);
+
+ g_return_if_fail(PURPLE_IS_CONTACT(contact));
+
+ if (PURPLE_BLIST_NODE(contact) == node)
+ return;
+
+ if (node && (PURPLE_IS_CONTACT(node) ||
+ PURPLE_IS_CHAT(node)))
+ g = PURPLE_GROUP(node->parent);
+ else if (group)
+ g = group;
+ else
+ g = purple_blist_get_default_group();
+
+ gnode = (PurpleBlistNode*)g;
+ cnode = (PurpleBlistNode*)contact;
+
+ if (cnode->parent) {
+ if (cnode->parent->child == cnode)
+ cnode->parent->child = cnode->next;
+ if (cnode->prev)
+ cnode->prev->next = cnode->next;
+ if (cnode->next)
+ cnode->next->prev = cnode->prev;
+
+ if (cnode->parent != gnode) {
+ bnode = cnode->child;
+ while (bnode) {
+ PurpleBlistNode *next_bnode = bnode->next;
+ PurpleBuddy *b = PURPLE_BUDDY(bnode);
+ PurpleAccount *account = purple_buddy_get_account(b);
+ GHashTable *account_buddies;
+
+ struct _purple_hbuddy *hb, *hb2;
+
+ hb = g_new(struct _purple_hbuddy, 1);
+ hb->name = g_strdup(purple_normalize(account, purple_buddy_get_name(b)));
+ hb->account = account;
+ hb->group = cnode->parent;
+
+ g_hash_table_remove(priv->buddies, hb);
+
+ account_buddies = g_hash_table_lookup(buddies_cache, account);
+ g_hash_table_remove(account_buddies, hb);
+
+ if (!purple_blist_find_buddy_in_group(account, purple_buddy_get_name(b), g)) {
+ hb->group = gnode;
+ g_hash_table_replace(priv->buddies, hb, b);
+
+ hb2 = g_new(struct _purple_hbuddy, 1);
+ hb2->name = g_strdup(hb->name);
+ hb2->account = account;
+ hb2->group = gnode;
+
+ g_hash_table_replace(account_buddies, hb2, b);
+
+ if (purple_account_get_connection(account))
+ purple_serv_move_buddy(b, (PurpleGroup *)cnode->parent, g);
+ } else {
+ gboolean empty_contact = FALSE;
+
+ /* this buddy already exists in the group, so we're
+ * gonna delete it instead */
+ g_free(hb->name);
+ g_free(hb);
+ if (purple_account_get_connection(account))
+ purple_account_remove_buddy(account, b, PURPLE_GROUP(cnode->parent));
+
+ if (!cnode->child->next)
+ empty_contact = TRUE;
+ purple_blist_remove_buddy(b);
+
+ /* in purple_blist_remove_buddy(), if the last buddy in a
+ * contact is removed, the contact is cleaned up and
+ * g_free'd, so we mustn't try to reference bnode->next */
+ if (empty_contact)
+ return;
+ }
+ bnode = next_bnode;
+ }
+ }
+
+ contact_counter = PURPLE_COUNTING_NODE(contact);
+ group_counter = PURPLE_COUNTING_NODE(cnode->parent);
+
+ if (purple_counting_node_get_online_count(contact_counter) > 0)
+ purple_counting_node_change_online_count(group_counter, -1);
+ if (purple_counting_node_get_current_size(contact_counter) > 0)
+ 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 (ops && ops->remove_node)
+ ops->remove_node(cnode);
+ }
+
+ if (node && (PURPLE_IS_CONTACT(node) ||
+ PURPLE_IS_CHAT(node))) {
+ if (node->next)
+ node->next->prev = cnode;
+ cnode->next = node->next;
+ cnode->prev = node;
+ cnode->parent = node->parent;
+ node->next = cnode;
+ } else {
+ if (gnode->child)
+ gnode->child->prev = cnode;
+ cnode->prev = NULL;
+ cnode->next = gnode->child;
+ gnode->child = cnode;
+ cnode->parent = gnode;
+ }
+
+ contact_counter = PURPLE_COUNTING_NODE(contact);
+ group_counter = PURPLE_COUNTING_NODE(g);
+
+ if (purple_counting_node_get_online_count(contact_counter) > 0)
+ purple_counting_node_change_online_count(group_counter, +1);
+ if (purple_counting_node_get_current_size(contact_counter) > 0)
+ 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 (ops && ops->update)
+ {
+ if (cnode->child)
+ ops->update(purplebuddylist, cnode);
+
+ for (bnode = cnode->child; bnode; bnode = bnode->next)
+ ops->update(purplebuddylist, bnode);
+ }
+}
+
+void purple_blist_add_group(PurpleGroup *group, PurpleBlistNode *node)
+{
+ PurpleBlistUiOps *ops;
+ PurpleBlistNode *gnode = (PurpleBlistNode*)group;
+ gchar* key;
+
+ g_return_if_fail(PURPLE_IS_GROUP(group));
+
+ ops = purple_blist_get_ui_ops();
+
+ /* if we're moving to overtop of ourselves, do nothing */
+ if (gnode == node) {
+ if (!purplebuddylist->root)
+ node = NULL;
+ 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 (gnode == purplebuddylist->root)
+ purplebuddylist->root = gnode->next;
+ if (gnode->prev)
+ gnode->prev->next = gnode->next;
+ if (gnode->next)
+ gnode->next->prev = gnode->prev;
+ } else {
+ key = purple_blist_fold_name(purple_group_get_name(group));
+ g_hash_table_insert(groups_cache, key, group);
+ }
+
+ if (node && PURPLE_IS_GROUP(node)) {
+ gnode->next = node->next;
+ gnode->prev = node;
+ if (node->next)
+ node->next->prev = gnode;
+ node->next = gnode;
+ } else {
+ if (purplebuddylist->root)
+ purplebuddylist->root->prev = gnode;
+ gnode->next = purplebuddylist->root;
+ gnode->prev = NULL;
+ purplebuddylist->root = gnode;
+ }
+
+ if (ops && ops->save_node) {
+ ops->save_node(gnode);
+ for (node = gnode->child; node; node = node->next)
+ ops->save_node(node);
+ }
+
+ if (ops && ops->update) {
+ ops->update(purplebuddylist, gnode);
+ for (node = gnode->child; node; node = node->next)
+ ops->update(purplebuddylist, node);
+ }
+
+ purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
+ gnode);
+}
+
+void purple_blist_remove_contact(PurpleContact *contact)
+{
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+ PurpleBlistNode *node, *gnode;
+ PurpleGroup *group;
+
+ g_return_if_fail(PURPLE_IS_CONTACT(contact));
+
+ node = (PurpleBlistNode *)contact;
+ gnode = node->parent;
+ group = PURPLE_GROUP(gnode);
+
+ if (node->child) {
+ /*
+ * If this contact has children then remove them. When the last
+ * buddy is removed from the contact, the contact is automatically
+ * deleted.
+ */
+ while (node->child->next) {
+ purple_blist_remove_buddy((PurpleBuddy*)node->child);
+ }
+ /*
+ * Remove the last buddy and trigger the deletion of the contact.
+ * It would probably be cleaner if contact-deletion was done after
+ * a timeout? Or if it had to be done manually, like below?
+ */
+ purple_blist_remove_buddy((PurpleBuddy*)node->child);
+ } else {
+ /* Remove the node from its parent */
+ if (gnode->child == node)
+ gnode->child = node->next;
+ if (node->prev)
+ node->prev->next = node->next;
+ if (node->next)
+ node->next->prev = node->prev;
+ purple_counting_node_change_total_size(PURPLE_COUNTING_NODE(group), -1);
+
+ /* Update the UI */
+ if (ops && ops->remove)
+ ops->remove(purplebuddylist, node);
+
+ if (ops && ops->remove_node)
+ ops->remove_node(node);
+
+ purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
+ PURPLE_BLIST_NODE(contact));
+
+ /* Delete the node */
+ g_object_unref(contact);
+ }
+}
+
+void purple_blist_remove_buddy(PurpleBuddy *buddy)
+{
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+ PurpleBlistNode *node, *cnode, *gnode;
+ PurpleCountingNode *contact_counter, *group_counter;
+ PurpleContact *contact;
+ PurpleGroup *group;
+ struct _purple_hbuddy hb;
+ GHashTable *account_buddies;
+ PurpleAccount *account;
+
+ g_return_if_fail(PURPLE_IS_BUDDY(buddy));
+
+ account = purple_buddy_get_account(buddy);
+ node = PURPLE_BLIST_NODE(buddy);
+ cnode = node->parent;
+ gnode = (cnode != NULL) ? cnode->parent : NULL;
+ contact = (PurpleContact *)cnode;
+ group = (PurpleGroup *)gnode;
+
+ /* Remove the node from its parent */
+ if (node->prev)
+ node->prev->next = node->next;
+ if (node->next)
+ node->next->prev = node->prev;
+ if ((cnode != NULL) && (cnode->child == node))
+ cnode->child = node->next;
+
+ /* Adjust size counts */
+ if (contact != NULL) {
+ contact_counter = PURPLE_COUNTING_NODE(contact);
+ group_counter = PURPLE_COUNTING_NODE(group);
+
+ if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
+ purple_counting_node_change_online_count(contact_counter, -1);
+ if (purple_counting_node_get_online_count(contact_counter) == 0)
+ purple_counting_node_change_online_count(group_counter, -1);
+ }
+ if (purple_account_is_connected(account)) {
+ purple_counting_node_change_current_size(contact_counter, -1);
+ if (purple_counting_node_get_current_size(contact_counter) == 0)
+ purple_counting_node_change_current_size(group_counter, -1);
+ }
+ purple_counting_node_change_total_size(contact_counter, -1);
+
+ /* Re-sort the contact */
+ if (cnode->child && purple_contact_get_priority_buddy(contact) == buddy) {
+ purple_contact_invalidate_priority_buddy(contact);
+
+ if (ops && ops->update)
+ ops->update(purplebuddylist, cnode);
+ }
+ }
+
+ /* Remove this buddy from the buddies hash table */
+ hb.name = (gchar *)purple_normalize(account, purple_buddy_get_name(buddy));
+ hb.account = account;
+ hb.group = gnode;
+ g_hash_table_remove(PURPLE_BUDDY_LIST_GET_PRIVATE(purplebuddylist)->buddies, &hb);
+
+ account_buddies = g_hash_table_lookup(buddies_cache, account);
+ g_hash_table_remove(account_buddies, &hb);
+
+ /* Update the UI */
+ if (ops && ops->remove)
+ ops->remove(purplebuddylist, node);
+
+ if (ops && ops->remove_node)
+ ops->remove_node(node);
+
+ /* Remove this buddy's pounces */
+ purple_pounce_destroy_all_by_buddy(buddy);
+
+ /* Signal that the buddy has been removed before freeing the memory for it */
+ purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
+ PURPLE_BLIST_NODE(buddy));
+
+ g_object_unref(buddy);
+
+ /* If the contact is empty then remove it */
+ if ((contact != NULL) && !cnode->child)
+ purple_blist_remove_contact(contact);
+}
+
+void purple_blist_remove_chat(PurpleChat *chat)
+{
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+ PurpleBlistNode *node, *gnode;
+ PurpleGroup *group;
+ PurpleCountingNode *group_counter;
+
+ g_return_if_fail(PURPLE_IS_CHAT(chat));
+
+ node = (PurpleBlistNode *)chat;
+ gnode = node->parent;
+ group = (PurpleGroup *)gnode;
+
+ if (gnode != NULL)
+ {
+ /* Remove the node from its parent */
+ if (gnode->child == node)
+ gnode->child = node->next;
+ if (node->prev)
+ node->prev->next = node->next;
+ if (node->next)
+ node->next->prev = node->prev;
+
+ /* Adjust size counts */
+ group_counter = PURPLE_COUNTING_NODE(group);
+ if (purple_account_is_connected(purple_chat_get_account(chat))) {
+ purple_counting_node_change_online_count(group_counter, -1);
+ purple_counting_node_change_current_size(group_counter, -1);
+ }
+ purple_counting_node_change_total_size(group_counter, -1);
+ }
+
+ /* Update the UI */
+ if (ops && ops->remove)
+ ops->remove(purplebuddylist, node);
+
+ if (ops && ops->remove_node)
+ ops->remove_node(node);
+
+ purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
+ PURPLE_BLIST_NODE(chat));
+
+ /* Delete the node */
+ g_object_unref(chat);
+}
+
+void purple_blist_remove_group(PurpleGroup *group)
+{
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+ PurpleBlistNode *node;
+ GList *l;
+ gchar* key;
+
+ g_return_if_fail(PURPLE_IS_GROUP(group));
+
+ if (group == purple_blist_get_default_group())
+ purple_debug_warning("buddylist", "cannot remove default group");
+
+ node = (PurpleBlistNode *)group;
+
+ /* Make sure the group is empty */
+ if (node->child)
+ return;
+
+ /* Remove the node from its parent */
+ if (purplebuddylist->root == node)
+ purplebuddylist->root = node->next;
+ if (node->prev)
+ node->prev->next = node->next;
+ if (node->next)
+ node->next->prev = node->prev;
+
+ key = purple_blist_fold_name(purple_group_get_name(group));
+ g_hash_table_remove(groups_cache, key);
+ g_free(key);
+
+ /* Update the UI */
+ if (ops && ops->remove)
+ ops->remove(purplebuddylist, node);
+
+ if (ops && ops->remove_node)
+ ops->remove_node(node);
+
+ purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
+ PURPLE_BLIST_NODE(group));
+
+ /* Remove the group from all accounts that are online */
+ for (l = purple_connections_get_all(); l != NULL; l = l->next)
+ {
+ PurpleConnection *gc = (PurpleConnection *)l->data;
+
+ if (purple_connection_get_state(gc) == PURPLE_CONNECTION_CONNECTED)
+ purple_account_remove_group(purple_connection_get_account(gc), group);
+ }
+
+ /* Delete the node */
+ g_object_unref(group);
+}
+
+PurpleBuddy *purple_blist_find_buddy(PurpleAccount *account, const char *name)
+{
+ PurpleBuddy *buddy;
+ struct _purple_hbuddy hb;
+ PurpleBlistNode *group;
+
+ g_return_val_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist), NULL);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+ g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL);
+
+ hb.account = account;
+ hb.name = (gchar *)purple_normalize(account, name);
+
+ for (group = purplebuddylist->root; group; group = group->next) {
+ if (!group->child)
+ continue;
+
+ hb.group = group;
+ if ((buddy = g_hash_table_lookup(PURPLE_BUDDY_LIST_GET_PRIVATE(purplebuddylist)->buddies,
+ &hb))) {
+ return buddy;
+ }
+ }
+
+ return NULL;
+}
+
+PurpleBuddy *purple_blist_find_buddy_in_group(PurpleAccount *account, const char *name,
+ PurpleGroup *group)
+{
+ struct _purple_hbuddy hb;
+
+ g_return_val_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist), NULL);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+ g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL);
+
+ hb.name = (gchar *)purple_normalize(account, name);
+ hb.account = account;
+ hb.group = (PurpleBlistNode*)group;
+
+ return g_hash_table_lookup(PURPLE_BUDDY_LIST_GET_PRIVATE(purplebuddylist)->buddies,
+ &hb);
+}
+
+static void find_acct_buddies(gpointer key, gpointer value, gpointer data)
+{
+ PurpleBuddy *buddy = value;
+ GSList **list = data;
+
+ *list = g_slist_prepend(*list, buddy);
+}
+
+GSList *purple_blist_find_buddies(PurpleAccount *account, const char *name)
+{
+ PurpleBuddy *buddy;
+ PurpleBlistNode *node;
+ GSList *ret = NULL;
+
+ g_return_val_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist), NULL);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+
+ if ((name != NULL) && (*name != '\0')) {
+ struct _purple_hbuddy hb;
+
+ hb.name = (gchar *)purple_normalize(account, name);
+ hb.account = account;
+
+ for (node = purplebuddylist->root; node != NULL; node = node->next) {
+ if (!node->child)
+ continue;
+
+ hb.group = node;
+ if ((buddy = g_hash_table_lookup(PURPLE_BUDDY_LIST_GET_PRIVATE(purplebuddylist)->buddies,
+ &hb)) != NULL)
+ ret = g_slist_prepend(ret, buddy);
+ }
+ } else {
+ GSList *list = NULL;
+ GHashTable *buddies = g_hash_table_lookup(buddies_cache, account);
+ g_hash_table_foreach(buddies, find_acct_buddies, &list);
+ ret = list;
+ }
+
+ return ret;
+}
+
+PurpleGroup *purple_blist_find_group(const char *name)
+{
+ gchar* key;
+ PurpleGroup *group;
+
+ g_return_val_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist), NULL);
+
+ if (name == NULL || name[0] == '\0')
+ name = PURPLE_BLIST_DEFAULT_GROUP_NAME;
+ if (g_strcmp0(name, "Buddies") == 0)
+ name = PURPLE_BLIST_DEFAULT_GROUP_NAME;
+ if (g_strcmp0(name, localized_default_group_name) == 0)
+ name = PURPLE_BLIST_DEFAULT_GROUP_NAME;
+
+ key = purple_blist_fold_name(name);
+ group = g_hash_table_lookup(groups_cache, key);
+ g_free(key);
+
+ return group;
+}
+
+PurpleGroup *
+purple_blist_get_default_group(void)
+{
+ PurpleGroup *group;
+
+ group = purple_blist_find_group(PURPLE_BLIST_DEFAULT_GROUP_NAME);
+ if (!group) {
+ group = purple_group_new(PURPLE_BLIST_DEFAULT_GROUP_NAME);
+ purple_blist_add_group(group, NULL);
+ }
+
+ return group;
+}
+
+PurpleChat *
+purple_blist_find_chat(PurpleAccount *account, const char *name)
+{
+ char *chat_name;
+ PurpleChat *chat;
+ PurplePlugin *prpl;
+ PurplePluginProtocolInfo *prpl_info = NULL;
+ struct proto_chat_entry *pce;
+ PurpleBlistNode *node, *group;
+ GList *parts;
+ char *normname;
+
+ g_return_val_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist), NULL);
+ g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL);
+
+ if (!purple_account_is_connected(account))
+ return NULL;
+
+ prpl = purple_find_prpl(purple_account_get_protocol_id(account));
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+
+ if (prpl_info->find_blist_chat != NULL)
+ return prpl_info->find_blist_chat(account, name);
+
+ normname = g_strdup(purple_normalize(account, name));
+ for (group = purplebuddylist->root; group != NULL; group = group->next) {
+ for (node = group->child; node != NULL; node = node->next) {
+ if (PURPLE_IS_CHAT(node)) {
+
+ chat = (PurpleChat*)node;
+
+ if (account != purple_chat_get_account(chat))
+ continue;
+
+ parts = prpl_info->chat_info(
+ purple_account_get_connection(purple_chat_get_account(chat)));
+
+ 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);
+
+ if (purple_chat_get_account(chat) == account && chat_name != NULL &&
+ normname != NULL && !strcmp(purple_normalize(account, chat_name), normname)) {
+ g_free(normname);
+ return chat;
+ }
+ }
+ }
+ }
+
+ g_free(normname);
+ return NULL;
+}
+
+void purple_blist_add_account(PurpleAccount *account)
+{
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+ PurpleBlistNode *gnode, *cnode, *bnode;
+ PurpleCountingNode *contact_counter, *group_counter;
+
+ g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
+
+ if (!ops || !ops->update)
+ return;
+
+ for (gnode = purplebuddylist->root; gnode; gnode = gnode->next) {
+ if (!PURPLE_IS_GROUP(gnode))
+ continue;
+ for (cnode = gnode->child; cnode; cnode = cnode->next) {
+ if (PURPLE_IS_CONTACT(cnode)) {
+ gboolean recompute = FALSE;
+ for (bnode = cnode->child; bnode; bnode = bnode->next) {
+ if (PURPLE_IS_BUDDY(bnode) &&
+ purple_buddy_get_account(PURPLE_BUDDY(bnode)) == account) {
+ recompute = TRUE;
+ contact_counter = PURPLE_COUNTING_NODE(cnode);
+ group_counter = PURPLE_COUNTING_NODE(gnode);
+ 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);
+ }
+ } 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);
+ }
+ }
+ ops->update(purplebuddylist, gnode);
+ }
+}
+
+void purple_blist_remove_account(PurpleAccount *account)
+{
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+ PurpleBlistNode *gnode, *cnode, *bnode;
+ PurpleCountingNode *contact_counter, *group_counter;
+ PurpleBuddy *buddy;
+ PurpleChat *chat;
+ PurpleContact *contact;
+ PurpleGroup *group;
+ GList *list = NULL, *iter = NULL;
+
+ g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
+
+ for (gnode = purplebuddylist->root; gnode; gnode = gnode->next) {
+ if (!PURPLE_IS_GROUP(gnode))
+ continue;
+
+ group = (PurpleGroup *)gnode;
+
+ for (cnode = gnode->child; cnode; cnode = cnode->next) {
+ if (PURPLE_IS_CONTACT(cnode)) {
+ gboolean recompute = FALSE;
+ contact = (PurpleContact *)cnode;
+
+ for (bnode = cnode->child; bnode; bnode = bnode->next) {
+ if (!PURPLE_IS_BUDDY(bnode))
+ continue;
+
+ buddy = (PurpleBuddy *)bnode;
+ if (account == purple_buddy_get_account(buddy)) {
+ PurplePresence *presence;
+
+ presence = purple_buddy_get_presence(buddy);
+ contact_counter = PURPLE_COUNTING_NODE(contact);
+ group_counter = PURPLE_COUNTING_NODE(group);
+
+ if(purple_presence_is_online(presence)) {
+ purple_counting_node_change_online_count(contact_counter, -1);
+ if (purple_counting_node_get_online_count(contact_counter) == 0)
+ purple_counting_node_change_online_count(group_counter, -1);
+
+ purple_blist_node_set_int(PURPLE_BLIST_NODE(buddy),
+ "last_seen", time(NULL));
+ }
+
+ purple_counting_node_change_current_size(contact_counter, -1);
+ if (purple_counting_node_get_current_size(contact_counter) == 0)
+ purple_counting_node_change_current_size(group_counter, -1);
+
+ if (!g_list_find(list, presence))
+ list = g_list_prepend(list, presence);
+
+ if (purple_contact_get_priority_buddy(contact) == buddy)
+ purple_contact_invalidate_priority_buddy(contact);
+ else
+ recompute = TRUE;
+
+ if (ops && ops->remove) {
+ ops->remove(purplebuddylist, bnode);
+ }
+ }
+ }
+ if (recompute) {
+ purple_contact_invalidate_priority_buddy(contact);
+
+ if (ops && ops->update)
+ ops->update(purplebuddylist, cnode);
+ }
+ } else if (PURPLE_IS_CHAT(cnode)) {
+ chat = PURPLE_CHAT(cnode);
+
+ if(purple_chat_get_account(chat) == account) {
+ group_counter = PURPLE_COUNTING_NODE(group);
+ 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);
+ }
+ }
+ }
+ }
+
+ for (iter = list; iter; iter = iter->next)
+ {
+ purple_presence_set_status_active(iter->data, "offline", TRUE);
+ }
+ g_list_free(list);
+}
+
+void
+purple_blist_request_add_buddy(PurpleAccount *account, const char *username,
+ const char *group, const char *alias)
+{
+ PurpleBlistUiOps *ui_ops;
+
+ 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);
+}
+
+void
+purple_blist_request_add_chat(PurpleAccount *account, PurpleGroup *group,
+ const char *alias, const char *name)
+{
+ PurpleBlistUiOps *ui_ops;
+
+ 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);
+}
+
+void
+purple_blist_request_add_group(void)
+{
+ PurpleBlistUiOps *ui_ops;
+
+ ui_ops = purple_blist_get_ui_ops();
+
+ if (ui_ops != NULL && ui_ops->request_add_group != NULL)
+ ui_ops->request_add_group();
+}
+
+void
+purple_blist_set_ui_ops(PurpleBlistUiOps *ops)
+{
+ gboolean overrode = FALSE;
+ blist_ui_ops = ops;
+
+ if (!ops)
+ return;
+
+ 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;
+ }
+
+ 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");
+ }
+}
+
+PurpleBlistUiOps *
+purple_blist_get_ui_ops(void)
+{
+ return blist_ui_ops;
+}
+
+const gchar *
+_purple_blist_get_localized_default_group_name(void)
+{
+ return localized_default_group_name;
+}
+
+void *
+purple_blist_get_handle(void)
+{
+ static int handle;
+
+ return &handle;
+}
+
+void
+purple_blist_init(void)
+{
+ void *handle = purple_blist_get_handle();
+
+ purple_signal_register(handle, "buddy-status-changed",
+ purple_marshal_VOID__POINTER_POINTER_POINTER,
+ G_TYPE_NONE, 3, PURPLE_TYPE_BUDDY, PURPLE_TYPE_STATUS,
+ PURPLE_TYPE_STATUS);
+ purple_signal_register(handle, "buddy-privacy-changed",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE,
+ 1, PURPLE_TYPE_BUDDY);
+
+ purple_signal_register(handle, "buddy-idle-changed",
+ purple_marshal_VOID__POINTER_INT_INT, G_TYPE_NONE,
+ 3, PURPLE_TYPE_BUDDY, G_TYPE_INT, G_TYPE_INT);
+
+ purple_signal_register(handle, "buddy-signed-on",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_BUDDY);
+
+ purple_signal_register(handle, "buddy-signed-off",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_BUDDY);
+
+ purple_signal_register(handle, "buddy-got-login-time",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_BUDDY);
+
+ purple_signal_register(handle, "blist-node-added",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_BLIST_NODE);
+
+ purple_signal_register(handle, "blist-node-removed",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_BLIST_NODE);
+
+ purple_signal_register(handle, "buddy-icon-changed",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_BUDDY);
+
+ purple_signal_register(handle, "update-idle", purple_marshal_VOID,
+ G_TYPE_NONE, 0);
+
+ purple_signal_register(handle, "blist-node-extended-menu",
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ PURPLE_TYPE_BLIST_NODE,
+ G_TYPE_POINTER); /* (GList **) */
+
+ purple_signal_register(handle, "blist-node-aliased",
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ PURPLE_TYPE_BLIST_NODE, G_TYPE_STRING);
+
+ purple_signal_register(handle, "buddy-caps-changed",
+ purple_marshal_VOID__POINTER_INT_INT, G_TYPE_NONE,
+ 3, PURPLE_TYPE_BUDDY, G_TYPE_INT, G_TYPE_INT);
+
+ purple_signal_connect(purple_accounts_get_handle(), "account-created",
+ handle,
+ PURPLE_CALLBACK(purple_blist_buddies_cache_add_account),
+ NULL);
+
+ purple_signal_connect(purple_accounts_get_handle(), "account-destroying",
+ handle,
+ PURPLE_CALLBACK(purple_blist_buddies_cache_remove_account),
+ NULL);
+}
+
+static void
+blist_node_destroy(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);
+ child = next_child;
+ }
+
+ /* Allow the UI to free data */
+ node->parent = NULL;
+ node->child = NULL;
+ node->next = NULL;
+ node->prev = NULL;
+ if (ui_ops && ui_ops->remove)
+ ui_ops->remove(purplebuddylist, node);
+
+ g_object_unref(node);
+}
+
+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;
+
+ if (save_timer != 0) {
+ purple_timeout_remove(save_timer);
+ save_timer = 0;
+ purple_blist_sync();
+ }
+
+ 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_free(localized_default_group_name);
+ localized_default_group_name = NULL;
+
+ purple_signals_disconnect_by_handle(purple_blist_get_handle());
+ purple_signals_unregister_by_instance(purple_blist_get_handle());
+}
+
+/**************************************************************************
+ * 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
+ **************************************************************************/
+
+/* GObject initialization function */
+static void
+purple_buddy_list_init(GTypeInstance *instance, gpointer klass)
+{
+ PurpleBuddyList *blist = PURPLE_BUDDY_LIST(instance);
+
+ PURPLE_DBUS_REGISTER_POINTER(blist, PurpleBuddyList);
+
+ PURPLE_BUDDY_LIST_GET_PRIVATE(blist)->buddies = g_hash_table_new_full(
+ (GHashFunc)_purple_blist_hbuddy_hash,
+ (GEqualFunc)_purple_blist_hbuddy_equal,
+ (GDestroyNotify)_purple_blist_hbuddy_free_key, NULL);
+}
+
+/* GObject finalize function */
+static void
+purple_buddy_list_finalize(GObject *object)
+{
+ g_hash_table_destroy(PURPLE_BUDDY_LIST_GET_PRIVATE(object)->buddies);
+
+ PURPLE_DBUS_UNREGISTER_POINTER(object);
+
+ G_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+/* Class initializer function */
+static void purple_buddy_list_class_init(PurpleBuddyListClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ obj_class->finalize = purple_buddy_list_finalize;
+
+ g_type_class_add_private(klass, sizeof(PurpleBuddyListPrivate));
+}
+
+GType
+purple_buddy_list_get_type(void)
+{
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleBuddyListClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_buddy_list_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleBuddyList),
+ 0,
+ (GInstanceInitFunc)purple_buddy_list_init,
+ NULL,
+ };
+
+ type = g_type_register_static(G_TYPE_OBJECT,
+ "PurpleBuddyList", &info, 0);
+ }
+
+ return type;
+}
diff --git a/libpurple/buddylist.h b/libpurple/buddylist.h
new file mode 100644
index 0000000000..3922f8a349
--- /dev/null
+++ b/libpurple/buddylist.h
@@ -0,0 +1,556 @@
+/* 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 _PURPLE_BUDDY_LIST_H_
+#define _PURPLE_BUDDY_LIST_H_
+/**
+ * SECTION:buddylist
+ * @section_id: libpurple-buddylist
+ * @short_description: <filename>buddylist.h</filename>
+ * @title: Buddy List API
+ * @see_also: <link linkend="chapter-signals-blist">Buddy List signals</link>
+ */
+
+/* I can't believe I let ChipX86 inspire me to write good code. -Sean */
+
+#include "blistnodetypes.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_BLIST_DEFAULT_GROUP_NAME _("Buddies")
+
+/**************************************************************************/
+/* Data Structures */
+/**************************************************************************/
+/**
+ * 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;
+};
+
+/**
+ * PurpleBuddyListClass:
+ *
+ * The base class for all #PurpleBuddyList's.
+ */
+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.
+ * @new_node: Sets UI-specific data on a node.
+ * @show: The core will call this when it's finished doing its core
+ * 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
+ * @save_node: This is called when a node has been modified and should be
+ * saved.
+ * <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
+ * <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
+ * <filename>blist.xml</filename> like in previous libpurple
+ * versions.
+ * <sbr/>@node: The node which has been modified.
+ * @save_account: Called to save all the data for an account. If the UI sets
+ * 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
+ * <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/>@account: The account whose data to save. If %NULL,
+ * save all data for all accounts.
+ *
+ * Buddy list UI operations.
+ *
+ * Any UI representing a buddy list must assign a filled-out PurpleBlistUiOps
+ * structure to the buddy list core.
+ */
+struct _PurpleBlistUiOps
+{
+ void (*new_list)(PurpleBuddyList *list);
+ void (*new_node)(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_group)(void);
+
+ void (*save_node)(PurpleBlistNode *node);
+ void (*remove_node)(PurpleBlistNode *node);
+
+ void (*save_account)(PurpleAccount *account);
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+/**************************************************************************/
+/* Buddy List API */
+/**************************************************************************/
+
+/**
+ * purple_buddy_list_get_type:
+ *
+ * Returns: The #GType for the #PurpleBuddyList object.
+ */
+GType purple_buddy_list_get_type(void);
+
+/**
+ * purple_blist_get_buddy_list:
+ *
+ * Returns the main buddy list.
+ *
+ * Returns: The main buddy list.
+ */
+PurpleBuddyList *purple_blist_get_buddy_list(void);
+
+/**
+ * purple_blist_get_root:
+ *
+ * Returns the root node of the main buddy list.
+ *
+ * Returns: The root node.
+ */
+PurpleBlistNode *purple_blist_get_root(void);
+
+/**
+ * purple_blist_get_buddies:
+ *
+ * Returns a list of every buddy in the list. Use of this function is
+ * discouraged if you do not actually need every buddy in the list. Use
+ * purple_blist_find_buddies instead.
+ *
+ * See purple_blist_find_buddies().
+ *
+ * Returns: A list of every buddy in the list. Caller is responsible for
+ * freeing the list.
+ */
+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.
+ */
+void purple_blist_show(void);
+
+/**
+ * purple_blist_set_visible:
+ * @show: Whether or not to show the buddy list
+ *
+ * Hides or unhides the buddy list.
+ */
+void purple_blist_set_visible(gboolean show);
+
+/**
+ * purple_blist_update_buddies_cache:
+ * @buddy: The buddy whose name will be changed.
+ * @new_name: The new name of the buddy.
+ *
+ * Updates the buddies hash table when a buddy has been renamed. This only
+ * updates the cache, the caller is responsible for the actual renaming of
+ * the buddy after updating the cache.
+ */
+void purple_blist_update_buddies_cache(PurpleBuddy *buddy, const char *new_name);
+
+/**
+ * purple_blist_update_groups_cache:
+ * @group: The group whose name will be changed.
+ * @new_name: The new name of the group.
+ *
+ * Updates the groups hash table when a group has been renamed. This only
+ * updates the cache, the caller is responsible for the actual renaming of
+ * the group after updating the cache.
+ */
+void purple_blist_update_groups_cache(PurpleGroup *group, const char *new_name);
+
+/**
+ * purple_blist_add_chat:
+ * @chat: The new chat who gets added
+ * @group: The group to add the new chat to.
+ * @node: The insertion point
+ *
+ * Adds a new chat to the buddy list.
+ *
+ * The chat will be inserted right after node or appended to the end
+ * of group if node is NULL. If both are NULL, the buddy will be added to
+ * the "Chats" group.
+ */
+void purple_blist_add_chat(PurpleChat *chat, PurpleGroup *group, PurpleBlistNode *node);
+
+/**
+ * purple_blist_add_buddy:
+ * @buddy: The new buddy who gets added
+ * @contact: The optional contact to place the buddy in.
+ * @group: The group to add the new buddy to.
+ * @node: The insertion point. Pass in NULL to add the node as
+ * the first child in the given group.
+ *
+ * Adds a new buddy to the buddy list.
+ *
+ * The buddy will be inserted right after node or prepended to the
+ * group if node is NULL. If both are NULL, the buddy will be added to
+ * the default group.
+ */
+void purple_blist_add_buddy(PurpleBuddy *buddy, PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node);
+
+/**
+ * purple_blist_add_group:
+ * @group: The group
+ * @node: The insertion point
+ *
+ * Adds a new group to the buddy list.
+ *
+ * The new group will be inserted after insert or prepended to the list if
+ * node is NULL.
+ */
+void purple_blist_add_group(PurpleGroup *group, PurpleBlistNode *node);
+
+/**
+ * purple_blist_add_contact:
+ * @contact: The contact
+ * @group: The group to add the contact to
+ * @node: The insertion point
+ *
+ * Adds a new contact to the buddy list.
+ *
+ * The new contact will be inserted after insert or prepended to the list if
+ * node is NULL.
+ */
+void purple_blist_add_contact(PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node);
+
+/**
+ * purple_blist_remove_buddy:
+ * @buddy: The buddy to be removed
+ *
+ * Removes a buddy from the buddy list and frees the memory allocated to it.
+ * This doesn't actually try to remove the buddy from the server list.
+ *
+ * See purple_account_remove_buddy().
+ */
+void purple_blist_remove_buddy(PurpleBuddy *buddy);
+
+/**
+ * purple_blist_remove_contact:
+ * @contact: The contact to be removed
+ *
+ * Removes a contact, and any buddies it contains, and frees the memory
+ * allocated to it. This calls purple_blist_remove_buddy and therefore
+ * doesn't remove the buddies from the server list.
+ *
+ * See purple_blist_remove_buddy().
+ */
+void purple_blist_remove_contact(PurpleContact *contact);
+
+/**
+ * purple_blist_remove_chat:
+ * @chat: The chat to be removed
+ *
+ * Removes a chat from the buddy list and frees the memory allocated to it.
+ */
+void purple_blist_remove_chat(PurpleChat *chat);
+
+/**
+ * purple_blist_remove_group:
+ * @group: The group to be removed
+ *
+ * Removes a group from the buddy list and frees the memory allocated to it and to
+ * its children
+ */
+void purple_blist_remove_group(PurpleGroup *group);
+
+/**
+ * purple_blist_find_buddy:
+ * @account: The account this buddy belongs to
+ * @name: The buddy's name
+ *
+ * Finds the buddy struct given a name and an account
+ *
+ * Returns: The buddy or NULL if the buddy does not exist
+ */
+PurpleBuddy *purple_blist_find_buddy(PurpleAccount *account, const char *name);
+
+/**
+ * purple_blist_find_buddy_in_group:
+ * @account: The account this buddy belongs to
+ * @name: The buddy's name
+ * @group: The group to look in
+ *
+ * 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
+ */
+PurpleBuddy *purple_blist_find_buddy_in_group(PurpleAccount *account, const char *name,
+ PurpleGroup *group);
+
+/**
+ * purple_blist_find_buddies:
+ * @account: The account this buddy belongs to
+ * @name: The buddy's name (or NULL to return all buddies for the account)
+ *
+ * Finds all PurpleBuddy structs given a name and an account
+ *
+ * Returns: NULL if the buddy doesn't exist, or a GSList of
+ * PurpleBuddy structs. You must free the GSList using
+ * g_slist_free. Do not free the PurpleBuddy structs that
+ * the list points to.
+ */
+GSList *purple_blist_find_buddies(PurpleAccount *account, const char *name);
+
+/**
+ * purple_blist_find_group:
+ * @name: The group's name
+ *
+ * Finds a group by name
+ *
+ * Returns: The group or NULL if the group does not exist
+ */
+PurpleGroup *purple_blist_find_group(const char *name);
+
+/**
+ * purple_blist_get_default_group:
+ *
+ * Finds or creates default group.
+ *
+ * Returns: The default group.
+ */
+PurpleGroup *purple_blist_get_default_group(void);
+
+/**
+ * purple_blist_find_chat:
+ * @account: The chat's account.
+ * @name: The chat's name.
+ *
+ * Finds a chat by name.
+ *
+ * Returns: The chat, or %NULL if the chat does not exist.
+ */
+PurpleChat *purple_blist_find_chat(PurpleAccount *account, const char *name);
+
+/**
+ * purple_blist_add_account:
+ * @account: The account
+ *
+ * Called when an account connects. Tells the UI to update all the
+ * buddies.
+ */
+void purple_blist_add_account(PurpleAccount *account);
+
+/**
+ * purple_blist_remove_account:
+ * @account: The account
+ *
+ * Called when an account disconnects. Sets the presence of all the buddies to 0
+ * and tells the UI to update them.
+ */
+void purple_blist_remove_account(PurpleAccount *account);
+
+/****************************************************************************************/
+/* Buddy list file management API */
+/****************************************************************************************/
+
+/**
+ * purple_blist_schedule_save:
+ *
+ * Schedule a save of the <filename>blist.xml</filename> file. This is used by
+ * the account API whenever the privacy settings are changed. If you make a
+ * change to <filename>blist.xml</filename> using one of the functions in the
+ * buddy list API, then the buddy list is saved automatically, so you should not
+ * need to call this.
+ */
+void purple_blist_schedule_save(void);
+
+/**
+ * purple_blist_request_add_buddy:
+ * @account: The account the buddy is added to.
+ * @username: The username of the buddy.
+ * @group: The name of the group to place the buddy in.
+ * @alias: The optional alias for the buddy.
+ *
+ * Requests from the user information needed to add a buddy to the
+ * buddy list.
+ */
+void purple_blist_request_add_buddy(PurpleAccount *account, const char *username,
+ const char *group, const char *alias);
+
+/**
+ * purple_blist_request_add_chat:
+ * @account: The account the buddy is added to.
+ * @group: The optional group to add the chat to.
+ * @alias: The optional alias for the chat.
+ * @name: The required chat name.
+ *
+ * Requests from the user information needed to add a chat to the
+ * buddy list.
+ */
+void purple_blist_request_add_chat(PurpleAccount *account, PurpleGroup *group,
+ const char *alias, const char *name);
+
+/**
+ * purple_blist_request_add_group:
+ *
+ * Requests from the user information needed to add a group to the
+ * buddy list.
+ */
+void purple_blist_request_add_group(void);
+
+/**************************************************************************/
+/* UI Registration Functions */
+/**************************************************************************/
+
+/**
+ * purple_blist_ui_ops_get_type:
+ *
+ * Returns: The #GType for the #PurpleBlistUiOps boxed structure.
+ */
+GType purple_blist_ui_ops_get_type(void);
+
+/**
+ * purple_blist_set_ui_ops:
+ * @ops: The ops struct.
+ *
+ * Sets the UI operations structure to be used for the buddy list.
+ */
+void purple_blist_set_ui_ops(PurpleBlistUiOps *ops);
+
+/**
+ * purple_blist_get_ui_ops:
+ *
+ * Returns the UI operations structure to be used for the buddy list.
+ *
+ * Returns: The UI operations structure.
+ */
+PurpleBlistUiOps *purple_blist_get_ui_ops(void);
+
+/**************************************************************************/
+/* Buddy List Subsystem */
+/**************************************************************************/
+
+/**
+ * purple_blist_get_handle:
+ *
+ * Returns the handle for the buddy list subsystem.
+ *
+ * Returns: The buddy list subsystem handle.
+ */
+void *purple_blist_get_handle(void);
+
+/**
+ * purple_blist_init:
+ *
+ * Initializes the buddy list subsystem.
+ */
+void purple_blist_init(void);
+
+/**
+ * purple_blist_boot:
+ *
+ * Loads the buddy list.
+ *
+ * You shouldn't call this. purple_core_init() will do it for you.
+ */
+void purple_blist_boot(void);
+
+/**
+ * purple_blist_uninit:
+ *
+ * Uninitializes the buddy list subsystem.
+ */
+void purple_blist_uninit(void);
+
+G_END_DECLS
+
+#endif /* _PURPLE_BUDDY_LIST_H_ */
diff --git a/libpurple/certificate.c b/libpurple/certificate.c
index d05860e6e3..3f657fd67a 100644
--- a/libpurple/certificate.c
+++ b/libpurple/certificate.c
@@ -1,8 +1,3 @@
-/**
- * @file certificate.c Public-Key Certificate API
- * @ingroup core
- */
-
/*
*
* purple
@@ -34,60 +29,15 @@
#include "signals.h"
#include "util.h"
-/** List holding pointers to all registered certificate schemes */
+/* List holding pointers to all registered certificate schemes */
static GList *cert_schemes = NULL;
-/** List of registered Verifiers */
+/* List of registered Verifiers */
static GList *cert_verifiers = NULL;
-/** List of registered Pools */
+/* List of registered Pools */
static GList *cert_pools = NULL;
-/*
- * TODO: Merge this with PurpleCertificateVerificationStatus for 3.0.0 */
-typedef enum {
- PURPLE_CERTIFICATE_UNKNOWN_ERROR = -1,
-
- /* Not an error */
- PURPLE_CERTIFICATE_NO_PROBLEMS = 0,
-
- /* Non-fatal */
- PURPLE_CERTIFICATE_NON_FATALS_MASK = 0x0000FFFF,
-
- /* The certificate is self-signed. */
- PURPLE_CERTIFICATE_SELF_SIGNED = 0x01,
-
- /* The CA is not in libpurple's pool of certificates. */
- PURPLE_CERTIFICATE_CA_UNKNOWN = 0x02,
-
- /* The current time is before the certificate's specified
- * activation time.
- */
- PURPLE_CERTIFICATE_NOT_ACTIVATED = 0x04,
-
- /* The current time is after the certificate's specified expiration time */
- PURPLE_CERTIFICATE_EXPIRED = 0x08,
-
- /* The certificate's subject name doesn't match the expected */
- PURPLE_CERTIFICATE_NAME_MISMATCH = 0x10,
-
- /* No CA pool was found. This shouldn't happen... */
- PURPLE_CERTIFICATE_NO_CA_POOL = 0x20,
-
- /* Fatal */
- PURPLE_CERTIFICATE_FATALS_MASK = 0xFFFF0000,
-
- /* The signature chain could not be validated. Due to limitations in the
- * the current API, this also indicates one of the CA certificates in the
- * chain is expired (or not yet activated). FIXME 3.0.0 */
- PURPLE_CERTIFICATE_INVALID_CHAIN = 0x10000,
-
- /* The signature has been revoked. */
- PURPLE_CERTIFICATE_REVOKED = 0x20000,
-
- PURPLE_CERTIFICATE_LAST = 0x40000,
-} PurpleCertificateInvalidityFlags;
-
static const gchar *
-invalidity_reason_to_string(PurpleCertificateInvalidityFlags flag)
+invalidity_reason_to_string(PurpleCertificateVerificationStatus flag)
{
switch (flag) {
case PURPLE_CERTIFICATE_SELF_SIGNED:
@@ -121,6 +71,9 @@ invalidity_reason_to_string(PurpleCertificateInvalidityFlags flag)
case PURPLE_CERTIFICATE_REVOKED:
return _("The certificate has been revoked.");
break;
+ case PURPLE_CERTIFICATE_REJECTED:
+ return _("The certificate was rejected by the user.");
+ break;
case PURPLE_CERTIFICATE_UNKNOWN_ERROR:
default:
return _("An unknown certificate error occurred.");
@@ -275,13 +228,14 @@ purple_certificate_signed_by(PurpleCertificate *crt, PurpleCertificate *issuer)
}
gboolean
-purple_certificate_check_signature_chain_with_failing(GList *chain,
+purple_certificate_check_signature_chain(GList *chain,
PurpleCertificate **failing)
{
GList *cur;
PurpleCertificate *crt, *issuer;
gchar *uid;
- time_t now, activation, expiration;
+ time_t now;
+ gint64 activation, expiration;
gboolean ret;
g_return_val_if_fail(chain, FALSE);
@@ -319,14 +273,31 @@ purple_certificate_check_signature_chain_with_failing(GList *chain,
purple_debug_error("certificate",
"...Failed to get validity times for certificate %s\n"
"Chain is INVALID\n", uid);
- else if (now > expiration)
+ else if (now > expiration) {
+#if GLIB_CHECK_VERSION(2,26,0)
+ GDateTime *exp_dt = g_date_time_new_from_unix_local(expiration);
+ gchar *expir_str = g_date_time_format(exp_dt, "%c");
+ g_date_time_unref(exp_dt);
+#else
+ gchar *expir_str = g_strdup(ctime(&expiration));
+#endif
purple_debug_error("certificate",
"...Issuer %s expired at %s\nChain is INVALID\n",
- uid, ctime(&expiration));
- else
+ uid, expir_str);
+ g_free(expir_str);
+ } else {
+#if GLIB_CHECK_VERSION(2,26,0)
+ GDateTime *act_dt = g_date_time_new_from_unix_local(activation);
+ gchar *activ_str = g_date_time_format(act_dt, "%c");
+ g_date_time_unref(act_dt);
+#else
+ gchar *activ_str = g_strdup(ctime(&activation));
+#endif
purple_debug_error("certificate",
"...Not-yet-activated issuer %s will be valid at %s\n"
- "Chain is INVALID\n", uid, ctime(&activation));
+ "Chain is INVALID\n", uid, activ_str);
+ g_free(activ_str);
+ }
if (failing)
*failing = crt;
@@ -363,12 +334,6 @@ purple_certificate_check_signature_chain_with_failing(GList *chain,
return TRUE;
}
-gboolean
-purple_certificate_check_signature_chain(GList *chain)
-{
- return purple_certificate_check_signature_chain_with_failing(chain, NULL);
-}
-
PurpleCertificate *
purple_certificate_import(PurpleCertificateScheme *scheme, const gchar *filename)
{
@@ -487,7 +452,7 @@ purple_certificate_check_subject_name(PurpleCertificate *crt, const gchar *name)
}
gboolean
-purple_certificate_get_times(PurpleCertificate *crt, time_t *activation, time_t *expiration)
+purple_certificate_get_times(PurpleCertificate *crt, gint64 *activation, gint64 *expiration)
{
PurpleCertificateScheme *scheme;
@@ -505,6 +470,56 @@ purple_certificate_get_times(PurpleCertificate *crt, time_t *activation, time_t
return (scheme->get_times)(crt, activation, expiration);
}
+GByteArray *
+purple_certificate_get_der_data(PurpleCertificate *crt)
+{
+ PurpleCertificateScheme *scheme;
+ GByteArray *data;
+
+ g_return_val_if_fail(crt, NULL);
+ g_return_val_if_fail(crt->scheme, NULL);
+
+ scheme = crt->scheme;
+
+ g_return_val_if_fail(scheme->get_der_data, NULL);
+
+ data = (scheme->get_der_data)(crt);
+
+ return data;
+}
+
+gchar *
+purple_certificate_get_display_string(PurpleCertificate *crt)
+{
+ PurpleCertificateScheme *scheme;
+ gchar *str;
+
+ g_return_val_if_fail(crt, NULL);
+ g_return_val_if_fail(crt->scheme, NULL);
+
+ scheme = crt->scheme;
+
+ g_return_val_if_fail(scheme->get_display_string, NULL);
+
+ str = (scheme->get_display_string)(crt);
+
+ return str;
+}
+
+GType
+purple_certificate_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleCertificate",
+ (GBoxedCopyFunc)purple_certificate_copy,
+ (GBoxedFreeFunc)purple_certificate_destroy);
+ }
+
+ return type;
+}
+
gchar *
purple_certificate_pool_mkpath(PurpleCertificatePool *pool, const gchar *id)
{
@@ -644,83 +659,95 @@ purple_certificate_pool_destroy_idlist(GList *idlist)
g_list_free(idlist);
}
+static PurpleCertificatePool *
+purple_certificate_pool_copy(PurpleCertificatePool *certificate_pool)
+{
+ PurpleCertificatePool *certificate_pool_copy;
+
+ g_return_val_if_fail(certificate_pool != NULL, NULL);
+
+ certificate_pool_copy = g_new(PurpleCertificatePool, 1);
+ *certificate_pool_copy = *certificate_pool;
+
+ return certificate_pool_copy;
+}
+
+GType
+purple_certificate_pool_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleCertificatePool",
+ (GBoxedCopyFunc)purple_certificate_pool_copy,
+ (GBoxedFreeFunc)g_free);
+ }
+
+ return type;
+}
+
/****************************************************************************/
/* Builtin Verifiers, Pools, etc. */
/****************************************************************************/
static void
-x509_singleuse_verify_cb (PurpleCertificateVerificationRequest *vrq, gint id)
+x509_singleuse_verify_accept_cb(PurpleCertificateVerificationRequest *vrq)
{
g_return_if_fail(vrq);
purple_debug_info("certificate/x509_singleuse",
- "VRQ on cert from %s gave %d\n",
- vrq->subject_name, id);
-
- /* Signal what happened back to the caller */
- if (1 == id) {
- /* Accepted! */
- purple_certificate_verify_complete(vrq,
- PURPLE_CERTIFICATE_VALID);
- } else {
- /* Not accepted */
- purple_certificate_verify_complete(vrq,
- PURPLE_CERTIFICATE_INVALID);
+ "VRQ on cert from %s accepted\n",
+ vrq->subject_name);
- }
+ purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_VALID);
+}
+
+static void
+x509_singleuse_verify_reject_cb(PurpleCertificateVerificationRequest *vrq)
+{
+ g_return_if_fail(vrq);
+
+ purple_debug_info("certificate/x509_singleuse",
+ "VRQ on cert from %s rejected\n",
+ vrq->subject_name);
+
+ purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_REJECTED);
}
static void
x509_singleuse_start_verify (PurpleCertificateVerificationRequest *vrq)
{
- gchar *sha_asc;
- GByteArray *sha_bin;
gchar *cn;
const gchar *cn_match;
gchar *primary, *secondary;
- PurpleCertificate *crt = (PurpleCertificate *) vrq->cert_chain->data;
-
- /* Pull out the SHA1 checksum */
- sha_bin = purple_certificate_get_fingerprint_sha1(crt);
- /* Now decode it for display */
- sha_asc = purple_base16_encode_chunked(sha_bin->data,
- sha_bin->len);
+ PurpleCertificate *crt = (PurpleCertificate *)vrq->cert_chain->data;
- /* Get the cert Common Name */
cn = purple_certificate_get_subject_name(crt);
- /* Determine whether the name matches */
if (purple_certificate_check_subject_name(crt, vrq->subject_name)) {
- cn_match = "";
+ cn_match = _("(MATCH)");
} else {
cn_match = _("(DOES NOT MATCH)");
}
- /* Make messages */
primary = g_strdup_printf(_("%s has presented the following certificate for just-this-once use:"), vrq->subject_name);
- secondary = g_strdup_printf(_("Common name: %s %s\nFingerprint (SHA1): %s"), cn, cn_match, sha_asc);
+ secondary = g_strdup_printf(_("Common name: %s %s"), cn, cn_match);
/* Make a semi-pretty display */
- purple_request_accept_cancel(
+ purple_request_certificate(
vrq->cb_data, /* TODO: Find what the handle ought to be */
_("Single-use Certificate Verification"),
primary,
secondary,
- 0, /* Accept by default */
- NULL, /* No account */
- NULL, /* No other user */
- NULL, /* No associated conversation */
- vrq,
- x509_singleuse_verify_cb,
- x509_singleuse_verify_cb );
-
- /* Cleanup */
+ crt,
+ _("Accept"), G_CALLBACK(x509_singleuse_verify_accept_cb),
+ _("Reject"), G_CALLBACK(x509_singleuse_verify_reject_cb),
+ vrq);
+
g_free(cn);
g_free(primary);
g_free(secondary);
- g_free(sha_asc);
- g_byte_array_free(sha_bin, TRUE);
}
static void
@@ -749,7 +776,7 @@ static PurpleCertificateVerifier x509_singleuse = {
static PurpleCertificatePool x509_ca;
-/** Holds a key-value pair for quickish certificate lookup */
+/* Holds a key-value pair for quickish certificate lookup */
typedef struct {
gchar *dn;
PurpleCertificate *crt;
@@ -765,18 +792,18 @@ x509_ca_element_free(x509_ca_element *el)
g_free(el);
}
-/** System directory to probe for CA certificates */
+/* System directory to probe for CA certificates */
/* This is set in the lazy_init function */
static GList *x509_ca_paths = NULL;
-/** A list of loaded CAs, populated from the above path whenever the lazy_init
- happens. Contains pointers to x509_ca_elements */
+/* A list of loaded CAs, populated from the above path whenever the lazy_init
+ happens. Contains pointers to x509_ca_elements */
static GList *x509_ca_certs = NULL;
-/** Used for lazy initialization purposes. */
+/* Used for lazy initialization purposes. */
static gboolean x509_ca_initialized = FALSE;
-/** Adds a certificate to the in-memory cache, doing nothing else */
+/* Adds a certificate to the in-memory cache, doing nothing else */
static gboolean
x509_ca_quiet_put_cert(PurpleCertificate *crt)
{
@@ -891,15 +918,21 @@ x509_ca_init(void)
{
/* Attempt to point at the appropriate system path */
if (NULL == x509_ca_paths) {
-#ifdef _WIN32
- x509_ca_paths = g_list_append(NULL, g_build_filename(DATADIR,
- "ca-certs", NULL));
-#else
-# ifdef SSL_CERTIFICATES_DIR
- x509_ca_paths = g_list_append(NULL, g_strdup(SSL_CERTIFICATES_DIR));
-# endif
+#ifdef SSL_CERTIFICATES_DIR
+# ifdef USE_WIN32_FHS
+ x509_ca_paths = g_list_append(x509_ca_paths,
+ g_strdup(wpurple_cert_dir()));
+# else
x509_ca_paths = g_list_append(x509_ca_paths,
- g_build_filename(DATADIR, "purple", "ca-certs", NULL));
+ g_strdup(SSL_CERTIFICATES_DIR));
+# endif /* USE_WIN32_FHS */
+#endif /* SSL_CERTIFICATES_DIR */
+#if defined(_WIN32) && !defined(USE_WIN32_FHS)
+ x509_ca_paths = g_list_append(x509_ca_paths, g_build_filename(
+ PURPLE_DATADIR, "ca-certs", NULL));
+#else
+ x509_ca_paths = g_list_append(x509_ca_paths, g_build_filename(
+ PURPLE_DATADIR, "purple", "ca-certs", NULL));
#endif
}
@@ -932,7 +965,7 @@ x509_ca_uninit(void)
x509_ca_paths = NULL;
}
-/** Look up a ca_element by dn */
+/* Look up a ca_element by dn */
static x509_ca_element *
x509_ca_locate_cert(GList *lst, const gchar *dn)
{
@@ -1277,108 +1310,44 @@ static PurpleCertificatePool x509_tls_peers = {
static PurpleCertificateVerifier x509_tls_cached;
-/* The following is several hacks piled together and needs to be fixed.
- * It exists because show_cert (see its comments) needs the original reason
- * given to user_auth in order to rebuild the dialog.
- */
-/* TODO: This will cause a ua_ctx to become memleaked if the request(s) get
- closed by handle or otherwise abnormally. */
-typedef struct {
- PurpleCertificateVerificationRequest *vrq;
- gchar *reason;
-} x509_tls_cached_ua_ctx;
-
-static x509_tls_cached_ua_ctx *
-x509_tls_cached_ua_ctx_new(PurpleCertificateVerificationRequest *vrq,
- const gchar *reason)
-{
- x509_tls_cached_ua_ctx *c;
-
- c = g_new0(x509_tls_cached_ua_ctx, 1);
- c->vrq = vrq;
- c->reason = g_strdup(reason);
-
- return c;
-}
-
-
-static void
-x509_tls_cached_ua_ctx_free(x509_tls_cached_ua_ctx *c)
-{
- g_return_if_fail(c);
- g_free(c->reason);
- g_free(c);
-}
-
-static void
-x509_tls_cached_user_auth(PurpleCertificateVerificationRequest *vrq,
- const gchar *reason);
-
-static void
-x509_tls_cached_show_cert(x509_tls_cached_ua_ctx *c, gint id)
-{
- PurpleCertificate *disp_crt = c->vrq->cert_chain->data;
-
- /* Since clicking a button closes the request, show it again */
- x509_tls_cached_user_auth(c->vrq, c->reason);
-
- /* Show the certificate AFTER re-opening the dialog so that this
- appears above the other */
- purple_certificate_display_x509(disp_crt);
-
- x509_tls_cached_ua_ctx_free(c);
-}
-
static void
-x509_tls_cached_user_auth_cb (x509_tls_cached_ua_ctx *c, gint id)
+x509_tls_cached_user_auth_accept_cb(PurpleCertificateVerificationRequest *vrq)
{
- PurpleCertificateVerificationRequest *vrq;
PurpleCertificatePool *tls_peers;
+ gchar *cache_id;
- g_return_if_fail(c);
- g_return_if_fail(c->vrq);
-
- vrq = c->vrq;
-
- x509_tls_cached_ua_ctx_free(c);
+ g_return_if_fail(vrq);
- tls_peers = purple_certificate_find_pool("x509","tls_peers");
+ tls_peers = purple_certificate_find_pool("x509", "tls_peers");
- if (2 == id) {
- gchar *cache_id = vrq->subject_name;
- purple_debug_info("certificate/x509/tls_cached",
+ cache_id = vrq->subject_name;
+ purple_debug_info("certificate/x509/tls_cached",
"User ACCEPTED cert\nCaching first in chain for future use as %s...\n",
cache_id);
- purple_certificate_pool_store(tls_peers, cache_id,
- vrq->cert_chain->data);
+ purple_certificate_pool_store(tls_peers, cache_id, vrq->cert_chain->data);
- purple_certificate_verify_complete(vrq,
- PURPLE_CERTIFICATE_VALID);
- } else {
- purple_debug_warning("certificate/x509/tls_cached",
- "User REJECTED cert\n");
- purple_certificate_verify_complete(vrq,
- PURPLE_CERTIFICATE_INVALID);
- }
+ purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_VALID);
}
static void
-x509_tls_cached_user_auth_accept_cb(x509_tls_cached_ua_ctx *c, gint ignore)
+x509_tls_cached_user_auth_reject_cb(PurpleCertificateVerificationRequest *vrq)
{
- x509_tls_cached_user_auth_cb(c, 2);
-}
+ g_return_if_fail(vrq);
-static void
-x509_tls_cached_user_auth_reject_cb(x509_tls_cached_ua_ctx *c, gint ignore)
-{
- x509_tls_cached_user_auth_cb(c, 1);
+ purple_debug_warning("certificate/x509/tls_cached", "User REJECTED cert\n");
+
+ purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_REJECTED);
}
-/** Validates a certificate by asking the user
- * @param reason String to explain why the user needs to accept/refuse the
- * certificate.
- * @todo Needs a handle argument
+/*
+ * x509_tls_cached_user_auth:
+ *
+ * Validates a certificate by asking the user
+ * @reason: String to explain why the user needs to accept/refuse the
+ * certificate.
+ *
+ * TODO Needs a handle argument
*/
static void
x509_tls_cached_user_auth(PurpleCertificateVerificationRequest *vrq,
@@ -1386,37 +1355,29 @@ x509_tls_cached_user_auth(PurpleCertificateVerificationRequest *vrq,
{
gchar *primary;
- /* Make messages */
primary = g_strdup_printf(_("Accept certificate for %s?"),
vrq->subject_name);
- /* Make a semi-pretty display */
- purple_request_action(
+ purple_request_certificate(
vrq->cb_data, /* TODO: Find what the handle ought to be */
_("SSL Certificate Verification"),
primary,
reason,
- 0, /* Accept by default */
- NULL, /* No account */
- NULL, /* No other user */
- NULL, /* No associated conversation */
- x509_tls_cached_ua_ctx_new(vrq, reason),
- 3, /* Number of actions */
- _("Accept"), x509_tls_cached_user_auth_accept_cb,
- _("Reject"), x509_tls_cached_user_auth_reject_cb,
- _("_View Certificate..."), x509_tls_cached_show_cert);
-
- /* Cleanup */
+ vrq->cert_chain->data,
+ _("Accept"), G_CALLBACK(x509_tls_cached_user_auth_accept_cb),
+ _("Reject"), G_CALLBACK(x509_tls_cached_user_auth_reject_cb),
+ vrq);
+
g_free(primary);
}
static void
x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq,
- PurpleCertificateInvalidityFlags flags);
+ PurpleCertificateVerificationStatus flags);
static void
x509_tls_cached_complete(PurpleCertificateVerificationRequest *vrq,
- PurpleCertificateInvalidityFlags flags)
+ PurpleCertificateVerificationStatus flags)
{
PurpleCertificatePool *tls_peers;
PurpleCertificate *peer_crt = vrq->cert_chain->data;
@@ -1438,13 +1399,16 @@ x509_tls_cached_complete(PurpleCertificateVerificationRequest *vrq,
secondary = g_strconcat(tmp, " ", error, NULL);
g_free(tmp);
+ purple_debug_error("certificate/x509/tls_cached",
+ "Unable to validate certificate: %s\n", secondary);
+
purple_notify_error(NULL, /* TODO: Probably wrong. */
_("SSL Certificate Error"),
_("Unable to validate certificate"),
- secondary);
+ secondary, NULL);
g_free(secondary);
- purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_INVALID);
+ purple_certificate_verify_complete(vrq, flags);
return;
} else if (flags & PURPLE_CERTIFICATE_NON_FATALS_MASK) {
/* Non-fatal error. Prompt the user. */
@@ -1509,7 +1473,7 @@ x509_tls_cached_complete(PurpleCertificateVerificationRequest *vrq,
static void
x509_tls_cached_cert_in_cache(PurpleCertificateVerificationRequest *vrq,
- PurpleCertificateInvalidityFlags flags)
+ PurpleCertificateVerificationStatus flags)
{
/* TODO: Looking this up by name over and over is expensive.
Fix, please! */
@@ -1563,7 +1527,7 @@ x509_tls_cached_cert_in_cache(PurpleCertificateVerificationRequest *vrq,
*/
static void
x509_tls_cached_check_subject_name(PurpleCertificateVerificationRequest *vrq,
- PurpleCertificateInvalidityFlags flags)
+ PurpleCertificateVerificationStatus flags)
{
PurpleCertificate *peer_crt;
GList *chain = vrq->cert_chain;
@@ -1592,7 +1556,7 @@ x509_tls_cached_check_subject_name(PurpleCertificateVerificationRequest *vrq,
*/
static void
x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq,
- PurpleCertificateInvalidityFlags flags)
+ PurpleCertificateVerificationStatus flags)
{
PurpleCertificatePool *ca;
PurpleCertificate *peer_crt;
@@ -1622,7 +1586,7 @@ x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq,
ca = purple_certificate_find_pool(x509_tls_cached.scheme_name, "ca");
/* Next, check that the certificate chain is valid */
- if (!purple_certificate_check_signature_chain_with_failing(chain,
+ if (!purple_certificate_check_signature_chain(chain,
&failing_crt))
{
gboolean chain_validated = FALSE;
@@ -1672,7 +1636,7 @@ x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq,
* CA, or is a trusted CA (based on fingerprint).
*/
/* If, for whatever reason, there is no Certificate Authority pool
- loaded, we'll verify the subject name and then warn about thsi. */
+ loaded, we'll verify the subject name and then warn about this. */
if ( !ca ) {
purple_debug_error("certificate/x509/tls_cached",
"No X.509 Certificate Authority pool "
@@ -1698,16 +1662,16 @@ x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq,
"Also checking for a CA with DN=%s\n",
ca2_id);
ca_crts = g_slist_concat(x509_ca_get_certs(ca_id), x509_ca_get_certs(ca2_id));
- g_free(ca_id);
- g_free(ca2_id);
if ( NULL == ca_crts ) {
flags |= PURPLE_CERTIFICATE_CA_UNKNOWN;
purple_debug_warning("certificate/x509/tls_cached",
- "No Certificate Authorities with either DN found "
+ "No Certificate Authorities with either DN "
"found. I'll prompt the user, I guess.\n");
x509_tls_cached_check_subject_name(vrq, flags);
+ g_free(ca_id);
+ g_free(ca2_id);
return;
}
@@ -1742,12 +1706,19 @@ x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq,
g_byte_array_free(ca_fpr, TRUE);
}
- if (valid == FALSE)
+ if (valid == FALSE) {
+ purple_debug_error("certificate/x509/tls_cached",
+ "Unable to verify final certificate %s signed by %s. "
+ "Not a trusted root or signed by a trusted root.\n",
+ ca2_id, ca_id);
flags |= PURPLE_CERTIFICATE_INVALID_CHAIN;
+ }
g_slist_foreach(ca_crts, (GFunc)purple_certificate_destroy, NULL);
g_slist_free(ca_crts);
g_byte_array_free(last_fpr, TRUE);
+ g_free(ca_id);
+ g_free(ca2_id);
x509_tls_cached_check_subject_name(vrq, flags);
}
@@ -1757,8 +1728,9 @@ x509_tls_cached_start_verify(PurpleCertificateVerificationRequest *vrq)
{
const gchar *tls_peers_name = "tls_peers"; /* Name of local cache */
PurpleCertificatePool *tls_peers;
- time_t now, activation, expiration;
- PurpleCertificateInvalidityFlags flags = PURPLE_CERTIFICATE_NO_PROBLEMS;
+ time_t now;
+ gint64 activation, expiration;
+ PurpleCertificateVerificationStatus flags = PURPLE_CERTIFICATE_VALID;
gboolean ret;
g_return_if_fail(vrq);
@@ -1780,15 +1752,31 @@ x509_tls_cached_start_verify(PurpleCertificateVerificationRequest *vrq)
"Failed to get validity times for certificate %s\n",
vrq->subject_name);
} else if (now > expiration) {
+#if GLIB_CHECK_VERSION(2,26,0)
+ GDateTime *exp_dt = g_date_time_new_from_unix_local(expiration);
+ gchar *expir_str = g_date_time_format(exp_dt, "%c");
+ g_date_time_unref(exp_dt);
+#else
+ gchar *expir_str = g_strdup(ctime(&expiration));
+#endif
flags |= PURPLE_CERTIFICATE_EXPIRED;
purple_debug_error("certificate/x509/tls_cached",
"Certificate %s expired at %s\n",
- vrq->subject_name, ctime(&expiration));
+ vrq->subject_name, expir_str);
+ g_free(expir_str);
} else if (now < activation) {
+#if GLIB_CHECK_VERSION(2,26,0)
+ GDateTime *act_dt = g_date_time_new_from_unix_local(activation);
+ gchar *activ_str = g_date_time_format(act_dt, "%c");
+ g_date_time_unref(act_dt);
+#else
+ gchar *activ_str = g_strdup(ctime(&activation));
+#endif
flags |= PURPLE_CERTIFICATE_NOT_ACTIVATED;
purple_debug_error("certificate/x509/tls_cached",
"Certificate %s is not yet valid, will be at %s\n",
- vrq->subject_name, ctime(&activation));
+ vrq->subject_name, activ_str);
+ g_free(activ_str);
}
tls_peers = purple_certificate_find_pool(x509_tls_cached.scheme_name,tls_peers_name);
@@ -2092,20 +2080,16 @@ purple_certificate_register_pool(PurpleCertificatePool *pool)
purple_signal_register(pool, /* Signals emitted from pool */
"certificate-stored",
purple_marshal_VOID__POINTER_POINTER,
- NULL, /* No callback return value */
+ G_TYPE_NONE, /* No callback return value */
2, /* Two non-data arguments */
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CERTIFICATEPOOL),
- purple_value_new(PURPLE_TYPE_STRING));
+ PURPLE_TYPE_CERTIFICATE_POOL, G_TYPE_STRING);
purple_signal_register(pool, /* Signals emitted from pool */
"certificate-deleted",
purple_marshal_VOID__POINTER_POINTER,
- NULL, /* No callback return value */
+ G_TYPE_NONE, /* No callback return value */
2, /* Two non-data arguments */
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CERTIFICATEPOOL),
- purple_value_new(PURPLE_TYPE_STRING));
+ PURPLE_TYPE_CERTIFICATE_POOL, G_TYPE_STRING);
purple_debug_info("certificate",
"CertificatePool %s registered\n",
@@ -2153,63 +2137,6 @@ purple_certificate_unregister_pool(PurpleCertificatePool *pool)
/* Scheme-specific functions */
/****************************************************************************/
-void
-purple_certificate_display_x509(PurpleCertificate *crt)
-{
- gchar *sha_asc;
- GByteArray *sha_bin;
- gchar *cn;
- time_t activation, expiration;
- gchar *activ_str, *expir_str;
- gchar *secondary;
-
- /* Pull out the SHA1 checksum */
- sha_bin = purple_certificate_get_fingerprint_sha1(crt);
- /* Now decode it for display */
- sha_asc = purple_base16_encode_chunked(sha_bin->data,
- sha_bin->len);
-
- /* Get the cert Common Name */
- /* TODO: Will break on CA certs */
- cn = purple_certificate_get_subject_name(crt);
-
- /* Get the certificate times */
- /* TODO: Check the times against localtime */
- /* TODO: errorcheck? */
- if (!purple_certificate_get_times(crt, &activation, &expiration)) {
- purple_debug_error("certificate",
- "Failed to get certificate times!\n");
- activation = expiration = 0;
- }
- activ_str = g_strdup(ctime(&activation));
- expir_str = g_strdup(ctime(&expiration));
-
- /* Make messages */
- secondary = g_strdup_printf(_("Common name: %s\n\n"
- "Fingerprint (SHA1): %s\n\n"
- "Activation date: %s\n"
- "Expiration date: %s\n"),
- cn ? cn : "(null)",
- sha_asc ? sha_asc : "(null)",
- activ_str ? activ_str : "(null)",
- expir_str ? expir_str : "(null)");
-
- /* Make a semi-pretty display */
- purple_notify_info(
- NULL, /* TODO: Find what the handle ought to be */
- _("Certificate Information"),
- "",
- secondary);
-
- /* Cleanup */
- g_free(cn);
- g_free(secondary);
- g_free(sha_asc);
- g_free(activ_str);
- g_free(expir_str);
- g_byte_array_free(sha_bin, TRUE);
-}
-
void purple_certificate_add_ca_search_path(const char *path)
{
if (g_list_find_custom(x509_ca_paths, path, (GCompareFunc)strcmp))
diff --git a/libpurple/certificate.h b/libpurple/certificate.h
index 66d961770c..d6f4a3b439 100644
--- a/libpurple/certificate.h
+++ b/libpurple/certificate.h
@@ -1,10 +1,3 @@
-/**
- * @file certificate.h Public-Key Certificate API
- * @ingroup core
- * @see @ref certificate-signals
- * @since 2.2.0
- */
-
/*
*
* purple
@@ -30,51 +23,123 @@
#ifndef _PURPLE_CERTIFICATE_H
#define _PURPLE_CERTIFICATE_H
+/**
+ * SECTION:certificate
+ * @section_id: libpurple-certificate
+ * @short_description: <filename>certificate.h</filename>
+ * @title: Public-Key Certificate API
+ * @see_also: <link linkend="chapter-signals-certificate">Certificate signals</link>
+ */
#include <time.h>
#include <glib.h>
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-
+/**
+ * PurpleCertificateVerificationStatus:
+ * @PURPLE_CERTIFICATE_UNKNOWN_ERROR: Unknown error.
+ * @PURPLE_CERTIFICATE_VALID: Not an error.
+ * @PURPLE_CERTIFICATE_NON_FATALS_MASK: Non-fatal.
+ * @PURPLE_CERTIFICATE_SELF_SIGNED: The certificate is self-signed.
+ * @PURPLE_CERTIFICATE_CA_UNKNOWN: The CA is not in libpurple's pool of
+ * certificates.
+ * @PURPLE_CERTIFICATE_NOT_ACTIVATED: The current time is before the
+ * certificate's specified activation time.
+ * @PURPLE_CERTIFICATE_EXPIRED: The current time is after the certificate's
+ * specified expiration time.
+ * @PURPLE_CERTIFICATE_NAME_MISMATCH: The certificate's subject name doesn't
+ * match the expected.
+ * @PURPLE_CERTIFICATE_NO_CA_POOL: No CA pool was found. This shouldn't happen.
+ * @PURPLE_CERTIFICATE_FATALS_MASK: Fatal
+ * @PURPLE_CERTIFICATE_INVALID_CHAIN: The signature chain could not be
+ * validated. Due to limitations in the the current API,
+ * this also indicates one of the CA certificates in the
+ * chain is expired (or not yet activated).
+ * @PURPLE_CERTIFICATE_REVOKED: The signature has been revoked.
+ * @PURPLE_CERTIFICATE_REJECTED: The certificate was rejected by the user.
+ */
+/* FIXME 3.0.0 PURPLE_CERTIFICATE_INVALID_CHAIN -- see description */
typedef enum
{
- PURPLE_CERTIFICATE_INVALID = 0,
- PURPLE_CERTIFICATE_VALID = 1
+ PURPLE_CERTIFICATE_UNKNOWN_ERROR = -1,
+ PURPLE_CERTIFICATE_VALID = 0,
+ PURPLE_CERTIFICATE_NON_FATALS_MASK = 0x0000FFFF,
+ PURPLE_CERTIFICATE_SELF_SIGNED = 0x01,
+ PURPLE_CERTIFICATE_CA_UNKNOWN = 0x02,
+ PURPLE_CERTIFICATE_NOT_ACTIVATED = 0x04,
+ PURPLE_CERTIFICATE_EXPIRED = 0x08,
+ PURPLE_CERTIFICATE_NAME_MISMATCH = 0x10,
+ PURPLE_CERTIFICATE_NO_CA_POOL = 0x20,
+ PURPLE_CERTIFICATE_FATALS_MASK = 0xFFFF0000,
+ PURPLE_CERTIFICATE_INVALID_CHAIN = 0x10000,
+ PURPLE_CERTIFICATE_REVOKED = 0x20000,
+ PURPLE_CERTIFICATE_REJECTED = 0x40000,
+
+ /*< private >*/
+ PURPLE_CERTIFICATE_LAST = 0x80000,
} PurpleCertificateVerificationStatus;
+#define PURPLE_TYPE_CERTIFICATE (purple_certificate_get_type())
typedef struct _PurpleCertificate PurpleCertificate;
+
+#define PURPLE_TYPE_CERTIFICATE_POOL (purple_certificate_pool_get_type())
typedef struct _PurpleCertificatePool PurpleCertificatePool;
+
typedef struct _PurpleCertificateScheme PurpleCertificateScheme;
typedef struct _PurpleCertificateVerifier PurpleCertificateVerifier;
typedef struct _PurpleCertificateVerificationRequest PurpleCertificateVerificationRequest;
/**
+ * PurpleCertificateVerifiedCallback:
+ * @st: Status code
+ * @userdata: User-defined data
+ *
* Callback function for the results of a verification check
- * @param st Status code
- * @param userdata User-defined data
*/
-typedef void (*PurpleCertificateVerifiedCallback)
- (PurpleCertificateVerificationStatus st,
- gpointer userdata);
+typedef void (*PurpleCertificateVerifiedCallback)(PurpleCertificateVerificationStatus st,
+ gpointer userdata);
-/** A certificate instance
+/**
+ * PurpleCertificate:
+ * @scheme: Scheme this certificate is under
+ * @data: Opaque pointer to internal data
+ *
+ * A certificate instance
*
- * An opaque data structure representing a single certificate under some
- * CertificateScheme
+ * An opaque data structure representing a single certificate under some
+ * CertificateScheme
*/
struct _PurpleCertificate
{
- /** Scheme this certificate is under */
PurpleCertificateScheme * scheme;
- /** Opaque pointer to internal data */
gpointer data;
};
/**
+ * PurpleCertificatePool:
+ * @scheme_name: Scheme this Pool operates for
+ * @name: Internal name to refer to the pool by
+ * @fullname: User-friendly name for this type. ex: N_("SSL Servers"). When
+ * this is displayed anywhere, it should be i18ned.
+ * ex: _(pool->fullname)
+ * @data: Internal pool data
+ * @init: Set up the Pool's internal state
+ * <sbr/>Upon calling purple_certificate_register_pool() , this
+ * function will be called. May be %NULL.
+ * <sbr/>Returns: %TRUE if the initialization succeeded,
+ * otherwise %FALSE.
+ * @uninit: Uninit the Pool's internal state. Will be called by
+ * purple_certificate_unregister_pool(). May be %NULL.
+ * @cert_in_pool: Check for presence of a certificate in the pool using unique
+ * ID
+ * @get_cert: Retrieve a PurpleCertificate from the pool
+ * @put_cert: Add a certificate to the pool. Must overwrite any other
+ * certificates sharing the same ID in the pool.
+ * <sbr/>Returns: %TRUE if the operation succeeded, otherwise
+ * %FALSE.
+ * @delete_cert: Delete a certificate from the pool
+ * @get_idlist: Returns a list of IDs stored in the pool
+ *
* Database for retrieval or storage of Certificates
*
* More or less a hash table; all lookups and writes are controlled by a string
@@ -82,301 +147,240 @@ struct _PurpleCertificate
*/
struct _PurpleCertificatePool
{
- /** Scheme this Pool operates for */
gchar *scheme_name;
- /** Internal name to refer to the pool by */
gchar *name;
-
- /** User-friendly name for this type
- * ex: N_("SSL Servers")
- * When this is displayed anywhere, it should be i18ned
- * ex: _(pool->fullname)
- */
gchar *fullname;
-
- /** Internal pool data */
gpointer data;
- /**
- * Set up the Pool's internal state
- *
- * Upon calling purple_certificate_register_pool() , this function will
- * be called. May be NULL.
- * @return TRUE if the initialization succeeded, otherwise FALSE
- */
gboolean (* init)(void);
-
- /**
- * Uninit the Pool's internal state
- *
- * Will be called by purple_certificate_unregister_pool() . May be NULL
- */
void (* uninit)(void);
- /** Check for presence of a certificate in the pool using unique ID */
gboolean (* cert_in_pool)(const gchar *id);
- /** Retrieve a PurpleCertificate from the pool */
PurpleCertificate * (* get_cert)(const gchar *id);
- /** Add a certificate to the pool. Must overwrite any other
- * certificates sharing the same ID in the pool.
- * @return TRUE if the operation succeeded, otherwise FALSE
- */
gboolean (* put_cert)(const gchar *id, PurpleCertificate *crt);
- /** Delete a certificate from the pool */
gboolean (* delete_cert)(const gchar *id);
- /** Returns a list of IDs stored in the pool */
GList * (* get_idlist)(void);
+ /*< private >*/
void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
void (*_purple_reserved4)(void);
};
-/** A certificate type
- *
- * A CertificateScheme must implement all of the fields in the structure,
- * and register it using purple_certificate_register_scheme()
- *
- * There may be only ONE CertificateScheme provided for each certificate
- * type, as specified by the "name" field.
+/**
+ * PurpleCertificateScheme:
+ * @name: Name of the certificate type. ex: "x509", "pgp", etc.
+ * <sbr/> This must be globally unique - you may not register more than
+ * one CertificateScheme of the same name at a time.
+ * @fullname: User-friendly name for this type. ex: N_("X.509 Certificates")
+ * <sbr/> When this is displayed anywhere, it should be i18ned. ex:
+ * _(scheme->fullname)
+ * @import_certificate: Imports a certificate from a file
+ * <sbr/> @filename: File to import the certificate from
+ * <sbr/> Returns: Pointer to the newly allocated
+ * Certificate struct or NULL on failure.
+ * @export_certificate: Exports a certificate to a file.
+ * <sbr/>See purple_certificate_export().
+ * <sbr/>@filename: File to export the certificate to
+ * <sbr/>@crt: Certificate to export
+ * <sbr/>Returns: %TRUE if the export succeeded,
+ * otherwise %FALSE
+ * @copy_certificate: Duplicates a certificate
+ * <sbr/>Certificates are generally assumed to be read-only,
+ * so feel free to do any sort of reference-counting magic
+ * you want here. If this ever changes, please remember to
+ * change the magic accordingly.
+ * <sbr/>Returns: Reference to the new copy
+ * @destroy_certificate: Destroys and frees a Certificate structure
+ * <sbr/> Destroys a Certificate's internal data
+ * structures and calls free(@crt)
+ * <sbr/> @crt: Certificate instance to be destroyed.
+ * It <emphasis>WILL NOT</emphasis> be destroyed if it is
+ * not of the correct CertificateScheme. Can be %NULL.
+ * @signed_by: Find whether "crt" has a valid signature from issuer "issuer".
+ * <sbr/>See purple_certificate_signed_by().
+ * @get_fingerprint_sha1: Retrieves the certificate public key fingerprint using
+ * SHA1
+ * <sbr/>@crt: Certificate instance
+ * <sbr/>Returns: Binary representation of SHA1 hash -
+ * must be freed using g_byte_array_free().
+ * @get_unique_id: Retrieves a unique certificate identifier
+ * <sbr/>@crt: Certificate instance
+ * <sbr/>Returns: Newly allocated string that can be used to
+ * uniquely identify the certificate.
+ * @get_issuer_unique_id: Retrieves a unique identifier for the certificate's
+ * issuer
+ * <sbr/>@crt: Certificate instance
+ * <sbr/>Returns: Newly allocated string that can be used
+ * to uniquely identify the issuer's
+ * certificate.
+ * @get_subject_name: Gets the certificate subject's name
+ * <sbr/>For X.509, this is the "Common Name" field, as we're
+ * only using it for hostname verification at the moment.
+ * <sbr/>See purple_certificate_get_subject_name().
+ * <sbr/>@crt: Certificate instance
+ * <sbr/>Returns: Newly allocated string with the certificate
+ * subject.
+ * @check_subject_name: Check the subject name against that on the certificate
+ * <sbr/>See purple_certificate_check_subject_name().
+ * <sbr/>Returns: %TRUE if it is a match, else %FALSE
+ * @get_times: Retrieve the certificate activation/expiration times
+ * @import_certificates: Imports certificates from a file
+ * <sbr/> @filename: File to import the certificates from
+ * <sbr/> Returns: #GSList of pointers to the newly
+ * allocated Certificate structs or
+ * %NULL on failure.
+ * @get_der_data: Retrieves the certificate data in DER form
+ * <sbr/>@crt: Certificate instance
+ * <sbr/>Returns: Binary DER representation of certificate - must
+ * be freed using g_byte_array_free().
+ * @get_display_string: Retrieves a string representation of the certificate
+ * suitable for display
+ * <sbr/>@crt: Certificate instance
+ * <sbr/>Returns: User-displayable string representation of
+ * certificate - must be freed using
+ * g_free().
+ *
+ * A certificate type.
+ *
+ * A CertificateScheme must implement all of the fields in the structure,
+ * and register it using purple_certificate_register_scheme().
+ *
+ * There may be only <emphasis>ONE</emphasis> CertificateScheme provided for
+ * each certificate type, as specified by the "name" field.
*/
struct _PurpleCertificateScheme
{
- /** Name of the certificate type
- * ex: "x509", "pgp", etc.
- * This must be globally unique - you may not register more than one
- * CertificateScheme of the same name at a time.
- */
gchar * name;
-
- /** User-friendly name for this type
- * ex: N_("X.509 Certificates")
- * When this is displayed anywhere, it should be i18ned
- * ex: _(scheme->fullname)
- */
gchar * fullname;
- /** Imports a certificate from a file
- *
- * @param filename File to import the certificate from
- * @return Pointer to the newly allocated Certificate struct
- * or NULL on failure.
- */
PurpleCertificate * (* import_certificate)(const gchar * filename);
-
- /**
- * Exports a certificate to a file
- *
- * @param filename File to export the certificate to
- * @param crt Certificate to export
- * @return TRUE if the export succeeded, otherwise FALSE
- * @see purple_certificate_export()
- */
gboolean (* export_certificate)(const gchar *filename, PurpleCertificate *crt);
- /**
- * Duplicates a certificate
- *
- * Certificates are generally assumed to be read-only, so feel free to
- * do any sort of reference-counting magic you want here. If this ever
- * changes, please remember to change the magic accordingly.
- * @return Reference to the new copy
- */
PurpleCertificate * (* copy_certificate)(PurpleCertificate *crt);
-
- /** Destroys and frees a Certificate structure
- *
- * Destroys a Certificate's internal data structures and calls
- * free(crt)
- *
- * @param crt Certificate instance to be destroyed. It WILL NOT be
- * destroyed if it is not of the correct
- * CertificateScheme. Can be NULL
- */
void (* destroy_certificate)(PurpleCertificate * crt);
- /** Find whether "crt" has a valid signature from issuer "issuer"
- * @see purple_certificate_signed_by() */
gboolean (*signed_by)(PurpleCertificate *crt, PurpleCertificate *issuer);
- /**
- * Retrieves the certificate public key fingerprint using SHA1
- *
- * @param crt Certificate instance
- * @return Binary representation of SHA1 hash - must be freed using
- * g_byte_array_free()
- */
GByteArray * (* get_fingerprint_sha1)(PurpleCertificate *crt);
-
- /**
- * Retrieves a unique certificate identifier
- *
- * @param crt Certificate instance
- * @return Newly allocated string that can be used to uniquely
- * identify the certificate.
- */
gchar * (* get_unique_id)(PurpleCertificate *crt);
-
- /**
- * Retrieves a unique identifier for the certificate's issuer
- *
- * @param crt Certificate instance
- * @return Newly allocated string that can be used to uniquely
- * identify the issuer's certificate.
- */
gchar * (* get_issuer_unique_id)(PurpleCertificate *crt);
- /**
- * Gets the certificate subject's name
- *
- * For X.509, this is the "Common Name" field, as we're only using it
- * for hostname verification at the moment
- *
- * @see purple_certificate_get_subject_name()
- *
- * @param crt Certificate instance
- * @return Newly allocated string with the certificate subject.
- */
gchar * (* get_subject_name)(PurpleCertificate *crt);
-
- /**
- * Check the subject name against that on the certificate
- * @see purple_certificate_check_subject_name()
- * @return TRUE if it is a match, else FALSE
- */
gboolean (* check_subject_name)(PurpleCertificate *crt, const gchar *name);
- /** Retrieve the certificate activation/expiration times */
- gboolean (* get_times)(PurpleCertificate *crt, time_t *activation, time_t *expiration);
+ gboolean (* get_times)(PurpleCertificate *crt, gint64 *activation, gint64 *expiration);
- /** Imports certificates from a file
- *
- * @param filename File to import the certificates from
- * @return GSList of pointers to the newly allocated Certificate structs
- * or NULL on failure.
- */
GSList * (* import_certificates)(const gchar * filename);
+ GByteArray * (* get_der_data)(PurpleCertificate *crt);
+ gchar * (* get_display_string)(PurpleCertificate *crt);
+
+ /*< private >*/
void (*_purple_reserved1)(void);
- void (*_purple_reserved2)(void);
- void (*_purple_reserved3)(void);
};
-/** A set of operations used to provide logic for verifying a Certificate's
- * authenticity.
+/**
+ * PurpleCertificateVerifier:
+ * @scheme_name: Name of the scheme this Verifier operates on. The scheme
+ * will be looked up by name when a Request is generated
+ * using this Verifier.
+ * @name: Name of the Verifier - case insensitive.
+ * @start_verification: Start the verification process. To be called from
+ * purple_certificate_verify() once it has constructed the
+ * request. This will use the information in the given
+ * VerificationRequest to check the certificate and
+ * callback the requester with the verification results.
+ * <sbr/>@vrq: The request to process.
+ * @destroy_request: Destroy a completed Request under this Verifier. The
+ * function pointed to here is only responsible for
+ * cleaning up whatever
+ * #PurpleCertificateVerificationRequest.data points to.
+ * It should not call free(@vrq).
+ * <sbr/>@vrq: The request to destroy.
+ *
+ * A set of operations used to provide logic for verifying a Certificate's
+ * authenticity.
*
* A Verifier provider must fill out these fields, then register it using
- * purple_certificate_register_verifier()
+ * purple_certificate_register_verifier().
*
* The (scheme_name, name) value must be unique for each Verifier - you may not
- * register more than one Verifier of the same name for each Scheme
+ * register more than one Verifier of the same name for each Scheme.
*/
struct _PurpleCertificateVerifier
{
- /** Name of the scheme this Verifier operates on
- *
- * The scheme will be looked up by name when a Request is generated
- * using this Verifier
- */
gchar *scheme_name;
-
- /** Name of the Verifier - case insensitive */
gchar *name;
- /**
- * Start the verification process
- *
- * To be called from purple_certificate_verify once it has
- * constructed the request. This will use the information in the
- * given VerificationRequest to check the certificate and callback
- * the requester with the verification results.
- *
- * @param vrq Request to process
- */
void (* start_verification)(PurpleCertificateVerificationRequest *vrq);
-
- /**
- * Destroy a completed Request under this Verifier
- * The function pointed to here is only responsible for cleaning up
- * whatever PurpleCertificateVerificationRequest::data points to.
- * It should not call free(vrq)
- *
- * @param vrq Request to destroy
- */
void (* destroy_request)(PurpleCertificateVerificationRequest *vrq);
+ /*< private >*/
void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
void (*_purple_reserved4)(void);
};
-/** Structure for a single certificate request
+/**
+ * PurpleCertificateVerificationRequest:
+ * @verifier: Reference to the verification logic used.
+ * @scheme: Reference to the scheme used. This is looked up from the
+ * Verifier when the Request is generated.
+ * @subject_name: Name to check that the certificate is issued to. For X.509
+ * certificates, this is the Common Name.
+ * @cert_chain: List of certificates in the chain to be verified (such as that
+ * returned by purple_ssl_get_peer_certificates()). This is most
+ * relevant for X.509 certificates used in SSL sessions. The list
+ * order should be: certificate, issuer, issuer's issuer, etc.
+ * @data: Internal data used by the Verifier code.
+ * @cb: Function to call with the verification result.
+ * @cb_data: Data to pass to the post-verification callback.
*
- * Useful for keeping track of the state of a verification that involves
- * several steps
+ * Structure for a single certificate request
+ *
+ * Useful for keeping track of the state of a verification that involves
+ * several steps
*/
struct _PurpleCertificateVerificationRequest
{
- /** Reference to the verification logic used */
PurpleCertificateVerifier *verifier;
- /** Reference to the scheme used.
- *
- * This is looked up from the Verifier when the Request is generated
- */
PurpleCertificateScheme *scheme;
-
- /**
- * Name to check that the certificate is issued to
- *
- * For X.509 certificates, this is the Common Name
- */
gchar *subject_name;
-
- /** List of certificates in the chain to be verified (such as that returned by purple_ssl_get_peer_certificates )
- *
- * This is most relevant for X.509 certificates used in SSL sessions.
- * The list order should be: certificate, issuer, issuer's issuer, etc.
- */
GList *cert_chain;
-
- /** Internal data used by the Verifier code */
gpointer data;
-
- /** Function to call with the verification result */
PurpleCertificateVerifiedCallback cb;
- /** Data to pass to the post-verification callback */
gpointer cb_data;
};
+G_BEGIN_DECLS
+
/*****************************************************************************/
-/** @name Certificate Verification Functions */
+/* Certificate Verification Functions */
/*****************************************************************************/
-/*@{*/
/**
+ * purple_certificate_verify:
+ * @verifier: Verification logic to use.
+ * See purple_certificate_find_verifier().
+ * @subject_name: Name that should match the first certificate in the
+ * chain for the certificate to be valid. Will be strdup'd
+ * into the Request struct
+ * @cert_chain: Certificate chain to check. If there is more than one
+ * certificate in the chain (X.509), the peer's
+ * certificate comes first, then the issuer/signer's
+ * certificate, etc. The whole list is duplicated into the
+ * Request struct.
+ * @cb: (scope call): Callback function to be called with whether the
+ * certificate was approved or not.
+ * @cb_data: User-defined data for the above.
+ *
* Constructs a verification request and passed control to the specified Verifier
*
* It is possible that the callback will be called immediately upon calling
* this function. Plan accordingly.
- *
- * @param verifier Verification logic to use.
- * @see purple_certificate_find_verifier()
- *
- * @param subject_name Name that should match the first certificate in the
- * chain for the certificate to be valid. Will be strdup'd
- * into the Request struct
- *
- * @param cert_chain Certificate chain to check. If there is more than one
- * certificate in the chain (X.509), the peer's
- * certificate comes first, then the issuer/signer's
- * certificate, etc. The whole list is duplicated into the
- * Request struct.
- *
- * @param cb Callback function to be called with whether the
- * certificate was approved or not.
- * @param cb_data User-defined data for the above.
*/
void
purple_certificate_verify (PurpleCertificateVerifier *verifier,
@@ -385,459 +389,541 @@ purple_certificate_verify (PurpleCertificateVerifier *verifier,
gpointer cb_data);
/**
- * Completes and destroys a VerificationRequest
- *
- * @param vrq Request to conclude
- * @param st Success/failure code to pass to the request's
+ * purple_certificate_verify_complete:
+ * @vrq: Request to conclude
+ * @st: Success/failure code to pass to the request's
* completion callback.
+ *
+ * Completes and destroys a VerificationRequest
*/
void
purple_certificate_verify_complete(PurpleCertificateVerificationRequest *vrq,
PurpleCertificateVerificationStatus st);
-/*@}*/
-
/*****************************************************************************/
-/** @name Certificate Functions */
+/* Certificate Functions */
/*****************************************************************************/
-/*@{*/
/**
+ * purple_certificate_get_type:
+ *
+ * Returns: The #GType for the #PurpleCertificate boxed structure.
+ */
+GType purple_certificate_get_type(void);
+
+/**
+ * purple_certificate_copy:
+ * @crt: Instance to duplicate
+ *
* Makes a duplicate of a certificate
*
- * @param crt Instance to duplicate
- * @return Pointer to new instance
+ * Returns: Pointer to new instance
*/
PurpleCertificate *
purple_certificate_copy(PurpleCertificate *crt);
/**
+ * purple_certificate_copy_list:
+ * @crt_list: List to duplicate
+ *
* Duplicates an entire list of certificates
*
- * @param crt_list List to duplicate
- * @return New list copy
+ * Returns: New list copy
*/
GList *
purple_certificate_copy_list(GList *crt_list);
/**
- * Destroys and free()'s a Certificate
+ * purple_certificate_destroy:
+ * @crt: Instance to destroy. May be NULL.
*
- * @param crt Instance to destroy. May be NULL.
+ * Destroys and free()'s a Certificate
*/
void
purple_certificate_destroy (PurpleCertificate *crt);
/**
- * Destroy an entire list of Certificate instances and the containing list
+ * purple_certificate_destroy_list:
+ * @crt_list: List of certificates to destroy. May be NULL.
*
- * @param crt_list List of certificates to destroy. May be NULL.
+ * Destroy an entire list of Certificate instances and the containing list
*/
void
purple_certificate_destroy_list (GList * crt_list);
/**
- * Check whether 'crt' has a valid signature made by 'issuer'
+ * purple_certificate_signed_by:
+ * @crt: Certificate instance to check signature of
+ * @issuer: Certificate thought to have signed 'crt'
*
- * @param crt Certificate instance to check signature of
- * @param issuer Certificate thought to have signed 'crt'
+ * Check whether 'crt' has a valid signature made by 'issuer'
*
- * @return TRUE if 'crt' has a valid signature made by 'issuer',
+ * Returns: TRUE if 'crt' has a valid signature made by 'issuer',
* otherwise FALSE
- * @todo Find a way to give the reason (bad signature, not the issuer, etc.)
*/
+/* TODO Find a way to give the reason (bad signature, not the issuer, etc.) */
gboolean
purple_certificate_signed_by(PurpleCertificate *crt, PurpleCertificate *issuer);
/**
- * Check that a certificate chain is valid and, if not, the failing certificate.
- *
- * Uses purple_certificate_signed_by() to verify that each PurpleCertificate
- * in the chain carries a valid signature from the next. A single-certificate
- * chain is considered to be valid.
- *
- * @param chain List of PurpleCertificate instances comprising the chain,
+ * purple_certificate_check_signature_chain:
+ * @chain: List of PurpleCertificate instances comprising the chain,
* in the order certificate, issuer, issuer's issuer, etc.
- * @param failing A pointer to a PurpleCertificate*. If not NULL, if the
+ * @failing: A pointer to a PurpleCertificate*. If not NULL, if the
* chain fails to validate, this will be set to the
* certificate whose signature could not be validated.
- * @return TRUE if the chain is valid. See description.
*
- * @since 2.6.0
- * @deprecated This function will become
- * purple_certificate_check_signature_chain in 3.0.0
- */
-gboolean
-purple_certificate_check_signature_chain_with_failing(GList *chain,
- PurpleCertificate **failing);
-
-/**
- * Check that a certificate chain is valid
+ * Check that a certificate chain is valid and, if not, the failing certificate.
*
* Uses purple_certificate_signed_by() to verify that each PurpleCertificate
* in the chain carries a valid signature from the next. A single-certificate
* chain is considered to be valid.
*
- * @param chain List of PurpleCertificate instances comprising the chain,
- * in the order certificate, issuer, issuer's issuer, etc.
- * @return TRUE if the chain is valid. See description.
- * @todo Specify which certificate in the chain caused a failure
- * @deprecated This function will be removed in 3.0.0 and replaced with
- * purple_certificate_check_signature_chain_with_failing
+ * Returns: TRUE if the chain is valid. See description.
*/
gboolean
-purple_certificate_check_signature_chain(GList *chain);
+purple_certificate_check_signature_chain(GList *chain,
+ PurpleCertificate **failing);
/**
+ * purple_certificate_import:
+ * @scheme: Scheme to import under
+ * @filename: File path to import from
+ *
* Imports a PurpleCertificate from a file
*
- * @param scheme Scheme to import under
- * @param filename File path to import from
- * @return Pointer to a new PurpleCertificate, or NULL on failure
+ * Returns: Pointer to a new PurpleCertificate, or NULL on failure
*/
PurpleCertificate *
purple_certificate_import(PurpleCertificateScheme *scheme, const gchar *filename);
/**
+ * purple_certificates_import:
+ * @scheme: Scheme to import under
+ * @filename: File path to import from
+ *
* Imports a list of PurpleCertificates from a file
*
- * @param scheme Scheme to import under
- * @param filename File path to import from
- * @return Pointer to a GSList of new PurpleCertificates, or NULL on failure
+ * Returns: Pointer to a GSList of new PurpleCertificates, or NULL on failure
*/
GSList *
purple_certificates_import(PurpleCertificateScheme *scheme, const gchar *filename);
/**
+ * purple_certificate_export:
+ * @filename: File to export the certificate to
+ * @crt: Certificate to export
+ *
* Exports a PurpleCertificate to a file
*
- * @param filename File to export the certificate to
- * @param crt Certificate to export
- * @return TRUE if the export succeeded, otherwise FALSE
+ * Returns: TRUE if the export succeeded, otherwise FALSE
*/
gboolean
purple_certificate_export(const gchar *filename, PurpleCertificate *crt);
/**
+ * purple_certificate_get_fingerprint_sha1:
+ * @crt: Certificate instance
+ *
* Retrieves the certificate public key fingerprint using SHA1.
+ * See purple_base16_encode_chunked().
*
- * @param crt Certificate instance
- * @return Binary representation of the hash. You are responsible for free()ing
+ * Returns: Binary representation of the hash. You are responsible for free()ing
* this.
- * @see purple_base16_encode_chunked()
*/
GByteArray *
purple_certificate_get_fingerprint_sha1(PurpleCertificate *crt);
/**
+ * purple_certificate_get_unique_id:
+ * @crt: Certificate instance
+ *
* Get a unique identifier for the certificate
*
- * @param crt Certificate instance
- * @return String representing the certificate uniquely. Must be g_free()'ed
+ * Returns: String representing the certificate uniquely. Must be g_free()'ed
*/
gchar *
purple_certificate_get_unique_id(PurpleCertificate *crt);
/**
+ * purple_certificate_get_issuer_unique_id:
+ * @crt: Certificate instance
+ *
* Get a unique identifier for the certificate's issuer
*
- * @param crt Certificate instance
- * @return String representing the certificate's issuer uniquely. Must be
+ * Returns: String representing the certificate's issuer uniquely. Must be
* g_free()'ed
*/
gchar *
purple_certificate_get_issuer_unique_id(PurpleCertificate *crt);
/**
+ * purple_certificate_get_subject_name:
+ * @crt: Certificate instance
+ *
* Gets the certificate subject's name
*
* For X.509, this is the "Common Name" field, as we're only using it
* for hostname verification at the moment
*
- * @param crt Certificate instance
- * @return Newly allocated string with the certificate subject.
+ * Returns: Newly allocated string with the certificate subject.
*/
gchar *
purple_certificate_get_subject_name(PurpleCertificate *crt);
/**
+ * purple_certificate_check_subject_name:
+ * @crt: Certificate instance
+ * @name: Name to check.
+ *
* Check the subject name against that on the certificate
- * @param crt Certificate instance
- * @param name Name to check.
- * @return TRUE if it is a match, else FALSE
+ *
+ * Returns: TRUE if it is a match, else FALSE
*/
gboolean
purple_certificate_check_subject_name(PurpleCertificate *crt, const gchar *name);
/**
- * Get the expiration/activation times.
- *
- * @param crt Certificate instance
- * @param activation Reference to store the activation time at. May be NULL
+ * purple_certificate_get_times:
+ * @crt: Certificate instance
+ * @activation: Reference to store the activation time at. May be NULL
* if you don't actually want it.
- * @param expiration Reference to store the expiration time at. May be NULL
+ * @expiration: Reference to store the expiration time at. May be NULL
* if you don't actually want it.
- * @return TRUE if the requested values were obtained, otherwise FALSE.
+ *
+ * Get the expiration/activation times.
+ *
+ * Returns: TRUE if the requested values were obtained, otherwise FALSE.
*/
gboolean
-purple_certificate_get_times(PurpleCertificate *crt, time_t *activation, time_t *expiration);
+purple_certificate_get_times(PurpleCertificate *crt, gint64 *activation, gint64 *expiration);
-/*@}*/
+/**
+ * purple_certificate_get_der_data:
+ * @crt: Certificate instance
+ *
+ * Retrieves the certificate data in DER form.
+ *
+ * Returns: Binary DER representation of the certificate - must be freed using
+ * g_byte_array_free().
+ */
+GByteArray *
+purple_certificate_get_der_data(PurpleCertificate *crt);
+
+/**
+ * purple_certificate_get_display_string:
+ * @crt: Certificate instance
+ *
+ * Retrieves a string suitable for displaying a certificate to the user.
+ *
+ * Returns: String representing the certificate that may be displayed to the user
+ * - must be freed using g_free().
+ */
+char *
+purple_certificate_get_display_string(PurpleCertificate *crt);
/*****************************************************************************/
-/** @name Certificate Pool Functions */
+/* Certificate Pool Functions */
/*****************************************************************************/
-/*@{*/
+
+/**
+ * purple_certificate_pool_get_type:
+ *
+ * Returns: The #GType for the #PurpleCertificatePool boxed structure.
+ */
+/* TODO: Boxing of PurpleCertificatePool is a temporary solution to having a
+ * GType for certificate pools. This should rather be a GObject instead of
+ * a GBoxed. */
+GType purple_certificate_pool_get_type(void);
+
/**
+ * purple_certificate_pool_mkpath:
+ * @pool: CertificatePool to build a path for
+ * @id: Key to look up a Certificate by. May be NULL.
+ *
* Helper function for generating file paths in ~/.purple/certificates for
* CertificatePools that use them.
*
* All components will be escaped for filesystem friendliness.
*
- * @param pool CertificatePool to build a path for
- * @param id Key to look up a Certificate by. May be NULL.
- * @return A newly allocated path of the form
+ * Returns: A newly allocated path of the form
* ~/.purple/certificates/scheme_name/pool_name/unique_id
*/
gchar *
purple_certificate_pool_mkpath(PurpleCertificatePool *pool, const gchar *id);
/**
+ * purple_certificate_pool_usable:
+ * @pool: Pool to check
+ *
* Determines whether a pool can be used.
*
* Checks whether the associated CertificateScheme is loaded.
*
- * @param pool Pool to check
- *
- * @return TRUE if the pool can be used, otherwise FALSE
+ * Returns: TRUE if the pool can be used, otherwise FALSE
*/
gboolean
purple_certificate_pool_usable(PurpleCertificatePool *pool);
/**
- * Looks up the scheme the pool operates under
+ * purple_certificate_pool_get_scheme:
+ * @pool: Pool to get the scheme of
*
- * @param pool Pool to get the scheme of
+ * Looks up the scheme the pool operates under.
+ * See purple_certificate_pool_usable()
*
- * @return Pointer to the pool's scheme, or NULL if it isn't loaded.
- * @see purple_certificate_pool_usable()
+ * Returns: Pointer to the pool's scheme, or NULL if it isn't loaded.
*/
PurpleCertificateScheme *
purple_certificate_pool_get_scheme(PurpleCertificatePool *pool);
/**
+ * purple_certificate_pool_contains:
+ * @pool: Pool to look in
+ * @id: ID to look for
+ *
* Check for presence of an ID in a pool.
- * @param pool Pool to look in
- * @param id ID to look for
- * @return TRUE if the ID is in the pool, else FALSE
+ *
+ * Returns: TRUE if the ID is in the pool, else FALSE
*/
gboolean
purple_certificate_pool_contains(PurpleCertificatePool *pool, const gchar *id);
/**
+ * purple_certificate_pool_retrieve:
+ * @pool: Pool to fish in
+ * @id: ID to look up
+ *
* Retrieve a certificate from a pool.
- * @param pool Pool to fish in
- * @param id ID to look up
- * @return Retrieved certificate, or NULL if it wasn't there
+ *
+ * Returns: Retrieved certificate, or NULL if it wasn't there
*/
PurpleCertificate *
purple_certificate_pool_retrieve(PurpleCertificatePool *pool, const gchar *id);
/**
+ * purple_certificate_pool_store:
+ * @pool: Pool to add to
+ * @id: ID to store the certificate with
+ * @crt: Certificate to store
+ *
* Add a certificate to a pool
*
* Any pre-existing certificate of the same ID will be overwritten.
*
- * @param pool Pool to add to
- * @param id ID to store the certificate with
- * @param crt Certificate to store
- * @return TRUE if the operation succeeded, otherwise FALSE
+ * Returns: TRUE if the operation succeeded, otherwise FALSE
*/
gboolean
purple_certificate_pool_store(PurpleCertificatePool *pool, const gchar *id, PurpleCertificate *crt);
/**
+ * purple_certificate_pool_delete:
+ * @pool: Pool to remove from
+ * @id: ID to remove
+ *
* Remove a certificate from a pool
*
- * @param pool Pool to remove from
- * @param id ID to remove
- * @return TRUE if the operation succeeded, otherwise FALSE
+ * Returns: TRUE if the operation succeeded, otherwise FALSE
*/
gboolean
purple_certificate_pool_delete(PurpleCertificatePool *pool, const gchar *id);
/**
+ * purple_certificate_pool_get_idlist:
+ * @pool: Pool to enumerate
+ *
* Get the list of IDs currently in the pool.
*
- * @param pool Pool to enumerate
- * @return GList pointing to newly-allocated id strings. Free using
+ * Returns: GList pointing to newly-allocated id strings. Free using
* purple_certificate_pool_destroy_idlist()
*/
GList *
purple_certificate_pool_get_idlist(PurpleCertificatePool *pool);
/**
- * Destroys the result given by purple_certificate_pool_get_idlist()
+ * purple_certificate_pool_destroy_idlist:
+ * @idlist: ID List to destroy
*
- * @param idlist ID List to destroy
+ * Destroys the result given by purple_certificate_pool_get_idlist()
*/
void
purple_certificate_pool_destroy_idlist(GList *idlist);
-/*@}*/
-
/*****************************************************************************/
-/** @name Certificate Subsystem API */
+/* Certificate Subsystem API */
/*****************************************************************************/
-/*@{*/
/**
+ * purple_certificate_init:
+ *
* Initialize the certificate system
*/
void
purple_certificate_init(void);
/**
+ * purple_certificate_uninit:
+ *
* Un-initialize the certificate system
*/
void
purple_certificate_uninit(void);
/**
+ * purple_certificate_get_handle:
+ *
* Get the Certificate subsystem handle for signalling purposes
*/
gpointer
purple_certificate_get_handle(void);
-/** Look up a registered CertificateScheme by name
- * @param name The scheme name. Case insensitive.
- * @return Pointer to the located Scheme, or NULL if it isn't found.
+/**
+ * purple_certificate_find_scheme:
+ * @name: The scheme name. Case insensitive.
+ *
+ * Look up a registered CertificateScheme by name
+ *
+ * Returns: Pointer to the located Scheme, or NULL if it isn't found.
*/
PurpleCertificateScheme *
purple_certificate_find_scheme(const gchar *name);
/**
+ * purple_certificate_get_schemes:
+ *
* Get all registered CertificateSchemes
*
- * @return GList pointing to all registered CertificateSchemes . This value
+ * Returns: GList pointing to all registered CertificateSchemes . This value
* is owned by libpurple
*/
GList *
purple_certificate_get_schemes(void);
-/** Register a CertificateScheme with libpurple
+/**
+ * purple_certificate_register_scheme:
+ * @scheme: Pointer to the scheme to register.
+ *
+ * Register a CertificateScheme with libpurple
*
* No two schemes can be registered with the same name; this function enforces
* that.
*
- * @param scheme Pointer to the scheme to register.
- * @return TRUE if the scheme was successfully added, otherwise FALSE
+ * Returns: TRUE if the scheme was successfully added, otherwise FALSE
*/
gboolean
purple_certificate_register_scheme(PurpleCertificateScheme *scheme);
-/** Unregister a CertificateScheme from libpurple
- *
- * @param scheme Scheme to unregister.
+/**
+ * purple_certificate_unregister_scheme:
+ * @scheme: Scheme to unregister.
* If the scheme is not registered, this is a no-op.
*
- * @return TRUE if the unregister completed successfully
+ * Unregister a CertificateScheme from libpurple
+ *
+ * Returns: TRUE if the unregister completed successfully
*/
gboolean
purple_certificate_unregister_scheme(PurpleCertificateScheme *scheme);
-/** Look up a registered PurpleCertificateVerifier by scheme and name
- * @param scheme_name Scheme name. Case insensitive.
- * @param ver_name The verifier name. Case insensitive.
- * @return Pointer to the located Verifier, or NULL if it isn't found.
+/**
+ * purple_certificate_find_verifier:
+ * @scheme_name: Scheme name. Case insensitive.
+ * @ver_name: The verifier name. Case insensitive.
+ *
+ * Look up a registered PurpleCertificateVerifier by scheme and name
+ *
+ * Returns: Pointer to the located Verifier, or NULL if it isn't found.
*/
PurpleCertificateVerifier *
purple_certificate_find_verifier(const gchar *scheme_name, const gchar *ver_name);
/**
+ * purple_certificate_get_verifiers:
+ *
* Get the list of registered CertificateVerifiers
*
- * @return GList of all registered PurpleCertificateVerifier. This value
+ * Returns: GList of all registered PurpleCertificateVerifier. This value
* is owned by libpurple
*/
GList *
purple_certificate_get_verifiers(void);
/**
+ * purple_certificate_register_verifier:
+ * @vr: Verifier to register.
+ *
* Register a CertificateVerifier with libpurple
*
- * @param vr Verifier to register.
- * @return TRUE if register succeeded, otherwise FALSE
+ * Returns: TRUE if register succeeded, otherwise FALSE
*/
gboolean
purple_certificate_register_verifier(PurpleCertificateVerifier *vr);
/**
+ * purple_certificate_unregister_verifier:
+ * @vr: Verifier to unregister.
+ *
* Unregister a CertificateVerifier with libpurple
*
- * @param vr Verifier to unregister.
- * @return TRUE if unregister succeeded, otherwise FALSE
+ * Returns: TRUE if unregister succeeded, otherwise FALSE
*/
gboolean
purple_certificate_unregister_verifier(PurpleCertificateVerifier *vr);
-/** Look up a registered PurpleCertificatePool by scheme and name
- * @param scheme_name Scheme name. Case insensitive.
- * @param pool_name Pool name. Case insensitive.
- * @return Pointer to the located Pool, or NULL if it isn't found.
+/**
+ * purple_certificate_find_pool:
+ * @scheme_name: Scheme name. Case insensitive.
+ * @pool_name: Pool name. Case insensitive.
+ *
+ * Look up a registered PurpleCertificatePool by scheme and name
+ *
+ * Returns: Pointer to the located Pool, or NULL if it isn't found.
*/
PurpleCertificatePool *
purple_certificate_find_pool(const gchar *scheme_name, const gchar *pool_name);
/**
+ * purple_certificate_get_pools:
+ *
* Get the list of registered Pools
*
- * @return GList of all registered PurpleCertificatePool s. This value
+ * Returns: GList of all registered PurpleCertificatePool s. This value
* is owned by libpurple
*/
GList *
purple_certificate_get_pools(void);
/**
+ * purple_certificate_register_pool:
+ * @pool: Pool to register.
+ *
* Register a CertificatePool with libpurple and call its init function
*
- * @param pool Pool to register.
- * @return TRUE if the register succeeded, otherwise FALSE
+ * Returns: TRUE if the register succeeded, otherwise FALSE
*/
gboolean
purple_certificate_register_pool(PurpleCertificatePool *pool);
/**
+ * purple_certificate_unregister_pool:
+ * @pool: Pool to unregister.
+ *
* Unregister a CertificatePool with libpurple and call its uninit function
*
- * @param pool Pool to unregister.
- * @return TRUE if the unregister succeeded, otherwise FALSE
+ * Returns: TRUE if the unregister succeeded, otherwise FALSE
*/
gboolean
purple_certificate_unregister_pool(PurpleCertificatePool *pool);
-/*@}*/
-
-
/**
- * Displays a window showing X.509 certificate information
+ * purple_certificate_add_ca_search_path:
+ * @path: Path to search for certificates.
*
- * @param crt Certificate under an "x509" Scheme
- * @todo Will break on CA certs, as they have no Common Name
- */
-void
-purple_certificate_display_x509(PurpleCertificate *crt);
-
-/**
* Add a search path for certificates.
- *
- * @param path Path to search for certificates.
*/
void purple_certificate_add_ca_search_path(const char *path);
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
+G_END_DECLS
#endif /* _PURPLE_CERTIFICATE_H */
diff --git a/libpurple/cipher.c b/libpurple/cipher.c
index 57e5d697c1..c839d577d8 100644
--- a/libpurple/cipher.c
+++ b/libpurple/cipher.c
@@ -1,24 +1,9 @@
-/*
- * 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
* source distribution.
*
- * Original des taken from gpg
- *
- * des.c - DES and Triple-DES encryption/decryption Algorithm
- * Copyright (C) 1998 Free Software Foundation, Inc.
- *
- * Please see below for more legal information!
- *
- * According to the definition of DES in FIPS PUB 46-2 from December 1993.
- * For a description of triple encryption, see:
- * Bruce Schneier: Applied Cryptography. Second Edition.
- * John Wiley & Sons, 1996. ISBN 0-471-12845-7. Pages 358 ff.
- *
- * This file is part of GnuPG.
- *
* 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
@@ -35,807 +20,536 @@
*/
#include "internal.h"
#include "cipher.h"
-#include "dbus-maybe.h"
#include "debug.h"
-#include "signals.h"
-#include "value.h"
-
-/*******************************************************************************
- * Structs
- ******************************************************************************/
-struct _PurpleCipher {
- gchar *name; /**< Internal name - used for searching */
- PurpleCipherOps *ops; /**< Operations supported by this cipher */
- guint ref; /**< Reference count */
-};
-
-struct _PurpleCipherContext {
- PurpleCipher *cipher; /**< Cipher this context is under */
- gpointer data; /**< Internal cipher state data */
-};
-
-/******************************************************************************
- * Globals
- *****************************************************************************/
-static GList *ciphers = NULL;
/******************************************************************************
* PurpleCipher API
*****************************************************************************/
-const gchar *
-purple_cipher_get_name(PurpleCipher *cipher) {
- g_return_val_if_fail(cipher, NULL);
-
- return cipher->name;
-}
+GType
+purple_cipher_get_type(void) {
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleCipherClass),
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ sizeof(PurpleCipher),
+ 0,
+ NULL,
+ NULL
+ };
+
+ type = g_type_register_static(G_TYPE_OBJECT,
+ "PurpleCipher",
+ &info, G_TYPE_FLAG_ABSTRACT);
+ }
-guint
-purple_cipher_get_capabilities(PurpleCipher *cipher) {
- PurpleCipherOps *ops = NULL;
- guint caps = 0;
-
- g_return_val_if_fail(cipher, 0);
-
- ops = cipher->ops;
- g_return_val_if_fail(ops, 0);
-
- if(ops->set_option)
- caps |= PURPLE_CIPHER_CAPS_SET_OPT;
- if(ops->get_option)
- caps |= PURPLE_CIPHER_CAPS_GET_OPT;
- if(ops->init)
- caps |= PURPLE_CIPHER_CAPS_INIT;
- if(ops->reset)
- caps |= PURPLE_CIPHER_CAPS_RESET;
- if(ops->uninit)
- caps |= PURPLE_CIPHER_CAPS_UNINIT;
- if(ops->set_iv)
- caps |= PURPLE_CIPHER_CAPS_SET_IV;
- if(ops->append)
- caps |= PURPLE_CIPHER_CAPS_APPEND;
- if(ops->digest)
- caps |= PURPLE_CIPHER_CAPS_DIGEST;
- if(ops->encrypt)
- caps |= PURPLE_CIPHER_CAPS_ENCRYPT;
- if(ops->decrypt)
- caps |= PURPLE_CIPHER_CAPS_DECRYPT;
- if(ops->set_salt)
- caps |= PURPLE_CIPHER_CAPS_SET_SALT;
- if(ops->get_salt_size)
- caps |= PURPLE_CIPHER_CAPS_GET_SALT_SIZE;
- if(ops->set_key)
- caps |= PURPLE_CIPHER_CAPS_SET_KEY;
- if(ops->get_key_size)
- caps |= PURPLE_CIPHER_CAPS_GET_KEY_SIZE;
- if(ops->set_batch_mode)
- caps |= PURPLE_CIPHER_CAPS_SET_BATCH_MODE;
- if(ops->get_batch_mode)
- caps |= PURPLE_CIPHER_CAPS_GET_BATCH_MODE;
- if(ops->get_block_size)
- caps |= PURPLE_CIPHER_CAPS_GET_BLOCK_SIZE;
- if(ops->set_key_with_len)
- caps |= PURPLE_CIPHER_CAPS_SET_KEY_WITH_LEN;
-
- return caps;
+ return type;
}
-gboolean
-purple_cipher_digest_region(const gchar *name, const guchar *data,
- size_t data_len, size_t in_len,
- guchar digest[], size_t *out_len)
+static const gchar *
+purple_cipher_get_name(PurpleCipher *cipher)
{
- PurpleCipher *cipher;
- PurpleCipherContext *context;
- gboolean ret = FALSE;
-
- g_return_val_if_fail(name, FALSE);
- g_return_val_if_fail(data, FALSE);
-
- cipher = purple_ciphers_find_cipher(name);
+ PurpleCipherClass *klass;
+ const gchar *name;
- g_return_val_if_fail(cipher, FALSE);
+ if (!PURPLE_IS_CIPHER(cipher))
+ return "(error: not a cipher)";
- if(!cipher->ops->append || !cipher->ops->digest) {
- purple_debug_warning("cipher", "purple_cipher_region failed: "
- "the %s cipher does not support appending and or "
- "digesting.", cipher->name);
- return FALSE;
- }
+ klass = PURPLE_CIPHER_GET_CLASS(cipher);
+ if (!klass)
+ return "(error: unknown cipher class)";
- context = purple_cipher_context_new(cipher, NULL);
- purple_cipher_context_append(context, data, data_len);
- ret = purple_cipher_context_digest(context, in_len, digest, out_len);
- purple_cipher_context_destroy(context);
+ name = g_type_name(G_TYPE_FROM_CLASS(klass));
+ if (!name)
+ return "(error: unknown cipher name)";
- return ret;
+ return name;
}
-/******************************************************************************
- * PurpleCiphers API
- *****************************************************************************/
-PurpleCipher *
-purple_ciphers_find_cipher(const gchar *name) {
- PurpleCipher *cipher;
- GList *l;
+static const gchar *
+purple_hash_get_name(PurpleHash *hash)
+{
+ PurpleHashClass *klass;
+ const gchar *name;
- g_return_val_if_fail(name, NULL);
+ if (!PURPLE_IS_HASH(hash))
+ return "(error: not a hash)";
- for(l = ciphers; l; l = l->next) {
- cipher = PURPLE_CIPHER(l->data);
+ klass = PURPLE_HASH_GET_CLASS(hash);
+ if (!klass)
+ return "(error: unknown hash class)";
- if(!g_ascii_strcasecmp(cipher->name, name))
- return cipher;
- }
+ name = g_type_name(G_TYPE_FROM_CLASS(klass));
+ if (!name)
+ return "(error: unknown hash name)";
- return NULL;
+ return name;
}
-PurpleCipher *
-purple_ciphers_register_cipher(const gchar *name, PurpleCipherOps *ops) {
- PurpleCipher *cipher = NULL;
-
- g_return_val_if_fail(name, NULL);
- g_return_val_if_fail(ops, NULL);
- g_return_val_if_fail(!purple_ciphers_find_cipher(name), NULL);
-
- cipher = g_new0(PurpleCipher, 1);
- PURPLE_DBUS_REGISTER_POINTER(cipher, PurpleCipher);
-
- cipher->name = g_strdup(name);
- cipher->ops = ops;
+void
+purple_cipher_reset(PurpleCipher *cipher) {
+ PurpleCipherClass *klass = NULL;
- ciphers = g_list_append(ciphers, cipher);
+ g_return_if_fail(PURPLE_IS_CIPHER(cipher));
- purple_signal_emit(purple_ciphers_get_handle(), "cipher-added", cipher);
+ klass = PURPLE_CIPHER_GET_CLASS(cipher);
- return cipher;
+ if (klass && klass->reset)
+ klass->reset(cipher);
+ else {
+ purple_debug_warning("cipher", "the %s cipher does not "
+ "implement the reset method",
+ purple_cipher_get_name(cipher));
+ }
}
-gboolean
-purple_ciphers_unregister_cipher(PurpleCipher *cipher) {
- g_return_val_if_fail(cipher, FALSE);
- g_return_val_if_fail(cipher->ref == 0, FALSE);
-
- purple_signal_emit(purple_ciphers_get_handle(), "cipher-removed", cipher);
-
- ciphers = g_list_remove(ciphers, cipher);
-
- g_free(cipher->name);
-
- PURPLE_DBUS_UNREGISTER_POINTER(cipher);
- g_free(cipher);
-
- return TRUE;
-}
+void
+purple_cipher_reset_state(PurpleCipher *cipher) {
+ PurpleCipherClass *klass = NULL;
-GList *
-purple_ciphers_get_ciphers() {
- return ciphers;
-}
+ g_return_if_fail(PURPLE_IS_CIPHER(cipher));
-/******************************************************************************
- * PurpleCipher Subsystem API
- *****************************************************************************/
-gpointer
-purple_ciphers_get_handle() {
- static gint handle;
+ klass = PURPLE_CIPHER_GET_CLASS(cipher);
- return &handle;
+ if (klass && klass->reset_state)
+ klass->reset_state(cipher);
+ else {
+ purple_debug_warning("cipher", "the %s cipher does not "
+ "implement the reset_state method",
+ purple_cipher_get_name(cipher));
+ }
}
-/* These are implemented in the purple-ciphers sublibrary built in the ciphers
- * directory. We could put a header file in there, but it's less hassle for
- * the developer to just add it here since they have to register it here as
- * well.
- */
-PurpleCipherOps *purple_des_cipher_get_ops();
-PurpleCipherOps *purple_des3_cipher_get_ops();
-PurpleCipherOps *purple_hmac_cipher_get_ops();
-PurpleCipherOps *purple_md4_cipher_get_ops();
-PurpleCipherOps *purple_md5_cipher_get_ops();
-PurpleCipherOps *purple_rc4_cipher_get_ops();
-PurpleCipherOps *purple_sha1_cipher_get_ops();
-PurpleCipherOps *purple_sha256_cipher_get_ops();
-
void
-purple_ciphers_init() {
- gpointer handle;
-
- handle = purple_ciphers_get_handle();
-
- purple_signal_register(handle, "cipher-added",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CIPHER));
- purple_signal_register(handle, "cipher-removed",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CIPHER));
-
- purple_ciphers_register_cipher("md5", purple_md5_cipher_get_ops());
- purple_ciphers_register_cipher("sha1", purple_sha1_cipher_get_ops());
- purple_ciphers_register_cipher("sha256", purple_sha256_cipher_get_ops());
- purple_ciphers_register_cipher("md4", purple_md4_cipher_get_ops());
- purple_ciphers_register_cipher("hmac", purple_hmac_cipher_get_ops());
- purple_ciphers_register_cipher("des", purple_des_cipher_get_ops());
- purple_ciphers_register_cipher("des3", purple_des3_cipher_get_ops());
- purple_ciphers_register_cipher("rc4", purple_rc4_cipher_get_ops());
-}
+purple_cipher_set_iv(PurpleCipher *cipher, guchar *iv, size_t len)
+{
+ PurpleCipherClass *klass = NULL;
-void
-purple_ciphers_uninit() {
- PurpleCipher *cipher;
- GList *l, *ll;
+ g_return_if_fail(PURPLE_IS_CIPHER(cipher));
+ g_return_if_fail(iv);
- for(l = ciphers; l; l = ll) {
- ll = l->next;
+ klass = PURPLE_CIPHER_GET_CLASS(cipher);
- cipher = PURPLE_CIPHER(l->data);
- purple_ciphers_unregister_cipher(cipher);
+ if (klass && klass->set_iv)
+ klass->set_iv(cipher, iv, len);
+ else {
+ purple_debug_warning("cipher", "the %s cipher does not "
+ "implement the set_iv method",
+ purple_cipher_get_name(cipher));
}
-
- g_list_free(ciphers);
-
- purple_signals_unregister_by_instance(purple_ciphers_get_handle());
}
-/******************************************************************************
- * PurpleCipherContext API
- *****************************************************************************/
void
-purple_cipher_context_set_option(PurpleCipherContext *context, const gchar *name,
- gpointer value)
+purple_cipher_append(PurpleCipher *cipher, const guchar *data,
+ size_t len)
{
- PurpleCipher *cipher = NULL;
+ PurpleCipherClass *klass = NULL;
- g_return_if_fail(context);
- g_return_if_fail(name);
+ g_return_if_fail(PURPLE_IS_CIPHER(cipher));
- cipher = context->cipher;
- g_return_if_fail(cipher);
+ klass = PURPLE_CIPHER_GET_CLASS(cipher);
- if(cipher->ops && cipher->ops->set_option)
- cipher->ops->set_option(context, name, value);
- else
- purple_debug_warning("cipher", "the %s cipher does not support the "
- "set_option operation\n", cipher->name);
+ if (klass && klass->append)
+ klass->append(cipher, data, len);
+ else {
+ purple_debug_warning("cipher", "the %s cipher does not "
+ "implement the append method",
+ purple_cipher_get_name(cipher));
+ }
}
-gpointer
-purple_cipher_context_get_option(PurpleCipherContext *context, const gchar *name) {
- PurpleCipher *cipher = NULL;
+gboolean
+purple_cipher_digest(PurpleCipher *cipher, guchar digest[], size_t len)
+{
+ PurpleCipherClass *klass = NULL;
- g_return_val_if_fail(context, NULL);
- g_return_val_if_fail(name, NULL);
+ g_return_val_if_fail(PURPLE_IS_CIPHER(cipher), FALSE);
- cipher = context->cipher;
- g_return_val_if_fail(cipher, NULL);
+ klass = PURPLE_CIPHER_GET_CLASS(cipher);
- if(cipher->ops && cipher->ops->get_option)
- return cipher->ops->get_option(context, name);
+ if (klass && klass->digest)
+ return klass->digest(cipher, digest, len);
else {
- purple_debug_warning("cipher", "the %s cipher does not support the "
- "get_option operation\n", cipher->name);
-
- return NULL;
+ purple_debug_warning("cipher", "the %s cipher does not "
+ "implement the digest method",
+ purple_cipher_get_name(cipher));
}
-}
-
-PurpleCipherContext *
-purple_cipher_context_new(PurpleCipher *cipher, void *extra) {
- PurpleCipherContext *context = NULL;
- g_return_val_if_fail(cipher, NULL);
+ return FALSE;
+}
- cipher->ref++;
+gboolean
+purple_cipher_digest_to_str(PurpleCipher *cipher, gchar digest_s[], size_t len)
+{
+ /* 8k is a bit excessive, will tweak later. */
+ guchar digest[BUF_LEN * 4];
+ size_t digest_size, n;
- context = g_new0(PurpleCipherContext, 1);
- context->cipher = cipher;
+ g_return_val_if_fail(PURPLE_IS_CIPHER(cipher), FALSE);
+ g_return_val_if_fail(digest_s, FALSE);
- if(cipher->ops->init)
- cipher->ops->init(context, extra);
+ digest_size = purple_cipher_get_digest_size(cipher);
- return context;
-}
+ g_return_val_if_fail(digest_size <= BUF_LEN * 4, FALSE);
-PurpleCipherContext *
-purple_cipher_context_new_by_name(const gchar *name, void *extra) {
- PurpleCipher *cipher;
+ if(!purple_cipher_digest(cipher, digest, sizeof(digest)))
+ return FALSE;
- g_return_val_if_fail(name, NULL);
+ /* Every digest byte occupies 2 chars + the NUL at the end. */
+ g_return_val_if_fail(digest_size * 2 + 1 <= len, FALSE);
- cipher = purple_ciphers_find_cipher(name);
+ for(n = 0; n < digest_size; n++)
+ sprintf(digest_s + (n * 2), "%02x", digest[n]);
- g_return_val_if_fail(cipher, NULL);
+ digest_s[n * 2] = '\0';
- return purple_cipher_context_new(cipher, extra);
+ return TRUE;
}
-void
-purple_cipher_context_reset(PurpleCipherContext *context, void *extra) {
- PurpleCipher *cipher = NULL;
+size_t
+purple_cipher_get_digest_size(PurpleCipher *cipher)
+{
+ PurpleCipherClass *klass = NULL;
- g_return_if_fail(context);
+ g_return_val_if_fail(PURPLE_IS_CIPHER(cipher), FALSE);
- cipher = context->cipher;
- g_return_if_fail(cipher);
+ klass = PURPLE_CIPHER_GET_CLASS(cipher);
- if(cipher->ops && cipher->ops->reset)
- context->cipher->ops->reset(context, extra);
-}
+ if (klass && klass->get_digest_size)
+ return klass->get_digest_size(cipher);
+ else {
+ purple_debug_warning("cipher", "the %s cipher does not "
+ "implement the get_digest_size method",
+ purple_cipher_get_name(cipher));
+ }
-void
-purple_cipher_context_destroy(PurpleCipherContext *context) {
- PurpleCipher *cipher = NULL;
+ return FALSE;
+}
- g_return_if_fail(context);
+ssize_t
+purple_cipher_encrypt(PurpleCipher *cipher, const guchar input[],
+ size_t in_len, guchar output[], size_t out_size)
+{
+ PurpleCipherClass *klass = NULL;
- cipher = context->cipher;
- g_return_if_fail(cipher);
+ g_return_val_if_fail(PURPLE_IS_CIPHER(cipher), -1);
+ g_return_val_if_fail(input != NULL, -1);
+ g_return_val_if_fail(output != NULL, -1);
+ g_return_val_if_fail(out_size >= in_len, -1);
- cipher->ref--;
+ klass = PURPLE_CIPHER_GET_CLASS(cipher);
- if(cipher->ops && cipher->ops->uninit)
- cipher->ops->uninit(context);
+ if (klass && klass->encrypt)
+ return klass->encrypt(cipher, input, in_len, output, out_size);
+ else {
+ purple_debug_warning("cipher", "the %s cipher does not "
+ "implement the encrypt method",
+ purple_cipher_get_name(cipher));
+ }
- memset(context, 0, sizeof(*context));
- g_free(context);
- context = NULL;
+ return -1;
}
-void
-purple_cipher_context_set_iv(PurpleCipherContext *context, guchar *iv, size_t len)
+ssize_t
+purple_cipher_decrypt(PurpleCipher *cipher, const guchar input[],
+ size_t in_len, guchar output[], size_t out_size)
{
- PurpleCipher *cipher = NULL;
+ PurpleCipherClass *klass = NULL;
- g_return_if_fail(context);
- g_return_if_fail(iv);
+ g_return_val_if_fail(PURPLE_IS_CIPHER(cipher), -1);
+ g_return_val_if_fail(input != NULL, -1);
+ g_return_val_if_fail(output != NULL, -1);
- cipher = context->cipher;
- g_return_if_fail(cipher);
+ klass = PURPLE_CIPHER_GET_CLASS(cipher);
- if(cipher->ops && cipher->ops->set_iv)
- cipher->ops->set_iv(context, iv, len);
- else
- purple_debug_warning("cipher", "the %s cipher does not support the set"
- "initialization vector operation\n", cipher->name);
+ if (klass && klass->decrypt)
+ return klass->decrypt(cipher, input, in_len, output, out_size);
+ else {
+ purple_debug_warning("cipher", "the %s cipher does not "
+ "implement the decrypt method",
+ purple_cipher_get_name(cipher));
+ }
+
+ return -1;
}
void
-purple_cipher_context_append(PurpleCipherContext *context, const guchar *data,
- size_t len)
-{
- PurpleCipher *cipher = NULL;
+purple_cipher_set_salt(PurpleCipher *cipher, const guchar *salt, size_t len) {
+ PurpleCipherClass *klass = NULL;
- g_return_if_fail(context);
+ g_return_if_fail(PURPLE_IS_CIPHER(cipher));
- cipher = context->cipher;
- g_return_if_fail(cipher);
+ klass = PURPLE_CIPHER_GET_CLASS(cipher);
- if(cipher->ops && cipher->ops->append)
- cipher->ops->append(context, data, len);
- else
- purple_debug_warning("cipher", "the %s cipher does not support the append "
- "operation\n", cipher->name);
+ if (klass && klass->set_salt)
+ klass->set_salt(cipher, salt, len);
+ else {
+ purple_debug_warning("cipher", "the %s cipher does not "
+ "implement the set_salt method",
+ purple_cipher_get_name(cipher));
+ }
}
-gboolean
-purple_cipher_context_digest(PurpleCipherContext *context, size_t in_len,
- guchar digest[], size_t *out_len)
-{
- PurpleCipher *cipher = NULL;
+void
+purple_cipher_set_key(PurpleCipher *cipher, const guchar *key, size_t len) {
+ PurpleCipherClass *klass = NULL;
- g_return_val_if_fail(context, FALSE);
+ g_return_if_fail(PURPLE_IS_CIPHER(cipher));
- cipher = context->cipher;
+ klass = PURPLE_CIPHER_GET_CLASS(cipher);
- if(cipher->ops && cipher->ops->digest)
- return cipher->ops->digest(context, in_len, digest, out_len);
+ if (klass && klass->set_key)
+ klass->set_key(cipher, key, len);
else {
- purple_debug_warning("cipher", "the %s cipher does not support the digest "
- "operation\n", cipher->name);
- return FALSE;
+ purple_debug_warning("cipher", "the %s cipher does not "
+ "implement the set_key method",
+ purple_cipher_get_name(cipher));
}
}
-gboolean
-purple_cipher_context_digest_to_str(PurpleCipherContext *context, size_t in_len,
- gchar digest_s[], size_t *out_len)
-{
- /* 8k is a bit excessive, will tweak later. */
- guchar digest[BUF_LEN * 4];
- gint n = 0;
- size_t dlen = 0;
-
- g_return_val_if_fail(context, FALSE);
- g_return_val_if_fail(digest_s, FALSE);
-
- if(!purple_cipher_context_digest(context, sizeof(digest), digest, &dlen))
- return FALSE;
-
- /* in_len must be greater than dlen * 2 so we have room for the NUL. */
- if(in_len <= dlen * 2)
- return FALSE;
+size_t
+purple_cipher_get_key_size(PurpleCipher *cipher) {
+ PurpleCipherClass *klass = NULL;
- for(n = 0; n < dlen; n++)
- sprintf(digest_s + (n * 2), "%02x", digest[n]);
+ g_return_val_if_fail(PURPLE_IS_CIPHER(cipher), -1);
- digest_s[n * 2] = '\0';
+ klass = PURPLE_CIPHER_GET_CLASS(cipher);
- if(out_len)
- *out_len = dlen * 2;
+ if (klass && klass->get_key_size)
+ return klass->get_key_size(cipher);
+ else {
+ purple_debug_warning("cipher", "the %s cipher does not "
+ "implement the get_key_size method",
+ purple_cipher_get_name(cipher));
+ }
- return TRUE;
+ return -1;
}
-gint
-purple_cipher_context_encrypt(PurpleCipherContext *context, const guchar data[],
- size_t len, guchar output[], size_t *outlen)
+void
+purple_cipher_set_batch_mode(PurpleCipher *cipher,
+ PurpleCipherBatchMode mode)
{
- PurpleCipher *cipher = NULL;
+ PurpleCipherClass *klass = NULL;
- g_return_val_if_fail(context, -1);
+ g_return_if_fail(PURPLE_IS_CIPHER(cipher));
- cipher = context->cipher;
- g_return_val_if_fail(cipher, -1);
+ klass = PURPLE_CIPHER_GET_CLASS(cipher);
- if(cipher->ops && cipher->ops->encrypt)
- return cipher->ops->encrypt(context, data, len, output, outlen);
+ if (klass && klass->set_batch_mode)
+ klass->set_batch_mode(cipher, mode);
else {
- purple_debug_warning("cipher", "the %s cipher does not support the encrypt"
- "operation\n", cipher->name);
-
- if(outlen)
- *outlen = -1;
-
- return -1;
+ purple_debug_warning("cipher", "the %s cipher does not "
+ "implement the set_batch_mode method",
+ purple_cipher_get_name(cipher));
}
}
-gint
-purple_cipher_context_decrypt(PurpleCipherContext *context, const guchar data[],
- size_t len, guchar output[], size_t *outlen)
+PurpleCipherBatchMode
+purple_cipher_get_batch_mode(PurpleCipher *cipher)
{
- PurpleCipher *cipher = NULL;
+ PurpleCipherClass *klass = NULL;
- g_return_val_if_fail(context, -1);
+ g_return_val_if_fail(PURPLE_IS_CIPHER(cipher), -1);
- cipher = context->cipher;
- g_return_val_if_fail(cipher, -1);
+ klass = PURPLE_CIPHER_GET_CLASS(cipher);
- if(cipher->ops && cipher->ops->decrypt)
- return cipher->ops->decrypt(context, data, len, output, outlen);
+ if (klass && klass->get_batch_mode)
+ return klass->get_batch_mode(cipher);
else {
- purple_debug_warning("cipher", "the %s cipher does not support the decrypt"
- "operation\n", cipher->name);
-
- if(outlen)
- *outlen = -1;
-
- return -1;
+ purple_debug_warning("cipher", "the %s cipher does not "
+ "implement the get_batch_mode method",
+ purple_cipher_get_name(cipher));
}
-}
-
-void
-purple_cipher_context_set_salt(PurpleCipherContext *context, guchar *salt) {
- PurpleCipher *cipher = NULL;
-
- g_return_if_fail(context);
-
- cipher = context->cipher;
- g_return_if_fail(cipher);
- if(cipher->ops && cipher->ops->set_salt)
- cipher->ops->set_salt(context, salt);
- else
- purple_debug_warning("cipher", "the %s cipher does not support the "
- "set_salt operation\n", cipher->name);
+ return -1;
}
size_t
-purple_cipher_context_get_salt_size(PurpleCipherContext *context) {
- PurpleCipher *cipher = NULL;
+purple_cipher_get_block_size(PurpleCipher *cipher)
+{
+ PurpleCipherClass *klass = NULL;
- g_return_val_if_fail(context, -1);
+ g_return_val_if_fail(PURPLE_IS_CIPHER(cipher), -1);
- cipher = context->cipher;
- g_return_val_if_fail(cipher, -1);
+ klass = PURPLE_CIPHER_GET_CLASS(cipher);
- if(cipher->ops && cipher->ops->get_salt_size)
- return cipher->ops->get_salt_size(context);
+ if (klass && klass->get_block_size)
+ return klass->get_block_size(cipher);
else {
- purple_debug_warning("cipher", "the %s cipher does not support the "
- "get_salt_size operation\n", cipher->name);
-
- return -1;
+ purple_debug_warning("cipher", "the %s cipher does not "
+ "implement the get_block_size method",
+ purple_cipher_get_name(cipher));
}
-}
-
-void
-purple_cipher_context_set_key(PurpleCipherContext *context, const guchar *key) {
- PurpleCipher *cipher = NULL;
- g_return_if_fail(context);
+ return -1;
+}
- cipher = context->cipher;
- g_return_if_fail(cipher);
+/******************************************************************************
+ * PurpleHash API
+ *****************************************************************************/
+GType
+purple_hash_get_type(void) {
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleHashClass),
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ sizeof(PurpleHash),
+ 0,
+ NULL,
+ NULL
+ };
+
+ type = g_type_register_static(G_TYPE_OBJECT,
+ "PurpleHash",
+ &info, G_TYPE_FLAG_ABSTRACT);
+ }
- if(cipher->ops && cipher->ops->set_key)
- cipher->ops->set_key(context, key);
- else
- purple_debug_warning("cipher", "the %s cipher does not support the "
- "set_key operation\n", cipher->name);
+ return type;
}
-size_t
-purple_cipher_context_get_key_size(PurpleCipherContext *context) {
- PurpleCipher *cipher = NULL;
+void
+purple_hash_reset(PurpleHash *hash) {
+ PurpleHashClass *klass = NULL;
- g_return_val_if_fail(context, -1);
+ g_return_if_fail(PURPLE_IS_HASH(hash));
- cipher = context->cipher;
- g_return_val_if_fail(cipher, -1);
+ klass = PURPLE_HASH_GET_CLASS(hash);
- if(cipher->ops && cipher->ops->get_key_size)
- return cipher->ops->get_key_size(context);
+ if (klass && klass->reset)
+ klass->reset(hash);
else {
- purple_debug_warning("cipher", "the %s cipher does not support the "
- "get_key_size operation\n", cipher->name);
-
- return -1;
+ purple_debug_warning("hash", "the %s hash does not implement "
+ "the reset method", purple_hash_get_name(hash));
}
}
void
-purple_cipher_context_set_batch_mode(PurpleCipherContext *context,
- PurpleCipherBatchMode mode)
-{
- PurpleCipher *cipher = NULL;
+purple_hash_reset_state(PurpleHash *hash) {
+ PurpleHashClass *klass = NULL;
- g_return_if_fail(context);
+ g_return_if_fail(PURPLE_IS_HASH(hash));
- cipher = context->cipher;
- g_return_if_fail(cipher);
+ klass = PURPLE_HASH_GET_CLASS(hash);
- if(cipher->ops && cipher->ops->set_batch_mode)
- cipher->ops->set_batch_mode(context, mode);
- else
- purple_debug_warning("cipher", "The %s cipher does not support the "
- "set_batch_mode operation\n", cipher->name);
+ if (klass && klass->reset_state)
+ klass->reset_state(hash);
+ else {
+ purple_debug_warning("hash", "the %s hash does not implement "
+ "the reset_state method", purple_hash_get_name(hash));
+ }
}
-PurpleCipherBatchMode
-purple_cipher_context_get_batch_mode(PurpleCipherContext *context)
+void
+purple_hash_append(PurpleHash *hash, const guchar *data,
+ size_t len)
{
- PurpleCipher *cipher = NULL;
+ PurpleHashClass *klass = NULL;
- g_return_val_if_fail(context, -1);
+ g_return_if_fail(PURPLE_IS_HASH(hash));
- cipher = context->cipher;
- g_return_val_if_fail(cipher, -1);
+ klass = PURPLE_HASH_GET_CLASS(hash);
- if(cipher->ops && cipher->ops->get_batch_mode)
- return cipher->ops->get_batch_mode(context);
+ if (klass && klass->append)
+ klass->append(hash, data, len);
else {
- purple_debug_warning("cipher", "The %s cipher does not support the "
- "get_batch_mode operation\n", cipher->name);
- return -1;
+ purple_debug_warning("hash", "the %s hash does not implement "
+ "the append method", purple_hash_get_name(hash));
}
}
-size_t
-purple_cipher_context_get_block_size(PurpleCipherContext *context)
+gboolean
+purple_hash_digest(PurpleHash *hash, guchar digest[], size_t len)
{
- PurpleCipher *cipher = NULL;
+ PurpleHashClass *klass = NULL;
- g_return_val_if_fail(context, -1);
+ g_return_val_if_fail(PURPLE_IS_HASH(hash), FALSE);
- cipher = context->cipher;
- g_return_val_if_fail(cipher, -1);
+ klass = PURPLE_HASH_GET_CLASS(hash);
- if(cipher->ops && cipher->ops->get_block_size)
- return cipher->ops->get_block_size(context);
+ if (klass && klass->digest)
+ return klass->digest(hash, digest, len);
else {
- purple_debug_warning("cipher", "The %s cipher does not support the "
- "get_block_size operation\n", cipher->name);
- return -1;
+ purple_debug_warning("hash", "the %s hash does not implement "
+ "the digest method", purple_hash_get_name(hash));
}
+
+ return FALSE;
}
-void
-purple_cipher_context_set_key_with_len(PurpleCipherContext *context,
- const guchar *key, size_t len)
+gboolean
+purple_hash_digest_to_str(PurpleHash *hash, gchar digest_s[], size_t len)
{
- PurpleCipher *cipher = NULL;
+ /* 8k is a bit excessive, will tweak later. */
+ guchar digest[BUF_LEN * 4];
+ size_t digest_size, n;
- g_return_if_fail(context);
+ g_return_val_if_fail(PURPLE_IS_HASH(hash), FALSE);
+ g_return_val_if_fail(digest_s, FALSE);
- cipher = context->cipher;
- g_return_if_fail(cipher);
+ digest_size = purple_hash_get_digest_size(hash);
- if(cipher->ops && cipher->ops->set_key_with_len)
- cipher->ops->set_key_with_len(context, key, len);
- else
- purple_debug_warning("cipher", "The %s cipher does not support the "
- "set_key_with_len operation\n", cipher->name);
-}
+ g_return_val_if_fail(digest_size <= BUF_LEN * 4, FALSE);
-void
-purple_cipher_context_set_data(PurpleCipherContext *context, gpointer data) {
- g_return_if_fail(context);
+ if(!purple_hash_digest(hash, digest, sizeof(digest)))
+ return FALSE;
- context->data = data;
-}
+ /* Every digest byte occupies 2 chars + the NUL at the end. */
+ g_return_val_if_fail(digest_size * 2 + 1 <= len, FALSE);
-gpointer
-purple_cipher_context_get_data(PurpleCipherContext *context) {
- g_return_val_if_fail(context, NULL);
+ for(n = 0; n < digest_size; n++)
+ sprintf(digest_s + (n * 2), "%02x", digest[n]);
- return context->data;
+ digest_s[n * 2] = '\0';
+
+ return TRUE;
}
-gchar *purple_cipher_http_digest_calculate_session_key(
- const gchar *algorithm,
- const gchar *username,
- const gchar *realm,
- const gchar *password,
- const gchar *nonce,
- const gchar *client_nonce)
+size_t
+purple_hash_get_digest_size(PurpleHash *hash)
{
- PurpleCipher *cipher;
- PurpleCipherContext *context;
- gchar hash[33]; /* We only support MD5. */
-
- g_return_val_if_fail(username != NULL, NULL);
- g_return_val_if_fail(realm != NULL, NULL);
- g_return_val_if_fail(password != NULL, NULL);
- g_return_val_if_fail(nonce != NULL, NULL);
-
- /* Check for a supported algorithm. */
- g_return_val_if_fail(algorithm == NULL ||
- *algorithm == '\0' ||
- g_ascii_strcasecmp(algorithm, "MD5") ||
- g_ascii_strcasecmp(algorithm, "MD5-sess"), NULL);
-
- cipher = purple_ciphers_find_cipher("md5");
- g_return_val_if_fail(cipher != NULL, NULL);
-
- context = purple_cipher_context_new(cipher, NULL);
-
- purple_cipher_context_append(context, (guchar *)username, strlen(username));
- purple_cipher_context_append(context, (guchar *)":", 1);
- purple_cipher_context_append(context, (guchar *)realm, strlen(realm));
- purple_cipher_context_append(context, (guchar *)":", 1);
- purple_cipher_context_append(context, (guchar *)password, strlen(password));
-
- if (algorithm != NULL && !g_ascii_strcasecmp(algorithm, "MD5-sess"))
- {
- guchar digest[16];
-
- if (client_nonce == NULL)
- {
- purple_cipher_context_destroy(context);
- purple_debug_error("cipher", "Required client_nonce missing for MD5-sess digest calculation.\n");
- return NULL;
- }
-
- purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
- purple_cipher_context_destroy(context);
-
- context = purple_cipher_context_new(cipher, NULL);
- purple_cipher_context_append(context, digest, sizeof(digest));
- purple_cipher_context_append(context, (guchar *)":", 1);
- purple_cipher_context_append(context, (guchar *)nonce, strlen(nonce));
- purple_cipher_context_append(context, (guchar *)":", 1);
- purple_cipher_context_append(context, (guchar *)client_nonce, strlen(client_nonce));
- }
+ PurpleHashClass *klass = NULL;
- purple_cipher_context_digest_to_str(context, sizeof(hash), hash, NULL);
- purple_cipher_context_destroy(context);
+ g_return_val_if_fail(PURPLE_IS_HASH(hash), FALSE);
- return g_strdup(hash);
+ klass = PURPLE_HASH_GET_CLASS(hash);
+
+ if (klass && klass->get_digest_size)
+ return klass->get_digest_size(hash);
+ else {
+ purple_debug_warning("hash", "the %s hash does not implement "
+ "the get_digest_size method", purple_hash_get_name(hash));
+ }
+
+ return FALSE;
}
-gchar *purple_cipher_http_digest_calculate_response(
- const gchar *algorithm,
- const gchar *method,
- const gchar *digest_uri,
- const gchar *qop,
- const gchar *entity,
- const gchar *nonce,
- const gchar *nonce_count,
- const gchar *client_nonce,
- const gchar *session_key)
+size_t
+purple_hash_get_block_size(PurpleHash *hash)
{
- PurpleCipher *cipher;
- PurpleCipherContext *context;
- static gchar hash2[33]; /* We only support MD5. */
-
- g_return_val_if_fail(method != NULL, NULL);
- g_return_val_if_fail(digest_uri != NULL, NULL);
- g_return_val_if_fail(nonce != NULL, NULL);
- g_return_val_if_fail(session_key != NULL, NULL);
-
- /* Check for a supported algorithm. */
- g_return_val_if_fail(algorithm == NULL ||
- *algorithm == '\0' ||
- g_ascii_strcasecmp(algorithm, "MD5") ||
- g_ascii_strcasecmp(algorithm, "MD5-sess"), NULL);
-
- /* Check for a supported "quality of protection". */
- g_return_val_if_fail(qop == NULL ||
- *qop == '\0' ||
- g_ascii_strcasecmp(qop, "auth") ||
- g_ascii_strcasecmp(qop, "auth-int"), NULL);
-
- cipher = purple_ciphers_find_cipher("md5");
- g_return_val_if_fail(cipher != NULL, NULL);
-
- context = purple_cipher_context_new(cipher, NULL);
-
- purple_cipher_context_append(context, (guchar *)method, strlen(method));
- purple_cipher_context_append(context, (guchar *)":", 1);
- purple_cipher_context_append(context, (guchar *)digest_uri, strlen(digest_uri));
-
- if (qop != NULL && !g_ascii_strcasecmp(qop, "auth-int"))
- {
- PurpleCipherContext *context2;
- gchar entity_hash[33];
-
- if (entity == NULL)
- {
- purple_cipher_context_destroy(context);
- purple_debug_error("cipher", "Required entity missing for auth-int digest calculation.\n");
- return NULL;
- }
-
- context2 = purple_cipher_context_new(cipher, NULL);
- purple_cipher_context_append(context2, (guchar *)entity, strlen(entity));
- purple_cipher_context_digest_to_str(context2, sizeof(entity_hash), entity_hash, NULL);
- purple_cipher_context_destroy(context2);
-
- purple_cipher_context_append(context, (guchar *)":", 1);
- purple_cipher_context_append(context, (guchar *)entity_hash, strlen(entity_hash));
- }
+ PurpleHashClass *klass = NULL;
- purple_cipher_context_digest_to_str(context, sizeof(hash2), hash2, NULL);
- purple_cipher_context_destroy(context);
-
- context = purple_cipher_context_new(cipher, NULL);
- purple_cipher_context_append(context, (guchar *)session_key, strlen(session_key));
- purple_cipher_context_append(context, (guchar *)":", 1);
- purple_cipher_context_append(context, (guchar *)nonce, strlen(nonce));
- purple_cipher_context_append(context, (guchar *)":", 1);
-
- if (qop != NULL && *qop != '\0')
- {
- if (nonce_count == NULL)
- {
- purple_cipher_context_destroy(context);
- purple_debug_error("cipher", "Required nonce_count missing for digest calculation.\n");
- return NULL;
- }
-
- if (client_nonce == NULL)
- {
- purple_cipher_context_destroy(context);
- purple_debug_error("cipher", "Required client_nonce missing for digest calculation.\n");
- return NULL;
- }
-
- purple_cipher_context_append(context, (guchar *)nonce_count, strlen(nonce_count));
- purple_cipher_context_append(context, (guchar *)":", 1);
- purple_cipher_context_append(context, (guchar *)client_nonce, strlen(client_nonce));
- purple_cipher_context_append(context, (guchar *)":", 1);
-
- purple_cipher_context_append(context, (guchar *)qop, strlen(qop));
-
- purple_cipher_context_append(context, (guchar *)":", 1);
- }
+ g_return_val_if_fail(PURPLE_IS_HASH(hash), -1);
+
+ klass = PURPLE_HASH_GET_CLASS(hash);
- purple_cipher_context_append(context, (guchar *)hash2, strlen(hash2));
- purple_cipher_context_digest_to_str(context, sizeof(hash2), hash2, NULL);
- purple_cipher_context_destroy(context);
+ if (klass && klass->get_block_size)
+ return klass->get_block_size(hash);
+ else {
+ purple_debug_warning("hash", "the %s hash does not implement "
+ "the get_block_size method", purple_hash_get_name(hash));
+ }
- return g_strdup(hash2);
+ return -1;
}
diff --git a/libpurple/cipher.h b/libpurple/cipher.h
index b6479c69db..2ed7ad7ba0 100644
--- a/libpurple/cipher.h
+++ b/libpurple/cipher.h
@@ -1,9 +1,3 @@
-/**
- * @file cipher.h Purple Cipher API
- * @ingroup core
- * @see @ref cipher-signals
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -24,478 +18,413 @@
* 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_CIPHER_H
#define PURPLE_CIPHER_H
+/**
+ * SECTION:cipher
+ * @section_id: libpurple-cipher
+ * @short_description: <filename>cipher.h</filename>
+ * @title: Cipher and Hash API
+ */
#include <glib.h>
+#include <glib-object.h>
#include <string.h>
-#define PURPLE_CIPHER(obj) ((PurpleCipher *)(obj)) /**< PurpleCipher typecast helper */
-#define PURPLE_CIPHER_OPS(obj) ((PurpleCipherOps *)(obj)) /**< PurpleCipherInfo typecase helper */
-#define PURPLE_CIPHER_CONTEXT(obj) ((PurpleCipherContext *)(obj)) /**< PurpleCipherContext typecast helper */
+#define PURPLE_TYPE_CIPHER (purple_cipher_get_type())
+#define PURPLE_CIPHER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_CIPHER, PurpleCipher))
+#define PURPLE_CIPHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_CIPHER, PurpleCipherClass))
+#define PURPLE_IS_CIPHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_CIPHER))
+#define PURPLE_IS_CIPHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_CIPHER))
+#define PURPLE_CIPHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_CIPHER, PurpleCipherClass))
-typedef struct _PurpleCipher PurpleCipher; /**< A handle to a PurpleCipher */
-typedef struct _PurpleCipherOps PurpleCipherOps; /**< Ops for a PurpleCipher */
-typedef struct _PurpleCipherContext PurpleCipherContext; /**< A context for a PurpleCipher */
+typedef struct _PurpleCipher PurpleCipher;
+typedef struct _PurpleCipherClass PurpleCipherClass;
+
+#define PURPLE_TYPE_HASH (purple_hash_get_type())
+#define PURPLE_HASH(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_HASH, PurpleHash))
+#define PURPLE_HASH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_HASH, PurpleHashClass))
+#define PURPLE_IS_HASH(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_HASH))
+#define PURPLE_IS_HASH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_HASH))
+#define PURPLE_HASH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_HASH, PurpleHashClass))
+
+typedef struct _PurpleHash PurpleHash;
+typedef struct _PurpleHashClass PurpleHashClass;
/**
+ * PurpleCipherBatchMode:
+ * @PURPLE_CIPHER_BATCH_MODE_ECB: Electronic Codebook Mode
+ * @PURPLE_CIPHER_BATCH_MODE_CBC: Cipher Block Chaining Mode
+ *
* Modes for batch encrypters
*/
-typedef enum _PurpleCipherBatchMode {
+typedef enum {
PURPLE_CIPHER_BATCH_MODE_ECB,
PURPLE_CIPHER_BATCH_MODE_CBC
} PurpleCipherBatchMode;
/**
- * The operation flags for a cipher
+ * PurpleCipher:
+ *
+ * Purple Cipher is an opaque data structure and should not be used directly.
*/
-typedef enum _PurpleCipherCaps {
- PURPLE_CIPHER_CAPS_SET_OPT = 1 << 1, /**< Set option flag */
- PURPLE_CIPHER_CAPS_GET_OPT = 1 << 2, /**< Get option flag */
- PURPLE_CIPHER_CAPS_INIT = 1 << 3, /**< Init flag */
- PURPLE_CIPHER_CAPS_RESET = 1 << 4, /**< Reset flag */
- PURPLE_CIPHER_CAPS_UNINIT = 1 << 5, /**< Uninit flag */
- PURPLE_CIPHER_CAPS_SET_IV = 1 << 6, /**< Set IV flag */
- PURPLE_CIPHER_CAPS_APPEND = 1 << 7, /**< Append flag */
- PURPLE_CIPHER_CAPS_DIGEST = 1 << 8, /**< Digest flag */
- PURPLE_CIPHER_CAPS_ENCRYPT = 1 << 9, /**< Encrypt flag */
- PURPLE_CIPHER_CAPS_DECRYPT = 1 << 10, /**< Decrypt flag */
- PURPLE_CIPHER_CAPS_SET_SALT = 1 << 11, /**< Set salt flag */
- PURPLE_CIPHER_CAPS_GET_SALT_SIZE = 1 << 12, /**< Get salt size flag */
- PURPLE_CIPHER_CAPS_SET_KEY = 1 << 13, /**< Set key flag */
- PURPLE_CIPHER_CAPS_GET_KEY_SIZE = 1 << 14, /**< Get key size flag */
- PURPLE_CIPHER_CAPS_SET_BATCH_MODE = 1 << 15, /**< Set batch mode flag */
- PURPLE_CIPHER_CAPS_GET_BATCH_MODE = 1 << 16, /**< Get batch mode flag */
- PURPLE_CIPHER_CAPS_GET_BLOCK_SIZE = 1 << 17, /**< The get block size flag */
- PURPLE_CIPHER_CAPS_SET_KEY_WITH_LEN = 1 << 18, /**< The set key with length flag */
- PURPLE_CIPHER_CAPS_UNKNOWN = 1 << 19 /**< Unknown */
-} PurpleCipherCaps;
+struct _PurpleCipher {
+ GObject gparent;
+};
/**
- * The operations of a cipher. Every cipher must implement one of these.
+ * PurpleCipherClass:
+ *
+ * The base class for all #PurpleCipher's.
*/
-struct _PurpleCipherOps {
- /** The set option function */
- void (*set_option)(PurpleCipherContext *context, const gchar *name, void *value);
-
- /** The get option function */
- void *(*get_option)(PurpleCipherContext *context, const gchar *name);
-
- /** The init function */
- void (*init)(PurpleCipherContext *context, void *extra);
+struct _PurpleCipherClass {
+ GObjectClass parent_class;
/** The reset function */
- void (*reset)(PurpleCipherContext *context, void *extra);
+ void (*reset)(PurpleCipher *cipher);
- /** The uninit function */
- void (*uninit)(PurpleCipherContext *context);
+ /** The reset state function */
+ void (*reset_state)(PurpleCipher *cipher);
/** The set initialization vector function */
- void (*set_iv)(PurpleCipherContext *context, guchar *iv, size_t len);
+ void (*set_iv)(PurpleCipher *cipher, guchar *iv, size_t len);
/** The append data function */
- void (*append)(PurpleCipherContext *context, const guchar *data, size_t len);
+ void (*append)(PurpleCipher *cipher, const guchar *data, size_t len);
/** The digest function */
- gboolean (*digest)(PurpleCipherContext *context, size_t in_len, guchar digest[], size_t *out_len);
+ gboolean (*digest)(PurpleCipher *cipher, guchar digest[], size_t len);
+
+ /** The get digest size function */
+ size_t (*get_digest_size)(PurpleCipher *cipher);
/** The encrypt function */
- int (*encrypt)(PurpleCipherContext *context, const guchar data[], size_t len, guchar output[], size_t *outlen);
+ ssize_t (*encrypt)(PurpleCipher *cipher, const guchar input[], size_t in_len, guchar output[], size_t out_size);
/** The decrypt function */
- int (*decrypt)(PurpleCipherContext *context, const guchar data[], size_t len, guchar output[], size_t *outlen);
+ ssize_t (*decrypt)(PurpleCipher *cipher, const guchar input[], size_t in_len, guchar output[], size_t out_size);
/** The set salt function */
- void (*set_salt)(PurpleCipherContext *context, guchar *salt);
-
- /** The get salt size function */
- size_t (*get_salt_size)(PurpleCipherContext *context);
+ void (*set_salt)(PurpleCipher *cipher, const guchar *salt, size_t len);
/** The set key function */
- void (*set_key)(PurpleCipherContext *context, const guchar *key);
+ void (*set_key)(PurpleCipher *cipher, const guchar *key, size_t len);
/** The get key size function */
- size_t (*get_key_size)(PurpleCipherContext *context);
+ size_t (*get_key_size)(PurpleCipher *cipher);
/** The set batch mode function */
- void (*set_batch_mode)(PurpleCipherContext *context, PurpleCipherBatchMode mode);
+ void (*set_batch_mode)(PurpleCipher *cipher, PurpleCipherBatchMode mode);
/** The get batch mode function */
- PurpleCipherBatchMode (*get_batch_mode)(PurpleCipherContext *context);
+ PurpleCipherBatchMode (*get_batch_mode)(PurpleCipher *cipher);
/** The get block size function */
- size_t (*get_block_size)(PurpleCipherContext *context);
+ size_t (*get_block_size)(PurpleCipher *cipher);
- /** The set key with length function */
- void (*set_key_with_len)(PurpleCipherContext *context, const guchar *key, size_t len);
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
};
-G_BEGIN_DECLS
-
-/*****************************************************************************/
-/** @name PurpleCipher API */
-/*****************************************************************************/
-/*@{*/
-
/**
- * Gets a cipher's name
+ * PurpleHash:
*
- * @param cipher The cipher handle
- *
- * @return The cipher's name
+ * Purple Hash is an opaque data structure and should not be used directly.
*/
-const gchar *purple_cipher_get_name(PurpleCipher *cipher);
+struct _PurpleHash {
+ GObject gparent;
+};
/**
- * Gets a cipher's capabilities
- *
- * @param cipher The cipher handle
+ * PurpleHashClass:
*
- * @return The cipher's info
+ * The base class for all #PurpleHash's.
*/
-guint purple_cipher_get_capabilities(PurpleCipher *cipher);
+struct _PurpleHashClass {
+ GObjectClass parent_class;
-/**
- * Gets a digest from a cipher
- *
- * @param name The cipher's name
- * @param data The data to hash
- * @param data_len The length of the data
- * @param in_len The length of the buffer
- * @param digest The returned digest
- * @param out_len The length written
- *
- * @return @c TRUE if successful, @c FALSE otherwise
- */
-gboolean purple_cipher_digest_region(const gchar *name, const guchar *data, size_t data_len, size_t in_len, guchar digest[], size_t *out_len);
+ /** The reset function */
+ void (*reset)(PurpleHash *hash);
-/*@}*/
-/******************************************************************************/
-/** @name PurpleCiphers API */
-/******************************************************************************/
-/*@{*/
+ /** The reset state function */
+ void (*reset_state)(PurpleHash *hash);
-/**
- * Finds a cipher by it's name
- *
- * @param name The name of the cipher to find
- *
- * @return The cipher handle or @c NULL
- */
-PurpleCipher *purple_ciphers_find_cipher(const gchar *name);
+ /** The append data function */
+ void (*append)(PurpleHash *hash, const guchar *data, size_t len);
-/**
- * Registers a cipher as a usable cipher
- *
- * @param name The name of the new cipher
- * @param ops The cipher ops to register
- *
- * @return The handle to the new cipher or @c NULL if it failed
- */
-PurpleCipher *purple_ciphers_register_cipher(const gchar *name, PurpleCipherOps *ops);
+ /** The digest function */
+ gboolean (*digest)(PurpleHash *hash, guchar digest[], size_t len);
-/**
- * Unregisters a cipher
- *
- * @param cipher The cipher handle to unregister
- *
- * @return Whether or not the cipher was successfully unloaded
- */
-gboolean purple_ciphers_unregister_cipher(PurpleCipher *cipher);
+ /** The get digest size function */
+ size_t (*get_digest_size)(PurpleHash *hash);
-/**
- * Gets the list of ciphers
- *
- * @return The list of available ciphers
- * @note This list should not be modified, it is owned by the cipher core
- */
-GList *purple_ciphers_get_ciphers(void);
+ /** The get block size function */
+ size_t (*get_block_size)(PurpleHash *hash);
-/*@}*/
-/******************************************************************************/
-/** @name PurpleCipher Subsystem API */
-/******************************************************************************/
-/*@{*/
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
-/**
- * Gets the handle to the cipher subsystem
- *
- * @return The handle to the cipher subsystem
- */
-gpointer purple_ciphers_get_handle(void);
+G_BEGIN_DECLS
-/**
- * Initializes the cipher core
- */
-void purple_ciphers_init(void);
+/*****************************************************************************/
+/* PurpleCipher API */
+/*****************************************************************************/
/**
- * Uninitializes the cipher core
+ * purple_cipher_get_type:
+ *
+ * Returns: The #GType for the Cipher object.
*/
-void purple_ciphers_uninit(void);
-
-/*@}*/
-/******************************************************************************/
-/** @name PurpleCipherContext API */
-/******************************************************************************/
-/*@{*/
+GType purple_cipher_get_type(void);
/**
- * Sets the value an option on a cipher context
+ * purple_cipher_reset:
+ * @cipher: The cipher
*
- * @param context The cipher context
- * @param name The name of the option
- * @param value The value to set
+ * Resets a cipher to it's default value
+ * Note: If you have set an IV you will have to set it after resetting
*/
-void purple_cipher_context_set_option(PurpleCipherContext *context, const gchar *name, gpointer value);
+void purple_cipher_reset(PurpleCipher *cipher);
/**
- * Gets the vale of an option on a cipher context
+ * purple_cipher_reset_state:
+ * @cipher: The cipher
+ *
+ * Resets a cipher state to it's default value, but doesn't touch stateless
+ * configuration.
*
- * @param context The cipher context
- * @param name The name of the option
- * @return The value of the option
+ * That means, IV and digest will be wiped out, but keys, ops or salt
+ * will remain untouched.
*/
-gpointer purple_cipher_context_get_option(PurpleCipherContext *context, const gchar *name);
+void purple_cipher_reset_state(PurpleCipher *cipher);
/**
- * Creates a new cipher context and initializes it
+ * purple_cipher_set_iv:
+ * @cipher: The cipher
+ * @iv: The initialization vector to set
+ * @len: The len of the IV
*
- * @param cipher The cipher to use
- * @param extra Extra data for the specific cipher
- *
- * @return The new cipher context
+ * Sets the initialization vector for a cipher
+ * Note: This should only be called right after a cipher is created or reset
*/
-PurpleCipherContext *purple_cipher_context_new(PurpleCipher *cipher, void *extra);
+void purple_cipher_set_iv(PurpleCipher *cipher, guchar *iv, size_t len);
/**
- * Creates a new cipher context by the cipher name and initializes it
- *
- * @param name The cipher's name
- * @param extra Extra data for the specific cipher
+ * purple_cipher_append:
+ * @cipher: The cipher
+ * @data: The data to append
+ * @len: The length of the data
*
- * @return The new cipher context
+ * Appends data to the cipher context
*/
-PurpleCipherContext *purple_cipher_context_new_by_name(const gchar *name, void *extra);
+void purple_cipher_append(PurpleCipher *cipher, const guchar *data, size_t len);
/**
- * Resets a cipher context to it's default value
- * @note If you have set an IV you will have to set it after resetting
+ * purple_cipher_digest:
+ * @cipher: The cipher
+ * @digest: The return buffer for the digest
+ * @len: The length of the buffer
*
- * @param context The context to reset
- * @param extra Extra data for the specific cipher
+ * Digests a cipher context
*/
-void purple_cipher_context_reset(PurpleCipherContext *context, gpointer extra);
+gboolean purple_cipher_digest(PurpleCipher *cipher, guchar digest[], size_t len);
/**
- * Destorys a cipher context and deinitializes it
+ * purple_cipher_digest_to_str:
+ * @cipher: The cipher
+ * @digest_s: The return buffer for the string digest
+ * @len: The length of the buffer
*
- * @param context The cipher context to destory
+ * Converts a guchar digest into a hex string
*/
-void purple_cipher_context_destroy(PurpleCipherContext *context);
+gboolean purple_cipher_digest_to_str(PurpleCipher *cipher, gchar digest_s[], size_t len);
/**
- * Sets the initialization vector for a context
- * @note This should only be called right after a cipher context is created or reset
+ * purple_cipher_get_digest_size:
+ * @cipher: The cipher whose digest size to get
+ *
+ * Gets the digest size of a cipher
*
- * @param context The context to set the IV to
- * @param iv The initialization vector to set
- * @param len The len of the IV
+ * Returns: The digest size of the cipher
*/
-void purple_cipher_context_set_iv(PurpleCipherContext *context, guchar *iv, size_t len);
+size_t purple_cipher_get_digest_size(PurpleCipher *cipher);
/**
- * Appends data to the context
+ * purple_cipher_encrypt:
+ * @cipher: The cipher
+ * @input: The data to encrypt
+ * @in_len: The length of the data
+ * @output: The output buffer
+ * @out_size: The size of the output buffer
+ *
+ * Encrypts data using the cipher
*
- * @param context The context to append data to
- * @param data The data to append
- * @param len The length of the data
+ * Returns: A length of data that was outputed or -1, if failed
*/
-void purple_cipher_context_append(PurpleCipherContext *context, const guchar *data, size_t len);
+ssize_t purple_cipher_encrypt(PurpleCipher *cipher, const guchar input[], size_t in_len, guchar output[], size_t out_size);
/**
- * Digests a context
+ * purple_cipher_decrypt:
+ * @cipher: The cipher
+ * @input: The data to encrypt
+ * @in_len: The length of the returned value
+ * @output: The output buffer
+ * @out_size: The size of the output buffer
*
- * @param context The context to digest
- * @param in_len The length of the buffer
- * @param digest The return buffer for the digest
- * @param out_len The length of the returned value
+ * Decrypts data using the cipher
+ *
+ * Returns: A length of data that was outputed or -1, if failed
*/
-gboolean purple_cipher_context_digest(PurpleCipherContext *context, size_t in_len, guchar digest[], size_t *out_len);
+ssize_t purple_cipher_decrypt(PurpleCipher *cipher, const guchar input[], size_t in_len, guchar output[], size_t out_size);
/**
- * Converts a guchar digest into a hex string
+ * purple_cipher_set_salt:
+ * @cipher: The cipher whose salt to set
+ * @salt: The salt
+ * @len: The length of the salt
*
- * @param context The context to get a digest from
- * @param in_len The length of the buffer
- * @param digest_s The return buffer for the string digest
- * @param out_len The length of the returned value
+ * Sets the salt on a cipher
*/
-gboolean purple_cipher_context_digest_to_str(PurpleCipherContext *context, size_t in_len, gchar digest_s[], size_t *out_len);
+void purple_cipher_set_salt(PurpleCipher *cipher, const guchar *salt, size_t len);
/**
- * Encrypts data using the context
+ * purple_cipher_set_key:
+ * @cipher: The cipher whose key to set
+ * @key: The key
+ * @len: The size of the key
*
- * @param context The context
- * @param data The data to encrypt
- * @param len The length of the data
- * @param output The output buffer
- * @param outlen The len of data that was outputed
- *
- * @return A cipher specific status code
+ * Sets the key on a cipher
*/
-gint purple_cipher_context_encrypt(PurpleCipherContext *context, const guchar data[], size_t len, guchar output[], size_t *outlen);
+void purple_cipher_set_key(PurpleCipher *cipher, const guchar *key, size_t len);
/**
- * Decrypts data using the context
+ * purple_cipher_get_key_size:
+ * @cipher: The cipher whose key size to get
*
- * @param context The context
- * @param data The data to encrypt
- * @param len The length of the returned value
- * @param output The output buffer
- * @param outlen The len of data that was outputed
+ * Gets the size of the key if the cipher supports it
*
- * @return A cipher specific status code
+ * Returns: The size of the key
*/
-gint purple_cipher_context_decrypt(PurpleCipherContext *context, const guchar data[], size_t len, guchar output[], size_t *outlen);
+size_t purple_cipher_get_key_size(PurpleCipher *cipher);
/**
- * Sets the salt on a context
+ * purple_cipher_set_batch_mode:
+ * @cipher: The cipher whose batch mode to set
+ * @mode: The batch mode under which the cipher should operate
*
- * @param context The context whose salt to set
- * @param salt The salt
+ * Sets the batch mode of a cipher
*/
-void purple_cipher_context_set_salt(PurpleCipherContext *context, guchar *salt);
+void purple_cipher_set_batch_mode(PurpleCipher *cipher, PurpleCipherBatchMode mode);
/**
- * Gets the size of the salt if the cipher supports it
+ * purple_cipher_get_batch_mode:
+ * @cipher: The cipher whose batch mode to get
*
- * @param context The context whose salt size to get
+ * Gets the batch mode of a cipher
*
- * @return The size of the salt
+ * Returns: The batch mode under which the cipher is operating
*/
-size_t purple_cipher_context_get_salt_size(PurpleCipherContext *context);
+PurpleCipherBatchMode purple_cipher_get_batch_mode(PurpleCipher *cipher);
/**
- * Sets the key on a context
+ * purple_cipher_get_block_size:
+ * @cipher: The cipher whose block size to get
+ *
+ * Gets the block size of a cipher
*
- * @param context The context whose key to set
- * @param key The key
+ * Returns: The block size of the cipher
*/
-void purple_cipher_context_set_key(PurpleCipherContext *context, const guchar *key);
+size_t purple_cipher_get_block_size(PurpleCipher *cipher);
+
+/*****************************************************************************/
+/* PurpleHash API */
+/*****************************************************************************/
/**
- * Gets the key size for a context
+ * purple_hash_get_type:
*
- * @param context The context whose key size to get
- *
- * @return The size of the key
+ * Returns: The #GType for the Hash object.
*/
-size_t purple_cipher_context_get_key_size(PurpleCipherContext *context);
+GType purple_hash_get_type(void);
/**
- * Sets the batch mode of a context
- *
- * @param context The context whose batch mode to set
- * @param mode The batch mode under which the cipher should operate
+ * purple_hash_reset:
+ * @hash: The hash
*
+ * Resets a hash to it's default value
+ * Note: If you have set an IV you will have to set it after resetting
*/
-void purple_cipher_context_set_batch_mode(PurpleCipherContext *context, PurpleCipherBatchMode mode);
+void purple_hash_reset(PurpleHash *hash);
/**
- * Gets the batch mode of a context
+ * purple_hash_reset_state:
+ * @hash: The hash
*
- * @param context The context whose batch mode to get
+ * Resets a hash state to it's default value, but doesn't touch stateless
+ * configuration.
*
- * @return The batch mode under which the cipher is operating
+ * That means, IV and digest will be wiped out, but keys, ops or salt
+ * will remain untouched.
*/
-PurpleCipherBatchMode purple_cipher_context_get_batch_mode(PurpleCipherContext *context);
+void purple_hash_reset_state(PurpleHash *hash);
/**
- * Gets the block size of a context
- *
- * @param context The context whose block size to get
+ * purple_hash_append:
+ * @hash: The hash
+ * @data: The data to append
+ * @len: The length of the data
*
- * @return The block size of the context
+ * Appends data to the hash context
*/
-size_t purple_cipher_context_get_block_size(PurpleCipherContext *context);
+void purple_hash_append(PurpleHash *hash, const guchar *data, size_t len);
/**
- * Sets the key with a given length on a context
- *
- * @param context The context whose key to set
- * @param key The key
- * @param len The length of the key
+ * purple_hash_digest:
+ * @hash: The hash
+ * @digest: The return buffer for the digest
+ * @len: The length of the buffer
*
+ * Digests a hash context
*/
-void purple_cipher_context_set_key_with_len(PurpleCipherContext *context, const guchar *key, size_t len);
+gboolean purple_hash_digest(PurpleHash *hash, guchar digest[], size_t len);
/**
- * Sets the cipher data for a context
+ * purple_hash_digest_to_str:
+ * @hash: The hash
+ * @digest_s: The return buffer for the string digest
+ * @len: The length of the buffer
*
- * @param context The context whose cipher data to set
- * @param data The cipher data to set
+ * Converts a guchar digest into a hex string
*/
-void purple_cipher_context_set_data(PurpleCipherContext *context, gpointer data);
+gboolean purple_hash_digest_to_str(PurpleHash *hash, gchar digest_s[], size_t len);
/**
- * Gets the cipher data for a context
+ * purple_hash_get_digest_size:
+ * @hash: The hash whose digest size to get
*
- * @param context The context whose cipher data to get
+ * Gets the digest size of a hash
*
- * @return The cipher data
+ * Returns: The digest size of the hash
*/
-gpointer purple_cipher_context_get_data(PurpleCipherContext *context);
-
-/*@}*/
-/*****************************************************************************/
-/** @name Purple Cipher HTTP Digest Helper Functions */
-/*****************************************************************************/
-/*@{*/
+size_t purple_hash_get_digest_size(PurpleHash *hash);
/**
- * Calculates a session key for HTTP Digest authentation
+ * purple_hash_get_block_size:
+ * @hash: The hash whose block size to get
*
- * See RFC 2617 for more information.
+ * Gets the block size of a hash
*
- * @param algorithm The hash algorithm to use
- * @param username The username provided by the user
- * @param realm The authentication realm provided by the server
- * @param password The password provided by the user
- * @param nonce The nonce provided by the server
- * @param client_nonce The nonce provided by the client
- *
- * @return The session key, or @c NULL if an error occurred.
- */
-gchar *purple_cipher_http_digest_calculate_session_key(
- const gchar *algorithm, const gchar *username,
- const gchar *realm, const gchar *password,
- const gchar *nonce, const gchar *client_nonce);
-
-/** Calculate a response for HTTP Digest authentication
- *
- * See RFC 2617 for more information.
- *
- * @param algorithm The hash algorithm to use
- * @param method The HTTP method in use
- * @param digest_uri The URI from the initial request
- * @param qop The "quality of protection"
- * @param entity The entity body
- * @param nonce The nonce provided by the server
- * @param nonce_count The nonce count
- * @param client_nonce The nonce provided by the client
- * @param session_key The session key from purple_cipher_http_digest_calculate_session_key()
- *
- * @return The hashed response, or @c NULL if an error occurred.
+ * Returns: The block size of the hash
*/
-gchar *purple_cipher_http_digest_calculate_response(
- const gchar *algorithm, const gchar *method,
- const gchar *digest_uri, const gchar *qop,
- const gchar *entity, const gchar *nonce,
- const gchar *nonce_count, const gchar *client_nonce,
- const gchar *session_key);
-
-/*@}*/
+size_t purple_hash_get_block_size(PurpleHash *hash);
G_END_DECLS
diff --git a/libpurple/ciphers/Makefile.am b/libpurple/ciphers/Makefile.am
deleted file mode 100644
index 9e2299d0c2..0000000000
--- a/libpurple/ciphers/Makefile.am
+++ /dev/null
@@ -1,16 +0,0 @@
-noinst_LTLIBRARIES=libpurple-ciphers.la
-
-libpurple_ciphers_la_SOURCES=\
- des.c \
- gchecksum.c \
- hmac.c \
- md4.c \
- md5.c \
- rc4.c \
- sha1.c \
- sha256.c
-
-AM_CPPFLAGS = \
- -I$(top_srcdir)/libpurple \
- $(GLIB_CFLAGS)
-
diff --git a/libpurple/ciphers/aescipher.c b/libpurple/ciphers/aescipher.c
new file mode 100644
index 0000000000..acb4eb9741
--- /dev/null
+++ b/libpurple/ciphers/aescipher.c
@@ -0,0 +1,725 @@
+/*
+ * 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
+ *
+ * Written by Tomek Wasilczyk <twasilczyk@pidgin.im>
+ */
+
+#include "internal.h"
+#include "glibcompat.h"
+
+#include "aescipher.h"
+#include "debug.h"
+#include "enums.h"
+
+#include <string.h>
+
+#if defined(HAVE_GNUTLS)
+# define PURPLE_AES_USE_GNUTLS 1
+# include <gnutls/gnutls.h>
+# include <gnutls/crypto.h>
+#elif defined(HAVE_NSS)
+# define PURPLE_AES_USE_NSS 1
+# include <nss.h>
+# include <pk11pub.h>
+# include <prerror.h>
+#else
+# warning "No GnuTLS or NSS support"
+#endif
+
+/* 128bit */
+#define PURPLE_AES_BLOCK_SIZE 16
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+#define PURPLE_AES_CIPHER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_AES_CIPHER, PurpleAESCipherPrivate))
+
+typedef struct {
+ guchar iv[PURPLE_AES_BLOCK_SIZE];
+ guchar key[32];
+ guint key_size;
+ gboolean failure;
+ PurpleCipherBatchMode batch_mode;
+} PurpleAESCipherPrivate;
+
+/******************************************************************************
+ * Enums
+ *****************************************************************************/
+enum {
+ PROP_NONE,
+ PROP_BATCH_MODE,
+ PROP_IV,
+ PROP_KEY,
+ PROP_LAST,
+};
+
+/*******************************************************************************
+ * Globals
+ ******************************************************************************/
+static GParamSpec *properties[PROP_LAST];
+
+/******************************************************************************
+ * Cipher Stuff
+ *****************************************************************************/
+
+typedef gboolean (*purple_aes_cipher_crypt_func)(
+ const guchar *input, guchar *output, size_t len,
+ guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size,
+ PurpleCipherBatchMode batch_mode);
+
+static void
+purple_aes_cipher_reset(PurpleCipher *cipher)
+{
+ PurpleAESCipherPrivate *priv = PURPLE_AES_CIPHER_GET_PRIVATE(cipher);
+
+ g_return_if_fail(priv != NULL);
+
+ memset(priv->iv, 0, sizeof(priv->iv));
+ memset(priv->key, 0, sizeof(priv->key));
+ priv->key_size = 32; /* 256bit */
+ priv->failure = FALSE;
+}
+
+static void
+purple_aes_cipher_set_iv(PurpleCipher *cipher, guchar *iv, size_t len)
+{
+ PurpleAESCipherPrivate *priv = PURPLE_AES_CIPHER_GET_PRIVATE(cipher);
+
+ if ((len > 0 && iv == NULL) ||
+ (len != 0 && len != sizeof(priv->iv))) {
+ purple_debug_error("cipher-aes", "invalid IV length\n");
+ priv->failure = TRUE;
+ return;
+ }
+
+ if (len == 0)
+ memset(priv->iv, 0, sizeof(priv->iv));
+ else
+ memcpy(priv->iv, iv, len);
+
+ g_object_notify_by_pspec(G_OBJECT(cipher), properties[PROP_IV]);
+}
+
+static void
+purple_aes_cipher_set_key(PurpleCipher *cipher, const guchar *key, size_t len)
+{
+ PurpleAESCipherPrivate *priv = PURPLE_AES_CIPHER_GET_PRIVATE(cipher);
+
+ if ((len > 0 && key == NULL) ||
+ (len != 0 && len != 16 && len != 24 && len != 32)) {
+ purple_debug_error("cipher-aes", "invalid key length\n");
+ priv->failure = TRUE;
+ return;
+ }
+
+ priv->key_size = len;
+ memset(priv->key, 0, sizeof(priv->key));
+ if (len > 0)
+ memcpy(priv->key, key, len);
+
+ g_object_notify_by_pspec(G_OBJECT(cipher), properties[PROP_KEY]);
+}
+
+static guchar *
+purple_aes_cipher_pad_pkcs7(const guchar input[], size_t in_len, size_t *out_len)
+{
+ int padding_len, total_len;
+ guchar *padded;
+
+ g_return_val_if_fail(input != NULL, NULL);
+ g_return_val_if_fail(out_len != NULL, NULL);
+
+ padding_len = PURPLE_AES_BLOCK_SIZE - (in_len % PURPLE_AES_BLOCK_SIZE);
+ total_len = in_len + padding_len;
+ g_assert((total_len % PURPLE_AES_BLOCK_SIZE) == 0);
+
+ padded = g_new(guchar, total_len);
+ *out_len = total_len;
+
+ memcpy(padded, input, in_len);
+ memset(padded + in_len, padding_len, padding_len);
+
+ return padded;
+}
+
+static ssize_t
+purple_aes_cipher_unpad_pkcs7(guchar input[], size_t in_len)
+{
+ guchar padding_len, i;
+ size_t out_len;
+
+ g_return_val_if_fail(input != NULL, -1);
+ g_return_val_if_fail(in_len > 0, -1);
+
+ padding_len = input[in_len - 1];
+ if (padding_len == 0 || padding_len > PURPLE_AES_BLOCK_SIZE ||
+ padding_len > in_len) {
+ purple_debug_warning("cipher-aes",
+ "Invalid padding length: %d (total %" G_GSIZE_FORMAT ") - "
+ "most probably, the key was invalid\n",
+ padding_len, in_len);
+ return -1;
+ }
+
+ out_len = in_len - padding_len;
+ for (i = 0; i < padding_len; i++) {
+ if (input[out_len + i] != padding_len) {
+ purple_debug_warning("cipher-aes",
+ "Padding doesn't match at pos %d (found %02x, "
+ "expected %02x) - "
+ "most probably, the key was invalid\n",
+ i, input[out_len + i], padding_len);
+ return -1;
+ }
+ }
+
+ memset(input + out_len, 0, padding_len);
+ return out_len;
+}
+
+#ifdef PURPLE_AES_USE_GNUTLS
+
+static gnutls_cipher_hd_t
+purple_aes_cipher_gnutls_crypt_init(guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32],
+ guint key_size)
+{
+ gnutls_cipher_hd_t handle;
+ gnutls_cipher_algorithm_t algorithm;
+ gnutls_datum_t key_info, iv_info;
+ int ret;
+
+ if (key_size == 16)
+ algorithm = GNUTLS_CIPHER_AES_128_CBC;
+ else if (key_size == 24)
+ algorithm = GNUTLS_CIPHER_AES_192_CBC;
+ else if (key_size == 32)
+ algorithm = GNUTLS_CIPHER_AES_256_CBC;
+ else
+ g_return_val_if_reached(NULL);
+
+ key_info.data = key;
+ key_info.size = key_size;
+
+ iv_info.data = iv;
+ iv_info.size = PURPLE_AES_BLOCK_SIZE;
+
+ ret = gnutls_cipher_init(&handle, algorithm, &key_info, &iv_info);
+ if (ret != 0) {
+ purple_debug_error("cipher-aes",
+ "gnutls_cipher_init failed: %d\n", ret);
+ return NULL;
+ }
+
+ return handle;
+}
+
+static gboolean
+purple_aes_cipher_gnutls_encrypt(const guchar *input, guchar *output, size_t len,
+ guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size,
+ PurpleCipherBatchMode batch_mode)
+{
+ gnutls_cipher_hd_t handle;
+ int ret;
+
+ /* We have to simulate ECB mode, which is not supported by GnuTLS. */
+ if (batch_mode == PURPLE_CIPHER_BATCH_MODE_ECB) {
+ size_t i;
+ for (i = 0; i < len / PURPLE_AES_BLOCK_SIZE; i++) {
+ int offset = i * PURPLE_AES_BLOCK_SIZE;
+ guchar iv_local[PURPLE_AES_BLOCK_SIZE];
+ gboolean succ;
+
+ memcpy(iv_local, iv, sizeof(iv_local));
+ succ = purple_aes_cipher_gnutls_encrypt(
+ input + offset, output + offset,
+ PURPLE_AES_BLOCK_SIZE,
+ iv_local, key, key_size,
+ PURPLE_CIPHER_BATCH_MODE_CBC);
+ if (!succ)
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ handle = purple_aes_cipher_gnutls_crypt_init(iv, key, key_size);
+ if (handle == NULL)
+ return FALSE;
+
+ ret = gnutls_cipher_encrypt2(handle, (guchar *)input, len, output, len);
+ gnutls_cipher_deinit(handle);
+
+ if (ret != 0) {
+ purple_debug_error("cipher-aes",
+ "gnutls_cipher_encrypt2 failed: %d\n", ret);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+purple_aes_cipher_gnutls_decrypt(const guchar *input, guchar *output, size_t len,
+ guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size,
+ PurpleCipherBatchMode batch_mode)
+{
+ gnutls_cipher_hd_t handle;
+ int ret;
+
+ /* We have to simulate ECB mode, which is not supported by GnuTLS. */
+ if (batch_mode == PURPLE_CIPHER_BATCH_MODE_ECB) {
+ size_t i;
+ for (i = 0; i < len / PURPLE_AES_BLOCK_SIZE; i++) {
+ int offset = i * PURPLE_AES_BLOCK_SIZE;
+ guchar iv_local[PURPLE_AES_BLOCK_SIZE];
+ gboolean succ;
+
+ memcpy(iv_local, iv, sizeof(iv_local));
+ succ = purple_aes_cipher_gnutls_decrypt(
+ input + offset, output + offset,
+ PURPLE_AES_BLOCK_SIZE,
+ iv_local, key, key_size,
+ PURPLE_CIPHER_BATCH_MODE_CBC);
+ if (!succ)
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ handle = purple_aes_cipher_gnutls_crypt_init(iv, key, key_size);
+ if (handle == NULL)
+ return FALSE;
+
+ ret = gnutls_cipher_decrypt2(handle, input, len, output, len);
+ gnutls_cipher_deinit(handle);
+
+ if (ret != 0) {
+ purple_debug_error("cipher-aes",
+ "gnutls_cipher_decrypt2 failed: %d\n", ret);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#elif defined(PURPLE_AES_USE_NSS)
+
+typedef struct {
+ PK11SlotInfo *slot;
+ PK11SymKey *sym_key;
+ SECItem *sec_param;
+ PK11Context *enc_context;
+} PurpleAESCipherNSSContext;
+
+static void
+purple_aes_cipher_nss_cleanup(PurpleAESCipherNSSContext *context)
+{
+ g_return_if_fail(context != NULL);
+
+ if (context->enc_context != NULL)
+ PK11_DestroyContext(context->enc_context, TRUE);
+ if (context->sec_param != NULL)
+ SECITEM_FreeItem(context->sec_param, TRUE);
+ if (context->sym_key != NULL)
+ PK11_FreeSymKey(context->sym_key);
+ if (context->slot != NULL)
+ PK11_FreeSlot(context->slot);
+
+ memset(context, 0, sizeof(PurpleAESCipherNSSContext));
+}
+
+static gboolean
+purple_aes_cipher_nss_crypt(const guchar *input, guchar *output, size_t len,
+ guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size,
+ CK_ATTRIBUTE_TYPE operation, CK_MECHANISM_TYPE cipher_mech)
+{
+ PurpleAESCipherNSSContext context;
+ SECItem key_item, iv_item;
+ SECStatus ret;
+ int outlen = 0;
+ unsigned int outlen_tmp = 0;
+
+ memset(&context, 0, sizeof(PurpleAESCipherNSSContext));
+
+ if (NSS_NoDB_Init(NULL) != SECSuccess) {
+ purple_debug_error("cipher-aes",
+ "NSS_NoDB_Init failed: %d\n", PR_GetError());
+ return FALSE;
+ }
+
+ context.slot = PK11_GetBestSlot(cipher_mech, NULL);
+ if (context.slot == NULL) {
+ purple_debug_error("cipher-aes",
+ "PK11_GetBestSlot failed: %d\n", PR_GetError());
+ return FALSE;
+ }
+
+ key_item.type = siBuffer;
+ key_item.data = key;
+ key_item.len = key_size;
+ context.sym_key = PK11_ImportSymKey(context.slot, cipher_mech,
+ PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, NULL);
+ if (context.sym_key == NULL) {
+ purple_debug_error("cipher-aes",
+ "PK11_ImportSymKey failed: %d\n", PR_GetError());
+ purple_aes_cipher_nss_cleanup(&context);
+ return FALSE;
+ }
+
+ iv_item.type = siBuffer;
+ iv_item.data = iv;
+ iv_item.len = PURPLE_AES_BLOCK_SIZE;
+ context.sec_param = PK11_ParamFromIV(cipher_mech, &iv_item);
+ if (context.sec_param == NULL) {
+ purple_debug_error("cipher-aes",
+ "PK11_ParamFromIV failed: %d\n", PR_GetError());
+ purple_aes_cipher_nss_cleanup(&context);
+ return FALSE;
+ }
+
+ context.enc_context = PK11_CreateContextBySymKey(cipher_mech, operation,
+ context.sym_key, context.sec_param);
+ if (context.enc_context == NULL) {
+ purple_debug_error("cipher-aes",
+ "PK11_CreateContextBySymKey failed: %d\n",
+ PR_GetError());
+ purple_aes_cipher_nss_cleanup(&context);
+ return FALSE;
+ }
+
+ ret = PK11_CipherOp(context.enc_context, output, &outlen, len,
+ (guchar *)input, len);
+ if (ret != SECSuccess) {
+ purple_debug_error("cipher-aes",
+ "PK11_CipherOp failed: %d\n", PR_GetError());
+ purple_aes_cipher_nss_cleanup(&context);
+ return FALSE;
+ }
+
+ ret = PK11_DigestFinal(context.enc_context, output + outlen, &outlen_tmp,
+ len - outlen);
+ if (ret != SECSuccess) {
+ purple_debug_error("cipher-aes",
+ "PK11_DigestFinal failed: %d\n", PR_GetError());
+ purple_aes_cipher_nss_cleanup(&context);
+ return FALSE;
+ }
+
+ purple_aes_cipher_nss_cleanup(&context);
+
+ outlen += outlen_tmp;
+ if (outlen != (int)len) {
+ purple_debug_error("cipher-aes",
+ "resulting length doesn't match: %d (expected: %"
+ G_GSIZE_FORMAT ")\n", outlen, len);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static CK_MECHANISM_TYPE
+purple_aes_cipher_nss_batch_mode(PurpleCipherBatchMode batch_mode)
+{
+ switch (batch_mode) {
+ case PURPLE_CIPHER_BATCH_MODE_CBC:
+ return CKM_AES_CBC;
+ case PURPLE_CIPHER_BATCH_MODE_ECB:
+ return CKM_AES_ECB;
+ }
+
+ return CKM_AES_CBC;
+}
+
+static gboolean
+purple_aes_cipher_nss_encrypt(const guchar *input, guchar *output, size_t len,
+ guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size,
+ PurpleCipherBatchMode batch_mode)
+{
+ return purple_aes_cipher_nss_crypt(input, output, len, iv, key, key_size,
+ CKA_ENCRYPT, purple_aes_cipher_nss_batch_mode(batch_mode));
+}
+
+static gboolean
+purple_aes_cipher_nss_decrypt(const guchar *input, guchar *output, size_t len,
+ guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size,
+ PurpleCipherBatchMode batch_mode)
+{
+ return purple_aes_cipher_nss_crypt(input, output, len, iv, key, key_size,
+ CKA_DECRYPT, purple_aes_cipher_nss_batch_mode(batch_mode));
+}
+
+#endif /* PURPLE_AES_USE_NSS */
+
+static ssize_t
+purple_aes_cipher_encrypt(PurpleCipher *cipher, const guchar input[],
+ size_t in_len, guchar output[], size_t out_size)
+{
+ PurpleAESCipherPrivate *priv = PURPLE_AES_CIPHER_GET_PRIVATE(cipher);
+ purple_aes_cipher_crypt_func encrypt_func;
+ guchar *input_padded;
+ size_t out_len = 0;
+ gboolean succ;
+
+ if (priv->failure)
+ return -1;
+
+ input_padded = purple_aes_cipher_pad_pkcs7(input, in_len, &out_len);
+
+ if (out_len > out_size) {
+ purple_debug_error("cipher-aes", "Output buffer too small (%"
+ G_GSIZE_FORMAT " > %" G_GSIZE_FORMAT ")",
+ out_len, out_size);
+ memset(input_padded, 0, out_len);
+ g_free(input_padded);
+ return -1;
+ }
+
+#if defined(PURPLE_AES_USE_GNUTLS)
+ encrypt_func = purple_aes_cipher_gnutls_encrypt;
+#elif defined(PURPLE_AES_USE_NSS)
+ encrypt_func = purple_aes_cipher_nss_encrypt;
+#else
+ purple_debug_error("cipher-aes", "No matching encrypt_func\n");
+ return -1;
+#endif
+
+ succ = encrypt_func(input_padded, output, out_len, priv->iv,
+ priv->key, priv->key_size, priv->batch_mode);
+
+ memset(input_padded, 0, out_len);
+ g_free(input_padded);
+
+ if (!succ) {
+ memset(output, 0, out_len);
+ return -1;
+ }
+
+ return out_len;
+}
+
+static ssize_t
+purple_aes_cipher_decrypt(PurpleCipher *cipher, const guchar input[],
+ size_t in_len, guchar output[], size_t out_size)
+{
+ PurpleAESCipherPrivate *priv = PURPLE_AES_CIPHER_GET_PRIVATE(cipher);
+ purple_aes_cipher_crypt_func decrypt_func;
+ gboolean succ;
+ ssize_t out_len;
+
+ if (priv->failure)
+ return -1;
+
+ if (in_len > out_size) {
+ purple_debug_error("cipher-aes", "Output buffer too small\n");
+ return -1;
+ }
+
+ if ((in_len % PURPLE_AES_BLOCK_SIZE) != 0 || in_len == 0) {
+ purple_debug_error("cipher-aes", "Malformed data\n");
+ return -1;
+ }
+
+#if defined(PURPLE_AES_USE_GNUTLS)
+ decrypt_func = purple_aes_cipher_gnutls_decrypt;
+#elif defined(PURPLE_AES_USE_NSS)
+ decrypt_func = purple_aes_cipher_nss_decrypt;
+#else
+ purple_debug_error("cipher-aes", "No matching decrypt_func\n");
+ return -1;
+#endif
+
+ succ = decrypt_func(input, output, in_len, priv->iv, priv->key,
+ priv->key_size, priv->batch_mode);
+
+ if (!succ) {
+ memset(output, 0, in_len);
+ return -1;
+ }
+
+ out_len = purple_aes_cipher_unpad_pkcs7(output, in_len);
+ if (out_len < 0) {
+ memset(output, 0, in_len);
+ return -1;
+ }
+
+ return out_len;
+}
+
+static size_t
+purple_aes_cipher_get_key_size(PurpleCipher *cipher)
+{
+ PurpleAESCipherPrivate *priv = PURPLE_AES_CIPHER_GET_PRIVATE(cipher);
+
+ return priv->key_size;
+}
+
+static void
+purple_aes_cipher_set_batch_mode(PurpleCipher *cipher,
+ PurpleCipherBatchMode mode)
+{
+ PurpleAESCipherPrivate *priv = PURPLE_AES_CIPHER_GET_PRIVATE(cipher);
+
+ if (mode != PURPLE_CIPHER_BATCH_MODE_CBC &&
+ mode != PURPLE_CIPHER_BATCH_MODE_ECB)
+ {
+ purple_debug_error("cipher-aes", "unsupported batch mode\n");
+ priv->failure = TRUE;
+ }
+
+ priv->batch_mode = mode;
+
+ g_object_notify_by_pspec(G_OBJECT(cipher), properties[PROP_BATCH_MODE]);
+}
+
+static PurpleCipherBatchMode
+purple_aes_cipher_get_batch_mode(PurpleCipher *cipher)
+{
+ PurpleAESCipherPrivate *priv = PURPLE_AES_CIPHER_GET_PRIVATE(cipher);
+
+ return priv->batch_mode;
+}
+
+static size_t
+purple_aes_cipher_get_block_size(PurpleCipher *cipher)
+{
+ return PURPLE_AES_BLOCK_SIZE;
+}
+
+/******************************************************************************
+ * Object Stuff
+ *****************************************************************************/
+static void
+purple_aes_cipher_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleCipher *cipher = PURPLE_CIPHER(obj);
+
+ switch(param_id) {
+ case PROP_BATCH_MODE:
+ g_value_set_enum(value,
+ purple_cipher_get_batch_mode(cipher));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_aes_cipher_set_property(GObject *obj, guint param_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ PurpleCipher *cipher = PURPLE_CIPHER(obj);
+
+ switch(param_id) {
+ case PROP_BATCH_MODE:
+ purple_cipher_set_batch_mode(cipher,
+ g_value_get_enum(value));
+ break;
+ case PROP_IV:
+ {
+ guchar *iv = (guchar *)g_value_get_string(value);
+ purple_cipher_set_iv(cipher, iv, strlen((gchar*)iv));
+ }
+ break;
+ case PROP_KEY:
+ purple_cipher_set_key(cipher, (guchar *)g_value_get_string(value),
+ purple_aes_cipher_get_key_size(cipher));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_aes_cipher_class_init(PurpleAESCipherClass *klass) {
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+ PurpleCipherClass *cipher_class = PURPLE_CIPHER_CLASS(klass);
+
+ obj_class->get_property = purple_aes_cipher_get_property;
+ obj_class->set_property = purple_aes_cipher_set_property;
+
+ cipher_class->reset = purple_aes_cipher_reset;
+ cipher_class->set_iv = purple_aes_cipher_set_iv;
+ cipher_class->encrypt = purple_aes_cipher_encrypt;
+ cipher_class->decrypt = purple_aes_cipher_decrypt;
+ cipher_class->set_key = purple_aes_cipher_set_key;
+ cipher_class->get_key_size = purple_aes_cipher_get_key_size;
+ cipher_class->set_batch_mode = purple_aes_cipher_set_batch_mode;
+ cipher_class->get_batch_mode = purple_aes_cipher_get_batch_mode;
+ cipher_class->get_block_size = purple_aes_cipher_get_block_size;
+
+ g_type_class_add_private(klass, sizeof(PurpleAESCipherPrivate));
+
+ properties[PROP_BATCH_MODE] = g_param_spec_enum("batch-mode",
+ "batch-mode", "batch-mode", PURPLE_TYPE_CIPHER_BATCH_MODE,
+ PURPLE_CIPHER_BATCH_MODE_CBC,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_IV] = g_param_spec_string("iv", "iv", "iv", NULL,
+ G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_KEY] = g_param_spec_string("key", "key", "key", NULL,
+ G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, PROP_LAST, properties);
+}
+
+static void
+purple_aes_cipher_init(PurpleCipher *cipher) {
+ purple_cipher_reset(cipher);
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+GType
+purple_aes_cipher_get_type(void) {
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleAESCipherClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_aes_cipher_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleAESCipher),
+ 0,
+ (GInstanceInitFunc)purple_aes_cipher_init,
+ NULL
+ };
+
+ type = g_type_register_static(PURPLE_TYPE_CIPHER,
+ "PurpleAESCipher",
+ &info, 0);
+ }
+
+ return type;
+}
+
+PurpleCipher *
+purple_aes_cipher_new(void) {
+ return g_object_new(PURPLE_TYPE_AES_CIPHER, NULL);
+}
diff --git a/libpurple/ciphers/aescipher.h b/libpurple/ciphers/aescipher.h
new file mode 100644
index 0000000000..8982ffa702
--- /dev/null
+++ b/libpurple/ciphers/aescipher.h
@@ -0,0 +1,65 @@
+/* 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 PURPLE_AES_CIPHER_H
+#define PURPLE_AES_CIPHER_H
+/**
+ * SECTION:aescipher
+ * @section_id: libpurple-aescipher
+ * @short_description: <filename>ciphers/aescipher.h</filename>
+ * @title: AES Cipher
+ */
+
+#include "cipher.h"
+
+#define PURPLE_TYPE_AES_CIPHER (purple_aes_cipher_get_type())
+#define PURPLE_AES_CIPHER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_AES_CIPHER, PurpleAESCipher))
+#define PURPLE_AES_CIPHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_AES_CIPHER, PurpleAESCipherClass))
+#define PURPLE_IS_AES_CIPHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_AES_CIPHER))
+#define PURPLE_IS_AES_CIPHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), PURPLE_TYPE_AES_CIPHER))
+#define PURPLE_AES_CIPHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_AES_CIPHER, PurpleAESCipherClass))
+
+typedef struct _PurpleAESCipher PurpleAESCipher;
+typedef struct _PurpleAESCipherClass PurpleAESCipherClass;
+
+struct _PurpleAESCipher {
+ PurpleCipher gparent;
+};
+
+struct _PurpleAESCipherClass {
+ PurpleCipherClass gparent;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+GType purple_aes_cipher_get_type(void);
+
+PurpleCipher *purple_aes_cipher_new(void);
+
+G_END_DECLS
+
+#endif /* PURPLE_AES_CIPHER_H */
diff --git a/libpurple/ciphers/des.c b/libpurple/ciphers/des.c
deleted file mode 100644
index 50083bf180..0000000000
--- a/libpurple/ciphers/des.c
+++ /dev/null
@@ -1,846 +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.
- *
- * Original des taken from gpg
- *
- * des.c - DES and Triple-DES encryption/decryption Algorithm
- * Copyright (C) 1998 Free Software Foundation, Inc.
- *
- * Please see below for more legal information!
- *
- * According to the definition of DES in FIPS PUB 46-2 from December 1993.
- * For a description of triple encryption, see:
- * Bruce Schneier: Applied Cryptography. Second Edition.
- * John Wiley & Sons, 1996. ISBN 0-471-12845-7. Pages 358 ff.
- *
- * This file is part of GnuPG.
- *
- * 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 <cipher.h>
-
-/******************************************************************************
- * DES
- *****************************************************************************/
-typedef struct _des_ctx
-{
- guint32 encrypt_subkeys[32];
- guint32 decrypt_subkeys[32];
-} des_ctx[1];
-
-/*
- * The s-box values are permuted according to the 'primitive function P'
- */
-static const guint32 sbox1[64] =
-{
- 0x00808200, 0x00000000, 0x00008000, 0x00808202, 0x00808002, 0x00008202, 0x00000002, 0x00008000,
- 0x00000200, 0x00808200, 0x00808202, 0x00000200, 0x00800202, 0x00808002, 0x00800000, 0x00000002,
- 0x00000202, 0x00800200, 0x00800200, 0x00008200, 0x00008200, 0x00808000, 0x00808000, 0x00800202,
- 0x00008002, 0x00800002, 0x00800002, 0x00008002, 0x00000000, 0x00000202, 0x00008202, 0x00800000,
- 0x00008000, 0x00808202, 0x00000002, 0x00808000, 0x00808200, 0x00800000, 0x00800000, 0x00000200,
- 0x00808002, 0x00008000, 0x00008200, 0x00800002, 0x00000200, 0x00000002, 0x00800202, 0x00008202,
- 0x00808202, 0x00008002, 0x00808000, 0x00800202, 0x00800002, 0x00000202, 0x00008202, 0x00808200,
- 0x00000202, 0x00800200, 0x00800200, 0x00000000, 0x00008002, 0x00008200, 0x00000000, 0x00808002
-};
-
-static const guint32 sbox2[64] =
-{
- 0x40084010, 0x40004000, 0x00004000, 0x00084010, 0x00080000, 0x00000010, 0x40080010, 0x40004010,
- 0x40000010, 0x40084010, 0x40084000, 0x40000000, 0x40004000, 0x00080000, 0x00000010, 0x40080010,
- 0x00084000, 0x00080010, 0x40004010, 0x00000000, 0x40000000, 0x00004000, 0x00084010, 0x40080000,
- 0x00080010, 0x40000010, 0x00000000, 0x00084000, 0x00004010, 0x40084000, 0x40080000, 0x00004010,
- 0x00000000, 0x00084010, 0x40080010, 0x00080000, 0x40004010, 0x40080000, 0x40084000, 0x00004000,
- 0x40080000, 0x40004000, 0x00000010, 0x40084010, 0x00084010, 0x00000010, 0x00004000, 0x40000000,
- 0x00004010, 0x40084000, 0x00080000, 0x40000010, 0x00080010, 0x40004010, 0x40000010, 0x00080010,
- 0x00084000, 0x00000000, 0x40004000, 0x00004010, 0x40000000, 0x40080010, 0x40084010, 0x00084000
-};
-
-static const guint32 sbox3[64] =
-{
- 0x00000104, 0x04010100, 0x00000000, 0x04010004, 0x04000100, 0x00000000, 0x00010104, 0x04000100,
- 0x00010004, 0x04000004, 0x04000004, 0x00010000, 0x04010104, 0x00010004, 0x04010000, 0x00000104,
- 0x04000000, 0x00000004, 0x04010100, 0x00000100, 0x00010100, 0x04010000, 0x04010004, 0x00010104,
- 0x04000104, 0x00010100, 0x00010000, 0x04000104, 0x00000004, 0x04010104, 0x00000100, 0x04000000,
- 0x04010100, 0x04000000, 0x00010004, 0x00000104, 0x00010000, 0x04010100, 0x04000100, 0x00000000,
- 0x00000100, 0x00010004, 0x04010104, 0x04000100, 0x04000004, 0x00000100, 0x00000000, 0x04010004,
- 0x04000104, 0x00010000, 0x04000000, 0x04010104, 0x00000004, 0x00010104, 0x00010100, 0x04000004,
- 0x04010000, 0x04000104, 0x00000104, 0x04010000, 0x00010104, 0x00000004, 0x04010004, 0x00010100
-};
-
-static const guint32 sbox4[64] =
-{
- 0x80401000, 0x80001040, 0x80001040, 0x00000040, 0x00401040, 0x80400040, 0x80400000, 0x80001000,
- 0x00000000, 0x00401000, 0x00401000, 0x80401040, 0x80000040, 0x00000000, 0x00400040, 0x80400000,
- 0x80000000, 0x00001000, 0x00400000, 0x80401000, 0x00000040, 0x00400000, 0x80001000, 0x00001040,
- 0x80400040, 0x80000000, 0x00001040, 0x00400040, 0x00001000, 0x00401040, 0x80401040, 0x80000040,
- 0x00400040, 0x80400000, 0x00401000, 0x80401040, 0x80000040, 0x00000000, 0x00000000, 0x00401000,
- 0x00001040, 0x00400040, 0x80400040, 0x80000000, 0x80401000, 0x80001040, 0x80001040, 0x00000040,
- 0x80401040, 0x80000040, 0x80000000, 0x00001000, 0x80400000, 0x80001000, 0x00401040, 0x80400040,
- 0x80001000, 0x00001040, 0x00400000, 0x80401000, 0x00000040, 0x00400000, 0x00001000, 0x00401040
-};
-
-static const guint32 sbox5[64] =
-{
- 0x00000080, 0x01040080, 0x01040000, 0x21000080, 0x00040000, 0x00000080, 0x20000000, 0x01040000,
- 0x20040080, 0x00040000, 0x01000080, 0x20040080, 0x21000080, 0x21040000, 0x00040080, 0x20000000,
- 0x01000000, 0x20040000, 0x20040000, 0x00000000, 0x20000080, 0x21040080, 0x21040080, 0x01000080,
- 0x21040000, 0x20000080, 0x00000000, 0x21000000, 0x01040080, 0x01000000, 0x21000000, 0x00040080,
- 0x00040000, 0x21000080, 0x00000080, 0x01000000, 0x20000000, 0x01040000, 0x21000080, 0x20040080,
- 0x01000080, 0x20000000, 0x21040000, 0x01040080, 0x20040080, 0x00000080, 0x01000000, 0x21040000,
- 0x21040080, 0x00040080, 0x21000000, 0x21040080, 0x01040000, 0x00000000, 0x20040000, 0x21000000,
- 0x00040080, 0x01000080, 0x20000080, 0x00040000, 0x00000000, 0x20040000, 0x01040080, 0x20000080
-};
-
-static const guint32 sbox6[64] =
-{
- 0x10000008, 0x10200000, 0x00002000, 0x10202008, 0x10200000, 0x00000008, 0x10202008, 0x00200000,
- 0x10002000, 0x00202008, 0x00200000, 0x10000008, 0x00200008, 0x10002000, 0x10000000, 0x00002008,
- 0x00000000, 0x00200008, 0x10002008, 0x00002000, 0x00202000, 0x10002008, 0x00000008, 0x10200008,
- 0x10200008, 0x00000000, 0x00202008, 0x10202000, 0x00002008, 0x00202000, 0x10202000, 0x10000000,
- 0x10002000, 0x00000008, 0x10200008, 0x00202000, 0x10202008, 0x00200000, 0x00002008, 0x10000008,
- 0x00200000, 0x10002000, 0x10000000, 0x00002008, 0x10000008, 0x10202008, 0x00202000, 0x10200000,
- 0x00202008, 0x10202000, 0x00000000, 0x10200008, 0x00000008, 0x00002000, 0x10200000, 0x00202008,
- 0x00002000, 0x00200008, 0x10002008, 0x00000000, 0x10202000, 0x10000000, 0x00200008, 0x10002008
-};
-
-static const guint32 sbox7[64] =
-{
- 0x00100000, 0x02100001, 0x02000401, 0x00000000, 0x00000400, 0x02000401, 0x00100401, 0x02100400,
- 0x02100401, 0x00100000, 0x00000000, 0x02000001, 0x00000001, 0x02000000, 0x02100001, 0x00000401,
- 0x02000400, 0x00100401, 0x00100001, 0x02000400, 0x02000001, 0x02100000, 0x02100400, 0x00100001,
- 0x02100000, 0x00000400, 0x00000401, 0x02100401, 0x00100400, 0x00000001, 0x02000000, 0x00100400,
- 0x02000000, 0x00100400, 0x00100000, 0x02000401, 0x02000401, 0x02100001, 0x02100001, 0x00000001,
- 0x00100001, 0x02000000, 0x02000400, 0x00100000, 0x02100400, 0x00000401, 0x00100401, 0x02100400,
- 0x00000401, 0x02000001, 0x02100401, 0x02100000, 0x00100400, 0x00000000, 0x00000001, 0x02100401,
- 0x00000000, 0x00100401, 0x02100000, 0x00000400, 0x02000001, 0x02000400, 0x00000400, 0x00100001
-};
-
-static const guint32 sbox8[64] =
-{
- 0x08000820, 0x00000800, 0x00020000, 0x08020820, 0x08000000, 0x08000820, 0x00000020, 0x08000000,
- 0x00020020, 0x08020000, 0x08020820, 0x00020800, 0x08020800, 0x00020820, 0x00000800, 0x00000020,
- 0x08020000, 0x08000020, 0x08000800, 0x00000820, 0x00020800, 0x00020020, 0x08020020, 0x08020800,
- 0x00000820, 0x00000000, 0x00000000, 0x08020020, 0x08000020, 0x08000800, 0x00020820, 0x00020000,
- 0x00020820, 0x00020000, 0x08020800, 0x00000800, 0x00000020, 0x08020020, 0x00000800, 0x00020820,
- 0x08000800, 0x00000020, 0x08000020, 0x08020000, 0x08020020, 0x08000000, 0x00020000, 0x08000820,
- 0x00000000, 0x08020820, 0x00020020, 0x08000020, 0x08020000, 0x08000800, 0x08000820, 0x00000000,
- 0x08020820, 0x00020800, 0x00020800, 0x00000820, 0x00000820, 0x00020020, 0x08000000, 0x08020800
-};
-
-
-/*
- * * These two tables are part of the 'permuted choice 1' function.
- * * In this implementation several speed improvements are done.
- * */
-static const guint32 leftkey_swap[16] =
-{
- 0x00000000, 0x00000001, 0x00000100, 0x00000101,
- 0x00010000, 0x00010001, 0x00010100, 0x00010101,
- 0x01000000, 0x01000001, 0x01000100, 0x01000101,
- 0x01010000, 0x01010001, 0x01010100, 0x01010101
-};
-
-static const guint32 rightkey_swap[16] =
-{
- 0x00000000, 0x01000000, 0x00010000, 0x01010000,
- 0x00000100, 0x01000100, 0x00010100, 0x01010100,
- 0x00000001, 0x01000001, 0x00010001, 0x01010001,
- 0x00000101, 0x01000101, 0x00010101, 0x01010101,
-};
-
-
-/*
- * Numbers of left shifts per round for encryption subkey schedule
- * To calculate the decryption key scheduling we just reverse the
- * ordering of the subkeys so we can omit the table for decryption
- * subkey schedule.
- */
-static const guint8 encrypt_rotate_tab[16] =
-{
- 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
-};
-
-/*
- * Macro to swap bits across two words
- **/
-#define DO_PERMUTATION(a, temp, b, offset, mask) \
- temp = ((a>>offset) ^ b) & mask; \
- b ^= temp; \
- a ^= temp<<offset;
-
-
-/*
- * This performs the 'initial permutation' for the data to be encrypted or decrypted
- **/
-#define INITIAL_PERMUTATION(left, temp, right) \
- DO_PERMUTATION(left, temp, right, 4, 0x0f0f0f0f) \
- DO_PERMUTATION(left, temp, right, 16, 0x0000ffff) \
- DO_PERMUTATION(right, temp, left, 2, 0x33333333) \
- DO_PERMUTATION(right, temp, left, 8, 0x00ff00ff) \
- DO_PERMUTATION(left, temp, right, 1, 0x55555555)
-
-
-/*
- * The 'inverse initial permutation'
- **/
-#define FINAL_PERMUTATION(left, temp, right) \
- DO_PERMUTATION(left, temp, right, 1, 0x55555555) \
- DO_PERMUTATION(right, temp, left, 8, 0x00ff00ff) \
- DO_PERMUTATION(right, temp, left, 2, 0x33333333) \
- DO_PERMUTATION(left, temp, right, 16, 0x0000ffff) \
- DO_PERMUTATION(left, temp, right, 4, 0x0f0f0f0f)
-
-
-/*
- * A full DES round including 'expansion function', 'sbox substitution'
- * and 'primitive function P' but without swapping the left and right word.
- **/
-#define DES_ROUND(from, to, work, subkey) \
- work = ((from<<1) | (from>>31)) ^ *subkey++; \
- to ^= sbox8[ work & 0x3f ]; \
- to ^= sbox6[ (work>>8) & 0x3f ]; \
- to ^= sbox4[ (work>>16) & 0x3f ]; \
- to ^= sbox2[ (work>>24) & 0x3f ]; \
- work = ((from>>3) | (from<<29)) ^ *subkey++; \
- to ^= sbox7[ work & 0x3f ]; \
- to ^= sbox5[ (work>>8) & 0x3f ]; \
- to ^= sbox3[ (work>>16) & 0x3f ]; \
- to ^= sbox1[ (work>>24) & 0x3f ];
-
-
-/*
- * Macros to convert 8 bytes from/to 32bit words
- **/
-#define READ_64BIT_DATA(data, left, right) \
- left = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; \
- right = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7];
-
-#define WRITE_64BIT_DATA(data, left, right) \
- data[0] = (left >> 24) &0xff; data[1] = (left >> 16) &0xff; \
- data[2] = (left >> 8) &0xff; data[3] = left &0xff; \
- data[4] = (right >> 24) &0xff; data[5] = (right >> 16) &0xff; \
- data[6] = (right >> 8) &0xff; data[7] = right &0xff;
-
-
-/*
- * des_key_schedule(): Calculate 16 subkeys pairs (even/odd) for
- * 16 encryption rounds.
- * To calculate subkeys for decryption the caller
- * have to reorder the generated subkeys.
- *
- * rawkey: 8 Bytes of key data
- * subkey: Array of at least 32 guint32s. Will be filled
- * with calculated subkeys.
- *
- **/
-static void
-des_key_schedule (const guint8 * rawkey, guint32 * subkey)
-{
- guint32 left, right, work;
- int round;
-
- READ_64BIT_DATA (rawkey, left, right)
-
- DO_PERMUTATION (right, work, left, 4, 0x0f0f0f0f)
- DO_PERMUTATION (right, work, left, 0, 0x10101010)
-
- left = (leftkey_swap[(left >> 0) & 0xf] << 3) | (leftkey_swap[(left >> 8) & 0xf] << 2)
- | (leftkey_swap[(left >> 16) & 0xf] << 1) | (leftkey_swap[(left >> 24) & 0xf])
- | (leftkey_swap[(left >> 5) & 0xf] << 7) | (leftkey_swap[(left >> 13) & 0xf] << 6)
- | (leftkey_swap[(left >> 21) & 0xf] << 5) | (leftkey_swap[(left >> 29) & 0xf] << 4);
-
- left &= 0x0fffffff;
-
- right = (rightkey_swap[(right >> 1) & 0xf] << 3) | (rightkey_swap[(right >> 9) & 0xf] << 2)
- | (rightkey_swap[(right >> 17) & 0xf] << 1) | (rightkey_swap[(right >> 25) & 0xf])
- | (rightkey_swap[(right >> 4) & 0xf] << 7) | (rightkey_swap[(right >> 12) & 0xf] << 6)
- | (rightkey_swap[(right >> 20) & 0xf] << 5) | (rightkey_swap[(right >> 28) & 0xf] << 4);
-
- right &= 0x0fffffff;
-
- for (round = 0; round < 16; ++round)
- {
- left = ((left << encrypt_rotate_tab[round]) | (left >> (28 - encrypt_rotate_tab[round]))) & 0x0fffffff;
- right = ((right << encrypt_rotate_tab[round]) | (right >> (28 - encrypt_rotate_tab[round]))) & 0x0fffffff;
-
- *subkey++ = ((left << 4) & 0x24000000)
- | ((left << 28) & 0x10000000)
- | ((left << 14) & 0x08000000)
- | ((left << 18) & 0x02080000)
- | ((left << 6) & 0x01000000)
- | ((left << 9) & 0x00200000)
- | ((left >> 1) & 0x00100000)
- | ((left << 10) & 0x00040000)
- | ((left << 2) & 0x00020000)
- | ((left >> 10) & 0x00010000)
- | ((right >> 13) & 0x00002000)
- | ((right >> 4) & 0x00001000)
- | ((right << 6) & 0x00000800)
- | ((right >> 1) & 0x00000400)
- | ((right >> 14) & 0x00000200)
- | (right & 0x00000100)
- | ((right >> 5) & 0x00000020)
- | ((right >> 10) & 0x00000010)
- | ((right >> 3) & 0x00000008)
- | ((right >> 18) & 0x00000004)
- | ((right >> 26) & 0x00000002)
- | ((right >> 24) & 0x00000001);
-
- *subkey++ = ((left << 15) & 0x20000000)
- | ((left << 17) & 0x10000000)
- | ((left << 10) & 0x08000000)
- | ((left << 22) & 0x04000000)
- | ((left >> 2) & 0x02000000)
- | ((left << 1) & 0x01000000)
- | ((left << 16) & 0x00200000)
- | ((left << 11) & 0x00100000)
- | ((left << 3) & 0x00080000)
- | ((left >> 6) & 0x00040000)
- | ((left << 15) & 0x00020000)
- | ((left >> 4) & 0x00010000)
- | ((right >> 2) & 0x00002000)
- | ((right << 8) & 0x00001000)
- | ((right >> 14) & 0x00000808)
- | ((right >> 9) & 0x00000400)
- | ((right) & 0x00000200)
- | ((right << 7) & 0x00000100)
- | ((right >> 7) & 0x00000020)
- | ((right >> 3) & 0x00000011)
- | ((right << 2) & 0x00000004)
- | ((right >> 21) & 0x00000002);
- }
-}
-
-
-/*
- * Fill a DES context with subkeys calculated from a 64bit key.
- * Does not check parity bits, but simply ignore them.
- * Does not check for weak keys.
- **/
-static void
-des_set_key (PurpleCipherContext *context, const guchar * key)
-{
- struct _des_ctx *ctx = purple_cipher_context_get_data(context);
- int i;
-
- des_key_schedule (key, ctx->encrypt_subkeys);
-
- for(i=0; i<32; i+=2)
- {
- ctx->decrypt_subkeys[i] = ctx->encrypt_subkeys[30-i];
- ctx->decrypt_subkeys[i+1] = ctx->encrypt_subkeys[31-i];
- }
-}
-
-
-/*
- * Electronic Codebook Mode DES encryption/decryption of data according
- * to 'mode'.
- **/
-static int
-des_ecb_crypt (struct _des_ctx *ctx, const guint8 * from, guint8 * to, int mode)
-{
- guint32 left, right, work;
- guint32 *keys;
-
- keys = mode ? ctx->decrypt_subkeys : ctx->encrypt_subkeys;
-
- READ_64BIT_DATA (from, left, right)
- INITIAL_PERMUTATION (left, work, right)
-
- DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
- DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
- DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
- DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
- DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
- DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
- DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
- DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
-
- FINAL_PERMUTATION (right, work, left)
- WRITE_64BIT_DATA (to, right, left)
-
- return 0;
-}
-
-static gint
-des_encrypt(PurpleCipherContext *context, const guchar data[],
- size_t len, guchar output[], size_t *outlen)
-{
- int offset = 0;
- int i = 0;
- int tmp;
- guint8 buf[8] = {0,0,0,0,0,0,0,0};
- while(offset+8<=len) {
- des_ecb_crypt(purple_cipher_context_get_data(context),
- data+offset,
- output+offset,
- 0);
- offset+=8;
- }
- *outlen = len;
- if(offset<len) {
- *outlen += len - offset;
- tmp = offset;
- while(tmp<len) {
- buf[i++] = data[tmp];
- tmp++;
- }
- des_ecb_crypt(purple_cipher_context_get_data(context),
- buf,
- output+offset,
- 0);
- }
- return 0;
-}
-
-static gint
-des_decrypt(PurpleCipherContext *context, const guchar data[],
- size_t len, guchar output[], size_t *outlen)
-{
- int offset = 0;
- int i = 0;
- int tmp;
- guint8 buf[8] = {0,0,0,0,0,0,0,0};
- while(offset+8<=len) {
- des_ecb_crypt(purple_cipher_context_get_data(context),
- data+offset,
- output+offset,
- 1);
- offset+=8;
- }
- *outlen = len;
- if(offset<len) {
- *outlen += len - offset;
- tmp = offset;
- while(tmp<len) {
- buf[i++] = data[tmp];
- tmp++;
- }
- des_ecb_crypt(purple_cipher_context_get_data(context),
- buf,
- output+offset,
- 1);
- }
- return 0;
-}
-
-static void
-des_init(PurpleCipherContext *context, gpointer extra) {
- struct _des_ctx *mctx;
- mctx = g_new0(struct _des_ctx, 1);
- purple_cipher_context_set_data(context, mctx);
-}
-
-static void
-des_uninit(PurpleCipherContext *context) {
- struct _des_ctx *des_context;
-
- des_context = purple_cipher_context_get_data(context);
- memset(des_context, 0, sizeof(*des_context));
-
- g_free(des_context);
- des_context = NULL;
-}
-
-static PurpleCipherOps DESOps = {
- NULL, /* Set option */
- NULL, /* Get option */
- des_init, /* init */
- NULL, /* reset */
- des_uninit, /* uninit */
- NULL, /* set iv */
- NULL, /* append */
- NULL, /* digest */
- des_encrypt, /* encrypt */
- des_decrypt, /* decrypt */
- NULL, /* set salt */
- NULL, /* get salt size */
- des_set_key, /* set key */
- NULL, /* get key size */
- NULL, /* set batch mode */
- NULL, /* get batch mode */
- NULL, /* get block size */
- NULL /* set key with len */
-};
-
-/******************************************************************************
- * Triple-DES
- *****************************************************************************/
-
-typedef struct _des3_ctx
-{
- PurpleCipherBatchMode mode;
- guchar iv[8];
- /* First key for encryption */
- struct _des_ctx key1;
- /* Second key for decryption */
- struct _des_ctx key2;
- /* Third key for encryption */
- struct _des_ctx key3;
-} des3_ctx[1];
-
-/*
- * Fill a DES3 context with subkeys calculated from 3 64bit key.
- * Does not check parity bits, but simply ignore them.
- * Does not check for weak keys.
- **/
-static void
-des3_set_key(PurpleCipherContext *context, const guchar * key)
-{
- struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
- int i;
-
- des_key_schedule (key + 0, ctx->key1.encrypt_subkeys);
- des_key_schedule (key + 8, ctx->key2.encrypt_subkeys);
- des_key_schedule (key + 16, ctx->key3.encrypt_subkeys);
-
- for (i = 0; i < 32; i += 2)
- {
- ctx->key1.decrypt_subkeys[i] = ctx->key1.encrypt_subkeys[30-i];
- ctx->key1.decrypt_subkeys[i+1] = ctx->key1.encrypt_subkeys[31-i];
- ctx->key2.decrypt_subkeys[i] = ctx->key2.encrypt_subkeys[30-i];
- ctx->key2.decrypt_subkeys[i+1] = ctx->key2.encrypt_subkeys[31-i];
- ctx->key3.decrypt_subkeys[i] = ctx->key3.encrypt_subkeys[30-i];
- ctx->key3.decrypt_subkeys[i+1] = ctx->key3.encrypt_subkeys[31-i];
- }
-}
-
-static gint
-des3_ecb_encrypt(struct _des3_ctx *ctx, const guchar data[],
- size_t len, guchar output[], size_t *outlen)
-{
- int offset = 0;
- int i = 0;
- int tmp;
- guint8 buf[8] = {0,0,0,0,0,0,0,0};
- while (offset + 8 <= len) {
- des_ecb_crypt(&ctx->key1,
- data+offset,
- output+offset,
- 0);
- des_ecb_crypt(&ctx->key2,
- output+offset,
- buf,
- 1);
- des_ecb_crypt(&ctx->key3,
- buf,
- output+offset,
- 0);
- offset += 8;
- }
- *outlen = len;
- if (offset < len) {
- *outlen += len - offset;
- tmp = offset;
- memset(buf, 0, 8);
- while (tmp < len) {
- buf[i++] = data[tmp];
- tmp++;
- }
- des_ecb_crypt(&ctx->key1,
- buf,
- output+offset,
- 0);
- des_ecb_crypt(&ctx->key2,
- output+offset,
- buf,
- 1);
- des_ecb_crypt(&ctx->key3,
- buf,
- output+offset,
- 0);
- }
- return 0;
-}
-
-static gint
-des3_cbc_encrypt(struct _des3_ctx *ctx, const guchar data[],
- size_t len, guchar output[], size_t *outlen)
-{
- int offset = 0;
- int i = 0;
- int tmp;
- guint8 buf[8];
- memcpy(buf, ctx->iv, 8);
- while (offset + 8 <= len) {
- for (i = 0; i < 8; i++)
- buf[i] ^= data[offset + i];
-
- des_ecb_crypt(&ctx->key1,
- buf,
- output+offset,
- 0);
- des_ecb_crypt(&ctx->key2,
- output+offset,
- buf,
- 1);
- des_ecb_crypt(&ctx->key3,
- buf,
- output+offset,
- 0);
- memcpy(buf, output+offset, 8);
- offset += 8;
- }
- *outlen = len;
- if (offset < len) {
- *outlen += len - offset;
- tmp = offset;
- i = 0;
- while (tmp < len) {
- buf[i++] ^= data[tmp];
- tmp++;
- }
- des_ecb_crypt(&ctx->key1,
- buf,
- output+offset,
- 0);
- des_ecb_crypt(&ctx->key2,
- output+offset,
- buf,
- 1);
- des_ecb_crypt(&ctx->key3,
- buf,
- output+offset,
- 0);
- }
- return 0;
-}
-
-static gint
-des3_encrypt(PurpleCipherContext *context, const guchar data[],
- size_t len, guchar output[], size_t *outlen)
-{
- struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
-
- if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_ECB) {
- return des3_ecb_encrypt(ctx, data, len, output, outlen);
- } else if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_CBC) {
- return des3_cbc_encrypt(ctx, data, len, output, outlen);
- } else {
- g_return_val_if_reached(0);
- }
-
- return 0;
-}
-
-static gint
-des3_ecb_decrypt(struct _des3_ctx *ctx, const guchar data[],
- size_t len, guchar output[], size_t *outlen)
-{
- int offset = 0;
- int i = 0;
- int tmp;
- guint8 buf[8] = {0,0,0,0,0,0,0,0};
- while (offset + 8 <= len) {
- /* NOTE: Apply key in reverse */
- des_ecb_crypt(&ctx->key3,
- data+offset,
- output+offset,
- 1);
- des_ecb_crypt(&ctx->key2,
- output+offset,
- buf,
- 0);
- des_ecb_crypt(&ctx->key1,
- buf,
- output+offset,
- 1);
- offset+=8;
- }
- *outlen = len;
- if (offset < len) {
- *outlen += len - offset;
- tmp = offset;
- memset(buf, 0, 8);
- while (tmp < len) {
- buf[i++] = data[tmp];
- tmp++;
- }
- des_ecb_crypt(&ctx->key3,
- buf,
- output+offset,
- 1);
- des_ecb_crypt(&ctx->key2,
- output+offset,
- buf,
- 0);
- des_ecb_crypt(&ctx->key1,
- buf,
- output+offset,
- 1);
- }
- return 0;
-}
-
-static gint
-des3_cbc_decrypt(struct _des3_ctx *ctx, const guchar data[],
- size_t len, guchar output[], size_t *outlen)
-{
- int offset = 0;
- int i = 0;
- int tmp;
- guint8 buf[8] = {0,0,0,0,0,0,0,0};
- guint8 link[8];
- memcpy(link, ctx->iv, 8);
- while (offset + 8 <= len) {
- des_ecb_crypt(&ctx->key3,
- data+offset,
- output+offset,
- 1);
- des_ecb_crypt(&ctx->key2,
- output+offset,
- buf,
- 0);
- des_ecb_crypt(&ctx->key1,
- buf,
- output+offset,
- 1);
- for (i = 0; i < 8; i++)
- output[offset + i] ^= link[i];
- memcpy(link, data + offset, 8);
- offset+=8;
- }
- *outlen = len;
- if(offset<len) {
- *outlen += len - offset;
- tmp = offset;
- memset(buf, 0, 8);
- i = 0;
- while(tmp<len) {
- buf[i++] = data[tmp];
- tmp++;
- }
- des_ecb_crypt(&ctx->key3,
- buf,
- output+offset,
- 1);
- des_ecb_crypt(&ctx->key2,
- output+offset,
- buf,
- 0);
- des_ecb_crypt(&ctx->key1,
- buf,
- output+offset,
- 1);
- for (i = 0; i < 8; i++)
- output[offset + i] ^= link[i];
- }
- return 0;
-}
-
-static gint
-des3_decrypt(PurpleCipherContext *context, const guchar data[],
- size_t len, guchar output[], size_t *outlen)
-{
- struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
-
- if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_ECB) {
- return des3_ecb_decrypt(ctx, data, len, output, outlen);
- } else if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_CBC) {
- return des3_cbc_decrypt(ctx, data, len, output, outlen);
- } else {
- g_return_val_if_reached(0);
- }
-
- return 0;
-}
-
-static void
-des3_set_batch(PurpleCipherContext *context, PurpleCipherBatchMode mode)
-{
- struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
-
- ctx->mode = mode;
-}
-
-static PurpleCipherBatchMode
-des3_get_batch(PurpleCipherContext *context)
-{
- struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
-
- return ctx->mode;
-}
-
-static void
-des3_set_iv(PurpleCipherContext *context, guchar *iv, size_t len)
-{
- struct _des3_ctx *ctx;
-
- g_return_if_fail(len == 8);
-
- ctx = purple_cipher_context_get_data(context);
-
- memcpy(ctx->iv, iv, len);
-}
-
-static void
-des3_init(PurpleCipherContext *context, gpointer extra)
-{
- struct _des3_ctx *mctx;
- mctx = g_new0(struct _des3_ctx, 1);
- purple_cipher_context_set_data(context, mctx);
-}
-
-static void
-des3_uninit(PurpleCipherContext *context)
-{
- struct _des3_ctx *des3_context;
-
- des3_context = purple_cipher_context_get_data(context);
- memset(des3_context, 0, sizeof(*des3_context));
-
- g_free(des3_context);
- des3_context = NULL;
-}
-
-static PurpleCipherOps DES3Ops = {
- NULL, /* Set option */
- NULL, /* Get option */
- des3_init, /* init */
- NULL, /* reset */
- des3_uninit, /* uninit */
- des3_set_iv, /* set iv */
- NULL, /* append */
- NULL, /* digest */
- des3_encrypt, /* encrypt */
- des3_decrypt, /* decrypt */
- NULL, /* set salt */
- NULL, /* get salt size */
- des3_set_key, /* set key */
- NULL, /* get key size */
- des3_set_batch, /* set batch mode */
- des3_get_batch, /* get batch mode */
- NULL, /* get block size */
- NULL /* set key with len */
-};
-
-/******************************************************************************
- * Registration
- *****************************************************************************/
-PurpleCipherOps *
-purple_des_cipher_get_ops(void) {
- return &DESOps;
-}
-
-PurpleCipherOps *
-purple_des3_cipher_get_ops(void) {
- return &DES3Ops;
-}
-
diff --git a/libpurple/ciphers/des3cipher.c b/libpurple/ciphers/des3cipher.c
new file mode 100644
index 0000000000..deb0546e2c
--- /dev/null
+++ b/libpurple/ciphers/des3cipher.c
@@ -0,0 +1,524 @@
+/*
+ * Original des taken from gpg
+ *
+ * des.c - Triple-DES encryption/decryption Algorithm
+ * Copyright (C) 1998 Free Software Foundation, Inc.
+ *
+ * Please see below for more legal information!
+ *
+ * According to the definition of DES in FIPS PUB 46-2 from December 1993.
+ * For a description of triple encryption, see:
+ * Bruce Schneier: Applied Cryptography. Second Edition.
+ * John Wiley & Sons, 1996. ISBN 0-471-12845-7. Pages 358 ff.
+ *
+ * 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 "glibcompat.h"
+
+#include "des3cipher.h"
+#include "descipher.h"
+#include "enums.h"
+
+#include <string.h>
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+
+#define PURPLE_DES3_CIPHER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_DES3_CIPHER, PurpleDES3CipherPrivate))
+
+typedef struct _PurpleDES3CipherPrivate PurpleDES3CipherPrivate;
+struct _PurpleDES3CipherPrivate
+{
+ PurpleCipherBatchMode mode;
+ guchar iv[8];
+ /* First key for encryption */
+ PurpleCipher *key1;
+ /* Second key for decryption */
+ PurpleCipher *key2;
+ /* Third key for encryption */
+ PurpleCipher *key3;
+};
+
+/******************************************************************************
+ * Enums
+ *****************************************************************************/
+enum {
+ PROP_NONE,
+ PROP_BATCH_MODE,
+ PROP_IV,
+ PROP_KEY,
+ PROP_LAST,
+};
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+static GObjectClass *parent_class = NULL;
+static GParamSpec *properties[PROP_LAST];
+
+/******************************************************************************
+ * Cipher Stuff
+ *****************************************************************************/
+
+static size_t
+purple_des3_cipher_get_key_size(PurpleCipher *cipher)
+{
+ return 24;
+}
+
+/*
+ * Fill a DES3 context with subkeys calculated from 3 64bit key.
+ * Does not check parity bits, but simply ignore them.
+ * Does not check for weak keys.
+ **/
+static void
+purple_des3_cipher_set_key(PurpleCipher *cipher, const guchar *key, size_t len)
+{
+ PurpleDES3Cipher *des3_cipher = PURPLE_DES3_CIPHER(cipher);
+ PurpleDES3CipherPrivate *priv = PURPLE_DES3_CIPHER_GET_PRIVATE(des3_cipher);
+
+ g_return_if_fail(len == 24);
+
+ purple_cipher_set_key(PURPLE_CIPHER(priv->key1), key + 0,
+ purple_cipher_get_key_size(PURPLE_CIPHER(priv->key1)));
+ purple_cipher_set_key(PURPLE_CIPHER(priv->key2), key + 8,
+ purple_cipher_get_key_size(PURPLE_CIPHER(priv->key2)));
+ purple_cipher_set_key(PURPLE_CIPHER(priv->key3), key + 16,
+ purple_cipher_get_key_size(PURPLE_CIPHER(priv->key3)));
+
+ g_object_notify_by_pspec(G_OBJECT(cipher), properties[PROP_KEY]);
+}
+
+static ssize_t
+purple_des3_cipher_ecb_encrypt(PurpleDES3Cipher *des3_cipher, const guchar input[],
+ size_t in_len, guchar output[], size_t out_size)
+{
+ gsize offset = 0;
+ int i = 0;
+ gsize tmp;
+ guint8 buf[8] = {0,0,0,0,0,0,0,0};
+ gsize out_len;
+ PurpleDES3CipherPrivate *priv = PURPLE_DES3_CIPHER_GET_PRIVATE(des3_cipher);
+
+ g_return_val_if_fail(out_size >= in_len, -1);
+
+ while (offset + 8 <= in_len) {
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key1),
+ input + offset, output + offset, 0);
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key2),
+ output + offset, buf, 1);
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key3),
+ buf, output + offset, 0);
+
+ offset += 8;
+ }
+
+ out_len = in_len;
+ if (offset < in_len) {
+ g_return_val_if_fail(in_len >= offset, -1);
+ out_len += in_len - offset;
+ g_return_val_if_fail(out_size >= out_len, -1);
+ tmp = offset;
+ memset(buf, 0, 8);
+ while (tmp < in_len) {
+ buf[i++] = input[tmp];
+ tmp++;
+ }
+
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key1),
+ buf, output + offset, 0);
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key2),
+ output + offset, buf, 1);
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key3),
+ buf, output + offset, 0);
+ }
+
+ return out_len;
+}
+
+static ssize_t
+purple_des3_cipher_cbc_encrypt(PurpleDES3Cipher *des3_cipher, const guchar input[],
+ size_t in_len, guchar output[], size_t out_size)
+{
+ gsize offset = 0;
+ int i = 0;
+ gsize tmp;
+ guint8 buf[8];
+ gsize out_len;
+ PurpleDES3CipherPrivate *priv = PURPLE_DES3_CIPHER_GET_PRIVATE(des3_cipher);
+
+ memcpy(buf, priv->iv, 8);
+
+ g_return_val_if_fail(out_size >= in_len, -1);
+
+ while (offset + 8 <= in_len) {
+ for (i = 0; i < 8; i++)
+ buf[i] ^= input[offset + i];
+
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key1),
+ buf, output + offset, 0);
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key2),
+ output + offset, buf, 1);
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key3),
+ buf, output + offset, 0);
+
+ memcpy(buf, output+offset, 8);
+ offset += 8;
+ }
+
+ out_len = in_len;
+ if (offset < in_len) {
+ g_return_val_if_fail(in_len >= offset, -1);
+ out_len += in_len - offset;
+ g_return_val_if_fail(out_size >= out_len, -1);
+ tmp = offset;
+ i = 0;
+ while (tmp < in_len) {
+ buf[i++] ^= input[tmp];
+ tmp++;
+ }
+
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key1),
+ buf, output + offset, 0);
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key2),
+ output + offset, buf, 1);
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key3),
+ buf, output + offset, 0);
+ }
+
+ return out_len;
+}
+
+static ssize_t
+purple_des3_cipher_encrypt(PurpleCipher *cipher, const guchar input[],
+ size_t in_len, guchar output[], size_t out_size)
+{
+ PurpleDES3Cipher *des3_cipher = PURPLE_DES3_CIPHER(cipher);
+ PurpleDES3CipherPrivate *priv = PURPLE_DES3_CIPHER_GET_PRIVATE(des3_cipher);
+
+ if (priv->mode == PURPLE_CIPHER_BATCH_MODE_ECB) {
+ return purple_des3_cipher_ecb_encrypt(des3_cipher, input, in_len, output, out_size);
+ } else if (priv->mode == PURPLE_CIPHER_BATCH_MODE_CBC) {
+ return purple_des3_cipher_cbc_encrypt(des3_cipher, input, in_len, output, out_size);
+ } else {
+ g_return_val_if_reached(0);
+ }
+
+ return 0;
+}
+
+static ssize_t
+purple_des3_cipher_ecb_decrypt(PurpleDES3Cipher *des3_cipher, const guchar input[],
+ size_t in_len, guchar output[], size_t out_size)
+{
+ gsize offset = 0;
+ int i = 0;
+ gsize tmp;
+ guint8 buf[8] = {0,0,0,0,0,0,0,0};
+ gsize out_len;
+ PurpleDES3CipherPrivate *priv = PURPLE_DES3_CIPHER_GET_PRIVATE(des3_cipher);
+
+ g_return_val_if_fail(out_size >= in_len, -1);
+
+ while (offset + 8 <= in_len) {
+ /* NOTE: Apply key in reverse */
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key3),
+ input + offset, output + offset, 1);
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key2),
+ output + offset, buf, 0);
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key1),
+ buf, output + offset, 1);
+
+ offset += 8;
+ }
+
+ out_len = in_len;
+ if (offset < in_len) {
+ g_return_val_if_fail(in_len >= offset, -1);
+ out_len += in_len - offset;
+ g_return_val_if_fail(out_size >= out_len, -1);
+ tmp = offset;
+ memset(buf, 0, 8);
+ while (tmp < in_len) {
+ buf[i++] = input[tmp];
+ tmp++;
+ }
+
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key3),
+ buf, output + offset, 1);
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key2),
+ output + offset, buf, 0);
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key1),
+ buf, output + offset, 1);
+ }
+
+ return out_len;
+}
+
+static ssize_t
+purple_des3_cipher_cbc_decrypt(PurpleDES3Cipher *des3_cipher, const guchar input[],
+ size_t in_len, guchar output[], size_t out_size)
+{
+ gsize offset = 0;
+ int i = 0;
+ gsize tmp;
+ guint8 buf[8] = {0,0,0,0,0,0,0,0};
+ guint8 link[8];
+ gsize out_len;
+ PurpleDES3CipherPrivate *priv = PURPLE_DES3_CIPHER_GET_PRIVATE(des3_cipher);
+
+ g_return_val_if_fail(out_size >= in_len, -1);
+
+ memcpy(link, priv->iv, 8);
+ while (offset + 8 <= in_len) {
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key3),
+ input + offset, output + offset, 1);
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key2),
+ output + offset, buf, 0);
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key1),
+ buf, output + offset, 1);
+
+ for (i = 0; i < 8; i++)
+ output[offset + i] ^= link[i];
+
+ memcpy(link, input + offset, 8);
+
+ offset+=8;
+ }
+
+ out_len = in_len;
+ if(offset<in_len) {
+ g_return_val_if_fail(in_len >= offset, -1);
+ out_len += in_len - offset;
+ g_return_val_if_fail(out_size >= out_len, -1);
+ tmp = offset;
+ memset(buf, 0, 8);
+ i = 0;
+ while(tmp<in_len) {
+ buf[i++] = input[tmp];
+ tmp++;
+ }
+
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key3),
+ buf, output + offset, 1);
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key2),
+ output + offset, buf, 0);
+ purple_des_cipher_ecb_crypt(PURPLE_DES_CIPHER(priv->key1),
+ buf, output + offset, 1);
+
+ for (i = 0; i < 8; i++)
+ output[offset + i] ^= link[i];
+ }
+
+ return out_len;
+}
+
+static ssize_t
+purple_des3_cipher_decrypt(PurpleCipher *cipher, const guchar input[],
+ size_t in_len, guchar output[], size_t out_size)
+{
+ PurpleDES3Cipher *des3_cipher = PURPLE_DES3_CIPHER(cipher);
+ PurpleDES3CipherPrivate *priv = PURPLE_DES3_CIPHER_GET_PRIVATE(cipher);
+
+ if (priv->mode == PURPLE_CIPHER_BATCH_MODE_ECB) {
+ return purple_des3_cipher_ecb_decrypt(des3_cipher, input, in_len, output, out_size);
+ } else if (priv->mode == PURPLE_CIPHER_BATCH_MODE_CBC) {
+ return purple_des3_cipher_cbc_decrypt(des3_cipher, input, in_len, output, out_size);
+ } else {
+ g_return_val_if_reached(0);
+ }
+
+ return 0;
+}
+
+static void
+purple_des3_cipher_set_batch_mode(PurpleCipher *cipher, PurpleCipherBatchMode mode)
+{
+ PurpleDES3Cipher *des3_cipher = PURPLE_DES3_CIPHER(cipher);
+ PurpleDES3CipherPrivate *priv = PURPLE_DES3_CIPHER_GET_PRIVATE(des3_cipher);
+
+ priv->mode = mode;
+
+ g_object_notify_by_pspec(G_OBJECT(cipher), properties[PROP_BATCH_MODE]);
+}
+
+static PurpleCipherBatchMode
+purple_des3_cipher_get_batch_mode(PurpleCipher *cipher)
+{
+ PurpleDES3Cipher *des3_cipher = PURPLE_DES3_CIPHER(cipher);
+ PurpleDES3CipherPrivate *priv = PURPLE_DES3_CIPHER_GET_PRIVATE(des3_cipher);
+
+ return priv->mode;
+}
+
+static void
+purple_des3_cipher_set_iv(PurpleCipher *cipher, guchar *iv, size_t len)
+{
+ PurpleDES3Cipher *des3_cipher = PURPLE_DES3_CIPHER(cipher);
+ PurpleDES3CipherPrivate *priv = PURPLE_DES3_CIPHER_GET_PRIVATE(des3_cipher);
+
+ g_return_if_fail(len == 8);
+
+ memcpy(priv->iv, iv, len);
+
+ g_object_notify_by_pspec(G_OBJECT(cipher), properties[PROP_IV]);
+}
+
+/******************************************************************************
+ * Object Stuff
+ *****************************************************************************/
+static void
+purple_des3_cipher_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleCipher *cipher = PURPLE_CIPHER(obj);
+
+ switch(param_id) {
+ case PROP_BATCH_MODE:
+ g_value_set_enum(value,
+ purple_cipher_get_batch_mode(cipher));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_des3_cipher_set_property(GObject *obj, guint param_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ PurpleCipher *cipher = PURPLE_CIPHER(obj);
+
+ switch(param_id) {
+ case PROP_BATCH_MODE:
+ purple_cipher_set_batch_mode(cipher,
+ g_value_get_enum(value));
+ break;
+ case PROP_IV:
+ {
+ guchar *iv = (guchar *)g_value_get_string(value);
+ purple_cipher_set_iv(cipher, iv, strlen((gchar*)iv));
+ }
+ break;
+ case PROP_KEY:
+ purple_cipher_set_key(cipher, (guchar *)g_value_get_string(value),
+ purple_des3_cipher_get_key_size(cipher));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_des3_cipher_finalize(GObject *obj)
+{
+ PurpleDES3Cipher *des3_cipher = PURPLE_DES3_CIPHER(obj);
+ PurpleDES3CipherPrivate *priv = PURPLE_DES3_CIPHER_GET_PRIVATE(des3_cipher);
+
+ g_object_unref(G_OBJECT(priv->key1));
+ g_object_unref(G_OBJECT(priv->key2));
+ g_object_unref(G_OBJECT(priv->key3));
+
+ memset(priv->iv, 0, sizeof(priv->iv));
+
+ parent_class->finalize(obj);
+}
+
+static void
+purple_des3_cipher_class_init(PurpleDES3CipherClass *klass) {
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+ PurpleCipherClass *cipher_class = PURPLE_CIPHER_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ obj_class->finalize = purple_des3_cipher_finalize;
+ obj_class->get_property = purple_des3_cipher_get_property;
+ obj_class->set_property = purple_des3_cipher_set_property;
+
+ cipher_class->set_iv = purple_des3_cipher_set_iv;
+ cipher_class->encrypt = purple_des3_cipher_encrypt;
+ cipher_class->decrypt = purple_des3_cipher_decrypt;
+ cipher_class->set_key = purple_des3_cipher_set_key;
+ cipher_class->set_batch_mode = purple_des3_cipher_set_batch_mode;
+ cipher_class->get_batch_mode = purple_des3_cipher_get_batch_mode;
+ cipher_class->get_key_size = purple_des3_cipher_get_key_size;
+
+ g_type_class_add_private(klass, sizeof(PurpleDES3CipherPrivate));
+
+ properties[PROP_BATCH_MODE] = g_param_spec_enum("batch-mode", "batch-mode",
+ "batch-mode", PURPLE_TYPE_CIPHER_BATCH_MODE, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_IV] = g_param_spec_string("iv", "iv", "iv", NULL,
+ G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_KEY] = g_param_spec_string("key", "key", "key", NULL,
+ G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, PROP_LAST, properties);
+}
+
+static void
+purple_des3_cipher_init(PurpleCipher *cipher) {
+ PurpleDES3Cipher *des3_cipher = PURPLE_DES3_CIPHER(cipher);
+ PurpleDES3CipherPrivate *priv = PURPLE_DES3_CIPHER_GET_PRIVATE(des3_cipher);
+
+ priv->key1 = purple_des_cipher_new();
+ priv->key2 = purple_des_cipher_new();
+ priv->key3 = purple_des_cipher_new();
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+GType
+purple_des3_cipher_get_type(void) {
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleDES3CipherClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_des3_cipher_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleDES3Cipher),
+ 0,
+ (GInstanceInitFunc)purple_des3_cipher_init,
+ NULL
+ };
+
+ type = g_type_register_static(PURPLE_TYPE_CIPHER,
+ "PurpleDES3Cipher",
+ &info, 0);
+ }
+
+ return type;
+}
+
+PurpleCipher *
+purple_des3_cipher_new(void) {
+ return g_object_new(PURPLE_TYPE_DES3_CIPHER, NULL);
+}
diff --git a/libpurple/ciphers/des3cipher.h b/libpurple/ciphers/des3cipher.h
new file mode 100644
index 0000000000..788b684cd2
--- /dev/null
+++ b/libpurple/ciphers/des3cipher.h
@@ -0,0 +1,66 @@
+/* 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 PURPLE_DES3_CIPHER_H
+#define PURPLE_DES3_CIPHER_H
+/**
+ * SECTION:des3cipher
+ * @section_id: libpurple-des3cipher
+ * @short_description: <filename>ciphers/des3cipher.h</filename>
+ * @title: Triple-DES Cipher
+ */
+
+#include "cipher.h"
+
+#define PURPLE_TYPE_DES3_CIPHER (purple_des3_cipher_get_type())
+#define PURPLE_DES3_CIPHER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_DES3_CIPHER, PurpleDES3Cipher))
+#define PURPLE_DES3_CIPHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_DES3_CIPHER, PurpleDES3CipherClass))
+#define PURPLE_IS_DES3_CIPHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_DES3_CIPHER))
+#define PURPLE_IS_DES3_CIPHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), PURPLE_TYPE_DES3_CIPHER))
+#define PURPLE_DES3_CIPHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_DES3_CIPHER, PurpleDES3CipherClass))
+
+typedef struct _PurpleDES3Cipher PurpleDES3Cipher;
+typedef struct _PurpleDES3CipherClass PurpleDES3CipherClass;
+
+struct _PurpleDES3Cipher {
+ PurpleCipher gparent;
+};
+
+struct _PurpleDES3CipherClass {
+ PurpleCipherClass gparent;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+GType purple_des3_cipher_get_type(void);
+
+PurpleCipher *purple_des3_cipher_new(void);
+
+G_END_DECLS
+
+#endif /* PURPLE_DES3_CIPHER_H */
+
diff --git a/libpurple/ciphers/descipher.c b/libpurple/ciphers/descipher.c
new file mode 100644
index 0000000000..82b10e0886
--- /dev/null
+++ b/libpurple/ciphers/descipher.c
@@ -0,0 +1,581 @@
+/*
+ * Original des taken from gpg
+ *
+ * des.c - DES encryption/decryption Algorithm
+ * Copyright (C) 1998 Free Software Foundation, Inc.
+ *
+ * Please see below for more legal information!
+ *
+ * According to the definition of DES in FIPS PUB 46-2 from December 1993.
+ * For a description of triple encryption, see:
+ * Bruce Schneier: Applied Cryptography. Second Edition.
+ * John Wiley & Sons, 1996. ISBN 0-471-12845-7. Pages 358 ff.
+ *
+ * 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 "glibcompat.h"
+
+#include "descipher.h"
+#include "enums.h"
+
+#include <string.h>
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+#define PURPLE_DES_CIPHER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_DES_CIPHER, PurpleDESCipherPrivate))
+
+typedef struct {
+ guint32 encrypt_subkeys[32];
+ guint32 decrypt_subkeys[32];
+} PurpleDESCipherPrivate;
+
+/******************************************************************************
+ * Enums
+ *****************************************************************************/
+enum {
+ PROP_NONE,
+ PROP_KEY,
+ PROP_LAST,
+};
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+static GObjectClass *parent_class = NULL;
+static GParamSpec *properties[PROP_LAST];
+
+/*
+ * The s-box values are permuted according to the 'primitive function P'
+ */
+static guint32 sbox1[64] = {
+ 0x00808200, 0x00000000, 0x00008000, 0x00808202, 0x00808002, 0x00008202,
+ 0x00000002, 0x00008000, 0x00000200, 0x00808200, 0x00808202, 0x00000200,
+ 0x00800202, 0x00808002, 0x00800000, 0x00000002, 0x00000202, 0x00800200,
+ 0x00800200, 0x00008200, 0x00008200, 0x00808000, 0x00808000, 0x00800202,
+ 0x00008002, 0x00800002, 0x00800002, 0x00008002, 0x00000000, 0x00000202,
+ 0x00008202, 0x00800000, 0x00008000, 0x00808202, 0x00000002, 0x00808000,
+ 0x00808200, 0x00800000, 0x00800000, 0x00000200, 0x00808002, 0x00008000,
+ 0x00008200, 0x00800002, 0x00000200, 0x00000002, 0x00800202, 0x00008202,
+ 0x00808202, 0x00008002, 0x00808000, 0x00800202, 0x00800002, 0x00000202,
+ 0x00008202, 0x00808200, 0x00000202, 0x00800200, 0x00800200, 0x00000000,
+ 0x00008002, 0x00008200, 0x00000000, 0x00808002
+};
+
+static guint32 sbox2[64] = {
+ 0x40084010, 0x40004000, 0x00004000, 0x00084010, 0x00080000, 0x00000010,
+ 0x40080010, 0x40004010, 0x40000010, 0x40084010, 0x40084000, 0x40000000,
+ 0x40004000, 0x00080000, 0x00000010, 0x40080010, 0x00084000, 0x00080010,
+ 0x40004010, 0x00000000, 0x40000000, 0x00004000, 0x00084010, 0x40080000,
+ 0x00080010, 0x40000010, 0x00000000, 0x00084000, 0x00004010, 0x40084000,
+ 0x40080000, 0x00004010, 0x00000000, 0x00084010, 0x40080010, 0x00080000,
+ 0x40004010, 0x40080000, 0x40084000, 0x00004000, 0x40080000, 0x40004000,
+ 0x00000010, 0x40084010, 0x00084010, 0x00000010, 0x00004000, 0x40000000,
+ 0x00004010, 0x40084000, 0x00080000, 0x40000010, 0x00080010, 0x40004010,
+ 0x40000010, 0x00080010, 0x00084000, 0x00000000, 0x40004000, 0x00004010,
+ 0x40000000, 0x40080010, 0x40084010, 0x00084000
+};
+
+static guint32 sbox3[64] = {
+ 0x00000104, 0x04010100, 0x00000000, 0x04010004, 0x04000100, 0x00000000,
+ 0x00010104, 0x04000100, 0x00010004, 0x04000004, 0x04000004, 0x00010000,
+ 0x04010104, 0x00010004, 0x04010000, 0x00000104, 0x04000000, 0x00000004,
+ 0x04010100, 0x00000100, 0x00010100, 0x04010000, 0x04010004, 0x00010104,
+ 0x04000104, 0x00010100, 0x00010000, 0x04000104, 0x00000004, 0x04010104,
+ 0x00000100, 0x04000000, 0x04010100, 0x04000000, 0x00010004, 0x00000104,
+ 0x00010000, 0x04010100, 0x04000100, 0x00000000, 0x00000100, 0x00010004,
+ 0x04010104, 0x04000100, 0x04000004, 0x00000100, 0x00000000, 0x04010004,
+ 0x04000104, 0x00010000, 0x04000000, 0x04010104, 0x00000004, 0x00010104,
+ 0x00010100, 0x04000004, 0x04010000, 0x04000104, 0x00000104, 0x04010000,
+ 0x00010104, 0x00000004, 0x04010004, 0x00010100
+};
+
+static guint32 sbox4[64] = {
+ 0x80401000, 0x80001040, 0x80001040, 0x00000040, 0x00401040, 0x80400040,
+ 0x80400000, 0x80001000, 0x00000000, 0x00401000, 0x00401000, 0x80401040,
+ 0x80000040, 0x00000000, 0x00400040, 0x80400000, 0x80000000, 0x00001000,
+ 0x00400000, 0x80401000, 0x00000040, 0x00400000, 0x80001000, 0x00001040,
+ 0x80400040, 0x80000000, 0x00001040, 0x00400040, 0x00001000, 0x00401040,
+ 0x80401040, 0x80000040, 0x00400040, 0x80400000, 0x00401000, 0x80401040,
+ 0x80000040, 0x00000000, 0x00000000, 0x00401000, 0x00001040, 0x00400040,
+ 0x80400040, 0x80000000, 0x80401000, 0x80001040, 0x80001040, 0x00000040,
+ 0x80401040, 0x80000040, 0x80000000, 0x00001000, 0x80400000, 0x80001000,
+ 0x00401040, 0x80400040, 0x80001000, 0x00001040, 0x00400000, 0x80401000,
+ 0x00000040, 0x00400000, 0x00001000, 0x00401040
+};
+
+static guint32 sbox5[64] = {
+ 0x00000080, 0x01040080, 0x01040000, 0x21000080, 0x00040000, 0x00000080,
+ 0x20000000, 0x01040000, 0x20040080, 0x00040000, 0x01000080, 0x20040080,
+ 0x21000080, 0x21040000, 0x00040080, 0x20000000, 0x01000000, 0x20040000,
+ 0x20040000, 0x00000000, 0x20000080, 0x21040080, 0x21040080, 0x01000080,
+ 0x21040000, 0x20000080, 0x00000000, 0x21000000, 0x01040080, 0x01000000,
+ 0x21000000, 0x00040080, 0x00040000, 0x21000080, 0x00000080, 0x01000000,
+ 0x20000000, 0x01040000, 0x21000080, 0x20040080, 0x01000080, 0x20000000,
+ 0x21040000, 0x01040080, 0x20040080, 0x00000080, 0x01000000, 0x21040000,
+ 0x21040080, 0x00040080, 0x21000000, 0x21040080, 0x01040000, 0x00000000,
+ 0x20040000, 0x21000000, 0x00040080, 0x01000080, 0x20000080, 0x00040000,
+ 0x00000000, 0x20040000, 0x01040080, 0x20000080
+};
+
+static guint32 sbox6[64] = {
+ 0x10000008, 0x10200000, 0x00002000, 0x10202008, 0x10200000, 0x00000008,
+ 0x10202008, 0x00200000, 0x10002000, 0x00202008, 0x00200000, 0x10000008,
+ 0x00200008, 0x10002000, 0x10000000, 0x00002008, 0x00000000, 0x00200008,
+ 0x10002008, 0x00002000, 0x00202000, 0x10002008, 0x00000008, 0x10200008,
+ 0x10200008, 0x00000000, 0x00202008, 0x10202000, 0x00002008, 0x00202000,
+ 0x10202000, 0x10000000, 0x10002000, 0x00000008, 0x10200008, 0x00202000,
+ 0x10202008, 0x00200000, 0x00002008, 0x10000008, 0x00200000, 0x10002000,
+ 0x10000000, 0x00002008, 0x10000008, 0x10202008, 0x00202000, 0x10200000,
+ 0x00202008, 0x10202000, 0x00000000, 0x10200008, 0x00000008, 0x00002000,
+ 0x10200000, 0x00202008, 0x00002000, 0x00200008, 0x10002008, 0x00000000,
+ 0x10202000, 0x10000000, 0x00200008, 0x10002008
+};
+
+static guint32 sbox7[64] = {
+ 0x00100000, 0x02100001, 0x02000401, 0x00000000, 0x00000400, 0x02000401,
+ 0x00100401, 0x02100400, 0x02100401, 0x00100000, 0x00000000, 0x02000001,
+ 0x00000001, 0x02000000, 0x02100001, 0x00000401, 0x02000400, 0x00100401,
+ 0x00100001, 0x02000400, 0x02000001, 0x02100000, 0x02100400, 0x00100001,
+ 0x02100000, 0x00000400, 0x00000401, 0x02100401, 0x00100400, 0x00000001,
+ 0x02000000, 0x00100400, 0x02000000, 0x00100400, 0x00100000, 0x02000401,
+ 0x02000401, 0x02100001, 0x02100001, 0x00000001, 0x00100001, 0x02000000,
+ 0x02000400, 0x00100000, 0x02100400, 0x00000401, 0x00100401, 0x02100400,
+ 0x00000401, 0x02000001, 0x02100401, 0x02100000, 0x00100400, 0x00000000,
+ 0x00000001, 0x02100401, 0x00000000, 0x00100401, 0x02100000, 0x00000400,
+ 0x02000001, 0x02000400, 0x00000400, 0x00100001
+};
+
+static guint32 sbox8[64] = {
+ 0x08000820, 0x00000800, 0x00020000, 0x08020820, 0x08000000, 0x08000820,
+ 0x00000020, 0x08000000, 0x00020020, 0x08020000, 0x08020820, 0x00020800,
+ 0x08020800, 0x00020820, 0x00000800, 0x00000020, 0x08020000, 0x08000020,
+ 0x08000800, 0x00000820, 0x00020800, 0x00020020, 0x08020020, 0x08020800,
+ 0x00000820, 0x00000000, 0x00000000, 0x08020020, 0x08000020, 0x08000800,
+ 0x00020820, 0x00020000, 0x00020820, 0x00020000, 0x08020800, 0x00000800,
+ 0x00000020, 0x08020020, 0x00000800, 0x00020820, 0x08000800, 0x00000020,
+ 0x08000020, 0x08020000, 0x08020020, 0x08000000, 0x00020000, 0x08000820,
+ 0x00000000, 0x08020820, 0x00020020, 0x08000020, 0x08020000, 0x08000800,
+ 0x08000820, 0x00000000, 0x08020820, 0x00020800, 0x00020800, 0x00000820,
+ 0x00000820, 0x00020020, 0x08000000, 0x08020800
+};
+
+/*
+ * These two tables are part of the 'permuted choice 1' function.
+ * In this implementation several speed improvements are done.
+ */
+static guint32 leftkey_swap[16] = {
+ 0x00000000, 0x00000001, 0x00000100, 0x00000101, 0x00010000, 0x00010001,
+ 0x00010100, 0x00010101, 0x01000000, 0x01000001, 0x01000100, 0x01000101,
+ 0x01010000, 0x01010001, 0x01010100, 0x01010101
+};
+
+static guint32 rightkey_swap[16] = {
+ 0x00000000, 0x01000000, 0x00010000, 0x01010000, 0x00000100, 0x01000100,
+ 0x00010100, 0x01010100, 0x00000001, 0x01000001, 0x00010001, 0x01010001,
+ 0x00000101, 0x01000101, 0x00010101, 0x01010101,
+};
+
+/*
+ * Numbers of left shifts per round for encryption subkey schedule
+ * To calculate the decryption key scheduling we just reverse the
+ * ordering of the subkeys so we can omit the table for decryption
+ * subkey schedule.
+ */
+static guint8 encrypt_rotate_tab[16] = {
+ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
+};
+
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+/*
+ * Macro to swap bits across two words
+ */
+#define DO_PERMUTATION(a, temp, b, offset, mask) \
+ temp = ((a>>offset) ^ b) & mask; \
+ b ^= temp; \
+ a ^= temp<<offset;
+
+/*
+ * This performs the 'initial permutation' for the data to be encrypted or
+ * decrypted
+ */
+#define INITIAL_PERMUTATION(left, temp, right) \
+ DO_PERMUTATION(left, temp, right, 4, 0x0f0f0f0f) \
+ DO_PERMUTATION(left, temp, right, 16, 0x0000ffff) \
+ DO_PERMUTATION(right, temp, left, 2, 0x33333333) \
+ DO_PERMUTATION(right, temp, left, 8, 0x00ff00ff) \
+ DO_PERMUTATION(left, temp, right, 1, 0x55555555)
+
+/*
+ * The 'inverse initial permutation'
+ */
+#define FINAL_PERMUTATION(left, temp, right) \
+ DO_PERMUTATION(left, temp, right, 1, 0x55555555) \
+ DO_PERMUTATION(right, temp, left, 8, 0x00ff00ff) \
+ DO_PERMUTATION(right, temp, left, 2, 0x33333333) \
+ DO_PERMUTATION(left, temp, right, 16, 0x0000ffff) \
+ DO_PERMUTATION(left, temp, right, 4, 0x0f0f0f0f)
+
+/*
+ * A full DES round including 'expansion function', 'sbox substitution'
+ * and 'primitive function P' but without swapping the left and right word.
+ */
+#define DES_ROUND(from, to, work, subkey) \
+ work = ((from<<1) | (from>>31)) ^ *subkey++; \
+ to ^= sbox8[ work & 0x3f ]; \
+ to ^= sbox6[ (work>>8) & 0x3f ]; \
+ to ^= sbox4[ (work>>16) & 0x3f ]; \
+ to ^= sbox2[ (work>>24) & 0x3f ]; \
+ work = ((from>>3) | (from<<29)) ^ *subkey++; \
+ to ^= sbox7[ work & 0x3f ]; \
+ to ^= sbox5[ (work>>8) & 0x3f ]; \
+ to ^= sbox3[ (work>>16) & 0x3f ]; \
+ to ^= sbox1[ (work>>24) & 0x3f ];
+
+
+/*
+ * Macros to convert 8 bytes from/to 32bit words
+ */
+#define READ_64BIT_DATA(data, left, right) \
+ left = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; \
+ right = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7];
+
+#define WRITE_64BIT_DATA(data, left, right) \
+ data[0] = (left >> 24) &0xff; data[1] = (left >> 16) &0xff; \
+ data[2] = (left >> 8) &0xff; data[3] = left &0xff; \
+ data[4] = (right >> 24) &0xff; data[5] = (right >> 16) &0xff; \
+ data[6] = (right >> 8) &0xff; data[7] = right &0xff;
+
+/******************************************************************************
+ * Cipher Stuff
+ *****************************************************************************/
+/*
+ * des_key_schedule(): Calculate 16 subkeys pairs (even/odd) for
+ * 16 encryption rounds.
+ * To calculate subkeys for decryption the caller
+ * have to reorder the generated subkeys.
+ *
+ * rawkey: 8 Bytes of key data
+ * subkey: Array of at least 32 guint32s. Will be filled
+ * with calculated subkeys.
+ *
+ */
+static void
+purple_des_cipher_key_schedule(const guint8 * rawkey, guint32 * subkey) {
+ guint32 left, right, work;
+ int round;
+
+ READ_64BIT_DATA (rawkey, left, right)
+
+ DO_PERMUTATION (right, work, left, 4, 0x0f0f0f0f)
+ DO_PERMUTATION (right, work, left, 0, 0x10101010)
+
+ left = (leftkey_swap[(left >> 0) & 0xf] << 3)
+ | (leftkey_swap[(left >> 8) & 0xf] << 2)
+ | (leftkey_swap[(left >> 16) & 0xf] << 1)
+ | (leftkey_swap[(left >> 24) & 0xf] )
+ | (leftkey_swap[(left >> 5) & 0xf] << 7)
+ | (leftkey_swap[(left >> 13) & 0xf] << 6)
+ | (leftkey_swap[(left >> 21) & 0xf] << 5)
+ | (leftkey_swap[(left >> 29) & 0xf] << 4);
+
+ left &= 0x0fffffff;
+
+ right = (rightkey_swap[(right >> 1) & 0xf] << 3)
+ | (rightkey_swap[(right >> 9) & 0xf] << 2)
+ | (rightkey_swap[(right >> 17) & 0xf] << 1)
+ | (rightkey_swap[(right >> 25) & 0xf] )
+ | (rightkey_swap[(right >> 4) & 0xf] << 7)
+ | (rightkey_swap[(right >> 12) & 0xf] << 6)
+ | (rightkey_swap[(right >> 20) & 0xf] << 5)
+ | (rightkey_swap[(right >> 28) & 0xf] << 4);
+
+ right &= 0x0fffffff;
+
+ for (round = 0; round < 16; ++round) {
+ left = ((left << encrypt_rotate_tab[round]) |
+ (left >> (28 - encrypt_rotate_tab[round]))) & 0x0fffffff;
+ right = ((right << encrypt_rotate_tab[round]) |
+ (right >> (28 - encrypt_rotate_tab[round]))) & 0x0fffffff;
+
+ *subkey++ = ((left << 4) & 0x24000000)
+ | ((left << 28) & 0x10000000)
+ | ((left << 14) & 0x08000000)
+ | ((left << 18) & 0x02080000)
+ | ((left << 6) & 0x01000000)
+ | ((left << 9) & 0x00200000)
+ | ((left >> 1) & 0x00100000)
+ | ((left << 10) & 0x00040000)
+ | ((left << 2) & 0x00020000)
+ | ((left >> 10) & 0x00010000)
+ | ((right >> 13) & 0x00002000)
+ | ((right >> 4) & 0x00001000)
+ | ((right << 6) & 0x00000800)
+ | ((right >> 1) & 0x00000400)
+ | ((right >> 14) & 0x00000200)
+ | (right & 0x00000100)
+ | ((right >> 5) & 0x00000020)
+ | ((right >> 10) & 0x00000010)
+ | ((right >> 3) & 0x00000008)
+ | ((right >> 18) & 0x00000004)
+ | ((right >> 26) & 0x00000002)
+ | ((right >> 24) & 0x00000001);
+
+ *subkey++ = ((left << 15) & 0x20000000)
+ | ((left << 17) & 0x10000000)
+ | ((left << 10) & 0x08000000)
+ | ((left << 22) & 0x04000000)
+ | ((left >> 2) & 0x02000000)
+ | ((left << 1) & 0x01000000)
+ | ((left << 16) & 0x00200000)
+ | ((left << 11) & 0x00100000)
+ | ((left << 3) & 0x00080000)
+ | ((left >> 6) & 0x00040000)
+ | ((left << 15) & 0x00020000)
+ | ((left >> 4) & 0x00010000)
+ | ((right >> 2) & 0x00002000)
+ | ((right << 8) & 0x00001000)
+ | ((right >> 14) & 0x00000808)
+ | ((right >> 9) & 0x00000400)
+ | ((right) & 0x00000200)
+ | ((right << 7) & 0x00000100)
+ | ((right >> 7) & 0x00000020)
+ | ((right >> 3) & 0x00000011)
+ | ((right << 2) & 0x00000004)
+ | ((right >> 21) & 0x00000002);
+ }
+}
+
+/*
+ * Fill a DES context with subkeys calculated from a 64bit key.
+ * Does not check parity bits, but simply ignore them.
+ * Does not check for weak keys.
+ */
+static void
+purple_des_cipher_set_key(PurpleCipher *cipher, const guchar *key, size_t len) {
+ PurpleDESCipher *des_cipher = PURPLE_DES_CIPHER(cipher);
+ PurpleDESCipherPrivate *priv = PURPLE_DES_CIPHER_GET_PRIVATE(des_cipher);
+ int i;
+
+ g_return_if_fail(len == 8);
+
+ purple_des_cipher_key_schedule(key, priv->encrypt_subkeys);
+
+ for(i = 0; i < 32; i += 2) {
+ priv->decrypt_subkeys[i] = priv->encrypt_subkeys[30 - i];
+ priv->decrypt_subkeys[i + 1] = priv->encrypt_subkeys[31 - i];
+ }
+
+ g_object_notify_by_pspec(G_OBJECT(cipher), properties[PROP_KEY]);
+}
+
+static size_t
+purple_des_cipher_get_key_size(PurpleCipher *cipher)
+{
+ return 8;
+}
+
+/*
+ * Electronic Codebook Mode DES encryption/decryption of data according to
+ * 'mode'.
+ */
+int
+purple_des_cipher_ecb_crypt(PurpleDESCipher *des_cipher, const guint8 * from, guint8 * to,
+ int mode)
+{
+ guint32 left, right, work;
+ guint32 *keys;
+ PurpleDESCipherPrivate *priv = PURPLE_DES_CIPHER_GET_PRIVATE(des_cipher);
+
+ keys = mode ? priv->decrypt_subkeys :
+ priv->encrypt_subkeys;
+
+ READ_64BIT_DATA (from, left, right)
+ INITIAL_PERMUTATION (left, work, right)
+
+ DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
+ DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
+ DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
+ DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
+ DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
+ DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
+ DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
+ DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
+
+ FINAL_PERMUTATION (right, work, left)
+ WRITE_64BIT_DATA (to, right, left)
+
+ return 0;
+}
+
+static ssize_t
+purple_des_cipher_encrypt(PurpleCipher *cipher, const guchar input[],
+ size_t in_len, guchar output[], size_t out_size)
+{
+ PurpleDESCipher *des_cipher = PURPLE_DES_CIPHER(cipher);
+
+ gsize offset = 0;
+ int i = 0;
+ gsize tmp;
+ guint8 buf[8] = {0,0,0,0,0,0,0,0};
+ gsize out_len;
+
+ g_return_val_if_fail(out_size >= in_len, -1);
+
+ while(offset + 8 <= in_len) {
+ purple_des_cipher_ecb_crypt(des_cipher, input + offset, output + offset, 0);
+ offset += 8;
+ }
+
+ out_len = in_len;
+
+ if(offset<in_len) {
+ g_return_val_if_fail(in_len >= offset, -1);
+ out_len += in_len - offset;
+ g_return_val_if_fail(out_size >= out_len, -1);
+ tmp = offset;
+ while(tmp<in_len) {
+ buf[i++] = input[tmp];
+ tmp++;
+ }
+
+ purple_des_cipher_ecb_crypt(des_cipher, buf, output + offset, 0);
+ }
+
+ return out_len;
+}
+
+static ssize_t
+purple_des_cipher_decrypt(PurpleCipher *cipher, const guchar input[],
+ size_t in_len, guchar output[], size_t out_size)
+{
+ PurpleDESCipher *des_cipher = PURPLE_DES_CIPHER(cipher);
+
+ gsize offset = 0;
+ int i = 0;
+ gsize tmp;
+ guint8 buf[8] = {0,0,0,0,0,0,0,0};
+ gsize out_len;
+
+ g_return_val_if_fail(out_size >= in_len, -1);
+
+ while(offset + 8 <= in_len) {
+ purple_des_cipher_ecb_crypt(des_cipher, input + offset, output + offset, 1);
+ offset += 8;
+ }
+
+ out_len = in_len;
+ if(offset<in_len) {
+ g_return_val_if_fail(in_len >= offset, -1);
+ out_len += in_len - offset;
+ g_return_val_if_fail(out_size >= out_len, -1);
+ tmp = offset;
+ while(tmp<in_len) {
+ buf[i++] = input[tmp];
+ tmp++;
+ }
+
+ purple_des_cipher_ecb_crypt(des_cipher, buf, output + offset, 1);
+ }
+
+ return out_len;
+}
+
+/******************************************************************************
+ * Object Stuff
+ *****************************************************************************/
+static void
+purple_des_cipher_set_property(GObject *obj, guint param_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ PurpleCipher *cipher = PURPLE_CIPHER(obj);
+
+ switch(param_id) {
+ case PROP_KEY:
+ purple_cipher_set_key(cipher, (guchar *)g_value_get_string(value),
+ purple_des_cipher_get_key_size(cipher));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_des_cipher_class_init(PurpleDESCipherClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+ PurpleCipherClass *cipher_class = PURPLE_CIPHER_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ obj_class->set_property = purple_des_cipher_set_property;
+
+ cipher_class->encrypt = purple_des_cipher_encrypt;
+ cipher_class->decrypt = purple_des_cipher_decrypt;
+ cipher_class->set_key = purple_des_cipher_set_key;
+ cipher_class->get_key_size = purple_des_cipher_get_key_size;
+
+ g_type_class_add_private(klass, sizeof(PurpleDESCipherPrivate));
+
+ properties[PROP_KEY] = g_param_spec_string("key", "key", "key", NULL,
+ G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, PROP_LAST, properties);
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+GType
+purple_des_cipher_get_type(void) {
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ .class_size = sizeof(PurpleDESCipherClass),
+ .class_init = (GClassInitFunc)purple_des_cipher_class_init,
+ .instance_size = sizeof(PurpleDESCipher),
+ .instance_init = (GInstanceInitFunc)purple_cipher_reset,
+ };
+
+ type = g_type_register_static(PURPLE_TYPE_CIPHER,
+ "PurpleDESCipher",
+ &info, 0);
+ }
+
+ return type;
+}
+
+/**
+ * purple_des_cipher_new:
+ *
+ * Creates a new #PurpleCipher instance which implements the DES block cipher.
+ *
+ * Return Value: The new DES implementation of #PurpleCipher.
+ */
+PurpleCipher *
+purple_des_cipher_new(void) {
+ return g_object_new(PURPLE_TYPE_DES_CIPHER, NULL);
+}
diff --git a/libpurple/ciphers/descipher.h b/libpurple/ciphers/descipher.h
new file mode 100644
index 0000000000..d01ee60e72
--- /dev/null
+++ b/libpurple/ciphers/descipher.h
@@ -0,0 +1,67 @@
+/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PURPLE_DES_CIPHER_H
+#define PURPLE_DES_CIPHER_H
+/**
+ * SECTION:descipher
+ * @section_id: libpurple-descipher
+ * @short_description: <filename>ciphers/descipher.h</filename>
+ * @title: DES Cipher
+ */
+
+#include "cipher.h"
+
+#define PURPLE_TYPE_DES_CIPHER (purple_des_cipher_get_type())
+#define PURPLE_DES_CIPHER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_DES_CIPHER, PurpleDESCipher))
+#define PURPLE_DES_CIPHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_DES_CIPHER, PurpleDESCipherClass))
+#define PURPLE_IS_DES_CIPHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_DES_CIPHER))
+#define PURPLE_IS_DES_CIPHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), PURPLE_TYPE_DES_CIPHER))
+#define PURPLE_DES_CIPHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_DES_CIPHER, PurpleDESCipherClass))
+
+typedef struct _PurpleDESCipher PurpleDESCipher;
+typedef struct _PurpleDESCipherClass PurpleDESCipherClass;
+
+struct _PurpleDESCipher {
+ PurpleCipher gparent;
+};
+
+struct _PurpleDESCipherClass {
+ PurpleCipherClass gparent;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+GType purple_des_cipher_get_type(void);
+
+PurpleCipher *purple_des_cipher_new(void);
+
+int purple_des_cipher_ecb_crypt(PurpleDESCipher *des_cipher, const guint8 * from, guint8 * to, int mode);
+
+G_END_DECLS
+
+#endif /* PURPLE_DES_CIPHER_H */
diff --git a/libpurple/ciphers/gchecksum.c b/libpurple/ciphers/gchecksum.c
deleted file mode 100644
index db335d230f..0000000000
--- a/libpurple/ciphers/gchecksum.c
+++ /dev/null
@@ -1,144 +0,0 @@
-#include <cipher.h>
-
-#if GLIB_CHECK_VERSION(2,16,0)
-
-static void
-purple_g_checksum_init(PurpleCipherContext *context, GChecksumType type)
-{
- GChecksum *checksum;
-
- checksum = g_checksum_new(type);
- purple_cipher_context_set_data(context, checksum);
-}
-
-static void
-purple_g_checksum_reset(PurpleCipherContext *context, GChecksumType type)
-{
- GChecksum *checksum;
-
- checksum = purple_cipher_context_get_data(context);
- g_return_if_fail(checksum != NULL);
-
-#if GLIB_CHECK_VERSION(2,18,0)
- g_checksum_reset(checksum);
-#else
- g_checksum_free(checksum);
- checksum = g_checksum_new(type);
- purple_cipher_context_set_data(context, checksum);
-#endif
-}
-
-static void
-purple_g_checksum_uninit(PurpleCipherContext *context)
-{
- GChecksum *checksum;
-
- checksum = purple_cipher_context_get_data(context);
- g_return_if_fail(checksum != NULL);
-
- g_checksum_free(checksum);
-}
-
-static void
-purple_g_checksum_append(PurpleCipherContext *context, const guchar *data,
- gsize len)
-{
- GChecksum *checksum;
-
- checksum = purple_cipher_context_get_data(context);
- g_return_if_fail(checksum != NULL);
-
- while (len >= G_MAXSSIZE) {
- g_checksum_update(checksum, data, G_MAXSSIZE);
- len -= G_MAXSSIZE;
- data += G_MAXSSIZE;
- }
-
- if (len)
- g_checksum_update(checksum, data, len);
-}
-
-static gboolean
-purple_g_checksum_digest(PurpleCipherContext *context, GChecksumType type,
- gsize len, guchar *digest, gsize *out_len)
-{
- GChecksum *checksum;
- const gssize required_length = g_checksum_type_get_length(type);
-
- checksum = purple_cipher_context_get_data(context);
-
- g_return_val_if_fail(len >= required_length, FALSE);
- g_return_val_if_fail(checksum != NULL, FALSE);
-
- g_checksum_get_digest(checksum, digest, &len);
-
- purple_cipher_context_reset(context, NULL);
-
- if (out_len)
- *out_len = len;
-
- return TRUE;
-}
-
-/******************************************************************************
- * Macros
- *****************************************************************************/
-#define PURPLE_G_CHECKSUM_IMPLEMENTATION(lower, camel, type, block_size) \
- static size_t \
- lower##_get_block_size(PurpleCipherContext *context) { \
- return (block_size); \
- } \
- \
- static void \
- lower##_init(PurpleCipherContext *context, gpointer extra) { \
- purple_g_checksum_init(context, (type)); \
- } \
- \
- static void \
- lower##_reset(PurpleCipherContext *context, gpointer extra) { \
- purple_g_checksum_reset(context, (type)); \
- } \
- \
- static gboolean \
- lower##_digest(PurpleCipherContext *context, gsize in_len, \
- guchar digest[], size_t *out_len) \
- { \
- return purple_g_checksum_digest(context, (type), in_len, digest, \
- out_len); \
- } \
- \
- static PurpleCipherOps camel##Ops = { \
- NULL, /* Set option */ \
- NULL, /* Get option */ \
- lower##_init, /* init */ \
- lower##_reset, /* reset */ \
- purple_g_checksum_uninit, /* uninit */ \
- NULL, /* set iv */ \
- purple_g_checksum_append, /* append */ \
- lower##_digest, /* digest */ \
- NULL, /* encrypt */ \
- NULL, /* decrypt */ \
- NULL, /* set salt */ \
- NULL, /* get salt size */ \
- NULL, /* set key */ \
- NULL, /* get key size */ \
- NULL, /* set batch mode */ \
- NULL, /* get batch mode */ \
- lower##_get_block_size, /* get block size */ \
- NULL /* set key with len */ \
- }; \
- \
- PurpleCipherOps * \
- purple_##lower##_cipher_get_ops(void) { \
- return &camel##Ops; \
- }
-
-/******************************************************************************
- * Macro Expansion
- *****************************************************************************/
-PURPLE_G_CHECKSUM_IMPLEMENTATION(md5, MD5, G_CHECKSUM_MD5, 64);
-PURPLE_G_CHECKSUM_IMPLEMENTATION(sha1, SHA1, G_CHECKSUM_SHA1, 64);
-PURPLE_G_CHECKSUM_IMPLEMENTATION(sha256, SHA256, G_CHECKSUM_SHA256, 64);
-
-#endif /* GLIB_CHECK_VERSION(2,16,0) */
-
diff --git a/libpurple/ciphers/hmac.c b/libpurple/ciphers/hmac.c
deleted file mode 100644
index b09dbd7e46..0000000000
--- a/libpurple/ciphers/hmac.c
+++ /dev/null
@@ -1,218 +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
- */
-#include <cipher.h>
-
-#include <util.h>
-
-struct HMAC_Context {
- PurpleCipherContext *hash;
- char *name;
- int blocksize;
- guchar *opad;
-};
-
- static void
-hmac_init(PurpleCipherContext *context, gpointer extra)
-{
- struct HMAC_Context *hctx;
- hctx = g_new0(struct HMAC_Context, 1);
- purple_cipher_context_set_data(context, hctx);
- purple_cipher_context_reset(context, extra);
-}
-
- static void
-hmac_reset(PurpleCipherContext *context, gpointer extra)
-{
- struct HMAC_Context *hctx;
-
- hctx = purple_cipher_context_get_data(context);
-
- g_free(hctx->name);
- hctx->name = NULL;
- if (hctx->hash)
- purple_cipher_context_destroy(hctx->hash);
- hctx->hash = NULL;
- hctx->blocksize = 0;
- g_free(hctx->opad);
- hctx->opad = NULL;
-}
-
- static void
-hmac_set_opt(PurpleCipherContext *context, const gchar *name, void *value)
-{
- struct HMAC_Context *hctx;
-
- hctx = purple_cipher_context_get_data(context);
-
- if (purple_strequal(name, "hash")) {
- g_free(hctx->name);
- if (hctx->hash)
- purple_cipher_context_destroy(hctx->hash);
- hctx->name = g_strdup((char*)value);
- hctx->hash = purple_cipher_context_new_by_name((char *)value, NULL);
- hctx->blocksize = purple_cipher_context_get_block_size(hctx->hash);
- }
-}
-
- static void *
-hmac_get_opt(PurpleCipherContext *context, const gchar *name)
-{
- struct HMAC_Context *hctx;
-
- hctx = purple_cipher_context_get_data(context);
-
- if (purple_strequal(name, "hash")) {
- return hctx->name;
- }
-
- return NULL;
-}
-
- static void
-hmac_append(PurpleCipherContext *context, const guchar *data, size_t len)
-{
- struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
-
- g_return_if_fail(hctx->hash != NULL);
-
- purple_cipher_context_append(hctx->hash, data, len);
-}
-
- static gboolean
-hmac_digest(PurpleCipherContext *context, size_t in_len, guchar *out, size_t *out_len)
-{
- struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
- PurpleCipherContext *hash = hctx->hash;
- guchar *inner_hash;
- size_t hash_len;
- gboolean result;
-
- g_return_val_if_fail(hash != NULL, FALSE);
-
- inner_hash = g_malloc(100); /* TODO: Should be enough for now... */
- result = purple_cipher_context_digest(hash, 100, inner_hash, &hash_len);
-
- purple_cipher_context_reset(hash, NULL);
-
- purple_cipher_context_append(hash, hctx->opad, hctx->blocksize);
- purple_cipher_context_append(hash, inner_hash, hash_len);
-
- g_free(inner_hash);
-
- result = result && purple_cipher_context_digest(hash, in_len, out, out_len);
-
- return result;
-}
-
- static void
-hmac_uninit(PurpleCipherContext *context)
-{
- struct HMAC_Context *hctx;
-
- purple_cipher_context_reset(context, NULL);
-
- hctx = purple_cipher_context_get_data(context);
-
- g_free(hctx);
-}
-
- static void
-hmac_set_key_with_len(PurpleCipherContext *context, const guchar * key, size_t key_len)
-{
- struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
- int blocksize, i;
- guchar *ipad;
- guchar *full_key;
-
- g_return_if_fail(hctx->hash != NULL);
-
- g_free(hctx->opad);
-
- blocksize = hctx->blocksize;
- ipad = g_malloc(blocksize);
- hctx->opad = g_malloc(blocksize);
-
- if (key_len > blocksize) {
- purple_cipher_context_reset(hctx->hash, NULL);
- purple_cipher_context_append(hctx->hash, key, key_len);
- full_key = g_malloc(100); /* TODO: Should be enough for now... */
- purple_cipher_context_digest(hctx->hash, 100, full_key, &key_len);
- } else
- full_key = g_memdup(key, key_len);
-
- if (key_len < blocksize) {
- full_key = g_realloc(full_key, blocksize);
- memset(full_key + key_len, 0, blocksize - key_len);
- }
-
- for(i = 0; i < blocksize; i++) {
- ipad[i] = 0x36 ^ full_key[i];
- hctx->opad[i] = 0x5c ^ full_key[i];
- }
-
- g_free(full_key);
-
- purple_cipher_context_reset(hctx->hash, NULL);
- purple_cipher_context_append(hctx->hash, ipad, blocksize);
- g_free(ipad);
-}
-
- static void
-hmac_set_key(PurpleCipherContext *context, const guchar * key)
-{
- hmac_set_key_with_len(context, key, strlen((char *)key));
-}
-
- static size_t
-hmac_get_block_size(PurpleCipherContext *context)
-{
- struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
-
- return hctx->blocksize;
-}
-
-static PurpleCipherOps HMACOps = {
- hmac_set_opt, /* Set option */
- hmac_get_opt, /* Get option */
- hmac_init, /* init */
- hmac_reset, /* reset */
- hmac_uninit, /* uninit */
- NULL, /* set iv */
- hmac_append, /* append */
- hmac_digest, /* digest */
- NULL, /* encrypt */
- NULL, /* decrypt */
- NULL, /* set salt */
- NULL, /* get salt size */
- hmac_set_key, /* set key */
- NULL, /* get key size */
- NULL, /* set batch mode */
- NULL, /* get batch mode */
- hmac_get_block_size, /* get block size */
- hmac_set_key_with_len /* set key with len */
-};
-
-PurpleCipherOps *
-purple_hmac_cipher_get_ops(void) {
- return &HMACOps;
-}
-
diff --git a/libpurple/ciphers/hmaccipher.c b/libpurple/ciphers/hmaccipher.c
new file mode 100644
index 0000000000..19c9f2ed91
--- /dev/null
+++ b/libpurple/ciphers/hmaccipher.c
@@ -0,0 +1,345 @@
+/*
+ * 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 "glibcompat.h"
+
+#include "hmaccipher.h"
+
+#include <string.h>
+
+/*******************************************************************************
+ * Structs
+ ******************************************************************************/
+#define PURPLE_HMAC_CIPHER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_HMAC_CIPHER, PurpleHMACCipherPrivate))
+
+typedef struct {
+ PurpleHash *hash;
+ guchar *ipad;
+ guchar *opad;
+} PurpleHMACCipherPrivate;
+
+/******************************************************************************
+ * Enums
+ *****************************************************************************/
+enum {
+ PROP_NONE,
+ PROP_HASH,
+ PROP_LAST,
+};
+
+/*******************************************************************************
+ * Globals
+ ******************************************************************************/
+static GObjectClass *parent_class = NULL;
+static GParamSpec *properties[PROP_LAST];
+
+/*******************************************************************************
+ * Helpers
+ ******************************************************************************/
+static void
+purple_hmac_cipher_set_hash(PurpleCipher *cipher,
+ PurpleHash *hash)
+{
+ PurpleHMACCipherPrivate *priv = PURPLE_HMAC_CIPHER_GET_PRIVATE(cipher);
+
+ priv->hash = g_object_ref(G_OBJECT(hash));
+
+ g_object_notify_by_pspec(G_OBJECT(cipher), properties[PROP_HASH]);
+}
+
+/*******************************************************************************
+ * Cipher Stuff
+ ******************************************************************************/
+static void
+purple_hmac_cipher_reset(PurpleCipher *cipher) {
+ PurpleHMACCipherPrivate *priv = PURPLE_HMAC_CIPHER_GET_PRIVATE(cipher);
+
+ if(PURPLE_IS_HASH(priv->hash))
+ purple_hash_reset(priv->hash);
+
+ if(priv->ipad) {
+ g_free(priv->ipad);
+ priv->ipad = NULL;
+ }
+ if(priv->opad) {
+ g_free(priv->opad);
+ priv->opad = NULL;
+ }
+}
+
+static void
+purple_hmac_cipher_reset_state(PurpleCipher *cipher) {
+ PurpleHMACCipherPrivate *priv = PURPLE_HMAC_CIPHER_GET_PRIVATE(cipher);
+
+ if(PURPLE_IS_HASH(priv->hash)) {
+ purple_hash_reset_state(priv->hash);
+ purple_hash_append(priv->hash, priv->ipad,
+ purple_hash_get_block_size(priv->hash));
+ }
+}
+
+static void
+purple_hmac_cipher_append(PurpleCipher *cipher, const guchar *d, size_t l) {
+ PurpleHMACCipherPrivate *priv = PURPLE_HMAC_CIPHER_GET_PRIVATE(cipher);
+
+ g_return_if_fail(PURPLE_IS_HASH(priv->hash));
+
+ purple_hash_append(priv->hash, d, l);
+}
+
+static gboolean
+purple_hmac_cipher_digest(PurpleCipher *cipher, guchar *out, size_t len)
+{
+ PurpleHMACCipherPrivate *priv = PURPLE_HMAC_CIPHER_GET_PRIVATE(cipher);
+ guchar *digest = NULL;
+ size_t hash_len, block_size;
+ gboolean result = FALSE;
+
+ g_return_val_if_fail(PURPLE_IS_HASH(priv->hash), FALSE);
+
+ hash_len = purple_hash_get_digest_size(priv->hash);
+ g_return_val_if_fail(hash_len > 0, FALSE);
+
+ block_size = purple_hash_get_block_size(priv->hash);
+ digest = g_malloc(hash_len);
+
+ /* get the digest of the data */
+ result = purple_hash_digest(priv->hash, digest, hash_len);
+ purple_hash_reset(priv->hash);
+
+ if(!result) {
+ g_free(digest);
+
+ return FALSE;
+ }
+
+ /* now append the opad and the digest from above */
+ purple_hash_append(priv->hash, priv->opad, block_size);
+ purple_hash_append(priv->hash, digest, hash_len);
+
+ /* do our last digest */
+ result = purple_hash_digest(priv->hash, out, len);
+
+ /* cleanup */
+ g_free(digest);
+
+ return result;
+}
+
+static size_t
+purple_hmac_cipher_get_digest_size(PurpleCipher *cipher)
+{
+ PurpleHMACCipherPrivate *priv = PURPLE_HMAC_CIPHER_GET_PRIVATE(cipher);
+
+ g_return_val_if_fail(priv->hash != NULL, 0);
+
+ return purple_hash_get_digest_size(priv->hash);
+}
+
+static void
+purple_hmac_cipher_set_key(PurpleCipher *cipher, const guchar *key,
+ size_t key_len)
+{
+ PurpleHMACCipherPrivate *priv = PURPLE_HMAC_CIPHER_GET_PRIVATE(cipher);
+ gsize block_size, i;
+ guchar *full_key;
+
+ g_return_if_fail(priv->hash);
+
+ g_free(priv->ipad);
+ g_free(priv->opad);
+
+ block_size = purple_hash_get_block_size(priv->hash);
+ priv->ipad = g_malloc(block_size);
+ priv->opad = g_malloc(block_size);
+
+ if (key_len > block_size) {
+ purple_hash_reset(priv->hash);
+ purple_hash_append(priv->hash, key, key_len);
+
+ key_len = purple_hash_get_digest_size(priv->hash);
+ full_key = g_malloc(key_len);
+ purple_hash_digest(priv->hash, full_key, key_len);
+ } else {
+ full_key = g_memdup(key, key_len);
+ }
+
+ if (key_len < block_size) {
+ full_key = g_realloc(full_key, block_size);
+ memset(full_key + key_len, 0, block_size - key_len);
+ }
+
+ for(i = 0; i < block_size; i++) {
+ priv->ipad[i] = 0x36 ^ full_key[i];
+ priv->opad[i] = 0x5c ^ full_key[i];
+ }
+
+ g_free(full_key);
+
+ purple_hash_reset(priv->hash);
+ purple_hash_append(priv->hash, priv->ipad, block_size);
+}
+
+static size_t
+purple_hmac_cipher_get_block_size(PurpleCipher *cipher)
+{
+ PurpleHMACCipherPrivate *priv = NULL;
+
+ g_return_val_if_fail(PURPLE_IS_HMAC_CIPHER(cipher), 0);
+
+ priv = PURPLE_HMAC_CIPHER_GET_PRIVATE(cipher);
+
+ return purple_hash_get_block_size(priv->hash);
+}
+
+/******************************************************************************
+ * Object Stuff
+ *****************************************************************************/
+static void
+purple_hmac_cipher_set_property(GObject *obj, guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleCipher *cipher = PURPLE_CIPHER(obj);
+
+ switch(param_id) {
+ case PROP_HASH:
+ purple_hmac_cipher_set_hash(cipher, g_value_get_object(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_hmac_cipher_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleHMACCipher *cipher = PURPLE_HMAC_CIPHER(obj);
+
+ switch(param_id) {
+ case PROP_HASH:
+ g_value_set_object(value,
+ purple_hmac_cipher_get_hash(cipher));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_hmac_cipher_finalize(GObject *obj) {
+ PurpleCipher *cipher = PURPLE_CIPHER(obj);
+ PurpleHMACCipherPrivate *priv = PURPLE_HMAC_CIPHER_GET_PRIVATE(cipher);
+
+ purple_hmac_cipher_reset(cipher);
+
+ if (priv->hash != NULL)
+ g_object_unref(priv->hash);
+
+ parent_class->finalize(obj);
+}
+
+static void
+purple_hmac_cipher_class_init(PurpleHMACCipherClass *klass) {
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+ PurpleCipherClass *cipher_class = PURPLE_CIPHER_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ g_type_class_add_private(klass, sizeof(PurpleHMACCipherPrivate));
+
+ obj_class->finalize = purple_hmac_cipher_finalize;
+ obj_class->get_property = purple_hmac_cipher_get_property;
+ obj_class->set_property = purple_hmac_cipher_set_property;
+
+ cipher_class->reset = purple_hmac_cipher_reset;
+ cipher_class->reset_state = purple_hmac_cipher_reset_state;
+ cipher_class->append = purple_hmac_cipher_append;
+ cipher_class->digest = purple_hmac_cipher_digest;
+ cipher_class->get_digest_size = purple_hmac_cipher_get_digest_size;
+ cipher_class->set_key = purple_hmac_cipher_set_key;
+ cipher_class->get_block_size = purple_hmac_cipher_get_block_size;
+
+ properties[PROP_HASH] = g_param_spec_object("hash", "hash", "hash",
+ PURPLE_TYPE_HASH,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, PROP_LAST, properties);
+}
+
+/******************************************************************************
+ * PurpleHMACCipher API
+ *****************************************************************************/
+GType
+purple_hmac_cipher_get_type(void) {
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleHMACCipherClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_hmac_cipher_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleHMACCipher),
+ 0,
+ (GInstanceInitFunc)purple_cipher_reset,
+ NULL,
+ };
+
+ type = g_type_register_static(PURPLE_TYPE_CIPHER,
+ "PurpleHMACCipher",
+ &info, 0);
+ }
+
+ return type;
+}
+
+PurpleCipher *
+purple_hmac_cipher_new(PurpleHash *hash) {
+ g_return_val_if_fail(PURPLE_IS_HASH(hash), NULL);
+
+ return g_object_new(PURPLE_TYPE_HMAC_CIPHER,
+ "hash", hash,
+ NULL);
+}
+
+PurpleHash *
+purple_hmac_cipher_get_hash(const PurpleHMACCipher *cipher) {
+ PurpleHMACCipherPrivate *priv = NULL;
+
+ g_return_val_if_fail(PURPLE_IS_HMAC_CIPHER(cipher), NULL);
+
+ priv = PURPLE_HMAC_CIPHER_GET_PRIVATE(cipher);
+
+ if(priv && priv->hash)
+ return priv->hash;
+
+ return NULL;
+}
+
diff --git a/libpurple/ciphers/hmaccipher.h b/libpurple/ciphers/hmaccipher.h
new file mode 100644
index 0000000000..36dcefd9e6
--- /dev/null
+++ b/libpurple/ciphers/hmaccipher.h
@@ -0,0 +1,67 @@
+/* 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 PURPLE_HMAC_CIPHER_H
+#define PURPLE_HMAC_CIPHER_H
+/**
+ * SECTION:hmaccipher
+ * @section_id: libpurple-hmaccipher
+ * @short_description: <filename>ciphers/hmaccipher.h</filename>
+ * @title: HMAC Cipher
+ */
+
+#include "cipher.h"
+
+#define PURPLE_TYPE_HMAC_CIPHER (purple_hmac_cipher_get_type())
+#define PURPLE_HMAC_CIPHER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_HMAC_CIPHER, PurpleHMACCipher))
+#define PURPLE_HMAC_CIPHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_HMAC_CIPHER, PurpleHMACCipherClass))
+#define PURPLE_IS_HMAC_CIPHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_HMAC_CIPHER))
+#define PURPLE_IS_HMAC_CIPHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), PURPLE_TYPE_HMAC_CIPHER))
+#define PURPLE_HMAC_CIPHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_HMAC_CIPHER, PurpleHMACCipherClass))
+
+typedef struct _PurpleHMACCipher PurpleHMACCipher;
+typedef struct _PurpleHMACCipherClass PurpleHMACCipherClass;
+
+struct _PurpleHMACCipher {
+ PurpleCipher gparent;
+};
+
+struct _PurpleHMACCipherClass {
+ PurpleCipherClass gparent;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+GType purple_hmac_cipher_get_type(void);
+
+PurpleCipher *purple_hmac_cipher_new(PurpleHash *hash);
+
+PurpleHash *purple_hmac_cipher_get_hash(const PurpleHMACCipher *cipher);
+
+G_END_DECLS
+
+#endif /* PURPLE_HMAC_CIPHER_H */
diff --git a/libpurple/ciphers/md4.c b/libpurple/ciphers/md4.c
deleted file mode 100644
index ac6654cf85..0000000000
--- a/libpurple/ciphers/md4.c
+++ /dev/null
@@ -1,297 +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.
- *
- * Original md4 taken from linux kernel
- * MD4 Message Digest Algorithm (RFC1320).
- *
- * Implementation derived from Andrew Tridgell and Steve French's
- * CIFS MD4 implementation, and the cryptoapi implementation
- * originally based on the public domain implementation written
- * by Colin Plumb in 1993.
- *
- * Copyright (c) Andrew Tridgell 1997-1998.
- * Modified by Steve French (sfrench@us.ibm.com) 2002
- * Copyright (c) Cryptoapi developers.
- * Copyright (c) 2002 David S. Miller (davem@redhat.com)
- * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
- *
- * 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 <cipher.h>
-
-#define MD4_DIGEST_SIZE 16
-#define MD4_HMAC_BLOCK_SIZE 64
-#define MD4_BLOCK_WORDS 16
-#define MD4_HASH_WORDS 4
-
-struct MD4_Context {
- guint32 hash[MD4_HASH_WORDS];
- guint32 block[MD4_BLOCK_WORDS];
- guint64 byte_count;
-};
-
-static inline guint32 lshift(guint32 x, unsigned int s)
-{
- x &= 0xFFFFFFFF;
- return ((x << s) & 0xFFFFFFFF) | (x >> (32 - s));
-}
-
-static inline guint32 F(guint32 x, guint32 y, guint32 z)
-{
- return (x & y) | ((~x) & z);
-}
-
-static inline guint32 G(guint32 x, guint32 y, guint32 z)
-{
- return (x & y) | (x & z) | (y & z);
-}
-
-static inline guint32 H(guint32 x, guint32 y, guint32 z)
-{
- return x ^ y ^ z;
-}
-
-#define ROUND1(a,b,c,d,k,s) (a = lshift(a + F(b,c,d) + k, s))
-#define ROUND2(a,b,c,d,k,s) (a = lshift(a + G(b,c,d) + k + (guint32)0x5A827999,s))
-#define ROUND3(a,b,c,d,k,s) (a = lshift(a + H(b,c,d) + k + (guint32)0x6ED9EBA1,s))
-
-static inline void le32_to_cpu_array(guint32 *buf, unsigned int words)
-{
- while (words--) {
- *buf=GUINT_FROM_LE(*buf);
- buf++;
- }
-}
-
-static inline void cpu_to_le32_array(guint32 *buf, unsigned int words)
-{
- while (words--) {
- *buf=GUINT_TO_LE(*buf);
- buf++;
- }
-}
-
-static void md4_transform(guint32 *hash, guint32 const *in)
-{
- guint32 a, b, c, d;
-
- a = hash[0];
- b = hash[1];
- c = hash[2];
- d = hash[3];
-
- ROUND1(a, b, c, d, in[0], 3);
- ROUND1(d, a, b, c, in[1], 7);
- ROUND1(c, d, a, b, in[2], 11);
- ROUND1(b, c, d, a, in[3], 19);
- ROUND1(a, b, c, d, in[4], 3);
- ROUND1(d, a, b, c, in[5], 7);
- ROUND1(c, d, a, b, in[6], 11);
- ROUND1(b, c, d, a, in[7], 19);
- ROUND1(a, b, c, d, in[8], 3);
- ROUND1(d, a, b, c, in[9], 7);
- ROUND1(c, d, a, b, in[10], 11);
- ROUND1(b, c, d, a, in[11], 19);
- ROUND1(a, b, c, d, in[12], 3);
- ROUND1(d, a, b, c, in[13], 7);
- ROUND1(c, d, a, b, in[14], 11);
- ROUND1(b, c, d, a, in[15], 19);
-
- ROUND2(a, b, c, d,in[ 0], 3);
- ROUND2(d, a, b, c, in[4], 5);
- ROUND2(c, d, a, b, in[8], 9);
- ROUND2(b, c, d, a, in[12], 13);
- ROUND2(a, b, c, d, in[1], 3);
- ROUND2(d, a, b, c, in[5], 5);
- ROUND2(c, d, a, b, in[9], 9);
- ROUND2(b, c, d, a, in[13], 13);
- ROUND2(a, b, c, d, in[2], 3);
- ROUND2(d, a, b, c, in[6], 5);
- ROUND2(c, d, a, b, in[10], 9);
- ROUND2(b, c, d, a, in[14], 13);
- ROUND2(a, b, c, d, in[3], 3);
- ROUND2(d, a, b, c, in[7], 5);
- ROUND2(c, d, a, b, in[11], 9);
- ROUND2(b, c, d, a, in[15], 13);
-
- ROUND3(a, b, c, d,in[ 0], 3);
- ROUND3(d, a, b, c, in[8], 9);
- ROUND3(c, d, a, b, in[4], 11);
- ROUND3(b, c, d, a, in[12], 15);
- ROUND3(a, b, c, d, in[2], 3);
- ROUND3(d, a, b, c, in[10], 9);
- ROUND3(c, d, a, b, in[6], 11);
- ROUND3(b, c, d, a, in[14], 15);
- ROUND3(a, b, c, d, in[1], 3);
- ROUND3(d, a, b, c, in[9], 9);
- ROUND3(c, d, a, b, in[5], 11);
- ROUND3(b, c, d, a, in[13], 15);
- ROUND3(a, b, c, d, in[3], 3);
- ROUND3(d, a, b, c, in[11], 9);
- ROUND3(c, d, a, b, in[7], 11);
- ROUND3(b, c, d, a, in[15], 15);
-
- hash[0] += a;
- hash[1] += b;
- hash[2] += c;
- hash[3] += d;
-}
-
-static inline void md4_transform_helper(struct MD4_Context *ctx)
-{
- le32_to_cpu_array(ctx->block, sizeof(ctx->block) / sizeof(guint32));
- md4_transform(ctx->hash, ctx->block);
-}
-
-static void
-md4_init(PurpleCipherContext *context, gpointer extra) {
- struct MD4_Context *mctx;
- mctx = g_new0(struct MD4_Context, 1);
- purple_cipher_context_set_data(context, mctx);
- purple_cipher_context_reset(context, extra);
-
- mctx->hash[0] = 0x67452301;
- mctx->hash[1] = 0xefcdab89;
- mctx->hash[2] = 0x98badcfe;
- mctx->hash[3] = 0x10325476;
- mctx->byte_count = 0;
-}
-
-static void
-md4_reset(PurpleCipherContext *context, gpointer extra) {
- struct MD4_Context *mctx;
-
- mctx = purple_cipher_context_get_data(context);
-
- mctx->hash[0] = 0x67452301;
- mctx->hash[1] = 0xefcdab89;
- mctx->hash[2] = 0x98badcfe;
- mctx->hash[3] = 0x10325476;
- mctx->byte_count = 0;
-}
-
- static void
-md4_append(PurpleCipherContext *context, const guchar *data, size_t len)
-{
- struct MD4_Context *mctx = purple_cipher_context_get_data(context);
- const guint32 avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f);
-
- mctx->byte_count += len;
-
- if (avail > len) {
- memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
- data, len);
- return;
- }
-
- memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
- data, avail);
-
- md4_transform_helper(mctx);
- data += avail;
- len -= avail;
-
- while (len >= sizeof(mctx->block)) {
- memcpy(mctx->block, data, sizeof(mctx->block));
- md4_transform_helper(mctx);
- data += sizeof(mctx->block);
- len -= sizeof(mctx->block);
- }
-
- memcpy(mctx->block, data, len);
-}
-
- static gboolean
-md4_digest(PurpleCipherContext *context, size_t in_len, guchar *out,
- size_t *out_len)
-{
- struct MD4_Context *mctx = purple_cipher_context_get_data(context);
- const unsigned int offset = mctx->byte_count & 0x3f;
- char *p = (char *)mctx->block + offset;
- int padding = 56 - (offset + 1);
-
-
- if(in_len<16) return FALSE;
- if(out_len) *out_len = 16;
- *p++ = 0x80;
- if (padding < 0) {
- memset(p, 0x00, padding + sizeof (guint64));
- md4_transform_helper(mctx);
- p = (char *)mctx->block;
- padding = 56;
- }
-
- memset(p, 0, padding);
- mctx->block[14] = mctx->byte_count << 3;
- mctx->block[15] = mctx->byte_count >> 29;
- le32_to_cpu_array(mctx->block, (sizeof(mctx->block) -
- sizeof(guint64)) / sizeof(guint32));
- md4_transform(mctx->hash, mctx->block);
- cpu_to_le32_array(mctx->hash, sizeof(mctx->hash) / sizeof(guint32));
- memcpy(out, mctx->hash, sizeof(mctx->hash));
- memset(mctx, 0, sizeof(*mctx));
- return TRUE;
-}
-
-static void
-md4_uninit(PurpleCipherContext *context) {
- struct MD4_Context *md4_context;
-
- purple_cipher_context_reset(context, NULL);
-
- md4_context = purple_cipher_context_get_data(context);
- memset(md4_context, 0, sizeof(*md4_context));
-
- g_free(md4_context);
- md4_context = NULL;
-}
-
- static size_t
-md4_get_block_size(PurpleCipherContext *context)
-{
- /* This does not change (in this case) */
- return MD4_HMAC_BLOCK_SIZE;
-}
-
-static PurpleCipherOps MD4Ops = {
- NULL, /* Set option */
- NULL, /* Get option */
- md4_init, /* init */
- md4_reset, /* reset */
- md4_uninit, /* uninit */
- NULL, /* set iv */
- md4_append, /* append */
- md4_digest, /* digest */
- NULL, /* encrypt */
- NULL, /* decrypt */
- NULL, /* set salt */
- NULL, /* get salt size */
- NULL, /* set key */
- NULL, /* get key size */
- NULL, /* set batch mode */
- NULL, /* get batch mode */
- md4_get_block_size, /* get block size */
- NULL /* set key with len */
-};
-
-PurpleCipherOps *
-purple_md4_cipher_get_ops(void) {
- return &MD4Ops;
-}
-
diff --git a/libpurple/ciphers/md4hash.c b/libpurple/ciphers/md4hash.c
new file mode 100644
index 0000000000..fca7b1b256
--- /dev/null
+++ b/libpurple/ciphers/md4hash.c
@@ -0,0 +1,311 @@
+/*
+ * Original md4 taken from linux kernel
+ * MD4 Message Digest Algorithm (RFC1320).
+ *
+ * Implementation derived from Andrew Tridgell and Steve French's
+ * CIFS MD4 implementation, and the cryptoapi implementation
+ * originally based on the public domain implementation written
+ * by Colin Plumb in 1993.
+ *
+ * Copyright (c) Andrew Tridgell 1997-1998.
+ * Modified by Steve French (sfrench@us.ibm.com) 2002
+ * Copyright (c) Cryptoapi developers.
+ * Copyright (c) 2002 David S. Miller (davem@redhat.com)
+ * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
+ */
+#include "internal.h"
+#include "md4hash.h"
+
+#include <string.h>
+
+#define MD4_DIGEST_SIZE 16
+#define MD4_BLOCK_WORDS 16
+#define MD4_HASH_WORDS 4
+
+#define PURPLE_MD4_HASH_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MD4_HASH, PurpleMD4HashPrivate))
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+typedef struct {
+ guint32 hash[MD4_HASH_WORDS];
+ guint32 block[MD4_BLOCK_WORDS];
+ guint64 byte_count;
+} PurpleMD4HashPrivate;
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+static GObjectClass *parent_class = NULL;
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+#define ROUND1(a,b,c,d,k,s) \
+ (a = lshift(a + F(b,c,d) + k, s))
+
+#define ROUND2(a,b,c,d,k,s) \
+ (a = lshift(a + G(b,c,d) + k + (guint32)0x5a827999,s))
+
+#define ROUND3(a,b,c,d,k,s) \
+ (a = lshift(a + H(b,c,d) + k + (guint32)0x6ed9eba1,s))
+
+static inline guint32
+lshift(guint32 x, unsigned int s) {
+ x &= 0xffffffff;
+ return (((x << s) & 0xffffffff) | (x >> (32 - s)));
+}
+
+static inline guint32
+F(guint32 x, guint32 y, guint32 z) {
+ return ((x & y) | ((~x) & z));
+}
+
+static inline guint32
+G(guint32 x, guint32 y, guint32 z) {
+ return ((x & y) | (x & z) | (y & z));
+}
+
+static inline guint32
+H(guint32 x, guint32 y, guint32 z) {
+ return (x ^ y ^ z);
+}
+
+static inline void
+le32_to_cpu_array(guint32 *buf, unsigned int words) {
+ while(words--) {
+ *buf = GUINT_FROM_LE(*buf);
+ buf++;
+ }
+}
+
+static inline void
+cpu_to_le32_array(guint32 *buf, unsigned int words) {
+ while(words--) {
+ *buf = GUINT_TO_LE(*buf);
+ buf++;
+ }
+}
+
+static void
+md4_transform(guint32 *hash, guint32 const *in) {
+ guint32 a, b, c, d;
+
+ a = hash[0];
+ b = hash[1];
+ c = hash[2];
+ d = hash[3];
+
+ ROUND1(a, b, c, d, in[0], 3);
+ ROUND1(d, a, b, c, in[1], 7);
+ ROUND1(c, d, a, b, in[2], 11);
+ ROUND1(b, c, d, a, in[3], 19);
+ ROUND1(a, b, c, d, in[4], 3);
+ ROUND1(d, a, b, c, in[5], 7);
+ ROUND1(c, d, a, b, in[6], 11);
+ ROUND1(b, c, d, a, in[7], 19);
+ ROUND1(a, b, c, d, in[8], 3);
+ ROUND1(d, a, b, c, in[9], 7);
+ ROUND1(c, d, a, b, in[10], 11);
+ ROUND1(b, c, d, a, in[11], 19);
+ ROUND1(a, b, c, d, in[12], 3);
+ ROUND1(d, a, b, c, in[13], 7);
+ ROUND1(c, d, a, b, in[14], 11);
+ ROUND1(b, c, d, a, in[15], 19);
+
+ ROUND2(a, b, c, d,in[ 0], 3);
+ ROUND2(d, a, b, c, in[4], 5);
+ ROUND2(c, d, a, b, in[8], 9);
+ ROUND2(b, c, d, a, in[12], 13);
+ ROUND2(a, b, c, d, in[1], 3);
+ ROUND2(d, a, b, c, in[5], 5);
+ ROUND2(c, d, a, b, in[9], 9);
+ ROUND2(b, c, d, a, in[13], 13);
+ ROUND2(a, b, c, d, in[2], 3);
+ ROUND2(d, a, b, c, in[6], 5);
+ ROUND2(c, d, a, b, in[10], 9);
+ ROUND2(b, c, d, a, in[14], 13);
+ ROUND2(a, b, c, d, in[3], 3);
+ ROUND2(d, a, b, c, in[7], 5);
+ ROUND2(c, d, a, b, in[11], 9);
+ ROUND2(b, c, d, a, in[15], 13);
+
+ ROUND3(a, b, c, d,in[ 0], 3);
+ ROUND3(d, a, b, c, in[8], 9);
+ ROUND3(c, d, a, b, in[4], 11);
+ ROUND3(b, c, d, a, in[12], 15);
+ ROUND3(a, b, c, d, in[2], 3);
+ ROUND3(d, a, b, c, in[10], 9);
+ ROUND3(c, d, a, b, in[6], 11);
+ ROUND3(b, c, d, a, in[14], 15);
+ ROUND3(a, b, c, d, in[1], 3);
+ ROUND3(d, a, b, c, in[9], 9);
+ ROUND3(c, d, a, b, in[5], 11);
+ ROUND3(b, c, d, a, in[13], 15);
+ ROUND3(a, b, c, d, in[3], 3);
+ ROUND3(d, a, b, c, in[11], 9);
+ ROUND3(c, d, a, b, in[7], 11);
+ ROUND3(b, c, d, a, in[15], 15);
+
+ hash[0] += a;
+ hash[1] += b;
+ hash[2] += c;
+ hash[3] += d;
+}
+
+static inline void
+md4_transform_helper(PurpleHash *hash) {
+ PurpleMD4HashPrivate *priv = PURPLE_MD4_HASH_GET_PRIVATE(hash);
+
+ le32_to_cpu_array(priv->block, sizeof(priv->block) / sizeof(guint32));
+ md4_transform(priv->hash, priv->block);
+}
+
+/******************************************************************************
+ * Hash Stuff
+ *****************************************************************************/
+static void
+purple_md4_hash_reset(PurpleHash *hash) {
+ PurpleMD4HashPrivate *priv = PURPLE_MD4_HASH_GET_PRIVATE(hash);
+
+ priv->hash[0] = 0x67452301;
+ priv->hash[1] = 0xefcdab89;
+ priv->hash[2] = 0x98badcfe;
+ priv->hash[3] = 0x10325476;
+
+ priv->byte_count = 0;
+
+ memset(priv->block, 0, sizeof(priv->block));
+}
+
+static void
+purple_md4_hash_append(PurpleHash *hash, const guchar *data, size_t len) {
+ PurpleMD4HashPrivate *priv = PURPLE_MD4_HASH_GET_PRIVATE(hash);
+ const guint32 avail = sizeof(priv->block) - (priv->byte_count & 0x3f);
+
+ priv->byte_count += len;
+
+ if(avail > len) {
+ memcpy((char *)priv->block +
+ (sizeof(priv->block) - avail),
+ data, len);
+ return;
+ }
+
+ memcpy((char *)priv->block +
+ (sizeof(priv->block) - avail),
+ data, avail);
+
+ md4_transform_helper(hash);
+ data += avail;
+ len -= avail;
+
+ while(len >= sizeof(priv->block)) {
+ memcpy(priv->block, data, sizeof(priv->block));
+ md4_transform_helper(hash);
+ data += sizeof(priv->block);
+ len -= sizeof(priv->block);
+ }
+
+ memcpy(priv->block, data, len);
+}
+
+static gboolean
+purple_md4_hash_digest(PurpleHash *hash, guchar *out, size_t len)
+{
+ PurpleMD4HashPrivate *priv = PURPLE_MD4_HASH_GET_PRIVATE(hash);
+ const unsigned int offset = priv->byte_count & 0x3f;
+ gchar *p = (gchar *)priv->block + offset;
+ gint padding = 56 - (offset + 1);
+
+ if(len < 16)
+ return FALSE;
+
+ *p++ = 0x80;
+
+ if(padding < 0) {
+ memset(p, 0x00, padding + sizeof(guint64));
+ md4_transform_helper(hash);
+ p = (gchar *)priv->block;
+ padding = 56;
+ }
+
+ memset(p, 0, padding);
+ priv->block[14] = priv->byte_count << 3;
+ priv->block[15] = priv->byte_count >> 29;
+ le32_to_cpu_array(priv->block,
+ (sizeof(priv->block) - sizeof(guint64)) /
+ sizeof(guint32));
+ md4_transform(priv->hash, priv->block);
+ cpu_to_le32_array(priv->hash, sizeof(priv->hash) / sizeof(guint32));
+ memcpy(out, priv->hash, sizeof(priv->hash));
+
+ return TRUE;
+}
+
+static size_t
+purple_md4_hash_get_digest_size(PurpleHash *hash)
+{
+ return 16;
+}
+
+static size_t
+purple_md4_hash_get_block_size(PurpleHash *hash)
+{
+ /* This does not change (in this case) */
+ return 64;
+}
+
+/******************************************************************************
+ * Object Stuff
+ *****************************************************************************/
+static void
+purple_md4_hash_class_init(PurpleMD4HashClass *klass) {
+ PurpleHashClass *hash_class = PURPLE_HASH_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ g_type_class_add_private(klass, sizeof(PurpleMD4HashPrivate));
+
+ hash_class->reset = purple_md4_hash_reset;
+ hash_class->reset_state = purple_md4_hash_reset;
+ hash_class->append = purple_md4_hash_append;
+ hash_class->digest = purple_md4_hash_digest;
+ hash_class->get_digest_size = purple_md4_hash_get_digest_size;
+ hash_class->get_block_size = purple_md4_hash_get_block_size;
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+GType
+purple_md4_hash_get_type(void) {
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleMD4HashClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_md4_hash_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleMD4Hash),
+ 0,
+ (GInstanceInitFunc)purple_hash_reset,
+ NULL,
+ };
+
+ type = g_type_register_static(PURPLE_TYPE_HASH,
+ "PurpleMD4Hash",
+ &info, 0);
+ }
+
+ return type;
+}
+
+PurpleHash *
+purple_md4_hash_new(void) {
+ return g_object_new(PURPLE_TYPE_MD4_HASH, NULL);
+}
diff --git a/libpurple/ciphers/md4hash.h b/libpurple/ciphers/md4hash.h
new file mode 100644
index 0000000000..101a6be950
--- /dev/null
+++ b/libpurple/ciphers/md4hash.h
@@ -0,0 +1,66 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PURPLE_MD4_HASH_H
+#define PURPLE_MD4_HASH_H
+/**
+ * SECTION:md4hash
+ * @section_id: libpurple-md4hash
+ * @short_description: <filename>ciphers/md4hash.h</filename>
+ * @title: MD4 Hash
+ */
+
+#include "cipher.h"
+
+#define PURPLE_TYPE_MD4_HASH (purple_md4_hash_get_type())
+#define PURPLE_MD4_HASH(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MD4_HASH, PurpleMD4Hash))
+#define PURPLE_MD4_HASH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MD4_HASH, PurpleMD4HashClass))
+#define PURPLE_IS_MD4_HASH(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_MD4_HASH))
+#define PURPLE_IS_MD4_HASH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), PURPLE_TYPE_MD4_HASH))
+#define PURPLE_MD4_HASH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MD4_HASH, PurpleMD4HashClass))
+
+typedef struct _PurpleMD4Hash PurpleMD4Hash;
+typedef struct _PurpleMD4HashClass PurpleMD4HashClass;
+
+struct _PurpleMD4Hash {
+ PurpleHash parent;
+};
+
+struct _PurpleMD4HashClass {
+ PurpleHashClass parent;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+GType purple_md4_hash_get_type(void);
+
+PurpleHash *purple_md4_hash_new(void);
+
+G_END_DECLS
+
+#endif /* PURPLE_MD4_HASH_H */
diff --git a/libpurple/ciphers/md5.c b/libpurple/ciphers/md5.c
deleted file mode 100644
index d739bc258d..0000000000
--- a/libpurple/ciphers/md5.c
+++ /dev/null
@@ -1,326 +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.
- *
- * Original md5
- * Copyright (C) 2001-2003 Christophe Devine <c.devine@cr0.net>
- *
- * 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 <cipher.h>
-
-#if !GLIB_CHECK_VERSION(2,16,0)
-
-#define MD5_HMAC_BLOCK_SIZE 64
-
-struct MD5Context {
- guint32 total[2];
- guint32 state[4];
- guchar buffer[64];
-};
-
-#define MD5_GET_GUINT32(n,b,i) { \
- (n) = ((guint32)(b) [(i) ] ) \
- | ((guint32)(b) [(i) + 1] << 8) \
- | ((guint32)(b) [(i) + 2] << 16) \
- | ((guint32)(b) [(i) + 3] << 24); \
-}
-#define MD5_PUT_GUINT32(n,b,i) { \
- (b)[(i) ] = (guchar)((n) ); \
- (b)[(i) + 1] = (guchar)((n) >> 8); \
- (b)[(i) + 2] = (guchar)((n) >> 16); \
- (b)[(i) + 3] = (guchar)((n) >> 24); \
-}
-
-static size_t
-md5_get_block_size(PurpleCipherContext *context)
-{
- /* This does not change (in this case) */
- return MD5_HMAC_BLOCK_SIZE;
-}
-
-static void
-md5_init(PurpleCipherContext *context, gpointer extra) {
- struct MD5Context *md5_context;
-
- md5_context = g_new0(struct MD5Context, 1);
-
- purple_cipher_context_set_data(context, md5_context);
-
- purple_cipher_context_reset(context, extra);
-}
-
-static void
-md5_reset(PurpleCipherContext *context, gpointer extra) {
- struct MD5Context *md5_context;
-
- md5_context = purple_cipher_context_get_data(context);
-
- md5_context->total[0] = 0;
- md5_context->total[1] = 0;
-
- md5_context->state[0] = 0x67452301;
- md5_context->state[1] = 0xEFCDAB89;
- md5_context->state[2] = 0x98BADCFE;
- md5_context->state[3] = 0x10325476;
-
- memset(md5_context->buffer, 0, sizeof(md5_context->buffer));
-}
-
-static void
-md5_uninit(PurpleCipherContext *context) {
- struct MD5Context *md5_context;
-
- purple_cipher_context_reset(context, NULL);
-
- md5_context = purple_cipher_context_get_data(context);
- memset(md5_context, 0, sizeof(*md5_context));
-
- g_free(md5_context);
- md5_context = NULL;
-}
-
-static void
-md5_process(struct MD5Context *md5_context, const guchar data[64]) {
- guint32 X[16], A, B, C, D;
-
- A = md5_context->state[0];
- B = md5_context->state[1];
- C = md5_context->state[2];
- D = md5_context->state[3];
-
- MD5_GET_GUINT32(X[ 0], data, 0);
- MD5_GET_GUINT32(X[ 1], data, 4);
- MD5_GET_GUINT32(X[ 2], data, 8);
- MD5_GET_GUINT32(X[ 3], data, 12);
- MD5_GET_GUINT32(X[ 4], data, 16);
- MD5_GET_GUINT32(X[ 5], data, 20);
- MD5_GET_GUINT32(X[ 6], data, 24);
- MD5_GET_GUINT32(X[ 7], data, 28);
- MD5_GET_GUINT32(X[ 8], data, 32);
- MD5_GET_GUINT32(X[ 9], data, 36);
- MD5_GET_GUINT32(X[10], data, 40);
- MD5_GET_GUINT32(X[11], data, 44);
- MD5_GET_GUINT32(X[12], data, 48);
- MD5_GET_GUINT32(X[13], data, 52);
- MD5_GET_GUINT32(X[14], data, 56);
- MD5_GET_GUINT32(X[15], data, 60);
-
-#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
-#define P(a,b,c,d,k,s,t) { \
- a += F(b,c,d) + X[k] + t; \
- a = S(a,s) + b; \
-}
-
- /* first pass */
-#define F(x,y,z) (z ^ (x & (y ^ z)))
- P(A, B, C, D, 0, 7, 0xD76AA478);
- P(D, A, B, C, 1, 12, 0xE8C7B756);
- P(C, D, A, B, 2, 17, 0x242070DB);
- P(B, C, D, A, 3, 22, 0xC1BDCEEE);
- P(A, B, C, D, 4, 7, 0xF57C0FAF);
- P(D, A, B, C, 5, 12, 0x4787C62A);
- P(C, D, A, B, 6, 17, 0xA8304613);
- P(B, C, D, A, 7, 22, 0xFD469501);
- P(A, B, C, D, 8, 7, 0x698098D8);
- P(D, A, B, C, 9, 12, 0x8B44F7AF);
- P(C, D, A, B, 10, 17, 0xFFFF5BB1);
- P(B, C, D, A, 11, 22, 0x895CD7BE);
- P(A, B, C, D, 12, 7, 0x6B901122);
- P(D, A, B, C, 13, 12, 0xFD987193);
- P(C, D, A, B, 14, 17, 0xA679438E);
- P(B, C, D, A, 15, 22, 0x49B40821);
-#undef F
-
- /* second pass */
-#define F(x,y,z) (y ^ (z & (x ^ y)))
- P(A, B, C, D, 1, 5, 0xF61E2562);
- P(D, A, B, C, 6, 9, 0xC040B340);
- P(C, D, A, B, 11, 14, 0x265E5A51);
- P(B, C, D, A, 0, 20, 0xE9B6C7AA);
- P(A, B, C, D, 5, 5, 0xD62F105D);
- P(D, A, B, C, 10, 9, 0x02441453);
- P(C, D, A, B, 15, 14, 0xD8A1E681);
- P(B, C, D, A, 4, 20, 0xE7D3FBC8);
- P(A, B, C, D, 9, 5, 0x21E1CDE6);
- P(D, A, B, C, 14, 9, 0xC33707D6);
- P(C, D, A, B, 3, 14, 0xF4D50D87);
- P(B, C, D, A, 8, 20, 0x455A14ED);
- P(A, B, C, D, 13, 5, 0xA9E3E905);
- P(D, A, B, C, 2, 9, 0xFCEFA3F8);
- P(C, D, A, B, 7, 14, 0x676F02D9);
- P(B, C, D, A, 12, 20, 0x8D2A4C8A);
-#undef F
-
- /* third pass */
-#define F(x,y,z) (x ^ y ^ z)
- P(A, B, C, D, 5, 4, 0xFFFA3942);
- P(D, A, B, C, 8, 11, 0x8771F681);
- P(C, D, A, B, 11, 16, 0x6D9D6122);
- P(B, C, D, A, 14, 23, 0xFDE5380C);
- P(A, B, C, D, 1, 4, 0xA4BEEA44);
- P(D, A, B, C, 4, 11, 0x4BDECFA9);
- P(C, D, A, B, 7, 16, 0xF6BB4B60);
- P(B, C, D, A, 10, 23, 0xBEBFBC70);
- P(A, B, C, D, 13, 4, 0x289B7EC6);
- P(D, A, B, C, 0, 11, 0xEAA127FA);
- P(C, D, A, B, 3, 16, 0xD4EF3085);
- P(B, C, D, A, 6, 23, 0x04881D05);
- P(A, B, C, D, 9, 4, 0xD9D4D039);
- P(D, A, B, C, 12, 11, 0xE6DB99E5);
- P(C, D, A, B, 15, 16, 0x1FA27CF8);
- P(B, C, D, A, 2, 23, 0xC4AC5665);
-#undef F
-
- /* forth pass */
-#define F(x,y,z) (y ^ (x | ~z))
- P(A, B, C, D, 0, 6, 0xF4292244);
- P(D, A, B, C, 7, 10, 0x432AFF97);
- P(C, D, A, B, 14, 15, 0xAB9423A7);
- P(B, C, D, A, 5, 21, 0xFC93A039);
- P(A, B, C, D, 12, 6, 0x655B59C3);
- P(D, A, B, C, 3, 10, 0x8F0CCC92);
- P(C, D, A, B, 10, 15, 0xFFEFF47D);
- P(B, C, D, A, 1, 21, 0x85845DD1);
- P(A, B, C, D, 8, 6, 0x6FA87E4F);
- P(D, A, B, C, 15, 10, 0xFE2CE6E0);
- P(C, D, A, B, 6, 15, 0xA3014314);
- P(B, C, D, A, 13, 21, 0x4E0811A1);
- P(A, B, C, D, 4, 6, 0xF7537E82);
- P(D, A, B, C, 11, 10, 0xBD3AF235);
- P(C, D, A, B, 2, 15, 0x2AD7D2BB);
- P(B, C, D, A, 9, 21, 0xEB86D391);
-#undef F
-#undef P
-#undef S
-
- md5_context->state[0] += A;
- md5_context->state[1] += B;
- md5_context->state[2] += C;
- md5_context->state[3] += D;
- }
-
-static void
-md5_append(PurpleCipherContext *context, const guchar *data, size_t len) {
- struct MD5Context *md5_context = NULL;
- guint32 left = 0, fill = 0;
-
- g_return_if_fail(context != NULL);
-
- md5_context = purple_cipher_context_get_data(context);
- g_return_if_fail(md5_context != NULL);
-
- left = md5_context->total[0] & 0x3F;
- fill = 64 - left;
-
- md5_context->total[0] += len;
- md5_context->total[0] &= 0xFFFFFFFF;
-
- if(md5_context->total[0] < len)
- md5_context->total[1]++;
-
- if(left && len >= fill) {
- memcpy((md5_context->buffer + left), data, fill);
- md5_process(md5_context, md5_context->buffer);
- len -= fill;
- data += fill;
- left = 0;
- }
-
- while(len >= 64) {
- md5_process(md5_context, data);
- len -= 64;
- data += 64;
- }
-
- if(len) {
- memcpy((md5_context->buffer + left), data, len);
- }
-}
-
- static gboolean
-md5_digest(PurpleCipherContext *context, size_t in_len, guchar digest[16],
- size_t *out_len)
-{
- struct MD5Context *md5_context = NULL;
- guint32 last, pad;
- guint32 high, low;
- guchar message[8];
- guchar padding[64] = {
- 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- };
-
- g_return_val_if_fail(in_len >= 16, FALSE);
-
- md5_context = purple_cipher_context_get_data(context);
-
- high = (md5_context->total[0] >> 29)
- | (md5_context->total[1] << 3);
- low = (md5_context->total[0] << 3);
-
- MD5_PUT_GUINT32(low, message, 0);
- MD5_PUT_GUINT32(high, message, 4);
-
- last = md5_context->total[0] & 0x3F;
- pad = (last < 56) ? (56 - last) : (120 - last);
-
- md5_append(context, padding, pad);
- md5_append(context, message, 8);
-
- MD5_PUT_GUINT32(md5_context->state[0], digest, 0);
- MD5_PUT_GUINT32(md5_context->state[1], digest, 4);
- MD5_PUT_GUINT32(md5_context->state[2], digest, 8);
- MD5_PUT_GUINT32(md5_context->state[3], digest, 12);
-
- if(out_len)
- *out_len = 16;
-
- return TRUE;
-}
-
-static PurpleCipherOps MD5Ops = {
- NULL, /* Set Option */
- NULL, /* Get Option */
- md5_init, /* init */
- md5_reset, /* reset */
- md5_uninit, /* uninit */
- NULL, /* set iv */
- md5_append, /* append */
- md5_digest, /* digest */
- NULL, /* encrypt */
- NULL, /* decrypt */
- NULL, /* set salt */
- NULL, /* get salt size */
- NULL, /* set key */
- NULL, /* get key size */
- NULL, /* set batch mode */
- NULL, /* get batch mode */
- md5_get_block_size, /* get block size */
- NULL /* set key with len */
-};
-
-PurpleCipherOps *
-purple_md5_cipher_get_ops(void) {
- return &MD5Ops;
-}
-
-#endif /* !GLIB_CHECK_VERSION(2,16,0) */
-
diff --git a/libpurple/ciphers/md5hash.c b/libpurple/ciphers/md5hash.c
new file mode 100644
index 0000000000..51d3e3f2a9
--- /dev/null
+++ b/libpurple/ciphers/md5hash.c
@@ -0,0 +1,191 @@
+/*
+ * 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 "md5hash.h"
+
+/*******************************************************************************
+ * Structs
+ ******************************************************************************/
+#define PURPLE_MD5_HASH_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MD5_HASH, PurpleMD5HashPrivate))
+
+typedef struct {
+ GChecksum *checksum;
+} PurpleMD5HashPrivate;
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+static GObjectClass *parent_class = NULL;
+
+/******************************************************************************
+ * Hash Stuff
+ *****************************************************************************/
+
+static void
+purple_md5_hash_reset(PurpleHash *hash)
+{
+ PurpleMD5Hash *md5_hash = PURPLE_MD5_HASH(hash);
+ PurpleMD5HashPrivate *priv = PURPLE_MD5_HASH_GET_PRIVATE(md5_hash);
+
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(priv->checksum != NULL);
+
+ g_checksum_reset(priv->checksum);
+}
+
+static void
+purple_md5_hash_append(PurpleHash *hash, const guchar *data,
+ gsize len)
+{
+ PurpleMD5Hash *md5_hash = PURPLE_MD5_HASH(hash);
+ PurpleMD5HashPrivate *priv = PURPLE_MD5_HASH_GET_PRIVATE(md5_hash);
+
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(priv->checksum != NULL);
+
+ while (len >= G_MAXSSIZE) {
+ g_checksum_update(priv->checksum, data, G_MAXSSIZE);
+ len -= G_MAXSSIZE;
+ data += G_MAXSSIZE;
+ }
+
+ if (len)
+ g_checksum_update(priv->checksum, data, len);
+}
+
+static gboolean
+purple_md5_hash_digest(PurpleHash *hash, guchar *digest, size_t buff_len)
+{
+ PurpleMD5Hash *md5_hash = PURPLE_MD5_HASH(hash);
+ PurpleMD5HashPrivate *priv = PURPLE_MD5_HASH_GET_PRIVATE(md5_hash);
+
+ const gssize required_len = g_checksum_type_get_length(G_CHECKSUM_MD5);
+ gsize digest_len = buff_len;
+
+ g_return_val_if_fail(priv != NULL, FALSE);
+ g_return_val_if_fail(priv->checksum != NULL, FALSE);
+
+ g_return_val_if_fail(required_len >= 0, FALSE);
+ g_return_val_if_fail(buff_len >= (gsize)required_len, FALSE);
+
+ g_checksum_get_digest(priv->checksum, digest, &digest_len);
+
+ if (digest_len != (gsize)required_len)
+ return FALSE;
+
+ purple_md5_hash_reset(hash);
+
+ return TRUE;
+}
+
+static size_t
+purple_md5_hash_get_block_size(PurpleHash *hash)
+{
+ return 64;
+}
+
+static size_t
+purple_md5_hash_get_digest_size(PurpleHash *hash)
+{
+ return g_checksum_type_get_length(G_CHECKSUM_MD5);
+}
+
+/******************************************************************************
+ * Object Stuff
+ *****************************************************************************/
+
+static void
+purple_md5_hash_finalize(GObject *obj)
+{
+ PurpleMD5Hash *md5_hash = PURPLE_MD5_HASH(obj);
+ PurpleMD5HashPrivate *priv = PURPLE_MD5_HASH_GET_PRIVATE(md5_hash);
+
+ if (priv->checksum)
+ g_checksum_free(priv->checksum);
+
+ parent_class->finalize(obj);
+}
+
+static void
+purple_md5_hash_class_init(PurpleMD5HashClass *klass) {
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+ PurpleHashClass *hash_class = PURPLE_HASH_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ obj_class->finalize = purple_md5_hash_finalize;
+
+ hash_class->reset = purple_md5_hash_reset;
+ hash_class->reset_state = purple_md5_hash_reset;
+ hash_class->append = purple_md5_hash_append;
+ hash_class->digest = purple_md5_hash_digest;
+ hash_class->get_digest_size = purple_md5_hash_get_digest_size;
+ hash_class->get_block_size = purple_md5_hash_get_block_size;
+
+ g_type_class_add_private(klass, sizeof(PurpleMD5HashPrivate));
+}
+
+static void
+purple_md5_hash_init(PurpleHash *hash)
+{
+ PurpleMD5Hash *md5_hash = PURPLE_MD5_HASH(hash);
+ PurpleMD5HashPrivate *priv = PURPLE_MD5_HASH_GET_PRIVATE(md5_hash);
+
+ priv->checksum = g_checksum_new(G_CHECKSUM_MD5);
+
+ purple_md5_hash_reset(hash);
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+GType
+purple_md5_hash_get_type(void) {
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleMD5HashClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_md5_hash_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleMD5Hash),
+ 0,
+ (GInstanceInitFunc)purple_md5_hash_init,
+ NULL,
+ };
+
+ type = g_type_register_static(PURPLE_TYPE_HASH,
+ "PurpleMD5Hash",
+ &info, 0);
+ }
+
+ return type;
+}
+
+PurpleHash *
+purple_md5_hash_new(void) {
+ return g_object_new(PURPLE_TYPE_MD5_HASH, NULL);
+}
diff --git a/libpurple/ciphers/md5hash.h b/libpurple/ciphers/md5hash.h
new file mode 100644
index 0000000000..f5bff931b7
--- /dev/null
+++ b/libpurple/ciphers/md5hash.h
@@ -0,0 +1,65 @@
+/* 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 PURPLE_MD5_HASH_H
+#define PURPLE_MD5_HASH_H
+/**
+ * SECTION:md5hash
+ * @section_id: libpurple-md5hash
+ * @short_description: <filename>ciphers/md5hash.h</filename>
+ * @title: MD5 Hash
+ */
+
+#include "cipher.h"
+
+#define PURPLE_TYPE_MD5_HASH (purple_md5_hash_get_type())
+#define PURPLE_MD5_HASH(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MD5_HASH, PurpleMD5Hash))
+#define PURPLE_MD5_HASH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MD5_HASH, PurpleMD5HashClass))
+#define PURPLE_IS_MD5_HASH(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_MD5_HASH))
+#define PURPLE_IS_MD5_HASH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), PURPLE_TYPE_MD5_HASH))
+#define PURPLE_MD5_HASH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MD5_HASH, PurpleMD5HashClass))
+
+typedef struct _PurpleMD5Hash PurpleMD5Hash;
+typedef struct _PurpleMD5HashClass PurpleMD5HashClass;
+
+struct _PurpleMD5Hash {
+ PurpleHash gparent;
+};
+
+struct _PurpleMD5HashClass {
+ PurpleHashClass gparent;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+GType purple_md5_hash_get_type(void);
+
+PurpleHash *purple_md5_hash_new(void);
+
+G_END_DECLS
+
+#endif /* PURPLE_MD5_HASH_H */
diff --git a/libpurple/ciphers/pbkdf2cipher.c b/libpurple/ciphers/pbkdf2cipher.c
new file mode 100644
index 0000000000..b32a006ede
--- /dev/null
+++ b/libpurple/ciphers/pbkdf2cipher.c
@@ -0,0 +1,420 @@
+/*
+ * 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
+ *
+ * Written by Tomek Wasilczyk <twasilczyk@pidgin.im>
+ */
+#include "internal.h"
+#include "glibcompat.h"
+
+#include "pbkdf2cipher.h"
+#include "hmaccipher.h"
+#include "debug.h"
+
+/* 1024bit */
+#define PBKDF2_HASH_MAX_LEN 128
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+#define PURPLE_PBKDF2_CIPHER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_PBKDF2_CIPHER, PurplePBKDF2CipherPrivate))
+
+typedef struct {
+ PurpleHash *hash;
+ guint iter_count;
+ size_t out_len;
+
+ guchar *salt;
+ size_t salt_len;
+ guchar *passphrase;
+ size_t passphrase_len;
+} PurplePBKDF2CipherPrivate;
+
+/******************************************************************************
+ * Enums
+ *****************************************************************************/
+enum {
+ PROP_NONE,
+ PROP_HASH,
+ PROP_ITER_COUNT,
+ PROP_OUT_LEN,
+ PROP_LAST,
+};
+
+/*******************************************************************************
+ * Globals
+ ******************************************************************************/
+static GObjectClass *parent_class = NULL;
+static GParamSpec *properties[PROP_LAST];
+
+/*******************************************************************************
+ * Helpers
+ ******************************************************************************/
+static void
+purple_pbkdf2_cipher_set_hash(PurpleCipher *cipher,
+ PurpleHash *hash)
+{
+ PurplePBKDF2CipherPrivate *priv = PURPLE_PBKDF2_CIPHER_GET_PRIVATE(cipher);
+
+ priv->hash = g_object_ref(G_OBJECT(hash));
+
+ g_object_notify_by_pspec(G_OBJECT(cipher), properties[PROP_HASH]);
+}
+
+/******************************************************************************
+ * Cipher Stuff
+ *****************************************************************************/
+static void
+purple_pbkdf2_cipher_reset(PurpleCipher *cipher)
+{
+ PurplePBKDF2CipherPrivate *priv = PURPLE_PBKDF2_CIPHER_GET_PRIVATE(cipher);
+
+ g_return_if_fail(priv != NULL);
+
+ if(PURPLE_IS_HASH(priv->hash))
+ purple_hash_reset(priv->hash);
+ priv->iter_count = 1;
+ priv->out_len = 256;
+
+ purple_cipher_reset_state(cipher);
+}
+
+static void
+purple_pbkdf2_cipher_reset_state(PurpleCipher *cipher)
+{
+ PurplePBKDF2CipherPrivate *priv = PURPLE_PBKDF2_CIPHER_GET_PRIVATE(cipher);
+
+ g_return_if_fail(priv != NULL);
+
+ purple_cipher_set_salt(cipher, NULL, 0);
+ purple_cipher_set_key(cipher, NULL, 0);
+}
+
+static size_t
+purple_pbkdf2_cipher_get_digest_size(PurpleCipher *cipher)
+{
+ PurplePBKDF2CipherPrivate *priv = PURPLE_PBKDF2_CIPHER_GET_PRIVATE(cipher);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return priv->out_len;
+}
+
+static void
+purple_pbkdf2_cipher_set_salt(PurpleCipher *cipher, const guchar *salt, size_t len)
+{
+ PurplePBKDF2CipherPrivate *priv = PURPLE_PBKDF2_CIPHER_GET_PRIVATE(cipher);
+
+ g_return_if_fail(priv != NULL);
+
+ g_free(priv->salt);
+ priv->salt = NULL;
+ priv->salt_len = 0;
+
+ if (len == 0)
+ return;
+ g_return_if_fail(salt != NULL);
+
+ priv->salt = g_memdup(salt, len);
+ priv->salt_len = len;
+}
+
+static void
+purple_pbkdf2_cipher_set_key(PurpleCipher *cipher, const guchar *key,
+ size_t len)
+{
+ PurplePBKDF2CipherPrivate *priv = PURPLE_PBKDF2_CIPHER_GET_PRIVATE(cipher);
+
+ g_return_if_fail(priv != NULL);
+
+ if (priv->passphrase != NULL) {
+ memset(priv->passphrase, 0, priv->passphrase_len);
+ g_free(priv->passphrase);
+ priv->passphrase = NULL;
+ }
+ priv->passphrase_len = 0;
+
+ if (len == 0)
+ return;
+ g_return_if_fail(key != NULL);
+
+ priv->passphrase = g_memdup(key, len);
+ priv->passphrase_len = len;
+}
+
+/* inspired by gnutls 3.1.10, pbkdf2-sha1.c */
+static gboolean
+purple_pbkdf2_cipher_digest(PurpleCipher *cipher, guchar digest[], size_t len)
+{
+ PurplePBKDF2CipherPrivate *priv = PURPLE_PBKDF2_CIPHER_GET_PRIVATE(cipher);
+
+ guchar halfkey[PBKDF2_HASH_MAX_LEN], halfkey_hash[PBKDF2_HASH_MAX_LEN];
+ guint halfkey_len, halfkey_count, halfkey_pad, halfkey_no;
+ guchar *salt_ext;
+ size_t salt_ext_len;
+ guint iter_no;
+ PurpleCipher *hash;
+
+ g_return_val_if_fail(priv != NULL, FALSE);
+ g_return_val_if_fail(digest != NULL, FALSE);
+ g_return_val_if_fail(len >= priv->out_len, FALSE);
+
+ g_return_val_if_fail(priv->hash != NULL, FALSE);
+ g_return_val_if_fail(priv->iter_count > 0, FALSE);
+ g_return_val_if_fail(priv->passphrase != NULL ||
+ priv->passphrase_len == 0, FALSE);
+ g_return_val_if_fail(priv->salt != NULL || priv->salt_len == 0,
+ FALSE);
+ g_return_val_if_fail(priv->out_len > 0, FALSE);
+ g_return_val_if_fail(priv->out_len < 0xFFFFFFFFU, FALSE);
+
+ salt_ext_len = priv->salt_len + 4;
+
+ hash = purple_hmac_cipher_new(priv->hash);
+ if (hash == NULL) {
+ purple_debug_error("pbkdf2", "Couldn't create new hmac "
+ "cipher\n");
+ return FALSE;
+ }
+ purple_cipher_set_key(hash, (const guchar*)priv->passphrase,
+ priv->passphrase_len);
+
+ halfkey_len = purple_cipher_get_digest_size(hash);
+ if (halfkey_len <= 0 || halfkey_len > PBKDF2_HASH_MAX_LEN) {
+ purple_debug_error("pbkdf2", "Unsupported hash function. "
+ "(digest size: %d)\n", halfkey_len);
+ return FALSE;
+ }
+
+ halfkey_count = ((priv->out_len - 1) / halfkey_len) + 1;
+ halfkey_pad = priv->out_len - (halfkey_count - 1) * halfkey_len;
+
+ salt_ext = g_new(guchar, salt_ext_len);
+ if (priv->salt_len > 0)
+ memcpy(salt_ext, priv->salt, priv->salt_len);
+
+ for (halfkey_no = 1; halfkey_no <= halfkey_count; halfkey_no++) {
+ memset(halfkey, 0, halfkey_len);
+
+ for (iter_no = 1; iter_no <= priv->iter_count; iter_no++) {
+ guint i;
+
+ purple_cipher_reset_state(hash);
+
+ if (iter_no == 1) {
+ salt_ext[salt_ext_len - 4] =
+ (halfkey_no & 0xff000000) >> 24;
+ salt_ext[salt_ext_len - 3] =
+ (halfkey_no & 0x00ff0000) >> 16;
+ salt_ext[salt_ext_len - 2] =
+ (halfkey_no & 0x0000ff00) >> 8;
+ salt_ext[salt_ext_len - 1] =
+ (halfkey_no & 0x000000ff) >> 0;
+
+ purple_cipher_append(hash, salt_ext,
+ salt_ext_len);
+ }
+ else
+ purple_cipher_append(hash, halfkey_hash,
+ halfkey_len);
+
+ if (!purple_cipher_digest(hash, halfkey_hash,
+ halfkey_len)) {
+ purple_debug_error("pbkdf2",
+ "Couldn't retrieve a digest\n");
+ g_free(salt_ext);
+ g_object_unref(hash);
+ return FALSE;
+ }
+
+ for (i = 0; i < halfkey_len; i++)
+ halfkey[i] ^= halfkey_hash[i];
+ }
+
+ memcpy(digest + (halfkey_no - 1) * halfkey_len, halfkey,
+ (halfkey_no == halfkey_count) ? halfkey_pad :
+ halfkey_len);
+ }
+
+ g_free(salt_ext);
+ g_object_unref(hash);
+
+ return TRUE;
+}
+
+/******************************************************************************
+ * Object Stuff
+ *****************************************************************************/
+static void
+purple_pbkdf2_cipher_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurplePBKDF2Cipher *cipher = PURPLE_PBKDF2_CIPHER(obj);
+ PurplePBKDF2CipherPrivate *priv = PURPLE_PBKDF2_CIPHER_GET_PRIVATE(cipher);
+
+ switch(param_id) {
+ case PROP_HASH:
+ g_value_set_object(value, purple_pbkdf2_cipher_get_hash(cipher));
+ break;
+ case PROP_ITER_COUNT:
+ g_value_set_uint(value, priv->iter_count);
+ break;
+ case PROP_OUT_LEN:
+ g_value_set_uint(value, priv->out_len);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_pbkdf2_cipher_set_property(GObject *obj, guint param_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ PurpleCipher *cipher = PURPLE_CIPHER(obj);
+ PurplePBKDF2CipherPrivate *priv = PURPLE_PBKDF2_CIPHER_GET_PRIVATE(cipher);
+
+ switch(param_id) {
+ case PROP_HASH:
+ purple_pbkdf2_cipher_set_hash(cipher, g_value_get_object(value));
+ break;
+ case PROP_ITER_COUNT:
+ priv->iter_count = g_value_get_uint(value);
+ break;
+ case PROP_OUT_LEN:
+ priv->out_len = g_value_get_uint(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_pbkdf2_cipher_finalize(GObject *obj)
+{
+ PurpleCipher *cipher = PURPLE_CIPHER(obj);
+ PurplePBKDF2CipherPrivate *priv = PURPLE_PBKDF2_CIPHER_GET_PRIVATE(cipher);
+
+ purple_pbkdf2_cipher_reset(cipher);
+
+ if (priv->hash != NULL)
+ g_object_unref(priv->hash);
+
+ parent_class->finalize(obj);
+}
+
+static void
+purple_pbkdf2_cipher_class_init(PurplePBKDF2CipherClass *klass) {
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+ PurpleCipherClass *cipher_class = PURPLE_CIPHER_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ obj_class->finalize = purple_pbkdf2_cipher_finalize;
+ obj_class->get_property = purple_pbkdf2_cipher_get_property;
+ obj_class->set_property = purple_pbkdf2_cipher_set_property;
+
+ cipher_class->reset = purple_pbkdf2_cipher_reset;
+ cipher_class->reset_state = purple_pbkdf2_cipher_reset_state;
+ cipher_class->digest = purple_pbkdf2_cipher_digest;
+ cipher_class->get_digest_size = purple_pbkdf2_cipher_get_digest_size;
+ cipher_class->set_salt = purple_pbkdf2_cipher_set_salt;
+ cipher_class->set_key = purple_pbkdf2_cipher_set_key;
+
+ g_type_class_add_private(klass, sizeof(PurplePBKDF2CipherPrivate));
+
+ properties[PROP_HASH] = g_param_spec_object("hash", "hash", "hash",
+ PURPLE_TYPE_HASH,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_ITER_COUNT] = g_param_spec_uint("iter-count", "iter-count",
+ "iter-count", 0,
+ G_MAXUINT, 0, G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_OUT_LEN] = g_param_spec_uint("out-len", "out-len",
+ "out-len", 0,
+ G_MAXUINT, 0, G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, PROP_LAST, properties);
+}
+
+static void
+purple_pbkdf2_cipher_init(PurpleCipher *cipher)
+{
+ purple_cipher_reset(cipher);
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+GType
+purple_pbkdf2_cipher_get_type(void) {
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurplePBKDF2CipherClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_pbkdf2_cipher_class_init,
+ NULL,
+ NULL,
+ sizeof(PurplePBKDF2Cipher),
+ 0,
+ (GInstanceInitFunc)purple_pbkdf2_cipher_init,
+ NULL
+ };
+
+ type = g_type_register_static(PURPLE_TYPE_CIPHER,
+ "PurplePBKDF2Cipher",
+ &info, 0);
+ }
+
+ return type;
+}
+
+PurpleCipher *
+purple_pbkdf2_cipher_new(PurpleHash *hash) {
+ g_return_val_if_fail(PURPLE_IS_HASH(hash), NULL);
+
+ return g_object_new(PURPLE_TYPE_PBKDF2_CIPHER,
+ "hash", hash,
+ NULL);
+}
+
+PurpleHash *
+purple_pbkdf2_cipher_get_hash(const PurplePBKDF2Cipher *cipher) {
+ PurplePBKDF2CipherPrivate *priv = NULL;
+
+ g_return_val_if_fail(PURPLE_IS_PBKDF2_CIPHER(cipher), NULL);
+
+ priv = PURPLE_PBKDF2_CIPHER_GET_PRIVATE(cipher);
+
+ if(priv && priv->hash)
+ return priv->hash;
+
+ return NULL;
+}
diff --git a/libpurple/ciphers/pbkdf2cipher.h b/libpurple/ciphers/pbkdf2cipher.h
new file mode 100644
index 0000000000..bfa8daea6f
--- /dev/null
+++ b/libpurple/ciphers/pbkdf2cipher.h
@@ -0,0 +1,68 @@
+/* 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 PURPLE_PBKDF2_CIPHER_H
+#define PURPLE_PBKDF2_CIPHER_H
+/**
+ * SECTION:pbkdf2cipher
+ * @section_id: libpurple-pbkdf2cipher
+ * @short_description: <filename>ciphers/pbkdf2cipher.h</filename>
+ * @title: PBKDF2 Cipher
+ */
+
+#include "cipher.h"
+
+#define PURPLE_TYPE_PBKDF2_CIPHER (purple_pbkdf2_cipher_get_type())
+#define PURPLE_PBKDF2_CIPHER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_PBKDF2_CIPHER, PurplePBKDF2Cipher))
+#define PURPLE_PBKDF2_CIPHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_PBKDF2_CIPHER, PurplePBKDF2CipherClass))
+#define PURPLE_IS_PBKDF2_CIPHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PBKDF2_CIPHER))
+#define PURPLE_IS_PBKDF2_CIPHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), PURPLE_TYPE_PBKDF2_CIPHER))
+#define PURPLE_PBKDF2_CIPHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_PBKDF2_CIPHER, PurplePBKDF2CipherClass))
+
+typedef struct _PurplePBKDF2Cipher PurplePBKDF2Cipher;
+typedef struct _PurplePBKDF2CipherClass PurplePBKDF2CipherClass;
+
+struct _PurplePBKDF2Cipher {
+ PurpleCipher gparent;
+};
+
+struct _PurplePBKDF2CipherClass {
+ PurpleCipherClass gparent;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+GType purple_pbkdf2_cipher_get_type(void);
+
+PurpleCipher *purple_pbkdf2_cipher_new(PurpleHash *hash);
+
+PurpleHash *purple_pbkdf2_cipher_get_hash(const PurplePBKDF2Cipher *cipher);
+
+G_END_DECLS
+
+#endif /* PURPLE_PBKDF2_CIPHER_H */
+
diff --git a/libpurple/ciphers/rc4.c b/libpurple/ciphers/rc4.c
deleted file mode 100644
index 2918f24548..0000000000
--- a/libpurple/ciphers/rc4.c
+++ /dev/null
@@ -1,192 +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
- */
-#include <cipher.h>
-#include <util.h>
-
-struct RC4Context {
- guchar state[256];
- guchar x;
- guchar y;
- gint key_len;
-};
-
-static void
-rc4_init(PurpleCipherContext *context, void *extra) {
- struct RC4Context *rc4_ctx;
- rc4_ctx = g_new0(struct RC4Context, 1);
- purple_cipher_context_set_data(context, rc4_ctx);
- purple_cipher_context_reset(context, extra);
-}
-
-
-static void
-rc4_reset(PurpleCipherContext *context, void *extra) {
- struct RC4Context *rc4_ctx;
- guint i;
-
- rc4_ctx = purple_cipher_context_get_data(context);
-
- g_return_if_fail(rc4_ctx);
-
- for(i = 0; i < 256; i++)
- rc4_ctx->state[i] = i;
- rc4_ctx->x = 0;
- rc4_ctx->y = 0;
-
- /* default is 5 bytes (40bit key) */
- rc4_ctx->key_len = 5;
-
-}
-
-static void
-rc4_uninit(PurpleCipherContext *context) {
- struct RC4Context *rc4_ctx;
-
- rc4_ctx = purple_cipher_context_get_data(context);
- memset(rc4_ctx, 0, sizeof(*rc4_ctx));
-
- g_free(rc4_ctx);
- rc4_ctx = NULL;
-}
-
-
-
-static void
-rc4_set_key (PurpleCipherContext *context, const guchar * key) {
- struct RC4Context *ctx;
- guchar *state;
- guchar temp_swap;
- guchar x, y;
- guint i;
-
- ctx = purple_cipher_context_get_data(context);
-
- x = 0;
- y = 0;
- state = &ctx->state[0];
- for(i = 0; i < 256; i++)
- {
- y = (key[x] + state[i] + y) % 256;
- temp_swap = state[i];
- state[i] = state[y];
- state[y] = temp_swap;
- x = (x + 1) % ctx->key_len;
- }
-}
-
-static void
-rc4_set_opt(PurpleCipherContext *context, const gchar *name, void *value) {
- struct RC4Context *ctx;
-
- ctx = purple_cipher_context_get_data(context);
-
- if(purple_strequal(name, "key_len")) {
- ctx->key_len = GPOINTER_TO_INT(value);
- }
-}
-
-static size_t
-rc4_get_key_size (PurpleCipherContext *context)
-{
- struct RC4Context *ctx;
-
- g_return_val_if_fail(context, -1);
-
- ctx = purple_cipher_context_get_data(context);
-
- g_return_val_if_fail(ctx, -1);
-
- return ctx->key_len;
-}
-
-static void *
-rc4_get_opt(PurpleCipherContext *context, const gchar *name) {
- struct RC4Context *ctx;
-
- ctx = purple_cipher_context_get_data(context);
-
- if(purple_strequal(name, "key_len")) {
- return GINT_TO_POINTER(ctx->key_len);
- }
-
- return NULL;
-}
-
-static gint
-rc4_encrypt(PurpleCipherContext *context, const guchar data[],
- size_t len, guchar output[], size_t *outlen) {
- struct RC4Context *ctx;
- guchar temp_swap;
- guchar x, y, z;
- guchar *state;
- guint i;
-
- ctx = purple_cipher_context_get_data(context);
-
- x = ctx->x;
- y = ctx->y;
- state = &ctx->state[0];
-
- for(i = 0; i < len; i++)
- {
- x = (x + 1) % 256;
- y = (state[x] + y) % 256;
- temp_swap = state[x];
- state[x] = state[y];
- state[y] = temp_swap;
- z = state[x] + (state[y]) % 256;
- output[i] = data[i] ^ state[z];
- }
- ctx->x = x;
- ctx->y = y;
- if(outlen)
- *outlen = len;
-
- return 0;
-}
-
-static PurpleCipherOps RC4Ops = {
- rc4_set_opt, /* Set Option */
- rc4_get_opt, /* Get Option */
- rc4_init, /* init */
- rc4_reset, /* reset */
- rc4_uninit, /* uninit */
- NULL, /* set iv */
- NULL, /* append */
- NULL, /* digest */
- rc4_encrypt, /* encrypt */
- NULL, /* decrypt */
- NULL, /* set salt */
- NULL, /* get salt size */
- rc4_set_key, /* set key */
- rc4_get_key_size, /* get key size */
- NULL, /* set batch mode */
- NULL, /* get batch mode */
- NULL, /* get block size */
- NULL /* set key with len */
-};
-
-PurpleCipherOps *
-purple_rc4_cipher_get_ops(void) {
- return &RC4Ops;
-}
-
diff --git a/libpurple/ciphers/rc4cipher.c b/libpurple/ciphers/rc4cipher.c
new file mode 100644
index 0000000000..7480b8f7f2
--- /dev/null
+++ b/libpurple/ciphers/rc4cipher.c
@@ -0,0 +1,262 @@
+/*
+ * 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 "glibcompat.h"
+
+#include "rc4cipher.h"
+
+/*******************************************************************************
+ * Structs
+ ******************************************************************************/
+#define PURPLE_RC4_CIPHER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_RC4_CIPHER, PurpleRC4CipherPrivate))
+
+typedef struct {
+ guchar state[256];
+ guchar x;
+ guchar y;
+ gint key_len;
+} PurpleRC4CipherPrivate;
+
+/******************************************************************************
+ * Enums
+ *****************************************************************************/
+enum {
+ PROP_ZERO,
+ PROP_KEY_LEN,
+ PROP_KEY,
+ PROP_LAST,
+};
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+static GObjectClass *parent_class = NULL;
+static GParamSpec *properties[PROP_LAST];
+
+/******************************************************************************
+ * Cipher Stuff
+ *****************************************************************************/
+static void
+purple_rc4_cipher_reset(PurpleCipher *cipher) {
+ PurpleRC4Cipher *rc4_cipher = PURPLE_RC4_CIPHER(cipher);
+ PurpleRC4CipherPrivate *priv = PURPLE_RC4_CIPHER_GET_PRIVATE(rc4_cipher);
+ guint i;
+
+ for(i = 0; i < 256; i++)
+ priv->state[i] = i;
+ priv->x = 0;
+ priv->y = 0;
+
+ /* default is 5 bytes (40bit key) */
+ priv->key_len = 5;
+}
+
+static void
+purple_rc4_cipher_set_key(PurpleCipher *cipher, const guchar *key, size_t len) {
+ PurpleRC4Cipher *rc4_cipher = PURPLE_RC4_CIPHER(cipher);
+ PurpleRC4CipherPrivate *priv = PURPLE_RC4_CIPHER_GET_PRIVATE(rc4_cipher);
+ guchar *state;
+ guchar temp_swap;
+ guchar x, y;
+ guint i;
+
+ x = 0;
+ y = 0;
+ state = &priv->state[0];
+ priv->key_len = len;
+ for(i = 0; i < 256; i++)
+ {
+ y = (key[x] + state[i] + y) % 256;
+ temp_swap = state[i];
+ state[i] = state[y];
+ state[y] = temp_swap;
+ x = (x + 1) % len;
+ }
+
+ g_object_notify_by_pspec(G_OBJECT(cipher), properties[PROP_KEY]);
+}
+
+static ssize_t
+purple_rc4_cipher_encrypt(PurpleCipher *cipher, const guchar input[], size_t in_len,
+ guchar output[], size_t out_size)
+{
+ PurpleRC4Cipher *rc4_cipher = PURPLE_RC4_CIPHER(cipher);
+ guchar temp_swap;
+ guchar x, y, z;
+ guchar *state;
+ guint i;
+ PurpleRC4CipherPrivate *priv = PURPLE_RC4_CIPHER_GET_PRIVATE(rc4_cipher);
+
+ x = priv->x;
+ y = priv->y;
+ state = &priv->state[0];
+
+ for(i = 0; i < in_len; i++)
+ {
+ x = (x + 1) % 256;
+ y = (state[x] + y) % 256;
+ temp_swap = state[x];
+ state[x] = state[y];
+ state[y] = temp_swap;
+ z = state[x] + (state[y]) % 256;
+ output[i] = input[i] ^ state[z];
+ }
+ priv->x = x;
+ priv->y = y;
+
+ return in_len;
+}
+
+/******************************************************************************
+ * Object Stuff
+ *****************************************************************************/
+static void
+purple_rc4_cipher_set_property(GObject *obj, guint param_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ PurpleCipher *cipher = PURPLE_CIPHER(obj);
+ PurpleRC4Cipher *rc4_cipher = PURPLE_RC4_CIPHER(obj);
+
+ switch(param_id) {
+ case PROP_KEY_LEN:
+ purple_rc4_cipher_set_key_len(rc4_cipher, g_value_get_int(value));
+ break;
+ case PROP_KEY:
+ {
+ guchar *key = (guchar *)g_value_get_string(value);
+ purple_rc4_cipher_set_key(cipher, key, strlen((gchar *) key));
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_rc4_cipher_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleRC4Cipher *rc4_cipher = PURPLE_RC4_CIPHER(obj);
+
+ switch(param_id) {
+ case PROP_KEY_LEN:
+ g_value_set_int(value,
+ purple_rc4_cipher_get_key_len(rc4_cipher));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_rc4_cipher_class_init(PurpleRC4CipherClass *klass) {
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+ PurpleCipherClass *cipher_class = PURPLE_CIPHER_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ obj_class->set_property = purple_rc4_cipher_set_property;
+ obj_class->get_property = purple_rc4_cipher_get_property;
+
+ cipher_class->reset = purple_rc4_cipher_reset;
+ cipher_class->encrypt = purple_rc4_cipher_encrypt;
+ cipher_class->set_key = purple_rc4_cipher_set_key;
+
+ g_type_class_add_private(klass, sizeof(PurpleRC4CipherPrivate));
+
+ properties[PROP_KEY_LEN] = g_param_spec_int("key-len", "key-len", "key-len",
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_KEY] = g_param_spec_string("key", "key", "key", NULL,
+ G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, PROP_LAST, properties);
+}
+
+static void
+purple_rc4_cipher_init(PurpleCipher *cipher) {
+ purple_rc4_cipher_reset(cipher);
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+GType
+purple_rc4_cipher_get_type(void) {
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleRC4CipherClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_rc4_cipher_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleRC4Cipher),
+ 0,
+ (GInstanceInitFunc)purple_rc4_cipher_init,
+ NULL,
+ };
+
+ type = g_type_register_static(PURPLE_TYPE_CIPHER,
+ "PurpleRC4Cipher",
+ &info, 0);
+ }
+
+ return type;
+}
+
+PurpleCipher *
+purple_rc4_cipher_new(void) {
+ return g_object_new(PURPLE_TYPE_RC4_CIPHER, NULL);
+}
+
+void
+purple_rc4_cipher_set_key_len(PurpleRC4Cipher *rc4_cipher,
+ gint key_len)
+{
+ PurpleRC4CipherPrivate *priv;
+
+ g_return_if_fail(PURPLE_IS_RC4_CIPHER(rc4_cipher));
+
+ priv = PURPLE_RC4_CIPHER_GET_PRIVATE(rc4_cipher);
+ priv->key_len = key_len;
+
+ g_object_notify_by_pspec(G_OBJECT(rc4_cipher), properties[PROP_KEY_LEN]);
+}
+
+gint
+purple_rc4_cipher_get_key_len(PurpleRC4Cipher *rc4_cipher)
+{
+ PurpleRC4CipherPrivate *priv;
+
+ g_return_val_if_fail(PURPLE_IS_RC4_CIPHER(rc4_cipher), 0);
+
+ priv = PURPLE_RC4_CIPHER_GET_PRIVATE(rc4_cipher);
+
+ return priv->key_len;
+}
diff --git a/libpurple/ciphers/rc4cipher.h b/libpurple/ciphers/rc4cipher.h
new file mode 100644
index 0000000000..94423fb9d5
--- /dev/null
+++ b/libpurple/ciphers/rc4cipher.h
@@ -0,0 +1,70 @@
+/* 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 PURPLE_RC4_CIPHER_H
+#define PURPLE_RC4_CIPHER_H
+/**
+ * SECTION:rc4cipher
+ * @section_id: libpurple-rc4cipher
+ * @short_description: <filename>ciphers/rc4cipher.h</filename>
+ * @title: RC4 Cipher
+ */
+
+#include "cipher.h"
+
+#define PURPLE_TYPE_RC4_CIPHER (purple_rc4_cipher_get_type())
+#define PURPLE_RC4_CIPHER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_RC4_CIPHER, PurpleRC4Cipher))
+#define PURPLE_RC4_CIPHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_RC4_CIPHER, PurpleRC4CipherClass))
+#define PURPLE_IS_RC4_CIPHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_RC4_CIPHER))
+#define PURPLE_IS_RC4_CIPHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), PURPLE_TYPE_RC4_CIPHER))
+#define PURPLE_RC4_CIPHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_RC4_CIPHER, PurpleRC4CipherClass))
+
+typedef struct _PurpleRC4Cipher PurpleRC4Cipher;
+typedef struct _PurpleRC4CipherClass PurpleRC4CipherClass;
+
+struct _PurpleRC4Cipher {
+ PurpleCipher gparent;
+};
+
+struct _PurpleRC4CipherClass {
+ PurpleCipherClass gparent;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+GType purple_rc4_cipher_get_type(void);
+
+PurpleCipher *purple_rc4_cipher_new(void);
+
+gint purple_rc4_cipher_get_key_len(PurpleRC4Cipher *rc4_cipher);
+void purple_rc4_cipher_set_key_len(PurpleRC4Cipher *rc4_cipher, gint key_len);
+
+G_END_DECLS
+
+#endif /* PURPLE_RC4_CIPHER_H */
+
+
diff --git a/libpurple/ciphers/sha1.c b/libpurple/ciphers/sha1.c
deleted file mode 100644
index a987658ee0..0000000000
--- a/libpurple/ciphers/sha1.c
+++ /dev/null
@@ -1,281 +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
- */
-#include <cipher.h>
-#include <util.h>
-
-#if !GLIB_CHECK_VERSION(2,16,0)
-
-#define SHA1_HMAC_BLOCK_SIZE 64
-#define SHA1_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xFFFFFFFF)
-
-struct SHA1Context {
- guint32 H[5];
- guint32 W[80];
-
- gint lenW;
-
- guint32 sizeHi;
- guint32 sizeLo;
-};
-
-static size_t
-sha1_get_block_size(PurpleCipherContext *context)
-{
- /* This does not change (in this case) */
- return SHA1_HMAC_BLOCK_SIZE;
-}
-
-static void
-sha1_hash_block(struct SHA1Context *sha1_ctx) {
- gint i;
- guint32 A, B, C, D, E, T;
-
- for(i = 16; i < 80; i++) {
- sha1_ctx->W[i] = SHA1_ROTL(sha1_ctx->W[i - 3] ^
- sha1_ctx->W[i - 8] ^
- sha1_ctx->W[i - 14] ^
- sha1_ctx->W[i - 16], 1);
- }
-
- A = sha1_ctx->H[0];
- B = sha1_ctx->H[1];
- C = sha1_ctx->H[2];
- D = sha1_ctx->H[3];
- E = sha1_ctx->H[4];
-
- for(i = 0; i < 20; i++) {
- T = (SHA1_ROTL(A, 5) + (((C ^ D) & B) ^ D) + E + sha1_ctx->W[i] + 0x5A827999) & 0xFFFFFFFF;
- E = D;
- D = C;
- C = SHA1_ROTL(B, 30);
- B = A;
- A = T;
- }
-
- for(i = 20; i < 40; i++) {
- T = (SHA1_ROTL(A, 5) + (B ^ C ^ D) + E + sha1_ctx->W[i] + 0x6ED9EBA1) & 0xFFFFFFFF;
- E = D;
- D = C;
- C = SHA1_ROTL(B, 30);
- B = A;
- A = T;
- }
-
- for(i = 40; i < 60; i++) {
- T = (SHA1_ROTL(A, 5) + ((B & C) | (D & (B | C))) + E + sha1_ctx->W[i] + 0x8F1BBCDC) & 0xFFFFFFFF;
- E = D;
- D = C;
- C = SHA1_ROTL(B, 30);
- B = A;
- A = T;
- }
-
- for(i = 60; i < 80; i++) {
- T = (SHA1_ROTL(A, 5) + (B ^ C ^ D) + E + sha1_ctx->W[i] + 0xCA62C1D6) & 0xFFFFFFFF;
- E = D;
- D = C;
- C = SHA1_ROTL(B, 30);
- B = A;
- A = T;
- }
-
- sha1_ctx->H[0] += A;
- sha1_ctx->H[1] += B;
- sha1_ctx->H[2] += C;
- sha1_ctx->H[3] += D;
- sha1_ctx->H[4] += E;
-}
-
-static void
-sha1_set_opt(PurpleCipherContext *context, const gchar *name, void *value) {
- struct SHA1Context *ctx;
-
- ctx = purple_cipher_context_get_data(context);
-
- if(purple_strequal(name, "sizeHi")) {
- ctx->sizeHi = GPOINTER_TO_INT(value);
- } else if(purple_strequal(name, "sizeLo")) {
- ctx->sizeLo = GPOINTER_TO_INT(value);
- } else if(purple_strequal(name, "lenW")) {
- ctx->lenW = GPOINTER_TO_INT(value);
- }
-}
-
-static void *
-sha1_get_opt(PurpleCipherContext *context, const gchar *name) {
- struct SHA1Context *ctx;
-
- ctx = purple_cipher_context_get_data(context);
-
- if(purple_strequal(name, "sizeHi")) {
- return GINT_TO_POINTER(ctx->sizeHi);
- } else if(purple_strequal(name, "sizeLo")) {
- return GINT_TO_POINTER(ctx->sizeLo);
- } else if(purple_strequal(name, "lenW")) {
- return GINT_TO_POINTER(ctx->lenW);
- }
-
- return NULL;
-}
-
-static void
-sha1_init(PurpleCipherContext *context, void *extra) {
- struct SHA1Context *sha1_ctx;
-
- sha1_ctx = g_new0(struct SHA1Context, 1);
-
- purple_cipher_context_set_data(context, sha1_ctx);
-
- purple_cipher_context_reset(context, extra);
-}
-
-static void
-sha1_reset(PurpleCipherContext *context, void *extra) {
- struct SHA1Context *sha1_ctx;
- gint i;
-
- sha1_ctx = purple_cipher_context_get_data(context);
-
- g_return_if_fail(sha1_ctx);
-
- sha1_ctx->lenW = 0;
- sha1_ctx->sizeHi = 0;
- sha1_ctx->sizeLo = 0;
-
- sha1_ctx->H[0] = 0x67452301;
- sha1_ctx->H[1] = 0xEFCDAB89;
- sha1_ctx->H[2] = 0x98BADCFE;
- sha1_ctx->H[3] = 0x10325476;
- sha1_ctx->H[4] = 0xC3D2E1F0;
-
- for(i = 0; i < 80; i++)
- sha1_ctx->W[i] = 0;
-}
-
-static void
-sha1_uninit(PurpleCipherContext *context) {
- struct SHA1Context *sha1_ctx;
-
- purple_cipher_context_reset(context, NULL);
-
- sha1_ctx = purple_cipher_context_get_data(context);
-
- memset(sha1_ctx, 0, sizeof(struct SHA1Context));
-
- g_free(sha1_ctx);
- sha1_ctx = NULL;
-}
-
-static void
-sha1_append(PurpleCipherContext *context, const guchar *data, size_t len) {
- struct SHA1Context *sha1_ctx;
- gint i;
-
- sha1_ctx = purple_cipher_context_get_data(context);
-
- g_return_if_fail(sha1_ctx);
-
- for(i = 0; i < len; i++) {
- sha1_ctx->W[sha1_ctx->lenW / 4] <<= 8;
- sha1_ctx->W[sha1_ctx->lenW / 4] |= data[i];
-
- if((++sha1_ctx->lenW) % 64 == 0) {
- sha1_hash_block(sha1_ctx);
- sha1_ctx->lenW = 0;
- }
-
- sha1_ctx->sizeLo += 8;
- sha1_ctx->sizeHi += (sha1_ctx->sizeLo < 8);
- }
-}
-
-static gboolean
-sha1_digest(PurpleCipherContext *context, size_t in_len, guchar digest[20],
- size_t *out_len)
-{
- struct SHA1Context *sha1_ctx;
- guchar pad0x80 = 0x80, pad0x00 = 0x00;
- guchar padlen[8];
- gint i;
-
- g_return_val_if_fail(in_len >= 20, FALSE);
-
- sha1_ctx = purple_cipher_context_get_data(context);
-
- g_return_val_if_fail(sha1_ctx, FALSE);
-
- padlen[0] = (guchar)((sha1_ctx->sizeHi >> 24) & 255);
- padlen[1] = (guchar)((sha1_ctx->sizeHi >> 16) & 255);
- padlen[2] = (guchar)((sha1_ctx->sizeHi >> 8) & 255);
- padlen[3] = (guchar)((sha1_ctx->sizeHi >> 0) & 255);
- padlen[4] = (guchar)((sha1_ctx->sizeLo >> 24) & 255);
- padlen[5] = (guchar)((sha1_ctx->sizeLo >> 16) & 255);
- padlen[6] = (guchar)((sha1_ctx->sizeLo >> 8) & 255);
- padlen[7] = (guchar)((sha1_ctx->sizeLo >> 0) & 255);
-
- /* pad with a 1, then zeroes, then length */
- purple_cipher_context_append(context, &pad0x80, 1);
- while(sha1_ctx->lenW != 56)
- purple_cipher_context_append(context, &pad0x00, 1);
- purple_cipher_context_append(context, padlen, 8);
-
- for(i = 0; i < 20; i++) {
- digest[i] = (guchar)(sha1_ctx->H[i / 4] >> 24);
- sha1_ctx->H[i / 4] <<= 8;
- }
-
- purple_cipher_context_reset(context, NULL);
-
- if(out_len)
- *out_len = 20;
-
- return TRUE;
-}
-
-static PurpleCipherOps SHA1Ops = {
- sha1_set_opt, /* Set Option */
- sha1_get_opt, /* Get Option */
- sha1_init, /* init */
- sha1_reset, /* reset */
- sha1_uninit, /* uninit */
- NULL, /* set iv */
- sha1_append, /* append */
- sha1_digest, /* digest */
- NULL, /* encrypt */
- NULL, /* decrypt */
- NULL, /* set salt */
- NULL, /* get salt size */
- NULL, /* set key */
- NULL, /* get key size */
- NULL, /* set batch mode */
- NULL, /* get batch mode */
- sha1_get_block_size, /* get block size */
- NULL /* set key with len */
-};
-
-PurpleCipherOps *
-purple_sha1_cipher_get_ops(void) {
- return &SHA1Ops;
-}
-
-#endif /* !GLIB_CHECK_VERSION(2,16,0) */
-
diff --git a/libpurple/ciphers/sha1hash.c b/libpurple/ciphers/sha1hash.c
new file mode 100644
index 0000000000..b286bd41c9
--- /dev/null
+++ b/libpurple/ciphers/sha1hash.c
@@ -0,0 +1,191 @@
+/*
+ * 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 "sha1hash.h"
+
+/*******************************************************************************
+ * Structs
+ ******************************************************************************/
+#define PURPLE_SHA1_HASH_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_SHA1_HASH, PurpleSHA1HashPrivate))
+
+typedef struct {
+ GChecksum *checksum;
+} PurpleSHA1HashPrivate;
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+static GObjectClass *parent_class = NULL;
+
+/******************************************************************************
+ * Hash Stuff
+ *****************************************************************************/
+
+static void
+purple_sha1_hash_reset(PurpleHash *hash)
+{
+ PurpleSHA1Hash *sha1_hash = PURPLE_SHA1_HASH(hash);
+ PurpleSHA1HashPrivate *priv = PURPLE_SHA1_HASH_GET_PRIVATE(sha1_hash);
+
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(priv->checksum != NULL);
+
+ g_checksum_reset(priv->checksum);
+}
+
+static void
+purple_sha1_hash_append(PurpleHash *hash, const guchar *data,
+ gsize len)
+{
+ PurpleSHA1Hash *sha1_hash = PURPLE_SHA1_HASH(hash);
+ PurpleSHA1HashPrivate *priv = PURPLE_SHA1_HASH_GET_PRIVATE(sha1_hash);
+
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(priv->checksum != NULL);
+
+ while (len >= G_MAXSSIZE) {
+ g_checksum_update(priv->checksum, data, G_MAXSSIZE);
+ len -= G_MAXSSIZE;
+ data += G_MAXSSIZE;
+ }
+
+ if (len)
+ g_checksum_update(priv->checksum, data, len);
+}
+
+static gboolean
+purple_sha1_hash_digest(PurpleHash *hash, guchar *digest, size_t buff_len)
+{
+ PurpleSHA1Hash *sha1_hash = PURPLE_SHA1_HASH(hash);
+ PurpleSHA1HashPrivate *priv = PURPLE_SHA1_HASH_GET_PRIVATE(sha1_hash);
+
+ const gssize required_len = g_checksum_type_get_length(G_CHECKSUM_SHA1);
+ gsize digest_len = buff_len;
+
+ g_return_val_if_fail(priv != NULL, FALSE);
+ g_return_val_if_fail(priv->checksum != NULL, FALSE);
+
+ g_return_val_if_fail(required_len >= 0, FALSE);
+ g_return_val_if_fail(buff_len >= (gsize)required_len, FALSE);
+
+ g_checksum_get_digest(priv->checksum, digest, &digest_len);
+
+ if (digest_len != (gsize)required_len)
+ return FALSE;
+
+ purple_sha1_hash_reset(hash);
+
+ return TRUE;
+}
+
+static size_t
+purple_sha1_hash_get_block_size(PurpleHash *hash)
+{
+ return 64;
+}
+
+static size_t
+purple_sha1_hash_get_digest_size(PurpleHash *hash)
+{
+ return g_checksum_type_get_length(G_CHECKSUM_SHA1);
+}
+
+/******************************************************************************
+ * Object Stuff
+ *****************************************************************************/
+
+static void
+purple_sha1_hash_finalize(GObject *obj)
+{
+ PurpleSHA1Hash *sha1_hash = PURPLE_SHA1_HASH(obj);
+ PurpleSHA1HashPrivate *priv = PURPLE_SHA1_HASH_GET_PRIVATE(sha1_hash);
+
+ if (priv->checksum)
+ g_checksum_free(priv->checksum);
+
+ parent_class->finalize(obj);
+}
+
+static void
+purple_sha1_hash_class_init(PurpleSHA1HashClass *klass) {
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+ PurpleHashClass *hash_class = PURPLE_HASH_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ obj_class->finalize = purple_sha1_hash_finalize;
+
+ hash_class->reset = purple_sha1_hash_reset;
+ hash_class->reset_state = purple_sha1_hash_reset;
+ hash_class->append = purple_sha1_hash_append;
+ hash_class->digest = purple_sha1_hash_digest;
+ hash_class->get_digest_size = purple_sha1_hash_get_digest_size;
+ hash_class->get_block_size = purple_sha1_hash_get_block_size;
+
+ g_type_class_add_private(klass, sizeof(PurpleSHA1HashPrivate));
+}
+
+static void
+purple_sha1_hash_init(PurpleHash *hash)
+{
+ PurpleSHA1Hash *sha1_hash = PURPLE_SHA1_HASH(hash);
+ PurpleSHA1HashPrivate *priv = PURPLE_SHA1_HASH_GET_PRIVATE(sha1_hash);
+
+ priv->checksum = g_checksum_new(G_CHECKSUM_SHA1);
+
+ purple_sha1_hash_reset(hash);
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+GType
+purple_sha1_hash_get_type(void) {
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleSHA1HashClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_sha1_hash_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleSHA1Hash),
+ 0,
+ (GInstanceInitFunc)purple_sha1_hash_init,
+ NULL,
+ };
+
+ type = g_type_register_static(PURPLE_TYPE_HASH,
+ "PurpleSHA1Hash",
+ &info, 0);
+ }
+
+ return type;
+}
+
+PurpleHash *
+purple_sha1_hash_new(void) {
+ return g_object_new(PURPLE_TYPE_SHA1_HASH, NULL);
+}
diff --git a/libpurple/ciphers/sha1hash.h b/libpurple/ciphers/sha1hash.h
new file mode 100644
index 0000000000..976847e93f
--- /dev/null
+++ b/libpurple/ciphers/sha1hash.h
@@ -0,0 +1,65 @@
+/* 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 PURPLE_SHA1_HASH_H
+#define PURPLE_SHA1_HASH_H
+/**
+ * SECTION:sha1hash
+ * @section_id: libpurple-sha1hash
+ * @short_description: <filename>ciphers/sha1hash.h</filename>
+ * @title: SHA1 Hash
+ */
+
+#include "cipher.h"
+
+#define PURPLE_TYPE_SHA1_HASH (purple_sha1_hash_get_type())
+#define PURPLE_SHA1_HASH(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_SHA1_HASH, PurpleSHA1Hash))
+#define PURPLE_SHA1_HASH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_SHA1_HASH, PurpleSHA1HashClass))
+#define PURPLE_IS_SHA1_HASH(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_SHA1_HASH))
+#define PURPLE_IS_SHA1_HASH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), PURPLE_TYPE_SHA1_HASH))
+#define PURPLE_SHA1_HASH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_SHA1_HASH, PurpleSHA1HashClass))
+
+typedef struct _PurpleSHA1Hash PurpleSHA1Hash;
+typedef struct _PurpleSHA1HashClass PurpleSHA1HashClass;
+
+struct _PurpleSHA1Hash {
+ PurpleHash gparent;
+};
+
+struct _PurpleSHA1HashClass {
+ PurpleHashClass gparent;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+GType purple_sha1_hash_get_type(void);
+
+PurpleHash *purple_sha1_hash_new(void);
+
+G_END_DECLS
+
+#endif /* PURPLE_SHA1_HASH_H */
diff --git a/libpurple/ciphers/sha256.c b/libpurple/ciphers/sha256.c
deleted file mode 100644
index 213a9cd3cf..0000000000
--- a/libpurple/ciphers/sha256.c
+++ /dev/null
@@ -1,283 +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
- */
-#include <cipher.h>
-
-#if !GLIB_CHECK_VERSION(2,16,0)
-
-#define SHA256_HMAC_BLOCK_SIZE 64
-#define SHA256_ROTR(X,n) ((((X) >> (n)) | ((X) << (32-(n)))) & 0xFFFFFFFF)
-
-static const guint32 sha256_K[64] =
-{
- 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
- 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
- 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
- 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
- 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
- 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
- 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
- 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
-};
-
-struct SHA256Context {
- guint32 H[8];
- guint32 W[64];
-
- gint lenW;
-
- guint32 sizeHi;
- guint32 sizeLo;
-};
-
-static size_t
-sha256_get_block_size(PurpleCipherContext *context)
-{
- /* This does not change (in this case) */
- return SHA256_HMAC_BLOCK_SIZE;
-}
-
-static void
-sha256_hash_block(struct SHA256Context *sha256_ctx) {
- gint i;
- guint32 A, B, C, D, E, F, G, H, T1, T2;
-
- for(i = 16; i < 64; i++) {
- sha256_ctx->W[i] =
- (SHA256_ROTR(sha256_ctx->W[i-2], 17) ^ SHA256_ROTR(sha256_ctx->W[i-2], 19) ^ (sha256_ctx->W[i-2] >> 10))
- + sha256_ctx->W[i-7]
- + (SHA256_ROTR(sha256_ctx->W[i-15], 7) ^ SHA256_ROTR(sha256_ctx->W[i-15], 18) ^ (sha256_ctx->W[i-15] >> 3))
- + sha256_ctx->W[i-16];
- }
-
- A = sha256_ctx->H[0];
- B = sha256_ctx->H[1];
- C = sha256_ctx->H[2];
- D = sha256_ctx->H[3];
- E = sha256_ctx->H[4];
- F = sha256_ctx->H[5];
- G = sha256_ctx->H[6];
- H = sha256_ctx->H[7];
-
- for(i = 0; i < 64; i++) {
- T1 = H
- + (SHA256_ROTR(E, 6) ^ SHA256_ROTR(E, 11) ^ SHA256_ROTR(E, 25))
- + ((E & F) ^ ((~E) & G))
- + sha256_K[i] + sha256_ctx->W[i];
- T2 = (SHA256_ROTR(A, 2) ^ SHA256_ROTR(A, 13) ^ SHA256_ROTR(A, 22))
- + ((A & B) ^ (A & C) ^ (B & C));
- H = G;
- G = F;
- F = E;
- E = D + T1;
- D = C;
- C = B;
- B = A;
- A = T1 + T2;
- }
-
- sha256_ctx->H[0] += A;
- sha256_ctx->H[1] += B;
- sha256_ctx->H[2] += C;
- sha256_ctx->H[3] += D;
- sha256_ctx->H[4] += E;
- sha256_ctx->H[5] += F;
- sha256_ctx->H[6] += G;
- sha256_ctx->H[7] += H;
-}
-
-static void
-sha256_set_opt(PurpleCipherContext *context, const gchar *name, void *value) {
- struct SHA256Context *ctx;
-
- ctx = purple_cipher_context_get_data(context);
-
- if(!strcmp(name, "sizeHi")) {
- ctx->sizeHi = GPOINTER_TO_INT(value);
- } else if(!strcmp(name, "sizeLo")) {
- ctx->sizeLo = GPOINTER_TO_INT(value);
- } else if(!strcmp(name, "lenW")) {
- ctx->lenW = GPOINTER_TO_INT(value);
- }
-}
-
-static void *
-sha256_get_opt(PurpleCipherContext *context, const gchar *name) {
- struct SHA256Context *ctx;
-
- ctx = purple_cipher_context_get_data(context);
-
- if(!strcmp(name, "sizeHi")) {
- return GINT_TO_POINTER(ctx->sizeHi);
- } else if(!strcmp(name, "sizeLo")) {
- return GINT_TO_POINTER(ctx->sizeLo);
- } else if(!strcmp(name, "lenW")) {
- return GINT_TO_POINTER(ctx->lenW);
- }
-
- return NULL;
-}
-
-static void
-sha256_init(PurpleCipherContext *context, void *extra) {
- struct SHA256Context *sha256_ctx;
-
- sha256_ctx = g_new0(struct SHA256Context, 1);
-
- purple_cipher_context_set_data(context, sha256_ctx);
-
- purple_cipher_context_reset(context, extra);
-}
-
-static void
-sha256_reset(PurpleCipherContext *context, void *extra) {
- struct SHA256Context *sha256_ctx;
- gint i;
-
- sha256_ctx = purple_cipher_context_get_data(context);
-
- g_return_if_fail(sha256_ctx);
-
- sha256_ctx->lenW = 0;
- sha256_ctx->sizeHi = 0;
- sha256_ctx->sizeLo = 0;
-
- sha256_ctx->H[0] = 0x6a09e667;
- sha256_ctx->H[1] = 0xbb67ae85;
- sha256_ctx->H[2] = 0x3c6ef372;
- sha256_ctx->H[3] = 0xa54ff53a;
- sha256_ctx->H[4] = 0x510e527f;
- sha256_ctx->H[5] = 0x9b05688c;
- sha256_ctx->H[6] = 0x1f83d9ab;
- sha256_ctx->H[7] = 0x5be0cd19;
-
- for(i = 0; i < 64; i++)
- sha256_ctx->W[i] = 0;
-}
-
-static void
-sha256_uninit(PurpleCipherContext *context) {
- struct SHA256Context *sha256_ctx;
-
- purple_cipher_context_reset(context, NULL);
-
- sha256_ctx = purple_cipher_context_get_data(context);
-
- memset(sha256_ctx, 0, sizeof(struct SHA256Context));
-
- g_free(sha256_ctx);
- sha256_ctx = NULL;
-}
-
-static void
-sha256_append(PurpleCipherContext *context, const guchar *data, size_t len) {
- struct SHA256Context *sha256_ctx;
- gint i;
-
- sha256_ctx = purple_cipher_context_get_data(context);
-
- g_return_if_fail(sha256_ctx);
-
- for(i = 0; i < len; i++) {
- sha256_ctx->W[sha256_ctx->lenW / 4] <<= 8;
- sha256_ctx->W[sha256_ctx->lenW / 4] |= data[i];
-
- if((++sha256_ctx->lenW) % 64 == 0) {
- sha256_hash_block(sha256_ctx);
- sha256_ctx->lenW = 0;
- }
-
- sha256_ctx->sizeLo += 8;
- sha256_ctx->sizeHi += (sha256_ctx->sizeLo < 8);
- }
-}
-
-static gboolean
-sha256_digest(PurpleCipherContext *context, size_t in_len, guchar digest[32],
- size_t *out_len)
-{
- struct SHA256Context *sha256_ctx;
- guchar pad0x80 = 0x80, pad0x00 = 0x00;
- guchar padlen[8];
- gint i;
-
- g_return_val_if_fail(in_len >= 32, FALSE);
-
- sha256_ctx = purple_cipher_context_get_data(context);
-
- g_return_val_if_fail(sha256_ctx, FALSE);
-
- padlen[0] = (guchar)((sha256_ctx->sizeHi >> 24) & 255);
- padlen[1] = (guchar)((sha256_ctx->sizeHi >> 16) & 255);
- padlen[2] = (guchar)((sha256_ctx->sizeHi >> 8) & 255);
- padlen[3] = (guchar)((sha256_ctx->sizeHi >> 0) & 255);
- padlen[4] = (guchar)((sha256_ctx->sizeLo >> 24) & 255);
- padlen[5] = (guchar)((sha256_ctx->sizeLo >> 16) & 255);
- padlen[6] = (guchar)((sha256_ctx->sizeLo >> 8) & 255);
- padlen[7] = (guchar)((sha256_ctx->sizeLo >> 0) & 255);
-
- /* pad with a 1, then zeroes, then length */
- purple_cipher_context_append(context, &pad0x80, 1);
- while(sha256_ctx->lenW != 56)
- purple_cipher_context_append(context, &pad0x00, 1);
- purple_cipher_context_append(context, padlen, 8);
-
- for(i = 0; i < 32; i++) {
- digest[i] = (guchar)(sha256_ctx->H[i / 4] >> 24);
- sha256_ctx->H[i / 4] <<= 8;
- }
-
- purple_cipher_context_reset(context, NULL);
-
- if(out_len)
- *out_len = 32;
-
- return TRUE;
-}
-
-static PurpleCipherOps SHA256Ops = {
- sha256_set_opt, /* Set Option */
- sha256_get_opt, /* Get Option */
- sha256_init, /* init */
- sha256_reset, /* reset */
- sha256_uninit, /* uninit */
- NULL, /* set iv */
- sha256_append, /* append */
- sha256_digest, /* digest */
- NULL, /* encrypt */
- NULL, /* decrypt */
- NULL, /* set salt */
- NULL, /* get salt size */
- NULL, /* set key */
- NULL, /* get key size */
- NULL, /* set batch mode */
- NULL, /* get batch mode */
- sha256_get_block_size, /* get block size */
- NULL /* set key with len */
-};
-
-PurpleCipherOps *
-purple_sha256_cipher_get_ops(void) {
- return &SHA256Ops;
-}
-
-#endif /* !GLIB_CHECK_VERSION(2,16,0) */
-
diff --git a/libpurple/ciphers/sha256hash.c b/libpurple/ciphers/sha256hash.c
new file mode 100644
index 0000000000..5732e83c56
--- /dev/null
+++ b/libpurple/ciphers/sha256hash.c
@@ -0,0 +1,191 @@
+/*
+ * 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 "sha256hash.h"
+
+/*******************************************************************************
+ * Structs
+ ******************************************************************************/
+#define PURPLE_SHA256_HASH_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_SHA256_HASH, PurpleSHA256HashPrivate))
+
+typedef struct {
+ GChecksum *checksum;
+} PurpleSHA256HashPrivate;
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+static GObjectClass *parent_class = NULL;
+
+/******************************************************************************
+ * Hash Stuff
+ *****************************************************************************/
+
+static void
+purple_sha256_hash_reset(PurpleHash *hash)
+{
+ PurpleSHA256Hash *sha256_hash = PURPLE_SHA256_HASH(hash);
+ PurpleSHA256HashPrivate *priv = PURPLE_SHA256_HASH_GET_PRIVATE(sha256_hash);
+
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(priv->checksum != NULL);
+
+ g_checksum_reset(priv->checksum);
+}
+
+static void
+purple_sha256_hash_append(PurpleHash *hash, const guchar *data,
+ gsize len)
+{
+ PurpleSHA256Hash *sha256_hash = PURPLE_SHA256_HASH(hash);
+ PurpleSHA256HashPrivate *priv = PURPLE_SHA256_HASH_GET_PRIVATE(sha256_hash);
+
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(priv->checksum != NULL);
+
+ while (len >= G_MAXSSIZE) {
+ g_checksum_update(priv->checksum, data, G_MAXSSIZE);
+ len -= G_MAXSSIZE;
+ data += G_MAXSSIZE;
+ }
+
+ if (len)
+ g_checksum_update(priv->checksum, data, len);
+}
+
+static gboolean
+purple_sha256_hash_digest(PurpleHash *hash, guchar *digest, size_t buff_len)
+{
+ PurpleSHA256Hash *sha256_hash = PURPLE_SHA256_HASH(hash);
+ PurpleSHA256HashPrivate *priv = PURPLE_SHA256_HASH_GET_PRIVATE(sha256_hash);
+
+ const gssize required_len = g_checksum_type_get_length(G_CHECKSUM_SHA256);
+ gsize digest_len = buff_len;
+
+ g_return_val_if_fail(priv != NULL, FALSE);
+ g_return_val_if_fail(priv->checksum != NULL, FALSE);
+
+ g_return_val_if_fail(required_len >= 0, FALSE);
+ g_return_val_if_fail(buff_len >= (gsize)required_len, FALSE);
+
+ g_checksum_get_digest(priv->checksum, digest, &digest_len);
+
+ if (digest_len != (gsize)required_len)
+ return FALSE;
+
+ purple_sha256_hash_reset(hash);
+
+ return TRUE;
+}
+
+static size_t
+purple_sha256_hash_get_block_size(PurpleHash *hash)
+{
+ return 64;
+}
+
+static size_t
+purple_sha256_hash_get_digest_size(PurpleHash *hash)
+{
+ return g_checksum_type_get_length(G_CHECKSUM_SHA256);
+}
+
+/******************************************************************************
+ * Object Stuff
+ *****************************************************************************/
+
+static void
+purple_sha256_hash_finalize(GObject *obj)
+{
+ PurpleSHA256Hash *sha256_hash = PURPLE_SHA256_HASH(obj);
+ PurpleSHA256HashPrivate *priv = PURPLE_SHA256_HASH_GET_PRIVATE(sha256_hash);
+
+ if (priv->checksum)
+ g_checksum_free(priv->checksum);
+
+ parent_class->finalize(obj);
+}
+
+static void
+purple_sha256_hash_class_init(PurpleSHA256HashClass *klass) {
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+ PurpleHashClass *hash_class = PURPLE_HASH_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ obj_class->finalize = purple_sha256_hash_finalize;
+
+ hash_class->reset = purple_sha256_hash_reset;
+ hash_class->reset_state = purple_sha256_hash_reset;
+ hash_class->append = purple_sha256_hash_append;
+ hash_class->digest = purple_sha256_hash_digest;
+ hash_class->get_digest_size = purple_sha256_hash_get_digest_size;
+ hash_class->get_block_size = purple_sha256_hash_get_block_size;
+
+ g_type_class_add_private(klass, sizeof(PurpleSHA256HashPrivate));
+}
+
+static void
+purple_sha256_hash_init(PurpleHash *hash)
+{
+ PurpleSHA256Hash *sha256_hash = PURPLE_SHA256_HASH(hash);
+ PurpleSHA256HashPrivate *priv = PURPLE_SHA256_HASH_GET_PRIVATE(sha256_hash);
+
+ priv->checksum = g_checksum_new(G_CHECKSUM_SHA256);
+
+ purple_sha256_hash_reset(hash);
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+GType
+purple_sha256_hash_get_type(void) {
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleSHA256HashClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_sha256_hash_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleSHA256Hash),
+ 0,
+ (GInstanceInitFunc)purple_sha256_hash_init,
+ NULL,
+ };
+
+ type = g_type_register_static(PURPLE_TYPE_HASH,
+ "PurpleSHA256Hash",
+ &info, 0);
+ }
+
+ return type;
+}
+
+PurpleHash *
+purple_sha256_hash_new(void) {
+ return g_object_new(PURPLE_TYPE_SHA256_HASH, NULL);
+}
diff --git a/libpurple/ciphers/sha256hash.h b/libpurple/ciphers/sha256hash.h
new file mode 100644
index 0000000000..f011ff611c
--- /dev/null
+++ b/libpurple/ciphers/sha256hash.h
@@ -0,0 +1,65 @@
+/* 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 PURPLE_SHA256_HASH_H
+#define PURPLE_SHA256_HASH_H
+/**
+ * SECTION:sha256hash
+ * @section_id: libpurple-sha256hash
+ * @short_description: <filename>ciphers/sha256hash.h</filename>
+ * @title: SHA256 Hash
+ */
+
+#include "cipher.h"
+
+#define PURPLE_TYPE_SHA256_HASH (purple_sha256_hash_get_type())
+#define PURPLE_SHA256_HASH(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_SHA256_HASH, PurpleSHA256Hash))
+#define PURPLE_SHA256_HASH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_SHA256_HASH, PurpleSHA256HashClass))
+#define PURPLE_IS_SHA256_HASH(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_SHA256_HASH))
+#define PURPLE_IS_SHA256_HASH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), PURPLE_TYPE_SHA256_HASH))
+#define PURPLE_SHA256_HASH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_SHA256_HASH, PurpleSHA256HashClass))
+
+typedef struct _PurpleSHA256Hash PurpleSHA256Hash;
+typedef struct _PurpleSHA256HashClass PurpleSHA256HashClass;
+
+struct _PurpleSHA256Hash {
+ PurpleHash gparent;
+};
+
+struct _PurpleSHA256HashClass {
+ PurpleHashClass gparent;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+GType purple_sha256_hash_get_type(void);
+
+PurpleHash *purple_sha256_hash_new(void);
+
+G_END_DECLS
+
+#endif /* PURPLE_SHA256_HASH_H */
diff --git a/libpurple/circbuffer.c b/libpurple/circbuffer.c
deleted file mode 100644
index 619d9c9758..0000000000
--- a/libpurple/circbuffer.c
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * @file circbuffer.h Buffer Utility Functions
- * @ingroup core
- */
-
-/* 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 "circbuffer.h"
-
-#define DEFAULT_BUF_SIZE 256
-
-PurpleCircBuffer *
-purple_circ_buffer_new(gsize growsize) {
- PurpleCircBuffer *buf = g_new0(PurpleCircBuffer, 1);
- buf->growsize = growsize ? growsize : DEFAULT_BUF_SIZE;
- return buf;
-}
-
-void purple_circ_buffer_destroy(PurpleCircBuffer *buf) {
- g_return_if_fail(buf != NULL);
-
- g_free(buf->buffer);
- g_free(buf);
-}
-
-static void grow_circ_buffer(PurpleCircBuffer *buf, gsize len) {
- int in_offset = 0, out_offset = 0;
- int start_buflen;
-
- g_return_if_fail(buf != NULL);
-
- start_buflen = buf->buflen;
-
- while ((buf->buflen - buf->bufused) < len)
- buf->buflen += buf->growsize;
-
- if (buf->inptr != NULL) {
- in_offset = buf->inptr - buf->buffer;
- out_offset = buf->outptr - buf->buffer;
- }
- buf->buffer = g_realloc(buf->buffer, buf->buflen);
-
- /* adjust the fill and remove pointer locations */
- if (buf->inptr == NULL) {
- buf->inptr = buf->outptr = buf->buffer;
- } else {
- buf->inptr = buf->buffer + in_offset;
- buf->outptr = buf->buffer + out_offset;
- }
-
- /* If the fill pointer is wrapped to before the remove
- * pointer, we need to shift the data */
- if (in_offset < out_offset
- || (in_offset == out_offset && buf->bufused > 0)) {
- int shift_n = MIN(buf->buflen - start_buflen,
- in_offset);
- memcpy(buf->buffer + start_buflen, buf->buffer,
- shift_n);
-
- /* If we couldn't fit the wrapped read buffer
- * at the end */
- if (shift_n < in_offset) {
- memmove(buf->buffer,
- buf->buffer + shift_n,
- in_offset - shift_n);
- buf->inptr = buf->buffer +
- (in_offset - shift_n);
- } else {
- buf->inptr = buf->buffer +
- start_buflen + in_offset;
- }
- }
-}
-
-void purple_circ_buffer_append(PurpleCircBuffer *buf, gconstpointer src, gsize len) {
-
- int len_stored;
-
- g_return_if_fail(buf != NULL);
-
- /* Grow the buffer, if necessary */
- if ((buf->buflen - buf->bufused) < len)
- grow_circ_buffer(buf, len);
-
- /* If there is not enough room to copy all of src before hitting
- * the end of the buffer then we will need to do two copies.
- * One copy from inptr to the end of the buffer, and the
- * second copy from the start of the buffer to the end of src. */
- if (buf->inptr >= buf->outptr)
- len_stored = MIN(len, buf->buflen
- - (buf->inptr - buf->buffer));
- else
- len_stored = len;
-
- if (len_stored > 0)
- memcpy(buf->inptr, src, len_stored);
-
- if (len_stored < len) {
- memcpy(buf->buffer, (char*)src + len_stored, len - len_stored);
- buf->inptr = buf->buffer + (len - len_stored);
- } else {
- buf->inptr += len_stored;
- }
-
- buf->bufused += len;
-}
-
-gsize purple_circ_buffer_get_max_read(const PurpleCircBuffer *buf) {
- gsize max_read;
-
- g_return_val_if_fail(buf != NULL, 0);
-
- if (buf->bufused == 0)
- max_read = 0;
- else if ((buf->outptr - buf->inptr) >= 0)
- max_read = buf->buflen - (buf->outptr - buf->buffer);
- else
- max_read = buf->inptr - buf->outptr;
-
- return max_read;
-}
-
-gboolean purple_circ_buffer_mark_read(PurpleCircBuffer *buf, gsize len) {
- g_return_val_if_fail(buf != NULL, FALSE);
- g_return_val_if_fail(purple_circ_buffer_get_max_read(buf) >= len, FALSE);
-
- buf->outptr += len;
- buf->bufused -= len;
- /* wrap to the start if we're at the end */
- if ((buf->outptr - buf->buffer) == buf->buflen)
- buf->outptr = buf->buffer;
-
- return TRUE;
-}
-
diff --git a/libpurple/circbuffer.h b/libpurple/circbuffer.h
deleted file mode 100644
index 77e7724161..0000000000
--- a/libpurple/circbuffer.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/**
- * @file circbuffer.h Buffer Utility Functions
- * @ingroup core
- */
-
-/* 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 _CIRCBUFFER_H
-#define _CIRCBUFFER_H
-
-#include <glib.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct _PurpleCircBuffer {
-
- /** A pointer to the starting address of our chunk of memory. */
- gchar *buffer;
-
- /** The incremental amount to increase this buffer by when
- * the buffer is not big enough to hold incoming data, in bytes. */
- gsize growsize;
-
- /** The length of this buffer, in bytes. */
- gsize buflen;
-
- /** The number of bytes of this buffer that contain unread data. */
- gsize bufused;
-
- /** A pointer to the next byte where new incoming data is
- * buffered to. */
- gchar *inptr;
-
- /** A pointer to the next byte of buffered data that should be
- * read by the consumer. */
- gchar *outptr;
-
-} PurpleCircBuffer;
-
-/**
- * Creates a new circular buffer. This will not allocate any memory for the
- * actual buffer until data is appended to it.
- *
- * @param growsize The amount that the buffer should grow the first time data
- * is appended and every time more space is needed. Pass in
- * "0" to use the default of 256 bytes.
- *
- * @return The new PurpleCircBuffer. This should be freed with
- * purple_circ_buffer_destroy when you are done with it
- */
-PurpleCircBuffer *purple_circ_buffer_new(gsize growsize);
-
-/**
- * Dispose of the PurpleCircBuffer and free any memory used by it (including any
- * memory used by the internal buffer).
- *
- * @param buf The PurpleCircBuffer to free
- */
-void purple_circ_buffer_destroy(PurpleCircBuffer *buf);
-
-/**
- * Append data to the PurpleCircBuffer. This will grow the internal
- * buffer to fit the added data, if needed.
- *
- * @param buf The PurpleCircBuffer to which to append the data
- * @param src pointer to the data to copy into the buffer
- * @param len number of bytes to copy into the buffer
- */
-void purple_circ_buffer_append(PurpleCircBuffer *buf, gconstpointer src, gsize len);
-
-/**
- * Determine the maximum number of contiguous bytes that can be read from the
- * PurpleCircBuffer.
- * Note: This may not be the total number of bytes that are buffered - a
- * subsequent call after calling purple_circ_buffer_mark_read() may indicate more
- * data is available to read.
- *
- * @param buf the PurpleCircBuffer for which to determine the maximum contiguous
- * bytes that can be read.
- *
- * @return the number of bytes that can be read from the PurpleCircBuffer
- */
-gsize purple_circ_buffer_get_max_read(const PurpleCircBuffer *buf);
-
-/**
- * Mark the number of bytes that have been read from the buffer.
- *
- * @param buf The PurpleCircBuffer to mark bytes read from
- * @param len The number of bytes to mark as read
- *
- * @return TRUE if we successfully marked the bytes as having been read, FALSE
- * otherwise.
- */
-gboolean purple_circ_buffer_mark_read(PurpleCircBuffer *buf, gsize len);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _CIRCBUFFER_H */
diff --git a/libpurple/circularbuffer.c b/libpurple/circularbuffer.c
new file mode 100644
index 0000000000..3bffc80b07
--- /dev/null
+++ b/libpurple/circularbuffer.c
@@ -0,0 +1,476 @@
+/* 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 "glibcompat.h"
+
+#include "circularbuffer.h"
+
+#define DEFAULT_BUF_SIZE 256
+
+#define PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_CIRCULAR_BUFFER, PurpleCircularBufferPrivate))
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+typedef struct {
+ /** A pointer to the starting address of our chunk of memory. */
+ gchar *buffer;
+
+ /** The incremental amount to increase this buffer by when
+ * the buffer is not big enough to hold incoming data, in bytes. */
+ gsize growsize;
+
+ /** The length of this buffer, in bytes. */
+ gsize buflen;
+
+ /** The number of bytes of this buffer that contain unread data. */
+ gsize bufused;
+
+ /** A pointer to the next byte where new incoming data is
+ * buffered to. */
+ gchar *input;
+
+ /** A pointer to the next byte of buffered data that should be
+ * read by the consumer. */
+ gchar *output;
+} PurpleCircularBufferPrivate;
+
+/******************************************************************************
+ * Enums
+ *****************************************************************************/
+enum {
+ PROP_ZERO,
+ PROP_GROW_SIZE,
+ PROP_BUFFER_USED,
+ PROP_INPUT,
+ PROP_OUTPUT,
+ PROP_LAST,
+};
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+static GObjectClass *parent_class = NULL;
+static GParamSpec *properties[PROP_LAST];
+
+/******************************************************************************
+ * Circular Buffer Implementation
+ *****************************************************************************/
+static void
+purple_circular_buffer_real_grow(PurpleCircularBuffer *buffer, gsize len) {
+ PurpleCircularBufferPrivate *priv = NULL;
+ gsize in_offset = 0, out_offset = 0;
+ gsize start_buflen;
+ GObject *obj;
+
+ priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer);
+
+ start_buflen = priv->buflen;
+
+ while((priv->buflen - priv->bufused) < len)
+ priv->buflen += priv->growsize;
+
+ if(priv->input != NULL) {
+ in_offset = priv->input - priv->buffer;
+ out_offset = priv->output - priv->buffer;
+ }
+
+ priv->buffer = g_realloc(priv->buffer, priv->buflen);
+
+ /* adjust the fill and remove pointer locations */
+ if(priv->input == NULL) {
+ priv->input = priv->output = priv->buffer;
+ } else {
+ priv->input = priv->buffer + in_offset;
+ priv->output = priv->buffer + out_offset;
+ }
+
+ /* If the fill pointer is wrapped to before the remove
+ * pointer, we need to shift the data */
+ if(in_offset < out_offset
+ || (in_offset == out_offset && priv->bufused > 0))
+ {
+ gsize shift_n = MIN(priv->buflen - start_buflen, in_offset);
+ memcpy(priv->buffer + start_buflen, priv->buffer, shift_n);
+
+ /* If we couldn't fit the wrapped read buffer at the end */
+ if (shift_n < in_offset) {
+ memmove(priv->buffer, priv->buffer + shift_n, in_offset - shift_n);
+ priv->input = priv->buffer + (in_offset - shift_n);
+ } else {
+ priv->input = priv->buffer + start_buflen + in_offset;
+ }
+ }
+
+ obj = G_OBJECT(buffer);
+ g_object_freeze_notify(obj);
+ g_object_notify_by_pspec(obj, properties[PROP_INPUT]);
+ g_object_notify_by_pspec(obj, properties[PROP_OUTPUT]);
+ g_object_thaw_notify(obj);
+}
+
+static void
+purple_circular_buffer_real_append(PurpleCircularBuffer *buffer,
+ gconstpointer src, gsize len)
+{
+ PurpleCircularBufferPrivate *priv = NULL;
+ gsize len_stored;
+ GObject *obj;
+
+ priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer);
+
+ /* Grow the buffer, if necessary */
+ if((priv->buflen - priv->bufused) < len)
+ purple_circular_buffer_grow(buffer, len);
+
+ /* If there is not enough room to copy all of src before hitting
+ * the end of the buffer then we will need to do two copies.
+ * One copy from input to the end of the buffer, and the
+ * second copy from the start of the buffer to the end of src. */
+ if(priv->input >= priv->output)
+ len_stored = MIN(len, priv->buflen - (priv->input - priv->buffer));
+ else
+ len_stored = len;
+
+ if(len_stored > 0)
+ memcpy(priv->input, src, len_stored);
+
+ if(len_stored < len) {
+ memcpy(priv->buffer, (char*)src + len_stored, len - len_stored);
+ priv->input = priv->buffer + (len - len_stored);
+ } else {
+ priv->input += len_stored;
+ }
+
+ priv->bufused += len;
+
+ obj = G_OBJECT(buffer);
+ g_object_freeze_notify(obj);
+ g_object_notify_by_pspec(obj, properties[PROP_BUFFER_USED]);
+ g_object_notify_by_pspec(obj, properties[PROP_INPUT]);
+ g_object_thaw_notify(obj);
+}
+
+static gsize
+purple_circular_buffer_real_max_read_size(const PurpleCircularBuffer *buffer) {
+ PurpleCircularBufferPrivate *priv = NULL;
+ gsize max_read;
+
+ priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer);
+
+ if(priv->bufused == 0)
+ max_read = 0;
+ else if((priv->output - priv->input) >= 0)
+ max_read = priv->buflen - (priv->output - priv->buffer);
+ else
+ max_read = priv->input - priv->output;
+
+ return max_read;
+}
+
+static gboolean
+purple_circular_buffer_real_mark_read(PurpleCircularBuffer *buffer,
+ gsize len)
+{
+ PurpleCircularBufferPrivate *priv = NULL;
+ GObject *obj;
+
+ g_return_val_if_fail(purple_circular_buffer_get_max_read(buffer) >= len, FALSE);
+
+ priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer);
+
+ priv->output += len;
+ priv->bufused -= len;
+
+ /* wrap to the start if we're at the end */
+ if((gsize)(priv->output - priv->buffer) == priv->buflen)
+ priv->output = priv->buffer;
+
+ obj = G_OBJECT(buffer);
+ g_object_freeze_notify(obj);
+ g_object_notify_by_pspec(obj, properties[PROP_BUFFER_USED]);
+ g_object_notify_by_pspec(obj, properties[PROP_OUTPUT]);
+ g_object_thaw_notify(obj);
+
+ return TRUE;
+}
+
+/******************************************************************************
+ * Private API
+ *****************************************************************************/
+static void
+purple_circular_buffer_set_grow_size(PurpleCircularBuffer *buffer,
+ gsize grow_size)
+{
+ PurpleCircularBufferPrivate *priv =
+ PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer);
+
+ priv->growsize = (grow_size != 0) ? grow_size : DEFAULT_BUF_SIZE;
+
+ g_object_notify_by_pspec(G_OBJECT(buffer), properties[PROP_GROW_SIZE]);
+}
+
+static const gchar *
+purple_circular_buffer_get_input(const PurpleCircularBuffer *buffer) {
+ PurpleCircularBufferPrivate *priv = NULL;
+
+ g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), NULL);
+
+ priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer);
+
+ return priv->input;
+}
+
+/******************************************************************************
+ * Object Stuff
+ *****************************************************************************/
+static void
+purple_circular_buffer_finalize(GObject *obj) {
+ PurpleCircularBufferPrivate *priv =
+ PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(obj);
+
+ g_free(priv->buffer);
+
+ G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+static void
+purple_circular_buffer_get_property(GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ PurpleCircularBuffer *buffer = PURPLE_CIRCULAR_BUFFER(obj);
+
+ switch(param_id) {
+ case PROP_GROW_SIZE:
+ g_value_set_ulong(value,
+ purple_circular_buffer_get_grow_size(buffer));
+ break;
+ case PROP_BUFFER_USED:
+ g_value_set_ulong(value,
+ purple_circular_buffer_get_used(buffer));
+ break;
+ case PROP_INPUT:
+ g_value_set_pointer(value,
+ (void*) purple_circular_buffer_get_input(buffer));
+ break;
+ case PROP_OUTPUT:
+ g_value_set_pointer(value,
+ (void*) purple_circular_buffer_get_output(buffer));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_circular_buffer_set_property(GObject *obj, guint param_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ PurpleCircularBuffer *buffer = PURPLE_CIRCULAR_BUFFER(obj);
+
+ switch(param_id) {
+ case PROP_GROW_SIZE:
+ purple_circular_buffer_set_grow_size(buffer,
+ g_value_get_ulong(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_circular_buffer_class_init(PurpleCircularBufferClass *klass) {
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+ PurpleCircularBufferClass *buffer_class = PURPLE_CIRCULAR_BUFFER_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ g_type_class_add_private(klass, sizeof(PurpleCircularBufferPrivate));
+
+ obj_class->finalize = purple_circular_buffer_finalize;
+ obj_class->get_property = purple_circular_buffer_get_property;
+ obj_class->set_property = purple_circular_buffer_set_property;
+
+ buffer_class->grow = purple_circular_buffer_real_grow;
+ buffer_class->append = purple_circular_buffer_real_append;
+ buffer_class->max_read_size = purple_circular_buffer_real_max_read_size;
+ buffer_class->mark_read = purple_circular_buffer_real_mark_read;
+
+ /* using a ulong for the gsize properties since there is no
+ * g_param_spec_size, and the ulong should always work. --gk 3/21/11
+ */
+ properties[PROP_GROW_SIZE] = g_param_spec_ulong("grow-size", "grow-size",
+ "The grow size of the buffer",
+ 0, G_MAXSIZE, 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_BUFFER_USED] = g_param_spec_ulong("buffer-used",
+ "buffer-used",
+ "The amount of the buffer used",
+ 0, G_MAXSIZE, 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_INPUT] = g_param_spec_pointer("input", "input",
+ "The input pointer of the buffer",
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_OUTPUT] = g_param_spec_pointer("output", "output",
+ "The output pointer of the buffer",
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, PROP_LAST, properties);
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+GType
+purple_circular_buffer_get_type(void) {
+ static GType type = 0;
+
+ if(G_UNLIKELY(type == 0)) {
+ static const GTypeInfo info = {
+ .class_size = sizeof(PurpleCircularBufferClass),
+ .class_init = (GClassInitFunc)purple_circular_buffer_class_init,
+ .instance_size = sizeof(PurpleCircularBuffer),
+ };
+
+ type = g_type_register_static(G_TYPE_OBJECT,
+ "PurpleCircularBuffer",
+ &info, 0);
+ }
+
+ return type;
+}
+
+PurpleCircularBuffer *
+purple_circular_buffer_new(gsize growsize) {
+ return g_object_new(PURPLE_TYPE_CIRCULAR_BUFFER,
+ "grow-size", growsize ? growsize : DEFAULT_BUF_SIZE,
+ NULL);
+}
+
+void
+purple_circular_buffer_grow(PurpleCircularBuffer *buffer, gsize len) {
+ PurpleCircularBufferClass *klass = NULL;
+
+ g_return_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer));
+
+ klass = PURPLE_CIRCULAR_BUFFER_GET_CLASS(buffer);
+ if(klass && klass->grow)
+ klass->grow(buffer, len);
+}
+
+void
+purple_circular_buffer_append(PurpleCircularBuffer *buffer, gconstpointer src,
+ gsize len)
+{
+ PurpleCircularBufferClass *klass = NULL;
+
+ g_return_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer));
+ g_return_if_fail(src != NULL);
+
+ klass = PURPLE_CIRCULAR_BUFFER_GET_CLASS(buffer);
+ if(klass && klass->append)
+ klass->append(buffer, src, len);
+}
+
+gsize
+purple_circular_buffer_get_max_read(const PurpleCircularBuffer *buffer) {
+ PurpleCircularBufferClass *klass = NULL;
+
+ g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), 0);
+
+ klass = PURPLE_CIRCULAR_BUFFER_GET_CLASS(buffer);
+ if(klass && klass->max_read_size)
+ return klass->max_read_size(buffer);
+
+ return 0;
+}
+
+gboolean
+purple_circular_buffer_mark_read(PurpleCircularBuffer *buffer, gsize len) {
+ PurpleCircularBufferClass *klass = NULL;
+
+ g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), FALSE);
+
+ klass = PURPLE_CIRCULAR_BUFFER_GET_CLASS(buffer);
+ if(klass && klass->mark_read)
+ return klass->mark_read(buffer, len);
+
+ return FALSE;
+}
+
+gsize
+purple_circular_buffer_get_grow_size(const PurpleCircularBuffer *buffer) {
+
+ PurpleCircularBufferPrivate *priv = NULL;
+
+ g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), 0);
+
+ priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer);
+
+ return priv->growsize;
+}
+
+gsize
+purple_circular_buffer_get_used(const PurpleCircularBuffer *buffer) {
+ PurpleCircularBufferPrivate *priv = NULL;
+
+ g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), 0);
+
+ priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer);
+
+ return priv->bufused;
+}
+
+const gchar *
+purple_circular_buffer_get_output(const PurpleCircularBuffer *buffer) {
+ PurpleCircularBufferPrivate *priv = NULL;
+
+ g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), NULL);
+
+ priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer);
+
+ return priv->output;
+}
+
+void
+purple_circular_buffer_reset(PurpleCircularBuffer *buffer) {
+ PurpleCircularBufferPrivate *priv = NULL;
+ GObject *obj;
+
+ g_return_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer));
+
+ priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer);
+
+ priv->input = priv->buffer;
+ priv->output = priv->buffer;
+
+ obj = G_OBJECT(buffer);
+ g_object_freeze_notify(obj);
+ g_object_notify_by_pspec(obj, properties[PROP_INPUT]);
+ g_object_notify_by_pspec(obj, properties[PROP_OUTPUT]);
+ g_object_thaw_notify(obj);
+}
+
diff --git a/libpurple/circularbuffer.h b/libpurple/circularbuffer.h
new file mode 100644
index 0000000000..015fbe9ffb
--- /dev/null
+++ b/libpurple/circularbuffer.h
@@ -0,0 +1,172 @@
+/* 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 PURPLE_CIRCULAR_BUFFER_H
+#define PURPLE_CIRCULAR_BUFFER_H
+/**
+ * SECTION:circularbuffer
+ * @section_id: libpurple-circularbuffer
+ * @short_description: <filename>circularbuffer.h</filename>
+ * @title: Buffer Utility Functions
+ */
+
+#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))
+
+typedef struct _PurpleCircularBuffer PurpleCircularBuffer;
+typedef struct _PurpleCircularBufferClass PurpleCircularBufferClass;
+
+struct _PurpleCircularBuffer {
+ /*< private >*/
+ GObject parent;
+};
+
+struct _PurpleCircularBufferClass {
+ /*< private >*/
+ GObjectClass parent;
+
+ void (*grow)(PurpleCircularBuffer *buffer, gsize len);
+ void (*append)(PurpleCircularBuffer *buffer, gconstpointer src, gsize len);
+ gsize (*max_read_size)(const PurpleCircularBuffer *buffer);
+ gboolean (*mark_read)(PurpleCircularBuffer *buffer, gsize len);
+
+ void (*purple_reserved1)(void);
+ void (*purple_reserved2)(void);
+ void (*purple_reserved3)(void);
+ 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
+ * is appended and every time more space is needed. Pass in
+ * "0" to use the default of 256 bytes.
+ *
+ * Creates a new circular buffer. This will not allocate any memory for the
+ * actual buffer until data is appended to it.
+ *
+ * Returns: The new PurpleCircularBuffer.
+ */
+PurpleCircularBuffer *purple_circular_buffer_new(gsize growsize);
+
+/**
+ * purple_circular_buffer_append:
+ * @buf: The PurpleCircularBuffer to which to append the data
+ * @src: pointer to the data to copy into the buffer
+ * @len: number of bytes to copy into the buffer
+ *
+ * Append data to the PurpleCircularBuffer. This will grow the internal
+ * buffer to fit the added data, if needed.
+ */
+void purple_circular_buffer_append(PurpleCircularBuffer *buf, gconstpointer src, gsize len);
+
+/**
+ * purple_circular_buffer_get_max_read:
+ * @buf: the PurpleCircularBuffer for which to determine the maximum
+ * contiguous bytes that can be read.
+ *
+ * Determine the maximum number of contiguous bytes that can be read from the
+ * PurpleCircularBuffer.
+ * Note: This may not be the total number of bytes that are buffered - a
+ * subsequent call after calling purple_circular_buffer_mark_read() may indicate
+ * more data is available to read.
+ *
+ * Returns: the number of bytes that can be read from the PurpleCircularBuffer
+ */
+gsize purple_circular_buffer_get_max_read(const PurpleCircularBuffer *buf);
+
+/**
+ * purple_circular_buffer_mark_read:
+ * @buf: The PurpleCircularBuffer to mark bytes read from
+ * @len: The number of bytes to mark as read
+ *
+ * Mark the number of bytes that have been read from the buffer.
+ *
+ * Returns: TRUE if we successfully marked the bytes as having been read, FALSE
+ * otherwise.
+ */
+gboolean purple_circular_buffer_mark_read(PurpleCircularBuffer *buf, gsize len);
+
+/**
+ * purple_circular_buffer_grow:
+ * @buffer: The PurpleCircularBuffer to grow.
+ * @len: The number of bytes the buffer should be able to hold.
+ *
+ * Increases the buffer size by a multiple of grow size, so that it can hold at
+ * least 'len' bytes.
+ */
+void purple_circular_buffer_grow(PurpleCircularBuffer *buffer, gsize len);
+
+/**
+ * purple_circular_buffer_get_grow_size:
+ * @buffer: The PurpleCircularBuffer from which to get grow size.
+ *
+ * Returns the number of bytes by which the buffer grows when more space is
+ * needed.
+ *
+ * Returns: The grow size of the buffer.
+ */
+gsize purple_circular_buffer_get_grow_size(const PurpleCircularBuffer *buffer);
+
+/**
+ * purple_circular_buffer_get_used:
+ * @buffer: The PurpleCircularBuffer from which to get used count.
+ *
+ * Returns the number of bytes of this buffer that contain unread data.
+ *
+ * Returns: The number of bytes that contain unread data.
+ */
+gsize purple_circular_buffer_get_used(const PurpleCircularBuffer *buffer);
+
+/**
+ * purple_circular_buffer_get_output:
+ * @buffer: The PurpleCircularBuffer from which to get the output pointer.
+ *
+ * Returns the output pointer of the buffer, where unread data is available.
+ * Use purple_circular_buffer_get_max_read() to determine the number of
+ * contiguous bytes that can be read from this output. After reading the data,
+ * call purple_circular_buffer_mark_read() to mark the retrieved data as read.
+ *
+ * Returns: The output pointer for the buffer.
+ */
+const gchar *purple_circular_buffer_get_output(const PurpleCircularBuffer *buffer);
+
+/**
+ * purple_circular_buffer_reset:
+ * @buffer: The PurpleCircularBuffer to reset.
+ *
+ * Resets the buffer contents.
+ */
+void purple_circular_buffer_reset(PurpleCircularBuffer *buffer);
+
+G_END_DECLS
+
+#endif /* PURPLE_CIRCULAR_BUFFER_H */
+
diff --git a/libpurple/cmds.c b/libpurple/cmds.c
index 6a495acd06..6bc5891071 100644
--- a/libpurple/cmds.c
+++ b/libpurple/cmds.c
@@ -1,8 +1,3 @@
-/**
- * @file cmds.c Commands API
- * @ingroup core
- */
-
/* Copyright (C) 2003-2004 Timothy Ringenbach <omarvo@hotmail.com
*
* This program is free software; you can redistribute it and/or modify
@@ -110,7 +105,7 @@ void purple_cmd_unregister(PurpleCmdId id)
}
}
-/**
+/*
* This sets args to a NULL-terminated array of strings. It should
* be freed using g_strfreev().
*/
@@ -202,7 +197,7 @@ PurpleCmdStatus purple_cmd_do_command(PurpleConversation *conv, const gchar *cmd
PurpleCmd *c;
GList *l;
gchar *err = NULL;
- gboolean is_im;
+ gboolean is_im = TRUE;
gboolean found = FALSE, tried_cmd = FALSE, right_type = FALSE, right_prpl = FALSE;
const gchar *prpl_id;
gchar **args = NULL;
@@ -212,12 +207,8 @@ PurpleCmdStatus purple_cmd_do_command(PurpleConversation *conv, const gchar *cmd
*error = NULL;
prpl_id = purple_account_get_protocol_id(purple_conversation_get_account(conv));
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
- is_im = TRUE;
- else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
+ if (PURPLE_IS_CHAT_CONVERSATION(conv))
is_im = FALSE;
- else
- return PURPLE_CMD_STATUS_FAILED;
rest = strchr(cmdline, ' ');
if (rest) {
@@ -311,10 +302,10 @@ GList *purple_cmd_list(PurpleConversation *conv)
for (l = cmds; l; l = l->next) {
c = l->data;
- if (conv && (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM))
+ if (conv && PURPLE_IS_IM_CONVERSATION(conv))
if (!(c->flags & PURPLE_CMD_FLAG_IM))
continue;
- if (conv && (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT))
+ if (conv && PURPLE_IS_CHAT_CONVERSATION(conv))
if (!(c->flags & PURPLE_CMD_FLAG_CHAT))
continue;
@@ -343,10 +334,10 @@ GList *purple_cmd_help(PurpleConversation *conv, const gchar *cmd)
if (cmd && !purple_strequal(cmd, c->cmd))
continue;
- if (conv && (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM))
+ if (conv && PURPLE_IS_IM_CONVERSATION(conv))
if (!(c->flags & PURPLE_CMD_FLAG_IM))
continue;
- if (conv && (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT))
+ if (conv && PURPLE_IS_CHAT_CONVERSATION(conv))
if (!(c->flags & PURPLE_CMD_FLAG_CHAT))
continue;
@@ -373,17 +364,20 @@ void purple_cmds_init(void)
gpointer handle = purple_cmds_get_handle();
purple_signal_register(handle, "cmd-added",
- purple_marshal_VOID__POINTER_INT_INT, NULL, 3,
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_INT),
- purple_value_new(PURPLE_TYPE_INT));
+ purple_marshal_VOID__POINTER_INT_INT, G_TYPE_NONE, 3,
+ G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);
purple_signal_register(handle, "cmd-removed",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_STRING));
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ G_TYPE_STRING);
}
void purple_cmds_uninit(void)
{
purple_signals_unregister_by_instance(purple_cmds_get_handle());
+
+ while (cmds) {
+ purple_cmd_free(cmds->data);
+ cmds = g_list_delete_link(cmds, cmds);
+ }
}
diff --git a/libpurple/cmds.h b/libpurple/cmds.h
index 5e189928f7..3eac993409 100644
--- a/libpurple/cmds.h
+++ b/libpurple/cmds.h
@@ -1,9 +1,3 @@
-/**
- * @file cmds.h Commands API
- * @ingroup core
- * @see @ref cmd-signals
- */
-
/* Copyright (C) 2003 Timothy Ringenbach <omarvo@hotmail.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -21,18 +15,29 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
*/
+
#ifndef _PURPLE_CMDS_H_
#define _PURPLE_CMDS_H_
+/**
+ * SECTION:cmds
+ * @section_id: libpurple-cmds
+ * @short_description: <filename>cmds.h</filename>
+ * @title: Commands API
+ * @see_also: <link linkend="chapter-signals-cmd">Command signals</link>
+ */
#include "conversation.h"
/**************************************************************************/
-/** @name Structures */
+/* Structures */
/**************************************************************************/
-/*@{*/
-/** The possible results of running a command with purple_cmd_do_command(). */
-typedef enum _PurpleCmdStatus {
+/**
+ * PurpleCmdStatus:
+ *
+ * The possible results of running a command with purple_cmd_do_command().
+ */
+typedef enum {
PURPLE_CMD_STATUS_OK,
PURPLE_CMD_STATUS_FAILED,
PURPLE_CMD_STATUS_NOT_FOUND,
@@ -41,34 +46,47 @@ typedef enum _PurpleCmdStatus {
PURPLE_CMD_STATUS_WRONG_TYPE
} PurpleCmdStatus;
-/** Commands registered with the core return one of these values when run.
- * Normally, a command will want to return one of the first two; in some
- * unusual cases, you might want to have several functions called for a
- * particular command; in this case, they should return
- * #PURPLE_CMD_RET_CONTINUE to cause the core to fall through to other
- * commands with the same name.
+/**
+ * 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_CONTINUE: Continue, looking for other commands with the same
+ * name to call.
+ *
+ * Commands registered with the core return one of these values when run.
+ * Normally, a command will want to return one of the first two; in some
+ * unusual cases, you might want to have several functions called for a
+ * particular command; in this case, they should return
+ * #PURPLE_CMD_RET_CONTINUE to cause the core to fall through to other
+ * commands with the same name.
*/
-typedef enum _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_CONTINUE /**< Continue, looking for other commands with the same name to call. */
+typedef enum {
+ PURPLE_CMD_RET_OK,
+ PURPLE_CMD_RET_FAILED,
+ PURPLE_CMD_RET_CONTINUE
} PurpleCmdRet;
#define PURPLE_CMD_FUNC(func) ((PurpleCmdFunc)func)
-/** A function implementing a command, as passed to purple_cmd_register().
+/**
+ * PurpleCmdFunc:
*
- * @todo document the arguments to these functions.
- * */
+ * A function implementing a command, as passed to purple_cmd_register().
+ */
+/* TODO document the arguments to these functions. */
typedef PurpleCmdRet (*PurpleCmdFunc)(PurpleConversation *, const gchar *cmd,
gchar **args, gchar **error, void *data);
-/** A unique integer representing a command registered with
- * purple_cmd_register(), which can subsequently be passed to
- * purple_cmd_unregister() to unregister that command.
+/**
+ * PurpleCmdId:
+ *
+ * A unique integer representing a command registered with
+ * purple_cmd_register(), which can subsequently be passed to
+ * purple_cmd_unregister() to unregister that command.
*/
typedef guint PurpleCmdId;
-typedef enum _PurpleCmdPriority {
+typedef enum {
PURPLE_CMD_P_VERY_LOW = -1000,
PURPLE_CMD_P_LOW = 0,
PURPLE_CMD_P_DEFAULT = 1000,
@@ -79,172 +97,185 @@ typedef enum _PurpleCmdPriority {
PURPLE_CMD_P_VERY_HIGH = 6000
} PurpleCmdPriority;
-/** Flags used to set various properties of commands. Every command should
- * have at least one of #PURPLE_CMD_FLAG_IM and #PURPLE_CMD_FLAG_CHAT set in
- * order to be even slighly useful.
+/**
+ * PurpleCmdFlag:
+ * @PURPLE_CMD_FLAG_IM: Command is usable in IMs.
+ * @PURPLE_CMD_FLAG_CHAT: Command is usable in multi-user chats.
+ * @PURPLE_CMD_FLAG_PRPL_ONLY: Command is usable only for a particular
+ * protocol.
+ * @PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS: Incorrect arguments to this command
+ * should be accepted anyway.
*
- * @see purple_cmd_register
+ * Flags used to set various properties of commands. Every command should
+ * have at least one of #PURPLE_CMD_FLAG_IM and #PURPLE_CMD_FLAG_CHAT set in
+ * order to be even slighly useful.
+ *
+ * See purple_cmd_register().
*/
-typedef enum _PurpleCmdFlag {
- /** Command is usable in IMs. */
+typedef enum {
PURPLE_CMD_FLAG_IM = 0x01,
- /** Command is usable in multi-user chats. */
PURPLE_CMD_FLAG_CHAT = 0x02,
- /** Command is usable only for a particular prpl. */
PURPLE_CMD_FLAG_PRPL_ONLY = 0x04,
- /** Incorrect arguments to this command should be accepted anyway. */
PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS = 0x08
} PurpleCmdFlag;
-/*@}*/
-
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name Commands API */
+/* Commands API */
/**************************************************************************/
-/*@{*/
/**
+ * purple_cmd_register:
+ * @cmd: The command. This should be a UTF-8 (or ASCII) string, with no spaces
+ * or other white space.
+ * @args: A string of characters describing to libpurple how to parse this
+ * command's arguments. If what the user types doesn't match this
+ * pattern, libpurple will keep looking for another command, unless
+ * the flag #PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS is passed in @f.
+ * This string should contain no whitespace, and use a single
+ * character for each argument. The recognized characters are:
+ * <itemizedlist>
+ * <listitem><literal>'w'</literal>: Matches a single word.</listitem>
+ * <listitem><literal>'W'</literal>: Matches a single word, with
+ * formatting.</listitem>
+ * <listitem><literal>'s'</literal>: Matches the rest of the
+ * arguments after this point,
+ * as a single string.</listitem>
+ * <listitem><literal>'S'</literal>: Same as <literal>'s'</literal>
+ * but with formatting.</listitem>
+ * </itemizedlist>
+ * If args is the empty string, then the command accepts no
+ * arguments. The args passed to the callback @func will be a %NULL
+ * terminated array of %NULL terminated strings, and will always
+ * match the number of arguments asked for, unless
+ * #PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS is passed.
+ * @p: This is the priority. Higher priority commands will be run first,
+ * and usually the first command will stop any others from being
+ * called.
+ * @f: Flags specifying various options about this command, combined with
+ * <literal>|</literal> (bitwise OR). You need to at least pass one of
+ * #PURPLE_CMD_FLAG_IM or #PURPLE_CMD_FLAG_CHAT (you may pass both) in
+ * order for the command to ever actually be called.
+ * @protocol_id: If the #PURPLE_CMD_FLAG_PRPL_ONLY flag is set, this is the id
+ * of the protocol to which the command applies (such as
+ * <literal>"prpl-msn"</literal>). If the flag is not set, this
+ * parameter is ignored; pass %NULL (or a humourous string of
+ * your choice!).
+ * @func: (scope call): This is the function to call when someone enters this
+ * command.
+ * @helpstr: a whitespace sensitive, UTF-8, HTML string describing how to
+ * use the command. The preferred format of this string is the
+ * command's name, followed by a space and any arguments it
+ * accepts (if it takes any arguments, otherwise no space),
+ * followed by a colon, two spaces, and a description of the
+ * command in sentence form. Do not include a slash before the
+ * command name.
+ * @data: User defined data to pass to the #PurpleCmdFunc @f.
+ *
* Register a new command with the core.
*
* The command will only happen if commands are enabled,
* which is a UI pref. UIs don't have to support commands at all.
*
- * @param cmd The command. This should be a UTF-8 (or ASCII) string, with no spaces
- * or other white space.
- * @param args A string of characters describing to libpurple how to parse this
- * command's arguments. If what the user types doesn't match this
- * pattern, libpurple will keep looking for another command, unless
- * the flag #PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS is passed in @a f.
- * This string should contain no whitespace, and use a single
- * character for each argument. The recognized characters are:
- * <ul>
- * <li><tt>'w'</tt>: Matches a single word.</li>
- * <li><tt>'W'</tt>: Matches a single word, with formatting.</li>
- * <li><tt>'s'</tt>: Matches the rest of the arguments after this
- * point, as a single string.</li>
- * <li><tt>'S'</tt>: Same as <tt>'s'</tt> but with formatting.</li>
- * </ul>
- * If args is the empty string, then the command accepts no arguments.
- * The args passed to the callback @a func will be a @c NULL
- * terminated array of @c NULL terminated strings, and will always
- * match the number of arguments asked for, unless
- * #PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS is passed.
- * @param p This is the priority. Higher priority commands will be run first,
- * and usually the first command will stop any others from being
- * called.
- * @param f Flags specifying various options about this command, combined with
- * <tt>|</tt> (bitwise OR). You need to at least pass one of
- * #PURPLE_CMD_FLAG_IM or #PURPLE_CMD_FLAG_CHAT (you may pass both) in
- * order for the command to ever actually be called.
- * @param prpl_id If the #PURPLE_CMD_FLAG_PRPL_ONLY flag is set, this is the id
- * of the prpl to which the command applies (such as
- * <tt>"prpl-msn"</tt>). If the flag is not set, this parameter
- * is ignored; pass @c NULL (or a humourous string of your
- * choice!).
- * @param func This is the function to call when someone enters this command.
- * @param helpstr a whitespace sensitive, UTF-8, HTML string describing how to
- * use the command. The preferred format of this string is the
- * command's name, followed by a space and any arguments it
- * accepts (if it takes any arguments, otherwise no space),
- * followed by a colon, two spaces, and a description of the
- * command in sentence form. Do not include a slash before the
- * command name.
- * @param data User defined data to pass to the #PurpleCmdFunc @a f.
- * @return A #PurpleCmdId, which is only used for calling
- * #purple_cmd_unregister, or @a 0 on failure.
+ * Returns: A #PurpleCmdId, which is only used for calling
+ * #purple_cmd_unregister, or 0 on failure.
*/
PurpleCmdId purple_cmd_register(const gchar *cmd, const gchar *args, PurpleCmdPriority p, PurpleCmdFlag f,
- const gchar *prpl_id, PurpleCmdFunc func, const gchar *helpstr, void *data);
+ const gchar *protocol_id, PurpleCmdFunc func, const gchar *helpstr, void *data);
/**
+ * purple_cmd_unregister:
+ * @id: The #PurpleCmdId to unregister, as returned by #purple_cmd_register.
+ *
* Unregister a command with the core.
*
* All registered commands must be unregistered, if they're registered by a plugin
* or something else that might go away. Normally this is called when the plugin
* unloads itself.
- *
- * @param id The #PurpleCmdId to unregister, as returned by #purple_cmd_register.
*/
void purple_cmd_unregister(PurpleCmdId id);
/**
- * Do a command.
- *
- * Normally the UI calls this to perform a command. This might also be useful
- * if aliases are ever implemented.
- *
- * @param conv The conversation the command was typed in.
- * @param cmdline The command the user typed (including all arguments) as a single string.
+ * purple_cmd_do_command:
+ * @conv: The conversation the command was typed in.
+ * @cmdline: The command the user typed (including all arguments) as a single string.
* The caller doesn't have to do any parsing, except removing the command
* prefix, which the core has no knowledge of. cmd should not contain any
* formatting, and should be in plain text (no html entities).
- * @param markup This is the same as cmd, but is the formatted version. It should be in
+ * @markup: This is the same as cmd, but is the formatted version. It should be in
* HTML, with < > and &, at least, escaped to html entities, and should
* include both the default formatting and any extra manual formatting.
- * @param errormsg If the command failed errormsg is filled in with the appropriate error
+ * @errormsg: If the command failed errormsg is filled in with the appropriate error
* message. It must be freed by the caller with g_free().
- * @return A #PurpleCmdStatus indicating if the command succeeded or failed.
+ *
+ * Do a command.
+ *
+ * Normally the UI calls this to perform a command. This might also be useful
+ * if aliases are ever implemented.
+ *
+ * Returns: A #PurpleCmdStatus indicating if the command succeeded or failed.
*/
PurpleCmdStatus purple_cmd_do_command(PurpleConversation *conv, const gchar *cmdline,
const gchar *markup, gchar **errormsg);
/**
+ * purple_cmd_list:
+ * @conv: The conversation, or %NULL.
+ *
* List registered commands.
*
- * Returns a <tt>GList</tt> (which must be freed by the caller) of all commands
- * that are valid in the context of @a conv, or all commands, if @a conv is @c
- * NULL. Don't keep this list around past the main loop, or anything else that
- * might unregister a command, as the <tt>const char *</tt>'s used get freed
+ * Returns a #GList (which must be freed by the caller) of all commands
+ * that are valid in the context of @conv, or all commands, if @conv is
+ * %NULL. Don't keep this list around past the main loop, or anything else that
+ * might unregister a command, as the <type>const char *</type>'s used get freed
* then.
*
- * @param conv The conversation, or @c NULL.
- * @return A @c GList of <tt>const char *</tt>, which must be freed with
- * <tt>g_list_free()</tt>.
+ * Returns: A #GList of <type>const char *</type>, which must be freed with
+ * g_list_free().
*/
GList *purple_cmd_list(PurpleConversation *conv);
/**
+ * purple_cmd_help:
+ * @conv: The conversation, or %NULL for no context.
+ * @cmd: The command. No wildcards accepted, but returns help for all
+ * commands if %NULL.
+ *
* Get the help string for a command.
*
* Returns the help strings for a given command in the form of a GList,
* one node for each matching command.
*
- * @param conv The conversation, or @c NULL for no context.
- * @param cmd The command. No wildcards accepted, but returns help for all
- * commands if @c NULL.
- * @return A <tt>GList</tt> of <tt>const char *</tt>s, which is the help string
+ * Returns: A #GList of <type>const char *</type>s, which is the help string
* for that command.
*/
GList *purple_cmd_help(PurpleConversation *conv, const gchar *cmd);
/**
+ * purple_cmds_get_handle:
+ *
* Get the handle for the commands API
- * @return The handle
- * @since 2.5.0
+ *
+ * Returns: The handle
*/
gpointer purple_cmds_get_handle(void);
/**
+ * purple_cmds_init:
+ *
* Initialize the commands subsystem.
- * @since 2.5.0
*/
void purple_cmds_init(void);
/**
+ * purple_cmds_uninit:
+ *
* Uninitialize the commands subsystem.
- * @since 2.5.0
*/
void purple_cmds_uninit(void);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_CMDS_H_ */
diff --git a/libpurple/connection.c b/libpurple/connection.c
index 9e0055fb17..d1a44a8ec2 100644
--- a/libpurple/connection.c
+++ b/libpurple/connection.c
@@ -1,8 +1,3 @@
-/**
- * @file connection.c Connection API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -26,11 +21,15 @@
#define _PURPLE_CONNECTION_C_
#include "internal.h"
+#include "glibcompat.h"
+
#include "account.h"
-#include "blist.h"
+#include "buddylist.h"
#include "connection.h"
#include "dbus-maybe.h"
#include "debug.h"
+#include "enums.h"
+#include "http.h"
#include "log.h"
#include "notify.h"
#include "prefs.h"
@@ -42,25 +41,93 @@
#define KEEPALIVE_INTERVAL 30
+#define PURPLE_CONNECTION_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_CONNECTION, PurpleConnectionPrivate))
+
+typedef struct _PurpleConnectionPrivate PurpleConnectionPrivate;
+
+/* Private data for a connection */
+struct _PurpleConnectionPrivate
+{
+ PurplePlugin *prpl; /* The protocol plugin. */
+ PurpleConnectionFlags flags; /* Connection flags. */
+
+ PurpleConnectionState state; /* The connection state. */
+
+ PurpleAccount *account; /* The account being connected to. */
+ char *password; /* The password used. */
+
+ GSList *active_chats; /* A list of active chats
+ (#PurpleChatConversation structs). */
+
+ /* TODO Remove this and use protocol-specific subclasses. */
+ void *proto_data; /* Protocol-specific data. */
+
+ char *display_name; /* How you appear to other people. */
+ guint keepalive; /* Keep-alive. */
+
+ /* Wants to Die state. This is set when the user chooses to log out, or
+ * when the protocol is disconnected and should not be automatically
+ * reconnected (incorrect password, etc.). prpls should rely on
+ * purple_connection_error() to set this for them rather than
+ * setting it themselves.
+ * See purple_connection_error_is_fatal()
+ */
+ gboolean wants_to_die;
+
+ gboolean is_finalizing; /* The object is being destroyed. */
+
+ /* The connection error and its description if an error occured */
+ PurpleConnectionErrorInfo *error_info;
+
+ guint disconnect_timeout; /* Timer used for nasty stack tricks */
+ time_t last_received; /* When we last received a packet. Set by the
+ prpl to avoid sending unneeded keepalives */
+};
+
+/* GObject property enums */
+enum
+{
+ PROP_0,
+ PROP_PRPL,
+ PROP_FLAGS,
+ PROP_STATE,
+ PROP_ACCOUNT,
+ PROP_PASSWORD,
+ PROP_DISPLAY_NAME,
+ PROP_LAST
+};
+
+static GObjectClass *parent_class;
+static GParamSpec *properties[PROP_LAST];
+
static GList *connections = NULL;
static GList *connections_connecting = NULL;
static PurpleConnectionUiOps *connection_ui_ops = NULL;
static int connections_handle;
+static PurpleConnectionErrorInfo *
+purple_connection_error_info_new(PurpleConnectionError type,
+ const gchar *description);
+
+/**************************************************************************
+ * Connection API
+ **************************************************************************/
static gboolean
send_keepalive(gpointer data)
{
PurpleConnection *gc = data;
PurplePluginProtocolInfo *prpl_info;
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
/* Only send keep-alives if we haven't heard from the
* server in a while.
*/
- if ((time(NULL) - gc->last_received) < KEEPALIVE_INTERVAL)
+ if ((time(NULL) - priv->last_received) < KEEPALIVE_INTERVAL)
return TRUE;
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(priv->prpl);
if (prpl_info->keepalive)
prpl_info->keepalive(gc);
@@ -71,244 +138,27 @@ static void
update_keepalive(PurpleConnection *gc, gboolean on)
{
PurplePluginProtocolInfo *prpl_info = NULL;
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
- if (gc != NULL && gc->prpl != NULL)
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ if (priv != NULL && priv->prpl != NULL)
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(priv->prpl);
if (!prpl_info || !prpl_info->keepalive)
return;
- if (on && !gc->keepalive)
+ if (on && !priv->keepalive)
{
purple_debug_info("connection", "Activating keepalive.\n");
- gc->keepalive = purple_timeout_add_seconds(KEEPALIVE_INTERVAL, send_keepalive, gc);
+ priv->keepalive = purple_timeout_add_seconds(KEEPALIVE_INTERVAL, send_keepalive, gc);
}
- else if (!on && gc->keepalive > 0)
+ else if (!on && priv->keepalive > 0)
{
purple_debug_info("connection", "Deactivating keepalive.\n");
- purple_timeout_remove(gc->keepalive);
- gc->keepalive = 0;
+ purple_timeout_remove(priv->keepalive);
+ priv->keepalive = 0;
}
}
-void
-purple_connection_new(PurpleAccount *account, gboolean regist, const char *password)
-{
- _purple_connection_new(account, regist, password);
-}
-
-void
-_purple_connection_new(PurpleAccount *account, gboolean regist, const char *password)
-{
- PurpleConnection *gc;
- PurplePlugin *prpl;
- PurplePluginProtocolInfo *prpl_info;
-
- g_return_if_fail(account != NULL);
-
- if (!purple_account_is_disconnected(account))
- return;
-
- prpl = purple_find_prpl(purple_account_get_protocol_id(account));
-
- if (prpl != NULL)
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
- else {
- gchar *message;
-
- message = g_strdup_printf(_("Missing protocol plugin for %s"),
- purple_account_get_username(account));
- purple_notify_error(NULL, regist ? _("Registration Error") :
- _("Connection Error"), message, NULL);
- g_free(message);
- return;
- }
-
- if (regist)
- {
- if (prpl_info->register_user == NULL)
- return;
- }
- else
- {
- if (((password == NULL) || (*password == '\0')) &&
- !(prpl_info->options & OPT_PROTO_NO_PASSWORD) &&
- !(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL))
- {
- purple_debug_error("connection", "Cannot connect to account %s without "
- "a password.\n", purple_account_get_username(account));
- return;
- }
- }
-
- gc = g_new0(PurpleConnection, 1);
- PURPLE_DBUS_REGISTER_POINTER(gc, PurpleConnection);
-
- gc->prpl = prpl;
- if ((password != NULL) && (*password != '\0'))
- gc->password = g_strdup(password);
- purple_connection_set_account(gc, account);
- purple_connection_set_state(gc, PURPLE_CONNECTING);
- connections = g_list_append(connections, gc);
- purple_account_set_connection(account, gc);
-
- purple_signal_emit(purple_connections_get_handle(), "signing-on", gc);
-
- if (regist)
- {
- purple_debug_info("connection", "Registering. gc = %p\n", gc);
-
- /* set this so we don't auto-reconnect after registering */
- gc->wants_to_die = TRUE;
-
- prpl_info->register_user(account);
- }
- else
- {
- purple_debug_info("connection", "Connecting. gc = %p\n", gc);
-
- purple_signal_emit(purple_accounts_get_handle(), "account-connecting", account);
- prpl_info->login(account);
- }
-}
-void
-purple_connection_new_unregister(PurpleAccount *account, const char *password, PurpleAccountUnregistrationCb cb, void *user_data)
-{
- _purple_connection_new_unregister(account, password, cb, user_data);
-}
-
-void
-_purple_connection_new_unregister(PurpleAccount *account, const char *password, PurpleAccountUnregistrationCb cb, void *user_data)
-{
- /* Lots of copy/pasted code to avoid API changes. You might want to integrate that into the previous function when posssible. */
- PurpleConnection *gc;
- PurplePlugin *prpl;
- PurplePluginProtocolInfo *prpl_info;
-
- g_return_if_fail(account != NULL);
-
- prpl = purple_find_prpl(purple_account_get_protocol_id(account));
-
- if (prpl != NULL)
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
- else {
- gchar *message;
-
- message = g_strdup_printf(_("Missing protocol plugin for %s"),
- purple_account_get_username(account));
- purple_notify_error(NULL, _("Unregistration Error"), message, NULL);
- g_free(message);
- return;
- }
-
- if (!purple_account_is_disconnected(account)) {
- prpl_info->unregister_user(account, cb, user_data);
- return;
- }
-
- if (((password == NULL) || (*password == '\0')) &&
- !(prpl_info->options & OPT_PROTO_NO_PASSWORD) &&
- !(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL))
- {
- purple_debug_error("connection", "Cannot connect to account %s without "
- "a password.\n", purple_account_get_username(account));
- return;
- }
-
- gc = g_new0(PurpleConnection, 1);
- PURPLE_DBUS_REGISTER_POINTER(gc, PurpleConnection);
-
- gc->prpl = prpl;
- if ((password != NULL) && (*password != '\0'))
- gc->password = g_strdup(password);
- purple_connection_set_account(gc, account);
- purple_connection_set_state(gc, PURPLE_CONNECTING);
- connections = g_list_append(connections, gc);
- purple_account_set_connection(account, gc);
-
- purple_signal_emit(purple_connections_get_handle(), "signing-on", gc);
-
- purple_debug_info("connection", "Unregistering. gc = %p\n", gc);
-
- prpl_info->unregister_user(account, cb, user_data);
-}
-
-void
-purple_connection_destroy(PurpleConnection *gc)
-{
- _purple_connection_destroy(gc);
-}
-
-void
-_purple_connection_destroy(PurpleConnection *gc)
-{
- PurpleAccount *account;
- GSList *buddies;
- PurplePluginProtocolInfo *prpl_info = NULL;
- gboolean remove = FALSE;
-
- g_return_if_fail(gc != NULL);
-
- account = purple_connection_get_account(gc);
-
- purple_debug_info("connection", "Disconnecting connection %p\n", gc);
-
- if (purple_connection_get_state(gc) != PURPLE_CONNECTING)
- remove = TRUE;
-
- purple_signal_emit(purple_connections_get_handle(), "signing-off", gc);
-
- while (gc->buddy_chats)
- {
- PurpleConversation *b = gc->buddy_chats->data;
-
- gc->buddy_chats = g_slist_remove(gc->buddy_chats, b);
- purple_conv_chat_left(PURPLE_CONV_CHAT(b));
- }
-
- update_keepalive(gc, FALSE);
-
- purple_proxy_connect_cancel_with_handle(gc);
-
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
- if (prpl_info->close)
- (prpl_info->close)(gc);
-
- /* Clear out the proto data that was freed in the prpl close method*/
- buddies = purple_find_buddies(account, NULL);
- while (buddies != NULL) {
- PurpleBuddy *buddy = buddies->data;
- purple_buddy_set_protocol_data(buddy, NULL);
- buddies = g_slist_delete_link(buddies, buddies);
- }
-
- connections = g_list_remove(connections, gc);
-
- purple_connection_set_state(gc, PURPLE_DISCONNECTED);
-
- if (remove)
- purple_blist_remove_account(account);
-
- purple_signal_emit(purple_connections_get_handle(), "signed-off", gc);
-
- purple_account_request_close_with_account(account);
- purple_request_close_with_handle(gc);
- purple_notify_close_with_handle(gc);
-
- purple_debug_info("connection", "Destroying connection %p\n", gc);
-
- purple_account_set_connection(account, NULL);
-
- g_free(gc->password);
- g_free(gc->display_name);
-
- if (gc->disconnect_timeout > 0)
- purple_timeout_remove(gc->disconnect_timeout);
-
- PURPLE_DBUS_UNREGISTER_POINTER(gc);
- g_free(gc);
-}
-
/*
* d:)->-<
*
@@ -323,24 +173,25 @@ void
purple_connection_set_state(PurpleConnection *gc, PurpleConnectionState state)
{
PurpleConnectionUiOps *ops;
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
- g_return_if_fail(gc != NULL);
+ g_return_if_fail(priv != NULL);
- if (gc->state == state)
+ if (priv->state == state)
return;
- gc->state = state;
+ priv->state = state;
ops = purple_connections_get_ui_ops();
- if (gc->state == PURPLE_CONNECTING) {
+ if (priv->state == PURPLE_CONNECTION_CONNECTING) {
connections_connecting = g_list_append(connections_connecting, gc);
}
else {
connections_connecting = g_list_remove(connections_connecting, gc);
}
- if (gc->state == PURPLE_CONNECTED) {
+ if (priv->state == PURPLE_CONNECTION_CONNECTED) {
PurpleAccount *account;
PurplePresence *presence;
@@ -374,11 +225,11 @@ purple_connection_set_state(PurpleConnection *gc, PurpleConnectionState state)
purple_signal_emit(purple_connections_get_handle(), "signed-on", gc);
purple_signal_emit_return_1(purple_connections_get_handle(), "autojoin", gc);
- serv_set_permit_deny(gc);
+ purple_serv_set_permit_deny(gc);
update_keepalive(gc, TRUE);
}
- else if (gc->state == PURPLE_DISCONNECTED) {
+ else if (priv->state == PURPLE_CONNECTION_DISCONNECTED) {
PurpleAccount *account = purple_connection_get_account(gc);
if (purple_prefs_get_bool("/purple/logging/log_system"))
@@ -401,78 +252,165 @@ purple_connection_set_state(PurpleConnection *gc, PurpleConnectionState state)
if (ops != NULL && ops->disconnected != NULL)
ops->disconnected(gc);
}
+
+ if (!priv->is_finalizing)
+ g_object_notify_by_pspec(G_OBJECT(gc), properties[PROP_STATE]);
}
void
-purple_connection_set_account(PurpleConnection *gc, PurpleAccount *account)
+purple_connection_set_flags(PurpleConnection *gc, PurpleConnectionFlags flags)
{
- g_return_if_fail(gc != NULL);
- g_return_if_fail(account != NULL);
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
- gc->account = account;
+ g_return_if_fail(priv != NULL);
+
+ priv->flags = flags;
+
+ if (!priv->is_finalizing)
+ g_object_notify_by_pspec(G_OBJECT(gc), properties[PROP_FLAGS]);
}
void
purple_connection_set_display_name(PurpleConnection *gc, const char *name)
{
- g_return_if_fail(gc != NULL);
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
+
+ g_return_if_fail(priv != NULL);
- g_free(gc->display_name);
- gc->display_name = g_strdup(name);
+ g_free(priv->display_name);
+ priv->display_name = g_strdup(name);
+
+ g_object_notify_by_pspec(G_OBJECT(gc), properties[PROP_DISPLAY_NAME]);
}
void
-purple_connection_set_protocol_data(PurpleConnection *connection, void *proto_data) {
- g_return_if_fail(connection != NULL);
+purple_connection_set_protocol_data(PurpleConnection *gc, void *proto_data)
+{
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
- connection->proto_data = proto_data;
+ g_return_if_fail(priv != NULL);
+
+ priv->proto_data = proto_data;
}
PurpleConnectionState
purple_connection_get_state(const PurpleConnection *gc)
{
- g_return_val_if_fail(gc != NULL, PURPLE_DISCONNECTED);
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
+
+ g_return_val_if_fail(priv != NULL, PURPLE_CONNECTION_DISCONNECTED);
- return gc->state;
+ return priv->state;
+}
+
+PurpleConnectionFlags
+purple_connection_get_flags(const PurpleConnection *gc)
+{
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return priv->flags;
+}
+
+gboolean
+purple_connection_is_disconnecting(const PurpleConnection *gc)
+{
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
+
+ g_return_val_if_fail(priv != NULL, TRUE);
+
+ return priv->is_finalizing;
}
PurpleAccount *
purple_connection_get_account(const PurpleConnection *gc)
{
- g_return_val_if_fail(gc != NULL, NULL);
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
+
+ g_return_val_if_fail(priv != NULL, NULL);
- return gc->account;
+ return priv->account;
}
PurplePlugin *
purple_connection_get_prpl(const PurpleConnection *gc)
{
- g_return_val_if_fail(gc != NULL, NULL);
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
- return gc->prpl;
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->prpl;
}
const char *
purple_connection_get_password(const PurpleConnection *gc)
{
- g_return_val_if_fail(gc != NULL, NULL);
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->password;
+}
+
+GSList *
+purple_connection_get_active_chats(const PurpleConnection *gc)
+{
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
- return gc->password ? gc->password : gc->account->password;
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->active_chats;
}
const char *
purple_connection_get_display_name(const PurpleConnection *gc)
{
- g_return_val_if_fail(gc != NULL, NULL);
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
+
+ g_return_val_if_fail(priv != NULL, NULL);
- return gc->display_name;
+ return priv->display_name;
}
void *
-purple_connection_get_protocol_data(const PurpleConnection *connection) {
- g_return_val_if_fail(connection != NULL, NULL);
+purple_connection_get_protocol_data(const PurpleConnection *gc)
+{
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->proto_data;
+}
+
+void
+_purple_connection_add_active_chat(PurpleConnection *gc, PurpleChatConversation *chat)
+{
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
+
+ g_return_if_fail(priv != NULL);
- return connection->proto_data;
+ 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_PRIVATE(gc);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->active_chats = g_slist_remove(priv->active_chats, chat);
+}
+
+gboolean
+_purple_connection_wants_to_die(const PurpleConnection *gc)
+{
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
+
+ g_return_val_if_fail(priv != NULL, FALSE);
+
+ return priv->wants_to_die;
}
void
@@ -481,7 +419,7 @@ purple_connection_update_progress(PurpleConnection *gc, const char *text,
{
PurpleConnectionUiOps *ops;
- g_return_if_fail(gc != NULL);
+ g_return_if_fail(PURPLE_IS_CONNECTION(gc));
g_return_if_fail(text != NULL);
g_return_if_fail(step < count);
g_return_if_fail(count > 1);
@@ -497,7 +435,7 @@ purple_connection_notice(PurpleConnection *gc, const char *text)
{
PurpleConnectionUiOps *ops;
- g_return_if_fail(gc != NULL);
+ g_return_if_fail(PURPLE_IS_CONNECTION(gc));
g_return_if_fail(text != NULL);
ops = purple_connections_get_ui_ops();
@@ -511,45 +449,28 @@ purple_connection_disconnect_cb(gpointer data)
{
PurpleAccount *account;
PurpleConnection *gc;
- char *password;
+ PurpleConnectionPrivate *priv;
account = data;
gc = purple_account_get_connection(account);
+ priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
- if (gc != NULL)
- gc->disconnect_timeout = 0;
+ if (priv != NULL)
+ priv->disconnect_timeout = 0;
- password = g_strdup(purple_account_get_password(account));
purple_account_disconnect(account);
- purple_account_set_password(account, password);
- g_free(password);
-
return FALSE;
}
void
-purple_connection_error(PurpleConnection *gc, const char *text)
-{
- /* prpls that have not been updated to use disconnection reasons will
- * be setting wants_to_die before calling this function, so choose
- * PURPLE_CONNECTION_ERROR_OTHER_ERROR (which is fatal) if it's true,
- * and PURPLE_CONNECTION_ERROR_NETWORK_ERROR (which isn't) if not. See
- * the documentation in connection.h.
- */
- PurpleConnectionError reason = gc->wants_to_die
- ? PURPLE_CONNECTION_ERROR_OTHER_ERROR
- : PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
- purple_connection_error_reason (gc, reason, text);
-}
-
-void
-purple_connection_error_reason (PurpleConnection *gc,
- PurpleConnectionError reason,
- const char *description)
+purple_connection_error (PurpleConnection *gc,
+ PurpleConnectionError reason,
+ const char *description)
{
PurpleConnectionUiOps *ops;
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
- g_return_if_fail(gc != NULL);
+ g_return_if_fail(priv != NULL);
/* 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
@@ -557,42 +478,49 @@ purple_connection_error_reason (PurpleConnection *gc,
*/
if (reason > PURPLE_CONNECTION_ERROR_OTHER_ERROR) {
purple_debug_error("connection",
- "purple_connection_error_reason: reason %u isn't a "
+ "purple_connection_error: reason %u isn't a "
"valid reason\n", reason);
reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
}
if (description == NULL) {
- purple_debug_error("connection", "purple_connection_error_reason called with NULL description\n");
+ purple_debug_error("connection", "purple_connection_error called with NULL description\n");
description = _("Unknown error");
}
/* If we've already got one error, we don't need any more */
- if (gc->disconnect_timeout > 0)
+ if (purple_connection_get_error_info(gc))
return;
- gc->wants_to_die = purple_connection_error_is_fatal (reason);
+ priv->wants_to_die = purple_connection_error_is_fatal (reason);
purple_debug_info("connection", "Connection error on %p (reason: %u description: %s)\n",
gc, reason, description);
ops = purple_connections_get_ui_ops();
- if (ops != NULL)
- {
- if (ops->report_disconnect_reason != NULL)
- ops->report_disconnect_reason (gc, reason, description);
- if (ops->report_disconnect != NULL)
- ops->report_disconnect (gc, description);
- }
+ if (ops && ops->report_disconnect)
+ ops->report_disconnect(gc, reason, description);
+
+ priv->error_info = purple_connection_error_info_new(reason, description);
purple_signal_emit(purple_connections_get_handle(), "connection-error",
gc, reason, description);
- gc->disconnect_timeout = purple_timeout_add(0, purple_connection_disconnect_cb,
+ priv->disconnect_timeout = purple_timeout_add(0, purple_connection_disconnect_cb,
purple_connection_get_account(gc));
}
+PurpleConnectionErrorInfo *
+purple_connection_get_error_info(const PurpleConnection *gc)
+{
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->error_info;
+}
+
void
purple_connection_ssl_error (PurpleConnection *gc,
PurpleSslErrorType ssl_error)
@@ -615,7 +543,7 @@ purple_connection_ssl_error (PurpleConnection *gc,
reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
}
- purple_connection_error_reason (gc, reason,
+ purple_connection_error (gc, reason,
purple_ssl_strerror(ssl_error));
}
@@ -648,16 +576,492 @@ purple_connection_error_is_fatal (PurpleConnectionError reason)
}
}
+void purple_connection_update_last_received(PurpleConnection *gc)
+{
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->last_received = time(NULL);
+}
+
+static PurpleConnectionErrorInfo *
+purple_connection_error_info_new(PurpleConnectionError type,
+ const gchar *description)
+{
+ PurpleConnectionErrorInfo *err;
+
+ g_return_val_if_fail(description != NULL, NULL);
+
+ err = g_new(PurpleConnectionErrorInfo, 1);
+
+ err->type = type;
+ err->description = g_strdup(description);
+
+ return err;
+}
+
+/**************************************************************************
+ * GBoxed code
+ **************************************************************************/
+static PurpleConnectionUiOps *
+purple_connection_ui_ops_copy(PurpleConnectionUiOps *ops)
+{
+ PurpleConnectionUiOps *ops_new;
+
+ g_return_val_if_fail(ops != NULL, NULL);
+
+ ops_new = g_new(PurpleConnectionUiOps, 1);
+ *ops_new = *ops;
+
+ return ops_new;
+}
+
+GType
+purple_connection_ui_ops_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleConnectionUiOps",
+ (GBoxedCopyFunc)purple_connection_ui_ops_copy,
+ (GBoxedFreeFunc)g_free);
+ }
+
+ return type;
+}
+
+static PurpleConnectionErrorInfo *
+purple_connection_error_info_copy(PurpleConnectionErrorInfo *err)
+{
+ g_return_val_if_fail(err != NULL, NULL);
+
+ return purple_connection_error_info_new(err->type, err->description);
+}
+
+static void
+purple_connection_error_info_free(PurpleConnectionErrorInfo *err)
+{
+ g_return_if_fail(err != NULL);
+
+ g_free(err->description);
+ g_free(err);
+}
+
+GType
+purple_connection_error_info_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleConnectionErrorInfo",
+ (GBoxedCopyFunc)purple_connection_error_info_copy,
+ (GBoxedFreeFunc)purple_connection_error_info_free);
+ }
+
+ return type;
+}
+
+/**************************************************************************
+ * GObject code
+ **************************************************************************/
+
+/* Set method for GObject properties */
+static void
+purple_connection_set_property(GObject *obj, guint param_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleConnection *gc = PURPLE_CONNECTION(obj);
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
+
+ switch (param_id) {
+ case PROP_PRPL:
+ priv->prpl = g_value_get_pointer(value);
+ break;
+ case PROP_FLAGS:
+ purple_connection_set_flags(gc, g_value_get_flags(value));
+ break;
+ case PROP_STATE:
+ purple_connection_set_state(gc, g_value_get_enum(value));
+ break;
+ case PROP_ACCOUNT:
+ priv->account = g_value_get_object(value);
+ break;
+ case PROP_PASSWORD:
+ g_free(priv->password);
+ priv->password = g_strdup(g_value_get_string(value));
+ break;
+ case PROP_DISPLAY_NAME:
+ purple_connection_set_display_name(gc, g_value_get_string(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* Get method for GObject properties */
+static void
+purple_connection_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleConnection *gc = PURPLE_CONNECTION(obj);
+
+ switch (param_id) {
+ case PROP_PRPL:
+ g_value_set_pointer(value, purple_connection_get_prpl(gc));
+ break;
+ case PROP_FLAGS:
+ g_value_set_flags(value, purple_connection_get_flags(gc));
+ break;
+ case PROP_STATE:
+ g_value_set_enum(value, purple_connection_get_state(gc));
+ break;
+ case PROP_ACCOUNT:
+ g_value_set_object(value, purple_connection_get_account(gc));
+ break;
+ case PROP_PASSWORD:
+ g_value_set_string(value, purple_connection_get_password(gc));
+ break;
+ case PROP_DISPLAY_NAME:
+ g_value_set_string(value, purple_connection_get_display_name(gc));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* GObject initialization function */
+static void
+purple_connection_init(GTypeInstance *instance, gpointer klass)
+{
+ PurpleConnection *gc = PURPLE_CONNECTION(instance);
+
+ purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTING);
+ connections = g_list_append(connections, gc);
+
+ PURPLE_DBUS_REGISTER_POINTER(gc, PurpleConnection);
+}
+
+/* Called when done constructing */
+static void
+purple_connection_constructed(GObject *object)
+{
+ PurpleConnection *gc = PURPLE_CONNECTION(object);
+ PurpleAccount *account;
+
+ G_OBJECT_CLASS(parent_class)->constructed(object);
+
+ g_object_get(gc, "account", &account, NULL);
+ purple_account_set_connection(account, gc);
+ g_object_unref(account);
+
+ purple_signal_emit(purple_connections_get_handle(), "signing-on", gc);
+}
+
+/* GObject finalize function */
+static void
+purple_connection_finalize(GObject *object)
+{
+ PurpleConnection *gc = PURPLE_CONNECTION(object);
+ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
+ PurpleAccount *account;
+ GSList *buddies;
+ PurplePluginProtocolInfo *prpl_info = NULL;
+ gboolean remove = FALSE;
+
+ priv->is_finalizing = TRUE;
+
+ account = purple_connection_get_account(gc);
+
+ purple_debug_info("connection", "Disconnecting connection %p\n", gc);
+
+ if (purple_connection_get_state(gc) != PURPLE_CONNECTION_CONNECTING)
+ remove = TRUE;
+
+ purple_signal_emit(purple_connections_get_handle(), "signing-off", gc);
+
+ while (priv->active_chats)
+ {
+ PurpleChatConversation *b = priv->active_chats->data;
+
+ priv->active_chats = g_slist_remove(priv->active_chats, b);
+ purple_chat_conversation_leave(b);
+ }
+
+ update_keepalive(gc, FALSE);
+
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(priv->prpl);
+ if (prpl_info->close)
+ (prpl_info->close)(gc);
+
+ /* Clear out the proto data that was freed in the prpl close method */
+ buddies = purple_blist_find_buddies(account, NULL);
+ while (buddies != NULL) {
+ PurpleBuddy *buddy = buddies->data;
+ purple_buddy_set_protocol_data(buddy, NULL);
+ buddies = g_slist_delete_link(buddies, buddies);
+ }
+
+ purple_http_conn_cancel_all(gc);
+ _purple_socket_cancel_with_connection(gc);
+ purple_proxy_connect_cancel_with_handle(gc);
+
+ connections = g_list_remove(connections, gc);
+
+ purple_connection_set_state(gc, PURPLE_CONNECTION_DISCONNECTED);
+
+ if (remove)
+ purple_blist_remove_account(account);
+
+ purple_signal_emit(purple_connections_get_handle(), "signed-off", gc);
+
+ purple_account_request_close_with_account(account);
+ purple_request_close_with_handle(gc);
+ purple_notify_close_with_handle(gc);
+
+ purple_debug_info("connection", "Destroying connection %p\n", gc);
+
+ purple_account_set_connection(account, NULL);
+
+ if (priv->error_info)
+ purple_connection_error_info_free(priv->error_info);
+
+ if (priv->disconnect_timeout > 0)
+ purple_timeout_remove(priv->disconnect_timeout);
+
+ purple_str_wipe(priv->password);
+ g_free(priv->display_name);
+
+ PURPLE_DBUS_UNREGISTER_POINTER(gc);
+
+ G_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+/* Class initializer function */
+static void purple_connection_class_init(PurpleConnectionClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ obj_class->finalize = purple_connection_finalize;
+ obj_class->constructed = purple_connection_constructed;
+
+ /* Setup properties */
+ obj_class->get_property = purple_connection_get_property;
+ obj_class->set_property = purple_connection_set_property;
+
+ g_type_class_add_private(klass, sizeof(PurpleConnectionPrivate));
+
+ properties[PROP_PRPL] = g_param_spec_pointer("prpl", "Protocol plugin",
+ "The prpl that is using the connection.",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_FLAGS] = g_param_spec_flags("flags", "Connection flags",
+ "The flags of the connection.",
+ PURPLE_TYPE_CONNECTION_FLAGS, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_STATE] = g_param_spec_enum("state", "Connection state",
+ "The current state of the connection.",
+ PURPLE_TYPE_CONNECTION_STATE, PURPLE_CONNECTION_DISCONNECTED,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_ACCOUNT] = g_param_spec_object("account", "Account",
+ "The account using the connection.", PURPLE_TYPE_ACCOUNT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_PASSWORD] = g_param_spec_string("password", "Password",
+ "The password used for connection.", NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_DISPLAY_NAME] = g_param_spec_string("display-name",
+ "Display name",
+ "Your name that appears to other people.", NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, PROP_LAST, properties);
+}
+
+GType
+purple_connection_get_type(void)
+{
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleConnectionClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_connection_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleConnection),
+ 0,
+ (GInstanceInitFunc)purple_connection_init,
+ NULL,
+ };
+
+ type = g_type_register_static(G_TYPE_OBJECT, "PurpleConnection",
+ &info, 0);
+ }
+
+ return type;
+}
+
+void
+_purple_connection_new(PurpleAccount *account, gboolean regist, const char *password)
+{
+ PurpleConnection *gc;
+ PurplePlugin *prpl;
+ PurplePluginProtocolInfo *prpl_info;
+
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+
+ if (!purple_account_is_disconnected(account))
+ return;
+
+ prpl = purple_find_prpl(purple_account_get_protocol_id(account));
+
+ if (prpl != NULL)
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+ else {
+ gchar *message;
+
+ message = g_strdup_printf(_("Missing protocol plugin for %s"),
+ purple_account_get_username(account));
+ purple_notify_error(NULL, regist ? _("Registration Error") :
+ _("Connection Error"), message, NULL,
+ purple_request_cpar_from_account(account));
+ g_free(message);
+ return;
+ }
+
+ if (regist)
+ {
+ if (prpl_info->register_user == NULL)
+ return;
+ }
+ else
+ {
+ if (((password == NULL) || (*password == '\0')) &&
+ !(prpl_info->options & OPT_PROTO_NO_PASSWORD) &&
+ !(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL))
+ {
+ purple_debug_error("connection", "Cannot connect to account %s without "
+ "a password.\n", purple_account_get_username(account));
+ return;
+ }
+ }
+
+ gc = g_object_new(PURPLE_TYPE_CONNECTION,
+ "prpl", prpl,
+ "password", password,
+ "account", account,
+ NULL);
+
+ if (regist)
+ {
+ purple_debug_info("connection", "Registering. gc = %p\n", gc);
+
+ /* set this so we don't auto-reconnect after registering */
+ PURPLE_CONNECTION_GET_PRIVATE(gc)->wants_to_die = TRUE;
+
+ prpl_info->register_user(account);
+ }
+ else
+ {
+ purple_debug_info("connection", "Connecting. gc = %p\n", gc);
+
+ purple_signal_emit(purple_accounts_get_handle(), "account-connecting", account);
+ prpl_info->login(account);
+ }
+}
+
+void
+_purple_connection_new_unregister(PurpleAccount *account, const char *password,
+ PurpleAccountUnregistrationCb cb, void *user_data)
+{
+ /* Lots of copy/pasted code to avoid API changes. You might want to integrate that into the previous function when posssible. */
+ PurpleConnection *gc;
+ PurplePlugin *prpl;
+ PurplePluginProtocolInfo *prpl_info;
+
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+
+ prpl = purple_find_prpl(purple_account_get_protocol_id(account));
+
+ if (prpl != NULL)
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+ else {
+ gchar *message;
+
+ message = g_strdup_printf(_("Missing protocol plugin for %s"),
+ purple_account_get_username(account));
+ purple_notify_error(NULL, _("Unregistration Error"), message,
+ NULL, purple_request_cpar_from_account(account));
+ g_free(message);
+ return;
+ }
+
+ if (!purple_account_is_disconnected(account)) {
+ prpl_info->unregister_user(account, cb, user_data);
+ return;
+ }
+
+ if (((password == NULL) || (*password == '\0')) &&
+ !(prpl_info->options & OPT_PROTO_NO_PASSWORD) &&
+ !(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL))
+ {
+ purple_debug_error("connection", "Cannot connect to account %s without "
+ "a password.\n", purple_account_get_username(account));
+ return;
+ }
+
+ gc = g_object_new(PURPLE_TYPE_CONNECTION,
+ "prpl", prpl,
+ "password", password,
+ "account", account,
+ NULL);
+
+ purple_debug_info("connection", "Unregistering. gc = %p\n", gc);
+
+ prpl_info->unregister_user(account, cb, user_data);
+}
+
+/**************************************************************************
+ * Connections API
+ **************************************************************************/
+
+void
+_purple_assert_connection_is_valid(PurpleConnection *gc,
+ const gchar *file, int line)
+{
+ if (gc && g_list_find(purple_connections_get_all(), gc))
+ return;
+
+ purple_debug_fatal("connection", "PURPLE_ASSERT_CONNECTION_IS_VALID(%p)"
+ " failed at %s:%d", gc, file, line);
+ exit(-1);
+}
+
void
purple_connections_disconnect_all(void)
{
GList *l;
PurpleConnection *gc;
+ PurpleConnectionPrivate *priv;
while ((l = purple_connections_get_all()) != NULL) {
gc = l->data;
- gc->wants_to_die = TRUE;
- purple_account_disconnect(gc->account);
+ priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
+ priv->wants_to_die = TRUE;
+ purple_account_disconnect(priv->account);
}
}
@@ -691,36 +1095,29 @@ purple_connections_init(void)
void *handle = purple_connections_get_handle();
purple_signal_register(handle, "signing-on",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONNECTION));
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_CONNECTION);
purple_signal_register(handle, "signed-on",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONNECTION));
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_CONNECTION);
purple_signal_register(handle, "signing-off",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONNECTION));
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_CONNECTION);
purple_signal_register(handle, "signed-off",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONNECTION));
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_CONNECTION);
purple_signal_register(handle, "connection-error",
- purple_marshal_VOID__POINTER_INT_POINTER, NULL, 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONNECTION),
- purple_value_new(PURPLE_TYPE_ENUM),
- purple_value_new(PURPLE_TYPE_STRING));
+ purple_marshal_VOID__POINTER_INT_POINTER,
+ G_TYPE_NONE, 3, PURPLE_TYPE_CONNECTION,
+ PURPLE_TYPE_CONNECTION_ERROR, G_TYPE_STRING);
purple_signal_register(handle, "autojoin",
- purple_marshal_BOOLEAN__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONNECTION));
+ purple_marshal_BOOLEAN__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_CONNECTION);
}
diff --git a/libpurple/connection.h b/libpurple/connection.h
index 1eb7c15173..d79a860a87 100644
--- a/libpurple/connection.h
+++ b/libpurple/connection.h
@@ -1,9 +1,3 @@
-/**
- * @file connection.h Connection API
- * @ingroup core
- * @see @ref connection-signals
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -24,125 +18,176 @@
* 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_CONNECTION_H_
#define _PURPLE_CONNECTION_H_
+/**
+ * SECTION:connection
+ * @section_id: libpurple-connection
+ * @short_description: <filename>connection.h</filename>
+ * @title: Connection API
+ * @see_also: <link linkend="chapter-signals-connection">Connection signals</link>
+ */
-/** @copydoc _PurpleConnection */
-typedef struct _PurpleConnection PurpleConnection;
+#define PURPLE_TYPE_CONNECTION (purple_connection_get_type())
+#define PURPLE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_CONNECTION, PurpleConnection))
+#define PURPLE_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_CONNECTION, PurpleConnectionClass))
+#define PURPLE_IS_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_CONNECTION))
+#define PURPLE_IS_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_CONNECTION))
+#define PURPLE_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_CONNECTION, PurpleConnectionClass))
-/**
+typedef struct _PurpleConnection PurpleConnection;
+typedef struct _PurpleConnectionClass PurpleConnectionClass;
+
+#define PURPLE_TYPE_CONNECTION_UI_OPS (purple_connection_ui_ops_get_type())
+
+typedef struct _PurpleConnectionUiOps PurpleConnectionUiOps;
+
+#define PURPLE_TYPE_CONNECTION_ERROR_INFO (purple_connection_error_info_get_type())
+
+typedef struct _PurpleConnectionErrorInfo PurpleConnectionErrorInfo;
+
+/**
+ * PurpleConnectionFlags:
+ * @PURPLE_CONNECTION_FLAG_HTML: Connection sends/receives in 'HTML'
+ * @PURPLE_CONNECTION_FLAG_NO_BGCOLOR: Connection does not send/receive
+ * background colors
+ * @PURPLE_CONNECTION_FLAG_AUTO_RESP: Send auto responses when away
+ * @PURPLE_CONNECTION_FLAG_FORMATTING_WBFO: The text buffer must be formatted
+ * as a whole
+ * @PURPLE_CONNECTION_FLAG_NO_NEWLINES: No new lines are allowed in outgoing
+ * messages
+ * @PURPLE_CONNECTION_FLAG_NO_FONTSIZE: Connection does not send/receive font
+ * sizes
+ * @PURPLE_CONNECTION_FLAG_NO_URLDESC: Connection does not support descriptions
+ * with links
+ * @PURPLE_CONNECTION_FLAG_NO_IMAGES: Connection does not support sending of
+ * images
+ * @PURPLE_CONNECTION_FLAG_ALLOW_CUSTOM_SMILEY: Connection supports sending
+ * and receiving custom smileys
+ * @PURPLE_CONNECTION_FLAG_SUPPORT_MOODS: Connection supports setting moods
+ * @PURPLE_CONNECTION_FLAG_SUPPORT_MOOD_MESSAGES: Connection supports setting
+ * a message on moods
+ *
* Flags to change behavior of the client for a given connection.
*/
-typedef enum
+typedef enum /*< flags >*/
{
- PURPLE_CONNECTION_HTML = 0x0001, /**< Connection sends/receives in 'HTML'. */
- PURPLE_CONNECTION_NO_BGCOLOR = 0x0002, /**< Connection does not send/receive
- background colors. */
- PURPLE_CONNECTION_AUTO_RESP = 0x0004, /**< Send auto responses when away. */
- PURPLE_CONNECTION_FORMATTING_WBFO = 0x0008, /**< The text buffer must be formatted as a whole */
- PURPLE_CONNECTION_NO_NEWLINES = 0x0010, /**< No new lines are allowed in outgoing messages */
- PURPLE_CONNECTION_NO_FONTSIZE = 0x0020, /**< Connection does not send/receive font sizes */
- PURPLE_CONNECTION_NO_URLDESC = 0x0040, /**< Connection does not support descriptions with links */
- PURPLE_CONNECTION_NO_IMAGES = 0x0080, /**< Connection does not support sending of images */
- PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY = 0x0100, /**< Connection supports sending and receiving custom smileys */
- PURPLE_CONNECTION_SUPPORT_MOODS = 0x0200, /**< Connection supports setting moods */
- PURPLE_CONNECTION_SUPPORT_MOOD_MESSAGES = 0x0400 /**< Connection supports setting a message on moods */
+ PURPLE_CONNECTION_FLAG_HTML = 0x0001,
+ PURPLE_CONNECTION_FLAG_NO_BGCOLOR = 0x0002,
+ PURPLE_CONNECTION_FLAG_AUTO_RESP = 0x0004,
+ PURPLE_CONNECTION_FLAG_FORMATTING_WBFO = 0x0008,
+ PURPLE_CONNECTION_FLAG_NO_NEWLINES = 0x0010,
+ PURPLE_CONNECTION_FLAG_NO_FONTSIZE = 0x0020,
+ PURPLE_CONNECTION_FLAG_NO_URLDESC = 0x0040,
+ PURPLE_CONNECTION_FLAG_NO_IMAGES = 0x0080,
+ PURPLE_CONNECTION_FLAG_ALLOW_CUSTOM_SMILEY = 0x0100,
+ PURPLE_CONNECTION_FLAG_SUPPORT_MOODS = 0x0200,
+ PURPLE_CONNECTION_FLAG_SUPPORT_MOOD_MESSAGES = 0x0400
} PurpleConnectionFlags;
+/**
+ * PurpleConnectionState:
+ * @PURPLE_CONNECTION_DISCONNECTED: Disconnected.
+ * @PURPLE_CONNECTION_CONNECTED: Connected.
+ * @PURPLE_CONNECTION_CONNECTING: Connecting.
+ */
typedef enum
{
- PURPLE_DISCONNECTED = 0, /**< Disconnected. */
- PURPLE_CONNECTED, /**< Connected. */
- PURPLE_CONNECTING /**< Connecting. */
-
+ PURPLE_CONNECTION_DISCONNECTED = 0,
+ PURPLE_CONNECTION_CONNECTED,
+ PURPLE_CONNECTION_CONNECTING
} PurpleConnectionState;
/**
- * Possible errors that can cause a connection to be closed.
+ * PurpleConnectionError:
+ * @PURPLE_CONNECTION_ERROR_NETWORK_ERROR: There was an error sending or
+ * receiving on the network socket, or there was some protocol error
+ * (such as the server sending malformed data).
+ * @PURPLE_CONNECTION_ERROR_INVALID_USERNAME: The username supplied was not
+ * valid.
+ * @PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED: The username, password or
+ * some other credential was incorrect. Use
+ * #PURPLE_CONNECTION_ERROR_INVALID_USERNAME instead if the username
+ * is known to be invalid.
+ * @PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE: libpurple doesn't speak
+ * any of the authentication methods the server offered.
+ * @PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT: libpurple was built without SSL
+ * support, and the connection needs SSL.
+ * @PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR: There was an error negotiating
+ * SSL on this connection, or the server does not support encryption
+ * but an account option was set to require it.
+ * @PURPLE_CONNECTION_ERROR_NAME_IN_USE: Someone is already connected to the
+ * server using the name you are trying to connect with.
+ * @PURPLE_CONNECTION_ERROR_INVALID_SETTINGS: The username/server/other
+ * preference for the account isn't valid. For instance, on IRC the
+ * username cannot contain white space. This reason should not be used
+ * for incorrect passwords etc: use
+ * #PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED for that.
+ * @PURPLE_CONNECTION_ERROR_CERT_NOT_PROVIDED: The server did not provide a
+ * SSL certificate.
+ * @PURPLE_CONNECTION_ERROR_CERT_UNTRUSTED: The server's SSL certificate could
+ * not be trusted.
+ * @PURPLE_CONNECTION_ERROR_CERT_EXPIRED: The server's SSL certificate has
+ * expired.
+ * @PURPLE_CONNECTION_ERROR_CERT_NOT_ACTIVATED: The server's SSL certificate is
+ * not yet valid.
+ * @PURPLE_CONNECTION_ERROR_CERT_HOSTNAME_MISMATCH: The server's SSL
+ * certificate did not match its hostname.
+ * @PURPLE_CONNECTION_ERROR_CERT_FINGERPRINT_MISMATCH: The server's SSL
+ * certificate does not have the expected fingerprint.
+ * @PURPLE_CONNECTION_ERROR_CERT_SELF_SIGNED: The server's SSL certificate is
+ * self-signed.
+ * @PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR: There was some other error
+ * validating the server's SSL certificate.
+ * @PURPLE_CONNECTION_ERROR_OTHER_ERROR: Some other error occurred which fits
+ * into none of the other categories.
*
- * @since 2.3.0
+ * Possible errors that can cause a connection to be closed.
*/
typedef enum
{
- /** There was an error sending or receiving on the network socket, or
- * there was some protocol error (such as the server sending malformed
- * data).
- */
PURPLE_CONNECTION_ERROR_NETWORK_ERROR = 0,
- /** The username supplied was not valid. */
PURPLE_CONNECTION_ERROR_INVALID_USERNAME = 1,
- /** The username, password or some other credential was incorrect. Use
- * #PURPLE_CONNECTION_ERROR_INVALID_USERNAME instead if the username
- * is known to be invalid.
- */
PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED = 2,
- /** libpurple doesn't speak any of the authentication methods the
- * server offered.
- */
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE = 3,
- /** libpurple was built without SSL support, and the connection needs
- * SSL.
- */
PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT = 4,
- /** There was an error negotiating SSL on this connection, or the
- * server does not support encryption but an account option was set to
- * require it.
- */
PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR = 5,
- /** Someone is already connected to the server using the name you are
- * trying to connect with.
- */
PURPLE_CONNECTION_ERROR_NAME_IN_USE = 6,
- /** The username/server/other preference for the account isn't valid.
- * For instance, on IRC the username cannot contain white space.
- * This reason should not be used for incorrect passwords etc: use
- * #PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED for that.
- *
- * @todo This reason really shouldn't be necessary. Usernames and
- * other account preferences should be validated when the
- * account is created.
- */
+ /* TODO This reason really shouldn't be necessary. Usernames and
+ * other account preferences should be validated when the
+ * account is created. */
PURPLE_CONNECTION_ERROR_INVALID_SETTINGS = 7,
- /** The server did not provide a SSL certificate. */
PURPLE_CONNECTION_ERROR_CERT_NOT_PROVIDED = 8,
- /** The server's SSL certificate could not be trusted. */
PURPLE_CONNECTION_ERROR_CERT_UNTRUSTED = 9,
- /** The server's SSL certificate has expired. */
PURPLE_CONNECTION_ERROR_CERT_EXPIRED = 10,
- /** The server's SSL certificate is not yet valid. */
PURPLE_CONNECTION_ERROR_CERT_NOT_ACTIVATED = 11,
- /** The server's SSL certificate did not match its hostname. */
PURPLE_CONNECTION_ERROR_CERT_HOSTNAME_MISMATCH = 12,
- /** The server's SSL certificate does not have the expected
- * fingerprint.
- */
PURPLE_CONNECTION_ERROR_CERT_FINGERPRINT_MISMATCH = 13,
- /** The server's SSL certificate is self-signed. */
PURPLE_CONNECTION_ERROR_CERT_SELF_SIGNED = 14,
- /** There was some other error validating the server's SSL certificate.
- */
PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR = 15,
- /** Some other error occurred which fits into none of the other
- * categories.
- */
- /* purple_connection_error_reason() in connection.c uses the fact that
+ /* purple_connection_error() in connection.c uses the fact that
* this is the last member of the enum when sanity-checking; if other
* reasons are added after it, the check must be updated.
*/
PURPLE_CONNECTION_ERROR_OTHER_ERROR = 16
} PurpleConnectionError;
-/** Holds the type of an error along with its description. */
-typedef struct
+/**
+ * PurpleConnectionErrorInfo:
+ * @type: The type of error.
+ * @description: A localised, human-readable description of the error.
+ *
+ * Holds the type of an error along with its description.
+ */
+struct _PurpleConnectionErrorInfo
{
- /** The type of error. */
PurpleConnectionError type;
- /** A localised, human-readable description of the error. */
char *description;
-} PurpleConnectionErrorInfo;
+};
#include <time.h>
@@ -152,475 +197,431 @@ typedef struct
#include "sslconn.h"
/**
+ * PurpleConnectionUiOps:
+ * @connect_progress: When an account is connecting, this operation is called to
+ * notify the UI of what is happening, as well as which @step
+ * out of @step_count has been reached (which might be
+ * displayed as a progress bar).
+ * <sbr/>See purple_connection_update_progress().
+ * @connected: Called when a connection is established (just before the
+ * <link linkend="connections-signed-on"><literal>"signed-on"</literal></link>
+ * signal).
+ * @disconnected: Called when a connection is ended (between the
+ * <link linkend="connections-signing-off"><literal>"signing-off"</literal></link>
+ * and <link linkend="connections-signed-off"><literal>"signed-off"</literal></link>
+ * signals).
+ * @notice: Used to display connection-specific notices. (Pidgin's Gtk user
+ * interface implements this as a no-op; purple_connection_notice(),
+ * which uses this operation, is not used by any of the protocols
+ * shipped with libpurple.)
+ * @network_connected: Called when libpurple discovers that the computer's
+ * network connection is active. On Linux, this uses
+ * Network Manager if available; on Windows, it uses
+ * Win32's network change notification infrastructure.
+ * @network_disconnected: Called when libpurple discovers that the computer's
+ * network connection has gone away.
+ * @report_disconnect: Called when an error causes a connection to be
+ * disconnected. Called before @disconnected.
+ * <sbr/>See purple_connection_error().
+ * <sbr/>@reason: why the connection ended, if known, or
+ * #PURPLE_CONNECTION_ERROR_OTHER_ERROR, if not.
+ * <sbr/>@text: a localized message describing the
+ * disconnection in more detail to the user.
+ *
* Connection UI operations. Used to notify the user of changes to
* connections, such as being disconnected, and to respond to the
* underlying network connection appearing and disappearing. UIs should
* call #purple_connections_set_ui_ops() with an instance of this struct.
*
- * @see @ref ui-ops
+ * See <link linkend="chapter-ui-ops">List of <literal>UiOps</literal> Structures</link>
*/
-typedef struct
+struct _PurpleConnectionUiOps
{
- /**
- * When an account is connecting, this operation is called to notify
- * the UI of what is happening, as well as which @a step out of @a
- * step_count has been reached (which might be displayed as a progress
- * bar).
- * @see #purple_connection_update_progress
- */
void (*connect_progress)(PurpleConnection *gc,
const char *text,
size_t step,
size_t step_count);
- /**
- * Called when a connection is established (just before the
- * @ref signed-on signal).
- */
void (*connected)(PurpleConnection *gc);
-
- /**
- * Called when a connection is ended (between the @ref signing-off
- * and @ref signed-off signals).
- */
void (*disconnected)(PurpleConnection *gc);
- /**
- * Used to display connection-specific notices. (Pidgin's Gtk user
- * interface implements this as a no-op; #purple_connection_notice(),
- * which uses this operation, is not used by any of the protocols
- * shipped with libpurple.)
- */
void (*notice)(PurpleConnection *gc, const char *text);
- /**
- * Called when an error causes a connection to be disconnected.
- * Called before #disconnected.
- * @param text a localized error message.
- * @see #purple_connection_error
- * @deprecated in favour of
- * #PurpleConnectionUiOps.report_disconnect_reason.
- */
- void (*report_disconnect)(PurpleConnection *gc, const char *text);
-
- /**
- * Called when libpurple discovers that the computer's network
- * connection is active. On Linux, this uses Network Manager if
- * available; on Windows, it uses Win32's network change notification
- * infrastructure.
- */
void (*network_connected)(void);
-
- /**
- * Called when libpurple discovers that the computer's network
- * connection has gone away.
- */
void (*network_disconnected)(void);
- /**
- * Called when an error causes a connection to be disconnected.
- * Called before #disconnected. This op is intended to replace
- * #report_disconnect. If both are implemented, this will be called
- * first; however, there's no real reason to implement both.
- *
- * @param reason why the connection ended, if known, or
- * #PURPLE_CONNECTION_ERROR_OTHER_ERROR, if not.
- * @param text a localized message describing the disconnection
- * in more detail to the user.
- * @see #purple_connection_error_reason
- *
- * @since 2.3.0
- */
- void (*report_disconnect_reason)(PurpleConnection *gc,
- PurpleConnectionError reason,
- const char *text);
+ void (*report_disconnect)(PurpleConnection *gc,
+ PurpleConnectionError reason,
+ const char *text);
+ /*< private >*/
void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
-} PurpleConnectionUiOps;
-
+ void (*_purple_reserved4)(void);
+};
-/* Represents an active connection on an account. */
+/**
+ * PurpleConnection:
+ *
+ * Represents an active connection on an account.
+ */
struct _PurpleConnection
{
- PurplePlugin *prpl; /**< The protocol plugin. */
- PurpleConnectionFlags flags; /**< Connection flags. */
-
- PurpleConnectionState state; /**< The connection state. */
-
- PurpleAccount *account; /**< The account being connected to. */
- char *password; /**< The password used. */
- int inpa; /**< The input watcher. */
-
- GSList *buddy_chats; /**< A list of active chats
- (#PurpleConversation structs of type
- #PURPLE_CONV_TYPE_CHAT). */
- void *proto_data; /**< Protocol-specific data. */
-
- char *display_name; /**< How you appear to other people. */
- guint keepalive; /**< Keep-alive. */
+ GObject gparent;
+};
- /** Wants to Die state. This is set when the user chooses to log out, or
- * when the protocol is disconnected and should not be automatically
- * reconnected (incorrect password, etc.). prpls should rely on
- * purple_connection_error_reason() to set this for them rather than
- * setting it themselves.
- * @see purple_connection_error_is_fatal
- */
- gboolean wants_to_die;
+/**
+ * PurpleConnectionClass:
+ *
+ * Base class for all #PurpleConnection's
+ */
+struct _PurpleConnectionClass {
+ GObjectClass parent_class;
- guint disconnect_timeout; /**< Timer used for nasty stack tricks */
- time_t last_received; /**< When we last received a packet. Set by the
- prpl to avoid sending unneeded keepalives */
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
};
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name Connection API */
+/* Connection API */
/**************************************************************************/
-/*@{*/
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_CONNECTION_C_)
/**
- * This function should only be called by purple_account_connect()
- * in account.c. If you're trying to sign on an account, use that
- * function instead.
- *
- * Creates a connection to the specified account and either connects
- * or attempts to register a new account. If you are logging in,
- * the connection uses the current active status for this account.
- * So if you want to sign on as "away," for example, you need to
- * have called purple_account_set_status(account, "away").
- * (And this will call purple_account_connect() automatically).
+ * purple_connection_get_type:
*
- * @param account The account the connection should be connecting to.
- * @param regist Whether we are registering a new account or just
- * trying to do a normal signon.
- * @param password The password to use.
- *
- * @deprecated As this is internal, we should make it private in 3.0.0.
+ * Returns: The #GType for the Connection object.
*/
-void purple_connection_new(PurpleAccount *account, gboolean regist,
- const char *password);
-#endif
+GType purple_connection_get_type(void);
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_CONNECTION_C_)
/**
- * This function should only be called by purple_account_unregister()
- * in account.c.
- *
- * Tries to unregister the account on the server. If the account is not
- * connected, also creates a new connection.
+ * purple_connection_error_info_get_type:
*
- * @param account The account to unregister
- * @param password The password to use.
- * @param cb Optional callback to be called when unregistration is complete
- * @param user_data user data to pass to the callback
- *
- * @deprecated As this is internal, we should make it private in 3.0.0.
+ * Returns: The #GType for the #PurpleConnectionErrorInfo boxed structure.
*/
-void purple_connection_new_unregister(PurpleAccount *account, const char *password, PurpleAccountUnregistrationCb cb, void *user_data);
-#endif
+GType purple_connection_error_info_get_type(void);
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_CONNECTION_C_)
/**
- * Disconnects and destroys a PurpleConnection.
- *
- * This function should only be called by purple_account_disconnect()
- * in account.c. If you're trying to sign off an account, use that
- * function instead.
+ * purple_connection_set_state:
+ * @gc: The connection.
+ * @state: The connection state.
*
- * @param gc The purple connection to destroy.
- *
- * @deprecated As this is internal, we should make it private in 3.0.0.
- */
-void purple_connection_destroy(PurpleConnection *gc);
-#endif
-
-/**
- * Sets the connection state. PRPLs should call this and pass in
- * the state #PURPLE_CONNECTED when the account is completely
+ * Sets the connection state. Protocols should call this and pass in
+ * the state #PURPLE_CONNECTION_CONNECTED when the account is completely
* signed on. What does it mean to be completely signed on? If
- * the core can call prpl->set_status, and it successfully changes
+ * the core can call protocol's set_status, and it successfully changes
* your status, then the account is online.
- *
- * @param gc The connection.
- * @param state The connection state.
*/
void purple_connection_set_state(PurpleConnection *gc, PurpleConnectionState state);
/**
- * Sets the connection's account.
+ * purple_connection_set_flags:
+ * @gc: The connection.
+ * @flags: The flags.
*
- * @param gc The connection.
- * @param account The account.
+ * Sets the connection flags.
*/
-void purple_connection_set_account(PurpleConnection *gc, PurpleAccount *account);
+void purple_connection_set_flags(PurpleConnection *gc, PurpleConnectionFlags flags);
/**
- * Sets the connection's displayed name.
+ * purple_connection_set_display_name:
+ * @gc: The connection.
+ * @name: The displayed name.
*
- * @param gc The connection.
- * @param name The displayed name.
+ * Sets the connection's displayed name.
*/
void purple_connection_set_display_name(PurpleConnection *gc, const char *name);
/**
- * Sets the protocol data for a connection.
- *
- * @param connection The PurpleConnection.
- * @param proto_data The protocol data to set for the connection.
+ * purple_connection_set_protocol_data:
+ * @connection: The PurpleConnection.
+ * @proto_data: The protocol data to set for the connection.
*
- * @since 2.6.0
+ * Sets the protocol data for a connection.
*/
void purple_connection_set_protocol_data(PurpleConnection *connection, void *proto_data);
/**
- * Returns the connection state.
+ * purple_connection_get_state:
+ * @gc: The connection.
*
- * @param gc The connection.
+ * Returns the connection state.
*
- * @return The connection state.
+ * Returns: The connection state.
*/
PurpleConnectionState purple_connection_get_state(const PurpleConnection *gc);
/**
+ * purple_connection_get_flags:
+ * @gc: The connection.
+ *
+ * Returns the connection flags.
+ *
+ * Returns: The connection flags.
+ */
+PurpleConnectionFlags purple_connection_get_flags(const PurpleConnection *gc);
+
+/**
+ * PURPLE_CONNECTION_IS_CONNECTED:
+ *
* Returns TRUE if the account is connected, otherwise returns FALSE.
*
- * @return TRUE if the account is connected, otherwise returns FALSE.
+ * Returns: TRUE if the account is connected, otherwise returns FALSE.
*/
#define PURPLE_CONNECTION_IS_CONNECTED(gc) \
- (purple_connection_get_state(gc) == PURPLE_CONNECTED)
+ (purple_connection_get_state(gc) == PURPLE_CONNECTION_CONNECTED)
/**
- * Returns the connection's account.
- *
+ * purple_connection_is_disconnecting:
* @param gc The connection.
*
- * @return The connection's account.
+ * Checks, if connection is in disconnecting state.
+ *
+ * Returns: %TRUE, if the account is disconnecting.
+ */
+gboolean
+purple_connection_is_disconnecting(const PurpleConnection *gc);
+
+/**
+ * purple_connection_get_account:
+ * @gc: The connection.
+ *
+ * Returns the connection's account.
+ *
+ * Returns: The connection's account.
*/
PurpleAccount *purple_connection_get_account(const PurpleConnection *gc);
/**
+ * purple_connection_get_prpl:
+ * @gc: The connection.
+ *
* Returns the protocol plugin managing a connection.
*
- * @param gc The connection.
+ * Returns: The protocol plugin.
+ */
+PurplePlugin *purple_connection_get_prpl(const PurpleConnection *gc);
+
+/**
+ * purple_connection_get_password:
+ * @gc: The connection.
*
- * @return The protocol plugin.
+ * Returns the connection's password.
*
- * @since 2.4.0
+ * Returns: The connection's password.
*/
-PurplePlugin * purple_connection_get_prpl(const PurpleConnection *gc);
+const char *purple_connection_get_password(const PurpleConnection *gc);
/**
- * Returns the connection's password.
+ * purple_connection_get_active_chats:
+ * @gc: The connection.
*
- * @param gc The connection.
+ * Returns a list of active chat conversations on a connection.
*
- * @return The connection's password.
+ * Returns: The active chats on the connection.
*/
-const char *purple_connection_get_password(const PurpleConnection *gc);
+GSList *purple_connection_get_active_chats(const PurpleConnection *gc);
/**
- * Returns the connection's displayed name.
+ * purple_connection_get_display_name:
+ * @gc: The connection.
*
- * @param gc The connection.
+ * Returns the connection's displayed name.
*
- * @return The connection's displayed name.
+ * Returns: The connection's displayed name.
*/
const char *purple_connection_get_display_name(const PurpleConnection *gc);
/**
- * Gets the protocol data from a connection.
+ * purple_connection_get_protocol_data:
+ * @gc: The PurpleConnection.
*
- * @param connection The PurpleConnection.
- *
- * @return The protocol data for the connection.
+ * Gets the protocol data from a connection.
*
- * @since 2.6.0
+ * Returns: The protocol data for the connection.
*/
-void *purple_connection_get_protocol_data(const PurpleConnection *connection);
+void *purple_connection_get_protocol_data(const PurpleConnection *gc);
/**
- * Updates the connection progress.
+ * purple_connection_update_progress:
+ * @gc: The connection.
+ * @text: Information on the current step.
+ * @step: The current step.
+ * @count: The total number of steps.
*
- * @param gc The connection.
- * @param text Information on the current step.
- * @param step The current step.
- * @param count The total number of steps.
+ * Updates the connection progress.
*/
void purple_connection_update_progress(PurpleConnection *gc, const char *text,
size_t step, size_t count);
/**
- * Displays a connection-specific notice.
+ * purple_connection_notice:
+ * @gc: The connection.
+ * @text: The notice text.
*
- * @param gc The connection.
- * @param text The notice text.
+ * Displays a connection-specific notice.
*/
void purple_connection_notice(PurpleConnection *gc, const char *text);
/**
- * Closes a connection with an error.
+ * purple_connection_error:
+ * @gc: the connection which is closing.
+ * @reason: why the connection is closing.
+ * @description: a localized description of the error (not %NULL ).
*
- * @param gc The connection.
- * @param reason The error text, which may not be @c NULL.
- * @deprecated in favour of #purple_connection_error_reason. Calling
- * @c purple_connection_error(gc, text) is equivalent to calling
- * @c purple_connection_error_reason(gc, reason, text) where @c reason is
- * #PURPLE_CONNECTION_ERROR_OTHER_ERROR if @c gc->wants_to_die is @c TRUE, and
- * #PURPLE_CONNECTION_ERROR_NETWORK_ERROR if not. (This is to keep
- * auto-reconnection behaviour the same when using old prpls which don't use
- * reasons yet.)
+ * Closes a connection with an error and a human-readable description of the
+ * error.
*/
-void purple_connection_error(PurpleConnection *gc, const char *reason);
+void
+purple_connection_error(PurpleConnection *gc,
+ PurpleConnectionError reason,
+ const char *description);
/**
- * Closes a connection with an error and a human-readable description of the
- * error. It also sets @c gc->wants_to_die to the value of
- * #purple_connection_error_is_fatal(@a reason), mainly for
- * backwards-compatibility.
+ * purple_connection_get_error_info:
+ * @gc: The connection.
*
- * @param gc the connection which is closing.
- * @param reason why the connection is closing.
- * @param description a non-@c NULL localized description of the error.
+ * Returns the #PurpleConnectionErrorInfo instance of a connection if an
+ * error exists.
*
- * @since 2.3.0
+ * Returns: The #PurpleConnectionErrorInfo instance of the connection if an
+ * error exists, %NULL otherwise.
*/
-void
-purple_connection_error_reason (PurpleConnection *gc,
- PurpleConnectionError reason,
- const char *description);
+PurpleConnectionErrorInfo *
+purple_connection_get_error_info(const PurpleConnection *gc);
/**
+ * purple_connection_ssl_error:
+ *
* Closes a connection due to an SSL error; this is basically a shortcut to
* turning the #PurpleSslErrorType into a #PurpleConnectionError and a
- * human-readable string and then calling purple_connection_error_reason().
- *
- * @since 2.3.0
+ * human-readable string and then calling purple_connection_error().
*/
void
purple_connection_ssl_error (PurpleConnection *gc,
PurpleSslErrorType ssl_error);
/**
+ * purple_connection_error_is_fatal:
+ *
* Reports whether a disconnection reason is fatal (in which case the account
* should probably not be automatically reconnected) or transient (so
* auto-reconnection is a good idea).
* For instance, #PURPLE_CONNECTION_ERROR_NETWORK_ERROR is a temporary error,
- * which might be caused by losing the network connection, so <tt>
- * purple_connection_error_is_fatal (PURPLE_CONNECTION_ERROR_NETWORK_ERROR)</tt>
- * is @c FALSE. On the other hand,
+ * which might be caused by losing the network connection, so <literal>
+ * purple_connection_error_is_fatal (PURPLE_CONNECTION_ERROR_NETWORK_ERROR)</literal>
+ * is %FALSE. On the other hand,
* #PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED probably indicates a
* misconfiguration of the account which needs the user to go fix it up, so
- * <tt> purple_connection_error_is_fatal
- * (PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED)</tt> is @c TRUE.
- *
- * (This function is meant to replace checking PurpleConnection.wants_to_die.)
+ * <literal> purple_connection_error_is_fatal
+ * (PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED)</literal> is %TRUE.
*
- * @return @c TRUE if the account should not be automatically reconnected, and
- * @c FALSE otherwise.
- *
- * @since 2.3.0
+ * Returns: %TRUE if the account should not be automatically reconnected, and
+ * %FALSE otherwise.
*/
gboolean
purple_connection_error_is_fatal (PurpleConnectionError reason);
-/*@}*/
+/**
+ * purple_connection_update_last_received:
+ * @gc: The connection.
+ *
+ * Indicate that a packet was received on the connection.
+ * Set by the protocol to avoid sending unneeded keepalives.
+ */
+void purple_connection_update_last_received(PurpleConnection *gc);
/**************************************************************************/
-/** @name Connections API */
+/* Connections API */
/**************************************************************************/
-/*@{*/
/**
+ * purple_connections_disconnect_all:
+ *
* Disconnects from all connections.
*/
void purple_connections_disconnect_all(void);
/**
+ * purple_connections_get_all:
+ *
* Returns a list of all active connections. This does not
* include connections that are in the process of connecting.
*
- * @constreturn A list of all active connections.
+ * Returns: (transfer none): A list of all active connections.
*/
GList *purple_connections_get_all(void);
/**
+ * purple_connections_get_connecting:
+ *
* Returns a list of all connections in the process of connecting.
*
- * @constreturn A list of connecting connections.
+ * Returns: (transfer none): A list of connecting connections.
*/
GList *purple_connections_get_connecting(void);
+/**************************************************************************/
+/* UI Registration Functions */
+/**************************************************************************/
+
/**
- * Checks if gc is still a valid pointer to a gc.
- *
- * @return @c TRUE if gc is valid.
+ * purple_connection_ui_ops_get_type:
*
- * @deprecated Do not use this. Instead, cancel your asynchronous request
- * when the PurpleConnection is destroyed.
+ * Returns: The #GType for the #PurpleConnectionUiOps boxed structure.
*/
-/*
- * TODO: Eventually this bad boy will be removed, because it is
- * a gross fix for a crashy problem.
- */
-#define PURPLE_CONNECTION_IS_VALID(gc) (g_list_find(purple_connections_get_all(), (gc)) != NULL)
-
-/*@}*/
-
-/**************************************************************************/
-/** @name UI Registration Functions */
-/**************************************************************************/
-/*@{*/
+GType purple_connection_ui_ops_get_type(void);
/**
- * Sets the UI operations structure to be used for connections.
+ * purple_connections_set_ui_ops:
+ * @ops: The UI operations structure.
*
- * @param ops The UI operations structure.
+ * Sets the UI operations structure to be used for connections.
*/
void purple_connections_set_ui_ops(PurpleConnectionUiOps *ops);
/**
+ * purple_connections_get_ui_ops:
+ *
* Returns the UI operations structure used for connections.
*
- * @return The UI operations structure in use.
+ * Returns: The UI operations structure in use.
*/
PurpleConnectionUiOps *purple_connections_get_ui_ops(void);
-/*@}*/
-
/**************************************************************************/
-/** @name Connections Subsystem */
+/* Connections Subsystem */
/**************************************************************************/
-/*@{*/
/**
+ * purple_connections_init:
+ *
* Initializes the connections subsystem.
*/
void purple_connections_init(void);
/**
+ * purple_connections_uninit:
+ *
* Uninitializes the connections subsystem.
*/
void purple_connections_uninit(void);
/**
+ * purple_connections_get_handle:
+ *
* Returns the handle to the connections subsystem.
*
- * @return The connections subsystem handle.
+ * Returns: The connections subsystem handle.
*/
void *purple_connections_get_handle(void);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_CONNECTION_H_ */
diff --git a/libpurple/conversation.c b/libpurple/conversation.c
index 051ab481b5..6f6e7db63f 100644
--- a/libpurple/conversation.c
+++ b/libpurple/conversation.c
@@ -20,138 +20,87 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#include "internal.h"
-#include "blist.h"
+#include "glibcompat.h"
+
+#include "buddylist.h"
#include "cmds.h"
#include "conversation.h"
#include "dbus-maybe.h"
#include "debug.h"
-#include "imgstore.h"
+#include "enums.h"
#include "notify.h"
#include "prefs.h"
#include "prpl.h"
#include "request.h"
#include "signals.h"
+#include "smiley-list.h"
#include "util.h"
-#define SEND_TYPED_TIMEOUT_SECONDS 5
-
-static GList *conversations = NULL;
-static GList *ims = NULL;
-static GList *chats = NULL;
-static PurpleConversationUiOps *default_ops = NULL;
+#define PURPLE_CONVERSATION_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_CONVERSATION, PurpleConversationPrivate))
-/**
- * A hash table used for efficient lookups of conversations by name.
- * struct _purple_hconv => PurpleConversation*
- */
-static GHashTable *conversation_cache = NULL;
+typedef struct _PurpleConversationPrivate PurpleConversationPrivate;
-struct _purple_hconv {
- PurpleConversationType type;
- char *name;
- const PurpleAccount *account;
-};
-
-static guint _purple_conversations_hconv_hash(struct _purple_hconv *hc)
+/* General private data for a conversation */
+struct _PurpleConversationPrivate
{
- return g_str_hash(hc->name) ^ hc->type ^ g_direct_hash(hc->account);
-}
+ PurpleAccount *account; /* The user using this conversation. */
-static guint _purple_conversations_hconv_equal(struct _purple_hconv *hc1, struct _purple_hconv *hc2)
-{
- return (hc1->type == hc2->type &&
- hc1->account == hc2->account &&
- g_str_equal(hc1->name, hc2->name));
-}
+ char *name; /* The name of the conversation. */
+ char *title; /* The window title. */
-static void _purple_conversations_hconv_free_key(struct _purple_hconv *hc)
-{
- g_free(hc->name);
- g_free(hc);
-}
+ gboolean logging; /* The status of logging. */
-static guint _purple_conversation_user_hash(gconstpointer data)
-{
- const gchar *name = data;
- gchar *collated;
- guint hash;
-
- collated = g_utf8_collate_key(name, -1);
- hash = g_str_hash(collated);
- g_free(collated);
- return hash;
-}
+ GList *logs; /* This conversation's logs */
-static gboolean _purple_conversation_user_equal(gconstpointer a, gconstpointer b)
-{
- return !g_utf8_collate(a, b);
-}
-
-void
-purple_conversations_set_ui_ops(PurpleConversationUiOps *ops)
-{
- default_ops = ops;
-}
-
-static gboolean
-reset_typing_cb(gpointer data)
-{
- PurpleConversation *c = (PurpleConversation *)data;
- PurpleConvIm *im;
+ PurpleConversationUiOps *ui_ops; /* UI-specific operations. */
- im = PURPLE_CONV_IM(c);
+ PurpleConnectionFlags features; /* The supported features */
+ GList *message_history; /* Message history, as a GList of PurpleMessages */
- purple_conv_im_set_typing_state(im, PURPLE_NOT_TYPING);
- purple_conv_im_stop_typing_timeout(im);
+ PurpleE2eeState *e2ee_state; /* End-to-end encryption state. */
- return FALSE;
-}
+ /* The list of remote smileys. This should be per-buddy (PurpleBuddy),
+ * but we don't have any class for people not on our buddy
+ * list (PurpleDude?). So, if we have one, we should switch to it. */
+ PurpleSmileyList *remote_smileys;
+};
-static gboolean
-send_typed_cb(gpointer data)
+/* GObject Property enums */
+enum
{
- PurpleConversation *conv = (PurpleConversation *)data;
- PurpleConnection *gc;
- const char *name;
-
- g_return_val_if_fail(conv != NULL, FALSE);
-
- gc = purple_conversation_get_gc(conv);
- name = purple_conversation_get_name(conv);
-
- if (gc != NULL && name != NULL) {
- /* We set this to 1 so that PURPLE_TYPING will be sent
- * if the Purple user types anything else.
- */
- purple_conv_im_set_type_again(PURPLE_CONV_IM(conv), 1);
-
- serv_send_typing(gc, name, PURPLE_TYPED);
-
- purple_debug(PURPLE_DEBUG_MISC, "conversation", "typed...\n");
- }
+ PROP_0,
+ PROP_ACCOUNT,
+ PROP_NAME,
+ PROP_TITLE,
+ PROP_LOGGING,
+ PROP_FEATURES,
+ PROP_LAST
+};
- return FALSE;
-}
+static GObjectClass *parent_class;
+static GParamSpec *properties[PROP_LAST];
static void
common_send(PurpleConversation *conv, const char *message, PurpleMessageFlags msgflags)
{
- PurpleConversationType type;
PurpleAccount *account;
PurpleConnection *gc;
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
+ PurpleMessage *msg;
char *displayed = NULL, *sent = NULL;
int err = 0;
+ g_return_if_fail(priv != NULL);
+
if (*message == '\0')
return;
account = purple_conversation_get_account(conv);
- gc = purple_conversation_get_gc(conv);
-
- g_return_if_fail(account != NULL);
- g_return_if_fail(gc != NULL);
+ g_return_if_fail(PURPLE_IS_ACCOUNT(account));
- type = purple_conversation_get_type(conv);
+ gc = purple_account_get_connection(account);
+ g_return_if_fail(PURPLE_IS_CONNECTION(gc));
/* Always linkfy the text for display, unless we're
* explicitly asked to do otheriwse*/
@@ -162,7 +111,7 @@ common_send(PurpleConversation *conv, const char *message, PurpleMessageFlags ms
displayed = purple_markup_linkify(message);
}
- if (displayed && (conv->features & PURPLE_CONNECTION_HTML) &&
+ if (displayed && (priv->features & PURPLE_CONNECTION_FLAG_HTML) &&
!(msgflags & PURPLE_MESSAGE_RAW)) {
sent = g_strdup(displayed);
} else
@@ -170,37 +119,37 @@ common_send(PurpleConversation *conv, const char *message, PurpleMessageFlags ms
msgflags |= PURPLE_MESSAGE_SEND;
- if (type == PURPLE_CONV_TYPE_IM) {
- PurpleConvIm *im = PURPLE_CONV_IM(conv);
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
+ msg = purple_message_new_outgoing(
+ purple_conversation_get_name(conv), sent, msgflags);
purple_signal_emit(purple_conversations_get_handle(), "sending-im-msg",
- account,
- purple_conversation_get_name(conv), &sent);
+ account, msg);
- if (sent != NULL && sent[0] != '\0') {
+ if (!purple_message_is_empty(msg)) {
- err = serv_send_im(gc, purple_conversation_get_name(conv),
- sent, msgflags);
+ err = purple_serv_send_im(gc, msg);
if ((err > 0) && (displayed != NULL))
- purple_conv_im_write(im, NULL, displayed, msgflags, time(NULL));
+ purple_conversation_write_message(conv, msg);
- purple_signal_emit(purple_conversations_get_handle(), "sent-im-msg",
- account,
- purple_conversation_get_name(conv), sent);
+ purple_signal_emit(purple_conversations_get_handle(),
+ "sent-im-msg", account, msg);
}
}
- else {
- purple_signal_emit(purple_conversations_get_handle(), "sending-chat-msg",
- account, &sent,
- purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)));
+ else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
+ int id = purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv));
+
+ msg = purple_message_new_outgoing(NULL, sent, msgflags);
+
+ purple_signal_emit(purple_conversations_get_handle(),
+ "sending-chat-msg", account, msg, id);
- if (sent != NULL && sent[0] != '\0') {
- err = serv_chat_send(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), sent, msgflags);
+ if (!purple_message_is_empty(msg)) {
+ err = purple_serv_chat_send(gc, id, msg);
- purple_signal_emit(purple_conversations_get_handle(), "sent-chat-msg",
- account, sent,
- purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)));
+ purple_signal_emit(purple_conversations_get_handle(),
+ "sent-chat-msg", account, msg, id);
}
}
@@ -213,9 +162,11 @@ common_send(PurpleConversation *conv, const char *message, PurpleMessageFlags ms
if (err == -E2BIG) {
msg = _("Unable to send message: The message is too large.");
- if (!purple_conv_present_error(who, account, msg)) {
+ if (!purple_conversation_present_error(who, account, msg)) {
char *msg2 = g_strdup_printf(_("Unable to send message to %s."), who);
- purple_notify_error(gc, NULL, msg2, _("The message is too large."));
+ purple_notify_error(gc, NULL, msg2,
+ _("The message is too large."),
+ purple_request_cpar_from_connection(gc));
g_free(msg2);
}
}
@@ -226,9 +177,10 @@ common_send(PurpleConversation *conv, const char *message, PurpleMessageFlags ms
else {
msg = _("Unable to send message.");
- if (!purple_conv_present_error(who, account, msg)) {
+ if (!purple_conversation_present_error(who, account, msg)) {
char *msg2 = g_strdup_printf(_("Unable to send message to %s."), who);
- purple_notify_error(gc, NULL, msg2, NULL);
+ purple_notify_error(gc, NULL, msg2, NULL,
+ purple_request_cpar_from_connection(gc));
g_free(msg2);
}
}
@@ -241,491 +193,189 @@ common_send(PurpleConversation *conv, const char *message, PurpleMessageFlags ms
static void
open_log(PurpleConversation *conv)
{
- conv->logs = g_list_append(NULL, purple_log_new(conv->type == PURPLE_CONV_TYPE_CHAT ? PURPLE_LOG_CHAT :
- PURPLE_LOG_IM, conv->name, conv->account,
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
+
+ g_return_if_fail(priv != NULL);
+
+ 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, time(NULL), NULL));
}
-/* Functions that deal with PurpleConvMessage */
+/* Functions that deal with PurpleMessage history */
static void
-add_message_to_history(PurpleConversation *conv, const char *who, const char *alias,
- const char *message, PurpleMessageFlags flags, time_t when)
+add_message_to_history(PurpleConversation *conv, PurpleMessage *msg)
{
- PurpleConvMessage *msg;
- PurpleConnection *gc;
-
- gc = purple_account_get_connection(conv->account);
-
- if (flags & PURPLE_MESSAGE_SEND) {
- const char *me = NULL;
- if (gc)
- me = purple_connection_get_display_name(gc);
- if (!me)
- me = conv->account->username;
- who = me;
- }
-
- msg = g_new0(PurpleConvMessage, 1);
- PURPLE_DBUS_REGISTER_POINTER(msg, PurpleConvMessage);
- msg->who = g_strdup(who);
- msg->alias = g_strdup(alias);
- msg->flags = flags;
- msg->what = g_strdup(message);
- msg->when = when;
- msg->conv = conv;
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
- conv->message_history = g_list_prepend(conv->message_history, msg);
-}
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(msg != NULL);
-static void
-free_conv_message(PurpleConvMessage *msg)
-{
- g_free(msg->who);
- g_free(msg->alias);
- g_free(msg->what);
- PURPLE_DBUS_UNREGISTER_POINTER(msg);
- g_free(msg);
-}
-
-static void
-message_history_free(GList *list)
-{
- g_list_foreach(list, (GFunc)free_conv_message, NULL);
- g_list_free(list);
+ g_object_ref(msg);
+ priv->message_history = g_list_prepend(priv->message_history, msg);
}
/**************************************************************************
* Conversation API
**************************************************************************/
-static void
-purple_conversation_chat_cleanup_for_rejoin(PurpleConversation *conv)
-{
- const char *disp;
- PurpleAccount *account;
- PurpleConnection *gc;
-
- account = purple_conversation_get_account(conv);
-
- purple_conversation_close_logs(conv);
- open_log(conv);
-
- gc = purple_account_get_connection(account);
-
- if ((disp = purple_connection_get_display_name(gc)) != NULL)
- purple_conv_chat_set_nick(PURPLE_CONV_CHAT(conv), disp);
- else
- {
- purple_conv_chat_set_nick(PURPLE_CONV_CHAT(conv),
- purple_account_get_username(account));
- }
-
- purple_conv_chat_clear_users(PURPLE_CONV_CHAT(conv));
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, NULL);
- PURPLE_CONV_CHAT(conv)->left = FALSE;
-
- purple_conversation_update(conv, PURPLE_CONV_UPDATE_CHATLEFT);
-}
-
-PurpleConversation *
-purple_conversation_new(PurpleConversationType type, PurpleAccount *account,
- const char *name)
-{
- PurpleConversation *conv;
- PurpleConnection *gc;
- PurpleConversationUiOps *ops;
- struct _purple_hconv *hc;
-
- g_return_val_if_fail(type != PURPLE_CONV_TYPE_UNKNOWN, NULL);
- g_return_val_if_fail(account != NULL, NULL);
- g_return_val_if_fail(name != NULL, NULL);
-
- /* Check if this conversation already exists. */
- if ((conv = purple_find_conversation_with_account(type, name, account)) != NULL)
- {
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT &&
- !purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv))) {
- purple_debug_warning("conversation", "Trying to create multiple "
- "chats (%s) with the same name is deprecated and will be "
- "removed in libpurple 3.0.0", name);
- }
-
- /*
- * This hack is necessary because some prpls (MSN) have unnamed chats
- * that all use the same name. A PurpleConversation for one of those
- * is only ever re-used if the user has left, so calls to
- * purple_conversation_new need to fall-through to creating a new
- * chat.
- * TODO 3.0.0: Remove this workaround and mandate unique names.
- */
- if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_CHAT ||
- purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)))
- {
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
- purple_conversation_chat_cleanup_for_rejoin(conv);
-
- return conv;
- }
- }
-
- gc = purple_account_get_connection(account);
- g_return_val_if_fail(gc != NULL, NULL);
-
- conv = g_new0(PurpleConversation, 1);
- PURPLE_DBUS_REGISTER_POINTER(conv, PurpleConversation);
-
- conv->type = type;
- conv->account = account;
- conv->name = g_strdup(name);
- conv->title = g_strdup(name);
- conv->data = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, NULL);
- /* copy features from the connection. */
- conv->features = gc->flags;
-
- if (type == PURPLE_CONV_TYPE_IM)
- {
- PurpleBuddyIcon *icon;
- conv->u.im = g_new0(PurpleConvIm, 1);
- conv->u.im->conv = conv;
- PURPLE_DBUS_REGISTER_POINTER(conv->u.im, PurpleConvIm);
-
- ims = g_list_prepend(ims, conv);
- if ((icon = purple_buddy_icons_find(account, name)))
- {
- purple_conv_im_set_icon(conv->u.im, icon);
- /* purple_conv_im_set_icon refs the icon. */
- purple_buddy_icon_unref(icon);
- }
-
- if (purple_prefs_get_bool("/purple/logging/log_ims"))
- {
- purple_conversation_set_logging(conv, TRUE);
- open_log(conv);
- }
- }
- else if (type == PURPLE_CONV_TYPE_CHAT)
- {
- const char *disp;
-
- conv->u.chat = g_new0(PurpleConvChat, 1);
- conv->u.chat->conv = conv;
- conv->u.chat->users = g_hash_table_new_full(_purple_conversation_user_hash,
- _purple_conversation_user_equal, g_free, NULL);
- PURPLE_DBUS_REGISTER_POINTER(conv->u.chat, PurpleConvChat);
-
- chats = g_list_prepend(chats, conv);
-
- if ((disp = purple_connection_get_display_name(account->gc)))
- purple_conv_chat_set_nick(conv->u.chat, disp);
- else
- purple_conv_chat_set_nick(conv->u.chat,
- purple_account_get_username(account));
-
- if (purple_prefs_get_bool("/purple/logging/log_chats"))
- {
- purple_conversation_set_logging(conv, TRUE);
- open_log(conv);
- }
- }
-
- conversations = g_list_prepend(conversations, conv);
-
- hc = g_new(struct _purple_hconv, 1);
- hc->name = g_strdup(purple_normalize(account, conv->name));
- hc->account = account;
- hc->type = type;
-
- g_hash_table_insert(conversation_cache, hc, conv);
-
- /* Auto-set the title. */
- purple_conversation_autoset_title(conv);
-
- /* Don't move this.. it needs to be one of the last things done otherwise
- * it causes mysterious crashes on my system.
- * -- Gary
- */
- ops = conv->ui_ops = default_ops;
- if (ops != NULL && ops->create_conversation != NULL)
- ops->create_conversation(conv);
-
- purple_signal_emit(purple_conversations_get_handle(),
- "conversation-created", conv);
-
- return conv;
-}
-
-void
-purple_conversation_destroy(PurpleConversation *conv)
-{
- PurplePluginProtocolInfo *prpl_info = NULL;
- PurpleConversationUiOps *ops;
- PurpleConnection *gc;
- const char *name;
- struct _purple_hconv hc;
-
- g_return_if_fail(conv != NULL);
-
- purple_request_close_with_handle(conv);
-
- ops = purple_conversation_get_ui_ops(conv);
- gc = purple_conversation_get_gc(conv);
- name = purple_conversation_get_name(conv);
-
- if (gc != NULL)
- {
- /* Still connected */
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
-
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
- {
- if (purple_prefs_get_bool("/purple/conversations/im/send_typing"))
- serv_send_typing(gc, name, PURPLE_NOT_TYPING);
-
- if (gc && prpl_info->convo_closed != NULL)
- prpl_info->convo_closed(gc, name);
- }
- else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
- {
- int chat_id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv));
-#if 0
- /*
- * This is unfortunately necessary, because calling
- * serv_chat_leave() calls this purple_conversation_destroy(),
- * which leads to two calls here.. We can't just return after
- * this, because then it'll return on the next pass. So, since
- * serv_got_chat_left(), which is eventually called from the
- * prpl that serv_chat_leave() calls, removes this conversation
- * from the gc's buddy_chats list, we're going to check to see
- * if this exists in the list. If so, we want to return after
- * calling this, because it'll be called again. If not, fall
- * through, because it'll have already been removed, and we'd
- * be on the 2nd pass.
- *
- * Long paragraph. <-- Short sentence.
- *
- * -- ChipX86
- */
-
- if (gc && g_slist_find(gc->buddy_chats, conv) != NULL) {
- serv_chat_leave(gc, chat_id);
-
- return;
- }
-#endif
- /*
- * Instead of all of that, lets just close the window when
- * the user tells us to, and let the prpl deal with the
- * internals on it's own time. Don't do this if the prpl already
- * knows it left the chat.
- */
- if (!purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)))
- serv_chat_leave(gc, chat_id);
-
- /*
- * If they didn't call serv_got_chat_left by now, it's too late.
- * So we better do it for them before we destroy the thing.
- */
- if (!purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)))
- serv_got_chat_left(gc, chat_id);
- }
- }
-
- /* remove from conversations and im/chats lists prior to emit */
- conversations = g_list_remove(conversations, conv);
-
- if(conv->type==PURPLE_CONV_TYPE_IM)
- ims = g_list_remove(ims, conv);
- else if(conv->type==PURPLE_CONV_TYPE_CHAT)
- chats = g_list_remove(chats, conv);
-
- hc.name = (gchar *)purple_normalize(conv->account, conv->name);
- hc.account = conv->account;
- hc.type = conv->type;
-
- g_hash_table_remove(conversation_cache, &hc);
-
- purple_signal_emit(purple_conversations_get_handle(),
- "deleting-conversation", conv);
-
- g_free(conv->name);
- g_free(conv->title);
-
- conv->name = NULL;
- conv->title = NULL;
-
- if (conv->type == PURPLE_CONV_TYPE_IM) {
- purple_conv_im_stop_typing_timeout(conv->u.im);
- purple_conv_im_stop_send_typed_timeout(conv->u.im);
-
- purple_buddy_icon_unref(conv->u.im->icon);
- conv->u.im->icon = NULL;
-
- PURPLE_DBUS_UNREGISTER_POINTER(conv->u.im);
- g_free(conv->u.im);
- conv->u.im = NULL;
- }
- else if (conv->type == PURPLE_CONV_TYPE_CHAT) {
- g_hash_table_destroy(conv->u.chat->users);
- conv->u.chat->users = NULL;
-
- g_list_foreach(conv->u.chat->in_room, (GFunc)purple_conv_chat_cb_destroy, NULL);
- g_list_free(conv->u.chat->in_room);
-
- g_list_foreach(conv->u.chat->ignored, (GFunc)g_free, NULL);
- g_list_free(conv->u.chat->ignored);
-
- conv->u.chat->in_room = NULL;
- conv->u.chat->ignored = NULL;
-
- g_free(conv->u.chat->who);
- conv->u.chat->who = NULL;
-
- g_free(conv->u.chat->topic);
- conv->u.chat->topic = NULL;
-
- g_free(conv->u.chat->nick);
-
- PURPLE_DBUS_UNREGISTER_POINTER(conv->u.chat);
- g_free(conv->u.chat);
- conv->u.chat = NULL;
- }
-
- g_hash_table_destroy(conv->data);
- conv->data = NULL;
-
- if (ops != NULL && ops->destroy_conversation != NULL)
- ops->destroy_conversation(conv);
- conv->ui_data = NULL;
-
- purple_conversation_close_logs(conv);
-
- purple_conversation_clear_message_history(conv);
-
- PURPLE_DBUS_UNREGISTER_POINTER(conv);
- g_free(conv);
- conv = NULL;
-}
-
-
void
purple_conversation_present(PurpleConversation *conv) {
PurpleConversationUiOps *ops;
- g_return_if_fail(conv != NULL);
+ g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
ops = purple_conversation_get_ui_ops(conv);
if(ops && ops->present)
ops->present(conv);
}
-
void
purple_conversation_set_features(PurpleConversation *conv, PurpleConnectionFlags features)
{
- g_return_if_fail(conv != NULL);
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
- conv->features = features;
+ g_return_if_fail(priv != NULL);
- purple_conversation_update(conv, PURPLE_CONV_UPDATE_FEATURES);
-}
+ priv->features = features;
+
+ g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_FEATURES]);
+ purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_FEATURES);
+}
PurpleConnectionFlags
purple_conversation_get_features(PurpleConversation *conv)
{
- g_return_val_if_fail(conv != NULL, 0);
- return conv->features;
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return priv->features;
}
+static PurpleConversationUiOps *
+purple_conversation_ui_ops_copy(PurpleConversationUiOps *ops)
+{
+ PurpleConversationUiOps *ops_new;
+
+ g_return_val_if_fail(ops != NULL, NULL);
+
+ ops_new = g_new(PurpleConversationUiOps, 1);
+ *ops_new = *ops;
-PurpleConversationType
-purple_conversation_get_type(const PurpleConversation *conv)
+ return ops_new;
+}
+
+GType
+purple_conversation_ui_ops_get_type(void)
{
- g_return_val_if_fail(conv != NULL, PURPLE_CONV_TYPE_UNKNOWN);
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleConversationUiOps",
+ (GBoxedCopyFunc)purple_conversation_ui_ops_copy,
+ (GBoxedFreeFunc)g_free);
+ }
- return conv->type;
+ return type;
}
void
purple_conversation_set_ui_ops(PurpleConversation *conv,
PurpleConversationUiOps *ops)
{
- g_return_if_fail(conv != NULL);
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
- if (conv->ui_ops == ops)
- return;
+ g_return_if_fail(priv != NULL);
- if (conv->ui_ops != NULL && conv->ui_ops->destroy_conversation != NULL)
- conv->ui_ops->destroy_conversation(conv);
+ if (priv->ui_ops == ops)
+ return;
- conv->ui_data = NULL;
+ if (priv->ui_ops != NULL && priv->ui_ops->destroy_conversation != NULL)
+ priv->ui_ops->destroy_conversation(conv);
- conv->ui_ops = ops;
+ priv->ui_ops = ops;
}
PurpleConversationUiOps *
purple_conversation_get_ui_ops(const PurpleConversation *conv)
{
- g_return_val_if_fail(conv != NULL, NULL);
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
- return conv->ui_ops;
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->ui_ops;
}
void
purple_conversation_set_account(PurpleConversation *conv, PurpleAccount *account)
{
- g_return_if_fail(conv != NULL);
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
+
+ g_return_if_fail(priv != NULL);
if (account == purple_conversation_get_account(conv))
return;
- conv->account = account;
+ _purple_conversations_update_cache(conv, NULL, account);
+ priv->account = account;
+
+ g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_ACCOUNT]);
- purple_conversation_update(conv, PURPLE_CONV_UPDATE_ACCOUNT);
+ purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_ACCOUNT);
}
PurpleAccount *
purple_conversation_get_account(const PurpleConversation *conv)
{
- g_return_val_if_fail(conv != NULL, NULL);
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
- return conv->account;
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->account;
}
PurpleConnection *
-purple_conversation_get_gc(const PurpleConversation *conv)
+purple_conversation_get_connection(const PurpleConversation *conv)
{
PurpleAccount *account;
- g_return_val_if_fail(conv != NULL, NULL);
+ g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
account = purple_conversation_get_account(conv);
if (account == NULL)
return NULL;
- return account->gc;
+ return purple_account_get_connection(account);
}
void
purple_conversation_set_title(PurpleConversation *conv, const char *title)
{
- g_return_if_fail(conv != NULL);
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
+
+ g_return_if_fail(priv != NULL);
g_return_if_fail(title != NULL);
- g_free(conv->title);
- conv->title = g_strdup(title);
+ g_free(priv->title);
+ priv->title = g_strdup(title);
+
+ if (!g_object_get_data(G_OBJECT(conv), "is-finalizing"))
+ g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_TITLE]);
- purple_conversation_update(conv, PURPLE_CONV_UPDATE_TITLE);
+ purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_TITLE);
}
const char *
purple_conversation_get_title(const PurpleConversation *conv)
{
- g_return_val_if_fail(conv != NULL, NULL);
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
- return conv->title;
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->title;
}
void
@@ -736,20 +386,19 @@ purple_conversation_autoset_title(PurpleConversation *conv)
PurpleChat *chat;
const char *text = NULL, *name;
- g_return_if_fail(conv != NULL);
+ g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
account = purple_conversation_get_account(conv);
name = purple_conversation_get_name(conv);
- if(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
- if(account && ((b = purple_find_buddy(account, name)) != NULL))
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
+ if (account && ((b = purple_blist_find_buddy(account, name)) != NULL))
text = purple_buddy_get_contact_alias(b);
- } else if(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
- if(account && ((chat = purple_blist_find_chat(account, name)) != NULL))
+ } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
+ if (account && ((chat = purple_blist_find_chat(account, name)) != NULL))
text = purple_chat_get_name(chat);
}
-
if(text == NULL)
text = name;
@@ -757,525 +406,322 @@ purple_conversation_autoset_title(PurpleConversation *conv)
}
void
-purple_conversation_foreach(void (*func)(PurpleConversation *conv))
-{
- PurpleConversation *conv;
- GList *l;
-
- g_return_if_fail(func != NULL);
-
- for (l = purple_get_conversations(); l != NULL; l = l->next) {
- conv = (PurpleConversation *)l->data;
-
- func(conv);
- }
-}
-
-void
purple_conversation_set_name(PurpleConversation *conv, const char *name)
{
- struct _purple_hconv *hc;
- g_return_if_fail(conv != NULL);
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
+
+ g_return_if_fail(priv != NULL);
- hc = g_new(struct _purple_hconv, 1);
- hc->type = conv->type;
- hc->account = conv->account;
- hc->name = (gchar *)purple_normalize(conv->account, conv->name);
+ _purple_conversations_update_cache(conv, name, NULL);
- g_hash_table_remove(conversation_cache, hc);
- g_free(conv->name);
+ g_free(priv->name);
+ priv->name = g_strdup(name);
- conv->name = g_strdup(name);
- hc->name = g_strdup(purple_normalize(conv->account, conv->name));
- g_hash_table_insert(conversation_cache, hc, conv);
+ g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_NAME]);
purple_conversation_autoset_title(conv);
+ purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_NAME);
}
const char *
purple_conversation_get_name(const PurpleConversation *conv)
{
- g_return_val_if_fail(conv != NULL, NULL);
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
+
+ g_return_val_if_fail(priv != NULL, NULL);
- return conv->name;
+ return priv->name;
}
void
-purple_conversation_set_logging(PurpleConversation *conv, gboolean log)
+purple_conversation_set_e2ee_state(PurpleConversation *conv,
+ PurpleE2eeState *state)
{
- g_return_if_fail(conv != NULL);
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
+
+ g_return_if_fail(priv != NULL);
- if (conv->logging != log)
+ if (state != NULL && purple_e2ee_state_get_provider(state) !=
+ purple_e2ee_provider_get_main())
{
- conv->logging = log;
- purple_conversation_update(conv, PURPLE_CONV_UPDATE_LOGGING);
+ purple_debug_error("conversation",
+ "This is not the main e2ee provider");
+
+ return;
}
-}
-gboolean
-purple_conversation_is_logging(const PurpleConversation *conv)
-{
- g_return_val_if_fail(conv != NULL, FALSE);
+ if (state == priv->e2ee_state)
+ return;
- return conv->logging;
-}
+ if (state)
+ purple_e2ee_state_ref(state);
+ purple_e2ee_state_unref(priv->e2ee_state);
+ priv->e2ee_state = state;
-void
-purple_conversation_close_logs(PurpleConversation *conv)
-{
- g_return_if_fail(conv != NULL);
-
- g_list_foreach(conv->logs, (GFunc)purple_log_free, NULL);
- g_list_free(conv->logs);
- conv->logs = NULL;
+ purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_E2EE);
}
-PurpleConvIm *
-purple_conversation_get_im_data(const PurpleConversation *conv)
+PurpleE2eeState *
+purple_conversation_get_e2ee_state(PurpleConversation *conv)
{
- g_return_val_if_fail(conv != NULL, NULL);
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
+ PurpleE2eeProvider *provider;
- if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_IM)
- return NULL;
+ g_return_val_if_fail(priv != NULL, NULL);
- return conv->u.im;
-}
+ if (priv->e2ee_state == NULL)
+ return NULL;
-PurpleConvChat *
-purple_conversation_get_chat_data(const PurpleConversation *conv)
-{
- g_return_val_if_fail(conv != NULL, NULL);
+ provider = purple_e2ee_provider_get_main();
+ if (provider == NULL)
+ return NULL;
- if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_CHAT)
+ if (purple_e2ee_state_get_provider(priv->e2ee_state) != provider) {
+ purple_debug_warning("conversation",
+ "e2ee state has invalid provider set");
return NULL;
+ }
- return conv->u.chat;
+ return priv->e2ee_state;
}
void
-purple_conversation_set_data(PurpleConversation *conv, const char *key,
- gpointer data)
+purple_conversation_set_logging(PurpleConversation *conv, gboolean log)
{
- g_return_if_fail(conv != NULL);
- g_return_if_fail(key != NULL);
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
- g_hash_table_replace(conv->data, g_strdup(key), data);
-}
+ g_return_if_fail(priv != NULL);
-gpointer
-purple_conversation_get_data(PurpleConversation *conv, const char *key)
-{
- g_return_val_if_fail(conv != NULL, NULL);
- g_return_val_if_fail(key != NULL, NULL);
+ if (priv->logging != log)
+ {
+ priv->logging = log;
+ if (log && priv->logs == NULL)
+ open_log(conv);
- return g_hash_table_lookup(conv->data, key);
-}
+ g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_LOGGING]);
-GList *
-purple_get_conversations(void)
-{
- return conversations;
+ purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_LOGGING);
+ }
}
-GList *
-purple_get_ims(void)
+gboolean
+purple_conversation_is_logging(const PurpleConversation *conv)
{
- return ims;
-}
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
-GList *
-purple_get_chats(void)
-{
- return chats;
-}
+ g_return_val_if_fail(priv != NULL, FALSE);
+ return priv->logging;
+}
-PurpleConversation *
-purple_find_conversation_with_account(PurpleConversationType type,
- const char *name,
- const PurpleAccount *account)
+void
+purple_conversation_close_logs(PurpleConversation *conv)
{
- PurpleConversation *c = NULL;
- struct _purple_hconv hc;
-
- g_return_val_if_fail(name != NULL, NULL);
-
- hc.name = (gchar *)purple_normalize(account, name);
- hc.account = account;
- hc.type = type;
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
- switch (type) {
- case PURPLE_CONV_TYPE_IM:
- case PURPLE_CONV_TYPE_CHAT:
- c = g_hash_table_lookup(conversation_cache, &hc);
- break;
- case PURPLE_CONV_TYPE_ANY:
- hc.type = PURPLE_CONV_TYPE_IM;
- c = g_hash_table_lookup(conversation_cache, &hc);
- if (!c) {
- hc.type = PURPLE_CONV_TYPE_CHAT;
- c = g_hash_table_lookup(conversation_cache, &hc);
- }
- break;
- default:
- g_return_val_if_reached(NULL);
- }
+ g_return_if_fail(priv != NULL);
- return c;
+ g_list_foreach(priv->logs, (GFunc)purple_log_free, NULL);
+ g_list_free(priv->logs);
+ priv->logs = NULL;
}
void
-purple_conversation_write(PurpleConversation *conv, const char *who,
- const char *message, PurpleMessageFlags flags,
- time_t mtime)
+_purple_conversation_write_common(PurpleConversation *conv, PurpleMessage *pmsg)
{
PurplePluginProtocolInfo *prpl_info = NULL;
PurpleConnection *gc = NULL;
PurpleAccount *account;
PurpleConversationUiOps *ops;
- const char *alias;
- char *displayed = NULL;
PurpleBuddy *b;
int plugin_return;
- PurpleConversationType type;
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
/* int logging_font_options = 0; */
- g_return_if_fail(conv != NULL);
- g_return_if_fail(message != NULL);
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(pmsg != NULL);
ops = purple_conversation_get_ui_ops(conv);
account = purple_conversation_get_account(conv);
- type = purple_conversation_get_type(conv);
if (account != NULL)
gc = purple_account_get_connection(account);
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT &&
- (gc != NULL && !g_slist_find(gc->buddy_chats, conv)))
+ if (PURPLE_IS_CHAT_CONVERSATION(conv) &&
+ (gc != NULL && !g_slist_find(purple_connection_get_active_chats(gc), conv)))
return;
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM &&
- !g_list_find(purple_get_conversations(), conv))
+ if (PURPLE_IS_IM_CONVERSATION(conv) &&
+ !g_list_find(purple_conversations_get_all(), conv))
return;
- displayed = g_strdup(message);
+ plugin_return = GPOINTER_TO_INT(purple_signal_emit_return_1(
+ purple_conversations_get_handle(),
+ (PURPLE_IS_IM_CONVERSATION(conv) ? "writing-im-msg" : "writing-chat-msg"),
+ conv, pmsg));
- if (who == NULL || *who == '\0')
- who = purple_conversation_get_name(conv);
- alias = who;
-
- plugin_return =
- GPOINTER_TO_INT(purple_signal_emit_return_1(
- purple_conversations_get_handle(),
- (type == PURPLE_CONV_TYPE_IM ? "writing-im-msg" : "writing-chat-msg"),
- account, who, &displayed, conv, flags));
-
- if (displayed == NULL)
+ if (purple_message_is_empty(pmsg))
return;
- if (plugin_return) {
- g_free(displayed);
+ if (plugin_return)
return;
- }
if (account != NULL) {
prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(account)));
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM ||
+ if (PURPLE_IS_IM_CONVERSATION(conv) ||
!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
- if (flags & PURPLE_MESSAGE_SEND) {
- b = purple_find_buddy(account,
- purple_account_get_username(account));
+ if (purple_message_get_flags(pmsg) & PURPLE_MESSAGE_SEND) {
+ const gchar *alias;
+
+ b = purple_blist_find_buddy(account,
+ purple_account_get_username(account));
- if (purple_account_get_alias(account) != NULL)
- alias = account->alias;
- else if (b != NULL && !purple_strequal(purple_buddy_get_name(b), purple_buddy_get_contact_alias(b)))
+ if (purple_account_get_private_alias(account) != NULL)
+ alias = purple_account_get_private_alias(account);
+ else if (b != NULL && !purple_strequal(purple_buddy_get_name(b),
+ purple_buddy_get_contact_alias(b)))
+ {
alias = purple_buddy_get_contact_alias(b);
- else if (purple_connection_get_display_name(gc) != NULL)
+ } else if (purple_connection_get_display_name(gc) != NULL)
alias = purple_connection_get_display_name(gc);
else
alias = purple_account_get_username(account);
+
+ purple_message_set_author_alias(pmsg, alias);
}
- else
+ else if (purple_message_get_flags(pmsg) & PURPLE_MESSAGE_RECV)
{
- b = purple_find_buddy(account, who);
+ /* TODO: PurpleDude - folks not on the buddy list */
+ b = purple_blist_find_buddy(account,
+ purple_message_get_author(pmsg));
- if (b != NULL)
- alias = purple_buddy_get_contact_alias(b);
+ if (b != NULL) {
+ purple_message_set_author_alias(pmsg,
+ purple_buddy_get_contact_alias(b));
+ }
}
}
}
- if (!(flags & PURPLE_MESSAGE_NO_LOG) && purple_conversation_is_logging(conv)) {
+ if (!(purple_message_get_flags(pmsg) & PURPLE_MESSAGE_NO_LOG) && purple_conversation_is_logging(conv)) {
GList *log;
- if (conv->logs == NULL)
- open_log(conv);
-
- log = conv->logs;
+ log = priv->logs;
while (log != NULL) {
- purple_log_write((PurpleLog *)log->data, flags, alias, mtime, displayed);
+ purple_log_write((PurpleLog *)log->data,
+ purple_message_get_flags(pmsg),
+ purple_message_get_author_alias(pmsg),
+ purple_message_get_time(pmsg),
+ purple_message_get_contents(pmsg));
log = log->next;
}
}
- if (ops && ops->write_conv)
- ops->write_conv(conv, who, alias, displayed, flags, mtime);
-
- add_message_to_history(conv, who, alias, message, flags, mtime);
-
- purple_signal_emit(purple_conversations_get_handle(),
- (type == PURPLE_CONV_TYPE_IM ? "wrote-im-msg" : "wrote-chat-msg"),
- account, who, displayed, conv, flags);
-
- g_free(displayed);
-}
-
-gboolean
-purple_conversation_has_focus(PurpleConversation *conv)
-{
- gboolean ret = FALSE;
- PurpleConversationUiOps *ops;
-
- g_return_val_if_fail(conv != NULL, FALSE);
-
- ops = purple_conversation_get_ui_ops(conv);
-
- if (ops != NULL && ops->has_focus != NULL)
- ret = ops->has_focus(conv);
-
- return ret;
-}
-
-/*
- * TODO: Need to make sure calls to this function happen in the core
- * instead of the UI. That way UIs have less work to do, and the
- * core/UI split is cleaner. Also need to make sure this is called
- * when chats are added/removed from the blist.
- */
-void
-purple_conversation_update(PurpleConversation *conv, PurpleConvUpdateType type)
-{
- g_return_if_fail(conv != NULL);
-
- purple_signal_emit(purple_conversations_get_handle(),
- "conversation-updated", conv, type);
-}
-
-/**************************************************************************
- * IM Conversation API
- **************************************************************************/
-PurpleConversation *
-purple_conv_im_get_conversation(const PurpleConvIm *im)
-{
- g_return_val_if_fail(im != NULL, NULL);
-
- return im->conv;
-}
-
-void
-purple_conv_im_set_icon(PurpleConvIm *im, PurpleBuddyIcon *icon)
-{
- g_return_if_fail(im != NULL);
-
- if (im->icon != icon)
- {
- purple_buddy_icon_unref(im->icon);
-
- im->icon = (icon == NULL ? NULL : purple_buddy_icon_ref(icon));
- }
-
- purple_conversation_update(purple_conv_im_get_conversation(im),
- PURPLE_CONV_UPDATE_ICON);
-}
-
-PurpleBuddyIcon *
-purple_conv_im_get_icon(const PurpleConvIm *im)
-{
- g_return_val_if_fail(im != NULL, NULL);
-
- return im->icon;
-}
-
-void
-purple_conv_im_set_typing_state(PurpleConvIm *im, PurpleTypingState state)
-{
- g_return_if_fail(im != NULL);
-
- if (im->typing_state != state)
- {
- im->typing_state = state;
-
- switch (state)
- {
- case PURPLE_TYPING:
- purple_signal_emit(purple_conversations_get_handle(),
- "buddy-typing", im->conv->account, im->conv->name);
- break;
- case PURPLE_TYPED:
- purple_signal_emit(purple_conversations_get_handle(),
- "buddy-typed", im->conv->account, im->conv->name);
- break;
- case PURPLE_NOT_TYPING:
- purple_signal_emit(purple_conversations_get_handle(),
- "buddy-typing-stopped", im->conv->account, im->conv->name);
- break;
- }
-
- purple_conv_im_update_typing(im);
+ if (ops) {
+ if (PURPLE_IS_CHAT_CONVERSATION(conv) && ops->write_chat)
+ ops->write_chat(PURPLE_CHAT_CONVERSATION(conv), pmsg);
+ else if (PURPLE_IS_IM_CONVERSATION(conv) && ops->write_im)
+ ops->write_im(PURPLE_IM_CONVERSATION(conv), pmsg);
+ else if (ops->write_conv)
+ ops->write_conv(conv, pmsg);
}
-}
-PurpleTypingState
-purple_conv_im_get_typing_state(const PurpleConvIm *im)
-{
- g_return_val_if_fail(im != NULL, 0);
+ add_message_to_history(conv, pmsg);
- return im->typing_state;
+ purple_signal_emit(purple_conversations_get_handle(),
+ (PURPLE_IS_IM_CONVERSATION(conv) ? "wrote-im-msg" : "wrote-chat-msg"),
+ conv, pmsg);
}
void
-purple_conv_im_start_typing_timeout(PurpleConvIm *im, int timeout)
+purple_conversation_write_message(PurpleConversation *conv, PurpleMessage *msg)
{
- PurpleConversation *conv;
-
- g_return_if_fail(im != NULL);
-
- if (im->typing_timeout > 0)
- purple_conv_im_stop_typing_timeout(im);
-
- conv = purple_conv_im_get_conversation(im);
-
- im->typing_timeout = purple_timeout_add_seconds(timeout, reset_typing_cb, conv);
-}
+ PurpleConversationClass *klass = NULL;
-void
-purple_conv_im_stop_typing_timeout(PurpleConvIm *im)
-{
- g_return_if_fail(im != NULL);
+ g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
- if (im->typing_timeout == 0)
- return;
+ klass = PURPLE_CONVERSATION_GET_CLASS(conv);
- purple_timeout_remove(im->typing_timeout);
- im->typing_timeout = 0;
+ if (klass && klass->write_message)
+ klass->write_message(conv, msg);
}
-guint
-purple_conv_im_get_typing_timeout(const PurpleConvIm *im)
+void purple_conversation_write_system_message(PurpleConversation *conv,
+ const gchar *message, PurpleMessageFlags flags)
{
- g_return_val_if_fail(im != NULL, 0);
-
- return im->typing_timeout;
+ _purple_conversation_write_common(conv,
+ purple_message_new_system(message, flags));
}
void
-purple_conv_im_set_type_again(PurpleConvIm *im, unsigned int val)
+purple_conversation_send(PurpleConversation *conv, const char *message)
{
- g_return_if_fail(im != NULL);
-
- if (val == 0)
- im->type_again = 0;
- else
- im->type_again = time(NULL) + val;
-}
-
-time_t
-purple_conv_im_get_type_again(const PurpleConvIm *im)
-{
- g_return_val_if_fail(im != NULL, 0);
-
- return im->type_again;
+ purple_conversation_send_with_flags(conv, message, 0);
}
void
-purple_conv_im_start_send_typed_timeout(PurpleConvIm *im)
+purple_conversation_send_with_flags(PurpleConversation *conv, const char *message,
+ PurpleMessageFlags flags)
{
- g_return_if_fail(im != NULL);
+ g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
+ g_return_if_fail(message != NULL);
- im->send_typed_timeout = purple_timeout_add_seconds(SEND_TYPED_TIMEOUT_SECONDS,
- send_typed_cb,
- purple_conv_im_get_conversation(im));
+ common_send(conv, message, flags);
}
-void
-purple_conv_im_stop_send_typed_timeout(PurpleConvIm *im)
+gboolean
+purple_conversation_has_focus(PurpleConversation *conv)
{
- g_return_if_fail(im != NULL);
-
- if (im->send_typed_timeout == 0)
- return;
-
- purple_timeout_remove(im->send_typed_timeout);
- im->send_typed_timeout = 0;
-}
+ gboolean ret = FALSE;
+ PurpleConversationUiOps *ops;
-guint
-purple_conv_im_get_send_typed_timeout(const PurpleConvIm *im)
-{
- g_return_val_if_fail(im != NULL, 0);
+ g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), FALSE);
- return im->send_typed_timeout;
-}
+ ops = purple_conversation_get_ui_ops(conv);
-void
-purple_conv_im_update_typing(PurpleConvIm *im)
-{
- g_return_if_fail(im != NULL);
+ if (ops != NULL && ops->has_focus != NULL)
+ ret = ops->has_focus(conv);
- purple_conversation_update(purple_conv_im_get_conversation(im),
- PURPLE_CONV_UPDATE_TYPING);
+ return ret;
}
+/*
+ * TODO: Need to make sure calls to this function happen in the core
+ * instead of the UI. That way UIs have less work to do, and the
+ * core/UI split is cleaner. Also need to make sure this is called
+ * when chats are added/removed from the blist.
+ */
void
-purple_conv_im_write(PurpleConvIm *im, const char *who, const char *message,
- PurpleMessageFlags flags, time_t mtime)
+purple_conversation_update(PurpleConversation *conv, PurpleConversationUpdateType type)
{
- PurpleConversation *c;
-
- g_return_if_fail(im != NULL);
- g_return_if_fail(message != NULL);
-
- c = purple_conv_im_get_conversation(im);
-
- if ((flags & PURPLE_MESSAGE_RECV) == PURPLE_MESSAGE_RECV) {
- purple_conv_im_set_typing_state(im, PURPLE_NOT_TYPING);
- }
+ g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
- /* Pass this on to either the ops structure or the default write func. */
- if (c->ui_ops != NULL && c->ui_ops->write_im != NULL)
- c->ui_ops->write_im(c, who, message, flags, mtime);
- else
- purple_conversation_write(c, who, message, flags, mtime);
+ purple_signal_emit(purple_conversations_get_handle(),
+ "conversation-updated", conv, type);
}
-gboolean purple_conv_present_error(const char *who, PurpleAccount *account, const char *what)
+gboolean purple_conversation_present_error(const char *who, PurpleAccount *account, const char *what)
{
PurpleConversation *conv;
g_return_val_if_fail(who != NULL, FALSE);
- g_return_val_if_fail(account !=NULL, FALSE);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
g_return_val_if_fail(what != NULL, FALSE);
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, account);
+ conv = purple_conversations_find_with_account(who, account);
if (conv != NULL)
- purple_conversation_write(conv, NULL, what, PURPLE_MESSAGE_ERROR, time(NULL));
+ purple_conversation_write_system_message(conv, what, PURPLE_MESSAGE_ERROR);
else
return FALSE;
return TRUE;
}
-void
-purple_conv_im_send(PurpleConvIm *im, const char *message)
-{
- purple_conv_im_send_with_flags(im, message, 0);
-}
-
static void
-purple_conv_send_confirm_cb(gpointer *data)
+purple_conversation_send_confirm_cb(gpointer *data)
{
PurpleConversation *conv = data[0];
char *message = data[1];
@@ -1285,17 +731,18 @@ purple_conv_send_confirm_cb(gpointer *data)
}
void
-purple_conv_send_confirm(PurpleConversation *conv, const char *message)
+purple_conversation_send_confirm(PurpleConversation *conv, const char *message)
{
char *text;
gpointer *data;
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
- g_return_if_fail(conv != NULL);
+ g_return_if_fail(priv != NULL);
g_return_if_fail(message != NULL);
- if (conv->ui_ops != NULL && conv->ui_ops->send_confirm != NULL)
+ if (priv->ui_ops != NULL && priv->ui_ops->send_confirm != NULL)
{
- conv->ui_ops->send_confirm(conv, message);
+ priv->ui_ops->send_confirm(conv, message);
return;
}
@@ -1305,1380 +752,369 @@ purple_conv_send_confirm(PurpleConversation *conv, const char *message)
data[1] = (gpointer)message;
purple_request_action(conv, NULL, _("Send Message"), text, 0,
- purple_conversation_get_account(conv), NULL, conv,
- data, 2,
- _("_Send Message"), G_CALLBACK(purple_conv_send_confirm_cb),
- _("Cancel"), NULL);
-}
-
-void
-purple_conv_im_send_with_flags(PurpleConvIm *im, const char *message, PurpleMessageFlags flags)
-{
- g_return_if_fail(im != NULL);
- g_return_if_fail(message != NULL);
-
- common_send(purple_conv_im_get_conversation(im), message, flags);
-}
-
-gboolean
-purple_conv_custom_smiley_add(PurpleConversation *conv, const char *smile,
- const char *cksum_type, const char *chksum,
- gboolean remote)
-{
- if (conv == NULL || smile == NULL || !*smile) {
- return FALSE;
- }
-
- /* TODO: check if the icon is in the cache and return false if so */
- /* TODO: add an icon cache (that doesn't suck) */
- if (conv->ui_ops != NULL && conv->ui_ops->custom_smiley_add !=NULL) {
- return conv->ui_ops->custom_smiley_add(conv, smile, remote);
- } else {
- purple_debug_info("conversation", "Could not find add custom smiley function");
- return FALSE;
- }
-
-}
-
-void
-purple_conv_custom_smiley_write(PurpleConversation *conv, const char *smile,
- const guchar *data, gsize size)
-{
- g_return_if_fail(conv != NULL);
- g_return_if_fail(smile != NULL && *smile);
-
- if (conv->ui_ops != NULL && conv->ui_ops->custom_smiley_write != NULL)
- conv->ui_ops->custom_smiley_write(conv, smile, data, size);
- else
- purple_debug_info("conversation", "Could not find the smiley write function");
-}
-
-void
-purple_conv_custom_smiley_close(PurpleConversation *conv, const char *smile)
-{
- g_return_if_fail(conv != NULL);
- g_return_if_fail(smile != NULL && *smile);
-
- if (conv->ui_ops != NULL && conv->ui_ops->custom_smiley_close != NULL)
- conv->ui_ops->custom_smiley_close(conv, smile);
- else
- purple_debug_info("conversation", "Could not find custom smiley close function");
-}
-
-
-/**************************************************************************
- * Chat Conversation API
- **************************************************************************/
-
-PurpleConversation *
-purple_conv_chat_get_conversation(const PurpleConvChat *chat)
-{
- g_return_val_if_fail(chat != NULL, NULL);
-
- return chat->conv;
-}
-
-GList *
-purple_conv_chat_set_users(PurpleConvChat *chat, GList *users)
-{
- g_return_val_if_fail(chat != NULL, NULL);
-
- chat->in_room = users;
-
- return users;
+ purple_request_cpar_from_account(
+ purple_conversation_get_account(conv)),
+ data, 2, _("_Send Message"),
+ G_CALLBACK(purple_conversation_send_confirm_cb), _("Cancel"), NULL);
}
GList *
-purple_conv_chat_get_users(const PurpleConvChat *chat)
-{
- g_return_val_if_fail(chat != NULL, NULL);
-
- return chat->in_room;
-}
-
-void
-purple_conv_chat_ignore(PurpleConvChat *chat, const char *name)
+purple_conversation_get_extended_menu(PurpleConversation *conv)
{
- g_return_if_fail(chat != NULL);
- g_return_if_fail(name != NULL);
+ GList *menu = NULL;
- /* Make sure the user isn't already ignored. */
- if (purple_conv_chat_is_user_ignored(chat, name))
- return;
+ g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
- purple_conv_chat_set_ignored(chat,
- g_list_append(chat->ignored, g_strdup(name)));
+ purple_signal_emit(purple_conversations_get_handle(),
+ "conversation-extended-menu", conv, &menu);
+ return menu;
}
-void
-purple_conv_chat_unignore(PurpleConvChat *chat, const char *name)
+void purple_conversation_clear_message_history(PurpleConversation *conv)
{
- GList *item;
+ GList *list;
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
- g_return_if_fail(chat != NULL);
- g_return_if_fail(name != NULL);
+ g_return_if_fail(priv != NULL);
- /* Make sure the user is actually ignored. */
- if (!purple_conv_chat_is_user_ignored(chat, name))
- return;
+ list = priv->message_history;
+ g_list_free_full(list, g_object_unref);
+ priv->message_history = NULL;
- item = g_list_find(purple_conv_chat_get_ignored(chat),
- purple_conv_chat_get_ignored_user(chat, name));
-
- purple_conv_chat_set_ignored(chat,
- g_list_remove_link(chat->ignored, item));
-
- g_free(item->data);
- g_list_free_1(item);
+ purple_signal_emit(purple_conversations_get_handle(),
+ "cleared-message-history", conv);
}
-GList *
-purple_conv_chat_set_ignored(PurpleConvChat *chat, GList *ignored)
+GList *purple_conversation_get_message_history(PurpleConversation *conv)
{
- g_return_val_if_fail(chat != NULL, NULL);
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
- chat->ignored = ignored;
+ g_return_val_if_fail(priv != NULL, NULL);
- return ignored;
+ return priv->message_history;
}
-GList *
-purple_conv_chat_get_ignored(const PurpleConvChat *chat)
+void purple_conversation_set_ui_data(PurpleConversation *conv, gpointer ui_data)
{
- g_return_val_if_fail(chat != NULL, NULL);
+ g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
- return chat->ignored;
+ conv->ui_data = ui_data;
}
-const char *
-purple_conv_chat_get_ignored_user(const PurpleConvChat *chat, const char *user)
+gpointer purple_conversation_get_ui_data(const PurpleConversation *conv)
{
- GList *ignored;
-
- g_return_val_if_fail(chat != NULL, NULL);
- g_return_val_if_fail(user != NULL, NULL);
-
- for (ignored = purple_conv_chat_get_ignored(chat);
- ignored != NULL;
- ignored = ignored->next) {
-
- const char *ign = (const char *)ignored->data;
-
- if (!purple_utf8_strcasecmp(user, ign) ||
- ((*ign == '+' || *ign == '%') && !purple_utf8_strcasecmp(user, ign + 1)))
- return ign;
+ g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
- if (*ign == '@') {
- ign++;
-
- if ((*ign == '+' && !purple_utf8_strcasecmp(user, ign + 1)) ||
- (*ign != '+' && !purple_utf8_strcasecmp(user, ign)))
- return ign;
- }
- }
-
- return NULL;
+ return conv->ui_data;
}
gboolean
-purple_conv_chat_is_user_ignored(const PurpleConvChat *chat, const char *user)
-{
- g_return_val_if_fail(chat != NULL, FALSE);
- g_return_val_if_fail(user != NULL, FALSE);
-
- return (purple_conv_chat_get_ignored_user(chat, user) != NULL);
-}
-
-void
-purple_conv_chat_set_topic(PurpleConvChat *chat, const char *who, const char *topic)
+purple_conversation_do_command(PurpleConversation *conv, const gchar *cmdline,
+ const gchar *markup, gchar **error)
{
- g_return_if_fail(chat != NULL);
-
- g_free(chat->who);
- g_free(chat->topic);
-
- chat->who = g_strdup(who);
- chat->topic = g_strdup(topic);
-
- purple_conversation_update(purple_conv_chat_get_conversation(chat),
- PURPLE_CONV_UPDATE_TOPIC);
-
- purple_signal_emit(purple_conversations_get_handle(), "chat-topic-changed",
- chat->conv, chat->who, chat->topic);
+ char *mark = (markup && *markup) ? NULL : g_markup_escape_text(cmdline, -1), *err = NULL;
+ PurpleCmdStatus status = purple_cmd_do_command(conv, cmdline, mark ? mark : markup, error ? error : &err);
+ g_free(mark);
+ g_free(err);
+ return (status == PURPLE_CMD_STATUS_OK);
}
-const char *
-purple_conv_chat_get_topic(const PurpleConvChat *chat)
+gssize
+purple_conversation_get_max_message_size(PurpleConversation *conv)
{
- g_return_val_if_fail(chat != NULL, NULL);
+ PurplePlugin *prpl;
+ PurplePluginProtocolInfo *prpl_info;
- return chat->topic;
-}
+ g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), 0);
-void
-purple_conv_chat_set_id(PurpleConvChat *chat, int id)
-{
- g_return_if_fail(chat != NULL);
+ prpl = purple_connection_get_prpl(
+ purple_conversation_get_connection(conv));
+ g_return_val_if_fail(prpl != NULL, 0);
- chat->id = id;
-}
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+ g_return_val_if_fail(prpl_info != NULL, 0);
-int
-purple_conv_chat_get_id(const PurpleConvChat *chat)
-{
- g_return_val_if_fail(chat != NULL, -1);
+ if (!PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_max_message_size))
+ return 0;
- return chat->id;
+ return prpl_info->get_max_message_size(conv);
}
-void
-purple_conv_chat_write(PurpleConvChat *chat, const char *who, const char *message,
- PurpleMessageFlags flags, time_t mtime)
+PurpleSmiley *
+purple_conversation_add_remote_smiley(PurpleConversation *conv,
+ const gchar *shortcut)
{
- PurpleAccount *account;
- PurpleConversation *conv;
- PurpleConnection *gc;
-
- g_return_if_fail(chat != NULL);
- g_return_if_fail(who != NULL);
- g_return_if_fail(message != NULL);
-
- conv = purple_conv_chat_get_conversation(chat);
- gc = purple_conversation_get_gc(conv);
- account = purple_connection_get_account(gc);
-
- /* Don't display this if the person who wrote it is ignored. */
- if (purple_conv_chat_is_user_ignored(chat, who))
- return;
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
+ PurpleSmiley *smiley;
- if (mtime < 0) {
- purple_debug_error("conversation",
- "purple_conv_chat_write ignoring negative timestamp\n");
- /* TODO: Would be more appropriate to use a value that indicates
- that the timestamp is unknown, and surface that in the UI. */
- mtime = time(NULL);
- }
-
- if (!(flags & PURPLE_MESSAGE_WHISPER)) {
- const char *str;
-
- str = purple_normalize(account, who);
+ g_return_val_if_fail(priv != NULL, NULL);
+ g_return_val_if_fail(shortcut != NULL, NULL);
+ g_return_val_if_fail(shortcut[0] != '\0', NULL);
- if (purple_strequal(str, chat->nick)) {
- flags |= PURPLE_MESSAGE_SEND;
- } else {
- flags |= PURPLE_MESSAGE_RECV;
-
- if (purple_utf8_has_word(message, chat->nick))
- flags |= PURPLE_MESSAGE_NICK;
- }
+ if (priv->remote_smileys == NULL) {
+ priv->remote_smileys = purple_smiley_list_new();
+ g_object_set(priv->remote_smileys,
+ "drop-failed-remotes", TRUE, NULL);
}
- /* Pass this on to either the ops structure or the default write func. */
- if (conv->ui_ops != NULL && conv->ui_ops->write_chat != NULL)
- conv->ui_ops->write_chat(conv, who, message, flags, mtime);
- else
- purple_conversation_write(conv, who, message, flags, mtime);
-}
-
-void
-purple_conv_chat_send(PurpleConvChat *chat, const char *message)
-{
- purple_conv_chat_send_with_flags(chat, message, 0);
-}
+ smiley = purple_smiley_list_get_by_shortcut(
+ priv->remote_smileys, shortcut);
-void
-purple_conv_chat_send_with_flags(PurpleConvChat *chat, const char *message, PurpleMessageFlags flags)
-{
- g_return_if_fail(chat != NULL);
- g_return_if_fail(message != NULL);
-
- common_send(purple_conv_chat_get_conversation(chat), message, flags);
-}
-
-void
-purple_conv_chat_add_user(PurpleConvChat *chat, const char *user,
- const char *extra_msg, PurpleConvChatBuddyFlags flags,
- gboolean new_arrival)
-{
- GList *users = g_list_append(NULL, (char *)user);
- GList *extra_msgs = g_list_append(NULL, (char *)extra_msg);
- GList *flags2 = g_list_append(NULL, GINT_TO_POINTER(flags));
-
- purple_conv_chat_add_users(chat, users, extra_msgs, flags2, new_arrival);
-
- g_list_free(users);
- g_list_free(extra_msgs);
- g_list_free(flags2);
-}
-
-static int
-purple_conv_chat_cb_compare(PurpleConvChatBuddy *a, PurpleConvChatBuddy *b)
-{
- PurpleConvChatBuddyFlags f1 = 0, f2 = 0;
- char *user1 = NULL, *user2 = NULL;
- gint ret = 0;
-
- if (a) {
- f1 = a->flags;
- if (a->alias_key)
- user1 = a->alias_key;
- else if (a->name)
- user1 = a->name;
- }
+ /* smiley was already added */
+ if (smiley)
+ return NULL;
- if (b) {
- f2 = b->flags;
- if (b->alias_key)
- user2 = b->alias_key;
- else if (b->name)
- user2 = b->name;
- }
+ smiley = purple_smiley_new_remote(shortcut);
- if (user1 == NULL || user2 == NULL) {
- if (!(user1 == NULL && user2 == NULL))
- ret = (user1 == NULL) ? -1: 1;
- } else if (f1 != f2) {
- /* sort more important users first */
- ret = (f1 > f2) ? -1 : 1;
- } else if (a->buddy != b->buddy) {
- ret = a->buddy ? -1 : 1;
- } else {
- ret = purple_utf8_strcasecmp(user1, user2);
+ if (!purple_smiley_list_add(priv->remote_smileys, smiley)) {
+ purple_debug_error("conversation", "failed adding remote "
+ "smiley to the list");
+ g_object_unref(smiley);
+ return NULL;
}
- return ret;
+ /* priv->remote_smileys holds the only one ref */
+ g_object_unref(smiley);
+ return smiley;
}
-void
-purple_conv_chat_add_users(PurpleConvChat *chat, GList *users, GList *extra_msgs,
- GList *flags, gboolean new_arrivals)
+PurpleSmiley *
+purple_conversation_get_remote_smiley(PurpleConversation *conv,
+ const gchar *shortcut)
{
- PurpleConversation *conv;
- PurpleConversationUiOps *ops;
- PurpleConvChatBuddy *cbuddy;
- PurpleConnection *gc;
- PurplePluginProtocolInfo *prpl_info;
- GList *ul, *fl;
- GList *cbuddies = NULL;
-
- g_return_if_fail(chat != NULL);
- g_return_if_fail(users != NULL);
-
- conv = purple_conv_chat_get_conversation(chat);
- ops = purple_conversation_get_ui_ops(conv);
-
- gc = purple_conversation_get_gc(conv);
- g_return_if_fail(gc != NULL);
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
- g_return_if_fail(prpl_info != NULL);
-
- ul = users;
- fl = flags;
- while ((ul != NULL) && (fl != NULL)) {
- const char *user = (const char *)ul->data;
- const char *alias = user;
- gboolean quiet;
- PurpleConvChatBuddyFlags flag = GPOINTER_TO_INT(fl->data);
- const char *extra_msg = (extra_msgs ? extra_msgs->data : NULL);
-
- if(!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
- if (purple_strequal(chat->nick, purple_normalize(conv->account, user))) {
- const char *alias2 = purple_account_get_alias(conv->account);
- if (alias2 != NULL)
- alias = alias2;
- else
- {
- const char *display_name = purple_connection_get_display_name(gc);
- if (display_name != NULL)
- alias = display_name;
- }
- } else {
- PurpleBuddy *buddy;
- if ((buddy = purple_find_buddy(gc->account, user)) != NULL)
- alias = purple_buddy_get_contact_alias(buddy);
- }
- }
-
- quiet = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_conversations_get_handle(),
- "chat-buddy-joining", conv, user, flag)) ||
- purple_conv_chat_is_user_ignored(chat, user);
-
- cbuddy = purple_conv_chat_cb_new(user, alias, flag);
- cbuddy->buddy = purple_find_buddy(conv->account, user) != NULL;
-
- chat->in_room = g_list_prepend(chat->in_room, cbuddy);
- g_hash_table_replace(chat->users, g_strdup(cbuddy->name), cbuddy);
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
- cbuddies = g_list_prepend(cbuddies, cbuddy);
+ g_return_val_if_fail(priv != NULL, NULL);
+ g_return_val_if_fail(shortcut != NULL, NULL);
+ g_return_val_if_fail(shortcut[0] != '\0', NULL);
- if (!quiet && new_arrivals) {
- char *alias_esc = g_markup_escape_text(alias, -1);
- char *tmp;
-
- if (extra_msg == NULL)
- tmp = g_strdup_printf(_("%s entered the room."), alias_esc);
- else {
- char *extra_msg_esc = g_markup_escape_text(extra_msg, -1);
- tmp = g_strdup_printf(_("%s [<I>%s</I>] entered the room."),
- alias_esc, extra_msg_esc);
- g_free(extra_msg_esc);
- }
- g_free(alias_esc);
-
- purple_conversation_write(conv, NULL, tmp,
- PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY,
- time(NULL));
- g_free(tmp);
- }
-
- purple_signal_emit(purple_conversations_get_handle(),
- "chat-buddy-joined", conv, user, flag, new_arrivals);
- ul = ul->next;
- fl = fl->next;
- if (extra_msgs != NULL)
- extra_msgs = extra_msgs->next;
- }
-
- cbuddies = g_list_sort(cbuddies, (GCompareFunc)purple_conv_chat_cb_compare);
-
- if (ops != NULL && ops->chat_add_users != NULL)
- ops->chat_add_users(conv, cbuddies, new_arrivals);
+ if (priv->remote_smileys == NULL)
+ return NULL;
- g_list_free(cbuddies);
+ return purple_smiley_list_get_by_shortcut(
+ priv->remote_smileys, shortcut);
}
-void
-purple_conv_chat_rename_user(PurpleConvChat *chat, const char *old_user,
- const char *new_user)
+PurpleSmileyList *
+purple_conversation_get_remote_smileys(PurpleConversation *conv)
{
- PurpleConversation *conv;
- PurpleConversationUiOps *ops;
- PurpleConnection *gc;
- PurplePluginProtocolInfo *prpl_info;
- PurpleConvChatBuddy *cb;
- PurpleConvChatBuddyFlags flags;
- const char *new_alias = new_user;
- char tmp[BUF_LONG];
- gboolean is_me = FALSE;
-
- g_return_if_fail(chat != NULL);
- g_return_if_fail(old_user != NULL);
- g_return_if_fail(new_user != NULL);
-
- conv = purple_conv_chat_get_conversation(chat);
- ops = purple_conversation_get_ui_ops(conv);
-
- gc = purple_conversation_get_gc(conv);
- g_return_if_fail(gc != NULL);
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
- g_return_if_fail(prpl_info != NULL);
-
- if (purple_strequal(chat->nick, purple_normalize(conv->account, old_user))) {
- const char *alias;
-
- /* Note this for later. */
- is_me = TRUE;
-
- if(!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
- alias = purple_account_get_alias(conv->account);
- if (alias != NULL)
- new_alias = alias;
- else
- {
- const char *display_name = purple_connection_get_display_name(gc);
- if (display_name != NULL)
- alias = display_name;
- }
- }
- } else if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
- PurpleBuddy *buddy;
- if ((buddy = purple_find_buddy(gc->account, new_user)) != NULL)
- new_alias = purple_buddy_get_contact_alias(buddy);
- }
-
- flags = purple_conv_chat_user_get_flags(chat, old_user);
- cb = purple_conv_chat_cb_new(new_user, new_alias, flags);
- cb->buddy = purple_find_buddy(conv->account, new_user) != NULL;
-
- chat->in_room = g_list_prepend(chat->in_room, cb);
- g_hash_table_replace(chat->users, g_strdup(cb->name), cb);
-
- if (ops != NULL && ops->chat_rename_user != NULL)
- ops->chat_rename_user(conv, old_user, new_user, new_alias);
-
- cb = purple_conv_chat_cb_find(chat, old_user);
-
- if (cb) {
- chat->in_room = g_list_remove(chat->in_room, cb);
- g_hash_table_remove(chat->users, cb->name);
- purple_conv_chat_cb_destroy(cb);
- }
-
- if (purple_conv_chat_is_user_ignored(chat, old_user)) {
- purple_conv_chat_unignore(chat, old_user);
- purple_conv_chat_ignore(chat, new_user);
- }
- else if (purple_conv_chat_is_user_ignored(chat, new_user))
- purple_conv_chat_unignore(chat, new_user);
-
- if (is_me)
- purple_conv_chat_set_nick(chat, new_user);
-
- if (purple_prefs_get_bool("/purple/conversations/chat/show_nick_change") &&
- !purple_conv_chat_is_user_ignored(chat, new_user)) {
-
- if (is_me) {
- char *escaped = g_markup_escape_text(new_user, -1);
- g_snprintf(tmp, sizeof(tmp),
- _("You are now known as %s"), escaped);
- g_free(escaped);
- } else {
- const char *old_alias = old_user;
- const char *new_alias = new_user;
- char *escaped;
- char *escaped2;
-
- if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
- PurpleBuddy *buddy;
-
- if ((buddy = purple_find_buddy(gc->account, old_user)) != NULL)
- old_alias = purple_buddy_get_contact_alias(buddy);
- if ((buddy = purple_find_buddy(gc->account, new_user)) != NULL)
- new_alias = purple_buddy_get_contact_alias(buddy);
- }
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
- escaped = g_markup_escape_text(old_alias, -1);
- escaped2 = g_markup_escape_text(new_alias, -1);
- g_snprintf(tmp, sizeof(tmp),
- _("%s is now known as %s"), escaped, escaped2);
- g_free(escaped);
- g_free(escaped2);
- }
+ g_return_val_if_fail(priv != NULL, NULL);
- purple_conversation_write(conv, NULL, tmp,
- PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY,
- time(NULL));
- }
+ return priv->remote_smileys;
}
-void
-purple_conv_chat_remove_user(PurpleConvChat *chat, const char *user, const char *reason)
-{
- GList *users = g_list_append(NULL, (char *)user);
-
- purple_conv_chat_remove_users(chat, users, reason);
- g_list_free(users);
-}
+/**************************************************************************
+ * GObject code
+ **************************************************************************/
-void
-purple_conv_chat_remove_users(PurpleConvChat *chat, GList *users, const char *reason)
+/* Set method for GObject properties */
+static void
+purple_conversation_set_property(GObject *obj, guint param_id, const GValue *value,
+ GParamSpec *pspec)
{
- PurpleConversation *conv;
- PurpleConnection *gc;
- PurplePluginProtocolInfo *prpl_info;
- PurpleConversationUiOps *ops;
- PurpleConvChatBuddy *cb;
- GList *l;
- gboolean quiet;
+ PurpleConversation *conv = PURPLE_CONVERSATION(obj);
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
- g_return_if_fail(chat != NULL);
- g_return_if_fail(users != NULL);
-
- conv = purple_conv_chat_get_conversation(chat);
-
- gc = purple_conversation_get_gc(conv);
- g_return_if_fail(gc != NULL);
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
- g_return_if_fail(prpl_info != NULL);
-
- ops = purple_conversation_get_ui_ops(conv);
-
- for (l = users; l != NULL; l = l->next) {
- const char *user = (const char *)l->data;
- quiet = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_conversations_get_handle(),
- "chat-buddy-leaving", conv, user, reason)) |
- purple_conv_chat_is_user_ignored(chat, user);
-
- cb = purple_conv_chat_cb_find(chat, user);
-
- if (cb) {
- chat->in_room = g_list_remove(chat->in_room, cb);
- g_hash_table_remove(chat->users, cb->name);
- purple_conv_chat_cb_destroy(cb);
- }
-
- /* NOTE: Don't remove them from ignored in case they re-enter. */
-
- if (!quiet) {
- const char *alias = user;
- char *alias_esc;
- char *tmp;
-
- if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
- PurpleBuddy *buddy;
-
- if ((buddy = purple_find_buddy(gc->account, user)) != NULL)
- alias = purple_buddy_get_contact_alias(buddy);
- }
-
- alias_esc = g_markup_escape_text(alias, -1);
-
- if (reason == NULL || !*reason)
- tmp = g_strdup_printf(_("%s left the room."), alias_esc);
- else {
- char *reason_esc = g_markup_escape_text(reason, -1);
- tmp = g_strdup_printf(_("%s left the room (%s)."),
- alias_esc, reason_esc);
- g_free(reason_esc);
- }
- g_free(alias_esc);
-
- purple_conversation_write(conv, NULL, tmp,
- PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY,
- time(NULL));
- g_free(tmp);
- }
-
- purple_signal_emit(purple_conversations_get_handle(), "chat-buddy-left",
- conv, user, reason);
+ switch (param_id) {
+ case PROP_ACCOUNT:
+ priv->account = g_value_get_object(value);
+ break;
+ case PROP_NAME:
+ g_free(priv->name);
+ priv->name = g_strdup(g_value_get_string(value));
+ break;
+ case PROP_TITLE:
+ g_free(priv->title);
+ priv->title = g_strdup(g_value_get_string(value));
+ break;
+ case PROP_LOGGING:
+ purple_conversation_set_logging(conv, g_value_get_boolean(value));
+ break;
+ case PROP_FEATURES:
+ purple_conversation_set_features(conv, g_value_get_flags(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
}
-
- if (ops != NULL && ops->chat_remove_users != NULL)
- ops->chat_remove_users(conv, users);
}
-void
-purple_conv_chat_clear_users(PurpleConvChat *chat)
+/* Get method for GObject properties */
+static void
+purple_conversation_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
{
- PurpleConversation *conv;
- PurpleConversationUiOps *ops;
- GList *users;
- GList *l;
- GList *names = NULL;
-
- g_return_if_fail(chat != NULL);
-
- conv = purple_conv_chat_get_conversation(chat);
- ops = purple_conversation_get_ui_ops(conv);
- users = chat->in_room;
-
- if (ops != NULL && ops->chat_remove_users != NULL) {
- for (l = users; l; l = l->next) {
- PurpleConvChatBuddy *cb = l->data;
- names = g_list_prepend(names, cb->name);
- }
- ops->chat_remove_users(conv, names);
- g_list_free(names);
- }
-
- for (l = users; l; l = l->next)
- {
- PurpleConvChatBuddy *cb = l->data;
+ PurpleConversation *conv = PURPLE_CONVERSATION(obj);
- purple_signal_emit(purple_conversations_get_handle(),
- "chat-buddy-leaving", conv, cb->name, NULL);
- purple_signal_emit(purple_conversations_get_handle(),
- "chat-buddy-left", conv, cb->name, NULL);
-
- purple_conv_chat_cb_destroy(cb);
+ switch (param_id) {
+ case PROP_ACCOUNT:
+ g_value_set_object(value, purple_conversation_get_account(conv));
+ break;
+ case PROP_NAME:
+ g_value_set_string(value, purple_conversation_get_name(conv));
+ break;
+ case PROP_TITLE:
+ g_value_set_string(value, purple_conversation_get_title(conv));
+ break;
+ case PROP_LOGGING:
+ g_value_set_boolean(value, purple_conversation_is_logging(conv));
+ break;
+ case PROP_FEATURES:
+ g_value_set_flags(value, purple_conversation_get_features(conv));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
}
-
- g_hash_table_remove_all(chat->users);
-
- g_list_free(users);
- chat->in_room = NULL;
}
-
-gboolean
-purple_conv_chat_find_user(PurpleConvChat *chat, const char *user)
-{
- g_return_val_if_fail(chat != NULL, FALSE);
- g_return_val_if_fail(user != NULL, FALSE);
-
- return (purple_conv_chat_cb_find(chat, user) != NULL);
-}
-
-void
-purple_conv_chat_user_set_flags(PurpleConvChat *chat, const char *user,
- PurpleConvChatBuddyFlags flags)
+/* Called when done constructing */
+static void
+purple_conversation_constructed(GObject *object)
{
- PurpleConversation *conv;
+ PurpleConversation *conv = PURPLE_CONVERSATION(object);
+ PurpleAccount *account;
+ PurpleConnection *gc;
PurpleConversationUiOps *ops;
- PurpleConvChatBuddy *cb;
- PurpleConvChatBuddyFlags oldflags;
-
- g_return_if_fail(chat != NULL);
- g_return_if_fail(user != NULL);
- cb = purple_conv_chat_cb_find(chat, user);
+ parent_class->constructed(object);
- if (!cb)
- return;
+ g_object_get(object, "account", &account, NULL);
+ gc = purple_account_get_connection(account);
- if (flags == cb->flags)
- return;
+ /* copy features from the connection. */
+ purple_conversation_set_features(conv, purple_connection_get_flags(gc));
- oldflags = cb->flags;
- cb->flags = flags;
+ /* add the conversation to the appropriate lists */
+ purple_conversations_add(conv);
- conv = purple_conv_chat_get_conversation(chat);
- ops = purple_conversation_get_ui_ops(conv);
+ /* Auto-set the title. */
+ purple_conversation_autoset_title(conv);
- if (ops != NULL && ops->chat_update_user != NULL)
- ops->chat_update_user(conv, user);
+ /* Don't move this.. it needs to be one of the last things done otherwise
+ * it causes mysterious crashes on my system.
+ * -- Gary
+ */
+ ops = purple_conversations_get_ui_ops();
+ purple_conversation_set_ui_ops(conv, ops);
+ if (ops != NULL && ops->create_conversation != NULL)
+ ops->create_conversation(conv);
purple_signal_emit(purple_conversations_get_handle(),
- "chat-buddy-flags", conv, user, oldflags, flags);
-}
-
-PurpleConvChatBuddyFlags
-purple_conv_chat_user_get_flags(PurpleConvChat *chat, const char *user)
-{
- PurpleConvChatBuddy *cb;
-
- g_return_val_if_fail(chat != NULL, 0);
- g_return_val_if_fail(user != NULL, 0);
-
- cb = purple_conv_chat_cb_find(chat, user);
-
- if (!cb)
- return PURPLE_CBFLAGS_NONE;
-
- return cb->flags;
-}
-
-void purple_conv_chat_set_nick(PurpleConvChat *chat, const char *nick) {
- g_return_if_fail(chat != NULL);
-
- g_free(chat->nick);
- chat->nick = g_strdup(purple_normalize(chat->conv->account, nick));
-}
-
-const char *purple_conv_chat_get_nick(PurpleConvChat *chat) {
- g_return_val_if_fail(chat != NULL, NULL);
-
- return chat->nick;
-}
-
-PurpleConversation *
-purple_find_chat(const PurpleConnection *gc, int id)
-{
- GList *l;
- PurpleConversation *conv;
-
- for (l = purple_get_chats(); l != NULL; l = l->next) {
- conv = (PurpleConversation *)l->data;
-
- if (purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)) == id &&
- purple_conversation_get_gc(conv) == gc)
- return conv;
- }
-
- return NULL;
-}
-
-void
-purple_conv_chat_left(PurpleConvChat *chat)
-{
- g_return_if_fail(chat != NULL);
+ "conversation-created", conv);
- chat->left = TRUE;
- purple_conversation_update(chat->conv, PURPLE_CONV_UPDATE_CHATLEFT);
+ g_object_unref(account);
}
static void
-invite_user_to_chat(gpointer data, PurpleRequestFields *fields)
+purple_conversation_dispose(GObject *object)
{
- PurpleConversation *conv;
- PurpleConvChat *chat;
- const char *user, *message;
-
- conv = data;
- chat = PURPLE_CONV_CHAT(conv);
- user = purple_request_fields_get_string(fields, "screenname");
- message = purple_request_fields_get_string(fields, "message");
-
- serv_chat_invite(purple_conversation_get_gc(conv), chat->id, message, user);
+ g_object_set_data(object, "is-finalizing", GINT_TO_POINTER(TRUE));
}
-void purple_conv_chat_invite_user(PurpleConvChat *chat, const char *user,
- const char *message, gboolean confirm)
-{
- PurpleAccount *account;
- PurpleConversation *conv;
- PurpleRequestFields *fields;
- PurpleRequestFieldGroup *group;
- PurpleRequestField *field;
-
- g_return_if_fail(chat);
-
- if (!user || !*user || !message || !*message)
- confirm = TRUE;
-
- conv = chat->conv;
- account = conv->account;
-
- if (!confirm) {
- serv_chat_invite(purple_account_get_connection(account),
- purple_conv_chat_get_id(chat), message, user);
- return;
- }
-
- fields = purple_request_fields_new();
- group = purple_request_field_group_new(_("Invite to chat"));
- purple_request_fields_add_group(fields, group);
-
- field = purple_request_field_string_new("screenname", _("Buddy"), user, FALSE);
- purple_request_field_group_add_field(group, field);
- purple_request_field_set_required(field, TRUE);
- purple_request_field_set_type_hint(field, "screenname");
-
- field = purple_request_field_string_new("message", _("Message"), message, FALSE);
- purple_request_field_group_add_field(group, field);
-
- purple_request_fields(conv, _("Invite to chat"), NULL,
- _("Please enter the name of the user you wish to invite, "
- "along with an optional invite message."),
- fields,
- _("Invite"), G_CALLBACK(invite_user_to_chat),
- _("Cancel"), NULL,
- account, user, conv,
- conv);
-}
-
-gboolean
-purple_conv_chat_has_left(PurpleConvChat *chat)
-{
- g_return_val_if_fail(chat != NULL, TRUE);
-
- return chat->left;
-}
-
-PurpleConvChatBuddy *
-purple_conv_chat_cb_new(const char *name, const char *alias, PurpleConvChatBuddyFlags flags)
+/* GObject finalize function */
+static void
+purple_conversation_finalize(GObject *object)
{
- PurpleConvChatBuddy *cb;
+ PurpleConversation *conv = PURPLE_CONVERSATION(object);
+ PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
+ PurpleConversationUiOps *ops = purple_conversation_get_ui_ops(conv);
- g_return_val_if_fail(name != NULL, NULL);
-
- cb = g_new0(PurpleConvChatBuddy, 1);
- cb->name = g_strdup(name);
- cb->flags = flags;
- cb->alias = g_strdup(alias);
- cb->attributes = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, g_free);
-
- PURPLE_DBUS_REGISTER_POINTER(cb, PurpleConvChatBuddy);
- return cb;
-}
-
-PurpleConvChatBuddy *
-purple_conv_chat_cb_find(PurpleConvChat *chat, const char *name)
-{
- g_return_val_if_fail(chat != NULL, NULL);
- g_return_val_if_fail(name != NULL, NULL);
+ purple_request_close_with_handle(conv);
- return g_hash_table_lookup(chat->users, name);
-}
+ purple_e2ee_state_unref(priv->e2ee_state);
+ priv->e2ee_state = NULL;
-void
-purple_conv_chat_cb_destroy(PurpleConvChatBuddy *cb)
-{
- if (cb == NULL)
- return;
+ /* remove from conversations and im/chats lists prior to emit */
+ purple_conversations_remove(conv);
purple_signal_emit(purple_conversations_get_handle(),
- "deleting-chat-buddy", cb);
+ "deleting-conversation", conv);
- g_free(cb->alias);
- g_free(cb->alias_key);
- g_free(cb->name);
- g_hash_table_destroy(cb->attributes);
+ purple_conversation_close_logs(conv);
+ purple_conversation_clear_message_history(conv);
- PURPLE_DBUS_UNREGISTER_POINTER(cb);
- g_free(cb);
-}
+ if (ops != NULL && ops->destroy_conversation != NULL)
+ ops->destroy_conversation(conv);
-const char *
-purple_conv_chat_cb_get_name(PurpleConvChatBuddy *cb)
-{
- g_return_val_if_fail(cb != NULL, NULL);
+ g_free(priv->name);
+ g_free(priv->title);
- return cb->name;
-}
+ priv->name = NULL;
+ priv->title = NULL;
-const char *
-purple_conv_chat_cb_get_attribute(PurpleConvChatBuddy *cb, const char *key)
-{
- g_return_val_if_fail(cb != NULL, NULL);
- g_return_val_if_fail(key != NULL, NULL);
-
- return g_hash_table_lookup(cb->attributes, key);
+ parent_class->finalize(object);
}
+/* Class initializer function */
static void
-append_attribute_key(gpointer key, gpointer value, gpointer user_data)
-{
- GList **list = user_data;
- *list = g_list_prepend(*list, key);
-}
-
-GList *
-purple_conv_chat_cb_get_attribute_keys(PurpleConvChatBuddy *cb)
-{
- GList *keys = NULL;
-
- g_return_val_if_fail(cb != NULL, NULL);
-
- g_hash_table_foreach(cb->attributes, (GHFunc)append_attribute_key, &keys);
-
- return keys;
-}
-
-void
-purple_conv_chat_cb_set_attribute(PurpleConvChat *chat, PurpleConvChatBuddy *cb, const char *key, const char *value)
-{
- PurpleConversation *conv;
- PurpleConversationUiOps *ops;
-
- g_return_if_fail(cb != NULL);
- g_return_if_fail(key != NULL);
- g_return_if_fail(value != NULL);
-
- g_hash_table_replace(cb->attributes, g_strdup(key), g_strdup(value));
-
- conv = purple_conv_chat_get_conversation(chat);
- ops = purple_conversation_get_ui_ops(conv);
-
- if (ops != NULL && ops->chat_update_user != NULL)
- ops->chat_update_user(conv, cb->name);
-}
-
-void
-purple_conv_chat_cb_set_attributes(PurpleConvChat *chat, PurpleConvChatBuddy *cb, GList *keys, GList *values)
-{
- PurpleConversation *conv;
- PurpleConversationUiOps *ops;
-
- g_return_if_fail(cb != NULL);
- g_return_if_fail(keys != NULL);
- g_return_if_fail(values != NULL);
-
- while (keys != NULL && values != NULL) {
- g_hash_table_replace(cb->attributes, g_strdup(keys->data), g_strdup(values->data));
- keys = g_list_next(keys);
- values = g_list_next(values);
- }
-
- conv = purple_conv_chat_get_conversation(chat);
- ops = purple_conversation_get_ui_ops(conv);
-
- if (ops != NULL && ops->chat_update_user != NULL)
- ops->chat_update_user(conv, cb->name);
-}
-
-GList *
-purple_conversation_get_extended_menu(PurpleConversation *conv)
+purple_conversation_class_init(PurpleConversationClass *klass)
{
- GList *menu = NULL;
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
- g_return_val_if_fail(conv != NULL, NULL);
+ parent_class = g_type_class_peek_parent(klass);
- purple_signal_emit(purple_conversations_get_handle(),
- "conversation-extended-menu", conv, &menu);
- return menu;
-}
+ obj_class->dispose = purple_conversation_dispose;
+ obj_class->finalize = purple_conversation_finalize;
+ obj_class->constructed = purple_conversation_constructed;
-void purple_conversation_clear_message_history(PurpleConversation *conv)
-{
- GList *list = conv->message_history;
- message_history_free(list);
- conv->message_history = NULL;
+ /* Setup properties */
+ obj_class->get_property = purple_conversation_get_property;
+ obj_class->set_property = purple_conversation_set_property;
- purple_signal_emit(purple_conversations_get_handle(),
- "cleared-message-history", conv);
-}
+ g_type_class_add_private(klass, sizeof(PurpleConversationPrivate));
-GList *purple_conversation_get_message_history(PurpleConversation *conv)
-{
- return conv->message_history;
-}
+ properties[PROP_ACCOUNT] = g_param_spec_object("account", "Account",
+ "The account for the conversation.", PURPLE_TYPE_ACCOUNT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
-const char *purple_conversation_message_get_sender(PurpleConvMessage *msg)
-{
- g_return_val_if_fail(msg, NULL);
- return msg->who;
-}
+ properties[PROP_NAME] = g_param_spec_string("name", "Name",
+ "The name of the conversation.", NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
-const char *purple_conversation_message_get_message(PurpleConvMessage *msg)
-{
- g_return_val_if_fail(msg, NULL);
- return msg->what;
-}
+ properties[PROP_TITLE] = g_param_spec_string("title", "Title",
+ "The title of the conversation.", NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
-PurpleMessageFlags purple_conversation_message_get_flags(PurpleConvMessage *msg)
-{
- g_return_val_if_fail(msg, 0);
- return msg->flags;
-}
+ properties[PROP_LOGGING] = g_param_spec_boolean("logging", "Logging status",
+ "Whether logging is enabled or not.", FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-time_t purple_conversation_message_get_timestamp(PurpleConvMessage *msg)
-{
- g_return_val_if_fail(msg, 0);
- return msg->when;
-}
+ properties[PROP_FEATURES] = g_param_spec_flags("features",
+ "Connection features",
+ "The connection features of the conversation.",
+ PURPLE_TYPE_CONNECTION_FLAGS, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-gboolean
-purple_conversation_do_command(PurpleConversation *conv, const gchar *cmdline,
- const gchar *markup, gchar **error)
-{
- char *mark = (markup && *markup) ? NULL : g_markup_escape_text(cmdline, -1), *err = NULL;
- PurpleCmdStatus status = purple_cmd_do_command(conv, cmdline, mark ? mark : markup, error ? error : &err);
- g_free(mark);
- g_free(err);
- return (status == PURPLE_CMD_STATUS_OK);
+ g_object_class_install_properties(obj_class, PROP_LAST, properties);
}
-void *
-purple_conversations_get_handle(void)
+GType
+purple_conversation_get_type(void)
{
- static int handle;
+ static GType type = 0;
- return &handle;
-}
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleConversationClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_conversation_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleConversation),
+ 0,
+ NULL,
+ NULL,
+ };
-void
-purple_conversations_init(void)
-{
- void *handle = purple_conversations_get_handle();
-
- conversation_cache = g_hash_table_new_full((GHashFunc)_purple_conversations_hconv_hash,
- (GEqualFunc)_purple_conversations_hconv_equal,
- (GDestroyNotify)_purple_conversations_hconv_free_key, NULL);
-
- /**********************************************************************
- * Register preferences
- **********************************************************************/
-
- /* Conversations */
- purple_prefs_add_none("/purple/conversations");
-
- /* Conversations -> Chat */
- purple_prefs_add_none("/purple/conversations/chat");
- purple_prefs_add_bool("/purple/conversations/chat/show_nick_change", TRUE);
-
- /* Conversations -> IM */
- purple_prefs_add_none("/purple/conversations/im");
- purple_prefs_add_bool("/purple/conversations/im/send_typing", TRUE);
-
-
- /**********************************************************************
- * Register signals
- **********************************************************************/
- purple_signal_register(handle, "writing-im-msg",
- purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_UINT,
- purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new_outgoing(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_UINT));
-
- purple_signal_register(handle, "wrote-im-msg",
- purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT,
- NULL, 5,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_UINT));
-
- purple_signal_register(handle, "sent-attention",
- purple_marshal_VOID__POINTER_POINTER_POINTER_UINT,
- NULL, 4,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_UINT));
-
- purple_signal_register(handle, "got-attention",
- purple_marshal_VOID__POINTER_POINTER_POINTER_UINT,
- NULL, 4,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_UINT));
-
- purple_signal_register(handle, "sending-im-msg",
- purple_marshal_VOID__POINTER_POINTER_POINTER,
- NULL, 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new_outgoing(PURPLE_TYPE_STRING));
-
- purple_signal_register(handle, "sent-im-msg",
- purple_marshal_VOID__POINTER_POINTER_POINTER,
- NULL, 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_STRING));
-
- purple_signal_register(handle, "receiving-im-msg",
- purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
- purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new_outgoing(PURPLE_TYPE_STRING),
- purple_value_new_outgoing(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new_outgoing(PURPLE_TYPE_UINT));
-
- purple_signal_register(handle, "received-im-msg",
- purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT,
- NULL, 5,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_UINT));
-
- purple_signal_register(handle, "blocked-im-msg",
- purple_marshal_VOID__POINTER_POINTER_POINTER_UINT_UINT,
- NULL, 5,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_UINT),
- purple_value_new(PURPLE_TYPE_UINT));
-
- purple_signal_register(handle, "writing-chat-msg",
- purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_UINT,
- purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new_outgoing(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_UINT));
-
- purple_signal_register(handle, "wrote-chat-msg",
- purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT,
- NULL, 5,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_UINT));
-
- purple_signal_register(handle, "sending-chat-msg",
- purple_marshal_VOID__POINTER_POINTER_UINT, NULL, 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new_outgoing(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_UINT));
-
- purple_signal_register(handle, "sent-chat-msg",
- purple_marshal_VOID__POINTER_POINTER_UINT, NULL, 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_UINT));
-
- purple_signal_register(handle, "receiving-chat-msg",
- purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
- purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new_outgoing(PURPLE_TYPE_STRING),
- purple_value_new_outgoing(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new_outgoing(PURPLE_TYPE_UINT));
-
- purple_signal_register(handle, "received-chat-msg",
- purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT,
- NULL, 5,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_UINT));
-
- purple_signal_register(handle, "conversation-created",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION));
-
- purple_signal_register(handle, "conversation-updated",
- purple_marshal_VOID__POINTER_UINT, NULL, 2,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_UINT));
-
- purple_signal_register(handle, "deleting-conversation",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION));
-
- purple_signal_register(handle, "buddy-typing",
- purple_marshal_VOID__POINTER_POINTER, NULL, 2,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING));
-
- purple_signal_register(handle, "buddy-typed",
- purple_marshal_VOID__POINTER_POINTER, NULL, 2,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING));
-
- purple_signal_register(handle, "buddy-typing-stopped",
- purple_marshal_VOID__POINTER_POINTER, NULL, 2,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING));
-
- purple_signal_register(handle, "chat-buddy-joining",
- purple_marshal_BOOLEAN__POINTER_POINTER_UINT,
- purple_value_new(PURPLE_TYPE_BOOLEAN), 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_UINT));
-
- purple_signal_register(handle, "chat-buddy-joined",
- purple_marshal_VOID__POINTER_POINTER_UINT_UINT, NULL, 4,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_UINT),
- purple_value_new(PURPLE_TYPE_BOOLEAN));
-
- purple_signal_register(handle, "chat-buddy-flags",
- purple_marshal_VOID__POINTER_POINTER_UINT_UINT, NULL, 4,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_UINT),
- purple_value_new(PURPLE_TYPE_UINT));
-
- purple_signal_register(handle, "chat-buddy-leaving",
- purple_marshal_BOOLEAN__POINTER_POINTER_POINTER,
- purple_value_new(PURPLE_TYPE_BOOLEAN), 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_STRING));
-
- purple_signal_register(handle, "chat-buddy-left",
- purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_STRING));
-
- purple_signal_register(handle, "deleting-chat-buddy",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CHATBUDDY));
-
- purple_signal_register(handle, "chat-inviting-user",
- purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new_outgoing(PURPLE_TYPE_STRING));
-
- purple_signal_register(handle, "chat-invited-user",
- purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_STRING));
-
- purple_signal_register(handle, "chat-invited",
- purple_marshal_INT__POINTER_POINTER_POINTER_POINTER_POINTER,
- purple_value_new(PURPLE_TYPE_INT), 5,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_POINTER));
-
- purple_signal_register(handle, "chat-invite-blocked",
- purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_POINTER,
- NULL, 5,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_BOXED, "GHashTable *"));
-
- purple_signal_register(handle, "chat-joined",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION));
-
- purple_signal_register(handle, "chat-join-failed",
- purple_marshal_VOID__POINTER_POINTER, NULL, 2,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONNECTION),
- purple_value_new(PURPLE_TYPE_POINTER));
-
- purple_signal_register(handle, "chat-left",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION));
-
- purple_signal_register(handle, "chat-topic-changed",
- purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_STRING));
-
- purple_signal_register(handle, "cleared-message-history",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION));
-
- purple_signal_register(handle, "conversation-extended-menu",
- purple_marshal_VOID__POINTER_POINTER, NULL, 2,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_BOXED, "GList **"));
-}
+ type = g_type_register_static(G_TYPE_OBJECT,
+ "PurpleConversation",
+ &info, G_TYPE_FLAG_ABSTRACT);
+ }
-void
-purple_conversations_uninit(void)
-{
- while (conversations)
- purple_conversation_destroy((PurpleConversation*)conversations->data);
- g_hash_table_destroy(conversation_cache);
- purple_signals_unregister_by_instance(purple_conversations_get_handle());
+ return type;
}
-
diff --git a/libpurple/conversation.h b/libpurple/conversation.h
index e01dc19145..2aa6783c65 100644
--- a/libpurple/conversation.h
+++ b/libpurple/conversation.h
@@ -1,9 +1,3 @@
-/**
- * @file conversation.h Conversation API
- * @ingroup core
- * @see @ref conversation-signals
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -24,954 +18,582 @@
* 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_CONVERSATION_H_
#define _PURPLE_CONVERSATION_H_
+/**
+ * SECTION:conversation
+ * @section_id: libpurple-conversation
+ * @short_description: <filename>conversation.h</filename>
+ * @title: Conversation Base Class
+ */
-/**************************************************************************/
-/** Data Structures */
-/**************************************************************************/
-
+#define PURPLE_TYPE_CONVERSATION (purple_conversation_get_type())
+#define PURPLE_CONVERSATION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_CONVERSATION, PurpleConversation))
+#define PURPLE_CONVERSATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_CONVERSATION, PurpleConversationClass))
+#define PURPLE_IS_CONVERSATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_CONVERSATION))
+#define PURPLE_IS_CONVERSATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_CONVERSATION))
+#define PURPLE_CONVERSATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_CONVERSATION, PurpleConversationClass))
-/** @copydoc _PurpleConversationUiOps */
-typedef struct _PurpleConversationUiOps PurpleConversationUiOps;
-/** @copydoc _PurpleConversation */
-typedef struct _PurpleConversation PurpleConversation;
-/** @copydoc _PurpleConvIm */
-typedef struct _PurpleConvIm PurpleConvIm;
-/** @copydoc _PurpleConvChat */
-typedef struct _PurpleConvChat PurpleConvChat;
-/** @copydoc _PurpleConvChatBuddy */
-typedef struct _PurpleConvChatBuddy PurpleConvChatBuddy;
-/** @copydoc _PurpleConvMessage */
-typedef struct _PurpleConvMessage PurpleConvMessage;
+#define PURPLE_TYPE_CONVERSATION_UI_OPS (purple_conversation_ui_ops_get_type())
-/**
- * A type of conversation.
- */
-typedef enum
-{
- PURPLE_CONV_TYPE_UNKNOWN = 0, /**< Unknown conversation type. */
- PURPLE_CONV_TYPE_IM, /**< Instant Message. */
- PURPLE_CONV_TYPE_CHAT, /**< Chat room. */
- PURPLE_CONV_TYPE_MISC, /**< A misc. conversation. */
- PURPLE_CONV_TYPE_ANY /**< Any type of conversation. */
+#define PURPLE_TYPE_CONVERSATION_MESSAGE (purple_conversation_message_get_type())
-} PurpleConversationType;
+/**************************************************************************/
+/** Data Structures */
+/**************************************************************************/
-/**
+typedef struct _PurpleConversation PurpleConversation;
+typedef struct _PurpleConversationClass PurpleConversationClass;
+
+typedef struct _PurpleConversationUiOps PurpleConversationUiOps;
+
+/**
+ * PurpleConversationUpdateType:
+ * @PURPLE_CONVERSATION_UPDATE_ADD: The buddy associated with the
+ * conversation was added.
+ * @PURPLE_CONVERSATION_UPDATE_REMOVE: The buddy associated with the
+ * conversation was removed.
+ * @PURPLE_CONVERSATION_UPDATE_ACCOUNT: The purple_account was changed.
+ * @PURPLE_CONVERSATION_UPDATE_TYPING: The typing state was updated.
+ * @PURPLE_CONVERSATION_UPDATE_UNSEEN: The unseen state was updated.
+ * @PURPLE_CONVERSATION_UPDATE_LOGGING: Logging for this conversation was
+ * enabled or disabled.
+ * @PURPLE_CONVERSATION_UPDATE_TOPIC: The topic for a chat was updated.
+ * @PURPLE_CONVERSATION_UPDATE_E2EE: The End-to-end encryption state was
+ * updated.
+ * @PURPLE_CONVERSATION_ACCOUNT_ONLINE: One of the user's accounts went online.
+ * @PURPLE_CONVERSATION_ACCOUNT_OFFLINE: One of the user's accounts went
+ * offline.
+ * @PURPLE_CONVERSATION_UPDATE_AWAY: The other user went away.
+ * @PURPLE_CONVERSATION_UPDATE_ICON: The other user's buddy icon changed.
+ * @PURPLE_CONVERSATION_UPDATE_FEATURES: The features for a chat have changed.
+ *
* Conversation update type.
*/
typedef enum
{
- PURPLE_CONV_UPDATE_ADD = 0, /**< The buddy associated with the conversation
- was added. */
- PURPLE_CONV_UPDATE_REMOVE, /**< The buddy associated with the conversation
- was removed. */
- PURPLE_CONV_UPDATE_ACCOUNT, /**< The purple_account was changed. */
- PURPLE_CONV_UPDATE_TYPING, /**< The typing state was updated. */
- PURPLE_CONV_UPDATE_UNSEEN, /**< The unseen state was updated. */
- PURPLE_CONV_UPDATE_LOGGING, /**< Logging for this conversation was
- enabled or disabled. */
- PURPLE_CONV_UPDATE_TOPIC, /**< The topic for a chat was updated. */
+ PURPLE_CONVERSATION_UPDATE_ADD = 0,
+ PURPLE_CONVERSATION_UPDATE_REMOVE,
+ PURPLE_CONVERSATION_UPDATE_ACCOUNT,
+ PURPLE_CONVERSATION_UPDATE_TYPING,
+ PURPLE_CONVERSATION_UPDATE_UNSEEN,
+ PURPLE_CONVERSATION_UPDATE_LOGGING,
+ PURPLE_CONVERSATION_UPDATE_TOPIC,
+ PURPLE_CONVERSATION_UPDATE_E2EE,
+
/*
* XXX These need to go when we implement a more generic core/UI event
* system.
*/
- PURPLE_CONV_ACCOUNT_ONLINE, /**< One of the user's accounts went online. */
- PURPLE_CONV_ACCOUNT_OFFLINE, /**< One of the user's accounts went offline. */
- PURPLE_CONV_UPDATE_AWAY, /**< The other user went away. */
- PURPLE_CONV_UPDATE_ICON, /**< The other user's buddy icon changed. */
- PURPLE_CONV_UPDATE_TITLE,
- PURPLE_CONV_UPDATE_CHATLEFT,
-
- PURPLE_CONV_UPDATE_FEATURES /**< The features for a chat have changed */
-
-} PurpleConvUpdateType;
-
-/**
- * The typing state of a user.
+ PURPLE_CONVERSATION_ACCOUNT_ONLINE,
+ PURPLE_CONVERSATION_ACCOUNT_OFFLINE,
+ PURPLE_CONVERSATION_UPDATE_AWAY,
+ PURPLE_CONVERSATION_UPDATE_ICON,
+ PURPLE_CONVERSATION_UPDATE_NAME,
+ PURPLE_CONVERSATION_UPDATE_TITLE,
+ PURPLE_CONVERSATION_UPDATE_CHATLEFT,
+
+ PURPLE_CONVERSATION_UPDATE_FEATURES
+
+} PurpleConversationUpdateType;
+
+/**
+ * PurpleMessageFlags:
+ * @PURPLE_MESSAGE_SEND: Outgoing message.
+ * @PURPLE_MESSAGE_RECV: Incoming message.
+ * @PURPLE_MESSAGE_SYSTEM: System message.
+ * @PURPLE_MESSAGE_AUTO_RESP: Auto response.
+ * @PURPLE_MESSAGE_ACTIVE_ONLY: Hint to the UI that this message should not be
+ * shown in conversations which are only open for
+ * internal UI purposes (e.g. for contact-aware
+ * conversations).
+ * @PURPLE_MESSAGE_NICK: Contains your nick.
+ * @PURPLE_MESSAGE_NO_LOG: Do not log.
+ * @PURPLE_MESSAGE_ERROR: Error message.
+ * @PURPLE_MESSAGE_DELAYED: Delayed message.
+ * @PURPLE_MESSAGE_RAW: "Raw" message - don't apply formatting
+ * @PURPLE_MESSAGE_IMAGES: Message contains images
+ * @PURPLE_MESSAGE_NOTIFY: Message is a notification
+ * @PURPLE_MESSAGE_NO_LINKIFY: Message should not be auto-linkified
+ * @PURPLE_MESSAGE_INVISIBLE: Message should not be displayed
+ *
+ * Flags applicable to a message. Most will have send, recv or system.
*/
-typedef enum
+typedef enum /*< flags >*/
{
- PURPLE_NOT_TYPING = 0, /**< Not typing. */
- PURPLE_TYPING, /**< Currently typing. */
- PURPLE_TYPED /**< Stopped typing momentarily. */
+ PURPLE_MESSAGE_SEND = 0x0001,
+ PURPLE_MESSAGE_RECV = 0x0002,
+ PURPLE_MESSAGE_SYSTEM = 0x0004,
+ PURPLE_MESSAGE_AUTO_RESP = 0x0008,
+ PURPLE_MESSAGE_ACTIVE_ONLY = 0x0010,
+ PURPLE_MESSAGE_NICK = 0x0020,
+ PURPLE_MESSAGE_NO_LOG = 0x0040,
+ PURPLE_MESSAGE_ERROR = 0x0200,
+ PURPLE_MESSAGE_DELAYED = 0x0400,
+ PURPLE_MESSAGE_RAW = 0x0800,
+ PURPLE_MESSAGE_IMAGES = 0x1000,
+ PURPLE_MESSAGE_NOTIFY = 0x2000,
+ PURPLE_MESSAGE_NO_LINKIFY = 0x4000,
+ PURPLE_MESSAGE_INVISIBLE = 0x8000
+} PurpleMessageFlags;
-} PurpleTypingState;
+#include <glib.h>
+#include <glib-object.h>
+#include "message.h"
+/**************************************************************************/
+/** PurpleConversation */
+/**************************************************************************/
/**
- * Flags applicable to a message. Most will have send, recv or system.
+ * PurpleConversation:
+ * @ui_data: The UI data associated with this conversation. This is a
+ * convenience field provided to the UIs -- it is not used by the
+ * libpurple core.
+ *
+ * A core representation of a conversation between two or more people.
+ *
+ * The conversation can be an IM or a chat.
+ *
+ * Note: When a conversation is destroyed with the last g_object_unref(), the
+ * specified conversation is removed from the parent window. If this
+ * conversation is the only one contained in the parent window, that
+ * window is also destroyed.
*/
-typedef enum
+struct _PurpleConversation
{
- PURPLE_MESSAGE_SEND = 0x0001, /**< Outgoing message. */
- PURPLE_MESSAGE_RECV = 0x0002, /**< Incoming message. */
- PURPLE_MESSAGE_SYSTEM = 0x0004, /**< System message. */
- PURPLE_MESSAGE_AUTO_RESP = 0x0008, /**< Auto response. */
- PURPLE_MESSAGE_ACTIVE_ONLY = 0x0010, /**< Hint to the UI that this
- message should not be
- shown in conversations
- which are only open for
- internal UI purposes
- (e.g. for contact-aware
- conversations). */
- PURPLE_MESSAGE_NICK = 0x0020, /**< Contains your nick. */
- PURPLE_MESSAGE_NO_LOG = 0x0040, /**< Do not log. */
- PURPLE_MESSAGE_WHISPER = 0x0080, /**< Whispered message. */
- PURPLE_MESSAGE_ERROR = 0x0200, /**< Error message. */
- PURPLE_MESSAGE_DELAYED = 0x0400, /**< Delayed message. */
- PURPLE_MESSAGE_RAW = 0x0800, /**< "Raw" message - don't
- apply formatting */
- PURPLE_MESSAGE_IMAGES = 0x1000, /**< Message contains images */
- PURPLE_MESSAGE_NOTIFY = 0x2000, /**< Message is a notification */
- PURPLE_MESSAGE_NO_LINKIFY = 0x4000, /**< Message should not be auto-
- linkified @since 2.1.0 */
- PURPLE_MESSAGE_INVISIBLE = 0x8000 /**< Message should not be displayed */
-} PurpleMessageFlags;
+ GObject gparent;
+
+ /*< public >*/
+ gpointer ui_data;
+};
/**
- * Flags applicable to users in Chats.
+ * PurpleConversationClass:
+ * @write_message: Writes a message to a chat or IM conversation. See
+ * purple_conversation_write_message().
+ *
+ * Base class for all #PurpleConversation's
*/
-typedef enum
-{
- PURPLE_CBFLAGS_NONE = 0x0000, /**< No flags */
- PURPLE_CBFLAGS_VOICE = 0x0001, /**< Voiced user or "Participant" */
- PURPLE_CBFLAGS_HALFOP = 0x0002, /**< Half-op */
- PURPLE_CBFLAGS_OP = 0x0004, /**< Channel Op or Moderator */
- PURPLE_CBFLAGS_FOUNDER = 0x0008, /**< Channel Founder */
- PURPLE_CBFLAGS_TYPING = 0x0010, /**< Currently typing */
- PURPLE_CBFLAGS_AWAY = 0x0020 /**< Currently away. @since 2.8.0 */
+struct _PurpleConversationClass {
+ GObjectClass parent_class;
-} PurpleConvChatBuddyFlags;
+ void (*write_message)(PurpleConversation *conv, PurpleMessage *msg);
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
#include "account.h"
#include "buddyicon.h"
+#include "e2ee.h"
#include "log.h"
-#include "server.h"
+#include "smiley-list.h"
+/**************************************************************************/
+/** PurpleConversationUiOps */
+/**************************************************************************/
/**
+ * PurpleConversationUiOps:
+ * @create_conversation: Called when @conv is created (but before the
+ * <link linkend="conversations-conversation-created"><literal>"conversation-created"</literal></link>
+ * signal is emitted).
+ * @destroy_conversation: Called just before @conv is freed.
+ * @write_chat: Write a message to a chat. If this field is %NULL, libpurple
+ * will fall back to using @write_conv.
+ * See purple_conversation_write().
+ * @write_im: Write a message to an IM conversation. If this field is %NULL,
+ * libpurple will fall back to using @write_conv.
+ * See purple_conversation_write().
+ * @write_conv: Write a message to a conversation. This is used rather than the
+ * chat- or im-specific ops for errors, system messages (such as "x
+ * is now know as y"), and as the fallback if @write_im and
+ * @write_chat are not implemented. It should be implemented, or
+ * the UI will miss conversation error messages and your users will
+ * hate you. See purple_conversation_write().
+ * @chat_add_users: Add @cbuddies to a chat.
+ * <sbr/>@cbuddies: A GList of #PurpleChatUser structs.
+ * <sbr/>@new_arrivals: Whether join notices should be shown.
+ * (Join notices are actually written to
+ * the conversation by
+ * purple_chat_conversation_add_users())
+ * @chat_rename_user: Rename the user in this chat named @old_name to @new_name.
+ * (The rename message is written to the conversation by
+ * libpurple.) See purple_chat_conversation_rename_user().
+ * <sbr/>@new_alias: @new_name's new alias, if they have one.
+ * @chat_remove_users: Remove @users from a chat @chat.
+ * See purple_chat_conversation_remove_users().
+ * @chat_update_user: Called when a user's flags are changed.
+ * See purple_chat_user_set_flags().
+ * @present: Present this conversation to the user; for example, by displaying
+ * the IM dialog.
+ * @has_focus: If this UI has a concept of focus (as in a windowing system) and
+ * this conversation has the focus, return %TRUE; otherwise, return
+ * %FALSE.
+ * @send_confirm: Prompt the user for confirmation to send @message. This
+ * function should arrange for the message to be sent if the user
+ * accepts. If this field is %NULL, libpurple will fall back to
+ * using purple_request_action().
+ *
* Conversation operations and events.
*
* Any UI representing a conversation must assign a filled-out
- * PurpleConversationUiOps structure to the PurpleConversation.
+ * #PurpleConversationUiOps structure to the #PurpleConversation.
*/
struct _PurpleConversationUiOps
{
- /** Called when @a conv is created (but before the @ref
- * conversation-created signal is emitted).
- */
void (*create_conversation)(PurpleConversation *conv);
-
- /** Called just before @a conv is freed. */
void (*destroy_conversation)(PurpleConversation *conv);
- /** Write a message to a chat. If this field is @c NULL, libpurple will
- * fall back to using #write_conv.
- * @see purple_conv_chat_write()
- */
- void (*write_chat)(PurpleConversation *conv, const char *who,
- const char *message, PurpleMessageFlags flags,
- time_t mtime);
- /** Write a message to an IM conversation. If this field is @c NULL,
- * libpurple will fall back to using #write_conv.
- * @see purple_conv_im_write()
- */
- void (*write_im)(PurpleConversation *conv, const char *who,
- const char *message, PurpleMessageFlags flags,
- time_t mtime);
- /** Write a message to a conversation. This is used rather than the
- * chat- or im-specific ops for errors, system messages (such as "x is
- * now know as y"), and as the fallback if #write_im and #write_chat
- * are not implemented. It should be implemented, or the UI will miss
- * conversation error messages and your users will hate you.
- *
- * @see purple_conversation_write()
- */
- void (*write_conv)(PurpleConversation *conv,
- const char *name,
- const char *alias,
- const char *message,
- PurpleMessageFlags flags,
- time_t mtime);
-
- /** Add @a cbuddies to a chat.
- * @param cbuddies A @c GList of #PurpleConvChatBuddy structs.
- * @param new_arrivals Whether join notices should be shown.
- * (Join notices are actually written to the
- * conversation by #purple_conv_chat_add_users().)
- */
- void (*chat_add_users)(PurpleConversation *conv,
+
+ void (*write_chat)(PurpleChatConversation *chat, PurpleMessage *msg);
+ void (*write_im)(PurpleIMConversation *im, PurpleMessage *msg);
+ void (*write_conv)(PurpleConversation *conv, PurpleMessage *msg);
+
+ void (*chat_add_users)(PurpleChatConversation *chat,
GList *cbuddies,
gboolean new_arrivals);
- /** Rename the user in this chat named @a old_name to @a new_name. (The
- * rename message is written to the conversation by libpurple.)
- * @param new_alias @a new_name's new alias, if they have one.
- * @see purple_conv_chat_add_users()
- */
- void (*chat_rename_user)(PurpleConversation *conv, const char *old_name,
+
+ void (*chat_rename_user)(PurpleChatConversation *chat, const char *old_name,
const char *new_name, const char *new_alias);
- /** Remove @a users from a chat.
- * @param users A @c GList of <tt>const char *</tt>s.
- * @see purple_conv_chat_rename_user()
- */
- void (*chat_remove_users)(PurpleConversation *conv, GList *users);
- /** Called when a user's flags are changed.
- * @see purple_conv_chat_user_set_flags()
- */
- void (*chat_update_user)(PurpleConversation *conv, const char *user);
- /** Present this conversation to the user; for example, by displaying
- * the IM dialog.
- */
- void (*present)(PurpleConversation *conv);
+ void (*chat_remove_users)(PurpleChatConversation *chat, GList *users);
- /** If this UI has a concept of focus (as in a windowing system) and
- * this conversation has the focus, return @c TRUE; otherwise, return
- * @c FALSE.
- */
- gboolean (*has_focus)(PurpleConversation *conv);
+ void (*chat_update_user)(PurpleChatUser *cb);
- /* Custom Smileys */
- gboolean (*custom_smiley_add)(PurpleConversation *conv, const char *smile, gboolean remote);
- void (*custom_smiley_write)(PurpleConversation *conv, const char *smile,
- const guchar *data, gsize size);
- void (*custom_smiley_close)(PurpleConversation *conv, const char *smile);
+ void (*present)(PurpleConversation *conv);
+ gboolean (*has_focus)(PurpleConversation *conv);
- /** Prompt the user for confirmation to send @a message. This function
- * should arrange for the message to be sent if the user accepts. If
- * this field is @c NULL, libpurple will fall back to using
- * #purple_request_action().
- */
void (*send_confirm)(PurpleConversation *conv, const char *message);
+ /*< private >*/
void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
void (*_purple_reserved4)(void);
};
-/**
- * Data specific to Instant Messages.
- */
-struct _PurpleConvIm
-{
- PurpleConversation *conv; /**< The parent conversation. */
-
- PurpleTypingState typing_state; /**< The current typing state. */
- guint typing_timeout; /**< The typing timer handle. */
- time_t type_again; /**< The type again time. */
- guint send_typed_timeout; /**< The type again timer handle. */
-
- PurpleBuddyIcon *icon; /**< The buddy icon. */
-};
-
-/**
- * Data specific to Chats.
- */
-struct _PurpleConvChat
-{
- PurpleConversation *conv; /**< The parent conversation. */
-
- GList *in_room; /**< The users in the room.
- * @deprecated Will be removed in 3.0.0
- */
- GList *ignored; /**< Ignored users. */
- char *who; /**< The person who set the topic. */
- char *topic; /**< The topic. */
- int id; /**< The chat ID. */
- char *nick; /**< Your nick in this chat. */
-
- gboolean left; /**< We left the chat and kept the window open */
- GHashTable *users; /**< Hash table of the users in the room.
- * @since 2.9.0
- */
-};
-
-/**
- * Data for "Chat Buddies"
- */
-struct _PurpleConvChatBuddy
-{
- char *name; /**< The chat participant's name in the chat. */
- char *alias; /**< The chat participant's alias, if known;
- * @a NULL otherwise.
- */
- char *alias_key; /**< A string by which this buddy will be sorted,
- * or @c NULL if the buddy should be sorted by
- * its @c name. (This is currently always @c
- * NULL.)
- */
- gboolean buddy; /**< @a TRUE if this chat participant is on the
- * buddy list; @a FALSE otherwise.
- */
- PurpleConvChatBuddyFlags flags; /**< A bitwise OR of flags for this participant,
- * such as whether they are a channel operator.
- */
- GHashTable *attributes; /**< A hash table of attributes about the user, such as
- * real name, user@host, etc.
- */
- gpointer ui_data; /** < The UI can put whatever it wants here. */
-};
-
-/**
- * Description of a conversation message
- *
- * @since 2.2.0
- */
-struct _PurpleConvMessage
-{
- char *who;
- char *what;
- PurpleMessageFlags flags;
- time_t when;
- PurpleConversation *conv; /**< @since 2.3.0 */
- char *alias; /**< @since 2.3.0 */
-};
-
-/**
- * A core representation of a conversation between two or more people.
- *
- * The conversation can be an IM or a chat.
- */
-struct _PurpleConversation
-{
- PurpleConversationType type; /**< The type of conversation. */
-
- PurpleAccount *account; /**< The user using this conversation. */
-
-
- char *name; /**< The name of the conversation. */
- char *title; /**< The window title. */
-
- gboolean logging; /**< The status of logging. */
-
- GList *logs; /**< This conversation's logs */
-
- union
- {
- PurpleConvIm *im; /**< IM-specific data. */
- PurpleConvChat *chat; /**< Chat-specific data. */
- void *misc; /**< Misc. data. */
-
- } u;
-
- PurpleConversationUiOps *ui_ops; /**< UI-specific operations. */
- void *ui_data; /**< UI-specific data. */
-
- GHashTable *data; /**< Plugin-specific data. */
-
- PurpleConnectionFlags features; /**< The supported features */
- GList *message_history; /**< Message history, as a GList of PurpleConvMessage's */
-};
-
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name Conversation API */
+/* Conversation API */
/**************************************************************************/
-/*@{*/
/**
- * Creates a new conversation of the specified type.
+ * purple_conversation_get_type:
*
- * @param type The type of conversation.
- * @param account The account opening the conversation window on the purple
- * user's end.
- * @param name The name of the conversation. For PURPLE_CONV_TYPE_IM,
- * this is the name of the buddy.
- *
- * @return The new conversation.
+ * Returns: The #GType for the Conversation object.
*/
-PurpleConversation *purple_conversation_new(PurpleConversationType type,
- PurpleAccount *account,
- const char *name);
+GType purple_conversation_get_type(void);
/**
- * Destroys the specified conversation and removes it from the parent
- * window.
- *
- * If this conversation is the only one contained in the parent window,
- * that window is also destroyed.
+ * purple_conversation_ui_ops_get_type:
*
- * @param conv The conversation to destroy.
+ * Returns: The #GType for the #PurpleConversationUiOps boxed structure.
*/
-void purple_conversation_destroy(PurpleConversation *conv);
-
+GType purple_conversation_ui_ops_get_type(void);
/**
+ * purple_conversation_present:
+ * @conv: The conversation to present
+ *
* Present a conversation to the user. This allows core code to initiate a
* conversation by displaying the IM dialog.
- * @param conv The conversation to present
*/
void purple_conversation_present(PurpleConversation *conv);
-
/**
- * Returns the specified conversation's type.
- *
- * @param conv The conversation.
+ * purple_conversation_set_ui_ops:
+ * @conv: The conversation.
+ * @ops: The UI conversation operations structure.
*
- * @return The conversation's type.
- */
-PurpleConversationType purple_conversation_get_type(const PurpleConversation *conv);
-
-/**
* Sets the specified conversation's UI operations structure.
- *
- * @param conv The conversation.
- * @param ops The UI conversation operations structure.
*/
void purple_conversation_set_ui_ops(PurpleConversation *conv,
PurpleConversationUiOps *ops);
/**
- * Sets the default conversation UI operations structure.
+ * purple_conversation_get_ui_ops:
+ * @conv: The conversation.
*
- * @param ops The UI conversation operations structure.
- */
-void purple_conversations_set_ui_ops(PurpleConversationUiOps *ops);
-
-/**
* Returns the specified conversation's UI operations structure.
*
- * @param conv The conversation.
- *
- * @return The operations structure.
+ * Returns: The operations structure.
*/
-PurpleConversationUiOps *purple_conversation_get_ui_ops(
- const PurpleConversation *conv);
+PurpleConversationUiOps *purple_conversation_get_ui_ops(const PurpleConversation *conv);
/**
+ * purple_conversation_set_account:
+ * @conv: The conversation.
+ * @account: The purple_account.
+ *
* Sets the specified conversation's purple_account.
*
* This purple_account represents the user using purple, not the person the user
* is having a conversation/chat/flame with.
- *
- * @param conv The conversation.
- * @param account The purple_account.
*/
void purple_conversation_set_account(PurpleConversation *conv,
PurpleAccount *account);
/**
+ * purple_conversation_get_account:
+ * @conv: The conversation.
+ *
* Returns the specified conversation's purple_account.
*
* This purple_account represents the user using purple, not the person the user
* is having a conversation/chat/flame with.
*
- * @param conv The conversation.
- *
- * @return The conversation's purple_account.
+ * Returns: The conversation's purple_account.
*/
PurpleAccount *purple_conversation_get_account(const PurpleConversation *conv);
/**
- * Returns the specified conversation's purple_connection.
+ * purple_conversation_get_connection:
+ * @conv: The conversation.
*
- * This is the same as purple_conversation_get_user(conv)->gc.
- *
- * @param conv The conversation.
+ * Returns the specified conversation's purple_connection.
*
- * @return The conversation's purple_connection.
+ * Returns: The conversation's purple_connection.
*/
-PurpleConnection *purple_conversation_get_gc(const PurpleConversation *conv);
+PurpleConnection *purple_conversation_get_connection(const PurpleConversation *conv);
/**
- * Sets the specified conversation's title.
+ * purple_conversation_set_title:
+ * @conv: The conversation.
+ * @title: The title.
*
- * @param conv The conversation.
- * @param title The title.
+ * Sets the specified conversation's title.
*/
void purple_conversation_set_title(PurpleConversation *conv, const char *title);
/**
- * Returns the specified conversation's title.
+ * purple_conversation_get_title:
+ * @conv: The conversation.
*
- * @param conv The conversation.
+ * Returns the specified conversation's title.
*
- * @return The title.
+ * Returns: The title.
*/
const char *purple_conversation_get_title(const PurpleConversation *conv);
/**
+ * purple_conversation_autoset_title:
+ * @conv: The conversation.
+ *
* Automatically sets the specified conversation's title.
*
* This function takes OPT_IM_ALIAS_TAB into account, as well as the
* user's alias.
- *
- * @param conv The conversation.
*/
void purple_conversation_autoset_title(PurpleConversation *conv);
/**
- * Sets the specified conversation's name.
+ * purple_conversation_set_name:
+ * @conv: The conversation.
+ * @name: The conversation's name.
*
- * @param conv The conversation.
- * @param name The conversation's name.
+ * Sets the specified conversation's name.
*/
void purple_conversation_set_name(PurpleConversation *conv, const char *name);
/**
- * Returns the specified conversation's name.
+ * purple_conversation_get_name:
+ * @conv: The conversation.
*
- * @param conv The conversation.
+ * Returns the specified conversation's name.
*
- * @return The conversation's name. If the conversation is an IM with a PurpleBuddy,
- * then it's the name of the PurpleBuddy.
+ * Returns: The conversation's name. If the conversation is an IM with a
+ * PurpleBuddy, then it's the name of the PurpleBuddy.
*/
const char *purple_conversation_get_name(const PurpleConversation *conv);
/**
- * Get an attribute of a chat buddy
+ * purple_conversation_set_e2ee_state:
+ * @conv: The conversation.
+ * @state: The E2EE state.
*
- * @param cb The chat buddy.
- * @param key The key of the attribute.
- *
- * @return The value of the attribute key.
+ * Sets current E2EE state for the conversation.
*/
-const char *purple_conv_chat_cb_get_attribute(PurpleConvChatBuddy *cb, const char *key);
+void
+purple_conversation_set_e2ee_state(PurpleConversation *conv,
+ PurpleE2eeState *state);
/**
- * Get the keys of all atributes of a chat buddy
+ * purple_conversation_get_e2ee_state:
+ * @conv: The conversation.
*
- * @param cb The chat buddy.
+ * Gets current conversation's E2EE state.
*
- * @return A list of the attributes of a chat buddy.
+ * Returns: Current E2EE state for conversation.
*/
-GList *purple_conv_chat_cb_get_attribute_keys(PurpleConvChatBuddy *cb);
-
-/**
- * Set an attribute of a chat buddy
- *
- * @param chat The chat.
- * @param cb The chat buddy.
- * @param key The key of the attribute.
- * @param value The value of the attribute.
- */
-void purple_conv_chat_cb_set_attribute(PurpleConvChat *chat, PurpleConvChatBuddy *cb, const char *key, const char *value);
+PurpleE2eeState *
+purple_conversation_get_e2ee_state(PurpleConversation *conv);
/**
- * Set attributes of a chat buddy
+ * purple_conversation_set_logging:
+ * @conv: The conversation.
+ * @log: %TRUE if logging should be enabled, or %FALSE otherwise.
*
- * @param chat The chat.
- * @param cb The chat buddy.
- * @param keys A GList of the keys.
- * @param values A GList of the values.
- */
-void
-purple_conv_chat_cb_set_attributes(PurpleConvChat *chat, PurpleConvChatBuddy *cb, GList *keys, GList *values);
-
-/**
* Enables or disables logging for this conversation.
- *
- * @param conv The conversation.
- * @param log @c TRUE if logging should be enabled, or @c FALSE otherwise.
*/
void purple_conversation_set_logging(PurpleConversation *conv, gboolean log);
/**
- * Returns whether or not logging is enabled for this conversation.
+ * purple_conversation_is_logging:
+ * @conv: The conversation.
*
- * @param conv The conversation.
+ * Returns whether or not logging is enabled for this conversation.
*
- * @return @c TRUE if logging is enabled, or @c FALSE otherwise.
+ * Returns: %TRUE if logging is enabled, or %FALSE otherwise.
*/
gboolean purple_conversation_is_logging(const PurpleConversation *conv);
/**
+ * purple_conversation_close_logs:
+ * @conv: The conversation.
+ *
* Closes any open logs for this conversation.
*
* Note that new logs will be opened as necessary (e.g. upon receipt of a
* message, if the conversation has logging enabled. To disable logging for
* the remainder of the conversation, use purple_conversation_set_logging().
- *
- * @param conv The conversation.
*/
void purple_conversation_close_logs(PurpleConversation *conv);
/**
- * Returns the specified conversation's IM-specific data.
- *
- * If the conversation type is not PURPLE_CONV_TYPE_IM, this will return @c NULL.
- *
- * @param conv The conversation.
- *
- * @return The IM-specific data.
- */
-PurpleConvIm *purple_conversation_get_im_data(const PurpleConversation *conv);
-
-#define PURPLE_CONV_IM(c) (purple_conversation_get_im_data(c))
-
-/**
- * Returns the specified conversation's chat-specific data.
- *
- * If the conversation type is not PURPLE_CONV_TYPE_CHAT, this will return @c NULL.
- *
- * @param conv The conversation.
- *
- * @return The chat-specific data.
- */
-PurpleConvChat *purple_conversation_get_chat_data(const PurpleConversation *conv);
-
-#define PURPLE_CONV_CHAT(c) (purple_conversation_get_chat_data(c))
-
-/**
- * Sets extra data for a conversation.
+ * purple_conversation_write_message:
+ * @conv: The conversation.
+ * @who: The user who sent the message.
+ * @message: The message to write.
+ * @flags: The message flags.
+ * @mtime: The time the message was sent.
*
- * @param conv The conversation.
- * @param key The unique key.
- * @param data The data to assign.
+ * Writes to a chat or an IM.
*/
-void purple_conversation_set_data(PurpleConversation *conv, const char *key,
- gpointer data);
+void purple_conversation_write_message(PurpleConversation *conv,
+ PurpleMessage *msg);
/**
- * Returns extra data in a conversation.
+ * purple_conversation_write_system_message:
+ * @conv: The conversation.
+ * @message: The message to write.
+ * @flags: The message flags (you don't need to set %PURPLE_MESSAGE_SYSTEM.
*
- * @param conv The conversation.
- * @param key The unqiue key.
- *
- * @return The data associated with the key.
+ * Wites a system message to a chat or an IM.
*/
-gpointer purple_conversation_get_data(PurpleConversation *conv, const char *key);
+void purple_conversation_write_system_message(PurpleConversation *conv,
+ const gchar *message, PurpleMessageFlags flags);
/**
- * Returns a list of all conversations.
- *
- * This list includes both IMs and chats.
+ * purple_conversation_send:
+ * @conv: The conversation.
+ * @message: The message to send.
*
- * @constreturn A GList of all conversations.
+ * Sends a message to this conversation. This function calls
+ * purple_conversation_send_with_flags() with no additional flags.
*/
-GList *purple_get_conversations(void);
+void purple_conversation_send(PurpleConversation *conv, const char *message);
/**
- * Returns a list of all IMs.
- *
- * @constreturn A GList of all IMs.
- */
-GList *purple_get_ims(void);
-
-/**
- * Returns a list of all chats.
- *
- * @constreturn A GList of all chats.
- */
-GList *purple_get_chats(void);
-
-/**
- * Finds a conversation with the specified type, name, and Purple account.
- *
- * @param type The type of the conversation.
- * @param name The name of the conversation.
- * @param account The purple_account associated with the conversation.
+ * purple_conversation_send_with_flags:
+ * @conv: The conversation.
+ * @message: The message to send.
+ * @flags: The PurpleMessageFlags flags to use in addition to
+ * PURPLE_MESSAGE_SEND.
*
- * @return The conversation if found, or @c NULL otherwise.
+ * Sends a message to this conversation with specified flags.
*/
-PurpleConversation *purple_find_conversation_with_account(
- PurpleConversationType type, const char *name,
- const PurpleAccount *account);
+void purple_conversation_send_with_flags(PurpleConversation *conv, const char *message,
+ PurpleMessageFlags flags);
/**
- * Writes to a conversation window.
+ * purple_conversation_set_features:
+ * @conv: The conversation
+ * @features: Bitset defining supported features
*
- * This function should not be used to write IM or chat messages. Use
- * purple_conv_im_write() and purple_conv_chat_write() instead. Those functions will
- * most likely call this anyway, but they may do their own formatting,
- * sound playback, etc.
- *
- * This can be used to write generic messages, such as "so and so closed
- * the conversation window."
- *
- * @param conv The conversation.
- * @param who The user who sent the message.
- * @param message The message.
- * @param flags The message flags.
- * @param mtime The time the message was sent.
- *
- * @see purple_conv_im_write()
- * @see purple_conv_chat_write()
+ * Set the features as supported for the given conversation.
*/
-void purple_conversation_write(PurpleConversation *conv, const char *who,
- const char *message, PurpleMessageFlags flags,
- time_t mtime);
-
-/**
- Set the features as supported for the given conversation.
- @param conv The conversation
- @param features Bitset defining supported features
-*/
void purple_conversation_set_features(PurpleConversation *conv,
PurpleConnectionFlags features);
/**
- Get the features supported by the given conversation.
- @param conv The conversation
-*/
+ * purple_conversation_get_features:
+ * @conv: The conversation
+ *
+ * Get the features supported by the given conversation.
+ */
PurpleConnectionFlags purple_conversation_get_features(PurpleConversation *conv);
/**
- * Determines if a conversation has focus
+ * purple_conversation_has_focus:
+ * @conv: The conversation.
*
- * @param conv The conversation.
+ * Determines if a conversation has focus
*
- * @return @c TRUE if the conversation has focus, @c FALSE if
+ * Returns: %TRUE if the conversation has focus, %FALSE if
* it does not or the UI does not have a concept of conversation focus
*/
gboolean purple_conversation_has_focus(PurpleConversation *conv);
/**
- * Updates the visual status and UI of a conversation.
+ * purple_conversation_update:
+ * @conv: The conversation.
+ * @type: The update type.
*
- * @param conv The conversation.
- * @param type The update type.
+ * Updates the visual status and UI of a conversation.
*/
-void purple_conversation_update(PurpleConversation *conv, PurpleConvUpdateType type);
+void purple_conversation_update(PurpleConversation *conv, PurpleConversationUpdateType type);
/**
- * Calls a function on each conversation.
+ * purple_conversation_get_message_history:
+ * @conv: The conversation
*
- * @param func The function.
- */
-void purple_conversation_foreach(void (*func)(PurpleConversation *conv));
-
-/**
* Retrieve the message history of a conversation.
*
- * @param conv The conversation
- *
- * @return A GList of PurpleConvMessage's. The must not modify the list or the data within.
- * The list contains the newest message at the beginning, and the oldest message at
- * the end.
- *
- * @since 2.2.0
+ * Returns: (transfer none): A GList of PurpleMessage's. You must not modify the
+ * list or the data within. The list contains the newest message at
+ * the beginning, and the oldest message at the end.
*/
GList *purple_conversation_get_message_history(PurpleConversation *conv);
/**
- * Clear the message history of a conversation.
- *
- * @param conv The conversation
+ * purple_conversation_clear_message_history:
+ * @conv: The conversation
*
- * @since 2.2.0
+ * Clear the message history of a conversation.
*/
void purple_conversation_clear_message_history(PurpleConversation *conv);
/**
- * Get the sender from a PurpleConvMessage
- *
- * @param msg A PurpleConvMessage
+ * purple_conversation_set_ui_data:
+ * @conv: The conversation.
+ * @ui_data: A pointer to associate with this conversation.
*
- * @return The name of the sender of the message
- *
- * @since 2.2.0
+ * Set the UI data associated with this conversation.
*/
-const char *purple_conversation_message_get_sender(PurpleConvMessage *msg);
+void purple_conversation_set_ui_data(PurpleConversation *conv, gpointer ui_data);
/**
- * Get the message from a PurpleConvMessage
- *
- * @param msg A PurpleConvMessage
+ * purple_conversation_get_ui_data:
+ * @conv: The conversation.
*
- * @return The name of the sender of the message
+ * Get the UI data associated with this conversation.
*
- * @since 2.2.0
+ * Returns: The UI data associated with this conversation. This is a
+ * convenience field provided to the UIs--it is not
+ * used by the libpurple core.
*/
-const char *purple_conversation_message_get_message(PurpleConvMessage *msg);
+gpointer purple_conversation_get_ui_data(const PurpleConversation *conv);
/**
- * Get the message-flags of a PurpleConvMessage
- *
- * @param msg A PurpleConvMessage
- *
- * @return The message flags
+ * purple_conversation_send_confirm:
+ * @conv: The conversation.
+ * @message: The message to send.
*
- * @since 2.2.0
- */
-PurpleMessageFlags purple_conversation_message_get_flags(PurpleConvMessage *msg);
-
-/**
- * Get the timestamp of a PurpleConvMessage
- *
- * @param msg A PurpleConvMessage
- *
- * @return The timestamp of the message
- *
- * @since 2.2.0
- */
-time_t purple_conversation_message_get_timestamp(PurpleConvMessage *msg);
-
-/*@}*/
-
-
-/**************************************************************************/
-/** @name IM Conversation API */
-/**************************************************************************/
-/*@{*/
-
-/**
- * Gets an IM's parent conversation.
- *
- * @param im The IM.
- *
- * @return The parent conversation.
- */
-PurpleConversation *purple_conv_im_get_conversation(const PurpleConvIm *im);
-
-/**
- * Sets the IM's buddy icon.
- *
- * This should only be called from within Purple. You probably want to
- * call purple_buddy_icon_set_data().
- *
- * @param im The IM.
- * @param icon The buddy icon.
- *
- * @see purple_buddy_icon_set_data()
- */
-void purple_conv_im_set_icon(PurpleConvIm *im, PurpleBuddyIcon *icon);
-
-/**
- * Returns the IM's buddy icon.
- *
- * @param im The IM.
- *
- * @return The buddy icon.
- */
-PurpleBuddyIcon *purple_conv_im_get_icon(const PurpleConvIm *im);
-
-/**
- * Sets the IM's typing state.
- *
- * @param im The IM.
- * @param state The typing state.
- */
-void purple_conv_im_set_typing_state(PurpleConvIm *im, PurpleTypingState state);
-
-/**
- * Returns the IM's typing state.
- *
- * @param im The IM.
- *
- * @return The IM's typing state.
- */
-PurpleTypingState purple_conv_im_get_typing_state(const PurpleConvIm *im);
-
-/**
- * Starts the IM's typing timeout.
- *
- * @param im The IM.
- * @param timeout The timeout.
- */
-void purple_conv_im_start_typing_timeout(PurpleConvIm *im, int timeout);
-
-/**
- * Stops the IM's typing timeout.
- *
- * @param im The IM.
- */
-void purple_conv_im_stop_typing_timeout(PurpleConvIm *im);
-
-/**
- * Returns the IM's typing timeout.
- *
- * @param im The IM.
- *
- * @return The timeout.
- */
-guint purple_conv_im_get_typing_timeout(const PurpleConvIm *im);
-
-/**
- * Sets the quiet-time when no PURPLE_TYPING messages will be sent.
- * Few protocols need this (maybe only MSN). If the user is still
- * typing after this quiet-period, then another PURPLE_TYPING message
- * will be sent.
- *
- * @param im The IM.
- * @param val The number of seconds to wait before allowing another
- * PURPLE_TYPING message to be sent to the user. Or 0 to
- * not send another PURPLE_TYPING message.
- */
-void purple_conv_im_set_type_again(PurpleConvIm *im, unsigned int val);
-
-/**
- * Returns the time after which another PURPLE_TYPING message should be sent.
- *
- * @param im The IM.
- *
- * @return The time in seconds since the epoch. Or 0 if no additional
- * PURPLE_TYPING message should be sent.
- */
-time_t purple_conv_im_get_type_again(const PurpleConvIm *im);
-
-/**
- * Starts the IM's type again timeout.
- *
- * @param im The IM.
- */
-void purple_conv_im_start_send_typed_timeout(PurpleConvIm *im);
-
-/**
- * Stops the IM's type again timeout.
- *
- * @param im The IM.
- */
-void purple_conv_im_stop_send_typed_timeout(PurpleConvIm *im);
-
-/**
- * Returns the IM's type again timeout interval.
- *
- * @param im The IM.
- *
- * @return The type again timeout interval.
- */
-guint purple_conv_im_get_send_typed_timeout(const PurpleConvIm *im);
-
-/**
- * Updates the visual typing notification for an IM conversation.
- *
- * @param im The IM.
- */
-void purple_conv_im_update_typing(PurpleConvIm *im);
-
-/**
- * Writes to an IM.
- *
- * @param im The IM.
- * @param who The user who sent the message.
- * @param message The message to write.
- * @param flags The message flags.
- * @param mtime The time the message was sent.
- */
-void purple_conv_im_write(PurpleConvIm *im, const char *who,
- const char *message, PurpleMessageFlags flags,
- time_t mtime);
-
-/**
- * Presents an IM-error to the user
- *
- * This is a helper function to find a conversation, write an error to it, and
- * raise the window. If a conversation with this user doesn't already exist,
- * the function will return FALSE and the calling function can attempt to present
- * the error another way (purple_notify_error, most likely)
- *
- * @param who The user this error is about
- * @param account The account this error is on
- * @param what The error
- * @return TRUE if the error was presented, else FALSE
- */
-gboolean purple_conv_present_error(const char *who, PurpleAccount *account, const char *what);
-
-/**
- * Sends a message to this IM conversation.
- *
- * @param im The IM.
- * @param message The message to send.
- */
-void purple_conv_im_send(PurpleConvIm *im, const char *message);
-
-/**
* Sends a message to a conversation after confirming with
* the user.
*
@@ -979,498 +601,117 @@ void purple_conv_im_send(PurpleConvIm *im, const char *message);
* hasn't explicitly and knowingly caused a message to be sent.
* The confirmation ensures that the user isn't sending a
* message by mistake.
- *
- * @param conv The conversation.
- * @param message The message to send.
- */
-void purple_conv_send_confirm(PurpleConversation *conv, const char *message);
-
-/**
- * Sends a message to this IM conversation with specified flags.
- *
- * @param im The IM.
- * @param message The message to send.
- * @param flags The PurpleMessageFlags flags to use in addition to PURPLE_MESSAGE_SEND.
- */
-void purple_conv_im_send_with_flags(PurpleConvIm *im, const char *message, PurpleMessageFlags flags);
-
-/**
- * Adds a smiley to the conversation's smiley tree. If this returns
- * @c TRUE you should call purple_conv_custom_smiley_write() one or more
- * times, and then purple_conv_custom_smiley_close(). If this returns
- * @c FALSE, either the conv or smile were invalid, or the icon was
- * found in the cache. In either case, calling write or close would
- * be an error.
- *
- * @param conv The conversation to associate the smiley with.
- * @param smile The text associated with the smiley
- * @param cksum_type The type of checksum.
- * @param chksum The checksum, as a NUL terminated base64 string.
- * @param remote @c TRUE if the custom smiley is set by the remote user (buddy).
- * @return @c TRUE if an icon is expected, else FALSE. Note that
- * it is an error to never call purple_conv_custom_smiley_close if
- * this function returns @c TRUE, but an error to call it if
- * @c FALSE is returned.
- */
-
-gboolean purple_conv_custom_smiley_add(PurpleConversation *conv, const char *smile,
- const char *cksum_type, const char *chksum,
- gboolean remote);
-
-
-/**
- * Updates the image associated with the current smiley.
- *
- * @param conv The conversation associated with the smiley.
- * @param smile The text associated with the smiley.
- * @param data The actual image data.
- * @param size The length of the data.
- */
-
-void purple_conv_custom_smiley_write(PurpleConversation *conv,
- const char *smile,
- const guchar *data,
- gsize size);
-
-/**
- * Close the custom smiley, all data has been written with
- * purple_conv_custom_smiley_write, and it is no longer valid
- * to call that function on that smiley.
- *
- * @param conv The purple conversation associated with the smiley.
- * @param smile The text associated with the smiley
- */
-
-void purple_conv_custom_smiley_close(PurpleConversation *conv, const char *smile);
-
-/*@}*/
-
-
-/**************************************************************************/
-/** @name Chat Conversation API */
-/**************************************************************************/
-/*@{*/
-
-/**
- * Gets a chat's parent conversation.
- *
- * @param chat The chat.
- *
- * @return The parent conversation.
- */
-PurpleConversation *purple_conv_chat_get_conversation(const PurpleConvChat *chat);
-
-/**
- * Sets the list of users in the chat room.
- *
- * @note Calling this function will not update the display of the users.
- * Please use purple_conv_chat_add_user(), purple_conv_chat_add_users(),
- * purple_conv_chat_remove_user(), and purple_conv_chat_remove_users() instead.
- *
- * @param chat The chat.
- * @param users The list of users.
- *
- * @return The list passed.
- *
- * @deprecated This function will be removed in 3.0.0. You shouldn't be using it anyway.
- */
-GList *purple_conv_chat_set_users(PurpleConvChat *chat, GList *users);
-
-/**
- * Returns a list of users in the chat room. The members of the list
- * are PurpleConvChatBuddy objects.
- *
- * @param chat The chat.
- *
- * @constreturn The list of users.
- */
-GList *purple_conv_chat_get_users(const PurpleConvChat *chat);
-
-/**
- * Ignores a user in a chat room.
- *
- * @param chat The chat.
- * @param name The name of the user.
- */
-void purple_conv_chat_ignore(PurpleConvChat *chat, const char *name);
-
-/**
- * Unignores a user in a chat room.
- *
- * @param chat The chat.
- * @param name The name of the user.
- */
-void purple_conv_chat_unignore(PurpleConvChat *chat, const char *name);
-
-/**
- * Sets the list of ignored users in the chat room.
- *
- * @param chat The chat.
- * @param ignored The list of ignored users.
- *
- * @return The list passed.
- */
-GList *purple_conv_chat_set_ignored(PurpleConvChat *chat, GList *ignored);
-
-/**
- * Returns the list of ignored users in the chat room.
- *
- * @param chat The chat.
- *
- * @constreturn The list of ignored users.
- */
-GList *purple_conv_chat_get_ignored(const PurpleConvChat *chat);
-
-/**
- * Returns the actual name of the specified ignored user, if it exists in
- * the ignore list.
- *
- * If the user found contains a prefix, such as '+' or '\@', this is also
- * returned. The username passed to the function does not have to have this
- * formatting.
- *
- * @param chat The chat.
- * @param user The user to check in the ignore list.
- *
- * @return The ignored user if found, complete with prefixes, or @c NULL
- * if not found.
- */
-const char *purple_conv_chat_get_ignored_user(const PurpleConvChat *chat,
- const char *user);
-
-/**
- * Returns @c TRUE if the specified user is ignored.
- *
- * @param chat The chat.
- * @param user The user.
- *
- * @return @c TRUE if the user is in the ignore list; @c FALSE otherwise.
- */
-gboolean purple_conv_chat_is_user_ignored(const PurpleConvChat *chat,
- const char *user);
-
-/**
- * Sets the chat room's topic.
- *
- * @param chat The chat.
- * @param who The user that set the topic.
- * @param topic The topic.
- */
-void purple_conv_chat_set_topic(PurpleConvChat *chat, const char *who,
- const char *topic);
-
-/**
- * Returns the chat room's topic.
- *
- * @param chat The chat.
- *
- * @return The chat's topic.
- */
-const char *purple_conv_chat_get_topic(const PurpleConvChat *chat);
-
-/**
- * Sets the chat room's ID.
- *
- * @param chat The chat.
- * @param id The ID.
- */
-void purple_conv_chat_set_id(PurpleConvChat *chat, int id);
-
-/**
- * Returns the chat room's ID.
- *
- * @param chat The chat.
- *
- * @return The ID.
- */
-int purple_conv_chat_get_id(const PurpleConvChat *chat);
-
-/**
- * Writes to a chat.
- *
- * @param chat The chat.
- * @param who The user who sent the message.
- * @param message The message to write.
- * @param flags The flags.
- * @param mtime The time the message was sent.
- */
-void purple_conv_chat_write(PurpleConvChat *chat, const char *who,
- const char *message, PurpleMessageFlags flags,
- time_t mtime);
-
-/**
- * Sends a message to this chat conversation.
- *
- * @param chat The chat.
- * @param message The message to send.
- */
-void purple_conv_chat_send(PurpleConvChat *chat, const char *message);
-
-/**
- * Sends a message to this chat conversation with specified flags.
- *
- * @param chat The chat.
- * @param message The message to send.
- * @param flags The PurpleMessageFlags flags to use.
- */
-void purple_conv_chat_send_with_flags(PurpleConvChat *chat, const char *message, PurpleMessageFlags flags);
-
-/**
- * Adds a user to a chat.
- *
- * @param chat The chat.
- * @param user The user to add.
- * @param extra_msg An extra message to display with the join message.
- * @param flags The users flags
- * @param new_arrival Decides whether or not to show a join notice.
- */
-void purple_conv_chat_add_user(PurpleConvChat *chat, const char *user,
- const char *extra_msg, PurpleConvChatBuddyFlags flags,
- gboolean new_arrival);
-
-/**
- * Adds a list of users to a chat.
- *
- * The data is copied from @a users, @a extra_msgs, and @a flags, so it is up to
- * the caller to free this list after calling this function.
- *
- * @param chat The chat.
- * @param users The list of users to add.
- * @param extra_msgs An extra message to display with the join message for each
- * user. This list may be shorter than @a 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 @c NULL and none of the users will have an
- * extra message.
- * @param flags The list of flags for each user.
- * @param new_arrivals Decides whether or not to show join notices.
- */
-void purple_conv_chat_add_users(PurpleConvChat *chat, GList *users, GList *extra_msgs,
- GList *flags, gboolean new_arrivals);
-
-/**
- * Renames a user in a chat.
- *
- * @param chat The chat.
- * @param old_user The old username.
- * @param new_user The new username.
- */
-void purple_conv_chat_rename_user(PurpleConvChat *chat, const char *old_user,
- const char *new_user);
-
-/**
- * Removes a user from a chat, optionally with a reason.
- *
- * It is up to the developer to free this list after calling this function.
- *
- * @param chat The chat.
- * @param user The user that is being removed.
- * @param reason The optional reason given for the removal. Can be @c NULL.
- */
-void purple_conv_chat_remove_user(PurpleConvChat *chat, const char *user,
- const char *reason);
-
-/**
- * Removes a list of users from a chat, optionally with a single reason.
- *
- * @param chat The chat.
- * @param users The users that are being removed.
- * @param reason The optional reason given for the removal. Can be @c NULL.
- */
-void purple_conv_chat_remove_users(PurpleConvChat *chat, GList *users,
- const char *reason);
-
-/**
- * Finds a user in a chat
- *
- * @param chat The chat.
- * @param user The user to look for.
- *
- * @return TRUE if the user is in the chat, FALSE if not
- */
-gboolean purple_conv_chat_find_user(PurpleConvChat *chat, const char *user);
-
-/**
- * Set a users flags in a chat
- *
- * @param chat The chat.
- * @param user The user to update.
- * @param flags The new flags.
- */
-void purple_conv_chat_user_set_flags(PurpleConvChat *chat, const char *user,
- PurpleConvChatBuddyFlags flags);
-
-/**
- * Get the flags for a user in a chat
- *
- * @param chat The chat.
- * @param user The user to find the flags for
- *
- * @return The flags for the user
- */
-PurpleConvChatBuddyFlags purple_conv_chat_user_get_flags(PurpleConvChat *chat,
- const char *user);
-
-/**
- * Clears all users from a chat.
- *
- * @param chat The chat.
- */
-void purple_conv_chat_clear_users(PurpleConvChat *chat);
-
-/**
- * Sets your nickname (used for hilighting) for a chat.
- *
- * @param chat The chat.
- * @param nick The nick.
- */
-void purple_conv_chat_set_nick(PurpleConvChat *chat, const char *nick);
-
-/**
- * Gets your nickname (used for hilighting) for a chat.
- *
- * @param chat The chat.
- * @return The nick.
- */
-const char *purple_conv_chat_get_nick(PurpleConvChat *chat);
-
-/**
- * Finds a chat with the specified chat ID.
- *
- * @param gc The purple_connection.
- * @param id The chat ID.
- *
- * @return The chat conversation.
- */
-PurpleConversation *purple_find_chat(const PurpleConnection *gc, int id);
-
-/**
- * Lets the core know we left a chat, without destroying it.
- * Called from serv_got_chat_left().
- *
- * @param chat The chat.
*/
-void purple_conv_chat_left(PurpleConvChat *chat);
+void purple_conversation_send_confirm(PurpleConversation *conv, const char *message);
/**
- * Invite a user to a chat.
- * The user will be prompted to enter the user's name or a message if one is
- * not given.
+ * purple_conversation_get_extended_menu:
+ * @conv: The conversation.
*
- * @param chat The chat.
- * @param user The user to invite to the chat.
- * @param message The message to send with the invitation.
- * @param confirm Prompt before sending the invitation. The user is always
- * prompted if either \a user or \a message is @c NULL.
+ * Retrieves the extended menu items for the conversation.
*
- * @since 2.6.0
+ * Returns: A list of PurpleMenuAction items, harvested by the
+ * chat-extended-menu signal. The list and the menuaction
+ * items should be freed by the caller.
*/
-void purple_conv_chat_invite_user(PurpleConvChat *chat, const char *user,
- const char *message, gboolean confirm);
+GList * purple_conversation_get_extended_menu(PurpleConversation *conv);
/**
- * Returns true if we're no longer in this chat,
- * and just left the window open.
+ * purple_conversation_do_command:
+ * @conv: The conversation.
+ * @cmdline: The entire command including the arguments.
+ * @markup: %NULL, or the formatted command line.
+ * @error: If the command failed errormsg is filled in with the appropriate error
+ * message, if not %NULL. It must be freed by the caller with g_free().
*
- * @param chat The chat.
+ * Perform a command in a conversation. Similar to purple_cmd_do_command().
*
- * @return @c TRUE if we left the chat already, @c FALSE if
- * we're still there.
+ * Returns: %TRUE if the command was executed successfully, %FALSE otherwise.
*/
-gboolean purple_conv_chat_has_left(PurpleConvChat *chat);
+gboolean purple_conversation_do_command(PurpleConversation *conv,
+ const gchar *cmdline, const gchar *markup, gchar **error);
/**
- * Creates a new chat buddy
+ * purple_conversation_get_max_message_size:
+ * @conv: The conversation to query.
*
- * @param name The name.
- * @param alias The alias.
- * @param flags The flags.
+ * Gets the maximum message size in bytes for the conversation.
*
- * @return The new chat buddy
- */
-PurpleConvChatBuddy *purple_conv_chat_cb_new(const char *name, const char *alias,
- PurpleConvChatBuddyFlags flags);
-
-/**
- * Find a chat buddy in a chat
+ * See #PurplePluginProtocolInfo's #get_max_message_size
*
- * @param chat The chat.
- * @param name The name of the chat buddy to find.
+ * Returns: Maximum message size, 0 if unspecified, -1 for infinite.
*/
-PurpleConvChatBuddy *purple_conv_chat_cb_find(PurpleConvChat *chat, const char *name);
+gssize
+purple_conversation_get_max_message_size(PurpleConversation *conv);
/**
- * Get the name of a chat buddy
+ * purple_conversation_add_remote_smiley:
+ * @conv: The conversation that receives new smiley.
+ * @shortcut: The shortcut for the new smiley.
*
- * @param cb The chat buddy.
+ * Adds new smiley to the list of remote smileys for this conversation.
+ * The smiley image have to be written and closed, when data is ready.
*
- * @return The name of the chat buddy.
- */
-const char *purple_conv_chat_cb_get_name(PurpleConvChatBuddy *cb);
-
-/**
- * Destroys a chat buddy
+ * You can retrieve smiley's #PurpleImage object using #purple_smiley_get_image
+ * and set its data with #purple_image_transfer_write
+ * and #purple_image_transfer_close.
*
- * @param cb The chat buddy to destroy
+ * Returns: (transfer none): New smiley, or %NULL if it's already being
+ * retrieved (or possibly, in case of error).
*/
-void purple_conv_chat_cb_destroy(PurpleConvChatBuddy *cb);
+PurpleSmiley *
+purple_conversation_add_remote_smiley(PurpleConversation *conv,
+ const gchar *shortcut);
/**
- * Retrieves the extended menu items for the conversation.
+ * purple_conversation_get_remote_smiley:
+ * @conv: The conversation.
+ * @shortcut: The shortcut.
*
- * @param conv The conversation.
+ * Lookups for the remote smiley previously added to this conversation.
*
- * @return A list of PurpleMenuAction items, harvested by the
- * chat-extended-menu signal. The list and the menuaction
- * items should be freed by the caller.
+ * You may use this function when you receive the smiley data, but it's
+ * better just to store and use the reference returned by
+ * #purple_conversation_add_remote_smiley.
*
- * @since 2.1.0
+ * Returns: (transfer none): The smiley, or %NULL if it doesn't exists.
*/
-GList * purple_conversation_get_extended_menu(PurpleConversation *conv);
+PurpleSmiley *
+purple_conversation_get_remote_smiley(PurpleConversation *conv,
+ const gchar *shortcut);
/**
- * Perform a command in a conversation. Similar to @see purple_cmd_do_command
- *
- * @param conv The conversation.
- * @param cmdline The entire command including the arguments.
- * @param markup @c NULL, or the formatted command line.
- * @param error If the command failed errormsg is filled in with the appropriate error
- * message, if not @c NULL. It must be freed by the caller with g_free().
+ * purple_conversation_get_remote_smileys:
+ * @conv: The conversation.
*
- * @return @c TRUE if the command was executed successfully, @c FALSE otherwise.
+ * Get all remote smileys previously added to this conversation.
*
- * @since 2.1.0
+ * Returns: (transfer none): The list of remote smileys.
*/
-gboolean purple_conversation_do_command(PurpleConversation *conv, const gchar *cmdline, const gchar *markup, gchar **error);
-
-/*@}*/
+PurpleSmileyList *
+purple_conversation_get_remote_smileys(PurpleConversation *conv);
/**************************************************************************/
-/** @name Conversations Subsystem */
+/* Conversation Helper API */
/**************************************************************************/
-/*@{*/
/**
- * Returns the conversation subsystem handle.
+ * purple_conversation_present_error:
+ * @who: The user this error is about
+ * @account: The account this error is on
+ * @what: The error
*
- * @return The conversation subsystem handle.
- */
-void *purple_conversations_get_handle(void);
-
-/**
- * Initializes the conversation subsystem.
- */
-void purple_conversations_init(void);
-
-/**
- * Uninitializes the conversation subsystem.
+ * Presents an IM-error to the user
+ *
+ * This is a helper function to find a conversation, write an error to it, and
+ * raise the window. If a conversation with this user doesn't already exist,
+ * the function will return FALSE and the calling function can attempt to present
+ * the error another way (purple_notify_error, most likely)
+ *
+ * Returns: TRUE if the error was presented, else FALSE
*/
-void purple_conversations_uninit(void);
-
-/*@}*/
+gboolean purple_conversation_present_error(const char *who, PurpleAccount *account, const char *what);
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_CONVERSATION_H_ */
diff --git a/libpurple/conversations.c b/libpurple/conversations.c
new file mode 100644
index 0000000000..f0b12cbcda
--- /dev/null
+++ b/libpurple/conversations.c
@@ -0,0 +1,468 @@
+/*
+ * 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 "conversations.h"
+
+static GList *conversations = NULL;
+static GList *ims = NULL;
+static GList *chats = NULL;
+static PurpleConversationUiOps *default_ops = NULL;
+
+/*
+ * A hash table used for efficient lookups of conversations by name.
+ * struct _purple_hconv => PurpleConversation*
+ */
+static GHashTable *conversation_cache = NULL;
+
+struct _purple_hconv {
+ gboolean im;
+ char *name;
+ const PurpleAccount *account;
+};
+
+static guint
+_purple_conversations_hconv_hash(struct _purple_hconv *hc)
+{
+ return g_str_hash(hc->name) ^ hc->im ^ g_direct_hash(hc->account);
+}
+
+static guint
+_purple_conversations_hconv_equal(struct _purple_hconv *hc1, struct _purple_hconv *hc2)
+{
+ return (hc1->im == hc2->im &&
+ hc1->account == hc2->account &&
+ g_str_equal(hc1->name, hc2->name));
+}
+
+static void
+_purple_conversations_hconv_free_key(struct _purple_hconv *hc)
+{
+ g_free(hc->name);
+ g_free(hc);
+}
+
+void
+purple_conversations_add(PurpleConversation *conv)
+{
+ PurpleAccount *account;
+ struct _purple_hconv *hc;
+
+ g_return_if_fail(conv != NULL);
+
+ if (g_list_find(conversations, conv) != NULL)
+ return;
+
+ conversations = g_list_prepend(conversations, conv);
+
+ if (PURPLE_IS_IM_CONVERSATION(conv))
+ ims = g_list_prepend(ims, conv);
+ else
+ chats = g_list_prepend(chats, conv);
+
+ account = purple_conversation_get_account(conv);
+
+ hc = g_new(struct _purple_hconv, 1);
+ hc->name = g_strdup(purple_normalize(account,
+ purple_conversation_get_name(conv)));
+ hc->account = account;
+ hc->im = PURPLE_IS_IM_CONVERSATION(conv);
+
+ g_hash_table_insert(conversation_cache, hc, conv);
+}
+
+void
+purple_conversations_remove(PurpleConversation *conv)
+{
+ PurpleAccount *account;
+ struct _purple_hconv hc;
+
+ g_return_if_fail(conv != NULL);
+
+ conversations = g_list_remove(conversations, conv);
+
+ if (PURPLE_IS_IM_CONVERSATION(conv))
+ ims = g_list_remove(ims, conv);
+ else
+ chats = g_list_remove(chats, conv);
+
+ account = purple_conversation_get_account(conv);
+
+ hc.name = (gchar *)purple_normalize(account,
+ purple_conversation_get_name(conv));
+ hc.account = account;
+ hc.im = PURPLE_IS_IM_CONVERSATION(conv);
+
+ g_hash_table_remove(conversation_cache, &hc);
+}
+
+void
+_purple_conversations_update_cache(PurpleConversation *conv, const char *name,
+ PurpleAccount *account)
+{
+ PurpleAccount *old_account;
+ struct _purple_hconv *hc;
+
+ g_return_if_fail(conv != NULL);
+ g_return_if_fail(account != NULL || name != NULL);
+
+ old_account = purple_conversation_get_account(conv);
+
+ hc = g_new(struct _purple_hconv, 1);
+ hc->im = PURPLE_IS_IM_CONVERSATION(conv);
+ hc->account = old_account;
+ hc->name = (gchar *)purple_normalize(old_account,
+ purple_conversation_get_name(conv));
+
+ g_hash_table_remove(conversation_cache, hc);
+
+ if (account)
+ hc->account = account;
+ if (name)
+ hc->name = g_strdup(purple_normalize(hc->account, name));
+
+ g_hash_table_insert(conversation_cache, hc, conv);
+}
+
+GList *
+purple_conversations_get_all(void)
+{
+ return conversations;
+}
+
+GList *
+purple_conversations_get_ims(void)
+{
+ return ims;
+}
+
+GList *
+purple_conversations_get_chats(void)
+{
+ return chats;
+}
+
+PurpleConversation *
+purple_conversations_find_with_account(const char *name,
+ const PurpleAccount *account)
+{
+ PurpleConversation *c = NULL;
+ struct _purple_hconv hc;
+
+ g_return_val_if_fail(name != NULL, NULL);
+
+ hc.name = (gchar *)purple_normalize(account, name);
+ hc.account = account;
+
+ hc.im = TRUE;
+ c = g_hash_table_lookup(conversation_cache, &hc);
+ if (!c) {
+ hc.im = FALSE;
+ c = g_hash_table_lookup(conversation_cache, &hc);
+ }
+
+ return c;
+}
+
+PurpleIMConversation *
+purple_conversations_find_im_with_account(const char *name,
+ const PurpleAccount *account)
+{
+ PurpleIMConversation *im = NULL;
+ struct _purple_hconv hc;
+
+ g_return_val_if_fail(name != NULL, NULL);
+
+ hc.name = (gchar *)purple_normalize(account, name);
+ hc.account = account;
+ hc.im = TRUE;
+
+ im = g_hash_table_lookup(conversation_cache, &hc);
+
+ return im;
+}
+
+PurpleChatConversation *
+purple_conversations_find_chat_with_account(const char *name,
+ const PurpleAccount *account)
+{
+ PurpleChatConversation *c = NULL;
+ struct _purple_hconv hc;
+
+ g_return_val_if_fail(name != NULL, NULL);
+
+ hc.name = (gchar *)purple_normalize(account, name);
+ hc.account = account;
+ hc.im = FALSE;
+
+ c = g_hash_table_lookup(conversation_cache, &hc);
+
+ return c;
+}
+
+PurpleChatConversation *
+purple_conversations_find_chat(const PurpleConnection *gc, int id)
+{
+ GList *l;
+ PurpleChatConversation *chat;
+
+ for (l = purple_conversations_get_chats(); l != NULL; l = l->next) {
+ chat = (PurpleChatConversation *)l->data;
+
+ if (purple_chat_conversation_get_id(chat) == id &&
+ purple_conversation_get_connection(PURPLE_CONVERSATION(chat)) == gc)
+ return chat;
+ }
+
+ return NULL;
+}
+
+void
+purple_conversations_set_ui_ops(PurpleConversationUiOps *ops)
+{
+ default_ops = ops;
+}
+
+PurpleConversationUiOps *
+purple_conversations_get_ui_ops(void)
+{
+ return default_ops;
+}
+
+void *
+purple_conversations_get_handle(void)
+{
+ static int handle;
+
+ return &handle;
+}
+
+void
+purple_conversations_init(void)
+{
+ void *handle = purple_conversations_get_handle();
+
+ conversation_cache = g_hash_table_new_full((GHashFunc)_purple_conversations_hconv_hash,
+ (GEqualFunc)_purple_conversations_hconv_equal,
+ (GDestroyNotify)_purple_conversations_hconv_free_key, NULL);
+
+ /**********************************************************************
+ * Register preferences
+ **********************************************************************/
+
+ /* Conversations */
+ purple_prefs_add_none("/purple/conversations");
+
+ /* Conversations -> Chat */
+ purple_prefs_add_none("/purple/conversations/chat");
+ purple_prefs_add_bool("/purple/conversations/chat/show_nick_change", TRUE);
+
+ /* Conversations -> IM */
+ purple_prefs_add_none("/purple/conversations/im");
+ purple_prefs_add_bool("/purple/conversations/im/send_typing", TRUE);
+
+
+ /**********************************************************************
+ * Register signals
+ **********************************************************************/
+ purple_signal_register(handle, "writing-im-msg",
+ purple_marshal_BOOLEAN__POINTER_POINTER, G_TYPE_BOOLEAN, 2,
+ PURPLE_TYPE_IM_CONVERSATION, PURPLE_TYPE_MESSAGE);
+
+ purple_signal_register(handle, "wrote-im-msg",
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ PURPLE_TYPE_IM_CONVERSATION, PURPLE_TYPE_MESSAGE);
+
+ purple_signal_register(handle, "sent-attention",
+ purple_marshal_VOID__POINTER_POINTER_POINTER_UINT,
+ G_TYPE_NONE, 4, PURPLE_TYPE_ACCOUNT, G_TYPE_STRING,
+ PURPLE_TYPE_CONVERSATION, G_TYPE_UINT);
+
+ purple_signal_register(handle, "got-attention",
+ purple_marshal_VOID__POINTER_POINTER_POINTER_UINT,
+ G_TYPE_NONE, 4, PURPLE_TYPE_ACCOUNT, G_TYPE_STRING,
+ PURPLE_TYPE_CONVERSATION, G_TYPE_UINT);
+
+ purple_signal_register(handle, "sending-im-msg",
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE,
+ 2, PURPLE_TYPE_ACCOUNT, PURPLE_TYPE_MESSAGE);
+
+ purple_signal_register(handle, "sent-im-msg",
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE,
+ 2, PURPLE_TYPE_ACCOUNT, PURPLE_TYPE_MESSAGE);
+
+ purple_signal_register(handle, "receiving-im-msg",
+ purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
+ G_TYPE_BOOLEAN, 5, PURPLE_TYPE_ACCOUNT,
+ G_TYPE_POINTER, /* pointer to a string */
+ G_TYPE_POINTER, /* pointer to a string */
+ PURPLE_TYPE_IM_CONVERSATION,
+ G_TYPE_POINTER); /* pointer to a string */
+
+ purple_signal_register(handle, "received-im-msg",
+ purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT,
+ G_TYPE_NONE, 5, PURPLE_TYPE_ACCOUNT, G_TYPE_STRING,
+ G_TYPE_STRING, PURPLE_TYPE_IM_CONVERSATION, G_TYPE_UINT);
+
+ purple_signal_register(handle, "blocked-im-msg",
+ purple_marshal_VOID__POINTER_POINTER_POINTER_UINT_UINT,
+ G_TYPE_NONE, 5, PURPLE_TYPE_ACCOUNT, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT);
+
+ purple_signal_register(handle, "writing-chat-msg",
+ purple_marshal_BOOLEAN__POINTER_POINTER, G_TYPE_BOOLEAN, 2,
+ PURPLE_TYPE_IM_CONVERSATION, PURPLE_TYPE_MESSAGE);
+
+ purple_signal_register(handle, "wrote-chat-msg",
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ PURPLE_TYPE_IM_CONVERSATION, PURPLE_TYPE_MESSAGE);
+
+ purple_signal_register(handle, "sending-chat-msg",
+ purple_marshal_VOID__POINTER_POINTER_UINT, G_TYPE_NONE,
+ 3, PURPLE_TYPE_ACCOUNT, PURPLE_TYPE_MESSAGE, G_TYPE_UINT);
+
+ purple_signal_register(handle, "sent-chat-msg",
+ purple_marshal_VOID__POINTER_POINTER_UINT, G_TYPE_NONE,
+ 3, PURPLE_TYPE_ACCOUNT, PURPLE_TYPE_MESSAGE, G_TYPE_UINT);
+
+ purple_signal_register(handle, "receiving-chat-msg",
+ purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
+ G_TYPE_BOOLEAN, 5, PURPLE_TYPE_ACCOUNT,
+ G_TYPE_POINTER, /* pointer to a string */
+ G_TYPE_POINTER, /* pointer to a string */
+ PURPLE_TYPE_CHAT_CONVERSATION,
+ G_TYPE_POINTER); /* pointer to an unsigned int */
+
+ purple_signal_register(handle, "received-chat-msg",
+ purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT,
+ G_TYPE_NONE, 5, PURPLE_TYPE_ACCOUNT, G_TYPE_STRING,
+ G_TYPE_STRING, PURPLE_TYPE_CHAT_CONVERSATION, G_TYPE_UINT);
+
+ purple_signal_register(handle, "conversation-created",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_CONVERSATION);
+
+ purple_signal_register(handle, "conversation-updated",
+ purple_marshal_VOID__POINTER_UINT, G_TYPE_NONE, 2,
+ PURPLE_TYPE_CONVERSATION, G_TYPE_UINT);
+
+ purple_signal_register(handle, "deleting-conversation",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_CONVERSATION);
+
+ purple_signal_register(handle, "buddy-typing",
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ PURPLE_TYPE_ACCOUNT, G_TYPE_STRING);
+
+ purple_signal_register(handle, "buddy-typed",
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ PURPLE_TYPE_ACCOUNT, G_TYPE_STRING);
+
+ purple_signal_register(handle, "buddy-typing-stopped",
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ PURPLE_TYPE_ACCOUNT, G_TYPE_STRING);
+
+ purple_signal_register(handle, "chat-user-joining",
+ purple_marshal_BOOLEAN__POINTER_POINTER_UINT,
+ G_TYPE_BOOLEAN, 3, PURPLE_TYPE_CHAT_CONVERSATION,
+ G_TYPE_STRING, G_TYPE_UINT);
+
+ purple_signal_register(handle, "chat-user-joined",
+ purple_marshal_VOID__POINTER_POINTER_UINT_UINT,
+ G_TYPE_NONE, 4, PURPLE_TYPE_CHAT_CONVERSATION,
+ G_TYPE_STRING, G_TYPE_UINT, G_TYPE_BOOLEAN);
+
+ purple_signal_register(handle, "chat-user-flags",
+ purple_marshal_VOID__POINTER_UINT_UINT, G_TYPE_NONE, 3,
+ PURPLE_TYPE_CHAT_USER, G_TYPE_UINT, G_TYPE_UINT);
+
+ purple_signal_register(handle, "chat-user-leaving",
+ purple_marshal_BOOLEAN__POINTER_POINTER_POINTER,
+ G_TYPE_BOOLEAN, 3, PURPLE_TYPE_CHAT_CONVERSATION,
+ G_TYPE_STRING, G_TYPE_STRING);
+
+ purple_signal_register(handle, "chat-user-left",
+ purple_marshal_VOID__POINTER_POINTER_POINTER,
+ G_TYPE_NONE, 3, PURPLE_TYPE_CHAT_CONVERSATION,
+ G_TYPE_STRING, G_TYPE_STRING);
+
+ purple_signal_register(handle, "deleting-chat-user",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_CHAT_USER);
+
+ purple_signal_register(handle, "chat-inviting-user",
+ purple_marshal_VOID__POINTER_POINTER_POINTER,
+ G_TYPE_NONE, 3, PURPLE_TYPE_CHAT_CONVERSATION,
+ G_TYPE_STRING,
+ G_TYPE_POINTER); /* pointer to a string */
+
+ purple_signal_register(handle, "chat-invited-user",
+ purple_marshal_VOID__POINTER_POINTER_POINTER,
+ G_TYPE_NONE, 3, PURPLE_TYPE_CHAT_CONVERSATION,
+ G_TYPE_STRING, G_TYPE_STRING);
+
+ purple_signal_register(handle, "chat-invited",
+ purple_marshal_INT__POINTER_POINTER_POINTER_POINTER_POINTER,
+ G_TYPE_INT, 5, PURPLE_TYPE_ACCOUNT, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
+
+ purple_signal_register(handle, "chat-invite-blocked",
+ purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_POINTER,
+ G_TYPE_NONE, 5, PURPLE_TYPE_ACCOUNT, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_POINTER); /* (GHashTable *) */
+
+ purple_signal_register(handle, "chat-joined",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_CHAT_CONVERSATION);
+
+ purple_signal_register(handle, "chat-join-failed",
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ PURPLE_TYPE_CONNECTION, G_TYPE_POINTER);
+
+ purple_signal_register(handle, "chat-left",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_CHAT_CONVERSATION);
+
+ purple_signal_register(handle, "chat-topic-changed",
+ purple_marshal_VOID__POINTER_POINTER_POINTER,
+ G_TYPE_NONE, 3, PURPLE_TYPE_CHAT_CONVERSATION,
+ G_TYPE_STRING, G_TYPE_STRING);
+
+ purple_signal_register(handle, "cleared-message-history",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_CONVERSATION);
+
+ purple_signal_register(handle, "conversation-extended-menu",
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ PURPLE_TYPE_CONVERSATION,
+ G_TYPE_POINTER); /* (GList **) */
+}
+
+void
+purple_conversations_uninit(void)
+{
+ while (conversations)
+ g_object_unref(G_OBJECT(conversations->data));
+
+ g_hash_table_destroy(conversation_cache);
+ purple_signals_unregister_by_instance(purple_conversations_get_handle());
+}
diff --git a/libpurple/conversations.h b/libpurple/conversations.h
new file mode 100644
index 0000000000..96cf18a34e
--- /dev/null
+++ b/libpurple/conversations.h
@@ -0,0 +1,175 @@
+/* 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 _PURPLE_CONVERSATIONS_H_
+#define _PURPLE_CONVERSATIONS_H_
+/**
+ * SECTION:conversations
+ * @section_id: libpurple-conversations
+ * @short_description: <filename>conversations.h</filename>
+ * @title: Conversations Subsystem API
+ * @see_also: <link linkend="chapter-signals-conversation">Conversation signals</link>
+ */
+
+#include "conversationtypes.h"
+#include "server.h"
+
+G_BEGIN_DECLS
+
+/**************************************************************************/
+/* Conversations Subsystem */
+/**************************************************************************/
+
+/**
+ * purple_conversations_add:
+ * @conv: The conversation.
+ *
+ * Adds a conversation to the list of conversations.
+ */
+void purple_conversations_add(PurpleConversation *conv);
+
+/**
+ * purple_conversations_remove:
+ * @conv: The conversation.
+ *
+ * Removes a conversation from the list of conversations.
+ */
+void purple_conversations_remove(PurpleConversation *conv);
+
+/**
+ * purple_conversations_get_all:
+ *
+ * Returns a list of all conversations.
+ *
+ * This list includes both IMs and chats.
+ *
+ * Returns: (transfer none): A GList of all conversations.
+ */
+GList *purple_conversations_get_all(void);
+
+/**
+ * purple_conversations_get_ims:
+ *
+ * Returns a list of all IMs.
+ *
+ * Returns: (transfer none): A GList of all IMs.
+ */
+GList *purple_conversations_get_ims(void);
+
+/**
+ * purple_conversations_get_chats:
+ *
+ * Returns a list of all chats.
+ *
+ * Returns: (transfer none): A GList of all chats.
+ */
+GList *purple_conversations_get_chats(void);
+
+/**
+ * purple_conversations_find_with_account:
+ * @name: The name of the conversation.
+ * @account: The account associated with the conversation.
+ *
+ * Finds a conversation of any type with the specified name and Purple account.
+ *
+ * Returns: The conversation if found, or %NULL otherwise.
+ */
+PurpleConversation *purple_conversations_find_with_account(const char *name,
+ const PurpleAccount *account);
+
+/**
+ * purple_conversations_find_im_with_account:
+ * @name: The name of the conversation.
+ * @account: The account associated with the conversation.
+ *
+ * Finds an IM with the specified name and Purple account.
+ *
+ * Returns: The conversation if found, or %NULL otherwise.
+ */
+PurpleIMConversation *purple_conversations_find_im_with_account(const char *name,
+ const PurpleAccount *account);
+
+/**
+ * purple_conversations_find_chat_with_account:
+ * @name: The name of the conversation.
+ * @account: The account associated with the conversation.
+ *
+ * Finds a chat with the specified name and Purple account.
+ *
+ * Returns: The conversation if found, or %NULL otherwise.
+ */
+PurpleChatConversation *purple_conversations_find_chat_with_account(const char *name,
+ const PurpleAccount *account);
+
+/**
+ * purple_conversations_find_chat:
+ * @gc: The purple_connection.
+ * @id: The chat ID.
+ *
+ * Finds a chat with the specified chat ID.
+ *
+ * Returns: The chat conversation.
+ */
+PurpleChatConversation *purple_conversations_find_chat(const PurpleConnection *gc, int id);
+
+/**
+ * purple_conversations_set_ui_ops:
+ * @ops: The UI conversation operations structure.
+ *
+ * Sets the default conversation UI operations structure.
+ */
+void purple_conversations_set_ui_ops(PurpleConversationUiOps *ops);
+
+/**
+ * purple_conversations_get_ui_ops:
+ *
+ * Gets the default conversation UI operations structure.
+ *
+ * Returns: The UI conversation operations structure.
+ */
+PurpleConversationUiOps *purple_conversations_get_ui_ops(void);
+
+/**
+ * purple_conversations_get_handle:
+ *
+ * Returns the conversation subsystem handle.
+ *
+ * Returns: The conversation subsystem handle.
+ */
+void *purple_conversations_get_handle(void);
+
+/**
+ * purple_conversations_init:
+ *
+ * Initializes the conversation subsystem.
+ */
+void purple_conversations_init(void);
+
+/**
+ * purple_conversations_uninit:
+ *
+ * Uninitializes the conversation subsystem.
+ */
+void purple_conversations_uninit(void);
+
+G_END_DECLS
+
+#endif /* _PURPLE_CONVERSATIONS_H_ */
diff --git a/libpurple/conversationtypes.c b/libpurple/conversationtypes.c
new file mode 100644
index 0000000000..bfa5b56ab5
--- /dev/null
+++ b/libpurple/conversationtypes.c
@@ -0,0 +1,1979 @@
+/*
+ * 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 "glibcompat.h"
+#include "conversationtypes.h"
+#include "dbus-maybe.h"
+#include "debug.h"
+#include "enums.h"
+
+#define SEND_TYPED_TIMEOUT_SECONDS 5
+
+#define PURPLE_CHAT_CONVERSATION_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_CHAT_CONVERSATION, PurpleChatConversationPrivate))
+
+typedef struct _PurpleChatConversationPrivate PurpleChatConversationPrivate;
+
+#define PURPLE_IM_CONVERSATION_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_IM_CONVERSATION, PurpleIMConversationPrivate))
+
+typedef struct _PurpleIMConversationPrivate PurpleIMConversationPrivate;
+
+#define PURPLE_CHAT_USER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_CHAT_USER, PurpleChatUserPrivate))
+
+typedef struct _PurpleChatUserPrivate PurpleChatUserPrivate;
+
+/*
+ * Data specific to Chats.
+ */
+struct _PurpleChatConversationPrivate
+{
+ GList *ignored; /* Ignored users. */
+ char *who; /* The person who set the topic. */
+ char *topic; /* The topic. */
+ int id; /* The chat ID. */
+ char *nick; /* Your nick in this chat. */
+ gboolean left; /* We left the chat and kept the window open */
+ GHashTable *users; /* Hash table of the users in the room. */
+};
+
+/* Chat Property enums */
+enum {
+ CHAT_PROP_0,
+ CHAT_PROP_TOPIC_WHO,
+ CHAT_PROP_TOPIC,
+ CHAT_PROP_ID,
+ CHAT_PROP_NICK,
+ CHAT_PROP_LEFT,
+ CHAT_PROP_LAST
+};
+
+/*
+ * Data specific to Instant Messages.
+ */
+struct _PurpleIMConversationPrivate
+{
+ PurpleIMTypingState typing_state; /* The current typing state. */
+ guint typing_timeout; /* The typing timer handle. */
+ time_t type_again; /* The type again time. */
+ guint send_typed_timeout; /* The type again timer handle. */
+ PurpleBuddyIcon *icon; /* The buddy icon. */
+};
+
+/* IM Property enums */
+enum {
+ IM_PROP_0,
+ IM_PROP_TYPING_STATE,
+ IM_PROP_ICON,
+ IM_PROP_LAST
+};
+
+/*
+ * Data for "Chat Buddies"
+ */
+struct _PurpleChatUserPrivate
+{
+ PurpleChatConversation *chat; /* The chat */
+ char *name; /* The chat participant's name in the
+ chat. */
+ char *alias; /* The chat participant's alias, if known;
+ NULL otherwise. */
+ char *alias_key; /* A string by which this user will be
+ sorted, or @c NULL if the user should be
+ sorted by its @name.
+ (This is currently always NULL. */
+ gboolean buddy; /* TRUE if this chat participant is on
+ the buddy list; FALSE otherwise. */
+ PurpleChatUserFlags flags; /* A bitwise OR of flags for this
+ participant, such as whether they
+ are a channel operator. */
+};
+
+/* Chat User Property enums */
+enum {
+ CU_PROP_0,
+ CU_PROP_CHAT,
+ CU_PROP_NAME,
+ CU_PROP_ALIAS,
+ CU_PROP_FLAGS,
+ CU_PROP_LAST
+};
+
+static PurpleConversationClass *parent_class;
+static GObjectClass *cb_parent_class;
+
+static GParamSpec *chat_properties[CHAT_PROP_LAST];
+static GParamSpec *im_properties[IM_PROP_LAST];
+static GParamSpec *cu_properties[CU_PROP_LAST];
+
+static int purple_chat_user_compare(PurpleChatUser *a,
+ PurpleChatUser *b);
+
+/**************************************************************************
+ * IM Conversation API
+ **************************************************************************/
+static gboolean
+reset_typing_cb(gpointer data)
+{
+ PurpleIMConversation *im = (PurpleIMConversation *)data;
+
+ purple_im_conversation_set_typing_state(im, PURPLE_IM_NOT_TYPING);
+ purple_im_conversation_stop_typing_timeout(im);
+
+ return FALSE;
+}
+
+static gboolean
+send_typed_cb(gpointer data)
+{
+ PurpleIMConversation *im = PURPLE_IM_CONVERSATION(data);
+ PurpleConnection *gc;
+ const char *name;
+
+ g_return_val_if_fail(im != NULL, FALSE);
+
+ gc = purple_conversation_get_connection(PURPLE_CONVERSATION(im));
+ name = purple_conversation_get_name(PURPLE_CONVERSATION(im));
+
+ if (gc != NULL && name != NULL) {
+ /* We set this to 1 so that PURPLE_IM_TYPING will be sent
+ * if the Purple user types anything else.
+ */
+ purple_im_conversation_set_type_again(im, 1);
+
+ purple_serv_send_typing(gc, name, PURPLE_IM_TYPED);
+
+ purple_debug(PURPLE_DEBUG_MISC, "conversationtypes", "typed...\n");
+ }
+
+ return FALSE;
+}
+
+void
+purple_im_conversation_set_icon(PurpleIMConversation *im, PurpleBuddyIcon *icon)
+{
+ PurpleIMConversationPrivate *priv = PURPLE_IM_CONVERSATION_GET_PRIVATE(im);
+
+ g_return_if_fail(priv != NULL);
+
+ if (priv->icon != icon)
+ {
+ purple_buddy_icon_unref(priv->icon);
+
+ priv->icon = (icon == NULL ? NULL : purple_buddy_icon_ref(icon));
+
+ g_object_notify_by_pspec(G_OBJECT(im), im_properties[IM_PROP_ICON]);
+ }
+
+ purple_conversation_update(PURPLE_CONVERSATION(im),
+ PURPLE_CONVERSATION_UPDATE_ICON);
+}
+
+PurpleBuddyIcon *
+purple_im_conversation_get_icon(const PurpleIMConversation *im)
+{
+ PurpleIMConversationPrivate *priv = PURPLE_IM_CONVERSATION_GET_PRIVATE(im);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->icon;
+}
+
+void
+purple_im_conversation_set_typing_state(PurpleIMConversation *im, PurpleIMTypingState state)
+{
+ PurpleAccount *account;
+ const char *name;
+ PurpleIMConversationPrivate *priv = PURPLE_IM_CONVERSATION_GET_PRIVATE(im);
+
+ g_return_if_fail(priv != NULL);
+
+ name = purple_conversation_get_name(PURPLE_CONVERSATION(im));
+ account = purple_conversation_get_account(PURPLE_CONVERSATION(im));
+
+ if (priv->typing_state != state)
+ {
+ priv->typing_state = state;
+
+ g_object_notify_by_pspec(G_OBJECT(im),
+ im_properties[IM_PROP_TYPING_STATE]);
+
+ switch (state)
+ {
+ case PURPLE_IM_TYPING:
+ purple_signal_emit(purple_conversations_get_handle(),
+ "buddy-typing", account, name);
+ break;
+ case PURPLE_IM_TYPED:
+ purple_signal_emit(purple_conversations_get_handle(),
+ "buddy-typed", account, name);
+ break;
+ case PURPLE_IM_NOT_TYPING:
+ purple_signal_emit(purple_conversations_get_handle(),
+ "buddy-typing-stopped", account, name);
+ break;
+ }
+
+ purple_im_conversation_update_typing(im);
+ }
+}
+
+PurpleIMTypingState
+purple_im_conversation_get_typing_state(const PurpleIMConversation *im)
+{
+ PurpleIMConversationPrivate *priv = PURPLE_IM_CONVERSATION_GET_PRIVATE(im);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return priv->typing_state;
+}
+
+void
+purple_im_conversation_start_typing_timeout(PurpleIMConversation *im, int timeout)
+{
+ PurpleIMConversationPrivate *priv = PURPLE_IM_CONVERSATION_GET_PRIVATE(im);
+
+ g_return_if_fail(priv != NULL);
+
+ if (priv->typing_timeout > 0)
+ purple_im_conversation_stop_typing_timeout(im);
+
+ priv->typing_timeout = purple_timeout_add_seconds(timeout, reset_typing_cb, im);
+}
+
+void
+purple_im_conversation_stop_typing_timeout(PurpleIMConversation *im)
+{
+ PurpleIMConversationPrivate *priv = PURPLE_IM_CONVERSATION_GET_PRIVATE(im);
+
+ g_return_if_fail(priv != NULL);
+
+ if (priv->typing_timeout == 0)
+ return;
+
+ purple_timeout_remove(priv->typing_timeout);
+ priv->typing_timeout = 0;
+}
+
+guint
+purple_im_conversation_get_typing_timeout(const PurpleIMConversation *im)
+{
+ PurpleIMConversationPrivate *priv = PURPLE_IM_CONVERSATION_GET_PRIVATE(im);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return priv->typing_timeout;
+}
+
+void
+purple_im_conversation_set_type_again(PurpleIMConversation *im, unsigned int val)
+{
+ PurpleIMConversationPrivate *priv = PURPLE_IM_CONVERSATION_GET_PRIVATE(im);
+
+ g_return_if_fail(priv != NULL);
+
+ if (val == 0)
+ priv->type_again = 0;
+ else
+ priv->type_again = time(NULL) + val;
+}
+
+time_t
+purple_im_conversation_get_type_again(const PurpleIMConversation *im)
+{
+ PurpleIMConversationPrivate *priv = PURPLE_IM_CONVERSATION_GET_PRIVATE(im);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return priv->type_again;
+}
+
+void
+purple_im_conversation_start_send_typed_timeout(PurpleIMConversation *im)
+{
+ PurpleIMConversationPrivate *priv = PURPLE_IM_CONVERSATION_GET_PRIVATE(im);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->send_typed_timeout = purple_timeout_add_seconds(SEND_TYPED_TIMEOUT_SECONDS,
+ send_typed_cb, im);
+}
+
+void
+purple_im_conversation_stop_send_typed_timeout(PurpleIMConversation *im)
+{
+ PurpleIMConversationPrivate *priv = PURPLE_IM_CONVERSATION_GET_PRIVATE(im);
+
+ g_return_if_fail(priv != NULL);
+
+ if (priv->send_typed_timeout == 0)
+ return;
+
+ purple_timeout_remove(priv->send_typed_timeout);
+ priv->send_typed_timeout = 0;
+}
+
+guint
+purple_im_conversation_get_send_typed_timeout(const PurpleIMConversation *im)
+{
+ PurpleIMConversationPrivate *priv = PURPLE_IM_CONVERSATION_GET_PRIVATE(im);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return priv->send_typed_timeout;
+}
+
+void
+purple_im_conversation_update_typing(PurpleIMConversation *im)
+{
+ g_return_if_fail(PURPLE_IS_IM_CONVERSATION(im));
+
+ purple_conversation_update(PURPLE_CONVERSATION(im),
+ PURPLE_CONVERSATION_UPDATE_TYPING);
+}
+
+static void
+im_conversation_write_message(PurpleConversation *conv, PurpleMessage *msg)
+{
+ PurpleIMConversation *im = PURPLE_IM_CONVERSATION(conv);
+ gboolean is_recv;
+
+ g_return_if_fail(im != NULL);
+ g_return_if_fail(msg != NULL);
+
+ is_recv = (purple_message_get_flags(msg) & PURPLE_MESSAGE_RECV);
+
+ if (is_recv)
+ purple_im_conversation_set_typing_state(im, PURPLE_IM_NOT_TYPING);
+
+ _purple_conversation_write_common(conv, msg);
+}
+
+/**************************************************************************
+ * GObject code for IMs
+ **************************************************************************/
+
+/* Set method for GObject properties */
+static void
+purple_im_conversation_set_property(GObject *obj, guint param_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleIMConversation *im = PURPLE_IM_CONVERSATION(obj);
+
+ switch (param_id) {
+ case IM_PROP_TYPING_STATE:
+ purple_im_conversation_set_typing_state(im, g_value_get_enum(value));
+ break;
+ case IM_PROP_ICON:
+ purple_im_conversation_set_icon(im, 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_im_conversation_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleIMConversation *im = PURPLE_IM_CONVERSATION(obj);
+
+ switch (param_id) {
+ case IM_PROP_TYPING_STATE:
+ g_value_set_enum(value, purple_im_conversation_get_typing_state(im));
+ break;
+ case IM_PROP_ICON:
+ g_value_set_pointer(value, purple_im_conversation_get_icon(im));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* GObject initialization function */
+static void purple_im_conversation_init(GTypeInstance *instance, gpointer klass)
+{
+ PURPLE_DBUS_REGISTER_POINTER(PURPLE_IM_CONVERSATION(instance),
+ PurpleIMConversation);
+}
+
+/* Called when done constructing */
+static void
+purple_im_conversation_constructed(GObject *object)
+{
+ PurpleIMConversation *im = PURPLE_IM_CONVERSATION(object);
+ PurpleAccount *account;
+ PurpleBuddyIcon *icon;
+ gchar *name;
+
+ G_OBJECT_CLASS(parent_class)->constructed(object);
+
+ g_object_get(object,
+ "account", &account,
+ "name", &name,
+ NULL);
+
+ if ((icon = purple_buddy_icons_find(account, name)))
+ {
+ purple_im_conversation_set_icon(im, icon);
+ /* purple_im_conversation_set_icon refs the icon. */
+ purple_buddy_icon_unref(icon);
+ }
+
+ if (purple_prefs_get_bool("/purple/logging/log_ims"))
+ purple_conversation_set_logging(PURPLE_CONVERSATION(im), TRUE);
+
+ g_object_unref(account);
+ g_free(name);
+}
+
+/* GObject dispose function */
+static void
+purple_im_conversation_dispose(GObject *object)
+{
+ PurpleIMConversationPrivate *priv = PURPLE_IM_CONVERSATION_GET_PRIVATE(object);
+
+ if (priv->icon) {
+ purple_buddy_icon_unref(priv->icon);
+ priv->icon = NULL;
+ }
+
+ G_OBJECT_CLASS(parent_class)->dispose(object);
+}
+
+/* GObject finalize function */
+static void
+purple_im_conversation_finalize(GObject *object)
+{
+ PurpleIMConversation *im = PURPLE_IM_CONVERSATION(object);
+ PurpleConnection *gc = purple_conversation_get_connection(PURPLE_CONVERSATION(im));
+ PurplePluginProtocolInfo *prpl_info = NULL;
+ const char *name = purple_conversation_get_name(PURPLE_CONVERSATION(im));
+
+ if (gc != NULL)
+ {
+ /* Still connected */
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
+
+ if (purple_prefs_get_bool("/purple/conversations/im/send_typing"))
+ purple_serv_send_typing(gc, name, PURPLE_IM_NOT_TYPING);
+
+ if (gc && prpl_info->convo_closed != NULL)
+ prpl_info->convo_closed(gc, name);
+ }
+
+ purple_im_conversation_stop_typing_timeout(im);
+ purple_im_conversation_stop_send_typed_timeout(im);
+
+ PURPLE_DBUS_UNREGISTER_POINTER(im);
+
+ G_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+/* Class initializer function */
+static void purple_im_conversation_class_init(PurpleIMConversationClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+ PurpleConversationClass *conv_class = PURPLE_CONVERSATION_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ obj_class->dispose = purple_im_conversation_dispose;
+ obj_class->finalize = purple_im_conversation_finalize;
+ obj_class->constructed = purple_im_conversation_constructed;
+
+ /* Setup properties */
+ obj_class->get_property = purple_im_conversation_get_property;
+ obj_class->set_property = purple_im_conversation_set_property;
+
+ conv_class->write_message = im_conversation_write_message;
+
+ g_type_class_add_private(klass, sizeof(PurpleIMConversationPrivate));
+
+ 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_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);
+
+ g_object_class_install_properties(obj_class, IM_PROP_LAST, im_properties);
+}
+
+GType
+purple_im_conversation_get_type(void)
+{
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleIMConversationClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_im_conversation_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleIMConversation),
+ 0,
+ (GInstanceInitFunc)purple_im_conversation_init,
+ NULL,
+ };
+
+ type = g_type_register_static(PURPLE_TYPE_CONVERSATION,
+ "PurpleIMConversation",
+ &info, 0);
+ }
+
+ return type;
+}
+
+PurpleIMConversation *
+purple_im_conversation_new(PurpleAccount *account, const char *name)
+{
+ PurpleIMConversation *im;
+ PurpleConnection *gc;
+
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ /* Check if this conversation already exists. */
+ if ((im = purple_conversations_find_im_with_account(name, account)) != NULL)
+ return im;
+
+ gc = purple_account_get_connection(account);
+ g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
+
+ im = g_object_new(PURPLE_TYPE_IM_CONVERSATION,
+ "account", account,
+ "name", name,
+ "title", name,
+ NULL);
+
+ return im;
+}
+
+/**************************************************************************
+ * Chat Conversation API
+ **************************************************************************/
+static guint
+_purple_conversation_user_hash(gconstpointer data)
+{
+ const gchar *name = data;
+ gchar *collated;
+ guint hash;
+
+ collated = g_utf8_collate_key(name, -1);
+ hash = g_str_hash(collated);
+ g_free(collated);
+ return hash;
+}
+
+static gboolean
+_purple_conversation_user_equal(gconstpointer a, gconstpointer b)
+{
+ return !g_utf8_collate(a, b);
+}
+
+GList *
+purple_chat_conversation_get_users(const PurpleChatConversation *chat)
+{
+ PurpleChatConversationPrivate *priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return g_hash_table_get_values(priv->users);
+}
+
+guint
+purple_chat_conversation_get_users_count(const PurpleChatConversation *chat)
+{
+ PurpleChatConversationPrivate *priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return g_hash_table_size(priv->users);
+}
+
+void
+purple_chat_conversation_ignore(PurpleChatConversation *chat, const char *name)
+{
+ PurpleChatConversationPrivate *priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(name != NULL);
+
+ /* Make sure the user isn't already ignored. */
+ if (purple_chat_conversation_is_ignored_user(chat, name))
+ return;
+
+ purple_chat_conversation_set_ignored(chat,
+ g_list_append(priv->ignored, g_strdup(name)));
+}
+
+void
+purple_chat_conversation_unignore(PurpleChatConversation *chat, const char *name)
+{
+ GList *item;
+ PurpleChatConversationPrivate *priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(name != NULL);
+
+ /* Make sure the user is actually ignored. */
+ if (!purple_chat_conversation_is_ignored_user(chat, name))
+ return;
+
+ item = g_list_find(purple_chat_conversation_get_ignored(chat),
+ purple_chat_conversation_get_ignored_user(chat, name));
+
+ purple_chat_conversation_set_ignored(chat,
+ g_list_remove_link(priv->ignored, item));
+
+ g_free(item->data);
+ g_list_free_1(item);
+}
+
+GList *
+purple_chat_conversation_set_ignored(PurpleChatConversation *chat, GList *ignored)
+{
+ PurpleChatConversationPrivate *priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ priv->ignored = ignored;
+ return ignored;
+}
+
+GList *
+purple_chat_conversation_get_ignored(const PurpleChatConversation *chat)
+{
+ PurpleChatConversationPrivate *priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->ignored;
+}
+
+const char *
+purple_chat_conversation_get_ignored_user(const PurpleChatConversation *chat, const char *user)
+{
+ GList *ignored;
+
+ g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
+ g_return_val_if_fail(user != NULL, NULL);
+
+ for (ignored = purple_chat_conversation_get_ignored(chat);
+ ignored != NULL;
+ ignored = ignored->next) {
+
+ const char *ign = (const char *)ignored->data;
+
+ if (!purple_utf8_strcasecmp(user, ign) ||
+ ((*ign == '+' || *ign == '%') && !purple_utf8_strcasecmp(user, ign + 1)))
+ return ign;
+
+ if (*ign == '@') {
+ ign++;
+
+ if ((*ign == '+' && !purple_utf8_strcasecmp(user, ign + 1)) ||
+ (*ign != '+' && !purple_utf8_strcasecmp(user, ign)))
+ return ign;
+ }
+ }
+
+ return NULL;
+}
+
+gboolean
+purple_chat_conversation_is_ignored_user(const PurpleChatConversation *chat, const char *user)
+{
+ g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), FALSE);
+ g_return_val_if_fail(user != NULL, FALSE);
+
+ return (purple_chat_conversation_get_ignored_user(chat, user) != NULL);
+}
+
+void
+purple_chat_conversation_set_topic(PurpleChatConversation *chat, const char *who, const char *topic)
+{
+ PurpleChatConversationPrivate *priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+ GObject *obj;
+
+ g_return_if_fail(priv != NULL);
+
+ g_free(priv->who);
+ g_free(priv->topic);
+
+ priv->who = g_strdup(who);
+ priv->topic = g_strdup(topic);
+
+ obj = G_OBJECT(chat);
+ g_object_freeze_notify(obj);
+ g_object_notify_by_pspec(obj, chat_properties[CHAT_PROP_TOPIC_WHO]);
+ g_object_notify_by_pspec(obj, chat_properties[CHAT_PROP_TOPIC]);
+ g_object_thaw_notify(obj);
+
+ purple_conversation_update(PURPLE_CONVERSATION(chat),
+ PURPLE_CONVERSATION_UPDATE_TOPIC);
+
+ purple_signal_emit(purple_conversations_get_handle(), "chat-topic-changed",
+ chat, priv->who, priv->topic);
+}
+
+const char *
+purple_chat_conversation_get_topic(const PurpleChatConversation *chat)
+{
+ PurpleChatConversationPrivate *priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->topic;
+}
+
+const char *
+purple_chat_conversation_get_topic_who(const PurpleChatConversation *chat)
+{
+ PurpleChatConversationPrivate *priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->who;
+}
+
+void
+purple_chat_conversation_set_id(PurpleChatConversation *chat, int id)
+{
+ PurpleChatConversationPrivate *priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->id = id;
+
+ g_object_notify_by_pspec(G_OBJECT(chat), chat_properties[CHAT_PROP_ID]);
+}
+
+int
+purple_chat_conversation_get_id(const PurpleChatConversation *chat)
+{
+ PurpleChatConversationPrivate *priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+
+ g_return_val_if_fail(priv != NULL, -1);
+
+ return priv->id;
+}
+
+static void
+chat_conversation_write_message(PurpleConversation *conv, PurpleMessage *msg)
+{
+ PurpleChatConversationPrivate *priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(conv);
+ PurpleMessageFlags flags;
+
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(msg != NULL);
+
+ /* 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)))
+ {
+ return;
+ }
+
+#if 0
+ PurpleAccount *account = purple_conversation_get_account(conv);
+ /* XXX: this should not be necessary */
+ if (purple_strequal(purple_normalize(account, who), priv->nick)) {
+ flags |= PURPLE_MESSAGE_SEND;
+ }
+#endif
+
+ flags = purple_message_get_flags(msg);
+ if (flags & PURPLE_MESSAGE_RECV) {
+ if (purple_utf8_has_word(purple_message_get_contents(msg), priv->nick)) {
+ flags |= PURPLE_MESSAGE_NICK;
+ purple_message_set_flags(msg, flags);
+ }
+ }
+
+ _purple_conversation_write_common(conv, msg);
+}
+
+void
+purple_chat_conversation_add_user(PurpleChatConversation *chat, const char *user,
+ const char *extra_msg, PurpleChatUserFlags flags,
+ gboolean new_arrival)
+{
+ GList *users = g_list_append(NULL, (char *)user);
+ GList *extra_msgs = g_list_append(NULL, (char *)extra_msg);
+ GList *flags2 = g_list_append(NULL, GINT_TO_POINTER(flags));
+
+ purple_chat_conversation_add_users(chat, users, extra_msgs, flags2, new_arrival);
+
+ g_list_free(users);
+ g_list_free(extra_msgs);
+ g_list_free(flags2);
+}
+
+void
+purple_chat_conversation_add_users(PurpleChatConversation *chat, GList *users, GList *extra_msgs,
+ GList *flags, gboolean new_arrivals)
+{
+ PurpleConversation *conv;
+ PurpleConversationUiOps *ops;
+ PurpleChatUser *chatuser;
+ PurpleChatConversationPrivate *priv;
+ PurpleAccount *account;
+ PurpleConnection *gc;
+ PurplePluginProtocolInfo *prpl_info;
+ GList *ul, *fl;
+ GList *cbuddies = NULL;
+
+ priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(users != NULL);
+
+ conv = PURPLE_CONVERSATION(chat);
+ ops = purple_conversation_get_ui_ops(conv);
+
+ account = purple_conversation_get_account(conv);
+ gc = purple_conversation_get_connection(conv);
+ g_return_if_fail(PURPLE_IS_CONNECTION(gc));
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
+ g_return_if_fail(prpl_info != NULL);
+
+ ul = users;
+ fl = flags;
+ while ((ul != NULL) && (fl != NULL)) {
+ const char *user = (const char *)ul->data;
+ const char *alias = user;
+ gboolean quiet;
+ PurpleChatUserFlags flag = GPOINTER_TO_INT(fl->data);
+ const char *extra_msg = (extra_msgs ? extra_msgs->data : NULL);
+
+ if(!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
+ if (purple_strequal(priv->nick, purple_normalize(account, user))) {
+ const char *alias2 = purple_account_get_private_alias(account);
+ if (alias2 != NULL)
+ alias = alias2;
+ else
+ {
+ const char *display_name = purple_connection_get_display_name(gc);
+ if (display_name != NULL)
+ alias = display_name;
+ }
+ } else {
+ PurpleBuddy *buddy;
+ if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), user)) != NULL)
+ alias = purple_buddy_get_contact_alias(buddy);
+ }
+ }
+
+ quiet = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_conversations_get_handle(),
+ "chat-user-joining", chat, user, flag)) ||
+ purple_chat_conversation_is_ignored_user(chat, user);
+
+ chatuser = purple_chat_user_new(chat, user, alias, flag);
+
+ g_hash_table_replace(priv->users,
+ g_strdup(purple_chat_user_get_name(chatuser)),
+ chatuser);
+
+ cbuddies = g_list_prepend(cbuddies, chatuser);
+
+ if (!quiet && new_arrivals) {
+ char *alias_esc = g_markup_escape_text(alias, -1);
+ char *tmp;
+
+ if (extra_msg == NULL)
+ tmp = g_strdup_printf(_("%s entered the room."), alias_esc);
+ else {
+ char *extra_msg_esc = g_markup_escape_text(extra_msg, -1);
+ tmp = g_strdup_printf(_("%s [<I>%s</I>] entered the room."),
+ alias_esc, extra_msg_esc);
+ g_free(extra_msg_esc);
+ }
+ g_free(alias_esc);
+
+ purple_conversation_write_system_message(
+ conv, tmp, PURPLE_MESSAGE_NO_LINKIFY);
+ g_free(tmp);
+ }
+
+ purple_signal_emit(purple_conversations_get_handle(),
+ "chat-user-joined", chat, user, flag, new_arrivals);
+ ul = ul->next;
+ fl = fl->next;
+ if (extra_msgs != NULL)
+ extra_msgs = extra_msgs->next;
+ }
+
+ cbuddies = g_list_sort(cbuddies, (GCompareFunc)purple_chat_user_compare);
+
+ if (ops != NULL && ops->chat_add_users != NULL)
+ ops->chat_add_users(chat, cbuddies, new_arrivals);
+
+ g_list_free(cbuddies);
+}
+
+void
+purple_chat_conversation_rename_user(PurpleChatConversation *chat, const char *old_user,
+ const char *new_user)
+{
+ PurpleConversation *conv;
+ PurpleConversationUiOps *ops;
+ PurpleAccount *account;
+ PurpleConnection *gc;
+ PurplePluginProtocolInfo *prpl_info;
+ PurpleChatUser *cb;
+ PurpleChatUserFlags flags;
+ PurpleChatConversationPrivate *priv;
+ const char *new_alias = new_user;
+ char tmp[BUF_LONG];
+ gboolean is_me = FALSE;
+
+ priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(old_user != NULL);
+ g_return_if_fail(new_user != NULL);
+
+ conv = PURPLE_CONVERSATION(chat);
+ ops = purple_conversation_get_ui_ops(conv);
+ account = purple_conversation_get_account(conv);
+
+ gc = purple_conversation_get_connection(conv);
+ g_return_if_fail(PURPLE_IS_CONNECTION(gc));
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
+ g_return_if_fail(prpl_info != NULL);
+
+ if (purple_strequal(priv->nick, purple_normalize(account, old_user))) {
+ const char *alias;
+
+ /* Note this for later. */
+ is_me = TRUE;
+
+ if(!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
+ alias = purple_account_get_private_alias(account);
+ if (alias != NULL)
+ new_alias = alias;
+ else
+ {
+ const char *display_name = purple_connection_get_display_name(gc);
+ if (display_name != NULL)
+ new_alias = display_name;
+ }
+ }
+ } else if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
+ PurpleBuddy *buddy;
+ if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), new_user)) != NULL)
+ new_alias = purple_buddy_get_contact_alias(buddy);
+ }
+
+ flags = purple_chat_user_get_flags(purple_chat_conversation_find_user(chat, old_user));
+ cb = purple_chat_user_new(chat, new_user, new_alias, flags);
+
+ g_hash_table_replace(priv->users,
+ g_strdup(purple_chat_user_get_name(cb)), cb);
+
+ if (ops != NULL && ops->chat_rename_user != NULL)
+ ops->chat_rename_user(chat, old_user, new_user, new_alias);
+
+ cb = purple_chat_conversation_find_user(chat, old_user);
+
+ if (cb)
+ g_hash_table_remove(priv->users, purple_chat_user_get_name(cb));
+
+ if (purple_chat_conversation_is_ignored_user(chat, old_user)) {
+ purple_chat_conversation_unignore(chat, old_user);
+ purple_chat_conversation_ignore(chat, new_user);
+ }
+ else if (purple_chat_conversation_is_ignored_user(chat, new_user))
+ purple_chat_conversation_unignore(chat, new_user);
+
+ if (is_me)
+ purple_chat_conversation_set_nick(chat, new_user);
+
+ if (purple_prefs_get_bool("/purple/conversations/chat/show_nick_change") &&
+ !purple_chat_conversation_is_ignored_user(chat, new_user)) {
+
+ if (is_me) {
+ char *escaped = g_markup_escape_text(new_user, -1);
+ g_snprintf(tmp, sizeof(tmp),
+ _("You are now known as %s"), escaped);
+ g_free(escaped);
+ } else {
+ const char *old_alias = old_user;
+ const char *new_alias = new_user;
+ char *escaped;
+ char *escaped2;
+
+ if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
+ PurpleBuddy *buddy;
+
+ if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), old_user)) != NULL)
+ old_alias = purple_buddy_get_contact_alias(buddy);
+ if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), new_user)) != NULL)
+ new_alias = purple_buddy_get_contact_alias(buddy);
+ }
+
+ escaped = g_markup_escape_text(old_alias, -1);
+ escaped2 = g_markup_escape_text(new_alias, -1);
+ g_snprintf(tmp, sizeof(tmp),
+ _("%s is now known as %s"), escaped, escaped2);
+ g_free(escaped);
+ g_free(escaped2);
+ }
+
+ purple_conversation_write_system_message(conv,
+ tmp, PURPLE_MESSAGE_NO_LINKIFY);
+ }
+}
+
+void
+purple_chat_conversation_remove_user(PurpleChatConversation *chat, const char *user, const char *reason)
+{
+ GList *users = g_list_append(NULL, (char *)user);
+
+ purple_chat_conversation_remove_users(chat, users, reason);
+
+ g_list_free(users);
+}
+
+void
+purple_chat_conversation_remove_users(PurpleChatConversation *chat, GList *users, const char *reason)
+{
+ PurpleConversation *conv;
+ PurpleConnection *gc;
+ PurplePluginProtocolInfo *prpl_info;
+ PurpleConversationUiOps *ops;
+ PurpleChatUser *cb;
+ PurpleChatConversationPrivate *priv;
+ GList *l;
+ gboolean quiet;
+
+ priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(users != NULL);
+
+ conv = PURPLE_CONVERSATION(chat);
+
+ gc = purple_conversation_get_connection(conv);
+ g_return_if_fail(PURPLE_IS_CONNECTION(gc));
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
+ g_return_if_fail(prpl_info != NULL);
+
+ ops = purple_conversation_get_ui_ops(conv);
+
+ for (l = users; l != NULL; l = l->next) {
+ const char *user = (const char *)l->data;
+ quiet = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_conversations_get_handle(),
+ "chat-user-leaving", chat, user, reason)) |
+ purple_chat_conversation_is_ignored_user(chat, user);
+
+ cb = purple_chat_conversation_find_user(chat, user);
+
+ if (cb) {
+ g_hash_table_remove(priv->users,
+ purple_chat_user_get_name(cb));
+ }
+
+ /* NOTE: Don't remove them from ignored in case they re-enter. */
+
+ if (!quiet) {
+ const char *alias = user;
+ char *alias_esc;
+ char *tmp;
+
+ if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
+ PurpleBuddy *buddy;
+
+ if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), user)) != NULL)
+ alias = purple_buddy_get_contact_alias(buddy);
+ }
+
+ alias_esc = g_markup_escape_text(alias, -1);
+
+ if (reason == NULL || !*reason)
+ tmp = g_strdup_printf(_("%s left the room."), alias_esc);
+ else {
+ char *reason_esc = g_markup_escape_text(reason, -1);
+ tmp = g_strdup_printf(_("%s left the room (%s)."),
+ alias_esc, reason_esc);
+ g_free(reason_esc);
+ }
+ g_free(alias_esc);
+
+ purple_conversation_write_system_message(conv,
+ tmp, PURPLE_MESSAGE_NO_LINKIFY);
+ g_free(tmp);
+ }
+
+ purple_signal_emit(purple_conversations_get_handle(), "chat-user-left",
+ conv, user, reason);
+ }
+
+ if (ops != NULL && ops->chat_remove_users != NULL)
+ ops->chat_remove_users(chat, users);
+}
+
+void
+purple_chat_conversation_clear_users(PurpleChatConversation *chat)
+{
+ PurpleConversationUiOps *ops;
+ GHashTableIter it;
+ PurpleChatConversationPrivate *priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+ gchar *name;
+
+ g_return_if_fail(priv != NULL);
+
+ ops = purple_conversation_get_ui_ops(PURPLE_CONVERSATION(chat));
+
+ if (ops != NULL && ops->chat_remove_users != NULL) {
+ GList *names = NULL;
+
+ g_hash_table_iter_init(&it, priv->users);
+ while (g_hash_table_iter_next(&it, (gpointer*)&name, NULL))
+ names = g_list_prepend(names, name);
+
+ ops->chat_remove_users(chat, names);
+ g_list_free(names);
+ }
+
+ g_hash_table_iter_init(&it, priv->users);
+ while (g_hash_table_iter_next(&it, (gpointer*)&name, NULL)) {
+ purple_signal_emit(purple_conversations_get_handle(),
+ "chat-user-leaving", chat, name, NULL);
+ purple_signal_emit(purple_conversations_get_handle(),
+ "chat-user-left", chat, name, NULL);
+ }
+
+ g_hash_table_remove_all(priv->users);
+}
+
+void purple_chat_conversation_set_nick(PurpleChatConversation *chat, const char *nick) {
+ PurpleChatConversationPrivate *priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+
+ g_return_if_fail(priv != NULL);
+
+ g_free(priv->nick);
+ priv->nick = g_strdup(purple_normalize(
+ purple_conversation_get_account(PURPLE_CONVERSATION(chat)), nick));
+
+ g_object_notify_by_pspec(G_OBJECT(chat), chat_properties[CHAT_PROP_NICK]);
+}
+
+const char *purple_chat_conversation_get_nick(PurpleChatConversation *chat) {
+ PurpleChatConversationPrivate *priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->nick;
+}
+
+static void
+invite_user_to_chat(gpointer data, PurpleRequestFields *fields)
+{
+ PurpleConversation *conv;
+ PurpleChatConversationPrivate *priv;
+ const char *user, *message;
+
+ conv = data;
+ priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(conv);
+ user = purple_request_fields_get_string(fields, "screenname");
+ message = purple_request_fields_get_string(fields, "message");
+
+ purple_serv_chat_invite(purple_conversation_get_connection(conv), priv->id, message, user);
+}
+
+void purple_chat_conversation_invite_user(PurpleChatConversation *chat, const char *user,
+ const char *message, gboolean confirm)
+{
+ PurpleAccount *account;
+ PurpleRequestFields *fields;
+ PurpleRequestFieldGroup *group;
+ PurpleRequestField *field;
+
+ g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
+
+ if (!user || !*user || !message || !*message)
+ confirm = TRUE;
+
+ account = purple_conversation_get_account(PURPLE_CONVERSATION(chat));
+
+ if (!confirm) {
+ purple_serv_chat_invite(purple_account_get_connection(account),
+ purple_chat_conversation_get_id(chat), message, user);
+ return;
+ }
+
+ fields = purple_request_fields_new();
+ group = purple_request_field_group_new(_("Invite to chat"));
+ purple_request_fields_add_group(fields, group);
+
+ field = purple_request_field_string_new("screenname", _("Buddy"), user, FALSE);
+ purple_request_field_group_add_field(group, field);
+ purple_request_field_set_required(field, TRUE);
+ purple_request_field_set_type_hint(field, "screenname");
+
+ field = purple_request_field_string_new("message", _("Message"), message, FALSE);
+ purple_request_field_group_add_field(group, field);
+
+ purple_request_fields(chat, _("Invite to chat"), NULL,
+ _("Please enter the name of the user you wish to invite, "
+ "along with an optional invite message."),
+ fields,
+ _("Invite"), G_CALLBACK(invite_user_to_chat),
+ _("Cancel"), NULL,
+ purple_request_cpar_from_conversation(PURPLE_CONVERSATION(chat)),
+ chat);
+}
+
+gboolean
+purple_chat_conversation_has_user(PurpleChatConversation *chat, const char *user)
+{
+ g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), FALSE);
+ g_return_val_if_fail(user != NULL, FALSE);
+
+ return (purple_chat_conversation_find_user(chat, user) != NULL);
+}
+
+void
+purple_chat_conversation_leave(PurpleChatConversation *chat)
+{
+ PurpleChatConversationPrivate *priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->left = TRUE;
+
+ if (!g_object_get_data(G_OBJECT(chat), "is-finalizing"))
+ g_object_notify_by_pspec(G_OBJECT(chat), chat_properties[CHAT_PROP_LEFT]);
+
+ purple_conversation_update(PURPLE_CONVERSATION(chat), PURPLE_CONVERSATION_UPDATE_CHATLEFT);
+}
+
+gboolean
+purple_chat_conversation_has_left(PurpleChatConversation *chat)
+{
+ PurpleChatConversationPrivate *priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+
+ g_return_val_if_fail(priv != NULL, TRUE);
+
+ return priv->left;
+}
+
+static void
+chat_conversation_cleanup_for_rejoin(PurpleChatConversation *chat)
+{
+ const char *disp;
+ PurpleAccount *account;
+ PurpleConnection *gc;
+ PurpleConversation *conv = PURPLE_CONVERSATION(chat);
+ PurpleChatConversationPrivate *priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+
+ account = purple_conversation_get_account(conv);
+
+ purple_conversation_close_logs(conv);
+ purple_conversation_set_logging(conv, TRUE);
+
+ gc = purple_account_get_connection(account);
+
+ if ((disp = purple_connection_get_display_name(gc)) != NULL)
+ purple_chat_conversation_set_nick(chat, disp);
+ else
+ {
+ purple_chat_conversation_set_nick(chat,
+ purple_account_get_username(account));
+ }
+
+ purple_chat_conversation_clear_users(chat);
+ purple_chat_conversation_set_topic(chat, NULL, NULL);
+ priv->left = FALSE;
+
+ g_object_notify_by_pspec(G_OBJECT(chat), chat_properties[CHAT_PROP_LEFT]);
+
+ purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_CHATLEFT);
+}
+
+PurpleChatUser *
+purple_chat_conversation_find_user(PurpleChatConversation *chat, const char *name)
+{
+ PurpleChatConversationPrivate *priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ return g_hash_table_lookup(priv->users, name);
+}
+
+/**************************************************************************
+ * GObject code for chats
+ **************************************************************************/
+
+/* Set method for GObject properties */
+static void
+purple_chat_conversation_set_property(GObject *obj, guint param_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(obj);
+
+ switch (param_id) {
+ case CHAT_PROP_ID:
+ purple_chat_conversation_set_id(chat, g_value_get_int(value));
+ break;
+ case CHAT_PROP_NICK:
+ purple_chat_conversation_set_nick(chat, g_value_get_string(value));
+ break;
+ case CHAT_PROP_LEFT:
+ {
+ gboolean left = g_value_get_boolean(value);
+ if (left)
+ purple_chat_conversation_leave(chat);
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* Get method for GObject properties */
+static void
+purple_chat_conversation_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(obj);
+
+ switch (param_id) {
+ case CHAT_PROP_TOPIC_WHO:
+ g_value_set_string(value, purple_chat_conversation_get_topic_who(chat));
+ break;
+ case CHAT_PROP_TOPIC:
+ g_value_set_string(value, purple_chat_conversation_get_topic(chat));
+ break;
+ case CHAT_PROP_ID:
+ g_value_set_int(value, purple_chat_conversation_get_id(chat));
+ break;
+ case CHAT_PROP_NICK:
+ g_value_set_string(value, purple_chat_conversation_get_nick(chat));
+ break;
+ case CHAT_PROP_LEFT:
+ g_value_set_boolean(value, purple_chat_conversation_has_left(chat));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* GObject initialization function */
+static void purple_chat_conversation_init(GTypeInstance *instance, gpointer klass)
+{
+ PurpleChatConversationPrivate *priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(instance);
+
+ PURPLE_DBUS_REGISTER_POINTER(PURPLE_CHAT_CONVERSATION(instance),
+ PurpleChatConversation);
+
+ priv->users = g_hash_table_new_full(_purple_conversation_user_hash,
+ _purple_conversation_user_equal, g_free, g_object_unref);
+}
+
+/* Called when done constructing */
+static void
+purple_chat_conversation_constructed(GObject *object)
+{
+ PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(object);
+ PurpleAccount *account;
+ const char *disp;
+
+ G_OBJECT_CLASS(parent_class)->constructed(object);
+
+ g_object_get(object, "account", &account, NULL);
+
+ if ((disp = purple_connection_get_display_name(purple_account_get_connection(account))))
+ purple_chat_conversation_set_nick(chat, disp);
+ else
+ purple_chat_conversation_set_nick(chat,
+ purple_account_get_username(account));
+
+ if (purple_prefs_get_bool("/purple/logging/log_chats"))
+ purple_conversation_set_logging(PURPLE_CONVERSATION(chat), TRUE);
+
+ g_object_unref(account);
+}
+
+/* GObject dispose function */
+static void
+purple_chat_conversation_dispose(GObject *object)
+{
+ PurpleChatConversationPrivate *priv =
+ PURPLE_CHAT_CONVERSATION_GET_PRIVATE(object);
+
+ g_hash_table_remove_all(priv->users);
+
+ G_OBJECT_CLASS(parent_class)->dispose(object);
+}
+
+/* GObject finalize function */
+static void
+purple_chat_conversation_finalize(GObject *object)
+{
+ PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(object);
+ PurpleConnection *gc = purple_conversation_get_connection(PURPLE_CONVERSATION(chat));
+ PurpleChatConversationPrivate *priv = PURPLE_CHAT_CONVERSATION_GET_PRIVATE(chat);
+
+ if (gc != NULL)
+ {
+ /* Still connected */
+ int chat_id = purple_chat_conversation_get_id(chat);
+#if 0
+ /*
+ * This is unfortunately necessary, because calling
+ * purple_serv_chat_leave() calls this purple_conversation_destroy(),
+ * which leads to two calls here.. We can't just return after
+ * this, because then it'll return on the next pass. So, since
+ * purple_serv_got_chat_left(), which is eventually called from the
+ * prpl that purple_serv_chat_leave() calls, removes this conversation
+ * from the gc's buddy_chats list, we're going to check to see
+ * if this exists in the list. If so, we want to return after
+ * calling this, because it'll be called again. If not, fall
+ * through, because it'll have already been removed, and we'd
+ * be on the 2nd pass.
+ *
+ * Long paragraph. <-- Short sentence.
+ *
+ * -- ChipX86
+ */
+
+ if (gc && g_slist_find(gc->buddy_chats, conv) != NULL) {
+ purple_serv_chat_leave(gc, chat_id);
+
+ return;
+ }
+#endif
+ /*
+ * Instead of all of that, lets just close the window when
+ * the user tells us to, and let the prpl deal with the
+ * internals on it's own time. Don't do this if the prpl already
+ * knows it left the chat.
+ */
+ if (!purple_chat_conversation_has_left(chat))
+ purple_serv_chat_leave(gc, chat_id);
+
+ /*
+ * If they didn't call purple_serv_got_chat_left by now, it's too late.
+ * So we better do it for them before we destroy the thing.
+ */
+ if (!purple_chat_conversation_has_left(chat))
+ purple_serv_got_chat_left(gc, chat_id);
+ }
+
+ g_hash_table_destroy(priv->users);
+ priv->users = NULL;
+
+ g_list_foreach(priv->ignored, (GFunc)g_free, NULL);
+ g_list_free(priv->ignored);
+ priv->ignored = NULL;
+
+ g_free(priv->who);
+ g_free(priv->topic);
+ g_free(priv->nick);
+
+ priv->who = NULL;
+ priv->topic = NULL;
+ priv->nick = NULL;
+
+ PURPLE_DBUS_UNREGISTER_POINTER(chat);
+
+ G_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+/* Class initializer function */
+static void purple_chat_conversation_class_init(PurpleChatConversationClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+ PurpleConversationClass *conv_class = PURPLE_CONVERSATION_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ obj_class->dispose = purple_chat_conversation_dispose;
+ obj_class->finalize = purple_chat_conversation_finalize;
+ obj_class->constructed = purple_chat_conversation_constructed;
+
+ /* Setup properties */
+ obj_class->get_property = purple_chat_conversation_get_property;
+ obj_class->set_property = purple_chat_conversation_set_property;
+
+ conv_class->write_message = chat_conversation_write_message;
+
+ g_type_class_add_private(klass, sizeof(PurpleChatConversationPrivate));
+
+ chat_properties[CHAT_PROP_TOPIC_WHO] = g_param_spec_string("topic-who",
+ "Who set topic",
+ "Who set the chat topic.", NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ chat_properties[CHAT_PROP_TOPIC] = g_param_spec_string("topic",
+ "Topic",
+ "Topic of the chat.", NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ chat_properties[CHAT_PROP_ID] = g_param_spec_int("chat-id",
+ "Chat ID",
+ "The ID of the chat.", G_MININT, G_MAXINT, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ chat_properties[CHAT_PROP_NICK] = g_param_spec_string("nick",
+ "Nickname",
+ "The nickname of the user in a chat.", NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ chat_properties[CHAT_PROP_LEFT] = g_param_spec_boolean("left",
+ "Left the chat",
+ "Whether the user has left the chat.", FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, CHAT_PROP_LAST,
+ chat_properties);
+}
+
+GType
+purple_chat_conversation_get_type(void)
+{
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleChatConversationClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_chat_conversation_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleChatConversation),
+ 0,
+ (GInstanceInitFunc)purple_chat_conversation_init,
+ NULL,
+ };
+
+ type = g_type_register_static(PURPLE_TYPE_CONVERSATION,
+ "PurpleChatConversation",
+ &info, 0);
+ }
+
+ return type;
+}
+
+PurpleChatConversation *
+purple_chat_conversation_new(PurpleAccount *account, const char *name)
+{
+ PurpleChatConversation *chat;
+ PurpleConnection *gc;
+
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ /* Check if this conversation already exists. */
+ if ((chat = purple_conversations_find_chat_with_account(name, account)) != NULL)
+ {
+ if (!purple_chat_conversation_has_left(chat)) {
+ purple_debug_warning("conversationtypes", "Trying to create "
+ "multiple chats (%s) with the same name is deprecated and "
+ "will be removed in libpurple 3.0.0", name);
+ } else {
+ /*
+ * This hack is necessary because some prpls (MSN) have unnamed chats
+ * that all use the same name. A PurpleConversation for one of those
+ * is only ever re-used if the user has left, so calls to
+ * purple_conversation_new need to fall-through to creating a new
+ * chat.
+ * TODO 3.0.0: Remove this workaround and mandate unique names.
+ */
+
+ chat_conversation_cleanup_for_rejoin(chat);
+ return chat;
+ }
+ }
+
+ gc = purple_account_get_connection(account);
+ g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
+
+ chat = g_object_new(PURPLE_TYPE_CHAT_CONVERSATION,
+ "account", account,
+ "name", name,
+ "title", name,
+ NULL);
+
+ return chat;
+}
+
+/**************************************************************************
+ * Chat Conversation User API
+ **************************************************************************/
+static int
+purple_chat_user_compare(PurpleChatUser *a, PurpleChatUser *b)
+{
+ PurpleChatUserFlags f1 = 0, f2 = 0;
+ PurpleChatUserPrivate *priva, *privb;
+ char *user1 = NULL, *user2 = NULL;
+ gint ret = 0;
+
+ priva = PURPLE_CHAT_USER_GET_PRIVATE(a);
+ privb = PURPLE_CHAT_USER_GET_PRIVATE(b);
+
+ if (priva) {
+ f1 = priva->flags;
+ if (priva->alias_key)
+ user1 = priva->alias_key;
+ else if (priva->name)
+ user1 = priva->name;
+ }
+
+ if (privb) {
+ f2 = privb->flags;
+ if (privb->alias_key)
+ user2 = privb->alias_key;
+ else if (privb->name)
+ user2 = privb->name;
+ }
+
+ if (user1 == NULL || user2 == NULL) {
+ if (!(user1 == NULL && user2 == NULL))
+ ret = (user1 == NULL) ? -1: 1;
+ } else if (f1 != f2) {
+ /* sort more important users first */
+ ret = (f1 > f2) ? -1 : 1;
+ } else if (priva->buddy != privb->buddy) {
+ ret = priva->buddy ? -1 : 1;
+ } else {
+ ret = purple_utf8_strcasecmp(user1, user2);
+ }
+
+ return ret;
+}
+
+const char *
+purple_chat_user_get_alias(const PurpleChatUser *cb)
+{
+ PurpleChatUserPrivate *priv;
+ priv = PURPLE_CHAT_USER_GET_PRIVATE(cb);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->alias;
+}
+
+const char *
+purple_chat_user_get_name(const PurpleChatUser *cb)
+{
+ PurpleChatUserPrivate *priv;
+ priv = PURPLE_CHAT_USER_GET_PRIVATE(cb);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->name;
+}
+
+void
+purple_chat_user_set_flags(PurpleChatUser *cb,
+ PurpleChatUserFlags flags)
+{
+ PurpleConversationUiOps *ops;
+ PurpleChatUserFlags oldflags;
+ PurpleChatUserPrivate *priv;
+ priv = PURPLE_CHAT_USER_GET_PRIVATE(cb);
+
+ g_return_if_fail(priv != NULL);
+
+ if (flags == priv->flags)
+ return;
+
+ oldflags = priv->flags;
+ priv->flags = flags;
+
+ g_object_notify_by_pspec(G_OBJECT(cb), cu_properties[CU_PROP_FLAGS]);
+
+ ops = purple_conversation_get_ui_ops(PURPLE_CONVERSATION(priv->chat));
+
+ if (ops != NULL && ops->chat_update_user != NULL)
+ ops->chat_update_user(cb);
+
+ purple_signal_emit(purple_conversations_get_handle(),
+ "chat-user-flags", cb, oldflags, flags);
+}
+
+PurpleChatUserFlags
+purple_chat_user_get_flags(const PurpleChatUser *cb)
+{
+ PurpleChatUserPrivate *priv;
+ priv = PURPLE_CHAT_USER_GET_PRIVATE(cb);
+
+ g_return_val_if_fail(priv != NULL, PURPLE_CHAT_USER_NONE);
+
+ return priv->flags;
+}
+
+void
+purple_chat_user_set_ui_data(PurpleChatUser *cb, gpointer ui_data)
+{
+ g_return_if_fail(PURPLE_IS_CHAT_USER(cb));
+
+ cb->ui_data = ui_data;
+}
+
+gpointer
+purple_chat_user_get_ui_data(const PurpleChatUser *cb)
+{
+ g_return_val_if_fail(PURPLE_IS_CHAT_USER(cb), NULL);
+
+ return cb->ui_data;
+}
+
+void
+purple_chat_user_set_chat(PurpleChatUser *cb,
+ PurpleChatConversation *chat)
+{
+ PurpleChatUserPrivate *priv;
+ priv = PURPLE_CHAT_USER_GET_PRIVATE(cb);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->chat = chat;
+
+ g_object_notify_by_pspec(G_OBJECT(cb), cu_properties[CU_PROP_CHAT]);
+}
+
+PurpleChatConversation *
+purple_chat_user_get_chat(const PurpleChatUser *cb)
+{
+ PurpleChatUserPrivate *priv;
+ priv = PURPLE_CHAT_USER_GET_PRIVATE(cb);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->chat;
+}
+
+gboolean
+purple_chat_user_is_buddy(const PurpleChatUser *cb)
+{
+ PurpleChatUserPrivate *priv;
+ priv = PURPLE_CHAT_USER_GET_PRIVATE(cb);
+
+ g_return_val_if_fail(priv != NULL, FALSE);
+
+ return priv->buddy;
+}
+
+/**************************************************************************
+ * GObject code for chat user
+ **************************************************************************/
+
+/* Set method for GObject properties */
+static void
+purple_chat_user_set_property(GObject *obj, guint param_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleChatUser *cb = PURPLE_CHAT_USER(obj);
+ PurpleChatUserPrivate *priv = PURPLE_CHAT_USER_GET_PRIVATE(cb);
+
+ switch (param_id) {
+ case CU_PROP_CHAT:
+ priv->chat = g_value_get_object(value);
+ break;
+ case CU_PROP_NAME:
+ g_free(priv->name);
+ priv->name = g_strdup(g_value_get_string(value));
+ break;
+ case CU_PROP_ALIAS:
+ g_free(priv->alias);
+ priv->alias = g_strdup(g_value_get_string(value));
+ break;
+ case CU_PROP_FLAGS:
+ priv->flags = g_value_get_flags(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* Get method for GObject properties */
+static void
+purple_chat_user_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleChatUser *cb = PURPLE_CHAT_USER(obj);
+
+ switch (param_id) {
+ case CU_PROP_CHAT:
+ g_value_set_object(value, purple_chat_user_get_chat(cb));
+ break;
+ case CU_PROP_NAME:
+ g_value_set_string(value, purple_chat_user_get_name(cb));
+ break;
+ case CU_PROP_ALIAS:
+ g_value_set_string(value, purple_chat_user_get_alias(cb));
+ break;
+ case CU_PROP_FLAGS:
+ g_value_set_flags(value, purple_chat_user_get_flags(cb));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* GObject initialization function */
+static void
+purple_chat_user_init(GTypeInstance *instance, gpointer klass)
+{
+ PURPLE_DBUS_REGISTER_POINTER(PURPLE_CHAT_USER(instance), PurpleChatUser);
+}
+
+/* Called when done constructing */
+static void
+purple_chat_user_constructed(GObject *object)
+{
+ PurpleChatUserPrivate *priv = PURPLE_CHAT_USER_GET_PRIVATE(object);
+ PurpleAccount *account;
+
+ cb_parent_class->constructed(object);
+
+ account = purple_conversation_get_account(PURPLE_CONVERSATION(priv->chat));
+
+ if (purple_blist_find_buddy(account, priv->name) != NULL)
+ priv->buddy = TRUE;
+}
+
+/* GObject finalize function */
+static void
+purple_chat_user_finalize(GObject *object)
+{
+ PurpleChatUser *cb = PURPLE_CHAT_USER(object);
+ PurpleChatUserPrivate *priv = PURPLE_CHAT_USER_GET_PRIVATE(cb);
+
+ purple_signal_emit(purple_conversations_get_handle(),
+ "deleting-chat-user", cb);
+
+ g_free(priv->alias);
+ g_free(priv->alias_key);
+ g_free(priv->name);
+
+ PURPLE_DBUS_UNREGISTER_POINTER(cb);
+
+ cb_parent_class->finalize(object);
+}
+
+/* Class initializer function */
+static void purple_chat_user_class_init(PurpleChatUserClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ cb_parent_class = g_type_class_peek_parent(klass);
+
+ obj_class->constructed = purple_chat_user_constructed;
+ obj_class->finalize = purple_chat_user_finalize;
+
+ /* Setup properties */
+ obj_class->get_property = purple_chat_user_get_property;
+ obj_class->set_property = purple_chat_user_set_property;
+
+ g_type_class_add_private(klass, sizeof(PurpleChatUserPrivate));
+
+ cu_properties[CU_PROP_CHAT] = g_param_spec_object("chat", "Chat",
+ "The chat the buddy belongs to.", PURPLE_TYPE_CHAT_CONVERSATION,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ cu_properties[CU_PROP_NAME] = g_param_spec_string("name", "Name",
+ "Name of the chat user.", NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+ cu_properties[CU_PROP_ALIAS] = g_param_spec_string("alias", "Alias",
+ "Alias of the chat user.", NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+ cu_properties[CU_PROP_FLAGS] = g_param_spec_flags("flags", "Buddy flags",
+ "The flags for the chat user.",
+ PURPLE_TYPE_CHAT_USER_FLAGS, PURPLE_CHAT_USER_NONE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, CU_PROP_LAST, cu_properties);
+}
+
+GType
+purple_chat_user_get_type(void)
+{
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleChatUserClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_chat_user_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleChatUser),
+ 0,
+ (GInstanceInitFunc)purple_chat_user_init,
+ NULL,
+ };
+
+ type = g_type_register_static(G_TYPE_OBJECT,
+ "PurpleChatUser",
+ &info, 0);
+ }
+
+ return type;
+}
+
+PurpleChatUser *
+purple_chat_user_new(PurpleChatConversation *chat, const char *name,
+ const char *alias, PurpleChatUserFlags flags)
+{
+ PurpleChatUser *cb;
+
+ g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ cb = g_object_new(PURPLE_TYPE_CHAT_USER,
+ "chat", chat,
+ "name", name,
+ "alias", alias,
+ "flags", flags,
+ NULL);
+
+ return cb;
+}
diff --git a/libpurple/conversationtypes.h b/libpurple/conversationtypes.h
new file mode 100644
index 0000000000..517c83263a
--- /dev/null
+++ b/libpurple/conversationtypes.h
@@ -0,0 +1,791 @@
+/* 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 _PURPLE_CONVERSATION_TYPES_H_
+#define _PURPLE_CONVERSATION_TYPES_H_
+/**
+ * SECTION:conversationtypes
+ * @section_id: libpurple-conversationtypes
+ * @short_description: <filename>conversationtypes.h</filename>
+ * @title: Chat and IM Conversation Objects
+ */
+
+/**************************************************************************/
+/** Data Structures */
+/**************************************************************************/
+
+#define PURPLE_TYPE_IM_CONVERSATION (purple_im_conversation_get_type())
+#define PURPLE_IM_CONVERSATION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_IM_CONVERSATION, PurpleIMConversation))
+#define PURPLE_IM_CONVERSATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_IM_CONVERSATION, PurpleIMConversationClass))
+#define PURPLE_IS_IM_CONVERSATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_IM_CONVERSATION))
+#define PURPLE_IS_IM_CONVERSATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_IM_CONVERSATION))
+#define PURPLE_IM_CONVERSATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_IM_CONVERSATION, PurpleIMConversationClass))
+
+typedef struct _PurpleIMConversation PurpleIMConversation;
+typedef struct _PurpleIMConversationClass PurpleIMConversationClass;
+
+#define PURPLE_TYPE_CHAT_CONVERSATION (purple_chat_conversation_get_type())
+#define PURPLE_CHAT_CONVERSATION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_CHAT_CONVERSATION, PurpleChatConversation))
+#define PURPLE_CHAT_CONVERSATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_CHAT_CONVERSATION, PurpleChatConversationClass))
+#define PURPLE_IS_CHAT_CONVERSATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_CHAT_CONVERSATION))
+#define PURPLE_IS_CHAT_CONVERSATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_CHAT_CONVERSATION))
+#define PURPLE_CHAT_CONVERSATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_CHAT_CONVERSATION, PurpleChatConversationClass))
+
+typedef struct _PurpleChatConversation PurpleChatConversation;
+typedef struct _PurpleChatConversationClass PurpleChatConversationClass;
+
+#define PURPLE_TYPE_CHAT_USER (purple_chat_user_get_type())
+#define PURPLE_CHAT_USER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_CHAT_USER, PurpleChatUser))
+#define PURPLE_CHAT_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_CHAT_USER, PurpleChatUserClass))
+#define PURPLE_IS_CHAT_USER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_CHAT_USER))
+#define PURPLE_IS_CHAT_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_CHAT_USER))
+#define PURPLE_CHAT_USER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_CHAT_USER, PurpleChatUserClass))
+
+typedef struct _PurpleChatUser PurpleChatUser;
+typedef struct _PurpleChatUserClass PurpleChatUserClass;
+
+/**
+ * PurpleIMTypingState:
+ * @PURPLE_IM_NOT_TYPING: Not typing.
+ * @PURPLE_IM_TYPING: Currently typing.
+ * @PURPLE_IM_TYPED: Stopped typing momentarily.
+ *
+ * The typing state of a user.
+ */
+typedef enum
+{
+ PURPLE_IM_NOT_TYPING = 0,
+ PURPLE_IM_TYPING,
+ PURPLE_IM_TYPED
+
+} PurpleIMTypingState;
+
+/**
+ * PurpleChatUserFlags:
+ * @PURPLE_CHAT_USER_NONE: No flags
+ * @PURPLE_CHAT_USER_VOICE: Voiced user or "Participant"
+ * @PURPLE_CHAT_USER_HALFOP: Half-op
+ * @PURPLE_CHAT_USER_OP: Channel Op or Moderator
+ * @PURPLE_CHAT_USER_FOUNDER: Channel Founder
+ * @PURPLE_CHAT_USER_TYPING: Currently typing
+ * @PURPLE_CHAT_USER_AWAY: Currently away.
+ *
+ * Flags applicable to users in Chats.
+ */
+typedef enum /*< flags >*/
+{
+ PURPLE_CHAT_USER_NONE = 0x0000,
+ PURPLE_CHAT_USER_VOICE = 0x0001,
+ PURPLE_CHAT_USER_HALFOP = 0x0002,
+ PURPLE_CHAT_USER_OP = 0x0004,
+ PURPLE_CHAT_USER_FOUNDER = 0x0008,
+ PURPLE_CHAT_USER_TYPING = 0x0010,
+ PURPLE_CHAT_USER_AWAY = 0x0020
+
+} PurpleChatUserFlags;
+
+#include "conversation.h"
+
+/**************************************************************************/
+/** PurpleIMConversation */
+/**************************************************************************/
+/**
+ * PurpleIMConversation:
+ *
+ * Structure representing an IM conversation instance.
+ */
+struct _PurpleIMConversation
+{
+ PurpleConversation parent_object;
+};
+
+/**
+ * PurpleIMConversationClass:
+ *
+ * Base class for all #PurpleIMConversation's
+ */
+struct _PurpleIMConversationClass {
+ PurpleConversationClass parent_class;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+/**************************************************************************/
+/** PurpleChatConversation */
+/**************************************************************************/
+/**
+ * PurpleChatConversation:
+ *
+ * Structure representing a chat conversation instance.
+ */
+struct _PurpleChatConversation
+{
+ PurpleConversation parent_object;
+};
+
+/**
+ * PurpleChatConversationClass:
+ *
+ * Base class for all #PurpleChatConversation's
+ */
+struct _PurpleChatConversationClass {
+ PurpleConversationClass parent_class;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+/**************************************************************************/
+/** PurpleChatUser */
+/**************************************************************************/
+/**
+ * PurpleChatUser:
+ * @ui_data: The UI data associated with this chat user. This is a convenience
+ * field provided to the UIs -- it is not used by the libpurple core.
+ *
+ * Structure representing a chat user instance.
+ */
+struct _PurpleChatUser
+{
+ GObject gparent;
+
+ /*< public >*/
+ gpointer ui_data;
+};
+
+/**
+ * PurpleChatUserClass:
+ *
+ * Base class for all #PurpleChatUser's
+ */
+struct _PurpleChatUserClass {
+ GObjectClass parent_class;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+/**************************************************************************/
+/* IM Conversation API */
+/**************************************************************************/
+
+/**
+ * purple_im_conversation_get_type:
+ *
+ * Returns: The #GType for the IMConversation object.
+ */
+GType purple_im_conversation_get_type(void);
+
+/**
+ * purple_im_conversation_new:
+ * @account: The account opening the conversation window on the purple
+ * user's end.
+ * @name: Name of the buddy.
+ *
+ * Creates a new IM conversation.
+ *
+ * Returns: The new conversation.
+ */
+PurpleIMConversation *purple_im_conversation_new(PurpleAccount *account,
+ const char *name);
+
+/**
+ * purple_im_conversation_set_icon:
+ * @im: The IM.
+ * @icon: The buddy icon.
+ *
+ * Sets the IM's buddy icon.
+ *
+ * This should only be called from within Purple. You probably want to
+ * call purple_buddy_icon_set_data().
+ *
+ * See purple_buddy_icon_set_data().
+ */
+void purple_im_conversation_set_icon(PurpleIMConversation *im, PurpleBuddyIcon *icon);
+
+/**
+ * purple_im_conversation_get_icon:
+ * @im: The IM.
+ *
+ * Returns the IM's buddy icon.
+ *
+ * Returns: The buddy icon.
+ */
+PurpleBuddyIcon *purple_im_conversation_get_icon(const PurpleIMConversation *im);
+
+/**
+ * purple_im_conversation_set_typing_state:
+ * @im: The IM.
+ * @state: The typing state.
+ *
+ * Sets the IM's typing state.
+ */
+void purple_im_conversation_set_typing_state(PurpleIMConversation *im, PurpleIMTypingState state);
+
+/**
+ * purple_im_conversation_get_typing_state:
+ * @im: The IM.
+ *
+ * Returns the IM's typing state.
+ *
+ * Returns: The IM's typing state.
+ */
+PurpleIMTypingState purple_im_conversation_get_typing_state(const PurpleIMConversation *im);
+
+/**
+ * purple_im_conversation_start_typing_timeout:
+ * @im: The IM.
+ * @timeout: How long in seconds to wait before setting the typing state
+ * to PURPLE_IM_NOT_TYPING.
+ *
+ * Starts the IM's typing timeout.
+ */
+void purple_im_conversation_start_typing_timeout(PurpleIMConversation *im, int timeout);
+
+/**
+ * purple_im_conversation_stop_typing_timeout:
+ * @im: The IM.
+ *
+ * Stops the IM's typing timeout.
+ */
+void purple_im_conversation_stop_typing_timeout(PurpleIMConversation *im);
+
+/**
+ * purple_im_conversation_get_typing_timeout:
+ * @im: The IM.
+ *
+ * Returns the IM's typing timeout.
+ *
+ * Returns: The timeout.
+ */
+guint purple_im_conversation_get_typing_timeout(const PurpleIMConversation *im);
+
+/**
+ * purple_im_conversation_set_type_again:
+ * @im: The IM.
+ * @val: The number of seconds to wait before allowing another
+ * PURPLE_IM_TYPING message to be sent to the user. Or 0 to
+ * not send another PURPLE_IM_TYPING message.
+ *
+ * Sets the quiet-time when no PURPLE_IM_TYPING messages will be sent.
+ * Few protocols need this (maybe only MSN). If the user is still
+ * typing after this quiet-period, then another PURPLE_IM_TYPING message
+ * will be sent.
+ */
+void purple_im_conversation_set_type_again(PurpleIMConversation *im, unsigned int val);
+
+/**
+ * purple_im_conversation_get_type_again:
+ * @im: The IM.
+ *
+ * Returns the time after which another PURPLE_IM_TYPING message should be sent.
+ *
+ * Returns: The time in seconds since the epoch. Or 0 if no additional
+ * PURPLE_IM_TYPING message should be sent.
+ */
+time_t purple_im_conversation_get_type_again(const PurpleIMConversation *im);
+
+/**
+ * purple_im_conversation_start_send_typed_timeout:
+ * @im: The IM.
+ *
+ * Starts the IM's type again timeout.
+ */
+void purple_im_conversation_start_send_typed_timeout(PurpleIMConversation *im);
+
+/**
+ * purple_im_conversation_stop_send_typed_timeout:
+ * @im: The IM.
+ *
+ * Stops the IM's type again timeout.
+ */
+void purple_im_conversation_stop_send_typed_timeout(PurpleIMConversation *im);
+
+/**
+ * purple_im_conversation_get_send_typed_timeout:
+ * @im: The IM.
+ *
+ * Returns the IM's type again timeout interval.
+ *
+ * Returns: The type again timeout interval.
+ */
+guint purple_im_conversation_get_send_typed_timeout(const PurpleIMConversation *im);
+
+/**
+ * purple_im_conversation_update_typing:
+ * @im: The IM.
+ *
+ * Updates the visual typing notification for an IM conversation.
+ */
+void purple_im_conversation_update_typing(PurpleIMConversation *im);
+
+/**************************************************************************/
+/* Chat Conversation API */
+/**************************************************************************/
+
+/**
+ * purple_chat_conversation_get_type:
+ *
+ * Returns: The #GType for the ChatConversation object.
+ */
+GType purple_chat_conversation_get_type(void);
+
+/**
+ * purple_chat_conversation_new:
+ * @account: The account opening the conversation window on the purple
+ * user's end.
+ * @name: The name of the conversation.
+ *
+ * Creates a new chat conversation.
+ *
+ * Returns: The new conversation.
+ */
+PurpleChatConversation *purple_chat_conversation_new(PurpleAccount *account,
+ const char *name);
+
+/**
+ * purple_chat_conversation_get_users:
+ * @chat: The chat.
+ *
+ * Returns a list of users in the chat room. The members of the list
+ * are PurpleChatUser objects.
+ *
+ * Returns: (transfer container): The list of users. Use g_list_free() when done
+ * using the list.
+ */
+GList *purple_chat_conversation_get_users(const PurpleChatConversation *chat);
+
+/**
+ * purple_chat_conversation_get_users_count:
+ * @chat: The chat.
+ *
+ * Returns count of users in the chat room.
+ *
+ * Returns: The count of users in the chat room.
+ */
+guint
+purple_chat_conversation_get_users_count(const PurpleChatConversation *chat);
+
+/**
+ * purple_chat_conversation_ignore:
+ * @chat: The chat.
+ * @name: The name of the user.
+ *
+ * Ignores a user in a chat room.
+ */
+void purple_chat_conversation_ignore(PurpleChatConversation *chat, const char *name);
+
+/**
+ * purple_chat_conversation_unignore:
+ * @chat: The chat.
+ * @name: The name of the user.
+ *
+ * Unignores a user in a chat room.
+ */
+void purple_chat_conversation_unignore(PurpleChatConversation *chat, const char *name);
+
+/**
+ * purple_chat_conversation_set_ignored:
+ * @chat: The chat.
+ * @ignored: The list of ignored users.
+ *
+ * Sets the list of ignored users in the chat room.
+ *
+ * Returns: The list passed.
+ */
+GList *purple_chat_conversation_set_ignored(PurpleChatConversation *chat, GList *ignored);
+
+/**
+ * purple_chat_conversation_get_ignored:
+ * @chat: The chat.
+ *
+ * Returns the list of ignored users in the chat room.
+ *
+ * Returns: (transfer none): The list of ignored users.
+ */
+GList *purple_chat_conversation_get_ignored(const PurpleChatConversation *chat);
+
+/**
+ * purple_chat_conversation_get_ignored_user:
+ * @chat: The chat.
+ * @user: The user to check in the ignore list.
+ *
+ * Returns the actual name of the specified ignored user, if it exists in
+ * the ignore list.
+ *
+ * If the user found contains a prefix, such as '+' or '\@', this is also
+ * returned. The username passed to the function does not have to have this
+ * formatting.
+ *
+ * Returns: The ignored user if found, complete with prefixes, or %NULL
+ * if not found.
+ */
+const char *purple_chat_conversation_get_ignored_user(const PurpleChatConversation *chat,
+ const char *user);
+
+/**
+ * purple_chat_conversation_is_ignored_user:
+ * @chat: The chat.
+ * @user: The user.
+ *
+ * Returns %TRUE if the specified user is ignored.
+ *
+ * Returns: %TRUE if the user is in the ignore list; %FALSE otherwise.
+ */
+gboolean purple_chat_conversation_is_ignored_user(const PurpleChatConversation *chat,
+ const char *user);
+
+/**
+ * purple_chat_conversation_set_topic:
+ * @chat: The chat.
+ * @who: The user that set the topic.
+ * @topic: The topic.
+ *
+ * Sets the chat room's topic.
+ */
+void purple_chat_conversation_set_topic(PurpleChatConversation *chat, const char *who,
+ const char *topic);
+
+/**
+ * purple_chat_conversation_get_topic:
+ * @chat: The chat.
+ *
+ * Returns the chat room's topic.
+ *
+ * Returns: The chat's topic.
+ */
+const char *purple_chat_conversation_get_topic(const PurpleChatConversation *chat);
+
+/**
+ * purple_chat_conversation_get_topic_who:
+ * @chat: The chat.
+ *
+ * Returns who set the chat room's topic.
+ *
+ * Returns: Who set the topic.
+ */
+const char *purple_chat_conversation_get_topic_who(const PurpleChatConversation *chat);
+
+/**
+ * purple_chat_conversation_set_id:
+ * @chat: The chat.
+ * @id: The ID.
+ *
+ * Sets the chat room's ID.
+ */
+void purple_chat_conversation_set_id(PurpleChatConversation *chat, int id);
+
+/**
+ * purple_chat_conversation_get_id:
+ * @chat: The chat.
+ *
+ * Returns the chat room's ID.
+ *
+ * Returns: The ID.
+ */
+int purple_chat_conversation_get_id(const PurpleChatConversation *chat);
+
+/**
+ * purple_chat_conversation_add_user:
+ * @chat: The chat.
+ * @user: The user to add.
+ * @extra_msg: An extra message to display with the join message.
+ * @flags: The users flags
+ * @new_arrival: Decides whether or not to show a join notice.
+ *
+ * Adds a user to a chat.
+ */
+void purple_chat_conversation_add_user(PurpleChatConversation *chat, const char *user,
+ const char *extra_msg, PurpleChatUserFlags flags,
+ gboolean new_arrival);
+
+/**
+ * purple_chat_conversation_add_users:
+ * @chat: The chat.
+ * @users: The list of users to add.
+ * @extra_msgs: 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: The list of flags for each user.
+ * @new_arrivals: Decides whether or not to show join notices.
+ *
+ * Adds a list of users to a chat.
+ *
+ * The data is copied from @users, @extra_msgs, and @flags, so it is up to
+ * the caller to free this list after calling this function.
+ */
+void purple_chat_conversation_add_users(PurpleChatConversation *chat,
+ GList *users, GList *extra_msgs, GList *flags, gboolean new_arrivals);
+
+/**
+ * purple_chat_conversation_rename_user:
+ * @chat: The chat.
+ * @old_user: The old username.
+ * @new_user: The new username.
+ *
+ * Renames a user in a chat.
+ */
+void purple_chat_conversation_rename_user(PurpleChatConversation *chat,
+ const char *old_user, const char *new_user);
+
+/**
+ * purple_chat_conversation_remove_user:
+ * @chat: The chat.
+ * @user: The user that is being removed.
+ * @reason: The optional reason given for the removal. Can be %NULL.
+ *
+ * Removes a user from a chat, optionally with a reason.
+ *
+ * It is up to the developer to free this list after calling this function.
+ */
+void purple_chat_conversation_remove_user(PurpleChatConversation *chat,
+ const char *user, const char *reason);
+
+/**
+ * purple_chat_conversation_remove_users:
+ * @chat: The chat.
+ * @users: 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.
+ */
+void purple_chat_conversation_remove_users(PurpleChatConversation *chat,
+ GList *users, const char *reason);
+
+/**
+ * purple_chat_conversation_has_user:
+ * @chat: The chat.
+ * @user: The user to look for.
+ *
+ * Checks if a user is in a chat
+ *
+ * Returns: TRUE if the user is in the chat, FALSE if not
+ */
+gboolean purple_chat_conversation_has_user(PurpleChatConversation *chat,
+ const char *user);
+
+/**
+ * purple_chat_conversation_clear_users:
+ * @chat: The chat.
+ *
+ * Clears all users from a chat.
+ */
+void purple_chat_conversation_clear_users(PurpleChatConversation *chat);
+
+/**
+ * purple_chat_conversation_set_nick:
+ * @chat: The chat.
+ * @nick: The nick.
+ *
+ * Sets your nickname (used for hilighting) for a chat.
+ */
+void purple_chat_conversation_set_nick(PurpleChatConversation *chat,
+ const char *nick);
+
+/**
+ * purple_chat_conversation_get_nick:
+ * @chat: The chat.
+ *
+ * Gets your nickname (used for hilighting) for a chat.
+ *
+ * Returns: The nick.
+ */
+const char *purple_chat_conversation_get_nick(PurpleChatConversation *chat);
+
+/**
+ * purple_chat_conversation_leave:
+ * @chat: The chat.
+ *
+ * Lets the core know we left a chat, without destroying it.
+ * Called from purple_serv_got_chat_left().
+ */
+void purple_chat_conversation_leave(PurpleChatConversation *chat);
+
+/**
+ * purple_chat_conversation_find_user:
+ * @chat: The chat.
+ * @name: The name of the chat user to find.
+ *
+ * Find a chat user in a chat
+ */
+PurpleChatUser *purple_chat_conversation_find_user(PurpleChatConversation *chat,
+ const char *name);
+
+/**
+ * purple_chat_conversation_invite_user:
+ * @chat: The chat.
+ * @user: The user to invite to the chat.
+ * @message: The message to send with the invitation.
+ * @confirm: Prompt before sending the invitation. The user is always
+ * prompted if either \a user or \a message is %NULL.
+ *
+ * Invite a user to a chat.
+ * The user will be prompted to enter the user's name or a message if one is
+ * not given.
+ */
+void purple_chat_conversation_invite_user(PurpleChatConversation *chat,
+ const char *user, const char *message, gboolean confirm);
+
+/**
+ * purple_chat_conversation_has_left:
+ * @chat: The chat.
+ *
+ * Returns true if we're no longer in this chat,
+ * and just left the window open.
+ *
+ * Returns: %TRUE if we left the chat already, %FALSE if
+ * we're still there.
+ */
+gboolean purple_chat_conversation_has_left(PurpleChatConversation *chat);
+
+/**************************************************************************/
+/* Chat Conversation User API */
+/**************************************************************************/
+
+/**
+ * purple_chat_user_get_type:
+ *
+ * Returns: The #GType for the ChatConversationBuddy object.
+ */
+GType purple_chat_user_get_type(void);
+
+/**
+ * purple_chat_user_set_chat:
+ * @cb: The chat user
+ * @chat: The chat conversation that the buddy belongs to.
+ *
+ * Set the chat conversation associated with this chat user.
+ */
+void purple_chat_user_set_chat(PurpleChatUser *cb,
+ PurpleChatConversation *chat);
+
+/**
+ * purple_chat_user_get_chat:
+ * @cb: The chat user.
+ *
+ * Get the chat conversation associated with this chat user.
+ *
+ * Returns: The chat conversation that the buddy belongs to.
+ */
+PurpleChatConversation *purple_chat_user_get_chat(const PurpleChatUser *cb);
+
+/**
+ * purple_chat_user_new:
+ * @chat: The chat that the buddy belongs to.
+ * @name: The name.
+ * @alias: The alias.
+ * @flags: The flags.
+ *
+ * Creates a new chat user
+ *
+ * Returns: The new chat user
+ */
+PurpleChatUser *purple_chat_user_new(PurpleChatConversation *chat,
+ const char *name, const char *alias, PurpleChatUserFlags flags);
+
+/**
+ * purple_chat_user_set_ui_data:
+ * @cb: The chat user
+ * @ui_data: A pointer to associate with this chat user.
+ *
+ * Set the UI data associated with this chat user.
+ */
+void purple_chat_user_set_ui_data(PurpleChatUser *cb, gpointer ui_data);
+
+/**
+ * purple_chat_user_get_ui_data:
+ * @cb: The chat user.
+ *
+ * Get the UI data associated with this chat user.
+ *
+ * Returns: The UI data associated with this chat user. This is a
+ * convenience field provided to the UIs--it is not
+ * used by the libpurple core.
+ */
+gpointer purple_chat_user_get_ui_data(const PurpleChatUser *cb);
+
+/**
+ * purple_chat_user_get_alias:
+ * @cb: The chat user.
+ *
+ * Get the alias of a chat user
+ *
+ * Returns: The alias of the chat user.
+ */
+const char *purple_chat_user_get_alias(const PurpleChatUser *cb);
+
+/**
+ * purple_chat_user_get_name:
+ * @cb: The chat user.
+ *
+ * Get the name of a chat user
+ *
+ * Returns: The name of the chat user.
+ */
+const char *purple_chat_user_get_name(const PurpleChatUser *cb);
+
+/**
+ * purple_chat_user_set_flags:
+ * @cb: The chat user.
+ * @flags: The new flags.
+ *
+ * Set the flags of a chat user.
+ */
+void purple_chat_user_set_flags(PurpleChatUser *cb, PurpleChatUserFlags flags);
+
+/**
+ * purple_chat_user_get_flags:
+ * @cb: The chat user.
+ *
+ * Get the flags of a chat user.
+ *
+ * Returns: The flags of the chat user.
+ */
+PurpleChatUserFlags purple_chat_user_get_flags(const PurpleChatUser *cb);
+
+/**
+ * purple_chat_user_is_buddy:
+ * @cb: The chat user.
+ *
+ * Indicates if this chat user is on the buddy list.
+ *
+ * Returns: TRUE if the chat user is on the buddy list.
+ */
+gboolean purple_chat_user_is_buddy(const PurpleChatUser *cb);
+
+G_END_DECLS
+
+#endif /* _PURPLE_CONVERSATION_TYPES_H_ */
diff --git a/libpurple/core.c b/libpurple/core.c
index 0d63074046..ef1cc3a446 100644
--- a/libpurple/core.c
+++ b/libpurple/core.c
@@ -1,8 +1,3 @@
-/**
- * @file core.c Purple Core API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -32,19 +27,24 @@
#include "core.h"
#include "debug.h"
#include "dnsquery.h"
-#include "ft.h"
+#include "xfer.h"
+#include "glibcompat.h"
+#include "http.h"
#include "idle.h"
-#include "imgstore.h"
+#include "image-store.h"
+#include "keyring.h"
+#include "message.h"
#include "network.h"
#include "notify.h"
#include "plugin.h"
#include "pounce.h"
#include "prefs.h"
-#include "privacy.h"
#include "proxy.h"
#include "savedstatuses.h"
#include "signals.h"
-#include "smiley.h"
+#include "smiley-custom.h"
+#include "smiley-parser.h"
+#include "smiley-theme.h"
#include "sound.h"
#include "sound-theme-loader.h"
#include "sslconn.h"
@@ -75,6 +75,29 @@ static PurpleCore *_core = NULL;
STATIC_PROTO_INIT
+static void
+purple_core_print_version(void)
+{
+ GHashTable *ui_info = purple_core_get_ui_info();
+ const gchar *ui_name;
+ const gchar *ui_version;
+ gchar *ui_full_name = NULL;
+
+ ui_name = ui_info ? g_hash_table_lookup(ui_info, "name") : NULL;
+ 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);
+ }
+
+ purple_debug_info("main", "Launching %s%slibpurple %s",
+ ui_full_name ? ui_full_name : "",
+ ui_full_name ? " with " : "",
+ purple_core_get_version());
+
+}
+
gboolean
purple_core_init(const char *ui)
{
@@ -85,7 +108,7 @@ purple_core_init(const char *ui)
g_return_val_if_fail(purple_get_core() == NULL, FALSE);
#ifdef ENABLE_NLS
- bindtextdomain(PACKAGE, LOCALEDIR);
+ bindtextdomain(PACKAGE, PURPLE_LOCALEDIR);
#endif
#ifdef _WIN32
wpurple_init();
@@ -109,12 +132,17 @@ purple_core_init(const char *ui)
purple_signal_register(core, "uri-handler",
purple_marshal_BOOLEAN__POINTER_POINTER_POINTER,
- purple_value_new(PURPLE_TYPE_BOOLEAN), 3,
- purple_value_new(PURPLE_TYPE_STRING), /* Protocol */
- purple_value_new(PURPLE_TYPE_STRING), /* Command */
- purple_value_new(PURPLE_TYPE_BOXED, "GHashTable *")); /* Parameters */
+ G_TYPE_BOOLEAN, 3,
+ G_TYPE_STRING, /* Protocol */
+ G_TYPE_STRING, /* Command */
+ G_TYPE_POINTER); /* Parameters (GHashTable *) */
+
+ purple_signal_register(core, "quitting", purple_marshal_VOID, G_TYPE_NONE,
+ 0);
+ purple_signal_register(core, "core-initialized", purple_marshal_VOID,
+ G_TYPE_NONE, 0);
- purple_signal_register(core, "quitting", purple_marshal_VOID, NULL, 0);
+ purple_core_print_version();
/* The prefs subsystem needs to be initialized before static protocols
* for protocol prefs to work. */
@@ -135,7 +163,6 @@ purple_core_init(const char *ui)
purple_dbus_init();
#endif
- purple_ciphers_init();
purple_cmds_init();
/* Since plugins get probed so early we should probably initialize their
@@ -148,15 +175,16 @@ purple_core_init(const char *ui)
purple_plugins_probe(G_MODULE_SUFFIX);
+ purple_keyring_init(); /* before accounts */
purple_theme_manager_init();
- /* The buddy icon code uses the imgstore, so init it early. */
- purple_imgstore_init();
+ /* The buddy icon code uses the image store, so init it early. */
+ _purple_image_store_init();
/* Accounts use status, buddy icons and connection signals, so
* initialize these before accounts
*/
- purple_status_init();
+ purple_statuses_init();
purple_buddy_icons_init();
purple_connections_init();
@@ -164,12 +192,13 @@ purple_core_init(const char *ui)
purple_savedstatuses_init();
purple_notify_init();
purple_certificate_init();
+ _purple_message_init();
purple_conversations_init();
purple_blist_init();
purple_log_init();
purple_network_init();
- purple_privacy_init();
purple_pounces_init();
+ _purple_socket_init();
purple_proxy_init();
purple_dnsquery_init();
purple_sound_init();
@@ -177,7 +206,10 @@ purple_core_init(const char *ui)
purple_stun_init();
purple_xfers_init();
purple_idle_init();
- purple_smileys_init();
+ purple_http_init();
+ _purple_smiley_custom_init();
+ _purple_smiley_parser_init();
+
/*
* Call this early on to try to auto-detect our IP address and
* hopefully save some time later.
@@ -190,6 +222,11 @@ purple_core_init(const char *ui)
/* The UI may have registered some theme types, so refresh them */
purple_theme_manager_refresh();
+ /* Load the buddy list after UI init */
+ purple_blist_boot();
+
+ purple_signal_emit(purple_get_core(), "core-initialized");
+
return TRUE;
}
@@ -224,24 +261,28 @@ purple_core_quit(void)
purple_plugins_unload(PURPLE_PLUGIN_STANDARD);
/* Save .xml files, remove signals, etc. */
- purple_smileys_uninit();
+ _purple_smiley_theme_uninit();
+ _purple_smiley_custom_uninit();
+ _purple_smiley_parser_uninit();
+ purple_http_uninit();
purple_idle_uninit();
purple_pounces_uninit();
+ purple_conversations_uninit();
purple_blist_uninit();
- purple_ciphers_uninit();
purple_notify_uninit();
- purple_conversations_uninit();
purple_connections_uninit();
purple_buddy_icons_uninit();
purple_savedstatuses_uninit();
- purple_status_uninit();
+ purple_statuses_uninit();
purple_accounts_uninit();
+ purple_keyring_uninit(); /* after accounts */
purple_sound_uninit();
purple_theme_manager_uninit();
purple_xfers_uninit();
purple_proxy_uninit();
+ _purple_socket_uninit();
purple_dnsquery_uninit();
- purple_imgstore_uninit();
+ _purple_image_store_uninit();
purple_network_uninit();
/* Everything after unloading all plugins must not fail if prpls aren't
@@ -261,9 +302,12 @@ purple_core_quit(void)
#endif
purple_cmds_uninit();
- /* Everything after util_uninit cannot try to write things to the confdir */
- purple_util_uninit();
purple_log_uninit();
+ _purple_message_uninit();
+ /* Everything after util_uninit cannot try to write things to the
+ * confdir nor use purple_escape_js
+ */
+ purple_util_uninit();
purple_signals_uninit();
@@ -307,6 +351,33 @@ purple_get_core(void)
return _core;
}
+static PurpleCoreUiOps *
+purple_core_ui_ops_copy(PurpleCoreUiOps *ops)
+{
+ PurpleCoreUiOps *ops_new;
+
+ g_return_val_if_fail(ops != NULL, NULL);
+
+ ops_new = g_new(PurpleCoreUiOps, 1);
+ *ops_new = *ops;
+
+ return ops_new;
+}
+
+GType
+purple_core_ui_ops_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleCoreUiOps",
+ (GBoxedCopyFunc)purple_core_ui_ops_copy,
+ (GBoxedFreeFunc)g_free);
+ }
+
+ return type;
+}
+
void
purple_core_set_ui_ops(PurpleCoreUiOps *ops)
{
@@ -330,7 +401,7 @@ static char *purple_dbus_owner_user_dir(void)
if ((dbus_connection = purple_dbus_get_connection()) == NULL)
return NULL;
- if ((msg = dbus_message_new_method_call(DBUS_SERVICE_PURPLE, DBUS_PATH_PURPLE, DBUS_INTERFACE_PURPLE, "PurpleUserDir")) == NULL)
+ if ((msg = dbus_message_new_method_call(PURPLE_DBUS_SERVICE, PURPLE_DBUS_PATH, PURPLE_DBUS_INTERFACE, "PurpleUserDir")) == NULL)
return NULL;
dbus_error_init(&dbus_error);
@@ -374,386 +445,6 @@ purple_core_ensure_single_instance()
return is_single_instance;
}
-static gboolean
-move_and_symlink_dir(const char *path, const char *basename, const char *old_base, const char *new_base, const char *relative)
-{
- char *new_name = g_build_filename(new_base, basename, NULL);
-#ifndef _WIN32
- char *old_name;
-#endif
- if (g_rename(path, new_name))
- {
- purple_debug_error("core", "Error renaming %s to %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
- path, new_name, g_strerror(errno));
- g_free(new_name);
- return FALSE;
- }
- g_free(new_name);
-
-#ifndef _WIN32
- /* NOTE: This new_name is relative. */
- new_name = g_build_filename(relative, basename, NULL);
- old_name = g_build_filename(old_base, basename, NULL);
- if (symlink(new_name, old_name))
- {
- purple_debug_warning("core", "Error symlinking %s to %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
- old_name, new_name, g_strerror(errno));
- }
- g_free(old_name);
- g_free(new_name);
-#endif
-
- return TRUE;
-}
-
-gboolean
-purple_core_migrate(void)
-{
- const char *user_dir = purple_user_dir();
- char *old_user_dir = g_strconcat(purple_home_dir(),
- G_DIR_SEPARATOR_S ".gaim", NULL);
- char *status_file;
- FILE *fp;
- GDir *dir;
- GError *err;
- const char *entry;
-#ifndef _WIN32
- char *logs_dir;
-#endif
- char *old_icons_dir;
-
- if (!g_file_test(old_user_dir, G_FILE_TEST_EXISTS))
- {
- /* ~/.gaim doesn't exist, so there's nothing to migrate. */
- g_free(old_user_dir);
- return TRUE;
- }
-
- status_file = g_strconcat(user_dir, G_DIR_SEPARATOR_S "migrating", NULL);
-
- if (g_file_test(user_dir, G_FILE_TEST_EXISTS))
- {
- /* If we're here, we have both ~/.gaim and .purple. */
-
- if (!g_file_test(status_file, G_FILE_TEST_EXISTS))
- {
- /* There's no "migrating" status file,
- * so ~/.purple is all up to date. */
- g_free(status_file);
- g_free(old_user_dir);
- return TRUE;
- }
- }
-
- /* If we're here, it's time to migrate from ~/.gaim to ~/.purple. */
-
- /* Ensure the user directory exists */
- if (!g_file_test(user_dir, G_FILE_TEST_IS_DIR))
- {
- if (g_mkdir(user_dir, S_IRUSR | S_IWUSR | S_IXUSR) == -1)
- {
- purple_debug_error("core", "Error creating directory %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
- user_dir, g_strerror(errno));
- g_free(status_file);
- g_free(old_user_dir);
- return FALSE;
- }
- }
-
- /* This writes ~/.purple/migrating, which allows us to detect
- * incomplete migrations and properly retry. */
- if (!(fp = g_fopen(status_file, "w")))
- {
- purple_debug_error("core", "Error opening file %s for writing: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
- status_file, g_strerror(errno));
- g_free(status_file);
- g_free(old_user_dir);
- return FALSE;
- }
- fclose(fp);
-
- /* Open ~/.gaim so we can loop over its contents. */
- err = NULL;
- if (!(dir = g_dir_open(old_user_dir, 0, &err)))
- {
- purple_debug_error("core", "Error opening directory %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
- status_file,
- (err ? err->message : "Unknown error"));
- if (err)
- g_error_free(err);
- g_free(status_file);
- g_free(old_user_dir);
- return FALSE;
- }
-
- /* Loop over the contents of ~/.gaim */
- while ((entry = g_dir_read_name(dir)))
- {
- char *name = g_build_filename(old_user_dir, entry, NULL);
-
-#ifndef _WIN32
- /* Deal with symlinks... */
- if (g_file_test(name, G_FILE_TEST_IS_SYMLINK))
- {
- /* We're only going to duplicate a logs symlink. */
- if (purple_strequal(entry, "logs"))
- {
- char *link;
- err = NULL;
-
- if ((link = g_file_read_link(name, &err)) == NULL)
- {
- char *name_utf8 = g_filename_to_utf8(name, -1, NULL, NULL, NULL);
- purple_debug_error("core", "Error reading symlink %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
- name_utf8 ? name_utf8 : name, err->message);
- g_free(name_utf8);
- g_error_free(err);
- g_free(name);
- g_dir_close(dir);
- g_free(status_file);
- g_free(old_user_dir);
- return FALSE;
- }
-
- logs_dir = g_build_filename(user_dir, "logs", NULL);
-
- if (purple_strequal(link, "../.purple/logs") ||
- purple_strequal(link, logs_dir))
- {
- /* If the symlink points to the new directory, we're
- * likely just trying again after a failed migration,
- * so there's no need to fail here. */
- g_free(link);
- g_free(logs_dir);
- continue;
- }
-
- /* In case we are trying again after a failed migration, we need
- * to unlink any existing symlink. If it's a directory, this
- * will fail, and so will the symlink below, which is good
- * because the user should sort things out. */
- g_unlink(logs_dir);
-
- /* Relative links will most likely still be
- * valid from ~/.purple, though it's not
- * guaranteed. Oh well. */
- if (symlink(link, logs_dir))
- {
- purple_debug_error("core", "Error symlinking %s to %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
- logs_dir, link, g_strerror(errno));
- g_free(link);
- g_free(name);
- g_free(logs_dir);
- g_dir_close(dir);
- g_free(status_file);
- g_free(old_user_dir);
- return FALSE;
- }
-
- g_free(link);
- g_free(logs_dir);
- continue;
- }
-
- /* Ignore all other symlinks. */
- continue;
- }
-#endif
-
- /* Deal with directories... */
- if (g_file_test(name, G_FILE_TEST_IS_DIR))
- {
- if (purple_strequal(entry, "icons"))
- {
- /* This is a special case for the Album plugin, which
- * stores data in the icons folder. We're not copying
- * the icons directory over because previous bugs
- * meant that it filled up with junk for many users.
- * This is a great time to purge it. */
-
- GDir *icons_dir;
- char *new_icons_dir;
- const char *icons_entry;
-
- err = NULL;
- if (!(icons_dir = g_dir_open(name, 0, &err)))
- {
- purple_debug_error("core", "Error opening directory %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
- name,
- (err ? err->message : "Unknown error"));
- if (err)
- g_error_free(err);
- g_free(name);
- g_dir_close(dir);
- g_free(status_file);
- g_free(old_user_dir);
- return FALSE;
- }
-
- new_icons_dir = g_build_filename(user_dir, "icons", NULL);
- /* Ensure the new icon directory exists */
- if (!g_file_test(new_icons_dir, G_FILE_TEST_IS_DIR))
- {
- if (g_mkdir(new_icons_dir, S_IRUSR | S_IWUSR | S_IXUSR) == -1)
- {
- purple_debug_error("core", "Error creating directory %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
- new_icons_dir, g_strerror(errno));
- g_free(new_icons_dir);
- g_dir_close(icons_dir);
- g_free(name);
- g_dir_close(dir);
- g_free(status_file);
- g_free(old_user_dir);
- return FALSE;
- }
- }
-
- while ((icons_entry = g_dir_read_name(icons_dir)))
- {
- char *icons_name = g_build_filename(name, icons_entry, NULL);
-
- if (g_file_test(icons_name, G_FILE_TEST_IS_DIR))
- {
- if (!move_and_symlink_dir(icons_name, icons_entry,
- name, new_icons_dir, "../../.purple/icons"))
- {
- g_free(icons_name);
- g_free(new_icons_dir);
- g_dir_close(icons_dir);
- g_free(name);
- g_dir_close(dir);
- g_free(status_file);
- g_free(old_user_dir);
- return FALSE;
- }
- }
- g_free(icons_name);
- }
-
- g_dir_close(icons_dir);
- }
- else if (purple_strequal(entry, "plugins"))
- {
- /* Do nothing, because we broke plugin compatibility.
- * This means that the plugins directory gets left behind. */
- }
- else
- {
- /* All other directories are moved and symlinked. */
- if (!move_and_symlink_dir(name, entry, old_user_dir, user_dir, "../.purple"))
- {
- g_free(name);
- g_dir_close(dir);
- g_free(status_file);
- g_free(old_user_dir);
- return FALSE;
- }
- }
- }
- else if (g_file_test(name, G_FILE_TEST_IS_REGULAR))
- {
- /* Regular files are copied. */
-
- char *new_name;
- FILE *new_file;
-
- if (!(fp = g_fopen(name, "rb")))
- {
- purple_debug_error("core", "Error opening file %s for reading: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
- name, g_strerror(errno));
- g_free(name);
- g_dir_close(dir);
- g_free(status_file);
- g_free(old_user_dir);
- return FALSE;
- }
-
- new_name = g_build_filename(user_dir, entry, NULL);
- if (!(new_file = g_fopen(new_name, "wb")))
- {
- purple_debug_error("core", "Error opening file %s for writing: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
- new_name, g_strerror(errno));
- fclose(fp);
- g_free(new_name);
- g_free(name);
- g_dir_close(dir);
- g_free(status_file);
- g_free(old_user_dir);
- return FALSE;
- }
-
- while (!feof(fp))
- {
- unsigned char buf[256];
- size_t size;
-
- size = fread(buf, 1, sizeof(buf), fp);
- if (size != sizeof(buf) && !feof(fp))
- {
- purple_debug_error("core", "Error reading %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
- name, g_strerror(errno));
- fclose(new_file);
- fclose(fp);
- g_free(new_name);
- g_free(name);
- g_dir_close(dir);
- g_free(status_file);
- g_free(old_user_dir);
- return FALSE;
- }
-
- if (!fwrite(buf, size, 1, new_file) && ferror(new_file) != 0)
- {
- purple_debug_error("core", "Error writing %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
- new_name, g_strerror(errno));
- fclose(new_file);
- fclose(fp);
- g_free(new_name);
- g_free(name);
- g_dir_close(dir);
- g_free(status_file);
- g_free(old_user_dir);
- return FALSE;
- }
- }
-
- if (fclose(new_file))
- {
- purple_debug_error("core", "Error writing: %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
- new_name, g_strerror(errno));
- }
- if (fclose(fp))
- {
- purple_debug_warning("core", "Error closing %s: %s\n",
- name, g_strerror(errno));
- }
- g_free(new_name);
- }
- else
- purple_debug_warning("core", "Not a regular file or directory: %s\n", name);
-
- g_free(name);
- }
-
- /* The migration was successful, so delete the status file. */
- if (g_unlink(status_file))
- {
- purple_debug_error("core", "Error unlinking file %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
- status_file, g_strerror(errno));
- g_free(status_file);
- return FALSE;
- }
-
- old_icons_dir = g_build_filename(old_user_dir, "icons", NULL);
- _purple_buddy_icon_set_old_icons_dir(old_icons_dir);
- g_free(old_icons_dir);
-
- g_free(old_user_dir);
-
- g_free(status_file);
- return TRUE;
-}
-
GHashTable* purple_core_get_ui_info() {
PurpleCoreUiOps *ops = purple_core_get_ui_ops();
diff --git a/libpurple/core.h b/libpurple/core.h
index c55591e234..679dec0f0a 100644
--- a/libpurple/core.h
+++ b/libpurple/core.h
@@ -1,9 +1,3 @@
-/**
- * @file core.h Startup and shutdown of libpurple
- * @defgroup core libpurple
- * @see @ref core-signals
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -25,196 +19,214 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-/*! @mainpage Pidgin/Finch/libpurple API Documentation
- *
- * <a href="group__core.html">libpurple</a> is intended to be the core of an IM
- * program. <a href="group__pidgin.html">Pidgin</a> is a GTK+ frontend
- * to libpurple, and <a href="group__finch.html">Finch</a> is an ncurses
- * frontend built using <a href="group__gnt.html">libgnt</a>
- * (GLib Ncurses Toolkit).
- */
-
#ifndef _PURPLE_CORE_H_
#define _PURPLE_CORE_H_
+/**
+ * SECTION:core
+ * @section_id: libpurple-core
+ * @short_description: <filename>core.h</filename>
+ * @title: Startup and Shutdown of libpurple
+ * @see_also: <link linkend="chapter-signals-core">Core signals</link>
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define PURPLE_TYPE_CORE_UI_OPS (purple_core_ui_ops_get_type())
typedef struct PurpleCore PurpleCore;
+typedef struct _PurpleCoreUiOps PurpleCoreUiOps;
-/** Callbacks that fire at different points of the initialization and teardown
- * of libpurple, along with a hook to return descriptive information about the
- * UI.
+/**
+ * PurpleCoreUiOps:
+ * @ui_prefs_init: Called just after the preferences subsystem is initialized;
+ * the UI could use this callback to add some preferences it
+ * needs to be in place when other subsystems are initialized.
+ * @debug_ui_init: Called just after the debug subsystem is initialized, but
+ * before just about every other component's initialization. The
+ * UI should use this hook to call purple_debug_set_ui_ops() so
+ * that debugging information for other components can be logged
+ * during their initialization.
+ * @ui_init: Called after all of libpurple has been initialized. The UI
+ * should use this hook to set all other necessary
+ * <link linkend="chapter-ui-ops"><literal>UiOps structures</literal></link>.
+ * @quit: Called after most of libpurple has been uninitialized.
+ * @get_ui_info: Called by purple_core_get_ui_info(); should return the
+ * information documented there.
+ *
+ * Callbacks that fire at different points of the initialization and teardown
+ * of libpurple, along with a hook to return descriptive information about the
+ * UI.
*/
-typedef struct
+struct _PurpleCoreUiOps
{
- /** Called just after the preferences subsystem is initialized; the UI
- * could use this callback to add some preferences it needs to be in
- * place when other subsystems are initialized.
- */
void (*ui_prefs_init)(void);
- /** Called just after the debug subsystem is initialized, but before
- * just about every other component's initialization. The UI should
- * use this hook to call purple_debug_set_ui_ops() so that debugging
- * information for other components can be logged during their
- * initialization.
- */
void (*debug_ui_init)(void);
- /** Called after all of libpurple has been initialized. The UI should
- * use this hook to set all other necessary UiOps structures.
- *
- * @see @ref ui-ops
- */
void (*ui_init)(void);
- /** Called after most of libpurple has been uninitialized. */
+
void (*quit)(void);
- /** Called by purple_core_get_ui_info(); should return the information
- * documented there.
- */
GHashTable* (*get_ui_info)(void);
+ /*< private >*/
void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
-} PurpleCoreUiOps;
+ void (*_purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
-#ifdef __cplusplus
-extern "C" {
-#endif
+/**
+ * purple_core_ui_ops_get_type:
+ *
+ * Returns: The #GType for the #PurpleCoreUiOps boxed structure.
+ */
+GType purple_core_ui_ops_get_type(void);
/**
+ * purple_core_init:
+ * @ui: The ID of the UI using the core. This should be a
+ * unique ID, registered with the purple team.
+ *
* Initializes the core of purple.
*
* This will setup preferences for all the core subsystems.
*
- * @param ui The ID of the UI using the core. This should be a
- * unique ID, registered with the purple team.
- *
- * @return @c TRUE if successful, or @c FALSE otherwise.
+ * Returns: %TRUE if successful, or %FALSE otherwise.
*/
gboolean purple_core_init(const char *ui);
/**
+ * purple_core_quit:
+ *
* Quits the core of purple, which, depending on the UI, may quit the
* application using the purple core.
*/
void purple_core_quit(void);
/**
- * <p>
+ * purple_core_quit_cb:
+ *
* Calls purple_core_quit(). This can be used as the function
* passed to purple_timeout_add() when you want to shutdown Purple
* in a specified amount of time. When shutting down Purple
* from a plugin, you must use this instead of purple_core_quit();
* for an immediate exit, use a timeout value of 0:
- * </p>
*
- * <code>purple_timeout_add(0, purple_core_quitcb, NULL);</code>
+ * <programlisting>
+ * purple_timeout_add(0, purple_core_quitcb, NULL)
+ * </programlisting>
*
- * <p>
* This is ensures that code from your plugin is not being
* executed when purple_core_quit() is called. If the plugin
* called purple_core_quit() directly, you would get a core dump
* after purple_core_quit() executes and control returns to your
* plugin because purple_core_quit() frees all plugins.
- * </p>
*/
gboolean purple_core_quit_cb(gpointer unused);
/**
+ * purple_core_get_version:
+ *
* Returns the version of the core library.
*
- * @return The version of the core library.
+ * Returns: The version of the core library.
*/
const char *purple_core_get_version(void);
/**
+ * purple_core_get_ui:
+ *
* Returns the ID of the UI that is using the core, as passed to
* purple_core_init().
*
- * @return The ID of the UI that is currently using the core.
+ * Returns: The ID of the UI that is currently using the core.
*/
const char *purple_core_get_ui(void);
/**
- * Returns a handle to the purple core.
+ * purple_get_core:
+ *
+ * This is used to connect to
+ * <link linkend="chapter-signals-core">core signals</link>.
*
- * This is used to connect to @ref core-signals "core signals".
+ * Returns: A handle to the purple core.
*/
PurpleCore *purple_get_core(void);
/**
- * Sets the UI ops for the core.
+ * purple_core_set_ui_ops:
+ * @ops: A UI ops structure for the core.
*
- * @param ops A UI ops structure for the core.
+ * Sets the UI ops for the core.
*/
void purple_core_set_ui_ops(PurpleCoreUiOps *ops);
/**
+ * purple_core_get_ui_ops:
+ *
* Returns the UI ops for the core.
*
- * @return The core's UI ops structure.
+ * Returns: The core's UI ops structure.
*/
PurpleCoreUiOps *purple_core_get_ui_ops(void);
/**
- * Migrates from <tt>.gaim</tt> to <tt>.purple</tt>.
+ * purple_core_ensure_single_instance:
*
- * UIs <strong>must not</strong> call this if they have been told to use a
- * custom user directory.
- *
- * @return A boolean indicating success or migration failure. On failure,
- * the application must display an error to the user and then exit.
- */
-gboolean purple_core_migrate(void);
-
-/**
* Ensures that only one instance is running. If libpurple is built with D-Bus
* support, this checks if another process owns the libpurple bus name and if
* so whether that process is using the same configuration directory as this
* process.
*
- * @return @c TRUE if this is the first instance of libpurple running;
- * @c FALSE if there is another instance running.
- *
- * @since 2.1.0
+ * Returns: %TRUE if this is the first instance of libpurple running;
+ * %FALSE if there is another instance running.
*/
gboolean purple_core_ensure_single_instance(void);
/**
+ * purple_core_get_ui_info:
+ *
* Returns a hash table containing various information about the UI. The
* following well-known entries may be in the table (along with any others the
* UI might choose to include):
*
- * <dl>
- * <dt><tt>name</tt></dt>
- * <dd>the user-readable name for the UI.</dd>
- *
- * <dt><tt>version</tt></dt>
- * <dd>a user-readable description of the current version of the UI.</dd>
- *
- * <dt><tt>website</tt></dt>
- * <dd>the UI's website, such as http://pidgin.im.</dd>
- *
- * <dt><tt>dev_website</tt></dt>
- * <dd>the UI's development/support website, such as http://developer.pidgin.im.</dd>
- *
- * <dt><tt>client_type</tt></dt>
- * <dd>the type of UI. Possible values include 'pc', 'console', 'phone',
+ * <informaltable frame='none'>
+ * <tgroup cols='2'><tbody>
+ * <row>
+ * <entry><literal>name</literal></entry>
+ * <entry>the user-readable name for the UI.</entry>
+ * </row>
+ * <row>
+ * <entry><literal>version</literal></entry>
+ * <entry>a user-readable description of the current version of the UI.</entry>
+ * </row>
+ * <row>
+ * <entry><literal>website</literal></entry>
+ * <entry>the UI's website, such as https://pidgin.im.</entry>
+ * </row>
+ * <row>
+ * <entry><literal>dev_website</literal></entry>
+ * <entry>the UI's development/support website, such as
+ * https://developer.pidgin.im.</entry>
+ * </row>
+ * <row>
+ * <entry><literal>client_type</literal></entry>
+ * <entry>the type of UI. Possible values include 'pc', 'console', 'phone',
* 'handheld', 'web', and 'bot'. These values are compared
- * programmatically and should not be localized.</dd>
+ * programmatically and should not be localized.</entry>
+ * </row>
+ * </tbody></tgroup>
+ * </informaltable>
*
- * </dl>
- *
- * @return A GHashTable with strings for keys and values. This
+ * Returns: A GHashTable with strings for keys and values. This
* hash table must not be freed and should not be modified.
*
- * @since 2.1.0
- *
*/
GHashTable* purple_core_get_ui_info(void);
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_CORE_H_ */
diff --git a/libpurple/dbus-analyze-functions.py b/libpurple/dbus-analyze-functions.py
index 4e26656644..421b6b2859 100644
--- a/libpurple/dbus-analyze-functions.py
+++ b/libpurple/dbus-analyze-functions.py
@@ -22,15 +22,19 @@ excluded = [\
# functions with untranslatable types are skipped, but this script
# assumes that all non-pointer type names beginning with "Purple"
# are enums, which is not true in this case.
- "purple_conv_placement_add_fnc",
- "purple_conv_placement_get_fnc",
- "purple_conv_placement_get_current_func",
- "purple_conv_placement_set_current_func",
+ "purple_conversation_placement_add_fnc",
+ "purple_conversation_placement_get_fnc",
+ "purple_conversation_placement_get_current_func",
+ "purple_conversation_placement_set_current_func",
# Similar to the above:
"purple_account_set_register_callback",
"purple_account_unregister",
- "purple_connection_new_unregister",
+
+ # Similar to the above, again
+ "purple_menu_action_new",
+ "purple_menu_action_set_callback",
+ "purple_menu_action_get_callback",
# These functions are excluded because they involve setting arbitrary
# data via pointers for protocols and UIs. This just won't work.
@@ -45,6 +49,9 @@ excluded = [\
# as pointer to a struct, instead of a pointer to an enum. This
# causes a compilation error. Someone should fix this script.
"purple_log_read",
+
+ # Similiar to the above:
+ "purple_notify_is_valid_ui_handle",
]
# This is a list of functions that return a GList* or GSList * whose elements
@@ -66,11 +73,11 @@ constlists = [
"purple_account_option_get_list",
"purple_connections_get_all",
"purple_connections_get_connecting",
- "purple_get_conversations",
- "purple_get_ims",
- "purple_get_chats",
- "purple_conv_chat_get_users",
- "purple_conv_chat_get_ignored",
+ "purple_conversations_get_all",
+ "purple_conversations_get_ims",
+ "purple_conversations_get_chats",
+ "purple_chat_conversation_get_users",
+ "purple_chat_conversation_get_ignored",
"purple_mime_document_get_fields",
"purple_mime_document_get_parts",
"purple_mime_part_get_fields",
@@ -167,7 +174,7 @@ class Binding:
return self.inputhash(type, name)
# known object types are transformed to integer handles
- elif type[0].startswith("Purple") or type[0] == "xmlnode":
+ elif type[0].startswith("Purple"):
return self.inputpurplestructure(type, name)
# special case for *_get_data functions, be careful here...
@@ -492,7 +499,7 @@ class ServerBinding (Binding):
if self.function.name in stringlists:
self.cdecls.append("\tchar **%s;" % name)
self.ccode.append("\tlist = %s;" % self.call)
- self.ccode.append("\t%s = (char **)purple_%s_to_array(list, FALSE, &%s_LEN);" % \
+ self.ccode.append("\t%s = (char **)purple_%s_to_array(list, &%s_LEN);" % \
(name, type[0], name))
self.cparamsout.append("DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &%s, %s_LEN" \
% (name, name))
@@ -504,7 +511,7 @@ class ServerBinding (Binding):
else:
self.cdecls.append("\tdbus_int32_t *%s;" % name)
self.ccode.append("\tlist = %s;" % self.call)
- self.ccode.append("\t%s = purple_dbusify_%s(list, FALSE, &%s_LEN);" % \
+ self.ccode.append("\t%s = purple_dbusify_%s(list, &%s_LEN);" % \
(name, type[0], name))
if (not (self.function.name in constlists)):
self.ccode.append("\tg_%s_free(list);" % type[0].lower()[1:])
diff --git a/libpurple/dbus-bindings.h b/libpurple/dbus-bindings.h
index 5611ae58a7..3fa179eb2c 100644
--- a/libpurple/dbus-bindings.h
+++ b/libpurple/dbus-bindings.h
@@ -1,8 +1,3 @@
-/**
- * @file dbus-bindings.h Purple DBUS Bindings
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -27,14 +22,18 @@
#ifndef _PURPLE_DBUS_BINDINGS_H_
#define _PURPLE_DBUS_BINDINGS_H_
+/**
+ * SECTION:dbus-bindings
+ * @section_id: libpurple-dbus-bindings
+ * @short_description: <filename>dbus-bindings.h</filename>
+ * @title: DBUS Bindings
+ */
#include <dbus/dbus.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <glib.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
gint purple_dbus_pointer_to_id(gconstpointer node);
gpointer purple_dbus_id_to_pointer(gint id, PurpleDBusType *type);
@@ -42,7 +41,7 @@ gint purple_dbus_pointer_to_id_error(gconstpointer ptr, DBusError *error);
gpointer purple_dbus_id_to_pointer_error(gint id, PurpleDBusType *type,
const char *typename, DBusError *error);
-#define NULLIFY(id) id = empty_to_null(id)
+#define NULLIFY(id) id = purple_emptystr_to_null(id)
#define CHECK_ERROR(error) if (dbus_error_is_set(error)) return NULL;
@@ -84,18 +83,14 @@ purple_dbus_message_iter_get_args_valist (DBusMessageIter *iter,
int first_arg_type,
va_list var_args);
-dbus_int32_t* purple_dbusify_GList(GList *list, gboolean free_memory,
- dbus_int32_t *len);
-dbus_int32_t* purple_dbusify_GSList(GSList *list, gboolean free_memory,
- dbus_int32_t *len);
-gpointer* purple_GList_to_array(GList *list, gboolean free_memory,
- dbus_int32_t *len);
-gpointer* purple_GSList_to_array(GSList *list, gboolean free_memory,
- dbus_int32_t *len);
+dbus_int32_t* purple_dbusify_GList(GList *list, dbus_int32_t *len);
+dbus_int32_t* purple_dbusify_GSList(GSList *list, dbus_int32_t *len);
+gpointer* purple_GList_to_array(GList *list, dbus_int32_t *len);
+gpointer* purple_GSList_to_array(GSList *list, dbus_int32_t *len);
GHashTable *purple_dbus_iter_hash_table(DBusMessageIter *iter, DBusError *error);
-const char* empty_to_null(const char *str);
-const char* null_to_empty(const char *s);
+const char* purple_emptystr_to_null(const char *str);
+const char* purple_null_to_emptystr(const char *s);
typedef struct {
const char *name;
@@ -107,8 +102,6 @@ void purple_dbus_register_bindings(void *handle, PurpleDBusBinding *bindings);
DBusConnection *purple_dbus_get_connection(void);
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif
diff --git a/libpurple/dbus-define-api.h b/libpurple/dbus-define-api.h
index 7916ec744f..55811e5760 100644
--- a/libpurple/dbus-define-api.h
+++ b/libpurple/dbus-define-api.h
@@ -1,25 +1,12 @@
-#error "This is file is not a valid C code"
+#error "This file is not a valid C code and is not intended to be compiled."
/* This file contains some of the macros from other header files as
function declarations. This does not make sense in C, but it
provides type information for the dbus-analyze-functions.py
program, which makes these macros callable by DBUS. */
-/* blist.h */
-gboolean PURPLE_BLIST_NODE_IS_CHAT(PurpleBlistNode *node);
-gboolean PURPLE_BLIST_NODE_IS_BUDDY(PurpleBlistNode *node);
-gboolean PURPLE_BLIST_NODE_IS_CONTACT(PurpleBlistNode *node);
-gboolean PURPLE_BLIST_NODE_IS_GROUP(PurpleBlistNode *node);
+/* buddylist.h */
gboolean PURPLE_BUDDY_IS_ONLINE(PurpleBuddy *buddy);
-gboolean PURPLE_BLIST_NODE_HAS_FLAG(PurpleBlistNode *node, int flags);
-gboolean PURPLE_BLIST_NODE_SHOULD_SAVE(PurpleBlistNode *node);
/* connection.h */
gboolean PURPLE_CONNECTION_IS_CONNECTED(PurpleConnection *connection);
-gboolean PURPLE_CONNECTION_IS_VALID(PurpleConnection *connection);
-
-/* conversation.h */
-PurpleConvIm *PURPLE_CONV_IM(const PurpleConversation *conversation);
-PurpleConvIm *PURPLE_CONV_CHAT(const PurpleConversation *conversation);
-
-
diff --git a/libpurple/dbus-maybe.h b/libpurple/dbus-maybe.h
index 6f4c8cbc3f..7156358c62 100644
--- a/libpurple/dbus-maybe.h
+++ b/libpurple/dbus-maybe.h
@@ -1,10 +1,16 @@
-/* This file contains macros that wrap calls to the purple dbus module.
- These macros call the appropriate functions if the build includes
- dbus support and do nothing otherwise. See "dbus-server.h" for
- documentation. */
-
#ifndef _PURPLE_DBUS_MAYBE_H_
#define _PURPLE_DBUS_MAYBE_H_
+/**
+ * SECTION:dbus-maybe
+ * @section_id: libpurple-dbus-maybe
+ * @short_description: <filename>dbus-maybe.h</filename>
+ * @title: DBUS Wrappers
+ *
+ * This file contains macros that wrap calls to the purple dbus module.
+ * These macros call the appropriate functions if the build includes
+ * dbus support and do nothing otherwise. See "dbus-server.h" for
+ * documentation.
+ */
#ifdef HAVE_DBUS
@@ -28,7 +34,6 @@
}
#define PURPLE_DBUS_UNREGISTER_POINTER(ptr)
-#define DBUS_EXPORT
#endif /* HAVE_DBUS */
diff --git a/libpurple/dbus-purple.h b/libpurple/dbus-purple.h
index e078956782..436ba638ca 100644
--- a/libpurple/dbus-purple.h
+++ b/libpurple/dbus-purple.h
@@ -23,9 +23,15 @@
#ifndef _DBUS_PURPLE_H_
#define _DBUS_PURPLE_H_
+/**
+ * SECTION:dbus-purple
+ * @section_id: libpurple-dbus-purple
+ * @short_description: <filename>dbus-purple.h</filename>
+ * @title: DBUS Purple defines
+ */
-#define DBUS_SERVICE_PURPLE "im.pidgin.purple.PurpleService"
-#define DBUS_PATH_PURPLE "/im/pidgin/purple/PurpleObject"
-#define DBUS_INTERFACE_PURPLE "im.pidgin.purple.PurpleInterface"
+#define PURPLE_DBUS_SERVICE "im.pidgin.purple.PurpleService"
+#define PURPLE_DBUS_PATH "/im/pidgin/purple/PurpleObject"
+#define PURPLE_DBUS_INTERFACE "im.pidgin.purple.PurpleInterface"
#endif /* _DBUS_PURPLE_H_ */
diff --git a/libpurple/dbus-server.c b/libpurple/dbus-server.c
index 86b8f5e122..66b717e912 100644
--- a/libpurple/dbus-server.c
+++ b/libpurple/dbus-server.c
@@ -36,7 +36,7 @@
#include <string.h>
#include "account.h"
-#include "blist.h"
+#include "buddylist.h"
#include "conversation.h"
#include "dbus-purple.h"
#include "dbus-server.h"
@@ -46,13 +46,13 @@
#include "core.h"
#include "savedstatuses.h"
#include "smiley.h"
+#include "smiley-list.h"
#include "util.h"
-#include "value.h"
#include "xmlnode.h"
/**************************************************************************/
-/** @name Purple DBUS pointer registration mechanism */
+/* Purple DBUS pointer registration mechanism */
/**************************************************************************/
/*
@@ -80,7 +80,7 @@ gboolean purple_dbus_is_owner(void)
return(DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER == dbus_request_name_reply);
}
-/**
+/*
* This function initializes the pointer-id traslation system. It
* creates the three above hashtables and defines parents of some types.
*/
@@ -180,7 +180,7 @@ purple_dbus_id_to_pointer_error(gint id, PurpleDBusType *type,
/**************************************************************************/
-/** @name Modified versions of some DBus functions */
+/* Modified versions of some DBus functions */
/**************************************************************************/
dbus_bool_t
@@ -279,10 +279,10 @@ purple_dbus_message_iter_get_args_valist(DBusMessageIter *iter,
/**************************************************************************/
-/** @name Useful functions */
+/* Useful functions */
/**************************************************************************/
-const char *empty_to_null(const char *str)
+const char *purple_emptystr_to_null(const char *str)
{
if (str == NULL || str[0] == 0)
return NULL;
@@ -291,7 +291,7 @@ const char *empty_to_null(const char *str)
}
const char *
-null_to_empty(const char *s)
+purple_null_to_emptystr(const char *s)
{
if (s)
return s;
@@ -300,7 +300,7 @@ null_to_empty(const char *s)
}
dbus_int32_t *
-purple_dbusify_GList(GList *list, gboolean free_memory, dbus_int32_t *len)
+purple_dbusify_GList(GList *list, dbus_int32_t *len)
{
dbus_int32_t *array;
int i;
@@ -311,14 +311,11 @@ purple_dbusify_GList(GList *list, gboolean free_memory, dbus_int32_t *len)
for (i = 0, elem = list; elem != NULL; elem = elem->next, i++)
array[i] = purple_dbus_pointer_to_id(elem->data);
- if (free_memory)
- g_list_free(list);
-
return array;
}
dbus_int32_t *
-purple_dbusify_GSList(GSList *list, gboolean free_memory, dbus_int32_t *len)
+purple_dbusify_GSList(GSList *list, dbus_int32_t *len)
{
dbus_int32_t *array;
int i;
@@ -329,14 +326,11 @@ purple_dbusify_GSList(GSList *list, gboolean free_memory, dbus_int32_t *len)
for (i = 0, elem = list; elem != NULL; elem = elem->next, i++)
array[i] = purple_dbus_pointer_to_id(elem->data);
- if (free_memory)
- g_slist_free(list);
-
return array;
}
gpointer *
-purple_GList_to_array(GList *list, gboolean free_memory, dbus_int32_t *len)
+purple_GList_to_array(GList *list, dbus_int32_t *len)
{
gpointer *array;
int i;
@@ -347,14 +341,11 @@ purple_GList_to_array(GList *list, gboolean free_memory, dbus_int32_t *len)
for (i = 0, elem = list; elem != NULL; elem = elem->next, i++)
array[i] = elem->data;
- if (free_memory)
- g_list_free(list);
-
return array;
}
gpointer *
-purple_GSList_to_array(GSList *list, gboolean free_memory, dbus_int32_t *len)
+purple_GSList_to_array(GSList *list, dbus_int32_t *len)
{
gpointer *array;
int i;
@@ -365,9 +356,6 @@ purple_GSList_to_array(GSList *list, gboolean free_memory, dbus_int32_t *len)
for (i = 0, elem = list; elem != NULL; elem = elem->next, i++)
array[i] = elem->data;
- if (free_memory)
- g_slist_free(list);
-
return array;
}
@@ -436,7 +424,7 @@ purple_dbus_dispatch_cb(DBusConnection *connection,
bindings = (PurpleDBusBinding*) user_data;
- if (!dbus_message_has_path(message, DBUS_PATH_PURPLE))
+ if (!dbus_message_has_path(message, PURPLE_DBUS_PATH))
return FALSE;
name = dbus_message_get_member(message);
@@ -500,10 +488,10 @@ static DBusMessage *purple_dbus_introspect(DBusMessage *message)
str = g_string_sized_new(0x1000); /* TODO: why this size? */
g_string_append(str, "<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN' 'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>\n");
- g_string_append_printf(str, "<node name='%s'>\n", DBUS_PATH_PURPLE);
+ g_string_append_printf(str, "<node name='%s'>\n", PURPLE_DBUS_PATH);
g_string_append(str, " <interface name='org.freedesktop.DBus.Introspectable'>\n <method name='Introspect'>\n <arg name='data' direction='out' type='s'/>\n </method>\n </interface>\n\n");
- g_string_append_printf(str, " <interface name='%s'>\n", DBUS_INTERFACE_PURPLE);
+ g_string_append_printf(str, " <interface name='%s'>\n", PURPLE_DBUS_INTERFACE);
bindings_list = NULL;
purple_signal_emit(purple_dbus_get_handle(), "dbus-introspect", &bindings_list);
@@ -571,7 +559,7 @@ purple_dbus_dispatch(DBusConnection *connection,
return DBUS_HANDLER_RESULT_HANDLED;
if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect") &&
- dbus_message_has_path(message, DBUS_PATH_PURPLE))
+ dbus_message_has_path(message, PURPLE_DBUS_PATH))
{
DBusMessage *reply;
reply = purple_dbus_introspect(message);
@@ -617,7 +605,7 @@ purple_dbus_dispatch_init(void)
dbus_connection_set_exit_on_disconnect (purple_dbus_connection, FALSE);
if (!dbus_connection_register_object_path(purple_dbus_connection,
- DBUS_PATH_PURPLE, &vtable, NULL))
+ PURPLE_DBUS_PATH, &vtable, NULL))
{
init_error = g_strdup_printf(N_("Failed to get name: %s"), error.name);
dbus_error_free(&error);
@@ -625,7 +613,7 @@ purple_dbus_dispatch_init(void)
}
dbus_request_name_reply = dbus_bus_request_name(purple_dbus_connection,
- DBUS_SERVICE_PURPLE, 0, &error);
+ PURPLE_DBUS_SERVICE, 0, &error);
if (dbus_error_is_set(&error))
{
@@ -638,25 +626,24 @@ purple_dbus_dispatch_init(void)
dbus_connection_setup_with_g_main(purple_dbus_connection, NULL);
- purple_debug_misc("dbus", "okkk\n");
-
purple_signal_register(purple_dbus_get_handle(), "dbus-method-called",
purple_marshal_BOOLEAN__POINTER_POINTER,
- purple_value_new(PURPLE_TYPE_BOOLEAN), 2,
- purple_value_new(PURPLE_TYPE_POINTER),
- purple_value_new(PURPLE_TYPE_POINTER));
+ G_TYPE_BOOLEAN, 2, G_TYPE_POINTER, G_TYPE_POINTER);
purple_signal_register(purple_dbus_get_handle(), "dbus-introspect",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new_outgoing(PURPLE_TYPE_POINTER));
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ G_TYPE_POINTER); /* pointer to a pointer */
PURPLE_DBUS_REGISTER_BINDINGS(purple_dbus_get_handle());
+
+ if (purple_debug_is_verbose())
+ purple_debug_misc("dbus", "initialized");
}
/**************************************************************************/
-/** @name Signals */
+/* Signals */
/**************************************************************************/
@@ -687,8 +674,8 @@ purple_dbus_convert_signal_name(const char *purple_name)
#define my_arg(type) (ptr != NULL ? * ((type *)ptr) : va_arg(data, type))
static gboolean
-purple_dbus_message_append_purple_values(DBusMessageIter *iter,
- int number, PurpleValue **purple_values, va_list data)
+purple_dbus_message_append_values(DBusMessageIter *iter,
+ int number, GType *types, va_list data)
{
int i;
gboolean error = FALSE;
@@ -704,38 +691,37 @@ purple_dbus_message_append_purple_values(DBusMessageIter *iter,
gboolean xboolean;
gpointer ptr = NULL;
gpointer val;
-
+#if 0
if (purple_value_is_outgoing(purple_values[i]))
{
ptr = my_arg(gpointer);
g_return_val_if_fail(ptr, TRUE);
}
-
- switch (purple_values[i]->type)
+#endif
+ switch (types[i])
{
- case PURPLE_TYPE_INT:
- case PURPLE_TYPE_ENUM:
+ case G_TYPE_INT:
xint = my_arg(gint);
dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &xint);
break;
- case PURPLE_TYPE_UINT:
+ case G_TYPE_UINT:
xuint = my_arg(guint);
dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &xuint);
break;
- case PURPLE_TYPE_INT64:
+ case G_TYPE_INT64:
xint64 = my_arg(gint64);
dbus_message_iter_append_basic(iter, DBUS_TYPE_INT64, &xint64);
break;
- case PURPLE_TYPE_UINT64:
+ case G_TYPE_UINT64:
xuint64 = my_arg(guint64);
dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &xuint64);
break;
- case PURPLE_TYPE_BOOLEAN:
+ case G_TYPE_BOOLEAN:
xboolean = my_arg(gboolean);
dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &xboolean);
break;
- case PURPLE_TYPE_STRING:
- str = null_to_empty(my_arg(char*));
+ case G_TYPE_STRING:
+ str = purple_null_to_emptystr(my_arg(char*));
if (!g_utf8_validate(str, -1, NULL)) {
gchar *tmp;
purple_debug_error("dbus", "Invalid UTF-8 string passed to signal, emitting salvaged string!\n");
@@ -746,19 +732,27 @@ purple_dbus_message_append_purple_values(DBusMessageIter *iter,
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
}
break;
- case PURPLE_TYPE_SUBTYPE: /* registered pointers only! */
- case PURPLE_TYPE_POINTER:
- case PURPLE_TYPE_OBJECT:
- case PURPLE_TYPE_BOXED:
- val = my_arg(gpointer);
- id = purple_dbus_pointer_to_id(val);
- if (id == 0 && val != NULL)
- error = TRUE; /* Some error happened. */
- dbus_message_iter_append_basic(iter,
- (sizeof(id) == sizeof(dbus_int32_t)) ? DBUS_TYPE_INT32 : DBUS_TYPE_INT64, &id);
- break;
- default: /* no conversion implemented */
- g_return_val_if_reached(TRUE);
+ default:
+ if (G_TYPE_IS_OBJECT(types[i]) ||
+ G_TYPE_IS_BOXED(types[i]) ||
+ types[i] == G_TYPE_POINTER )
+ {
+ val = my_arg(gpointer);
+ id = purple_dbus_pointer_to_id(val);
+ if (id == 0 && val != NULL)
+ error = TRUE; /* Some error happened. */
+ dbus_message_iter_append_basic(iter,
+ (sizeof(id) == sizeof(dbus_int32_t)) ? DBUS_TYPE_INT32 : DBUS_TYPE_INT64, &id);
+ }
+ else if (G_TYPE_IS_ENUM(types[i]))
+ {
+ xint = my_arg(gint);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &xint);
+ }
+ else /* no conversion implemented */
+ {
+ g_return_val_if_reached(TRUE);
+ }
}
}
return error;
@@ -768,7 +762,7 @@ purple_dbus_message_append_purple_values(DBusMessageIter *iter,
void
purple_dbus_signal_emit_purple(const char *name, int num_values,
- PurpleValue **values, va_list vargs)
+ GType *types, va_list vargs)
{
DBusMessage *signal;
DBusMessageIter iter;
@@ -792,10 +786,10 @@ purple_dbus_signal_emit_purple(const char *name, int num_values,
return;
newname = purple_dbus_convert_signal_name(name);
- signal = dbus_message_new_signal(DBUS_PATH_PURPLE, DBUS_INTERFACE_PURPLE, newname);
+ signal = dbus_message_new_signal(PURPLE_DBUS_PATH, PURPLE_DBUS_INTERFACE, newname);
dbus_message_iter_init_append(signal, &iter);
- if (purple_dbus_message_append_purple_values(&iter, num_values, values, vargs))
+ if (purple_dbus_message_append_values(&iter, num_values, types, vargs))
if (purple_debug_is_verbose())
purple_debug_warning("dbus",
"The signal \"%s\" caused some dbus error."
@@ -845,8 +839,8 @@ purple_dbus_uninit(void)
return;
dbus_error_init(&error);
- dbus_connection_unregister_object_path(purple_dbus_connection, DBUS_PATH_PURPLE);
- dbus_bus_release_name(purple_dbus_connection, DBUS_SERVICE_PURPLE, &error);
+ dbus_connection_unregister_object_path(purple_dbus_connection, PURPLE_DBUS_PATH);
+ dbus_bus_release_name(purple_dbus_connection, PURPLE_DBUS_SERVICE, &error);
dbus_error_free(&error);
dbus_connection_unref(purple_dbus_connection);
purple_dbus_connection = NULL;
diff --git a/libpurple/dbus-server.h b/libpurple/dbus-server.h
index 5215dccbae..d6d4daac99 100644
--- a/libpurple/dbus-server.h
+++ b/libpurple/dbus-server.h
@@ -1,9 +1,3 @@
-/**
- * @file dbus-server.h Purple DBUS Server
- * @ingroup core
- * @see @ref dbus-server-signals
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -27,26 +21,33 @@
#ifndef _PURPLE_DBUS_SERVER_H_
#define _PURPLE_DBUS_SERVER_H_
+/**
+ * SECTION:dbus-server
+ * @section_id: libpurple-dbus-server
+ * @short_description: <filename>dbus-server.h</filename>
+ * @title: DBUS Server
+ * @see_also: <link linkend="chapter-signals-dbus-server">D-Bus Server signals</link>
+ */
#include "dbus-purple.h"
-#include "value.h"
G_BEGIN_DECLS
-/**
- Types of pointers are identified by the ADDRESS of a PurpleDbusType
- object. This way, plugins can easily access types defined in purple
- proper as well as introduce their own types that will not conflict
- with those introduced by other plugins.
-
- The structure PurpleDbusType has only one element (PurpleDBusType::parent), a
- contains a pointer to the parent type, or @c NULL if the type has no
- parent. Parent means the same as the base class in object oriented
- programming.
-*/
-
typedef struct _PurpleDBusType PurpleDBusType;
+/**
+ * PurpleDBusType:
+ *
+ * Types of pointers are identified by the ADDRESS of a PurpleDbusType
+ * object. This way, plugins can easily access types defined in purple
+ * proper as well as introduce their own types that will not conflict
+ * with those introduced by other plugins.
+ *
+ * The structure PurpleDbusType has only one element (PurpleDBusType::parent), a
+ * contains a pointer to the parent type, or %NULL if the type has no
+ * parent. Parent means the same as the base class in object oriented
+ * programming.
+ */
struct _PurpleDBusType {
PurpleDBusType *parent;
};
@@ -84,122 +85,133 @@ struct _PurpleDBusType {
title = g_strdup_printf("Unable to Load %s Plugin", plugin->info->name); \
purple_notify_error(NULL, title, \
_("Purple's D-BUS server is not running for the reason listed below"), \
- _(purple_dbus_get_init_error())); \
+ _(purple_dbus_get_init_error()), NULL); \
g_free(title); \
return FALSE; \
}
/**
- Initializes purple dbus pointer registration engine.
-
- Remote dbus applications need a way of addressing objects exposed
- by purple to the outside world. In purple itself, these objects (such
- as PurpleBuddy and company) are identified by pointers. The purple
- dbus pointer registration engine converts pointers to handles and
- back.
-
- In order for an object to participate in the scheme, it must
- register itself and its type with the engine. This registration
- allocates an integer id which can be resolved to the pointer and
- back.
-
- Handles are not persistent. They are reissued every time purple is
- started. This is not good; external applications that use purple
- should work even whether purple was restarted in the middle of the
- interaction.
-
- Pointer registration is only a temporary solution. When PurpleBuddy
- and similar structures have been converted into gobjects, this
- registration will be done automatically by objects themselves.
-
- By the way, this kind of object-handle translation should be so
- common that there must be a library (maybe even glib) that
- implements it. I feel a bit like reinventing the wheel here.
-*/
+ * purple_dbus_init_ids:
+ *
+ * Initializes purple dbus pointer registration engine.
+ *
+ * Remote dbus applications need a way of addressing objects exposed
+ * by purple to the outside world. In purple itself, these objects (such
+ * as PurpleBuddy and company) are identified by pointers. The purple
+ * dbus pointer registration engine converts pointers to handles and
+ * back.
+ *
+ * In order for an object to participate in the scheme, it must
+ * register itself and its type with the engine. This registration
+ * allocates an integer id which can be resolved to the pointer and
+ * back.
+ *
+ * Handles are not persistent. They are reissued every time purple is
+ * started. This is not good; external applications that use purple
+ * should work even whether purple was restarted in the middle of the
+ * interaction.
+ *
+ * Pointer registration is only a temporary solution. When PurpleBuddy
+ * and similar structures have been converted into gobjects, this
+ * registration will be done automatically by objects themselves.
+ *
+ * By the way, this kind of object-handle translation should be so
+ * common that there must be a library (maybe even glib) that
+ * implements it. I feel a bit like reinventing the wheel here.
+ */
void purple_dbus_init_ids(void);
/**
- Registers a typed pointer.
-
- @param node The pointer to register.
- @param type Type of that pointer.
+ * purple_dbus_register_pointer:
+ * @node: The pointer to register.
+ * @type: Type of that pointer.
+ *
+ * Registers a typed pointer.
*/
void purple_dbus_register_pointer(gpointer node, PurpleDBusType *type);
/**
- Unregisters a pointer previously registered with
- purple_dbus_register_pointer.
-
- @param node The pointer to register.
+ * purple_dbus_unregister_pointer:
+ * @node: The pointer to register.
+ *
+ * Unregisters a pointer previously registered with
+ * purple_dbus_register_pointer.
*/
void purple_dbus_unregister_pointer(gpointer node);
-
-
/**
- Emits a dbus signal.
-
- @param name The name of the signal ("bla-bla-blaa")
- @param num_values The number of parameters.
- @param values Array of pointers to #PurpleValue objects representing
- the types of the parameters.
- @param vargs A va_list containing the actual parameters.
- */
+ * purple_dbus_signal_emit_purple:
+ * @name: The name of the signal ("bla-bla-blaa")
+ * @num_values: The number of parameters.
+ * @types: Array of GTypes representing the types of the parameters.
+ * @vargs: A va_list containing the actual parameters.
+ *
+ * Emits a dbus signal.
+ */
void purple_dbus_signal_emit_purple(const char *name, int num_values,
- PurpleValue **values, va_list vargs);
+ GType *types, va_list vargs);
/**
+ * purple_dbus_get_init_error:
+ *
* Returns whether Purple's D-BUS subsystem is up and running. If it's
- * NOT running then purple_dbus_dispatch_init() failed for some reason,
+ * NOT running then #purple_dbus_dispatch_init failed for some reason,
* and a message should have been purple_debug_error()'ed.
*
* Purple plugins that use D-BUS should use the
* PURPLE_DBUS_RETURN_FALSE_IF_DISABLED macro to short-circuit
* initialization if Purple's D-BUS subsystem is not running.
*
- * @return If the D-BUS subsystem started with no problems then this
- * will return NULL and everything will be hunky dory. If
- * there was an error initializing the D-BUS subsystem then
- * this will return an error message explaining why.
+ * Returns: If the D-BUS subsystem started with no problems then this
+ * will return NULL and everything will be hunky dory. If
+ * there was an error initializing the D-BUS subsystem then
+ * this will return an error message explaining why.
*/
const char *purple_dbus_get_init_error(void);
/**
+ * purple_dbus_get_handle:
+ *
* Returns the dbus subsystem handle.
*
- * @return The dbus subsystem handle.
+ * Returns: The dbus subsystem handle.
*/
void *purple_dbus_get_handle(void);
/**
- * Determines whether this instance owns the DBus service name
+ * purple_dbus_is_owner:
*
- * @since 2.1.0
+ * Determines whether this instance owns the DBus service name
*/
gboolean purple_dbus_is_owner(void);
/**
+ * purple_dbus_init:
+ *
* Starts Purple's D-BUS server. It is responsible for handling DBUS
* requests from other applications.
*/
void purple_dbus_init(void);
/**
+ * purple_dbus_uninit:
+ *
* Uninitializes Purple's D-BUS server.
*/
void purple_dbus_uninit(void);
/**
-
- Macro #DBUS_EXPORT expands to nothing. It is used to indicate to the
- dbus-analyze-functions.py script that the given function should be
- available to other applications through DBUS. If
- dbus-analyze-functions.py is run without the "--export-only" option,
- this prefix is ignored.
-
+ * DBUS_EXPORT:
+ *
+ * Macro #DBUS_EXPORT expands to nothing. It is used to indicate to the
+ * dbus-analyze-functions.py script that the given function should be
+ * available to other applications through DBUS. If
+ * dbus-analyze-functions.py is run without the "--export-only" option,
+ * this prefix is ignored.
*/
-
+#ifndef DBUS_EXPORT
#define DBUS_EXPORT
+#endif
/*
Here we include the list of #PURPLE_DBUS_DECLARE_TYPE statements for
diff --git a/libpurple/dbus-useful.c b/libpurple/dbus-useful.c
index b288887b1f..99d456d4f3 100644
--- a/libpurple/dbus-useful.c
+++ b/libpurple/dbus-useful.c
@@ -2,6 +2,7 @@
#include <glib.h>
#include "dbus-useful.h"
+#include "accounts.h"
#include "conversation.h"
#include "util.h"
@@ -25,7 +26,7 @@ purple_accounts_find_ext(const char *name, const char *protocol_id,
if (who && strcmp(purple_normalize(NULL, purple_account_get_username(account)), who))
continue;
- if (protocol_id && strcmp(account->protocol_id, protocol_id))
+ if (protocol_id && strcmp(purple_account_get_protocol_id(account), protocol_id))
continue;
if (account_test && !account_test(account))
diff --git a/libpurple/dbus-useful.h b/libpurple/dbus-useful.h
index 663c4c7a10..b452fc676a 100644
--- a/libpurple/dbus-useful.h
+++ b/libpurple/dbus-useful.h
@@ -1,5 +1,16 @@
+#ifndef _PURPLE_DBUS_USEFUL_H_
+#define _PURPLE_DBUS_USEFUL_H_
+/**
+ * SECTION:dbus-useful
+ * @section_id: libpurple-dbus-useful
+ * @short_description: <filename>dbus-useful.h</filename>
+ * @title: Misc functions for DBUS server
+ */
+
#include "conversation.h"
+G_BEGIN_DECLS
+
PurpleAccount *purple_accounts_find_ext(const char *name, const char *protocol_id,
gboolean (*account_test)(const PurpleAccount *account));
@@ -7,7 +18,6 @@ PurpleAccount *purple_accounts_find_any(const char *name, const char *protocol);
PurpleAccount *purple_accounts_find_connected(const char *name, const char *protocol);
+G_END_DECLS
-
-
-
+#endif
diff --git a/libpurple/debug.c b/libpurple/debug.c
index ca17f62bde..6c96c942d0 100644
--- a/libpurple/debug.c
+++ b/libpurple/debug.c
@@ -1,8 +1,3 @@
-/**
- * @file debug.c Debug API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -50,6 +45,8 @@ static gboolean debug_enabled = FALSE;
static gboolean debug_verbose = FALSE;
static gboolean debug_unsafe = FALSE;
+static gboolean debug_colored = FALSE;
+
static void
purple_debug_vargs(PurpleDebugLevel level, const char *category,
const char *format, va_list args)
@@ -67,20 +64,40 @@ purple_debug_vargs(PurpleDebugLevel level, const char *category,
return;
arg_s = g_strdup_vprintf(format, args);
+ g_strchomp(arg_s); /* strip trailing linefeeds */
if (debug_enabled) {
gchar *ts_s;
const char *mdate;
time_t mtime = time(NULL);
-
+ const gchar *format_pre, *format_post;
+
+ format_pre = "";
+ format_post = "";
+
+ if (!debug_colored)
+ format_pre = "";
+ else if (level == PURPLE_DEBUG_MISC)
+ format_pre = "\033[0;37m";
+ else if (level == PURPLE_DEBUG_INFO)
+ format_pre = "";
+ else if (level == PURPLE_DEBUG_WARNING)
+ format_pre = "\033[0;33m";
+ else if (level == PURPLE_DEBUG_ERROR)
+ format_pre = "\033[1;31m";
+ else if (level == PURPLE_DEBUG_FATAL)
+ format_pre = "\033[1;33;41m";
+
+ if (format_pre[0] != '\0')
+ format_post = "\033[0m";
mdate = purple_utf8_strftime("%H:%M:%S", localtime(&mtime));
ts_s = g_strdup_printf("(%s) ", mdate);
if (category == NULL)
- g_print("%s%s", ts_s, arg_s);
+ g_print("%s%s%s%s\n", format_pre, ts_s, arg_s, format_post);
else
- g_print("%s%s: %s", ts_s, category, arg_s);
+ g_print("%s%s%s: %s%s\n", format_pre, ts_s, category, arg_s, format_post);
g_free(ts_s);
}
@@ -177,6 +194,33 @@ purple_debug_is_enabled()
return debug_enabled;
}
+static PurpleDebugUiOps *
+purple_debug_ui_ops_copy(PurpleDebugUiOps *ops)
+{
+ PurpleDebugUiOps *ops_new;
+
+ g_return_val_if_fail(ops != NULL, NULL);
+
+ ops_new = g_new(PurpleDebugUiOps, 1);
+ *ops_new = *ops;
+
+ return ops_new;
+}
+
+GType
+purple_debug_ui_ops_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleDebugUiOps",
+ (GBoxedCopyFunc)purple_debug_ui_ops_copy,
+ (GBoxedFreeFunc)g_free);
+ }
+
+ return type;
+}
+
void
purple_debug_set_ui_ops(PurpleDebugUiOps *ops)
{
@@ -207,6 +251,12 @@ purple_debug_set_unsafe(gboolean unsafe)
debug_unsafe = unsafe;
}
+void
+purple_debug_set_colored(gboolean colored)
+{
+ debug_colored = colored;
+}
+
PurpleDebugUiOps *
purple_debug_get_ui_ops(void)
{
@@ -224,12 +274,5 @@ purple_debug_init(void)
purple_debug_set_verbose(TRUE);
purple_prefs_add_none("/purple/debug");
-
- /*
- * This pref is obsolete and no longer referenced anywhere. It only
- * survives here because it would be an API break if we removed it.
- * Remove this when we get to 3.0.0 :)
- */
- purple_prefs_add_bool("/purple/debug/timestamps", TRUE);
}
diff --git a/libpurple/debug.h b/libpurple/debug.h
index b90b86b99c..6b9d853cda 100644
--- a/libpurple/debug.h
+++ b/libpurple/debug.h
@@ -1,8 +1,3 @@
-/**
- * @file debug.h Debug API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -23,221 +18,260 @@
* 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_DEBUG_H_
#define _PURPLE_DEBUG_H_
+/**
+ * SECTION:debug
+ * @section_id: libpurple-debug
+ * @short_description: <filename>debug.h</filename>
+ * @title: Debug API
+ */
#include <glib.h>
+#include <glib-object.h>
+
#include <stdarg.h>
+#define PURPLE_TYPE_DEBUG_UI_OPS (purple_debug_ui_ops_get_type())
+
+typedef struct _PurpleDebugUiOps PurpleDebugUiOps;
+
/**
+ * PurpleDebugLevel:
+ * @PURPLE_DEBUG_ALL: All debug levels.
+ * @PURPLE_DEBUG_MISC: General chatter.
+ * @PURPLE_DEBUG_INFO: General operation Information.
+ * @PURPLE_DEBUG_WARNING: Warnings.
+ * @PURPLE_DEBUG_ERROR: Errors.
+ * @PURPLE_DEBUG_FATAL: Fatal errors.
+ *
* Debug levels.
*/
typedef enum
{
- PURPLE_DEBUG_ALL = 0, /**< All debug levels. */
- PURPLE_DEBUG_MISC, /**< General chatter. */
- PURPLE_DEBUG_INFO, /**< General operation Information. */
- PURPLE_DEBUG_WARNING, /**< Warnings. */
- PURPLE_DEBUG_ERROR, /**< Errors. */
- PURPLE_DEBUG_FATAL /**< Fatal errors. */
+ PURPLE_DEBUG_ALL = 0,
+ PURPLE_DEBUG_MISC,
+ PURPLE_DEBUG_INFO,
+ PURPLE_DEBUG_WARNING,
+ PURPLE_DEBUG_ERROR,
+ PURPLE_DEBUG_FATAL
} PurpleDebugLevel;
/**
+ * PurpleDebugUiOps:
+ *
* Debug UI operations.
*/
-typedef struct
+struct _PurpleDebugUiOps
{
void (*print)(PurpleDebugLevel level, const char *category,
const char *arg_s);
gboolean (*is_enabled)(PurpleDebugLevel level,
const char *category);
+ /*< private >*/
void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
void (*_purple_reserved4)(void);
-} PurpleDebugUiOps;
+};
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name Debug API */
+/* Debug API */
/**************************************************************************/
/**
- * Outputs debug information.
+ * purple_debug:
+ * @level: The debug level.
+ * @category: The category (or %NULL).
+ * @format: The format string.
*
- * @param level The debug level.
- * @param category The category (or @c NULL).
- * @param format The format string.
+ * Outputs debug information.
*/
void purple_debug(PurpleDebugLevel level, const char *category,
const char *format, ...) G_GNUC_PRINTF(3, 4);
/**
+ * purple_debug_misc:
+ * @category: The category (or %NULL).
+ * @format: The format string.
+ *
* Outputs misc. level debug information.
*
* This is a wrapper for purple_debug(), and uses PURPLE_DEBUG_MISC as
* the level.
*
- * @param category The category (or @c NULL).
- * @param format The format string.
- *
- * @see purple_debug()
+ * See purple_debug().
*/
void purple_debug_misc(const char *category, const char *format, ...) G_GNUC_PRINTF(2, 3);
/**
+ * purple_debug_info:
+ * @category: The category (or %NULL).
+ * @format: The format string.
+ *
* Outputs info level debug information.
*
* This is a wrapper for purple_debug(), and uses PURPLE_DEBUG_INFO as
* the level.
*
- * @param category The category (or @c NULL).
- * @param format The format string.
- *
- * @see purple_debug()
+ * See purple_debug().
*/
void purple_debug_info(const char *category, const char *format, ...) G_GNUC_PRINTF(2, 3);
/**
+ * purple_debug_warning:
+ * @category: The category (or %NULL).
+ * @format: The format string.
+ *
* Outputs warning level debug information.
*
* This is a wrapper for purple_debug(), and uses PURPLE_DEBUG_WARNING as
* the level.
*
- * @param category The category (or @c NULL).
- * @param format The format string.
- *
- * @see purple_debug()
+ * See purple_debug().
*/
void purple_debug_warning(const char *category, const char *format, ...) G_GNUC_PRINTF(2, 3);
/**
+ * purple_debug_error:
+ * @category: The category (or %NULL).
+ * @format: The format string.
+ *
* Outputs error level debug information.
*
* This is a wrapper for purple_debug(), and uses PURPLE_DEBUG_ERROR as
* the level.
*
- * @param category The category (or @c NULL).
- * @param format The format string.
- *
- * @see purple_debug()
+ * See purple_debug().
*/
void purple_debug_error(const char *category, const char *format, ...) G_GNUC_PRINTF(2, 3);
/**
+ * purple_debug_fatal:
+ * @category: The category (or %NULL).
+ * @format: The format string.
+ *
* Outputs fatal error level debug information.
*
* This is a wrapper for purple_debug(), and uses PURPLE_DEBUG_ERROR as
* the level.
*
- * @param category The category (or @c NULL).
- * @param format The format string.
- *
- * @see purple_debug()
+ * See purple_debug().
*/
void purple_debug_fatal(const char *category, const char *format, ...) G_GNUC_PRINTF(2, 3);
/**
- * Enable or disable printing debug output to the console.
+ * purple_debug_set_enabled:
+ * @enabled: TRUE to enable debug output or FALSE to disable it.
*
- * @param enabled TRUE to enable debug output or FALSE to disable it.
+ * Enable or disable printing debug output to the console.
*/
void purple_debug_set_enabled(gboolean enabled);
/**
+ * purple_debug_is_enabled:
+ *
* Check if console debug output is enabled.
*
- * @return TRUE if debugging is enabled, FALSE if it is not.
+ * Returns: TRUE if debugging is enabled, FALSE if it is not.
*/
gboolean purple_debug_is_enabled(void);
/**
+ * purple_debug_set_verbose:
+ * @verbose: TRUE to enable verbose debugging or FALSE to disable it.
+ *
* Enable or disable verbose debugging. This ordinarily should only be called
* by #purple_debug_init, but there are cases where this can be useful for
* plugins.
- *
- * @param verbose TRUE to enable verbose debugging or FALSE to disable it.
- *
- * @since 2.6.0
*/
void purple_debug_set_verbose(gboolean verbose);
/**
- * Check if verbose logging is enabled.
+ * purple_debug_is_verbose:
*
- * @return TRUE if verbose debugging is enabled, FALSE if it is not.
+ * Check if verbose logging is enabled.
*
- * @since 2.6.0
+ * Returns: TRUE if verbose debugging is enabled, FALSE if it is not.
*/
gboolean purple_debug_is_verbose(void);
/**
- * Enable or disable unsafe debugging. This ordinarily should only be called
- * by #purple_debug_init, but there are cases where this can be useful for
- * plugins.
- *
- * @param unsafe TRUE to enable debug logging of messages that could
+ * purple_debug_set_unsafe:
+ * @unsafe: TRUE to enable debug logging of messages that could
* potentially contain passwords and other sensitive information.
* FALSE to disable it.
*
- * @since 2.6.0
+ * Enable or disable unsafe debugging. This ordinarily should only be called
+ * by #purple_debug_init, but there are cases where this can be useful for
+ * plugins.
*/
void purple_debug_set_unsafe(gboolean unsafe);
/**
+ * purple_debug_is_unsafe:
+ *
* Check if unsafe debugging is enabled. Defaults to FALSE.
*
- * @return TRUE if the debug logging of all messages is enabled, FALSE
+ * Returns: TRUE if the debug logging of all messages is enabled, FALSE
* if messages that could potentially contain passwords and other
* sensitive information are not logged.
- *
- * @since 2.6.0
*/
gboolean purple_debug_is_unsafe(void);
-/*@}*/
+/**
+ * purple_debug_set_colored:
+ * @colored: TRUE to enable colored output, FALSE to disable it.
+ *
+ * Enable or disable colored output for bash console.
+ */
+void purple_debug_set_colored(gboolean colored);
/**************************************************************************/
-/** @name UI Registration Functions */
+/* UI Registration Functions */
/**************************************************************************/
-/*@{*/
/**
+ * purple_debug_ui_ops_get_type:
+ *
+ * Returns: The #GType for the #PurpleDebugUiOps boxed structure.
+ */
+GType purple_debug_ui_ops_get_type(void);
+
+/**
+ * purple_debug_set_ui_ops:
+ * @ops: The UI operations structure.
+ *
* Sets the UI operations structure to be used when outputting debug
* information.
- *
- * @param ops The UI operations structure.
*/
void purple_debug_set_ui_ops(PurpleDebugUiOps *ops);
/**
+ * purple_debug_get_ui_ops:
+ *
* Returns the UI operations structure used when outputting debug
* information.
*
- * @return The UI operations structure in use.
+ * Returns: The UI operations structure in use.
*/
PurpleDebugUiOps *purple_debug_get_ui_ops(void);
-/*@}*/
-
/**************************************************************************/
-/** @name Debug Subsystem */
+/* Debug Subsystem */
/**************************************************************************/
-/*@{*/
/**
+ * purple_debug_init:
+ *
* Initializes the debug subsystem.
*/
void purple_debug_init(void);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_DEBUG_H_ */
diff --git a/libpurple/desktopitem.c b/libpurple/desktopitem.c
index d7f6d39d2d..8f2efa087a 100644
--- a/libpurple/desktopitem.c
+++ b/libpurple/desktopitem.c
@@ -1,8 +1,3 @@
-/**
- * @file purple-desktop-item.c Functions for managing .desktop files
- * @ingroup core
- */
-
/* 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.
@@ -823,10 +818,12 @@ lookup_locale (const PurpleDesktopItem *item, const char *key, const char *local
}
}
-/**
+/*
+ * try_english_key:
+ *
* Fallback to find something suitable for C locale.
*
- * @return A newly allocated string which should be g_freed by the caller.
+ * Returns: A newly allocated string which should be g_freed by the caller.
*/
static char *
try_english_key (PurpleDesktopItem *item, const char *key)
diff --git a/libpurple/desktopitem.h b/libpurple/desktopitem.h
index b9ae357ee1..d2b3393d38 100644
--- a/libpurple/desktopitem.h
+++ b/libpurple/desktopitem.h
@@ -1,8 +1,3 @@
-/**
- * @file desktopitem.h Functions for managing .desktop files
- * @ingroup core
- */
-
/* 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.
@@ -55,6 +50,12 @@
#ifndef _PURPLE_DESKTOP_ITEM_H_
#define _PURPLE_DESKTOP_ITEM_H_
+/**
+ * SECTION:desktopitem
+ * @section_id: libpurple-desktopitem
+ * @short_description: <filename>desktopitem.h</filename>
+ * @title: Managing .desktop files
+ */
#include <glib.h>
#include <glib-object.h>
@@ -116,54 +117,59 @@ GType purple_desktop_item_get_type (void);
#define PURPLE_DESKTOP_ITEM_DOC_PATH "X-GNOME-DocPath" /* string */
/**
- * This function loads 'filename' and turns it into a PurpleDesktopItem.
+ * purple_desktop_item_new_from_file:
+ * @filename: The filename or directory path to load the PurpleDesktopItem from
*
- * @param filename The filename or directory path to load the PurpleDesktopItem from
+ * This function loads 'filename' and turns it into a PurpleDesktopItem.
*
- * @return The newly loaded item, or NULL on error.
+ * Returns: The newly loaded item, or NULL on error.
*/
PurpleDesktopItem *purple_desktop_item_new_from_file (const char *filename);
/**
+ * purple_desktop_item_get_entry_type:
+ * @item: A desktop item
+ *
* Gets the type attribute (the 'Type' field) of the item. This should
* usually be 'Application' for an application, but it can be 'Directory'
* for a directory description. There are other types available as well.
* The type usually indicates how the desktop item should be handeled and
* how the 'Exec' field should be handeled.
*
- * @param item A desktop item
- *
- * @return The type of the specified 'item'. The returned memory
+ * Returns: The type of the specified 'item'. The returned memory
* remains owned by the PurpleDesktopItem and should not be freed.
*/
PurpleDesktopItemType purple_desktop_item_get_entry_type (const PurpleDesktopItem *item);
/**
- * Gets the value of an attribute of the item, as a string.
+ * purple_desktop_item_get_string:
+ * @item: A desktop item
+ * @attr: The attribute to look for
*
- * @param item A desktop item
- * @param attr The attribute to look for
+ * Gets the value of an attribute of the item, as a string.
*
- * @return The value of the specified item attribute.
+ * Returns: The value of the specified item attribute.
*/
const char *purple_desktop_item_get_string (const PurpleDesktopItem *item,
const char *attr);
/**
+ * purple_desktop_item_copy:
+ * @item: The item to be copied
+ *
* Creates a copy of a PurpleDesktopItem. The new copy has a refcount of 1.
* Note: Section stack is NOT copied.
*
- * @param item The item to be copied
- *
- * @return The new copy
+ * Returns: The new copy
*/
PurpleDesktopItem *purple_desktop_item_copy (const PurpleDesktopItem *item);
/**
+ * purple_desktop_item_unref:
+ * @item: A desktop item
+ *
* Decreases the reference count of the specified item, and destroys
* the item if there are no more references left.
- *
- * @param item A desktop item
*/
void purple_desktop_item_unref (PurpleDesktopItem *item);
diff --git a/libpurple/dnsquery.c b/libpurple/dnsquery.c
index 508e502013..5c2f6a478c 100644
--- a/libpurple/dnsquery.c
+++ b/libpurple/dnsquery.c
@@ -1,8 +1,3 @@
-/**
- * @file dnsquery.c DNS query API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -82,8 +77,7 @@ struct _PurpleDnsQueryResolverProcess {
};
static GSList *free_dns_children = NULL;
-/* TODO: Make me a GQueue when we require >= glib 2.4 */
-static GSList *queued_requests = NULL;
+static GQueue *queued_requests = NULL;
static int number_of_dns_children = 0;
@@ -245,7 +239,7 @@ write_to_parent(int fd, const void *buf, size_t count)
ssize_t written;
written = write(fd, buf, count);
- if (written != count) {
+ if (written < 0 || (gsize)written != count) {
if (written < 0)
fprintf(stderr, "dns[%d]: Error writing data to "
"parent: %s\n", getpid(), strerror(errno));
@@ -299,6 +293,10 @@ purple_dnsquery_resolver_run(int child_out, int child_in, gboolean show_debug)
}
rc = read(child_in, &dns_params, sizeof(dns_params_t));
if (rc < 0) {
+ if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
+ /* Try again */
+ continue;
+ }
fprintf(stderr, "dns[%d]: Error: Could not read dns_params: "
"%s\n", getpid(), strerror(errno));
break;
@@ -421,7 +419,7 @@ cope_with_gdb_brokenness(void)
if(n < 0)
return;
- e[MIN(n,sizeof(e)-1)] = '\0';
+ e[MIN((gsize)n,sizeof(e)-1)] = '\0';
if(strstr(e,"gdb")) {
purple_debug_info("dns",
@@ -510,11 +508,13 @@ purple_dnsquery_resolver_new(gboolean show_debug)
return resolver;
}
-/**
- * @return TRUE if the request was sent succesfully. FALSE
- * if the request could not be sent. This isn't
- * necessarily an error. If the child has expired,
- * for example, we won't be able to send the message.
+/*
+ * send_dns_request_to_child:
+ *
+ * Returns: TRUE if the request was sent succesfully. FALSE
+ * if the request could not be sent. This isn't
+ * necessarily an error. If the child has expired,
+ * for example, we won't be able to send the message.
*/
static gboolean
send_dns_request_to_child(PurpleDnsQueryData *query_data,
@@ -530,12 +530,12 @@ send_dns_request_to_child(PurpleDnsQueryData *query_data,
* instance, we can't use it. */
pid = waitpid(resolver->dns_pid, NULL, WNOHANG);
if (pid > 0) {
- purple_debug_warning("dns", "DNS child %d no longer exists\n",
+ purple_debug_info("dns", "DNS child %d no longer exists\n",
resolver->dns_pid);
purple_dnsquery_resolver_destroy(resolver);
return FALSE;
} else if (pid < 0) {
- purple_debug_warning("dns", "Wait for DNS child %d failed: %s\n",
+ purple_debug_info("dns", "Wait for DNS child %d failed: %s\n",
resolver->dns_pid, g_strerror(errno));
purple_dnsquery_resolver_destroy(resolver);
return FALSE;
@@ -554,7 +554,7 @@ send_dns_request_to_child(PurpleDnsQueryData *query_data,
purple_dnsquery_resolver_destroy(resolver);
return FALSE;
}
- if (rc < sizeof(dns_params)) {
+ if ((gsize)rc < sizeof(dns_params)) {
purple_debug_error("dns", "Tried to write %" G_GSSIZE_FORMAT
" bytes to child but only wrote %" G_GSSIZE_FORMAT "\n",
sizeof(dns_params), rc);
@@ -579,12 +579,11 @@ handle_next_queued_request(void)
PurpleDnsQueryData *query_data;
PurpleDnsQueryResolverProcess *resolver;
- if (queued_requests == NULL)
+ if (g_queue_is_empty(queued_requests))
/* No more DNS queries, yay! */
return;
- query_data = queued_requests->data;
- queued_requests = g_slist_delete_link(queued_requests, queued_requests);
+ query_data = g_queue_pop_head(queued_requests);
/*
* If we have any children, attempt to have them perform the DNS
@@ -608,7 +607,7 @@ handle_next_queued_request(void)
if (number_of_dns_children >= MAX_DNS_CHILDREN)
{
/* Apparently all our children are busy */
- queued_requests = g_slist_prepend(queued_requests, query_data);
+ g_queue_push_head(queued_requests, query_data);
return;
}
@@ -694,7 +693,7 @@ host_resolved(gpointer data, gint source, PurpleInputCondition cond)
static void
resolve_host(PurpleDnsQueryData *query_data)
{
- queued_requests = g_slist_append(queued_requests, query_data);
+ g_queue_push_tail(queued_requests, query_data);
handle_next_queued_request();
}
@@ -732,8 +731,10 @@ static gpointer
dns_thread(gpointer data)
{
PurpleDnsQueryData *query_data;
-#ifdef HAVE_GETADDRINFO
+#if defined(HAVE_GETADDRINFO) || defined(USE_IDN)
int rc;
+#endif
+#ifdef HAVE_GETADDRINFO
struct addrinfo hints, *res, *tmp;
char servname[20];
#else
@@ -818,8 +819,8 @@ resolve_host(PurpleDnsQueryData *query_data)
* Spin off a separate thread to perform the DNS lookup so
* that we don't block the UI.
*/
- query_data->resolver = g_thread_create(dns_thread,
- query_data, FALSE, &err);
+ query_data->resolver = g_thread_try_new("dnsquery resolver", dns_thread,
+ query_data, &err);
if (query_data->resolver == NULL)
{
char message[1024];
@@ -828,6 +829,8 @@ resolve_host(PurpleDnsQueryData *query_data)
g_error_free(err);
purple_dnsquery_failed(query_data, message);
}
+ else
+ g_thread_unref(query_data->resolver);
}
#else /* not PURPLE_DNSQUERY_USE_FORK or _WIN32 */
@@ -894,7 +897,7 @@ initiate_resolving(gpointer data)
/* resolve_ip calls purple_dnsquery_resolved */
return FALSE;
- proxy_type = purple_proxy_info_get_type(
+ proxy_type = purple_proxy_info_get_proxy_type(
purple_proxy_get_setup(query_data->account));
if (proxy_type == PURPLE_PROXY_TOR) {
purple_dnsquery_failed(query_data,
@@ -912,7 +915,7 @@ initiate_resolving(gpointer data)
}
PurpleDnsQueryData *
-purple_dnsquery_a_account(PurpleAccount *account, const char *hostname, int port,
+purple_dnsquery_a(PurpleAccount *account, const char *hostname, int port,
PurpleDnsQueryConnectFunction callback, gpointer data)
{
PurpleDnsQueryData *query_data;
@@ -942,13 +945,6 @@ purple_dnsquery_a_account(PurpleAccount *account, const char *hostname, int port
return query_data;
}
-PurpleDnsQueryData *
-purple_dnsquery_a(const char *hostname, int port,
- PurpleDnsQueryConnectFunction callback, gpointer data)
-{
- return purple_dnsquery_a_account(NULL, hostname, port, callback, data);
-}
-
void
purple_dnsquery_destroy(PurpleDnsQueryData *query_data)
{
@@ -958,7 +954,7 @@ purple_dnsquery_destroy(PurpleDnsQueryData *query_data)
ops->destroy(query_data);
#if defined(PURPLE_DNSQUERY_USE_FORK)
- queued_requests = g_slist_remove(queued_requests, query_data);
+ g_queue_remove(queued_requests, query_data);
if (query_data->resolver != NULL)
/*
@@ -1017,6 +1013,33 @@ purple_dnsquery_get_port(PurpleDnsQueryData *query_data)
return query_data->port;
}
+static PurpleDnsQueryUiOps *
+purple_dnsquery_ui_ops_copy(PurpleDnsQueryUiOps *ops)
+{
+ PurpleDnsQueryUiOps *ops_new;
+
+ g_return_val_if_fail(ops != NULL, NULL);
+
+ ops_new = g_new(PurpleDnsQueryUiOps, 1);
+ *ops_new = *ops;
+
+ return ops_new;
+}
+
+GType
+purple_dnsquery_ui_ops_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleDnsQueryUiOps",
+ (GBoxedCopyFunc)purple_dnsquery_ui_ops_copy,
+ (GBoxedFreeFunc)g_free);
+ }
+
+ return type;
+}
+
void
purple_dnsquery_set_ui_ops(PurpleDnsQueryUiOps *ops)
{
@@ -1035,6 +1058,9 @@ purple_dnsquery_get_ui_ops(void)
void
purple_dnsquery_init(void)
{
+#if defined(PURPLE_DNSQUERY_USE_FORK)
+ queued_requests = g_queue_new();
+#endif
}
void
@@ -1046,5 +1072,9 @@ purple_dnsquery_uninit(void)
purple_dnsquery_resolver_destroy(free_dns_children->data);
free_dns_children = g_slist_remove(free_dns_children, free_dns_children->data);
}
+
+ g_queue_free(queued_requests);
+ queued_requests = NULL;
#endif /* end PURPLE_DNSQUERY_USE_FORK */
}
+
diff --git a/libpurple/dnsquery.h b/libpurple/dnsquery.h
index dde19983f6..7adee548e1 100644
--- a/libpurple/dnsquery.h
+++ b/libpurple/dnsquery.h
@@ -1,8 +1,3 @@
-/**
- * @file dnsquery.h DNS query API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -23,21 +18,35 @@
* 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_DNSQUERY_H_
#define _PURPLE_DNSQUERY_H_
+/**
+ * SECTION:dnsquery
+ * @section_id: libpurple-dnsquery
+ * @short_description: <filename>dnsquery.h</filename>
+ * @title: DNS Query API
+ */
#include <glib.h>
#include "eventloop.h"
#include "account.h"
+#define PURPLE_TYPE_DNSQUERY_UI_OPS (purple_dnsquery_ui_ops_get_type())
+
/**
+ * PurpleDnsQueryData:
+ *
* An opaque structure representing a DNS query. The hostname and port
* associated with the query can be retrieved using
* purple_dnsquery_get_host() and purple_dnsquery_get_port().
*/
typedef struct _PurpleDnsQueryData PurpleDnsQueryData;
+typedef struct _PurpleDnsQueryUiOps PurpleDnsQueryUiOps;
/**
+ * PurpleDnsQueryConnectFunction:
+ *
* The "hosts" parameter is a linked list containing pairs of
* one size_t addrlen and one struct sockaddr *addr. It should
* be free'd by the callback function.
@@ -45,136 +54,142 @@ typedef struct _PurpleDnsQueryData PurpleDnsQueryData;
typedef void (*PurpleDnsQueryConnectFunction)(GSList *hosts, gpointer data, const char *error_message);
/**
- * Callbacks used by the UI if it handles resolving DNS
+ * PurpleDnsQueryResolvedCallback:
+ *
+ * DNS query resolved callback used by the UI if it handles resolving DNS
*/
typedef void (*PurpleDnsQueryResolvedCallback) (PurpleDnsQueryData *query_data, GSList *hosts);
+
+/**
+ * PurpleDnsQueryFailedCallback:
+ *
+ * DNS query failed callback used by the UI if it handles resolving DNS
+ */
typedef void (*PurpleDnsQueryFailedCallback) (PurpleDnsQueryData *query_data, const gchar *error_message);
/**
+ * PurpleDnsQueryUiOps:
+ * @resolve_host: If implemented, return %TRUE if the UI takes responsibility
+ * for DNS queries. When returning %FALSE, the standard
+ * implementation is used.
+ * @destroy: Called just before @query_data is freed; this should cancel
+ * any further use of @query_data the UI would make. Unneeded if
+ * @resolve_host is not implemented.
+ *
* DNS Request UI operations; UIs should implement this if they want to do DNS
* lookups themselves, rather than relying on the core.
*
- * @see @ref ui-ops
+ * See <link linkend="chapter-ui-ops">List of <literal>UiOps</literal>
+ * Structures</link>
*/
-typedef struct
+struct _PurpleDnsQueryUiOps
{
- /** If implemented, return TRUE if the UI takes responsibility for DNS
- * queries. When returning FALSE, the standard implementation is used. */
gboolean (*resolve_host)(PurpleDnsQueryData *query_data,
PurpleDnsQueryResolvedCallback resolved_cb,
PurpleDnsQueryFailedCallback failed_cb);
- /** Called just before @a query_data is freed; this should cancel any
- * further use of @a query_data the UI would make. Unneeded if
- * #resolve_host is not implemented.
- */
void (*destroy)(PurpleDnsQueryData *query_data);
+ /*< private >*/
void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
void (*_purple_reserved4)(void);
-} PurpleDnsQueryUiOps;
+};
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name DNS query API */
+/* DNS query API */
/**************************************************************************/
-/*@{*/
/**
- * Perform an asynchronous DNS query.
- *
- * @param account the account that the query is being done for (or NULL)
- * @param hostname The hostname to resolve.
- * @param port A port number which is stored in the struct sockaddr.
- * @param callback The callback function to call after resolving.
- * @param data Extra data to pass to the callback function.
+ * purple_dnsquery_ui_ops_get_type:
*
- * @return NULL if there was an error, otherwise return a reference to
- * a data structure that can be used to cancel the pending
- * DNS query, if needed.
- *
- * @since 2.8.0
+ * Returns: The #GType for the #PurpleDnsQueryUiOps boxed structure.
*/
-PurpleDnsQueryData *purple_dnsquery_a_account(PurpleAccount *account, const char *hostname, int port, PurpleDnsQueryConnectFunction callback, gpointer data);
+GType purple_dnsquery_ui_ops_get_type(void);
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_DNSQUERY_C_)
/**
- * Perform an asynchronous DNS query.
+ * purple_dnsquery_a:
+ * @account: The account that the query is being done for (or NULL)
+ * @hostname: The hostname to resolve.
+ * @port: A port number which is stored in the struct sockaddr.
+ * @callback: (scope call): The callback function to call after resolving.
+ * @data: Extra data to pass to the callback function.
*
- * @param hostname The hostname to resolve.
- * @param port A port number which is stored in the struct sockaddr.
- * @param callback The callback function to call after resolving.
- * @param data Extra data to pass to the callback function.
+ * Perform an asynchronous DNS query.
*
- * @return NULL if there was an error, otherwise return a reference to
+ * Returns: NULL if there was an error, otherwise return a reference to
* a data structure that can be used to cancel the pending
* DNS query, if needed.
*
- * @deprecated Use purple_dnsquery_a_account instead
*/
-PurpleDnsQueryData *purple_dnsquery_a(const char *hostname, int port, PurpleDnsQueryConnectFunction callback, gpointer data);
-#endif
+PurpleDnsQueryData *purple_dnsquery_a(PurpleAccount *account, const char *hostname, int port, PurpleDnsQueryConnectFunction callback, gpointer data);
/**
- * Cancel a DNS query and destroy the associated data structure.
- *
- * @param query_data The DNS query to cancel. This data structure
+ * purple_dnsquery_destroy:
+ * @query_data: The DNS query to cancel. This data structure
* is freed by this function.
+ *
+ * Cancel a DNS query and destroy the associated data structure.
*/
void purple_dnsquery_destroy(PurpleDnsQueryData *query_data);
/**
+ * purple_dnsquery_set_ui_ops:
+ * @ops: The UI operations structure.
+ *
* Sets the UI operations structure to be used when doing a DNS
* resolve. The UI operations need only be set if the UI wants to
* handle the resolve itself; otherwise, leave it as NULL.
- *
- * @param ops The UI operations structure.
*/
void purple_dnsquery_set_ui_ops(PurpleDnsQueryUiOps *ops);
/**
+ * purple_dnsquery_get_ui_ops:
+ *
* Returns the UI operations structure to be used when doing a DNS
* resolve.
*
- * @return The UI operations structure.
+ * Returns: The UI operations structure.
*/
PurpleDnsQueryUiOps *purple_dnsquery_get_ui_ops(void);
/**
+ * purple_dnsquery_get_host:
+ * @query_data: The DNS query
+ *
* Get the host associated with a PurpleDnsQueryData
*
- * @param query_data The DNS query
- * @return The host.
+ * Returns: The host.
*/
char *purple_dnsquery_get_host(PurpleDnsQueryData *query_data);
/**
+ * purple_dnsquery_get_port:
+ * @query_data: The DNS query
+ *
* Get the port associated with a PurpleDnsQueryData
*
- * @param query_data The DNS query
- * @return The port.
+ * Returns: The port.
*/
unsigned short purple_dnsquery_get_port(PurpleDnsQueryData *query_data);
/**
+ * purple_dnsquery_init:
+ *
* Initializes the DNS query subsystem.
*/
void purple_dnsquery_init(void);
/**
+ * purple_dnsquery_uninit:
+ *
* Uninitializes the DNS query subsystem.
*/
void purple_dnsquery_uninit(void);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_DNSQUERY_H_ */
diff --git a/libpurple/dnssrv.c b/libpurple/dnssrv.c
index 060b5ddb7c..d7f8550963 100644
--- a/libpurple/dnssrv.c
+++ b/libpurple/dnssrv.c
@@ -1,7 +1,3 @@
-/**
- * @file dnssrv.c
- */
-
/* purple
*
* Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
@@ -97,7 +93,7 @@ typedef struct _PurpleSrvResponseContainer {
static gboolean purple_srv_txt_query_ui_resolve(PurpleSrvTxtQueryData *query_data);
-/**
+/*
* Sort by priority, then by weight. Strictly numerically--no
* randomness. Technically we only need to sort by pref and then
* make sure any records with weight 0 are at the beginning of
@@ -121,16 +117,17 @@ responsecompare(gconstpointer ar, gconstpointer br)
return 1;
}
-/**
+/*
+ * select_random_response:
+ * @list: The list of PurpleSrvResponseContainer. This function
+ * removes a node from this list and returns the new list.
+ * @container_ptr: The PurpleSrvResponseContainer that was chosen
+ * will be returned here.
+ *
* Iterate over a list of PurpleSrvResponseContainer making the sum
* the running total of the sums. Select a random integer in the range
* (1, sum+1), then find the first element greater than or equal to the
* number selected. From RFC 2782.
- *
- * @param list The list of PurpleSrvResponseContainer. This function
- * removes a node from this list and returns the new list.
- * @param container_ptr The PurpleSrvResponseContainer that was chosen
- * will be returned here.
*/
static GList *
select_random_response(GList *list, PurpleSrvResponseContainer **container_ptr)
@@ -139,6 +136,8 @@ select_random_response(GList *list, PurpleSrvResponseContainer **container_ptr)
size_t runningtotal;
int r;
+ g_return_val_if_fail(list != NULL, NULL);
+
runningtotal = 0;
cur = list;
@@ -159,6 +158,8 @@ select_random_response(GList *list, PurpleSrvResponseContainer **container_ptr)
r = runningtotal ? g_random_int_range(1, runningtotal + 1) : 0;
cur = list;
while (r > ((PurpleSrvResponseContainer *)cur->data)->sum) {
+ if (G_UNLIKELY(!cur->next))
+ break;
cur = cur->next;
}
@@ -167,7 +168,7 @@ select_random_response(GList *list, PurpleSrvResponseContainer **container_ptr)
return g_list_delete_link(list, cur);
}
-/**
+/*
* Reorder a GList of PurpleSrvResponses that have the same priority
* (aka "pref").
*/
@@ -182,6 +183,8 @@ srv_reorder(GList *list, int num)
/* Nothing to sort */
return;
+ g_return_if_fail(list != NULL);
+
/* First build a list of container structs */
for (i = 0, cur = list; i < num; i++, cur = cur->next) {
container = g_new(PurpleSrvResponseContainer, 1);
@@ -196,6 +199,7 @@ srv_reorder(GList *list, int num)
*/
cur = list;
while (container_list) {
+ g_return_if_fail(cur);
container_list = select_random_response(container_list, &container);
cur->data = container->response;
g_free(container);
@@ -203,12 +207,14 @@ srv_reorder(GList *list, int num)
}
}
-/**
+/*
+ * purple_srv_sort:
+ * @list: The original list, resorted
+ *
* Sorts a GList of PurpleSrvResponses according to the
* algorithm described in RFC 2782.
*
- * @param response GList of PurpleSrvResponse's
- * @param The original list, resorted
+ * Returns: GList of PurpleSrvResponse's
*/
static GList *
purple_srv_sort(GList *list)
@@ -227,6 +233,9 @@ purple_srv_sort(GList *list)
count = 1;
while (cur) {
PurpleSrvResponse *next_response;
+
+ g_return_val_if_fail(cur->data, list);
+
pref = ((PurpleSrvResponse *)cur->data)->pref;
next_response = cur->next ? cur->next->data : NULL;
if (!next_response || next_response->pref != pref) {
@@ -424,8 +433,10 @@ resolve(int in, int out)
srvres = g_new0(PurpleSrvResponse, 1);
if (strlen(name) > sizeof(srvres->hostname) - 1) {
- purple_debug_error("dnssrv", "hostname is longer than available buffer ('%s', %zd bytes)!",
- name, strlen(name));
+ purple_debug_error("dnssrv", "hostname is "
+ "longer than available buffer ('%s', %"
+ G_GSIZE_FORMAT " bytes)!",
+ name, strlen(name));
}
g_strlcpy(srvres->hostname, name, sizeof(srvres->hostname));
srvres->pref = pref;
@@ -557,7 +568,7 @@ resolved(gpointer data, gint source, PurpleInputCondition cond)
res->content = g_new0(gchar, len);
red = read(source, res->content, len);
- if (red != len) {
+ if (red < 0 || (gsize)red != len) {
purple_debug_error("dnssrv","unable to read txt "
"response: %s\n", g_strerror(errno));
size = 0;
@@ -585,7 +596,7 @@ resolved(gpointer data, gint source, PurpleInputCondition cond)
#else /* _WIN32 */
-/** The Jabber Server code was inspiration for parts of this. */
+/* The Jabber Server code was inspiration for parts of this. */
static gboolean
res_main_thread_cb(gpointer data)
@@ -707,7 +718,7 @@ res_thread(gpointer data)
txtres = g_new0(PurpleTxtResponse, 1);
s = g_string_new("");
- for (i = 0; i < txt_data->dwStringCount; ++i)
+ for (i = 0; i < (int)txt_data->dwStringCount; ++i)
s = g_string_append(s, txt_data->pStringArray[i]);
txtres->content = g_string_free(s, FALSE);
@@ -732,15 +743,7 @@ res_thread(gpointer data)
#endif
PurpleSrvTxtQueryData *
-purple_srv_resolve(const char *protocol, const char *transport,
- const char *domain, PurpleSrvCallback cb, gpointer extradata)
-{
- return purple_srv_resolve_account(NULL, protocol, transport, domain,
- cb, extradata);
-}
-
-PurpleSrvTxtQueryData *
-purple_srv_resolve_account(PurpleAccount *account, const char *protocol,
+purple_srv_resolve(PurpleAccount *account, const char *protocol,
const char *transport, const char *domain, PurpleSrvCallback cb,
gpointer extradata)
{
@@ -762,7 +765,7 @@ purple_srv_resolve_account(PurpleAccount *account, const char *protocol,
g_return_val_if_reached(NULL);
}
- proxy_type = purple_proxy_info_get_type(
+ proxy_type = purple_proxy_info_get_proxy_type(
purple_proxy_get_setup(account));
if (proxy_type == PURPLE_PROXY_TOR) {
purple_debug_info("dnssrv", "Aborting SRV lookup in Tor Proxy mode.\n");
@@ -804,6 +807,12 @@ purple_srv_resolve_account(PurpleAccount *account, const char *protocol,
return NULL;
}
+ /*
+ * TODO: We should put a cap on the number of forked processes that we
+ * allow at any given time. If we get too many requests they
+ * should be put into a queue and handled later. (This is what
+ * we do for A record lookups.)
+ */
pid = fork();
if (pid == -1) {
purple_debug_error("dnssrv", "Could not create process!\n");
@@ -842,11 +851,13 @@ purple_srv_resolve_account(PurpleAccount *account, const char *protocol,
return query_data;
#else
- query_data->resolver = g_thread_create(res_thread, query_data, FALSE, &err);
+ query_data->resolver = g_thread_try_new("dnssrv srv resolver", res_thread, query_data, &err);
if (query_data->resolver == NULL) {
query_data->error_message = g_strdup_printf("SRV thread create failure: %s\n", (err && err->message) ? err->message : "");
g_error_free(err);
}
+ else
+ g_thread_unref(query_data->resolver);
/* The query isn't going to happen, so finish the SRV lookup now.
* Asynchronously call the callback since stuff may not expect
@@ -858,13 +869,7 @@ purple_srv_resolve_account(PurpleAccount *account, const char *protocol,
#endif
}
-PurpleSrvTxtQueryData *purple_txt_resolve(const char *owner,
- const char *domain, PurpleTxtCallback cb, gpointer extradata)
-{
- return purple_txt_resolve_account(NULL, owner, domain, cb, extradata);
-}
-
-PurpleSrvTxtQueryData *purple_txt_resolve_account(PurpleAccount *account,
+PurpleSrvTxtQueryData *purple_txt_resolve(PurpleAccount *account,
const char *owner, const char *domain, PurpleTxtCallback cb,
gpointer extradata)
{
@@ -880,7 +885,7 @@ PurpleSrvTxtQueryData *purple_txt_resolve_account(PurpleAccount *account,
GError* err = NULL;
#endif
- proxy_type = purple_proxy_info_get_type(
+ proxy_type = purple_proxy_info_get_proxy_type(
purple_proxy_get_setup(account));
if (proxy_type == PURPLE_PROXY_TOR) {
purple_debug_info("dnssrv", "Aborting TXT lookup in Tor Proxy mode.\n");
@@ -923,6 +928,12 @@ PurpleSrvTxtQueryData *purple_txt_resolve_account(PurpleAccount *account,
return NULL;
}
+ /*
+ * TODO: We should put a cap on the number of forked processes that we
+ * allow at any given time. If we get too many requests they
+ * should be put into a queue and handled later. (This is what
+ * we do for A record lookups.)
+ */
pid = fork();
if (pid == -1) {
purple_debug_error("dnssrv", "Could not create process!\n");
@@ -961,11 +972,13 @@ PurpleSrvTxtQueryData *purple_txt_resolve_account(PurpleAccount *account,
return query_data;
#else
- query_data->resolver = g_thread_create(res_thread, query_data, FALSE, &err);
+ query_data->resolver = g_thread_try_new("dnssrv srv resolver", res_thread, query_data, &err);
if (query_data->resolver == NULL) {
query_data->error_message = g_strdup_printf("TXT thread create failure: %s\n", (err && err->message) ? err->message : "");
g_error_free(err);
}
+ else
+ g_thread_unref(query_data->resolver);
/* The query isn't going to happen, so finish the TXT lookup now.
* Asynchronously call the callback since stuff may not expect
@@ -977,18 +990,6 @@ PurpleSrvTxtQueryData *purple_txt_resolve_account(PurpleAccount *account,
#endif
}
-void
-purple_txt_cancel(PurpleSrvTxtQueryData *query_data)
-{
- purple_srv_txt_query_destroy(query_data);
-}
-
-void
-purple_srv_cancel(PurpleSrvTxtQueryData *query_data)
-{
- purple_srv_txt_query_destroy(query_data);
-}
-
const gchar *
purple_txt_response_get_content(PurpleTxtResponse *resp)
{
@@ -1119,9 +1120,39 @@ purple_srv_txt_query_get_query(PurpleSrvTxtQueryData *query_data)
int
-purple_srv_txt_query_get_type(PurpleSrvTxtQueryData *query_data)
+purple_srv_txt_query_get_query_type(PurpleSrvTxtQueryData *query_data)
{
g_return_val_if_fail(query_data != NULL, 0);
return query_data->type;
}
+
+/**************************************************************************
+ * GBoxed code
+ **************************************************************************/
+static PurpleSrvTxtQueryUiOps *
+purple_srv_txt_query_ui_ops_copy(PurpleSrvTxtQueryUiOps *ops)
+{
+ PurpleSrvTxtQueryUiOps *ops_new;
+
+ g_return_val_if_fail(ops != NULL, NULL);
+
+ ops_new = g_new(PurpleSrvTxtQueryUiOps, 1);
+ *ops_new = *ops;
+
+ return ops_new;
+}
+
+GType
+purple_srv_txt_query_ui_ops_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleSrvTxtQueryUiOps",
+ (GBoxedCopyFunc)purple_srv_txt_query_ui_ops_copy,
+ (GBoxedFreeFunc)g_free);
+ }
+
+ return type;
+}
diff --git a/libpurple/dnssrv.h b/libpurple/dnssrv.h
index 7435c10e0c..2c0323a40a 100644
--- a/libpurple/dnssrv.h
+++ b/libpurple/dnssrv.h
@@ -1,7 +1,3 @@
-/**
- * @file dnssrv.h
- */
-
/* purple
*
* Copyright (C) 2005, Thomas Butter <butter@uni-mannheim.de>
@@ -23,20 +19,22 @@
#ifndef _PURPLE_DNSSRV_H
#define _PURPLE_DNSSRV_H
+/**
+ * SECTION:dnssrv
+ * @section_id: libpurple-dnssrv
+ * @short_description: <filename>dnssrv.h</filename>
+ * @title: DNS SRV Utilities
+ */
-#ifdef __cplusplus
-extern "C" {
-#endif
+#define PURPLE_TYPE_SRV_TXT_QUERY_UI_OPS (purple_srv_txt_query_ui_ops_get_type())
typedef struct _PurpleSrvTxtQueryData PurpleSrvTxtQueryData;
typedef struct _PurpleSrvResponse PurpleSrvResponse;
typedef struct _PurpleTxtResponse PurpleTxtResponse;
-
-/* For compatibility, should be removed for 3.0.0
- */
-typedef struct _PurpleSrvTxtQueryData PurpleSrvQueryData;
+typedef struct _PurpleSrvTxtQueryUiOps PurpleSrvTxtQueryUiOps;
#include <glib.h>
+#include <glib-object.h>
enum PurpleDnsType {
PurpleDnsTypeTxt = 16,
@@ -58,34 +56,38 @@ typedef void (*PurpleSrvTxtQueryResolvedCallback) (PurpleSrvTxtQueryData *query
typedef void (*PurpleSrvTxtQueryFailedCallback) (PurpleSrvTxtQueryData *query_data, const gchar *error_message);
/**
+ * PurpleSrvTxtQueryUiOps:
+ * @resolve: implemented, return %TRUE if the UI takes responsibility for SRV
+ * queries. When returning %FALSE, the standard implementation is
+ * used. These callbacks <emphasis>MUST</emphasis> be called
+ * asynchronously.
+ * @destroy: Called just before @query_data is freed; this should cancel any
+ * further use of @query_data the UI would make. Unneeded if @resolve
+ * is not implemented.
+ *
* SRV Request UI operations; UIs should implement this if they want to do SRV
* lookups themselves, rather than relying on the core.
*
- * @see @ref ui-ops
+ * See <link linkend="chapter-ui-ops">List of <literal>UiOps</literal> Structures</link>
*/
-typedef struct
+struct _PurpleSrvTxtQueryUiOps
{
- /** If implemented, return TRUE if the UI takes responsibility for SRV
- * queries. When returning FALSE, the standard implementation is used.
- * These callbacks MUST be called asynchronously. */
gboolean (*resolve)(PurpleSrvTxtQueryData *query_data,
PurpleSrvTxtQueryResolvedCallback resolved_cb,
PurpleSrvTxtQueryFailedCallback failed_cb);
- /** Called just before @a query_data is freed; this should cancel any
- * further use of @a query_data the UI would make. Unneeded if
- * #resolve_host is not implemented.
- */
void (*destroy)(PurpleSrvTxtQueryData *query_data);
+ /*< private >*/
void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
void (*_purple_reserved4)(void);
-} PurpleSrvTxtQueryUiOps;
+};
/**
- * @param resp An array of PurpleSrvResponse of size results. The array
+ * PurpleSrvCallback:
+ * @resp: An array of PurpleSrvResponse of size results. The array
* is sorted based on the order described in the DNS SRV RFC.
* Users of this API should try each record in resp in order,
* starting at the beginning.
@@ -93,150 +95,124 @@ typedef struct
typedef void (*PurpleSrvCallback)(PurpleSrvResponse *resp, int results, gpointer data);
/**
- * Callback that returns the data retrieved from a DNS TXT lookup.
+ * PurpleTxtCallback:
+ * @responses: A GList of PurpleTxtResponse objects.
+ * @data: The extra data passed to purple_txt_resolve.
*
- * @param responses A GList of PurpleTxtResponse objects.
- * @param data The extra data passed to purple_txt_resolve.
+ * Callback that returns the data retrieved from a DNS TXT lookup.
*/
typedef void (*PurpleTxtCallback)(GList *responses, gpointer data);
-/**
- * Queries an SRV record.
- *
- * @param account the account that the query is being done for (or NULL)
- * @param protocol Name of the protocol (e.g. "sip")
- * @param transport Name of the transport ("tcp" or "udp")
- * @param domain Domain name to query (e.g. "blubb.com")
- * @param cb A callback which will be called with the results
- * @param extradata Extra data to be passed to the callback
- *
- * @since 2.8.0
- */
-PurpleSrvTxtQueryData *purple_srv_resolve_account(PurpleAccount *account, const char *protocol, const char *transport, const char *domain, PurpleSrvCallback cb, gpointer extradata);
+G_BEGIN_DECLS
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_DNSSRV_C_)
/**
- * Queries an SRV record.
- *
- * @param protocol Name of the protocol (e.g. "sip")
- * @param transport Name of the transport ("tcp" or "udp")
- * @param domain Domain name to query (e.g. "blubb.com")
- * @param cb A callback which will be called with the results
- * @param extradata Extra data to be passed to the callback
+ * purple_srv_txt_query_ui_ops_get_type:
*
- * @deprecated Use purple_srv_resolve_account instead
+ * Returns: The #GType for the #PurpleSrvTxtQueryUiOps boxed structure.
*/
-PurpleSrvTxtQueryData *purple_srv_resolve(const char *protocol, const char *transport, const char *domain, PurpleSrvCallback cb, gpointer extradata);
-#endif
+GType purple_srv_txt_query_ui_ops_get_type(void);
/**
- * Cancel an SRV or DNS query.
+ * purple_srv_resolve:
+ * @account: The account that the query is being done for (or %NULL)
+ * @protocol: Name of the protocol (e.g. "sip")
+ * @transport: Name of the transport ("tcp" or "udp")
+ * @domain: Domain name to query (e.g. "blubb.com")
+ * @cb: (scope call): A callback which will be called with the results
+ * @extradata: Extra data to be passed to the callback
*
- * @param query_data The request to cancel.
+ * Queries an SRV record.
*
- * @deprecated Use purple_srv_txt_query_destroy instead
+ * Returns: %NULL if there was an error, otherwise return a reference to
+ * a data structure that can be used to cancel the pending
+ * DNS query, if needed.
*/
-void purple_srv_cancel(PurpleSrvTxtQueryData *query_data);
+PurpleSrvTxtQueryData *purple_srv_resolve(PurpleAccount *account, const char *protocol, const char *transport, const char *domain, PurpleSrvCallback cb, gpointer extradata);
/**
- * Queries an TXT record.
- *
- * @param account the account that the query is being done for (or NULL)
- * @param owner Name of the protocol (e.g. "_xmppconnect")
- * @param domain Domain name to query (e.g. "blubb.com")
- * @param cb A callback which will be called with the results
- * @param extradata Extra data to be passed to the callback
+ * purple_txt_resolve:
+ * @account: The account that the query is being done for (or %NULL)
+ * @owner: Name of the protocol (e.g. "_xmppconnect")
+ * @domain: Domain name to query (e.g. "blubb.com")
+ * @cb: (scope call): A callback which will be called with the results
+ * @extradata: Extra data to be passed to the callback
*
- * @since 2.8.0
- */
-PurpleSrvTxtQueryData *purple_txt_resolve_account(PurpleAccount *account, const char *owner, const char *domain, PurpleTxtCallback cb, gpointer extradata);
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_DNSSRV_C_)
-/**
* Queries an TXT record.
*
- * @param owner Name of the protocol (e.g. "_xmppconnect")
- * @param domain Domain name to query (e.g. "blubb.com")
- * @param cb A callback which will be called with the results
- * @param extradata Extra data to be passed to the callback
- *
- * @deprecated Use purple_txt_resolve_account instead
- *
- * @since 2.6.0
+ * Returns: %NULL if there was an error, otherwise return a reference to
+ * a data structure that can be used to cancel the pending
+ * DNS query, if needed.
*/
-PurpleSrvTxtQueryData *purple_txt_resolve(const char *owner, const char *domain, PurpleTxtCallback cb, gpointer extradata);
-#endif
+PurpleSrvTxtQueryData *purple_txt_resolve(PurpleAccount *account, const char *owner, const char *domain, PurpleTxtCallback cb, gpointer extradata);
/**
- * Cancel an TXT DNS query.
- *
- * @param query_data The request to cancel.
- * @since 2.6.0
+ * purple_txt_response_get_content:
+ * @response: The TXT response record
*
- * @deprecated Use purple_srv_txt_query_destroy instead
- */
-void purple_txt_cancel(PurpleSrvTxtQueryData *query_data);
-
-/**
* Get the value of the current TXT record.
*
- * @param response The TXT response record
- * @returns The value of the current TXT record.
- * @since 2.6.0
+ * Returns: The value of the current TXT record.
*/
const gchar *purple_txt_response_get_content(PurpleTxtResponse *response);
/**
- * Destroy a TXT DNS response object.
+ * purple_txt_response_destroy:
+ * @response: The PurpleTxtResponse to destroy.
*
- * @param response The PurpleTxtResponse to destroy.
- * @since 2.6.0
+ * Destroy a TXT DNS response object.
*/
void purple_txt_response_destroy(PurpleTxtResponse *response);
/**
- * Cancel a SRV/TXT query and destroy the associated data structure.
- *
- * @param query_data The SRV/TXT query to cancel. This data structure
+ * purple_srv_txt_query_destroy:
+ * @query_data: The SRV/TXT query to cancel. This data structure
* is freed by this function.
+ *
+ * Cancel a SRV/TXT query and destroy the associated data structure.
*/
void purple_srv_txt_query_destroy(PurpleSrvTxtQueryData *query_data);
/**
+ * purple_srv_txt_query_set_ui_ops:
+ * @ops: The UI operations structure.
+ *
* Sets the UI operations structure to be used when doing a SRV/TXT
* resolve. The UI operations need only be set if the UI wants to
* handle the resolve itself; otherwise, leave it as NULL.
- *
- * @param ops The UI operations structure.
*/
void purple_srv_txt_query_set_ui_ops(PurpleSrvTxtQueryUiOps *ops);
/**
+ * purple_srv_txt_query_get_ui_ops:
+ *
* Returns the UI operations structure to be used when doing a SRV/TXT
* resolve.
*
- * @return The UI operations structure.
+ * Returns: The UI operations structure.
*/
PurpleSrvTxtQueryUiOps *purple_srv_txt_query_get_ui_ops(void);
/**
- * Get the query from a PurpleDnsQueryData
+ * purple_srv_txt_query_get_query:
+ * @query_data: The SRV/TXT query
+ *
+ * Get the query from a PurpleSrvTxtQueryData
*
- * @param query_data The SRV/TXT query
- * @return The query.
+ * Returns: The query.
*/
char *purple_srv_txt_query_get_query(PurpleSrvTxtQueryData *query_data);
/**
- * Get the type from a PurpleDnsQueryData (TXT or SRV)
+ * purple_srv_txt_query_get_query_type:
+ * @query_data: The query
*
- * @param query_data The query
- * @return The query.
+ * Get the type from a PurpleSrvTxtQueryData (TXT or SRV)
+ *
+ * Returns: The query type.
*/
-int purple_srv_txt_query_get_type(PurpleSrvTxtQueryData *query_data);
+int purple_srv_txt_query_get_query_type(PurpleSrvTxtQueryData *query_data);
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_DNSSRV_H */
+
diff --git a/libpurple/e2ee.c b/libpurple/e2ee.c
new file mode 100644
index 0000000000..93568c545a
--- /dev/null
+++ b/libpurple/e2ee.c
@@ -0,0 +1,252 @@
+/* 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 "e2ee.h"
+
+#include "debug.h"
+
+struct _PurpleE2eeState
+{
+ PurpleE2eeProvider *provider;
+
+ gchar *name;
+ gchar *stock_icon;
+
+ guint ref_count;
+};
+
+struct _PurpleE2eeProvider
+{
+ gchar *name;
+ PurpleE2eeConvMenuCallback conv_menu_cb;
+};
+
+static PurpleE2eeProvider *main_provider = NULL;
+
+/*** Encryption states for conversations. *************************************/
+
+PurpleE2eeState *
+purple_e2ee_state_new(PurpleE2eeProvider *provider)
+{
+ PurpleE2eeState *state;
+
+ g_return_val_if_fail(provider != NULL, NULL);
+
+ state = g_new0(PurpleE2eeState, 1);
+ state->provider = provider;
+ state->ref_count = 1;
+
+ return state;
+}
+
+void
+purple_e2ee_state_ref(PurpleE2eeState *state)
+{
+ g_return_if_fail(state != NULL);
+
+ state->ref_count++;
+}
+
+PurpleE2eeState *
+purple_e2ee_state_unref(PurpleE2eeState *state)
+{
+ if (state == NULL)
+ return NULL;
+
+ state->ref_count--;
+ if (state->ref_count > 0)
+ return state;
+
+ g_free(state->name);
+ g_free(state->stock_icon);
+ g_free(state);
+
+ return NULL;
+}
+
+PurpleE2eeProvider *
+purple_e2ee_state_get_provider(PurpleE2eeState *state)
+{
+ g_return_val_if_fail(state != NULL, NULL);
+
+ return state->provider;
+}
+
+void
+purple_e2ee_state_set_name(PurpleE2eeState *state, const gchar *name)
+{
+ g_return_if_fail(state != NULL);
+ g_return_if_fail(name != NULL);
+
+ g_free(state->name);
+ state->name = g_strdup(name);
+}
+
+const gchar *
+purple_e2ee_state_get_name(PurpleE2eeState *state)
+{
+ g_return_val_if_fail(state != NULL, NULL);
+
+ return state->name;
+}
+
+void
+purple_e2ee_state_set_stock_icon(PurpleE2eeState *state,
+ const gchar *stock_icon)
+{
+ g_return_if_fail(state != NULL);
+ g_return_if_fail(stock_icon != NULL);
+
+ g_free(state->stock_icon);
+ state->stock_icon = g_strdup(stock_icon);
+}
+
+const gchar *
+purple_e2ee_state_get_stock_icon(PurpleE2eeState *state)
+{
+ g_return_val_if_fail(state, NULL);
+
+ return state->stock_icon;
+}
+
+/*** Encryption providers API. ************************************************/
+
+PurpleE2eeProvider *
+purple_e2ee_provider_new(void)
+{
+ PurpleE2eeProvider *provider;
+
+ provider = g_new0(PurpleE2eeProvider, 1);
+
+ return provider;
+}
+
+void
+purple_e2ee_provider_free(PurpleE2eeProvider *provider)
+{
+ g_return_if_fail(provider != NULL);
+
+ if (provider == main_provider) {
+ purple_debug_error("e2ee", "This provider is still registered");
+ return;
+ }
+
+ g_free(provider->name);
+ g_free(provider);
+}
+
+gboolean
+purple_e2ee_provider_register(PurpleE2eeProvider *provider)
+{
+ g_return_val_if_fail(provider != NULL, FALSE);
+
+ if (main_provider != NULL)
+ return FALSE;
+
+ main_provider = provider;
+ return TRUE;
+}
+
+void
+purple_e2ee_provider_unregister(PurpleE2eeProvider *provider)
+{
+ GList *it, *clear_states = NULL;
+ g_return_if_fail(provider != NULL);
+
+ if (main_provider != provider) {
+ purple_debug_warning("e2ee", "This provider is not registered");
+ return;
+ }
+
+ for (it = purple_conversations_get_all(); it; it = g_list_next(it)) {
+ PurpleConversation *conv = it->data;
+ PurpleE2eeState *state;
+
+ state = purple_conversation_get_e2ee_state(conv);
+ if (!state)
+ continue;
+ if (provider == purple_e2ee_state_get_provider(state))
+ clear_states = g_list_prepend(clear_states, conv);
+ }
+
+ main_provider = NULL;
+
+ for (it = clear_states; it; it = g_list_next(it))
+ purple_conversation_set_e2ee_state(it->data, NULL);
+ g_list_free(clear_states);
+}
+
+PurpleE2eeProvider *
+purple_e2ee_provider_get_main(void)
+{
+ return main_provider;
+}
+
+void
+purple_e2ee_provider_set_name(PurpleE2eeProvider *provider, const gchar *name)
+{
+ g_return_if_fail(provider != NULL);
+ g_return_if_fail(name != NULL);
+
+ g_free(provider->name);
+ provider->name = g_strdup(name);
+}
+
+const gchar *
+purple_e2ee_provider_get_name(PurpleE2eeProvider *provider)
+{
+ g_return_val_if_fail(provider != NULL, NULL);
+
+ return provider->name;
+}
+
+void
+purple_e2ee_provider_set_conv_menu_cb(PurpleE2eeProvider *provider,
+ PurpleE2eeConvMenuCallback conv_menu_cb)
+{
+ g_return_if_fail(provider != NULL);
+
+ provider->conv_menu_cb = conv_menu_cb;
+}
+
+PurpleE2eeConvMenuCallback
+purple_e2ee_provider_get_conv_menu_cb(PurpleE2eeProvider *provider)
+{
+ g_return_val_if_fail(provider != NULL, NULL);
+
+ return provider->conv_menu_cb;
+}
+
+GList *
+purple_e2ee_provider_get_conv_menu_actions(PurpleE2eeProvider *provider,
+ PurpleConversation *conv)
+{
+ PurpleE2eeConvMenuCallback cb;
+
+ g_return_val_if_fail(provider, NULL);
+ g_return_val_if_fail(conv, NULL);
+
+ cb = purple_e2ee_provider_get_conv_menu_cb(provider);
+ if (cb == NULL)
+ return NULL;
+
+ return cb(conv);
+}
diff --git a/libpurple/e2ee.h b/libpurple/e2ee.h
new file mode 100644
index 0000000000..3352cbe351
--- /dev/null
+++ b/libpurple/e2ee.h
@@ -0,0 +1,259 @@
+/* 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 _PURPLE_E2EE_H_
+#define _PURPLE_E2EE_H_
+/**
+ * SECTION:e2ee
+ * @section_id: libpurple-e2ee
+ * @short_description: <filename>e2ee.h</filename>
+ * @title: End-to-end Encryption API
+ */
+
+typedef struct _PurpleE2eeState PurpleE2eeState;
+
+typedef struct _PurpleE2eeProvider PurpleE2eeProvider;
+
+#include <glib.h>
+#include "conversation.h"
+
+typedef GList * (*PurpleE2eeConvMenuCallback)(PurpleConversation *conv);
+
+G_BEGIN_DECLS
+
+/**************************************************************************/
+/* Encryption states for conversations. */
+/**************************************************************************/
+
+/**
+ * purple_e2ee_state_new:
+ * @provider: The E2EE provider that created this state.
+ *
+ * Creates new E2EE state.
+ *
+ * State objects are global (shared between multiple conversations).
+ *
+ * Returns: New E2EE state.
+ */
+PurpleE2eeState *
+purple_e2ee_state_new(PurpleE2eeProvider *provider);
+
+/**
+ * purple_e2ee_state_ref:
+ * @state: The E2EE state.
+ *
+ * Increment the reference count.
+ */
+void
+purple_e2ee_state_ref(PurpleE2eeState *state);
+
+/**
+ * purple_e2ee_state_unref:
+ * @state: The E2EE state.
+ *
+ * Decrement the reference count.
+ *
+ * If the reference count reaches zero, the state will be freed.
+ *
+ * Returns: @state or %NULL if the reference count reached zero.
+ */
+PurpleE2eeState *
+purple_e2ee_state_unref(PurpleE2eeState *state);
+
+/**
+ * purple_e2ee_state_get_provider:
+ * @state: The E2EE state.
+ *
+ * Gets the provider of specified E2EE state.
+ *
+ * Returns: The provider for this state.
+ */
+PurpleE2eeProvider *
+purple_e2ee_state_get_provider(PurpleE2eeState *state);
+
+/**
+ * purple_e2ee_state_set_name:
+ * @state: The E2EE state.
+ * @name: The localized name.
+ *
+ * Sets the name for the E2EE state.
+ */
+void
+purple_e2ee_state_set_name(PurpleE2eeState *state, const gchar *name);
+
+/**
+ * purple_e2ee_state_get_name:
+ * @state: The E2EE state.
+ *
+ * Gets the name of the E2EE state.
+ *
+ * Returns: The localized name.
+ */
+const gchar *
+purple_e2ee_state_get_name(PurpleE2eeState *state);
+
+/**
+ * purple_e2ee_state_set_stock_icon:
+ * @state: The E2EE state.
+ * @stock_icon: The stock icon identifier.
+ *
+ * Sets the icon for the E2EE state.
+ */
+void
+purple_e2ee_state_set_stock_icon(PurpleE2eeState *state,
+ const gchar *stock_icon);
+
+/**
+ * purple_e2ee_state_get_stock_icon:
+ * @state: The E2EE state.
+ *
+ * Gets the icon of the E2EE state.
+ *
+ * Returns: The stock icon identifier.
+ */
+const gchar *
+purple_e2ee_state_get_stock_icon(PurpleE2eeState *state);
+
+
+/**************************************************************************/
+/* Encryption providers API. */
+/**************************************************************************/
+
+/**
+ * purple_e2ee_provider_new:
+ *
+ * Creates new E2EE provider.
+ *
+ * Returns: New E2EE provider.
+ */
+PurpleE2eeProvider *
+purple_e2ee_provider_new(void);
+
+/**
+ * purple_e2ee_provider_free:
+ * @provider: The provider.
+ *
+ * Destroys the E2EE provider.
+ *
+ * The provider have to be unregistered prior.
+ */
+void
+purple_e2ee_provider_free(PurpleE2eeProvider *provider);
+
+/**
+ * purple_e2ee_provider_register:
+ * @provider: The E2EE provider.
+ *
+ * Registers the E2EE provider.
+ *
+ * Currently, there is no support for multiple E2EE providers - only the first
+ * one is registered.
+ *
+ * Returns: %TRUE, if the provider was successfully registered,
+ * %FALSE otherwise.
+ */
+gboolean
+purple_e2ee_provider_register(PurpleE2eeProvider *provider);
+
+/**
+ * purple_e2ee_provider_unregister:
+ * @provider: The E2EE provider.
+ *
+ * Unregisters the E2EE provider.
+ */
+void
+purple_e2ee_provider_unregister(PurpleE2eeProvider *provider);
+
+/**
+ * purple_e2ee_provider_get_main:
+ *
+ * Gets main E2EE provider.
+ *
+ * Returns: The main E2EE provider.
+ */
+PurpleE2eeProvider *
+purple_e2ee_provider_get_main(void);
+
+/**
+ * purple_e2ee_provider_set_name:
+ * @provider: The E2EE provider.
+ * @name: The localized name.
+ *
+ * Sets the name for the E2EE provider.
+ */
+void
+purple_e2ee_provider_set_name(PurpleE2eeProvider *provider, const gchar *name);
+
+/**
+ * purple_e2ee_provider_get_name:
+ * @provider: The E2EE provider.
+ *
+ * Gets the name of the E2EE provider.
+ *
+ * Returns: The localized name of specified E2EE provider.
+ */
+const gchar *
+purple_e2ee_provider_get_name(PurpleE2eeProvider *provider);
+
+/**
+ * purple_e2ee_provider_set_conv_menu_cb:
+ * @provider: The E2EE provider.
+ * @conv_menu_cb: (scope call): The callback.
+ *
+ * Sets the conversation menu callback for the E2EE provider.
+ *
+ * The function is called, when user extends the E2EE menu for the conversation
+ * specified in its parameter.
+ *
+ * Function should return the GList of PurpleMenuAction objects.
+ */
+void
+purple_e2ee_provider_set_conv_menu_cb(PurpleE2eeProvider *provider,
+ PurpleE2eeConvMenuCallback conv_menu_cb);
+
+/**
+ * purple_e2ee_provider_get_conv_menu_cb:
+ * @provider: The E2EE provider.
+ *
+ * Gets the conversation menu callback of the E2EE provider.
+ *
+ * Returns: The callback.
+ */
+PurpleE2eeConvMenuCallback
+purple_e2ee_provider_get_conv_menu_cb(PurpleE2eeProvider *provider);
+
+/**
+ * purple_e2ee_provider_get_conv_menu_actions:
+ * @provider: The E2EE provider.
+ * @conv: The conversation.
+ *
+ * Returns the list of actions for an E2EE menu.
+ *
+ * Returns: (transfer full): the #GList of #PurpleMenuAction's. Should be
+ * #g_list_free or #g_list_free_full'd when done using it.
+ */
+GList *
+purple_e2ee_provider_get_conv_menu_actions(PurpleE2eeProvider *provider,
+ PurpleConversation *conv);
+
+G_END_DECLS
+
+#endif /* _PURPLE_E2EE_H_ */
diff --git a/libpurple/enums.c.in b/libpurple/enums.c.in
new file mode 100644
index 0000000000..dd05073b96
--- /dev/null
+++ b/libpurple/enums.c.in
@@ -0,0 +1,64 @@
+/*** BEGIN file-header ***/
+/* 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
+ */
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include "enums.h"
+
+/*** END file-header ***/
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+#include "@filename@"
+
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+@enum_name@_get_type(void) {
+ static volatile gsize g_define_type_id__volatile = 0;
+
+ if(g_once_init_enter(&g_define_type_id__volatile)) {
+ static const G@Type@Value values [] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+ { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+ { 0, NULL, NULL }
+ };
+
+ GType g_define_type_id =
+ g_@type@_register_static(g_intern_static_string("@EnumName@"), values);
+ g_once_init_leave(&g_define_type_id__volatile, g_define_type_id);
+ }
+
+ return g_define_type_id__volatile;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+/*** END file-tail ***/
diff --git a/libpurple/enums.h.in b/libpurple/enums.h.in
new file mode 100644
index 0000000000..bc3f106d13
--- /dev/null
+++ b/libpurple/enums.h.in
@@ -0,0 +1,53 @@
+/*** BEGIN file-header ***/
+/* 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 PURPLE_ENUMS_H
+#define PURPLE_ENUMS_H
+/**
+ * SECTION:enums
+ * @section_id: libpurple-enums
+ * @short_description: <filename>enums.h</filename>
+ * @title: GEnums and GFlags
+ *
+ * This file contains all the #GEnum and #GFlags type definitions of libpurple.
+ */
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+/*** BEGIN value-header ***/
+GType @enum_name@_get_type(void) G_GNUC_CONST;
+#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+
+G_END_DECLS
+
+#endif /* PURPLE_ENUMS_H */
+/*** END file-tail ***/
diff --git a/libpurple/eventloop.c b/libpurple/eventloop.c
index 35467a4e9b..6922d3dc8f 100644
--- a/libpurple/eventloop.c
+++ b/libpurple/eventloop.c
@@ -1,8 +1,3 @@
-/**
- * @file eventloop.c Purple Event Loop API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -91,6 +86,16 @@ purple_input_get_error(int fd, int *error)
}
}
+int
+purple_input_pipe(int pipefd[2])
+{
+#ifdef _WIN32
+ return wpurple_input_pipe(pipefd);
+#else
+ return pipe(pipefd);
+#endif
+}
+
void
purple_eventloop_set_ui_ops(PurpleEventLoopUiOps *ops)
{
@@ -104,3 +109,33 @@ purple_eventloop_get_ui_ops(void)
return eventloop_ui_ops;
}
+
+/**************************************************************************
+ * GBoxed code
+ **************************************************************************/
+static PurpleEventLoopUiOps *
+purple_eventloop_ui_ops_copy(PurpleEventLoopUiOps *ops)
+{
+ PurpleEventLoopUiOps *ops_new;
+
+ g_return_val_if_fail(ops != NULL, NULL);
+
+ ops_new = g_new(PurpleEventLoopUiOps, 1);
+ *ops_new = *ops;
+
+ return ops_new;
+}
+
+GType
+purple_eventloop_ui_ops_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleEventLoopUiOps",
+ (GBoxedCopyFunc)purple_eventloop_ui_ops_copy,
+ (GBoxedFreeFunc)g_free);
+ }
+
+ return type;
+}
diff --git a/libpurple/eventloop.h b/libpurple/eventloop.h
index ca95f6cbe8..f92eb0e928 100644
--- a/libpurple/eventloop.h
+++ b/libpurple/eventloop.h
@@ -1,8 +1,3 @@
-/**
- * @file eventloop.h Purple Event Loop API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -23,254 +18,285 @@
* 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_EVENTLOOP_H_
#define _PURPLE_EVENTLOOP_H_
+/**
+ * SECTION:eventloop
+ * @section_id: libpurple-eventloop
+ * @short_description: <filename>eventloop.h</filename>
+ * @title: Event Loop API
+ */
#include <glib.h>
+#include <glib-object.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
+#define PURPLE_TYPE_EVENTLOOP_UI_OPS (purple_eventloop_ui_ops_get_type())
/**
+ * PurpleInputCondition:
+ * @PURPLE_INPUT_READ: A read condition.
+ * @PURPLE_INPUT_WRITE: A write condition.
+ *
* An input condition.
*/
typedef enum
{
- PURPLE_INPUT_READ = 1 << 0, /**< A read condition. */
- PURPLE_INPUT_WRITE = 1 << 1 /**< A write condition. */
+ PURPLE_INPUT_READ = 1 << 0,
+ PURPLE_INPUT_WRITE = 1 << 1
} PurpleInputCondition;
-/** The type of callbacks to handle events on file descriptors, as passed to
- * purple_input_add(). The callback will receive the @c user_data passed to
- * purple_input_add(), the file descriptor on which the event occurred, and the
- * condition that was satisfied to cause the callback to be invoked.
+/**
+ * PurpleInputFunction:
+ *
+ * The type of callbacks to handle events on file descriptors, as passed to
+ * purple_input_add(). The callback will receive the @user_data passed to
+ * purple_input_add(), the file descriptor on which the event occurred, and the
+ * condition that was satisfied to cause the callback to be invoked.
*/
typedef void (*PurpleInputFunction)(gpointer, gint, PurpleInputCondition);
-/** @copydoc _PurpleEventLoopUiOps */
typedef struct _PurpleEventLoopUiOps PurpleEventLoopUiOps;
-/** An abstraction of an application's mainloop; libpurple will use this to
- * watch file descriptors and schedule timed callbacks. If your application
- * uses the glib mainloop, there is an implementation of this struct in
- * <tt>libpurple/example/nullclient.c</tt> which you can use verbatim.
+/**
+ * PurpleEventLoopUiOps:
+ * @timeout_add: Should create a callback timer with an interval measured in
+ * milliseconds. The supplied @function should be called every
+ * @interval seconds until it returns %FALSE, after which it
+ * should not be called again.
+ * <sbr/>Analogous to g_timeout_add in glib.
+ * <sbr/>Note: On Win32, this function may be called from a thread
+ * other than the libpurple thread. You should make sure to detect
+ * this situation and to only call "function" from the libpurple
+ * thread.
+ * <sbr/>See purple_timeout_add().
+ * <sbr/>@interval: the interval in
+ * <emphasis>milliseconds</emphasis> between
+ * calls to @function.
+ * <sbr/>@data: arbitrary data to be passed to @function at each
+ * call.
+ * <sbr/>Returns: a handle for the timeout, which can be passed to
+ * @timeout_remove.
+ * @timeout_remove: Should remove a callback timer. Analogous to
+ * g_source_remove() in glib.
+ * <sbr/>See purple_timeout_remove().
+ * <sbr/>@handle: an identifier for a timeout, as returned by
+ * @timeout_add.
+ * <sbr/>Returns: %TRUE if the timeout identified by @handle
+ * was found and removed.
+ * @input_add: Should add an input handler. Analogous to g_io_add_watch_full()
+ * in glib.
+ * <sbr/>See purple_input_add().
+ * <sbr/>@fd: a file descriptor to watch for events
+ * <sbr/>@cond: a bitwise OR of events on @fd for which @func
+ * should be called.
+ * <sbr/>@func: a callback to fire whenever a relevant event on
+ * @fd occurs.
+ * <sbr/>@user_data: arbitrary data to pass to @fd.
+ * <sbr/>Returns: an identifier for this input handler, which can
+ * be passed to @input_remove.
+ * @input_remove: Should remove an input handler. Analogous to g_source_remove()
+ * in glib.
+ * <sbr/>See purple_input_remove().
+ * <sbr/>@handle: an identifier, as returned by #input_add.
+ * <sbr/>Returns: %TRUE if the input handler was found and
+ * removed.
+ * @input_get_error: If implemented, should get the current error status for an
+ * input.
+ * <sbr/>Implementation of this UI op is optional. Implement
+ * it if the UI's sockets or event loop needs to customize
+ * determination of socket error status. If unimplemented,
+ * <literal>getsockopt(2)</literal> will be used instead.
+ * <sbr/>See purple_input_get_error().
+ * @timeout_add_seconds: If implemented, should create a callback timer with an
+ * interval measured in seconds. Analogous to
+ * g_timeout_add_seconds() in glib.
+ * <sbr/>This allows UIs to group timers for better power
+ * efficiency. For this reason, @interval may be rounded
+ * by up to a second.
+ * <sbr/>Implementation of this UI op is optional. If it's
+ * not implemented, calls to purple_timeout_add_seconds()
+ * will be serviced by @timeout_add.
+ * <sbr/>See purple_timeout_add_seconds().
+ *
+ * An abstraction of an application's mainloop; libpurple will use this to
+ * watch file descriptors and schedule timed callbacks. If your application
+ * uses the glib mainloop, there is an implementation of this struct in
+ * <filename>libpurple/example/nullclient.c</filename> which you can use
+ * verbatim.
*/
struct _PurpleEventLoopUiOps
{
- /**
- * Should create a callback timer with an interval measured in
- * milliseconds. The supplied @a function should be called every @a
- * interval seconds until it returns @c FALSE, after which it should not
- * be called again.
- *
- * Analogous to g_timeout_add in glib.
- *
- * Note: On Win32, this function may be called from a thread other than
- * the libpurple thread. You should make sure to detect this situation
- * and to only call "function" from the libpurple thread.
- *
- * @param interval the interval in <em>milliseconds</em> between calls
- * to @a function.
- * @param data arbitrary data to be passed to @a function at each
- * call.
- * @todo Who is responsible for freeing @a data?
- *
- * @return a handle for the timeout, which can be passed to
- * #timeout_remove.
- *
- * @see purple_timeout_add
- **/
+ /* TODO Who is responsible for freeing @data? */
guint (*timeout_add)(guint interval, GSourceFunc function, gpointer data);
- /**
- * Should remove a callback timer. Analogous to g_source_remove in glib.
- * @param handle an identifier for a timeout, as returned by
- * #timeout_add.
- * @return @c TRUE if the timeout identified by @a handle was
- * found and removed.
- * @see purple_timeout_remove
- */
gboolean (*timeout_remove)(guint handle);
- /**
- * Should add an input handler. Analogous to g_io_add_watch_full in
- * glib.
- *
- * @param fd a file descriptor to watch for events
- * @param cond a bitwise OR of events on @a fd for which @a func
- * should be called.
- * @param func a callback to fire whenever a relevant event on @a
- * fd occurs.
- * @param user_data arbitrary data to pass to @a fd.
- * @return an identifier for this input handler, which can be
- * passed to #input_remove.
- *
- * @see purple_input_add
- */
guint (*input_add)(int fd, PurpleInputCondition cond,
PurpleInputFunction func, gpointer user_data);
- /**
- * Should remove an input handler. Analogous to g_source_remove in glib.
- * @param handle an identifier, as returned by #input_add.
- * @return @c TRUE if the input handler was found and removed.
- * @see purple_input_remove
- */
gboolean (*input_remove)(guint handle);
-
- /**
- * If implemented, should get the current error status for an input.
- *
- * Implementation of this UI op is optional. Implement it if the UI's
- * sockets or event loop needs to customize determination of socket
- * error status. If unimplemented, <tt>getsockopt(2)</tt> will be used
- * instead.
- *
- * @see purple_input_get_error
- */
int (*input_get_error)(int fd, int *error);
- /**
- * If implemented, should create a callback timer with an interval
- * measured in seconds. Analogous to g_timeout_add_seconds in glib.
- *
- * This allows UIs to group timers for better power efficiency. For
- * this reason, @a interval may be rounded by up to a second.
- *
- * Implementation of this UI op is optional. If it's not implemented,
- * calls to purple_timeout_add_seconds() will be serviced by
- * #timeout_add.
- *
- * @see purple_timeout_add_seconds()
- * @since 2.1.0
- **/
guint (*timeout_add_seconds)(guint interval, GSourceFunc function,
gpointer data);
+ /*< private >*/
+ void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
void (*_purple_reserved4)(void);
};
+G_BEGIN_DECLS
+
/**************************************************************************/
-/** @name Event Loop API */
+/* Event Loop API */
/**************************************************************************/
-/*@{*/
+
/**
+ * purple_timeout_add:
+ * @interval: The time between calls of the function, in milliseconds.
+ * @function: (scope call): The function to call.
+ * @data: data to pass to @function.
+ *
* Creates a callback timer.
*
- * The timer will repeat until the function returns @c FALSE. The
+ * The timer will repeat until the function returns %FALSE. The
* first call will be at the end of the first interval.
*
* If the timer is in a multiple of seconds, use purple_timeout_add_seconds()
* instead as it allows UIs to group timers for power efficiency.
*
- * @param interval The time between calls of the function, in
- * milliseconds.
- * @param function The function to call.
- * @param data data to pass to @a function.
- * @return A handle to the timer which can be passed to
+ * Returns: A handle to the timer which can be passed to
* purple_timeout_remove() to remove the timer.
*/
guint purple_timeout_add(guint interval, GSourceFunc function, gpointer data);
/**
+ * purple_timeout_add_seconds:
+ * @interval: The time between calls of the function, in seconds.
+ * @function: (scope call): The function to call.
+ * @data: data to pass to @function.
+ *
* Creates a callback timer.
*
- * The timer will repeat until the function returns @c FALSE. The
+ * The timer will repeat until the function returns %FALSE. The
* first call will be at the end of the first interval.
*
* This function allows UIs to group timers for better power efficiency. For
- * this reason, @a interval may be rounded by up to a second.
+ * this reason, @interval may be rounded by up to a second.
*
- * @param interval The time between calls of the function, in
- * seconds.
- * @param function The function to call.
- * @param data data to pass to @a function.
- * @return A handle to the timer which can be passed to
+ * Returns: A handle to the timer which can be passed to
* purple_timeout_remove() to remove the timer.
- *
- * @since 2.1.0
*/
guint purple_timeout_add_seconds(guint interval, GSourceFunc function, gpointer data);
/**
- * Removes a timeout handler.
+ * purple_timeout_remove:
+ * @handle: The handle, as returned by purple_timeout_add().
*
- * @param handle The handle, as returned by purple_timeout_add().
+ * Removes a timeout handler.
*
- * @return @c TRUE if the handler was successfully removed.
+ * Returns: %TRUE if the handler was successfully removed.
*/
gboolean purple_timeout_remove(guint handle);
/**
+ * purple_input_add:
+ * @fd: The input file descriptor.
+ * @cond: The condition type.
+ * @func: (scope call): The callback function for data.
+ * @user_data: User-specified data.
+ *
* Adds an input handler.
*
- * @param fd The input file descriptor.
- * @param cond The condition type.
- * @param func The callback function for data.
- * @param user_data User-specified data.
+ * See g_io_add_watch_full().
*
- * @return The resulting handle (will be greater than 0).
- * @see g_io_add_watch_full
+ * Returns: The resulting handle (will be greater than 0).
*/
guint purple_input_add(int fd, PurpleInputCondition cond,
PurpleInputFunction func, gpointer user_data);
/**
- * Removes an input handler.
+ * purple_input_remove:
+ * @handle: The handle of the input handler. Note that this is the return
+ * value from purple_input_add(), <emphasis>not</emphasis> the
+ * file descriptor.
*
- * @param handle The handle of the input handler. Note that this is the return
- * value from purple_input_add(), <i>not</i> the file descriptor.
+ * Removes an input handler.
*/
gboolean purple_input_remove(guint handle);
/**
+ * purple_input_get_error:
+ * @fd: The input file descriptor.
+ * @error: A pointer to an #int which on return will have the error, or
+ * 0 if no error.
+ *
* Get the current error status for an input.
*
* The return value and error follow getsockopt() with a level of SOL_SOCKET and an
* option name of SO_ERROR, and this is how the error is determined if the UI does not
* implement the input_get_error UI op.
*
- * @param fd The input file descriptor.
- * @param error A pointer to an @c int which on return will have the error, or
- * @c 0 if no error.
- *
- * @return @c 0 if there is no error; @c -1 if there is an error, in which case
- * @a errno will be set.
+ * Returns: 0 if there is no error; -1 if there is an error, in which case
+ * #errno will be set.
*/
int
purple_input_get_error(int fd, int *error);
+/**
+ * purple_input_pipe:
+ * @pipefd: Array used to return file descriptors for both ends of pipe.
+ *
+ * Creates a pipe - an unidirectional data channel that can be used for
+ * interprocess communication.
+ *
+ * File descriptors for both ends of pipe will be written into provided array.
+ * The first one (pipefd[0]) can be used for reading, the second one (pipefd[1])
+ * for writing.
+ *
+ * On Windows it's simulated by creating a pair of connected sockets, on other
+ * systems pipe() is used.
+ *
+ * Returns: 0 on success, -1 on error.
+ */
+int
+purple_input_pipe(int pipefd[2]);
-/*@}*/
/**************************************************************************/
-/** @name UI Registration Functions */
+/* UI Registration Functions */
/**************************************************************************/
-/*@{*/
+
/**
- * Sets the UI operations structure to be used for accounts.
+ * purple_eventloop_ui_ops_get_type:
+ *
+ * Returns: The #GType for the #PurpleEventLoopUiOps boxed structure.
+ */
+GType purple_eventloop_ui_ops_get_type(void);
+
+/**
+ * purple_eventloop_set_ui_ops:
+ * @ops: The UI operations structure.
*
- * @param ops The UI operations structure.
+ * Sets the UI operations structure to be used for accounts.
*/
void purple_eventloop_set_ui_ops(PurpleEventLoopUiOps *ops);
/**
+ * purple_eventloop_get_ui_ops:
+ *
* Returns the UI operations structure used for accounts.
*
- * @return The UI operations structure in use.
+ * Returns: The UI operations structure in use.
*/
PurpleEventLoopUiOps *purple_eventloop_get_ui_ops(void);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_EVENTLOOP_H_ */
diff --git a/libpurple/example/Makefile.am b/libpurple/example/Makefile.am
index 583420917e..d7af565490 100644
--- a/libpurple/example/Makefile.am
+++ b/libpurple/example/Makefile.am
@@ -12,10 +12,6 @@ nullclient_LDADD = \
AM_CPPFLAGS = \
-DSTANDALONE \
- -DDATADIR=\"$(datadir)\" \
- -DLIBDIR=\"$(libdir)/purple-$(PURPLE_MAJOR_VERSION)/\" \
- -DLOCALEDIR=\"$(datadir)/locale\" \
- -DSYSCONFDIR=\"$(sysconfdir)\" \
-I$(top_builddir)/libpurple \
-I$(top_srcdir)/libpurple \
-I$(top_srcdir) \
diff --git a/libpurple/example/defines.h b/libpurple/example/defines.h
index 191ac30ccb..5a2613137e 100644
--- a/libpurple/example/defines.h
+++ b/libpurple/example/defines.h
@@ -1,3 +1,26 @@
+/*
+ * 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
+ *
+ */
+
#define CUSTOM_USER_DIRECTORY "/dev/null"
#define CUSTOM_PLUGIN_PATH ""
#define PLUGIN_SAVE_PREF "/purple/nullclient/plugins/saved"
diff --git a/libpurple/example/nullclient.c b/libpurple/example/nullclient.c
index 73548e9910..a2d420d02f 100644
--- a/libpurple/example/nullclient.c
+++ b/libpurple/example/nullclient.c
@@ -24,13 +24,15 @@
#include "purple.h"
#include <glib.h>
+#include <glib/gprintf.h>
#include <signal.h>
#include <string.h>
-#ifndef _WIN32
-#include <unistd.h>
+#ifdef _WIN32
+# include <conio.h>
+# include "win32/win32dep.h"
#else
-#include "win32/win32dep.h"
+# include <unistd.h>
#endif
#include "defines.h"
@@ -64,13 +66,13 @@ static gboolean purple_glib_io_invoke(GIOChannel *source, GIOCondition condition
purple_cond |= PURPLE_INPUT_WRITE;
closure->function(closure->data, g_io_channel_unix_get_fd(source),
- purple_cond);
+ purple_cond);
return TRUE;
}
-static guint glib_input_add(gint fd, PurpleInputCondition condition, PurpleInputFunction function,
- gpointer data)
+static guint glib_input_add(gint fd, PurpleInputCondition condition,
+ PurpleInputFunction function, gpointer data)
{
PurpleGLibIOClosure *closure = g_new0(PurpleGLibIOClosure, 1);
GIOChannel *channel;
@@ -89,8 +91,8 @@ static guint glib_input_add(gint fd, PurpleInputCondition condition, PurpleInput
#else
channel = g_io_channel_unix_new(fd);
#endif
- closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond,
- purple_glib_io_invoke, closure, purple_glib_io_destroy);
+ closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT,
+ cond, purple_glib_io_invoke, closure, purple_glib_io_destroy);
g_io_channel_unref(channel);
return closure->result;
@@ -103,35 +105,27 @@ static PurpleEventLoopUiOps glib_eventloops =
glib_input_add,
g_source_remove,
NULL,
-#if GLIB_CHECK_VERSION(2,14,0)
g_timeout_add_seconds,
-#else
- NULL,
-#endif
/* padding */
NULL,
NULL,
+ NULL,
NULL
};
/*** End of the eventloop functions. ***/
/*** Conversation uiops ***/
static void
-null_write_conv(PurpleConversation *conv, const char *who, const char *alias,
- const char *message, PurpleMessageFlags flags, time_t mtime)
+null_write_conv(PurpleConversation *conv, PurpleMessage *msg)
{
- const char *name;
- if (alias && *alias)
- name = alias;
- else if (who && *who)
- name = who;
- else
- name = NULL;
-
- printf("(%s) %s %s: %s\n", purple_conversation_get_name(conv),
- purple_utf8_strftime("(%H:%M:%S)", localtime(&mtime)),
- name, message);
+ time_t mtime = purple_message_get_time(msg);
+
+ printf("(%s) %s %s: %s\n",
+ purple_conversation_get_name(conv),
+ purple_utf8_strftime("(%H:%M:%S)", localtime(&mtime)),
+ purple_message_get_author_alias(msg),
+ purple_message_get_contents(msg));
}
static PurpleConversationUiOps null_conv_uiops =
@@ -147,9 +141,6 @@ static PurpleConversationUiOps null_conv_uiops =
NULL, /* chat_update_user */
NULL, /* present */
NULL, /* has_focus */
- NULL, /* custom_smiley_add */
- NULL, /* custom_smiley_write */
- NULL, /* custom_smiley_close */
NULL, /* send_confirm */
NULL,
NULL,
@@ -178,6 +169,7 @@ static PurpleCoreUiOps null_core_uiops =
NULL,
NULL,
NULL,
+ NULL,
NULL
};
@@ -218,26 +210,19 @@ init_libpurple(void)
abort();
}
- /* Create and load the buddylist. */
- purple_set_blist(purple_blist_new());
- purple_blist_load();
-
/* Load the preferences. */
purple_prefs_load();
/* Load the desired plugins. The client should save the list of loaded plugins in
* the preferences using purple_plugins_save_loaded(PLUGIN_SAVE_PREF) */
purple_plugins_load_saved(PLUGIN_SAVE_PREF);
-
- /* Load the pounces. */
- purple_pounces_load();
}
static void
signed_on(PurpleConnection *gc, gpointer null)
{
PurpleAccount *account = purple_connection_get_account(gc);
- printf("Account connected: %s %s\n", account->username, account->protocol_id);
+ printf("Account connected: %s %s\n", purple_account_get_username(account), purple_account_get_protocol_id(account));
}
static void
@@ -248,6 +233,36 @@ connect_to_signals_for_demonstration_purposes_only(void)
PURPLE_CALLBACK(signed_on), NULL);
}
+#if defined(_WIN32) || defined(__BIONIC__)
+#ifndef PASS_MAX
+# define PASS_MAX 1024
+#endif
+static gchar *
+getpass(const gchar *prompt)
+{
+ static gchar buff[PASS_MAX + 1];
+ guint i = 0;
+
+ g_fprintf(stderr, "%s", prompt);
+ fflush(stderr);
+
+ while (i < sizeof(buff) - 1) {
+#ifdef __BIONIC__
+ buff[i] = getc(stdin);
+#else
+ buff[i] = _getch();
+#endif
+ if (buff[i] == '\r' || buff[i] == '\n')
+ break;
+ i++;
+ }
+ buff[i] = '\0';
+ g_fprintf(stderr, "\n");
+
+ return buff;
+}
+#endif /* _WIN32 || __BIONIC__ */
+
int main(int argc, char *argv[])
{
GList *iter;
@@ -280,7 +295,7 @@ int main(int argc, char *argv[])
PurplePluginInfo *info = plugin->info;
if (info && info->name) {
printf("\t%d: %s\n", i++, info->name);
- names = g_list_append(names, info->id);
+ names = g_list_append(names, (gpointer)info->id);
}
}
printf("Select the protocol [0-%d]: ", i-1);
@@ -309,7 +324,7 @@ int main(int argc, char *argv[])
/* Get the password for the account */
password = getpass("Password: ");
- purple_account_set_password(account, password);
+ purple_account_set_password(account, password, NULL, NULL);
/* It's necessary to enable the account first. */
purple_account_set_enabled(account, UI_ID, TRUE);
@@ -324,4 +339,3 @@ int main(int argc, char *argv[])
return 0;
}
-
diff --git a/libpurple/ft.c b/libpurple/ft.c
deleted file mode 100644
index 347c54dfce..0000000000
--- a/libpurple/ft.c
+++ /dev/null
@@ -1,1787 +0,0 @@
-/**
- * @file ft.c File Transfer API
- */
-
-/* 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 "dbus-maybe.h"
-#include "ft.h"
-#include "network.h"
-#include "notify.h"
-#include "prefs.h"
-#include "proxy.h"
-#include "request.h"
-#include "util.h"
-#include "debug.h"
-
-#define FT_INITIAL_BUFFER_SIZE 4096
-#define FT_MAX_BUFFER_SIZE 65535
-
-static PurpleXferUiOps *xfer_ui_ops = NULL;
-static GList *xfers;
-
-/*
- * A hack to store more data since we can't extend the size of PurpleXfer
- * easily.
- */
-static GHashTable *xfers_data = NULL;
-
-typedef struct _PurpleXferPrivData {
- /*
- * 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/prpl call the respective
- * function, which is somewhat akin to a fd watch being triggered.
- */
- enum {
- PURPLE_XFER_READY_NONE = 0x0,
- PURPLE_XFER_READY_UI = 0x1,
- PURPLE_XFER_READY_PRPL = 0x2,
- } ready;
-
- /* TODO: Should really use a PurpleCircBuffer for this. */
- GByteArray *buffer;
-
- gpointer thumbnail_data; /**< thumbnail image */
- gsize thumbnail_size;
- gchar *thumbnail_mimetype;
-} PurpleXferPrivData;
-
-static int purple_xfer_choose_file(PurpleXfer *xfer);
-
-static void
-purple_xfer_priv_data_destroy(gpointer data)
-{
- PurpleXferPrivData *priv = data;
-
- if (priv->buffer)
- g_byte_array_free(priv->buffer, TRUE);
-
- g_free(priv->thumbnail_data);
-
- g_free(priv->thumbnail_mimetype);
-
- g_free(priv);
-}
-
-static const gchar *
-purple_xfer_status_type_to_string(PurpleXferStatusType type)
-{
- static const struct {
- PurpleXferStatusType type;
- const char *name;
- } type_names[] = {
- { PURPLE_XFER_STATUS_UNKNOWN, "unknown" },
- { PURPLE_XFER_STATUS_NOT_STARTED, "not started" },
- { PURPLE_XFER_STATUS_ACCEPTED, "accepted" },
- { PURPLE_XFER_STATUS_STARTED, "started" },
- { PURPLE_XFER_STATUS_DONE, "done" },
- { PURPLE_XFER_STATUS_CANCEL_LOCAL, "cancelled locally" },
- { PURPLE_XFER_STATUS_CANCEL_REMOTE, "cancelled remotely" }
- };
- int i;
-
- for (i = 0; i < G_N_ELEMENTS(type_names); ++i)
- if (type_names[i].type == type)
- return type_names[i].name;
-
- return "invalid state";
-}
-
-GList *
-purple_xfers_get_all()
-{
- return xfers;
-}
-
-PurpleXfer *
-purple_xfer_new(PurpleAccount *account, PurpleXferType type, const char *who)
-{
- PurpleXfer *xfer;
- PurpleXferUiOps *ui_ops;
- PurpleXferPrivData *priv;
-
- g_return_val_if_fail(type != PURPLE_XFER_UNKNOWN, NULL);
- g_return_val_if_fail(account != NULL, NULL);
- g_return_val_if_fail(who != NULL, NULL);
-
- xfer = g_new0(PurpleXfer, 1);
- PURPLE_DBUS_REGISTER_POINTER(xfer, PurpleXfer);
-
- xfer->ref = 1;
- xfer->type = type;
- xfer->account = account;
- xfer->who = g_strdup(who);
- xfer->ui_ops = ui_ops = purple_xfers_get_ui_ops();
- xfer->message = NULL;
- xfer->current_buffer_size = FT_INITIAL_BUFFER_SIZE;
- xfer->fd = -1;
-
- priv = g_new0(PurpleXferPrivData, 1);
- priv->ready = PURPLE_XFER_READY_NONE;
-
- if (ui_ops && ui_ops->data_not_sent) {
- /* If the ui will handle unsent data no need for buffer */
- priv->buffer = NULL;
- } else {
- priv->buffer = g_byte_array_sized_new(FT_INITIAL_BUFFER_SIZE);
- }
-
- g_hash_table_insert(xfers_data, xfer, priv);
-
- ui_ops = purple_xfer_get_ui_ops(xfer);
-
- if (ui_ops != NULL && ui_ops->new_xfer != NULL)
- ui_ops->new_xfer(xfer);
-
- xfers = g_list_prepend(xfers, xfer);
-
- if (purple_debug_is_verbose())
- purple_debug_info("xfer", "new %p [%d]\n", xfer, xfer->ref);
-
- return xfer;
-}
-
-static void
-purple_xfer_destroy(PurpleXfer *xfer)
-{
- PurpleXferUiOps *ui_ops;
-
- g_return_if_fail(xfer != NULL);
-
- if (purple_debug_is_verbose())
- purple_debug_info("xfer", "destroyed %p [%d]\n", xfer, xfer->ref);
-
- /* Close the file browser, if it's open */
- purple_request_close_with_handle(xfer);
-
- 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)
- ui_ops->destroy(xfer);
-
- g_free(xfer->who);
- g_free(xfer->filename);
- g_free(xfer->remote_ip);
- g_free(xfer->local_filename);
-
- g_hash_table_remove(xfers_data, xfer);
-
- PURPLE_DBUS_UNREGISTER_POINTER(xfer);
- xfers = g_list_remove(xfers, xfer);
- g_free(xfer);
-}
-
-void
-purple_xfer_ref(PurpleXfer *xfer)
-{
- g_return_if_fail(xfer != NULL);
-
- xfer->ref++;
-
- if (purple_debug_is_verbose())
- purple_debug_info("xfer", "ref'd %p [%d]\n", xfer, xfer->ref);
-}
-
-void
-purple_xfer_unref(PurpleXfer *xfer)
-{
- g_return_if_fail(xfer != NULL);
- g_return_if_fail(xfer->ref > 0);
-
- xfer->ref--;
-
- if (purple_debug_is_verbose())
- purple_debug_info("xfer", "unref'd %p [%d]\n", xfer, xfer->ref);
-
- if (xfer->ref == 0)
- purple_xfer_destroy(xfer);
-}
-
-static void
-purple_xfer_set_status(PurpleXfer *xfer, PurpleXferStatusType status)
-{
- g_return_if_fail(xfer != NULL);
-
- if (purple_debug_is_verbose())
- purple_debug_info("xfer", "Changing status of xfer %p from %s to %s\n",
- xfer, purple_xfer_status_type_to_string(xfer->status),
- purple_xfer_status_type_to_string(status));
-
- if (xfer->status == status)
- return;
-
- xfer->status = status;
-
- if(xfer->type == PURPLE_XFER_SEND) {
- switch(status) {
- case PURPLE_XFER_STATUS_ACCEPTED:
- purple_signal_emit(purple_xfers_get_handle(), "file-send-accept", xfer);
- break;
- case PURPLE_XFER_STATUS_STARTED:
- purple_signal_emit(purple_xfers_get_handle(), "file-send-start", xfer);
- break;
- case PURPLE_XFER_STATUS_DONE:
- purple_signal_emit(purple_xfers_get_handle(), "file-send-complete", xfer);
- break;
- case PURPLE_XFER_STATUS_CANCEL_LOCAL:
- case PURPLE_XFER_STATUS_CANCEL_REMOTE:
- purple_signal_emit(purple_xfers_get_handle(), "file-send-cancel", xfer);
- break;
- default:
- break;
- }
- } else if(xfer->type == PURPLE_XFER_RECEIVE) {
- switch(status) {
- case PURPLE_XFER_STATUS_ACCEPTED:
- purple_signal_emit(purple_xfers_get_handle(), "file-recv-accept", xfer);
- break;
- case PURPLE_XFER_STATUS_STARTED:
- purple_signal_emit(purple_xfers_get_handle(), "file-recv-start", xfer);
- break;
- case PURPLE_XFER_STATUS_DONE:
- purple_signal_emit(purple_xfers_get_handle(), "file-recv-complete", xfer);
- break;
- case PURPLE_XFER_STATUS_CANCEL_LOCAL:
- case PURPLE_XFER_STATUS_CANCEL_REMOTE:
- purple_signal_emit(purple_xfers_get_handle(), "file-recv-cancel", xfer);
- break;
- default:
- break;
- }
- }
-}
-
-static void
-purple_xfer_conversation_write_internal(PurpleXfer *xfer,
- const char *message, gboolean is_error, gboolean print_thumbnail)
-{
- PurpleConversation *conv = NULL;
- PurpleMessageFlags flags = PURPLE_MESSAGE_SYSTEM;
- char *escaped;
- gconstpointer thumbnail_data;
- gsize size;
-
- g_return_if_fail(xfer != NULL);
- g_return_if_fail(message != NULL);
-
- thumbnail_data = purple_xfer_get_thumbnail(xfer, &size);
-
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, xfer->who,
- purple_xfer_get_account(xfer));
-
- if (conv == NULL)
- return;
-
- escaped = g_markup_escape_text(message, -1);
-
- if (is_error)
- flags |= PURPLE_MESSAGE_ERROR;
-
- if (print_thumbnail && thumbnail_data) {
- gchar *message_with_img;
- gpointer data = g_memdup(thumbnail_data, size);
- int id = purple_imgstore_add_with_id(data, size, NULL);
-
- message_with_img =
- g_strdup_printf("<img id='%d'> %s", id, escaped);
- purple_conversation_write(conv, NULL, message_with_img, flags,
- time(NULL));
- purple_imgstore_unref_by_id(id);
- g_free(message_with_img);
- } else {
- purple_conversation_write(conv, NULL, escaped, flags, time(NULL));
- }
- g_free(escaped);
-}
-
-void
-purple_xfer_conversation_write(PurpleXfer *xfer, gchar *message,
- gboolean is_error)
-{
- purple_xfer_conversation_write_internal(xfer, message, is_error, FALSE);
-}
-
-/* maybe this one should be exported publically? */
-static void
-purple_xfer_conversation_write_with_thumbnail(PurpleXfer *xfer,
- const gchar *message)
-{
- purple_xfer_conversation_write_internal(xfer, message, FALSE, TRUE);
-}
-
-
-static void purple_xfer_show_file_error(PurpleXfer *xfer, const char *filename)
-{
- int err = errno;
- gchar *msg = NULL, *utf8;
- PurpleXferType xfer_type = purple_xfer_get_type(xfer);
- PurpleAccount *account = purple_xfer_get_account(xfer);
-
- utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
- switch(xfer_type) {
- case PURPLE_XFER_SEND:
- msg = g_strdup_printf(_("Error reading %s: \n%s.\n"),
- utf8, g_strerror(err));
- break;
- case PURPLE_XFER_RECEIVE:
- msg = g_strdup_printf(_("Error writing %s: \n%s.\n"),
- utf8, g_strerror(err));
- break;
- default:
- msg = g_strdup_printf(_("Error accessing %s: \n%s.\n"),
- utf8, g_strerror(err));
- break;
- }
- g_free(utf8);
-
- purple_xfer_conversation_write(xfer, msg, TRUE);
- purple_xfer_error(xfer_type, account, xfer->who, msg);
- g_free(msg);
-}
-
-static void
-purple_xfer_choose_file_ok_cb(void *user_data, const char *filename)
-{
- PurpleXfer *xfer;
- PurpleXferType type;
- struct stat st;
- gchar *dir;
-
- xfer = (PurpleXfer *)user_data;
- type = purple_xfer_get_type(xfer);
-
- if (g_stat(filename, &st) != 0) {
- /* File not found. */
- if (type == PURPLE_XFER_RECEIVE) {
-#ifndef _WIN32
- int mode = W_OK;
-#else
- int mode = F_OK;
-#endif
- dir = g_path_get_dirname(filename);
-
- if (g_access(dir, mode) == 0) {
- purple_xfer_request_accepted(xfer, filename);
- } else {
- purple_xfer_ref(xfer);
- purple_notify_message(
- NULL, PURPLE_NOTIFY_MSG_ERROR, NULL,
- _("Directory is not writable."), NULL,
- (PurpleNotifyCloseCallback)purple_xfer_choose_file, xfer);
- }
-
- g_free(dir);
- }
- else {
- purple_xfer_show_file_error(xfer, filename);
- purple_xfer_cancel_local(xfer);
- }
- }
- else if ((type == PURPLE_XFER_SEND) && (st.st_size == 0)) {
-
- purple_notify_error(NULL, NULL,
- _("Cannot send a file of 0 bytes."), NULL);
-
- purple_xfer_cancel_local(xfer);
- }
- else if ((type == PURPLE_XFER_SEND) && S_ISDIR(st.st_mode)) {
- /*
- * XXX - Sending a directory should be valid for some protocols.
- */
- purple_notify_error(NULL, NULL,
- _("Cannot send a directory."), NULL);
-
- purple_xfer_cancel_local(xfer);
- }
- else if ((type == PURPLE_XFER_RECEIVE) && S_ISDIR(st.st_mode)) {
- char *msg, *utf8;
- utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
- msg = g_strdup_printf(
- _("%s is not a regular file. Cowardly refusing to overwrite it.\n"), utf8);
- g_free(utf8);
- purple_notify_error(NULL, NULL, msg, NULL);
- g_free(msg);
- purple_xfer_request_denied(xfer);
- }
- else if (type == PURPLE_XFER_SEND) {
-#ifndef _WIN32
- int mode = R_OK;
-#else
- int mode = F_OK;
-#endif
-
- if (g_access(filename, mode) == 0) {
- purple_xfer_request_accepted(xfer, filename);
- } else {
- purple_xfer_ref(xfer);
- purple_notify_message(
- NULL, PURPLE_NOTIFY_MSG_ERROR, NULL,
- _("File is not readable."), NULL,
- (PurpleNotifyCloseCallback)purple_xfer_choose_file, xfer);
- }
- }
- else {
- purple_xfer_request_accepted(xfer, filename);
- }
-
- purple_xfer_unref(xfer);
-}
-
-static void
-purple_xfer_choose_file_cancel_cb(void *user_data, const char *filename)
-{
- PurpleXfer *xfer = (PurpleXfer *)user_data;
-
- purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL);
- if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND)
- purple_xfer_cancel_local(xfer);
- else
- purple_xfer_request_denied(xfer);
- purple_xfer_unref(xfer);
-}
-
-static int
-purple_xfer_choose_file(PurpleXfer *xfer)
-{
- purple_request_file(xfer, NULL, purple_xfer_get_filename(xfer),
- (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE),
- G_CALLBACK(purple_xfer_choose_file_ok_cb),
- G_CALLBACK(purple_xfer_choose_file_cancel_cb),
- purple_xfer_get_account(xfer), xfer->who, NULL,
- xfer);
-
- return 0;
-}
-
-static int
-cancel_recv_cb(PurpleXfer *xfer)
-{
- purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL);
- purple_xfer_request_denied(xfer);
- purple_xfer_unref(xfer);
-
- return 0;
-}
-
-static void
-purple_xfer_ask_recv(PurpleXfer *xfer)
-{
- char *buf, *size_buf;
- size_t size;
- gconstpointer thumb;
- gsize thumb_size;
-
- /* If we have already accepted the request, ask the destination file
- name directly */
- if (purple_xfer_get_status(xfer) != PURPLE_XFER_STATUS_ACCEPTED) {
- PurpleBuddy *buddy = purple_find_buddy(xfer->account, xfer->who);
-
- if (purple_xfer_get_filename(xfer) != NULL)
- {
- size = purple_xfer_get_size(xfer);
- size_buf = purple_str_size_to_units(size);
- buf = g_strdup_printf(_("%s wants to send you %s (%s)"),
- buddy ? purple_buddy_get_alias(buddy) : xfer->who,
- purple_xfer_get_filename(xfer), size_buf);
- g_free(size_buf);
- }
- else
- {
- buf = g_strdup_printf(_("%s wants to send you a file"),
- buddy ? purple_buddy_get_alias(buddy) : xfer->who);
- }
-
- if (xfer->message != NULL)
- serv_got_im(purple_account_get_connection(xfer->account),
- xfer->who, xfer->message, 0, time(NULL));
-
- if ((thumb = purple_xfer_get_thumbnail(xfer, &thumb_size))) {
- purple_request_accept_cancel_with_icon(xfer, NULL, buf, NULL,
- PURPLE_DEFAULT_ACTION_NONE, xfer->account, xfer->who, NULL,
- thumb, thumb_size, xfer,
- G_CALLBACK(purple_xfer_choose_file),
- G_CALLBACK(cancel_recv_cb));
- } else {
- purple_request_accept_cancel(xfer, NULL, buf, NULL,
- PURPLE_DEFAULT_ACTION_NONE, xfer->account, xfer->who, NULL,
- xfer, G_CALLBACK(purple_xfer_choose_file),
- G_CALLBACK(cancel_recv_cb));
- }
-
- g_free(buf);
- } else
- purple_xfer_choose_file(xfer);
-}
-
-static int
-ask_accept_ok(PurpleXfer *xfer)
-{
- purple_xfer_request_accepted(xfer, NULL);
-
- return 0;
-}
-
-static int
-ask_accept_cancel(PurpleXfer *xfer)
-{
- purple_xfer_request_denied(xfer);
- purple_xfer_unref(xfer);
-
- return 0;
-}
-
-static void
-purple_xfer_ask_accept(PurpleXfer *xfer)
-{
- char *buf, *buf2 = NULL;
- PurpleBuddy *buddy = purple_find_buddy(xfer->account, xfer->who);
-
- buf = g_strdup_printf(_("Accept file transfer request from %s?"),
- buddy ? purple_buddy_get_alias(buddy) : xfer->who);
- if (purple_xfer_get_remote_ip(xfer) &&
- purple_xfer_get_remote_port(xfer))
- buf2 = g_strdup_printf(_("A file is available for download from:\n"
- "Remote host: %s\nRemote port: %d"),
- purple_xfer_get_remote_ip(xfer),
- purple_xfer_get_remote_port(xfer));
- purple_request_accept_cancel(xfer, NULL, buf, buf2,
- PURPLE_DEFAULT_ACTION_NONE,
- xfer->account, xfer->who, NULL,
- xfer,
- G_CALLBACK(ask_accept_ok),
- G_CALLBACK(ask_accept_cancel));
- g_free(buf);
- g_free(buf2);
-}
-
-void
-purple_xfer_request(PurpleXfer *xfer)
-{
- g_return_if_fail(xfer != NULL);
- g_return_if_fail(xfer->ops.init != NULL);
-
- purple_xfer_ref(xfer);
-
- if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE)
- {
- purple_signal_emit(purple_xfers_get_handle(), "file-recv-request", xfer);
- if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL)
- {
- /* 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)
- {
- gchar* message = NULL;
- PurpleBuddy *buddy = purple_find_buddy(xfer->account, xfer->who);
-
- message = g_strdup_printf(_("%s is offering to send file %s"),
- buddy ? purple_buddy_get_alias(buddy) : xfer->who, purple_xfer_get_filename(xfer));
- purple_xfer_conversation_write_with_thumbnail(xfer, message);
- g_free(message);
-
- /* Ask for a filename to save to if it's not already given by a plugin */
- if (xfer->local_filename == NULL)
- purple_xfer_ask_recv(xfer);
- }
- else
- {
- purple_xfer_ask_accept(xfer);
- }
- }
- else
- {
- purple_xfer_choose_file(xfer);
- }
-}
-
-void
-purple_xfer_request_accepted(PurpleXfer *xfer, const char *filename)
-{
- PurpleXferType type;
- struct stat st;
- char *msg, *utf8, *base;
- PurpleAccount *account;
- PurpleBuddy *buddy;
-
- if (xfer == NULL)
- return;
-
- type = purple_xfer_get_type(xfer);
- account = purple_xfer_get_account(xfer);
-
- purple_debug_misc("xfer", "request accepted for %p\n", xfer);
-
- if (!filename && type == PURPLE_XFER_RECEIVE) {
- xfer->status = PURPLE_XFER_STATUS_ACCEPTED;
- xfer->ops.init(xfer);
- return;
- } else {
- g_return_if_fail(filename != NULL);
- }
-
- buddy = purple_find_buddy(account, xfer->who);
-
- if (type == PURPLE_XFER_SEND) {
- /* Sending a file */
- /* Check the filename. */
- PurpleXferUiOps *ui_ops;
- ui_ops = purple_xfer_get_ui_ops(xfer);
-
-#ifdef _WIN32
- if (g_strrstr(filename, "../") || g_strrstr(filename, "..\\"))
-#else
- if (g_strrstr(filename, "../"))
-#endif
- {
- 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, xfer->who, msg);
- g_free(utf8);
- g_free(msg);
-
- purple_xfer_unref(xfer);
- return;
- }
-
- if (ui_ops == NULL || (ui_ops->ui_read == NULL && ui_ops->ui_write == NULL)) {
- if (g_stat(filename, &st) == -1) {
- purple_xfer_show_file_error(xfer, filename);
- purple_xfer_unref(xfer);
- return;
- }
-
- purple_xfer_set_local_filename(xfer, filename);
- purple_xfer_set_size(xfer, st.st_size);
- } else {
- purple_xfer_set_local_filename(xfer, filename);
- }
-
- base = g_path_get_basename(filename);
- utf8 = g_filename_to_utf8(base, -1, NULL, NULL, NULL);
- g_free(base);
- purple_xfer_set_filename(xfer, utf8);
-
- msg = g_strdup_printf(_("Offering to send %s to %s"),
- utf8, buddy ? purple_buddy_get_alias(buddy) : xfer->who);
- g_free(utf8);
- purple_xfer_conversation_write(xfer, msg, FALSE);
- g_free(msg);
- }
- else {
- /* Receiving a file */
- xfer->status = PURPLE_XFER_STATUS_ACCEPTED;
- purple_xfer_set_local_filename(xfer, filename);
-
- msg = g_strdup_printf(_("Starting transfer of %s from %s"),
- xfer->filename, buddy ? purple_buddy_get_alias(buddy) : xfer->who);
- purple_xfer_conversation_write(xfer, msg, FALSE);
- g_free(msg);
- }
-
- purple_xfer_add(xfer);
- xfer->ops.init(xfer);
-
-}
-
-void
-purple_xfer_request_denied(PurpleXfer *xfer)
-{
- g_return_if_fail(xfer != NULL);
-
- purple_debug_misc("xfer", "xfer %p denied\n", xfer);
-
- if (xfer->ops.request_denied != NULL)
- xfer->ops.request_denied(xfer);
-
- purple_xfer_unref(xfer);
-}
-
-PurpleXferType
-purple_xfer_get_type(const PurpleXfer *xfer)
-{
- g_return_val_if_fail(xfer != NULL, PURPLE_XFER_UNKNOWN);
-
- return xfer->type;
-}
-
-PurpleAccount *
-purple_xfer_get_account(const PurpleXfer *xfer)
-{
- g_return_val_if_fail(xfer != NULL, NULL);
-
- return xfer->account;
-}
-
-const char *
-purple_xfer_get_remote_user(const PurpleXfer *xfer)
-{
- g_return_val_if_fail(xfer != NULL, NULL);
- return xfer->who;
-}
-
-PurpleXferStatusType
-purple_xfer_get_status(const PurpleXfer *xfer)
-{
- g_return_val_if_fail(xfer != NULL, PURPLE_XFER_STATUS_UNKNOWN);
-
- return xfer->status;
-}
-
-/* FIXME: Rename with cancelled for 3.0.0. */
-gboolean
-purple_xfer_is_canceled(const PurpleXfer *xfer)
-{
- g_return_val_if_fail(xfer != NULL, TRUE);
-
- if ((purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) ||
- (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_REMOTE))
- return TRUE;
- else
- return FALSE;
-}
-
-gboolean
-purple_xfer_is_completed(const PurpleXfer *xfer)
-{
- g_return_val_if_fail(xfer != NULL, TRUE);
-
- return (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_DONE);
-}
-
-const char *
-purple_xfer_get_filename(const PurpleXfer *xfer)
-{
- g_return_val_if_fail(xfer != NULL, NULL);
-
- return xfer->filename;
-}
-
-const char *
-purple_xfer_get_local_filename(const PurpleXfer *xfer)
-{
- g_return_val_if_fail(xfer != NULL, NULL);
-
- return xfer->local_filename;
-}
-
-size_t
-purple_xfer_get_bytes_sent(const PurpleXfer *xfer)
-{
- g_return_val_if_fail(xfer != NULL, 0);
-
- return xfer->bytes_sent;
-}
-
-size_t
-purple_xfer_get_bytes_remaining(const PurpleXfer *xfer)
-{
- g_return_val_if_fail(xfer != NULL, 0);
-
- return xfer->bytes_remaining;
-}
-
-size_t
-purple_xfer_get_size(const PurpleXfer *xfer)
-{
- g_return_val_if_fail(xfer != NULL, 0);
-
- return xfer->size;
-}
-
-double
-purple_xfer_get_progress(const PurpleXfer *xfer)
-{
- g_return_val_if_fail(xfer != NULL, 0.0);
-
- if (purple_xfer_get_size(xfer) == 0)
- return 0.0;
-
- return ((double)purple_xfer_get_bytes_sent(xfer) /
- (double)purple_xfer_get_size(xfer));
-}
-
-unsigned int
-purple_xfer_get_local_port(const PurpleXfer *xfer)
-{
- g_return_val_if_fail(xfer != NULL, -1);
-
- return xfer->local_port;
-}
-
-const char *
-purple_xfer_get_remote_ip(const PurpleXfer *xfer)
-{
- g_return_val_if_fail(xfer != NULL, NULL);
-
- return xfer->remote_ip;
-}
-
-unsigned int
-purple_xfer_get_remote_port(const PurpleXfer *xfer)
-{
- g_return_val_if_fail(xfer != NULL, -1);
-
- return xfer->remote_port;
-}
-
-time_t
-purple_xfer_get_start_time(const PurpleXfer *xfer)
-{
- g_return_val_if_fail(xfer != NULL, 0);
-
- return xfer->start_time;
-}
-
-time_t
-purple_xfer_get_end_time(const PurpleXfer *xfer)
-{
- g_return_val_if_fail(xfer != NULL, 0);
-
- return xfer->end_time;
-}
-
-void
-purple_xfer_set_completed(PurpleXfer *xfer, gboolean completed)
-{
- PurpleXferUiOps *ui_ops;
-
- g_return_if_fail(xfer != NULL);
-
- if (completed == TRUE) {
- char *msg = NULL;
- PurpleConversation *conv;
-
- purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_DONE);
-
- if (purple_xfer_get_filename(xfer) != NULL)
- {
- char *filename = g_markup_escape_text(purple_xfer_get_filename(xfer), -1);
- if (purple_xfer_get_local_filename(xfer)
- && purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE)
- {
- char *local = g_markup_escape_text(purple_xfer_get_local_filename(xfer), -1);
- msg = g_strdup_printf(_("Transfer of file <A HREF=\"file://%s\">%s</A> complete"),
- local, filename);
- g_free(local);
- }
- else
- msg = g_strdup_printf(_("Transfer of file %s complete"),
- filename);
- g_free(filename);
- }
- else
- msg = g_strdup(_("File transfer complete"));
-
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, xfer->who,
- purple_xfer_get_account(xfer));
-
- if (conv != NULL)
- purple_conversation_write(conv, NULL, msg, PURPLE_MESSAGE_SYSTEM, time(NULL));
- g_free(msg);
- }
-
- ui_ops = purple_xfer_get_ui_ops(xfer);
-
- if (ui_ops != NULL && ui_ops->update_progress != NULL)
- ui_ops->update_progress(xfer, purple_xfer_get_progress(xfer));
-}
-
-void
-purple_xfer_set_message(PurpleXfer *xfer, const char *message)
-{
- g_return_if_fail(xfer != NULL);
-
- g_free(xfer->message);
- xfer->message = g_strdup(message);
-}
-
-void
-purple_xfer_set_filename(PurpleXfer *xfer, const char *filename)
-{
- g_return_if_fail(xfer != NULL);
-
- g_free(xfer->filename);
- xfer->filename = g_strdup(filename);
-}
-
-void
-purple_xfer_set_local_filename(PurpleXfer *xfer, const char *filename)
-{
- g_return_if_fail(xfer != NULL);
-
- g_free(xfer->local_filename);
- xfer->local_filename = g_strdup(filename);
-}
-
-void
-purple_xfer_set_size(PurpleXfer *xfer, size_t size)
-{
- g_return_if_fail(xfer != NULL);
-
- xfer->size = size;
- xfer->bytes_remaining = xfer->size - purple_xfer_get_bytes_sent(xfer);
-}
-
-void
-purple_xfer_set_bytes_sent(PurpleXfer *xfer, size_t bytes_sent)
-{
- g_return_if_fail(xfer != NULL);
-
- xfer->bytes_sent = bytes_sent;
- xfer->bytes_remaining = purple_xfer_get_size(xfer) - bytes_sent;
-}
-
-PurpleXferUiOps *
-purple_xfer_get_ui_ops(const PurpleXfer *xfer)
-{
- g_return_val_if_fail(xfer != NULL, NULL);
-
- return xfer->ui_ops;
-}
-
-void
-purple_xfer_set_init_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *))
-{
- g_return_if_fail(xfer != NULL);
-
- xfer->ops.init = fnc;
-}
-
-void purple_xfer_set_request_denied_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *))
-{
- g_return_if_fail(xfer != NULL);
-
- xfer->ops.request_denied = fnc;
-}
-
-void
-purple_xfer_set_read_fnc(PurpleXfer *xfer, gssize (*fnc)(guchar **, PurpleXfer *))
-{
- g_return_if_fail(xfer != NULL);
-
- xfer->ops.read = fnc;
-}
-
-void
-purple_xfer_set_write_fnc(PurpleXfer *xfer,
- gssize (*fnc)(const guchar *, size_t, PurpleXfer *))
-{
- g_return_if_fail(xfer != NULL);
-
- xfer->ops.write = fnc;
-}
-
-void
-purple_xfer_set_ack_fnc(PurpleXfer *xfer,
- void (*fnc)(PurpleXfer *, const guchar *, size_t))
-{
- g_return_if_fail(xfer != NULL);
-
- xfer->ops.ack = fnc;
-}
-
-void
-purple_xfer_set_start_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *))
-{
- g_return_if_fail(xfer != NULL);
-
- xfer->ops.start = fnc;
-}
-
-void
-purple_xfer_set_end_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *))
-{
- g_return_if_fail(xfer != NULL);
-
- xfer->ops.end = fnc;
-}
-
-void
-purple_xfer_set_cancel_send_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *))
-{
- g_return_if_fail(xfer != NULL);
-
- xfer->ops.cancel_send = fnc;
-}
-
-void
-purple_xfer_set_cancel_recv_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *))
-{
- g_return_if_fail(xfer != NULL);
-
- xfer->ops.cancel_recv = fnc;
-}
-
-static void
-purple_xfer_increase_buffer_size(PurpleXfer *xfer)
-{
- xfer->current_buffer_size = MIN(xfer->current_buffer_size * 1.5,
- FT_MAX_BUFFER_SIZE);
-}
-
-gssize
-purple_xfer_read(PurpleXfer *xfer, guchar **buffer)
-{
- gssize s, r;
-
- g_return_val_if_fail(xfer != NULL, 0);
- g_return_val_if_fail(buffer != NULL, 0);
-
- if (purple_xfer_get_size(xfer) == 0)
- s = xfer->current_buffer_size;
- else
- s = MIN(purple_xfer_get_bytes_remaining(xfer), xfer->current_buffer_size);
-
- if (xfer->ops.read != NULL) {
- r = (xfer->ops.read)(buffer, xfer);
- }
- else {
- *buffer = g_malloc0(s);
-
- r = read(xfer->fd, *buffer, s);
- if (r < 0 && errno == EAGAIN)
- r = 0;
- else if (r < 0)
- r = -1;
- else if (r == 0)
- r = -1;
- }
-
- if (r == xfer->current_buffer_size)
- /*
- * We managed to read the entire buffer. This means our this
- * network is fast and our buffer is too small, so make it
- * bigger.
- */
- purple_xfer_increase_buffer_size(xfer);
-
- return r;
-}
-
-gssize
-purple_xfer_write(PurpleXfer *xfer, const guchar *buffer, gsize size)
-{
- gssize r, s;
-
- g_return_val_if_fail(xfer != NULL, 0);
- g_return_val_if_fail(buffer != NULL, 0);
- g_return_val_if_fail(size != 0, 0);
-
- s = MIN(purple_xfer_get_bytes_remaining(xfer), size);
-
- if (xfer->ops.write != NULL) {
- r = (xfer->ops.write)(buffer, s, xfer);
- } else {
- r = write(xfer->fd, buffer, s);
- if (r < 0 && errno == EAGAIN)
- r = 0;
- }
- if (r >= 0 && (purple_xfer_get_bytes_sent(xfer)+r) >= purple_xfer_get_size(xfer) &&
- !purple_xfer_is_completed(xfer))
- purple_xfer_set_completed(xfer, TRUE);
-
-
- return r;
-}
-
-static void
-do_transfer(PurpleXfer *xfer)
-{
- PurpleXferUiOps *ui_ops;
- guchar *buffer = NULL;
- gssize r = 0;
-
- ui_ops = purple_xfer_get_ui_ops(xfer);
-
- if (xfer->type == PURPLE_XFER_RECEIVE) {
- r = purple_xfer_read(xfer, &buffer);
- if (r > 0) {
- size_t wc;
- if (ui_ops && ui_ops->ui_write)
- wc = ui_ops->ui_write(xfer, buffer, r);
- else
- wc = fwrite(buffer, 1, r, xfer->dest_fp);
-
- if (wc != r) {
- purple_debug_error("filetransfer", "Unable to write whole buffer.\n");
- purple_xfer_cancel_local(xfer);
- g_free(buffer);
- return;
- }
-
- if ((purple_xfer_get_size(xfer) > 0) &&
- ((purple_xfer_get_bytes_sent(xfer)+r) >= purple_xfer_get_size(xfer)))
- purple_xfer_set_completed(xfer, TRUE);
- } else if(r < 0) {
- purple_xfer_cancel_remote(xfer);
- g_free(buffer);
- return;
- }
- } else if (xfer->type == PURPLE_XFER_SEND) {
- size_t result = 0;
- size_t s = MIN(purple_xfer_get_bytes_remaining(xfer), xfer->current_buffer_size);
- PurpleXferPrivData *priv = g_hash_table_lookup(xfers_data, xfer);
- gboolean read = TRUE;
-
- /* this is so the prpl can keep the connection open
- if it needs to for some odd reason. */
- if (s == 0) {
- if (xfer->watcher) {
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
- }
- return;
- }
-
- if (priv->buffer) {
- if (priv->buffer->len < s) {
- s -= priv->buffer->len;
- read = TRUE;
- } else {
- read = FALSE;
- }
- }
-
- if (read) {
- if (ui_ops && ui_ops->ui_read) {
- gssize tmp = ui_ops->ui_read(xfer, &buffer, s);
- if (tmp == 0) {
- /*
- * The UI claimed it was ready, but didn't have any data for
- * us... It will call purple_xfer_ui_ready when ready, which
- * sets back up this watcher.
- */
- if (xfer->watcher != 0) {
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
- }
-
- /* Need to indicate the prpl is still ready... */
- priv->ready |= PURPLE_XFER_READY_PRPL;
-
- g_return_if_reached();
- } else if (tmp < 0) {
- purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
- purple_xfer_cancel_local(xfer);
- return;
- }
-
- result = tmp;
- } else {
- buffer = g_malloc(s);
- result = fread(buffer, 1, s, xfer->dest_fp);
- if (result != s) {
- purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
- purple_xfer_cancel_local(xfer);
- g_free(buffer);
- return;
- }
- }
- }
-
- if (priv->buffer) {
- g_byte_array_append(priv->buffer, buffer, result);
- g_free(buffer);
- buffer = priv->buffer->data;
- result = priv->buffer->len;
- }
-
- r = purple_xfer_write(xfer, buffer, result);
-
- if (r == -1) {
- purple_xfer_cancel_remote(xfer);
- if (!priv->buffer)
- /* We don't free buffer if priv->buffer is set, because in
- that case buffer doesn't belong to us. */
- g_free(buffer);
- return;
- } else if (r == result) {
- /*
- * We managed to write 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);
- } else {
- if (ui_ops && ui_ops->data_not_sent)
- ui_ops->data_not_sent(xfer, buffer + r, result - r);
- }
-
- if (priv->buffer) {
- /*
- * Remove what we wrote
- * If we wrote the whole buffer the byte array will be empty
- * Otherwise we'll keep what wasn't sent for next time.
- */
- buffer = NULL;
- g_byte_array_remove_range(priv->buffer, 0, r);
- }
- }
-
- if (r > 0) {
- if (purple_xfer_get_size(xfer) > 0)
- xfer->bytes_remaining -= r;
-
- xfer->bytes_sent += r;
-
- if (xfer->ops.ack != NULL)
- xfer->ops.ack(xfer, buffer, r);
-
- g_free(buffer);
-
- if (ui_ops != NULL && ui_ops->update_progress != NULL)
- ui_ops->update_progress(xfer,
- purple_xfer_get_progress(xfer));
- }
-
- if (purple_xfer_is_completed(xfer))
- purple_xfer_end(xfer);
-}
-
-static void
-transfer_cb(gpointer data, gint source, PurpleInputCondition condition)
-{
- PurpleXfer *xfer = data;
-
- if (xfer->dest_fp == NULL) {
- /* The UI is moderating its side manually */
- PurpleXferPrivData *priv = g_hash_table_lookup(xfers_data, xfer);
- if (0 == (priv->ready & PURPLE_XFER_READY_UI)) {
- priv->ready |= PURPLE_XFER_READY_PRPL;
-
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
-
- purple_debug_misc("xfer", "prpl is ready on ft %p, waiting for UI\n", xfer);
- return;
- }
-
- priv->ready = PURPLE_XFER_READY_NONE;
- }
-
- do_transfer(xfer);
-}
-
-static void
-begin_transfer(PurpleXfer *xfer, PurpleInputCondition cond)
-{
- PurpleXferType type = purple_xfer_get_type(xfer);
- PurpleXferUiOps *ui_ops = purple_xfer_get_ui_ops(xfer);
-
- if (xfer->start_time != 0) {
- purple_debug_error("xfer", "Transfer is being started multiple times\n");
- g_return_if_reached();
- }
-
- if (ui_ops == NULL || (ui_ops->ui_read == NULL && ui_ops->ui_write == NULL)) {
- xfer->dest_fp = g_fopen(purple_xfer_get_local_filename(xfer),
- type == PURPLE_XFER_RECEIVE ? "wb" : "rb");
-
- if (xfer->dest_fp == NULL) {
- purple_xfer_show_file_error(xfer, purple_xfer_get_local_filename(xfer));
- purple_xfer_cancel_local(xfer);
- return;
- }
-
- if (fseek(xfer->dest_fp, xfer->bytes_sent, SEEK_SET) != 0) {
- purple_debug_error("xfer", "couldn't seek\n");
- purple_xfer_show_file_error(xfer, purple_xfer_get_local_filename(xfer));
- purple_xfer_cancel_local(xfer);
- return;
- }
- }
-
- if (xfer->fd != -1)
- xfer->watcher = purple_input_add(xfer->fd, cond, transfer_cb, xfer);
-
- xfer->start_time = time(NULL);
-
- if (xfer->ops.start != NULL)
- xfer->ops.start(xfer);
-}
-
-static void
-connect_cb(gpointer data, gint source, const gchar *error_message)
-{
- PurpleXfer *xfer = (PurpleXfer *)data;
-
- if (source < 0) {
- purple_xfer_cancel_local(xfer);
- return;
- }
-
- xfer->fd = source;
-
- begin_transfer(xfer, PURPLE_INPUT_READ);
-}
-
-void
-purple_xfer_ui_ready(PurpleXfer *xfer)
-{
- PurpleInputCondition cond;
- PurpleXferType type;
- PurpleXferPrivData *priv;
-
- g_return_if_fail(xfer != NULL);
-
- priv = g_hash_table_lookup(xfers_data, xfer);
- priv->ready |= PURPLE_XFER_READY_UI;
-
- if (0 == (priv->ready & PURPLE_XFER_READY_PRPL)) {
- purple_debug_misc("xfer", "UI is ready on ft %p, waiting for prpl\n", xfer);
- return;
- }
-
- purple_debug_misc("xfer", "UI (and prpl) ready on ft %p, so proceeding\n", xfer);
-
- type = purple_xfer_get_type(xfer);
- if (type == PURPLE_XFER_SEND)
- cond = PURPLE_INPUT_WRITE;
- else /* if (type == PURPLE_XFER_RECEIVE) */
- cond = PURPLE_INPUT_READ;
-
- if (xfer->watcher == 0 && xfer->fd != -1)
- xfer->watcher = purple_input_add(xfer->fd, cond, transfer_cb, xfer);
-
- priv->ready = PURPLE_XFER_READY_NONE;
-
- do_transfer(xfer);
-}
-
-void
-purple_xfer_prpl_ready(PurpleXfer *xfer)
-{
- PurpleXferPrivData *priv;
-
- g_return_if_fail(xfer != NULL);
-
- priv = g_hash_table_lookup(xfers_data, xfer);
- priv->ready |= PURPLE_XFER_READY_PRPL;
-
- /* I don't think fwrite/fread are ever *not* ready */
- if (xfer->dest_fp == NULL && 0 == (priv->ready & PURPLE_XFER_READY_UI)) {
- purple_debug_misc("xfer", "prpl is ready on ft %p, waiting for UI\n", xfer);
- return;
- }
-
- purple_debug_misc("xfer", "Prpl (and UI) ready on ft %p, so proceeding\n", xfer);
-
- priv->ready = PURPLE_XFER_READY_NONE;
-
- do_transfer(xfer);
-}
-
-void
-purple_xfer_start(PurpleXfer *xfer, int fd, const char *ip,
- unsigned int port)
-{
- PurpleInputCondition cond;
- PurpleXferType type;
-
- g_return_if_fail(xfer != NULL);
- g_return_if_fail(purple_xfer_get_type(xfer) != PURPLE_XFER_UNKNOWN);
-
- type = purple_xfer_get_type(xfer);
-
- purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_STARTED);
-
- /*
- * FIXME 3.0.0 -- there's too much broken code depending on fd == 0
- * meaning "don't use a real fd"
- */
- if (fd == 0)
- fd = -1;
-
- if (type == PURPLE_XFER_RECEIVE) {
- cond = PURPLE_INPUT_READ;
-
- if (ip != NULL) {
- xfer->remote_ip = g_strdup(ip);
- xfer->remote_port = port;
-
- /* Establish a file descriptor. */
- purple_proxy_connect(NULL, xfer->account, xfer->remote_ip,
- xfer->remote_port, connect_cb, xfer);
-
- return;
- }
- else {
- xfer->fd = fd;
- }
- }
- else {
- cond = PURPLE_INPUT_WRITE;
-
- xfer->fd = fd;
- }
-
- begin_transfer(xfer, cond);
-}
-
-void
-purple_xfer_end(PurpleXfer *xfer)
-{
- g_return_if_fail(xfer != NULL);
-
- /* See if we are actually trying to cancel this. */
- if (!purple_xfer_is_completed(xfer)) {
- purple_xfer_cancel_local(xfer);
- return;
- }
-
- xfer->end_time = time(NULL);
- if (xfer->ops.end != NULL)
- xfer->ops.end(xfer);
-
- if (xfer->watcher != 0) {
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
- }
-
- if (xfer->fd != -1)
- close(xfer->fd);
-
- if (xfer->dest_fp != NULL) {
- fclose(xfer->dest_fp);
- xfer->dest_fp = NULL;
- }
-
- purple_xfer_unref(xfer);
-}
-
-void
-purple_xfer_add(PurpleXfer *xfer)
-{
- PurpleXferUiOps *ui_ops;
-
- g_return_if_fail(xfer != NULL);
-
- ui_ops = purple_xfer_get_ui_ops(xfer);
-
- if (ui_ops != NULL && ui_ops->add_xfer != NULL)
- ui_ops->add_xfer(xfer);
-}
-
-void
-purple_xfer_cancel_local(PurpleXfer *xfer)
-{
- PurpleXferUiOps *ui_ops;
- char *msg = NULL;
-
- g_return_if_fail(xfer != NULL);
-
- /* TODO: We definitely want to close any open request dialogs associated
- with this transfer. However, in some cases the request dialog might
- own a reference on the xfer. This happens at least with the "%s wants
- to send you %s" dialog from purple_xfer_ask_recv(). In these cases
- the ref count will not be decremented when the request dialog is
- closed, so the ref count will never reach 0 and the xfer will never
- be freed. This is a memleak and should be fixed. It's not clear what
- the correct fix is. Probably requests should have a destroy function
- that is called when the request is destroyed. But also, ref counting
- xfer objects makes this code REALLY complicated. An alternate fix is
- to not ref count and instead just make sure the object still exists
- when we try to use it. */
- purple_request_close_with_handle(xfer);
-
- purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL);
- xfer->end_time = time(NULL);
-
- if (purple_xfer_get_filename(xfer) != NULL)
- {
- msg = g_strdup_printf(_("You cancelled the transfer of %s"),
- purple_xfer_get_filename(xfer));
- }
- else
- {
- msg = g_strdup(_("File transfer cancelled"));
- }
- purple_xfer_conversation_write(xfer, msg, FALSE);
- g_free(msg);
-
- if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND)
- {
- if (xfer->ops.cancel_send != NULL)
- xfer->ops.cancel_send(xfer);
- }
- else
- {
- if (xfer->ops.cancel_recv != NULL)
- xfer->ops.cancel_recv(xfer);
- }
-
- if (xfer->watcher != 0) {
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
- }
-
- if (xfer->fd != -1)
- close(xfer->fd);
-
- if (xfer->dest_fp != NULL) {
- fclose(xfer->dest_fp);
- xfer->dest_fp = NULL;
- }
-
- ui_ops = purple_xfer_get_ui_ops(xfer);
-
- if (ui_ops != NULL && ui_ops->cancel_local != NULL)
- ui_ops->cancel_local(xfer);
-
- xfer->bytes_remaining = 0;
-
- purple_xfer_unref(xfer);
-}
-
-void
-purple_xfer_cancel_remote(PurpleXfer *xfer)
-{
- PurpleXferUiOps *ui_ops;
- gchar *msg;
- PurpleAccount *account;
- PurpleBuddy *buddy;
-
- g_return_if_fail(xfer != NULL);
-
- purple_request_close_with_handle(xfer);
- purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE);
- xfer->end_time = time(NULL);
-
- account = purple_xfer_get_account(xfer);
- buddy = purple_find_buddy(account, xfer->who);
-
- if (purple_xfer_get_filename(xfer) != NULL)
- {
- msg = g_strdup_printf(_("%s cancelled the transfer of %s"),
- buddy ? purple_buddy_get_alias(buddy) : xfer->who, purple_xfer_get_filename(xfer));
- }
- else
- {
- msg = g_strdup_printf(_("%s cancelled the file transfer"),
- buddy ? purple_buddy_get_alias(buddy) : xfer->who);
- }
- purple_xfer_conversation_write(xfer, msg, TRUE);
- purple_xfer_error(purple_xfer_get_type(xfer), account, xfer->who, msg);
- g_free(msg);
-
- if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND)
- {
- if (xfer->ops.cancel_send != NULL)
- xfer->ops.cancel_send(xfer);
- }
- else
- {
- if (xfer->ops.cancel_recv != NULL)
- xfer->ops.cancel_recv(xfer);
- }
-
- if (xfer->watcher != 0) {
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
- }
-
- if (xfer->fd != -1)
- close(xfer->fd);
-
- if (xfer->dest_fp != NULL) {
- fclose(xfer->dest_fp);
- xfer->dest_fp = NULL;
- }
-
- ui_ops = purple_xfer_get_ui_ops(xfer);
-
- if (ui_ops != NULL && ui_ops->cancel_remote != NULL)
- ui_ops->cancel_remote(xfer);
-
- xfer->bytes_remaining = 0;
-
- purple_xfer_unref(xfer);
-}
-
-void
-purple_xfer_error(PurpleXferType type, PurpleAccount *account, const char *who, const char *msg)
-{
- char *title;
-
- g_return_if_fail(msg != NULL);
- g_return_if_fail(type != PURPLE_XFER_UNKNOWN);
-
- if (account) {
- PurpleBuddy *buddy;
- buddy = purple_find_buddy(account, who);
- if (buddy)
- who = purple_buddy_get_alias(buddy);
- }
-
- if (type == PURPLE_XFER_SEND)
- title = g_strdup_printf(_("File transfer to %s failed."), who);
- else
- title = g_strdup_printf(_("File transfer from %s failed."), who);
-
- purple_notify_error(NULL, NULL, title, msg);
-
- g_free(title);
-}
-
-void
-purple_xfer_update_progress(PurpleXfer *xfer)
-{
- PurpleXferUiOps *ui_ops;
-
- g_return_if_fail(xfer != NULL);
-
- ui_ops = purple_xfer_get_ui_ops(xfer);
- if (ui_ops != NULL && ui_ops->update_progress != NULL)
- ui_ops->update_progress(xfer, purple_xfer_get_progress(xfer));
-}
-
-gconstpointer
-purple_xfer_get_thumbnail(const PurpleXfer *xfer, gsize *len)
-{
- PurpleXferPrivData *priv = g_hash_table_lookup(xfers_data, xfer);
-
- if (len)
- *len = priv->thumbnail_size;
-
- return priv->thumbnail_data;
-}
-
-const gchar *
-purple_xfer_get_thumbnail_mimetype(const PurpleXfer *xfer)
-{
- PurpleXferPrivData *priv = g_hash_table_lookup(xfers_data, xfer);
-
- return priv->thumbnail_mimetype;
-}
-
-void
-purple_xfer_set_thumbnail(PurpleXfer *xfer, gconstpointer thumbnail,
- gsize size, const gchar *mimetype)
-{
- PurpleXferPrivData *priv = g_hash_table_lookup(xfers_data, xfer);
-
- g_free(priv->thumbnail_data);
- g_free(priv->thumbnail_mimetype);
-
- if (thumbnail && size > 0) {
- priv->thumbnail_data = g_memdup(thumbnail, size);
- priv->thumbnail_size = size;
- priv->thumbnail_mimetype = g_strdup(mimetype);
- } else {
- priv->thumbnail_data = NULL;
- priv->thumbnail_size = 0;
- priv->thumbnail_mimetype = NULL;
- }
-}
-
-void
-purple_xfer_prepare_thumbnail(PurpleXfer *xfer, const gchar *formats)
-{
- if (xfer->ui_ops->add_thumbnail) {
- xfer->ui_ops->add_thumbnail(xfer, formats);
- }
-}
-
-/**************************************************************************
- * File Transfer Subsystem API
- **************************************************************************/
-void *
-purple_xfers_get_handle(void) {
- static int handle = 0;
-
- return &handle;
-}
-
-void
-purple_xfers_init(void) {
- void *handle = purple_xfers_get_handle();
-
- xfers_data = g_hash_table_new_full(g_direct_hash, g_direct_equal,
- NULL, purple_xfer_priv_data_destroy);
-
- /* register signals */
- purple_signal_register(handle, "file-recv-accept",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_XFER));
- purple_signal_register(handle, "file-send-accept",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_XFER));
- purple_signal_register(handle, "file-recv-start",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_XFER));
- purple_signal_register(handle, "file-send-start",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_XFER));
- purple_signal_register(handle, "file-send-cancel",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_XFER));
- purple_signal_register(handle, "file-recv-cancel",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_XFER));
- purple_signal_register(handle, "file-send-complete",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_XFER));
- purple_signal_register(handle, "file-recv-complete",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_XFER));
- purple_signal_register(handle, "file-recv-request",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_XFER));
-}
-
-void
-purple_xfers_uninit(void)
-{
- void *handle = purple_xfers_get_handle();
-
- purple_signals_disconnect_by_handle(handle);
- purple_signals_unregister_by_instance(handle);
-
- g_hash_table_destroy(xfers_data);
- xfers_data = NULL;
-}
-
-void
-purple_xfers_set_ui_ops(PurpleXferUiOps *ops) {
- xfer_ui_ops = ops;
-}
-
-PurpleXferUiOps *
-purple_xfers_get_ui_ops(void) {
- return xfer_ui_ops;
-}
diff --git a/libpurple/ft.h b/libpurple/ft.h
deleted file mode 100644
index 3636cae247..0000000000
--- a/libpurple/ft.h
+++ /dev/null
@@ -1,785 +0,0 @@
-/**
- * @file ft.h File Transfer API
- * @ingroup core
- * @see @ref xfer-signals
- */
-
-/* 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 _PURPLE_FT_H_
-#define _PURPLE_FT_H_
-
-/**************************************************************************/
-/** Data Structures */
-/**************************************************************************/
-typedef struct _PurpleXfer PurpleXfer;
-
-#include <glib.h>
-#include <stdio.h>
-
-#include "account.h"
-
-/**
- * Types of file transfers.
- */
-typedef enum
-{
- PURPLE_XFER_UNKNOWN = 0, /**< Unknown file transfer type. */
- PURPLE_XFER_SEND, /**< File sending. */
- PURPLE_XFER_RECEIVE /**< File receiving. */
-
-} PurpleXferType;
-
-/**
- * The different states of the xfer.
- */
-typedef enum
-{
- PURPLE_XFER_STATUS_UNKNOWN = 0, /**< Unknown, the xfer may be null. */
- PURPLE_XFER_STATUS_NOT_STARTED, /**< It hasn't started yet. */
- PURPLE_XFER_STATUS_ACCEPTED, /**< Receive accepted, but destination file not selected yet */
- PURPLE_XFER_STATUS_STARTED, /**< purple_xfer_start has been called. */
- PURPLE_XFER_STATUS_DONE, /**< The xfer completed successfully. */
- PURPLE_XFER_STATUS_CANCEL_LOCAL, /**< The xfer was cancelled by us. */
- PURPLE_XFER_STATUS_CANCEL_REMOTE /**< The xfer was cancelled by the other end, or we couldn't connect. */
-} PurpleXferStatusType;
-
-/**
- * File transfer UI operations.
- *
- * Any UI representing a file transfer must assign a filled-out
- * PurpleXferUiOps structure to the purple_xfer.
- */
-typedef struct
-{
- void (*new_xfer)(PurpleXfer *xfer);
- void (*destroy)(PurpleXfer *xfer);
- void (*add_xfer)(PurpleXfer *xfer);
- void (*update_progress)(PurpleXfer *xfer, double percent);
- void (*cancel_local)(PurpleXfer *xfer);
- void (*cancel_remote)(PurpleXfer *xfer);
-
- /**
- * UI op to write data received from the prpl. The UI must deal with the
- * entire buffer and return size, or it is treated as an error.
- *
- * @param xfer The file transfer structure
- * @param buffer The buffer to write
- * @param size The size of the buffer
- *
- * @return size if the write was successful, or a value between 0 and
- * size on error.
- * @since 2.6.0
- */
- gssize (*ui_write)(PurpleXfer *xfer, const guchar *buffer, gssize size);
-
- /**
- * UI op to read data to send to the prpl for a file transfer.
- *
- * @param xfer The file transfer structure
- * @param buffer A pointer to a buffer. The UI must allocate this buffer.
- * libpurple will free the data.
- * @param size The maximum amount of data to put in the buffer.
- *
- * @returns The amount of data in the buffer, 0 if nothing is available,
- * and a negative value if an error occurred and the transfer
- * should be cancelled (libpurple will cancel).
- * @since 2.6.0
- */
- gssize (*ui_read)(PurpleXfer *xfer, guchar **buffer, gssize size);
-
- /**
- * Op to notify the UI that not all the data read in was written. The UI
- * should re-enqueue this data and return it the next time read is called.
- *
- * This MUST be implemented if read and write are implemented.
- *
- * @param xfer The file transfer structure
- * @param buffer A pointer to the beginning of the unwritten data.
- * @param size The amount of unwritten data.
- *
- * @since 2.6.0
- */
- void (*data_not_sent)(PurpleXfer *xfer, const guchar *buffer, gsize size);
-
- /**
- * Op to create a thumbnail image for a file transfer
- *
- * @param xfer The file transfer structure
- */
- void (*add_thumbnail)(PurpleXfer *xfer, const gchar *formats);
-} PurpleXferUiOps;
-
-/**
- * A core representation of a file transfer.
- */
-struct _PurpleXfer
-{
- guint ref; /**< The reference count. */
- PurpleXferType type; /**< The type of transfer. */
-
- PurpleAccount *account; /**< The account. */
-
- char *who; /**< The person on the other end of the
- transfer. */
-
- char *message; /**< A message sent with the request */
- char *filename; /**< The name sent over the network. */
- char *local_filename; /**< The name on the local hard drive. */
- size_t size; /**< The size of the file. */
-
- FILE *dest_fp; /**< The destination file pointer. */
-
- char *remote_ip; /**< The remote IP address. */
- int local_port; /**< The local port. */
- int remote_port; /**< The remote port. */
-
- int fd; /**< The socket file descriptor. */
- int watcher; /**< Watcher. */
-
- size_t bytes_sent; /**< The number of bytes sent. */
- size_t bytes_remaining; /**< The number of bytes remaining. */
- time_t start_time; /**< When the transfer of data began. */
- time_t end_time; /**< When the transfer of data ended. */
-
- size_t current_buffer_size; /**< This gradually increases for fast
- network connections. */
-
- PurpleXferStatusType status; /**< File Transfer's status. */
-
- /** I/O operations, which should be set by the prpl 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, PurpleXfer *xfer);
- gssize (*write)(const guchar *buffer, size_t size, PurpleXfer *xfer);
- void (*ack)(PurpleXfer *xfer, const guchar *buffer, size_t size);
- } ops;
-
- PurpleXferUiOps *ui_ops; /**< UI-specific operations. */
- void *ui_data; /**< UI-specific data. */
-
- void *data; /**< prpl-specific data. */
-};
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**************************************************************************/
-/** @name File Transfer API */
-/**************************************************************************/
-/*@{*/
-
-/**
- * Creates a new file transfer handle.
- * This is called by prpls.
- * The handle starts with a ref count of 1, and this reference
- * is owned by the core. The prpl normally does not need to
- * purple_xfer_ref or unref.
- *
- * @param account The account sending or receiving the file.
- * @param type The type of file transfer.
- * @param who The name of the remote user.
- *
- * @return A file transfer handle.
- */
-PurpleXfer *purple_xfer_new(PurpleAccount *account,
- PurpleXferType type, const char *who);
-
-/**
- * Returns all xfers
- *
- * @return all current xfers with refs
- */
-GList *purple_xfers_get_all(void);
-
-/**
- * Increases the reference count on a PurpleXfer.
- * Please call purple_xfer_unref later.
- *
- * @param xfer A file transfer handle.
- */
-void purple_xfer_ref(PurpleXfer *xfer);
-
-/**
- * Decreases the reference count on a PurpleXfer.
- * If the reference reaches 0, purple_xfer_destroy (an internal function)
- * will destroy the xfer. It calls the ui destroy cb first.
- * Since the core keeps a ref on the xfer, only an erroneous call to
- * this function will destroy the xfer while still in use.
- *
- * @param xfer A file transfer handle.
- */
-void purple_xfer_unref(PurpleXfer *xfer);
-
-/**
- * Requests confirmation for a file transfer from the user. If receiving
- * a file which is known at this point, this requests user to accept and
- * save the file. If the filename is unknown (not set) this only requests user
- * to accept the file transfer. In this case protocol must call this function
- * again once the filename is available.
- *
- * @param xfer The file transfer to request confirmation on.
- */
-void purple_xfer_request(PurpleXfer *xfer);
-
-/**
- * Called if the user accepts the file transfer request.
- *
- * @param xfer The file transfer.
- * @param filename The filename.
- */
-void purple_xfer_request_accepted(PurpleXfer *xfer, const char *filename);
-
-/**
- * Called if the user rejects the file transfer request.
- *
- * @param xfer The file transfer.
- */
-void purple_xfer_request_denied(PurpleXfer *xfer);
-
-/**
- * Returns the type of file transfer.
- *
- * @param xfer The file transfer.
- *
- * @return The type of the file transfer.
- */
-PurpleXferType purple_xfer_get_type(const PurpleXfer *xfer);
-
-/**
- * Returns the account the file transfer is using.
- *
- * @param xfer The file transfer.
- *
- * @return The account.
- */
-PurpleAccount *purple_xfer_get_account(const PurpleXfer *xfer);
-
-/**
- * Returns the name of the remote user.
- *
- * @param xfer The file transfer.
- *
- * @return The name of the remote user.
- *
- * @since 2.1.0
- */
-const char *purple_xfer_get_remote_user(const PurpleXfer *xfer);
-
-/**
- * Returns the status of the xfer.
- *
- * @param xfer The file transfer.
- *
- * @return The status.
- */
-PurpleXferStatusType purple_xfer_get_status(const PurpleXfer *xfer);
-
-/**
- * Returns true if the file transfer was cancelled.
- *
- * @param xfer The file transfer.
- *
- * @return Whether or not the transfer was cancelled.
- * FIXME: This should be renamed using cancelled for 3.0.0.
- */
-gboolean purple_xfer_is_canceled(const PurpleXfer *xfer);
-
-/**
- * Returns the completed state for a file transfer.
- *
- * @param xfer The file transfer.
- *
- * @return The completed state.
- */
-gboolean purple_xfer_is_completed(const PurpleXfer *xfer);
-
-/**
- * Returns the name of the file being sent or received.
- *
- * @param xfer The file transfer.
- *
- * @return The filename.
- */
-const char *purple_xfer_get_filename(const PurpleXfer *xfer);
-
-/**
- * Returns the file's destination filename,
- *
- * @param xfer The file transfer.
- *
- * @return The destination filename.
- */
-const char *purple_xfer_get_local_filename(const PurpleXfer *xfer);
-
-/**
- * Returns the number of bytes sent (or received) so far.
- *
- * @param xfer The file transfer.
- *
- * @return The number of bytes sent.
- */
-size_t purple_xfer_get_bytes_sent(const PurpleXfer *xfer);
-
-/**
- * Returns the number of bytes remaining to send or receive.
- *
- * @param xfer The file transfer.
- *
- * @return The number of bytes remaining.
- */
-size_t purple_xfer_get_bytes_remaining(const PurpleXfer *xfer);
-
-/**
- * Returns the size of the file being sent or received.
- *
- * @param xfer The file transfer.
- *
- * @return The total size of the file.
- */
-size_t purple_xfer_get_size(const PurpleXfer *xfer);
-
-/**
- * Returns the current percentage of progress of the transfer.
- *
- * This is a number between 0 (0%) and 1 (100%).
- *
- * @param xfer The file transfer.
- *
- * @return The percentage complete.
- */
-double purple_xfer_get_progress(const PurpleXfer *xfer);
-
-/**
- * Returns the local port number in the file transfer.
- *
- * @param xfer The file transfer.
- *
- * @return The port number on this end.
- */
-unsigned int purple_xfer_get_local_port(const PurpleXfer *xfer);
-
-/**
- * Returns the remote IP address in the file transfer.
- *
- * @param xfer The file transfer.
- *
- * @return The IP address on the other end.
- */
-const char *purple_xfer_get_remote_ip(const PurpleXfer *xfer);
-
-/**
- * Returns the remote port number in the file transfer.
- *
- * @param xfer The file transfer.
- *
- * @return The port number on the other end.
- */
-unsigned int purple_xfer_get_remote_port(const PurpleXfer *xfer);
-
-/**
- * Returns the time the transfer of a file started.
- *
- * @param xfer The file transfer.
- *
- * @return The time when the transfer started.
- * @since 2.4.0
- */
-time_t purple_xfer_get_start_time(const PurpleXfer *xfer);
-
-/**
- * Returns the time the transfer of a file ended.
- *
- * @param xfer The file transfer.
- *
- * @return The time when the transfer ended.
- * @since 2.4.0
- */
-time_t purple_xfer_get_end_time(const PurpleXfer *xfer);
-
-/**
- * Sets the completed state for the file transfer.
- *
- * @param xfer The file transfer.
- * @param completed The completed state.
- */
-void purple_xfer_set_completed(PurpleXfer *xfer, gboolean completed);
-
-/**
- * Sets the filename for the file transfer.
- *
- * @param xfer The file transfer.
- * @param message The message.
- */
-void purple_xfer_set_message(PurpleXfer *xfer, const char *message);
-
-/**
- * Sets the filename for the file transfer.
- *
- * @param xfer The file transfer.
- * @param filename The filename.
- */
-void purple_xfer_set_filename(PurpleXfer *xfer, const char *filename);
-
-/**
- * Sets the local filename for the file transfer.
- *
- * @param xfer The file transfer.
- * @param filename The filename
- */
-void purple_xfer_set_local_filename(PurpleXfer *xfer, const char *filename);
-
-/**
- * Sets the size of the file in a file transfer.
- *
- * @param xfer The file transfer.
- * @param size The size of the file.
- */
-void purple_xfer_set_size(PurpleXfer *xfer, size_t size);
-
-/**
- * Sets the current working position in the active file transfer. This
- * can be used to jump backward in the file if the protocol detects
- * that some bit of data needs to be resent or has been sent twice.
- *
- * It's used for pausing and resuming an oscar file transfer.
- *
- * @param xfer The file transfer.
- * @param bytes_sent The new current position in the file. If we're
- * sending a file then this is the byte that we will
- * send. If we're receiving a file, this is the
- * next byte that we expect to receive.
- */
-void purple_xfer_set_bytes_sent(PurpleXfer *xfer, size_t bytes_sent);
-
-/**
- * Returns the UI operations structure for a file transfer.
- *
- * @param xfer The file transfer.
- *
- * @return The UI operations structure.
- */
-PurpleXferUiOps *purple_xfer_get_ui_ops(const PurpleXfer *xfer);
-
-/**
- * Sets the read function for the file transfer.
- *
- * @param xfer The file transfer.
- * @param fnc The read function.
- */
-void purple_xfer_set_read_fnc(PurpleXfer *xfer,
- gssize (*fnc)(guchar **, PurpleXfer *));
-
-/**
- * Sets the write function for the file transfer.
- *
- * @param xfer The file transfer.
- * @param fnc The write function.
- */
-void purple_xfer_set_write_fnc(PurpleXfer *xfer,
- gssize (*fnc)(const guchar *, size_t, PurpleXfer *));
-
-/**
- * Sets the acknowledge function for the file transfer.
- *
- * @param xfer The file transfer.
- * @param fnc The acknowledge function.
- */
-void purple_xfer_set_ack_fnc(PurpleXfer *xfer,
- void (*fnc)(PurpleXfer *, const guchar *, size_t));
-
-/**
- * Sets the function to be called if the request is denied.
- *
- * @param xfer The file transfer.
- * @param fnc The request denied prpl callback.
- */
-void purple_xfer_set_request_denied_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *));
-
-/**
- * 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.
- *
- * @param xfer The file transfer.
- * @param fnc The transfer initialization function.
- */
-void purple_xfer_set_init_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *));
-
-/**
- * Sets the start transfer function for the file transfer.
- *
- * @param xfer The file transfer.
- * @param fnc The start transfer function.
- */
-void purple_xfer_set_start_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *));
-
-/**
- * Sets the end transfer function for the file transfer.
- *
- * @param xfer The file transfer.
- * @param fnc The end transfer function.
- */
-void purple_xfer_set_end_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *));
-
-/**
- * Sets the cancel send function for the file transfer.
- *
- * @param xfer The file transfer.
- * @param fnc The cancel send function.
- */
-void purple_xfer_set_cancel_send_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *));
-
-/**
- * Sets the cancel receive function for the file transfer.
- *
- * @param xfer The file transfer.
- * @param fnc The cancel receive function.
- */
-void purple_xfer_set_cancel_recv_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *));
-
-/**
- * Reads in data from a file transfer stream.
- *
- * @param xfer The file transfer.
- * @param buffer The buffer that will be created to contain the data.
- *
- * @return The number of bytes read, or -1.
- */
-gssize purple_xfer_read(PurpleXfer *xfer, guchar **buffer);
-
-/**
- * Writes data to a file transfer stream.
- *
- * @param xfer The file transfer.
- * @param buffer The buffer to read the data from.
- * @param size The number of bytes to write.
- *
- * @return The number of bytes written, or -1.
- */
-gssize purple_xfer_write(PurpleXfer *xfer, const guchar *buffer, gsize size);
-
-/**
- * Starts a file transfer.
- *
- * Either @a fd must be specified <i>or</i> @a ip and @a port on a
- * file receive transfer. On send, @a fd must be specified, and
- * @a ip and @a port are ignored.
- *
- * Prior to libpurple 2.6.0, passing '0' to @a fd was special-cased to
- * allow the protocol plugin to facilitate the file transfer itself. As of
- * 2.6.0, this is supported (for backward compatibility), but will be
- * removed in libpurple 3.0.0. If a prpl detects that the running libpurple
- * is running 2.6.0 or higher, it should use the invalid fd '-1'.
- *
- * @param xfer The file transfer.
- * @param fd The file descriptor for the socket.
- * @param ip The IP address to connect to.
- * @param port The port to connect to.
- */
-void purple_xfer_start(PurpleXfer *xfer, int fd, const char *ip,
- unsigned int port);
-
-/**
- * Ends a file transfer.
- *
- * @param xfer The file transfer.
- */
-void purple_xfer_end(PurpleXfer *xfer);
-
-/**
- * Adds a new file transfer to the list of file transfers. Call this only
- * if you are not using purple_xfer_start.
- *
- * @param xfer The file transfer.
- */
-void purple_xfer_add(PurpleXfer *xfer);
-
-/**
- * Cancels a file transfer on the local end.
- *
- * @param xfer The file transfer.
- */
-void purple_xfer_cancel_local(PurpleXfer *xfer);
-
-/**
- * Cancels a file transfer from the remote end.
- *
- * @param xfer The file transfer.
- */
-void purple_xfer_cancel_remote(PurpleXfer *xfer);
-
-/**
- * Displays a file transfer-related error message.
- *
- * This is a wrapper around purple_notify_error(), which automatically
- * specifies a title ("File transfer to <i>user</i> failed" or
- * "File Transfer from <i>user</i> failed").
- *
- * @param type The type of file transfer.
- * @param account The account sending or receiving the file.
- * @param who The user on the other end of the transfer.
- * @param msg The message to display.
- */
-void purple_xfer_error(PurpleXferType type, PurpleAccount *account, const char *who, const char *msg);
-
-/**
- * Updates file transfer progress.
- *
- * @param xfer The file transfer.
- */
-void purple_xfer_update_progress(PurpleXfer *xfer);
-
-/**
- * Displays a file transfer-related message in the conversation window
- *
- * This is a wrapper around purple_conversation_write
- *
- * @param xfer The file transfer to which this message relates.
- * @param message The message to display.
- * @param is_error Is this an error message?.
- */
-void purple_xfer_conversation_write(PurpleXfer *xfer, char *message, gboolean is_error);
-
-/**
- * Allows the UI to signal it's ready to send/receive data (depending on
- * the direction of the file transfer. Used when the UI is providing
- * read/write/data_not_sent UI ops.
- *
- * @param xfer The file transfer which is ready.
- *
- * @since 2.6.0
- */
-void purple_xfer_ui_ready(PurpleXfer *xfer);
-
-/**
- * Allows the prpl to signal it's ready to send/receive data (depending on
- * the direction of the file transfer. Used when the prpl provides read/write
- * ops and cannot/does not provide a raw fd to the core.
- *
- * @param xfer The file transfer which is ready.
- *
- * @since 2.6.0
- */
-void purple_xfer_prpl_ready(PurpleXfer *xfer);
-
-/**
- * Gets the thumbnail data for a transfer
- *
- * @param xfer The file transfer to get the thumbnail for
- * @param len If not @c NULL, the length of the thumbnail data returned
- * will be set in the location pointed to by this.
- * @return The thumbnail data, or NULL if there is no thumbnail
- * @since 2.7.0
- */
-gconstpointer purple_xfer_get_thumbnail(const PurpleXfer *xfer, gsize *len);
-
-/**
- * Gets the mimetype of the thumbnail preview for a transfer
- *
- * @param xfer The file transfer to get the mimetype for
- * @return The mimetype of the thumbnail, or @c NULL if not thumbnail is set
- * @since 2.7.0
- */
-const gchar *purple_xfer_get_thumbnail_mimetype(const PurpleXfer *xfer);
-
-
-/**
- * Sets the thumbnail data for a transfer
- *
- * @param xfer The file transfer to set the data for
- * @param thumbnail A pointer to the thumbnail data, this will be copied
- * @param size The size in bytes of the passed in thumbnail data
- * @param mimetype The mimetype of the generated thumbnail
- * @since 2.7.0
- */
-void purple_xfer_set_thumbnail(PurpleXfer *xfer, gconstpointer thumbnail,
- gsize size, const gchar *mimetype);
-
-/**
- * Prepare a thumbnail for a transfer (if the UI supports it)
- * will be no-op in case the UI doesn't implement thumbnail creation
- *
- * @param xfer The file transfer to create a thumbnail for
- * @param formats A comma-separated list of mimetypes for image formats
- * the protocols can use for thumbnails.
- * @since 2.7.0
- */
-void purple_xfer_prepare_thumbnail(PurpleXfer *xfer, const gchar *formats);
-
-
-/*@}*/
-
-/**************************************************************************/
-/** @name UI Registration Functions */
-/**************************************************************************/
-/*@{*/
-
-/**
- * Returns the handle to the file transfer subsystem
- *
- * @return The handle
- */
-void *purple_xfers_get_handle(void);
-
-/**
- * Initializes the file transfer subsystem
- */
-void purple_xfers_init(void);
-
-/**
- * Uninitializes the file transfer subsystem
- */
-void purple_xfers_uninit(void);
-
-/**
- * Sets the UI operations structure to be used in all purple file transfers.
- *
- * @param ops The UI operations structure.
- */
-void purple_xfers_set_ui_ops(PurpleXferUiOps *ops);
-
-/**
- * Returns the UI operations structure to be used in all purple file transfers.
- *
- * @return The UI operations structure.
- */
-PurpleXferUiOps *purple_xfers_get_ui_ops(void);
-
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _PURPLE_FT_H_ */
diff --git a/libpurple/gaim-compat.h b/libpurple/gaim-compat.h
deleted file mode 100644
index 848b9c4d29..0000000000
--- a/libpurple/gaim-compat.h
+++ /dev/null
@@ -1,2317 +0,0 @@
-/**
- * @file gaim-compat.h Gaim Compat macros
- * @ingroup core
- */
-
-/* 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 _GAIM_COMPAT_H_
-#define _GAIM_COMPAT_H_
-
-#include <glib.h>
-
-/* from account.h */
-#define GaimAccountUiOps PurpleAccountUiOps
-#define GaimAccount PurpleAccount
-
-#define GaimFilterAccountFunc PurpleFilterAccountFunc
-#define GaimAccountRequestAuthorizationCb PurpleAccountRequestAuthorizationCb
-
-#define gaim_account_new purple_account_new
-#define gaim_account_destroy purple_account_destroy
-#define gaim_account_connect purple_account_connect
-#define gaim_account_register purple_account_register
-#define gaim_account_disconnect purple_account_disconnect
-#define gaim_account_notify_added purple_account_notify_added
-#define gaim_account_request_add purple_account_request_add
-#define gaim_account_request_close purple_account_request_close
-
-#define gaim_account_request_authorization purple_account_request_authorization
-#define gaim_account_request_change_password purple_account_request_change_password
-#define gaim_account_request_change_user_info purple_account_request_change_user_info
-
-#define gaim_account_set_username purple_account_set_username
-#define gaim_account_set_password purple_account_set_password
-#define gaim_account_set_alias purple_account_set_alias
-#define gaim_account_set_user_info purple_account_set_user_info
-#define gaim_account_set_buddy_icon_path purple_account_set_buddy_icon_path
-#define gaim_account_set_protocol_id purple_account_set_protocol_id
-#define gaim_account_set_connection purple_account_set_connection
-#define gaim_account_set_remember_password purple_account_set_remember_password
-#define gaim_account_set_check_mail purple_account_set_check_mail
-#define gaim_account_set_enabled purple_account_set_enabled
-#define gaim_account_set_proxy_info purple_account_set_proxy_info
-#define gaim_account_set_status_types purple_account_set_status_types
-#define gaim_account_set_status purple_account_set_status
-#define gaim_account_set_status_list purple_account_set_status_list
-
-#define gaim_account_clear_settings purple_account_clear_settings
-
-#define gaim_account_set_int purple_account_set_int
-#define gaim_account_set_string purple_account_set_string
-#define gaim_account_set_bool purple_account_set_bool
-
-#define gaim_account_set_ui_int purple_account_set_ui_int
-#define gaim_account_set_ui_string purple_account_set_ui_string
-#define gaim_account_set_ui_bool purple_account_set_ui_bool
-
-#define gaim_account_is_connected purple_account_is_connected
-#define gaim_account_is_connecting purple_account_is_connecting
-#define gaim_account_is_disconnected purple_account_is_disconnected
-
-#define gaim_account_get_username purple_account_get_username
-#define gaim_account_get_password purple_account_get_password
-#define gaim_account_get_alias purple_account_get_alias
-#define gaim_account_get_user_info purple_account_get_user_info
-#define gaim_account_get_buddy_icon_path purple_account_get_buddy_icon_path
-#define gaim_account_get_protocol_id purple_account_get_protocol_id
-#define gaim_account_get_protocol_name purple_account_get_protocol_name
-#define gaim_account_get_connection purple_account_get_connection
-#define gaim_account_get_remember_password purple_account_get_remember_password
-#define gaim_account_get_check_mail purple_account_get_check_mail
-#define gaim_account_get_enabled purple_account_get_enabled
-#define gaim_account_get_proxy_info purple_account_get_proxy_info
-#define gaim_account_get_active_status purple_account_get_active_status
-#define gaim_account_get_status purple_account_get_status
-#define gaim_account_get_status_type purple_account_get_status_type
-#define gaim_account_get_status_type_with_primitive \
- purple_account_get_status_type_with_primitive
-
-#define gaim_account_get_presence purple_account_get_presence
-#define gaim_account_is_status_active purple_account_is_status_active
-#define gaim_account_get_status_types purple_account_get_status_types
-
-#define gaim_account_get_int purple_account_get_int
-#define gaim_account_get_string purple_account_get_string
-#define gaim_account_get_bool purple_account_get_bool
-
-#define gaim_account_get_ui_int purple_account_get_ui_int
-#define gaim_account_get_ui_string purple_account_get_ui_string
-#define gaim_account_get_ui_bool purple_account_get_ui_bool
-
-
-#define gaim_account_get_log purple_account_get_log
-#define gaim_account_destroy_log purple_account_destroy_log
-
-#define gaim_account_add_buddy purple_account_add_buddy
-#define gaim_account_add_buddies purple_account_add_buddies
-#define gaim_account_remove_buddy purple_account_remove_buddy
-#define gaim_account_remove_buddies purple_account_remove_buddies
-
-#define gaim_account_remove_group purple_account_remove_group
-
-#define gaim_account_change_password purple_account_change_password
-
-#define gaim_account_supports_offline_message purple_account_supports_offline_message
-
-#define gaim_accounts_add purple_accounts_add
-#define gaim_accounts_remove purple_accounts_remove
-#define gaim_accounts_delete purple_accounts_delete
-#define gaim_accounts_reorder purple_accounts_reorder
-
-#define gaim_accounts_get_all purple_accounts_get_all
-#define gaim_accounts_get_all_active purple_accounts_get_all_active
-
-#define gaim_accounts_find purple_accounts_find
-
-#define gaim_accounts_restore_current_statuses purple_accounts_restore_current_statuses
-
-#define gaim_accounts_set_ui_ops purple_accounts_set_ui_ops
-#define gaim_accounts_get_ui_ops purple_accounts_get_ui_ops
-
-#define gaim_accounts_get_handle purple_accounts_get_handle
-
-#define gaim_accounts_init purple_accounts_init
-#define gaim_accounts_uninit purple_accounts_uninit
-
-/* from accountopt.h */
-
-#define GaimAccountOption PurpleAccountOption
-#define GaimAccountUserSplit PurpleAccountUserSplit
-
-#define gaim_account_option_new purple_account_option_new
-#define gaim_account_option_bool_new purple_account_option_bool_new
-#define gaim_account_option_int_new purple_account_option_int_new
-#define gaim_account_option_string_new purple_account_option_string_new
-#define gaim_account_option_list_new purple_account_option_list_new
-
-#define gaim_account_option_destroy purple_account_option_destroy
-
-#define gaim_account_option_set_default_bool purple_account_option_set_default_bool
-#define gaim_account_option_set_default_int purple_account_option_set_default_int
-#define gaim_account_option_set_default_string purple_account_option_set_default_string
-
-#define gaim_account_option_set_masked purple_account_option_set_masked
-
-#define gaim_account_option_set_list purple_account_option_set_list
-
-#define gaim_account_option_add_list_item purple_account_option_add_list_item
-
-#define gaim_account_option_get_type purple_account_option_get_type
-#define gaim_account_option_get_text purple_account_option_get_text
-#define gaim_account_option_get_setting purple_account_option_get_setting
-
-#define gaim_account_option_get_default_bool purple_account_option_get_default_bool
-#define gaim_account_option_get_default_int purple_account_option_get_default_int
-#define gaim_account_option_get_default_string purple_account_option_get_default_string
-#define gaim_account_option_get_default_list_value purple_account_option_get_default_list_value
-
-#define gaim_account_option_get_masked purple_account_option_get_masked
-#define gaim_account_option_get_list purple_account_option_get_list
-
-#define gaim_account_user_split_new purple_account_user_split_new
-#define gaim_account_user_split_destroy purple_account_user_split_destroy
-
-#define gaim_account_user_split_get_text purple_account_user_split_get_text
-#define gaim_account_user_split_get_default_value purple_account_user_split_get_default_value
-#define gaim_account_user_split_get_separator purple_account_user_split_get_separator
-
-/* from blist.h */
-
-#define GaimBuddyList PurpleBuddyList
-#define GaimBlistUiOps PurpleBlistUiOps
-#define GaimBlistNode PurpleBlistNode
-
-#define GaimChat PurpleChat
-#define GaimGroup PurpleGroup
-#define GaimContact PurpleContact
-#define GaimBuddy PurpleBuddy
-
-#define GAIM_BLIST_GROUP_NODE PURPLE_BLIST_GROUP_NODE
-#define GAIM_BLIST_CONTACT_NODE PURPLE_BLIST_CONTACT_NODE
-#define GAIM_BLIST_BUDDY_NODE PURPLE_BLIST_BUDDY_NODE
-#define GAIM_BLIST_CHAT_NODE PURPLE_BLIST_CHAT_NODE
-#define GAIM_BLIST_OTHER_NODE PURPLE_BLIST_OTHER_NODE
-#define GaimBlistNodeType PurpleBlistNodeType
-
-#define GAIM_BLIST_NODE_IS_CHAT PURPLE_BLIST_NODE_IS_CHAT
-#define GAIM_BLIST_NODE_IS_BUDDY PURPLE_BLIST_NODE_IS_BUDDY
-#define GAIM_BLIST_NODE_IS_CONTACT PURPLE_BLIST_NODE_IS_CONTACT
-#define GAIM_BLIST_NODE_IS_GROUP PURPLE_BLIST_NODE_IS_GROUP
-
-#define GAIM_BUDDY_IS_ONLINE PURPLE_BUDDY_IS_ONLINE
-
-#define GAIM_BLIST_NODE_FLAG_NO_SAVE PURPLE_BLIST_NODE_FLAG_NO_SAVE
-#define GaimBlistNodeFlags PurpleBlistNodeFlags
-
-#define GAIM_BLIST_NODE_HAS_FLAG PURPLE_BLIST_NODE_HAS_FLAG
-#define GAIM_BLIST_NODE_SHOULD_SAVE PURPLE_BLIST_NODE_SHOULD_SAVE
-
-#define GAIM_BLIST_NODE_NAME PURPLE_BLIST_NODE_NAME
-
-
-#define gaim_blist_new purple_blist_new
-#define gaim_set_blist purple_set_blist
-#define gaim_get_blist purple_get_blist
-
-#define gaim_blist_get_root purple_blist_get_root
-#define gaim_blist_node_next purple_blist_node_next
-
-#define gaim_blist_show purple_blist_show
-
-#define gaim_blist_destroy purple_blist_destroy
-
-#define gaim_blist_set_visible purple_blist_set_visible
-
-#define gaim_blist_update_buddy_status purple_blist_update_buddy_status
-#define gaim_blist_update_buddy_icon purple_blist_update_buddy_icon
-
-
-#define gaim_blist_alias_contact purple_blist_alias_contact
-#define gaim_blist_alias_buddy purple_blist_alias_buddy
-#define gaim_blist_server_alias_buddy purple_blist_server_alias_buddy
-#define gaim_blist_alias_chat purple_blist_alias_chat
-
-#define gaim_blist_rename_buddy purple_blist_rename_buddy
-#define gaim_blist_rename_group purple_blist_rename_group
-
-#define gaim_chat_new purple_chat_new
-#define gaim_blist_add_chat purple_blist_add_chat
-
-#define gaim_buddy_new purple_buddy_new
-#define gaim_buddy_set_icon purple_buddy_set_icon
-#define gaim_buddy_get_account purple_buddy_get_account
-#define gaim_buddy_get_name purple_buddy_get_name
-#define gaim_buddy_get_icon purple_buddy_get_icon
-#define gaim_buddy_get_contact purple_buddy_get_contact
-#define gaim_buddy_get_presence purple_buddy_get_presence
-
-#define gaim_blist_add_buddy purple_blist_add_buddy
-
-#define gaim_group_new purple_group_new
-
-#define gaim_blist_add_group purple_blist_add_group
-
-#define gaim_contact_new purple_contact_new
-
-#define gaim_blist_add_contact purple_blist_add_contact
-#define gaim_blist_merge_contact purple_blist_merge_contact
-
-#define gaim_contact_get_priority_buddy purple_contact_get_priority_buddy
-#define gaim_contact_set_alias purple_contact_set_alias
-#define gaim_contact_get_alias purple_contact_get_alias
-#define gaim_contact_on_account purple_contact_on_account
-
-#define gaim_contact_invalidate_priority_buddy purple_contact_invalidate_priority_buddy
-
-#define gaim_blist_remove_buddy purple_blist_remove_buddy
-#define gaim_blist_remove_contact purple_blist_remove_contact
-#define gaim_blist_remove_chat purple_blist_remove_chat
-#define gaim_blist_remove_group purple_blist_remove_group
-
-#define gaim_buddy_get_alias_only purple_buddy_get_alias_only
-#define gaim_buddy_get_server_alias purple_buddy_get_server_alias
-#define gaim_buddy_get_contact_alias purple_buddy_get_contact_alias
-#define gaim_buddy_get_local_alias purple_buddy_get_local_alias
-#define gaim_buddy_get_alias purple_buddy_get_alias
-
-#define gaim_chat_get_name purple_chat_get_name
-
-#define gaim_find_buddy purple_find_buddy
-#define gaim_find_buddy_in_group purple_find_buddy_in_group
-#define gaim_find_buddies purple_find_buddies
-
-#define gaim_find_group purple_find_group
-
-#define gaim_blist_find_chat purple_blist_find_chat
-
-#define gaim_chat_get_group purple_chat_get_group
-#define gaim_buddy_get_group purple_buddy_get_group
-
-#define gaim_group_get_accounts purple_group_get_accounts
-#define gaim_group_on_account purple_group_on_account
-
-#define gaim_blist_add_account purple_blist_add_account
-#define gaim_blist_remove_account purple_blist_remove_account
-
-#define gaim_blist_get_group_size purple_blist_get_group_size
-#define gaim_blist_get_group_online_count purple_blist_get_group_online_count
-
-#define gaim_blist_load purple_blist_load
-#define gaim_blist_schedule_save purple_blist_schedule_save
-
-#define gaim_blist_request_add_buddy purple_blist_request_add_buddy
-#define gaim_blist_request_add_chat purple_blist_request_add_chat
-#define gaim_blist_request_add_group purple_blist_request_add_group
-
-#define gaim_blist_node_set_bool purple_blist_node_set_bool
-#define gaim_blist_node_get_bool purple_blist_node_get_bool
-#define gaim_blist_node_set_int purple_blist_node_set_int
-#define gaim_blist_node_get_int purple_blist_node_get_int
-#define gaim_blist_node_set_string purple_blist_node_set_string
-#define gaim_blist_node_get_string purple_blist_node_get_string
-
-#define gaim_blist_node_remove_setting purple_blist_node_remove_setting
-
-#define gaim_blist_node_set_flags purple_blist_node_set_flags
-#define gaim_blist_node_get_flags purple_blist_node_get_flags
-
-#define gaim_blist_node_get_extended_menu purple_blist_node_get_extended_menu
-
-#define gaim_blist_set_ui_ops purple_blist_set_ui_ops
-#define gaim_blist_get_ui_ops purple_blist_get_ui_ops
-
-#define gaim_blist_get_handle purple_blist_get_handle
-
-#define gaim_blist_init purple_blist_init
-#define gaim_blist_uninit purple_blist_uninit
-
-
-#define GaimBuddyIcon PurpleBuddyIcon
-
-#define gaim_buddy_icon_new(account, username, icon_data, icon_len)\
- purple_buddy_icon_new(account, username, g_memdup(icon_data, icon_len), icon_len)
-#define gaim_buddy_icon_ref purple_buddy_icon_ref
-#define gaim_buddy_icon_unref purple_buddy_icon_unref
-#define gaim_buddy_icon_update purple_buddy_icon_update
-
-#define gaim_buddy_icon_set_data(icon, data, len) \
- purple_buddy_icon_set_data(icon, g_memdup(data, len), len, NULL);
-
-#define gaim_buddy_icon_get_account purple_buddy_icon_get_account
-#define gaim_buddy_icon_get_username purple_buddy_icon_get_username
-#define gaim_buddy_icon_get_data purple_buddy_icon_get_data
-#define gaim_buddy_icon_get_type purple_buddy_icon_get_extension
-
-#define gaim_buddy_icons_set_for_user(icon, data, len) \
- purple_buddy_icons_set_for_user(icon, g_memdup(data, len), len, NULL)
-#define gaim_buddy_icons_set_caching purple_buddy_icons_set_caching
-#define gaim_buddy_icons_is_caching purple_buddy_icons_is_caching
-#define gaim_buddy_icons_set_cache_dir purple_buddy_icons_set_cache_dir
-#define gaim_buddy_icons_get_cache_dir purple_buddy_icons_get_cache_dir
-#define gaim_buddy_icons_get_handle purple_buddy_icons_get_handle
-
-#define gaim_buddy_icons_init purple_buddy_icons_init
-#define gaim_buddy_icons_uninit purple_buddy_icons_uninit
-
-#define gaim_buddy_icon_get_scale_size purple_buddy_icon_get_scale_size
-
-/* from cipher.h */
-
-#define GAIM_CIPHER PURPLE_CIPHER
-#define GAIM_CIPHER_OPS PURPLE_CIPHER_OPS
-#define GAIM_CIPHER_CONTEXT PURPLE_CIPHER_CONTEXT
-
-#define GaimCipher PurpleCipher
-#define GaimCipherOps PurpleCipherOps
-#define GaimCipherContext PurpleCipherContext
-
-#define GAIM_CIPHER_CAPS_SET_OPT PURPLE_CIPHER_CAPS_SET_OPT
-#define GAIM_CIPHER_CAPS_GET_OPT PURPLE_CIPHER_CAPS_GET_OPT
-#define GAIM_CIPHER_CAPS_INIT PURPLE_CIPHER_CAPS_INIT
-#define GAIM_CIPHER_CAPS_RESET PURPLE_CIPHER_CAPS_RESET
-#define GAIM_CIPHER_CAPS_UNINIT PURPLE_CIPHER_CAPS_UNINIT
-#define GAIM_CIPHER_CAPS_SET_IV PURPLE_CIPHER_CAPS_SET_IV
-#define GAIM_CIPHER_CAPS_APPEND PURPLE_CIPHER_CAPS_APPEND
-#define GAIM_CIPHER_CAPS_DIGEST PURPLE_CIPHER_CAPS_DIGEST
-#define GAIM_CIPHER_CAPS_ENCRYPT PURPLE_CIPHER_CAPS_ENCRYPT
-#define GAIM_CIPHER_CAPS_DECRYPT PURPLE_CIPHER_CAPS_DECRYPT
-#define GAIM_CIPHER_CAPS_SET_SALT PURPLE_CIPHER_CAPS_SET_SALT
-#define GAIM_CIPHER_CAPS_GET_SALT_SIZE PURPLE_CIPHER_CAPS_GET_SALT_SIZE
-#define GAIM_CIPHER_CAPS_SET_KEY PURPLE_CIPHER_CAPS_SET_KEY
-#define GAIM_CIPHER_CAPS_GET_KEY_SIZE PURPLE_CIPHER_CAPS_GET_KEY_SIZE
-#define GAIM_CIPHER_CAPS_UNKNOWN PURPLE_CIPHER_CAPS_UNKNOWN
-
-#define gaim_cipher_get_name purple_cipher_get_name
-#define gaim_cipher_get_capabilities purple_cipher_get_capabilities
-#define gaim_cipher_digest_region purple_cipher_digest_region
-
-#define gaim_ciphers_find_cipher purple_ciphers_find_cipher
-#define gaim_ciphers_register_cipher purple_ciphers_register_cipher
-#define gaim_ciphers_unregister_cipher purple_ciphers_unregister_cipher
-#define gaim_ciphers_get_ciphers purple_ciphers_get_ciphers
-
-#define gaim_ciphers_get_handle purple_ciphers_get_handle
-#define gaim_ciphers_init purple_ciphers_init
-#define gaim_ciphers_uninit purple_ciphers_uninit
-
-#define gaim_cipher_context_set_option purple_cipher_context_set_option
-#define gaim_cipher_context_get_option purple_cipher_context_get_option
-
-#define gaim_cipher_context_new purple_cipher_context_new
-#define gaim_cipher_context_new_by_name purple_cipher_context_new_by_name
-#define gaim_cipher_context_reset purple_cipher_context_reset
-#define gaim_cipher_context_destroy purple_cipher_context_destroy
-#define gaim_cipher_context_set_iv purple_cipher_context_set_iv
-#define gaim_cipher_context_append purple_cipher_context_append
-#define gaim_cipher_context_digest purple_cipher_context_digest
-#define gaim_cipher_context_digest_to_str purple_cipher_context_digest_to_str
-#define gaim_cipher_context_encrypt purple_cipher_context_encrypt
-#define gaim_cipher_context_decrypt purple_cipher_context_decrypt
-#define gaim_cipher_context_set_salt purple_cipher_context_set_salt
-#define gaim_cipher_context_get_salt_size purple_cipher_context_get_salt_size
-#define gaim_cipher_context_set_key purple_cipher_context_set_key
-#define gaim_cipher_context_get_key_size purple_cipher_context_get_key_size
-#define gaim_cipher_context_set_data purple_cipher_context_set_data
-#define gaim_cipher_context_get_data purple_cipher_context_get_data
-
-#define gaim_cipher_http_digest_calculate_session_key \
- purple_cipher_http_digest_calculate_session_key
-
-#define gaim_cipher_http_digest_calculate_response \
- purple_cipher_http_digest_calculate_response
-
-/* from circbuffer.h */
-
-#define GaimCircBuffer PurpleCircBuffer
-
-#define gaim_circ_buffer_new purple_circ_buffer_new
-#define gaim_circ_buffer_destroy purple_circ_buffer_destroy
-#define gaim_circ_buffer_append purple_circ_buffer_append
-#define gaim_circ_buffer_get_max_read purple_circ_buffer_get_max_read
-#define gaim_circ_buffer_mark_read purple_circ_buffer_mark_read
-
-/* from cmds.h */
-
-#define GaimCmdPriority PurpleCmdPriority
-#define GaimCmdFlag PurpleCmdFlag
-#define GaimCmdStatus PurpleCmdStatus
-#define GaimCmdRet PurpleCmdRet
-
-#define GAIM_CMD_STATUS_OK PURPLE_CMD_STATUS_OK
-#define GAIM_CMD_STATUS_FAILED PURPLE_CMD_STATUS_FAILED
-#define GAIM_CMD_STATUS_NOT_FOUND PURPLE_CMD_STATUS_NOT_FOUND
-#define GAIM_CMD_STATUS_WRONG_ARGS PURPLE_CMD_STATUS_WRONG_ARGS
-#define GAIM_CMD_STATUS_WRONG_PRPL PURPLE_CMD_STATUS_WRONG_PRPL
-#define GAIM_CMD_STATUS_WRONG_TYPE PURPLE_CMD_STATUS_WRONG_TYPE
-
-#define GAIM_CMD_FUNC PURPLE_CMD_FUNC
-
-#define GAIM_CMD_RET_OK PURPLE_CMD_RET_OK
-#define GAIM_CMD_RET_FAILED PURPLE_CMD_RET_FAILED
-#define GAIM_CMD_RET_CONTINUE PURPLE_CMD_RET_CONTINUE
-
-#define GAIM_CMD_P_VERY_LOW PURPLE_CMD_P_VERY_LOW
-#define GAIM_CMD_P_LOW PURPLE_CMD_P_LOW
-#define GAIM_CMD_P_DEFAULT PURPLE_CMD_P_DEFAULT
-#define GAIM_CMD_P_PRPL PURPLE_CMD_P_PRPL
-#define GAIM_CMD_P_PLUGIN PURPLE_CMD_P_PLUGIN
-#define GAIM_CMD_P_ALIAS PURPLE_CMD_P_ALIAS
-#define GAIM_CMD_P_HIGH PURPLE_CMD_P_HIGH
-#define GAIM_CMD_P_VERY_HIGH PURPLE_CMD_P_VERY_HIGH
-
-#define GAIM_CMD_FLAG_IM PURPLE_CMD_FLAG_IM
-#define GAIM_CMD_FLAG_CHAT PURPLE_CMD_FLAG_CHAT
-#define GAIM_CMD_FLAG_PRPL_ONLY PURPLE_CMD_FLAG_PRPL_ONLY
-#define GAIM_CMD_FLAG_ALLOW_WRONG_ARGS PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
-
-
-#define GaimCmdFunc PurpleCmdFunc
-
-#define GaimCmdId PurpleCmdId
-
-#define gaim_cmd_register purple_cmd_register
-#define gaim_cmd_unregister purple_cmd_unregister
-#define gaim_cmd_do_command purple_cmd_do_command
-#define gaim_cmd_list purple_cmd_list
-#define gaim_cmd_help purple_cmd_help
-
-/* from connection.h */
-
-#define GaimConnection PurpleConnection
-
-#define GAIM_CONNECTION_HTML PURPLE_CONNECTION_HTML
-#define GAIM_CONNECTION_NO_BGCOLOR PURPLE_CONNECTION_NO_BGCOLOR
-#define GAIM_CONNECTION_AUTO_RESP PURPLE_CONNECTION_AUTO_RESP
-#define GAIM_CONNECTION_FORMATTING_WBFO PURPLE_CONNECTION_FORMATTING_WBFO
-#define GAIM_CONNECTION_NO_NEWLINES PURPLE_CONNECTION_NO_NEWLINES
-#define GAIM_CONNECTION_NO_FONTSIZE PURPLE_CONNECTION_NO_FONTSIZE
-#define GAIM_CONNECTION_NO_URLDESC PURPLE_CONNECTION_NO_URLDESC
-#define GAIM_CONNECTION_NO_IMAGES PURPLE_CONNECTION_NO_IMAGES
-
-#define GaimConnectionFlags PurpleConnectionFlags
-
-#define GAIM_DISCONNECTED PURPLE_DISCONNECTED
-#define GAIM_CONNECTED PURPLE_CONNECTED
-#define GAIM_CONNECTING PURPLE_CONNECTING
-
-#define GaimConnectionState PurpleConnectionState
-
-#define GaimConnectionUiOps PurpleConnectionUiOps
-
-#define gaim_connection_new purple_connection_new
-#define gaim_connection_destroy purple_connection_destroy
-
-#define gaim_connection_set_state purple_connection_set_state
-#define gaim_connection_set_account purple_connection_set_account
-#define gaim_connection_set_display_name purple_connection_set_display_name
-#define gaim_connection_get_state purple_connection_get_state
-
-#define GAIM_CONNECTION_IS_CONNECTED PURPLE_CONNECTION_IS_CONNECTED
-
-#define gaim_connection_get_account purple_connection_get_account
-#define gaim_connection_get_password purple_connection_get_password
-#define gaim_connection_get_display_name purple_connection_get_display_name
-
-#define gaim_connection_update_progress purple_connection_update_progress
-
-#define gaim_connection_notice purple_connection_notice
-#define gaim_connection_error purple_connection_error
-
-#define gaim_connections_disconnect_all purple_connections_disconnect_all
-
-#define gaim_connections_get_all purple_connections_get_all
-#define gaim_connections_get_connecting purple_connections_get_connecting
-
-#define GAIM_CONNECTION_IS_VALID PURPLE_CONNECTION_IS_VALID
-
-#define gaim_connections_set_ui_ops purple_connections_set_ui_ops
-#define gaim_connections_get_ui_ops purple_connections_get_ui_ops
-
-#define gaim_connections_init purple_connections_init
-#define gaim_connections_uninit purple_connections_uninit
-#define gaim_connections_get_handle purple_connections_get_handle
-
-
-/* from conversation.h */
-
-#define GaimConversationUiOps PurpleConversationUiOps
-#define GaimConversation PurpleConversation
-#define GaimConvIm PurpleConvIm
-#define GaimConvChat PurpleConvChat
-#define GaimConvChatBuddy PurpleConvChatBuddy
-
-#define GAIM_CONV_TYPE_UNKNOWN PURPLE_CONV_TYPE_UNKNOWN
-#define GAIM_CONV_TYPE_IM PURPLE_CONV_TYPE_IM
-#define GAIM_CONV_TYPE_CHAT PURPLE_CONV_TYPE_CHAT
-#define GAIM_CONV_TYPE_MISC PURPLE_CONV_TYPE_MISC
-#define GAIM_CONV_TYPE_ANY PURPLE_CONV_TYPE_ANY
-
-#define GaimConversationType PurpleConversationType
-
-#define GAIM_CONV_UPDATE_ADD PURPLE_CONV_UPDATE_ADD
-#define GAIM_CONV_UPDATE_REMOVE PURPLE_CONV_UPDATE_REMOVE
-#define GAIM_CONV_UPDATE_ACCOUNT PURPLE_CONV_UPDATE_ACCOUNT
-#define GAIM_CONV_UPDATE_TYPING PURPLE_CONV_UPDATE_TYPING
-#define GAIM_CONV_UPDATE_UNSEEN PURPLE_CONV_UPDATE_UNSEEN
-#define GAIM_CONV_UPDATE_LOGGING PURPLE_CONV_UPDATE_LOGGING
-#define GAIM_CONV_UPDATE_TOPIC PURPLE_CONV_UPDATE_TOPIC
-#define GAIM_CONV_ACCOUNT_ONLINE PURPLE_CONV_ACCOUNT_ONLINE
-#define GAIM_CONV_ACCOUNT_OFFLINE PURPLE_CONV_ACCOUNT_OFFLINE
-#define GAIM_CONV_UPDATE_AWAY PURPLE_CONV_UPDATE_AWAY
-#define GAIM_CONV_UPDATE_ICON PURPLE_CONV_UPDATE_ICON
-#define GAIM_CONV_UPDATE_TITLE PURPLE_CONV_UPDATE_TITLE
-#define GAIM_CONV_UPDATE_CHATLEFT PURPLE_CONV_UPDATE_CHATLEFT
-#define GAIM_CONV_UPDATE_FEATURES PURPLE_CONV_UPDATE_FEATURES
-
-#define GaimConvUpdateType PurpleConvUpdateType
-
-#define GAIM_NOT_TYPING PURPLE_NOT_TYPING
-#define GAIM_TYPING PURPLE_TYPING
-#define GAIM_TYPED PURPLE_TYPED
-
-#define GaimTypingState PurpleTypingState
-
-#define GAIM_MESSAGE_SEND PURPLE_MESSAGE_SEND
-#define GAIM_MESSAGE_RECV PURPLE_MESSAGE_RECV
-#define GAIM_MESSAGE_SYSTEM PURPLE_MESSAGE_SYSTEM
-#define GAIM_MESSAGE_AUTO_RESP PURPLE_MESSAGE_AUTO_RESP
-#define GAIM_MESSAGE_ACTIVE_ONLY PURPLE_MESSAGE_ACTIVE_ONLY
-#define GAIM_MESSAGE_NICK PURPLE_MESSAGE_NICK
-#define GAIM_MESSAGE_NO_LOG PURPLE_MESSAGE_NO_LOG
-#define GAIM_MESSAGE_WHISPER PURPLE_MESSAGE_WHISPER
-#define GAIM_MESSAGE_ERROR PURPLE_MESSAGE_ERROR
-#define GAIM_MESSAGE_DELAYED PURPLE_MESSAGE_DELAYED
-#define GAIM_MESSAGE_RAW PURPLE_MESSAGE_RAW
-#define GAIM_MESSAGE_IMAGES PURPLE_MESSAGE_IMAGES
-
-#define GaimMessageFlags PurpleMessageFlags
-
-#define GAIM_CBFLAGS_NONE PURPLE_CBFLAGS_NONE
-#define GAIM_CBFLAGS_VOICE PURPLE_CBFLAGS_VOICE
-#define GAIM_CBFLAGS_HALFOP PURPLE_CBFLAGS_HALFOP
-#define GAIM_CBFLAGS_OP PURPLE_CBFLAGS_OP
-#define GAIM_CBFLAGS_FOUNDER PURPLE_CBFLAGS_FOUNDER
-#define GAIM_CBFLAGS_TYPING PURPLE_CBFLAGS_TYPING
-
-#define GaimConvChatBuddyFlags PurpleConvChatBuddyFlags
-
-#define gaim_conversations_set_ui_ops purple_conversations_set_ui_ops
-
-#define gaim_conversation_new purple_conversation_new
-#define gaim_conversation_destroy purple_conversation_destroy
-#define gaim_conversation_present purple_conversation_present
-#define gaim_conversation_get_type purple_conversation_get_type
-#define gaim_conversation_set_ui_ops purple_conversation_set_ui_ops
-#define gaim_conversation_get_ui_ops purple_conversation_get_ui_ops
-#define gaim_conversation_set_account purple_conversation_set_account
-#define gaim_conversation_get_account purple_conversation_get_account
-#define gaim_conversation_get_gc purple_conversation_get_gc
-#define gaim_conversation_set_title purple_conversation_set_title
-#define gaim_conversation_get_title purple_conversation_get_title
-#define gaim_conversation_autoset_title purple_conversation_autoset_title
-#define gaim_conversation_set_name purple_conversation_set_name
-#define gaim_conversation_get_name purple_conversation_get_name
-#define gaim_conversation_set_logging purple_conversation_set_logging
-#define gaim_conversation_is_logging purple_conversation_is_logging
-#define gaim_conversation_close_logs purple_conversation_close_logs
-#define gaim_conversation_get_im_data purple_conversation_get_im_data
-
-#define GAIM_CONV_IM PURPLE_CONV_IM
-
-#define gaim_conversation_get_chat_data purple_conversation_get_chat_data
-
-#define GAIM_CONV_CHAT PURPLE_CONV_CHAT
-
-#define gaim_conversation_set_data purple_conversation_set_data
-#define gaim_conversation_get_data purple_conversation_get_data
-
-#define gaim_get_conversations purple_get_conversations
-#define gaim_get_ims purple_get_ims
-#define gaim_get_chats purple_get_chats
-
-#define gaim_find_conversation_with_account \
- purple_find_conversation_with_account
-
-#define gaim_conversation_write purple_conversation_write
-#define gaim_conversation_set_features purple_conversation_set_features
-#define gaim_conversation_get_features purple_conversation_get_features
-#define gaim_conversation_has_focus purple_conversation_has_focus
-#define gaim_conversation_update purple_conversation_update
-#define gaim_conversation_foreach purple_conversation_foreach
-
-#define gaim_conv_im_get_conversation purple_conv_im_get_conversation
-#define gaim_conv_im_set_icon purple_conv_im_set_icon
-#define gaim_conv_im_get_icon purple_conv_im_get_icon
-#define gaim_conv_im_set_typing_state purple_conv_im_set_typing_state
-#define gaim_conv_im_get_typing_state purple_conv_im_get_typing_state
-
-#define gaim_conv_im_start_typing_timeout purple_conv_im_start_typing_timeout
-#define gaim_conv_im_stop_typing_timeout purple_conv_im_stop_typing_timeout
-#define gaim_conv_im_get_typing_timeout purple_conv_im_get_typing_timeout
-#define gaim_conv_im_set_type_again purple_conv_im_set_type_again
-#define gaim_conv_im_get_type_again purple_conv_im_get_type_again
-
-#define gaim_conv_im_start_send_typed_timeout \
- purple_conv_im_start_send_typed_timeout
-
-#define gaim_conv_im_stop_send_typed_timeout \
- purple_conv_im_stop_send_typed_timeout
-
-#define gaim_conv_im_get_send_typed_timeout \
- purple_conv_im_get_send_typed_timeout
-
-#define gaim_conv_present_error purple_conv_present_error
-#define gaim_conv_send_confirm purple_conv_send_confirm
-
-#define gaim_conv_im_update_typing purple_conv_im_update_typing
-#define gaim_conv_im_write purple_conv_im_write
-#define gaim_conv_im_send purple_conv_im_send
-#define gaim_conv_im_send_with_flags purple_conv_im_send_with_flags
-
-#define gaim_conv_custom_smiley_add purple_conv_custom_smiley_add
-#define gaim_conv_custom_smiley_write purple_conv_custom_smiley_write
-#define gaim_conv_custom_smiley_close purple_conv_custom_smiley_close
-
-#define gaim_conv_chat_get_conversation purple_conv_chat_get_conversation
-#define gaim_conv_chat_set_users purple_conv_chat_set_users
-#define gaim_conv_chat_get_users purple_conv_chat_get_users
-#define gaim_conv_chat_ignore purple_conv_chat_ignore
-#define gaim_conv_chat_unignore purple_conv_chat_unignore
-#define gaim_conv_chat_set_ignored purple_conv_chat_set_ignored
-#define gaim_conv_chat_get_ignored purple_conv_chat_get_ignored
-#define gaim_conv_chat_get_ignored_user purple_conv_chat_get_ignored_user
-#define gaim_conv_chat_is_user_ignored purple_conv_chat_is_user_ignored
-#define gaim_conv_chat_set_topic purple_conv_chat_set_topic
-#define gaim_conv_chat_get_topic purple_conv_chat_get_topic
-#define gaim_conv_chat_set_id purple_conv_chat_set_id
-#define gaim_conv_chat_get_id purple_conv_chat_get_id
-#define gaim_conv_chat_write purple_conv_chat_write
-#define gaim_conv_chat_send purple_conv_chat_send
-#define gaim_conv_chat_send_with_flags purple_conv_chat_send_with_flags
-#define gaim_conv_chat_add_user purple_conv_chat_add_user
-#define gaim_conv_chat_add_users purple_conv_chat_add_users
-#define gaim_conv_chat_rename_user purple_conv_chat_rename_user
-#define gaim_conv_chat_remove_user purple_conv_chat_remove_user
-#define gaim_conv_chat_remove_users purple_conv_chat_remove_users
-#define gaim_conv_chat_find_user purple_conv_chat_find_user
-#define gaim_conv_chat_user_set_flags purple_conv_chat_user_set_flags
-#define gaim_conv_chat_user_get_flags purple_conv_chat_user_get_flags
-#define gaim_conv_chat_clear_users purple_conv_chat_clear_users
-#define gaim_conv_chat_set_nick purple_conv_chat_set_nick
-#define gaim_conv_chat_get_nick purple_conv_chat_get_nick
-#define gaim_conv_chat_left purple_conv_chat_left
-#define gaim_conv_chat_has_left purple_conv_chat_has_left
-
-#define gaim_find_chat purple_find_chat
-
-#define gaim_conv_chat_cb_new purple_conv_chat_cb_new
-#define gaim_conv_chat_cb_find purple_conv_chat_cb_find
-#define gaim_conv_chat_cb_get_name purple_conv_chat_cb_get_name
-#define gaim_conv_chat_cb_destroy purple_conv_chat_cb_destroy
-
-#define gaim_conversations_get_handle purple_conversations_get_handle
-#define gaim_conversations_init purple_conversations_init
-#define gaim_conversations_uninit purple_conversations_uninit
-
-/* from core.h */
-
-#define GaimCore PurpleCore
-
-#define GaimCoreUiOps PurpleCoreUiOps
-
-#define gaim_core_init purple_core_init
-#define gaim_core_quit purple_core_quit
-
-#define gaim_core_quit_cb purple_core_quit_cb
-#define gaim_core_get_version purple_core_get_version
-#define gaim_core_get_ui purple_core_get_ui
-#define gaim_get_core purple_get_core
-#define gaim_core_set_ui_ops purple_core_set_ui_ops
-#define gaim_core_get_ui_ops purple_core_get_ui_ops
-
-/* from debug.h */
-
-#define GAIM_DEBUG_ALL PURPLE_DEBUG_ALL
-#define GAIM_DEBUG_MISC PURPLE_DEBUG_MISC
-#define GAIM_DEBUG_INFO PURPLE_DEBUG_INFO
-#define GAIM_DEBUG_WARNING PURPLE_DEBUG_WARNING
-#define GAIM_DEBUG_ERROR PURPLE_DEBUG_ERROR
-#define GAIM_DEBUG_FATAL PURPLE_DEBUG_FATAL
-
-#define GaimDebugLevel PurpleDebugLevel
-
-#define GaimDebugUiOps PurpleDebugUiOps
-
-
-#define gaim_debug purple_debug
-#define gaim_debug_misc purple_debug_misc
-#define gaim_debug_info purple_debug_info
-#define gaim_debug_warning purple_debug_warning
-#define gaim_debug_error purple_debug_error
-#define gaim_debug_fatal purple_debug_fatal
-
-#define gaim_debug_set_enabled purple_debug_set_enabled
-#define gaim_debug_is_enabled purple_debug_is_enabled
-
-#define gaim_debug_set_ui_ops purple_debug_set_ui_ops
-#define gaim_debug_get_ui_ops purple_debug_get_ui_ops
-
-#define gaim_debug_init purple_debug_init
-
-/* from desktopitem.h */
-
-#define GAIM_DESKTOP_ITEM_TYPE_NULL PURPLE_DESKTOP_ITEM_TYPE_NULL
-#define GAIM_DESKTOP_ITEM_TYPE_OTHER PURPLE_DESKTOP_ITEM_TYPE_OTHER
-#define GAIM_DESKTOP_ITEM_TYPE_APPLICATION PURPLE_DESKTOP_ITEM_TYPE_APPLICATION
-#define GAIM_DESKTOP_ITEM_TYPE_LINK PURPLE_DESKTOP_ITEM_TYPE_LINK
-#define GAIM_DESKTOP_ITEM_TYPE_FSDEVICE PURPLE_DESKTOP_ITEM_TYPE_FSDEVICE
-#define GAIM_DESKTOP_ITEM_TYPE_MIME_TYPE PURPLE_DESKTOP_ITEM_TYPE_MIME_TYPE
-#define GAIM_DESKTOP_ITEM_TYPE_DIRECTORY PURPLE_DESKTOP_ITEM_TYPE_DIRECTORY
-#define GAIM_DESKTOP_ITEM_TYPE_SERVICE PURPLE_DESKTOP_ITEM_TYPE_SERVICE
-#define GAIM_DESKTOP_ITEM_TYPE_SERVICE_TYPE PURPLE_DESKTOP_ITEM_TYPE_SERVICE_TYPE
-
-#define GaimDesktopItemType PurpleDesktopItemType
-
-#define GaimDesktopItem PurpleDesktopItem
-
-#define GAIM_TYPE_DESKTOP_ITEM PURPLE_TYPE_DESKTOP_ITEM
-#define gaim_desktop_item_get_type purple_desktop_item_get_type
-
-/* standard */
-/* ugh, i'm just copying these as strings, rather than pidginifying them */
-#define GAIM_DESKTOP_ITEM_ENCODING "Encoding" /* string */
-#define GAIM_DESKTOP_ITEM_VERSION "Version" /* numeric */
-#define GAIM_DESKTOP_ITEM_NAME "Name" /* localestring */
-#define GAIM_DESKTOP_ITEM_GENERIC_NAME "GenericName" /* localestring */
-#define GAIM_DESKTOP_ITEM_TYPE "Type" /* string */
-#define GAIM_DESKTOP_ITEM_FILE_PATTERN "FilePattern" /* regexp(s) */
-#define GAIM_DESKTOP_ITEM_TRY_EXEC "TryExec" /* string */
-#define GAIM_DESKTOP_ITEM_NO_DISPLAY "NoDisplay" /* boolean */
-#define GAIM_DESKTOP_ITEM_COMMENT "Comment" /* localestring */
-#define GAIM_DESKTOP_ITEM_EXEC "Exec" /* string */
-#define GAIM_DESKTOP_ITEM_ACTIONS "Actions" /* strings */
-#define GAIM_DESKTOP_ITEM_ICON "Icon" /* string */
-#define GAIM_DESKTOP_ITEM_MINI_ICON "MiniIcon" /* string */
-#define GAIM_DESKTOP_ITEM_HIDDEN "Hidden" /* boolean */
-#define GAIM_DESKTOP_ITEM_PATH "Path" /* string */
-#define GAIM_DESKTOP_ITEM_TERMINAL "Terminal" /* boolean */
-#define GAIM_DESKTOP_ITEM_TERMINAL_OPTIONS "TerminalOptions" /* string */
-#define GAIM_DESKTOP_ITEM_SWALLOW_TITLE "SwallowTitle" /* string */
-#define GAIM_DESKTOP_ITEM_SWALLOW_EXEC "SwallowExec" /* string */
-#define GAIM_DESKTOP_ITEM_MIME_TYPE "MimeType" /* regexp(s) */
-#define GAIM_DESKTOP_ITEM_PATTERNS "Patterns" /* regexp(s) */
-#define GAIM_DESKTOP_ITEM_DEFAULT_APP "DefaultApp" /* string */
-#define GAIM_DESKTOP_ITEM_DEV "Dev" /* string */
-#define GAIM_DESKTOP_ITEM_FS_TYPE "FSType" /* string */
-#define GAIM_DESKTOP_ITEM_MOUNT_POINT "MountPoint" /* string */
-#define GAIM_DESKTOP_ITEM_READ_ONLY "ReadOnly" /* boolean */
-#define GAIM_DESKTOP_ITEM_UNMOUNT_ICON "UnmountIcon" /* string */
-#define GAIM_DESKTOP_ITEM_SORT_ORDER "SortOrder" /* strings */
-#define GAIM_DESKTOP_ITEM_URL "URL" /* string */
-#define GAIM_DESKTOP_ITEM_DOC_PATH "X-GNOME-DocPath" /* string */
-
-#define gaim_desktop_item_new_from_file purple_desktop_item_new_from_file
-#define gaim_desktop_item_get_entry_type purple_desktop_item_get_entry_type
-#define gaim_desktop_item_get_string purple_desktop_item_get_string
-#define gaim_desktop_item_copy purple_desktop_item_copy
-#define gaim_desktop_item_unref purple_desktop_item_unref
-
-/* from dnsquery.h */
-
-#define GaimDnsQueryData PurpleDnsQueryData
-#define GaimDnsQueryConnectFunction PurpleDnsQueryConnectFunction
-
-#define gaim_dnsquery_a purple_dnsquery_a
-#define gaim_dnsquery_destroy purple_dnsquery_destroy
-#define gaim_dnsquery_init purple_dnsquery_init
-#define gaim_dnsquery_uninit purple_dnsquery_uninit
-#define gaim_dnsquery_set_ui_ops purple_dnsquery_set_ui_ops
-#define gaim_dnsquery_get_host purple_dnsquery_get_host
-#define gaim_dnsquery_get_port purple_dnsquery_get_port
-
-/* from dnssrv.h */
-
-#define GaimSrvResponse PurpleSrvResponse
-#define GaimSrvQueryData PurpleSrvTxtQueryData
-#define GaimSrvCallback PurpleSrvCallback
-
-#define gaim_srv_resolve purple_srv_resolve
-#define gaim_srv_cancel purple_srv_cancel
-
-/* from eventloop.h */
-
-#define GAIM_INPUT_READ PURPLE_INPUT_READ
-#define GAIM_INPUT_WRITE PURPLE_INPUT_WRITE
-
-#define GaimInputCondition PurpleInputCondition
-#define GaimInputFunction PurpleInputFunction
-#define GaimEventLoopUiOps PurpleEventLoopUiOps
-
-#define gaim_timeout_add purple_timeout_add
-#define gaim_timeout_remove purple_timeout_remove
-#define gaim_input_add purple_input_add
-#define gaim_input_remove purple_input_remove
-
-#define gaim_eventloop_set_ui_ops purple_eventloop_set_ui_ops
-#define gaim_eventloop_get_ui_ops purple_eventloop_get_ui_ops
-
-/* from ft.h */
-
-#define GaimXfer PurpleXfer
-
-#define GAIM_XFER_UNKNOWN PURPLE_XFER_UNKNOWN
-#define GAIM_XFER_SEND PURPLE_XFER_SEND
-#define GAIM_XFER_RECEIVE PURPLE_XFER_RECEIVE
-
-#define GaimXferType PurpleXferType
-
-#define GAIM_XFER_STATUS_UNKNOWN PURPLE_XFER_STATUS_UNKNOWN
-#define GAIM_XFER_STATUS_NOT_STARTED PURPLE_XFER_STATUS_NOT_STARTED
-#define GAIM_XFER_STATUS_ACCEPTED PURPLE_XFER_STATUS_ACCEPTED
-#define GAIM_XFER_STATUS_STARTED PURPLE_XFER_STATUS_STARTED
-#define GAIM_XFER_STATUS_DONE PURPLE_XFER_STATUS_DONE
-#define GAIM_XFER_STATUS_CANCEL_LOCAL PURPLE_XFER_STATUS_CANCEL_LOCAL
-#define GAIM_XFER_STATUS_CANCEL_REMOTE PURPLE_XFER_STATUS_CANCEL_REMOTE
-
-#define GaimXferStatusType PurpleXferStatusType
-
-#define GaimXferUiOps PurpleXferUiOps
-
-#define gaim_xfer_new purple_xfer_new
-#define gaim_xfer_ref purple_xfer_ref
-#define gaim_xfer_unref purple_xfer_unref
-#define gaim_xfer_request purple_xfer_request
-#define gaim_xfer_request_accepted purple_xfer_request_accepted
-#define gaim_xfer_request_denied purple_xfer_request_denied
-#define gaim_xfer_get_type purple_xfer_get_type
-#define gaim_xfer_get_account purple_xfer_get_account
-#define gaim_xfer_get_status purple_xfer_get_status
-#define gaim_xfer_is_canceled purple_xfer_is_canceled
-#define gaim_xfer_is_completed purple_xfer_is_completed
-#define gaim_xfer_get_filename purple_xfer_get_filename
-#define gaim_xfer_get_local_filename purple_xfer_get_local_filename
-#define gaim_xfer_get_bytes_sent purple_xfer_get_bytes_sent
-#define gaim_xfer_get_bytes_remaining purple_xfer_get_bytes_remaining
-#define gaim_xfer_get_size purple_xfer_get_size
-#define gaim_xfer_get_progress purple_xfer_get_progress
-#define gaim_xfer_get_local_port purple_xfer_get_local_port
-#define gaim_xfer_get_remote_ip purple_xfer_get_remote_ip
-#define gaim_xfer_get_remote_port purple_xfer_get_remote_port
-#define gaim_xfer_set_completed purple_xfer_set_completed
-#define gaim_xfer_set_message purple_xfer_set_message
-#define gaim_xfer_set_filename purple_xfer_set_filename
-#define gaim_xfer_set_local_filename purple_xfer_set_local_filename
-#define gaim_xfer_set_size purple_xfer_set_size
-#define gaim_xfer_set_bytes_sent purple_xfer_set_bytes_sent
-#define gaim_xfer_get_ui_ops purple_xfer_get_ui_ops
-#define gaim_xfer_set_read_fnc purple_xfer_set_read_fnc
-#define gaim_xfer_set_write_fnc purple_xfer_set_write_fnc
-#define gaim_xfer_set_ack_fnc purple_xfer_set_ack_fnc
-#define gaim_xfer_set_request_denied_fnc purple_xfer_set_request_denied_fnc
-#define gaim_xfer_set_init_fnc purple_xfer_set_init_fnc
-#define gaim_xfer_set_start_fnc purple_xfer_set_start_fnc
-#define gaim_xfer_set_end_fnc purple_xfer_set_end_fnc
-#define gaim_xfer_set_cancel_send_fnc purple_xfer_set_cancel_send_fnc
-#define gaim_xfer_set_cancel_recv_fnc purple_xfer_set_cancel_recv_fnc
-
-#define gaim_xfer_read purple_xfer_read
-#define gaim_xfer_write purple_xfer_write
-#define gaim_xfer_start purple_xfer_start
-#define gaim_xfer_end purple_xfer_end
-#define gaim_xfer_add purple_xfer_add
-#define gaim_xfer_cancel_local purple_xfer_cancel_local
-#define gaim_xfer_cancel_remote purple_xfer_cancel_remote
-#define gaim_xfer_error purple_xfer_error
-#define gaim_xfer_update_progress purple_xfer_update_progress
-#define gaim_xfer_conversation_write purple_xfer_conversation_write
-
-#define gaim_xfers_get_handle purple_xfers_get_handle
-#define gaim_xfers_init purple_xfers_init
-#define gaim_xfers_uninit purple_xfers_uninit
-#define gaim_xfers_set_ui_ops purple_xfers_set_ui_ops
-#define gaim_xfers_get_ui_ops purple_xfers_get_ui_ops
-
-/* from gaim-client.h */
-
-#define gaim_init purple_init
-
-/* from idle.h */
-
-#define GaimIdleUiOps PurpleIdleUiOps
-
-#define gaim_idle_touch purple_idle_touch
-#define gaim_idle_set purple_idle_set
-#define gaim_idle_set_ui_ops purple_idle_set_ui_ops
-#define gaim_idle_get_ui_ops purple_idle_get_ui_ops
-#define gaim_idle_init purple_idle_init
-#define gaim_idle_uninit purple_idle_uninit
-
-/* from imgstore.h */
-
-#define GaimStoredImage PurpleStoredImage
-
-#define gaim_imgstore_add(data, size, filename) \
- purple_imgstore_add_with_id(g_memdup(data, size), size, filename)
-#define gaim_imgstore_get purple_imgstore_find_by_id
-#define gaim_imgstore_get_data purple_imgstore_get_data
-#define gaim_imgstore_get_size purple_imgstore_get_size
-#define gaim_imgstore_get_filename purple_imgstore_get_filename
-#define gaim_imgstore_ref purple_imgstore_ref_by_id
-#define gaim_imgstore_unref purple_imgstore_unref_by_id
-
-
-/* from log.h */
-
-#define GaimLog PurpleLog
-#define GaimLogLogger PurpleLogLogger
-#define GaimLogCommonLoggerData PurpleLogCommonLoggerData
-#define GaimLogSet PurpleLogSet
-
-#define GAIM_LOG_IM PURPLE_LOG_IM
-#define GAIM_LOG_CHAT PURPLE_LOG_CHAT
-#define GAIM_LOG_SYSTEM PURPLE_LOG_SYSTEM
-
-#define GaimLogType PurpleLogType
-
-#define GAIM_LOG_READ_NO_NEWLINE PURPLE_LOG_READ_NO_NEWLINE
-
-#define GaimLogReadFlags PurpleLogReadFlags
-
-#define GaimLogSetCallback PurpleLogSetCallback
-
-#define gaim_log_new purple_log_new
-#define gaim_log_free purple_log_free
-#define gaim_log_write purple_log_write
-#define gaim_log_read purple_log_read
-
-#define gaim_log_get_logs purple_log_get_logs
-#define gaim_log_get_log_sets purple_log_get_log_sets
-#define gaim_log_get_system_logs purple_log_get_system_logs
-#define gaim_log_get_size purple_log_get_size
-#define gaim_log_get_total_size purple_log_get_total_size
-#define gaim_log_get_log_dir purple_log_get_log_dir
-#define gaim_log_compare purple_log_compare
-#define gaim_log_set_compare purple_log_set_compare
-#define gaim_log_set_free purple_log_set_free
-
-#define gaim_log_common_writer purple_log_common_writer
-#define gaim_log_common_lister purple_log_common_lister
-#define gaim_log_common_total_sizer purple_log_common_total_sizer
-#define gaim_log_common_sizer purple_log_common_sizer
-
-#define gaim_log_logger_new purple_log_logger_new
-#define gaim_log_logger_free purple_log_logger_free
-#define gaim_log_logger_add purple_log_logger_add
-#define gaim_log_logger_remove purple_log_logger_remove
-#define gaim_log_logger_set purple_log_logger_set
-#define gaim_log_logger_get purple_log_logger_get
-
-#define gaim_log_logger_get_options purple_log_logger_get_options
-
-#define gaim_log_init purple_log_init
-#define gaim_log_get_handle purple_log_get_handle
-#define gaim_log_uninit purple_log_uninit
-
-/* from mime.h */
-
-#define GaimMimeDocument PurpleMimeDocument
-#define GaimMimePart PurpleMimePart
-
-#define gaim_mime_document_new purple_mime_document_new
-#define gaim_mime_document_free purple_mime_document_free
-#define gaim_mime_document_parse purple_mime_document_parse
-#define gaim_mime_document_parsen purple_mime_document_parsen
-#define gaim_mime_document_write purple_mime_document_write
-#define gaim_mime_document_get_fields purple_mime_document_get_fields
-#define gaim_mime_document_get_field purple_mime_document_get_field
-#define gaim_mime_document_set_field purple_mime_document_set_field
-#define gaim_mime_document_get_parts purple_mime_document_get_parts
-
-#define gaim_mime_part_new purple_mime_part_new
-#define gaim_mime_part_get_fields purple_mime_part_get_fields
-#define gaim_mime_part_get_field purple_mime_part_get_field
-#define gaim_mime_part_get_field_decoded purple_mime_part_get_field_decoded
-#define gaim_mime_part_set_field purple_mime_part_set_field
-#define gaim_mime_part_get_data purple_mime_part_get_data
-#define gaim_mime_part_get_data_decoded purple_mime_part_get_data_decoded
-#define gaim_mime_part_get_length purple_mime_part_get_length
-#define gaim_mime_part_set_data purple_mime_part_set_data
-
-
-/* from network.h */
-
-#define GaimNetworkListenData PurpleNetworkListenData
-
-#define GaimNetworkListenCallback PurpleNetworkListenCallback
-
-#define gaim_network_ip_atoi purple_network_ip_atoi
-#define gaim_network_set_public_ip purple_network_set_public_ip
-#define gaim_network_get_public_ip purple_network_get_public_ip
-#define gaim_network_get_local_system_ip purple_network_get_local_system_ip
-#define gaim_network_get_my_ip purple_network_get_my_ip
-
-#define gaim_network_listen purple_network_listen
-#define gaim_network_listen_range purple_network_listen_range
-#define gaim_network_listen_cancel purple_network_listen_cancel
-#define gaim_network_get_port_from_fd purple_network_get_port_from_fd
-
-#define gaim_network_is_available purple_network_is_available
-
-#define gaim_network_init purple_network_init
-#define gaim_network_uninit purple_network_uninit
-
-/* from notify.h */
-
-
-#define GaimNotifyUserInfoEntry PurpleNotifyUserInfoEntry
-#define GaimNotifyUserInfo PurpleNotifyUserInfo
-
-#define GaimNotifyCloseCallback PurpleNotifyCloseCallback
-
-#define GAIM_NOTIFY_MESSAGE PURPLE_NOTIFY_MESSAGE
-#define GAIM_NOTIFY_EMAIL PURPLE_NOTIFY_EMAIL
-#define GAIM_NOTIFY_EMAILS PURPLE_NOTIFY_EMAILS
-#define GAIM_NOTIFY_FORMATTED PURPLE_NOTIFY_FORMATTED
-#define GAIM_NOTIFY_SEARCHRESULTS PURPLE_NOTIFY_SEARCHRESULTS
-#define GAIM_NOTIFY_USERINFO PURPLE_NOTIFY_USERINFO
-#define GAIM_NOTIFY_URI PURPLE_NOTIFY_URI
-
-#define GaimNotifyType PurpleNotifyType
-
-#define GAIM_NOTIFY_MSG_ERROR PURPLE_NOTIFY_MSG_ERROR
-#define GAIM_NOTIFY_MSG_WARNING PURPLE_NOTIFY_MSG_WARNING
-#define GAIM_NOTIFY_MSG_INFO PURPLE_NOTIFY_MSG_INFO
-
-#define GaimNotifyMsgType PurpleNotifyMsgType
-
-#define GAIM_NOTIFY_BUTTON_LABELED PURPLE_NOTIFY_BUTTON_LABELED
-#define GAIM_NOTIFY_BUTTON_CONTINUE PURPLE_NOTIFY_BUTTON_CONTINUE
-#define GAIM_NOTIFY_BUTTON_ADD PURPLE_NOTIFY_BUTTON_ADD
-#define GAIM_NOTIFY_BUTTON_INFO PURPLE_NOTIFY_BUTTON_INFO
-#define GAIM_NOTIFY_BUTTON_IM PURPLE_NOTIFY_BUTTON_IM
-#define GAIM_NOTIFY_BUTTON_JOIN PURPLE_NOTIFY_BUTTON_JOIN
-#define GAIM_NOTIFY_BUTTON_INVITE PURPLE_NOTIFY_BUTTON_INVITE
-
-#define GaimNotifySearchButtonType PurpleNotifySearchButtonType
-
-#define GaimNotifySearchResults PurpleNotifySearchResults
-
-#define GAIM_NOTIFY_USER_INFO_ENTRY_PAIR PURPLE_NOTIFY_USER_INFO_ENTRY_PAIR
-#define GAIM_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK
-#define GAIM_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER
-
-#define GaimNotifyUserInfoEntryType PurpleNotifyUserInfoEntryType
-
-#define GaimNotifySearchColumn PurpleNotifySearchColumn
-#define GaimNotifySearchResultsCallback PurpleNotifySearchResultsCallback
-#define GaimNotifySearchButton PurpleNotifySearchButton
-
-#define GaimNotifyUiOps PurpleNotifyUiOps
-
-#define gaim_notify_searchresults purple_notify_searchresults
-#define gaim_notify_searchresults_free purple_notify_searchresults_free
-#define gaim_notify_searchresults_new_rows purple_notify_searchresults_new_rows
-#define gaim_notify_searchresults_button_add purple_notify_searchresults_button_add
-#define gaim_notify_searchresults_button_add_labeled purple_notify_searchresults_button_add_labeled
-#define gaim_notify_searchresults_new purple_notify_searchresults_new
-#define gaim_notify_searchresults_column_new purple_notify_searchresults_column_new
-#define gaim_notify_searchresults_column_add purple_notify_searchresults_column_add
-#define gaim_notify_searchresults_row_add purple_notify_searchresults_row_add
-#define gaim_notify_searchresults_get_rows_count purple_notify_searchresults_get_rows_count
-#define gaim_notify_searchresults_get_columns_count purple_notify_searchresults_get_columns_count
-#define gaim_notify_searchresults_row_get purple_notify_searchresults_row_get
-#define gaim_notify_searchresults_column_get_title purple_notify_searchresults_column_get_title
-
-#define gaim_notify_message purple_notify_message
-#define gaim_notify_email purple_notify_email
-#define gaim_notify_emails purple_notify_emails
-#define gaim_notify_formatted purple_notify_formatted
-#define gaim_notify_userinfo purple_notify_userinfo
-
-#define gaim_notify_user_info_new purple_notify_user_info_new
-#define gaim_notify_user_info_destroy purple_notify_user_info_destroy
-#define gaim_notify_user_info_get_entries purple_notify_user_info_get_entries
-#define gaim_notify_user_info_get_text_with_newline purple_notify_user_info_get_text_with_newline
-#define gaim_notify_user_info_add_pair purple_notify_user_info_add_pair
-#define gaim_notify_user_info_prepend_pair purple_notify_user_info_prepend_pair
-#define gaim_notify_user_info_remove_entry purple_notify_user_info_remove_entry
-#define gaim_notify_user_info_entry_new purple_notify_user_info_entry_new
-#define gaim_notify_user_info_add_section_break purple_notify_user_info_add_section_break
-#define gaim_notify_user_info_add_section_header purple_notify_user_info_add_section_header
-#define gaim_notify_user_info_remove_last_item purple_notify_user_info_remove_last_item
-#define gaim_notify_user_info_entry_get_label purple_notify_user_info_entry_get_label
-#define gaim_notify_user_info_entry_set_label purple_notify_user_info_entry_set_label
-#define gaim_notify_user_info_entry_get_value purple_notify_user_info_entry_get_value
-#define gaim_notify_user_info_entry_set_value purple_notify_user_info_entry_set_value
-#define gaim_notify_user_info_entry_get_type purple_notify_user_info_entry_get_type
-#define gaim_notify_user_info_entry_set_type purple_notify_user_info_entry_set_type
-
-#define gaim_notify_uri purple_notify_uri
-#define gaim_notify_close purple_notify_close
-#define gaim_notify_close_with_handle purple_notify_close_with_handle
-
-#define gaim_notify_info purple_notify_info
-#define gaim_notify_warning purple_notify_warning
-#define gaim_notify_error purple_notify_error
-
-#define gaim_notify_set_ui_ops purple_notify_set_ui_ops
-#define gaim_notify_get_ui_ops purple_notify_get_ui_ops
-
-#define gaim_notify_get_handle purple_notify_get_handle
-
-#define gaim_notify_init purple_notify_init
-#define gaim_notify_uninit purple_notify_uninit
-
-/* from ntlm.h */
-
-#define gaim_ntlm_gen_type1 purple_ntlm_gen_type1
-#define gaim_ntlm_parse_type2 purple_ntlm_parse_type2
-#define gaim_ntlm_gen_type3 purple_ntlm_gen_type3
-
-/* from plugin.h */
-
-#ifdef GAIM_PLUGINS
-#ifndef PURPLE_PLUGINS
-#define PURPLE_PLUGINS
-#endif
-#endif
-
-#define GaimPlugin PurplePlugin
-#define GaimPluginInfo PurplePluginInfo
-#define GaimPluginUiInfo PurplePluginUiInfo
-#define GaimPluginLoaderInfo PurplePluginLoaderInfo
-#define GaimPluginAction PurplePluginAction
-#define GaimPluginPriority PurplePluginPriority
-
-#define GAIM_PLUGIN_UNKNOWN PURPLE_PLUGIN_UNKNOWN
-#define GAIM_PLUGIN_STANDARD PURPLE_PLUGIN_STANDARD
-#define GAIM_PLUGIN_LOADER PURPLE_PLUGIN_LOADER
-#define GAIM_PLUGIN_PROTOCOL PURPLE_PLUGIN_PROTOCOL
-
-#define GaimPluginType PurplePluginType
-
-#define GAIM_PRIORITY_DEFAULT PURPLE_PRIORITY_DEFAULT
-#define GAIM_PRIORITY_HIGHEST PURPLE_PRIORITY_HIGHEST
-#define GAIM_PRIORITY_LOWEST PURPLE_PRIORITY_LOWEST
-
-#define GAIM_PLUGIN_FLAG_INVISIBLE PURPLE_PLUGIN_FLAG_INVISIBLE
-
-#define GAIM_PLUGIN_MAGIC PURPLE_PLUGIN_MAGIC
-
-#define GAIM_PLUGIN_LOADER_INFO PURPLE_PLUGIN_LOADER_INFO
-#define GAIM_PLUGIN_HAS_PREF_FRAME PURPLE_PLUGIN_HAS_PREF_FRAME
-#define GAIM_PLUGIN_UI_INFO PURPLE_PLUGIN_UI_INFO
-
-#define GAIM_PLUGIN_HAS_ACTIONS PURPLE_PLUGIN_HAS_ACTIONS
-#define GAIM_PLUGIN_ACTIONS PURPLE_PLUGIN_ACTIONS
-
-#define GAIM_INIT_PLUGIN PURPLE_INIT_PLUGIN
-
-#define gaim_plugin_new purple_plugin_new
-#define gaim_plugin_probe purple_plugin_probe
-#define gaim_plugin_register purple_plugin_register
-#define gaim_plugin_load purple_plugin_load
-#define gaim_plugin_unload purple_plugin_unload
-#define gaim_plugin_reload purple_plugin_reload
-#define gaim_plugin_destroy purple_plugin_destroy
-#define gaim_plugin_is_loaded purple_plugin_is_loaded
-#define gaim_plugin_is_unloadable purple_plugin_is_unloadable
-#define gaim_plugin_get_id purple_plugin_get_id
-#define gaim_plugin_get_name purple_plugin_get_name
-#define gaim_plugin_get_version purple_plugin_get_version
-#define gaim_plugin_get_summary purple_plugin_get_summary
-#define gaim_plugin_get_description purple_plugin_get_description
-#define gaim_plugin_get_author purple_plugin_get_author
-#define gaim_plugin_get_homepage purple_plugin_get_homepage
-
-#define gaim_plugin_ipc_register purple_plugin_ipc_register
-#define gaim_plugin_ipc_unregister purple_plugin_ipc_unregister
-#define gaim_plugin_ipc_unregister_all purple_plugin_ipc_unregister_all
-#define gaim_plugin_ipc_get_params purple_plugin_ipc_get_params
-#define gaim_plugin_ipc_call purple_plugin_ipc_call
-
-#define gaim_plugins_add_search_path purple_plugins_add_search_path
-#define gaim_plugins_unload_all purple_plugins_unload_all
-#define gaim_plugins_destroy_all purple_plugins_destroy_all
-#define gaim_plugins_save_loaded purple_plugins_save_loaded
-#define gaim_plugins_load_saved purple_plugins_load_saved
-#define gaim_plugins_probe purple_plugins_probe
-#define gaim_plugins_enabled purple_plugins_enabled
-
-#define gaim_plugins_register_probe_notify_cb purple_plugins_register_probe_notify_cb
-#define gaim_plugins_unregister_probe_notify_cb purple_plugins_unregister_probe_notify_cb
-#define gaim_plugins_register_load_notify_cb purple_plugins_register_load_notify_cb
-#define gaim_plugins_unregister_load_notify_cb purple_plugins_unregister_load_notify_cb
-#define gaim_plugins_register_unload_notify_cb purple_plugins_register_unload_notify_cb
-#define gaim_plugins_unregister_unload_notify_cb purple_plugins_unregister_unload_notify_cb
-
-#define gaim_plugins_find_with_name purple_plugins_find_with_name
-#define gaim_plugins_find_with_filename purple_plugins_find_with_filename
-#define gaim_plugins_find_with_basename purple_plugins_find_with_basename
-#define gaim_plugins_find_with_id purple_plugins_find_with_id
-
-#define gaim_plugins_get_loaded purple_plugins_get_loaded
-#define gaim_plugins_get_protocols purple_plugins_get_protocols
-#define gaim_plugins_get_all purple_plugins_get_all
-
-#define gaim_plugins_get_handle purple_plugins_get_handle
-#define gaim_plugins_init purple_plugins_init
-#define gaim_plugins_uninit purple_plugins_uninit
-
-#define gaim_plugin_action_new purple_plugin_action_new
-#define gaim_plugin_action_free purple_plugin_action_free
-
-/* pluginpref.h */
-
-#define GaimPluginPrefFrame PurplePluginPrefFrame
-#define GaimPluginPref PurplePluginPref
-
-#define GAIM_STRING_FORMAT_TYPE_NONE PURPLE_STRING_FORMAT_TYPE_NONE
-#define GAIM_STRING_FORMAT_TYPE_MULTILINE PURPLE_STRING_FORMAT_TYPE_MULTILINE
-#define GAIM_STRING_FORMAT_TYPE_HTML PURPLE_STRING_FORMAT_TYPE_HTML
-
-#define GaimStringFormatType PurpleStringFormatType
-
-#define GAIM_PLUGIN_PREF_NONE PURPLE_PLUGIN_PREF_NONE
-#define GAIM_PLUGIN_PREF_CHOICE PURPLE_PLUGIN_PREF_CHOICE
-#define GAIM_PLUGIN_PREF_INFO PURPLE_PLUGIN_PREF_INFO
-#define GAIM_PLUGIN_PREF_STRING_FORMAT PURPLE_PLUGIN_PREF_STRING_FORMAT
-
-#define GaimPluginPrefType PurplePluginPrefType
-
-#define gaim_plugin_pref_frame_new purple_plugin_pref_frame_new
-#define gaim_plugin_pref_frame_destroy purple_plugin_pref_frame_destroy
-#define gaim_plugin_pref_frame_add purple_plugin_pref_frame_add
-#define gaim_plugin_pref_frame_get_prefs purple_plugin_pref_frame_get_prefs
-
-#define gaim_plugin_pref_new purple_plugin_pref_new
-#define gaim_plugin_pref_new_with_name purple_plugin_pref_new_with_name
-#define gaim_plugin_pref_new_with_label purple_plugin_pref_new_with_label
-#define gaim_plugin_pref_new_with_name_and_label purple_plugin_pref_new_with_name_and_label
-#define gaim_plugin_pref_destroy purple_plugin_pref_destroy
-#define gaim_plugin_pref_set_name purple_plugin_pref_set_name
-#define gaim_plugin_pref_get_name purple_plugin_pref_get_name
-#define gaim_plugin_pref_set_label purple_plugin_pref_set_label
-#define gaim_plugin_pref_get_label purple_plugin_pref_get_label
-#define gaim_plugin_pref_set_bounds purple_plugin_pref_set_bounds
-#define gaim_plugin_pref_get_bounds purple_plugin_pref_get_bounds
-#define gaim_plugin_pref_set_type purple_plugin_pref_set_type
-#define gaim_plugin_pref_get_type purple_plugin_pref_get_type
-#define gaim_plugin_pref_add_choice purple_plugin_pref_add_choice
-#define gaim_plugin_pref_get_choices purple_plugin_pref_get_choices
-#define gaim_plugin_pref_set_max_length purple_plugin_pref_set_max_length
-#define gaim_plugin_pref_get_max_length purple_plugin_pref_get_max_length
-#define gaim_plugin_pref_set_masked purple_plugin_pref_set_masked
-#define gaim_plugin_pref_get_masked purple_plugin_pref_get_masked
-#define gaim_plugin_pref_set_format_type purple_plugin_pref_set_format_type
-#define gaim_plugin_pref_get_format_type purple_plugin_pref_get_format_type
-
-/* from pounce.h */
-
-#define GaimPounce PurplePounce
-
-#define GAIM_POUNCE_NONE PURPLE_POUNCE_NONE
-#define GAIM_POUNCE_SIGNON PURPLE_POUNCE_SIGNON
-#define GAIM_POUNCE_SIGNOFF PURPLE_POUNCE_SIGNOFF
-#define GAIM_POUNCE_AWAY PURPLE_POUNCE_AWAY
-#define GAIM_POUNCE_AWAY_RETURN PURPLE_POUNCE_AWAY_RETURN
-#define GAIM_POUNCE_IDLE PURPLE_POUNCE_IDLE
-#define GAIM_POUNCE_IDLE_RETURN PURPLE_POUNCE_IDLE_RETURN
-#define GAIM_POUNCE_TYPING PURPLE_POUNCE_TYPING
-#define GAIM_POUNCE_TYPED PURPLE_POUNCE_TYPED
-#define GAIM_POUNCE_TYPING_STOPPED PURPLE_POUNCE_TYPING_STOPPED
-#define GAIM_POUNCE_MESSAGE_RECEIVED PURPLE_POUNCE_MESSAGE_RECEIVED
-#define GaimPounceEvent PurplePounceEvent
-
-#define GAIM_POUNCE_OPTION_NONE PURPLE_POUNCE_OPTION_NONE
-#define GAIM_POUNCE_OPTION_AWAY PURPLE_POUNCE_OPTION_AWAY
-#define GaimPounceOption PurplePounceOption
-
-#define GaimPounceCb PurplePounceCb
-
-#define gaim_pounce_new purple_pounce_new
-#define gaim_pounce_destroy purple_pounce_destroy
-#define gaim_pounce_destroy_all_by_account purple_pounce_destroy_all_by_account
-#define gaim_pounce_set_events purple_pounce_set_events
-#define gaim_pounce_set_options purple_pounce_set_options
-#define gaim_pounce_set_pouncer purple_pounce_set_pouncer
-#define gaim_pounce_set_pouncee purple_pounce_set_pouncee
-#define gaim_pounce_set_save purple_pounce_set_save
-#define gaim_pounce_action_register purple_pounce_action_register
-#define gaim_pounce_action_set_enabled purple_pounce_action_set_enabled
-#define gaim_pounce_action_set_attribute purple_pounce_action_set_attribute
-#define gaim_pounce_set_data purple_pounce_set_data
-#define gaim_pounce_get_events purple_pounce_get_events
-#define gaim_pounce_get_options purple_pounce_get_options
-#define gaim_pounce_get_pouncer purple_pounce_get_pouncer
-#define gaim_pounce_get_pouncee purple_pounce_get_pouncee
-#define gaim_pounce_get_save purple_pounce_get_save
-#define gaim_pounce_action_is_enabled purple_pounce_action_is_enabled
-#define gaim_pounce_action_get_attribute purple_pounce_action_get_attribute
-#define gaim_pounce_get_data purple_pounce_get_data
-#define gaim_pounce_execute purple_pounce_execute
-
-#define gaim_find_pounce purple_find_pounce
-#define gaim_pounces_load purple_pounces_load
-#define gaim_pounces_register_handler purple_pounces_register_handler
-#define gaim_pounces_unregister_handler purple_pounces_unregister_handler
-#define gaim_pounces_get_all purple_pounces_get_all
-#define gaim_pounces_get_handle purple_pounces_get_handle
-#define gaim_pounces_init purple_pounces_init
-#define gaim_pounces_uninit purple_pounces_uninit
-
-/* from prefs.h */
-
-
-#define GAIM_PREF_NONE PURPLE_PREF_NONE
-#define GAIM_PREF_BOOLEAN PURPLE_PREF_BOOLEAN
-#define GAIM_PREF_INT PURPLE_PREF_INT
-#define GAIM_PREF_STRING PURPLE_PREF_STRING
-#define GAIM_PREF_STRING_LIST PURPLE_PREF_STRING_LIST
-#define GAIM_PREF_PATH PURPLE_PREF_PATH
-#define GAIM_PREF_PATH_LIST PURPLE_PREF_PATH_LIST
-#define GaimPrefType PurplePrefType
-
-#define GaimPrefCallback PurplePrefCallback
-
-#define gaim_prefs_get_handle purple_prefs_get_handle
-#define gaim_prefs_init purple_prefs_init
-#define gaim_prefs_uninit purple_prefs_uninit
-#define gaim_prefs_add_none purple_prefs_add_none
-#define gaim_prefs_add_bool purple_prefs_add_bool
-#define gaim_prefs_add_int purple_prefs_add_int
-#define gaim_prefs_add_string purple_prefs_add_string
-#define gaim_prefs_add_string_list purple_prefs_add_string_list
-#define gaim_prefs_add_path purple_prefs_add_path
-#define gaim_prefs_add_path_list purple_prefs_add_path_list
-#define gaim_prefs_remove purple_prefs_remove
-#define gaim_prefs_rename purple_prefs_rename
-#define gaim_prefs_rename_boolean_toggle purple_prefs_rename_boolean_toggle
-#define gaim_prefs_destroy purple_prefs_destroy
-#define gaim_prefs_set_generic purple_prefs_set_generic
-#define gaim_prefs_set_bool purple_prefs_set_bool
-#define gaim_prefs_set_int purple_prefs_set_int
-#define gaim_prefs_set_string purple_prefs_set_string
-#define gaim_prefs_set_string_list purple_prefs_set_string_list
-#define gaim_prefs_set_path purple_prefs_set_path
-#define gaim_prefs_set_path_list purple_prefs_set_path_list
-#define gaim_prefs_exists purple_prefs_exists
-#define gaim_prefs_get_type purple_prefs_get_type
-#define gaim_prefs_get_bool purple_prefs_get_bool
-#define gaim_prefs_get_int purple_prefs_get_int
-#define gaim_prefs_get_string purple_prefs_get_string
-#define gaim_prefs_get_string_list purple_prefs_get_string_list
-#define gaim_prefs_get_path purple_prefs_get_path
-#define gaim_prefs_get_path_list purple_prefs_get_path_list
-#define gaim_prefs_connect_callback purple_prefs_connect_callback
-#define gaim_prefs_disconnect_callback purple_prefs_disconnect_callback
-#define gaim_prefs_disconnect_by_handle purple_prefs_disconnect_by_handle
-#define gaim_prefs_trigger_callback purple_prefs_trigger_callback
-#define gaim_prefs_load purple_prefs_load
-#define gaim_prefs_update_old purple_prefs_update_old
-
-/* from privacy.h */
-
-#define GAIM_PRIVACY_ALLOW_ALL PURPLE_PRIVACY_ALLOW_ALL
-#define GAIM_PRIVACY_DENY_ALL PURPLE_PRIVACY_DENY_ALL
-#define GAIM_PRIVACY_ALLOW_USERS PURPLE_PRIVACY_ALLOW_USERS
-#define GAIM_PRIVACY_DENY_USERS PURPLE_PRIVACY_DENY_USERS
-#define GAIM_PRIVACY_ALLOW_BUDDYLIST PURPLE_PRIVACY_ALLOW_BUDDYLIST
-#define GaimPrivacyType PurplePrivacyType
-
-#define GaimPrivacyUiOps PurplePrivacyUiOps
-
-#define gaim_privacy_permit_add purple_privacy_permit_add
-#define gaim_privacy_permit_remove purple_privacy_permit_remove
-#define gaim_privacy_deny_add purple_privacy_deny_add
-#define gaim_privacy_deny_remove purple_privacy_deny_remove
-#define gaim_privacy_allow purple_privacy_allow
-#define gaim_privacy_deny purple_privacy_deny
-#define gaim_privacy_check purple_privacy_check
-#define gaim_privacy_set_ui_ops purple_privacy_set_ui_ops
-#define gaim_privacy_get_ui_ops purple_privacy_get_ui_ops
-#define gaim_privacy_init purple_privacy_init
-
-/* from proxy.h */
-
-#define GAIM_PROXY_USE_GLOBAL PURPLE_PROXY_USE_GLOBAL
-#define GAIM_PROXY_NONE PURPLE_PROXY_NONE
-#define GAIM_PROXY_HTTP PURPLE_PROXY_HTTP
-#define GAIM_PROXY_SOCKS4 PURPLE_PROXY_SOCKS4
-#define GAIM_PROXY_SOCKS5 PURPLE_PROXY_SOCKS5
-#define GAIM_PROXY_USE_ENVVAR PURPLE_PROXY_USE_ENVVAR
-#define GaimProxyType PurpleProxyType
-
-#define GaimProxyInfo PurpleProxyInfo
-
-#define GaimProxyConnectData PurpleProxyConnectData
-#define GaimProxyConnectFunction PurpleProxyConnectFunction
-
-#define gaim_proxy_info_new purple_proxy_info_new
-#define gaim_proxy_info_destroy purple_proxy_info_destroy
-#define gaim_proxy_info_set_type purple_proxy_info_set_type
-#define gaim_proxy_info_set_host purple_proxy_info_set_host
-#define gaim_proxy_info_set_port purple_proxy_info_set_port
-#define gaim_proxy_info_set_username purple_proxy_info_set_username
-#define gaim_proxy_info_set_password purple_proxy_info_set_password
-#define gaim_proxy_info_get_type purple_proxy_info_get_type
-#define gaim_proxy_info_get_host purple_proxy_info_get_host
-#define gaim_proxy_info_get_port purple_proxy_info_get_port
-#define gaim_proxy_info_get_username purple_proxy_info_get_username
-#define gaim_proxy_info_get_password purple_proxy_info_get_password
-
-#define gaim_global_proxy_get_info purple_global_proxy_get_info
-#define gaim_proxy_get_handle purple_proxy_get_handle
-#define gaim_proxy_init purple_proxy_init
-#define gaim_proxy_uninit purple_proxy_uninit
-#define gaim_proxy_get_setup purple_proxy_get_setup
-
-#define gaim_proxy_connect purple_proxy_connect
-#define gaim_proxy_connect_socks5 purple_proxy_connect_socks5
-#define gaim_proxy_connect_cancel purple_proxy_connect_cancel
-#define gaim_proxy_connect_cancel_with_handle purple_proxy_connect_cancel_with_handle
-
-/* from prpl.h */
-
-#define GaimPluginProtocolInfo PurplePluginProtocolInfo
-
-#define GAIM_ICON_SCALE_DISPLAY PURPLE_ICON_SCALE_DISPLAY
-#define GAIM_ICON_SCALE_SEND PURPLE_ICON_SCALE_SEND
-#define GaimIconScaleRules PurpleIconScaleRules
-
-#define GaimBuddyIconSpec PurpleBuddyIconSpec
-
-#define GaimProtocolOptions PurpleProtocolOptions
-
-#define GAIM_IS_PROTOCOL_PLUGIN PURPLE_IS_PROTOCOL_PLUGIN
-
-#define GAIM_PLUGIN_PROTOCOL_INFO PURPLE_PLUGIN_PROTOCOL_INFO
-
-#define gaim_prpl_got_account_idle purple_prpl_got_account_idle
-#define gaim_prpl_got_account_login_time purple_prpl_got_account_login_time
-#define gaim_prpl_got_account_status purple_prpl_got_account_status
-#define gaim_prpl_got_user_idle purple_prpl_got_user_idle
-#define gaim_prpl_got_user_login_time purple_prpl_got_user_login_time
-#define gaim_prpl_got_user_status purple_prpl_got_user_status
-#define gaim_prpl_change_account_status purple_prpl_change_account_status
-#define gaim_prpl_get_statuses purple_prpl_get_statuses
-
-#define gaim_find_prpl purple_find_prpl
-
-/* from request.h */
-
-#define GAIM_DEFAULT_ACTION_NONE PURPLE_DEFAULT_ACTION_NONE
-
-#define GAIM_REQUEST_INPUT PURPLE_REQUEST_INPUT
-#define GAIM_REQUEST_CHOICE PURPLE_REQUEST_CHOICE
-#define GAIM_REQUEST_ACTION PURPLE_REQUEST_ACTION
-#define GAIM_REQUEST_FIELDS PURPLE_REQUEST_FIELDS
-#define GAIM_REQUEST_FILE PURPLE_REQUEST_FILE
-#define GAIM_REQUEST_FOLDER PURPLE_REQUEST_FOLDER
-#define GaimRequestType PurpleRequestType
-
-#define GAIM_REQUEST_FIELD_NONE PURPLE_REQUEST_FIELD_NONE
-#define GAIM_REQUEST_FIELD_STRING PURPLE_REQUEST_FIELD_STRING
-#define GAIM_REQUEST_FIELD_INTEGER PURPLE_REQUEST_FIELD_INTEGER
-#define GAIM_REQUEST_FIELD_BOOLEAN PURPLE_REQUEST_FIELD_BOOLEAN
-#define GAIM_REQUEST_FIELD_CHOICE PURPLE_REQUEST_FIELD_CHOICE
-#define GAIM_REQUEST_FIELD_LIST PURPLE_REQUEST_FIELD_LIST
-#define GAIM_REQUEST_FIELD_LABEL PURPLE_REQUEST_FIELD_LABEL
-#define GAIM_REQUEST_FIELD_IMAGE PURPLE_REQUEST_FIELD_IMAGE
-#define GAIM_REQUEST_FIELD_ACCOUNT PURPLE_REQUEST_FIELD_ACCOUNT
-#define GaimRequestFieldType PurpleRequestFieldType
-
-#define GaimRequestFields PurpleRequestFields
-
-#define GaimRequestFieldGroup PurpleRequestFieldGroup
-
-#define GaimRequestField PurpleRequestField
-
-#define GaimRequestUiOps PurpleRequestUiOps
-
-#define GaimRequestInputCb PurpleRequestInputCb
-#define GaimRequestActionCb PurpleRequestActionCb
-#define GaimRequestChoiceCb PurpleRequestChoiceCb
-#define GaimRequestFieldsCb PurpleRequestFieldsCb
-#define GaimRequestFileCb PurpleRequestFileCb
-
-#define gaim_request_fields_new purple_request_fields_new
-#define gaim_request_fields_destroy purple_request_fields_destroy
-#define gaim_request_fields_add_group purple_request_fields_add_group
-#define gaim_request_fields_get_groups purple_request_fields_get_groups
-#define gaim_request_fields_exists purple_request_fields_exists
-#define gaim_request_fields_get_required purple_request_fields_get_required
-#define gaim_request_fields_is_field_required purple_request_fields_is_field_required
-#define gaim_request_fields_all_required_filled purple_request_fields_all_required_filled
-#define gaim_request_fields_get_field purple_request_fields_get_field
-#define gaim_request_fields_get_string purple_request_fields_get_string
-#define gaim_request_fields_get_integer purple_request_fields_get_integer
-#define gaim_request_fields_get_bool purple_request_fields_get_bool
-#define gaim_request_fields_get_choice purple_request_fields_get_choice
-#define gaim_request_fields_get_account purple_request_fields_get_account
-
-#define gaim_request_field_group_new purple_request_field_group_new
-#define gaim_request_field_group_destroy purple_request_field_group_destroy
-#define gaim_request_field_group_add_field purple_request_field_group_add_field
-#define gaim_request_field_group_get_title purple_request_field_group_get_title
-#define gaim_request_field_group_get_fields purple_request_field_group_get_fields
-
-#define gaim_request_field_new purple_request_field_new
-#define gaim_request_field_destroy purple_request_field_destroy
-#define gaim_request_field_set_label purple_request_field_set_label
-#define gaim_request_field_set_visible purple_request_field_set_visible
-#define gaim_request_field_set_type_hint purple_request_field_set_type_hint
-#define gaim_request_field_set_required purple_request_field_set_required
-#define gaim_request_field_get_type purple_request_field_get_type
-#define gaim_request_field_get_id purple_request_field_get_id
-#define gaim_request_field_get_label purple_request_field_get_label
-#define gaim_request_field_is_visible purple_request_field_is_visible
-#define gaim_request_field_get_type_hint purple_request_field_get_type_hint
-#define gaim_request_field_is_required purple_request_field_is_required
-
-#define gaim_request_field_string_new purple_request_field_string_new
-#define gaim_request_field_string_set_default_value \
- purple_request_field_string_set_default_value
-#define gaim_request_field_string_set_value purple_request_field_string_set_value
-#define gaim_request_field_string_set_masked purple_request_field_string_set_masked
-#define gaim_request_field_string_set_editable purple_request_field_string_set_editable
-#define gaim_request_field_string_get_default_value \
- purple_request_field_string_get_default_value
-#define gaim_request_field_string_get_value purple_request_field_string_get_value
-#define gaim_request_field_string_is_multiline purple_request_field_string_is_multiline
-#define gaim_request_field_string_is_masked purple_request_field_string_is_masked
-#define gaim_request_field_string_is_editable purple_request_field_string_is_editable
-
-#define gaim_request_field_int_new purple_request_field_int_new
-#define gaim_request_field_int_set_default_value \
- purple_request_field_int_set_default_value
-#define gaim_request_field_int_set_value purple_request_field_int_set_value
-#define gaim_request_field_int_get_default_value \
- purple_request_field_int_get_default_value
-#define gaim_request_field_int_get_value purple_request_field_int_get_value
-
-#define gaim_request_field_bool_new purple_request_field_bool_new
-#define gaim_request_field_bool_set_default_value \
- purple_request_field_book_set_default_value
-#define gaim_request_field_bool_set_value purple_request_field_bool_set_value
-#define gaim_request_field_bool_get_default_value \
- purple_request_field_bool_get_default_value
-#define gaim_request_field_bool_get_value purple_request_field_bool_get_value
-
-#define gaim_request_field_choice_new purple_request_field_choice_new
-#define gaim_request_field_choice_add purple_request_field_choice_add
-#define gaim_request_field_choice_set_default_value \
- purple_request_field_choice_set_default_value
-#define gaim_request_field_choice_set_value purple_request_field_choice_set_value
-#define gaim_request_field_choice_get_default_value \
- purple_request_field_choice_get_default_value
-#define gaim_request_field_choice_get_value purple_request_field_choice_get_value
-#define gaim_request_field_choice_get_labels purple_request_field_choice_get_labels
-
-#define gaim_request_field_list_new purple_request_field_list_new
-#define gaim_request_field_list_set_multi_select purple_request_field_list_set_multi_select
-#define gaim_request_field_list_get_multi_select purple_request_field_list_get_multi_select
-#define gaim_request_field_list_get_data purple_request_field_list_get_data
-#define gaim_request_field_list_add purple_request_field_list_add
-#define gaim_request_field_list_add_selected purple_request_field_list_add_selected
-#define gaim_request_field_list_clear_selected purple_request_field_list_clear_selected
-#define gaim_request_field_list_set_selected purple_request_field_list_set_selected
-#define gaim_request_field_list_is_selected purple_request_field_list_is_selected
-#define gaim_request_field_list_get_selected purple_request_field_list_get_selected
-#define gaim_request_field_list_get_items purple_request_field_list_get_items
-
-#define gaim_request_field_label_new purple_request_field_label_new
-
-#define gaim_request_field_image_new purple_request_field_image_new
-#define gaim_request_field_image_set_scale purple_request_field_image_set_scale
-#define gaim_request_field_image_get_buffer purple_request_field_image_get_buffer
-#define gaim_request_field_image_get_size purple_request_field_image_get_size
-#define gaim_request_field_image_get_scale_x purple_request_field_image_get_scale_x
-#define gaim_request_field_image_get_scale_y purple_request_field_image_get_scale_y
-
-#define gaim_request_field_account_new purple_request_field_account_new
-#define gaim_request_field_account_set_default_value purple_request_field_account_set_default_value
-#define gaim_request_field_account_set_value purple_request_field_account_set_value
-#define gaim_request_field_account_set_show_all purple_request_field_account_set_show_all
-#define gaim_request_field_account_set_filter purple_request_field_account_set_filter
-#define gaim_request_field_account_get_default_value purple_request_field_account_get_default_value
-#define gaim_request_field_account_get_value purple_request_field_account_get_value
-#define gaim_request_field_account_get_show_all purple_request_field_account_get_show_all
-#define gaim_request_field_account_get_filter purple_request_field_account_get_filter
-
-#define gaim_request_input purple_request_input
-#define gaim_request_choice purple_request_choice
-#define gaim_request_choice_varg purple_request_choice_varg
-#define gaim_request_action purple_request_action
-#define gaim_request_action_varg purple_request_action_varg
-#define gaim_request_fields(handle, title, primary, secondary, fields, ok_text, ok_cb, cancel_text, cancel_cb, user_data) purple_request_fields(handle, title, primary, secondary, fields, ok_text, ok_cb, cancel_text, cancel_cb, NULL, NULL, NULL, user_data)
-#define gaim_request_close purple_request_close
-#define gaim_request_close_with_handle purple_request_close_with_handle
-
-#define gaim_request_yes_no purple_request_yes_no
-#define gaim_request_ok_cancel purple_request_ok_cancel
-#define gaim_request_accept_cancel purple_request_accept_cancel
-
-#define gaim_request_file purple_request_file
-#define gaim_request_folder purple_request_folder
-
-#define gaim_request_set_ui_ops purple_request_set_ui_ops
-#define gaim_request_get_ui_ops purple_request_get_ui_ops
-
-/* from roomlist.h */
-
-#define GaimRoomlist PurpleRoomlist
-#define GaimRoomlistRoom PurpleRoomlistRoom
-#define GaimRoomlistField PurpleRoomlistField
-#define GaimRoomlistUiOps PurpleRoomlistUiOps
-
-#define GAIM_ROOMLIST_ROOMTYPE_CATEGORY PURPLE_ROOMLIST_ROOMTYPE_CATEGORY
-#define GAIM_ROOMLIST_ROOMTYPE_ROOM PURPLE_ROOMLIST_ROOMTYPE_ROOM
-#define GaimRoomlistRoomType PurpleRoomlistRoomType
-
-#define GAIM_ROOMLIST_FIELD_BOOL PURPLE_ROOMLIST_BOOL
-#define GAIM_ROOMLIST_FIELD_INT PURPLE_ROOMLIST_INT
-#define GAIM_ROOMLIST_FIELD_STRING PURPLE_ROOMLIST_STRING
-#define GaimRoomlistFieldType PurpleRoomlistFieldType
-
-#define gaim_roomlist_show_with_account purple_roomlist_show_with_account
-#define gaim_roomlist_new purple_roomlist_new
-#define gaim_roomlist_ref purple_roomlist_ref
-#define gaim_roomlist_unref purple_roomlist_unref
-#define gaim_roomlist_set_fields purple_roomlist_set_fields
-#define gaim_roomlist_set_in_progress purple_roomlist_set_in_progress
-#define gaim_roomlist_get_in_progress purple_roomlist_get_in_progress
-#define gaim_roomlist_room_add purple_roomlist_room_add
-
-#define gaim_roomlist_get_list purple_roomlist_get_list
-#define gaim_roomlist_cancel_get_list purple_roomlist_cancel_get_list
-#define gaim_roomlist_expand_category purple_roomlist_expand_category
-
-#define gaim_roomlist_room_new purple_roomlist_room_new
-#define gaim_roomlist_room_add_field purple_roomlist_room_add_field
-#define gaim_roomlist_room_join purple_roomlist_room_join
-#define gaim_roomlist_field_new purple_roomlist_field_new
-
-#define gaim_roomlist_set_ui_ops purple_roomlist_set_ui_ops
-#define gaim_roomlist_get_ui_ops purple_roomlist_get_ui_ops
-
-/* from savedstatuses.h */
-
-#define GaimSavedStatus PurpleSavedStatus
-#define GaimSavedStatusSub PurpleSavedStatusSub
-
-#define gaim_savedstatus_new purple_savedstatus_new
-#define gaim_savedstatus_set_title purple_savedstatus_set_title
-#define gaim_savedstatus_set_type purple_savedstatus_set_type
-#define gaim_savedstatus_set_message purple_savedstatus_set_message
-#define gaim_savedstatus_set_substatus purple_savedstatus_set_substatus
-#define gaim_savedstatus_unset_substatus purple_savedstatus_unset_substatus
-#define gaim_savedstatus_delete purple_savedstatus_delete
-
-#define gaim_savedstatuses_get_all purple_savedstatuses_get_all
-#define gaim_savedstatuses_get_popular purple_savedstatuses_get_popular
-#define gaim_savedstatus_get_current purple_savedstatus_get_current
-#define gaim_savedstatus_get_default purple_savedstatus_get_default
-#define gaim_savedstatus_get_idleaway purple_savedstatus_get_idleaway
-#define gaim_savedstatus_is_idleaway purple_savedstatus_is_idleaway
-#define gaim_savedstatus_set_idleaway purple_savedstatus_set_idleaway
-#define gaim_savedstatus_get_startup purple_savedstatus_get_startup
-#define gaim_savedstatus_find purple_savedstatus_find
-#define gaim_savedstatus_find_by_creation_time purple_savedstatus_find_by_creation_time
-#define gaim_savedstatus_find_transient_by_type_and_message \
- purple_savedstatus_find_transient_by_type_and_message
-
-#define gaim_savedstatus_is_transient purple_savedstatus_is_transient
-#define gaim_savedstatus_get_title purple_savedstatus_get_title
-#define gaim_savedstatus_get_type purple_savedstatus_get_type
-#define gaim_savedstatus_get_message purple_savedstatus_get_message
-#define gaim_savedstatus_get_creation_time purple_savedstatus_get_creation_time
-#define gaim_savedstatus_has_substatuses purple_savedstatus_has_substatuses
-#define gaim_savedstatus_get_substatus purple_savedstatus_get_substatus
-#define gaim_savedstatus_substatus_get_type purple_savedstatus_substatus_get_type
-#define gaim_savedstatus_substatus_get_message purple_savedstatus_substatus_get_message
-#define gaim_savedstatus_activate purple_savedstatus_activate
-#define gaim_savedstatus_activate_for_account purple_savedstatus_activate_for_account
-
-#define gaim_savedstatuses_get_handle purple_savedstatuses_get_handle
-#define gaim_savedstatuses_init purple_savedstatuses_init
-#define gaim_savedstatuses_uninit purple_savedstatuses_uninit
-
-/* from signals.h */
-
-#define GAIM_CALLBACK PURPLE_CALLBACK
-
-#define GaimCallback PurpleCallback
-#define GaimSignalMarshalFunc PurpleSignalMarshalFunc
-
-#define GAIM_SIGNAL_PRIORITY_DEFAULT PURPLE_SIGNAL_PRIORITY_DEFAULT
-#define GAIM_SIGNAL_PRIORITY_HIGHEST PURPLE_SIGNAL_PRIORITY_HIGHEST
-#define GAIM_SIGNAL_PRIORITY_LOWEST PURPLE_SIGNAL_PRIORITY_LOWEST
-
-#define gaim_signal_register purple_signal_register
-#define gaim_signal_unregister purple_signal_unregister
-
-#define gaim_signals_unregister_by_instance purple_signals_unregister_by_instance
-
-#define gaim_signal_get_values purple_signal_get_values
-#define gaim_signal_connect_priority purple_signal_connect_priority
-#define gaim_signal_connect purple_signal_connect
-#define gaim_signal_connect_priority_vargs purple_signal_connect_priority_vargs
-#define gaim_signal_connect_vargs purple_signal_connect_vargs
-#define gaim_signal_disconnect purple_signal_disconnect
-
-#define gaim_signals_disconnect_by_handle purple_signals_disconnect_by_handle
-
-#define gaim_signal_emit purple_signal_emit
-#define gaim_signal_emit_vargs purple_signal_emit_vargs
-#define gaim_signal_emit_return_1 purple_signal_emit_vargs
-#define gaim_signal_emit_vargs_return_1 purple_signal_emit_vargs_return_1
-
-#define gaim_signals_init purple_signals_init
-#define gaim_signals_uninit purple_signals_uninit
-
-#define gaim_marshal_VOID \
- purple_marshal_VOID
-#define gaim_marshal_VOID__INT \
- purple_marshal_VOID__INT
-#define gaim_marshal_VOID__INT_INT \
- purple_marshal_VOID_INT_INT
-#define gaim_marshal_VOID__POINTER \
- purple_marshal_VOID__POINTER
-#define gaim_marshal_VOID__POINTER_UINT \
- purple_marshal_VOID__POINTER_UINT
-#define gaim_marshal_VOID__POINTER_INT_INT \
- purple_marshal_VOID__POINTER_INT_INT
-#define gaim_marshal_VOID__POINTER_POINTER \
- purple_marshal_VOID__POINTER_POINTER
-#define gaim_marshal_VOID__POINTER_POINTER_UINT \
- purple_marshal_VOID__POINTER_POINTER_UINT
-#define gaim_marshal_VOID__POINTER_POINTER_UINT_UINT \
- purple_marshal_VOID__POINTER_POINTER_UINT_UINT
-#define gaim_marshal_VOID__POINTER_POINTER_POINTER \
- purple_marshal_VOID__POINTER_POINTER_POINTER
-#define gaim_marshal_VOID__POINTER_POINTER_POINTER_POINTER \
- purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER
-#define gaim_marshal_VOID__POINTER_POINTER_POINTER_POINTER_POINTER \
- purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_POINTER
-#define gaim_marshal_VOID__POINTER_POINTER_POINTER_UINT \
- purple_marshal_VOID__POINTER_POINTER_POINTER_UINT
-#define gaim_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT \
- purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT
-#define gaim_marshal_VOID__POINTER_POINTER_POINTER_UINT_UINT \
- purple_marshal_VOID__POINTER_POINTER_POINTER_UINT_UINT
-
-#define gaim_marshal_INT__INT \
- purple_marshal_INT__INT
-#define gaim_marshal_INT__INT_INT \
- purple_marshal_INT__INT_INT
-#define gaim_marshal_INT__POINTER_POINTER_POINTER_POINTER_POINTER \
- purple_marshal_INT__POINTER_POINTER_POINTER_POINTER_POINTER
-
-#define gaim_marshal_BOOLEAN__POINTER \
- purple_marshal_BOOLEAN__POINTER
-#define gaim_marshal_BOOLEAN__POINTER_POINTER \
- purple_marshal_BOOLEAN__POINTER_POINTER
-#define gaim_marshal_BOOLEAN__POINTER_POINTER_POINTER \
- purple_marshal_BOOLEAN__POINTER_POINTER_POINTER
-#define gaim_marshal_BOOLEAN__POINTER_POINTER_UINT \
- purple_marshal_BOOLEAN__POINTER_POINTER_UINT
-#define gaim_marshal_BOOLEAN__POINTER_POINTER_POINTER_UINT \
- purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_UINT
-#define gaim_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER \
- purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER
-#define gaim_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER \
- purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER
-
-#define gaim_marshal_BOOLEAN__INT_POINTER \
- purple_marshal_BOOLEAN__INT_POINTER
-
-#define gaim_marshal_POINTER__POINTER_INT \
- purple_marshal_POINTER__POINTER_INT
-#define gaim_marshal_POINTER__POINTER_INT64 \
- purple_marshal_POINTER__POINTER_INT64
-#define gaim_marshal_POINTER__POINTER_INT_BOOLEAN \
- purple_marshal_POINTER__POINTER_INT_BOOLEAN
-#define gaim_marshal_POINTER__POINTER_INT64_BOOLEAN \
- purple_marshal_POINTER__POINTER_INT64_BOOLEAN
-#define gaim_marshal_POINTER__POINTER_POINTER \
- purple_marshal_POINTER__POINTER_POINTER
-
-/* from sound.h */
-
-#define GAIM_SOUND_BUDDY_ARRIVE PURPLE_SOUND_BUDDY_ARRIVE
-#define GAIM_SOUND_BUDDY_LEAVE PURPLE_SOUND_BUDDY_LEAVE
-#define GAIM_SOUND_RECEIVE PURPLE_SOUND_RECEIVE
-#define GAIM_SOUND_FIRST_RECEIVE PURPLE_SOUND_FIRST_RECEIVE
-#define GAIM_SOUND_SEND PURPLE_SOUND_SEND
-#define GAIM_SOUND_CHAT_JOIN PURPLE_SOUND_CHAT_JOIN
-#define GAIM_SOUND_CHAT_LEAVE PURPLE_SOUND_CHAT_LEAVE
-#define GAIM_SOUND_CHAT_YOU_SAY PURPLE_SOUND_CHAT_YOU_SAY
-#define GAIM_SOUND_CHAT_SAY PURPLE_SOUND_CHAT_SAY
-#define GAIM_SOUND_POUNCE_DEFAULT PURPLE_SOUND_POUNCE_DEFAULT
-#define GAIM_SOUND_CHAT_NICK PURPLE_SOUND_CHAT_NICK
-#define GAIM_NUM_SOUNDS PURPLE_NUM_SOUNDS
-#define GaimSoundEventID PurpleSoundEventID
-
-#define GaimSoundUiOps PurpleSoundUiOps
-
-#define gaim_sound_play_file purple_sound_play_file
-#define gaim_sound_play_event purple_sound_play_event
-#define gaim_sound_set_ui_ops purple_sound_set_ui_ops
-#define gaim_sound_get_ui_ops purple_sound_get_ui_ops
-#define gaim_sound_init purple_sound_init
-#define gaim_sound_uninit purple_sound_uninit
-
-#define gaim_sounds_get_handle purple_sounds_get_handle
-
-/* from sslconn.h */
-
-#define GAIM_SSL_DEFAULT_PORT PURPLE_SSL_DEFAULT_PORT
-
-#define GAIM_SSL_HANDSHAKE_FAILED PURPLE_SSL_HANDSHAKE_FAILED
-#define GAIM_SSL_CONNECT_FAILED PURPLE_SSL_CONNECT_FAILED
-#define GaimSslErrorType PurpleSslErrorType
-
-#define GaimSslConnection PurpleSslConnection
-
-#define GaimSslInputFunction PurpleSslInputFunction
-#define GaimSslErrorFunction PurpleSslErrorFunction
-
-#define GaimSslOps PurpleSslOps
-
-#define gaim_ssl_is_supported purple_ssl_is_supported
-#define gaim_ssl_connect purple_ssl_connect
-#define gaim_ssl_connect_fd purple_ssl_connect_fd
-#define gaim_ssl_input_add purple_ssl_input_add
-#define gaim_ssl_close purple_ssl_close
-#define gaim_ssl_read purple_ssl_read
-#define gaim_ssl_write purple_ssl_write
-
-#define gaim_ssl_set_ops purple_ssl_set_ops
-#define gaim_ssl_get_ops purple_ssl_get_ops
-#define gaim_ssl_init purple_ssl_init
-#define gaim_ssl_uninit purple_ssl_uninit
-
-/* from status.h */
-
-#define GaimStatusType PurpleStatusType
-#define GaimStatusAttr PurpleStatusAttr
-#define GaimPresence PurplePresence
-#define GaimStatus PurpleStatus
-
-#define GAIM_PRESENCE_CONTEXT_UNSET PURPLE_PRESENCE_CONTEXT_UNSET
-#define GAIM_PRESENCE_CONTEXT_ACCOUNT PURPLE_PRESENCE_CONTEXT_ACCOUNT
-#define GAIM_PRESENCE_CONTEXT_CONV PURPLE_PRESENCE_CONTEXT_CONV
-#define GAIM_PRESENCE_CONTEXT_BUDDY PURPLE_PRESENCE_CONTEXT_BUDDY
-#define GaimPresenceContext PurplePresenceContext
-
-#define GAIM_STATUS_UNSET PURPLE_STATUS_UNSET
-#define GAIM_STATUS_OFFLINE PURPLE_STATUS_OFFLINE
-#define GAIM_STATUS_AVAILABLE PURPLE_STATUS_AVAILABLE
-#define GAIM_STATUS_UNAVAILABLE PURPLE_STATUS_UNAVAILABLE
-#define GAIM_STATUS_INVISIBLE PURPLE_STATUS_INVISIBLE
-#define GAIM_STATUS_AWAY PURPLE_STATUS_AWAY
-#define GAIM_STATUS_EXTENDED_AWAY PURPLE_STATUS_EXTENDED_AWAY
-#define GAIM_STATUS_MOBILE PURPLE_STATUS_MOBILE
-#define GAIM_STATUS_NUM_PRIMITIVES PURPLE_STATUS_NUM_PRIMITIVES
-#define GaimStatusPrimitive PurpleStatusPrimitive
-
-#define gaim_primitive_get_id_from_type purple_primitive_get_id_from_type
-#define gaim_primitive_get_name_from_type purple_primitive_get_name_from_type
-#define gaim_primitive_get_type_from_id purple_primitive_get_type_from_id
-
-#define gaim_status_type_new_full purple_status_type_new_full
-#define gaim_status_type_new purple_status_type_new
-#define gaim_status_type_new_with_attrs purple_status_type_new_with_attrs
-#define gaim_status_type_destroy purple_status_type_destroy
-#define gaim_status_type_set_primary_attr purple_status_type_set_primary_attr
-#define gaim_status_type_add_attr purple_status_type_add_attr
-#define gaim_status_type_add_attrs purple_status_type_add_attrs
-#define gaim_status_type_add_attrs_vargs purple_status_type_add_attrs_vargs
-#define gaim_status_type_get_primitive purple_status_type_get_primitive
-#define gaim_status_type_get_id purple_status_type_get_id
-#define gaim_status_type_get_name purple_status_type_get_name
-#define gaim_status_type_is_saveable purple_status_type_is_saveable
-#define gaim_status_type_is_user_settable purple_status_type_is_user_settable
-#define gaim_status_type_is_independent purple_status_type_is_independent
-#define gaim_status_type_is_exclusive purple_status_type_is_exclusive
-#define gaim_status_type_is_available purple_status_type_is_available
-#define gaim_status_type_get_primary_attr purple_status_type_get_primary_attr
-#define gaim_status_type_get_attr purple_status_type_get_attr
-#define gaim_status_type_get_attrs purple_status_type_get_attrs
-#define gaim_status_type_find_with_id purple_status_type_find_with_id
-
-#define gaim_status_attr_new purple_status_attr_new
-#define gaim_status_attr_destroy purple_status_attr_destroy
-#define gaim_status_attr_get_id purple_status_attr_get_id
-#define gaim_status_attr_get_name purple_status_attr_get_name
-#define gaim_status_attr_get_value purple_status_attr_get_value
-
-#define gaim_status_new purple_status_new
-#define gaim_status_destroy purple_status_destroy
-#define gaim_status_set_active purple_status_set_active
-#define gaim_status_set_active_with_attrs purple_status_set_active_with_attrs
-#define gaim_status_set_active_with_attrs_list purple_status_set_active_with_attrs_list
-#define gaim_status_set_attr_boolean purple_status_set_attr_boolean
-#define gaim_status_set_attr_int purple_status_set_attr_int
-#define gaim_status_set_attr_string purple_status_set_attr_string
-#define gaim_status_get_type purple_status_get_type
-#define gaim_status_get_presence purple_status_get_presence
-#define gaim_status_get_id purple_status_get_id
-#define gaim_status_get_name purple_status_get_name
-#define gaim_status_is_independent purple_status_is_independent
-#define gaim_status_is_exclusive purple_status_is_exclusive
-#define gaim_status_is_available purple_status_is_available
-#define gaim_status_is_active purple_status_is_active
-#define gaim_status_is_online purple_status_is_online
-#define gaim_status_get_attr_value purple_status_get_attr_value
-#define gaim_status_get_attr_boolean purple_status_get_attr_boolean
-#define gaim_status_get_attr_int purple_status_get_attr_int
-#define gaim_status_get_attr_string purple_status_get_attr_string
-#define gaim_status_compare purple_status_compare
-
-#define gaim_presence_new purple_presence_new
-#define gaim_presence_new_for_account purple_presence_new_for_account
-#define gaim_presence_new_for_conv purple_presence_new_for_conv
-#define gaim_presence_new_for_buddy purple_presence_new_for_buddy
-#define gaim_presence_destroy purple_presence_destroy
-#define gaim_presence_add_status purple_presence_add_status
-#define gaim_presence_add_list purple_presence_add_list
-#define gaim_presence_set_status_active purple_presence_set_status_active
-#define gaim_presence_switch_status purple_presence_switch_status
-#define gaim_presence_set_idle purple_presence_set_idle
-#define gaim_presence_set_login_time purple_presence_set_login_time
-#define gaim_presence_get_context purple_presence_get_context
-#define gaim_presence_get_account purple_presence_get_account
-#define gaim_presence_get_conversation purple_presence_get_conversation
-#define gaim_presence_get_chat_user purple_presence_get_chat_user
-#define gaim_presence_get_statuses purple_presence_get_statuses
-#define gaim_presence_get_status purple_presence_get_status
-#define gaim_presence_get_active_status purple_presence_get_active_status
-#define gaim_presence_is_available purple_presence_is_available
-#define gaim_presence_is_online purple_presence_is_online
-#define gaim_presence_is_status_active purple_presence_is_status_active
-#define gaim_presence_is_status_primitive_active \
- purple_presence_is_status_primitive_active
-#define gaim_presence_is_idle purple_presence_is_idle
-#define gaim_presence_get_idle_time purple_presence_get_idle_time
-#define gaim_presence_get_login_time purple_presence_get_login_time
-#define gaim_presence_compare purple_presence_compare
-
-#define gaim_status_get_handle purple_status_get_handle
-#define gaim_status_init purple_status_init
-#define gaim_status_uninit purple_status_uninit
-
-/* from stringref.h */
-
-#define GaimStringref PurpleStringref
-
-#define gaim_stringref_new purple_stringref_new
-#define gaim_stringref_new_noref purple_stringref_new_noref
-#define gaim_stringref_printf purple_stringref_printf
-#define gaim_stringref_ref purple_stringref_ref
-#define gaim_stringref_unref purple_stringref_unref
-#define gaim_stringref_value purple_stringref_value
-#define gaim_stringref_cmp purple_stringref_cmp
-#define gaim_stringref_len purple_stringref_len
-
-/* from stun.h */
-
-#define GaimStunNatDiscovery PurpleStunNatDiscovery
-
-#define GAIM_STUN_STATUS_UNDISCOVERED PURPLE_STUN_STATUS_UNDISCOVERED
-#define GAIM_STUN_STATUS_UNKNOWN PURPLE_STUN_STATUS_UNKNOWN
-#define GAIM_STUN_STATUS_DISCOVERING PURPLE_STUN_STATUS_DISCOVERING
-#define GAIM_STUN_STATUS_DISCOVERED PURPLE_STUN_STATUS_DISCOVERED
-#define GaimStunStatus PurpleStunStatus
-
-#define GAIM_STUN_NAT_TYPE_PUBLIC_IP PURPLE_STUN_NAT_TYPE_PUBLIC_IP
-#define GAIM_STUN_NAT_TYPE_UNKNOWN_NAT PURPLE_STUN_NAT_TYPE_UNKNOWN_NAT
-#define GAIM_STUN_NAT_TYPE_FULL_CONE PURPLE_STUN_NAT_TYPE_FULL_CONE
-#define GAIM_STUN_NAT_TYPE_RESTRICTED_CONE PURPLE_STUN_NAT_TYPE_RESTRICTED_CONE
-#define GAIM_STUN_NAT_TYPE_PORT_RESTRICTED_CONE PURPLE_STUN_NAT_TYPE_PORT_RESTRICTED_CONE
-#define GAIM_STUN_NAT_TYPE_SYMMETRIC PURPLE_STUN_NAT_TYPE_SYMMETRIC
-#define GaimStunNatType PurpleStunNatType
-
-/* why didn't this have a Gaim prefix before? */
-#define StunCallback PurpleStunCallback
-
-#define gaim_stun_discover purple_stun_discover
-#define gaim_stun_init purple_stun_init
-
-/* from upnp.h */
-
-/* suggested rename: PurpleUPnpMappingHandle */
-#define UPnPMappingAddRemove PurpleUPnPMappingAddRemove
-
-#define GaimUPnPCallback PurpleUPnPCallback
-
-#define gaim_upnp_discover purple_upnp_discover
-#define gaim_upnp_get_public_ip purple_upnp_get_public_ip
-#define gaim_upnp_cancel_port_mapping purple_upnp_cancel_port_mapping
-#define gaim_upnp_set_port_mapping purple_upnp_set_port_mapping
-
-#define gaim_upnp_remove_port_mapping purple_upnp_remove_port_mapping
-
-/* from util.h */
-
-#define GaimUtilFetchUrlData PurpleUtilFetchUrlData
-#define GaimMenuAction PurpleMenuAction
-
-#define GaimInfoFieldFormatCallback PurpleIntoFieldFormatCallback
-
-#define GaimKeyValuePair PurpleKeyValuePair
-
-#define gaim_menu_action_new purple_menu_action_new
-#define gaim_menu_action_free purple_menu_action_free
-
-#define gaim_base16_encode purple_base16_encode
-#define gaim_base16_decode purple_base16_decode
-#define gaim_base64_encode purple_base64_encode
-#define gaim_base64_decode purple_base64_decode
-#define gaim_quotedp_decode purple_quotedp_decode
-
-#define gaim_mime_decode_field purple_mime_deco_field
-
-#define gaim_utf8_strftime purple_utf8_strftime
-#define gaim_date_format_short purple_date_format_short
-#define gaim_date_format_long purple_date_format_long
-#define gaim_date_format_full purple_date_format_full
-#define gaim_time_format purple_time_format
-#define gaim_time_build purple_time_build
-
-#define GAIM_NO_TZ_OFF PURPLE_NO_TZ_OFF
-
-#define gaim_str_to_time purple_str_to_time
-
-#define gaim_markup_find_tag purple_markup_find_tag
-#define gaim_markup_extract_info_field purple_markup_extract_info_field
-#define gaim_markup_html_to_xhtml purple_markup_html_to_xhtml
-#define gaim_markup_strip_html purple_markup_strip_html
-#define gaim_markup_linkify purple_markup_linkify
-#define gaim_markup_slice purple_markup_slice
-#define gaim_markup_get_tag_name purple_markup_get_tag_name
-#define gaim_unescape_html purple_unescape_html
-
-#define gaim_home_dir purple_home_dir
-#define gaim_user_dir purple_user_dir
-
-#define gaim_util_set_user_dir purple_util_set_user_dir
-
-#define gaim_build_dir purple_build_dir
-
-#define gaim_util_write_data_to_file purple_util_write_data_to_file
-
-#define gaim_util_read_xml_from_file purple_util_read_xml_from_file
-
-#define gaim_mkstemp purple_mkstemp
-
-#define gaim_program_is_valid purple_program_is_valid
-
-#define gaim_running_gnome purple_running_gnome
-#define gaim_running_kde purple_running_kde
-#define gaim_running_osx purple_running_osx
-
-#define gaim_fd_get_ip purple_fd_get_ip
-
-#define gaim_normalize purple_normalize
-#define gaim_normalize_nocase purple_normalize_nocase
-
-#define gaim_strdup_withhtml purple_strdup_withhtml
-
-#define gaim_str_has_prefix purple_str_has_prefix
-#define gaim_str_has_suffix purple_str_has_suffix
-#define gaim_str_add_cr purple_str_add_cr
-#define gaim_str_strip_char purple_str_strip_char
-
-#define gaim_util_chrreplace purple_util_chrreplace
-
-#define gaim_strreplace purple_strreplace
-
-#define gaim_utf8_ncr_encode purple_utf8_ncr_encode
-#define gaim_utf8_ncr_decode purple_utf8_ncr_decode
-
-#define gaim_strcasereplace purple_strcasereplace
-#define gaim_strcasestr purple_strcasestr
-
-#define gaim_str_size_to_units purple_str_size_to_units
-#define gaim_str_seconds_to_string purple_str_seconds_to_string
-#define gaim_str_binary_to_ascii purple_str_binary_to_ascii
-
-
-#define gaim_got_protocol_handler_uri purple_got_protocol_handler_uri
-
-#define gaim_url_parse purple_url_parse
-
-#define GaimUtilFetchUrlCallback PurpleUtilFetchUrlCallback
-#define gaim_util_fetch_url purple_util_fetch_url
-#define gaim_util_fetch_url_request purple_util_fetch_url_request
-#define gaim_util_fetch_url_cancel purple_util_fetch_url_cancel
-
-#define gaim_url_decode purple_url_decode
-#define gaim_url_encode purple_url_encode
-
-#define gaim_email_is_valid purple_email_is_valid
-
-#define gaim_uri_list_extract_uris purple_uri_list_extract_uris
-#define gaim_uri_list_extract_filenames purple_uri_list_extract_filenames
-
-#define gaim_utf8_try_convert purple_utf8_try_convert
-#define gaim_utf8_salvage purple_utf8_salvage
-#define gaim_utf8_strcasecmp purple_utf8_strcasecmp
-#define gaim_utf8_has_word purple_utf8_has_word
-
-#define gaim_print_utf8_to_console purple_print_utf8_to_console
-
-#define gaim_message_meify purple_message_meify
-
-#define gaim_text_strip_mnemonic purple_text_strip_mnemonic
-
-#define gaim_unescape_filename purple_unescape_filename
-#define gaim_escape_filename purple_escape_filename
-
-/* from value.h */
-
-#define GAIM_TYPE_UNKNOWN PURPLE_TYPE_UNKNOWN
-#define GAIM_TYPE_SUBTYPE PURPLE_TYPE_SUBTYPE
-#define GAIM_TYPE_CHAR PURPLE_TYPE_CHAR
-#define GAIM_TYPE_UCHAR PURPLE_TYPE_UCHAR
-#define GAIM_TYPE_BOOLEAN PURPLE_TYPE_BOOLEAN
-#define GAIM_TYPE_SHORT PURPLE_TYPE_SHORT
-#define GAIM_TYPE_USHORT PURPLE_TYPE_USHORT
-#define GAIM_TYPE_INT PURPLE_TYPE_INT
-#define GAIM_TYPE_UINT PURPLE_TYPE_UINT
-#define GAIM_TYPE_LONG PURPLE_TYPE_LONG
-#define GAIM_TYPE_ULONG PURPLE_TYPE_ULONG
-#define GAIM_TYPE_INT64 PURPLE_TYPE_INT64
-#define GAIM_TYPE_UINT64 PURPLE_TYPE_UINT64
-#define GAIM_TYPE_STRING PURPLE_TYPE_STRING
-#define GAIM_TYPE_OBJECT PURPLE_TYPE_OBJECT
-#define GAIM_TYPE_POINTER PURPLE_TYPE_POINTER
-#define GAIM_TYPE_ENUM PURPLE_TYPE_ENUM
-#define GAIM_TYPE_BOXED PURPLE_TYPE_BOXED
-#define GaimType PurpleType
-
-
-#define GAIM_SUBTYPE_UNKNOWN PURPLE_SUBTYPE_UNKNOWN
-#define GAIM_SUBTYPE_ACCOUNT PURPLE_SUBTYPE_ACCOUNT
-#define GAIM_SUBTYPE_BLIST PURPLE_SUBTYPE_BLIST
-#define GAIM_SUBTYPE_BLIST_BUDDY PURPLE_SUBTYPE_BLIST_BUDDY
-#define GAIM_SUBTYPE_BLIST_GROUP PURPLE_SUBTYPE_BLIST_GROUP
-#define GAIM_SUBTYPE_BLIST_CHAT PURPLE_SUBTYPE_BLIST_CHAT
-#define GAIM_SUBTYPE_BUDDY_ICON PURPLE_SUBTYPE_BUDDY_ICON
-#define GAIM_SUBTYPE_CONNECTION PURPLE_SUBTYPE_CONNECTION
-#define GAIM_SUBTYPE_CONVERSATION PURPLE_SUBTYPE_CONVERSATION
-#define GAIM_SUBTYPE_PLUGIN PURPLE_SUBTYPE_PLUGIN
-#define GAIM_SUBTYPE_BLIST_NODE PURPLE_SUBTYPE_BLIST_NODE
-#define GAIM_SUBTYPE_CIPHER PURPLE_SUBTYPE_CIPHER
-#define GAIM_SUBTYPE_STATUS PURPLE_SUBTYPE_STATUS
-#define GAIM_SUBTYPE_LOG PURPLE_SUBTYPE_LOG
-#define GAIM_SUBTYPE_XFER PURPLE_SUBTYPE_XFER
-#define GAIM_SUBTYPE_SAVEDSTATUS PURPLE_SUBTYPE_SAVEDSTATUS
-#define GAIM_SUBTYPE_XMLNODE PURPLE_SUBTYPE_XMLNODE
-#define GAIM_SUBTYPE_USERINFO PURPLE_SUBTYPE_USERINFO
-#define GaimSubType PurpleSubType
-
-#define GaimValue PurpleValue
-
-#define gaim_value_new purple_value_new
-#define gaim_value_new_outgoing purple_value_new_outgoing
-#define gaim_value_destroy purple_value_destroy
-#define gaim_value_dup purple_value_dup
-#define gaim_value_purple_buddy_icon_get_extensionget_type purple_value_get_type
-#define gaim_value_get_subtype purple_value_get_subtype
-#define gaim_value_get_specific_type purple_value_get_specific_type
-#define gaim_value_is_outgoing purple_value_is_outgoing
-#define gaim_value_set_char purple_value_set_char
-#define gaim_value_set_uchar purple_value_set_uchar
-#define gaim_value_set_boolean purple_value_set_boolean
-#define gaim_value_set_short purple_value_set_short
-#define gaim_value_set_ushort purple_value_set_ushort
-#define gaim_value_set_int purple_value_set_int
-#define gaim_value_set_uint purple_value_set_uint
-#define gaim_value_set_long purple_value_set_long
-#define gaim_value_set_ulong purple_value_set_ulong
-#define gaim_value_set_int64 purple_value_set_int64
-#define gaim_value_set_uint64 purple_value_set_uint64
-#define gaim_value_set_string purple_value_set_string
-#define gaim_value_set_object purple_value_set_object
-#define gaim_value_set_pointer purple_value_set_pointer
-#define gaim_value_set_enum purple_value_set_enum
-#define gaim_value_set_boxed purple_value_set_boxed
-#define gaim_value_get_char purple_value_get_char
-#define gaim_value_get_uchar purple_value_get_uchar
-#define gaim_value_get_boolean purple_value_get_boolean
-#define gaim_value_get_short purple_value_get_short
-#define gaim_value_get_ushort purple_value_get_ushort
-#define gaim_value_get_int purple_value_get_int
-#define gaim_value_get_uint purple_value_get_uint
-#define gaim_value_get_long purple_value_get_long
-#define gaim_value_get_ulong purple_value_get_ulong
-#define gaim_value_get_int64 purple_value_get_int64
-#define gaim_value_get_uint64 purple_value_get_uint64
-#define gaim_value_get_string purple_value_get_string
-#define gaim_value_get_object purple_value_get_object
-#define gaim_value_get_pointer purple_value_get_pointer
-#define gaim_value_get_enum purple_value_get_enum
-#define gaim_value_get_boxed purple_value_get_boxed
-
-/* from version.h */
-
-#define GAIM_MAJOR_VERSION PURPLE_MAJOR_VERSION
-#define GAIM_MINOR_VERSION PURPLE_MINOR_VERSION
-#define GAIM_MICRO_VERSION PURPLE_MICRO_VERSION
-
-#define GAIM_VERSION_CHECK PURPLE_VERSION_CHECK
-
-/* from whiteboard.h */
-
-#define GaimWhiteboardPrplOps PurpleWhiteboardPrplOps
-#define GaimWhiteboard PurpleWhiteboard
-#define GaimWhiteboardUiOps PurpleWhiteboardUiOps
-
-#define gaim_whiteboard_set_ui_ops purple_whiteboard_set_ui_ops
-#define gaim_whiteboard_set_prpl_ops purple_whiteboard_set_prpl_ops
-
-#define gaim_whiteboard_create purple_whiteboard_create
-#define gaim_whiteboard_destroy purple_whiteboard_destroy
-#define gaim_whiteboard_start purple_whiteboard_start
-#define gaim_whiteboard_get_session purple_whiteboard_get_session
-#define gaim_whiteboard_draw_list_destroy purple_whiteboard_draw_list_destroy
-#define gaim_whiteboard_get_dimensions purple_whiteboard_get_dimensions
-#define gaim_whiteboard_set_dimensions purple_whiteboard_set_dimensions
-#define gaim_whiteboard_draw_point purple_whiteboard_draw_point
-#define gaim_whiteboard_send_draw_list purple_whiteboard_send_draw_list
-#define gaim_whiteboard_draw_line purple_whiteboard_draw_line
-#define gaim_whiteboard_clear purple_whiteboard_clear
-#define gaim_whiteboard_send_clear purple_whiteboard_send_clear
-#define gaim_whiteboard_send_brush purple_whiteboard_send_brush
-#define gaim_whiteboard_get_brush purple_whiteboard_get_brush
-#define gaim_whiteboard_set_brush purple_whiteboard_set_brush
-
-/* for static plugins */
-#define gaim_init_ssl_plugin purple_init_ssl_plugin
-#define gaim_init_ssl_openssl_plugin purple_init_ssl_openssl_plugin
-#define gaim_init_ssl_gnutls_plugin purple_init_ssl_gnutls_plugin
-#define gaim_init_gg_plugin purple_init_gg_plugin
-#define gaim_init_jabber_plugin purple_init_jabber_plugin
-#define gaim_init_sametime_plugin purple_init_sametime_plugin
-#define gaim_init_msn_plugin purple_init_msn_plugin
-#define gaim_init_novell_plugin purple_init_novell_plugin
-#define gaim_init_qq_plugin purple_init_qq_plugin
-#define gaim_init_simple_plugin purple_init_simple_plugin
-#define gaim_init_yahoo_plugin purple_init_yahoo_plugin
-#define gaim_init_zephyr_plugin purple_init_zephyr_plugin
-#define gaim_init_aim_plugin purple_init_aim_plugin
-#define gaim_init_icq_plugin purple_init_icq_plugin
-
-#endif /* _GAIM_COMPAT_H_ */
diff --git a/libpurple/gconf/Makefile.am b/libpurple/gconf/Makefile.am
index 0e913aa80a..a6846876cf 100644
--- a/libpurple/gconf/Makefile.am
+++ b/libpurple/gconf/Makefile.am
@@ -5,11 +5,14 @@ EXTRA_DIST = purple.schemas.in
if INSTALL_I18N
schema_in_files = purple.schemas.in
schema_DATA = $(schema_in_files:.schemas.in=.schemas)
-@INTLTOOL_SCHEMAS_RULE@
+
+# silenced INTLTOOL_SCHEMAS_RULE
+%.schemas: %.schemas.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po)
+ $(AM_V_GEN) LC_ALL=C $(INTLTOOL_MERGE) -s -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< $@ > /dev/null
if GCONF_SCHEMAS_INSTALL
install-data-local:
- GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $(schema_DATA) 2>&1 | \
+ LANG=en GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $(schema_DATA) 2>&1 | \
grep -v "^WARNING: failed to install schema" | grep -v "^Attached schema" 1>&2 || true
else
install-data-local:
diff --git a/libpurple/glibcompat.h b/libpurple/glibcompat.h
new file mode 100644
index 0000000000..86dc3a5c4d
--- /dev/null
+++ b/libpurple/glibcompat.h
@@ -0,0 +1,181 @@
+/* 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 _GLIBCOMPAT_H_
+#define _GLIBCOMPAT_H_
+/*
+ * SECTION:glibcompat
+ * @section_id: libpurple-glibcompat
+ * @short_description: <filename>glibcompat.h</filename>
+ * @title: GLib version-dependent definitions
+ *
+ * This file is internal to libpurple. Do not use!
+ * Also, any public API should not depend on this file.
+ */
+
+#include <glib.h>
+
+#if !GLIB_CHECK_VERSION(2, 36, 0)
+
+#include <errno.h>
+#include <fcntl.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+static inline gboolean g_close(gint fd, GError **error)
+{
+ int res;
+ int errsv;
+
+ res = close(fd);
+
+ if (G_LIKELY(res == 0))
+ return TRUE;
+ if (G_UNLIKELY(errno == EINTR))
+ return TRUE;
+
+ errsv = errno;
+ g_set_error_literal(error, G_FILE_ERROR,
+ g_file_error_from_errno(errsv), g_strerror(errsv));
+ errno = errsv;
+
+ return FALSE;
+}
+
+#if !GLIB_CHECK_VERSION(2, 32, 0)
+
+#include <glib-object.h>
+#include <string.h>
+
+#define G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+#define G_GNUC_END_IGNORE_DEPRECATIONS
+
+#define G_SOURCE_REMOVE FALSE
+#define G_SOURCE_CONTINUE TRUE
+
+#define g_signal_handlers_disconnect_by_data(instance, data) \
+ g_signal_handlers_disconnect_matched((instance), G_SIGNAL_MATCH_DATA, \
+ 0, 0, NULL, NULL, (data))
+
+static inline GThread * g_thread_try_new(const gchar *name, GThreadFunc func,
+ gpointer data, GError **error)
+{
+ return g_thread_create(func, data, TRUE, error);
+}
+
+#if !GLIB_CHECK_VERSION(2, 30, 0)
+
+static inline gchar *g_utf8_substring(const gchar *str, glong start_pos,
+ glong end_pos)
+{
+ gchar *start = g_utf8_offset_to_pointer(str, start_pos);
+ gchar *end = g_utf8_offset_to_pointer(start, end_pos - start_pos);
+ gchar *out = g_malloc(end - start + 1);
+
+ memcpy(out, start, end - start);
+ out[end - start] = 0;
+
+ return out;
+}
+
+#if !GLIB_CHECK_VERSION(2, 28, 0)
+
+static inline gint64 g_get_monotonic_time(void)
+{
+ GTimeVal time_s;
+
+ g_get_current_time(&time_s);
+
+ return ((gint64)time_s.tv_sec << 32) | time_s.tv_usec;
+}
+
+static inline void g_list_free_full(GList *list, GDestroyNotify free_func)
+{
+ g_list_foreach(list, (GFunc)free_func, NULL);
+ g_list_free(list);
+}
+
+static inline void g_slist_free_full(GSList *list, GDestroyNotify free_func)
+{
+ g_slist_foreach(list, (GFunc)free_func, NULL);
+ g_slist_free(list);
+}
+
+#if !GLIB_CHECK_VERSION(2, 26, 0)
+
+typedef struct stat GStatBuf;
+
+static inline void g_object_notify_by_pspec(GObject *object, GParamSpec *pspec)
+{
+ g_object_notify(object, g_param_spec_get_name(pspec));
+}
+
+static inline void g_object_class_install_properties(GObjectClass *oclass,
+ guint n_pspecs, GParamSpec **pspecs)
+{
+ guint i;
+ for (i = 1; i < n_pspecs; ++i)
+ g_object_class_install_property(oclass, i, pspecs[i]);
+}
+
+#endif /* < 2.26.0 */
+
+#endif /* < 2.28.0 */
+
+#endif /* < 2.30.0 */
+
+#endif /* < 2.32.0 */
+
+#endif /* < 2.36.0 */
+
+
+/* glib's definition of g_stat+GStatBuf seems to be broken on mingw64-w32 (and
+ * possibly other 32-bit windows), so instead of relying on it,
+ * we'll define our own.
+ */
+#if defined(_WIN32) && !defined(_MSC_VER) && !defined(_WIN64)
+# include <glib/gstdio.h>
+typedef struct _stat GStatBufW32;
+static inline int
+purple_g_stat(const gchar *filename, GStatBufW32 *buf)
+{
+ return g_stat(filename, (GStatBuf*)buf);
+}
+# define GStatBuf GStatBufW32
+# define g_stat purple_g_stat
+#endif
+
+
+#ifdef __clang__
+
+#undef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+#define G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ _Pragma ("clang diagnostic push") \
+ _Pragma ("clang diagnostic ignored \"-Wdeprecated-declarations\"")
+
+#undef G_GNUC_END_IGNORE_DEPRECATIONS
+#define G_GNUC_END_IGNORE_DEPRECATIONS \
+ _Pragma ("clang diagnostic pop")
+
+#endif /* __clang__ */
+
+#endif /* _GLIBCOMPAT_H_ */
diff --git a/libpurple/http.c b/libpurple/http.c
new file mode 100644
index 0000000000..a12abec3ba
--- /dev/null
+++ b/libpurple/http.c
@@ -0,0 +1,3219 @@
+/* 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 "http.h"
+
+#include "internal.h"
+#include "glibcompat.h"
+
+#include "debug.h"
+#include "ntlm.h"
+#include "purple-socket.h"
+
+#include <zlib.h>
+#ifndef z_const
+#define z_const
+#endif
+
+#define PURPLE_HTTP_URL_CREDENTIALS_CHARS "a-z0-9.,~_/*!&%?=+\\^-"
+#define PURPLE_HTTP_MAX_RECV_BUFFER_LEN 10240
+#define PURPLE_HTTP_MAX_READ_BUFFER_LEN 10240
+#define PURPLE_HTTP_GZ_BUFF_LEN 1024
+
+#define PURPLE_HTTP_REQUEST_DEFAULT_MAX_REDIRECTS 20
+#define PURPLE_HTTP_REQUEST_DEFAULT_TIMEOUT 30
+#define PURPLE_HTTP_REQUEST_DEFAULT_MAX_LENGTH 1048576
+#define PURPLE_HTTP_REQUEST_HARD_MAX_LENGTH G_MAXINT32-1
+
+#define PURPLE_HTTP_PROGRESS_WATCHER_DEFAULT_INTERVAL 250000
+
+typedef struct _PurpleHttpSocket PurpleHttpSocket;
+
+typedef struct _PurpleHttpHeaders PurpleHttpHeaders;
+
+typedef struct _PurpleHttpKeepaliveHost PurpleHttpKeepaliveHost;
+
+typedef struct _PurpleHttpKeepaliveRequest PurpleHttpKeepaliveRequest;
+
+typedef struct _PurpleHttpGzStream PurpleHttpGzStream;
+
+struct _PurpleHttpSocket
+{
+ PurpleSocket *ps;
+
+ gboolean is_busy;
+ guint use_count;
+ PurpleHttpKeepaliveHost *host;
+};
+
+struct _PurpleHttpRequest
+{
+ int ref_count;
+
+ gchar *url;
+ gchar *method;
+ PurpleHttpHeaders *headers;
+ PurpleHttpCookieJar *cookie_jar;
+ PurpleHttpKeepalivePool *keepalive_pool;
+
+ gchar *contents;
+ int contents_length;
+ PurpleHttpContentReader contents_reader;
+ gpointer contents_reader_data;
+ PurpleHttpContentWriter response_writer;
+ gpointer response_writer_data;
+
+ int timeout;
+ int max_redirects;
+ gboolean http11;
+ guint max_length;
+};
+
+struct _PurpleHttpConnection
+{
+ PurpleConnection *gc;
+ PurpleHttpCallback callback;
+ gpointer user_data;
+ gboolean is_reading;
+ gboolean is_keepalive;
+ gboolean is_cancelling;
+
+ PurpleHttpURL *url;
+ PurpleHttpRequest *request;
+ PurpleHttpResponse *response;
+
+ PurpleHttpKeepaliveRequest *socket_request;
+ PurpleHttpConnectionSet *connection_set;
+ PurpleHttpSocket *socket;
+ GString *request_header;
+ guint request_header_written, request_contents_written;
+ gboolean main_header_got, headers_got;
+ GString *response_buffer;
+ PurpleHttpGzStream *gz_stream;
+
+ GString *contents_reader_buffer;
+ gboolean contents_reader_requested;
+
+ int redirects_count;
+
+ int length_expected;
+ guint length_got, length_got_decompressed;
+
+ gboolean is_chunked, in_chunk, chunks_done;
+ int chunk_length, chunk_got;
+
+ GList *link_global, *link_gc;
+
+ guint timeout_handle;
+
+ PurpleHttpProgressWatcher watcher;
+ gpointer watcher_user_data;
+ guint watcher_interval_threshold;
+ gint64 watcher_last_call;
+ guint watcher_delayed_handle;
+};
+
+struct _PurpleHttpResponse
+{
+ int code;
+ gchar *error;
+
+ GString *contents;
+ PurpleHttpHeaders *headers;
+};
+
+struct _PurpleHttpURL
+{
+ gchar *protocol;
+ gchar *username;
+ gchar *password;
+ gchar *host;
+ int port;
+ gchar *path;
+ gchar *fragment;
+};
+
+struct _PurpleHttpHeaders
+{
+ GList *list;
+ GHashTable *by_name;
+};
+
+typedef struct
+{
+ time_t expires;
+ gchar *value;
+} PurpleHttpCookie;
+
+struct _PurpleHttpCookieJar
+{
+ int ref_count;
+
+ GHashTable *tab;
+};
+
+struct _PurpleHttpKeepaliveRequest
+{
+ PurpleConnection *gc;
+ PurpleSocketConnectCb cb;
+ gpointer user_data;
+
+ PurpleHttpKeepaliveHost *host;
+ PurpleHttpSocket *hs;
+};
+
+struct _PurpleHttpKeepaliveHost
+{
+ PurpleHttpKeepalivePool *pool;
+
+ gchar *host;
+ int port;
+ gboolean is_ssl;
+
+ GSList *sockets; /* list of PurpleHttpSocket */
+
+ GSList *queue; /* list of PurpleHttpKeepaliveRequest */
+ guint process_queue_timeout;
+};
+
+struct _PurpleHttpKeepalivePool
+{
+ gboolean is_destroying;
+
+ int ref_count;
+
+ guint limit_per_host;
+
+ /* key: purple_http_socket_hash, value: PurpleHttpKeepaliveHost */
+ GHashTable *by_hash;
+};
+
+struct _PurpleHttpConnectionSet
+{
+ gboolean is_destroying;
+
+ GHashTable *connections;
+};
+
+struct _PurpleHttpGzStream
+{
+ gboolean failed;
+ z_stream zs;
+ gsize max_output;
+ gsize decompressed;
+ GString *pending;
+};
+
+static time_t purple_http_rfc1123_to_time(const gchar *str);
+
+static gboolean purple_http_request_is_method(PurpleHttpRequest *request,
+ const gchar *method);
+
+static PurpleHttpConnection * purple_http_connection_new(
+ PurpleHttpRequest *request, PurpleConnection *gc);
+static void purple_http_connection_terminate(PurpleHttpConnection *hc);
+static void purple_http_conn_notify_progress_watcher(PurpleHttpConnection *hc);
+static void
+purple_http_conn_retry(PurpleHttpConnection *http_conn);
+
+static PurpleHttpResponse * purple_http_response_new(void);
+static void purple_http_response_free(PurpleHttpResponse *response);
+
+static void purple_http_cookie_jar_parse(PurpleHttpCookieJar *cookie_jar,
+ GList *values);
+static gchar * purple_http_cookie_jar_gen(PurpleHttpCookieJar *cookie_jar);
+gchar * purple_http_cookie_jar_dump(PurpleHttpCookieJar *cjar);
+
+static PurpleHttpKeepaliveRequest *
+purple_http_keepalive_pool_request(PurpleHttpKeepalivePool *pool,
+ PurpleConnection *gc, const gchar *host, int port, gboolean is_ssl,
+ PurpleSocketConnectCb cb, gpointer user_data);
+static void
+purple_http_keepalive_pool_request_cancel(PurpleHttpKeepaliveRequest *req);
+static void
+purple_http_keepalive_pool_release(PurpleHttpSocket *hs, gboolean invalidate);
+
+static void
+purple_http_connection_set_remove(PurpleHttpConnectionSet *set,
+ PurpleHttpConnection *http_conn);
+
+static GRegex *purple_http_re_url, *purple_http_re_url_host,
+ *purple_http_re_rfc1123;
+
+/*
+ * Values: pointers to running PurpleHttpConnection.
+ */
+static GList *purple_http_hc_list;
+
+/*
+ * Keys: pointers to PurpleConnection.
+ * Values: GList of pointers to running PurpleHttpConnection.
+ */
+static GHashTable *purple_http_hc_by_gc;
+
+/*
+ * Keys: pointers to PurpleConnection.
+ * Values: gboolean TRUE.
+ */
+static GHashTable *purple_http_cancelling_gc;
+
+/*
+ * Keys: pointers to PurpleHttpConnection.
+ * Values: pointers to links in purple_http_hc_list.
+ */
+static GHashTable *purple_http_hc_by_ptr;
+
+/*** Helper functions *********************************************************/
+
+static time_t purple_http_rfc1123_to_time(const gchar *str)
+{
+ static const gchar *months[13] = {
+ "jan", "feb", "mar", "apr", "may", "jun",
+ "jul", "aug", "sep", "oct", "nov", "dec", NULL
+ };
+ GMatchInfo *match_info;
+ gchar *d_date, *d_month, *d_year, *d_time;
+ int month;
+ gchar *iso_date;
+ time_t t;
+
+ g_return_val_if_fail(str != NULL, 0);
+
+ g_regex_match(purple_http_re_rfc1123, str, 0, &match_info);
+ if (!g_match_info_matches(match_info)) {
+ g_match_info_free(match_info);
+ return 0;
+ }
+
+ d_date = g_match_info_fetch(match_info, 1);
+ d_month = g_match_info_fetch(match_info, 2);
+ d_year = g_match_info_fetch(match_info, 3);
+ d_time = g_match_info_fetch(match_info, 4);
+
+ g_match_info_free(match_info);
+
+ month = 0;
+ while (months[month] != NULL) {
+ if (0 == g_ascii_strcasecmp(d_month, months[month]))
+ break;
+ month++;
+ }
+ month++;
+
+ iso_date = g_strdup_printf("%s-%02d-%sT%s+00:00",
+ d_year, month, d_date, d_time);
+
+ g_free(d_date);
+ g_free(d_month);
+ g_free(d_year);
+ g_free(d_time);
+
+ if (month > 12) {
+ purple_debug_warning("http", "Invalid month: %s\n", d_month);
+ g_free(iso_date);
+ return 0;
+ }
+
+ t = purple_str_to_time(iso_date, TRUE, NULL, NULL, NULL);
+
+ g_free(iso_date);
+
+ return t;
+}
+
+/*** GZip streams *************************************************************/
+
+static PurpleHttpGzStream *
+purple_http_gz_new(gsize max_output, gboolean is_deflate)
+{
+ PurpleHttpGzStream *gzs = g_new0(PurpleHttpGzStream, 1);
+ int windowBits;
+
+ if (is_deflate)
+ windowBits = -MAX_WBITS;
+ else /* is gzip */
+ windowBits = MAX_WBITS + 32;
+
+ if (inflateInit2(&gzs->zs, windowBits) != Z_OK) {
+ purple_debug_error("http", "Cannot initialize zlib stream\n");
+ g_free(gzs);
+ return NULL;
+ }
+
+ gzs->max_output = max_output;
+
+ return gzs;
+}
+
+static GString *
+purple_http_gz_put(PurpleHttpGzStream *gzs, const gchar *buf, gsize len)
+{
+ const gchar *compressed_buff;
+ gsize compressed_len;
+ GString *ret;
+ z_stream *zs;
+
+ g_return_val_if_fail(gzs != NULL, NULL);
+ g_return_val_if_fail(buf != NULL, NULL);
+
+ if (gzs->failed)
+ return NULL;
+
+ zs = &gzs->zs;
+
+ if (gzs->pending) {
+ g_string_append_len(gzs->pending, buf, len);
+ compressed_buff = gzs->pending->str;
+ compressed_len = gzs->pending->len;
+ } else {
+ compressed_buff = buf;
+ compressed_len = len;
+ }
+
+ zs->next_in = (z_const Bytef*)compressed_buff;
+ zs->avail_in = compressed_len;
+
+ ret = g_string_new(NULL);
+ while (zs->avail_in > 0) {
+ int gzres;
+ gchar decompressed_buff[PURPLE_HTTP_GZ_BUFF_LEN];
+ gsize decompressed_len;
+
+ zs->next_out = (Bytef*)decompressed_buff;
+ zs->avail_out = sizeof(decompressed_buff);
+ decompressed_len = zs->avail_out = sizeof(decompressed_buff);
+ gzres = inflate(zs, Z_FULL_FLUSH);
+ decompressed_len -= zs->avail_out;
+
+ if (gzres == Z_OK || gzres == Z_STREAM_END) {
+ if (decompressed_len == 0)
+ break;
+ if (gzs->decompressed + decompressed_len >=
+ gzs->max_output)
+ {
+ purple_debug_warning("http", "Maximum amount of"
+ " decompressed data is reached\n");
+ decompressed_len = gzs->max_output -
+ gzs->decompressed;
+ gzres = Z_STREAM_END;
+ }
+ gzs->decompressed += decompressed_len;
+ g_string_append_len(ret, decompressed_buff,
+ decompressed_len);
+ if (gzres == Z_STREAM_END)
+ break;
+ } else {
+ purple_debug_error("http",
+ "Decompression failed (%d): %s\n", gzres,
+ zs->msg);
+ gzs->failed = TRUE;
+ return NULL;
+ }
+ }
+
+ if (gzs->pending) {
+ g_string_free(gzs->pending, TRUE);
+ gzs->pending = NULL;
+ }
+
+ if (zs->avail_in > 0) {
+ gzs->pending = g_string_new_len((gchar*)zs->next_in,
+ zs->avail_in);
+ }
+
+ return ret;
+}
+
+static void
+purple_http_gz_free(PurpleHttpGzStream *gzs)
+{
+ if (gzs == NULL)
+ return;
+ inflateEnd(&gzs->zs);
+ if (gzs->pending)
+ g_string_free(gzs->pending, TRUE);
+ g_free(gzs);
+}
+
+/*** HTTP Sockets *************************************************************/
+
+static gchar *
+purple_http_socket_hash(const gchar *host, int port, gboolean is_ssl)
+{
+ return g_strdup_printf("%c:%s:%d", (is_ssl ? 'S' : 'R'), host, port);
+}
+
+static PurpleHttpSocket *
+purple_http_socket_connect_new(PurpleConnection *gc, const gchar *host,
+ int port, gboolean is_ssl, PurpleSocketConnectCb cb, gpointer user_data)
+{
+ PurpleHttpSocket *hs = g_new0(PurpleHttpSocket, 1);
+
+ hs->ps = purple_socket_new(gc);
+ purple_socket_set_data(hs->ps, "hs", hs);
+ purple_socket_set_tls(hs->ps, is_ssl);
+ purple_socket_set_host(hs->ps, host);
+ purple_socket_set_port(hs->ps, port);
+ if (!purple_socket_connect(hs->ps, cb, user_data)) {
+ purple_socket_destroy(hs->ps);
+ g_free(hs);
+ return NULL;
+ }
+
+ if (purple_debug_is_verbose())
+ purple_debug_misc("http", "new socket created: %p\n", hs);
+
+ return hs;
+}
+
+static void
+purple_http_socket_close_free(PurpleHttpSocket *hs)
+{
+ if (hs == NULL)
+ return;
+
+ if (purple_debug_is_verbose())
+ purple_debug_misc("http", "destroying socket: %p\n", hs);
+
+ purple_socket_destroy(hs->ps);
+ g_free(hs);
+}
+
+/*** Headers collection *******************************************************/
+
+static PurpleHttpHeaders * purple_http_headers_new(void);
+static void purple_http_headers_free(PurpleHttpHeaders *hdrs);
+static void purple_http_headers_add(PurpleHttpHeaders *hdrs, const gchar *key,
+ const gchar *value);
+static const GList * purple_http_headers_get_all(PurpleHttpHeaders *hdrs);
+static GList * purple_http_headers_get_all_by_name(
+ PurpleHttpHeaders *hdrs, const gchar *key);
+static const gchar * purple_http_headers_get(PurpleHttpHeaders *hdrs,
+ const gchar *key);
+static gboolean purple_http_headers_get_int(PurpleHttpHeaders *hdrs,
+ const gchar *key, int *dst);
+static gboolean purple_http_headers_match(PurpleHttpHeaders *hdrs,
+ const gchar *key, const gchar *value);
+static gchar * purple_http_headers_dump(PurpleHttpHeaders *hdrs);
+
+static PurpleHttpHeaders * purple_http_headers_new(void)
+{
+ PurpleHttpHeaders *hdrs = g_new0(PurpleHttpHeaders, 1);
+
+ hdrs->by_name = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+ (GDestroyNotify)g_list_free);
+
+ return hdrs;
+}
+
+static void purple_http_headers_free_kvp(PurpleKeyValuePair *kvp)
+{
+ g_free(kvp->key);
+ g_free(kvp->value);
+ g_free(kvp);
+}
+
+static void purple_http_headers_free(PurpleHttpHeaders *hdrs)
+{
+ if (hdrs == NULL)
+ return;
+
+ g_hash_table_destroy(hdrs->by_name);
+ g_list_free_full(hdrs->list,
+ (GDestroyNotify)purple_http_headers_free_kvp);
+ g_free(hdrs);
+}
+
+static void purple_http_headers_add(PurpleHttpHeaders *hdrs, const gchar *key,
+ const gchar *value)
+{
+ PurpleKeyValuePair *kvp;
+ GList *named_values, *new_values;
+ gchar *key_low;
+
+ g_return_if_fail(hdrs != NULL);
+ g_return_if_fail(key != NULL);
+ g_return_if_fail(value != NULL);
+
+ kvp = g_new0(PurpleKeyValuePair, 1);
+ kvp->key = g_strdup(key);
+ kvp->value = g_strdup(value);
+ hdrs->list = g_list_append(hdrs->list, kvp);
+
+ key_low = g_ascii_strdown(key, -1);
+ named_values = g_hash_table_lookup(hdrs->by_name, key_low);
+ new_values = g_list_append(named_values, kvp->value);
+ if (named_values)
+ g_free(key_low);
+ else
+ g_hash_table_insert(hdrs->by_name, key_low, new_values);
+}
+
+static void purple_http_headers_remove(PurpleHttpHeaders *hdrs,
+ const gchar *key)
+{
+ GList *it, *curr;
+
+ g_return_if_fail(hdrs != NULL);
+ g_return_if_fail(key != NULL);
+
+ if (!g_hash_table_remove(hdrs->by_name, key))
+ return;
+
+ /* Could be optimized to O(1). */
+ it = g_list_first(hdrs->list);
+ while (it) {
+ PurpleKeyValuePair *kvp = it->data;
+ curr = it;
+ it = g_list_next(it);
+ if (g_ascii_strcasecmp(kvp->key, key) != 0)
+ continue;
+
+ hdrs->list = g_list_delete_link(hdrs->list, curr);
+ purple_http_headers_free_kvp(kvp);
+ }
+}
+
+static const GList * purple_http_headers_get_all(PurpleHttpHeaders *hdrs)
+{
+ g_return_val_if_fail(hdrs != NULL, NULL);
+
+ return hdrs->list;
+}
+
+/* return const */
+static GList * purple_http_headers_get_all_by_name(
+ PurpleHttpHeaders *hdrs, const gchar *key)
+{
+ GList *values;
+ gchar *key_low;
+
+ g_return_val_if_fail(hdrs != NULL, NULL);
+ g_return_val_if_fail(key != NULL, NULL);
+
+ key_low = g_ascii_strdown(key, -1);
+ values = g_hash_table_lookup(hdrs->by_name, key_low);
+ g_free(key_low);
+
+ return values;
+}
+
+static const gchar * purple_http_headers_get(PurpleHttpHeaders *hdrs,
+ const gchar *key)
+{
+ const GList *values = purple_http_headers_get_all_by_name(hdrs, key);
+
+ if (!values)
+ return NULL;
+
+ return values->data;
+}
+
+static gboolean purple_http_headers_get_int(PurpleHttpHeaders *hdrs,
+ const gchar *key, int *dst)
+{
+ int val;
+ const gchar *str;
+
+ str = purple_http_headers_get(hdrs, key);
+ if (!str)
+ return FALSE;
+
+ if (1 != sscanf(str, "%d", &val))
+ return FALSE;
+
+ *dst = val;
+ return TRUE;
+}
+
+static gboolean purple_http_headers_match(PurpleHttpHeaders *hdrs,
+ const gchar *key, const gchar *value)
+{
+ const gchar *str;
+
+ str = purple_http_headers_get(hdrs, key);
+ if (str == NULL || value == NULL)
+ return str == value;
+
+ return (g_ascii_strcasecmp(str, value) == 0);
+}
+
+static gchar * purple_http_headers_dump(PurpleHttpHeaders *hdrs)
+{
+ const GList *hdr;
+
+ GString *s = g_string_new("");
+
+ hdr = purple_http_headers_get_all(hdrs);
+ while (hdr) {
+ PurpleKeyValuePair *kvp = hdr->data;
+ hdr = g_list_next(hdr);
+
+ g_string_append_printf(s, "%s: %s%s", kvp->key,
+ (gchar*)kvp->value, hdr ? "\n" : "");
+ }
+
+ return g_string_free(s, FALSE);
+}
+
+/*** HTTP protocol backend ****************************************************/
+
+static void _purple_http_disconnect(PurpleHttpConnection *hc,
+ gboolean is_graceful);
+
+static void _purple_http_gen_headers(PurpleHttpConnection *hc);
+static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc, gint fd);
+static void _purple_http_recv(gpointer _hc, gint fd,
+ PurpleInputCondition cond);
+static void _purple_http_send(gpointer _hc, gint fd, PurpleInputCondition cond);
+
+/* closes current connection (if exists), estabilishes one and proceeds with
+ * request */
+static gboolean _purple_http_reconnect(PurpleHttpConnection *hc);
+
+static void _purple_http_error(PurpleHttpConnection *hc, const char *format,
+ ...) G_GNUC_PRINTF(2, 3);
+
+static void _purple_http_error(PurpleHttpConnection *hc, const char *format,
+ ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ hc->response->error = g_strdup_vprintf(format, args);
+ va_end(args);
+
+ if (purple_debug_is_verbose())
+ purple_debug_warning("http", "error: %s\n", hc->response->error);
+
+ purple_http_conn_cancel(hc);
+}
+
+static void _purple_http_gen_headers(PurpleHttpConnection *hc)
+{
+ GString *h;
+ PurpleHttpURL *url;
+ const GList *hdr;
+ PurpleHttpRequest *req;
+ PurpleHttpHeaders *hdrs;
+ gchar *request_url, *tmp_url = NULL;
+
+ PurpleProxyInfo *proxy;
+ gboolean proxy_http = FALSE;
+ const gchar *proxy_username, *proxy_password;
+
+ g_return_if_fail(hc != NULL);
+
+ if (hc->request_header != NULL)
+ return;
+
+ req = hc->request;
+ url = hc->url;
+ hdrs = req->headers;
+ proxy = purple_proxy_get_setup(hc->gc ?
+ purple_connection_get_account(hc->gc) : NULL);
+
+ proxy_http = (purple_proxy_info_get_proxy_type(proxy) == PURPLE_PROXY_HTTP ||
+ purple_proxy_info_get_proxy_type(proxy) == PURPLE_PROXY_USE_ENVVAR);
+ /* this is HTTP proxy, but used with tunelling with CONNECT */
+ if (proxy_http && url->port != 80)
+ proxy_http = FALSE;
+
+ hc->request_header = h = g_string_new("");
+ hc->request_header_written = 0;
+ hc->request_contents_written = 0;
+
+ if (proxy_http)
+ request_url = tmp_url = purple_http_url_print(url);
+ else
+ request_url = url->path;
+
+ g_string_append_printf(h, "%s %s HTTP/%s\r\n",
+ req->method ? req->method : "GET",
+ request_url,
+ req->http11 ? "1.1" : "1.0");
+
+ if (tmp_url)
+ g_free(tmp_url);
+
+ if (!purple_http_headers_get(hdrs, "host"))
+ g_string_append_printf(h, "Host: %s\r\n", url->host);
+ if (!purple_http_headers_get(hdrs, "connection")) {
+ g_string_append(h, "Connection: ");
+ g_string_append(h, hc->is_keepalive ?
+ "Keep-Alive\r\n" : "close\r\n");
+ }
+ if (!purple_http_headers_get(hdrs, "accept"))
+ g_string_append(h, "Accept: */*\r\n");
+ if (!purple_http_headers_get(hdrs, "accept-encoding"))
+ g_string_append(h, "Accept-Encoding: gzip, deflate\r\n");
+
+ if (!purple_http_headers_get(hdrs, "content-length") && (
+ req->contents_length > 0 ||
+ purple_http_request_is_method(req, "post")))
+ {
+ g_string_append_printf(h, "Content-Length: %u\r\n",
+ req->contents_length);
+ }
+
+ if (proxy_http)
+ g_string_append(h, "Proxy-Connection: close\r\n"); /* TEST: proxy+KeepAlive */
+
+ proxy_username = purple_proxy_info_get_username(proxy);
+ if (proxy_http && proxy_username != NULL && proxy_username[0] != '\0') {
+ gchar *proxy_auth, *ntlm_type1, *tmp;
+ int len;
+
+ proxy_password = purple_proxy_info_get_password(proxy);
+ if (proxy_password == NULL)
+ proxy_password = "";
+
+ tmp = g_strdup_printf("%s:%s", proxy_username, proxy_password);
+ len = strlen(tmp);
+ proxy_auth = purple_base64_encode((const guchar *)tmp, len);
+ memset(tmp, 0, len);
+ g_free(tmp);
+
+ ntlm_type1 = purple_ntlm_gen_type1(purple_get_host_name(), "");
+
+ g_string_append_printf(h, "Proxy-Authorization: Basic %s\r\n",
+ proxy_auth);
+ g_string_append_printf(h, "Proxy-Authorization: NTLM %s\r\n",
+ ntlm_type1);
+ g_string_append(h, "Proxy-Connection: close\r\n"); /* TEST: proxy+KeepAlive */
+
+ memset(proxy_auth, 0, strlen(proxy_auth));
+ g_free(proxy_auth);
+ g_free(ntlm_type1);
+ }
+
+ hdr = purple_http_headers_get_all(hdrs);
+ while (hdr) {
+ PurpleKeyValuePair *kvp = hdr->data;
+ hdr = g_list_next(hdr);
+
+ g_string_append_printf(h, "%s: %s\r\n",
+ kvp->key, (gchar*)kvp->value);
+ }
+
+ if (!purple_http_cookie_jar_is_empty(req->cookie_jar)) {
+ gchar * cookies = purple_http_cookie_jar_gen(req->cookie_jar);
+ g_string_append_printf(h, "Cookie: %s\r\n", cookies);
+ g_free(cookies);
+ }
+
+ g_string_append_printf(h, "\r\n");
+
+ if (purple_debug_is_unsafe() && purple_debug_is_verbose()) {
+ purple_debug_misc("http", "Generated request headers:\n%s",
+ h->str);
+ }
+}
+
+static gboolean _purple_http_recv_headers(PurpleHttpConnection *hc,
+ const gchar *buf, int len)
+{
+ gchar *eol, *delim;
+
+ if (hc->headers_got) {
+ purple_debug_error("http", "Headers already got\n");
+ _purple_http_error(hc, _("Error parsing HTTP"));
+ return FALSE;
+ }
+
+ g_string_append_len(hc->response_buffer, buf, len);
+ if (hc->response_buffer->len > PURPLE_HTTP_MAX_RECV_BUFFER_LEN) {
+ purple_debug_error("http",
+ "Buffer too big when parsing headers\n");
+ _purple_http_error(hc, _("Error parsing HTTP"));
+ return FALSE;
+ }
+
+ while ((eol = strstr(hc->response_buffer->str, "\r\n"))
+ != NULL)
+ {
+ gchar *hdrline = hc->response_buffer->str;
+ int hdrline_len = eol - hdrline;
+
+ hdrline[hdrline_len] = '\0';
+
+ if (hdrline[0] == '\0') {
+ if (!hc->main_header_got) {
+ if (purple_debug_is_verbose() &&
+ hc->is_keepalive)
+ {
+ purple_debug_misc("http", "Got keep-"
+ "alive terminator from previous"
+ " request\n");
+ } else {
+ purple_debug_warning("http", "Got empty"
+ " line at the beginning - this "
+ "may be a HTTP server quirk\n");
+ }
+ } else /* hc->main_header_got */ {
+ hc->headers_got = TRUE;
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("http", "Got headers "
+ "end\n");
+ }
+ }
+ } else if (!hc->main_header_got) {
+ hc->main_header_got = TRUE;
+ delim = strchr(hdrline, ' ');
+ if (delim == NULL || 1 != sscanf(delim + 1, "%d",
+ &hc->response->code))
+ {
+ purple_debug_warning("http",
+ "Invalid response code\n");
+ _purple_http_error(hc, _("Error parsing HTTP"));
+ return FALSE;
+ }
+ if (purple_debug_is_verbose())
+ purple_debug_misc("http",
+ "Got main header with code %d\n",
+ hc->response->code);
+ } else {
+ if (purple_debug_is_verbose() &&
+ purple_debug_is_unsafe())
+ purple_debug_misc("http", "Got header: %s\n",
+ hdrline);
+ delim = strchr(hdrline, ':');
+ if (delim == NULL || delim == hdrline) {
+ purple_debug_warning("http",
+ "Bad header delimiter\n");
+ _purple_http_error(hc, _("Error parsing HTTP"));
+ return FALSE;
+ }
+ *delim++ = '\0';
+ while (*delim == ' ')
+ delim++;
+
+ purple_http_headers_add(hc->response->headers, hdrline, delim);
+ }
+
+ g_string_erase(hc->response_buffer, 0, hdrline_len + 2);
+ if (hc->headers_got)
+ break;
+ }
+ return TRUE;
+}
+
+static gboolean _purple_http_recv_body_data(PurpleHttpConnection *hc,
+ const gchar *buf, int len)
+{
+ GString *decompressed = NULL;
+
+ if (hc->length_expected >= 0 &&
+ len + hc->length_got > (guint)hc->length_expected)
+ {
+ len = hc->length_expected - hc->length_got;
+ }
+
+ hc->length_got += len;
+
+ if (hc->gz_stream != NULL) {
+ decompressed = purple_http_gz_put(hc->gz_stream, buf, len);
+ if (decompressed == NULL) {
+ _purple_http_error(hc,
+ _("Error while decompressing data"));
+ return FALSE;
+ }
+ buf = decompressed->str;
+ len = decompressed->len;
+ }
+
+ g_assert(hc->request->max_length <=
+ PURPLE_HTTP_REQUEST_HARD_MAX_LENGTH);
+ if (hc->length_got_decompressed + len > hc->request->max_length) {
+ purple_debug_warning("http",
+ "Maximum length exceeded, truncating\n");
+ len = hc->request->max_length - hc->length_got_decompressed;
+ hc->length_expected = hc->length_got;
+ }
+ hc->length_got_decompressed += len;
+
+ if (len == 0) {
+ if (decompressed != NULL)
+ g_string_free(decompressed, TRUE);
+ return TRUE;
+ }
+
+ if (hc->request->response_writer != NULL) {
+ gboolean succ;
+ succ = hc->request->response_writer(hc, hc->response, buf,
+ hc->length_got_decompressed, len,
+ hc->request->response_writer_data);
+ if (!succ) {
+ if (decompressed != NULL)
+ g_string_free(decompressed, TRUE);
+ purple_debug_error("http",
+ "Cannot write using callback\n");
+ _purple_http_error(hc,
+ _("Error handling retrieved data"));
+ return FALSE;
+ }
+ } else {
+ if (hc->response->contents == NULL)
+ hc->response->contents = g_string_new("");
+ g_string_append_len(hc->response->contents, buf, len);
+ }
+
+ if (decompressed != NULL)
+ g_string_free(decompressed, TRUE);
+
+ purple_http_conn_notify_progress_watcher(hc);
+ return TRUE;
+}
+
+static gboolean _purple_http_recv_body_chunked(PurpleHttpConnection *hc,
+ const gchar *buf, int len)
+{
+ gchar *eol, *line;
+ int line_len;
+
+ if (hc->chunks_done)
+ return FALSE;
+ if (!hc->response_buffer)
+ hc->response_buffer = g_string_new("");
+
+ g_string_append_len(hc->response_buffer, buf, len);
+ if (hc->response_buffer->len > PURPLE_HTTP_MAX_RECV_BUFFER_LEN) {
+ purple_debug_error("http",
+ "Buffer too big when searching for chunk\n");
+ _purple_http_error(hc, _("Error parsing HTTP"));
+ return FALSE;
+ }
+
+ while (hc->response_buffer->len > 0) {
+ if (hc->in_chunk) {
+ int got_now = hc->response_buffer->len;
+ if (hc->chunk_got + got_now > hc->chunk_length)
+ got_now = hc->chunk_length - hc->chunk_got;
+ hc->chunk_got += got_now;
+
+ if (!_purple_http_recv_body_data(hc,
+ hc->response_buffer->str, got_now))
+ return FALSE;
+
+ g_string_erase(hc->response_buffer, 0, got_now);
+ hc->in_chunk = (hc->chunk_got < hc->chunk_length);
+
+ continue;
+ }
+
+ line = hc->response_buffer->str;
+ eol = strstr(line, "\r\n");
+ if (eol == line) {
+ g_string_erase(hc->response_buffer, 0, 2);
+ line = hc->response_buffer->str;
+ eol = strstr(line, "\r\n");
+ }
+ if (eol == NULL) {
+ /* waiting for more data (unlikely, but possible) */
+ if (hc->response_buffer->len > 20) {
+ purple_debug_warning("http", "Chunk length not "
+ "found (buffer too large)\n");
+ _purple_http_error(hc, _("Error parsing HTTP"));
+ return FALSE;
+ }
+ return TRUE;
+ }
+ line_len = eol - line;
+
+ if (1 != sscanf(line, "%x", &hc->chunk_length)) {
+ if (purple_debug_is_unsafe())
+ purple_debug_warning("http",
+ "Chunk length not found in [%s]\n",
+ line);
+ else
+ purple_debug_warning("http",
+ "Chunk length not found\n");
+ _purple_http_error(hc, _("Error parsing HTTP"));
+ return FALSE;
+ }
+ hc->chunk_got = 0;
+ hc->in_chunk = TRUE;
+
+ if (purple_debug_is_verbose())
+ purple_debug_misc("http", "Found chunk of length %d\n", hc->chunk_length);
+
+ g_string_erase(hc->response_buffer, 0, line_len + 2);
+
+ if (hc->chunk_length == 0) {
+ hc->chunks_done = TRUE;
+ hc->in_chunk = FALSE;
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean _purple_http_recv_body(PurpleHttpConnection *hc,
+ const gchar *buf, int len)
+{
+ if (hc->is_chunked)
+ return _purple_http_recv_body_chunked(hc, buf, len);
+
+ return _purple_http_recv_body_data(hc, buf, len);
+}
+
+static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc, gint fd)
+{
+ int len;
+ gchar buf[4096];
+ gboolean got_anything;
+
+ len = purple_socket_read(hc->socket->ps, (guchar*)buf, sizeof(buf));
+ got_anything = (len > 0);
+
+ if (len < 0 && errno == EAGAIN)
+ return FALSE;
+
+ if (len < 0) {
+ _purple_http_error(hc, _("Error reading from %s: %s"),
+ hc->url->host, g_strerror(errno));
+ return FALSE;
+ }
+
+ /* EOF */
+ if (len == 0) {
+ if (hc->request->max_length == 0) {
+ /* It's definitely YHttpServer quirk. */
+ purple_debug_warning("http", "Got EOF, but no data was "
+ "expected (this may be a server quirk)\n");
+ hc->length_expected = hc->length_got;
+ }
+ if (hc->length_expected >= 0 &&
+ hc->length_got < (guint)hc->length_expected)
+ {
+ purple_debug_warning("http", "No more data while reading"
+ " contents\n");
+ _purple_http_error(hc, _("Error parsing HTTP"));
+ return FALSE;
+ }
+ if (hc->headers_got)
+ hc->length_expected = hc->length_got;
+ else if (hc->length_got == 0 && hc->socket->use_count > 1) {
+ purple_debug_info("http", "Keep-alive connection "
+ "expired (when reading), retrying...\n");
+ purple_http_conn_retry(hc);
+ return FALSE;
+ } else {
+ const gchar *server = purple_http_headers_get(
+ hc->response->headers, "Server");
+ if (server &&
+ g_ascii_strcasecmp(server, "YHttpServer") == 0)
+ {
+ purple_debug_warning("http", "No more data "
+ "while parsing headers (YHttpServer "
+ "quirk)\n");
+ hc->headers_got = TRUE;
+ hc->length_expected = hc->length_got = 0;
+ hc->length_got_decompressed = 0;
+ } else {
+ purple_debug_warning("http", "No more data "
+ "while parsing headers\n");
+ _purple_http_error(hc, _("Error parsing HTTP"));
+ return FALSE;
+ }
+ }
+ }
+
+ if (!hc->headers_got && len > 0) {
+ if (!_purple_http_recv_headers(hc, buf, len))
+ return FALSE;
+ len = 0;
+ if (hc->headers_got) {
+ gboolean is_gzip, is_deflate;
+ if (!purple_http_headers_get_int(hc->response->headers,
+ "Content-Length", &hc->length_expected))
+ hc->length_expected = -1;
+ hc->is_chunked = (purple_http_headers_match(
+ hc->response->headers,
+ "Transfer-Encoding", "chunked"));
+ is_gzip = purple_http_headers_match(
+ hc->response->headers, "Content-Encoding",
+ "gzip");
+ is_deflate = purple_http_headers_match(
+ hc->response->headers, "Content-Encoding",
+ "deflate");
+ if (is_gzip || is_deflate) {
+ hc->gz_stream = purple_http_gz_new(
+ hc->request->max_length + 1,
+ is_deflate);
+ }
+ }
+ if (hc->headers_got && hc->response_buffer &&
+ hc->response_buffer->len > 0)
+ {
+ int buffer_len = hc->response_buffer->len;
+ gchar *buffer = g_string_free(hc->response_buffer, FALSE);
+ hc->response_buffer = NULL;
+ _purple_http_recv_body(hc, buffer, buffer_len);
+ }
+ if (!hc->headers_got)
+ return got_anything;
+ }
+
+ if (len > 0) {
+ if (!_purple_http_recv_body(hc, buf, len))
+ return FALSE;
+ }
+
+ if (hc->is_chunked && hc->chunks_done && hc->length_expected < 0)
+ hc->length_expected = hc->length_got;
+
+ if (hc->length_expected >= 0 &&
+ hc->length_got >= (guint)hc->length_expected)
+ {
+ const gchar *redirect;
+
+ if (hc->is_chunked && !hc->chunks_done) {
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("http",
+ "I need the terminating empty chunk\n");
+ }
+ return TRUE;
+ }
+
+ if (!hc->headers_got) {
+ hc->response->code = 0;
+ purple_debug_warning("http", "No headers got\n");
+ _purple_http_error(hc, _("Error parsing HTTP"));
+ return FALSE;
+ }
+
+ if (purple_debug_is_unsafe() && purple_debug_is_verbose()) {
+ gchar *hdrs = purple_http_headers_dump(
+ hc->response->headers);
+ purple_debug_misc("http", "Got response headers: %s\n",
+ hdrs);
+ g_free(hdrs);
+ }
+
+ purple_http_cookie_jar_parse(hc->request->cookie_jar,
+ purple_http_headers_get_all_by_name(
+ hc->response->headers, "Set-Cookie"));
+
+ if (purple_debug_is_unsafe() && purple_debug_is_verbose() &&
+ !purple_http_cookie_jar_is_empty(
+ hc->request->cookie_jar))
+ {
+ gchar *cookies = purple_http_cookie_jar_dump(
+ hc->request->cookie_jar);
+ purple_debug_misc("http", "Cookies: %s\n", cookies);
+ g_free(cookies);
+ }
+
+ if (hc->response->code == 407) {
+ _purple_http_error(hc, _("Invalid proxy credentials"));
+ return FALSE;
+ }
+
+ redirect = purple_http_headers_get(hc->response->headers,
+ "location");
+ if (redirect && (hc->request->max_redirects == -1 ||
+ hc->request->max_redirects > hc->redirects_count))
+ {
+ PurpleHttpURL *url = purple_http_url_parse(redirect);
+
+ hc->redirects_count++;
+
+ if (!url) {
+ if (purple_debug_is_unsafe())
+ purple_debug_warning("http",
+ "Invalid redirect to %s\n",
+ redirect);
+ else
+ purple_debug_warning("http",
+ "Invalid redirect\n");
+ _purple_http_error(hc, _("Error parsing HTTP"));
+ }
+
+ purple_http_url_relative(hc->url, url);
+ purple_http_url_free(url);
+
+ _purple_http_reconnect(hc);
+ return FALSE;
+ }
+
+ _purple_http_disconnect(hc, TRUE);
+ purple_http_connection_terminate(hc);
+ return FALSE;
+ }
+
+ return got_anything;
+}
+
+static void _purple_http_recv(gpointer _hc, gint fd, PurpleInputCondition cond)
+{
+ PurpleHttpConnection *hc = _hc;
+
+ while (_purple_http_recv_loopbody(hc, fd));
+}
+
+static void _purple_http_send_got_data(PurpleHttpConnection *hc,
+ gboolean success, gboolean eof, size_t stored)
+{
+ int estimated_length;
+
+ g_return_if_fail(hc != NULL);
+
+ if (!success) {
+ _purple_http_error(hc, _("Error requesting data to write"));
+ return;
+ }
+
+ hc->contents_reader_requested = FALSE;
+ g_string_set_size(hc->contents_reader_buffer, stored);
+ if (!eof)
+ return;
+
+ estimated_length = hc->request_contents_written + stored;
+
+ if (hc->request->contents_length != -1 &&
+ hc->request->contents_length != estimated_length)
+ {
+ purple_debug_warning("http",
+ "Invalid amount of data has been written\n");
+ }
+ hc->request->contents_length = estimated_length;
+}
+
+static void _purple_http_send(gpointer _hc, gint fd, PurpleInputCondition cond)
+{
+ PurpleHttpConnection *hc = _hc;
+ int written, write_len;
+ const gchar *write_from;
+ gboolean writing_headers;
+
+ /* Waiting for data. This could be written more efficiently, by removing
+ * (and later, adding) hs->inpa. */
+ if (hc->contents_reader_requested)
+ return;
+
+ _purple_http_gen_headers(hc);
+
+ writing_headers =
+ (hc->request_header_written < hc->request_header->len);
+ if (writing_headers) {
+ write_from = hc->request_header->str +
+ hc->request_header_written;
+ write_len = hc->request_header->len -
+ hc->request_header_written;
+ } else if (hc->request->contents_reader) {
+ if (hc->contents_reader_requested)
+ return; /* waiting for data */
+ if (!hc->contents_reader_buffer)
+ hc->contents_reader_buffer = g_string_new("");
+ if (hc->contents_reader_buffer->len == 0) {
+ hc->contents_reader_requested = TRUE;
+ g_string_set_size(hc->contents_reader_buffer,
+ PURPLE_HTTP_MAX_READ_BUFFER_LEN);
+ hc->request->contents_reader(hc,
+ hc->contents_reader_buffer->str,
+ hc->request_contents_written,
+ PURPLE_HTTP_MAX_READ_BUFFER_LEN,
+ hc->request->contents_reader_data,
+ _purple_http_send_got_data);
+ return;
+ }
+ write_from = hc->contents_reader_buffer->str;
+ write_len = hc->contents_reader_buffer->len;
+ } else {
+ write_from = hc->request->contents +
+ hc->request_contents_written;
+ write_len = hc->request->contents_length -
+ hc->request_contents_written;
+ }
+
+ if (write_len == 0) {
+ purple_debug_warning("http", "Nothing to write\n");
+ written = 0;
+ } else {
+ written = purple_socket_write(hc->socket->ps,
+ (const guchar*)write_from, write_len);
+ }
+
+ if (written < 0 && errno == EAGAIN)
+ return;
+
+ if (written < 0) {
+ if (hc->request_header_written == 0 &&
+ hc->socket->use_count > 1)
+ {
+ purple_debug_info("http", "Keep-alive connection "
+ "expired (when writing), retrying...\n");
+ purple_http_conn_retry(hc);
+ return;
+ }
+
+ _purple_http_error(hc, _("Error writing to %s: %s"),
+ hc->url->host, g_strerror(errno));
+ return;
+ }
+
+ if (writing_headers) {
+ hc->request_header_written += written;
+ purple_http_conn_notify_progress_watcher(hc);
+ if (hc->request_header_written < hc->request_header->len)
+ return;
+ if (hc->request->contents_length > 0)
+ return;
+ } else {
+ hc->request_contents_written += written;
+ purple_http_conn_notify_progress_watcher(hc);
+ if (hc->contents_reader_buffer)
+ g_string_erase(hc->contents_reader_buffer, 0, written);
+ if (hc->request->contents_length > 0 &&
+ hc->request_contents_written <
+ (guint)hc->request->contents_length)
+ {
+ return;
+ }
+ }
+
+ /* request is completely written, let's read the response */
+ hc->is_reading = TRUE;
+ purple_socket_watch(hc->socket->ps, PURPLE_INPUT_READ,
+ _purple_http_recv, hc);
+}
+
+static void _purple_http_disconnect(PurpleHttpConnection *hc,
+ gboolean is_graceful)
+{
+ g_return_if_fail(hc != NULL);
+
+ if (hc->request_header)
+ g_string_free(hc->request_header, TRUE);
+ hc->request_header = NULL;
+
+ if (hc->response_buffer)
+ g_string_free(hc->response_buffer, TRUE);
+ hc->response_buffer = NULL;
+
+ if (hc->socket_request)
+ purple_http_keepalive_pool_request_cancel(hc->socket_request);
+ else {
+ purple_http_keepalive_pool_release(hc->socket, !is_graceful);
+ hc->socket = NULL;
+ }
+}
+
+static void
+_purple_http_connected(PurpleSocket *ps, const gchar *error, gpointer _hc)
+{
+ PurpleHttpSocket *hs = NULL;
+ PurpleHttpConnection *hc = _hc;
+
+ if (ps != NULL)
+ hs = purple_socket_get_data(ps, "hs");
+
+ hc->socket_request = NULL;
+ hc->socket = hs;
+
+ if (error != NULL) {
+ _purple_http_error(hc, _("Unable to connect to %s: %s"),
+ hc->url->host, error);
+ return;
+ }
+
+ purple_socket_watch(ps, PURPLE_INPUT_WRITE, _purple_http_send, hc);
+}
+
+static gboolean _purple_http_reconnect(PurpleHttpConnection *hc)
+{
+ PurpleHttpURL *url;
+ gboolean is_ssl = FALSE;
+
+ g_return_val_if_fail(hc != NULL, FALSE);
+ g_return_val_if_fail(hc->url != NULL, FALSE);
+
+ _purple_http_disconnect(hc, TRUE);
+
+ if (purple_debug_is_verbose()) {
+ if (purple_debug_is_unsafe()) {
+ gchar *urlp = purple_http_url_print(hc->url);
+ purple_debug_misc("http", "Connecting to %s...\n", urlp);
+ g_free(urlp);
+ } else
+ purple_debug_misc("http", "Connecting to %s...\n",
+ hc->url->host);
+ }
+
+ url = hc->url;
+ if (g_strcmp0(url->protocol, "") == 0 ||
+ g_ascii_strcasecmp(url->protocol, "http") == 0)
+ {
+ /* do nothing */
+ } else if (g_ascii_strcasecmp(url->protocol, "https") == 0) {
+ is_ssl = TRUE;
+ } else {
+ _purple_http_error(hc, _("Unsupported protocol: %s"),
+ url->protocol);
+ return FALSE;
+ }
+
+ if (is_ssl && !purple_ssl_is_supported()) {
+ _purple_http_error(hc, _("Unable to connect to %s: %s"),
+ url->host, _("Server requires TLS/SSL, "
+ "but no TLS/SSL support was found."));
+ return FALSE;
+ }
+
+ if (hc->request->keepalive_pool != NULL) {
+ hc->socket_request = purple_http_keepalive_pool_request(
+ hc->request->keepalive_pool, hc->gc, url->host,
+ url->port, is_ssl, _purple_http_connected, hc);
+ } else {
+ hc->socket = purple_http_socket_connect_new(hc->gc, url->host,
+ url->port, is_ssl, _purple_http_connected, hc);
+ }
+
+ if (hc->socket_request == NULL && hc->socket == NULL) {
+ _purple_http_error(hc, _("Unable to connect to %s"), url->host);
+ return FALSE;
+ }
+
+ purple_http_headers_free(hc->response->headers);
+ hc->response->headers = purple_http_headers_new();
+ hc->response_buffer = g_string_new("");
+ hc->main_header_got = FALSE;
+ hc->headers_got = FALSE;
+ if (hc->response->contents != NULL)
+ g_string_free(hc->response->contents, TRUE);
+ hc->response->contents = NULL;
+ hc->length_got = 0;
+ hc->length_got_decompressed = 0;
+ hc->length_expected = -1;
+ hc->is_chunked = FALSE;
+ hc->in_chunk = FALSE;
+ hc->chunks_done = FALSE;
+
+ purple_http_conn_notify_progress_watcher(hc);
+
+ return TRUE;
+}
+
+/*** Performing HTTP requests *************************************************/
+
+static gboolean purple_http_request_timeout(gpointer _hc)
+{
+ PurpleHttpConnection *hc = _hc;
+
+ purple_debug_warning("http", "Timeout reached for request %p\n", hc);
+
+ purple_http_conn_cancel(hc);
+
+ return FALSE;
+}
+
+PurpleHttpConnection * purple_http_get(PurpleConnection *gc,
+ PurpleHttpCallback callback, gpointer user_data, const gchar *url)
+{
+ PurpleHttpRequest *request;
+ PurpleHttpConnection *hc;
+
+ g_return_val_if_fail(url != NULL, NULL);
+
+ request = purple_http_request_new(url);
+ hc = purple_http_request(gc, request, callback, user_data);
+ purple_http_request_unref(request);
+
+ return hc;
+}
+
+PurpleHttpConnection * purple_http_get_printf(PurpleConnection *gc,
+ PurpleHttpCallback callback, gpointer user_data,
+ const gchar *format, ...)
+{
+ va_list args;
+ gchar *value;
+ PurpleHttpConnection *ret;
+
+ g_return_val_if_fail(format != NULL, NULL);
+
+ va_start(args, format);
+ value = g_strdup_vprintf(format, args);
+ va_end(args);
+
+ ret = purple_http_get(gc, callback, user_data, value);
+ g_free(value);
+
+ return ret;
+}
+
+PurpleHttpConnection * purple_http_request(PurpleConnection *gc,
+ PurpleHttpRequest *request, PurpleHttpCallback callback,
+ gpointer user_data)
+{
+ PurpleHttpConnection *hc;
+
+ g_return_val_if_fail(request != NULL, NULL);
+
+ if (request->url == NULL) {
+ purple_debug_error("http", "Cannot perform new request - "
+ "URL is not set\n");
+ return NULL;
+ }
+
+ if (g_hash_table_lookup(purple_http_cancelling_gc, gc)) {
+ purple_debug_warning("http", "Cannot perform another HTTP "
+ "request while cancelling all related with this "
+ "PurpleConnection\n");
+ return NULL;
+ }
+
+ hc = purple_http_connection_new(request, gc);
+ hc->callback = callback;
+ hc->user_data = user_data;
+
+ hc->url = purple_http_url_parse(request->url);
+
+ if (purple_debug_is_unsafe())
+ purple_debug_misc("http", "Performing new request %p for %s.\n",
+ hc, request->url);
+ else
+ purple_debug_misc("http", "Performing new request %p to %s.\n",
+ hc, hc->url ? hc->url->host : NULL);
+
+ if (!hc->url || hc->url->host == NULL || hc->url->host[0] == '\0') {
+ purple_debug_error("http", "Invalid URL requested.\n");
+ purple_http_connection_terminate(hc);
+ return NULL;
+ }
+
+ _purple_http_reconnect(hc);
+
+ hc->timeout_handle = purple_timeout_add_seconds(request->timeout,
+ purple_http_request_timeout, hc);
+
+ return hc;
+}
+
+/*** HTTP connection API ******************************************************/
+
+static void purple_http_connection_free(PurpleHttpConnection *hc);
+static gboolean purple_http_conn_notify_progress_watcher_timeout(gpointer _hc);
+
+static PurpleHttpConnection * purple_http_connection_new(
+ PurpleHttpRequest *request, PurpleConnection *gc)
+{
+ PurpleHttpConnection *hc = g_new0(PurpleHttpConnection, 1);
+
+ g_assert(request != NULL);
+
+ hc->request = request;
+ purple_http_request_ref(request);
+ hc->response = purple_http_response_new();
+ hc->is_keepalive = (request->keepalive_pool != NULL);
+
+ hc->link_global = purple_http_hc_list =
+ g_list_prepend(purple_http_hc_list, hc);
+ g_hash_table_insert(purple_http_hc_by_ptr, hc, hc->link_global);
+ if (gc) {
+ GList *gc_list = g_hash_table_lookup(purple_http_hc_by_gc, gc);
+ g_hash_table_steal(purple_http_hc_by_gc, gc);
+ hc->link_gc = gc_list = g_list_prepend(gc_list, hc);
+ g_hash_table_insert(purple_http_hc_by_gc, gc, gc_list);
+ hc->gc = gc;
+ }
+
+ return hc;
+}
+
+static void purple_http_connection_free(PurpleHttpConnection *hc)
+{
+ if (hc->timeout_handle)
+ purple_timeout_remove(hc->timeout_handle);
+ if (hc->watcher_delayed_handle)
+ purple_timeout_remove(hc->watcher_delayed_handle);
+
+ if (hc->connection_set != NULL)
+ purple_http_connection_set_remove(hc->connection_set, hc);
+
+ purple_http_url_free(hc->url);
+ purple_http_request_unref(hc->request);
+ purple_http_response_free(hc->response);
+
+ if (hc->contents_reader_buffer)
+ g_string_free(hc->contents_reader_buffer, TRUE);
+ purple_http_gz_free(hc->gz_stream);
+
+ if (hc->request_header)
+ g_string_free(hc->request_header, TRUE);
+
+ purple_http_hc_list = g_list_delete_link(purple_http_hc_list,
+ hc->link_global);
+ g_hash_table_remove(purple_http_hc_by_ptr, hc);
+ if (hc->gc) {
+ GList *gc_list, *gc_list_new;
+ gc_list = g_hash_table_lookup(purple_http_hc_by_gc, hc->gc);
+ g_assert(gc_list != NULL);
+
+ gc_list_new = g_list_delete_link(gc_list, hc->link_gc);
+ if (gc_list != gc_list_new) {
+ g_hash_table_steal(purple_http_hc_by_gc, hc->gc);
+ if (gc_list_new)
+ g_hash_table_insert(purple_http_hc_by_gc,
+ hc->gc, gc_list_new);
+ }
+ }
+
+ g_free(hc);
+}
+
+/* call callback and do the cleanup */
+static void purple_http_connection_terminate(PurpleHttpConnection *hc)
+{
+ g_return_if_fail(hc != NULL);
+
+ purple_debug_misc("http", "Request %p performed %s.\n", hc,
+ purple_http_response_is_successful(hc->response) ?
+ "successfully" : "without success");
+
+ if (hc->callback)
+ hc->callback(hc, hc->response, hc->user_data);
+
+ purple_http_connection_free(hc);
+}
+
+void purple_http_conn_cancel(PurpleHttpConnection *http_conn)
+{
+ if (http_conn == NULL)
+ return;
+
+ if (http_conn->is_cancelling)
+ return;
+ http_conn->is_cancelling = TRUE;
+
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("http", "Cancelling connection %p...\n",
+ http_conn);
+ }
+
+ http_conn->response->code = 0;
+ _purple_http_disconnect(http_conn, FALSE);
+ purple_http_connection_terminate(http_conn);
+}
+
+static void
+purple_http_conn_retry(PurpleHttpConnection *http_conn)
+{
+ if (http_conn == NULL)
+ return;
+
+ purple_debug_info("http", "Retrying connection %p...\n", http_conn);
+
+ http_conn->response->code = 0;
+ _purple_http_disconnect(http_conn, FALSE);
+ _purple_http_reconnect(http_conn);
+}
+
+void purple_http_conn_cancel_all(PurpleConnection *gc)
+{
+ GList *gc_list;
+
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("http", "Cancelling all running HTTP "
+ "connections\n");
+ }
+
+ gc_list = g_hash_table_lookup(purple_http_hc_by_gc, gc);
+
+ g_hash_table_insert(purple_http_cancelling_gc, gc, GINT_TO_POINTER(TRUE));
+
+ while (gc_list) {
+ PurpleHttpConnection *hc = gc_list->data;
+ gc_list = g_list_next(gc_list);
+ purple_http_conn_cancel(hc);
+ }
+
+ g_hash_table_remove(purple_http_cancelling_gc, gc);
+
+ if (NULL != g_hash_table_lookup(purple_http_hc_by_gc, gc))
+ purple_debug_fatal("http", "Couldn't cancel all connections "
+ "related to gc=%p (it shouldn't happen)\n", gc);
+}
+
+gboolean purple_http_conn_is_running(PurpleHttpConnection *http_conn)
+{
+ if (http_conn == NULL)
+ return FALSE;
+ return (NULL != g_hash_table_lookup(purple_http_hc_by_ptr, http_conn));
+}
+
+PurpleHttpRequest * purple_http_conn_get_request(PurpleHttpConnection *http_conn)
+{
+ g_return_val_if_fail(http_conn != NULL, NULL);
+
+ return http_conn->request;
+}
+
+PurpleHttpCookieJar * purple_http_conn_get_cookie_jar(
+ PurpleHttpConnection *http_conn)
+{
+ return purple_http_request_get_cookie_jar(purple_http_conn_get_request(
+ http_conn));
+}
+
+PurpleConnection * purple_http_conn_get_purple_connection(
+ PurpleHttpConnection *http_conn)
+{
+ g_return_val_if_fail(http_conn != NULL, NULL);
+
+ return http_conn->gc;
+}
+
+void purple_http_conn_set_progress_watcher(PurpleHttpConnection *http_conn,
+ PurpleHttpProgressWatcher watcher, gpointer user_data,
+ gint interval_threshold)
+{
+ g_return_if_fail(http_conn != NULL);
+
+ if (interval_threshold < 0) {
+ interval_threshold =
+ PURPLE_HTTP_PROGRESS_WATCHER_DEFAULT_INTERVAL;
+ }
+
+ http_conn->watcher = watcher;
+ http_conn->watcher_user_data = user_data;
+ http_conn->watcher_interval_threshold = interval_threshold;
+}
+
+static void purple_http_conn_notify_progress_watcher(
+ PurpleHttpConnection *hc)
+{
+ gint64 now;
+ gboolean reading_state;
+ int processed, total;
+
+ g_return_if_fail(hc != NULL);
+
+ if (hc->watcher == NULL)
+ return;
+
+ reading_state = hc->is_reading;
+ if (reading_state) {
+ total = hc->length_expected;
+ processed = hc->length_got;
+ } else {
+ total = hc->request->contents_length;
+ processed = hc->request_contents_written;
+ if (total == 0)
+ total = -1;
+ }
+ if (total != -1 && total < processed) {
+ purple_debug_warning("http", "Processed too much\n");
+ total = processed;
+ }
+
+ now = g_get_monotonic_time();
+ if (hc->watcher_last_call + hc->watcher_interval_threshold
+ > now && processed != total)
+ {
+ if (hc->watcher_delayed_handle)
+ return;
+ hc->watcher_delayed_handle = purple_timeout_add_seconds(
+ 1 + hc->watcher_interval_threshold / 1000000,
+ purple_http_conn_notify_progress_watcher_timeout, hc);
+ return;
+ }
+
+ if (hc->watcher_delayed_handle)
+ purple_timeout_remove(hc->watcher_delayed_handle);
+ hc->watcher_delayed_handle = 0;
+
+ hc->watcher_last_call = now;
+ hc->watcher(hc, reading_state, processed, total, hc->watcher_user_data);
+}
+
+static gboolean purple_http_conn_notify_progress_watcher_timeout(gpointer _hc)
+{
+ PurpleHttpConnection *hc = _hc;
+
+ purple_http_conn_notify_progress_watcher(hc);
+
+ return FALSE;
+}
+
+/*** Cookie jar API ***********************************************************/
+
+static PurpleHttpCookie * purple_http_cookie_new(const gchar *value);
+void purple_http_cookie_free(PurpleHttpCookie *cookie);
+
+static void purple_http_cookie_jar_set_ext(PurpleHttpCookieJar *cookie_jar,
+ const gchar *name, const gchar *value, time_t expires);
+
+static PurpleHttpCookie * purple_http_cookie_new(const gchar *value)
+{
+ PurpleHttpCookie *cookie = g_new0(PurpleHttpCookie, 1);
+
+ cookie->value = g_strdup(value);
+ cookie->expires = -1;
+
+ return cookie;
+}
+
+void purple_http_cookie_free(PurpleHttpCookie *cookie)
+{
+ g_free(cookie->value);
+ g_free(cookie);
+}
+
+void purple_http_cookie_jar_free(PurpleHttpCookieJar *cookie_jar);
+
+PurpleHttpCookieJar * purple_http_cookie_jar_new(void)
+{
+ PurpleHttpCookieJar *cjar = g_new0(PurpleHttpCookieJar, 1);
+
+ cjar->ref_count = 1;
+ cjar->tab = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+ (GDestroyNotify)purple_http_cookie_free);
+
+ return cjar;
+}
+
+void purple_http_cookie_jar_free(PurpleHttpCookieJar *cookie_jar)
+{
+ g_hash_table_destroy(cookie_jar->tab);
+ g_free(cookie_jar);
+}
+
+void purple_http_cookie_jar_ref(PurpleHttpCookieJar *cookie_jar)
+{
+ g_return_if_fail(cookie_jar != NULL);
+
+ cookie_jar->ref_count++;
+}
+
+PurpleHttpCookieJar * purple_http_cookie_jar_unref(
+ PurpleHttpCookieJar *cookie_jar)
+{
+ if (cookie_jar == NULL)
+ return NULL;
+
+ g_return_val_if_fail(cookie_jar->ref_count > 0, NULL);
+
+ cookie_jar->ref_count--;
+ if (cookie_jar->ref_count > 0)
+ return cookie_jar;
+
+ purple_http_cookie_jar_free(cookie_jar);
+ return NULL;
+}
+
+static void purple_http_cookie_jar_parse(PurpleHttpCookieJar *cookie_jar,
+ GList *values)
+{
+ values = g_list_first(values);
+ while (values) {
+ const gchar *cookie = values->data;
+ const gchar *eqsign, *semicolon;
+ gchar *name, *value;
+ time_t expires = -1;
+ values = g_list_next(values);
+
+ eqsign = strchr(cookie, '=');
+ semicolon = strchr(cookie, ';');
+
+ if (eqsign == NULL || eqsign == cookie ||
+ (semicolon != NULL && semicolon < eqsign))
+ {
+ if (purple_debug_is_unsafe())
+ purple_debug_warning("http",
+ "Invalid cookie: [%s]\n", cookie);
+ else
+ purple_debug_warning("http", "Invalid cookie.");
+ continue;
+ }
+
+ name = g_strndup(cookie, eqsign - cookie);
+ eqsign++;
+ if (semicolon != NULL)
+ value = g_strndup(eqsign, semicolon - eqsign);
+ else
+ value = g_strdup(eqsign);
+
+ if (semicolon != NULL) {
+ GMatchInfo *match_info;
+ GRegex *re_expires = g_regex_new( /* XXX: make it static */
+ "expires=([a-z0-9, :]+)",
+ G_REGEX_OPTIMIZE | G_REGEX_CASELESS,
+ G_REGEX_MATCH_NOTEMPTY, NULL);
+
+ g_regex_match(re_expires, semicolon, 0, &match_info);
+ if (g_match_info_matches(match_info)) {
+ gchar *expire_date =
+ g_match_info_fetch(match_info, 1);
+ expires = purple_http_rfc1123_to_time(
+ expire_date);
+ g_free(expire_date);
+ }
+ g_match_info_free(match_info);
+
+ g_regex_unref(re_expires);
+ }
+
+ purple_http_cookie_jar_set_ext(cookie_jar, name, value, expires);
+
+ g_free(name);
+ g_free(value);
+ }
+}
+
+static gchar * purple_http_cookie_jar_gen(PurpleHttpCookieJar *cookie_jar)
+{
+ GHashTableIter it;
+ gchar *key;
+ PurpleHttpCookie *cookie;
+ GString *str;
+ time_t now = time(NULL);
+
+ g_return_val_if_fail(cookie_jar != NULL, NULL);
+
+ str = g_string_new("");
+
+ g_hash_table_iter_init(&it, cookie_jar->tab);
+ while (g_hash_table_iter_next(&it, (gpointer*)&key,
+ (gpointer*)&cookie))
+ {
+ if (cookie->expires != -1 && cookie->expires <= now)
+ continue;
+ g_string_append_printf(str, "%s=%s; ", key, cookie->value);
+ }
+
+ if (str->len > 0)
+ g_string_truncate(str, str->len - 2);
+ return g_string_free(str, FALSE);
+}
+
+void purple_http_cookie_jar_set(PurpleHttpCookieJar *cookie_jar,
+ const gchar *name, const gchar *value)
+{
+ purple_http_cookie_jar_set_ext(cookie_jar, name, value, -1);
+}
+
+static void purple_http_cookie_jar_set_ext(PurpleHttpCookieJar *cookie_jar,
+ const gchar *name, const gchar *value, time_t expires)
+{
+ g_return_if_fail(cookie_jar != NULL);
+ g_return_if_fail(name != NULL);
+
+ if (expires != -1 && time(NULL) >= expires)
+ value = NULL;
+
+ if (value != NULL) {
+ PurpleHttpCookie *cookie = purple_http_cookie_new(value);
+ cookie->expires = expires;
+ g_hash_table_insert(cookie_jar->tab, g_strdup(name), cookie);
+ } else
+ g_hash_table_remove(cookie_jar->tab, name);
+}
+
+const gchar * purple_http_cookie_jar_get(PurpleHttpCookieJar *cookie_jar,
+ const gchar *name)
+{
+ PurpleHttpCookie *cookie;
+
+ g_return_val_if_fail(cookie_jar != NULL, NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ cookie = g_hash_table_lookup(cookie_jar->tab, name);
+ if (!cookie)
+ return NULL;
+
+ return cookie->value;
+}
+
+gchar * purple_http_cookie_jar_dump(PurpleHttpCookieJar *cjar)
+{
+ GHashTableIter it;
+ gchar *key;
+ PurpleHttpCookie *cookie;
+ GString *str = g_string_new("");
+
+ g_hash_table_iter_init(&it, cjar->tab);
+ while (g_hash_table_iter_next(&it, (gpointer*)&key, (gpointer*)&cookie))
+ g_string_append_printf(str, "%s: %s (expires: %" G_GINT64_FORMAT
+ ")\n", key, cookie->value, (gint64)cookie->expires);
+
+ if (str->len > 0)
+ g_string_truncate(str, str->len - 1);
+ return g_string_free(str, FALSE);
+}
+
+gboolean purple_http_cookie_jar_is_empty(PurpleHttpCookieJar *cookie_jar)
+{
+ g_return_val_if_fail(cookie_jar != NULL, TRUE);
+
+ return g_hash_table_size(cookie_jar->tab) == 0;
+}
+
+/*** HTTP Keep-Alive pool API *************************************************/
+
+static void
+purple_http_keepalive_host_process_queue(PurpleHttpKeepaliveHost *host);
+
+static void
+purple_http_keepalive_host_free(gpointer _host)
+{
+ PurpleHttpKeepaliveHost *host = _host;
+
+ g_free(host->host);
+
+ g_slist_free_full(host->queue,
+ (GDestroyNotify)purple_http_keepalive_pool_request_cancel);
+ g_slist_free_full(host->sockets,
+ (GDestroyNotify)purple_http_socket_close_free);
+
+ if (host->process_queue_timeout > 0) {
+ purple_timeout_remove(host->process_queue_timeout);
+ host->process_queue_timeout = 0;
+ }
+
+
+ g_free(host);
+}
+
+PurpleHttpKeepalivePool *
+purple_http_keepalive_pool_new(void)
+{
+ PurpleHttpKeepalivePool *pool = g_new0(PurpleHttpKeepalivePool, 1);
+
+ pool->ref_count = 1;
+ pool->by_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+ purple_http_keepalive_host_free);
+
+ return pool;
+}
+
+static void
+purple_http_keepalive_pool_free(PurpleHttpKeepalivePool *pool)
+{
+ g_return_if_fail(pool != NULL);
+
+ if (pool->is_destroying)
+ return;
+ pool->is_destroying = TRUE;
+ g_hash_table_destroy(pool->by_hash);
+ g_free(pool);
+}
+
+void
+purple_http_keepalive_pool_ref(PurpleHttpKeepalivePool *pool)
+{
+ g_return_if_fail(pool != NULL);
+
+ pool->ref_count++;
+}
+
+PurpleHttpKeepalivePool *
+purple_http_keepalive_pool_unref(PurpleHttpKeepalivePool *pool)
+{
+ if (pool == NULL)
+ return NULL;
+
+ g_return_val_if_fail(pool->ref_count > 0, NULL);
+
+ pool->ref_count--;
+ if (pool->ref_count > 0)
+ return pool;
+
+ purple_http_keepalive_pool_free(pool);
+ return NULL;
+}
+
+static PurpleHttpKeepaliveRequest *
+purple_http_keepalive_pool_request(PurpleHttpKeepalivePool *pool,
+ PurpleConnection *gc, const gchar *host, int port, gboolean is_ssl,
+ PurpleSocketConnectCb cb, gpointer user_data)
+{
+ PurpleHttpKeepaliveRequest *req;
+ PurpleHttpKeepaliveHost *kahost;
+ gchar *hash;
+
+ g_return_val_if_fail(pool != NULL, NULL);
+ g_return_val_if_fail(host != NULL, NULL);
+
+ if (pool->is_destroying) {
+ purple_debug_error("http", "pool is destroying\n");
+ return NULL;
+ }
+
+ hash = purple_http_socket_hash(host, port, is_ssl);
+ kahost = g_hash_table_lookup(pool->by_hash, hash);
+
+ if (kahost == NULL) {
+ kahost = g_new0(PurpleHttpKeepaliveHost, 1);
+ kahost->pool = pool;
+ kahost->host = g_strdup(host);
+ kahost->port = port;
+ kahost->is_ssl = is_ssl;
+
+ g_hash_table_insert(pool->by_hash, g_strdup(hash), kahost);
+ }
+
+ g_free(hash);
+
+ req = g_new0(PurpleHttpKeepaliveRequest, 1);
+ req->gc = gc;
+ req->cb = cb;
+ req->user_data = user_data;
+ req->host = kahost;
+
+ kahost->queue = g_slist_append(kahost->queue, req);
+
+ purple_http_keepalive_host_process_queue(kahost);
+
+ return req;
+}
+
+static void
+_purple_http_keepalive_socket_connected(PurpleSocket *ps,
+ const gchar *error, gpointer _req)
+{
+ PurpleHttpSocket *hs = NULL;
+ PurpleHttpKeepaliveRequest *req = _req;
+
+ if (ps != NULL)
+ hs = purple_socket_get_data(ps, "hs");
+
+ if (hs != NULL)
+ hs->use_count++;
+
+ req->cb(ps, error, req->user_data);
+ g_free(req);
+}
+
+static gboolean
+_purple_http_keepalive_host_process_queue_cb(gpointer _host)
+{
+ PurpleHttpKeepaliveRequest *req;
+ PurpleHttpKeepaliveHost *host = _host;
+ PurpleHttpSocket *hs = NULL;
+ GSList *it;
+ guint sockets_count;
+
+ g_return_val_if_fail(host != NULL, FALSE);
+
+ host->process_queue_timeout = 0;
+
+ if (host->queue == NULL)
+ return FALSE;
+
+ sockets_count = 0;
+ it = host->sockets;
+ while (it != NULL) {
+ PurpleHttpSocket *hs_current = it->data;
+
+ sockets_count++;
+
+ if (!hs_current->is_busy) {
+ hs = hs_current;
+ break;
+ }
+
+ it = g_slist_next(it);
+ }
+
+ /* There are no free sockets and we cannot create another one. */
+ if (hs == NULL && sockets_count >= host->pool->limit_per_host &&
+ host->pool->limit_per_host > 0)
+ {
+ return FALSE;
+ }
+
+ req = host->queue->data;
+ host->queue = g_slist_remove(host->queue, req);
+
+ if (hs != NULL) {
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("http", "locking a (previously used) "
+ "socket: %p\n", hs);
+ }
+
+ hs->is_busy = TRUE;
+ hs->use_count++;
+
+ purple_http_keepalive_host_process_queue(host);
+
+ req->cb(hs->ps, NULL, req->user_data);
+ g_free(req);
+
+ return FALSE;
+ }
+
+ hs = purple_http_socket_connect_new(req->gc, req->host->host,
+ req->host->port, req->host->is_ssl,
+ _purple_http_keepalive_socket_connected, req);
+ if (hs == NULL) {
+ purple_debug_error("http", "failed creating new socket");
+ return FALSE;
+ }
+
+ req->hs = hs;
+ hs->is_busy = TRUE;
+ hs->host = host;
+
+ if (purple_debug_is_verbose())
+ purple_debug_misc("http", "locking a (new) socket: %p\n", hs);
+
+ host->sockets = g_slist_append(host->sockets, hs);
+
+ return FALSE;
+}
+
+static void
+purple_http_keepalive_host_process_queue(PurpleHttpKeepaliveHost *host)
+{
+ g_return_if_fail(host != NULL);
+
+ if (host->process_queue_timeout > 0)
+ return;
+
+ host->process_queue_timeout = purple_timeout_add(0,
+ _purple_http_keepalive_host_process_queue_cb, host);
+}
+
+static void
+purple_http_keepalive_pool_request_cancel(PurpleHttpKeepaliveRequest *req)
+{
+ if (req == NULL)
+ return;
+
+ if (req->host != NULL)
+ req->host->queue = g_slist_remove(req->host->queue, req);
+
+ if (req->hs != NULL) {
+ if (G_LIKELY(req->host)) {
+ req->host->sockets = g_slist_remove(req->host->sockets,
+ req->hs);
+ }
+ purple_http_socket_close_free(req->hs);
+ /* req should already be free'd here */
+ } else {
+ req->cb(NULL, _("Cancelled"), req->user_data);
+ g_free(req);
+ }
+}
+
+static void
+purple_http_keepalive_pool_release(PurpleHttpSocket *hs, gboolean invalidate)
+{
+ PurpleHttpKeepaliveHost *host;
+
+ if (hs == NULL)
+ return;
+
+ if (purple_debug_is_verbose())
+ purple_debug_misc("http", "releasing a socket: %p\n", hs);
+
+ purple_socket_watch(hs->ps, 0, NULL, NULL);
+ hs->is_busy = FALSE;
+ host = hs->host;
+
+ if (host == NULL) {
+ purple_http_socket_close_free(hs);
+ return;
+ }
+
+ if (invalidate) {
+ host->sockets = g_slist_remove(host->sockets, hs);
+ purple_http_socket_close_free(hs);
+ }
+
+ purple_http_keepalive_host_process_queue(host);
+}
+
+void
+purple_http_keepalive_pool_set_limit_per_host(PurpleHttpKeepalivePool *pool,
+ guint limit)
+{
+ g_return_if_fail(pool != NULL);
+
+ pool->limit_per_host = limit;
+}
+
+guint
+purple_http_keepalive_pool_get_limit_per_host(PurpleHttpKeepalivePool *pool)
+{
+ g_return_val_if_fail(pool != NULL, 0);
+
+ return pool->limit_per_host;
+}
+
+/*** HTTP connection set API **************************************************/
+
+PurpleHttpConnectionSet *
+purple_http_connection_set_new(void)
+{
+ PurpleHttpConnectionSet *set;
+
+ set = g_new0(PurpleHttpConnectionSet, 1);
+ set->connections = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+ return set;
+}
+
+void
+purple_http_connection_set_destroy(PurpleHttpConnectionSet *set)
+{
+ if (set == NULL)
+ return;
+
+ set->is_destroying = TRUE;
+
+ while (TRUE) {
+ GHashTableIter iter;
+ PurpleHttpConnection *http_conn;
+
+ g_hash_table_iter_init(&iter, set->connections);
+ if (!g_hash_table_iter_next(&iter, (gpointer*)&http_conn, NULL))
+ break;
+
+ purple_http_conn_cancel(http_conn);
+ }
+
+ g_hash_table_destroy(set->connections);
+ g_free(set);
+}
+
+void
+purple_http_connection_set_add(PurpleHttpConnectionSet *set,
+ PurpleHttpConnection *http_conn)
+{
+ if (set->is_destroying)
+ return;
+ if (http_conn->connection_set == set)
+ return;
+ if (http_conn->connection_set != NULL) {
+ purple_http_connection_set_remove(http_conn->connection_set,
+ http_conn);
+ }
+ g_hash_table_insert(set->connections, http_conn, (gpointer)TRUE);
+ http_conn->connection_set = set;
+}
+
+static void
+purple_http_connection_set_remove(PurpleHttpConnectionSet *set,
+ PurpleHttpConnection *http_conn)
+{
+ g_hash_table_remove(set->connections, http_conn);
+ if (http_conn->connection_set == set)
+ http_conn->connection_set = NULL;
+}
+
+/*** Request API **************************************************************/
+
+static void purple_http_request_free(PurpleHttpRequest *request);
+
+PurpleHttpRequest * purple_http_request_new(const gchar *url)
+{
+ PurpleHttpRequest *request;
+
+ request = g_new0(PurpleHttpRequest, 1);
+
+ request->ref_count = 1;
+ request->url = g_strdup(url);
+ request->headers = purple_http_headers_new();
+ request->cookie_jar = purple_http_cookie_jar_new();
+ request->keepalive_pool = purple_http_keepalive_pool_new();
+
+ request->timeout = PURPLE_HTTP_REQUEST_DEFAULT_TIMEOUT;
+ request->max_redirects = PURPLE_HTTP_REQUEST_DEFAULT_MAX_REDIRECTS;
+ request->http11 = TRUE;
+ request->max_length = PURPLE_HTTP_REQUEST_DEFAULT_MAX_LENGTH;
+
+ return request;
+}
+
+static void purple_http_request_free(PurpleHttpRequest *request)
+{
+ purple_http_headers_free(request->headers);
+ purple_http_cookie_jar_unref(request->cookie_jar);
+ purple_http_keepalive_pool_unref(request->keepalive_pool);
+ g_free(request->url);
+ g_free(request);
+}
+
+void purple_http_request_ref(PurpleHttpRequest *request)
+{
+ g_return_if_fail(request != NULL);
+
+ request->ref_count++;
+}
+
+PurpleHttpRequest * purple_http_request_unref(PurpleHttpRequest *request)
+{
+ if (request == NULL)
+ return NULL;
+
+ g_return_val_if_fail(request->ref_count > 0, NULL);
+
+ request->ref_count--;
+ if (request->ref_count > 0)
+ return request;
+
+ purple_http_request_free(request);
+ return NULL;
+}
+
+void purple_http_request_set_url(PurpleHttpRequest *request, const gchar *url)
+{
+ g_return_if_fail(request != NULL);
+ g_return_if_fail(url != NULL);
+
+ g_free(request->url);
+ request->url = g_strdup(url);
+}
+
+void purple_http_request_set_url_printf(PurpleHttpRequest *request,
+ const gchar *format, ...)
+{
+ va_list args;
+ gchar *value;
+
+ g_return_if_fail(request != NULL);
+ g_return_if_fail(format != NULL);
+
+ va_start(args, format);
+ value = g_strdup_vprintf(format, args);
+ va_end(args);
+
+ purple_http_request_set_url(request, value);
+ g_free(value);
+}
+
+const gchar * purple_http_request_get_url(PurpleHttpRequest *request)
+{
+ g_return_val_if_fail(request != NULL, NULL);
+
+ return request->url;
+}
+
+void purple_http_request_set_method(PurpleHttpRequest *request, const gchar *method)
+{
+ g_return_if_fail(request != NULL);
+
+ g_free(request->method);
+ request->method = g_strdup(method);
+}
+
+const gchar * purple_http_request_get_method(PurpleHttpRequest *request)
+{
+ g_return_val_if_fail(request != NULL, NULL);
+
+ return request->method;
+}
+
+static gboolean purple_http_request_is_method(PurpleHttpRequest *request,
+ const gchar *method)
+{
+ const gchar *rmethod;
+
+ g_return_val_if_fail(request != NULL, FALSE);
+ g_return_val_if_fail(method != NULL, FALSE);
+
+ rmethod = purple_http_request_get_method(request);
+ if (rmethod == NULL)
+ return (g_ascii_strcasecmp(method, "get") == 0);
+ return (g_ascii_strcasecmp(method, rmethod) == 0);
+}
+
+void
+purple_http_request_set_keepalive_pool(PurpleHttpRequest *request,
+ PurpleHttpKeepalivePool *pool)
+{
+ g_return_if_fail(request != NULL);
+
+ if (pool != NULL)
+ purple_http_keepalive_pool_ref(pool);
+
+ if (request->keepalive_pool != NULL) {
+ purple_http_keepalive_pool_unref(request->keepalive_pool);
+ request->keepalive_pool = NULL;
+ }
+
+ if (pool != NULL)
+ request->keepalive_pool = pool;
+}
+
+PurpleHttpKeepalivePool *
+purple_http_request_get_keepalive_pool(PurpleHttpRequest *request)
+{
+ g_return_val_if_fail(request != NULL, FALSE);
+
+ return request->keepalive_pool;
+}
+
+void purple_http_request_set_contents(PurpleHttpRequest *request,
+ const gchar *contents, int length)
+{
+ g_return_if_fail(request != NULL);
+ g_return_if_fail(length >= -1);
+
+ request->contents_reader = NULL;
+ request->contents_reader_data = NULL;
+
+ g_free(request->contents);
+ if (contents == NULL || length == 0) {
+ request->contents = NULL;
+ request->contents_length = 0;
+ return;
+ }
+
+ if (length == -1)
+ length = strlen(contents);
+ request->contents = g_memdup(contents, length);
+ request->contents_length = length;
+}
+
+void purple_http_request_set_contents_reader(PurpleHttpRequest *request,
+ PurpleHttpContentReader reader, int contents_length, gpointer user_data)
+{
+ g_return_if_fail(request != NULL);
+ g_return_if_fail(reader != NULL);
+ g_return_if_fail(contents_length >= -1);
+
+ g_free(request->contents);
+ request->contents = NULL;
+ request->contents_length = contents_length;
+ request->contents_reader = reader;
+ request->contents_reader_data = user_data;
+}
+
+void purple_http_request_set_response_writer(PurpleHttpRequest *request,
+ PurpleHttpContentWriter writer, gpointer user_data)
+{
+ g_return_if_fail(request != NULL);
+
+ if (writer == NULL)
+ user_data = NULL;
+ request->response_writer = writer;
+ request->response_writer_data = user_data;
+}
+
+void purple_http_request_set_timeout(PurpleHttpRequest *request, int timeout)
+{
+ g_return_if_fail(request != NULL);
+
+ if (timeout < -1)
+ timeout = -1;
+
+ request->timeout = timeout;
+}
+
+int purple_http_request_get_timeout(PurpleHttpRequest *request)
+{
+ g_return_val_if_fail(request != NULL, -1);
+
+ return request->timeout;
+}
+
+void purple_http_request_set_max_redirects(PurpleHttpRequest *request,
+ int max_redirects)
+{
+ g_return_if_fail(request != NULL);
+
+ if (max_redirects < -1)
+ max_redirects = -1;
+
+ request->max_redirects = max_redirects;
+}
+
+int purple_http_request_get_max_redirects(PurpleHttpRequest *request)
+{
+ g_return_val_if_fail(request != NULL, -1);
+
+ return request->max_redirects;
+}
+
+void purple_http_request_set_cookie_jar(PurpleHttpRequest *request,
+ PurpleHttpCookieJar *cookie_jar)
+{
+ g_return_if_fail(request != NULL);
+ g_return_if_fail(cookie_jar != NULL);
+
+ purple_http_cookie_jar_ref(cookie_jar);
+ purple_http_cookie_jar_unref(request->cookie_jar);
+ request->cookie_jar = cookie_jar;
+}
+
+PurpleHttpCookieJar * purple_http_request_get_cookie_jar(
+ PurpleHttpRequest *request)
+{
+ g_return_val_if_fail(request != NULL, NULL);
+
+ return request->cookie_jar;
+}
+
+void purple_http_request_set_http11(PurpleHttpRequest *request, gboolean http11)
+{
+ g_return_if_fail(request != NULL);
+
+ request->http11 = http11;
+}
+
+gboolean purple_http_request_is_http11(PurpleHttpRequest *request)
+{
+ g_return_val_if_fail(request != NULL, FALSE);
+
+ return request->http11;
+}
+
+void purple_http_request_set_max_len(PurpleHttpRequest *request, int max_len)
+{
+ g_return_if_fail(request != NULL);
+
+ if (max_len < 0 || max_len > PURPLE_HTTP_REQUEST_HARD_MAX_LENGTH)
+ max_len = PURPLE_HTTP_REQUEST_HARD_MAX_LENGTH;
+
+ request->max_length = max_len;
+}
+
+int purple_http_request_get_max_len(PurpleHttpRequest *request)
+{
+ g_return_val_if_fail(request != NULL, -1);
+
+ return request->max_length;
+}
+
+void purple_http_request_header_set(PurpleHttpRequest *request,
+ const gchar *key, const gchar *value)
+{
+ g_return_if_fail(request != NULL);
+ g_return_if_fail(key != NULL);
+
+ purple_http_headers_remove(request->headers, key);
+ if (value)
+ purple_http_headers_add(request->headers, key, value);
+}
+
+void purple_http_request_header_set_printf(PurpleHttpRequest *request,
+ const gchar *key, const gchar *format, ...)
+{
+ va_list args;
+ gchar *value;
+
+ g_return_if_fail(request != NULL);
+ g_return_if_fail(key != NULL);
+ g_return_if_fail(format != NULL);
+
+ va_start(args, format);
+ value = g_strdup_vprintf(format, args);
+ va_end(args);
+
+ purple_http_request_header_set(request, key, value);
+ g_free(value);
+}
+
+void purple_http_request_header_add(PurpleHttpRequest *request,
+ const gchar *key, const gchar *value)
+{
+ g_return_if_fail(request != NULL);
+ g_return_if_fail(key != NULL);
+
+ purple_http_headers_add(request->headers, key, value);
+}
+
+/*** HTTP response API ********************************************************/
+
+static PurpleHttpResponse * purple_http_response_new(void)
+{
+ PurpleHttpResponse *response = g_new0(PurpleHttpResponse, 1);
+
+ return response;
+}
+
+static void purple_http_response_free(PurpleHttpResponse *response)
+{
+ if (response->contents != NULL)
+ g_string_free(response->contents, TRUE);
+ g_free(response->error);
+ purple_http_headers_free(response->headers);
+ g_free(response);
+}
+
+gboolean purple_http_response_is_successful(PurpleHttpResponse *response)
+{
+ int code;
+
+ g_return_val_if_fail(response != NULL, FALSE);
+
+ code = response->code;
+
+ if (code <= 0)
+ return FALSE;
+
+ /* TODO: HTTP/1.1 100 Continue */
+
+ if (code / 100 == 2)
+ return TRUE;
+
+ return FALSE;
+}
+
+int purple_http_response_get_code(PurpleHttpResponse *response)
+{
+ g_return_val_if_fail(response != NULL, 0);
+
+ return response->code;
+}
+
+const gchar * purple_http_response_get_error(PurpleHttpResponse *response)
+{
+ g_return_val_if_fail(response != NULL, NULL);
+
+ if (response->error != NULL)
+ return response->error;
+
+ if (!purple_http_response_is_successful(response)) {
+ static gchar errmsg[200];
+ if (response->code <= 0) {
+ g_snprintf(errmsg, sizeof(errmsg),
+ _("Unknown HTTP error"));
+ } else {
+ g_snprintf(errmsg, sizeof(errmsg),
+ _("Invalid HTTP response code (%d)"),
+ response->code);
+ }
+ return errmsg;
+ }
+
+ return NULL;
+}
+
+gsize purple_http_response_get_data_len(PurpleHttpResponse *response)
+{
+ g_return_val_if_fail(response != NULL, 0);
+
+ if (response->contents == NULL)
+ return 0;
+
+ return response->contents->len;
+}
+
+const gchar * purple_http_response_get_data(PurpleHttpResponse *response, size_t *len)
+{
+ const gchar *ret = "";
+
+ g_return_val_if_fail(response != NULL, "");
+
+ if (response->contents != NULL) {
+ ret = response->contents->str;
+ if (len)
+ *len = response->contents->len;
+ } else {
+ if (len)
+ *len = 0;
+ }
+
+ return ret;
+}
+
+const GList * purple_http_response_get_all_headers(PurpleHttpResponse *response)
+{
+ g_return_val_if_fail(response != NULL, NULL);
+
+ return purple_http_headers_get_all(response->headers);
+}
+
+const GList * purple_http_response_get_headers_by_name(
+ PurpleHttpResponse *response, const gchar *name)
+{
+ g_return_val_if_fail(response != NULL, NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ return purple_http_headers_get_all_by_name(response->headers, name);
+}
+
+const gchar * purple_http_response_get_header(PurpleHttpResponse *response,
+ const gchar *name)
+{
+ g_return_val_if_fail(response != NULL, NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ return purple_http_headers_get(response->headers, name);
+}
+
+/*** URL functions ************************************************************/
+
+PurpleHttpURL *
+purple_http_url_parse(const char *raw_url)
+{
+ PurpleHttpURL *url;
+ GMatchInfo *match_info;
+
+ gchar *host_full, *tmp;
+
+ g_return_val_if_fail(raw_url != NULL, NULL);
+
+ url = g_new0(PurpleHttpURL, 1);
+
+ if (!g_regex_match(purple_http_re_url, raw_url, 0, &match_info)) {
+ if (purple_debug_is_verbose() && purple_debug_is_unsafe()) {
+ purple_debug_warning("http",
+ "Invalid URL provided: %s\n",
+ raw_url);
+ }
+ return NULL;
+ }
+
+ url->protocol = g_match_info_fetch(match_info, 1);
+ host_full = g_match_info_fetch(match_info, 2);
+ url->path = g_match_info_fetch(match_info, 3);
+ url->fragment = g_match_info_fetch(match_info, 4);
+ g_match_info_free(match_info);
+
+ if (g_strcmp0(url->protocol, "") == 0) {
+ g_free(url->protocol);
+ url->protocol = NULL;
+ } else if (url->protocol != NULL) {
+ tmp = url->protocol;
+ url->protocol = g_ascii_strdown(url->protocol, -1);
+ g_free(tmp);
+ }
+ if (host_full[0] == '\0') {
+ g_free(host_full);
+ host_full = NULL;
+ }
+ if (url->path[0] == '\0') {
+ g_free(url->path);
+ url->path = NULL;
+ }
+ if ((url->protocol == NULL) != (host_full == NULL))
+ purple_debug_warning("http", "Protocol or host not present "
+ "(unlikely case)\n");
+
+ if (host_full) {
+ gchar *port_str;
+
+ if (!g_regex_match(purple_http_re_url_host, host_full, 0,
+ &match_info))
+ {
+ if (purple_debug_is_verbose() &&
+ purple_debug_is_unsafe())
+ {
+ purple_debug_warning("http",
+ "Invalid host provided for URL: %s\n",
+ raw_url);
+ }
+
+ g_free(host_full);
+ purple_http_url_free(url);
+ return NULL;
+ }
+
+ url->username = g_match_info_fetch(match_info, 1);
+ url->password = g_match_info_fetch(match_info, 2);
+ url->host = g_match_info_fetch(match_info, 3);
+ port_str = g_match_info_fetch(match_info, 4);
+
+ if (port_str && port_str[0])
+ url->port = atoi(port_str);
+
+ if (url->username[0] == '\0') {
+ g_free(url->username);
+ url->username = NULL;
+ }
+ if (url->password[0] == '\0') {
+ g_free(url->password);
+ url->password = NULL;
+ }
+ if (g_strcmp0(url->host, "") == 0) {
+ g_free(url->host);
+ url->host = NULL;
+ } else if (url->host != NULL) {
+ tmp = url->host;
+ url->host = g_ascii_strdown(url->host, -1);
+ g_free(tmp);
+ }
+
+ g_free(port_str);
+ g_match_info_free(match_info);
+
+ g_free(host_full);
+ host_full = NULL;
+ }
+
+ if (url->host != NULL) {
+ if (url->protocol == NULL)
+ url->protocol = g_strdup("http");
+ if (url->port == 0 && 0 == strcmp(url->protocol, "http"))
+ url->port = 80;
+ if (url->port == 0 && 0 == strcmp(url->protocol, "https"))
+ url->port = 443;
+ if (url->path == NULL)
+ url->path = g_strdup("/");
+ if (url->path[0] != '/')
+ purple_debug_warning("http",
+ "URL path doesn't start with slash\n");
+ }
+
+ return url;
+}
+
+void
+purple_http_url_free(PurpleHttpURL *parsed_url)
+{
+ if (parsed_url == NULL)
+ return;
+
+ g_free(parsed_url->protocol);
+ g_free(parsed_url->username);
+ g_free(parsed_url->password);
+ g_free(parsed_url->host);
+ g_free(parsed_url->path);
+ g_free(parsed_url->fragment);
+ g_free(parsed_url);
+}
+
+void
+purple_http_url_relative(PurpleHttpURL *base_url, PurpleHttpURL *relative_url)
+{
+ g_return_if_fail(base_url != NULL);
+ g_return_if_fail(relative_url != NULL);
+
+ if (relative_url->host) {
+ g_free(base_url->protocol);
+ base_url->protocol = g_strdup(relative_url->protocol);
+ g_free(base_url->username);
+ base_url->username = g_strdup(relative_url->username);
+ g_free(base_url->password);
+ base_url->password = g_strdup(relative_url->password);
+ g_free(base_url->host);
+ base_url->host = g_strdup(relative_url->host);
+ base_url->port = relative_url->port;
+
+ g_free(base_url->path);
+ base_url->path = NULL;
+ }
+
+ if (relative_url->path) {
+ if (relative_url->path[0] == '/' ||
+ base_url->path == NULL)
+ {
+ g_free(base_url->path);
+ base_url->path = g_strdup(relative_url->path);
+ } else {
+ gchar *last_slash = strrchr(base_url->path, '/');
+ gchar *tmp;
+ if (last_slash == NULL)
+ base_url->path[0] = '\0';
+ else
+ last_slash[1] = '\0';
+ tmp = base_url->path;
+ base_url->path = g_strconcat(base_url->path,
+ relative_url->path, NULL);
+ g_free(tmp);
+ }
+ }
+
+ g_free(base_url->fragment);
+ base_url->fragment = g_strdup(relative_url->fragment);
+}
+
+gchar *
+purple_http_url_print(PurpleHttpURL *parsed_url)
+{
+ GString *url = g_string_new("");
+ gboolean before_host_printed = FALSE, host_printed = FALSE;
+ gboolean port_is_default = FALSE;
+
+ if (parsed_url->protocol) {
+ g_string_append_printf(url, "%s://", parsed_url->protocol);
+ before_host_printed = TRUE;
+ if (parsed_url->port == 80 && 0 == strcmp(parsed_url->protocol,
+ "http"))
+ port_is_default = TRUE;
+ if (parsed_url->port == 443 && 0 == strcmp(parsed_url->protocol,
+ "https"))
+ port_is_default = TRUE;
+ }
+ if (parsed_url->username || parsed_url->password) {
+ if (parsed_url->username)
+ g_string_append(url, parsed_url->username);
+ g_string_append_printf(url, ":%s", parsed_url->password);
+ g_string_append(url, "@");
+ before_host_printed = TRUE;
+ }
+ if (parsed_url->host || parsed_url->port) {
+ if (!parsed_url->host)
+ g_string_append_printf(url, "{???}:%d",
+ parsed_url->port);
+ else {
+ g_string_append(url, parsed_url->host);
+ if (!port_is_default)
+ g_string_append_printf(url, ":%d",
+ parsed_url->port);
+ }
+ host_printed = TRUE;
+ }
+ if (parsed_url->path) {
+ if (!host_printed && before_host_printed)
+ g_string_append(url, "{???}");
+ g_string_append(url, parsed_url->path);
+ }
+ if (parsed_url->fragment)
+ g_string_append_printf(url, "#%s", parsed_url->fragment);
+
+ return g_string_free(url, FALSE);
+}
+
+const gchar *
+purple_http_url_get_protocol(const PurpleHttpURL *parsed_url)
+{
+ g_return_val_if_fail(parsed_url != NULL, NULL);
+
+ return parsed_url->protocol;
+}
+
+const gchar *
+purple_http_url_get_username(const PurpleHttpURL *parsed_url)
+{
+ g_return_val_if_fail(parsed_url != NULL, NULL);
+
+ return parsed_url->username;
+}
+
+const gchar *
+purple_http_url_get_password(const PurpleHttpURL *parsed_url)
+{
+ g_return_val_if_fail(parsed_url != NULL, NULL);
+
+ return parsed_url->password;
+}
+
+const gchar *
+purple_http_url_get_host(const PurpleHttpURL *parsed_url)
+{
+ g_return_val_if_fail(parsed_url != NULL, NULL);
+
+ return parsed_url->host;
+}
+
+int
+purple_http_url_get_port(const PurpleHttpURL *parsed_url)
+{
+ g_return_val_if_fail(parsed_url != NULL, 0);
+
+ return parsed_url->port;
+}
+
+const gchar *
+purple_http_url_get_path(const PurpleHttpURL *parsed_url)
+{
+ g_return_val_if_fail(parsed_url != NULL, NULL);
+
+ return parsed_url->path;
+}
+
+const gchar *
+purple_http_url_get_fragment(const PurpleHttpURL *parsed_url)
+{
+ g_return_val_if_fail(parsed_url != NULL, NULL);
+
+ return parsed_url->fragment;
+}
+
+/*** HTTP Subsystem ***********************************************************/
+
+void purple_http_init(void)
+{
+ purple_http_re_url = g_regex_new("^"
+
+ "(?:" /* host part beginning */
+ "([a-z]+)\\:/*" /* protocol */
+ "([^/]+)" /* username, password, host, port */
+ ")?" /* host part ending */
+
+ "([^#]*)" /* path */
+
+ "(?:#" "(.*)" ")?" /* fragment */
+
+ "$", G_REGEX_OPTIMIZE | G_REGEX_CASELESS,
+ G_REGEX_MATCH_NOTEMPTY, NULL);
+
+ purple_http_re_url_host = g_regex_new("^"
+
+ "(?:" /* user credentials part beginning */
+ "([" PURPLE_HTTP_URL_CREDENTIALS_CHARS "]+)" /* username */
+ "(?::([" PURPLE_HTTP_URL_CREDENTIALS_CHARS "]+))" /* password */
+ "@)?" /* user credentials part ending */
+
+ "([a-z0-9.-]+)" /* host */
+ "(?::([0-9]+))?" /* port*/
+
+ "$", G_REGEX_OPTIMIZE | G_REGEX_CASELESS,
+ G_REGEX_MATCH_NOTEMPTY, NULL);
+
+ purple_http_re_rfc1123 = g_regex_new(
+ "^[a-z]+, " /* weekday */
+ "([0-9]+) " /* date */
+ "([a-z]+) " /* month */
+ "([0-9]+) " /* year */
+ "([0-9]+:[0-9]+:[0-9]+) " /* time */
+ "(?:GMT|UTC)$",
+ G_REGEX_OPTIMIZE | G_REGEX_CASELESS,
+ G_REGEX_MATCH_NOTEMPTY, NULL);
+
+ purple_http_hc_list = NULL;
+ purple_http_hc_by_ptr = g_hash_table_new(g_direct_hash, g_direct_equal);
+ purple_http_hc_by_gc = g_hash_table_new_full(g_direct_hash,
+ g_direct_equal, NULL, (GDestroyNotify)g_list_free);
+ purple_http_cancelling_gc = g_hash_table_new(g_direct_hash, g_direct_equal);
+}
+
+static void purple_http_foreach_conn_cancel(gpointer _hc, gpointer user_data)
+{
+ PurpleHttpConnection *hc = _hc;
+ purple_http_conn_cancel(hc);
+}
+
+void purple_http_uninit(void)
+{
+ g_regex_unref(purple_http_re_url);
+ purple_http_re_url = NULL;
+ g_regex_unref(purple_http_re_url_host);
+ purple_http_re_url_host = NULL;
+ g_regex_unref(purple_http_re_rfc1123);
+ purple_http_re_rfc1123 = NULL;
+
+ g_list_foreach(purple_http_hc_list, purple_http_foreach_conn_cancel,
+ NULL);
+
+ if (purple_http_hc_list != NULL ||
+ 0 != g_hash_table_size(purple_http_hc_by_ptr) ||
+ 0 != g_hash_table_size(purple_http_hc_by_gc))
+ purple_debug_warning("http",
+ "Couldn't cleanup all connections.\n");
+
+ g_list_free(purple_http_hc_list);
+ purple_http_hc_list = NULL;
+ g_hash_table_destroy(purple_http_hc_by_gc);
+ purple_http_hc_by_gc = NULL;
+ g_hash_table_destroy(purple_http_hc_by_ptr);
+ purple_http_hc_by_ptr = NULL;
+ g_hash_table_destroy(purple_http_cancelling_gc);
+ purple_http_cancelling_gc = NULL;
+}
diff --git a/libpurple/http.h b/libpurple/http.h
new file mode 100644
index 0000000000..a05bc38a54
--- /dev/null
+++ b/libpurple/http.h
@@ -0,0 +1,959 @@
+/* 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 _PURPLE_HTTP_H_
+#define _PURPLE_HTTP_H_
+/**
+ * SECTION:http
+ * @section_id: libpurple-http
+ * @short_description: <filename>http.h</filename>
+ * @title: HTTP API
+ */
+
+#include <glib.h>
+
+#include "connection.h"
+
+/**
+ * PurpleHttpRequest:
+ *
+ * A structure containing all data required to generate a single HTTP request.
+ */
+typedef struct _PurpleHttpRequest PurpleHttpRequest;
+
+/**
+ * PurpleHttpConnection:
+ *
+ * A representation of actually running HTTP request. Can be used to cancel the
+ * request.
+ */
+typedef struct _PurpleHttpConnection PurpleHttpConnection;
+
+/**
+ * PurpleHttpResponse:
+ *
+ * All information got with response for HTTP request.
+ */
+typedef struct _PurpleHttpResponse PurpleHttpResponse;
+
+/**
+ * PurpleHttpURL:
+ *
+ * Parsed representation for the URL.
+ */
+typedef struct _PurpleHttpURL PurpleHttpURL;
+
+/**
+ * PurpleHttpCookieJar:
+ *
+ * An collection of cookies, got from HTTP response or provided for HTTP
+ * request.
+ */
+typedef struct _PurpleHttpCookieJar PurpleHttpCookieJar;
+
+/**
+ * PurpleHttpKeepalivePool:
+ *
+ * A pool of TCP connections for HTTP Keep-Alive session.
+ */
+typedef struct _PurpleHttpKeepalivePool PurpleHttpKeepalivePool;
+
+/**
+ * PurpleHttpConnectionSet:
+ *
+ * A set of running HTTP requests. Can be used to cancel all of them at once.
+ */
+typedef struct _PurpleHttpConnectionSet PurpleHttpConnectionSet;
+
+/**
+ * PurpleHttpCallback:
+ *
+ * An callback called after performing (successfully or not) HTTP request.
+ */
+typedef void (*PurpleHttpCallback)(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer user_data);
+
+/**
+ * PurpleHttpContentReaderCb:
+ *
+ * An callback called after storing data requested by PurpleHttpContentReader.
+ */
+typedef void (*PurpleHttpContentReaderCb)(PurpleHttpConnection *http_conn,
+ gboolean success, gboolean eof, size_t stored);
+
+/**
+ * PurpleHttpContentReader:
+ * @http_conn: Connection, which requests data.
+ * @buffer: Buffer to store data to (with offset ignored).
+ * @offset: Position, from where to read data.
+ * @length: Length of data to read.
+ * @user_data: The user data passed with callback function.
+ * @cb: The function to call after storing data to buffer.
+ *
+ * An callback for getting large request contents (ie. from file stored on
+ * disk).
+ */
+typedef void (*PurpleHttpContentReader)(PurpleHttpConnection *http_conn,
+ gchar *buffer, size_t offset, size_t length, gpointer user_data,
+ PurpleHttpContentReaderCb cb);
+
+/**
+ * PurpleHttpContentWriter:
+ * @http_conn: Connection, which requests data.
+ * @response: Response at point got so far (may change later).
+ * @buffer: Buffer to read data from (with offset ignored).
+ * @offset: Position of data got (its value is offset + length of
+ * previous call), can be safely ignored.
+ * @length: Length of data read.
+ * @user_data: The user data passed with callback function.
+ *
+ * An callback for writting large response contents.
+ *
+ * Returns: TRUE, if succeeded, FALSE otherwise.
+ */
+typedef gboolean (*PurpleHttpContentWriter)(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, const gchar *buffer, size_t offset,
+ size_t length, gpointer user_data);
+
+/**
+ * PurpleHttpProgressWatcher:
+ * @http_conn: The HTTP Connection.
+ * @reading_state: FALSE, is we are sending the request, TRUE, when reading
+ * the response.
+ * @processed: The amount of data already processed.
+ * @total: Total amount of data (in current state).
+ * @user_data: The user data passed with callback function.
+ *
+ * An callback for watching HTTP connection progress.
+ */
+typedef void (*PurpleHttpProgressWatcher)(PurpleHttpConnection *http_conn,
+ gboolean reading_state, int processed, int total, gpointer user_data);
+
+G_BEGIN_DECLS
+
+/**************************************************************************/
+/* Performing HTTP requests */
+/**************************************************************************/
+
+/**
+ * purple_http_get:
+ * @gc: The connection for which the request is needed, or NULL.
+ * @callback: (scope call): The callback function.
+ * @user_data: The user data to pass to the callback function.
+ * @url: The URL.
+ *
+ * Fetches the data from a URL with GET request, and passes it to a callback
+ * function.
+ *
+ * Returns: The HTTP connection struct.
+ */
+PurpleHttpConnection * purple_http_get(PurpleConnection *gc,
+ PurpleHttpCallback callback, gpointer user_data, const gchar *url);
+
+/**
+ * purple_http_get_printf:
+ * @gc: The connection for which the request is needed, or NULL.
+ * @callback: (scope call): The callback function.
+ * @user_data: The user data to pass to the callback function.
+ * @format: The format string.
+ *
+ * Constructs an URL and fetches the data from it with GET request, then passes
+ * it to a callback function.
+ *
+ * Returns: The HTTP connection struct.
+ */
+PurpleHttpConnection * purple_http_get_printf(PurpleConnection *gc,
+ PurpleHttpCallback callback, gpointer user_data,
+ const gchar *format, ...) G_GNUC_PRINTF(4, 5);
+
+/**
+ * purple_http_request:
+ * @gc: The connection for which the request is needed, or NULL.
+ * @request: The request.
+ * @callback: (scope call): The callback function.
+ * @user_data: The user data to pass to the callback function.
+ *
+ * Fetches a HTTP request and passes the response to a callback function.
+ * Provided request struct can be shared by multiple http requests but can not
+ * be modified when any of these is running.
+ *
+ * Returns: The HTTP connection struct.
+ */
+PurpleHttpConnection * purple_http_request(PurpleConnection *gc,
+ PurpleHttpRequest *request, PurpleHttpCallback callback,
+ gpointer user_data);
+
+/**************************************************************************/
+/* HTTP connection API */
+/**************************************************************************/
+
+/**
+ * purple_http_conn_cancel:
+ * @http_conn: The data returned when you initiated the HTTP request.
+ *
+ * Cancel a pending HTTP request.
+ */
+void purple_http_conn_cancel(PurpleHttpConnection *http_conn);
+
+/**
+ * purple_http_conn_cancel_all:
+ * @gc: The handle.
+ *
+ * Cancels all HTTP connections associated with the specified handle.
+ */
+void purple_http_conn_cancel_all(PurpleConnection *gc);
+
+/**
+ * purple_http_conn_is_running:
+ * @http_conn: The HTTP connection (may be invalid pointer).
+ *
+ * Checks, if provided HTTP request is running.
+ *
+ * Returns: TRUE, if provided connection is currently running.
+ */
+gboolean purple_http_conn_is_running(PurpleHttpConnection *http_conn);
+
+/**
+ * purple_http_conn_get_request:
+ * @http_conn: The HTTP connection.
+ *
+ * Gets PurpleHttpRequest used for specified HTTP connection.
+ *
+ * Returns: The PurpleHttpRequest object.
+ */
+PurpleHttpRequest * purple_http_conn_get_request(
+ PurpleHttpConnection *http_conn);
+
+/**
+ * purple_http_conn_get_cookie_jar:
+ * @http_conn: The HTTP connection.
+ *
+ * Gets cookie jar used within connection.
+ *
+ * Returns: The cookie jar.
+ */
+PurpleHttpCookieJar * purple_http_conn_get_cookie_jar(
+ PurpleHttpConnection *http_conn);
+
+/**
+ * purple_http_conn_get_purple_connection:
+ * @http_conn: The HTTP connection.
+ *
+ * Gets PurpleConnection tied with specified HTTP connection.
+ *
+ * Returns: The PurpleConnection object.
+ */
+PurpleConnection * purple_http_conn_get_purple_connection(
+ PurpleHttpConnection *http_conn);
+
+/**
+ * purple_http_conn_set_progress_watcher:
+ * @http_conn: The HTTP connection.
+ * @watcher: (scope call): The watcher.
+ * @user_data: The user data to pass to the callback function.
+ * @interval_threshold: Minimum interval (in microseconds) of calls to
+ * watcher, or -1 for default.
+ *
+ * Sets the watcher, called after writing or reading data to/from HTTP stream.
+ * May be used for updating transfer progress gauge.
+ */
+void purple_http_conn_set_progress_watcher(PurpleHttpConnection *http_conn,
+ PurpleHttpProgressWatcher watcher, gpointer user_data,
+ gint interval_threshold);
+
+
+/**************************************************************************/
+/* URL processing API */
+/**************************************************************************/
+
+/**
+ * purple_http_url_parse:
+ * @url: The URL to parse.
+ *
+ * Parses a URL.
+ *
+ * The returned data must be freed with purple_http_url_free.
+ *
+ * Returns: The parsed url or NULL, if the URL is invalid.
+ */
+PurpleHttpURL *
+purple_http_url_parse(const char *url);
+
+/**
+ * purple_http_url_free:
+ * @parsed_url: The parsed URL struct, or NULL.
+ *
+ * Frees the parsed URL struct.
+ */
+void
+purple_http_url_free(PurpleHttpURL *parsed_url);
+
+/**
+ * purple_http_url_relative:
+ * @base_url: The base URL. The result is stored here.
+ * @relative_url: The relative URL.
+ *
+ * Converts the base URL to the absolute form of the provided relative URL.
+ *
+ * Example: "https://example.com/path/to/file.html" + "subdir/other-file.html" =
+ * "https://example.com/path/to/subdir/another-file.html"
+ */
+void
+purple_http_url_relative(PurpleHttpURL *base_url, PurpleHttpURL *relative_url);
+
+/**
+ * purple_http_url_print:
+ * @parsed_url: The URL struct.
+ *
+ * Converts the URL struct to the printable form. The result may not be a valid
+ * URL (in cases, when the struct doesn't have all fields filled properly).
+ *
+ * The result must be g_free'd.
+ *
+ * Returns: The printable form of the URL.
+ */
+gchar *
+purple_http_url_print(PurpleHttpURL *parsed_url);
+
+/**
+ * purple_http_url_get_protocol:
+ * @parsed_url: The URL struct.
+ *
+ * Gets the protocol part of URL.
+ *
+ * Returns: The protocol.
+ */
+const gchar *
+purple_http_url_get_protocol(const PurpleHttpURL *parsed_url);
+
+/**
+ * purple_http_url_get_username:
+ * @parsed_url: The URL struct.
+ *
+ * Gets the username part of URL.
+ *
+ * Returns: The username.
+ */
+const gchar *
+purple_http_url_get_username(const PurpleHttpURL *parsed_url);
+
+/**
+ * purple_http_url_get_password:
+ * @parsed_url: The URL struct.
+ *
+ * Gets the password part of URL.
+ *
+ * Returns: The password.
+ */
+const gchar *
+purple_http_url_get_password(const PurpleHttpURL *parsed_url);
+
+/**
+ * purple_http_url_get_host:
+ * @parsed_url: The URL struct.
+ *
+ * Gets the hostname part of URL.
+ *
+ * Returns: The hostname.
+ */
+const gchar *
+purple_http_url_get_host(const PurpleHttpURL *parsed_url);
+
+/**
+ * purple_http_url_get_port:
+ * @parsed_url: The URL struct.
+ *
+ * Gets the port part of URL.
+ *
+ * Returns: The port number.
+ */
+int
+purple_http_url_get_port(const PurpleHttpURL *parsed_url);
+
+/**
+ * purple_http_url_get_path:
+ * @parsed_url: The URL struct.
+ *
+ * Gets the path part of URL.
+ *
+ * Returns: The path.
+ */
+const gchar *
+purple_http_url_get_path(const PurpleHttpURL *parsed_url);
+
+/**
+ * purple_http_url_get_fragment:
+ * @parsed_url: The URL struct.
+ *
+ * Gets the fragment part of URL.
+ *
+ * Returns: The fragment.
+ */
+const gchar *
+purple_http_url_get_fragment(const PurpleHttpURL *parsed_url);
+
+
+/**************************************************************************/
+/* Cookie jar API */
+/**************************************************************************/
+
+/**
+ * purple_http_cookie_jar_new:
+ *
+ * Creates new cookie jar,
+ *
+ * Returns: empty cookie jar.
+ */
+PurpleHttpCookieJar * purple_http_cookie_jar_new(void);
+
+/**
+ * purple_http_cookie_jar_ref:
+ * @cookie_jar: The cookie jar.
+ *
+ * Increment the reference count.
+ */
+void purple_http_cookie_jar_ref(PurpleHttpCookieJar *cookie_jar);
+
+/**
+ * purple_http_cookie_jar_unref:
+ * @cookie_jar: The cookie jar.
+ *
+ * Decrement the reference count.
+ *
+ * If the reference count reaches zero, the cookie jar will be freed.
+ *
+ * Returns: @cookie_jar or %NULL if the reference count reached zero.
+ */
+PurpleHttpCookieJar * purple_http_cookie_jar_unref(
+ PurpleHttpCookieJar *cookie_jar);
+
+/**
+ * purple_http_cookie_jar_set:
+ * @cookie_jar: The cookie jar.
+ * @name: Cookie name.
+ * @value: Cookie contents.
+ *
+ * Sets the cookie.
+ */
+void purple_http_cookie_jar_set(PurpleHttpCookieJar *cookie_jar,
+ const gchar *name, const gchar *value);
+
+/**
+ * purple_http_cookie_jar_get:
+ * @cookie_jar: The cookie jar.
+ * @name: Cookie name.
+ *
+ * Gets the cookie.
+ *
+ * Returns: Cookie contents, or NULL, if cookie doesn't exists.
+ */
+const gchar * purple_http_cookie_jar_get(PurpleHttpCookieJar *cookie_jar,
+ const gchar *name);
+
+/**
+ * purple_http_cookie_jar_is_empty:
+ * @cookie_jar: The cookie jar.
+ *
+ * Checks, if the cookie jar contains any cookies.
+ *
+ * Returns: TRUE, if cookie jar contains any cookie, FALSE otherwise.
+ */
+gboolean purple_http_cookie_jar_is_empty(PurpleHttpCookieJar *cookie_jar);
+
+
+/**************************************************************************/
+/* HTTP Request API */
+/**************************************************************************/
+
+/**
+ * purple_http_request_new:
+ * @url: The URL to request for, or NULL to leave empty (to be set with
+ * purple_http_request_set_url).
+ *
+ * Creates the new instance of HTTP request configuration.
+ *
+ * Returns: The new instance of HTTP request struct.
+ */
+PurpleHttpRequest * purple_http_request_new(const gchar *url);
+
+/**
+ * purple_http_request_ref:
+ * @request: The request.
+ *
+ * Increment the reference count.
+ */
+void purple_http_request_ref(PurpleHttpRequest *request);
+
+/**
+ * purple_http_request_unref:
+ * @request: The request.
+ *
+ * Decrement the reference count.
+ *
+ * If the reference count reaches zero, the http request struct will be freed.
+ *
+ * Returns: @request or %NULL if the reference count reached zero.
+ */
+PurpleHttpRequest * purple_http_request_unref(PurpleHttpRequest *request);
+
+/**
+ * purple_http_request_set_url:
+ * @request: The request.
+ * @url: The url.
+ *
+ * Sets URL for HTTP request.
+ */
+void purple_http_request_set_url(PurpleHttpRequest *request, const gchar *url);
+
+/**
+ * purple_http_request_set_url_printf:
+ * @request: The request.
+ * @format: The format string.
+ *
+ * Constructs and sets an URL for HTTP request.
+ */
+void purple_http_request_set_url_printf(PurpleHttpRequest *request,
+ const gchar *format, ...) G_GNUC_PRINTF(2, 3);
+
+/**
+ * purple_http_request_get_url:
+ * @request: The request.
+ *
+ * Gets URL set for the HTTP request.
+ *
+ * Returns: URL set for this request.
+ */
+const gchar * purple_http_request_get_url(PurpleHttpRequest *request);
+
+/**
+ * purple_http_request_set_method:
+ * @request: The request.
+ * @method: The method, or NULL for default.
+ *
+ * Sets custom HTTP method used for the request.
+ */
+void purple_http_request_set_method(PurpleHttpRequest *request,
+ const gchar *method);
+
+/**
+ * purple_http_request_get_method:
+ * @request: The request.
+ *
+ * Gets HTTP method set for the request.
+ *
+ * Returns: The method.
+ */
+const gchar * purple_http_request_get_method(PurpleHttpRequest *request);
+
+/**
+ * purple_http_request_set_keepalive_pool:
+ * @request: The request.
+ * @pool: The new KeepAlive pool, or NULL to reset.
+ *
+ * Sets HTTP KeepAlive connections pool for the request.
+ *
+ * It increases pool's reference count.
+ */
+void
+purple_http_request_set_keepalive_pool(PurpleHttpRequest *request,
+ PurpleHttpKeepalivePool *pool);
+
+/**
+ * purple_http_request_get_keepalive_pool:
+ * @request: The request.
+ *
+ * Gets HTTP KeepAlive connections pool associated with the request.
+ *
+ * It doesn't affect pool's reference count.
+ *
+ * Returns: The KeepAlive pool, used for the request.
+ */
+PurpleHttpKeepalivePool *
+purple_http_request_get_keepalive_pool(PurpleHttpRequest *request);
+
+/**
+ * purple_http_request_set_contents:
+ * @request: The request.
+ * @contents: The contents.
+ * @length: The length of contents (-1 if it's a NULL-terminated string)
+ *
+ * Sets contents of HTTP request (for example, POST data).
+ */
+void purple_http_request_set_contents(PurpleHttpRequest *request,
+ const gchar *contents, int length);
+
+/**
+ * purple_http_request_set_contents_reader:
+ * @request: The request.
+ * @reader: (scope call): The reader callback.
+ * @contents_length: The size of all contents.
+ * @user_data: The user data to pass to the callback function.
+ *
+ * Sets contents reader for HTTP request, used mainly for possible large
+ * uploads.
+ */
+void purple_http_request_set_contents_reader(PurpleHttpRequest *request,
+ PurpleHttpContentReader reader, int contents_length, gpointer user_data);
+
+/**
+ * purple_http_request_set_response_writer:
+ * @request: The request.
+ * @writer: (scope call): The writer callback, or %NULL to remove existing.
+ * @user_data: The user data to pass to the callback function.
+ *
+ * Set contents writer for HTTP response.
+ */
+void purple_http_request_set_response_writer(PurpleHttpRequest *request,
+ PurpleHttpContentWriter writer, gpointer user_data);
+
+/**
+ * purple_http_request_set_timeout:
+ * @request: The request.
+ * @timeout: Time (in seconds) after that timeout will be cancelled,
+ * -1 for infinite time.
+ *
+ * Set maximum amount of time, that request is allowed to run.
+ */
+void purple_http_request_set_timeout(PurpleHttpRequest *request, int timeout);
+
+/**
+ * purple_http_request_get_timeout:
+ * @request: The request.
+ *
+ * Get maximum amount of time, that request is allowed to run.
+ *
+ * Returns: Timeout currently set (-1 for infinite).
+ */
+int purple_http_request_get_timeout(PurpleHttpRequest *request);
+
+/**
+ * purple_http_request_set_max_redirects:
+ * @request: The request.
+ * @max_redirects: Maximum amount of redirects, or -1 for unlimited.
+ *
+ * Sets maximum amount of redirects.
+ */
+void purple_http_request_set_max_redirects(PurpleHttpRequest *request,
+ int max_redirects);
+
+/**
+ * purple_http_request_get_max_redirects:
+ * @request: The request.
+ *
+ * Gets maximum amount of redirects.
+ *
+ * Returns: Current maximum amount of redirects (-1 for unlimited).
+ */
+int purple_http_request_get_max_redirects(PurpleHttpRequest *request);
+
+/**
+ * purple_http_request_set_cookie_jar:
+ * @request: The request.
+ * @cookie_jar: The cookie jar.
+ *
+ * Sets cookie jar used for the request.
+ */
+void purple_http_request_set_cookie_jar(PurpleHttpRequest *request,
+ PurpleHttpCookieJar *cookie_jar);
+
+/**
+ * purple_http_request_get_cookie_jar:
+ * @request: The request.
+ *
+ * Gets cookie jar used for the request.
+ *
+ * Returns: The cookie jar.
+ */
+PurpleHttpCookieJar * purple_http_request_get_cookie_jar(
+ PurpleHttpRequest *request);
+
+/**
+ * purple_http_request_set_http11:
+ * @request: The request.
+ * @http11: TRUE for HTTP/1.1, FALSE for HTTP/1.0.
+ *
+ * Sets HTTP version to use.
+ */
+void purple_http_request_set_http11(PurpleHttpRequest *request,
+ gboolean http11);
+
+/**
+ * purple_http_request_is_http11:
+ * @request: The request.
+ *
+ * Gets used HTTP version.
+ *
+ * Returns: TRUE, if we use HTTP/1.1, FALSE for HTTP/1.0.
+ */
+gboolean purple_http_request_is_http11(PurpleHttpRequest *request);
+
+/**
+ * purple_http_request_set_max_len:
+ * @request: The request.
+ * @max_len: Maximum length of response to read (-1 for the maximum
+ * supported amount).
+ *
+ * Sets maximum length of response content to read.
+ *
+ * Headers length doesn't count here.
+ *
+ */
+void purple_http_request_set_max_len(PurpleHttpRequest *request, int max_len);
+
+/**
+ * purple_http_request_get_max_len:
+ * @request: The request.
+ *
+ * Gets maximum length of response content to read.
+ *
+ * Returns: Maximum length of response to read, or -1 if unlimited.
+ */
+int purple_http_request_get_max_len(PurpleHttpRequest *request);
+
+/**
+ * purple_http_request_header_set:
+ * @request: The request.
+ * @key: A header to be set.
+ * @value: A value to set, or NULL to remove specified header.
+ *
+ * Sets (replaces, if exists) specified HTTP request header with provided value.
+ *
+ * See purple_http_request_header_add().
+ */
+void purple_http_request_header_set(PurpleHttpRequest *request,
+ const gchar *key, const gchar *value);
+
+/**
+ * purple_http_request_header_set_printf:
+ * @request: The request.
+ * @key: A header to be set.
+ * @format: The format string.
+ *
+ * Constructs and sets (replaces, if exists) specified HTTP request header.
+ */
+void purple_http_request_header_set_printf(PurpleHttpRequest *request,
+ const gchar *key, const gchar *format, ...) G_GNUC_PRINTF(3, 4);
+
+/**
+ * purple_http_request_header_add:
+ * @key: A header to be set.
+ * @value: A value to set.
+ *
+ * Adds (without replacing, if exists) an HTTP request header.
+ *
+ * See purple_http_request_header_set().
+ */
+void purple_http_request_header_add(PurpleHttpRequest *request,
+ const gchar *key, const gchar *value);
+
+
+/**************************************************************************/
+/* HTTP Keep-Alive pool API */
+/**************************************************************************/
+
+/**
+ * purple_http_keepalive_pool_new:
+ *
+ * Creates a new HTTP Keep-Alive pool.
+ */
+PurpleHttpKeepalivePool *
+purple_http_keepalive_pool_new(void);
+
+/**
+ * purple_http_keepalive_pool_ref:
+ * @pool: The HTTP Keep-Alive pool.
+ *
+ * Increment the reference count.
+ */
+void
+purple_http_keepalive_pool_ref(PurpleHttpKeepalivePool *pool);
+
+/**
+ * purple_http_keepalive_pool_unref:
+ * @pool: The HTTP Keep-Alive pool.
+ *
+ * Decrement the reference count.
+ *
+ * If the reference count reaches zero, the pool will be freed and all
+ * connections will be closed.
+ *
+ * Returns: @pool or %NULL if the reference count reached zero.
+ */
+PurpleHttpKeepalivePool *
+purple_http_keepalive_pool_unref(PurpleHttpKeepalivePool *pool);
+
+/**
+ * purple_http_keepalive_pool_set_limit_per_host:
+ * @pool: The HTTP Keep-Alive pool.
+ * @limit: The new limit, 0 for unlimited.
+ *
+ * Sets maximum allowed number of connections to specific host-triple (is_ssl +
+ * hostname + port).
+ */
+void
+purple_http_keepalive_pool_set_limit_per_host(PurpleHttpKeepalivePool *pool,
+ guint limit);
+
+/**
+ * purple_http_keepalive_pool_get_limit_per_host:
+ * @pool: The HTTP Keep-Alive pool.
+ *
+ * Gets maximum allowed number of connections to specific host-triple (is_ssl +
+ * hostname + port).
+ *
+ * Returns: The limit.
+ */
+guint
+purple_http_keepalive_pool_get_limit_per_host(PurpleHttpKeepalivePool *pool);
+
+
+/**************************************************************************/
+/* HTTP connection set API */
+/**************************************************************************/
+
+PurpleHttpConnectionSet *
+purple_http_connection_set_new(void);
+
+void
+purple_http_connection_set_destroy(PurpleHttpConnectionSet *set);
+
+void
+purple_http_connection_set_add(PurpleHttpConnectionSet *set,
+ PurpleHttpConnection *http_conn);
+
+
+/**************************************************************************/
+/* HTTP response API */
+/**************************************************************************/
+
+/**
+ * purple_http_response_is_successful:
+ * @response: The response.
+ *
+ * Checks, if HTTP request was performed successfully.
+ *
+ * Returns: TRUE, if request was performed successfully.
+ */
+gboolean purple_http_response_is_successful(PurpleHttpResponse *response);
+
+/**
+ * purple_http_response_get_code:
+ * @response: The response.
+ *
+ * Gets HTTP response code.
+ *
+ * Returns: HTTP response code.
+ */
+int purple_http_response_get_code(PurpleHttpResponse *response);
+
+/**
+ * purple_http_response_get_error:
+ * @response: The response.
+ *
+ * Gets error description.
+ *
+ * Returns: Localized error description or NULL, if there was no error.
+ */
+const gchar * purple_http_response_get_error(PurpleHttpResponse *response);
+
+/**
+ * purple_http_response_get_data_len:
+ * @response: The response.
+ *
+ * Gets HTTP response data length.
+ *
+ * Returns: Data length;
+ */
+gsize purple_http_response_get_data_len(PurpleHttpResponse *response);
+
+/**
+ * purple_http_response_get_data:
+ * @response: The response.
+ * @len: Return address for the size of the data. Can be NULL.
+ *
+ * Gets HTTP response data.
+ *
+ * Response data is not written, if writer callback was set for request.
+ *
+ * Returns: The data.
+ */
+const gchar * purple_http_response_get_data(PurpleHttpResponse *response, size_t *len);
+
+/**
+ * purple_http_response_get_all_headers:
+ * @response: The response.
+ *
+ * Gets all headers got with response.
+ *
+ * Returns: GList of PurpleKeyValuePair, which keys are header field
+ * names (gchar*) and values are its contents (gchar*).
+ */
+const GList * purple_http_response_get_all_headers(PurpleHttpResponse *response);
+
+/**
+ * purple_http_response_get_headers_by_name:
+ * @response: The response.
+ * @name: The name of header field.
+ *
+ * Gets all headers with specified name got with response.
+ *
+ * Returns: GList of header field records contents (gchar*).
+ */
+const GList * purple_http_response_get_headers_by_name(
+ PurpleHttpResponse *response, const gchar *name);
+
+/**
+ * purple_http_response_get_header:
+ * @response: The response.
+ * @name: The name of header field.
+ *
+ * Gets one header contents with specified name got with response.
+ *
+ * To get all headers with the same name, use
+ * purple_http_response_get_headers_by_name instead.
+ *
+ * Returns: Header field contents or NULL, if there is no such one.
+ */
+const gchar * purple_http_response_get_header(PurpleHttpResponse *response,
+ const gchar *name);
+
+
+/**************************************************************************/
+/* HTTP Subsystem */
+/**************************************************************************/
+
+/**
+ * purple_http_init:
+ *
+ * Initializes the http subsystem.
+ */
+void purple_http_init(void);
+
+/**
+ * purple_http_uninit:
+ *
+ * Uninitializes the http subsystem.
+ */
+void purple_http_uninit(void);
+
+G_END_DECLS
+
+#endif /* _PURPLE_HTTP_H_ */
diff --git a/libpurple/idle.c b/libpurple/idle.c
index 909ae72f00..712516c1db 100644
--- a/libpurple/idle.c
+++ b/libpurple/idle.c
@@ -41,7 +41,7 @@ typedef enum
static PurpleIdleUiOps *idle_ui_ops = NULL;
-/**
+/*
* This is needed for the I'dle Mak'er plugin to work correctly. We
* use it to determine if we're the ones who set our accounts idle
* or if someone else did it (the I'dle Mak'er plugin, for example).
@@ -114,7 +114,7 @@ static gint time_until_next_idle_event;
static void
check_idleness(void)
{
- time_t time_idle;
+ time_t time_idle = 0;
gboolean auto_away;
const gchar *idle_reporting;
gboolean report_idle = TRUE;
@@ -142,7 +142,6 @@ check_idleness(void)
else
{
/* Don't report idle time */
- time_idle = 0;
report_idle = FALSE;
/* If we're not reporting idle, we can still do auto-away.
@@ -231,8 +230,7 @@ check_idleness_timer(void)
}
static void
-im_msg_sent_cb(PurpleAccount *account, const char *receiver,
- const char *message, void *data)
+im_msg_sent_cb(PurpleAccount *account, PurpleMessage *msg, void *data)
{
/* Check our idle time after an IM is sent */
check_idleness();
@@ -282,6 +280,33 @@ purple_idle_set(time_t time)
last_active_time = time;
}
+static PurpleIdleUiOps *
+purple_idle_ui_ops_copy(PurpleIdleUiOps *ops)
+{
+ PurpleIdleUiOps *ops_new;
+
+ g_return_val_if_fail(ops != NULL, NULL);
+
+ ops_new = g_new(PurpleIdleUiOps, 1);
+ *ops_new = *ops;
+
+ return ops_new;
+}
+
+GType
+purple_idle_ui_ops_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleIdleUiOps",
+ (GBoxedCopyFunc)purple_idle_ui_ops_copy,
+ (GBoxedFreeFunc)g_free);
+ }
+
+ return type;
+}
+
void
purple_idle_set_ui_ops(PurpleIdleUiOps *ops)
{
diff --git a/libpurple/idle.h b/libpurple/idle.h
index f01c75bc60..985300ca8d 100644
--- a/libpurple/idle.h
+++ b/libpurple/idle.h
@@ -1,8 +1,3 @@
-/**
- * @file idle.h Idle API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -23,34 +18,48 @@
* 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_IDLE_H_
#define _PURPLE_IDLE_H_
+/**
+ * SECTION:idle
+ * @section_id: libpurple-idle
+ * @short_description: <filename>idle.h</filename>
+ * @title: Idle API
+ */
#include <time.h>
+#include <glib-object.h>
+
+#define PURPLE_TYPE_IDLE_UI_OPS (purple_idle_ui_ops_get_type())
+
+typedef struct _PurpleIdleUiOps PurpleIdleUiOps;
/**
+ * PurpleIdleUiOps:
+ *
* Idle UI operations.
*/
-typedef struct
+struct _PurpleIdleUiOps
{
time_t (*get_time_idle)(void);
+ /*< private >*/
void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
void (*_purple_reserved4)(void);
-} PurpleIdleUiOps;
+};
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name Idle API */
+/* Idle API */
/**************************************************************************/
-/*@{*/
/**
+ * purple_idle_touch:
+ *
* Touch our idle tracker. This signifies that the user is
* 'active'. The conversation code calls this when the
* user sends an IM, for example.
@@ -58,47 +67,56 @@ extern "C" {
void purple_idle_touch(void);
/**
+ * purple_idle_set:
+ *
* Fake our idle time by setting the time at which our
* accounts purportedly became idle. This is used by
* the I'dle Mak'er plugin.
*/
void purple_idle_set(time_t time);
-/*@}*/
-
/**************************************************************************/
-/** @name Idle Subsystem */
+/* Idle Subsystem */
/**************************************************************************/
-/*@{*/
/**
- * Sets the UI operations structure to be used for idle reporting.
+ * purple_idle_ui_ops_get_type:
*
- * @param ops The UI operations structure.
+ * Returns: The #GType for the #PurpleIdleUiOps boxed structure.
+ */
+GType purple_idle_ui_ops_get_type(void);
+
+/**
+ * purple_idle_set_ui_ops:
+ * @ops: The UI operations structure.
+ *
+ * Sets the UI operations structure to be used for idle reporting.
*/
void purple_idle_set_ui_ops(PurpleIdleUiOps *ops);
/**
+ * purple_idle_get_ui_ops:
+ *
* Returns the UI operations structure used for idle reporting.
*
- * @return The UI operations structure in use.
+ * Returns: The UI operations structure in use.
*/
PurpleIdleUiOps *purple_idle_get_ui_ops(void);
/**
+ * purple_idle_init:
+ *
* Initializes the idle system.
*/
void purple_idle_init(void);
/**
+ * purple_idle_uninit:
+ *
* Uninitializes the idle system.
*/
void purple_idle_uninit(void);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_IDLE_H_ */
diff --git a/libpurple/image-store.c b/libpurple/image-store.c
new file mode 100644
index 0000000000..9e68bb6e9c
--- /dev/null
+++ b/libpurple/image-store.c
@@ -0,0 +1,233 @@
+/* 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 "image-store.h"
+
+#include "eventloop.h"
+#include "glibcompat.h"
+#include "util.h"
+
+#define TEMP_IMAGE_TIMEOUT 5
+
+static GHashTable *id_to_image = NULL;
+static guint last_id = 0;
+
+/* keys: timeout handle */
+static GHashTable *temp_images = NULL;
+
+/* keys: img id */
+static GSList *perm_images = NULL;
+
+static void
+image_reset_id(gpointer _id)
+{
+ g_return_if_fail(id_to_image != NULL);
+
+ g_hash_table_remove(id_to_image, _id);
+}
+
+static guint
+image_set_id(PurpleImage *image)
+{
+ /* Use the next unused id number. We do it in a loop on the off chance
+ * that next id wraps back around to 0 and the hash table still contains
+ * entries from the first time around.
+ */
+ while (TRUE) {
+ last_id++;
+
+ if (G_UNLIKELY(last_id == 0))
+ continue;
+
+ if (purple_image_store_get(last_id) == NULL)
+ break;
+ }
+
+ g_object_set_data_full(G_OBJECT(image), "purple-image-store-id",
+ GINT_TO_POINTER(last_id), image_reset_id);
+ g_hash_table_insert(id_to_image, GINT_TO_POINTER(last_id), image);
+ return last_id;
+}
+
+static guint
+image_get_id(PurpleImage *image)
+{
+ return GPOINTER_TO_INT(g_object_get_data(G_OBJECT(image),
+ "purple-image-store-id"));
+}
+
+guint
+purple_image_store_add(PurpleImage *image)
+{
+ guint id;
+
+ g_return_val_if_fail(PURPLE_IS_IMAGE(image), 0);
+
+ id = image_get_id(image);
+ if (id > 0)
+ return id;
+
+ id = image_set_id(image);
+
+ g_object_ref(image);
+ perm_images = g_slist_prepend(perm_images, image);
+
+ return id;
+}
+
+guint
+purple_image_store_add_weak(PurpleImage *image)
+{
+ guint id;
+
+ g_return_val_if_fail(PURPLE_IS_IMAGE(image), 0);
+
+ id = image_get_id(image);
+ if (id > 0)
+ return id;
+
+ return image_set_id(image);
+}
+
+static gboolean
+remove_temporary(gpointer _image)
+{
+ PurpleImage *image = _image;
+ guint handle;
+
+ handle = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(image),
+ "purple-image-store-handle"));
+
+ g_hash_table_remove(temp_images, GINT_TO_POINTER(handle));
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+cancel_temporary(gpointer key, gpointer value, gpointer _unused)
+{
+ purple_timeout_remove(GPOINTER_TO_INT(key));
+}
+
+guint
+purple_image_store_add_temporary(PurpleImage *image)
+{
+ guint id;
+ guint handle;
+
+ g_return_val_if_fail(PURPLE_IS_IMAGE(image), 0);
+
+ id = image_get_id(image);
+ /* XXX: add_temporary doesn't extend previous temporary call, sorry */
+ if (id > 0)
+ return id;
+
+ id = image_set_id(image);
+
+ g_object_ref(image);
+ handle = purple_timeout_add_seconds(TEMP_IMAGE_TIMEOUT,
+ remove_temporary, image);
+ g_object_set_data(G_OBJECT(image), "purple-image-store-handle",
+ GINT_TO_POINTER(handle));
+ g_hash_table_insert(temp_images, GINT_TO_POINTER(handle), image);
+
+ return id;
+}
+
+PurpleImage *
+purple_image_store_get(guint id)
+{
+ return g_hash_table_lookup(id_to_image, GINT_TO_POINTER(id));
+}
+
+/* TODO: handle PURPLE_IMAGE_STORE_STOCK_PROTOCOL */
+PurpleImage *
+purple_image_store_get_from_uri(const gchar *uri)
+{
+ guint64 longid;
+ guint id;
+ gchar *endptr;
+ gchar endchar;
+
+ g_return_val_if_fail(uri != NULL, NULL);
+
+ if (!purple_str_has_prefix(uri, PURPLE_IMAGE_STORE_PROTOCOL))
+ return NULL;
+
+ uri += sizeof(PURPLE_IMAGE_STORE_PROTOCOL) - 1;
+ if (uri[0] == '-')
+ return NULL;
+
+ longid = g_ascii_strtoull(uri, &endptr, 10);
+ endchar = endptr[0];
+ if (endchar != '\0' && endchar != '"' &&
+ endchar != '\'' && endchar != ' ')
+ {
+ return NULL;
+ }
+
+ id = longid;
+ if (id != longid)
+ return NULL;
+
+ return purple_image_store_get(id);
+}
+
+gchar *
+purple_image_store_get_uri(PurpleImage *image)
+{
+ gboolean is_ready;
+ const gchar *path;
+ guint img_id;
+
+ g_return_val_if_fail(PURPLE_IS_IMAGE(image), NULL);
+
+ is_ready = purple_image_is_ready(image);
+ path = purple_image_get_path(image);
+
+ if (is_ready && path)
+ return g_filename_to_uri(path, NULL, NULL);
+
+ img_id = purple_image_store_add_weak(image);
+ return g_strdup_printf(PURPLE_IMAGE_STORE_PROTOCOL "%u", img_id);
+}
+
+void
+_purple_image_store_init(void)
+{
+ id_to_image = g_hash_table_new(g_direct_hash, g_direct_equal);
+ temp_images = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ NULL, g_object_unref);
+}
+
+void
+_purple_image_store_uninit(void)
+{
+ g_slist_free_full(perm_images, g_object_unref);
+ perm_images = NULL;
+
+ g_hash_table_foreach(temp_images, cancel_temporary, NULL);
+ g_hash_table_destroy(temp_images);
+ temp_images = NULL;
+
+ g_hash_table_destroy(id_to_image);
+ id_to_image = NULL;
+}
diff --git a/libpurple/image-store.h b/libpurple/image-store.h
new file mode 100644
index 0000000000..d00d271b6f
--- /dev/null
+++ b/libpurple/image-store.h
@@ -0,0 +1,166 @@
+/* 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 _PURPLE_IMAGE_STORE_H_
+#define _PURPLE_IMAGE_STORE_H_
+/**
+ * SECTION:image-store
+ * @include:image-store.h
+ * @section_id: libpurple-image-store
+ * @short_description: a global, temporary storage for images
+ * @title: Image store
+ *
+ * Image store references #PurpleImage's by a simple integer value.
+ * It's a convenient way to embed images in HTML-like strings.
+ *
+ * Integer ID's are tracked for being valid - when a image is destroyed, it's
+ * also removed from the store. If the application runs for a very long time and
+ * all possible id numbers from integer range are utilized, it will use
+ * previously released numbers.
+ */
+
+#include "image.h"
+
+/**
+ * PURPLE_IMAGE_STORE_PROTOCOL:
+ *
+ * A global URI prefix for images stored in this subsystem.
+ */
+#define PURPLE_IMAGE_STORE_PROTOCOL "purple-image:"
+
+/**
+ * PURPLE_IMAGE_STORE_STOCK_PROTOCOL:
+ *
+ * A global URI prefix for stock images, with names defined by libpurple and
+ * contents defined by the UI.
+ */
+#define PURPLE_IMAGE_STORE_STOCK_PROTOCOL "purple-stock-image:"
+
+G_BEGIN_DECLS
+
+/**
+ * purple_image_store_add:
+ * @image: the image.
+ *
+ * Permanently adds an image to the store. If the @image is already in the
+ * store, it will return its current id.
+ *
+ * This function increases @image's reference count, so it won't be destroyed
+ * until image store subsystem is shut down. Don't decrease @image's reference
+ * count by yourself - if you don't want the store to hold the reference,
+ * use #purple_image_store_add_weak.
+ *
+ * Returns: the unique identifier for the @image.
+ */
+guint
+purple_image_store_add(PurpleImage *image);
+
+/**
+ * purple_image_store_add_weak:
+ * @image: the image.
+ *
+ * Adds an image to the store without increasing reference count. It will be
+ * removed from the store when @image gets destroyed.
+ *
+ * If the @image is already in the store, it will return its current id.
+ *
+ * Returns: the unique identifier for the @image.
+ */
+guint
+purple_image_store_add_weak(PurpleImage *image);
+
+/**
+ * purple_image_store_add_temporary:
+ * @image: the image.
+ *
+ * Adds an image to the store to be used in a short period of time.
+ * If the @image is already in the store, it will just return its current id.
+ *
+ * Increases reference count for the @image for a time long enough to display
+ * the @image by the UI. In current implementation it's five seconds, but may be
+ * changed in the future, so if you need more sophisticated reference
+ * management, implement it on your own.
+ *
+ * Returns: the unique identifier for the @image.
+ */
+guint
+purple_image_store_add_temporary(PurpleImage *image);
+
+/**
+ * purple_image_store_get:
+ * @id: the unique identifier of an image.
+ *
+ * Finds the image with a certain identifier within a store.
+ *
+ * Returns: (transfer none): the image referenced by @id, or %NULL if it
+ * doesn't exists.
+ */
+PurpleImage *
+purple_image_store_get(guint id);
+
+/**
+ * purple_image_store_get_from_uri:
+ * @uri: the URI of a potential #PurpleImage. Should not be %NULL.
+ *
+ * Checks, if the @uri is pointing to any #PurpleImage by referring
+ * to #PURPLE_IMAGE_STORE_PROTOCOL and returns the image, if it's valid.
+ *
+ * The function doesn't throw any warning, if the @uri is for any
+ * other protocol.
+ *
+ * Returns: (transfer none): the image referenced by @uri, or %NULL if it
+ * doesn't point to any valid image.
+ */
+PurpleImage *
+purple_image_store_get_from_uri(const gchar *uri);
+
+/**
+ * purple_image_store_get_uri:
+ * @image: the image.
+ *
+ * Generates an URI for the @image, to be retrieved using
+ * #purple_image_store_get_from_uri.
+ *
+ * Returns: (transfer full): the URI for the @image. Should be #g_free'd when
+ * you done using it.
+ */
+gchar *
+purple_image_store_get_uri(PurpleImage *image);
+
+/**
+ * _purple_image_store_init: (skip)
+ *
+ * Initializes the image store subsystem.
+ */
+void
+_purple_image_store_init(void);
+
+/**
+ * _purple_image_store_uninit: (skip)
+ *
+ * Uninitializes the image store subsystem.
+ */
+void
+_purple_image_store_uninit(void);
+
+G_END_DECLS
+
+#endif /* _PURPLE_IMAGE_STORE_H_ */
diff --git a/libpurple/image.c b/libpurple/image.c
new file mode 100644
index 0000000000..02ee809a94
--- /dev/null
+++ b/libpurple/image.c
@@ -0,0 +1,622 @@
+/* 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 "glibcompat.h"
+
+#include "debug.h"
+#include "image.h"
+
+#define PURPLE_IMAGE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_IMAGE, PurpleImagePrivate))
+
+typedef struct {
+ gchar *path;
+ GString *contents;
+
+ const gchar *extension;
+ const gchar *mime;
+ gchar *gen_filename;
+ gchar *friendly_filename;
+
+ gboolean is_ready;
+ gboolean has_failed;
+} PurpleImagePrivate;
+
+enum
+{
+ PROP_0,
+ PROP_IS_READY,
+ PROP_HAS_FAILED,
+ PROP_LAST
+};
+
+enum
+{
+ SIG_READY,
+ SIG_FAILED,
+ SIG_LAST
+};
+
+static GObjectClass *parent_class;
+static guint signals[SIG_LAST];
+static GParamSpec *properties[PROP_LAST];
+
+/******************************************************************************
+ * Internal logic
+ ******************************************************************************/
+
+static void
+has_failed(PurpleImage *image)
+{
+ gboolean ready_changed;
+ PurpleImagePrivate *priv;
+ priv = PURPLE_IMAGE_GET_PRIVATE(image);
+
+ g_return_if_fail(!priv->has_failed);
+
+ priv->has_failed = TRUE;
+ ready_changed = (priv->is_ready != FALSE);
+ priv->is_ready = FALSE;
+
+ if (priv->contents) {
+ g_string_free(priv->contents, TRUE);
+ priv->contents = NULL;
+ }
+
+ if (ready_changed) {
+ g_object_notify_by_pspec(G_OBJECT(image),
+ properties[PROP_IS_READY]);
+ }
+ g_object_notify_by_pspec(G_OBJECT(image), properties[PROP_HAS_FAILED]);
+ g_signal_emit(image, signals[SIG_FAILED], 0);
+}
+
+static void
+became_ready(PurpleImage *image)
+{
+ PurpleImagePrivate *priv;
+ priv = PURPLE_IMAGE_GET_PRIVATE(image);
+
+ g_return_if_fail(!priv->has_failed);
+ g_return_if_fail(!priv->is_ready);
+
+ priv->is_ready = TRUE;
+
+ g_object_notify_by_pspec(G_OBJECT(image), properties[PROP_IS_READY]);
+ g_signal_emit(image, signals[SIG_READY], 0);
+}
+
+static void
+steal_contents(PurpleImagePrivate *priv, gpointer data, gsize length)
+{
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(priv->contents == NULL);
+ g_return_if_fail(data != NULL);
+ g_return_if_fail(length > 0);
+
+ priv->contents = g_string_new(NULL);
+ g_free(priv->contents->str);
+ priv->contents->str = data;
+ priv->contents->len = priv->contents->allocated_len = length;
+}
+
+static void
+fill_data(PurpleImage *image)
+{
+ PurpleImagePrivate *priv;
+ GError *error = NULL;
+ gchar *contents;
+ gsize length;
+
+ priv = PURPLE_IMAGE_GET_PRIVATE(image);
+ if (priv->contents)
+ return;
+
+ if (!priv->is_ready)
+ return;
+
+ g_return_if_fail(priv->path);
+ (void)g_file_get_contents(priv->path, &contents, &length, &error);
+ if (error) {
+ purple_debug_error("image", "failed to read '%s' image: %s",
+ priv->path, error->message);
+ g_error_free(error);
+
+ has_failed(image);
+ return;
+ }
+
+ steal_contents(priv, contents, length);
+}
+
+
+/******************************************************************************
+ * API implementation
+ ******************************************************************************/
+
+PurpleImage *
+purple_image_new_from_file(const gchar *path, gboolean be_eager)
+{
+ PurpleImagePrivate *priv;
+ PurpleImage *img;
+
+ g_return_val_if_fail(path != NULL, NULL);
+ g_return_val_if_fail(g_file_test(path, G_FILE_TEST_EXISTS), NULL);
+
+ img = g_object_new(PURPLE_TYPE_IMAGE, NULL);
+ purple_image_set_friendly_filename(img, path);
+ priv = PURPLE_IMAGE_GET_PRIVATE(img);
+ priv->path = g_strdup(path);
+
+ if (be_eager) {
+ fill_data(img);
+ if (!priv->contents) {
+ g_object_unref(img);
+ return NULL;
+ }
+
+ g_assert(priv->is_ready && !priv->has_failed);
+ }
+
+ return img;
+}
+
+PurpleImage *
+purple_image_new_from_data(gpointer data, gsize length)
+{
+ PurpleImage *img;
+ PurpleImagePrivate *priv;
+
+ g_return_val_if_fail(data != NULL, NULL);
+ g_return_val_if_fail(length > 0, NULL);
+
+ img = g_object_new(PURPLE_TYPE_IMAGE, NULL);
+ priv = PURPLE_IMAGE_GET_PRIVATE(img);
+
+ steal_contents(priv, data, length);
+
+ return img;
+}
+
+gboolean
+purple_image_save(PurpleImage *image, const gchar *path)
+{
+ PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
+ gconstpointer data;
+ gsize len;
+ gboolean succ;
+
+ g_return_val_if_fail(priv != NULL, FALSE);
+ g_return_val_if_fail(path != NULL, FALSE);
+ g_return_val_if_fail(path[0] != '\0', FALSE);
+
+ data = purple_image_get_data(image);
+ len = purple_image_get_size(image);
+
+ g_return_val_if_fail(data != NULL, FALSE);
+ g_return_val_if_fail(len > 0, FALSE);
+
+ succ = purple_util_write_data_to_file_absolute(path, data, len);
+ if (succ && priv->path == NULL)
+ priv->path = g_strdup(path);
+
+ return succ;
+}
+
+const gchar *
+purple_image_get_path(PurpleImage *image)
+{
+ PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->path;
+}
+
+gboolean
+purple_image_is_ready(PurpleImage *image)
+{
+ PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
+
+ g_return_val_if_fail(priv != NULL, FALSE);
+
+ return priv->is_ready;
+}
+
+gboolean
+purple_image_has_failed(PurpleImage *image)
+{
+ PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
+
+ g_return_val_if_fail(priv != NULL, TRUE);
+
+ return priv->has_failed;
+}
+
+gsize
+purple_image_get_size(PurpleImage *image)
+{
+ PurpleImagePrivate *priv;
+ priv = PURPLE_IMAGE_GET_PRIVATE(image);
+
+ g_return_val_if_fail(priv != NULL, 0);
+ g_return_val_if_fail(priv->is_ready, 0);
+
+ fill_data(image);
+ g_return_val_if_fail(priv->contents, 0);
+
+ return priv->contents->len;
+}
+
+gconstpointer
+purple_image_get_data(PurpleImage *image)
+{
+ PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+ g_return_val_if_fail(priv->is_ready, NULL);
+
+ fill_data(image);
+ g_return_val_if_fail(priv->contents, NULL);
+
+ return priv->contents->str;
+}
+
+const gchar *
+purple_image_get_extension(PurpleImage *image)
+{
+ PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
+ gconstpointer data;
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ if (priv->extension)
+ return priv->extension;
+
+ if (purple_image_get_size(image) < 4)
+ return NULL;
+
+ data = purple_image_get_data(image);
+ g_assert(data != NULL);
+
+ if (memcmp(data, "GIF8", 4) == 0)
+ return priv->extension = "gif";
+ if (memcmp(data, "\xff\xd8\xff", 3) == 0) /* 4th may be e0 through ef */
+ return priv->extension = "jpg";
+ if (memcmp(data, "\x89PNG", 4) == 0)
+ return priv->extension = "png";
+ if (memcmp(data, "MM", 2) == 0)
+ return priv->extension = "tif";
+ if (memcmp(data, "II", 2) == 0)
+ return priv->extension = "tif";
+ if (memcmp(data, "BM", 2) == 0)
+ return priv->extension = "bmp";
+ if (memcmp(data, "\x00\x00\x01\x00", 4) == 0)
+ return priv->extension = "ico";
+
+ return NULL;
+}
+
+const gchar *
+purple_image_get_mimetype(PurpleImage *image)
+{
+ PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
+ const gchar *ext = purple_image_get_extension(image);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ if (priv->mime)
+ return priv->mime;
+
+ g_return_val_if_fail(ext != NULL, NULL);
+
+ if (g_strcmp0(ext, "gif") == 0)
+ return priv->mime = "image/gif";
+ if (g_strcmp0(ext, "jpg") == 0)
+ return priv->mime = "image/jpeg";
+ if (g_strcmp0(ext, "png") == 0)
+ return priv->mime = "image/png";
+ if (g_strcmp0(ext, "tif") == 0)
+ return priv->mime = "image/tiff";
+ if (g_strcmp0(ext, "bmp") == 0)
+ return priv->mime = "image/bmp";
+ if (g_strcmp0(ext, "ico") == 0)
+ return priv->mime = "image/vnd.microsoft.icon";
+
+ return NULL;
+}
+
+const gchar *
+purple_image_generate_filename(PurpleImage *image)
+{
+ PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
+ gconstpointer data;
+ gsize len;
+ const gchar *ext;
+ gchar *checksum;
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ if (priv->gen_filename)
+ return priv->gen_filename;
+
+ ext = purple_image_get_extension(image);
+ data = purple_image_get_data(image);
+ len = purple_image_get_size(image);
+
+ g_return_val_if_fail(ext != NULL, NULL);
+ g_return_val_if_fail(data != NULL, NULL);
+ g_return_val_if_fail(len > 0, NULL);
+
+ checksum = g_compute_checksum_for_data(G_CHECKSUM_SHA1, data, len);
+ priv->gen_filename = g_strdup_printf("%s.%s", checksum, ext);
+ g_free(checksum);
+
+ return priv->gen_filename;
+}
+
+void
+purple_image_set_friendly_filename(PurpleImage *image, const gchar *filename)
+{
+ PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
+ gchar *newname;
+ const gchar *escaped;
+
+ g_return_if_fail(priv != NULL);
+
+ newname = g_path_get_basename(filename);
+ escaped = purple_escape_filename(newname);
+ g_free(newname);
+ newname = NULL;
+
+ if (g_strcmp0(escaped, "") == 0 || g_strcmp0(escaped, ".") == 0 ||
+ g_strcmp0(escaped, G_DIR_SEPARATOR_S) == 0 ||
+ g_strcmp0(escaped, "/") == 0 || g_strcmp0(escaped, "\\") == 0)
+ {
+ escaped = NULL;
+ }
+
+ g_free(priv->friendly_filename);
+ priv->friendly_filename = g_strdup(escaped);
+}
+
+const gchar *
+purple_image_get_friendly_filename(PurpleImage *image)
+{
+ PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ if (G_UNLIKELY(!priv->friendly_filename)) {
+ const gchar *newname = purple_image_generate_filename(image);
+ gsize newname_len = strlen(newname);
+
+ if (newname_len < 10)
+ return NULL;
+
+ /* let's use last 6 characters from checksum + 4 characters
+ * from file ext */
+ newname += newname_len - 10;
+ priv->friendly_filename = g_strdup(newname);
+ }
+
+ if (G_UNLIKELY(priv->is_ready &&
+ strchr(priv->friendly_filename, '.') == NULL))
+ {
+ const gchar *ext = purple_image_get_extension(image);
+ gchar *tmp;
+ if (!ext)
+ return priv->friendly_filename;
+
+ tmp = g_strdup_printf("%s.%s", priv->friendly_filename, ext);
+ g_free(priv->friendly_filename);
+ priv->friendly_filename = tmp;
+ }
+
+ return priv->friendly_filename;
+}
+
+PurpleImage *
+purple_image_transfer_new(void)
+{
+ PurpleImage *img;
+ PurpleImagePrivate *priv;
+
+ img = g_object_new(PURPLE_TYPE_IMAGE, NULL);
+ priv = PURPLE_IMAGE_GET_PRIVATE(img);
+
+ priv->is_ready = FALSE;
+ priv->contents = g_string_new(NULL);
+
+ return img;
+}
+
+void
+purple_image_transfer_write(PurpleImage *image, gconstpointer data,
+ gsize length)
+{
+ PurpleImagePrivate *priv =
+ PURPLE_IMAGE_GET_PRIVATE(image);
+
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(!priv->has_failed);
+ g_return_if_fail(!priv->is_ready);
+ g_return_if_fail(priv->contents != NULL);
+ g_return_if_fail(data != NULL || length == 0);
+
+ if (length == 0)
+ return;
+
+ g_string_append_len(priv->contents, (const gchar*)data, length);
+}
+
+void
+purple_image_transfer_close(PurpleImage *image)
+{
+ PurpleImagePrivate *priv =
+ PURPLE_IMAGE_GET_PRIVATE(image);
+
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(!priv->has_failed);
+ g_return_if_fail(!priv->is_ready);
+ g_return_if_fail(priv->contents != NULL);
+
+ if (priv->contents->len == 0) {
+ purple_debug_error("image", "image is empty");
+ has_failed(image);
+ return;
+ }
+
+ became_ready(image);
+}
+
+void
+purple_image_transfer_failed(PurpleImage *image)
+{
+ PurpleImagePrivate *priv =
+ PURPLE_IMAGE_GET_PRIVATE(image);
+
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(!priv->has_failed);
+ g_return_if_fail(!priv->is_ready);
+
+ has_failed(image);
+}
+
+/******************************************************************************
+ * Object stuff
+ ******************************************************************************/
+
+static void
+purple_image_init(GTypeInstance *instance, gpointer klass)
+{
+ PurpleImage *image = PURPLE_IMAGE(instance);
+ PurpleImagePrivate *priv =
+ PURPLE_IMAGE_GET_PRIVATE(image);
+
+ priv->contents = NULL;
+ priv->is_ready = TRUE;
+ priv->has_failed = FALSE;
+}
+
+static void
+purple_image_finalize(GObject *obj)
+{
+ PurpleImage *image = PURPLE_IMAGE(obj);
+ PurpleImagePrivate *priv =
+ PURPLE_IMAGE_GET_PRIVATE(image);
+
+ if (priv->contents)
+ g_string_free(priv->contents, TRUE);
+ g_free(priv->path);
+ g_free(priv->gen_filename);
+ g_free(priv->friendly_filename);
+
+ G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+static void
+purple_image_get_property(GObject *object, guint par_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleImage *image = PURPLE_IMAGE(object);
+ PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
+
+ switch (par_id) {
+ case PROP_IS_READY:
+ g_value_set_boolean(value, priv->is_ready);
+ break;
+ case PROP_HAS_FAILED:
+ g_value_set_boolean(value, priv->has_failed);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, par_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_image_class_init(PurpleImageClass *klass)
+{
+ GObjectClass *gobj_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ g_type_class_add_private(klass, sizeof(PurpleImagePrivate));
+
+ gobj_class->finalize = purple_image_finalize;
+ gobj_class->get_property = purple_image_get_property;
+
+ properties[PROP_IS_READY] = g_param_spec_boolean("is-ready",
+ "Is ready", "The image is ready to be displayed. Image may "
+ "change the state to failed in a single case: if it's backed "
+ "by a file and that file fails to load",
+ TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_HAS_FAILED] = g_param_spec_boolean("has-failed",
+ "Has hailed", "The remote host has failed to send the image",
+ FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(gobj_class, PROP_LAST, properties);
+
+ /**
+ * PurpleImage::failed:
+ * @image: a image that failed to transfer.
+ *
+ * Called when a @image fails to be transferred. It's guaranteed to be
+ * fired at most once for a particular @image.
+ */
+ signals[SIG_FAILED] = g_signal_new("failed", G_OBJECT_CLASS_TYPE(klass),
+ 0, 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+ /**
+ * PurpleImage::ready:
+ * @image: a image that became ready.
+ *
+ * Called when a @image becames ready to be displayed. It's guaranteed to be
+ * fired at most once for a particular @image.
+ */
+ signals[SIG_READY] = g_signal_new("ready", G_OBJECT_CLASS_TYPE(klass),
+ 0, 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+}
+
+GType
+purple_image_get_type(void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY(type == 0)) {
+ static const GTypeInfo info = {
+ .class_size = sizeof(PurpleImageClass),
+ .class_init = (GClassInitFunc)purple_image_class_init,
+ .instance_size = sizeof(PurpleImage),
+ .instance_init = purple_image_init,
+ };
+
+ type = g_type_register_static(G_TYPE_OBJECT,
+ "PurpleImage", &info, 0);
+ }
+
+ return type;
+}
diff --git a/libpurple/image.h b/libpurple/image.h
new file mode 100644
index 0000000000..44fb595d82
--- /dev/null
+++ b/libpurple/image.h
@@ -0,0 +1,305 @@
+/* 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 _PURPLE_IMAGE_H_
+#define _PURPLE_IMAGE_H_
+/**
+ * SECTION:image
+ * @include:image.h
+ * @section_id: libpurple-image
+ * @short_description: implementation-independent image data container
+ * @title: Images
+ *
+ * #PurpleImage object is a container for raw image data. It doesn't manipulate
+ * image data, just stores it in its binary format - png, jpeg etc. Thus, it's
+ * totally independent from the UI.
+ *
+ * This class also provides certain file-related features, like: friendly
+ * filenames (not necessarily real filename for displaying); remote images
+ * (which data is not yet loaded) or guessing file format from its header.
+ */
+
+#include <glib-object.h>
+
+typedef struct _PurpleImage PurpleImage;
+typedef struct _PurpleImageClass PurpleImageClass;
+
+#define PURPLE_TYPE_IMAGE (purple_image_get_type())
+#define PURPLE_IMAGE(smiley) (G_TYPE_CHECK_INSTANCE_CAST((smiley), PURPLE_TYPE_IMAGE, PurpleImage))
+#define PURPLE_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_IMAGE, PurpleImageClass))
+#define PURPLE_IS_IMAGE(smiley) (G_TYPE_CHECK_INSTANCE_TYPE((smiley), PURPLE_TYPE_IMAGE))
+#define PURPLE_IS_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_IMAGE))
+#define PURPLE_IMAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_IMAGE, PurpleImageClass))
+
+/**
+ * PurpleImage:
+ *
+ * An image data container.
+ */
+struct _PurpleImage
+{
+ /*< private >*/
+ GObject parent;
+};
+
+/**
+ * PurpleImageClass:
+ *
+ * Base class for #PurpleImage objects.
+ */
+struct _PurpleImageClass
+{
+ /*< private >*/
+ GObjectClass parent_class;
+
+ void (*purple_reserved1)(void);
+ void (*purple_reserved2)(void);
+ void (*purple_reserved3)(void);
+ void (*purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+/**
+ * purple_image_get_type:
+ *
+ * Returns: the #GType for an image.
+ */
+GType
+purple_image_get_type(void);
+
+/**
+ * purple_image_new_from_file:
+ * @path: the path to the image file.
+ * @be_eager: %TRUE, if file should be loaded now, %FALSE when needed.
+ *
+ * Loads an image file as a new #PurpleImage object. The @path must exists, be
+ * readable and should point to a valid image file. If you don't set @be_eager
+ * parameter, there will be a risk that file will be removed from disk before
+ * you access its data.
+ *
+ * Returns: the new #PurpleImage.
+ */
+PurpleImage *
+purple_image_new_from_file(const gchar *path, gboolean be_eager);
+
+/**
+ * purple_image_new_from_data:
+ * @data: the pointer to the image data buffer.
+ * @length: the length of @data.
+ *
+ * Creates a new #PurpleImage object with contents of @data buffer.
+ *
+ * The @data buffer is owned by #PurpleImage object, so you might want
+ * to #g_memdup it first.
+ *
+ * Returns: the new #PurpleImage.
+ */
+PurpleImage *
+purple_image_new_from_data(gpointer data, gsize length);
+
+/**
+ * purple_image_save:
+ * @image: the image.
+ * @path: destination of a saved image file.
+ *
+ * Saves an @image to the disk.
+ *
+ * Returns: %TRUE if succeeded, %FALSE otherwise.
+ */
+gboolean
+purple_image_save(PurpleImage *image, const gchar *path);
+
+/**
+ * purple_image_get_path:
+ * @image: the image.
+ *
+ * Returns the physical path of the @image file. It is set only, if the @image is
+ * really backed by an existing file. In the other case it returns %NULL.
+ *
+ * Returns: the physical path of the @image, or %NULL.
+ */
+const gchar *
+purple_image_get_path(PurpleImage *image);
+
+/**
+ * purple_image_is_ready:
+ * @image: the image.
+ *
+ * Checks, if the @image data is ready to be displayed. Remote image data may
+ * not be accessible at the moment, so you should check it before using
+ * #purple_image_get_data. If the image is not ready yet, you may wait for
+ * #PurpleImage::ready signal.
+ *
+ * Returns: %TRUE, if the @image is ready.
+ */
+gboolean
+purple_image_is_ready(PurpleImage *image);
+
+/**
+ * purple_image_has_failed:
+ * @image: the image.
+ *
+ * Checks, if the @image has failed to load its data. It can be caused either by
+ * a remote failure (and #purple_image_transfer_failed call) or local file being
+ * removed (see #purple_image_new_from_file).
+ *
+ * Returns: %TRUE, if the @image has failed to load.
+ */
+gboolean
+purple_image_has_failed(PurpleImage *image);
+
+/**
+ * purple_image_get_size:
+ * @image: the image.
+ *
+ * Returns the size of @image's data.
+ *
+ * Returns: the size of data, or 0 in case of failure.
+ */
+gsize
+purple_image_get_size(PurpleImage *image);
+
+/**
+ * purple_image_get_data:
+ * @image: the image.
+ *
+ * Returns the pointer to the buffer containing image data.
+ *
+ * Returns: (transfer none): the @image data.
+ */
+gconstpointer
+purple_image_get_data(PurpleImage *image);
+
+/**
+ * purple_image_get_extension:
+ * @image: the image.
+ *
+ * Guesses the @image format based on its contents.
+ *
+ * Returns: (transfer none): the file extension suitable for @image format.
+ */
+const gchar *
+purple_image_get_extension(PurpleImage *image);
+
+/**
+ * purple_image_get_mimetype:
+ * @image: the image.
+ *
+ * Guesses the @image mime-type based on its contents.
+ *
+ * Returns: (transfer none): the mime-type suitable for @image format.
+ */
+const gchar *
+purple_image_get_mimetype(PurpleImage *image);
+
+/**
+ * purple_image_generate_filename:
+ * @image: the image.
+ *
+ * Calculates almost-unique filename by computing checksum from file contents
+ * and appending a suitable extension. You should not assume the checksum
+ * is SHA-1, because it may change in the future.
+ *
+ * Returns: (transfer none): the generated file name.
+ */
+const gchar *
+purple_image_generate_filename(PurpleImage *image);
+
+/**
+ * purple_image_set_friendly_filename:
+ * @image: the image.
+ * @filename: the friendly filename.
+ *
+ * Sets the "friendly filename" for the @image. This don't have to be a real
+ * name, because it's used for displaying or as a default file name when the
+ * user wants to save the @image to the disk.
+ *
+ * The provided @filename may either be a full path, or contain
+ * filesystem-unfriendly characters, because it will be reformatted.
+ */
+void
+purple_image_set_friendly_filename(PurpleImage *image, const gchar *filename);
+
+/**
+ * purple_image_get_friendly_filename:
+ * @image: the image.
+ *
+ * Returns the "friendly filename" for the @image, to be displayed or used as
+ * a default name when saving a file to the disk.
+ * See #purple_image_set_friendly_filename.
+ *
+ * If the friendly filename was not set, it will be generated with
+ * #purple_image_generate_filename.
+ *
+ * Returns: (transfer none): the friendly filename.
+ */
+const gchar *
+purple_image_get_friendly_filename(PurpleImage *image);
+
+/**
+ * purple_image_transfer_new:
+ *
+ * Creates a new remote image, with a possibility to load data with
+ * #purple_image_transfer_write and #purple_image_transfer_close. In case of
+ * failure, you should call #urple_image_transfer_failed.
+ *
+ * Returns: the new image object.
+ */
+PurpleImage *
+purple_image_transfer_new(void);
+
+/**
+ * purple_image_transfer_write:
+ * @image: the image.
+ * @data: the incoming data buffer.
+ * @length: the length of @data.
+ *
+ * Adds a chunk of data to the internal receive buffer. Called when receiving
+ * a remote file.
+ */
+void
+purple_image_transfer_write(PurpleImage *image, gconstpointer data,
+ gsize length);
+
+/**
+ * purple_image_transfer_close:
+ * @image: the image.
+ *
+ * Marks a remote @image as ready to be displayed. Called when finishing
+ * transfer of remote file. You may call this only once for a certain @image.
+ */
+void
+purple_image_transfer_close(PurpleImage *image);
+
+/**
+ * purple_image_transfer_failed:
+ * @image: the image.
+ *
+ * Marks a remote @image as failed to transfer. Called on error in remote file
+ * transfer. You may call this only once for a certain @image.
+ */
+void
+purple_image_transfer_failed(PurpleImage *image);
+
+G_END_DECLS
+
+#endif /* _PURPLE_IMAGE_H_ */
diff --git a/libpurple/imgstore.c b/libpurple/imgstore.c
deleted file mode 100644
index 06c75b0e47..0000000000
--- a/libpurple/imgstore.c
+++ /dev/null
@@ -1,231 +0,0 @@
-/**
- * @file imgstore.c IM Image Store API
- * @ingroup core
- */
-
-/* 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 "dbus-maybe.h"
-#include "debug.h"
-#include "imgstore.h"
-#include "util.h"
-
-static GHashTable *imgstore;
-static unsigned int nextid = 0;
-
-/*
- * NOTE: purple_imgstore_add() creates these without zeroing the memory, so
- * NOTE: make sure to update that function when adding members.
- */
-struct _PurpleStoredImage
-{
- int id;
- guint8 refcount;
- size_t size; /**< The image data's size. */
- char *filename; /**< The filename (for the UI) */
- gpointer data; /**< The image data. */
-};
-
-PurpleStoredImage *
-purple_imgstore_add(gpointer data, size_t size, const char *filename)
-{
- PurpleStoredImage *img;
-
- g_return_val_if_fail(data != NULL, NULL);
- g_return_val_if_fail(size > 0, NULL);
-
- img = g_new(PurpleStoredImage, 1);
- PURPLE_DBUS_REGISTER_POINTER(img, PurpleStoredImage);
- img->data = data;
- img->size = size;
- img->filename = g_strdup(filename);
- img->refcount = 1;
- img->id = 0;
-
- return img;
-}
-
-PurpleStoredImage *
-purple_imgstore_new_from_file(const char *path)
-{
- gchar *data = NULL;
- size_t len;
- GError *err = NULL;
-
- g_return_val_if_fail(path != NULL && *path != '\0', NULL);
-
- if (!g_file_get_contents(path, &data, &len, &err)) {
- purple_debug_error("imgstore", "Error reading %s: %s\n",
- path, err->message);
- g_error_free(err);
- return NULL;
- }
- return purple_imgstore_add(data, len, path);
-}
-
-int
-purple_imgstore_add_with_id(gpointer data, size_t size, const char *filename)
-{
- PurpleStoredImage *img = purple_imgstore_add(data, size, filename);
- if (!img) {
- return 0;
- }
-
- /*
- * Use the next unused id number. We do it in a loop on the
- * off chance that nextid wraps back around to 0 and the hash
- * table still contains entries from the first time around.
- */
- do {
- img->id = ++nextid;
- } while (img->id == 0 || g_hash_table_lookup(imgstore, &(img->id)) != NULL);
-
- g_hash_table_insert(imgstore, &(img->id), img);
-
- return img->id;
-}
-
-PurpleStoredImage *purple_imgstore_find_by_id(int id)
-{
- PurpleStoredImage *img = g_hash_table_lookup(imgstore, &id);
-
- if (img != NULL)
- purple_debug_misc("imgstore", "retrieved image id %d\n", img->id);
-
- return img;
-}
-
-gconstpointer purple_imgstore_get_data(PurpleStoredImage *img)
-{
- g_return_val_if_fail(img != NULL, NULL);
-
- return img->data;
-}
-
-size_t purple_imgstore_get_size(PurpleStoredImage *img)
-{
- g_return_val_if_fail(img != NULL, 0);
-
- return img->size;
-}
-
-const char *purple_imgstore_get_filename(const PurpleStoredImage *img)
-{
- g_return_val_if_fail(img != NULL, NULL);
-
- return img->filename;
-}
-
-const char *purple_imgstore_get_extension(PurpleStoredImage *img)
-{
- g_return_val_if_fail(img != NULL, NULL);
-
- return purple_util_get_image_extension(img->data, img->size);
-}
-
-void purple_imgstore_ref_by_id(int id)
-{
- PurpleStoredImage *img = purple_imgstore_find_by_id(id);
-
- g_return_if_fail(img != NULL);
-
- purple_imgstore_ref(img);
-}
-
-void purple_imgstore_unref_by_id(int id)
-{
- PurpleStoredImage *img = purple_imgstore_find_by_id(id);
-
- g_return_if_fail(img != NULL);
-
- purple_imgstore_unref(img);
-}
-
-PurpleStoredImage *
-purple_imgstore_ref(PurpleStoredImage *img)
-{
- g_return_val_if_fail(img != NULL, NULL);
-
- img->refcount++;
-
- return img;
-}
-
-PurpleStoredImage *
-purple_imgstore_unref(PurpleStoredImage *img)
-{
- if (img == NULL)
- return NULL;
-
- g_return_val_if_fail(img->refcount > 0, NULL);
-
- img->refcount--;
-
- if (img->refcount == 0)
- {
- purple_signal_emit(purple_imgstore_get_handle(),
- "image-deleting", img);
- if (img->id)
- g_hash_table_remove(imgstore, &img->id);
-
- g_free(img->data);
- g_free(img->filename);
- PURPLE_DBUS_UNREGISTER_POINTER(img);
- g_free(img);
- img = NULL;
- }
-
- return img;
-}
-
-void *
-purple_imgstore_get_handle()
-{
- static int handle;
-
- return &handle;
-}
-
-void
-purple_imgstore_init()
-{
- void *handle = purple_imgstore_get_handle();
-
- purple_signal_register(handle, "image-deleting",
- purple_marshal_VOID__POINTER, NULL,
- 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_STORED_IMAGE));
-
- imgstore = g_hash_table_new(g_int_hash, g_int_equal);
-}
-
-void
-purple_imgstore_uninit()
-{
- g_hash_table_destroy(imgstore);
-
- purple_signals_unregister_by_instance(purple_imgstore_get_handle());
-}
diff --git a/libpurple/imgstore.h b/libpurple/imgstore.h
deleted file mode 100644
index 17e15a1645..0000000000
--- a/libpurple/imgstore.h
+++ /dev/null
@@ -1,230 +0,0 @@
-/**
- * @file imgstore.h IM Image Store API
- * @ingroup core
- * @see @ref imgstore-signals
- */
-
-/* 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 _PURPLE_IMGSTORE_H_
-#define _PURPLE_IMGSTORE_H_
-
-#include <glib.h>
-
-/**
- * A set of utility functions that provide a reference-counted immutable
- * wrapper around an image's data and filename. These functions do not
- * cache any data to disk.
- */
-typedef struct _PurpleStoredImage PurpleStoredImage;
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Create a new PurpleStoredImage.
- *
- * Despite the name of this function, the image is NOT added to the image
- * store and no ID is assigned. If you need to reference the image by an
- * ID, use purple_imgstore_add_with_id() instead.
- *
- * The caller owns a reference to this image and must dereference it with
- * purple_imgstore_unref() for it to be freed.
- *
- * @param data Pointer to the image data, which the imgstore will take
- * ownership of and free as appropriate. If you want a
- * copy of the data, make it before calling this function.
- * @param size Image data's size.
- * @param filename Filename associated with image. This is for your
- * convenience. It could be the full path to the
- * image or, more commonly, the filename of the image
- * without any directory information. It can also be
- * NULL, if you don't need to keep track of a filename.
- *
- * @return The stored image, or NULL if the image was not added (because of
- * empty data or size).
- */
-PurpleStoredImage *
-purple_imgstore_add(gpointer data, size_t size, const char *filename);
-
-/**
- * Create a PurpleStoredImage using purple_imgstore_add() by reading the
- * given filename from disk.
- *
- * The image is not added to the image store and no ID is assigned. If you
- * need to reference the image by an ID, use purple_imgstore_add_with_id()
- * instead.
- *
- * The caller owns a reference to this image and must dereference it with
- * purple_imgstore_unref() for it to be freed.
- *
- * @param path The path to the image.
- *
- * @return The stored image, or NULL if the image was not added (because of
- * empty data or size).
- *
- * @since 2.5.0
- */
-PurpleStoredImage *
-purple_imgstore_new_from_file(const char *path);
-
-/**
- * Create a PurpleStoredImage using purple_imgstore_add() and add the
- * image to the image store. A unique ID will be assigned to the image.
- *
- * The caller owns a reference to the image and must dereference it with
- * purple_imgstore_unref() or purple_imgstore_unref_by_id() for it to be
- * freed.
- *
- * @param data Pointer to the image data, which the imgstore will take
- * ownership of and free as appropriate. If you want a
- * copy of the data, make it before calling this function.
- * @param size Image data's size.
- * @param filename Filename associated with image. This is for your
- * convenience. It could be the full path to the
- * image or, more commonly, the filename of the image
- * without any directory information. It can also be
- * NULL, if you don't need to keep track of a filename.
- *
- * @return ID for the image. This is a unique number that can be used
- * within libpurple to reference the image. 0 is returned if the
- * image was not added (because of empty data or size).
- */
-int purple_imgstore_add_with_id(gpointer data, size_t size, const char *filename);
-
-/**
- * Retrieve an image from the store. The caller does not own a
- * reference to the image.
- *
- * @param id The ID for the image.
- *
- * @return A pointer to the requested image, or NULL if it was not found.
- */
-PurpleStoredImage *purple_imgstore_find_by_id(int id);
-
-/**
- * Retrieves a pointer to the image's data.
- *
- * @param img The Image.
- *
- * @return A pointer to the data, which must not
- * be freed or modified.
- */
-gconstpointer purple_imgstore_get_data(PurpleStoredImage *img);
-
-/**
- * Retrieves the length of the image's data.
- *
- * @param img The Image.
- *
- * @return The size of the data that the pointer returned by
- * purple_imgstore_get_data points to.
- */
-size_t purple_imgstore_get_size(PurpleStoredImage *img);
-
-/**
- * Retrieves a pointer to the image's filename.
- *
- * @param img The image.
- *
- * @return A pointer to the filename, which must not
- * be freed or modified.
- */
-const char *purple_imgstore_get_filename(const PurpleStoredImage *img);
-
-/**
- * Looks at the magic numbers of the image data (the first few bytes)
- * and returns an extension corresponding to the image's file type.
- *
- * @param img The image.
- *
- * @return The image's extension (for example "png") or "icon"
- * if unknown.
- */
-const char *purple_imgstore_get_extension(PurpleStoredImage *img);
-
-/**
- * Increment the reference count.
- *
- * @param img The image.
- *
- * @return @a img
- */
-PurpleStoredImage *
-purple_imgstore_ref(PurpleStoredImage *img);
-
-/**
- * Decrement the reference count.
- *
- * If the reference count reaches zero, the image will be freed.
- *
- * @param img The image.
- *
- * @return @a img or @c NULL if the reference count reached zero.
- */
-PurpleStoredImage *
-purple_imgstore_unref(PurpleStoredImage *img);
-
-/**
- * Increment the reference count using an ID.
- *
- * This is a convience wrapper for purple_imgstore_find_by_id() and
- * purple_imgstore_ref(), so if you have a PurpleStoredImage, it'll
- * be more efficient to call purple_imgstore_ref() directly.
- *
- * @param id The ID for the image.
- */
-void purple_imgstore_ref_by_id(int id);
-
-/**
- * Decrement the reference count using an ID.
- *
- * This is a convience wrapper for purple_imgstore_find_by_id() and
- * purple_imgstore_unref(), so if you have a PurpleStoredImage, it'll
- * be more efficient to call purple_imgstore_unref() directly.
- *
- * @param id The ID for the image.
- */
-void purple_imgstore_unref_by_id(int id);
-
-/**
- * Returns the image store subsystem handle.
- *
- * @return The subsystem handle.
- */
-void *purple_imgstore_get_handle(void);
-
-/**
- * Initializes the image store subsystem.
- */
-void purple_imgstore_init(void);
-
-/**
- * Uninitializes the image store subsystem.
- */
-void purple_imgstore_uninit(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _PURPLE_IMGSTORE_H_ */
diff --git a/libpurple/internal.h b/libpurple/internal.h
index c93b94d1e6..4651d851c4 100644
--- a/libpurple/internal.h
+++ b/libpurple/internal.h
@@ -1,8 +1,3 @@
-/**
- * @file internal.h Internal definitions and includes
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -23,8 +18,15 @@
* 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_INTERNAL_H_
#define _PURPLE_INTERNAL_H_
+/*
+ * SECTION:internal
+ * @section_id: libpurple-internal
+ * @short_description: <filename>internal.h</filename>
+ * @title: Internal definitions and includes
+ */
#ifdef HAVE_CONFIG_H
# include <config.h>
@@ -51,6 +53,7 @@
# define __APPLE_CC__ 0
# endif
# include <libintl.h>
+# undef printf
# define _(String) ((const char *)dgettext(PACKAGE, String))
# ifdef gettext_noop
# define N_(String) gettext_noop (String)
@@ -78,7 +81,6 @@
#define BUF_LEN MSG_LEN
#define BUF_LONG BUF_LEN * 2
-#include <sys/stat.h>
#include <sys/types.h>
#ifndef _WIN32
#include <sys/time.h>
@@ -126,20 +128,10 @@
#endif
#include <glib.h>
-
-/* This wasn't introduced until Glib 2.14 :( */
-#ifndef G_MAXSSIZE
-# if GLIB_SIZEOF_LONG == 8
-# define G_MAXSSIZE ((gssize) 0x7fffffffffffffff)
-# else
-# define G_MAXSSIZE ((gssize) 0x7fffffff)
-# endif
-#endif
-
#include <glib/gstdio.h>
#ifdef _WIN32
-#include "win32dep.h"
+#include "win32/win32dep.h"
#endif
#ifdef HAVE_CONFIG_H
@@ -152,28 +144,34 @@
#endif
#endif
-#include <glib-object.h>
-
-#if !GLIB_CHECK_VERSION(2, 32, 0)
-
-#define G_GNUC_BEGIN_IGNORE_DEPRECATIONS
-#define G_GNUC_END_IGNORE_DEPRECATIONS
+#define PURPLE_STATIC_ASSERT(condition, message) \
+ { typedef char static_assertion_failed_ ## message \
+ [(condition) ? 1 : -1]; static_assertion_failed_ ## message dummy; \
+ (void)dummy; }
-#endif /* 2.32.0 */
+/* This is meant to track use-after-free errors.
+ * TODO: it should be disabled in released code. */
+#define PURPLE_ASSERT_CONNECTION_IS_VALID(gc) \
+ _purple_assert_connection_is_valid(gc, __FILE__, __LINE__)
#ifdef __clang__
-#undef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
-#define G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+#define PURPLE_BEGIN_IGNORE_CAST_ALIGN \
_Pragma ("clang diagnostic push") \
- _Pragma ("clang diagnostic ignored \"-Wdeprecated-declarations\"")
+ _Pragma ("clang diagnostic ignored \"-Wcast-align\"")
-#undef G_GNUC_END_IGNORE_DEPRECATIONS
-#define G_GNUC_END_IGNORE_DEPRECATIONS \
+#define PURPLE_END_IGNORE_CAST_ALIGN \
_Pragma ("clang diagnostic pop")
+#else
+
+#define PURPLE_BEGIN_IGNORE_CAST_ALIGN
+#define PURPLE_END_IGNORE_CAST_ALIGN
+
#endif /* __clang__ */
+#include <glib-object.h>
+
#ifdef __COVERITY__
/* avoid TAINTED_SCALAR warning */
@@ -182,12 +180,6 @@
#endif
-/* Safer ways to work with static buffers. When using non-static
- * buffers, either use g_strdup_* functions (preferred) or use
- * g_strlcpy/g_strlcpy directly. */
-#define purple_strlcpy(dest, src) g_strlcpy(dest, src, sizeof(dest))
-#define purple_strlcat(dest, src) g_strlcat(dest, src, sizeof(dest))
-
typedef union
{
struct sockaddr sa;
@@ -196,15 +188,46 @@ typedef union
struct sockaddr_storage storage;
} common_sockaddr_t;
-#define PURPLE_WEBSITE "http://pidgin.im/"
-#define PURPLE_DEVEL_WEBSITE "http://developer.pidgin.im/"
+#define PURPLE_WEBSITE "https://pidgin.im/"
+#define PURPLE_DEVEL_WEBSITE "https://developer.pidgin.im/"
/* INTERNAL FUNCTIONS */
-#include "account.h"
+#include "accounts.h"
#include "connection.h"
+/**
+ * _purple_account_set_current_error:
+ * @account: The account to set the error for.
+ * @new_err: The #PurpleConnectionErrorInfo instance representing the
+ * error.
+ *
+ * Sets an error for an account.
+ */
+void _purple_account_set_current_error(PurpleAccount *account,
+ PurpleConnectionErrorInfo *new_err);
+
+/**
+ * _purple_account_to_xmlnode:
+ * @account: The account
+ *
+ * Get an XML description of an account.
+ *
+ * Returns: The XML description of the account.
+ */
+PurpleXmlNode *_purple_account_to_xmlnode(PurpleAccount *account);
+
+/**
+ * _purple_blist_get_last_child:
+ * @node: The node whose last child is to be retrieved.
+ *
+ * Returns the last child of a particular node.
+ *
+ * Returns: The last child of the node.
+ */
+PurpleBlistNode *_purple_blist_get_last_child(PurpleBlistNode *node);
+
/* This is for the accounts code to notify the buddy icon code that
* it's done loading. We may want to replace this with a signal. */
void
@@ -215,13 +238,13 @@ _purple_buddy_icons_account_loaded_cb(void);
void
_purple_buddy_icons_blist_loaded_cb(void);
-/* This is for the purple_core_migrate() code to tell the buddy
- * icon subsystem about the old icons directory so it can
- * migrate any icons in use. */
-void
-_purple_buddy_icon_set_old_icons_dir(const char *dirname);
-
/**
+ * _purple_connection_new:
+ * @account: The account the connection should be connecting to.
+ * @regist: Whether we are registering a new account or just
+ * trying to do a normal signon.
+ * @password: The password to use.
+ *
* Creates a connection to the specified account and either connects
* or attempts to register a new account. If you are logging in,
* the connection uses the current active status for this account.
@@ -229,41 +252,102 @@ _purple_buddy_icon_set_old_icons_dir(const char *dirname);
* have called purple_account_set_status(account, "away").
* (And this will call purple_account_connect() automatically).
*
- * @note This function should only be called by purple_account_connect()
+ * Note: This function should only be called by purple_account_connect()
* in account.c. If you're trying to sign on an account, use that
* function instead.
- *
- * @param account The account the connection should be connecting to.
- * @param regist Whether we are registering a new account or just
- * trying to do a normal signon.
- * @param password The password to use.
*/
void _purple_connection_new(PurpleAccount *account, gboolean regist,
const char *password);
/**
+ * _purple_connection_new_unregister:
+ * @account: The account to unregister
+ * @password: The password to use.
+ * @cb: Optional callback to be called when unregistration is complete
+ * @user_data: user data to pass to the callback
+ *
* Tries to unregister the account on the server. If the account is not
* connected, also creates a new connection.
*
- * @note This function should only be called by purple_account_unregister()
+ * Note: This function should only be called by purple_account_unregister()
* in account.c.
- *
- * @param account The account to unregister
- * @param password The password to use.
- * @param cb Optional callback to be called when unregistration is complete
- * @param user_data user data to pass to the callback
*/
void _purple_connection_new_unregister(PurpleAccount *account, const char *password,
PurpleAccountUnregistrationCb cb, void *user_data);
/**
- * Disconnects and destroys a PurpleConnection.
+ * _purple_connection_wants_to_die:
+ * @gc: The connection to check
+ *
+ * Checks if a connection is disconnecting, and should not attempt to reconnect.
+ *
+ * Note: This function should only be called by purple_account_set_enabled()
+ * in account.c.
+ */
+gboolean _purple_connection_wants_to_die(const PurpleConnection *gc);
+
+/**
+ * _purple_connection_add_active_chat:
+ * @gc: The connection
+ * @chat: The chat conversation to add
+ *
+ * Adds a chat to the active chats list of a connection
+ *
+ * Note: This function should only be called by purple_serv_got_joined_chat()
+ * in server.c.
+ */
+void _purple_connection_add_active_chat(PurpleConnection *gc,
+ PurpleChatConversation *chat);
+/**
+ * _purple_connection_remove_active_chat:
+ * @gc: The connection
+ * @chat: The chat conversation to remove
+ *
+ * Removes a chat from the active chats list of a connection
+ *
+ * Note: This function should only be called by purple_serv_got_chat_left()
+ * in server.c.
+ */
+void _purple_connection_remove_active_chat(PurpleConnection *gc,
+ PurpleChatConversation *chat);
+
+/**
+ * _purple_conversations_update_cache:
+ * @conv: The conversation.
+ * @name: The new name. If no change, use %NULL.
+ * @account: The new account. If no change, use %NULL.
+ *
+ * Updates the conversation cache to use a new conversation name and/or
+ * account. This function only updates the conversation cache. It is the
+ * caller's responsibility to actually update the conversation.
+ *
+ * Note: This function should only be called by purple_conversation_set_name()
+ * and purple_conversation_set_account() in conversation.c.
+ */
+void _purple_conversations_update_cache(PurpleConversation *conv,
+ const char *name, PurpleAccount *account);
+
+/**
+ * _purple_statuses_get_primitive_scores:
+ *
+ * Note: This function should only be called by
+ * purple_buddy_presence_compute_score() in presence.c.
+ *
+ * Returns: The primitive scores array from status.c.
+ */
+int *_purple_statuses_get_primitive_scores(void);
+
+/**
+ * _purple_blist_get_localized_default_group_name:
*
- * @note This function should only be called by purple_account_disconnect()
- * in account.c. If you're trying to sign off an account, use that
- * function instead.
+ * Returns the name of default group for previously used non-English
+ * localization. It's used for merging default group, in case when roster
+ * contains localized name.
*
- * @param gc The purple connection to destroy.
+ * Please note, prpls shouldn't save default group name depending on current
+ * locale. So, this function is mostly for libpurple2 compatibility. And for
+ * improperly written prpls.
*/
-void _purple_connection_destroy(PurpleConnection *gc);
+const gchar *
+_purple_blist_get_localized_default_group_name(void);
/**
* Sets most commonly used socket flags: O_NONBLOCK and FD_CLOEXEC.
@@ -275,4 +359,78 @@ void _purple_connection_destroy(PurpleConnection *gc);
gboolean
_purple_network_set_common_socket_flags(int fd);
+/**
+ * A fstat alternative, like g_stat for stat.
+ *
+ * @param fd The file descriptor.
+ * @param st The stat buffer.
+ *
+ * @return the result just like for fstat.
+ */
+int
+_purple_fstat(int fd, GStatBuf *st);
+
+/**
+ * _purple_socket_cancel_with_connection:
+ * @gc The connection.
+ *
+ * Cancels all #PurpleSocket instances bound with @gc.
+ */
+void
+_purple_socket_cancel_with_connection(PurpleConnection *gc);
+
+/**
+ * _purple_socket_init: (skip)
+ *
+ * Initializes the #PurpleSocket subsystem.
+ */
+void
+_purple_socket_init(void);
+
+/**
+ * _purple_socket_uninit: (skip)
+ *
+ * Uninitializes the #PurpleSocket subsystem.
+ */
+void
+_purple_socket_uninit(void);
+
+/**
+ * _purple_message_init: (skip)
+ *
+ * Initializes the #PurpleMessage subsystem.
+ */
+void
+_purple_message_init(void);
+
+/**
+ * _purple_message_uninit: (skip)
+ *
+ * Uninitializes the #PurpleMessage subsystem.
+ */
+void
+_purple_message_uninit(void);
+
+void
+_purple_assert_connection_is_valid(PurpleConnection *gc,
+ const gchar *file, int line);
+
+/**
+ * _purple_conversation_write_common:
+ * @conv: The conversation.
+ * @msg: The message.
+ *
+ * Writes to a conversation window.
+ *
+ * This function should not be used to write IM or chat messages. Use
+ * purple_conversation_write_message() instead. This function will
+ * most likely call this anyway, but it may do it's own formatting,
+ * sound playback, etc. depending on whether the conversation is a chat or an
+ * IM.
+ *
+ * See purple_conversation_write_message().
+ */
+void
+_purple_conversation_write_common(PurpleConversation *conv, PurpleMessage *msg);
+
#endif /* _PURPLE_INTERNAL_H_ */
diff --git a/libpurple/keyring.c b/libpurple/keyring.c
new file mode 100644
index 0000000000..5a525e92f1
--- /dev/null
+++ b/libpurple/keyring.c
@@ -0,0 +1,1382 @@
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program ; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+ */
+
+#include <glib.h>
+#include <string.h>
+#include "account.h"
+#include "keyring.h"
+#include "signals.h"
+#include "core.h"
+#include "debug.h"
+#include "internal.h"
+#include "dbus-maybe.h"
+
+struct _PurpleKeyring
+{
+ gchar *name;
+ gchar *id;
+ PurpleKeyringRead read_password;
+ PurpleKeyringSave save_password;
+ PurpleKeyringCancelRequests cancel_requests;
+ PurpleKeyringClose close_keyring;
+ PurpleKeyringImportPassword import_password;
+ PurpleKeyringExportPassword export_password;
+ PurpleKeyringReadSettings read_settings;
+ PurpleKeyringApplySettings apply_settings;
+
+ gboolean is_closing;
+ gboolean is_cancelling;
+ gboolean close_after_cancel;
+};
+
+typedef struct
+{
+ GError *error;
+ PurpleKeyringSetInUseCallback cb;
+ gpointer cb_data;
+ PurpleKeyring *new_kr;
+ PurpleKeyring *old_kr;
+
+ /*
+ * We are done when finished is positive and read_outstanding is zero.
+ */
+ gboolean finished;
+ int read_outstanding;
+
+ gboolean abort;
+ gboolean force;
+ gboolean succeeded;
+} PurpleKeyringChangeTracker;
+
+typedef void (*PurpleKeyringDropCallback)(gpointer data);
+
+typedef struct
+{
+ PurpleKeyringDropCallback cb;
+ gpointer cb_data;
+
+ gboolean finished;
+ int drop_outstanding;
+} PurpleKeyringDropTracker;
+
+typedef struct
+{
+ PurpleKeyringSaveCallback cb;
+ gpointer cb_data;
+} PurpleKeyringSetPasswordData;
+
+typedef struct
+{
+ gchar *keyring_id;
+ gchar *mode;
+ gchar *data;
+} PurpleKeyringFailedImport;
+
+static void
+purple_keyring_change_tracker_free(PurpleKeyringChangeTracker *tracker)
+{
+ if (tracker->error)
+ g_error_free(tracker->error);
+ g_free(tracker);
+}
+
+static void
+purple_keyring_failed_import_free(PurpleKeyringFailedImport *import)
+{
+ g_return_if_fail(import != NULL);
+
+ g_free(import->keyring_id);
+ g_free(import->mode);
+ purple_str_wipe(import->data);
+ g_free(import);
+}
+
+static void
+purple_keyring_close(PurpleKeyring *keyring);
+
+static void
+purple_keyring_drop_passwords(PurpleKeyring *keyring,
+ PurpleKeyringDropCallback cb, gpointer data);
+
+/* A list of available keyrings */
+static GList *purple_keyring_keyrings = NULL;
+
+/* Keyring being used. */
+static PurpleKeyring *purple_keyring_inuse = NULL;
+
+/* Keyring id marked to use (may not be loadable). */
+static gchar *purple_keyring_to_use = NULL;
+
+static guint purple_keyring_pref_cbid = 0;
+static GList *purple_keyring_loaded_plugins = NULL;
+static PurpleKeyringChangeTracker *current_change_tracker = NULL;
+static gboolean purple_keyring_is_quitting = FALSE;
+static GHashTable *purple_keyring_failed_imports = NULL;
+
+static const gchar *
+purple_keyring_print_account(PurpleAccount *account)
+{
+ static gchar print_buff[100];
+
+ if (account == NULL) {
+ g_snprintf(print_buff, 100, "(null)");
+ return print_buff;
+ }
+
+ g_snprintf(print_buff, 100, "%s:%s",
+ purple_account_get_protocol_id(account),
+ purple_account_get_username(account));
+ return print_buff;
+}
+
+/**************************************************************************/
+/* Setting used keyrings */
+/**************************************************************************/
+
+PurpleKeyring *
+purple_keyring_find_keyring_by_id(const gchar *id)
+{
+ GList *it;
+
+ for (it = purple_keyring_keyrings; it != NULL; it = it->next) {
+ PurpleKeyring *keyring = it->data;
+ const gchar *curr_id = purple_keyring_get_id(keyring);
+
+ if (g_strcmp0(id, curr_id) == 0)
+ return keyring;
+ }
+
+ return NULL;
+}
+
+static void
+purple_keyring_pref_callback(const gchar *pref, PurplePrefType type,
+ gconstpointer id, gpointer data)
+{
+ PurpleKeyring *new_keyring;
+
+ g_return_if_fail(g_strcmp0(pref, "/purple/keyring/active") == 0);
+ g_return_if_fail(type == PURPLE_PREF_STRING);
+ g_return_if_fail(id != NULL);
+
+ new_keyring = purple_keyring_find_keyring_by_id(id);
+ g_return_if_fail(new_keyring != NULL);
+
+ purple_keyring_set_inuse(new_keyring, FALSE, NULL, NULL);
+}
+
+static void
+purple_keyring_pref_connect(void)
+{
+ g_return_if_fail(purple_keyring_pref_cbid == 0);
+
+ purple_keyring_pref_cbid = purple_prefs_connect_callback(NULL,
+ "/purple/keyring/active", purple_keyring_pref_callback, NULL);
+}
+
+static void
+purple_keyring_pref_disconnect(void)
+{
+ g_return_if_fail(purple_keyring_pref_cbid != 0);
+
+ purple_prefs_disconnect_callback(purple_keyring_pref_cbid);
+ purple_keyring_pref_cbid = 0;
+}
+
+PurpleKeyring *
+purple_keyring_get_inuse(void)
+{
+ return purple_keyring_inuse;
+}
+
+static void
+purple_keyring_set_inuse_drop_cb(gpointer _tracker)
+{
+ PurpleKeyringChangeTracker *tracker = _tracker;
+
+ g_return_if_fail(tracker != NULL);
+
+ if (tracker->succeeded) {
+ purple_keyring_close(tracker->old_kr);
+
+ purple_debug_info("keyring", "Successfully changed keyring.\n");
+
+ purple_keyring_inuse = tracker->new_kr;
+ current_change_tracker = NULL;
+
+ if (tracker->cb != NULL)
+ tracker->cb(NULL, tracker->cb_data);
+ purple_keyring_change_tracker_free(tracker);
+ return;
+ }
+
+ purple_debug_error("keyring", "Failed to change keyring, aborting.\n");
+
+ purple_keyring_close(tracker->new_kr);
+
+ purple_keyring_pref_disconnect();
+ purple_prefs_set_string("/purple/keyring/active",
+ purple_keyring_get_id(tracker->old_kr));
+ purple_keyring_pref_connect();
+
+ 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."));
+ }
+
+ if (tracker->cb != NULL)
+ tracker->cb(tracker->error, tracker->cb_data);
+
+ purple_keyring_change_tracker_free(tracker);
+}
+
+static void
+purple_keyring_set_inuse_save_cb(PurpleAccount *account, GError *error,
+ gpointer _tracker)
+{
+ PurpleKeyringChangeTracker *tracker = _tracker;
+
+ g_return_if_fail(account != NULL);
+ g_return_if_fail(tracker != NULL);
+
+ tracker->read_outstanding--;
+
+ if (error == NULL) {
+ /* no error */
+ } else if (g_error_matches(error, PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_NOPASSWORD))
+ {
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("keyring", "No password found while "
+ "changing keyring for account %s: %s.\n",
+ purple_keyring_print_account(account),
+ error->message);
+ }
+ } else if (g_error_matches(error, PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_ACCESSDENIED))
+ {
+ purple_debug_info("keyring", "Access denied while changing "
+ "keyring for account %s: %s.\n",
+ purple_keyring_print_account(account), error->message);
+ tracker->abort = TRUE;
+ if (tracker->error != NULL)
+ g_error_free(tracker->error);
+ tracker->error = g_error_copy(error);
+ } else if (g_error_matches(error, PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_CANCELLED))
+ {
+ purple_debug_info("keyring", "Operation cancelled while "
+ "changing keyring for account %s: %s.\n",
+ purple_keyring_print_account(account), error->message);
+ tracker->abort = TRUE;
+ if (tracker->error == NULL)
+ tracker->error = g_error_copy(error);
+ } else if (g_error_matches(error, PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_BACKENDFAIL))
+ {
+ purple_debug_error("keyring", "Failed to communicate with "
+ "backend while changing keyring for account %s: %s. "
+ "Aborting changes.\n",
+ purple_keyring_print_account(account), error->message);
+ tracker->abort = TRUE;
+ if (tracker->error != NULL)
+ g_error_free(tracker->error);
+ tracker->error = g_error_copy(error);
+ } else {
+ purple_debug_error("keyring", "Unknown error while changing "
+ "keyring for account %s: %s. Aborting changes.\n",
+ purple_keyring_print_account(account), error->message);
+ tracker->abort = TRUE;
+ if (tracker->error == NULL)
+ tracker->error = g_error_copy(error);
+ }
+
+ purple_signal_emit(purple_keyring_get_handle(), "password-migration",
+ account);
+
+ if (!tracker->finished || tracker->read_outstanding > 0)
+ return;
+
+ /* This was the last one. */
+ if (tracker->abort && !tracker->force) {
+ tracker->succeeded = FALSE;
+ purple_keyring_drop_passwords(tracker->new_kr,
+ purple_keyring_set_inuse_drop_cb, tracker);
+ } else {
+ tracker->succeeded = TRUE;
+ purple_keyring_drop_passwords(tracker->old_kr,
+ purple_keyring_set_inuse_drop_cb, tracker);
+ }
+}
+
+static void
+purple_keyring_set_inuse_read_cb(PurpleAccount *account, const gchar *password,
+ GError *error, gpointer _tracker)
+{
+ PurpleKeyringChangeTracker *tracker = _tracker;
+ PurpleKeyringSave save_cb;
+
+ g_return_if_fail(account != NULL);
+ g_return_if_fail(tracker != NULL);
+
+ if (tracker->abort) {
+ purple_keyring_set_inuse_save_cb(account, NULL, tracker);
+ return;
+ }
+
+ if (error != NULL) {
+ if (tracker->force == TRUE || g_error_matches(error,
+ PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_NOPASSWORD))
+ {
+ /* Don't save password, and ignore it. */
+ } else {
+ tracker->abort = TRUE;
+ }
+ purple_keyring_set_inuse_save_cb(account, error, tracker);
+ return;
+ }
+
+ save_cb = purple_keyring_get_save_password(tracker->new_kr);
+ g_assert(save_cb != NULL);
+
+ save_cb(account, password, purple_keyring_set_inuse_save_cb, tracker);
+}
+
+void
+purple_keyring_set_inuse(PurpleKeyring *newkeyring, gboolean force,
+ PurpleKeyringSetInUseCallback cb, gpointer data)
+{
+ PurpleKeyring *oldkeyring;
+ PurpleKeyringChangeTracker *tracker;
+ GList *it;
+ PurpleKeyringRead read_cb;
+
+ if (current_change_tracker != NULL) {
+ GError *error;
+ purple_debug_error("keyring", "There is password migration "
+ "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."));
+ cb(error, data);
+ g_error_free(error);
+ return;
+ }
+
+ oldkeyring = purple_keyring_get_inuse();
+
+ if (oldkeyring == newkeyring) {
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("keyring",
+ "Old and new keyring are the same: %s.\n",
+ (newkeyring != NULL) ?
+ newkeyring->id : "(null)");
+ }
+ if (cb != NULL)
+ cb(NULL, data);
+ return;
+ }
+
+ purple_debug_info("keyring", "Attempting to set new keyring: %s.\n",
+ (newkeyring != NULL) ? newkeyring->id : "(null)");
+
+ 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_keyring_inuse = newkeyring;
+ g_assert(current_change_tracker == NULL);
+ if (cb != NULL)
+ cb(NULL, data);
+ return;
+ }
+
+ /* Starting a migration. */
+
+ read_cb = purple_keyring_get_read_password(oldkeyring);
+ g_assert(read_cb != NULL);
+
+ purple_debug_misc("keyring", "Starting migration from: %s.\n",
+ oldkeyring->id);
+
+ tracker = g_new0(PurpleKeyringChangeTracker, 1);
+ current_change_tracker = tracker;
+
+ tracker->cb = cb;
+ tracker->cb_data = data;
+ tracker->new_kr = newkeyring;
+ tracker->old_kr = oldkeyring;
+ tracker->force = force;
+
+ for (it = purple_accounts_get_all(); it != NULL; it = it->next) {
+ if (tracker->abort) {
+ tracker->finished = TRUE;
+ break;
+ }
+ tracker->read_outstanding++;
+
+ if (it->next == NULL)
+ tracker->finished = TRUE;
+
+ read_cb(it->data, purple_keyring_set_inuse_read_cb, tracker);
+ }
+}
+
+void
+purple_keyring_register(PurpleKeyring *keyring)
+{
+ const gchar *keyring_id;
+
+ g_return_if_fail(keyring != NULL);
+
+ keyring_id = purple_keyring_get_id(keyring);
+
+ purple_debug_info("keyring", "Registering keyring: %s\n",
+ keyring_id ? keyring_id : "(null)");
+
+ if (purple_keyring_get_id(keyring) == NULL ||
+ purple_keyring_get_name(keyring) == NULL ||
+ purple_keyring_get_read_password(keyring) == NULL ||
+ purple_keyring_get_save_password(keyring) == NULL)
+ {
+ purple_debug_error("keyring", "Cannot register %s, some "
+ "required fields are missing.\n",
+ keyring_id ? keyring_id : "(null)");
+ return;
+ }
+
+ if (purple_keyring_find_keyring_by_id(keyring_id) != NULL) {
+ purple_debug_error("keyring",
+ "Keyring is already registered.\n");
+ return;
+ }
+
+ /* If this is the configured keyring, use it. */
+ if (purple_keyring_inuse == NULL &&
+ g_strcmp0(keyring_id, purple_keyring_to_use) == 0)
+ {
+ purple_debug_misc("keyring", "Keyring %s matches keyring to "
+ "use, using it.", keyring_id);
+ purple_keyring_set_inuse(keyring, TRUE, NULL, NULL);
+ }
+
+ PURPLE_DBUS_REGISTER_POINTER(keyring, PurpleKeyring);
+ purple_signal_emit(purple_keyring_get_handle(), "keyring-register",
+ keyring_id, keyring);
+ if (purple_debug_is_verbose()) {
+ purple_debug_info("keyring", "Registered keyring: %s.\n",
+ keyring_id);
+ }
+
+ purple_keyring_keyrings = g_list_prepend(purple_keyring_keyrings,
+ keyring);
+}
+
+void
+purple_keyring_unregister(PurpleKeyring *keyring)
+{
+ PurpleKeyring *inuse;
+ PurpleKeyring *fallback;
+ const gchar *keyring_id;
+
+ g_return_if_fail(keyring != NULL);
+
+ keyring_id = purple_keyring_get_id(keyring);
+
+ purple_debug_info("keyring", "Unregistering keyring: %s.\n",
+ keyring_id);
+
+ purple_signal_emit(purple_keyring_get_handle(), "keyring-unregister",
+ keyring_id, keyring);
+ PURPLE_DBUS_UNREGISTER_POINTER(keyring);
+
+ inuse = purple_keyring_get_inuse();
+ fallback = purple_keyring_find_keyring_by_id(PURPLE_DEFAULT_KEYRING);
+
+ if (inuse == keyring) {
+ if (inuse != fallback) {
+ purple_keyring_set_inuse(fallback, TRUE, NULL, NULL);
+ } else {
+ fallback = NULL;
+ purple_keyring_set_inuse(NULL, TRUE, NULL, NULL);
+ }
+ }
+
+ purple_keyring_keyrings = g_list_remove(purple_keyring_keyrings,
+ keyring);
+}
+
+GList *
+purple_keyring_get_options(void)
+{
+ GList *options = NULL;
+ GList *it;
+ static gchar currentDisabledName[40];
+
+ if (purple_keyring_get_inuse() == NULL && purple_keyring_to_use != NULL
+ && purple_keyring_to_use[0] != '\0')
+ {
+ g_snprintf(currentDisabledName, sizeof(currentDisabledName),
+ _("%s (disabled)"), purple_keyring_to_use);
+
+ options = g_list_append(options, currentDisabledName);
+ options = g_list_append(options, purple_keyring_to_use);
+ }
+
+ for (it = purple_keyring_keyrings; it != NULL; it = it->next) {
+ PurpleKeyring *keyring = it->data;
+
+ options = g_list_append(options,
+ (gpointer)purple_keyring_get_name(keyring));
+ options = g_list_append(options,
+ (gpointer)purple_keyring_get_id(keyring));
+ }
+
+ return options;
+}
+
+
+/**************************************************************************/
+/* Keyring plugin wrappers */
+/**************************************************************************/
+
+static void
+purple_keyring_close(PurpleKeyring *keyring)
+{
+ PurpleKeyringClose close_cb;
+
+ g_return_if_fail(keyring != NULL);
+
+ if (keyring->is_cancelling) {
+ keyring->close_after_cancel = TRUE;
+ return;
+ }
+ if (keyring->is_closing)
+ return;
+ keyring->is_closing = TRUE;
+
+ close_cb = purple_keyring_get_close_keyring(keyring);
+
+ if (close_cb != NULL)
+ close_cb();
+
+ keyring->is_closing = FALSE;
+}
+
+static void
+purple_keyring_cancel_requests(PurpleKeyring *keyring)
+{
+ PurpleKeyringCancelRequests cancel_cb;
+
+ g_return_if_fail(keyring != NULL);
+
+ if (keyring->is_cancelling)
+ return;
+ keyring->is_cancelling = TRUE;
+
+ cancel_cb = purple_keyring_get_cancel_requests(keyring);
+
+ if (cancel_cb != NULL)
+ cancel_cb();
+
+ keyring->is_cancelling = FALSE;
+
+ if (keyring->close_after_cancel) {
+ keyring->close_after_cancel = FALSE;
+ purple_keyring_close(keyring);
+ }
+}
+
+static void
+purple_keyring_drop_passwords_save_cb(PurpleAccount *account, GError *error,
+ gpointer _tracker)
+{
+ PurpleKeyringDropTracker *tracker = _tracker;
+
+ tracker->drop_outstanding--;
+
+ if (!tracker->finished || tracker->drop_outstanding > 0)
+ return;
+
+ if (tracker->cb)
+ tracker->cb(tracker->cb_data);
+ g_free(tracker);
+}
+
+static void
+purple_keyring_drop_passwords(PurpleKeyring *keyring,
+ PurpleKeyringDropCallback cb, gpointer data)
+{
+ GList *it;
+ PurpleKeyringSave save_cb;
+ PurpleKeyringDropTracker *tracker;
+
+ g_return_if_fail(keyring != NULL);
+
+ save_cb = purple_keyring_get_save_password(keyring);
+ g_assert(save_cb != NULL);
+
+ tracker = g_new0(PurpleKeyringDropTracker, 1);
+ tracker->cb = cb;
+ tracker->cb_data = data;
+
+ for (it = purple_accounts_get_all(); it != NULL; it = it->next) {
+ PurpleAccount *account = it->data;
+
+ tracker->drop_outstanding++;
+ if (it->next == NULL)
+ tracker->finished = TRUE;
+
+ save_cb(account, NULL, purple_keyring_drop_passwords_save_cb,
+ tracker);
+ }
+}
+
+gboolean
+purple_keyring_import_password(PurpleAccount *account, const gchar *keyring_id,
+ const gchar *mode, const gchar *data, GError **error)
+{
+ PurpleKeyring *keyring;
+ PurpleKeyring *inuse;
+ PurpleKeyringImportPassword import;
+
+ g_return_val_if_fail(account != NULL, FALSE);
+
+ if (keyring_id == NULL)
+ keyring_id = PURPLE_DEFAULT_KEYRING;
+
+ purple_debug_misc("keyring", "Importing password for account %s to "
+ "keyring %s.\n", purple_keyring_print_account(account),
+ keyring_id);
+
+ 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."));
+ }
+ purple_debug_warning("Keyring", "Specified keyring is not "
+ "registered, cannot import password info for account "
+ "%s.\n", purple_keyring_print_account(account));
+ return FALSE;
+ }
+
+ 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."));
+ }
+ purple_debug_warning("Keyring",
+ "No keyring loaded, cannot import password info for "
+ "account %s.\n", purple_keyring_print_account(account));
+
+ import = g_new0(PurpleKeyringFailedImport, 1);
+ import->keyring_id = g_strdup(keyring_id);
+ import->mode = g_strdup(mode);
+ import->data = g_strdup(data);
+ g_hash_table_insert(purple_keyring_failed_imports, account,
+ import);
+ return FALSE;
+ }
+
+ 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."));
+ }
+ purple_debug_error("keyring",
+ "Specified keyring %s is not currently used (%s). "
+ "Data will be lost.\n", keyring_id,
+ purple_keyring_get_id(inuse));
+ return FALSE;
+ }
+
+ import = purple_keyring_get_import_password(inuse);
+ if (import == NULL) {
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("Keyring", "Configured keyring "
+ "cannot import password info. This might be "
+ "normal.\n");
+ }
+ return TRUE;
+ }
+
+ return import(account, mode, data, error);
+}
+
+gboolean
+purple_keyring_export_password(PurpleAccount *account, const gchar **keyring_id,
+ const gchar **mode, gchar **data, GError **error,
+ GDestroyNotify *destroy)
+{
+ PurpleKeyring *inuse;
+ PurpleKeyringExportPassword export;
+
+ g_return_val_if_fail(account != NULL, FALSE);
+ g_return_val_if_fail(keyring_id != NULL, FALSE);
+ g_return_val_if_fail(mode != NULL, FALSE);
+ g_return_val_if_fail(data != NULL, FALSE);
+ g_return_val_if_fail(error != NULL, FALSE);
+
+ inuse = purple_keyring_get_inuse();
+
+ if (inuse == NULL) {
+ PurpleKeyringFailedImport *import = g_hash_table_lookup(
+ 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."));
+ purple_debug_warning("keyring",
+ "No keyring configured, cannot export password "
+ "info.\n");
+ return FALSE;
+ } else {
+ purple_debug_info("keyring", "No keyring configured, "
+ "getting fallback export data for %s.\n",
+ purple_keyring_print_account(account));
+
+ *keyring_id = import->keyring_id;
+ *mode = import->mode;
+ *data = g_strdup(import->data);
+ *destroy = (GDestroyNotify)purple_str_wipe;
+ return TRUE;
+ }
+ }
+
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("keyring",
+ "Exporting password for account %s from keyring %s\n",
+ purple_keyring_print_account(account),
+ purple_keyring_get_id(inuse));
+ }
+
+ *keyring_id = purple_keyring_get_id(inuse);
+
+ export = purple_keyring_get_export_password(inuse);
+ if (export == NULL) {
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("Keyring", "Configured keyring "
+ "cannot export password info. This might be "
+ "normal.\n");
+ }
+ *mode = NULL;
+ *data = NULL;
+ *destroy = NULL;
+ return TRUE;
+ }
+
+ return export(account, mode, data, error, destroy);
+}
+
+void
+purple_keyring_get_password(PurpleAccount *account,
+ PurpleKeyringReadCallback cb, gpointer data)
+{
+ GError *error;
+ PurpleKeyring *inuse;
+ PurpleKeyringRead read_cb;
+
+ g_return_if_fail(account != NULL);
+
+ if (purple_keyring_is_quitting) {
+ purple_debug_error("keyring", "Cannot request a password while "
+ "quitting.\n");
+ if (cb == NULL)
+ return;
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_INTERNAL,
+ _("Cannot request a password while quitting."));
+ cb(account, NULL, error, data);
+ g_error_free(error);
+ return;
+ }
+
+ inuse = purple_keyring_get_inuse();
+
+ if (inuse == NULL) {
+ purple_debug_error("keyring", "No keyring configured.\n");
+ if (cb == NULL)
+ return;
+
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_NOKEYRING,
+ _("No keyring configured."));
+ cb(account, NULL, error, data);
+ g_error_free(error);
+ return;
+ }
+
+ read_cb = purple_keyring_get_read_password(inuse);
+ g_assert(read_cb != NULL);
+
+ purple_debug_info("keyring", "Reading password for account %s...\n",
+ purple_keyring_print_account(account));
+ read_cb(account, cb, data);
+}
+
+static void
+purple_keyring_set_password_save_cb(PurpleAccount *account, GError *error,
+ gpointer _set_data)
+{
+ PurpleKeyringSetPasswordData *set_data = _set_data;
+
+ g_return_if_fail(account != NULL);
+ g_return_if_fail(set_data != NULL);
+
+ if (error == NULL && purple_debug_is_verbose()) {
+ purple_debug_misc("keyring", "Password for account %s "
+ "saved successfully.\n",
+ purple_keyring_print_account(account));
+ } else if (purple_debug_is_verbose()) {
+ purple_debug_warning("keyring", "Password for account %s "
+ "not saved successfully.\n",
+ purple_keyring_print_account(account));
+ }
+
+ if (error != NULL) {
+ purple_notify_error(NULL, _("Keyrings"),
+ _("Failed to save a password in keyring."),
+ error->message, NULL);
+ }
+
+ if (set_data->cb != NULL)
+ set_data->cb(account, error, set_data->cb_data);
+ g_free(set_data);
+}
+
+void
+purple_keyring_set_password(PurpleAccount *account, const gchar *password,
+ PurpleKeyringSaveCallback cb, gpointer data)
+{
+ GError *error;
+ PurpleKeyring *inuse;
+ PurpleKeyringSave save_cb;
+ PurpleKeyringSetPasswordData *set_data;
+
+ g_return_if_fail(account != NULL);
+
+ if (purple_keyring_is_quitting) {
+ purple_debug_error("keyring", "Cannot save a password while "
+ "quitting.\n");
+ if (cb == NULL)
+ return;
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_INTERNAL,
+ _("Cannot save a password while quitting."));
+ cb(account, error, data);
+ g_error_free(error);
+ return;
+ }
+
+ if (current_change_tracker != NULL) {
+ purple_debug_error("keyring", "Cannot save a password during "
+ "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."));
+ cb(account, error, data);
+ g_error_free(error);
+ return;
+ }
+
+ inuse = purple_keyring_get_inuse();
+ if (inuse == NULL) {
+ if (cb == NULL)
+ return;
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_NOKEYRING,
+ _("No keyring configured."));
+ cb(account, error, data);
+ g_error_free(error);
+ return;
+ }
+
+ save_cb = purple_keyring_get_save_password(inuse);
+ g_assert(save_cb != NULL);
+
+ set_data = g_new(PurpleKeyringSetPasswordData, 1);
+ set_data->cb = cb;
+ set_data->cb_data = data;
+ purple_debug_info("keyring", "%s password for account %s...\n",
+ (password ? "Saving" : "Removing"),
+ purple_keyring_print_account(account));
+ save_cb(account, password, purple_keyring_set_password_save_cb, set_data);
+}
+
+PurpleRequestFields *
+purple_keyring_read_settings(void)
+{
+ PurpleKeyring *inuse;
+ PurpleKeyringReadSettings read_settings;
+
+ if (purple_keyring_is_quitting || current_change_tracker != NULL) {
+ purple_debug_error("keyring", "Cannot read settngs at the "
+ "moment.\n");
+ return NULL;
+ }
+
+ inuse = purple_keyring_get_inuse();
+ if (inuse == NULL) {
+ purple_debug_error("keyring", "No keyring in use.\n");
+ return NULL;
+ }
+
+ read_settings = purple_keyring_get_read_settings(inuse);
+ if (read_settings == NULL)
+ return NULL;
+ return read_settings();
+}
+
+gboolean
+purple_keyring_apply_settings(void *notify_handle, PurpleRequestFields *fields)
+{
+ PurpleKeyring *inuse;
+ PurpleKeyringApplySettings apply_settings;
+
+ g_return_val_if_fail(fields != NULL, FALSE);
+
+ if (purple_keyring_is_quitting || current_change_tracker != NULL) {
+ purple_debug_error("keyring", "Cannot apply settngs at the "
+ "moment.\n");
+ return FALSE;
+ }
+
+ inuse = purple_keyring_get_inuse();
+ if (inuse == NULL) {
+ purple_debug_error("keyring", "No keyring in use.\n");
+ return FALSE;
+ }
+
+ apply_settings = purple_keyring_get_apply_settings(inuse);
+ if (apply_settings == NULL) {
+ purple_debug_warning("keyring", "Applying settings not "
+ "supported.\n");
+ return FALSE;
+ }
+ return apply_settings(notify_handle, fields);
+}
+
+/**************************************************************************/
+/* PurpleKeyring accessors */
+/**************************************************************************/
+
+PurpleKeyring *
+purple_keyring_new(void)
+{
+ return g_new0(PurpleKeyring, 1);
+}
+
+void
+purple_keyring_free(PurpleKeyring *keyring)
+{
+ g_return_if_fail(keyring != NULL);
+
+ g_free(keyring->name);
+ g_free(keyring->id);
+ g_free(keyring);
+}
+
+const gchar *
+purple_keyring_get_name(const PurpleKeyring *keyring)
+{
+ g_return_val_if_fail(keyring != NULL, NULL);
+
+ return keyring->name;
+}
+
+const gchar *
+purple_keyring_get_id(const PurpleKeyring *keyring)
+{
+ g_return_val_if_fail(keyring != NULL, NULL);
+
+ return keyring->id;
+}
+
+PurpleKeyringRead
+purple_keyring_get_read_password(const PurpleKeyring *keyring)
+{
+ g_return_val_if_fail(keyring != NULL, NULL);
+
+ return keyring->read_password;
+}
+
+PurpleKeyringSave
+purple_keyring_get_save_password(const PurpleKeyring *keyring)
+{
+ g_return_val_if_fail(keyring != NULL, NULL);
+
+ return keyring->save_password;
+}
+
+PurpleKeyringCancelRequests
+purple_keyring_get_cancel_requests(const PurpleKeyring *keyring)
+{
+ g_return_val_if_fail(keyring != NULL, NULL);
+
+ return keyring->cancel_requests;
+}
+
+PurpleKeyringClose
+purple_keyring_get_close_keyring(const PurpleKeyring *keyring)
+{
+ g_return_val_if_fail(keyring != NULL, NULL);
+
+ return keyring->close_keyring;
+}
+
+PurpleKeyringImportPassword
+purple_keyring_get_import_password(const PurpleKeyring *keyring)
+{
+ g_return_val_if_fail(keyring != NULL, NULL);
+
+ return keyring->import_password;
+}
+
+PurpleKeyringExportPassword
+purple_keyring_get_export_password(const PurpleKeyring *keyring)
+{
+ g_return_val_if_fail(keyring != NULL, NULL);
+
+ return keyring->export_password;
+}
+
+PurpleKeyringReadSettings
+purple_keyring_get_read_settings(const PurpleKeyring *keyring)
+{
+ g_return_val_if_fail(keyring != NULL, NULL);
+
+ return keyring->read_settings;
+}
+
+PurpleKeyringApplySettings
+purple_keyring_get_apply_settings(const PurpleKeyring *keyring)
+{
+ g_return_val_if_fail(keyring != NULL, NULL);
+
+ return keyring->apply_settings;
+}
+
+void
+purple_keyring_set_name(PurpleKeyring *keyring, const gchar *name)
+{
+ g_return_if_fail(keyring != NULL);
+ g_return_if_fail(name != NULL);
+
+ g_free(keyring->name);
+ keyring->name = g_strdup(name);
+}
+
+void
+purple_keyring_set_id(PurpleKeyring *keyring, const gchar *id)
+{
+ g_return_if_fail(keyring != NULL);
+ g_return_if_fail(id != NULL);
+
+ g_free(keyring->id);
+ keyring->id = g_strdup(id);
+}
+
+void
+purple_keyring_set_read_password(PurpleKeyring *keyring,
+ PurpleKeyringRead read_cb)
+{
+ g_return_if_fail(keyring != NULL);
+ g_return_if_fail(read_cb != NULL);
+
+ keyring->read_password = read_cb;
+}
+
+void
+purple_keyring_set_save_password(PurpleKeyring *keyring,
+ PurpleKeyringSave save_cb)
+{
+ g_return_if_fail(keyring != NULL);
+ g_return_if_fail(save_cb != NULL);
+
+ keyring->save_password = save_cb;
+}
+
+void
+purple_keyring_set_cancel_requests(PurpleKeyring *keyring,
+ PurpleKeyringCancelRequests cancel_requests)
+{
+ g_return_if_fail(keyring != NULL);
+
+ keyring->cancel_requests = cancel_requests;
+}
+
+void
+purple_keyring_set_close_keyring(PurpleKeyring *keyring,
+ PurpleKeyringClose close_cb)
+{
+ g_return_if_fail(keyring != NULL);
+
+ keyring->close_keyring = close_cb;
+}
+
+void
+purple_keyring_set_import_password(PurpleKeyring *keyring,
+ PurpleKeyringImportPassword import_password)
+{
+ g_return_if_fail(keyring != NULL);
+
+ keyring->import_password = import_password;
+}
+
+void
+purple_keyring_set_export_password(PurpleKeyring *keyring,
+ PurpleKeyringExportPassword export_password)
+{
+ g_return_if_fail(keyring != NULL);
+
+ keyring->export_password = export_password;
+}
+
+void
+purple_keyring_set_read_settings(PurpleKeyring *keyring,
+PurpleKeyringReadSettings read_settings)
+{
+ g_return_if_fail(keyring != NULL);
+
+ keyring->read_settings = read_settings;
+}
+
+void
+purple_keyring_set_apply_settings(PurpleKeyring *keyring,
+PurpleKeyringApplySettings apply_settings)
+{
+ g_return_if_fail(keyring != NULL);
+
+ keyring->apply_settings = apply_settings;
+}
+
+
+/**************************************************************************/
+/* Error Codes */
+/**************************************************************************/
+
+GQuark purple_keyring_error_domain(void)
+{
+ return g_quark_from_static_string("libpurple keyring");
+}
+
+/**************************************************************************/
+/* Keyring Subsystem */
+/**************************************************************************/
+
+static void purple_keyring_core_initialized_cb(void)
+{
+ if (purple_keyring_inuse == NULL) {
+ purple_notify_error(NULL, _("Keyrings"),
+ _("Failed to load selected keyring."),
+ _("Check your system configuration or select another "
+ "one in Preferences dialog."), NULL);
+ }
+}
+
+static void purple_keyring_core_quitting_cb()
+{
+ if (current_change_tracker != NULL) {
+ PurpleKeyringChangeTracker *tracker = current_change_tracker;
+ tracker->abort = TRUE;
+ if (tracker->old_kr)
+ purple_keyring_cancel_requests(tracker->old_kr);
+ if (current_change_tracker == tracker && tracker->new_kr)
+ purple_keyring_cancel_requests(tracker->new_kr);
+ }
+
+ purple_keyring_is_quitting = TRUE;
+ if (purple_keyring_inuse != NULL)
+ purple_keyring_cancel_requests(purple_keyring_inuse);
+}
+
+void
+purple_keyring_init(void)
+{
+ const gchar *touse;
+ GList *it;
+
+ purple_keyring_keyrings = NULL;
+ purple_keyring_inuse = NULL;
+
+ purple_keyring_failed_imports = g_hash_table_new_full(g_direct_hash,
+ g_direct_equal, NULL,
+ (GDestroyNotify)purple_keyring_failed_import_free);
+
+ /* void keyring_register(const char *keyring_id,
+ * PurpleKeyring * keyring);
+ *
+ * A signal called when keyring is registered.
+ *
+ * @param keyring_id The keyring ID.
+ * @param keyring The keyring.
+ */
+ purple_signal_register(purple_keyring_get_handle(), "keyring-register",
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2, G_TYPE_STRING,
+ PURPLE_TYPE_KEYRING);
+
+ /* void keyring_unregister(const char *keyring_id,
+ * PurpleKeyring * keyring);
+ *
+ * A signal called when keyring is unregistered.
+ *
+ * @param keyring_id The keyring ID.
+ * @param keyring The keyring.
+ */
+ purple_signal_register(purple_keyring_get_handle(), "keyring-unregister",
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2, G_TYPE_STRING,
+ PURPLE_TYPE_KEYRING);
+
+ /* void password_migration(PurpleAccount* account);
+ *
+ * A signal called, when a password for the account was moved to another
+ * keyring.
+ *
+ * @param account The account.
+ */
+ purple_signal_register(purple_keyring_get_handle(), "password-migration",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1, PURPLE_TYPE_ACCOUNT);
+
+ touse = purple_prefs_get_string("/purple/keyring/active");
+ if (touse == NULL) {
+ purple_prefs_add_none("/purple/keyring");
+ purple_prefs_add_string("/purple/keyring/active",
+ PURPLE_DEFAULT_KEYRING);
+ purple_keyring_to_use = g_strdup(PURPLE_DEFAULT_KEYRING);
+ } else
+ purple_keyring_to_use = g_strdup(touse);
+
+ purple_keyring_pref_connect();
+
+ for (it = purple_plugins_get_all(); it != NULL; it = it->next) {
+ PurplePlugin *plugin = (PurplePlugin *)it->data;
+
+ if (plugin->info == NULL || plugin->info->id == NULL)
+ continue;
+ if (strncmp(plugin->info->id, "keyring-", 8) != 0)
+ continue;
+
+ if (purple_plugin_is_loaded(plugin))
+ continue;
+
+ if (purple_plugin_load(plugin)) {
+ purple_keyring_loaded_plugins = g_list_append(
+ purple_keyring_loaded_plugins, plugin);
+ }
+ }
+
+ if (purple_keyring_inuse == NULL)
+ purple_debug_error("keyring", "Selected keyring failed to load\n");
+
+ purple_signal_connect(purple_get_core(), "core-initialized",
+ purple_keyring_get_handle(),
+ PURPLE_CALLBACK(purple_keyring_core_initialized_cb), NULL);
+ purple_signal_connect(purple_get_core(), "quitting",
+ purple_keyring_get_handle(),
+ PURPLE_CALLBACK(purple_keyring_core_quitting_cb), NULL);
+}
+
+void
+purple_keyring_uninit(void)
+{
+ GList *it;
+
+ purple_keyring_pref_disconnect();
+
+ g_free(purple_keyring_to_use);
+ purple_keyring_inuse = NULL;
+
+ g_hash_table_destroy(purple_keyring_failed_imports);
+ purple_keyring_failed_imports = NULL;
+
+ for (it = g_list_first(purple_keyring_loaded_plugins); it != NULL;
+ it = g_list_next(it))
+ {
+ PurplePlugin *plugin = (PurplePlugin *)it->data;
+ if (g_list_find(purple_plugins_get_loaded(), plugin) == NULL)
+ continue;
+ purple_plugin_unload(plugin);
+ }
+ g_list_free(purple_keyring_loaded_plugins);
+ purple_keyring_loaded_plugins = NULL;
+
+ purple_signals_unregister_by_instance(purple_keyring_get_handle());
+ purple_signals_disconnect_by_handle(purple_keyring_get_handle());
+}
+
+void *
+purple_keyring_get_handle(void)
+{
+ static int handle;
+
+ return &handle;
+}
+
+static PurpleKeyring *
+purple_keyring_copy(PurpleKeyring *keyring)
+{
+ PurpleKeyring *keyring_copy;
+
+ g_return_val_if_fail(keyring != NULL, NULL);
+
+ keyring_copy = purple_keyring_new();
+ *keyring_copy = *keyring;
+
+ keyring_copy->name = g_strdup(keyring->name);
+ keyring_copy->id = g_strdup(keyring->id);
+
+ return keyring_copy;
+}
+
+GType
+purple_keyring_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleKeyring",
+ (GBoxedCopyFunc)purple_keyring_copy,
+ (GBoxedFreeFunc)purple_keyring_free);
+ }
+
+ return type;
+}
diff --git a/libpurple/keyring.h b/libpurple/keyring.h
new file mode 100644
index 0000000000..2b7b0ccb3c
--- /dev/null
+++ b/libpurple/keyring.h
@@ -0,0 +1,647 @@
+/* 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 _PURPLE_KEYRING_H_
+#define _PURPLE_KEYRING_H_
+/**
+ * SECTION:keyring
+ * @section_id: libpurple-keyring
+ * @short_description: <filename>keyring.h</filename>
+ * @title: Keyring API
+ */
+
+#include "account.h"
+#include "request.h"
+
+#define PURPLE_TYPE_KEYRING (purple_keyring_get_type())
+
+/**
+ * PURPLE_DEFAULT_KEYRING:
+ *
+ * Default keyring ID.
+ */
+#define PURPLE_DEFAULT_KEYRING "keyring-internal"
+
+/**
+ * PURPLE_KEYRING_ERROR:
+ *
+ * Keyring subsystem error domain.
+ */
+#define PURPLE_KEYRING_ERROR purple_keyring_error_domain()
+
+/**************************************************************************/
+/* Data structures and types */
+/**************************************************************************/
+
+typedef struct _PurpleKeyring PurpleKeyring;
+
+/**************************************************************************/
+/* Callbacks for keyrings access functions */
+/**************************************************************************/
+
+/**
+ * PurpleKeyringReadCallback:
+ * @account: The account.
+ * @password: The password.
+ * @error: Error that may have occurred.
+ * @data: Data passed to the callback.
+ *
+ * Callback for once a password is read.
+ *
+ * If there was a problem, the password will be NULL, and the error set.
+ */
+typedef void (*PurpleKeyringReadCallback)(PurpleAccount *account,
+ const gchar *password, GError *error, gpointer data);
+
+/**
+ * PurpleKeyringSaveCallback:
+ * @account: The account.
+ * @error: Error that may have occurred.
+ * @data: Data passed to the callback.
+ *
+ * Callback for once a password has been stored.
+ *
+ * If there was a problem, the error will be set.
+ */
+typedef void (*PurpleKeyringSaveCallback)(PurpleAccount *account, GError *error,
+ gpointer data);
+
+/**
+ * PurpleKeyringChangeMasterCallback:
+ * @error: Error that has occurred.
+ * @data: Data passed to the callback.
+ *
+ * Callback for once the master password for a keyring has been changed.
+ */
+typedef void (*PurpleKeyringChangeMasterCallback)(GError *error, gpointer data);
+
+/**
+ * PurpleKeyringSetInUseCallback:
+ * @error: An error that might have occurred.
+ * @data: A pointer to user supplied data.
+ *
+ * Callback for when we change the keyring.
+ */
+typedef void (*PurpleKeyringSetInUseCallback)(GError *error, gpointer data);
+
+/**************************************************************************/
+/* Keyrings access functions */
+/**************************************************************************/
+
+/**
+ * PurpleKeyringRead:
+ * @account: The account.
+ * @cb: A callback for once the password is found.
+ * @data: Data to be passed to the callback.
+ *
+ * Read the password for an account.
+ */
+typedef void (*PurpleKeyringRead)(PurpleAccount *account,
+ PurpleKeyringReadCallback cb, gpointer data);
+
+/**
+ * PurpleKeyringSave:
+ * @account: The account.
+ * @password: The password to be stored. If the password is NULL, this
+ * means that the keyring should forget about that password.
+ * @cb: A callback for once the password is saved.
+ * @data: Data to be passed to the callback.
+ *
+ * Store a password in the keyring.
+ */
+typedef void (*PurpleKeyringSave)(PurpleAccount *account, const gchar *password,
+ PurpleKeyringSaveCallback cb, gpointer data);
+
+/**
+ * PurpleKeyringCancelRequests:
+ *
+ * Cancel all running requests.
+ *
+ * After calling that, all queued requests should run their callbacks (most
+ * probably, with failure result).
+ */
+typedef void (*PurpleKeyringCancelRequests)(void);
+
+/**
+ * PurpleKeyringClose:
+ *
+ * Close the keyring.
+ *
+ * This will be called so the keyring can do any cleanup it needs.
+ */
+typedef void (*PurpleKeyringClose)(void);
+
+/**
+ * PurpleKeyringImportPassword:
+ * @account: The account.
+ * @mode: A keyring specific option that was stored. Can be NULL.
+ * @data: Data that was stored. Can be NULL.
+ *
+ * Import serialized (and maybe encrypted) password.
+ *
+ * This is not async because it is not meant to prompt for a master password and
+ * decrypt passwords.
+ *
+ * Returns: TRUE on success, FALSE on failure.
+ */
+typedef gboolean (*PurpleKeyringImportPassword)(PurpleAccount *account,
+ const gchar *mode, const gchar *data, GError **error);
+
+/**
+ * PurpleKeyringExportPassword:
+ * @account: The account.
+ * @mode: An option field that can be used by the plugin. This is
+ * expected to be a static string.
+ * @data: The data to be stored in the XML node. This string will be
+ * freed using destroy() once not needed anymore.
+ * @error: Will be set if a problem occured.
+ * @destroy: A function to be called, if non NULL, to free data.
+ *
+ * Export serialized (and maybe encrypted) password.
+ *
+ * Returns: TRUE on success, FALSE on failure.
+ */
+typedef gboolean (*PurpleKeyringExportPassword)(PurpleAccount *account,
+ const gchar **mode, gchar **data, GError **error,
+ GDestroyNotify *destroy);
+
+/**
+ * PurpleKeyringReadSettings:
+ *
+ * Read keyring settings.
+ *
+ * Returns: New copy of current settings (must be free'd with
+ * purple_request_fields_destroy).
+ */
+typedef PurpleRequestFields * (*PurpleKeyringReadSettings)(void);
+
+/**
+ * PurpleKeyringApplySettings:
+ * @notify_handle: A handle that can be passed to purple_notify_message.
+ * @fields: Modified settings (originally taken from
+ * PurpleKeyringReadSettings).
+ *
+ * Applies modified keyring settings.
+ *
+ * Returns: TRUE, if succeeded, FALSE otherwise.
+ */
+typedef gboolean (*PurpleKeyringApplySettings)(void *notify_handle,
+ PurpleRequestFields *fields);
+
+G_BEGIN_DECLS
+
+/**************************************************************************/
+/* Setting used keyrings */
+/**************************************************************************/
+
+/**
+ * purple_keyring_find_keyring_by_id:
+ * @id: The id for the keyring.
+ *
+ * Find a keyring by an id.
+ *
+ * Returns: The keyring, or NULL if not found.
+ */
+PurpleKeyring *
+purple_keyring_find_keyring_by_id(const gchar *id);
+
+/**
+ * purple_keyring_get_inuse:
+ *
+ * Get the keyring being used.
+ */
+PurpleKeyring *
+purple_keyring_get_inuse(void);
+
+/**
+ * purple_keyring_set_inuse:
+ * @newkeyring: The new keyring to use.
+ * @force: %FALSE if the change can be cancelled. If this is %TRUE
+ * and an error occurs, data might be lost.
+ * @cb: (scope call): A callback for once the change is complete.
+ * @data: Data to be passed to the callback.
+ *
+ * Set the keyring to use. This function will move all passwords from
+ * the old keyring to the new one.
+ *
+ * If it fails, it will cancel all changes, close the new keyring, and notify
+ * the callback. If it succeeds, it will remove all passwords from the old safe
+ * and close that safe.
+ */
+void
+purple_keyring_set_inuse(PurpleKeyring *newkeyring, gboolean force,
+ PurpleKeyringSetInUseCallback cb, gpointer data);
+
+/**
+ * purple_keyring_register:
+ * @keyring: The keyring to register.
+ *
+ * Register a keyring plugin.
+ */
+void
+purple_keyring_register(PurpleKeyring *keyring);
+
+/**
+ * purple_keyring_unregister:
+ * @keyring: The keyring to unregister.
+ *
+ * Unregister a keyring plugin.
+ *
+ * In case the keyring is in use, passwords will be moved to a fallback safe,
+ * and the keyring to unregister will be properly closed.
+ */
+void
+purple_keyring_unregister(PurpleKeyring *keyring);
+
+/**
+ * purple_keyring_get_options:
+ *
+ * Returns a GList containing the IDs and names of the registered
+ * keyrings.
+ *
+ * Returns: The list of IDs and names.
+ */
+GList *
+purple_keyring_get_options(void);
+
+/**************************************************************************/
+/* Keyring plugin wrappers */
+/**************************************************************************/
+
+/**
+ * purple_keyring_import_password:
+ * @account: The account.
+ * @keyring_id: The plugin ID that was stored in the xml file. Can be NULL.
+ * @mode: A keyring specific option that was stored. Can be NULL.
+ * @data: Data that was stored, can be NULL.
+ *
+ * Import serialized (and maybe encrypted) password into current keyring.
+ *
+ * It's used by account.c while reading a password from xml.
+ *
+ * Returns: TRUE if the input was accepted, FALSE otherwise.
+ */
+gboolean
+purple_keyring_import_password(PurpleAccount *account, const gchar *keyring_id,
+ const gchar *mode, const gchar *data, GError **error);
+
+/**
+ * purple_keyring_export_password:
+ * @account: The account for which we want the info.
+ * @keyring_id: The plugin id to be stored in the XML node. This will be
+ * NULL or a string that can be considered static.
+ * @mode: An option field that can be used by the plugin. This will
+ * be NULL or a string that can be considered static.
+ * @data: The data to be stored in the XML node. This string must be
+ * freed using destroy() once not needed anymore if it is not
+ * NULL.
+ * @error: Will be set if a problem occured.
+ * @destroy: A function to be called, if non NULL, to free data.
+ *
+ * Export serialized (and maybe encrypted) password out of current keyring.
+ *
+ * It's used by account.c while syncing accounts to xml.
+ *
+ * Returns: TRUE if the info was exported successfully, FALSE otherwise.
+ */
+gboolean
+purple_keyring_export_password(PurpleAccount *account, const gchar **keyring_id,
+ const gchar **mode, gchar **data, GError **error,
+ GDestroyNotify *destroy);
+
+/**
+ * purple_keyring_get_password:
+ * @account: The account.
+ * @cb: (scope call): A callback for once the password is read.
+ * @data: Data passed to the callback.
+ *
+ * Read a password from the current keyring.
+ */
+void
+purple_keyring_get_password(PurpleAccount *account,
+ PurpleKeyringReadCallback cb, gpointer data);
+
+/**
+ * purple_keyring_set_password:
+ * @account: The account.
+ * @password: The password to save.
+ * @cb: (scope call): A callback for once the password is saved.
+ * @data: Data to be passed to the callback.
+ *
+ * Save a password to the current keyring.
+ */
+void
+purple_keyring_set_password(PurpleAccount *account, const gchar *password,
+ PurpleKeyringSaveCallback cb, gpointer data);
+
+/**
+ * purple_keyring_read_settings:
+ *
+ * Reads settings from current keyring.
+ *
+ * Returns: New copy of current settings (must be free'd with
+ * purple_request_fields_destroy).
+ */
+PurpleRequestFields *
+purple_keyring_read_settings(void);
+
+/**
+ * purple_keyring_apply_settings:
+ * @notify_handle: A handle that can be passed to purple_notify_message.
+ * @fields: Modified settings (originally taken from
+ * PurpleKeyringReadSettings).
+ *
+ * Applies modified settings to current keyring.
+ *
+ * Returns: TRUE, if succeeded, FALSE otherwise.
+ */
+gboolean
+purple_keyring_apply_settings(void *notify_handle, PurpleRequestFields *fields);
+
+/**************************************************************************/
+/* PurpleKeyring accessors */
+/**************************************************************************/
+
+/**
+ * purple_keyring_get_type:
+ *
+ * Returns: The #GType for the #PurpleKeyring boxed structure.
+ */
+GType purple_keyring_get_type(void);
+
+/**
+ * purple_keyring_new:
+ *
+ * Creates a new keyring wrapper.
+ */
+PurpleKeyring *
+purple_keyring_new(void);
+
+/**
+ * purple_keyring_free:
+ * @keyring: Keyring wrapper struct.
+ *
+ * Frees all data allocated with purple_keyring_new.
+ */
+void
+purple_keyring_free(PurpleKeyring *keyring);
+
+/**
+ * purple_keyring_get_name:
+ * @keyring: The keyring.
+ *
+ * Gets friendly user name.
+ *
+ * Returns: Friendly user name.
+ */
+const gchar *
+purple_keyring_get_name(const PurpleKeyring *keyring);
+
+/**
+ * purple_keyring_get_id:
+ * @keyring: The keyring.
+ *
+ * Gets keyring ID.
+ *
+ * Returns: Keyring ID.
+ */
+const gchar *
+purple_keyring_get_id(const PurpleKeyring *keyring);
+
+PurpleKeyringRead
+purple_keyring_get_read_password(const PurpleKeyring *keyring);
+
+PurpleKeyringSave
+purple_keyring_get_save_password(const PurpleKeyring *keyring);
+
+PurpleKeyringCancelRequests
+purple_keyring_get_cancel_requests(const PurpleKeyring *keyring);
+
+PurpleKeyringClose
+purple_keyring_get_close_keyring(const PurpleKeyring *keyring);
+
+PurpleKeyringImportPassword
+purple_keyring_get_import_password(const PurpleKeyring *keyring);
+
+PurpleKeyringExportPassword
+purple_keyring_get_export_password(const PurpleKeyring *keyring);
+
+PurpleKeyringReadSettings
+purple_keyring_get_read_settings(const PurpleKeyring *keyring);
+
+PurpleKeyringApplySettings
+purple_keyring_get_apply_settings(const PurpleKeyring *keyring);
+
+/**
+ * purple_keyring_set_name:
+ * @keyring: The keyring.
+ * @name: Friendly user name.
+ *
+ * Sets friendly user name.
+ *
+ * This field is required.
+ */
+void
+purple_keyring_set_name(PurpleKeyring *keyring, const gchar *name);
+
+/**
+ * purple_keyring_set_id:
+ * @keyring: The keyring.
+ * @id: Keyring ID.
+ *
+ * Sets keyring ID.
+ *
+ * This field is required.
+ */
+void
+purple_keyring_set_id(PurpleKeyring *keyring, const gchar *id);
+
+/**
+ * purple_keyring_set_read_password:
+ * @keyring: The keyring.
+ * @read_cb: (scope call): Read password method.
+ *
+ * Sets read password method.
+ *
+ * This field is required.
+ */
+void
+purple_keyring_set_read_password(PurpleKeyring *keyring,
+ PurpleKeyringRead read_cb);
+
+/**
+ * purple_keyring_set_save_password:
+ * @keyring: The keyring.
+ * @save_cb: (scope call): Save password method.
+ *
+ * Sets save password method.
+ *
+ * This field is required.
+ */
+void
+purple_keyring_set_save_password(PurpleKeyring *keyring,
+ PurpleKeyringSave save_cb);
+
+/**
+ * purple_keyring_set_cancel_requests:
+ * @keyring: The keyring.
+ * @cancel_requests: (scope call): Cancel requests method.
+ *
+ * Sets cancel requests method.
+ */
+void
+purple_keyring_set_cancel_requests(PurpleKeyring *keyring,
+ PurpleKeyringCancelRequests cancel_requests);
+
+/**
+ * purple_keyring_set_close_keyring:
+ * @keyring: The keyring.
+ * @close_cb: (scope call): Close keyring method.
+ *
+ * Sets close keyring method.
+ */
+void
+purple_keyring_set_close_keyring(PurpleKeyring *keyring,
+ PurpleKeyringClose close_cb);
+
+/**
+ * purple_keyring_set_import_password:
+ * @keyring: The keyring.
+ * @import_password: (scope call): Import password method.
+ *
+ * Sets import password method.
+ */
+void
+purple_keyring_set_import_password(PurpleKeyring *keyring,
+ PurpleKeyringImportPassword import_password);
+
+/**
+ * purple_keyring_set_export_password:
+ * @keyring: The keyring.
+ * @export_password: (scope call): Export password method.
+ *
+ * Sets export password method.
+ */
+void
+purple_keyring_set_export_password(PurpleKeyring *keyring,
+ PurpleKeyringExportPassword export_password);
+
+/**
+ * purple_keyring_set_read_settings:
+ * @keyring: The keyring.
+ * @read_settings: (scope call): Read settings method.
+ *
+ * Sets read settings method.
+ */
+void
+purple_keyring_set_read_settings(PurpleKeyring *keyring,
+ PurpleKeyringReadSettings read_settings);
+
+/**
+ * purple_keyring_set_apply_settings:
+ * @keyring: The keyring.
+ * @apply_settings: (scope call): Apply settings method.
+ *
+ * Sets apply settings method.
+ */
+void
+purple_keyring_set_apply_settings(PurpleKeyring *keyring,
+ PurpleKeyringApplySettings apply_settings);
+
+/**************************************************************************/
+/* Error Codes */
+/**************************************************************************/
+
+/**
+ * purple_keyring_error_domain:
+ *
+ * Gets keyring subsystem error domain.
+ *
+ * Returns: keyring subsystem error domain.
+ */
+GQuark
+purple_keyring_error_domain(void);
+
+/**
+ * PurpleKeyringError:
+ * @PURPLE_KEYRING_ERROR_UNKNOWN: Unknown error.
+ * @PURPLE_KEYRING_ERROR_NOKEYRING: No keyring configured.
+ * @PURPLE_KEYRING_ERROR_INTERNAL: Internal keyring system error.
+ * @PURPLE_KEYRING_ERROR_BACKENDFAIL: Failed to communicate with the backend
+ * or internal backend error.
+ * @PURPLE_KEYRING_ERROR_NOPASSWORD: No password stored for the specified
+ * account.
+ * @PURPLE_KEYRING_ERROR_ACCESSDENIED: Access denied for the specified keyring
+ * or entry.
+ * @PURPLE_KEYRING_ERROR_CANCELLED: Operation was cancelled.
+ *
+ * Error codes for keyring subsystem.
+ */
+enum PurpleKeyringError
+{
+ PURPLE_KEYRING_ERROR_UNKNOWN = 0,
+
+ PURPLE_KEYRING_ERROR_NOKEYRING = 10,
+ PURPLE_KEYRING_ERROR_INTERNAL,
+ PURPLE_KEYRING_ERROR_BACKENDFAIL,
+
+ PURPLE_KEYRING_ERROR_NOPASSWORD = 20,
+ PURPLE_KEYRING_ERROR_ACCESSDENIED,
+ PURPLE_KEYRING_ERROR_CANCELLED
+};
+
+/*}@*/
+
+/**************************************************************************/
+/* Keyring Subsystem */
+/**************************************************************************/
+
+/**
+ * purple_keyring_init:
+ *
+ * Initializes the keyring subsystem.
+ */
+void
+purple_keyring_init(void);
+
+/**
+ * purple_keyring_uninit:
+ *
+ * Uninitializes the keyring subsystem.
+ */
+void
+purple_keyring_uninit(void);
+
+/**
+ * purple_keyring_get_handle:
+ *
+ * Returns the keyring subsystem handle.
+ *
+ * Returns: The keyring subsystem handle.
+ */
+void *
+purple_keyring_get_handle(void);
+
+/*}@*/
+
+G_END_DECLS
+
+#endif /* _PURPLE_KEYRING_H_ */
diff --git a/libpurple/log.c b/libpurple/log.c
index ea583fce48..99b2b4df44 100644
--- a/libpurple/log.c
+++ b/libpurple/log.c
@@ -1,8 +1,3 @@
-/**
- * @file log.c Logging API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -28,12 +23,12 @@
#include "account.h"
#include "dbus-maybe.h"
#include "debug.h"
-#include "internal.h"
+#include "glibcompat.h"
+#include "image-store.h"
#include "log.h"
#include "prefs.h"
#include "util.h"
#include "stringref.h"
-#include "imgstore.h"
#include "time.h"
static GSList *loggers = NULL;
@@ -292,6 +287,10 @@ gint purple_log_get_activity_score(PurpleLogType type, const char *name, PurpleA
while (logs) {
PurpleLog *log = (PurpleLog*)(logs->data);
+ if (!log) {
+ g_warn_if_reached();
+ continue;
+ }
/* Activity score counts bytes in the log, exponentially
decayed with a half-life of 14 days. */
score_double += purple_log_get_size(log) *
@@ -453,6 +452,8 @@ PurpleLogLogger *purple_log_logger_new(const char *id, const char *name, int fun
void purple_log_logger_free(PurpleLogLogger *logger)
{
+ if (!logger)
+ return;
g_free(logger->name);
g_free(logger->id);
g_free(logger);
@@ -700,17 +701,16 @@ void purple_log_init(void)
#else
#error Unknown size of time_t
#endif
- purple_value_new(PURPLE_TYPE_STRING), 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_LOG),
+ G_TYPE_STRING, 3,
+ PURPLE_TYPE_LOG,
#if SIZEOF_TIME_T == 4
- purple_value_new(PURPLE_TYPE_INT),
+ G_TYPE_INT,
#elif SIZEOF_TIME_T == 8
- purple_value_new(PURPLE_TYPE_INT64),
+ G_TYPE_INT64,
#else
# error Unknown size of time_t
#endif
- purple_value_new(PURPLE_TYPE_BOOLEAN));
+ G_TYPE_BOOLEAN);
purple_prefs_connect_callback(NULL, "/purple/logging/format",
logger_pref_cb, NULL);
@@ -745,6 +745,33 @@ purple_log_uninit(void)
g_hash_table_destroy(logsize_users_decayed);
}
+static PurpleLog *
+purple_log_copy(PurpleLog *log)
+{
+ PurpleLog *log_copy;
+
+ g_return_val_if_fail(log != NULL, NULL);
+
+ log_copy = g_new(PurpleLog, 1);
+ *log_copy = *log;
+
+ return log_copy;
+}
+
+GType
+purple_log_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleLog",
+ (GBoxedCopyFunc)purple_log_copy,
+ (GBoxedFreeFunc)g_free);
+ }
+
+ return type;
+}
+
/****************************************************************************
* LOGGERS ******************************************************************
****************************************************************************/
@@ -771,7 +798,9 @@ static char *log_get_timestamp(PurpleLog *log, time_t when)
}
/* NOTE: This can return msg (which you may or may not want to g_free())
- * NOTE: or a newly allocated string which you MUST g_free(). */
+ * NOTE: or a newly allocated string which you MUST g_free().
+ * TODO: XXX: does it really works?
+ */
static char *
convert_image_tags(const PurpleLog *log, const char *msg)
{
@@ -801,13 +830,13 @@ convert_image_tags(const PurpleLog *log, const char *msg)
{
FILE *image_file;
char *dir;
- PurpleStoredImage *image;
+ PurpleImage *image;
gconstpointer image_data;
- char *new_filename = NULL;
+ const gchar *new_filename = NULL;
char *path = NULL;
size_t image_byte_count;
- image = purple_imgstore_find_by_id(imgid);
+ image = purple_image_store_get(imgid);
if (image == NULL)
{
/* This should never happen. */
@@ -816,10 +845,10 @@ convert_image_tags(const PurpleLog *log, const char *msg)
g_return_val_if_reached((char *)msg);
}
- image_data = purple_imgstore_get_data(image);
- image_byte_count = purple_imgstore_get_size(image);
+ image_data = purple_image_get_data(image);
+ image_byte_count = purple_image_get_size(image);
dir = purple_log_get_log_dir(log->type, log->name, log->account);
- new_filename = purple_util_get_image_filename(image_data, image_byte_count);
+ new_filename = purple_image_generate_filename(image);
path = g_build_filename(dir, new_filename, NULL);
@@ -835,7 +864,10 @@ convert_image_tags(const PurpleLog *log, const char *msg)
fclose(image_file);
/* Attempt to not leave half-written files around. */
- unlink(path);
+ if (g_unlink(path)) {
+ purple_debug_error("log", "Error deleting partial "
+ "file %s: %s\n", path, g_strerror(errno));
+ }
}
else
{
@@ -851,8 +883,7 @@ convert_image_tags(const PurpleLog *log, const char *msg)
}
/* Write the new image tag */
- g_string_append_printf(newmsg, "<IMG SRC=\"%s\">", new_filename);
- g_free(new_filename);
+ g_string_append_printf(newmsg, "<img src=\"%s\">", new_filename);
g_free(path);
}
@@ -911,8 +942,9 @@ void purple_log_common_writer(PurpleLog *log, const char *ext)
"Could not create log file %s\n", path);
if (log->conv != NULL)
- purple_conversation_write(log->conv, NULL, _("Logging of this conversation failed."),
- PURPLE_MESSAGE_ERROR, time(NULL));
+ purple_conversation_write_system_message(log->conv,
+ _("Logging of this conversation failed."),
+ PURPLE_MESSAGE_ERROR);
g_free(path);
return;
@@ -1014,7 +1046,7 @@ int purple_log_common_total_sizer(PurpleLogType type, const char *name, PurpleAc
strlen(filename) >= (17 + strlen(ext)))
{
char *tmp = g_build_filename(path, filename, NULL);
- struct stat st;
+ GStatBuf st;
if (g_stat(tmp, &st))
{
purple_debug_error("log", "Error stating log file: %s\n", tmp);
@@ -1032,7 +1064,7 @@ int purple_log_common_total_sizer(PurpleLogType type, const char *name, PurpleAc
int purple_log_common_sizer(PurpleLog *log)
{
- struct stat st;
+ GStatBuf st;
PurpleLogCommonLoggerData *data = log->logger_data;
g_return_val_if_fail(data != NULL, 0);
@@ -1103,7 +1135,7 @@ static void log_get_log_sets_common(GHashTable *sets)
/* Find the account for username in the list of accounts for protocol. */
username_unescaped = purple_unescape_filename(username);
for (account_iter = g_list_first(accounts) ; account_iter != NULL ; account_iter = account_iter->next) {
- if (purple_strequal(((PurpleAccount *)account_iter->data)->username, username_unescaped)) {
+ if (purple_strequal(purple_account_get_username((PurpleAccount *)account_iter->data), username_unescaped)) {
account = account_iter->data;
break;
}
@@ -1147,7 +1179,7 @@ static void log_get_log_sets_common(GHashTable *sets)
/* Determine if this (account, name) combination exists as a buddy. */
if (account != NULL && *name != '\0')
- set->buddy = (purple_find_buddy(account, name) != NULL);
+ set->buddy = (purple_blist_find_buddy(account, name) != NULL);
else
set->buddy = FALSE;
@@ -1436,9 +1468,6 @@ static gsize html_logger_write(PurpleLog *log, PurpleMessageFlags type,
written += fprintf(data->file, "<font size=\"2\">(%s)</font> %s<br/>\n", date, msg_fixed);
else if (type & PURPLE_MESSAGE_ERROR)
written += fprintf(data->file, "<font color=\"#FF0000\"><font size=\"2\">(%s)</font><b> %s</b></font><br/>\n", date, msg_fixed);
- else if (type & PURPLE_MESSAGE_WHISPER)
- written += fprintf(data->file, "<font color=\"#6C2585\"><font size=\"2\">(%s)</font><b> %s:</b></font> %s<br/>\n",
- date, escaped_from, msg_fixed);
else if (type & PURPLE_MESSAGE_AUTO_RESP) {
if (type & PURPLE_MESSAGE_SEND)
written += fprintf(data->file, _("<font color=\"#16569E\"><font size=\"2\">(%s)</font> <b>%s &lt;AUTO-REPLY&gt;:</b></font> %s<br/>\n"), date, escaped_from, msg_fixed);
@@ -1550,7 +1579,7 @@ static gsize txt_logger_write(PurpleLog *log,
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;
if (log->type == PURPLE_LOG_SYSTEM)
@@ -1594,9 +1623,7 @@ static gsize txt_logger_write(PurpleLog *log,
/* This shouldn't happen */
g_free(stripped);
return written;
- } else if (type & PURPLE_MESSAGE_WHISPER)
- written += fprintf(data->file, "(%s) *%s* %s", date, from, stripped);
- else
+ } else
written += fprintf(data->file, "(%s) %s%s %s\n", date, from ? from : "",
from ? ":" : "", stripped);
}
@@ -1672,7 +1699,7 @@ static GList *old_logger_list(PurpleLogType type, const char *sn, PurpleAccount
char *logfile = g_strdup_printf("%s.log", purple_normalize(account, sn));
char *pathstr = g_build_filename(purple_user_dir(), "logs", logfile, NULL);
PurpleStringref *pathref = purple_stringref_new(pathstr);
- struct stat st;
+ GStatBuf st;
time_t log_last_modified;
FILE *index;
FILE *file;
@@ -1701,7 +1728,7 @@ static GList *old_logger_list(PurpleLogType type, const char *sn, PurpleAccount
g_free(pathstr);
return NULL;
}
- if (fstat(file_fd, &st) == -1) {
+ if (_purple_fstat(file_fd, &st) == -1) {
purple_stringref_unref(pathref);
g_free(pathstr);
fclose(file);
@@ -1714,7 +1741,7 @@ static GList *old_logger_list(PurpleLogType type, const char *sn, PurpleAccount
index_fd = g_open(pathstr, 0, O_RDONLY);
if (index_fd != -1) {
- if (fstat(index_fd, &st) != 0) {
+ if (_purple_fstat(index_fd, &st) != 0) {
close(index_fd);
index_fd = -1;
}
@@ -1933,7 +1960,7 @@ static int old_logger_total_size(PurpleLogType type, const char *name, PurpleAcc
char *logfile = g_strdup_printf("%s.log", purple_normalize(account, name));
char *pathstr = g_build_filename(purple_user_dir(), "logs", logfile, NULL);
int size;
- struct stat st;
+ GStatBuf st;
if (g_stat(pathstr, &st))
size = 0;
@@ -2038,14 +2065,14 @@ static void old_logger_get_log_sets(PurpleLogSetCallback cb, GHashTable *sets)
!found && gnode != NULL;
gnode = purple_blist_node_get_sibling_next(gnode))
{
- if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
+ if (!PURPLE_IS_GROUP(gnode))
continue;
for (cnode = purple_blist_node_get_first_child(gnode);
!found && cnode != NULL;
cnode = purple_blist_node_get_sibling_next(cnode))
{
- if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
+ if (!PURPLE_IS_CONTACT(cnode))
continue;
for (bnode = purple_blist_node_get_first_child(cnode);
diff --git a/libpurple/log.h b/libpurple/log.h
index 59249b5162..2812c0f434 100644
--- a/libpurple/log.h
+++ b/libpurple/log.h
@@ -1,9 +1,3 @@
-/**
- * @file log.h Logging API
- * @ingroup core
- * @see @ref log-signals
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -24,11 +18,20 @@
* 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_LOG_H_
#define _PURPLE_LOG_H_
+/**
+ * SECTION:log
+ * @section_id: libpurple-log
+ * @short_description: <filename>log.h</filename>
+ * @title: Logging API
+ * @see_also: <link linkend="chapter-signals-log">Log signals</link>
+ */
#include <stdio.h>
+#define PURPLE_TYPE_LOG (purple_log_get_type())
/********************************************************
* DATA STRUCTURES **************************************
@@ -50,68 +53,76 @@ typedef enum {
} PurpleLogReadFlags;
#include "account.h"
-#include "conversation.h"
+#include "conversations.h"
typedef void (*PurpleLogSetCallback) (GHashTable *sets, PurpleLogSet *set);
/**
+ * PurpleLogLogger:
+ * @name: The logger's name
+ * @id: An identifier to refer to this logger
+ * @create: This gets called when the log is first created. I don't think
+ * this is actually needed.
+ * @write: This is used to write to the log file
+ * @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
+ * @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
+ * default implementation is used
+ * @list_syslog: This function returns a sorted #GList of available system
+ * #PurpleLog's
+ * @get_log_sets: Adds #PurpleLogSet's to a #GHashTable. By passing the data in
+ * the #PurpleLogSet's to list, the caller can get every
+ * available #PurpleLog from the logger. Loggers using
+ * purple_log_common_writer() (or otherwise storing their logs in
+ * the same directory structure as the stock loggers) do not
+ * need to implement this function.
+ * <sbr/>Loggers which implement this function must create a
+ * #PurpleLogSet, then call @cb with @sets and the newly created
+ * #PurpleLogSet.
+ * @remove: Attempts to delete the specified log, indicating success or
+ * failure
+ * @is_deletable: Tests whether a log is deletable
+ *
* A log logger.
*
- * This struct gets filled out and is included in the PurpleLog. It contains everything
- * needed to write and read from logs.
+ * This struct gets filled out and is included in the PurpleLog. It contains
+ * everything needed to write and read from logs.
*/
struct _PurpleLogLogger {
- char *name; /**< The logger's name */
- char *id; /**< an identifier to refer to this logger */
+ char *name;
+ char *id;
- /** This gets called when the log is first created.
- I don't think this is actually needed. */
void (*create)(PurpleLog *log);
- /** This is used to write to the log file */
gsize (*write)(PurpleLog *log,
PurpleMessageFlags type,
const char *from,
time_t time,
const char *message);
- /** Called when the log is destroyed */
void (*finalize)(PurpleLog *log);
- /** This function returns a sorted GList of available PurpleLogs */
GList *(*list)(PurpleLogType type, const char *name, PurpleAccount *account);
- /** Given one of the logs returned by the logger's list function,
- * this returns the contents of the log in GtkIMHtml markup */
char *(*read)(PurpleLog *log, PurpleLogReadFlags *flags);
- /** Given one of the logs returned by the logger's list function,
- * this returns the size of the log in bytes */
int (*size)(PurpleLog *log);
- /** Returns the total size of all the logs. If this is undefined a default
- * implementation is used */
int (*total_size)(PurpleLogType type, const char *name, PurpleAccount *account);
- /** This function returns a sorted GList of available system PurpleLogs */
GList *(*list_syslog)(PurpleAccount *account);
- /** Adds PurpleLogSets to a GHashTable. By passing the data in the PurpleLogSets
- * to list, the caller can get every available PurpleLog from the logger.
- * Loggers using purple_log_common_writer() (or otherwise storing their
- * logs in the same directory structure as the stock loggers) do not
- * need to implement this function.
- *
- * Loggers which implement this function must create a PurpleLogSet,
- * then call @a cb with @a sets and the newly created PurpleLogSet. */
void (*get_log_sets)(PurpleLogSetCallback cb, GHashTable *sets);
- /* Attempts to delete the specified log, indicating success or failure */
gboolean (*remove)(PurpleLog *log);
- /* Tests whether a log is deletable */
gboolean (*is_deletable)(PurpleLog *log);
+ /*< private >*/
void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
@@ -119,32 +130,40 @@ struct _PurpleLogLogger {
};
/**
+ * PurpleLog:
+ * @type: The type of log this is
+ * @name: The name of this log
+ * @account: The account this log is taking place on
+ * @conv: The conversation being logged
+ * @time: The time this conversation started, converted to the local
+ * timezone
+ * @logger: The logging mechanism this log is to use
+ * @logger_data: Data used by the log logger
+ * @tm: The time this conversation started, saved with original
+ * timezone data, if available and if struct tm has the BSD
+ * timezone fields, else %NULL. Do NOT modify anything in this
+ * struct.
+ *
* A log. Not the wooden type.
*/
struct _PurpleLog {
- PurpleLogType type; /**< The type of log this is */
- char *name; /**< The name of this log */
- PurpleAccount *account; /**< The account this log is taking
- place on */
- PurpleConversation *conv; /**< The conversation being logged */
- time_t time; /**< The time this conversation
- started, converted to the local timezone */
-
- PurpleLogLogger *logger; /**< The logging mechanism this log
- is to use */
- void *logger_data; /**< Data used by the log logger */
- struct tm *tm; /**< The time this conversation
- started, saved with original
- timezone data, if available and
- if struct tm has the BSD
- timezone fields, else @c NULL.
- Do NOT modify anything in this struct.*/
+ PurpleLogType type;
+ char *name;
+ PurpleAccount *account;
+ PurpleConversation *conv;
+ time_t time;
+
+ PurpleLogLogger *logger;
+ void *logger_data;
+ struct tm *tm;
/* IMPORTANT: Some code in log.c allocates these without zeroing them.
* IMPORTANT: Update that code if you add members here. */
};
/**
+ * PurpleLogCommonLoggerData:
+ *
* A common logger_data struct containing a file handle and path, as well
* as a pointer to something else for additional data.
*/
@@ -155,73 +174,85 @@ struct _PurpleLogCommonLoggerData {
};
/**
+ * PurpleLogSet:
+ * @type: The type of logs available
+ * @name: The name of the logs available
+ * @account: The account the available logs took place on. This will be
+ * %NULL if the account no longer exists. (Depending on a
+ * logger's implementation of list, it may not be possible to
+ * load such logs.)
+ * @buddy: Is this (account, name) a buddy on the buddy list?
+ * @normalized_name: The normalized version of @name. It must be set, and may
+ * be set to the same pointer value as @name.
+ *
* Describes available logs.
*
* By passing the elements of this struct to purple_log_get_logs(), the caller
* can get all available PurpleLogs.
*/
struct _PurpleLogSet {
- PurpleLogType type; /**< The type of logs available */
- char *name; /**< The name of the logs available */
- PurpleAccount *account; /**< The account the available logs
- took place on. This will be
- @c NULL if the account no longer
- exists. (Depending on a
- logger's implementation of
- list, it may not be possible
- to load such logs.) */
- gboolean buddy; /**< Is this (account, name) a buddy
- on the buddy list? */
- char *normalized_name; /**< The normalized version of
- @a name. It must be set, and
- may be set to the same pointer
- value as @a name. */
+ PurpleLogType type;
+ char *name;
+ PurpleAccount *account;
+ gboolean buddy;
+ char *normalized_name;
/* IMPORTANT: Some code in log.c allocates these without zeroing them.
* IMPORTANT: Update that code if you add members here. */
};
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/***************************************/
-/** @name Log Functions */
+/* Log Functions */
/***************************************/
-/*@{*/
/**
- * Creates a new log
+ * purple_log_get_type:
*
- * @param type The type of log this is.
- * @param name The name of this conversation (buddy name, chat name,
+ * Returns: The #GType for the #PurpleLog boxed structure.
+ */
+/* TODO Boxing of PurpleLog is a temporary solution to having a GType for
+ * logs. This should rather be a GObject instead of a GBoxed.
+ */
+GType purple_log_get_type(void);
+
+/**
+ * purple_log_new:
+ * @type: The type of log this is.
+ * @name: The name of this conversation (buddy name, chat name,
* etc.)
- * @param account The account the conversation is occurring on
- * @param conv The conversation being logged
- * @param time The time this conversation started
- * @param tm The time this conversation started, with timezone data,
+ * @account: The account the conversation is occurring on
+ * @conv: The conversation being logged
+ * @time: The time this conversation started
+ * @tm: The time this conversation started, with timezone data,
* if available and if struct tm has the BSD timezone fields.
- * @return The new log
+ *
+ * Creates a new log
+ *
+ * Returns: The new log
*/
PurpleLog *purple_log_new(PurpleLogType type, const char *name, PurpleAccount *account,
PurpleConversation *conv, time_t time, const struct tm *tm);
/**
- * Frees a log
+ * purple_log_free:
+ * @log: The log to destroy
*
- * @param log The log to destroy
+ * Frees a log
*/
void purple_log_free(PurpleLog *log);
/**
- * Writes to a log file. Assumes you have checked preferences already.
- *
- * @param log The log to write to
- * @param type The type of message being logged
- * @param from Whom this message is coming from, or @c NULL for
+ * purple_log_write:
+ * @log: The log to write to
+ * @type: The type of message being logged
+ * @from: Whom this message is coming from, or %NULL for
* system messages
- * @param time A timestamp in UNIX time
- * @param message The message to log
+ * @time: A timestamp in UNIX time
+ * @message: The message to log
+ *
+ * Writes to a log file. Assumes you have checked preferences already.
*/
void purple_log_write(PurpleLog *log,
PurpleMessageFlags type,
@@ -230,26 +261,31 @@ void purple_log_write(PurpleLog *log,
const char *message);
/**
- * Reads from a log
+ * purple_log_read:
+ * @log: The log to read from
+ * @flags: The returned logging flags.
*
- * @param log The log to read from
- * @param flags The returned logging flags.
+ * Reads from a log
*
- * @return The contents of this log in Purple Markup.
+ * Returns: The contents of this log in Purple Markup.
*/
char *purple_log_read(PurpleLog *log, PurpleLogReadFlags *flags);
/**
+ * purple_log_get_logs:
+ * @type: The type of the log
+ * @name: The name of the log
+ * @account: The account
+ *
* Returns a list of all available logs
*
- * @param type The type of the log
- * @param name The name of the log
- * @param account The account
- * @return A sorted list of PurpleLogs
+ * Returns: A sorted list of PurpleLogs
*/
GList *purple_log_get_logs(PurpleLogType type, const char *name, PurpleAccount *account);
/**
+ * purple_log_get_log_sets:
+ *
* Returns a GHashTable of PurpleLogSets.
*
* A "log set" here means the information necessary to gather the
@@ -263,114 +299,132 @@ GList *purple_log_get_logs(PurpleLogType type, const char *name, PurpleAccount *
* destroyed. If a PurpleLogSet is removed from the GHashTable, it
* must be freed with purple_log_set_free().
*
- * @return A GHashTable of all available unique PurpleLogSets
+ * Returns: A GHashTable of all available unique PurpleLogSets
*/
GHashTable *purple_log_get_log_sets(void);
/**
+ * purple_log_get_system_logs:
+ * @account: The account
+ *
* Returns a list of all available system logs
*
- * @param account The account
- * @return A sorted list of PurpleLogs
+ * Returns: A sorted list of PurpleLogs
*/
GList *purple_log_get_system_logs(PurpleAccount *account);
/**
+ * purple_log_get_size:
+ * @log: The log
+ *
* Returns the size of a log
*
- * @param log The log
- * @return The size of the log, in bytes
+ * Returns: The size of the log, in bytes
*/
int purple_log_get_size(PurpleLog *log);
/**
+ * purple_log_get_total_size:
+ * @type: The type of the log
+ * @name: The name of the log
+ * @account: The account
+ *
* Returns the size, in bytes, of all available logs in this conversation
*
- * @param type The type of the log
- * @param name The name of the log
- * @param account The account
- * @return The size in bytes
+ * Returns: The size in bytes
*/
int purple_log_get_total_size(PurpleLogType type, const char *name, PurpleAccount *account);
/**
+ * purple_log_get_activity_score:
+ * @type: The type of the log
+ * @name: The name of the log
+ * @account: The account
+ *
* Returns the activity score of a log, based on total size in bytes,
* which is then decayed based on age
*
- * @param type The type of the log
- * @param name The name of the log
- * @param account The account
- * @return The activity score
- *
- * @since 2.6.0
+ * Returns: The activity score
*/
int purple_log_get_activity_score(PurpleLogType type, const char *name, PurpleAccount *account);
/**
+ * purple_log_is_deletable:
+ * @log: The log
+ *
* Tests whether a log is deletable
*
- * A return value of @c FALSE indicates that purple_log_delete() will fail on this
- * log, unless something changes between the two calls. A return value of @c TRUE,
+ * A return value of %FALSE indicates that purple_log_delete() will fail on this
+ * log, unless something changes between the two calls. A return value of %TRUE,
* however, does not guarantee the log can be deleted.
*
- * @param log The log
- * @return A boolean indicating if the log is deletable
+ * Returns: A boolean indicating if the log is deletable
*/
gboolean purple_log_is_deletable(PurpleLog *log);
/**
+ * purple_log_delete:
+ * @log: The log
+ *
* Deletes a log
*
- * @param log The log
- * @return A boolean indicating success or failure
+ * Returns: A boolean indicating success or failure
*/
gboolean purple_log_delete(PurpleLog *log);
/**
+ * purple_log_get_log_dir:
+ * @type: The type of the log.
+ * @name: The name of the log.
+ * @account: The account.
+ *
* Returns the default logger directory Purple uses for a given account
* and username. This would be where Purple stores logs created by
* the built-in text or HTML loggers.
*
- * @param type The type of the log.
- * @param name The name of the log.
- * @param account The account.
- * @return The default logger directory for Purple.
+ * Returns: The default logger directory for Purple.
*/
char *purple_log_get_log_dir(PurpleLogType type, const char *name, PurpleAccount *account);
/**
+ * purple_log_compare:
+ * @y: A PurpleLog
+ * @z: Another PurpleLog
+ *
* Implements GCompareFunc for PurpleLogs
*
- * @param y A PurpleLog
- * @param z Another PurpleLog
- * @return A value as specified by GCompareFunc
+ * Returns: A value as specified by GCompareFunc
*/
gint purple_log_compare(gconstpointer y, gconstpointer z);
/**
+ * purple_log_set_compare:
+ * @y: A PurpleLogSet
+ * @z: Another PurpleLogSet
+ *
* Implements GCompareFunc for PurpleLogSets
*
- * @param y A PurpleLogSet
- * @param z Another PurpleLogSet
- * @return A value as specified by GCompareFunc
+ * Returns: A value as specified by GCompareFunc
*/
gint purple_log_set_compare(gconstpointer y, gconstpointer z);
/**
- * Frees a log set
+ * purple_log_set_free:
+ * @set: The log set to destroy
*
- * @param set The log set to destroy
+ * Frees a log set
*/
void purple_log_set_free(PurpleLogSet *set);
-/*@}*/
-
/******************************************/
-/** @name Common Logger Functions */
+/* Common Logger Functions */
/******************************************/
-/*@{*/
/**
+ * purple_log_common_writer:
+ * @log: The log to write to.
+ * @ext: The file extension to give to this log file.
+ *
* Opens a new log file in the standard Purple log location
* with the given file extension, named for the current time,
* for writing. If a log file is already open, the existing
@@ -379,205 +433,216 @@ void purple_log_set_free(PurpleLogSet *set);
* file handle and log path.
*
* This function is intended to be used as a "common"
- * implementation of a logger's @c write function.
+ * implementation of a logger's <literal>write</literal> function.
* It should only be passed to purple_log_logger_new() and never
* called directly.
- *
- * @param log The log to write to.
- * @param ext The file extension to give to this log file.
*/
void purple_log_common_writer(PurpleLog *log, const char *ext);
/**
+ * purple_log_common_lister:
+ * @type: The type of the logs being listed.
+ * @name: The name of the log.
+ * @account: The account of the log.
+ * @ext: The file extension this log format uses.
+ * @logger: A reference to the logger struct for this log.
+ *
* Returns a sorted GList of PurpleLogs of the requested type.
*
* This function should only be used with logs that are written
* with purple_log_common_writer(). It's intended to be used as
- * a "common" implementation of a logger's @c list function.
+ * a "common" implementation of a logger's <literal>list</literal> function.
* It should only be passed to purple_log_logger_new() and never
* called directly.
*
- * @param type The type of the logs being listed.
- * @param name The name of the log.
- * @param account The account of the log.
- * @param ext The file extension this log format uses.
- * @param logger A reference to the logger struct for this log.
- *
- * @return A sorted GList of PurpleLogs matching the parameters.
+ * Returns: A sorted GList of PurpleLogs matching the parameters.
*/
GList *purple_log_common_lister(PurpleLogType type, const char *name,
PurpleAccount *account, const char *ext,
PurpleLogLogger *logger);
/**
+ * purple_log_common_total_sizer:
+ * @type: The type of the logs being sized.
+ * @name: The name of the logs to size
+ * (e.g. the username or chat name).
+ * @account: The account of the log.
+ * @ext: The file extension this log format uses.
+ *
* Returns the total size of all the logs for a given user, with
* a given extension.
*
* This function should only be used with logs that are written
* with purple_log_common_writer(). It's intended to be used as
- * a "common" implementation of a logger's @c total_size function.
- * It should only be passed to purple_log_logger_new() and never
+ * a "common" implementation of a logger's <literal>total_size</literal>
+ * function. It should only be passed to purple_log_logger_new() and never
* called directly.
*
- * @param type The type of the logs being sized.
- * @param name The name of the logs to size
- * (e.g. the username or chat name).
- * @param account The account of the log.
- * @param ext The file extension this log format uses.
- *
- * @return The size of all the logs with the specified extension
+ * Returns: The size of all the logs with the specified extension
* for the specified user.
*/
int purple_log_common_total_sizer(PurpleLogType type, const char *name,
PurpleAccount *account, const char *ext);
/**
+ * purple_log_common_sizer:
+ * @log: The PurpleLog to size.
+ *
* Returns the size of a given PurpleLog.
*
* This function should only be used with logs that are written
* with purple_log_common_writer(). It's intended to be used as
- * a "common" implementation of a logger's @c size function.
+ * a "common" implementation of a logger's <literal>size</literal> function.
* It should only be passed to purple_log_logger_new() and never
* called directly.
*
- * @param log The PurpleLog to size.
- *
- * @return An integer indicating the size of the log in bytes.
+ * Returns: An integer indicating the size of the log in bytes.
*/
int purple_log_common_sizer(PurpleLog *log);
/**
+ * purple_log_common_deleter:
+ * @log: The PurpleLog to delete.
+ *
* Deletes a log
*
* This function should only be used with logs that are written
* with purple_log_common_writer(). It's intended to be used as
- * a "common" implementation of a logger's @c delete function.
+ * a "common" implementation of a logger's <literal>delete</literal> function.
* It should only be passed to purple_log_logger_new() and never
* called directly.
*
- * @param log The PurpleLog to delete.
- *
- * @return A boolean indicating success or failure.
+ * Returns: A boolean indicating success or failure.
*/
gboolean purple_log_common_deleter(PurpleLog *log);
/**
+ * purple_log_common_is_deletable:
+ * @log: The PurpleLog to check.
+ *
* Checks to see if a log is deletable
*
* This function should only be used with logs that are written
* with purple_log_common_writer(). It's intended to be used as
- * a "common" implementation of a logger's @c is_deletable function.
- * It should only be passed to purple_log_logger_new() and never
+ * a "common" implementation of a logger's <literal>is_deletable</literal>
+ * function. It should only be passed to purple_log_logger_new() and never
* called directly.
*
- * @param log The PurpleLog to check.
- *
- * @return A boolean indicating if the log is deletable.
+ * Returns: A boolean indicating if the log is deletable.
*/
gboolean purple_log_common_is_deletable(PurpleLog *log);
-/*@}*/
-
/******************************************/
-/** @name Logger Functions */
+/* Logger Functions */
/******************************************/
-/*@{*/
/**
+ * purple_log_logger_new:
+ * @id: The logger's id.
+ * @name: The logger's name.
+ * @functions: The number of functions being passed. The following
+ * functions are currently available (in order):
+ * <literal>create</literal>, <literal>write</literal>,
+ * <literal>finalize</literal>, <literal>list</literal>,
+ * <literal>read</literal>, <literal>size</literal>,
+ * <literal>total_size</literal>, <literal>list_syslog</literal>,
+ * <literal>get_log_sets</literal>, <literal>remove</literal>,
+ * <literal>is_deletable</literal>.
+ * For details on these functions, see PurpleLogLogger.
+ * Functions may not be skipped. For example, passing
+ * <literal>create</literal> and <literal>write</literal> is
+ * acceptable (for a total of two functions). Passing
+ * <literal>create</literal> and <literal>finalize</literal>,
+ * however, is not. To accomplish that, the caller must pass
+ * <literal>create</literal>, %NULL (a placeholder for
+ * <literal>write</literal>), and <literal>finalize</literal>
+ * (for a total of 3 functions).
+ *
* Creates a new logger
*
- * @param id The logger's id.
- * @param name The logger's name.
- * @param functions The number of functions being passed. The following
- * functions are currently available (in order): @c create,
- * @c write, @c finalize, @c list, @c read, @c size,
- * @c total_size, @c list_syslog, @c get_log_sets,
- * @c remove, @c is_deletable.
- * For details on these functions, see PurpleLogLogger.
- * Functions may not be skipped. For example, passing
- * @c create and @c write is acceptable (for a total of
- * two functions). Passing @c create and @c finalize,
- * however, is not. To accomplish that, the caller must
- * pass @c create, @c NULL (a placeholder for @c write),
- * and @c finalize (for a total of 3 functions).
- *
- * @return The new logger
+ * Returns: The new logger
*/
PurpleLogLogger *purple_log_logger_new(const char *id, const char *name, int functions, ...);
/**
- * Frees a logger
+ * purple_log_logger_free:
+ * @logger: The logger to free
*
- * @param logger The logger to free
+ * Frees a logger
*/
void purple_log_logger_free(PurpleLogLogger *logger);
/**
- * Adds a new logger
+ * purple_log_logger_add:
+ * @logger: The new logger to add
*
- * @param logger The new logger to add
+ * Adds a new logger
*/
void purple_log_logger_add (PurpleLogLogger *logger);
/**
+ * purple_log_logger_remove:
+ * @logger: The logger to remove
*
* Removes a logger
- *
- * @param logger The logger to remove
*/
void purple_log_logger_remove (PurpleLogLogger *logger);
/**
+ * purple_log_logger_set:
+ * @logger: The logger to set
*
* Sets the current logger
- *
- * @param logger The logger to set
*/
void purple_log_logger_set (PurpleLogLogger *logger);
/**
+ * purple_log_logger_get:
*
* Returns the current logger
*
- * @return logger The current logger
+ * Returns: logger The current logger
*/
PurpleLogLogger *purple_log_logger_get (void);
/**
+ * purple_log_logger_get_options:
+ *
* Returns a GList containing the IDs and names of the registered
* loggers.
*
- * @return The list of IDs and names.
+ * Returns: The list of IDs and names.
*/
GList *purple_log_logger_get_options(void);
/**************************************************************************/
-/** @name Log Subsystem */
+/* Log Subsystem */
/**************************************************************************/
-/*@{*/
/**
+ * purple_log_init:
+ *
* Initializes the log subsystem.
*/
void purple_log_init(void);
/**
+ * purple_log_get_handle:
+ *
* Returns the log subsystem handle.
*
- * @return The log subsystem handle.
+ * Returns: The log subsystem handle.
*/
void *purple_log_get_handle(void);
/**
+ * purple_log_uninit:
+ *
* Uninitializes the log subsystem.
*/
void purple_log_uninit(void);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_LOG_H_ */
diff --git a/libpurple/marshallers.list b/libpurple/marshallers.list
index 533197890b..088b422b9b 100644
--- a/libpurple/marshallers.list
+++ b/libpurple/marshallers.list
@@ -6,3 +6,4 @@ VOID:ENUM,STRING,STRING
VOID:ENUM,STRING,STRING,BOOLEAN
VOID:FLAGS,FLAGS
VOID:STRING,STRING,OBJECT,OBJECT
+BOOLEAN:OBJECT
diff --git a/libpurple/media-gst.h b/libpurple/media-gst.h
index 91d9986d8a..c8dba0a691 100644
--- a/libpurple/media-gst.h
+++ b/libpurple/media-gst.h
@@ -1,8 +1,3 @@
-/**
- * @file media-gst.h Media API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -26,14 +21,18 @@
#ifndef _PURPLE_MEDIA_GST_H_
#define _PURPLE_MEDIA_GST_H_
+/**
+ * SECTION:media-gst
+ * @section_id: libpurple-media-gst
+ * @short_description: <filename>media-gst.h</filename>
+ * @title: Media Element API
+ */
#include "media.h"
#include "mediamanager.h"
#include <gst/gst.h>
-G_BEGIN_DECLS
-
#define PURPLE_TYPE_MEDIA_ELEMENT_TYPE (purple_media_element_type_get_type())
#define PURPLE_TYPE_MEDIA_ELEMENT_INFO (purple_media_element_info_get_type())
#define PURPLE_MEDIA_ELEMENT_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA_ELEMENT_INFO, PurpleMediaElementInfo))
@@ -42,107 +41,120 @@ G_BEGIN_DECLS
#define PURPLE_IS_MEDIA_ELEMENT_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA_ELEMENT_INFO))
#define PURPLE_MEDIA_ELEMENT_INFO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA_ELEMENT_INFO, PurpleMediaElementInfo))
-/** An opaque structure representing an audio/video source/sink. */
+/**
+ * PurpleMediaElementInfo:
+ *
+ * An opaque structure representing an audio/video source/sink.
+ */
typedef struct _PurpleMediaElementInfo PurpleMediaElementInfo;
typedef struct _PurpleMediaElementInfoClass PurpleMediaElementInfoClass;
+
typedef GstElement *(*PurpleMediaElementCreateCallback)(PurpleMedia *media,
const gchar *session_id, const gchar *participant);
+/**
+ * PurpleMediaElementType:
+ * @PURPLE_MEDIA_ELEMENT_NONE: empty element
+ * @PURPLE_MEDIA_ELEMENT_AUDIO: supports audio
+ * @PURPLE_MEDIA_ELEMENT_VIDEO: supports video
+ * @PURPLE_MEDIA_ELEMENT_AUDIO_VIDEO: supports audio and video
+ * @PURPLE_MEDIA_ELEMENT_NO_SRCS: has no src pads
+ * @PURPLE_MEDIA_ELEMENT_ONE_SRC: has one src pad
+ * @PURPLE_MEDIA_ELEMENT_MULTI_SRC: has multiple src pads
+ * @PURPLE_MEDIA_ELEMENT_REQUEST_SRC: src pads must be requested
+ * @PURPLE_MEDIA_ELEMENT_NO_SINKS: has no sink pads
+ * @PURPLE_MEDIA_ELEMENT_ONE_SINK: has one sink pad
+ * @PURPLE_MEDIA_ELEMENT_MULTI_SINK: has multiple sink pads
+ * @PURPLE_MEDIA_ELEMENT_REQUEST_SINK: sink pads must be requested
+ * @PURPLE_MEDIA_ELEMENT_UNIQUE: This element is unique and only one
+ * instance of it should be created at a
+ * time
+ * @PURPLE_MEDIA_ELEMENT_SRC: can be set as an active src
+ * @PURPLE_MEDIA_ELEMENT_SINK: can be set as an active sink
+ */
typedef enum {
- PURPLE_MEDIA_ELEMENT_NONE = 0, /** empty element */
- PURPLE_MEDIA_ELEMENT_AUDIO = 1, /** supports audio */
- PURPLE_MEDIA_ELEMENT_VIDEO = 1 << 1, /** supports video */
+ PURPLE_MEDIA_ELEMENT_NONE = 0,
+ PURPLE_MEDIA_ELEMENT_AUDIO = 1,
+ PURPLE_MEDIA_ELEMENT_VIDEO = 1 << 1,
PURPLE_MEDIA_ELEMENT_AUDIO_VIDEO = PURPLE_MEDIA_ELEMENT_AUDIO
- | PURPLE_MEDIA_ELEMENT_VIDEO, /** supports audio and video */
-
- PURPLE_MEDIA_ELEMENT_NO_SRCS = 0, /** has no src pads */
- PURPLE_MEDIA_ELEMENT_ONE_SRC = 1 << 2, /** has one src pad */
- PURPLE_MEDIA_ELEMENT_MULTI_SRC = 1 << 3, /** has multiple src pads */
- PURPLE_MEDIA_ELEMENT_REQUEST_SRC = 1 << 4, /** src pads must be requested */
-
- PURPLE_MEDIA_ELEMENT_NO_SINKS = 0, /** has no sink pads */
- PURPLE_MEDIA_ELEMENT_ONE_SINK = 1 << 5, /** has one sink pad */
- PURPLE_MEDIA_ELEMENT_MULTI_SINK = 1 << 6, /** has multiple sink pads */
- PURPLE_MEDIA_ELEMENT_REQUEST_SINK = 1 << 7, /** sink pads must be requested */
-
- PURPLE_MEDIA_ELEMENT_UNIQUE = 1 << 8, /** This element is unique and
- only one instance of it should
- be created at a time */
-
- PURPLE_MEDIA_ELEMENT_SRC = 1 << 9, /** can be set as an active src */
- PURPLE_MEDIA_ELEMENT_SINK = 1 << 10, /** can be set as an active sink */
+ | PURPLE_MEDIA_ELEMENT_VIDEO,
+ PURPLE_MEDIA_ELEMENT_NO_SRCS = 0,
+ PURPLE_MEDIA_ELEMENT_ONE_SRC = 1 << 2,
+ PURPLE_MEDIA_ELEMENT_MULTI_SRC = 1 << 3,
+ PURPLE_MEDIA_ELEMENT_REQUEST_SRC = 1 << 4,
+ PURPLE_MEDIA_ELEMENT_NO_SINKS = 0,
+ PURPLE_MEDIA_ELEMENT_ONE_SINK = 1 << 5,
+ PURPLE_MEDIA_ELEMENT_MULTI_SINK = 1 << 6,
+ PURPLE_MEDIA_ELEMENT_REQUEST_SINK = 1 << 7,
+ PURPLE_MEDIA_ELEMENT_UNIQUE = 1 << 8,
+ PURPLE_MEDIA_ELEMENT_SRC = 1 << 9,
+ PURPLE_MEDIA_ELEMENT_SINK = 1 << 10,
} PurpleMediaElementType;
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**
- * Gets the element type's GType.
+ * purple_media_element_type_get_type:
*
- * @return The element type's GType.
+ * Gets the element type's GType.
*
- * @since 2.6.0
+ * Returns: The element type's GType.
*/
GType purple_media_element_type_get_type(void);
/**
- * Gets the element info's GType.
+ * purple_media_element_info_get_type:
*
- * @return The element info's GType.
+ * Gets the element info's GType.
*
- * @since 2.6.0
+ * Returns: The element info's GType.
*/
GType purple_media_element_info_get_type(void);
/**
- * Gets the source from a session
+ * purple_media_get_src:
+ * @media: The media object the session is in.
+ * @sess_id: The session id of the session to get the source from.
*
- * @param media The media object the session is in.
- * @param sess_id The session id of the session to get the source from.
- *
- * @return The source retrieved.
+ * Gets the source from a session
*
- * @since 2.6.0
+ * Returns: The source retrieved.
*/
GstElement *purple_media_get_src(PurpleMedia *media, const gchar *sess_id);
/**
- * Gets the tee from a given session/stream.
- *
- * @param media The instance to get the tee from.
- * @param session_id The id of the session to get the tee from.
- * @param participant Optionally, the participant of the stream to get the tee from.
+ * purple_media_get_tee:
+ * @media: The instance to get the tee from.
+ * @session_id: The id of the session to get the tee from.
+ * @participant: Optionally, the participant of the stream to get the tee from.
*
- * @return The GstTee element from the chosen session/stream.
+ * Gets the tee from a given session/stream.
*
- * @since 2.6.0
+ * Returns: The GstTee element from the chosen session/stream.
*/
GstElement *purple_media_get_tee(PurpleMedia *media,
const gchar *session_id, const gchar *participant);
/**
- * Gets the pipeline from the media manager.
- *
- * @param manager The media manager to get the pipeline from.
+ * purple_media_manager_get_pipeline:
+ * @manager: The media manager to get the pipeline from.
*
- * @return The pipeline.
+ * Gets the pipeline from the media manager.
*
- * @since 2.6.0
+ * Returns: The pipeline.
*/
GstElement *purple_media_manager_get_pipeline(PurpleMediaManager *manager);
/**
- * Returns a GStreamer source or sink for audio or video.
- *
- * @param manager The media manager to use to obtain the source/sink.
- * @param type The type of source/sink to get.
- * @param media The media call this element is requested for.
- * @param session_id The id of the session this element is requested for or NULL.
- * @param participant The remote user this element is requested for or NULL.
- *
- * @since 2.6.0
+ * purple_media_manager_get_element:
+ * @manager: The media manager to use to obtain the source/sink.
+ * @type: The type of source/sink to get.
+ * @media: The media call this element is requested for.
+ * @session_id: The id of the session this element is requested for or NULL.
+ * @participant: The remote user this element is requested for or NULL.
+ *
+ * Returns: A GStreamer source or sink for audio or video.
*/
GstElement *purple_media_manager_get_element(PurpleMediaManager *manager,
PurpleMediaSessionType type, PurpleMedia *media,
@@ -160,27 +172,25 @@ PurpleMediaElementInfo *purple_media_manager_get_active_element(
PurpleMediaManager *manager, PurpleMediaElementType type);
/**
+ * purple_media_manager_set_video_caps:
+ * @manager: The media manager to set the media formats.
+ * @caps: Set of allowed media formats.
+ *
* Reduces media formats supported by the video source to given set.
*
* Useful to force negotiation of smaller picture resolution more suitable for
* use with particular codec and communication protocol without rescaling.
- *
- * @param manager The media manager to set the media formats.
- * @param caps Set of allowed media formats.
- *
- * @since 2.8.0
*/
void purple_media_manager_set_video_caps(PurpleMediaManager *manager,
GstCaps *caps);
/**
- * Returns current set of media formats limiting the output from video source.
- *
- * @param manager The media manager to get the media formats from.
+ * purple_media_manager_get_video_caps:
+ * @manager: The media manager to get the media formats from.
*
- * @return @c GstCaps limiting the video source's formats.
+ * Returns current set of media formats limiting the output from video source.
*
- * @since 2.8.0
+ * Returns: #GstCaps limiting the video source's formats.
*/
GstCaps *purple_media_manager_get_video_caps(PurpleMediaManager *manager);
@@ -192,10 +202,6 @@ GstElement *purple_media_element_info_call_create(
PurpleMediaElementInfo *info, PurpleMedia *media,
const gchar *session_id, const gchar *participant);
-#ifdef __cplusplus
-}
-#endif
-
G_END_DECLS
#endif /* _PURPLE_MEDIA_GST_H_ */
diff --git a/libpurple/media.c b/libpurple/media.c
index 98ba92034b..631dbcb64c 100644
--- a/libpurple/media.c
+++ b/libpurple/media.c
@@ -1,8 +1,3 @@
-/**
- * @file media.c Media API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -44,23 +39,6 @@
typedef struct _PurpleMediaSession PurpleMediaSession;
/** @copydoc _PurpleMediaStream */
typedef struct _PurpleMediaStream PurpleMediaStream;
-/** @copydoc _PurpleMediaClass */
-typedef struct _PurpleMediaClass PurpleMediaClass;
-/** @copydoc _PurpleMediaPrivate */
-typedef struct _PurpleMediaPrivate PurpleMediaPrivate;
-
-/** The media class */
-struct _PurpleMediaClass
-{
- GObjectClass parent_class; /**< The parent class. */
-};
-
-/** The media class's private data */
-struct _PurpleMedia
-{
- GObject parent; /**< The parent of this object. */
- PurpleMediaPrivate *priv; /**< The private data of this object. */
-};
struct _PurpleMediaSession
{
@@ -201,7 +179,8 @@ purple_media_class_init (PurpleMediaClass *klass)
"Purple Media Manager",
"The media manager that contains this media session.",
PURPLE_TYPE_MEDIA_MANAGER,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
/*
* This one should be PURPLE_TYPE_MEDIA_BACKEND, but it doesn't
@@ -212,13 +191,13 @@ purple_media_class_init (PurpleMediaClass *klass)
"Purple Media Backend",
"The backend object this media object uses.",
G_TYPE_OBJECT,
- G_PARAM_READABLE));
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class, PROP_ACCOUNT,
- g_param_spec_pointer("account",
- "PurpleAccount",
+ g_param_spec_pointer("account", "PurpleAccount",
"The account this media session is on.",
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class, PROP_CONFERENCE_TYPE,
g_param_spec_string("conference-type",
@@ -226,20 +205,22 @@ purple_media_class_init (PurpleMediaClass *klass)
"The type of conference that this media object "
"has been created to provide.",
NULL,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class, PROP_INITIATOR,
g_param_spec_boolean("initiator",
"initiator",
"If the local user initiated the conference.",
FALSE,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class, PROP_PRPL_DATA,
g_param_spec_pointer("prpl-data",
"gpointer",
"Data the prpl plugin set on the media session.",
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
purple_media_signals[S_ERROR] = g_signal_new("error", G_TYPE_FROM_CLASS(klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL,
@@ -377,7 +358,7 @@ purple_media_set_property (GObject *object, guint prop_id, const GValue *value,
media->priv->manager = g_value_dup_object(value);
break;
case PROP_ACCOUNT:
- media->priv->account = g_value_get_pointer(value);
+ media->priv->account = g_value_get_object(value);
break;
case PROP_CONFERENCE_TYPE:
media->priv->conference_type =
@@ -438,7 +419,7 @@ purple_media_get_property (GObject *object, guint prop_id, GValue *value, GParam
g_value_set_object(value, media->priv->backend);
break;
case PROP_ACCOUNT:
- g_value_set_pointer(value, media->priv->account);
+ g_value_set_object(value, media->priv->account);
break;
case PROP_CONFERENCE_TYPE:
g_value_set_string(value,
@@ -1097,7 +1078,7 @@ purple_media_add_stream(PurpleMedia *media, const gchar *sess_id,
media->priv->participants = g_list_prepend(
media->priv->participants, g_strdup(who));
- g_signal_emit_by_name(media, "state-changed",
+ g_signal_emit(media, purple_media_signals[STATE_CHANGED], 0,
PURPLE_MEDIA_STATE_NEW, NULL, who);
}
diff --git a/libpurple/media.h b/libpurple/media.h
index b001b7511c..8578ba5d19 100644
--- a/libpurple/media.h
+++ b/libpurple/media.h
@@ -1,8 +1,3 @@
-/**
- * @file media.h Media API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -26,15 +21,19 @@
#ifndef _PURPLE_MEDIA_H_
#define _PURPLE_MEDIA_H_
-
-#include "media/candidate.h"
-#include "media/codec.h"
-#include "media/enum-types.h"
+/**
+ * SECTION:media
+ * @section_id: libpurple-media
+ * @short_description: <filename>media.h</filename>
+ * @title: Media Object API
+ */
#include <glib.h>
#include <glib-object.h>
-G_BEGIN_DECLS
+#include "media/candidate.h"
+#include "media/codec.h"
+#include "media/enum-types.h"
#define PURPLE_TYPE_MEDIA (purple_media_get_type())
#define PURPLE_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA, PurpleMedia))
@@ -43,107 +42,138 @@ G_BEGIN_DECLS
#define PURPLE_IS_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA))
#define PURPLE_MEDIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA, PurpleMediaClass))
-/** An opaque structure representing a media call. */
typedef struct _PurpleMedia PurpleMedia;
#include "signals.h"
#include "util.h"
-#ifdef __cplusplus
-extern "C" {
+#ifdef USE_VV
+
+typedef struct _PurpleMediaClass PurpleMediaClass;
+typedef struct _PurpleMediaPrivate PurpleMediaPrivate;
+
+/**
+ * PurpleMedia:
+ *
+ * The media instance
+ */
+struct _PurpleMedia
+{
+ GObject parent;
+
+ /*< private >*/
+ PurpleMediaPrivate *priv;
+};
+
+/**
+ * PurpleMediaClass:
+ *
+ * The media class
+ */
+struct _PurpleMediaClass
+{
+ GObjectClass parent_class;
+
+ /*< private >*/
+ void (*purple_reserved1)(void);
+ void (*purple_reserved2)(void);
+ void (*purple_reserved3)(void);
+ void (*purple_reserved4)(void);
+};
+
#endif
+G_BEGIN_DECLS
+
/**
- * Gets the media class's GType
+ * purple_media_get_type:
*
- * @return The media class's GType.
+ * Gets the media class's GType
*
- * @since 2.6.0
+ * Returns: The media class's GType.
*/
GType purple_media_get_type(void);
/**
- * Gets a list of session IDs.
+ * purple_media_get_session_ids:
+ * @media: The media session from which to retrieve session IDs.
*
- * @param media The media session from which to retrieve session IDs.
- *
- * @return GList of session IDs. The caller must free the list.
+ * Gets a list of session IDs.
*
- * @since 2.6.0
+ * Returns: GList of session IDs. The caller must free the list.
*/
GList *purple_media_get_session_ids(PurpleMedia *media);
/**
- * Gets the PurpleAccount this media session is on.
- *
- * @param media The media session to retrieve the account from.
+ * purple_media_get_account:
+ * @media: The media session to retrieve the account from.
*
- * @return The account retrieved.
+ * Gets the PurpleAccount this media session is on.
*
- * @since 2.6.0
+ * Returns: The account retrieved.
*/
PurpleAccount *purple_media_get_account(PurpleMedia *media);
/**
- * Gets the prpl data from the media session.
+ * purple_media_get_prpl_data:
+ * @media: The media session to retrieve the protocol data from.
*
- * @param media The media session to retrieve the prpl data from.
+ * Gets the protocol data from the media session.
*
- * @return The prpl data retrieved.
- *
- * @since 2.6.0
+ * Returns: The protocol data retrieved.
*/
gpointer purple_media_get_prpl_data(PurpleMedia *media);
/**
- * Sets the prpl data on the media session.
- *
- * @param media The media session to set the prpl data on.
- * @param prpl_data The data to set on the media session.
+ * purple_media_set_prpl_data:
+ * @media: The media session to set the protocol data on.
+ * @protocol_data: The data to set on the media session.
*
- * @since 2.6.0
+ * Sets the protocol data on the media session.
*/
-void purple_media_set_prpl_data(PurpleMedia *media, gpointer prpl_data);
+void purple_media_set_prpl_data(PurpleMedia *media, gpointer protocol_data);
/**
- * Signals an error in the media session.
- *
- * @param media The media object to set the state on.
- * @param error The format of the error message to send in the signal.
- * @param ... The arguments to plug into the format.
+ * purple_media_error:
+ * @media: The media object to set the state on.
+ * @error: The format of the error message to send in the signal.
+ * @...: The arguments to plug into the format.
*
- * @since 2.6.0
+ * Signals an error in the media session.
*/
void purple_media_error(PurpleMedia *media, const gchar *error, ...);
/**
- * Ends all streams that match the given parameters
- *
- * @param media The media object with which to end streams.
- * @param session_id The session to end streams on.
- * @param participant The participant to end streams with.
+ * purple_media_end:
+ * @media: The media object with which to end streams.
+ * @session_id: The session to end streams on.
+ * @participant: The participant to end streams with.
*
- * @since 2.6.0
+ * Ends all streams that match the given parameters
*/
void purple_media_end(PurpleMedia *media, const gchar *session_id,
const gchar *participant);
/**
- * Signals different information about the given stream.
+ * purple_media_stream_info:
+ * @media: The media instance to containing the stream to signal.
+ * @type: The type of info being signaled.
+ * @session_id: The id of the session of the stream being signaled.
+ * @participant: The participant of the stream being signaled.
+ * @local: TRUE if the info originated locally, FALSE if on the remote end.
*
- * @param media The media instance to containing the stream to signal.
- * @param type The type of info being signaled.
- * @param session_id The id of the session of the stream being signaled.
- * @param participant The participant of the stream being signaled.
- * @param local TRUE if the info originated locally, FALSE if on the remote end.
- *
- * @since 2.6.0
+ * Signals different information about the given stream.
*/
void purple_media_stream_info(PurpleMedia *media, PurpleMediaInfoType type,
const gchar *session_id, const gchar *participant,
gboolean local);
/**
+ * purple_media_set_params:
+ * @media: The media object to set the parameters on.
+ * @num_params: The number of parameters to pass
+ * @params: Array of @c GParameter to pass
+ *
* Sets various optional parameters of the media call.
*
* Currently supported are:
@@ -154,59 +184,50 @@ void purple_media_stream_info(PurpleMedia *media, PurpleMediaInfoType type,
* - "sdes-location" : The LOCATION to put in SDES messages
* - "sdes-note" : The NOTE to put in SDES messages
* - "sdes-phone" : The PHONE to put in SDES messages
- *
- * @param media The media object to set the parameters on.
- * @param num_params The number of parameters to pass
- * @param params Array of @c GParameter to pass
- *
- * @since 2.8.0
*/
void purple_media_set_params(PurpleMedia *media,
guint num_params, GParameter *params);
/**
- * Gets the list of optional parameters supported by the media backend.
- *
- * The list is owned by the @c PurpleMedia internals and should NOT be freed.
+ * purple_media_get_available_params:
+ * @media: The media object
*
- * @param media The media object
+ * Gets the list of optional parameters supported by the media backend.
*
- * @return NULL-terminated array of names of supported parameters.
+ * The list is owned by the #PurpleMedia internals and should NOT be freed.
*
- * @since 2.8.0
+ * Returns: NULL-terminated array of names of supported parameters.
*/
const gchar **purple_media_get_available_params(PurpleMedia *media);
/**
- * Checks if given optional parameter is supported by the media backend.
+ * purple_media_param_is_supported:
+ * @media: The media object
+ * @param: name of parameter
*
- * @param media The media object
- * @param param name of parameter
- *
- * @return @c TRUE if backend recognizes the parameter, @c FALSE otherwise.
+ * Checks if given optional parameter is supported by the media backend.
*
- * @since 2.8.0
+ * Returns: %TRUE if backend recognizes the parameter, %FALSE otherwise.
*/
gboolean purple_media_param_is_supported(PurpleMedia *media, const gchar *param);
/**
+ * purple_media_add_stream:
+ * @media: The media object to find the session in.
+ * @sess_id: The session id of the session to add the stream to.
+ * @who: The name of the remote user to add the stream for.
+ * @type: The type of stream to create.
+ * @initiator: Whether or not the local user initiated the stream.
+ * @transmitter: The transmitter to use for the stream.
+ * @num_params: The number of parameters to pass to Farsight.
+ * @params: The parameters to pass to Farsight.
+ *
* Adds a stream to a session.
*
* It only adds a stream to one audio session or video session as
- * the @c sess_id must be unique between sessions.
+ * the @sess_id must be unique between sessions.
*
- * @param media The media object to find the session in.
- * @param sess_id The session id of the session to add the stream to.
- * @param who The name of the remote user to add the stream for.
- * @param type The type of stream to create.
- * @param initiator Whether or not the local user initiated the stream.
- * @param transmitter The transmitter to use for the stream.
- * @param num_params The number of parameters to pass to Farsight.
- * @param params The parameters to pass to Farsight.
- *
- * @return @c TRUE The stream was added successfully, @c FALSE otherwise.
- *
- * @since 2.6.0
+ * Returns: %TRUE The stream was added successfully, %FALSE otherwise.
*/
gboolean purple_media_add_stream(PurpleMedia *media, const gchar *sess_id,
const gchar *who, PurpleMediaSessionType type,
@@ -214,49 +235,45 @@ gboolean purple_media_add_stream(PurpleMedia *media, const gchar *sess_id,
guint num_params, GParameter *params);
/**
- * Gets the session type from a session
- *
- * @param media The media object to find the session in.
- * @param sess_id The session id of the session to get the type from.
+ * purple_media_get_session_type:
+ * @media: The media object to find the session in.
+ * @sess_id: The session id of the session to get the type from.
*
- * @return The retreived session type.
+ * Gets the session type from a session
*
- * @since 2.6.0
+ * Returns: The retreived session type.
*/
PurpleMediaSessionType purple_media_get_session_type(PurpleMedia *media, const gchar *sess_id);
/**
- * Gets the PurpleMediaManager this media session is a part of.
- *
- * @param media The media object to get the manager instance from.
+ * purple_media_get_manager:
+ * @media: The media object to get the manager instance from.
*
- * @return The PurpleMediaManager instance retrieved.
+ * Gets the PurpleMediaManager this media session is a part of.
*
- * @since 2.6.0
+ * Returns: The PurpleMediaManager instance retrieved.
*/
struct _PurpleMediaManager *purple_media_get_manager(PurpleMedia *media);
/**
- * Gets the codecs from a session.
+ * purple_media_get_codecs:
+ * @media: The media object to find the session in.
+ * @sess_id: The session id of the session to get the codecs from.
*
- * @param media The media object to find the session in.
- * @param sess_id The session id of the session to get the codecs from.
- *
- * @return The retreieved codecs.
+ * Gets the codecs from a session.
*
- * @since 2.6.0
+ * Returns: The retreieved codecs.
*/
GList *purple_media_get_codecs(PurpleMedia *media, const gchar *sess_id);
/**
- * Adds remote candidates to the stream.
- *
- * @param media The media object to find the session in.
- * @param sess_id The session id of the session find the stream in.
- * @param participant The name of the remote user to add the candidates for.
- * @param remote_candidates The remote candidates to add.
+ * purple_media_add_remote_candidates:
+ * @media: The media object to find the session in.
+ * @sess_id: The session id of the session find the stream in.
+ * @participant: The name of the remote user to add the candidates for.
+ * @remote_candidates: The remote candidates to add.
*
- * @since 2.6.0
+ * Adds remote candidates to the stream.
*/
void purple_media_add_remote_candidates(PurpleMedia *media,
const gchar *sess_id,
@@ -264,183 +281,166 @@ void purple_media_add_remote_candidates(PurpleMedia *media,
GList *remote_candidates);
/**
- * Gets the local candidates from a stream.
- *
- * @param media The media object to find the session in.
- * @param sess_id The session id of the session to find the stream in.
- * @param participant The name of the remote user to get the candidates from.
+ * purple_media_get_local_candidates:
+ * @media: The media object to find the session in.
+ * @sess_id: The session id of the session to find the stream in.
+ * @participant: The name of the remote user to get the candidates from.
*
- * @since 2.6.0
+ * Gets the local candidates from a stream.
*/
GList *purple_media_get_local_candidates(PurpleMedia *media,
const gchar *sess_id,
const gchar *participant);
/**
- * Gets the active local candidates for the stream.
- *
- * @param media The media object to find the session in.
- * @param sess_id The session id of the session to find the stream in.
- * @param participant The name of the remote user to get the active candidate
+ * purple_media_get_active_local_candidates:
+ * @media: The media object to find the session in.
+ * @sess_id: The session id of the session to find the stream in.
+ * @participant: The name of the remote user to get the active candidate
* from.
*
- * @return The active candidates retrieved.
+ * Gets the active local candidates for the stream.
*
- * @since 2.8.0
+ * Returns: The active candidates retrieved.
*/
GList *purple_media_get_active_local_candidates(PurpleMedia *media,
const gchar *sess_id, const gchar *participant);
/**
- * Gets the active remote candidates for the stream.
- *
- * @param media The media object to find the session in.
- * @param sess_id The session id of the session to find the stream in.
- * @param participant The name of the remote user to get the remote candidate
+ * purple_media_get_active_remote_candidates:
+ * @media: The media object to find the session in.
+ * @sess_id: The session id of the session to find the stream in.
+ * @participant: The name of the remote user to get the remote candidate
* from.
*
- * @return The remote candidates retrieved.
+ * Gets the active remote candidates for the stream.
*
- * @since 2.8.0
+ * Returns: The remote candidates retrieved.
*/
GList *purple_media_get_active_remote_candidates(PurpleMedia *media,
const gchar *sess_id, const gchar *participant);
/**
- * Sets remote candidates from the stream.
- *
- * @param media The media object to find the session in.
- * @param sess_id The session id of the session find the stream in.
- * @param participant The name of the remote user to set the candidates from.
- * @param codecs The list of remote codecs to set.
+ * purple_media_set_remote_codecs:
+ * @media: The media object to find the session in.
+ * @sess_id: The session id of the session find the stream in.
+ * @participant: The name of the remote user to set the candidates from.
+ * @codecs: The list of remote codecs to set.
*
- * @return @c TRUE The codecs were set successfully, or @c FALSE otherwise.
+ * Sets remote candidates from the stream.
*
- * @since 2.6.0
+ * Returns: %TRUE The codecs were set successfully, or %FALSE otherwise.
*/
gboolean purple_media_set_remote_codecs(PurpleMedia *media, const gchar *sess_id,
const gchar *participant, GList *codecs);
/**
- * Returns whether or not the candidates for set of streams are prepared
- *
- * @param media The media object to find the remote user in.
- * @param session_id The session id of the session to check.
- * @param participant The remote user to check for.
+ * purple_media_candidates_prepared:
+ * @media: The media object to find the remote user in.
+ * @session_id: The session id of the session to check.
+ * @participant: The remote user to check for.
*
- * @return @c TRUE All streams for the given session_id/participant combination have candidates prepared, @c FALSE otherwise.
+ * Returns whether or not the candidates for set of streams are prepared
*
- * @since 2.6.0
+ * Returns: %TRUE All streams for the given session_id/participant combination have candidates prepared, %FALSE otherwise.
*/
gboolean purple_media_candidates_prepared(PurpleMedia *media,
const gchar *session_id, const gchar *participant);
/**
- * Sets the send codec for the a session.
+ * purple_media_set_send_codec:
+ * @media: The media object to find the session in.
+ * @sess_id: The session id of the session to set the codec for.
+ * @codec: The codec to set the session to stream.
*
- * @param media The media object to find the session in.
- * @param sess_id The session id of the session to set the codec for.
- * @param codec The codec to set the session to stream.
- *
- * @return @c TRUE The codec was successfully changed, or @c FALSE otherwise.
+ * Sets the send codec for the a session.
*
- * @since 2.6.0
+ * Returns: %TRUE The codec was successfully changed, or %FALSE otherwise.
*/
gboolean purple_media_set_send_codec(PurpleMedia *media, const gchar *sess_id, PurpleMediaCodec *codec);
/**
- * Gets whether a session's codecs are ready to be used.
- *
- * @param media The media object to find the session in.
- * @param sess_id The session id of the session to check.
+ * purple_media_codecs_ready:
+ * @media: The media object to find the session in.
+ * @sess_id: The session id of the session to check.
*
- * @return @c TRUE The codecs are ready, or @c FALSE otherwise.
+ * Gets whether a session's codecs are ready to be used.
*
- * @since 2.6.0
+ * Returns: %TRUE The codecs are ready, or %FALSE otherwise.
*/
gboolean purple_media_codecs_ready(PurpleMedia *media, const gchar *sess_id);
/**
- * Gets whether the local user is the conference/session/stream's initiator.
- *
- * @param media The media instance to find the session in.
- * @param sess_id The session id of the session to check.
- * @param participant The participant of the stream to check.
+ * purple_media_is_initiator:
+ * @media: The media instance to find the session in.
+ * @sess_id: The session id of the session to check.
+ * @participant: The participant of the stream to check.
*
- * @return TRUE if the local user is the stream's initator, else FALSE.
+ * Gets whether the local user is the conference/session/stream's initiator.
*
- * @since 2.6.0
+ * Returns: TRUE if the local user is the stream's initator, else FALSE.
*/
gboolean purple_media_is_initiator(PurpleMedia *media,
const gchar *sess_id, const gchar *participant);
/**
- * Gets whether a streams selected have been accepted.
+ * purple_media_accepted:
+ * @media: The media object to find the session in.
+ * @sess_id: The session id of the session to check.
+ * @participant: The participant to check.
*
- * @param media The media object to find the session in.
- * @param sess_id The session id of the session to check.
- * @param participant The participant to check.
- *
- * @return @c TRUE The selected streams have been accepted, or @c FALSE otherwise.
+ * Gets whether a streams selected have been accepted.
*
- * @since 2.6.0
+ * Returns: %TRUE The selected streams have been accepted, or %FALSE otherwise.
*/
gboolean purple_media_accepted(PurpleMedia *media, const gchar *sess_id,
const gchar *participant);
/**
- * Sets the input volume of all the selected sessions.
+ * purple_media_set_input_volume:
+ * @media: The media object the sessions are in.
+ * @session_id: The session to select (if any).
+ * @level: The level to set the volume to.
*
- * @param media The media object the sessions are in.
- * @param session_id The session to select (if any).
- * @param level The level to set the volume to.
- *
- * @since 2.6.0
+ * Sets the input volume of all the selected sessions.
*/
void purple_media_set_input_volume(PurpleMedia *media, const gchar *session_id, double level);
/**
- * Sets the output volume of all the selected streams.
- *
- * @param media The media object the streams are in.
- * @param session_id The session to limit the streams to (if any).
- * @param participant The participant to limit the streams to (if any).
- * @param level The level to set the volume to.
+ * purple_media_set_output_volume:
+ * @media: The media object the streams are in.
+ * @session_id: The session to limit the streams to (if any).
+ * @participant: The participant to limit the streams to (if any).
+ * @level: The level to set the volume to.
*
- * @since 2.6.0
+ * Sets the output volume of all the selected streams.
*/
void purple_media_set_output_volume(PurpleMedia *media, const gchar *session_id,
const gchar *participant, double level);
/**
- * Sets a video output window for the given session/stream.
- *
- * @param media The media instance to set the output window on.
- * @param session_id The session to set the output window on.
- * @param participant Optionally, the participant to set the output window on.
- * @param window_id The window id use for embedding the video in.
+ * purple_media_set_output_window:
+ * @media: The media instance to set the output window on.
+ * @session_id: The session to set the output window on.
+ * @participant: Optionally, the participant to set the output window on.
+ * @window_id: The window id use for embedding the video in.
*
- * @return An id to reference the output window.
+ * Sets a video output window for the given session/stream.
*
- * @since 2.6.0
+ * Returns: An id to reference the output window.
*/
gulong purple_media_set_output_window(PurpleMedia *media,
const gchar *session_id, const gchar *participant,
gulong window_id);
/**
- * Removes all output windows from a given media session.
- *
- * @param media The instance to remove all output windows from.
+ * purple_media_remove_output_windows:
+ * @media: The instance to remove all output windows from.
*
- * @since 2.6.0
+ * Removes all output windows from a given media session.
*/
void purple_media_remove_output_windows(PurpleMedia *media);
-#ifdef __cplusplus
-}
-#endif
-
G_END_DECLS
#endif /* _PURPLE_MEDIA_H_ */
diff --git a/libpurple/media/backend-fs2.c b/libpurple/media/backend-fs2.c
index 100636b723..d0720fdcb5 100644
--- a/libpurple/media/backend-fs2.c
+++ b/libpurple/media/backend-fs2.c
@@ -1,8 +1,3 @@
-/**
- * @file backend-fs2.c Farstream backend for media API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -25,6 +20,7 @@
*/
#include "internal.h"
+#include "glibcompat.h"
#include "backend-fs2.h"
@@ -41,6 +37,7 @@
#include <farstream/fs-conference.h>
#include <farstream/fs-element-added-notifier.h>
#include <farstream/fs-utils.h>
+#include <gst/gststructure.h>
#endif
/** @copydoc _PurpleMediaBackendFs2Class */
@@ -239,9 +236,17 @@ purple_media_network_protocol_from_fs(FsNetworkProtocol protocol)
g_return_val_if_reached(PURPLE_MEDIA_NETWORK_PROTOCOL_TCP);
}
+#if GST_CHECK_VERSION(1,0,0)
+static GstPadProbeReturn
+event_probe_cb(GstPad *srcpad, GstPadProbeInfo *info, gpointer unused)
+#else
static gboolean
event_probe_cb(GstPad *srcpad, GstEvent *event, gboolean release_pad)
+#endif
{
+#if GST_CHECK_VERSION(1,0,0)
+ GstEvent *event = GST_PAD_PROBE_INFO_EVENT(info);
+#endif
if (GST_EVENT_TYPE(event) == GST_EVENT_CUSTOM_DOWNSTREAM
&& gst_event_has_name(event, "purple-unlink-tee")) {
@@ -249,22 +254,40 @@ event_probe_cb(GstPad *srcpad, GstEvent *event, gboolean release_pad)
gst_pad_unlink(srcpad, gst_pad_get_peer(srcpad));
+#if GST_CHECK_VERSION(1,0,0)
+ gst_pad_remove_probe(srcpad,
+ g_value_get_ulong(gst_structure_get_value(s, "handler-id")));
+#else
gst_pad_remove_event_probe(srcpad,
g_value_get_uint(gst_structure_get_value(s, "handler-id")));
+#endif
if (g_value_get_boolean(gst_structure_get_value(s, "release-pad")))
gst_element_release_request_pad(GST_ELEMENT_PARENT(srcpad), srcpad);
+#if GST_CHECK_VERSION(1,0,0)
+ return GST_PAD_PROBE_DROP;
+#else
return FALSE;
+#endif
}
+#if GST_CHECK_VERSION(1,0,0)
+ return GST_PAD_PROBE_OK;
+#else
return TRUE;
+#endif
}
static void
unlink_teepad_dynamic(GstPad *srcpad, gboolean release_pad)
{
+#if GST_CHECK_VERSION(1,0,0)
+ gulong id = gst_pad_add_probe(srcpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
+ event_probe_cb, NULL, NULL);
+#else
guint id = gst_pad_add_event_probe(srcpad, G_CALLBACK(event_probe_cb), NULL);
+#endif
if (GST_IS_GHOST_PAD(srcpad))
srcpad = gst_ghost_pad_get_target(GST_GHOST_PAD(srcpad));
@@ -273,7 +296,11 @@ unlink_teepad_dynamic(GstPad *srcpad, gboolean release_pad)
gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM,
gst_structure_new("purple-unlink-tee",
"release-pad", G_TYPE_BOOLEAN, release_pad,
+#if GST_CHECK_VERSION(1,0,0)
+ "handler-id", G_TYPE_ULONG, id,
+#else
"handler-id", G_TYPE_UINT, id,
+#endif
NULL)));
}
@@ -855,15 +882,31 @@ gst_msg_db_to_percent(GstMessage *msg, gchar *value_name)
gdouble value_db;
gdouble percent;
- list = gst_structure_get_value(
- gst_message_get_structure(msg), value_name);
+ 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;
}
static void
+purple_media_error_fs(PurpleMedia *media, const gchar *error,
+ const GstStructure *fs_error)
+{
+ const gchar *error_msg = gst_structure_get_string(fs_error, "error-msg");
+
+ purple_media_error(media, "%s%s%s", error,
+ error_msg ? _("\n\nMessage from Farsight: ") : "",
+ error_msg ? error_msg : "");
+}
+
+static void
gst_handle_message_element(GstBus *bus, GstMessage *msg,
PurpleMediaBackendFs2 *self)
{
@@ -871,11 +914,12 @@ gst_handle_message_element(GstBus *bus, GstMessage *msg,
PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg));
static guint level_id = 0;
+ const GstStructure *structure = gst_message_get_structure(msg);
if (level_id == 0)
level_id = g_signal_lookup("level", PURPLE_TYPE_MEDIA);
- if (gst_structure_has_name(msg->structure, "level")) {
+ if (gst_structure_has_name(structure, "level")) {
GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg));
gchar *name;
gchar *participant = NULL;
@@ -930,31 +974,63 @@ gst_handle_message_element(GstBus *bus, GstMessage *msg,
return;
#ifdef HAVE_FARSIGHT
- if (gst_structure_has_name(msg->structure, "farsight-error")) {
+ if (gst_structure_has_name(structure, "farsight-error")) {
#else
- if (gst_structure_has_name(msg->structure, "farstream-error")) {
+ if (gst_structure_has_name(structure, "farstream-error")) {
#endif
FsError error_no;
- gst_structure_get_enum(msg->structure, "error-no",
+ gboolean error_emitted = FALSE;
+ gst_structure_get_enum(structure, "error-no",
FS_TYPE_ERROR, (gint*)&error_no);
switch (error_no) {
+ case FS_ERROR_CONSTRUCTION:
+ purple_media_error_fs(priv->media,
+ _("Error initializing the call. "
+ "This probably denotes problem in "
+#ifdef HAVE_FARSIGHT
+ "installation of GStreamer or Farsight."),
+#else
+ "installation of GStreamer or Farstream."),
+#endif
+ structure);
+ error_emitted = TRUE;
+ break;
+ case FS_ERROR_NETWORK:
+ purple_media_error_fs(priv->media, _("Network error."),
+ structure);
+ error_emitted = TRUE;
+ purple_media_end(priv->media, NULL, NULL);
+ break;
+ case FS_ERROR_NEGOTIATION_FAILED:
+ purple_media_error_fs(priv->media,
+ _("Codec negotiation failed. "
+ "This problem might be resolved by installing"
+ "more GStreamer codecs."),
+ structure);
+ error_emitted = TRUE;
+ purple_media_end(priv->media, NULL, NULL);
+ break;
case FS_ERROR_NO_CODECS:
- purple_media_error(priv->media, _("No codecs"
- " found. Install some"
- " GStreamer codecs found"
- " in GStreamer plugins"
- " packages."));
+ purple_media_error(priv->media,
+ _("No codecs found. "
+ "Install some GStreamer codecs found "
+ " in GStreamer plugins packages."));
+ error_emitted = TRUE;
purple_media_end(priv->media, NULL, NULL);
break;
#ifdef HAVE_FARSIGHT
case FS_ERROR_NO_CODECS_LEFT:
- purple_media_error(priv->media, _("No codecs"
- " left. Your codec"
- " preferences in"
- " fs-codecs.conf are too"
- " strict."));
+ purple_media_error(priv->media,
+ _("No codecs left. Your codec preferences "
+ "in fs-codecs.conf are too strict."));
+ error_emitted = TRUE;
purple_media_end(priv->media, NULL, NULL);
break;
+ case FS_ERROR_CONNECTION_FAILED:
+ purple_media_error(priv->media,
+ _("Could not connect to the remote party"));
+ error_emitted = TRUE;
+ break;
case FS_ERROR_UNKNOWN_CNAME:
/*
* Unknown CName is only a problem for the
@@ -971,22 +1047,22 @@ gst_handle_message_element(GstBus *bus, GstMessage *msg,
"farstream-error: %i: %s\n",
#endif
error_no,
- gst_structure_get_string(
- msg->structure, "error-msg"));
+ gst_structure_get_string(structure, "error-msg"));
break;
}
if (FS_ERROR_IS_FATAL(error_no)) {
+ if (!error_emitted)
#ifdef HAVE_FARSIGHT
- purple_media_error(priv->media, _("A non-recoverable "
- "Farsight2 error has occurred."));
+ purple_media_error(priv->media,
+ _("A non-recoverable Farsight2 error has occurred."));
#else
- purple_media_error(priv->media, _("A non-recoverable "
- "Farstream error has occurred."));
+ purple_media_error(priv->media,
+ _("A non-recoverable Farstream error has occurred."));
#endif
purple_media_end(priv->media, NULL, NULL);
}
- } else if (gst_structure_has_name(msg->structure,
+ } else if (gst_structure_has_name(structure,
#ifdef HAVE_FARSIGHT
"farsight-new-local-candidate")) {
#else
@@ -1001,9 +1077,9 @@ gst_handle_message_element(GstBus *bus, GstMessage *msg,
PurpleMediaBackendFs2Stream *media_stream;
gchar *name;
- value = gst_structure_get_value(msg->structure, "stream");
+ value = gst_structure_get_value(structure, "stream");
stream = g_value_get_object(value);
- value = gst_structure_get_value(msg->structure, "candidate");
+ value = gst_structure_get_value(structure, "candidate");
local_candidate = g_value_get_boxed(value);
session = get_session_from_fs_stream(self, stream);
@@ -1025,7 +1101,7 @@ gst_handle_message_element(GstBus *bus, GstMessage *msg,
g_signal_emit_by_name(self, "new-candidate",
session->id, name, candidate);
g_object_unref(candidate);
- } else if (gst_structure_has_name(msg->structure,
+ } else if (gst_structure_has_name(structure,
#ifdef HAVE_FARSIGHT
"farsight-local-candidates-prepared")) {
#else
@@ -1037,7 +1113,7 @@ gst_handle_message_element(GstBus *bus, GstMessage *msg,
PurpleMediaBackendFs2Session *session;
gchar *name;
- value = gst_structure_get_value(msg->structure, "stream");
+ value = gst_structure_get_value(structure, "stream");
stream = g_value_get_object(value);
session = get_session_from_fs_stream(self, stream);
@@ -1047,7 +1123,7 @@ gst_handle_message_element(GstBus *bus, GstMessage *msg,
g_signal_emit_by_name(self, "candidates-prepared",
session->id, name);
- } else if (gst_structure_has_name(msg->structure,
+ } else if (gst_structure_has_name(structure,
#ifdef HAVE_FARSIGHT
"farsight-new-active-candidate-pair")) {
#else
@@ -1062,13 +1138,11 @@ gst_handle_message_element(GstBus *bus, GstMessage *msg,
PurpleMediaCandidate *lcandidate, *rcandidate;
gchar *name;
- value = gst_structure_get_value(msg->structure, "stream");
+ value = gst_structure_get_value(structure, "stream");
stream = g_value_get_object(value);
- value = gst_structure_get_value(msg->structure,
- "local-candidate");
+ value = gst_structure_get_value(structure, "local-candidate");
local_candidate = g_value_get_boxed(value);
- value = gst_structure_get_value(msg->structure,
- "remote-candidate");
+ value = gst_structure_get_value(structure, "remote-candidate");
remote_candidate = g_value_get_boxed(value);
g_object_get(stream, "participant", &participant, NULL);
@@ -1085,7 +1159,7 @@ gst_handle_message_element(GstBus *bus, GstMessage *msg,
g_object_unref(lcandidate);
g_object_unref(rcandidate);
- } else if (gst_structure_has_name(msg->structure,
+ } else if (gst_structure_has_name(structure,
#ifdef HAVE_FARSIGHT
"farsight-recv-codecs-changed")) {
#else
@@ -1095,7 +1169,7 @@ gst_handle_message_element(GstBus *bus, GstMessage *msg,
GList *codecs;
FsCodec *codec;
- value = gst_structure_get_value(msg->structure, "codecs");
+ value = gst_structure_get_value(structure, "codecs");
codecs = g_value_get_boxed(value);
codec = codecs->data;
@@ -1106,7 +1180,7 @@ gst_handle_message_element(GstBus *bus, GstMessage *msg,
"farstream-recv-codecs-changed: %s\n",
#endif
codec->encoding_name);
- } else if (gst_structure_has_name(msg->structure,
+ } else if (gst_structure_has_name(structure,
#ifdef HAVE_FARSIGHT
"farsight-component-state-changed")) {
#else
@@ -1117,9 +1191,9 @@ gst_handle_message_element(GstBus *bus, GstMessage *msg,
guint component;
const gchar *state;
- value = gst_structure_get_value(msg->structure, "state");
+ value = gst_structure_get_value(structure, "state");
fsstate = g_value_get_enum(value);
- value = gst_structure_get_value(msg->structure, "component");
+ value = gst_structure_get_value(structure, "component");
component = g_value_get_uint(value);
switch (fsstate) {
@@ -1154,7 +1228,7 @@ gst_handle_message_element(GstBus *bus, GstMessage *msg,
#endif
"component: %u state: %s\n",
component, state);
- } else if (gst_structure_has_name(msg->structure,
+ } else if (gst_structure_has_name(structure,
#ifdef HAVE_FARSIGHT
"farsight-send-codec-changed")) {
#else
@@ -1164,7 +1238,7 @@ gst_handle_message_element(GstBus *bus, GstMessage *msg,
FsCodec *codec;
gchar *codec_str;
- value = gst_structure_get_value(msg->structure, "codec");
+ value = gst_structure_get_value(structure, "codec");
codec = g_value_get_boxed(value);
codec_str = fs_codec_to_string(codec);
@@ -1177,7 +1251,7 @@ gst_handle_message_element(GstBus *bus, GstMessage *msg,
codec_str);
g_free(codec_str);
- } else if (gst_structure_has_name(msg->structure,
+ } else if (gst_structure_has_name(structure,
#ifdef HAVE_FARSIGHT
"farsight-codecs-changed")) {
#else
@@ -1187,7 +1261,7 @@ gst_handle_message_element(GstBus *bus, GstMessage *msg,
FsSession *fssession;
GList *sessions;
- value = gst_structure_get_value(msg->structure, "session");
+ value = gst_structure_get_value(structure, "session");
fssession = g_value_get_object(value);
sessions = g_hash_table_get_values(priv->sessions);
@@ -1631,7 +1705,11 @@ create_src(PurpleMediaBackendFs2 *self, const gchar *sess_id,
srcpad = gst_element_get_static_pad(session->srcvalve, "src");
g_object_set(volume, "volume", input_volume, NULL);
} else {
+#if GST_CHECK_VERSION(1,0,0)
+ srcpad = gst_element_get_request_pad(session->tee, "src_%u");
+#else
srcpad = gst_element_get_request_pad(session->tee, "src%d");
+#endif
}
purple_debug_info("backend-fs2", "connecting pad: %s\n",
@@ -1641,11 +1719,14 @@ create_src(PurpleMediaBackendFs2 *self, const gchar *sess_id,
gst_object_unref(session->src);
gst_object_unref(sinkpad);
- gst_element_set_state(session->src, GST_STATE_PLAYING);
-
purple_media_manager_create_output_window(purple_media_get_manager(
priv->media), priv->media, sess_id, NULL);
+ purple_debug_info("backend-fs2", "create_src: setting source "
+ "state to GST_STATE_PLAYING - it may hang here on win32\n");
+ gst_element_set_state(session->src, GST_STATE_PLAYING);
+ purple_debug_info("backend-fs2", "create_src: state set\n");
+
return TRUE;
}
@@ -1849,14 +1930,10 @@ src_pad_added_cb(FsStream *fsstream, GstPad *srcpad,
* audioresample ! audioconvert ! realsink
*/
stream->queue = gst_element_factory_make("queue", NULL);
- stream->volume = gst_element_factory_make(
- "volume", NULL);
- g_object_set(stream->volume, "volume",
- output_volume, NULL);
- stream->level = gst_element_factory_make(
- "level", NULL);
- stream->src = gst_element_factory_make(
- "liveadder", NULL);
+ stream->volume = gst_element_factory_make("volume", NULL);
+ g_object_set(stream->volume, "volume", output_volume, NULL);
+ stream->level = gst_element_factory_make("level", NULL);
+ stream->src = gst_element_factory_make("liveadder", NULL);
sink = purple_media_manager_get_element(
purple_media_get_manager(priv->media),
PURPLE_MEDIA_RECV_AUDIO, priv->media,
@@ -1875,10 +1952,12 @@ src_pad_added_cb(FsStream *fsstream, GstPad *srcpad,
gst_element_link(stream->queue, stream->volume);
sink = stream->queue;
} else if (codec->media_type == FS_MEDIA_TYPE_VIDEO) {
- stream->src = gst_element_factory_make(
- "fsfunnel", NULL);
- sink = gst_element_factory_make(
- "fakesink", NULL);
+#if GST_CHECK_VERSION(1,0,0)
+ stream->src = gst_element_factory_make("funnel", NULL);
+#else
+ stream->src = gst_element_factory_make("fsfunnel", NULL);
+#endif
+ sink = gst_element_factory_make("fakesink", NULL);
g_object_set(G_OBJECT(sink), "async", FALSE, NULL);
gst_bin_add(GST_BIN(priv->confbin), sink);
gst_element_set_state(sink, GST_STATE_PLAYING);
@@ -1892,7 +1971,11 @@ src_pad_added_cb(FsStream *fsstream, GstPad *srcpad,
gst_element_link_many(stream->src, stream->tee, sink, NULL);
}
+#if GST_CHECK_VERSION(1,0,0)
+ sinkpad = gst_element_get_request_pad(stream->src, "sink_%u");
+#else
sinkpad = gst_element_get_request_pad(stream->src, "sink%d");
+#endif
gst_pad_link(srcpad, sinkpad);
gst_object_unref(sinkpad);
@@ -1950,7 +2033,7 @@ create_stream(PurpleMediaBackendFs2 *self,
we need to do this to allow them to override when using non-standard
TURN modes, like Google f.ex. */
gboolean got_turn_from_prpl = FALSE;
- int i;
+ guint i;
session = get_session(self, sess_id);
@@ -2074,8 +2157,8 @@ G_GNUC_END_IGNORE_DEPRECATIONS
if (!fs_stream_set_transmitter(fsstream, transmitter,
_params, _num_params, &err)) {
purple_debug_error("backend-fs2",
- "Could not set transmitter %s: %s.\n",
- transmitter, err->message);
+ "Could not set transmitter %s: %s.\n",
+ transmitter, err ? err->message : NULL);
g_clear_error(&err);
g_free(_params);
return FALSE;
@@ -2394,14 +2477,44 @@ purple_media_backend_fs2_set_send_codec(PurpleMediaBackend *self,
return TRUE;
}
+static const gchar **
+purple_media_backend_fs2_get_available_params(void)
+{
+ static const gchar *supported_params[] = {
+ "sdes-cname", "sdes-email", "sdes-location", "sdes-name", "sdes-note",
+ "sdes-phone", "sdes-tool", NULL
+ };
+
+ return supported_params;
+}
+
+static const gchar*
+param_to_sdes_type(const gchar *param)
+{
+ const gchar **supported = purple_media_backend_fs2_get_available_params();
+ static const gchar *sdes_types[] = {
+ "cname", "email", "location", "name", "note", "phone", "tool", NULL
+ };
+ guint i;
+
+ for (i = 0; supported[i] != NULL; ++i) {
+ if (!strcmp(param, supported[i])) {
+ return sdes_types[i];
+ }
+ }
+
+ return NULL;
+}
+
static void
purple_media_backend_fs2_set_params(PurpleMediaBackend *self,
guint num_params, GParameter *params)
{
PurpleMediaBackendFs2Private *priv;
- const gchar **supported = purple_media_backend_fs2_get_available_params();
- const gchar **p;
guint i;
+#ifndef HAVE_FARSIGHT
+ GstStructure *sdes;
+#endif
g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self));
@@ -2414,27 +2527,30 @@ purple_media_backend_fs2_set_params(PurpleMediaBackend *self,
return;
}
+#ifdef HAVE_FARSIGHT
for (i = 0; i != num_params; ++i) {
- for (p = supported; *p != NULL; ++p) {
- if (!strcmp(params[i].name, *p)) {
- g_object_set(priv->conference,
- params[i].name, g_value_get_string(&params[i].value),
- NULL);
- break;
- }
+ if (param_to_sdes_type(params[i].name)) {
+ g_object_set(priv->conference,
+ params[i].name, g_value_get_string(&params[i].value),
+ NULL);
}
}
-}
+#else
+ g_object_get(G_OBJECT(priv->conference), "sdes", &sdes, NULL);
-static const gchar **
-purple_media_backend_fs2_get_available_params(void)
-{
- static const gchar *supported_params[] = {
- "sdes-cname", "sdes-email", "sdes-location", "sdes-name", "sdes-note",
- "sdes-phone", "sdes-tool", NULL
- };
+ for (i = 0; i != num_params; ++i) {
+ const gchar *sdes_type = param_to_sdes_type(params[i].name);
+ if (!sdes_type)
+ continue;
- return supported_params;
+ gst_structure_set(sdes, sdes_type,
+ G_TYPE_STRING, g_value_get_string(&params[i].value),
+ NULL);
+ }
+
+ g_object_set(G_OBJECT(priv->conference), "sdes", sdes, NULL);
+ gst_structure_free(sdes);
+#endif /* HAVE_FARSIGHT */
}
#else
GType
diff --git a/libpurple/media/backend-fs2.h b/libpurple/media/backend-fs2.h
index 47fb02dac5..52bb6e5c35 100644
--- a/libpurple/media/backend-fs2.h
+++ b/libpurple/media/backend-fs2.h
@@ -1,8 +1,3 @@
-/**
- * @file backend-fs2.h Farsight 2 backend for media API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -24,14 +19,18 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+#ifndef _MEDIA_BACKEND_FS2_H_
+#define _MEDIA_BACKEND_FS2_H_
/*
+ * SECTION:backend-fs2
+ * @section_id: libpurple-backend-fs2
+ * @short_description: <filename>media/backend-fs2.h</filename>
+ * @title: Farsight 2 backend for media API
+ *
* This file should not yet be part of libpurple's API.
* It should remain internal only for now.
*/
-#ifndef _MEDIA_BACKEND_FS2_H_
-#define _MEDIA_BACKEND_FS2_H_
-
#include <glib-object.h>
G_BEGIN_DECLS
@@ -43,15 +42,19 @@ G_BEGIN_DECLS
#define PURPLE_MEDIA_BACKEND_FS2_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEDIA_BACKEND_FS2, PurpleMediaBackendFs2))
#define PURPLE_MEDIA_BACKEND_FS2_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA_BACKEND_FS2, PurpleMediaBackendFs2))
-/** An opaque structure representing the Farsight 2 media backend. */
+/**
+ * PurpleMediaBackendFs2:
+ *
+ * An opaque structure representing the Farsight 2 media backend.
+ */
typedef struct _PurpleMediaBackendFs2 PurpleMediaBackendFs2;
/**
- * Gets the type of the Farsight 2 media backend object.
+ * purple_media_backend_fs2_get_type:
*
- * @return The Farsight 2 media backend's GType
+ * Gets the type of the Farsight 2 media backend object.
*
- * @since 2.7.0
+ * Returns: The Farsight 2 media backend's GType
*/
GType purple_media_backend_fs2_get_type(void);
diff --git a/libpurple/media/backend-iface.c b/libpurple/media/backend-iface.c
index fd53898db1..618f521886 100644
--- a/libpurple/media/backend-iface.c
+++ b/libpurple/media/backend-iface.c
@@ -1,8 +1,3 @@
-/**
- * @file backend-iface.c Interface for media backend
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -53,13 +48,15 @@ purple_media_backend_base_init(gpointer iface)
"The type of conference that this backend "
"has been created to provide.",
NULL,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
g_object_interface_install_property(iface,
g_param_spec_object("media",
"Purple Media",
"The media object that this backend is bound to.",
PURPLE_TYPE_MEDIA,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
purple_media_backend_signals[S_ERROR] =
g_signal_new("error", G_TYPE_FROM_CLASS(iface),
diff --git a/libpurple/media/backend-iface.h b/libpurple/media/backend-iface.h
index 94105fbaca..f6d2521974 100644
--- a/libpurple/media/backend-iface.h
+++ b/libpurple/media/backend-iface.h
@@ -1,8 +1,3 @@
-/**
- * @file backend-iface.h Interface for media backends
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -26,6 +21,12 @@
#ifndef _MEDIA_BACKEND_IFACE_H_
#define _MEDIA_BACKEND_IFACE_H_
+/**
+ * SECTION:backend-iface
+ * @section_id: libpurple-backend-iface
+ * @short_description: <filename>media/backend-iface.h</filename>
+ * @title: Interface for Media Backends
+ */
#include "codec.h"
#include "enum-types.h"
@@ -39,16 +40,27 @@ G_BEGIN_DECLS
#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))
-/** A placeholder to represent any media backend */
+/**
+ * PurpleMediaBackend:
+ *
+ * A placeholder to represent any media backend
+ */
typedef struct _PurpleMediaBackend PurpleMediaBackend;
-/** A structure to derive media backends from. */
+
+/**
+ * PurpleMediaBackendIface:
+ *
+ * A structure to derive media backends from.
+ */
typedef struct _PurpleMediaBackendIface PurpleMediaBackendIface;
struct _PurpleMediaBackendIface
{
- GTypeInterface parent_iface; /**< The parent iface class */
+ /*< private >*/
+ GTypeInterface parent_iface; /* The parent iface class */
- /** Implementable functions called with purple_media_backend_* */
+ /*< public >*/
+ /* Implementable functions called with purple_media_backend_* */
gboolean (*add_stream) (PurpleMediaBackend *self,
const gchar *sess_id, const gchar *who,
PurpleMediaSessionType type, gboolean initiator,
@@ -74,29 +86,28 @@ struct _PurpleMediaBackendIface
};
/**
- * Gets the media backend's GType.
+ * purple_media_backend_get_type:
*
- * @return The media backend's GType.
+ * Gets the media backend's GType.
*
- * @since 2.7.0
+ * Returns: The media backend's GType.
*/
GType purple_media_backend_get_type(void);
/**
- * Creates and adds a stream to the media backend.
- *
- * @param self The backend to add the stream to.
- * @param sess_id The session id of the stream to add.
- * @param who The remote participant of the stream to add.
- * @param type The media type and direction of the stream to add.
- * @param initiator True if the local user initiated the stream.
- * @param transmitter The string id of the tranmsitter to use.
- * @param num_params The number of parameters in the param parameter.
- * @param params The additional parameters to pass when creating the stream.
+ * purple_media_backend_add_stream:
+ * @self: The backend to add the stream to.
+ * @sess_id: The session id of the stream to add.
+ * @who: The remote participant of the stream to add.
+ * @type: The media type and direction of the stream to add.
+ * @initiator: True if the local user initiated the stream.
+ * @transmitter: The string id of the tranmsitter to use.
+ * @num_params: The number of parameters in the param parameter.
+ * @params: The additional parameters to pass when creating the stream.
*
- * @return True if the stream was successfully created, othewise False.
+ * Creates and adds a stream to the media backend.
*
- * @since 2.7.0
+ * Returns: True if the stream was successfully created, othewise False.
*/
gboolean purple_media_backend_add_stream(PurpleMediaBackend *self,
const gchar *sess_id, const gchar *who,
@@ -105,117 +116,108 @@ gboolean purple_media_backend_add_stream(PurpleMediaBackend *self,
guint num_params, GParameter *params);
/**
- * Add remote candidates to a stream.
- *
- * @param self The backend the stream is in.
- * @param sess_id The session id associated with the stream.
- * @param participant The participant associated with the stream.
- * @param remote_candidates The list of remote candidates to add.
+ * purple_media_backend_add_remote_candidates:
+ * @self: The backend the stream is in.
+ * @sess_id: The session id associated with the stream.
+ * @participant: The participant associated with the stream.
+ * @remote_candidates: The list of remote candidates to add.
*
- * @since 2.7.0
+ * Add remote candidates to a stream.
*/
void purple_media_backend_add_remote_candidates(PurpleMediaBackend *self,
const gchar *sess_id, const gchar *participant,
GList *remote_candidates);
/**
+ * purple_media_backend_codecs_ready:
+ * @self: The media backend the session is in.
+ * @sess_id: The session id of the session to check.
+ *
* Get whether or not a session's codecs are ready.
*
* A codec is ready if all of the attributes and additional
* parameters have been collected.
*
- * @param self The media backend the session is in.
- * @param sess_id The session id of the session to check.
- *
- * @return True if the codecs are ready, otherwise False.
- *
- * @since 2.7.0
+ * Returns: True if the codecs are ready, otherwise False.
*/
gboolean purple_media_backend_codecs_ready(PurpleMediaBackend *self,
const gchar *sess_id);
/**
+ * purple_media_backend_get_codecs:
+ * @self: The media backend the session is in.
+ * @sess_id: The session id of the session to use.
+ *
* Gets the codec intersection list for a session.
*
* The intersection list consists of all codecs that are compatible
* between the local and remote software.
*
- * @param self The media backend the session is in.
- * @param sess_id The session id of the session to use.
- *
- * @return The codec intersection list.
- *
- * @since 2.7.0
+ * Returns: The codec intersection list.
*/
GList *purple_media_backend_get_codecs(PurpleMediaBackend *self,
const gchar *sess_id);
/**
- * Gets the list of local candidates for a stream.
+ * purple_media_backend_get_local_candidates:
+ * @self: The media backend the stream is in.
+ * @sess_id: The session id associated with the stream.
+ * @participant: The participant associated with the stream.
*
- * @param self The media backend the stream is in.
- * @param sess_id The session id associated with the stream.
- * @param particilant The participant associated with the stream.
- *
- * @return The list of local candidates.
+ * Gets the list of local candidates for a stream.
*
- * @since 2.7.0
+ * Returns: The list of local candidates.
*/
GList *purple_media_backend_get_local_candidates(PurpleMediaBackend *self,
const gchar *sess_id, const gchar *participant);
/**
- * Sets the remote codecs on a stream.
+ * purple_media_backend_set_remote_codecs:
+ * @self: The media backend the stream is in.
+ * @sess_id: The session id the stream is associated with.
+ * @participant: The participant the stream is associated with.
+ * @codecs: The list of remote codecs to set.
*
- * @param self The media backend the stream is in.
- * @param sess_id The session id the stream is associated with.
- * @param participant The participant the stream is associated with.
- * @param codecs The list of remote codecs to set.
- *
- * @return True if the remote codecs were set successfully, otherwise False.
+ * Sets the remote codecs on a stream.
*
- * @since 2.7.0
+ * Returns: True if the remote codecs were set successfully, otherwise False.
*/
gboolean purple_media_backend_set_remote_codecs(PurpleMediaBackend *self,
const gchar *sess_id, const gchar *participant,
GList *codecs);
/**
- * Sets which codec format to send media content in for a session.
- *
- * @param self The media backend the session is in.
- * @param sess_id The session id of the session to set the codec for.
- * @param codec The codec to set.
+ * purple_media_backend_set_send_codec:
+ * @self: The media backend the session is in.
+ * @sess_id: The session id of the session to set the codec for.
+ * @codec: The codec to set.
*
- * @return True if set successfully, otherwise False.
+ * Sets which codec format to send media content in for a session.
*
- * @since 2.7.0
+ * Returns: True if set successfully, otherwise False.
*/
gboolean purple_media_backend_set_send_codec(PurpleMediaBackend *self,
const gchar *sess_id, PurpleMediaCodec *codec);
/**
- * Sets various optional parameters of the media backend.
- *
- * @param self The media backend to set the parameters on.
- * @param num_params The number of parameters to pass to backend
- * @param params Array of @c GParameter to pass to backend
+ * purple_media_backend_set_params:
+ * @self: The media backend to set the parameters on.
+ * @num_params: The number of parameters to pass to backend
+ * @params: Array of @c GParameter to pass to backend
*
- * @since 2.8.0
+ * Sets various optional parameters of the media backend.
*/
void purple_media_backend_set_params(PurpleMediaBackend *self,
guint num_params, GParameter *params);
/**
- * Gets the list of optional parameters supported by the media backend.
+ * purple_media_backend_get_available_params:
+ * @self: The media backend
*
+ * Gets the list of optional parameters supported by the media backend.
* The list should NOT be freed.
*
- * @param self The media backend
- *
- * @return NULL-terminated array of names of supported parameters.
- *
- * @since 2.8.0
+ * Returns: NULL-terminated array of names of supported parameters.
*/
const gchar **purple_media_backend_get_available_params(PurpleMediaBackend *self);
diff --git a/libpurple/media/candidate.c b/libpurple/media/candidate.c
index d417b12745..be9d9dd3c4 100644
--- a/libpurple/media/candidate.c
+++ b/libpurple/media/candidate.c
@@ -1,8 +1,3 @@
-/**
- * @file candidate.c Candidate for Media API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -237,42 +232,42 @@ purple_media_candidate_class_init(PurpleMediaCandidateClass *klass)
"Foundation",
"The foundation of the candidate.",
NULL,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class, PROP_COMPONENT_ID,
g_param_spec_uint("component-id",
"Component ID",
"The component id of the candidate.",
0, G_MAXUINT, 0,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class, PROP_IP,
g_param_spec_string("ip",
"IP Address",
"The IP address of the candidate.",
NULL,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class, PROP_PORT,
g_param_spec_uint("port",
"Port",
"The port of the candidate.",
0, G_MAXUINT16, 0,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class, PROP_BASE_IP,
g_param_spec_string("base-ip",
"Base IP",
"The internal IP address of the candidate.",
NULL,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class, PROP_BASE_PORT,
g_param_spec_uint("base-port",
"Base Port",
"The internal port of the candidate.",
0, G_MAXUINT16, 0,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class, PROP_PROTOCOL,
g_param_spec_enum("protocol",
@@ -280,14 +275,14 @@ purple_media_candidate_class_init(PurpleMediaCandidateClass *klass)
"The protocol of the candidate.",
PURPLE_TYPE_MEDIA_NETWORK_PROTOCOL,
PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class, PROP_PRIORITY,
g_param_spec_uint("priority",
"Priority",
"The priority of the candidate.",
0, G_MAXUINT32, 0,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class, PROP_TYPE,
g_param_spec_enum("type",
@@ -295,28 +290,28 @@ purple_media_candidate_class_init(PurpleMediaCandidateClass *klass)
"The type of the candidate.",
PURPLE_TYPE_MEDIA_CANDIDATE_TYPE,
PURPLE_MEDIA_CANDIDATE_TYPE_HOST,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class, PROP_USERNAME,
g_param_spec_string("username",
"Username",
"The username used to connect to the candidate.",
NULL,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class, PROP_PASSWORD,
g_param_spec_string("password",
"Password",
"The password use to connect to the candidate.",
NULL,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class, PROP_TTL,
g_param_spec_uint("ttl",
"TTL",
"The TTL of the candidate.",
0, G_MAXUINT, 0,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_type_class_add_private(klass, sizeof(PurpleMediaCandidatePrivate));
}
diff --git a/libpurple/media/candidate.h b/libpurple/media/candidate.h
index 1dfa0fd05e..da8024028b 100644
--- a/libpurple/media/candidate.h
+++ b/libpurple/media/candidate.h
@@ -1,8 +1,3 @@
-/**
- * @file candidate.h Candidate for Media API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -26,6 +21,12 @@
#ifndef _PURPLE_MEDIA_CANDIDATE_H_
#define _PURPLE_MEDIA_CANDIDATE_H_
+/**
+ * SECTION:candidate
+ * @section_id: libpurple-candidate
+ * @short_description: <filename>media/candidate.h</filename>
+ * @title: Candidate for Media API
+ */
#include "enum-types.h"
@@ -40,31 +41,35 @@ G_BEGIN_DECLS
#define PURPLE_MEDIA_CANDIDATE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEDIA_CANDIDATE, PurpleMediaCandidate))
#define PURPLE_MEDIA_CANDIDATE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA_CANDIDATE, PurpleMediaCandidate))
-/** An opaque structure representing a network candidate (IP Address and port pair). */
+/**
+ * PurpleMediaCandidate:
+ *
+ * An opaque structure representing a network candidate (IP Address and port
+ * pair).
+ */
typedef struct _PurpleMediaCandidate PurpleMediaCandidate;
/**
- * Gets the type of the media candidate structure.
+ * purple_media_candidate_get_type:
*
- * @return The media canditate's GType
+ * Gets the type of the media candidate structure.
*
- * @since 2.6.0
+ * Returns: The media canditate's GType
*/
GType purple_media_candidate_get_type(void);
/**
- * Creates a PurpleMediaCandidate instance.
- *
- * @param foundation The foundation of the candidate.
- * @param component_id The component this candidate is for.
- * @param type The type of candidate.
- * @param proto The protocol this component is for.
- * @param ip The IP address of this component.
- * @param port The network port.
+ * purple_media_candidate_new:
+ * @foundation: The foundation of the candidate.
+ * @component_id: The component this candidate is for.
+ * @type: The type of candidate.
+ * @proto: The protocol this component is for.
+ * @ip: The IP address of this component.
+ * @port: The network port.
*
- * @return The newly created PurpleMediaCandidate instance.
+ * Creates a PurpleMediaCandidate instance.
*
- * @since 2.6.0
+ * Returns: The newly created PurpleMediaCandidate instance.
*/
PurpleMediaCandidate *purple_media_candidate_new(
const gchar *foundation, guint component_id,
@@ -73,176 +78,158 @@ PurpleMediaCandidate *purple_media_candidate_new(
const gchar *ip, guint port);
/**
- * Copies a PurpleMediaCandidate.
+ * purple_media_candidate_copy:
+ * @candidate: The candidate to copy.
*
- * @param candidate The candidate to copy.
- *
- * @return The copy of the PurpleMediaCandidate.
+ * Copies a PurpleMediaCandidate.
*
- * @since 2.7.0
+ * Returns: The copy of the PurpleMediaCandidate.
*/
PurpleMediaCandidate *purple_media_candidate_copy(
PurpleMediaCandidate *candidate);
/**
- * Copies a GList of PurpleMediaCandidate and its contents.
- *
- * @param candidates The list of candidates to be copied.
+ * purple_media_candidate_list_copy:
+ * @candidates: The list of candidates to be copied.
*
- * @return The copy of the GList.
+ * Copies a GList of PurpleMediaCandidate and its contents.
*
- * @since 2.6.0
+ * Returns: The copy of the GList.
*/
GList *purple_media_candidate_list_copy(GList *candidates);
/**
- * Frees a GList of PurpleMediaCandidate and its contents.
+ * purple_media_candidate_list_free:
+ * @candidates: The list of candidates to be freed.
*
- * @param candidates The list of candidates to be freed.
- *
- * @since 2.6.0
+ * Frees a GList of PurpleMediaCandidate and its contents.
*/
void purple_media_candidate_list_free(GList *candidates);
/**
- * Gets the foundation (identifier) from the candidate.
- *
- * @param candidate The candidate to get the foundation from.
+ * purple_media_candidate_get_foundation:
+ * @candidate: The candidate to get the foundation from.
*
- * @return The foundation.
+ * Gets the foundation (identifier) from the candidate.
*
- * @since 2.6.0
+ * Returns: The foundation.
*/
gchar *purple_media_candidate_get_foundation(PurpleMediaCandidate *candidate);
/**
- * Gets the component id (rtp or rtcp)
+ * purple_media_candidate_get_component_id:
+ * @candidate: The candidate to get the compnent id from.
*
- * @param candidate The candidate to get the compnent id from.
- *
- * @return The component id.
+ * Gets the component id (rtp or rtcp)
*
- * @since 2.6.0
+ * Returns: The component id.
*/
guint purple_media_candidate_get_component_id(PurpleMediaCandidate *candidate);
/**
- * Gets the IP address.
- *
- * @param candidate The candidate to get the IP address from.
+ * purple_media_candidate_get_ip:
+ * @candidate: The candidate to get the IP address from.
*
- * @return The IP address.
+ * Gets the IP address.
*
- * @since 2.6.0
+ * Returns: The IP address.
*/
gchar *purple_media_candidate_get_ip(PurpleMediaCandidate *candidate);
/**
- * Gets the port.
- *
- * @param candidate The candidate to get the port from.
+ * purple_media_candidate_get_port:
+ * @candidate: The candidate to get the port from.
*
- * @return The port.
+ * Gets the port.
*
- * @since 2.6.0
+ * Returns: The port.
*/
guint16 purple_media_candidate_get_port(PurpleMediaCandidate *candidate);
/**
- * Gets the base (internal) IP address.
+ * purple_media_candidate_get_base_ip:
+ * @candidate: The candidate to get the base IP address from.
*
+ * Gets the base (internal) IP address.
* This can be NULL.
*
- * @param candidate The candidate to get the base IP address from.
- *
- * @return The base IP address.
- *
- * @since 2.6.0
+ * Returns: The base IP address.
*/
gchar *purple_media_candidate_get_base_ip(PurpleMediaCandidate *candidate);
/**
- * Gets the base (internal) port.
+ * purple_media_candidate_get_base_port:
+ * @candidate: The candidate to get the base port.
*
+ * Gets the base (internal) port.
* Invalid if the base IP is NULL.
*
- * @param candidate The candidate to get the base port.
- *
- * @return The base port.
- *
- * @since 2.6.0
+ * Returns: The base port.
*/
guint16 purple_media_candidate_get_base_port(PurpleMediaCandidate *candidate);
/**
- * Gets the protocol (TCP or UDP).
+ * purple_media_candidate_get_protocol:
+ * @candidate: The candidate to get the protocol from.
*
- * @param candidate The candidate to get the protocol from.
- *
- * @return The protocol.
+ * Gets the protocol (TCP or UDP).
*
- * @since 2.6.0
+ * Returns: The protocol.
*/
PurpleMediaNetworkProtocol purple_media_candidate_get_protocol(
PurpleMediaCandidate *candidate);
/**
- * Gets the priority.
+ * purple_media_candidate_get_priority:
+ * @candidate: The candidate to get the priority from.
*
- * @param candidate The candidate to get the priority from.
- *
- * @return The priority.
+ * Gets the priority.
*
- * @since 2.6.0
+ * Returns: The priority.
*/
guint32 purple_media_candidate_get_priority(PurpleMediaCandidate *candidate);
/**
- * Gets the candidate type.
- *
- * @param candidate The candidate to get the candidate type from.
+ * purple_media_candidate_get_candidate_type:
+ * @candidate: The candidate to get the candidate type from.
*
- * @return The candidate type.
+ * Gets the candidate type.
*
- * @since 2.6.0
+ * Returns: The candidate type.
*/
PurpleMediaCandidateType purple_media_candidate_get_candidate_type(
PurpleMediaCandidate *candidate);
/**
- * Gets the username.
+ * purple_media_candidate_get_username:
+ * @candidate: The candidate to get the username from.
*
+ * Gets the username.
* This can be NULL. It depends on the transmission type.
*
- * @param The candidate to get the username from.
- *
- * @return The username.
- *
- * @since 2.6.0
+ * Returns: The username.
*/
gchar *purple_media_candidate_get_username(PurpleMediaCandidate *candidate);
/**
+ * purple_media_candidate_get_password:
+ * @candidate: The candidate to get the password from.
+ *
* Gets the password.
*
* This can be NULL. It depends on the transmission type.
*
- * @param The candidate to get the password from.
- *
- * @return The password.
- *
- * @since 2.6.0
+ * Returns: The password.
*/
gchar *purple_media_candidate_get_password(PurpleMediaCandidate *candidate);
/**
- * Gets the TTL.
- *
- * @param The candidate to get the TTL from.
+ * purple_media_candidate_get_ttl:
+ * @candidate: The candidate to get the TTL from.
*
- * @return The TTL.
+ * Gets the TTL.
*
- * @since 2.6.0
+ * Returns: The TTL.
*/
guint purple_media_candidate_get_ttl(PurpleMediaCandidate *candidate);
diff --git a/libpurple/media/codec.c b/libpurple/media/codec.c
index 24fd2ad461..57d8636f89 100644
--- a/libpurple/media/codec.c
+++ b/libpurple/media/codec.c
@@ -1,8 +1,3 @@
-/**
- * @file codec.c Codec for Media API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -24,6 +19,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+#include "internal.h"
+#include "glibcompat.h"
#include "codec.h"
/** @copydoc _PurpleMediaCodecClass */
@@ -65,8 +62,11 @@ enum {
PROP_CLOCK_RATE,
PROP_CHANNELS,
PROP_OPTIONAL_PARAMS,
+ PROP_LAST
};
+static GParamSpec *properties[PROP_LAST];
+
static void
purple_media_codec_init(PurpleMediaCodec *info)
{
@@ -171,48 +171,48 @@ purple_media_codec_class_init(PurpleMediaCodecClass *klass)
gobject_class->set_property = purple_media_codec_set_property;
gobject_class->get_property = purple_media_codec_get_property;
- g_object_class_install_property(gobject_class, PROP_ID,
- g_param_spec_uint("id",
+ g_type_class_add_private(klass, sizeof(PurpleMediaCodecPrivate));
+
+ properties[PROP_ID] = g_param_spec_uint("id",
"ID",
"The numeric identifier of the codec.",
0, G_MAXUINT, 0,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
- g_object_class_install_property(gobject_class, PROP_ENCODING_NAME,
- g_param_spec_string("encoding-name",
+ properties[PROP_ENCODING_NAME] = g_param_spec_string("encoding-name",
"Encoding Name",
"The name of the codec.",
NULL,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
- g_object_class_install_property(gobject_class, PROP_MEDIA_TYPE,
- g_param_spec_flags("media-type",
+ properties[PROP_MEDIA_TYPE] = g_param_spec_flags("media-type",
"Media Type",
"Whether this is an audio of video codec.",
PURPLE_TYPE_MEDIA_SESSION_TYPE,
PURPLE_MEDIA_NONE,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
- g_object_class_install_property(gobject_class, PROP_CLOCK_RATE,
- g_param_spec_uint("clock-rate",
+ properties[PROP_CLOCK_RATE] = g_param_spec_uint("clock-rate",
"Create Callback",
"The function called to create this element.",
0, G_MAXUINT, 0,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property(gobject_class, PROP_CHANNELS,
- g_param_spec_uint("channels",
+ properties[PROP_CHANNELS] = g_param_spec_uint("channels",
"Channels",
"The number of channels in this codec.",
0, G_MAXUINT, 0,
- G_PARAM_READWRITE));
- g_object_class_install_property(gobject_class, PROP_OPTIONAL_PARAMS,
- g_param_spec_pointer("optional-params",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_OPTIONAL_PARAMS] = g_param_spec_pointer("optional-params",
"Optional Params",
"A list of optional parameters for the codec.",
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_type_class_add_private(klass, sizeof(PurpleMediaCodecPrivate));
+ g_object_class_install_properties(gobject_class, PROP_LAST, properties);
}
PurpleMediaCodec *
@@ -290,6 +290,8 @@ purple_media_codec_add_optional_parameter(PurpleMediaCodec *codec,
new_param->value = g_strdup(value);
priv->optional_params = g_list_append(
priv->optional_params, new_param);
+
+ g_object_notify_by_pspec(G_OBJECT(codec), properties[PROP_OPTIONAL_PARAMS]);
}
void
@@ -308,6 +310,8 @@ purple_media_codec_remove_optional_parameter(PurpleMediaCodec *codec,
priv->optional_params =
g_list_remove(priv->optional_params, param);
g_free(param);
+
+ g_object_notify_by_pspec(G_OBJECT(codec), properties[PROP_OPTIONAL_PARAMS]);
}
PurpleKeyValuePair *
diff --git a/libpurple/media/codec.h b/libpurple/media/codec.h
index 46a4ee194e..ad739f8d6c 100644
--- a/libpurple/media/codec.h
+++ b/libpurple/media/codec.h
@@ -1,8 +1,3 @@
-/**
- * @file codec.h Codec for Media API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -26,10 +21,20 @@
#ifndef _PURPLE_MEDIA_CODEC_H_
#define _PURPLE_MEDIA_CODEC_H_
+/**
+ * SECTION:codec
+ * @section_id: libpurple-codec
+ * @short_description: <filename>media/codec.h</filename>
+ * @title: Codec for Media API
+ */
#include "enum-types.h"
-/** An opaque structure representing an audio or video codec. */
+/**
+ * PurpleMediaCodec:
+ *
+ * An opaque structure representing an audio or video codec.
+ */
typedef struct _PurpleMediaCodec PurpleMediaCodec;
#include "../util.h"
@@ -47,164 +52,151 @@ G_BEGIN_DECLS
/**
- * Gets the type of the media codec structure.
+ * purple_media_codec_get_type:
*
- * @return The media codec's GType
+ * Gets the type of the media codec structure.
*
- * @since 2.6.0
+ * Returns: The media codec's GType
*/
GType purple_media_codec_get_type(void);
/**
- * Creates a new PurpleMediaCodec instance.
- *
- * @param id Codec identifier.
- * @param encoding_name Name of the media type this encodes.
- * @param media_type PurpleMediaSessionType of this codec.
- * @param clock_rate The clock rate this codec encodes at, if applicable.
+ * purple_media_codec_new:
+ * @id: Codec identifier.
+ * @encoding_name: Name of the media type this encodes.
+ * @media_type: PurpleMediaSessionType of this codec.
+ * @clock_rate: The clock rate this codec encodes at, if applicable.
*
- * @return The newly created PurpleMediaCodec.
+ * Creates a new PurpleMediaCodec instance.
*
- * @since 2.6.0
+ * Returns: The newly created PurpleMediaCodec.
*/
PurpleMediaCodec *purple_media_codec_new(int id, const char *encoding_name,
PurpleMediaSessionType media_type, guint clock_rate);
/**
- * Gets the codec id.
- *
- * @param The codec to get the id from.
+ * purple_media_codec_get_id:
+ * @codec: The codec to get the id from.
*
- * @return The codec id.
+ * Gets the codec id.
*
- * @since 2.6.0
+ * Returns: The codec id.
*/
guint purple_media_codec_get_id(PurpleMediaCodec *codec);
/**
- * Gets the encoding name.
- *
- * @param The codec to get the encoding name from.
+ * purple_media_codec_get_encoding_name:
+ * @codec: The codec to get the encoding name from.
*
- * @return The encoding name.
+ * Gets the encoding name.
*
- * @since 2.6.0
+ * Returns: The encoding name.
*/
gchar *purple_media_codec_get_encoding_name(PurpleMediaCodec *codec);
/**
- * Gets the clock rate.
- *
- * @param The codec to get the clock rate from.
+ * purple_media_codec_get_clock_rate:
+ * @codec: The codec to get the clock rate from.
*
- * @return The clock rate.
+ * Gets the clock rate.
*
- * @since 2.6.0
+ * Returns: The clock rate.
*/
guint purple_media_codec_get_clock_rate(PurpleMediaCodec *codec);
/**
- * Gets the number of channels.
- *
- * @param The codec to get the number of channels from.
+ * purple_media_codec_get_channels:
+ * @codec: The codec to get the number of channels from.
*
- * @return The number of channels.
+ * Gets the number of channels.
*
- * @since 2.6.0
+ * Returns: The number of channels.
*/
guint purple_media_codec_get_channels(PurpleMediaCodec *codec);
/**
+ * purple_media_codec_get_optional_parameters:
+ * @codec: The codec to get the optional parameters from.
+ *
* Gets a list of the optional parameters.
*
* The list consists of PurpleKeyValuePair's.
*
- * @param The codec to get the optional parameters from.
- *
- * @return The list of optional parameters. The list is owned by the codec and
+ * Returns: The list of optional parameters. The list is owned by the codec and
* should not be freed.
- *
- * @since 2.6.0
*/
GList *purple_media_codec_get_optional_parameters(PurpleMediaCodec *codec);
/**
- * Adds an optional parameter to the codec.
- *
- * @param codec The codec to add the parameter to.
- * @param name The name of the parameter to add.
- * @param value The value of the parameter to add.
+ * purple_media_codec_add_optional_parameter:
+ * @codec: The codec to add the parameter to.
+ * @name: The name of the parameter to add.
+ * @value: The value of the parameter to add.
*
- * @since 2.6.0
+ * Adds an optional parameter to the codec.
*/
void purple_media_codec_add_optional_parameter(PurpleMediaCodec *codec,
const gchar *name, const gchar *value);
/**
- * Removes an optional parameter from the codec.
- *
- * @param codec The codec to remove the parameter from.
- * @param param A pointer to the parameter to remove.
+ * purple_media_codec_remove_optional_parameter:
+ * @codec: The codec to remove the parameter from.
+ * @param: A pointer to the parameter to remove.
*
- * @since 2.6.0
+ * Removes an optional parameter from the codec.
*/
void purple_media_codec_remove_optional_parameter(PurpleMediaCodec *codec,
PurpleKeyValuePair *param);
/**
- * Gets an optional parameter based on the values given.
- *
- * @param codec The codec to find the parameter in.
- * @param name The name of the parameter to search for.
- * @param value The value to search for or NULL.
+ * purple_media_codec_get_optional_parameter:
+ * @codec: The codec to find the parameter in.
+ * @name: The name of the parameter to search for.
+ * @value: The value to search for or NULL.
*
- * @return The value found or NULL.
+ * Gets an optional parameter based on the values given.
*
- * @since 2.6.0
+ * Returns: The value found or NULL.
*/
PurpleKeyValuePair *purple_media_codec_get_optional_parameter(
PurpleMediaCodec *codec, const gchar *name,
const gchar *value);
/**
- * Copies a PurpleMediaCodec object.
- *
- * @param codec The codec to copy.
+ * purple_media_codec_copy:
+ * @codec: The codec to copy.
*
- * @return The copy of the codec.
+ * Copies a PurpleMediaCodec object.
*
- * @since 2.7.0
+ * Returns: The copy of the codec.
*/
PurpleMediaCodec *purple_media_codec_copy(PurpleMediaCodec *codec);
/**
- * Copies a GList of PurpleMediaCodec and its contents.
- *
- * @param codecs The list of codecs to be copied.
+ * purple_media_codec_list_copy:
+ * @codecs: The list of codecs to be copied.
*
- * @return The copy of the GList.
+ * Copies a GList of PurpleMediaCodec and its contents.
*
- * @since 2.6.0
+ * Returns: The copy of the GList.
*/
GList *purple_media_codec_list_copy(GList *codecs);
/**
- * Frees a GList of PurpleMediaCodec and its contents.
- *
- * @param codecs The list of codecs to be freed.
+ * purple_media_codec_list_free:
+ * @codecs: The list of codecs to be freed.
*
- * @since 2.6.0
+ * Frees a GList of PurpleMediaCodec and its contents.
*/
void purple_media_codec_list_free(GList *codecs);
/**
- * Creates a string representation of the codec.
- *
- * @param codec The codec to create the string of.
+ * purple_media_codec_to_string:
+ * @codec: The codec to create the string of.
*
- * @return The new string representation.
+ * Creates a string representation of the codec.
*
- * @since 2.6.0
+ * Returns: The new string representation.
*/
gchar *purple_media_codec_to_string(const PurpleMediaCodec *codec);
diff --git a/libpurple/media/enum-types.c b/libpurple/media/enum-types.c
index 5aa59089fb..c59ddcb705 100644
--- a/libpurple/media/enum-types.c
+++ b/libpurple/media/enum-types.c
@@ -1,8 +1,3 @@
-/**
- * @file enum-types.c Enum types for Media API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
diff --git a/libpurple/media/enum-types.h b/libpurple/media/enum-types.h
index e05baf2142..e420ad4175 100644
--- a/libpurple/media/enum-types.h
+++ b/libpurple/media/enum-types.h
@@ -1,8 +1,3 @@
-/**
- * @file enum-types.h Enum types for Media API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -26,6 +21,12 @@
#ifndef _PURPLE_MEDIA_ENUM_TYPES_H_
#define _PURPLE_MEDIA_ENUM_TYPES_H_
+/**
+ * SECTION:enum-types
+ * @section_id: libpurple-enum-types
+ * @short_description: <filename>media/enum-types.h</filename>
+ * @title: Enum types for Media API
+ */
#include <glib-object.h>
@@ -38,7 +39,11 @@ G_BEGIN_DECLS
#define PURPLE_TYPE_MEDIA_SESSION_TYPE (purple_media_session_type_get_type())
#define PURPLE_MEDIA_TYPE_STATE (purple_media_state_changed_get_type())
-/** Media candidate types */
+/**
+ * PurpleMediaCandidateType:
+ *
+ * Media candidate types
+ */
typedef enum {
PURPLE_MEDIA_CANDIDATE_TYPE_HOST,
PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX,
@@ -47,7 +52,11 @@ typedef enum {
PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST
} PurpleMediaCandidateType;
-/** Media caps */
+/**
+ * PurpleMediaCaps:
+ *
+ * Media caps
+ */
typedef enum {
PURPLE_MEDIA_CAPS_NONE = 0,
PURPLE_MEDIA_CAPS_AUDIO = 1,
@@ -59,14 +68,22 @@ typedef enum {
PURPLE_MEDIA_CAPS_CHANGE_DIRECTION = 1 << 6
} PurpleMediaCaps;
-/** Media component types */
+/**
+ * PurpleMediaComponentType:
+ *
+ * Media component types
+ */
typedef enum {
PURPLE_MEDIA_COMPONENT_NONE = 0,
PURPLE_MEDIA_COMPONENT_RTP = 1,
PURPLE_MEDIA_COMPONENT_RTCP = 2
} PurpleMediaComponentType;
-/** Media info types */
+/**
+ * PurpleMediaInfoType:
+ *
+ * Media info types
+ */
typedef enum {
PURPLE_MEDIA_INFO_HANGUP = 0,
PURPLE_MEDIA_INFO_ACCEPT,
@@ -79,13 +96,21 @@ typedef enum {
PURPLE_MEDIA_INFO_UNHOLD
} PurpleMediaInfoType;
-/** Media network protocols */
+/**
+ * PurpleMediaNetworkProtocol:
+ *
+ * Media network protocols
+ */
typedef enum {
PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
PURPLE_MEDIA_NETWORK_PROTOCOL_TCP
} PurpleMediaNetworkProtocol;
-/** Media session types */
+/**
+ * PurpleMediaSessionType:
+ *
+ * Media session types
+ */
typedef enum {
PURPLE_MEDIA_NONE = 0,
PURPLE_MEDIA_RECV_AUDIO = 1 << 0,
@@ -96,7 +121,11 @@ typedef enum {
PURPLE_MEDIA_VIDEO = PURPLE_MEDIA_RECV_VIDEO | PURPLE_MEDIA_SEND_VIDEO
} PurpleMediaSessionType;
-/** Media state-changed types */
+/**
+ * PurpleMediaState:
+ *
+ * Media state-changed types
+ */
typedef enum {
PURPLE_MEDIA_STATE_NEW = 0,
PURPLE_MEDIA_STATE_CONNECTED,
@@ -104,56 +133,56 @@ typedef enum {
} PurpleMediaState;
/**
- * Gets the media candidate type's GType
+ * purple_media_candidate_type_get_type:
*
- * @return The media candidate type's GType.
+ * Gets the media candidate type's GType
*
- * @since 2.6.0
+ * Returns: The media candidate type's GType.
*/
GType purple_media_candidate_type_get_type(void);
/**
- * Gets the type of the media caps flags
+ * purple_media_caps_get_type:
*
- * @return The media caps flags' GType
+ * Gets the type of the media caps flags
*
- * @since 2.7.0
+ * Returns: The media caps flags' GType
*/
GType purple_media_caps_get_type(void);
/**
- * Gets the type of the info type enum
+ * purple_media_info_type_get_type:
*
- * @return The info type enum's GType
+ * Gets the type of the info type enum
*
- * @since 2.6.0
+ * Returns: The info type enum's GType
*/
GType purple_media_info_type_get_type(void);
/**
- * Gets the media network protocol's GType
+ * purple_media_network_protocol_get_type:
*
- * @return The media network protocol's GType.
+ * Gets the media network protocol's GType
*
- * @since 2.6.0
+ * Returns: The media network protocol's GType.
*/
GType purple_media_network_protocol_get_type(void);
/**
- * Gets the media session type's GType
+ * purple_media_session_type_get_type:
*
- * @return The media session type's GType.
+ * Gets the media session type's GType
*
- * @since 2.6.0
+ * Returns: The media session type's GType.
*/
GType purple_media_session_type_get_type(void);
/**
- * Gets the type of the state-changed enum
+ * purple_media_state_changed_get_type:
*
- * @return The state-changed enum's GType
+ * Gets the type of the state-changed enum
*
- * @since 2.6.0
+ * Returns: The state-changed enum's GType
*/
GType purple_media_state_changed_get_type(void);
diff --git a/libpurple/mediamanager.c b/libpurple/mediamanager.c
index 1f49f2f941..922d96a132 100644
--- a/libpurple/mediamanager.c
+++ b/libpurple/mediamanager.c
@@ -1,8 +1,3 @@
-/**
- * @file mediamanager.c Media Manager API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -44,28 +39,17 @@
#else
#include <farstream/fs-element-added-notifier.h>
#endif
+#if GST_CHECK_VERSION(1,0,0)
+#include <gst/video/videooverlay.h>
+#else
#include <gst/interfaces/xoverlay.h>
+#endif
-/** @copydoc _PurpleMediaManagerPrivate */
-typedef struct _PurpleMediaManagerPrivate PurpleMediaManagerPrivate;
/** @copydoc _PurpleMediaOutputWindow */
typedef struct _PurpleMediaOutputWindow PurpleMediaOutputWindow;
/** @copydoc _PurpleMediaManagerPrivate */
typedef struct _PurpleMediaElementInfoPrivate PurpleMediaElementInfoPrivate;
-/** The media manager class. */
-struct _PurpleMediaManagerClass
-{
- GObjectClass parent_class; /**< The parent class. */
-};
-
-/** The media manager's data. */
-struct _PurpleMediaManager
-{
- GObject parent; /**< The parent of this manager. */
- PurpleMediaManagerPrivate *priv; /**< Private data for the manager. */
-};
-
struct _PurpleMediaOutputWindow
{
gulong id;
@@ -270,8 +254,11 @@ purple_media_manager_get_pipeline(PurpleMediaManager *manager)
gst_bus_add_signal_watch(GST_BUS(bus));
g_signal_connect(G_OBJECT(bus), "message",
G_CALLBACK(pipeline_bus_call), manager);
- gst_bus_set_sync_handler(bus,
- gst_bus_sync_signal_handler, NULL);
+#if GST_CHECK_VERSION(1,0,0)
+ 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
gst_object_unref(bus);
filename = g_build_filename(purple_user_dir(),
@@ -405,20 +392,31 @@ request_pad_unlinked_cb(GstPad *pad, GstPad *peer, gpointer user_data)
{
GstElement *parent = GST_ELEMENT_PARENT(pad);
GstIterator *iter;
+#if GST_CHECK_VERSION(1,0,0)
+ GValue tmp = G_VALUE_INIT;
+#endif
GstPad *remaining_pad;
GstIteratorResult result;
- gst_element_release_request_pad(GST_ELEMENT_PARENT(pad), pad);
+ gst_element_release_request_pad(parent, pad);
iter = gst_element_iterate_src_pads(parent);
+#if GST_CHECK_VERSION(1,0,0)
+ result = gst_iterator_next(iter, &tmp);
+#else
result = gst_iterator_next(iter, (gpointer)&remaining_pad);
+#endif
if (result == GST_ITERATOR_DONE) {
gst_element_set_locked_state(parent, TRUE);
gst_element_set_state(parent, GST_STATE_NULL);
gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(parent)), parent);
} else if (result == GST_ITERATOR_OK) {
+#if GST_CHECK_VERSION(1,0,0)
+ remaining_pad = g_value_get_object(&tmp);
+ g_value_reset(&tmp);
+#endif
gst_object_unref(remaining_pad);
}
@@ -456,7 +454,11 @@ purple_media_manager_get_video_caps(PurpleMediaManager *manager)
{
#ifdef USE_VV
if (manager->priv->video_caps == NULL)
+#if GST_CHECK_VERSION(1,0,0)
+ manager->priv->video_caps = gst_caps_from_string("video/x-raw,"
+#else
manager->priv->video_caps = gst_caps_from_string("video/x-raw-yuv,"
+#endif
"width=[250,352], height=[200,288], framerate=[1/1,20/1]");
return manager->priv->video_caps;
#else
@@ -539,7 +541,11 @@ purple_media_manager_get_element(PurpleMediaManager *manager,
g_free(id);
tee = gst_bin_get_by_name(GST_BIN(ret), "tee");
+#if GST_CHECK_VERSION(1,0,0)
+ pad = gst_element_get_request_pad(tee, "src_%u");
+#else
pad = gst_element_get_request_pad(tee, "src%d");
+#endif
gst_object_unref(tee);
ghost = gst_ghost_pad_new(NULL, pad);
gst_object_unref(pad);
@@ -730,9 +736,12 @@ window_id_cb(GstBus *bus, GstMessage *msg, PurpleMediaOutputWindow *ow)
{
GstElement *sink;
- if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT ||
- !gst_structure_has_name(msg->structure,
- "prepare-xwindow-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
+ || !gst_structure_has_name(msg->structure, "prepare-xwindow-id"))
+#endif
return;
sink = GST_ELEMENT(GST_MESSAGE_SRC(msg));
@@ -746,8 +755,16 @@ window_id_cb(GstBus *bus, GstMessage *msg, PurpleMediaOutputWindow *ow)
| G_SIGNAL_MATCH_DATA, 0, 0, NULL,
window_id_cb, ow);
- gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(
- GST_MESSAGE_SRC(msg)), ow->window_id);
+#if GST_CHECK_VERSION(1,0,0)
+ gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(GST_MESSAGE_SRC(msg)),
+ ow->window_id);
+#elif GST_CHECK_VERSION(0,10,31)
+ gst_x_overlay_set_window_handle(GST_X_OVERLAY(GST_MESSAGE_SRC(msg)),
+ ow->window_id);
+#else
+ gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(GST_MESSAGE_SRC(msg)),
+ ow->window_id);
+#endif
}
#endif
@@ -772,17 +789,19 @@ purple_media_manager_create_output_window(PurpleMediaManager *manager,
(participant == ow->participant)) &&
!strcmp(session_id, ow->session_id)) {
GstBus *bus;
- GstElement *queue, *colorspace;
+ GstElement *queue, *convert;
GstElement *tee = purple_media_get_tee(media,
session_id, participant);
if (tee == NULL)
continue;
- queue = gst_element_factory_make(
- "queue", NULL);
- colorspace = gst_element_factory_make(
- "ffmpegcolorspace", NULL);
+ queue = gst_element_factory_make("queue", NULL);
+#if GST_CHECK_VERSION(1,0,0)
+ convert = gst_element_factory_make("videoconvert", NULL);
+#else
+ convert = gst_element_factory_make("ffmpegcolorspace", NULL);
+#endif
ow->sink = purple_media_manager_get_element(
manager, PURPLE_MEDIA_RECV_VIDEO,
ow->media, ow->session_id,
@@ -795,7 +814,7 @@ purple_media_manager_create_output_window(PurpleMediaManager *manager,
if (g_object_class_find_property(klass,
"sync"))
g_object_set(G_OBJECT(ow->sink),
- "sync", "FALSE", NULL);
+ "sync", FALSE, NULL);
if (g_object_class_find_property(klass,
"async"))
g_object_set(G_OBJECT(ow->sink),
@@ -803,7 +822,7 @@ purple_media_manager_create_output_window(PurpleMediaManager *manager,
}
gst_bin_add_many(GST_BIN(GST_ELEMENT_PARENT(tee)),
- queue, colorspace, ow->sink, NULL);
+ queue, convert, ow->sink, NULL);
bus = gst_pipeline_get_bus(GST_PIPELINE(
manager->priv->pipeline));
@@ -812,10 +831,10 @@ purple_media_manager_create_output_window(PurpleMediaManager *manager,
gst_object_unref(bus);
gst_element_set_state(ow->sink, GST_STATE_PLAYING);
- gst_element_set_state(colorspace, GST_STATE_PLAYING);
+ gst_element_set_state(convert, GST_STATE_PLAYING);
gst_element_set_state(queue, GST_STATE_PLAYING);
- gst_element_link(colorspace, ow->sink);
- gst_element_link(queue, colorspace);
+ gst_element_link(convert, ow->sink);
+ gst_element_link(queue, convert);
gst_element_link(tee, queue);
}
}
@@ -1184,14 +1203,16 @@ purple_media_element_info_class_init(PurpleMediaElementInfoClass *klass)
"ID",
"The unique identifier of the element.",
NULL,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class, PROP_NAME,
g_param_spec_string("name",
"Name",
"The friendly/display name of this element.",
NULL,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class, PROP_TYPE,
g_param_spec_flags("type",
@@ -1199,13 +1220,15 @@ purple_media_element_info_class_init(PurpleMediaElementInfoClass *klass)
"The type of element this is.",
PURPLE_TYPE_MEDIA_ELEMENT_TYPE,
PURPLE_MEDIA_ELEMENT_NONE,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class, PROP_CREATE_CB,
g_param_spec_pointer("create-cb",
"Create Callback",
"The function called to create this element.",
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
g_type_class_add_private(klass, sizeof(PurpleMediaElementInfoPrivate));
}
diff --git a/libpurple/mediamanager.h b/libpurple/mediamanager.h
index d9f54ad543..e55865c983 100644
--- a/libpurple/mediamanager.h
+++ b/libpurple/mediamanager.h
@@ -1,8 +1,3 @@
-/**
- * @file mediamanager.h Media Manager API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -26,20 +21,24 @@
#ifndef _PURPLE_MEDIA_MANAGER_H_
#define _PURPLE_MEDIA_MANAGER_H_
+/**
+ * SECTION:mediamanager
+ * @section_id: libpurple-mediamanager
+ * @short_description: <filename>mediamanager.h</filename>
+ * @title: Media Manager Object
+ */
#include <glib.h>
#include <glib-object.h>
-/** An opaque structure representing a group of (usually all) media calls. */
typedef struct _PurpleMediaManager PurpleMediaManager;
-/** The GObject class structure of the PurpleMediaManager object. */
typedef struct _PurpleMediaManagerClass PurpleMediaManagerClass;
+typedef struct _PurpleMediaManagerPrivate PurpleMediaManagerPrivate;
+
#include "account.h"
#include "media.h"
-G_BEGIN_DECLS
-
#define PURPLE_TYPE_MEDIA_MANAGER (purple_media_manager_get_type())
#define PURPLE_MEDIA_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManager))
#define PURPLE_MEDIA_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManagerClass))
@@ -47,45 +46,70 @@ G_BEGIN_DECLS
#define PURPLE_IS_MEDIA_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA_MANAGER))
#define PURPLE_MEDIA_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManagerClass))
-#ifdef __cplusplus
-extern "C" {
-#endif
+/**
+ * PurpleMediaManager:
+ *
+ * The media manager's data.
+ */
+struct _PurpleMediaManager
+{
+ GObject parent;
+
+ /*< private >*/
+ PurpleMediaManagerPrivate *priv;
+};
+
+/**
+ * PurpleMediaManagerClass:
+ *
+ * The media manager class.
+ */
+struct _PurpleMediaManagerClass
+{
+ GObjectClass parent_class;
+
+ /*< private >*/
+ void (*purple_reserved1)(void);
+ void (*purple_reserved2)(void);
+ void (*purple_reserved3)(void);
+ void (*purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name Media Manager API */
+/* Media Manager API */
/**************************************************************************/
-/*@{*/
/**
- * Gets the media manager's GType.
+ * purple_media_manager_get_type:
*
- * @return The media manager's GType.
+ * Gets the media manager's GType.
*
- * @since 2.6.0
+ * Returns: The media manager's GType.
*/
GType purple_media_manager_get_type(void);
/**
- * Gets the "global" media manager object. It's created if it doesn't already exist.
+ * purple_media_manager_get:
*
- * @return The "global" instance of the media manager object.
+ * Gets the "global" media manager object. It's created if it doesn't already exist.
*
- * @since 2.6.0
+ * Returns: The "global" instance of the media manager object.
*/
PurpleMediaManager *purple_media_manager_get(void);
/**
- * Creates a media session.
- *
- * @param manager The media manager to create the session under.
- * @param account The account to create the session on.
- * @param conference_type The conference type to feed into Farsight2.
- * @param remote_user The remote user to initiate the session with.
- * @param initiator TRUE if the local user is the initiator of this media call, FALSE otherwise.
+ * purple_media_manager_create_media:
+ * @manager: The media manager to create the session under.
+ * @account: The account to create the session on.
+ * @conference_type: The conference type to feed into Farsight2.
+ * @remote_user: The remote user to initiate the session with.
+ * @initiator: TRUE if the local user is the initiator of this media call, FALSE otherwise.
*
- * @return A newly created media session.
+ * Creates a media session.
*
- * @since 2.6.0
+ * Returns: A newly created media session.
*/
PurpleMedia *purple_media_manager_create_media(PurpleMediaManager *manager,
PurpleAccount *account,
@@ -94,153 +118,138 @@ PurpleMedia *purple_media_manager_create_media(PurpleMediaManager *manager,
gboolean initiator);
/**
- * Gets all of the media sessions.
+ * purple_media_manager_get_media:
+ * @manager: The media manager to get all of the sessions from.
*
- * @param manager The media manager to get all of the sessions from.
- *
- * @return A list of all the media sessions.
+ * Gets all of the media sessions.
*
- * @since 2.6.0
+ * Returns: A list of all the media sessions.
*/
GList *purple_media_manager_get_media(PurpleMediaManager *manager);
/**
- * Gets all of the media sessions for a given account.
+ * purple_media_manager_get_media_by_account:
+ * @manager: The media manager to get the sessions from.
+ * @account: The account the sessions are on.
*
- * @param manager The media manager to get the sessions from.
- * @param account The account the sessions are on.
- *
- * @return A list of the media sessions on the given account.
+ * Gets all of the media sessions for a given account.
*
- * @since 2.6.0
+ * Returns: A list of the media sessions on the given account.
*/
GList *purple_media_manager_get_media_by_account(
PurpleMediaManager *manager, PurpleAccount *account);
/**
- * Removes a media session from the media manager.
- *
- * @param manager The media manager to remove the media session from.
- * @param media The media session to remove.
+ * purple_media_manager_remove_media:
+ * @manager: The media manager to remove the media session from.
+ * @media: The media session to remove.
*
- * @since 2.6.0
+ * Removes a media session from the media manager.
*/
void
purple_media_manager_remove_media(PurpleMediaManager *manager,
PurpleMedia *media);
/**
+ * purple_media_manager_create_output_window:
+ * @manager: Manager the output windows are registered with.
+ * @media: Media session the output windows are registered for.
+ * @session_id: The session the output windows are registered with.
+ * @participant: The participant the output windows are registered with.
+ *
* Signals that output windows should be created for the chosen stream.
*
* This shouldn't be called outside of mediamanager.c and media.c
*
- * @param manager Manager the output windows are registered with.
- * @param media Media session the output windows are registered for.
- * @param session_id The session the output windows are registered with.
- * @param participant The participant the output windows are registered with.
- *
- * @return TRUE if it succeeded, FALSE if it failed.
- *
- * @since 2.6.0
+ * Returns: TRUE if it succeeded, FALSE if it failed.
*/
gboolean purple_media_manager_create_output_window(
PurpleMediaManager *manager, PurpleMedia *media,
const gchar *session_id, const gchar *participant);
/**
- * Registers a video output window to be created for a given stream.
- *
- * @param manager The manager to register the output window with.
- * @param media The media instance to find the stream in.
- * @param session_id The session the stream is associated with.
- * @param participant The participant the stream is associated with.
- * @param window_id The window ID to embed the video in.
+ * purple_media_manager_set_output_window:
+ * @manager: The manager to register the output window with.
+ * @media: The media instance to find the stream in.
+ * @session_id: The session the stream is associated with.
+ * @participant: The participant the stream is associated with.
+ * @window_id: The window ID to embed the video in.
*
- * @return A unique ID to the registered output window, 0 if it failed.
+ * Registers a video output window to be created for a given stream.
*
- * @since 2.6.0
+ * Returns: A unique ID to the registered output window, 0 if it failed.
*/
gulong purple_media_manager_set_output_window(PurpleMediaManager *manager,
PurpleMedia *media, const gchar *session_id,
const gchar *participant, gulong window_id);
/**
- * Remove a previously registerd output window.
+ * purple_media_manager_remove_output_window:
+ * @manager: The manager the output window was registered with.
+ * @output_window_id: The ID of the output window.
*
- * @param manager The manager the output window was registered with.
- * @param output_window_id The ID of the output window.
- *
- * @return TRUE if it found the output window and was successful, else FALSE.
+ * Remove a previously registerd output window.
*
- * @since 2.6.0
+ * Returns: TRUE if it found the output window and was successful, else FALSE.
*/
gboolean purple_media_manager_remove_output_window(
PurpleMediaManager *manager, gulong output_window_id);
/**
- * Remove all output windows for a given conference/session/participant/stream.
+ * purple_media_manager_remove_output_windows:
+ * @manager: The manager the output windows were registered with.
+ * @media: The media instance the output windows were registered for.
+ * @session_id: The session the output windows were registered for.
+ * @participant: The participant the output windows were registered for.
*
- * @param manager The manager the output windows were registered with.
- * @param media The media instance the output windows were registered for.
- * @param session_id The session the output windows were registered for.
- * @param participant The participant the output windows were registered for.
- *
- * @since 2.6.0
+ * Remove all output windows for a given conference/session/participant/stream.
*/
void purple_media_manager_remove_output_windows(
PurpleMediaManager *manager, PurpleMedia *media,
const gchar *session_id, const gchar *participant);
/**
- * Sets which media caps the UI supports.
- *
- * @param manager The manager to set the caps on.
- * @param caps The caps to set.
+ * purple_media_manager_set_ui_caps:
+ * @manager: The manager to set the caps on.
+ * @caps: The caps to set.
*
- * @since 2.6.0
+ * Sets which media caps the UI supports.
*/
void purple_media_manager_set_ui_caps(PurpleMediaManager *manager,
PurpleMediaCaps caps);
/**
- * Gets which media caps the UI supports.
+ * purple_media_manager_get_ui_caps:
+ * @manager: The manager to get caps from.
*
- * @param manager The manager to get caps from.
- *
- * @return caps The caps retrieved.
+ * Gets which media caps the UI supports.
*
- * @since 2.6.0
+ * Returns: caps The caps retrieved.
*/
PurpleMediaCaps purple_media_manager_get_ui_caps(PurpleMediaManager *manager);
/**
- * Sets which media backend type media objects will use.
- *
- * @param manager The manager to set the caps on.
- * @param backend_type The media backend type to use.
+ * purple_media_manager_set_backend_type:
+ * @manager: The manager to set the caps on.
+ * @backend_type: The media backend type to use.
*
- * @since 2.7.0
+ * Sets which media backend type media objects will use.
*/
void purple_media_manager_set_backend_type(PurpleMediaManager *manager,
GType backend_type);
/**
- * Gets which media backend type media objects will use.
+ * purple_media_manager_get_backend_type:
+ * @manager: The manager to get the media backend type from.
*
- * @param manager The manager to get the media backend type from.
- *
- * @return The type of media backend type media objects will use.
+ * Gets which media backend type media objects will use.
*
- * @since 2.7.0
+ * Returns: The type of media backend type media objects will use.
*/
GType purple_media_manager_get_backend_type(PurpleMediaManager *manager);
/*}@*/
-#ifdef __cplusplus
-}
-#endif
-
G_END_DECLS
#endif /* _PURPLE_MEDIA_MANAGER_H_ */
diff --git a/libpurple/memorypool.c b/libpurple/memorypool.c
new file mode 100644
index 0000000000..dbfcdaa0b7
--- /dev/null
+++ b/libpurple/memorypool.c
@@ -0,0 +1,383 @@
+/*
+ * 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 "memorypool.h"
+
+#include <string.h>
+
+#define PURPLE_MEMORY_POOL_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEMORY_POOL, PurpleMemoryPoolPrivate))
+#define PURPLE_MEMORY_POOL_BLOCK_PADDING (sizeof(guint64))
+#define PURPLE_MEMORY_POINTER_SHIFT(pointer, value) \
+ (gpointer)((guintptr)(pointer) + (value))
+#define PURPLE_MEMORY_PADDED(pointer, padding) \
+ (gpointer)((((guintptr)(pointer) - 1) / (padding) + 1) * padding)
+
+#define PURPLE_MEMORY_POOL_DEFAULT_BLOCK_SIZE 1024
+#define PURPLE_MEMORY_POOL_DISABLED FALSE
+
+typedef struct _PurpleMemoryPoolBlock PurpleMemoryPoolBlock;
+
+typedef struct
+{
+ gboolean disabled;
+ gulong block_size;
+
+ PurpleMemoryPoolBlock *first_block;
+ PurpleMemoryPoolBlock *last_block;
+} PurpleMemoryPoolPrivate;
+
+struct _PurpleMemoryPoolBlock
+{
+ gpointer available_ptr;
+ gpointer end_ptr;
+ PurpleMemoryPoolBlock *next;
+};
+
+enum
+{
+ PROP_ZERO,
+ PROP_BLOCK_SIZE,
+ PROP_LAST
+};
+
+static GObjectClass *parent_class = NULL;
+static GParamSpec *properties[PROP_LAST];
+
+
+/*******************************************************************************
+ * Memory allocation/deallocation
+ ******************************************************************************/
+
+static PurpleMemoryPoolBlock *
+purple_memory_pool_block_new(gulong block_size)
+{
+ gpointer block_raw;
+ PurpleMemoryPoolBlock *block;
+ gsize total_size;
+
+ /* ceil block struct size to the multipy of align */
+ total_size = ((sizeof(PurpleMemoryPoolBlock) - 1) /
+ PURPLE_MEMORY_POOL_BLOCK_PADDING + 1) *
+ sizeof(PurpleMemoryPoolBlock);
+ g_return_val_if_fail(block_size < G_MAXSIZE - total_size, NULL);
+ total_size += block_size;
+
+ block_raw = g_try_malloc(total_size);
+ g_return_val_if_fail(block_raw != NULL, NULL);
+ block = block_raw;
+
+ /* in fact, we don't set available_ptr padded to
+ * PURPLE_MEMORY_POOL_BLOCK_PADDING, but we guarantee, there is at least
+ * block_size long block if padded to that value. */
+ block->available_ptr = PURPLE_MEMORY_POINTER_SHIFT(block_raw,
+ sizeof(PurpleMemoryPoolBlock));
+ block->end_ptr = PURPLE_MEMORY_POINTER_SHIFT(block_raw, total_size);
+ block->next = NULL;
+
+ return block;
+}
+
+static gpointer
+purple_memory_pool_alloc_impl(PurpleMemoryPool *pool, gsize size, guint alignment)
+{
+ PurpleMemoryPoolPrivate *priv = PURPLE_MEMORY_POOL_GET_PRIVATE(pool);
+ PurpleMemoryPoolBlock *blk;
+ gpointer mem = NULL;
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ if (priv->disabled) {
+ /* XXX: this may cause some leaks */
+ return g_try_malloc(size);
+ }
+
+ g_return_val_if_fail(alignment <= PURPLE_MEMORY_POOL_BLOCK_PADDING, NULL);
+ g_warn_if_fail(alignment >= 1);
+ if (alignment < 1)
+ alignment = 1;
+
+ blk = priv->last_block;
+
+ if (blk) {
+ mem = PURPLE_MEMORY_PADDED(blk->available_ptr, alignment);
+ if (mem >= blk->end_ptr)
+ mem = NULL;
+ else if (mem < blk->available_ptr) /* gpointer overflow */
+ mem = NULL;
+ else if (PURPLE_MEMORY_POINTER_SHIFT(mem, size) >= blk->end_ptr)
+ mem = NULL;
+ }
+
+ if (mem == NULL) {
+ gsize real_size = priv->block_size;
+ if (real_size < size)
+ real_size = size;
+ blk = purple_memory_pool_block_new(real_size);
+ g_return_val_if_fail(blk != NULL, NULL);
+
+ g_assert((priv->first_block == NULL) ==
+ (priv->last_block == NULL));
+
+ if (priv->first_block == NULL) {
+ priv->first_block = blk;
+ priv->last_block = blk;
+ } else {
+ priv->last_block->next = blk;
+ priv->last_block = blk;
+ }
+
+ mem = PURPLE_MEMORY_PADDED(blk->available_ptr, alignment);
+ g_assert((guintptr)mem + size < (guintptr)blk->end_ptr);
+ g_assert(mem >= blk->available_ptr); /* gpointer overflow */
+ }
+
+ g_assert(blk != NULL);
+ g_assert(mem != NULL);
+
+ blk->available_ptr = PURPLE_MEMORY_POINTER_SHIFT(mem, size);
+ g_assert(blk->available_ptr <= blk->end_ptr);
+
+ return mem;
+}
+
+static void
+purple_memory_pool_cleanup_impl(PurpleMemoryPool *pool)
+{
+ PurpleMemoryPoolPrivate *priv = PURPLE_MEMORY_POOL_GET_PRIVATE(pool);
+ PurpleMemoryPoolBlock *blk;
+
+ g_return_if_fail(priv != NULL);
+
+ blk = priv->first_block;
+ priv->first_block = NULL;
+ priv->last_block = NULL;
+ while (blk) {
+ PurpleMemoryPoolBlock *next = blk->next;
+ g_free(blk);
+ blk = next;
+ }
+}
+
+
+/*******************************************************************************
+ * API implementation
+ ******************************************************************************/
+
+void
+purple_memory_pool_set_block_size(PurpleMemoryPool *pool, gulong block_size)
+{
+ PurpleMemoryPoolPrivate *priv = PURPLE_MEMORY_POOL_GET_PRIVATE(pool);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->block_size = block_size;
+ g_object_notify_by_pspec(G_OBJECT(pool), properties[PROP_BLOCK_SIZE]);
+}
+
+gpointer
+purple_memory_pool_alloc(PurpleMemoryPool *pool, gsize size, guint alignment)
+{
+ PurpleMemoryPoolClass *klass;
+
+ if (size == 0)
+ return NULL;
+
+ g_return_val_if_fail(PURPLE_IS_MEMORY_POOL(pool), NULL);
+
+ klass = PURPLE_MEMORY_POOL_GET_CLASS(pool);
+ g_return_val_if_fail(klass != NULL, NULL);
+ g_return_val_if_fail(klass->palloc != NULL, NULL);
+
+ return klass->palloc(pool, size, alignment);
+}
+
+gpointer
+purple_memory_pool_alloc0(PurpleMemoryPool *pool, gsize size, guint alignment)
+{
+ gpointer mem;
+
+ if (size == 0)
+ return NULL;
+
+ mem = purple_memory_pool_alloc(pool, size, alignment);
+ g_return_val_if_fail(mem != NULL, NULL);
+
+ memset(mem, 0, size);
+
+ return mem;
+}
+
+void
+purple_memory_pool_free(PurpleMemoryPool *pool, gpointer mem)
+{
+ PurpleMemoryPoolClass *klass;
+
+ if (mem == NULL)
+ return;
+
+ g_return_if_fail(PURPLE_IS_MEMORY_POOL(pool));
+
+ klass = PURPLE_MEMORY_POOL_GET_CLASS(pool);
+ g_return_if_fail(klass != NULL);
+
+ if (klass->pfree)
+ klass->pfree(pool, mem);
+}
+
+void
+purple_memory_pool_cleanup(PurpleMemoryPool *pool)
+{
+ PurpleMemoryPoolClass *klass;
+
+ g_return_if_fail(PURPLE_IS_MEMORY_POOL(pool));
+
+ klass = PURPLE_MEMORY_POOL_GET_CLASS(pool);
+ g_return_if_fail(klass != NULL);
+
+ klass->cleanup(pool);
+}
+
+
+/*******************************************************************************
+ * Object stuff
+ ******************************************************************************/
+
+PurpleMemoryPool *
+purple_memory_pool_new(void)
+{
+ return g_object_new(PURPLE_TYPE_MEMORY_POOL, NULL);
+}
+
+static void
+purple_memory_pool_init(GTypeInstance *instance, gpointer klass)
+{
+ PurpleMemoryPool *pool = PURPLE_MEMORY_POOL(instance);
+ PurpleMemoryPoolPrivate *priv = PURPLE_MEMORY_POOL_GET_PRIVATE(pool);
+
+ priv->disabled = PURPLE_MEMORY_POOL_DISABLED;
+}
+
+static void
+purple_memory_pool_finalize(GObject *obj)
+{
+ purple_memory_pool_cleanup(PURPLE_MEMORY_POOL(obj));
+
+ G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+static void
+purple_memory_pool_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleMemoryPool *pool = PURPLE_MEMORY_POOL(obj);
+ PurpleMemoryPoolPrivate *priv = PURPLE_MEMORY_POOL_GET_PRIVATE(pool);
+
+ switch (param_id) {
+ case PROP_BLOCK_SIZE:
+ g_value_set_ulong(value, priv->block_size);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ }
+}
+
+static void
+purple_memory_pool_set_property(GObject *obj, guint param_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ PurpleMemoryPool *pool = PURPLE_MEMORY_POOL(obj);
+ PurpleMemoryPoolPrivate *priv = PURPLE_MEMORY_POOL_GET_PRIVATE(pool);
+
+ switch (param_id) {
+ case PROP_BLOCK_SIZE:
+ priv->block_size = g_value_get_ulong(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ }
+}
+
+static void
+purple_memory_pool_class_init(PurpleMemoryPoolClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ g_type_class_add_private(klass, sizeof(PurpleMemoryPoolPrivate));
+
+ obj_class->finalize = purple_memory_pool_finalize;
+ obj_class->get_property = purple_memory_pool_get_property;
+ obj_class->set_property = purple_memory_pool_set_property;
+
+ klass->palloc = purple_memory_pool_alloc_impl;
+ klass->cleanup = purple_memory_pool_cleanup_impl;
+
+ properties[PROP_BLOCK_SIZE] = g_param_spec_ulong("block-size",
+ "Block size", "The default size of each block of pool memory.",
+ 0, G_MAXULONG, PURPLE_MEMORY_POOL_DEFAULT_BLOCK_SIZE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, PROP_LAST, properties);
+}
+
+GType
+purple_memory_pool_get_type(void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY(type == 0)) {
+ static const GTypeInfo info = {
+ .class_size = sizeof(PurpleMemoryPoolClass),
+ .class_init = (GClassInitFunc)purple_memory_pool_class_init,
+ .instance_size = sizeof(PurpleMemoryPool),
+ .instance_init = (GInstanceInitFunc)purple_memory_pool_init
+ };
+
+ type = g_type_register_static(G_TYPE_OBJECT,
+ "PurpleMemoryPool", &info, 0);
+ }
+
+ return type;
+}
+
+gchar *
+purple_memory_pool_strdup(PurpleMemoryPool *pool, const gchar *str)
+{
+ gsize str_len;
+ gchar *str_dup;
+
+ if (str == NULL)
+ return NULL;
+
+ g_return_val_if_fail(PURPLE_IS_MEMORY_POOL(pool), NULL);
+
+ str_len = strlen(str);
+ str_dup = purple_memory_pool_alloc(pool, str_len + 1, sizeof(gchar));
+ g_return_val_if_fail(str_dup != NULL, NULL);
+
+ memcpy(str_dup, str, str_len);
+ str_dup[str_len] = '\0';
+
+ return str_dup;
+}
diff --git a/libpurple/memorypool.h b/libpurple/memorypool.h
new file mode 100644
index 0000000000..0eb396cdba
--- /dev/null
+++ b/libpurple/memorypool.h
@@ -0,0 +1,201 @@
+/*
+ * 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 PURPLE_MEMORY_POOL_H
+#define PURPLE_MEMORY_POOL_H
+/**
+ * SECTION:memorypool
+ * @include:memorypool.h
+ * @section_id: libpurple-memorypool
+ * @short_description: a container for a large number of small chunks of memory
+ * @title: Memory pools
+ *
+ * A #PurpleMemoryPool allows allocating many small objects within a single
+ * memory range and releasing them all at once using a single call. This
+ * prevents memory fragmentation and improves performance when used properly.
+ * It's purpose is to act as an internal storage for other object private
+ * structures, like tree nodes, string chunks, list elements.
+ *
+ * Current implementation is not optimized for releasing individual objects,
+ * so it may be extremely inefficient, when misused. On every memory allocation,
+ * it checks if there is enough space in current block. If there is not enough
+ * room here, it creates another block of memory. On pool destruction or calling
+ * #purple_memory_pool_cleanup, the whole block chain will be freed, using only
+ * one #g_free call for every block.
+ */
+
+#include <glib-object.h>
+
+#define PURPLE_TYPE_MEMORY_POOL (purple_memory_pool_get_type())
+#define PURPLE_MEMORY_POOL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEMORY_POOL, PurpleMemoryPool))
+#define PURPLE_MEMORY_POOL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEMORY_POOL, PurpleMemoryPoolClass))
+#define PURPLE_IS_MEMORY_POOL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_MEMORY_POOL))
+#define PURPLE_IS_MEMORY_POOL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEMORY_POOL))
+#define PURPLE_MEMORY_POOL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEMORY_POOL, PurpleMemoryPoolClass))
+
+typedef struct _PurpleMemoryPool PurpleMemoryPool;
+typedef struct _PurpleMemoryPoolClass PurpleMemoryPoolClass;
+
+/**
+ * PurpleMemoryPool:
+ *
+ * The memory pool object instance.
+ */
+struct _PurpleMemoryPool
+{
+ /*< private >*/
+ GObject parent_instance;
+};
+
+/**
+ * PurpleMemoryPoolClass:
+ * @palloc: alloates memory for a specific memory pool subclass,
+ * see #purple_memory_pool_alloc.
+ * @pfree: frees memory allocated within a pool, see #purple_memory_pool_free.
+ * May be %NULL.
+ * @cleanup: frees (or marks as unused) all memory allocated within a pool.
+ * See #purple_memory_pool_cleanup.
+ *
+ * Base class for #PurpleMemoryPool objects.
+ */
+struct _PurpleMemoryPoolClass
+{
+ /*< private >*/
+ GObjectClass parent_class;
+
+ /*< public >*/
+ gpointer (*palloc)(PurpleMemoryPool *pool, gsize size, guint alignment);
+ gpointer (*pfree)(PurpleMemoryPool *pool, gpointer mem);
+ void (*cleanup)(PurpleMemoryPool *pool);
+
+ /*< private >*/
+ void (*purple_reserved1)(void);
+ void (*purple_reserved2)(void);
+ void (*purple_reserved3)(void);
+ void (*purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+/**
+ * purple_memory_pool_get_type:
+ *
+ * Returns: the #GType for a #PurpleMemoryPool.
+ */
+GType
+purple_memory_pool_get_type(void);
+
+/**
+ * purple_memory_pool_new:
+ *
+ * Creates a new memory pool.
+ *
+ * Returns: the new #PurpleMemoryPool.
+ */
+PurpleMemoryPool *
+purple_memory_pool_new(void);
+
+/**
+ * purple_memory_pool_set_block_size:
+ * @pool: the memory pool.
+ * @block_size: the new default block size.
+ *
+ * Sets new default block size for a memory pool. You might want to call this
+ * before any allocation, to have it applied to the every created block.
+ */
+void
+purple_memory_pool_set_block_size(PurpleMemoryPool *pool, gulong block_size);
+
+/**
+ * purple_memory_pool_alloc:
+ * @pool: the memory pool.
+ * @size: the size of memory to be allocated.
+ * @alignment: the alignment of memory block (should be a power of two).
+ *
+ * Allocates an aligned memory block within a pool.
+ *
+ * Returns: the pointer to a memory block. This should be freed with
+ * a call to #purple_memory_pool_free.
+ */
+gpointer
+purple_memory_pool_alloc(PurpleMemoryPool *pool, gsize size, guint alignment);
+
+/**
+ * purple_memory_pool_alloc0:
+ * @pool: the memory pool.
+ * @size: the size of memory to be allocated.
+ * @alignment: the alignment of memory block (should be a power of two).
+ *
+ * Allocates an aligned memory block within a pool and sets its contents to
+ * zeros.
+ *
+ * Returns: the pointer to a memory block. This should be freed with
+ * a call to #purple_memory_pool_free.
+ */
+gpointer
+purple_memory_pool_alloc0(PurpleMemoryPool *pool, gsize size, guint alignment);
+
+/**
+ * purple_memory_pool_free:
+ * @pool: the memory pool.
+ * @mem: the pointer to a memory block.
+ *
+ * Frees a memory allocated within a memory pool. This can be a no-op in certain
+ * implementations. Thus, it don't need to be called in every case. Thus, the
+ * freed memory is wasted until you call #purple_memory_pool_cleanup
+ * or destroy the @pool.
+ */
+void
+purple_memory_pool_free(PurpleMemoryPool *pool, gpointer mem);
+
+/**
+ * purple_memory_pool_cleanup:
+ * @pool: the memory pool.
+ *
+ * Marks all memory allocated within a memory pool as not used. It may free
+ * resources, but don't have to.
+ */
+void
+purple_memory_pool_cleanup(PurpleMemoryPool *pool);
+
+/**
+ * purple_memory_pool_strdup:
+ * @pool: the memory pool.
+ * @str: the string to duplicate.
+ *
+ * Duplicates a string using a memory allocated within a memory pool. If @str is
+ * %NULL, it returns %NULL. The returned string should be freed with g_free()
+ * when no longer needed.
+ *
+ * Returns: a newly-allocated copy of @str.
+ */
+gchar *
+purple_memory_pool_strdup(PurpleMemoryPool *pool, const gchar *str);
+
+G_END_DECLS
+
+#endif /* PURPLE_MEMORY_POOL_H */
diff --git a/libpurple/message.c b/libpurple/message.c
new file mode 100644
index 0000000000..d4a8ed14fb
--- /dev/null
+++ b/libpurple/message.c
@@ -0,0 +1,407 @@
+/* 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 "dbus-maybe.h"
+#include "glibcompat.h"
+
+#include "debug.h"
+#include "enums.h"
+#include "message.h"
+
+#define PURPLE_MESSAGE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MESSAGE, PurpleMessagePrivate))
+
+typedef struct {
+ guint id;
+ gchar *author;
+ gchar *author_alias;
+ gchar *recipient;
+ gchar *contents;
+ guint64 msgtime;
+ PurpleMessageFlags flags;
+} PurpleMessagePrivate;
+
+enum
+{
+ PROP_0,
+ PROP_ID,
+ PROP_AUTHOR,
+ PROP_AUTHOR_ALIAS,
+ PROP_RECIPIENT,
+ PROP_CONTENTS,
+ PROP_TIME,
+ PROP_FLAGS,
+ PROP_LAST
+};
+
+static GObjectClass *parent_class;
+static GParamSpec *properties[PROP_LAST];
+
+static GHashTable *messages = NULL;
+
+/******************************************************************************
+ * API implementation
+ ******************************************************************************/
+
+PurpleMessage *
+purple_message_new_outgoing(const gchar *who, const gchar *contents,
+ PurpleMessageFlags flags)
+{
+ g_warn_if_fail(!(flags & PURPLE_MESSAGE_RECV));
+ g_warn_if_fail(!(flags & PURPLE_MESSAGE_SYSTEM));
+
+ flags |= PURPLE_MESSAGE_SEND;
+
+ /* who may be NULL for outgoing MUC messages */
+ return g_object_new(PURPLE_TYPE_MESSAGE,
+ "author-alias", _("Me"),
+ "recipient", who,
+ "contents", contents,
+ "time", (guint64)time(NULL),
+ "flags", flags,
+ NULL);
+}
+
+PurpleMessage *
+purple_message_new_incoming(const gchar *who, const gchar *contents,
+ PurpleMessageFlags flags, guint64 timestamp)
+{
+ g_warn_if_fail(!(flags & PURPLE_MESSAGE_SEND));
+ g_warn_if_fail(!(flags & PURPLE_MESSAGE_SYSTEM));
+
+ flags |= PURPLE_MESSAGE_RECV;
+
+ if (timestamp == 0)
+ timestamp = time(NULL);
+
+ return g_object_new(PURPLE_TYPE_MESSAGE,
+ "author", who,
+ "author-alias", who,
+ "contents", contents,
+ "time", timestamp,
+ "flags", flags,
+ NULL);
+}
+
+PurpleMessage *
+purple_message_new_system(const gchar *contents, PurpleMessageFlags flags)
+{
+ g_warn_if_fail(!(flags & PURPLE_MESSAGE_SEND));
+ g_warn_if_fail(!(flags & PURPLE_MESSAGE_RECV));
+
+ flags |= PURPLE_MESSAGE_SYSTEM;
+
+ return g_object_new(PURPLE_TYPE_MESSAGE,
+ "contents", contents,
+ "time", (guint64)time(NULL),
+ "flags", flags,
+ NULL);
+}
+
+guint
+purple_message_get_id(const PurpleMessage *msg)
+{
+ PurpleMessagePrivate *priv = PURPLE_MESSAGE_GET_PRIVATE(msg);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return priv->id;
+}
+
+PurpleMessage *
+purple_message_find_by_id(guint id)
+{
+ g_return_val_if_fail(id > 0, NULL);
+
+ return g_hash_table_lookup(messages, GINT_TO_POINTER(id));
+}
+
+const gchar *
+purple_message_get_author(const PurpleMessage *msg)
+{
+ PurpleMessagePrivate *priv = PURPLE_MESSAGE_GET_PRIVATE(msg);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->author;
+}
+
+const gchar *
+purple_message_get_recipient(const PurpleMessage *msg)
+{
+ PurpleMessagePrivate *priv = PURPLE_MESSAGE_GET_PRIVATE(msg);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->recipient;
+}
+
+void
+purple_message_set_author_alias(PurpleMessage *msg, const gchar *alias)
+{
+ g_object_set(msg, "author-alias", alias, NULL);
+}
+
+const gchar *
+purple_message_get_author_alias(const PurpleMessage *msg)
+{
+ PurpleMessagePrivate *priv = PURPLE_MESSAGE_GET_PRIVATE(msg);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ if (priv->author_alias == NULL)
+ return purple_message_get_author(msg);
+
+ return priv->author_alias;
+}
+
+void
+purple_message_set_contents(PurpleMessage *msg, const gchar *cont)
+{
+ g_object_set(msg, "contents", cont, NULL);
+}
+
+const gchar *
+purple_message_get_contents(const PurpleMessage *msg)
+{
+ PurpleMessagePrivate *priv = PURPLE_MESSAGE_GET_PRIVATE(msg);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->contents;
+}
+
+gboolean
+purple_message_is_empty(const PurpleMessage *msg)
+{
+ const gchar *cont = purple_message_get_contents(msg);
+
+ return (cont == NULL || cont[0] == '\0');
+}
+
+void
+purple_message_set_time(PurpleMessage *msg, guint64 msgtime)
+{
+ g_object_set(msg, "time", msgtime, NULL);
+}
+
+guint64
+purple_message_get_time(const PurpleMessage *msg)
+{
+ PurpleMessagePrivate *priv = PURPLE_MESSAGE_GET_PRIVATE(msg);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return priv->msgtime;
+}
+
+void
+purple_message_set_flags(PurpleMessage *msg, PurpleMessageFlags flags)
+{
+ g_object_set(msg, "flags", flags, NULL);
+}
+
+PurpleMessageFlags
+purple_message_get_flags(const PurpleMessage *msg)
+{
+ PurpleMessagePrivate *priv = PURPLE_MESSAGE_GET_PRIVATE(msg);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return priv->flags;
+}
+
+/******************************************************************************
+ * Object stuff
+ ******************************************************************************/
+
+static void
+purple_message_init(GTypeInstance *instance, gpointer klass)
+{
+ static guint max_id = 0;
+
+ PurpleMessage *msg = PURPLE_MESSAGE(instance);
+ PurpleMessagePrivate *priv = PURPLE_MESSAGE_GET_PRIVATE(msg);
+ PURPLE_DBUS_REGISTER_POINTER(msg, PurpleMessage);
+
+ priv->id = ++max_id;
+ g_hash_table_insert(messages, GINT_TO_POINTER(max_id), msg);
+}
+
+static void
+purple_message_finalize(GObject *obj)
+{
+ PurpleMessage *message = PURPLE_MESSAGE(obj);
+ PurpleMessagePrivate *priv = PURPLE_MESSAGE_GET_PRIVATE(message);
+
+ g_free(priv->author);
+ g_free(priv->author_alias);
+ g_free(priv->recipient);
+ g_free(priv->contents);
+
+ G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+static void
+purple_message_get_property(GObject *object, guint par_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleMessage *message = PURPLE_MESSAGE(object);
+ PurpleMessagePrivate *priv = PURPLE_MESSAGE_GET_PRIVATE(message);
+
+ switch (par_id) {
+ case PROP_ID:
+ g_value_set_uint(value, priv->id);
+ break;
+ case PROP_AUTHOR:
+ g_value_set_string(value, priv->author);
+ break;
+ case PROP_AUTHOR_ALIAS:
+ g_value_set_string(value, priv->author_alias);
+ break;
+ case PROP_RECIPIENT:
+ g_value_set_string(value, priv->recipient);
+ break;
+ case PROP_CONTENTS:
+ g_value_set_string(value, priv->contents);
+ break;
+ case PROP_TIME:
+ g_value_set_uint64(value, priv->msgtime);
+ break;
+ case PROP_FLAGS:
+ g_value_set_flags(value, priv->flags);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, par_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_message_set_property(GObject *object, guint par_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleMessage *message = PURPLE_MESSAGE(object);
+ PurpleMessagePrivate *priv = PURPLE_MESSAGE_GET_PRIVATE(message);
+
+ switch (par_id) {
+ case PROP_AUTHOR:
+ g_free(priv->author);
+ priv->author = g_strdup(g_value_get_string(value));
+ break;
+ case PROP_AUTHOR_ALIAS:
+ g_free(priv->author_alias);
+ priv->author_alias = g_strdup(g_value_get_string(value));
+ break;
+ case PROP_RECIPIENT:
+ g_free(priv->recipient);
+ priv->recipient = g_strdup(g_value_get_string(value));
+ break;
+ case PROP_CONTENTS:
+ g_free(priv->contents);
+ priv->contents = g_strdup(g_value_get_string(value));
+ break;
+ case PROP_TIME:
+ priv->msgtime = g_value_get_uint64(value);
+ break;
+ case PROP_FLAGS:
+ priv->flags = g_value_get_flags(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, par_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_message_class_init(PurpleMessageClass *klass)
+{
+ GObjectClass *gobj_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ g_type_class_add_private(klass, sizeof(PurpleMessagePrivate));
+
+ gobj_class->finalize = purple_message_finalize;
+ gobj_class->get_property = purple_message_get_property;
+ gobj_class->set_property = purple_message_set_property;
+
+ properties[PROP_ID] = g_param_spec_uint("id",
+ "ID", "The session-unique message id",
+ 0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ properties[PROP_AUTHOR] = g_param_spec_string("author",
+ "Author", "The username of the person, who sent the message.",
+ NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ properties[PROP_AUTHOR_ALIAS] = g_param_spec_string("author-alias",
+ "Author's alias", "The alias of the person, who sent the "
+ "message. For outgoing messages, it's your alias.",
+ NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ properties[PROP_RECIPIENT] = g_param_spec_string("recipient",
+ "Recipient", "The username of the recipient.",
+ NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ properties[PROP_CONTENTS] = g_param_spec_string("contents",
+ "Contents", "The message text",
+ NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ properties[PROP_TIME] = g_param_spec_uint64("time",
+ "Time", "Message timestamp",
+ 0, G_MAXUINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ properties[PROP_FLAGS] = g_param_spec_flags("flags",
+ "Flags", "Bitwise set of #PurpleMessageFlags flags",
+ PURPLE_TYPE_MESSAGE_FLAGS, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(gobj_class, PROP_LAST, properties);
+}
+
+GType
+purple_message_get_type(void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY(type == 0)) {
+ static const GTypeInfo info = {
+ .class_size = sizeof(PurpleMessageClass),
+ .class_init = (GClassInitFunc)purple_message_class_init,
+ .instance_size = sizeof(PurpleMessage),
+ .instance_init = purple_message_init,
+ };
+
+ type = g_type_register_static(G_TYPE_OBJECT,
+ "PurpleMessage", &info, 0);
+ }
+
+ return type;
+}
+
+void
+_purple_message_init(void)
+{
+ messages = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ NULL, g_object_unref);
+}
+
+void
+_purple_message_uninit(void)
+{
+ g_hash_table_destroy(messages);
+ messages = NULL;
+}
diff --git a/libpurple/message.h b/libpurple/message.h
new file mode 100644
index 0000000000..375bd3a256
--- /dev/null
+++ b/libpurple/message.h
@@ -0,0 +1,276 @@
+/* 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 _PURPLE_MESSAGE_H_
+#define _PURPLE_MESSAGE_H_
+/**
+ * SECTION:message
+ * @include:message.h
+ * @section_id: libpurple-message
+ * @short_description: serializable messages
+ * @title: Message model
+ *
+ * #PurpleMessage object collects data about a certain (incoming or outgoing) message.
+ * It (TODO: will be) serializable, so it can be stored in log and retrieved
+ * with any metadata.
+ */
+
+#include <glib-object.h>
+
+typedef struct _PurpleMessage PurpleMessage;
+typedef struct _PurpleMessageClass PurpleMessageClass;
+
+#define PURPLE_TYPE_MESSAGE (purple_message_get_type())
+#define PURPLE_MESSAGE(smiley) (G_TYPE_CHECK_INSTANCE_CAST((smiley), PURPLE_TYPE_MESSAGE, PurpleMessage))
+#define PURPLE_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MESSAGE, PurpleMessageClass))
+#define PURPLE_IS_MESSAGE(smiley) (G_TYPE_CHECK_INSTANCE_TYPE((smiley), PURPLE_TYPE_MESSAGE))
+#define PURPLE_IS_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MESSAGE))
+#define PURPLE_MESSAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MESSAGE, PurpleMessageClass))
+
+/**
+ * PurpleMessage:
+ *
+ * A message data container.
+ */
+struct _PurpleMessage
+{
+ /*< private >*/
+ GObject parent;
+};
+
+/**
+ * PurpleMessageClass:
+ *
+ * Base class for #PurpleMessage objects.
+ */
+struct _PurpleMessageClass
+{
+ /*< private >*/
+ GObjectClass parent_class;
+
+ void (*purple_reserved1)(void);
+ void (*purple_reserved2)(void);
+ void (*purple_reserved3)(void);
+ void (*purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+/**
+ * purple_message_get_type:
+ *
+ * Returns: the #GType for a message.
+ */
+GType
+purple_message_get_type(void);
+
+/**
+ * purple_message_new_outgoing:
+ * @who: Message's recipient.
+ * @contents: The contents of a message.
+ * @flags: The message flags.
+ *
+ * Creates new outgoing message (the user is the author).
+ *
+ * You don't need to set the #PURPLE_MESSAGE_SEND flag.
+ *
+ * Returns: the new #PurpleMessage.
+ */
+PurpleMessage *
+purple_message_new_outgoing(const gchar *who, const gchar *contents,
+ PurpleMessageFlags flags);
+
+/**
+ * purple_message_new_incoming:
+ * @who: Message's author.
+ * @contents: The contents of a message.
+ * @flags: The message flags.
+ * @timestamp: The time of transmitting a message. May be %0 for a current time.
+ *
+ * Creates new incoming message (the user is the recipient).
+ *
+ * You don't need to set the #PURPLE_MESSAGE_RECV flag.
+ *
+ * Returns: the new #PurpleMessage.
+ */
+PurpleMessage *
+purple_message_new_incoming(const gchar *who, const gchar *contents,
+ PurpleMessageFlags flags, guint64 timestamp);
+
+/**
+ * purple_message_new_system:
+ * @contents: The contents of a message.
+ * @flags: The message flags.
+ *
+ * Creates new system message.
+ *
+ * You don't need to set the #PURPLE_MESSAGE_SYSTEM flag.
+ *
+ * Returns: the new #PurpleMessage.
+ */
+PurpleMessage *
+purple_message_new_system(const gchar *contents, PurpleMessageFlags flags);
+
+/**
+ * purple_message_get_id:
+ * @msg: The message.
+ *
+ * Returns the unique identifier of the message. These identifiers are not
+ * serialized - it's a per-session id.
+ *
+ * Returns: the global identifier of @msg.
+ */
+guint
+purple_message_get_id(const PurpleMessage *msg);
+
+/**
+ * purple_message_find_by_id:
+ * @id: The message identifier.
+ *
+ * Finds the message with a given @id.
+ *
+ * Returns: the #PurpleMessage, or %NULL if not found.
+ */
+PurpleMessage *
+purple_message_find_by_id(guint id);
+
+/**
+ * purple_message_get_author:
+ * @msg: The message.
+ *
+ * Returns the author of the message - his screen name (not a local alias).
+ *
+ * Returns: the author of @msg.
+ */
+const gchar *
+purple_message_get_author(const PurpleMessage *msg);
+
+/**
+ * purple_message_get_recipient:
+ * @msg: The message.
+ *
+ * Returns the recipient of the message - his screen name (not a local alias).
+ *
+ * Returns: the recipient of @msg.
+ */
+const gchar *
+purple_message_get_recipient(const PurpleMessage *msg);
+
+/**
+ * purple_message_set_author_alias:
+ * @msg: The message.
+ * @alias: The alias.
+ *
+ * Sets the alias of @msg's author. You don't normally need to call this.
+ */
+void
+purple_message_set_author_alias(PurpleMessage *msg, const gchar *alias);
+
+/**
+ * purple_message_get_author_alias:
+ * @msg: The message.
+ *
+ * Returns the alias of @msg author.
+ *
+ * Returns: the @msg author's alias.
+ */
+const gchar *
+purple_message_get_author_alias(const PurpleMessage *msg);
+
+/**
+ * purple_message_set_contents:
+ * @msg: The message.
+ * @cont: The contents.
+ *
+ * Sets the contents of the @msg. It might be HTML.
+ */
+void
+purple_message_set_contents(PurpleMessage *msg, const gchar *cont);
+
+/**
+ * purple_message_get_contents:
+ * @msg: The message.
+ *
+ * Returns the contents of the message.
+ *
+ * Returns: the contents of @msg.
+ */
+const gchar *
+purple_message_get_contents(const PurpleMessage *msg);
+
+/**
+ * purple_message_is_empty:
+ * @msg: The message.
+ *
+ * Checks, if the message's body is empty.
+ *
+ * Returns: %TRUE, if @msg is empty.
+ */
+gboolean
+purple_message_is_empty(const PurpleMessage *msg);
+
+/**
+ * purple_message_set_time:
+ * @msg: The message.
+ * @msgtime: The timestamp of a message.
+ *
+ * Sets the @msg's timestamp. It should be a date of posting, but it can be
+ * a date of receiving (if the former is not available).
+ */
+void
+purple_message_set_time(PurpleMessage *msg, guint64 msgtime);
+
+/**
+ * purple_message_get_time:
+ * @msg: The message.
+ *
+ * Returns a @msg's timestamp.
+ *
+ * Returns: @msg's timestamp.
+ */
+guint64
+purple_message_get_time(const PurpleMessage *msg);
+
+/**
+ * purple_message_set_flags:
+ * @msg: The message.
+ * @flags: The message flags.
+ *
+ * Sets flags for @msg. It shouldn't be in a conflict with a message type,
+ * so use it carefully.
+ */
+void
+purple_message_set_flags(PurpleMessage *msg, PurpleMessageFlags flags);
+
+/**
+ * purple_message_get_flags:
+ * @msg: The message.
+ *
+ * Returns the flags of a @msg.
+ *
+ * Returns: the flags of a @msg.
+ */
+PurpleMessageFlags
+purple_message_get_flags(const PurpleMessage *msg);
+
+G_END_DECLS
+
+#endif /* _PURPLE_MESSAGE_H_ */
diff --git a/libpurple/mime.c b/libpurple/mime.c
index c63542d1ea..9eac7896e7 100644
--- a/libpurple/mime.c
+++ b/libpurple/mime.c
@@ -28,9 +28,7 @@
#include "mime.h"
#include "util.h"
-/**
- * @struct mime_fields
- *
+/*
* Utility structure used in both MIME document and parts, which maps
* field names to their values, and keeps an easily accessible list of
* keys.
diff --git a/libpurple/mime.h b/libpurple/mime.h
index b568a49288..967215eff9 100644
--- a/libpurple/mime.h
+++ b/libpurple/mime.h
@@ -23,143 +23,162 @@
#ifndef _PURPLE_MIME_H
#define _PURPLE_MIME_H
-
-#include <glib.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
- * @file mime.h
- * @ingroup core
+ * SECTION:mime
+ * @section_id: libpurple-mime
+ * @short_description: <filename>mime.h</filename>
+ * @title: Multi-part MIME Message Parsing
*
* Rudimentary parsing of multi-part MIME messages into more
* accessible structures.
*/
+#include <glib.h>
+
/**
+ * PurpleMimeDocument:
+ *
* A MIME document.
*/
typedef struct _PurpleMimeDocument PurpleMimeDocument;
/**
+ * PurpleMimePart:
+ *
* A part of a multipart MIME document.
*/
typedef struct _PurpleMimePart PurpleMimePart;
+G_BEGIN_DECLS
+
/**
+ * purple_mime_document_new:
+ *
* Allocate an empty MIME document.
*/
PurpleMimeDocument *purple_mime_document_new(void);
/**
- * Frees memory used in a MIME document and all of its parts and fields
+ * purple_mime_document_free:
+ * @doc: The MIME document to free.
*
- * @param doc The MIME document to free.
+ * Frees memory used in a MIME document and all of its parts and fields
*/
void purple_mime_document_free(PurpleMimeDocument *doc);
/**
- * Parse a MIME document from a NUL-terminated string.
+ * purple_mime_document_parse:
+ * @buf: The NULL-terminated string containing the MIME-encoded data.
*
- * @param buf The NULL-terminated string containing the MIME-encoded data.
+ * Parse a MIME document from a NUL-terminated string.
*
- * @returns A MIME document.
+ * Returns: A MIME document.
*/
PurpleMimeDocument *purple_mime_document_parse(const char *buf);
/**
- * Parse a MIME document from a string
+ * purple_mime_document_parsen:
+ * @buf: The string containing the MIME-encoded data.
+ * @len: Length of buf.
*
- * @param buf The string containing the MIME-encoded data.
- * @param len Length of buf.
+ * Parse a MIME document from a string
*
- * @returns A MIME document.
+ * Returns: A MIME document.
*/
PurpleMimeDocument *purple_mime_document_parsen(const char *buf, gsize len);
/**
+ * purple_mime_document_write:
+ *
* Write (append) a MIME document onto a GString.
*/
void purple_mime_document_write(PurpleMimeDocument *doc, GString *str);
/**
- * The list of fields in the header of a document
+ * purple_mime_document_get_fields:
+ * @doc: The MIME document.
*
- * @param doc The MIME document.
+ * The list of fields in the header of a document
*
- * @constreturn A list of strings indicating the fields (but not the values
- * of the fields) in the header of doc.
+ * Returns: (transfer none): A list of strings indicating the fields (but not
+ * the values of the fields) in the header of doc.
*/
GList *purple_mime_document_get_fields(PurpleMimeDocument *doc);
/**
- * Get the value of a specific field in the header of a document.
+ * purple_mime_document_get_field:
+ * @doc: The MIME document.
+ * @field: Case-insensitive field name.
*
- * @param doc The MIME document.
- * @param field Case-insensitive field name.
+ * Get the value of a specific field in the header of a document.
*
- * @returns Value associated with the indicated header field, or
+ * Returns: Value associated with the indicated header field, or
* NULL if the field doesn't exist.
*/
const char *purple_mime_document_get_field(PurpleMimeDocument *doc,
const char *field);
/**
+ * purple_mime_document_set_field:
+ * @doc: The MIME document.
+ * @field: Case-insensitive field name.
+ * @value: Value to associate with the indicated header field,
+ * of NULL to remove the field.
+ *
* Set or replace the value of a specific field in the header of a
* document.
- *
- * @param doc The MIME document.
- * @param field Case-insensitive field name.
- * @param value Value to associate with the indicated header field,
- * of NULL to remove the field.
*/
void purple_mime_document_set_field(PurpleMimeDocument *doc,
const char *field,
const char *value);
/**
- * The list of parts in a multipart document.
+ * purple_mime_document_get_parts:
+ * @doc: The MIME document.
*
- * @param doc The MIME document.
+ * The list of parts in a multipart document.
*
- * @constreturn List of PurpleMimePart contained within doc.
+ * Returns: (transfer none): List of PurpleMimePart contained within doc.
*/
GList *purple_mime_document_get_parts(PurpleMimeDocument *doc);
/**
- * Create and insert a new part into a MIME document.
+ * purple_mime_part_new:
+ * @doc: The new part's parent MIME document.
*
- * @param doc The new part's parent MIME document.
+ * Create and insert a new part into a MIME document.
*/
PurpleMimePart *purple_mime_part_new(PurpleMimeDocument *doc);
/**
- * The list of fields in the header of a document part.
+ * purple_mime_part_get_fields:
+ * @part: The MIME document part.
*
- * @param part The MIME document part.
+ * The list of fields in the header of a document part.
*
- * @constreturn List of strings indicating the fields (but not the values
- * of the fields) in the header of part.
+ * Returns: (transfer none): List of strings indicating the fields (but not the
+ * values of the fields) in the header of part.
*/
GList *purple_mime_part_get_fields(PurpleMimePart *part);
/**
- * Get the value of a specific field in the header of a document part.
+ * purple_mime_part_get_field:
+ * @part: The MIME document part.
+ * @field: Case-insensitive name of the header field.
*
- * @param part The MIME document part.
- * @param field Case-insensitive name of the header field.
+ * Get the value of a specific field in the header of a document part.
*
- * @returns Value of the specified header field, or NULL if the
+ * Returns: Value of the specified header field, or NULL if the
* field doesn't exist.
*/
const char *purple_mime_part_get_field(PurpleMimePart *part,
const char *field);
/**
+ * purple_mime_part_get_field_decoded:
+ *
* Get the decoded value of a specific field in the header of a
* document part.
*/
@@ -167,52 +186,55 @@ char *purple_mime_part_get_field_decoded(PurpleMimePart *part,
const char *field);
/**
+ * purple_mime_part_set_field:
+ * @part: The part of the MIME document.
+ * @field: Case-insensitive field name
+ * @value: Value to associate with the indicated header field,
+ * of NULL to remove the field.
+ *
* Set or replace the value of a specific field in the header of a
* document.
- *
- * @param part The part of the MIME document.
- * @param field Case-insensitive field name
- * @param value Value to associate with the indicated header field,
- * of NULL to remove the field.
*/
void purple_mime_part_set_field(PurpleMimePart *part,
const char *field,
const char *value);
/**
- * Get the (possibly encoded) data portion of a MIME document part.
+ * purple_mime_part_get_data:
+ * @part: The MIME document part.
*
- * @param part The MIME document part.
+ * Get the (possibly encoded) data portion of a MIME document part.
*
- * @returns NULL-terminated data found in the document part
+ * Returns: NULL-terminated data found in the document part
*/
const char *purple_mime_part_get_data(PurpleMimePart *part);
/**
+ * purple_mime_part_get_data_decoded:
+ * @part: The MIME documemt part.
+ * @data: Buffer for the data.
+ * @len: The length of the buffer.
+ *
* Get the data portion of a MIME document part, after attempting to
* decode it according to the content-transfer-encoding field. If the
* specified encoding method is not supported, this function will
* return NULL.
- *
- * @param part The MIME documemt part.
- * @param data Buffer for the data.
- * @param len The length of the buffer.
*/
void purple_mime_part_get_data_decoded(PurpleMimePart *part,
guchar **data, gsize *len);
/**
+ * purple_mime_part_get_length:
+ * @part: The MIME document part.
+ *
* Get the length of the data portion of a MIME document part.
*
- * @param part The MIME document part.
- * @returns Length of the data in the document part.
+ * Returns: Length of the data in the document part.
*/
gsize purple_mime_part_get_length(PurpleMimePart *part);
void purple_mime_part_set_data(PurpleMimePart *part, const char *data);
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif
diff --git a/libpurple/nat-pmp.c b/libpurple/nat-pmp.c
index 4bcd569939..780021a768 100644
--- a/libpurple/nat-pmp.c
+++ b/libpurple/nat-pmp.c
@@ -1,8 +1,3 @@
-/**
- * @file nat-pmp.c NAT-PMP Implementation
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
diff --git a/libpurple/nat-pmp.h b/libpurple/nat-pmp.h
index 077c74c30c..5e179c9831 100644
--- a/libpurple/nat-pmp.h
+++ b/libpurple/nat-pmp.h
@@ -1,8 +1,3 @@
-/**
- * @file nat-pmp.h NAT-PMP Implementation
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -31,6 +26,12 @@
#ifndef _PURPLE_NAT_PMP_H
#define _PURPLE_NAT_PMP_H
+/**
+ * SECTION:nat-pmp
+ * @section_id: libpurple-nat-pmp
+ * @short_description: <filename>nat-pmp.h</filename>
+ * @title: NAT-PMP Implementation
+ */
#include <glib.h>
@@ -41,37 +42,44 @@ typedef enum {
PURPLE_PMP_TYPE_TCP
} PurplePmpType;
+G_BEGIN_DECLS
+
/**
+ * purple_pmp_init:
+ *
* Initialize nat-pmp
*/
void purple_pmp_init(void);
-/**
- *
- */
char *purple_pmp_get_public_ip(void);
/**
- * Remove the NAT-PMP mapping for a specified type on a specified port
+ * purple_pmp_create_map:
+ * @type: The PurplePmpType
+ * @privateport: The private port on which we are listening locally
+ * @publicport: The public port on which we are expecting a response
+ * @lifetime: The lifetime of the mapping. It is recommended that this
+ * be PURPLE_PMP_LIFETIME.
*
- * @param type The PurplePmpType
- * @param privateport The private port on which we are listening locally
- * @param publicport The public port on which we are expecting a response
- * @param lifetime The lifetime of the mapping. It is recommended that this be PURPLE_PMP_LIFETIME.
+ * Remove the NAT-PMP mapping for a specified type on a specified port
*
- * @returns TRUE if succesful; FALSE if unsuccessful
+ * Returns: TRUE if successful; FALSE if unsuccessful
*/
-gboolean purple_pmp_create_map(PurplePmpType type, unsigned short privateport, unsigned short publicport, int lifetime);
+gboolean purple_pmp_create_map(PurplePmpType type, unsigned short privateport,
+ unsigned short publicport, int lifetime);
/**
- * Remove the NAT-PMP mapping for a specified type on a specified port
+ * purple_pmp_destroy_map:
+ * @type: The PurplePmpType
+ * @privateport: The private port on which the mapping was previously made
*
- * @param type The PurplePmpType
- * @param privateport The private port on which the mapping was previously made
+ * Remove the NAT-PMP mapping for a specified type on a specified port
*
- * @returns TRUE if succesful; FALSE if unsuccessful
+ * Returns: TRUE if successful; FALSE if unsuccessful
*/
gboolean purple_pmp_destroy_map(PurplePmpType type, unsigned short privateport);
+G_END_DECLS
+
#endif
diff --git a/libpurple/network.c b/libpurple/network.c
index 3b29acf1a6..38e3074861 100644
--- a/libpurple/network.c
+++ b/libpurple/network.c
@@ -1,8 +1,3 @@
-/**
- * @file network.c Network Implementation
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -85,7 +80,12 @@ static gboolean have_nm_state = FALSE;
static int current_network_count;
/* Mutex for the other global vars */
+#if GLIB_CHECK_VERSION(2, 32, 0)
+static GMutex mutex;
+#else
static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
+#endif
+
static gboolean network_initialized = FALSE;
static HANDLE network_change_handle = NULL;
static int (WSAAPI *MyWSANSPIoctl) (
@@ -101,7 +101,7 @@ struct _PurpleNetworkListenData {
gboolean adding;
PurpleNetworkListenCallback cb;
gpointer cb_data;
- UPnPMappingAddRemove *mapping_data;
+ PurpleUPnPMappingAddRemove *mapping_data;
int timer;
};
@@ -121,28 +121,6 @@ static gchar *turn_ip = NULL;
static GHashTable *upnp_port_mappings = NULL;
static GHashTable *nat_pmp_port_mappings = NULL;
-const unsigned char *
-purple_network_ip_atoi(const char *ip)
-{
- static unsigned char ret[4];
- gchar *delimiter = ".";
- gchar **split;
- int i;
-
- g_return_val_if_fail(ip != NULL, NULL);
-
- split = g_strsplit(ip, delimiter, 4);
- for (i = 0; split[i] != NULL; i++)
- ret[i] = atoi(split[i]);
- g_strfreev(split);
-
- /* i should always be 4 */
- if (i != 4)
- return NULL;
-
- return ret;
-}
-
void
purple_network_set_public_ip(const char *ip)
{
@@ -162,9 +140,9 @@ purple_network_get_public_ip(void)
const char *
purple_network_get_local_system_ip(int fd)
{
- char buffer[1024];
+ struct ifreq buffer[100];
+ guchar *it, *it_end;
static char ip[16];
- char *tmp;
struct ifconf ifc;
struct ifreq *ifr;
struct sockaddr_in *sinptr;
@@ -176,21 +154,26 @@ purple_network_get_local_system_ip(int fd)
source = socket(PF_INET,SOCK_STREAM, 0);
ifc.ifc_len = sizeof(buffer);
- ifc.ifc_req = (struct ifreq *)buffer;
+ ifc.ifc_req = buffer;
ioctl(source, SIOCGIFCONF, &ifc);
if (fd < 0 && source >= 0)
close(source);
- tmp = buffer;
- while (tmp < buffer + ifc.ifc_len)
- {
- ifr = (struct ifreq *)tmp;
- tmp += HX_SIZE_OF_IFREQ(*ifr);
+ it = (guchar*)buffer;
+ it_end = it + ifc.ifc_len;
+ while (it < it_end) {
+ /* in this case "it" is:
+ * a) (struct ifreq)-aligned
+ * b) not aligned, because of OS quirks (see
+ * _SIZEOF_ADDR_IFREQ), so the OS should deal with it.
+ */
+ ifr = (struct ifreq *)(gpointer)it;
+ it += HX_SIZE_OF_IFREQ(*ifr);
if (ifr->ifr_addr.sa_family == AF_INET)
{
- sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
+ sinptr = (struct sockaddr_in *)(gpointer)&ifr->ifr_addr;
if (sinptr->sin_addr.s_addr != lhost)
{
add = ntohl(sinptr->sin_addr.s_addr);
@@ -227,21 +210,22 @@ purple_network_get_all_local_system_ips(void)
int family = ifa->ifa_addr ? ifa->ifa_addr->sa_family : AF_UNSPEC;
char host[INET6_ADDRSTRLEN];
const char *tmp = NULL;
+ common_sockaddr_t *addr =
+ (common_sockaddr_t *)(gpointer)ifa->ifa_addr;
if ((family != AF_INET && family != AF_INET6) || ifa->ifa_flags & IFF_LOOPBACK)
continue;
if (family == AF_INET)
- tmp = inet_ntop(family, &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr, host, sizeof(host));
+ tmp = inet_ntop(family, &addr->in.sin_addr, host, sizeof(host));
else {
- struct sockaddr_in6 *sockaddr = (struct sockaddr_in6 *)ifa->ifa_addr;
/* Peer-peer link-local communication is a big TODO. I am not sure
* how communicating link-local addresses is supposed to work, and
* it seems like it would require attempting the cartesian product
* of the local and remote interfaces to see if any match (eww).
*/
- if (!IN6_IS_ADDR_LINKLOCAL(&sockaddr->sin6_addr))
- tmp = inet_ntop(family, &sockaddr->sin6_addr, host, sizeof(host));
+ if (!IN6_IS_ADDR_LINKLOCAL(&addr->in6.sin6_addr))
+ tmp = inet_ntop(family, &addr->in6.sin6_addr, host, sizeof(host));
}
if (tmp != NULL)
result = g_list_prepend(result, g_strdup(tmp));
@@ -253,25 +237,28 @@ purple_network_get_all_local_system_ips(void)
#else /* HAVE_GETIFADDRS && HAVE_INET_NTOP */
GList *result = NULL;
int source = socket(PF_INET,SOCK_STREAM, 0);
- char buffer[1024];
- char *tmp;
+ struct ifreq buffer[100];
+ guchar *it, *it_end;
struct ifconf ifc;
struct ifreq *ifr;
ifc.ifc_len = sizeof(buffer);
- ifc.ifc_req = (struct ifreq *)buffer;
+ ifc.ifc_req = buffer;
ioctl(source, SIOCGIFCONF, &ifc);
close(source);
- tmp = buffer;
- while (tmp < buffer + ifc.ifc_len) {
+ it = (guchar*)buffer;
+ it_end = it + ifc.ifc_len;
+ while (it < it_end) {
char dst[INET_ADDRSTRLEN];
- ifr = (struct ifreq *)tmp;
- tmp += HX_SIZE_OF_IFREQ(*ifr);
+ /* alignment: see purple_network_get_local_system_ip */
+ ifr = (struct ifreq *)(gpointer)it;
+ it += HX_SIZE_OF_IFREQ(*ifr);
if (ifr->ifr_addr.sa_family == AF_INET) {
- struct sockaddr_in *sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
+ struct sockaddr_in *sinptr =
+ (struct sockaddr_in *)(gpointer)&ifr->ifr_addr;
inet_ntop(AF_INET, &sinptr->sin_addr, dst,
sizeof(dst));
@@ -287,6 +274,26 @@ purple_network_get_all_local_system_ips(void)
#endif /* HAVE_GETIFADDRS && HAVE_INET_NTOP */
}
+/*
+ * purple_network_is_ipv4:
+ * @hostname: The hostname to be verified.
+ *
+ * Checks, if specified hostname is valid ipv4 address.
+ *
+ * Returns: TRUE, if the hostname is valid.
+ */
+static gboolean
+purple_network_is_ipv4(const gchar *hostname)
+{
+ g_return_val_if_fail(hostname != NULL, FALSE);
+
+ /* We don't accept ipv6 here. */
+ if (strchr(hostname, ':') != NULL)
+ return FALSE;
+
+ return g_hostname_is_ip_address(hostname);
+}
+
const char *
purple_network_get_my_ip(int fd)
{
@@ -297,7 +304,7 @@ purple_network_get_my_ip(int fd)
if (!purple_prefs_get_bool("/purple/network/auto_ip")) {
ip = purple_network_get_public_ip();
/* Make sure the IP address entered by the user is valid */
- if ((ip != NULL) && (purple_network_ip_atoi(ip) != NULL))
+ if ((ip != NULL) && (purple_network_is_ipv4(ip)))
return ip;
} else {
/* Check if STUN discovery was already done */
@@ -391,14 +398,9 @@ purple_network_finish_pmp_map_cb(gpointer data)
return FALSE;
}
-static gboolean listen_map_external = TRUE;
-void purple_network_listen_map_external(gboolean map_external)
-{
- listen_map_external = map_external;
-}
-
static PurpleNetworkListenData *
-purple_network_do_listen(unsigned short port, int socket_family, int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data)
+purple_network_do_listen(unsigned short port, int socket_family, int socket_type, gboolean map_external,
+ PurpleNetworkListenCallback cb, gpointer cb_data)
{
int listenfd = -1;
const int on = 1;
@@ -498,7 +500,7 @@ purple_network_do_listen(unsigned short port, int socket_family, int socket_type
listen_data->cb_data = cb_data;
listen_data->socket_type = socket_type;
- if (!purple_socket_speaks_ipv4(listenfd) || !listen_map_external ||
+ if (!purple_socket_speaks_ipv4(listenfd) || !map_external ||
!purple_prefs_get_bool("/purple/network/map_ports"))
{
purple_debug_info("network", "Skipping external port mapping.\n");
@@ -526,27 +528,19 @@ purple_network_do_listen(unsigned short port, int socket_family, int socket_type
}
PurpleNetworkListenData *
-purple_network_listen_family(unsigned short port, int socket_family,
- int socket_type, PurpleNetworkListenCallback cb,
+purple_network_listen(unsigned short port, int socket_family, int socket_type,
+ gboolean map_external, PurpleNetworkListenCallback cb,
gpointer cb_data)
{
g_return_val_if_fail(port != 0, NULL);
- return purple_network_do_listen(port, socket_family, socket_type,
+ return purple_network_do_listen(port, socket_family, socket_type, map_external,
cb, cb_data);
}
PurpleNetworkListenData *
-purple_network_listen(unsigned short port, int socket_type,
- PurpleNetworkListenCallback cb, gpointer cb_data)
-{
- return purple_network_listen_family(port, AF_UNSPEC, socket_type,
- cb, cb_data);
-}
-
-PurpleNetworkListenData *
-purple_network_listen_range_family(unsigned short start, unsigned short end,
- int socket_family, int socket_type,
+purple_network_listen_range(unsigned short start, unsigned short end,
+ int socket_family, int socket_type, gboolean map_external,
PurpleNetworkListenCallback cb,
gpointer cb_data)
{
@@ -561,7 +555,7 @@ purple_network_listen_range_family(unsigned short start, unsigned short end,
}
for (; start <= end; start++) {
- ret = purple_network_do_listen(start, AF_UNSPEC, socket_type, cb, cb_data);
+ ret = purple_network_do_listen(start, AF_UNSPEC, socket_type, map_external, cb, cb_data);
if (ret != NULL)
break;
}
@@ -569,15 +563,6 @@ purple_network_listen_range_family(unsigned short start, unsigned short end,
return ret;
}
-PurpleNetworkListenData *
-purple_network_listen_range(unsigned short start, unsigned short end,
- int socket_type, PurpleNetworkListenCallback cb,
- gpointer cb_data)
-{
- return purple_network_listen_range_family(start, end, AF_UNSPEC,
- socket_type, cb, cb_data);
-}
-
void purple_network_listen_cancel(PurpleNetworkListenData *listen_data)
{
if (listen_data->mapping_data != NULL)
@@ -721,11 +706,19 @@ static gpointer wpurple_network_change_thread(gpointer data)
WSACOMPLETION completion;
WSAOVERLAPPED overlapped;
+#if GLIB_CHECK_VERSION(2, 32, 0)
+ g_mutex_lock(&mutex);
+#else
g_static_mutex_lock(&mutex);
+#endif
if (network_initialized == FALSE) {
/* purple_network_uninit has been called */
WSACloseEvent(nla_event);
+#if GLIB_CHECK_VERSION(2, 32, 0)
+ g_mutex_unlock(&mutex);
+#else
g_static_mutex_unlock(&mutex);
+#endif
g_thread_exit(NULL);
return NULL;
}
@@ -743,13 +736,21 @@ static gpointer wpurple_network_change_thread(gpointer data)
msg, errorid));
g_free(msg);
WSACloseEvent(nla_event);
+#if GLIB_CHECK_VERSION(2, 32, 0)
+ g_mutex_unlock(&mutex);
+#else
g_static_mutex_unlock(&mutex);
+#endif
g_thread_exit(NULL);
return NULL;
}
}
+#if GLIB_CHECK_VERSION(2, 32, 0)
+ g_mutex_unlock(&mutex);
+#else
g_static_mutex_unlock(&mutex);
-
+#endif
+
memset(&completion, 0, sizeof(WSACOMPLETION));
completion.Type = NSP_NOTIFY_EVENT;
overlapped.hEvent = nla_event;
@@ -760,10 +761,18 @@ static gpointer wpurple_network_change_thread(gpointer data)
if (errorid == WSA_INVALID_HANDLE) {
purple_timeout_add(0, _print_debug_msg,
g_strdup("Invalid NLA handle; resetting.\n"));
+#if GLIB_CHECK_VERSION(2, 32, 0)
+ g_mutex_lock(&mutex);
+#else
g_static_mutex_lock(&mutex);
+#endif
retval = WSALookupServiceEnd(network_change_handle);
network_change_handle = NULL;
+#if GLIB_CHECK_VERSION(2, 32, 0)
+ g_mutex_unlock(&mutex);
+#else
g_static_mutex_unlock(&mutex);
+#endif
continue;
/* WSA_IO_PENDING indicates successful async notification will happen */
} else if (errorid != WSA_IO_PENDING) {
@@ -785,11 +794,19 @@ static gpointer wpurple_network_change_thread(gpointer data)
last_trigger = time(NULL);
+#if GLIB_CHECK_VERSION(2, 32, 0)
+ g_mutex_lock(&mutex);
+#else
g_static_mutex_lock(&mutex);
+#endif
if (network_initialized == FALSE) {
/* Time to die */
WSACloseEvent(nla_event);
+#if GLIB_CHECK_VERSION(2, 32, 0)
+ g_mutex_unlock(&mutex);
+#else
g_static_mutex_unlock(&mutex);
+#endif
g_thread_exit(NULL);
return NULL;
}
@@ -803,7 +820,11 @@ static gpointer wpurple_network_change_thread(gpointer data)
}
WSAResetEvent(nla_event);
+#if GLIB_CHECK_VERSION(2, 32, 0)
+ g_mutex_unlock(&mutex);
+#else
g_static_mutex_unlock(&mutex);
+#endif
purple_timeout_add(0, wpurple_network_change_thread_cb, NULL);
}
@@ -969,14 +990,14 @@ purple_network_ip_lookup_cb(GSList *hosts, gpointer data,
}
if (hosts && g_slist_next(hosts)) {
- struct sockaddr *addr = g_slist_next(hosts)->data;
+ common_sockaddr_t *addr = g_slist_next(hosts)->data;
char dst[INET6_ADDRSTRLEN];
- if (addr->sa_family == AF_INET6) {
- inet_ntop(addr->sa_family, &((struct sockaddr_in6 *) addr)->sin6_addr,
+ if (addr->sa.sa_family == AF_INET6) {
+ inet_ntop(addr->sa.sa_family, &addr->in6.sin6_addr,
dst, sizeof(dst));
} else {
- inet_ntop(addr->sa_family, &((struct sockaddr_in *) addr)->sin_addr,
+ inet_ntop(addr->sa.sa_family, &addr->in.sin_addr,
dst, sizeof(dst));
}
@@ -998,7 +1019,7 @@ purple_network_set_stun_server(const gchar *stun_server)
if (stun_server && stun_server[0] != '\0') {
if (purple_network_is_available()) {
purple_debug_info("network", "running DNS query for STUN server\n");
- purple_dnsquery_a_account(NULL, stun_server, 3478, purple_network_ip_lookup_cb,
+ purple_dnsquery_a(NULL, stun_server, 3478, purple_network_ip_lookup_cb,
&stun_ip);
} else {
purple_debug_info("network",
@@ -1016,7 +1037,7 @@ purple_network_set_turn_server(const gchar *turn_server)
if (turn_server && turn_server[0] != '\0') {
if (purple_network_is_available()) {
purple_debug_info("network", "running DNS query for TURN server\n");
- purple_dnsquery_a_account(NULL, turn_server,
+ purple_dnsquery_a(NULL, turn_server,
purple_prefs_get_int("/purple/network/turn_port"),
purple_network_ip_lookup_cb, &turn_ip);
} else {
@@ -1173,7 +1194,10 @@ purple_network_init(void)
else {
current_network_count = cnt;
if ((MyWSANSPIoctl = (void*) wpurple_find_and_loadproc("ws2_32.dll", "WSANSPIoctl"))) {
- if (!g_thread_create(wpurple_network_change_thread, NULL, FALSE, &err))
+ GThread *thread = g_thread_try_new("Network Monitor thread", wpurple_network_change_thread, NULL, &err);
+ if (thread)
+ g_thread_unref(thread);
+ else
purple_debug_error("network", "Couldn't create Network Monitor thread: %s\n", err ? err->message : "");
}
}
@@ -1225,7 +1249,7 @@ purple_network_init(void)
#endif
purple_signal_register(purple_network_get_handle(), "network-configuration-changed",
- purple_marshal_VOID, NULL, 0);
+ purple_marshal_VOID, G_TYPE_NONE, 0);
purple_pmp_init();
purple_upnp_init();
@@ -1259,7 +1283,11 @@ purple_network_uninit(void)
#endif
#ifdef _WIN32
+#if GLIB_CHECK_VERSION(2, 32, 0)
+ g_mutex_lock(&mutex);
+#else
g_static_mutex_lock(&mutex);
+#endif
network_initialized = FALSE;
if (network_change_handle != NULL) {
int retval;
@@ -1276,7 +1304,11 @@ purple_network_uninit(void)
network_change_handle = NULL;
}
+#if GLIB_CHECK_VERSION(2, 32, 0)
+ g_mutex_unlock(&mutex);
+#else
g_static_mutex_unlock(&mutex);
+#endif
#endif
purple_signal_unregister(purple_network_get_handle(),
diff --git a/libpurple/network.h b/libpurple/network.h
index 333f8e7f30..78679dd39a 100644
--- a/libpurple/network.h
+++ b/libpurple/network.h
@@ -1,8 +1,3 @@
-/**
- * @file network.h Network API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -23,82 +18,83 @@
* 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_NETWORK_H_
#define _PURPLE_NETWORK_H_
+/**
+ * SECTION:network
+ * @section_id: libpurple-network
+ * @short_description: <filename>network.h</filename>
+ * @title: Network API
+ */
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include <glib.h>
+
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name Network API */
+/* Network API */
/**************************************************************************/
-/*@{*/
typedef struct _PurpleNetworkListenData PurpleNetworkListenData;
typedef void (*PurpleNetworkListenCallback) (int listenfd, gpointer data);
/**
- * Converts a dot-decimal IP address to an array of unsigned
- * chars. For example, converts 192.168.0.1 to a 4 byte
- * array containing 192, 168, 0 and 1.
- *
- * @param ip An IP address in dot-decimal notiation.
- * @return An array of 4 bytes containing an IP addresses
- * equivalent to the given parameter, or NULL if
- * the given IP address is invalid. This value
- * is statically allocated and should not be
- * freed.
- */
-const unsigned char *purple_network_ip_atoi(const char *ip);
-
-/**
+ * purple_network_set_public_ip:
+ * @ip: The local IP address.
+ *
* Sets the IP address of the local system in preferences. This
* is the IP address that should be used for incoming connections
* (file transfer, direct IM, etc.) and should therefore be
* publicly accessible.
- *
- * @param ip The local IP address.
*/
void purple_network_set_public_ip(const char *ip);
/**
+ * purple_network_get_public_ip:
+ *
* Returns the IP address of the local system set in preferences.
*
* This returns the value set via purple_network_set_public_ip().
* You probably want to use purple_network_get_my_ip() instead.
*
- * @return The local IP address set in preferences.
+ * Returns: The local IP address set in preferences.
*/
const char *purple_network_get_public_ip(void);
/**
+ * purple_network_get_local_system_ip:
+ * @fd: The fd to use to help figure out the IP, or else -1.
+ *
* Returns the IP address of the local system.
*
* You probably want to use purple_network_get_my_ip() instead.
*
- * @note The returned string is a pointer to a static buffer. If this
+ * Note: The returned string is a pointer to a static buffer. If this
* function is called twice, it may be important to make a copy
* of the returned string.
*
- * @param fd The fd to use to help figure out the IP, or else -1.
- * @return The local IP address.
+ * Returns: The local IP address.
*/
const char *purple_network_get_local_system_ip(int fd);
/**
+ * purple_network_get_all_local_system_ips:
+ *
* Returns all IP addresses of the local system.
*
- * @note The caller must free this list. If libpurple was built with
+ * Note: The caller must free this list. If libpurple was built with
* support for it, this function also enumerates IPv6 addresses.
- * @since 2.7.0
*
- * @return A list of local IP addresses.
+ * Returns: A list of local IP addresses.
*/
GList *purple_network_get_all_local_system_ips(void);
/**
+ * purple_network_get_my_ip:
+ * @fd: The fd to use to help figure out the IP, or -1.
+ *
* Returns the IP address that should be used anywhere a
* public IP addresses is needed (listening for an incoming
* file transfer, etc).
@@ -108,28 +104,33 @@ GList *purple_network_get_all_local_system_ips(void);
* IP address returned by purple_network_get_local_system_ip()
* is returned.
*
- * @note The returned string is a pointer to a static buffer. If this
+ * Note: The returned string is a pointer to a static buffer. If this
* function is called twice, it may be important to make a copy
* of the returned string.
*
- * @param fd The fd to use to help figure out the IP, or -1.
- * @return The local IP address to be used.
+ * Returns: The local IP address to be used.
*/
const char *purple_network_get_my_ip(int fd);
/**
- * Should calls to purple_network_listen() and purple_network_listen_range()
- * map the port externally using NAT-PMP or UPnP?
- * The default value is TRUE
- *
- * @param map_external Should the open port be mapped externally?
- * @deprecated In 3.0.0 a boolean will be added to the functions mentioned
- * above to perform the same function.
- * @since 2.3.0
- */
-void purple_network_listen_map_external(gboolean map_external);
-
-/**
+ * purple_network_listen:
+ * @port: The port number to bind to. Must be greater than 0.
+ * @socket_family: The protocol family of the socket. This should be
+ * AF_INET for IPv4 or AF_INET6 for IPv6. IPv6 sockets
+ * may or may not be able to accept IPv4 connections
+ * based on the system configuration (use
+ * purple_socket_speaks_ipv4 to check). If an IPv6
+ * socket doesn't accept V4-mapped addresses, you will
+ * need a second listener to support both v4 and v6.
+ * @socket_type: The type of socket to open for listening.
+ * This will be either SOCK_STREAM for TCP or SOCK_DGRAM for UDP.
+ * @map_external: Should the open port be mapped externally using
+ * NAT-PNP or UPnP? (default should be %TRUE)
+ * @cb: (scope call): The callback to be invoked when the port to listen on is
+ * available. The file descriptor of the listening socket will be
+ * specified in this callback, or -1 if no socket could be established.
+ * @cb_data: extra data to be returned when cb is called
+ *
* Attempts to open a listening port ONLY on the specified port number.
* You probably want to use purple_network_listen_range() instead of this.
* This function is useful, for example, if you wanted to write a telnet
@@ -142,43 +143,41 @@ void purple_network_listen_map_external(gboolean map_external);
* close the listening socket, and add a new watcher on the new socket accept
* returned.
*
- * @param port The port number to bind to. Must be greater than 0.
- * @param socket_type The type of socket to open for listening.
- * This will be either SOCK_STREAM for TCP or SOCK_DGRAM for UDP.
- * @param cb The callback to be invoked when the port to listen on is available.
- * The file descriptor of the listening socket will be specified in
- * this callback, or -1 if no socket could be established.
- * @param cb_data extra data to be returned when cb is called
- *
- * @return A pointer to a data structure that can be used to cancel
- * the pending listener, or NULL if unable to obtain a local
- * socket to listen on.
- */
-PurpleNetworkListenData *purple_network_listen(unsigned short port,
- int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data);
-
-/**
- * \copydoc purple_network_listen
- *
* Libpurple does not currently do any port mapping (stateful firewall hole
* poking) for IPv6-only listeners (if an IPv6 socket supports v4-mapped
* addresses, a mapping is done).
*
- * @param socket_family The protocol family of the socket. This should be
- * AF_INET for IPv4 or AF_INET6 for IPv6. IPv6 sockets
- * may or may not be able to accept IPv4 connections
- * based on the system configuration (use
- * purple_socket_speaks_ipv4 to check). If an IPv6
- * socket doesn't accept V4-mapped addresses, you will
- * need a second listener to support both v4 and v6.
- * @since 2.7.0
- * @deprecated This function will be renamed to purple_network_listen in 3.0.0.
+ * Returns: A pointer to a data structure that can be used to cancel
+ * the pending listener, or %NULL if unable to obtain a local
+ * socket to listen on.
*/
-PurpleNetworkListenData *purple_network_listen_family(unsigned short port,
- int socket_family, int socket_type, PurpleNetworkListenCallback cb,
- gpointer cb_data);
+PurpleNetworkListenData *purple_network_listen(unsigned short port,
+ int socket_family, int socket_type, gboolean map_external,
+ PurpleNetworkListenCallback cb, gpointer cb_data);
/**
+ * purple_network_listen_range:
+ * @start: The port number to bind to, or 0 to pick a random port.
+ * Users are allowed to override this arg in prefs.
+ * @end: The highest possible port in the range of ports to listen on,
+ * or 0 to pick a random port. Users are allowed to override this
+ * arg in prefs.
+ * @socket_family: The protocol family of the socket. This should be
+ * AF_INET for IPv4 or AF_INET6 for IPv6. IPv6 sockets
+ * may or may not be able to accept IPv4 connections
+ * based on the system configuration (use
+ * purple_socket_speaks_ipv4 to check). If an IPv6
+ * socket doesn't accept V4-mapped addresses, you will
+ * need a second listener to support both v4 and v6.
+ * @socket_type: The type of socket to open for listening.
+ * This will be either SOCK_STREAM for TCP or SOCK_DGRAM for UDP.
+ * @map_external: Should the open port be mapped externally using
+ * NAT-PNP or UPnP? (default should be %TRUE)
+ * @cb: (scope call): The callback to be invoked when the port to listen on is
+ * available. The file descriptor of the listening socket will be
+ * specified in this callback, or -1 if no socket could be established.
+ * @cb_data: extra data to be returned when cb is called
+ *
* Opens a listening port selected from a range of ports. The range of
* ports used is chosen in the following manner:
* If a range is specified in preferences, these values are used.
@@ -192,136 +191,121 @@ PurpleNetworkListenData *purple_network_listen_family(unsigned short port,
* the listening socket, and add a new watcher on the new socket accept
* returned.
*
- * @param start The port number to bind to, or 0 to pick a random port.
- * Users are allowed to override this arg in prefs.
- * @param end The highest possible port in the range of ports to listen on,
- * or 0 to pick a random port. Users are allowed to override this
- * arg in prefs.
- * @param socket_type The type of socket to open for listening.
- * This will be either SOCK_STREAM for TCP or SOCK_DGRAM for UDP.
- * @param cb The callback to be invoked when the port to listen on is available.
- * The file descriptor of the listening socket will be specified in
- * this callback, or -1 if no socket could be established.
- * @param cb_data extra data to be returned when cb is called
- *
- * @return A pointer to a data structure that can be used to cancel
- * the pending listener, or NULL if unable to obtain a local
- * socket to listen on.
- */
-PurpleNetworkListenData *purple_network_listen_range(unsigned short start,
- unsigned short end, int socket_type,
- PurpleNetworkListenCallback cb, gpointer cb_data);
-
-/**
- * \copydoc purple_network_listen_range
- *
* Libpurple does not currently do any port mapping (stateful firewall hole
* poking) for IPv6-only listeners (if an IPv6 socket supports v4-mapped
* addresses, a mapping is done).
*
- * @param socket_family The protocol family of the socket. This should be
- * AF_INET for IPv4 or AF_INET6 for IPv6. IPv6 sockets
- * may or may not be able to accept IPv4 connections
- * based on the system configuration (use
- * purple_socket_speaks_ipv4 to check). If an IPv6
- * socket doesn't accept V4-mapped addresses, you will
- * need a second listener to support both v4 and v6.
- * @since 2.7.0
- * @deprecated This function will be renamed to purple_network_listen_range
- * in 3.0.0.
+ * Returns: A pointer to a data structure that can be used to cancel
+ * the pending listener, or %NULL if unable to obtain a local
+ * socket to listen on.
*/
-PurpleNetworkListenData *purple_network_listen_range_family(
+PurpleNetworkListenData *purple_network_listen_range(
unsigned short start, unsigned short end, int socket_family,
- int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data);
+ int socket_type, gboolean map_external,
+ PurpleNetworkListenCallback cb, gpointer cb_data);
/**
+ * purple_network_listen_cancel:
+ * @listen_data: This listener attempt will be cancelled and
+ * the struct will be freed.
+ *
* This can be used to cancel any in-progress listener connection
* by passing in the return value from either purple_network_listen()
* or purple_network_listen_range().
- *
- * @param listen_data This listener attempt will be cancelled and
- * the struct will be freed.
*/
void purple_network_listen_cancel(PurpleNetworkListenData *listen_data);
/**
- * Gets a port number from a file descriptor.
- *
- * @param fd The file descriptor. This should be a tcp socket. The current
+ * purple_network_get_port_from_fd:
+ * @fd: The file descriptor. This should be a tcp socket. The current
* implementation probably dies on anything but IPv4. Perhaps this
* possible bug will inspire new and valuable contributors to Purple.
- * @return The port number, in host byte order.
+ *
+ * Gets a port number from a file descriptor.
+ *
+ * Returns: The port number, in host byte order.
*/
unsigned short purple_network_get_port_from_fd(int fd);
/**
+ * purple_network_is_available:
+ *
* Detects if there is an available network connection.
*
- * @return TRUE if the network is available
+ * Returns: TRUE if the network is available
*/
gboolean purple_network_is_available(void);
/**
- * Makes purple_network_is_available() always return @c TRUE.
+ * purple_network_force_online:
+ *
+ * Makes purple_network_is_available() always return %TRUE.
*
* This is what backs the --force-online command line argument in Pidgin,
* for example. This is useful for offline testing, especially when
* combined with nullprpl.
- *
- * @since 2.6.0
*/
void purple_network_force_online(void);
/**
+ * purple_network_get_handle:
+ *
* Get the handle for the network system
*
- * @return the handle to the network system
+ * Returns: the handle to the network system
*/
void *purple_network_get_handle(void);
/**
+ * purple_network_set_stun_server:
+ * @stun_server: The host name of the STUN server to set
+ *
* Update the STUN server IP given the host name
* Will result in a DNS query being executed asynchronous
- *
- * @param stun_server The host name of the STUN server to set
- * @since 2.6.0
*/
void purple_network_set_stun_server(const gchar *stun_server);
/**
+ * purple_network_get_stun_ip:
+ *
* Get the IP address of the STUN server as a string representation
*
- * @return the IP address
- * @since 2.6.0
+ * Returns: the IP address
*/
const gchar *purple_network_get_stun_ip(void);
/**
+ * purple_network_set_turn_server:
+ * @turn_server: The host name of the TURN server to set
+ *
* Update the TURN server IP given the host name
* Will result in a DNS query being executed asynchronous
- *
- * @param turn_server The host name of the TURN server to set
- * @since 2.6.0
*/
void purple_network_set_turn_server(const gchar *turn_server);
/**
+ * purple_network_get_turn_ip:
+ *
* Get the IP address of the TURN server as a string representation
*
- * @return the IP address
- * @since 2.6.0
+ * Returns: the IP address
*/
const gchar *purple_network_get_turn_ip(void);
/**
- * Remove a port mapping (UPnP or NAT-PMP) associated with listening socket
+ * purple_network_remove_port_mapping:
+ * @fd: Socket to remove the port mapping for
*
- * @param fd Socket to remove the port mapping for
- * @since 2.6.0
+ * Remove a port mapping (UPnP or NAT-PMP) associated with listening socket
*/
void purple_network_remove_port_mapping(gint fd);
/**
+ * purple_network_convert_idn_to_ascii:
+ * @in: The hostname to be converted.
+ * @out: The output buffer where an allocated string will be returned.
+ * The caller is responsible for freeing this.
+ *
* Convert a UTF-8 domain name to ASCII in accordance with the IDNA
* specification. If libpurple is compiled without IDN support, this function
* copies the input into the output buffer.
@@ -331,29 +315,25 @@ void purple_network_remove_port_mapping(gint fd);
*
* In general, a buffer of about 512 bytes is the appropriate size to use.
*
- * @param in The hostname to be converted.
- * @param out The output buffer where an allocated string will be returned.
- * The caller is responsible for freeing this.
- * @returns 0 on success, -1 if the out is NULL, or an error code
+ * Returns: 0 on success, -1 if the out is NULL, or an error code
* that currently corresponds to the Idna_rc enum in libidn.
- * @since 2.6.0
*/
int purple_network_convert_idn_to_ascii(const gchar *in, gchar **out);
/**
+ * purple_network_init:
+ *
* Initializes the network subsystem.
*/
void purple_network_init(void);
/**
+ * purple_network_uninit:
+ *
* Shuts down the network subsystem.
*/
void purple_network_uninit(void);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_NETWORK_H_ */
diff --git a/libpurple/notify.c b/libpurple/notify.c
index 45514bd95d..02e9d9ab6b 100644
--- a/libpurple/notify.c
+++ b/libpurple/notify.c
@@ -1,8 +1,3 @@
-/**
- * @file notify.c Notification API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -41,7 +36,7 @@ typedef struct
gpointer cb_user_data;
} PurpleNotifyInfo;
-/**
+/*
* Definition of a user info entry
*/
struct _PurpleNotifyUserInfoEntry
@@ -53,13 +48,24 @@ struct _PurpleNotifyUserInfoEntry
struct _PurpleNotifyUserInfo
{
- GList *user_info_entries;
+ GQueue entries;
+};
+
+/*
+ * Single column of a search result.
+ */
+struct _PurpleNotifySearchColumn
+{
+ char *title; /* Title of the column. */
+ gboolean visible; /* Should the column be visible to the user. Defaults to TRUE. */
+
};
void *
-purple_notify_message(void *handle, PurpleNotifyMsgType type,
- const char *title, const char *primary,
- const char *secondary, PurpleNotifyCloseCallback cb, gpointer user_data)
+purple_notify_message(void *handle, PurpleNotifyMsgType type, const char *title,
+ const char *primary, const char *secondary,
+ PurpleRequestCommonParameters *cpar, PurpleNotifyCloseCallback cb,
+ gpointer user_data)
{
PurpleNotifyUiOps *ops;
@@ -69,7 +75,7 @@ purple_notify_message(void *handle, PurpleNotifyMsgType type,
if (ops != NULL && ops->notify_message != NULL) {
void *ui_handle = ops->notify_message(type, title, primary,
- secondary);
+ secondary, cpar);
if (ui_handle != NULL) {
PurpleNotifyInfo *info = g_new0(PurpleNotifyInfo, 1);
@@ -363,42 +369,31 @@ purple_notify_searchresults_column_new(const char *title)
sc = g_new0(PurpleNotifySearchColumn, 1);
sc->title = g_strdup(title);
+ sc->visible = TRUE;
return sc;
}
-guint
-purple_notify_searchresults_get_columns_count(PurpleNotifySearchResults *results)
-{
- g_return_val_if_fail(results != NULL, 0);
-
- return g_list_length(results->columns);
-}
-
-guint
-purple_notify_searchresults_get_rows_count(PurpleNotifySearchResults *results)
+const char *purple_notify_searchresult_column_get_title(const PurpleNotifySearchColumn *column)
{
- g_return_val_if_fail(results != NULL, 0);
-
- return g_list_length(results->rows);
+ g_return_val_if_fail(column != NULL, NULL);
+
+ return column->title;
}
-char *
-purple_notify_searchresults_column_get_title(PurpleNotifySearchResults *results,
- unsigned int column_id)
+void purple_notify_searchresult_column_set_visible(PurpleNotifySearchColumn *column, gboolean visible)
{
- g_return_val_if_fail(results != NULL, NULL);
+ g_return_if_fail(column != NULL);
- return ((PurpleNotifySearchColumn *)g_list_nth_data(results->columns, column_id))->title;
+ column->visible = visible;
}
-GList *
-purple_notify_searchresults_row_get(PurpleNotifySearchResults *results,
- unsigned int row_id)
+gboolean
+purple_notify_searchresult_column_is_visible(const PurpleNotifySearchColumn *column)
{
- g_return_val_if_fail(results != NULL, NULL);
+ g_return_val_if_fail(column != NULL, FALSE);
- return g_list_nth_data(results->rows, row_id);
+ return column->visible;
}
void *
@@ -454,7 +449,7 @@ purple_notify_user_info_entry_new(const char *label, const char *value)
return user_info_entry;
}
-static void
+void
purple_notify_user_info_entry_destroy(PurpleNotifyUserInfoEntry *user_info_entry)
{
g_return_if_fail(user_info_entry != NULL);
@@ -472,7 +467,7 @@ purple_notify_user_info_new()
user_info = g_new0(PurpleNotifyUserInfo, 1);
PURPLE_DBUS_REGISTER_POINTER(user_info, PurpleNotifyUserInfo);
- user_info->user_info_entries = NULL;
+ g_queue_init(&user_info->entries);
return user_info;
}
@@ -482,23 +477,23 @@ purple_notify_user_info_destroy(PurpleNotifyUserInfo *user_info)
{
GList *l;
- for (l = user_info->user_info_entries; l != NULL; l = l->next) {
+ for (l = user_info->entries.head; l != NULL; l = l->next) {
PurpleNotifyUserInfoEntry *user_info_entry = l->data;
purple_notify_user_info_entry_destroy(user_info_entry);
}
- g_list_free(user_info->user_info_entries);
+ g_queue_clear(&user_info->entries);
PURPLE_DBUS_UNREGISTER_POINTER(user_info);
g_free(user_info);
}
-GList *
+GQueue *
purple_notify_user_info_get_entries(PurpleNotifyUserInfo *user_info)
{
g_return_val_if_fail(user_info != NULL, NULL);
- return user_info->user_info_entries;
+ return &user_info->entries;
}
char *
@@ -509,7 +504,7 @@ purple_notify_user_info_get_text_with_newline(PurpleNotifyUserInfo *user_info, c
text = g_string_new("");
- for (l = user_info->user_info_entries; l != NULL; l = l->next) {
+ for (l = user_info->entries.head; l != NULL; l = l->next) {
PurpleNotifyUserInfoEntry *user_info_entry = l->data;
/* Add a newline before a section header */
if (user_info_entry->type == PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER)
@@ -577,7 +572,7 @@ purple_notify_user_info_entry_set_value(PurpleNotifyUserInfoEntry *user_info_ent
}
PurpleNotifyUserInfoEntryType
-purple_notify_user_info_entry_get_type(PurpleNotifyUserInfoEntry *user_info_entry)
+purple_notify_user_info_entry_get_entry_type(PurpleNotifyUserInfoEntry *user_info_entry)
{
g_return_val_if_fail(user_info_entry != NULL, PURPLE_NOTIFY_USER_INFO_ENTRY_PAIR);
@@ -585,7 +580,8 @@ purple_notify_user_info_entry_get_type(PurpleNotifyUserInfoEntry *user_info_entr
}
void
-purple_notify_user_info_entry_set_type(PurpleNotifyUserInfoEntry *user_info_entry, PurpleNotifyUserInfoEntryType type)
+purple_notify_user_info_entry_set_entry_type(PurpleNotifyUserInfoEntry *user_info_entry,
+ PurpleNotifyUserInfoEntryType type)
{
g_return_if_fail(user_info_entry != NULL);
@@ -593,33 +589,41 @@ purple_notify_user_info_entry_set_type(PurpleNotifyUserInfoEntry *user_info_entr
}
void
-purple_notify_user_info_add_pair(PurpleNotifyUserInfo *user_info, const char *label, const char *value)
+purple_notify_user_info_add_pair_html(PurpleNotifyUserInfo *user_info, const char *label, const char *value)
{
PurpleNotifyUserInfoEntry *entry;
entry = purple_notify_user_info_entry_new(label, value);
- user_info->user_info_entries = g_list_append(user_info->user_info_entries, entry);
+ g_queue_push_tail(&user_info->entries, entry);
}
void
purple_notify_user_info_add_pair_plaintext(PurpleNotifyUserInfo *user_info, const char *label, const char *value)
{
gchar *escaped;
- PurpleNotifyUserInfoEntry *entry;
escaped = g_markup_escape_text(value, -1);
- entry = purple_notify_user_info_entry_new(label, escaped);
+ purple_notify_user_info_add_pair_html(user_info, label, escaped);
g_free(escaped);
- user_info->user_info_entries = g_list_append(user_info->user_info_entries, entry);
}
void
-purple_notify_user_info_prepend_pair(PurpleNotifyUserInfo *user_info, const char *label, const char *value)
+purple_notify_user_info_prepend_pair_html(PurpleNotifyUserInfo *user_info, const char *label, const char *value)
{
PurpleNotifyUserInfoEntry *entry;
entry = purple_notify_user_info_entry_new(label, value);
- user_info->user_info_entries = g_list_prepend(user_info->user_info_entries, entry);
+ g_queue_push_head(&user_info->entries, entry);
+}
+
+void
+purple_notify_user_info_prepend_pair_plaintext(PurpleNotifyUserInfo *user_info, const char *label, const char *value)
+{
+ gchar *escaped;
+
+ escaped = g_markup_escape_text(value, -1);
+ purple_notify_user_info_prepend_pair_html(user_info, label, escaped);
+ g_free(escaped);
}
void
@@ -628,7 +632,7 @@ purple_notify_user_info_remove_entry(PurpleNotifyUserInfo *user_info, PurpleNoti
g_return_if_fail(user_info != NULL);
g_return_if_fail(entry != NULL);
- user_info->user_info_entries = g_list_remove(user_info->user_info_entries, entry);
+ g_queue_remove(&user_info->entries, entry);
}
void
@@ -639,7 +643,7 @@ purple_notify_user_info_add_section_header(PurpleNotifyUserInfo *user_info, cons
entry = purple_notify_user_info_entry_new(label, NULL);
entry->type = PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER;
- user_info->user_info_entries = g_list_append(user_info->user_info_entries, entry);
+ g_queue_push_tail(&user_info->entries, entry);
}
void
@@ -650,7 +654,7 @@ purple_notify_user_info_prepend_section_header(PurpleNotifyUserInfo *user_info,
entry = purple_notify_user_info_entry_new(label, NULL);
entry->type = PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER;
- user_info->user_info_entries = g_list_prepend(user_info->user_info_entries, entry);
+ g_queue_push_head(&user_info->entries, entry);
}
void
@@ -661,7 +665,7 @@ purple_notify_user_info_add_section_break(PurpleNotifyUserInfo *user_info)
entry = purple_notify_user_info_entry_new(NULL, NULL);
entry->type = PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK;
- user_info->user_info_entries = g_list_append(user_info->user_info_entries, entry);
+ g_queue_push_tail(&user_info->entries, entry);
}
void
@@ -672,17 +676,53 @@ purple_notify_user_info_prepend_section_break(PurpleNotifyUserInfo *user_info)
entry = purple_notify_user_info_entry_new(NULL, NULL);
entry->type = PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK;
- user_info->user_info_entries = g_list_prepend(user_info->user_info_entries, entry);
+ g_queue_push_head(&user_info->entries, entry);
}
void
purple_notify_user_info_remove_last_item(PurpleNotifyUserInfo *user_info)
{
- GList *last = g_list_last(user_info->user_info_entries);
- if (last) {
- purple_notify_user_info_entry_destroy(last->data);
- user_info->user_info_entries = g_list_delete_link(user_info->user_info_entries, last);
+ PurpleNotifyUserInfoEntry *entry;
+
+ entry = g_queue_pop_tail(&user_info->entries);
+ if (entry)
+ purple_notify_user_info_entry_destroy(entry);
+}
+
+static PurpleNotifyUserInfo *
+purple_notify_user_info_copy(PurpleNotifyUserInfo *user_info)
+{
+ PurpleNotifyUserInfo *user_info_copy;
+ GList *l;
+
+ g_return_val_if_fail(user_info != NULL, NULL);
+
+ user_info_copy = purple_notify_user_info_new();
+
+ for (l = user_info->entries.head; l != NULL; l = l->next) {
+ PurpleNotifyUserInfoEntry *new_entry, *user_info_entry = l->data;
+
+ new_entry = purple_notify_user_info_entry_new(user_info_entry->label,
+ user_info_entry->value);
+ new_entry->type = user_info_entry->type;
+ g_queue_push_tail(&user_info_copy->entries, new_entry);
+ }
+
+ return user_info_copy;
+}
+
+GType
+purple_notify_user_info_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleNotifyUserInfo",
+ (GBoxedCopyFunc)purple_notify_user_info_copy,
+ (GBoxedFreeFunc)purple_notify_user_info_destroy);
}
+
+ return type;
}
void *
@@ -714,6 +754,28 @@ purple_notify_uri(void *handle, const char *uri)
return NULL;
}
+gboolean
+purple_notify_is_valid_ui_handle(void *ui_handle, PurpleNotifyType *type)
+{
+ GList *it;
+
+ if (ui_handle == NULL)
+ return FALSE;
+
+ for (it = handles; it != NULL; it = g_list_next(it)) {
+ PurpleNotifyInfo *info = it->data;
+
+ if (info->ui_handle != ui_handle)
+ continue;
+
+ if (type != NULL)
+ *type = info->type;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
void
purple_notify_close(PurpleNotifyType type, void *ui_handle)
{
@@ -771,6 +833,33 @@ purple_notify_close_with_handle(void *handle)
}
}
+static PurpleNotifyUiOps *
+purple_notify_ui_ops_copy(PurpleNotifyUiOps *ops)
+{
+ PurpleNotifyUiOps *ops_new;
+
+ g_return_val_if_fail(ops != NULL, NULL);
+
+ ops_new = g_new(PurpleNotifyUiOps, 1);
+ *ops_new = *ops;
+
+ return ops_new;
+}
+
+GType
+purple_notify_ui_ops_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleNotifyUiOps",
+ (GBoxedCopyFunc)purple_notify_ui_ops_copy,
+ (GBoxedFreeFunc)g_free);
+ }
+
+ return type;
+}
+
void
purple_notify_set_ui_ops(PurpleNotifyUiOps *ops)
{
@@ -797,27 +886,22 @@ purple_notify_init(void)
gpointer handle = purple_notify_get_handle();
purple_signal_register(handle, "displaying-email-notification",
- purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER, NULL, 4,
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_STRING));
+ purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER,
+ G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING);
purple_signal_register(handle, "displaying-emails-notification",
- purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT, NULL, 5,
- purple_value_new(PURPLE_TYPE_POINTER),
- purple_value_new(PURPLE_TYPE_POINTER),
- purple_value_new(PURPLE_TYPE_POINTER),
- purple_value_new(PURPLE_TYPE_POINTER),
- purple_value_new(PURPLE_TYPE_UINT));
+ purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT,
+ G_TYPE_NONE, 5, G_TYPE_POINTER, G_TYPE_POINTER,
+ G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_UINT);
+
+ purple_signal_register(handle, "displaying-emails-clear",
+ purple_marshal_VOID, G_TYPE_NONE, 0);
purple_signal_register(handle, "displaying-userinfo",
- purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_USERINFO));
+ purple_marshal_VOID__POINTER_POINTER_POINTER,
+ G_TYPE_NONE, 3, PURPLE_TYPE_ACCOUNT, G_TYPE_STRING,
+ PURPLE_TYPE_NOTIFY_USER_INFO);
}
void
diff --git a/libpurple/notify.h b/libpurple/notify.h
index dc410b600c..7d9fe2bfef 100644
--- a/libpurple/notify.h
+++ b/libpurple/notify.h
@@ -1,9 +1,3 @@
-/**
- * @file notify.h Notification API
- * @ingroup core
- * @see @ref notify-signals
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -24,58 +18,101 @@
* 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_NOTIFY_H_
#define _PURPLE_NOTIFY_H_
+/**
+ * SECTION:notify
+ * @section_id: libpurple-notify
+ * @short_description: <filename>notify.h</filename>
+ * @title: Notification API
+ * @see_also: <link linkend="chapter-signals-notify">Notify signals</link>
+ */
#include <stdlib.h>
#include <glib-object.h>
#include <glib.h>
typedef struct _PurpleNotifyUserInfoEntry PurpleNotifyUserInfoEntry;
-typedef struct _PurpleNotifyUserInfo PurpleNotifyUserInfo;
+
+#define PURPLE_TYPE_NOTIFY_USER_INFO (purple_notify_user_info_get_type())
+typedef struct _PurpleNotifyUserInfo PurpleNotifyUserInfo;
+
+/**
+ * PurpleNotifySearchColumn:
+ *
+ * Single column of a search result.
+ */
+typedef struct _PurpleNotifySearchColumn PurpleNotifySearchColumn;
#include "connection.h"
+#include "request.h"
+
+typedef struct _PurpleNotifySearchResults PurpleNotifySearchResults;
+
+#define PURPLE_TYPE_NOTIFY_UI_OPS (purple_notify_ui_ops_get_type())
+typedef struct _PurpleNotifyUiOps PurpleNotifyUiOps;
/**
+ * PurpleNotifyCloseCallback:
+ *
* Notification close callbacks.
*/
typedef void (*PurpleNotifyCloseCallback) (gpointer user_data);
/**
+ * PurpleNotifyType:
+ * @PURPLE_NOTIFY_MESSAGE: Message notification.
+ * @PURPLE_NOTIFY_EMAIL: Single email notification.
+ * @PURPLE_NOTIFY_EMAILS: Multiple email notification.
+ * @PURPLE_NOTIFY_FORMATTED: Formatted text.
+ * @PURPLE_NOTIFY_SEARCHRESULTS: Buddy search results.
+ * @PURPLE_NOTIFY_USERINFO: Formatted userinfo text.
+ * @PURPLE_NOTIFY_URI: URI notification or display.
+ *
* Notification types.
*/
typedef enum
{
- PURPLE_NOTIFY_MESSAGE = 0, /**< Message notification. */
- PURPLE_NOTIFY_EMAIL, /**< Single email notification. */
- PURPLE_NOTIFY_EMAILS, /**< Multiple email notification. */
- PURPLE_NOTIFY_FORMATTED, /**< Formatted text. */
- PURPLE_NOTIFY_SEARCHRESULTS, /**< Buddy search results. */
- PURPLE_NOTIFY_USERINFO, /**< Formatted userinfo text. */
- PURPLE_NOTIFY_URI /**< URI notification or display. */
+ PURPLE_NOTIFY_MESSAGE = 0,
+ PURPLE_NOTIFY_EMAIL,
+ PURPLE_NOTIFY_EMAILS,
+ PURPLE_NOTIFY_FORMATTED,
+ PURPLE_NOTIFY_SEARCHRESULTS,
+ PURPLE_NOTIFY_USERINFO,
+ PURPLE_NOTIFY_URI
} PurpleNotifyType;
/**
+ * PurpleNotifyMsgType:
+ * @PURPLE_NOTIFY_MSG_ERROR: Error notification.
+ * @PURPLE_NOTIFY_MSG_WARNING: Warning notification.
+ * @PURPLE_NOTIFY_MSG_INFO: Information notification.
+ *
* Notification message types.
*/
typedef enum
{
- PURPLE_NOTIFY_MSG_ERROR = 0, /**< Error notification. */
- PURPLE_NOTIFY_MSG_WARNING, /**< Warning notification. */
- PURPLE_NOTIFY_MSG_INFO /**< Information notification. */
+ PURPLE_NOTIFY_MSG_ERROR = 0,
+ PURPLE_NOTIFY_MSG_WARNING,
+ PURPLE_NOTIFY_MSG_INFO
} PurpleNotifyMsgType;
/**
+ * PurpleNotifySearchButtonType:
+ * @PURPLE_NOTIFY_BUTTON_LABELED: special use, see
+ * purple_notify_searchresults_button_add_labeled()
+ *
* The types of buttons
*/
typedef enum
{
- PURPLE_NOTIFY_BUTTON_LABELED = 0, /**< special use, see _button_add_labeled */
+ PURPLE_NOTIFY_BUTTON_LABELED = 0,
PURPLE_NOTIFY_BUTTON_CONTINUE = 1,
PURPLE_NOTIFY_BUTTON_ADD,
PURPLE_NOTIFY_BUTTON_INFO,
@@ -86,17 +123,24 @@ typedef enum
/**
+ * PurpleNotifySearchResults:
+ * @columns: List of the search column objects.
+ * @rows: List of rows in the result.
+ * @buttons: List of buttons to display.
+ *
* Search results object.
*/
-typedef struct
+struct _PurpleNotifySearchResults
{
- GList *columns; /**< List of the search column objects. */
- GList *rows; /**< List of rows in the result. */
- GList *buttons; /**< List of buttons to display. */
+ GList *columns;
+ GList *rows;
+ GList *buttons;
-} PurpleNotifySearchResults;
+};
/**
+ * PurpleNotifyUserInfoEntryType:
+ *
* Types of PurpleNotifyUserInfoEntry objects
*/
typedef enum
@@ -106,45 +150,45 @@ typedef enum
PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER
} PurpleNotifyUserInfoEntryType;
-/**
- * Single column of a search result.
- */
-typedef struct
-{
- char *title; /**< Title of the column. */
-
-} PurpleNotifySearchColumn;
/**
- * Callback for a button in a search result.
+ * PurpleNotifySearchResultsCallback:
+ * @c: the PurpleConnection passed to purple_notify_searchresults
+ * @row: the contents of the selected row
+ * @user_data: User defined data.
*
- * @param c the PurpleConnection passed to purple_notify_searchresults
- * @param row the contents of the selected row
- * @param user_data User defined data.
+ * Callback for a button in a search result.
*/
typedef void (*PurpleNotifySearchResultsCallback)(PurpleConnection *c, GList *row,
gpointer user_data);
/**
+ * PurpleNotifySearchButton:
+ * @callback: Function to be called when clicked.
+ * @label: only for PURPLE_NOTIFY_BUTTON_LABELED
+ *
* Definition of a button.
*/
typedef struct
{
PurpleNotifySearchButtonType type;
- PurpleNotifySearchResultsCallback callback; /**< Function to be called when clicked. */
- char *label; /**< only for PURPLE_NOTIFY_BUTTON_LABELED */
+ PurpleNotifySearchResultsCallback callback;
+ char *label;
} PurpleNotifySearchButton;
/**
+ * PurpleNotifyUiOps:
+ *
* Notification UI operations.
*/
-typedef struct
+struct _PurpleNotifyUiOps
{
void *(*notify_message)(PurpleNotifyMsgType type, const char *title,
- const char *primary, const char *secondary);
+ const char *primary, const char *secondary,
+ PurpleRequestCommonParameters *cpar);
void *(*notify_email)(PurpleConnection *gc,
const char *subject, const char *from,
@@ -173,40 +217,39 @@ typedef struct
void (*close_notify)(PurpleNotifyType type, void *ui_handle);
+ /*< private >*/
void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
void (*_purple_reserved4)(void);
-} PurpleNotifyUiOps;
+};
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**************************************************************************/
/** Search results notification API */
/**************************************************************************/
-/*@{*/
/**
+ * purple_notify_searchresults:
+ * @gc: The PurpleConnection handle associated with the information.
+ * @title: The title of the message. If this is NULL, the title
+ * will be "Search Results."
+ * @primary: The main point of the message.
+ * @secondary: The secondary information.
+ * @results: The PurpleNotifySearchResults instance.
+ * @cb: (scope call): The callback to call when the user closes
+ * the notification.
+ * @user_data: The data to pass to the close callback and any other
+ * callback associated with a button.
+ *
* Displays results from a buddy search. This can be, for example,
* a window with a list of all found buddies, where you are given the
* option of adding buddies to your buddy list.
*
- * @param gc The PurpleConnection handle associated with the information.
- * @param title The title of the message. If this is NULL, the title
- * will be "Search Results."
- * @param primary The main point of the message.
- * @param secondary The secondary information.
- * @param results The PurpleNotifySearchResults instance.
- * @param cb The callback to call when the user closes
- * the notification.
- * @param user_data The data to pass to the close callback and any other
- * callback associated with a button.
- *
- * @return A UI-specific handle.
+ * Returns: A UI-specific handle.
*/
void *purple_notify_searchresults(PurpleConnection *gc, const char *title,
const char *primary, const char *secondary,
@@ -214,18 +257,20 @@ void *purple_notify_searchresults(PurpleConnection *gc, const char *title,
gpointer user_data);
/**
- * Frees a PurpleNotifySearchResults object.
+ * purple_notify_searchresults_free:
+ * @results: The PurpleNotifySearchResults to free.
*
- * @param results The PurpleNotifySearchResults to free.
+ * Frees a PurpleNotifySearchResults object.
*/
void purple_notify_searchresults_free(PurpleNotifySearchResults *results);
/**
- * Replace old rows with the new. Reuse an existing window.
+ * purple_notify_searchresults_new_rows:
+ * @gc: The PurpleConnection structure.
+ * @results: The PurpleNotifySearchResults structure.
+ * @data: Data returned by the purple_notify_searchresults().
*
- * @param gc The PurpleConnection structure.
- * @param results The PurpleNotifySearchResults structure.
- * @param data Data returned by the purple_notify_searchresults().
+ * Replace old rows with the new. Reuse an existing window.
*/
void purple_notify_searchresults_new_rows(PurpleConnection *gc,
PurpleNotifySearchResults *results,
@@ -233,12 +278,13 @@ void purple_notify_searchresults_new_rows(PurpleConnection *gc,
/**
- * Adds a stock button that will be displayed in the search results dialog.
+ * purple_notify_searchresults_button_add:
+ * @results: The search results object.
+ * @type: Type of the button. (TODO: Only one button of a given type
+ * can be displayed.)
+ * @cb: (scope call): Function that will be called on the click event.
*
- * @param results The search results object.
- * @param type Type of the button. (TODO: Only one button of a given type
- * can be displayed.)
- * @param cb Function that will be called on the click event.
+ * Adds a stock button that will be displayed in the search results dialog.
*/
void purple_notify_searchresults_button_add(PurpleNotifySearchResults *results,
PurpleNotifySearchButtonType type,
@@ -246,12 +292,13 @@ void purple_notify_searchresults_button_add(PurpleNotifySearchResults *results,
/**
+ * purple_notify_searchresults_button_add_labeled:
+ * @results: The search results object
+ * @label: The label to display
+ * @cb: (scope call): Function that will be called on the click event
+ *
* Adds a plain labelled button that will be displayed in the search results
* dialog.
- *
- * @param results The search results object
- * @param label The label to display
- * @param cb Function that will be called on the click event
*/
void purple_notify_searchresults_button_add_labeled(PurpleNotifySearchResults *results,
const char *label,
@@ -259,164 +306,114 @@ void purple_notify_searchresults_button_add_labeled(PurpleNotifySearchResults *r
/**
+ * purple_notify_searchresults_new:
+ *
* Returns a newly created search results object.
*
- * @return The new search results object.
+ * Returns: The new search results object.
*/
PurpleNotifySearchResults *purple_notify_searchresults_new(void);
/**
- * Returns a newly created search result column object.
+ * purple_notify_searchresults_column_new:
+ * @title: Title of the column. NOTE: Title will get g_strdup()ed.
*
- * @param title Title of the column. NOTE: Title will get g_strdup()ed.
+ * Returns a newly created search result column object. The column defaults
+ * to being visible.
*
- * @return The new search column object.
+ * Returns: The new search column object.
*/
PurpleNotifySearchColumn *purple_notify_searchresults_column_new(const char *title);
/**
- * Adds a new column to the search result object.
+ * purple_notify_searchresult_column_get_title:
+ * @column: The search column object.
*
- * @param results The result object to which the column will be added.
- * @param column The column that will be added to the result object.
- */
-void purple_notify_searchresults_column_add(PurpleNotifySearchResults *results,
- PurpleNotifySearchColumn *column);
-
-/**
- * Adds a new row of the results to the search results object.
+ * Returns the title of the column
*
- * @param results The search results object.
- * @param row The row of the results.
+ * Returns: The title of the column
*/
-void purple_notify_searchresults_row_add(PurpleNotifySearchResults *results,
- GList *row);
+const char *purple_notify_searchresult_column_get_title(const PurpleNotifySearchColumn *column);
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_NOTIFY_C_)
/**
- * Returns a number of the rows in the search results object.
- *
- * @deprecated This function will be removed in Pidgin 3.0.0 unless
- * there is sufficient demand to keep it. Using this
- * function encourages looping through the results
- * inefficiently. Instead of using this function you
- * should iterate through the results using a loop
- * similar to this:
- * for (l = results->rows; l != NULL; l = l->next)
- * If you really need to get the number of rows you
- * can use g_list_length(results->rows).
+ * purple_notify_searchresult_column_set_visible:
+ * @column: The search column object.
+ * @visible: TRUE if visible, or FALSE if not.
*
- * @param results The search results object.
- *
- * @return Number of the result rows.
+ * Sets whether or not a search result column is visible.
*/
-guint purple_notify_searchresults_get_rows_count(PurpleNotifySearchResults *results);
-#endif
+void purple_notify_searchresult_column_set_visible(PurpleNotifySearchColumn *column, gboolean visible);
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_NOTIFY_C_)
/**
- * Returns a number of the columns in the search results object.
- *
- * @deprecated This function will be removed in Pidgin 3.0.0 unless
- * there is sufficient demand to keep it. Using this
- * function encourages looping through the columns
- * inefficiently. Instead of using this function you
- * should iterate through the columns using a loop
- * similar to this:
- * for (l = results->columns; l != NULL; l = l->next)
- * If you really need to get the number of columns you
- * can use g_list_length(results->columns).
+ * purple_notify_searchresult_column_is_visible:
+ * @column: The search column object.
*
- * @param results The search results object.
+ * Returns whether or not a search result column is visible.
*
- * @return Number of the columns.
+ * Returns: TRUE if the search result column is visible. FALSE otherwise.
*/
-guint purple_notify_searchresults_get_columns_count(PurpleNotifySearchResults *results);
-#endif
+gboolean purple_notify_searchresult_column_is_visible(const PurpleNotifySearchColumn *column);
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_NOTIFY_C_)
/**
- * Returns a row of the results from the search results object.
- *
- * @deprecated This function will be removed in Pidgin 3.0.0 unless
- * there is sufficient demand to keep it. Using this
- * function encourages looping through the results
- * inefficiently. Instead of using this function you
- * should iterate through the results using a loop
- * similar to this:
- * for (l = results->rows; l != NULL; l = l->next)
- * If you really need to get the data for a particular
- * row you can use g_list_nth_data(results->rows, row_id).
+ * purple_notify_searchresults_column_add:
+ * @results: The result object to which the column will be added.
+ * @column: The column that will be added to the result object.
*
- * @param results The search results object.
- * @param row_id Index of the row to be returned.
- *
- * @return Row of the results.
+ * Adds a new column to the search result object.
*/
-GList *purple_notify_searchresults_row_get(PurpleNotifySearchResults *results,
- unsigned int row_id);
-#endif
+void purple_notify_searchresults_column_add(PurpleNotifySearchResults *results,
+ PurpleNotifySearchColumn *column);
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_NOTIFY_C_)
/**
- * Returns a title of the search results object's column.
- *
- * @deprecated This function will be removed in Pidgin 3.0.0 unless
- * there is sufficient demand to keep it. Using this
- * function encourages looping through the columns
- * inefficiently. Instead of using this function you
- * should iterate through the name of a particular
- * column you can use
- * g_list_nth_data(results->columns, row_id).
+ * purple_notify_searchresults_row_add:
+ * @results: The search results object.
+ * @row: The row of the results.
*
- * @param results The search results object.
- * @param column_id Index of the column.
- *
- * @return Title of the column.
+ * Adds a new row of the results to the search results object.
*/
-char *purple_notify_searchresults_column_get_title(PurpleNotifySearchResults *results,
- unsigned int column_id);
-#endif
-
-/*@}*/
+void purple_notify_searchresults_row_add(PurpleNotifySearchResults *results,
+ GList *row);
/**************************************************************************/
-/** @name Notification API */
+/* Notification API */
/**************************************************************************/
-/*@{*/
/**
- * Displays a notification message to the user.
+ * purple_notify_message:
+ * @handle: The plugin or connection handle.
+ * @type: The notification type.
+ * @title: The title of the message.
+ * @primary: The main point of the message.
+ * @secondary: The secondary information.
+ * @cpar: The #PurpleRequestCommonParameters associated with this
+ * request, or %NULL if none is.
+ * @cb: (scope call): The callback to call when the user closes
+ * the notification.
+ * @user_data: The data to pass to the callback.
*
- * @param handle The plugin or connection handle.
- * @param type The notification type.
- * @param title The title of the message.
- * @param primary The main point of the message.
- * @param secondary The secondary information.
- * @param cb The callback to call when the user closes
- * the notification.
- * @param user_data The data to pass to the callback.
+ * Displays a notification message to the user.
*
- * @return A UI-specific handle.
+ * Returns: A UI-specific handle.
*/
void *purple_notify_message(void *handle, PurpleNotifyMsgType type,
- const char *title, const char *primary,
- const char *secondary, PurpleNotifyCloseCallback cb,
- gpointer user_data);
+ const char *title, const char *primary, const char *secondary,
+ PurpleRequestCommonParameters *cpar, PurpleNotifyCloseCallback cb,
+ gpointer user_data);
/**
- * Displays a single email notification to the user.
+ * purple_notify_email:
+ * @handle: The plugin or connection handle.
+ * @subject: The subject of the email.
+ * @from: The from address.
+ * @to: The destination address.
+ * @url: The URL where the message can be read.
+ * @cb: (scope call): The callback to call when the user closes
+ * the notification.
+ * @user_data: The data to pass to the callback.
*
- * @param handle The plugin or connection handle.
- * @param subject The subject of the email.
- * @param from The from address.
- * @param to The destination address.
- * @param url The URL where the message can be read.
- * @param cb The callback to call when the user closes
- * the notification.
- * @param user_data The data to pass to the callback.
+ * Displays a single email notification to the user.
*
- * @return A UI-specific handle.
+ * Returns: A UI-specific handle.
*/
void *purple_notify_email(void *handle, const char *subject,
const char *from, const char *to,
@@ -424,23 +421,24 @@ void *purple_notify_email(void *handle, const char *subject,
gpointer user_data);
/**
+ * purple_notify_emails:
+ * @handle: The plugin or connection handle.
+ * @count: The number of emails. '0' can be used to signify that
+ * the user has no unread emails and the UI should remove
+ * the mail notification.
+ * @detailed: %TRUE if there is information for each email in the
+ * arrays.
+ * @subjects: The array of subjects.
+ * @froms: The array of from addresses.
+ * @tos: The array of destination addresses.
+ * @urls: The URLs where the messages can be read.
+ * @cb: (scope call): The callback to call when the user closes
+ * the notification.
+ * @user_data: The data to pass to the callback.
+ *
* Displays a notification for multiple emails to the user.
*
- * @param handle The plugin or connection handle.
- * @param count The number of emails. '0' can be used to signify that
- * the user has no unread emails and the UI should remove
- * the mail notification.
- * @param detailed @c TRUE if there is information for each email in the
- * arrays.
- * @param subjects The array of subjects.
- * @param froms The array of from addresses.
- * @param tos The array of destination addresses.
- * @param urls The URLs where the messages can be read.
- * @param cb The callback to call when the user closes
- * the notification.
- * @param user_data The data to pass to the callback.
- *
- * @return A UI-specific handle.
+ * Returns: A UI-specific handle.
*/
void *purple_notify_emails(void *handle, size_t count, gboolean detailed,
const char **subjects, const char **froms,
@@ -448,367 +446,424 @@ void *purple_notify_emails(void *handle, size_t count, gboolean detailed,
PurpleNotifyCloseCallback cb, gpointer user_data);
/**
+ * purple_notify_formatted:
+ * @handle: The plugin or connection handle.
+ * @title: The title of the message.
+ * @primary: The main point of the message.
+ * @secondary: The secondary information.
+ * @text: The formatted text.
+ * @cb: (scope call): The callback to call when the user closes
+ * the notification.
+ * @user_data: The data to pass to the callback.
+ *
* Displays a notification with formatted text.
*
* The text is essentially a stripped-down format of HTML, the same that
* IMs may send.
*
- * @param handle The plugin or connection handle.
- * @param title The title of the message.
- * @param primary The main point of the message.
- * @param secondary The secondary information.
- * @param text The formatted text.
- * @param cb The callback to call when the user closes
- * the notification.
- * @param user_data The data to pass to the callback.
- *
- * @return A UI-specific handle.
+ * Returns: A UI-specific handle.
*/
void *purple_notify_formatted(void *handle, const char *title,
const char *primary, const char *secondary,
const char *text, PurpleNotifyCloseCallback cb, gpointer user_data);
/**
+ * purple_notify_userinfo:
+ * @gc: The PurpleConnection handle associated with the information.
+ * @who: The username associated with the information.
+ * @user_info: The PurpleNotifyUserInfo which contains the information
+ * @cb: (scope call): The callback to call when the user closes the
+ * notification.
+ * @user_data: The data to pass to the callback.
+ *
* Displays user information with formatted text, passing information giving
* the connection and username from which the user information came.
*
* The text is essentially a stripped-down format of HTML, the same that
* IMs may send.
*
- * @param gc The PurpleConnection handle associated with the information.
- * @param who The username associated with the information.
- * @param user_info The PurpleNotifyUserInfo which contains the information
- * @param cb The callback to call when the user closes the notification.
- * @param user_data The data to pass to the callback.
- *
- * @return A UI-specific handle.
+ * Returns: A UI-specific handle.
*/
void *purple_notify_userinfo(PurpleConnection *gc, const char *who,
PurpleNotifyUserInfo *user_info, PurpleNotifyCloseCallback cb,
gpointer user_data);
/**
+ * purple_notify_user_info_get_type:
+ *
+ * Returns: The #GType for the #PurpleNotifyUserInfo boxed structure.
+ */
+GType purple_notify_user_info_get_type(void);
+
+/**
+ * purple_notify_user_info_new:
+ *
* Create a new PurpleNotifyUserInfo which is suitable for passing to
* purple_notify_userinfo()
*
- * @return A new PurpleNotifyUserInfo, which the caller must destroy when done
+ * Returns: A new PurpleNotifyUserInfo, which the caller must destroy when done
*/
PurpleNotifyUserInfo *purple_notify_user_info_new(void);
/**
- * Destroy a PurpleNotifyUserInfo
+ * purple_notify_user_info_destroy:
+ * @user_info: The PurpleNotifyUserInfo
*
- * @param user_info The PurpleNotifyUserInfo
+ * Destroy a PurpleNotifyUserInfo
*/
void purple_notify_user_info_destroy(PurpleNotifyUserInfo *user_info);
/**
+ * purple_notify_user_info_get_entries:
+ * @user_info: The PurpleNotifyUserInfo
+ *
* Retrieve the array of PurpleNotifyUserInfoEntry objects from a
* PurpleNotifyUserInfo
*
- * This GList may be manipulated directly with normal GList functions such
- * as g_list_insert(). Only PurpleNotifyUserInfoEntry are allowed in the
- * list. If a PurpleNotifyUserInfoEntry item is added to the list, it
- * should not be g_free()'d by the caller; PurpleNotifyUserInfo will g_free
- * it when destroyed.
+ * This GQueue may be manipulated directly with normal GQueue functions such
+ * as g_queue_push_tail(). Only PurpleNotifyUserInfoEntry are allowed in the
+ * queue. If a PurpleNotifyUserInfoEntry item is added to the queue, it
+ * should not be freed by the caller; PurpleNotifyUserInfo will free it when
+ * destroyed.
*
* To remove a PurpleNotifyUserInfoEntry, use
- * purple_notify_user_info_remove_entry(). Do not use the GList directly.
- *
- * @param user_info The PurpleNotifyUserInfo
+ * purple_notify_user_info_remove_entry(). Do not use the GQueue directly.
*
- * @constreturn A GList of PurpleNotifyUserInfoEntry objects
+ * Returns: (transfer none): A GQueue of PurpleNotifyUserInfoEntry objects.
*/
-GList *purple_notify_user_info_get_entries(PurpleNotifyUserInfo *user_info);
+GQueue *purple_notify_user_info_get_entries(PurpleNotifyUserInfo *user_info);
/**
+ * purple_notify_user_info_get_text_with_newline:
+ * @user_info: The PurpleNotifyUserInfo
+ * @newline: The separation character
+ *
* Create a textual representation of a PurpleNotifyUserInfo, separating
* entries with newline
- *
- * @param user_info The PurpleNotifyUserInfo
- * @param newline The separation character
*/
char *purple_notify_user_info_get_text_with_newline(PurpleNotifyUserInfo *user_info, const char *newline);
/**
- * Add a label/value pair to a PurpleNotifyUserInfo object.
- * PurpleNotifyUserInfo keeps track of the order in which pairs are added.
- *
- * @param user_info The PurpleNotifyUserInfo
- * @param label A label, which for example might be displayed by a
+ * purple_notify_user_info_add_pair_html:
+ * @user_info: The PurpleNotifyUserInfo
+ * @label: A label, which for example might be displayed by a
* UI with a colon after it ("Status:"). Do not include
* a colon. If NULL, value will be displayed without a
* label.
- * @param value The value, which might be displayed by a UI after
+ * @value: The value, which might be displayed by a UI after
* the label. This should be valid HTML. If you want
* to insert plaintext then use
* purple_notify_user_info_add_pair_plaintext(), instead.
* If this is NULL the label will still be displayed;
* the UI should treat label as independent and not
* include a colon if it would otherwise.
+ *
+ * Add a label/value pair to a PurpleNotifyUserInfo object.
+ * PurpleNotifyUserInfo keeps track of the order in which pairs are added.
*/
-/*
- * TODO: In 3.0.0 this function should be renamed to
- * purple_notify_user_info_add_pair_html(). And optionally
- * purple_notify_user_info_add_pair_plaintext() could be renamed to
- * purple_notify_user_info_add_pair().
- */
-void purple_notify_user_info_add_pair(PurpleNotifyUserInfo *user_info, const char *label, const char *value);
+void purple_notify_user_info_add_pair_html(PurpleNotifyUserInfo *user_info, const char *label, const char *value);
/**
- * Like purple_notify_user_info_add_pair, but value should be plaintext
+ * purple_notify_user_info_add_pair_plaintext:
+ *
+ * Like purple_notify_user_info_add_pair_html, but value should be plaintext
* and will be escaped using g_markup_escape_text().
*/
void purple_notify_user_info_add_pair_plaintext(PurpleNotifyUserInfo *user_info, const char *label, const char *value);
/**
- * Prepend a label/value pair to a PurpleNotifyUserInfo object
+ * purple_notify_user_info_prepend_pair_html:
*
- * @param user_info The PurpleNotifyUserInfo
- * @param label A label, which for example might be displayed by a
- * UI with a colon after it ("Status:"). Do not include
- * a colon. If NULL, value will be displayed without a
- * label.
- * @param value The value, which might be displayed by a UI after
- * the label. If NULL, label will still be displayed;
- * the UI should then treat label as independent and not
- * include a colon if it would otherwise.
+ * Like purple_notify_user_info_add_pair_html, but the pair is inserted
+ * at the beginning of the list.
*/
-void purple_notify_user_info_prepend_pair(PurpleNotifyUserInfo *user_info, const char *label, const char *value);
+void purple_notify_user_info_prepend_pair_html(PurpleNotifyUserInfo *user_info, const char *label, const char *value);
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_NOTIFY_C_)
/**
- * Remove a PurpleNotifyUserInfoEntry from a PurpleNotifyUserInfo object
- * without freeing the entry.
+ * purple_notify_user_info_prepend_pair_plaintext:
*
- * @param user_info The PurpleNotifyUserInfo
- * @param user_info_entry The PurpleNotifyUserInfoEntry
+ * Like purple_notify_user_info_prepend_pair_html, but value should be plaintext
+ * and will be escaped using g_markup_escape_text().
+ */
+void purple_notify_user_info_prepend_pair_plaintext(PurpleNotifyUserInfo *user_info, const char *label, const char *value);
+
+/**
+ * purple_notify_user_info_remove_entry:
+ * @user_info: The PurpleNotifyUserInfo
+ * @user_info_entry: The PurpleNotifyUserInfoEntry
*
- * @deprecated Nothing is using this function and it should be removed
- * in 3.0.0. Or, if we decide we want to keep it in 3.0.0
- * then we should make purple_notify_user_info_entry_destroy
- * public so that entries can be free'd after they're removed.
+ * Remove a PurpleNotifyUserInfoEntry from a PurpleNotifyUserInfo object
+ * without freeing the entry.
*/
void purple_notify_user_info_remove_entry(PurpleNotifyUserInfo *user_info, PurpleNotifyUserInfoEntry *user_info_entry);
-#endif
/**
- * Create a new PurpleNotifyUserInfoEntry
- *
- * If added to a PurpleNotifyUserInfo object, this should not be free()'d,
- * as PurpleNotifyUserInfo will do so when destroyed.
- * purple_notify_user_info_add_pair() and
- * purple_notify_user_info_prepend_pair() are convenience methods for
- * creating entries and adding them to a PurpleNotifyUserInfo.
- *
- * @param label A label, which for example might be displayed by a UI
+ * purple_notify_user_info_entry_new:
+ * @label: A label, which for example might be displayed by a UI
* with a colon after it ("Status:"). Do not include a
* colon. If NULL, value will be displayed without a label.
- * @param value The value, which might be displayed by a UI after the
+ * @value: The value, which might be displayed by a UI after the
* label. If NULL, label will still be displayed; the UI
* should then treat label as independent and not include a
* colon if it would otherwise.
*
- * @result A new PurpleNotifyUserInfoEntry
+ * Create a new PurpleNotifyUserInfoEntry
+ *
+ * If added to a PurpleNotifyUserInfo object, this should not be free()'d,
+ * as PurpleNotifyUserInfo will do so when destroyed.
+ * purple_notify_user_info_add_pair_html(),
+ * purple_notify_user_info_add_pair_plaintext(),
+ * purple_notify_user_info_prepend_pair_html() and
+ * purple_notify_user_info_prepend_pair_plaintext() are convenience
+ * methods for creating entries and adding them to a PurpleNotifyUserInfo.
+ *
+ * Returns: A new PurpleNotifyUserInfoEntry
*/
PurpleNotifyUserInfoEntry *purple_notify_user_info_entry_new(const char *label, const char *value);
/**
- * Add a section break. A UI might display this as a horizontal line.
+ * purple_notify_user_info_entry_destroy:
+ * @user_info_entry: The PurpleNotifyUserInfoEntry
+ *
+ * Destroy a PurpleNotifyUserInfoEntry
+ */
+void purple_notify_user_info_entry_destroy(PurpleNotifyUserInfoEntry *user_info_entry);
+
+/**
+ * purple_notify_user_info_add_section_break:
+ * @user_info: The PurpleNotifyUserInfo
*
- * @param user_info The PurpleNotifyUserInfo
+ * Add a section break. A UI might display this as a horizontal line.
*/
void purple_notify_user_info_add_section_break(PurpleNotifyUserInfo *user_info);
/**
- * Prepend a section break. A UI might display this as a horizontal line.
+ * purple_notify_user_info_prepend_section_break:
+ * @user_info: The PurpleNotifyUserInfo
*
- * @param user_info The PurpleNotifyUserInfo
- * @since 2.5.0
+ * Prepend a section break. A UI might display this as a horizontal line.
*/
void purple_notify_user_info_prepend_section_break(PurpleNotifyUserInfo *user_info);
/**
+ * purple_notify_user_info_add_section_header:
+ * @user_info: The PurpleNotifyUserInfo
+ * @label: The name of the section
+ *
* Add a section header. A UI might display this in a different font
* from other text.
- *
- * @param user_info The PurpleNotifyUserInfo
- * @param label The name of the section
*/
void purple_notify_user_info_add_section_header(PurpleNotifyUserInfo *user_info, const char *label);
/**
+ * purple_notify_user_info_prepend_section_header:
+ * @user_info: The PurpleNotifyUserInfo
+ * @label: The name of the section
+ *
* Prepend a section header. A UI might display this in a different font
* from other text.
- *
- * @param user_info The PurpleNotifyUserInfo
- * @param label The name of the section
- * @since 2.5.0
*/
void purple_notify_user_info_prepend_section_header(PurpleNotifyUserInfo *user_info, const char *label);
/**
+ * purple_notify_user_info_remove_last_item:
+ *
* Remove the last item which was added to a PurpleNotifyUserInfo. This
* could be used to remove a section header which is not needed.
*/
void purple_notify_user_info_remove_last_item(PurpleNotifyUserInfo *user_info);
/**
- * Get the label for a PurpleNotifyUserInfoEntry
+ * purple_notify_user_info_entry_get_label:
+ * @user_info_entry: The PurpleNotifyUserInfoEntry
*
- * @param user_info_entry The PurpleNotifyUserInfoEntry
+ * Get the label for a PurpleNotifyUserInfoEntry
*
- * @return The label
+ * Returns: The label
*/
const gchar *purple_notify_user_info_entry_get_label(PurpleNotifyUserInfoEntry *user_info_entry);
/**
- * Set the label for a PurpleNotifyUserInfoEntry
+ * purple_notify_user_info_entry_set_label:
+ * @user_info_entry: The PurpleNotifyUserInfoEntry
+ * @label: The label
*
- * @param user_info_entry The PurpleNotifyUserInfoEntry
- * @param label The label
+ * Set the label for a PurpleNotifyUserInfoEntry
*/
void purple_notify_user_info_entry_set_label(PurpleNotifyUserInfoEntry *user_info_entry, const char *label);
/**
- * Get the value for a PurpleNotifyUserInfoEntry
+ * purple_notify_user_info_entry_get_value:
+ * @user_info_entry: The PurpleNotifyUserInfoEntry
*
- * @param user_info_entry The PurpleNotifyUserInfoEntry
+ * Get the value for a PurpleNotifyUserInfoEntry
*
- * @result The value
+ * Returns: The value
*/
const gchar *purple_notify_user_info_entry_get_value(PurpleNotifyUserInfoEntry *user_info_entry);
/**
- * Set the value for a PurpleNotifyUserInfoEntry
+ * purple_notify_user_info_entry_set_value:
+ * @user_info_entry: The PurpleNotifyUserInfoEntry
+ * @value: The value
*
- * @param user_info_entry The PurpleNotifyUserInfoEntry
- * @param value The value
+ * Set the value for a PurpleNotifyUserInfoEntry
*/
void purple_notify_user_info_entry_set_value(PurpleNotifyUserInfoEntry *user_info_entry, const char *value);
/**
- * Get the type of a PurpleNotifyUserInfoEntry
+ * purple_notify_user_info_entry_get_entry_type:
+ * @user_info_entry: The PurpleNotifyUserInfoEntry
*
- * @param user_info_entry The PurpleNotifyUserInfoEntry
+ * Get the type of a PurpleNotifyUserInfoEntry
*
- * @return The PurpleNotifyUserInfoEntryType
+ * Returns: The PurpleNotifyUserInfoEntryType
*/
-PurpleNotifyUserInfoEntryType purple_notify_user_info_entry_get_type(PurpleNotifyUserInfoEntry *user_info_entry);
+PurpleNotifyUserInfoEntryType purple_notify_user_info_entry_get_entry_type(
+ PurpleNotifyUserInfoEntry *user_info_entry);
/**
- * Set the type of a PurpleNotifyUserInfoEntry
+ * purple_notify_user_info_entry_set_entry_type:
+ * @user_info_entry: The PurpleNotifyUserInfoEntry
+ * @type: The PurpleNotifyUserInfoEntryType
*
- * @param user_info_entry The PurpleNotifyUserInfoEntry
- * @param type The PurpleNotifyUserInfoEntryType
+ * Set the type of a PurpleNotifyUserInfoEntry
*/
-void purple_notify_user_info_entry_set_type(PurpleNotifyUserInfoEntry *user_info_entry,
- PurpleNotifyUserInfoEntryType type);
+void purple_notify_user_info_entry_set_entry_type(
+ PurpleNotifyUserInfoEntry *user_info_entry, PurpleNotifyUserInfoEntryType type);
/**
- * Opens a URI or somehow presents it to the user.
+ * purple_notify_uri:
+ * @handle: The plugin or connection handle.
+ * @uri: The URI to display or go to.
*
- * @param handle The plugin or connection handle.
- * @param uri The URI to display or go to.
+ * Opens a URI or somehow presents it to the user.
*
- * @return A UI-specific handle, if any. This may only be presented if
+ * Returns: A UI-specific handle, if any. This may only be presented if
* the UI code displays a dialog instead of a webpage, or something
* similar.
*/
void *purple_notify_uri(void *handle, const char *uri);
/**
+ * purple_notify_is_valid_ui_handle:
+ * @ui_handle: The UI handle.
+ * @type: The pointer to variable, where request type may be stored
+ * (may be %NULL).
+ *
+ * Checks, if passed UI handle is valid.
+ *
+ * Returns: TRUE, if handle is valid, FALSE otherwise.
+ */
+gboolean
+purple_notify_is_valid_ui_handle(void *ui_handle, PurpleNotifyType *type);
+
+/**
+ * purple_notify_close:
+ * @type: The notification type.
+ * @ui_handle: The notification UI handle.
+ *
* Closes a notification.
*
* This should be used only by the UI operation functions and part of the
* core.
- *
- * @param type The notification type.
- * @param ui_handle The notification UI handle.
*/
void purple_notify_close(PurpleNotifyType type, void *ui_handle);
/**
- * Closes all notifications registered with the specified handle.
+ * purple_notify_close_with_handle:
+ * @handle: The handle.
*
- * @param handle The handle.
+ * Closes all notifications registered with the specified handle.
*/
void purple_notify_close_with_handle(void *handle);
/**
+ * purple_notify_info:
+ *
* A wrapper for purple_notify_message that displays an information message.
*/
-#define purple_notify_info(handle, title, primary, secondary) \
+#define purple_notify_info(handle, title, primary, secondary, cpar) \
purple_notify_message((handle), PURPLE_NOTIFY_MSG_INFO, (title), \
- (primary), (secondary), NULL, NULL)
+ (primary), (secondary), (cpar), NULL, NULL)
/**
+ * purple_notify_warning:
+ *
* A wrapper for purple_notify_message that displays a warning message.
*/
-#define purple_notify_warning(handle, title, primary, secondary) \
+#define purple_notify_warning(handle, title, primary, secondary, cpar) \
purple_notify_message((handle), PURPLE_NOTIFY_MSG_WARNING, (title), \
- (primary), (secondary), NULL, NULL)
+ (primary), (secondary), (cpar), NULL, NULL)
/**
+ * purple_notify_error:
+ *
* A wrapper for purple_notify_message that displays an error message.
*/
-#define purple_notify_error(handle, title, primary, secondary) \
+#define purple_notify_error(handle, title, primary, secondary, cpar) \
purple_notify_message((handle), PURPLE_NOTIFY_MSG_ERROR, (title), \
- (primary), (secondary), NULL, NULL)
-
-/*@}*/
+ (primary), (secondary), (cpar), NULL, NULL)
/**************************************************************************/
-/** @name UI Registration Functions */
+/* UI Registration Functions */
/**************************************************************************/
-/*@{*/
/**
+ * purple_notify_ui_ops_get_type:
+ *
+ * Returns: The #GType for the #PurpleNotifyUiOps boxed structure.
+ */
+GType purple_notify_ui_ops_get_type(void);
+
+/**
+ * purple_notify_set_ui_ops:
+ * @ops: The UI operations structure.
+ *
* Sets the UI operations structure to be used when displaying a
* notification.
- *
- * @param ops The UI operations structure.
*/
void purple_notify_set_ui_ops(PurpleNotifyUiOps *ops);
/**
+ * purple_notify_get_ui_ops:
+ *
* Returns the UI operations structure to be used when displaying a
* notification.
*
- * @return The UI operations structure.
+ * Returns: The UI operations structure.
*/
PurpleNotifyUiOps *purple_notify_get_ui_ops(void);
-/*@}*/
-
/**************************************************************************/
-/** @name Notify Subsystem */
+/* Notify Subsystem */
/**************************************************************************/
-/*@{*/
/**
+ * purple_notify_get_handle:
+ *
* Returns the notify subsystem handle.
*
- * @return The notify subsystem handle.
+ * Returns: The notify subsystem handle.
*/
void *purple_notify_get_handle(void);
/**
+ * purple_notify_init:
+ *
* Initializes the notify subsystem.
*/
void purple_notify_init(void);
/**
+ * purple_notify_uninit:
+ *
* Uninitializes the notify subsystem.
*/
void purple_notify_uninit(void);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_NOTIFY_H_ */
diff --git a/libpurple/ntlm.c b/libpurple/ntlm.c
index 4bbfaf4c53..e61b110aa4 100644
--- a/libpurple/ntlm.c
+++ b/libpurple/ntlm.c
@@ -1,7 +1,3 @@
-/**
- * @file ntlm.c
- */
-
/* purple
*
* Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
@@ -28,8 +24,11 @@
#include "util.h"
#include "ntlm.h"
-#include "cipher.h"
#include "debug.h"
+
+#include "ciphers/descipher.h"
+#include "ciphers/md4hash.h"
+
#include <string.h>
#define NTLM_NEGOTIATE_NTLM2_KEY 0x00080000
@@ -122,7 +121,7 @@ purple_ntlm_gen_type1(const gchar *hostname, const gchar *domain)
host_off = sizeof(struct type1_message);
dom_off = sizeof(struct type1_message) + hostnamelen;
msg = g_malloc0(sizeof(struct type1_message) + hostnamelen + domainlen);
- tmsg = (struct type1_message*)msg;
+ tmsg = (struct type1_message*)(gpointer)msg;
tmsg->protocol[0] = 'N';
tmsg->protocol[1] = 'T';
tmsg->protocol[2] = 'L';
@@ -150,24 +149,27 @@ guint8 *
purple_ntlm_parse_type2(const gchar *type2, guint32 *flags)
{
gsize retlen;
- struct type2_message *tmsg;
+ guchar *buff;
+ struct type2_message tmsg;
static guint8 nonce[8];
- tmsg = (struct type2_message*)purple_base64_decode(type2, &retlen);
- if (tmsg != NULL && retlen >= (sizeof(struct type2_message) - 1)) {
- memcpy(nonce, tmsg->nonce, 8);
+ buff = purple_base64_decode(type2, &retlen);
+
+ if (buff != NULL && retlen >= (sizeof(struct type2_message) - 1)) {
+ memcpy(&tmsg, buff, MIN(retlen, sizeof(tmsg)));
+ memcpy(nonce, tmsg.nonce, 8);
if (flags != NULL)
- *flags = GUINT16_FROM_LE(tmsg->flags);
+ *flags = GUINT16_FROM_LE(tmsg.flags);
} else {
purple_debug_error("ntlm", "Unable to parse type2 message - returning empty nonce.\n");
memset(nonce, 0, 8);
}
- g_free(tmsg);
+ g_free(buff);
return nonce;
}
-/**
+/*
* Create a 64bit DES key by taking a 56bit key and adding
* a parity bit after every 7th bit.
*/
@@ -191,14 +193,13 @@ static void
des_ecb_encrypt(const guint8 *plaintext, guint8 *result, const guint8 *key)
{
PurpleCipher *cipher;
- PurpleCipherContext *context;
- size_t outlen;
-
- cipher = purple_ciphers_find_cipher("des");
- context = purple_cipher_context_new(cipher, NULL);
- purple_cipher_context_set_key(context, key);
- purple_cipher_context_encrypt(context, plaintext, 8, result, &outlen);
- purple_cipher_context_destroy(context);
+ gssize encsiz;
+
+ cipher = purple_des_cipher_new();
+ purple_cipher_set_key(cipher, key, 8);
+ encsiz = purple_cipher_encrypt(cipher, plaintext, 8, result, 8);
+ g_warn_if_fail(encsiz == 8);
+ g_object_unref(cipher);
}
/*
@@ -220,16 +221,41 @@ calc_resp(guint8 *keys, const guint8 *plaintext, unsigned char *results)
des_ecb_encrypt(plaintext, results + 16, key);
}
+/*
+ * TODO: We think we should be using cryptographically secure random numbers
+ * here. We think the rand() function is probably bad. We think
+ * /dev/urandom is a step up, but using a random function from an SSL
+ * library would probably be best. In Windows we could possibly also
+ * use CryptGenRandom.
+ */
static void
-gensesskey(char *buffer, const char *oldkey)
+gensesskey(char *buffer)
{
- int i = 0;
- if(oldkey == NULL) {
- for(i=0; i<16; i++) {
- buffer[i] = (char)(rand() & 0xff);
+ int fd;
+ int i;
+ ssize_t red = 0;
+
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd >= 0) {
+ red = read(fd, buffer, 16);
+ if (red < 0) {
+ purple_debug_warning("ntlm", "Error reading from /dev/urandom: %s."
+ " Falling back to inferior method.\n", g_strerror(errno));
+ red = 0;
+ } else if (red < 16) {
+ purple_debug_warning("ntlm", "Tried reading 16 bytes from "
+ "/dev/urandom but only got %"
+ G_GSSIZE_FORMAT ". Falling back to "
+ "inferior method\n", (gssize)red);
}
+ close(fd);
} else {
- memcpy(buffer, oldkey, 16);
+ purple_debug_warning("ntlm", "Error opening /dev/urandom: %s."
+ " Falling back to inferior method.\n", g_strerror(errno));
+ }
+
+ for (i = red; i < 16; i++) {
+ buffer[i] = (char)(rand() & 0xff);
}
}
@@ -250,8 +276,7 @@ purple_ntlm_gen_type3(const gchar *username, const gchar *passw, const gchar *ho
unsigned char magic[] = { 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 };
unsigned char nt_hpw[21];
char nt_pw[128];
- PurpleCipher *cipher;
- PurpleCipherContext *context;
+ PurpleHash *hash;
char *tmp;
int idx;
gchar *ucs2le;
@@ -352,11 +377,10 @@ purple_ntlm_gen_type3(const gchar *username, const gchar *passw, const gchar *ho
nt_pw[2 * idx + 1] = 0;
}
- cipher = purple_ciphers_find_cipher("md4");
- context = purple_cipher_context_new(cipher, NULL);
- purple_cipher_context_append(context, (guint8 *)nt_pw, 2 * lennt);
- purple_cipher_context_digest(context, 21, nt_hpw, NULL);
- purple_cipher_context_destroy(context);
+ hash = purple_md4_hash_new();
+ purple_hash_append(hash, (guint8 *)nt_pw, 2 * lennt);
+ purple_hash_digest(hash, nt_hpw, sizeof(nt_hpw));
+ g_object_unref(hash);
memset(nt_hpw + 16, 0, 5);
calc_resp(nt_hpw, nonce, nt_resp);
@@ -366,7 +390,7 @@ purple_ntlm_gen_type3(const gchar *username, const gchar *passw, const gchar *ho
/* LCS Stuff */
if (flags) {
tmsg->flags = GUINT32_TO_LE(0x409082d4);
- gensesskey(sesskey, NULL);
+ gensesskey(sesskey);
memcpy(tmp, sesskey, 0x10);
}
diff --git a/libpurple/ntlm.h b/libpurple/ntlm.h
index e61a4f5f07..cea622eecc 100644
--- a/libpurple/ntlm.h
+++ b/libpurple/ntlm.h
@@ -1,7 +1,3 @@
-/**
- * @file ntlm.h
- */
-
/* purple
*
* Copyright (C) 2005, Thomas Butter <butter@uni-mannheim.de>
@@ -26,48 +22,55 @@
#ifndef _PURPLE_NTLM_H
#define _PURPLE_NTLM_H
+/**
+ * SECTION:ntlm
+ * @section_id: libpurple-ntlm
+ * @short_description: <filename>ntlm.h</filename>
+ * @title: NTLM Authentication
+ */
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**
+ * purple_ntlm_gen_type1:
+ * @hostname: Your hostname
+ * @domain: The domain to authenticate to
+ *
* Generates the base64 encoded type 1 message needed for NTLM authentication
*
- * @param hostname Your hostname
- * @param domain The domain to authenticate to
- * @return base64 encoded string to send to the server. This should
+ * Returns: base64 encoded string to send to the server. This should
* be g_free'd by the caller.
*/
gchar *purple_ntlm_gen_type1(const gchar *hostname, const gchar *domain);
/**
- * Parses the ntlm type 2 message
+ * purple_ntlm_parse_type2:
+ * @type2: String containing the base64 encoded type2 message
+ * @flags: If not %NULL, this will store the flags for the message
*
- * @param type2 String containing the base64 encoded type2 message
- * @param flags If not @c NULL, this will store the flags for the message
+ * Parses the ntlm type 2 message
*
- * @return The nonce for use in message type3. This is a statically
+ * Returns: The nonce for use in message type3. This is a statically
* allocated 8 byte binary string.
*/
guint8 *purple_ntlm_parse_type2(const gchar *type2, guint32 *flags);
/**
+ * purple_ntlm_gen_type3:
+ * @username: The username
+ * @passw: The password
+ * @hostname: The hostname
+ * @domain: The domain to authenticate against
+ * @nonce: The nonce returned by purple_ntlm_parse_type2
+ * @flags: Pointer to the flags returned by purple_ntlm_parse_type2
+ *
* Generates a type3 message
*
- * @param username The username
- * @param passw The password
- * @param hostname The hostname
- * @param domain The domain to authenticate against
- * @param nonce The nonce returned by purple_ntlm_parse_type2
- * @param flags Pointer to the flags returned by purple_ntlm_parse_type2
- * @return A base64 encoded type3 message. This should be g_free'd by
- * the caller.
+ * Returns: A base64 encoded type3 message. This should be g_free'd by
+ * the caller.
*/
gchar *purple_ntlm_gen_type3(const gchar *username, const gchar *passw, const gchar *hostname, const gchar *domain, const guint8 *nonce, guint32 *flags);
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_NTLM_H */
diff --git a/libpurple/plugin.c b/libpurple/plugin.c
index 61bbc5d589..03f67d9af0 100644
--- a/libpurple/plugin.c
+++ b/libpurple/plugin.c
@@ -49,8 +49,8 @@ typedef struct
PurpleSignalMarshalFunc marshal;
int num_params;
- PurpleValue **params;
- PurpleValue *ret_value;
+ GType *param_types;
+ GType ret_type;
} PurplePluginIpcCommand;
@@ -64,13 +64,6 @@ static GList *plugin_loaders = NULL;
static GList *plugins_to_disable = NULL;
#endif
-static void (*probe_cb)(void *) = NULL;
-static void *probe_cb_data = NULL;
-static void (*load_cb)(PurplePlugin *, void *) = NULL;
-static void *load_cb_data = NULL;
-static void (*unload_cb)(PurplePlugin *, void *) = NULL;
-static void *unload_cb_data = NULL;
-
#ifdef PURPLE_PLUGINS
static gboolean
@@ -163,7 +156,7 @@ find_loader_for_plugin(const PurplePlugin *plugin)
#endif /* PURPLE_PLUGINS */
-/**
+/*
* Negative if a before b, 0 if equal, positive if a after b.
*/
static gint
@@ -539,7 +532,7 @@ purple_plugin_load(PurplePlugin *plugin)
dep_name);
purple_notify_error(NULL, NULL,
- _("Unable to load the plugin"), tmp);
+ _("Unable to load the plugin"), tmp, NULL);
g_free(tmp);
g_list_free(dep_list);
@@ -565,7 +558,7 @@ purple_plugin_load(PurplePlugin *plugin)
plugin->info->name);
purple_notify_error(NULL, NULL,
- _("Unable to load your plugin."), tmp);
+ _("Unable to load your plugin."), tmp, NULL);
g_free(tmp);
g_list_free(dep_list);
@@ -580,7 +573,7 @@ purple_plugin_load(PurplePlugin *plugin)
for (l = dep_list; l != NULL; l = l->next)
{
PurplePlugin *dep_plugin = (PurplePlugin *)l->data;
- dep_plugin->dependent_plugins = g_list_prepend(dep_plugin->dependent_plugins, plugin->info->id);
+ dep_plugin->dependent_plugins = g_list_prepend(dep_plugin->dependent_plugins, (gpointer)plugin->info->id);
}
g_list_free(dep_list);
@@ -612,9 +605,6 @@ purple_plugin_load(PurplePlugin *plugin)
plugin->loaded = TRUE;
- if (load_cb != NULL)
- load_cb(plugin, load_cb_data);
-
purple_signal_emit(purple_plugins_get_handle(), "plugin-load", plugin);
return TRUE;
@@ -742,9 +732,6 @@ purple_plugin_unload(PurplePlugin *plugin)
g_free(plugin->error);
plugin->error = NULL;
- if (unload_cb != NULL)
- unload_cb(plugin, unload_cb_data);
-
purple_signal_emit(purple_plugins_get_handle(), "plugin-unload", plugin);
purple_prefs_disconnect_by_handle(plugin);
@@ -975,26 +962,15 @@ static void
destroy_ipc_info(void *data)
{
PurplePluginIpcCommand *ipc_command = (PurplePluginIpcCommand *)data;
- int i;
-
- if (ipc_command->params != NULL)
- {
- for (i = 0; i < ipc_command->num_params; i++)
- purple_value_destroy(ipc_command->params[i]);
-
- g_free(ipc_command->params);
- }
-
- if (ipc_command->ret_value != NULL)
- purple_value_destroy(ipc_command->ret_value);
+ g_free(ipc_command->param_types);
g_free(ipc_command);
}
gboolean
purple_plugin_ipc_register(PurplePlugin *plugin, const char *command,
PurpleCallback func, PurpleSignalMarshalFunc marshal,
- PurpleValue *ret_value, int num_params, ...)
+ GType ret_type, int num_params, ...)
{
PurplePluginIpcInfo *ipc_info;
PurplePluginIpcCommand *ipc_command;
@@ -1017,19 +993,19 @@ purple_plugin_ipc_register(PurplePlugin *plugin, const char *command,
ipc_command->func = func;
ipc_command->marshal = marshal;
ipc_command->num_params = num_params;
- ipc_command->ret_value = ret_value;
+ ipc_command->ret_type = ret_type;
if (num_params > 0)
{
va_list args;
int i;
- ipc_command->params = g_new0(PurpleValue *, num_params);
+ ipc_command->param_types = g_new0(GType, num_params);
va_start(args, num_params);
for (i = 0; i < num_params; i++)
- ipc_command->params[i] = va_arg(args, PurpleValue *);
+ ipc_command->param_types[i] = va_arg(args, GType);
va_end(args);
}
@@ -1092,9 +1068,9 @@ purple_plugin_ipc_unregister_all(PurplePlugin *plugin)
}
gboolean
-purple_plugin_ipc_get_params(PurplePlugin *plugin, const char *command,
- PurpleValue **ret_value, int *num_params,
- PurpleValue ***params)
+purple_plugin_ipc_get_types(PurplePlugin *plugin, const char *command,
+ GType *ret_type, int *num_params,
+ GType **param_types)
{
PurplePluginIpcInfo *ipc_info;
PurplePluginIpcCommand *ipc_command;
@@ -1118,11 +1094,11 @@ purple_plugin_ipc_get_params(PurplePlugin *plugin, const char *command,
if (num_params != NULL)
*num_params = ipc_command->num_params;
- if (params != NULL)
- *params = ipc_command->params;
+ if (param_types != NULL)
+ *param_types = ipc_command->param_types;
- if (ret_value != NULL)
- *ret_value = ipc_command->ret_value;
+ if (ret_type != NULL)
+ *ret_type = ipc_command->ret_type;
return TRUE;
}
@@ -1179,18 +1155,14 @@ void
purple_plugins_init(void) {
void *handle = purple_plugins_get_handle();
- purple_plugins_add_search_path(LIBDIR);
+ purple_plugins_add_search_path(PURPLE_LIBDIR);
purple_signal_register(handle, "plugin-load",
purple_marshal_VOID__POINTER,
- NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_PLUGIN));
+ G_TYPE_NONE, 1, PURPLE_TYPE_PLUGIN);
purple_signal_register(handle, "plugin-unload",
purple_marshal_VOID__POINTER,
- NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_PLUGIN));
+ G_TYPE_NONE, 1, PURPLE_TYPE_PLUGIN);
}
void
@@ -1436,10 +1408,6 @@ purple_plugins_probe(const char *ext)
(GCompareFunc)compare_prpl);
}
}
-
- if (probe_cb != NULL)
- probe_cb(probe_cb_data);
-
#endif /* PURPLE_PLUGINS */
}
@@ -1510,50 +1478,6 @@ purple_plugins_enabled(void)
#endif
}
-void
-purple_plugins_register_probe_notify_cb(void (*func)(void *), void *data)
-{
- probe_cb = func;
- probe_cb_data = data;
-}
-
-void
-purple_plugins_unregister_probe_notify_cb(void (*func)(void *))
-{
- probe_cb = NULL;
- probe_cb_data = NULL;
-}
-
-void
-purple_plugins_register_load_notify_cb(void (*func)(PurplePlugin *, void *),
- void *data)
-{
- load_cb = func;
- load_cb_data = data;
-}
-
-void
-purple_plugins_unregister_load_notify_cb(void (*func)(PurplePlugin *, void *))
-{
- load_cb = NULL;
- load_cb_data = NULL;
-}
-
-void
-purple_plugins_register_unload_notify_cb(void (*func)(PurplePlugin *, void *),
- void *data)
-{
- unload_cb = func;
- unload_cb_data = data;
-}
-
-void
-purple_plugins_unregister_unload_notify_cb(void (*func)(PurplePlugin *, void *))
-{
- unload_cb = NULL;
- unload_cb_data = NULL;
-}
-
PurplePlugin *
purple_plugins_find_with_name(const char *name)
{
@@ -1673,3 +1597,30 @@ purple_plugin_action_free(PurplePluginAction *action)
g_free(action->label);
g_free(action);
}
+
+static PurplePlugin *
+purple_plugin_copy(PurplePlugin *plugin)
+{
+ PurplePlugin *plugin_copy;
+
+ g_return_val_if_fail(plugin != NULL, NULL);
+
+ plugin_copy = g_new(PurplePlugin, 1);
+ *plugin_copy = *plugin;
+
+ return plugin_copy;
+}
+
+GType
+purple_plugin_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurplePlugin",
+ (GBoxedCopyFunc)purple_plugin_copy,
+ (GBoxedFreeFunc)g_free);
+ }
+
+ return type;
+}
diff --git a/libpurple/plugin.h b/libpurple/plugin.h
index 9225aecd92..4454ccd4ef 100644
--- a/libpurple/plugin.h
+++ b/libpurple/plugin.h
@@ -1,11 +1,3 @@
-/**
- * @file plugin.h Plugin API
- * @ingroup core
- * @see @ref plugin-signals
- * @see @ref plugin-ids
- * @see @ref plugin-i18n
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -26,39 +18,55 @@
* 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_PLUGIN_H_
#define _PURPLE_PLUGIN_H_
+/**
+ * SECTION:plugin
+ * @section_id: libpurple-plugin
+ * @short_description: <filename>plugin.h</filename>
+ * @title: Plugin API
+ * @see_also: <link linkend="chapter-signals-plugin">Plugin signals</link>,
+ * <link linkend="chapter-plugin-ids">Plugin IDs</link>,
+ * <link linkend="chapter-plugin-i18n">Third Party Plugin Translation</link>
+ */
#include <glib.h>
#include <gmodule.h>
#include "signals.h"
-#include "value.h"
-/** @copydoc _PurplePlugin */
+#define PURPLE_TYPE_PLUGIN (purple_plugin_get_type())
+
typedef struct _PurplePlugin PurplePlugin;
-/** @copydoc _PurplePluginInfo */
typedef struct _PurplePluginInfo PurplePluginInfo;
-/** @copydoc _PurplePluginUiInfo */
typedef struct _PurplePluginUiInfo PurplePluginUiInfo;
-/** @copydoc _PurplePluginLoaderInfo */
typedef struct _PurplePluginLoaderInfo PurplePluginLoaderInfo;
-
-/** @copydoc _PurplePluginAction */
typedef struct _PurplePluginAction PurplePluginAction;
-typedef int PurplePluginPriority; /**< Plugin priority. */
+/**
+ * PurplePluginPriority:
+ *
+ * Plugin priority.
+ */
+typedef int PurplePluginPriority;
#include "pluginpref.h"
/**
+ * PurplePluginType:
+ * @PURPLE_PLUGIN_UNKNOWN: Unknown type.
+ * @PURPLE_PLUGIN_STANDARD: Standard plugin.
+ * @PURPLE_PLUGIN_LOADER: Loader plugin.
+ * @PURPLE_PLUGIN_PROTOCOL: Protocol plugin.
+ *
* Plugin types.
*/
typedef enum
{
- PURPLE_PLUGIN_UNKNOWN = -1, /**< Unknown type. */
- PURPLE_PLUGIN_STANDARD = 0, /**< Standard plugin. */
- PURPLE_PLUGIN_LOADER, /**< Loader plugin. */
- PURPLE_PLUGIN_PROTOCOL /**< Protocol plugin. */
+ PURPLE_PLUGIN_UNKNOWN = -1,
+ PURPLE_PLUGIN_STANDARD = 0,
+ PURPLE_PLUGIN_LOADER,
+ PURPLE_PLUGIN_PROTOCOL
} PurplePluginType;
@@ -71,6 +79,25 @@ typedef enum
#define PURPLE_PLUGIN_MAGIC 5 /* once we hit 6.0.0 I think we can remove this */
/**
+ * PurplePluginInfo:
+ * @load: If a plugin defines a @load function, and it returns %FALSE,
+ * then the plugin will not be loaded.
+ * @ui_info: Used only by UI-specific plugins to build a preference screen
+ * with a custom UI.
+ * @prefs_info: Used by any plugin to display preferences. If @ui_info has been
+ * specified, this will be ignored.
+ * @actions: This callback has a different use depending on whether this
+ * plugin type is #PURPLE_PLUGIN_STANDARD or
+ * #PURPLE_PLUGIN_PROTOCOL.
+ * <sbr/>If #PURPLE_PLUGIN_STANDARD then the list of actions will
+ * show up in the Tools menu, under a submenu with the name of the
+ * plugin. @context will be NULL.
+ * <sbr/>If PURPLE_PLUGIN_PROTOCOL then the list of actions will
+ * show up in the Accounts menu, under a submenu with the name of
+ * the account. @context will be set to the #PurpleConnection for
+ * that account. This callback will only be called for online
+ * accounts.
+ *
* Detailed information about a plugin.
*
* This is used in the version 2.0 API and up.
@@ -86,41 +113,25 @@ struct _PurplePluginInfo
GList *dependencies;
PurplePluginPriority priority;
- char *id;
- char *name;
- char *version;
- char *summary;
- char *description;
- char *author;
- char *homepage;
-
- /**
- * If a plugin defines a 'load' function, and it returns FALSE,
- * then the plugin will not be loaded.
- */
+ const char *id;
+ const char *name;
+ const char *version;
+ const char *summary;
+ const char *description;
+ const char *author;
+ const char *homepage;
+
gboolean (*load)(PurplePlugin *plugin);
gboolean (*unload)(PurplePlugin *plugin);
void (*destroy)(PurplePlugin *plugin);
- void *ui_info; /**< Used only by UI-specific plugins to build a preference screen with a custom UI */
+ void *ui_info;
void *extra_info;
- PurplePluginUiInfo *prefs_info; /**< Used by any plugin to display preferences. If #ui_info has been specified, this will be ignored. */
-
- /**
- * This callback has a different use depending on whether this
- * plugin type is PURPLE_PLUGIN_STANDARD or PURPLE_PLUGIN_PROTOCOL.
- *
- * If PURPLE_PLUGIN_STANDARD then the list of actions will show up
- * in the Tools menu, under a submenu with the name of the plugin.
- * context will be NULL.
- *
- * If PURPLE_PLUGIN_PROTOCOL then the list of actions will show up
- * in the Accounts menu, under a submenu with the name of the
- * account. context will be set to the PurpleConnection for that
- * account. This callback will only be called for online accounts.
- */
+ PurplePluginUiInfo *prefs_info;
+
GList *(*actions)(PurplePlugin *plugin, gpointer context);
+ /*< private >*/
void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
@@ -128,6 +139,8 @@ struct _PurplePluginInfo
};
/**
+ * PurplePluginLoaderInfo:
+ *
* Extra information for loader plugins.
*/
struct _PurplePluginLoaderInfo
@@ -139,6 +152,7 @@ struct _PurplePluginLoaderInfo
gboolean (*unload)(PurplePlugin *plugin);
void (*destroy)(PurplePlugin *plugin);
+ /*< private >*/
void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
@@ -146,21 +160,35 @@ struct _PurplePluginLoaderInfo
};
/**
+ * PurplePlugin:
+ * @native_plugin: Native C plugin.
+ * @loaded: The loaded state.
+ * @handle: The module handle.
+ * @path: The path to the plugin.
+ * @info: The plugin information.
+ * @ipc_data: IPC data.
+ * @extra: Plugin-specific data.
+ * @unloadable: Unloadable
+ * @dependent_plugins: Plugins depending on this
+ * @ui_data: The UI data.
+ *
* A plugin handle.
*/
struct _PurplePlugin
{
- gboolean native_plugin; /**< Native C plugin. */
- gboolean loaded; /**< The loaded state. */
- void *handle; /**< The module handle. */
- char *path; /**< The path to the plugin. */
- PurplePluginInfo *info; /**< The plugin information. */
+ gboolean native_plugin;
+ gboolean loaded;
+ void *handle;
+ char *path;
+ PurplePluginInfo *info;
char *error;
- void *ipc_data; /**< IPC data. */
- void *extra; /**< Plugin-specific data. */
- gboolean unloadable; /**< Unloadable */
- GList *dependent_plugins; /**< Plugins depending on this */
+ void *ipc_data;
+ void *extra;
+ gboolean unloadable;
+ GList *dependent_plugins;
+ gpointer ui_data;
+ /*< private >*/
void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
@@ -172,10 +200,9 @@ struct _PurplePlugin
struct _PurplePluginUiInfo {
PurplePluginPrefFrame *(*get_plugin_pref_frame)(PurplePlugin *plugin);
+ gpointer (*get_plugin_pref_request)(PurplePlugin *plugin);
- int page_num; /**< Reserved */
- PurplePluginPrefFrame *frame; /**< Reserved */
-
+ /*< private >*/
void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
@@ -190,19 +217,20 @@ struct _PurplePluginUiInfo {
/**
+ * PurplePluginAction:
+ * @plugin: Set to the owning plugin
+ * @context: %NULL for plugin actions menu, set to the #PurpleConnection for
+ * account actions menu
+ *
* The structure used in the actions member of PurplePluginInfo
*/
struct _PurplePluginAction {
char *label;
void (*callback)(PurplePluginAction *);
- /** set to the owning plugin */
PurplePlugin *plugin;
- /** NULL for plugin actions menu, set to the PurpleConnection for
- account actions menu */
gpointer context;
-
gpointer user_data;
};
@@ -215,6 +243,8 @@ struct _PurplePluginAction {
/**
+ * PURPLE_INIT_PLUGIN:
+ *
* Handles the initialization of modules.
*/
#if !defined(PURPLE_PLUGINS) || defined(PURPLE_STATIC_PRPL)
@@ -239,502 +269,478 @@ struct _PurplePluginAction {
#endif
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name Plugin API */
+/* Plugin API */
/**************************************************************************/
-/*@{*/
/**
- * Creates a new plugin structure.
+ * purple_plugin_get_type:
*
- * @param native Whether or not the plugin is native.
- * @param path The path to the plugin, or @c NULL if statically compiled.
+ * Returns: The #GType for the #PurplePlugin boxed structure.
+ */
+/* TODO Boxing of PurplePlugin is a temporary solution to having a GType for
+ * plugins. This should rather be a GObject instead of a GBoxed.
+ */
+GType purple_plugin_get_type(void);
+
+/**
+ * purple_plugin_new:
+ * @native: Whether or not the plugin is native.
+ * @path: The path to the plugin, or %NULL if statically compiled.
+ *
+ * Creates a new plugin structure.
*
- * @return A new PurplePlugin structure.
+ * Returns: A new PurplePlugin structure.
*/
PurplePlugin *purple_plugin_new(gboolean native, const char *path);
/**
+ * purple_plugin_probe:
+ * @filename: The plugin's filename.
+ *
* Probes a plugin, retrieving the information on it and adding it to the
* list of available plugins.
*
- * @param filename The plugin's filename.
+ * See purple_plugin_load(), purple_plugin_destroy().
*
- * @return The plugin handle.
- *
- * @see purple_plugin_load()
- * @see purple_plugin_destroy()
+ * Returns: The plugin handle.
*/
PurplePlugin *purple_plugin_probe(const char *filename);
/**
+ * purple_plugin_register:
+ * @plugin: The plugin to register.
+ *
* Registers a plugin and prepares it for loading.
*
* This shouldn't be called by anything but the internal module code.
* Plugins should use the PURPLE_INIT_PLUGIN() macro to register themselves
* with the core.
*
- * @param plugin The plugin to register.
- *
- * @return @c TRUE if the plugin was registered successfully. Otherwise
- * @c FALSE is returned (this happens if the plugin does not contain
+ * Returns: %TRUE if the plugin was registered successfully. Otherwise
+ * %FALSE is returned (this happens if the plugin does not contain
* the necessary information).
*/
gboolean purple_plugin_register(PurplePlugin *plugin);
/**
- * Attempts to load a previously probed plugin.
+ * purple_plugin_load:
+ * @plugin: The plugin to load.
*
- * @param plugin The plugin to load.
+ * Attempts to load a previously probed plugin.
*
- * @return @c TRUE if successful, or @c FALSE otherwise.
+ * See purple_plugin_reload(), purple_plugin_unload().
*
- * @see purple_plugin_reload()
- * @see purple_plugin_unload()
+ * Returns: %TRUE if successful, or %FALSE otherwise.
*/
gboolean purple_plugin_load(PurplePlugin *plugin);
/**
- * Unloads the specified plugin.
+ * purple_plugin_unload:
+ * @plugin: The plugin handle.
*
- * @param plugin The plugin handle.
+ * Unloads the specified plugin.
*
- * @return @c TRUE if successful, or @c FALSE otherwise.
+ * See purple_plugin_load(), purple_plugin_reload().
*
- * @see purple_plugin_load()
- * @see purple_plugin_reload()
+ * Returns: %TRUE if successful, or %FALSE otherwise.
*/
gboolean purple_plugin_unload(PurplePlugin *plugin);
/**
+ * purple_plugin_disable:
+ *
* Disable a plugin.
*
* This function adds the plugin to a list of plugins to "disable at the next
* startup" by excluding said plugins from the list of plugins to save. The
* UI needs to call purple_plugins_save_loaded() after calling this for it
* to have any effect.
- *
- * @since 2.3.0
*/
void purple_plugin_disable(PurplePlugin *plugin);
/**
- * Reloads a plugin.
+ * purple_plugin_reload:
+ * @plugin: The old plugin handle.
*
- * @param plugin The old plugin handle.
+ * Reloads a plugin.
*
- * @return @c TRUE if successful, or @c FALSE otherwise.
+ * See purple_plugin_load(), purple_plugin_unload().
*
- * @see purple_plugin_load()
- * @see purple_plugin_unload()
+ * Returns: %TRUE if successful, or %FALSE otherwise.
*/
gboolean purple_plugin_reload(PurplePlugin *plugin);
/**
- * Unloads a plugin and destroys the structure from memory.
+ * purple_plugin_destroy:
+ * @plugin: The plugin handle.
*
- * @param plugin The plugin handle.
+ * Unloads a plugin and destroys the structure from memory.
*/
void purple_plugin_destroy(PurplePlugin *plugin);
/**
- * Returns whether or not a plugin is currently loaded.
+ * purple_plugin_is_loaded:
+ * @plugin: The plugin.
*
- * @param plugin The plugin.
+ * Returns whether or not a plugin is currently loaded.
*
- * @return @c TRUE if loaded, or @c FALSE otherwise.
+ * Returns: %TRUE if loaded, or %FALSE otherwise.
*/
gboolean purple_plugin_is_loaded(const PurplePlugin *plugin);
/**
+ * purple_plugin_is_unloadable:
+ * @plugin: The plugin.
+ *
* Returns whether or not a plugin is unloadable.
*
- * If this returns @c TRUE, the plugin is guaranteed to not
- * be loadable. However, a return value of @c FALSE does not
+ * If this returns %TRUE, the plugin is guaranteed to not
+ * be loadable. However, a return value of %FALSE does not
* guarantee the plugin is loadable.
*
- * @param plugin The plugin.
- *
- * @return @c TRUE if the plugin is known to be unloadable,\
- * @c FALSE otherwise
+ * Returns: %TRUE if the plugin is known to be unloadable,\
+ * %FALSE otherwise
*/
gboolean purple_plugin_is_unloadable(const PurplePlugin *plugin);
/**
- * Returns a plugin's id.
+ * purple_plugin_get_id:
+ * @plugin: The plugin.
*
- * @param plugin The plugin.
+ * Returns a plugin's id.
*
- * @return The plugin's id.
+ * Returns: The plugin's id.
*/
const gchar *purple_plugin_get_id(const PurplePlugin *plugin);
/**
- * Returns a plugin's name.
+ * purple_plugin_get_name:
+ * @plugin: The plugin.
*
- * @param plugin The plugin.
+ * Returns a plugin's name.
*
- * @return THe name of the plugin, or @c NULL.
+ * Returns: THe name of the plugin, or %NULL.
*/
const gchar *purple_plugin_get_name(const PurplePlugin *plugin);
/**
- * Returns a plugin's version.
+ * purple_plugin_get_version:
+ * @plugin: The plugin.
*
- * @param plugin The plugin.
+ * Returns a plugin's version.
*
- * @return The plugin's version or @c NULL.
+ * Returns: The plugin's version or %NULL.
*/
const gchar *purple_plugin_get_version(const PurplePlugin *plugin);
/**
- * Returns a plugin's summary.
+ * purple_plugin_get_summary:
+ * @plugin: The plugin.
*
- * @param plugin The plugin.
+ * Returns a plugin's summary.
*
- * @return The plugin's summary.
+ * Returns: The plugin's summary.
*/
const gchar *purple_plugin_get_summary(const PurplePlugin *plugin);
/**
- * Returns a plugin's description.
+ * purple_plugin_get_description:
+ * @plugin: The plugin.
*
- * @param plugin The plugin.
+ * Returns a plugin's description.
*
- * @return The plugin's description.
+ * Returns: The plugin's description.
*/
const gchar *purple_plugin_get_description(const PurplePlugin *plugin);
/**
- * Returns a plugin's author.
+ * purple_plugin_get_author:
+ * @plugin: The plugin.
*
- * @param plugin The plugin.
+ * Returns a plugin's author.
*
- * @return The plugin's author.
+ * Returns: The plugin's author.
*/
const gchar *purple_plugin_get_author(const PurplePlugin *plugin);
/**
- * Returns a plugin's homepage.
+ * purple_plugin_get_homepage:
+ * @plugin: The plugin.
*
- * @param plugin The plugin.
+ * Returns a plugin's homepage.
*
- * @return The plugin's homepage.
+ * Returns: The plugin's homepage.
*/
const gchar *purple_plugin_get_homepage(const PurplePlugin *plugin);
-/*@}*/
-
/**************************************************************************/
-/** @name Plugin IPC API */
+/* Plugin IPC API */
/**************************************************************************/
-/*@{*/
/**
- * Registers an IPC command in a plugin.
+ * purple_plugin_ipc_register:
+ * @plugin: The plugin to register the command with.
+ * @command: The name of the command.
+ * @func: (scope call): The function to execute.
+ * @marshal: The marshalling function.
+ * @ret_type: The return type.
+ * @num_params: The number of parameters.
+ * @...: The parameter types.
*
- * @param plugin The plugin to register the command with.
- * @param command The name of the command.
- * @param func The function to execute.
- * @param marshal The marshalling function.
- * @param ret_value The return value type.
- * @param num_params The number of parameters.
- * @param ... The parameter types.
+ * Registers an IPC command in a plugin.
*
- * @return TRUE if the function was registered successfully, or
+ * Returns: TRUE if the function was registered successfully, or
* FALSE otherwise.
*/
gboolean purple_plugin_ipc_register(PurplePlugin *plugin, const char *command,
PurpleCallback func,
PurpleSignalMarshalFunc marshal,
- PurpleValue *ret_value, int num_params, ...);
+ GType ret_type, int num_params, ...);
/**
- * Unregisters an IPC command in a plugin.
+ * purple_plugin_ipc_unregister:
+ * @plugin: The plugin to unregister the command from.
+ * @command: The name of the command.
*
- * @param plugin The plugin to unregister the command from.
- * @param command The name of the command.
+ * Unregisters an IPC command in a plugin.
*/
void purple_plugin_ipc_unregister(PurplePlugin *plugin, const char *command);
/**
- * Unregisters all IPC commands in a plugin.
+ * purple_plugin_ipc_unregister_all:
+ * @plugin: The plugin to unregister the commands from.
*
- * @param plugin The plugin to unregister the commands from.
+ * Unregisters all IPC commands in a plugin.
*/
void purple_plugin_ipc_unregister_all(PurplePlugin *plugin);
/**
- * Returns a list of value types used for an IPC command.
+ * purple_plugin_ipc_get_types:
+ * @plugin: The plugin.
+ * @command: The name of the command.
+ * @ret_type: The returned return type.
+ * @num_params: The returned number of parameters.
+ * @param_types: The returned list of parameter types.
*
- * @param plugin The plugin.
- * @param command The name of the command.
- * @param ret_value The returned return value.
- * @param num_params The returned number of parameters.
- * @param params The returned list of parameters.
+ * Returns a list of value types used for an IPC command.
*
- * @return TRUE if the command was found, or FALSE otherwise.
+ * Returns: TRUE if the command was found, or FALSE otherwise.
*/
-gboolean purple_plugin_ipc_get_params(PurplePlugin *plugin, const char *command,
- PurpleValue **ret_value, int *num_params,
- PurpleValue ***params);
+gboolean purple_plugin_ipc_get_types(PurplePlugin *plugin, const char *command,
+ GType *ret_type, int *num_params,
+ GType **param_types);
/**
- * Executes an IPC command.
+ * purple_plugin_ipc_call:
+ * @plugin: The plugin to execute the command on.
+ * @command: The name of the command.
+ * @ok: TRUE if the call was successful, or FALSE otherwise.
+ * @...: The parameters to pass.
*
- * @param plugin The plugin to execute the command on.
- * @param command The name of the command.
- * @param ok TRUE if the call was successful, or FALSE otherwise.
- * @param ... The parameters to pass.
+ * Executes an IPC command.
*
- * @return The return value, which will be NULL if the command doesn't
+ * Returns: The return value, which will be NULL if the command doesn't
* return a value.
*/
void *purple_plugin_ipc_call(PurplePlugin *plugin, const char *command,
gboolean *ok, ...);
-/*@}*/
-
/**************************************************************************/
-/** @name Plugins API */
+/* Plugins API */
/**************************************************************************/
-/*@{*/
/**
- * Add a new directory to search for plugins
+ * purple_plugins_add_search_path:
+ * @path: The new search path.
*
- * @param path The new search path.
+ * Add a new directory to search for plugins
*/
void purple_plugins_add_search_path(const char *path);
/**
- * Returns a list of plugin search paths.
+ * purple_plugins_get_search_paths:
*
- * @constreturn A list of searched paths.
+ * Returns a list of plugin search paths.
*
- * @since 2.6.0
+ * Returns: (transfer none): A list of searched paths.
*/
GList *purple_plugins_get_search_paths(void);
/**
+ * purple_plugins_unload_all:
+ *
* Unloads all loaded plugins.
*/
void purple_plugins_unload_all(void);
/**
+ * purple_plugins_unload:
+ *
* Unloads all plugins of a specific type.
*/
void purple_plugins_unload(PurplePluginType type);
/**
+ * purple_plugins_destroy_all:
+ *
* Destroys all registered plugins.
*/
void purple_plugins_destroy_all(void);
/**
- * Saves the list of loaded plugins to the specified preference key
+ * purple_plugins_save_loaded:
+ * @key: The preference key to save the list of plugins to.
*
- * @param key The preference key to save the list of plugins to.
+ * Saves the list of loaded plugins to the specified preference key
*/
void purple_plugins_save_loaded(const char *key);
/**
+ * purple_plugins_load_saved:
+ * @key: The preference key containing the list of plugins.
+ *
* Attempts to load all the plugins in the specified preference key
* that were loaded when purple last quit.
- *
- * @param key The preference key containing the list of plugins.
*/
void purple_plugins_load_saved(const char *key);
/**
- * Probes for plugins in the registered module paths.
+ * purple_plugins_probe:
+ * @ext: The extension type to probe for, or %NULL for all.
*
- * @param ext The extension type to probe for, or @c NULL for all.
+ * Probes for plugins in the registered module paths.
*
- * @see purple_plugin_set_probe_path()
+ * See purple_plugins_add_search_path().
*/
void purple_plugins_probe(const char *ext);
/**
+ * purple_plugins_enabled:
+ *
* Returns whether or not plugin support is enabled.
*
- * @return TRUE if plugin support is enabled, or FALSE otherwise.
+ * Returns: TRUE if plugin support is enabled, or FALSE otherwise.
*/
gboolean purple_plugins_enabled(void);
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_PLUGIN_C_)
-/**
- * Registers a function that will be called when probing is finished.
- *
- * @param func The callback function.
- * @param data Data to pass to the callback.
- * @deprecated If you need this, ask for a plugin-probe signal to be added.
- */
-void purple_plugins_register_probe_notify_cb(void (*func)(void *), void *data);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_PLUGIN_C_)
-/**
- * Unregisters a function that would be called when probing is finished.
- *
- * @param func The callback function.
- * @deprecated If you need this, ask for a plugin-probe signal to be added.
- */
-void purple_plugins_unregister_probe_notify_cb(void (*func)(void *));
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_PLUGIN_C_)
-/**
- * Registers a function that will be called when a plugin is loaded.
- *
- * @param func The callback function.
- * @param data Data to pass to the callback.
- * @deprecated Use the plugin-load signal instead.
- */
-void purple_plugins_register_load_notify_cb(void (*func)(PurplePlugin *, void *),
- void *data);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_PLUGIN_C_)
/**
- * Unregisters a function that would be called when a plugin is loaded.
+ * purple_plugins_find_with_name:
+ * @name: The plugin name.
*
- * @param func The callback function.
- * @deprecated Use the plugin-load signal instead.
- */
-void purple_plugins_unregister_load_notify_cb(void (*func)(PurplePlugin *, void *));
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_PLUGIN_C_)
-/**
- * Registers a function that will be called when a plugin is unloaded.
- *
- * @param func The callback function.
- * @param data Data to pass to the callback.
- * @deprecated Use the plugin-unload signal instead.
- */
-void purple_plugins_register_unload_notify_cb(void (*func)(PurplePlugin *, void *),
- void *data);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_PLUGIN_C_)
-/**
- * Unregisters a function that would be called when a plugin is unloaded.
- *
- * @param func The callback function.
- * @deprecated Use the plugin-unload signal instead.
- */
-void purple_plugins_unregister_unload_notify_cb(void (*func)(PurplePlugin *,
- void *));
-#endif
-
-/**
* Finds a plugin with the specified name.
*
- * @param name The plugin name.
- *
- * @return The plugin if found, or @c NULL if not found.
+ * Returns: The plugin if found, or %NULL if not found.
*/
PurplePlugin *purple_plugins_find_with_name(const char *name);
/**
- * Finds a plugin with the specified filename (filename with a path).
+ * purple_plugins_find_with_filename:
+ * @filename: The plugin filename.
*
- * @param filename The plugin filename.
+ * Finds a plugin with the specified filename (filename with a path).
*
- * @return The plugin if found, or @c NULL if not found.
+ * Returns: The plugin if found, or %NULL if not found.
*/
PurplePlugin *purple_plugins_find_with_filename(const char *filename);
/**
- * Finds a plugin with the specified basename (filename without a path).
+ * purple_plugins_find_with_basename:
+ * @basename: The plugin basename.
*
- * @param basename The plugin basename.
+ * Finds a plugin with the specified basename (filename without a path).
*
- * @return The plugin if found, or @c NULL if not found.
+ * Returns: The plugin if found, or %NULL if not found.
*/
PurplePlugin *purple_plugins_find_with_basename(const char *basename);
/**
- * Finds a plugin with the specified plugin ID.
+ * purple_plugins_find_with_id:
+ * @id: The plugin ID.
*
- * @param id The plugin ID.
+ * Finds a plugin with the specified plugin ID.
*
- * @return The plugin if found, or @c NULL if not found.
+ * Returns: The plugin if found, or %NULL if not found.
*/
PurplePlugin *purple_plugins_find_with_id(const char *id);
/**
+ * purple_plugins_get_loaded:
+ *
* Returns a list of all loaded plugins.
*
- * @constreturn A list of all loaded plugins.
+ * Returns: (transfer none): A list of all loaded plugins.
*/
GList *purple_plugins_get_loaded(void);
/**
+ * purple_plugins_get_protocols:
+ *
* Returns a list of all valid protocol plugins. A protocol
* plugin is considered invalid if it does not contain the call
* to the PURPLE_INIT_PLUGIN() macro, or if it was compiled
* against an incompatable API version.
*
- * @constreturn A list of all protocol plugins.
+ * Returns: (transfer none): A list of all protocol plugins.
*/
GList *purple_plugins_get_protocols(void);
/**
+ * purple_plugins_get_all:
+ *
* Returns a list of all plugins, whether loaded or not.
*
- * @constreturn A list of all plugins.
+ * Returns: (transfer none): A list of all plugins.
*/
GList *purple_plugins_get_all(void);
-/*@}*/
-
/**************************************************************************/
-/** @name Plugins SubSytem API */
+/* Plugins SubSytem API */
/**************************************************************************/
-/*@{*/
/**
+ * purple_plugins_get_handle:
+ *
* Returns the plugin subsystem handle.
*
- * @return The plugin sybsystem handle.
+ * Returns: The plugin sybsystem handle.
*/
void *purple_plugins_get_handle(void);
/**
+ * purple_plugins_init:
+ *
* Initializes the plugin subsystem
*/
void purple_plugins_init(void);
/**
+ * purple_plugins_uninit:
+ *
* Uninitializes the plugin subsystem
*/
void purple_plugins_uninit(void);
-/*@}*/
-
/**
- * Allocates and returns a new PurplePluginAction.
+ * purple_plugin_action_new:
+ * @label: The description of the action to show to the user.
+ * @callback: (scope call): The callback to call when the user selects this
+ * action.
*
- * @param label The description of the action to show to the user.
- * @param callback The callback to call when the user selects this action.
+ * Allocates and returns a new PurplePluginAction.
*/
PurplePluginAction *purple_plugin_action_new(const char* label, void (*callback)(PurplePluginAction *));
/**
- * Frees a PurplePluginAction
+ * purple_plugin_action_free:
+ * @action: The PurplePluginAction to free.
*
- * @param action The PurplePluginAction to free.
+ * Frees a PurplePluginAction
*/
void purple_plugin_action_free(PurplePluginAction *action);
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_PLUGIN_H_ */
diff --git a/libpurple/pluginpref.c b/libpurple/pluginpref.c
index 61c0fc493f..a0455d68df 100644
--- a/libpurple/pluginpref.c
+++ b/libpurple/pluginpref.c
@@ -194,7 +194,7 @@ purple_plugin_pref_set_bounds(PurplePluginPref *pref, int min, int max)
g_return_if_fail(pref != NULL);
g_return_if_fail(pref->name != NULL);
- if (purple_prefs_get_type(pref->name) != PURPLE_PREF_INT)
+ if (purple_prefs_get_pref_type(pref->name) != PURPLE_PREF_INT)
{
purple_debug_warning("pluginpref",
"purple_plugin_pref_set_bounds: %s is not an integer pref\n",
@@ -218,7 +218,7 @@ void purple_plugin_pref_get_bounds(PurplePluginPref *pref, int *min, int *max)
g_return_if_fail(pref != NULL);
g_return_if_fail(pref->name != NULL);
- if (purple_prefs_get_type(pref->name) != PURPLE_PREF_INT)
+ if (purple_prefs_get_pref_type(pref->name) != PURPLE_PREF_INT)
{
purple_debug_warning("pluginpref",
"purple_plugin_pref_get_bounds: %s is not an integer pref\n",
@@ -231,7 +231,7 @@ void purple_plugin_pref_get_bounds(PurplePluginPref *pref, int *min, int *max)
}
void
-purple_plugin_pref_set_type(PurplePluginPref *pref, PurplePluginPrefType type)
+purple_plugin_pref_set_pref_type(PurplePluginPref *pref, PurplePluginPrefType type)
{
g_return_if_fail(pref != NULL);
@@ -239,7 +239,7 @@ purple_plugin_pref_set_type(PurplePluginPref *pref, PurplePluginPrefType type)
}
PurplePluginPrefType
-purple_plugin_pref_get_type(PurplePluginPref *pref)
+purple_plugin_pref_get_pref_type(PurplePluginPref *pref)
{
g_return_val_if_fail(pref != NULL, PURPLE_PLUGIN_PREF_NONE);
@@ -251,7 +251,7 @@ purple_plugin_pref_add_choice(PurplePluginPref *pref, const char *label, gpointe
{
g_return_if_fail(pref != NULL);
g_return_if_fail(label != NULL);
- g_return_if_fail(choice || purple_prefs_get_type(pref->name) == PURPLE_PREF_INT);
+ g_return_if_fail(choice || purple_prefs_get_pref_type(pref->name) == PURPLE_PREF_INT);
pref->choices = g_list_append(pref->choices, (gpointer)label);
pref->choices = g_list_append(pref->choices, choice);
diff --git a/libpurple/pluginpref.h b/libpurple/pluginpref.h
index 6ce04ad9ab..41d20c7a22 100644
--- a/libpurple/pluginpref.h
+++ b/libpurple/pluginpref.h
@@ -1,8 +1,3 @@
-/**
- * @file pluginpref.h Plugin Preferences API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -24,246 +19,294 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
*/
+
#ifndef _PURPLE_PLUGINPREF_H_
#define _PURPLE_PLUGINPREF_H_
+/**
+ * SECTION:pluginpref
+ * @section_id: libpurple-pluginpref
+ * @short_description: <filename>pluginpref.h</filename>
+ * @title: Plugin Preferences Frontend
+ */
typedef struct _PurplePluginPrefFrame PurplePluginPrefFrame;
typedef struct _PurplePluginPref PurplePluginPref;
/**
+ * PurpleStringFormatType:
+ * @PURPLE_STRING_FORMAT_TYPE_NONE: The string is plain text.
+ * @PURPLE_STRING_FORMAT_TYPE_MULTILINE: The string can have newlines.
+ * @PURPLE_STRING_FORMAT_TYPE_HTML: The string can be in HTML.
+ *
* String format for preferences.
*/
typedef enum
{
- PURPLE_STRING_FORMAT_TYPE_NONE = 0, /**< The string is plain text. */
- PURPLE_STRING_FORMAT_TYPE_MULTILINE = 1 << 0, /**< The string can have newlines. */
- PURPLE_STRING_FORMAT_TYPE_HTML = 1 << 1 /**< The string can be in HTML. */
+ PURPLE_STRING_FORMAT_TYPE_NONE = 0,
+ PURPLE_STRING_FORMAT_TYPE_MULTILINE = 1 << 0,
+ PURPLE_STRING_FORMAT_TYPE_HTML = 1 << 1
} PurpleStringFormatType;
+/**
+ * PurplePluginPrefType:
+ * @PURPLE_PLUGIN_PREF_INFO: no-value label
+ * @PURPLE_PLUGIN_PREF_STRING_FORMAT: The preference has a string value.
+ */
typedef enum {
PURPLE_PLUGIN_PREF_NONE,
PURPLE_PLUGIN_PREF_CHOICE,
- PURPLE_PLUGIN_PREF_INFO, /**< no-value label */
- PURPLE_PLUGIN_PREF_STRING_FORMAT /**< The preference has a string value. */
+ PURPLE_PLUGIN_PREF_INFO,
+ PURPLE_PLUGIN_PREF_STRING_FORMAT
} PurplePluginPrefType;
#include <glib.h>
#include "prefs.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name Plugin Preference API */
+/* Plugin Preference API */
/**************************************************************************/
-/*@{*/
/**
+ * purple_plugin_pref_frame_new:
+ *
* Create a new plugin preference frame
*
- * @return a new PurplePluginPrefFrame
+ * Returns: a new PurplePluginPrefFrame
*/
PurplePluginPrefFrame *purple_plugin_pref_frame_new(void);
/**
- * Destroy a plugin preference frame
+ * purple_plugin_pref_frame_destroy:
+ * @frame: The plugin frame to destroy
*
- * @param frame The plugin frame to destroy
+ * Destroy a plugin preference frame
*/
void purple_plugin_pref_frame_destroy(PurplePluginPrefFrame *frame);
/**
- * Adds a plugin preference to a plugin preference frame
+ * purple_plugin_pref_frame_add:
+ * @frame: The plugin frame to add the preference to
+ * @pref: The preference to add to the frame
*
- * @param frame The plugin frame to add the preference to
- * @param pref The preference to add to the frame
+ * Adds a plugin preference to a plugin preference frame
*/
void purple_plugin_pref_frame_add(PurplePluginPrefFrame *frame, PurplePluginPref *pref);
/**
+ * purple_plugin_pref_frame_get_prefs:
+ * @frame: The plugin frame to get the plugin preferences from
+ *
* Get the plugin preferences from a plugin preference frame
*
- * @param frame The plugin frame to get the plugin preferences from
- * @constreturn a GList of plugin preferences
+ * Returns: (transfer none): a GList of plugin preferences
*/
GList *purple_plugin_pref_frame_get_prefs(PurplePluginPrefFrame *frame);
/**
+ * purple_plugin_pref_new:
+ *
* Create a new plugin preference
*
- * @return a new PurplePluginPref
+ * Returns: a new PurplePluginPref
*/
PurplePluginPref *purple_plugin_pref_new(void);
/**
+ * purple_plugin_pref_new_with_name:
+ * @name: The name of the pref
+ *
* Create a new plugin preference with name
*
- * @param name The name of the pref
- * @return a new PurplePluginPref
+ * Returns: a new PurplePluginPref
*/
PurplePluginPref *purple_plugin_pref_new_with_name(const char *name);
/**
+ * purple_plugin_pref_new_with_label:
+ * @label: The label to be displayed
+ *
* Create a new plugin preference with label
*
- * @param label The label to be displayed
- * @return a new PurplePluginPref
+ * Returns: a new PurplePluginPref
*/
PurplePluginPref *purple_plugin_pref_new_with_label(const char *label);
/**
+ * purple_plugin_pref_new_with_name_and_label:
+ * @name: The name of the pref
+ * @label: The label to be displayed
+ *
* Create a new plugin preference with name and label
*
- * @param name The name of the pref
- * @param label The label to be displayed
- * @return a new PurplePluginPref
+ * Returns: a new PurplePluginPref
*/
PurplePluginPref *purple_plugin_pref_new_with_name_and_label(const char *name, const char *label);
/**
- * Destroy a plugin preference
+ * purple_plugin_pref_destroy:
+ * @pref: The preference to destroy
*
- * @param pref The preference to destroy
+ * Destroy a plugin preference
*/
void purple_plugin_pref_destroy(PurplePluginPref *pref);
/**
- * Set a plugin pref name
+ * purple_plugin_pref_set_name:
+ * @pref: The plugin pref
+ * @name: The name of the pref
*
- * @param pref The plugin pref
- * @param name The name of the pref
+ * Set a plugin pref name
*/
void purple_plugin_pref_set_name(PurplePluginPref *pref, const char *name);
/**
+ * purple_plugin_pref_get_name:
+ * @pref: The plugin pref
+ *
* Get a plugin pref name
*
- * @param pref The plugin pref
- * @return The name of the pref
+ * Returns: The name of the pref
*/
const char *purple_plugin_pref_get_name(PurplePluginPref *pref);
/**
- * Set a plugin pref label
+ * purple_plugin_pref_set_label:
+ * @pref: The plugin pref
+ * @label: The label for the plugin pref
*
- * @param pref The plugin pref
- * @param label The label for the plugin pref
+ * Set a plugin pref label
*/
void purple_plugin_pref_set_label(PurplePluginPref *pref, const char *label);
/**
+ * purple_plugin_pref_get_label:
+ * @pref: The plugin pref
+ *
* Get a plugin pref label
*
- * @param pref The plugin pref
- * @return The label for the plugin pref
+ * Returns: The label for the plugin pref
*/
const char *purple_plugin_pref_get_label(PurplePluginPref *pref);
/**
- * Set the bounds for an integer pref
+ * purple_plugin_pref_set_bounds:
+ * @pref: The plugin pref
+ * @min: The min value
+ * @max: The max value
*
- * @param pref The plugin pref
- * @param min The min value
- * @param max The max value
+ * Set the bounds for an integer pref
*/
void purple_plugin_pref_set_bounds(PurplePluginPref *pref, int min, int max);
/**
- * Get the bounds for an integer pref
+ * purple_plugin_pref_get_bounds:
+ * @pref: The plugin pref
+ * @min: The min value
+ * @max: The max value
*
- * @param pref The plugin pref
- * @param min The min value
- * @param max The max value
+ * Get the bounds for an integer pref
*/
void purple_plugin_pref_get_bounds(PurplePluginPref *pref, int *min, int *max);
/**
- * Set the type of a plugin pref
+ * purple_plugin_pref_set_pref_type:
+ * @pref: The plugin pref
+ * @type: The type
*
- * @param pref The plugin pref
- * @param type The type
+ * Set the type of a plugin pref
*/
-void purple_plugin_pref_set_type(PurplePluginPref *pref, PurplePluginPrefType type);
+void purple_plugin_pref_set_pref_type(PurplePluginPref *pref, PurplePluginPrefType type);
/**
+ * purple_plugin_pref_get_pref_type:
+ * @pref: The plugin pref
+ *
* Get the type of a plugin pref
*
- * @param pref The plugin pref
- * @return The type
+ * Returns: The type
*/
-PurplePluginPrefType purple_plugin_pref_get_type(PurplePluginPref *pref);
+PurplePluginPrefType purple_plugin_pref_get_pref_type(PurplePluginPref *pref);
/**
- * Set the choices for a choices plugin pref
+ * purple_plugin_pref_add_choice:
+ * @pref: The plugin pref
+ * @label: The label for the choice
+ * @choice: A gpointer of the choice
*
- * @param pref The plugin pref
- * @param label The label for the choice
- * @param choice A gpointer of the choice
+ * Set the choices for a choices plugin pref
*/
void purple_plugin_pref_add_choice(PurplePluginPref *pref, const char *label, gpointer choice);
/**
+ * purple_plugin_pref_get_choices:
+ * @pref: The plugin pref
+ *
* Get the choices for a choices plugin pref
*
- * @param pref The plugin pref
- * @constreturn GList of the choices
+ * Returns: (transfer none): GList of the choices
*/
GList *purple_plugin_pref_get_choices(PurplePluginPref *pref);
/**
- * Set the max length for a string plugin pref
+ * purple_plugin_pref_set_max_length:
+ * @pref: The plugin pref
+ * @max_length: The max length of the string
*
- * @param pref The plugin pref
- * @param max_length The max length of the string
+ * Set the max length for a string plugin pref
*/
void purple_plugin_pref_set_max_length(PurplePluginPref *pref, unsigned int max_length);
/**
+ * purple_plugin_pref_get_max_length:
+ * @pref: The plugin pref
+ *
* Get the max length for a string plugin pref
*
- * @param pref The plugin pref
- * @return the max length
+ * Returns: the max length
*/
unsigned int purple_plugin_pref_get_max_length(PurplePluginPref *pref);
/**
- * Sets the masking of a string plugin pref
+ * purple_plugin_pref_set_masked:
+ * @pref: The plugin pref
+ * @mask: The value to set
*
- * @param pref The plugin pref
- * @param mask The value to set
+ * Sets the masking of a string plugin pref
*/
void purple_plugin_pref_set_masked(PurplePluginPref *pref, gboolean mask);
/**
+ * purple_plugin_pref_get_masked:
+ * @pref: The plugin pref
+ *
* Gets the masking of a string plugin pref
*
- * @param pref The plugin pref
- * @return The masking
+ * Returns: The masking
*/
gboolean purple_plugin_pref_get_masked(PurplePluginPref *pref);
/**
+ * purple_plugin_pref_set_format_type:
+ * @pref: The plugin pref
+ * @format: The format of the string
+ *
* Sets the format type for a formattable-string plugin pref. You need to set the
* pref type to PURPLE_PLUGIN_PREF_STRING_FORMAT first before setting the format.
- *
- * @param pref The plugin pref
- * @param format The format of the string
*/
void purple_plugin_pref_set_format_type(PurplePluginPref *pref, PurpleStringFormatType format);
/**
+ * purple_plugin_pref_get_format_type:
+ * @pref: The plugin pref
+ *
* Gets the format type of the formattable-string plugin pref.
*
- * @param pref The plugin pref
- * @return The format of the pref
+ * Returns: The format of the pref
*/
PurpleStringFormatType purple_plugin_pref_get_format_type(PurplePluginPref *pref);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_PLUGINPREF_H_ */
diff --git a/libpurple/plugins/Makefile.am b/libpurple/plugins/Makefile.am
index adc6209773..539a332574 100644
--- a/libpurple/plugins/Makefile.am
+++ b/libpurple/plugins/Makefile.am
@@ -1,4 +1,4 @@
-DIST_SUBDIRS = mono perl ssl tcl
+DIST_SUBDIRS = mono perl ssl tcl keyrings
if USE_PERL
PERL_DIR = perl
@@ -20,31 +20,31 @@ SUBDIRS = \
$(MONO_DIR) \
$(PERL_DIR) \
ssl \
- $(TCL_DIR)
-
-plugindir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
-
-autoaccept_la_LDFLAGS = -module -avoid-version
-buddynote_la_LDFLAGS = -module -avoid-version
-ciphertest_la_LDFLAGS = -module -avoid-version
-codeinline_la_LDFLAGS = -module -avoid-version
-debug_example_la_LDFLAGS = -module -avoid-version
-helloworld_la_LDFLAGS = -module -avoid-version
-idle_la_LDFLAGS = -module -avoid-version
-joinpart_la_LDFLAGS = -module -avoid-version
-log_reader_la_LDFLAGS = -module -avoid-version
-newline_la_LDFLAGS = -module -avoid-version
-notify_example_la_LDFLAGS = -module -avoid-version
-offlinemsg_la_LDFLAGS = -module -avoid-version
-one_time_password_la_LDFLAGS = -module -avoid-version
-pluginpref_example_la_LDFLAGS = -module -avoid-version
-psychic_la_LDFLAGS = -module -avoid-version
-signals_test_la_LDFLAGS = -module -avoid-version
-simple_la_LDFLAGS = -module -avoid-version
-statenotify_la_LDFLAGS = -module -avoid-version
+ $(TCL_DIR) \
+ keyrings
+
+plugindir = @PURPLE_PLUGINDIR@
+
+autoaccept_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+buddynote_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+ciphertest_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+codeinline_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+debug_example_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+helloworld_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+idle_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+joinpart_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+log_reader_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+notify_example_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+offlinemsg_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+one_time_password_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+pluginpref_example_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+psychic_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+signals_test_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+simple_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+statenotify_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
# this can't be in a conditional otherwise automake 1.4 yells
-dbus_example_la_LDFLAGS = -module -avoid-version
+dbus_example_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
if PLUGINS
@@ -54,11 +54,9 @@ plugin_LTLIBRARIES = \
idle.la \
joinpart.la \
log_reader.la \
- newline.la \
offlinemsg.la \
psychic.la \
- statenotify.la \
- $(DBUS_LTLIB)
+ statenotify.la
noinst_LTLIBRARIES = \
ciphertest.la \
@@ -69,7 +67,8 @@ noinst_LTLIBRARIES = \
one_time_password.la \
pluginpref_example.la \
signals_test.la \
- simple.la
+ simple.la \
+ $(DBUS_LTLIB)
autoaccept_la_SOURCES = autoaccept.c
buddynote_la_SOURCES = buddynote.c
@@ -80,7 +79,6 @@ helloworld_la_SOURCES = helloworld.c
idle_la_SOURCES = idle.c
joinpart_la_SOURCES = joinpart.c
log_reader_la_SOURCES = log_reader.c
-newline_la_SOURCES = newline.c
notify_example_la_SOURCES = notify_example.c
offlinemsg_la_SOURCES = offlinemsg.c
one_time_password_la_SOURCES = one_time_password.c
@@ -90,37 +88,36 @@ signals_test_la_SOURCES = signals-test.c
simple_la_SOURCES = simple.c
statenotify_la_SOURCES = statenotify.c
-autoaccept_la_LIBADD = $(GLIB_LIBS)
-buddynote_la_LIBADD = $(GLIB_LIBS)
-ciphertest_la_LIBADD = $(GLIB_LIBS)
-codeinline_la_LIBADD = $(GLIB_LIBS)
-idle_la_LIBADD = $(GLIB_LIBS)
-joinpart_la_LIBADD = $(GLIB_LIBS)
-log_reader_la_LIBADD = $(GLIB_LIBS)
-newline_la_LIBADD = $(GLIB_LIBS)
-notify_example_la_LIBADD = $(GLIB_LIBS)
-offlinemsg_la_LIBADD = $(GLIB_LIBS)
-one_time_password_la_LIBADD = $(GLIB_LIBS)
-pluginpref_example_la_LIBADD = $(GLIB_LIBS)
-psychic_la_LIBADD = $(GLIB_LIBS)
-signals_test_la_LIBADD = $(GLIB_LIBS)
-simple_la_LIBADD = $(GLIB_LIBS)
-statenotify_la_LIBADD = $(GLIB_LIBS)
+autoaccept_la_LIBADD = @PURPLE_LIBS@
+buddynote_la_LIBADD = @PURPLE_LIBS@
+ciphertest_la_LIBADD = @PURPLE_LIBS@
+codeinline_la_LIBADD = @PURPLE_LIBS@
+idle_la_LIBADD = @PURPLE_LIBS@
+joinpart_la_LIBADD = @PURPLE_LIBS@
+log_reader_la_LIBADD = @PURPLE_LIBS@
+notify_example_la_LIBADD = @PURPLE_LIBS@
+offlinemsg_la_LIBADD = @PURPLE_LIBS@
+one_time_password_la_LIBADD = @PURPLE_LIBS@
+pluginpref_example_la_LIBADD = @PURPLE_LIBS@
+psychic_la_LIBADD = @PURPLE_LIBS@
+signals_test_la_LIBADD = @PURPLE_LIBS@
+simple_la_LIBADD = @PURPLE_LIBS@
+statenotify_la_LIBADD = @PURPLE_LIBS@
if ENABLE_DBUS
CLEANFILES = dbus-example-bindings.c
dbus_example_la_SOURCES = dbus-example.c
-dbus_example_la_LIBADD = $(GLIB_LIBS) $(DBUS_LIBS)
+dbus_example_la_LIBADD = @PURPLE_LIBS@ $(DBUS_LIBS)
.PHONY: always
$(top_builddir)/libpurple/dbus-types.h: always
- cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F)
+ $(AM_V_GEN)cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F)
dbus-example-bindings.c: $(top_srcdir)/libpurple/dbus-analyze-functions.py $(dbus_example_la_SOURCES)
- cat $(srcdir)/$(dbus_example_la_SOURCES) | \
+ $(AM_V_GEN)cat $(srcdir)/$(dbus_example_la_SOURCES) | \
$(PYTHON) $(top_srcdir)/libpurple/dbus-analyze-functions.py --export-only > $@
$(dbus_example_la_OBJECTS) dbus-example.so: dbus-example-bindings.c $(top_builddir)/libpurple/dbus-types.h
@@ -140,13 +137,16 @@ EXTRA_DIST = \
startup.py
AM_CPPFLAGS = \
- -DDATADIR=\"$(datadir)\" \
-I$(top_srcdir)/libpurple \
-I$(top_builddir)/libpurple \
$(DEBUG_CFLAGS) \
$(GLIB_CFLAGS) \
$(PLUGIN_CFLAGS) \
- $(DBUS_CFLAGS)
+ $(DBUS_CFLAGS) \
+ $(NSS_CFLAGS)
+
+PLUGIN_LIBS = \
+ $(NSS_LIBS)
#
# This part allows people to build their own plugins in here.
@@ -155,7 +155,7 @@ AM_CPPFLAGS = \
SUFFIXES = .c .so
.c.so:
$(LIBTOOL) --mode=compile $(CC) -DHAVE_CONFIG_H -I$(top_builddir) $(AM_CPPFLAGS) $(CFLAGS) -c $< -o tmp$@.lo $(PLUGIN_CFLAGS)
- $(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o libtmp$@.la -rpath $(plugindir) tmp$@.lo $(LIBS) $(LDFLAGS) -module -avoid-version $(PLUGIN_LIBS)
+ $(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o libtmp$@.la -rpath $(plugindir) tmp$@.lo $(LIBS) $(LDFLAGS) -module @PLUGIN_LDFLAGS@ $(PLUGIN_LIBS)
@rm -f tmp$@.lo tmp$@.o libtmp$@.la
@cp .libs/libtmp$@*.so $@
@rm -rf .libs/libtmp$@.*
diff --git a/libpurple/plugins/Makefile.mingw b/libpurple/plugins/Makefile.mingw
index a62253fedb..64683d9665 100644
--- a/libpurple/plugins/Makefile.mingw
+++ b/libpurple/plugins/Makefile.mingw
@@ -10,6 +10,7 @@ include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
PERL_PLUGIN := ./perl
TCL_PLUGIN := ./tcl
SSL_PLUGIN := ./ssl
+KEYRING_PLUGIN := ./keyrings
.SUFFIXES:
.SUFFIXES: .c .dll
@@ -45,14 +46,16 @@ LIBS = \
.PHONY: all clean plugins install
all: $(PURPLE_DLL).a plugins
- $(MAKE) -C $(PERL_PLUGIN) -f $(MINGW_MAKEFILE)
- $(MAKE) -C $(TCL_PLUGIN) -f $(MINGW_MAKEFILE)
- $(MAKE) -C $(SSL_PLUGIN) -f $(MINGW_MAKEFILE)
+ $(MAKE_at) $(MAKE) -C $(PERL_PLUGIN) -f $(MINGW_MAKEFILE)
+ $(MAKE_at) $(MAKE) -C $(TCL_PLUGIN) -f $(MINGW_MAKEFILE)
+ $(MAKE_at) $(MAKE) -C $(SSL_PLUGIN) -f $(MINGW_MAKEFILE)
+ $(MAKE_at) $(MAKE) -C $(KEYRING_PLUGIN) -f $(MINGW_MAKEFILE)
install: all $(PURPLE_INSTALL_PLUGINS_DIR)
- $(MAKE) -C $(PERL_PLUGIN) -f $(MINGW_MAKEFILE) install
- $(MAKE) -C $(TCL_PLUGIN) -f $(MINGW_MAKEFILE) install
- $(MAKE) -C $(SSL_PLUGIN) -f $(MINGW_MAKEFILE) install
+ $(MAKE_at) $(MAKE) -C $(PERL_PLUGIN) -f $(MINGW_MAKEFILE) install
+ $(MAKE_at) $(MAKE) -C $(TCL_PLUGIN) -f $(MINGW_MAKEFILE) install
+ $(MAKE_at) $(MAKE) -C $(SSL_PLUGIN) -f $(MINGW_MAKEFILE) install
+ $(MAKE_at) $(MAKE) -C $(KEYRING_PLUGIN) -f $(MINGW_MAKEFILE) install
cp *.dll $(PURPLE_INSTALL_PLUGINS_DIR)
%.dll: %.c $(PURPLE_CONFIG_H) $(PURPLE_VERSION_H)
@@ -65,7 +68,6 @@ plugins: \
idle.dll \
joinpart.dll \
log_reader.dll \
- newline.dll \
offlinemsg.dll \
psychic.dll \
statenotify.dll
@@ -75,8 +77,9 @@ plugins: \
##
clean:
rm -f *.o *.dll
- $(MAKE) -C $(PERL_PLUGIN) -f $(MINGW_MAKEFILE) clean
- $(MAKE) -C $(TCL_PLUGIN) -f $(MINGW_MAKEFILE) clean
- $(MAKE) -C $(SSL_PLUGIN) -f $(MINGW_MAKEFILE) clean
+ $(MAKE_at) $(MAKE) -C $(PERL_PLUGIN) -f $(MINGW_MAKEFILE) clean
+ $(MAKE_at) $(MAKE) -C $(TCL_PLUGIN) -f $(MINGW_MAKEFILE) clean
+ $(MAKE_at) $(MAKE) -C $(SSL_PLUGIN) -f $(MINGW_MAKEFILE) clean
+ $(MAKE_at) $(MAKE) -C $(KEYRING_PLUGIN) -f $(MINGW_MAKEFILE) clean
include $(PIDGIN_COMMON_TARGETS)
diff --git a/libpurple/plugins/autoaccept.c b/libpurple/plugins/autoaccept.c
index 1e5a2d36d7..6ed0c6208f 100644
--- a/libpurple/plugins/autoaccept.c
+++ b/libpurple/plugins/autoaccept.c
@@ -34,9 +34,9 @@
#include <plugin.h>
#include <version.h>
-#include <blist.h>
+#include <buddylist.h>
#include <conversation.h>
-#include <ft.h>
+#include <xfer.h>
#include <request.h>
#include <notify.h>
#include <util.h>
@@ -73,11 +73,13 @@ static void
auto_accept_complete_cb(PurpleXfer *xfer, PurpleXfer *my)
{
if (xfer == my && purple_prefs_get_bool(PREF_NOTIFY) &&
- !purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, xfer->who, xfer->account))
+ !purple_conversations_find_im_with_account(purple_xfer_get_remote_user(xfer), purple_xfer_get_account(xfer)))
{
char *message = g_strdup_printf(_("Autoaccepted file transfer of \"%s\" from \"%s\" completed."),
- xfer->filename, xfer->who);
- purple_notify_info(NULL, _("Autoaccept complete"), message, NULL);
+ purple_xfer_get_filename(xfer), purple_xfer_get_remote_user(xfer));
+ purple_notify_info(NULL, _("Autoaccept complete"), message,
+ NULL, purple_request_cpar_from_account(
+ purple_xfer_get_account(xfer)));
g_free(message);
}
}
@@ -93,14 +95,14 @@ file_recv_request_cb(PurpleXfer *xfer, gpointer handle)
int accept_setting;
- account = xfer->account;
- node = PURPLE_BLIST_NODE(purple_find_buddy(account, xfer->who));
+ account = purple_xfer_get_account(xfer);
+ node = PURPLE_BLIST_NODE(purple_blist_find_buddy(account, purple_xfer_get_remote_user(xfer)));
/* If person is on buddy list, use the buddy setting; otherwise, use the
stranger setting. */
if (node) {
node = purple_blist_node_get_parent(node);
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CONTACT(node));
+ g_return_if_fail(PURPLE_IS_CONTACT(node));
accept_setting = purple_blist_node_get_int(node, "autoaccept");
} else {
accept_setting = purple_prefs_get_int(PREF_STRANGER);
@@ -121,7 +123,7 @@ file_recv_request_cb(PurpleXfer *xfer, gpointer handle)
gchar *ext;
if (purple_prefs_get_bool(PREF_NEWDIR))
- dirname = g_build_filename(pref, purple_normalize(account, xfer->who), NULL);
+ dirname = g_build_filename(pref, purple_normalize(account, purple_xfer_get_remote_user(xfer)), NULL);
else
dirname = g_build_filename(pref, NULL);
@@ -133,9 +135,9 @@ file_recv_request_cb(PurpleXfer *xfer, gpointer handle)
/* Escape filename (if escaping is turned on) */
if (purple_prefs_get_bool(PREF_ESCAPE)) {
- escape = purple_escape_filename(xfer->filename);
+ escape = purple_escape_filename(purple_xfer_get_filename(xfer));
} else {
- escape = xfer->filename;
+ escape = purple_xfer_get_filename(xfer);
}
filename = g_build_filename(dirname, escape, NULL);
@@ -174,7 +176,7 @@ file_recv_request_cb(PurpleXfer *xfer, gpointer handle)
PURPLE_CALLBACK(auto_accept_complete_cb), xfer);
break;
case FT_REJECT:
- xfer->status = PURPLE_XFER_STATUS_CANCEL_LOCAL;
+ purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL);
break;
}
}
@@ -182,9 +184,9 @@ file_recv_request_cb(PurpleXfer *xfer, gpointer handle)
static void
save_cb(PurpleBlistNode *node, int choice)
{
- if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if (PURPLE_IS_BUDDY(node))
node = purple_blist_node_get_parent(node);
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CONTACT(node));
+ g_return_if_fail(PURPLE_IS_CONTACT(node));
purple_blist_node_set_int(node, "autoaccept", choice);
}
@@ -193,22 +195,20 @@ set_auto_accept_settings(PurpleBlistNode *node, gpointer plugin)
{
char *message;
- if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if (PURPLE_IS_BUDDY(node))
node = purple_blist_node_get_parent(node);
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CONTACT(node));
+ g_return_if_fail(PURPLE_IS_CONTACT(node));
message = g_strdup_printf(_("When a file-transfer request arrives from %s"),
- purple_contact_get_alias((PurpleContact *)node));
+ purple_contact_get_alias(PURPLE_CONTACT(node)));
purple_request_choice(plugin, _("Set Autoaccept Setting"), message,
- NULL, purple_blist_node_get_int(node, "autoaccept"),
+ NULL, GINT_TO_POINTER(purple_blist_node_get_int(node, "autoaccept")),
_("_Save"), G_CALLBACK(save_cb),
_("_Cancel"), NULL,
- NULL, NULL, NULL,
- node,
- _("Ask"), FT_ASK,
- _("Auto Accept"), FT_ACCEPT,
- _("Auto Reject"), FT_REJECT,
- NULL, purple_contact_get_alias((PurpleContact *)node), NULL,
+ NULL, node,
+ _("Ask"), GINT_TO_POINTER(FT_ASK),
+ _("Auto Accept"), GINT_TO_POINTER(FT_ACCEPT),
+ _("Auto Reject"), GINT_TO_POINTER(FT_REJECT),
NULL);
g_free(message);
}
@@ -218,8 +218,8 @@ context_menu(PurpleBlistNode *node, GList **menu, gpointer plugin)
{
PurpleMenuAction *action;
- if (!PURPLE_BLIST_NODE_IS_BUDDY(node) && !PURPLE_BLIST_NODE_IS_CONTACT(node) &&
- !(purple_blist_node_get_flags(node) & PURPLE_BLIST_NODE_FLAG_NO_SAVE))
+ if (!PURPLE_IS_BUDDY(node) && !PURPLE_IS_CONTACT(node) &&
+ !purple_blist_node_is_transient(node))
return;
action = purple_menu_action_new(_("Autoaccept File Transfers..."),
@@ -239,7 +239,7 @@ plugin_load(PurplePlugin *plugin)
* --Mark Doliner, 2011-01-03
*/
if (!purple_prefs_exists(PREF_STRANGER)) {
- if (purple_prefs_get_bool(PREF_STRANGER_OLD))
+ if (purple_prefs_exists(PREF_STRANGER_OLD) && purple_prefs_get_bool(PREF_STRANGER_OLD))
purple_prefs_add_int(PREF_STRANGER, FT_REJECT);
else
purple_prefs_set_int(PREF_STRANGER, FT_ASK);
@@ -274,7 +274,7 @@ get_plugin_pref_frame(PurplePlugin *plugin)
pref = purple_plugin_pref_new_with_name_and_label(PREF_STRANGER,
_("When a file-transfer request arrives from a user who is\n"
"*not* on your buddy list:"));
- purple_plugin_pref_set_type(pref, PURPLE_PLUGIN_PREF_CHOICE);
+ purple_plugin_pref_set_pref_type(pref, PURPLE_PLUGIN_PREF_CHOICE);
purple_plugin_pref_add_choice(pref, _("Ask"), GINT_TO_POINTER(FT_ASK));
purple_plugin_pref_add_choice(pref, _("Auto Accept"), GINT_TO_POINTER(FT_ACCEPT));
purple_plugin_pref_add_choice(pref, _("Auto Reject"), GINT_TO_POINTER(FT_REJECT));
@@ -298,7 +298,6 @@ get_plugin_pref_frame(PurplePlugin *plugin)
static PurplePluginUiInfo prefs_info = {
get_plugin_pref_frame,
- 0,
NULL,
/* padding */
diff --git a/libpurple/plugins/buddynote.c b/libpurple/plugins/buddynote.c
index e7d676215a..2ab5495459 100644
--- a/libpurple/plugins/buddynote.c
+++ b/libpurple/plugins/buddynote.c
@@ -49,8 +49,7 @@ buddynote_edit_cb(PurpleBlistNode *node, gpointer data)
note, TRUE, FALSE, "html",
_("Save"), G_CALLBACK(do_it_cb),
_("Cancel"), G_CALLBACK(dont_do_it_cb),
- NULL, NULL, NULL,
- node);
+ NULL, node);
}
static void
@@ -58,7 +57,7 @@ buddynote_extended_menu_cb(PurpleBlistNode *node, GList **m)
{
PurpleMenuAction *bna = NULL;
- if (purple_blist_node_get_flags(node) & PURPLE_BLIST_NODE_FLAG_NO_SAVE)
+ if (purple_blist_node_is_transient(node))
return;
*m = g_list_append(*m, bna);
diff --git a/libpurple/plugins/ciphertest.c b/libpurple/plugins/ciphertest.c
index e742cc3b96..0369cb36a4 100644
--- a/libpurple/plugins/ciphertest.c
+++ b/libpurple/plugins/ciphertest.c
@@ -32,10 +32,16 @@
#include <glib.h>
#include <string.h>
-#include "cipher.h"
#include "debug.h"
#include "plugin.h"
#include "version.h"
+#include "util.h"
+
+#include "ciphers/aescipher.h"
+#include "ciphers/md5hash.h"
+#include "ciphers/pbkdf2cipher.h"
+#include "ciphers/sha1hash.h"
+#include "ciphers/sha256hash.h"
struct test {
const gchar *question;
@@ -62,14 +68,13 @@ struct test md5_tests[8] = {
static void
cipher_test_md5(void) {
- PurpleCipher *cipher;
- PurpleCipherContext *context;
+ PurpleHash *hash;
gchar digest[33];
gboolean ret;
gint i = 0;
- cipher = purple_ciphers_find_cipher("md5");
- if(!cipher) {
+ hash = purple_md5_hash_new();
+ if(!hash) {
purple_debug_info("cipher-test",
"could not find md5 cipher, not testing\n");
return;
@@ -77,17 +82,14 @@ cipher_test_md5(void) {
purple_debug_info("cipher-test", "Running md5 tests\n");
- context = purple_cipher_context_new(cipher, NULL);
-
while(md5_tests[i].answer) {
purple_debug_info("cipher-test", "Test %02d:\n", i);
purple_debug_info("cipher-test", "Testing '%s'\n", md5_tests[i].question);
- purple_cipher_context_append(context, (guchar *)md5_tests[i].question,
+ purple_hash_append(hash, (guchar *)md5_tests[i].question,
strlen(md5_tests[i].question));
- ret = purple_cipher_context_digest_to_str(context, sizeof(digest),
- digest, NULL);
+ ret = purple_hash_digest_to_str(hash, digest, sizeof(digest));
if(!ret) {
purple_debug_info("cipher-test", "failed\n");
@@ -97,11 +99,11 @@ cipher_test_md5(void) {
md5_tests[i].answer);
}
- purple_cipher_context_reset(context, NULL);
+ purple_hash_reset(hash);
i++;
}
- purple_cipher_context_destroy(context);
+ g_object_unref(hash);
purple_debug_info("cipher-test", "md5 tests completed\n\n");
}
@@ -119,14 +121,13 @@ struct test sha1_tests[5] = {
static void
cipher_test_sha1(void) {
- PurpleCipher *cipher;
- PurpleCipherContext *context;
+ PurpleHash *hash;
gchar digest[41];
gint i = 0;
gboolean ret;
- cipher = purple_ciphers_find_cipher("sha1");
- if(!cipher) {
+ hash = purple_sha1_hash_new();
+ if(!hash) {
purple_debug_info("cipher-test",
"could not find sha1 cipher, not testing\n");
return;
@@ -134,8 +135,6 @@ cipher_test_sha1(void) {
purple_debug_info("cipher-test", "Running sha1 tests\n");
- context = purple_cipher_context_new(cipher, NULL);
-
while(sha1_tests[i].answer) {
purple_debug_info("cipher-test", "Test %02d:\n", i);
purple_debug_info("cipher-test", "Testing '%s'\n",
@@ -143,7 +142,7 @@ cipher_test_sha1(void) {
sha1_tests[i].question : "'a'x1000, 1000 times");
if(sha1_tests[i].question) {
- purple_cipher_context_append(context, (guchar *)sha1_tests[i].question,
+ purple_hash_append(hash, (guchar *)sha1_tests[i].question,
strlen(sha1_tests[i].question));
} else {
gint j;
@@ -152,11 +151,10 @@ cipher_test_sha1(void) {
memset(buff, 'a', 1000);
for(j = 0; j < 1000; j++)
- purple_cipher_context_append(context, buff, 1000);
+ purple_hash_append(hash, buff, 1000);
}
- ret = purple_cipher_context_digest_to_str(context, sizeof(digest),
- digest, NULL);
+ ret = purple_hash_digest_to_str(hash, digest, sizeof(digest));
if(!ret) {
purple_debug_info("cipher-test", "failed\n");
@@ -166,11 +164,11 @@ cipher_test_sha1(void) {
sha1_tests[i].answer);
}
- purple_cipher_context_reset(context, NULL);
+ purple_hash_reset(hash);
i++;
}
- purple_cipher_context_destroy(context);
+ g_object_unref(hash);
purple_debug_info("cipher-test", "sha1 tests completed\n\n");
}
@@ -194,7 +192,7 @@ cipher_test_digest(void)
purple_debug_info("cipher-test", "Running HTTP Digest tests\n");
- session_key = purple_cipher_http_digest_calculate_session_key(
+ session_key = purple_http_digest_calculate_session_key(
algorithm, username, realm, password,
nonce, client_nonce);
@@ -210,7 +208,7 @@ cipher_test_digest(void)
purple_debug_info("cipher-test", "\tsession_key: Got: %s\n", session_key);
purple_debug_info("cipher-test", "\tsession_key: Wanted: %s\n", "939e7578ed9e3c518a452acee763bce9");
- response = purple_cipher_http_digest_calculate_response(
+ response = purple_http_digest_calculate_response(
algorithm, method, digest_uri, qop, entity,
nonce, nonce_count, client_nonce, session_key);
@@ -233,6 +231,348 @@ cipher_test_digest(void)
}
/**************************************************************************
+ * PBKDF2 stuff
+ **************************************************************************/
+
+#ifdef HAVE_NSS
+# include <nss.h>
+# include <secmod.h>
+# include <pk11func.h>
+# include <prerror.h>
+# include <secerr.h>
+#endif
+
+typedef struct {
+ const gchar *hash;
+ const guint iter_count;
+ const gchar *passphrase;
+ const gchar *salt;
+ const guint out_len;
+ const gchar *answer;
+} pbkdf2_test;
+
+pbkdf2_test pbkdf2_tests[] = {
+ { "sha256", 1, "password", "salt", 32, "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b"},
+ { "sha1", 1, "password", "salt", 32, "0c60c80f961f0e71f3a9b524af6012062fe037a6e0f0eb94fe8fc46bdc637164"},
+ { "sha1", 1000, "ala ma kota", "", 16, "924dba137b5bcf6d0de84998f3d8e1f9"},
+ { "sha1", 1, "", "", 32, "1e437a1c79d75be61e91141dae20affc4892cc99abcc3fe753887bccc8920176"},
+ { "sha256", 100, "some password", "and salt", 1, "c7"},
+ { "sha1", 10000, "pretty long password W Szczebrzeszynie chrzaszcz brzmi w trzcinie i Szczebrzeszyn z tego slynie", "Grzegorz Brzeczyszczykiewicz", 32, "8cb0cb164f2554733ae02f5751b0e84a88fb385446e85a3991bdcdf1ea11795c"},
+ { NULL, 0, NULL, NULL, 0, NULL}
+};
+
+#ifdef HAVE_NSS
+
+static gchar*
+cipher_pbkdf2_nss_sha1(const gchar *passphrase, const gchar *salt,
+ guint iter_count, guint out_len)
+{
+ PK11SlotInfo *slot;
+ SECAlgorithmID *algorithm = NULL;
+ PK11SymKey *symkey = NULL;
+ const SECItem *symkey_data = NULL;
+ SECItem salt_item, passphrase_item;
+ guchar *passphrase_buff, *salt_buff;
+ gchar *ret;
+
+ g_return_val_if_fail(passphrase != NULL, NULL);
+ g_return_val_if_fail(iter_count > 0, NULL);
+ g_return_val_if_fail(out_len > 0, NULL);
+
+ NSS_NoDB_Init(NULL);
+
+ slot = PK11_GetBestSlot(PK11_AlgtagToMechanism(SEC_OID_PKCS5_PBKDF2),
+ NULL);
+ if (slot == NULL) {
+ purple_debug_error("cipher-test", "NSS: couldn't get slot: "
+ "%d\n", PR_GetError());
+ return NULL;
+ }
+
+ salt_buff = (guchar*)g_strdup(salt ? salt : "");
+ salt_item.type = siBuffer;
+ salt_item.data = salt_buff;
+ salt_item.len = salt ? strlen(salt) : 0;
+
+ algorithm = PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2,
+ SEC_OID_AES_256_CBC, SEC_OID_HMAC_SHA1, out_len, iter_count,
+ &salt_item);
+ if (algorithm == NULL) {
+ purple_debug_error("cipher-test", "NSS: couldn't create "
+ "algorithm ID: %d\n", PR_GetError());
+ PK11_FreeSlot(slot);
+ g_free(salt_buff);
+ return NULL;
+ }
+
+ passphrase_buff = (guchar*)g_strdup(passphrase);
+ passphrase_item.type = siBuffer;
+ passphrase_item.data = passphrase_buff;
+ passphrase_item.len = strlen(passphrase);
+
+ symkey = PK11_PBEKeyGen(slot, algorithm, &passphrase_item, PR_FALSE,
+ NULL);
+ if (symkey == NULL) {
+ purple_debug_error("cipher-test", "NSS: Couldn't generate key: "
+ "%d\n", PR_GetError());
+ SECOID_DestroyAlgorithmID(algorithm, PR_TRUE);
+ PK11_FreeSlot(slot);
+ g_free(passphrase_buff);
+ g_free(salt_buff);
+ return NULL;
+ }
+
+ if (PK11_ExtractKeyValue(symkey) == SECSuccess)
+ symkey_data = PK11_GetKeyData(symkey);
+
+ if (symkey_data == NULL || symkey_data->data == NULL) {
+ purple_debug_error("cipher-test", "NSS: Couldn't extract key "
+ "value: %d\n", PR_GetError());
+ PK11_FreeSymKey(symkey);
+ SECOID_DestroyAlgorithmID(algorithm, PR_TRUE);
+ PK11_FreeSlot(slot);
+ g_free(passphrase_buff);
+ g_free(salt_buff);
+ return NULL;
+ }
+
+ if (symkey_data->len != out_len) {
+ purple_debug_error("cipher-test", "NSS: Invalid key length: %d "
+ "(should be %d)\n", symkey_data->len, out_len);
+ PK11_FreeSymKey(symkey);
+ SECOID_DestroyAlgorithmID(algorithm, PR_TRUE);
+ PK11_FreeSlot(slot);
+ g_free(passphrase_buff);
+ g_free(salt_buff);
+ return NULL;
+ }
+
+ ret = purple_base16_encode(symkey_data->data, symkey_data->len);
+
+ PK11_FreeSymKey(symkey);
+ SECOID_DestroyAlgorithmID(algorithm, PR_TRUE);
+ PK11_FreeSlot(slot);
+ g_free(passphrase_buff);
+ g_free(salt_buff);
+ return ret;
+}
+
+#endif /* HAVE_NSS */
+
+static void
+cipher_test_pbkdf2(void)
+{
+ PurpleCipher *cipher;
+ PurpleHash *hash;
+ int i = 0;
+ gboolean fail = FALSE;
+
+ purple_debug_info("cipher-test", "Running PBKDF2 tests\n");
+
+ while (!fail && pbkdf2_tests[i].answer) {
+ pbkdf2_test *test = &pbkdf2_tests[i];
+ gchar digest[2 * 32 + 1 + 10];
+ gchar *digest_nss = NULL;
+ gboolean ret, skip_nss = FALSE;
+
+ i++;
+
+ purple_debug_info("cipher-test", "Test %02d:\n", i);
+ purple_debug_info("cipher-test",
+ "\tTesting '%s' with salt:'%s' hash:%s iter_count:%d \n",
+ test->passphrase, test->salt, test->hash,
+ test->iter_count);
+
+ if (!strcmp(test->hash, "sha1"))
+ hash = purple_sha1_hash_new();
+ else if (!strcmp(test->hash, "sha256"))
+ hash = purple_sha256_hash_new();
+ else
+ hash = NULL;
+
+ cipher = purple_pbkdf2_cipher_new(hash);
+
+ g_object_set(G_OBJECT(cipher), "iter_count", GUINT_TO_POINTER(test->iter_count), NULL);
+ g_object_set(G_OBJECT(cipher), "out_len", GUINT_TO_POINTER(test->out_len), NULL);
+ purple_cipher_set_salt(cipher, (const guchar*)test->salt, test->salt ? strlen(test->salt): 0);
+ purple_cipher_set_key(cipher, (const guchar*)test->passphrase, strlen(test->passphrase));
+
+ ret = purple_cipher_digest_to_str(cipher, digest, sizeof(digest));
+ purple_cipher_reset(cipher);
+
+ if (!ret) {
+ purple_debug_info("cipher-test", "\tfailed\n");
+ fail = TRUE;
+ g_object_unref(cipher);
+ g_object_unref(hash);
+ continue;
+ }
+
+ if (g_strcmp0(test->hash, "sha1") != 0)
+ skip_nss = TRUE;
+ if (test->out_len != 16 && test->out_len != 32)
+ skip_nss = TRUE;
+
+#ifdef HAVE_NSS
+ if (!skip_nss) {
+ digest_nss = cipher_pbkdf2_nss_sha1(test->passphrase,
+ test->salt, test->iter_count, test->out_len);
+ }
+#else
+ skip_nss = TRUE;
+#endif
+
+ purple_debug_info("cipher-test", "\tGot: %s\n", digest);
+ if (digest_nss)
+ purple_debug_info("cipher-test", "\tGot from NSS: %s\n", digest_nss);
+ purple_debug_info("cipher-test", "\tWanted: %s\n", test->answer);
+
+ if (g_strcmp0(digest, test->answer) == 0 &&
+ (skip_nss || g_strcmp0(digest, digest_nss) == 0)) {
+ purple_debug_info("cipher-test", "\tTest OK\n");
+ }
+ else {
+ purple_debug_info("cipher-test", "\twrong answer\n");
+ fail = TRUE;
+ }
+
+ g_object_unref(cipher);
+ g_object_unref(hash);
+ }
+
+ if (fail)
+ purple_debug_info("cipher-test", "PBKDF2 tests FAILED\n\n");
+ else
+ purple_debug_info("cipher-test", "PBKDF2 tests completed successfully\n\n");
+}
+
+/**************************************************************************
+ * AES stuff
+ **************************************************************************/
+
+typedef struct {
+ const gchar *iv;
+ const gchar *key;
+ const gchar *plaintext;
+ const gchar *cipher;
+} aes_test;
+
+aes_test aes_tests[] = {
+ { NULL, "000102030405060708090a0b0c0d0e0f", "plaintext", "152e5b950e5f28fafadee9e96fcc59c9" },
+ { NULL, "000102030405060708090a0b0c0d0e0f", NULL, "954f64f2e4e86e9eee82d20216684899" },
+ { "01010101010101010101010101010101", "000102030405060708090a0b0c0d0e0f", NULL, "35d14e6d3e3a279cf01e343e34e7ded3" },
+ { "01010101010101010101010101010101", "000102030405060708090a0b0c0d0e0f", "plaintext", "19d1996e8c098cf3c94bba5dcf5bc57e" },
+ { "01010101010101010101010101010101", "000102030405060708090a0b0c0d0e0f1011121314151617", "plaintext", "e8fba0deae94f63fe72ae9b8ef128aed" },
+ { "01010101010101010101010101010101", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "plaintext", "e2dc50f6c60b33ac3b5953b6503cb684" },
+ { "01010101010101010101010101010101", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "W Szczebrzeszynie chrzaszcz brzmi w trzcinie i Szczebrzeszyn z tego slynie", "8fcc068964e3505f0c2fac61c24592e5c8a9582d3a3264217cf605e9fd1cb056e679e159c4ac3110e8ce6c76c6630d42658c566ba3750c0e6da385b3a9baaa8b3a735b4c1ecaacf58037c8c281e523d2" },
+ { NULL, NULL, NULL, NULL }
+};
+
+static void
+cipher_test_aes(void)
+{
+ PurpleCipher *cipher;
+ int i = 0;
+ gboolean fail = FALSE;
+
+ purple_debug_info("cipher-test", "Running AES tests\n");
+
+ cipher = purple_aes_cipher_new();
+ if (cipher == NULL) {
+ purple_debug_error("cipher-test", "AES cipher not found\n");
+ fail = TRUE;
+ }
+
+ while (!fail && aes_tests[i].cipher) {
+ aes_test *test = &aes_tests[i];
+ gsize key_size;
+ guchar *key;
+ guchar cipher_s[1024], decipher_s[1024];
+ ssize_t cipher_len, decipher_len;
+ gchar *cipher_b16, *deciphered;
+
+ purple_debug_info("cipher-test", "Test %02d:\n", i);
+ purple_debug_info("cipher-test", "\tTesting '%s' (%" G_GSIZE_FORMAT "bit) \n",
+ test->plaintext ? test->plaintext : "(null)",
+ strlen(test->key) * 8 / 2);
+
+ i++;
+
+ purple_cipher_reset(cipher);
+
+ if (test->iv) {
+ gsize iv_size;
+ guchar *iv = purple_base16_decode(test->iv, &iv_size);
+ g_assert(iv != NULL);
+ purple_cipher_set_iv(cipher, iv, iv_size);
+ g_free(iv);
+ }
+
+ key = purple_base16_decode(test->key, &key_size);
+ g_assert(key != NULL);
+ purple_cipher_set_key(cipher, key, key_size);
+ g_free(key);
+
+ if (purple_cipher_get_key_size(cipher) != key_size) {
+ purple_debug_info("cipher-test", "\tinvalid key size\n");
+ fail = TRUE;
+ continue;
+ }
+
+ cipher_len = purple_cipher_encrypt(cipher,
+ (const guchar*)(test->plaintext ? test->plaintext : ""),
+ test->plaintext ? (strlen(test->plaintext) + 1) : 0,
+ cipher_s, sizeof(cipher_s));
+ if (cipher_len < 0) {
+ purple_debug_info("cipher-test", "\tencryption failed\n");
+ fail = TRUE;
+ continue;
+ }
+
+ cipher_b16 = purple_base16_encode(cipher_s, cipher_len);
+
+ purple_debug_info("cipher-test", "\tGot: %s\n", cipher_b16);
+ purple_debug_info("cipher-test", "\tWanted: %s\n", test->cipher);
+
+ if (g_strcmp0(cipher_b16, test->cipher) != 0) {
+ purple_debug_info("cipher-test",
+ "\tencrypted data doesn't match\n");
+ g_free(cipher_b16);
+ fail = TRUE;
+ continue;
+ }
+ g_free(cipher_b16);
+
+ decipher_len = purple_cipher_decrypt(cipher,
+ cipher_s, cipher_len, decipher_s, sizeof(decipher_s));
+ if (decipher_len < 0) {
+ purple_debug_info("cipher-test", "\tdecryption failed\n");
+ fail = TRUE;
+ continue;
+ }
+
+ deciphered = (decipher_len > 0) ? (gchar*)decipher_s : NULL;
+
+ if (g_strcmp0(deciphered, test->plaintext) != 0) {
+ purple_debug_info("cipher-test",
+ "\tdecrypted data doesn't match\n");
+ fail = TRUE;
+ continue;
+ }
+
+ purple_debug_info("cipher-test", "\tTest OK\n");
+ }
+
+ if (cipher != NULL)
+ g_object_unref(cipher);
+
+ if (fail)
+ purple_debug_info("cipher-test", "AES tests FAILED\n\n");
+ else
+ purple_debug_info("cipher-test", "AES tests completed successfully\n\n");
+}
+
+/**************************************************************************
* Plugin stuff
**************************************************************************/
static gboolean
@@ -240,6 +580,8 @@ plugin_load(PurplePlugin *plugin) {
cipher_test_md5();
cipher_test_sha1();
cipher_test_digest();
+ cipher_test_pbkdf2();
+ cipher_test_aes();
return TRUE;
}
diff --git a/libpurple/plugins/codeinline.c b/libpurple/plugins/codeinline.c
index b8c6beedd4..b4cf22fdb3 100644
--- a/libpurple/plugins/codeinline.c
+++ b/libpurple/plugins/codeinline.c
@@ -28,20 +28,37 @@
PurplePlugin *plugin_handle = NULL;
-static gboolean outgoing_msg_cb(PurpleAccount *account, const char *who, char **message,
- PurpleConversation *conv, PurpleMessageFlags flags, gpointer null)
+static char *
+outgoing_msg_common(const char *message)
{
char *m;
- char **ms = g_strsplit(*message, "<u>", -1);
+ char **ms = g_strsplit(message, "<u>", -1);
m = g_strjoinv("<font face=\"monospace\" color=\"#00b025\">", ms);
g_strfreev(ms);
ms = g_strsplit(m, "</u>", -1);
g_free(m);
- m = g_strjoinv("</font>", ms);
- g_free(*message);
- *message = m;
- return FALSE;
+ return g_strjoinv("</font>", ms);
+}
+
+static gboolean outgoing_msg_cb1(PurpleAccount *account, const char *who, char **message,
+ PurpleConversation *conv, PurpleMessageFlags flags, gpointer null)
+{
+ char *m;
+
+ m = outgoing_msg_common(*message);
+ g_free(*message);
+ *message = m;
+
+ return FALSE;
+}
+
+static void
+outgoing_msg_cb2(PurpleAccount *account, PurpleMessage *msg,
+ PurpleConversation *conv, PurpleMessageFlags flags, gpointer null)
+{
+ purple_message_set_contents(msg,
+ outgoing_msg_common(purple_message_get_contents(msg)));
}
static gboolean
@@ -50,9 +67,9 @@ plugin_load(PurplePlugin *plugin)
void *handle = purple_conversations_get_handle();
plugin_handle = plugin;
purple_signal_connect(handle, "writing-im-msg", plugin,
- PURPLE_CALLBACK(outgoing_msg_cb), NULL);
+ PURPLE_CALLBACK(outgoing_msg_cb1), NULL);
purple_signal_connect(handle, "sending-im-msg", plugin,
- PURPLE_CALLBACK(outgoing_msg_cb), NULL);
+ PURPLE_CALLBACK(outgoing_msg_cb2), NULL);
return TRUE;
}
diff --git a/libpurple/plugins/dbus-example.c b/libpurple/plugins/dbus-example.c
index cf9f255382..fa7ee3b308 100644
--- a/libpurple/plugins/dbus-example.c
+++ b/libpurple/plugins/dbus-example.c
@@ -35,12 +35,13 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+/* When writing a third-party plugin, do not include libpurple's internal.h
+ * included below. This file is for internal libpurple use only. We're including
+ * it here for our own convenience. */
#include "internal.h"
-#include "blist.h"
-#include "notify.h"
-#include "plugin.h"
-#include "version.h"
+/* This file defines PURPLE_PLUGINS and includes all the libpurple headers */
+#include <purple.h>
#include <stdio.h>
#include <stdlib.h>
@@ -78,12 +79,12 @@ DBUS_EXPORT const char *dbus_example_get_buddy_name(PurpleBuddy *buddy);
static PurpleText hello;
/* Here come the definitions of the four exported functions. */
-PurpleText* dbus_example_get_hello_object(void)
+DBUS_EXPORT PurpleText* dbus_example_get_hello_object(void)
{
return &hello;
}
-void dbus_example_set_text(PurpleText *obj, const char *text)
+DBUS_EXPORT void dbus_example_set_text(PurpleText *obj, const char *text)
{
if (obj != NULL) {
g_free(obj->text);
@@ -91,7 +92,7 @@ void dbus_example_set_text(PurpleText *obj, const char *text)
}
}
-const char *dbus_example_get_text(PurpleText *obj)
+DBUS_EXPORT const char *dbus_example_get_text(PurpleText *obj)
{
if (obj != NULL)
return obj->text;
@@ -99,7 +100,7 @@ const char *dbus_example_get_text(PurpleText *obj)
return NULL;
}
-const char *dbus_example_get_buddy_name(PurpleBuddy *buddy)
+DBUS_EXPORT const char *dbus_example_get_buddy_name(PurpleBuddy *buddy)
{
return purple_buddy_get_name(buddy);
}
diff --git a/libpurple/plugins/debug_example.c b/libpurple/plugins/debug_example.c
index 1a9e409f78..9e5f6680e2 100644
--- a/libpurple/plugins/debug_example.c
+++ b/libpurple/plugins/debug_example.c
@@ -20,36 +20,13 @@
*
*/
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-/* We're including glib.h again for the gboolean type. */
-#include <glib.h>
-
-/* This will prevent compiler errors in some instances and is better explained in the
- * how-to documents on the wiki */
-#ifndef G_GNUC_NULL_TERMINATED
-# if __GNUC__ >= 4
-# define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
-# else
-# define G_GNUC_NULL_TERMINATED
-# endif
-#endif
-
-/* This is the required definition of PURPLE_PLUGINS as required for a plugin,
- * but we protect it with an #ifndef because config.h may define it for us
- * already and this would cause an unneeded compiler warning. */
-#ifndef PURPLE_PLUGINS
-# define PURPLE_PLUGINS
-#endif
-
-/* Here we're including the necessary libpurple headers for this plugin. Note
- * that we're including them in alphabetical order. This isn't necessary but
- * we do this throughout our source for consistency. */
-#include "debug.h"
-#include "plugin.h"
-#include "version.h"
+/* When writing a third-party plugin, do not include libpurple's internal.h
+ * included below. This file is for internal libpurple use only. We're including
+ * it here for our own convenience. */
+#include "internal.h"
+
+/* This file defines PURPLE_PLUGINS and includes all the libpurple headers */
+#include <purple.h>
/* It's more convenient to type PLUGIN_ID all the time than it is to type
* "core-debugexample", so define this convenience macro. */
@@ -113,7 +90,7 @@ static PurplePluginInfo info = {
"Debug API Example", /* summary */
"Debug API Example", /* description */
PLUGIN_AUTHOR, /* author */
- "http://pidgin.im", /* homepage */
+ "https://pidgin.im", /* homepage */
plugin_load, /* load */
NULL, /* unload */
diff --git a/libpurple/plugins/filectl.c b/libpurple/plugins/filectl.c
index 383ffed5a4..a8bebcd014 100644
--- a/libpurple/plugins/filectl.c
+++ b/libpurple/plugins/filectl.c
@@ -39,7 +39,7 @@ char *getarg(char *, int, int);
void
run_commands()
{
- struct stat finfo;
+ GStatBuf finfo;
char filename[MAXPATHLEN];
char buffer[1024];
char *command, *arg1, *arg2;
@@ -99,7 +99,7 @@ run_commands()
{
/*
purple_conversation_write(conv, arg2, WFLAG_SEND, NULL, time(NULL), -1);
- serv_send_im(conv->gc, arg1, arg2, 0);
+ purple_serv_send_im(conv->gc, arg1, arg2, 0);
*/
}
@@ -143,7 +143,7 @@ void
init_file()
{
/* most of this was taken from Bash v2.04 by the FSF */
- struct stat finfo;
+ GStatBuf finfo;
char filename[MAXPATHLEN];
snprintf(filename, MAXPATHLEN, "%s" G_DIR_SEPARATOR_S "control", purple_user_dir());
@@ -159,7 +159,7 @@ gboolean
check_file()
{
/* most of this was taken from Bash v2.04 by the FSF */
- struct stat finfo;
+ GStatBuf finfo;
char filename[MAXPATHLEN];
snprintf(filename, MAXPATHLEN, "%s" G_DIR_SEPARATOR_S "control", purple_user_dir());
diff --git a/libpurple/plugins/fortuneprofile.pl b/libpurple/plugins/fortuneprofile.pl
index ca9531c74e..ac98c67e9e 100644
--- a/libpurple/plugins/fortuneprofile.pl
+++ b/libpurple/plugins/fortuneprofile.pl
@@ -51,7 +51,7 @@ use Gaim;
summary => "Sets your AIM profile to a fortune (with a header and footer of your choice).",
description => "Sets your AIM profile to a fortune (with a header and footer of your choice).",
author => "Sean Egan <seanegan\@gmail.com>",
- url => "http://pidgin.im/",
+ url => "https://pidgin.im/",
load => "plugin_load"
);
diff --git a/libpurple/plugins/helloworld.c b/libpurple/plugins/helloworld.c
index 7bb2b1105b..5495750f80 100644
--- a/libpurple/plugins/helloworld.c
+++ b/libpurple/plugins/helloworld.c
@@ -21,36 +21,13 @@
*
*/
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-/* config.h may define PURPLE_PLUGINS; protect the definition here so that we
- * don't get complaints about redefinition when it's not necessary. */
-#ifndef PURPLE_PLUGINS
-# define PURPLE_PLUGINS
-#endif
-
-#include <glib.h>
-
-/* This will prevent compiler errors in some instances and is better explained in the
- * how-to documents on the wiki */
-#ifndef G_GNUC_NULL_TERMINATED
-# if __GNUC__ >= 4
-# define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
-# else
-# define G_GNUC_NULL_TERMINATED
-# endif
-#endif
-
-#include <notify.h>
-#include <plugin.h>
-#include <version.h>
-
-/* we're adding this here and assigning it in plugin_load because we need
- * a valid plugin handle for our call to purple_notify_message() in the
- * plugin_action_test_cb() callback function */
-PurplePlugin *helloworld_plugin = NULL;
+/* When writing a third-party plugin, do not include libpurple's internal.h
+ * included below. This file is for internal libpurple use only. We're including
+ * it here for our own convenience. */
+#include "internal.h"
+
+/* This file defines PURPLE_PLUGINS and includes all the libpurple headers */
+#include <purple.h>
/* This function is the callback for the plugin action we added. All we're
* doing here is displaying a message. When the user selects the plugin
@@ -58,9 +35,9 @@ PurplePlugin *helloworld_plugin = NULL;
static void
plugin_action_test_cb (PurplePluginAction * action)
{
- purple_notify_message (helloworld_plugin, PURPLE_NOTIFY_MSG_INFO,
+ purple_notify_message (action->plugin, PURPLE_NOTIFY_MSG_INFO,
"Plugin Actions Test", "This is a plugin actions test :)", NULL, NULL,
- NULL);
+ NULL, NULL);
}
/* we tell libpurple in the PurplePluginInfo struct to call this function to
@@ -93,9 +70,7 @@ plugin_load (PurplePlugin * plugin)
{
purple_notify_message (plugin, PURPLE_NOTIFY_MSG_INFO, "Hello World!",
"This is the Hello World! plugin :)", NULL, NULL,
- NULL);
-
- helloworld_plugin = plugin; /* assign this here so we have a valid handle later */
+ NULL, NULL);
return TRUE;
}
diff --git a/libpurple/plugins/idle.c b/libpurple/plugins/idle.c
index 5d6cf72131..1c71c1d592 100644
--- a/libpurple/plugins/idle.c
+++ b/libpurple/plugins/idle.c
@@ -28,6 +28,7 @@
#include "debug.h"
#include "notify.h"
#include "plugin.h"
+#include "presence.h"
#include "request.h"
#include "server.h"
#include "status.h"
@@ -151,7 +152,7 @@ idle_action(PurplePluginAction *action)
purple_request_field_account_set_show_all(field, FALSE);
purple_request_field_group_add_field(group, field);
- field = purple_request_field_int_new("mins", _("Minutes"), 10);
+ field = purple_request_field_int_new("mins", _("Minutes"), 10, 0, 9999);
purple_request_field_group_add_field(group, field);
request = purple_request_fields_new();
@@ -164,8 +165,7 @@ idle_action(PurplePluginAction *action)
request,
_("_Set"), G_CALLBACK(idle_action_ok),
_("_Cancel"), NULL,
- NULL, NULL, NULL,
- NULL);
+ NULL, NULL);
}
static void
@@ -177,7 +177,7 @@ unidle_action(PurplePluginAction *action)
if (idled_accts == NULL)
{
- purple_notify_info(NULL, NULL, _("None of your accounts are idle."), NULL);
+ purple_notify_info(NULL, NULL, _("None of your accounts are idle."), NULL, NULL);
return;
}
@@ -198,8 +198,7 @@ unidle_action(PurplePluginAction *action)
request,
_("_Unset"), G_CALLBACK(unidle_action_ok),
_("_Cancel"), NULL,
- NULL, NULL, NULL,
- NULL);
+ NULL, NULL);
}
static void
@@ -211,7 +210,7 @@ idle_all_action(PurplePluginAction *action)
group = purple_request_field_group_new(NULL);
- field = purple_request_field_int_new("mins", _("Minutes"), 10);
+ field = purple_request_field_int_new("mins", _("Minutes"), 10, 0, 9999);
purple_request_field_group_add_field(group, field);
request = purple_request_fields_new();
@@ -224,8 +223,7 @@ idle_all_action(PurplePluginAction *action)
request,
_("_Set"), G_CALLBACK(idle_all_action_ok),
_("_Cancel"), NULL,
- NULL, NULL, NULL,
- NULL);
+ NULL, NULL);
}
static void
diff --git a/libpurple/plugins/joinpart.c b/libpurple/plugins/joinpart.c
index 9969022d36..288628bf5f 100644
--- a/libpurple/plugins/joinpart.c
+++ b/libpurple/plugins/joinpart.c
@@ -79,22 +79,22 @@ static void joinpart_key_destroy(struct joinpart_key *key)
static gboolean should_hide_notice(PurpleConversation *conv, const char *name,
GHashTable *users)
{
- PurpleConvChat *chat;
- int threshold;
+ PurpleChatConversation *chat;
+ guint threshold;
struct joinpart_key key;
time_t *last_said;
g_return_val_if_fail(conv != NULL, FALSE);
- g_return_val_if_fail(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT, FALSE);
+ g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(conv), FALSE);
/* If the room is small, don't bother. */
- chat = PURPLE_CONV_CHAT(conv);
+ chat = PURPLE_CHAT_CONVERSATION(conv);
threshold = purple_prefs_get_int(THRESHOLD_PREF);
- if (g_list_length(purple_conv_chat_get_users(chat)) < threshold)
+ if (purple_chat_conversation_get_users_count(chat) < threshold)
return FALSE;
if (!purple_prefs_get_bool(HIDE_BUDDIES_PREF) &&
- purple_find_buddy(purple_conversation_get_account(conv), name))
+ purple_blist_find_buddy(purple_conversation_get_account(conv), name))
return FALSE;
/* Only show the notice if the user has spoken recently. */
@@ -111,14 +111,14 @@ static gboolean should_hide_notice(PurpleConversation *conv, const char *name,
return TRUE;
}
-static gboolean chat_buddy_leaving_cb(PurpleConversation *conv, const char *name,
+static gboolean chat_user_leaving_cb(PurpleConversation *conv, const char *name,
const char *reason, GHashTable *users)
{
return should_hide_notice(conv, name, users);
}
-static gboolean chat_buddy_joining_cb(PurpleConversation *conv, const char *name,
- PurpleConvChatBuddyFlags flags,
+static gboolean chat_user_joining_cb(PurpleConversation *conv, const char *name,
+ PurpleChatUserFlags flags,
GHashTable *users)
{
return should_hide_notice(conv, name, users);
@@ -186,10 +186,10 @@ static gboolean plugin_load(PurplePlugin *plugin)
g_free);
conv_handle = purple_conversations_get_handle();
- purple_signal_connect(conv_handle, "chat-buddy-joining", plugin,
- PURPLE_CALLBACK(chat_buddy_joining_cb), users);
- purple_signal_connect(conv_handle, "chat-buddy-leaving", plugin,
- PURPLE_CALLBACK(chat_buddy_leaving_cb), users);
+ purple_signal_connect(conv_handle, "chat-user-joining", plugin,
+ PURPLE_CALLBACK(chat_user_joining_cb), users);
+ purple_signal_connect(conv_handle, "chat-user-leaving", plugin,
+ PURPLE_CALLBACK(chat_user_leaving_cb), users);
purple_signal_connect(conv_handle, "received-chat-msg", plugin,
PURPLE_CALLBACK(received_chat_msg_cb), users);
@@ -252,8 +252,7 @@ get_plugin_pref_frame(PurplePlugin *plugin)
static PurplePluginUiInfo prefs_info = {
get_plugin_pref_frame,
- 0, /* page_num (reserved) */
- NULL, /* frame (reserved) */
+ NULL,
/* padding */
NULL,
diff --git a/libpurple/plugins/keyrings/Makefile.am b/libpurple/plugins/keyrings/Makefile.am
new file mode 100644
index 0000000000..59fe64aae6
--- /dev/null
+++ b/libpurple/plugins/keyrings/Makefile.am
@@ -0,0 +1,90 @@
+EXTRA_DIST = \
+ Makefile.mingw \
+ wincred.c
+CLEANFILES =
+
+plugindir = @PURPLE_PLUGINDIR@
+
+internalkeyring_la_CFLAGS = $(AM_CPPFLAGS)
+internalkeyring_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+internalkeyring_la_SOURCES = internalkeyring.c
+internalkeyring_la_LIBADD = @PURPLE_LIBS@
+
+if ENABLE_SECRETSERVICE
+
+secretservice_la_CFLAGS = $(AM_CPPFLAGS) $(SECRETSERVICE_CFLAGS)
+secretservice_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+secretservice_la_SOURCES = secretservice.c
+secretservice_la_LIBADD = @PURPLE_LIBS@ $(SECRETSERVICE_LIBS)
+
+endif
+
+if ENABLE_GNOMEKEYRING
+
+gnomekeyring_la_CFLAGS = $(AM_CPPFLAGS) $(GNOMEKEYRING_CFLAGS)
+gnomekeyring_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+gnomekeyring_la_SOURCES = gnomekeyring.c
+gnomekeyring_la_LIBADD = @PURPLE_LIBS@ $(GNOMEKEYRING_LIBS)
+
+endif
+
+if IS_WIN32
+
+wincred_la_CFLAGS = $(AM_CPPFLAGS)
+wincred_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+wincred_la_SOURCES = wincred.c
+wincred_la_LIBADD = @PURPLE_LIBS@
+
+endif
+
+if ENABLE_KWALLET
+
+kwallet_la_CXXFLAGS = $(KWALLET_CXXFLAGS) $(QT4_CFLAGS)
+kwallet_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+kwallet_la_SOURCES = kwallet.cpp
+kwallet_la_BUILTSOURCES = kwallet.moc
+kwallet_la_LIBADD = @PURPLE_LIBS@ $(KWALLET_LIBS) $(QT4_LIBS)
+
+kwallet.cpp: kwallet.moc
+
+kwallet.moc:
+ $(AM_V_GEN)$(MOC) $(kwallet_la_CXXFLAGS) -i $(srcdir)/kwallet.cpp -o $@
+
+CLEANFILES += kwallet.moc
+
+endif
+
+if PLUGINS
+
+plugin_LTLIBRARIES = \
+ internalkeyring.la
+
+if ENABLE_SECRETSERVICE
+plugin_LTLIBRARIES += \
+ secretservice.la
+endif
+
+if ENABLE_GNOMEKEYRING
+plugin_LTLIBRARIES += \
+ gnomekeyring.la
+endif
+
+if IS_WIN32
+plugin_LTLIBRARIES += \
+ wincred.la
+endif
+
+if ENABLE_KWALLET
+plugin_LTLIBRARIES += \
+ kwallet.la
+endif
+
+endif
+
+#XXX: that might be done better than adding DEBUG_CPPFLAGS to all objects (not only C++ ones)
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/libpurple \
+ -I$(top_builddir)/libpurple \
+ $(GLIB_CFLAGS) \
+ $(DEBUG_CPPFLAGS) \
+ $(PLUGIN_CFLAGS)
diff --git a/libpurple/plugins/keyrings/Makefile.mingw b/libpurple/plugins/keyrings/Makefile.mingw
new file mode 100644
index 0000000000..84257d722f
--- /dev/null
+++ b/libpurple/plugins/keyrings/Makefile.mingw
@@ -0,0 +1,82 @@
+#
+# Makefile.mingw
+#
+# Description: Makefile for keyring plugins.
+#
+
+PIDGIN_TREE_TOP := ../../..
+include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
+
+##
+## VARIABLE DEFINITIONS
+##
+TARGET_INTERNAL = internalkeyring
+TARGET_WINCRED = wincred
+
+##
+## INCLUDE PATHS
+##
+INCLUDE_PATHS += \
+ -I. \
+ -I$(GTK_TOP)/include \
+ -I$(GTK_TOP)/include/glib-2.0 \
+ -I$(GTK_TOP)/lib/glib-2.0/include \
+ -I$(PURPLE_TOP) \
+ -I$(PURPLE_TOP)/win32 \
+ -I$(PIDGIN_TREE_TOP)
+
+LIB_PATHS += \
+ -L$(GTK_TOP)/lib \
+ -L$(PURPLE_TOP)
+
+##
+## SOURCES, OBJECTS
+##
+C_SRC_INTERNAL = internalkeyring.c
+OBJECTS_INTERNAL = $(C_SRC_INTERNAL:%.c=%.o)
+
+C_SRC_WINCRED = wincred.c
+OBJECTS_WINCRED = $(C_SRC_WINCRED:%.c=%.o)
+
+##
+## LIBRARIES
+##
+LIBS = \
+ -lglib-2.0 \
+ -lgobject-2.0 \
+ -lws2_32 \
+ -lintl \
+ -lpurple
+
+include $(PIDGIN_COMMON_RULES)
+
+##
+## TARGET DEFINITIONS
+##
+.PHONY: all install clean
+
+all: $(TARGET_INTERNAL).dll $(TARGET_WINCRED).dll
+
+install: all $(PURPLE_INSTALL_PLUGINS_DIR) $(PURPLE_INSTALL_DIR)
+ cp $(TARGET_INTERNAL).dll $(PURPLE_INSTALL_PLUGINS_DIR)
+ cp $(TARGET_WINCRED).dll $(PURPLE_INSTALL_PLUGINS_DIR)
+
+$(OBJECTS_INTERNAL): $(PURPLE_CONFIG_H)
+
+##
+## BUILD DLL
+##
+$(TARGET_INTERNAL).dll: $(PURPLE_DLL) $(OBJECTS_INTERNAL)
+ $(CC) -shared $(OBJECTS_INTERNAL) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET_INTERNAL).dll
+
+$(TARGET_WINCRED).dll: $(PURPLE_DLL) $(OBJECTS_WINCRED)
+ $(CC) -shared $(OBJECTS_WINCRED) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET_WINCRED).dll
+
+##
+## CLEAN RULES
+##
+clean:
+ rm -f $(OBJECTS_INTERNAL) $(TARGET_INTERNAL).dll
+ rm -f $(OBJECTS_WINCRED) $(TARGET_WINCRED).dll
+
+include $(PIDGIN_COMMON_TARGETS)
diff --git a/libpurple/plugins/keyrings/gnomekeyring.c b/libpurple/plugins/keyrings/gnomekeyring.c
new file mode 100644
index 0000000000..92189ace34
--- /dev/null
+++ b/libpurple/plugins/keyrings/gnomekeyring.c
@@ -0,0 +1,475 @@
+/**
+ * @file gnomekeyring.c Gnome keyring 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 "debug.h"
+#include "glibcompat.h"
+#include "keyring.h"
+#include "plugin.h"
+#include "version.h"
+
+#include <gnome-keyring.h>
+#include <gnome-keyring-memory.h>
+
+#define GNOMEKEYRING_NAME N_("GNOME Keyring")
+#define GNOMEKEYRING_DESCRIPTION N_("This plugin will store passwords in " \
+ "GNOME Keyring.")
+#define GNOMEKEYRING_AUTHOR "Tomek Wasilczyk <twasilczyk@pidgin.im>"
+#define GNOMEKEYRING_ID "keyring-gnomekeyring"
+
+static PurpleKeyring *keyring_handler = NULL;
+static GList *request_queue = NULL;
+static gpointer current_request = NULL;
+
+typedef struct
+{
+ enum
+ {
+ GNOMEKEYRING_REQUEST_READ,
+ GNOMEKEYRING_REQUEST_SAVE
+ } type;
+ PurpleAccount *account;
+ gchar *password;
+ union
+ {
+ PurpleKeyringReadCallback read;
+ PurpleKeyringSaveCallback save;
+ } cb;
+ gpointer cb_data;
+ gboolean handled;
+} gnomekeyring_request;
+
+static void gnomekeyring_cancel_queue(void);
+static void gnomekeyring_process_queue(void);
+
+static void gnomekeyring_request_free(gnomekeyring_request *req)
+{
+ if (req->password != NULL) {
+ memset(req->password, 0, strlen(req->password));
+ gnome_keyring_memory_free(req->password);
+ }
+ g_free(req);
+}
+
+static void
+gnomekeyring_enqueue(gnomekeyring_request *req)
+{
+ request_queue = g_list_append(request_queue, req);
+ gnomekeyring_process_queue();
+}
+
+static void
+gnomekeyring_read_cb(GnomeKeyringResult result, const char *password,
+ gpointer _req)
+{
+ gnomekeyring_request *req = _req;
+ PurpleAccount *account;
+ GError *error = NULL;
+
+ g_return_if_fail(req != NULL);
+
+ current_request = NULL;
+ account = req->account;
+
+ if (result == GNOME_KEYRING_RESULT_OK) {
+ error = NULL;
+ } else if (result == GNOME_KEYRING_RESULT_NO_MATCH) {
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_NOPASSWORD,
+ _("No password found for account."));
+ } else if (result == GNOME_KEYRING_RESULT_DENIED ||
+ result == GNOME_KEYRING_RESULT_CANCELLED)
+ {
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_ACCESSDENIED,
+ _("Access denied."));
+ gnomekeyring_cancel_queue();
+ } else if (result == GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON ||
+ result == GNOME_KEYRING_RESULT_IO_ERROR)
+ {
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_BACKENDFAIL,
+ _("Communication with GNOME Keyring failed."));
+ } else {
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_BACKENDFAIL,
+ _("Unknown error (code: %d)."), result);
+ }
+
+ if (error == NULL && password == NULL) {
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_BACKENDFAIL,
+ _("Unknown error (password empty)."));
+ }
+
+ if (error == NULL) {
+ purple_debug_misc("keyring-gnome",
+ "Got password for account %s (%s).\n",
+ purple_account_get_username(account),
+ purple_account_get_protocol_id(account));
+ } else if (result == GNOME_KEYRING_RESULT_NO_MATCH) {
+ if (purple_debug_is_verbose()) {
+ purple_debug_info("keyring-gnome",
+ "Password for account %s (%s) isn't stored.\n",
+ purple_account_get_username(account),
+ purple_account_get_protocol_id(account));
+ }
+ } else {
+ password = NULL;
+ purple_debug_warning("keyring-gnome", "Failed to read "
+ "password for account %s (%s), code: %d.\n",
+ purple_account_get_username(account),
+ purple_account_get_protocol_id(account),
+ result);
+ }
+
+ if (req->cb.read != NULL)
+ req->cb.read(account, password, error, req->cb_data);
+ req->handled = TRUE;
+
+ if (error)
+ g_error_free(error);
+
+ gnomekeyring_process_queue();
+}
+
+static void
+gnomekeyring_save_cb(GnomeKeyringResult result, gpointer _req)
+{
+ gnomekeyring_request *req = _req;
+ PurpleAccount *account;
+ GError *error = NULL;
+ gboolean already_removed = FALSE;
+
+ g_return_if_fail(req != NULL);
+
+ current_request = NULL;
+ account = req->account;
+
+ if (result == GNOME_KEYRING_RESULT_OK) {
+ error = NULL;
+ } else if (result == GNOME_KEYRING_RESULT_NO_MATCH &&
+ req->password == NULL)
+ {
+ error = NULL;
+ already_removed = TRUE;
+ } else if (result == GNOME_KEYRING_RESULT_DENIED ||
+ result == GNOME_KEYRING_RESULT_CANCELLED)
+ {
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_ACCESSDENIED,
+ _("Access denied."));
+ gnomekeyring_cancel_queue();
+ } else if (result == GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON ||
+ result == GNOME_KEYRING_RESULT_IO_ERROR)
+ {
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_BACKENDFAIL,
+ _("Communication with GNOME Keyring failed."));
+ } else {
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_BACKENDFAIL,
+ _("Unknown error (code: %d)."), result);
+ }
+
+ if (already_removed) {
+ /* no operation */
+ } else if (error == NULL) {
+ purple_debug_misc("keyring-gnome",
+ "Password %s for account %s (%s).\n",
+ req->password ? "saved" : "removed",
+ purple_account_get_username(account),
+ purple_account_get_protocol_id(account));
+ } else {
+ purple_debug_warning("keyring-gnome", "Failed updating "
+ "password for account %s (%s), code: %d.\n",
+ purple_account_get_username(account),
+ purple_account_get_protocol_id(account),
+ result);
+ }
+
+ if (req->cb.save != NULL)
+ req->cb.save(account, error, req->cb_data);
+ req->handled = TRUE;
+
+ if (error)
+ g_error_free(error);
+
+ gnomekeyring_process_queue();
+}
+
+static void
+gnomekeyring_request_cancel(gpointer _req)
+{
+ gnomekeyring_request *req = _req;
+ PurpleAccount *account;
+ GError *error;
+
+ g_return_if_fail(req != NULL);
+
+ if (req->handled) {
+ gnomekeyring_request_free(req);
+ return;
+ }
+
+ purple_debug_warning("keyring-gnome",
+ "operation cancelled (%d %s:%s)\n", req->type,
+ purple_account_get_protocol_id(req->account),
+ purple_account_get_username(req->account));
+
+ account = req->account;
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_CANCELLED,
+ _("Operation cancelled."));
+ if (req->type == GNOMEKEYRING_REQUEST_READ && req->cb.read)
+ req->cb.read(account, NULL, error, req->cb_data);
+ if (req->type == GNOMEKEYRING_REQUEST_SAVE && req->cb.save)
+ req->cb.save(account, error, req->cb_data);
+ g_error_free(error);
+
+ gnomekeyring_request_free(req);
+ gnomekeyring_process_queue();
+}
+
+static void
+gnomekeyring_cancel_queue(void)
+{
+ GList *cancel_list = request_queue;
+
+ if (request_queue == NULL)
+ return;
+
+ purple_debug_info("gnome-keyring", "cancelling all pending requests\n");
+ request_queue = NULL;
+
+ g_list_free_full(cancel_list, gnomekeyring_request_cancel);
+}
+
+static void
+gnomekeyring_process_queue(void)
+{
+ gnomekeyring_request *req;
+ PurpleAccount *account;
+ GList *first;
+
+ if (request_queue == NULL)
+ return;
+
+ if (current_request) {
+ if (purple_debug_is_verbose())
+ purple_debug_misc("keyring-gnome", "busy...\n");
+ return;
+ }
+
+ first = g_list_first(request_queue);
+ req = first->data;
+ request_queue = g_list_delete_link(request_queue, first);
+ account = req->account;
+
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("keyring-gnome",
+ "%s password for account %s (%s)\n",
+ req->type == GNOMEKEYRING_REQUEST_READ ? "reading" :
+ (req->password == NULL ? "removing" : "updating"),
+ purple_account_get_username(account),
+ purple_account_get_protocol_id(account));
+ }
+
+ if (req->type == GNOMEKEYRING_REQUEST_READ) {
+ current_request = gnome_keyring_find_password(
+ GNOME_KEYRING_NETWORK_PASSWORD, gnomekeyring_read_cb,
+ req, gnomekeyring_request_cancel,
+ "user", purple_account_get_username(account),
+ "protocol", purple_account_get_protocol_id(account),
+ NULL);
+ } else if (req->type == GNOMEKEYRING_REQUEST_SAVE &&
+ req->password != NULL)
+ {
+ gchar *display_name = g_strdup_printf(
+ _("Pidgin IM password for account %s"),
+ purple_account_get_username(account));
+ current_request = gnome_keyring_store_password(
+ GNOME_KEYRING_NETWORK_PASSWORD, GNOME_KEYRING_DEFAULT,
+ display_name, req->password, gnomekeyring_save_cb, req,
+ gnomekeyring_request_cancel,
+ "user", purple_account_get_username(account),
+ "protocol", purple_account_get_protocol_id(account),
+ NULL);
+ g_free(display_name);
+ } else if (req->type == GNOMEKEYRING_REQUEST_SAVE &&
+ req->password == NULL)
+ {
+ current_request = gnome_keyring_delete_password(
+ GNOME_KEYRING_NETWORK_PASSWORD, gnomekeyring_save_cb,
+ req, gnomekeyring_request_cancel,
+ "user", purple_account_get_username(account),
+ "protocol", purple_account_get_protocol_id(account),
+ NULL);
+ } else {
+ g_return_if_reached();
+ }
+}
+
+static void
+gnomekeyring_read(PurpleAccount *account, PurpleKeyringReadCallback cb,
+ gpointer data)
+{
+ gnomekeyring_request *req;
+
+ g_return_if_fail(account != NULL);
+
+ req = g_new0(gnomekeyring_request, 1);
+ req->type = GNOMEKEYRING_REQUEST_READ;
+ req->account = account;
+ req->cb.read = cb;
+ req->cb_data = data;
+
+ gnomekeyring_enqueue(req);
+}
+
+static void
+gnomekeyring_save(PurpleAccount *account, const gchar *password,
+ PurpleKeyringSaveCallback cb, gpointer data)
+{
+ gnomekeyring_request *req;
+
+ g_return_if_fail(account != NULL);
+
+ req = g_new0(gnomekeyring_request, 1);
+ req->type = GNOMEKEYRING_REQUEST_SAVE;
+ req->account = account;
+ req->password = gnome_keyring_memory_strdup(password);
+ req->cb.save = cb;
+ req->cb_data = data;
+
+ gnomekeyring_enqueue(req);
+}
+
+static void
+gnomekeyring_cancel(void)
+{
+ gnomekeyring_cancel_queue();
+ if (current_request) {
+ gnome_keyring_cancel_request(current_request);
+ while (g_main_iteration(FALSE));
+ }
+}
+
+static void
+gnomekeyring_close(void)
+{
+ gnomekeyring_cancel();
+}
+
+static gboolean
+gnomekeyring_load(PurplePlugin *plugin)
+{
+ GModule *gkr_module;
+
+ /* libgnome-keyring may crash, if was unloaded before glib main loop
+ * termination.
+ */
+ gkr_module = g_module_open("libgnome-keyring", 0);
+ if (gkr_module == NULL) {
+ purple_debug_info("keyring-gnome", "GNOME Keyring module not "
+ "found\n");
+ return FALSE;
+ }
+ g_module_make_resident(gkr_module);
+
+ if (!gnome_keyring_is_available()) {
+ purple_debug_info("keyring-gnome", "GNOME Keyring service is "
+ "disabled\n");
+ return FALSE;
+ }
+
+ keyring_handler = purple_keyring_new();
+
+ purple_keyring_set_name(keyring_handler, _(GNOMEKEYRING_NAME));
+ purple_keyring_set_id(keyring_handler, GNOMEKEYRING_ID);
+ purple_keyring_set_read_password(keyring_handler, gnomekeyring_read);
+ purple_keyring_set_save_password(keyring_handler, gnomekeyring_save);
+ purple_keyring_set_cancel_requests(keyring_handler,
+ gnomekeyring_cancel);
+ purple_keyring_set_close_keyring(keyring_handler, gnomekeyring_close);
+
+ purple_keyring_register(keyring_handler);
+
+ return TRUE;
+}
+
+static gboolean
+gnomekeyring_unload(PurplePlugin *plugin)
+{
+ if (purple_keyring_get_inuse() == keyring_handler) {
+ purple_debug_warning("keyring-gnome",
+ "keyring in use, cannot unload\n");
+ return FALSE;
+ }
+
+ gnomekeyring_close();
+
+ purple_keyring_unregister(keyring_handler);
+ purple_keyring_free(keyring_handler);
+ keyring_handler = NULL;
+
+ return TRUE;
+}
+
+PurplePluginInfo plugininfo =
+{
+ PURPLE_PLUGIN_MAGIC, /* magic */
+ PURPLE_MAJOR_VERSION, /* major_version */
+ PURPLE_MINOR_VERSION, /* minor_version */
+ PURPLE_PLUGIN_STANDARD, /* type */
+ NULL, /* ui_requirement */
+ PURPLE_PLUGIN_FLAG_INVISIBLE, /* flags */
+ NULL, /* dependencies */
+ PURPLE_PRIORITY_DEFAULT, /* priority */
+ GNOMEKEYRING_ID, /* id */
+ GNOMEKEYRING_NAME, /* name */
+ DISPLAY_VERSION, /* version */
+ "GNOME Keyring Plugin", /* summary */
+ GNOMEKEYRING_DESCRIPTION, /* description */
+ GNOMEKEYRING_AUTHOR, /* author */
+ PURPLE_WEBSITE, /* homepage */
+ gnomekeyring_load, /* load */
+ gnomekeyring_unload, /* unload */
+ NULL, /* destroy */
+ NULL, /* ui_info */
+ NULL, /* extra_info */
+ NULL, /* prefs_info */
+ NULL, /* actions */
+ NULL, NULL, NULL, NULL /* padding */
+};
+
+static void
+init_plugin(PurplePlugin *plugin)
+{
+}
+
+PURPLE_INIT_PLUGIN(gnome_keyring, init_plugin, plugininfo)
diff --git a/libpurple/plugins/keyrings/internalkeyring.c b/libpurple/plugins/keyrings/internalkeyring.c
new file mode 100644
index 0000000000..cb78a12514
--- /dev/null
+++ b/libpurple/plugins/keyrings/internalkeyring.c
@@ -0,0 +1,1055 @@
+/**
+ * @file internalkeyring.c internal keyring
+ * @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 "debug.h"
+#include "keyring.h"
+#include "plugin.h"
+#include "version.h"
+
+#include "ciphers/aescipher.h"
+#include "ciphers/pbkdf2cipher.h"
+#include "ciphers/sha256hash.h"
+
+#define INTKEYRING_NAME N_("Internal keyring")
+#define INTKEYRING_DESCRIPTION N_("This plugin provides the default password " \
+ "storage behaviour for libpurple.")
+#define INTKEYRING_AUTHOR "Tomek Wasilczyk <twasilczyk@pidgin.im>"
+#define INTKEYRING_ID PURPLE_DEFAULT_KEYRING
+
+#define INTKEYRING_VERIFY_STR "[verification-string]"
+#define INTKEYRING_PBKDF2_ITERATIONS 10000
+#define INTKEYRING_PBKDF2_ITERATIONS_MIN 1000
+#define INTKEYRING_PBKDF2_ITERATIONS_MAX 1000000000
+#define INTKEYRING_KEY_LEN (256/8)
+#define INTKEYRING_ENCRYPT_BUFF_LEN 1000
+#define INTKEYRING_ENCRYPTED_MIN_LEN 50
+#define INTKEYRING_ENCRYPTION_METHOD "pbkdf2-sha256-aes256"
+
+#define INTKEYRING_PREFS "/plugins/keyrings/internal/"
+
+/* win32 build defines such macro to override read() routine */
+#undef read
+
+typedef struct
+{
+ enum
+ {
+ INTKEYRING_REQUEST_READ,
+ INTKEYRING_REQUEST_SAVE
+ } type;
+ PurpleAccount *account;
+ gchar *password;
+ union
+ {
+ PurpleKeyringReadCallback read;
+ PurpleKeyringSaveCallback save;
+ } cb;
+ gpointer cb_data;
+} intkeyring_request;
+
+typedef struct
+{
+ guchar *data;
+ size_t len;
+} intkeyring_buff_t;
+
+static intkeyring_buff_t *intkeyring_key;
+static GHashTable *intkeyring_passwords = NULL;
+static GHashTable *intkeyring_ciphertexts = NULL;
+
+static gboolean intkeyring_opened = FALSE;
+static gboolean intkeyring_unlocked = FALSE;
+
+static GList *intkeyring_pending_requests = NULL;
+static void *intkeyring_masterpw_uirequest = NULL;
+
+static PurpleKeyring *keyring_handler = NULL;
+
+static void
+intkeyring_read(PurpleAccount *account, PurpleKeyringReadCallback cb,
+ gpointer data);
+static void
+intkeyring_save(PurpleAccount *account, const gchar *password,
+ PurpleKeyringSaveCallback cb, gpointer data);
+static void
+intkeyring_reencrypt_passwords(void);
+static void
+intkeyring_unlock(const gchar *message);
+
+static void
+intkeyring_request_free(intkeyring_request *req)
+{
+ g_return_if_fail(req != NULL);
+
+ purple_str_wipe(req->password);
+ g_free(req);
+}
+
+static intkeyring_buff_t *
+intkeyring_buff_new(guchar *data, size_t len)
+{
+ intkeyring_buff_t *ret = g_new(intkeyring_buff_t, 1);
+
+ ret->data = data;
+ ret->len = len;
+
+ return ret;
+}
+
+static void
+intkeyring_buff_free(intkeyring_buff_t *buff)
+{
+ if (buff == NULL)
+ return;
+
+ memset(buff->data, 0, buff->len);
+ g_free(buff->data);
+ g_free(buff);
+}
+
+static intkeyring_buff_t *
+intkeyring_buff_from_base64(const gchar *base64)
+{
+ guchar *data;
+ gsize len;
+
+ data = purple_base64_decode(base64, &len);
+
+ return intkeyring_buff_new(data, len);
+}
+
+/************************************************************************/
+/* Generic encryption stuff */
+/************************************************************************/
+
+static intkeyring_buff_t *
+intkeyring_derive_key(const gchar *passphrase, intkeyring_buff_t *salt)
+{
+ PurpleCipher *cipher;
+ PurpleHash *hash;
+ gboolean succ;
+ intkeyring_buff_t *ret;
+
+ g_return_val_if_fail(passphrase != NULL, NULL);
+
+ hash = purple_sha256_hash_new();
+ cipher = purple_pbkdf2_cipher_new(hash);
+
+ g_object_set(G_OBJECT(cipher), "iter_count",
+ GUINT_TO_POINTER(purple_prefs_get_int(INTKEYRING_PREFS
+ "pbkdf2_iterations")), NULL);
+ g_object_set(G_OBJECT(cipher), "out_len", GUINT_TO_POINTER(
+ INTKEYRING_KEY_LEN), NULL);
+ purple_cipher_set_salt(cipher, salt->data, salt->len);
+ purple_cipher_set_key(cipher, (const guchar*)passphrase,
+ strlen(passphrase));
+
+ ret = intkeyring_buff_new(g_new(guchar, INTKEYRING_KEY_LEN),
+ INTKEYRING_KEY_LEN);
+ succ = purple_cipher_digest(cipher, ret->data, ret->len);
+
+ g_object_unref(cipher);
+ g_object_unref(hash);
+
+ if (!succ) {
+ intkeyring_buff_free(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
+static intkeyring_buff_t *
+intkeyring_gen_salt(size_t len)
+{
+ intkeyring_buff_t *ret;
+ size_t filled = 0;
+
+ g_return_val_if_fail(len > 0, NULL);
+
+ ret = intkeyring_buff_new(g_new(guchar, len), len);
+
+ while (filled < len) {
+ guint32 r = g_random_int();
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ ret->data[filled++] = r & 0xFF;
+ if (filled >= len)
+ break;
+ r >>= 8;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * Encrypts a plaintext using the specified key.
+ *
+ * Random IV will be generated and stored with ciphertext.
+ *
+ * Encryption scheme:
+ * [ IV ] ++ AES( [ plaintext ] ++ [ min length padding ] ++
+ * [ control string ] ++ [ pkcs7 padding ] )
+ * where:
+ * IV: Random, 128bit IV.
+ * plaintext: The plaintext.
+ * min length padding: The padding used to hide the rough length of short
+ * plaintexts, may have a length of 0.
+ * control string: Constant string, verifies corectness of decryption.
+ * pkcs7 padding: The padding used to determine total length of encrypted
+ * content (also provides some verification).
+ *
+ * @param key The AES key.
+ * @param str The NUL-terminated plaintext.
+ * @return The ciphertext with IV, encoded as base64. Must be g_free'd.
+ */
+static gchar *
+intkeyring_encrypt(intkeyring_buff_t *key, const gchar *str)
+{
+ PurpleCipher *cipher;
+ intkeyring_buff_t *iv;
+ guchar plaintext[INTKEYRING_ENCRYPT_BUFF_LEN];
+ size_t plaintext_len, text_len, verify_len;
+ guchar encrypted_raw[INTKEYRING_ENCRYPT_BUFF_LEN];
+ ssize_t encrypted_size;
+
+ g_return_val_if_fail(key != NULL, NULL);
+ g_return_val_if_fail(str != NULL, NULL);
+
+ text_len = strlen(str);
+ verify_len = strlen(INTKEYRING_VERIFY_STR);
+ plaintext_len = INTKEYRING_ENCRYPTED_MIN_LEN;
+ if (plaintext_len < text_len)
+ plaintext_len = text_len;
+
+ g_return_val_if_fail(plaintext_len + verify_len <= sizeof(plaintext),
+ NULL);
+
+ cipher = purple_aes_cipher_new();
+ g_return_val_if_fail(cipher != NULL, NULL);
+
+ memset(plaintext, 0, plaintext_len);
+ memcpy(plaintext, str, text_len);
+ memcpy(plaintext + plaintext_len, INTKEYRING_VERIFY_STR, verify_len);
+ plaintext_len += verify_len;
+
+ iv = intkeyring_gen_salt(purple_cipher_get_block_size(cipher));
+ g_return_val_if_fail(iv != NULL, NULL);
+ purple_cipher_set_iv(cipher, iv->data, iv->len);
+ purple_cipher_set_key(cipher, key->data, key->len);
+ purple_cipher_set_batch_mode(cipher,
+ PURPLE_CIPHER_BATCH_MODE_CBC);
+
+ memcpy(encrypted_raw, iv->data, iv->len);
+
+ encrypted_size = purple_cipher_encrypt(cipher,
+ plaintext, plaintext_len, encrypted_raw + iv->len,
+ sizeof(encrypted_raw) - iv->len);
+ encrypted_size += iv->len;
+
+ memset(plaintext, 0, plaintext_len);
+ intkeyring_buff_free(iv);
+ g_object_unref(cipher);
+
+ if (encrypted_size < 0)
+ return NULL;
+
+ return purple_base64_encode(encrypted_raw, encrypted_size);
+
+}
+
+static gchar *
+intkeyring_decrypt(intkeyring_buff_t *key, const gchar *str)
+{
+ PurpleCipher *cipher;
+ guchar *encrypted_raw;
+ gsize encrypted_size;
+ size_t iv_len, verify_len, text_len;
+ guchar plaintext[INTKEYRING_ENCRYPT_BUFF_LEN];
+ const gchar *verify_str = NULL;
+ ssize_t plaintext_len;
+ gchar *ret;
+
+ g_return_val_if_fail(key != NULL, NULL);
+ g_return_val_if_fail(str != NULL, NULL);
+
+ cipher = purple_aes_cipher_new();
+ g_return_val_if_fail(cipher != NULL, NULL);
+
+ encrypted_raw = purple_base64_decode(str, &encrypted_size);
+ g_return_val_if_fail(encrypted_raw != NULL, NULL);
+
+ iv_len = purple_cipher_get_block_size(cipher);
+ if (encrypted_size < iv_len) {
+ g_free(encrypted_raw);
+ return NULL;
+ }
+
+ purple_cipher_set_iv(cipher, encrypted_raw, iv_len);
+ purple_cipher_set_key(cipher, key->data, key->len);
+ purple_cipher_set_batch_mode(cipher,
+ PURPLE_CIPHER_BATCH_MODE_CBC);
+
+ plaintext_len = purple_cipher_decrypt(cipher,
+ encrypted_raw + iv_len, encrypted_size - iv_len,
+ plaintext, sizeof(plaintext));
+
+ g_free(encrypted_raw);
+ g_object_unref(cipher);
+
+ verify_len = strlen(INTKEYRING_VERIFY_STR);
+ /* Don't remove the len > 0 check! */
+ if (plaintext_len > 0 && (gsize)plaintext_len > verify_len &&
+ plaintext[plaintext_len] == '\0')
+ {
+ verify_str = (gchar*)plaintext + plaintext_len - verify_len;
+ }
+
+ if (g_strcmp0(verify_str, INTKEYRING_VERIFY_STR) != 0) {
+ purple_debug_warning("keyring-internal",
+ "Verification failed on decryption\n");
+ memset(plaintext, 0, sizeof(plaintext));
+ return NULL;
+ }
+ g_assert(plaintext_len > 0);
+
+ text_len = plaintext_len - verify_len;
+ ret = g_new(gchar, text_len + 1);
+ memcpy(ret, plaintext, text_len);
+ memset(plaintext, 0, plaintext_len);
+ ret[text_len] = '\0';
+
+ return ret;
+}
+
+/************************************************************************/
+/* Password encryption */
+/************************************************************************/
+
+static gboolean
+intkeyring_change_masterpw(const gchar *new_password)
+{
+ intkeyring_buff_t *salt, *key;
+ gchar *verifier = NULL, *salt_b64 = NULL;
+ int old_iter;
+ gboolean succ = TRUE;;
+
+ g_return_val_if_fail(intkeyring_unlocked, FALSE);
+
+ old_iter = purple_prefs_get_int(INTKEYRING_PREFS "pbkdf2_iterations");
+ purple_prefs_set_int(INTKEYRING_PREFS "pbkdf2_iterations",
+ purple_prefs_get_int(INTKEYRING_PREFS
+ "pbkdf2_desired_iterations"));
+
+ salt = intkeyring_gen_salt(32);
+ key = intkeyring_derive_key(new_password, salt);
+
+ if (salt && key && key->len == INTKEYRING_KEY_LEN) {
+ /* In fact, verify str will be concatenated twice before
+ * encryption (it's used as a suffix in encryption routine),
+ * but it's not a problem.
+ */
+ verifier = intkeyring_encrypt(key, INTKEYRING_VERIFY_STR);
+ salt_b64 = purple_base64_encode(salt->data, salt->len);
+ }
+
+ if (!verifier || !salt_b64) {
+ purple_debug_error("keyring-internal", "Failed to change "
+ "master password\n");
+ succ = FALSE;
+ purple_prefs_set_int(INTKEYRING_PREFS "pbkdf2_iterations",
+ old_iter);
+ } else {
+ purple_prefs_set_string(INTKEYRING_PREFS "pbkdf2_salt",
+ salt_b64);
+ purple_prefs_set_string(INTKEYRING_PREFS "key_verifier",
+ verifier);
+
+ intkeyring_buff_free(intkeyring_key);
+ intkeyring_key = key;
+ key = NULL;
+
+ intkeyring_reencrypt_passwords();
+
+ purple_signal_emit(purple_keyring_get_handle(),
+ "password-migration", NULL);
+ }
+
+ g_free(salt_b64);
+ g_free(verifier);
+ intkeyring_buff_free(salt);
+ intkeyring_buff_free(key);
+
+ return succ;
+}
+
+static void
+intkeyring_process_queue(void)
+{
+ GList *requests, *it;
+ gboolean open = intkeyring_unlocked;
+
+ requests = g_list_first(intkeyring_pending_requests);
+ intkeyring_pending_requests = NULL;
+
+ for (it = requests; it != NULL; it = g_list_next(it)) {
+ intkeyring_request *req = it->data;
+
+ if (open && req->type == INTKEYRING_REQUEST_READ) {
+ intkeyring_read(req->account, req->cb.read,
+ req->cb_data);
+ } else if (open && req->type == INTKEYRING_REQUEST_SAVE) {
+ intkeyring_save(req->account, req->password,
+ req->cb.save, req->cb_data);
+ } 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."));
+ if (req->type == INTKEYRING_REQUEST_READ) {
+ req->cb.read(req->account, NULL, error,
+ req->cb_data);
+ } else if (req->type == INTKEYRING_REQUEST_SAVE)
+ req->cb.save(req->account, error, req->cb_data);
+ else
+ g_assert_not_reached();
+ g_error_free(error);
+ }
+
+ intkeyring_request_free(req);
+ }
+ g_list_free(requests);
+}
+
+static void
+intkeyring_decrypt_password(PurpleAccount *account, const gchar *ciphertext)
+{
+ gchar *plaintext;
+
+ plaintext = intkeyring_decrypt(intkeyring_key, ciphertext);
+ if (plaintext == NULL) {
+ purple_debug_warning("keyring-internal",
+ "Failed to decrypt a password\n");
+ return;
+ }
+
+ g_hash_table_replace(intkeyring_passwords, account, plaintext);
+}
+
+static void
+intkeyring_encrypt_password_if_needed(PurpleAccount *account)
+{
+ const gchar *plaintext;
+ gchar *ciphertext;
+
+ if (intkeyring_key == NULL) {
+ g_hash_table_remove(intkeyring_ciphertexts, account);
+ return;
+ }
+
+ ciphertext = g_hash_table_lookup(intkeyring_ciphertexts, account);
+ if (ciphertext != NULL)
+ return;
+
+ plaintext = g_hash_table_lookup(intkeyring_passwords, account);
+ if (plaintext == NULL)
+ return;
+
+ ciphertext = intkeyring_encrypt(intkeyring_key, plaintext);
+ g_return_if_fail(ciphertext != NULL);
+
+ g_hash_table_replace(intkeyring_ciphertexts, account, ciphertext);
+}
+
+static void
+intkeyring_encrypt_passwords_if_needed_it(gpointer account, gpointer plaintext,
+ gpointer _unused)
+{
+ intkeyring_encrypt_password_if_needed(account);
+}
+
+static void
+intkeyring_reencrypt_passwords(void)
+{
+ g_hash_table_remove_all(intkeyring_ciphertexts);
+ g_hash_table_foreach(intkeyring_passwords,
+ intkeyring_encrypt_passwords_if_needed_it, NULL);
+}
+
+static void
+intkeyring_unlock_decrypt(gpointer account, gpointer ciphertext,
+ gpointer _unused)
+{
+ intkeyring_decrypt_password(account, ciphertext);
+}
+
+/************************************************************************/
+/* Opening and unlocking keyring */
+/************************************************************************/
+
+static void
+intkeyring_unlock_ok(gpointer _unused,
+ PurpleRequestFields *fields)
+{
+ const gchar *masterpw;
+ gchar *verifier;
+ intkeyring_buff_t *salt, *key;
+
+ intkeyring_masterpw_uirequest = NULL;
+
+ if (g_strcmp0(purple_prefs_get_string(INTKEYRING_PREFS
+ "encryption_method"), INTKEYRING_ENCRYPTION_METHOD) != 0)
+ {
+ purple_notify_error(NULL,
+ _("Unlocking internal keyring"),
+ _("Selected encryption method is not supported."),
+ _("Most probably, your passwords were encrypted with "
+ "newer Pidgin/libpurple version, please update."),
+ NULL);
+ return;
+ }
+
+ masterpw = purple_request_fields_get_string(fields, "password");
+
+ if (masterpw == NULL || masterpw[0] == '\0') {
+ intkeyring_unlock(_("No password entered."));
+ return;
+ }
+
+ salt = intkeyring_buff_from_base64(purple_prefs_get_string(
+ INTKEYRING_PREFS "pbkdf2_salt"));
+ key = intkeyring_derive_key(masterpw, salt);
+ intkeyring_buff_free(salt);
+
+ verifier = intkeyring_decrypt(key, purple_prefs_get_string(
+ INTKEYRING_PREFS "key_verifier"));
+
+ if (g_strcmp0(verifier, INTKEYRING_VERIFY_STR) != 0) {
+ g_free(verifier);
+ intkeyring_buff_free(key);
+ intkeyring_unlock(_("Invalid master password entered, "
+ "try again."));
+ return;
+ }
+
+ g_free(verifier);
+ intkeyring_key = key;
+ intkeyring_unlocked = TRUE;
+
+ g_hash_table_foreach(intkeyring_ciphertexts,
+ intkeyring_unlock_decrypt, NULL);
+
+ intkeyring_process_queue();
+}
+
+static void
+intkeyring_unlock_cancel(gpointer _unused,
+ PurpleRequestFields *fields)
+{
+ intkeyring_masterpw_uirequest = NULL;
+ intkeyring_process_queue();
+}
+
+static void
+intkeyring_unlock(const gchar *message)
+{
+ PurpleRequestFields *fields;
+ PurpleRequestFieldGroup *group;
+ PurpleRequestField *field;
+ const gchar *primary_msg, *secondary_msg = NULL;
+
+ if (intkeyring_unlocked || intkeyring_masterpw_uirequest != NULL)
+ return;
+
+ if (!purple_prefs_get_bool(INTKEYRING_PREFS "encrypt_passwords")) {
+ intkeyring_unlocked = TRUE;
+ intkeyring_process_queue();
+ return;
+ }
+
+ 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("password",
+ _("Master password"), "", FALSE);
+ purple_request_field_string_set_masked(field, TRUE);
+ purple_request_field_group_add_field(group, field);
+
+ primary_msg = _("Please, enter master password");
+ if (message) {
+ secondary_msg = primary_msg;
+ primary_msg = message;
+ }
+
+ intkeyring_masterpw_uirequest = purple_request_fields(NULL,
+ _("Unlocking internal keyring"),
+ primary_msg, secondary_msg, fields,
+ _("OK"), G_CALLBACK(intkeyring_unlock_ok),
+ _("Cancel"), G_CALLBACK(intkeyring_unlock_cancel),
+ NULL, NULL);
+}
+
+static void
+intkeyring_open(void)
+{
+ if (intkeyring_opened)
+ return;
+ intkeyring_opened = TRUE;
+
+ intkeyring_passwords = g_hash_table_new_full(g_direct_hash,
+ g_direct_equal, NULL, (GDestroyNotify)purple_str_wipe);
+ intkeyring_ciphertexts = g_hash_table_new_full(g_direct_hash,
+ g_direct_equal, NULL, g_free);
+}
+
+/************************************************************************/
+/* Keyring interface implementation */
+/************************************************************************/
+
+static void
+intkeyring_read(PurpleAccount *account, PurpleKeyringReadCallback cb,
+ gpointer data)
+{
+ const char *password;
+ GError *error;
+
+ intkeyring_open();
+
+ if (!intkeyring_unlocked && g_hash_table_lookup(intkeyring_ciphertexts,
+ account) != NULL)
+ {
+ intkeyring_request *req = g_new0(intkeyring_request, 1);
+
+ req->type = INTKEYRING_REQUEST_READ;
+ req->account = account;
+ req->cb.read = cb;
+ req->cb_data = data;
+ intkeyring_pending_requests =
+ g_list_append(intkeyring_pending_requests, req);
+
+ intkeyring_unlock(NULL);
+ return;
+ }
+
+ password = g_hash_table_lookup(intkeyring_passwords, account);
+
+ if (password != NULL) {
+ purple_debug_misc("keyring-internal",
+ "Got password for account %s (%s).\n",
+ purple_account_get_username(account),
+ purple_account_get_protocol_id(account));
+ if (cb != NULL)
+ cb(account, password, NULL, data);
+ } else {
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("keyring-internal",
+ "No password for account %s (%s).\n",
+ 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."));
+ if (cb != NULL)
+ cb(account, NULL, error, data);
+ g_error_free(error);
+ }
+}
+
+static void
+intkeyring_save(PurpleAccount *account, const gchar *password,
+ PurpleKeyringSaveCallback cb, gpointer data)
+{
+ void *old_password;
+
+ intkeyring_open();
+
+ if (!intkeyring_unlocked) {
+ intkeyring_request *req;
+
+ if (password == NULL) {
+ g_hash_table_remove(intkeyring_ciphertexts, account);
+ g_hash_table_remove(intkeyring_passwords, account);
+ if (cb)
+ cb(account, NULL, data);
+ return;
+ }
+
+ req = g_new0(intkeyring_request, 1);
+ req->type = INTKEYRING_REQUEST_SAVE;
+ req->account = account;
+ req->password = g_strdup(password);
+ req->cb.save = cb;
+ req->cb_data = data;
+ intkeyring_pending_requests =
+ g_list_append(intkeyring_pending_requests, req);
+
+ intkeyring_unlock(NULL);
+ return;
+ }
+
+ g_hash_table_remove(intkeyring_ciphertexts, account);
+
+ old_password = g_hash_table_lookup(intkeyring_passwords, account);
+
+ if (password == NULL)
+ g_hash_table_remove(intkeyring_passwords, account);
+ else {
+ g_hash_table_replace(intkeyring_passwords, account,
+ g_strdup(password));
+ }
+
+ intkeyring_encrypt_password_if_needed(account);
+
+ if (!(password == NULL && old_password == NULL)) {
+ purple_debug_misc("keyring-internal",
+ "Password %s for account %s (%s).\n",
+ (password == NULL ? "removed" : (old_password == NULL ?
+ "saved" : "updated")),
+ purple_account_get_username(account),
+ purple_account_get_protocol_id(account));
+ } else if (purple_debug_is_verbose()) {
+ purple_debug_misc("keyring-internal",
+ "Password for account %s (%s) was already removed.\n",
+ purple_account_get_username(account),
+ purple_account_get_protocol_id(account));
+ }
+
+ if (cb != NULL)
+ cb(account, NULL, data);
+}
+
+static void
+intkeyring_close(void)
+{
+ if (!intkeyring_opened)
+ return;
+ intkeyring_opened = FALSE;
+ intkeyring_unlocked = FALSE;
+
+ if (intkeyring_masterpw_uirequest) {
+ purple_request_close(PURPLE_REQUEST_FIELDS,
+ intkeyring_masterpw_uirequest);
+ }
+ g_warn_if_fail(intkeyring_masterpw_uirequest == NULL);
+ g_warn_if_fail(intkeyring_pending_requests == NULL);
+
+ intkeyring_buff_free(intkeyring_key);
+ intkeyring_key = NULL;
+ g_hash_table_destroy(intkeyring_passwords);
+ intkeyring_passwords = NULL;
+ g_hash_table_destroy(intkeyring_ciphertexts);
+ intkeyring_ciphertexts = NULL;
+}
+
+static gboolean
+intkeyring_import_password(PurpleAccount *account, const char *mode,
+ const char *data, GError **error)
+{
+ g_return_val_if_fail(account != NULL, FALSE);
+ g_return_val_if_fail(data != NULL, FALSE);
+
+ intkeyring_open();
+
+ if (mode == NULL)
+ mode = "cleartext";
+
+ if (g_strcmp0(mode, "cleartext") == 0) {
+ g_hash_table_replace(intkeyring_passwords, account,
+ g_strdup(data));
+ return TRUE;
+ } else if (g_strcmp0(mode, "ciphertext") == 0) {
+ if (intkeyring_unlocked)
+ intkeyring_decrypt_password(account, data);
+ else {
+ g_hash_table_replace(intkeyring_ciphertexts, account,
+ g_strdup(data));
+ }
+ return TRUE;
+ } else {
+ if (error != NULL) {
+ *error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_BACKENDFAIL,
+ _("Invalid password storage mode."));
+ }
+ return FALSE;
+ }
+}
+
+static gboolean
+intkeyring_export_password(PurpleAccount *account, const char **mode,
+ char **data, GError **error, GDestroyNotify *destroy)
+{
+ gchar *ciphertext = NULL;
+ intkeyring_open();
+
+ if (!purple_prefs_get_bool(INTKEYRING_PREFS "encrypt_passwords")) {
+ gchar *cleartext = g_hash_table_lookup(intkeyring_passwords,
+ account);
+
+ if (cleartext == NULL)
+ return FALSE;
+
+ *mode = "cleartext";
+ *data = g_strdup(cleartext);
+ *destroy = (GDestroyNotify)purple_str_wipe;
+ return TRUE;
+ }
+
+ ciphertext = g_strdup(g_hash_table_lookup(intkeyring_ciphertexts,
+ account));
+
+ if (ciphertext == NULL && intkeyring_unlocked) {
+ gchar *plaintext = g_hash_table_lookup(intkeyring_passwords,
+ account);
+
+ if (plaintext == NULL)
+ return FALSE;
+
+ purple_debug_warning("keyring-internal", "Encrypted password "
+ "is missing at export (it shouldn't happen)\n");
+ ciphertext = intkeyring_encrypt(intkeyring_key, plaintext);
+ }
+
+ if (ciphertext == NULL)
+ return FALSE;
+
+ *mode = "ciphertext";
+ *data = ciphertext;
+ *destroy = (GDestroyNotify)g_free;
+ return TRUE;
+}
+
+static PurpleRequestFields *
+intkeyring_read_settings(void)
+{
+ PurpleRequestFields *fields;
+ PurpleRequestFieldGroup *group;
+ PurpleRequestField *field;
+
+ fields = purple_request_fields_new();
+ group = purple_request_field_group_new(NULL);
+ purple_request_fields_add_group(fields, group);
+
+ field = purple_request_field_bool_new("encrypt_passwords",
+ _("Encrypt passwords"), purple_prefs_get_bool(
+ INTKEYRING_PREFS "encrypt_passwords"));
+ purple_request_field_group_add_field(group, field);
+
+ group = purple_request_field_group_new(_("Master password"));
+ purple_request_fields_add_group(fields, group);
+
+ field = purple_request_field_string_new("passphrase1",
+ _("New passphrase:"), "", FALSE);
+ purple_request_field_string_set_masked(field, TRUE);
+ purple_request_field_group_add_field(group, field);
+
+ field = purple_request_field_string_new("passphrase2",
+ _("New passphrase (again):"), "", FALSE);
+ purple_request_field_string_set_masked(field, TRUE);
+ purple_request_field_group_add_field(group, field);
+
+ group = purple_request_field_group_new(_("Advanced settings"));
+ purple_request_fields_add_group(fields, group);
+
+ field = purple_request_field_int_new("pbkdf2_desired_iterations",
+ _("Number of PBKDF2 iterations:"), purple_prefs_get_int(
+ INTKEYRING_PREFS "pbkdf2_desired_iterations"),
+ INTKEYRING_PBKDF2_ITERATIONS_MIN,
+ INTKEYRING_PBKDF2_ITERATIONS_MAX);
+ purple_request_field_group_add_field(group, field);
+
+ return fields;
+}
+
+static gboolean
+intkeyring_apply_settings(void *notify_handle,
+ PurpleRequestFields *fields)
+{
+ const gchar *passphrase, *passphrase2;
+
+ intkeyring_unlock(_("You have to unlock the keyring first."));
+ if (!intkeyring_unlocked)
+ return FALSE;
+
+ passphrase = purple_request_fields_get_string(fields, "passphrase1");
+ if (g_strcmp0(passphrase, "") == 0)
+ passphrase = NULL;
+ passphrase2 = purple_request_fields_get_string(fields, "passphrase2");
+ if (g_strcmp0(passphrase2, "") == 0)
+ passphrase2 = NULL;
+
+ if (g_strcmp0(passphrase, passphrase2) != 0) {
+ purple_notify_error(notify_handle,
+ _("Internal keyring settings"),
+ _("Passphrases do not match"), NULL, NULL);
+ return FALSE;
+ }
+
+ if (purple_request_fields_get_bool(fields, "encrypt_passwords") &&
+ !passphrase && !intkeyring_key)
+ {
+ purple_notify_error(notify_handle,
+ _("Internal keyring settings"),
+ _("You have to set up a Master password, if you want "
+ "to enable encryption"), NULL, NULL);
+ return FALSE;
+ }
+
+ if (!purple_request_fields_get_bool(fields, "encrypt_passwords") &&
+ passphrase)
+ {
+ purple_notify_error(notify_handle,
+ _("Internal keyring settings"),
+ _("You don't need any master password, if you won't "
+ "enable passwords encryption"), NULL, NULL);
+ return FALSE;
+ }
+
+ purple_prefs_set_string(INTKEYRING_PREFS "encryption_method",
+ INTKEYRING_ENCRYPTION_METHOD);
+
+ purple_prefs_set_int(INTKEYRING_PREFS "pbkdf2_desired_iterations",
+ purple_request_fields_get_integer(fields,
+ "pbkdf2_desired_iterations"));
+
+ if (passphrase != NULL) {
+ if (!intkeyring_change_masterpw(passphrase))
+ return FALSE;
+ }
+
+ purple_prefs_set_bool(INTKEYRING_PREFS "encrypt_passwords",
+ purple_request_fields_get_bool(fields, "encrypt_passwords"));
+
+ purple_signal_emit(purple_keyring_get_handle(), "password-migration",
+ NULL);
+
+
+ return TRUE;
+}
+
+static gboolean
+intkeyring_load(PurplePlugin *plugin)
+{
+ keyring_handler = purple_keyring_new();
+
+ purple_keyring_set_name(keyring_handler, _(INTKEYRING_NAME));
+ purple_keyring_set_id(keyring_handler, INTKEYRING_ID);
+ purple_keyring_set_read_password(keyring_handler,
+ intkeyring_read);
+ purple_keyring_set_save_password(keyring_handler,
+ intkeyring_save);
+ purple_keyring_set_close_keyring(keyring_handler,
+ intkeyring_close);
+ purple_keyring_set_import_password(keyring_handler,
+ intkeyring_import_password);
+ purple_keyring_set_export_password(keyring_handler,
+ intkeyring_export_password);
+ purple_keyring_set_read_settings(keyring_handler,
+ intkeyring_read_settings);
+ purple_keyring_set_apply_settings(keyring_handler,
+ intkeyring_apply_settings);
+
+ purple_keyring_register(keyring_handler);
+
+ return TRUE;
+}
+
+static gboolean
+intkeyring_unload(PurplePlugin *plugin)
+{
+ if (purple_keyring_get_inuse() == keyring_handler) {
+ purple_debug_warning("keyring-internal",
+ "keyring in use, cannot unload\n");
+ return FALSE;
+ }
+
+ intkeyring_close();
+
+ purple_keyring_unregister(keyring_handler);
+ purple_keyring_free(keyring_handler);
+ keyring_handler = NULL;
+
+ if (intkeyring_key != NULL) {
+ purple_debug_warning("keyring-internal", "Master key should be "
+ "cleaned up at this point\n");
+ intkeyring_buff_free(intkeyring_key);
+ intkeyring_key = NULL;
+ }
+
+ return TRUE;
+}
+
+PurplePluginInfo plugininfo =
+{
+ PURPLE_PLUGIN_MAGIC, /* magic */
+ PURPLE_MAJOR_VERSION, /* major_version */
+ PURPLE_MINOR_VERSION, /* minor_version */
+ PURPLE_PLUGIN_STANDARD, /* type */
+ NULL, /* ui_requirement */
+ PURPLE_PLUGIN_FLAG_INVISIBLE, /* flags */
+ NULL, /* dependencies */
+ PURPLE_PRIORITY_DEFAULT, /* priority */
+ INTKEYRING_ID, /* id */
+ INTKEYRING_NAME, /* name */
+ DISPLAY_VERSION, /* version */
+ "Internal Keyring Plugin", /* summary */
+ INTKEYRING_DESCRIPTION, /* description */
+ INTKEYRING_AUTHOR, /* author */
+ PURPLE_WEBSITE, /* homepage */
+ intkeyring_load, /* load */
+ intkeyring_unload, /* unload */
+ NULL, /* destroy */
+ NULL, /* ui_info */
+ NULL, /* extra_info */
+ NULL, /* prefs_info */
+ NULL, /* actions */
+ NULL, NULL, NULL, NULL /* padding */
+};
+
+static void
+init_plugin(PurplePlugin *plugin)
+{
+ purple_prefs_add_none("/plugins/keyrings");
+ purple_prefs_add_none("/plugins/keyrings/internal");
+ purple_prefs_add_bool(INTKEYRING_PREFS "encrypt_passwords", FALSE);
+ purple_prefs_add_string(INTKEYRING_PREFS "encryption_method",
+ INTKEYRING_ENCRYPTION_METHOD);
+ purple_prefs_add_int(INTKEYRING_PREFS "pbkdf2_desired_iterations",
+ INTKEYRING_PBKDF2_ITERATIONS);
+ purple_prefs_add_int(INTKEYRING_PREFS "pbkdf2_iterations",
+ INTKEYRING_PBKDF2_ITERATIONS);
+ purple_prefs_add_string(INTKEYRING_PREFS "pbkdf2_salt", "");
+ purple_prefs_add_string(INTKEYRING_PREFS "key_verifier", "");
+}
+
+PURPLE_INIT_PLUGIN(internal_keyring, init_plugin, plugininfo)
diff --git a/libpurple/plugins/keyrings/kwallet.cpp b/libpurple/plugins/keyrings/kwallet.cpp
new file mode 100644
index 0000000000..40fc42ff3a
--- /dev/null
+++ b/libpurple/plugins/keyrings/kwallet.cpp
@@ -0,0 +1,584 @@
+/**
+ * @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 "plugin.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_AUTHOR "QuLogic (qulogic[at]pidgin.im)"
+#define KWALLET_ID "keyring-kwallet"
+
+#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 gboolean
+kwallet_load(PurplePlugin *plugin)
+{
+ if (!qCoreApp) {
+ int argc = 0;
+ qCoreApp = new QCoreApplication(argc, NULL);
+ qCoreApp->setApplicationName(kwallet_get_ui_name());
+ }
+
+ if (!kwallet_is_enabled()) {
+ 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
+kwallet_unload(PurplePlugin *plugin)
+{
+ if (purple_keyring_get_inuse() == keyring_handler) {
+ 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;
+}
+
+PurplePluginInfo plugininfo =
+{
+ PURPLE_PLUGIN_MAGIC, /* magic */
+ PURPLE_MAJOR_VERSION, /* major_version */
+ PURPLE_MINOR_VERSION, /* minor_version */
+ PURPLE_PLUGIN_STANDARD, /* type */
+ NULL, /* ui_requirement */
+ PURPLE_PLUGIN_FLAG_INVISIBLE, /* flags */
+ NULL, /* dependencies */
+ PURPLE_PRIORITY_DEFAULT, /* priority */
+ KWALLET_ID, /* id */
+ KWALLET_NAME, /* name */
+ DISPLAY_VERSION, /* version */
+ "KWallet Keyring Plugin", /* summary */
+ KWALLET_DESCRIPTION, /* description */
+ KWALLET_AUTHOR, /* author */
+ PURPLE_WEBSITE, /* homepage */
+ kwallet_load, /* load */
+ kwallet_unload, /* unload */
+ NULL, /* destroy */
+ NULL, /* ui_info */
+ NULL, /* extra_info */
+ NULL, /* prefs_info */
+ NULL, /* actions */
+ NULL, NULL, NULL, NULL /* padding */
+};
+
+static void
+init_plugin(PurplePlugin *plugin)
+{
+}
+
+PURPLE_INIT_PLUGIN(kwallet_keyring, init_plugin, plugininfo)
+
+} /* extern "C" */
+
+#include "kwallet.moc"
diff --git a/libpurple/plugins/keyrings/secretservice.c b/libpurple/plugins/keyrings/secretservice.c
new file mode 100644
index 0000000000..286d1a5d9c
--- /dev/null
+++ b/libpurple/plugins/keyrings/secretservice.c
@@ -0,0 +1,339 @@
+/* purple
+ * @file secretservice.c Secret Service password storage
+ * @ingroup plugins
+ *
+ * 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
+ */
+
+#error "This keyring needs some more work (see TODO)"
+
+/* TODO
+ *
+ * This keyring needs some more work, so it will be disabled until its quality
+ * was raised. Some of the pain points:
+ * - throws a lot of g_warnings
+ * - it doesn't notify about some backend faults (like access denied), some of
+ * them are not handled at all
+ * - it could use libsecret's Complete API
+ * - code formatting could be better
+ */
+
+#include "internal.h"
+#include "account.h"
+#include "debug.h"
+#include "keyring.h"
+#include "plugin.h"
+#include "version.h"
+
+#include <libsecret/secret.h>
+
+#define SECRETSERVICE_NAME N_("Secret Service")
+#define SECRETSERVICE_ID "keyring-libsecret"
+
+static PurpleKeyring *keyring_handler = NULL;
+
+static const SecretSchema purple_schema = {
+ "im.pidgin.Purple", SECRET_SCHEMA_NONE,
+ {
+ {"user", SECRET_SCHEMA_ATTRIBUTE_STRING},
+ {"protocol", SECRET_SCHEMA_ATTRIBUTE_STRING},
+ {"NULL", 0}
+ },
+ /* Reserved fields */
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+typedef struct _InfoStorage InfoStorage;
+
+struct _InfoStorage
+{
+ PurpleAccount *account;
+ gpointer cb;
+ gpointer user_data;
+};
+
+/***********************************************/
+/* Keyring interface */
+/***********************************************/
+static void
+ss_read_continue(GObject *object, GAsyncResult *result, gpointer data)
+{
+ InfoStorage *storage = data;
+ PurpleAccount *account = storage->account;
+ PurpleKeyringReadCallback cb = storage->cb;
+ char *password;
+ GError *error = NULL;
+
+ password = secret_password_lookup_finish(result, &error);
+
+ if (error != NULL) {
+ int code = error->code;
+
+ switch (code) {
+ case G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND:
+ case G_DBUS_ERROR_IO_ERROR:
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_BACKENDFAIL,
+ "Failed to communicate with Secret "
+ "Service (account : %s).",
+ purple_account_get_username(account));
+ if (cb != NULL)
+ cb(account, NULL, error, storage->user_data);
+ g_error_free(error);
+ break;
+
+ default:
+ purple_debug_error("keyring-libsecret",
+ "Unknown error (account: %s (%s), "
+ "domain: %s, code: %d): %s.\n",
+ purple_account_get_username(account),
+ purple_account_get_protocol_id(account),
+ g_quark_to_string(error->domain), code,
+ error->message);
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_BACKENDFAIL,
+ "Unknown error (account : %s).",
+ purple_account_get_username(account));
+ if (cb != NULL)
+ cb(account, NULL, error, storage->user_data);
+ g_error_free(error);
+ break;
+ }
+
+ } else if (password == NULL) {
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_NOPASSWORD,
+ "No password found for account: %s",
+ purple_account_get_username(account));
+ if (cb != NULL)
+ cb(account, NULL, error, storage->user_data);
+ g_error_free(error);
+
+ } else {
+ if (cb != NULL)
+ cb(account, password, NULL, storage->user_data);
+ }
+
+ g_free(storage);
+}
+
+static void
+ss_read(PurpleAccount *account, PurpleKeyringReadCallback cb, gpointer data)
+{
+ InfoStorage *storage = g_new0(InfoStorage, 1);
+
+ storage->account = account;
+ storage->cb = cb;
+ storage->user_data = data;
+
+ secret_password_lookup(&purple_schema, NULL, ss_read_continue, storage,
+ "user", purple_account_get_username(account),
+ "protocol", purple_account_get_protocol_id(account), NULL);
+}
+
+static void
+ss_save_continue(GObject *object, GAsyncResult *result, gpointer data)
+{
+ InfoStorage *storage = data;
+ PurpleKeyringSaveCallback cb;
+ GError *error = NULL;
+ PurpleAccount *account;
+
+ account = storage->account;
+ cb = storage->cb;
+
+ secret_password_store_finish(result, &error);
+
+ if (error != NULL) {
+ int code = error->code;
+
+ switch (code) {
+ case G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND:
+ case G_DBUS_ERROR_IO_ERROR:
+ purple_debug_info("keyring-libsecret",
+ "Failed to communicate with Secret "
+ "Service (account : %s (%s)).\n",
+ purple_account_get_username(account),
+ purple_account_get_protocol_id(account));
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_BACKENDFAIL,
+ "Failed to communicate with Secret Service (account : %s).",
+ purple_account_get_username(account));
+ if (cb != NULL)
+ cb(account, error, storage->user_data);
+ g_error_free(error);
+ break;
+
+ default:
+ purple_debug_error("keyring-libsecret",
+ "Unknown error (account: %s (%s), "
+ "domain: %s, code: %d): %s.\n",
+ purple_account_get_username(account),
+ purple_account_get_protocol_id(account),
+ g_quark_to_string(error->domain), code,
+ error->message);
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_BACKENDFAIL,
+ "Unknown error (account : %s).",
+ purple_account_get_username(account));
+ if (cb != NULL)
+ cb(account, error, storage->user_data);
+ g_error_free(error);
+ break;
+ }
+
+ } else {
+ purple_debug_info("keyring-libsecret", "Password for %s updated.\n",
+ purple_account_get_username(account));
+
+ if (cb != NULL)
+ cb(account, NULL, storage->user_data);
+ }
+
+ g_free(storage);
+}
+
+static void
+ss_save(PurpleAccount *account,
+ const gchar *password,
+ PurpleKeyringSaveCallback cb,
+ gpointer data)
+{
+ InfoStorage *storage = g_new0(InfoStorage, 1);
+
+ storage->account = account;
+ storage->cb = cb;
+ storage->user_data = data;
+
+ if (password != NULL && *password != '\0') {
+ const char *username = purple_account_get_username(account);
+ char *label;
+
+ purple_debug_info("keyring-libsecret",
+ "Updating password for account %s (%s).\n",
+ username, purple_account_get_protocol_id(account));
+
+ label = g_strdup_printf(_("Pidgin IM password for account %s"), username);
+ secret_password_store(&purple_schema, SECRET_COLLECTION_DEFAULT,
+ label, password, NULL, ss_save_continue, storage,
+ "user", username,
+ "protocol", purple_account_get_protocol_id(account),
+ NULL);
+ g_free(label);
+
+ } else { /* password == NULL, delete password. */
+ purple_debug_info("keyring-libsecret",
+ "Forgetting password for account %s (%s).\n",
+ purple_account_get_username(account),
+ purple_account_get_protocol_id(account));
+
+ secret_password_clear(&purple_schema, NULL, ss_save_continue,
+ storage, "user", purple_account_get_username(account),
+ "protocol", purple_account_get_protocol_id(account),
+ NULL);
+ }
+}
+
+static void
+ss_close(void)
+{
+}
+
+static gboolean
+ss_init(void)
+{
+ keyring_handler = purple_keyring_new();
+
+ purple_keyring_set_name(keyring_handler, _(SECRETSERVICE_NAME));
+ purple_keyring_set_id(keyring_handler, SECRETSERVICE_ID);
+ purple_keyring_set_read_password(keyring_handler, ss_read);
+ purple_keyring_set_save_password(keyring_handler, ss_save);
+ purple_keyring_set_close_keyring(keyring_handler, ss_close);
+
+ purple_keyring_register(keyring_handler);
+
+ return TRUE;
+}
+
+static void
+ss_uninit(void)
+{
+ ss_close();
+ purple_keyring_unregister(keyring_handler);
+ purple_keyring_free(keyring_handler);
+ keyring_handler = NULL;
+}
+
+/***********************************************/
+/* Plugin interface */
+/***********************************************/
+
+static gboolean
+ss_load(PurplePlugin *plugin)
+{
+ return ss_init();
+}
+
+static gboolean
+ss_unload(PurplePlugin *plugin)
+{
+ if (purple_keyring_get_inuse() == keyring_handler)
+ return FALSE;
+
+ ss_uninit();
+
+ return TRUE;
+}
+
+PurplePluginInfo plugininfo =
+{
+ PURPLE_PLUGIN_MAGIC, /* magic */
+ PURPLE_MAJOR_VERSION, /* major_version */
+ PURPLE_MINOR_VERSION, /* minor_version */
+ PURPLE_PLUGIN_STANDARD, /* type */
+ NULL, /* ui_requirement */
+ PURPLE_PLUGIN_FLAG_INVISIBLE, /* flags */
+ NULL, /* dependencies */
+ PURPLE_PRIORITY_DEFAULT, /* priority */
+ SECRETSERVICE_ID, /* id */
+ SECRETSERVICE_NAME, /* name */
+ DISPLAY_VERSION, /* version */
+ "Secret Service Plugin", /* summary */
+ N_("This plugin will store passwords in Secret Service."), /* description */
+ "Elliott Sales de Andrade (qulogic[at]pidgin.im)", /* author */
+ PURPLE_WEBSITE, /* homepage */
+ ss_load, /* load */
+ ss_unload, /* unload */
+ NULL, /* destroy */
+ NULL, /* ui_info */
+ NULL, /* extra_info */
+ NULL, /* prefs_info */
+ NULL, /* actions */
+ NULL, /* padding... */
+ NULL,
+ NULL,
+ NULL,
+};
+
+static void
+init_plugin(PurplePlugin *plugin)
+{
+}
+
+PURPLE_INIT_PLUGIN(secret_service, init_plugin, plugininfo)
diff --git a/libpurple/plugins/keyrings/wincred.c b/libpurple/plugins/keyrings/wincred.c
new file mode 100644
index 0000000000..be33a26035
--- /dev/null
+++ b/libpurple/plugins/keyrings/wincred.c
@@ -0,0 +1,320 @@
+/**
+ * @file wincred.c Passwords storage using Windows credentials
+ * @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 "debug.h"
+#include "internal.h"
+#include "keyring.h"
+#include "plugin.h"
+#include "version.h"
+
+#include <wincred.h>
+
+#define WINCRED_NAME N_("Windows credentials")
+#define WINCRED_SUMMARY N_("Store passwords using Windows credentials")
+#define WINCRED_DESCRIPTION N_("This plugin stores passwords using Windows " \
+ "credentials.")
+#define WINCRED_AUTHOR "Tomek Wasilczyk <twasilczyk@pidgin.im>"
+#define WINCRED_ID "keyring-wincred"
+
+#define WINCRED_MAX_TARGET_NAME 256
+
+static PurpleKeyring *keyring_handler = NULL;
+
+static gunichar2 *
+wincred_get_target_name(PurpleAccount *account)
+{
+ gchar target_name_utf8[WINCRED_MAX_TARGET_NAME];
+ gunichar2 *target_name_utf16;
+
+ g_return_val_if_fail(account != NULL, NULL);
+
+ g_snprintf(target_name_utf8, WINCRED_MAX_TARGET_NAME, "libpurple_%s_%s",
+ purple_account_get_protocol_id(account),
+ purple_account_get_username(account));
+
+ target_name_utf16 = g_utf8_to_utf16(target_name_utf8, -1,
+ NULL, NULL, NULL);
+
+ if (target_name_utf16 == NULL) {
+ purple_debug_fatal("keyring-wincred", "Couldn't convert target "
+ "name\n");
+ }
+
+ return target_name_utf16;
+}
+
+static void
+wincred_read(PurpleAccount *account, PurpleKeyringReadCallback cb,
+ gpointer data)
+{
+ GError *error = NULL;
+ gunichar2 *target_name = NULL;
+ gchar *password;
+ PCREDENTIALW credential;
+
+ g_return_if_fail(account != NULL);
+
+ target_name = wincred_get_target_name(account);
+ g_return_if_fail(target_name != NULL);
+
+ if (!CredReadW(target_name, CRED_TYPE_GENERIC, 0, &credential)) {
+ DWORD error_code = GetLastError();
+
+ if (error_code == ERROR_NOT_FOUND) {
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("keyring-wincred",
+ "No password found for account %s\n",
+ purple_account_get_username(account));
+ }
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_NOPASSWORD,
+ _("Password not found."));
+ } else if (error_code == ERROR_NO_SUCH_LOGON_SESSION) {
+ purple_debug_error("keyring-wincred",
+ "Cannot read password, no valid logon "
+ "session\n");
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_ACCESSDENIED,
+ _("Cannot read password, no valid logon "
+ "session."));
+ } else {
+ purple_debug_error("keyring-wincred",
+ "Cannot read password, error %lx\n",
+ error_code);
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_BACKENDFAIL,
+ _("Cannot read password (error %lx)."), error_code);
+ }
+
+ if (cb != NULL)
+ cb(account, NULL, error, data);
+ g_error_free(error);
+ return;
+ }
+
+ password = g_utf16_to_utf8((gunichar2*)credential->CredentialBlob,
+ credential->CredentialBlobSize / sizeof(gunichar2),
+ NULL, NULL, NULL);
+
+ memset(credential->CredentialBlob, 0, credential->CredentialBlobSize);
+ CredFree(credential);
+
+ if (password == NULL) {
+ purple_debug_error("keyring-wincred",
+ "Cannot convert password\n");
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_BACKENDFAIL,
+ _("Cannot read password (unicode error)."));
+ } else {
+ purple_debug_misc("keyring-wincred",
+ _("Got password for account %s.\n"),
+ purple_account_get_username(account));
+ }
+
+ if (cb != NULL)
+ cb(account, password, error, data);
+ if (error != NULL)
+ g_error_free(error);
+
+ purple_str_wipe(password);
+}
+
+static void
+wincred_save(PurpleAccount *account, const gchar *password,
+ PurpleKeyringSaveCallback cb, gpointer data)
+{
+ GError *error = NULL;
+ gunichar2 *target_name = NULL;
+ gunichar2 *username_utf16 = NULL;
+ gunichar2 *password_utf16 = NULL;
+ CREDENTIALW credential;
+
+ g_return_if_fail(account != NULL);
+
+ target_name = wincred_get_target_name(account);
+ g_return_if_fail(target_name != NULL);
+
+ if (password == NULL) {
+ if (CredDeleteW(target_name, CRED_TYPE_GENERIC, 0)) {
+ purple_debug_misc("keyring-wincred", "Password for "
+ "account %s removed\n",
+ purple_account_get_username(account));
+ } else {
+ DWORD error_code = GetLastError();
+
+ if (error_code == ERROR_NOT_FOUND) {
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("keyring-wincred",
+ "Password for account %s was already "
+ "removed.\n",
+ purple_account_get_username(account));
+ }
+ } else if (error_code == ERROR_NO_SUCH_LOGON_SESSION) {
+ purple_debug_error("keyring-wincred",
+ "Cannot remove password, no valid "
+ "logon session\n");
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_ACCESSDENIED,
+ _("Cannot remove password, no valid "
+ "logon session."));
+ } else {
+ purple_debug_error("keyring-wincred",
+ "Cannot remove password, error %lx\n",
+ error_code);
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_BACKENDFAIL,
+ _("Cannot remove password (error %lx)."),
+ error_code);
+ }
+ }
+
+ if (cb != NULL)
+ cb(account, error, data);
+ if (error != NULL)
+ g_error_free(error);
+ return;
+ }
+
+ username_utf16 = g_utf8_to_utf16(purple_account_get_username(account),
+ -1, NULL, NULL, NULL);
+ password_utf16 = g_utf8_to_utf16(password, -1, NULL, NULL, NULL);
+
+ if (username_utf16 == NULL || password_utf16 == NULL) {
+ g_free(username_utf16);
+ purple_utf16_wipe(password_utf16);
+
+ purple_debug_fatal("keyring-wincred", "Couldn't convert "
+ "username or password\n");
+ g_return_if_reached();
+ }
+
+ memset(&credential, 0, sizeof(CREDENTIALW));
+ credential.Type = CRED_TYPE_GENERIC;
+ credential.TargetName = target_name;
+ credential.CredentialBlobSize = purple_utf16_size(password_utf16) - 2;
+ credential.CredentialBlob = (LPBYTE)password_utf16;
+ credential.Persist = CRED_PERSIST_LOCAL_MACHINE;
+ credential.UserName = username_utf16;
+
+ if (!CredWriteW(&credential, 0)) {
+ DWORD error_code = GetLastError();
+
+ if (error_code == ERROR_NO_SUCH_LOGON_SESSION) {
+ purple_debug_error("keyring-wincred",
+ "Cannot store password, no valid logon "
+ "session\n");
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_ACCESSDENIED,
+ _("Cannot remove password, no valid logon "
+ "session."));
+ } else {
+ purple_debug_error("keyring-wincred",
+ "Cannot store password, error %lx\n",
+ error_code);
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_BACKENDFAIL,
+ _("Cannot store password (error %lx)."), error_code);
+ }
+ } else {
+ purple_debug_misc("keyring-wincred",
+ "Password updated for account %s.\n",
+ purple_account_get_username(account));
+ }
+
+ g_free(target_name);
+ g_free(username_utf16);
+ purple_utf16_wipe(password_utf16);
+
+ if (cb != NULL)
+ cb(account, error, data);
+ if (error != NULL)
+ g_error_free(error);
+}
+
+static gboolean
+wincred_load(PurplePlugin *plugin)
+{
+ keyring_handler = purple_keyring_new();
+
+ purple_keyring_set_name(keyring_handler, _(WINCRED_NAME));
+ purple_keyring_set_id(keyring_handler, WINCRED_ID);
+ purple_keyring_set_read_password(keyring_handler, wincred_read);
+ purple_keyring_set_save_password(keyring_handler, wincred_save);
+
+ purple_keyring_register(keyring_handler);
+
+ return TRUE;
+}
+
+static gboolean
+wincred_unload(PurplePlugin *plugin)
+{
+ if (purple_keyring_get_inuse() == keyring_handler) {
+ purple_debug_warning("keyring-wincred",
+ "keyring in use, cannot unload\n");
+ return FALSE;
+ }
+
+ purple_keyring_unregister(keyring_handler);
+ purple_keyring_free(keyring_handler);
+ keyring_handler = NULL;
+
+ return TRUE;
+}
+
+PurplePluginInfo plugininfo =
+{
+ PURPLE_PLUGIN_MAGIC, /* magic */
+ PURPLE_MAJOR_VERSION, /* major_version */
+ PURPLE_MINOR_VERSION, /* minor_version */
+ PURPLE_PLUGIN_STANDARD, /* type */
+ NULL, /* ui_requirement */
+ PURPLE_PLUGIN_FLAG_INVISIBLE, /* flags */
+ NULL, /* dependencies */
+ PURPLE_PRIORITY_DEFAULT, /* priority */
+ WINCRED_ID, /* id */
+ WINCRED_NAME, /* name */
+ DISPLAY_VERSION, /* version */
+ WINCRED_SUMMARY, /* summary */
+ WINCRED_DESCRIPTION, /* description */
+ WINCRED_AUTHOR, /* author */
+ PURPLE_WEBSITE, /* homepage */
+ wincred_load, /* load */
+ wincred_unload, /* unload */
+ NULL, /* destroy */
+ NULL, /* ui_info */
+ NULL, /* extra_info */
+ NULL, /* prefs_info */
+ NULL, /* actions */
+ NULL, NULL, NULL, NULL /* padding */
+};
+
+static void
+init_plugin(PurplePlugin *plugin)
+{
+}
+
+PURPLE_INIT_PLUGIN(wincred_keyring, init_plugin, plugininfo)
diff --git a/libpurple/plugins/log_reader.c b/libpurple/plugins/log_reader.c
index 51b5542ec1..ec0d252c64 100644
--- a/libpurple/plugins/log_reader.c
+++ b/libpurple/plugins/log_reader.c
@@ -3,6 +3,7 @@
#include <stdio.h>
#include "debug.h"
+#include "glibcompat.h"
#include "log.h"
#include "plugin.h"
#include "pluginpref.h"
@@ -12,11 +13,6 @@
#include "version.h"
#include "xmlnode.h"
-/* This must be the last Purple header included. */
-#ifdef _WIN32
-#include "win32dep.h"
-#endif
-
/* Where is the Windows partition mounted? */
#ifndef PURPLE_LOG_READER_WINDOWS_MOUNT_POINT
#define PURPLE_LOG_READER_WINDOWS_MOUNT_POINT "/mnt/windows"
@@ -92,7 +88,7 @@ static GList *adium_logger_list(PurpleLogType type, const char *sn, PurpleAccoun
prpl_name = g_ascii_strup(prpl_info->list_icon(account, NULL), -1);
- temp = g_strdup_printf("%s.%s", prpl_name, account->username);
+ temp = g_strdup_printf("%s.%s", prpl_name, purple_account_get_username(account));
path = g_build_filename(logdir, temp, sn, NULL);
g_free(temp);
@@ -297,9 +293,9 @@ static int adium_logger_size (PurpleLog *log)
data = log->logger_data;
if (purple_prefs_get_bool("/plugins/core/log_reader/fast_sizes")) {
- struct stat st;
+ GStatBuf st;
- if (!data->path || stat(data->path, &st))
+ if (!data->path || g_stat(data->path, &st))
st.st_size = 0;
return st.st_size;
@@ -440,8 +436,8 @@ static void messenger_plus_logger_finalize(PurpleLog *log)
static PurpleLogLogger *msn_logger;
struct msn_logger_data {
- xmlnode *root;
- xmlnode *message;
+ PurpleXmlNode *root;
+ PurpleXmlNode *message;
const char *session_id;
int last_log;
GString *text;
@@ -450,7 +446,7 @@ struct msn_logger_data {
/* This function is really confusing. It makes baby rlaager cry...
In other news: "You lost a lot of blood but we found most of it."
*/
-static time_t msn_logger_parse_timestamp(xmlnode *message, struct tm **tm_out)
+static time_t msn_logger_parse_timestamp(PurpleXmlNode *message, struct tm **tm_out)
{
const char *datetime;
static struct tm tm2;
@@ -479,7 +475,7 @@ static time_t msn_logger_parse_timestamp(xmlnode *message, struct tm **tm_out)
}
#endif
- datetime = xmlnode_get_attrib(message, "DateTime");
+ datetime = purple_xmlnode_get_attrib(message, "DateTime");
if (!(datetime && *datetime))
{
purple_debug_error("MSN log timestamp parse",
@@ -500,7 +496,7 @@ static time_t msn_logger_parse_timestamp(xmlnode *message, struct tm **tm_out)
#endif
- date = xmlnode_get_attrib(message, "Date");
+ date = purple_xmlnode_get_attrib(message, "Date");
if (!(date && *date))
{
purple_debug_error("MSN log timestamp parse",
@@ -509,7 +505,7 @@ static time_t msn_logger_parse_timestamp(xmlnode *message, struct tm **tm_out)
return stamp;
}
- time = xmlnode_get_attrib(message, "Time");
+ time = purple_xmlnode_get_attrib(message, "Time");
if (!(time && *time))
{
purple_debug_error("MSN log timestamp parse",
@@ -627,15 +623,15 @@ static GList *msn_logger_list(PurpleLogType type, const char *sn, PurpleAccount
GError *error = NULL;
gchar *contents = NULL;
gsize length;
- xmlnode *root;
- xmlnode *message;
+ PurpleXmlNode *root;
+ PurpleXmlNode *message;
const char *old_session_id = "";
struct msn_logger_data *data = NULL;
g_return_val_if_fail(sn != NULL, NULL);
g_return_val_if_fail(account != NULL, NULL);
- if (strcmp(account->protocol_id, "prpl-msn"))
+ if (strcmp(purple_account_get_protocol_id(account), "prpl-msn"))
return NULL;
logdir = purple_prefs_get_string("/plugins/core/log_reader/msn/log_directory");
@@ -644,7 +640,7 @@ static GList *msn_logger_list(PurpleLogType type, const char *sn, PurpleAccount
if (!logdir || !*logdir)
return NULL;
- buddy = purple_find_buddy(account, sn);
+ buddy = purple_blist_find_buddy(account, sn);
if ((username = g_strdup(purple_account_get_string(
account, "log_reader_msn_log_folder", NULL)))) {
@@ -658,11 +654,11 @@ static GList *msn_logger_list(PurpleLogType type, const char *sn, PurpleAccount
return list;
}
} else {
- username = g_strdup(purple_normalize(account, account->username));
+ username = g_strdup(purple_normalize(account, purple_account_get_username(account)));
}
if (buddy) {
- savedfilename = purple_blist_node_get_string((PurpleBlistNode *)buddy,
+ savedfilename = purple_blist_node_get_string(PURPLE_BLIST_NODE(buddy),
"log_reader_msn_log_filename");
}
@@ -824,21 +820,21 @@ static GList *msn_logger_list(PurpleLogType type, const char *sn, PurpleAccount
* detected for both buddies.
*/
if (buddy && logfile) {
- PurpleBlistNode *node = (PurpleBlistNode *)buddy;
+ PurpleBlistNode *node = PURPLE_BLIST_NODE(buddy);
purple_blist_node_set_string(node, "log_reader_msn_log_filename", logfile);
g_free(logfile);
}
- root = xmlnode_from_str(contents, length);
+ root = purple_xmlnode_from_str(contents, length);
g_free(contents);
if (!root)
return list;
- for (message = xmlnode_get_child(root, "Message"); message;
- message = xmlnode_get_next_twin(message)) {
+ for (message = purple_xmlnode_get_child(root, "Message"); message;
+ message = purple_xmlnode_get_next_twin(message)) {
const char *session_id;
- session_id = xmlnode_get_attrib(message, "SessionID");
+ session_id = purple_xmlnode_get_attrib(message, "SessionID");
if (!session_id) {
purple_debug_error("MSN log parse",
"Error parsing message: %s\n", "SessionID missing");
@@ -882,7 +878,7 @@ static char * msn_logger_read (PurpleLog *log, PurpleLogReadFlags *flags)
{
struct msn_logger_data *data;
GString *text = NULL;
- xmlnode *message;
+ PurpleXmlNode *message;
if (flags != NULL)
*flags = PURPLE_LOG_READ_NO_NEWLINE;
@@ -911,22 +907,22 @@ static char * msn_logger_read (PurpleLog *log, PurpleLogReadFlags *flags)
}
for (message = data->message; message;
- message = xmlnode_get_next_twin(message)) {
+ message = purple_xmlnode_get_next_twin(message)) {
const char *new_session_id;
- xmlnode *text_node;
+ PurpleXmlNode *text_node;
const char *from_name = NULL;
const char *to_name = NULL;
- xmlnode *from;
- xmlnode *to;
+ PurpleXmlNode *from;
+ PurpleXmlNode *to;
enum name_guesses name_guessed = NAME_GUESS_UNKNOWN;
const char *their_name;
- struct tm *tm;
+ struct tm *tm = NULL;
char *timestamp;
char *tmp;
const char *style;
- new_session_id = xmlnode_get_attrib(message, "SessionID");
+ new_session_id = purple_xmlnode_get_attrib(message, "SessionID");
/* If this triggers, something is wrong with the XML. */
if (!new_session_id) {
@@ -942,16 +938,16 @@ static char * msn_logger_read (PurpleLog *log, PurpleLogReadFlags *flags)
break;
}
- text_node = xmlnode_get_child(message, "Text");
+ text_node = purple_xmlnode_get_child(message, "Text");
if (!text_node)
continue;
- from = xmlnode_get_child(message, "From");
+ from = purple_xmlnode_get_child(message, "From");
if (from) {
- xmlnode *user = xmlnode_get_child(from, "User");
+ PurpleXmlNode *user = purple_xmlnode_get_child(from, "User");
if (user) {
- from_name = xmlnode_get_attrib(user, "FriendlyName");
+ from_name = purple_xmlnode_get_attrib(user, "FriendlyName");
/* This saves a check later. */
if (!*from_name)
@@ -959,11 +955,11 @@ static char * msn_logger_read (PurpleLog *log, PurpleLogReadFlags *flags)
}
}
- to = xmlnode_get_child(message, "To");
+ to = purple_xmlnode_get_child(message, "To");
if (to) {
- xmlnode *user = xmlnode_get_child(to, "User");
+ PurpleXmlNode *user = purple_xmlnode_get_child(to, "User");
if (user) {
- to_name = xmlnode_get_attrib(user, "FriendlyName");
+ to_name = purple_xmlnode_get_attrib(user, "FriendlyName");
/* This saves a check later. */
if (!*to_name)
@@ -973,26 +969,23 @@ static char * msn_logger_read (PurpleLog *log, PurpleLogReadFlags *flags)
their_name = from_name;
if (from_name && purple_prefs_get_bool("/plugins/core/log_reader/use_name_heuristics")) {
- const char *friendly_name = purple_connection_get_display_name(log->account->gc);
+ const char *friendly_name = purple_connection_get_display_name(purple_account_get_connection(log->account));
if (friendly_name != NULL) {
int friendly_name_length = strlen(friendly_name);
const char *alias;
int alias_length;
- PurpleBuddy *buddy = purple_find_buddy(log->account, log->name);
+ PurpleBuddy *buddy = purple_blist_find_buddy(log->account, log->name);
gboolean from_name_matches;
gboolean to_name_matches;
if (buddy)
their_name = purple_buddy_get_alias(buddy);
- if (log->account->alias)
- {
- alias = log->account->alias;
+ alias = purple_account_get_private_alias(log->account);
+ if (alias) {
alias_length = strlen(alias);
- }
- else
- {
+ } else {
alias = "";
alias_length = 0;
}
@@ -1103,21 +1096,25 @@ static char * msn_logger_read (PurpleLog *log, PurpleLogReadFlags *flags)
text = g_string_append(text, ";\">");
}
- msn_logger_parse_timestamp(message, &tm);
-
- timestamp = g_strdup_printf("<font size=\"2\">(%02u:%02u:%02u)</font> ",
+ if (msn_logger_parse_timestamp(message, &tm)) {
+ timestamp = g_strdup_printf(
+ "<font size=\"2\">(%02u:%02u:%02u)</font> ",
tm->tm_hour, tm->tm_min, tm->tm_sec);
- text = g_string_append(text, timestamp);
- g_free(timestamp);
+ text = g_string_append(text, timestamp);
+ g_free(timestamp);
+ } else {
+ text = g_string_append(text,
+ "<font size=\"2\">(00:00:00)</font> ");
+ }
if (from_name) {
text = g_string_append(text, "<b>");
if (name_guessed == NAME_GUESS_ME) {
- if (log->account->alias)
- text = g_string_append(text, log->account->alias);
+ if (purple_account_get_private_alias(log->account))
+ text = g_string_append(text, purple_account_get_private_alias(log->account));
else
- text = g_string_append(text, log->account->username);
+ text = g_string_append(text, purple_account_get_username(log->account));
}
else if (name_guessed == NAME_GUESS_THEM)
text = g_string_append(text, their_name);
@@ -1130,9 +1127,9 @@ static char * msn_logger_read (PurpleLog *log, PurpleLogReadFlags *flags)
if (name_guessed != NAME_GUESS_UNKNOWN)
text = g_string_append(text, "</span>");
- style = xmlnode_get_attrib(text_node, "Style");
+ style = purple_xmlnode_get_attrib(text_node, "Style");
- tmp = xmlnode_get_data(text_node);
+ tmp = purple_xmlnode_get_data(text_node);
if (style && *style) {
text = g_string_append(text, "<span style=\"");
text = g_string_append(text, style);
@@ -1177,7 +1174,7 @@ static void msn_logger_finalize(PurpleLog *log)
data = log->logger_data;
if (data->last_log)
- xmlnode_free(data->root);
+ purple_xmlnode_free(data->root);
if (data->text)
g_string_free(data->text, FALSE);
@@ -1443,7 +1440,7 @@ static char * trillian_logger_read (PurpleLog *log, PurpleLogReadFlags *flags)
}
/* Load miscellaneous data. */
- buddy = purple_find_buddy(log->account, log->name);
+ buddy = purple_blist_find_buddy(log->account, log->name);
escaped = g_markup_escape_text(read, -1);
g_free(read);
@@ -1672,7 +1669,7 @@ static char * trillian_logger_read (PurpleLog *log, PurpleLogReadFlags *flags)
const char *acct_name;
line2++;
line = line2;
- acct_name = purple_account_get_alias(log->account);
+ acct_name = purple_account_get_private_alias(log->account);
if (!acct_name)
acct_name = purple_account_get_username(log->account);
@@ -1789,7 +1786,7 @@ static GList *qip_logger_list(PurpleLogType type, const char *sn, PurpleAccount
memset(&tm, 0, sizeof(tm));
/* QIP only supports ICQ. */
- if (strcmp(account->protocol_id, "prpl-icq"))
+ if (strcmp(purple_account_get_protocol_id(account), "prpl-icq"))
return NULL;
logdir = purple_prefs_get_string("/plugins/core/log_reader/qip/log_directory");
@@ -1806,7 +1803,7 @@ static GList *qip_logger_list(PurpleLogType type, const char *sn, PurpleAccount
if (!prpl_info->list_icon)
return NULL;
- username = g_strdup(purple_normalize(account, account->username));
+ username = g_strdup(purple_normalize(account, purple_account_get_username(account)));
filename = g_strdup_printf("%s.txt", purple_normalize(account, sn));
path = g_build_filename(logdir, username, "History", filename, NULL);
g_free(username);
@@ -1983,7 +1980,7 @@ static char *qip_logger_read(PurpleLog *log, PurpleLogReadFlags *flags)
contents = g_markup_escape_text(utf8_string, -1);
g_free(utf8_string);
- buddy = purple_find_buddy(log->account, log->name);
+ buddy = purple_blist_find_buddy(log->account, log->name);
/* Apply formatting... */
formatted = g_string_sized_new(data->length + 2);
@@ -2053,7 +2050,7 @@ static char *qip_logger_read(PurpleLog *log, PurpleLogReadFlags *flags)
}
} else {
const char *acct_name;
- acct_name = purple_account_get_alias(log->account);
+ acct_name = purple_account_get_private_alias(log->account);
if (!acct_name)
acct_name = purple_account_get_username(log->account);
@@ -2226,7 +2223,6 @@ static GList *amsn_logger_parse_file(char *filename, const char *sn, PurpleAccou
log->logger = amsn_logger;
log->logger_data = data;
list = g_list_prepend(list, log);
- found_start = FALSE;
purple_debug_info("aMSN logger",
"Found log for %s:"
@@ -2261,10 +2257,10 @@ static GList *amsn_logger_list(PurpleLogType type, const char *sn, PurpleAccount
return NULL;
/* aMSN only works with MSN/WLM */
- if (strcmp(account->protocol_id, "prpl-msn"))
+ if (strcmp(purple_account_get_protocol_id(account), "prpl-msn"))
return NULL;
- username = g_strdup(purple_normalize(account, account->username));
+ username = g_strdup(purple_normalize(account, purple_account_get_username(account)));
buddy_log = g_strdup_printf("%s.log", purple_normalize(account, sn));
log_path = g_build_filename(logdir, username, "logs", NULL);
@@ -2481,6 +2477,7 @@ init_plugin(PurplePlugin *plugin)
static void log_reader_init_prefs(void) {
char *path;
#ifdef _WIN32
+ const gchar *reg_key;
char *folder;
gboolean found = FALSE;
#endif
@@ -2571,7 +2568,11 @@ static void log_reader_init_prefs(void) {
*/
path = NULL;
- if ((folder = wpurple_read_reg_string(HKEY_CLASSES_ROOT, "Trillian.SkinZip\\shell\\Add\\command\\", NULL))) {
+ folder = NULL;
+ reg_key = "Trillian.SkinZip\\shell\\Add\\command\\";
+ if (wpurple_reg_val_exists(HKEY_CLASSES_ROOT, reg_key, NULL))
+ folder = wpurple_read_reg_string(HKEY_CLASSES_ROOT, reg_key, NULL);
+ if (folder) {
char *value = folder;
char *temp;
@@ -2609,7 +2610,7 @@ static void log_reader_init_prefs(void) {
/* Read talk.ini file to find the log directory. */
GError *error = NULL;
-#if 0 && GLIB_CHECK_VERSION(2,6,0) /* FIXME: Not tested yet. */
+#if 0 /* FIXME: Not tested yet. */
GKeyFile *key_file;
purple_debug_info("Trillian talk.ini read", "Reading %s\n", path);
@@ -2636,18 +2637,24 @@ static void log_reader_init_prefs(void) {
g_key_file_free(key_file);
}
-#else /* !GLIB_CHECK_VERSION(2,6,0) */
+#else
gchar *contents = NULL;
- purple_debug_info("Trillian talk.ini read",
- "Reading %s\n", path);
- if (!g_file_get_contents(path, &contents, NULL, &error)) {
+ if (g_file_test(path, G_FILE_TEST_IS_REGULAR)) {
+ purple_debug_info("Trillian talk.ini read",
+ "Reading %s\n", path);
+ } else {
+ g_free(path);
+ path = NULL;
+ }
+
+ if (path && !g_file_get_contents(path, &contents, NULL, &error)) {
purple_debug_error("Trillian talk.ini read",
"Error reading talk.ini: %s\n",
(error && error->message) ? error->message : "Unknown error");
if (error)
g_error_free(error);
- } else {
+ } else if (contents) {
char *cursor, *line;
line = cursor = contents;
while (*cursor) {
@@ -2672,7 +2679,7 @@ static void log_reader_init_prefs(void) {
g_free(contents);
}
g_free(path);
-#endif /* !GLIB_CHECK_VERSION(2,6,0) */
+#endif
} /* path */
if (!found) {
@@ -2938,8 +2945,7 @@ get_plugin_pref_frame(PurplePlugin *plugin)
static PurplePluginUiInfo prefs_info = {
get_plugin_pref_frame,
- 0, /* page_num (reserved) */
- NULL, /* frame (reserved) */
+ NULL,
/* padding */
NULL,
diff --git a/libpurple/plugins/mono/Makefile.am b/libpurple/plugins/mono/Makefile.am
index 6e334828e7..b9ee99681f 100644
--- a/libpurple/plugins/mono/Makefile.am
+++ b/libpurple/plugins/mono/Makefile.am
@@ -5,7 +5,7 @@ mono_sources = GetBuddyBack.cs \
EXTRA_DIST = $(mono_sources)
-monodir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+monodir = @PURPLE_PLUGINDIR@
mono_SCRIPTS = MPlugin.dll GetBuddyBack.dll
mono_build_sources = $(addprefix $(srcdir)/, $(mono_sources))
diff --git a/libpurple/plugins/mono/api/Makefile.am b/libpurple/plugins/mono/api/Makefile.am
index 6af08dfc6e..3122fc5924 100644
--- a/libpurple/plugins/mono/api/Makefile.am
+++ b/libpurple/plugins/mono/api/Makefile.am
@@ -1,4 +1,4 @@
-monodir=$(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+monodir=@PURPLE_PLUGINDIR@
mono_sources = \
BlistNode.cs \
diff --git a/libpurple/plugins/mono/loader/Makefile.am b/libpurple/plugins/mono/loader/Makefile.am
index cb59a6e6ea..ee7a8499c7 100644
--- a/libpurple/plugins/mono/loader/Makefile.am
+++ b/libpurple/plugins/mono/loader/Makefile.am
@@ -1,4 +1,4 @@
-plugindir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+plugindir = @PURPLE_PLUGINDIR@
plugin_LTLIBRARIES = mono.la
@@ -12,7 +12,7 @@ mono_la_SOURCES = \
blist-glue.c \
status-glue.c
-mono_la_LDFLAGS = -module -avoid-version
+mono_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
mono_la_LIBADD = $(MONO_LIBS)
diff --git a/libpurple/plugins/mono/loader/blist-glue.c b/libpurple/plugins/mono/loader/blist-glue.c
index a424914036..303e77da0a 100644
--- a/libpurple/plugins/mono/loader/blist-glue.c
+++ b/libpurple/plugins/mono/loader/blist-glue.c
@@ -1,5 +1,5 @@
#include <string.h>
-#include "blist.h"
+#include "buddylist.h"
#include "mono-helper.h"
#include "mono-glue.h"
@@ -14,7 +14,7 @@ MonoObject* purple_blist_build_buddy_object(void* data)
{
MonoObject *obj = NULL;
- PurpleBuddy *buddy = (PurpleBuddy*)data;
+ PurpleBuddy *buddy = PURPLE_BUDDY(data);
obj = ml_create_api_object("Buddy");
g_return_val_if_fail(obj != NULL, NULL);
diff --git a/libpurple/plugins/mono/loader/mono-helper.c b/libpurple/plugins/mono/loader/mono-helper.c
index 4404544409..1a1600e83c 100644
--- a/libpurple/plugins/mono/loader/mono-helper.c
+++ b/libpurple/plugins/mono/loader/mono-helper.c
@@ -14,7 +14,6 @@
#include <string.h>
#include "mono-helper.h"
#include "mono-glue.h"
-#include "value.h"
#include "debug.h"
static gboolean _runtime_active = FALSE;
@@ -159,20 +158,15 @@ gboolean ml_is_api_dll(MonoImage *image)
return FALSE;
}
-MonoObject* ml_object_from_purple_type(PurpleType type, gpointer data)
-{
- return NULL;
-}
-
-MonoObject* ml_object_from_purple_subtype(PurpleSubType type, gpointer data)
+MonoObject* ml_object_from_purple_type(GType type, gpointer data)
{
MonoObject *obj = NULL;
switch (type) {
- case PURPLE_SUBTYPE_BLIST_BUDDY:
+ case PURPLE_TYPE_BUDDY:
obj = purple_blist_build_buddy_object(data);
break;
- case PURPLE_SUBTYPE_STATUS:
+ case PURPLE_TYPE_STATUS:
obj = purple_status_build_status_object(data);
break;
default:
diff --git a/libpurple/plugins/mono/loader/mono-helper.h b/libpurple/plugins/mono/loader/mono-helper.h
index 1d454388d5..5b0c140636 100644
--- a/libpurple/plugins/mono/loader/mono-helper.h
+++ b/libpurple/plugins/mono/loader/mono-helper.h
@@ -8,7 +8,6 @@
#include <mono/metadata/debug-helpers.h>
#include <mono/metadata/tokentype.h>
#include "plugin.h"
-#include "value.h"
#include "debug.h"
typedef struct {
@@ -50,9 +49,7 @@ void ml_set_domain(MonoDomain *d);
void ml_init_internal_calls(void);
-MonoObject* ml_object_from_purple_type(PurpleType type, gpointer data);
-
-MonoObject* ml_object_from_purple_subtype(PurpleSubType type, gpointer data);
+MonoObject* ml_object_from_purple_type(GType type, gpointer data);
MonoObject* ml_create_api_object(char *class_name);
diff --git a/libpurple/plugins/mono/loader/signal-glue.c b/libpurple/plugins/mono/loader/signal-glue.c
index c6b4be01c6..0ffce4669b 100644
--- a/libpurple/plugins/mono/loader/signal-glue.c
+++ b/libpurple/plugins/mono/loader/signal-glue.c
@@ -1,15 +1,14 @@
#include "mono-glue.h"
#include "mono-helper.h"
#include "debug.h"
-#include "blist.h"
+#include "buddylist.h"
#include "signals.h"
-#include "value.h"
typedef struct {
MonoObject *func;
char *signal;
- PurpleValue **values;
- PurpleValue *ret_value;
+ GType *types;
+ GType ret_type;
int num_vals;
} SignalData;
@@ -30,15 +29,9 @@ static gpointer dispatch_callback(SignalData *sig_data, int num_vals, ...)
array = mono_array_new(ml_get_domain(), mono_get_object_class(), num_vals);
for (i = 0; i < num_vals; i++) {
- if (purple_value_get_type(sig_data->values[i]) == PURPLE_TYPE_SUBTYPE) {
- purple_obj = va_arg(args, gpointer);
- obj = ml_object_from_purple_subtype(purple_value_get_subtype(sig_data->values[i]), purple_obj);
- mono_array_set(array, MonoObject*, i, obj);
- } else {
- purple_obj = va_arg(args, gpointer);
- obj = ml_object_from_purple_type(purple_value_get_type(sig_data->values[i]), purple_obj);
- mono_array_set(array, MonoObject*, i, obj);
- }
+ purple_obj = va_arg(args, gpointer);
+ obj = ml_object_from_purple_type(sig_data->types[i], purple_obj);
+ mono_array_set(array, MonoObject*, i, obj);
}
va_end(args);
@@ -77,7 +70,7 @@ int purple_signal_connect_glue(MonoObject* h, MonoObject *plugin, MonoString *si
sig_data->func = func;
sig_data->signal = sig;
- purple_signal_get_values(*instance, sig, &sig_data->ret_value, &sig_data->num_vals, &sig_data->values);
+ purple_signal_get_types(*instance, sig, &sig_data->ret_type, &sig_data->num_vals, &sig_data->types);
klass = mono_object_get_class(plugin);
@@ -88,17 +81,16 @@ int purple_signal_connect_glue(MonoObject* h, MonoObject *plugin, MonoString *si
return purple_signal_connect(*instance, sig, (gpointer)klass, get_callback(sig_data), (gpointer)sig_data);
}
-static int determine_index(PurpleType type)
+static int determine_index(GType type)
{
switch (type) {
- case PURPLE_TYPE_SUBTYPE:
- case PURPLE_TYPE_STRING:
- case PURPLE_TYPE_OBJECT:
- case PURPLE_TYPE_POINTER:
- case PURPLE_TYPE_BOXED:
+ case G_TYPE_STRING:
+ case G_TYPE_POINTER:
return 1;
break;
default:
+ if (G_TYPE_IS_OBJECT(type) || G_TYPE_IS_BOXED(type))
+ return 1;
return type;
break;
}
@@ -118,13 +110,13 @@ static PurpleCallback get_callback(SignalData *sig_data)
{
int i, index = 0;
- if (sig_data->ret_value == NULL)
+ if (sig_data->ret_type == NULL)
index = 0;
else
- index = determine_index(purple_value_get_type(sig_data->ret_value));
+ index = determine_index(sig_data->ret_type);
for (i = 0; i < sig_data->num_vals; i++) {
- index += determine_index(purple_value_get_type(sig_data->values[i]));
+ index += determine_index(sig_data->types[i]);
}
purple_debug(PURPLE_DEBUG_INFO, "mono", "get_callback index = %d\n", index);
diff --git a/libpurple/plugins/newline.c b/libpurple/plugins/newline.c
deleted file mode 100644
index 25ba535140..0000000000
--- a/libpurple/plugins/newline.c
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Displays messages on a new line, below the nick
- * Copyright (C) 2004 Stu Tomlinson <stu@nosnilmot.com>
- *
- * 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 <string.h>
-
-#include <conversation.h>
-#include <debug.h>
-#include <plugin.h>
-#include <signals.h>
-#include <util.h>
-#include <version.h>
-
-static gboolean
-addnewline_msg_cb(PurpleAccount *account, char *sender, char **message,
- PurpleConversation *conv, int *flags, void *data)
-{
- if (((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) &&
- !purple_prefs_get_bool("/plugins/core/newline/im")) ||
- ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) &&
- !purple_prefs_get_bool("/plugins/core/newline/chat")))
- return FALSE;
-
- if (g_ascii_strncasecmp(*message, "/me ", strlen("/me "))) {
- char *tmp = g_strdup_printf("<br/>%s", *message);
- g_free(*message);
- *message = tmp;
- }
-
- return FALSE;
-}
-
-static PurplePluginPrefFrame *
-get_plugin_pref_frame(PurplePlugin *plugin) {
- PurplePluginPrefFrame *frame;
- PurplePluginPref *ppref;
-
- frame = purple_plugin_pref_frame_new();
-
- ppref = purple_plugin_pref_new_with_name_and_label(
- "/plugins/core/newline/im", _("Add new line in IMs"));
- purple_plugin_pref_frame_add(frame, ppref);
-
- ppref = purple_plugin_pref_new_with_name_and_label(
- "/plugins/core/newline/chat", _("Add new line in Chats"));
- purple_plugin_pref_frame_add(frame, ppref);
-
- return frame;
-}
-
-
-static gboolean
-plugin_load(PurplePlugin *plugin)
-{
- void *conversation = purple_conversations_get_handle();
-
- purple_signal_connect(conversation, "writing-im-msg",
- plugin, PURPLE_CALLBACK(addnewline_msg_cb), NULL);
- purple_signal_connect(conversation, "writing-chat-msg",
- plugin, PURPLE_CALLBACK(addnewline_msg_cb), NULL);
-
- return TRUE;
-}
-
-static PurplePluginUiInfo prefs_info = {
- get_plugin_pref_frame,
- 0, /* page_num (Reserved) */
- NULL, /* frame (Reserved) */
- /* Padding */
- NULL,
- NULL,
- NULL,
- NULL
-};
-
-static PurplePluginInfo info =
-{
- PURPLE_PLUGIN_MAGIC, /**< magic */
- PURPLE_MAJOR_VERSION, /**< major version */
- PURPLE_MINOR_VERSION, /**< minor version */
- PURPLE_PLUGIN_STANDARD, /**< type */
- NULL, /**< ui_requirement */
- 0, /**< flags */
- NULL, /**< dependencies */
- PURPLE_PRIORITY_DEFAULT, /**< priority */
-
- "core-plugin_pack-newline", /**< id */
- N_("New Line"), /**< name */
- DISPLAY_VERSION, /**< version */
- N_("Prepends a newline to displayed message."), /**< summary */
- N_("Prepends a newline to messages so that the "
- "rest of the message appears below the "
- "username in the conversation window."), /**< description */
- "Stu Tomlinson <stu@nosnilmot.com>", /**< author */
- PURPLE_WEBSITE, /**< homepage */
-
- plugin_load, /**< load */
- NULL, /**< unload */
- NULL, /**< destroy */
-
- NULL, /**< ui_info */
- NULL, /**< extra_info */
- &prefs_info, /**< prefs_info */
- NULL, /**< actions */
-
- /* padding */
- NULL,
- NULL,
- NULL,
- NULL
-};
-
-static void
-init_plugin(PurplePlugin *plugin) {
- purple_prefs_add_none("/plugins/core/newline");
- purple_prefs_add_bool("/plugins/core/newline/im", TRUE);
- purple_prefs_add_bool("/plugins/core/newline/chat", TRUE);
-}
-
-PURPLE_INIT_PLUGIN(newline, init_plugin, info)
diff --git a/libpurple/plugins/notify_example.c b/libpurple/plugins/notify_example.c
index 3a62417520..7423b4880b 100644
--- a/libpurple/plugins/notify_example.c
+++ b/libpurple/plugins/notify_example.c
@@ -20,65 +20,44 @@
*
*/
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include <glib.h>
-
-/* This will prevent compiler errors in some instances and is better explained in the
- * how-to documents on the wiki */
-#ifndef G_GNUC_NULL_TERMINATED
-# if __GNUC__ >= 4
-# define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
-# else
-# define G_GNUC_NULL_TERMINATED
-# endif
-#endif
-
-/* This is the required definition of PURPLE_PLUGINS as required for a plugin,
- * but we protect it with an #ifndef because config.h may define it for us
- * already and this would cause an unneeded compiler warning. */
-#ifndef PURPLE_PLUGINS
-# define PURPLE_PLUGINS
-#endif
+/* When writing a third-party plugin, do not include libpurple's internal.h
+ * included below. This file is for internal libpurple use only. We're including
+ * it here for our own convenience. */
+#include "internal.h"
+
+/* This file defines PURPLE_PLUGINS and includes all the libpurple headers */
+#include <purple.h>
#define PLUGIN_ID "core-notifyexample"
#define PLUGIN_AUTHOR "John Bailey <rekkanoryo@cpw.pidgin.im>"
-#include <notify.h>
-#include <plugin.h>
-#include <version.h>
-
-static PurplePlugin *notify_example = NULL;
-
/* The next four functions and the calls within them should cause dialog boxes to appear
* when you select the plugin action from the Tools->Notify Example menu */
static void
notify_error_cb(PurplePluginAction *action)
{
- purple_notify_error(notify_example, "Test Notification", "Test Notification",
- "This is a test error notification");
+ purple_notify_error(action->plugin, "Test Notification", "Test Notification",
+ "This is a test error notification", NULL);
}
static void
notify_info_cb(PurplePluginAction *action)
{
- purple_notify_info(notify_example, "Test Notification", "Test Notification",
- "This is a test informative notification");
+ purple_notify_info(action->plugin, "Test Notification", "Test Notification",
+ "This is a test informative notification", NULL);
}
static void
notify_warn_cb(PurplePluginAction *action)
{
- purple_notify_warning(notify_example, "Test Notification", "Test Notification",
- "This is a test warning notification");
+ purple_notify_warning(action->plugin, "Test Notification", "Test Notification",
+ "This is a test warning notification", NULL);
}
static void
notify_format_cb(PurplePluginAction *action)
{
- purple_notify_formatted(notify_example, "Test Notification", "Test Notification",
+ purple_notify_formatted(action->plugin, "Test Notification", "Test Notification",
"Test Notification",
"<I>This is a test notification with formatted text.</I>", NULL, NULL);
}
@@ -87,7 +66,7 @@ static void
notify_uri_cb(PurplePluginAction *action)
{
/* This one should open your web browser of choice. */
- purple_notify_uri(notify_example, "http://www.pidgin.im/");
+ purple_notify_uri(action->plugin, "https://pidgin.im/");
}
static GList *
@@ -117,9 +96,6 @@ plugin_actions(PurplePlugin *plugin, gpointer context)
static gboolean
plugin_load(PurplePlugin *plugin)
{
- /* we need a handle for all the notify calls */
- notify_example = plugin;
-
return TRUE;
}
@@ -139,7 +115,7 @@ static PurplePluginInfo info = {
"Notify API Example", /* summary */
"Notify API Example", /* description */
PLUGIN_AUTHOR, /* author */
- "http://pidgin.im", /* homepage */
+ "https://pidgin.im", /* homepage */
plugin_load, /* load */
NULL, /* unload */
diff --git a/libpurple/plugins/offlinemsg.c b/libpurple/plugins/offlinemsg.c
index b58da95a8b..7a4beb5445 100644
--- a/libpurple/plugins/offlinemsg.c
+++ b/libpurple/plugins/offlinemsg.c
@@ -29,7 +29,7 @@
/* Purple headers */
#include <version.h>
-#include <blist.h>
+#include <buddylist.h>
#include <conversation.h>
#include <core.h>
#include <debug.h>
@@ -67,9 +67,9 @@ discard_data(OfflineMsg *offline)
static void
cancel_poune(OfflineMsg *offline)
{
- purple_conversation_set_data(offline->conv, "plugin_pack:offlinemsg",
+ g_object_set_data(G_OBJECT(offline->conv), "plugin_pack:offlinemsg",
GINT_TO_POINTER(OFFLINE_MSG_NO));
- purple_conv_im_send_with_flags(PURPLE_CONV_IM(offline->conv), offline->message, 0);
+ purple_conversation_send_with_flags(offline->conv, offline->message, 0);
discard_data(offline);
}
@@ -80,6 +80,7 @@ record_pounce(OfflineMsg *offline)
PurplePounceEvent event;
PurplePounceOption option;
PurpleConversation *conv;
+ char *temp;
event = PURPLE_POUNCE_SIGNON;
option = PURPLE_POUNCE_OPTION_NONE;
@@ -88,36 +89,43 @@ record_pounce(OfflineMsg *offline)
event, option);
purple_pounce_action_set_enabled(pounce, "send-message", TRUE);
- purple_pounce_action_set_attribute(pounce, "send-message", "message", offline->message);
+
+ temp = g_strdup_printf("(%s) %s", _("Offline message"),
+ offline->message);
+ purple_pounce_action_set_attribute(pounce, "send-message", "message",
+ temp);
+ g_free(temp);
conv = offline->conv;
- if (!purple_conversation_get_data(conv, "plugin_pack:offlinemsg"))
- purple_conversation_write(conv, NULL, _("The rest of the messages will be saved "
- "as pounces. You can edit/delete the pounce from the `Buddy "
- "Pounce' dialog."),
- PURPLE_MESSAGE_SYSTEM, time(NULL));
- purple_conversation_set_data(conv, "plugin_pack:offlinemsg",
+ if (!g_object_get_data(G_OBJECT(conv), "plugin_pack:offlinemsg")) {
+ purple_conversation_write_system_message(conv,
+ _("The rest of the messages will be saved "
+ "as pounces. You can edit/delete the pounce from the `Buddy "
+ "Pounce' dialog."), 0);
+ }
+ g_object_set_data(G_OBJECT(conv), "plugin_pack:offlinemsg",
GINT_TO_POINTER(OFFLINE_MSG_YES));
- purple_conv_im_write(PURPLE_CONV_IM(conv), offline->who, offline->message,
- PURPLE_MESSAGE_SEND, time(NULL));
+ /* TODO: use a reference to a PurpleMessage */
+ purple_conversation_write_message(conv,
+ purple_message_new_outgoing(offline->who, offline->message, 0));
discard_data(offline);
}
static void
-sending_msg_cb(PurpleAccount *account, const char *who, char **message, gpointer handle)
+sending_msg_cb(PurpleAccount *account, PurpleMessage *msg, gpointer handle)
{
PurpleBuddy *buddy;
OfflineMsg *offline;
PurpleConversation *conv;
OfflineMessageSetting setting;
+ const gchar *who = purple_message_get_recipient(msg);
- if (message == NULL || *message == NULL ||
- **message == '\0')
+ if (purple_message_is_empty(msg))
return;
- buddy = purple_find_buddy(account, who);
+ buddy = purple_blist_find_buddy(account, who);
if (!buddy)
return;
@@ -131,13 +139,12 @@ sending_msg_cb(PurpleAccount *account, const char *who, char **message, gpointer
return;
}
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
- who, account);
+ conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(who, account));
if (!conv)
return;
- setting = GPOINTER_TO_INT(purple_conversation_get_data(conv, "plugin_pack:offlinemsg"));
+ setting = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "plugin_pack:offlinemsg"));
if (setting == OFFLINE_MSG_NO)
return;
@@ -145,8 +152,8 @@ sending_msg_cb(PurpleAccount *account, const char *who, char **message, gpointer
offline->conv = conv;
offline->account = account;
offline->who = g_strdup(who);
- offline->message = *message;
- *message = NULL;
+ offline->message = g_strdup(purple_message_get_contents(msg));
+ purple_message_set_contents(msg, NULL);
if (purple_prefs_get_bool(PREF_ALWAYS) || setting == OFFLINE_MSG_YES)
record_pounce(offline);
@@ -159,8 +166,7 @@ sending_msg_cb(PurpleAccount *account, const char *who, char **message, gpointer
purple_request_action(handle, _("Offline Message"), ask,
_("You can edit/delete the pounce from the `Buddy Pounces' dialog"),
- 0,
- offline->account, offline->who, offline->conv,
+ 0, purple_request_cpar_from_conversation(offline->conv),
offline, 2,
_("Yes"), record_pounce,
_("No"), cancel_poune);
@@ -202,7 +208,6 @@ get_plugin_pref_frame(PurplePlugin *plugin)
static PurplePluginUiInfo prefs_info = {
get_plugin_pref_frame,
- 0,
NULL,
/* padding */
diff --git a/libpurple/plugins/one_time_password.c b/libpurple/plugins/one_time_password.c
index caf29212a9..ed8fe64271 100644
--- a/libpurple/plugins/one_time_password.c
+++ b/libpurple/plugins/one_time_password.c
@@ -46,7 +46,7 @@ signed_on_cb(PurpleConnection *conn, void *data)
purple_account_get_username(account),
purple_account_get_protocol_name(account));
- purple_account_set_password(account, NULL);
+ purple_account_set_password(account, NULL, NULL, NULL);
/* TODO: Do we need to somehow clear conn->password ? */
}
}
diff --git a/libpurple/plugins/perl/Makefile.am b/libpurple/plugins/perl/Makefile.am
index 142f73b745..fb84fcaa84 100644
--- a/libpurple/plugins/perl/Makefile.am
+++ b/libpurple/plugins/perl/Makefile.am
@@ -1,11 +1,11 @@
-plugindir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+plugindir = @PURPLE_PLUGINDIR@
perl_dirs = common
plugin_LTLIBRARIES = perl.la
-perl_la_LDFLAGS = -module -avoid-version
-perl_la_LIBADD = $(GLIB_LIBS) $(PERL_LIBS)
+perl_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+perl_la_LIBADD = @PURPLE_LIBS@ $(PERL_LIBS)
perl_la_SOURCES = \
perl.c \
perl-common.c \
@@ -47,9 +47,7 @@ common_sources = \
common/Connection.xs \
common/Conversation.xs \
common/Debug.xs \
- common/FT.xs \
common/Idle.xs \
- common/ImgStore.xs \
common/Log.xs \
common/Makefile.PL.in \
common/Network.xs \
@@ -58,7 +56,7 @@ common_sources = \
common/PluginPref.xs \
common/Pounce.xs \
common/Prefs.xs \
- common/Privacy.xs \
+ common/Presence.xs \
common/Proxy.xs \
common/Prpl.xs \
common/Purple.pm \
@@ -69,12 +67,12 @@ common_sources = \
common/SavedStatuses.xs \
common/Server.xs \
common/Signal.xs \
- common/Smiley.xs \
common/Sound.xs \
common/Status.xs \
common/Stringref.xs \
common/Util.xs \
common/Whiteboard.xs \
+ common/Xfer.xs \
common/XMLNode.xs \
common/module.h \
common/typemap
@@ -92,14 +90,14 @@ EXTRA_DIST = \
$(perl_scripts)
common/Makefile: common/Makefile.PL
- @if test "x${top_srcdir}" != "x${top_builddir}"; then \
+ $(AM_V_GEN)if test "x${top_srcdir}" != "x${top_builddir}"; then \
for f in ${common_sources}; do \
srcloc=${srcdir}; \
case $$srcloc in /*) ;; *) srcloc=../${srcdir} ;; esac; \
${LN_S} -f $$srcloc/$$f $$f; \
done; \
fi
- @cd common && $(perlpath) Makefile.PL
+ $(AM_V_at)cd common && $(perlpath) Makefile.PL > /dev/null
common/Makefile.PL: common/Makefile.PL.in $(top_builddir)/config.status
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)
@@ -110,8 +108,8 @@ all-local: common/Makefile
if [ ! -f Makefile ]; then \
$(perlpath) Makefile.PL; \
fi && \
- ($(MAKE) CC="$(CC)" CCFLAGS="$(PERL_CFLAGS) $(CFLAGS)" $(PERL_EXTRA_OPTS) || \
- $(MAKE) CC="$(CC)" CCFLAGS="$(PERL_CFLAGS) $(CFLAGS)" $(PERL_EXTRA_OPTS)) && \
+ ($(MAKE) CC="@$(abs_top_srcdir)/libpurple/tag.sh CC $(CC)" LD="@$(abs_top_srcdir)/libpurple/tag.sh LD $(CC)" PERLRUN="@$(abs_top_srcdir)/libpurple/tag.sh PERL $(PERL)" CCFLAGS="$(PERL_CFLAGS) $(CFLAGS)" CP="@cp" RM_F="@rm -f" CHMOD="@chmod" $(PERL_EXTRA_OPTS) || \
+ $(MAKE) CC="@$(abs_top_srcdir)/libpurple/tag.sh CC $(CC)" LD="@$(abs_top_srcdir)/libpurple/tag.sh LD $(CC)" PERLRUN="@$(abs_top_srcdir)/libpurple/tag.sh PERL $(PERL)" CCFLAGS="$(PERL_CFLAGS) $(CFLAGS)" CP="@cp" RM_F="@rm -f" CHMOD="@chmod" $(PERL_EXTRA_OPTS)) && \
cd ..; \
done
@@ -165,8 +163,8 @@ AM_CPPFLAGS = \
-I$(top_srcdir) \
-I$(top_srcdir)/libpurple \
-I$(top_builddir)/libpurple \
- -DLIBDIR=\"$(libdir)/purple-$(PURPLE_MAJOR_VERSION)\" \
$(DEBUG_CFLAGS) \
$(GLIB_CFLAGS) \
$(PLUGIN_CFLAGS) \
- $(PERL_CFLAGS)
+ $(PERL_CFLAGS) \
+ -Wno-float-equal
diff --git a/libpurple/plugins/perl/Makefile.mingw b/libpurple/plugins/perl/Makefile.mingw
index 80b483e62e..d3dac8761f 100755..100644
--- a/libpurple/plugins/perl/Makefile.mingw
+++ b/libpurple/plugins/perl/Makefile.mingw
@@ -27,11 +27,11 @@ INCLUDE_PATHS += -I. \
-I$(GTK_TOP)/include \
-I$(GTK_TOP)/include/glib-2.0 \
-I$(GTK_TOP)/lib/glib-2.0/include \
- -I$(PERL_LIB_TOP)/CORE
+ -I$(PERL_LIB_TOP)/include
LIB_PATHS += -L$(GTK_TOP)/lib \
-L$(PURPLE_TOP) \
- -L$(PERL_LIB_TOP)
+ -L$(PERL_LIB_TOP)/lib
##
## SOURCES, OBJECTS
@@ -62,11 +62,11 @@ include $(PIDGIN_COMMON_RULES)
.PHONY: all install clean
all: $(TARGET).dll
- $(MAKE) -C ./common -f $(MINGW_MAKEFILE)
+ $(MAKE_at) $(MAKE) -C ./common -f $(MINGW_MAKEFILE)
install: all $(PURPLE_INSTALL_PLUGINS_DIR)
cp $(TARGET).dll $(PURPLE_INSTALL_PLUGINS_DIR)
- $(MAKE) -C ./common -f $(MINGW_MAKEFILE) install
+ $(MAKE_at) $(MAKE) -C ./common -f $(MINGW_MAKEFILE) install
$(OBJECTS): $(PURPLE_CONFIG_H)
@@ -82,6 +82,6 @@ $(TARGET).dll $(TARGET).dll.a: $(PURPLE_DLL).a $(OBJECTS)
clean:
rm -rf $(OBJECTS)
rm -rf $(TARGET).dll $(TARGET).dll.a
- $(MAKE) -C ./common -f $(MINGW_MAKEFILE) clean
+ $(MAKE_at) $(MAKE) -C ./common -f $(MINGW_MAKEFILE) clean
include $(PIDGIN_COMMON_TARGETS)
diff --git a/libpurple/plugins/perl/common/Account.xs b/libpurple/plugins/perl/common/Account.xs
index 4976b84299..ea9b5b56cf 100644
--- a/libpurple/plugins/perl/common/Account.xs
+++ b/libpurple/plugins/perl/common/Account.xs
@@ -1,4 +1,56 @@
#include "module.h"
+#include "../perl-handlers.h"
+
+MODULE = Purple::Account PACKAGE = Purple::Accounts PREFIX = purple_accounts_
+PROTOTYPES: ENABLE
+
+void
+purple_accounts_add(account)
+ Purple::Account account
+
+void
+purple_accounts_remove(account)
+ Purple::Account account
+
+void
+purple_accounts_delete(account)
+ Purple::Account account
+
+void
+purple_accounts_reorder(account, new_index)
+ Purple::Account account
+ size_t new_index
+
+void
+purple_accounts_get_all()
+PREINIT:
+ GList *l;
+PPCODE:
+ for (l = purple_accounts_get_all(); l != NULL; l = l->next) {
+ XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Account")));
+ }
+
+void
+purple_accounts_get_all_active()
+PREINIT:
+ GList *list, *iter;
+PPCODE:
+ list = purple_accounts_get_all_active();
+ for (iter = list; iter != NULL; iter = iter->next) {
+ XPUSHs(sv_2mortal(purple_perl_bless_object(iter->data, "Purple::Account")));
+ }
+ g_list_free(list);
+
+void
+purple_accounts_restore_current_statuses()
+
+Purple::Account
+purple_accounts_find(name, protocol)
+ const char * name
+ const char * protocol
+
+Purple::Handle
+purple_accounts_get_handle()
MODULE = Purple::Account PACKAGE = Purple::Account PREFIX = purple_account_
PROTOTYPES: ENABLE
@@ -15,10 +67,6 @@ purple_account_new(class, username, protocol_id)
username, protocol_id
void
-purple_account_destroy(account)
- Purple::Account account
-
-void
purple_account_connect(account)
Purple::Account account
@@ -44,12 +92,16 @@ purple_account_set_username(account, username)
const char * username
void
-purple_account_set_password(account, password)
+purple_account_set_password(account, password, func, data = 0)
Purple::Account account
const char * password
+ SV *func
+ SV *data
+CODE:
+ purple_perl_account_set_password(account, password, func, data);
void
-purple_account_set_alias(account, alias)
+purple_account_set_private_alias(account, alias)
Purple::Account account
const char * alias
@@ -130,12 +182,16 @@ const char *
purple_account_get_username(account)
Purple::Account account
-const char *
-purple_account_get_password(account)
+void
+purple_account_get_password(account, func, data = 0)
Purple::Account account
+ SV *func
+ SV *data
+CODE:
+ purple_perl_account_get_password(account, func, data);
const char *
-purple_account_get_alias(account)
+purple_account_get_private_alias(account)
Purple::Account account
const char *
@@ -199,9 +255,10 @@ purple_account_destroy_log(account)
Purple::Account account
void
-purple_account_add_buddies(account, list)
+purple_account_add_buddies(account, list, message)
Purple::Account account
SV * list
+ const char *message
PREINIT:
GList *t_GL;
int i, t_len;
@@ -212,13 +269,14 @@ PPCODE:
for (i = 0; i <= t_len; i++)
t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(list), i, 0)));
- purple_account_add_buddies(account, t_GL);
+ purple_account_add_buddies(account, t_GL, message);
g_list_free(t_GL);
void
-purple_account_add_buddy(account, buddy)
- Purple::Account account
- Purple::BuddyList::Buddy buddy
+purple_account_add_buddy(account, buddy, message)
+ Purple::Account account
+ Purple::BuddyList::Buddy buddy
+ const char * message
void
purple_account_change_password(account, a, b)
@@ -262,53 +320,34 @@ purple_account_remove_group(account, group)
Purple::Account account
Purple::BuddyList::Group group
-MODULE = Purple::Account PACKAGE = Purple::Accounts PREFIX = purple_accounts_
+MODULE = Purple::Account PACKAGE = Purple::Account::Privacy PREFIX = purple_account_privacy_
PROTOTYPES: ENABLE
-void
-purple_accounts_add(account)
- Purple::Account account
-
-void
-purple_accounts_remove(account)
- Purple::Account account
-
-void
-purple_accounts_delete(account)
- Purple::Account account
-
-void
-purple_accounts_reorder(account, new_index)
- Purple::Account account
- size_t new_index
-
-void
-purple_accounts_get_all()
-PREINIT:
- GList *l;
-PPCODE:
- for (l = purple_accounts_get_all(); l != NULL; l = l->next) {
- XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Account")));
- }
+gboolean
+purple_account_privacy_permit_add(account, name, local_only)
+ Purple::Account account
+ const char * name
+ gboolean local_only
-void
-purple_accounts_get_all_active()
-PREINIT:
- GList *list, *iter;
-PPCODE:
- list = purple_accounts_get_all_active();
- for (iter = list; iter != NULL; iter = iter->next) {
- XPUSHs(sv_2mortal(purple_perl_bless_object(iter->data, "Purple::Account")));
- }
- g_list_free(list);
+gboolean
+purple_account_privacy_permit_remove(account, name, local_only)
+ Purple::Account account
+ const char * name
+ gboolean local_only
-void
-purple_accounts_restore_current_statuses()
+gboolean
+purple_account_privacy_deny_add(account, name, local_only)
+ Purple::Account account
+ const char * name
+ gboolean local_only
-Purple::Account
-purple_accounts_find(name, protocol)
- const char * name
- const char * protocol
+gboolean
+purple_account_privacy_deny_remove(account, name, local_only)
+ Purple::Account account
+ const char * name
+ gboolean local_only
-Purple::Handle
-purple_accounts_get_handle()
+gboolean
+purple_account_privacy_check(account, who)
+ Purple::Account account
+ const char * who
diff --git a/libpurple/plugins/perl/common/AccountOpts.xs b/libpurple/plugins/perl/common/AccountOpts.xs
index 976d3c4b23..190ada4b13 100644
--- a/libpurple/plugins/perl/common/AccountOpts.xs
+++ b/libpurple/plugins/perl/common/AccountOpts.xs
@@ -98,11 +98,11 @@ PPCODE:
}
Purple::PrefType
-purple_account_option_get_type(option)
+purple_account_option_get_pref_type(option)
Purple::Account::Option option
gboolean
-purple_account_option_get_masked(option)
+purple_account_option_string_get_masked(option)
Purple::Account::Option option
int
@@ -138,7 +138,7 @@ PPCODE:
purple_account_option_set_list(option, t_GL);
void
-purple_account_option_set_masked(option, masked)
+purple_account_option_string_set_masked(option, masked)
Purple::Account::Option option
gboolean masked
diff --git a/libpurple/plugins/perl/common/BuddyIcon.xs b/libpurple/plugins/perl/common/BuddyIcon.xs
index 66813ab6bb..bc381d4d40 100644
--- a/libpurple/plugins/perl/common/BuddyIcon.xs
+++ b/libpurple/plugins/perl/common/BuddyIcon.xs
@@ -7,7 +7,7 @@ Purple::Buddy::Icon
purple_buddy_icon_ref(icon)
Purple::Buddy::Icon icon
-Purple::Buddy::Icon
+void
purple_buddy_icon_unref(icon)
Purple::Buddy::Icon icon
@@ -45,7 +45,7 @@ purple_buddy_icon_get_scale_size(spec, width, height)
int *width
int *height
-gchar_own *
+const gchar *
purple_buddy_icon_get_full_path(icon);
Purple::Buddy::Icon icon
diff --git a/libpurple/plugins/perl/common/BuddyList.xs b/libpurple/plugins/perl/common/BuddyList.xs
index 15aa7ab6f1..518dd3a2bf 100644
--- a/libpurple/plugins/perl/common/BuddyList.xs
+++ b/libpurple/plugins/perl/common/BuddyList.xs
@@ -10,61 +10,6 @@ chat_components_foreach(gpointer key, gpointer value, gpointer user_data)
purple_debug_error("perl", "hv_store failed\n");
}
-MODULE = Purple::BuddyList PACKAGE = Purple PREFIX = purple_
-PROTOTYPES: ENABLE
-
-BOOT:
-{
- HV *stash = gv_stashpv("Purple::BuddyList::Node", 1);
-
- static const constiv *civ, const_iv[] = {
-#define const_iv(name) {#name, (IV)PURPLE_BLIST_##name##_NODE}
- const_iv(GROUP),
- const_iv(CONTACT),
- const_iv(BUDDY),
- const_iv(CHAT),
- const_iv(OTHER),
-#undef const_iv
-#define const_iv(name) {#name, (IV)PURPLE_BLIST_NODE_FLAG_##name}
- const_iv(NO_SAVE),
- };
-
- for (civ = const_iv + sizeof(const_iv) / sizeof(const_iv[0]); civ-- > const_iv; )
- newCONSTSUB(stash, (char *)civ->name, newSViv(civ->iv));
-}
-
-Purple::BuddyList
-purple_get_blist()
-
-void
-purple_set_blist(blist)
- Purple::BuddyList blist
-
-MODULE = Purple::BuddyList PACKAGE = Purple::Find PREFIX = purple_find_
-PROTOTYPES: ENABLE
-
-Purple::BuddyList::Buddy
-purple_find_buddy(account, name)
- Purple::Account account
- const char * name
-
-void
-purple_find_buddies(account, name)
- Purple::Account account
- const char * name
-PREINIT:
- GSList *l, *ll;
-PPCODE:
- ll = purple_find_buddies(account, name);
- for (l = ll; l != NULL; l = l->next) {
- XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::BuddyList::Buddy")));
- }
- g_slist_free(ll);
-
-Purple::BuddyList::Group
-purple_find_group(name)
- const char *name
-
MODULE = Purple::BuddyList PACKAGE = Purple::Find PREFIX = purple_
PROTOTYPES: ENABLE
@@ -83,11 +28,6 @@ Purple::BuddyList::Buddy
purple_contact_get_priority_buddy(contact)
Purple::BuddyList::Contact contact
-void
-purple_contact_set_alias(contact, alias)
- Purple::BuddyList::Contact contact
- const char * alias
-
const char *
purple_contact_get_alias(contact)
Purple::BuddyList::Contact contact
@@ -101,6 +41,11 @@ void
purple_contact_invalidate_priority_buddy(contact)
Purple::BuddyList::Contact contact
+void
+purple_contact_merge(source, node)
+ Purple::BuddyList::Contact source
+ Purple::BuddyList::Node node
+
MODULE = Purple::BuddyList PACKAGE = Purple::BuddyList::Group PREFIX = purple_group_
PROTOTYPES: ENABLE
@@ -125,6 +70,11 @@ purple_group_on_account(group, account)
Purple::BuddyList::Group group
Purple::Account account
+void
+purple_group_set_name(group, name)
+ Purple::BuddyList::Group group
+ const char * name
+
const char *
purple_group_get_name(group)
Purple::BuddyList::Group group
@@ -132,6 +82,31 @@ purple_group_get_name(group)
MODULE = Purple::BuddyList PACKAGE = Purple::BuddyList PREFIX = purple_blist_
PROTOTYPES: ENABLE
+Purple::BuddyList
+purple_blist_get_buddy_list()
+
+Purple::BuddyList::Buddy
+purple_blist_find_buddy(account, name)
+ Purple::Account account
+ const char * name
+
+void
+purple_blist_find_buddies(account, name)
+ Purple::Account account
+ const char * name
+PREINIT:
+ GSList *l, *ll;
+PPCODE:
+ ll = purple_blist_find_buddies(account, name);
+ for (l = ll; l != NULL; l = l->next) {
+ XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::BuddyList::Buddy")));
+ }
+ g_slist_free(ll);
+
+Purple::BuddyList::Group
+purple_blist_find_group(name)
+ const char *name
+
void
purple_blist_add_contact(contact, group, node)
Purple::BuddyList::Contact contact
@@ -139,11 +114,6 @@ purple_blist_add_contact(contact, group, node)
Purple::BuddyList::Node node
void
-purple_blist_merge_contact(source, node)
- Purple::BuddyList::Contact source
- Purple::BuddyList::Node node
-
-void
purple_blist_add_group(group, node)
Purple::BuddyList::Group group
Purple::BuddyList::Node node
@@ -182,54 +152,14 @@ purple_blist_add_chat(chat, group, node)
Purple::BuddyList::Group group
Purple::BuddyList::Node node
-Purple::BuddyList
-purple_blist_new()
-
void
purple_blist_show()
void
-purple_blist_destroy();
-
-void
purple_blist_set_visible(show)
gboolean show
void
-purple_blist_update_buddy_status(buddy, old_status)
- Purple::BuddyList::Buddy buddy
- Purple::Status old_status
-
-void
-purple_blist_update_buddy_icon(buddy)
- Purple::BuddyList::Buddy buddy
-
-void
-purple_blist_rename_buddy(buddy, name)
- Purple::BuddyList::Buddy buddy
- const char * name
-
-void
-purple_blist_alias_buddy(buddy, alias)
- Purple::BuddyList::Buddy buddy
- const char * alias
-
-void
-purple_blist_server_alias_buddy(buddy, alias)
- Purple::BuddyList::Buddy buddy
- const char * alias
-
-void
-purple_blist_alias_chat(chat, alias)
- Purple::BuddyList::Chat chat
- const char * alias
-
-void
-purple_blist_rename_group(group, name)
- Purple::BuddyList::Group group
- const char * name
-
-void
purple_blist_add_account(account)
Purple::Account account
@@ -237,18 +167,6 @@ void
purple_blist_remove_account(account)
Purple::Account account
-int
-purple_blist_get_group_size(group, offline)
- Purple::BuddyList::Group group
- gboolean offline
-
-int
-purple_blist_get_group_online_count(group)
- Purple::BuddyList::Group group
-
-void
-purple_blist_load()
-
void
purple_blist_schedule_save()
@@ -306,27 +224,63 @@ purple_blist_node_get_string(node, key)
const char * key
void
+purple_blist_node_set_transient(node, transient)
+ Purple::BuddyList::Node node
+ gboolean transient
+
+gboolean
+purple_blist_node_is_transient(node);
+ Purple::BuddyList::Node node
+
+void
purple_blist_node_remove_setting(node, key)
Purple::BuddyList::Node node
const char * key
+MODULE = Purple::BuddyList PACKAGE = Purple::BuddyList::CountingNode PREFIX = purple_counting_node_
+PROTOTYPES: ENABLE
+
+int
+purple_counting_node_get_total_size(counter);
+ Purple::BuddyList::CountingNode counter
+
+int
+purple_counting_node_get_current_size(counter);
+ Purple::BuddyList::CountingNode counter
+
+int
+purple_counting_node_get_online_count(counter);
+ Purple::BuddyList::CountingNode counter
+
void
-purple_blist_node_set_flags(node, flags)
- Purple::BuddyList::Node node
- Purple::BuddyList::NodeFlags flags
+purple_counting_node_change_total_size(counter, delta);
+ Purple::BuddyList::CountingNode counter
+ int delta
-Purple::BuddyList::NodeFlags
-purple_blist_node_get_flags(node)
- Purple::BuddyList::Node node
+void
+purple_counting_node_change_current_size(counter, delta);
+ Purple::BuddyList::CountingNode counter
+ int delta
-Purple::BuddyList::NodeType
-purple_blist_node_get_type(node)
- Purple::BuddyList::Node node
+void
+purple_counting_node_change_online_count(counter, delta);
+ Purple::BuddyList::CountingNode counter
+ int delta
-Purple::BuddyList::Node
-purple_blist_node_next(node, offline)
- Purple::BuddyList::Node node
- gboolean offline
+void
+purple_counting_node_set_total_size(counter, totalsize);
+ Purple::BuddyList::CountingNode counter
+ int totalsize
+
+void
+purple_counting_node_set_current_size(counter, currentsize);
+ Purple::BuddyList::CountingNode counter
+ int currentsize
+
+void
+purple_counting_node_set_online_count(counter, onlinecount);
+ Purple::BuddyList::CountingNode counter
+ int onlinecount
MODULE = Purple::BuddyList PACKAGE = Purple::BuddyList::Chat PREFIX = purple_chat_
PROTOTYPES: ENABLE
@@ -335,10 +289,19 @@ Purple::BuddyList::Group
purple_chat_get_group(chat)
Purple::BuddyList::Chat chat
+void
+purple_chat_set_alias(chat, alias)
+ Purple::BuddyList::Chat chat
+ const char * alias
+
const char *
purple_chat_get_name(chat)
Purple::BuddyList::Chat chat
+const char *
+purple_chat_get_name_only(chat)
+ Purple::BuddyList::Chat chat
+
HV *
purple_chat_get_components(chat)
Purple::BuddyList::Chat chat
@@ -389,6 +352,26 @@ purple_buddy_new(account, name, alias)
const char *name
const char *alias
+void
+purple_buddy_update_status(buddy, old_status)
+ Purple::BuddyList::Buddy buddy
+ Purple::Status old_status
+
+void
+purple_buddy_set_name(buddy, name)
+ Purple::BuddyList::Buddy buddy
+ const char * name
+
+void
+purple_buddy_set_local_alias(buddy, alias)
+ Purple::BuddyList::Buddy buddy
+ const char * alias
+
+void
+purple_buddy_set_server_alias(buddy, alias)
+ Purple::BuddyList::Buddy buddy
+ const char * alias
+
const char *
purple_buddy_get_server_alias(buddy)
Purple::BuddyList::Buddy buddy
@@ -431,9 +414,5 @@ purple_buddy_get_contact_alias(buddy)
Purple::BuddyList::Buddy buddy
const char *
-purple_buddy_get_local_alias(buddy)
- Purple::BuddyList::Buddy buddy
-
-const char *
purple_buddy_get_alias(buddy)
Purple::BuddyList::Buddy buddy
diff --git a/libpurple/plugins/perl/common/Certificate.xs b/libpurple/plugins/perl/common/Certificate.xs
index 9dd50a4abf..4d580b3270 100644
--- a/libpurple/plugins/perl/common/Certificate.xs
+++ b/libpurple/plugins/perl/common/Certificate.xs
@@ -43,8 +43,20 @@ BOOT:
static const constiv *civ, const_iv[] = {
#define const_iv(name) {#name, (IV)PURPLE_CERTIFICATE_##name}
- const_iv(INVALID),
+ const_iv(UNKNOWN_ERROR),
const_iv(VALID),
+ const_iv(NON_FATALS_MASK),
+ const_iv(SELF_SIGNED),
+ const_iv(CA_UNKNOWN),
+ const_iv(NOT_ACTIVATED),
+ const_iv(EXPIRED),
+ const_iv(NAME_MISMATCH),
+ const_iv(NO_CA_POOL),
+ const_iv(FATALS_MASK),
+ const_iv(INVALID_CHAIN),
+ const_iv(REVOKED),
+ const_iv(REJECTED),
+ const_iv(LAST),
};
for (civ = const_iv + sizeof(const_iv) / sizeof(const_iv[0]); civ-- > const_iv; )
@@ -68,10 +80,6 @@ void
purple_certificate_destroy(crt)
Purple::Certificate crt
-void
-purple_certificate_display_x509(crt)
- Purple::Certificate crt
-
## changed order of arguments, so that $cert->export($file) could be used
gboolean
purple_certificate_export(crt, filename)
@@ -149,7 +157,7 @@ purple_certificate_verify_complete(vrq, st)
Purple::Certificate::VerificationStatus st
gboolean
-purple_certificate_get_times(crt, OUTLIST time_t activation, OUTLIST time_t expiration)
+purple_certificate_get_times(crt, OUTLIST gint64 activation, OUTLIST gint64 expiration)
Purple::Certificate crt
PROTOTYPE: $
@@ -202,7 +210,7 @@ purple_certificate_check_signature_chain(...)
l = g_list_prepend(l, purple_perl_ref_object(ST(i)));
}
l = g_list_reverse(l);
- ret = purple_certificate_check_signature_chain(l);
+ ret = purple_certificate_check_signature_chain(l, NULL);
g_list_free(l);
if(ret) XSRETURN_YES;
XSRETURN_NO;
diff --git a/libpurple/plugins/perl/common/Cipher.xs b/libpurple/plugins/perl/common/Cipher.xs
index ff8e576910..c6ad9434ff 100644
--- a/libpurple/plugins/perl/common/Cipher.xs
+++ b/libpurple/plugins/perl/common/Cipher.xs
@@ -6,7 +6,6 @@ PROTOTYPES: ENABLE
BOOT:
{
HV *stash = gv_stashpv("Purple::Cipher::BatchMode", 1);
- HV *cipher_caps = gv_stashpv("Purple::Cipher::Caps", 1);
static const constiv *civ, const_iv[] = {
#define const_iv(name) {#name, (IV)PURPLE_CIPHER_BATCH_MODE_##name}
@@ -15,73 +14,12 @@ BOOT:
#undef const_iv
};
- static const constiv bm_const_iv[] = {
-#define const_iv(name) {#name, (IV)PURPLE_CIPHER_CAPS_##name}
- const_iv(SET_OPT),
- const_iv(GET_OPT),
- const_iv(INIT),
- const_iv(RESET),
- const_iv(UNINIT),
- const_iv(SET_IV),
- const_iv(APPEND),
- const_iv(DIGEST),
- const_iv(ENCRYPT),
- const_iv(DECRYPT),
- const_iv(SET_SALT),
- const_iv(GET_SALT_SIZE),
- const_iv(SET_KEY),
- const_iv(GET_KEY_SIZE),
- const_iv(SET_BATCH_MODE),
- const_iv(GET_BATCH_MODE),
- const_iv(GET_BLOCK_SIZE),
- const_iv(SET_KEY_WITH_LEN),
- const_iv(UNKNOWN),
-#undef const_iv
- };
-
for (civ = const_iv + sizeof(const_iv) / sizeof(const_iv[0]); civ-- > const_iv; )
newCONSTSUB(stash, (char *)civ->name, newSViv(civ->iv));
-
- for (civ = bm_const_iv + sizeof(bm_const_iv) / sizeof(bm_const_iv[0]); civ-- > bm_const_iv; )
- newCONSTSUB(cipher_caps, (char *)civ->name, newSViv(civ->iv));
}
-const gchar *
-purple_cipher_get_name(cipher)
- Purple::Cipher cipher
-
-guint
-purple_cipher_get_capabilities(cipher)
- Purple::Cipher cipher
-
-size_t
-purple_cipher_digest_region(name, data_sv, in_len, digest)
- const gchar *name
- SV *data_sv
- size_t in_len
- SV *digest
- PREINIT:
- gboolean ret;
- guchar *buff = NULL;
- guchar *data = NULL;
- size_t data_len;
- CODE:
- data = (guchar *)SvPV(data_sv, data_len);
- SvUPGRADE(digest, SVt_PV);
- buff = (guchar *)SvGROW(digest, in_len);
- ret = purple_cipher_digest_region(name, data, data_len, in_len, buff, &RETVAL);
- if(!ret) {
- SvSetSV_nosteal(digest, &PL_sv_undef);
- XSRETURN_UNDEF;
- }
- SvCUR_set(digest, RETVAL);
- /* coverity[extra_comma] */
- SvPOK_only(digest);
- OUTPUT:
- RETVAL
-
gchar_own*
-purple_cipher_http_digest_calculate_response(algorithm, method, digest_uri, qop, entity, nonce, nonce_count, client_nonce, session_key)
+purple_http_digest_calculate_response(algorithm, method, digest_uri, qop, entity, nonce, nonce_count, client_nonce, session_key)
const gchar* algorithm
const gchar* method
const gchar* digest_uri
@@ -93,7 +31,7 @@ purple_cipher_http_digest_calculate_response(algorithm, method, digest_uri, qop,
const gchar* session_key
gchar_own*
-purple_cipher_http_digest_calculate_session_key(algorithm, username, realm, password, nonce, client_nonce)
+purple_http_digest_calculate_session_key(algorithm, username, realm, password, nonce, client_nonce)
const gchar* algorithm
const gchar* username
const gchar* realm
@@ -101,213 +39,291 @@ purple_cipher_http_digest_calculate_session_key(algorithm, username, realm, pass
const gchar* nonce
const gchar* client_nonce
-MODULE = Purple::Cipher PACKAGE = Purple::Ciphers PREFIX = purple_ciphers_
-PROTOTYPES: ENABLE
-
-Purple::Cipher
-purple_ciphers_find_cipher(name)
- gchar * name
-
-Purple::Cipher
-purple_ciphers_register_cipher(name, ops)
- gchar * name
- Purple::Cipher::Ops ops
-
-gboolean
-purple_ciphers_unregister_cipher(cipher)
- Purple::Cipher cipher
-
void
-purple_ciphers_get_ciphers()
-PREINIT:
- GList *l;
-PPCODE:
- for (l = purple_ciphers_get_ciphers(); l != NULL; l = l->next) {
- XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Cipher")));
- }
-
-Purple::Handle
-purple_ciphers_get_handle()
-
-MODULE = Purple::Cipher PACKAGE = Purple::Cipher::Context PREFIX = purple_cipher_context_
-PROTOTYPES: ENABLE
-
-void
-purple_cipher_context_set_option(context, name, value)
- Purple::Cipher::Context context
- gchar *name
- gpointer value
-
-gpointer
-purple_cipher_context_get_option(context, name)
- Purple::Cipher::Context context
- gchar *name
-
-Purple::Cipher::Context
-purple_cipher_context_new(klass, cipher, extra = NULL)
+purple_cipher_reset(cipher)
Purple::Cipher cipher
- void *extra
- C_ARGS: cipher, extra
-
-Purple::Cipher::Context
-purple_cipher_context_new_by_name(klass, name, extra = NULL)
- gchar *name
- void *extra
- C_ARGS: name, extra
void
-purple_cipher_context_reset(context, extra = NULL)
- Purple::Cipher::Context context
- gpointer extra
-
-void
-purple_cipher_context_destroy(context)
- Purple::Cipher::Context context
-
-void
-purple_cipher_context_set_iv(Purple::Cipher::Context context, guchar *iv, size_t length(iv))
+purple_cipher_set_iv(Purple::Cipher cipher, guchar *iv, size_t length(iv))
PROTOTYPE: $$
void
-purple_cipher_context_append(Purple::Cipher::Context context, guchar *data, size_t length(data))
+purple_cipher_append(Purple::Cipher cipher, guchar *data, size_t length(data))
PROTOTYPE: $$
-size_t
-purple_cipher_context_digest(context, in_len, digest)
- Purple::Cipher::Context context
- size_t in_len
+gboolean
+purple_cipher_digest(cipher, digest)
+ Purple::Cipher cipher
SV *digest
PREINIT:
- gboolean ret;
guchar *buff = NULL;
+ size_t digest_size;
CODE:
- SvUPGRADE(digest, SVt_PV);
- buff = (guchar *)SvGROW(digest, in_len);
- ret = purple_cipher_context_digest(context, in_len, buff, &RETVAL);
- if(!ret) {
+ digest_size = purple_cipher_get_digest_size(cipher);
+ SvUPGRADE_common(digest, SVt_PV);
+ buff = (guchar *)SvGROW(digest, digest_size);
+ if (purple_cipher_digest(cipher, buff, digest_size)) {
+ SvCUR_set(digest, digest_size);
+ /* coverity[extra_comma] */
+ SvPOK_only(digest);
+ RETVAL = 1;
+ } else {
SvSetSV_nosteal(digest, &PL_sv_undef);
- XSRETURN_UNDEF;
+ RETVAL = 0;
}
- SvCUR_set(digest, RETVAL);
- /* coverity[extra_comma] */
- SvPOK_only(digest);
OUTPUT:
RETVAL
-size_t
-purple_cipher_context_digest_to_str(context, in_len, digest_s)
- Purple::Cipher::Context context
- size_t in_len
+gboolean
+purple_cipher_digest_to_str(cipher, digest_s)
+ Purple::Cipher cipher
SV *digest_s
PREINIT:
- gboolean ret;
gchar *buff = NULL;
+ size_t digest_size, str_len;
CODE:
- in_len += 1; /* perl shouldn't need to care about '\0' at the end */
- SvUPGRADE(digest_s, SVt_PV);
- buff = SvGROW(digest_s, in_len);
- ret = purple_cipher_context_digest_to_str(context, in_len, buff, &RETVAL);
- if(!ret) {
+ digest_size = purple_cipher_get_digest_size(cipher);
+ str_len = 2 * digest_size;
+ SvUPGRADE_common(digest_s, SVt_PV);
+ buff = SvGROW(digest_s, str_len + 1);
+ if (purple_cipher_digest_to_str(cipher, buff, str_len + 1)) {
+ SvCUR_set(digest_s, str_len);
+ /* coverity[extra_comma] */
+ SvPOK_only(digest_s);
+ RETVAL = 1;
+ } else {
SvSetSV_nosteal(digest_s, &PL_sv_undef);
- XSRETURN_UNDEF;
+ RETVAL = 0;
}
- SvCUR_set(digest_s, RETVAL);
- /* coverity[extra_comma] */
- SvPOK_only(digest_s);
OUTPUT:
RETVAL
-gint
-purple_cipher_context_encrypt(context, data_sv, output, OUTLIST size_t outlen)
- Purple::Cipher::Context context
- SV *data_sv
+gboolean
+purple_cipher_encrypt(cipher, input, output)
+ Purple::Cipher cipher
+ SV *input
SV *output
- PROTOTYPE: $$$
PREINIT:
- size_t datalen;
+ size_t input_len, output_len;
+ ssize_t ret;
guchar *buff = NULL;
guchar *data = NULL;
CODE:
- data = (guchar *)SvPV(data_sv, datalen);
- SvUPGRADE(output, SVt_PV);
- buff = (guchar *)SvGROW(output, datalen);
- RETVAL = purple_cipher_context_encrypt(context, data, datalen, buff, &outlen);
- if(outlen != 0) {
+ data = (guchar *)SvPV(input, input_len);
+ output_len = input_len + purple_cipher_get_block_size(cipher);
+ SvUPGRADE_common(output, SVt_PV);
+ buff = (guchar *)SvGROW(output, output_len);
+ ret = purple_cipher_encrypt(cipher, data, input_len, buff, output_len);
+ if (ret >= 0) {
+ RETVAL = 1;
/* coverity[extra_comma] */
SvPOK_only(output);
- SvCUR_set(output, outlen);
+ SvCUR_set(output, ret);
} else {
+ RETVAL = 0;
SvSetSV_nosteal(output, &PL_sv_undef);
}
OUTPUT:
RETVAL
-gint
-purple_cipher_context_decrypt(context, data_sv, output, OUTLIST size_t outlen)
- Purple::Cipher::Context context
- SV *data_sv
+gboolean
+purple_cipher_decrypt(cipher, input, output)
+ Purple::Cipher cipher
+ SV *input
SV *output
- PROTOTYPE: $$$
PREINIT:
- size_t datalen;
+ size_t input_len, output_len;
+ ssize_t ret;
guchar *buff = NULL;
guchar *data = NULL;
CODE:
- data = (guchar *)SvPV(data_sv, datalen);
- SvUPGRADE(output, SVt_PV);
- buff = (guchar *)SvGROW(output, datalen);
- RETVAL = purple_cipher_context_decrypt(context, data, datalen, buff, &outlen);
- if(outlen != 0) {
+ data = (guchar *)SvPV(input, input_len);
+ output_len = input_len + purple_cipher_get_block_size(cipher);
+ SvUPGRADE_common(output, SVt_PV);
+ buff = (guchar *)SvGROW(output, output_len);
+ ret = purple_cipher_decrypt(cipher, data, input_len, buff, output_len);
+ if (ret >= 0) {
+ RETVAL = 1;
/* coverity[extra_comma] */
SvPOK_only(output);
- SvCUR_set(output, outlen);
+ SvCUR_set(output, ret);
} else {
+ RETVAL = 0;
SvSetSV_nosteal(output, &PL_sv_undef);
}
OUTPUT:
RETVAL
void
-purple_cipher_context_set_salt(context, salt)
- Purple::Cipher::Context context
+purple_cipher_set_salt(cipher, salt, len)
+ Purple::Cipher cipher
guchar *salt
-
-size_t
-purple_cipher_context_get_salt_size(context)
- Purple::Cipher::Context context
+ size_t len
void
-purple_cipher_context_set_key(context, key)
- Purple::Cipher::Context context
+purple_cipher_set_key(cipher, key, len)
+ Purple::Cipher cipher
guchar *key
+ size_t len
size_t
-purple_cipher_context_get_key_size(context)
- Purple::Cipher::Context context
-
-void
-purple_cipher_context_set_data(context, data)
- Purple::Cipher::Context context
- gpointer data
-
-gpointer
-purple_cipher_context_get_data(context)
- Purple::Cipher::Context context
+purple_cipher_get_key_size(cipher)
+ Purple::Cipher cipher
Purple::Cipher::BatchMode
-purple_cipher_context_get_batch_mode(context)
- Purple::Cipher::Context context
+purple_cipher_get_batch_mode(cipher)
+ Purple::Cipher cipher
size_t
-purple_cipher_context_get_block_size(context)
- Purple::Cipher::Context context
+purple_cipher_get_block_size(cipher)
+ Purple::Cipher cipher
void
-purple_cipher_context_set_batch_mode(context, mode)
- Purple::Cipher::Context context
+purple_cipher_set_batch_mode(cipher, mode)
+ Purple::Cipher cipher
Purple::Cipher::BatchMode mode
+MODULE = Purple::Cipher PACKAGE = Purple::AESCipher PREFIX = purple_aes_cipher_
+PROTOTYPES: ENABLE
+
+Purple::Cipher
+purple_aes_cipher_new()
+
+MODULE = Purple::Cipher PACKAGE = Purple::DES3Cipher PREFIX = purple_des3_cipher_
+PROTOTYPES: ENABLE
+
+Purple::Cipher
+purple_des3_cipher_new()
+
+MODULE = Purple::Cipher PACKAGE = Purple::DESCipher PREFIX = purple_des_cipher_
+PROTOTYPES: ENABLE
+
+Purple::Cipher
+purple_des_cipher_new()
+
+MODULE = Purple::Cipher PACKAGE = Purple::HMACCipher PREFIX = purple_hmac_cipher_
+PROTOTYPES: ENABLE
+
+Purple::Cipher
+purple_hmac_cipher_new(hash)
+ Purple::Hash hash
+
+MODULE = Purple::Cipher PACKAGE = Purple::PBKDF2Cipher PREFIX = purple_pbkdf2_cipher_
+PROTOTYPES: ENABLE
+
+Purple::Cipher
+purple_pbkdf2_cipher_new(hash)
+ Purple::Hash hash
+
+MODULE = Purple::Cipher PACKAGE = Purple::RC4Cipher PREFIX = purple_rc4_cipher_
+PROTOTYPES: ENABLE
+
+Purple::Cipher
+purple_rc4_cipher_new()
+
+MODULE = Purple::Cipher PACKAGE = Purple::Hash PREFIX = purple_hash_
+PROTOTYPES: ENABLE
+
+gchar_own*
+purple_http_digest_calculate_response(algorithm, method, digest_uri, qop, entity, nonce, nonce_count, client_nonce, session_key)
+ const gchar* algorithm
+ const gchar* method
+ const gchar* digest_uri
+ const gchar* qop
+ const gchar* entity
+ const gchar* nonce
+ const gchar* nonce_count
+ const gchar* client_nonce
+ const gchar* session_key
+
+gchar_own*
+purple_http_digest_calculate_session_key(algorithm, username, realm, password, nonce, client_nonce)
+ const gchar* algorithm
+ const gchar* username
+ const gchar* realm
+ const gchar* password
+ const gchar* nonce
+ const gchar* client_nonce
+
+void
+purple_hash_reset(hash)
+ Purple::Hash hash
+
void
-purple_cipher_context_set_key_with_len(Purple::Cipher::Context context, guchar *key, size_t length(key))
+purple_hash_append(Purple::Hash hash, guchar *data, size_t length(data))
PROTOTYPE: $$
+gboolean
+purple_hash_digest(hash, digest)
+ Purple::Hash hash
+ SV *digest
+ PREINIT:
+ guchar *buff = NULL;
+ size_t digest_size;
+ CODE:
+ digest_size = purple_hash_get_digest_size(hash);
+ SvUPGRADE_common(digest, SVt_PV);
+ buff = (guchar *)SvGROW(digest, digest_size);
+ if (purple_hash_digest(hash, buff, digest_size)) {
+ SvCUR_set(digest, digest_size);
+ /* coverity[extra_comma] */
+ SvPOK_only(digest);
+ RETVAL = 1;
+ } else {
+ SvSetSV_nosteal(digest, &PL_sv_undef);
+ RETVAL = 0;
+ }
+ OUTPUT:
+ RETVAL
+
+gboolean
+purple_hash_digest_to_str(hash, digest_s)
+ Purple::Hash hash
+ SV *digest_s
+ PREINIT:
+ gchar *buff = NULL;
+ size_t digest_size, str_len;
+ CODE:
+ digest_size = purple_hash_get_digest_size(hash);
+ str_len = 2 * digest_size;
+ SvUPGRADE_common(digest_s, SVt_PV);
+ buff = SvGROW(digest_s, str_len + 1);
+ if (purple_hash_digest_to_str(hash, buff, str_len + 1)) {
+ SvCUR_set(digest_s, str_len);
+ /* coverity[extra_comma] */
+ SvPOK_only(digest_s);
+ RETVAL = 1;
+ } else {
+ SvSetSV_nosteal(digest_s, &PL_sv_undef);
+ RETVAL = 0;
+ }
+ OUTPUT:
+ RETVAL
+
+size_t
+purple_hash_get_block_size(hash)
+ Purple::Hash hash
+
+MODULE = Purple::Hash PACKAGE = Purple::MD4Hash PREFIX = purple_md4_hash_
+PROTOTYPES: ENABLE
+
+Purple::Hash
+purple_md4_hash_new()
+
+MODULE = Purple::Hash PACKAGE = Purple::MD5Hash PREFIX = purple_md5_hash_
+PROTOTYPES: ENABLE
+
+Purple::Hash
+purple_md5_hash_new()
+
+MODULE = Purple::Hash PACKAGE = Purple::SHA1Hash PREFIX = purple_sha1_hash_
+PROTOTYPES: ENABLE
+
+Purple::Hash
+purple_sha1_hash_new()
+
+MODULE = Purple::Hash PACKAGE = Purple::SHA256Hash PREFIX = purple_sha256_hash_
+PROTOTYPES: ENABLE
+
+Purple::Hash
+purple_sha256_hash_new()
diff --git a/libpurple/plugins/perl/common/Connection.xs b/libpurple/plugins/perl/common/Connection.xs
index 74bd3ba8bd..334731c9d6 100644
--- a/libpurple/plugins/perl/common/Connection.xs
+++ b/libpurple/plugins/perl/common/Connection.xs
@@ -8,7 +8,7 @@ BOOT:
HV *stash = gv_stashpv("Purple::Connection::State", 1);
static const constiv *civ, const_iv[] = {
-#define const_iv(name) {#name, (IV)PURPLE_##name}
+#define const_iv(name) {#name, (IV)PURPLE_CONNECTION_##name}
const_iv(DISCONNECTED),
const_iv(CONNECTED),
const_iv(CONNECTING),
@@ -26,6 +26,16 @@ const char *
purple_connection_get_password(gc)
Purple::Connection gc
+void
+purple_connection_get_active_chats(gc)
+ Purple::Connection gc
+PREINIT:
+ GSList *l;
+PPCODE:
+ for (l = purple_connection_get_active_chats(gc); l != NULL; l = l->next) {
+ XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::ChatConversation")));
+ }
+
const char *
purple_connection_get_display_name(gc)
Purple::Connection gc
@@ -36,25 +46,11 @@ purple_connection_notice(gc, text)
const char *text
void
-purple_connection_error(gc, reason)
- Purple::Connection gc
- const char *reason
-
-void
-purple_connection_destroy(gc)
- Purple::Connection gc
-
-void
purple_connection_set_state(gc, state)
Purple::Connection gc
Purple::ConnectionState state
void
-purple_connection_set_account(gc, account)
- Purple::Connection gc
- Purple::Account account
-
-void
purple_connection_set_display_name(gc, name)
Purple::Connection gc
const char *name
diff --git a/libpurple/plugins/perl/common/Conversation.xs b/libpurple/plugins/perl/common/Conversation.xs
index 3239b57d99..92ab4abf7a 100644
--- a/libpurple/plugins/perl/common/Conversation.xs
+++ b/libpurple/plugins/perl/common/Conversation.xs
@@ -5,23 +5,14 @@ PROTOTYPES: ENABLE
BOOT:
{
- HV *type_stash = gv_stashpv("Purple::Conversation::Type", 1);
- HV *update_stash = gv_stashpv("Purple::Conversation::Update::Type", 1);
- HV *typing_stash = gv_stashpv("Purple::Conversation::TypingState", 1);
- HV *flags_stash = gv_stashpv("Purple::Conversation::Flags", 1);
- HV *cbflags_stash = gv_stashpv("Purple::Conversation::ChatBuddy::Flags", 1);
-
- static const constiv *civ, type_const_iv[] = {
-#define const_iv(name) {#name, (IV)PURPLE_CONV_TYPE_##name}
- const_iv(UNKNOWN),
- const_iv(IM),
- const_iv(CHAT),
- const_iv(MISC),
- const_iv(ANY),
- };
- static const constiv update_const_iv[] = {
+ HV *update_stash = gv_stashpv("Purple::Conversation::UpdateType", 1);
+ HV *typing_stash = gv_stashpv("Purple::IMTypingState", 1);
+ HV *flags_stash = gv_stashpv("Purple::MessageFlags", 1);
+ HV *cbflags_stash = gv_stashpv("Purple::ChatUser::Flags", 1);
+
+ static const constiv *civ, update_const_iv[] = {
#undef const_iv
-#define const_iv(name) {#name, (IV)PURPLE_CONV_UPDATE_##name}
+#define const_iv(name) {#name, (IV)PURPLE_CONVERSATION_UPDATE_##name}
const_iv(ADD),
const_iv(REMOVE),
const_iv(ACCOUNT),
@@ -41,7 +32,7 @@ BOOT:
};
static const constiv typing_const_iv[] = {
#undef const_iv
-#define const_iv(name) {#name, (IV)PURPLE_##name}
+#define const_iv(name) {#name, (IV)PURPLE_IM_##name}
const_iv(NOT_TYPING),
const_iv(TYPING),
const_iv(TYPED),
@@ -56,7 +47,6 @@ BOOT:
const_iv(ACTIVE_ONLY),
const_iv(NICK),
const_iv(NO_LOG),
- const_iv(WHISPER),
const_iv(ERROR),
const_iv(DELAYED),
const_iv(RAW),
@@ -66,7 +56,7 @@ BOOT:
};
static const constiv cbflags_const_iv[] = {
#undef const_iv
-#define const_iv(name) {#name, (IV)PURPLE_CBFLAGS_##name}
+#define const_iv(name) {#name, (IV)PURPLE_CHAT_USER_##name}
const_iv(NONE),
const_iv(VOICE),
const_iv(HALFOP),
@@ -75,9 +65,6 @@ BOOT:
const_iv(TYPING),
};
- for (civ = type_const_iv + sizeof(type_const_iv) / sizeof(type_const_iv[0]); civ-- > type_const_iv; )
- newCONSTSUB(type_stash, (char *)civ->name, newSViv(civ->iv));
-
for (civ = update_const_iv + sizeof(update_const_iv) / sizeof(update_const_iv[0]); civ-- > update_const_iv; )
newCONSTSUB(update_stash, (char *)civ->name, newSViv(civ->iv));
@@ -91,62 +78,76 @@ BOOT:
newCONSTSUB(cbflags_stash, (char *)civ->name, newSViv(civ->iv));
}
+MODULE = Purple::Conversation PACKAGE = Purple::Conversations PREFIX = purple_conversations_
+PROTOTYPES: ENABLE
+
+void
+purple_conversations_add(conv)
+ Purple::Conversation conv
+
+void
+purple_conversations_remove(conv)
+ Purple::Conversation conv
+
+Purple::Handle
+purple_conversations_get_handle()
+
+Purple::ChatConversation
+purple_conversations_find_chat(gc, id)
+ Purple::Connection gc
+ int id
+
void
-purple_get_ims()
+purple_conversations_get_ims()
PREINIT:
GList *l;
PPCODE:
- for (l = purple_get_ims(); l != NULL; l = l->next) {
- XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Conversation")));
+ for (l = purple_conversations_get_ims(); l != NULL; l = l->next) {
+ XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::IMConversation")));
}
void
-purple_get_conversations()
+purple_conversations_get_all()
PREINIT:
GList *l;
PPCODE:
- for (l = purple_get_conversations(); l != NULL; l = l->next) {
+ for (l = purple_conversations_get_all(); l != NULL; l = l->next) {
XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Conversation")));
}
void
-purple_get_chats()
+purple_conversations_get_chats()
PREINIT:
GList *l;
PPCODE:
- for (l = purple_get_chats(); l != NULL; l = l->next) {
- XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Conversation")));
+ for (l = purple_conversations_get_chats(); l != NULL; l = l->next) {
+ XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::ChatConversation")));
}
Purple::Conversation
-purple_find_conversation_with_account(type, name, account)
- Purple::ConversationType type
+purple_conversations_find_with_account(name, account)
const char *name
Purple::Account account
-MODULE = Purple::Conversation PACKAGE = Purple::Conversations PREFIX = purple_conversations_
-PROTOTYPES: ENABLE
+Purple::ChatConversation
+purple_conversations_find_chat_with_account(name, account)
+ const char *name
+ Purple::Account account
-Purple::Handle
-purple_conversations_get_handle()
+Purple::IMConversation
+purple_conversations_find_im_with_account(name, account)
+ const char *name
+ Purple::Account account
MODULE = Purple::Conversation PACKAGE = Purple::Conversation PREFIX = purple_conversation_
PROTOTYPES: ENABLE
-void
-purple_conversation_destroy(conv)
- Purple::Conversation conv
-
-Purple::ConversationType
-purple_conversation_get_type(conv)
- Purple::Conversation conv
-
Purple::Account
purple_conversation_get_account(conv)
Purple::Conversation conv
Purple::Connection
-purple_conversation_get_gc(conv)
+purple_conversation_get_connection(conv)
Purple::Conversation conv
void
@@ -180,19 +181,6 @@ gboolean
purple_conversation_is_logging(conv)
Purple::Conversation conv
-Purple::Conversation::IM
-purple_conversation_get_im_data(conv)
- Purple::Conversation conv
-
-Purple::Conversation::Chat
-purple_conversation_get_chat_data(conv)
- Purple::Conversation conv
-
-gpointer
-purple_conversation_get_data(conv, key)
- Purple::Conversation conv
- const char * key
-
Purple::ConnectionFlags
purple_conversation_get_features(conv)
Purple::Conversation conv
@@ -204,15 +192,7 @@ purple_conversation_has_focus(conv)
void
purple_conversation_update(conv, type)
Purple::Conversation conv
- Purple::ConvUpdateType type
-
-Purple::Conversation
-purple_conversation_new(class, type, account, name)
- Purple::ConversationType type
- Purple::Account account
- const char *name
- C_ARGS:
- type, account, name
+ Purple::Conversation::UpdateType type
void
purple_conversation_set_account(conv, account);
@@ -220,12 +200,15 @@ purple_conversation_set_account(conv, account);
Purple::Account account
void
-purple_conversation_write(conv, who, message, flags, mtime)
+purple_conversation_send(conv, message)
+ Purple::Conversation conv
+ const char *message
+
+void
+purple_conversation_send_with_flags(conv, message, flags)
Purple::Conversation conv
- const char *who
const char *message
Purple::MessageFlags flags
- time_t mtime
gboolean
purple_conversation_do_command(conv, cmdline, markup, error)
@@ -234,150 +217,119 @@ purple_conversation_do_command(conv, cmdline, markup, error)
const char *markup
char **error
-MODULE = Purple::Conversation PACKAGE = Purple::Conversation::IM PREFIX = purple_conv_im_
+MODULE = Purple::Conversation PACKAGE = Purple::IMConversation PREFIX = purple_im_conversation_
PROTOTYPES: ENABLE
-Purple::Conversation
-purple_conv_im_get_conversation(im)
- Purple::Conversation::IM im
+Purple::IMConversation
+purple_im_conversation_new(class, account, name)
+ Purple::Account account
+ const char *name
+ C_ARGS:
+ account, name
void
-purple_conv_im_set_icon(im, icon)
- Purple::Conversation::IM im
+purple_im_conversation_set_icon(im, icon)
+ Purple::IMConversation im
Purple::Buddy::Icon icon
Purple::Buddy::Icon
-purple_conv_im_get_icon(im)
- Purple::Conversation::IM im
+purple_im_conversation_get_icon(im)
+ Purple::IMConversation im
void
-purple_conv_im_set_typing_state(im, state)
- Purple::Conversation::IM im
- Purple::TypingState state
+purple_im_conversation_set_typing_state(im, state)
+ Purple::IMConversation im
+ Purple::IMTypingState state
-Purple::TypingState
-purple_conv_im_get_typing_state(im)
- Purple::Conversation::IM im
+Purple::IMTypingState
+purple_im_conversation_get_typing_state(im)
+ Purple::IMConversation im
void
-purple_conv_im_start_typing_timeout(im, timeout)
- Purple::Conversation::IM im
+purple_im_conversation_start_typing_timeout(im, timeout)
+ Purple::IMConversation im
int timeout
void
-purple_conv_im_stop_typing_timeout(im)
- Purple::Conversation::IM im
+purple_im_conversation_stop_typing_timeout(im)
+ Purple::IMConversation im
guint
-purple_conv_im_get_typing_timeout(im)
- Purple::Conversation::IM im
+purple_im_conversation_get_typing_timeout(im)
+ Purple::IMConversation im
void
-purple_conv_im_set_type_again(im, val)
- Purple::Conversation::IM im
+purple_im_conversation_set_type_again(im, val)
+ Purple::IMConversation im
time_t val
time_t
-purple_conv_im_get_type_again(im)
- Purple::Conversation::IM im
+purple_im_conversation_get_type_again(im)
+ Purple::IMConversation im
void
-purple_conv_im_start_send_typed_timeout(im)
- Purple::Conversation::IM im
+purple_im_conversation_start_send_typed_timeout(im)
+ Purple::IMConversation im
void
-purple_conv_im_stop_send_typed_timeout(im)
- Purple::Conversation::IM im
+purple_im_conversation_stop_send_typed_timeout(im)
+ Purple::IMConversation im
guint
-purple_conv_im_get_send_typed_timeout(im)
- Purple::Conversation::IM im
+purple_im_conversation_get_send_typed_timeout(im)
+ Purple::IMConversation im
void
-purple_conv_im_update_typing(im)
- Purple::Conversation::IM im
+purple_im_conversation_update_typing(im)
+ Purple::IMConversation im
-void
-purple_conv_im_send(im, message)
- Purple::Conversation::IM im
- const char *message
-
-void
-purple_conv_im_send_with_flags(im, message, flags)
- Purple::Conversation::IM im
- const char *message
- Purple::MessageFlags flags
-
-void
-purple_conv_im_write(im, who, message, flags, mtime)
- Purple::Conversation::IM im
- const char *who
- const char *message
- Purple::MessageFlags flags
- time_t mtime
-
-MODULE = Purple::Conversation PACKAGE = Purple::Conversation PREFIX = purple_conv_
+MODULE = Purple::Conversation PACKAGE = Purple::Conversation::Helper PREFIX = purple_conversation_helper_
PROTOTYPES: ENABLE
gboolean
-purple_conv_present_error(who, account, what)
+purple_conversation_present_error(who, account, what)
const char *who
Purple::Account account
const char *what
-void
-purple_conv_custom_smiley_close(conv, smile)
- Purple::Conversation conv
- const char *smile
-
-MODULE = Purple::Conversation PACKAGE = Purple::Conversation::Chat PREFIX = purple_conv_chat_
+MODULE = Purple::Conversation PACKAGE = Purple::Conversation PREFIX = purple_conversation_
PROTOTYPES: ENABLE
-Purple::Conversation
-purple_conv_chat_get_conversation(chat)
- Purple::Conversation::Chat chat
-
-void
-purple_conv_chat_set_users(chat, users)
- Purple::Conversation::Chat chat
- SV * users
-PREINIT:
- GList *l, *t_GL;
- int i, t_len;
-PPCODE:
- t_GL = NULL;
- t_len = av_len((AV *)SvRV(users));
-
- for (i = 0; i <= t_len; i++)
- t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(users), i, 0)));
+MODULE = Purple::Conversation PACKAGE = Purple::ChatConversation PREFIX = purple_chat_conversation_
+PROTOTYPES: ENABLE
- for (l = purple_conv_chat_set_users(chat, t_GL); l != NULL; l = l->next) {
- XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::ListEntry")));
- }
+Purple::ChatConversation
+purple_chat_conversation_new(class, account, name)
+ Purple::Account account
+ const char *name
+ C_ARGS:
+ account, name
void
-purple_conv_chat_get_users(chat)
- Purple::Conversation::Chat chat
+purple_chat_conversation_get_users(chat)
+ Purple::ChatConversation chat
PREINIT:
- GList *l;
+ GList *l, *users;
PPCODE:
- for (l = purple_conv_chat_get_users(chat); l != NULL; l = l->next) {
+ users = purple_chat_conversation_get_users(chat);
+ for (l = users; l != NULL; l = l->next) {
XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::ListEntry")));
}
+ g_list_free(users);
void
-purple_conv_chat_ignore(chat, name)
- Purple::Conversation::Chat chat
+purple_chat_conversation_ignore(chat, name)
+ Purple::ChatConversation chat
const char *name
void
-purple_conv_chat_unignore(chat, name)
- Purple::Conversation::Chat chat
+purple_chat_conversation_unignore(chat, name)
+ Purple::ChatConversation chat
const char *name
void
-purple_conv_chat_set_ignored(chat, ignored)
- Purple::Conversation::Chat chat
+purple_chat_conversation_set_ignored(chat, ignored)
+ Purple::ChatConversation chat
SV * ignored
PREINIT:
GList *l, *t_GL;
@@ -389,55 +341,36 @@ PPCODE:
for (i = 0; i <= t_len; i++)
t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(ignored), i, 0)));
- for (l = purple_conv_chat_set_ignored(chat, t_GL); l != NULL; l = l->next) {
+ for (l = purple_chat_conversation_set_ignored(chat, t_GL); l != NULL; l = l->next) {
XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::ListEntry")));
}
void
-purple_conv_chat_get_ignored(chat)
- Purple::Conversation::Chat chat
+purple_chat_conversation_get_ignored(chat)
+ Purple::ChatConversation chat
PREINIT:
GList *l;
PPCODE:
- for (l = purple_conv_chat_get_ignored(chat); l != NULL; l = l->next) {
+ for (l = purple_chat_conversation_get_ignored(chat); l != NULL; l = l->next) {
XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::ListEntry")));
}
const char *
-purple_conv_chat_get_topic(chat)
- Purple::Conversation::Chat chat
+purple_chat_conversation_get_topic(chat)
+ Purple::ChatConversation chat
void
-purple_conv_chat_set_id(chat, id)
- Purple::Conversation::Chat chat
+purple_chat_conversation_set_id(chat, id)
+ Purple::ChatConversation chat
int id
int
-purple_conv_chat_get_id(chat)
- Purple::Conversation::Chat chat
-
-void
-purple_conv_chat_send(chat, message)
- Purple::Conversation::Chat chat
- const char * message
+purple_chat_conversation_get_id(chat)
+ Purple::ChatConversation chat
void
-purple_conv_chat_send_with_flags(chat, message, flags)
- Purple::Conversation::Chat chat
- const char * message
- Purple::MessageFlags flags
-
-void
-purple_conv_chat_write(chat, who, message, flags, mtime)
- Purple::Conversation::Chat chat
- const char *who
- const char *message
- Purple::MessageFlags flags
- time_t mtime
-
-void
-purple_conv_chat_add_users(chat, users, extra_msgs, flags, new_arrivals)
- Purple::Conversation::Chat chat
+purple_chat_conversation_add_users(chat, users, extra_msgs, flags, new_arrivals)
+ Purple::ChatConversation chat
SV * users
SV * extra_msgs
SV * flags
@@ -464,48 +397,39 @@ PPCODE:
for (i = 0; i <= t_len; i++)
t_GL_extra_msgs = g_list_append(t_GL_extra_msgs, SvPVutf8_nolen(*av_fetch((AV *)SvRV(extra_msgs), i, 0)));
- purple_conv_chat_add_users(chat, t_GL_users, t_GL_extra_msgs, t_GL_flags, new_arrivals);
+ purple_chat_conversation_add_users(chat, t_GL_users, t_GL_extra_msgs, t_GL_flags, new_arrivals);
g_list_free(t_GL_users);
g_list_free(t_GL_extra_msgs);
g_list_free(t_GL_flags);
gboolean
-purple_conv_chat_find_user(chat, user)
- Purple::Conversation::Chat chat
+purple_chat_conversation_has_user(chat, user)
+ Purple::ChatConversation chat
const char * user
-void purple_conv_chat_clear_users(chat)
- Purple::Conversation::Chat chat
+void purple_chat_conversation_clear_users(chat)
+ Purple::ChatConversation chat
-void purple_conv_chat_set_nick(chat, nick)
- Purple::Conversation::Chat chat
+void purple_chat_conversation_set_nick(chat, nick)
+ Purple::ChatConversation chat
const char * nick
const char *
-purple_conv_chat_get_nick(chat)
- Purple::Conversation::Chat chat
+purple_chat_conversation_get_nick(chat)
+ Purple::ChatConversation chat
-Purple::Conversation
-purple_find_chat(gc, id)
- Purple::Connection gc
- int id
+void purple_chat_conversation_leave(chat)
+ Purple::ChatConversation chat
-void purple_conv_chat_left(chat)
- Purple::Conversation::Chat chat
+gboolean purple_chat_conversation_has_left(chat)
+ Purple::ChatConversation chat
-gboolean purple_conv_chat_has_left(chat)
- Purple::Conversation::Chat chat
-
-Purple::Conversation::ChatBuddy
-purple_conv_chat_cb_find(chat, name)
- Purple::Conversation::Chat chat
+Purple::ChatUser
+purple_chat_conversation_find_user(chat, name)
+ Purple::ChatConversation chat
const char *name
const char *
-purple_conv_chat_cb_get_name(cb)
- Purple::Conversation::ChatBuddy cb
-
-void
-purple_conv_chat_cb_destroy(cb);
- Purple::Conversation::ChatBuddy cb
+purple_chat_user_get_name(cb)
+ Purple::ChatUser cb
diff --git a/libpurple/plugins/perl/common/ImgStore.xs b/libpurple/plugins/perl/common/ImgStore.xs
deleted file mode 100644
index 3f066fda58..0000000000
--- a/libpurple/plugins/perl/common/ImgStore.xs
+++ /dev/null
@@ -1,53 +0,0 @@
-#include "module.h"
-
-MODULE = Purple::ImgStore PACKAGE = Purple::ImgStore PREFIX = purple_imgstore_
-PROTOTYPES: ENABLE
-
-Purple::StoredImage
-purple_imgstore_add(data, size, filename)
- void *data
- size_t size
- const char *filename
-
-int
-purple_imgstore_add_with_id(data, size, filename)
- void *data
- size_t size
- const char *filename
-
-Purple::StoredImage
-purple_imgstore_find_by_id(id)
- int id
-
-gconstpointer
-purple_imgstore_get_data(i)
- Purple::StoredImage i
-
-const char *
-purple_imgstore_get_filename(i)
- Purple::StoredImage i
-
-size_t
-purple_imgstore_get_size(i)
- Purple::StoredImage i
-
-const char *
-purple_imgstore_get_extension(i)
- Purple::StoredImage i
-
-Purple::StoredImage
-purple_imgstore_ref(id)
- Purple::StoredImage id
-
-Purple::StoredImage
-purple_imgstore_unref(id)
- Purple::StoredImage id
-
-void
-purple_imgstore_ref_by_id(id)
- int id
-
-void
-purple_imgstore_unref_by_id(id)
- int id
-
diff --git a/libpurple/plugins/perl/common/MANIFEST b/libpurple/plugins/perl/common/MANIFEST
index 75e701224a..0deef2ce3e 100644
--- a/libpurple/plugins/perl/common/MANIFEST
+++ b/libpurple/plugins/perl/common/MANIFEST
@@ -7,8 +7,6 @@ Cmds.xs
Connection.xs
Conversation.xs
Debug.xs
-FT.xs
-ImgStore.xs
Log.xs
Makefile.PL
Network.xs
@@ -17,7 +15,7 @@ Plugin.xs
PluginPref.xs
Pounce.xs
Prefs.xs
-Privacy.xs
+Presence.xs
Proxy.xs
Prpl.xs
Purple.pm
@@ -28,11 +26,11 @@ SSLConn.xs
SavedStatuses.xs
Server.xs
Signal.xs
-Smiley.xs
Sound.xs
Status.xs
Stringref.xs
Util.xs
+Xfer.xs
XMLNode.xs
MANIFEST
typemap
diff --git a/libpurple/plugins/perl/common/Makefile.PL.in b/libpurple/plugins/perl/common/Makefile.PL.in
index 55589555bd..bd1ca5f723 100644
--- a/libpurple/plugins/perl/common/Makefile.PL.in
+++ b/libpurple/plugins/perl/common/Makefile.PL.in
@@ -4,14 +4,14 @@ use ExtUtils::MakeMaker;
# of the Makefile that is written.
WriteMakefile(
'NAME' => 'Purple',
- 'VERSION_FROM' => '@srcdir@/Purple.pm', # finds $VERSION
+ 'VERSION' => '@VERSION@',
'PREREQ_PM' => {}, # e.g., Module::Name => 1.1
($] >= 5.005 ? ## Add these new keywords supported since 5.005
(ABSTRACT_FROM => '@srcdir@/Purple.pm', # finds $ABSTRACT
- AUTHOR => 'Purple <http://pidgin.im/>') : ()),
- 'DEFINE' => '@DEBUG_CFLAGS@',
+ AUTHOR => 'Purple <https://pidgin.im/>') : ()),
+ 'DEFINE' => '@DEBUG_CFLAGS@ -Wno-float-equal',
'dynamic_lib' => { 'OTHERLDFLAGS' => '@LDFLAGS@' },
- 'INC' => '-I. -I@srcdir@ -I@top_srcdir@ -I@top_srcdir@/libpurple @GLIB_CFLAGS@',
+ 'INC' => '-I. -I@srcdir@ -I@top_srcdir@ -I@top_builddir@ -I@top_srcdir@/libpurple @GLIB_CFLAGS@ -DHAVE_CONFIG_H',
'OBJECT' => '$(O_FILES)', # link all the C files too
# 'OPTIMIZE' => '-g', # For debugging
'INSTALLDIRS' => 'vendor',
diff --git a/libpurple/plugins/perl/common/Makefile.mingw b/libpurple/plugins/perl/common/Makefile.mingw
index ac8944fb97..369803f6c1 100644
--- a/libpurple/plugins/perl/common/Makefile.mingw
+++ b/libpurple/plugins/perl/common/Makefile.mingw
@@ -28,9 +28,10 @@ INCLUDE_PATHS += -I. \
-I$(GTK_TOP)/include \
-I$(GTK_TOP)/include/glib-2.0 \
-I$(GTK_TOP)/lib/glib-2.0/include \
- -I$(PERL_LIB_TOP)/CORE
+ -I$(PERL_LIB_TOP)/include
-LIB_PATHS += -L$(PERL_LIB_TOP) \
+LIB_PATHS += \
+ -L$(PERL_LIB_TOP)/lib \
-L$(PERL_PLUGIN_TOP) \
-L$(PURPLE_TOP) \
-L$(GTK_TOP)/lib
@@ -49,10 +50,8 @@ XS_FILES = Account.xs \
Conversation.xs \
Core.xs \
Debug.xs \
- FT.xs \
Idle.xs \
Purple.xs \
- ImgStore.xs \
Log.xs \
Network.xs \
Notify.xs \
@@ -60,7 +59,7 @@ XS_FILES = Account.xs \
PluginPref.xs \
Pounce.xs \
Prefs.xs \
- Privacy.xs \
+ Presence.xs \
Proxy.xs \
Prpl.xs \
Request.xs \
@@ -69,11 +68,11 @@ XS_FILES = Account.xs \
SavedStatuses.xs \
Server.xs \
Signal.xs \
- Smiley.xs \
Sound.xs \
Status.xs \
Stringref.xs \
Util.xs \
+ Xfer.xs \
Whiteboard.xs \
XMLNode.xs
@@ -87,7 +86,8 @@ OBJECTS = $(C_FILES:%.c=%.o)
LIBS = -lperl510 \
-lperl \
-lpurple \
- -lglib-2.0
+ -lglib-2.0 \
+ -lgobject-2.0
include $(PIDGIN_COMMON_RULES)
@@ -110,9 +110,10 @@ install: all
$(C_FILES): $(PURPLE_CONFIG_H)
$(AUTOSPLIT):
- mkdir -p ./lib/auto
- cp Purple.pm ./lib
- $(PERL) -MAutoSplit -e 'autosplit("lib/Purple.pm")'
+ @echo -e " GEN\t$@"
+ @mkdir -p ./lib/auto
+ @cp Purple.pm ./lib
+ @$(PERL) -MAutoSplit -e 'autosplit("lib/Purple.pm")'
$(TARGET).dll: $(PURPLE_DLL).a $(PURPLE_PERL_DLL).a $(FALLBACKS) $(OBJECTS)
$(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET).dll
diff --git a/libpurple/plugins/perl/common/Network.xs b/libpurple/plugins/perl/common/Network.xs
index 5b94030360..276061eb4b 100644
--- a/libpurple/plugins/perl/common/Network.xs
+++ b/libpurple/plugins/perl/common/Network.xs
@@ -18,27 +18,22 @@ purple_network_get_port_from_fd(fd)
const char *
purple_network_get_public_ip()
-const unsigned char *
-purple_network_ip_atoi(ip)
- const char *ip
-PPCODE:
- RETVAL = purple_network_ip_atoi(ip);
- sv_setpvn(TARG, (const char *)RETVAL, 4);
- XSprePUSH;
- PUSHTARG;
-
Purple::NetworkListenData
-purple_network_listen(port, socket_type, cb, cb_data)
+purple_network_listen(port, socket_family, socket_type, map_external, cb, cb_data)
unsigned short port
+ int socket_family
int socket_type
+ gboolean map_external
Purple::NetworkListenCallback cb
gpointer cb_data
Purple::NetworkListenData
-purple_network_listen_range(start, end, socket_type, cb, cb_data)
+purple_network_listen_range(start, end, socket_family, socket_type, map_external, cb, cb_data)
unsigned short start
unsigned short end
+ int socket_family
int socket_type
+ gboolean map_external
Purple::NetworkListenCallback cb
gpointer cb_data
diff --git a/libpurple/plugins/perl/common/Notify.xs b/libpurple/plugins/perl/common/Notify.xs
index 87bb928f9c..5bac54fb4b 100644
--- a/libpurple/plugins/perl/common/Notify.xs
+++ b/libpurple/plugins/perl/common/Notify.xs
@@ -102,6 +102,10 @@ purple_notify_message(handle, type, title, primary, secondary, cb, user_data)
const char *secondary
Purple::NotifyCloseCallback cb
gpointer user_data
+CODE:
+ RETVAL = purple_notify_message(handle, type, title, primary, secondary, NULL, cb, user_data);
+OUTPUT:
+ RETVAL
void *
purple_notify_searchresults(gc, title, primary, secondary, results, cb, user_data)
@@ -135,7 +139,7 @@ purple_notify_user_info_get_entries(user_info)
PREINIT:
GList *l;
PPCODE:
- l = purple_notify_user_info_get_entries(user_info);
+ l = purple_notify_user_info_get_entries(user_info)->head;
for (; l != NULL; l = l->next) {
XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::NotifyUserInfoEntry")));
}
@@ -145,12 +149,12 @@ purple_notify_user_info_get_text_with_newline(user_info, newline)
Purple::NotifyUserInfo user_info
const char *newline
-void purple_notify_user_info_add_pair(user_info, label, value)
+void purple_notify_user_info_add_pair_html(user_info, label, value)
Purple::NotifyUserInfo user_info
const char *label
const char *value
-void purple_notify_user_info_prepend_pair(user_info, label, value)
+void purple_notify_user_info_prepend_pair_html(user_info, label, value)
Purple::NotifyUserInfo user_info
const char *label
const char *value
diff --git a/libpurple/plugins/perl/common/PluginPref.xs b/libpurple/plugins/perl/common/PluginPref.xs
index 87d42d5b41..545d247ab7 100644
--- a/libpurple/plugins/perl/common/PluginPref.xs
+++ b/libpurple/plugins/perl/common/PluginPref.xs
@@ -112,7 +112,7 @@ purple_plugin_pref_get_name(pref)
Purple::PluginPref pref
Purple::PluginPrefType
-purple_plugin_pref_get_type(pref)
+purple_plugin_pref_get_pref_type(pref)
Purple::PluginPref pref
Purple::PluginPref
@@ -170,7 +170,7 @@ purple_plugin_pref_set_name(pref, name)
const char *name
void
-purple_plugin_pref_set_type(pref, type)
+purple_plugin_pref_set_pref_type(pref, type)
Purple::PluginPref pref
Purple::PluginPrefType type
PREINIT:
@@ -185,4 +185,4 @@ CODE:
} else if (type == 3) {
gpp_type = PURPLE_PLUGIN_PREF_STRING_FORMAT;
}
- purple_plugin_pref_set_type(pref, gpp_type);
+ purple_plugin_pref_set_pref_type(pref, gpp_type);
diff --git a/libpurple/plugins/perl/common/Pounce.xs b/libpurple/plugins/perl/common/Pounce.xs
index 2d63911d8a..4190bd2f82 100644
--- a/libpurple/plugins/perl/common/Pounce.xs
+++ b/libpurple/plugins/perl/common/Pounce.xs
@@ -121,9 +121,6 @@ PPCODE:
Purple::Handle
purple_pounces_get_handle()
-gboolean
-purple_pounces_load()
-
void
purple_pounces_unregister_handler(ui)
const char *ui
diff --git a/libpurple/plugins/perl/common/Prefs.xs b/libpurple/plugins/perl/common/Prefs.xs
index 53360da69a..96019263f5 100644
--- a/libpurple/plugins/perl/common/Prefs.xs
+++ b/libpurple/plugins/perl/common/Prefs.xs
@@ -153,7 +153,7 @@ PPCODE:
}
Purple::PrefType
-purple_prefs_get_type(name)
+purple_prefs_get_pref_type(name)
const char *name
gboolean
@@ -179,11 +179,6 @@ purple_prefs_set_bool(name, value)
gboolean value
void
-purple_prefs_set_generic(name, value)
- const char *name
- gpointer value
-
-void
purple_prefs_set_int(name, value)
const char *name
int value
@@ -247,6 +242,3 @@ PPCODE:
XPUSHs(sv_2mortal(newSVpv(l->data, 0)));
g_free(l->data);
}
-
-void
-purple_prefs_update_old()
diff --git a/libpurple/plugins/perl/common/Presence.xs b/libpurple/plugins/perl/common/Presence.xs
new file mode 100644
index 0000000000..8f8bf5530e
--- /dev/null
+++ b/libpurple/plugins/perl/common/Presence.xs
@@ -0,0 +1,102 @@
+#include "module.h"
+
+MODULE = Purple::Presence PACKAGE = Purple::Presence PREFIX = purple_presence_
+PROTOTYPES: ENABLE
+
+Purple::Status
+purple_presence_get_active_status(presence)
+ Purple::Presence presence
+
+time_t
+purple_presence_get_idle_time(presence)
+ Purple::Presence presence
+
+time_t
+purple_presence_get_login_time(presence)
+ Purple::Presence presence
+
+Purple::Status
+purple_presence_get_status(presence, status_id)
+ Purple::Presence presence
+ const char *status_id
+
+void
+purple_presence_get_statuses(presence)
+ Purple::Presence presence
+PREINIT:
+ GList *l;
+PPCODE:
+ for (l = purple_presence_get_statuses(presence); l != NULL; l = l->next) {
+ XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Status")));
+ }
+
+gboolean
+purple_presence_is_available(presence)
+ Purple::Presence presence
+
+gboolean
+purple_presence_is_idle(presence)
+ Purple::Presence presence
+
+gboolean
+purple_presence_is_online(presence)
+ Purple::Presence presence
+
+gboolean
+purple_presence_is_status_active(presence, status_id)
+ Purple::Presence presence
+ const char *status_id
+
+gboolean
+purple_presence_is_status_primitive_active(presence, primitive)
+ Purple::Presence presence
+ Purple::StatusPrimitive primitive
+
+void
+purple_presence_set_idle(presence, idle, idle_time)
+ Purple::Presence presence
+ gboolean idle
+ time_t idle_time
+
+void
+purple_presence_set_login_time(presence, login_time)
+ Purple::Presence presence
+ time_t login_time
+
+void
+purple_presence_set_status_active(presence, status_id, active)
+ Purple::Presence presence
+ const char *status_id
+ gboolean active
+
+void
+purple_presence_switch_status(presence, status_id)
+ Purple::Presence presence
+ const char *status_id
+
+MODULE = Purple::Presence PACKAGE = Purple::AccountPresence PREFIX = purple_account_presence_
+PROTOTYPES: ENABLE
+
+Purple::Account
+purple_account_presence_get_account(presence)
+ Purple::AccountPresence presence
+
+Purple::AccountPresence
+purple_account_presence_new(account)
+ Purple::Account account
+
+MODULE = Purple::Presence PACKAGE = Purple::BuddyPresence PREFIX = purple_buddy_presence_
+PROTOTYPES: ENABLE
+
+gint
+purple_buddy_presence_compare(presence1, presence2)
+ Purple::BuddyPresence presence1
+ Purple::BuddyPresence presence2
+
+Purple::BuddyList::Buddy
+purple_buddy_presence_get_buddy(presence)
+ Purple::BuddyPresence presence
+
+Purple::BuddyPresence
+purple_buddy_presence_new(buddy)
+ Purple::BuddyList::Buddy buddy
diff --git a/libpurple/plugins/perl/common/Privacy.xs b/libpurple/plugins/perl/common/Privacy.xs
deleted file mode 100644
index dbb63a594d..0000000000
--- a/libpurple/plugins/perl/common/Privacy.xs
+++ /dev/null
@@ -1,33 +0,0 @@
-#include "module.h"
-
-MODULE = Purple::Privacy PACKAGE = Purple::Privacy PREFIX = purple_privacy_
-PROTOTYPES: ENABLE
-
-gboolean
-purple_privacy_permit_add(account, name, local_only)
- Purple::Account account
- const char * name
- gboolean local_only
-
-gboolean
-purple_privacy_permit_remove(account, name, local_only)
- Purple::Account account
- const char * name
- gboolean local_only
-
-gboolean
-purple_privacy_deny_add(account, name, local_only)
- Purple::Account account
- const char * name
- gboolean local_only
-
-gboolean
-purple_privacy_deny_remove(account, name, local_only)
- Purple::Account account
- const char * name
- gboolean local_only
-
-gboolean
-purple_privacy_check(account, who)
- Purple::Account account
- const char * who
diff --git a/libpurple/plugins/perl/common/Proxy.xs b/libpurple/plugins/perl/common/Proxy.xs
index bba23733e8..02642593fe 100644
--- a/libpurple/plugins/perl/common/Proxy.xs
+++ b/libpurple/plugins/perl/common/Proxy.xs
@@ -44,7 +44,7 @@ purple_proxy_info_get_port(info)
Purple::ProxyInfo info
Purple::ProxyType
-purple_proxy_info_get_type(info)
+purple_proxy_info_get_proxy_type(info)
Purple::ProxyInfo info
const char *
@@ -70,7 +70,7 @@ purple_proxy_info_set_port(info, port)
int port
void
-purple_proxy_info_set_type(info, type)
+purple_proxy_info_set_proxy_type(info, type)
Purple::ProxyInfo info
Purple::ProxyType type
diff --git a/libpurple/plugins/perl/common/Prpl.xs b/libpurple/plugins/perl/common/Prpl.xs
index 765ce5d41a..3e13133d21 100644
--- a/libpurple/plugins/perl/common/Prpl.xs
+++ b/libpurple/plugins/perl/common/Prpl.xs
@@ -65,7 +65,7 @@ CODE:
if (!gc)
RETVAL = 0;
else {
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
if (prpl_info && prpl_info->send_raw != NULL) {
RETVAL = prpl_info->send_raw(gc, str, strlen(str));
} else {
diff --git a/libpurple/plugins/perl/common/Purple.pm b/libpurple/plugins/perl/common/Purple.pm
index 6357325aab..8288096b30 100644
--- a/libpurple/plugins/perl/common/Purple.pm
+++ b/libpurple/plugins/perl/common/Purple.pm
@@ -27,8 +27,6 @@ our @EXPORT = qw(
);
-our $VERSION = '0.01';
-
sub AUTOLOAD {
# This AUTOLOAD is used to 'autoload' constants from the constant()
# XS function.
@@ -77,7 +75,7 @@ loaded in Purple as plugins. The scripts can interact with IMs, chats,
accounts, the buddy list, libpurple signals, and more.
The API for the perl interface is very similar to that of the Purple C
-API, which can be viewed at http://developer.pidgin.im/doxygen/ or in
+API, which can be viewed at https://developer.pidgin.im/doxygen/ or in
the header files in the Purple source tree.
=head1 FUNCTIONS
@@ -112,9 +110,9 @@ Returns a list of all instant messages currently open.
=head1 SEE ALSO
-Purple C API documentation - http://developer.pidgin.im/doxygen/
+Purple C API documentation - https://developer.pidgin.im/doxygen/
-Purple website - http://pidgin.im/
+Purple website - https://pidgin.im/
=head1 AUTHOR
diff --git a/libpurple/plugins/perl/common/Purple.xs b/libpurple/plugins/perl/common/Purple.xs
index 6d760039f8..e70d04ec57 100644
--- a/libpurple/plugins/perl/common/Purple.xs
+++ b/libpurple/plugins/perl/common/Purple.xs
@@ -7,15 +7,14 @@ PURPLE_PERL_BOOT_PROTO(Account__Option);
PURPLE_PERL_BOOT_PROTO(Buddy__Icon);
PURPLE_PERL_BOOT_PROTO(BuddyList);
PURPLE_PERL_BOOT_PROTO(Certificate);
-PURPLE_PERL_BOOT_PROTO(Cipher);
PURPLE_PERL_BOOT_PROTO(Cmd);
PURPLE_PERL_BOOT_PROTO(Connection);
PURPLE_PERL_BOOT_PROTO(Conversation);
PURPLE_PERL_BOOT_PROTO(Core);
PURPLE_PERL_BOOT_PROTO(Debug);
+PURPLE_PERL_BOOT_PROTO(Hash);
PURPLE_PERL_BOOT_PROTO(Xfer);
PURPLE_PERL_BOOT_PROTO(Idle);
-PURPLE_PERL_BOOT_PROTO(ImgStore);
PURPLE_PERL_BOOT_PROTO(Log);
PURPLE_PERL_BOOT_PROTO(Network);
PURPLE_PERL_BOOT_PROTO(Notify);
@@ -23,7 +22,6 @@ PURPLE_PERL_BOOT_PROTO(Plugin);
PURPLE_PERL_BOOT_PROTO(PluginPref);
PURPLE_PERL_BOOT_PROTO(Pounce);
PURPLE_PERL_BOOT_PROTO(Prefs);
-PURPLE_PERL_BOOT_PROTO(Privacy);
PURPLE_PERL_BOOT_PROTO(Proxy);
PURPLE_PERL_BOOT_PROTO(Prpl);
PURPLE_PERL_BOOT_PROTO(Request);
@@ -32,7 +30,6 @@ PURPLE_PERL_BOOT_PROTO(SSL);
PURPLE_PERL_BOOT_PROTO(SavedStatus);
PURPLE_PERL_BOOT_PROTO(Serv);
PURPLE_PERL_BOOT_PROTO(Signal);
-PURPLE_PERL_BOOT_PROTO(Smiley);
PURPLE_PERL_BOOT_PROTO(Sound);
PURPLE_PERL_BOOT_PROTO(Status);
PURPLE_PERL_BOOT_PROTO(Stringref);
@@ -49,15 +46,14 @@ BOOT:
PURPLE_PERL_BOOT(Buddy__Icon);
PURPLE_PERL_BOOT(BuddyList);
PURPLE_PERL_BOOT(Certificate);
- PURPLE_PERL_BOOT(Cipher);
PURPLE_PERL_BOOT(Cmd);
PURPLE_PERL_BOOT(Connection);
PURPLE_PERL_BOOT(Conversation);
PURPLE_PERL_BOOT(Core);
PURPLE_PERL_BOOT(Debug);
+ PURPLE_PERL_BOOT(Hash);
PURPLE_PERL_BOOT(Xfer);
PURPLE_PERL_BOOT(Idle);
- PURPLE_PERL_BOOT(ImgStore);
PURPLE_PERL_BOOT(Log);
PURPLE_PERL_BOOT(Network);
PURPLE_PERL_BOOT(Notify);
@@ -65,7 +61,6 @@ BOOT:
PURPLE_PERL_BOOT(PluginPref);
PURPLE_PERL_BOOT(Pounce);
PURPLE_PERL_BOOT(Prefs);
- PURPLE_PERL_BOOT(Privacy);
PURPLE_PERL_BOOT(Proxy);
PURPLE_PERL_BOOT(Prpl);
PURPLE_PERL_BOOT(Request);
@@ -74,7 +69,6 @@ BOOT:
PURPLE_PERL_BOOT(SavedStatus);
PURPLE_PERL_BOOT(Serv);
PURPLE_PERL_BOOT(Signal);
- PURPLE_PERL_BOOT(Smiley);
PURPLE_PERL_BOOT(Sound);
PURPLE_PERL_BOOT(Status);
PURPLE_PERL_BOOT(Stringref);
diff --git a/libpurple/plugins/perl/common/Request.xs b/libpurple/plugins/perl/common/Request.xs
index 499a6756a8..5d30af234f 100644
--- a/libpurple/plugins/perl/common/Request.xs
+++ b/libpurple/plugins/perl/common/Request.xs
@@ -144,7 +144,7 @@ CODE:
gpr->cancel_fun = purple_perl_sv_from_fun(handle, cancel_cb);
g_free(basename);
- RETVAL = purple_request_input(handle, title, primary, secondary, default_value, multiline, masked, hint, ok_text, G_CALLBACK(purple_perl_request_ok_cb), cancel_text, G_CALLBACK(purple_perl_request_cancel_cb), NULL, NULL, NULL, gpr);
+ RETVAL = purple_request_input(handle, title, primary, secondary, default_value, multiline, masked, hint, ok_text, G_CALLBACK(purple_perl_request_ok_cb), cancel_text, G_CALLBACK(purple_perl_request_cancel_cb), NULL, gpr);
OUTPUT:
RETVAL
@@ -167,7 +167,7 @@ CODE:
gpr->cancel_fun = purple_perl_sv_from_fun(handle, cancel_cb);
g_free(basename);
- RETVAL = purple_request_file(handle, title, filename, savedialog, G_CALLBACK(purple_perl_request_ok_cb), G_CALLBACK(purple_perl_request_cancel_cb), NULL, NULL, NULL, gpr);
+ RETVAL = purple_request_file(handle, title, filename, savedialog, G_CALLBACK(purple_perl_request_ok_cb), G_CALLBACK(purple_perl_request_cancel_cb), NULL, gpr);
OUTPUT:
RETVAL
@@ -193,7 +193,7 @@ CODE:
gpr->cancel_fun = purple_perl_sv_from_fun(handle, cancel_cb);
g_free(basename);
- RETVAL = purple_request_fields(handle, title, primary, secondary, fields, ok_text, G_CALLBACK(purple_perl_request_ok_cb), cancel_text, G_CALLBACK(purple_perl_request_cancel_cb), NULL, NULL, NULL, gpr);
+ RETVAL = purple_request_fields(handle, title, primary, secondary, fields, ok_text, G_CALLBACK(purple_perl_request_ok_cb), cancel_text, G_CALLBACK(purple_perl_request_cancel_cb), NULL, gpr);
OUTPUT:
RETVAL
@@ -287,51 +287,60 @@ Purple::Request::Field
purple_request_field_choice_new(class, id, text, default_value = 0)
const char *id
const char *text
- int default_value
+ gpointer default_value
C_ARGS: id, text, default_value
void
-purple_request_field_choice_add(field, label)
+purple_request_field_choice_add(field, label, value)
Purple::Request::Field field
const char *label
+ gpointer value
-int
+gpointer
purple_request_field_choice_get_default_value(field)
Purple::Request::Field field
-void
-purple_request_field_choice_get_labels(field)
- Purple::Request::Field field
-PREINIT:
- GList *l;
-PPCODE:
- for (l = purple_request_field_choice_get_labels(field); l != NULL; l = l->next) {
- XPUSHs(sv_2mortal(newSVpv(l->data, 0)));
- }
-
-int
+ # I'm not sure, if this is the correct implementation - if anyone will need it,
+ # he will add this back to API.
+ #void
+ #purple_request_field_choice_get_elements(field)
+ # Purple::Request::Field field
+ #PREINIT:
+ # GList *l;
+ #PPCODE:
+ # for (l = purple_request_field_choice_get_elements(field); l != NULL; l = l->next) {
+ # XPUSHs(sv_2mortal(newSVpv(l->data, 0)));
+ # l = l->next;
+ # if (l == NULL)
+ # break;
+ # XPUSHs(l->data);
+ # }
+
+gpointer
purple_request_field_choice_get_value(field)
Purple::Request::Field field
void
purple_request_field_choice_set_default_value(field, default_value)
Purple::Request::Field field
- int default_value
+ gpointer default_value
void
purple_request_field_choice_set_value(field, value)
Purple::Request::Field field
- int value
+ gpointer value
MODULE = Purple::Request PACKAGE = Purple::Request::Field PREFIX = purple_request_field_
PROTOTYPES: ENABLE
Purple::Request::Field
-purple_request_field_int_new(clas, id, text, default_value = 0)
+purple_request_field_int_new(clas, id, text, default_value = 0, lower_bound = INT_MIN, upper_bound = INT_MAX)
const char *id
const char *text
int default_value
- C_ARGS: id, text, default_value
+ int lower_bound
+ int upper_bound
+ C_ARGS: id, text, default_value, lower_bound, upper_bound
int
purple_request_field_int_get_default_value(field)
@@ -374,12 +383,6 @@ purple_request_field_list_new(class, id, text)
C_ARGS: id, text
void
-purple_request_field_list_add(field, item, data)
- Purple::Request::Field field
- const char *item
- void * data
-
-void
purple_request_field_list_add_icon(field, item, icon_path, data)
Purple::Request::Field field
const char *item
@@ -484,10 +487,6 @@ purple_request_field_string_get_value(field)
Purple::Request::Field field
gboolean
-purple_request_field_string_is_editable(field)
- Purple::Request::Field field
-
-gboolean
purple_request_field_string_is_masked(field)
Purple::Request::Field field
@@ -501,11 +500,6 @@ purple_request_field_string_set_default_value(field, default_value)
const char *default_value
void
-purple_request_field_string_set_editable(field, editable)
- Purple::Request::Field field
- gboolean editable
-
-void
purple_request_field_string_set_masked(field, masked)
Purple::Request::Field field
gboolean masked
@@ -562,11 +556,11 @@ purple_request_field_get_label(field)
Purple::Request::Field field
Purple::RequestFieldType
-purple_request_field_get_type(field)
+purple_request_field_get_field_type(field)
Purple::Request::Field field
const char *
-purple_request_field_get_type_hint(field)
+purple_request_field_get_field_type_hint(field)
Purple::Request::Field field
gboolean
@@ -608,7 +602,7 @@ purple_request_fields_get_bool(fields, id)
Purple::Request::Fields fields
const char *id
-int
+gpointer
purple_request_fields_get_choice(fields, id)
Purple::Request::Fields fields
const char *id
@@ -637,7 +631,7 @@ void
purple_request_fields_get_required(fields)
Purple::Request::Fields fields
PREINIT:
- GList *l;
+ const GList *l;
PPCODE:
for (l = purple_request_fields_get_required(fields); l != NULL; l = l->next) {
XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Request::Field")));
diff --git a/libpurple/plugins/perl/common/Roomlist.xs b/libpurple/plugins/perl/common/Roomlist.xs
index 7b2727c890..1c993d9442 100644
--- a/libpurple/plugins/perl/common/Roomlist.xs
+++ b/libpurple/plugins/perl/common/Roomlist.xs
@@ -50,10 +50,6 @@ purple_roomlist_new(account)
Purple::Account account
void
-purple_roomlist_ref(list)
- Purple::Roomlist list
-
-void
purple_roomlist_room_add(list, room)
Purple::Roomlist list
Purple::Roomlist::Room room
@@ -94,7 +90,3 @@ void
purple_roomlist_show_with_account(account)
Purple::Account account
-void
-purple_roomlist_unref(list)
- Purple::Roomlist list
-
diff --git a/libpurple/plugins/perl/common/SSLConn.xs b/libpurple/plugins/perl/common/SSLConn.xs
index dbc337ca37..3730e80683 100644
--- a/libpurple/plugins/perl/common/SSLConn.xs
+++ b/libpurple/plugins/perl/common/SSLConn.xs
@@ -16,13 +16,6 @@ purple_ssl_input_add(gsc, func, data)
Purple::Ssl::Connection gsc
Purple::SslInputFunction func
-Purple::Ssl::Connection
-purple_ssl_connect_fd(account, fd, func, error_func, data)
- Purple::Account account
- int fd
- PurpleSslInputFunction func
- PurpleSslErrorFunction error_func
-
*/
MODULE = Purple::SSL PACKAGE = Purple::SSL PREFIX = purple_ssl_
diff --git a/libpurple/plugins/perl/common/SavedStatuses.xs b/libpurple/plugins/perl/common/SavedStatuses.xs
index 6a8b27e659..067ad0c495 100644
--- a/libpurple/plugins/perl/common/SavedStatuses.xs
+++ b/libpurple/plugins/perl/common/SavedStatuses.xs
@@ -4,7 +4,7 @@
* type I get errors from gcc. One way about ignoring types in a cast, and the
* other about assigning to read-only variables.
const Purple::StatusType
-purple_savedstatus_substatus_get_type(substatus)
+purple_savedstatus_substatus_get_status_type(substatus)
const Purple::SavedStatus::Sub substatus
*/
@@ -22,7 +22,7 @@ purple_savedstatus_set_title(status, title)
const char *title
void
-purple_savedstatus_set_type(status, type)
+purple_savedstatus_set_primitive_type(status, type)
Purple::SavedStatus status
Purple::StatusPrimitive type
@@ -88,7 +88,7 @@ purple_savedstatus_get_title(saved_status)
const Purple::SavedStatus saved_status
Purple::StatusPrimitive
-purple_savedstatus_get_type(saved_status)
+purple_savedstatus_get_primitive_type(saved_status)
const Purple::SavedStatus saved_status
const char *
diff --git a/libpurple/plugins/perl/common/Server.xs b/libpurple/plugins/perl/common/Server.xs
index 13e2aefcbf..f9b604aaec 100644
--- a/libpurple/plugins/perl/common/Server.xs
+++ b/libpurple/plugins/perl/common/Server.xs
@@ -5,58 +5,44 @@ PROTOTYPES: ENABLE
void
-serv_add_deny(con, a)
+purple_serv_add_deny(con, a)
Purple::Connection con
const char * a
void
-serv_add_permit(a, b)
+purple_serv_add_permit(a, b)
Purple::Connection a
const char * b
void
-serv_alias_buddy(buddy)
+purple_serv_alias_buddy(buddy)
Purple::BuddyList::Buddy buddy
void
-serv_chat_invite(con, a, b, c)
+purple_serv_chat_invite(con, a, b, c)
Purple::Connection con
int a
const char * b
const char * c
void
-serv_chat_leave(a, b)
+purple_serv_chat_leave(a, b)
Purple::Connection a
int b
-int
-serv_chat_send(con, a, b, flags)
- Purple::Connection con
- int a
- const char * b
- Purple::MessageFlags flags
-
-void
-serv_chat_whisper(con, a, b, c)
- Purple::Connection con
- int a
- const char * b
- const char * c
-
void
-serv_get_info(con, a)
+purple_serv_get_info(con, a)
Purple::Connection con
const char * a
void
-serv_got_alias(gc, who, alias)
+purple_serv_got_alias(gc, who, alias)
Purple::Connection gc
const char *who
const char *alias
void
-serv_got_chat_in(g, id, who, chatflags, message, mtime)
+purple_serv_got_chat_in(g, id, who, chatflags, message, mtime)
Purple::Connection g
int id
const char *who
@@ -65,7 +51,7 @@ serv_got_chat_in(g, id, who, chatflags, message, mtime)
time_t mtime
void
-serv_got_chat_invite(gc, name, who, message, components)
+purple_serv_got_chat_invite(gc, name, who, message, components)
Purple::Connection gc
const char *name
const char *who
@@ -89,41 +75,41 @@ CODE:
g_hash_table_insert(t_GHash, t_key, t_value);
}
- serv_got_chat_invite(gc, name, who, message, t_GHash);
+ purple_serv_got_chat_invite(gc, name, who, message, t_GHash);
void
-serv_got_chat_left(g, id)
+purple_serv_got_chat_left(g, id)
Purple::Connection g
int id
void
-serv_got_im(gc, who, msg, imflags, mtime)
+purple_serv_got_im(gc, who, msg, imflags, mtime)
Purple::Connection gc
const char *who
const char *msg
Purple::MessageFlags imflags
time_t mtime
-Purple::Conversation
-serv_got_joined_chat(gc, id, name)
+Purple::ChatConversation
+purple_serv_got_joined_chat(gc, id, name)
Purple::Connection gc
int id
const char *name
void
-serv_got_typing(gc, name, timeout, state)
+purple_serv_got_typing(gc, name, timeout, state)
Purple::Connection gc
const char *name
int timeout
- Purple::TypingState state
+ Purple::IMTypingState state
void
-serv_got_typing_stopped(gc, name)
+purple_serv_got_typing_stopped(gc, name)
Purple::Connection gc
const char *name
void
-serv_join_chat(conn, components)
+purple_serv_join_chat(conn, components)
Purple::Connection conn
HV * components
PREINIT:
@@ -143,17 +129,17 @@ CODE:
g_hash_table_insert(t_GHash, t_key, t_value);
}
- serv_join_chat(conn, t_GHash);
+ purple_serv_join_chat(conn, t_GHash);
g_hash_table_destroy(t_GHash);
void
-serv_move_buddy(buddy, group1, group2)
+purple_serv_move_buddy(buddy, group1, group2)
Purple::BuddyList::Buddy buddy
Purple::BuddyList::Group group1
Purple::BuddyList::Group group2
void
-serv_reject_chat(con, components)
+purple_serv_reject_chat(con, components)
Purple::Connection con
SV * components
INIT:
@@ -174,43 +160,36 @@ CODE:
g_hash_table_insert(t_GHash, t_key, t_value);
}
- serv_reject_chat(con, t_GHash);
+ purple_serv_reject_chat(con, t_GHash);
void
-serv_rem_deny(con, a)
+purple_serv_rem_deny(con, a)
Purple::Connection con
const char * a
void
-serv_rem_permit(con, a)
+purple_serv_rem_permit(con, a)
Purple::Connection con
const char * a
void
-serv_send_file(gc, who, file)
+purple_serv_send_file(gc, who, file)
Purple::Connection gc
const char *who
const char *file
int
-serv_send_im(con, a, b, flags )
- Purple::Connection con
- const char * a
- const char * b
- Purple::MessageFlags flags
-
-int
-serv_send_typing(con, a, state)
+purple_serv_send_typing(con, a, state)
Purple::Connection con
const char * a
- Purple::TypingState state
+ Purple::IMTypingState state
void
-serv_set_info(con, a)
+purple_serv_set_info(con, a)
Purple::Connection con
const char * a
void
-serv_set_permit_deny(con)
+purple_serv_set_permit_deny(con)
Purple::Connection con
diff --git a/libpurple/plugins/perl/common/Smiley.xs b/libpurple/plugins/perl/common/Smiley.xs
deleted file mode 100644
index c00b694828..0000000000
--- a/libpurple/plugins/perl/common/Smiley.xs
+++ /dev/null
@@ -1,80 +0,0 @@
-#include "module.h"
-
-MODULE = Purple::Smiley PACKAGE = Purple::Smiley PREFIX = purple_smiley_
-PROTOTYPES: ENABLE
-
-Purple::Smiley
-purple_smiley_new(img, shortcut)
- Purple::StoredImage img
- const char * shortcut
-
-Purple::Smiley
-purple_smiley_new_from_file(shortcut, filepath)
- const char * shortcut
- const char * filepath
-
-void
-purple_smiley_delete(smiley)
- Purple::Smiley smiley
-
-gboolean
-purple_smiley_set_shortcut(smiley, shortcut)
- Purple::Smiley smiley
- const char * shortcut
-
-void
-purple_smiley_set_data(smiley, data, data_len)
- Purple::Smiley smiley
- guchar * data
- size_t data_len
-
-const char *
-purple_smiley_get_shortcut(smiley)
- Purple::Smiley smiley
-
-const char *
-purple_smiley_get_checksum(smiley)
- Purple::Smiley smiley
-
-Purple::StoredImage
-purple_smiley_get_stored_image(smiley)
- Purple::Smiley smiley
-
-gconstpointer
-purple_smiley_get_data(smiley, len)
- Purple::Smiley smiley
- size_t * len
-
-const char *
-purple_smiley_get_extension(smiley)
- Purple::Smiley smiley
-
-
-gchar_own *
-purple_smiley_get_full_path(smiley)
- Purple::Smiley smiley
-
-
-MODULE = Purple::Smiley PACKAGE = Purple::Smileys PREFIX = purple_smileys_
-PROTOTYPES: ENABLE
-
-void
-purple_smileys_get_all()
-PREINIT:
- GList *l;
-PPCODE:
- for (l = purple_smileys_get_all(); l != NULL; l = g_list_delete_link(l, l)) {
- XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Smiley")));
- }
-
-Purple::Smiley
-purple_smileys_find_by_shortcut(shortcut)
- const char * shortcut
-
-Purple::Smiley
-purple_smileys_find_by_checksum(checksum)
- const char * checksum
-
-const char *
-purple_smileys_get_storing_dir()
-
diff --git a/libpurple/plugins/perl/common/Status.xs b/libpurple/plugins/perl/common/Status.xs
index 7c8d615680..8c50ace626 100644
--- a/libpurple/plugins/perl/common/Status.xs
+++ b/libpurple/plugins/perl/common/Status.xs
@@ -39,22 +39,14 @@ purple_status_set_active_with_attrs(status, active, args)
*/
-MODULE = Purple::Status PACKAGE = Purple::Presence PREFIX = purple_presence_
+MODULE = Purple::Status PACKAGE = Purple::Primitive PREFIX = purple_primitive_
PROTOTYPES: ENABLE
BOOT:
{
- HV *context_stash = gv_stashpv("Purple::Presence::Context", 1);
HV *primitive_stash = gv_stashpv("Purple::Status::Primitive", 1);
- static const constiv *civ, context_const_iv[] = {
-#define const_iv(name) {#name, (IV)PURPLE_PRESENCE_CONTEXT_##name}
- const_iv(UNSET),
- const_iv(ACCOUNT),
- const_iv(CONV),
- const_iv(BUDDY),
- };
- static const constiv primitive_const_iv[] = {
+ static const constiv *civ, primitive_const_iv[] = {
#undef const_iv
#define const_iv(name) {#name, (IV)PURPLE_STATUS_##name}
const_iv(UNSET),
@@ -67,150 +59,10 @@ BOOT:
const_iv(MOBILE),
};
- for (civ = context_const_iv + sizeof(context_const_iv) / sizeof(context_const_iv[0]); civ-- > context_const_iv; )
- newCONSTSUB(context_stash, (char *)civ->name, newSViv(civ->iv));
-
for (civ = primitive_const_iv + sizeof(primitive_const_iv) / sizeof(primitive_const_iv[0]); civ-- > primitive_const_iv; )
newCONSTSUB(primitive_stash, (char *)civ->name, newSViv(civ->iv));
}
-void
-purple_presence_add_list(presence, source_list)
- Purple::Presence presence
- SV *source_list
-PREINIT:
- GList *t_GL;
- int i, t_len;
-PPCODE:
- t_GL = NULL;
- t_len = av_len((AV *)SvRV(source_list));
-
- for (i = 0; i <= t_len; i++) {
- t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(source_list), i, 0)));
- }
- purple_presence_add_list(presence, t_GL);
- g_list_free(t_GL);
-
-void
-purple_presence_add_status(presence, status)
- Purple::Presence presence
- Purple::Status status
-
-gint
-purple_presence_compare(presence1, presence2)
- Purple::Presence presence1
- Purple::Presence presence2
-
-void
-purple_presence_destroy(presence)
- Purple::Presence presence
-
-Purple::Account
-purple_presence_get_account(presence)
- Purple::Presence presence
-
-Purple::Status
-purple_presence_get_active_status(presence)
- Purple::Presence presence
-
-const char *
-purple_presence_get_chat_user(presence)
- Purple::Presence presence
-
-Purple::PresenceContext
-purple_presence_get_context(presence)
- Purple::Presence presence
-
-Purple::Conversation
-purple_presence_get_conversation(presence)
- Purple::Presence presence
-
-time_t
-purple_presence_get_idle_time(presence)
- Purple::Presence presence
-
-time_t
-purple_presence_get_login_time(presence)
- Purple::Presence presence
-
-Purple::Status
-purple_presence_get_status(presence, status_id)
- Purple::Presence presence
- const char *status_id
-
-void
-purple_presence_get_statuses(presence)
- Purple::Presence presence
-PREINIT:
- GList *l;
-PPCODE:
- for (l = purple_presence_get_statuses(presence); l != NULL; l = l->next) {
- XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Status")));
- }
-
-gboolean
-purple_presence_is_available(presence)
- Purple::Presence presence
-
-gboolean
-purple_presence_is_idle(presence)
- Purple::Presence presence
-
-gboolean
-purple_presence_is_online(presence)
- Purple::Presence presence
-
-gboolean
-purple_presence_is_status_active(presence, status_id)
- Purple::Presence presence
- const char *status_id
-
-gboolean
-purple_presence_is_status_primitive_active(presence, primitive)
- Purple::Presence presence
- Purple::StatusPrimitive primitive
-
-Purple::Presence
-purple_presence_new(context)
- Purple::PresenceContext context
-
-Purple::Presence
-purple_presence_new_for_account(account)
- Purple::Account account
-
-Purple::Presence
-purple_presence_new_for_buddy(buddy)
- Purple::BuddyList::Buddy buddy
-
-Purple::Presence
-purple_presence_new_for_conv(conv)
- Purple::Conversation conv
-
-void
-purple_presence_set_idle(presence, idle, idle_time)
- Purple::Presence presence
- gboolean idle
- time_t idle_time
-
-void
-purple_presence_set_login_time(presence, login_time)
- Purple::Presence presence
- time_t login_time
-
-void
-purple_presence_set_status_active(presence, status_id, active)
- Purple::Presence presence
- const char *status_id
- gboolean active
-
-void
-purple_presence_switch_status(presence, status_id)
- Purple::Presence presence
- const char *status_id
-
-MODULE = Purple::Status PACKAGE = Purple::Primitive PREFIX = purple_primitive_
-PROTOTYPES: ENABLE
-
const char *
purple_primitive_get_id_from_type(type)
Purple::StatusPrimitive type
@@ -223,30 +75,30 @@ Purple::StatusPrimitive
purple_primitive_get_type_from_id(id)
const char *id
-MODULE = Purple::Status PACKAGE = Purple::StatusAttr PREFIX = purple_status_attr_
+MODULE = Purple::Status PACKAGE = Purple::StatusAttr PREFIX = purple_status_attribute_
PROTOTYPES: ENABLE
void
-purple_status_attr_destroy(attr)
+purple_status_attribute_destroy(attr)
Purple::StatusAttr attr
const char *
-purple_status_attr_get_id(attr)
+purple_status_attribute_get_id(attr)
Purple::StatusAttr attr
const char *
-purple_status_attr_get_name(attr)
+purple_status_attribute_get_name(attr)
Purple::StatusAttr attr
-Purple::Value
-purple_status_attr_get_value(attr)
+GValue *
+purple_status_attribute_get_value(attr)
Purple::StatusAttr attr
Purple::StatusAttr
-purple_status_attr_new(id, name, value_type)
+purple_status_attribute_new(id, name, value_type)
const char *id
const char *name
- Purple::Value value_type
+ GValue *value_type
MODULE = Purple::Status PACKAGE = Purple::Status PREFIX = purple_status_
PROTOTYPES: ENABLE
@@ -256,10 +108,6 @@ purple_status_compare(status1, status2)
Purple::Status status1
Purple::Status status2
-void
-purple_status_destroy(status)
- Purple::Status status
-
gboolean
purple_status_get_attr_boolean(status, id)
Purple::Status status
@@ -275,13 +123,13 @@ purple_status_get_attr_string(status, id)
Purple::Status status
const char *id
-Purple::Value
+GValue *
purple_status_get_attr_value(status, id)
Purple::Status status
const char *id
Purple::Handle
-purple_status_get_handle()
+purple_statuses_get_handle()
const char *
purple_status_get_id(status)
@@ -296,7 +144,7 @@ purple_status_get_presence(status)
Purple::Status status
Purple::StatusType
-purple_status_get_type(status)
+purple_status_get_status_type(status)
Purple::Status status
gboolean
@@ -329,29 +177,10 @@ purple_status_set_active(status, active)
Purple::Status status
gboolean active
-void
-purple_status_set_attr_boolean(status, id, value)
- Purple::Status status
- const char *id
- gboolean value
-
-void
-purple_status_set_attr_string(status, id, value)
- Purple::Status status
- const char *id
- const char *value
-
MODULE = Purple::Status PACKAGE = Purple::StatusType PREFIX = purple_status_type_
PROTOTYPES: ENABLE
void
-purple_status_type_add_attr(status_type, id, name, value)
- Purple::StatusType status_type
- const char *id
- const char *name
- Purple::Value value
-
-void
purple_status_type_destroy(status_type)
Purple::StatusType status_type
@@ -397,10 +226,6 @@ const char *
purple_status_type_get_name(status_type)
Purple::StatusType status_type
-const char *
-purple_status_type_get_primary_attr(status_type)
- Purple::StatusType status_type
-
Purple::StatusPrimitive
purple_status_type_get_primitive(status_type)
Purple::StatusType status_type
@@ -440,8 +265,3 @@ purple_status_type_new_full(primitive, id, name, saveable, user_settable, indepe
gboolean saveable
gboolean user_settable
gboolean independent
-
-void
-purple_status_type_set_primary_attr(status_type, attr_id)
- Purple::StatusType status_type
- const char *attr_id
diff --git a/libpurple/plugins/perl/common/Util.xs b/libpurple/plugins/perl/common/Util.xs
index 5eafa72c6d..8eb03bdfe2 100644
--- a/libpurple/plugins/perl/common/Util.xs
+++ b/libpurple/plugins/perl/common/Util.xs
@@ -1,31 +1,5 @@
#include "module.h"
-static void
-purple_perl_util_url_cb(PurpleUtilFetchUrlData *url_data, void *user_data,
- const gchar *url_text, size_t size,
- const gchar *error_message)
-{
- SV *sv = (SV *)user_data;
- dSP;
- ENTER;
- SAVETMPS;
- PUSHMARK(SP);
-
- XPUSHs(sv_2mortal(newSVpvn(url_text, size)));
- PUTBACK;
-
- call_sv(sv, G_EVAL | G_SCALAR);
- SPAGAIN;
-
- /* XXX Make sure this destroys it correctly and that we don't want
- * something like sv_2mortal(sv) or something else here instead. */
- SvREFCNT_dec(sv);
-
- PUTBACK;
- FREETMPS;
- LEAVE;
-}
-
static void markup_find_tag_foreach(GQuark key_id, char *data, HV *hv) {
const char *key = NULL;
key = g_quark_to_string(key_id);
@@ -136,37 +110,6 @@ const char *
purple_url_encode(str)
const char *str
- # XXX: this made perl assert()...
- #
- #gboolean
- #purple_url_parse(url, OUTLIST gchar_own *ret_host, OUTLIST int ret_port, OUTLIST gchar_own *ret_path, OUTLIST gchar_own *ret_user, OUTLIST gchar_own *ret_passwd)
- # const char *url
- # PROTOTYPE: $
-
-void
-purple_url_parse(url)
- const char *url
- PREINIT:
- char *ret_host;
- int ret_port;
- char *ret_path;
- char *ret_user;
- char *ret_passwd;
- gboolean ret;
- PPCODE:
- ret = purple_url_parse(url, &ret_host, &ret_port, &ret_path, &ret_user, &ret_passwd);
- XPUSHs(sv_2mortal(newSViv(ret)));
- XPUSHs(ret_host ? sv_2mortal(newSVpv(ret_host, 0)) : sv_2mortal(newSV(0)));
- XPUSHs(sv_2mortal(newSViv(ret_port)));
- XPUSHs(ret_path ? sv_2mortal(newSVpv(ret_path, 0)) : sv_2mortal(newSV(0)));
- XPUSHs(ret_user ? sv_2mortal(newSVpv(ret_user, 0)) : sv_2mortal(newSV(0)));
- XPUSHs(ret_passwd ? sv_2mortal(newSVpv(ret_passwd, 0)) : sv_2mortal(newSV(0)));
- g_free(ret_host);
- g_free(ret_path);
- g_free(ret_user);
- g_free(ret_passwd);
-
-
const char *
purple_user_dir()
@@ -203,6 +146,14 @@ gboolean
purple_ip_address_is_valid(ip)
const char* ip
+gboolean
+purple_ipv4_address_is_valid(ip)
+ const char* ip
+
+gboolean
+purple_ipv6_address_is_valid(ip)
+ const char* ip
+
const char*
purple_normalize_nocase(account, str)
Purple::Account account
@@ -453,30 +404,6 @@ purple_markup_unescape_entity(text)
MODULE = Purple::Util PACKAGE = Purple::Util PREFIX = purple_util_
PROTOTYPES: ENABLE
- #XXX: expand...
-void
-purple_util_fetch_url(plugin, url, full, user_agent, http11, cb)
- Purple::Plugin plugin
- const char *url
- gboolean full
- const char *user_agent
- gboolean http11
- SV * cb
-PREINIT:
- PurpleUtilFetchUrlData *data;
-PPCODE:
- /* XXX: i don't like this... only plugins can use it... */
- SV *sv = purple_perl_sv_from_fun(plugin, cb);
-
- if (sv != NULL) {
- data = purple_util_fetch_url(url, full, user_agent, http11,
- purple_perl_util_url_cb, sv);
- XPUSHs(sv_2mortal(purple_perl_bless_object(data, "Purple::Util::FetchUrlData")));
- } else {
- purple_debug_warning("perl", "Callback not a valid type, only strings and coderefs allowed in purple_util_fetch_url.\n");
- XSRETURN_UNDEF;
- }
-
void
purple_util_set_user_dir(dir)
const char *dir
@@ -499,14 +426,6 @@ purple_util_format_song_info(title, artist, album, unused)
const char* album
gpointer unused
-const char*
-purple_util_get_image_extension(const char *data, size_t length(data))
- PROTOTYPE: $
-
-gchar_own*
-purple_util_get_image_filename(const char *image_data, size_t length(image_data))
- PROTOTYPE: $
-
Purple::XMLNode
purple_util_read_xml_from_file(filename, description)
const char* filename
@@ -516,4 +435,3 @@ gboolean
purple_util_write_data_to_file_absolute(filename_full, char *data, gssize length(data))
const char* filename_full
PROTOTYPE: $$
-
diff --git a/libpurple/plugins/perl/common/Whiteboard.xs b/libpurple/plugins/perl/common/Whiteboard.xs
index 5edb1f15c0..248026cd9f 100644
--- a/libpurple/plugins/perl/common/Whiteboard.xs
+++ b/libpurple/plugins/perl/common/Whiteboard.xs
@@ -8,16 +8,12 @@ purple_whiteboard_clear(wb)
Purple::Whiteboard wb
Purple::Whiteboard
-purple_whiteboard_create(account, who, state)
+purple_whiteboard_new(account, who, state)
Purple::Account account
const char* who
int state
void
-purple_whiteboard_destroy(wb)
- Purple::Whiteboard wb
-
-void
purple_whiteboard_draw_line(wb, x1, y1, x2, y2, color, size)
Purple::Whiteboard wb
int x1
diff --git a/libpurple/plugins/perl/common/XMLNode.xs b/libpurple/plugins/perl/common/XMLNode.xs
index 0c02e58427..bb7adb7666 100644
--- a/libpurple/plugins/perl/common/XMLNode.xs
+++ b/libpurple/plugins/perl/common/XMLNode.xs
@@ -1,22 +1,22 @@
#include "module.h"
-MODULE = Purple::XMLNode PACKAGE = Purple::XMLNode PREFIX = xmlnode_
+MODULE = Purple::XMLNode PACKAGE = Purple::XMLNode PREFIX = purple_xmlnode_
PROTOTYPES: ENABLE
Purple::XMLNode
-xmlnode_copy(src)
+purple_xmlnode_copy(src)
Purple::XMLNode src
void
-xmlnode_free(node)
+purple_xmlnode_free(node)
Purple::XMLNode node
Purple::XMLNode
-xmlnode_from_str(const char *str, gssize length(str))
+purple_xmlnode_from_str(const char *str, gssize length(str))
PROTOTYPE: $
const char *
-xmlnode_get_name(node)
+purple_xmlnode_get_name(node)
Purple::XMLNode node
CODE:
RETVAL = node->name;
@@ -24,99 +24,99 @@ xmlnode_get_name(node)
RETVAL
const char *
-xmlnode_get_attrib(node, attr)
+purple_xmlnode_get_attrib(node, attr)
Purple::XMLNode node
const char *attr
Purple::XMLNode
-xmlnode_get_child(parent, name)
+purple_xmlnode_get_child(parent, name)
Purple::XMLNode parent
const char *name
PREINIT:
- xmlnode *tmp;
+ PurpleXmlNode *tmp;
CODE:
if (!name || *name == '\0') {
tmp = parent->child;
- while (tmp && tmp->type != XMLNODE_TYPE_TAG)
+ while (tmp && tmp->type != PURPLE_XMLNODE_TYPE_TAG)
tmp = tmp->next;
RETVAL = tmp;
} else
- RETVAL = xmlnode_get_child(parent, name);
+ RETVAL = purple_xmlnode_get_child(parent, name);
OUTPUT:
RETVAL
Purple::XMLNode
-xmlnode_get_child_with_namespace(parent, name, xmlns)
+purple_xmlnode_get_child_with_namespace(parent, name, xmlns)
Purple::XMLNode parent
const char *name
const char *xmlns
gchar_own *
-xmlnode_get_data(node)
+purple_xmlnode_get_data(node)
Purple::XMLNode node
Purple::XMLNode
-xmlnode_get_next(node)
+purple_xmlnode_get_next(node)
Purple::XMLNode node
PREINIT:
- xmlnode *tmp;
+ PurpleXmlNode *tmp;
CODE:
tmp = node->next;
- while (tmp && tmp->type != XMLNODE_TYPE_TAG)
+ while (tmp && tmp->type != PURPLE_XMLNODE_TYPE_TAG)
tmp = tmp->next;
RETVAL = tmp;
OUTPUT:
RETVAL
Purple::XMLNode
-xmlnode_get_next_twin(node)
+purple_xmlnode_get_next_twin(node)
Purple::XMLNode node
void
-xmlnode_insert_child(parent, child)
+purple_xmlnode_insert_child(parent, child)
Purple::XMLNode parent
Purple::XMLNode child
void
-xmlnode_insert_data(node, data, size)
+purple_xmlnode_insert_data(node, data, size)
Purple::XMLNode node
const char *data
gssize size
Purple::XMLNode
-xmlnode_new(class, name)
+purple_xmlnode_new(class, name)
const char *name
C_ARGS:
name
Purple::XMLNode
-xmlnode_new_child(parent, name)
+purple_xmlnode_new_child(parent, name)
Purple::XMLNode parent
const char *name
void
-xmlnode_remove_attrib(node, attr)
+purple_xmlnode_remove_attrib(node, attr)
Purple::XMLNode node
const char *attr
void
-xmlnode_set_attrib(node, attr, value)
+purple_xmlnode_set_attrib(node, attr, value)
Purple::XMLNode node
const char *attr
const char *value
gchar_own *
-xmlnode_to_formatted_str(node)
+purple_xmlnode_to_formatted_str(node)
Purple::XMLNode node
CODE:
- RETVAL = xmlnode_to_formatted_str(node, NULL);
+ RETVAL = purple_xmlnode_to_formatted_str(node, NULL);
OUTPUT:
RETVAL
gchar_own *
-xmlnode_to_str(node)
+purple_xmlnode_to_str(node)
Purple::XMLNode node
CODE:
- RETVAL = xmlnode_to_str(node, NULL);
+ RETVAL = purple_xmlnode_to_str(node, NULL);
OUTPUT:
RETVAL
diff --git a/libpurple/plugins/perl/common/FT.xs b/libpurple/plugins/perl/common/Xfer.xs
index 58d1985a72..ea437b34ea 100644
--- a/libpurple/plugins/perl/common/FT.xs
+++ b/libpurple/plugins/perl/common/Xfer.xs
@@ -9,7 +9,7 @@ BOOT:
HV *status_stash = gv_stashpv("Purple::Xfer::Status", 1);
static const constiv *civ, type_const_iv[] = {
-#define const_iv(name) {#name, (IV)PURPLE_XFER_##name}
+#define const_iv(name) {#name, (IV)PURPLE_XFER_TYPE_##name}
const_iv(UNKNOWN),
const_iv(SEND),
const_iv(RECEIVE),
@@ -104,16 +104,16 @@ size_t
purple_xfer_get_size(xfer)
Purple::Xfer xfer
-Purple::XferStatusType
+Purple::XferStatus
purple_xfer_get_status(xfer)
Purple::Xfer xfer
Purple::XferType
-purple_xfer_get_type(xfer)
+purple_xfer_get_xfer_type(xfer)
Purple::Xfer xfer
gboolean
-purple_xfer_is_canceled(xfer)
+purple_xfer_is_cancelled(xfer)
Purple::Xfer xfer
gboolean
@@ -126,10 +126,6 @@ purple_xfer_read(xfer, buffer)
guchar **buffer
void
-purple_xfer_ref(xfer)
- Purple::Xfer xfer
-
-void
purple_xfer_request(xfer)
Purple::Xfer xfer
@@ -168,10 +164,6 @@ purple_xfer_set_size(xfer, size)
size_t size
void
-purple_xfer_unref(xfer)
- Purple::Xfer xfer
-
-void
purple_xfer_update_progress(xfer)
Purple::Xfer xfer
diff --git a/libpurple/plugins/perl/common/module.h b/libpurple/plugins/perl/common/module.h
index 45ea18e5a8..a8273f78f5 100644
--- a/libpurple/plugins/perl/common/module.h
+++ b/libpurple/plugins/perl/common/module.h
@@ -1,6 +1,8 @@
/* Allow the Perl code to see deprecated functions, so we can continue to
* export them to Perl plugins. */
-#undef PURPLE_DISABLE_DEPRECATED
+/* Re-enable this after 3.0.0 release.
+ #undef PURPLE_DISABLE_DEPRECATED
+ */
typedef struct group *Purple__Group;
@@ -23,20 +25,30 @@ typedef struct group *Purple__Group;
#include "../perl-common.h"
-#include "account.h"
+#include "accounts.h"
#include "accountopt.h"
-#include "blist.h"
+#include "buddylist.h"
#include "buddyicon.h"
#include "certificate.h"
#include "cipher.h"
+#include "ciphers/aescipher.h"
+#include "ciphers/des3cipher.h"
+#include "ciphers/descipher.h"
+#include "ciphers/hmaccipher.h"
+#include "ciphers/pbkdf2cipher.h"
+#include "ciphers/rc4cipher.h"
+#include "ciphers/md4hash.h"
+#include "ciphers/md5hash.h"
+#include "ciphers/sha1hash.h"
+#include "ciphers/sha256hash.h"
#include "cmds.h"
#include "connection.h"
-#include "conversation.h"
+#include "conversations.h"
#include "core.h"
#include "debug.h"
#include "desktopitem.h"
#include "eventloop.h"
-#include "ft.h"
+#include "xfer.h"
#ifdef PURPLE_GTKPERL
#include "gtkaccount.h"
#include "gtkblist.h"
@@ -45,14 +57,13 @@ typedef struct group *Purple__Group;
#include "gtkutils.h"
#endif
#include "idle.h"
-#include "imgstore.h"
#include "network.h"
#include "notify.h"
#include "plugin.h"
#include "pluginpref.h"
#include "pounce.h"
#include "prefs.h"
-#include "privacy.h"
+#include "presence.h"
#include "prpl.h"
#include "proxy.h"
#include "request.h"
@@ -67,7 +78,6 @@ typedef struct group *Purple__Group;
#include "stringref.h"
/* Ewww. perl has it's own util.h which is in the include path :( */
#include "libpurple/util.h"
-#include "value.h"
#include "whiteboard.h"
#include "xmlnode.h"
@@ -86,11 +96,11 @@ typedef struct group *Purple__Group;
typedef PurpleAccount * Purple__Account;
typedef PurpleAccountOption * Purple__Account__Option;
typedef PurpleAccountUserSplit * Purple__Account__UserSplit;
+typedef PurpleAccountPrivacyType Purple__Account__PrivacyType;
-/* blist.h */
+/* buddylist.h */
typedef PurpleBlistNode * Purple__BuddyList__Node;
-typedef PurpleBlistNodeFlags Purple__BuddyList__NodeFlags;
-typedef PurpleBlistNodeType Purple__BuddyList__NodeType;
+typedef PurpleCountingNode * Purple__BuddyList__CountingNode;
typedef PurpleBuddyList * Purple__BuddyList;
typedef PurpleBuddy * Purple__BuddyList__Buddy;
typedef PurpleChat * Purple__BuddyList__Chat;
@@ -110,9 +120,7 @@ typedef PurpleCertificateVerificationStatus Purple__Certificate__VerificationSta
/* cipher.h */
typedef PurpleCipher * Purple__Cipher;
-typedef PurpleCipherCaps Purple__CipherCaps;
-typedef PurpleCipherContext * Purple__Cipher__Context;
-typedef PurpleCipherOps * Purple__Cipher__Ops;
+typedef PurpleHash * Purple__Hash;
typedef PurpleCipherBatchMode Purple__Cipher__BatchMode;
/* cmds.h */
@@ -126,16 +134,15 @@ typedef PurpleConnection * Purple__Connection;
typedef PurpleConnectionFlags Purple__ConnectionFlags;
typedef PurpleConnectionState Purple__ConnectionState;
-/* conversation.h */
-typedef PurpleConversationType Purple__ConversationType;
-typedef PurpleConvUpdateType Purple__ConvUpdateType;
-typedef PurpleTypingState Purple__TypingState;
-typedef PurpleMessageFlags Purple__MessageFlags;
-typedef PurpleConvChatBuddyFlags Purple__ConvChatBuddyFlags;
+/* conversations.h */
+typedef PurpleMessageFlags Purple__MessageFlags;
typedef PurpleConversation * Purple__Conversation;
-typedef PurpleConvIm * Purple__Conversation__IM;
-typedef PurpleConvChat * Purple__Conversation__Chat;
-typedef PurpleConvChatBuddy * Purple__Conversation__ChatBuddy;
+typedef PurpleConversationUpdateType Purple__Conversation__UpdateType;
+typedef PurpleIMConversation * Purple__IMConversation;
+typedef PurpleIMTypingState Purple__IMTypingState;
+typedef PurpleChatConversation * Purple__ChatConversation;
+typedef PurpleChatUser * Purple__ChatUser;
+typedef PurpleChatUserFlags Purple__ChatUser__Flags;
/* core.h */
@@ -151,10 +158,10 @@ typedef PurpleDesktopItemType Purple__DesktopItemType;
/* eventloop.h */
typedef PurpleInputCondition * Purple__InputCondition;
-/* ft.h */
+/* xfer.h */
typedef PurpleXfer * Purple__Xfer;
typedef PurpleXferType Purple__XferType;
-typedef PurpleXferStatusType Purple__XferStatusType;
+typedef PurpleXferStatus Purple__XferStatus;
#ifdef PURPLE_GTKPERL
@@ -183,9 +190,6 @@ typedef GtkTextView * Purple__GTK__TextView;
/* gtkconn.h */
#endif
-/* imgstore.h */
-typedef PurpleStoredImage * Purple__StoredImage;
-
/* log.h */
typedef PurpleLog * Purple__Log;
typedef PurpleLogCommonLoggerData * Purple__LogCommonLoggerData;
@@ -230,8 +234,10 @@ typedef PurplePounceEvent Purple__PounceEvent;
/* prefs.h */
typedef PurplePrefType Purple__PrefType;
-/* privacy.h */
-typedef PurplePrivacyType Purple__PrivacyType;
+/* presence.h */
+typedef PurplePresence * Purple__Presence;
+typedef PurpleAccountPresence * Purple__AccountPresence;
+typedef PurpleBuddyPresence * Purple__BuddyPresence;
/* proxy.h */
typedef PurpleProxyInfo * Purple__ProxyInfo;
@@ -274,10 +280,8 @@ typedef PurpleSslErrorType Purple__SslErrorType;
typedef PurpleSslOps * Purple__Ssl__Ops;
/* status.h */
-typedef PurplePresence * Purple__Presence;
-typedef PurplePresenceContext Purple__PresenceContext;
typedef PurpleStatus * Purple__Status;
-typedef PurpleStatusAttr * Purple__StatusAttr;
+typedef PurpleStatusAttribute * Purple__StatusAttr;
typedef PurpleStatusPrimitive Purple__StatusPrimitive;
typedef PurpleStatusType * Purple__StatusType;
@@ -286,18 +290,14 @@ typedef PurpleStringref * Purple__Stringref;
/* util.h */
typedef PurpleInfoFieldFormatCallback Purple__Util__InfoFieldFormatCallback;
-typedef PurpleUtilFetchUrlData Purple__Util__FetchUrlData;
typedef PurpleMenuAction * Purple__Menu__Action;
-/* value.h */
-typedef PurpleValue * Purple__Value;
-
/* whiteboard.h */
typedef PurpleWhiteboard * Purple__Whiteboard;
/* xmlnode.h */
-typedef xmlnode * Purple__XMLNode;
-typedef XMLNodeType XMLNode__Type;
+typedef PurpleXmlNode * Purple__XMLNode;
+typedef PurpleXmlNodeType XMLNode__Type;
/* other */
typedef void * Purple__Handle;
diff --git a/libpurple/plugins/perl/common/typemap b/libpurple/plugins/perl/common/typemap
index 52b2363d73..cef36f79da 100644
--- a/libpurple/plugins/perl/common/typemap
+++ b/libpurple/plugins/perl/common/typemap
@@ -1,6 +1,7 @@
TYPEMAP
guint T_IV
gint T_IV
+gint64 T_IV
const gint * T_PTR
const guint * T_PTR
const guint8 * T_PTR
@@ -33,14 +34,15 @@ GData ** T_PTR
const unsigned char * T_PV
struct tm * T_PTR
const struct tm * T_PTR
-xmlnode * T_PTR
-const xmlnode * T_PTR
gssize T_IV
const void * T_PTR
+GValue * T_PTR
+GType T_IV
Purple::Account T_PurpleObj
Purple::Account::Option T_PurpleObj
Purple::Account::UserSplit T_PurpleObj
+Purple::Account::PrivacyType T_IV
Purple::Buddy::Icon T_PurpleObj
Purple::Buddy::Icon::Spec T_PurpleObj
@@ -50,22 +52,15 @@ Purple::BuddyList::Chat T_PurpleObj
Purple::BuddyList::Contact T_PurpleObj
Purple::BuddyList::Group T_PurpleObj
Purple::BuddyList::Node T_PurpleObj
-Purple::BuddyList::NodeFlags T_IV
-Purple::BuddyList::NodeType T_IV
+Purple::BuddyList::CountingNode T_PurpleObj
Purple::Cipher T_PurpleObj
-Purple::CipherCaps T_IV
-Purple::Cipher::Ops T_PurpleObj
-Purple::Cipher::Context T_PurpleObj
+Purple::Hash T_PurpleObj
Purple::Cmd::Flag T_IV
Purple::Cmd::Id T_IV
Purple::Cmd::Priority T_IV
Purple::Cmd::Ret T_IV
Purple::Connection T_PurpleObj
-Purple::Conversation T_PurpleObj
-Purple::Conversation::Chat T_PurpleObj
-Purple::Conversation::ChatBuddy T_PurpleObj
-Purple::Conversation::IM T_PurpleObj
Purple::Core T_PurpleObj
Purple::Desktop::Item T_PurpleObj
@@ -110,8 +105,6 @@ Purple::PluginPrefType T_IV
Purple::PluginPref::Frame T_PurpleObj
Purple::Pounce T_PurpleObj
Purple::PounceEvent T_IV
-Purple::Presence T_PurpleObj
-Purple::PrivacyType T_IV
Purple::ProtocolOptions T_IV
Purple::ProxyInfo T_PurpleObj
Purple::ProxyType T_IV
@@ -140,8 +133,8 @@ Purple::Ssl::Connection T_PurpleObj
Purple::Ssl::Ops T_PurpleObj
Purple::Presence T_PurpleObj
-Purple::PresenceContext T_IV
-Purple::Smiley T_PurpleObj
+Purple::AccountPresence T_PurpleObj
+Purple::BuddyPresence T_PurpleObj
Purple::Status T_PurpleObj
Purple::StatusAttr T_PurpleObj
Purple::StatusPrimitive T_IV
@@ -153,11 +146,10 @@ Purple::String::Format::Type T_IV
Purple::Stringref T_PurpleObj
Purple::Util::FetchUrlData T_PTR
Purple::Util::InfoFieldFormatCallback T_PTR
-Purple::Value T_PurpleObj
Purple::Xfer T_PurpleObj
Purple::XferType T_IV
-Purple::XferStatusType T_IV
+Purple::XferStatus T_IV
Purple::XMLNode T_PurpleObj
XMLNode::Type T_IV
@@ -180,12 +172,15 @@ Purple::Cipher::BatchMode T_IV
/* debug.h */
Purple::DebugLevel T_IV
-/* conversation.h */
-Purple::ConvChatBuddyFlags T_IV
-Purple::ConvUpdateType T_IV
-Purple::ConversationType T_IV
+/* conversations.h */
+Purple::Conversation T_PurpleObj
+Purple::ChatConversation T_PurpleObj
+Purple::ChatUser T_PurpleObj
+Purple::IMConversation T_PurpleObj
+Purple::ChatUser::Flags T_IV
+Purple::Conversation::UpdateType T_IV
Purple::MessageFlags T_IV
-Purple::TypingState T_IV
+Purple::IMTypingState T_IV
Purple::UnseenState T_IV
/* connection.h */
diff --git a/libpurple/plugins/perl/perl-common.c b/libpurple/plugins/perl/perl-common.c
index 49288bff58..dee51b1217 100644
--- a/libpurple/plugins/perl/perl-common.c
+++ b/libpurple/plugins/perl/perl-common.c
@@ -1,8 +1,9 @@
-#include "debug.h"
-#include "value.h"
-
#include "perl-common.h"
+#include "cipher.h"
+#include "debug.h"
+#include "savedstatuses.h"
+
extern PerlInterpreter *my_perl;
static GHashTable *object_stashes = NULL;
@@ -43,7 +44,10 @@ create_sv_ptr(void *object)
{
SV *sv;
- sv = newSViv((IV)object);
+ PURPLE_STATIC_ASSERT(sizeof(IV) >= sizeof(void *),
+ sv_can_not_hold_a_pointer);
+
+ sv = newSViv((IV)(gintptr)object);
sv_magic(sv, NULL, '~', NULL, 0);
@@ -380,102 +384,74 @@ purple_perl_sv_from_value(const PurpleValue *value, va_list list)
#endif
void *
-purple_perl_data_from_sv(PurpleValue *value, SV *sv)
+purple_perl_data_from_sv(GType type, SV *sv)
{
-
- switch (purple_value_get_type(value)) {
- case PURPLE_TYPE_BOOLEAN: return (void *)SvIV(sv);
- case PURPLE_TYPE_INT: return (void *)SvIV(sv);
- case PURPLE_TYPE_UINT: return (void *)SvUV(sv);
- case PURPLE_TYPE_LONG: return (void *)SvIV(sv);
- case PURPLE_TYPE_ULONG: return (void *)SvUV(sv);
- case PURPLE_TYPE_INT64: return (void *)SvIV(sv);
- case PURPLE_TYPE_UINT64: return (void *)SvUV(sv);
- case PURPLE_TYPE_STRING: return g_strdup(SvPVutf8_nolen(sv));
- case PURPLE_TYPE_POINTER: return (void *)SvIV(sv);
- case PURPLE_TYPE_BOXED: return (void *)SvIV(sv);
-
- default:
- return NULL;
+ switch (type) {
+ case G_TYPE_BOOLEAN: return (void *)(gintptr)SvIV(sv);
+ case G_TYPE_INT: return (void *)(gintptr)SvIV(sv);
+ case G_TYPE_UINT: return (void *)(gintptr)SvUV(sv);
+ case G_TYPE_LONG: return (void *)(gintptr)SvIV(sv);
+ case G_TYPE_ULONG: return (void *)(gintptr)SvUV(sv);
+ case G_TYPE_INT64: return (void *)(gintptr)SvIV(sv);
+ case G_TYPE_UINT64: return (void *)(gintptr)SvUV(sv);
+ case G_TYPE_STRING: return g_strdup(SvPVutf8_nolen(sv));
+ case G_TYPE_POINTER: return (void *)(gintptr)SvIV(sv);
}
return NULL;
}
static SV *
-purple_perl_sv_from_subtype(const PurpleValue *value, void *arg)
+purple_perl_sv_from_purple_type(GType type, void *arg)
{
const char *stash = "Purple"; /* ? */
- switch (purple_value_get_subtype(value)) {
- case PURPLE_SUBTYPE_ACCOUNT:
- stash = "Purple::Account";
- break;
- case PURPLE_SUBTYPE_BLIST:
- stash = "Purple::BuddyList";
- break;
- case PURPLE_SUBTYPE_BLIST_BUDDY:
- stash = "Purple::BuddyList::Buddy";
- break;
- case PURPLE_SUBTYPE_BLIST_GROUP:
- stash = "Purple::BuddyList::Group";
- break;
- case PURPLE_SUBTYPE_BLIST_CHAT:
- stash = "Purple::BuddyList::Chat";
- break;
- case PURPLE_SUBTYPE_BUDDY_ICON:
- stash = "Purple::Buddy::Icon";
- break;
- case PURPLE_SUBTYPE_CONNECTION:
- stash = "Purple::Connection";
- break;
- case PURPLE_SUBTYPE_CONVERSATION:
- stash = "Purple::Conversation";
- break;
- case PURPLE_SUBTYPE_PLUGIN:
- stash = "Purple::Plugin";
- break;
- case PURPLE_SUBTYPE_BLIST_NODE:
- stash = "Purple::BuddyList::Node";
- break;
- case PURPLE_SUBTYPE_CIPHER:
- stash = "Purple::Cipher";
- break;
- case PURPLE_SUBTYPE_STATUS:
- stash = "Purple::Status";
- break;
- case PURPLE_SUBTYPE_SAVEDSTATUS:
- stash = "Purple::SavedStatus";
- break;
- case PURPLE_SUBTYPE_LOG:
- stash = "Purple::Log";
- break;
- case PURPLE_SUBTYPE_XFER:
- stash = "Purple::Xfer";
- break;
- case PURPLE_SUBTYPE_XMLNODE:
- stash = "Purple::XMLNode";
- break;
- case PURPLE_SUBTYPE_USERINFO:
- stash = "Purple::NotifyUserInfo";
- break;
- case PURPLE_SUBTYPE_STORED_IMAGE:
- stash = "Purple::StoredImage";
- break;
- case PURPLE_SUBTYPE_CERTIFICATEPOOL:
- stash = "Purple::Certificate::Pool";
- break;
- case PURPLE_SUBTYPE_UNKNOWN:
- stash = "Purple::Unknown";
- break;
- }
+ if (type == PURPLE_TYPE_ACCOUNT)
+ stash = "Purple::Account";
+ else if (type == PURPLE_TYPE_CONTACT)
+ stash = "Purple::BuddyList::Contact";
+ else if (type == PURPLE_TYPE_BUDDY)
+ stash = "Purple::BuddyList::Buddy";
+ else if (type == PURPLE_TYPE_GROUP)
+ stash = "Purple::BuddyList::Group";
+ else if (type == PURPLE_TYPE_CHAT)
+ stash = "Purple::BuddyList::Chat";
+ else if (type == PURPLE_TYPE_BUDDY_ICON)
+ stash = "Purple::Buddy::Icon";
+ else if (type == PURPLE_TYPE_CONNECTION)
+ stash = "Purple::Connection";
+ else if (type == PURPLE_TYPE_CONVERSATION)
+ stash = "Purple::Conversation";
+ else if (type == PURPLE_TYPE_PLUGIN)
+ stash = "Purple::Plugin";
+ else if (type == PURPLE_TYPE_BLIST_NODE)
+ stash = "Purple::BuddyList::Node";
+ else if (type == PURPLE_TYPE_CIPHER)
+ stash = "Purple::Cipher";
+ else if (type == PURPLE_TYPE_STATUS)
+ stash = "Purple::Status";
+ else if (type == PURPLE_TYPE_SAVEDSTATUS)
+ stash = "Purple::SavedStatus";
+ else if (type == PURPLE_TYPE_LOG)
+ stash = "Purple::Log";
+ else if (type == PURPLE_TYPE_XFER)
+ stash = "Purple::Xfer";
+ else if (type == PURPLE_TYPE_XMLNODE)
+ stash = "Purple::XMLNode";
+ else if (type == PURPLE_TYPE_NOTIFY_USER_INFO)
+ stash = "Purple::NotifyUserInfo";
+ else if (type == PURPLE_TYPE_CERTIFICATE_POOL)
+ stash = "Purple::Certificate::Pool";
+ else
+ stash = "Purple::Unknown";
return sv_2mortal(purple_perl_bless_object(arg, stash));
}
SV *
-purple_perl_sv_from_vargs(const PurpleValue *value, va_list *args, void ***copy_arg)
+purple_perl_sv_from_vargs(GType type, va_list *args, void ***copy_arg)
{
+#if 0
if (purple_value_is_outgoing(value)) {
switch (purple_value_get_type(value)) {
case PURPLE_TYPE_SUBTYPE:
@@ -553,80 +529,68 @@ purple_perl_sv_from_vargs(const PurpleValue *value, va_list *args, void ***copy_
return NULL;
}
} else {
- switch (purple_value_get_type(value)) {
- case PURPLE_TYPE_SUBTYPE:
- if ((*copy_arg = va_arg(*args, void *)) == NULL)
- return &PL_sv_undef;
-
- return purple_perl_sv_from_subtype(value, *copy_arg);
-
- case PURPLE_TYPE_BOOLEAN:
- *copy_arg = GINT_TO_POINTER( va_arg(*args, gboolean) );
+#endif
+ switch (type) {
+ case G_TYPE_BOOLEAN:
+ *copy_arg = GINT_TO_POINTER( va_arg(*args, gboolean) );
- return newSViv((gboolean)GPOINTER_TO_INT(*copy_arg));
+ return newSViv((gboolean)GPOINTER_TO_INT(*copy_arg));
- case PURPLE_TYPE_INT:
- *copy_arg = GINT_TO_POINTER( va_arg(*args, int) );
+ case G_TYPE_INT:
+ *copy_arg = GINT_TO_POINTER( va_arg(*args, int) );
- return newSViv(GPOINTER_TO_INT(*copy_arg));
+ return newSViv(GPOINTER_TO_INT(*copy_arg));
- case PURPLE_TYPE_UINT:
- *copy_arg = GUINT_TO_POINTER(va_arg(*args, unsigned int));
+ case G_TYPE_UINT:
+ *copy_arg = GUINT_TO_POINTER(va_arg(*args, unsigned int));
- return newSVuv(GPOINTER_TO_UINT(*copy_arg));
+ return newSVuv(GPOINTER_TO_UINT(*copy_arg));
- case PURPLE_TYPE_LONG:
- *copy_arg = (void *)va_arg(*args, long);
+ case G_TYPE_LONG:
+ *copy_arg = (void *)va_arg(*args, long);
- return newSViv((long)*copy_arg);
+ return newSViv((long)*copy_arg);
- case PURPLE_TYPE_ULONG:
- *copy_arg = (void *)va_arg(*args, unsigned long);
+ case G_TYPE_ULONG:
+ *copy_arg = (void *)va_arg(*args, unsigned long);
- return newSVuv((unsigned long)*copy_arg);
+ return newSVuv((unsigned long)*copy_arg);
- case PURPLE_TYPE_INT64:
+ case G_TYPE_INT64:
+ /* XXX This yells and complains. */
#if 0
- /* XXX This yells and complains. */
- *copy_arg = va_arg(*args, gint64);
+ *copy_arg = (void *)va_arg(*args, gint64);
- return newSViv(*copy_arg);
+ return newSViv((gint64)*copy_arg);
#endif
- break;
+ break;
- case PURPLE_TYPE_UINT64:
- /* XXX This also yells and complains. */
+ case G_TYPE_UINT64:
+ /* XXX This also yells and complains. */
#if 0
- *copy_arg = (void *)va_arg(*args, guint64);
+ *copy_arg = (void *)va_arg(*args, guint64);
- return newSVuv(*copy_arg);
+ return newSVuv((guint64)*copy_arg);
#endif
- break;
-
- case PURPLE_TYPE_STRING:
- if ((*copy_arg = (void *)va_arg(*args, char *)) == NULL)
- return &PL_sv_undef;
+ break;
- return newSVGChar((char *)*copy_arg);
+ case G_TYPE_STRING:
+ if ((*copy_arg = (void *)va_arg(*args, char *)) == NULL)
+ return &PL_sv_undef;
- case PURPLE_TYPE_POINTER:
- if ((*copy_arg = (void *)va_arg(*args, void *)) == NULL)
- return &PL_sv_undef;
+ return newSVGChar((char *)*copy_arg);
- return newSViv((IV)*copy_arg);
+ case G_TYPE_POINTER:
+ if ((*copy_arg = (void *)va_arg(*args, void *)) == NULL)
+ return &PL_sv_undef;
- case PURPLE_TYPE_BOXED:
- /* Uh.. I dunno. Try this? */
- if ((*copy_arg = (void *)va_arg(*args, void *)) == NULL)
- return &PL_sv_undef;
+ return newSViv((IV)(gintptr)*copy_arg);
- return sv_2mortal(purple_perl_bless_object(*copy_arg,
- purple_value_get_specific_type(value)));
+ default:
+ if ((*copy_arg = va_arg(*args, void *)) == NULL)
+ return &PL_sv_undef;
- default:
- /* If this happens, things are going to get screwed up... */
- return NULL;
- }
+ return purple_perl_sv_from_purple_type(type, *copy_arg);
}
return NULL;
diff --git a/libpurple/plugins/perl/perl-common.h b/libpurple/plugins/perl/perl-common.h
index cb065cf71d..a760382d34 100644
--- a/libpurple/plugins/perl/perl-common.h
+++ b/libpurple/plugins/perl/perl-common.h
@@ -25,7 +25,6 @@
#undef _WIN32DEP_H_
#endif
#include "plugin.h"
-#include "value.h"
#define is_hvref(o) \
((o) && SvROK(o) && SvRV(o) && (SvTYPE(SvRV(o)) == SVt_PVHV))
@@ -39,9 +38,18 @@
#define PURPLE_PERL_BOOT(x) \
purple_perl_callXS(boot_Purple__##x, cv, mark)
+#ifdef HAVE_NEW_SVUPGRADE
+# define SvUPGRADE_common(a, b) SvUPGRADE(a, b)
+#else
+# define SvUPGRADE_common(a, b) if (!SvUPGRADE(a, b)) { croak("Cannot upgrade variable"); }
+#endif
+
+typedef struct _PurplePerlInfoStrings PurplePerlInfoStrings;
+
typedef struct
{
PurplePlugin *plugin;
+ PurplePerlInfoStrings *info_strings;
char *package;
char *load_sub;
char *unload_sub;
@@ -69,8 +77,7 @@ gboolean purple_perl_value_from_sv(PurpleValue *value, SV *sv);
SV *purple_perl_sv_from_value(const PurpleValue *value);
#endif
-void *purple_perl_data_from_sv(PurpleValue *value, SV *sv);
-SV *purple_perl_sv_from_vargs(const PurpleValue *value, va_list *args,
- void ***copy_arg);
+void *purple_perl_data_from_sv(GType type, SV *sv);
+SV *purple_perl_sv_from_vargs(GType type, va_list *args, void ***copy_arg);
SV *purple_perl_sv_from_fun(PurplePlugin *plugin, SV *callback);
#endif /* _PURPLE_PERL_COMMON_H_ */
diff --git a/libpurple/plugins/perl/perl-handlers.c b/libpurple/plugins/perl/perl-handlers.c
index 384869458c..d87f63cfc7 100644
--- a/libpurple/plugins/perl/perl-handlers.c
+++ b/libpurple/plugins/perl/perl-handlers.c
@@ -4,6 +4,12 @@
#include "debug.h"
#include "signals.h"
+typedef struct
+{
+ SV *callback;
+ SV *data;
+} PurplePerlAccountPasswordHandler;
+
extern PerlInterpreter *my_perl;
static GSList *cmd_handlers = NULL;
static GSList *signal_handlers = NULL;
@@ -279,7 +285,7 @@ perl_signal_cb(va_list args, void *data)
int i;
int count;
int value_count;
- PurpleValue *ret_value, **values;
+ GType ret_type, *value_types;
SV **sv_args;
DATATYPE **copy_args;
@@ -290,14 +296,14 @@ perl_signal_cb(va_list args, void *data)
SAVETMPS;
PUSHMARK(sp);
- purple_signal_get_values(handler->instance, handler->signal,
- &ret_value, &value_count, &values);
+ purple_signal_get_types(handler->instance, handler->signal,
+ &ret_type, &value_count, &value_types);
sv_args = g_new(SV *, value_count);
copy_args = g_new(void **, value_count);
for (i = 0; i < value_count; i++) {
- sv_args[i] = purple_perl_sv_from_vargs(values[i],
+ sv_args[i] = purple_perl_sv_from_vargs(value_types[i],
#ifdef VA_COPY_AS_ARRAY
(va_list*)args,
#else
@@ -312,7 +318,7 @@ perl_signal_cb(va_list args, void *data)
PUTBACK;
- if (ret_value != NULL) {
+ if (ret_type != G_TYPE_NONE) {
count = call_sv(handler->callback, G_EVAL | G_SCALAR);
SPAGAIN;
@@ -320,7 +326,7 @@ perl_signal_cb(va_list args, void *data)
if (count != 1)
croak("Uh oh! call_sv returned %i != 1", i);
else
- ret_val = purple_perl_data_from_sv(ret_value, POPs);
+ ret_val = purple_perl_data_from_sv(ret_type, POPs);
} else {
call_sv(handler->callback, G_EVAL | G_SCALAR);
@@ -333,6 +339,7 @@ perl_signal_cb(va_list args, void *data)
SvPVutf8_nolen(ERRSV));
}
+#if 0
/* See if any parameters changed. */
for (i = 0; i < value_count; i++) {
if (purple_value_is_outgoing(values[i])) {
@@ -395,6 +402,7 @@ perl_signal_cb(va_list args, void *data)
#endif
}
}
+#endif
PUTBACK;
FREETMPS;
@@ -845,3 +853,115 @@ void purple_perl_pref_cb_clear_for_plugin(PurplePlugin *plugin)
destroy_prefs_handler(handler);
}
}
+
+static void
+perl_account_save_cb(PurpleAccount *account, GError *error, gpointer _handler)
+{
+ PurplePerlAccountPasswordHandler *handler = _handler;
+ SV *accountSV, *errorSV;
+
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+
+ accountSV = sv_2mortal(purple_perl_bless_object(account,
+ "Purple::Account"));
+ XPUSHs(accountSV);
+
+ errorSV = sv_2mortal(purple_perl_bless_object(error, "GLib::Error"));
+ XPUSHs(errorSV);
+
+ XPUSHs((SV *)handler->data);
+
+ PUTBACK;
+ call_sv(handler->callback, G_EVAL | G_SCALAR);
+ SPAGAIN;
+
+ if (SvTRUE(ERRSV)) {
+ purple_debug_error("perl", "Perl plugin command function "
+ "exited abnormally: %s\n", SvPVutf8_nolen(ERRSV));
+ }
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ g_free(handler);
+}
+
+static void
+perl_account_read_cb(PurpleAccount *account, const gchar *password,
+ GError *error, gpointer _handler)
+{
+ PurplePerlAccountPasswordHandler *handler = _handler;
+ SV *accountSV, *passwordSV, *errorSV;
+
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+
+ accountSV = sv_2mortal(purple_perl_bless_object(account,
+ "Purple::Account"));
+ XPUSHs(accountSV);
+
+ passwordSV = sv_2mortal(newSVpv(password, 0));
+ XPUSHs(passwordSV);
+
+ errorSV = sv_2mortal(purple_perl_bless_object(error, "GLib::Error"));
+ XPUSHs(errorSV);
+
+ XPUSHs((SV *)handler->data);
+
+ PUTBACK;
+ call_sv(handler->callback, G_EVAL | G_SCALAR);
+ SPAGAIN;
+
+ if (SvTRUE(ERRSV)) {
+ purple_debug_error("perl", "Perl plugin command function "
+ "exited abnormally: %s\n", SvPVutf8_nolen(ERRSV));
+ }
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ g_free(handler);
+}
+
+void
+purple_perl_account_get_password(PurpleAccount *account, SV *func, SV *data)
+{
+ PurplePerlAccountPasswordHandler *handler;
+
+ if (func == &PL_sv_undef)
+ func = NULL;
+ if (data == &PL_sv_undef)
+ data = NULL;
+
+ handler = g_new0(PurplePerlAccountPasswordHandler, 1);
+ handler->callback = (func != NULL ? newSVsv(func) : NULL);
+ handler->data = (data != NULL ? newSVsv(data) : NULL);
+
+ purple_account_get_password(account, perl_account_read_cb, handler);
+}
+
+void
+purple_perl_account_set_password(PurpleAccount *account, const gchar *password,
+ SV *func, SV *data)
+{
+ PurplePerlAccountPasswordHandler *handler;
+
+ if (func == &PL_sv_undef)
+ func = NULL;
+ if (data == &PL_sv_undef)
+ data = NULL;
+
+ handler = g_new0(PurplePerlAccountPasswordHandler, 1);
+ handler->callback = (func != NULL ? newSVsv(func) : NULL);
+ handler->data = (data != NULL ? newSVsv(data) : NULL);
+
+ purple_account_set_password(account, password, perl_account_save_cb,
+ handler);
+}
diff --git a/libpurple/plugins/perl/perl-handlers.h b/libpurple/plugins/perl/perl-handlers.h
index c0079d2fb9..b349c9da3d 100644
--- a/libpurple/plugins/perl/perl-handlers.h
+++ b/libpurple/plugins/perl/perl-handlers.h
@@ -25,7 +25,7 @@ typedef struct
SV *callback;
SV *data;
PurplePlugin *plugin;
- int iotag;
+ guint iotag;
} PurplePerlTimeoutHandler;
@@ -44,7 +44,7 @@ typedef struct
SV *callback;
SV *data;
PurplePlugin *plugin;
- int iotag;
+ guint iotag;
} PurplePerlPrefsHandler;
@@ -82,4 +82,10 @@ guint purple_perl_prefs_connect_callback(PurplePlugin *plugin, const char *name,
void purple_perl_prefs_disconnect_callback(guint callback_id);
void purple_perl_pref_cb_clear_for_plugin(PurplePlugin *plugin);
+void
+purple_perl_account_get_password(PurpleAccount *account, SV *func, SV *data);
+void
+purple_perl_account_set_password(PurpleAccount *account, const char *password,
+ SV *func, SV *data);
+
#endif /* _PURPLE_PERL_HANDLERS_H_ */
diff --git a/libpurple/plugins/perl/perl.c b/libpurple/plugins/perl/perl.c
index cb08ece70f..9741c34972 100644
--- a/libpurple/plugins/perl/perl.c
+++ b/libpurple/plugins/perl/perl.c
@@ -106,11 +106,22 @@ extern void boot_DynaLoader _((pTHX_ CV * cv)); /* perl is so wacky */
PerlInterpreter *my_perl = NULL;
+struct _PurplePerlInfoStrings
+{
+ char *name;
+ char *id;
+ char *homepage;
+ char *author;
+ char *summary;
+ char *description;
+ char *version;
+};
+
static PurplePluginUiInfo ui_info =
{
purple_perl_get_plugin_frame,
- 0, /* page_num (Reserved) */
- NULL, /* frame (Reserved) */
+ NULL,
+
/* Padding */
NULL,
NULL,
@@ -126,6 +137,21 @@ static PurpleGtkPluginUiInfo gtk_ui_info =
};
#endif
+static void perl_infostrings_free(PurplePerlInfoStrings *info_strings)
+{
+ if (info_strings == NULL)
+ return;
+
+ g_free(info_strings->name);
+ g_free(info_strings->id);
+ g_free(info_strings->homepage);
+ g_free(info_strings->author);
+ g_free(info_strings->summary);
+ g_free(info_strings->description);
+ g_free(info_strings->version);
+ g_free(info_strings);
+}
+
static void
#ifdef OLD_PERL
xs_init()
@@ -353,6 +379,7 @@ probe_perl_plugin(PurplePlugin *plugin)
info = g_new0(PurplePluginInfo, 1);
gps = g_new0(PurplePerlScript, 1);
+ gps->info_strings = g_new0(PurplePerlInfoStrings, 1);
info->magic = PURPLE_PLUGIN_MAGIC;
info->major_version = PURPLE_MAJOR_VERSION;
@@ -372,9 +399,9 @@ probe_perl_plugin(PurplePlugin *plugin)
/* We know this one exists. */
key = hv_fetch(plugin_info, "name", strlen("name"), 0);
- info->name = g_strdup(SvPVutf8_nolen(*key));
+ info->name = gps->info_strings->name = g_strdup(SvPVutf8_nolen(*key));
/* Set id here in case we don't find one later. */
- info->id = g_strdup(info->name);
+ info->id = gps->info_strings->id = g_strdup(info->name);
#ifdef PURPLE_GTKPERL
if ((key = hv_fetch(plugin_info, "GTK_UI",
@@ -384,23 +411,23 @@ probe_perl_plugin(PurplePlugin *plugin)
if ((key = hv_fetch(plugin_info, "url",
strlen("url"), 0)))
- info->homepage = g_strdup(SvPVutf8_nolen(*key));
+ info->homepage = gps->info_strings->homepage = g_strdup(SvPVutf8_nolen(*key));
if ((key = hv_fetch(plugin_info, "author",
strlen("author"), 0)))
- info->author = g_strdup(SvPVutf8_nolen(*key));
+ info->author = gps->info_strings->author = g_strdup(SvPVutf8_nolen(*key));
if ((key = hv_fetch(plugin_info, "summary",
strlen("summary"), 0)))
- info->summary = g_strdup(SvPVutf8_nolen(*key));
+ info->summary = gps->info_strings->summary = g_strdup(SvPVutf8_nolen(*key));
if ((key = hv_fetch(plugin_info, "description",
strlen("description"), 0)))
- info->description = g_strdup(SvPVutf8_nolen(*key));
+ info->description = gps->info_strings->description = g_strdup(SvPVutf8_nolen(*key));
if ((key = hv_fetch(plugin_info, "version",
strlen("version"), 0)))
- info->version = g_strdup(SvPVutf8_nolen(*key));
+ info->version = gps->info_strings->version = g_strdup(SvPVutf8_nolen(*key));
/* We know this one exists. */
key = hv_fetch(plugin_info, "load", strlen("load"), 0);
@@ -415,8 +442,8 @@ probe_perl_plugin(PurplePlugin *plugin)
if ((key = hv_fetch(plugin_info, "id",
strlen("id"), 0))) {
- g_free(info->id);
- info->id = g_strdup_printf("perl-%s",
+ g_free(gps->info_strings->id);
+ info->id = gps->info_strings->id = g_strdup_printf("perl-%s",
SvPVutf8_nolen(*key));
}
@@ -603,16 +630,11 @@ destroy_perl_plugin(PurplePlugin *plugin)
if (plugin->info != NULL) {
PurplePerlScript *gps;
- g_free(plugin->info->name);
- g_free(plugin->info->id);
- g_free(plugin->info->homepage);
- g_free(plugin->info->author);
- g_free(plugin->info->summary);
- g_free(plugin->info->description);
- g_free(plugin->info->version);
-
gps = (PurplePerlScript *)plugin->info->extra_info;
if (gps != NULL) {
+ perl_infostrings_free(gps->info_strings);
+ gps->info_strings = NULL;
+
g_free(gps->package);
g_free(gps->load_sub);
g_free(gps->unload_sub);
diff --git a/libpurple/plugins/perl/scripts/account.pl b/libpurple/plugins/perl/scripts/account.pl
index e56ee28d21..dcf1249f20 100644
--- a/libpurple/plugins/perl/scripts/account.pl
+++ b/libpurple/plugins/perl/scripts/account.pl
@@ -34,6 +34,37 @@ sub plugin_init {
return %PLUGIN_INFO;
}
+sub set_password_cb
+{
+ my $account = shift;
+ my $error = shift;
+ my $data = shift;
+
+ if ($error) {
+ Purple::Debug::warning($MODULE_NAME, "Failed to set password " .
+ "for $account\n");
+ return;
+ }
+
+ Purple::Debug::misc($MODULE_NAME, "Password for $account was set\n");
+}
+
+sub get_password_cb
+{
+ my $account = shift;
+ my $password = shift;
+ my $error = shift;
+ my $data = shift;
+
+ if ($error) {
+ Purple::Debug::warning($MODULE_NAME, "Failed to get password for $account\n");
+ return;
+ }
+
+ Purple::Debug::misc($MODULE_NAME, "Got password for $account\n");
+
+ $account->set_password($password, \&set_password_cb);
+}
# This is the sub defined in %PLUGIN_INFO to be called when the plugin is loaded
# Note: The plugin has a reference to itself on top of the argument stack.
@@ -101,6 +132,8 @@ sub plugin_load {
$account->set_status("available", TRUE);
$account->connect();
+ $account->get_password(\&get_password_cb);
+
print "\n\n";
Purple::Debug::info($MODULE_NAME, "plugin_load() - Testing $MODULE_NAME Completed.\n");
print "\n\n" . "#" x 80 . "\n\n";
diff --git a/libpurple/plugins/perl/scripts/count_down.pl b/libpurple/plugins/perl/scripts/count_down.pl
index 3d81669751..336fa84ecb 100644
--- a/libpurple/plugins/perl/scripts/count_down.pl
+++ b/libpurple/plugins/perl/scripts/count_down.pl
@@ -7,7 +7,7 @@ use Purple;
summary => "Makes a countdown in days from today.",
description => "Long description coming....",
author => "John H. Kelm <johnhkelm\@gmail.com>",
- url => "http://pidgin.im",
+ url => "https://pidgin.im",
load => "plugin_load",
unload => "plugin_unload"
diff --git a/libpurple/plugins/perl/scripts/gtk_frame_test.pl b/libpurple/plugins/perl/scripts/gtk_frame_test.pl
index 0b6d68cd5d..4936b794b8 100644
--- a/libpurple/plugins/perl/scripts/gtk_frame_test.pl
+++ b/libpurple/plugins/perl/scripts/gtk_frame_test.pl
@@ -9,7 +9,7 @@ use Purple;
summary => "Test plugin for the Perl interpreter.",
description => "Implements a set of test proccedures to ensure all functions that work in the C API still work in the Perl plugin interface. As XSUBs are added, this *should* be updated to test the changes. Furthermore, this will function as the tutorial perl plugin.",
author => "John H. Kelm <johnhkelm\@gmail.com>",
- url => "http://pidgin.im",
+ url => "https://pidgin.im",
GTK_UI => TRUE,
gtk_prefs_info => "foo",
diff --git a/libpurple/plugins/perl/scripts/signals-test.pl b/libpurple/plugins/perl/scripts/signals-test.pl
index c2cecb886a..8f8d289490 100644
--- a/libpurple/plugins/perl/scripts/signals-test.pl
+++ b/libpurple/plugins/perl/scripts/signals-test.pl
@@ -10,7 +10,7 @@ use Purple;
description => "Demonstrate the use of purple signals from " .
"a perl plugin.",
author => "Sadrul Habib Chowdhury <sadrul\@pidgin.im>",
- url => "http://developer.pidgin.im/wiki/sadrul/",
+ url => "https://developer.pidgin.im/wiki/sadrul/",
load => "plugin_load",
unload => "plugin_unload"
diff --git a/libpurple/plugins/pluginpref_example.c b/libpurple/plugins/pluginpref_example.c
index 8b8724833d..0d112cff1f 100644
--- a/libpurple/plugins/pluginpref_example.c
+++ b/libpurple/plugins/pluginpref_example.c
@@ -19,20 +19,13 @@
* 02111-1301, USA.
*/
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#ifndef PURPLE_PLUGINS
-# define PURPLE_PLUGINS
-#endif
-
+/* When writing a third-party plugin, do not include libpurple's internal.h
+ * included below. This file is for internal libpurple use only. We're including
+ * it here for our own convenience. */
#include "internal.h"
-#include "plugin.h"
-#include "pluginpref.h"
-#include "prefs.h"
-#include "version.h"
+/* This file defines PURPLE_PLUGINS and includes all the libpurple headers */
+#include <purple.h>
static PurplePluginPrefFrame *
get_plugin_pref_frame(PurplePlugin *plugin) {
@@ -61,7 +54,7 @@ get_plugin_pref_frame(PurplePlugin *plugin) {
ppref = purple_plugin_pref_new_with_name_and_label(
"/plugins/core/pluginpref_example/int_choice",
"integer choice");
- purple_plugin_pref_set_type(ppref, PURPLE_PLUGIN_PREF_CHOICE);
+ purple_plugin_pref_set_pref_type(ppref, PURPLE_PLUGIN_PREF_CHOICE);
purple_plugin_pref_add_choice(ppref, "One", GINT_TO_POINTER(1));
purple_plugin_pref_add_choice(ppref, "Two", GINT_TO_POINTER(2));
purple_plugin_pref_add_choice(ppref, "Four", GINT_TO_POINTER(4));
@@ -95,7 +88,7 @@ get_plugin_pref_frame(PurplePlugin *plugin) {
ppref = purple_plugin_pref_new_with_name_and_label(
"/plugins/core/pluginpref_example/string_choice",
"string choice");
- purple_plugin_pref_set_type(ppref, PURPLE_PLUGIN_PREF_CHOICE);
+ purple_plugin_pref_set_pref_type(ppref, PURPLE_PLUGIN_PREF_CHOICE);
purple_plugin_pref_add_choice(ppref, "red", "red");
purple_plugin_pref_add_choice(ppref, "orange", "orange");
purple_plugin_pref_add_choice(ppref, "yellow", "yellow");
@@ -109,8 +102,8 @@ get_plugin_pref_frame(PurplePlugin *plugin) {
static PurplePluginUiInfo prefs_info = {
get_plugin_pref_frame,
- 0, /* page_num (Reserved) */
- NULL, /* frame (Reserved) */
+ NULL,
+
/* Padding */
NULL,
NULL,
diff --git a/libpurple/plugins/psychic.c b/libpurple/plugins/psychic.c
index 787bfd1654..468db3535d 100644
--- a/libpurple/plugins/psychic.c
+++ b/libpurple/plugins/psychic.c
@@ -3,13 +3,12 @@
#include "internal.h"
#include "account.h"
-#include "blist.h"
+#include "buddylist.h"
#include "conversation.h"
#include "debug.h"
#include "signals.h"
#include "status.h"
#include "version.h"
-#include "privacy.h"
#include "plugin.h"
#include "pluginpref.h"
@@ -34,7 +33,7 @@
static void
buddy_typing_cb(PurpleAccount *acct, const char *name, void *data) {
- PurpleConversation *gconv;
+ PurpleIMConversation *im;
if(purple_prefs_get_bool(PREF_STATUS) &&
! purple_status_is_available(purple_account_get_active_status(acct))) {
@@ -43,23 +42,23 @@ buddy_typing_cb(PurpleAccount *acct, const char *name, void *data) {
}
if(purple_prefs_get_bool(PREF_BUDDIES) &&
- ! purple_find_buddy(acct, name)) {
+ ! purple_blist_find_buddy(acct, name)) {
purple_debug_info("psychic", "not in blist, doing nothing\n");
return;
}
- if(FALSE == purple_privacy_check(acct, name)) {
+ if(FALSE == purple_account_privacy_check(acct, name)) {
purple_debug_info("psychic", "user %s is blocked\n", name);
return;
}
- gconv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, acct);
- if(! gconv) {
+ im = purple_conversations_find_im_with_account(name, acct);
+ if(! im) {
purple_debug_info("psychic", "no previous conversation exists\n");
- gconv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, name);
+ im = purple_im_conversation_new(acct, name);
if(purple_prefs_get_bool(PREF_RAISE)) {
- purple_conversation_present(gconv);
+ purple_conversation_present(PURPLE_CONVERSATION(im));
}
if(purple_prefs_get_bool(PREF_NOTICE)) {
@@ -68,14 +67,13 @@ buddy_typing_cb(PurpleAccount *acct, const char *name, void *data) {
translate it literally. If you can't find a fitting cultural
reference in your language, consider translating something
like this instead: "You feel a new message coming." */
- purple_conversation_write(gconv, NULL,
- _("You feel a disturbance in the force..."),
- PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG | PURPLE_MESSAGE_ACTIVE_ONLY,
- time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
+ _("You feel a disturbance in the force..."),
+ PURPLE_MESSAGE_NO_LOG | PURPLE_MESSAGE_ACTIVE_ONLY);
}
/* Necessary because we may be creating a new conversation window. */
- purple_conv_im_set_typing_state(PURPLE_CONV_IM(gconv), PURPLE_TYPING);
+ purple_im_conversation_set_typing_state(im, PURPLE_IM_TYPING);
}
}
@@ -125,8 +123,7 @@ plugin_load(PurplePlugin *plugin) {
static PurplePluginUiInfo prefs_info = {
get_plugin_pref_frame,
- 0, /* page_num (Reserved) */
- NULL, /* frame (Reserved) */
+ NULL,
/* padding */
NULL,
diff --git a/libpurple/plugins/signals-test.c b/libpurple/plugins/signals-test.c
index 2992d4dc15..ae3ddd8756 100644
--- a/libpurple/plugins/signals-test.c
+++ b/libpurple/plugins/signals-test.c
@@ -24,12 +24,11 @@
#include <stdio.h>
-#include "cipher.h"
#include "connection.h"
#include "conversation.h"
#include "core.h"
#include "debug.h"
-#include "ft.h"
+#include "xfer.h"
#include "signals.h"
#include "version.h"
#include "status.h"
@@ -74,29 +73,29 @@ account_alias_changed(PurpleAccount *account, const char *old, gpointer data)
{
purple_debug_misc("signals test", "account-alias-changed (%s, %s, %s)\n",
purple_account_get_username(account),
- old, purple_account_get_alias(account));
+ old, purple_account_get_private_alias(account));
}
static int
-account_authorization_requested_cb(PurpleAccount *account, const char *user, gpointer data)
+account_authorization_requested_cb(PurpleAccount *account, const char *user, const char *message, char *response, gpointer data)
{
- purple_debug_misc("signals test", "account-authorization-requested (%s, %s)\n",
- purple_account_get_username(account), user);
- return 0;
+ purple_debug_misc("signals test", "account-authorization-requested (%s, %s, %s)\n",
+ purple_account_get_username(account), user, message);
+ return PURPLE_ACCOUNT_RESPONSE_PASS;
}
static void
-account_authorization_granted_cb(PurpleAccount *account, const char *user, gpointer data)
+account_authorization_granted_cb(PurpleAccount *account, const char *user, const char *message, gpointer data)
{
- purple_debug_misc("signals test", "account-authorization-granted (%s, %s)\n",
- purple_account_get_username(account), user);
+ purple_debug_misc("signals test", "account-authorization-granted (%s, %s, %s)\n",
+ purple_account_get_username(account), user, message);
}
static void
-account_authorization_denied_cb(PurpleAccount *account, const char *user, gpointer data)
+account_authorization_denied_cb(PurpleAccount *account, const char *user, const char *message, gpointer data)
{
- purple_debug_misc("signals test", "account-authorization-denied (%s, %s)\n",
- purple_account_get_username(account), user);
+ purple_debug_misc("signals test", "account-authorization-denied (%s, %s, %s)\n",
+ purple_account_get_username(account), user, message);
}
/**************************************************************************
@@ -149,12 +148,12 @@ static void
blist_node_added_cb(PurpleBlistNode *bnode, void *data)
{
const char *name;
- if (PURPLE_BLIST_NODE_IS_GROUP(bnode))
+ if (PURPLE_IS_GROUP(bnode))
name = purple_group_get_name(PURPLE_GROUP(bnode));
- else if (PURPLE_BLIST_NODE_IS_CONTACT(bnode))
+ else if (PURPLE_IS_CONTACT(bnode))
/* Close enough */
name = purple_contact_get_alias(PURPLE_CONTACT(bnode));
- else if (PURPLE_BLIST_NODE_IS_BUDDY(bnode))
+ else if (PURPLE_IS_BUDDY(bnode))
name = purple_buddy_get_name(PURPLE_BUDDY(bnode));
else
name = "(unknown)";
@@ -167,12 +166,12 @@ static void
blist_node_removed_cb(PurpleBlistNode *bnode, void *data)
{
const char *name;
- if (PURPLE_BLIST_NODE_IS_GROUP(bnode))
+ if (PURPLE_IS_GROUP(bnode))
name = purple_group_get_name(PURPLE_GROUP(bnode));
- else if (PURPLE_BLIST_NODE_IS_CONTACT(bnode))
+ else if (PURPLE_IS_CONTACT(bnode))
/* Close enough */
name = purple_contact_get_alias(PURPLE_CONTACT(bnode));
- else if (PURPLE_BLIST_NODE_IS_BUDDY(bnode))
+ else if (PURPLE_IS_BUDDY(bnode))
name = purple_buddy_get_name(PURPLE_BUDDY(bnode));
else
name = "(unknown)";
@@ -184,62 +183,54 @@ blist_node_removed_cb(PurpleBlistNode *bnode, void *data)
static void
blist_node_aliased(PurpleBlistNode *node, const char *old_alias)
{
- PurpleContact *p = (PurpleContact *)node;
- PurpleBuddy *b = (PurpleBuddy *)node;
- PurpleChat *c = (PurpleChat *)node;
- PurpleGroup *g = (PurpleGroup *)node;
+ PurpleContact *p = PURPLE_CONTACT(node);
+ PurpleBuddy *b = PURPLE_BUDDY(node);
+ PurpleChat *c = PURPLE_CHAT(node);
+ PurpleGroup *g = PURPLE_GROUP(node);
- if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ if (PURPLE_IS_CONTACT(node)) {
purple_debug_misc("signals test",
"blist-node-aliased (Contact: %s, %s)\n",
purple_contact_get_alias(p), old_alias);
- } else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ } else if (PURPLE_IS_BUDDY(node)) {
purple_debug_misc("signals test",
"blist-node-aliased (Buddy: %s, %s)\n",
purple_buddy_get_name(b), old_alias);
- } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if (PURPLE_IS_CHAT(node)) {
purple_debug_misc("signals test",
"blist-node-aliased (Chat: %s, %s)\n",
purple_chat_get_name(c), old_alias);
- } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ } else if (PURPLE_IS_GROUP(node)) {
purple_debug_misc("signals test",
"blist-node-aliased (Group: %s, %s)\n",
purple_group_get_name(g), old_alias);
- } else {
- purple_debug_misc("signals test",
- "blist-node-aliased (UNKNOWN: %d, %s)\n",
- purple_blist_node_get_type(node), old_alias);
}
}
static void
blist_node_extended_menu_cb(PurpleBlistNode *node, void *data)
{
- PurpleContact *p = (PurpleContact *)node;
- PurpleBuddy *b = (PurpleBuddy *)node;
- PurpleChat *c = (PurpleChat *)node;
- PurpleGroup *g = (PurpleGroup *)node;
+ PurpleContact *p = PURPLE_CONTACT(node);
+ PurpleBuddy *b = PURPLE_BUDDY(node);
+ PurpleChat *c = PURPLE_CHAT(node);
+ PurpleGroup *g = PURPLE_GROUP(node);
- if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ if (PURPLE_IS_CONTACT(node)) {
purple_debug_misc("signals test",
"blist-node-extended-menu (Contact: %s)\n",
purple_contact_get_alias(p));
- } else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ } else if (PURPLE_IS_BUDDY(node)) {
purple_debug_misc("signals test",
"blist-node-extended-menu (Buddy: %s)\n",
purple_buddy_get_name(b));
- } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if (PURPLE_IS_CHAT(node)) {
purple_debug_misc("signals test",
"blist-node-extended-menu (Chat: %s)\n",
purple_chat_get_name(c));
- } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ } else if (PURPLE_IS_GROUP(node)) {
purple_debug_misc("signals test",
"blist-node-extended-menu (Group: %s)\n",
purple_group_get_name(g));
- } else {
- purple_debug_misc("signals test",
- "blist-node-extended-menu (UNKNOWN: %d)\n",
- purple_blist_node_get_type(node));
}
}
@@ -302,26 +293,30 @@ writing_im_msg_cb(PurpleAccount *account, const char *who, char **buffer,
}
static void
-wrote_im_msg_cb(PurpleAccount *account, const char *who, const char *buffer,
- PurpleConversation *conv, PurpleMessageFlags flags, void *data)
+wrote_im_msg_cb(PurpleConversation *conv, PurpleMessage *msg, gpointer data)
{
- purple_debug_misc("signals test", "wrote-im-msg (%s, %s, %s)\n",
- purple_account_get_username(account), purple_conversation_get_name(conv), buffer);
+ purple_debug_misc("signals test", "wrote-im-msg (%s, %s)\n",
+ purple_conversation_get_name(conv),
+ purple_message_get_contents(msg));
}
static void
-sending_im_msg_cb(PurpleAccount *account, char *recipient, char **buffer, void *data)
+sending_im_msg_cb(PurpleAccount *account, PurpleMessage *msg, void *data)
{
purple_debug_misc("signals test", "sending-im-msg (%s, %s, %s)\n",
- purple_account_get_username(account), recipient, *buffer);
+ purple_account_get_username(account),
+ purple_message_get_recipient(msg),
+ purple_message_get_contents(msg));
}
static void
-sent_im_msg_cb(PurpleAccount *account, const char *recipient, const char *buffer, void *data)
+sent_im_msg_cb(PurpleAccount *account, PurpleMessage *msg, void *data)
{
purple_debug_misc("signals test", "sent-im-msg (%s, %s, %s)\n",
- purple_account_get_username(account), recipient, buffer);
+ purple_account_get_username(account),
+ purple_message_get_recipient(msg),
+ purple_message_get_contents(msg));
}
static gboolean
@@ -355,27 +350,29 @@ writing_chat_msg_cb(PurpleAccount *account, const char *who, char **buffer,
}
static void
-wrote_chat_msg_cb(PurpleAccount *account, const char *who, const char *buffer,
- PurpleConversation *conv, PurpleMessageFlags flags, void *data)
+wrote_chat_msg_cb(PurpleConversation *conv, PurpleMessage *msg, gpointer data)
{
purple_debug_misc("signals test", "wrote-chat-msg (%s, %s)\n",
- purple_conversation_get_name(conv), buffer);
+ purple_conversation_get_name(conv),
+ purple_message_get_contents(msg));
}
static gboolean
-sending_chat_msg_cb(PurpleAccount *account, char **buffer, int id, void *data)
+sending_chat_msg_cb(PurpleAccount *account, PurpleMessage *msg, int id, void *data)
{
purple_debug_misc("signals test", "sending-chat-msg (%s, %s, %d)\n",
- purple_account_get_username(account), *buffer, id);
+ purple_account_get_username(account),
+ purple_message_get_contents(msg), id);
return FALSE;
}
static void
-sent_chat_msg_cb(PurpleAccount *account, const char *buffer, int id, void *data)
+sent_chat_msg_cb(PurpleAccount *account, PurpleMessage *msg, int id, void *data)
{
purple_debug_misc("signals test", "sent-chat-msg (%s, %s, %d)\n",
- purple_account_get_username(account), buffer, id);
+ purple_account_get_username(account),
+ purple_message_get_contents(msg), id);
}
static gboolean
@@ -429,46 +426,48 @@ buddy_typing_stopped_cb(PurpleAccount *account, const char *name, void *data)
}
static gboolean
-chat_buddy_joining_cb(PurpleConversation *conv, const char *user,
- PurpleConvChatBuddyFlags flags, void *data)
+chat_user_joining_cb(PurpleConversation *conv, const char *user,
+ PurpleChatUserFlags flags, void *data)
{
- purple_debug_misc("signals test", "chat-buddy-joining (%s, %s, %d)\n",
+ purple_debug_misc("signals test", "chat-user-joining (%s, %s, %d)\n",
purple_conversation_get_name(conv), user, flags);
return FALSE;
}
static void
-chat_buddy_joined_cb(PurpleConversation *conv, const char *user,
- PurpleConvChatBuddyFlags flags, gboolean new_arrival, void *data)
+chat_user_joined_cb(PurpleConversation *conv, const char *user,
+ PurpleChatUserFlags flags, gboolean new_arrival, void *data)
{
- purple_debug_misc("signals test", "chat-buddy-joined (%s, %s, %d, %d)\n",
+ purple_debug_misc("signals test", "chat-user-joined (%s, %s, %d, %d)\n",
purple_conversation_get_name(conv), user, flags, new_arrival);
}
static void
-chat_buddy_flags_cb(PurpleConversation *conv, const char *user,
- PurpleConvChatBuddyFlags oldflags, PurpleConvChatBuddyFlags newflags, void *data)
+chat_user_flags_cb(PurpleChatUser *cb, PurpleChatUserFlags oldflags,
+ PurpleChatUserFlags newflags, void *data)
{
- purple_debug_misc("signals test", "chat-buddy-flags (%s, %s, %d, %d)\n",
- purple_conversation_get_name(conv), user, oldflags, newflags);
+ purple_debug_misc("signals test", "chat-user-flags (%s, %s, %d, %d)\n",
+ purple_conversation_get_name(PURPLE_CONVERSATION(
+ purple_chat_user_get_chat(cb))),
+ purple_chat_user_get_name(cb), oldflags, newflags);
}
static gboolean
-chat_buddy_leaving_cb(PurpleConversation *conv, const char *user,
+chat_user_leaving_cb(PurpleConversation *conv, const char *user,
const char *reason, void *data)
{
- purple_debug_misc("signals test", "chat-buddy-leaving (%s, %s, %s)\n",
+ purple_debug_misc("signals test", "chat-user-leaving (%s, %s, %s)\n",
purple_conversation_get_name(conv), user, reason);
return FALSE;
}
static void
-chat_buddy_left_cb(PurpleConversation *conv, const char *user,
+chat_user_left_cb(PurpleConversation *conv, const char *user,
const char *reason, void *data)
{
- purple_debug_misc("signals test", "chat-buddy-left (%s, %s, %s)\n",
+ purple_debug_misc("signals test", "chat-user-left (%s, %s, %s)\n",
purple_conversation_get_name(conv), user, reason);
}
@@ -524,21 +523,6 @@ chat_topic_changed_cb(PurpleConversation *conv, const char *who,
(who) ? who : "unknown");
}
/**************************************************************************
- * Ciphers signal callbacks
- **************************************************************************/
-static void
-cipher_added_cb(PurpleCipher *cipher, void *data) {
- purple_debug_misc("signals test", "cipher %s added\n",
- purple_cipher_get_name(cipher));
-}
-
-static void
-cipher_removed_cb(PurpleCipher *cipher, void *data) {
- purple_debug_misc("signals test", "cipher %s removed\n",
- purple_cipher_get_name(cipher));
-}
-
-/**************************************************************************
* Core signal callbacks
**************************************************************************/
static void
@@ -635,7 +619,7 @@ notify_email_cb(char *subject, char *from, char *to, char *url) {
static void
notify_emails_cb(char **subjects, char **froms, char **tos, char **urls, guint count) {
- int i;
+ guint i;
purple_debug_misc("signals test", "notify emails: count=%d\n", count);
for(i=0; i<count && i<5; i++) {
if(subjects[i]==NULL || froms[i]==NULL || tos[i]==NULL || urls[i]==NULL) continue;
@@ -649,7 +633,7 @@ notify_emails_cb(char **subjects, char **froms, char **tos, char **urls, guint c
**************************************************************************/
static gboolean
jabber_iq_received(PurpleConnection *pc, const char *type, const char *id,
- const char *from, xmlnode *iq)
+ const char *from, PurpleXmlNode *iq)
{
purple_debug_misc("signals test", "jabber IQ (type=%s, id=%s, from=%s) %p\n",
type, id, from ? from : "(null)", iq);
@@ -660,7 +644,7 @@ jabber_iq_received(PurpleConnection *pc, const char *type, const char *id,
static gboolean
jabber_message_received(PurpleConnection *pc, const char *type, const char *id,
- const char *from, const char *to, xmlnode *message)
+ const char *from, const char *to, PurpleXmlNode *message)
{
purple_debug_misc("signals test", "jabber message (type=%s, id=%s, "
"from=%s to=%s) %p\n",
@@ -673,7 +657,7 @@ jabber_message_received(PurpleConnection *pc, const char *type, const char *id,
static gboolean
jabber_presence_received(PurpleConnection *pc, const char *type,
- const char *from, xmlnode *presence)
+ const char *from, PurpleXmlNode *presence)
{
purple_debug_misc("signals test", "jabber presence (type=%s, from=%s) %p\n",
type ? type : "(null)", from ? from : "(null)", presence);
@@ -684,24 +668,24 @@ jabber_presence_received(PurpleConnection *pc, const char *type,
static gboolean
jabber_watched_iq(PurpleConnection *pc, const char *type, const char *id,
- const char *from, xmlnode *child)
+ const char *from, PurpleXmlNode *child)
{
purple_debug_misc("signals test", "jabber watched IQ (type=%s, id=%s, from=%s)\n"
"child %p name=%s, namespace=%s\n",
type, id, from, child, child->name,
- xmlnode_get_namespace(child));
+ purple_xmlnode_get_namespace(child));
if (g_str_equal(type, "get") || g_str_equal(type, "set")) {
/* Send the requisite reply */
- xmlnode *iq = xmlnode_new("iq");
- xmlnode_set_attrib(iq, "to", from);
- xmlnode_set_attrib(iq, "id", id);
- xmlnode_set_attrib(iq, "type", "result");
+ PurpleXmlNode *iq = purple_xmlnode_new("iq");
+ purple_xmlnode_set_attrib(iq, "to", from);
+ purple_xmlnode_set_attrib(iq, "id", id);
+ purple_xmlnode_set_attrib(iq, "type", "result");
purple_signal_emit(purple_connection_get_prpl(pc),
"jabber-sending-xmlnode", pc, &iq);
if (iq != NULL)
- xmlnode_free(iq);
+ purple_xmlnode_free(iq);
}
/* Cookie monster eats IQ stanzas; the prpl shouldn't keep processing */
@@ -719,7 +703,6 @@ plugin_load(PurplePlugin *plugin)
void *conn_handle = purple_connections_get_handle();
void *conv_handle = purple_conversations_get_handle();
void *accounts_handle = purple_accounts_get_handle();
- void *ciphers_handle = purple_ciphers_get_handle();
void *ft_handle = purple_xfers_get_handle();
void *sound_handle = purple_sounds_get_handle();
void *notify_handle = purple_notify_get_handle();
@@ -808,16 +791,16 @@ plugin_load(PurplePlugin *plugin)
plugin, PURPLE_CALLBACK(buddy_typing_cb), NULL);
purple_signal_connect(conv_handle, "buddy-typing-stopped",
plugin, PURPLE_CALLBACK(buddy_typing_stopped_cb), NULL);
- purple_signal_connect(conv_handle, "chat-buddy-joining",
- plugin, PURPLE_CALLBACK(chat_buddy_joining_cb), NULL);
- purple_signal_connect(conv_handle, "chat-buddy-joined",
- plugin, PURPLE_CALLBACK(chat_buddy_joined_cb), NULL);
- purple_signal_connect(conv_handle, "chat-buddy-flags",
- plugin, PURPLE_CALLBACK(chat_buddy_flags_cb), NULL);
- purple_signal_connect(conv_handle, "chat-buddy-leaving",
- plugin, PURPLE_CALLBACK(chat_buddy_leaving_cb), NULL);
- purple_signal_connect(conv_handle, "chat-buddy-left",
- plugin, PURPLE_CALLBACK(chat_buddy_left_cb), NULL);
+ purple_signal_connect(conv_handle, "chat-user-joining",
+ plugin, PURPLE_CALLBACK(chat_user_joining_cb), NULL);
+ purple_signal_connect(conv_handle, "chat-user-joined",
+ plugin, PURPLE_CALLBACK(chat_user_joined_cb), NULL);
+ purple_signal_connect(conv_handle, "chat-user-flags",
+ plugin, PURPLE_CALLBACK(chat_user_flags_cb), NULL);
+ purple_signal_connect(conv_handle, "chat-user-leaving",
+ plugin, PURPLE_CALLBACK(chat_user_leaving_cb), NULL);
+ purple_signal_connect(conv_handle, "chat-user-left",
+ plugin, PURPLE_CALLBACK(chat_user_left_cb), NULL);
purple_signal_connect(conv_handle, "chat-inviting-user",
plugin, PURPLE_CALLBACK(chat_inviting_user_cb), NULL);
purple_signal_connect(conv_handle, "chat-invited-user",
@@ -831,12 +814,6 @@ plugin_load(PurplePlugin *plugin)
purple_signal_connect(conv_handle, "chat-topic-changed",
plugin, PURPLE_CALLBACK(chat_topic_changed_cb), NULL);
- /* Ciphers signals */
- purple_signal_connect(ciphers_handle, "cipher-added",
- plugin, PURPLE_CALLBACK(cipher_added_cb), NULL);
- purple_signal_connect(ciphers_handle, "cipher-removed",
- plugin, PURPLE_CALLBACK(cipher_removed_cb), NULL);
-
/* Core signals */
purple_signal_connect(core_handle, "quitting",
plugin, PURPLE_CALLBACK(quitting_cb), NULL);
diff --git a/libpurple/plugins/ssl/Makefile.am b/libpurple/plugins/ssl/Makefile.am
index f95ef88f58..0425390540 100644
--- a/libpurple/plugins/ssl/Makefile.am
+++ b/libpurple/plugins/ssl/Makefile.am
@@ -1,53 +1,36 @@
EXTRA_DIST = \
Makefile.mingw
-plugindir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+plugindir = @PURPLE_PLUGINDIR@
-ssl_la_LDFLAGS = -module -avoid-version
-ssl_gnutls_la_LDFLAGS = -module -avoid-version
-ssl_nss_la_LDFLAGS = -module -avoid-version
+ssl_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+ssl_gnutls_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+ssl_nss_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
if PLUGINS
-# I'm sorry to report that Automake Conditionals don't support
-# if USE_GNUTLS && USE_NSS
-# but only support testing a single variable. Hence:
-
-if USE_GNUTLS
-if USE_NSS
plugin_LTLIBRARIES = \
- ssl.la \
- ssl-gnutls.la \
- ssl-nss.la
-else
-plugin_LTLIBRARIES = \
- ssl.la \
+ ssl.la
+if USE_GNUTLS
+plugin_LTLIBRARIES += \
ssl-gnutls.la
endif
-else
if USE_NSS
-plugin_LTLIBRARIES = \
- ssl.la \
+plugin_LTLIBRARIES += \
ssl-nss.la
-else
-plugin_LTLIBRARIES = \
- ssl.la
-endif
endif
ssl_la_SOURCES = ssl.c
ssl_gnutls_la_SOURCES = ssl-gnutls.c
ssl_nss_la_SOURCES = ssl-nss.c
-ssl_la_LIBADD = $(GLIB_LIBS)
-ssl_gnutls_la_LIBADD = $(GLIB_LIBS) $(GNUTLS_LIBS)
-ssl_nss_la_LIBADD = $(GLIB_LIBS) $(NSS_LIBS)
+ssl_la_LIBADD = @PURPLE_LIBS@
+ssl_gnutls_la_LIBADD = @PURPLE_LIBS@ $(GNUTLS_LIBS)
+ssl_nss_la_LIBADD = @PURPLE_LIBS@ $(NSS_LIBS)
endif # PLUGINS
AM_CPPFLAGS = \
- -DDATADIR=\"$(datadir)\" \
- -DLIBDIR=\"$(libdir)/libpurple\" \
-I$(top_srcdir)/libpurple \
-I$(top_builddir)/libpurple \
$(DEBUG_CFLAGS) \
@@ -56,3 +39,4 @@ AM_CPPFLAGS = \
ssl_gnutls_la_CFLAGS = $(AM_CPPFLAGS) $(GNUTLS_CFLAGS)
ssl_nss_la_CFLAGS = $(AM_CPPFLAGS) $(NSS_CFLAGS)
+
diff --git a/libpurple/plugins/ssl/Makefile.mingw b/libpurple/plugins/ssl/Makefile.mingw
index 267603c3f0..a3683b5c05 100644
--- a/libpurple/plugins/ssl/Makefile.mingw
+++ b/libpurple/plugins/ssl/Makefile.mingw
@@ -12,18 +12,7 @@ include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
##
TARGET = ssl
TARGET_NSS = ssl-nss
-
-NEEDED_DLLS = \
- $(NSS_TOP)/lib/freebl3.dll \
- $(NSS_TOP)/lib/libnspr4.dll \
- $(NSS_TOP)/lib/libplc4.dll \
- $(NSS_TOP)/lib/libplds4.dll \
- $(NSS_TOP)/lib/nss3.dll \
- $(NSS_TOP)/lib/nssutil3.dll \
- $(NSS_TOP)/lib/smime3.dll \
- $(NSS_TOP)/lib/softokn3.dll \
- $(NSS_TOP)/lib/sqlite3.dll \
- $(NSS_TOP)/lib/ssl3.dll
+TARGET_GNUTLS = ssl-gnutls
##
## INCLUDE PATHS
@@ -35,32 +24,40 @@ INCLUDE_PATHS += -I. \
-I$(PURPLE_TOP) \
-I$(PURPLE_TOP)/win32 \
-I$(PIDGIN_TREE_TOP) \
- -I$(NSS_TOP)/include
+ -I$(NSS_TOP)/include/nspr4 \
+ -I$(NSS_TOP)/include/nss3 \
+ -I$(GNUTLS_TOP)/include
-LIB_PATHS += -L$(GTK_TOP)/lib \
+LIB_PATHS += \
+ -L$(GTK_TOP)/lib \
-L$(PURPLE_TOP) \
- -L$(NSS_TOP)/lib
+ -L$(NSS_TOP)/lib \
+ -L$(GNUTLS_TOP)/lib
##
## SOURCES, OBJECTS
##
C_SRC = ssl.c
C_SRC_NSS = ssl-nss.c
+C_SRC_GNUTLS = ssl-gnutls.c
OBJECTS = $(C_SRC:%.c=%.o)
OBJECTS_NSS = $(C_SRC_NSS:%.c=%.o)
+OBJECTS_GNUTLS = $(C_SRC_GNUTLS:%.c=%.o)
##
## LIBRARIES
##
LIBS = \
-lglib-2.0 \
+ -lgobject-2.0 \
-lws2_32 \
-lintl \
-lpurple \
-lnss3 \
-lnspr4 \
-lssl3 \
- -lsmime3
+ -lsmime3 \
+ -lgnutls
include $(PIDGIN_COMMON_RULES)
@@ -69,14 +66,14 @@ include $(PIDGIN_COMMON_RULES)
##
.PHONY: all install clean
-all: $(TARGET).dll $(TARGET_NSS).dll
+all: $(TARGET).dll $(TARGET_NSS).dll $(TARGET_GNUTLS).dll
install: all $(PURPLE_INSTALL_PLUGINS_DIR) $(PURPLE_INSTALL_DIR)
cp $(TARGET).dll $(PURPLE_INSTALL_PLUGINS_DIR)
cp $(TARGET_NSS).dll $(PURPLE_INSTALL_PLUGINS_DIR)
- cp $(NEEDED_DLLS) $(PURPLE_INSTALL_DIR)
+ cp $(TARGET_GNUTLS).dll $(PURPLE_INSTALL_PLUGINS_DIR)
-$(OBJECTS) $(OBJECTS_NSS): $(PURPLE_CONFIG_H)
+$(OBJECTS) $(OBJECTS_NSS) $(OBJECTS_GNUTLS): $(PURPLE_CONFIG_H)
##
## BUILD DLL
@@ -87,10 +84,13 @@ $(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS)
$(TARGET_NSS).dll: $(PURPLE_DLL) $(OBJECTS_NSS)
$(CC) -shared $(OBJECTS_NSS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET_NSS).dll
+$(TARGET_GNUTLS).dll: $(PURPLE_DLL) $(OBJECTS_GNUTLS)
+ $(CC) -shared $(OBJECTS_GNUTLS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET_GNUTLS).dll
+
##
## CLEAN RULES
##
clean:
- rm -f $(OBJECTS) $(OBJECTS_NSS) $(TARGET).dll $(TARGET_NSS).dll
+ rm -f $(OBJECTS) $(OBJECTS_NSS) $(TARGET).dll $(TARGET_NSS).dll $(TARGET_GNUTLS).dll
include $(PIDGIN_COMMON_TARGETS)
diff --git a/libpurple/plugins/ssl/ssl-gnutls.c b/libpurple/plugins/ssl/ssl-gnutls.c
index 7dedda5eee..2dc22f716e 100644
--- a/libpurple/plugins/ssl/ssl-gnutls.c
+++ b/libpurple/plugins/ssl/ssl-gnutls.c
@@ -285,9 +285,9 @@ static void ssl_gnutls_handshake_cb(gpointer data, gint source,
{
const gnutls_datum_t *cert_list;
- unsigned int cert_list_size = 0;
+ guint cert_list_size = 0;
gnutls_session_t session=gnutls_data->session;
- int i;
+ guint i;
cert_list =
gnutls_certificate_get_peers(session, &cert_list_size);
@@ -412,6 +412,11 @@ ssl_gnutls_connect(PurpleSslConnection *gsc)
gnutls_set_default_priority(gnutls_data->session);
#endif
+ if (gsc->host) {
+ gnutls_server_name_set(gnutls_data->session, GNUTLS_NAME_DNS,
+ gsc->host, strlen(gsc->host));
+ }
+
gnutls_credentials_set(gnutls_data->session, GNUTLS_CRD_CERTIFICATE,
xcred);
@@ -468,6 +473,11 @@ ssl_gnutls_read(PurpleSslConnection *gsc, void *data, size_t len)
if(s == GNUTLS_E_AGAIN || s == GNUTLS_E_INTERRUPTED) {
s = -1;
errno = EAGAIN;
+#ifdef GNUTLS_E_PREMATURE_TERMINATION
+ } else if (s == GNUTLS_E_PREMATURE_TERMINATION) {
+ purple_debug_warning("gnutls", "premature termination\n");
+ s = 0;
+#endif
} else if(s < 0) {
purple_debug_error("gnutls", "receive failed: %s\n",
gnutls_strerror(s));
@@ -613,7 +623,7 @@ x509_crtdata_delref(x509_crtdata_t *cd)
/** Helper macro to retrieve the GnuTLS crt_t from a PurpleCertificate */
#define X509_GET_GNUTLS_DATA(pcrt) ( ((x509_crtdata_t *) (pcrt->data))->crt)
-/** Transforms a gnutls_datum containing an X.509 certificate into a Certificate instance under the x509_gnutls scheme
+/** Transforms a gnutls_datum_t containing an X.509 certificate into a Certificate instance under the x509_gnutls scheme
*
* @param dt Datum to transform
* @param mode GnuTLS certificate format specifier (GNUTLS_X509_FMT_PEM for
@@ -1112,7 +1122,7 @@ x509_check_name (PurpleCertificate *crt, const gchar *name)
}
static gboolean
-x509_times (PurpleCertificate *crt, time_t *activation, time_t *expiration)
+x509_times (PurpleCertificate *crt, gint64 *activation, gint64 *expiration)
{
gnutls_x509_crt_t crt_dat;
/* GnuTLS time functions return this on error */
@@ -1138,6 +1148,101 @@ x509_times (PurpleCertificate *crt, time_t *activation, time_t *expiration)
return success;
}
+static GByteArray *
+x509_get_der_data(PurpleCertificate *crt)
+{
+ gnutls_x509_crt_t crt_dat;
+ GByteArray *data;
+ size_t len;
+ int ret;
+
+ crt_dat = X509_GET_GNUTLS_DATA(crt);
+ g_return_val_if_fail(crt_dat, NULL);
+
+ /* Obtain the output size required */
+ len = 0;
+ ret = gnutls_x509_crt_export(crt_dat, GNUTLS_X509_FMT_DER, NULL, &len);
+ g_return_val_if_fail(ret == GNUTLS_E_SHORT_MEMORY_BUFFER, NULL);
+
+ /* Now allocate a buffer and *really* export it */
+ data = g_byte_array_sized_new(len);
+ data->len = len;
+ ret = gnutls_x509_crt_export(crt_dat, GNUTLS_X509_FMT_DER, data->data, &len);
+ if (ret != 0) {
+ purple_debug_error("gnutls/x509",
+ "Failed to export cert to buffer with code %d\n",
+ ret);
+ g_byte_array_free(data, TRUE);
+ return NULL;
+ }
+
+ return data;
+}
+
+static gchar *
+x509_display_string(PurpleCertificate *crt)
+{
+ gchar *sha_asc;
+ GByteArray *sha_bin;
+ gchar *cn;
+ gint64 activation, expiration;
+ gchar *activ_str, *expir_str;
+ gchar *text;
+#if GLIB_CHECK_VERSION(2,26,0)
+ GDateTime *act_dt, *exp_dt;
+#endif
+
+ /* Pull out the SHA1 checksum */
+ sha_bin = x509_sha1sum(crt);
+ g_return_val_if_fail(sha_bin != NULL, NULL);
+ sha_asc = purple_base16_encode_chunked(sha_bin->data, sha_bin->len);
+
+ /* Get the cert Common Name */
+ /* TODO: Will break on CA certs */
+ cn = x509_common_name(crt);
+
+ /* Get the certificate times */
+ /* TODO: Check the times against localtime */
+ /* TODO: errorcheck? */
+ if (!x509_times(crt, &activation, &expiration)) {
+ purple_debug_error("certificate",
+ "Failed to get certificate times!\n");
+ activation = expiration = 0;
+ }
+
+#if GLIB_CHECK_VERSION(2,26,0)
+ act_dt = g_date_time_new_from_unix_local(activation);
+ activ_str = g_date_time_format(act_dt, "%c");
+ g_date_time_unref(act_dt);
+
+ exp_dt = g_date_time_new_from_unix_local(expiration);
+ expir_str = g_date_time_format(exp_dt, "%c");
+ g_date_time_unref(exp_dt);
+#else
+ activ_str = g_strdup(ctime(&activation));
+ expir_str = g_strdup(ctime(&expiration));
+#endif
+
+ /* Make messages */
+ text = g_strdup_printf(_("Common name: %s\n\n"
+ "Fingerprint (SHA1): %s\n\n"
+ "Activation date: %s\n"
+ "Expiration date: %s\n"),
+ cn ? cn : "(null)",
+ sha_asc ? sha_asc : "(null)",
+ activ_str ? activ_str : "(null)",
+ expir_str ? expir_str : "(null)");
+
+ /* Cleanup */
+ g_free(cn);
+ g_free(sha_asc);
+ g_free(activ_str);
+ g_free(expir_str);
+ g_byte_array_free(sha_bin, TRUE);
+
+ return text;
+}
+
/* X.509 certificate operations provided by this plugin */
static PurpleCertificateScheme x509_gnutls = {
"x509", /* Scheme name */
@@ -1154,9 +1259,9 @@ static PurpleCertificateScheme x509_gnutls = {
x509_check_name, /* Check subject name */
x509_times, /* Activation/Expiration time */
x509_importcerts_from_file, /* Multiple certificates import function */
+ x509_get_der_data, /* Binary DER data */
+ x509_display_string, /* Display representation */
- NULL,
- NULL,
NULL
};
@@ -1174,6 +1279,7 @@ static PurpleSslOps ssl_ops =
/* padding */
NULL,
NULL,
+ NULL,
NULL
};
diff --git a/libpurple/plugins/ssl/ssl-nss.c b/libpurple/plugins/ssl/ssl-nss.c
index fb71c55509..789379d51d 100644
--- a/libpurple/plugins/ssl/ssl-nss.c
+++ b/libpurple/plugins/ssl/ssl-nss.c
@@ -889,7 +889,7 @@ x509_check_name (PurpleCertificate *crt, const gchar *name)
}
static gboolean
-x509_times (PurpleCertificate *crt, time_t *activation, time_t *expiration)
+x509_times (PurpleCertificate *crt, gint64 *activation, gint64 *expiration)
{
CERTCertificate *crt_dat;
PRTime nss_activ, nss_expir;
@@ -915,49 +915,99 @@ x509_times (PurpleCertificate *crt, time_t *activation, time_t *expiration)
if (activation) {
*activation = nss_activ;
-#if SIZEOF_TIME_T == 4
- /** Hack to deal with dates past the 32-bit barrier.
- Handling is different for signed vs unsigned 32-bit types.
- */
- if (*activation != nss_activ) {
- if (nss_activ < 0) {
- purple_debug_warning("nss",
- "Setting Activation Date to epoch to handle pre-epoch value\n");
- *activation = 0;
- } else {
- purple_debug_error("nss",
- "Activation date past 32-bit barrier, forcing invalidity\n");
- return FALSE;
- }
- }
-#endif
}
if (expiration) {
*expiration = nss_expir;
-#if SIZEOF_TIME_T == 4
- if (*expiration != nss_expir) {
- if (*expiration < nss_expir) {
- if (*expiration < 0) {
- purple_debug_warning("nss",
- "Setting Expiration Date to 32-bit signed max\n");
- *expiration = PR_INT32_MAX;
- } else {
- purple_debug_warning("nss",
- "Setting Expiration Date to 32-bit unsigned max\n");
- *expiration = PR_UINT32_MAX;
- }
- } else {
- purple_debug_error("nss",
- "Expiration date prior to unix epoch, forcing invalidity\n");
- return FALSE;
- }
- }
-#endif
}
return TRUE;
}
+static GByteArray *
+x509_get_der_data(PurpleCertificate *crt)
+{
+ CERTCertificate *crt_dat;
+ SECItem *dercrt;
+ GByteArray *data;
+
+ crt_dat = X509_NSS_DATA(crt);
+ g_return_val_if_fail(crt_dat, NULL);
+
+ dercrt = SEC_ASN1EncodeItem(NULL, NULL, crt_dat,
+ SEC_ASN1_GET(SEC_SignedCertificateTemplate));
+ g_return_val_if_fail(dercrt != NULL, FALSE);
+
+ data = g_byte_array_sized_new(dercrt->len);
+ memcpy(data->data, dercrt->data, dercrt->len);
+ data->len = dercrt->len;
+
+ SECITEM_FreeItem(dercrt, PR_TRUE);
+
+ return data;
+}
+
+static gchar *
+x509_display_string(PurpleCertificate *crt)
+{
+ gchar *sha_asc;
+ GByteArray *sha_bin;
+ gchar *cn;
+ gint64 activation, expiration;
+ gchar *activ_str, *expir_str;
+ gchar *text;
+#if GLIB_CHECK_VERSION(2,26,0)
+ GDateTime *act_dt, *exp_dt;
+#endif
+
+ /* Pull out the SHA1 checksum */
+ sha_bin = x509_sha1sum(crt);
+ sha_asc = purple_base16_encode_chunked(sha_bin->data, sha_bin->len);
+
+ /* Get the cert Common Name */
+ /* TODO: Will break on CA certs */
+ cn = x509_common_name(crt);
+
+ /* Get the certificate times */
+ /* TODO: Check the times against localtime */
+ /* TODO: errorcheck? */
+ if (!x509_times(crt, &activation, &expiration)) {
+ purple_debug_error("certificate",
+ "Failed to get certificate times!\n");
+ activation = expiration = 0;
+ }
+#if GLIB_CHECK_VERSION(2,26,0)
+ act_dt = g_date_time_new_from_unix_local(activation);
+ activ_str = g_date_time_format(act_dt, "%c");
+ g_date_time_unref(act_dt);
+
+ exp_dt = g_date_time_new_from_unix_local(expiration);
+ expir_str = g_date_time_format(exp_dt, "%c");
+ g_date_time_unref(exp_dt);
+#else
+ activ_str = g_strdup(ctime(&activation));
+ expir_str = g_strdup(ctime(&expiration));
+#endif
+
+ /* Make messages */
+ text = g_strdup_printf(_("Common name: %s\n\n"
+ "Fingerprint (SHA1): %s\n\n"
+ "Activation date: %s\n"
+ "Expiration date: %s\n"),
+ cn ? cn : "(null)",
+ sha_asc ? sha_asc : "(null)",
+ activ_str ? activ_str : "(null)",
+ expir_str ? expir_str : "(null)");
+
+ /* Cleanup */
+ g_free(cn);
+ g_free(sha_asc);
+ g_free(activ_str);
+ g_free(expir_str);
+ g_byte_array_free(sha_bin, TRUE);
+
+ return text;
+}
+
static PurpleCertificateScheme x509_nss = {
"x509", /* Scheme name */
N_("X.509 Certificates"), /* User-visible scheme name */
@@ -973,9 +1023,9 @@ static PurpleCertificateScheme x509_nss = {
x509_check_name, /* Check subject name */
x509_times, /* Activation/Expiration time */
x509_importcerts_from_file, /* Multiple certificate import function */
+ x509_get_der_data, /* Binary DER data */
+ x509_display_string, /* Display representation */
- NULL,
- NULL,
NULL
};
@@ -992,6 +1042,7 @@ static PurpleSslOps ssl_ops =
/* padding */
NULL,
NULL,
+ NULL,
NULL
};
diff --git a/libpurple/plugins/statenotify.c b/libpurple/plugins/statenotify.c
index bfbe595e50..cf007f85c5 100644
--- a/libpurple/plugins/statenotify.c
+++ b/libpurple/plugins/statenotify.c
@@ -1,6 +1,6 @@
#include "internal.h"
-#include "blist.h"
+#include "buddylist.h"
#include "conversation.h"
#include "debug.h"
#include "signals.h"
@@ -16,7 +16,7 @@ static void
write_status(PurpleBuddy *buddy, const char *message)
{
PurpleAccount *account = NULL;
- PurpleConversation *conv;
+ PurpleIMConversation *im;
const char *who;
char buf[256];
char *escaped;
@@ -25,15 +25,13 @@ write_status(PurpleBuddy *buddy, const char *message)
account = purple_buddy_get_account(buddy);
buddy_name = purple_buddy_get_name(buddy);
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
- buddy_name, account);
+ im = purple_conversations_find_im_with_account(buddy_name, account);
- if (conv == NULL)
+ if (im == NULL)
return;
- g_return_if_fail(conv->type == PURPLE_CONV_TYPE_IM);
/* Prevent duplicate notifications for buddies in multiple groups */
- if (buddy != purple_find_buddy(account, buddy_name))
+ if (buddy != purple_blist_find_buddy(account, buddy_name))
return;
who = purple_buddy_get_alias(buddy);
@@ -42,7 +40,8 @@ write_status(PurpleBuddy *buddy, const char *message)
g_snprintf(buf, sizeof(buf), message, escaped);
g_free(escaped);
- purple_conv_im_write(conv->u.im, NULL, buf, PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_ACTIVE_ONLY | PURPLE_MESSAGE_NO_LINKIFY, time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(im), buf,
+ PURPLE_MESSAGE_ACTIVE_ONLY | PURPLE_MESSAGE_NO_LINKIFY);
}
static void
@@ -136,8 +135,7 @@ plugin_load(PurplePlugin *plugin)
static PurplePluginUiInfo prefs_info =
{
get_plugin_pref_frame,
- 0, /* page_num (Reserved) */
- NULL, /* frame (Reserved) */
+ NULL,
/* padding */
NULL,
diff --git a/libpurple/plugins/tcl/Makefile.am b/libpurple/plugins/tcl/Makefile.am
index a29ac91334..0b0adc07a2 100644
--- a/libpurple/plugins/tcl/Makefile.am
+++ b/libpurple/plugins/tcl/Makefile.am
@@ -1,13 +1,13 @@
-plugindir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+plugindir = @PURPLE_PLUGINDIR@
-tcl_la_LDFLAGS = -module -avoid-version
+tcl_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
plugin_LTLIBRARIES = tcl.la
tcl_la_SOURCES = tcl.c tcl_glib.c tcl_glib.h tcl_cmds.c tcl_signals.c tcl_purple.h \
tcl_ref.c tcl_cmd.c
-tcl_la_LIBADD = $(GLIB_LIBS) $(TCL_LIBS) $(TK_LIBS)
+tcl_la_LIBADD = @PURPLE_LIBS@ $(TCL_LIBS) $(TK_LIBS)
EXTRA_DIST = signal-test.tcl Makefile.mingw
diff --git a/libpurple/plugins/tcl/Makefile.mingw b/libpurple/plugins/tcl/Makefile.mingw
index 6ea97372c2..f7f640fb3f 100644
--- a/libpurple/plugins/tcl/Makefile.mingw
+++ b/libpurple/plugins/tcl/Makefile.mingw
@@ -25,7 +25,7 @@ INCLUDE_PATHS += -I. \
LIB_PATHS += -L$(GTK_TOP)/lib \
-L$(PURPLE_TOP) \
- -L$(TCL_LIB_TOP)
+ -L$(TCL_LIB_TOP)/lib
##
## SOURCES, OBJECTS
@@ -44,11 +44,12 @@ OBJECTS = $(C_SRC:%.c=%.o)
##
LIBS = \
-lglib-2.0 \
+ -lgobject-2.0 \
-lws2_32 \
-lintl \
-lpurple \
- -ltclstub84 \
- -ltkstub84
+ -ltclstub85 \
+ -ltkstub85
include $(PIDGIN_COMMON_RULES)
diff --git a/libpurple/plugins/tcl/signal-test.tcl b/libpurple/plugins/tcl/signal-test.tcl
index 91bd715681..c88964b72d 100644
--- a/libpurple/plugins/tcl/signal-test.tcl
+++ b/libpurple/plugins/tcl/signal-test.tcl
@@ -119,5 +119,5 @@ proc plugin_init { } {
"Tests Tcl signal handlers" \
"Debugs a ridiculous amount of signal information." \
"Ethan Blanton <elb@pidgin.im>" \
- "http://www.pidgin.im/"
+ "https://pidgin.im/"
}
diff --git a/libpurple/plugins/tcl/tcl.c b/libpurple/plugins/tcl/tcl.c
index 7f82852a67..eba2161ae3 100644
--- a/libpurple/plugins/tcl/tcl.c
+++ b/libpurple/plugins/tcl/tcl.c
@@ -30,7 +30,6 @@
#include <stdio.h>
#include <sys/types.h>
-#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
@@ -50,6 +49,16 @@ struct tcl_plugin_data {
Tcl_Interp *interp;
};
+typedef struct {
+ char *id;
+ char *name;
+ char *version;
+ char *summary;
+ char *description;
+ char *author;
+ char *homepage;
+} tcl_plugin_info_strings;
+
PurpleStringref *PurpleTclRefAccount;
PurpleStringref *PurpleTclRefConnection;
PurpleStringref *PurpleTclRefConversation;
@@ -68,6 +77,21 @@ PurplePlugin *_tcl_plugin;
static gboolean tcl_loaded = FALSE;
+static void tcl_plugin_info_strings_free(tcl_plugin_info_strings *strings)
+{
+ if (strings == NULL)
+ return;
+
+ g_free(strings->id);
+ g_free(strings->name);
+ g_free(strings->version);
+ g_free(strings->summary);
+ g_free(strings->description);
+ g_free(strings->author);
+ g_free(strings->homepage);
+ g_free(strings);
+}
+
PurplePlugin *tcl_interp_get_plugin(Tcl_Interp *interp)
{
struct tcl_plugin_data *data;
@@ -219,7 +243,9 @@ static gboolean tcl_probe_plugin(PurplePlugin *plugin)
result = Tcl_GetObjResult(interp);
if (Tcl_ListObjGetElements(interp, result, &nelems, &listitems) == TCL_OK) {
if ((nelems == 6) || (nelems == 7)) {
+ tcl_plugin_info_strings *strings = g_new0(tcl_plugin_info_strings, 1);
info = g_new0(PurplePluginInfo, 1);
+ info->extra_info = strings;
info->magic = PURPLE_PLUGIN_MAGIC;
info->major_version = PURPLE_MAJOR_VERSION;
@@ -227,17 +253,17 @@ static gboolean tcl_probe_plugin(PurplePlugin *plugin)
info->type = PURPLE_PLUGIN_STANDARD;
info->dependencies = g_list_append(info->dependencies, "core-tcl");
- info->name = g_strdup(Tcl_GetString(listitems[0]));
- info->version = g_strdup(Tcl_GetString(listitems[1]));
- info->summary = g_strdup(Tcl_GetString(listitems[2]));
- info->description = g_strdup(Tcl_GetString(listitems[3]));
- info->author = g_strdup(Tcl_GetString(listitems[4]));
- info->homepage = g_strdup(Tcl_GetString(listitems[5]));
+ info->name = strings->name = g_strdup(Tcl_GetString(listitems[0]));
+ info->version = strings->version = g_strdup(Tcl_GetString(listitems[1]));
+ info->summary = strings->summary = g_strdup(Tcl_GetString(listitems[2]));
+ info->description = strings->description = g_strdup(Tcl_GetString(listitems[3]));
+ info->author = strings->author = g_strdup(Tcl_GetString(listitems[4]));
+ info->homepage = strings->homepage = g_strdup(Tcl_GetString(listitems[5]));
if (nelems == 6)
- info->id = g_strdup_printf("tcl-%s", Tcl_GetString(listitems[0]));
+ info->id = strings->id = g_strdup_printf("tcl-%s", Tcl_GetString(listitems[0]));
else if (nelems == 7)
- info->id = g_strdup_printf("tcl-%s", Tcl_GetString(listitems[6]));
+ info->id = strings->id = g_strdup_printf("tcl-%s", Tcl_GetString(listitems[6]));
plugin->info = info;
@@ -314,12 +340,9 @@ static gboolean tcl_unload_plugin(PurplePlugin *plugin)
static void tcl_destroy_plugin(PurplePlugin *plugin)
{
if (plugin->info != NULL) {
- g_free(plugin->info->id);
- g_free(plugin->info->name);
- g_free(plugin->info->version);
- g_free(plugin->info->description);
- g_free(plugin->info->author);
- g_free(plugin->info->homepage);
+ tcl_plugin_info_strings *info_strings = plugin->info->extra_info;
+ tcl_plugin_info_strings_free(info_strings);
+ plugin->info->extra_info = NULL;
}
return;
@@ -433,61 +456,21 @@ LPFNTKINIT wtk_Init = NULL;
#define Tk_Init wtk_Init
static gboolean tcl_win32_init() {
- const char regkey[] = "SOFTWARE\\ActiveState\\ActiveTcl\\";
- char *version = NULL;
gboolean retval = FALSE;
- if ((version = wpurple_read_reg_string(HKEY_LOCAL_MACHINE, regkey, "CurrentVersion"))
- || (version = wpurple_read_reg_string(HKEY_CURRENT_USER, regkey, "CurrentVersion"))) {
- char *path = NULL;
- char *regkey2;
- char **tokens;
- int major = 0, minor = 0, micro = 0;
-
- tokens = g_strsplit(version, ".", 0);
- if (tokens[0] && tokens[1] && tokens[2]) {
- major = atoi(tokens[0]);
- minor = atoi(tokens[1]);
- micro = atoi(tokens[2]);
+ if(!(wtcl_CreateInterp = (LPFNTCLCREATEINTERP) wpurple_find_and_loadproc("tcl85.dll", "Tcl_CreateInterp"))) {
+ purple_debug(PURPLE_DEBUG_INFO, "tcl", "tcl_win32_init error loading Tcl_CreateInterp\n");
+ } else {
+ if(!(wtk_Init = (LPFNTKINIT) wpurple_find_and_loadproc("tk85.dll", "Tk_Init"))) {
+ HMODULE mod;
+ purple_debug(PURPLE_DEBUG_INFO, "tcl", "tcl_win32_init error loading Tk_Init\n");
+ if((mod = GetModuleHandle("tcl85.dll")))
+ FreeLibrary(mod);
+ } else {
+ retval = TRUE;
}
- g_strfreev(tokens);
-
- regkey2 = g_strdup_printf("%s%s\\", regkey, version);
- if (!(major == 8 && minor == 4 && micro >= 5))
- purple_debug(PURPLE_DEBUG_INFO, "tcl", "Unsupported ActiveTCL version %s found.\n", version);
- else if ((path = wpurple_read_reg_string(HKEY_LOCAL_MACHINE, regkey2, NULL)) || (path = wpurple_read_reg_string(HKEY_CURRENT_USER, regkey2, NULL))) {
- char *tclpath;
- char *tkpath;
-
- purple_debug(PURPLE_DEBUG_INFO, "tcl", "Loading ActiveTCL version %s from \"%s\"\n", version, path);
-
- tclpath = g_build_filename(path, "bin", "tcl84.dll", NULL);
- tkpath = g_build_filename(path, "bin", "tk84.dll", NULL);
-
- if(!(wtcl_CreateInterp = (LPFNTCLCREATEINTERP) wpurple_find_and_loadproc(tclpath, "Tcl_CreateInterp"))) {
- purple_debug(PURPLE_DEBUG_INFO, "tcl", "tcl_win32_init error loading Tcl_CreateInterp\n");
- } else {
- if(!(wtk_Init = (LPFNTKINIT) wpurple_find_and_loadproc(tkpath, "Tk_Init"))) {
- HMODULE mod;
- purple_debug(PURPLE_DEBUG_INFO, "tcl", "tcl_win32_init error loading Tk_Init\n");
- if((mod = GetModuleHandle("tcl84.dll")))
- FreeLibrary(mod);
- } else {
- retval = TRUE;
- }
- }
- g_free(tclpath);
- g_free(tkpath);
- }
- g_free(path);
- g_free(regkey2);
}
- g_free(version);
-
- if (!retval)
- purple_debug(PURPLE_DEBUG_INFO, "tcl", _("Unable to detect ActiveTCL installation. If you wish to use TCL plugins, install ActiveTCL from http://www.activestate.com\n"));
-
return retval;
}
diff --git a/libpurple/plugins/tcl/tcl_cmds.c b/libpurple/plugins/tcl/tcl_cmds.c
index c7a0b3fe0e..c5e05e3dd7 100644
--- a/libpurple/plugins/tcl/tcl_cmds.c
+++ b/libpurple/plugins/tcl/tcl_cmds.c
@@ -29,10 +29,11 @@
#include "account.h"
#include "server.h"
#include "notify.h"
-#include "blist.h"
+#include "buddylist.h"
#include "savedstatuses.h"
#include "debug.h"
#include "prefs.h"
+#include "presence.h"
#include "core.h"
#include "tcl_purple.h"
@@ -70,7 +71,7 @@ static PurpleConversation *tcl_validate_conversation(Tcl_Obj *obj, Tcl_Interp *i
if (convo == NULL)
return NULL;
- for (cur = purple_get_conversations(); cur != NULL; cur = g_list_next(cur)) {
+ for (cur = purple_conversations_get_all(); cur != NULL; cur = g_list_next(cur)) {
if (convo == cur->data)
return convo;
}
@@ -118,7 +119,7 @@ int tcl_cmd_account(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CO
PurpleAccount *account;
PurpleStatus *status;
PurpleStatusType *status_type;
- PurpleValue *value;
+ GValue *value;
char *attr_id;
int error;
int b, i;
@@ -139,7 +140,7 @@ int tcl_cmd_account(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CO
}
if ((account = tcl_validate_account(objv[2], interp)) == NULL)
return TCL_ERROR;
- alias = purple_account_get_alias(account);
+ alias = purple_account_get_private_alias(account);
Tcl_SetObjResult(interp, Tcl_NewStringObj(alias ? (char *)alias : "", -1));
break;
case CMD_ACCOUNT_CONNECT:
@@ -292,27 +293,27 @@ int tcl_cmd_account(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CO
Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid attribute for account", -1));
return TCL_ERROR;
}
- switch (purple_value_get_type(value)) {
- case PURPLE_TYPE_BOOLEAN:
+ switch (G_VALUE_TYPE(value)) {
+ case G_TYPE_BOOLEAN:
error = Tcl_GetBooleanFromObj(interp, objv[i + 1], &b);
if (error != TCL_OK)
return error;
l = g_list_append(l, attr_id);
l = g_list_append(l, GINT_TO_POINTER(b));
break;
- case PURPLE_TYPE_INT:
+ case G_TYPE_INT:
error = Tcl_GetIntFromObj(interp, objv[i + 1], &b);
if (error != TCL_OK)
return error;
l = g_list_append(l, attr_id);
l = g_list_append(l, GINT_TO_POINTER(b));
break;
- case PURPLE_TYPE_STRING:
+ case G_TYPE_STRING:
l = g_list_append(l, attr_id);
l = g_list_append(l, Tcl_GetString(objv[i + 1]));
break;
default:
- Tcl_SetObjResult(interp, Tcl_NewStringObj("unknown PurpleValue type", -1));
+ Tcl_SetObjResult(interp, Tcl_NewStringObj("unknown GValue type", -1));
return TCL_ERROR;
}
}
@@ -401,7 +402,7 @@ static PurpleBlistNode *tcl_list_to_buddy(Tcl_Interp *interp, int count, Tcl_Obj
return NULL;
if (!strcmp(type, "buddy")) {
- node = PURPLE_BLIST_NODE(purple_find_buddy(account, name));
+ node = PURPLE_BLIST_NODE(purple_blist_find_buddy(account, name));
} else if (!strcmp(type, "group")) {
node = PURPLE_BLIST_NODE(purple_blist_find_chat(account, name));
}
@@ -414,7 +415,6 @@ int tcl_cmd_buddy(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONS
Tcl_Obj *list, *tclgroup, *tclgrouplist, *tclcontact, *tclcontactlist, *tclbud, **elems, *result;
const char *cmds[] = { "alias", "handle", "info", "list", NULL };
enum { CMD_BUDDY_ALIAS, CMD_BUDDY_HANDLE, CMD_BUDDY_INFO, CMD_BUDDY_LIST } cmd;
- PurpleBlistNodeType type;
PurpleBlistNode *node, *gnode, *bnode;
PurpleAccount *account;
PurpleBuddy *bud;
@@ -438,13 +438,12 @@ int tcl_cmd_buddy(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONS
return error;
if ((node = tcl_list_to_buddy(interp, count, elems)) == NULL)
return TCL_ERROR;
- type = purple_blist_node_get_type(node);
- if (type == PURPLE_BLIST_CHAT_NODE)
+ if (PURPLE_IS_CHAT(node))
Tcl_SetObjResult(interp,
- Tcl_NewStringObj(purple_chat_get_name((PurpleChat *)node), -1));
- else if (type == PURPLE_BLIST_BUDDY_NODE)
+ Tcl_NewStringObj(purple_chat_get_name(PURPLE_CHAT(node)), -1));
+ else if (PURPLE_IS_BUDDY(node))
Tcl_SetObjResult(interp,
- Tcl_NewStringObj((char *)purple_buddy_get_alias((PurpleBuddy *)node), -1));
+ Tcl_NewStringObj((char *)purple_buddy_get_alias(PURPLE_BUDDY(node)), -1));
return TCL_OK;
break;
case CMD_BUDDY_HANDLE:
@@ -476,11 +475,11 @@ int tcl_cmd_buddy(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONS
}
if ((account = tcl_validate_account(elems[2], interp)) == NULL)
return TCL_ERROR;
- serv_get_info(purple_account_get_connection(account), Tcl_GetString(elems[1]));
+ purple_serv_get_info(purple_account_get_connection(account), Tcl_GetString(elems[1]));
} else {
if ((account = tcl_validate_account(objv[2], interp)) == NULL)
return TCL_ERROR;
- serv_get_info(purple_account_get_connection(account), Tcl_GetString(objv[3]));
+ purple_serv_get_info(purple_account_get_connection(account), Tcl_GetString(objv[3]));
}
break;
case CMD_BUDDY_LIST:
@@ -499,14 +498,12 @@ int tcl_cmd_buddy(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONS
tclgroup = Tcl_NewListObj(0, NULL);
Tcl_ListObjAppendElement(interp, tclgroup, Tcl_NewStringObj("group", -1));
Tcl_ListObjAppendElement(interp, tclgroup,
- Tcl_NewStringObj(purple_group_get_name((PurpleGroup *)gnode), -1));
+ Tcl_NewStringObj(purple_group_get_name(PURPLE_GROUP(gnode)), -1));
tclgrouplist = Tcl_NewListObj(0, NULL);
for (node = purple_blist_node_get_first_child(gnode); node != NULL; node = purple_blist_node_get_sibling_next(node)) {
PurpleAccount *account;
- type = purple_blist_node_get_type(node);
- switch (type) {
- case PURPLE_BLIST_CONTACT_NODE:
+ if (PURPLE_IS_CONTACT(node)) {
tclcontact = Tcl_NewListObj(0, NULL);
Tcl_IncrRefCount(tclcontact);
Tcl_ListObjAppendElement(interp, tclcontact, Tcl_NewStringObj("contact", -1));
@@ -514,9 +511,9 @@ int tcl_cmd_buddy(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONS
Tcl_IncrRefCount(tclcontactlist);
count = 0;
for (bnode = purple_blist_node_get_first_child(node); bnode != NULL; bnode = purple_blist_node_get_sibling_next(bnode)) {
- if (purple_blist_node_get_type(bnode) != PURPLE_BLIST_BUDDY_NODE)
+ if (!PURPLE_IS_BUDDY(bnode))
continue;
- bud = (PurpleBuddy *)bnode;
+ bud = PURPLE_BUDDY(bnode);
account = purple_buddy_get_account(bud);
if (!all && !purple_account_is_connected(account))
continue;
@@ -533,9 +530,8 @@ int tcl_cmd_buddy(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONS
}
Tcl_DecrRefCount(tclcontact);
Tcl_DecrRefCount(tclcontactlist);
- break;
- case PURPLE_BLIST_CHAT_NODE:
- cnode = (PurpleChat *)node;
+ } else if (PURPLE_IS_CHAT(node)) {
+ cnode = PURPLE_CHAT(node);
account = purple_chat_get_account(cnode);
if (!all && !purple_account_is_connected(account))
continue;
@@ -544,9 +540,8 @@ int tcl_cmd_buddy(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONS
Tcl_ListObjAppendElement(interp, tclbud, Tcl_NewStringObj(purple_chat_get_name(cnode), -1));
Tcl_ListObjAppendElement(interp, tclbud, purple_tcl_ref_new(PurpleTclRefAccount, account));
Tcl_ListObjAppendElement(interp, tclgrouplist, tclbud);
- break;
- default:
- purple_debug(PURPLE_DEBUG_WARNING, "tcl", "Unexpected buddy type %d", type);
+ } else {
+ purple_debug(PURPLE_DEBUG_WARNING, "tcl", "Unexpected buddy type %s", G_OBJECT_TYPE_NAME(node));
continue;
}
}
@@ -748,13 +743,13 @@ int tcl_cmd_connection(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj
if ((gc = tcl_validate_gc(objv[2], interp)) == NULL)
return TCL_ERROR;
switch (purple_connection_get_state(gc)) {
- case PURPLE_DISCONNECTED:
+ case PURPLE_CONNECTION_DISCONNECTED:
Tcl_SetObjResult(interp, Tcl_NewStringObj("disconnected", -1));
break;
- case PURPLE_CONNECTED:
+ case PURPLE_CONNECTION_CONNECTED:
Tcl_SetObjResult(interp, Tcl_NewStringObj("connected", -1));
break;
- case PURPLE_CONNECTING:
+ case PURPLE_CONNECTION_CONNECTING:
Tcl_SetObjResult(interp, Tcl_NewStringObj("connecting", -1));
break;
}
@@ -775,10 +770,11 @@ int tcl_cmd_conversation(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Ob
enum { CMD_CONV_NEW_CHAT, CMD_CONV_NEW_IM } newopt;
PurpleConversation *convo;
PurpleAccount *account;
- PurpleConversationType type;
+ PurpleMessage *pmsg;
+ gboolean is_chat = FALSE;
GList *cur;
char *opt, *from, *what;
- int error, argsused, flags = 0;
+ int error, argsused;
if (objc < 2) {
Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?args?");
@@ -797,8 +793,7 @@ int tcl_cmd_conversation(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Ob
account = NULL;
if ((account = tcl_validate_account(objv[2], interp)) == NULL)
return TCL_ERROR;
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
- Tcl_GetString(objv[3]),
+ convo = purple_conversations_find_with_account(Tcl_GetString(objv[3]),
account);
Tcl_SetObjResult(interp, purple_tcl_ref_new(PurpleTclRefConversation, convo));
break;
@@ -813,7 +808,7 @@ int tcl_cmd_conversation(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Ob
break;
case CMD_CONV_LIST:
list = Tcl_NewListObj(0, NULL);
- for (cur = purple_get_conversations(); cur != NULL; cur = g_list_next(cur)) {
+ for (cur = purple_conversations_get_all(); cur != NULL; cur = g_list_next(cur)) {
elem = purple_tcl_ref_new(PurpleTclRefConversation, cur->data);
Tcl_ListObjAppendElement(interp, list, elem);
}
@@ -825,7 +820,7 @@ int tcl_cmd_conversation(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Ob
return TCL_ERROR;
}
argsused = 2;
- type = PURPLE_CONV_TYPE_IM;
+ is_chat = FALSE;
while (argsused < objc) {
opt = Tcl_GetString(objv[argsused]);
if (*opt == '-') {
@@ -835,10 +830,10 @@ int tcl_cmd_conversation(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Ob
argsused++;
switch (newopt) {
case CMD_CONV_NEW_CHAT:
- type = PURPLE_CONV_TYPE_CHAT;
+ is_chat = TRUE;
break;
case CMD_CONV_NEW_IM:
- type = PURPLE_CONV_TYPE_IM;
+ is_chat = FALSE;
break;
}
} else {
@@ -851,7 +846,10 @@ int tcl_cmd_conversation(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Ob
}
if ((account = tcl_validate_account(objv[argsused++], interp)) == NULL)
return TCL_ERROR;
- convo = purple_conversation_new(type, account, Tcl_GetString(objv[argsused]));
+ if (is_chat)
+ convo = PURPLE_CONVERSATION(purple_chat_conversation_new(account, Tcl_GetString(objv[argsused])));
+ else
+ convo = PURPLE_CONVERSATION(purple_im_conversation_new(account, Tcl_GetString(objv[argsused])));
Tcl_SetObjResult(interp, purple_tcl_ref_new(PurpleTclRefConversation, convo));
break;
case CMD_CONV_WRITE:
@@ -868,19 +866,17 @@ int tcl_cmd_conversation(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Ob
switch (style) {
case CMD_CONV_WRITE_SEND:
- flags = PURPLE_MESSAGE_SEND;
+ pmsg = purple_message_new_outgoing(from, what, 0);
break;
case CMD_CONV_WRITE_RECV:
- flags = PURPLE_MESSAGE_RECV;
+ pmsg = purple_message_new_incoming(from, what, 0, 0);
break;
case CMD_CONV_WRITE_SYSTEM:
- flags = PURPLE_MESSAGE_SYSTEM;
+ default:
+ pmsg = purple_message_new_system(what, 0);
break;
}
- if (purple_conversation_get_type(convo) == PURPLE_CONV_TYPE_CHAT)
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), from, what, flags, time(NULL));
- else
- purple_conv_im_write(PURPLE_CONV_IM(convo), from, what, flags, time(NULL));
+ purple_conversation_write_message(convo, pmsg);
break;
case CMD_CONV_NAME:
if (objc != 3) {
@@ -912,10 +908,7 @@ int tcl_cmd_conversation(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Ob
if ((convo = tcl_validate_conversation(objv[2], interp)) == NULL)
return TCL_ERROR;
what = Tcl_GetString(objv[3]);
- if (purple_conversation_get_type(convo) == PURPLE_CONV_TYPE_CHAT)
- purple_conv_chat_send(PURPLE_CONV_CHAT(convo), what);
- else
- purple_conv_im_send(PURPLE_CONV_IM(convo), what);
+ purple_conversation_send(convo, what);
break;
}
@@ -1009,7 +1002,7 @@ int tcl_cmd_notify(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CON
msg2 = Tcl_GetString(objv[4]);
}
- purple_notify_message(_tcl_plugin, optind[type], title, msg1, msg2, NULL, NULL);
+ purple_notify_message(_tcl_plugin, optind[type], title, msg1, msg2, NULL, NULL, NULL);
return TCL_OK;
}
@@ -1068,7 +1061,7 @@ int tcl_cmd_prefs(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONS
Tcl_WrongNumArgs(interp, 1, objv, "path");
return TCL_ERROR;
}
- preftype = purple_prefs_get_type(Tcl_GetString(objv[2]));
+ preftype = purple_prefs_get_pref_type(Tcl_GetString(objv[2]));
switch (preftype) {
case PURPLE_PREF_NONE:
Tcl_SetObjResult(interp,
@@ -1109,7 +1102,7 @@ int tcl_cmd_prefs(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONS
Tcl_WrongNumArgs(interp, 1, objv, "path value");
return TCL_ERROR;
}
- preftype = purple_prefs_get_type(Tcl_GetString(objv[2]));
+ preftype = purple_prefs_get_pref_type(Tcl_GetString(objv[2]));
switch (preftype) {
case PURPLE_PREF_NONE:
Tcl_SetObjResult(interp,
@@ -1149,7 +1142,7 @@ int tcl_cmd_prefs(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONS
Tcl_WrongNumArgs(interp, 1, objv, "path");
return TCL_ERROR;
}
- preftype = purple_prefs_get_type(Tcl_GetString(objv[2]));
+ preftype = purple_prefs_get_pref_type(Tcl_GetString(objv[2]));
switch (preftype) {
case PURPLE_PREF_NONE:
Tcl_SetObjResult(interp, Tcl_NewStringObj("none", -1));
@@ -1179,12 +1172,11 @@ int tcl_cmd_prefs(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONS
int tcl_cmd_presence(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
const char *cmds[] = { "account", "active_status", "available",
- "chat_user", "context", "conversation", "idle",
- "login", "online", "status", "statuses", NULL };
+ "idle", "type", "login", "online", "status",
+ "statuses", NULL };
enum { CMD_PRESENCE_ACCOUNT, CMD_PRESENCE_ACTIVE_STATUS,
- CMD_PRESENCE_AVAILABLE, CMD_PRESENCE_CHAT_USER,
- CMD_PRESENCE_CONTEXT, CMD_PRESENCE_CONVERSATION,
- CMD_PRESENCE_IDLE, CMD_PRESENCE_LOGIN, CMD_PRESENCE_ONLINE,
+ CMD_PRESENCE_AVAILABLE, CMD_PRESENCE_IDLE, CMD_PRESENCE_TYPE,
+ CMD_PRESENCE_LOGIN, CMD_PRESENCE_ONLINE,
CMD_PRESENCE_STATUS, CMD_PRESENCE_STATUSES } cmd;
Tcl_Obj *result;
Tcl_Obj *list, *elem;
@@ -1209,7 +1201,7 @@ int tcl_cmd_presence(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *C
if ((presence = purple_tcl_ref_get(interp, objv[2], PurpleTclRefPresence)) == NULL)
return TCL_ERROR;
Tcl_SetObjResult(interp, purple_tcl_ref_new(PurpleTclRefAccount,
- purple_presence_get_account(presence)));
+ purple_account_presence_get_account(PURPLE_ACCOUNT_PRESENCE(presence))));
break;
case CMD_PRESENCE_ACTIVE_STATUS:
if (objc != 3 && objc != 4 && objc != 5) {
@@ -1260,47 +1252,17 @@ int tcl_cmd_presence(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *C
Tcl_SetObjResult(interp,
Tcl_NewBooleanObj(purple_presence_is_available(presence)));
break;
- case CMD_PRESENCE_CHAT_USER:
+ case CMD_PRESENCE_TYPE:
if (objc != 3) {
Tcl_WrongNumArgs(interp, 2, objv, "presence");
return TCL_ERROR;
}
if ((presence = purple_tcl_ref_get(interp, objv[2], PurpleTclRefPresence)) == NULL)
return TCL_ERROR;
- Tcl_SetObjResult(interp,
- Tcl_NewStringObj(purple_presence_get_chat_user(presence), -1));
- break;
- case CMD_PRESENCE_CONTEXT:
- if (objc != 3) {
- Tcl_WrongNumArgs(interp, 2, objv, "presence");
- return TCL_ERROR;
- }
- if ((presence = purple_tcl_ref_get(interp, objv[2], PurpleTclRefPresence)) == NULL)
- return TCL_ERROR;
- switch (purple_presence_get_context(presence)) {
- case PURPLE_PRESENCE_CONTEXT_UNSET:
- Tcl_SetObjResult(interp, Tcl_NewStringObj("unset", -1));
- break;
- case PURPLE_PRESENCE_CONTEXT_ACCOUNT:
+ if (PURPLE_IS_ACCOUNT_PRESENCE(presence))
Tcl_SetObjResult(interp, Tcl_NewStringObj("account", -1));
- break;
- case PURPLE_PRESENCE_CONTEXT_CONV:
- Tcl_SetObjResult(interp, Tcl_NewStringObj("conversation", -1));
- break;
- case PURPLE_PRESENCE_CONTEXT_BUDDY:
+ else if (PURPLE_IS_BUDDY_PRESENCE(presence))
Tcl_SetObjResult(interp, Tcl_NewStringObj("buddy", -1));
- break;
- }
- break;
- case CMD_PRESENCE_CONVERSATION:
- if (objc != 3) {
- Tcl_WrongNumArgs(interp, 2, objv, "presence");
- return TCL_ERROR;
- }
- if ((presence = purple_tcl_ref_get(interp, objv[2], PurpleTclRefPresence)) == NULL)
- return TCL_ERROR;
- Tcl_SetObjResult(interp, purple_tcl_ref_new(PurpleTclRefConversation,
- purple_presence_get_conversation(presence)));
break;
case CMD_PRESENCE_IDLE:
if (objc < 3 || objc > 5) {
@@ -1417,7 +1379,7 @@ int tcl_cmd_savedstatus(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj
return TCL_ERROR;
result = Tcl_NewListObj(0, NULL);
Tcl_ListObjAppendElement(interp, result, Tcl_NewStringObj(purple_savedstatus_get_title(saved_status), -1));
- Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(purple_savedstatus_get_type(saved_status)));
+ Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(purple_savedstatus_get_primitive_type(saved_status)));
Tcl_ListObjAppendElement(interp, result, Tcl_NewStringObj(purple_savedstatus_get_message(saved_status), -1));
Tcl_SetObjResult(interp,result);
break;
@@ -1451,7 +1413,7 @@ int tcl_cmd_send_im(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CO
who = Tcl_GetString(objv[2]);
text = Tcl_GetString(objv[3]);
- serv_send_im(gc, who, text, 0);
+ purple_serv_send_im(gc, purple_message_new_outgoing(who, text, 0));
return TCL_OK;
}
@@ -1512,11 +1474,12 @@ int tcl_cmd_signal(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CON
int tcl_cmd_status(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
const char *cmds[] = { "attr", "type", NULL };
- enum { CMD_STATUS_ATTR, CMD_STATUS_TYPE } cmd;
+ enum { CMD_STATUS_ATTRIBUTE, CMD_STATUS_TYPE } cmd;
PurpleStatus *status;
PurpleStatusType *status_type;
int error;
-#if !(defined PURPLE_DISABLE_DEPRECATED)
+# if (0)
+/* #if !(defined PURPLE_DISABLE_DEPRECATED) */
PurpleValue *value;
const char *attr;
int v;
@@ -1531,8 +1494,9 @@ int tcl_cmd_status(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CON
return error;
switch (cmd) {
- case CMD_STATUS_ATTR:
-#if !(defined PURPLE_DISABLE_DEPRECATED)
+ case CMD_STATUS_ATTRIBUTE:
+# if (0)
+/* #if !(defined PURPLE_DISABLE_DEPRECATED) */
if (objc != 4 && objc != 5) {
Tcl_WrongNumArgs(interp, 2, objv, "status attr_id ?value?");
return TCL_ERROR;
@@ -1587,7 +1551,7 @@ int tcl_cmd_status(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CON
}
if ((status = purple_tcl_ref_get(interp, objv[2], PurpleTclRefStatus)) == NULL)
return TCL_ERROR;
- status_type = purple_status_get_type(status);
+ status_type = purple_status_get_status_type(status);
Tcl_SetObjResult(interp, purple_tcl_ref_new(PurpleTclRefStatusType,
status_type));
break;
@@ -1599,8 +1563,8 @@ int tcl_cmd_status(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CON
int tcl_cmd_status_attr(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
const char *cmds[] = { "id", "name", NULL };
- enum { CMD_STATUS_ATTR_ID, CMD_STATUS_ATTR_NAME } cmd;
- PurpleStatusAttr *attr;
+ enum { CMD_STATUS_ATTRIBUTE_ID, CMD_STATUS_ATTRIBUTE_NAME } cmd;
+ PurpleStatusAttribute *attr;
int error;
if (objc < 2) {
@@ -1612,7 +1576,7 @@ int tcl_cmd_status_attr(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj
return error;
switch (cmd) {
- case CMD_STATUS_ATTR_ID:
+ case CMD_STATUS_ATTRIBUTE_ID:
if (objc != 3) {
Tcl_WrongNumArgs(interp, 2, objv, "attr");
return TCL_ERROR;
@@ -1620,9 +1584,9 @@ int tcl_cmd_status_attr(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj
if ((attr = purple_tcl_ref_get(interp, objv[2], PurpleTclRefStatusAttr)) == NULL)
return TCL_ERROR;
Tcl_SetObjResult(interp,
- Tcl_NewStringObj(purple_status_attr_get_id(attr), -1));
+ Tcl_NewStringObj(purple_status_attribute_get_id(attr), -1));
break;
- case CMD_STATUS_ATTR_NAME:
+ case CMD_STATUS_ATTRIBUTE_NAME:
if (objc != 3) {
Tcl_WrongNumArgs(interp, 2, objv, "attr");
return TCL_ERROR;
@@ -1630,7 +1594,7 @@ int tcl_cmd_status_attr(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj
if ((attr = purple_tcl_ref_get(interp, objv[2], PurpleTclRefStatusAttr)) == NULL)
return TCL_ERROR;
Tcl_SetObjResult(interp,
- Tcl_NewStringObj(purple_status_attr_get_name(attr), -1));
+ Tcl_NewStringObj(purple_status_attribute_get_name(attr), -1));
break;
}
@@ -1640,13 +1604,13 @@ int tcl_cmd_status_attr(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj
int tcl_cmd_status_type(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
const char *cmds[] = { "attr", "attrs", "available", "exclusive", "id",
- "independent", "name", "primary_attr",
+ "independent", "name",
"primitive", "saveable", "user_settable",
NULL };
enum { CMD_STATUS_TYPE_ATTR, CMD_STATUS_TYPE_ATTRS,
CMD_STATUS_TYPE_AVAILABLE, CMD_STATUS_TYPE_EXCLUSIVE,
CMD_STATUS_TYPE_ID, CMD_STATUS_TYPE_INDEPENDENT,
- CMD_STATUS_TYPE_NAME, CMD_STATUS_TYPE_PRIMARY_ATTR,
+ CMD_STATUS_TYPE_NAME,
CMD_STATUS_TYPE_PRIMITIVE, CMD_STATUS_TYPE_SAVEABLE,
CMD_STATUS_TYPE_USER_SETTABLE } cmd;
PurpleStatusType *status_type;
@@ -1751,18 +1715,6 @@ int tcl_cmd_status_type(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj
Tcl_NewStringObj(purple_primitive_get_id_from_type
(purple_status_type_get_primitive(status_type)), -1));
break;
- case CMD_STATUS_TYPE_PRIMARY_ATTR:
-#if !(defined PURPLE_DISABLE_DEPRECATED)
- if (objc != 3) {
- Tcl_WrongNumArgs(interp, 2, objv, "statustype");
- return TCL_ERROR;
- }
- if ((status_type = purple_tcl_ref_get(interp, objv[2], PurpleTclRefStatusType)) == NULL)
- return TCL_ERROR;
- Tcl_SetObjResult(interp,
- Tcl_NewStringObj(purple_status_type_get_primary_attr(status_type), -1));
-#endif
- break;
case CMD_STATUS_TYPE_SAVEABLE:
if (objc != 3) {
Tcl_WrongNumArgs(interp, 2, objv, "statustype");
diff --git a/libpurple/plugins/tcl/tcl_glib.c b/libpurple/plugins/tcl/tcl_glib.c
index c169e1a521..91e45980b6 100644
--- a/libpurple/plugins/tcl/tcl_glib.c
+++ b/libpurple/plugins/tcl/tcl_glib.c
@@ -224,7 +224,8 @@ static gboolean tcl_file_callback(GIOChannel *source, GIOCondition condition, gp
return TRUE;
tfh->pending |= mask;
- fev = (struct tcl_file_event *)ckalloc(sizeof(struct tcl_file_event));
+ /* ckalloc returns memory "suitably aligned for any use" */
+ fev = (gpointer)ckalloc(sizeof(struct tcl_file_event));
memset(fev, 0, sizeof(struct tcl_file_event));
fev->header.proc = tcl_file_event_callback;
fev->fd = tfh->fd;
diff --git a/libpurple/plugins/tcl/tcl_purple.h b/libpurple/plugins/tcl/tcl_purple.h
index 5e1d376ee1..6662d11aee 100644
--- a/libpurple/plugins/tcl/tcl_purple.h
+++ b/libpurple/plugins/tcl/tcl_purple.h
@@ -28,7 +28,6 @@
#include "internal.h"
#include "cmds.h"
#include "plugin.h"
-#include "value.h"
#include "stringref.h"
struct tcl_signal_handler {
@@ -41,13 +40,13 @@ struct tcl_signal_handler {
Tcl_Obj *args;
Tcl_Obj *proc;
- PurpleValue *returntype;
+ GType returntype;
int nargs;
- PurpleValue **argtypes;
+ GType *argtypes;
};
struct tcl_cmd_handler {
- int id;
+ PurpleCmdId id;
Tcl_Obj *cmd;
Tcl_Interp *interp;
diff --git a/libpurple/plugins/tcl/tcl_ref.c b/libpurple/plugins/tcl/tcl_ref.c
index 6189750b9f..e51650cd21 100644
--- a/libpurple/plugins/tcl/tcl_ref.c
+++ b/libpurple/plugins/tcl/tcl_ref.c
@@ -117,12 +117,12 @@ static int purple_tcl_ref_set(Tcl_Interp *interp, Tcl_Obj *obj)
PurpleStringref *type;
void *value;
static const char prefix[] = "purple-";
- static const int prefixlen = sizeof(prefix) - 1;
+ static const gsize prefixlen = sizeof(prefix) - 1;
if (strlen(bytes) < prefixlen
|| strncmp(bytes, prefix, prefixlen)
|| (ptr = strchr(bytes, ':')) == NULL
- || (ptr - bytes) == prefixlen)
+ || (gsize)(ptr - bytes) == prefixlen)
goto badobject;
/* Bad Ethan */
diff --git a/libpurple/plugins/tcl/tcl_signals.c b/libpurple/plugins/tcl/tcl_signals.c
index c0895905f6..f83d1b2d9f 100644
--- a/libpurple/plugins/tcl/tcl_signals.c
+++ b/libpurple/plugins/tcl/tcl_signals.c
@@ -29,7 +29,6 @@
#include "conversation.h"
#include "signals.h"
#include "debug.h"
-#include "value.h"
#include "core.h"
static GList *tcl_callbacks;
@@ -74,7 +73,7 @@ gboolean tcl_signal_connect(struct tcl_signal_handler *handler)
{
GString *proc;
- purple_signal_get_values(handler->instance,
+ purple_signal_get_types(handler->instance,
Tcl_GetString(handler->signal),
&handler->returntype, &handler->nargs,
&handler->argtypes);
@@ -136,24 +135,22 @@ void tcl_signal_disconnect(void *instance, const char *signal, Tcl_Interp *inter
tcl_callbacks = g_list_remove_all(tcl_callbacks, NULL);
}
-static PurpleStringref *ref_type(PurpleSubType type)
+static PurpleStringref *ref_purple_type(GType type)
{
- switch (type) {
- case PURPLE_SUBTYPE_ACCOUNT:
+ if (type == PURPLE_TYPE_ACCOUNT)
return PurpleTclRefAccount;
- case PURPLE_SUBTYPE_CONNECTION:
+ else if (type == PURPLE_TYPE_CONNECTION)
return PurpleTclRefConnection;
- case PURPLE_SUBTYPE_CONVERSATION:
+ else if (type == PURPLE_TYPE_CONVERSATION)
return PurpleTclRefConversation;
- case PURPLE_SUBTYPE_PLUGIN:
+ else if (type == PURPLE_TYPE_PLUGIN)
return PurpleTclRefPlugin;
- case PURPLE_SUBTYPE_STATUS:
+ else if (type == PURPLE_TYPE_STATUS)
return PurpleTclRefStatus;
- case PURPLE_SUBTYPE_XFER:
+ else if (type == PURPLE_TYPE_XFER)
return PurpleTclRefXfer;
- default:
+ else
return NULL;
- }
}
static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handler)
@@ -179,59 +176,57 @@ static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handle
Tcl_ListObjAppendElement(handler->interp, cmd, arg);
for (i = 0; i < handler->nargs; i++) {
+#if 0
if (purple_value_is_outgoing(handler->argtypes[i]))
g_string_printf(name, "%s::arg%d",
Tcl_GetString(handler->namespace), i);
-
- switch(purple_value_get_type(handler->argtypes[i])) {
- case PURPLE_TYPE_UNKNOWN: /* What? I guess just pass the word ... */
- /* treat this as a pointer, but complain first */
- purple_debug(PURPLE_DEBUG_ERROR, "tcl", "unknown PurpleValue type %d\n",
- purple_value_get_type(handler->argtypes[i]));
- /* fall through */
- case PURPLE_TYPE_POINTER:
- case PURPLE_TYPE_OBJECT:
- case PURPLE_TYPE_BOXED:
+#endif
+ switch(handler->argtypes[i]) {
+ case G_TYPE_POINTER:
+#if 0
+ case G_TYPE_OBJECT:
+ case G_TYPE_BOXED:
/* These are all "pointer" types to us */
if (purple_value_is_outgoing(handler->argtypes[i]))
purple_debug_error("tcl", "pointer types do not currently support outgoing arguments\n");
+#endif
arg = purple_tcl_ref_new(PurpleTclRefPointer, va_arg(args, void *));
break;
- case PURPLE_TYPE_BOOLEAN:
+ case G_TYPE_BOOLEAN:
+#if 0
if (purple_value_is_outgoing(handler->argtypes[i])) {
vals[i] = va_arg(args, gboolean *);
Tcl_LinkVar(handler->interp, name->str,
(char *)&vals[i], TCL_LINK_BOOLEAN);
arg = Tcl_NewStringObj(name->str, -1);
- } else {
- arg = Tcl_NewBooleanObj(va_arg(args, gboolean));
- }
+ } else
+#endif
+ arg = Tcl_NewBooleanObj(va_arg(args, gboolean));
break;
- case PURPLE_TYPE_CHAR:
- case PURPLE_TYPE_UCHAR:
- case PURPLE_TYPE_SHORT:
- case PURPLE_TYPE_USHORT:
- case PURPLE_TYPE_INT:
- case PURPLE_TYPE_UINT:
- case PURPLE_TYPE_LONG:
- case PURPLE_TYPE_ULONG:
- case PURPLE_TYPE_ENUM:
+ case G_TYPE_CHAR:
+ case G_TYPE_UCHAR:
+ case G_TYPE_INT:
+ case G_TYPE_UINT:
+ case G_TYPE_LONG:
+ case G_TYPE_ULONG:
/* I should really cast these individually to
* preserve as much information as possible ...
* but heh */
+#if 0
if (purple_value_is_outgoing(handler->argtypes[i])) {
vals[i] = va_arg(args, int *);
Tcl_LinkVar(handler->interp, name->str,
vals[i], TCL_LINK_INT);
arg = Tcl_NewStringObj(name->str, -1);
- } else {
- arg = Tcl_NewIntObj(va_arg(args, int));
- }
+ } else
+#endif
+ arg = Tcl_NewIntObj(va_arg(args, int));
break;
- case PURPLE_TYPE_INT64:
- case PURPLE_TYPE_UINT64:
+ case G_TYPE_INT64:
+ case G_TYPE_UINT64:
/* Tcl < 8.4 doesn't have wide ints, so we have ugly
* ifdefs in here */
+#if 0
if (purple_value_is_outgoing(handler->argtypes[i])) {
vals[i] = (void *)va_arg(args, gint64 *);
#if (TCL_MAJOR_VERSION >= 8 && TCL_MINOR_VERSION >= 4)
@@ -246,14 +241,15 @@ static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handle
#endif /* Tcl >= 8.4 */
arg = Tcl_NewStringObj(name->str, -1);
} else {
- #if (TCL_MAJOR_VERSION >= 8 && TCL_MINOR_VERSION >= 4)
- arg = Tcl_NewWideIntObj(va_arg(args, gint64));
- #else
- arg = Tcl_NewIntObj((int)va_arg(args, int));
- #endif /* Tcl >= 8.4 */
- }
+#endif
+ #if (TCL_MAJOR_VERSION >= 8 && TCL_MINOR_VERSION >= 4)
+ arg = Tcl_NewWideIntObj(va_arg(args, gint64));
+ #else
+ arg = Tcl_NewIntObj((int)va_arg(args, int));
+ #endif /* Tcl >= 8.4 */
break;
- case PURPLE_TYPE_STRING:
+ case G_TYPE_STRING:
+#if 0
if (purple_value_is_outgoing(handler->argtypes[i])) {
strs[i] = va_arg(args, char **);
if (strs[i] == NULL || *strs[i] == NULL) {
@@ -267,71 +263,77 @@ static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handle
Tcl_LinkVar(handler->interp, name->str,
(char *)&vals[i], TCL_LINK_STRING);
arg = Tcl_NewStringObj(name->str, -1);
- } else {
- arg = Tcl_NewStringObj(va_arg(args, char *), -1);
- }
+ } else
+#endif
+ arg = Tcl_NewStringObj(va_arg(args, char *), -1);
break;
- case PURPLE_TYPE_SUBTYPE:
- switch (purple_value_get_subtype(handler->argtypes[i])) {
- case PURPLE_SUBTYPE_UNKNOWN:
- purple_debug(PURPLE_DEBUG_ERROR, "tcl", "subtype unknown\n");
- /* fall through */
- case PURPLE_SUBTYPE_ACCOUNT:
- case PURPLE_SUBTYPE_CONNECTION:
- case PURPLE_SUBTYPE_CONVERSATION:
- case PURPLE_SUBTYPE_STATUS:
- case PURPLE_SUBTYPE_PLUGIN:
- case PURPLE_SUBTYPE_XFER:
+ default:
+ if (handler->argtypes[i] == PURPLE_TYPE_ACCOUNT ||
+ handler->argtypes[i] == PURPLE_TYPE_CONNECTION ||
+ handler->argtypes[i] == PURPLE_TYPE_CONVERSATION ||
+ handler->argtypes[i] == PURPLE_TYPE_STATUS ||
+ handler->argtypes[i] == PURPLE_TYPE_PLUGIN ||
+ handler->argtypes[i] == PURPLE_TYPE_XFER )
+ {
+#if 0
if (purple_value_is_outgoing(handler->argtypes[i]))
purple_debug_error("tcl", "pointer subtypes do not currently support outgoing arguments\n");
- arg = purple_tcl_ref_new(ref_type(purple_value_get_subtype(handler->argtypes[i])), va_arg(args, void *));
- break;
- case PURPLE_SUBTYPE_BLIST:
- case PURPLE_SUBTYPE_BLIST_BUDDY:
- case PURPLE_SUBTYPE_BLIST_GROUP:
- case PURPLE_SUBTYPE_BLIST_CHAT:
+#endif
+ arg = purple_tcl_ref_new(ref_purple_type(handler->argtypes[i]), va_arg(args, void *));
+ }
+ else
+ if (handler->argtypes[i] == PURPLE_TYPE_CONTACT ||
+ handler->argtypes[i] == PURPLE_TYPE_BUDDY ||
+ handler->argtypes[i] == PURPLE_TYPE_GROUP ||
+ handler->argtypes[i] == PURPLE_TYPE_CHAT )
+ {
/* We're going to switch again for code-deduping */
+#if 0
if (purple_value_is_outgoing(handler->argtypes[i]))
node = *va_arg(args, PurpleBlistNode **);
else
- node = va_arg(args, PurpleBlistNode *);
- switch (purple_blist_node_get_type(node)) {
- case PURPLE_BLIST_GROUP_NODE:
+#endif
+ node = va_arg(args, PurpleBlistNode *);
+
+ if (PURPLE_IS_GROUP(node)) {
arg = Tcl_NewListObj(0, NULL);
Tcl_ListObjAppendElement(handler->interp, arg,
Tcl_NewStringObj("group", -1));
Tcl_ListObjAppendElement(handler->interp, arg,
- Tcl_NewStringObj(purple_group_get_name((PurpleGroup *)node), -1));
- break;
- case PURPLE_BLIST_CONTACT_NODE:
+ Tcl_NewStringObj(purple_group_get_name(PURPLE_GROUP(node)), -1));
+ } else if (PURPLE_IS_CONTACT(node)) {
/* g_string_printf(val, "contact {%s}", Contact Name? ); */
arg = Tcl_NewStringObj("contact", -1);
- break;
- case PURPLE_BLIST_BUDDY_NODE:
+ } else if (PURPLE_IS_BUDDY(node)) {
arg = Tcl_NewListObj(0, NULL);
Tcl_ListObjAppendElement(handler->interp, arg,
Tcl_NewStringObj("buddy", -1));
Tcl_ListObjAppendElement(handler->interp, arg,
- Tcl_NewStringObj(purple_buddy_get_name((PurpleBuddy *)node), -1));
+ Tcl_NewStringObj(purple_buddy_get_name(PURPLE_BUDDY(node)), -1));
Tcl_ListObjAppendElement(handler->interp, arg,
purple_tcl_ref_new(PurpleTclRefAccount,
- purple_buddy_get_account((PurpleBuddy *)node)));
- break;
- case PURPLE_BLIST_CHAT_NODE:
+ purple_buddy_get_account(PURPLE_BUDDY(node))));
+ } else if (PURPLE_IS_CHAT(node)) {
arg = Tcl_NewListObj(0, NULL);
Tcl_ListObjAppendElement(handler->interp, arg,
Tcl_NewStringObj("chat", -1));
Tcl_ListObjAppendElement(handler->interp, arg,
- Tcl_NewStringObj(purple_chat_get_name((PurpleChat *)node), -1));
+ Tcl_NewStringObj(purple_chat_get_name(PURPLE_CHAT(node)), -1));
Tcl_ListObjAppendElement(handler->interp, arg,
purple_tcl_ref_new(PurpleTclRefAccount,
- purple_chat_get_account((PurpleChat *)node)));
- break;
- case PURPLE_BLIST_OTHER_NODE:
- arg = Tcl_NewStringObj("other", -1);
- break;
+ purple_chat_get_account(PURPLE_CHAT(node))));
}
- break;
+ }
+ else if (G_TYPE_IS_ENUM(handler->argtypes[i]))
+ {
+ arg = Tcl_NewIntObj(va_arg(args, int));
+ }
+ else
+ {
+ /* What? I guess just pass the word ... */
+ /* treat this as a pointer, but complain first */
+ purple_debug(PURPLE_DEBUG_ERROR, "tcl", "unknown type %s\n",
+ g_type_name(handler->argtypes[i]));
}
}
Tcl_ListObjAppendElement(handler->interp, cmd, arg);
@@ -345,7 +347,7 @@ static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handle
result = Tcl_GetObjResult(handler->interp);
/* handle return values -- strings and words only */
if (handler->returntype) {
- if (purple_value_get_type(handler->returntype) == PURPLE_TYPE_STRING) {
+ if (handler->returntype == G_TYPE_STRING) {
retval = (void *)g_strdup(Tcl_GetString(result));
} else {
if (Tcl_GetIntFromObj(handler->interp, result, (int *)&retval) != TCL_OK) {
@@ -361,14 +363,14 @@ static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handle
for (i = 0; i < handler->nargs; i++) {
g_string_printf(name, "%s::arg%d",
Tcl_GetString(handler->namespace), i);
+#if 0
if (purple_value_is_outgoing(handler->argtypes[i])
- && purple_value_get_type(handler->argtypes[i]) != PURPLE_TYPE_SUBTYPE)
+ && purple_value_get_type(handler->argtypes[i]) != G_TYPE_SUBTYPE)
Tcl_UnlinkVar(handler->interp, name->str);
-
/* We basically only have to deal with strings on the
* way out */
- switch (purple_value_get_type(handler->argtypes[i])) {
- case PURPLE_TYPE_STRING:
+ switch (handler->argtypes[i]) {
+ case G_TYPE_STRING:
if (purple_value_is_outgoing(handler->argtypes[i])) {
if (vals[i] != NULL && *(char **)vals[i] != NULL) {
g_free(*strs[i]);
@@ -381,6 +383,7 @@ static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handle
/* nothing */
;
}
+#endif
}
g_string_free(name, TRUE);
diff --git a/libpurple/plugins/test.pl b/libpurple/plugins/test.pl
index f2c4acbfe4..6b6a40e5ad 100644
--- a/libpurple/plugins/test.pl
+++ b/libpurple/plugins/test.pl
@@ -7,7 +7,7 @@ use Purple;
summary => 'Provides as a test base for the perl plugin.',
description => 'Provides as a test base for the perl plugin.',
author => 'Etan Reisner <deryni\@pidgin.im>',
- url => 'http://pidgin.im',
+ url => 'https://pidgin.im',
load => "plugin_load"
);
diff --git a/libpurple/pounce.c b/libpurple/pounce.c
index 185b302dc9..8b8fc29eda 100644
--- a/libpurple/pounce.c
+++ b/libpurple/pounce.c
@@ -1,8 +1,3 @@
-/**
- * @file pounce.c Buddy Pounce API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -32,6 +27,31 @@
#include "pounce.h"
#include "util.h"
+/*
+ * A buddy pounce structure.
+ *
+ * Buddy pounces are actions triggered by a buddy-related event. For
+ * example, a sound can be played or an IM window opened when a buddy
+ * signs on or returns from away. Such responses are handled in the
+ * UI. The events themselves are done in the core.
+ */
+struct _PurplePounce
+{
+ char *ui_type; /* The type of UI. */
+
+ PurplePounceEvent events; /* The event(s) to pounce on. */
+ PurplePounceOption options; /* The pounce options */
+ PurpleAccount *pouncer; /* The user who is pouncing. */
+
+ char *pouncee; /* The buddy to pounce on. */
+
+ GHashTable *actions; /* The registered actions. */
+
+ gboolean save; /* Whether or not the pounce should
+ be saved after activation. */
+ void *data; /* Pounce-specific data. */
+};
+
typedef struct
{
GString *buffer;
@@ -115,15 +135,15 @@ static void
action_parameter_to_xmlnode(gpointer key, gpointer value, gpointer user_data)
{
const char *name, *param_value;
- xmlnode *node, *child;
+ PurpleXmlNode *node, *child;
name = (const char *)key;
param_value = (const char *)value;
- node = (xmlnode *)user_data;
+ node = (PurpleXmlNode *)user_data;
- child = xmlnode_new_child(node, "param");
- xmlnode_set_attrib(child, "name", name);
- xmlnode_insert_data(child, param_value, -1);
+ child = purple_xmlnode_new_child(node, "param");
+ purple_xmlnode_set_attrib(child, "name", name);
+ purple_xmlnode_insert_data(child, param_value, -1);
}
static void
@@ -131,43 +151,43 @@ action_parameter_list_to_xmlnode(gpointer key, gpointer value, gpointer user_dat
{
const char *action;
PurplePounceActionData *action_data;
- xmlnode *node, *child;
+ PurpleXmlNode *node, *child;
action = (const char *)key;
action_data = (PurplePounceActionData *)value;
- node = (xmlnode *)user_data;
+ node = (PurpleXmlNode *)user_data;
if (!action_data->enabled)
return;
- child = xmlnode_new_child(node, "action");
- xmlnode_set_attrib(child, "type", action);
+ child = purple_xmlnode_new_child(node, "action");
+ purple_xmlnode_set_attrib(child, "type", action);
g_hash_table_foreach(action_data->atts, action_parameter_to_xmlnode, child);
}
static void
-add_event_to_xmlnode(xmlnode *node, const char *type)
+add_event_to_xmlnode(PurpleXmlNode *node, const char *type)
{
- xmlnode *child;
+ PurpleXmlNode *child;
- child = xmlnode_new_child(node, "event");
- xmlnode_set_attrib(child, "type", type);
+ child = purple_xmlnode_new_child(node, "event");
+ purple_xmlnode_set_attrib(child, "type", type);
}
static void
-add_option_to_xmlnode(xmlnode *node, const char *type)
+add_option_to_xmlnode(PurpleXmlNode *node, const char *type)
{
- xmlnode *child;
+ PurpleXmlNode *child;
- child = xmlnode_new_child(node, "option");
- xmlnode_set_attrib(child, "type", type);
+ child = purple_xmlnode_new_child(node, "option");
+ purple_xmlnode_set_attrib(child, "type", type);
}
-static xmlnode *
+static PurpleXmlNode *
pounce_to_xmlnode(PurplePounce *pounce)
{
- xmlnode *node, *child;
+ PurpleXmlNode *node, *child;
PurpleAccount *pouncer;
PurplePounceEvent events;
PurplePounceOption options;
@@ -176,24 +196,24 @@ pounce_to_xmlnode(PurplePounce *pounce)
events = purple_pounce_get_events(pounce);
options = purple_pounce_get_options(pounce);
- node = xmlnode_new("pounce");
- xmlnode_set_attrib(node, "ui", pounce->ui_type);
+ node = purple_xmlnode_new("pounce");
+ purple_xmlnode_set_attrib(node, "ui", pounce->ui_type);
- child = xmlnode_new_child(node, "account");
- xmlnode_set_attrib(child, "protocol", pouncer->protocol_id);
- xmlnode_insert_data(child,
+ child = purple_xmlnode_new_child(node, "account");
+ purple_xmlnode_set_attrib(child, "protocol", purple_account_get_protocol_id(pouncer));
+ purple_xmlnode_insert_data(child,
purple_normalize(pouncer, purple_account_get_username(pouncer)), -1);
- child = xmlnode_new_child(node, "pouncee");
- xmlnode_insert_data(child, purple_pounce_get_pouncee(pounce), -1);
+ child = purple_xmlnode_new_child(node, "pouncee");
+ purple_xmlnode_insert_data(child, purple_pounce_get_pouncee(pounce), -1);
/* Write pounce options */
- child = xmlnode_new_child(node, "options");
+ child = purple_xmlnode_new_child(node, "options");
if (options & PURPLE_POUNCE_OPTION_AWAY)
add_option_to_xmlnode(child, "on-away");
/* Write pounce events */
- child = xmlnode_new_child(node, "events");
+ child = purple_xmlnode_new_child(node, "events");
if (events & PURPLE_POUNCE_SIGNON)
add_event_to_xmlnode(child, "sign-on");
if (events & PURPLE_POUNCE_SIGNOFF)
@@ -216,28 +236,28 @@ pounce_to_xmlnode(PurplePounce *pounce)
add_event_to_xmlnode(child, "message-received");
/* Write pounce actions */
- child = xmlnode_new_child(node, "actions");
+ child = purple_xmlnode_new_child(node, "actions");
g_hash_table_foreach(pounce->actions, action_parameter_list_to_xmlnode, child);
if (purple_pounce_get_save(pounce))
- xmlnode_new_child(node, "save");
+ purple_xmlnode_new_child(node, "save");
return node;
}
-static xmlnode *
+static PurpleXmlNode *
pounces_to_xmlnode(void)
{
- xmlnode *node, *child;
+ PurpleXmlNode *node, *child;
GList *cur;
- node = xmlnode_new("pounces");
- xmlnode_set_attrib(node, "version", "1.0");
+ node = purple_xmlnode_new("pounces");
+ purple_xmlnode_set_attrib(node, "version", "1.0");
for (cur = purple_pounces_get_all(); cur != NULL; cur = cur->next)
{
child = pounce_to_xmlnode(cur->data);
- xmlnode_insert_child(node, child);
+ purple_xmlnode_insert_child(node, child);
}
return node;
@@ -246,7 +266,7 @@ pounces_to_xmlnode(void)
static void
sync_pounces(void)
{
- xmlnode *node;
+ PurpleXmlNode *node;
char *data;
if (!pounces_loaded)
@@ -257,10 +277,10 @@ sync_pounces(void)
}
node = pounces_to_xmlnode();
- data = xmlnode_to_formatted_str(node, NULL);
+ data = purple_xmlnode_to_formatted_str(node, NULL);
purple_util_write_data_to_file("pounces.xml", data, -1);
g_free(data);
- xmlnode_free(node);
+ purple_xmlnode_free(node);
}
static gboolean
@@ -405,12 +425,8 @@ end_element_handler(GMarkupParseContext *context, const gchar *element_name,
}
if (purple_strequal(element_name, "account")) {
- char *tmp;
g_free(data->account_name);
data->account_name = g_strdup(buffer);
- tmp = data->protocol_id;
- data->protocol_id = g_strdup(_purple_oscar_convert(buffer, tmp));
- g_free(tmp);
}
else if (purple_strequal(element_name, "pouncee")) {
g_free(data->pouncee);
@@ -551,7 +567,7 @@ static GMarkupParser pounces_parser =
NULL
};
-gboolean
+static gboolean
purple_pounces_load(void)
{
gchar *filename = g_build_filename(purple_user_dir(), "pounces.xml", NULL);
@@ -1105,18 +1121,18 @@ buddy_idle_changed_cb(PurpleBuddy *buddy, gboolean old_idle, gboolean idle)
static void
buddy_typing_cb(PurpleAccount *account, const char *name, void *data)
{
- PurpleConversation *conv;
+ PurpleIMConversation *im;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, account);
- if (conv != NULL)
+ im = purple_conversations_find_im_with_account(name, account);
+ if (im != NULL)
{
- PurpleTypingState state;
+ PurpleIMTypingState state;
PurplePounceEvent event;
- state = purple_conv_im_get_typing_state(PURPLE_CONV_IM(conv));
- if (state == PURPLE_TYPED)
+ state = purple_im_conversation_get_typing_state(im);
+ if (state == PURPLE_IM_TYPED)
event = PURPLE_POUNCE_TYPED;
- else if (state == PURPLE_NOT_TYPING)
+ else if (state == PURPLE_IM_NOT_TYPING)
event = PURPLE_POUNCE_TYPING_STOPPED;
else
event = PURPLE_POUNCE_TYPING;
@@ -1169,6 +1185,8 @@ purple_pounces_init(void)
purple_signal_connect(conv_handle, "received-im-msg",
handle, PURPLE_CALLBACK(received_message_cb), NULL);
+
+ purple_pounces_load();
}
void
diff --git a/libpurple/pounce.h b/libpurple/pounce.h
index 434fe318bb..264142437d 100644
--- a/libpurple/pounce.h
+++ b/libpurple/pounce.h
@@ -1,8 +1,3 @@
-/**
- * @file pounce.h Buddy Pounce API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -23,8 +18,15 @@
* 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_POUNCE_H_
#define _PURPLE_POUNCE_H_
+/**
+ * SECTION:pounce
+ * @section_id: libpurple-pounce
+ * @short_description: <filename>pounce.h</filename>
+ * @title: Buddy Pounce API
+ */
typedef struct _PurplePounce PurplePounce;
@@ -32,358 +34,368 @@ typedef struct _PurplePounce PurplePounce;
#include "account.h"
/**
+ * PurplePounceEvent:
+ * @PURPLE_POUNCE_NONE: No events.
+ * @PURPLE_POUNCE_SIGNON: The buddy signed on.
+ * @PURPLE_POUNCE_SIGNOFF: The buddy signed off.
+ * @PURPLE_POUNCE_AWAY: The buddy went away.
+ * @PURPLE_POUNCE_AWAY_RETURN: The buddy returned from away.
+ * @PURPLE_POUNCE_IDLE: The buddy became idle.
+ * @PURPLE_POUNCE_IDLE_RETURN: The buddy is no longer idle.
+ * @PURPLE_POUNCE_TYPING: The buddy started typing.
+ * @PURPLE_POUNCE_TYPED: The buddy has entered text.
+ * @PURPLE_POUNCE_TYPING_STOPPED: The buddy stopped typing.
+ * @PURPLE_POUNCE_MESSAGE_RECEIVED: The buddy sent a message.
+ *
* Events that trigger buddy pounces.
*/
typedef enum
{
- PURPLE_POUNCE_NONE = 0x000, /**< No events. */
- PURPLE_POUNCE_SIGNON = 0x001, /**< The buddy signed on. */
- PURPLE_POUNCE_SIGNOFF = 0x002, /**< The buddy signed off. */
- PURPLE_POUNCE_AWAY = 0x004, /**< The buddy went away. */
- PURPLE_POUNCE_AWAY_RETURN = 0x008, /**< The buddy returned from away. */
- PURPLE_POUNCE_IDLE = 0x010, /**< The buddy became idle. */
- PURPLE_POUNCE_IDLE_RETURN = 0x020, /**< The buddy is no longer idle. */
- PURPLE_POUNCE_TYPING = 0x040, /**< The buddy started typing. */
- PURPLE_POUNCE_TYPED = 0x080, /**< The buddy has entered text. */
- PURPLE_POUNCE_TYPING_STOPPED = 0x100, /**< The buddy stopped typing. */
- PURPLE_POUNCE_MESSAGE_RECEIVED = 0x200 /**< The buddy sent a message */
+ PURPLE_POUNCE_NONE = 0x000,
+ PURPLE_POUNCE_SIGNON = 0x001,
+ PURPLE_POUNCE_SIGNOFF = 0x002,
+ PURPLE_POUNCE_AWAY = 0x004,
+ PURPLE_POUNCE_AWAY_RETURN = 0x008,
+ PURPLE_POUNCE_IDLE = 0x010,
+ PURPLE_POUNCE_IDLE_RETURN = 0x020,
+ PURPLE_POUNCE_TYPING = 0x040,
+ PURPLE_POUNCE_TYPED = 0x080,
+ PURPLE_POUNCE_TYPING_STOPPED = 0x100,
+ PURPLE_POUNCE_MESSAGE_RECEIVED = 0x200
} PurplePounceEvent;
+/**
+ * PurplePounceOption:
+ * @PURPLE_POUNCE_OPTION_NONE: No Option
+ * @PURPLE_POUNCE_OPTION_AWAY: Pounce only when away
+ */
typedef enum
{
- PURPLE_POUNCE_OPTION_NONE = 0x00, /**< No Option */
- PURPLE_POUNCE_OPTION_AWAY = 0x01 /**< Pounce only when away */
+ PURPLE_POUNCE_OPTION_NONE = 0x00,
+ PURPLE_POUNCE_OPTION_AWAY = 0x01
} PurplePounceOption;
-/** A pounce callback. */
-typedef void (*PurplePounceCb)(PurplePounce *, PurplePounceEvent, void *);
-
/**
- * A buddy pounce structure.
+ * PurplePounceCb:
*
- * Buddy pounces are actions triggered by a buddy-related event. For
- * example, a sound can be played or an IM window opened when a buddy
- * signs on or returns from away. Such responses are handled in the
- * UI. The events themselves are done in the core.
+ * A pounce callback.
*/
-struct _PurplePounce
-{
- char *ui_type; /**< The type of UI. */
-
- PurplePounceEvent events; /**< The event(s) to pounce on. */
- PurplePounceOption options; /**< The pounce options */
- PurpleAccount *pouncer; /**< The user who is pouncing. */
-
- char *pouncee; /**< The buddy to pounce on. */
-
- GHashTable *actions; /**< The registered actions. */
-
- gboolean save; /**< Whether or not the pounce should
- be saved after activation. */
- void *data; /**< Pounce-specific data. */
-};
+typedef void (*PurplePounceCb)(PurplePounce *, PurplePounceEvent, void *);
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name Buddy Pounce API */
+/* Buddy Pounce API */
/**************************************************************************/
-/*@{*/
/**
- * Creates a new buddy pounce.
+ * purple_pounce_new:
+ * @ui_type: The type of UI the pounce is for.
+ * @pouncer: The account that will pounce.
+ * @pouncee: The buddy to pounce on.
+ * @event: The event(s) to pounce on.
+ * @option: Pounce options.
*
- * @param ui_type The type of UI the pounce is for.
- * @param pouncer The account that will pounce.
- * @param pouncee The buddy to pounce on.
- * @param event The event(s) to pounce on.
- * @param option Pounce options.
+ * Creates a new buddy pounce.
*
- * @return The new buddy pounce structure.
+ * Returns: The new buddy pounce structure.
*/
PurplePounce *purple_pounce_new(const char *ui_type, PurpleAccount *pouncer,
const char *pouncee, PurplePounceEvent event,
PurplePounceOption option);
/**
- * Destroys a buddy pounce.
+ * purple_pounce_destroy:
+ * @pounce: The buddy pounce.
*
- * @param pounce The buddy pounce.
+ * Destroys a buddy pounce.
*/
void purple_pounce_destroy(PurplePounce *pounce);
/**
- * Destroys all buddy pounces for the account
+ * purple_pounce_destroy_all_by_account:
+ * @account: The account to remove all pounces from.
*
- * @param account The account to remove all pounces from.
+ * Destroys all buddy pounces for the account
*/
void purple_pounce_destroy_all_by_account(PurpleAccount *account);
/**
- * Destroys all buddy pounces for a buddy
- *
- * @param buddy The buddy whose pounces are to be removed
+ * purple_pounce_destroy_all_by_buddy:
+ * @buddy: The buddy whose pounces are to be removed
*
- * @since 2.8.0
+ * Destroys all buddy pounces for a buddy
*/
void purple_pounce_destroy_all_by_buddy(PurpleBuddy *buddy);
/**
- * Sets the events a pounce should watch for.
+ * purple_pounce_set_events:
+ * @pounce: The buddy pounce.
+ * @events: The events to watch for.
*
- * @param pounce The buddy pounce.
- * @param events The events to watch for.
+ * Sets the events a pounce should watch for.
*/
void purple_pounce_set_events(PurplePounce *pounce, PurplePounceEvent events);
/**
- * Sets the options for a pounce.
+ * purple_pounce_set_options:
+ * @pounce: The buddy pounce.
+ * @options: The options for the pounce.
*
- * @param pounce The buddy pounce.
- * @param options The options for the pounce.
+ * Sets the options for a pounce.
*/
void purple_pounce_set_options(PurplePounce *pounce, PurplePounceOption options);
/**
- * Sets the account that will do the pouncing.
+ * purple_pounce_set_pouncer:
+ * @pounce: The buddy pounce.
+ * @pouncer: The account that will pounce.
*
- * @param pounce The buddy pounce.
- * @param pouncer The account that will pounce.
+ * Sets the account that will do the pouncing.
*/
void purple_pounce_set_pouncer(PurplePounce *pounce, PurpleAccount *pouncer);
/**
- * Sets the buddy a pounce should pounce on.
+ * purple_pounce_set_pouncee:
+ * @pounce: The buddy pounce.
+ * @pouncee: The buddy to pounce on.
*
- * @param pounce The buddy pounce.
- * @param pouncee The buddy to pounce on.
+ * Sets the buddy a pounce should pounce on.
*/
void purple_pounce_set_pouncee(PurplePounce *pounce, const char *pouncee);
/**
- * Sets whether or not the pounce should be saved after execution.
+ * purple_pounce_set_save:
+ * @pounce: The buddy pounce.
+ * @save: %TRUE if the pounce should be saved, or %FALSE otherwise.
*
- * @param pounce The buddy pounce.
- * @param save @c TRUE if the pounce should be saved, or @c FALSE otherwise.
+ * Sets whether or not the pounce should be saved after execution.
*/
void purple_pounce_set_save(PurplePounce *pounce, gboolean save);
/**
- * Registers an action type for the pounce.
+ * purple_pounce_action_register:
+ * @pounce: The buddy pounce.
+ * @name: The action name.
*
- * @param pounce The buddy pounce.
- * @param name The action name.
+ * Registers an action type for the pounce.
*/
void purple_pounce_action_register(PurplePounce *pounce, const char *name);
/**
- * Enables or disables an action for a pounce.
+ * purple_pounce_action_set_enabled:
+ * @pounce: The buddy pounce.
+ * @action: The name of the action.
+ * @enabled: The enabled state.
*
- * @param pounce The buddy pounce.
- * @param action The name of the action.
- * @param enabled The enabled state.
+ * Enables or disables an action for a pounce.
*/
void purple_pounce_action_set_enabled(PurplePounce *pounce, const char *action,
gboolean enabled);
/**
- * Sets a value for an attribute in an action.
+ * purple_pounce_action_set_attribute:
+ * @pounce: The buddy pounce.
+ * @action: The action name.
+ * @attr: The attribute name.
+ * @value: The value.
*
- * If @a value is @c NULL, the value will be unset.
+ * Sets a value for an attribute in an action.
*
- * @param pounce The buddy pounce.
- * @param action The action name.
- * @param attr The attribute name.
- * @param value The value.
+ * If @value is %NULL, the value will be unset.
*/
void purple_pounce_action_set_attribute(PurplePounce *pounce, const char *action,
const char *attr, const char *value);
/**
- * Sets the pounce-specific data.
+ * purple_pounce_set_data:
+ * @pounce: The buddy pounce.
+ * @data: Data specific to the pounce.
*
- * @param pounce The buddy pounce.
- * @param data Data specific to the pounce.
+ * Sets the pounce-specific data.
*/
void purple_pounce_set_data(PurplePounce *pounce, void *data);
/**
- * Returns the events a pounce should watch for.
+ * purple_pounce_get_events:
+ * @pounce: The buddy pounce.
*
- * @param pounce The buddy pounce.
+ * Returns the events a pounce should watch for.
*
- * @return The events the pounce is watching for.
+ * Returns: The events the pounce is watching for.
*/
PurplePounceEvent purple_pounce_get_events(const PurplePounce *pounce);
/**
- * Returns the options for a pounce.
+ * purple_pounce_get_options:
+ * @pounce: The buddy pounce.
*
- * @param pounce The buddy pounce.
+ * Returns the options for a pounce.
*
- * @return The options for the pounce.
+ * Returns: The options for the pounce.
*/
PurplePounceOption purple_pounce_get_options(const PurplePounce *pounce);
/**
- * Returns the account that will do the pouncing.
+ * purple_pounce_get_pouncer:
+ * @pounce: The buddy pounce.
*
- * @param pounce The buddy pounce.
+ * Returns the account that will do the pouncing.
*
- * @return The account that will pounce.
+ * Returns: The account that will pounce.
*/
PurpleAccount *purple_pounce_get_pouncer(const PurplePounce *pounce);
/**
- * Returns the buddy a pounce should pounce on.
+ * purple_pounce_get_pouncee:
+ * @pounce: The buddy pounce.
*
- * @param pounce The buddy pounce.
+ * Returns the buddy a pounce should pounce on.
*
- * @return The buddy to pounce on.
+ * Returns: The buddy to pounce on.
*/
const char *purple_pounce_get_pouncee(const PurplePounce *pounce);
/**
- * Returns whether or not the pounce should save after execution.
+ * purple_pounce_get_save:
+ * @pounce: The buddy pounce.
*
- * @param pounce The buddy pounce.
+ * Returns whether or not the pounce should save after execution.
*
- * @return @c TRUE if the pounce should be saved after execution, or
- * @c FALSE otherwise.
+ * Returns: %TRUE if the pounce should be saved after execution, or
+ * %FALSE otherwise.
*/
gboolean purple_pounce_get_save(const PurplePounce *pounce);
/**
- * Returns whether or not an action is enabled.
+ * purple_pounce_action_is_enabled:
+ * @pounce: The buddy pounce.
+ * @action: The action name.
*
- * @param pounce The buddy pounce.
- * @param action The action name.
+ * Returns whether or not an action is enabled.
*
- * @return @c TRUE if the action is enabled, or @c FALSE otherwise.
+ * Returns: %TRUE if the action is enabled, or %FALSE otherwise.
*/
gboolean purple_pounce_action_is_enabled(const PurplePounce *pounce,
const char *action);
/**
- * Returns the value for an attribute in an action.
+ * purple_pounce_action_get_attribute:
+ * @pounce: The buddy pounce.
+ * @action: The action name.
+ * @attr: The attribute name.
*
- * @param pounce The buddy pounce.
- * @param action The action name.
- * @param attr The attribute name.
+ * Returns the value for an attribute in an action.
*
- * @return The attribute value, if it exists, or @c NULL.
+ * Returns: The attribute value, if it exists, or %NULL.
*/
const char *purple_pounce_action_get_attribute(const PurplePounce *pounce,
const char *action,
const char *attr);
/**
- * Returns the pounce-specific data.
+ * purple_pounce_get_data:
+ * @pounce: The buddy pounce.
*
- * @param pounce The buddy pounce.
+ * Returns the pounce-specific data.
*
- * @return The data specific to a buddy pounce.
+ * Returns: The data specific to a buddy pounce.
*/
void *purple_pounce_get_data(const PurplePounce *pounce);
/**
- * Executes a pounce with the specified pouncer, pouncee, and event type.
+ * purple_pounce_execute:
+ * @pouncer: The account that will do the pouncing.
+ * @pouncee: The buddy that is being pounced.
+ * @events: The events that triggered the pounce.
*
- * @param pouncer The account that will do the pouncing.
- * @param pouncee The buddy that is being pounced.
- * @param events The events that triggered the pounce.
+ * Executes a pounce with the specified pouncer, pouncee, and event type.
*/
void purple_pounce_execute(const PurpleAccount *pouncer, const char *pouncee,
PurplePounceEvent events);
-/*@}*/
-
/**************************************************************************/
-/** @name Buddy Pounce Subsystem API */
+/* Buddy Pounce Subsystem API */
/**************************************************************************/
-/*@{*/
/**
- * Finds a pounce with the specified event(s) and buddy.
+ * purple_find_pounce:
+ * @pouncer: The account to match against.
+ * @pouncee: The buddy to match against.
+ * @events: The event(s) to match against.
*
- * @param pouncer The account to match against.
- * @param pouncee The buddy to match against.
- * @param events The event(s) to match against.
+ * Finds a pounce with the specified event(s) and buddy.
*
- * @return The pounce if found, or @c NULL otherwise.
+ * Returns: The pounce if found, or %NULL otherwise.
*/
PurplePounce *purple_find_pounce(const PurpleAccount *pouncer,
const char *pouncee, PurplePounceEvent events);
-
/**
- * Loads the pounces.
+ * purple_pounces_register_handler:
+ * @ui: The UI name.
+ * @cb: (scope call): The callback function.
+ * @new_pounce: The function called when a pounce is created.
+ * @free_pounce: The function called when a pounce is freed.
*
- * @return @c TRUE if the pounces could be loaded.
- */
-gboolean purple_pounces_load(void);
-
-/**
* Registers a pounce handler for a UI.
- *
- * @param ui The UI name.
- * @param cb The callback function.
- * @param new_pounce The function called when a pounce is created.
- * @param free_pounce The function called when a pounce is freed.
*/
void purple_pounces_register_handler(const char *ui, PurplePounceCb cb,
void (*new_pounce)(PurplePounce *pounce),
void (*free_pounce)(PurplePounce *pounce));
/**
- * Unregisters a pounce handle for a UI.
+ * purple_pounces_unregister_handler:
+ * @ui: The UI name.
*
- * @param ui The UI name.
+ * Unregisters a pounce handle for a UI.
*/
void purple_pounces_unregister_handler(const char *ui);
/**
+ * purple_pounces_get_all:
+ *
* Returns a list of all registered buddy pounces.
*
- * @constreturn The list of buddy pounces.
+ * Returns: (transfer none): The list of buddy pounces.
*/
GList *purple_pounces_get_all(void);
/**
- * Returns a list of registered buddy pounces for the ui-type.
+ * purple_pounces_get_all_for_ui:
+ * @ui: The ID of the UI using the core.
*
- * @param ui The ID of the UI using the core.
+ * Returns a list of registered buddy pounces for the ui-type.
*
- * @return The list of buddy pounces. The list should be freed by
+ * Returns: The list of buddy pounces. The list should be freed by
* the caller when it's no longer used.
- * @since 2.1.0
*/
GList *purple_pounces_get_all_for_ui(const char *ui);
/**
+ * purple_pounces_get_handle:
+ *
* Returns the buddy pounce subsystem handle.
*
- * @return The subsystem handle.
+ * Returns: The subsystem handle.
*/
void *purple_pounces_get_handle(void);
/**
+ * purple_pounces_init:
+ *
* Initializes the pounces subsystem.
*/
void purple_pounces_init(void);
/**
+ * purple_pounces_uninit:
+ *
* Uninitializes the pounces subsystem.
*/
void purple_pounces_uninit(void);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_POUNCE_H_ */
diff --git a/libpurple/prefs.c b/libpurple/prefs.c
index a24b4c4b62..b038144547 100644
--- a/libpurple/prefs.c
+++ b/libpurple/prefs.c
@@ -28,7 +28,6 @@
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
-#include <sys/stat.h>
#include <sys/types.h>
#include <glib.h>
#include "internal.h"
@@ -36,10 +35,6 @@
#include "debug.h"
#include "util.h"
-#ifdef _WIN32
-#include "win32dep.h"
-#endif
-
struct pref_cb {
PurplePrefCallback func;
gpointer data;
@@ -52,6 +47,11 @@ struct purple_pref {
PurplePrefType type;
char *name;
union {
+ /* 'generic' is kind of ugly. We use it as an elegant way to refer to
+ the value of this pref when calling callback functions. We could
+ use 'boolean' or 'integer' or any other field... but it feels
+ mildly cleaner to use a gpointer. Maybe it would be best to use a
+ GValue? */
gpointer generic;
gboolean boolean;
int integer;
@@ -110,59 +110,59 @@ purple_pref *find_pref(const char *name)
*********************************************************************/
/*
- * This function recursively creates the xmlnode tree from the prefs
+ * This function recursively creates the PurpleXmlNode tree from the prefs
* tree structure. Yay recursion!
*/
static void
-pref_to_xmlnode(xmlnode *parent, struct purple_pref *pref)
+pref_to_xmlnode(PurpleXmlNode *parent, struct purple_pref *pref)
{
- xmlnode *node, *childnode;
+ PurpleXmlNode *node, *childnode;
struct purple_pref *child;
char buf[21];
GList *cur;
/* Create a new node */
- node = xmlnode_new_child(parent, "pref");
- xmlnode_set_attrib(node, "name", pref->name);
+ node = purple_xmlnode_new_child(parent, "pref");
+ purple_xmlnode_set_attrib(node, "name", pref->name);
/* Set the type of this node (if type == PURPLE_PREF_NONE then do nothing) */
if (pref->type == PURPLE_PREF_INT) {
- xmlnode_set_attrib(node, "type", "int");
+ purple_xmlnode_set_attrib(node, "type", "int");
g_snprintf(buf, sizeof(buf), "%d", pref->value.integer);
- xmlnode_set_attrib(node, "value", buf);
+ purple_xmlnode_set_attrib(node, "value", buf);
}
else if (pref->type == PURPLE_PREF_STRING) {
- xmlnode_set_attrib(node, "type", "string");
- xmlnode_set_attrib(node, "value", pref->value.string ? pref->value.string : "");
+ purple_xmlnode_set_attrib(node, "type", "string");
+ purple_xmlnode_set_attrib(node, "value", pref->value.string ? pref->value.string : "");
}
else if (pref->type == PURPLE_PREF_STRING_LIST) {
- xmlnode_set_attrib(node, "type", "stringlist");
+ purple_xmlnode_set_attrib(node, "type", "stringlist");
for (cur = pref->value.stringlist; cur != NULL; cur = cur->next)
{
- childnode = xmlnode_new_child(node, "item");
- xmlnode_set_attrib(childnode, "value", cur->data ? cur->data : "");
+ childnode = purple_xmlnode_new_child(node, "item");
+ purple_xmlnode_set_attrib(childnode, "value", cur->data ? cur->data : "");
}
}
else if (pref->type == PURPLE_PREF_PATH) {
char *encoded = g_filename_to_utf8(pref->value.string ? pref->value.string : "", -1, NULL, NULL, NULL);
- xmlnode_set_attrib(node, "type", "path");
- xmlnode_set_attrib(node, "value", encoded);
+ purple_xmlnode_set_attrib(node, "type", "path");
+ purple_xmlnode_set_attrib(node, "value", encoded);
g_free(encoded);
}
else if (pref->type == PURPLE_PREF_PATH_LIST) {
- xmlnode_set_attrib(node, "type", "pathlist");
+ purple_xmlnode_set_attrib(node, "type", "pathlist");
for (cur = pref->value.stringlist; cur != NULL; cur = cur->next)
{
char *encoded = g_filename_to_utf8(cur->data ? cur->data : "", -1, NULL, NULL, NULL);
- childnode = xmlnode_new_child(node, "item");
- xmlnode_set_attrib(childnode, "value", encoded);
+ childnode = purple_xmlnode_new_child(node, "item");
+ purple_xmlnode_set_attrib(childnode, "value", encoded);
g_free(encoded);
}
}
else if (pref->type == PURPLE_PREF_BOOLEAN) {
- xmlnode_set_attrib(node, "type", "bool");
+ purple_xmlnode_set_attrib(node, "type", "bool");
g_snprintf(buf, sizeof(buf), "%d", pref->value.boolean);
- xmlnode_set_attrib(node, "value", buf);
+ purple_xmlnode_set_attrib(node, "value", buf);
}
/* All My Children */
@@ -170,18 +170,18 @@ pref_to_xmlnode(xmlnode *parent, struct purple_pref *pref)
pref_to_xmlnode(node, child);
}
-static xmlnode *
+static PurpleXmlNode *
prefs_to_xmlnode(void)
{
- xmlnode *node;
+ PurpleXmlNode *node;
struct purple_pref *pref, *child;
pref = &prefs;
/* Create the root preference node */
- node = xmlnode_new("pref");
- xmlnode_set_attrib(node, "version", "1");
- xmlnode_set_attrib(node, "name", "/");
+ node = purple_xmlnode_new("pref");
+ purple_xmlnode_set_attrib(node, "version", "1");
+ purple_xmlnode_set_attrib(node, "name", "/");
/* All My Children */
for (child = pref->first_child; child != NULL; child = child->sibling)
@@ -193,7 +193,7 @@ prefs_to_xmlnode(void)
static void
sync_prefs(void)
{
- xmlnode *node;
+ PurpleXmlNode *node;
char *data;
if (!prefs_loaded)
@@ -208,10 +208,10 @@ sync_prefs(void)
}
node = prefs_to_xmlnode();
- data = xmlnode_to_formatted_str(node, NULL);
+ data = purple_xmlnode_to_formatted_str(node, NULL);
purple_util_write_data_to_file("prefs.xml", data, -1);
g_free(data);
- xmlnode_free(node);
+ purple_xmlnode_free(node);
}
static gboolean
@@ -387,23 +387,21 @@ purple_prefs_load()
return FALSE;
}
- purple_debug_info("prefs", "Reading %s\n", filename);
+ purple_debug_misc("prefs", "Reading %s", filename);
if(!g_file_get_contents(filename, &contents, &length, &error)) {
-#ifdef _WIN32
- gchar *common_appdata = wpurple_get_special_folder(CSIDL_COMMON_APPDATA);
-#endif
+ const gchar *sysconfdir = PURPLE_SYSCONFDIR;
g_free(filename);
g_error_free(error);
error = NULL;
-#ifdef _WIN32
- filename = g_build_filename(common_appdata ? common_appdata : "", "purple", "prefs.xml", NULL);
- g_free(common_appdata);
-#else
- filename = g_build_filename(SYSCONFDIR, "purple", "prefs.xml", NULL);
+#ifndef __COVERITY__
+ /* coverity dead_error_line false positive */
+ if (sysconfdir == NULL)
+ sysconfdir = "";
#endif
+ filename = g_build_filename(sysconfdir, "purple", "prefs.xml", NULL);
purple_debug_info("prefs", "Reading %s\n", filename);
@@ -439,7 +437,8 @@ purple_prefs_load()
return FALSE;
}
- purple_debug_info("prefs", "Finished reading %s\n", filename);
+ if (purple_debug_is_verbose())
+ purple_debug_misc("prefs", "Finished reading %s", filename);
g_markup_parse_context_free(context);
g_free(contents);
g_free(filename);
@@ -571,8 +570,7 @@ add_pref(PurplePrefType type, const char *name)
parent = find_pref_parent(name);
- if(!parent)
- return NULL;
+ g_return_val_if_fail(parent, NULL);
my_name = get_path_basename(name);
@@ -782,21 +780,6 @@ purple_prefs_trigger_callback(const char *name)
}
void
-purple_prefs_set_generic(const char *name, gpointer value)
-{
- struct purple_pref *pref = find_pref(name);
-
- if(!pref) {
- purple_debug_error("prefs",
- "purple_prefs_set_generic: Unknown pref %s\n", name);
- return;
- }
-
- pref->value.generic = value;
- do_callbacks(name, pref);
-}
-
-void
purple_prefs_set_bool(const char *name, gboolean value)
{
struct purple_pref *pref = find_pref(name);
@@ -965,7 +948,7 @@ purple_prefs_exists(const char *name)
}
PurplePrefType
-purple_prefs_get_type(const char *name)
+purple_prefs_get_pref_type(const char *name)
{
struct purple_pref *pref = find_pref(name);
@@ -1337,8 +1320,8 @@ purple_prefs_get_children_names(const char *name)
return list;
}
-void
-purple_prefs_update_old()
+static void
+prefs_update_old(void)
{
purple_prefs_rename("/core", "/purple");
@@ -1357,6 +1340,7 @@ purple_prefs_update_old()
purple_prefs_remove("/purple/conversations/chat/show_leave");
purple_prefs_remove("/purple/conversations/combine_chat_im");
purple_prefs_remove("/purple/conversations/use_alias_for_title");
+ purple_prefs_remove("/purple/debug/timestamps");
purple_prefs_remove("/purple/logging/log_signon_signoff");
purple_prefs_remove("/purple/logging/log_idle_state");
purple_prefs_remove("/purple/logging/log_away_state");
@@ -1442,7 +1426,7 @@ purple_prefs_init(void)
purple_prefs_remove("/purple/contact/idle_score");
purple_prefs_load();
- purple_prefs_update_old();
+ prefs_update_old();
}
void
diff --git a/libpurple/prefs.h b/libpurple/prefs.h
index 2d02f3f2e7..1b98beabdc 100644
--- a/libpurple/prefs.h
+++ b/libpurple/prefs.h
@@ -1,8 +1,3 @@
-/**
- * @file prefs.h Prefs API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -24,128 +19,157 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
*/
+
#ifndef _PURPLE_PREFS_H_
#define _PURPLE_PREFS_H_
+/**
+ * SECTION:prefs
+ * @section_id: libpurple-prefs
+ * @short_description: <filename>prefs.h</filename>
+ * @title: Preferences API
+ */
#include <glib.h>
/**
+ * PurplePrefType:
+ * @PURPLE_PREF_NONE: No type.
+ * @PURPLE_PREF_BOOLEAN: Boolean.
+ * @PURPLE_PREF_INT: Integer.
+ * @PURPLE_PREF_STRING: String.
+ * @PURPLE_PREF_STRING_LIST: List of strings.
+ * @PURPLE_PREF_PATH: Path.
+ * @PURPLE_PREF_PATH_LIST: List of paths.
+ *
* Preference data types.
*/
-typedef enum _PurplePrefType
+typedef enum
{
- PURPLE_PREF_NONE, /**< No type. */
- PURPLE_PREF_BOOLEAN, /**< Boolean. */
- PURPLE_PREF_INT, /**< Integer. */
- PURPLE_PREF_STRING, /**< String. */
- PURPLE_PREF_STRING_LIST, /**< List of strings. */
- PURPLE_PREF_PATH, /**< Path. */
- PURPLE_PREF_PATH_LIST /**< List of paths. */
+ PURPLE_PREF_NONE,
+ PURPLE_PREF_BOOLEAN,
+ PURPLE_PREF_INT,
+ PURPLE_PREF_STRING,
+ PURPLE_PREF_STRING_LIST,
+ PURPLE_PREF_PATH,
+ PURPLE_PREF_PATH_LIST
} PurplePrefType;
/**
- * The type of callbacks for preference changes.
- *
- * @param name the name of the preference which has changed.
- * @param type the type of the preferenced named @a name
- * @param val the new value of the preferencs; should be cast to the correct
+ * PurplePrefCallback:
+ * @name: the name of the preference which has changed.
+ * @type: the type of the preferenced named @name
+ * @val: the new value of the preferencs; should be cast to the correct
* type. For instance, to recover the value of a #PURPLE_PREF_INT
- * preference, use <tt>GPOINTER_TO_INT(val)</tt>. Alternatively,
- * just call purple_prefs_get_int(), purple_prefs_get_string_list()
- * etc.
- * @param data Arbitrary data specified when the callback was connected with
+ * preference, use <literal>GPOINTER_TO_INT(val)</literal>.
+ * Alternatively, just call purple_prefs_get_int(),
+ * purple_prefs_get_string_list() etc.
+ * @data: Arbitrary data specified when the callback was connected with
* purple_prefs_connect_callback().
*
- * @see purple_prefs_connect_callback()
+ * The type of callbacks for preference changes.
+ *
+ * See purple_prefs_connect_callback().
*/
typedef void (*PurplePrefCallback) (const char *name, PurplePrefType type,
gconstpointer val, gpointer data);
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name Prefs API
+/* Prefs API
Preferences are named according to a directory-like structure.
Example: "/plugins/core/potato/is_from_idaho" (probably a boolean) */
/**************************************************************************/
-/*@{*/
/**
+ * purple_prefs_get_handle:
+ *
* Returns the prefs subsystem handle.
*
- * @return The prefs subsystem handle.
+ * Returns: The prefs subsystem handle.
*/
void *purple_prefs_get_handle(void);
/**
+ * purple_prefs_init:
+ *
* Initialize core prefs
*/
void purple_prefs_init(void);
/**
+ * purple_prefs_uninit:
+ *
* Uninitializes the prefs subsystem.
*/
void purple_prefs_uninit(void);
/**
- * Add a new typeless pref.
+ * purple_prefs_add_none:
+ * @name: The name of the pref
*
- * @param name The name of the pref
+ * Add a new typeless pref.
*/
void purple_prefs_add_none(const char *name);
/**
- * Add a new boolean pref.
+ * purple_prefs_add_bool:
+ * @name: The name of the pref
+ * @value: The initial value to set
*
- * @param name The name of the pref
- * @param value The initial value to set
+ * Add a new boolean pref.
*/
void purple_prefs_add_bool(const char *name, gboolean value);
/**
- * Add a new integer pref.
+ * purple_prefs_add_int:
+ * @name: The name of the pref
+ * @value: The initial value to set
*
- * @param name The name of the pref
- * @param value The initial value to set
+ * Add a new integer pref.
*/
void purple_prefs_add_int(const char *name, int value);
/**
- * Add a new string pref.
+ * purple_prefs_add_string:
+ * @name: The name of the pref
+ * @value: The initial value to set
*
- * @param name The name of the pref
- * @param value The initial value to set
+ * Add a new string pref.
*/
void purple_prefs_add_string(const char *name, const char *value);
/**
+ * purple_prefs_add_string_list:
+ * @name: The name of the pref
+ * @value: The initial value to set
+ *
* Add a new string list pref.
*
- * @param name The name of the pref
- * @param value The initial value to set
- * @note This function takes a copy of the strings in the value list. The list
+ * Note: This function takes a copy of the strings in the value list. The list
* itself and original copies of the strings are up to the caller to
* free.
*/
void purple_prefs_add_string_list(const char *name, GList *value);
/**
- * Add a new path pref.
+ * purple_prefs_add_path:
+ * @name: The name of the pref
+ * @value: The initial value to set
*
- * @param name The name of the pref
- * @param value The initial value to set
+ * Add a new path pref.
*/
void purple_prefs_add_path(const char *name, const char *value);
/**
+ * purple_prefs_add_path_list:
+ * @name: The name of the pref
+ * @value: The initial value to set
+ *
* Add a new path list pref.
*
- * @param name The name of the pref
- * @param value The initial value to set
- * @note This function takes a copy of the strings in the value list. The list
+ * Note: This function takes a copy of the strings in the value list. The list
* itself and original copies of the strings are up to the caller to
* free.
*/
@@ -153,219 +177,229 @@ void purple_prefs_add_path_list(const char *name, GList *value);
/**
- * Remove a pref.
+ * purple_prefs_remove:
+ * @name: The name of the pref
*
- * @param name The name of the pref
+ * Remove a pref.
*/
void purple_prefs_remove(const char *name);
/**
- * Rename a pref
+ * purple_prefs_rename:
+ * @oldname: The old name of the pref
+ * @newname: The new name for the pref
*
- * @param oldname The old name of the pref
- * @param newname The new name for the pref
+ * Rename a pref
*/
void purple_prefs_rename(const char *oldname, const char *newname);
/**
- * Rename a boolean pref, toggling it's value
+ * purple_prefs_rename_boolean_toggle:
+ * @oldname: The old name of the pref
+ * @newname: The new name for the pref
*
- * @param oldname The old name of the pref
- * @param newname The new name for the pref
+ * Rename a boolean pref, toggling it's value
*/
void purple_prefs_rename_boolean_toggle(const char *oldname, const char *newname);
/**
+ * purple_prefs_destroy:
+ *
* Remove all prefs.
*/
void purple_prefs_destroy(void);
/**
- * Set raw pref value
- *
- * @param name The name of the pref
- * @param value The value to set
+ * purple_prefs_set_bool:
+ * @name: The name of the pref
+ * @value: The value to set
*
- * @deprecated We're not really sure what purpose this function serves, so it
- * will be removed in 3.0.0. Preferences values set using this
- * function aren't serialized to prefs.xml, which could be
- * misleading. There is also no purple_prefs_get_generic, which
- * means that if you can't really get the value (other in a
- * connected callback). If you think you have a use for this then
- * please let us know.
- */
-/* TODO: When this is removed, also remove struct purple_pref->value.generic */
-void purple_prefs_set_generic(const char *name, gpointer value);
-
-/**
* Set boolean pref value
- *
- * @param name The name of the pref
- * @param value The value to set
*/
void purple_prefs_set_bool(const char *name, gboolean value);
/**
- * Set integer pref value
+ * purple_prefs_set_int:
+ * @name: The name of the pref
+ * @value: The value to set
*
- * @param name The name of the pref
- * @param value The value to set
+ * Set integer pref value
*/
void purple_prefs_set_int(const char *name, int value);
/**
- * Set string pref value
+ * purple_prefs_set_string:
+ * @name: The name of the pref
+ * @value: The value to set
*
- * @param name The name of the pref
- * @param value The value to set
+ * Set string pref value
*/
void purple_prefs_set_string(const char *name, const char *value);
/**
- * Set string list pref value
+ * purple_prefs_set_string_list:
+ * @name: The name of the pref
+ * @value: The value to set
*
- * @param name The name of the pref
- * @param value The value to set
+ * Set string list pref value
*/
void purple_prefs_set_string_list(const char *name, GList *value);
/**
- * Set path pref value
+ * purple_prefs_set_path:
+ * @name: The name of the pref
+ * @value: The value to set
*
- * @param name The name of the pref
- * @param value The value to set
+ * Set path pref value
*/
void purple_prefs_set_path(const char *name, const char *value);
/**
- * Set path list pref value
+ * purple_prefs_set_path_list:
+ * @name: The name of the pref
+ * @value: The value to set
*
- * @param name The name of the pref
- * @param value The value to set
+ * Set path list pref value
*/
void purple_prefs_set_path_list(const char *name, GList *value);
/**
+ * purple_prefs_exists:
+ * @name: The name of the pref
+ *
* Check if a pref exists
*
- * @param name The name of the pref
- * @return TRUE if the pref exists. Otherwise FALSE.
+ * Returns: TRUE if the pref exists. Otherwise FALSE.
*/
gboolean purple_prefs_exists(const char *name);
/**
+ * purple_prefs_get_pref_type:
+ * @name: The name of the pref
+ *
* Get pref type
*
- * @param name The name of the pref
- * @return The type of the pref
+ * Returns: The type of the pref
*/
-PurplePrefType purple_prefs_get_type(const char *name);
+PurplePrefType purple_prefs_get_pref_type(const char *name);
/**
+ * purple_prefs_get_bool:
+ * @name: The name of the pref
+ *
* Get boolean pref value
*
- * @param name The name of the pref
- * @return The value of the pref
+ * Returns: The value of the pref
*/
gboolean purple_prefs_get_bool(const char *name);
/**
+ * purple_prefs_get_int:
+ * @name: The name of the pref
+ *
* Get integer pref value
*
- * @param name The name of the pref
- * @return The value of the pref
+ * Returns: The value of the pref
*/
int purple_prefs_get_int(const char *name);
/**
+ * purple_prefs_get_string:
+ * @name: The name of the pref
+ *
* Get string pref value
*
- * @param name The name of the pref
- * @return The value of the pref
+ * Returns: The value of the pref
*/
const char *purple_prefs_get_string(const char *name);
/**
+ * purple_prefs_get_string_list:
+ * @name: The name of the pref
+ *
* Get string list pref value
*
- * @param name The name of the pref
- * @return The value of the pref
+ * Returns: The value of the pref
*/
GList *purple_prefs_get_string_list(const char *name);
/**
+ * purple_prefs_get_path:
+ * @name: The name of the pref
+ *
* Get path pref value
*
- * @param name The name of the pref
- * @return The value of the pref
+ * Returns: The value of the pref
*/
const char *purple_prefs_get_path(const char *name);
/**
+ * purple_prefs_get_path_list:
+ * @name: The name of the pref
+ *
* Get path list pref value
*
- * @param name The name of the pref
- * @return The value of the pref
+ * Returns: The value of the pref
*/
GList *purple_prefs_get_path_list(const char *name);
/**
+ * purple_prefs_get_children_names:
+ * @name: The parent pref
+ *
* Returns a list of children for a pref
*
- * @param name The parent pref
- * @return A list of newly allocated strings denoting the names of the children.
- * Returns @c NULL if there are no children or if pref doesn't exist.
+ * Returns: 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.
- *
- * @since 2.1.0
*/
GList *purple_prefs_get_children_names(const char *name);
/**
- * Add a callback to a pref (and its children)
+ * purple_prefs_connect_callback:
+ * @handle: The handle of the receiver.
+ * @name: The name of the preference
+ * @cb: (scope call): The callback function
+ * @data: The data to pass to the callback function.
*
- * @param handle The handle of the receiver.
- * @param name The name of the preference
- * @param cb The callback function
- * @param data The data to pass to the callback function.
+ * Add a callback to a pref (and its children)
*
- * @return An id to disconnect the callback
+ * See purple_prefs_disconnect_callback().
*
- * @see purple_prefs_disconnect_callback
+ * Returns: An id to disconnect the callback
*/
guint purple_prefs_connect_callback(void *handle, const char *name, PurplePrefCallback cb,
gpointer data);
/**
+ * purple_prefs_disconnect_callback:
+ *
* Remove a callback to a pref
*/
void purple_prefs_disconnect_callback(guint callback_id);
/**
+ * purple_prefs_disconnect_by_handle:
+ *
* Remove all pref callbacks by handle
*/
void purple_prefs_disconnect_by_handle(void *handle);
/**
+ * purple_prefs_trigger_callback:
+ *
* Trigger callbacks as if the pref changed
*/
void purple_prefs_trigger_callback(const char *name);
/**
+ * purple_prefs_load:
+ *
* Read preferences
*/
gboolean purple_prefs_load(void);
-/**
- * Rename legacy prefs and delete some that no longer exist.
- */
-void purple_prefs_update_old(void);
-
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_PREFS_H_ */
diff --git a/libpurple/presence.c b/libpurple/presence.c
new file mode 100644
index 0000000000..5536645861
--- /dev/null
+++ b/libpurple/presence.c
@@ -0,0 +1,1000 @@
+/* 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 "glibcompat.h"
+#include "debug.h"
+#include "dbus-maybe.h"
+#include "presence.h"
+
+#define PURPLE_PRESENCE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_PRESENCE, PurplePresencePrivate))
+
+/** @copydoc _PurplePresencePrivate */
+typedef struct _PurplePresencePrivate PurplePresencePrivate;
+
+#define PURPLE_ACCOUNT_PRESENCE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_ACCOUNT_PRESENCE, PurpleAccountPresencePrivate))
+
+/** @copydoc _PurpleAccountPresencePrivate */
+typedef struct _PurpleAccountPresencePrivate PurpleAccountPresencePrivate;
+
+#define PURPLE_BUDDY_PRESENCE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_BUDDY_PRESENCE, PurpleBuddyPresencePrivate))
+
+/** @copydoc _PurpleBuddyPresencePrivate */
+typedef struct _PurpleBuddyPresencePrivate PurpleBuddyPresencePrivate;
+
+/** Private data for a presence */
+struct _PurplePresencePrivate
+{
+ gboolean idle;
+ time_t idle_time;
+ time_t login_time;
+
+ GList *statuses;
+ GHashTable *status_table;
+
+ PurpleStatus *active_status;
+};
+
+/* Presence property enums */
+enum
+{
+ PRES_PROP_0,
+ PRES_PROP_IDLE,
+ PRES_PROP_IDLE_TIME,
+ PRES_PROP_LOGIN_TIME,
+ PRES_PROP_STATUSES,
+ PRES_PROP_ACTIVE_STATUS,
+ PRES_PROP_LAST
+};
+
+/** Private data for an account presence */
+struct _PurpleAccountPresencePrivate
+{
+ PurpleAccount *account;
+};
+
+/* Account presence property enums */
+enum
+{
+ ACPRES_PROP_0,
+ ACPRES_PROP_ACCOUNT,
+ ACPRES_PROP_LAST
+};
+
+/** Private data for a buddy presence */
+struct _PurpleBuddyPresencePrivate
+{
+ PurpleBuddy *buddy;
+};
+
+/* Buddy presence property enums */
+enum
+{
+ BUDPRES_PROP_0,
+ BUDPRES_PROP_BUDDY,
+ BUDPRES_PROP_LAST
+};
+
+static GObjectClass *parent_class;
+static PurplePresenceClass *presence_class;
+
+static GParamSpec *properties[PRES_PROP_LAST];
+static GParamSpec *ap_properties[ACPRES_PROP_LAST];
+static GParamSpec *bp_properties[BUDPRES_PROP_LAST];
+
+/**************************************************************************
+* PurplePresence API
+**************************************************************************/
+
+void
+purple_presence_set_status_active(PurplePresence *presence, const char *status_id,
+ gboolean active)
+{
+ PurpleStatus *status;
+
+ g_return_if_fail(PURPLE_IS_PRESENCE(presence));
+ g_return_if_fail(status_id != NULL);
+
+ status = purple_presence_get_status(presence, status_id);
+
+ g_return_if_fail(PURPLE_IS_STATUS(status));
+ /* TODO: Should we do the following? */
+ /* g_return_if_fail(active == status->active); */
+
+ if (purple_status_is_exclusive(status))
+ {
+ if (!active)
+ {
+ purple_debug_warning("presence",
+ "Attempted to set a non-independent status "
+ "(%s) inactive. Only independent statuses "
+ "can be specifically marked inactive.",
+ status_id);
+ return;
+ }
+ }
+
+ purple_status_set_active(status, active);
+}
+
+void
+purple_presence_switch_status(PurplePresence *presence, const char *status_id)
+{
+ purple_presence_set_status_active(presence, status_id, TRUE);
+}
+
+void
+purple_presence_set_idle(PurplePresence *presence, gboolean idle, time_t idle_time)
+{
+ gboolean old_idle;
+ PurplePresencePrivate *priv = PURPLE_PRESENCE_GET_PRIVATE(presence);
+ PurplePresenceClass *klass = PURPLE_PRESENCE_GET_CLASS(presence);
+ GObject *obj;
+
+ g_return_if_fail(priv != NULL);
+
+ if (priv->idle == idle && priv->idle_time == idle_time)
+ return;
+
+ old_idle = priv->idle;
+ priv->idle = idle;
+ priv->idle_time = (idle ? idle_time : 0);
+
+ obj = G_OBJECT(presence);
+ g_object_freeze_notify(obj);
+ g_object_notify_by_pspec(obj, properties[PRES_PROP_IDLE]);
+ g_object_notify_by_pspec(obj, properties[PRES_PROP_IDLE_TIME]);
+ g_object_thaw_notify(obj);
+
+ if (klass->update_idle)
+ klass->update_idle(presence, old_idle);
+}
+
+void
+purple_presence_set_login_time(PurplePresence *presence, time_t login_time)
+{
+ PurplePresencePrivate *priv = PURPLE_PRESENCE_GET_PRIVATE(presence);
+
+ g_return_if_fail(priv != NULL);
+
+ if (priv->login_time == login_time)
+ return;
+
+ priv->login_time = login_time;
+
+ g_object_notify_by_pspec(G_OBJECT(presence),
+ properties[PRES_PROP_LOGIN_TIME]);
+}
+
+GList *
+purple_presence_get_statuses(const PurplePresence *presence)
+{
+ PurplePresencePrivate *priv = PURPLE_PRESENCE_GET_PRIVATE(presence);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->statuses;
+}
+
+PurpleStatus *
+purple_presence_get_status(const PurplePresence *presence, const char *status_id)
+{
+ PurpleStatus *status;
+ PurplePresencePrivate *priv = PURPLE_PRESENCE_GET_PRIVATE(presence);
+ GList *l = NULL;
+
+ g_return_val_if_fail(priv != NULL, NULL);
+ g_return_val_if_fail(status_id != NULL, NULL);
+
+ /* What's the purpose of this hash table? */
+ status = (PurpleStatus *)g_hash_table_lookup(priv->status_table,
+ status_id);
+
+ if (status == NULL) {
+ for (l = purple_presence_get_statuses(presence);
+ l != NULL && status == NULL; l = l->next)
+ {
+ PurpleStatus *temp_status = l->data;
+
+ if (purple_strequal(status_id, purple_status_get_id(temp_status)))
+ status = temp_status;
+ }
+
+ if (status != NULL)
+ g_hash_table_insert(priv->status_table,
+ g_strdup(purple_status_get_id(status)), status);
+ }
+
+ return status;
+}
+
+PurpleStatus *
+purple_presence_get_active_status(const PurplePresence *presence)
+{
+ PurplePresencePrivate *priv = PURPLE_PRESENCE_GET_PRIVATE(presence);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->active_status;
+}
+
+gboolean
+purple_presence_is_available(const PurplePresence *presence)
+{
+ PurpleStatus *status;
+
+ g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), FALSE);
+
+ status = purple_presence_get_active_status(presence);
+
+ return ((status != NULL && purple_status_is_available(status)) &&
+ !purple_presence_is_idle(presence));
+}
+
+gboolean
+purple_presence_is_online(const PurplePresence *presence)
+{
+ PurpleStatus *status;
+
+ g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), FALSE);
+
+ if ((status = purple_presence_get_active_status(presence)) == NULL)
+ return FALSE;
+
+ return purple_status_is_online(status);
+}
+
+gboolean
+purple_presence_is_status_active(const PurplePresence *presence,
+ const char *status_id)
+{
+ PurpleStatus *status;
+
+ g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), FALSE);
+ g_return_val_if_fail(status_id != NULL, FALSE);
+
+ status = purple_presence_get_status(presence, status_id);
+
+ return (status != NULL && purple_status_is_active(status));
+}
+
+gboolean
+purple_presence_is_status_primitive_active(const PurplePresence *presence,
+ PurpleStatusPrimitive primitive)
+{
+ GList *l;
+
+ g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), FALSE);
+ g_return_val_if_fail(primitive != PURPLE_STATUS_UNSET, FALSE);
+
+ for (l = purple_presence_get_statuses(presence);
+ l != NULL; l = l->next)
+ {
+ PurpleStatus *temp_status = l->data;
+ PurpleStatusType *type = purple_status_get_status_type(temp_status);
+
+ if (purple_status_type_get_primitive(type) == primitive &&
+ purple_status_is_active(temp_status))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+gboolean
+purple_presence_is_idle(const PurplePresence *presence)
+{
+ PurplePresencePrivate *priv = PURPLE_PRESENCE_GET_PRIVATE(presence);
+
+ g_return_val_if_fail(priv != NULL, FALSE);
+
+ return purple_presence_is_online(presence) && priv->idle;
+}
+
+time_t
+purple_presence_get_idle_time(const PurplePresence *presence)
+{
+ PurplePresencePrivate *priv = PURPLE_PRESENCE_GET_PRIVATE(presence);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return priv->idle_time;
+}
+
+time_t
+purple_presence_get_login_time(const PurplePresence *presence)
+{
+ PurplePresencePrivate *priv = PURPLE_PRESENCE_GET_PRIVATE(presence);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return purple_presence_is_online(presence) ? priv->login_time : 0;
+}
+
+/**************************************************************************
+ * GObject code for PurplePresence
+ **************************************************************************/
+
+/* Set method for GObject properties */
+static void
+purple_presence_set_property(GObject *obj, guint param_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ PurplePresence *presence = PURPLE_PRESENCE(obj);
+ PurplePresencePrivate *priv = PURPLE_PRESENCE_GET_PRIVATE(presence);
+
+ switch (param_id) {
+ case PRES_PROP_IDLE:
+ purple_presence_set_idle(presence, g_value_get_boolean(value), 0);
+ break;
+ case PRES_PROP_IDLE_TIME:
+#if SIZEOF_TIME_T == 4
+ purple_presence_set_idle(presence, TRUE, g_value_get_int(value));
+#elif SIZEOF_TIME_T == 8
+ purple_presence_set_idle(presence, TRUE, g_value_get_int64(value));
+#else
+#error Unknown size of time_t
+#endif
+ break;
+ case PRES_PROP_LOGIN_TIME:
+#if SIZEOF_TIME_T == 4
+ purple_presence_set_login_time(presence, g_value_get_int(value));
+#elif SIZEOF_TIME_T == 8
+ purple_presence_set_login_time(presence, g_value_get_int64(value));
+#else
+#error Unknown size of time_t
+#endif
+ break;
+ case PRES_PROP_ACTIVE_STATUS:
+ priv->active_status = g_value_get_object(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* Get method for GObject properties */
+static void
+purple_presence_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurplePresence *presence = PURPLE_PRESENCE(obj);
+
+ switch (param_id) {
+ case PRES_PROP_IDLE:
+ g_value_set_boolean(value, purple_presence_is_idle(presence));
+ break;
+ case PRES_PROP_IDLE_TIME:
+#if SIZEOF_TIME_T == 4
+ g_value_set_int(value, purple_presence_get_idle_time(presence));
+#elif SIZEOF_TIME_T == 8
+ g_value_set_int64(value, purple_presence_get_idle_time(presence));
+#else
+#error Unknown size of time_t
+#endif
+ break;
+ case PRES_PROP_LOGIN_TIME:
+#if SIZEOF_TIME_T == 4
+ g_value_set_int(value, purple_presence_get_login_time(presence));
+#elif SIZEOF_TIME_T == 8
+ g_value_set_int64(value, purple_presence_get_login_time(presence));
+#else
+#error Unknown size of time_t
+#endif
+ break;
+ case PRES_PROP_STATUSES:
+ g_value_set_pointer(value, purple_presence_get_statuses(presence));
+ break;
+ case PRES_PROP_ACTIVE_STATUS:
+ g_value_set_object(value, purple_presence_get_active_status(presence));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* GObject initialization function */
+static void
+purple_presence_init(GTypeInstance *instance, gpointer klass)
+{
+ PURPLE_DBUS_REGISTER_POINTER(PURPLE_PRESENCE(instance), PurplePresence);
+
+ PURPLE_PRESENCE_GET_PRIVATE(instance)->status_table =
+ g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, NULL);
+}
+
+/* GObject dispose function */
+static void
+purple_presence_dispose(GObject *object)
+{
+ PurplePresencePrivate *priv = PURPLE_PRESENCE_GET_PRIVATE(object);
+
+ if (priv->statuses) {
+ g_list_foreach(priv->statuses, (GFunc)g_object_unref, NULL);
+ g_list_free(priv->statuses);
+ priv->statuses = NULL;
+ }
+
+ parent_class->dispose(object);
+}
+
+/* GObject finalize function */
+static void
+purple_presence_finalize(GObject *object)
+{
+ PurplePresencePrivate *priv = PURPLE_PRESENCE_GET_PRIVATE(object);
+
+ g_hash_table_destroy(priv->status_table);
+
+ PURPLE_DBUS_UNREGISTER_POINTER(object);
+
+ parent_class->finalize(object);
+}
+
+/* Class initializer function */
+static void purple_presence_class_init(PurplePresenceClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ obj_class->dispose = purple_presence_dispose;
+ obj_class->finalize = purple_presence_finalize;
+
+ /* Setup properties */
+ obj_class->get_property = purple_presence_get_property;
+ obj_class->set_property = purple_presence_set_property;
+
+ g_type_class_add_private(klass, sizeof(PurplePresencePrivate));
+
+ properties[PRES_PROP_IDLE] = g_param_spec_boolean("idle", "Idle",
+ "Whether the presence is in idle state.", FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PRES_PROP_IDLE_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
+ ("idle-time", "Idle time",
+ "The idle time of the presence",
+#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_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PRES_PROP_LOGIN_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
+ ("login-time", "Login time",
+ "The login time of the presence.",
+#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_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PRES_PROP_STATUSES] = g_param_spec_pointer("statuses",
+ "Statuses",
+ "The list of statuses in the presence.",
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ properties[PRES_PROP_ACTIVE_STATUS] = g_param_spec_object("active-status",
+ "Active status",
+ "The active status for the presence.", PURPLE_TYPE_STATUS,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, PRES_PROP_LAST, properties);
+}
+
+GType
+purple_presence_get_type(void)
+{
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurplePresenceClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_presence_class_init,
+ NULL,
+ NULL,
+ sizeof(PurplePresence),
+ 0,
+ (GInstanceInitFunc)purple_presence_init,
+ NULL,
+ };
+
+ type = g_type_register_static(G_TYPE_OBJECT, "PurplePresence",
+ &info, G_TYPE_FLAG_ABSTRACT);
+ }
+
+ return type;
+}
+
+/**************************************************************************
+* PurpleAccountPresence API
+**************************************************************************/
+static void
+purple_account_presence_update_idle(PurplePresence *presence, gboolean old_idle)
+{
+ PurpleAccount *account;
+ PurpleConnection *gc = NULL;
+ PurplePlugin *prpl = NULL;
+ PurplePluginProtocolInfo *prpl_info = NULL;
+ gboolean idle = purple_presence_is_idle(presence);
+ time_t idle_time = purple_presence_get_idle_time(presence);
+ time_t current_time = time(NULL);
+
+ account = purple_account_presence_get_account(PURPLE_ACCOUNT_PRESENCE(presence));
+
+ if (purple_prefs_get_bool("/purple/logging/log_system"))
+ {
+ PurpleLog *log = purple_account_get_log(account, FALSE);
+
+ if (log != NULL)
+ {
+ char *msg, *tmp;
+
+ if (idle)
+ tmp = g_strdup_printf(_("+++ %s became idle"), purple_account_get_username(account));
+ else
+ tmp = g_strdup_printf(_("+++ %s became unidle"), purple_account_get_username(account));
+
+ msg = g_markup_escape_text(tmp, -1);
+ g_free(tmp);
+ purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
+ purple_account_get_username(account),
+ (idle ? idle_time : current_time), msg);
+ g_free(msg);
+ }
+ }
+
+ gc = purple_account_get_connection(account);
+
+ if(gc)
+ prpl = purple_connection_get_prpl(gc);
+
+ if(PURPLE_CONNECTION_IS_CONNECTED(gc) && prpl != NULL)
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+
+ if (prpl_info && prpl_info->set_idle)
+ prpl_info->set_idle(gc, (idle ? (current_time - idle_time) : 0));
+}
+
+PurpleAccount *
+purple_account_presence_get_account(const PurpleAccountPresence *presence)
+{
+ PurpleAccountPresencePrivate *priv = PURPLE_ACCOUNT_PRESENCE_GET_PRIVATE(presence);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->account;
+}
+
+static int
+purple_buddy_presence_compute_score(const PurpleBuddyPresence *buddy_presence)
+{
+ GList *l;
+ int score = 0;
+ PurplePresence *presence = PURPLE_PRESENCE(buddy_presence);
+ PurpleBuddy *b = purple_buddy_presence_get_buddy(buddy_presence);
+ int *primitive_scores = _purple_statuses_get_primitive_scores();
+ int offline_score = purple_prefs_get_int("/purple/status/scores/offline_msg");
+ int idle_score = purple_prefs_get_int("/purple/status/scores/idle");
+
+ for (l = purple_presence_get_statuses(presence); l != NULL; l = l->next) {
+ PurpleStatus *status = (PurpleStatus *)l->data;
+ PurpleStatusType *type = purple_status_get_status_type(status);
+
+ if (purple_status_is_active(status)) {
+ score += primitive_scores[purple_status_type_get_primitive(type)];
+ if (!purple_status_is_online(status)) {
+ if (b && purple_account_supports_offline_message(purple_buddy_get_account(b), b))
+ score += offline_score;
+ }
+ }
+ }
+ score += purple_account_get_int(purple_buddy_get_account(b), "score", 0);
+ if (purple_presence_is_idle(presence))
+ score += idle_score;
+ return score;
+}
+
+gint
+purple_buddy_presence_compare(const PurpleBuddyPresence *buddy_presence1,
+ const PurpleBuddyPresence *buddy_presence2)
+{
+ PurplePresence *presence1 = PURPLE_PRESENCE(buddy_presence1);
+ PurplePresence *presence2 = PURPLE_PRESENCE(buddy_presence2);
+ time_t idle_time_1, idle_time_2;
+ int score1 = 0, score2 = 0;
+ int idle_time_score = purple_prefs_get_int("/purple/status/scores/idle_time");
+
+ if (presence1 == presence2)
+ return 0;
+ else if (presence1 == NULL)
+ return 1;
+ else if (presence2 == NULL)
+ return -1;
+
+ if (purple_presence_is_online(presence1) &&
+ !purple_presence_is_online(presence2))
+ return -1;
+ else if (purple_presence_is_online(presence2) &&
+ !purple_presence_is_online(presence1))
+ return 1;
+
+ /* Compute the score of the first set of statuses. */
+ score1 = purple_buddy_presence_compute_score(buddy_presence1);
+
+ /* Compute the score of the second set of statuses. */
+ score2 = purple_buddy_presence_compute_score(buddy_presence2);
+
+ idle_time_1 = time(NULL) - purple_presence_get_idle_time(presence1);
+ idle_time_2 = time(NULL) - purple_presence_get_idle_time(presence2);
+
+ if (idle_time_1 > idle_time_2)
+ score1 += idle_time_score;
+ else if (idle_time_1 < idle_time_2)
+ score2 += idle_time_score;
+
+ if (score1 < score2)
+ return 1;
+ else if (score1 > score2)
+ return -1;
+
+ return 0;
+}
+
+/**************************************************************************
+ * GObject code for PurpleAccountPresence
+ **************************************************************************/
+
+/* Set method for GObject properties */
+static void
+purple_account_presence_set_property(GObject *obj, guint param_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleAccountPresence *account_presence = PURPLE_ACCOUNT_PRESENCE(obj);
+ PurpleAccountPresencePrivate *priv =
+ PURPLE_ACCOUNT_PRESENCE_GET_PRIVATE(account_presence);
+
+ switch (param_id) {
+ case ACPRES_PROP_ACCOUNT:
+ priv->account = g_value_get_object(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* Get method for GObject properties */
+static void
+purple_account_presence_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleAccountPresence *account_presence = PURPLE_ACCOUNT_PRESENCE(obj);
+
+ switch (param_id) {
+ case ACPRES_PROP_ACCOUNT:
+ g_value_set_object(value,
+ purple_account_presence_get_account(account_presence));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* Called when done constructing */
+static void
+purple_account_presence_constructed(GObject *object)
+{
+ PurplePresence *presence = PURPLE_PRESENCE(object);
+ PurpleAccountPresencePrivate *priv = PURPLE_ACCOUNT_PRESENCE_GET_PRIVATE(presence);
+
+ G_OBJECT_CLASS(presence_class)->constructed(object);
+
+ PURPLE_PRESENCE_GET_PRIVATE(presence)->statuses =
+ purple_prpl_get_statuses(priv->account, presence);
+}
+
+/* Class initializer function */
+static void purple_account_presence_class_init(PurpleAccountPresenceClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ PURPLE_PRESENCE_CLASS(klass)->update_idle = purple_account_presence_update_idle;
+
+ presence_class = g_type_class_peek_parent(klass);
+
+ obj_class->constructed = purple_account_presence_constructed;
+
+ /* Setup properties */
+ obj_class->get_property = purple_account_presence_get_property;
+ obj_class->set_property = purple_account_presence_set_property;
+
+ g_type_class_add_private(klass, sizeof(PurpleAccountPresencePrivate));
+
+ ap_properties[ACPRES_PROP_ACCOUNT] = g_param_spec_object("account",
+ "Account",
+ "The account that this presence is of.", PURPLE_TYPE_ACCOUNT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, ACPRES_PROP_LAST,
+ ap_properties);
+}
+
+GType
+purple_account_presence_get_type(void)
+{
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleAccountPresenceClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_account_presence_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleAccountPresence),
+ 0,
+ NULL,
+ NULL,
+ };
+
+ type = g_type_register_static(PURPLE_TYPE_PRESENCE,
+ "PurpleAccountPresence",
+ &info, 0);
+ }
+
+ return type;
+}
+
+PurpleAccountPresence *
+purple_account_presence_new(PurpleAccount *account)
+{
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+
+ return g_object_new(PURPLE_TYPE_ACCOUNT_PRESENCE,
+ "account", account,
+ NULL);
+}
+
+/**************************************************************************
+* PurpleBuddyPresence API
+**************************************************************************/
+static void
+purple_buddy_presence_update_idle(PurplePresence *presence, gboolean old_idle)
+{
+ PurpleBuddy *buddy = purple_buddy_presence_get_buddy(PURPLE_BUDDY_PRESENCE(presence));
+ time_t current_time = time(NULL);
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+ PurpleAccount *account = purple_buddy_get_account(buddy);
+ gboolean idle = purple_presence_is_idle(presence);
+
+ if (!old_idle && idle)
+ {
+ if (purple_prefs_get_bool("/purple/logging/log_system"))
+ {
+ PurpleLog *log = purple_account_get_log(account, FALSE);
+
+ if (log != NULL)
+ {
+ char *tmp, *tmp2;
+ tmp = g_strdup_printf(_("%s became idle"),
+ purple_buddy_get_alias(buddy));
+ tmp2 = g_markup_escape_text(tmp, -1);
+ g_free(tmp);
+
+ purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
+ purple_buddy_get_alias(buddy), current_time, tmp2);
+ g_free(tmp2);
+ }
+ }
+ }
+ else if (old_idle && !idle)
+ {
+ if (purple_prefs_get_bool("/purple/logging/log_system"))
+ {
+ PurpleLog *log = purple_account_get_log(account, FALSE);
+
+ if (log != NULL)
+ {
+ char *tmp, *tmp2;
+ tmp = g_strdup_printf(_("%s became unidle"),
+ purple_buddy_get_alias(buddy));
+ tmp2 = g_markup_escape_text(tmp, -1);
+ g_free(tmp);
+
+ purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
+ purple_buddy_get_alias(buddy), current_time, tmp2);
+ g_free(tmp2);
+ }
+ }
+ }
+
+ if (old_idle != idle)
+ purple_signal_emit(purple_blist_get_handle(), "buddy-idle-changed", buddy,
+ old_idle, idle);
+
+ purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy));
+
+ /* Should this be done here? It'd perhaps make more sense to
+ * 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);
+}
+
+PurpleBuddy *
+purple_buddy_presence_get_buddy(const PurpleBuddyPresence *presence)
+{
+ PurpleBuddyPresencePrivate *priv = PURPLE_BUDDY_PRESENCE_GET_PRIVATE(presence);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->buddy;
+}
+
+/**************************************************************************
+ * GObject code for PurpleBuddyPresence
+ **************************************************************************/
+
+/* Set method for GObject properties */
+static void
+purple_buddy_presence_set_property(GObject *obj, guint param_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleBuddyPresence *buddy_presence = PURPLE_BUDDY_PRESENCE(obj);
+ PurpleBuddyPresencePrivate *priv =
+ PURPLE_BUDDY_PRESENCE_GET_PRIVATE(buddy_presence);
+
+ switch (param_id) {
+ case BUDPRES_PROP_BUDDY:
+ priv->buddy = g_value_get_object(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* Get method for GObject properties */
+static void
+purple_buddy_presence_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleBuddyPresence *buddy_presence = PURPLE_BUDDY_PRESENCE(obj);
+
+ switch (param_id) {
+ case BUDPRES_PROP_BUDDY:
+ g_value_set_object(value,
+ purple_buddy_presence_get_buddy(buddy_presence));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* Called when done constructing */
+static void
+purple_buddy_presence_constructed(GObject *object)
+{
+ PurplePresence *presence = PURPLE_PRESENCE(object);
+ PurpleBuddyPresencePrivate *priv = PURPLE_BUDDY_PRESENCE_GET_PRIVATE(presence);
+ PurpleAccount *account;
+
+ G_OBJECT_CLASS(presence_class)->constructed(object);
+
+ account = purple_buddy_get_account(priv->buddy);
+ PURPLE_PRESENCE_GET_PRIVATE(presence)->statuses =
+ purple_prpl_get_statuses(account, presence);
+}
+
+/* Class initializer function */
+static void purple_buddy_presence_class_init(PurpleBuddyPresenceClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ PURPLE_PRESENCE_CLASS(klass)->update_idle = purple_buddy_presence_update_idle;
+
+ presence_class = g_type_class_peek_parent(klass);
+
+ obj_class->constructed = purple_buddy_presence_constructed;
+
+ /* Setup properties */
+ obj_class->get_property = purple_buddy_presence_get_property;
+ obj_class->set_property = purple_buddy_presence_set_property;
+
+ g_type_class_add_private(klass, sizeof(PurpleBuddyPresencePrivate));
+
+ bp_properties[BUDPRES_PROP_BUDDY] = g_param_spec_object("buddy", "Buddy",
+ "The buddy that this presence is of.", PURPLE_TYPE_BUDDY,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, BUDPRES_PROP_LAST,
+ bp_properties);
+}
+
+GType
+purple_buddy_presence_get_type(void)
+{
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleBuddyPresenceClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_buddy_presence_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleBuddyPresence),
+ 0,
+ NULL,
+ NULL,
+ };
+
+ type = g_type_register_static(PURPLE_TYPE_PRESENCE,
+ "PurpleBuddyPresence",
+ &info, 0);
+ }
+
+ return type;
+}
+
+PurpleBuddyPresence *
+purple_buddy_presence_new(PurpleBuddy *buddy)
+{
+ g_return_val_if_fail(PURPLE_IS_BUDDY(buddy), NULL);
+
+ return g_object_new(PURPLE_TYPE_BUDDY_PRESENCE,
+ "buddy", buddy,
+ NULL);
+}
diff --git a/libpurple/presence.h b/libpurple/presence.h
new file mode 100644
index 0000000000..f4e47cdf8f
--- /dev/null
+++ b/libpurple/presence.h
@@ -0,0 +1,416 @@
+/*
+ * 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 _PURPLE_PRESENCE_H_
+#define _PURPLE_PRESENCE_H_
+/**
+ * SECTION:presence
+ * @section_id: libpurple-presence
+ * @short_description: <filename>presence.h</filename>
+ * @title: Presence Objects API
+ *
+ * This file contains the presence base type, account presence, and buddy
+ * presence API.
+ */
+
+#define PURPLE_TYPE_PRESENCE (purple_presence_get_type())
+#define PURPLE_PRESENCE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_PRESENCE, PurplePresence))
+#define PURPLE_PRESENCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_PRESENCE, PurplePresenceClass))
+#define PURPLE_IS_PRESENCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PRESENCE))
+#define PURPLE_IS_PRESENCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_PRESENCE))
+#define PURPLE_PRESENCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_PRESENCE, PurplePresenceClass))
+
+typedef struct _PurplePresence PurplePresence;
+typedef struct _PurplePresenceClass PurplePresenceClass;
+
+#define PURPLE_TYPE_ACCOUNT_PRESENCE (purple_account_presence_get_type())
+#define PURPLE_ACCOUNT_PRESENCE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_ACCOUNT_PRESENCE, PurpleAccountPresence))
+#define PURPLE_ACCOUNT_PRESENCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_ACCOUNT_PRESENCE, PurpleAccountPresenceClass))
+#define PURPLE_IS_ACCOUNT_PRESENCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_ACCOUNT_PRESENCE))
+#define PURPLE_IS_ACCOUNT_PRESENCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_ACCOUNT_PRESENCE))
+#define PURPLE_ACCOUNT_PRESENCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_ACCOUNT_PRESENCE, PurpleAccountPresenceClass))
+
+typedef struct _PurpleAccountPresence PurpleAccountPresence;
+typedef struct _PurpleAccountPresenceClass PurpleAccountPresenceClass;
+
+#define PURPLE_TYPE_BUDDY_PRESENCE (purple_buddy_presence_get_type())
+#define PURPLE_BUDDY_PRESENCE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_BUDDY_PRESENCE, PurpleBuddyPresence))
+#define PURPLE_BUDDY_PRESENCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_BUDDY_PRESENCE, PurpleBuddyPresenceClass))
+#define PURPLE_IS_BUDDY_PRESENCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_BUDDY_PRESENCE))
+#define PURPLE_IS_BUDDY_PRESENCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_BUDDY_PRESENCE))
+#define PURPLE_BUDDY_PRESENCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_BUDDY_PRESENCE, PurpleBuddyPresenceClass))
+
+typedef struct _PurpleBuddyPresence PurpleBuddyPresence;
+typedef struct _PurpleBuddyPresenceClass PurpleBuddyPresenceClass;
+
+#include "account.h"
+#include "buddylist.h"
+#include "status.h"
+
+/**
+ * PurplePresence:
+ *
+ * A PurplePresence is like a collection of PurpleStatuses (plus some
+ * other random info). For any buddy, or for any one of your accounts,
+ * or for any person with which you're chatting, you may know various
+ * amounts of information. This information is all contained in
+ * one PurplePresence. If one of your buddies is away and idle,
+ * then the presence contains the PurpleStatus for their awayness,
+ * and it contains their current idle time. PurplePresences are
+ * never saved to disk. The information they contain is only relevant
+ * for the current PurpleSession.
+ *
+ * Note: When a presence is destroyed with the last g_object_unref(), all
+ * statuses added to this list will be destroyed along with the presence.
+ */
+struct _PurplePresence
+{
+ GObject gparent;
+};
+
+/**
+ * PurplePresenceClass:
+ * @update_idle: Updates the logs and the UI when the idle state or time of the
+ * presence changes.
+ *
+ * Base class for all #PurplePresence's
+ */
+struct _PurplePresenceClass {
+ GObjectClass parent_class;
+
+ void (*update_idle)(PurplePresence *presence, gboolean old_idle);
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+/**
+ * PurpleAccountPresence:
+ *
+ * A presence for an account
+ */
+struct _PurpleAccountPresence
+{
+ PurplePresence parent;
+};
+
+/**
+ * PurpleAccountPresenceClass:
+ *
+ * Base class for all #PurpleAccountPresence's
+ */
+struct _PurpleAccountPresenceClass {
+ PurplePresenceClass parent_class;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+/**
+ * PurpleBuddyPresence:
+ *
+ * A presence for a buddy
+ */
+struct _PurpleBuddyPresence
+{
+ PurplePresence parent;
+};
+
+/**
+ * PurpleBuddyPresenceClass:
+ *
+ * Base class for all #PurpleBuddyPresence's
+ */
+struct _PurpleBuddyPresenceClass {
+ PurplePresenceClass parent_class;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+/**************************************************************************/
+/* PurpleAccountPresence API */
+/**************************************************************************/
+
+/**
+ * purple_account_presence_get_type:
+ *
+ * Returns: The #GType for the #PurpleAccountPresence object.
+ */
+GType purple_account_presence_get_type(void);
+
+/**
+ * purple_account_presence_new:
+ * @account: The account to associate with the presence.
+ *
+ * Creates a presence for an account.
+ *
+ * Returns: The new presence.
+ */
+PurpleAccountPresence *purple_account_presence_new(PurpleAccount *account);
+
+/**
+ * purple_account_presence_get_account:
+ * @presence: The presence.
+ *
+ * Returns an account presence's account.
+ *
+ * Returns: The presence's account.
+ */
+PurpleAccount *purple_account_presence_get_account(const PurpleAccountPresence *presence);
+
+/**************************************************************************/
+/* PurpleBuddyPresence API */
+/**************************************************************************/
+
+/**
+ * purple_buddy_presence_get_type:
+ *
+ * Returns: The #GType for the #PurpleBuddyPresence object.
+ */
+GType purple_buddy_presence_get_type(void);
+
+/**
+ * purple_buddy_presence_new:
+ * @buddy: The buddy to associate with the presence.
+ *
+ * Creates a presence for a buddy.
+ *
+ * Returns: The new presence.
+ */
+PurpleBuddyPresence *purple_buddy_presence_new(PurpleBuddy *buddy);
+
+/**
+ * purple_buddy_presence_get_buddy:
+ * @presence: The presence.
+ *
+ * Returns the buddy presence's buddy.
+ *
+ * Returns: The presence's buddy.
+ */
+PurpleBuddy *purple_buddy_presence_get_buddy(const PurpleBuddyPresence *presence);
+
+/**
+ * purple_buddy_presence_compare:
+ * @buddy_presence1: The first presence.
+ * @buddy_presence2: The second presence.
+ *
+ * Compares two buddy presences for availability.
+ *
+ * Returns: -1 if @buddy_presence1 is more available than @buddy_presence2.
+ * 0 if @buddy_presence1 is equal to @buddy_presence2.
+ * 1 if @buddy_presence1 is less available than @buddy_presence2.
+ */
+gint purple_buddy_presence_compare(const PurpleBuddyPresence *buddy_presence1,
+ const PurpleBuddyPresence *buddy_presence2);
+
+/**************************************************************************/
+/* PurplePresence API */
+/**************************************************************************/
+
+/**
+ * purple_presence_get_type:
+ *
+ * Returns: The #GType for the #PurplePresence object.
+ */
+GType purple_presence_get_type(void);
+
+/**
+ * purple_presence_set_status_active:
+ * @presence: The presence.
+ * @status_id: The ID of the status.
+ * @active: The active state.
+ *
+ * Sets the active state of a status in a presence.
+ *
+ * Only independent statuses can be set unactive. Normal statuses can only
+ * be set active, so if you wish to disable a status, set another
+ * non-independent status to active, or use purple_presence_switch_status().
+ */
+void purple_presence_set_status_active(PurplePresence *presence,
+ const char *status_id, gboolean active);
+
+/**
+ * purple_presence_switch_status:
+ * @presence: The presence.
+ * @status_id: The status ID to switch to.
+ *
+ * Switches the active status in a presence.
+ *
+ * This is similar to purple_presence_set_status_active(), except it won't
+ * activate independent statuses.
+ */
+void purple_presence_switch_status(PurplePresence *presence,
+ const char *status_id);
+
+/**
+ * purple_presence_set_idle:
+ * @presence: The presence.
+ * @idle: The idle state.
+ * @idle_time: The idle time, if @idle is TRUE. This
+ * is the time at which the user became idle,
+ * in seconds since the epoch. If this value is
+ * unknown then 0 should be used.
+ *
+ * Sets the idle state and time on a presence.
+ */
+void purple_presence_set_idle(PurplePresence *presence, gboolean idle,
+ time_t idle_time);
+
+/**
+ * purple_presence_set_login_time:
+ * @presence: The presence.
+ * @login_time: The login time.
+ *
+ * Sets the login time on a presence.
+ */
+void purple_presence_set_login_time(PurplePresence *presence, time_t login_time);
+
+/**
+ * purple_presence_get_statuses:
+ * @presence: The presence.
+ *
+ * Returns all the statuses in a presence.
+ *
+ * Returns: (transfer none): The statuses.
+ */
+GList *purple_presence_get_statuses(const PurplePresence *presence);
+
+/**
+ * purple_presence_get_status:
+ * @presence: The presence.
+ * @status_id: The ID of the status.
+ *
+ * Returns the status with the specified ID from a presence.
+ *
+ * Returns: The status if found, or NULL.
+ */
+PurpleStatus *purple_presence_get_status(const PurplePresence *presence,
+ const char *status_id);
+
+/**
+ * purple_presence_get_active_status:
+ * @presence: The presence.
+ *
+ * Returns the active exclusive status from a presence.
+ *
+ * Returns: The active exclusive status.
+ */
+PurpleStatus *purple_presence_get_active_status(const PurplePresence *presence);
+
+/**
+ * purple_presence_is_available:
+ * @presence: The presence.
+ *
+ * Returns whether or not a presence is available.
+ *
+ * Available presences are online and possibly invisible, but not away or idle.
+ *
+ * Returns: TRUE if the presence is available, or FALSE otherwise.
+ */
+gboolean purple_presence_is_available(const PurplePresence *presence);
+
+/**
+ * purple_presence_is_online:
+ * @presence: The presence.
+ *
+ * Returns whether or not a presence is online.
+ *
+ * Returns: TRUE if the presence is online, or FALSE otherwise.
+ */
+gboolean purple_presence_is_online(const PurplePresence *presence);
+
+/**
+ * purple_presence_is_status_active:
+ * @presence: The presence.
+ * @status_id: The ID of the status.
+ *
+ * Returns whether or not a status in a presence is active.
+ *
+ * A status is active if itself or any of its sub-statuses are active.
+ *
+ * Returns: TRUE if the status is active, or FALSE.
+ */
+gboolean purple_presence_is_status_active(const PurplePresence *presence,
+ const char *status_id);
+
+/**
+ * purple_presence_is_status_primitive_active:
+ * @presence: The presence.
+ * @primitive: The status primitive.
+ *
+ * Returns whether or not a status with the specified primitive type
+ * in a presence is active.
+ *
+ * A status is active if itself or any of its sub-statuses are active.
+ *
+ * Returns: TRUE if the status is active, or FALSE.
+ */
+gboolean purple_presence_is_status_primitive_active(
+ const PurplePresence *presence, PurpleStatusPrimitive primitive);
+
+/**
+ * purple_presence_is_idle:
+ * @presence: The presence.
+ *
+ * Returns whether or not a presence is idle.
+ *
+ * Returns: TRUE if the presence is idle, or FALSE otherwise.
+ * If the presence is offline (purple_presence_is_online()
+ * returns FALSE) then FALSE is returned.
+ */
+gboolean purple_presence_is_idle(const PurplePresence *presence);
+
+/**
+ * purple_presence_get_idle_time:
+ * @presence: The presence.
+ *
+ * Returns the presence's idle time.
+ *
+ * Returns: The presence's idle time.
+ */
+time_t purple_presence_get_idle_time(const PurplePresence *presence);
+
+/**
+ * purple_presence_get_login_time:
+ * @presence: The presence.
+ *
+ * Returns the presence's login time.
+ *
+ * Returns: The presence's login time.
+ */
+time_t purple_presence_get_login_time(const PurplePresence *presence);
+
+G_END_DECLS
+
+#endif /* _PURPLE_PRESENCE_H_ */
diff --git a/libpurple/privacy.c b/libpurple/privacy.c
deleted file mode 100644
index 00dd7e4493..0000000000
--- a/libpurple/privacy.c
+++ /dev/null
@@ -1,411 +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
- */
-#include "internal.h"
-
-#include "account.h"
-#include "privacy.h"
-#include "server.h"
-#include "util.h"
-
-static PurplePrivacyUiOps *privacy_ops = NULL;
-
-gboolean
-purple_privacy_permit_add(PurpleAccount *account, const char *who,
- gboolean local_only)
-{
- GSList *l;
- char *name;
- PurpleBuddy *buddy;
- PurpleBlistUiOps *blist_ops;
-
- g_return_val_if_fail(account != NULL, FALSE);
- g_return_val_if_fail(who != NULL, FALSE);
-
- name = g_strdup(purple_normalize(account, who));
-
- for (l = account->permit; l != NULL; l = l->next) {
- if (g_str_equal(name, l->data))
- /* This buddy already exists */
- break;
- }
-
- if (l != NULL)
- {
- /* This buddy already exists, so bail out */
- g_free(name);
- return FALSE;
- }
-
- account->permit = g_slist_append(account->permit, name);
-
- if (!local_only && purple_account_is_connected(account))
- serv_add_permit(purple_account_get_connection(account), who);
-
- if (privacy_ops != NULL && privacy_ops->permit_added != NULL)
- privacy_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);
-
- /* This lets the UI know a buddy has had its privacy setting changed */
- buddy = purple_find_buddy(account, name);
- if (buddy != NULL) {
- purple_signal_emit(purple_blist_get_handle(),
- "buddy-privacy-changed", buddy);
- }
- return TRUE;
-}
-
-gboolean
-purple_privacy_permit_remove(PurpleAccount *account, const char *who,
- gboolean local_only)
-{
- GSList *l;
- const char *name;
- PurpleBuddy *buddy;
- char *del;
- PurpleBlistUiOps *blist_ops;
-
- g_return_val_if_fail(account != NULL, FALSE);
- g_return_val_if_fail(who != NULL, FALSE);
-
- name = purple_normalize(account, who);
-
- for (l = account->permit; l != NULL; l = l->next) {
- if (g_str_equal(name, l->data))
- /* We found the buddy we were looking for */
- break;
- }
-
- if (l == NULL)
- /* We didn't find the buddy we were looking for, so bail out */
- return FALSE;
-
- /* We should not free l->data just yet. There can be occasions where
- * l->data == who. In such cases, freeing l->data here can cause crashes
- * later when who is used. */
- del = l->data;
- account->permit = g_slist_delete_link(account->permit, l);
-
- if (!local_only && purple_account_is_connected(account))
- serv_rem_permit(purple_account_get_connection(account), who);
-
- if (privacy_ops != NULL && privacy_ops->permit_removed != NULL)
- privacy_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);
-
- buddy = purple_find_buddy(account, name);
- if (buddy != NULL) {
- purple_signal_emit(purple_blist_get_handle(),
- "buddy-privacy-changed", buddy);
- }
- g_free(del);
- return TRUE;
-}
-
-gboolean
-purple_privacy_deny_add(PurpleAccount *account, const char *who,
- gboolean local_only)
-{
- GSList *l;
- char *name;
- PurpleBuddy *buddy;
- PurpleBlistUiOps *blist_ops;
-
- g_return_val_if_fail(account != NULL, FALSE);
- g_return_val_if_fail(who != NULL, FALSE);
-
- name = g_strdup(purple_normalize(account, who));
-
- for (l = account->deny; l != NULL; l = l->next) {
- if (g_str_equal(name, l->data))
- /* This buddy already exists */
- break;
- }
-
- if (l != NULL)
- {
- /* This buddy already exists, so bail out */
- g_free(name);
- return FALSE;
- }
-
- account->deny = g_slist_append(account->deny, name);
-
- if (!local_only && purple_account_is_connected(account))
- serv_add_deny(purple_account_get_connection(account), who);
-
- if (privacy_ops != NULL && privacy_ops->deny_added != NULL)
- privacy_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);
-
- buddy = purple_find_buddy(account, name);
- if (buddy != NULL) {
- purple_signal_emit(purple_blist_get_handle(),
- "buddy-privacy-changed", buddy);
- }
- return TRUE;
-}
-
-gboolean
-purple_privacy_deny_remove(PurpleAccount *account, const char *who,
- gboolean local_only)
-{
- GSList *l;
- const char *normalized;
- char *name;
- PurpleBuddy *buddy;
- PurpleBlistUiOps *blist_ops;
-
- g_return_val_if_fail(account != NULL, FALSE);
- g_return_val_if_fail(who != NULL, FALSE);
-
- normalized = purple_normalize(account, who);
-
- for (l = account->deny; l != NULL; l = l->next) {
- if (g_str_equal(normalized, l->data))
- /* We found the buddy we were looking for */
- break;
- }
-
- if (l == NULL)
- /* We didn't find the buddy we were looking for, so bail out */
- return FALSE;
-
- buddy = purple_find_buddy(account, normalized);
-
- name = l->data;
- account->deny = g_slist_delete_link(account->deny, l);
-
- if (!local_only && purple_account_is_connected(account))
- serv_rem_deny(purple_account_get_connection(account), name);
-
- if (privacy_ops != NULL && privacy_ops->deny_removed != NULL)
- privacy_ops->deny_removed(account, who);
-
- if (buddy != NULL) {
- purple_signal_emit(purple_blist_get_handle(),
- "buddy-privacy-changed", buddy);
- }
-
- g_free(name);
-
- blist_ops = purple_blist_get_ui_ops();
- if (blist_ops != NULL && blist_ops->save_account != NULL)
- blist_ops->save_account(account);
-
- return TRUE;
-}
-
-/**
- * This makes sure your permit list contains all buddies from your
- * buddy list and ONLY buddies from your buddy list.
- */
-static void
-add_all_buddies_to_permit_list(PurpleAccount *account, gboolean local)
-{
- GSList *list;
-
- /* Remove anyone in the permit list who is not in the buddylist */
- for (list = account->permit; list != NULL; ) {
- char *person = list->data;
- list = list->next;
- if (!purple_find_buddy(account, person))
- purple_privacy_permit_remove(account, person, local);
- }
-
- /* Now make sure everyone in the buddylist is in the permit list */
- list = purple_find_buddies(account, NULL);
- while (list != NULL)
- {
- PurpleBuddy *buddy = list->data;
- const gchar *name = purple_buddy_get_name(buddy);
-
- if (!g_slist_find_custom(account->permit, name, (GCompareFunc)g_utf8_collate))
- purple_privacy_permit_add(account, name, local);
- list = g_slist_delete_link(list, list);
- }
-}
-
-/*
- * TODO: All callers of this function pass in FALSE for local and
- * restore and I don't understand when you would ever want to
- * use TRUE for either of them. I think both parameters could
- * safely be removed in the next major version bump.
- */
-void
-purple_privacy_allow(PurpleAccount *account, const char *who, gboolean local,
- gboolean restore)
-{
- GSList *list;
- PurplePrivacyType type = account->perm_deny;
-
- switch (account->perm_deny) {
- case PURPLE_PRIVACY_ALLOW_ALL:
- return;
- case PURPLE_PRIVACY_ALLOW_USERS:
- purple_privacy_permit_add(account, who, local);
- break;
- case PURPLE_PRIVACY_DENY_USERS:
- purple_privacy_deny_remove(account, who, local);
- break;
- case PURPLE_PRIVACY_DENY_ALL:
- if (!restore) {
- /* Empty the allow-list. */
- const char *norm = purple_normalize(account, who);
- for (list = account->permit; list != NULL;) {
- char *person = list->data;
- list = list->next;
- if (!purple_strequal(norm, person))
- purple_privacy_permit_remove(account, person, local);
- }
- }
- purple_privacy_permit_add(account, who, local);
- account->perm_deny = PURPLE_PRIVACY_ALLOW_USERS;
- break;
- case PURPLE_PRIVACY_ALLOW_BUDDYLIST:
- if (!purple_find_buddy(account, who)) {
- add_all_buddies_to_permit_list(account, local);
- purple_privacy_permit_add(account, who, local);
- account->perm_deny = PURPLE_PRIVACY_ALLOW_USERS;
- }
- break;
- default:
- g_return_if_reached();
- }
-
- /* Notify the server if the privacy setting was changed */
- if (type != account->perm_deny && purple_account_is_connected(account))
- serv_set_permit_deny(purple_account_get_connection(account));
-}
-
-/*
- * TODO: All callers of this function pass in FALSE for local and
- * restore and I don't understand when you would ever want to
- * use TRUE for either of them. I think both parameters could
- * safely be removed in the next major version bump.
- */
-void
-purple_privacy_deny(PurpleAccount *account, const char *who, gboolean local,
- gboolean restore)
-{
- GSList *list;
- PurplePrivacyType type = account->perm_deny;
-
- switch (account->perm_deny) {
- case PURPLE_PRIVACY_ALLOW_ALL:
- if (!restore) {
- /* Empty the deny-list. */
- const char *norm = purple_normalize(account, who);
- for (list = account->deny; list != NULL; ) {
- char *person = list->data;
- list = list->next;
- if (!purple_strequal(norm, person))
- purple_privacy_deny_remove(account, person, local);
- }
- }
- purple_privacy_deny_add(account, who, local);
- account->perm_deny = PURPLE_PRIVACY_DENY_USERS;
- break;
- case PURPLE_PRIVACY_ALLOW_USERS:
- purple_privacy_permit_remove(account, who, local);
- break;
- case PURPLE_PRIVACY_DENY_USERS:
- purple_privacy_deny_add(account, who, local);
- break;
- case PURPLE_PRIVACY_DENY_ALL:
- break;
- case PURPLE_PRIVACY_ALLOW_BUDDYLIST:
- if (purple_find_buddy(account, who)) {
- add_all_buddies_to_permit_list(account, local);
- purple_privacy_permit_remove(account, who, local);
- account->perm_deny = PURPLE_PRIVACY_ALLOW_USERS;
- }
- break;
- default:
- g_return_if_reached();
- }
-
- /* Notify the server if the privacy setting was changed */
- if (type != account->perm_deny && purple_account_is_connected(account))
- serv_set_permit_deny(purple_account_get_connection(account));
-}
-
-gboolean
-purple_privacy_check(PurpleAccount *account, const char *who)
-{
- GSList *list;
-
- switch (account->perm_deny) {
- case PURPLE_PRIVACY_ALLOW_ALL:
- return TRUE;
-
- case PURPLE_PRIVACY_DENY_ALL:
- return FALSE;
-
- case PURPLE_PRIVACY_ALLOW_USERS:
- who = purple_normalize(account, who);
- for (list=account->permit; list!=NULL; list=list->next) {
- if (g_str_equal(who, list->data))
- return TRUE;
- }
- return FALSE;
-
- case PURPLE_PRIVACY_DENY_USERS:
- who = purple_normalize(account, who);
- for (list=account->deny; list!=NULL; list=list->next) {
- if (g_str_equal(who, list->data))
- return FALSE;
- }
- return TRUE;
-
- case PURPLE_PRIVACY_ALLOW_BUDDYLIST:
- return (purple_find_buddy(account, who) != NULL);
-
- default:
- g_return_val_if_reached(TRUE);
- }
-}
-
-void
-purple_privacy_set_ui_ops(PurplePrivacyUiOps *ops)
-{
- privacy_ops = ops;
-}
-
-PurplePrivacyUiOps *
-purple_privacy_get_ui_ops(void)
-{
- return privacy_ops;
-}
-
-void
-purple_privacy_init(void)
-{
-}
diff --git a/libpurple/privacy.h b/libpurple/privacy.h
deleted file mode 100644
index 0250541128..0000000000
--- a/libpurple/privacy.h
+++ /dev/null
@@ -1,194 +0,0 @@
-/**
- * @file privacy.h Privacy API
- * @ingroup core
- */
-
-/* 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 _PURPLE_PRIVACY_H_
-#define _PURPLE_PRIVACY_H_
-
-/**
- * Privacy data types.
- */
-typedef enum _PurplePrivacyType
-{
- PURPLE_PRIVACY_ALLOW_ALL = 1,
- PURPLE_PRIVACY_DENY_ALL,
- PURPLE_PRIVACY_ALLOW_USERS,
- PURPLE_PRIVACY_DENY_USERS,
- PURPLE_PRIVACY_ALLOW_BUDDYLIST
-} PurplePrivacyType;
-
-#include "account.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Privacy core/UI operations.
- */
-typedef struct
-{
- void (*permit_added)(PurpleAccount *account, const char *name);
- void (*permit_removed)(PurpleAccount *account, const char *name);
- void (*deny_added)(PurpleAccount *account, const char *name);
- void (*deny_removed)(PurpleAccount *account, const char *name);
-
- void (*_purple_reserved1)(void);
- void (*_purple_reserved2)(void);
- void (*_purple_reserved3)(void);
- void (*_purple_reserved4)(void);
-} PurplePrivacyUiOps;
-
-/**
- * Adds a user to the account's permit list.
- *
- * @param account The account.
- * @param name The name of the user to add to the list.
- * @param local_only If TRUE, only the local list is updated, and not
- * the server.
- *
- * @return TRUE if the user was added successfully, or @c FALSE otherwise.
- */
-gboolean purple_privacy_permit_add(PurpleAccount *account, const char *name,
- gboolean local_only);
-
-/**
- * Removes a user from the account's permit list.
- *
- * @param account The account.
- * @param name The name of the user to add to the list.
- * @param local_only If TRUE, only the local list is updated, and not
- * the server.
- *
- * @return TRUE if the user was removed successfully, or @c FALSE otherwise.
- */
-gboolean purple_privacy_permit_remove(PurpleAccount *account, const char *name,
- gboolean local_only);
-
-/**
- * Adds a user to the account's deny list.
- *
- * @param account The account.
- * @param name The name of the user to add to the list.
- * @param local_only If TRUE, only the local list is updated, and not
- * the server.
- *
- * @return TRUE if the user was added successfully, or @c FALSE otherwise.
- */
-gboolean purple_privacy_deny_add(PurpleAccount *account, const char *name,
- gboolean local_only);
-
-/**
- * Removes a user from the account's deny list.
- *
- * @param account The account.
- * @param name The name of the user to add to the list.
- * @param local_only If TRUE, only the local list is updated, and not
- * the server.
- *
- * @return TRUE if the user was removed successfully, or @c FALSE otherwise.
- */
-gboolean purple_privacy_deny_remove(PurpleAccount *account, const char *name,
- gboolean local_only);
-
-/**
- * Allow a user to send messages. If current privacy setting for the account is:
- * PURPLE_PRIVACY_ALLOW_USERS: The user is added to the allow-list.
- * PURPLE_PRIVACY_DENY_USERS : The user is removed from the deny-list.
- * PURPLE_PRIVACY_ALLOW_ALL : No changes made.
- * PURPLE_PRIVACY_DENY_ALL : The privacy setting is changed to
- * PURPLE_PRIVACY_ALLOW_USERS and the user
- * is added to the allow-list.
- * PURPLE_PRIVACY_ALLOW_BUDDYLIST: No changes made if the user is already in
- * the buddy-list. Otherwise the setting is
- * changed to PURPLE_PRIVACY_ALLOW_USERS, all the
- * buddies are added to the allow-list, and the
- * user is also added to the allow-list.
- *
- * @param account The account.
- * @param who The name of the user.
- * @param local Whether the change is local-only.
- * @param restore Should the previous allow/deny list be restored if the
- * privacy setting is changed.
- */
-void purple_privacy_allow(PurpleAccount *account, const char *who, gboolean local,
- gboolean restore);
-
-/**
- * Block messages from a user. If current privacy setting for the account is:
- * PURPLE_PRIVACY_ALLOW_USERS: The user is removed from the allow-list.
- * PURPLE_PRIVACY_DENY_USERS : The user is added to the deny-list.
- * PURPLE_PRIVACY_DENY_ALL : No changes made.
- * PURPLE_PRIVACY_ALLOW_ALL : The privacy setting is changed to
- * PURPLE_PRIVACY_DENY_USERS and the user is
- * added to the deny-list.
- * PURPLE_PRIVACY_ALLOW_BUDDYLIST: If the user is not in the buddy-list,
- * then no changes made. Otherwise, the setting
- * is changed to PURPLE_PRIVACY_ALLOW_USERS, all
- * the buddies are added to the allow-list, and
- * this user is removed from the list.
- *
- * @param account The account.
- * @param who The name of the user.
- * @param local Whether the change is local-only.
- * @param restore Should the previous allow/deny list be restored if the
- * privacy setting is changed.
- */
-void purple_privacy_deny(PurpleAccount *account, const char *who, gboolean local,
- gboolean restore);
-
-/**
- * Check the privacy-setting for a user.
- *
- * @param account The account.
- * @param who The name of the user.
- *
- * @return @c FALSE if the specified account's privacy settings block the user or @c TRUE otherwise. The meaning of "block" is protocol-dependent and generally relates to status and/or sending of messages.
- */
-gboolean purple_privacy_check(PurpleAccount *account, const char *who);
-
-/**
- * Sets the UI operations structure for the privacy subsystem.
- *
- * @param ops The UI operations structure.
- */
-void purple_privacy_set_ui_ops(PurplePrivacyUiOps *ops);
-
-/**
- * Returns the UI operations structure for the privacy subsystem.
- *
- * @return The UI operations structure.
- */
-PurplePrivacyUiOps *purple_privacy_get_ui_ops(void);
-
-/**
- * Initializes the privacy subsystem.
- */
-void purple_privacy_init(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _PURPLE_PRIVACY_H_ */
diff --git a/libpurple/protocols/Makefile.am b/libpurple/protocols/Makefile.am
index 7fea4bd5fc..429db4e87f 100644
--- a/libpurple/protocols/Makefile.am
+++ b/libpurple/protocols/Makefile.am
@@ -1,5 +1,5 @@
EXTRA_DIST = Makefile.mingw
-DIST_SUBDIRS = bonjour gg irc jabber msn myspace mxit novell null oscar sametime silc silc10 simple yahoo zephyr
+DIST_SUBDIRS = bonjour gg irc jabber msn mxit novell null oscar sametime silc simple yahoo zephyr
SUBDIRS = $(DYNAMIC_PRPLS) $(STATIC_PRPLS)
diff --git a/libpurple/protocols/Makefile.mingw b/libpurple/protocols/Makefile.mingw
index 8b6ca1a4ed..f0871c3dc2 100644
--- a/libpurple/protocols/Makefile.mingw
+++ b/libpurple/protocols/Makefile.mingw
@@ -8,22 +8,22 @@
PIDGIN_TREE_TOP := ../..
include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
-SUBDIRS = gg irc jabber msn mxit novell null oscar sametime silc simple yahoo bonjour myspace
+SUBDIRS = gg irc jabber msn mxit novell null oscar sametime silc simple yahoo bonjour
.PHONY: all install clean
all:
- for subdir in $(SUBDIRS); do \
+ $(MAKE_at) for subdir in $(SUBDIRS); do \
$(MAKE) -C $$subdir -f $(MINGW_MAKEFILE) || exit 1; \
done;
install: all
- for subdir in $(SUBDIRS); do \
+ $(MAKE_at) for subdir in $(SUBDIRS); do \
$(MAKE) -C $$subdir -f $(MINGW_MAKEFILE) install || exit 1; \
done;
clean:
- for subdir in $(SUBDIRS); do \
+ $(MAKE_at) for subdir in $(SUBDIRS); do \
$(MAKE) -C $$subdir -f $(MINGW_MAKEFILE) clean || exit 1; \
done;
diff --git a/libpurple/protocols/bonjour/Makefile.am b/libpurple/protocols/bonjour/Makefile.am
index 1a9606cbbf..b7845ade21 100644
--- a/libpurple/protocols/bonjour/Makefile.am
+++ b/libpurple/protocols/bonjour/Makefile.am
@@ -1,10 +1,11 @@
EXTRA_DIST = \
+ mdns_avahi.c \
mdns_win32.c \
dns_sd_proxy.c \
dns_sd_proxy.h \
Makefile.mingw
-pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+pkgdir = @PURPLE_PLUGINDIR@
BONJOURSOURCES = \
bonjour.c \
@@ -13,7 +14,6 @@ BONJOURSOURCES = \
buddy.h \
jabber.c \
jabber.h \
- mdns_avahi.c \
mdns_common.c \
mdns_common.h \
mdns_interface.h \
@@ -23,9 +23,18 @@ BONJOURSOURCES = \
bonjour_ft.c \
bonjour_ft.h
+if IS_WIN32
+BONJOURSOURCES += \
+ dns_sd_proxy.c \
+ mdns_win32.c
+else
+BONJOURSOURCES += \
+ mdns_avahi.c
+endif
+
AM_CFLAGS = $(st)
-libbonjour_la_LDFLAGS = -module -avoid-version
+libbonjour_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
if STATIC_BONJOUR
@@ -33,21 +42,25 @@ st = -DPURPLE_STATIC_PRPL
noinst_LTLIBRARIES = libbonjour.la
libbonjour_la_SOURCES = $(BONJOURSOURCES)
libbonjour_la_CFLAGS = $(AM_CFLAGS)
-libbonjour_la_LIBADD = $(AVAHI_LIBS)
else
st =
pkg_LTLIBRARIES = libbonjour.la
libbonjour_la_SOURCES = $(BONJOURSOURCES)
-libbonjour_la_LIBADD = $(GLIB_LIBS) $(LIBXML_LIBS) $(AVAHI_LIBS)
endif
+libbonjour_la_LIBADD = @PURPLE_LIBS@ $(LIBXML_LIBS) $(AVAHI_LIBS)
+
+if IS_WIN32
+libbonjour_la_LIBADD += -lnetapi32
+endif
+
AM_CPPFLAGS = \
-I$(top_srcdir)/libpurple \
-I$(top_builddir)/libpurple \
$(GLIB_CFLAGS) \
$(DEBUG_CFLAGS) \
$(LIBXML_CFLAGS) \
- $(AVAHI_CFLAGS) \ No newline at end of file
+ $(AVAHI_CFLAGS)
diff --git a/libpurple/protocols/bonjour/Makefile.mingw b/libpurple/protocols/bonjour/Makefile.mingw
index f0f3138f71..2e97374d26 100644
--- a/libpurple/protocols/bonjour/Makefile.mingw
+++ b/libpurple/protocols/bonjour/Makefile.mingw
@@ -58,6 +58,7 @@ OBJECTS = $(C_SRC:%.c=%.o)
##
LIBS = \
-lglib-2.0 \
+ -lgobject-2.0 \
-lws2_32 \
-lintl \
-lnetapi32 \
diff --git a/libpurple/protocols/bonjour/bonjour.c b/libpurple/protocols/bonjour/bonjour.c
index ac527c07e9..bc26c6136d 100644
--- a/libpurple/protocols/bonjour/bonjour.c
+++ b/libpurple/protocols/bonjour/bonjour.c
@@ -51,7 +51,7 @@ const char *
bonjour_get_jid(PurpleAccount *account)
{
PurpleConnection *conn = purple_account_get_connection(account);
- BonjourData *bd = conn->proto_data;
+ BonjourData *bd = purple_connection_get_protocol_data(conn);
return bd->jid;
}
@@ -68,11 +68,11 @@ bonjour_removeallfromlocal(PurpleConnection *conn, PurpleGroup *bonjour_group)
/* Go through and remove all buddies that belong to this account */
for (cnode = purple_blist_node_get_first_child((PurpleBlistNode *) bonjour_group); cnode; cnode = cnodenext) {
cnodenext = purple_blist_node_get_sibling_next(cnode);
- if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
+ if (!PURPLE_IS_CONTACT(cnode))
continue;
for (bnode = purple_blist_node_get_first_child(cnode); bnode; bnode = bnodenext) {
bnodenext = purple_blist_node_get_sibling_next(bnode);
- if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
+ if (!PURPLE_IS_BUDDY(bnode))
continue;
buddy = (PurpleBuddy *) bnode;
if (purple_buddy_get_account(buddy) != account)
@@ -94,16 +94,19 @@ bonjour_login(PurpleAccount *account)
#ifdef _WIN32
if (!dns_sd_available()) {
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_OTHER_ERROR,
_("Unable to find Apple's \"Bonjour for Windows\" toolkit, see "
- "http://d.pidgin.im/BonjourWindows for more information."));
+ "https://developer.pidgin.im/BonjourWindows for more "
+ "information."));
return;
}
#endif /* _WIN32 */
- gc->flags |= PURPLE_CONNECTION_HTML;
- gc->proto_data = bd = g_new0(BonjourData, 1);
+ purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_HTML |
+ PURPLE_CONNECTION_FLAG_NO_IMAGES);
+ bd = g_new0(BonjourData, 1);
+ purple_connection_set_protocol_data(gc, bd);
/* Start waiting for jabber connections (iChat style) */
bd->jabber_data = g_new0(BonjourJabber, 1);
@@ -114,7 +117,7 @@ bonjour_login(PurpleAccount *account)
if (bonjour_jabber_start(bd->jabber_data) == -1) {
/* Send a message about the connection error */
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to listen for incoming IM connections"));
return;
@@ -141,7 +144,7 @@ bonjour_login(PurpleAccount *account)
bd->dns_sd_data->account = account;
if (!bonjour_dns_sd_start(bd->dns_sd_data))
{
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to establish connection with the local mDNS server. Is it running?"));
return;
@@ -150,16 +153,16 @@ bonjour_login(PurpleAccount *account)
bonjour_dns_sd_update_buddy_icon(bd->dns_sd_data);
/* Show the buddy list by telling Purple we have already connected */
- purple_connection_set_state(gc, PURPLE_CONNECTED);
+ purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
}
static void
bonjour_close(PurpleConnection *connection)
{
PurpleGroup *bonjour_group;
- BonjourData *bd = connection->proto_data;
+ BonjourData *bd = purple_connection_get_protocol_data(connection);
- bonjour_group = purple_find_group(BONJOUR_GROUP_NAME);
+ bonjour_group = purple_blist_find_group(BONJOUR_GROUP_NAME);
/* Remove all the bonjour buddies */
bonjour_removeallfromlocal(connection, bonjour_group);
@@ -192,7 +195,7 @@ bonjour_close(PurpleConnection *connection)
if (bd != NULL)
g_free(bd->jid);
g_free(bd);
- connection->proto_data = NULL;
+ purple_connection_set_protocol_data(connection, NULL);
}
static const char *
@@ -202,12 +205,16 @@ bonjour_list_icon(PurpleAccount *account, PurpleBuddy *buddy)
}
static int
-bonjour_send_im(PurpleConnection *connection, const char *to, const char *msg, PurpleMessageFlags flags)
+bonjour_send_im(PurpleConnection *connection, PurpleMessage *msg)
{
- if(!to || !msg)
+ BonjourData *bd = purple_connection_get_protocol_data(connection);
+
+ if (purple_message_is_empty(msg) || !purple_message_get_recipient(msg))
return 0;
- return bonjour_jabber_send_message(((BonjourData*)(connection->proto_data))->jabber_data, to, msg);
+ return bonjour_jabber_send_message(bd->jabber_data,
+ purple_message_get_recipient(msg),
+ purple_message_get_contents(msg));
}
static void
@@ -220,7 +227,7 @@ bonjour_set_status(PurpleAccount *account, PurpleStatus *status)
gchar *stripped;
gc = purple_account_get_connection(account);
- bd = gc->proto_data;
+ bd = purple_connection_get_protocol_data(gc);
presence = purple_account_get_presence(account);
message = purple_status_get_attr_string(status, "message");
@@ -253,7 +260,7 @@ bonjour_set_status(PurpleAccount *account, PurpleStatus *status)
* if there is no add_buddy callback.
*/
static void
-bonjour_fake_add_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group) {
+bonjour_fake_add_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group, const char *message) {
purple_debug_error("bonjour", "Buddy '%s' manually added; removing. "
"Bonjour buddies must be discovered and not manually added.\n",
purple_buddy_get_name(buddy));
@@ -285,14 +292,14 @@ bonjour_status_types(PurpleAccount *account)
BONJOUR_STATUS_ID_AVAILABLE,
NULL, TRUE, TRUE, FALSE,
"message", _("Message"),
- purple_value_new(PURPLE_TYPE_STRING), NULL);
+ purple_value_new(G_TYPE_STRING), NULL);
status_types = g_list_append(status_types, type);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY,
BONJOUR_STATUS_ID_AWAY,
NULL, TRUE, TRUE, FALSE,
"message", _("Message"),
- purple_value_new(PURPLE_TYPE_STRING), NULL);
+ purple_value_new(G_TYPE_STRING), NULL);
status_types = g_list_append(status_types, type);
type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
@@ -306,7 +313,7 @@ bonjour_status_types(PurpleAccount *account)
static void
bonjour_convo_closed(PurpleConnection *connection, const char *who)
{
- PurpleBuddy *buddy = purple_find_buddy(connection->account, who);
+ PurpleBuddy *buddy = purple_blist_find_buddy(purple_connection_get_account(connection), who);
BonjourBuddy *bb;
if (buddy == NULL || (bb = purple_buddy_get_protocol_data(buddy)) == NULL)
@@ -322,10 +329,10 @@ bonjour_convo_closed(PurpleConnection *connection, const char *who)
bb->conversation = NULL;
}
-static
-void bonjour_set_buddy_icon(PurpleConnection *conn, PurpleStoredImage *img)
+static void
+bonjour_set_buddy_icon(PurpleConnection *conn, PurpleImage *img)
{
- BonjourData *bd = conn->proto_data;
+ BonjourData *bd = purple_connection_get_protocol_data(conn);
bonjour_dns_sd_update_buddy_icon(bd->dns_sd_data);
}
@@ -371,9 +378,12 @@ bonjour_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboole
else
status_description = purple_status_get_name(status);
- purple_notify_user_info_add_pair(user_info, _("Status"), status_description);
- if (message != NULL)
- purple_notify_user_info_add_pair(user_info, _("Message"), message);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), status_description);
+ if (message != NULL) {
+ /* 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, _("Message"), message);
+ }
if (bb == NULL) {
purple_debug_error("bonjour", "Got tooltip request for a buddy without protocol data.\n");
@@ -382,43 +392,56 @@ bonjour_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboole
/* Only show first/last name if there is a nickname set (to avoid duplication) */
if (bb->nick != NULL && *bb->nick != '\0') {
- if (bb->first != NULL && *bb->first != '\0')
- purple_notify_user_info_add_pair(user_info, _("First name"), bb->first);
- if (bb->last != NULL && *bb->last != '\0')
- purple_notify_user_info_add_pair(user_info, _("Last name"), bb->last);
+ if (bb->first != NULL && *bb->first != '\0') {
+ /* 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, _("First name"), bb->first);
+ }
+ if (bb->last != NULL && *bb->last != '\0') {
+ /* 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, _("Last name"), bb->last);
+ }
}
- if (bb->email != NULL && *bb->email != '\0')
- purple_notify_user_info_add_pair(user_info, _("Email"), bb->email);
+ if (bb->email != NULL && *bb->email != '\0') {
+ /* 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, _("Email"), bb->email);
+ }
- if (bb->AIM != NULL && *bb->AIM != '\0')
- purple_notify_user_info_add_pair(user_info, _("AIM Account"), bb->AIM);
+ if (bb->AIM != NULL && *bb->AIM != '\0') {
+ /* 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, _("AIM Account"), bb->AIM);
+ }
- if (bb->jid != NULL && *bb->jid != '\0')
- purple_notify_user_info_add_pair(user_info, _("XMPP Account"), bb->jid);
+ if (bb->jid != NULL && *bb->jid != '\0') {
+ /* 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, _("XMPP Account"), bb->jid);
+ }
}
static void
-bonjour_do_group_change(PurpleBuddy *buddy, const char *new_group) {
- PurpleBlistNodeFlags oldflags;
-
+bonjour_do_group_change(PurpleBuddy *buddy, const char *new_group)
+{
if (buddy == NULL)
return;
- oldflags = purple_blist_node_get_flags((PurpleBlistNode *)buddy);
-
/* If we're moving them out of the bonjour group, make them persistent */
if (purple_strequal(new_group, BONJOUR_GROUP_NAME))
- purple_blist_node_set_flags((PurpleBlistNode *)buddy, oldflags | PURPLE_BLIST_NODE_FLAG_NO_SAVE);
+ purple_blist_node_set_transient(PURPLE_BLIST_NODE(buddy), TRUE);
else
- purple_blist_node_set_flags((PurpleBlistNode *)buddy, oldflags ^ PURPLE_BLIST_NODE_FLAG_NO_SAVE);
+ purple_blist_node_set_transient(PURPLE_BLIST_NODE(buddy),
+ !purple_blist_node_is_transient(PURPLE_BLIST_NODE(buddy)));
}
static void
bonjour_group_buddy(PurpleConnection *connection, const char *who, const char *old_group, const char *new_group)
{
- PurpleBuddy *buddy = purple_find_buddy(connection->account, who);
+ PurpleBuddy *buddy = purple_blist_find_buddy(purple_connection_get_account(connection), who);
bonjour_do_group_change(buddy, new_group);
@@ -443,11 +466,17 @@ bonjour_rename_group(PurpleConnection *connection, const char *old_name, PurpleG
static gboolean
bonjour_can_receive_file(PurpleConnection *connection, const char *who)
{
- PurpleBuddy *buddy = purple_find_buddy(connection->account, who);
+ PurpleBuddy *buddy = purple_blist_find_buddy(purple_connection_get_account(connection), who);
return (buddy != NULL && purple_buddy_get_protocol_data(buddy) != NULL);
}
+static gssize
+bonjour_get_max_message_size(PurpleConversation *conv)
+{
+ return -1; /* 5MB successfully tested. */
+}
+
static gboolean
plugin_unload(PurplePlugin *plugin)
{
@@ -463,6 +492,7 @@ static PurplePlugin *my_protocol = NULL;
static PurplePluginProtocolInfo prpl_info =
{
+ sizeof(PurplePluginProtocolInfo), /* struct_size */
OPT_PROTO_NO_PASSWORD,
NULL, /* user_splits */
NULL, /* protocol_options */
@@ -498,12 +528,10 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* get_chat_name */
NULL, /* chat_invite */
NULL, /* chat_leave */
- NULL, /* chat_whisper */
NULL, /* chat_send */
NULL, /* keepalive */
NULL, /* register_user */
NULL, /* get_cb_info */
- NULL, /* get_cb_away */
NULL, /* alias_buddy */
bonjour_group_buddy, /* group_buddy */
bonjour_rename_group, /* rename_group */
@@ -528,15 +556,13 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* unregister_user */
NULL, /* send_attention */
NULL, /* get_attention_types */
- sizeof(PurplePluginProtocolInfo), /* struct_size */
NULL, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* get_media_caps */
NULL, /* get_moods */
NULL, /* set_public_alias */
NULL, /* get_public_alias */
- NULL, /* add_buddy_with_invite */
- NULL /* add_buddies_with_invite */
+ bonjour_get_max_message_size /* get_max_message_size */
};
static PurplePluginInfo info =
@@ -672,6 +698,8 @@ initialize_default_account_values(void)
{
#ifndef _WIN32
struct passwd *info;
+#else
+ GThread *lookup_thread;
#endif
const char *fullname = NULL, *splitpoint, *tmp;
gchar *conv = NULL;
@@ -687,7 +715,11 @@ initialize_default_account_values(void)
fullname = NULL;
#else
/* The Win32 username lookup functions are synchronous so we do it in a thread */
- g_thread_create(_win32_name_lookup_thread, NULL, FALSE, NULL);
+ lookup_thread = g_thread_try_new("bonjour dns thread", _win32_name_lookup_thread, NULL, NULL);
+ if (lookup_thread)
+ g_thread_unref(lookup_thread);
+ else
+ purple_debug_fatal("bonjour", "failed to create lookup thread\n");
#endif
/* Make sure fullname is valid UTF-8. If not, try to convert it. */
diff --git a/libpurple/protocols/bonjour/bonjour_ft.c b/libpurple/protocols/bonjour/bonjour_ft.c
index b3beb0ba52..094eb6e9a6 100644
--- a/libpurple/protocols/bonjour/bonjour_ft.c
+++ b/libpurple/protocols/bonjour/bonjour_ft.c
@@ -24,11 +24,11 @@
#include "debug.h"
#include "notify.h"
#include "proxy.h"
-#include "ft.h"
+#include "xfer.h"
#include "buddy.h"
#include "bonjour.h"
#include "bonjour_ft.h"
-#include "cipher.h"
+#include "ciphers/sha1hash.h"
static void
bonjour_bytestreams_init(PurpleXfer *xfer);
@@ -38,7 +38,7 @@ static void
bonjour_xfer_init(PurpleXfer *xfer);
static void
bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *sid, const char *from,
- const int filesize, const char *filename, int option);
+ const goffset filesize, const char *filename, int option);
static void bonjour_free_xfer(PurpleXfer *xfer);
/* Look for specific xfer handle */
@@ -47,7 +47,7 @@ static unsigned int next_id = 0;
static void
xep_ft_si_reject(BonjourData *bd, const char *id, const char *to, const char *error_code, const char *error_type)
{
- xmlnode *error_node;
+ PurpleXmlNode *error_node;
XepIq *iq;
g_return_if_fail(error_code != NULL);
@@ -62,21 +62,21 @@ xep_ft_si_reject(BonjourData *bd, const char *id, const char *to, const char *er
if(iq == NULL)
return;
- error_node = xmlnode_new_child(iq->node, "error");
- xmlnode_set_attrib(error_node, "code", error_code);
- xmlnode_set_attrib(error_node, "type", error_type);
+ error_node = purple_xmlnode_new_child(iq->node, "error");
+ purple_xmlnode_set_attrib(error_node, "code", error_code);
+ purple_xmlnode_set_attrib(error_node, "type", error_type);
/* TODO: Make this better */
if (!strcmp(error_code, "403")) {
- xmlnode *tmp_node = xmlnode_new_child(error_node, "forbidden");
- xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
+ PurpleXmlNode *tmp_node = purple_xmlnode_new_child(error_node, "forbidden");
+ purple_xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
- tmp_node = xmlnode_new_child(error_node, "text");
- xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
- xmlnode_insert_data(tmp_node, "Offer Declined", -1);
+ tmp_node = purple_xmlnode_new_child(error_node, "text");
+ purple_xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
+ purple_xmlnode_insert_data(tmp_node, "Offer Declined", -1);
} else if (!strcmp(error_code, "404")) {
- xmlnode *tmp_node = xmlnode_new_child(error_node, "item-not-found");
- xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
+ PurpleXmlNode *tmp_node = purple_xmlnode_new_child(error_node, "item-not-found");
+ purple_xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
}
xep_iq_send_and_free(iq);
@@ -90,12 +90,12 @@ static void bonjour_xfer_cancel_send(PurpleXfer *xfer)
static void bonjour_xfer_request_denied(PurpleXfer *xfer)
{
- XepXfer *xf = xfer->data;
+ XepXfer *xf = purple_xfer_get_protocol_data(xfer);
purple_debug_info("bonjour", "Bonjour-xfer-request-denied.\n");
if(xf)
- xep_ft_si_reject(xf->data, xf->sid, xfer->who, "403", "cancel");
+ xep_ft_si_reject(xf->data, xf->sid, purple_xfer_get_remote_user(xfer), "403", "cancel");
bonjour_free_xfer(xfer);
}
@@ -134,10 +134,10 @@ static void bonjour_xfer_end(PurpleXfer *xfer)
/* We can't allow the server side to close the connection until the client is complete,
* otherwise there is a RST resulting in an error on the client side */
- if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && purple_xfer_is_completed(xfer)) {
+ if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND && purple_xfer_is_completed(xfer)) {
struct socket_cleanup *sc = g_new0(struct socket_cleanup, 1);
- sc->fd = xfer->fd;
- xfer->fd = -1;
+ sc->fd = purple_xfer_get_fd(xfer);
+ purple_xfer_set_fd(xfer, -1);
sc->handle = purple_input_add(sc->fd, PURPLE_INPUT_READ,
_wait_for_socket_close, sc);
}
@@ -162,11 +162,11 @@ bonjour_si_xfer_find(BonjourData *bd, const char *sid, const char *from)
xfer = xfers->data;
if(xfer == NULL)
break;
- xf = xfer->data;
+ xf = purple_xfer_get_protocol_data(xfer);
if(xf == NULL)
break;
- if(xf->sid && xfer->who && !strcmp(xf->sid, sid) &&
- !strcmp(xfer->who, from))
+ if(xf->sid && purple_xfer_get_remote_user(xfer) && !strcmp(xf->sid, sid) &&
+ !strcmp(purple_xfer_get_remote_user(xfer), from))
return xfer;
}
@@ -178,9 +178,9 @@ bonjour_si_xfer_find(BonjourData *bd, const char *sid, const char *from)
static void
xep_ft_si_offer(PurpleXfer *xfer, const gchar *to)
{
- xmlnode *si_node, *feature, *field, *file, *x;
+ PurpleXmlNode *si_node, *feature, *field, *file, *x;
XepIq *iq;
- XepXfer *xf = xfer->data;
+ XepXfer *xf = purple_xfer_get_protocol_data(xfer);
BonjourData *bd = NULL;
char buf[32];
@@ -201,55 +201,55 @@ xep_ft_si_offer(PurpleXfer *xfer, const gchar *to)
return;
/*Construct Stream initialization offer message.*/
- si_node = xmlnode_new_child(iq->node, "si");
- xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si");
- xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer");
+ si_node = purple_xmlnode_new_child(iq->node, "si");
+ purple_xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si");
+ purple_xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer");
g_free(xf->sid);
xf->sid = g_strdup(xf->iq_id);
- xmlnode_set_attrib(si_node, "id", xf->sid);
+ purple_xmlnode_set_attrib(si_node, "id", xf->sid);
- file = xmlnode_new_child(si_node, "file");
- xmlnode_set_namespace(file, "http://jabber.org/protocol/si/profile/file-transfer");
- xmlnode_set_attrib(file, "name", xfer->filename);
- g_snprintf(buf, sizeof(buf), "%" G_GSIZE_FORMAT, xfer->size);
- xmlnode_set_attrib(file, "size", buf);
+ file = purple_xmlnode_new_child(si_node, "file");
+ purple_xmlnode_set_namespace(file, "http://jabber.org/protocol/si/profile/file-transfer");
+ purple_xmlnode_set_attrib(file, "name", purple_xfer_get_filename(xfer));
+ g_snprintf(buf, sizeof(buf), "%" G_GOFFSET_FORMAT, purple_xfer_get_size(xfer));
+ purple_xmlnode_set_attrib(file, "size", buf);
- feature = xmlnode_new_child(si_node, "feature");
- xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
+ feature = purple_xmlnode_new_child(si_node, "feature");
+ purple_xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
- x = xmlnode_new_child(feature, "x");
- xmlnode_set_namespace(x, "jabber:x:data");
- xmlnode_set_attrib(x, "type", "form");
+ x = purple_xmlnode_new_child(feature, "x");
+ purple_xmlnode_set_namespace(x, "jabber:x:data");
+ purple_xmlnode_set_attrib(x, "type", "form");
- field = xmlnode_new_child(x, "field");
- xmlnode_set_attrib(field, "var", "stream-method");
- xmlnode_set_attrib(field, "type", "list-single");
+ field = purple_xmlnode_new_child(x, "field");
+ purple_xmlnode_set_attrib(field, "var", "stream-method");
+ purple_xmlnode_set_attrib(field, "type", "list-single");
if (xf->mode & XEP_BYTESTREAMS) {
- xmlnode *option = xmlnode_new_child(field, "option");
- xmlnode *value = xmlnode_new_child(option, "value");
- xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
+ PurpleXmlNode *option = purple_xmlnode_new_child(field, "option");
+ PurpleXmlNode *value = purple_xmlnode_new_child(option, "value");
+ purple_xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
}
if (xf->mode & XEP_IBB) {
- xmlnode *option = xmlnode_new_child(field, "option");
- xmlnode *value = xmlnode_new_child(option, "value");
- xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1);
+ PurpleXmlNode *option = purple_xmlnode_new_child(field, "option");
+ PurpleXmlNode *value = purple_xmlnode_new_child(option, "value");
+ purple_xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1);
}
xep_iq_send_and_free(iq);
}
static void
-xep_ft_si_result(PurpleXfer *xfer, char *to)
+xep_ft_si_result(PurpleXfer *xfer, const char *to)
{
- xmlnode *si_node, *feature, *field, *value, *x;
+ PurpleXmlNode *si_node, *feature, *field, *value, *x;
XepIq *iq;
XepXfer *xf;
BonjourData *bd;
if(!to || !xfer)
return;
- xf = xfer->data;
+ xf = purple_xfer_get_protocol_data(xfer);
if(!xf)
return;
@@ -260,22 +260,22 @@ xep_ft_si_result(PurpleXfer *xfer, char *to)
if(iq == NULL)
return;
- si_node = xmlnode_new_child(iq->node, "si");
- xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si");
- /*xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer");*/
+ si_node = purple_xmlnode_new_child(iq->node, "si");
+ purple_xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si");
+ /*purple_xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer");*/
- feature = xmlnode_new_child(si_node, "feature");
- xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
+ feature = purple_xmlnode_new_child(si_node, "feature");
+ purple_xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
- x = xmlnode_new_child(feature, "x");
- xmlnode_set_namespace(x, "jabber:x:data");
- xmlnode_set_attrib(x, "type", "submit");
+ x = purple_xmlnode_new_child(feature, "x");
+ purple_xmlnode_set_namespace(x, "jabber:x:data");
+ purple_xmlnode_set_attrib(x, "type", "submit");
- field = xmlnode_new_child(x, "field");
- xmlnode_set_attrib(field, "var", "stream-method");
+ field = purple_xmlnode_new_child(x, "field");
+ purple_xmlnode_set_attrib(field, "var", "stream-method");
- value = xmlnode_new_child(field, "value");
- xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
+ value = purple_xmlnode_new_child(field, "value");
+ purple_xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
xep_iq_send_and_free(iq);
}
@@ -289,14 +289,14 @@ xep_ft_si_result(PurpleXfer *xfer, char *to)
* @param node The node to free the tree from
*/
static void
-xmlnode_free_tree(xmlnode *node)
+purple_xmlnode_free_tree(PurpleXmlNode *node)
{
g_return_if_fail(node != NULL);
- while(xmlnode_get_parent(node))
- node = xmlnode_get_parent(node);
+ while(purple_xmlnode_get_parent(node))
+ node = purple_xmlnode_get_parent(node);
- xmlnode_free(node);
+ purple_xmlnode_free(node);
}
static void
@@ -309,14 +309,14 @@ bonjour_free_xfer(PurpleXfer *xfer)
return;
}
- purple_debug_info("bonjour", "bonjour-free-xfer-%p.\n", xfer);
+ purple_debug_misc("bonjour", "bonjour-free-xfer-%p.\n", xfer);
- xf = (XepXfer*)xfer->data;
+ 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_info("bonjour", "B free xfer from lists(%p).\n", bd->xfer_lists);
+ 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);
@@ -330,13 +330,13 @@ bonjour_free_xfer(PurpleXfer *xfer)
g_free(xf->buddy_ip);
g_free(xf->sid);
- xmlnode_free_tree(xf->streamhost);
+ purple_xmlnode_free_tree(xf->streamhost);
g_free(xf);
- xfer->data = NULL;
+ purple_xfer_set_protocol_data(xfer, NULL);
}
- purple_debug_info("bonjour", "Need close socket=%d.\n", xfer->fd);
+ purple_debug_misc("bonjour", "Need close socket.\n");
}
PurpleXfer *
@@ -350,13 +350,14 @@ bonjour_new_xfer(PurpleConnection *gc, const char *who)
return NULL;
purple_debug_info("bonjour", "Bonjour-new-xfer to %s.\n", who);
- bd = (BonjourData*) gc->proto_data;
+ bd = purple_connection_get_protocol_data(gc);
if(bd == NULL)
return NULL;
/* Build the file transfer handle */
- xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who);
- xfer->data = xep_xfer = g_new0(XepXfer, 1);
+ 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->data = bd;
purple_debug_info("bonjour", "Bonjour-new-xfer bd=%p data=%p.\n", bd, xep_xfer->data);
@@ -401,13 +402,13 @@ bonjour_xfer_init(PurpleXfer *xfer)
BonjourBuddy *bb;
XepXfer *xf;
- xf = (XepXfer*)xfer->data;
+ xf = purple_xfer_get_protocol_data(xfer);
if(xf == NULL)
return;
purple_debug_info("bonjour", "Bonjour-xfer-init.\n");
- buddy = purple_find_buddy(xfer->account, xfer->who);
+ buddy = purple_blist_find_buddy(purple_xfer_get_account(xfer), purple_xfer_get_remote_user(xfer));
/* this buddy is offline. */
if (buddy == NULL || (bb = purple_buddy_get_protocol_data(buddy)) == NULL)
return;
@@ -415,19 +416,19 @@ bonjour_xfer_init(PurpleXfer *xfer)
/* Assume it is the first IP. We could do something like keep track of which one is in use or something. */
if (bb->ips)
xf->buddy_ip = g_strdup(bb->ips->data);
- if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
+ if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND) {
/* initiate file transfer, send SI offer. */
- purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_SEND.\n");
- xep_ft_si_offer(xfer, xfer->who);
+ purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_TYPE_SEND.\n");
+ xep_ft_si_offer(xfer, purple_xfer_get_remote_user(xfer));
} else {
/* accept file transfer request, send SI result. */
- xep_ft_si_result(xfer, xfer->who);
- purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_RECEIVE.\n");
+ xep_ft_si_result(xfer, purple_xfer_get_remote_user(xfer));
+ purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_TYPE_RECEIVE.\n");
}
}
void
-xep_si_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb)
+xep_si_parse(PurpleConnection *pc, PurpleXmlNode *packet, PurpleBuddy *pb)
{
const char *type, *id;
BonjourData *bd;
@@ -438,7 +439,7 @@ xep_si_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb)
g_return_if_fail(packet != NULL);
g_return_if_fail(pb != NULL);
- bd = (BonjourData*) pc->proto_data;
+ bd = purple_connection_get_protocol_data(pc);
if(bd == NULL)
return;
@@ -446,30 +447,30 @@ xep_si_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb)
name = purple_buddy_get_name(pb);
- type = xmlnode_get_attrib(packet, "type");
- id = xmlnode_get_attrib(packet, "id");
+ type = purple_xmlnode_get_attrib(packet, "type");
+ id = purple_xmlnode_get_attrib(packet, "id");
if(!type)
return;
if(!strcmp(type, "set")) {
const char *profile;
- xmlnode *si;
+ PurpleXmlNode *si;
gboolean parsed_receive = FALSE;
- si = xmlnode_get_child(packet, "si");
+ si = purple_xmlnode_get_child(packet, "si");
purple_debug_info("bonjour", "si offer Message type - SET.\n");
- if (si && (profile = xmlnode_get_attrib(si, "profile"))
+ if (si && (profile = purple_xmlnode_get_attrib(si, "profile"))
&& !strcmp(profile, "http://jabber.org/protocol/si/profile/file-transfer")) {
const char *filename = NULL, *filesize_str = NULL;
goffset filesize = 0;
- xmlnode *file;
+ PurpleXmlNode *file;
- const char *sid = xmlnode_get_attrib(si, "id");
+ const char *sid = purple_xmlnode_get_attrib(si, "id");
- if ((file = xmlnode_get_child(si, "file"))) {
- filename = xmlnode_get_attrib(file, "name");
- if((filesize_str = xmlnode_get_attrib(file, "size")))
+ if ((file = purple_xmlnode_get_child(si, "file"))) {
+ filename = purple_xmlnode_get_attrib(file, "name");
+ if((filesize_str = purple_xmlnode_get_attrib(file, "size")))
filesize = g_ascii_strtoll(filesize_str, NULL, 10);
}
@@ -540,6 +541,7 @@ xep_cmp_addr(const char *host, const char *buddy_ip)
{
#if defined(AF_INET6) && defined(HAVE_GETADDRINFO)
struct addrinfo hint, *res = NULL;
+ common_sockaddr_t addr;
int ret;
memset(&hint, 0, sizeof(hint));
@@ -549,9 +551,11 @@ xep_cmp_addr(const char *host, const char *buddy_ip)
ret = getaddrinfo(host, NULL, &hint, &res);
if(ret)
goto out;
+ memcpy(&addr, res->ai_addr, sizeof(addr));
- if(res->ai_family != AF_INET6 ||
- !IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr)) {
+ if (res->ai_family != AF_INET6 ||
+ !IN6_IS_ADDR_LINKLOCAL(&addr.in6.sin6_addr))
+ {
freeaddrinfo(res);
goto out;
}
@@ -584,13 +588,13 @@ xep_addr_differ(const char *buddy_ip, const char *host)
* @return A pointer to the new, cloned twin if successful
* or NULL otherwise.
*/
-static xmlnode *
-xmlnode_insert_twin_copy(xmlnode *node) {
- xmlnode *copy;
+static PurpleXmlNode *
+purple_xmlnode_insert_twin_copy(PurpleXmlNode *node) {
+ PurpleXmlNode *copy;
g_return_val_if_fail(node != NULL, NULL);
- copy = xmlnode_copy(node);
+ copy = purple_xmlnode_copy(node);
g_return_val_if_fail(copy != NULL, NULL);
copy->next = node->next;
@@ -619,9 +623,9 @@ xmlnode_insert_twin_copy(xmlnode *node) {
* Otherwise returns FALSE.
*/
static gboolean
-add_ipv6_link_local_ifaces(xmlnode *cur_streamhost, const char *host,
+add_ipv6_link_local_ifaces(PurpleXmlNode *cur_streamhost, const char *host,
const PurpleBuddy *pb) {
- xmlnode *new_streamhost = NULL;
+ PurpleXmlNode *new_streamhost = NULL;
struct in6_addr in6_addr;
BonjourBuddy *bb;
GSList *ip_elem;
@@ -636,10 +640,10 @@ add_ipv6_link_local_ifaces(xmlnode *cur_streamhost, const char *host,
for (ip_elem = bb->ips;
(ip_elem = g_slist_find_custom(ip_elem, host, (GCompareFunc)&xep_addr_differ));
ip_elem = ip_elem->next) {
- purple_debug_info("bonjour", "Inserting an xmlnode twin copy for %s with new host address %s\n",
+ purple_debug_info("bonjour", "Inserting an PurpleXmlNode twin copy for %s with new host address %s\n",
host, (char*)ip_elem->data);
- new_streamhost = xmlnode_insert_twin_copy(cur_streamhost);
- xmlnode_set_attrib(new_streamhost, "host", ip_elem->data);
+ new_streamhost = purple_xmlnode_insert_twin_copy(cur_streamhost);
+ purple_xmlnode_set_attrib(new_streamhost, "host", ip_elem->data);
}
if (!new_streamhost)
@@ -650,19 +654,18 @@ add_ipv6_link_local_ifaces(xmlnode *cur_streamhost, const char *host,
}
static gboolean
-__xep_bytestreams_parse(PurpleBuddy *pb, PurpleXfer *xfer, xmlnode *streamhost,
+__xep_bytestreams_parse(PurpleBuddy *pb, PurpleXfer *xfer, PurpleXmlNode *streamhost,
const char *iq_id)
{
char *tmp_iq_id;
const char *jid, *host, *port;
int portnum;
- XepXfer *xf = NULL;
+ XepXfer *xf = purple_xfer_get_protocol_data(xfer);
- xf = (XepXfer*)xfer->data;
- for(; streamhost; streamhost = xmlnode_get_next_twin(streamhost)) {
- if(!(jid = xmlnode_get_attrib(streamhost, "jid")) ||
- !(host = xmlnode_get_attrib(streamhost, "host")) ||
- !(port = xmlnode_get_attrib(streamhost, "port")) ||
+ for(; streamhost; streamhost = purple_xmlnode_get_next_twin(streamhost)) {
+ if(!(jid = purple_xmlnode_get_attrib(streamhost, "jid")) ||
+ !(host = purple_xmlnode_get_attrib(streamhost, "host")) ||
+ !(port = purple_xmlnode_get_attrib(streamhost, "port")) ||
!(portnum = atoi(port))) {
purple_debug_info("bonjour", "bytestream offer Message parse error.\n");
continue;
@@ -694,10 +697,10 @@ __xep_bytestreams_parse(PurpleBuddy *pb, PurpleXfer *xfer, xmlnode *streamhost,
}
void
-xep_bytestreams_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb)
+xep_bytestreams_parse(PurpleConnection *pc, PurpleXmlNode *packet, PurpleBuddy *pb)
{
const char *type, *from, *iq_id, *sid;
- xmlnode *query, *streamhost;
+ PurpleXmlNode *query, *streamhost;
BonjourData *bd;
PurpleXfer *xfer;
@@ -705,19 +708,19 @@ xep_bytestreams_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb)
g_return_if_fail(packet != NULL);
g_return_if_fail(pb != NULL);
- bd = (BonjourData*) pc->proto_data;
+ bd = purple_connection_get_protocol_data(pc);
if(bd == NULL)
return;
purple_debug_info("bonjour", "xep-bytestreams-parse.\n");
- type = xmlnode_get_attrib(packet, "type");
+ type = purple_xmlnode_get_attrib(packet, "type");
from = purple_buddy_get_name(pb);
- query = xmlnode_get_child(packet,"query");
+ query = purple_xmlnode_get_child(packet,"query");
if(!type)
return;
- query = xmlnode_copy(query);
+ query = purple_xmlnode_copy(query);
if (!query)
return;
@@ -728,11 +731,11 @@ xep_bytestreams_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb)
purple_debug_info("bonjour", "bytestream offer Message type - SET.\n");
- iq_id = xmlnode_get_attrib(packet, "id");
+ iq_id = purple_xmlnode_get_attrib(packet, "id");
- sid = xmlnode_get_attrib(query, "sid");
+ sid = purple_xmlnode_get_attrib(query, "sid");
xfer = bonjour_si_xfer_find(bd, sid, from);
- streamhost = xmlnode_get_child(query, "streamhost");
+ streamhost = purple_xmlnode_get_child(query, "streamhost");
if(xfer && streamhost && __xep_bytestreams_parse(pb, xfer, streamhost, iq_id))
return; /* success */
@@ -740,12 +743,12 @@ xep_bytestreams_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb)
purple_debug_error("bonjour", "Didn't find an acceptable streamhost.\n");
if (iq_id && xfer != NULL)
- xep_ft_si_reject(bd, iq_id, xfer->who, "404", "cancel");
+ xep_ft_si_reject(bd, iq_id, purple_xfer_get_remote_user(xfer), "404", "cancel");
}
static void
bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *sid, const char *from,
- const int filesize, const char *filename, int option)
+ const goffset filesize, const char *filename, int option)
{
PurpleXfer *xfer;
XepXfer *xf;
@@ -754,15 +757,16 @@ bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *sid, cons
if(pc == NULL || id == NULL || from == NULL)
return;
- bd = (BonjourData*) pc->proto_data;
+ bd = purple_connection_get_protocol_data(pc);
if(bd == NULL)
return;
purple_debug_info("bonjour", "bonjour-xfer-receive.\n");
/* Build the file transfer handle */
- xfer = purple_xfer_new(pc->account, PURPLE_XFER_RECEIVE, from);
- xfer->data = xf = g_new0(XepXfer, 1);
+ 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->data = bd;
purple_xfer_set_filename(xfer, filename);
xf->iq_id = g_strdup(id);
@@ -784,7 +788,7 @@ static void
bonjour_sock5_request_cb(gpointer data, gint source, PurpleInputCondition cond)
{
PurpleXfer *xfer = data;
- XepXfer *xf = xfer->data;
+ XepXfer *xf = purple_xfer_get_protocol_data(xfer);
int acceptfd;
int len = 0;
@@ -802,8 +806,6 @@ bonjour_sock5_request_cb(gpointer data, gint source, PurpleInputCondition cond)
/* This should cancel the ft */
purple_debug_error("bonjour", "Error accepting incoming SOCKS5 connection. (%d)\n", errno);
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
close(source);
purple_xfer_cancel_remote(xfer);
return;
@@ -811,29 +813,26 @@ bonjour_sock5_request_cb(gpointer data, gint source, PurpleInputCondition cond)
purple_debug_info("bonjour", "Accepted SOCKS5 ft connection - fd=%d\n", acceptfd);
_purple_network_set_common_socket_flags(acceptfd);
- purple_input_remove(xfer->watcher);
+ purple_input_remove(purple_xfer_get_watcher(xfer));
close(source);
- xfer->watcher = purple_input_add(acceptfd, PURPLE_INPUT_READ,
- bonjour_sock5_request_cb, xfer);
+ purple_xfer_set_watcher(xfer, purple_input_add(acceptfd, PURPLE_INPUT_READ,
+ bonjour_sock5_request_cb, xfer));
xf->sock5_req_state++;
xf->rxlen = 0;
}
break;
case 0x01:
- xfer->fd = source;
+ purple_xfer_set_fd(xfer, source);
len = read(source, xf->rx_buf + xf->rxlen, 3);
if(len < 0 && errno == EAGAIN)
return;
else if(len <= 0){
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
- close(source);
purple_xfer_cancel_remote(xfer);
return;
} else {
- purple_input_remove(xfer->watcher);
- xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
- bonjour_sock5_request_cb, xfer);
+ purple_input_remove(purple_xfer_get_watcher(xfer));
+ purple_xfer_set_watcher(xfer, purple_input_add(source, PURPLE_INPUT_WRITE,
+ bonjour_sock5_request_cb, xfer));
xf->sock5_req_state++;
xf->rxlen = 0;
bonjour_sock5_request_cb(xfer, source, PURPLE_INPUT_WRITE);
@@ -846,15 +845,13 @@ bonjour_sock5_request_cb(gpointer data, gint source, PurpleInputCondition cond)
if (len < 0 && errno == EAGAIN)
return;
else if (len < 0) {
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
close(source);
purple_xfer_cancel_remote(xfer);
return;
} else {
- purple_input_remove(xfer->watcher);
- xfer->watcher = purple_input_add(source, PURPLE_INPUT_READ,
- bonjour_sock5_request_cb, xfer);
+ purple_input_remove(purple_xfer_get_watcher(xfer));
+ purple_xfer_set_watcher(xfer, purple_input_add(source, PURPLE_INPUT_READ,
+ bonjour_sock5_request_cb, xfer));
xf->sock5_req_state++;
xf->rxlen = 0;
}
@@ -863,9 +860,9 @@ bonjour_sock5_request_cb(gpointer data, gint source, PurpleInputCondition cond)
len = read(source, xf->rx_buf + xf->rxlen, 20);
if(len<=0){
} else {
- purple_input_remove(xfer->watcher);
- xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
- bonjour_sock5_request_cb, xfer);
+ purple_input_remove(purple_xfer_get_watcher(xfer));
+ purple_xfer_set_watcher(xfer, purple_input_add(source, PURPLE_INPUT_WRITE,
+ bonjour_sock5_request_cb, xfer));
xf->sock5_req_state++;
xf->rxlen = 0;
bonjour_sock5_request_cb(xfer, source, PURPLE_INPUT_WRITE);
@@ -884,14 +881,12 @@ bonjour_sock5_request_cb(gpointer data, gint source, PurpleInputCondition cond)
if (len < 0 && errno == EAGAIN) {
return;
} else if (len < 0) {
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
close(source);
purple_xfer_cancel_remote(xfer);
return;
} else {
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
+ purple_input_remove(purple_xfer_get_watcher(xfer));
+ purple_xfer_set_watcher(xfer, 0);
xf->rxlen = 0;
/*close(source);*/
purple_xfer_start(xfer, source, NULL, -1);
@@ -909,7 +904,7 @@ bonjour_bytestreams_listen(int sock, gpointer data)
PurpleXfer *xfer = data;
XepXfer *xf;
XepIq *iq;
- xmlnode *query, *streamhost;
+ PurpleXmlNode *query, *streamhost;
gchar *port;
GSList *local_ips;
BonjourData *bd;
@@ -920,30 +915,30 @@ bonjour_bytestreams_listen(int sock, gpointer data)
return;
}
- xfer->watcher = purple_input_add(sock, PURPLE_INPUT_READ,
- bonjour_sock5_request_cb, xfer);
- xf = (XepXfer*)xfer->data;
+ 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->listen_data = NULL;
bd = xf->data;
- iq = xep_iq_new(bd, XEP_IQ_SET, xfer->who, bonjour_get_jid(bd->jabber_data->account), xf->sid);
+ iq = xep_iq_new(bd, XEP_IQ_SET, purple_xfer_get_remote_user(xfer), bonjour_get_jid(bd->jabber_data->account), xf->sid);
- query = xmlnode_new_child(iq->node, "query");
- xmlnode_set_namespace(query, "http://jabber.org/protocol/bytestreams");
- xmlnode_set_attrib(query, "sid", xf->sid);
- xmlnode_set_attrib(query, "mode", "tcp");
+ query = purple_xmlnode_new_child(iq->node, "query");
+ purple_xmlnode_set_namespace(query, "http://jabber.org/protocol/bytestreams");
+ purple_xmlnode_set_attrib(query, "sid", xf->sid);
+ purple_xmlnode_set_attrib(query, "mode", "tcp");
- xfer->local_port = purple_network_get_port_from_fd(sock);
+ purple_xfer_set_local_port(xfer, purple_network_get_port_from_fd(sock));
local_ips = bonjour_jabber_get_local_ips(sock);
- port = g_strdup_printf("%hu", (guint16)purple_xfer_get_local_port(xfer));
+ port = g_strdup_printf("%hu", purple_xfer_get_local_port(xfer));
while(local_ips) {
- streamhost = xmlnode_new_child(query, "streamhost");
- xmlnode_set_attrib(streamhost, "jid", xf->sid);
- xmlnode_set_attrib(streamhost, "host", local_ips->data);
- xmlnode_set_attrib(streamhost, "port", port);
+ streamhost = purple_xmlnode_new_child(query, "streamhost");
+ purple_xmlnode_set_attrib(streamhost, "jid", xf->sid);
+ purple_xmlnode_set_attrib(streamhost, "host", local_ips->data);
+ purple_xmlnode_set_attrib(streamhost, "port", port);
g_free(local_ips->data);
local_ips = g_slist_delete_link(local_ips, local_ips);
}
@@ -960,12 +955,10 @@ bonjour_bytestreams_init(PurpleXfer *xfer)
return;
purple_debug_info("bonjour", "Bonjour-bytestreams-init.\n");
- xf = xfer->data;
+ xf = purple_xfer_get_protocol_data(xfer);
- purple_network_listen_map_external(FALSE);
- xf->listen_data = purple_network_listen_range(0, 0, SOCK_STREAM,
+ xf->listen_data = purple_network_listen_range(0, 0, AF_UNSPEC, SOCK_STREAM, FALSE,
bonjour_bytestreams_listen, xfer);
- purple_network_listen_map_external(TRUE);
if (xf->listen_data == NULL)
purple_xfer_cancel_local(xfer);
@@ -976,9 +969,9 @@ static void
bonjour_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_message)
{
PurpleXfer *xfer = data;
- XepXfer *xf = xfer->data;
+ XepXfer *xf = purple_xfer_get_protocol_data(xfer);
XepIq *iq;
- xmlnode *q_node, *tmp_node;
+ PurpleXmlNode *q_node, *tmp_node;
BonjourData *bd;
gboolean ret = FALSE;
@@ -988,7 +981,7 @@ bonjour_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_me
purple_debug_error("bonjour", "Error connecting via SOCKS5 to %s - %s\n",
xf->proxy_host, error_message ? error_message : "(null)");
- tmp_node = xmlnode_get_next_twin(xf->streamhost);
+ tmp_node = purple_xmlnode_get_next_twin(xf->streamhost);
ret = __xep_bytestreams_parse(xf->pb, xfer, tmp_node, xf->iq_id);
if (!ret) {
@@ -1006,11 +999,11 @@ bonjour_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_me
/* Here, start the file transfer.*/
/* Notify Initiator of Connection */
- iq = xep_iq_new(bd, XEP_IQ_RESULT, xfer->who, bonjour_get_jid(bd->jabber_data->account), xf->iq_id);
- q_node = xmlnode_new_child(iq->node, "query");
- xmlnode_set_namespace(q_node, "http://jabber.org/protocol/bytestreams");
- tmp_node = xmlnode_new_child(q_node, "streamhost-used");
- xmlnode_set_attrib(tmp_node, "jid", xf->jid);
+ iq = xep_iq_new(bd, XEP_IQ_RESULT, purple_xfer_get_remote_user(xfer), bonjour_get_jid(bd->jabber_data->account), xf->iq_id);
+ q_node = purple_xmlnode_new_child(iq->node, "query");
+ purple_xmlnode_set_namespace(q_node, "http://jabber.org/protocol/bytestreams");
+ tmp_node = purple_xmlnode_new_child(q_node, "streamhost-used");
+ purple_xmlnode_set_attrib(tmp_node, "jid", xf->jid);
xep_iq_send_and_free(iq);
purple_xfer_start(xfer, source, NULL, -1);
@@ -1021,6 +1014,7 @@ bonjour_bytestreams_connect(PurpleXfer *xfer)
{
PurpleBuddy *pb;
PurpleAccount *account = NULL;
+ PurpleHash *hash;
XepXfer *xf;
char dstaddr[41];
const gchar *name = NULL;
@@ -1033,7 +1027,7 @@ bonjour_bytestreams_connect(PurpleXfer *xfer)
purple_debug_info("bonjour", "bonjour-bytestreams-connect.\n");
- xf = (XepXfer*)xfer->data;
+ xf = purple_xfer_get_protocol_data(xfer);
if(!xf)
return;
@@ -1042,8 +1036,12 @@ bonjour_bytestreams_connect(PurpleXfer *xfer)
account = purple_buddy_get_account(pb);
p = g_strdup_printf("%s%s%s", xf->sid, name, bonjour_get_jid(account));
- purple_cipher_digest_region("sha1", (guchar *)p, strlen(p),
- sizeof(hashval), hashval, NULL);
+
+ hash = purple_sha1_hash_new();
+ purple_hash_append(hash, (guchar *)p, strlen(p));
+ purple_hash_digest(hash, hashval, sizeof(hashval));
+ g_object_unref(G_OBJECT(hash));
+
g_free(p);
memset(dstaddr, 0, 41);
@@ -1052,7 +1050,7 @@ bonjour_bytestreams_connect(PurpleXfer *xfer)
snprintf(p, 3, "%02x", hashval[i]);
xf->proxy_info = purple_proxy_info_new();
- purple_proxy_info_set_type(xf->proxy_info, PURPLE_PROXY_SOCKS5);
+ purple_proxy_info_set_proxy_type(xf->proxy_info, PURPLE_PROXY_SOCKS5);
purple_proxy_info_set_host(xf->proxy_info, xf->proxy_host);
purple_proxy_info_set_port(xf->proxy_info, xf->proxy_port);
xf->proxy_connection = purple_proxy_connect_socks5_account(
@@ -1063,7 +1061,7 @@ bonjour_bytestreams_connect(PurpleXfer *xfer)
bonjour_bytestreams_connect_cb, xfer);
if(xf->proxy_connection == NULL) {
- xep_ft_si_reject(xf->data, xf->iq_id, xfer->who, "404", "cancel");
+ xep_ft_si_reject(xf->data, xf->iq_id, purple_xfer_get_remote_user(xfer), "404", "cancel");
/* Cancel the connection */
purple_xfer_cancel_local(xfer);
}
diff --git a/libpurple/protocols/bonjour/bonjour_ft.h b/libpurple/protocols/bonjour/bonjour_ft.h
index 8effca94dc..6f8d62464f 100644
--- a/libpurple/protocols/bonjour/bonjour_ft.h
+++ b/libpurple/protocols/bonjour/bonjour_ft.h
@@ -50,7 +50,7 @@ struct _XepXfer
char *jid;
char *proxy_host;
int proxy_port;
- xmlnode *streamhost;
+ PurpleXmlNode *streamhost;
PurpleBuddy *pb;
};
@@ -71,7 +71,7 @@ PurpleXfer *bonjour_new_xfer(PurpleConnection *gc, const char *who);
*/
void bonjour_send_file(PurpleConnection *gc, const char *who, const char *file);
-void xep_si_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb);
+void xep_si_parse(PurpleConnection *pc, PurpleXmlNode *packet, PurpleBuddy *pb);
void
-xep_bytestreams_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb);
+xep_bytestreams_parse(PurpleConnection *pc, PurpleXmlNode *packet, PurpleBuddy *pb);
#endif
diff --git a/libpurple/protocols/bonjour/buddy.c b/libpurple/protocols/bonjour/buddy.c
index 890a7e1b09..d33eb70206 100644
--- a/libpurple/protocols/bonjour/buddy.c
+++ b/libpurple/protocols/bonjour/buddy.c
@@ -20,7 +20,7 @@
#include "internal.h"
#include "buddy.h"
#include "account.h"
-#include "blist.h"
+#include "buddylist.h"
#include "bonjour.h"
#include "mdns_interface.h"
#include "debug.h"
@@ -141,7 +141,7 @@ bonjour_buddy_add_to_purple(BonjourBuddy *bonjour_buddy, PurpleBuddy *buddy)
*/
/* Make sure the Bonjour group exists in our buddy list */
- group = purple_find_group(BONJOUR_GROUP_NAME); /* Use the buddy's domain, instead? */
+ group = purple_blist_find_group(BONJOUR_GROUP_NAME); /* Use the buddy's domain, instead? */
if (group == NULL) {
group = purple_group_new(BONJOUR_GROUP_NAME);
purple_blist_add_group(group, NULL);
@@ -149,11 +149,11 @@ bonjour_buddy_add_to_purple(BonjourBuddy *bonjour_buddy, PurpleBuddy *buddy)
/* Make sure the buddy exists in our buddy list */
if (buddy == NULL)
- buddy = purple_find_buddy(account, bonjour_buddy->name);
+ buddy = purple_blist_find_buddy(account, bonjour_buddy->name);
if (buddy == NULL) {
buddy = purple_buddy_new(account, bonjour_buddy->name, NULL);
- purple_blist_node_set_flags((PurpleBlistNode *)buddy, PURPLE_BLIST_NODE_FLAG_NO_SAVE);
+ purple_blist_node_set_transient(PURPLE_BLIST_NODE(buddy), TRUE);
purple_blist_add_buddy(buddy, NULL, group, NULL);
}
@@ -162,7 +162,7 @@ bonjour_buddy_add_to_purple(BonjourBuddy *bonjour_buddy, PurpleBuddy *buddy)
/* Create the alias for the buddy using the first and the last name */
if (bonjour_buddy->nick && *bonjour_buddy->nick)
- serv_got_alias(purple_account_get_connection(account), name, bonjour_buddy->nick);
+ purple_serv_got_alias(purple_account_get_connection(account), name, bonjour_buddy->nick);
else {
gchar *alias = NULL;
const char *first, *last;
@@ -173,7 +173,7 @@ bonjour_buddy_add_to_purple(BonjourBuddy *bonjour_buddy, PurpleBuddy *buddy)
(first && *first ? first : ""),
(first && *first && last && *last ? " " : ""),
(last && *last ? last : ""));
- serv_got_alias(purple_account_get_connection(account), name, alias);
+ purple_serv_got_alias(purple_account_get_connection(account), name, alias);
g_free(alias);
}
@@ -207,14 +207,14 @@ bonjour_buddy_add_to_purple(BonjourBuddy *bonjour_buddy, PurpleBuddy *buddy)
* If the buddy is being saved, mark as offline, otherwise delete
*/
void bonjour_buddy_signed_off(PurpleBuddy *pb) {
- if (PURPLE_BLIST_NODE_SHOULD_SAVE(pb)) {
+ if (purple_blist_node_is_transient(PURPLE_BLIST_NODE(pb))) {
+ purple_account_remove_buddy(purple_buddy_get_account(pb), pb, NULL);
+ purple_blist_remove_buddy(pb);
+ } else {
purple_prpl_got_user_status(purple_buddy_get_account(pb),
purple_buddy_get_name(pb), "offline", NULL);
bonjour_buddy_delete(purple_buddy_get_protocol_data(pb));
purple_buddy_set_protocol_data(pb, NULL);
- } else {
- purple_account_remove_buddy(purple_buddy_get_account(pb), pb, NULL);
- purple_blist_remove_buddy(pb);
}
}
@@ -223,21 +223,12 @@ void bonjour_buddy_signed_off(PurpleBuddy *pb) {
*/
void bonjour_buddy_got_buddy_icon(BonjourBuddy *buddy, gconstpointer data, gsize len) {
/* Recalculate the hash instead of using the current phsh to make sure it is accurate for the icon. */
- char *p, *hash;
+ gchar *hash;
if (data == NULL || len == 0)
return;
- /* Take advantage of the fact that we use a SHA-1 hash of the data as the filename. */
- hash = purple_util_get_image_filename(data, len);
-
- /* Get rid of the extension */
- if (!(p = strchr(hash, '.'))) {
- g_free(hash);
- return;
- }
-
- *p = '\0';
+ hash = g_compute_checksum_for_data(G_CHECKSUM_SHA1, data, len);
purple_debug_info("bonjour", "Got buddy icon for %s icon hash='%s' phsh='%s'.\n", buddy->name,
hash, buddy->phsh ? buddy->phsh : "(null)");
diff --git a/libpurple/protocols/bonjour/dns_sd_proxy.c b/libpurple/protocols/bonjour/dns_sd_proxy.c
index f1189063ed..f146223adb 100644
--- a/libpurple/protocols/bonjour/dns_sd_proxy.c
+++ b/libpurple/protocols/bonjour/dns_sd_proxy.c
@@ -19,7 +19,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
*/
-#include "win32dep.h"
+#include "internal.h"
#include "dns_sd_proxy.h"
#ifndef LINK_DNS_SD_DIRECTLY
diff --git a/libpurple/protocols/bonjour/dns_sd_proxy.h b/libpurple/protocols/bonjour/dns_sd_proxy.h
index 27df91baa1..b658ec825e 100644
--- a/libpurple/protocols/bonjour/dns_sd_proxy.h
+++ b/libpurple/protocols/bonjour/dns_sd_proxy.h
@@ -21,17 +21,107 @@
#ifndef _DNS_SD_PROXY
#define _DNS_SD_PROXY
+#include <config.h>
#ifndef _MSC_VER
#include <stdint.h>
#endif
-/* fixup to make pidgin compile against win32 bonjour */
-#if defined(_WIN32) && !defined(_MSC_VER)
-#define _MSL_STDINT_H
-#endif
+#ifdef IS_WIN32_CROSS_COMPILED
-#include <dns_sd.h>
+/* I'm not sure, if we really need to include this for the following definitions
+ * modeled after Apple's dns_sd.h file.
+ *
+ * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+# if defined(_WIN32) && !defined(EFI32) && !defined(EFI64)
+# define DNSSD_API __stdcall
+# else
+# define DNSSD_API
+# endif
+
+# define kDNSServiceInterfaceIndexAny 0
+# define kDNSServiceMaxDomainName 1009
+
+typedef gint32 DNSServiceErrorType;
+typedef guint32 DNSServiceFlags;
+typedef struct _DNSServiceRef_t *DNSServiceRef;
+typedef struct _DNSRecordRef_t *DNSRecordRef;
+typedef guint32 DNSServiceProtocol;
+
+typedef union _TXTRecordRef_t {
+ gchar PrivateData[16];
+ gchar *ForceNaturalAlignment;
+} TXTRecordRef;
+
+typedef void (DNSSD_API *DNSServiceBrowseReply)();
+typedef void (DNSSD_API *DNSServiceGetAddrInfoReply)();
+typedef void (DNSSD_API *DNSServiceQueryRecordReply)(
+ DNSServiceRef sdRef, DNSServiceFlags flags, guint32 interfaceIndex,
+ DNSServiceErrorType errorCode, const gchar *fullname,
+ guint16 rrtype, guint16 rrclass, guint16 rdlen, const void *rdata,
+ guint32 ttl, void *context);
+typedef void (DNSSD_API *DNSServiceRegisterReply)();
+typedef void (DNSSD_API *DNSServiceResolveReply)(
+ DNSServiceRef sdRef, DNSServiceFlags flags, guint32 interfaceIndex,
+ DNSServiceErrorType errorCode, const gchar *fullname,
+ const gchar *hosttarget, guint16 port, guint16 txtLen,
+ const guchar *txtRecord, void *context);
+
+enum {
+ kDNSServiceErr_NoError = 0,
+ kDNSServiceErr_Unknown = -65537,
+};
+
+enum {
+ kDNSServiceFlagsAdd = 0x2,
+ kDNSServiceFlagsLongLivedQuery = 0x100,
+};
+
+enum {
+ kDNSServiceType_NULL = 10,
+ kDNSServiceType_TXT = 16,
+};
+
+enum {
+ kDNSServiceClass_IN = 1,
+};
+
+enum {
+ kDNSServiceProtocol_IPv4 = 0x01,
+};
+
+#else
+/* fixup to make pidgin compile against win32 bonjour */
+# if defined(_WIN32) && !defined(_MSC_VER)
+# define _MSL_STDINT_H
+# endif
+# include <dns_sd.h>
+#endif /* IS_WIN32_CROSS_COMPILED */
gboolean dns_sd_available(void);
diff --git a/libpurple/protocols/bonjour/jabber.c b/libpurple/protocols/bonjour/jabber.c
index e8f19bbed3..9797614b5e 100644
--- a/libpurple/protocols/bonjour/jabber.c
+++ b/libpurple/protocols/bonjour/jabber.c
@@ -28,8 +28,6 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
-#else
-#include "libc_interface.h"
#endif
#include <sys/types.h>
@@ -52,7 +50,7 @@
#include "network.h"
#include "eventloop.h"
#include "connection.h"
-#include "blist.h"
+#include "buddylist.h"
#include "xmlnode.h"
#include "debug.h"
#include "notify.h"
@@ -82,14 +80,14 @@ enum sent_stream_start_types {
};
static void
-xep_iq_parse(xmlnode *packet, PurpleBuddy *pb);
+xep_iq_parse(PurpleXmlNode *packet, PurpleBuddy *pb);
static BonjourJabberConversation *
bonjour_jabber_conv_new(PurpleBuddy *pb, PurpleAccount *account, const char *ip) {
BonjourJabberConversation *bconv = g_new0(BonjourJabberConversation, 1);
bconv->socket = -1;
- bconv->tx_buf = purple_circ_buffer_new(512);
+ bconv->tx_buf = purple_circular_buffer_new(512);
bconv->tx_handler = 0;
bconv->rx_handler = 0;
bconv->pb = pb;
@@ -122,14 +120,14 @@ _font_size_ichat_to_purple(int size)
}
static gchar *
-get_xmlnode_contents(xmlnode *node)
+get_xmlnode_contents(PurpleXmlNode *node)
{
gchar *contents;
- contents = xmlnode_to_str(node, NULL);
+ contents = purple_xmlnode_to_str(node, NULL);
/* we just want the stuff inside <font></font>
- * There isn't stuff exposed in xmlnode.c to do this more cleanly. */
+ * There isn't stuff exposed in PurpleXmlNode.c to do this more cleanly. */
if (contents) {
char *bodystart = strchr(contents, '>');
@@ -144,27 +142,27 @@ get_xmlnode_contents(xmlnode *node)
}
static void
-_jabber_parse_and_write_message_to_ui(xmlnode *message_node, PurpleBuddy *pb)
+_jabber_parse_and_write_message_to_ui(PurpleXmlNode *message_node, PurpleBuddy *pb)
{
- xmlnode *body_node, *html_node, *events_node;
+ PurpleXmlNode *body_node, *html_node, *events_node;
PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(pb));
gchar *body = NULL;
- body_node = xmlnode_get_child(message_node, "body");
- html_node = xmlnode_get_child(message_node, "html");
+ body_node = purple_xmlnode_get_child(message_node, "body");
+ html_node = purple_xmlnode_get_child(message_node, "html");
if (body_node == NULL && html_node == NULL) {
purple_debug_error("bonjour", "No body or html node found, discarding message.\n");
return;
}
- events_node = xmlnode_get_child_with_namespace(message_node, "x", "jabber:x:event");
+ events_node = purple_xmlnode_get_child_with_namespace(message_node, "x", "jabber:x:event");
if (events_node != NULL) {
#if 0
- if (xmlnode_get_child(events_node, "composing") != NULL)
+ if (purple_xmlnode_get_child(events_node, "composing") != NULL)
composing_event = TRUE;
#endif
- if (xmlnode_get_child(events_node, "id") != NULL) {
+ if (purple_xmlnode_get_child(events_node, "id") != NULL) {
/* The user is just typing */
/* TODO: Deal with typing notification */
return;
@@ -172,33 +170,33 @@ _jabber_parse_and_write_message_to_ui(xmlnode *message_node, PurpleBuddy *pb)
}
if (html_node != NULL) {
- xmlnode *html_body_node;
+ PurpleXmlNode *html_body_node;
- html_body_node = xmlnode_get_child(html_node, "body");
+ html_body_node = purple_xmlnode_get_child(html_node, "body");
if (html_body_node != NULL) {
- xmlnode *html_body_font_node;
+ PurpleXmlNode *html_body_font_node;
- html_body_font_node = xmlnode_get_child(html_body_node, "font");
+ html_body_font_node = purple_xmlnode_get_child(html_body_node, "font");
/* Types of messages sent by iChat */
if (html_body_font_node != NULL) {
gchar *html_body;
- const char *font_face, *font_size,
+ const char *font_face, *font_size, *font_color,
*ichat_balloon_color, *ichat_text_color;
- font_face = xmlnode_get_attrib(html_body_font_node, "face");
+ font_face = purple_xmlnode_get_attrib(html_body_font_node, "face");
/* The absolute iChat font sizes should be converted to 1..7 range */
- font_size = xmlnode_get_attrib(html_body_font_node, "ABSZ");
+ font_size = purple_xmlnode_get_attrib(html_body_font_node, "ABSZ");
if (font_size != NULL)
font_size = _font_size_ichat_to_purple(atoi(font_size));
- /*font_color = xmlnode_get_attrib(html_body_font_node, "color");*/
- ichat_balloon_color = xmlnode_get_attrib(html_body_node, "ichatballooncolor");
- ichat_text_color = xmlnode_get_attrib(html_body_node, "ichattextcolor");
+ font_color = purple_xmlnode_get_attrib(html_body_font_node, "color");
+ ichat_balloon_color = purple_xmlnode_get_attrib(html_body_node, "ichatballooncolor");
+ ichat_text_color = purple_xmlnode_get_attrib(html_body_node, "ichattextcolor");
html_body = get_xmlnode_contents(html_body_font_node);
if (html_body == NULL)
/* This is the kind of formatted messages that Purple creates */
- html_body = xmlnode_to_str(html_body_font_node, NULL);
+ html_body = purple_xmlnode_to_str(html_body_font_node, NULL);
if (html_body != NULL) {
GString *str = g_string_new("<font");
@@ -207,7 +205,9 @@ _jabber_parse_and_write_message_to_ui(xmlnode *message_node, PurpleBuddy *pb)
g_string_append_printf(str, " face='%s'", font_face);
if (font_size)
g_string_append_printf(str, " size='%s'", font_size);
- if (ichat_text_color)
+ if (font_color)
+ g_string_append_printf(str, " color='%s'", font_color);
+ else if (ichat_text_color)
g_string_append_printf(str, " color='%s'", ichat_text_color);
if (ichat_balloon_color)
g_string_append_printf(str, " back='%s'", ichat_balloon_color);
@@ -223,7 +223,7 @@ _jabber_parse_and_write_message_to_ui(xmlnode *message_node, PurpleBuddy *pb)
/* Compose the message */
if (body == NULL && body_node != NULL)
- body = xmlnode_get_data(body_node);
+ body = purple_xmlnode_get_data(body_node);
if (body == NULL) {
purple_debug_error("bonjour", "No html body or regular body found.\n");
@@ -231,7 +231,7 @@ _jabber_parse_and_write_message_to_ui(xmlnode *message_node, PurpleBuddy *pb)
}
/* Send the message to the UI */
- serv_got_im(gc, purple_buddy_get_name(pb), body, 0, time(NULL));
+ purple_serv_got_im(gc, purple_buddy_get_name(pb), body, 0, time(NULL));
g_free(body);
}
@@ -278,7 +278,7 @@ _send_data_write_cb(gpointer data, gint source, PurpleInputCondition cond)
BonjourJabberConversation *bconv = bb->conversation;
int ret, writelen;
- writelen = purple_circ_buffer_get_max_read(bconv->tx_buf);
+ writelen = purple_circular_buffer_get_max_read(bconv->tx_buf);
if (writelen == 0) {
purple_input_remove(bconv->tx_handler);
@@ -286,7 +286,7 @@ _send_data_write_cb(gpointer data, gint source, PurpleInputCondition cond)
return;
}
- ret = send(bconv->socket, bconv->tx_buf->outptr, writelen, 0);
+ ret = send(bconv->socket, purple_circular_buffer_get_output(bconv->tx_buf), writelen, 0);
if (ret < 0 && errno == EAGAIN)
return;
@@ -300,18 +300,18 @@ _send_data_write_cb(gpointer data, gint source, PurpleInputCondition cond)
account = purple_buddy_get_account(pb);
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, account);
+ conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
if (conv != NULL)
- purple_conversation_write(conv, NULL,
- _("Unable to send message."),
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("Unable to send message."),
+ PURPLE_MESSAGE_ERROR);
bonjour_jabber_close_conversation(bb->conversation);
bb->conversation = NULL;
return;
}
- purple_circ_buffer_mark_read(bconv->tx_buf, ret);
+ purple_circular_buffer_mark_read(bconv->tx_buf, ret);
}
static gint
@@ -327,7 +327,7 @@ _send_data(PurpleBuddy *pb, char *message)
|| bconv->connect_data != NULL
|| bconv->sent_stream_start != FULLY_SENT
|| !bconv->recv_stream_start
- || purple_circ_buffer_get_max_read(bconv->tx_buf) > 0) {
+ || purple_circular_buffer_get_max_read(bconv->tx_buf) > 0) {
ret = -1;
errno = EAGAIN;
} else {
@@ -346,11 +346,11 @@ _send_data(PurpleBuddy *pb, char *message)
account = purple_buddy_get_account(pb);
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, account);
+ conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
if (conv != NULL)
- purple_conversation_write(conv, NULL,
- _("Unable to send message."),
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("Unable to send message."),
+ PURPLE_MESSAGE_ERROR);
bonjour_jabber_close_conversation(bb->conversation);
bb->conversation = NULL;
@@ -362,13 +362,13 @@ _send_data(PurpleBuddy *pb, char *message)
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);
- purple_circ_buffer_append(bconv->tx_buf, message + ret, len - ret);
+ purple_circular_buffer_append(bconv->tx_buf, message + ret, len - ret);
}
return ret;
}
-void bonjour_jabber_process_packet(PurpleBuddy *pb, xmlnode *packet) {
+void bonjour_jabber_process_packet(PurpleBuddy *pb, PurpleXmlNode *packet) {
g_return_if_fail(packet != NULL);
g_return_if_fail(pb != NULL);
@@ -396,10 +396,10 @@ static void bonjour_jabber_stream_ended(BonjourJabberConversation *bconv) {
#if 0
if(bconv->pb != NULL) {
PurpleConversation *conv;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bconv->pb->name, bconv->pb->account);
+ conv = purple_conversations_find_im_with_account(bconv->pb->name, bconv->pb->account);
if (conv != NULL) {
char *tmp = g_strdup_printf(_("%s has closed the conversation."), bconv->pb->name);
- purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(conv, tmp, 0);
g_free(tmp);
}
}
@@ -485,11 +485,11 @@ _start_stream(gpointer data, gint source, PurpleInputCondition condition)
purple_debug_error("bonjour", "Error starting stream with buddy %s at %s error: %s\n",
bname ? bname : "(unknown)", bconv->ip, err ? err : "(null)");
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bname, bconv->account);
+ conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname, bconv->account));
if (conv != NULL)
- purple_conversation_write(conv, NULL,
- _("Unable to send the message, the conversation couldn't be started."),
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("Unable to send the message, the conversation couldn't be started."),
+ PURPLE_MESSAGE_ERROR);
bonjour_jabber_close_conversation(bconv);
if(bb != NULL)
@@ -550,11 +550,11 @@ static gboolean bonjour_jabber_send_stream_init(BonjourJabberConversation *bconv
if (bconv->pb) {
PurpleConversation *conv;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bname, bconv->account);
+ conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname, bconv->account));
if (conv != NULL)
- purple_conversation_write(conv, NULL,
- _("Unable to send the message, the conversation couldn't be started."),
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("Unable to send the message, the conversation couldn't be started."),
+ PURPLE_MESSAGE_ERROR);
}
close(client_socket);
@@ -595,11 +595,11 @@ void bonjour_jabber_stream_started(BonjourJabberConversation *bconv) {
if (bconv->pb) {
PurpleConversation *conv;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bname, bconv->account);
+ conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname, bconv->account));
if (conv != NULL)
- purple_conversation_write(conv, NULL,
- _("Unable to send the message, the conversation couldn't be started."),
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("Unable to send the message, the conversation couldn't be started."),
+ PURPLE_MESSAGE_ERROR);
}
/* We don't want to recieve anything else */
@@ -616,7 +616,7 @@ void bonjour_jabber_stream_started(BonjourJabberConversation *bconv) {
/* If the stream has been completely started and we know who we're talking to, we can start doing stuff. */
/* I don't think the circ_buffer can actually contain anything without a buddy being associated, but lets be explicit. */
if (bconv->sent_stream_start == FULLY_SENT && bconv->recv_stream_start
- && bconv->pb && purple_circ_buffer_get_max_read(bconv->tx_buf) > 0) {
+ && 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);
@@ -673,7 +673,7 @@ _server_socket_handler(gpointer data, int server_socket, PurpleInputCondition co
mbba = g_new0(struct _match_buddies_by_address_t, 1);
mbba->address = address_text;
- buddies = purple_find_buddies(jdata->account, NULL);
+ buddies = purple_blist_find_buddies(jdata->account, NULL);
g_slist_foreach(buddies, _match_buddies_by_address, mbba);
g_slist_free(buddies);
@@ -699,14 +699,14 @@ _server_socket_handler(gpointer data, int server_socket, PurpleInputCondition co
}
static int
-start_serversocket_listening(int port, int socket, struct sockaddr *addr, size_t addr_size, gboolean ip6, gboolean allow_port_fallback)
+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, addr_size) != 0) {
+ 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));
@@ -717,12 +717,12 @@ start_serversocket_listening(int port, int socket, struct sockaddr *addr, size_t
}
#ifdef PF_INET6
if (ip6)
- ((struct sockaddr_in6 *) addr)->sin6_port = 0;
+ addr->in6.sin6_port = 0;
else
#endif
- ((struct sockaddr_in *) addr)->sin_port = 0;
+ addr->in.sin_port = 0;
- if (bind(socket, addr, addr_size) != 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;
}
@@ -739,7 +739,7 @@ start_serversocket_listening(int port, int socket, struct sockaddr *addr, size_t
#if 0
/* TODO: Why isn't this being used? */
- data->socket = purple_network_listen(jdata->port, SOCK_STREAM);
+ data->socket = purple_network_listen(jdata->port, AF_UNSPEC, SOCK_STREAM, TRUE);
if (jdata->socket == -1)
{
@@ -768,7 +768,7 @@ bonjour_jabber_start(BonjourJabber *jdata)
#ifdef PF_INET6
if (jdata->socket6 != -1) {
- struct sockaddr_in6 addr6;
+ common_sockaddr_t addr6;
#ifdef IPV6_V6ONLY
int on = 1;
if (setsockopt(jdata->socket6, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) != 0) {
@@ -776,11 +776,12 @@ bonjour_jabber_start(BonjourJabber *jdata)
return -1;
}
#endif
- memset(&addr6, 0, sizeof(addr6));
- addr6.sin6_family = AF_INET6;
- addr6.sin6_port = htons(jdata->port);
- addr6.sin6_addr = in6addr_any;
- ipv6_port = start_serversocket_listening(jdata->port, jdata->socket6, (struct sockaddr *) &addr6, sizeof(addr6), TRUE, TRUE);
+ 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);
@@ -793,11 +794,12 @@ bonjour_jabber_start(BonjourJabber *jdata)
}
#endif
if (jdata->socket != -1) {
- struct sockaddr_in addr4;
+ common_sockaddr_t addr4;
memset(&addr4, 0, sizeof(addr4));
- addr4.sin_family = AF_INET;
- addr4.sin_port = htons(jdata->port);
- ipv4_port = start_serversocket_listening(jdata->port, jdata->socket, (struct sockaddr *) &addr4, sizeof(addr4), FALSE, ipv6_port != -1);
+ 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, ipv6_port != -1);
/* 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);
@@ -868,11 +870,11 @@ _connected_to_buddy(gpointer data, gint source, const gchar *error)
purple_debug_error("bonjour", "No more addresses for buddy %s. Aborting", purple_buddy_get_name(pb));
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, account);
+ conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
if (conv != NULL)
- purple_conversation_write(conv, NULL,
- _("Unable to send the message, the conversation couldn't be started."),
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("Unable to send the message, the conversation couldn't be started."),
+ PURPLE_MESSAGE_ERROR);
bonjour_jabber_close_conversation(bb->conversation);
bb->conversation = NULL;
@@ -889,11 +891,11 @@ _connected_to_buddy(gpointer data, gint source, const gchar *error)
account = purple_buddy_get_account(pb);
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, account);
+ conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
if (conv != NULL)
- purple_conversation_write(conv, NULL,
- _("Unable to send the message, the conversation couldn't be started."),
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("Unable to send the message, the conversation couldn't be started."),
+ PURPLE_MESSAGE_ERROR);
close(source);
bonjour_jabber_close_conversation(bb->conversation);
@@ -915,7 +917,7 @@ bonjour_jabber_conv_match_by_name(BonjourJabberConversation *bconv) {
g_return_if_fail(bconv->ip != NULL);
g_return_if_fail(bconv->pb == NULL);
- pb = purple_find_buddy(bconv->account, bconv->buddy_name);
+ pb = purple_blist_find_buddy(bconv->account, bconv->buddy_name);
if (pb && (bb = purple_buddy_get_protocol_data(pb))) {
const char *ip;
GSList *tmp = bb->ips;
@@ -927,7 +929,9 @@ bonjour_jabber_conv_match_by_name(BonjourJabberConversation *bconv) {
while(tmp) {
ip = tmp->data;
if (ip != NULL && g_ascii_strcasecmp(ip, bconv->ip) == 0) {
- BonjourJabber *jdata = ((BonjourData*) bconv->account->gc->proto_data)->jabber_data;
+ PurpleConnection *pc = purple_account_get_connection(bconv->account);
+ BonjourData *bd = purple_connection_get_protocol_data(pc);
+ BonjourJabber *jdata = bd->jabber_data;
purple_debug_info("bonjour", "Matched buddy %s to incoming conversation \"from\" attrib and IP (%s)\n",
purple_buddy_get_name(pb), bconv->ip);
@@ -960,14 +964,16 @@ bonjour_jabber_conv_match_by_name(BonjourJabberConversation *bconv) {
void
bonjour_jabber_conv_match_by_ip(BonjourJabberConversation *bconv) {
- BonjourJabber *jdata = ((BonjourData*) bconv->account->gc->proto_data)->jabber_data;
+ 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;
GSList *buddies;
mbba = g_new0(struct _match_buddies_by_address_t, 1);
mbba->address = bconv->ip;
- buddies = purple_find_buddies(jdata->account, NULL);
+ buddies = purple_blist_find_buddies(jdata->account, NULL);
g_slist_foreach(buddies, _match_buddies_by_address, mbba);
g_slist_free(buddies);
@@ -1016,7 +1022,7 @@ _find_or_start_conversation(BonjourJabber *jdata, const gchar *to)
g_return_val_if_fail(jdata != NULL, NULL);
g_return_val_if_fail(to != NULL, NULL);
- pb = purple_find_buddy(jdata->account, to);
+ pb = purple_blist_find_buddy(jdata->account, to);
if (pb == NULL || (bb = purple_buddy_get_protocol_data(pb)) == NULL)
/* You can not send a message to an offline buddy */
return NULL;
@@ -1037,7 +1043,7 @@ _find_or_start_conversation(BonjourJabber *jdata, const gchar *to)
proxy_info = purple_proxy_info_new();
purple_account_set_proxy_info(jdata->account, proxy_info);
}
- purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_NONE);
+ purple_proxy_info_set_proxy_type(proxy_info, PURPLE_PROXY_NONE);
connect_data = purple_proxy_connect(
purple_account_get_connection(jdata->account),
@@ -1062,7 +1068,7 @@ _find_or_start_conversation(BonjourJabber *jdata, const gchar *to)
int
bonjour_jabber_send_message(BonjourJabber *jdata, const gchar *to, const gchar *body)
{
- xmlnode *message_node, *node, *node2;
+ PurpleXmlNode *message_node, *node, *node2;
gchar *message, *xhtml;
PurpleBuddy *pb;
BonjourBuddy *bb;
@@ -1077,32 +1083,32 @@ bonjour_jabber_send_message(BonjourJabber *jdata, const gchar *to, const gchar *
purple_markup_html_to_xhtml(body, &xhtml, &message);
- message_node = xmlnode_new("message");
- xmlnode_set_attrib(message_node, "to", bb->name);
- xmlnode_set_attrib(message_node, "from", bonjour_get_jid(jdata->account));
- xmlnode_set_attrib(message_node, "type", "chat");
+ message_node = purple_xmlnode_new("message");
+ purple_xmlnode_set_attrib(message_node, "to", bb->name);
+ purple_xmlnode_set_attrib(message_node, "from", bonjour_get_jid(jdata->account));
+ purple_xmlnode_set_attrib(message_node, "type", "chat");
/* Enclose the message from the UI within a "font" node */
- node = xmlnode_new_child(message_node, "body");
- xmlnode_insert_data(node, message, strlen(message));
+ node = purple_xmlnode_new_child(message_node, "body");
+ purple_xmlnode_insert_data(node, message, strlen(message));
g_free(message);
- node = xmlnode_new_child(message_node, "html");
- xmlnode_set_namespace(node, "http://www.w3.org/1999/xhtml");
+ node = purple_xmlnode_new_child(message_node, "html");
+ purple_xmlnode_set_namespace(node, "http://www.w3.org/1999/xhtml");
- node = xmlnode_new_child(node, "body");
+ node = purple_xmlnode_new_child(node, "body");
message = g_strdup_printf("<font>%s</font>", xhtml);
- node2 = xmlnode_from_str(message, strlen(message));
+ node2 = purple_xmlnode_from_str(message, strlen(message));
g_free(xhtml);
g_free(message);
- xmlnode_insert_child(node, node2);
+ purple_xmlnode_insert_child(node, node2);
- node = xmlnode_new_child(message_node, "x");
- xmlnode_set_namespace(node, "jabber:x:event");
- xmlnode_insert_child(node, xmlnode_new("composing"));
+ node = purple_xmlnode_new_child(message_node, "x");
+ purple_xmlnode_set_namespace(node, "jabber:x:event");
+ purple_xmlnode_insert_child(node, purple_xmlnode_new("composing"));
- message = xmlnode_to_str(message_node, NULL);
- xmlnode_free(message_node);
+ message = purple_xmlnode_to_str(message_node, NULL);
+ purple_xmlnode_free(message_node);
ret = _send_data(pb, message) >= 0;
@@ -1120,7 +1126,9 @@ _async_bonjour_jabber_close_conversation_cb(gpointer data) {
void
async_bonjour_jabber_close_conversation(BonjourJabberConversation *bconv) {
- BonjourJabber *jdata = ((BonjourData*) bconv->account->gc->proto_data)->jabber_data;
+ PurpleConnection *pc = purple_account_get_connection(bconv->account);
+ BonjourData *bd = purple_connection_get_protocol_data(pc);
+ BonjourJabber *jdata = bd->jabber_data;
jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
@@ -1140,9 +1148,14 @@ bonjour_jabber_close_conversation(BonjourJabberConversation *bconv)
if (bconv != NULL) {
BonjourData *bd = NULL;
- if(PURPLE_CONNECTION_IS_VALID(bconv->account->gc)) {
- bd = bconv->account->gc->proto_data;
- bd->jabber_data->pending_conversations = g_slist_remove(bd->jabber_data->pending_conversations, bconv);
+ PurpleConnection *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);
}
/* Cancel any file transfers that are waiting to begin */
@@ -1155,7 +1168,7 @@ bonjour_jabber_close_conversation(BonjourJabberConversation *bconv)
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 (strcmp(xfer->who, purple_buddy_get_name(bconv->pb)) == 0
+ if (strcmp(purple_xfer_get_remote_user(xfer), purple_buddy_get_name(bconv->pb)) == 0
&& (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_NOT_STARTED
|| purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_UNKNOWN)) {
purple_xfer_cancel_remote(xfer);
@@ -1169,7 +1182,7 @@ bonjour_jabber_close_conversation(BonjourJabberConversation *bconv)
/* 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) != len) {
+ if (send(bconv->socket, STREAM_END, len, 0) != (gssize)len) {
purple_debug_error("bonjour",
"bonjour_jabber_close_conversation: "
"couldn't send data\n");
@@ -1184,7 +1197,7 @@ bonjour_jabber_close_conversation(BonjourJabberConversation *bconv)
purple_input_remove(bconv->tx_handler);
/* Free all the data related to the conversation */
- purple_circ_buffer_destroy(bconv->tx_buf);
+ 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) {
@@ -1219,15 +1232,15 @@ bonjour_jabber_stop(BonjourJabber *jdata)
purple_input_remove(jdata->watcher_id6);
/* Close all the conversation sockets and remove all the watchers after sending end streams */
- if (jdata->account->gc != NULL) {
+ if (!purple_account_is_disconnected(jdata->account)) {
GSList *buddies, *l;
- buddies = purple_find_buddies(jdata->account, NULL);
+ buddies = purple_blist_find_buddies(jdata->account, NULL);
for (l = buddies; l; l = l->next) {
BonjourBuddy *bb = purple_buddy_get_protocol_data((PurpleBuddy*) l->data);
if (bb && bb->conversation) {
/* Any ongoing connection attempt is cancelled
- * by _purple_connection_destroy */
+ * when a connection is destroyed */
bb->conversation->connect_data = NULL;
bonjour_jabber_close_conversation(bb->conversation);
bb->conversation = NULL;
@@ -1246,34 +1259,34 @@ bonjour_jabber_stop(BonjourJabber *jdata)
XepIq *
xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id)
{
- xmlnode *iq_node = NULL;
+ PurpleXmlNode *iq_node = NULL;
XepIq *iq = NULL;
g_return_val_if_fail(data != NULL, NULL);
g_return_val_if_fail(to != NULL, NULL);
g_return_val_if_fail(id != NULL, NULL);
- iq_node = xmlnode_new("iq");
+ iq_node = purple_xmlnode_new("iq");
- xmlnode_set_attrib(iq_node, "to", to);
- xmlnode_set_attrib(iq_node, "from", from);
- xmlnode_set_attrib(iq_node, "id", id);
+ purple_xmlnode_set_attrib(iq_node, "to", to);
+ purple_xmlnode_set_attrib(iq_node, "from", from);
+ purple_xmlnode_set_attrib(iq_node, "id", id);
switch (type) {
case XEP_IQ_SET:
- xmlnode_set_attrib(iq_node, "type", "set");
+ purple_xmlnode_set_attrib(iq_node, "type", "set");
break;
case XEP_IQ_GET:
- xmlnode_set_attrib(iq_node, "type", "get");
+ purple_xmlnode_set_attrib(iq_node, "type", "get");
break;
case XEP_IQ_RESULT:
- xmlnode_set_attrib(iq_node, "type", "result");
+ purple_xmlnode_set_attrib(iq_node, "type", "result");
break;
case XEP_IQ_ERROR:
- xmlnode_set_attrib(iq_node, "type", "error");
+ purple_xmlnode_set_attrib(iq_node, "type", "error");
break;
case XEP_IQ_NONE:
default:
- xmlnode_set_attrib(iq_node, "type", "none");
+ purple_xmlnode_set_attrib(iq_node, "type", "none");
break;
}
@@ -1298,7 +1311,7 @@ check_if_blocked(PurpleBuddy *pb)
acc = purple_buddy_get_account(pb);
- for(l = acc->deny; l != NULL; l = l->next) {
+ for(l = purple_account_privacy_get_denied(acc); l != NULL; l = l->next) {
const gchar *name = purple_buddy_get_name(pb);
const gchar *username = bonjour_get_jid(acc);
@@ -1312,7 +1325,7 @@ check_if_blocked(PurpleBuddy *pb)
}
static void
-xep_iq_parse(xmlnode *packet, PurpleBuddy *pb)
+xep_iq_parse(PurpleXmlNode *packet, PurpleBuddy *pb)
{
PurpleAccount *account;
PurpleConnection *gc;
@@ -1323,7 +1336,7 @@ xep_iq_parse(xmlnode *packet, PurpleBuddy *pb)
account = purple_buddy_get_account(pb);
gc = purple_account_get_connection(account);
- if (xmlnode_get_child(packet, "si") != NULL || xmlnode_get_child(packet, "error") != NULL)
+ if (purple_xmlnode_get_child(packet, "si") != NULL || purple_xmlnode_get_child(packet, "error") != NULL)
xep_si_parse(gc, packet, pb);
else
xep_bytestreams_parse(gc, packet, pb);
@@ -1340,12 +1353,12 @@ xep_iq_send_and_free(XepIq *iq)
/* Send the message */
if (pb != NULL) {
/* Convert xml node into stream */
- gchar *msg = xmlnode_to_str(iq->node, NULL);
+ gchar *msg = purple_xmlnode_to_str(iq->node, NULL);
ret = _send_data(pb, msg);
g_free(msg);
}
- xmlnode_free(iq->node);
+ purple_xmlnode_free(iq->node);
iq->node = NULL;
g_free(iq);
@@ -1363,7 +1376,7 @@ bonjour_jabber_get_local_ips(int fd)
#ifdef HAVE_GETIFADDRS /* This is required for IPv6 */
{
struct ifaddrs *ifap, *ifa;
- struct sockaddr *addr;
+ common_sockaddr_t addr;
char addrstr[INET6_ADDRSTRLEN];
ret = getifaddrs(&ifap);
@@ -1377,23 +1390,25 @@ bonjour_jabber_get_local_ips(int fd)
if (!(ifa->ifa_flags & IFF_RUNNING) || (ifa->ifa_flags & IFF_LOOPBACK) || ifa->ifa_addr == NULL)
continue;
- addr = ifa->ifa_addr;
+ memcpy(&addr, ifa->ifa_addr, sizeof(addr));
address_text = NULL;
- switch (addr->sa_family) {
+ switch (addr.sa.sa_family) {
case AF_INET:
- address_text = inet_ntop(addr->sa_family, &((struct sockaddr_in *)addr)->sin_addr,
+ address_text = inet_ntop(addr.sa.sa_family,
+ &addr.in.sin_addr,
addrstr, sizeof(addrstr));
break;
#ifdef PF_INET6
case AF_INET6:
- address_text = inet_ntop(addr->sa_family, &((struct sockaddr_in6 *)addr)->sin6_addr,
+ address_text = inet_ntop(addr.sa.sa_family,
+ &addr.in6.sin6_addr,
addrstr, sizeof(addrstr));
break;
#endif
}
if (address_text != NULL) {
- if (addr->sa_family == AF_INET)
+ if (addr.sa.sa_family == AF_INET)
ips = g_slist_append(ips, g_strdup(address_text));
else
ips = g_slist_prepend(ips, g_strdup(address_text));
diff --git a/libpurple/protocols/bonjour/jabber.h b/libpurple/protocols/bonjour/jabber.h
index 4096497008..390e76367a 100644
--- a/libpurple/protocols/bonjour/jabber.h
+++ b/libpurple/protocols/bonjour/jabber.h
@@ -31,7 +31,7 @@
#include "xmlnode.h"
#include "account.h"
-#include "circbuffer.h"
+#include "circularbuffer.h"
typedef struct _BonjourJabber
{
@@ -50,13 +50,13 @@ typedef struct _BonjourJabberConversation
guint rx_handler;
guint tx_handler;
guint close_timeout;
- PurpleCircBuffer *tx_buf;
+ 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;
- xmlnode *current;
+ PurpleXmlNode *current;
PurpleBuddy *pb;
PurpleAccount *account;
@@ -83,7 +83,7 @@ void async_bonjour_jabber_close_conversation(BonjourJabberConversation *bconv);
void bonjour_jabber_stream_started(BonjourJabberConversation *bconv);
-void bonjour_jabber_process_packet(PurpleBuddy *pb, xmlnode *packet);
+void bonjour_jabber_process_packet(PurpleBuddy *pb, PurpleXmlNode *packet);
void bonjour_jabber_stop(BonjourJabber *data);
@@ -102,7 +102,7 @@ typedef enum {
typedef struct _XepIq {
XepIqType type;
char *id;
- xmlnode *node;
+ PurpleXmlNode *node;
char *to;
void *data;
} XepIq;
diff --git a/libpurple/protocols/bonjour/mdns_avahi.c b/libpurple/protocols/bonjour/mdns_avahi.c
index 5f28a70a43..8446bb2466 100644
--- a/libpurple/protocols/bonjour/mdns_avahi.c
+++ b/libpurple/protocols/bonjour/mdns_avahi.c
@@ -122,7 +122,7 @@ _resolver_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtoco
g_return_if_fail(r != NULL);
- pb = purple_find_buddy(account, name);
+ pb = purple_blist_find_buddy(account, name);
bb = (pb != NULL) ? purple_buddy_get_protocol_data(pb) : NULL;
switch (event) {
@@ -136,7 +136,7 @@ _resolver_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtoco
res = g_slist_find_custom(b_impl->resolvers, r, _find_resolver_data_by_resolver);
if (res != NULL) {
rd = res->data;
- b_impl->resolvers = g_slist_remove_link(b_impl->resolvers, res);
+ b_impl->resolvers = g_slist_delete_link(b_impl->resolvers, res);
/* We've already freed the resolver */
rd->resolver = NULL;
@@ -266,7 +266,7 @@ _browser_callback(AvahiServiceBrowser *b, AvahiIfIndex interface,
break;
case AVAHI_BROWSER_REMOVE:
purple_debug_info("bonjour", "_browser_callback - Remove service\n");
- pb = purple_find_buddy(account, name);
+ pb = purple_blist_find_buddy(account, name);
if (pb != NULL) {
BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
AvahiBuddyImplData *b_impl;
@@ -618,7 +618,7 @@ void _mdns_delete_buddy(BonjourBuddy *buddy) {
void _mdns_retrieve_buddy_icon(BonjourBuddy* buddy) {
PurpleConnection *conn = purple_account_get_connection(buddy->account);
- BonjourData *bd = conn->proto_data;
+ BonjourData *bd = purple_connection_get_protocol_data(conn);
AvahiSessionImplData *session_idata = bd->dns_sd_data->mdns_impl_data;
AvahiBuddyImplData *idata = buddy->mdns_impl_data;
gchar *name;
diff --git a/libpurple/protocols/bonjour/mdns_common.c b/libpurple/protocols/bonjour/mdns_common.c
index 9319e36160..ae9c578c23 100644
--- a/libpurple/protocols/bonjour/mdns_common.c
+++ b/libpurple/protocols/bonjour/mdns_common.c
@@ -179,35 +179,27 @@ void bonjour_dns_sd_retrieve_buddy_icon(BonjourBuddy* buddy) {
}
void bonjour_dns_sd_update_buddy_icon(BonjourDnsSd *data) {
- PurpleStoredImage *img;
+ PurpleImage *img;
if ((img = purple_buddy_icons_find_account_icon(data->account))) {
gconstpointer avatar_data;
gsize avatar_len;
- avatar_data = purple_imgstore_get_data(img);
- avatar_len = purple_imgstore_get_size(img);
+ avatar_data = purple_image_get_data(img);
+ avatar_len = purple_image_get_size(img);
if (_mdns_set_buddy_icon_data(data, avatar_data, avatar_len)) {
- /* The filename is a SHA-1 hash of the data (conveniently what we need) */
- const char *p, *filename = purple_imgstore_get_filename(img);
-
g_free(data->phsh);
data->phsh = NULL;
- /* Get rid of the extension */
- p = strchr(filename, '.');
- if (p)
- data->phsh = g_strndup(filename, p - filename);
- else
- purple_debug_error("bonjour", "account buddy icon returned unexpected filename (%s)"
- "; unable to extract hash. Clearing buddy icon\n", filename);
+ data->phsh = g_compute_checksum_for_data(
+ G_CHECKSUM_SHA1, avatar_data, avatar_len);
/* Update our TXT record */
publish_presence(data, PUBLISH_UPDATE);
}
- purple_imgstore_unref(img);
+ g_object_unref(img);
} else {
/* We need to do this regardless of whether data->phsh is set so that we
* cancel any icons that are currently in the process of being set */
@@ -257,7 +249,7 @@ void
bonjour_dns_sd_set_jid(PurpleAccount *account, const char *hostname)
{
PurpleConnection *conn = purple_account_get_connection(account);
- BonjourData *bd = conn->proto_data;
+ BonjourData *bd = purple_connection_get_protocol_data(conn);
const char *tmp, *account_name = purple_account_get_username(account);
/* Previously we allowed the hostname part of the jid to be set
diff --git a/libpurple/protocols/bonjour/mdns_types.h b/libpurple/protocols/bonjour/mdns_types.h
index c843f140d6..b8ec2d2857 100644
--- a/libpurple/protocols/bonjour/mdns_types.h
+++ b/libpurple/protocols/bonjour/mdns_types.h
@@ -37,7 +37,7 @@ typedef struct _BonjourDnsSd {
gchar *msg;
} BonjourDnsSd;
-typedef enum _PublishType {
+typedef enum {
PUBLISH_START,
PUBLISH_UPDATE
} PublishType;
diff --git a/libpurple/protocols/bonjour/mdns_win32.c b/libpurple/protocols/bonjour/mdns_win32.c
index 9f1798cf43..fc3dfc70ec 100644
--- a/libpurple/protocols/bonjour/mdns_win32.c
+++ b/libpurple/protocols/bonjour/mdns_win32.c
@@ -105,7 +105,8 @@ _mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition) {
purple_debug_error("bonjour", "Error (%d) handling mDNS response.\n", errorCode);
/* This happens when the mDNSResponder goes down, I haven't seen it happen any other time (in my limited testing) */
if (errorCode == kDNSServiceErr_Unknown) {
- purple_connection_error_reason(srh->account->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error(purple_account_get_connection(srh->account),
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Error communicating with local mDNSResponder."));
}
}
@@ -176,8 +177,8 @@ _mdns_resolve_host_callback(DNSServiceRef sdRef, DNSServiceFlags flags,
g_free(args->resolver_query);
args->resolver_query = NULL;
- if ((pb = purple_find_buddy(args->account, args->res_data->name))) {
- if (pb->proto_data != args->bb) {
+ if ((pb = purple_blist_find_buddy(args->account, args->res_data->name))) {
+ if (purple_buddy_get_protocol_data(pb) != args->bb) {
purple_debug_error("bonjour", "Found purple buddy for %s not matching bonjour buddy record.",
args->res_data->name);
goto cleanup;
@@ -293,7 +294,7 @@ _mdns_service_resolve_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint3
if (idata->resolvers == NULL) {
PurpleBuddy *pb;
/* See if this is now attached to a PurpleBuddy */
- if ((pb = purple_find_buddy(args->account, args->bb->name)))
+ if ((pb = purple_blist_find_buddy(args->account, args->bb->name)))
bonjour_buddy_signed_off(pb);
else {
/* Remove from the pending list */
@@ -347,8 +348,8 @@ _mdns_service_browse_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32
Win32BuddyImplData *idata;
/* Is there an existing buddy? */
- if ((pb = purple_find_buddy(account, serviceName)))
- bb = pb->proto_data;
+ if ((pb = purple_blist_find_buddy(account, serviceName)))
+ bb = purple_buddy_get_protocol_data(pb);
/* Is there a pending buddy? */
else {
while (tmp) {
@@ -368,7 +369,7 @@ _mdns_service_browse_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32
if (pb == NULL)
pending_buddies = g_slist_prepend(pending_buddies, bb);
else
- pb->proto_data = bb;
+ purple_buddy_set_protocol_data(pb, bb);
}
rd = g_new0(Win32SvcResolverData, 1);
@@ -403,12 +404,12 @@ _mdns_service_browse_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32
serviceName, interfaceIndex, regtype ? regtype : "",
replyDomain ? replyDomain : "");
- pb = purple_find_buddy(account, serviceName);
+ pb = purple_blist_find_buddy(account, serviceName);
if (pb != NULL) {
GSList *l;
/* There may be multiple presences, we should only get rid of this one */
Win32SvcResolverData *rd_search;
- BonjourBuddy *bb = pb->proto_data;
+ BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
Win32BuddyImplData *idata;
g_return_if_fail(bb != NULL);
diff --git a/libpurple/protocols/bonjour/parser.c b/libpurple/protocols/bonjour/parser.c
index 113cef5d52..eaef50a76a 100644
--- a/libpurple/protocols/bonjour/parser.c
+++ b/libpurple/protocols/bonjour/parser.c
@@ -57,7 +57,7 @@ bonjour_parser_element_start_libxml(void *user_data,
{
BonjourJabberConversation *bconv = user_data;
- xmlnode *node;
+ PurpleXmlNode *node;
int i;
g_return_if_fail(element_name != NULL);
@@ -85,10 +85,10 @@ bonjour_parser_element_start_libxml(void *user_data,
bonjour_jabber_conv_match_by_ip(bconv);
if(bconv->current)
- node = xmlnode_new_child(bconv->current, (const char*) element_name);
+ node = purple_xmlnode_new_child(bconv->current, (const char*) element_name);
else
- node = xmlnode_new((const char*) element_name);
- xmlnode_set_namespace(node, (const char*) namespace);
+ node = purple_xmlnode_new((const char*) element_name);
+ purple_xmlnode_set_namespace(node, (const char*) namespace);
for(i=0; i < nb_attributes * 5; i+=5) {
const char *name = (const char *)attributes[i];
@@ -104,7 +104,7 @@ bonjour_parser_element_start_libxml(void *user_data,
txt = attrib;
attrib = purple_unescape_text(txt);
g_free(txt);
- xmlnode_set_attrib_full(node, name, attrib_ns, prefix, attrib);
+ purple_xmlnode_set_attrib_full(node, name, attrib_ns, prefix, attrib);
g_free(attrib);
}
@@ -119,7 +119,7 @@ bonjour_parser_element_end_libxml(void *user_data, const xmlChar *element_name,
BonjourJabberConversation *bconv = user_data;
if(!bconv->current) {
- /* We don't keep a reference to the start stream xmlnode,
+ /* We don't keep a reference to the start stream PurpleXmlNode,
* so we have to check for it here to close the conversation */
if(!xmlStrcmp(element_name, (xmlChar*) "stream"))
/* Asynchronously close the conversation to prevent bonjour_parser_setup()
@@ -132,10 +132,10 @@ bonjour_parser_element_end_libxml(void *user_data, const xmlChar *element_name,
if(!xmlStrcmp((xmlChar*) bconv->current->name, element_name))
bconv->current = bconv->current->parent;
} else {
- xmlnode *packet = bconv->current;
+ PurpleXmlNode *packet = bconv->current;
bconv->current = NULL;
bonjour_jabber_process_packet(bconv->pb, packet);
- xmlnode_free(packet);
+ purple_xmlnode_free(packet);
}
}
@@ -150,7 +150,7 @@ bonjour_parser_element_text_libxml(void *user_data, const xmlChar *text, int tex
if(!text || !text_len)
return;
- xmlnode_insert_data(bconv->current, (const char*) text, text_len);
+ purple_xmlnode_insert_data(bconv->current, (const char*) text, text_len);
}
static void
diff --git a/libpurple/protocols/gg/Makefile.am b/libpurple/protocols/gg/Makefile.am
index 173c72c06c..eb7df1cc10 100644
--- a/libpurple/protocols/gg/Makefile.am
+++ b/libpurple/protocols/gg/Makefile.am
@@ -1,56 +1,17 @@
+pkgdir = @PURPLE_PLUGINDIR@
+
EXTRA_DIST = \
+ account.c \
+ account.h \
Makefile.mingw \
- lib/COPYING \
- lib/common.c \
- lib/config.h \
- lib/dcc7.c \
- lib/dcc.c \
- lib/debug.c \
- lib/debug.h \
- lib/deflate.c \
- lib/deflate.h \
- lib/encoding.c \
- lib/encoding.h \
- lib/endian.c \
- lib/events.c \
- lib/fileio.h \
- lib/handlers.c \
- lib/http.c \
- lib/internal.h \
- lib/libgadu.c \
- lib/libgadu.h \
- lib/message.c \
- lib/message.h \
- lib/network.c \
- lib/network.h \
- lib/obsolete.c \
- lib/packets.pb-c.c \
- lib/packets.pb-c.h \
- lib/protobuf.c \
- lib/protobuf-c.c \
- lib/protobuf-c.h \
- lib/protobuf.h \
- lib/protocol.h \
- lib/pubdir50.c \
- lib/pubdir.c \
- lib/resolver.c \
- lib/resolver.h \
- lib/session.h \
- lib/sha1.c \
- lib/strman.h \
- lib/tvbuff.c \
- lib/tvbuff.h \
- lib/tvbuilder.c \
- lib/tvbuilder.h
-
-pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+ lib/COPYING
if ! HAVE_LIBGADU
INTGG_SOURCES = \
lib/common.c \
lib/config.h \
- lib/dcc7.c \
lib/dcc.c \
+ lib/dcc7.c \
lib/debug.c \
lib/debug.h \
lib/deflate.c \
@@ -72,18 +33,18 @@ INTGG_SOURCES = \
lib/obsolete.c \
lib/packets.pb-c.c \
lib/packets.pb-c.h \
- lib/protobuf.c \
lib/protobuf-c.c \
lib/protobuf-c.h \
+ lib/protobuf.c \
lib/protobuf.h \
lib/protocol.h \
- lib/pubdir50.c \
lib/pubdir.c \
+ lib/pubdir50.c \
lib/resolver.c \
lib/resolver.h \
lib/session.h \
- lib/sha1.c \
lib/strman.h \
+ lib/sha1.c \
lib/tvbuff.c \
lib/tvbuff.h \
lib/tvbuilder.c \
@@ -104,20 +65,62 @@ endif
GGSOURCES = \
$(INTGG_SOURCES) \
- gg-utils.h \
- gg-utils.c \
- confer.h \
- confer.c \
- search.h \
- search.c \
- buddylist.h \
- buddylist.c \
+ avatar.c \
+ avatar.h \
+ blist.c \
+ blist.h \
+ chat.c \
+ chat.h \
+ deprecated.c \
+ deprecated.h \
+ edisc.c \
+ edisc.h \
+ gg.c \
gg.h \
- gg.c
+ html.c \
+ html.h \
+ image-prpl.c \
+ image-prpl.h \
+ keymapper.c \
+ keymapper.h \
+ libgadu-events.c \
+ libgadu-events.h \
+ libgaduw.c \
+ libgaduw.h \
+ message-prpl.c \
+ message-prpl.h \
+ multilogon.c \
+ multilogon.h \
+ pubdir-prpl.c \
+ pubdir-prpl.h \
+ purplew.c \
+ purplew.h \
+ resolver-purple.c \
+ resolver-purple.h \
+ roster.c \
+ roster.h \
+ servconn.c \
+ servconn.h \
+ status.c \
+ status.h \
+ tcpsocket.c \
+ tcpsocket.h \
+ utils.c \
+ utils.h \
+ validator.c \
+ validator.h \
+ xml.c \
+ xml.h \
+ oauth/oauth.c \
+ oauth/oauth.h \
+ oauth/oauth-parameter.c \
+ oauth/oauth-parameter.h \
+ oauth/oauth-purple.c \
+ oauth/oauth-purple.h
AM_CFLAGS = $(st)
-libgg_la_LDFLAGS = -module -avoid-version
+libgg_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
if STATIC_GG
@@ -125,21 +128,22 @@ st = -DPURPLE_STATIC_PRPL
noinst_LTLIBRARIES = libgg.la
libgg_la_SOURCES = $(GGSOURCES)
libgg_la_CFLAGS = $(AM_CFLAGS)
-libgg_la_LIBADD = $(LIBGADU_LIBS) $(INTGG_LIBS)
else
st =
pkg_LTLIBRARIES = libgg.la
libgg_la_SOURCES = $(GGSOURCES)
-libgg_la_LIBADD = $(GLIB_LIBS) $(LIBGADU_LIBS) $(INTGG_LIBS)
endif
+libgg_la_LIBADD = @PURPLE_LIBS@ $(LIBGADU_LIBS) $(JSON_LIBS)
+
AM_CPPFLAGS = \
-I$(top_srcdir)/libpurple \
-I$(top_builddir)/libpurple \
$(LIBGADU_CFLAGS) \
$(INTGG_CFLAGS) \
$(GLIB_CFLAGS) \
+ $(JSON_CFLAGS) \
$(DEBUG_CFLAGS)
diff --git a/libpurple/protocols/gg/Makefile.mingw b/libpurple/protocols/gg/Makefile.mingw
index 2010074360..d6a9972b3a 100644
--- a/libpurple/protocols/gg/Makefile.mingw
+++ b/libpurple/protocols/gg/Makefile.mingw
@@ -24,51 +24,54 @@ endif
##
## INCLUDE PATHS
##
-INCLUDE_PATHS += \
+INCLUDE_PATHS +=\
-I$(PIDGIN_TREE_TOP) \
-I$(PURPLE_TOP) \
- -I. \
- -I./lib \
+ -I$(PURPLE_TOP)/win32 \
-I$(GTK_TOP)/include \
-I$(GTK_TOP)/include/glib-2.0 \
-I$(GTK_TOP)/lib/glib-2.0/include \
- -I$(PURPLE_TOP)/win32
+ -I$(GNUTLS_TOP)/include \
+ -I$(JSON_GLIB_TOP)/include/json-glib-1.0 \
+ -I$(LIBGADU_TOP)/include
-LIB_PATHS += -L$(GTK_TOP)/lib \
- -L$(PURPLE_TOP) \
+LIB_PATHS += \
+ -L$(GTK_TOP)/lib \
+ -L$(GNUTLS_TOP)/lib \
+ -L$(LIBGADU_TOP)/lib \
+ -L$(JSON_GLIB_TOP)/lib \
+ -L$(PURPLE_TOP)
##
## SOURCES, OBJECTS
##
C_SRC = \
- lib/common.c \
- lib/dcc7.c \
- lib/dcc.c \
- lib/debug.c \
- lib/deflate.c \
- lib/encoding.c \
- lib/endian.c \
- lib/events.c \
- lib/handlers.c \
- lib/http.c \
- lib/libgadu.c \
- lib/message.c \
- lib/network.c \
- lib/obsolete.c \
- lib/packets.pb-c.c \
- lib/protobuf.c \
- lib/protobuf-c.c \
- lib/pubdir50.c \
- lib/pubdir.c \
- lib/resolver.c \
- lib/sha1.c \
- lib/tvbuff.c \
- lib/tvbuilder.c \
- buddylist.c \
- confer.c \
+ avatar.c \
+ blist.c \
+ chat.c \
+ deprecated.c \
+ edisc.c \
gg.c \
- search.c \
- gg-utils.c
+ html.c \
+ image-prpl.c \
+ keymapper.c \
+ libgadu-events.c \
+ libgaduw.c \
+ message-prpl.c \
+ multilogon.c \
+ oauth/oauth.c \
+ oauth/oauth-parameter.c \
+ oauth/oauth-purple.c \
+ pubdir-prpl.c \
+ purplew.c \
+ resolver-purple.c \
+ roster.c \
+ servconn.c \
+ status.c \
+ tcpsocket.c \
+ utils.c \
+ validator.c \
+ xml.c
OBJECTS = $(C_SRC:%.c=%.o)
@@ -76,8 +79,12 @@ OBJECTS = $(C_SRC:%.c=%.o)
## LIBRARIES
##
LIBS = \
+ -lgadu \
-lglib-2.0 \
+ -lgobject-2.0 \
+ -lgnutls \
-lintl \
+ -ljson-glib-1.0 \
-lpurple \
-lws2_32 \
-lz
diff --git a/libpurple/protocols/gg/account.c b/libpurple/protocols/gg/account.c
new file mode 100644
index 0000000000..31a511c9a7
--- /dev/null
+++ b/libpurple/protocols/gg/account.c
@@ -0,0 +1,645 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 "account.h"
+
+#include <libgadu.h>
+#include <debug.h>
+
+#include "deprecated.h"
+#include "purplew.h"
+#include "utils.h"
+#include "libgaduw.h"
+#include "validator.h"
+
+/*******************************************************************************
+ * Token requesting.
+ ******************************************************************************/
+
+typedef struct
+{
+ ggp_account_token_cb callback;
+ PurpleConnection *gc;
+ void *user_data;
+} ggp_account_token_reqdata;
+
+static void ggp_account_token_response(struct gg_http *h, gboolean success,
+ gboolean cancelled, gpointer _reqdata);
+
+/******************************************************************************/
+
+void ggp_account_token_free(ggp_account_token *token)
+{
+ if (!token)
+ return;
+ g_free(token->id);
+ g_free(token->data);
+ g_free(token);
+}
+
+void ggp_account_token_request(PurpleConnection *gc,
+ ggp_account_token_cb callback, void *user_data)
+{
+ struct gg_http *h;
+ ggp_account_token_reqdata *reqdata;
+
+ purple_debug_info("gg", "ggp_account_token_request: "
+ "requesting token...\n");
+
+ if (!ggp_deprecated_setup_proxy(gc)) {
+ callback(gc, NULL, user_data);
+ return;
+ }
+
+ h = gg_token(TRUE);
+
+ if (!h) {
+ callback(gc, NULL, user_data);
+ return;
+ }
+
+ reqdata = g_new(ggp_account_token_reqdata, 1);
+ reqdata->callback = callback;
+ reqdata->gc = gc;
+ reqdata->user_data = user_data;
+ ggp_libgaduw_http_watch(gc, h, ggp_account_token_response, reqdata,
+ TRUE);
+}
+
+static void ggp_account_token_response(struct gg_http *h, gboolean success,
+ gboolean cancelled, gpointer _reqdata)
+{
+ ggp_account_token_reqdata *reqdata = _reqdata;
+ struct gg_token *token_info;
+ ggp_account_token *token = NULL;
+
+ g_assert(!(success && cancelled));
+
+ if (cancelled)
+ purple_debug_info("gg", "ggp_account_token_handler: "
+ "cancelled\n");
+ else if (success) {
+ purple_debug_info("gg", "ggp_account_token_handler: "
+ "got token\n");
+
+ token = g_new(ggp_account_token, 1);
+
+ token_info = h->data;
+ token->id = g_strdup(token_info->tokenid);
+ token->size = h->body_size;
+ token->data = g_memdup(h->body, token->size);
+ token->length = token_info->length;
+ } else {
+ purple_debug_error("gg", "ggp_account_token_handler: error\n");
+ purple_notify_error(
+ purple_connection_get_account(reqdata->gc),
+ _("Token Error"),
+ _("Unable to fetch the token."), NULL,
+ purple_request_cpar_from_connection(reqdata->gc));
+ }
+
+ reqdata->callback(reqdata->gc, token, reqdata->user_data);
+ g_free(reqdata);
+}
+
+gboolean ggp_account_token_validate(ggp_account_token *token,
+ const gchar *value)
+{
+ if (strlen(value) != token->length)
+ return FALSE;
+ return g_regex_match_simple("^[a-zA-Z0-9]+$", value, 0, 0);
+}
+
+/*******************************************************************************
+ * New account registration.
+ ******************************************************************************/
+
+typedef struct
+{
+ ggp_account_token *token;
+ PurpleConnection *gc;
+
+ gchar *email;
+ gchar *password;
+ gchar *token_value;
+ gboolean password_remember;
+} ggp_account_register_data;
+
+static void ggp_account_register_dialog(PurpleConnection *gc,
+ ggp_account_token *token, gpointer _register_data);
+static void ggp_account_register_dialog_ok(
+ ggp_account_register_data *register_data, PurpleRequestFields *fields);
+#if 0
+static void ggp_account_register_dialog_invalid(
+ ggp_account_register_data *register_data, const gchar *message);
+#endif
+static void ggp_account_register_dialog_cancel(
+ ggp_account_register_data *register_data, PurpleRequestFields *fields);
+static void ggp_account_register_response(struct gg_http *h, gboolean success,
+ gboolean cancelled, gpointer _reqdata);
+static void ggp_account_register_completed(
+ ggp_account_register_data *register_data, gboolean success);
+
+#define GGP_ACCOUNT_REGISTER_TITLE _("Register New Gadu-Gadu Account")
+
+/******************************************************************************/
+
+void ggp_account_register(PurpleAccount *account)
+{
+ PurpleConnection *gc = purple_account_get_connection(account);
+ ggp_account_register_data *register_data;
+
+ purple_debug_info("gg", "ggp_account_register\n");
+
+ register_data = g_new0(ggp_account_register_data, 1);
+ register_data->gc = gc;
+ register_data->password_remember = TRUE;
+
+ ggp_account_token_request(gc, ggp_account_register_dialog,
+ register_data);
+}
+
+static void ggp_account_register_dialog(PurpleConnection *gc,
+ ggp_account_token *token, gpointer _register_data)
+{
+ PurpleRequestFields *fields;
+ PurpleRequestFieldGroup *main_group, *password_group, *token_group;
+ PurpleRequestField *field, *field_password;
+ ggp_account_register_data *register_data = _register_data;
+
+ purple_debug_info("gg", "ggp_account_register_dialog(%p, %p, %p)\n",
+ gc, token, _register_data);
+ if (!token) {
+ ggp_account_register_completed(register_data, FALSE);
+ return;
+ }
+
+ fields = purple_request_fields_new();
+ main_group = purple_request_field_group_new(NULL);
+ purple_request_fields_add_group(fields, main_group);
+
+ field = purple_request_field_string_new("email", _("Email"),
+ register_data->email, FALSE);
+ purple_request_field_set_required(field, TRUE);
+ purple_request_field_set_validator(field,
+ purple_request_field_email_validator, NULL);
+ purple_request_field_group_add_field(main_group, field);
+
+ password_group = purple_request_field_group_new(_("Password"));
+ purple_request_fields_add_group(fields, password_group);
+
+ field = purple_request_field_string_new("password1", _("Password"),
+ register_data->password, FALSE);
+ purple_request_field_set_required(field, TRUE);
+ purple_request_field_string_set_masked(field, TRUE);
+ purple_request_field_set_validator(field, ggp_validator_password, NULL);
+ purple_request_field_group_add_field(password_group, field);
+ field_password = field;
+
+ field = purple_request_field_string_new("password2",
+ _("Password (again)"), register_data->password, FALSE);
+ purple_request_field_set_required(field, TRUE);
+ purple_request_field_string_set_masked(field, TRUE);
+ purple_request_field_set_validator(field, ggp_validator_password_equal,
+ field_password);
+ purple_request_field_group_add_field(password_group, field);
+
+ field = purple_request_field_bool_new("password_remember",
+ _("Remember password"), register_data->password_remember);
+ purple_request_field_group_add_field(password_group, field);
+
+ token_group = purple_request_field_group_new(_("Captcha"));
+ purple_request_fields_add_group(fields, token_group);
+
+ field = purple_request_field_string_new("token_value",
+ _("Enter text from image below"), register_data->token_value,
+ FALSE);
+ purple_request_field_set_required(field, TRUE);
+ purple_request_field_set_validator(field, ggp_validator_token, token);
+ purple_request_field_group_add_field(token_group, field);
+ purple_debug_info("gg", "token set %p\n", register_data->token);
+
+ field = purple_request_field_image_new("token_image", _("Captcha"),
+ token->data, token->size);
+ purple_request_field_group_add_field(token_group, field);
+
+ register_data->token = token;
+
+ purple_request_fields(gc,
+ GGP_ACCOUNT_REGISTER_TITLE,
+ GGP_ACCOUNT_REGISTER_TITLE,
+ _("Please, fill in the following fields"), fields,
+ _("OK"), G_CALLBACK(ggp_account_register_dialog_ok),
+ _("Cancel"), G_CALLBACK(ggp_account_register_dialog_cancel),
+ purple_request_cpar_from_connection(gc), register_data);
+}
+
+static void ggp_account_register_dialog_cancel(
+ ggp_account_register_data *register_data, PurpleRequestFields *fields)
+{
+ purple_debug_info("gg", "ggp_account_register_dialog_cancel(%p, %p)\n",
+ register_data, fields);
+
+ ggp_account_register_completed(register_data, FALSE);
+}
+
+static void ggp_account_register_dialog_ok(
+ ggp_account_register_data *register_data, PurpleRequestFields *fields)
+{
+ struct gg_http *h;
+
+ purple_debug_misc("gg", "ggp_account_register_dialog_ok(%p, %p)\n",
+ register_data, fields);
+
+ g_free(register_data->email);
+ g_free(register_data->password);
+ g_free(register_data->token_value);
+
+ register_data->email = g_strdup(
+ purple_request_fields_get_string(fields, "email"));
+ register_data->password = g_strdup(
+ purple_request_fields_get_string(fields, "password1"));
+ register_data->password_remember =
+ purple_request_fields_get_bool(fields, "password_remember");
+ register_data->token_value = g_strdup(
+ purple_request_fields_get_string(fields, "token_value"));
+
+ g_assert(register_data->email != NULL);
+ g_assert(register_data->password != NULL);
+ g_assert(register_data->token_value != NULL);
+
+ h = gg_register3(register_data->email, register_data->password,
+ register_data->token->id, register_data->token_value, TRUE);
+
+ ggp_libgaduw_http_watch(register_data->gc, h,
+ ggp_account_register_response, register_data, TRUE);
+}
+
+#if 0
+/* libgadu 1.12.x: use it for invalid token */
+static void ggp_account_register_dialog_invalid(
+ ggp_account_register_data *register_data, const gchar *message)
+{
+ purple_debug_warning("gg", "ggp_account_register_dialog_invalid: %s\n",
+ message);
+ ggp_account_register_dialog(register_data->gc, register_data->token,
+ register_data);
+ purple_notify_error(purple_connection_get_account(register_data->gc),
+ GGP_ACCOUNT_REGISTER_TITLE, message, NULL);
+}
+#endif
+
+static void ggp_account_register_response(struct gg_http *h, gboolean success,
+ gboolean cancelled, gpointer _register_data)
+{
+ ggp_account_register_data *register_data = _register_data;
+ PurpleAccount *account =
+ purple_connection_get_account(register_data->gc);
+ struct gg_pubdir *register_result = h->data;
+ uin_t uin;
+ gchar *tmp;
+
+ g_assert(!(success && cancelled));
+
+ if (cancelled) {
+ purple_debug_info("gg", "ggp_account_register_response: "
+ "cancelled\n");
+ ggp_account_register_completed(register_data, FALSE);
+ return;
+ }
+ if (!success || !register_result->success) {
+ /* TODO (libgadu 1.12.x): check register_result->error */
+ purple_debug_error("gg", "ggp_account_register_response: "
+ "error\n");
+ purple_notify_error(NULL,
+ GGP_ACCOUNT_REGISTER_TITLE,
+ _("Unable to register new account. "
+ "An unknown error occurred."), NULL,
+ purple_request_cpar_from_account(account));
+ ggp_account_register_completed(register_data, FALSE);
+ return;
+ }
+
+ uin = register_result->uin;
+ purple_debug_info("gg", "ggp_account_register_response: "
+ "registered uin %u\n", uin);
+
+ purple_account_set_username(account, ggp_uin_to_str(uin));
+ purple_account_set_remember_password(account,
+ register_data->password_remember);
+ purple_account_set_password(account, register_data->password,
+ NULL, NULL);
+
+ tmp = g_strdup_printf(_("Your new GG number: %u."), uin);
+ purple_notify_info(account, GGP_ACCOUNT_REGISTER_TITLE,
+ _("Registration completed successfully!"), tmp,
+ purple_request_cpar_from_account(account));
+ g_free(tmp);
+
+ ggp_account_register_completed(register_data, TRUE);
+}
+
+static void ggp_account_register_completed(
+ ggp_account_register_data *register_data, gboolean success)
+{
+ PurpleAccount *account =
+ purple_connection_get_account(register_data->gc);
+
+ purple_debug_misc("gg", "ggp_account_register_completed: %d\n",
+ success);
+
+ g_free(register_data->email);
+ g_free(register_data->password);
+ g_free(register_data->token_value);
+ ggp_account_token_free(register_data->token);
+ g_free(register_data);
+
+ purple_account_disconnect(account);
+ purple_account_register_completed(account, success);
+}
+
+/*******************************************************************************
+ * Password change.
+ ******************************************************************************/
+
+typedef struct
+{
+ ggp_account_token *token;
+ PurpleConnection *gc;
+
+ gchar *email;
+ gchar *password_current;
+ gchar *password_new;
+ gchar *token_value;
+} ggp_account_chpass_data;
+
+static void ggp_account_chpass_data_free(ggp_account_chpass_data *chpass_data);
+static void ggp_account_chpass_dialog(PurpleConnection *gc,
+ ggp_account_token *token, gpointer _chpass_data);
+static void ggp_account_chpass_dialog_ok(
+ ggp_account_chpass_data *chpass_data, PurpleRequestFields *fields);
+static void ggp_account_chpass_dialog_invalid(
+ ggp_account_chpass_data *chpass_data, const gchar *message);
+static void ggp_account_chpass_dialog_cancel(
+ ggp_account_chpass_data *chpass_data, PurpleRequestFields *fields);
+static void ggp_account_chpass_response(struct gg_http *h, gboolean success,
+ gboolean cancelled, gpointer _chpass_data);
+
+#define GGP_ACCOUNT_CHPASS_TITLE _("Password change")
+
+/******************************************************************************/
+
+static void ggp_account_chpass_data_free(ggp_account_chpass_data *chpass_data)
+{
+ g_free(chpass_data->email);
+ g_free(chpass_data->password_current);
+ g_free(chpass_data->password_new);
+ g_free(chpass_data->token_value);
+ ggp_account_token_free(chpass_data->token);
+ g_free(chpass_data);
+}
+
+void ggp_account_chpass(PurpleConnection *gc)
+{
+ ggp_account_chpass_data *chpass_data;
+
+ purple_debug_info("gg", "ggp_account_chpass\n");
+
+ chpass_data = g_new0(ggp_account_chpass_data, 1);
+ chpass_data->gc = gc;
+
+ ggp_account_token_request(gc, ggp_account_chpass_dialog, chpass_data);
+}
+
+static void ggp_account_chpass_dialog(PurpleConnection *gc,
+ ggp_account_token *token, gpointer _chpass_data)
+{
+ ggp_account_chpass_data *chpass_data = _chpass_data;
+ PurpleAccount *account = purple_connection_get_account(chpass_data->gc);
+ PurpleRequestFields *fields;
+ PurpleRequestFieldGroup *main_group, *password_group, *token_group;
+ PurpleRequestField *field, *field_password;
+ gchar *primary;
+
+ purple_debug_info("gg", "ggp_account_chpass_dialog(%p, %p, %p)\n",
+ gc, token, _chpass_data);
+ if (!token) {
+ ggp_account_chpass_data_free(chpass_data);
+ return;
+ }
+
+ fields = purple_request_fields_new();
+ main_group = purple_request_field_group_new(NULL);
+ purple_request_fields_add_group(fields, main_group);
+
+ field = purple_request_field_string_new("email",
+ _("New email address"), chpass_data->email, FALSE);
+ purple_request_field_set_required(field, TRUE);
+ purple_request_field_set_validator(field,
+ purple_request_field_email_validator, NULL);
+ purple_request_field_group_add_field(main_group, field);
+
+ password_group = purple_request_field_group_new(_("Password"));
+ purple_request_fields_add_group(fields, password_group);
+
+ field = purple_request_field_string_new("password_current",
+ _("Current password"), chpass_data->password_current, FALSE);
+ purple_request_field_set_required(field, TRUE);
+ purple_request_field_string_set_masked(field, TRUE);
+ purple_request_field_group_add_field(password_group, field);
+
+ field = purple_request_field_string_new("password_new1",
+ _("Password"), chpass_data->password_new, FALSE);
+ purple_request_field_set_required(field, TRUE);
+ purple_request_field_string_set_masked(field, TRUE);
+ purple_request_field_set_validator(field, ggp_validator_password, NULL);
+ purple_request_field_group_add_field(password_group, field);
+ field_password = field;
+
+ field = purple_request_field_string_new("password_new2",
+ _("Password (retype)"), chpass_data->password_new, FALSE);
+ purple_request_field_set_required(field, TRUE);
+ purple_request_field_string_set_masked(field, TRUE);
+ purple_request_field_set_validator(field, ggp_validator_password_equal,
+ field_password);
+ purple_request_field_group_add_field(password_group, field);
+
+ token_group = purple_request_field_group_new(_("Captcha"));
+ purple_request_fields_add_group(fields, token_group);
+
+ field = purple_request_field_string_new("token_value",
+ _("Enter text from image below"), chpass_data->token_value,
+ FALSE);
+ purple_request_field_set_required(field, TRUE);
+ purple_request_field_set_validator(field, ggp_validator_token, token);
+ purple_request_field_group_add_field(token_group, field);
+
+ field = purple_request_field_image_new("token_image", _("Captcha"),
+ token->data, token->size);
+ purple_request_field_group_add_field(token_group, field);
+
+ chpass_data->token = token;
+
+ primary = g_strdup_printf(_("Change password for %s"),
+ purple_account_get_username(account));
+
+ purple_request_fields(gc, GGP_ACCOUNT_CHPASS_TITLE, primary,
+ _("Please enter your current password and your new password."),
+ fields,
+ _("OK"), G_CALLBACK(ggp_account_chpass_dialog_ok),
+ _("Cancel"), G_CALLBACK(ggp_account_chpass_dialog_cancel),
+ purple_request_cpar_from_connection(gc), chpass_data);
+
+ g_free(primary);
+}
+
+static void ggp_account_chpass_dialog_ok(
+ ggp_account_chpass_data *chpass_data, PurpleRequestFields *fields)
+{
+ PurpleAccount *account = purple_connection_get_account(chpass_data->gc);
+ struct gg_http *h;
+ uin_t uin;
+
+ purple_debug_misc("gg", "ggp_account_chpass_dialog_ok(%p, %p)\n",
+ chpass_data, fields);
+
+ g_free(chpass_data->email);
+ g_free(chpass_data->password_current);
+ g_free(chpass_data->password_new);
+ g_free(chpass_data->token_value);
+
+ chpass_data->email = g_strdup(
+ purple_request_fields_get_string(fields, "email"));
+ chpass_data->password_current = g_strdup(
+ purple_request_fields_get_string(fields, "password_current"));
+ chpass_data->password_new = g_strdup(
+ purple_request_fields_get_string(fields, "password_new1"));
+ chpass_data->token_value = g_strdup(
+ purple_request_fields_get_string(fields, "token_value"));
+
+ g_assert(chpass_data->email != NULL);
+ g_assert(chpass_data->password_current != NULL);
+ g_assert(chpass_data->password_new != NULL);
+ g_assert(chpass_data->token_value != NULL);
+
+ if (g_utf8_collate(chpass_data->password_current,
+ purple_connection_get_password(chpass_data->gc)) != 0)
+ {
+ g_free(chpass_data->password_current);
+ chpass_data->password_current = NULL;
+ ggp_account_chpass_dialog_invalid(chpass_data,
+ _("Your current password is different from the one that"
+ " you specified."));
+ return;
+ }
+ if (g_utf8_collate(chpass_data->password_current,
+ chpass_data->password_new) == 0)
+ {
+ g_free(chpass_data->password_new);
+ chpass_data->password_new = NULL;
+ ggp_account_chpass_dialog_invalid(chpass_data,
+ _("New password have to be different from the current "
+ "one."));
+ return;
+ }
+
+ uin = ggp_str_to_uin(purple_account_get_username(account));
+ purple_debug_info("gg", "ggp_account_chpass_dialog_ok: validation ok "
+ "[token id=%s, value=%s]\n",
+ chpass_data->token->id, chpass_data->token_value);
+ h = gg_change_passwd4(uin, chpass_data->email,
+ chpass_data->password_current, chpass_data->password_new,
+ chpass_data->token->id, chpass_data->token_value, TRUE);
+
+ ggp_libgaduw_http_watch(chpass_data->gc, h,
+ ggp_account_chpass_response, chpass_data, TRUE);
+}
+
+static void ggp_account_chpass_dialog_invalid(
+ ggp_account_chpass_data *chpass_data, const gchar *message)
+{
+ purple_debug_warning("gg", "ggp_account_chpass_dialog_invalid: %s\n",
+ message);
+ ggp_account_chpass_dialog(chpass_data->gc, chpass_data->token,
+ chpass_data);
+ purple_notify_error(purple_connection_get_account(chpass_data->gc),
+ GGP_ACCOUNT_CHPASS_TITLE, message, NULL,
+ purple_request_cpar_from_connection(chpass_data->gc));
+}
+
+static void ggp_account_chpass_dialog_cancel(
+ ggp_account_chpass_data *chpass_data, PurpleRequestFields *fields)
+{
+ ggp_account_chpass_data_free(chpass_data);
+}
+
+static void ggp_account_chpass_response(struct gg_http *h, gboolean success,
+ gboolean cancelled, gpointer _chpass_data)
+{
+ ggp_account_chpass_data *chpass_data = _chpass_data;
+ PurpleAccount *account =
+ purple_connection_get_account(chpass_data->gc);
+ struct gg_pubdir *chpass_result = h->data;
+
+ g_assert(!(success && cancelled));
+
+ if (cancelled) {
+ purple_debug_info("gg", "ggp_account_chpass_response: "
+ "cancelled\n");
+ ggp_account_chpass_data_free(chpass_data);
+ return;
+ }
+ if (!success || !chpass_result->success) {
+ /* TODO (libgadu 1.12.x): check chpass_result->error */
+ purple_debug_error("gg", "ggp_account_chpass_response: "
+ "error\n");
+ purple_notify_error(NULL,
+ GGP_ACCOUNT_CHPASS_TITLE,
+ _("Unable to change password. "
+ "An unknown error occurred."), NULL,
+ purple_request_cpar_from_connection(chpass_data->gc));
+ ggp_account_chpass_data_free(chpass_data);
+ return;
+ }
+
+ purple_debug_info("gg", "ggp_account_chpass_response: "
+ "password changed\n");
+
+ purple_account_set_password(account, chpass_data->password_new,
+ NULL, NULL);
+
+ purple_notify_info(account, GGP_ACCOUNT_CHPASS_TITLE,
+ _("Your password has been changed."), NULL,
+ purple_request_cpar_from_connection(chpass_data->gc));
+
+ ggp_account_chpass_data_free(chpass_data);
+
+ /* TODO: reconnect / check how it is done in original client */
+ purple_account_disconnect(account);
+}
diff --git a/libpurple/protocols/gg/account.h b/libpurple/protocols/gg/account.h
new file mode 100644
index 0000000000..383d989d0a
--- /dev/null
+++ b/libpurple/protocols/gg/account.h
@@ -0,0 +1,62 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 _GGP_ACCOUNT_H
+#define _GGP_ACCOUNT_H
+
+#error "This file is outdated"
+
+#include <internal.h>
+
+typedef struct
+{
+ gchar *id;
+ gpointer data;
+ size_t size;
+ guint length;
+} ggp_account_token;
+
+/**
+ * token must be free'd with ggp_account_token_free
+ */
+typedef void (*ggp_account_token_cb)(PurpleConnection *gc,
+ ggp_account_token *token, gpointer user_data);
+
+void ggp_account_token_request(PurpleConnection *gc,
+ ggp_account_token_cb callback, void *user_data);
+gboolean ggp_account_token_validate(ggp_account_token *token,
+ const gchar *value);
+void ggp_account_token_free(ggp_account_token *token);
+
+
+void ggp_account_register(PurpleAccount *account);
+
+void ggp_account_chpass(PurpleConnection *gc);
+
+#endif /* _GGP_ACCOUNT_H */
diff --git a/libpurple/protocols/gg/avatar.c b/libpurple/protocols/gg/avatar.c
new file mode 100644
index 0000000000..1a6b9f919b
--- /dev/null
+++ b/libpurple/protocols/gg/avatar.c
@@ -0,0 +1,396 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 "avatar.h"
+
+#include <debug.h>
+#include <glibcompat.h>
+#include <http.h>
+
+#include "gg.h"
+#include "utils.h"
+#include "oauth/oauth-purple.h"
+
+/* Common */
+
+static inline ggp_avatar_session_data *
+ggp_avatar_get_avdata(PurpleConnection *gc);
+
+static gboolean ggp_avatar_timer_cb(gpointer _gc);
+
+#define GGP_AVATAR_USERAGENT "GG Client build 11.0.0.7562"
+#define GGP_AVATAR_SIZE_MAX 1048576
+
+/* Buddy avatars updating */
+
+typedef struct
+{
+ uin_t uin;
+ time_t timestamp;
+
+ PurpleConnection *gc;
+ PurpleHttpConnection *request;
+} ggp_avatar_buddy_update_req;
+
+static gboolean ggp_avatar_buddy_update_next(PurpleConnection *gc);
+static void ggp_avatar_buddy_update_received(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer user_data);
+
+#define GGP_AVATAR_BUDDY_URL "http://avatars.gg.pl/%u/s,big"
+
+/* Own avatar setting */
+
+typedef struct
+{
+ PurpleImage *img;
+} ggp_avatar_own_data;
+
+static void ggp_avatar_own_got_token(PurpleConnection *gc, const gchar *token,
+ gpointer img);
+static void ggp_avatar_own_sent(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer user_data);
+
+#define GGP_AVATAR_RESPONSE_MAX 10240
+
+/*******************************************************************************
+ * Common.
+ ******************************************************************************/
+
+void ggp_avatar_setup(PurpleConnection *gc)
+{
+ ggp_avatar_session_data *avdata = ggp_avatar_get_avdata(gc);
+
+ avdata->pending_updates = NULL;
+ avdata->current_update = NULL;
+ avdata->own_data = g_new0(ggp_avatar_own_data, 1);
+
+ avdata->timer = purple_timeout_add_seconds(1, ggp_avatar_timer_cb, gc);
+}
+
+void ggp_avatar_cleanup(PurpleConnection *gc)
+{
+ ggp_avatar_session_data *avdata = ggp_avatar_get_avdata(gc);
+
+ purple_timeout_remove(avdata->timer);
+
+ if (avdata->current_update != NULL) {
+ ggp_avatar_buddy_update_req *current_update =
+ avdata->current_update;
+
+ purple_http_conn_cancel(current_update->request);
+ g_free(current_update);
+ }
+ avdata->current_update = NULL;
+
+ g_free(avdata->own_data);
+
+ g_list_free_full(avdata->pending_updates, &g_free);
+ avdata->pending_updates = NULL;
+}
+
+static inline ggp_avatar_session_data *
+ggp_avatar_get_avdata(PurpleConnection *gc)
+{
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+ return &accdata->avatar_data;
+}
+
+static gboolean ggp_avatar_timer_cb(gpointer _gc)
+{
+ PurpleConnection *gc = _gc;
+ ggp_avatar_session_data *avdata;
+
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
+
+ avdata = ggp_avatar_get_avdata(gc);
+ if (avdata->current_update != NULL) {
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("gg", "ggp_avatar_timer_cb(%p): "
+ "there is already an update running\n", gc);
+ }
+ return TRUE;
+ }
+
+ while (!ggp_avatar_buddy_update_next(gc));
+
+ return TRUE;
+}
+
+/*******************************************************************************
+ * Buddy avatars updating.
+ ******************************************************************************/
+
+void ggp_avatar_buddy_update(PurpleConnection *gc, uin_t uin, time_t timestamp)
+{
+ ggp_avatar_session_data *avdata = ggp_avatar_get_avdata(gc);
+ ggp_avatar_buddy_update_req *pending_update =
+ g_new(ggp_avatar_buddy_update_req, 1); /* TODO: leak? */
+
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("gg", "ggp_avatar_buddy_update(%p, %u, %lu)\n", gc,
+ uin, timestamp);
+ }
+
+ pending_update->uin = uin;
+ pending_update->timestamp = timestamp;
+
+ avdata->pending_updates = g_list_append(avdata->pending_updates,
+ pending_update);
+}
+
+void ggp_avatar_buddy_remove(PurpleConnection *gc, uin_t uin)
+{
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("gg", "ggp_avatar_buddy_remove(%p, %u)\n", gc, uin);
+ }
+
+ purple_buddy_icons_set_for_user(purple_connection_get_account(gc),
+ ggp_uin_to_str(uin), NULL, 0, NULL);
+}
+
+/* return TRUE if avatar update was performed or there is no new requests,
+ FALSE if we can request another one immediately */
+static gboolean ggp_avatar_buddy_update_next(PurpleConnection *gc)
+{
+ PurpleHttpRequest *req;
+ ggp_avatar_session_data *avdata = ggp_avatar_get_avdata(gc);
+ GList *pending_update_it;
+ ggp_avatar_buddy_update_req *pending_update;
+ PurpleBuddy *buddy;
+ PurpleAccount *account = purple_connection_get_account(gc);
+ time_t old_timestamp;
+ const char *old_timestamp_str;
+
+ pending_update_it = g_list_first(avdata->pending_updates);
+ if (pending_update_it == NULL)
+ return TRUE;
+
+ pending_update = pending_update_it->data;
+ avdata->pending_updates = g_list_remove(avdata->pending_updates,
+ pending_update);
+ buddy = purple_blist_find_buddy(account, ggp_uin_to_str(pending_update->uin));
+
+ if (!buddy) {
+ if (ggp_str_to_uin(purple_account_get_username(account)) ==
+ pending_update->uin)
+ {
+ purple_debug_misc("gg",
+ "ggp_avatar_buddy_update_next(%p): own "
+ "avatar update requested, but we don't have "
+ "ourselves on buddy list\n", gc);
+ } else {
+ purple_debug_warning("gg",
+ "ggp_avatar_buddy_update_next(%p): "
+ "%u update requested, but he's not on buddy "
+ "list\n", gc, pending_update->uin);
+ }
+ return FALSE;
+ }
+
+ old_timestamp_str = purple_buddy_icons_get_checksum_for_user(buddy);
+ old_timestamp = old_timestamp_str ? g_ascii_strtoull(
+ old_timestamp_str, NULL, 10) : 0;
+ if (old_timestamp == pending_update->timestamp) {
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("gg",
+ "ggp_avatar_buddy_update_next(%p): "
+ "%u have up to date avatar with ts=%lu\n", gc,
+ pending_update->uin, pending_update->timestamp);
+ }
+ return FALSE;
+ }
+ if (old_timestamp > pending_update->timestamp) {
+ purple_debug_warning("gg",
+ "ggp_avatar_buddy_update_next(%p): "
+ "saved timestamp for %u is newer than received "
+ "(%lu > %lu)\n", gc, pending_update->uin, old_timestamp,
+ pending_update->timestamp);
+ }
+
+ purple_debug_info("gg",
+ "ggp_avatar_buddy_update_next(%p): "
+ "updating %u with ts=%lu...\n", gc, pending_update->uin,
+ pending_update->timestamp);
+
+ pending_update->gc = gc;
+ avdata->current_update = pending_update;
+
+ req = purple_http_request_new(NULL);
+ purple_http_request_set_url_printf(req, GGP_AVATAR_BUDDY_URL,
+ pending_update->uin);
+ purple_http_request_header_set(req, "User-Agent", GGP_AVATAR_USERAGENT);
+ purple_http_request_set_max_len(req, GGP_AVATAR_SIZE_MAX);
+ pending_update->request = purple_http_request(gc, req,
+ ggp_avatar_buddy_update_received, pending_update);
+ purple_http_request_unref(req);
+
+ return TRUE;
+}
+
+static void ggp_avatar_buddy_update_received(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _pending_update)
+{
+ ggp_avatar_buddy_update_req *pending_update = _pending_update;
+ PurpleBuddy *buddy;
+ PurpleAccount *account;
+ PurpleConnection *gc = pending_update->gc;
+ ggp_avatar_session_data *avdata;
+ gchar timestamp_str[20];
+ const gchar *got_data;
+ size_t got_len;
+
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
+
+ avdata = ggp_avatar_get_avdata(gc);
+ g_assert(pending_update == avdata->current_update);
+ avdata->current_update = NULL;
+
+ if (!purple_http_response_is_successful(response)) {
+ purple_debug_error("gg", "ggp_avatar_buddy_update_received: bad"
+ " response while getting avatar for %u: %s\n",
+ pending_update->uin,
+ purple_http_response_get_error(response));
+ g_free(pending_update);
+ return;
+ }
+
+ account = purple_connection_get_account(gc);
+ buddy = purple_blist_find_buddy(account, ggp_uin_to_str(pending_update->uin));
+
+ if (!buddy) {
+ purple_debug_warning("gg", "ggp_avatar_buddy_update_received: "
+ "buddy %u disappeared\n", pending_update->uin);
+ g_free(pending_update);
+ return;
+ }
+
+ g_snprintf(timestamp_str, sizeof(timestamp_str), "%lu",
+ pending_update->timestamp);
+ got_data = purple_http_response_get_data(response, &got_len);
+ purple_buddy_icons_set_for_user(account, purple_buddy_get_name(buddy),
+ g_memdup(got_data, got_len), got_len, timestamp_str);
+
+ purple_debug_info("gg", "ggp_avatar_buddy_update_received: "
+ "got avatar for buddy %u [ts=%lu]\n", pending_update->uin,
+ pending_update->timestamp);
+ g_free(pending_update);
+}
+
+/*******************************************************************************
+ * Own avatar setting.
+ ******************************************************************************/
+
+/**
+ * TODO: use new, GG11 method, when IMToken will be provided by libgadu.
+ *
+ * POST https://avatars.mpa.gg.pl/avatars/user,<uin>/0
+ * Authorization: IMToken 0123456789abcdef0123456789abcdef01234567
+ * photo=<avatar content>
+ */
+
+void ggp_avatar_own_set(PurpleConnection *gc, PurpleImage *img)
+{
+ ggp_avatar_own_data *own_data;
+
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
+
+ purple_debug_info("gg", "ggp_avatar_own_set(%p, %p)", gc, img);
+
+ own_data = ggp_avatar_get_avdata(gc)->own_data;
+
+ if (img == NULL) {
+ purple_debug_warning("gg", "ggp_avatar_own_set: avatar removing"
+ " is probably not possible within old protocol");
+ return;
+ }
+
+ own_data->img = img;
+
+ ggp_oauth_request(gc, ggp_avatar_own_got_token, img, NULL, NULL);
+}
+
+static void
+ggp_avatar_own_got_token(PurpleConnection *gc, const gchar *token,
+ gpointer _img)
+{
+ PurpleHttpRequest *req;
+ PurpleImage *img = _img;
+ ggp_avatar_own_data *own_data = ggp_avatar_get_avdata(gc)->own_data;
+ gchar *img_data, *img_data_e, *request_data;
+ PurpleAccount *account = purple_connection_get_account(gc);
+ uin_t uin = ggp_str_to_uin(purple_account_get_username(account));
+
+ if (img != own_data->img) {
+ purple_debug_warning("gg", "ggp_avatar_own_got_token: "
+ "avatar was changed in meantime\n");
+ return;
+ }
+ own_data->img = NULL;
+
+ img_data = purple_base64_encode(purple_image_get_data(img),
+ purple_image_get_size(img));
+ img_data_e = g_uri_escape_string(img_data, NULL, FALSE);
+ g_free(img_data);
+ request_data = g_strdup_printf("uin=%d&photo=%s", uin, img_data_e);
+ g_free(img_data_e);
+
+ purple_debug_misc("gg", "ggp_avatar_own_got_token: "
+ "uploading new avatar...\n");
+
+ req = purple_http_request_new("http://avatars.nowe.gg/upload");
+ purple_http_request_set_max_len(req, GGP_AVATAR_RESPONSE_MAX);
+ purple_http_request_set_method(req, "POST");
+ purple_http_request_header_set(req, "Authorization", token);
+ purple_http_request_header_set(req, "From", "avatars to avatars");
+ purple_http_request_header_set(req, "Content-Type",
+ "application/x-www-form-urlencoded");
+ purple_http_request_set_contents(req, request_data, -1);
+ purple_http_request(gc, req, ggp_avatar_own_sent, NULL);
+ purple_http_request_unref(req);
+
+ g_free(request_data);
+}
+
+static void ggp_avatar_own_sent(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer user_data)
+{
+ PurpleConnection *gc =
+ purple_http_conn_get_purple_connection(http_conn);
+
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
+
+ if (!purple_http_response_is_successful(response)) {
+ purple_debug_error("gg", "ggp_avatar_own_sent: "
+ "avatar not sent. %s\n",
+ purple_http_response_get_error(response));
+ return;
+ }
+ purple_debug_info("gg", "ggp_avatar_own_sent: %s\n",
+ purple_http_response_get_data(response, NULL));
+}
diff --git a/libpurple/protocols/gg/avatar.h b/libpurple/protocols/gg/avatar.h
new file mode 100644
index 0000000000..d108030a2d
--- /dev/null
+++ b/libpurple/protocols/gg/avatar.h
@@ -0,0 +1,53 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 _GGP_AVATAR_H
+#define _GGP_AVATAR_H
+
+#include <internal.h>
+#include <libgadu.h>
+
+typedef struct
+{
+ guint timer;
+ GList *pending_updates;
+
+ gpointer current_update;
+ gpointer own_data;
+} ggp_avatar_session_data;
+
+void ggp_avatar_setup(PurpleConnection *gc);
+void ggp_avatar_cleanup(PurpleConnection *gc);
+
+void ggp_avatar_buddy_update(PurpleConnection *gc, uin_t uin, time_t timestamp);
+void ggp_avatar_buddy_remove(PurpleConnection *gc, uin_t uin);
+
+void ggp_avatar_own_set(PurpleConnection *gc, PurpleImage *img);
+
+#endif /* _GGP_AVATAR_H */
diff --git a/libpurple/protocols/gg/buddylist.c b/libpurple/protocols/gg/blist.c
index 5f361d2b74..3db3ebccbb 100644
--- a/libpurple/protocols/gg/buddylist.c
+++ b/libpurple/protocols/gg/blist.c
@@ -22,10 +22,11 @@
#include <libgadu.h>
+#include <debug.h>
#include "gg.h"
-#include "gg-utils.h"
-#include "buddylist.h"
+#include "utils.h"
+#include "blist.h"
#define F_FIRSTNAME 0
#define F_LASTNAME 1
@@ -36,9 +37,10 @@
#define F_UIN 6
/* void ggp_buddylist_send(PurpleConnection *gc) {{{ */
+/* this is for for notify purposes, not synchronizing buddy list */
void ggp_buddylist_send(PurpleConnection *gc)
{
- GGPInfo *info = gc->proto_data;
+ GGPInfo *info = purple_connection_get_protocol_data(gc);
PurpleAccount *account = purple_connection_get_account(gc);
GSList *buddies;
uin_t *userlist;
@@ -46,13 +48,13 @@ void ggp_buddylist_send(PurpleConnection *gc)
int i = 0, ret = 0;
int size;
- buddies = purple_find_buddies(account, NULL);
+ buddies = purple_blist_find_buddies(account, NULL);
size = g_slist_length(buddies);
userlist = g_new(uin_t, size);
types = g_new(gchar, size);
- for (buddies = purple_find_buddies(account, NULL); buddies;
+ for (buddies = purple_blist_find_buddies(account, NULL); buddies;
buddies = g_slist_delete_link(buddies, buddies), ++i)
{
PurpleBuddy *buddy = buddies->data;
@@ -61,7 +63,7 @@ void ggp_buddylist_send(PurpleConnection *gc)
userlist[i] = ggp_str_to_uin(name);
types[i] = GG_USER_NORMAL;
purple_debug_info("gg", "ggp_buddylist_send: adding %d\n",
- userlist[i]);
+ userlist[i]);
}
ret = gg_notify_ex(info->session, userlist, types, size);
@@ -81,7 +83,7 @@ void ggp_buddylist_load(PurpleConnection *gc, char *buddylist)
PurpleGroup *group;
gchar **users_tbl;
int i;
- char *utf8buddylist = charset_convert(buddylist, "CP1250", "UTF-8");
+ char *utf8buddylist = ggp_convert_from_cp1250(buddylist);
/* Don't limit the number of records in a buddylist. */
users_tbl = g_strsplit(utf8buddylist, "\r\n", -1);
@@ -90,11 +92,11 @@ void ggp_buddylist_load(PurpleConnection *gc, char *buddylist)
gchar **data_tbl;
gchar *name, *show, *g;
- if (strlen(users_tbl[i]) == 0)
+ if (!*users_tbl[i])
continue;
data_tbl = g_strsplit(users_tbl[i], ";", 8);
- if (ggp_array_size(data_tbl) < 8) {
+ if (g_strv_length(data_tbl) < 8) {
purple_debug_warning("gg",
"Something is wrong on line %d of the buddylist. Skipping.\n",
i + 1);
@@ -116,7 +118,7 @@ void ggp_buddylist_load(PurpleConnection *gc, char *buddylist)
purple_debug_info("gg", "got buddy: name=%s; show=%s\n", name, show);
- if (purple_find_buddy(purple_connection_get_account(gc), name)) {
+ if (purple_blist_find_buddy(purple_connection_get_account(gc), name)) {
g_strfreev(data_tbl);
continue;
}
@@ -127,17 +129,17 @@ void ggp_buddylist_load(PurpleConnection *gc, char *buddylist)
/* XXX: Probably buddy should be added to all the groups. */
/* Hard limit to at most 50 groups */
gchar **group_tbl = g_strsplit(data_tbl[F_GROUP], ",", 50);
- if (ggp_array_size(group_tbl) > 0) {
+ if (g_strv_length(group_tbl) > 0) {
g_free(g);
g = g_strdup(group_tbl[0]);
}
g_strfreev(group_tbl);
}
- buddy = purple_buddy_new(purple_connection_get_account(gc), name,
- strlen(show) ? show : NULL);
+ buddy = purple_buddy_new(purple_connection_get_account(gc),
+ name, strlen(show) ? show : NULL);
- if (!(group = purple_find_group(g))) {
+ if (!(group = purple_blist_find_group(g))) {
group = purple_group_new(g);
purple_blist_add_group(group, NULL);
}
@@ -161,8 +163,9 @@ char *ggp_buddylist_dump(PurpleAccount *account)
GString *buddylist = g_string_sized_new(1024);
char *ptr;
- for (buddies = purple_find_buddies(account, NULL); buddies;
- buddies = g_slist_delete_link(buddies, buddies)) {
+ for (buddies = purple_blist_find_buddies(account, NULL); buddies;
+ buddies = g_slist_delete_link(buddies, buddies))
+ {
PurpleBuddy *buddy = buddies->data;
PurpleGroup *group = purple_buddy_get_group(buddy);
const char *bname = purple_buddy_get_name(buddy);
@@ -178,11 +181,22 @@ char *ggp_buddylist_dump(PurpleAccount *account)
"", gname, bname, "", "");
}
- ptr = charset_convert(buddylist->str, "UTF-8", "CP1250");
+ ptr = ggp_convert_to_cp1250(buddylist->str);
g_string_free(buddylist, TRUE);
return ptr;
}
/* }}} */
+const char * ggp_buddylist_get_buddy_name(PurpleConnection *gc, const uin_t uin)
+{
+ const char *uin_s = ggp_uin_to_str(uin);
+ PurpleBuddy *buddy = purple_blist_find_buddy(
+ purple_connection_get_account(gc), uin_s);
+
+ if (buddy != NULL)
+ return purple_buddy_get_alias(buddy);
+ else
+ return uin_s;
+}
/* vim: set ts=8 sts=0 sw=8 noet: */
diff --git a/libpurple/protocols/gg/buddylist.h b/libpurple/protocols/gg/blist.h
index d1a16dfde8..59df7df3e3 100644
--- a/libpurple/protocols/gg/buddylist.h
+++ b/libpurple/protocols/gg/blist.h
@@ -1,5 +1,5 @@
/**
- * @file buddylist.h
+ * @file blist.h
*
* purple
*
@@ -21,11 +21,10 @@
*/
-#ifndef _PURPLE_GG_BUDDYLIST_H
-#define _PURPLE_GG_BUDDYLIST_H
+#ifndef _PURPLE_GG_BLIST_H
+#define _PURPLE_GG_BLIST_H
#include "connection.h"
-#include "account.h"
void
ggp_buddylist_send(PurpleConnection *gc);
@@ -50,8 +49,18 @@ ggp_buddylist_load(PurpleConnection *gc, char *buddylist);
char *
ggp_buddylist_dump(PurpleAccount *account);
+/**
+ * Returns the best name of a buddy from the buddylist.
+ *
+ * @param gc PurpleConnection instance.
+ * @param uin UIN of the buddy.
+ *
+ * @return Name of the buddy, or UIN converted to string, if there is no such
+ * user on the list.
+ */
+const char * ggp_buddylist_get_buddy_name(PurpleConnection *gc, const uin_t uin);
-#endif /* _PURPLE_GG_BUDDYLIST_H */
+#endif /* _PURPLE_GG_BLIST_H */
/* vim: set ts=8 sts=0 sw=8 noet: */
diff --git a/libpurple/protocols/gg/chat.c b/libpurple/protocols/gg/chat.c
new file mode 100644
index 0000000000..1dd9c14c7f
--- /dev/null
+++ b/libpurple/protocols/gg/chat.c
@@ -0,0 +1,623 @@
+/* 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.
+ *
+ * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
+ * As a recipient of this file you may choose, which license to receive the
+ * code under. As a contributor, you have to ensure the new code is
+ * compatible with both.
+ *
+ * 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 "chat.h"
+
+#include <debug.h>
+
+#include "gg.h"
+#include "utils.h"
+#include "message-prpl.h"
+
+#if GGP_ENABLE_GG11
+
+typedef struct _ggp_chat_local_info ggp_chat_local_info;
+
+struct _ggp_chat_session_data
+{
+ ggp_chat_local_info *chats;
+ int chats_count;
+
+ gboolean got_all_chats_info;
+ GSList *pending_joins;
+};
+
+struct _ggp_chat_local_info
+{
+ int local_id;
+ uint64_t id;
+
+ PurpleChatConversation *conv;
+ PurpleConnection *gc;
+
+ gboolean left;
+ gboolean previously_joined;
+
+ uin_t *participants;
+ int participants_count;
+};
+
+static ggp_chat_local_info * ggp_chat_new(PurpleConnection *gc, uint64_t id);
+static ggp_chat_local_info * ggp_chat_get(PurpleConnection *gc, uint64_t id);
+static void ggp_chat_open_conv(ggp_chat_local_info *chat);
+static ggp_chat_local_info * ggp_chat_get_local(PurpleConnection *gc,
+ int local_id);
+static void ggp_chat_joined(ggp_chat_local_info *chat, uin_t uin);
+static void ggp_chat_left(ggp_chat_local_info *chat, uin_t uin);
+static const gchar * ggp_chat_get_name_from_id(uint64_t id);
+static uint64_t ggp_chat_get_id_from_name(const gchar * name);
+static void ggp_chat_join_id(PurpleConnection *gc, uint64_t id);
+
+static inline ggp_chat_session_data *
+ggp_chat_get_sdata(PurpleConnection *gc)
+{
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+ return accdata->chat_data;
+}
+
+void ggp_chat_setup(PurpleConnection *gc)
+{
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+ ggp_chat_session_data *sdata = g_new0(ggp_chat_session_data, 1);
+
+ accdata->chat_data = sdata;
+}
+
+void ggp_chat_cleanup(PurpleConnection *gc)
+{
+ ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc);
+ int i;
+
+ g_slist_free_full(sdata->pending_joins, g_free);
+ for (i = 0; i < sdata->chats_count; i++)
+ g_free(sdata->chats[i].participants);
+ g_free(sdata->chats);
+ g_free(sdata);
+}
+
+static ggp_chat_local_info * ggp_chat_new(PurpleConnection *gc, uint64_t id)
+{
+ ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc);
+ int local_id;
+ ggp_chat_local_info *chat;
+
+ if (NULL != (chat = ggp_chat_get(gc, id)))
+ return chat;
+
+ local_id = sdata->chats_count++;
+ sdata->chats = g_realloc(sdata->chats,
+ sdata->chats_count * sizeof(ggp_chat_local_info));
+ chat = &sdata->chats[local_id];
+
+ chat->local_id = local_id;
+ chat->id = id;
+ chat->conv = NULL;
+ chat->gc = gc;
+ chat->left = FALSE;
+ chat->previously_joined = FALSE;
+ chat->participants = NULL;
+ chat->participants_count = 0;
+
+ return chat;
+}
+
+static ggp_chat_local_info * ggp_chat_get(PurpleConnection *gc, uint64_t id)
+{
+ ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc);
+ int i;
+
+ for (i = 0; i < sdata->chats_count; i++) {
+ if (sdata->chats[i].id == id)
+ return &sdata->chats[i];
+ }
+
+ return NULL;
+}
+
+static void ggp_chat_open_conv(ggp_chat_local_info *chat)
+{
+ int i;
+
+ if (chat->conv != NULL)
+ return;
+
+ chat->conv = purple_serv_got_joined_chat(chat->gc, chat->local_id,
+ ggp_chat_get_name_from_id(chat->id));
+ if (chat->previously_joined) {
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(chat->conv),
+ _("You have re-joined the chat"), 0);
+ }
+ chat->previously_joined = TRUE;
+
+ purple_chat_conversation_clear_users(chat->conv);
+ for (i = 0; i < chat->participants_count; i++) {
+ purple_chat_conversation_add_user(chat->conv,
+ ggp_uin_to_str(chat->participants[i]), NULL,
+ PURPLE_CHAT_USER_NONE, FALSE);
+ }
+}
+
+static ggp_chat_local_info * ggp_chat_get_local(PurpleConnection *gc,
+ int local_id)
+{
+ ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc);
+ int i;
+
+ for (i = 0; i < sdata->chats_count; i++) {
+ if (sdata->chats[i].local_id == local_id)
+ return &sdata->chats[i];
+ }
+
+ return NULL;
+}
+
+void ggp_chat_got_event(PurpleConnection *gc, const struct gg_event *ev)
+{
+ ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc);
+ ggp_chat_local_info *chat;
+ uint32_t i;
+
+ if (ev->type == GG_EVENT_CHAT_INFO) {
+ const struct gg_event_chat_info *eci = &ev->event.chat_info;
+ chat = ggp_chat_new(gc, eci->id);
+ for (i = 0; i < eci->participants_count; i++)
+ ggp_chat_joined(chat, eci->participants[i]);
+ } else if (ev->type == GG_EVENT_CHAT_INFO_GOT_ALL) {
+ GSList *it = sdata->pending_joins;
+ sdata->got_all_chats_info = TRUE;
+ while (it) {
+ uint64_t *id_p = it->data;
+ ggp_chat_join_id(gc, *id_p);
+ it = g_slist_next(it);
+ }
+ g_slist_free_full(sdata->pending_joins, g_free);
+ sdata->pending_joins = NULL;
+ } else if (ev->type == GG_EVENT_CHAT_INFO_UPDATE) {
+ const struct gg_event_chat_info_update *eciu =
+ &ev->event.chat_info_update;
+ chat = ggp_chat_get(gc, eciu->id);
+ if (!chat) {
+ purple_debug_error("gg", "ggp_chat_got_event: "
+ "chat %" G_GUINT64_FORMAT " not found\n",
+ eciu->id);
+ return;
+ }
+ if (eciu->type == GG_CHAT_INFO_UPDATE_ENTERED)
+ ggp_chat_joined(chat, eciu->participant);
+ else if (eciu->type == GG_CHAT_INFO_UPDATE_EXITED)
+ ggp_chat_left(chat, eciu->participant);
+ else
+ purple_debug_warning("gg", "ggp_chat_got_event: "
+ "unknown update type - %d", eciu->type);
+ } else if (ev->type == GG_EVENT_CHAT_CREATED) {
+ const struct gg_event_chat_created *ecc =
+ &ev->event.chat_created;
+ uin_t me = ggp_str_to_uin(purple_account_get_username(
+ purple_connection_get_account(gc)));
+ chat = ggp_chat_new(gc, ecc->id);
+ ggp_chat_joined(chat, me);
+ ggp_chat_open_conv(chat);
+ } else if (ev->type == GG_EVENT_CHAT_INVITE_ACK) {
+ /* ignore */
+ } else {
+ purple_debug_fatal("gg", "ggp_chat_got_event: unexpected event "
+ "- %d\n", ev->type);
+ }
+}
+
+static int ggp_chat_participant_find(ggp_chat_local_info *chat, uin_t uin)
+{
+ int i;
+ for (i = 0; i < chat->participants_count; i++)
+ if (chat->participants[i] == uin)
+ return i;
+ return -1;
+}
+
+static void ggp_chat_joined(ggp_chat_local_info *chat, uin_t uin)
+{
+ int idx = ggp_chat_participant_find(chat, uin);
+ if (idx >= 0) {
+ purple_debug_warning("gg", "ggp_chat_joined: "
+ "user %u is already present in chat %" G_GUINT64_FORMAT
+ "\n", uin, chat->id);
+ return;
+ }
+ chat->participants_count++;
+ chat->participants = g_realloc(chat->participants,
+ sizeof(uin) * chat->participants_count);
+ chat->participants[chat->participants_count - 1] = uin;
+
+ if (!chat->conv)
+ return;
+ purple_chat_conversation_add_user(chat->conv,
+ ggp_uin_to_str(uin), NULL, PURPLE_CHAT_USER_NONE, TRUE);
+}
+
+static void ggp_chat_left(ggp_chat_local_info *chat, uin_t uin)
+{
+ uin_t me;
+ int idx = ggp_chat_participant_find(chat, uin);
+
+ if (idx < 0) {
+ purple_debug_warning("gg", "ggp_chat_joined: "
+ "user %u isn't present in chat %" G_GUINT64_FORMAT "\n",
+ uin, chat->id);
+ return;
+ }
+ chat->participants[idx] =
+ chat->participants[chat->participants_count - 1];
+ chat->participants_count--;
+ chat->participants = g_realloc(chat->participants,
+ sizeof(uin) * chat->participants_count);
+
+ if (chat->conv == NULL)
+ return;
+
+ me = ggp_str_to_uin(purple_account_get_username(
+ purple_connection_get_account(chat->gc)));
+
+ if (me == uin) {
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(chat->conv),
+ _("You have left the chat"), 0);
+ purple_serv_got_chat_left(chat->gc, chat->local_id);
+ chat->conv = NULL;
+ chat->left = TRUE;
+ }
+ purple_chat_conversation_remove_user(chat->conv, ggp_uin_to_str(uin), NULL);
+}
+
+GList * ggp_chat_info(PurpleConnection *gc)
+{
+ GList *m = NULL;
+ struct proto_chat_entry *pce;
+
+ pce = g_new0(struct proto_chat_entry, 1);
+ pce->label = _("_Conference identifier:");
+ pce->identifier = "id";
+ pce->required = FALSE;
+ m = g_list_append(m, pce);
+
+ return m;
+}
+
+GHashTable * ggp_chat_info_defaults(PurpleConnection *gc, const char *chat_name)
+{
+ GHashTable *defaults;
+
+ defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
+
+ if (chat_name != NULL && ggp_chat_get_id_from_name(chat_name) != 0)
+ g_hash_table_insert(defaults, "id", g_strdup(chat_name));
+
+ return defaults;
+}
+
+char * ggp_chat_get_name(GHashTable *components)
+{
+ return g_strdup((gchar*)g_hash_table_lookup(components, "id"));
+}
+
+static const gchar * ggp_chat_get_name_from_id(uint64_t id)
+{
+ static gchar buff[30];
+ g_snprintf(buff, sizeof(buff), "%" G_GUINT64_FORMAT, id);
+ return buff;
+}
+
+static uint64_t ggp_chat_get_id_from_name(const gchar * name)
+{
+ uint64_t id;
+ gchar *endptr;
+
+ if (name == NULL)
+ return 0;
+
+ id = g_ascii_strtoull(name, &endptr, 10);
+
+ if (*endptr != '\0' || id == G_MAXUINT64)
+ return 0;
+
+ return id;
+}
+
+void ggp_chat_join(PurpleConnection *gc, GHashTable *components)
+{
+ ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc);
+ GGPInfo *info = purple_connection_get_protocol_data(gc);
+ const gchar *id_cs;
+ gchar *id_s;
+ uint64_t id;
+
+ id_cs = g_hash_table_lookup(components, "id");
+ id_s = g_strdup(id_cs);
+ if (id_s)
+ g_strstrip(id_s);
+ if (id_s == NULL || id_s[0] == '\0') {
+ g_free(id_s);
+ if (gg_chat_create(info->session) < 0) {
+ purple_debug_error("gg", "ggp_chat_join; "
+ "cannot create\n");
+ purple_serv_got_join_chat_failed(gc, components);
+ }
+ return;
+ }
+ id = ggp_chat_get_id_from_name(id_s);
+ g_free(id_s);
+
+ if (!id) {
+ char *buff = g_strdup_printf(
+ _("%s is not a valid room identifier"), id_cs);
+ purple_notify_error(gc, _("Invalid Room Identifier"),
+ _("Invalid Room Identifier"), buff, NULL);
+ g_free(buff);
+ purple_serv_got_join_chat_failed(gc, components);
+ return;
+ }
+
+ if (sdata->got_all_chats_info)
+ ggp_chat_join_id(gc, id);
+ else {
+ uint64_t *id_p = g_new(uint64_t, 1);
+ *id_p = id;
+ sdata->pending_joins = g_slist_append(sdata->pending_joins, id_p);
+ }
+
+}
+
+static void ggp_chat_join_id(PurpleConnection *gc, uint64_t id)
+{
+ GHashTable *components;
+ ggp_chat_local_info *chat = ggp_chat_get(gc, id);
+
+ if (chat && !chat->left) {
+ ggp_chat_open_conv(chat);
+ return;
+ }
+
+ if (!chat) {
+ char *id_s = g_strdup_printf("%" G_GUINT64_FORMAT, id);
+ char *buff = g_strdup_printf(
+ _("%s is not a valid room identifier"), id_s);
+ g_free(id_s);
+ purple_notify_error(gc, _("Invalid Room Identifier"),
+ _("Invalid Room Identifier"), buff, NULL);
+ g_free(buff);
+ } else { /* if (chat->left) */
+ purple_notify_error(gc, _("Could not join chat room"),
+ _("Could not join chat room"),
+ _("You have to ask for invitation from another chat "
+ "participant"), NULL);
+ }
+
+ components = ggp_chat_info_defaults(gc, ggp_chat_get_name_from_id(id));
+ purple_serv_got_join_chat_failed(gc, components);
+ g_hash_table_destroy(components);
+}
+
+void ggp_chat_leave(PurpleConnection *gc, int local_id)
+{
+ GGPInfo *info = purple_connection_get_protocol_data(gc);
+ ggp_chat_local_info *chat;
+ uin_t me;
+
+ chat = ggp_chat_get_local(gc, local_id);
+ if (!chat) {
+ purple_debug_error("gg", "ggp_chat_leave: "
+ "chat %u doesn't exists\n", local_id);
+ return;
+ }
+
+ if (gg_chat_leave(info->session, chat->id) < 0) {
+ purple_debug_error("gg", "ggp_chat_leave: "
+ "unable to leave chat %" G_GUINT64_FORMAT "\n",
+ chat->id);
+ }
+ chat->conv = NULL;
+
+ me = ggp_str_to_uin(purple_account_get_username(
+ purple_connection_get_account(chat->gc)));
+
+ ggp_chat_left(chat, me);
+ chat->left = TRUE;
+}
+
+void ggp_chat_invite(PurpleConnection *gc, int local_id, const char *message,
+ const char *who)
+{
+ GGPInfo *info = purple_connection_get_protocol_data(gc);
+ ggp_chat_local_info *chat;
+ uin_t invited;
+
+ chat = ggp_chat_get_local(gc, local_id);
+ if (!chat) {
+ purple_debug_error("gg", "ggp_chat_invite: "
+ "chat %u doesn't exists\n", local_id);
+ return;
+ }
+
+ invited = ggp_str_to_uin(who);
+ if (gg_chat_invite(info->session, chat->id, &invited, 1) < 0) {
+ purple_debug_error("gg", "ggp_chat_invite: "
+ "unable to invite %s to chat %" G_GUINT64_FORMAT "\n",
+ who, chat->id);
+ }
+}
+
+int ggp_chat_send(PurpleConnection *gc, int local_id, PurpleMessage *msg)
+{
+ GGPInfo *info = purple_connection_get_protocol_data(gc);
+ PurpleChatConversation *conv;
+ ggp_chat_local_info *chat;
+ gboolean succ = TRUE;
+ const gchar *me;
+ gchar *gg_msg;
+
+ chat = ggp_chat_get_local(gc, local_id);
+ if (!chat) {
+ purple_debug_error("gg", "ggp_chat_send: "
+ "chat %u doesn't exists\n", local_id);
+ return -1;
+ }
+
+ conv = purple_conversations_find_chat_with_account(
+ ggp_chat_get_name_from_id(chat->id),
+ purple_connection_get_account(gc));
+
+ gg_msg = ggp_message_format_to_gg(PURPLE_CONVERSATION(conv),
+ purple_message_get_contents(msg));
+
+ if (gg_chat_send_message(info->session, chat->id, gg_msg, TRUE) < 0)
+ succ = FALSE;
+ g_free(gg_msg);
+
+ me = purple_account_get_username(purple_connection_get_account(gc));
+ purple_serv_got_chat_in(gc, chat->local_id, me,
+ purple_message_get_flags(msg),
+ purple_message_get_contents(msg),
+ purple_message_get_time(msg));
+
+ return succ ? 0 : -1;
+}
+
+void ggp_chat_got_message(PurpleConnection *gc, uint64_t chat_id,
+ const char *message, time_t time, uin_t who)
+{
+ ggp_chat_local_info *chat;
+ uin_t me;
+
+ me = ggp_str_to_uin(purple_account_get_username(
+ purple_connection_get_account(gc)));
+
+ chat = ggp_chat_get(gc, chat_id);
+ if (!chat) {
+ purple_debug_error("gg", "ggp_chat_got_message: "
+ "chat %" G_GUINT64_FORMAT " doesn't exists\n", chat_id);
+ return;
+ }
+
+ ggp_chat_open_conv(chat);
+ if (who == me) {
+ PurpleMessage *pmsg;
+
+ pmsg = purple_message_new_outgoing(
+ ggp_uin_to_str(who), message, 0);
+ purple_message_set_time(pmsg, time);
+
+ purple_conversation_write_message(
+ PURPLE_CONVERSATION(chat->conv), pmsg);
+ } else {
+ purple_serv_got_chat_in(gc, chat->local_id, ggp_uin_to_str(who),
+ PURPLE_MESSAGE_RECV, message, time);
+ }
+}
+
+static gboolean ggp_chat_roomlist_get_list_finish(gpointer roomlist)
+{
+ purple_roomlist_set_in_progress(PURPLE_ROOMLIST(roomlist), FALSE);
+ g_object_unref(roomlist);
+
+ return FALSE;
+}
+
+PurpleRoomlist * ggp_chat_roomlist_get_list(PurpleConnection *gc)
+{
+ ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc);
+ PurpleRoomlist *roomlist;
+ GList *fields = NULL;
+ int i;
+
+ purple_debug_info("gg", "ggp_chat_roomlist_get_list\n");
+
+ roomlist = purple_roomlist_new(purple_connection_get_account(gc));
+
+ fields = g_list_append(fields, purple_roomlist_field_new(
+ PURPLE_ROOMLIST_FIELD_STRING, _("Conference identifier"), "id",
+ TRUE));
+
+ fields = g_list_append(fields, purple_roomlist_field_new(
+ PURPLE_ROOMLIST_FIELD_STRING, _("Start Date"), "date",
+ FALSE));
+
+ fields = g_list_append(fields, purple_roomlist_field_new(
+ PURPLE_ROOMLIST_FIELD_INT, _("User Count"), "users",
+ FALSE));
+
+ fields = g_list_append(fields, purple_roomlist_field_new(
+ PURPLE_ROOMLIST_FIELD_STRING, _("Status"), "status",
+ FALSE));
+
+ purple_roomlist_set_fields(roomlist, fields);
+
+ for (i = sdata->chats_count - 1; i >= 0 ; i--) {
+ PurpleRoomlistRoom *room;
+ ggp_chat_local_info *chat = &sdata->chats[i];
+ const gchar *name;
+ time_t date;
+ const gchar *status;
+ int count = chat->participants_count;
+
+ date = (uint32_t)(chat->id >> 32);
+
+ if (chat->conv)
+ status = _("Joined");
+ else if (chat->left)
+ status = _("Chat left");
+ else {
+ status = _("Can join chat");
+ count--;
+ }
+
+ name = ggp_chat_get_name_from_id(chat->id);
+ room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM,
+ name, NULL);
+ purple_roomlist_room_add_field(roomlist, room, name);
+ purple_roomlist_room_add_field(roomlist, room, purple_date_format_full(localtime(&date)));
+ purple_roomlist_room_add_field(roomlist, room, GINT_TO_POINTER(count));
+ purple_roomlist_room_add_field(roomlist, room, status);
+ purple_roomlist_room_add(roomlist, room);
+ }
+
+ /* TODO
+ * purple_roomlist_set_in_progress(roomlist, FALSE);
+ */
+ g_object_ref(roomlist);
+ purple_timeout_add(1, ggp_chat_roomlist_get_list_finish, roomlist);
+ return roomlist;
+}
+
+#else
+void ggp_chat_setup(PurpleConnection *gc)
+{
+}
+
+void ggp_chat_cleanup(PurpleConnection *gc)
+{
+}
+#endif
diff --git a/libpurple/protocols/gg/chat.h b/libpurple/protocols/gg/chat.h
new file mode 100644
index 0000000000..10eb0bbda9
--- /dev/null
+++ b/libpurple/protocols/gg/chat.h
@@ -0,0 +1,60 @@
+/* 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.
+ *
+ * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
+ * As a recipient of this file you may choose, which license to receive the
+ * code under. As a contributor, you have to ensure the new code is
+ * compatible with both.
+ *
+ * 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 _GGP_CHAT_H
+#define _GGP_CHAT_H
+
+#include <internal.h>
+#include <libgadu.h>
+
+typedef struct _ggp_chat_session_data ggp_chat_session_data;
+
+#include "gg.h"
+
+void ggp_chat_setup(PurpleConnection *gc);
+void ggp_chat_cleanup(PurpleConnection *gc);
+
+#if GGP_ENABLE_GG11
+void ggp_chat_got_event(PurpleConnection *gc, const struct gg_event *ev);
+
+GList * ggp_chat_info(PurpleConnection *gc);
+GHashTable * ggp_chat_info_defaults(PurpleConnection *gc,
+ const char *chat_name);
+char * ggp_chat_get_name(GHashTable *components);
+void ggp_chat_join(PurpleConnection *gc, GHashTable *components);
+void ggp_chat_leave(PurpleConnection *gc, int local_id);
+void ggp_chat_invite(PurpleConnection *gc, int local_id, const char *message,
+ const char *who);
+int ggp_chat_send(PurpleConnection *gc, int local_id, PurpleMessage *msg);
+
+void ggp_chat_got_message(PurpleConnection *gc, uint64_t chat_id,
+ const char *message, time_t time, uin_t who);
+
+PurpleRoomlist * ggp_chat_roomlist_get_list(PurpleConnection *gc);
+#endif
+
+#endif /* _GGP_CHAT_H */
diff --git a/libpurple/protocols/gg/confer.c b/libpurple/protocols/gg/confer.c
deleted file mode 100644
index dad03d6518..0000000000
--- a/libpurple/protocols/gg/confer.c
+++ /dev/null
@@ -1,170 +0,0 @@
-/**
- * @file confer.c
- *
- * purple
- *
- * Copyright (C) 2005 Bartosz Oler <bartosz@bzimage.us>
- *
- * 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 <libgadu.h>
-#include "gg.h"
-#include "gg-utils.h"
-#include "confer.h"
-
-/* PurpleConversation *ggp_confer_find_by_name(PurpleConnection *gc, const gchar *name) {{{ */
-PurpleConversation *ggp_confer_find_by_name(PurpleConnection *gc, const gchar *name)
-{
- g_return_val_if_fail(gc != NULL, NULL);
- g_return_val_if_fail(name != NULL, NULL);
-
- return purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, name,
- purple_connection_get_account(gc));
-}
-/* }}} */
-
-/* void ggp_confer_participants_add_uin(PurpleConnection *gc, const gchar *chat_name, const uin_t uin) {{{ */
-void ggp_confer_participants_add_uin(PurpleConnection *gc, const gchar *chat_name,
- const uin_t uin)
-{
- PurpleConversation *conv;
- GGPInfo *info = gc->proto_data;
- GGPChat *chat;
- GList *l;
- gchar *str_uin;
-
- for (l = info->chats; l != NULL; l = l->next) {
- chat = l->data;
-
- if (g_utf8_collate(chat->name, chat_name) != 0)
- continue;
-
- if (g_list_find(chat->participants, GINT_TO_POINTER(uin)) == NULL) {
- chat->participants = g_list_append(
- chat->participants, GINT_TO_POINTER(uin));
-
- str_uin = g_strdup_printf("%lu", (unsigned long int)uin);
- conv = ggp_confer_find_by_name(gc, chat_name);
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(conv), str_uin, NULL,
- PURPLE_CBFLAGS_NONE, TRUE);
-
- g_free(str_uin);
- }
- break;
- }
-}
-/* }}} */
-
-/* void ggp_confer_participants_add(PurpleConnection *gc, const gchar *chat_name, const uin_t *recipients, int count) {{{ */
-void ggp_confer_participants_add(PurpleConnection *gc, const gchar *chat_name,
- const uin_t *recipients, int count)
-{
- GGPInfo *info = gc->proto_data;
- GList *l;
- gchar *str_uin;
-
- for (l = info->chats; l != NULL; l = l->next) {
- GGPChat *chat = l->data;
- int i;
-
- if (g_utf8_collate(chat->name, chat_name) != 0)
- continue;
-
- for (i = 0; i < count; i++) {
- PurpleConversation *conv;
-
- if (g_list_find(chat->participants,
- GINT_TO_POINTER(recipients[i])) != NULL) {
- continue;
- }
-
- chat->participants = g_list_append(chat->participants,
- GINT_TO_POINTER(recipients[i]));
-
- str_uin = g_strdup_printf("%lu", (unsigned long int)recipients[i]);
- conv = ggp_confer_find_by_name(gc, chat_name);
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(conv),
- str_uin, NULL,
- PURPLE_CBFLAGS_NONE, TRUE);
- g_free(str_uin);
- }
- break;
- }
-}
-/* }}} */
-
-/* const char *ggp_confer_find_by_participants(PurpleConnection *gc, const uin_t *recipients, int count) {{{ */
-const char *ggp_confer_find_by_participants(PurpleConnection *gc,
- const uin_t *recipients, int count)
-{
- GGPInfo *info = gc->proto_data;
- GGPChat *chat = NULL;
- GList *l;
- int matches;
-
- g_return_val_if_fail(info->chats != NULL, NULL);
-
- for (l = info->chats; l != NULL; l = l->next) {
- GList *m;
-
- chat = l->data;
- matches = 0;
-
- for (m = chat->participants; m != NULL; m = m->next) {
- uin_t uin = GPOINTER_TO_INT(m->data);
- int i;
-
- for (i = 0; i < count; i++)
- if (uin == recipients[i])
- matches++;
- }
-
- if (matches == count)
- break;
-
- chat = NULL;
- }
-
- if (chat == NULL)
- return NULL;
- else
- return chat->name;
-}
-/* }}} */
-
-/* const char *ggp_confer_add_new(PurpleConnection *gc, const char *name) {{{ */
-const char *ggp_confer_add_new(PurpleConnection *gc, const char *name)
-{
- GGPInfo *info = gc->proto_data;
- GGPChat *chat;
-
- chat = g_new0(GGPChat, 1);
-
- if (name == NULL)
- chat->name = g_strdup_printf("conf#%d", info->chats_count++);
- else
- chat->name = g_strdup(name);
-
- chat->participants = NULL;
-
- info->chats = g_list_append(info->chats, chat);
-
- return chat->name;
-}
-/* }}} */
-
-/* vim: set ts=8 sts=0 sw=8 noet: */
diff --git a/libpurple/protocols/gg/confer.h b/libpurple/protocols/gg/confer.h
deleted file mode 100644
index 37e93df128..0000000000
--- a/libpurple/protocols/gg/confer.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- * @file confer.h
- *
- * purple
- *
- * Copyright (C) 2005 Bartosz Oler <bartosz@bzimage.us>
- *
- * 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 _PURPLE_GG_CONFER_H
-#define _PURPLE_GG_CONFER_H
-
-#include "gg.h"
-
-/**
- * Finds a CHAT conversation for the current account with the specified name.
- *
- * @param gc PurpleConnection instance.
- * @param name Name of the conversation.
- *
- * @return PurpleConversation or NULL if not found.
- */
-PurpleConversation *
-ggp_confer_find_by_name(PurpleConnection *gc, const gchar *name);
-
-/**
- * Adds the specified UIN to the specified conversation.
- *
- * @param gc PurpleConnection.
- * @param chat_name Name of the conversation.
- */
-void
-ggp_confer_participants_add_uin(PurpleConnection *gc, const gchar *chat_name,
- const uin_t uin);
-
-/**
- * Add the specified UINs to the specified conversation.
- *
- * @param gc PurpleConnection.
- * @param chat_name Name of the conversation.
- * @param recipients List of the UINs.
- * @param count Number of the UINs.
- */
-void
-ggp_confer_participants_add(PurpleConnection *gc, const gchar *chat_name,
- const uin_t *recipients, int count);
-
-/**
- * Finds a conversation in which all the specified recipients participate.
- *
- * TODO: This function should be rewritten to better handle situations when
- * somebody adds more people to the converation.
- *
- * @param gc PurpleConnection.
- * @param recipients List of the people in the conversation.
- * @param count Number of people.
- *
- * @return Name of the conversation.
- */
-const char*
-ggp_confer_find_by_participants(PurpleConnection *gc, const uin_t *recipients,
- int count);
-
-/**
- * Adds a new conversation to the internal list of conversations.
- * If name is NULL then it will be automagically generated.
- *
- * @param gc PurpleConnection.
- * @param name Name of the conversation.
- *
- * @return Name of the conversation.
- */
-const char*
-ggp_confer_add_new(PurpleConnection *gc, const char *name);
-
-
-#endif /* _PURPLE_GG_CONFER_H */
-
-/* vim: set ts=8 sts=0 sw=8 noet: */
diff --git a/libpurple/protocols/gg/deprecated.c b/libpurple/protocols/gg/deprecated.c
new file mode 100644
index 0000000000..681737cd6c
--- /dev/null
+++ b/libpurple/protocols/gg/deprecated.c
@@ -0,0 +1,67 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 "deprecated.h"
+
+#include <libgadu.h>
+
+#include "gg.h"
+
+gboolean ggp_deprecated_setup_proxy(PurpleConnection *gc)
+{
+#if ! GGP_ENABLE_GG11
+ PurpleProxyInfo *gpi = purple_proxy_get_setup(purple_connection_get_account(gc));
+
+ if ((purple_proxy_info_get_proxy_type(gpi) != PURPLE_PROXY_NONE) &&
+ (purple_proxy_info_get_host(gpi) == NULL ||
+ purple_proxy_info_get_port(gpi) <= 0))
+ {
+ gg_proxy_enabled = 0;
+ purple_notify_error(NULL, NULL, _("Invalid proxy settings"),
+ _("Either the host name or port number specified for "
+ "your given proxy type is invalid."),
+ purple_request_cpar_from_connection(gc));
+ return FALSE;
+ }
+
+ if (purple_proxy_info_get_proxy_type(gpi) == PURPLE_PROXY_NONE) {
+ gg_proxy_enabled = 0;
+ return TRUE;
+ }
+
+ gg_proxy_enabled = 1;
+ /* TODO: memleak */
+ gg_proxy_host = g_strdup(purple_proxy_info_get_host(gpi));
+ gg_proxy_port = purple_proxy_info_get_port(gpi);
+ gg_proxy_username = g_strdup(purple_proxy_info_get_username(gpi));
+ gg_proxy_password = g_strdup(purple_proxy_info_get_password(gpi));
+#endif
+
+ return TRUE;
+}
diff --git a/libpurple/protocols/mxit/cipher.h b/libpurple/protocols/gg/deprecated.h
index 1c1c4c0070..bcc4ae706c 100644
--- a/libpurple/protocols/mxit/cipher.h
+++ b/libpurple/protocols/gg/deprecated.h
@@ -1,12 +1,16 @@
-/*
- * MXit Protocol libPurple Plugin
+/* purple
*
- * -- encryption --
+ * 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.
*
- * Pieter Loubser <libpurple@mxit.com>
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
*
- * (C) Copyright 2009 MXit Lifestyle (Pty) Ltd.
- * <http://www.mxitlifestyle.com>
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
*
* 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
@@ -23,16 +27,11 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#ifndef _MXIT_CIPHER_H_
-#define _MXIT_CIPHER_H_
+#ifndef _GGP_DEPRECATED_H
+#define _GGP_DEPRECATED_H
+#include <internal.h>
-struct MXitSession;
+gboolean ggp_deprecated_setup_proxy(PurpleConnection *gc);
-
-char* mxit_encrypt_password( struct MXitSession* session );
-
-char* mxit_decrypt_message( struct MXitSession* session, char* message );
-char* mxit_encrypt_message( struct MXitSession* session, char* message );
-
-#endif /* _MXIT_CIPHER_H_ */
+#endif /* _GGP_DEPRECATED_H */
diff --git a/libpurple/protocols/gg/edisc.c b/libpurple/protocols/gg/edisc.c
new file mode 100644
index 0000000000..3b0bea7022
--- /dev/null
+++ b/libpurple/protocols/gg/edisc.c
@@ -0,0 +1,1225 @@
+/* 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.
+ *
+ * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
+ * As a recipient of this file you may choose, which license to receive the
+ * code under. As a contributor, you have to ensure the new code is
+ * compatible with both.
+ *
+ * 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 "edisc.h"
+
+#include <debug.h>
+#include <glibcompat.h>
+
+#include "gg.h"
+#include "libgaduw.h"
+#include "utils.h"
+#include <http.h>
+
+#include <json-glib/json-glib.h>
+
+#define GGP_EDISC_OS "WINNT x86-msvc"
+#define GGP_EDISC_TYPE "desktop"
+#define GGP_EDISC_API "6"
+
+#define GGP_EDISC_RESPONSE_MAX 10240
+#define GGP_EDISC_FNAME_ALLOWED "1234567890" \
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+ " [](){}-+=_;'<>,.&$!"
+
+typedef struct _ggp_edisc_auth_data ggp_edisc_auth_data;
+
+typedef struct _ggp_edisc_xfer ggp_edisc_xfer;
+
+struct _ggp_edisc_session_data
+{
+ GHashTable *xfers_initialized;
+ GHashTable *xfers_history;
+
+ PurpleHttpCookieJar *cookies;
+ gchar *security_token;
+
+ PurpleHttpConnection *auth_request;
+ gboolean auth_done;
+ GList *auth_pending;
+};
+
+struct _ggp_edisc_xfer
+{
+ gchar *filename;
+ gchar *ticket_id;
+
+ gboolean allowed, ready;
+
+ PurpleConnection *gc;
+ PurpleHttpConnection *hc;
+
+ gsize already_read;
+};
+
+typedef enum
+{
+ GGP_EDISC_XFER_ACK_STATUS_UNKNOWN,
+ GGP_EDISC_XFER_ACK_STATUS_ALLOWED,
+ GGP_EDISC_XFER_ACK_STATUS_REJECTED
+} ggp_edisc_xfer_ack_status;
+
+typedef void (*ggp_ggdrive_auth_cb)(PurpleConnection *gc, gboolean success,
+ gpointer user_data);
+
+/* Setting up. */
+static inline ggp_edisc_session_data *
+ggp_edisc_get_sdata(PurpleConnection *gc);
+
+/* Misc. */
+static void ggp_edisc_set_defaults(PurpleHttpRequest *req);
+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);
+static ggp_edisc_xfer_ack_status
+ggp_edisc_xfer_parse_ack_status(const gchar *str);
+
+
+/* Sending a file. */
+static void ggp_edisc_xfer_send_ticket_changed(PurpleConnection *gc,
+ PurpleXfer *xfer, gboolean is_allowed);
+
+/* Receiving a file. */
+static void ggp_edisc_xfer_recv_reject(PurpleXfer *xfer);
+static void ggp_edisc_xfer_recv_ticket_got(PurpleConnection *gc,
+ const gchar *ticket_id);
+static void ggp_edisc_xfer_recv_ticket_completed(PurpleXfer *xfer);
+
+/* Authentication. */
+static void ggp_ggdrive_auth(PurpleConnection *gc, ggp_ggdrive_auth_cb cb,
+ gpointer user_data);
+
+/*******************************************************************************
+ * Setting up.
+ ******************************************************************************/
+
+static inline ggp_edisc_session_data *
+ggp_edisc_get_sdata(PurpleConnection *gc)
+{
+ GGPInfo *accdata;
+
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
+
+ accdata = purple_connection_get_protocol_data(gc);
+ g_return_val_if_fail(accdata != NULL, NULL);
+
+ return accdata->edisc_data;
+}
+
+void ggp_edisc_setup(PurpleConnection *gc)
+{
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+ ggp_edisc_session_data *sdata = g_new0(ggp_edisc_session_data, 1);
+
+ accdata->edisc_data = sdata;
+
+ sdata->cookies = purple_http_cookie_jar_new();
+ sdata->xfers_initialized = g_hash_table_new(g_str_hash, g_str_equal);
+ sdata->xfers_history = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+}
+
+void ggp_edisc_cleanup(PurpleConnection *gc)
+{
+ ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc);
+
+ g_return_if_fail(sdata != NULL);
+
+ purple_http_conn_cancel(sdata->auth_request);
+ g_list_free_full(sdata->auth_pending, g_free);
+ g_free(sdata->security_token);
+
+ purple_http_cookie_jar_unref(sdata->cookies);
+ g_hash_table_destroy(sdata->xfers_initialized);
+ g_hash_table_destroy(sdata->xfers_history);
+
+ g_free(sdata);
+}
+
+/*******************************************************************************
+ * Misc.
+ ******************************************************************************/
+
+static void ggp_edisc_set_defaults(PurpleHttpRequest *req)
+{
+ purple_http_request_set_max_len(req, GGP_EDISC_RESPONSE_MAX);
+ purple_http_request_header_set(req, "X-gged-api-version",
+ GGP_EDISC_API);
+
+ /* optional fields */
+ purple_http_request_header_set(req, "User-Agent", "Mozilla/5.0 (Windows"
+ " NT 6.1; rv:11.0) Gecko/20120613 GG/11.0.0.8169 (WINNT_x86-msv"
+ "c; pl; beta; standard)");
+ purple_http_request_header_set(req, "Accept", "text/html,application/xh"
+ "tml+xml,application/xml;q=0.9,*/*;q=0.8");
+ purple_http_request_header_set(req, "Accept-Language",
+ "pl,en-us;q=0.7,en;q=0.3");
+ /* purple_http_request_header_set(req, "Accept-Encoding",
+ * "gzip, deflate"); */
+ purple_http_request_header_set(req, "Accept-Charset",
+ "ISO-8859-2,utf-8;q=0.7,*;q=0.7");
+ purple_http_request_header_set(req, "Connection", "keep-alive");
+ purple_http_request_header_set(req, "Content-Type",
+ "application/x-www-form-urlencoded; charset=UTF-8");
+}
+
+static int ggp_edisc_parse_error(const gchar *data)
+{
+ JsonParser *parser;
+ JsonObject *result;
+ int error_id;
+
+ parser = ggp_json_parse(data);
+ result = json_node_get_object(json_parser_get_root(parser));
+ result = json_object_get_object_member(result, "result");
+ error_id = json_object_get_int_member(result, "appStatus");
+ purple_debug_info("gg", "edisc error: %s (%d)\n",
+ json_object_get_string_member(result, "errorMsg"),
+ error_id);
+ g_object_unref(parser);
+
+ return error_id;
+}
+
+/*******************************************************************************
+ * 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))
+ g_return_if_reached();
+ purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE);
+ purple_xfer_conversation_write(xfer, msg, TRUE);
+ purple_xfer_error(
+ purple_xfer_get_xfer_type(xfer),
+ purple_xfer_get_account(xfer),
+ purple_xfer_get_remote_user(xfer),
+ msg);
+ ggp_edisc_xfer_free(xfer);
+ purple_xfer_end(xfer);
+}
+
+void ggp_edisc_xfer_ticket_changed(PurpleConnection *gc, const char *data)
+{
+ ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc);
+ PurpleXfer *xfer;
+ JsonParser *parser;
+ JsonObject *ticket;
+ const gchar *ticket_id, *send_status;
+ ggp_edisc_xfer_ack_status ack_status;
+ gboolean is_completed;
+
+ g_return_if_fail(sdata != NULL);
+
+ parser = ggp_json_parse(data);
+ ticket = json_node_get_object(json_parser_get_root(parser));
+ ticket_id = json_object_get_string_member(ticket, "id");
+ ack_status = ggp_edisc_xfer_parse_ack_status(
+ json_object_get_string_member(ticket, "ack_status"));
+ send_status = json_object_get_string_member(ticket, "send_status");
+
+ if (ticket_id == NULL)
+ ticket_id = "";
+ xfer = g_hash_table_lookup(sdata->xfers_initialized, ticket_id);
+ if (xfer == NULL) {
+ purple_debug_misc("gg", "ggp_edisc_event_ticket_changed: "
+ "ticket %s not found, updating it...\n",
+ purple_debug_is_unsafe() ? ticket_id : "");
+ ggp_edisc_xfer_recv_ticket_got(gc, ticket_id);
+ g_object_unref(parser);
+ return;
+ }
+
+ is_completed = FALSE;
+ if (g_strcmp0("in_progress", send_status) == 0) {
+ /* do nothing */
+ } else if (g_strcmp0("completed", send_status) == 0) {
+ is_completed = TRUE;
+ } else if (g_strcmp0("expired", send_status) == 0)
+ ggp_edisc_xfer_error(xfer, _("File transfer expired."));
+ else {
+ purple_debug_warning("gg", "ggp_edisc_event_ticket_changed: "
+ "unknown send_status=%s\n", send_status);
+ g_object_unref(parser);
+ return;
+ }
+
+ g_object_unref(parser);
+
+ if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE) {
+ if (is_completed)
+ ggp_edisc_xfer_recv_ticket_completed(xfer);
+ } else {
+ if (ack_status != GGP_EDISC_XFER_ACK_STATUS_UNKNOWN)
+ ggp_edisc_xfer_send_ticket_changed(gc, xfer, ack_status
+ == GGP_EDISC_XFER_ACK_STATUS_ALLOWED);
+ }
+
+}
+
+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];
+
+ g_snprintf(ticket_url, sizeof(ticket_url),
+ "https://drive.mpa.gg.pl/send_ticket/%s", ticket_id);
+
+ return ticket_url;
+}
+
+static void ggp_edisc_xfer_progress_watcher(PurpleHttpConnection *hc,
+ gboolean reading_state, int processed, int total, gpointer _xfer)
+{
+ PurpleXfer *xfer = _xfer;
+ gboolean eof;
+ int total_real;
+
+ if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE) {
+ if (!reading_state)
+ return;
+ } else {
+ if (reading_state)
+ return;
+ }
+
+ eof = (processed >= total);
+ total_real = purple_xfer_get_size(xfer);
+ if (eof || processed > total_real)
+ processed = total_real; /* just to be sure */
+
+ purple_xfer_set_bytes_sent(xfer, processed);
+ purple_xfer_update_progress(xfer);
+}
+
+static ggp_edisc_xfer_ack_status
+ggp_edisc_xfer_parse_ack_status(const gchar *str)
+{
+ g_return_val_if_fail(str != NULL, GGP_EDISC_XFER_ACK_STATUS_UNKNOWN);
+
+ if (g_strcmp0("unknown", str) == 0)
+ return GGP_EDISC_XFER_ACK_STATUS_UNKNOWN;
+ if (g_strcmp0("allowed", str) == 0)
+ return GGP_EDISC_XFER_ACK_STATUS_ALLOWED;
+ if (g_strcmp0("rejected", str) == 0)
+ return GGP_EDISC_XFER_ACK_STATUS_REJECTED;
+
+ purple_debug_warning("gg", "ggp_edisc_xfer_parse_ack_status: "
+ "unknown status (%s)\n", str);
+ return GGP_EDISC_XFER_ACK_STATUS_UNKNOWN;
+}
+
+/*******************************************************************************
+ * Sending a file.
+ ******************************************************************************/
+
+static void ggp_edisc_xfer_send_init(PurpleXfer *xfer);
+static void ggp_edisc_xfer_send_init_authenticated(PurpleConnection *gc,
+ gboolean success, gpointer _xfer);
+static void ggp_edisc_xfer_send_init_ticket_created(PurpleHttpConnection *hc,
+ PurpleHttpResponse *response, gpointer _xfer);
+static void ggp_edisc_xfer_send_reader(PurpleHttpConnection *hc,
+ gchar *buffer, size_t offset, size_t length, gpointer _xfer,
+ PurpleHttpContentReaderCb cb);
+static void ggp_edisc_xfer_send_start(PurpleXfer *xfer);
+static void ggp_edisc_xfer_send_done(PurpleHttpConnection *hc,
+ PurpleHttpResponse *response, gpointer _xfer);
+
+gboolean ggp_edisc_xfer_can_receive_file(PurpleConnection *gc,
+ const char *who)
+{
+ PurpleBuddy *buddy;
+
+ g_return_val_if_fail(gc != NULL, FALSE);
+ g_return_val_if_fail(who != NULL, FALSE);
+
+ buddy = purple_blist_find_buddy(purple_connection_get_account(gc), who);
+ if (buddy == NULL)
+ return FALSE;
+
+ /* TODO: check, if this buddy have us on his list */
+
+ return PURPLE_BUDDY_IS_ONLINE(buddy);
+}
+
+static void ggp_edisc_xfer_send_init(PurpleXfer *xfer)
+{
+ ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer);
+
+ purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_NOT_STARTED);
+
+ edisc_xfer->filename = g_strdup(purple_xfer_get_filename(xfer));
+ g_strcanon(edisc_xfer->filename, GGP_EDISC_FNAME_ALLOWED, '_');
+
+ ggp_ggdrive_auth(edisc_xfer->gc, ggp_edisc_xfer_send_init_authenticated,
+ xfer);
+}
+
+static void ggp_edisc_xfer_send_init_authenticated(PurpleConnection *gc,
+ gboolean success, gpointer _xfer)
+{
+ 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);
+ gchar *data;
+
+ if (purple_xfer_is_cancelled(xfer))
+ return;
+
+ if (!success) {
+ ggp_edisc_xfer_error(xfer, _("Authentication failed"));
+ return;
+ }
+
+ g_return_if_fail(sdata != NULL);
+
+ req = purple_http_request_new("https://drive.mpa.gg.pl/send_ticket");
+ purple_http_request_set_method(req, "PUT");
+
+ ggp_edisc_set_defaults(req);
+ purple_http_request_set_cookie_jar(req, sdata->cookies);
+
+ purple_http_request_header_set(req, "X-gged-security-token",
+ sdata->security_token);
+
+ data = g_strdup_printf("{\"send_ticket\":{"
+ "\"recipient\":\"%s\","
+ "\"file_name\":\"%s\","
+ "\"file_size\":\"%u\""
+ "}}",
+ purple_xfer_get_remote_user(xfer),
+ edisc_xfer->filename,
+ (int)purple_xfer_get_size(xfer));
+ purple_http_request_set_contents(req, data, -1);
+ g_free(data);
+
+ edisc_xfer->hc = purple_http_request(gc, req,
+ ggp_edisc_xfer_send_init_ticket_created, xfer);
+ purple_http_request_unref(req);
+}
+
+static void ggp_edisc_xfer_send_init_ticket_created(PurpleHttpConnection *hc,
+ PurpleHttpResponse *response, gpointer _xfer)
+{
+ 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);
+ const gchar *data = purple_http_response_get_data(response, NULL);
+ ggp_edisc_xfer_ack_status ack_status;
+ JsonParser *parser;
+ JsonObject *ticket;
+
+ if (purple_xfer_is_cancelled(xfer))
+ return;
+
+ g_return_if_fail(sdata != NULL);
+
+ edisc_xfer->hc = NULL;
+
+ if (!purple_http_response_is_successful(response)) {
+ int error_id = ggp_edisc_parse_error(data);
+ if (error_id == 206) /* recipient not logged in */
+ ggp_edisc_xfer_error(xfer,
+ _("Recipient not logged in"));
+ else if (error_id == 207) /* bad sender recipient relation */
+ ggp_edisc_xfer_error(xfer, _("Recipient didn't added "
+ "you to his buddy list"));
+ else
+ ggp_edisc_xfer_error(xfer,
+ _("Cannot offer sending a file"));
+ return;
+ }
+
+ parser = ggp_json_parse(data);
+ ticket = json_node_get_object(json_parser_get_root(parser));
+ ticket = json_object_get_object_member(ticket, "result");
+ ticket = json_object_get_object_member(ticket, "send_ticket");
+ edisc_xfer->ticket_id = g_strdup(json_object_get_string_member(
+ ticket, "id"));
+ ack_status = ggp_edisc_xfer_parse_ack_status(
+ json_object_get_string_member(ticket, "ack_status"));
+ /* send_mode: "normal", "publink" (for legacy clients) */
+
+ g_object_unref(parser);
+
+ if (edisc_xfer->ticket_id == NULL) {
+ purple_debug_error("gg",
+ "ggp_edisc_xfer_send_init_ticket_created: "
+ "couldn't get ticket id\n");
+ return;
+ }
+
+ purple_debug_info("gg", "ggp_edisc_xfer_send_init_ticket_created: "
+ "ticket \"%s\" created\n", edisc_xfer->ticket_id);
+
+ g_hash_table_insert(sdata->xfers_initialized,
+ edisc_xfer->ticket_id, xfer);
+ g_hash_table_insert(sdata->xfers_history,
+ g_strdup(edisc_xfer->ticket_id), GINT_TO_POINTER(1));
+
+ if (ack_status != GGP_EDISC_XFER_ACK_STATUS_UNKNOWN)
+ ggp_edisc_xfer_send_ticket_changed(edisc_xfer->gc, xfer,
+ ack_status == GGP_EDISC_XFER_ACK_STATUS_ALLOWED);
+}
+
+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);
+ if (!edisc_xfer) {
+ purple_debug_fatal("gg", "ggp_edisc_event_ticket_changed: "
+ "transfer %p already free'd\n", xfer);
+ return;
+ }
+
+ if (!is_allowed) {
+ 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;
+ }
+
+ if (edisc_xfer->allowed) {
+ purple_debug_misc("gg", "ggp_edisc_event_ticket_changed: "
+ "transfer %p already allowed\n", xfer);
+ return;
+ }
+ edisc_xfer->allowed = TRUE;
+
+ purple_xfer_start(xfer, -1, NULL, 0);
+}
+
+static void ggp_edisc_xfer_send_reader(PurpleHttpConnection *hc,
+ gchar *buffer, size_t offset, size_t length, gpointer _xfer,
+ PurpleHttpContentReaderCb cb)
+{
+ PurpleXfer *xfer = _xfer;
+ ggp_edisc_xfer *edisc_xfer;
+ int stored;
+ gboolean success, eof = FALSE;
+
+ g_return_if_fail(xfer != NULL);
+ edisc_xfer = purple_xfer_get_protocol_data(xfer);
+ g_return_if_fail(edisc_xfer != NULL);
+
+ if (edisc_xfer->already_read != offset) {
+ purple_debug_error("gg", "ggp_edisc_xfer_send_reader: "
+ "Invalid offset (%" G_GSIZE_FORMAT " != %" G_GSIZE_FORMAT ")\n",
+ edisc_xfer->already_read, offset);
+ ggp_edisc_xfer_error(xfer, _("Error while reading a file"));
+ return;
+ }
+
+ stored = purple_xfer_read_file(xfer, (guchar *)buffer, length);
+
+ if (stored < 0)
+ success = FALSE;
+ else {
+ success = TRUE;
+ edisc_xfer->already_read += stored;
+ eof = ((goffset)edisc_xfer->already_read >= purple_xfer_get_size(xfer));
+ }
+
+ cb(hc, success, eof, stored);
+}
+
+static void ggp_edisc_xfer_send_start(PurpleXfer *xfer)
+{
+ ggp_edisc_session_data *sdata;
+ ggp_edisc_xfer *edisc_xfer;
+ gchar *upload_url, *filename_e;
+ PurpleHttpRequest *req;
+
+ g_return_if_fail(xfer != NULL);
+ edisc_xfer = purple_xfer_get_protocol_data(xfer);
+ g_return_if_fail(edisc_xfer != NULL);
+ sdata = ggp_edisc_get_sdata(edisc_xfer->gc);
+ g_return_if_fail(sdata != NULL);
+
+ filename_e = purple_strreplace(edisc_xfer->filename, " ", "%20");
+ upload_url = g_strdup_printf("https://drive.mpa.gg.pl/me/file/outbox/"
+ "%s%%2C%s", edisc_xfer->ticket_id, filename_e);
+ g_free(filename_e);
+ req = purple_http_request_new(upload_url);
+ g_free(upload_url);
+
+ purple_http_request_set_method(req, "PUT");
+ purple_http_request_set_timeout(req, -1);
+
+ ggp_edisc_set_defaults(req);
+ purple_http_request_set_cookie_jar(req, sdata->cookies);
+
+ purple_http_request_header_set(req, "X-gged-local-revision", "0");
+ purple_http_request_header_set(req, "X-gged-security-token",
+ sdata->security_token);
+ purple_http_request_header_set(req, "X-gged-metadata",
+ "{\"node_type\": \"file\"}");
+
+ purple_http_request_set_contents_reader(req, ggp_edisc_xfer_send_reader,
+ purple_xfer_get_size(xfer), xfer);
+
+ edisc_xfer->hc = purple_http_request(edisc_xfer->gc, req,
+ ggp_edisc_xfer_send_done, xfer);
+ purple_http_request_unref(req);
+
+ purple_http_conn_set_progress_watcher(edisc_xfer->hc,
+ ggp_edisc_xfer_progress_watcher, xfer, 250000);
+}
+
+static void ggp_edisc_xfer_send_done(PurpleHttpConnection *hc,
+ PurpleHttpResponse *response, gpointer _xfer)
+{
+ PurpleXfer *xfer = _xfer;
+ ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer);
+ const gchar *data = purple_http_response_get_data(response, NULL);
+ JsonParser *parser;
+ JsonObject *result;
+ int result_status = -1;
+
+ 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)) {
+ ggp_edisc_xfer_error(xfer, _("Error while sending a file"));
+ return;
+ }
+
+ 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"))
+ 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
+ ggp_edisc_xfer_error(xfer, _("Error while sending a file"));
+}
+
+PurpleXfer * ggp_edisc_xfer_send_new(PurpleConnection *gc, const char *who)
+{
+ PurpleXfer *xfer;
+ ggp_edisc_xfer *edisc_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);
+
+ edisc_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;
+}
+
+void ggp_edisc_xfer_send_file(PurpleConnection *gc, const char *who,
+ const char *filename)
+{
+ PurpleXfer *xfer;
+
+ g_return_if_fail(gc != NULL);
+ g_return_if_fail(who != NULL);
+
+ /* Nothing interesting here, this code is common among prpls.
+ * See ggp_edisc_xfer_send_new. */
+
+ xfer = ggp_edisc_xfer_send_new(gc, who);
+ if (filename)
+ purple_xfer_request_accepted(xfer, filename);
+ else
+ purple_xfer_request(xfer);
+}
+
+/*******************************************************************************
+ * Receiving a file.
+ ******************************************************************************/
+
+static void ggp_edisc_xfer_recv_ticket_update_authenticated(
+ PurpleConnection *gc, gboolean success, gpointer _ticket);
+static void ggp_edisc_xfer_recv_ticket_update_got(PurpleHttpConnection *hc,
+ PurpleHttpResponse *response, gpointer user_data);
+static PurpleXfer * ggp_edisc_xfer_recv_new(PurpleConnection *gc,
+ const char *who);
+static void ggp_edisc_xfer_recv_accept(PurpleXfer *xfer);
+static void ggp_edisc_xfer_recv_start(PurpleXfer *xfer);
+static void ggp_edisc_xfer_recv_ack(PurpleXfer *xfer, gboolean accept);
+static void ggp_edisc_xfer_recv_ack_done(PurpleHttpConnection *hc,
+ PurpleHttpResponse *response, gpointer _xfer);
+static gboolean ggp_edisc_xfer_recv_writer(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, const gchar *buffer, size_t offset,
+ size_t length, gpointer user_data);
+static void ggp_edisc_xfer_recv_done(PurpleHttpConnection *hc,
+ PurpleHttpResponse *response, gpointer _xfer);
+
+static void ggp_edisc_xfer_recv_ticket_got(PurpleConnection *gc,
+ const gchar *ticket_id)
+{
+ ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc);
+
+ g_return_if_fail(sdata != NULL);
+
+ if (g_hash_table_lookup(sdata->xfers_history, ticket_id))
+ return;
+
+ ggp_ggdrive_auth(gc, ggp_edisc_xfer_recv_ticket_update_authenticated,
+ g_strdup(ticket_id));
+}
+
+static void ggp_edisc_xfer_recv_ticket_update_authenticated(
+ PurpleConnection *gc, gboolean success, gpointer _ticket)
+{
+ ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc);
+ PurpleHttpRequest *req;
+ gchar *ticket = _ticket;
+
+ g_return_if_fail(sdata != NULL);
+
+ if (!success) {
+ purple_debug_warning("gg",
+ "ggp_edisc_xfer_recv_ticket_update_authenticated: "
+ "update of ticket %s aborted due to authentication "
+ "failure\n", ticket);
+ g_free(ticket);
+ return;
+ }
+
+ req = purple_http_request_new(ggp_edisc_xfer_ticket_url(ticket));
+ g_free(ticket);
+
+ ggp_edisc_set_defaults(req);
+ purple_http_request_set_cookie_jar(req, sdata->cookies);
+
+ purple_http_request_header_set(req, "X-gged-security-token",
+ sdata->security_token);
+
+ purple_http_request(gc, req, ggp_edisc_xfer_recv_ticket_update_got,
+ NULL);
+ purple_http_request_unref(req);
+}
+
+static void ggp_edisc_xfer_recv_ticket_update_got(PurpleHttpConnection *hc,
+ PurpleHttpResponse *response, gpointer user_data)
+{
+ PurpleConnection *gc = purple_http_conn_get_purple_connection(hc);
+ PurpleXfer *xfer;
+ ggp_edisc_xfer *edisc_xfer;
+ JsonParser *parser;
+ JsonObject *result;
+ int status = -1;
+ ggp_edisc_session_data *sdata;
+
+ const gchar *ticket_id, *file_name, *send_mode_str;
+ uin_t sender, recipient;
+ int file_size;
+
+ if (!purple_http_response_is_successful(response)) {
+ purple_debug_error("gg",
+ "ggp_edisc_xfer_recv_ticket_update_got: "
+ "cannot fetch update for ticket (code=%d)\n",
+ purple_http_response_get_code(response));
+ return;
+ }
+
+ sdata = ggp_edisc_get_sdata(gc);
+ g_return_if_fail(sdata != NULL);
+
+ parser = ggp_json_parse(purple_http_response_get_data(response, NULL));
+ 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"))
+ status = json_object_get_int_member(result, "status");
+ result = json_object_get_object_member(result, "send_ticket");
+
+ if (status != 0) {
+ purple_debug_warning("gg",
+ "ggp_edisc_xfer_recv_ticket_update_got: failed to get "
+ "update (status=%d)\n", status);
+ g_object_unref(parser);
+ return;
+ }
+
+ ticket_id = json_object_get_string_member(result, "id");
+ sender = ggp_str_to_uin(json_object_get_string_member(result,
+ "sender"));
+ recipient = ggp_str_to_uin(json_object_get_string_member(result,
+ "recipient"));
+ file_size = g_ascii_strtoll(json_object_get_string_member(result,
+ "file_size"), NULL, 10);
+ file_name = json_object_get_string_member(result, "file_name");
+
+ /* GG11: normal
+ * AQQ 2.4.2.10: direct_inbox
+ */
+ send_mode_str = json_object_get_string_member(result, "send_mode");
+
+ /* more fields:
+ * send_progress (float), ack_status, send_status
+ */
+
+ if (purple_debug_is_verbose() && purple_debug_is_unsafe())
+ purple_debug_info("gg", "Got ticket update: id=%s, sender=%u, "
+ "recipient=%u, file name=\"%s\", file size=%d, "
+ "send mode=%s)\n",
+ ticket_id,
+ sender, recipient,
+ file_name, file_size,
+ send_mode_str);
+
+ xfer = g_hash_table_lookup(sdata->xfers_initialized, ticket_id);
+ if (xfer != NULL) {
+ purple_debug_misc("gg", "ggp_edisc_xfer_recv_ticket_update_got:"
+ " ticket %s already updated\n",
+ purple_debug_is_unsafe() ? ticket_id : "");
+ g_object_unref(parser);
+ return;
+ }
+
+ if (recipient != ggp_get_my_uin(gc)) {
+ purple_debug_misc("gg", "ggp_edisc_xfer_recv_ticket_update_got:"
+ " ticket %s is not for incoming transfer "
+ "(its from %u to %u)\n",
+ purple_debug_is_unsafe() ? ticket_id : "",
+ sender, recipient);
+ g_object_unref(parser);
+ return;
+ }
+
+ xfer = ggp_edisc_xfer_recv_new(gc, ggp_uin_to_str(sender));
+ 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->ticket_id = g_strdup(ticket_id);
+ g_hash_table_insert(sdata->xfers_initialized,
+ edisc_xfer->ticket_id, xfer);
+ g_hash_table_insert(sdata->xfers_history,
+ g_strdup(ticket_id), GINT_TO_POINTER(1));
+
+ g_object_unref(parser);
+}
+
+static PurpleXfer * ggp_edisc_xfer_recv_new(PurpleConnection *gc,
+ const char *who)
+{
+ PurpleXfer *xfer;
+ ggp_edisc_xfer *edisc_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;
+
+ 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);
+
+ return xfer;
+}
+
+static void ggp_edisc_xfer_recv_reject(PurpleXfer *xfer)
+{
+ ggp_edisc_xfer_recv_ack(xfer, FALSE);
+}
+
+static void ggp_edisc_xfer_recv_accept(PurpleXfer *xfer)
+{
+ ggp_edisc_xfer_recv_ack(xfer, TRUE);
+}
+
+static void ggp_edisc_xfer_recv_ack(PurpleXfer *xfer, gboolean accept)
+{
+ ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer);
+ ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(edisc_xfer->gc);
+ PurpleHttpRequest *req;
+
+ g_return_if_fail(sdata != NULL);
+
+ edisc_xfer->allowed = accept;
+
+ req = purple_http_request_new(ggp_edisc_xfer_ticket_url(
+ edisc_xfer->ticket_id));
+ purple_http_request_set_method(req, "PUT");
+
+ ggp_edisc_set_defaults(req);
+ purple_http_request_set_cookie_jar(req, sdata->cookies);
+ purple_http_request_header_set(req, "X-gged-security-token",
+ sdata->security_token);
+
+ purple_http_request_header_set(req, "X-gged-ack-status",
+ accept ? "allow" : "reject");
+
+ edisc_xfer->hc = purple_http_request(edisc_xfer->gc, req,
+ accept ? ggp_edisc_xfer_recv_ack_done : NULL, xfer);
+ purple_http_request_unref(req);
+
+ if (!accept) {
+ edisc_xfer->hc = NULL;
+ ggp_edisc_xfer_free(xfer);
+ }
+}
+
+static void ggp_edisc_xfer_recv_ack_done(PurpleHttpConnection *hc,
+ PurpleHttpResponse *response, gpointer _xfer)
+{
+ PurpleXfer *xfer = _xfer;
+ ggp_edisc_xfer *edisc_xfer;
+
+ if (purple_xfer_is_cancelled(xfer))
+ g_return_if_reached();
+
+ edisc_xfer = purple_xfer_get_protocol_data(xfer);
+ edisc_xfer->hc = NULL;
+
+ if (!purple_http_response_is_successful(response)) {
+ ggp_edisc_xfer_error(xfer, _("Cannot confirm file transfer."));
+ return;
+ }
+
+ purple_debug_info("gg", "ggp_edisc_xfer_recv_ack_done: [%s]\n",
+ purple_http_response_get_data(response, NULL));
+}
+
+static void ggp_edisc_xfer_recv_ticket_completed(PurpleXfer *xfer)
+{
+ ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer);
+
+ if (edisc_xfer->ready)
+ return;
+ edisc_xfer->ready = TRUE;
+
+ purple_xfer_start(xfer, -1, NULL, 0);
+}
+
+static void ggp_edisc_xfer_recv_start(PurpleXfer *xfer)
+{
+ ggp_edisc_session_data *sdata;
+ ggp_edisc_xfer *edisc_xfer;
+ gchar *upload_url;
+ PurpleHttpRequest *req;
+
+ g_return_if_fail(xfer != NULL);
+ edisc_xfer = purple_xfer_get_protocol_data(xfer);
+ g_return_if_fail(edisc_xfer != NULL);
+ sdata = ggp_edisc_get_sdata(edisc_xfer->gc);
+ g_return_if_fail(sdata != NULL);
+
+ upload_url = g_strdup_printf("https://drive.mpa.gg.pl/me/file/inbox/"
+ "%s,%s?api_version=%s&security_token=%s",
+ edisc_xfer->ticket_id, purple_url_encode(purple_xfer_get_filename(xfer)),
+ GGP_EDISC_API, sdata->security_token);
+ req = purple_http_request_new(upload_url);
+ g_free(upload_url);
+
+ purple_http_request_set_timeout(req, -1);
+
+ ggp_edisc_set_defaults(req);
+ purple_http_request_set_max_len(req, purple_xfer_get_size(xfer) + 1);
+ purple_http_request_set_cookie_jar(req, sdata->cookies);
+
+ purple_http_request_set_response_writer(req, ggp_edisc_xfer_recv_writer,
+ xfer);
+
+ edisc_xfer->hc = purple_http_request(edisc_xfer->gc, req,
+ ggp_edisc_xfer_recv_done, xfer);
+ purple_http_request_unref(req);
+
+ purple_http_conn_set_progress_watcher(edisc_xfer->hc,
+ ggp_edisc_xfer_progress_watcher, xfer, 250000);
+}
+
+static gboolean ggp_edisc_xfer_recv_writer(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, const gchar *buffer, size_t offset,
+ 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;
+
+ if (stored < 0 || (gsize)stored != length) {
+ purple_debug_error("gg", "ggp_edisc_xfer_recv_writer: "
+ "saved too less\n");
+ return FALSE;
+ }
+
+ if (stored > purple_xfer_get_bytes_remaining(xfer)) {
+ purple_debug_error("gg", "ggp_edisc_xfer_recv_writer: "
+ "saved too much (%" G_GSSIZE_FORMAT " > %" G_GOFFSET_FORMAT ")\n",
+ stored, purple_xfer_get_bytes_remaining(xfer));
+ return FALSE;
+ }
+
+ /* May look redundant with ggp_edisc_xfer_progress_watcher,
+ * but it isn't!
+ */
+ purple_xfer_set_bytes_sent(xfer,
+ purple_xfer_get_bytes_sent(xfer) + stored);
+
+ return TRUE;
+}
+
+static void ggp_edisc_xfer_recv_done(PurpleHttpConnection *hc,
+ PurpleHttpResponse *response, gpointer _xfer)
+{
+ PurpleXfer *xfer = _xfer;
+ ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(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)) {
+ ggp_edisc_xfer_error(xfer, _("Error while receiving a file"));
+ return;
+ }
+
+ 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");
+ ggp_edisc_xfer_error(xfer, _("Error while receiving a file"));
+ }
+}
+
+/*******************************************************************************
+ * Authentication.
+ ******************************************************************************/
+
+struct _ggp_edisc_auth_data
+{
+ ggp_ggdrive_auth_cb cb;
+ gpointer user_data;
+};
+
+static void ggp_ggdrive_auth_done(PurpleHttpConnection *hc,
+ PurpleHttpResponse *response, gpointer user_data);
+
+static void ggp_ggdrive_auth(PurpleConnection *gc, ggp_ggdrive_auth_cb cb,
+ gpointer user_data)
+{
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+ ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc);
+ ggp_edisc_auth_data *auth;
+ const gchar *imtoken;
+ gchar *metadata;
+ PurpleHttpRequest *req;
+
+ g_return_if_fail(sdata != NULL);
+
+ imtoken = ggp_get_imtoken(gc);
+ if (!imtoken) {
+ cb(gc, FALSE, user_data);
+ return;
+ }
+
+ if (sdata->auth_done) {
+ cb(gc, sdata->security_token != NULL, user_data);
+ return;
+ }
+
+ auth = g_new0(ggp_edisc_auth_data, 1);
+ auth->cb = cb;
+ auth->user_data = user_data;
+ sdata->auth_pending = g_list_prepend(sdata->auth_pending, auth);
+
+ if (sdata->auth_request)
+ return;
+
+ purple_debug_info("gg", "ggp_ggdrive_auth(gc=%p)\n", gc);
+
+ req = purple_http_request_new("https://drive.mpa.gg.pl/signin");
+ purple_http_request_set_method(req, "PUT");
+
+ ggp_edisc_set_defaults(req);
+ purple_http_request_set_cookie_jar(req, sdata->cookies);
+
+ metadata = g_strdup_printf("{"
+ "\"id\": \"%032x\", "
+ "\"name\": \"%s\", "
+ "\"os_version\": \"" GGP_EDISC_OS "\", "
+ "\"client_version\": \"%s\", "
+ "\"type\": \"" GGP_EDISC_TYPE "\"}",
+ g_random_int_range(1, 1 << 16),
+ purple_get_host_name(),
+ ggp_libgaduw_version(gc));
+
+ purple_http_request_header_set_printf(req, "Authorization",
+ "IMToken %s", imtoken);
+ purple_http_request_header_set_printf(req, "X-gged-user",
+ "gg/pl:%u", accdata->session->uin);
+ purple_http_request_header_set(req, "X-gged-client-metadata", metadata);
+ g_free(metadata);
+
+ sdata->auth_request = purple_http_request(gc, req,
+ ggp_ggdrive_auth_done, NULL);
+ purple_http_request_unref(req);
+}
+
+static void ggp_ggdrive_auth_results(PurpleConnection *gc, gboolean success)
+{
+ ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc);
+ GList *it;
+
+ purple_debug_info("gg", "ggp_ggdrive_auth_results(gc=%p): %d\n",
+ gc, success);
+
+ g_return_if_fail(sdata != NULL);
+
+ it = g_list_first(sdata->auth_pending);
+ while (it) {
+ ggp_edisc_auth_data *auth = it->data;
+ it = g_list_next(it);
+
+ auth->cb(gc, success, auth->user_data);
+ g_free(auth);
+ }
+ g_list_free(sdata->auth_pending);
+ sdata->auth_pending = NULL;
+ sdata->auth_done = TRUE;
+}
+
+static void ggp_ggdrive_auth_done(PurpleHttpConnection *hc,
+ PurpleHttpResponse *response, gpointer user_data)
+{
+ PurpleConnection *gc = purple_http_conn_get_purple_connection(hc);
+ ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc);
+ JsonParser *parser;
+ JsonObject *result;
+ int status = -1;
+
+ g_return_if_fail(sdata != NULL);
+
+ sdata->auth_request = NULL;
+
+ if (!purple_http_response_is_successful(response)) {
+ purple_debug_misc("gg", "ggp_ggdrive_auth_done: authentication "
+ "failed due to unsuccessful request (code = %d)\n",
+ purple_http_response_get_code(response));
+ ggp_ggdrive_auth_results(gc, FALSE);
+ return;
+ }
+
+ parser = ggp_json_parse(purple_http_response_get_data(response, NULL));
+ 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"))
+ status = json_object_get_int_member(result, "status");
+ g_object_unref(parser);
+
+ if (status != 0 ) {
+ purple_debug_misc("gg", "ggp_ggdrive_auth_done: authentication "
+ "failed due to bad result (status=%d)\n", status);
+ if (purple_debug_is_verbose())
+ purple_debug_misc("gg", "ggp_ggdrive_auth_done: "
+ "result = %s\n",
+ purple_http_response_get_data(response, NULL));
+ ggp_ggdrive_auth_results(gc, FALSE);
+ return;
+ }
+
+ sdata->security_token = g_strdup(purple_http_response_get_header(
+ response, "X-gged-security-token"));
+ if (!sdata->security_token) {
+ purple_debug_misc("gg", "ggp_ggdrive_auth_done: authentication "
+ "failed due to missing security token header\n");
+ ggp_ggdrive_auth_results(gc, FALSE);
+ return;
+ }
+
+ if (purple_debug_is_unsafe())
+ purple_debug_misc("gg", "ggp_ggdrive_auth_done: "
+ "security_token=%s\n", sdata->security_token);
+ ggp_ggdrive_auth_results(gc, TRUE);
+}
diff --git a/libpurple/protocols/gg/edisc.h b/libpurple/protocols/gg/edisc.h
new file mode 100644
index 0000000000..3d5377efac
--- /dev/null
+++ b/libpurple/protocols/gg/edisc.h
@@ -0,0 +1,49 @@
+/* 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.
+ *
+ * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
+ * As a recipient of this file you may choose, which license to receive the
+ * code under. As a contributor, you have to ensure the new code is
+ * compatible with both.
+ *
+ * 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 _GGP_EDISC_H
+#define _GGP_EDISC_H
+
+#include <internal.h>
+
+typedef struct _ggp_edisc_session_data ggp_edisc_session_data;
+
+/* Setting up. */
+void ggp_edisc_setup(PurpleConnection *gc);
+void ggp_edisc_cleanup(PurpleConnection *gc);
+
+/* General xfer functions. */
+void ggp_edisc_xfer_ticket_changed(PurpleConnection *gc,
+ const char *data);
+
+/* Sending a file. */
+gboolean ggp_edisc_xfer_can_receive_file(PurpleConnection *gc, const char *who);
+void ggp_edisc_xfer_send_file(PurpleConnection *gc, const char *who,
+ const char *filename);
+PurpleXfer * ggp_edisc_xfer_send_new(PurpleConnection *gc, const char *who);
+
+#endif /* _GGP_EDISC_H */
diff --git a/libpurple/protocols/gg/gg-utils.c b/libpurple/protocols/gg/gg-utils.c
deleted file mode 100644
index 96e3b295ce..0000000000
--- a/libpurple/protocols/gg/gg-utils.c
+++ /dev/null
@@ -1,147 +0,0 @@
-/**
- * @file gg-utils.c
- *
- * purple
- *
- * Copyright (C) 2005 Bartosz Oler <bartosz@bzimage.us>
- *
- * 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 "gg-utils.h"
-
-
-/* uin_t ggp_str_to_uin(const char *str) {{{ */
-uin_t ggp_str_to_uin(const char *str)
-{
- char *tmp;
- long num;
-
- if (!str)
- return 0;
-
- errno = 0;
- num = strtol(str, &tmp, 10);
-
- if (*str == '\0' || *tmp != '\0')
- return 0;
-
- if ((errno == ERANGE || (num == LONG_MAX || num == LONG_MIN))
-#if (LONG_MAX > UINT_MAX)
- || num > (long)UINT_MAX
-#endif
- || num < 0)
- return 0;
-
- return (uin_t) num;
-}
-/* }}} */
-
-/* unsigned int ggp_array_size(char **array) {{{ */
-unsigned int ggp_array_size(char **array)
-{
- unsigned int i;
-
- for (i = 0; array[i] != NULL && i < UINT_MAX; i++)
- {}
-
- return i;
-}
-/* }}} */
-
-/* char *charset_convert(const gchar *locstr, const char *encsrc, const char *encdst) {{{ */
-char *charset_convert(const gchar *locstr, const char *encsrc, const char *encdst)
-{
- gchar *msg;
- GError *err = NULL;
-
- if (locstr == NULL)
- return NULL;
-
- msg = g_convert_with_fallback(locstr, strlen(locstr), encdst, encsrc,
- "?", NULL, NULL, &err);
- if (err != NULL) {
- purple_debug_error("gg", "Error converting from %s to %s: %s\n",
- encsrc, encdst, err->message);
- g_error_free(err);
- }
-
- /* Just in case? */
- if (msg == NULL)
- msg = g_strdup(locstr);
-
- return msg;
-}
-/* }}} */
-
-/* ggp_get_uin(PurpleAccount *account) {{{ */
-uin_t ggp_get_uin(PurpleAccount *account)
-{
- return ggp_str_to_uin(purple_account_get_username(account));
-}
-/* }}} */
-
-/* char *ggp_buddy_get_name(PurpleConnection *gc, const uin_t uin) {{{ */
-char *ggp_buddy_get_name(PurpleConnection *gc, const uin_t uin)
-{
- PurpleBuddy *buddy;
- gchar *str_uin;
-
- str_uin = g_strdup_printf("%lu", (unsigned long int)uin);
-
- buddy = purple_find_buddy(purple_connection_get_account(gc), str_uin);
- if (buddy != NULL) {
- g_free(str_uin);
- return g_strdup(purple_buddy_get_alias(buddy));
- } else {
- return str_uin;
- }
-}
-/* }}} */
-
-void ggp_status_fake_to_self(PurpleAccount *account)
-{
- PurplePresence *presence;
- PurpleStatus *status;
- const char *status_id;
- const char *msg;
-
- if (! purple_find_buddy(account, purple_account_get_username(account)))
- return;
-
- presence = purple_account_get_presence(account);
- status = purple_presence_get_active_status(presence);
- msg = purple_status_get_attr_string(status, "message");
- if (msg && !*msg)
- msg = NULL;
-
- status_id = purple_status_get_id(status);
- if (strcmp(status_id, "invisible") == 0) {
- status_id = "offline";
- }
-
- if (msg) {
- if (strlen(msg) > GG_STATUS_DESCR_MAXSIZE) {
- msg = purple_markup_slice(msg, 0, GG_STATUS_DESCR_MAXSIZE);
- }
- }
- purple_prpl_got_user_status(account, purple_account_get_username(account),
- status_id,
- msg ? "message" : NULL, msg, NULL);
-}
-
-
-/* vim: set ts=8 sts=0 sw=8 noet: */
diff --git a/libpurple/protocols/gg/gg-utils.h b/libpurple/protocols/gg/gg-utils.h
deleted file mode 100644
index 48e8188250..0000000000
--- a/libpurple/protocols/gg/gg-utils.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/**
- * @file gg-utils.h
- *
- * purple
- *
- * Copyright (C) 2005 Bartosz Oler <bartosz@bzimage.us>
- *
- * 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 _PURPLE_GG_UTILS_H
-#define _PURPLE_GG_UTILS_H
-
-#include "internal.h"
-
-#include "plugin.h"
-#include "version.h"
-#include "notify.h"
-#include "status.h"
-#include "blist.h"
-#include "accountopt.h"
-#include "debug.h"
-#include "util.h"
-#include "request.h"
-
-#include "gg.h"
-
-
-/**
- * Convert a base 10 string to a UIN.
- *
- * @param str The string to convert
- *
- * @return UIN or 0 if an error occurred.
- */
-uin_t
-ggp_str_to_uin(const char *str);
-
-/**
- * Calculate size of a NULL-terminated array.
- *
- * @param array The array.
- *
- * @return Size of the array.
- */
-unsigned int
-ggp_array_size(char **array);
-
-/**
- * Convert enconding of a given string.
- *
- * @param locstr Input string.
- * @param encsrc Current encoding of the string.
- * @param encdst Target encoding of the string.
- *
- * @return Converted string (it must be g_free()ed when not used. Or NULL if
- * locstr is NULL.
- */
-char *
-charset_convert(const gchar *locstr, const char *encsrc, const char *encdst);
-
-/**
- * Get UIN of a given account.
- *
- * @param account Current account.
- *
- * @return UIN of an account.
- */
-uin_t
-ggp_get_uin(PurpleAccount *account);
-
-/**
- * Returns the best name of a buddy from the buddylist.
- *
- * @param gc PurpleConnection instance.
- * @param uin UIN of the buddy.
- *
- * @return Name of the buddy, or UIN converted to string.
- */
-char *
-ggp_buddy_get_name(PurpleConnection *gc, const uin_t uin);
-
-/**
- * Manages the display of account's status in the buddylist.
- *
- * @param account Current account.
- */
-void
-ggp_status_fake_to_self(PurpleAccount *account);
-
-
-#endif /* _PURPLE_GG_UTILS_H */
-
-/* vim: set ts=8 sts=0 sw=8 noet: */
diff --git a/libpurple/protocols/gg/gg.c b/libpurple/protocols/gg/gg.c
index d923e88b7a..a1b80d7b17 100644
--- a/libpurple/protocols/gg/gg.c
+++ b/libpurple/protocols/gg/gg.c
@@ -26,92 +26,87 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#include "internal.h"
+#include <internal.h>
#include "plugin.h"
#include "version.h"
#include "notify.h"
-#include "status.h"
-#include "blist.h"
+#include "buddylist.h"
#include "accountopt.h"
#include "debug.h"
#include "util.h"
#include "request.h"
#include "xmlnode.h"
-#include <libgadu.h>
-
#include "gg.h"
-#include "confer.h"
+#include "chat.h"
#include "search.h"
-#include "buddylist.h"
-#include "gg-utils.h"
-
-#define DISABLE_AVATARS 1
-
-static PurplePlugin *my_protocol = NULL;
-
-/* Prototypes */
-static void ggp_set_status(PurpleAccount *account, PurpleStatus *status);
-static int ggp_to_gg_status(PurpleStatus *status, char **msg);
+#include "blist.h"
+#include "utils.h"
+#include "resolver-purple.h"
+#include "deprecated.h"
+#include "purplew.h"
+#include "libgadu-events.h"
+#include "multilogon.h"
+#include "status.h"
+#include "servconn.h"
+#include "tcpsocket.h"
+#include "pubdir-prpl.h"
+#include "message-prpl.h"
+#include "html.h"
+#include "libgaduw.h"
/* ---------------------------------------------------------------------- */
-/* ----- EXTERNAL CALLBACKS --------------------------------------------- */
-/* ---------------------------------------------------------------------- */
+ggp_buddy_data * ggp_buddy_get_data(PurpleBuddy *buddy)
+{
+ ggp_buddy_data *buddy_data = purple_buddy_get_protocol_data(buddy);
+ if (buddy_data)
+ return buddy_data;
-/* ----- HELPERS -------------------------------------------------------- */
+ buddy_data = g_new0(ggp_buddy_data, 1); /* TODO: leak */
+ purple_buddy_set_protocol_data(buddy, buddy_data);
+ return buddy_data;
+}
-static PurpleInputCondition
-ggp_tcpsocket_inputcond_gg_to_purple(enum gg_check_t check)
+static void ggp_buddy_free(PurpleBuddy *buddy)
{
- PurpleInputCondition cond = 0;
+ ggp_buddy_data *buddy_data = purple_buddy_get_protocol_data(buddy);
- if (check & GG_CHECK_READ)
- cond |= PURPLE_INPUT_READ;
- if (check & GG_CHECK_WRITE)
- cond |= PURPLE_INPUT_WRITE;
+ if (!buddy_data)
+ return;
- return cond;
+ g_free(buddy_data);
+ purple_buddy_set_protocol_data(buddy, NULL);
}
-/**
- * Set up libgadu's proxy.
- *
- * @param account Account for which to set up the proxy.
- *
- * @return Zero if proxy setup is valid, otherwise -1.
- */
-static int ggp_setup_proxy(PurpleAccount *account)
+const gchar * ggp_get_imtoken(PurpleConnection *gc)
{
- PurpleProxyInfo *gpi;
-
- gpi = purple_proxy_get_setup(account);
-
- if ((purple_proxy_info_get_type(gpi) != PURPLE_PROXY_NONE) &&
- (purple_proxy_info_get_host(gpi) == NULL ||
- purple_proxy_info_get_port(gpi) <= 0)) {
-
- gg_proxy_enabled = 0;
- purple_notify_error(NULL, NULL, _("Invalid proxy settings"),
- _("Either the host name or port number specified for your given proxy type is invalid."));
- return -1;
- } else if (purple_proxy_info_get_type(gpi) != PURPLE_PROXY_NONE) {
- gg_proxy_enabled = 1;
- gg_proxy_host = g_strdup(purple_proxy_info_get_host(gpi));
- gg_proxy_port = purple_proxy_info_get_port(gpi);
- gg_proxy_username = g_strdup(purple_proxy_info_get_username(gpi));
- gg_proxy_password = g_strdup(purple_proxy_info_get_password(gpi));
- } else {
- gg_proxy_enabled = 0;
- }
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+
+ if (accdata->imtoken)
+ return accdata->imtoken;
- return 0;
+ if (accdata->imtoken_warned)
+ return NULL;
+ accdata->imtoken_warned = TRUE;
+
+ purple_notify_error(gc, _("Authentication failed"),
+ _("IMToken value has not been received."),
+ _("Some features will be disabled. "
+ "You may try again after a while."),
+ purple_request_cpar_from_connection(gc));
+ return NULL;
}
-/* }}} */
+uin_t ggp_own_uin(PurpleConnection *gc)
+{
+ return ggp_str_to_uin(purple_account_get_username(
+ purple_connection_get_account(gc)));
+}
/* ---------------------------------------------------------------------- */
+/* buddy list import/export from/to file */
static void ggp_callback_buddylist_save_ok(PurpleConnection *gc, const char *filename)
{
@@ -123,21 +118,22 @@ static void ggp_callback_buddylist_save_ok(PurpleConnection *gc, const char *fil
purple_debug_info("gg", "file = %s\n", filename);
if (buddylist == NULL) {
- purple_notify_info(account, _("Save Buddylist..."),
- _("Your buddylist is empty, nothing was written to the file."),
- NULL);
+ purple_notify_info(account, _("Save Buddylist..."), _("Your "
+ "buddylist is empty, nothing was written to the file."),
+ NULL, purple_request_cpar_from_connection(gc));
return;
}
- if(purple_util_write_data_to_file_absolute(filename, buddylist, -1)) {
+ if (purple_util_write_data_to_file_absolute(filename, buddylist, -1)) {
purple_notify_info(account, _("Save Buddylist..."),
- _("Buddylist saved successfully!"), NULL);
+ _("Buddylist saved successfully!"), NULL,
+ purple_request_cpar_from_connection(gc));
} else {
gchar *primary = g_strdup_printf(
_("Couldn't write buddy list for %s to %s"),
purple_account_get_username(account), filename);
purple_notify_error(account, _("Save Buddylist..."),
- primary, NULL);
+ primary, NULL, purple_request_cpar_from_connection(gc));
g_free(primary);
}
@@ -154,10 +150,9 @@ static void ggp_callback_buddylist_load_ok(PurpleConnection *gc, gchar *file)
purple_debug_info("gg", "file_name = %s\n", file);
if (!g_file_get_contents(file, &buddylist, &length, &error)) {
- purple_notify_error(account,
- _("Couldn't load buddylist"),
- _("Couldn't load buddylist"),
- error->message);
+ purple_notify_error(account, _("Couldn't load buddylist"),
+ _("Couldn't load buddylist"), error->message,
+ purple_request_cpar_from_connection(gc));
purple_debug_error("gg",
"Couldn't load buddylist. file = %s; error = %s\n",
@@ -171,9 +166,9 @@ static void ggp_callback_buddylist_load_ok(PurpleConnection *gc, gchar *file)
ggp_buddylist_load(gc, buddylist);
g_free(buddylist);
- purple_notify_info(account,
- _("Load Buddylist..."),
- _("Buddylist loaded successfully!"), NULL);
+ purple_notify_info(account, _("Load Buddylist..."),
+ _("Buddylist loaded successfully!"), NULL,
+ purple_request_cpar_from_connection(gc));
}
/* }}} */
@@ -185,9 +180,8 @@ static void ggp_action_buddylist_save(PurplePluginAction *action)
PurpleConnection *gc = (PurpleConnection *)action->context;
purple_request_file(action, _("Save buddylist..."), NULL, TRUE,
- G_CALLBACK(ggp_callback_buddylist_save_ok), NULL,
- purple_connection_get_account(gc), NULL, NULL,
- gc);
+ G_CALLBACK(ggp_callback_buddylist_save_ok), NULL,
+ purple_request_cpar_from_connection(gc), gc);
}
static void ggp_action_buddylist_load(PurplePluginAction *action)
@@ -195,290 +189,30 @@ static void ggp_action_buddylist_load(PurplePluginAction *action)
PurpleConnection *gc = (PurpleConnection *)action->context;
purple_request_file(action, _("Load buddylist from file..."), NULL,
- FALSE,
- G_CALLBACK(ggp_callback_buddylist_load_ok), NULL,
- purple_connection_get_account(gc), NULL, NULL,
- gc);
-}
-
-/* ----- PUBLIC DIRECTORY SEARCH ---------------------------------------- */
-
-static void ggp_callback_show_next(PurpleConnection *gc, GList *row, gpointer user_data)
-{
- GGPInfo *info = gc->proto_data;
- GGPSearchForm *form = user_data;
- guint32 seq;
-
- form->page_number++;
-
- ggp_search_remove(info->searches, form->seq);
- purple_debug_info("gg", "ggp_callback_show_next(): Removed seq %u\n",
- form->seq);
-
- seq = ggp_search_start(gc, form);
- ggp_search_add(info->searches, seq, form);
- purple_debug_info("gg", "ggp_callback_show_next(): Added seq %u\n",
- seq);
-}
-
-static void ggp_callback_add_buddy(PurpleConnection *gc, GList *row, gpointer user_data)
-{
- purple_blist_request_add_buddy(purple_connection_get_account(gc),
- g_list_nth_data(row, 0), NULL, NULL);
-}
-
-static void ggp_callback_im(PurpleConnection *gc, GList *row, gpointer user_data)
-{
- PurpleAccount *account;
- PurpleConversation *conv;
- char *name;
-
- account = purple_connection_get_account(gc);
-
- name = g_list_nth_data(row, 0);
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, name);
- purple_conversation_present(conv);
-}
-
-static void ggp_callback_find_buddies(PurpleConnection *gc, PurpleRequestFields *fields)
-{
- GGPInfo *info = gc->proto_data;
- GGPSearchForm *form;
- guint32 seq;
-
- form = ggp_search_form_new(GGP_SEARCH_TYPE_FULL);
-
- form->user_data = info;
- form->lastname = g_strdup(
- purple_request_fields_get_string(fields, "lastname"));
- form->firstname = g_strdup(
- purple_request_fields_get_string(fields, "firstname"));
- form->nickname = g_strdup(
- purple_request_fields_get_string(fields, "nickname"));
- form->city = g_strdup(
- purple_request_fields_get_string(fields, "city"));
- form->birthyear = g_strdup(
- purple_request_fields_get_string(fields, "year"));
-
- switch (purple_request_fields_get_choice(fields, "gender")) {
- case 1:
- form->gender = g_strdup(GG_PUBDIR50_GENDER_MALE);
- break;
- case 2:
- form->gender = g_strdup(GG_PUBDIR50_GENDER_FEMALE);
- break;
- default:
- form->gender = NULL;
- break;
- }
-
- form->active = purple_request_fields_get_bool(fields, "active")
- ? g_strdup(GG_PUBDIR50_ACTIVE_TRUE) : NULL;
-
- seq = ggp_search_start(gc, form);
- ggp_search_add(info->searches, seq, form);
- purple_debug_info("gg", "ggp_callback_find_buddies(): Added seq %u\n",
- seq);
-}
-
-static void ggp_find_buddies(PurplePluginAction *action)
-{
- PurpleConnection *gc = (PurpleConnection *)action->context;
-
- PurpleRequestFields *fields;
- PurpleRequestFieldGroup *group;
- PurpleRequestField *field;
-
- 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("lastname",
- _("Last name"), NULL, FALSE);
- purple_request_field_string_set_masked(field, FALSE);
- purple_request_field_group_add_field(group, field);
-
- field = purple_request_field_string_new("firstname",
- _("First name"), NULL, FALSE);
- purple_request_field_string_set_masked(field, FALSE);
- purple_request_field_group_add_field(group, field);
-
- field = purple_request_field_string_new("nickname",
- _("Nickname"), NULL, FALSE);
- purple_request_field_string_set_masked(field, FALSE);
- purple_request_field_group_add_field(group, field);
-
- field = purple_request_field_string_new("city",
- _("City"), NULL, FALSE);
- purple_request_field_string_set_masked(field, FALSE);
- purple_request_field_group_add_field(group, field);
-
- field = purple_request_field_string_new("year",
- _("Year of birth"), NULL, FALSE);
- purple_request_field_group_add_field(group, field);
-
- field = purple_request_field_choice_new("gender", _("Gender"), 0);
- purple_request_field_choice_add(field, _("Male or female"));
- purple_request_field_choice_add(field, _("Male"));
- purple_request_field_choice_add(field, _("Female"));
- purple_request_field_group_add_field(group, field);
-
- field = purple_request_field_bool_new("active",
- _("Only online"), FALSE);
- purple_request_field_group_add_field(group, field);
-
- purple_request_fields(gc,
- _("Find buddies"),
- _("Find buddies"),
- _("Please, enter your search criteria below"),
- fields,
- _("OK"), G_CALLBACK(ggp_callback_find_buddies),
- _("Cancel"), NULL,
- purple_connection_get_account(gc), NULL, NULL,
- gc);
-}
-
-/* ----- CHANGE STATUS BROADCASTING ------------------------------------------------ */
-
-static void ggp_action_change_status_broadcasting_ok(PurpleConnection *gc, PurpleRequestFields *fields)
-{
- GGPInfo *info = gc->proto_data;
- int selected_field;
- PurpleAccount *account = purple_connection_get_account(gc);
- PurpleStatus *status;
-
- selected_field = purple_request_fields_get_choice(fields, "status_broadcasting");
-
- if (selected_field == 0)
- info->status_broadcasting = TRUE;
- else
- info->status_broadcasting = FALSE;
-
- status = purple_account_get_active_status(account);
-
- ggp_set_status(account, status);
-}
-
-static void ggp_action_change_status_broadcasting(PurplePluginAction *action)
-{
- PurpleConnection *gc = (PurpleConnection *)action->context;
- GGPInfo *info = gc->proto_data;
-
- PurpleRequestFields *fields;
- PurpleRequestFieldGroup *group;
- PurpleRequestField *field;
-
- fields = purple_request_fields_new();
- group = purple_request_field_group_new(NULL);
- purple_request_fields_add_group(fields, group);
-
- field = purple_request_field_choice_new("status_broadcasting", _("Show status to:"), 0);
- purple_request_field_choice_add(field, _("All people"));
- purple_request_field_choice_add(field, _("Only buddies"));
- purple_request_field_group_add_field(group, field);
-
- if (info->status_broadcasting)
- purple_request_field_choice_set_default_value(field, 0);
- else
- purple_request_field_choice_set_default_value(field, 1);
-
- purple_request_fields(gc,
- _("Change status broadcasting"),
- _("Change status broadcasting"),
- _("Please, select who can see your status"),
- fields,
- _("OK"), G_CALLBACK(ggp_action_change_status_broadcasting_ok),
- _("Cancel"), NULL,
- purple_connection_get_account(gc), NULL, NULL,
- gc);
-}
-
-/* ----- CONFERENCES ---------------------------------------------------- */
-
-static void ggp_callback_add_to_chat_ok(PurpleBuddy *buddy, PurpleRequestFields *fields)
-{
- PurpleConnection *conn;
- PurpleRequestField *field;
- GList *sel;
-
- conn = purple_account_get_connection(purple_buddy_get_account(buddy));
-
- g_return_if_fail(conn != NULL);
-
- field = purple_request_fields_get_field(fields, "name");
- sel = purple_request_field_list_get_selected(field);
-
- if (sel == NULL) {
- purple_debug_error("gg", "No chat selected\n");
- return;
- }
-
- ggp_confer_participants_add_uin(conn, sel->data,
- ggp_str_to_uin(purple_buddy_get_name(buddy)));
-}
-
-static void ggp_bmenu_add_to_chat(PurpleBlistNode *node, gpointer ignored)
-{
- PurpleBuddy *buddy;
- PurpleConnection *gc;
- GGPInfo *info;
-
- PurpleRequestFields *fields;
- PurpleRequestFieldGroup *group;
- PurpleRequestField *field;
-
- GList *l;
- gchar *msg;
-
- buddy = (PurpleBuddy *)node;
- gc = purple_account_get_connection(purple_buddy_get_account(buddy));
- info = gc->proto_data;
-
- fields = purple_request_fields_new();
- group = purple_request_field_group_new(NULL);
- purple_request_fields_add_group(fields, group);
-
- field = purple_request_field_list_new("name", "Chat name");
- for (l = info->chats; l != NULL; l = l->next) {
- GGPChat *chat = l->data;
- purple_request_field_list_add(field, chat->name, chat->name);
- }
- purple_request_field_group_add_field(group, field);
-
- msg = g_strdup_printf(_("Select a chat for buddy: %s"),
- purple_buddy_get_alias(buddy));
- purple_request_fields(gc,
- _("Add to chat..."),
- _("Add to chat..."),
- msg,
- fields,
- _("Add"), G_CALLBACK(ggp_callback_add_to_chat_ok),
- _("Cancel"), NULL,
- purple_connection_get_account(gc), NULL, NULL,
- buddy);
- g_free(msg);
+ FALSE, G_CALLBACK(ggp_callback_buddylist_load_ok), NULL,
+ purple_request_cpar_from_connection(gc), gc);
}
/* ----- BLOCK BUDDIES -------------------------------------------------- */
static void ggp_add_deny(PurpleConnection *gc, const char *who)
{
- GGPInfo *info = gc->proto_data;
+ GGPInfo *info = purple_connection_get_protocol_data(gc);
uin_t uin = ggp_str_to_uin(who);
-
+
purple_debug_info("gg", "ggp_add_deny: %u\n", uin);
-
+
gg_remove_notify_ex(info->session, uin, GG_USER_NORMAL);
gg_add_notify_ex(info->session, uin, GG_USER_BLOCKED);
}
static void ggp_rem_deny(PurpleConnection *gc, const char *who)
{
- GGPInfo *info = gc->proto_data;
+ GGPInfo *info = purple_connection_get_protocol_data(gc);
uin_t uin = ggp_str_to_uin(who);
-
+
purple_debug_info("gg", "ggp_rem_deny: %u\n", uin);
-
+
gg_remove_notify_ex(info->session, uin, GG_USER_BLOCKED);
gg_add_notify_ex(info->session, uin, GG_USER_NORMAL);
}
@@ -487,744 +221,14 @@ static void ggp_rem_deny(PurpleConnection *gc, const char *who)
/* ----- INTERNAL CALLBACKS --------------------------------------------- */
/* ---------------------------------------------------------------------- */
-#if !DISABLE_AVATARS
-
-struct gg_fetch_avatar_data
-{
- PurpleConnection *gc;
- gchar *uin;
- gchar *avatar_url;
-};
-
-
-static void gg_fetch_avatar_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
- const gchar *data, size_t len, const gchar *error_message) {
- struct gg_fetch_avatar_data *d = user_data;
- PurpleAccount *account;
- PurpleBuddy *buddy;
- gpointer buddy_icon_data;
-
- purple_debug_info("gg", "gg_fetch_avatar_cb: got avatar image for %s\n",
- d->uin);
-
- /* FIXME: This shouldn't be necessary */
- if (!PURPLE_CONNECTION_IS_VALID(d->gc)) {
- g_free(d->uin);
- g_free(d->avatar_url);
- g_free(d);
- g_return_if_reached();
- }
-
- account = purple_connection_get_account(d->gc);
- buddy = purple_find_buddy(account, d->uin);
-
- if (buddy == NULL)
- goto out;
-
- buddy_icon_data = g_memdup(data, len);
-
- purple_buddy_icons_set_for_user(account, purple_buddy_get_name(buddy),
- buddy_icon_data, len, d->avatar_url);
- purple_debug_info("gg", "gg_fetch_avatar_cb: UIN %s should have avatar "
- "now\n", d->uin);
-
-out:
- g_free(d->uin);
- g_free(d->avatar_url);
- g_free(d);
-}
-
-static void gg_get_avatar_url_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
- const gchar *url_text, size_t len, const gchar *error_message) {
- struct gg_fetch_avatar_data *data;
- PurpleConnection *gc = user_data;
- PurpleAccount *account;
- PurpleBuddy *buddy;
- const char *uin;
- const char *is_blank;
- const char *checksum;
-
- gchar *bigavatar = NULL;
- xmlnode *xml = NULL;
- xmlnode *xmlnode_users;
- xmlnode *xmlnode_user;
- xmlnode *xmlnode_avatars;
- xmlnode *xmlnode_avatar;
- xmlnode *xmlnode_bigavatar;
-
- g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc));
- account = purple_connection_get_account(gc);
-
- if (error_message != NULL)
- purple_debug_error("gg", "gg_get_avatars_cb error: %s\n", error_message);
- else if (len > 0 && url_text && *url_text) {
- xml = xmlnode_from_str(url_text, -1);
- if (xml == NULL)
- goto out;
-
- xmlnode_users = xmlnode_get_child(xml, "users");
- if (xmlnode_users == NULL)
- goto out;
-
- xmlnode_user = xmlnode_get_child(xmlnode_users, "user");
- if (xmlnode_user == NULL)
- goto out;
-
- uin = xmlnode_get_attrib(xmlnode_user, "uin");
-
- xmlnode_avatars = xmlnode_get_child(xmlnode_user, "avatars");
- if (xmlnode_avatars == NULL)
- goto out;
-
- xmlnode_avatar = xmlnode_get_child(xmlnode_avatars, "avatar");
- if (xmlnode_avatar == NULL)
- goto out;
-
- xmlnode_bigavatar = xmlnode_get_child(xmlnode_avatar, "originBigAvatar");
- if (xmlnode_bigavatar == NULL)
- goto out;
-
- is_blank = xmlnode_get_attrib(xmlnode_avatar, "blank");
- bigavatar = xmlnode_get_data(xmlnode_bigavatar);
-
- purple_debug_info("gg", "gg_get_avatar_url_cb: UIN %s, IS_BLANK %s, "
- "URL %s\n",
- uin ? uin : "(null)", is_blank ? is_blank : "(null)",
- bigavatar ? bigavatar : "(null)");
-
- if (uin != NULL && bigavatar != NULL) {
- buddy = purple_find_buddy(account, uin);
- if (buddy == NULL)
- goto out;
-
- checksum = purple_buddy_icons_get_checksum_for_user(buddy);
-
- if (purple_strequal(is_blank, "1")) {
- purple_buddy_icons_set_for_user(account,
- purple_buddy_get_name(buddy), NULL, 0, NULL);
- } else if (!purple_strequal(checksum, bigavatar)) {
- data = g_new0(struct gg_fetch_avatar_data, 1);
- data->gc = gc;
- data->uin = g_strdup(uin);
- data->avatar_url = g_strdup(bigavatar);
-
- purple_debug_info("gg", "gg_get_avatar_url_cb: "
- "requesting avatar for %s\n", uin);
- url_data = purple_util_fetch_url_request_len_with_account(account,
- bigavatar, TRUE, "Mozilla/4.0 (compatible; MSIE 5.0)",
- FALSE, NULL, FALSE, -1, gg_fetch_avatar_cb, data);
- }
- }
- }
-
-out:
- if (xml)
- xmlnode_free(xml);
- g_free(bigavatar);
-}
-
-#endif
-
-/**
- * Try to update avatar of the buddy.
- *
- * @param gc PurpleConnection
- * @param uin UIN of the buddy.
- */
-static void ggp_update_buddy_avatar(PurpleConnection *gc, uin_t uin)
-{
-#if DISABLE_AVATARS
- purple_debug_warning("gg", "ggp_update_buddy_avatar: disabled, please "
- "update to 3.0.0, when available\n");
-#else
- gchar *avatarurl;
- PurpleUtilFetchUrlData *url_data;
-
- purple_debug_info("gg", "ggp_update_buddy_avatar(gc, %u)\n", uin);
-
- avatarurl = g_strdup_printf("http://api.gadu-gadu.pl/avatars/%u/0.xml", uin);
-
- url_data = purple_util_fetch_url_request_len_with_account(
- purple_connection_get_account(gc), avatarurl, TRUE,
- "Mozilla/4.0 (compatible; MSIE 5.5)", FALSE, NULL, FALSE, -1,
- gg_get_avatar_url_cb, gc);
-
- g_free(avatarurl);
-#endif
-}
-
-/**
- * Handle change of the status of the buddy.
- *
- * @param gc PurpleConnection
- * @param uin UIN of the buddy.
- * @param status ID of the status.
- * @param descr Description.
- */
-static void ggp_generic_status_handler(PurpleConnection *gc, uin_t uin,
- int status, const char *descr)
-{
- gchar *from;
- const char *st;
- char *status_msg = NULL;
-
- ggp_update_buddy_avatar(gc, uin);
-
- from = g_strdup_printf("%u", uin);
-
- switch (status) {
- case GG_STATUS_NOT_AVAIL:
- case GG_STATUS_NOT_AVAIL_DESCR:
- st = purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE);
- break;
- case GG_STATUS_FFC:
- case GG_STATUS_FFC_DESCR:
- st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE);
- break;
- case GG_STATUS_AVAIL:
- case GG_STATUS_AVAIL_DESCR:
- st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE);
- break;
- case GG_STATUS_BUSY:
- case GG_STATUS_BUSY_DESCR:
- st = purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY);
- break;
- case GG_STATUS_DND:
- case GG_STATUS_DND_DESCR:
- st = purple_primitive_get_id_from_type(PURPLE_STATUS_UNAVAILABLE);
- break;
- case GG_STATUS_BLOCKED:
- /* user is blocking us.... */
- st = "blocked";
- break;
- default:
- st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE);
- purple_debug_info("gg",
- "GG_EVENT_NOTIFY: Unknown status: %d\n", status);
- break;
- }
-
- if (descr != NULL) {
- status_msg = g_strdup(descr);
- g_strstrip(status_msg);
- if (status_msg[0] == '\0') {
- g_free(status_msg);
- status_msg = NULL;
- }
- }
-
- purple_debug_info("gg", "status of %u is %s [%s]\n", uin, st,
- status_msg ? status_msg : "");
- if (status_msg == NULL) {
- purple_prpl_got_user_status(purple_connection_get_account(gc),
- from, st, NULL);
- } else {
- purple_prpl_got_user_status(purple_connection_get_account(gc),
- from, st, "message", status_msg, NULL);
- g_free(status_msg);
- }
- g_free(from);
-}
-
-static void ggp_sr_close_cb(gpointer user_data)
-{
- GGPSearchForm *form = user_data;
- GGPInfo *info = form->user_data;
-
- ggp_search_remove(info->searches, form->seq);
- purple_debug_info("gg", "ggp_sr_close_cb(): Removed seq %u\n",
- form->seq);
- ggp_search_form_destroy(form);
-}
-
-/**
- * Translate a status' ID to a more user-friendly name.
- *
- * @param id The ID of the status.
- *
- * @return The user-friendly name of the status.
- */
-static const char *ggp_status_by_id(unsigned int id)
-{
- const char *st;
-
- purple_debug_info("gg", "ggp_status_by_id: %d\n", id);
- switch (id) {
- case GG_STATUS_NOT_AVAIL:
- case GG_STATUS_NOT_AVAIL_DESCR:
- st = _("Offline");
- break;
- case GG_STATUS_AVAIL:
- case GG_STATUS_AVAIL_DESCR:
- st = _("Available");
- break;
- case GG_STATUS_FFC:
- case GG_STATUS_FFC_DESCR:
- return _("Chatty");
- case GG_STATUS_DND:
- case GG_STATUS_DND_DESCR:
- return _("Do Not Disturb");
- case GG_STATUS_BUSY:
- case GG_STATUS_BUSY_DESCR:
- st = _("Away");
- break;
- default:
- st = _("Unknown");
- break;
- }
-
- return st;
-}
-
-static void ggp_pubdir_handle_info(PurpleConnection *gc, gg_pubdir50_t req,
- GGPSearchForm *form)
-{
- PurpleNotifyUserInfo *user_info;
- PurpleBuddy *buddy;
- char *val, *who;
-
- user_info = purple_notify_user_info_new();
-
- val = ggp_search_get_result(req, 0, GG_PUBDIR50_STATUS);
- /* XXX: Use of ggp_str_to_uin() is an ugly hack! */
- purple_notify_user_info_add_pair(user_info, _("Status"), ggp_status_by_id(ggp_str_to_uin(val)));
- g_free(val);
-
- who = ggp_search_get_result(req, 0, GG_PUBDIR50_UIN);
- purple_notify_user_info_add_pair(user_info, _("UIN"), who);
-
- val = ggp_search_get_result(req, 0, GG_PUBDIR50_FIRSTNAME);
- purple_notify_user_info_add_pair(user_info, _("First Name"), val);
- g_free(val);
-
- val = ggp_search_get_result(req, 0, GG_PUBDIR50_NICKNAME);
- purple_notify_user_info_add_pair(user_info, _("Nickname"), val);
- g_free(val);
-
- val = ggp_search_get_result(req, 0, GG_PUBDIR50_CITY);
- purple_notify_user_info_add_pair(user_info, _("City"), val);
- g_free(val);
-
- val = ggp_search_get_result(req, 0, GG_PUBDIR50_BIRTHYEAR);
- if (strncmp(val, "0", 1)) {
- purple_notify_user_info_add_pair(user_info, _("Birth Year"), val);
- }
- g_free(val);
-
- /*
- * Include a status message, if exists and buddy is in the blist.
- */
- buddy = purple_find_buddy(purple_connection_get_account(gc), who);
- if (NULL != buddy) {
- PurpleStatus *status;
- const char *msg;
- char *text;
-
- status = purple_presence_get_active_status(purple_buddy_get_presence(buddy));
- msg = purple_status_get_attr_string(status, "message");
-
- if (msg != NULL) {
- text = g_markup_escape_text(msg, -1);
- purple_notify_user_info_add_pair(user_info, _("Message"), text);
- g_free(text);
- }
- }
-
- purple_notify_userinfo(gc, who, user_info, ggp_sr_close_cb, form);
- g_free(who);
- purple_notify_user_info_destroy(user_info);
-}
-
-static void ggp_pubdir_handle_full(PurpleConnection *gc, gg_pubdir50_t req,
- GGPSearchForm *form)
-{
- PurpleNotifySearchResults *results;
- PurpleNotifySearchColumn *column;
- int res_count;
- int start;
- int i;
-
- g_return_if_fail(form != NULL);
-
- res_count = gg_pubdir50_count(req);
- res_count = (res_count > PUBDIR_RESULTS_MAX) ? PUBDIR_RESULTS_MAX : res_count;
- if (form->page_size == 0)
- form->page_size = res_count;
-
- results = purple_notify_searchresults_new();
-
- if (results == NULL) {
- purple_debug_error("gg", "ggp_pubdir_reply_handler: "
- "Unable to display the search results.\n");
- purple_notify_error(gc, NULL,
- _("Unable to display the search results."),
- NULL);
- if (form->window == NULL)
- ggp_sr_close_cb(form);
- return;
- }
-
- column = purple_notify_searchresults_column_new(_("UIN"));
- purple_notify_searchresults_column_add(results, column);
-
- column = purple_notify_searchresults_column_new(_("First Name"));
- purple_notify_searchresults_column_add(results, column);
-
- column = purple_notify_searchresults_column_new(_("Nickname"));
- purple_notify_searchresults_column_add(results, column);
-
- column = purple_notify_searchresults_column_new(_("City"));
- purple_notify_searchresults_column_add(results, column);
-
- column = purple_notify_searchresults_column_new(_("Birth Year"));
- purple_notify_searchresults_column_add(results, column);
-
- purple_debug_info("gg", "Going with %d entries\n", res_count);
-
- start = (int)ggp_str_to_uin(gg_pubdir50_get(req, 0, GG_PUBDIR50_START));
- purple_debug_info("gg", "start = %d\n", start);
-
- for (i = 0; i < res_count; i++) {
- GList *row = NULL;
- char *birth = ggp_search_get_result(req, i, GG_PUBDIR50_BIRTHYEAR);
-
- /* TODO: Status will be displayed as an icon. */
- /* row = g_list_append(row, ggp_search_get_result(req, i, GG_PUBDIR50_STATUS)); */
- row = g_list_append(row, ggp_search_get_result(req, i,
- GG_PUBDIR50_UIN));
- row = g_list_append(row, ggp_search_get_result(req, i,
- GG_PUBDIR50_FIRSTNAME));
- row = g_list_append(row, ggp_search_get_result(req, i,
- GG_PUBDIR50_NICKNAME));
- row = g_list_append(row, ggp_search_get_result(req, i,
- GG_PUBDIR50_CITY));
- row = g_list_append(row,
- (birth && strncmp(birth, "0", 1)) ? birth : g_strdup("-"));
-
- purple_notify_searchresults_row_add(results, row);
- }
-
- purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_CONTINUE,
- ggp_callback_show_next);
- purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_ADD,
- ggp_callback_add_buddy);
- purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_IM,
- ggp_callback_im);
-
- if (form->window == NULL) {
- void *h = purple_notify_searchresults(gc,
- _("Gadu-Gadu Public Directory"),
- _("Search results"), NULL, results,
- (PurpleNotifyCloseCallback)ggp_sr_close_cb,
- form);
-
- if (h == NULL) {
- purple_debug_error("gg", "ggp_pubdir_reply_handler: "
- "Unable to display the search results.\n");
- purple_notify_error(gc, NULL,
- _("Unable to display the search results."),
- NULL);
- return;
- }
-
- form->window = h;
- } else {
- purple_notify_searchresults_new_rows(gc, results, form->window);
- }
-}
-
-static void ggp_pubdir_reply_handler(PurpleConnection *gc, gg_pubdir50_t req)
-{
- GGPInfo *info = gc->proto_data;
- GGPSearchForm *form;
- int res_count;
- guint32 seq;
-
- seq = gg_pubdir50_seq(req);
- form = ggp_search_get(info->searches, seq);
- purple_debug_info("gg",
- "ggp_pubdir_reply_handler(): seq %u --> form %p\n", seq, form);
- /*
- * this can happen when user will request more results
- * and close the results window before they arrive.
- */
- g_return_if_fail(form != NULL);
-
- res_count = gg_pubdir50_count(req);
- if (res_count < 1) {
- purple_debug_info("gg", "GG_EVENT_PUBDIR50_SEARCH_REPLY: Nothing found\n");
- purple_notify_error(gc, NULL,
- _("No matching users found"),
- _("There are no users matching your search criteria."));
- if (form->window == NULL)
- ggp_sr_close_cb(form);
- return;
- }
-
- switch (form->search_type) {
- case GGP_SEARCH_TYPE_INFO:
- ggp_pubdir_handle_info(gc, req, form);
- break;
- case GGP_SEARCH_TYPE_FULL:
- ggp_pubdir_handle_full(gc, req, form);
- break;
- default:
- purple_debug_warning("gg", "Unknown search_type!\n");
- break;
- }
-}
-
-static void ggp_recv_image_handler(PurpleConnection *gc, const struct gg_event *ev)
-{
- gint imgid = 0;
- GGPInfo *info = gc->proto_data;
- GList *entry = g_list_first(info->pending_richtext_messages);
- gchar *handlerid = g_strdup_printf("IMGID_HANDLER-%i", ev->event.image_reply.crc32);
-
- imgid = purple_imgstore_add_with_id(
- g_memdup(ev->event.image_reply.image, ev->event.image_reply.size),
- ev->event.image_reply.size,
- ev->event.image_reply.filename);
-
- purple_debug_info("gg", "ggp_recv_image_handler: got image with crc32: %u\n", ev->event.image_reply.crc32);
-
- while(entry) {
- if (strstr((gchar *)entry->data, handlerid) != NULL) {
- gchar **split = g_strsplit((gchar *)entry->data, handlerid, 3);
- gchar *text = g_strdup_printf("%s%i%s", split[0], imgid, split[1]);
- purple_debug_info("gg", "ggp_recv_image_handler: found message matching crc32: %s\n", (gchar *)entry->data);
- g_strfreev(split);
- info->pending_richtext_messages = g_list_remove(info->pending_richtext_messages, entry->data);
- /* We don't have any more images to download */
- if (strstr(text, "<IMG ID=\"IMGID_HANDLER") == NULL) {
- gchar *buf = g_strdup_printf("%lu", (unsigned long int)ev->event.image_reply.sender);
- serv_got_im(gc, buf, text, PURPLE_MESSAGE_IMAGES, time(NULL));
- g_free(buf);
- purple_debug_info("gg", "ggp_recv_image_handler: richtext message: %s\n", text);
- g_free(text);
- break;
- }
- info->pending_richtext_messages = g_list_append(info->pending_richtext_messages, text);
- break;
- }
- entry = g_list_next(entry);
- }
- g_free(handlerid);
-
- return;
-}
-
-
-/**
- * Dispatch a message received from a buddy.
- *
- * @param gc PurpleConnection.
- * @param ev Gadu-Gadu event structure.
- *
- * Image receiving, some code borrowed from Kadu http://www.kadu.net
- */
-static void ggp_recv_message_handler(PurpleConnection *gc, const struct gg_event *ev)
-{
- GGPInfo *info = gc->proto_data;
- PurpleConversation *conv;
- gchar *from;
- gchar *msg;
- gchar *tmp;
-
- if (ev->event.msg.message == NULL)
- {
- purple_debug_warning("gg", "ggp_recv_message_handler: NULL as message pointer\n");
- return;
- }
-
- from = g_strdup_printf("%lu", (unsigned long int)ev->event.msg.sender);
-
- /*
- tmp = charset_convert((const char *)ev->event.msg.message,
- "CP1250", "UTF-8");
- */
- tmp = g_strdup_printf("%s", ev->event.msg.message);
- purple_str_strip_char(tmp, '\r');
- msg = g_markup_escape_text(tmp, -1);
- g_free(tmp);
-
- /* We got richtext message */
- if (ev->event.msg.formats_length)
- {
- gboolean got_image = FALSE, bold = FALSE, italic = FALSE, under = FALSE;
- char *cformats = (char *)ev->event.msg.formats;
- char *cformats_end = cformats + ev->event.msg.formats_length;
- gint increased_len = 0;
- struct gg_msg_richtext_format *actformat;
- struct gg_msg_richtext_image *actimage;
- GString *message = g_string_new(msg);
- gchar *handlerid;
-
- purple_debug_info("gg", "ggp_recv_message_handler: richtext msg from (%s): %s %i formats\n", from, msg, ev->event.msg.formats_length);
-
- while (cformats < cformats_end)
- {
- gint byteoffset;
- actformat = (struct gg_msg_richtext_format *)cformats;
- cformats += sizeof(struct gg_msg_richtext_format);
- byteoffset = g_utf8_offset_to_pointer(message->str, actformat->position + increased_len) - message->str;
-
- if(actformat->position == 0 && actformat->font == 0) {
- purple_debug_warning("gg", "ggp_recv_message_handler: bogus formatting (inc: %i)\n", increased_len);
- continue;
- }
- purple_debug_info("gg", "ggp_recv_message_handler: format at pos: %i, image:%i, bold:%i, italic: %i, under:%i (inc: %i)\n",
- actformat->position,
- (actformat->font & GG_FONT_IMAGE) != 0,
- (actformat->font & GG_FONT_BOLD) != 0,
- (actformat->font & GG_FONT_ITALIC) != 0,
- (actformat->font & GG_FONT_UNDERLINE) != 0,
- increased_len);
-
- if (actformat->font & GG_FONT_IMAGE) {
- got_image = TRUE;
- actimage = (struct gg_msg_richtext_image*)(cformats);
- cformats += sizeof(struct gg_msg_richtext_image);
- purple_debug_info("gg", "ggp_recv_message_handler: image received, size: %d, crc32: %i\n", actimage->size, actimage->crc32);
-
- /* Checking for errors, image size shouldn't be
- * larger than 255.000 bytes */
- if (actimage->size > 255000) {
- purple_debug_warning("gg", "ggp_recv_message_handler: received image large than 255 kb\n");
- continue;
- }
-
- gg_image_request(info->session, ev->event.msg.sender,
- actimage->size, actimage->crc32);
-
- handlerid = g_strdup_printf("<IMG ID=\"IMGID_HANDLER-%i\">", actimage->crc32);
- g_string_insert(message, byteoffset, handlerid);
- increased_len += strlen(handlerid);
- g_free(handlerid);
- continue;
- }
-
- if (actformat->font & GG_FONT_BOLD) {
- if (bold == FALSE) {
- g_string_insert(message, byteoffset, "<b>");
- increased_len += 3;
- bold = TRUE;
- }
- } else if (bold) {
- g_string_insert(message, byteoffset, "</b>");
- increased_len += 4;
- bold = FALSE;
- }
-
- if (actformat->font & GG_FONT_ITALIC) {
- if (italic == FALSE) {
- g_string_insert(message, byteoffset, "<i>");
- increased_len += 3;
- italic = TRUE;
- }
- } else if (italic) {
- g_string_insert(message, byteoffset, "</i>");
- increased_len += 4;
- italic = FALSE;
- }
-
- if (actformat->font & GG_FONT_UNDERLINE) {
- if (under == FALSE) {
- g_string_insert(message, byteoffset, "<u>");
- increased_len += 3;
- under = TRUE;
- }
- } else if (under) {
- g_string_insert(message, byteoffset, "</u>");
- increased_len += 4;
- under = FALSE;
- }
-
- if (actformat->font & GG_FONT_COLOR) {
- cformats += sizeof(struct gg_msg_richtext_color);
- }
- }
-
- msg = message->str;
- g_string_free(message, FALSE);
-
- if (got_image) {
- info->pending_richtext_messages = g_list_append(info->pending_richtext_messages, msg);
- return;
- }
- }
-
- purple_debug_info("gg", "ggp_recv_message_handler: msg from (%s): %s (class = %d; rcpt_count = %d)\n",
- from, msg, ev->event.msg.msgclass,
- ev->event.msg.recipients_count);
-
- if (ev->event.msg.recipients_count == 0) {
- serv_got_im(gc, from, msg, 0, ev->event.msg.time);
- } else {
- const char *chat_name;
- int chat_id;
- char *buddy_name;
-
- chat_name = ggp_confer_find_by_participants(gc,
- ev->event.msg.recipients,
- ev->event.msg.recipients_count);
-
- if (chat_name == NULL) {
- chat_name = ggp_confer_add_new(gc, NULL);
- serv_got_joined_chat(gc, info->chats_count, chat_name);
-
- ggp_confer_participants_add_uin(gc, chat_name,
- ev->event.msg.sender);
-
- ggp_confer_participants_add(gc, chat_name,
- ev->event.msg.recipients,
- ev->event.msg.recipients_count);
- }
- conv = ggp_confer_find_by_name(gc, chat_name);
- chat_id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv));
-
- buddy_name = ggp_buddy_get_name(gc, ev->event.msg.sender);
- serv_got_chat_in(gc, chat_id, buddy_name,
- PURPLE_MESSAGE_RECV, msg, ev->event.msg.time);
- g_free(buddy_name);
- }
- g_free(msg);
- g_free(from);
-}
-
-static void ggp_send_image_handler(PurpleConnection *gc, const struct gg_event *ev)
-{
- GGPInfo *info = gc->proto_data;
- PurpleStoredImage *image;
- gint imgid = GPOINTER_TO_INT(g_hash_table_lookup(info->pending_images, GINT_TO_POINTER(ev->event.image_request.crc32)));
-
- purple_debug_info("gg", "ggp_send_image_handler: image request received, crc32: %u, imgid: %d\n", ev->event.image_request.crc32, imgid);
-
- if(imgid)
- {
- if((image = purple_imgstore_find_by_id(imgid))) {
- gint image_size = purple_imgstore_get_size(image);
- gconstpointer image_bin = purple_imgstore_get_data(image);
- const char *image_filename = purple_imgstore_get_filename(image);
-
- purple_debug_info("gg", "ggp_send_image_handler: sending image imgid: %i, crc: %u\n", imgid, ev->event.image_request.crc32);
- gg_image_reply(info->session, (unsigned long int)ev->event.image_request.sender, image_filename, image_bin, image_size);
- purple_imgstore_unref(image);
- } else {
- purple_debug_error("gg", "ggp_send_image_handler: image imgid: %i, crc: %u in hash but not found in imgstore!\n", imgid, ev->event.image_request.crc32);
- }
- g_hash_table_remove(info->pending_images, GINT_TO_POINTER(ev->event.image_request.crc32));
- }
-}
-
static void ggp_typing_notification_handler(PurpleConnection *gc, uin_t uin, int length) {
gchar *from;
from = g_strdup_printf("%u", uin);
if (length)
- serv_got_typing(gc, from, 0, PURPLE_TYPING);
+ purple_serv_got_typing(gc, from, 0, PURPLE_IM_TYPING);
else
- serv_got_typing_stopped(gc, from);
+ purple_serv_got_typing_stopped(gc, from);
g_free(from);
}
@@ -1235,55 +239,56 @@ static void ggp_typing_notification_handler(PurpleConnection *gc, uin_t uin, int
* @param data Raw XML contents.
*
* @see http://toxygen.net/libgadu/protocol/#ch1.13
+ * @todo: this may not be necessary anymore
*/
static void ggp_xml_event_handler(PurpleConnection *gc, char *data)
{
- xmlnode *xml = NULL;
- xmlnode *xmlnode_next_event;
+ PurpleXmlNode *xml = NULL;
+ PurpleXmlNode *xmlnode_next_event;
- xml = xmlnode_from_str(data, -1);
- if (xml == NULL)
+ xml = purple_xmlnode_from_str(data, -1);
+ if (xml == NULL) {
+ purple_debug_error("gg", "ggp_xml_event_handler: "
+ "invalid xml: [%s]\n", data);
goto out;
+ }
+
+ xmlnode_next_event = purple_xmlnode_get_child(xml, "event");
+ while (xmlnode_next_event != NULL) {
+ PurpleXmlNode *xmlnode_current_event = xmlnode_next_event;
- xmlnode_next_event = xmlnode_get_child(xml, "event");
- while (xmlnode_next_event != NULL)
- {
- xmlnode *xmlnode_current_event = xmlnode_next_event;
-
- xmlnode *xmlnode_type;
+ PurpleXmlNode *xmlnode_type;
char *event_type_raw;
int event_type = 0;
-
- xmlnode *xmlnode_sender;
+
+ PurpleXmlNode *xmlnode_sender;
char *event_sender_raw;
uin_t event_sender = 0;
- xmlnode_next_event = xmlnode_get_next_twin(xmlnode_next_event);
-
- xmlnode_type = xmlnode_get_child(xmlnode_current_event, "type");
+ xmlnode_next_event = purple_xmlnode_get_next_twin(xmlnode_next_event);
+
+ xmlnode_type = purple_xmlnode_get_child(xmlnode_current_event, "type");
if (xmlnode_type == NULL)
continue;
- event_type_raw = xmlnode_get_data(xmlnode_type);
+ event_type_raw = purple_xmlnode_get_data(xmlnode_type);
if (event_type_raw != NULL)
event_type = atoi(event_type_raw);
g_free(event_type_raw);
-
- xmlnode_sender = xmlnode_get_child(xmlnode_current_event, "sender");
- if (xmlnode_sender != NULL)
- {
- event_sender_raw = xmlnode_get_data(xmlnode_sender);
+
+ xmlnode_sender = purple_xmlnode_get_child(xmlnode_current_event, "sender");
+ if (xmlnode_sender != NULL) {
+ event_sender_raw = purple_xmlnode_get_data(xmlnode_sender);
if (event_sender_raw != NULL)
event_sender = ggp_str_to_uin(event_sender_raw);
g_free(event_sender_raw);
}
-
+
switch (event_type)
{
case 28: /* avatar update */
purple_debug_info("gg",
"ggp_xml_event_handler: avatar updated (uid: %u)\n",
event_sender);
- ggp_update_buddy_avatar(gc, event_sender);
break;
default:
purple_debug_error("gg",
@@ -1291,30 +296,36 @@ static void ggp_xml_event_handler(PurpleConnection *gc, char *data)
event_type, event_sender);
}
}
-
+
out:
if (xml)
- xmlnode_free(xml);
+ purple_xmlnode_free(xml);
}
static void ggp_callback_recv(gpointer _gc, gint fd, PurpleInputCondition cond)
{
PurpleConnection *gc = _gc;
- GGPInfo *info = gc->proto_data;
+ GGPInfo *info = purple_connection_get_protocol_data(gc);
struct gg_event *ev;
- int i;
if (!(ev = gg_watch_fd(info->session))) {
purple_debug_error("gg",
"ggp_callback_recv: gg_watch_fd failed -- CRITICAL!\n");
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to read from socket"));
return;
}
- purple_input_remove(gc->inpa);
- gc->inpa = purple_input_add(info->session->fd,
+#if GGP_ENABLE_GG11
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("gg", "ggp_callback_recv: got event %s",
+ gg_debug_event(ev->type));
+ }
+#endif
+
+ purple_input_remove(info->inpa);
+ info->inpa = purple_input_add(info->session->fd,
ggp_tcpsocket_inputcond_gg_to_purple(info->session->check),
ggp_callback_recv, gc);
@@ -1322,82 +333,28 @@ static void ggp_callback_recv(gpointer _gc, gint fd, PurpleInputCondition cond)
case GG_EVENT_NONE:
/* Nothing happened. */
break;
+ case GG_EVENT_CONN_FAILED:
+ purple_connection_error (gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Server disconnected"));
+ break;
case GG_EVENT_MSG:
- ggp_recv_message_handler(gc, ev);
+ ggp_message_got(gc, &ev->event.msg);
break;
case GG_EVENT_ACK:
- /* Changing %u to %i fixes compiler warning */
- purple_debug_info("gg",
- "ggp_callback_recv: message sent to: %i, delivery status=%d, seq=%d\n",
- ev->event.ack.recipient, ev->event.ack.status,
- ev->event.ack.seq);
+#if GGP_ENABLE_GG11
+ case GG_EVENT_ACK110:
+#endif
break;
case GG_EVENT_IMAGE_REPLY:
- ggp_recv_image_handler(gc, ev);
+ ggp_image_recv(gc, &ev->event.image_reply);
break;
case GG_EVENT_IMAGE_REQUEST:
- ggp_send_image_handler(gc, ev);
- break;
- case GG_EVENT_NOTIFY:
- case GG_EVENT_NOTIFY_DESCR:
- {
- struct gg_notify_reply *n;
- char *descr;
-
- purple_debug_info("gg", "notify_pre: (%d) status: %d\n",
- ev->event.notify->uin,
- GG_S(ev->event.notify->status));
-
- n = (ev->type == GG_EVENT_NOTIFY) ? ev->event.notify
- : ev->event.notify_descr.notify;
-
- for (; n->uin; n++) {
- descr = (ev->type == GG_EVENT_NOTIFY) ? NULL
- : ev->event.notify_descr.descr;
-
- purple_debug_info("gg",
- "notify: (%d) status: %d; descr: %s\n",
- n->uin, GG_S(n->status), descr ? descr : "(null)");
-
- ggp_generic_status_handler(gc,
- n->uin, GG_S(n->status), descr);
- }
- }
+ ggp_image_send(gc, &ev->event.image_request);
break;
case GG_EVENT_NOTIFY60:
- for (i = 0; ev->event.notify60[i].uin; i++) {
- purple_debug_info("gg",
- "notify60: (%d) status=%d; version=%d; descr=%s\n",
- ev->event.notify60[i].uin,
- GG_S(ev->event.notify60[i].status),
- ev->event.notify60[i].version,
- ev->event.notify60[i].descr ? ev->event.notify60[i].descr : "(null)");
-
- ggp_generic_status_handler(gc, ev->event.notify60[i].uin,
- GG_S(ev->event.notify60[i].status),
- ev->event.notify60[i].descr);
- }
- break;
- case GG_EVENT_STATUS:
- purple_debug_info("gg", "status: (%d) status=%d; descr=%s\n",
- ev->event.status.uin, GG_S(ev->event.status.status),
- ev->event.status.descr ? ev->event.status.descr : "(null)");
-
- ggp_generic_status_handler(gc, ev->event.status.uin,
- GG_S(ev->event.status.status), ev->event.status.descr);
- break;
case GG_EVENT_STATUS60:
- purple_debug_info("gg",
- "status60: (%d) status=%d; version=%d; descr=%s\n",
- ev->event.status60.uin, GG_S(ev->event.status60.status),
- ev->event.status60.version,
- ev->event.status60.descr ? ev->event.status60.descr : "(null)");
-
- ggp_generic_status_handler(gc, ev->event.status60.uin,
- GG_S(ev->event.status60.status), ev->event.status60.descr);
- break;
- case GG_EVENT_PUBDIR50_SEARCH_REPLY:
- ggp_pubdir_reply_handler(gc, ev->event.pubdir50);
+ ggp_status_got_others(gc, ev);
break;
case GG_EVENT_TYPING_NOTIFICATION:
ggp_typing_notification_handler(gc, ev->event.typing_notification.uin,
@@ -1407,8 +364,49 @@ static void ggp_callback_recv(gpointer _gc, gint fd, PurpleInputCondition cond)
purple_debug_info("gg", "GG_EVENT_XML_EVENT\n");
ggp_xml_event_handler(gc, ev->event.xml_event.data);
break;
+ case GG_EVENT_USER_DATA:
+ ggp_events_user_data(gc, &ev->event.user_data);
+ break;
+#if GGP_ENABLE_GG11
+ case GG_EVENT_JSON_EVENT:
+ ggp_events_json(gc, &ev->event.json_event);
+ break;
+#endif
+ case GG_EVENT_USERLIST100_VERSION:
+ ggp_roster_version(gc, &ev->event.userlist100_version);
+ break;
+ case GG_EVENT_USERLIST100_REPLY:
+ ggp_roster_reply(gc, &ev->event.userlist100_reply);
+ break;
+ case GG_EVENT_MULTILOGON_MSG:
+ ggp_message_got_multilogon(gc, &ev->event.multilogon_msg);
+ break;
+ case GG_EVENT_MULTILOGON_INFO:
+ ggp_multilogon_info(gc, &ev->event.multilogon_info);
+ break;
+#if GGP_ENABLE_GG11
+ case GG_EVENT_IMTOKEN:
+ purple_debug_info("gg", "gg11: got IMTOKEN\n");
+ g_free(info->imtoken);
+ info->imtoken = g_strdup(ev->event.imtoken.imtoken);
+ break;
+ case GG_EVENT_PONG110:
+ purple_debug_info("gg", "gg11: got PONG110 %lu\n",
+ (long unsigned)ev->event.pong110.time);
+ break;
+ case GG_EVENT_CHAT_INFO:
+ case GG_EVENT_CHAT_INFO_GOT_ALL:
+ case GG_EVENT_CHAT_INFO_UPDATE:
+ case GG_EVENT_CHAT_CREATED:
+ case GG_EVENT_CHAT_INVITE_ACK:
+ ggp_chat_got_event(gc, ev);
+ break;
+#endif
+ case GG_EVENT_DISCONNECT:
+ ggp_servconn_remote_disconnect(gc);
+ break;
default:
- purple_debug_error("gg",
+ purple_debug_warning("gg",
"unsupported event type=%d\n", ev->type);
break;
}
@@ -1416,20 +414,23 @@ static void ggp_callback_recv(gpointer _gc, gint fd, PurpleInputCondition cond)
gg_free_event(ev);
}
-static void ggp_async_login_handler(gpointer _gc, gint fd, PurpleInputCondition cond)
+void ggp_async_login_handler(gpointer _gc, gint fd, PurpleInputCondition cond)
{
PurpleConnection *gc = _gc;
GGPInfo *info;
struct gg_event *ev;
- g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc));
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
- info = gc->proto_data;
+ info = purple_connection_get_protocol_data(gc);
purple_debug_info("gg", "login_handler: session: check = %d; state = %d;\n",
info->session->check, info->session->state);
switch (info->session->state) {
+ case GG_STATE_ERROR:
+ purple_debug_info("gg", "GG_STATE_ERROR\n");
+ break;
case GG_STATE_RESOLVING:
purple_debug_info("gg", "GG_STATE_RESOLVING\n");
break;
@@ -1454,15 +455,23 @@ static void ggp_async_login_handler(gpointer _gc, gint fd, PurpleInputCondition
case GG_STATE_TLS_NEGOTIATION:
purple_debug_info("gg", "GG_STATE_TLS_NEGOTIATION\n");
break;
+#if GGP_ENABLE_GG11
+ case GG_STATE_RESOLVING_HUB:
+ purple_debug_info("gg", "GG_STATE_RESOLVING_HUB\n");
+ break;
+ case GG_STATE_READING_HUB:
+ purple_debug_info("gg", "GG_STATE_READING_HUB\n");
+ break;
+#endif
default:
purple_debug_error("gg", "unknown state = %d\n",
- info->session->state);
+ info->session->state);
break;
}
if (!(ev = gg_watch_fd(info->session))) {
purple_debug_error("gg", "login_handler: gg_watch_fd failed!\n");
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to read from socket"));
return;
@@ -1471,12 +480,14 @@ static void ggp_async_login_handler(gpointer _gc, gint fd, PurpleInputCondition
purple_debug_info("gg", "login_handler: session: check = %d; state = %d;\n",
info->session->check, info->session->state);
- purple_input_remove(gc->inpa);
+ purple_input_remove(info->inpa);
+ info->inpa = 0;
/** XXX I think that this shouldn't be done if ev->type is GG_EVENT_CONN_FAILED or GG_EVENT_CONN_SUCCESS -datallah */
if (info->session->fd >= 0)
- gc->inpa = purple_input_add(info->session->fd,
- ggp_tcpsocket_inputcond_gg_to_purple(info->session->check),
+ info->inpa = purple_input_add(info->session->fd,
+ (info->session->check == 1) ? PURPLE_INPUT_WRITE :
+ PURPLE_INPUT_READ,
ggp_async_login_handler, gc);
switch (ev->type) {
@@ -1486,34 +497,102 @@ static void ggp_async_login_handler(gpointer _gc, gint fd, PurpleInputCondition
break;
case GG_EVENT_CONN_SUCCESS:
{
- purple_debug_info("gg", "GG_EVENT_CONN_SUCCESS\n");
- purple_input_remove(gc->inpa);
- gc->inpa = purple_input_add(info->session->fd,
- ggp_tcpsocket_inputcond_gg_to_purple(info->session->check),
+#if GGP_ENABLE_GG11
+ purple_debug_info("gg", "GG_EVENT_CONN_SUCCESS:"
+ " successfully connected to %s\n",
+ info->session->connect_host);
+ ggp_servconn_add_server(info->session->
+ connect_host);
+#endif
+ purple_input_remove(info->inpa);
+ info->inpa = purple_input_add(info->session->fd,
+ PURPLE_INPUT_READ,
ggp_callback_recv, gc);
- ggp_buddylist_send(gc);
purple_connection_update_progress(gc, _("Connected"), 1, 2);
- purple_connection_set_state(gc, PURPLE_CONNECTED);
+ purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
+
+ ggp_buddylist_send(gc);
+ ggp_roster_request_update(gc);
}
break;
case GG_EVENT_CONN_FAILED:
- purple_input_remove(gc->inpa);
- gc->inpa = 0;
- purple_connection_error_reason (gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Connection failed"));
+ if (info->inpa > 0) {
+ purple_input_remove(info->inpa);
+ info->inpa = 0;
+ }
+ purple_debug_info("gg", "Connection failure: %d\n",
+ ev->event.failure);
+ switch (ev->event.failure) {
+ case GG_FAILURE_RESOLVING:
+ purple_connection_error(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Unable to resolve "
+ "hostname"));
+ break;
+ case GG_FAILURE_PASSWORD:
+ purple_connection_error(gc,
+ PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
+ _("Incorrect password"));
+ break;
+ case GG_FAILURE_TLS:
+ purple_connection_error(gc,
+ PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
+ _("SSL Connection Failed"));
+ break;
+ case GG_FAILURE_INTRUDER:
+ purple_connection_error(gc,
+ PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
+ _("Your account has been "
+ "disabled because too many "
+ "incorrect passwords were "
+ "entered"));
+ break;
+ case GG_FAILURE_UNAVAILABLE:
+ purple_connection_error(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Service temporarily "
+ "unavailable"));
+ break;
+ case GG_FAILURE_PROXY:
+ purple_connection_error(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Error connecting to proxy "
+ "server"));
+ break;
+ case GG_FAILURE_HUB:
+ purple_connection_error(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Error connecting to master "
+ "server"));
+ break;
+#if GGP_ENABLE_GG11
+ case GG_FAILURE_INTERNAL:
+ purple_connection_error(gc,
+ PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+ _("Internal error"));
+ break;
+#endif
+ default:
+ purple_connection_error(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Connection failed"));
+ }
break;
case GG_EVENT_MSG:
- if (ev->event.msg.sender == 0)
+ if (ev->event.msg.sender == 0) {
+ if (ev->event.msg.message == NULL)
+ break;
+
/* system messages are mostly ads */
purple_debug_info("gg", "System message:\n%s\n",
ev->event.msg.message);
- else
+ } else {
purple_debug_warning("gg", "GG_EVENT_MSG: message from user %u "
"unexpected while connecting:\n%s\n",
ev->event.msg.sender,
ev->event.msg.message);
+ }
break;
default:
purple_debug_error("gg", "strange event: %d\n", ev->type);
@@ -1532,683 +611,301 @@ static const char *ggp_list_icon(PurpleAccount *account, PurpleBuddy *buddy)
return "gadu-gadu";
}
-static char *ggp_status_text(PurpleBuddy *b)
+static const char *ggp_normalize(const PurpleAccount *account, const char *who)
{
- PurpleStatus *status;
- const char *msg;
- char *text;
- char *tmp;
+ static char normalized[21]; /* maximum unsigned long long int size */
- status = purple_presence_get_active_status(
- purple_buddy_get_presence(b));
- msg = purple_status_get_attr_string(status, "message");
-
- if (msg == NULL)
+ uin_t uin = ggp_str_to_uin(who);
+ if (uin <= 0)
return NULL;
- tmp = purple_markup_strip_html(msg);
- text = g_markup_escape_text(tmp, -1);
- g_free(tmp);
+ g_snprintf(normalized, sizeof(normalized), "%u", uin);
- return text;
+ return normalized;
}
+/* TODO:
+ * - move to status.c ?
+ * - add information about not adding to his buddy list (not_a_friend)
+ */
static void ggp_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
{
PurpleStatus *status;
- char *text, *tmp;
- const char *msg, *name, *alias;
+ char *tmp;
+ const char *name, *alias;
+ gchar *msg;
g_return_if_fail(b != NULL);
status = purple_presence_get_active_status(purple_buddy_get_presence(b));
- msg = purple_status_get_attr_string(status, "message");
name = purple_status_get_name(status);
alias = purple_buddy_get_alias(b);
+ ggp_status_from_purplestatus(status, &msg);
- purple_notify_user_info_add_pair (user_info, _("Alias"), alias);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Alias"), alias);
if (msg != NULL) {
- text = g_markup_escape_text(msg, -1);
if (PURPLE_BUDDY_IS_ONLINE(b)) {
- tmp = g_strdup_printf("%s: %s", name, text);
- purple_notify_user_info_add_pair(user_info, _("Status"), tmp);
+ tmp = g_strdup_printf("%s: %s", name, msg);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), tmp);
g_free(tmp);
} else {
- purple_notify_user_info_add_pair(user_info, _("Message"), text);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Message"), msg);
}
- g_free(text);
+ g_free(msg);
/* We don't want to duplicate 'Status: Offline'. */
} else if (PURPLE_BUDDY_IS_ONLINE(b)) {
- purple_notify_user_info_add_pair(user_info, _("Status"), name);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), name);
}
}
-static GList *ggp_status_types(PurpleAccount *account)
+static void ggp_login(PurpleAccount *account)
{
- PurpleStatusType *type;
- GList *types = NULL;
-
- type = purple_status_type_new_with_attrs(
- PURPLE_STATUS_AVAILABLE, NULL, NULL, TRUE, TRUE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
- NULL);
- types = g_list_append(types, type);
-
- /*
- * Without this selecting Invisible as own status doesn't
- * work. It's not used and not needed to show status of buddies.
- */
- type = purple_status_type_new_with_attrs(
- PURPLE_STATUS_INVISIBLE, NULL, NULL, TRUE, TRUE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
- NULL);
- types = g_list_append(types, type);
-
- type = purple_status_type_new_with_attrs(
- PURPLE_STATUS_AWAY, NULL, NULL, TRUE, TRUE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
- NULL);
- types = g_list_append(types, type);
-
- /*
- * New statuses for GG 8.0 like PoGGadaj ze mna (not yet because
- * libpurple can't support Chatty status) and Nie przeszkadzac
- */
- type = purple_status_type_new_with_attrs(
- PURPLE_STATUS_UNAVAILABLE, NULL, NULL, TRUE, TRUE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
- NULL);
- types = g_list_append(types, type);
-
- /*
- * This status is necessary to display guys who are blocking *us*.
- */
- type = purple_status_type_new_with_attrs(
- PURPLE_STATUS_INVISIBLE, "blocked", _("Blocked"), TRUE, FALSE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), NULL);
- types = g_list_append(types, type);
-
- type = purple_status_type_new_with_attrs(
- PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
- NULL);
- types = g_list_append(types, type);
-
- return types;
-}
-
-static GList *ggp_blist_node_menu(PurpleBlistNode *node)
-{
- PurpleMenuAction *act;
- GList *m = NULL;
- PurpleAccount *account;
+ PurpleConnection *gc = purple_account_get_connection(account);
+ struct gg_login_params *glp;
GGPInfo *info;
+#if GGP_ENABLE_GG11
+ const char *address;
+#endif
+ const gchar *encryption_type, *protocol_version;
- if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
- return NULL;
-
- account = purple_buddy_get_account((PurpleBuddy *) node);
- info = purple_account_get_connection(account)->proto_data;
- if (info->chats) {
- act = purple_menu_action_new(_("Add to chat"),
- PURPLE_CALLBACK(ggp_bmenu_add_to_chat),
- NULL, NULL);
- m = g_list_append(m, act);
- }
-
- return m;
-}
-
-static GList *ggp_chat_info(PurpleConnection *gc)
-{
- GList *m = NULL;
- struct proto_chat_entry *pce;
-
- pce = g_new0(struct proto_chat_entry, 1);
- pce->label = _("Chat _name:");
- pce->identifier = "name";
- pce->required = TRUE;
- m = g_list_append(m, pce);
+ if (!ggp_deprecated_setup_proxy(gc))
+ return;
- return m;
-}
+ purple_connection_set_flags(gc,
+#if ! GGP_ENABLE_GG11
+ PURPLE_CONNECTION_FLAG_NO_IMAGES |
+#endif
+ PURPLE_CONNECTION_FLAG_HTML |
+ PURPLE_CONNECTION_FLAG_NO_URLDESC);
-static void ggp_login_to(PurpleAccount *account, uint32_t server)
-{
- PurpleConnection *gc;
- PurplePresence *presence;
- PurpleStatus *status;
- struct gg_login_params *glp;
- GGPInfo *info;
- const gchar *encryption_type;
+ glp = g_new0(struct gg_login_params, 1);
+#if GGP_ENABLE_GG11
+ glp->struct_size = sizeof(struct gg_login_params);
+#endif
+ info = g_new0(GGPInfo, 1);
- if (ggp_setup_proxy(account) == -1)
+ purple_connection_set_protocol_data(gc, info);
+
+ ggp_tcpsocket_setup(gc, glp);
+ ggp_image_setup(gc);
+ ggp_avatar_setup(gc);
+ ggp_roster_setup(gc);
+ ggp_multilogon_setup(gc);
+ ggp_status_setup(gc);
+ ggp_chat_setup(gc);
+ ggp_message_setup(gc);
+ ggp_edisc_setup(gc);
+
+ glp->uin = ggp_str_to_uin(purple_account_get_username(account));
+ glp->password =
+ ggp_convert_to_cp1250(purple_connection_get_password(gc));
+
+ if (glp->uin == 0) {
+ purple_connection_error(gc,
+ PURPLE_CONNECTION_ERROR_INVALID_USERNAME,
+ _("The username specified is invalid."));
+ purple_str_wipe(glp->password);
+ g_free(glp);
return;
+ }
- gc = purple_account_get_connection(account);
- glp = g_new0(struct gg_login_params, 1);
- info = gc->proto_data;
- g_return_if_fail(info);
-
- /* Probably this should be moved to *_new() function. */
- info->session = NULL;
- info->chats = NULL;
- info->chats_count = 0;
- info->token = NULL;
- info->searches = ggp_search_new();
- info->pending_richtext_messages = NULL;
- info->pending_images = g_hash_table_new(g_direct_hash, g_direct_equal);
- info->status_broadcasting = purple_account_get_bool(account, "status_broadcasting", TRUE);
-
- glp->uin = ggp_get_uin(account);
- glp->password = (char *)purple_account_get_password(account);
glp->image_size = 255;
+ glp->status_flags = GG_STATUS_FLAG_UNKNOWN;
- presence = purple_account_get_presence(account);
- status = purple_presence_get_active_status(presence);
+ if (purple_account_get_bool(account, "show_links_from_strangers", 1))
+ glp->status_flags |= GG_STATUS_FLAG_SPAM;
glp->encoding = GG_ENCODING_UTF8;
- glp->protocol_features = (GG_FEATURE_STATUS80|GG_FEATURE_DND_FFC
- |GG_FEATURE_TYPING_NOTIFICATION);
+ glp->protocol_features = (GG_FEATURE_DND_FFC |
+ GG_FEATURE_TYPING_NOTIFICATION | GG_FEATURE_MULTILOGON |
+ GG_FEATURE_USER_DATA);
glp->async = 1;
- glp->status = ggp_to_gg_status(status, &glp->status_descr);
-
- encryption_type = purple_account_get_string(account, "encryption", "none");
- purple_debug_info("gg", "Requested encryption type: %s\n", encryption_type);
+
+ encryption_type = purple_account_get_string(account, "encryption",
+ "opportunistic_tls");
+ purple_debug_info("gg", "Requested encryption type: %s\n",
+ encryption_type);
if (strcmp(encryption_type, "opportunistic_tls") == 0)
- glp->tls = 1;
- else
- glp->tls = 0;
- purple_debug_info("gg", "TLS enabled: %d\n", glp->tls);
+ glp->tls = GG_SSL_ENABLED;
+ else if (strcmp(encryption_type, "require_tls") == 0) {
+ if (gg_libgadu_check_feature(GG_LIBGADU_FEATURE_SSL))
+ glp->tls = GG_SSL_REQUIRED;
+ else {
+ purple_connection_error(gc,
+ PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
+ _("SSL support unavailable"));
+ purple_str_wipe(glp->password);
+ g_free(glp);
+ return;
+ }
+ }
+ else /* encryption_type == "none" */
+ glp->tls = GG_SSL_DISABLED;
+ purple_debug_misc("gg", "TLS mode: %d\n", glp->tls);
+
+ protocol_version = purple_account_get_string(account,
+ "protocol_version", "default");
+ purple_debug_info("gg", "Requested protocol version: %s\n",
+ protocol_version);
+#if GGP_ENABLE_GG11
+ if (g_strcmp0(protocol_version, "gg10") == 0)
+ glp->protocol_version = GG_PROTOCOL_VERSION_100;
+ else if (g_strcmp0(protocol_version, "gg11") == 0)
+ glp->protocol_version = GG_PROTOCOL_VERSION_110;
+ glp->compatibility = GG_COMPAT_1_12_0;
+#else
+ glp->protocol_version = 0x2e;
+#endif
+
+ ggp_status_set_initial(gc, glp);
- if (!info->status_broadcasting)
- glp->status = glp->status|GG_STATUS_FRIENDS_MASK;
- glp->server_addr = server;
+#if GGP_ENABLE_GG11
+ address = purple_account_get_string(account, "gg_server", "");
+ if (address && *address)
+ glp->connect_host = g_strdup(address);
+#endif
info->session = gg_login(glp);
+#if GGP_ENABLE_GG11
+ g_free(glp->connect_host);
+#endif
+ purple_str_wipe(glp->password);
+ g_free(glp);
+
purple_connection_update_progress(gc, _("Connecting"), 0, 2);
if (info->session == NULL) {
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Connection failed"));
- g_free(glp);
return;
}
- gc->inpa = purple_input_add(info->session->fd,
- ggp_tcpsocket_inputcond_gg_to_purple(info->session->check),
- ggp_async_login_handler, gc);
-}
-
-static void
-ggp_login_resolved(GSList *hosts, gpointer _account, const char *error_message)
-{
- PurpleAccount *account = _account;
- PurpleConnection *gc;
- GGPInfo *info;
- uint32_t server_addr = 0;
-
- gc = purple_account_get_connection(account);
- info = gc->proto_data;
- g_return_if_fail(info);
- info->dns_query = NULL;
-
- while (hosts && (hosts = g_slist_delete_link(hosts, hosts))) {
- struct sockaddr *addr = hosts->data;
-
- if (addr->sa_family == AF_INET && server_addr == 0) {
- struct sockaddr_in *addrv4 = (struct sockaddr_in *)addr;
-
- server_addr = addrv4->sin_addr.s_addr;
- }
- g_free(hosts->data);
- hosts = g_slist_delete_link(hosts, hosts);
+ if (info->session->fd > 0) {
+ info->inpa = purple_input_add(info->session->fd,
+ PURPLE_INPUT_READ, ggp_async_login_handler, gc);
}
-
- if (server_addr == 0) {
- gchar *tmp = g_strdup_printf(
- _("Unable to resolve hostname: %s"), error_message);
- purple_connection_error_reason(gc,
- /* should this be a settings error? */
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
- g_free(tmp);
- return;
- }
-
- ggp_login_to(account, server_addr);
-}
-
-static void
-ggp_login(PurpleAccount *account)
-{
- PurpleConnection *gc;
- GGPInfo *info;
- const char *address;
-
- gc = purple_account_get_connection(account);
- info = g_new0(GGPInfo, 1);
- gc->proto_data = info;
-
- address = purple_account_get_string(account, "gg_server", "");
- if (address == NULL || address[0] == '\0') {
- purple_debug_info("gg", "Trying to retrieve address from gg appmsg service\n");
- ggp_login_to(account, 0);
- return;
- }
-
- purple_debug_info("gg", "Using gg server given by user (%s)\n", address);
- info->dns_query = purple_dnsquery_a_account(account, address, 8074,
- ggp_login_resolved, account);
}
static void ggp_close(PurpleConnection *gc)
{
- if (gc == NULL) {
- purple_debug_info("gg", "gc == NULL\n");
- return;
- }
+ PurpleAccount *account;
+ GGPInfo *info;;
- if (gc->proto_data) {
- PurpleAccount *account = purple_connection_get_account(gc);
- PurpleStatus *status;
- GGPInfo *info = gc->proto_data;
+ g_return_if_fail(gc != NULL);
- if (info->dns_query)
- purple_dnsquery_destroy(info->dns_query);
+ account = purple_connection_get_account(gc);
+ info = purple_connection_get_protocol_data(gc);
- status = purple_account_get_active_status(account);
+ purple_notify_close_with_handle(gc);
+ if (info) {
if (info->session != NULL) {
- ggp_set_status(account, status);
+ ggp_status_set_disconnected(account);
gg_logoff(info->session);
gg_free_session(info->session);
}
- purple_account_set_bool(account, "status_broadcasting", info->status_broadcasting);
+ ggp_image_cleanup(gc);
+ ggp_avatar_cleanup(gc);
+ ggp_roster_cleanup(gc);
+ ggp_multilogon_cleanup(gc);
+ ggp_status_cleanup(gc);
+ ggp_chat_cleanup(gc);
+ ggp_message_cleanup(gc);
+ ggp_edisc_cleanup(gc);
- /* Immediately close any notifications on this handle since that process depends
- * upon the contents of info->searches, which we are about to destroy.
- */
- purple_notify_close_with_handle(gc);
+ if (info->inpa > 0)
+ purple_input_remove(info->inpa);
+ g_free(info->imtoken);
- ggp_search_destroy(info->searches);
- g_list_free(info->pending_richtext_messages);
- g_hash_table_destroy(info->pending_images);
+ purple_connection_set_protocol_data(gc, NULL);
g_free(info);
- gc->proto_data = NULL;
}
- if (gc->inpa > 0)
- purple_input_remove(gc->inpa);
-
purple_debug_info("gg", "Connection closed.\n");
}
-static int ggp_send_im(PurpleConnection *gc, const char *who, const char *msg,
- PurpleMessageFlags flags)
+static unsigned int ggp_send_typing(PurpleConnection *gc, const char *name, PurpleIMTypingState state)
{
- GGPInfo *info = gc->proto_data;
- char *tmp, *plain;
- int ret = 1;
- unsigned char format[1024];
- unsigned int format_length = sizeof(struct gg_msg_richtext);
- gint pos = 0;
- GData *attribs;
- const char *start, *end = NULL, *last;
-
- if (msg == NULL || *msg == '\0') {
- return 0;
- }
-
- last = msg;
-
- /* Check if the message is richtext */
- /* TODO: Check formatting, too */
- if(purple_markup_find_tag("img", last, &start, &end, &attribs)) {
-
- GString *string_buffer = g_string_new(NULL);
- struct gg_msg_richtext fmt;
-
- do {
- PurpleStoredImage *image;
- const char *id;
-
- /* Add text before the image */
- if(start - last) {
- pos = pos + g_utf8_strlen(last, start - last);
- g_string_append_len(string_buffer, last, start - last);
- }
-
- if((id = g_datalist_get_data(&attribs, "id")) && (image = purple_imgstore_find_by_id(atoi(id)))) {
- struct gg_msg_richtext_format actformat;
- struct gg_msg_richtext_image actimage;
- gint image_size = purple_imgstore_get_size(image);
- gconstpointer image_bin = purple_imgstore_get_data(image);
- const char *image_filename = purple_imgstore_get_filename(image);
- uint32_t crc32 = gg_crc32(0, image_bin, image_size);
-
- g_hash_table_insert(info->pending_images, GINT_TO_POINTER(crc32), GINT_TO_POINTER(atoi(id)));
- purple_imgstore_ref(image);
- purple_debug_info("gg", "ggp_send_im_richtext: got crc: %u for imgid: %i\n", crc32, atoi(id));
-
- actformat.font = GG_FONT_IMAGE;
- actformat.position = pos;
-
- actimage.unknown1 = 0x0109;
- actimage.size = gg_fix32(image_size);
- actimage.crc32 = gg_fix32(crc32);
-
- if (actimage.size > 255000) {
- purple_debug_warning("gg", "ggp_send_im_richtext: image over 255kb!\n");
- } else {
- purple_debug_info("gg", "ggp_send_im_richtext: adding images to richtext, size: %i, crc32: %u, name: %s\n", actimage.size, actimage.crc32, image_filename);
-
- memcpy(format + format_length, &actformat, sizeof(actformat));
- format_length += sizeof(actformat);
- memcpy(format + format_length, &actimage, sizeof(actimage));
- format_length += sizeof(actimage);
- }
- } else {
- purple_debug_error("gg", "ggp_send_im_richtext: image not found in the image store!");
- }
-
- last = end + 1;
- g_datalist_clear(&attribs);
-
- } while(purple_markup_find_tag("img", last, &start, &end, &attribs));
-
- /* Add text after the images */
- if(last && *last) {
- pos = pos + g_utf8_strlen(last, -1);
- g_string_append(string_buffer, last);
- }
-
- fmt.flag = 2;
- fmt.length = format_length - sizeof(fmt);
- memcpy(format, &fmt, sizeof(fmt));
+ GGPInfo *info = purple_connection_get_protocol_data(gc);
+ int dummy_length; /* we don't send real length of typed message */
- purple_debug_info("gg", "ggp_send_im: richtext msg = %s\n", string_buffer->str);
- plain = purple_unescape_html(string_buffer->str);
- g_string_free(string_buffer, TRUE);
- } else {
- purple_debug_info("gg", "ggp_send_im: msg = %s\n", msg);
- plain = purple_unescape_html(msg);
- }
-
- /*
- tmp = charset_convert(plain, "UTF-8", "CP1250");
- */
- tmp = g_strdup_printf("%s", plain);
-
- if (tmp && (format_length - sizeof(struct gg_msg_richtext))) {
- if(gg_send_message_richtext(info->session, GG_CLASS_CHAT, ggp_str_to_uin(who), (unsigned char *)tmp, format, format_length) < 0) {
- ret = -1;
- } else {
- ret = 1;
- }
- } else if (NULL == tmp || *tmp == 0) {
- ret = 0;
- } else if (strlen(tmp) > GG_MSG_MAXSIZE) {
- ret = -E2BIG;
- } else if (gg_send_message(info->session, GG_CLASS_CHAT,
- ggp_str_to_uin(who), (unsigned char *)tmp) < 0) {
- ret = -1;
- } else {
- ret = 1;
- }
-
- g_free(plain);
- g_free(tmp);
-
- return ret;
-}
-
-static unsigned int ggp_send_typing(PurpleConnection *gc, const char *name, PurpleTypingState state)
-{
- int dummy_length; // we don't send real length of typed message
-
- if (state == PURPLE_TYPED) // not supported
+ if (state == PURPLE_IM_TYPED) /* not supported */
return 1;
-
- if (state == PURPLE_TYPING)
+
+ if (state == PURPLE_IM_TYPING)
dummy_length = (int)g_random_int();
- else // PURPLE_NOT_TYPING
+ else /* PURPLE_NOT_TYPING */
dummy_length = 0;
-
+
gg_typing_notification(
- ((GGPInfo*)gc->proto_data)->session,
+ info->session,
ggp_str_to_uin(name),
- dummy_length);
-
- return 1; // wait 1 second before another notification
-}
-
-static void ggp_get_info(PurpleConnection *gc, const char *name)
-{
- GGPInfo *info = gc->proto_data;
- GGPSearchForm *form;
- guint32 seq;
-
- form = ggp_search_form_new(GGP_SEARCH_TYPE_INFO);
-
- form->user_data = info;
- form->uin = g_strdup(name);
-
- seq = ggp_search_start(gc, form);
- ggp_search_add(info->searches, seq, form);
- purple_debug_info("gg", "ggp_get_info(): Added seq %u", seq);
-}
-
-static int ggp_to_gg_status(PurpleStatus *status, char **msg)
-{
- const char *status_id = purple_status_get_id(status);
- int new_status, new_status_descr;
- const char *new_msg;
-
- g_return_val_if_fail(msg != NULL, 0);
-
- purple_debug_info("gg", "ggp_to_gg_status: Requested status = %s\n",
- status_id);
-
- if (strcmp(status_id, "available") == 0) {
- new_status = GG_STATUS_AVAIL;
- new_status_descr = GG_STATUS_AVAIL_DESCR;
- } else if (strcmp(status_id, "away") == 0) {
- new_status = GG_STATUS_BUSY;
- new_status_descr = GG_STATUS_BUSY_DESCR;
- } else if (strcmp(status_id, "unavailable") == 0) {
- new_status = GG_STATUS_DND;
- new_status_descr = GG_STATUS_DND_DESCR;
- } else if (strcmp(status_id, "invisible") == 0) {
- new_status = GG_STATUS_INVISIBLE;
- new_status_descr = GG_STATUS_INVISIBLE_DESCR;
- } else if (strcmp(status_id, "offline") == 0) {
- new_status = GG_STATUS_NOT_AVAIL;
- new_status_descr = GG_STATUS_NOT_AVAIL_DESCR;
- } else {
- new_status = GG_STATUS_AVAIL;
- new_status_descr = GG_STATUS_AVAIL_DESCR;
- purple_debug_info("gg",
- "ggp_set_status: unknown status requested (status_id=%s)\n",
- status_id);
- }
-
- new_msg = purple_status_get_attr_string(status, "message");
-
- if(new_msg) {
- /*
- char *tmp = purple_markup_strip_html(new_msg);
- *msg = charset_convert(tmp, "UTF-8", "CP1250");
- g_free(tmp);
- */
- *msg = purple_markup_strip_html(new_msg);
-
- return new_status_descr;
- } else {
- *msg = NULL;
- return new_status;
- }
-}
-
-static void ggp_set_status(PurpleAccount *account, PurpleStatus *status)
-{
- PurpleConnection *gc;
- GGPInfo *info;
- int new_status;
- char *new_msg = NULL;
-
- if (!purple_status_is_active(status))
- return;
-
- gc = purple_account_get_connection(account);
- info = gc->proto_data;
-
- new_status = ggp_to_gg_status(status, &new_msg);
-
- if (!info->status_broadcasting)
- new_status = new_status|GG_STATUS_FRIENDS_MASK;
-
- if (new_msg == NULL) {
- gg_change_status(info->session, new_status);
- } else {
- gg_change_status_descr(info->session, new_status, new_msg);
- g_free(new_msg);
- }
-
- ggp_status_fake_to_self(account);
+ dummy_length);
+ return 1; /* wait 1 second before another notification */
}
-static void ggp_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
+static void ggp_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *message)
{
- PurpleAccount *account;
- GGPInfo *info = gc->proto_data;
+ PurpleAccount *account = purple_connection_get_account(gc);
+ GGPInfo *info = purple_connection_get_protocol_data(gc);
const gchar *name = purple_buddy_get_name(buddy);
gg_add_notify(info->session, ggp_str_to_uin(name));
- account = purple_connection_get_account(gc);
- if (strcmp(purple_account_get_username(account), name) == 0) {
- ggp_status_fake_to_self(account);
- }
+ /* gg server won't tell us our status here */
+ if (strcmp(purple_account_get_username(account), name) == 0)
+ ggp_status_fake_to_self(gc);
+
+ ggp_roster_add_buddy(gc, buddy, group, message);
+ ggp_pubdir_request_buddy_alias(gc, buddy);
}
static void ggp_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
- PurpleGroup *group)
+ PurpleGroup *group)
{
- GGPInfo *info = gc->proto_data;
+ GGPInfo *info = purple_connection_get_protocol_data(gc);
gg_remove_notify(info->session, ggp_str_to_uin(purple_buddy_get_name(buddy)));
+ ggp_roster_remove_buddy(gc, buddy, group);
}
-static void ggp_join_chat(PurpleConnection *gc, GHashTable *data)
+static void ggp_keepalive(PurpleConnection *gc)
{
- GGPInfo *info = gc->proto_data;
- GGPChat *chat;
- char *chat_name;
- GList *l;
- PurpleConversation *conv;
- PurpleAccount *account = purple_connection_get_account(gc);
+ GGPInfo *info = purple_connection_get_protocol_data(gc);
- chat_name = g_hash_table_lookup(data, "name");
-
- if (chat_name == NULL)
- return;
-
- purple_debug_info("gg", "joined %s chat\n", chat_name);
-
- for (l = info->chats; l != NULL; l = l->next) {
- chat = l->data;
+ /* purple_debug_info("gg", "Keeping connection alive....\n"); */
- if (chat != NULL && g_utf8_collate(chat->name, chat_name) == 0) {
- purple_notify_error(gc, _("Chat error"),
- _("This chat name is already in use"), NULL);
- return;
- }
+ if (gg_ping(info->session) < 0) {
+ purple_debug_info("gg", "Not connected to the server "
+ "or gg_session is not correct\n");
+ purple_connection_error (gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Not connected to the server"));
}
-
- ggp_confer_add_new(gc, chat_name);
- conv = serv_got_joined_chat(gc, info->chats_count, chat_name);
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(conv),
- purple_account_get_username(account), NULL,
- PURPLE_CBFLAGS_NONE, TRUE);
}
-static char *ggp_get_chat_name(GHashTable *data) {
- return g_strdup(g_hash_table_lookup(data, "name"));
+static void ggp_action_multilogon(PurplePluginAction *action)
+{
+ ggp_multilogon_dialog((PurpleConnection *)action->context);
}
-static int ggp_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags)
+static void ggp_action_status_broadcasting(PurplePluginAction *action)
{
- PurpleConversation *conv;
- GGPInfo *info = gc->proto_data;
- GGPChat *chat = NULL;
- GList *l;
- /* char *msg, *plain; */
- gchar *msg;
- uin_t *uins;
- int count = 0;
-
- if ((conv = purple_find_chat(gc, id)) == NULL)
- return -EINVAL;
-
- for (l = info->chats; l != NULL; l = l->next) {
- chat = l->data;
-
- if (g_utf8_collate(chat->name, conv->name) == 0) {
- break;
- }
-
- chat = NULL;
- }
-
- if (chat == NULL) {
- purple_debug_error("gg",
- "ggp_chat_send: Hm... that's strange. No such chat?\n");
- return -EINVAL;
- }
-
- uins = g_new0(uin_t, g_list_length(chat->participants));
-
- for (l = chat->participants; l != NULL; l = l->next) {
- uin_t uin = GPOINTER_TO_INT(l->data);
-
- uins[count++] = uin;
- }
-
- /*
- plain = purple_unescape_html(message);
- msg = charset_convert(plain, "UTF-8", "CP1250");
- g_free(plain);
- */
- msg = purple_unescape_html(message);
- gg_send_message_confer(info->session, GG_CLASS_CHAT, count, uins,
- (unsigned char *)msg);
- g_free(msg);
- g_free(uins);
-
- serv_got_chat_in(gc, id,
- purple_account_get_username(purple_connection_get_account(gc)),
- flags, message, time(NULL));
-
- return 0;
+ ggp_status_broadcasting_dialog((PurpleConnection *)action->context);
}
-static void ggp_keepalive(PurpleConnection *gc)
+static void ggp_action_search(PurplePluginAction *action)
{
- GGPInfo *info = gc->proto_data;
-
- /* purple_debug_info("gg", "Keeping connection alive....\n"); */
+ ggp_pubdir_search((PurpleConnection *)action->context, NULL);
+}
- if (gg_ping(info->session) < 0) {
- purple_debug_info("gg", "Not connected to the server "
- "or gg_session is not correct\n");
- purple_connection_error_reason (gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Not connected to the server"));
- }
+static void ggp_action_set_info(PurplePluginAction *action)
+{
+ ggp_pubdir_set_info((PurpleConnection *)action->context);
}
static GList *ggp_actions(PurplePlugin *plugin, gpointer context)
@@ -2216,63 +913,95 @@ static GList *ggp_actions(PurplePlugin *plugin, gpointer context)
GList *m = NULL;
PurplePluginAction *act;
+ act = purple_plugin_action_new(_("Show other sessions"),
+ ggp_action_multilogon);
+ m = g_list_append(m, act);
+
+ act = purple_plugin_action_new(_("Show status only for buddies"),
+ ggp_action_status_broadcasting);
+ m = g_list_append(m, act);
+
+ m = g_list_append(m, NULL);
+
act = purple_plugin_action_new(_("Find buddies..."),
- ggp_find_buddies);
+ ggp_action_search);
m = g_list_append(m, act);
- act = purple_plugin_action_new(_("Change status broadcasting"),
- ggp_action_change_status_broadcasting);
+ act = purple_plugin_action_new(_("Set User Info"),
+ ggp_action_set_info);
m = g_list_append(m, act);
m = g_list_append(m, NULL);
act = purple_plugin_action_new(_("Save buddylist to file..."),
- ggp_action_buddylist_save);
+ ggp_action_buddylist_save);
m = g_list_append(m, act);
act = purple_plugin_action_new(_("Load buddylist from file..."),
- ggp_action_buddylist_load);
+ ggp_action_buddylist_load);
m = g_list_append(m, act);
return m;
}
+static const char* ggp_list_emblem(PurpleBuddy *buddy)
+{
+ ggp_buddy_data *buddy_data = ggp_buddy_get_data(buddy);
+
+ if (buddy_data->blocked)
+ return "not-authorized";
+ if (buddy_data->not_a_friend)
+ return "unavailable";
+
+ return NULL;
+}
+
static gboolean ggp_offline_message(const PurpleBuddy *buddy)
{
return TRUE;
}
-static gboolean ggp_load(PurplePlugin *plugin)
+static GHashTable * ggp_get_account_text_table(PurpleAccount *account)
{
- purple_debug_info("gg", "Loading Gadu-Gadu protocol plugin with "
- "libgadu %s...\n", gg_libgadu_version());
-
- gg_is_gpl_compliant();
+ GHashTable *table;
+ table = g_hash_table_new(g_str_hash, g_str_equal);
+ g_hash_table_insert(table, "login_label", (gpointer)_("GG number..."));
+ return table;
+}
- return TRUE;
+static gssize
+ggp_get_max_message_size(PurpleConversation *conv)
+{
+ /* TODO: it may depend on protocol version or other factors */
+ return 1200; /* no more than 1232 */
}
static PurplePluginProtocolInfo prpl_info =
{
- OPT_PROTO_IM_IMAGE,
+ sizeof(PurplePluginProtocolInfo), /* struct_size */
+ 0,
NULL, /* user_splits */
NULL, /* protocol_options */
- {"png", 32, 32, 96, 96, 0, PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
+ {"png", 1, 1, 200, 200, 0, PURPLE_ICON_SCALE_DISPLAY | PURPLE_ICON_SCALE_SEND}, /* icon_spec */
ggp_list_icon, /* list_icon */
- NULL, /* list_emblem */
- ggp_status_text, /* status_text */
+ ggp_list_emblem, /* list_emblem */
+ ggp_status_buddy_text, /* status_text */
ggp_tooltip_text, /* tooltip_text */
ggp_status_types, /* status_types */
- ggp_blist_node_menu, /* blist_node_menu */
+ NULL, /* blist_node_menu */
+#if GGP_ENABLE_GG11
ggp_chat_info, /* chat_info */
- NULL, /* chat_info_defaults */
+ ggp_chat_info_defaults, /* chat_info_defaults */
+#else
+ NULL, NULL,
+#endif
ggp_login, /* login */
ggp_close, /* close */
- ggp_send_im, /* send_im */
+ ggp_message_send_im, /* send_im */
NULL, /* set_info */
ggp_send_typing, /* send_typing */
- ggp_get_info, /* get_info */
- ggp_set_status, /* set_away */
+ ggp_pubdir_get_info_prpl, /* get_info */
+ ggp_status_set_purplestatus, /* set_away */
NULL, /* set_idle */
NULL, /* change_passwd */
ggp_add_buddy, /* add_buddy */
@@ -2284,34 +1013,40 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* rem_permit */
ggp_rem_deny, /* rem_deny */
NULL, /* set_permit_deny */
- ggp_join_chat, /* join_chat */
- NULL, /* reject_chat */
- ggp_get_chat_name, /* get_chat_name */
- NULL, /* chat_invite */
- NULL, /* chat_leave */
- NULL, /* chat_whisper */
+#if GGP_ENABLE_GG11
+ ggp_chat_join, /* join_chat */
+ NULL, /* TODO */ /* reject_chat */
+ ggp_chat_get_name, /* get_chat_name */
+ ggp_chat_invite, /* chat_invite */
+ ggp_chat_leave, /* chat_leave */
ggp_chat_send, /* chat_send */
+#else
+ NULL, NULL, NULL, NULL, NULL, NULL,
+#endif
ggp_keepalive, /* keepalive */
NULL, /* register_user */
NULL, /* get_cb_info */
- NULL, /* get_cb_away */
- NULL, /* alias_buddy */
- NULL, /* group_buddy */
- NULL, /* rename_group */
- NULL, /* buddy_free */
+ ggp_roster_alias_buddy, /* alias_buddy */
+ ggp_roster_group_buddy, /* group_buddy */
+ ggp_roster_rename_group, /* rename_group */
+ ggp_buddy_free, /* buddy_free */
NULL, /* convo_closed */
- NULL, /* normalize */
- NULL, /* set_buddy_icon */
+ ggp_normalize, /* normalize */
+ ggp_avatar_own_set, /* set_buddy_icon */
NULL, /* remove_group */
NULL, /* get_cb_real_name */
NULL, /* set_chat_topic */
NULL, /* find_blist_chat */
- NULL, /* roomlist_get_list */
+#if GGP_ENABLE_GG11
+ ggp_chat_roomlist_get_list, /* roomlist_get_list */
+#else
+ NULL,
+#endif
NULL, /* roomlist_cancel */
NULL, /* roomlist_expand_category */
- NULL, /* can_receive_file */
- NULL, /* send_file */
- NULL, /* new_xfer */
+ ggp_edisc_xfer_can_receive_file, /* can_receive_file */
+ ggp_edisc_xfer_send_file, /* send_file */
+ ggp_edisc_xfer_send_new, /* new_xfer */
ggp_offline_message, /* offline_message */
NULL, /* whiteboard_prpl_ops */
NULL, /* send_raw */
@@ -2319,17 +1054,18 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* unregister_user */
NULL, /* send_attention */
NULL, /* get_attention_types */
- sizeof(PurplePluginProtocolInfo), /* struct_size */
- NULL, /* get_account_text_table */
- NULL, /* initiate_media */
- NULL, /* can_do_media */
+ ggp_get_account_text_table, /* get_account_text_table */
+ NULL, /* initiate_media */
+ NULL, /* can_do_media */
NULL, /* get_moods */
NULL, /* set_public_alias */
NULL, /* get_public_alias */
- NULL, /* add_buddy_with_invite */
- NULL /* add_buddies_with_invite */
+ ggp_get_max_message_size /* get_max_message_size */
};
+static gboolean ggp_load(PurplePlugin *plugin);
+static gboolean ggp_unload(PurplePlugin *plugin);
+
static PurplePluginInfo info = {
PURPLE_PLUGIN_MAGIC, /* magic */
PURPLE_MAJOR_VERSION, /* major_version */
@@ -2350,7 +1086,7 @@ static PurplePluginInfo info = {
PURPLE_WEBSITE, /* homepage */
ggp_load, /* load */
- NULL, /* unload */
+ ggp_unload, /* unload */
NULL, /* destroy */
NULL, /* ui_info */
@@ -2365,43 +1101,21 @@ static PurplePluginInfo info = {
NULL
};
-static void purple_gg_debug_handler(int level, const char * format, va_list args) {
- PurpleDebugLevel purple_level;
- char *msg = g_strdup_vprintf(format, args);
-
- /* This is pretty pointless since the GG_DEBUG levels don't correspond to
- * the purple ones */
- switch (level) {
- case GG_DEBUG_FUNCTION:
- purple_level = PURPLE_DEBUG_INFO;
- break;
- case GG_DEBUG_MISC:
- case GG_DEBUG_NET:
- case GG_DEBUG_DUMP:
- case GG_DEBUG_TRAFFIC:
- default:
- purple_level = PURPLE_DEBUG_MISC;
- break;
- }
-
- purple_debug(purple_level, "gg", "%s", msg);
- g_free(msg);
-}
+static PurpleAccountOption *ggp_server_option;
static void init_plugin(PurplePlugin *plugin)
{
PurpleAccountOption *option;
GList *encryption_options = NULL;
+ GList *protocol_version = NULL;
- option = purple_account_option_string_new(_("Nickname"),
- "nick", _("Gadu-Gadu User"));
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
- option);
+ purple_prefs_add_none("/plugins/prpl/gg");
option = purple_account_option_string_new(_("GG server"),
"gg_server", "");
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
option);
+ ggp_server_option = option;
#define ADD_VALUE(list, desc, v) { \
PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1); \
@@ -2410,22 +1124,53 @@ static void init_plugin(PurplePlugin *plugin)
list = g_list_append(list, kvp); \
}
- ADD_VALUE(encryption_options, _("Don't use encryption"), "none");
ADD_VALUE(encryption_options, _("Use encryption if available"),
"opportunistic_tls");
-#if 0
- /* TODO */
ADD_VALUE(encryption_options, _("Require encryption"), "require_tls");
-#endif
+ ADD_VALUE(encryption_options, _("Don't use encryption"), "none");
option = purple_account_option_list_new(_("Connection security"),
"encryption", encryption_options);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
option);
- my_protocol = plugin;
+ ADD_VALUE(protocol_version, _("Default"), "default");
+ ADD_VALUE(protocol_version, "GG 10", "gg10");
+ ADD_VALUE(protocol_version, "GG 11", "gg11");
+
+ option = purple_account_option_list_new(_("Protocol version"),
+ "protocol_version", protocol_version);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
+ option);
+
+ option = purple_account_option_bool_new(_("Show links from strangers"),
+ "show_links_from_strangers", 1);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
+ option);
+}
+
+static gboolean ggp_load(PurplePlugin *plugin)
+{
+ purple_debug_info("gg", "Loading Gadu-Gadu protocol plugin with "
+ "libgadu %s...\n", gg_libgadu_version());
+
+ ggp_libgaduw_setup();
+ ggp_resolver_purple_setup();
+ ggp_servconn_setup(ggp_server_option);
+ ggp_html_setup();
+ ggp_message_setup_global();
- gg_debug_handler = purple_gg_debug_handler;
+ return TRUE;
+}
+
+static gboolean ggp_unload(PurplePlugin *plugin)
+{
+ ggp_servconn_cleanup();
+ ggp_html_cleanup();
+ ggp_message_cleanup_global();
+ ggp_libgaduw_cleanup();
+
+ return TRUE;
}
PURPLE_INIT_PLUGIN(gg, init_plugin, info);
diff --git a/libpurple/protocols/gg/gg.h b/libpurple/protocols/gg/gg.h
index 2fa2954764..c79c4bc402 100644
--- a/libpurple/protocols/gg/gg.h
+++ b/libpurple/protocols/gg/gg.h
@@ -24,51 +24,58 @@
#ifndef _PURPLE_GG_H
#define _PURPLE_GG_H
+#define GGP_UIN_LEN_MAX 10
+
+#ifdef _WIN32
+#define GGP_ENABLE_GG11 1
+#else
+#define GGP_ENABLE_GG11 0
+#endif
+
#include <libgadu.h>
+
#include "internal.h"
-#include "dnsquery.h"
#include "search.h"
#include "connection.h"
+#include "image-prpl.h"
+#include "avatar.h"
+#include "roster.h"
+#include "multilogon.h"
+#include "status.h"
+#include "chat.h"
+#include "message-prpl.h"
+#include "edisc.h"
-#define PUBDIR_RESULTS_MAX 20
-
-
-typedef struct
-{
- char *name;
- GList *participants;
-
-} GGPChat;
+typedef struct {
+ struct gg_session *session;
+ guint inpa;
-typedef void (*GGPTokenCallback)(PurpleConnection *);
+ gchar *imtoken;
+ gboolean imtoken_warned;
+
+ ggp_image_session_data *image_data;
+ ggp_avatar_session_data avatar_data;
+ ggp_roster_session_data roster_data;
+ ggp_multilogon_session_data *multilogon_data;
+ ggp_status_session_data *status_data;
+ ggp_chat_session_data *chat_data;
+ ggp_message_session_data *message_data;
+ ggp_edisc_session_data *edisc_data;
+} GGPInfo;
typedef struct
{
- char *id;
- char *data;
- unsigned int size;
+ gboolean blocked;
+ gboolean not_a_friend;
+} ggp_buddy_data;
- struct gg_http *req;
- guint inpa;
+ggp_buddy_data * ggp_buddy_get_data(PurpleBuddy *buddy);
- GGPTokenCallback cb;
+const gchar * ggp_get_imtoken(PurpleConnection *gc);
-} GGPToken;
+uin_t ggp_own_uin(PurpleConnection *gc);
-typedef struct {
-
- struct gg_session *session;
- GGPToken *token;
- GList *chats;
- GGPSearches *searches;
- int chats_count;
- GList *pending_richtext_messages;
- GHashTable *pending_images;
- gboolean status_broadcasting; //When TRUE status is visible to all, when FALSE status is visible only to friends.
- PurpleDnsQueryData *dns_query;
-} GGPInfo;
+void ggp_async_login_handler(gpointer _gc, gint fd, PurpleInputCondition cond);
#endif /* _PURPLE_GG_H */
-
-/* vim: set ts=8 sts=0 sw=8 noet: */
diff --git a/libpurple/protocols/gg/html.c b/libpurple/protocols/gg/html.c
new file mode 100644
index 0000000000..a4bad98e7e
--- /dev/null
+++ b/libpurple/protocols/gg/html.c
@@ -0,0 +1,181 @@
+/* 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.
+ *
+ * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
+ * As a recipient of this file you may choose, which license to receive the
+ * code under. As a contributor, you have to ensure the new code is
+ * compatible with both.
+ *
+ * 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 "html.h"
+
+#include <debug.h>
+
+typedef struct
+{
+ GRegex *re_html_attr;
+ GRegex *re_css_attr;
+ GRegex *re_color_hex;
+ GRegex *re_color_rgb;
+} ggp_html_global_data;
+
+static ggp_html_global_data global_data;
+
+void ggp_html_setup(void)
+{
+ global_data.re_html_attr = g_regex_new(
+ "([a-z-]+)=\"([^\"]+)\"",
+ G_REGEX_OPTIMIZE, 0, NULL);
+ global_data.re_css_attr = g_regex_new(
+ "([a-z-]+): *([^;]+)",
+ G_REGEX_OPTIMIZE, 0, NULL);
+ global_data.re_color_hex = g_regex_new(
+ "^#([0-9a-fA-F]+){6}$",
+ G_REGEX_OPTIMIZE, 0, NULL);
+ global_data.re_color_rgb = g_regex_new(
+ "^rgb\\(([0-9]+), *([0-9]+), *([0-9]+)\\)$",
+ G_REGEX_OPTIMIZE, 0, NULL);
+}
+
+void ggp_html_cleanup(void)
+{
+ g_regex_unref(global_data.re_html_attr);
+ g_regex_unref(global_data.re_css_attr);
+ g_regex_unref(global_data.re_color_hex);
+ g_regex_unref(global_data.re_color_rgb);
+}
+
+GHashTable * ggp_html_tag_attribs(const gchar *attribs_str)
+{
+ GMatchInfo *match;
+ GHashTable *attribs = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, g_free);
+
+ if (attribs_str == NULL)
+ return attribs;
+
+ g_regex_match(global_data.re_html_attr, attribs_str, 0, &match);
+ while (g_match_info_matches(match)) {
+ g_hash_table_insert(attribs,
+ g_match_info_fetch(match, 1),
+ g_match_info_fetch(match, 2));
+
+ g_match_info_next(match, NULL);
+ }
+ g_match_info_free(match);
+
+ return attribs;
+}
+
+GHashTable * ggp_html_css_attribs(const gchar *attribs_str)
+{
+ GMatchInfo *match;
+ GHashTable *attribs = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, g_free);
+
+ if (attribs_str == NULL)
+ return attribs;
+
+ g_regex_match(global_data.re_css_attr, attribs_str, 0, &match);
+ while (g_match_info_matches(match)) {
+ g_hash_table_insert(attribs,
+ g_match_info_fetch(match, 1),
+ g_match_info_fetch(match, 2));
+
+ g_match_info_next(match, NULL);
+ }
+ g_match_info_free(match);
+
+ return attribs;
+}
+
+int ggp_html_decode_color(const gchar *str)
+{
+ GMatchInfo *match;
+ int color = -1;
+
+ g_regex_match(global_data.re_color_hex, str, 0, &match);
+ if (g_match_info_matches(match)) {
+ if (sscanf(str + 1, "%x", &color) != 1)
+ color = -1;
+ }
+ g_match_info_free(match);
+ if (color >= 0)
+ return color;
+
+ g_regex_match(global_data.re_color_rgb, str, 0, &match);
+ if (g_match_info_matches(match)) {
+ int r = -1, g = -1, b = -1;
+ gchar *c_str;
+
+ c_str = g_match_info_fetch(match, 1);
+ if (c_str)
+ r = atoi(c_str);
+ g_free(c_str);
+
+ c_str = g_match_info_fetch(match, 2);
+ if (c_str)
+ g = atoi(c_str);
+ g_free(c_str);
+
+ c_str = g_match_info_fetch(match, 3);
+ if (c_str)
+ b = atoi(c_str);
+ g_free(c_str);
+
+ if (r >= 0 && r < 256 && g >= 0 && g < 256 && b >= 0 && b < 256)
+ color = (r << 16) | (g << 8) | b;
+ }
+ g_match_info_free(match);
+ if (color >= 0)
+ return color;
+
+ return -1;
+}
+
+ggp_html_tag ggp_html_parse_tag(const gchar *tag_str)
+{
+ if (0 == g_ascii_strcasecmp(tag_str, "eom"))
+ return GGP_HTML_TAG_EOM;
+ if (0 == g_ascii_strcasecmp(tag_str, "span"))
+ return GGP_HTML_TAG_SPAN;
+ if (0 == g_ascii_strcasecmp(tag_str, "div"))
+ return GGP_HTML_TAG_DIV;
+ if (0 == g_ascii_strcasecmp(tag_str, "br"))
+ return GGP_HTML_TAG_BR;
+ if (0 == g_ascii_strcasecmp(tag_str, "a"))
+ return GGP_HTML_TAG_A;
+ if (0 == g_ascii_strcasecmp(tag_str, "b"))
+ return GGP_HTML_TAG_B;
+ if (0 == g_ascii_strcasecmp(tag_str, "i"))
+ return GGP_HTML_TAG_I;
+ if (0 == g_ascii_strcasecmp(tag_str, "u"))
+ return GGP_HTML_TAG_U;
+ if (0 == g_ascii_strcasecmp(tag_str, "s"))
+ return GGP_HTML_TAG_S;
+ if (0 == g_ascii_strcasecmp(tag_str, "img"))
+ return GGP_HTML_TAG_IMG;
+ if (0 == g_ascii_strcasecmp(tag_str, "font"))
+ return GGP_HTML_TAG_FONT;
+ if (0 == g_ascii_strcasecmp(tag_str, "hr"))
+ return GGP_HTML_TAG_HR;
+ return GGP_HTML_TAG_UNKNOWN;
+}
diff --git a/libpurple/protocols/gg/html.h b/libpurple/protocols/gg/html.h
new file mode 100644
index 0000000000..e352b6fd08
--- /dev/null
+++ b/libpurple/protocols/gg/html.h
@@ -0,0 +1,59 @@
+/* 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.
+ *
+ * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
+ * As a recipient of this file you may choose, which license to receive the
+ * code under. As a contributor, you have to ensure the new code is
+ * compatible with both.
+ *
+ * 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 _GGP_HTML_H
+#define _GGP_HTML_H
+
+#include <internal.h>
+
+typedef enum
+{
+ GGP_HTML_TAG_UNKNOWN,
+ GGP_HTML_TAG_EOM,
+ GGP_HTML_TAG_A,
+ GGP_HTML_TAG_B,
+ GGP_HTML_TAG_I,
+ GGP_HTML_TAG_U,
+ GGP_HTML_TAG_S,
+ GGP_HTML_TAG_IMG,
+ GGP_HTML_TAG_FONT,
+ GGP_HTML_TAG_SPAN,
+ GGP_HTML_TAG_DIV,
+ GGP_HTML_TAG_BR,
+ GGP_HTML_TAG_HR,
+} ggp_html_tag;
+
+void ggp_html_setup(void);
+void ggp_html_cleanup(void);
+
+GHashTable * ggp_html_tag_attribs(const gchar *attribs_str);
+GHashTable * ggp_html_css_attribs(const gchar *attribs_str);
+int ggp_html_decode_color(const gchar *str);
+ggp_html_tag ggp_html_parse_tag(const gchar *tag_str);
+
+
+#endif /* _GGP_HTML_H */
diff --git a/libpurple/protocols/gg/image-prpl.c b/libpurple/protocols/gg/image-prpl.c
new file mode 100644
index 0000000000..884652c51e
--- /dev/null
+++ b/libpurple/protocols/gg/image-prpl.c
@@ -0,0 +1,259 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 "image-prpl.h"
+
+#include <debug.h>
+#include <glibcompat.h>
+
+#include "gg.h"
+#include "utils.h"
+
+#include <image-store.h>
+
+struct _ggp_image_session_data
+{
+ GHashTable *recv_images;
+ GHashTable *sent_images;
+};
+
+typedef struct
+{
+ PurpleImage *image;
+ gchar *conv_name; /* TODO: callback */
+} ggp_image_sent;
+
+static void ggp_image_sent_free(gpointer _sent_image)
+{
+ ggp_image_sent *sent_image = _sent_image;
+ g_object_unref(sent_image->image);
+ g_free(sent_image->conv_name);
+ g_free(sent_image);
+}
+
+static uint64_t ggp_image_params_to_id(uint32_t crc32, uint32_t size)
+{
+ return ((uint64_t)crc32 << 32) | size;
+}
+
+static inline ggp_image_session_data *
+ggp_image_get_sdata(PurpleConnection *gc)
+{
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+ return accdata->image_data;
+}
+
+void ggp_image_setup(PurpleConnection *gc)
+{
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+ ggp_image_session_data *sdata = g_new0(ggp_image_session_data, 1);
+
+ accdata->image_data = sdata;
+
+ sdata->recv_images = g_hash_table_new_full(
+ g_int64_hash, g_int64_equal, g_free, g_object_unref);
+ sdata->sent_images = g_hash_table_new_full(
+ g_int64_hash, g_int64_equal, g_free,
+ ggp_image_sent_free);
+}
+
+void ggp_image_cleanup(PurpleConnection *gc)
+{
+ ggp_image_session_data *sdata = ggp_image_get_sdata(gc);
+
+ g_hash_table_destroy(sdata->recv_images);
+ g_hash_table_destroy(sdata->sent_images);
+ g_free(sdata);
+}
+
+ggp_image_prepare_result
+ggp_image_prepare(PurpleConversation *conv, PurpleImage *image, uint64_t *id)
+{
+ PurpleConnection *gc = purple_conversation_get_connection(conv);
+ ggp_image_session_data *sdata = ggp_image_get_sdata(gc);
+ size_t image_size;
+ gconstpointer image_data;
+ uint32_t image_crc;
+ ggp_image_sent *sent_image;
+
+ g_return_val_if_fail(image, GGP_IMAGE_PREPARE_FAILURE);
+
+ image_size = purple_image_get_size(image);
+
+ if (image_size > GGP_IMAGE_SIZE_MAX) {
+ purple_debug_warning("gg", "ggp_image_prepare: image "
+ "is too big (max bytes: %d)\n", GGP_IMAGE_SIZE_MAX);
+ return GGP_IMAGE_PREPARE_TOO_BIG;
+ }
+
+ g_object_ref(image);
+ image_data = purple_image_get_data(image);
+ image_crc = gg_crc32(0, image_data, image_size);
+
+ purple_debug_info("gg", "ggp_image_prepare: image prepared "
+ "[crc=%u, size=%" G_GSIZE_FORMAT "]",
+ image_crc, image_size);
+
+ *id = ggp_image_params_to_id(image_crc, image_size);
+
+ g_object_ref(image);
+ sent_image = g_new(ggp_image_sent, 1);
+ sent_image->image = image;
+ sent_image->conv_name = g_strdup(purple_conversation_get_name(conv));
+ g_hash_table_insert(sdata->sent_images, ggp_uint64dup(*id),
+ sent_image);
+
+ return GGP_IMAGE_PREPARE_OK;
+}
+
+void ggp_image_recv(PurpleConnection *gc,
+ const struct gg_event_image_reply *image_reply)
+{
+ ggp_image_session_data *sdata = ggp_image_get_sdata(gc);
+ PurpleImage *img;
+ uint64_t id;
+
+ id = ggp_image_params_to_id(image_reply->crc32, image_reply->size);
+ img = g_hash_table_lookup(sdata->recv_images, &id);
+ if (!img) {
+ purple_debug_warning("gg", "ggp_image_recv: "
+ "image " GGP_IMAGE_ID_FORMAT " wasn't requested\n",
+ id);
+ return;
+ }
+
+ purple_debug_info("gg", "ggp_image_recv: got image "
+ "[crc=%u, size=%u, filename=%s, id=" GGP_IMAGE_ID_FORMAT "]",
+ image_reply->crc32, image_reply->size,
+ image_reply->filename, id);
+
+ purple_image_set_friendly_filename(img, image_reply->filename);
+
+ purple_image_transfer_write(img,
+ g_memdup(image_reply->image, image_reply->size),
+ image_reply->size);
+ purple_image_transfer_close(img);
+}
+
+void ggp_image_send(PurpleConnection *gc,
+ const struct gg_event_image_request *image_request)
+{
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+ ggp_image_session_data *sdata = ggp_image_get_sdata(gc);
+ ggp_image_sent *sent_image;
+ PurpleConversation *conv;
+ uint64_t id;
+ gchar *gg_filename;
+
+ purple_debug_info("gg", "ggp_image_send: got image request "
+ "[uin=%u, crc=%u, size=%u]\n",
+ image_request->sender,
+ image_request->crc32,
+ image_request->size);
+
+ id = ggp_image_params_to_id(image_request->crc32, image_request->size);
+
+ sent_image = g_hash_table_lookup(sdata->sent_images, &id);
+
+ if (sent_image == NULL && image_request->sender == ggp_str_to_uin(
+ purple_account_get_username(purple_connection_get_account(gc))))
+ {
+ purple_debug_misc("gg", "ggp_image_send: requested image "
+ "not found, but this may be another session request\n");
+ return;
+ }
+ if (sent_image == NULL) {
+ purple_debug_warning("gg", "ggp_image_send: requested image "
+ "not found\n");
+ return;
+ }
+
+ purple_debug_misc("gg", "ggp_image_send: requested image found "
+ "[id=" GGP_IMAGE_ID_FORMAT ", conv=%s]\n",
+ id, sent_image->conv_name);
+
+ g_return_if_fail(sent_image->image);
+
+ /* TODO: check allowed recipients */
+ gg_filename = g_strdup_printf(GGP_IMAGE_ID_FORMAT, id);
+ gg_image_reply(accdata->session, image_request->sender,
+ gg_filename,
+ purple_image_get_data(sent_image->image),
+ purple_image_get_size(sent_image->image));
+ g_free(gg_filename);
+
+ conv = purple_conversations_find_with_account(
+ sent_image->conv_name,
+ purple_connection_get_account(gc));
+ if (conv != NULL) {
+ gchar *msg = g_strdup_printf(_("Image delivered to %u."),
+ image_request->sender);
+ purple_conversation_write_system_message(conv, msg,
+ PURPLE_MESSAGE_NO_LOG | PURPLE_MESSAGE_NOTIFY);
+ g_free(msg);
+ }
+}
+
+PurpleImage *
+ggp_image_request(PurpleConnection *gc, uin_t uin, uint64_t id)
+{
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+ ggp_image_session_data *sdata = ggp_image_get_sdata(gc);
+ PurpleImage *img;
+ uint32_t crc = id >> 32;
+ uint32_t size = id;
+
+ if (size > GGP_IMAGE_SIZE_MAX && crc <= GGP_IMAGE_SIZE_MAX) {
+ uint32_t tmp;
+ purple_debug_warning("gg", "ggp_image_request: "
+ "crc and size are swapped!\n");
+ tmp = crc;
+ crc = size;
+ size = tmp;
+ id = ggp_image_params_to_id(crc, size);
+ }
+
+ img = g_hash_table_lookup(sdata->recv_images, &id);
+ if (img) {
+ purple_debug_info("gg", "ggp_image_request: "
+ "image " GGP_IMAGE_ID_FORMAT " got from cache", id);
+ return img;
+ }
+
+
+ img = purple_image_transfer_new();
+ g_hash_table_insert(sdata->recv_images, ggp_uint64dup(id), img);
+
+ purple_debug_info("gg", "ggp_image_request: requesting image "
+ GGP_IMAGE_ID_FORMAT, id);
+ if (gg_image_request(accdata->session, uin, size, crc) != 0)
+ purple_debug_error("gg", "ggp_image_request: failed");
+
+ return img;
+}
diff --git a/libpurple/protocols/gg/image-prpl.h b/libpurple/protocols/gg/image-prpl.h
new file mode 100644
index 0000000000..666fed047a
--- /dev/null
+++ b/libpurple/protocols/gg/image-prpl.h
@@ -0,0 +1,68 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 _GGP_IMAGE_H
+#define _GGP_IMAGE_H
+
+#include <internal.h>
+#include <libgadu.h>
+
+#define GGP_IMAGE_SIZE_MAX 255000
+#define GGP_IMAGE_ID_FORMAT "%016" G_GINT64_MODIFIER "x"
+
+typedef struct _ggp_image_session_data ggp_image_session_data;
+
+typedef enum
+{
+ GGP_IMAGE_PREPARE_OK = 0,
+ GGP_IMAGE_PREPARE_FAILURE,
+ GGP_IMAGE_PREPARE_TOO_BIG
+} ggp_image_prepare_result;
+
+void
+ggp_image_setup(PurpleConnection *gc);
+
+void
+ggp_image_cleanup(PurpleConnection *gc);
+
+ggp_image_prepare_result
+ggp_image_prepare(PurpleConversation *conv, PurpleImage *image, uint64_t *id);
+
+void
+ggp_image_recv(PurpleConnection *gc,
+ const struct gg_event_image_reply *image_reply);
+
+void
+ggp_image_send(PurpleConnection *gc,
+ const struct gg_event_image_request *image_request);
+
+PurpleImage *
+ggp_image_request(PurpleConnection *gc, uin_t uin, uint64_t id);
+
+#endif /* _GGP_IMAGE_H */
diff --git a/libpurple/protocols/gg/keymapper.c b/libpurple/protocols/gg/keymapper.c
new file mode 100644
index 0000000000..d7acb3f7e3
--- /dev/null
+++ b/libpurple/protocols/gg/keymapper.c
@@ -0,0 +1,106 @@
+/* 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.
+ *
+ * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
+ * As a recipient of this file you may choose, which license to receive the
+ * code under. As a contributor, you have to ensure the new code is
+ * compatible with both.
+ *
+ * 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 "keymapper.h"
+
+/* The problem: we want to convert 64-bit unique integers into unique gpointer
+ * keys (that may be 32-bit or 64-bit, or whatever else). We also want to
+ * convert it back.
+ *
+ * The idea: let's store every value in our internal memory. Then, its address
+ * can be also an unique key. We also need a map, to quickly figure out the
+ * address for any previously stored value.
+ *
+ * The naming problem: values becames the keys and vice-versa.
+ */
+
+/* TODO
+ * For a 64-bit gpointer, keymapper could just do nothing and return the value
+ * as a key. But it have to be figured out at a compile time.
+ */
+
+struct _ggp_keymapper
+{
+ /* Table keys: pointers to 64-bit mapped *values*.
+ * Table values: keys (gpointers) corresponding to mapped values.
+ *
+ * Ultimately, both keys and values are the same pointers.
+ *
+ * Yes, it's hard to comment it well enough.
+ */
+ GHashTable *val_to_key;
+};
+
+ggp_keymapper *
+ggp_keymapper_new(void)
+{
+ ggp_keymapper *km;
+
+ km = g_new0(ggp_keymapper, 1);
+ km->val_to_key = g_hash_table_new_full(g_int64_hash, g_int64_equal,
+ g_free, NULL);
+
+ return km;
+}
+
+void
+ggp_keymapper_free(ggp_keymapper *km)
+{
+ if (km == NULL)
+ return;
+
+ g_hash_table_destroy(km->val_to_key);
+ g_free(km);
+}
+
+gpointer
+ggp_keymapper_to_key(ggp_keymapper *km, guint64 val)
+{
+ guint64 *key;
+
+ g_return_val_if_fail(km != NULL, NULL);
+
+ key = g_hash_table_lookup(km->val_to_key, &val);
+ if (key)
+ return key;
+
+ key = g_new(guint64, 1);
+ *key = val;
+
+ g_hash_table_insert(km->val_to_key, key, key);
+
+ return key;
+}
+
+guint64
+ggp_keymapper_from_key(ggp_keymapper *km, gpointer key)
+{
+ g_return_val_if_fail(km != NULL, 0);
+ g_return_val_if_fail(key != NULL, 0);
+
+ return *((guint64*)key);
+}
diff --git a/libpurple/protocols/gg/keymapper.h b/libpurple/protocols/gg/keymapper.h
new file mode 100644
index 0000000000..63edae36d9
--- /dev/null
+++ b/libpurple/protocols/gg/keymapper.h
@@ -0,0 +1,48 @@
+/* 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.
+ *
+ * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
+ * As a recipient of this file you may choose, which license to receive the
+ * code under. As a contributor, you have to ensure the new code is
+ * compatible with both.
+ *
+ * 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 _GGP_KEYMAPPER_H
+#define _GGP_KEYMAPPER_H
+
+typedef struct _ggp_keymapper ggp_keymapper;
+
+#include <purple.h>
+
+ggp_keymapper *
+ggp_keymapper_new(void);
+
+void
+ggp_keymapper_free(ggp_keymapper *km);
+
+gpointer
+ggp_keymapper_to_key(ggp_keymapper *km, guint64 val);
+
+/* The key have to be valid. */
+guint64
+ggp_keymapper_from_key(ggp_keymapper *km, gpointer key);
+
+#endif /* _GGP_KEYMAPPER_H */
diff --git a/libpurple/protocols/gg/lib/config.h b/libpurple/protocols/gg/lib/config.h
index 3a5dbe3788..f036b5cefb 100644
--- a/libpurple/protocols/gg/lib/config.h
+++ b/libpurple/protocols/gg/lib/config.h
@@ -31,7 +31,7 @@
/* Defined if this machine has C99-compiliant vsnprintf(). */
#undef HAVE_C99_VSNPRINTF
-#ifndef _WIN32
+#if !defined(_WIN32) || (defined(__MINGW64_VERSION_MAJOR) && __MINGW64_VERSION_MAJOR >= 3)
# define HAVE_C99_VSNPRINTF
#endif
diff --git a/libpurple/protocols/gg/libgadu-events.c b/libpurple/protocols/gg/libgadu-events.c
new file mode 100644
index 0000000000..4fedbc57bd
--- /dev/null
+++ b/libpurple/protocols/gg/libgadu-events.c
@@ -0,0 +1,124 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 "libgadu-events.h"
+
+#include <debug.h>
+
+#include "avatar.h"
+#include "edisc.h"
+
+void ggp_events_user_data(PurpleConnection *gc, struct gg_event_user_data *data)
+{
+ guint user_idx;
+ gboolean is_update;
+
+ purple_debug_info("gg", "GG_EVENT_USER_DATA [type=%d, user_count=%"
+ G_GSIZE_FORMAT "]\n", data->type, data->user_count);
+
+ /*
+ type =
+ 1, 3: user information sent after connecting (divided by
+ 20 contacts; 3 - last one; 1 - rest of them)
+ 0: data update
+ */
+ is_update = (data->type == 0);
+
+ for (user_idx = 0; user_idx < data->user_count; user_idx++) {
+ struct gg_event_user_data_user *data_user =
+ &data->users[user_idx];
+ uin_t uin = data_user->uin;
+ guint attr_idx;
+ gboolean got_avatar = FALSE;
+ for (attr_idx = 0; attr_idx < data_user->attr_count; attr_idx++) {
+ struct gg_event_user_data_attr *data_attr =
+ &data_user->attrs[attr_idx];
+ if (strcmp(data_attr->key, "avatar") == 0) {
+ time_t timestamp;
+ if (data_attr->type == 0) {
+ ggp_avatar_buddy_remove(gc, uin);
+ continue;
+ }
+
+ timestamp = atoi(data_attr->value);
+ if (timestamp <= 0)
+ continue;
+ got_avatar = TRUE;
+ ggp_avatar_buddy_update(gc, uin, timestamp);
+ }
+ }
+
+ if (!is_update && !got_avatar)
+ ggp_avatar_buddy_remove(gc, uin);
+ }
+}
+
+#if GGP_ENABLE_GG11
+static void ggp_events_new_version(const gchar *data)
+{
+ /* data = {"severity":"download"} */
+ purple_debug_info("gg", "Gadu-Gadu server reports new client version."
+ " %s", data);
+}
+
+void ggp_events_json(PurpleConnection *gc, struct gg_event_json_event *ev)
+{
+ static const gchar *ignored_events[] = {
+ "edisc/scope_files_changed",
+ "notifications/state",
+ "invitations/list",
+ "notifications/list", /* gifts */
+ NULL
+ };
+ const gchar **it;
+
+ if (g_strcmp0("edisc/send_ticket_changed", ev->type) == 0) {
+ ggp_edisc_xfer_ticket_changed(gc, ev->data);
+ return;
+ }
+
+ if (g_strcmp0("updates/new-version", ev->type) == 0) {
+ ggp_events_new_version(ev->data);
+ return;
+ }
+
+ for (it = ignored_events; *it != NULL; it++) {
+ if (g_strcmp0(*it, ev->type) == 0)
+ return;
+ }
+
+ if (purple_debug_is_unsafe() && purple_debug_is_verbose())
+ purple_debug_warning("gg", "ggp_events_json: "
+ "unhandled event \"%s\": %s\n",
+ ev->type, ev->data);
+ else
+ purple_debug_warning("gg", "ggp_events_json: "
+ "unhandled event \"%s\"\n", ev->type);
+}
+#endif
diff --git a/libpurple/protocols/gg/libgadu-events.h b/libpurple/protocols/gg/libgadu-events.h
new file mode 100644
index 0000000000..ca1f6ec726
--- /dev/null
+++ b/libpurple/protocols/gg/libgadu-events.h
@@ -0,0 +1,45 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 _GGP_LIBGADU_EVENTS_H
+#define _GGP_LIBGADU_EVENTS_H
+
+#include <internal.h>
+#include <libgadu.h>
+
+#include "gg.h"
+
+void ggp_events_user_data(PurpleConnection *gc,
+ struct gg_event_user_data *data);
+
+#if GGP_ENABLE_GG11
+void ggp_events_json(PurpleConnection *gc, struct gg_event_json_event *ev);
+#endif
+
+#endif /* _GGP_LIBGADU_EVENTS_H */
diff --git a/libpurple/protocols/gg/libgaduw.c b/libpurple/protocols/gg/libgaduw.c
new file mode 100644
index 0000000000..11dfc77a4f
--- /dev/null
+++ b/libpurple/protocols/gg/libgaduw.c
@@ -0,0 +1,209 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 "libgaduw.h"
+
+#include <debug.h>
+
+#include "purplew.h"
+#include "gg.h"
+
+static void ggp_libgaduw_debug_handler(int level, const char * format,
+ va_list args);
+
+/*******************************************************************************
+ * Setup/cleanup.
+ ******************************************************************************/
+
+void ggp_libgaduw_setup(void)
+{
+ gg_debug_handler = ggp_libgaduw_debug_handler;
+}
+
+void ggp_libgaduw_cleanup(void)
+{
+ gg_debug_handler = NULL;
+}
+
+/*******************************************************************************
+ * General.
+ ******************************************************************************/
+
+const gchar * ggp_libgaduw_version(PurpleConnection *gc)
+{
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+ const gchar *ver = accdata->session->client_version;
+
+ if (ver != NULL && isdigit(ver[0]))
+ return ver;
+ return GG_DEFAULT_CLIENT_VERSION;
+}
+
+static void ggp_libgaduw_debug_handler(int level, const char * format,
+ va_list args)
+{
+ PurpleDebugLevel purple_level;
+ char *msg;
+
+ if ((level & GG_DEBUG_NET) ||
+ (level & GG_DEBUG_FUNCTION)
+#if GGP_ENABLE_GG11
+ || (level & GG_DEBUG_VERBOSE)
+#endif
+ )
+ {
+ if (!purple_debug_is_verbose())
+ return;
+ }
+
+ if ((level & GG_DEBUG_DUMP) || /* GG session protocol packets */
+ (level & GG_DEBUG_TRAFFIC)) /* HTTP traffic */
+ {
+ if (!purple_debug_is_verbose() || !purple_debug_is_unsafe())
+ return;
+ }
+
+ msg = g_strdup_vprintf(format, args);
+
+#if GGP_ENABLE_GG11
+ if (level & GG_DEBUG_ERROR)
+ purple_level = PURPLE_DEBUG_ERROR;
+ else if (level & GG_DEBUG_WARNING)
+ purple_level = PURPLE_DEBUG_WARNING;
+ else
+#endif
+ purple_level = PURPLE_DEBUG_MISC;
+
+ purple_debug(purple_level, "gg", "%s", msg);
+ g_free(msg);
+}
+
+/*******************************************************************************
+ * HTTP requests.
+ ******************************************************************************/
+
+static void ggp_libgaduw_http_processing_cancel(PurpleConnection *gc,
+ void *_req);
+
+static void ggp_libgaduw_http_handler(gpointer _req, gint fd,
+ PurpleInputCondition cond);
+
+static void ggp_libgaduw_http_finish(ggp_libgaduw_http_req *req,
+ gboolean success);
+
+/******************************************************************************/
+
+ggp_libgaduw_http_req * ggp_libgaduw_http_watch(PurpleConnection *gc,
+ struct gg_http *h, ggp_libgaduw_http_cb cb,
+ gpointer user_data, gboolean show_processing)
+{
+ ggp_libgaduw_http_req *req;
+ purple_debug_misc("gg", "ggp_libgaduw_http_watch(h=%p, "
+ "show_processing=%d)\n", h, show_processing);
+
+ req = g_new(ggp_libgaduw_http_req, 1);
+ req->user_data = user_data;
+ req->cb = cb;
+ req->cancelled = FALSE;
+ req->h = h;
+ req->processing = NULL;
+ if (show_processing)
+ req->processing = ggp_purplew_request_processing(gc, NULL,
+ req, ggp_libgaduw_http_processing_cancel);
+ req->inpa = ggp_purplew_http_input_add(h, ggp_libgaduw_http_handler,
+ req);
+
+ return req;
+}
+
+static void ggp_libgaduw_http_processing_cancel(PurpleConnection *gc,
+ void *_req)
+{
+ ggp_libgaduw_http_req *req = _req;
+ req->processing = NULL;
+ ggp_libgaduw_http_cancel(req);
+}
+
+static void ggp_libgaduw_http_handler(gpointer _req, gint fd,
+ PurpleInputCondition cond)
+{
+ ggp_libgaduw_http_req *req = _req;
+
+ if (req->h->callback(req->h) == -1 || req->h->state == GG_STATE_ERROR) {
+ purple_debug_error("gg", "ggp_libgaduw_http_handler: failed to "
+ "make http request: %d\n", req->h->error);
+ ggp_libgaduw_http_finish(req, FALSE);
+ return;
+ }
+
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("gg", "ggp_libgaduw_http_handler: got fd "
+ "update [check=%d, state=%d]\n", req->h->check,
+ req->h->state);
+ }
+
+ if (req->h->state != GG_STATE_DONE) {
+ purple_input_remove(req->inpa);
+ req->inpa = ggp_purplew_http_input_add(req->h,
+ ggp_libgaduw_http_handler, req);
+ return;
+ }
+
+ if (!req->h->data || !req->h->body) {
+ purple_debug_error("gg", "ggp_libgaduw_http_handler: got empty "
+ "http response: %d\n", req->h->error);
+ ggp_libgaduw_http_finish(req, FALSE);
+ return;
+ }
+
+ ggp_libgaduw_http_finish(req, TRUE);
+}
+
+void ggp_libgaduw_http_cancel(ggp_libgaduw_http_req *req)
+{
+ purple_debug_misc("gg", "ggp_libgaduw_http_cancel\n");
+ req->cancelled = TRUE;
+ gg_http_stop(req->h);
+ ggp_libgaduw_http_finish(req, FALSE);
+}
+
+static void ggp_libgaduw_http_finish(ggp_libgaduw_http_req *req,
+ gboolean success)
+{
+ purple_debug_misc("gg", "ggp_libgaduw_http_finish(h=%p, processing=%p):"
+ " success=%d\n", req->h, req->processing, success);
+ if (req->processing) {
+ ggp_purplew_request_processing_done(req->processing);
+ req->processing = NULL;
+ }
+ purple_input_remove(req->inpa);
+ req->cb(req->h, success, req->cancelled, req->user_data);
+ req->h->destroy(req->h);
+ g_free(req);
+}
diff --git a/libpurple/protocols/gg/libgaduw.h b/libpurple/protocols/gg/libgaduw.h
new file mode 100644
index 0000000000..f913076593
--- /dev/null
+++ b/libpurple/protocols/gg/libgaduw.h
@@ -0,0 +1,62 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 _GGP_LIBGADUW_H
+#define _GGP_LIBGADUW_H
+
+#include <internal.h>
+#include <libgadu.h>
+
+#include "purplew.h"
+
+typedef void (*ggp_libgaduw_http_cb)(struct gg_http *h, gboolean success,
+ gboolean cancelled, gpointer user_data);
+
+typedef struct
+{
+ gpointer user_data;
+ ggp_libgaduw_http_cb cb;
+
+ gboolean cancelled;
+ struct gg_http *h;
+ ggp_purplew_request_processing_handle *processing;
+ guint inpa;
+} ggp_libgaduw_http_req;
+
+void ggp_libgaduw_setup(void);
+void ggp_libgaduw_cleanup(void);
+
+const gchar * ggp_libgaduw_version(PurpleConnection *gc);
+
+ggp_libgaduw_http_req * ggp_libgaduw_http_watch(PurpleConnection *gc,
+ struct gg_http *h, ggp_libgaduw_http_cb cb, gpointer user_data,
+ gboolean show_processing);
+void ggp_libgaduw_http_cancel(ggp_libgaduw_http_req *req);
+
+#endif /* _GGP_LIBGADUW_H */
diff --git a/libpurple/protocols/gg/message-prpl.c b/libpurple/protocols/gg/message-prpl.c
new file mode 100644
index 0000000000..63bc94c54c
--- /dev/null
+++ b/libpurple/protocols/gg/message-prpl.c
@@ -0,0 +1,686 @@
+/* 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.
+ *
+ * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
+ * As a recipient of this file you may choose, which license to receive the
+ * code under. As a contributor, you have to ensure the new code is
+ * compatible with both.
+ *
+ * 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 "message-prpl.h"
+
+#include <debug.h>
+#include <glibcompat.h>
+#include <image-store.h>
+
+#include "gg.h"
+#include "chat.h"
+#include "utils.h"
+#include "html.h"
+
+#define GGP_GG10_DEFAULT_FORMAT "<span style=\"color:#000000; " \
+ "font-family:'MS Shell Dlg 2'; font-size:9pt; \">"
+#define GGP_GG10_DEFAULT_FORMAT_REPLACEMENT "<span>"
+#define GGP_GG11_FORCE_COMPAT FALSE
+
+typedef struct
+{
+ enum
+ {
+ GGP_MESSAGE_GOT_TYPE_IM,
+ GGP_MESSAGE_GOT_TYPE_CHAT,
+ GGP_MESSAGE_GOT_TYPE_MULTILOGON
+ } type;
+
+ uin_t user;
+ gchar *text;
+ time_t time;
+ uint64_t chat_id;
+
+ PurpleConnection *gc;
+} ggp_message_got_data;
+
+typedef struct
+{
+ GRegex *re_html_tag;
+ GRegex *re_gg_img;
+} ggp_message_global_data;
+
+static ggp_message_global_data global_data;
+
+struct _ggp_message_session_data
+{
+};
+
+typedef struct
+{
+ int size;
+ gchar *face;
+ int color, bgcolor;
+ gboolean b, i, u, s;
+} ggp_font;
+
+static ggp_font * ggp_font_new(void);
+static ggp_font * ggp_font_clone(ggp_font *font);
+static void ggp_font_free(gpointer font);
+
+static PurpleIMConversation * ggp_message_get_conv(PurpleConnection *gc,
+ uin_t uin);
+static void ggp_message_got_data_free(ggp_message_got_data *msg);
+static void ggp_message_got_display(PurpleConnection *gc,
+ ggp_message_got_data *msg);
+static void ggp_message_format_from_gg(ggp_message_got_data *msg,
+ const gchar *text);
+
+/**************/
+
+void ggp_message_setup_global(void)
+{
+ global_data.re_html_tag = g_regex_new(
+ "<(/)?([a-zA-Z]+)( [^>]+)?>",
+ G_REGEX_OPTIMIZE, 0, NULL);
+ global_data.re_gg_img = g_regex_new(
+ "<img name=\"([0-9a-fA-F]+)\"/?>",
+ G_REGEX_OPTIMIZE, 0, NULL);
+}
+
+void ggp_message_cleanup_global(void)
+{
+ g_regex_unref(global_data.re_html_tag);
+ g_regex_unref(global_data.re_gg_img);
+}
+
+static inline ggp_message_session_data *
+ggp_message_get_sdata(PurpleConnection *gc)
+{
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+ return accdata->message_data;
+}
+
+void ggp_message_setup(PurpleConnection *gc)
+{
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+ ggp_message_session_data *sdata = g_new0(ggp_message_session_data, 1);
+
+ accdata->message_data = sdata;
+}
+
+void ggp_message_cleanup(PurpleConnection *gc)
+{
+ ggp_message_session_data *sdata = ggp_message_get_sdata(gc);
+
+ g_free(sdata);
+}
+
+static ggp_font * ggp_font_new(void)
+{
+ ggp_font *font;
+
+ font = g_new0(ggp_font, 1);
+ font->color = -1;
+ font->bgcolor = -1;
+
+ return font;
+}
+
+static ggp_font * ggp_font_clone(ggp_font * font)
+{
+ ggp_font *clone = g_new0(ggp_font, 1);
+
+ *clone = *font;
+ clone->face = g_strdup(font->face);
+
+ return clone;
+}
+
+static void ggp_font_free(gpointer _font)
+{
+ ggp_font *font = _font;
+
+ g_free(font->face);
+ g_free(font);
+}
+
+/**/
+
+static PurpleIMConversation * ggp_message_get_conv(PurpleConnection *gc,
+ uin_t uin)
+{
+ PurpleAccount *account = purple_connection_get_account(gc);
+ PurpleIMConversation *im;
+ const gchar *who = ggp_uin_to_str(uin);
+
+ im = purple_conversations_find_im_with_account(who, account);
+ if (im)
+ return im;
+ im = purple_im_conversation_new(account, who);
+ return im;
+}
+
+static void ggp_message_got_data_free(ggp_message_got_data *msg)
+{
+ g_free(msg->text);
+ g_free(msg);
+}
+
+void ggp_message_got(PurpleConnection *gc, const struct gg_event_msg *ev)
+{
+ ggp_message_got_data *msg = g_new0(ggp_message_got_data, 1);
+
+ msg->gc = gc;
+ msg->time = ev->time;
+ msg->user = ev->sender;
+
+#if GGP_ENABLE_GG11
+ if (ev->chat_id != 0) {
+ msg->type = GGP_MESSAGE_GOT_TYPE_CHAT;
+ msg->chat_id = ev->chat_id;
+ } else
+#endif
+ {
+ msg->type = GGP_MESSAGE_GOT_TYPE_IM;
+ }
+
+ ggp_message_format_from_gg(msg, ev->xhtml_message);
+
+ ggp_message_got_display(gc, msg);
+ ggp_message_got_data_free(msg);
+}
+
+void ggp_message_got_multilogon(PurpleConnection *gc,
+ const struct gg_event_msg *ev)
+{
+ ggp_message_got_data *msg = g_new0(ggp_message_got_data, 1);
+
+ msg->gc = gc;
+ msg->time = ev->time;
+ msg->user = ev->sender; /* not really a sender*/
+
+#if GGP_ENABLE_GG11
+ if (ev->chat_id != 0) {
+ msg->type = GGP_MESSAGE_GOT_TYPE_CHAT;
+ msg->chat_id = ev->chat_id;
+ } else
+#endif
+ {
+ msg->type = GGP_MESSAGE_GOT_TYPE_MULTILOGON;
+ }
+
+ ggp_message_format_from_gg(msg, ev->xhtml_message);
+
+ ggp_message_got_display(gc, msg);
+ ggp_message_got_data_free(msg);
+}
+
+static void ggp_message_got_display(PurpleConnection *gc,
+ ggp_message_got_data *msg)
+{
+ if (msg->type == GGP_MESSAGE_GOT_TYPE_IM) {
+ purple_serv_got_im(gc, ggp_uin_to_str(msg->user), msg->text,
+ PURPLE_MESSAGE_RECV, msg->time);
+ }
+#if GGP_ENABLE_GG11
+ else if (msg->type == GGP_MESSAGE_GOT_TYPE_CHAT) {
+ ggp_chat_got_message(gc, msg->chat_id, msg->text, msg->time,
+ msg->user);
+ }
+#endif
+ else if (msg->type == GGP_MESSAGE_GOT_TYPE_MULTILOGON) {
+ PurpleIMConversation *im = ggp_message_get_conv(gc, msg->user);
+ PurpleMessage *pmsg;
+
+ pmsg = purple_message_new_outgoing(NULL, msg->text, 0);
+ purple_message_set_time(pmsg, msg->time);
+
+ purple_conversation_write_message(PURPLE_CONVERSATION(im), pmsg);
+ } else
+ purple_debug_error("gg", "ggp_message_got_display: "
+ "unexpected message type: %d\n", msg->type);
+}
+
+static gboolean ggp_message_format_from_gg_found_img(const GMatchInfo *info,
+ GString *res, gpointer data)
+{
+ ggp_message_got_data *msg = data;
+ gchar *name, *replacement;
+ int64_t id;
+ PurpleImage *image;
+ guint image_id;
+
+ name = g_match_info_fetch(info, 1);
+ if (sscanf(name, "%" G_GINT64_MODIFIER "x", &id) != 1)
+ id = 0;
+ g_free(name);
+ if (!id) {
+ /* TODO: stock broken image? */
+ g_string_append_printf(res, "[%s]", _("broken image"));
+ return FALSE;
+ }
+
+ image = ggp_image_request(msg->gc, msg->user, id);
+ if (!image) {
+ purple_debug_warning("gg", "ggp_message_format_from_gg_"
+ "found_img: couldn't request image");
+ g_string_append_printf(res, "[%s]", _("broken image"));
+ return FALSE;
+ }
+
+ image_id = purple_image_store_add_weak(image);
+ replacement = g_strdup_printf("<img src=\""
+ PURPLE_IMAGE_STORE_PROTOCOL "%u\">", image_id);
+ g_string_append(res, replacement);
+ g_free(replacement);
+
+ return FALSE;
+}
+
+static void ggp_message_format_from_gg(ggp_message_got_data *msg,
+ const gchar *text)
+{
+ gchar *text_new, *tmp;
+
+ if (text == NULL) {
+ msg->text = g_strdup("");
+ return;
+ }
+
+ text_new = g_strdup(text);
+ purple_str_strip_char(text_new, '\r');
+
+ tmp = text_new;
+ text_new = purple_strreplace(text_new, GGP_GG10_DEFAULT_FORMAT,
+ GGP_GG10_DEFAULT_FORMAT_REPLACEMENT);
+ g_free(tmp);
+
+ tmp = text_new;
+ text_new = g_regex_replace_eval(global_data.re_gg_img, text_new, -1, 0,
+ 0, ggp_message_format_from_gg_found_img, msg, NULL);
+ g_free(tmp);
+
+ msg->text = text_new;
+}
+
+gchar * ggp_message_format_to_gg(PurpleConversation *conv, const gchar *text)
+{
+ gchar *text_new, *tmp;
+ GList *rt = NULL; /* reformatted text */
+ GMatchInfo *match;
+ guint pos = 0;
+ GList *pending_objects = NULL;
+ GList *font_stack = NULL;
+ static int html_sizes_pt[7] = { 7, 8, 9, 10, 12, 14, 16 };
+
+ ggp_font *font_new, *font_current, *font_base;
+ gboolean font_changed = FALSE;
+ gboolean in_any_tag = FALSE;
+
+ if (purple_debug_is_verbose())
+ purple_debug_info("gg", "ggp formatting text: [%s]", text);
+
+ /* default font */
+ font_base = ggp_font_new();
+ font_current = ggp_font_new();
+ font_new = ggp_font_new();
+
+ /* GG11 doesn't use nbsp, it just print spaces */
+ text_new = purple_strreplace(text, "&nbsp;", " ");
+
+ /* add end-of-message tag */
+ if (strstr(text_new, "<eom>") != NULL) {
+ tmp = text_new;
+ text_new = purple_strreplace(text_new, "<eom>", "");
+ g_free(tmp);
+ purple_debug_warning("gg", "ggp_message_format_to_gg: "
+ "unexpected <eom> tag\n");
+ }
+ tmp = text_new;
+ text_new = g_strdup_printf("%s<eom></eom>", text_new);
+ g_free(tmp);
+
+ g_regex_match(global_data.re_html_tag, text_new, 0, &match);
+ while (g_match_info_matches(match)) {
+ int m_start, m_end, m_pos;
+ gboolean tag_close;
+ gchar *tag_str, *attribs_str;
+ ggp_html_tag tag;
+ gboolean text_before;
+
+ /* reading tag and its contents */
+ g_match_info_fetch_pos(match, 0, &m_start, &m_end);
+ g_assert(m_start >= 0 && m_end >= 0);
+ text_before = ((guint)m_start > pos);
+ g_match_info_fetch_pos(match, 1, &m_pos, NULL);
+ tag_close = (m_pos >= 0);
+ tag_str = g_match_info_fetch(match, 2);
+ tag = ggp_html_parse_tag(tag_str);
+ attribs_str = g_match_info_fetch(match, 3);
+ g_match_info_next(match, NULL);
+
+ if (tag == GGP_HTML_TAG_UNKNOWN) {
+ purple_debug_warning("gg", "ggp_message_format_to_gg: "
+ "uknown tag %s\n", tag_str);
+ }
+
+ /* closing *all* formatting-related tags (GG11 weirness)
+ * and adding pending objects */
+ if ((text_before && (font_changed || pending_objects)) ||
+ (tag == GGP_HTML_TAG_EOM && tag_close))
+ {
+ font_changed = FALSE;
+ if (in_any_tag) {
+ in_any_tag = FALSE;
+ if (font_current->s && !GGP_GG11_FORCE_COMPAT)
+ rt = g_list_prepend(rt,
+ g_strdup("</s>"));
+ if (font_current->u)
+ rt = g_list_prepend(rt,
+ g_strdup("</u>"));
+ if (font_current->i)
+ rt = g_list_prepend(rt,
+ g_strdup("</i>"));
+ if (font_current->b)
+ rt = g_list_prepend(rt,
+ g_strdup("</b>"));
+ rt = g_list_prepend(rt, g_strdup("</span>"));
+ }
+ if (pending_objects) {
+ rt = g_list_concat(pending_objects, rt);
+ pending_objects = NULL;
+ }
+ }
+
+ /* opening formatting-related tags again */
+ if (text_before && !in_any_tag) {
+ gchar *style;
+ GList *styles = NULL;
+ gboolean has_size = (font_new->size > 0 &&
+ font_new->size <= 7 && font_new->size != 3);
+
+ if (has_size)
+ styles = g_list_append(styles, g_strdup_printf(
+ "font-size:%dpt;",
+ html_sizes_pt[font_new->size - 1]));
+ if (font_new->face)
+ styles = g_list_append(styles, g_strdup_printf(
+ "font-family:%s;", font_new->face));
+ if (font_new->bgcolor >= 0 && !GGP_GG11_FORCE_COMPAT)
+ styles = g_list_append(styles, g_strdup_printf(
+ "background-color:#%06x;",
+ font_new->bgcolor));
+ if (font_new->color >= 0)
+ styles = g_list_append(styles, g_strdup_printf(
+ "color:#%06x;", font_new->color));
+
+ if (styles) {
+ gchar *combined = ggp_strjoin_list(" ", styles);
+ g_list_free_full(styles, g_free);
+ style = g_strdup_printf(" style=\"%s\"",
+ combined);
+ g_free(combined);
+ } else
+ style = g_strdup("");
+ rt = g_list_prepend(rt, g_strdup_printf("<span%s>",
+ style));
+ g_free(style);
+
+ if (font_new->b)
+ rt = g_list_prepend(rt, g_strdup("<b>"));
+ if (font_new->i)
+ rt = g_list_prepend(rt, g_strdup("<i>"));
+ if (font_new->u)
+ rt = g_list_prepend(rt, g_strdup("<u>"));
+ if (font_new->s && !GGP_GG11_FORCE_COMPAT)
+ rt = g_list_prepend(rt, g_strdup("<s>"));
+
+ ggp_font_free(font_current);
+ font_current = font_new;
+ font_new = ggp_font_clone(font_current);
+
+ in_any_tag = TRUE;
+ }
+ if (text_before) {
+ rt = g_list_prepend(rt,
+ g_strndup(text_new + pos, m_start - pos));
+ }
+
+ /* set formatting of a following text */
+ if (tag == GGP_HTML_TAG_B) {
+ font_changed |= (font_new->b != !tag_close);
+ font_new->b = !tag_close;
+ } else if (tag == GGP_HTML_TAG_I) {
+ font_changed |= (font_new->i != !tag_close);
+ font_new->i = !tag_close;
+ } else if (tag == GGP_HTML_TAG_U) {
+ font_changed |= (font_new->u != !tag_close);
+ font_new->u = !tag_close;
+ } else if (tag == GGP_HTML_TAG_S) {
+ font_changed |= (font_new->s != !tag_close);
+ font_new->s = !tag_close;
+ } else if (tag == GGP_HTML_TAG_IMG && !tag_close) {
+ GHashTable *attribs = ggp_html_tag_attribs(attribs_str);
+ gchar *val = NULL;
+ uint64_t id;
+ ggp_image_prepare_result res = -1;
+ PurpleImage *image = NULL;
+
+ val = g_hash_table_lookup(attribs, "src");
+ if (val)
+ image = purple_image_store_get_from_uri(val);
+
+ if (image != NULL)
+ res = ggp_image_prepare(conv, image, &id);
+
+ if (res == GGP_IMAGE_PREPARE_OK) {
+ pending_objects = g_list_prepend(
+ pending_objects, g_strdup_printf(
+ "<img name=\"" GGP_IMAGE_ID_FORMAT
+ "\">", id));
+ } else if (res == GGP_IMAGE_PREPARE_TOO_BIG) {
+ purple_conversation_write_system_message(conv,
+ _("Image is too large, please try "
+ "smaller one."), PURPLE_MESSAGE_ERROR);
+ } else {
+ purple_conversation_write_system_message(conv,
+ _("Image cannot be sent."),
+ PURPLE_MESSAGE_ERROR);
+ }
+
+ g_hash_table_destroy(attribs);
+ } else if (tag == GGP_HTML_TAG_FONT && !tag_close) {
+ GHashTable *attribs = ggp_html_tag_attribs(attribs_str);
+ gchar *val = NULL;
+
+ font_stack = g_list_prepend(font_stack,
+ ggp_font_clone(font_new));
+
+ if ((val = g_hash_table_lookup(attribs, "size")) != NULL
+ && val[0] >= '1' && val[0] <= '7' &&
+ val[1] == '\0')
+ {
+ int size = val[0] - '0';
+ font_changed |= (font_new->size != size);
+ font_new->size = size;
+ }
+
+ if ((val = g_hash_table_lookup(attribs, "face"))
+ != NULL)
+ {
+ font_changed |=
+ (g_strcmp0(font_new->face, val) != 0);
+ g_free(font_new->face);
+ font_new->face = g_strdup(val);
+ }
+
+ if ((val = g_hash_table_lookup(attribs, "color"))
+ != NULL && val[0] == '#' && strlen(val) == 7)
+ {
+ int color = ggp_html_decode_color(val);
+ font_changed |= (font_new->color != color);
+ font_new->color = color;
+ }
+
+ g_hash_table_destroy(attribs);
+ }
+ else if ((tag == GGP_HTML_TAG_SPAN || tag == GGP_HTML_TAG_DIV)
+ && !tag_close)
+ {
+ GHashTable *attribs, *styles = NULL;
+ gchar *style = NULL;
+ gchar *val = NULL;
+
+ attribs = ggp_html_tag_attribs(attribs_str);
+
+ font_stack = g_list_prepend(font_stack,
+ ggp_font_clone(font_new));
+ if (tag == GGP_HTML_TAG_DIV)
+ pending_objects = g_list_prepend(
+ pending_objects, g_strdup("<br>"));
+
+ style = g_hash_table_lookup(attribs, "style");
+ if (style)
+ styles = ggp_html_css_attribs(style);
+
+ if (styles && (val = g_hash_table_lookup(styles,
+ "background-color")) != NULL)
+ {
+ int color = ggp_html_decode_color(val);
+ font_changed |= (font_new->bgcolor != color);
+ font_new->bgcolor = color;
+ }
+
+ if (styles && (val = g_hash_table_lookup(styles,
+ "color")) != NULL)
+ {
+ int color = ggp_html_decode_color(val);
+ font_changed |= (font_new->color != color);
+ font_new->color = color;
+ }
+
+ if (styles)
+ g_hash_table_destroy(styles);
+ g_hash_table_destroy(attribs);
+ }
+ else if ((tag == GGP_HTML_TAG_FONT || tag == GGP_HTML_TAG_SPAN
+ || tag == GGP_HTML_TAG_DIV) && tag_close)
+ {
+ font_changed = TRUE;
+
+ ggp_font_free(font_new);
+ if (font_stack) {
+ font_new = (ggp_font*)font_stack->data;
+ font_stack = g_list_delete_link(
+ font_stack, font_stack);
+ }
+ else
+ font_new = ggp_font_clone(font_base);
+ } else if (tag == GGP_HTML_TAG_BR) {
+ pending_objects = g_list_prepend(pending_objects,
+ g_strdup("<br>"));
+ } else if (tag == GGP_HTML_TAG_HR) {
+ pending_objects = g_list_prepend(pending_objects,
+ g_strdup("<br><span>---</span><br>"));
+ } else if (tag == GGP_HTML_TAG_A || tag == GGP_HTML_TAG_EOM) {
+ /* do nothing */
+ } else if (tag == GGP_HTML_TAG_UNKNOWN) {
+ purple_debug_warning("gg", "ggp_message_format_to_gg: "
+ "uknown tag %s\n", tag_str);
+ } else {
+ purple_debug_error("gg", "ggp_message_format_to_gg: "
+ "not handled tag %s\n", tag_str);
+ }
+
+ pos = m_end;
+ g_free(tag_str);
+ g_free(attribs_str);
+ }
+ g_match_info_free(match);
+
+ if (pos < strlen(text_new) || in_any_tag) {
+ purple_debug_fatal("gg", "ggp_message_format_to_gg: "
+ "end of message not reached\n");
+ }
+
+ /* releasing fonts recources */
+ ggp_font_free(font_new);
+ ggp_font_free(font_current);
+ ggp_font_free(font_base);
+ g_list_free_full(font_stack, ggp_font_free);
+
+ /* combining reformatted text info one string */
+ rt = g_list_reverse(rt);
+ g_free(text_new);
+ text_new = ggp_strjoin_list("", rt);
+ g_list_free_full(rt, g_free);
+
+ if (purple_debug_is_verbose())
+ purple_debug_info("gg", "reformatted text: [%s]", text_new);
+
+ return text_new;
+}
+
+int ggp_message_send_im(PurpleConnection *gc, PurpleMessage *msg)
+{
+ GGPInfo *info = purple_connection_get_protocol_data(gc);
+ PurpleIMConversation *im;
+ ggp_buddy_data *buddy_data;
+ gchar *gg_msg;
+ gboolean succ;
+ const gchar *rcpt = purple_message_get_recipient(msg);
+
+ /* TODO: return -ENOTCONN, if not connected */
+
+ if (purple_message_is_empty(msg))
+ return 0;
+
+ buddy_data = ggp_buddy_get_data(purple_blist_find_buddy(
+ purple_connection_get_account(gc), rcpt));
+
+ if (buddy_data->blocked)
+ return -1;
+
+ im = purple_conversations_find_im_with_account(
+ rcpt, purple_connection_get_account(gc));
+
+ gg_msg = ggp_message_format_to_gg(PURPLE_CONVERSATION(im),
+ purple_message_get_contents(msg));
+
+ /* TODO: splitting messages */
+ if (strlen(gg_msg) > GG_MSG_MAXSIZE) {
+ g_free(gg_msg);
+ return -E2BIG;
+ }
+
+#if GGP_ENABLE_GG11
+ succ = (gg_send_message_html(info->session, GG_CLASS_CHAT,
+ ggp_str_to_uin(rcpt), (unsigned char *)gg_msg) >= 0);
+#else
+ {
+ gchar *plain = purple_markup_strip_html(gg_msg);
+ succ = (gg_send_message(info->session, GG_CLASS_CHAT,
+ ggp_str_to_uin(rcpt), (unsigned char *)plain) >= 0);
+ g_free(plain);
+ }
+#endif
+
+ g_free(gg_msg);
+
+ return succ ? 1 : -1;
+}
diff --git a/libpurple/protocols/gg/message-prpl.h b/libpurple/protocols/gg/message-prpl.h
new file mode 100644
index 0000000000..20744840d8
--- /dev/null
+++ b/libpurple/protocols/gg/message-prpl.h
@@ -0,0 +1,48 @@
+/* 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.
+ *
+ * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
+ * As a recipient of this file you may choose, which license to receive the
+ * code under. As a contributor, you have to ensure the new code is
+ * compatible with both.
+ *
+ * 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 _GGP_MESSAGE_PRPL_H
+#define _GGP_MESSAGE_PRPL_H
+
+#include <internal.h>
+#include <libgadu.h>
+
+typedef struct _ggp_message_session_data ggp_message_session_data;
+
+void ggp_message_setup_global(void);
+void ggp_message_cleanup_global(void);
+void ggp_message_setup(PurpleConnection *gc);
+void ggp_message_cleanup(PurpleConnection *gc);
+
+void ggp_message_got(PurpleConnection *gc, const struct gg_event_msg *ev);
+void ggp_message_got_multilogon(PurpleConnection *gc,
+ const struct gg_event_msg *ev);
+
+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 */
diff --git a/libpurple/protocols/gg/multilogon.c b/libpurple/protocols/gg/multilogon.c
new file mode 100644
index 0000000000..c69cda680e
--- /dev/null
+++ b/libpurple/protocols/gg/multilogon.c
@@ -0,0 +1,255 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 "multilogon.h"
+
+#include <debug.h>
+
+#include "gg.h"
+#include "keymapper.h"
+#include "utils.h"
+#include "message-prpl.h"
+
+typedef struct
+{
+ uint64_t id;
+ uint32_t remote_addr;
+ gchar *name;
+ time_t logon_time;
+} ggp_multilogon_session_info;
+
+struct _ggp_multilogon_session_data
+{
+ int session_count;
+ ggp_multilogon_session_info *sessions;
+ PurpleRequestDatasheet *sheet_handle;
+ gpointer dialog_handle;
+ ggp_keymapper *sid_mapper;
+};
+
+static inline ggp_multilogon_session_data *
+ggp_multilogon_get_mldata(PurpleConnection *gc);
+
+
+static inline ggp_multilogon_session_data *
+ggp_multilogon_get_mldata(PurpleConnection *gc)
+{
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+ return accdata->multilogon_data;
+}
+
+void
+ggp_multilogon_setup(PurpleConnection *gc)
+{
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+
+ ggp_multilogon_session_data *mldata =
+ g_new0(ggp_multilogon_session_data, 1);
+ accdata->multilogon_data = mldata;
+
+ mldata->sid_mapper = ggp_keymapper_new();
+}
+
+static void
+ggp_multilogon_free_sessions(PurpleConnection *gc)
+{
+ ggp_multilogon_session_data *mldata = ggp_multilogon_get_mldata(gc);
+ int i;
+
+ for (i = 0; i < mldata->session_count; i++)
+ g_free(mldata->sessions[i].name);
+ g_free(mldata->sessions);
+
+ mldata->sessions = NULL;
+ mldata->session_count = 0;
+}
+
+void
+ggp_multilogon_cleanup(PurpleConnection *gc)
+{
+ ggp_multilogon_session_data *mldata = ggp_multilogon_get_mldata(gc);
+
+ if (mldata->dialog_handle) {
+ purple_request_close(PURPLE_REQUEST_FIELDS,
+ mldata->dialog_handle);
+ mldata->dialog_handle = NULL;
+ }
+
+ ggp_multilogon_free_sessions(gc);
+ ggp_keymapper_free(mldata->sid_mapper);
+ g_free(mldata);
+}
+
+static uint64_t
+ggp_multilogon_sid_from_libgadu(gg_multilogon_id_t lsid)
+{
+ uint64_t sid;
+
+ memcpy(&sid, lsid.id, sizeof(uint64_t));
+
+ return sid;
+}
+
+static gg_multilogon_id_t
+ggp_multilogon_sid_to_libgadu(uint64_t sid)
+{
+ gg_multilogon_id_t lsid;
+
+ memcpy(lsid.id, &sid, sizeof(uint64_t));
+
+ return lsid;
+}
+
+static void
+ggp_multilogon_fill_sessions(PurpleRequestDatasheet *sheet,
+ PurpleConnection *gc)
+{
+ ggp_multilogon_session_data *mldata = ggp_multilogon_get_mldata(gc);
+ ggp_keymapper *km = mldata->sid_mapper;
+ int i;
+
+ purple_request_datasheet_record_mark_all_for_rem(sheet);
+
+ for (i = 0; i < mldata->session_count; i++) {
+ ggp_multilogon_session_info *sess = &mldata->sessions[i];
+ PurpleRequestDatasheetRecord *rec;
+
+ rec = purple_request_datasheet_record_add(sheet,
+ ggp_keymapper_to_key(km, sess->id));
+
+ purple_request_datasheet_record_set_string_data(rec, 0,
+ ggp_ipv4_to_str(sess->remote_addr));
+ purple_request_datasheet_record_set_string_data(rec, 1,
+ purple_date_format_full(localtime(&sess->logon_time)));
+ purple_request_datasheet_record_set_string_data(rec, 2,
+ sess->name);
+ }
+
+ purple_request_datasheet_record_remove_marked(sheet);
+}
+
+void
+ggp_multilogon_info(PurpleConnection *gc, struct gg_event_multilogon_info *info)
+{
+ ggp_multilogon_session_data *mldata = ggp_multilogon_get_mldata(gc);
+ int i;
+
+ ggp_multilogon_free_sessions(gc);
+
+ purple_debug_info("gg", "ggp_multilogon_info: session list changed "
+ "(count now: %d)\n", info->count);
+
+ mldata->sessions = g_new(ggp_multilogon_session_info, info->count);
+ for (i = 0; i < info->count; i++) {
+ struct gg_multilogon_session *lsess = &info->sessions[i];
+ ggp_multilogon_session_info *psess = &mldata->sessions[i];
+
+ psess->id = ggp_multilogon_sid_from_libgadu(lsess->id);
+ psess->remote_addr = lsess->remote_addr;
+ psess->name = g_strdup(lsess->name);
+ psess->logon_time = lsess->logon_time;
+ }
+
+ mldata->session_count = info->count;
+
+ if (mldata->sheet_handle != NULL)
+ ggp_multilogon_fill_sessions(mldata->sheet_handle, gc);
+}
+
+static void
+ggp_multilogon_disconnect(PurpleRequestDatasheetRecord *rec, gpointer _gc)
+{
+ PurpleConnection *gc = _gc;
+ ggp_multilogon_session_data *mldata = ggp_multilogon_get_mldata(gc);
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+ uint64_t sid;
+ gpointer key;
+
+ key = purple_request_datasheet_record_get_key(rec);
+ sid = ggp_keymapper_from_key(mldata->sid_mapper, key);
+
+ gg_multilogon_disconnect(accdata->session,
+ ggp_multilogon_sid_to_libgadu(sid));
+
+ purple_request_datasheet_record_remove(
+ purple_request_datasheet_record_get_datasheet(rec), key);
+}
+
+void
+ggp_multilogon_dialog(PurpleConnection *gc)
+{
+ ggp_multilogon_session_data *mldata = ggp_multilogon_get_mldata(gc);
+ PurpleRequestField *field;
+ PurpleRequestFields *fields;
+ PurpleRequestFieldGroup *group;
+ PurpleRequestCommonParameters *cpar;
+ PurpleRequestDatasheet *sheet;
+ PurpleRequestDatasheetAction *action;
+ gpointer dialog_handle;
+
+ if (mldata->dialog_handle != NULL)
+ return;
+
+ fields = purple_request_fields_new();
+ group = purple_request_field_group_new(NULL);
+ purple_request_fields_add_group(fields, group);
+
+ sheet = purple_request_datasheet_new();
+ purple_request_datasheet_add_column(sheet,
+ PURPLE_REQUEST_DATASHEET_COLUMN_STRING, _("IP"));
+ purple_request_datasheet_add_column(sheet,
+ PURPLE_REQUEST_DATASHEET_COLUMN_STRING, _("Logon time"));
+ purple_request_datasheet_add_column(sheet,
+ PURPLE_REQUEST_DATASHEET_COLUMN_STRING, _("Session"));
+
+ action = purple_request_datasheet_action_new();
+ purple_request_datasheet_action_set_label(action, _("Disconnect"));
+ purple_request_datasheet_action_set_cb(action,
+ ggp_multilogon_disconnect, gc);
+ purple_request_datasheet_add_action(sheet, action);
+ ggp_multilogon_fill_sessions(sheet, gc);
+
+ field = purple_request_field_datasheet_new("sessions", NULL, sheet);
+ purple_request_field_group_add_field(group, field);
+
+ cpar = purple_request_cpar_new();
+ purple_request_cpar_set_icon(cpar, PURPLE_REQUEST_ICON_DIALOG);
+
+ dialog_handle = purple_request_fields(gc,
+ _("Other Gadu-Gadu sessions"), NULL, NULL, fields,
+ NULL, NULL, _("Close"), NULL,
+ cpar, NULL);
+ mldata->sheet_handle = sheet;
+ mldata->dialog_handle = dialog_handle;
+
+ purple_request_add_close_notify(dialog_handle,
+ purple_callback_set_zero, &mldata->sheet_handle);
+ purple_request_add_close_notify(dialog_handle,
+ purple_callback_set_zero, &mldata->dialog_handle);
+}
diff --git a/libpurple/protocols/gg/multilogon.h b/libpurple/protocols/gg/multilogon.h
new file mode 100644
index 0000000000..3650c23d3b
--- /dev/null
+++ b/libpurple/protocols/gg/multilogon.h
@@ -0,0 +1,46 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 _GGP_MULTILOGON_H
+#define _GGP_MULTILOGON_H
+
+#include <internal.h>
+#include <libgadu.h>
+
+typedef struct _ggp_multilogon_session_data ggp_multilogon_session_data;
+
+void ggp_multilogon_setup(PurpleConnection *gc);
+void ggp_multilogon_cleanup(PurpleConnection *gc);
+
+void ggp_multilogon_info(PurpleConnection *gc,
+ struct gg_event_multilogon_info *msg);
+
+void ggp_multilogon_dialog(PurpleConnection *gc);
+
+#endif /* _GGP_MULTILOGON_H */
diff --git a/libpurple/protocols/gg/oauth/oauth-parameter.c b/libpurple/protocols/gg/oauth/oauth-parameter.c
new file mode 100644
index 0000000000..198afe9372
--- /dev/null
+++ b/libpurple/protocols/gg/oauth/oauth-parameter.c
@@ -0,0 +1,159 @@
+/* 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.
+ *
+ * Code adapted from libgadu (C) 2008 Wojtek Kaniewski <wojtekka@irc.pl>
+ * (http://toxygen.net/libgadu/) during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * 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 "oauth-parameter.h"
+
+struct gg_oauth_parameter {
+ char *key;
+ char *value;
+ struct gg_oauth_parameter *next;
+};
+
+int gg_oauth_parameter_set(gg_oauth_parameter_t **list, const char *key, const char *value)
+{
+ gg_oauth_parameter_t *p, *new_p;
+ char *new_key;
+ char *new_value;
+
+ if (value == NULL)
+ return 0;
+
+ if (list == NULL)
+ return -1;
+
+ new_key = strdup(key);
+
+ if (new_key == NULL)
+ return -1;
+
+ new_value = strdup(value);
+
+ if (new_value == NULL) {
+ free(new_key);
+ return -1;
+ }
+
+ new_p = malloc(sizeof(gg_oauth_parameter_t));
+
+ if (new_p == NULL) {
+ free(new_key);
+ free(new_value);
+ return -1;
+ }
+
+ memset(new_p, 0, sizeof(gg_oauth_parameter_t));
+ new_p->key = new_key;
+ new_p->value = new_value;
+
+ if (*list != NULL) {
+ p = *list;
+
+ while (p != NULL && p->next != NULL)
+ p = p->next;
+
+ p->next = new_p;
+ } else {
+ *list = new_p;
+ }
+
+ return 0;
+}
+
+char *gg_oauth_parameter_join(gg_oauth_parameter_t *list, int header)
+{
+ gg_oauth_parameter_t *p;
+ int len = 0;
+ char *res, *out;
+
+ if (header)
+ len += strlen("OAuth ");
+
+ for (p = list; p; p = p->next) {
+ gchar *escaped;
+ len += strlen(p->key);
+
+ len += (header) ? 3 : 1;
+
+ escaped = g_uri_escape_string(p->value, NULL, FALSE);
+ len += strlen(escaped);
+ g_free(escaped);
+
+ if (p->next)
+ len += 1;
+ }
+
+ res = malloc(len + 1);
+
+ if (res == NULL)
+ return NULL;
+
+ out = res;
+
+ *out = 0;
+
+ if (header) {
+ strcpy(out, "OAuth ");
+ out += strlen(out);
+ }
+
+ for (p = list; p; p = p->next) {
+ gchar *escaped;
+ strcpy(out, p->key);
+ out += strlen(p->key);
+
+ strcpy(out++, "=");
+
+ if (header)
+ strcpy(out++, "\"");
+
+ escaped = g_uri_escape_string(p->value, NULL, FALSE);
+ strcpy(out, escaped);
+ out += strlen(escaped);
+ g_free(escaped);
+
+ if (header)
+ strcpy(out++, "\"");
+
+ if (p->next != NULL)
+ strcpy(out++, (header) ? "," : "&");
+ }
+
+ return res;
+}
+
+void gg_oauth_parameter_free(gg_oauth_parameter_t *list)
+{
+ while (list != NULL) {
+ gg_oauth_parameter_t *next;
+
+ next = list->next;
+
+ free(list->key);
+ free(list->value);
+ free(list);
+
+ list = next;
+ }
+}
diff --git a/libpurple/protocols/gg/oauth/oauth-parameter.h b/libpurple/protocols/gg/oauth/oauth-parameter.h
new file mode 100644
index 0000000000..e5d1169dbd
--- /dev/null
+++ b/libpurple/protocols/gg/oauth/oauth-parameter.h
@@ -0,0 +1,37 @@
+/* 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.
+ *
+ * Code adapted from libgadu (C) 2008 Wojtek Kaniewski <wojtekka@irc.pl>
+ * (http://toxygen.net/libgadu/) during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * 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 _GGP_OAUTH_PARAMETER_H
+#define _GGP_OAUTH_PARAMETER_H
+
+#include <internal.h>
+
+typedef struct gg_oauth_parameter gg_oauth_parameter_t;
+
+int gg_oauth_parameter_set(gg_oauth_parameter_t **list, const char *key, const char *value);
+char *gg_oauth_parameter_join(gg_oauth_parameter_t *list, int header);
+void gg_oauth_parameter_free(gg_oauth_parameter_t *list);
+
+#endif /* _GGP_OAUTH_PARAMETER_H */
diff --git a/libpurple/protocols/gg/oauth/oauth-purple.c b/libpurple/protocols/gg/oauth/oauth-purple.c
new file mode 100644
index 0000000000..40faca4f7b
--- /dev/null
+++ b/libpurple/protocols/gg/oauth/oauth-purple.c
@@ -0,0 +1,264 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 "oauth-purple.h"
+
+#include "oauth.h"
+#include "../utils.h"
+#include "../xml.h"
+
+#include <debug.h>
+#include <http.h>
+
+#define GGP_OAUTH_RESPONSE_MAX 10240
+
+typedef struct
+{
+ PurpleConnection *gc;
+ ggp_oauth_request_cb callback;
+ gpointer user_data;
+ gchar *token;
+ gchar *token_secret;
+
+ gchar *sign_method, *sign_url;
+} ggp_oauth_data;
+
+static void ggp_oauth_data_free(ggp_oauth_data *data);
+
+static void ggp_oauth_request_token_got(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer user_data);
+
+static void ggp_oauth_authorization_done(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer user_data);
+
+static void ggp_oauth_access_token_got(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer user_data);
+
+static void ggp_oauth_data_free(ggp_oauth_data *data)
+{
+ g_free(data->token);
+ g_free(data->token_secret);
+ g_free(data->sign_method);
+ g_free(data->sign_url);
+ g_free(data);
+}
+
+void ggp_oauth_request(PurpleConnection *gc, ggp_oauth_request_cb callback,
+ gpointer user_data, const gchar *sign_method, const gchar *sign_url)
+{
+ PurpleAccount *account = purple_connection_get_account(gc);
+ PurpleHttpRequest *req;
+ char *auth;
+ const char *method = "POST";
+ 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,
+ purple_account_get_username(account),
+ purple_connection_get_password(gc), NULL, NULL);
+
+ data = g_new0(ggp_oauth_data, 1);
+ data->gc = gc;
+ data->callback = callback;
+ data->user_data = user_data;
+ data->sign_method = g_strdup(sign_method);
+ data->sign_url = g_strdup(sign_url);
+
+ req = purple_http_request_new(url);
+ purple_http_request_set_max_len(req, GGP_OAUTH_RESPONSE_MAX);
+ purple_http_request_set_method(req, method);
+ purple_http_request_header_set(req, "Authorization", auth);
+ purple_http_request(gc, req, ggp_oauth_request_token_got, data);
+ purple_http_request_unref(req);
+
+ free(auth);
+}
+
+static void ggp_oauth_request_token_got(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer user_data)
+{
+ ggp_oauth_data *data = user_data;
+ PurpleAccount *account;
+ PurpleHttpRequest *req;
+ PurpleXmlNode *xml;
+ gchar *request_data;
+ gboolean succ = TRUE;
+ const gchar *xml_raw;
+
+ PURPLE_ASSERT_CONNECTION_IS_VALID(data->gc);
+
+ account = purple_connection_get_account(data->gc);
+
+ if (!purple_http_response_is_successful(response)) {
+ purple_debug_error("gg", "ggp_oauth_request_token_got: "
+ "requested token not received\n");
+ ggp_oauth_data_free(data);
+ return;
+ }
+
+ purple_debug_misc("gg", "ggp_oauth_request_token_got: "
+ "got request token, doing authorization...\n");
+
+ xml_raw = purple_http_response_get_data(response, NULL);
+ xml = purple_xmlnode_from_str(xml_raw, -1);
+ if (xml == NULL) {
+ purple_debug_error("gg", "ggp_oauth_request_token_got: "
+ "invalid xml\n");
+ ggp_oauth_data_free(data);
+ return;
+ }
+
+ succ &= ggp_xml_get_string(xml, "oauth_token", &data->token);
+ succ &= ggp_xml_get_string(xml, "oauth_token_secret",
+ &data->token_secret);
+ purple_xmlnode_free(xml);
+ if (!succ) {
+ purple_debug_error("gg", "ggp_oauth_request_token_got: "
+ "invalid xml - token is not present\n");
+ ggp_oauth_data_free(data);
+ return;
+ }
+
+ request_data = g_strdup_printf(
+ "callback_url=http://www.mojageneracja.pl&request_token=%s&"
+ "uin=%s&password=%s", data->token,
+ purple_account_get_username(account),
+ purple_connection_get_password(data->gc));
+
+ req = purple_http_request_new("https://login.gadu-gadu.pl/authorize");
+ purple_http_request_set_max_len(req, GGP_OAUTH_RESPONSE_MAX);
+ /* we don't need any results, nor 302 redirection */
+ purple_http_request_set_max_redirects(req, 0);
+ purple_http_request_set_method(req, "POST");
+ purple_http_request_header_set(req, "Content-Type", "application/x-www-form-urlencoded");
+ purple_http_request_set_contents(req, request_data, -1);
+ purple_http_request(data->gc, req, ggp_oauth_authorization_done, data);
+ purple_http_request_unref(req);
+
+ g_free(request_data);
+}
+
+static void ggp_oauth_authorization_done(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer user_data)
+{
+ ggp_oauth_data *data = user_data;
+ PurpleAccount *account;
+ PurpleHttpRequest *req;
+ char *auth;
+ const char *method = "POST";
+ const char *url = "http://api.gadu-gadu.pl/access_token";
+ int response_code;
+
+ PURPLE_ASSERT_CONNECTION_IS_VALID(data->gc);
+
+ account = purple_connection_get_account(data->gc);
+
+ response_code = purple_http_response_get_code(response);
+ if (response_code != 302) {
+ purple_debug_error("gg", "ggp_oauth_authorization_done: "
+ "failed (code = %d)", response_code);
+ ggp_oauth_data_free(data);
+ return;
+ }
+
+ purple_debug_misc("gg", "ggp_oauth_authorization_done: "
+ "authorization done, requesting access token...\n");
+
+ auth = gg_oauth_generate_header(method, url,
+ purple_account_get_username(account),
+ purple_connection_get_password(data->gc),
+ data->token, data->token_secret);
+
+ req = purple_http_request_new(url);
+ purple_http_request_set_max_len(req, GGP_OAUTH_RESPONSE_MAX);
+ purple_http_request_set_method(req, method);
+ purple_http_request_header_set(req, "Authorization", auth);
+ purple_http_request(data->gc, req, ggp_oauth_access_token_got, data);
+ purple_http_request_unref(req);
+
+ free(auth);
+}
+
+static void ggp_oauth_access_token_got(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer user_data)
+{
+ ggp_oauth_data *data = user_data;
+ gchar *token, *token_secret;
+ PurpleXmlNode *xml;
+ const gchar *xml_raw;
+ gboolean succ = TRUE;
+
+ xml_raw = purple_http_response_get_data(response, NULL);
+ xml = purple_xmlnode_from_str(xml_raw, -1);
+ if (xml == NULL) {
+ purple_debug_error("gg", "ggp_oauth_access_token_got: "
+ "invalid xml\n");
+ ggp_oauth_data_free(data);
+ return;
+ }
+
+ succ &= ggp_xml_get_string(xml, "oauth_token", &token);
+ succ &= ggp_xml_get_string(xml, "oauth_token_secret",
+ &token_secret);
+ purple_xmlnode_free(xml);
+ if (!succ || strlen(token) < 10) {
+ purple_debug_error("gg", "ggp_oauth_access_token_got: "
+ "invalid xml - token is not present\n");
+ ggp_oauth_data_free(data);
+ return;
+ }
+
+ if (data->sign_url) {
+ PurpleAccount *account;
+ gchar *auth;
+
+ purple_debug_misc("gg", "ggp_oauth_access_token_got: "
+ "got access token, returning signed url\n");
+
+ account = purple_connection_get_account(data->gc);
+ auth = gg_oauth_generate_header(
+ data->sign_method, data->sign_url,
+ purple_account_get_username(account),
+ purple_connection_get_password(data->gc),
+ token, token_secret);
+ data->callback(data->gc, auth, data->user_data);
+ } else {
+ purple_debug_misc("gg", "ggp_oauth_access_token_got: "
+ "got access token, returning it\n");
+ data->callback(data->gc, token, data->user_data);
+ }
+
+ g_free(token);
+ g_free(token_secret);
+ ggp_oauth_data_free(data);
+}
diff --git a/libpurple/protocols/gg/oauth/oauth-purple.h b/libpurple/protocols/gg/oauth/oauth-purple.h
new file mode 100644
index 0000000000..0b16102739
--- /dev/null
+++ b/libpurple/protocols/gg/oauth/oauth-purple.h
@@ -0,0 +1,42 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 _GGP_OAUTH_PURPLE_H
+#define _GGP_OAUTH_PURPLE_H
+
+#include <internal.h>
+#include <libgadu.h>
+
+typedef void (*ggp_oauth_request_cb)(PurpleConnection *gc, const gchar *token,
+ gpointer user_data);
+
+void ggp_oauth_request(PurpleConnection *gc, ggp_oauth_request_cb callback,
+ gpointer user_data, const gchar *sign_method, const gchar *sign_url);
+
+#endif /* _GGP_OAUTH_PURPLE_H */
diff --git a/libpurple/protocols/gg/oauth/oauth.c b/libpurple/protocols/gg/oauth/oauth.c
new file mode 100644
index 0000000000..c9c7526cb5
--- /dev/null
+++ b/libpurple/protocols/gg/oauth/oauth.c
@@ -0,0 +1,155 @@
+/* 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.
+ *
+ * Code adapted from libgadu (C) 2008 Wojtek Kaniewski <wojtekka@irc.pl>
+ * (http://toxygen.net/libgadu/) during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * 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 "oauth.h"
+
+#include "oauth-parameter.h"
+#include "ciphers/hmaccipher.h"
+#include "ciphers/sha1hash.h"
+
+char *gg_oauth_static_nonce; /* dla unit testów */
+char *gg_oauth_static_timestamp; /* dla unit testów */
+
+static void gg_oauth_generate_nonce(char *buf, int len)
+{
+ const char charset[] = "0123456789";
+
+ if (buf == NULL || len < 1)
+ return;
+
+ while (len > 1) {
+ *buf++ = charset[(unsigned) (((float) sizeof(charset) - 1.0) * rand() / (RAND_MAX + 1.0))];
+ len--;
+ }
+
+ *buf = 0;
+}
+
+static gchar *gg_hmac_sha1(const char *key, const char *message)
+{
+ PurpleCipher *cipher;
+ PurpleHash *hash;
+ guchar digest[20];
+
+ hash = purple_sha1_hash_new();
+ cipher = purple_hmac_cipher_new(hash);
+
+ purple_cipher_set_key(cipher, (guchar *)key, strlen(key));
+ purple_cipher_append(cipher, (guchar *)message, strlen(message));
+ purple_cipher_digest(cipher, digest, sizeof(digest));
+
+ g_object_unref(cipher);
+ g_object_unref(hash);
+
+ return purple_base64_encode(digest, sizeof(digest));
+}
+
+static char *
+gg_oauth_generate_signature(const char *method, const char *url,
+ const char *request, const char *consumer_secret,
+ const char *token_secret)
+{
+ char *text, *key, *res;
+ gchar *url_e, *request_e, *consumer_secret_e, *token_secret_e;
+
+ url_e = g_uri_escape_string(url, "?", FALSE);
+ g_strdelimit(url_e, "?", '\0');
+ request_e = g_uri_escape_string(request, NULL, FALSE);
+ text = g_strdup_printf("%s&%s&%s", method, url_e, request_e);
+ g_free(url_e);
+ g_free(request_e);
+
+ consumer_secret_e = g_uri_escape_string(consumer_secret, NULL, FALSE);
+ token_secret_e = token_secret ? g_uri_escape_string(token_secret, NULL, FALSE) : NULL;
+ key = g_strdup_printf("%s&%s", consumer_secret_e, token_secret ? token_secret_e : "");
+ g_free(consumer_secret_e);
+ g_free(token_secret_e);
+
+ res = gg_hmac_sha1(key, text);
+
+ free(key);
+ free(text);
+
+ return res;
+}
+
+char *
+gg_oauth_generate_header(const char *method, const char *url,
+ const char *consumer_key, const char *consumer_secret,
+ const char *token, const char *token_secret)
+{
+ char *request, *signature, *res;
+ char nonce[80], timestamp[16];
+ gg_oauth_parameter_t *params = NULL;
+
+ if (gg_oauth_static_nonce == NULL)
+ gg_oauth_generate_nonce(nonce, sizeof(nonce));
+ else {
+ strncpy(nonce, gg_oauth_static_nonce, sizeof(nonce) - 1);
+ nonce[sizeof(nonce) - 1] = 0;
+ }
+
+ if (gg_oauth_static_timestamp == NULL)
+ snprintf(timestamp, sizeof(timestamp), "%ld", time(NULL));
+ else {
+ strncpy(timestamp, gg_oauth_static_timestamp, sizeof(timestamp) - 1);
+ timestamp[sizeof(timestamp) - 1] = 0;
+ }
+
+ gg_oauth_parameter_set(&params, "oauth_consumer_key", consumer_key);
+ gg_oauth_parameter_set(&params, "oauth_nonce", nonce);
+ gg_oauth_parameter_set(&params, "oauth_signature_method", "HMAC-SHA1");
+ gg_oauth_parameter_set(&params, "oauth_timestamp", timestamp);
+ gg_oauth_parameter_set(&params, "oauth_token", token);
+ gg_oauth_parameter_set(&params, "oauth_version", "1.0");
+
+ request = gg_oauth_parameter_join(params, 0);
+
+ signature = gg_oauth_generate_signature(method, url, request, consumer_secret, token_secret);
+
+ free(request);
+
+ gg_oauth_parameter_free(params);
+ params = NULL;
+
+ if (signature == NULL)
+ return NULL;
+
+ gg_oauth_parameter_set(&params, "oauth_version", "1.0");
+ gg_oauth_parameter_set(&params, "oauth_nonce", nonce);
+ gg_oauth_parameter_set(&params, "oauth_timestamp", timestamp);
+ gg_oauth_parameter_set(&params, "oauth_consumer_key", consumer_key);
+ gg_oauth_parameter_set(&params, "oauth_token", token);
+ gg_oauth_parameter_set(&params, "oauth_signature_method", "HMAC-SHA1");
+ gg_oauth_parameter_set(&params, "oauth_signature", signature);
+
+ free(signature);
+
+ res = gg_oauth_parameter_join(params, 1);
+
+ gg_oauth_parameter_free(params);
+
+ return res;
+}
diff --git a/libpurple/protocols/mxit/http.h b/libpurple/protocols/gg/oauth/oauth.h
index 03808fff58..3de71309bf 100644
--- a/libpurple/protocols/mxit/http.h
+++ b/libpurple/protocols/gg/oauth/oauth.h
@@ -1,12 +1,12 @@
-/*
- * MXit Protocol libPurple Plugin
+/* purple
*
- * -- MXit client protocol implementation --
+ * 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.
*
- * Pieter Loubser <libpurple@mxit.com>
- *
- * (C) Copyright 2009 MXit Lifestyle (Pty) Ltd.
- * <http://www.mxitlifestyle.com>
+ * Code adapted from libgadu (C) 2008 Wojtek Kaniewski <wojtekka@irc.pl>
+ * (http://toxygen.net/libgadu/) during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
*
* 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
@@ -23,25 +23,14 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+#ifndef _GGP_OAUTH_H
+#define _GGP_OAUTH_H
-#ifndef _MXIT_HTTP_H_
-#define _MXIT_HTTP_H_
-
-
-
-struct http_request
-{
- struct MXitSession* session;
- char* host;
- int port;
- char* data;
- int datalen;
-};
-
-
-void mxit_http_send_request( struct MXitSession* session, char* host, int port, const char* data, int datalen );
-
-
+#include <internal.h>
+#include <libgadu.h>
-#endif /* _MXIT_HTTP_H_ */
+char * gg_oauth_generate_header(const char *method, const char *url,
+ const char *consumer_key, const char *consumer_secret,
+ const char *token, const char *token_secret);
+#endif /* _GGP_OAUTH_H */
diff --git a/libpurple/protocols/gg/pubdir-prpl.c b/libpurple/protocols/gg/pubdir-prpl.c
new file mode 100644
index 0000000000..afc0ee1e90
--- /dev/null
+++ b/libpurple/protocols/gg/pubdir-prpl.c
@@ -0,0 +1,967 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 "pubdir-prpl.h"
+
+#include <debug.h>
+#include <http.h>
+#include <request.h>
+
+#include "oauth/oauth-purple.h"
+#include "xml.h"
+#include "utils.h"
+#include "status.h"
+
+typedef struct
+{
+ PurpleConnection *gc;
+ ggp_pubdir_request_cb cb;
+ void *user_data;
+ enum
+ {
+ GGP_PUBDIR_REQUEST_TYPE_INFO,
+ GGP_PUBDIR_REQUEST_TYPE_SEARCH,
+ } type;
+ union
+ {
+ struct
+ {
+ uin_t uin;
+ } user_info;
+ ggp_pubdir_search_form *search_form;
+ } params;
+} ggp_pubdir_request;
+
+void ggp_pubdir_request_free(ggp_pubdir_request *request);
+void ggp_pubdir_record_free(ggp_pubdir_record *records, int count);
+
+static void ggp_pubdir_get_info_got_token(PurpleConnection *gc,
+ const gchar *token, gpointer _request);
+static void ggp_pubdir_got_data(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer user_data);
+
+static void ggp_pubdir_get_info_prpl_got(PurpleConnection *gc,
+ int records_count, const ggp_pubdir_record *records, int next_offset,
+ void *_uin_p);
+
+static void ggp_pubdir_request_buddy_alias_got(PurpleConnection *gc,
+ int records_count, const ggp_pubdir_record *records, int next_offset,
+ void *user_data);
+
+/* Searching for buddies. */
+
+#define GGP_PUBDIR_SEARCH_TITLE _("Gadu-Gadu Public Directory")
+#define GGP_PUBDIR_SEARCH_PER_PAGE 20
+
+struct _ggp_pubdir_search_form
+{
+ gchar *nick, *city;
+ ggp_pubdir_gender gender;
+ int offset;
+ int limit;
+
+ void *display_handle;
+};
+
+void ggp_pubdir_search_form_free(ggp_pubdir_search_form *form);
+ggp_pubdir_search_form * ggp_pubdir_search_form_clone(
+ const ggp_pubdir_search_form *form);
+
+static void ggp_pubdir_search_request(PurpleConnection *gc,
+ PurpleRequestFields *fields);
+static gchar * ggp_pubdir_search_make_query(const ggp_pubdir_search_form *form);
+static void ggp_pubdir_search_execute(PurpleConnection *gc,
+ const ggp_pubdir_search_form *form,
+ ggp_pubdir_request_cb cb, void *user_data);
+static void ggp_pubdir_search_got_token(PurpleConnection *gc,
+ const gchar *token, gpointer _request);
+static void ggp_pubdir_search_results_display(PurpleConnection *gc,
+ int records_count, const ggp_pubdir_record *records, int next_offset,
+ void *user_data);
+static void ggp_pubdir_search_results_close(gpointer _form);
+static void ggp_pubdir_search_results_next(PurpleConnection *gc, GList *row,
+ gpointer _form);
+static void ggp_pubdir_search_results_add(PurpleConnection *gc, GList *row,
+ gpointer _form);
+static void ggp_pubdir_search_results_im(PurpleConnection *gc, GList *row,
+ gpointer _form);
+static void ggp_pubdir_search_results_info(PurpleConnection *gc, GList *row,
+ gpointer _form);
+static void ggp_pubdir_search_results_new(PurpleConnection *gc, GList *row,
+ gpointer _form);
+
+/* Own profile. */
+
+static void ggp_pubdir_set_info_dialog(PurpleConnection *gc, int records_count,
+ const ggp_pubdir_record *records, int next_offset, void *user_data);
+static void ggp_pubdir_set_info_request(PurpleConnection *gc,
+ PurpleRequestFields *fields);
+static void ggp_pubdir_set_info_got_token(PurpleConnection *gc,
+ const gchar *token, gpointer _record);
+
+static void ggp_pubdir_set_info_got_response(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer user_data);
+
+/******************************************************************************/
+
+static const gchar *ggp_pubdir_provinces[] =
+{
+ N_("Not specified"),
+ "dolnośląskie",
+ "kujawsko-pomorskie",
+ "lubelskie",
+ "lubuskie",
+ "łódzkie",
+ "małopolskie",
+ "mazowieckie",
+ "opolskie",
+ "podkarpackie",
+ "podlaskie",
+ "pomorskie",
+ "śląskie",
+ "świętokrzyskie",
+ "warmińsko-mazurskie",
+ "wielkopolskie",
+ "zachodniopomorskie",
+};
+
+static gsize ggp_pubdir_provinces_count = sizeof(ggp_pubdir_provinces)/sizeof(gchar*);
+
+/******************************************************************************/
+
+void ggp_pubdir_record_free(ggp_pubdir_record *records, int count)
+{
+ int i;
+ for (i = 0; i < count; i++) {
+ g_free(records[i].label);
+ g_free(records[i].nickname);
+ g_free(records[i].first_name);
+ g_free(records[i].last_name);
+ g_free(records[i].city);
+ }
+ g_free(records);
+}
+
+void ggp_pubdir_request_free(ggp_pubdir_request *request)
+{
+ if (request->type == GGP_PUBDIR_REQUEST_TYPE_SEARCH)
+ ggp_pubdir_search_form_free(request->params.search_form);
+ g_free(request);
+}
+
+void ggp_pubdir_get_info(PurpleConnection *gc, uin_t uin,
+ ggp_pubdir_request_cb cb, void *user_data)
+{
+ ggp_pubdir_request *request = g_new0(ggp_pubdir_request, 1);
+ gchar *url;
+
+ request->type = GGP_PUBDIR_REQUEST_TYPE_INFO;
+ request->gc = gc;
+ request->cb = cb;
+ request->user_data = user_data;
+ request->params.user_info.uin = uin;
+
+ url = g_strdup_printf("http://api.gadu-gadu.pl/users/%u", uin);
+ ggp_oauth_request(gc, ggp_pubdir_get_info_got_token, request,
+ "GET", url);
+ g_free(url);
+}
+
+static void ggp_pubdir_get_info_got_token(PurpleConnection *gc,
+ const gchar *token, gpointer _request)
+{
+ PurpleHttpRequest *req;
+ ggp_pubdir_request *request = _request;
+
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
+
+ if (!token) {
+ request->cb(gc, -1, NULL, 0, request->user_data);
+ ggp_pubdir_request_free(request);
+ return;
+ }
+
+ req = purple_http_request_new(NULL);
+ purple_http_request_set_url_printf(req,
+ "http://api.gadu-gadu.pl/users/%u",
+ request->params.user_info.uin);
+ purple_http_request_header_set(req, "Authorization", token);
+ purple_http_request(gc, req, ggp_pubdir_got_data, request);
+ purple_http_request_unref(req);
+}
+
+static void ggp_pubdir_got_data(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _request)
+{
+ ggp_pubdir_request *request = _request;
+ PurpleConnection *gc = request->gc;
+ gboolean succ = TRUE;
+ PurpleXmlNode *xml;
+ const gchar *xml_raw;
+ unsigned int status, next_offset;
+ int record_count, i;
+ ggp_pubdir_record *records;
+
+ xml_raw = purple_http_response_get_data(response, NULL);
+
+ if (purple_debug_is_verbose() && purple_debug_is_unsafe()) {
+ purple_debug_misc("gg", "ggp_pubdir_got_data: xml=[%s]\n",
+ xml_raw);
+ }
+
+ xml = purple_xmlnode_from_str(xml_raw, -1);
+ if (xml == NULL) {
+ purple_debug_error("gg", "ggp_pubdir_got_data: "
+ "invalid xml\n");
+ request->cb(gc, -1, NULL, 0, request->user_data);
+ ggp_pubdir_request_free(request);
+ return;
+ }
+
+ succ &= ggp_xml_get_uint(xml, "status", &status);
+ if (!ggp_xml_get_uint(xml, "nextOffset", &next_offset))
+ next_offset = 0;
+ xml = purple_xmlnode_get_child(xml, "users");
+ if (!succ || status != 0 || !xml) {
+ purple_debug_error("gg", "ggp_pubdir_got_data: "
+ "invalid reply\n");
+ request->cb(gc, -1, NULL, 0, request->user_data);
+ ggp_pubdir_request_free(request);
+ return;
+ }
+
+ record_count = ggp_xml_child_count(xml, "user");
+ records = g_new0(ggp_pubdir_record, record_count);
+
+ xml = purple_xmlnode_get_child(xml, "user");
+ i = 0;
+ while (xml) {
+ ggp_pubdir_record *record = &records[i++];
+ gchar *city = NULL, *birth_s = NULL;
+ unsigned int gender = 0;
+ const gchar *uin_s;
+
+ g_assert(i <= record_count);
+
+ record->uin = ggp_str_to_uin(purple_xmlnode_get_attrib(xml, "uin"));
+ if (record->uin == 0)
+ ggp_xml_get_uint(xml, "uin", &record->uin);
+ if (record->uin == 0)
+ purple_debug_error("gg", "ggp_pubdir_got_data:"
+ " invalid uin\n");
+ uin_s = ggp_uin_to_str(record->uin);
+
+ ggp_xml_get_string(xml, "label", &record->label);
+ ggp_xml_get_string(xml, "nick", &record->nickname);
+ ggp_xml_get_string(xml, "name", &record->first_name);
+ ggp_xml_get_string(xml, "surname", &record->last_name);
+ ggp_xml_get_string(xml, "city", &city);
+ ggp_xml_get_string(xml, "birth", &birth_s);
+ ggp_xml_get_uint(xml, "gender", &gender);
+ ggp_xml_get_uint(xml, "age", &record->age);
+ ggp_xml_get_uint(xml, "province", &record->province);
+
+ record->label = ggp_free_if_equal(record->label, uin_s);
+ record->label = ggp_free_if_equal(record->label, "");
+ record->nickname = ggp_free_if_equal(record->nickname, uin_s);
+ record->nickname = ggp_free_if_equal(record->nickname, "");
+ record->first_name = ggp_free_if_equal(record->first_name, "");
+ record->last_name = ggp_free_if_equal(record->last_name, "");
+
+ if (record->label) {}
+ else if (record->nickname)
+ record->label = g_strdup(record->nickname);
+ else if (record->first_name && record->last_name)
+ record->label = g_strdup_printf("%s %s",
+ record->first_name, record->last_name);
+ else if (record->first_name)
+ record->label = g_strdup(record->first_name);
+ else if (record->last_name)
+ record->label = g_strdup(record->last_name);
+ if (record->label)
+ g_strstrip(record->label);
+ if (record->nickname)
+ g_strstrip(record->nickname);
+
+ if (gender == 1)
+ record->gender = GGP_PUBDIR_GENDER_FEMALE;
+ else if (gender == 2)
+ record->gender = GGP_PUBDIR_GENDER_MALE;
+ else
+ record->gender = GGP_PUBDIR_GENDER_UNSPECIFIED;
+
+ if (city && city[0] != '\0')
+ record->city = g_strdup(city);
+ if (record->city)
+ g_strstrip(record->city);
+ if (!record->city) {
+ g_free(record->city);
+ record->city = NULL;
+ }
+
+ record->birth = ggp_date_from_iso8601(birth_s);
+ /*TODO: calculate age from birth */
+
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("gg", "ggp_pubdir_got_data: [uin:%d] "
+ "[label:%s] [nick:%s] [first name:%s] "
+ "[last name:%s] [city:%s] [gender:%d] [age:%d] "
+ "[birth:%lu]\n", record->uin, record->label,
+ record->nickname, record->first_name,
+ record->last_name, record->city, record->gender,
+ record->age, record->birth);
+ }
+
+ g_free(city);
+
+ xml = purple_xmlnode_get_next_twin(xml);
+ }
+
+ request->cb(gc, record_count, records, next_offset, request->user_data);
+
+ ggp_pubdir_request_free(request);
+ ggp_pubdir_record_free(records, record_count);
+}
+
+void ggp_pubdir_get_info_prpl(PurpleConnection *gc, const char *name)
+{
+ uin_t uin = ggp_str_to_uin(name);
+ uin_t *uin_p = g_new0(uin_t, 1);
+
+ *uin_p = uin;
+
+ purple_debug_info("gg", "ggp_pubdir_get_info_prpl: %u\n", uin);
+
+ ggp_pubdir_get_info(gc, uin, ggp_pubdir_get_info_prpl_got, uin_p);
+}
+
+static void ggp_pubdir_get_info_prpl_got(PurpleConnection *gc,
+ int records_count, const ggp_pubdir_record *records, int next_offset,
+ void *_uin_p)
+{
+ uin_t uin = *((uin_t*)_uin_p);
+ PurpleNotifyUserInfo *info = purple_notify_user_info_new();
+ const ggp_pubdir_record *record = &records[0];
+ PurpleBuddy *buddy;
+
+ g_free(_uin_p);
+
+ if (records_count < 1) {
+ purple_debug_error("gg", "ggp_pubdir_get_info_prpl_got: "
+ "couldn't get info for %u\n", uin);
+ purple_notify_user_info_add_pair_plaintext(info, NULL,
+ _("Cannot get user information"));
+ purple_notify_userinfo(gc, ggp_uin_to_str(uin), info,
+ NULL, NULL);
+ purple_notify_user_info_destroy(info);
+ return;
+ }
+
+ purple_debug_info("gg", "ggp_pubdir_get_info_prpl_got: %u\n", uin);
+ g_assert(uin == record->uin);
+ g_assert(records_count == 1);
+
+ buddy = purple_blist_find_buddy(purple_connection_get_account(gc),
+ ggp_uin_to_str(uin));
+ if (buddy) {
+ const char *alias;
+ PurpleStatus *status;
+ gchar *status_message;
+
+ alias = purple_buddy_get_alias_only(buddy);
+ if (alias)
+ purple_notify_user_info_add_pair_plaintext(info,
+ _("Alias"), alias);
+
+ status = purple_presence_get_active_status(
+ purple_buddy_get_presence(buddy));
+ ggp_status_from_purplestatus(status, &status_message);
+ purple_notify_user_info_add_pair_plaintext(info, _("Status"),
+ purple_status_get_name(status));
+ if (status_message) {
+ purple_notify_user_info_add_pair_plaintext(info,
+ _("Message"), status_message);
+ }
+ }
+
+ if (record->nickname) {
+ purple_notify_user_info_add_pair_plaintext(info,
+ _("Nickname"), record->nickname);
+ }
+ if (record->first_name) {
+ purple_notify_user_info_add_pair_plaintext(info,
+ _("First name"), record->first_name);
+ }
+ if (record->last_name) {
+ purple_notify_user_info_add_pair_plaintext(info,
+ _("Last name"), record->last_name);
+ }
+ if (record->gender != GGP_PUBDIR_GENDER_UNSPECIFIED) {
+ purple_notify_user_info_add_pair_plaintext(info, _("Gender"),
+ record->gender == GGP_PUBDIR_GENDER_FEMALE ?
+ _("Female") : _("Male"));
+ }
+ if (record->city) {
+ purple_notify_user_info_add_pair_plaintext(info, _("City"),
+ record->city);
+ }
+ if (record->birth) {
+ purple_notify_user_info_add_pair_plaintext(info, _("Birthday"),
+ ggp_date_strftime("%Y-%m-%d", record->birth));
+ } else if (record->age) {
+ gchar *age_s = g_strdup_printf("%d", record->age);
+ purple_notify_user_info_add_pair_plaintext(info, _("Age"),
+ age_s);
+ g_free(age_s);
+ }
+
+ purple_notify_userinfo(gc, ggp_uin_to_str(uin), info, NULL, NULL);
+ purple_notify_user_info_destroy(info);
+}
+
+void ggp_pubdir_request_buddy_alias(PurpleConnection *gc, PurpleBuddy *buddy)
+{
+ uin_t uin = ggp_str_to_uin(purple_buddy_get_name(buddy));
+
+ purple_debug_info("gg", "ggp_pubdir_request_buddy_alias: %u\n", uin);
+
+ ggp_pubdir_get_info(gc, uin, ggp_pubdir_request_buddy_alias_got, NULL);
+}
+
+static void ggp_pubdir_request_buddy_alias_got(PurpleConnection *gc,
+ int records_count, const ggp_pubdir_record *records, int next_offset,
+ void *user_data)
+{
+ uin_t uin;
+ const gchar *alias;
+
+ if (records_count < 0) {
+ purple_debug_error("gg", "ggp_pubdir_request_buddy_alias_got: "
+ "couldn't get info for user\n");
+ return;
+ }
+ uin = records[0].uin;
+
+ alias = records[0].label;
+ if (!alias) {
+ purple_debug_info("gg", "ggp_pubdir_request_buddy_alias_got: "
+ "public alias for %u is not available\n", uin);
+ return;
+ }
+
+ purple_debug_info("gg", "ggp_pubdir_request_buddy_alias_got: "
+ "public alias for %u is \"%s\"\n", uin, alias);
+
+ purple_serv_got_alias(gc, ggp_uin_to_str(uin), alias);
+}
+
+/*******************************************************************************
+ * Searching for buddies.
+ ******************************************************************************/
+
+void ggp_pubdir_search_form_free(ggp_pubdir_search_form *form)
+{
+ g_free(form->nick);
+ g_free(form->city);
+ g_free(form);
+}
+
+ggp_pubdir_search_form * ggp_pubdir_search_form_clone(
+ const ggp_pubdir_search_form *form)
+{
+ ggp_pubdir_search_form *dup = g_new(ggp_pubdir_search_form, 1);
+
+ dup->nick = g_strdup(form->nick);
+ dup->city = g_strdup(form->city);
+ dup->gender = form->gender;
+ dup->offset = form->offset;
+ dup->limit = form->limit;
+
+ dup->display_handle = form->display_handle;
+
+ return dup;
+}
+
+void ggp_pubdir_search(PurpleConnection *gc,
+ const ggp_pubdir_search_form *form)
+{
+ PurpleRequestFields *fields;
+ PurpleRequestFieldGroup *group;
+ PurpleRequestField *field;
+
+ purple_debug_info("gg", "ggp_pubdir_search\n");
+
+ 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("name", _("Name"),
+ form ? form->nick : NULL, FALSE);
+ purple_request_field_group_add_field(group, field);
+
+ field = purple_request_field_string_new("city", _("City"),
+ form ? form->city : NULL, FALSE);
+ purple_request_field_group_add_field(group, field);
+
+ field = purple_request_field_choice_new("gender", _("Gender"),
+ form ? GINT_TO_POINTER(form->gender) : NULL);
+ purple_request_field_choice_add(field, _("Male or female"), NULL);
+ purple_request_field_choice_add(field, _("Male"),
+ GINT_TO_POINTER(GGP_PUBDIR_GENDER_MALE));
+ purple_request_field_choice_add(field, _("Female"),
+ GINT_TO_POINTER(GGP_PUBDIR_GENDER_FEMALE));
+ purple_request_field_group_add_field(group, field);
+
+ purple_request_fields(gc, _("Find buddies"), _("Find buddies"),
+ _("Please, enter your search criteria below"), fields,
+ _("OK"), G_CALLBACK(ggp_pubdir_search_request),
+ _("Cancel"), NULL,
+ purple_request_cpar_from_connection(gc), gc);
+}
+
+static void ggp_pubdir_search_request(PurpleConnection *gc,
+ PurpleRequestFields *fields)
+{
+ ggp_pubdir_search_form *form = g_new0(ggp_pubdir_search_form, 1);
+
+ purple_debug_info("gg", "ggp_pubdir_search_request\n");
+
+ form->nick = g_strdup(purple_request_fields_get_string(fields, "name"));
+ form->city = g_strdup(purple_request_fields_get_string(fields, "city"));
+ form->gender = GPOINTER_TO_INT(purple_request_fields_get_choice(fields,
+ "gender"));
+ form->offset = 0;
+ form->limit = GGP_PUBDIR_SEARCH_PER_PAGE;
+
+ ggp_pubdir_search_execute(gc, form, ggp_pubdir_search_results_display,
+ form);
+}
+
+static gchar * ggp_pubdir_search_make_query(const ggp_pubdir_search_form *form)
+{
+ gchar *nick, *city, *gender;
+ gchar *query;
+
+ if (form->nick && form->nick[0] != '\0') {
+ gchar *nick_e = g_uri_escape_string(form->nick, NULL, FALSE);
+ nick = g_strdup_printf("&nick=%s", nick_e);
+ g_free(nick_e);
+ } else
+ nick = g_strdup("");
+
+ if (form->city && form->city[0] != '\0') {
+ gchar *city_e = g_uri_escape_string(form->city, NULL, FALSE);
+ city = g_strdup_printf("&city=%s", city_e);
+ g_free(city_e);
+ } else
+ city = g_strdup("");
+
+ if (form->gender != GGP_PUBDIR_GENDER_UNSPECIFIED) {
+ gender = g_strdup_printf("&gender=%d",
+ form->gender == GGP_PUBDIR_GENDER_MALE ? 2 : 1);
+ } else
+ gender = g_strdup("");
+
+ query = g_strdup_printf("/users.xml?offset=%d&limit=%d%s%s%s",
+ form->offset, form->limit, nick, city, gender);
+
+ g_free(nick);
+ g_free(city);
+ g_free(gender);
+
+ return query;
+}
+
+static void ggp_pubdir_search_execute(PurpleConnection *gc,
+ const ggp_pubdir_search_form *form,
+ ggp_pubdir_request_cb cb, void *user_data)
+{
+ ggp_pubdir_request *request = g_new0(ggp_pubdir_request, 1);
+ gchar *url;
+ ggp_pubdir_search_form *local_form = ggp_pubdir_search_form_clone(form);
+ gchar *query;
+
+ request->type = GGP_PUBDIR_REQUEST_TYPE_SEARCH;
+ request->gc = gc;
+ request->cb = cb;
+ request->user_data = user_data;
+ request->params.search_form = local_form;
+
+ query = ggp_pubdir_search_make_query(form);
+ purple_debug_misc("gg", "ggp_pubdir_search_execute: %s\n", query);
+ url = g_strdup_printf("http://api.gadu-gadu.pl%s", query);
+ ggp_oauth_request(gc, ggp_pubdir_search_got_token, request,
+ "GET", url);
+ g_free(query);
+ g_free(url);
+}
+
+static void ggp_pubdir_search_got_token(PurpleConnection *gc,
+ const gchar *token, gpointer _request)
+{
+ PurpleHttpRequest *req;
+ ggp_pubdir_request *request = _request;
+ gchar *query;
+
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
+
+ if (!token) {
+ request->cb(gc, -1, NULL, 0, request->user_data);
+ ggp_pubdir_request_free(request);
+ return;
+ }
+
+ purple_debug_misc("gg", "ggp_pubdir_search_got_token\n");
+
+ query = ggp_pubdir_search_make_query(request->params.search_form);
+
+ req = purple_http_request_new(NULL);
+ purple_http_request_set_url_printf(req, "http://api.gadu-gadu.pl%s", query);
+ purple_http_request_header_set(req, "Authorization", token);
+ purple_http_request(gc, req, ggp_pubdir_got_data, request);
+ purple_http_request_unref(req);
+
+ g_free(query);
+}
+
+
+static void ggp_pubdir_search_results_display(PurpleConnection *gc,
+ int records_count, const ggp_pubdir_record *records, int next_offset,
+ void *_form)
+{
+ ggp_pubdir_search_form *form = _form;
+ PurpleNotifySearchResults *results;
+ int i;
+
+ purple_debug_info("gg", "ggp_pubdir_search_results_display: "
+ "got %d records (next offset: %d)\n",
+ records_count, next_offset);
+
+ if (records_count < 0 ||
+ (records_count == 0 && form->offset != 0))
+ {
+ purple_notify_error(gc, GGP_PUBDIR_SEARCH_TITLE,
+ _("Error while searching for buddies"), NULL,
+ purple_request_cpar_from_connection(gc));
+ ggp_pubdir_search_form_free(form);
+ return;
+ }
+
+ if (records_count == 0) {
+ purple_notify_info(gc, GGP_PUBDIR_SEARCH_TITLE,
+ _("No matching users found"),
+ _("There are no users matching your search criteria."),
+ purple_request_cpar_from_connection(gc));
+ ggp_pubdir_search_form_free(form);
+ return;
+ }
+
+ form->offset = next_offset;
+
+ results = purple_notify_searchresults_new();
+
+ purple_notify_searchresults_column_add(results,
+ purple_notify_searchresults_column_new(_("GG Number")));
+ purple_notify_searchresults_column_add(results,
+ purple_notify_searchresults_column_new(_("Name")));
+ purple_notify_searchresults_column_add(results,
+ purple_notify_searchresults_column_new(_("City")));
+ purple_notify_searchresults_column_add(results,
+ purple_notify_searchresults_column_new(_("Gender")));
+ purple_notify_searchresults_column_add(results,
+ purple_notify_searchresults_column_new(_("Age")));
+
+ for (i = 0; i < records_count; i++) {
+ GList *row = NULL;
+ const ggp_pubdir_record *record = &records[i];
+ gchar *gender = NULL, *age = NULL;
+
+ if (record->gender == GGP_PUBDIR_GENDER_MALE)
+ gender = g_strdup("male");
+ else if (record->gender == GGP_PUBDIR_GENDER_FEMALE)
+ gender = g_strdup("female");
+
+ if (record->age)
+ age = g_strdup_printf("%d", record->age);
+
+ row = g_list_append(row, g_strdup(ggp_uin_to_str(record->uin)));
+ row = g_list_append(row, g_strdup(record->label));
+ row = g_list_append(row, g_strdup(record->city));
+ row = g_list_append(row, gender);
+ row = g_list_append(row, age);
+ purple_notify_searchresults_row_add(results, row);
+ }
+
+ purple_notify_searchresults_button_add(results,
+ PURPLE_NOTIFY_BUTTON_ADD, ggp_pubdir_search_results_add);
+ purple_notify_searchresults_button_add(results,
+ PURPLE_NOTIFY_BUTTON_IM, ggp_pubdir_search_results_im);
+ purple_notify_searchresults_button_add(results,
+ PURPLE_NOTIFY_BUTTON_INFO, ggp_pubdir_search_results_info);
+ purple_notify_searchresults_button_add_labeled(results, _("New search"),
+ ggp_pubdir_search_results_new);
+ if (next_offset != 0)
+ purple_notify_searchresults_button_add(results,
+ PURPLE_NOTIFY_BUTTON_CONTINUE,
+ ggp_pubdir_search_results_next);
+
+ if (!form->display_handle)
+ form->display_handle = purple_notify_searchresults(gc,
+ GGP_PUBDIR_SEARCH_TITLE, _("Search results"), NULL,
+ results, ggp_pubdir_search_results_close, form);
+ else
+ purple_notify_searchresults_new_rows(gc, results,
+ form->display_handle);
+ g_assert(form->display_handle);
+}
+
+static void ggp_pubdir_search_results_close(gpointer _form)
+{
+ ggp_pubdir_search_form *form = _form;
+ ggp_pubdir_search_form_free(form);
+}
+
+static void ggp_pubdir_search_results_next(PurpleConnection *gc, GList *row,
+ gpointer _form)
+{
+ ggp_pubdir_search_form *form = _form;
+ ggp_pubdir_search_execute(gc, form, ggp_pubdir_search_results_display,
+ form);
+}
+
+static void ggp_pubdir_search_results_add(PurpleConnection *gc, GList *row,
+ gpointer _form)
+{
+ purple_blist_request_add_buddy(purple_connection_get_account(gc),
+ g_list_nth_data(row, 0), NULL, g_list_nth_data(row, 1));
+}
+
+static void ggp_pubdir_search_results_im(PurpleConnection *gc, GList *row,
+ gpointer _form)
+{
+ purple_conversation_present(PURPLE_CONVERSATION(purple_im_conversation_new(
+ purple_connection_get_account(gc), g_list_nth_data(row, 0))));
+}
+
+static void ggp_pubdir_search_results_info(PurpleConnection *gc, GList *row,
+ gpointer _form)
+{
+ ggp_pubdir_get_info_prpl(gc, g_list_nth_data(row, 0));
+}
+
+static void ggp_pubdir_search_results_new(PurpleConnection *gc, GList *row,
+ gpointer _form)
+{
+ ggp_pubdir_search_form *form = _form;
+ ggp_pubdir_search(gc, form);
+}
+
+/*******************************************************************************
+ * Own profile.
+ ******************************************************************************/
+
+void ggp_pubdir_set_info(PurpleConnection *gc)
+{
+ ggp_pubdir_get_info(gc, ggp_str_to_uin(purple_account_get_username(
+ purple_connection_get_account(gc))),
+ ggp_pubdir_set_info_dialog, NULL);
+}
+
+static void ggp_pubdir_set_info_dialog(PurpleConnection *gc, int records_count,
+ const ggp_pubdir_record *records, int next_offset, void *user_data)
+{
+ PurpleRequestFields *fields;
+ PurpleRequestFieldGroup *group;
+ PurpleRequestField *field;
+ gsize i;
+ const ggp_pubdir_record *record;
+
+ purple_debug_info("gg", "ggp_pubdir_set_info_dialog (record: %d)\n",
+ records_count);
+
+ record = (records_count == 1 ? &records[0] : 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("first_name", _("First name"),
+ record ? record->first_name : NULL, FALSE);
+ purple_request_field_group_add_field(group, field);
+
+ field = purple_request_field_string_new("last_name", _("Last name"),
+ record ? record->last_name : NULL, FALSE);
+ purple_request_field_group_add_field(group, field);
+
+ field = purple_request_field_choice_new("gender", _("Gender"),
+ record ? GINT_TO_POINTER(record->gender) :
+ GGP_PUBDIR_GENDER_UNSPECIFIED);
+ purple_request_field_set_required(field, TRUE);
+ purple_request_field_choice_add(field, _("Male"),
+ GINT_TO_POINTER(GGP_PUBDIR_GENDER_MALE));
+ purple_request_field_choice_add(field, _("Female"),
+ GINT_TO_POINTER(GGP_PUBDIR_GENDER_FEMALE));
+ purple_request_field_group_add_field(group, field);
+
+ field = purple_request_field_string_new("birth_date", _("Birth Day"),
+ (record && record->birth) ?
+ ggp_date_strftime("%Y-%m-%d", record->birth) : NULL, FALSE);
+ purple_request_field_set_required(field, TRUE);
+ purple_request_field_group_add_field(group, field);
+
+ field = purple_request_field_string_new("city", _("City"),
+ record ? record->city : NULL, FALSE);
+ purple_request_field_group_add_field(group, field);
+
+ field = purple_request_field_choice_new("province", _("Voivodeship"), 0);
+ purple_request_field_group_add_field(group, field);
+ for (i = 0; i < ggp_pubdir_provinces_count; i++) {
+ purple_request_field_choice_add(field, ggp_pubdir_provinces[i],
+ GINT_TO_POINTER(i));
+ if (record && i == record->province) {
+ purple_request_field_choice_set_value(field, GINT_TO_POINTER(i));
+ purple_request_field_choice_set_default_value(field, GINT_TO_POINTER(i));
+ }
+ }
+
+ purple_request_fields(gc, _("Set User Info"), _("Set User Info"),
+ NULL, fields,
+ _("OK"), G_CALLBACK(ggp_pubdir_set_info_request),
+ _("Cancel"), NULL,
+ purple_request_cpar_from_connection(gc), gc);
+}
+
+static void ggp_pubdir_set_info_request(PurpleConnection *gc,
+ PurpleRequestFields *fields)
+{
+ gchar *url;
+ uin_t uin = ggp_str_to_uin(purple_account_get_username(
+ purple_connection_get_account(gc)));
+ ggp_pubdir_record *record = g_new0(ggp_pubdir_record, 1);
+ gchar *birth_s;
+
+ purple_debug_info("gg", "ggp_pubdir_set_info_request\n");
+
+ record->uin = uin;
+ record->first_name = g_strdup(purple_request_fields_get_string(fields,
+ "first_name"));
+ record->last_name = g_strdup(purple_request_fields_get_string(fields,
+ "last_name"));
+ record->gender = GPOINTER_TO_INT(
+ purple_request_fields_get_choice(fields, "gender"));
+ record->city = g_strdup(purple_request_fields_get_string(fields,
+ "city"));
+ record->province = GPOINTER_TO_INT(
+ purple_request_fields_get_choice(fields, "province"));
+
+ birth_s = g_strdup_printf("%sT10:00:00+00:00",
+ purple_request_fields_get_string(fields, "birth_date"));
+ record->birth = ggp_date_from_iso8601(birth_s);
+ g_free(birth_s);
+ purple_debug_info("gg", "ggp_pubdir_set_info_request: birth [%lu][%s]\n",
+ record->birth, purple_request_fields_get_string(
+ fields, "birth_date"));
+
+ url = g_strdup_printf("http://api.gadu-gadu.pl/users/%u.xml", uin);
+ ggp_oauth_request(gc, ggp_pubdir_set_info_got_token, record,
+ "PUT", url);
+ g_free(url);
+}
+
+static void ggp_pubdir_set_info_got_token(PurpleConnection *gc,
+ const gchar *token, gpointer _record)
+{
+ PurpleHttpRequest *req;
+ ggp_pubdir_record *record = _record;
+ gchar *request_data;
+ gchar *name, *surname, *city;
+ uin_t uin = record->uin;
+
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
+
+ if (!token) {
+ /* TODO: notify about failure */
+ ggp_pubdir_record_free(record, 1);
+ return;
+ }
+
+ name = g_uri_escape_string(record->first_name, NULL, FALSE);
+ surname = g_uri_escape_string(record->last_name, NULL, FALSE);
+ city = g_uri_escape_string(record->city, NULL, FALSE);
+
+ request_data = g_strdup_printf(
+ "name=%s&"
+ "surname=%s&"
+ "birth=%sT10:00:00%%2B00:00&"
+ "birth_priv=2&"
+ "gender=%d&"
+ "gender_priv=2&"
+ "city=%s&"
+ "province=%d",
+ name, surname,
+ ggp_date_strftime("%Y-%m-%d", record->birth),
+ record->gender,
+ city,
+ record->province);
+
+ if (purple_debug_is_verbose() && purple_debug_is_unsafe()) {
+ purple_debug_misc("gg", "ggp_pubdir_set_info_got_token: "
+ "query [%s]\n", request_data);
+ }
+
+ req = purple_http_request_new(NULL);
+ purple_http_request_set_method(req, "PUT");
+ purple_http_request_set_url_printf(req,
+ "http://api.gadu-gadu.pl/users/%u.xml", uin);
+ purple_http_request_header_set(req, "Authorization", token);
+ purple_http_request_header_set(req, "Content-Type",
+ "application/x-www-form-urlencoded");
+ purple_http_request_set_contents(req, request_data, -1);
+ purple_http_request(gc, req, ggp_pubdir_set_info_got_response, NULL);
+ purple_http_request_unref(req);
+
+ g_free(request_data);
+ ggp_pubdir_record_free(record, 1);
+}
+
+static void ggp_pubdir_set_info_got_response(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer user_data)
+{
+ if (!purple_http_response_is_successful(response)) {
+ purple_debug_error("gg", "ggp_pubdir_set_info_got_response: "
+ "failed\n");
+ return;
+ }
+
+ purple_debug_info("gg", "ggp_pubdir_set_info_got_response: [%s]\n",
+ purple_http_response_get_data(response, NULL));
+ /* <result><status>0</status></result> */
+
+ /* TODO: notify about failure */
+}
diff --git a/libpurple/protocols/gg/pubdir-prpl.h b/libpurple/protocols/gg/pubdir-prpl.h
new file mode 100644
index 0000000000..84ef22a60f
--- /dev/null
+++ b/libpurple/protocols/gg/pubdir-prpl.h
@@ -0,0 +1,73 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 _GGP_PUBDIR_PRPL_H
+#define _GGP_PUBDIR_PRPL_H
+
+#include <internal.h>
+#include <libgadu.h>
+
+typedef enum
+{
+ GGP_PUBDIR_GENDER_UNSPECIFIED,
+ GGP_PUBDIR_GENDER_FEMALE,
+ GGP_PUBDIR_GENDER_MALE,
+} ggp_pubdir_gender;
+
+typedef struct
+{
+ uin_t uin;
+ gchar *label;
+ gchar *nickname;
+ gchar *first_name;
+ gchar *last_name;
+ ggp_pubdir_gender gender;
+ gchar *city;
+ unsigned int province;
+ time_t birth;
+ unsigned int age;
+} ggp_pubdir_record;
+
+typedef struct _ggp_pubdir_search_form ggp_pubdir_search_form;
+
+typedef void (*ggp_pubdir_request_cb)(PurpleConnection *gc, int records_count,
+ const ggp_pubdir_record *records, int next_offset, void *user_data);
+
+void ggp_pubdir_get_info(PurpleConnection *gc, uin_t uin,
+ ggp_pubdir_request_cb cb, void *user_data);
+
+void ggp_pubdir_get_info_prpl(PurpleConnection *gc, const char *name);
+void ggp_pubdir_request_buddy_alias(PurpleConnection *gc, PurpleBuddy *buddy);
+
+void ggp_pubdir_search(PurpleConnection *gc,
+ const ggp_pubdir_search_form *form);
+
+void ggp_pubdir_set_info(PurpleConnection *gc);
+
+#endif /* _GGP_PUBDIR_PRPL_H */
diff --git a/libpurple/protocols/gg/purplew.c b/libpurple/protocols/gg/purplew.c
new file mode 100644
index 0000000000..ba3df716e5
--- /dev/null
+++ b/libpurple/protocols/gg/purplew.c
@@ -0,0 +1,152 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 "purplew.h"
+
+#include <request.h>
+#include <debug.h>
+
+#include "tcpsocket.h"
+
+guint ggp_purplew_http_input_add(struct gg_http *http_req,
+ PurpleInputFunction func, gpointer user_data)
+{
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("gg", "ggp_purplew_http_input_add: "
+ "[req=%p, fd=%d, cond=%d]\n",
+ http_req, http_req->fd, http_req->check);
+ }
+ return purple_input_add(http_req->fd,
+ ggp_tcpsocket_inputcond_gg_to_purple(http_req->check),
+ func, user_data);
+}
+
+static void ggp_purplew_request_processing_cancel(
+ ggp_purplew_request_processing_handle *handle, gint id)
+{
+ handle->cancel_cb(handle->gc, handle->user_data);
+ g_free(handle);
+}
+
+ggp_purplew_request_processing_handle * ggp_purplew_request_processing(
+ PurpleConnection *gc, const gchar *msg, void *user_data,
+ ggp_purplew_request_processing_cancel_cb cancel_cb)
+{
+ ggp_purplew_request_processing_handle *handle =
+ g_new(ggp_purplew_request_processing_handle, 1);
+
+ handle->gc = gc;
+ handle->cancel_cb = cancel_cb;
+ handle->user_data = user_data;
+ handle->request_handle = purple_request_action(gc, _("Please wait..."),
+ (msg ? msg : _("Please wait...")), NULL,
+ PURPLE_DEFAULT_ACTION_NONE,
+ purple_request_cpar_from_connection(gc), handle, 1,
+ _("Cancel"), G_CALLBACK(ggp_purplew_request_processing_cancel));
+
+ return handle;
+}
+
+void ggp_purplew_request_processing_done(
+ ggp_purplew_request_processing_handle *handle)
+{
+ purple_request_close(PURPLE_REQUEST_ACTION, handle->request_handle);
+ g_free(handle);
+}
+
+PurpleGroup * ggp_purplew_buddy_get_group_only(PurpleBuddy *buddy)
+{
+ PurpleGroup *group = purple_buddy_get_group(buddy);
+ if (!group)
+ return NULL;
+ if (0 == g_strcmp0(PURPLE_BLIST_DEFAULT_GROUP_NAME,
+ purple_group_get_name(group)))
+ {
+ return NULL;
+ }
+ if (0 == g_strcmp0("Buddies", purple_group_get_name(group)))
+ return NULL;
+ return group;
+}
+
+GList * ggp_purplew_group_get_buddies(PurpleGroup *group, PurpleAccount *account)
+{
+ GList *buddies = NULL;
+ PurpleBlistNode *gnode, *cnode, *bnode;
+
+ g_return_val_if_fail(group != NULL, NULL);
+
+ gnode = PURPLE_BLIST_NODE(group);
+ for (cnode = gnode->child; cnode; cnode = cnode->next) {
+ if (!PURPLE_IS_CONTACT(cnode))
+ continue;
+ for (bnode = cnode->child; bnode; bnode = bnode->next) {
+ PurpleBuddy *buddy;
+ if (!PURPLE_IS_BUDDY(bnode))
+ continue;
+
+ buddy = PURPLE_BUDDY(bnode);
+ if (account == NULL ||
+ purple_buddy_get_account(buddy) == account)
+ {
+ buddies = g_list_append(buddies, buddy);
+ }
+ }
+ }
+
+ return buddies;
+}
+
+GList * ggp_purplew_account_get_groups(PurpleAccount *account, gboolean exclusive)
+{
+ PurpleBlistNode *bnode;
+ GList *groups = NULL;
+ for (bnode = purple_blist_get_root(); bnode; bnode = bnode->next) {
+ PurpleGroup *group;
+ GSList *accounts;
+ gboolean have_specified = FALSE, have_others = FALSE;
+
+ if (!PURPLE_IS_GROUP(bnode))
+ continue;
+
+ group = PURPLE_GROUP(bnode);
+ for (accounts = purple_group_get_accounts(group); accounts;
+ accounts = g_slist_delete_link(accounts, accounts))
+ {
+ if (accounts->data == account)
+ have_specified = TRUE;
+ else
+ have_others = TRUE;
+ }
+
+ if (have_specified && (!exclusive || !have_others))
+ groups = g_list_append(groups, group);
+ }
+ return groups;
+}
diff --git a/libpurple/protocols/gg/purplew.h b/libpurple/protocols/gg/purplew.h
new file mode 100644
index 0000000000..bd73db6b13
--- /dev/null
+++ b/libpurple/protocols/gg/purplew.h
@@ -0,0 +1,76 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 _GGP_PURPLEW_H
+#define _GGP_PURPLEW_H
+
+#include <internal.h>
+#include <libgadu.h>
+
+/**
+ * Adds an input handler in purple event loop for http request.
+ *
+ * @see purple_input_add
+ *
+ * @param http_req Http connection to watch.
+ * @param func The callback function for data.
+ * @param user_data User-specified data.
+ *
+ * @return The resulting handle (will be greater than 0).
+ */
+guint ggp_purplew_http_input_add(struct gg_http *http_req,
+ PurpleInputFunction func, gpointer user_data);
+
+typedef void (*ggp_purplew_request_processing_cancel_cb)(PurpleConnection *gc,
+ void *user_data);
+
+typedef struct
+{
+ PurpleConnection *gc;
+ ggp_purplew_request_processing_cancel_cb cancel_cb;
+ void *request_handle;
+ void *user_data;
+} ggp_purplew_request_processing_handle;
+
+ggp_purplew_request_processing_handle * ggp_purplew_request_processing(
+ PurpleConnection *gc, const gchar *msg, void *user_data,
+ ggp_purplew_request_processing_cancel_cb oncancel);
+
+void ggp_purplew_request_processing_done(
+ ggp_purplew_request_processing_handle *handle);
+
+/* ignores default group */
+PurpleGroup * ggp_purplew_buddy_get_group_only(PurpleBuddy *buddy);
+
+GList * ggp_purplew_group_get_buddies(PurpleGroup *group, PurpleAccount *account);
+
+/* you must g_free returned list */
+GList * ggp_purplew_account_get_groups(PurpleAccount *account, gboolean exclusive);
+
+#endif /* _GGP_PURPLEW_H */
diff --git a/libpurple/protocols/gg/resolver-purple.c b/libpurple/protocols/gg/resolver-purple.c
new file mode 100644
index 0000000000..2f255ffb3c
--- /dev/null
+++ b/libpurple/protocols/gg/resolver-purple.c
@@ -0,0 +1,186 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 <dnsquery.h>
+
+#include <libgadu.h>
+#include "resolver-purple.h"
+
+static int ggp_resolver_purple_start(int *fd, void **private_data,
+ const char *hostname);
+
+static void ggp_resolver_purple_cleanup(void **private_data, int force);
+
+static void ggp_resolver_purple_cb(GSList *hosts, gpointer cbdata,
+ const char *error_message);
+
+typedef struct
+{
+ PurpleDnsQueryData *purpleQuery;
+
+ /**
+ * File descriptors:
+ * pipes[0] - for reading
+ * pipes[1] - for writing
+ */
+ int pipes[2];
+} ggp_resolver_purple_data;
+
+
+extern void ggp_resolver_purple_setup(void)
+{
+ if (gg_global_set_custom_resolver(ggp_resolver_purple_start,
+ ggp_resolver_purple_cleanup) != 0)
+ {
+ purple_debug_error("gg", "failed to set custom resolver\n");
+ }
+}
+
+void ggp_resolver_purple_cb(GSList *hosts, gpointer cbdata,
+ const char *error_message)
+{
+ ggp_resolver_purple_data *data = (ggp_resolver_purple_data*)cbdata;
+ const int fd = data->pipes[1];
+ int ipv4_count, all_count, write_size;
+ struct in_addr *addresses;
+
+ purple_debug_misc("gg", "ggp_resolver_purple_cb(%p, %p, \"%s\")\n",
+ hosts, cbdata, error_message);
+
+ data->purpleQuery = NULL;
+
+ if (error_message) {
+ purple_debug_error("gg", "ggp_resolver_purple_cb failed: %s\n",
+ error_message);
+ }
+
+ all_count = g_slist_length(hosts);
+ g_assert(all_count % 2 == 0);
+ all_count /= 2;
+ addresses = malloc((all_count + 1) * sizeof(struct in_addr));
+
+ ipv4_count = 0;
+ while (hosts && (hosts = g_slist_delete_link(hosts, hosts))) {
+ common_sockaddr_t addr;
+ char dst[INET6_ADDRSTRLEN];
+
+ memcpy(&addr, hosts->data, sizeof(addr));
+
+ if (addr.sa.sa_family == AF_INET6) {
+ inet_ntop(addr.sa.sa_family, &addr.in6.sin6_addr,
+ dst, sizeof(dst));
+ purple_debug_misc("gg", "ggp_resolver_purple_cb "
+ "ipv6 (ignore): %s\n", dst);
+ } else if (addr.sa.sa_family == AF_INET) {
+ inet_ntop(addr.sa.sa_family, &addr.in.sin_addr,
+ dst, sizeof(dst));
+ purple_debug_misc("gg", "ggp_resolver_purple_cb "
+ "ipv4: %s\n", dst);
+
+ g_assert(ipv4_count < all_count);
+ addresses[ipv4_count++] = addr.in.sin_addr;
+ } else {
+ purple_debug_warning("gg", "ggp_resolver_purple_cb "
+ "unexpected sa_family: %d\n",
+ addr.sa.sa_family);
+ }
+
+ g_free(hosts->data);
+ hosts = g_slist_delete_link(hosts, hosts);
+ }
+
+ addresses[ipv4_count].s_addr = INADDR_NONE;
+
+ write_size = (ipv4_count + 1) * sizeof(struct in_addr);
+ if (write(fd, addresses, write_size) != write_size) {
+ purple_debug_error("gg",
+ "ggp_resolver_purple_cb write error\n");
+ }
+ free(addresses);
+}
+
+int ggp_resolver_purple_start(int *fd, void **private_data,
+ const char *hostname)
+{
+ ggp_resolver_purple_data *data;
+ purple_debug_misc("gg", "ggp_resolver_purple_start(%p, %p, \"%s\")\n",
+ fd, private_data, hostname);
+
+ data = malloc(sizeof(ggp_resolver_purple_data));
+ *private_data = (void*)data;
+ data->purpleQuery = NULL;
+ data->pipes[0] = 0;
+ data->pipes[1] = 0;
+
+ if (purple_input_pipe(data->pipes) != 0) {
+ purple_debug_error("gg", "ggp_resolver_purple_start: "
+ "unable to create pipe\n");
+ ggp_resolver_purple_cleanup(private_data, 0);
+ return -1;
+ }
+
+ *fd = data->pipes[0];
+
+ /* account and port is unknown in this context */
+ data->purpleQuery = purple_dnsquery_a(NULL, hostname, 80,
+ ggp_resolver_purple_cb, (gpointer)data);
+
+ if (!data->purpleQuery) {
+ purple_debug_error("gg", "ggp_resolver_purple_start: "
+ "unable to call purple_dnsquery_a\n");
+ ggp_resolver_purple_cleanup(private_data, 0);
+ return -1;
+ }
+
+ return 0;
+}
+
+void ggp_resolver_purple_cleanup(void **private_data, int force)
+{
+ ggp_resolver_purple_data *data =
+ (ggp_resolver_purple_data*)(*private_data);
+
+ purple_debug_misc("gg", "ggp_resolver_purple_cleanup(%p, %d)\n",
+ private_data, force);
+
+ if (!data)
+ return;
+ *private_data = NULL;
+
+ if (data->purpleQuery)
+ purple_dnsquery_destroy(data->purpleQuery);
+ if (data->pipes[0])
+ close(data->pipes[0]);
+ if (data->pipes[1])
+ close(data->pipes[1]);
+
+ free(data);
+}
diff --git a/libpurple/protocols/gg/resolver-purple.h b/libpurple/protocols/gg/resolver-purple.h
new file mode 100644
index 0000000000..76ad26c85a
--- /dev/null
+++ b/libpurple/protocols/gg/resolver-purple.h
@@ -0,0 +1,38 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 _GGP_RESOLVER_PURPLE_H
+#define _GGP_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 */
diff --git a/libpurple/protocols/gg/roster.c b/libpurple/protocols/gg/roster.c
new file mode 100644
index 0000000000..e22931c40d
--- /dev/null
+++ b/libpurple/protocols/gg/roster.c
@@ -0,0 +1,1092 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 "roster.h"
+
+#include "gg.h"
+#include "xml.h"
+#include "utils.h"
+#include "purplew.h"
+
+#include <debug.h>
+#include <glibcompat.h>
+
+#define GGP_ROSTER_SYNC_SETT "gg-synchronized"
+#define GGP_ROSTER_DEBUG 0
+#define GGP_ROSTER_GROUPID_DEFAULT "00000000-0000-0000-0000-000000000000"
+#define GGP_ROSTER_GROUPID_BOTS "0b345af6-0001-0000-0000-000000000004"
+
+/* TODO: ignored contacts synchronization (?) */
+
+typedef struct
+{
+ int version;
+
+ PurpleXmlNode *xml;
+
+ PurpleXmlNode *groups_node, *contacts_node;
+
+ /**
+ * Key: (uin_t) user identifier
+ * Value: (PurpleXmlNode*) xml node for contact
+ */
+ GHashTable *contact_nodes;
+
+ /**
+ * Key: (gchar*) group id
+ * Value: (PurpleXmlNode*) xml node for group
+ */
+ GHashTable *group_nodes;
+
+ /**
+ * Key: (gchar*) group name
+ * Value: (gchar*) group id
+ */
+ GHashTable *group_ids;
+
+ /**
+ * Key: (gchar*) group id
+ * Value: (gchar*) group name
+ */
+ GHashTable *group_names;
+
+ gchar *bots_group_id;
+
+ gboolean needs_update;
+} ggp_roster_content;
+
+typedef struct
+{
+ enum
+ {
+ GGP_ROSTER_CHANGE_CONTACT_UPDATE,
+ GGP_ROSTER_CHANGE_CONTACT_REMOVE,
+ GGP_ROSTER_CHANGE_GROUP_RENAME,
+ } type;
+ union
+ {
+ uin_t uin;
+ struct
+ {
+ gchar *old_name;
+ gchar *new_name;
+ } group_rename;
+ } data;
+} ggp_roster_change;
+
+static inline ggp_roster_session_data *
+ggp_roster_get_rdata(PurpleConnection *gc);
+static void ggp_roster_content_free(ggp_roster_content *content);
+static void ggp_roster_change_free(gpointer change);
+static int ggp_roster_get_version(PurpleConnection *gc);
+static gboolean ggp_roster_timer_cb(gpointer _gc);
+#if GGP_ROSTER_DEBUG
+static void ggp_roster_dump(ggp_roster_content *content);
+#endif
+
+/* synchronization control */
+static gboolean ggp_roster_is_synchronized(PurpleBuddy *buddy);
+static void ggp_roster_set_synchronized(PurpleConnection *gc,
+ PurpleBuddy *buddy, gboolean synchronized);
+
+/* buddy list import */
+static gboolean ggp_roster_reply_list_read_group(PurpleXmlNode *node,
+ ggp_roster_content *content);
+static gboolean ggp_roster_reply_list_read_buddy(PurpleConnection *gc,
+ PurpleXmlNode *node, ggp_roster_content *content, GHashTable *remove_buddies);
+static void ggp_roster_reply_list(PurpleConnection *gc, uint32_t version,
+ const char *reply);
+
+/* buddy list export */
+static const gchar * ggp_roster_send_update_group_add(
+ ggp_roster_content *content, PurpleGroup *group);
+static gboolean ggp_roster_send_update_contact_update(PurpleConnection *gc,
+ ggp_roster_change *change);
+static gboolean ggp_roster_send_update_contact_remove(PurpleConnection *gc,
+ ggp_roster_change *change);
+static gboolean ggp_roster_send_update_group_rename(PurpleConnection *gc,
+ ggp_roster_change *change);
+static void ggp_roster_send_update(PurpleConnection *gc);
+static void ggp_roster_reply_ack(PurpleConnection *gc, uint32_t version);
+static void ggp_roster_reply_reject(PurpleConnection *gc, uint32_t version);
+
+/******************************************************************************/
+
+static inline ggp_roster_session_data *
+ggp_roster_get_rdata(PurpleConnection *gc)
+{
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+ return &accdata->roster_data;
+}
+
+static void ggp_roster_content_free(ggp_roster_content *content)
+{
+ if (content == NULL)
+ return;
+ if (content->xml)
+ purple_xmlnode_free(content->xml);
+ if (content->contact_nodes)
+ g_hash_table_destroy(content->contact_nodes);
+ if (content->group_nodes)
+ g_hash_table_destroy(content->group_nodes);
+ if (content->group_ids)
+ g_hash_table_destroy(content->group_ids);
+ if (content->group_names)
+ g_hash_table_destroy(content->group_names);
+ if (content->bots_group_id)
+ g_free(content->bots_group_id);
+ g_free(content);
+}
+
+static void ggp_roster_change_free(gpointer _change)
+{
+ ggp_roster_change *change = _change;
+
+ if (change->type == GGP_ROSTER_CHANGE_GROUP_RENAME) {
+ g_free(change->data.group_rename.old_name);
+ g_free(change->data.group_rename.new_name);
+ }
+
+ g_free(change);
+}
+
+static int ggp_roster_get_version(PurpleConnection *gc)
+{
+ ggp_roster_content *content = ggp_roster_get_rdata(gc)->content;
+ if (content == NULL)
+ return 0;
+ return content->version;
+}
+
+static gboolean ggp_roster_timer_cb(gpointer _gc)
+{
+ PurpleConnection *gc = _gc;
+
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
+
+ ggp_roster_send_update(gc);
+
+ return TRUE;
+}
+
+#if GGP_ROSTER_DEBUG
+static void ggp_roster_dump(ggp_roster_content *content)
+{
+ char *str;
+ int len;
+
+ g_return_if_fail(content != NULL);
+ g_return_if_fail(content->xml != NULL);
+
+ str = purple_xmlnode_to_formatted_str(content->xml, &len);
+ purple_debug_misc("gg", "ggp_roster_dump: [%s]\n", str);
+ g_free(str);
+}
+#endif
+
+/*******************************************************************************
+ * Setup.
+ ******************************************************************************/
+
+gboolean ggp_roster_enabled(void)
+{
+ static gboolean checked = FALSE;
+ static gboolean enabled;
+
+ if (!checked) {
+ enabled = gg_libgadu_check_feature(
+ GG_LIBGADU_FEATURE_USERLIST100);
+ checked = TRUE;
+ }
+ return enabled;
+}
+
+void ggp_roster_setup(PurpleConnection *gc)
+{
+ ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
+
+ rdata->content = NULL;
+ rdata->sent_updates = NULL;
+ rdata->pending_updates = NULL;
+ rdata->timer = 0;
+ rdata->is_updating = FALSE;
+
+ if (ggp_roster_enabled())
+ rdata->timer = purple_timeout_add_seconds(2,
+ ggp_roster_timer_cb, gc);
+}
+
+void ggp_roster_cleanup(PurpleConnection *gc)
+{
+ ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
+
+ if (rdata->timer)
+ purple_timeout_remove(rdata->timer);
+ ggp_roster_content_free(rdata->content);
+ g_list_free_full(rdata->sent_updates, ggp_roster_change_free);
+ g_list_free_full(rdata->pending_updates, ggp_roster_change_free);
+}
+
+/*******************************************************************************
+ * Synchronization control.
+ ******************************************************************************/
+
+static gboolean ggp_roster_is_synchronized(PurpleBuddy *buddy)
+{
+ gboolean ret = purple_blist_node_get_bool(PURPLE_BLIST_NODE(buddy),
+ GGP_ROSTER_SYNC_SETT);
+ return ret;
+}
+
+static void ggp_roster_set_synchronized(PurpleConnection *gc,
+ PurpleBuddy *buddy, gboolean synchronized)
+{
+ ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
+ uin_t uin = ggp_str_to_uin(purple_buddy_get_name(buddy));
+ ggp_roster_change *change;
+
+ purple_blist_node_set_bool(PURPLE_BLIST_NODE(buddy),
+ GGP_ROSTER_SYNC_SETT, synchronized);
+ if (!synchronized) {
+ change = g_new0(ggp_roster_change, 1);
+ change->type = GGP_ROSTER_CHANGE_CONTACT_UPDATE;
+ change->data.uin = uin;
+ rdata->pending_updates =
+ g_list_append(rdata->pending_updates, change);
+ }
+}
+
+void ggp_roster_request_update(PurpleConnection *gc)
+{
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+ int local_version = ggp_roster_get_version(gc);
+
+ if (!ggp_roster_enabled()) {
+ purple_debug_warning("gg", "ggp_roster_request_update: "
+ "feature disabled\n");
+ return;
+ }
+
+ purple_debug_info("gg", "ggp_roster_request_update: local=%u\n",
+ local_version);
+
+ gg_userlist100_request(accdata->session, GG_USERLIST100_GET,
+ local_version, GG_USERLIST100_FORMAT_TYPE_GG100, NULL);
+}
+
+/*******************************************************************************
+ * Libgadu callbacks.
+ ******************************************************************************/
+
+void ggp_roster_reply(PurpleConnection *gc,
+ struct gg_event_userlist100_reply *reply)
+{
+ if (GG_USERLIST100_FORMAT_TYPE_GG100 != reply->format_type) {
+ purple_debug_warning("gg", "ggp_roster_reply: "
+ "unsupported format type (%x)\n", reply->format_type);
+ return;
+ }
+
+ if (reply->type == GG_USERLIST100_REPLY_LIST)
+ ggp_roster_reply_list(gc, reply->version, reply->reply);
+ else if (reply->type == 0x01) /* list up to date (TODO: push to libgadu) */
+ purple_debug_info("gg", "ggp_roster_reply: list up to date\n");
+ else if (reply->type == GG_USERLIST100_REPLY_ACK)
+ ggp_roster_reply_ack(gc, reply->version);
+ else if (reply->type == GG_USERLIST100_REPLY_REJECT)
+ ggp_roster_reply_reject(gc, reply->version);
+ else
+ purple_debug_error("gg", "ggp_roster_reply: "
+ "unsupported reply (%x)\n", reply->type);
+}
+
+void ggp_roster_version(PurpleConnection *gc,
+ struct gg_event_userlist100_version *version)
+{
+ int local_version = ggp_roster_get_version(gc);
+ int remote_version = version->version;
+
+ purple_debug_info("gg", "ggp_roster_version: local=%u, remote=%u\n",
+ local_version, remote_version);
+
+ if (local_version < remote_version)
+ ggp_roster_request_update(gc);
+}
+
+/*******************************************************************************
+ * Libpurple callbacks.
+ ******************************************************************************/
+
+void ggp_roster_alias_buddy(PurpleConnection *gc, const char *who,
+ const char *alias)
+{
+ PurpleBuddy *buddy;
+
+ g_return_if_fail(who != NULL);
+
+ if (!ggp_roster_enabled())
+ return;
+
+ purple_debug_misc("gg", "ggp_roster_alias_buddy(\"%s\", \"%s\")\n",
+ who, alias);
+
+ buddy = purple_blist_find_buddy(purple_connection_get_account(gc), who);
+ g_return_if_fail(buddy != NULL);
+
+ ggp_roster_set_synchronized(gc, buddy, FALSE);
+}
+
+void ggp_roster_group_buddy(PurpleConnection *gc, const char *who,
+ const char *old_group, const char *new_group)
+{
+ ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
+ ggp_roster_change *change;
+
+ if (!ggp_roster_enabled())
+ return;
+ if (rdata->is_updating)
+ return;
+
+ purple_debug_misc("gg", "ggp_roster_group_buddy: "
+ "who=\"%s\", group=\"%s\" -> \"%s\")\n",
+ who, old_group, new_group);
+
+ /* purple_blist_find_buddy(..., who) is not accessible at this moment */
+ change = g_new0(ggp_roster_change, 1);
+ change->type = GGP_ROSTER_CHANGE_CONTACT_UPDATE;
+ change->data.uin = ggp_str_to_uin(who);
+ rdata->pending_updates = g_list_append(rdata->pending_updates, change);
+}
+
+void ggp_roster_rename_group(PurpleConnection *gc, const char *old_name,
+ PurpleGroup *group, GList *moved_buddies)
+{
+ ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
+ ggp_roster_change *change;
+
+ if (!ggp_roster_enabled())
+ return;
+
+ change = g_new0(ggp_roster_change, 1);
+ change->type = GGP_ROSTER_CHANGE_GROUP_RENAME;
+ change->data.group_rename.old_name = g_strdup(old_name);
+ change->data.group_rename.new_name =
+ g_strdup(purple_group_get_name(group));
+ rdata->pending_updates = g_list_append(rdata->pending_updates, change);
+}
+
+void ggp_roster_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
+ PurpleGroup *group, const char *message)
+{
+ g_return_if_fail(gc != NULL);
+ g_return_if_fail(buddy != NULL);
+
+ if (!ggp_roster_enabled())
+ return;
+
+ ggp_roster_set_synchronized(gc, buddy, FALSE);
+}
+
+void ggp_roster_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
+ PurpleGroup *group)
+{
+ ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
+ ggp_roster_change *change;
+
+ if (!ggp_roster_enabled())
+ return;
+
+ change = g_new0(ggp_roster_change, 1);
+ change->type = GGP_ROSTER_CHANGE_CONTACT_REMOVE;
+ change->data.uin = ggp_str_to_uin(purple_buddy_get_name(buddy));
+ rdata->pending_updates = g_list_append(rdata->pending_updates, change);
+}
+
+/*******************************************************************************
+ * Buddy list import.
+ ******************************************************************************/
+
+static gboolean ggp_roster_reply_list_read_group(PurpleXmlNode *node,
+ ggp_roster_content *content)
+{
+ char *name, *id;
+ gboolean removable;
+ gboolean succ = TRUE, is_bot, is_default;
+
+ succ &= ggp_xml_get_string(node, "Id", &id);
+ succ &= ggp_xml_get_string(node, "Name", &name);
+ succ &= ggp_xml_get_bool(node, "IsRemovable", &removable);
+
+ if (!succ) {
+ g_free(id);
+ g_free(name);
+ g_return_val_if_reached(FALSE);
+ }
+
+ is_bot = (strcmp(id, GGP_ROSTER_GROUPID_BOTS) == 0 ||
+ g_strcmp0(name, "Pomocnicy") == 0);
+ is_default = (strcmp(id, GGP_ROSTER_GROUPID_DEFAULT) == 0 ||
+ g_strcmp0(name, PURPLE_BLIST_DEFAULT_GROUP_NAME) == 0 ||
+ g_strcmp0(name, "[default]") == 0);
+
+ if (!content->bots_group_id && is_bot)
+ content->bots_group_id = g_strdup(id);
+
+ if (!removable || is_bot || is_default) {
+ g_free(id);
+ g_free(name);
+ return TRUE;
+ }
+
+ g_hash_table_insert(content->group_nodes, g_strdup(id), node);
+ g_hash_table_insert(content->group_ids, g_strdup(name), g_strdup(id));
+ g_hash_table_insert(content->group_names, id, name);
+
+ return TRUE;
+}
+
+static gboolean ggp_roster_reply_list_read_buddy(PurpleConnection *gc,
+ PurpleXmlNode *node, ggp_roster_content *content, GHashTable *remove_buddies)
+{
+ gchar *alias, *group_name = NULL;
+ uin_t uin;
+ gboolean succ = TRUE;
+ PurpleXmlNode *group_list, *group_elem;
+ PurpleBuddy *buddy = NULL;
+ PurpleGroup *group = NULL;
+ PurpleGroup *currentGroup;
+ gboolean alias_changed;
+ PurpleAccount *account = purple_connection_get_account(gc);
+
+ succ &= ggp_xml_get_string(node, "ShowName", &alias);
+ succ &= ggp_xml_get_uint(node, "GGNumber", &uin);
+
+ group_list = purple_xmlnode_get_child(node, "Groups");
+ succ &= (group_list != NULL);
+
+ if (!succ) {
+ g_free(alias);
+ g_return_val_if_reached(FALSE);
+ }
+
+ g_hash_table_insert(content->contact_nodes, GINT_TO_POINTER(uin), node);
+
+ /* check, if alias is set */
+ if (strlen(alias) == 0 ||
+ strcmp(alias, ggp_uin_to_str(uin)) == 0)
+ {
+ g_free(alias);
+ alias = NULL;
+ }
+
+ /* getting (eventually creating) group */
+ group_elem = purple_xmlnode_get_child(group_list, "GroupId");
+ while (group_elem != NULL) {
+ gchar *id;
+ gboolean isbot;
+
+ if (!ggp_xml_get_string(group_elem, NULL, &id))
+ continue;
+ isbot = (0 == g_strcmp0(id, content->bots_group_id));
+ group_name = g_hash_table_lookup(content->group_names, id);
+ g_free(id);
+
+ /* we don't want to import bots;
+ * they are inserted to roster by default
+ */
+ if (isbot) {
+ g_free(alias);
+ return TRUE;
+ }
+
+ if (group_name != NULL)
+ break;
+
+ group_elem = purple_xmlnode_get_next_twin(group_elem);
+ }
+ if (group_name) {
+ group = purple_blist_find_group(group_name);
+ if (!group) {
+ group = purple_group_new(group_name);
+ purple_blist_add_group(group, NULL);
+ }
+ }
+
+ /* add buddy, if doesn't exists */
+ buddy = purple_blist_find_buddy(account, ggp_uin_to_str(uin));
+ g_hash_table_remove(remove_buddies, GINT_TO_POINTER(uin));
+ if (!buddy) {
+ purple_debug_info("gg", "ggp_roster_reply_list_read_buddy: "
+ "adding %u (%s) to buddy list\n", uin, alias);
+ buddy = purple_buddy_new(account, ggp_uin_to_str(uin), alias);
+ purple_blist_add_buddy(buddy, NULL, group, NULL);
+ ggp_roster_set_synchronized(gc, buddy, TRUE);
+
+ g_free(alias);
+ return TRUE;
+ }
+
+ /* buddy exists, but is not synchronized - local list has priority */
+ if (!ggp_roster_is_synchronized(buddy)) {
+ purple_debug_misc("gg", "ggp_roster_reply_list_read_buddy: "
+ "ignoring not synchronized %u (%s)\n",
+ uin, purple_buddy_get_name(buddy));
+ g_free(alias);
+ return TRUE;
+ }
+
+ currentGroup = ggp_purplew_buddy_get_group_only(buddy);
+ alias_changed =
+ (0 != g_strcmp0(alias, purple_buddy_get_alias_only(buddy)));
+
+ if (currentGroup == group && !alias_changed) {
+ g_free(alias);
+ return TRUE;
+ }
+
+ purple_debug_misc("gg", "ggp_roster_reply_list_read_buddy: "
+ "updating %u (%s) - alias=\"%s\"->\"%s\", group=%p->%p (%s)\n",
+ uin, purple_buddy_get_name(buddy),
+ purple_buddy_get_alias(buddy), alias,
+ currentGroup, group, group_name);
+ if (alias_changed)
+ purple_buddy_set_local_alias(buddy, alias);
+ if (currentGroup != group)
+ purple_blist_add_buddy(buddy, NULL, group, NULL);
+
+ g_free(alias);
+ return TRUE;
+}
+
+static void ggp_roster_reply_list(PurpleConnection *gc, uint32_t version,
+ const char *data)
+{
+ ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
+ PurpleXmlNode *xml, *xml_it;
+ PurpleAccount *account;
+ GSList *local_buddies;
+ GHashTable *remove_buddies;
+ GList *update_buddies = NULL, *local_groups, *it, *table_values;
+ ggp_roster_content *content;
+
+ g_return_if_fail(gc != NULL);
+ g_return_if_fail(data != NULL);
+
+ account = purple_connection_get_account(gc);
+
+ purple_debug_info("gg", "ggp_roster_reply_list: got list, version=%u\n",
+ version);
+
+ xml = purple_xmlnode_from_str(data, -1);
+ if (xml == NULL) {
+ purple_debug_warning("gg", "ggp_roster_reply_list: "
+ "invalid xml\n");
+ return;
+ }
+
+ ggp_roster_content_free(rdata->content);
+ rdata->content = NULL;
+ rdata->is_updating = TRUE;
+ content = g_new0(ggp_roster_content, 1);
+ content->version = version;
+ content->xml = xml;
+ content->contact_nodes = g_hash_table_new(NULL, NULL);
+ content->group_nodes = g_hash_table_new_full(
+ g_str_hash, g_str_equal, g_free, NULL);
+ content->group_ids = g_hash_table_new_full(
+ g_str_hash, g_str_equal, g_free, g_free);
+ content->group_names = g_hash_table_new_full(
+ g_str_hash, g_str_equal, g_free, g_free);
+
+#if GGP_ROSTER_DEBUG
+ ggp_roster_dump(content);
+#endif
+
+ /* reading groups */
+ content->groups_node = purple_xmlnode_get_child(xml, "Groups");
+ if (content->groups_node == NULL) {
+ ggp_roster_content_free(content);
+ g_return_if_reached();
+ }
+ xml_it = purple_xmlnode_get_child(content->groups_node, "Group");
+ while (xml_it != NULL) {
+ if (!ggp_roster_reply_list_read_group(xml_it, content)) {
+ ggp_roster_content_free(content);
+ g_return_if_reached();
+ }
+
+ xml_it = purple_xmlnode_get_next_twin(xml_it);
+ }
+
+ /* dumping current group list */
+ local_groups = ggp_purplew_account_get_groups(account, TRUE);
+
+ /* dumping current buddy list
+ * we will:
+ * - remove synchronized ones, if not found in list at server
+ * - upload not synchronized ones
+ */
+ local_buddies = purple_blist_find_buddies(account, NULL);
+ remove_buddies = g_hash_table_new(g_direct_hash, g_direct_equal);
+ while (local_buddies) {
+ PurpleBuddy *buddy = local_buddies->data;
+ uin_t uin = ggp_str_to_uin(purple_buddy_get_name(buddy));
+ local_buddies =
+ g_slist_delete_link(local_buddies, local_buddies);
+
+ if (!uin)
+ continue;
+
+ if (ggp_roster_is_synchronized(buddy))
+ g_hash_table_insert(remove_buddies,
+ GINT_TO_POINTER(uin), buddy);
+ else
+ update_buddies = g_list_append(update_buddies, buddy);
+ }
+
+ /* reading buddies */
+ content->contacts_node = purple_xmlnode_get_child(xml, "Contacts");
+ if (content->contacts_node == NULL) {
+ g_hash_table_destroy(remove_buddies);
+ g_list_free(update_buddies);
+ ggp_roster_content_free(content);
+ g_return_if_reached();
+ }
+ xml_it = purple_xmlnode_get_child(content->contacts_node, "Contact");
+ while (xml_it != NULL) {
+ if (!ggp_roster_reply_list_read_buddy(gc, xml_it, content,
+ remove_buddies))
+ {
+ g_hash_table_destroy(remove_buddies);
+ g_list_free(update_buddies);
+ ggp_roster_content_free(content);
+ g_return_if_reached();
+ }
+
+ xml_it = purple_xmlnode_get_next_twin(xml_it);
+ }
+
+ /* removing buddies, which are not present in roster */
+ table_values = g_hash_table_get_values(remove_buddies);
+ it = g_list_first(table_values);
+ while (it) {
+ PurpleBuddy *buddy = it->data;
+ it = g_list_next(it);
+ if (!ggp_roster_is_synchronized(buddy))
+ continue;
+ purple_debug_info("gg", "ggp_roster_reply_list: "
+ "removing %s from buddy list\n",
+ purple_buddy_get_name(buddy));
+ purple_blist_remove_buddy(buddy);
+ }
+ g_list_free(table_values);
+ g_hash_table_destroy(remove_buddies);
+
+ /* remove groups, which are empty, but had contacts before
+ * synchronization
+ */
+ it = g_list_first(local_groups);
+ while (it) {
+ PurpleGroup *group = it->data;
+ it = g_list_next(it);
+ if (purple_counting_node_get_total_size(PURPLE_COUNTING_NODE(group)) != 0)
+ continue;
+ purple_debug_info("gg", "ggp_roster_reply_list: "
+ "removing group %s\n", purple_group_get_name(group));
+ purple_blist_remove_group(group);
+ }
+ g_list_free(local_groups);
+
+ /* adding not synchronized buddies */
+ it = g_list_first(update_buddies);
+ while (it) {
+ PurpleBuddy *buddy = it->data;
+ uin_t uin = ggp_str_to_uin(purple_buddy_get_name(buddy));
+ ggp_roster_change *change;
+
+ it = g_list_next(it);
+ g_assert(uin > 0);
+
+ purple_debug_misc("gg", "ggp_roster_reply_list: "
+ "adding change of %u for roster\n", uin);
+ change = g_new0(ggp_roster_change, 1);
+ change->type = GGP_ROSTER_CHANGE_CONTACT_UPDATE;
+ change->data.uin = uin;
+ rdata->pending_updates =
+ g_list_append(rdata->pending_updates, change);
+ }
+ g_list_free(update_buddies);
+
+ rdata->content = content;
+ rdata->is_updating = FALSE;
+ purple_debug_info("gg", "ggp_roster_reply_list: "
+ "import done, version=%u\n", version);
+}
+
+/*******************************************************************************
+ * Buddy list export.
+ ******************************************************************************/
+
+static const gchar * ggp_roster_send_update_group_add(
+ ggp_roster_content *content, PurpleGroup *group)
+{
+ gchar *id_dyn;
+ const char *id_existing, *group_name;
+ static gchar id[40];
+ PurpleXmlNode *group_node;
+ gboolean succ = TRUE;
+
+ if (group) {
+ group_name = purple_group_get_name(group);
+ id_existing =
+ g_hash_table_lookup(content->group_ids, group_name);
+ } else
+ id_existing = GGP_ROSTER_GROUPID_DEFAULT;
+ if (id_existing)
+ return id_existing;
+
+ purple_debug_info("gg", "ggp_roster_send_update_group_add: adding %s\n",
+ purple_group_get_name(group));
+
+ id_dyn = purple_uuid_random();
+ g_snprintf(id, sizeof(id), "%s", id_dyn);
+ g_free(id_dyn);
+
+ group_node = purple_xmlnode_new_child(content->groups_node, "Group");
+ succ &= ggp_xml_set_string(group_node, "Id", id);
+ succ &= ggp_xml_set_string(group_node, "Name", group_name);
+ succ &= ggp_xml_set_string(group_node, "IsExpanded", "true");
+ succ &= ggp_xml_set_string(group_node, "IsRemovable", "true");
+ content->needs_update = TRUE;
+
+ g_hash_table_insert(content->group_ids, g_strdup(group_name),
+ g_strdup(id));
+ g_hash_table_insert(content->group_nodes, g_strdup(id), group_node);
+
+ g_return_val_if_fail(succ, NULL);
+
+ return id;
+}
+
+static gboolean ggp_roster_send_update_contact_update(PurpleConnection *gc,
+ ggp_roster_change *change)
+{
+ PurpleAccount *account = purple_connection_get_account(gc);
+ ggp_roster_content *content = ggp_roster_get_rdata(gc)->content;
+ uin_t uin = change->data.uin;
+ PurpleBuddy *buddy;
+ PurpleXmlNode *buddy_node, *contact_groups;
+ gboolean succ = TRUE;
+ const char *group_id;
+
+ g_return_val_if_fail(change->type == GGP_ROSTER_CHANGE_CONTACT_UPDATE,
+ FALSE);
+
+ buddy = purple_blist_find_buddy(account, ggp_uin_to_str(uin));
+ if (!buddy)
+ return TRUE;
+ buddy_node = g_hash_table_lookup(content->contact_nodes,
+ GINT_TO_POINTER(uin));
+
+ group_id = ggp_roster_send_update_group_add(content,
+ ggp_purplew_buddy_get_group_only(buddy));
+
+ if (buddy_node) { /* update existing */
+ purple_debug_misc("gg", "ggp_roster_send_update_contact_update:"
+ " updating %u...\n", uin);
+
+ succ &= ggp_xml_set_string(buddy_node, "ShowName",
+ purple_buddy_get_alias(buddy));
+
+ contact_groups = purple_xmlnode_get_child(buddy_node, "Groups");
+ g_assert(contact_groups);
+ ggp_xmlnode_remove_children(contact_groups);
+ succ &= ggp_xml_set_string(contact_groups, "GroupId", group_id);
+
+ g_return_val_if_fail(succ, FALSE);
+
+ return TRUE;
+ }
+
+ /* add new */
+ purple_debug_misc("gg", "ggp_roster_send_update_contact_update: "
+ "adding %u...\n", uin);
+ buddy_node = purple_xmlnode_new_child(content->contacts_node, "Contact");
+ succ &= ggp_xml_set_string(buddy_node, "Guid", purple_uuid_random());
+ succ &= ggp_xml_set_uint(buddy_node, "GGNumber", uin);
+ succ &= ggp_xml_set_string(buddy_node, "ShowName",
+ purple_buddy_get_alias(buddy));
+
+ contact_groups = purple_xmlnode_new_child(buddy_node, "Groups");
+ g_assert(contact_groups);
+ succ &= ggp_xml_set_string(contact_groups, "GroupId", group_id);
+
+ purple_xmlnode_new_child(buddy_node, "Avatars");
+ succ &= ggp_xml_set_bool(buddy_node, "FlagBuddy", TRUE);
+ succ &= ggp_xml_set_bool(buddy_node, "FlagNormal", TRUE);
+ succ &= ggp_xml_set_bool(buddy_node, "FlagFriend", TRUE);
+
+ /* we don't use Guid, so update is not needed
+ * content->needs_update = TRUE;
+ */
+
+ g_hash_table_insert(content->contact_nodes, GINT_TO_POINTER(uin),
+ buddy_node);
+
+ g_return_val_if_fail(succ, FALSE);
+
+ return TRUE;
+}
+
+static gboolean ggp_roster_send_update_contact_remove(PurpleConnection *gc,
+ ggp_roster_change *change)
+{
+ PurpleAccount *account = purple_connection_get_account(gc);
+ ggp_roster_content *content = ggp_roster_get_rdata(gc)->content;
+ uin_t uin = change->data.uin;
+ PurpleBuddy *buddy;
+ PurpleXmlNode *buddy_node;
+
+ g_return_val_if_fail(change->type == GGP_ROSTER_CHANGE_CONTACT_REMOVE,
+ FALSE);
+
+ buddy = purple_blist_find_buddy(account, ggp_uin_to_str(uin));
+ if (buddy) {
+ purple_debug_info("gg", "ggp_roster_send_update_contact_remove:"
+ " contact %u re-added\n", uin);
+ return TRUE;
+ }
+
+ buddy_node = g_hash_table_lookup(content->contact_nodes,
+ GINT_TO_POINTER(uin));
+ if (!buddy_node) /* already removed */
+ return TRUE;
+
+ purple_debug_info("gg", "ggp_roster_send_update_contact_remove: "
+ "removing %u\n", uin);
+ purple_xmlnode_free(buddy_node);
+ g_hash_table_remove(content->contact_nodes, GINT_TO_POINTER(uin));
+
+ return TRUE;
+}
+
+static gboolean ggp_roster_send_update_group_rename(PurpleConnection *gc,
+ ggp_roster_change *change)
+{
+ PurpleAccount *account = purple_connection_get_account(gc);
+ ggp_roster_content *content = ggp_roster_get_rdata(gc)->content;
+ const char *old_name = change->data.group_rename.old_name;
+ const char *new_name = change->data.group_rename.new_name;
+ PurpleXmlNode *group_node;
+ const char *group_id;
+
+ g_return_val_if_fail(change->type == GGP_ROSTER_CHANGE_GROUP_RENAME,
+ FALSE);
+
+ purple_debug_misc("gg", "ggp_roster_send_update_group_rename: "
+ "\"%s\"->\"%s\"\n", old_name, new_name);
+
+ /* moving to or from default group instead of renaming it */
+ if (0 == g_strcmp0(old_name, PURPLE_BLIST_DEFAULT_GROUP_NAME) ||
+ 0 == g_strcmp0(new_name, PURPLE_BLIST_DEFAULT_GROUP_NAME))
+ {
+ PurpleGroup *group;
+ GList *group_buddies;
+ group = purple_blist_find_group(new_name);
+ if (!group)
+ return TRUE;
+ purple_debug_info("gg", "ggp_roster_send_update_group_rename: "
+ "invalidating buddies in default group\n");
+ group_buddies = ggp_purplew_group_get_buddies(group, account);
+ while (group_buddies) {
+ ggp_roster_set_synchronized(gc, group_buddies->data,
+ FALSE);
+ group_buddies = g_list_delete_link(group_buddies,
+ group_buddies);
+ }
+ return TRUE;
+ }
+ group_id = g_hash_table_lookup(content->group_ids, old_name);
+ if (!group_id) {
+ purple_debug_info("gg", "ggp_roster_send_update_group_rename: "
+ "%s is not present at roster\n", old_name);
+ return TRUE;
+ }
+
+ group_node = g_hash_table_lookup(content->group_nodes, group_id);
+ if (!group_node) {
+ purple_debug_error("gg", "ggp_roster_send_update_group_rename: "
+ "node for %s not found, id=%s\n", old_name, group_id);
+ g_hash_table_remove(content->group_ids, old_name);
+ return TRUE;
+ }
+
+ g_hash_table_remove(content->group_ids, old_name);
+ g_hash_table_insert(content->group_ids, g_strdup(new_name),
+ g_strdup(group_id));
+ g_hash_table_insert(content->group_nodes, g_strdup(group_id),
+ group_node);
+ return ggp_xml_set_string(group_node, "Name", new_name);
+}
+
+static void ggp_roster_send_update(PurpleConnection *gc)
+{
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+ ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
+ ggp_roster_content *content = rdata->content;
+ GList *updates_it;
+ gchar *str;
+ int len;
+
+ /* an update is running now */
+ if (rdata->sent_updates)
+ return;
+
+ /* no pending updates found */
+ if (!rdata->pending_updates)
+ return;
+
+ /* not initialized */
+ if (!content)
+ return;
+
+ purple_debug_info("gg", "ggp_roster_send_update: "
+ "pending updates found\n");
+
+ rdata->sent_updates = rdata->pending_updates;
+ rdata->pending_updates = NULL;
+
+ updates_it = g_list_first(rdata->sent_updates);
+ while (updates_it) {
+ ggp_roster_change *change = updates_it->data;
+ gboolean succ = FALSE;
+ updates_it = g_list_next(updates_it);
+
+ if (change->type == GGP_ROSTER_CHANGE_CONTACT_UPDATE)
+ succ = ggp_roster_send_update_contact_update(gc,
+ change);
+ else if (change->type == GGP_ROSTER_CHANGE_CONTACT_REMOVE)
+ succ = ggp_roster_send_update_contact_remove(gc,
+ change);
+ else if (change->type == GGP_ROSTER_CHANGE_GROUP_RENAME)
+ succ = ggp_roster_send_update_group_rename(gc, change);
+ else
+ purple_debug_fatal("gg", "ggp_roster_send_update: "
+ "not handled\n");
+ g_return_if_fail(succ);
+ }
+
+#if GGP_ROSTER_DEBUG
+ ggp_roster_dump(content);
+#endif
+
+ str = purple_xmlnode_to_str(content->xml, &len);
+ gg_userlist100_request(accdata->session, GG_USERLIST100_PUT,
+ content->version, GG_USERLIST100_FORMAT_TYPE_GG100, str);
+ g_free(str);
+}
+
+static void ggp_roster_reply_ack(PurpleConnection *gc, uint32_t version)
+{
+ PurpleAccount *account = purple_connection_get_account(gc);
+ ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
+ ggp_roster_content *content = rdata->content;
+ GList *updates_it;
+
+ purple_debug_info("gg", "ggp_roster_reply_ack: version=%u\n", version);
+
+ /* set synchronization flag for all buddies, that were updated at roster */
+ updates_it = g_list_first(rdata->sent_updates);
+ while (updates_it) {
+ ggp_roster_change *change = updates_it->data;
+ PurpleBuddy *buddy;
+ updates_it = g_list_next(updates_it);
+
+ if (change->type != GGP_ROSTER_CHANGE_CONTACT_UPDATE)
+ continue;
+
+ buddy = purple_blist_find_buddy(account,
+ ggp_uin_to_str(change->data.uin));
+ if (buddy)
+ ggp_roster_set_synchronized(gc, buddy, TRUE);
+ }
+
+ /* we need to remove "synchronized" flag for all contacts, that have
+ * beed modified between roster update start and now
+ */
+ updates_it = g_list_first(rdata->pending_updates);
+ while (updates_it) {
+ ggp_roster_change *change = updates_it->data;
+ PurpleBuddy *buddy;
+ updates_it = g_list_next(updates_it);
+
+ if (change->type != GGP_ROSTER_CHANGE_CONTACT_UPDATE)
+ continue;
+
+ buddy = purple_blist_find_buddy(account,
+ ggp_uin_to_str(change->data.uin));
+ if (buddy && ggp_roster_is_synchronized(buddy))
+ ggp_roster_set_synchronized(gc, buddy, FALSE);
+ }
+
+ g_list_free_full(rdata->sent_updates, ggp_roster_change_free);
+ rdata->sent_updates = NULL;
+
+ /* bump roster version or update it, if needed */
+ g_return_if_fail(content != NULL);
+ if (content->needs_update) {
+ ggp_roster_content_free(rdata->content);
+ rdata->content = NULL;
+ /* we have to wait for gg_event_userlist100_version
+ * ggp_roster_request_update(gc);
+ */
+ }
+ else
+ content->version = version;
+}
+
+static void ggp_roster_reply_reject(PurpleConnection *gc, uint32_t version)
+{
+ ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
+
+ purple_debug_info("gg", "ggp_roster_reply_reject: version=%u\n",
+ version);
+
+ g_return_if_fail(rdata->sent_updates);
+
+ rdata->pending_updates = g_list_concat(rdata->pending_updates,
+ rdata->sent_updates);
+ rdata->sent_updates = NULL;
+
+ ggp_roster_content_free(rdata->content);
+ rdata->content = NULL;
+ ggp_roster_request_update(gc);
+}
+
+/******************************************************************************/
diff --git a/libpurple/protocols/gg/roster.h b/libpurple/protocols/gg/roster.h
new file mode 100644
index 0000000000..b7c5fb2d84
--- /dev/null
+++ b/libpurple/protocols/gg/roster.h
@@ -0,0 +1,73 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 _GGP_ROSTER_H
+#define _GGP_ROSTER_H
+
+#include <internal.h>
+#include <libgadu.h>
+
+typedef struct
+{
+ gpointer content;
+ gboolean is_updating;
+
+ GList *sent_updates;
+ GList *pending_updates;
+
+ guint timer;
+} ggp_roster_session_data;
+
+/* setup */
+gboolean ggp_roster_enabled(void);
+void ggp_roster_setup(PurpleConnection *gc);
+void ggp_roster_cleanup(PurpleConnection *gc);
+
+/* synchronization control */
+void ggp_roster_request_update(PurpleConnection *gc);
+
+/* libgadu callbacks */
+void ggp_roster_reply(PurpleConnection *gc,
+ struct gg_event_userlist100_reply *reply);
+void ggp_roster_version(PurpleConnection *gc,
+ struct gg_event_userlist100_version *version);
+
+/* libpurple callbacks */
+void ggp_roster_alias_buddy(PurpleConnection *gc, const char *who,
+ const char *alias);
+void ggp_roster_group_buddy(PurpleConnection *gc, const char *who,
+ const char *old_group, const char *new_group);
+void ggp_roster_rename_group(PurpleConnection *, const char *old_name,
+ PurpleGroup *group, GList *moved_buddies);
+void ggp_roster_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
+ PurpleGroup *group, const char *message);
+void ggp_roster_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
+ PurpleGroup *group);
+
+#endif /* _GGP_ROSTER_H */
diff --git a/libpurple/protocols/gg/search.c b/libpurple/protocols/gg/search.c
deleted file mode 100644
index ebafe74e46..0000000000
--- a/libpurple/protocols/gg/search.c
+++ /dev/null
@@ -1,223 +0,0 @@
-/**
- * @file search.c
- *
- * purple
- *
- * Copyright (C) 2005 Bartosz Oler <bartosz@bzimage.us>
- *
- * 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 <libgadu.h>
-
-#include "gg-utils.h"
-#include "search.h"
-
-
-/* GGPSearchForm *ggp_search_form_new() {{{ */
-GGPSearchForm *ggp_search_form_new(GGPSearchType st)
-{
- GGPSearchForm *form;
-
- form = g_new0(GGPSearchForm, 1);
-
- form->search_type = st;
- form->window = NULL;
- form->user_data = NULL;
- form->seq = 0;
- form->page_number = 0;
- form->page_size = 0;
-
- form->uin = NULL;
- form->lastname = NULL;
- form->firstname = NULL;
- form->nickname = NULL;
- form->city = NULL;
- form->birthyear = NULL;
- form->gender = NULL;
- form->active = NULL;
-
- return form;
-}
-/* }}} */
-
-/* void ggp_search_form_destroy(GGPSearchForm *form) {{{ */
-void ggp_search_form_destroy(GGPSearchForm *form)
-{
- g_return_if_fail(form != NULL);
-
- form->window = NULL;
- form->user_data = NULL;
- form->seq = 0;
- form->page_number = 0;
- form->page_size = 0;
-
- g_free(form->uin);
- g_free(form->lastname);
- g_free(form->firstname);
- g_free(form->nickname);
- g_free(form->city);
- g_free(form->birthyear);
- g_free(form->gender);
- g_free(form->active);
- g_free(form);
-}
-/* }}} */
-
-/* void ggp_search_add(GGPSearches *searches, guint32 seq, GGPSearchForm *form) {{{ */
-void ggp_search_add(GGPSearches *searches, guint32 seq, GGPSearchForm *form)
-{
- guint32 *tmp;
-
- g_return_if_fail(searches != NULL);
- g_return_if_fail(form != NULL);
-
- tmp = g_new0(guint32, 1);
- *tmp = seq;
- form->seq = seq;
-
- g_hash_table_insert(searches, tmp, form);
-}
-/* }}} */
-
-/* void ggp_search_remove(GGPSearches *searches, guint32 seq) {{{ */
-void ggp_search_remove(GGPSearches *searches, guint32 seq)
-{
- g_return_if_fail(searches != NULL);
-
- g_hash_table_remove(searches, &seq);
-}
-/* }}} */
-
-/* GGPSearchForm *ggp_search_get(GGPSearches *searches, seq) {{{ */
-GGPSearchForm *ggp_search_get(GGPSearches *searches, guint32 seq)
-{
- g_return_val_if_fail(searches != NULL, NULL);
-
- return g_hash_table_lookup(searches, &seq);
-}
-/* }}} */
-
-/* GGPSearches *ggp_search_new() {{{ */
-GGPSearches *ggp_search_new(void)
-{
- GGPSearches *searches;
-
- searches = g_hash_table_new_full(g_int_hash, g_int_equal,
- g_free, NULL);
-
- return searches;
-}
-/* }}} */
-
-/* void ggp_search_destroy(GGPSearches *searches) {{{ */
-void ggp_search_destroy(GGPSearches *searches)
-{
- g_return_if_fail(searches != NULL);
-
- g_hash_table_destroy(searches);
-}
-/* }}} */
-
-/* guint32 ggp_search_start(PurpleConnection *gc, GGPSearchForm *form) {{{ */
-guint32 ggp_search_start(PurpleConnection *gc, GGPSearchForm *form)
-{
- GGPInfo *info = gc->proto_data;
- gg_pubdir50_t req;
- guint seq, offset;
- gchar *tmp;
-
- purple_debug_info("gg", "It's time to perform a search...\n");
-
- if ((req = gg_pubdir50_new(GG_PUBDIR50_SEARCH)) == NULL) {
- purple_debug_error("gg",
- "ggp_bmenu_show_details: Unable to create req variable.\n");
- return 0;
- }
-
- if (form->uin != NULL) {
- purple_debug_info("gg", " uin: %s\n", form->uin);
- gg_pubdir50_add(req, GG_PUBDIR50_UIN, form->uin);
- } else {
- if (form->lastname != NULL) {
- purple_debug_info("gg", " lastname: %s\n", form->lastname);
- gg_pubdir50_add(req, GG_PUBDIR50_LASTNAME, form->lastname);
- }
-
- if (form->firstname != NULL) {
- purple_debug_info("gg", " firstname: %s\n", form->firstname);
- gg_pubdir50_add(req, GG_PUBDIR50_FIRSTNAME, form->firstname);
- }
-
- if (form->nickname != NULL) {
- purple_debug_info("gg", " nickname: %s\n", form->nickname);
- gg_pubdir50_add(req, GG_PUBDIR50_NICKNAME, form->nickname);
- }
-
- if (form->city != NULL) {
- purple_debug_info("gg", " city: %s\n", form->city);
- gg_pubdir50_add(req, GG_PUBDIR50_CITY, form->city);
- }
-
- if (form->birthyear != NULL) {
- purple_debug_info("gg", " birthyear: %s\n", form->birthyear);
- gg_pubdir50_add(req, GG_PUBDIR50_BIRTHYEAR, form->birthyear);
- }
-
- if (form->gender != NULL) {
- purple_debug_info("gg", " gender: %s\n", form->gender);
- gg_pubdir50_add(req, GG_PUBDIR50_GENDER, form->gender);
- }
-
- if (form->active != NULL) {
- purple_debug_info("gg", " active: %s\n", form->active);
- gg_pubdir50_add(req, GG_PUBDIR50_ACTIVE, form->active);
- }
- }
-
- offset = form->page_size * form->page_number;
- purple_debug_info("gg", "page number: %u, page size: %u, offset: %u\n",
- form->page_number, form->page_size, offset);
- tmp = g_strdup_printf("%u", offset);
- gg_pubdir50_add(req, GG_PUBDIR50_START, tmp);
- g_free(tmp);
-
- if ((seq = gg_pubdir50(info->session, req)) == 0) {
- purple_debug_warning("gg", "ggp_bmenu_show_details: Search failed.\n");
- gg_pubdir50_free(req);
- return 0;
- }
-
- purple_debug_info("gg", "search sequence number: %d\n", seq);
- gg_pubdir50_free(req);
-
- return seq;
-}
-/* }}} */
-
-/* char *ggp_search_get_result(gg_pubdir50_t res, int num, const char *field) {{{ */
-char *ggp_search_get_result(gg_pubdir50_t res, int num, const char *field)
-{
- char *tmp;
-
- tmp = g_strdup(gg_pubdir50_get(res, num, field));
-
- return (tmp == NULL) ? g_strdup("") : tmp;
-}
-/* }}} */
-
-
-/* vim: set ts=8 sts=0 sw=8 noet: */
diff --git a/libpurple/protocols/gg/search.h b/libpurple/protocols/gg/search.h
deleted file mode 100644
index c1f5a9339a..0000000000
--- a/libpurple/protocols/gg/search.h
+++ /dev/null
@@ -1,152 +0,0 @@
-/**
- * @file search.h
- *
- * purple
- *
- * Copyright (C) 2005 Bartosz Oler <bartosz@bzimage.us>
- *
- * 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 _PURPLE_GG_SEARCH_H
-#define _PURPLE_GG_SEARCH_H
-
-#include "connection.h"
-
-#include <libgadu.h>
-#include "gg.h"
-
-
-typedef enum {
- GGP_SEARCH_TYPE_INFO,
- GGP_SEARCH_TYPE_FULL
-
-} GGPSearchType;
-
-typedef struct {
-
- char *uin;
- char *lastname;
- char *firstname;
- char *nickname;
- char *city;
- char *birthyear;
- char *gender;
- char *active;
-
- GGPSearchType search_type;
- guint32 seq;
- guint16 page_number;
- guint16 page_size; /* how many contacts fits into one page of results */
-
- void *user_data;
- void *window;
-} GGPSearchForm;
-
-typedef GHashTable GGPSearches;
-
-
-/**
- * Create a new GGPSearchForm structure, and set the fields
- * to the sane defaults.
- *
- * @return Newly allocated GGPSearchForm.
- */
-GGPSearchForm *
-ggp_search_form_new(GGPSearchType st);
-
-/**
- * Destroy a Search Form.
- *
- * @param form Search Form to destroy.
- */
-void
-ggp_search_form_destroy(GGPSearchForm *form);
-
-/**
- * Add a search to the list of searches.
- *
- * @param searches The list of searches.
- * @param seq Search (form) ID number.
- * @param form The search form to add.
- */
-void
-ggp_search_add(GGPSearches *searches, guint32 seq, GGPSearchForm *form);
-
-/**
- * Remove a search from the list.
- *
- * If you want to destory the search completely also call:
- * ggp_search_form_destroy().
- *
- * @param searches The list of searches.
- * @param seq ID number of the search.
- */
-void
-ggp_search_remove(GGPSearches *searches, guint32 seq);
-
-/**
- * Return the search with the specified ID.
- *
- * @param searches The list of searches.
- * @param seq ID number of the search.
- */
-GGPSearchForm *
-ggp_search_get(GGPSearches *searches, guint32 seq);
-
-/**
- * Create a new GGPSearches structure.
- *
- * @return GGPSearches instance.
- */
-GGPSearches *
-ggp_search_new(void);
-
-/**
- * Destroy GGPSearches instance.
- *
- * @param searches GGPSearches instance.
- */
-void
-ggp_search_destroy(GGPSearches *searches);
-
-/**
- * Initiate a search in the public directory.
- *
- * @param gc PurpleConnection.
- * @param form Filled in GGPSearchForm.
- *
- * @return Sequence number of a search or 0 if an error occurred.
- */
-guint32
-ggp_search_start(PurpleConnection *gc, GGPSearchForm *form);
-
-/*
- * Return converted to the UTF-8 value of the specified field.
- *
- * @param res Public directory look-up result.
- * @param num Id of the record.
- * @param fileld Name of the field.
- *
- * @return UTF-8 encoded value of the field.
- */
-char *
-ggp_search_get_result(gg_pubdir50_t res, int num, const char *field);
-
-
-#endif /* _PURPLE_GG_SEARCH_H */
-
-/* vim: set ts=8 sts=0 sw=8 noet: */
diff --git a/libpurple/protocols/gg/servconn.c b/libpurple/protocols/gg/servconn.c
new file mode 100644
index 0000000000..9eb583ca2c
--- /dev/null
+++ b/libpurple/protocols/gg/servconn.c
@@ -0,0 +1,105 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 "servconn.h"
+
+#include "utils.h"
+
+#include <debug.h>
+#include <glibcompat.h>
+
+#define GGP_SERVCONN_HISTORY_PREF "/plugins/prpl/gg/server_history"
+#define GGP_SERVCONN_HISTORY_MAXLEN 15
+
+typedef struct
+{
+ GList *server_history;
+ PurpleAccountOption *server_option;
+} ggp_servconn_global_data;
+
+static ggp_servconn_global_data global_data;
+
+void ggp_servconn_setup(PurpleAccountOption *server_option)
+{
+ purple_prefs_add_string(GGP_SERVCONN_HISTORY_PREF, "");
+
+ global_data.server_option = server_option;
+ global_data.server_history = ggp_strsplit_list(purple_prefs_get_string(
+ GGP_SERVCONN_HISTORY_PREF), ";", GGP_SERVCONN_HISTORY_MAXLEN + 1);
+ global_data.server_history = ggp_list_truncate(
+ global_data.server_history, GGP_SERVCONN_HISTORY_MAXLEN,
+ g_free);
+
+ purple_account_option_string_set_hints(global_data.server_option,
+ ggp_servconn_get_servers());
+}
+
+void ggp_servconn_cleanup(void)
+{
+ g_list_free_full(global_data.server_history, &g_free);
+}
+
+void ggp_servconn_add_server(const gchar *server)
+{
+ GList *old_entry;
+ gchar *joined;
+
+ old_entry = g_list_find_custom(global_data.server_history, server,
+ (GCompareFunc)g_strcmp0);
+ if (old_entry) {
+ g_free(old_entry->data);
+ global_data.server_history = g_list_delete_link(
+ global_data.server_history, old_entry);
+ }
+
+ global_data.server_history = g_list_prepend(global_data.server_history,
+ g_strdup(server));
+ global_data.server_history = ggp_list_truncate(
+ global_data.server_history, GGP_SERVCONN_HISTORY_MAXLEN,
+ g_free);
+
+ joined = ggp_strjoin_list(";", global_data.server_history);
+ purple_prefs_set_string(GGP_SERVCONN_HISTORY_PREF, joined);
+ g_free(joined);
+ purple_account_option_string_set_hints(global_data.server_option,
+ ggp_servconn_get_servers());
+}
+
+GSList * ggp_servconn_get_servers(void)
+{
+ return ggp_list_copy_to_slist_deep(global_data.server_history,
+ (GCopyFunc)g_strdup, NULL);
+}
+
+void
+ggp_servconn_remote_disconnect(PurpleConnection *gc)
+{
+ purple_debug_info("gg", "Server remotely closes connection");
+ purple_account_disconnect(purple_connection_get_account(gc));
+}
diff --git a/libpurple/protocols/gg/servconn.h b/libpurple/protocols/gg/servconn.h
new file mode 100644
index 0000000000..87914ad6e1
--- /dev/null
+++ b/libpurple/protocols/gg/servconn.h
@@ -0,0 +1,43 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 _GGP_SERVCONN_H
+#define _GGP_SERVCONN_H
+
+#include <internal.h>
+#include <accountopt.h>
+
+void ggp_servconn_setup(PurpleAccountOption *server_option);
+void ggp_servconn_cleanup(void);
+
+void ggp_servconn_add_server(const gchar *server);
+GSList * ggp_servconn_get_servers(void);
+void ggp_servconn_remote_disconnect(PurpleConnection *gc);
+
+#endif /* _GGP_SERVCONN_H */
diff --git a/libpurple/protocols/gg/status.c b/libpurple/protocols/gg/status.c
new file mode 100644
index 0000000000..e7c04e1353
--- /dev/null
+++ b/libpurple/protocols/gg/status.c
@@ -0,0 +1,471 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 "status.h"
+
+#include <libgadu.h>
+#include <debug.h>
+#include <request.h>
+
+#include "gg.h"
+#include "utils.h"
+
+struct _ggp_status_session_data
+{
+ gboolean status_broadcasting;
+ gchar *current_description;
+};
+
+static inline ggp_status_session_data *
+ggp_status_get_ssdata(PurpleConnection *gc);
+
+static gchar * ggp_status_validate_description(const gchar* msg);
+
+static inline ggp_status_session_data *
+ggp_status_get_ssdata(PurpleConnection *gc)
+{
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+ return accdata->status_data;
+}
+
+void ggp_status_setup(PurpleConnection *gc)
+{
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+ PurpleAccount *account = purple_connection_get_account(gc);
+
+ ggp_status_session_data *ssdata = g_new0(ggp_status_session_data, 1);
+ accdata->status_data = ssdata;
+
+ ssdata->status_broadcasting =
+ purple_account_get_bool(account, "status_broadcasting", TRUE);
+}
+
+void ggp_status_cleanup(PurpleConnection *gc)
+{
+ ggp_status_session_data *ssdata = ggp_status_get_ssdata(gc);
+ g_free(ssdata->current_description);
+ g_free(ssdata);
+}
+
+static gchar * ggp_status_validate_description(const gchar* msg)
+{
+ if (msg == NULL || msg[0] == '\0')
+ return NULL;
+
+ return ggp_utf8_strndup(msg, GG_STATUS_DESCR_MAXSIZE);
+}
+
+GList * ggp_status_types(PurpleAccount *account)
+{
+ GList *types = NULL;
+
+ types = g_list_append(types, purple_status_type_new_with_attrs(
+ PURPLE_STATUS_AVAILABLE, NULL, NULL,
+ TRUE, TRUE, FALSE, "message", _("Message"),
+ purple_value_new(G_TYPE_STRING), NULL));
+
+ types = g_list_append(types, purple_status_type_new_with_attrs(
+ PURPLE_STATUS_AVAILABLE, "freeforchat", _("Chatty"),
+ TRUE, TRUE, FALSE, "message", _("Message"),
+ purple_value_new(G_TYPE_STRING), NULL));
+
+ types = g_list_append(types, purple_status_type_new_with_attrs(
+ PURPLE_STATUS_AWAY, NULL, NULL,
+ TRUE, TRUE, FALSE, "message", _("Message"),
+ purple_value_new(G_TYPE_STRING), NULL));
+
+ types = g_list_append(types, purple_status_type_new_with_attrs(
+ PURPLE_STATUS_UNAVAILABLE, NULL, NULL,
+ TRUE, TRUE, FALSE, "message", _("Message"),
+ purple_value_new(G_TYPE_STRING), NULL));
+
+ types = g_list_append(types, purple_status_type_new_with_attrs(
+ PURPLE_STATUS_INVISIBLE, NULL, NULL,
+ TRUE, TRUE, FALSE, "message", _("Message"),
+ purple_value_new(G_TYPE_STRING), NULL));
+
+ types = g_list_append(types, purple_status_type_new_with_attrs(
+ PURPLE_STATUS_OFFLINE, NULL, NULL,
+ TRUE, TRUE, FALSE, "message", _("Message"),
+ purple_value_new(G_TYPE_STRING), NULL));
+
+ return types;
+}
+
+int ggp_status_from_purplestatus(PurpleStatus *status, gchar **message)
+{
+ const char *status_id = purple_status_get_id(status);
+ const char *status_message =
+ purple_status_get_attr_string(status, "message");
+
+ g_return_val_if_fail(message != NULL, 0);
+
+ *message = NULL;
+ if (status_message) {
+ gchar *stripped = purple_markup_strip_html(status_message);
+ g_strstrip(stripped);
+ *message = ggp_status_validate_description(stripped);
+ g_free(stripped);
+ }
+
+ if (0 == strcmp(status_id, "available"))
+ return status_message ? GG_STATUS_AVAIL_DESCR : GG_STATUS_AVAIL;
+ if (0 == strcmp(status_id, "freeforchat"))
+ return status_message ? GG_STATUS_FFC_DESCR : GG_STATUS_FFC;
+ if (0 == strcmp(status_id, "away"))
+ return status_message ? GG_STATUS_BUSY_DESCR : GG_STATUS_BUSY;
+ if (0 == strcmp(status_id, "unavailable"))
+ return status_message ? GG_STATUS_DND_DESCR : GG_STATUS_DND;
+ if (0 == strcmp(status_id, "invisible"))
+ return status_message ?
+ GG_STATUS_INVISIBLE_DESCR : GG_STATUS_INVISIBLE;
+ if (0 == strcmp(status_id, "offline"))
+ return status_message ?
+ GG_STATUS_NOT_AVAIL_DESCR : GG_STATUS_NOT_AVAIL;
+
+ purple_debug_error("gg", "ggp_status_from_purplestatus: "
+ "unknown status requested (%s)\n", status_id);
+ return status_message ? GG_STATUS_AVAIL_DESCR : GG_STATUS_AVAIL;
+}
+
+const gchar * ggp_status_to_purplestatus(int status)
+{
+ switch (status)
+ {
+ case GG_STATUS_NOT_AVAIL:
+ case GG_STATUS_NOT_AVAIL_DESCR:
+ case GG_STATUS_BLOCKED:
+#if GGP_ENABLE_GG11
+ case GG_STATUS_UNKNOWN:
+#endif
+ return purple_primitive_get_id_from_type(
+ PURPLE_STATUS_OFFLINE);
+ case GG_STATUS_FFC:
+ case GG_STATUS_FFC_DESCR:
+ return "freeforchat";
+ case GG_STATUS_AVAIL:
+ case GG_STATUS_AVAIL_DESCR:
+ return purple_primitive_get_id_from_type(
+ PURPLE_STATUS_AVAILABLE);
+ case GG_STATUS_BUSY:
+ case GG_STATUS_BUSY_DESCR:
+ return purple_primitive_get_id_from_type(
+ PURPLE_STATUS_AWAY);
+ case GG_STATUS_INVISIBLE:
+ case GG_STATUS_INVISIBLE_DESCR:
+ return purple_primitive_get_id_from_type(
+ PURPLE_STATUS_INVISIBLE);
+ case GG_STATUS_DND:
+ case GG_STATUS_DND_DESCR:
+ return purple_primitive_get_id_from_type(
+ PURPLE_STATUS_UNAVAILABLE);
+ default:
+ purple_debug_warning("gg", "ggp_status_to_purplestatus: unknown status %#02x\n", status);
+ return purple_primitive_get_id_from_type(
+ PURPLE_STATUS_AVAILABLE);
+ }
+}
+
+const gchar * ggp_status_get_name(const gchar *purple_status)
+{
+ if (g_strcmp0(purple_status, "freeforchat") == 0)
+ return _("Chatty");
+ return purple_primitive_get_name_from_type(
+ purple_primitive_get_type_from_id(purple_status));
+}
+
+/*******************************************************************************
+ * Own status.
+ ******************************************************************************/
+
+static void ggp_status_broadcasting_dialog_ok(PurpleConnection *gc,
+ PurpleRequestFields *fields);
+
+/******************************************************************************/
+
+void ggp_status_set_initial(PurpleConnection *gc, struct gg_login_params *glp)
+{
+ ggp_status_session_data *ssdata = ggp_status_get_ssdata(gc);
+ PurpleAccount *account = purple_connection_get_account(gc);
+
+ glp->status = ggp_status_from_purplestatus(
+ purple_account_get_active_status(account), &glp->status_descr);
+ if (!ggp_status_get_status_broadcasting(gc))
+ glp->status |= GG_STATUS_FRIENDS_MASK;
+ ssdata->current_description = g_strdup(glp->status_descr);
+}
+
+gboolean ggp_status_set(PurpleAccount *account, int status, const gchar* msg)
+{
+ PurpleConnection *gc = purple_account_get_connection(account);
+ ggp_status_session_data *ssdata = ggp_status_get_ssdata(gc);
+ GGPInfo *accdata = purple_connection_get_protocol_data(gc);
+ gchar *new_description = ggp_status_validate_description(msg);
+
+ if (!ssdata->status_broadcasting)
+ status |= GG_STATUS_FRIENDS_MASK;
+
+ if ((status == GG_STATUS_NOT_AVAIL ||
+ status == GG_STATUS_NOT_AVAIL_DESCR) &&
+ 0 == g_strcmp0(ssdata->current_description, new_description))
+ {
+ purple_debug_info("gg", "ggp_status_set: new status doesn't "
+ "differ when closing connection - ignore\n");
+ g_free(new_description);
+ return FALSE;
+ }
+ g_free(ssdata->current_description);
+ ssdata->current_description = new_description;
+
+ if (msg == NULL)
+ gg_change_status(accdata->session, status);
+ else
+ gg_change_status_descr(accdata->session, status, new_description);
+
+ return TRUE;
+}
+
+void ggp_status_set_purplestatus(PurpleAccount *account, PurpleStatus *status)
+{
+ int status_gg;
+ gchar *msg = NULL;
+
+ if (!purple_status_is_active(status))
+ return;
+
+ status_gg = ggp_status_from_purplestatus(status, &msg);
+ ggp_status_set(account, status_gg, msg);
+ g_free(msg);
+}
+
+void ggp_status_set_disconnected(PurpleAccount *account)
+{
+ gchar *msg = NULL;
+
+ ggp_status_from_purplestatus(purple_account_get_active_status(account),
+ &msg);
+ if (!ggp_status_set(account,
+ msg ? GG_STATUS_NOT_AVAIL_DESCR : GG_STATUS_NOT_AVAIL, msg))
+ {
+ g_free(msg);
+ return;
+ }
+
+ /*
+ struct gg_event *ev;
+ guint64 wait_start = ggp_microtime(), now;
+ int sleep_time = 5000;
+ while ((ev = gg_watch_fd(info->session)) != NULL)
+ {
+ if (ev->type == GG_EVENT_DISCONNECT_ACK)
+ break;
+ now = ggp_microtime();
+ if (now - wait_start + sleep_time >= 100000)
+ break;
+ usleep(sleep_time);
+ sleep_time *= 2;
+ }
+ */
+ g_usleep(100000);
+
+ g_free(msg);
+}
+
+void ggp_status_fake_to_self(PurpleConnection *gc)
+{
+ PurpleAccount *account = purple_connection_get_account(gc);
+ PurpleStatus *status = purple_presence_get_active_status(
+ purple_account_get_presence(account));
+ const char *status_msg = purple_status_get_attr_string(status,
+ "message");
+ gchar *status_msg_gg = NULL;
+
+ if (status_msg != NULL && status_msg[0] != '\0') {
+ status_msg_gg = g_new0(gchar, GG_STATUS_DESCR_MAXSIZE + 1);
+ g_utf8_strncpy(status_msg_gg, status_msg,
+ GG_STATUS_DESCR_MAXSIZE);
+ }
+
+ purple_prpl_got_user_status(account,
+ purple_account_get_username(account),
+ purple_status_get_id(status),
+ status_msg_gg ? "message" : NULL, status_msg_gg, NULL);
+
+ g_free(status_msg_gg);
+}
+
+gboolean ggp_status_get_status_broadcasting(PurpleConnection *gc)
+{
+ return ggp_status_get_ssdata(gc)->status_broadcasting;
+}
+
+void ggp_status_set_status_broadcasting(PurpleConnection *gc,
+ gboolean broadcasting)
+{
+ PurpleAccount *account = purple_connection_get_account(gc);
+
+ ggp_status_get_ssdata(gc)->status_broadcasting = broadcasting;
+ purple_account_set_bool(account, "status_broadcasting", broadcasting);
+ ggp_status_set_purplestatus(account,
+ purple_account_get_active_status(account));
+}
+
+void ggp_status_broadcasting_dialog(PurpleConnection *gc)
+{
+ PurpleRequestFields *fields;
+ PurpleRequestFieldGroup *group;
+ PurpleRequestField *field;
+
+ fields = purple_request_fields_new();
+ group = purple_request_field_group_new(NULL);
+ purple_request_fields_add_group(fields, group);
+
+ field = purple_request_field_bool_new("buddies_only",
+ _("Show status only for buddies"),
+ !ggp_status_get_status_broadcasting(gc));
+ purple_request_field_group_add_field(group, field);
+
+ purple_request_fields(gc,
+ _("Change status broadcasting"),
+ _("Please, select who can see your status"),
+ NULL,
+ fields,
+ _("OK"), G_CALLBACK(ggp_status_broadcasting_dialog_ok),
+ _("Cancel"), NULL,
+ purple_request_cpar_from_connection(gc), gc);
+}
+
+static void ggp_status_broadcasting_dialog_ok(PurpleConnection *gc,
+ PurpleRequestFields *fields)
+{
+ ggp_status_set_status_broadcasting(gc,
+ !purple_request_fields_get_bool(fields, "buddies_only"));
+}
+
+/*******************************************************************************
+ * Buddy status.
+ ******************************************************************************/
+
+void ggp_status_got_others_buddy(PurpleConnection *gc, uin_t uin, int status,
+ const char *descr);
+
+/******************************************************************************/
+
+void ggp_status_got_others(PurpleConnection *gc, struct gg_event *ev)
+{
+ if (ev->type == GG_EVENT_NOTIFY60) {
+ struct gg_event_notify60 *notify = ev->event.notify60;
+ int i;
+ for (i = 0; notify[i].uin; i++)
+ ggp_status_got_others_buddy(gc, notify[i].uin,
+ GG_S(notify[i].status), notify[i].descr);
+ } else if (ev->type == GG_EVENT_STATUS60) {
+ struct gg_event_status60 *notify = &ev->event.status60;
+ ggp_status_got_others_buddy(gc, notify->uin,
+ GG_S(notify->status), notify->descr);
+ } else
+ purple_debug_fatal("gg", "ggp_status_got_others: "
+ "unexpected event %d\n", ev->type);
+}
+
+void ggp_status_got_others_buddy(PurpleConnection *gc, uin_t uin, int status,
+ const char *descr)
+{
+ PurpleAccount *account = purple_connection_get_account(gc);
+ PurpleBuddy *buddy = purple_blist_find_buddy(account, ggp_uin_to_str(uin));
+ const gchar *purple_status = ggp_status_to_purplestatus(status);
+ gchar *status_message = NULL;
+ gboolean is_own;
+
+ is_own = (!g_strcmp0(ggp_uin_to_str(uin),
+ purple_account_get_username(account)));
+
+ if (!buddy) {
+ if (!is_own) {
+ purple_debug_warning("gg",
+ "ggp_status_got_others_buddy: "
+ "buddy %u not found\n", uin);
+ }
+ return;
+ }
+ ggp_buddy_get_data(buddy)->blocked = (status == GG_STATUS_BLOCKED);
+#if GGP_ENABLE_GG11
+ ggp_buddy_get_data(buddy)->not_a_friend = (status == GG_STATUS_UNKNOWN);
+#else
+ ggp_buddy_get_data(buddy)->not_a_friend = FALSE;
+#endif
+
+ if (descr != NULL) {
+ status_message = g_strdup(descr);
+ g_strstrip(status_message);
+ if (status_message[0] == '\0') {
+ g_free(status_message);
+ status_message = NULL;
+ }
+ }
+
+ if (uin == ggp_str_to_uin(purple_account_get_username(account))) {
+ purple_debug_info("gg", "ggp_status_got_others_buddy: "
+ "own status changed to %s [%s]\n",
+ purple_status, status_message ? status_message : "");
+ } else if (purple_debug_is_verbose()) {
+ purple_debug_misc("gg", "ggp_status_got_others_buddy: "
+ "status of %u changed to %s [%s]\n", uin,
+ purple_status, status_message ? status_message : "");
+ }
+
+ if (status_message) {
+ purple_prpl_got_user_status(account, ggp_uin_to_str(uin),
+ purple_status, "message", status_message, NULL);
+ } else {
+ purple_prpl_got_user_status(account, ggp_uin_to_str(uin),
+ purple_status, NULL);
+ }
+
+ g_free(status_message);
+}
+
+char * ggp_status_buddy_text(PurpleBuddy *buddy)
+{
+ ggp_buddy_data *buddy_data = ggp_buddy_get_data(buddy);
+ const gchar *purple_message;
+
+ if (buddy_data->blocked)
+ return g_strdup(_("Blocked"));
+ if (buddy_data->not_a_friend)
+ return g_strdup(_("Not a buddy"));
+
+ purple_message = purple_status_get_attr_string(
+ purple_presence_get_active_status(
+ purple_buddy_get_presence(buddy)), "message");
+ if (!purple_message)
+ return NULL;
+
+ return g_markup_escape_text(purple_message, -1);
+}
diff --git a/libpurple/protocols/gg/status.h b/libpurple/protocols/gg/status.h
new file mode 100644
index 0000000000..68dfa32019
--- /dev/null
+++ b/libpurple/protocols/gg/status.h
@@ -0,0 +1,65 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 _GGP_STATUS_H
+#define _GGP_STATUS_H
+
+#include <internal.h>
+#include <libgadu.h>
+
+typedef struct _ggp_status_session_data ggp_status_session_data;
+
+void ggp_status_setup(PurpleConnection *gc);
+void ggp_status_cleanup(PurpleConnection *gc);
+
+GList * ggp_status_types(PurpleAccount *account);
+int ggp_status_from_purplestatus(PurpleStatus *status, gchar **message);
+const gchar * ggp_status_to_purplestatus(int status);
+const gchar * ggp_status_get_name(const gchar *purple_status);
+
+/* own status */
+
+void ggp_status_set_initial(PurpleConnection *gc, struct gg_login_params *glp);
+
+gboolean ggp_status_set(PurpleAccount *account, int status, const gchar* msg);
+void ggp_status_set_purplestatus(PurpleAccount *account, PurpleStatus *status);
+void ggp_status_set_disconnected(PurpleAccount *account);
+void ggp_status_fake_to_self(PurpleConnection *gc);
+
+gboolean ggp_status_get_status_broadcasting(PurpleConnection *gc);
+void ggp_status_set_status_broadcasting(PurpleConnection *gc,
+ gboolean broadcasting);
+void ggp_status_broadcasting_dialog(PurpleConnection *gc);
+
+/* buddy status */
+
+void ggp_status_got_others(PurpleConnection *gc, struct gg_event *ev);
+char * ggp_status_buddy_text(PurpleBuddy *buddy);
+
+#endif /* _GGP_STATUS_H */
diff --git a/libpurple/protocols/gg/tcpsocket.c b/libpurple/protocols/gg/tcpsocket.c
new file mode 100644
index 0000000000..a24af6f4f1
--- /dev/null
+++ b/libpurple/protocols/gg/tcpsocket.c
@@ -0,0 +1,147 @@
+/* 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.
+ *
+ * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
+ * As a recipient of this file you may choose, which license to receive the
+ * code under. As a contributor, you have to ensure the new code is
+ * compatible with both.
+ *
+ * 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 "tcpsocket.h"
+
+#include "gg.h"
+
+#include "debug.h"
+#include "purple-socket.h"
+
+#if GGP_ENABLE_GG11
+
+static void
+ggp_tcpsocket_connected(PurpleSocket *ps, const gchar *error, gpointer priv_gg)
+{
+ PurpleConnection *gc = purple_socket_get_connection(ps);
+ GGPInfo *info = purple_connection_get_protocol_data(gc);
+ int fd = -1;
+
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
+
+ if (error == NULL)
+ fd = purple_socket_get_fd(ps);
+
+ if (!gg_socket_manager_connected(ps, priv_gg, fd)) {
+ purple_debug_error("gg", "socket not handled");
+ purple_socket_destroy(ps);
+ }
+
+ if (info->inpa > 0)
+ purple_input_remove(info->inpa);
+ if (info->session->fd < 0)
+ return;
+ info->inpa = purple_input_add(info->session->fd,
+ ggp_tcpsocket_inputcond_gg_to_purple(info->session->check),
+ ggp_async_login_handler, gc);
+}
+
+static void*
+ggp_tcpsocket_connect(void *_gc, const char *host, int port, int is_tls,
+ int is_async, void *priv)
+{
+ PurpleConnection *gc = _gc;
+ PurpleSocket *ps;
+
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
+ g_return_val_if_fail(!purple_connection_is_disconnecting(gc), NULL);
+
+ g_return_val_if_fail(host != NULL, NULL);
+ g_return_val_if_fail(is_async, NULL);
+
+ purple_debug_misc("gg", "ggp_tcpsocket_connect(%p, %s:%d, %s, %p)",
+ gc, host, port, is_tls ? "tls" : "tcp", priv);
+
+ ps = purple_socket_new(gc);
+ purple_socket_set_tls(ps, is_tls);
+ purple_socket_set_host(ps, host);
+ purple_socket_set_port(ps, port);
+ if (!purple_socket_connect(ps, ggp_tcpsocket_connected, priv)) {
+ purple_socket_destroy(ps);
+ return NULL;
+ }
+
+ return ps;
+}
+
+static void
+ggp_tcpsocket_close(void *_gc, void *_ps)
+{
+ PurpleSocket *ps = _ps;
+
+ purple_socket_destroy(ps);
+}
+
+static ssize_t
+ggp_tcpsocket_read(void *_gc, void *_ps, unsigned char *buffer, size_t bufsize)
+{
+ PurpleSocket *ps = _ps;
+
+ return purple_socket_read(ps, buffer, bufsize);
+}
+
+static ssize_t
+ggp_tcpsocket_write(void *_gc, void *_ps, const unsigned char *data, size_t len)
+{
+ PurpleSocket *ps = _ps;
+
+ return purple_socket_write(ps, data, len);
+}
+
+void
+ggp_tcpsocket_setup(PurpleConnection *gc, struct gg_login_params *glp)
+{
+ glp->socket_manager_type = purple_ssl_is_supported() ?
+ GG_SOCKET_MANAGER_TYPE_TLS : GG_SOCKET_MANAGER_TYPE_TCP;
+ glp->socket_manager.cb_data = gc;
+ glp->socket_manager.connect_cb = ggp_tcpsocket_connect;
+ glp->socket_manager.close_cb = ggp_tcpsocket_close;
+ glp->socket_manager.read_cb = ggp_tcpsocket_read;
+ glp->socket_manager.write_cb = ggp_tcpsocket_write;
+}
+
+#else
+
+void
+ggp_tcpsocket_setup(PurpleConnection *gc, struct gg_login_params *glp)
+{
+}
+
+#endif
+
+PurpleInputCondition
+ggp_tcpsocket_inputcond_gg_to_purple(enum gg_check_t check)
+{
+ PurpleInputCondition cond = 0;
+
+ if (check & GG_CHECK_READ)
+ cond |= PURPLE_INPUT_READ;
+ if (check & GG_CHECK_WRITE)
+ cond |= PURPLE_INPUT_WRITE;
+
+ return cond;
+}
diff --git a/libpurple/protocols/gg/tcpsocket.h b/libpurple/protocols/gg/tcpsocket.h
new file mode 100644
index 0000000000..b642d2245f
--- /dev/null
+++ b/libpurple/protocols/gg/tcpsocket.h
@@ -0,0 +1,40 @@
+/* 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.
+ *
+ * Component written by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * This file is dual-licensed under the GPL2+ and the X11 (MIT) licences.
+ * As a recipient of this file you may choose, which license to receive the
+ * code under. As a contributor, you have to ensure the new code is
+ * compatible with both.
+ *
+ * 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 _GGP_TCPSOCKET_H
+#define _GGP_TCPSOCKET_H
+
+#include <internal.h>
+#include <libgadu.h>
+
+void
+ggp_tcpsocket_setup(PurpleConnection *gc, struct gg_login_params *glp);
+
+PurpleInputCondition
+ggp_tcpsocket_inputcond_gg_to_purple(enum gg_check_t check);
+
+#endif /* _GGP_TCPSOCKET_H */
diff --git a/libpurple/protocols/gg/utils.c b/libpurple/protocols/gg/utils.c
new file mode 100644
index 0000000000..1b636943af
--- /dev/null
+++ b/libpurple/protocols/gg/utils.c
@@ -0,0 +1,274 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 "utils.h"
+
+#include "gg.h"
+
+#include <debug.h>
+
+uin_t ggp_str_to_uin(const char *str)
+{
+ char *endptr;
+ uin_t uin;
+
+ if (!str || str[0] < '0' || str[0] > '9')
+ return 0;
+
+ errno = 0;
+ uin = strtoul(str, &endptr, 10);
+
+ if (errno == ERANGE || endptr[0] != '\0')
+ return 0;
+
+ return uin;
+}
+
+const char * ggp_uin_to_str(uin_t uin)
+{
+ static char buff[GGP_UIN_LEN_MAX + 1];
+
+ g_snprintf(buff, GGP_UIN_LEN_MAX + 1, "%u", uin);
+
+ return buff;
+}
+
+uin_t ggp_get_my_uin(PurpleConnection *gc)
+{
+ g_return_val_if_fail(gc != NULL, 0);
+
+ return ggp_str_to_uin(purple_account_get_username(
+ purple_connection_get_account(gc)));
+}
+
+static gchar * ggp_convert(const gchar *src, const char *srcenc,
+ const char *dstenc)
+{
+ gchar *dst;
+ GError *err = NULL;
+
+ if (src == NULL)
+ return NULL;
+
+ dst = g_convert_with_fallback(src, strlen(src), dstenc, srcenc, "?",
+ NULL, NULL, &err);
+ if (err != NULL) {
+ purple_debug_error("gg", "error converting from %s to %s: %s\n",
+ srcenc, dstenc, err->message);
+ g_error_free(err);
+ }
+
+ if (dst == NULL)
+ dst = g_strdup(src);
+
+ return dst;
+}
+
+gchar * ggp_convert_to_cp1250(const gchar *src)
+{
+ return ggp_convert(src, "UTF-8", "CP1250");
+}
+
+gchar * ggp_convert_from_cp1250(const gchar *src)
+{
+ return ggp_convert(src, "CP1250", "UTF-8");
+}
+
+gboolean ggp_password_validate(const gchar *password)
+{
+ const int len = strlen(password);
+ if (len < 6 || len > 15)
+ return FALSE;
+ return g_regex_match_simple("^[ a-zA-Z0-9~`!@#$%^&*()_+=[\\]{};':\",./?"
+ "<>\\\\|-]+$", password, 0, 0);
+}
+
+gchar * ggp_utf8_strndup(const gchar *str, gsize n)
+{
+ size_t raw_len;
+ gchar *end_ptr;
+ if (str == NULL)
+ return NULL;
+ raw_len = strlen(str);
+ if (raw_len <= n)
+ return g_strdup(str);
+
+ end_ptr = g_utf8_offset_to_pointer(str, g_utf8_pointer_to_offset(str, &str[n]));
+ raw_len = end_ptr - str;
+
+ if (raw_len > n) {
+ end_ptr = g_utf8_prev_char(end_ptr);
+ raw_len = end_ptr - str;
+ }
+
+ g_assert(raw_len <= n);
+
+ return g_strndup(str, raw_len);
+}
+
+GSList * ggp_list_copy_to_slist_deep(GList *list, GCopyFunc func,
+ gpointer user_data)
+{
+ GSList *new_list = NULL;
+ GList *it;
+
+ it = g_list_first(list);
+ while (it) {
+ new_list = g_slist_append(new_list, func(it->data, user_data));
+ it = g_list_next(it);
+ }
+ return new_list;
+}
+
+GList * ggp_strsplit_list(const gchar *string, const gchar *delimiter,
+ gint max_tokens)
+{
+ gchar **splitted, **it;
+ GList *list = NULL;
+
+ it = splitted = g_strsplit(string, delimiter, max_tokens);
+ while (*it) {
+ list = g_list_append(list, *it);
+ it++;
+ }
+ g_free(splitted);
+
+ return list;
+}
+
+gchar * ggp_strjoin_list(const gchar *separator, GList *list)
+{
+ gchar **str_array;
+ gchar *joined;
+ gint list_len, i;
+ GList *it;
+
+ list_len = g_list_length(list);
+ str_array = g_new(gchar*, list_len + 1);
+
+ it = g_list_first(list);
+ i = 0;
+ while (it) {
+ str_array[i++] = it->data;
+ it = g_list_next(it);
+ }
+ str_array[i] = NULL;
+
+ joined = g_strjoinv(separator, str_array);
+ g_free(str_array);
+
+ return joined;
+}
+
+const gchar * ggp_ipv4_to_str(uint32_t raw_ip)
+{
+ static gchar buff[INET_ADDRSTRLEN];
+ buff[0] = '\0';
+
+ g_snprintf(buff, sizeof(buff), "%d.%d.%d.%d",
+ ((raw_ip >> 0) & 0xFF),
+ ((raw_ip >> 8) & 0xFF),
+ ((raw_ip >> 16) & 0xFF),
+ ((raw_ip >> 24) & 0xFF));
+
+ return buff;
+}
+
+GList * ggp_list_truncate(GList *list, guint length, GDestroyNotify free_func)
+{
+ while (g_list_length(list) > length) {
+ GList *last = g_list_last(list);
+ free_func(last->data);
+ list = g_list_delete_link(list, last);
+ }
+ return list;
+}
+
+gchar * ggp_free_if_equal(gchar *str, const gchar *pattern)
+{
+ if (g_strcmp0(str, pattern) == 0) {
+ g_free(str);
+ return NULL;
+ }
+ return str;
+}
+
+const gchar * ggp_date_strftime(const gchar *format, time_t date)
+{
+ GDate g_date;
+ static gchar buff[30];
+
+ g_date_set_time_t(&g_date, date);
+ if (0 == g_date_strftime(buff, sizeof(buff), format, &g_date))
+ return NULL;
+ return buff;
+}
+
+time_t ggp_date_from_iso8601(const gchar *str)
+{
+ GTimeVal g_timeval;
+
+ if (!str)
+ return 0;
+ if (!g_time_val_from_iso8601(str, &g_timeval))
+ return 0;
+ return g_timeval.tv_sec;
+}
+
+uint64_t * ggp_uint64dup(uint64_t val)
+{
+ uint64_t *ptr = g_new(uint64_t, 1);
+ *ptr = val;
+ return ptr;
+}
+
+gint ggp_int64_compare(gconstpointer _a, gconstpointer _b)
+{
+ const int64_t *ap = _a, *bp = _b;
+ const int64_t a = *ap, b = *bp;
+ if (a == b)
+ return 0;
+ if (a < b)
+ return -1;
+ else
+ return 1;
+}
+
+JsonParser * ggp_json_parse(const gchar *data)
+{
+ JsonParser *parser;
+
+ parser = json_parser_new();
+ if (json_parser_load_from_data(parser, data, -1, NULL))
+ return parser;
+
+ if (purple_debug_is_unsafe())
+ purple_debug_warning("gg", "Invalid JSON: %s\n", data);
+ return NULL;
+}
diff --git a/libpurple/protocols/gg/utils.h b/libpurple/protocols/gg/utils.h
new file mode 100644
index 0000000000..2fd4f86f3e
--- /dev/null
+++ b/libpurple/protocols/gg/utils.h
@@ -0,0 +1,111 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 _GGP_UTILS_H
+#define _GGP_UTILS_H
+
+#include <internal.h>
+#include <libgadu.h>
+#include <json-glib/json-glib.h>
+
+/**
+ * Converts stringified UIN to uin_t.
+ *
+ * @param str The string to convert.
+ *
+ * @return Converted UIN or 0 if an error occurred.
+ */
+uin_t ggp_str_to_uin(const char *str);
+
+/**
+ * Stringifies UIN.
+ *
+ * @param uin UIN to stringify.
+ *
+ * @return Stringified UIN.
+ */
+const char * ggp_uin_to_str(uin_t uin);
+
+/**
+ * Gets UIN for the account.
+ *
+ * @param gc The connection, in which account is connected.
+ * @return UIN for this account.
+ */
+uin_t ggp_get_my_uin(PurpleConnection *gc);
+
+/**
+ * Converts encoding of a given string from UTF-8 to CP1250.
+ *
+ * @param src Input string.
+ *
+ * @return Converted string (must be freed with g_free). If src is NULL,
+ * then NULL is returned.
+ */
+gchar * ggp_convert_to_cp1250(const gchar *src);
+
+/**
+ * Converts encoding of a given string from CP1250 to UTF-8.
+ *
+ * @param src Input string.
+ *
+ * @return Converted string (must be freed with g_free). If src is NULL,
+ * then NULL is returned.
+ */
+gchar * ggp_convert_from_cp1250(const gchar *src);
+
+gboolean ggp_password_validate(const gchar *password);
+
+gchar * ggp_utf8_strndup(const gchar *str, gsize n);
+
+GSList * ggp_list_copy_to_slist_deep(GList *list, GCopyFunc func,
+ gpointer user_data);
+
+GList * ggp_strsplit_list(const gchar *string, const gchar *delimiter,
+ gint max_tokens);
+
+gchar * ggp_strjoin_list(const gchar *separator, GList *list);
+
+const gchar * ggp_ipv4_to_str(uint32_t raw_ip);
+
+GList * ggp_list_truncate(GList *list, guint length, GDestroyNotify free_func);
+
+gchar * ggp_free_if_equal(gchar *str, const gchar *pattern);
+
+const gchar * ggp_date_strftime(const gchar *format, time_t date);
+
+time_t ggp_date_from_iso8601(const gchar *str);
+
+uint64_t * ggp_uint64dup(uint64_t val);
+
+gint ggp_int64_compare(gconstpointer a, gconstpointer b);
+
+JsonParser * ggp_json_parse(const gchar *data);
+
+#endif /* _GGP_UTILS_H */
diff --git a/libpurple/protocols/gg/validator.c b/libpurple/protocols/gg/validator.c
new file mode 100644
index 0000000000..878e9e52b3
--- /dev/null
+++ b/libpurple/protocols/gg/validator.c
@@ -0,0 +1,98 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 "validator.h"
+
+#include "utils.h"
+
+#if 0
+#include "account.h"
+
+gboolean ggp_validator_token(PurpleRequestField *field, gchar **errmsg,
+ void *token)
+{
+ const char *value;
+
+ g_return_val_if_fail(field != NULL, FALSE);
+ g_return_val_if_fail(purple_request_field_get_field_type(field) ==
+ PURPLE_REQUEST_FIELD_STRING, FALSE);
+
+ value = purple_request_field_string_get_value(field);
+
+ if (value != NULL && ggp_account_token_validate(token, value))
+ return TRUE;
+
+ if (errmsg)
+ *errmsg = g_strdup(_("Captcha validation failed"));
+ return FALSE;
+}
+#endif
+
+gboolean ggp_validator_password(PurpleRequestField *field, gchar **errmsg,
+ void *user_data)
+{
+ const char *value;
+
+ g_return_val_if_fail(field != NULL, FALSE);
+ g_return_val_if_fail(purple_request_field_get_field_type(field) ==
+ PURPLE_REQUEST_FIELD_STRING, FALSE);
+
+ value = purple_request_field_string_get_value(field);
+
+ if (value != NULL && ggp_password_validate(value))
+ return TRUE;
+
+ if (errmsg)
+ *errmsg = g_strdup(_("Password can contain 6-15 alphanumeric characters"));
+ return FALSE;
+}
+
+gboolean ggp_validator_password_equal(PurpleRequestField *field, gchar **errmsg,
+ void *field2_p)
+{
+ const char *value1, *value2;
+ PurpleRequestField *field2 = field2_p;
+
+ g_return_val_if_fail(field != NULL, FALSE);
+ g_return_val_if_fail(field2 != NULL, FALSE);
+ g_return_val_if_fail(purple_request_field_get_field_type(field) ==
+ PURPLE_REQUEST_FIELD_STRING, FALSE);
+ g_return_val_if_fail(purple_request_field_get_field_type(field2) ==
+ PURPLE_REQUEST_FIELD_STRING, FALSE);
+
+ value1 = purple_request_field_string_get_value(field);
+ value2 = purple_request_field_string_get_value(field2);
+
+ if (g_strcmp0(value1, value2) == 0)
+ return TRUE;
+
+ if (errmsg)
+ *errmsg = g_strdup(_("Passwords do not match"));
+ return FALSE;
+}
diff --git a/libpurple/protocols/gg/validator.h b/libpurple/protocols/gg/validator.h
new file mode 100644
index 0000000000..7eb1271b3c
--- /dev/null
+++ b/libpurple/protocols/gg/validator.h
@@ -0,0 +1,48 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 _GGP_VALIDATOR_H
+#define _GGP_VALIDATOR_H
+
+#include <internal.h>
+#include <request.h>
+
+#if 0
+/* see account.h */
+gboolean ggp_validator_token(PurpleRequestField *field, gchar **errmsg,
+ void *token);
+#endif
+
+gboolean ggp_validator_password(PurpleRequestField *field, gchar **errmsg,
+ void *user_data);
+
+gboolean ggp_validator_password_equal(PurpleRequestField *field, gchar **errmsg,
+ void *field2);
+
+#endif /* _GGP_VALIDATOR_H */
diff --git a/libpurple/protocols/gg/xml.c b/libpurple/protocols/gg/xml.c
new file mode 100644
index 0000000000..ef10dca776
--- /dev/null
+++ b/libpurple/protocols/gg/xml.c
@@ -0,0 +1,157 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 "xml.h"
+
+#include "debug.h"
+
+gboolean ggp_xml_get_string(const PurpleXmlNode *xml, gchar *childName, gchar **var)
+{
+ char *str;
+
+ g_return_val_if_fail(xml != NULL, FALSE);
+ g_return_val_if_fail(var != NULL, FALSE);
+
+ if (childName != NULL) {
+ xml = purple_xmlnode_get_child(xml, childName);
+ if (xml == NULL)
+ return FALSE;
+ }
+
+ str = purple_xmlnode_get_data(xml);
+ if (str == NULL)
+ return FALSE;
+
+ *var = str;
+ return TRUE;
+}
+
+gboolean ggp_xml_get_bool(const PurpleXmlNode *xml, gchar *childName, gboolean *var)
+{
+ char *str;
+ gboolean succ;
+
+ succ = ggp_xml_get_string(xml, childName, &str);
+ if (!succ)
+ return FALSE;
+
+ *var = (strcmp(str, "true") == 0 ||
+ strcmp(str, "True") == 0 ||
+ strcmp(str, "TRUE") == 0 ||
+ strcmp(str, "1") == 0);
+ g_free(str);
+
+ return TRUE;
+}
+
+gboolean ggp_xml_get_uint(const PurpleXmlNode *xml, gchar *childName, unsigned int *var)
+{
+ char *str, *endptr;
+ gboolean succ;
+ unsigned int val;
+
+ succ = ggp_xml_get_string(xml, childName, &str);
+ if (!succ)
+ return FALSE;
+
+ errno = 0;
+ val = strtoul(str, &endptr, 10);
+
+ succ = (errno != ERANGE && endptr[0] == '\0');
+ g_free(str);
+
+ if (succ)
+ *var = val;
+ return succ;
+}
+
+gboolean ggp_xml_set_string(PurpleXmlNode *xml, gchar *childName, const gchar *val)
+{
+ g_return_val_if_fail(xml != NULL, FALSE);
+ g_return_val_if_fail(val != NULL, FALSE);
+
+ if (childName != NULL) {
+ PurpleXmlNode *child = purple_xmlnode_get_child(xml, childName);
+ if (child == NULL)
+ child = purple_xmlnode_new_child(xml, childName);
+ xml = child;
+ }
+
+ ggp_xmlnode_remove_children(xml);
+ purple_xmlnode_insert_data(xml, val, -1);
+
+ return TRUE;
+}
+
+gboolean ggp_xml_set_bool(PurpleXmlNode *xml, gchar *childName, gboolean val)
+{
+ return ggp_xml_set_string(xml, childName, val ? "true" : "false");
+}
+
+gboolean ggp_xml_set_uint(PurpleXmlNode *xml, gchar *childName, unsigned int val)
+{
+ gchar buff[20];
+ g_snprintf(buff, sizeof(buff), "%u", val);
+ return ggp_xml_set_string(xml, childName, buff);
+}
+
+void ggp_xmlnode_remove_children(PurpleXmlNode *xml)
+{
+ PurpleXmlNode *child = xml->child;
+ while (child) {
+ PurpleXmlNode *next = child->next;
+ if (child->type != PURPLE_XMLNODE_TYPE_ATTRIB)
+ purple_xmlnode_free(child);
+ child = next;
+ }
+}
+
+unsigned int ggp_xml_child_count(PurpleXmlNode *xml, const gchar *childName)
+{
+ PurpleXmlNode *child;
+ unsigned int count = 0;
+
+ g_return_val_if_fail(xml != NULL, 0);
+
+ if (childName) {
+ child = purple_xmlnode_get_child(xml, childName);
+ while (child) {
+ child = purple_xmlnode_get_next_twin(child);
+ count++;
+ }
+ } else {
+ child = xml->child;
+ while (child) {
+ child = child->next;
+ count++;
+ }
+ }
+
+ return count;
+}
diff --git a/libpurple/protocols/gg/xml.h b/libpurple/protocols/gg/xml.h
new file mode 100644
index 0000000000..313b0e3e8c
--- /dev/null
+++ b/libpurple/protocols/gg/xml.h
@@ -0,0 +1,48 @@
+/* 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.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 _GGP_XML_H
+#define _GGP_XML_H
+
+#include <internal.h>
+#include <xmlnode.h>
+
+gboolean ggp_xml_get_string(const PurpleXmlNode *xml, gchar *childName, gchar **var);
+gboolean ggp_xml_get_bool(const PurpleXmlNode *xml, gchar *childName, gboolean *var);
+gboolean ggp_xml_get_uint(const PurpleXmlNode *xml, gchar *childName, unsigned int *var);
+
+gboolean ggp_xml_set_string(PurpleXmlNode *xml, gchar *childName, const gchar *val);
+gboolean ggp_xml_set_bool(PurpleXmlNode *xml, gchar *childName, gboolean val);
+gboolean ggp_xml_set_uint(PurpleXmlNode *xml, gchar *childName, unsigned int val);
+
+void ggp_xmlnode_remove_children(PurpleXmlNode *xml);
+
+unsigned int ggp_xml_child_count(PurpleXmlNode *xml, const gchar *childName);
+
+#endif /* _GGP_XML_H */
diff --git a/libpurple/protocols/irc/Makefile.am b/libpurple/protocols/irc/Makefile.am
index 6c8f8e60a4..7760f61aeb 100644
--- a/libpurple/protocols/irc/Makefile.am
+++ b/libpurple/protocols/irc/Makefile.am
@@ -1,7 +1,7 @@
EXTRA_DIST = \
Makefile.mingw
-pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+pkgdir = @PURPLE_PLUGINDIR@
IRCSOURCES = \
cmds.c \
@@ -13,7 +13,7 @@ IRCSOURCES = \
AM_CFLAGS = $(st)
-libirc_la_LDFLAGS = -module -avoid-version
+libirc_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
if STATIC_IRC
@@ -27,7 +27,7 @@ else
st =
pkg_LTLIBRARIES = libirc.la
libirc_la_SOURCES = $(IRCSOURCES)
-libirc_la_LIBADD = $(GLIB_LIBS) $(SASL_LIBS)
+libirc_la_LIBADD = @PURPLE_LIBS@ $(SASL_LIBS)
endif
diff --git a/libpurple/protocols/irc/Makefile.mingw b/libpurple/protocols/irc/Makefile.mingw
index 0f6b448a4f..d32cfa4fa0 100644
--- a/libpurple/protocols/irc/Makefile.mingw
+++ b/libpurple/protocols/irc/Makefile.mingw
@@ -50,6 +50,7 @@ OBJECTS = $(C_SRC:%.c=%.o)
##
LIBS = \
-lglib-2.0 \
+ -lgobject-2.0 \
-lws2_32 \
-lintl \
-lpurple
diff --git a/libpurple/protocols/irc/cmds.c b/libpurple/protocols/irc/cmds.c
index 70f4cf2022..99caf6b3f8 100644
--- a/libpurple/protocols/irc/cmds.c
+++ b/libpurple/protocols/irc/cmds.c
@@ -34,17 +34,14 @@ static void irc_do_mode(struct irc_conn *irc, const char *target, const char *si
int irc_cmd_default(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
- PurpleConversation *convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, target, irc->account);
+ PurpleConversation *convo = purple_conversations_find_with_account(target, irc->account);
char *buf;
if (!convo)
return 1;
buf = g_strdup_printf(_("Unknown command: %s"), cmd);
- if (purple_conversation_get_type(convo) == PURPLE_CONV_TYPE_IM)
- purple_conv_im_write(PURPLE_CONV_IM(convo), "", buf, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL));
- else
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), "", buf, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(convo, buf, PURPLE_MESSAGE_NO_LOG);
g_free(buf);
return 1;
@@ -100,30 +97,34 @@ int irc_cmd_ctcp_action(struct irc_conn *irc, const char *cmd, const char *targe
const char *src;
char *msg;
PurpleConversation *convo;
+ PurpleMessage *pmsg;
if (!args || !args[0] || !gc)
return 0;
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
- target, irc->account);
+ convo = purple_conversations_find_with_account(target, irc->account);
msg = g_strdup_printf("/me %s", args[0]);
/* XXX: we'd prefer to keep this in conversation.c */
- if (purple_conversation_get_type(convo) == PURPLE_CONV_TYPE_IM) {
+ if (PURPLE_IS_IM_CONVERSATION(convo)) {
+ pmsg = purple_message_new_outgoing(
+ purple_conversation_get_name(convo), msg, 0);
+
purple_signal_emit(purple_conversations_get_handle(),
- "sending-im-msg", irc->account,
- purple_conversation_get_name(convo), &msg);
+ "sending-im-msg", irc->account, pmsg);
} else {
+ pmsg = purple_message_new_outgoing(NULL, msg, 0);
+
purple_signal_emit(purple_conversations_get_handle(),
- "sending-chat-msg", irc->account, &msg,
- purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)));
+ "sending-chat-msg", irc->account, pmsg,
+ purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(convo)));
}
- if (!msg || !msg[0]) {
- g_free(msg);
+ g_free(msg);
+ if (purple_message_is_empty(pmsg))
return 0;
- }
+ msg = g_strdup(purple_message_get_contents(pmsg)); /* XXX: is it really necessary? */
if (strncmp(msg, "/me ", 4) != 0) {
newargs = g_new0(char *, 2);
@@ -166,14 +167,13 @@ int irc_cmd_ctcp_action(struct irc_conn *irc, const char *cmd, const char *targe
}
/* XXX: we'd prefer to keep this in conversation.c */
- if (purple_conversation_get_type(convo) == PURPLE_CONV_TYPE_IM) {
+ if (PURPLE_IS_IM_CONVERSATION(convo)) {
purple_signal_emit(purple_conversations_get_handle(),
- "sent-im-msg", irc->account,
- purple_conversation_get_name(convo), msg);
+ "sent-im-msg", irc->account, pmsg);
} else {
purple_signal_emit(purple_conversations_get_handle(),
- "sent-chat-msg", irc->account, msg,
- purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)));
+ "sent-chat-msg", irc->account, pmsg,
+ purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(convo)));
}
g_free(msg);
@@ -184,13 +184,13 @@ int irc_cmd_ctcp_action(struct irc_conn *irc, const char *cmd, const char *targe
g_free(escaped);
if (action[strlen(action) - 1] == '\n')
action[strlen(action) - 1] = '\0';
- if (purple_conversation_get_type(convo) == PURPLE_CONV_TYPE_CHAT)
- serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
+ if (PURPLE_IS_CHAT_CONVERSATION(convo))
+ purple_serv_got_chat_in(gc, purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(convo)),
purple_connection_get_display_name(gc),
PURPLE_MESSAGE_SEND, action, time(NULL));
else
- purple_conv_im_write(PURPLE_CONV_IM(convo), purple_connection_get_display_name(gc),
- action, PURPLE_MESSAGE_SEND, time(NULL));
+ purple_conversation_write_message(convo, purple_message_new_outgoing(
+ purple_connection_get_display_name(gc), action, 0));
g_free(action);
}
@@ -245,13 +245,11 @@ int irc_cmd_join(struct irc_conn *irc, const char *cmd, const char *target, cons
int irc_cmd_kick(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
char *buf;
- PurpleConversation *convo;
if (!args || !args[0])
return 0;
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, target, irc->account);
- if (!convo)
+ if (!purple_conversations_find_chat_with_account(target, irc->account))
return 0;
if (args[1])
@@ -468,7 +466,7 @@ int irc_cmd_quit(struct irc_conn *irc, const char *cmd, const char *target, cons
irc->quitting = TRUE;
- if (!irc->account->disconnecting)
+ if (!purple_account_is_disconnecting(irc->account))
purple_account_set_status(irc->account, "offline", TRUE, NULL);
}
@@ -491,20 +489,21 @@ int irc_cmd_quote(struct irc_conn *irc, const char *cmd, const char *target, con
int irc_cmd_query(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
- PurpleConversation *convo;
+ PurpleIMConversation *im;
PurpleConnection *gc;
if (!args || !args[0])
return 0;
- convo = purple_conversation_new(PURPLE_CONV_TYPE_IM, irc->account, args[0]);
- purple_conversation_present(convo);
+ im = purple_im_conversation_new(irc->account, args[0]);
+ purple_conversation_present(PURPLE_CONVERSATION(im));
if (args[1]) {
gc = purple_account_get_connection(irc->account);
irc_cmd_privmsg(irc, cmd, target, args);
- purple_conv_im_write(PURPLE_CONV_IM(convo), purple_connection_get_display_name(gc),
- args[1], PURPLE_MESSAGE_SEND, time(NULL));
+ purple_conversation_write_message(PURPLE_CONVERSATION(im),
+ purple_message_new_outgoing(
+ purple_connection_get_display_name(gc), args[1], 0));
}
return 0;
@@ -562,17 +561,17 @@ int irc_cmd_topic(struct irc_conn *irc, const char *cmd, const char *target, con
{
char *buf;
const char *topic;
- PurpleConversation *convo;
+ PurpleChatConversation *chat;
if (!args)
return 0;
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, target, irc->account);
- if (!convo)
+ chat = purple_conversations_find_chat_with_account(target, irc->account);
+ if (!chat)
return 0;
if (!args[0]) {
- topic = purple_conv_chat_get_topic (PURPLE_CONV_CHAT(convo));
+ topic = purple_chat_conversation_get_topic (chat);
if (topic) {
char *tmp, *tmp2;
@@ -583,7 +582,8 @@ int irc_cmd_topic(struct irc_conn *irc, const char *cmd, const char *target, con
g_free(tmp2);
} else
buf = g_strdup(_("No topic is set"));
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), target, buf, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(chat), buf, PURPLE_MESSAGE_NO_LOG);
g_free(buf);
return 0;
diff --git a/libpurple/protocols/irc/dcc_send.c b/libpurple/protocols/irc/dcc_send.c
index a9e7d7a340..184a89a1bc 100644
--- a/libpurple/protocols/irc/dcc_send.c
+++ b/libpurple/protocols/irc/dcc_send.c
@@ -24,7 +24,7 @@
#include "internal.h"
#include "irc.h"
#include "debug.h"
-#include "ft.h"
+#include "xfer.h"
#include "notify.h"
#include "network.h"
@@ -34,11 +34,12 @@
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 = xfer->data;
+ struct irc_xfer_rx_data *xd = purple_xfer_get_protocol_data(xfer);
g_free(xd->ip);
g_free(xd);
@@ -51,10 +52,10 @@ static void irc_dccsend_recv_destroy(PurpleXfer *xfer)
*/
static void irc_dccsend_recv_ack(PurpleXfer *xfer, const guchar *data, size_t size) {
guint32 l;
- size_t result;
+ gssize result;
- l = htonl(xfer->bytes_sent);
- result = write(xfer->fd, &l, sizeof(l));
+ l = htonl(purple_xfer_get_bytes_sent(xfer));
+ result = purple_xfer_write(xfer, (guchar *)&l, sizeof(l));
if (result != sizeof(l)) {
purple_debug_error("irc", "unable to send acknowledgement: %s\n", g_strerror(errno));
/* TODO: We should probably close the connection here or something. */
@@ -62,9 +63,9 @@ static void irc_dccsend_recv_ack(PurpleXfer *xfer, const guchar *data, size_t si
}
static void irc_dccsend_recv_init(PurpleXfer *xfer) {
- struct irc_xfer_rx_data *xd = xfer->data;
+ struct irc_xfer_rx_data *xd = purple_xfer_get_protocol_data(xfer);
- purple_xfer_start(xfer, -1, xd->ip, xfer->remote_port);
+ purple_xfer_start(xfer, -1, xd->ip, xd->remote_port);
g_free(xd->ip);
xd->ip = NULL;
}
@@ -110,14 +111,14 @@ void irc_dccsend_recv(struct irc_conn *irc, const char *from, const char *msg) {
}
i++;
- xfer = purple_xfer_new(irc->account, PURPLE_XFER_RECEIVE, from);
+ xfer = purple_xfer_new(irc->account, PURPLE_XFER_TYPE_RECEIVE, from);
if (xfer)
{
xd = g_new0(struct irc_xfer_rx_data, 1);
- xfer->data = xd;
+ purple_xfer_set_protocol_data(xfer, xd);
purple_xfer_set_filename(xfer, filename->str);
- xfer->remote_port = atoi(token[i+1]);
+ xd->remote_port = atoi(token[i+1]);
nip = strtoul(token[i], NULL, 10);
if (nip) {
@@ -157,7 +158,7 @@ struct irc_xfer_send_data {
static void irc_dccsend_send_destroy(PurpleXfer *xfer)
{
- struct irc_xfer_send_data *xd = xfer->data;
+ struct irc_xfer_send_data *xd = purple_xfer_get_protocol_data(xfer);
if (xd == NULL)
return;
@@ -178,7 +179,7 @@ static void irc_dccsend_send_destroy(PurpleXfer *xfer)
static void irc_dccsend_send_read(gpointer data, int source, PurpleInputCondition cond)
{
PurpleXfer *xfer = data;
- struct irc_xfer_send_data *xd = xfer->data;
+ struct irc_xfer_send_data *xd = purple_xfer_get_protocol_data(xfer);
char buffer[64];
int len;
@@ -198,12 +199,14 @@ static void irc_dccsend_send_read(gpointer data, int source, PurpleInputConditio
xd->rxlen += len;
while (1) {
+ gint32 val;
size_t acked;
if (xd->rxlen < 4)
break;
- acked = ntohl(*((gint32 *)xd->rxqueue));
+ memcpy(&val, xd->rxqueue, sizeof(val));
+ acked = ntohl(val);
xd->rxlen -= 4;
if (xd->rxlen) {
@@ -215,7 +218,7 @@ static void irc_dccsend_send_read(gpointer data, int source, PurpleInputConditio
xd->rxqueue = NULL;
}
- if (acked >= purple_xfer_get_size(xfer)) {
+ if ((goffset)acked >= purple_xfer_get_size(xfer)) {
purple_input_remove(xd->inpa);
xd->inpa = 0;
purple_xfer_set_completed(xfer, TRUE);
@@ -228,13 +231,13 @@ static void irc_dccsend_send_read(gpointer data, int source, PurpleInputConditio
static gssize irc_dccsend_send_write(const guchar *buffer, size_t size, PurpleXfer *xfer)
{
gssize s;
- int ret;
+ gssize ret;
- s = MIN(purple_xfer_get_bytes_remaining(xfer), size);
+ s = MIN((gssize)purple_xfer_get_bytes_remaining(xfer), (gssize)size);
if (!s)
return 0;
- ret = write(xfer->fd, buffer, s);
+ ret = purple_xfer_write(xfer, buffer, s);
if (ret < 0 && errno == EAGAIN)
ret = 0;
@@ -244,7 +247,7 @@ static gssize irc_dccsend_send_write(const guchar *buffer, size_t size, PurpleXf
static void irc_dccsend_send_connected(gpointer data, int source, PurpleInputCondition cond) {
PurpleXfer *xfer = (PurpleXfer *) data;
- struct irc_xfer_send_data *xd = xfer->data;
+ struct irc_xfer_send_data *xd = purple_xfer_get_protocol_data(xfer);
int conn;
conn = accept(xd->fd, NULL, 0);
@@ -257,8 +260,8 @@ static void irc_dccsend_send_connected(gpointer data, int source, PurpleInputCon
return;
}
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
+ purple_input_remove(purple_xfer_get_watcher(xfer));
+ purple_xfer_set_watcher(xfer, 0);
close(xd->fd);
xd->fd = -1;
@@ -281,24 +284,25 @@ irc_dccsend_network_listen_cb(int sock, gpointer data)
struct in_addr addr;
unsigned short int port;
- xd = xfer->data;
+ xd = purple_xfer_get_protocol_data(xfer);
xd->listen_data = NULL;
if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL
|| purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_REMOTE) {
- purple_xfer_unref(xfer);
+ g_object_unref(xfer);
return;
}
- xd = xfer->data;
+ xd = purple_xfer_get_protocol_data(xfer);
gc = purple_account_get_connection(purple_xfer_get_account(xfer));
- irc = gc->proto_data;
+ irc = purple_connection_get_protocol_data(gc);
- purple_xfer_unref(xfer);
+ g_object_unref(xfer);
if (sock < 0) {
purple_notify_error(gc, NULL, _("File Transfer Failed"),
- _("Unable to open a listening port."));
+ _("Unable to open a listening port."),
+ purple_request_cpar_from_connection(gc));
purple_xfer_cancel_local(xfer);
return;
}
@@ -308,17 +312,17 @@ irc_dccsend_network_listen_cb(int sock, gpointer data)
port = purple_network_get_port_from_fd(sock);
purple_debug_misc("irc", "port is %hu\n", port);
/* Monitor the listening socket */
- xfer->watcher = 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] = xfer->who;
+ arg[0] = purple_xfer_get_remote_user(xfer);
inet_aton(purple_network_get_my_ip(irc->fd), &addr);
- arg[1] = tmp = g_strdup_printf("\001DCC SEND \"%s\" %u %hu %" G_GSIZE_FORMAT "\001",
- xfer->filename, ntohl(addr.s_addr),
- port, xfer->size);
+ 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(gc->proto_data, "msg", NULL, arg);
+ irc_cmd_privmsg(purple_connection_get_protocol_data(gc), "msg", NULL, arg);
g_free(tmp);
}
@@ -327,19 +331,20 @@ irc_dccsend_network_listen_cb(int sock, gpointer data)
*/
static void irc_dccsend_send_init(PurpleXfer *xfer) {
PurpleConnection *gc = purple_account_get_connection(purple_xfer_get_account(xfer));
- struct irc_xfer_send_data *xd = xfer->data;
+ struct irc_xfer_send_data *xd = purple_xfer_get_protocol_data(xfer);
- xfer->filename = g_path_get_basename(xfer->local_filename);
+ purple_xfer_set_filename(xfer, g_path_get_basename(purple_xfer_get_local_filename(xfer)));
- purple_xfer_ref(xfer);
+ g_object_ref(xfer);
/* Create a listening socket */
- xd->listen_data = purple_network_listen_range(0, 0, SOCK_STREAM,
+ 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) {
- purple_xfer_unref(xfer);
+ g_object_unref(xfer);
purple_notify_error(gc, NULL, _("File Transfer Failed"),
- _("Unable to open a listening port."));
+ _("Unable to open a listening port."),
+ purple_request_cpar_from_connection(gc));
purple_xfer_cancel_local(xfer);
}
@@ -350,12 +355,12 @@ PurpleXfer *irc_dccsend_new_xfer(PurpleConnection *gc, const char *who) {
struct irc_xfer_send_data *xd;
/* Build the file transfer handle */
- xfer = purple_xfer_new(purple_connection_get_account(gc), PURPLE_XFER_SEND, who);
+ 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;
- xfer->data = xd;
+ purple_xfer_set_protocol_data(xfer, xd);
/* Setup our I/O op functions */
purple_xfer_set_init_fnc(xfer, irc_dccsend_send_init);
diff --git a/libpurple/protocols/irc/irc.c b/libpurple/protocols/irc/irc.c
index a211b1031a..0b9d6aea28 100644
--- a/libpurple/protocols/irc/irc.c
+++ b/libpurple/protocols/irc/irc.c
@@ -26,7 +26,7 @@
#include "internal.h"
#include "accountopt.h"
-#include "blist.h"
+#include "buddylist.h"
#include "conversation.h"
#include "debug.h"
#include "notify.h"
@@ -50,8 +50,8 @@ static void irc_login_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInpu
static void irc_login_cb(gpointer data, gint source, const gchar *error_message);
static void irc_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error, gpointer data);
static void irc_close(PurpleConnection *gc);
-static int irc_im_send(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags);
-static int irc_chat_send(PurpleConnection *gc, int id, const char *what, PurpleMessageFlags flags);
+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_input_cb(gpointer data, gint source, PurpleInputCondition cond);
static void irc_input_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond);
@@ -68,14 +68,16 @@ static void irc_view_motd(PurplePluginAction *action)
struct irc_conn *irc;
char *title;
- if (gc == NULL || gc->proto_data == NULL) {
+ if (gc == NULL || purple_connection_get_protocol_data(gc) == NULL) {
purple_debug(PURPLE_DEBUG_ERROR, "irc", "got MOTD request for NULL gc\n");
return;
}
- irc = gc->proto_data;
+ irc = purple_connection_get_protocol_data(gc);
if (irc->motd == NULL) {
- purple_notify_error(gc, _("Error displaying MOTD"), _("No MOTD available"),
- _("There is no MOTD associated with this connection."));
+ purple_notify_error(gc, _("Error displaying MOTD"),
+ _("No MOTD available"),
+ _("There is no MOTD associated with this connection."),
+ purple_request_cpar_from_connection(gc));
return;
}
title = g_strdup_printf(_("MOTD for %s"), irc->server);
@@ -98,7 +100,7 @@ static int do_send(struct irc_conn *irc, const char *buf, gsize len)
static int irc_send_raw(PurpleConnection *gc, const char *buf, int len)
{
- struct irc_conn *irc = (struct irc_conn*)gc->proto_data;
+ struct irc_conn *irc = purple_connection_get_protocol_data(gc);
if (len == -1) {
len = strlen(buf);
}
@@ -111,8 +113,9 @@ irc_send_cb(gpointer data, gint source, PurpleInputCondition cond)
{
struct irc_conn *irc = data;
int ret, writelen;
+ const gchar *buffer = NULL;
- writelen = purple_circ_buffer_get_max_read(irc->outbuf);
+ writelen = purple_circular_buffer_get_max_read(irc->outbuf);
if (writelen == 0) {
purple_input_remove(irc->writeh);
@@ -120,7 +123,9 @@ irc_send_cb(gpointer data, gint source, PurpleInputCondition cond)
return;
}
- ret = do_send(irc, irc->outbuf->outptr, writelen);
+ buffer = purple_circular_buffer_get_output(irc->outbuf);
+
+ ret = do_send(irc, buffer, writelen);
if (ret < 0 && errno == EAGAIN)
return;
@@ -128,13 +133,13 @@ irc_send_cb(gpointer data, gint source, PurpleInputCondition cond)
PurpleConnection *gc = purple_account_get_connection(irc->account);
gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
g_strerror(errno));
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
g_free(tmp);
return;
}
- purple_circ_buffer_mark_read(irc->outbuf, ret);
+ purple_circular_buffer_mark_read(irc->outbuf, ret);
#if 0
/* We *could* try to write more if we wrote it all */
@@ -173,7 +178,7 @@ int irc_send_len(struct irc_conn *irc, const char *buf, int buflen)
PurpleConnection *gc = purple_account_get_connection(irc->account);
gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
g_strerror(errno));
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
g_free(tmp);
} else if (ret < buflen) {
@@ -183,7 +188,7 @@ int irc_send_len(struct irc_conn *irc, const char *buf, int buflen)
irc->writeh = purple_input_add(
irc->gsc ? irc->gsc->fd : irc->fd,
PURPLE_INPUT_WRITE, irc_send_cb, irc);
- purple_circ_buffer_append(irc->outbuf, tosend + ret,
+ purple_circular_buffer_append(irc->outbuf, tosend + ret,
buflen - ret);
}
g_free(tosend);
@@ -220,7 +225,7 @@ void irc_buddy_query(struct irc_conn *irc)
break;
g_string_append_printf(string, "%s ", ib->name);
ib->new_online_status = FALSE;
- irc->buddies_outstanding = g_list_remove_link(irc->buddies_outstanding, lp);
+ irc->buddies_outstanding = g_list_delete_link(irc->buddies_outstanding, lp);
}
if (string->len) {
@@ -271,7 +276,7 @@ static GList *irc_status_types(PurpleAccount *account)
type = purple_status_type_new_with_attrs(
PURPLE_STATUS_AWAY, NULL, NULL, TRUE, TRUE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_append(types, type);
@@ -332,19 +337,21 @@ static void irc_login(PurpleAccount *account)
const char *username = purple_account_get_username(account);
gc = purple_account_get_connection(account);
- gc->flags |= PURPLE_CONNECTION_NO_NEWLINES;
+ purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_NO_NEWLINES |
+ PURPLE_CONNECTION_FLAG_NO_IMAGES);
if (strpbrk(username, " \t\v\r\n") != NULL) {
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
_("IRC nick and server may not contain whitespace"));
return;
}
- gc->proto_data = irc = g_new0(struct irc_conn, 1);
+ irc = g_new0(struct irc_conn, 1);
+ purple_connection_set_protocol_data(gc, irc);
irc->fd = -1;
irc->account = account;
- irc->outbuf = purple_circ_buffer_new(512);
+ irc->outbuf = purple_circular_buffer_new(512);
userparts = g_strsplit(username, "@", 2);
purple_connection_set_display_name(gc, userparts[0]);
@@ -366,7 +373,7 @@ static void irc_login(PurpleAccount *account)
purple_account_get_int(account, "port", IRC_DEFAULT_SSL_PORT),
irc_login_cb_ssl, irc_ssl_connect_failure, gc);
} else {
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
_("SSL support unavailable"));
return;
@@ -379,7 +386,7 @@ static void irc_login(PurpleAccount *account)
purple_account_get_int(account, "port", IRC_DEFAULT_PORT),
irc_login_cb, gc) == NULL)
{
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to connect"));
return;
@@ -391,7 +398,7 @@ static gboolean do_login(PurpleConnection *gc) {
char *buf, *tmp = NULL;
char *server;
const char *nickname, *identname, *realname;
- struct irc_conn *irc = gc->proto_data;
+ struct irc_conn *irc = purple_connection_get_protocol_data(gc);
const char *pass = purple_connection_get_password(gc);
#ifdef HAVE_CYRUS_SASL
const gboolean use_sasl = purple_account_get_bool(irc->account, "sasl", FALSE);
@@ -469,12 +476,12 @@ static void irc_login_cb_ssl(gpointer data, PurpleSslConnection *gsc,
static void irc_login_cb(gpointer data, gint source, const gchar *error_message)
{
PurpleConnection *gc = data;
- struct irc_conn *irc = gc->proto_data;
+ struct irc_conn *irc = purple_connection_get_protocol_data(gc);
if (source < 0) {
gchar *tmp = g_strdup_printf(_("Unable to connect: %s"),
error_message);
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
g_free(tmp);
return;
@@ -483,7 +490,7 @@ static void irc_login_cb(gpointer data, gint source, const gchar *error_message)
irc->fd = source;
if (do_login(gc)) {
- gc->inpa = purple_input_add(irc->fd, PURPLE_INPUT_READ, irc_input_cb, gc);
+ irc->inpa = purple_input_add(irc->fd, PURPLE_INPUT_READ, irc_input_cb, gc);
}
}
@@ -492,7 +499,7 @@ irc_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error,
gpointer data)
{
PurpleConnection *gc = data;
- struct irc_conn *irc = gc->proto_data;
+ struct irc_conn *irc = purple_connection_get_protocol_data(gc);
irc->gsc = NULL;
@@ -501,7 +508,7 @@ irc_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error,
static void irc_close(PurpleConnection *gc)
{
- struct irc_conn *irc = gc->proto_data;
+ struct irc_conn *irc = purple_connection_get_protocol_data(gc);
if (irc == NULL)
return;
@@ -509,8 +516,10 @@ static void irc_close(PurpleConnection *gc)
if (irc->gsc || (irc->fd >= 0))
irc_cmd_quit(irc, "quit", NULL, NULL);
- if (gc->inpa)
- purple_input_remove(gc->inpa);
+ if (irc->inpa) {
+ purple_input_remove(irc->inpa);
+ irc->inpa = 0;
+ }
g_free(irc->inbuf);
if (irc->gsc) {
@@ -530,7 +539,7 @@ static void irc_close(PurpleConnection *gc)
if (irc->writeh)
purple_input_remove(irc->writeh);
- purple_circ_buffer_destroy(irc->outbuf);
+ g_object_unref(G_OBJECT(irc->outbuf));
g_free(irc->mode_chars);
g_free(irc->reqnick);
@@ -549,15 +558,16 @@ static void irc_close(PurpleConnection *gc)
g_free(irc);
}
-static int irc_im_send(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags)
+static int irc_im_send(PurpleConnection *gc, PurpleMessage *msg)
{
- struct irc_conn *irc = gc->proto_data;
+ struct irc_conn *irc = purple_connection_get_protocol_data(gc);
char *plain;
const char *args[2];
- args[0] = irc_nick_skip_mode(irc, who);
+ args[0] = irc_nick_skip_mode(irc, purple_message_get_recipient(msg));
- purple_markup_html_to_xhtml(what, NULL, &plain);
+ purple_markup_html_to_xhtml(purple_message_get_contents(msg),
+ NULL, &plain);
args[1] = plain;
irc_cmd_privmsg(irc, "msg", NULL, args);
@@ -567,7 +577,7 @@ static int irc_im_send(PurpleConnection *gc, const char *who, const char *what,
static void irc_get_info(PurpleConnection *gc, const char *who)
{
- struct irc_conn *irc = gc->proto_data;
+ struct irc_conn *irc = purple_connection_get_protocol_data(gc);
const char *args[2];
args[0] = who;
args[1] = NULL;
@@ -582,7 +592,7 @@ static void irc_set_status(PurpleAccount *account, PurpleStatus *status)
const char *status_id = purple_status_get_id(status);
g_return_if_fail(gc != NULL);
- irc = gc->proto_data;
+ irc = purple_connection_get_protocol_data(gc);
if (!purple_status_is_active(status))
return;
@@ -599,9 +609,9 @@ static void irc_set_status(PurpleAccount *account, PurpleStatus *status)
}
}
-static void irc_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
+static void irc_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *message)
{
- struct irc_conn *irc = (struct irc_conn *)gc->proto_data;
+ struct irc_conn *irc = purple_connection_get_protocol_data(gc);
struct irc_buddy *ib;
const char *bname = purple_buddy_get_name(buddy);
@@ -626,7 +636,7 @@ static void irc_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup
static void irc_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
{
- struct irc_conn *irc = (struct irc_conn *)gc->proto_data;
+ struct irc_conn *irc = purple_connection_get_protocol_data(gc);
struct irc_buddy *ib;
ib = g_hash_table_lookup(irc->buddies, purple_buddy_get_name(buddy));
@@ -637,9 +647,10 @@ static void irc_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGro
static void read_input(struct irc_conn *irc, int len)
{
+ PurpleConnection *connection = purple_account_get_connection(irc->account);
char *cur, *end;
- irc->account->gc->last_received = time(NULL);
+ purple_connection_update_last_received(connection);
irc->inbufused += len;
irc->inbuf[irc->inbufused] = '\0';
@@ -671,7 +682,7 @@ static void irc_input_cb_ssl(gpointer data, PurpleSslConnection *gsc,
{
PurpleConnection *gc = data;
- struct irc_conn *irc = gc->proto_data;
+ struct irc_conn *irc = purple_connection_get_protocol_data(gc);
int len;
if(!g_list_find(purple_connections_get_all(), gc)) {
@@ -692,12 +703,12 @@ static void irc_input_cb_ssl(gpointer data, PurpleSslConnection *gsc,
} else if (len < 0) {
gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
g_strerror(errno));
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
g_free(tmp);
return;
} else if (len == 0) {
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Server closed the connection"));
return;
@@ -709,7 +720,7 @@ static void irc_input_cb_ssl(gpointer data, PurpleSslConnection *gsc,
static void irc_input_cb(gpointer data, gint source, PurpleInputCondition cond)
{
PurpleConnection *gc = data;
- struct irc_conn *irc = gc->proto_data;
+ struct irc_conn *irc = purple_connection_get_protocol_data(gc);
int len;
if (irc->inbuflen < irc->inbufused + IRC_INITIAL_BUFSIZE) {
@@ -723,12 +734,12 @@ static void irc_input_cb(gpointer data, gint source, PurpleInputCondition cond)
} else if (len < 0) {
gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
g_strerror(errno));
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
g_free(tmp);
return;
} else if (len == 0) {
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Server closed the connection"));
return;
@@ -739,7 +750,7 @@ static void irc_input_cb(gpointer data, gint source, PurpleInputCondition cond)
static void irc_chat_join (PurpleConnection *gc, GHashTable *data)
{
- struct irc_conn *irc = gc->proto_data;
+ struct irc_conn *irc = purple_connection_get_protocol_data(gc);
const char *args[2];
args[0] = g_hash_table_lookup(data, "channel");
@@ -753,8 +764,8 @@ static char *irc_get_chat_name(GHashTable *data) {
static void irc_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name)
{
- struct irc_conn *irc = gc->proto_data;
- PurpleConversation *convo = purple_find_chat(gc, id);
+ struct irc_conn *irc = purple_connection_get_protocol_data(gc);
+ PurpleConversation *convo = PURPLE_CONVERSATION(purple_conversations_find_chat(gc, id));
const char *args[2];
if (!convo) {
@@ -769,8 +780,8 @@ static void irc_chat_invite(PurpleConnection *gc, int id, const char *message, c
static void irc_chat_leave (PurpleConnection *gc, int id)
{
- struct irc_conn *irc = gc->proto_data;
- PurpleConversation *convo = purple_find_chat(gc, id);
+ struct irc_conn *irc = purple_connection_get_protocol_data(gc);
+ PurpleConversation *convo = PURPLE_CONVERSATION(purple_conversations_find_chat(gc, id));
const char *args[2];
if (!convo)
@@ -779,13 +790,13 @@ static void irc_chat_leave (PurpleConnection *gc, int id)
args[0] = purple_conversation_get_name(convo);
args[1] = NULL;
irc_cmd_part(irc, "part", purple_conversation_get_name(convo), args);
- serv_got_chat_left(gc, id);
+ purple_serv_got_chat_left(gc, id);
}
-static int irc_chat_send(PurpleConnection *gc, int id, const char *what, PurpleMessageFlags flags)
+static int irc_chat_send(PurpleConnection *gc, int id, PurpleMessage *msg)
{
- struct irc_conn *irc = gc->proto_data;
- PurpleConversation *convo = purple_find_chat(gc, id);
+ struct irc_conn *irc = purple_connection_get_protocol_data(gc);
+ PurpleConversation *convo = PURPLE_CONVERSATION(purple_conversations_find_chat(gc, id));
const char *args[2];
char *tmp;
@@ -798,13 +809,16 @@ static int irc_chat_send(PurpleConnection *gc, int id, const char *what, PurpleM
return irc_parse_cmd(irc, convo->name, what + 1);
}
#endif
- purple_markup_html_to_xhtml(what, NULL, &tmp);
- args[0] = convo->name;
+ purple_markup_html_to_xhtml(purple_message_get_contents(msg), NULL, &tmp);
+ args[0] = purple_conversation_get_name(convo);
args[1] = tmp;
irc_cmd_privmsg(irc, "msg", NULL, args);
- serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), flags, what, time(NULL));
+ /* TODO: use msg */
+ purple_serv_got_chat_in(gc, id, purple_connection_get_display_name(gc),
+ purple_message_get_flags(msg),
+ purple_message_get_contents(msg), time(NULL));
g_free(tmp);
return 0;
}
@@ -838,8 +852,9 @@ static void irc_chat_set_topic(PurpleConnection *gc, int id, const char *topic)
const char *name = NULL;
struct irc_conn *irc;
- irc = gc->proto_data;
- name = purple_conversation_get_name(purple_find_chat(gc, id));
+ irc = purple_connection_get_protocol_data(gc);
+ name = purple_conversation_get_name(PURPLE_CONVERSATION(
+ purple_conversations_find_chat(gc, id)));
if (name == NULL)
return;
@@ -856,10 +871,10 @@ static PurpleRoomlist *irc_roomlist_get_list(PurpleConnection *gc)
PurpleRoomlistField *f;
char *buf;
- irc = gc->proto_data;
+ irc = purple_connection_get_protocol_data(gc);
if (irc->roomlist)
- purple_roomlist_unref(irc->roomlist);
+ g_object_unref(irc->roomlist);
irc->roomlist = purple_roomlist_new(purple_connection_get_account(gc));
@@ -883,31 +898,41 @@ static PurpleRoomlist *irc_roomlist_get_list(PurpleConnection *gc)
static void irc_roomlist_cancel(PurpleRoomlist *list)
{
- PurpleConnection *gc = purple_account_get_connection(list->account);
+ PurpleAccount *account = purple_roomlist_get_account(list);
+ PurpleConnection *gc = purple_account_get_connection(account);
struct irc_conn *irc;
if (gc == NULL)
return;
- irc = gc->proto_data;
+ irc = purple_connection_get_protocol_data(gc);
purple_roomlist_set_in_progress(list, FALSE);
if (irc->roomlist == list) {
irc->roomlist = NULL;
- purple_roomlist_unref(list);
+ g_object_unref(list);
}
}
static void irc_keepalive(PurpleConnection *gc)
{
- struct irc_conn *irc = gc->proto_data;
+ struct irc_conn *irc = purple_connection_get_protocol_data(gc);
if ((time(NULL) - irc->recv_time) > PING_TIMEOUT)
irc_cmd_ping(irc, NULL, NULL, NULL);
}
+static gssize
+irc_get_max_message_size(PurpleConversation *conv)
+{
+ /* TODO: this static value is got from pidgin-otr, but it depends on
+ * some factors, for example IRC channel name. */
+ return 417;
+}
+
static PurplePluginProtocolInfo prpl_info =
{
+ sizeof(PurplePluginProtocolInfo), /* struct_size */
OPT_PROTO_CHAT_TOPIC | OPT_PROTO_PASSWORD_OPTIONAL |
OPT_PROTO_SLASH_COMMANDS_NATIVE,
NULL, /* user_splits */
@@ -944,12 +969,10 @@ static PurplePluginProtocolInfo prpl_info =
irc_get_chat_name, /* get_chat_name */
irc_chat_invite, /* chat_invite */
irc_chat_leave, /* chat_leave */
- NULL, /* chat_whisper */
irc_chat_send, /* chat_send */
irc_keepalive, /* keepalive */
NULL, /* register_user */
NULL, /* get_cb_info */
- NULL, /* get_cb_away */
NULL, /* alias_buddy */
NULL, /* group_buddy */
NULL, /* rename_group */
@@ -974,27 +997,25 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* unregister_user */
NULL, /* send_attention */
NULL, /* get_attention_types */
- sizeof(PurplePluginProtocolInfo), /* struct_size */
NULL, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* get_media_caps */
NULL, /* get_moods */
NULL, /* set_public_alias */
NULL, /* get_public_alias */
- NULL, /* add_buddy_with_invite */
- NULL /* add_buddies_with_invite */
+ irc_get_max_message_size /* get_max_message_size */
};
static gboolean load_plugin (PurplePlugin *plugin) {
purple_signal_register(plugin, "irc-sending-text",
- purple_marshal_VOID__POINTER_POINTER, NULL, 2,
- purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
- purple_value_new_outgoing(PURPLE_TYPE_STRING));
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ PURPLE_TYPE_CONNECTION,
+ G_TYPE_POINTER); /* pointer to a string */
purple_signal_register(plugin, "irc-receiving-text",
- purple_marshal_VOID__POINTER_POINTER, NULL, 2,
- purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
- purple_value_new_outgoing(PURPLE_TYPE_STRING));
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ PURPLE_TYPE_CONNECTION,
+ G_TYPE_POINTER); /* pointer to a string */
return TRUE;
}
diff --git a/libpurple/protocols/irc/irc.h b/libpurple/protocols/irc/irc.h
index d268ec7628..8e475dceeb 100644
--- a/libpurple/protocols/irc/irc.h
+++ b/libpurple/protocols/irc/irc.h
@@ -29,8 +29,8 @@
#include <sasl/sasl.h>
#endif
-#include "circbuffer.h"
-#include "ft.h"
+#include "circularbuffer.h"
+#include "xfer.h"
#include "roomlist.h"
#include "sslconn.h"
@@ -58,6 +58,7 @@ struct irc_conn {
GHashTable *cmds;
char *server;
int fd;
+ guint inpa;
guint timer;
GHashTable *buddies;
@@ -90,7 +91,7 @@ struct irc_conn {
gboolean quitting;
- PurpleCircBuffer *outbuf;
+ PurpleCircularBuffer *outbuf;
guint writeh;
time_t recv_time;
@@ -189,8 +190,6 @@ void irc_msg_authtryagain(struct irc_conn *irc, const char *name, const char *fr
void irc_msg_authfail(struct irc_conn *irc, const char *name, const char *from, char **args);
#endif
-void irc_msg_ignore(struct irc_conn *irc, const char *name, const char *from, char **args);
-
void irc_cmd_table_build(struct irc_conn *irc);
int irc_cmd_default(struct irc_conn *irc, const char *cmd, const char *target, const char **args);
diff --git a/libpurple/protocols/irc/msgs.c b/libpurple/protocols/irc/msgs.c
index 4a1ad33f6f..03a7f60698 100644
--- a/libpurple/protocols/irc/msgs.c
+++ b/libpurple/protocols/irc/msgs.c
@@ -29,7 +29,7 @@
#include "internal.h"
#include "conversation.h"
-#include "blist.h"
+#include "buddylist.h"
#include "notify.h"
#include "util.h"
#include "debug.h"
@@ -44,7 +44,7 @@
static char *irc_mask_nick(const char *mask);
static char *irc_mask_userhost(const char *mask);
-static void irc_chat_remove_buddy(PurpleConversation *convo, char *data[2]);
+static void irc_chat_remove_buddy(PurpleChatConversation *chat, char *data[2]);
static void irc_buddy_status(char *name, struct irc_buddy *ib, struct irc_conn *irc);
static void irc_connected(struct irc_conn *irc, const char *nick);
@@ -74,7 +74,7 @@ static char *irc_mask_userhost(const char *mask)
return g_strdup(strchr(mask, '!') + 1);
}
-static void irc_chat_remove_buddy(PurpleConversation *convo, char *data[2])
+static void irc_chat_remove_buddy(PurpleChatConversation *chat, char *data[2])
{
char *message, *stripped;
@@ -82,8 +82,8 @@ static void irc_chat_remove_buddy(PurpleConversation *convo, char *data[2])
message = g_strdup_printf("quit: %s", stripped);
g_free(stripped);
- if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(convo), data[0]))
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), data[0], message);
+ if (purple_chat_conversation_has_user(chat, data[0]))
+ purple_chat_conversation_remove_user(chat, data[0], message);
g_free(message);
}
@@ -100,18 +100,18 @@ static void irc_connected(struct irc_conn *irc, const char *nick)
return;
purple_connection_set_display_name(gc, nick);
- purple_connection_set_state(gc, PURPLE_CONNECTED);
+ purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
account = purple_connection_get_account(gc);
/* If we're away then set our away message */
status = purple_account_get_active_status(irc->account);
- if (purple_status_type_get_primitive(purple_status_get_type(status)) != PURPLE_STATUS_AVAILABLE) {
- PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ if (purple_status_type_get_primitive(purple_status_get_status_type(status)) != PURPLE_STATUS_AVAILABLE) {
+ PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
prpl_info->set_status(irc->account, status);
}
/* this used to be in the core, but it's not now */
- for (buddies = purple_find_buddies(account, NULL); buddies;
+ for (buddies = purple_blist_find_buddies(account, NULL); buddies;
buddies = g_slist_delete_link(buddies, buddies))
{
PurpleBuddy *b = buddies->data;
@@ -167,9 +167,7 @@ void irc_msg_default(struct irc_conn *irc, const char *name, const char *from, c
g_free(tmp);
/* Check for an existing conversation */
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
- convname,
- irc->account);
+ convo = purple_conversations_find_with_account(convname, irc->account);
g_free(convname);
if (convo == NULL) {
@@ -190,10 +188,9 @@ void irc_msg_default(struct irc_conn *irc, const char *name, const char *from, c
clean = purple_utf8_salvage(end);
tmp = g_strdup_printf("%.3s: %s", numeric, clean);
g_free(clean);
- purple_conversation_write(convo, "", tmp,
- PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG
- |PURPLE_MESSAGE_RAW|PURPLE_MESSAGE_NO_LINKIFY,
- time(NULL));
+ purple_conversation_write_system_message(convo, tmp,
+ PURPLE_MESSAGE_NO_LOG | PURPLE_MESSAGE_RAW |
+ PURPLE_MESSAGE_NO_LINKIFY);
g_free(tmp);
return;
@@ -247,7 +244,7 @@ void irc_msg_away(struct irc_conn *irc, const char *name, const char *from, char
gc = purple_account_get_connection(irc->account);
if (gc) {
msg = g_markup_escape_text(args[2], -1);
- serv_got_im(gc, args[1], msg, PURPLE_MESSAGE_AUTO_RESP, time(NULL));
+ purple_serv_got_im(gc, args[1], msg, PURPLE_MESSAGE_AUTO_RESP, time(NULL));
g_free(msg);
}
}
@@ -258,15 +255,15 @@ void irc_msg_badmode(struct irc_conn *irc, const char *name, const char *from, c
g_return_if_fail(gc);
- purple_notify_error(gc, NULL, _("Bad mode"), args[1]);
+ purple_notify_error(gc, NULL, _("Bad mode"), args[1],
+ purple_request_cpar_from_connection(gc));
}
void irc_msg_ban(struct irc_conn *irc, const char *name, const char *from, char **args)
{
- PurpleConversation *convo;
+ PurpleChatConversation *chat;
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- args[1], irc->account);
+ chat = purple_conversations_find_chat_with_account(args[1], irc->account);
if (!strcmp(name, "367")) {
char *msg = NULL;
@@ -282,22 +279,19 @@ void irc_msg_ban(struct irc_conn *irc, const char *name, const char *from, char
} else {
msg = g_strdup_printf(_("Ban on %s"), args[2]);
}
- if (convo) {
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), "", msg,
- PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG,
- time(NULL));
+ if (chat) {
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(chat), msg, PURPLE_MESSAGE_NO_LOG);
} else {
purple_debug_info("irc", "%s\n", msg);
}
g_free(msg);
} else if (!strcmp(name, "368")) {
- if (!convo)
+ if (!chat)
return;
/* End of ban list */
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), "",
- _("End of ban list"),
- PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG,
- time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(chat),
+ _("End of ban list"), PURPLE_MESSAGE_NO_LOG);
}
}
@@ -309,40 +303,40 @@ void irc_msg_banned(struct irc_conn *irc, const char *name, const char *from, ch
g_return_if_fail(gc);
buf = g_strdup_printf(_("You are banned from %s."), args[1]);
- purple_notify_error(gc, _("Banned"), _("Banned"), buf);
+ purple_notify_error(gc, _("Banned"), _("Banned"), buf,
+ purple_request_cpar_from_connection(gc));
g_free(buf);
}
void irc_msg_banfull(struct irc_conn *irc, const char *name, const char *from, char **args)
{
- PurpleConversation *convo;
+ PurpleChatConversation *chat;
char *buf, *nick;
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[1], irc->account);
- if (!convo)
+ chat = purple_conversations_find_chat_with_account(args[1], irc->account);
+ if (!chat)
return;
nick = g_markup_escape_text(args[2], -1);
buf = g_strdup_printf(_("Cannot ban %s: banlist is full"), nick);
g_free(nick);
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), "", buf,
- PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG,
- time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(chat),
+ buf, PURPLE_MESSAGE_NO_LOG);
g_free(buf);
}
void irc_msg_chanmode(struct irc_conn *irc, const char *name, const char *from, char **args)
{
- PurpleConversation *convo;
+ PurpleChatConversation *chat;
char *buf, *escaped;
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[1], irc->account);
- if (!convo) /* XXX punt on channels we are not in for now */
+ chat = purple_conversations_find_chat_with_account(args[1], irc->account);
+ if (!chat) /* XXX punt on channels we are not in for now */
return;
escaped = (args[3] != NULL) ? g_markup_escape_text(args[3], -1) : NULL;
buf = g_strdup_printf("mode for %s: %s %s", args[1], args[2], escaped ? escaped : "");
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), "", buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(chat), buf, 0);
g_free(escaped);
g_free(buf);
@@ -417,15 +411,13 @@ void irc_msg_endwhois(struct irc_conn *irc, const char *name, const char *from,
tmp = g_strdup_printf("%s%s%s", tmp2,
(irc->whois.ircop ? _(" <i>(ircop)</i>") : ""),
(irc->whois.identified ? _(" <i>(identified)</i>") : ""));
- purple_notify_user_info_add_pair(user_info, _("Nick"), tmp);
+ purple_notify_user_info_add_pair_html(user_info, _("Nick"), tmp);
g_free(tmp2);
g_free(tmp);
if (irc->whois.away) {
- tmp = g_markup_escape_text(irc->whois.away, strlen(irc->whois.away));
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Away"), irc->whois.away);
g_free(irc->whois.away);
- purple_notify_user_info_add_pair(user_info, _("Away"), tmp);
- g_free(tmp);
}
if (irc->whois.real) {
purple_notify_user_info_add_pair_plaintext(user_info, _("Real name"), irc->whois.real);
@@ -445,24 +437,24 @@ void irc_msg_endwhois(struct irc_conn *irc, const char *name, const char *from,
}
if (irc->whois.server) {
tmp = g_strdup_printf("%s (%s)", irc->whois.server, irc->whois.serverinfo);
- purple_notify_user_info_add_pair(user_info, _("Server"), tmp);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Server"), tmp);
g_free(tmp);
g_free(irc->whois.server);
g_free(irc->whois.serverinfo);
}
if (irc->whois.channels) {
- purple_notify_user_info_add_pair(user_info, _("Currently on"), irc->whois.channels->str);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Currently on"), irc->whois.channels->str);
g_string_free(irc->whois.channels, TRUE);
}
if (irc->whois.idle) {
gchar *timex = purple_str_seconds_to_string(irc->whois.idle);
- purple_notify_user_info_add_pair(user_info, _("Idle for"), timex);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Idle for"), timex);
g_free(timex);
- purple_notify_user_info_add_pair(user_info,
+ purple_notify_user_info_add_pair_plaintext(user_info,
_("Online since"), purple_date_format_full(localtime(&irc->whois.signon)));
}
- if (!strcmp(irc->whois.nick, "Paco-Paco")) {
- purple_notify_user_info_add_pair(user_info,
+ if (!strcmp(irc->whois.nick, "elb")) {
+ purple_notify_user_info_add_pair_plaintext(user_info,
_("<b>Defining adjective:</b>"), _("Glorious"));
}
@@ -478,28 +470,24 @@ void irc_msg_endwhois(struct irc_conn *irc, const char *name, const char *from,
void irc_msg_who(struct irc_conn *irc, const char *name, const char *from, char **args)
{
if (!strcmp(name, "352")) {
- PurpleConversation *conv;
- PurpleConvChat *chat;
- PurpleConvChatBuddy *cb;
+ PurpleChatConversation *chat;
+ PurpleChatUser *cb;
char *cur, *userhost, *realname;
- PurpleConvChatBuddyFlags flags;
- GList *keys = NULL, *values = NULL;
+ PurpleChatUserFlags flags;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[1], irc->account);
- if (!conv) {
+ chat = purple_conversations_find_chat_with_account(args[1], irc->account);
+ if (!chat) {
purple_debug(PURPLE_DEBUG_ERROR, "irc","Got a WHO response for %s, which doesn't exist\n", args[1]);
return;
}
- cb = purple_conv_chat_cb_find(PURPLE_CONV_CHAT(conv), args[5]);
+ cb = purple_chat_conversation_find_user(chat, args[5]);
if (!cb) {
purple_debug(PURPLE_DEBUG_ERROR, "irc", "Got a WHO response for %s who isn't a buddy.\n", args[5]);
return;
}
-
- chat = PURPLE_CONV_CHAT(conv);
userhost = g_strdup_printf("%s@%s", args[2], args[3]);
@@ -513,30 +501,19 @@ void irc_msg_who(struct irc_conn *irc, const char *name, const char *from, char
}
realname = g_strdup(cur);
- keys = g_list_prepend(keys, "userhost");
- values = g_list_prepend(values, userhost);
-
- keys = g_list_prepend(keys, "realname");
- values = g_list_prepend(values, realname);
-
- purple_conv_chat_cb_set_attributes(chat, cb, keys, values);
-
- g_list_free(keys);
- g_list_free(values);
-
- g_free(userhost);
- g_free(realname);
+ g_object_set_data_full(G_OBJECT(cb), "userhost", userhost, (GDestroyNotify)g_free);
+ g_object_set_data_full(G_OBJECT(cb), "realname", realname, (GDestroyNotify)g_free);
- flags = cb->flags;
+ flags = purple_chat_user_get_flags(cb);
/* FIXME: I'm not sure this is really a good idea, now
* that we no longer do periodic WHO. It seems to me
* like it's more likely to be confusing than not.
* Comments? */
- if (args[6][0] == 'G' && !(flags & PURPLE_CBFLAGS_AWAY)) {
- purple_conv_chat_user_set_flags(chat, cb->name, flags | PURPLE_CBFLAGS_AWAY);
- } else if(args[6][0] == 'H' && (flags & PURPLE_CBFLAGS_AWAY)) {
- purple_conv_chat_user_set_flags(chat, cb->name, flags & ~PURPLE_CBFLAGS_AWAY);
+ if (args[6][0] == 'G' && !(flags & PURPLE_CHAT_USER_AWAY)) {
+ purple_chat_user_set_flags(cb, flags | PURPLE_CHAT_USER_AWAY);
+ } else if(args[6][0] == 'H' && (flags & PURPLE_CHAT_USER_AWAY)) {
+ purple_chat_user_set_flags(cb, flags & ~PURPLE_CHAT_USER_AWAY);
}
}
}
@@ -553,7 +530,7 @@ void irc_msg_list(struct irc_conn *irc, const char *name, const char *from, char
if (!strcmp(name, "323")) {
purple_roomlist_set_in_progress(irc->roomlist, FALSE);
- purple_roomlist_unref(irc->roomlist);
+ g_object_unref(irc->roomlist);
irc->roomlist = NULL;
return;
}
@@ -580,7 +557,7 @@ void irc_msg_list(struct irc_conn *irc, const char *name, const char *from, char
void irc_msg_topic(struct irc_conn *irc, const char *name, const char *from, char **args)
{
char *chan, *topic, *msg, *nick, *tmp, *tmp2;
- PurpleConversation *convo;
+ PurpleChatConversation *chat;
if (!strcmp(name, "topic")) {
chan = args[0];
@@ -590,8 +567,8 @@ void irc_msg_topic(struct irc_conn *irc, const char *name, const char *from, cha
topic = irc_mirc2txt (args[2]);
}
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, chan, irc->account);
- if (!convo) {
+ chat = purple_conversations_find_chat_with_account(chan, irc->account);
+ if (!chat) {
purple_debug(PURPLE_DEBUG_ERROR, "irc", "Got a topic for %s, which doesn't exist\n", chan);
g_free(topic);
return;
@@ -602,28 +579,29 @@ void irc_msg_topic(struct irc_conn *irc, const char *name, const char *from, cha
tmp2 = purple_markup_linkify(tmp);
g_free(tmp);
if (!strcmp(name, "topic")) {
- const char *current_topic = purple_conv_chat_get_topic(PURPLE_CONV_CHAT(convo));
+ const char *current_topic = purple_chat_conversation_get_topic(chat);
if (!(current_topic != NULL && strcmp(tmp2, current_topic) == 0))
{
char *nick_esc;
nick = irc_mask_nick(from);
nick_esc = g_markup_escape_text(nick, -1);
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), nick, topic);
+ purple_chat_conversation_set_topic(chat, nick, topic);
if (*tmp2)
msg = g_strdup_printf(_("%s has changed the topic to: %s"), nick_esc, tmp2);
else
msg = g_strdup_printf(_("%s has cleared the topic."), nick_esc);
g_free(nick_esc);
g_free(nick);
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), from, msg, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(chat), msg, 0);
g_free(msg);
}
} else {
char *chan_esc = g_markup_escape_text(chan, -1);
msg = g_strdup_printf(_("The topic for %s is: %s"), chan_esc, tmp2);
g_free(chan_esc);
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, topic);
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), "", msg, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_chat_conversation_set_topic(chat, NULL, topic);
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(chat), msg, 0);
g_free(msg);
}
g_free(tmp2);
@@ -632,13 +610,13 @@ void irc_msg_topic(struct irc_conn *irc, const char *name, const char *from, cha
void irc_msg_topicinfo(struct irc_conn *irc, const char *name, const char *from, char **args)
{
- PurpleConversation *convo;
+ PurpleChatConversation *chat;
struct tm *tm;
time_t t;
char *msg, *timestamp, *datestamp;
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[1], irc->account);
- if (!convo) {
+ chat = purple_conversations_find_chat_with_account(args[1], irc->account);
+ if (!chat) {
purple_debug(PURPLE_DEBUG_ERROR, "irc", "Got topic info for %s, which doesn't exist\n", args[1]);
return;
}
@@ -653,7 +631,8 @@ void irc_msg_topicinfo(struct irc_conn *irc, const char *name, const char *from,
timestamp = g_strdup(purple_time_format(tm));
datestamp = g_strdup(purple_date_format_short(tm));
msg = g_strdup_printf(_("Topic for %s set by %s at %s on %s"), args[1], args[2], timestamp, datestamp);
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), "", msg, PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY, time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(chat),
+ msg, PURPLE_MESSAGE_NO_LINKIFY);
g_free(timestamp);
g_free(datestamp);
g_free(msg);
@@ -667,7 +646,9 @@ void irc_msg_unknown(struct irc_conn *irc, const char *name, const char *from, c
g_return_if_fail(gc);
buf = g_strdup_printf(_("Unknown message '%s'"), args[1]);
- purple_notify_error(gc, _("Unknown message"), buf, _("The IRC server received a message it did not understand."));
+ purple_notify_error(gc, _("Unknown message"), buf, _("The IRC server "
+ "received a message it did not understand."),
+ purple_request_cpar_from_connection(gc));
g_free(buf);
}
@@ -677,7 +658,7 @@ void irc_msg_names(struct irc_conn *irc, const char *name, const char *from, cha
PurpleConversation *convo;
if (!strcmp(name, "366")) {
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, args[1], irc->account);
+ convo = purple_conversations_find_with_account(args[1], irc->account);
if (!convo) {
purple_debug(PURPLE_DEBUG_ERROR, "irc", "Got a NAMES list for %s, which doesn't exist\n", args[1]);
g_string_free(irc->names, TRUE);
@@ -687,35 +668,32 @@ void irc_msg_names(struct irc_conn *irc, const char *name, const char *from, cha
names = cur = g_string_free(irc->names, FALSE);
irc->names = NULL;
- if (purple_conversation_get_data(convo, IRC_NAMES_FLAG)) {
+ if (g_object_get_data(G_OBJECT(convo), IRC_NAMES_FLAG)) {
msg = g_strdup_printf(_("Users on %s: %s"), args[1], names ? names : "");
- if (purple_conversation_get_type(convo) == PURPLE_CONV_TYPE_CHAT)
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), "", msg, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL));
- else
- purple_conv_im_write(PURPLE_CONV_IM(convo), "", msg, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(convo, msg, PURPLE_MESSAGE_NO_LOG);
g_free(msg);
} else if (cur != NULL) {
GList *users = NULL;
GList *flags = NULL;
while (*cur) {
- PurpleConvChatBuddyFlags f = PURPLE_CBFLAGS_NONE;
+ PurpleChatUserFlags f = PURPLE_CHAT_USER_NONE;
end = strchr(cur, ' ');
if (!end)
end = cur + strlen(cur);
if (*cur == '@') {
- f = PURPLE_CBFLAGS_OP;
+ f = PURPLE_CHAT_USER_OP;
cur++;
} else if (*cur == '%') {
- f = PURPLE_CBFLAGS_HALFOP;
+ f = PURPLE_CHAT_USER_HALFOP;
cur++;
} else if(*cur == '+') {
- f = PURPLE_CBFLAGS_VOICE;
+ f = PURPLE_CHAT_USER_VOICE;
cur++;
} else if(irc->mode_chars
&& strchr(irc->mode_chars, *cur)) {
if (*cur == '~')
- f = PURPLE_CBFLAGS_FOUNDER;
+ f = PURPLE_CHAT_USER_FOUNDER;
cur++;
}
tmp = g_strndup(cur, end - cur);
@@ -729,7 +707,7 @@ void irc_msg_names(struct irc_conn *irc, const char *name, const char *from, cha
if (users != NULL) {
GList *l;
- purple_conv_chat_add_users(PURPLE_CONV_CHAT(convo), users, NULL, flags, FALSE);
+ purple_chat_conversation_add_users(PURPLE_CHAT_CONVERSATION(convo), users, NULL, flags, FALSE);
for (l = users; l != NULL; l = l->next)
g_free(l->data);
@@ -738,7 +716,7 @@ void irc_msg_names(struct irc_conn *irc, const char *name, const char *from, cha
g_list_free(flags);
}
- purple_conversation_set_data(convo, IRC_NAMES_FLAG,
+ g_object_set_data(G_OBJECT(convo), IRC_NAMES_FLAG,
GINT_TO_POINTER(TRUE));
}
g_free(names);
@@ -792,13 +770,15 @@ void irc_msg_motd(struct irc_conn *irc, const char *name, const char *from, char
void irc_msg_time(struct irc_conn *irc, const char *name, const char *from, char **args)
{
- PurpleConnection *gc = purple_account_get_connection(irc->account);
+ PurpleConnection *gc;
+
+ gc = purple_account_get_connection(irc->account);
g_return_if_fail(gc);
purple_notify_message(gc, PURPLE_NOTIFY_MSG_INFO, _("Time Response"),
- _("The IRC server's local time is:"),
- args[2], NULL, NULL);
+ _("The IRC server's local time is:"), args[2], NULL, NULL,
+ purple_request_cpar_from_connection(gc));
}
void irc_msg_nochan(struct irc_conn *irc, const char *name, const char *from, char **args)
@@ -807,7 +787,8 @@ void irc_msg_nochan(struct irc_conn *irc, const char *name, const char *from, ch
g_return_if_fail(gc);
- purple_notify_error(gc, NULL, _("No such channel"), args[1]);
+ purple_notify_error(gc, NULL, _("No such channel"), args[1],
+ purple_request_cpar_from_connection(gc));
}
void irc_msg_nonick(struct irc_conn *irc, const char *name, const char *from, char **args)
@@ -815,18 +796,17 @@ void irc_msg_nonick(struct irc_conn *irc, const char *name, const char *from, ch
PurpleConnection *gc;
PurpleConversation *convo;
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, args[1], irc->account);
+ convo = purple_conversations_find_with_account(args[1], irc->account);
if (convo) {
- if (purple_conversation_get_type(convo) == PURPLE_CONV_TYPE_CHAT) /* does this happen? */
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), args[1], _("no such channel"),
- PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL));
- else
- purple_conv_im_write(PURPLE_CONV_IM(convo), args[1], _("User is not logged in"),
- PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(convo,
+ PURPLE_IS_IM_CONVERSATION(convo) ? _("User is not logged in") : _("no such channel"),
+ PURPLE_MESSAGE_NO_LOG);
+
} else {
if ((gc = purple_account_get_connection(irc->account)) == NULL)
return;
- purple_notify_error(gc, NULL, _("No such nick or channel"), args[1]);
+ purple_notify_error(gc, NULL, _("No such nick or channel"),
+ args[1], purple_request_cpar_from_connection(gc));
}
if (irc->whois.nick && !purple_utf8_strcasecmp(irc->whois.nick, args[1])) {
@@ -838,39 +818,42 @@ void irc_msg_nonick(struct irc_conn *irc, const char *name, const char *from, ch
void irc_msg_nosend(struct irc_conn *irc, const char *name, const char *from, char **args)
{
PurpleConnection *gc;
- PurpleConversation *convo;
+ PurpleChatConversation *chat;
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[1], irc->account);
- if (convo) {
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), args[1], args[2], PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL));
+ chat = purple_conversations_find_chat_with_account(args[1], irc->account);
+ if (chat) {
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(chat), args[2],
+ PURPLE_MESSAGE_NO_LOG);
} else {
if ((gc = purple_account_get_connection(irc->account)) == NULL)
return;
- purple_notify_error(gc, NULL, _("Could not send"), args[2]);
+ purple_notify_error(gc, NULL, _("Could not send"), args[2],
+ purple_request_cpar_from_connection(gc));
}
}
void irc_msg_notinchan(struct irc_conn *irc, const char *name, const char *from, char **args)
{
- PurpleConversation *convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[1], irc->account);
+ PurpleChatConversation *chat = purple_conversations_find_chat_with_account(args[1], irc->account);
purple_debug(PURPLE_DEBUG_INFO, "irc", "We're apparently not in %s, but tried to use it\n", args[1]);
- if (convo) {
- /*g_slist_remove(irc->gc->buddy_chats, convo);
- purple_conversation_set_account(convo, NULL);*/
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), args[1], args[2], PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL));
+ if (chat) {
+ /*g_slist_remove(irc->gc->buddy_chats, chat);
+ purple_conversation_set_account(chat, NULL);*/
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(chat),
+ args[2], PURPLE_MESSAGE_NO_LOG);
}
}
void irc_msg_notop(struct irc_conn *irc, const char *name, const char *from, char **args)
{
- PurpleConversation *convo;
+ PurpleChatConversation *chat;
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[1], irc->account);
- if (!convo)
+ chat = purple_conversations_find_chat_with_account(args[1], irc->account);
+ if (!chat)
return;
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), "", args[2], PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(chat), args[2], 0);
}
void irc_msg_invite(struct irc_conn *irc, const char *name, const char *from, char **args)
@@ -886,7 +869,7 @@ void irc_msg_invite(struct irc_conn *irc, const char *name, const char *from, ch
g_hash_table_insert(components, g_strdup("channel"), g_strdup(args[1]));
- serv_got_chat_invite(gc, args[1], nick, NULL, components);
+ purple_serv_got_chat_invite(gc, args[1], nick, NULL, components);
g_free(nick);
}
@@ -898,7 +881,8 @@ void irc_msg_inviteonly(struct irc_conn *irc, const char *name, const char *from
g_return_if_fail(gc);
buf = g_strdup_printf(_("Joining %s requires an invitation."), args[1]);
- purple_notify_error(gc, _("Invitation only"), _("Invitation only"), buf);
+ purple_notify_error(gc, _("Invitation only"), _("Invitation only"), buf,
+ purple_request_cpar_from_connection(gc));
g_free(buf);
}
@@ -927,7 +911,7 @@ void irc_msg_ison(struct irc_conn *irc, const char *name, const char *from, char
static void irc_buddy_status(char *name, struct irc_buddy *ib, struct irc_conn *irc)
{
PurpleConnection *gc = purple_account_get_connection(irc->account);
- PurpleBuddy *buddy = purple_find_buddy(irc->account, name);
+ PurpleBuddy *buddy = purple_blist_find_buddy(irc->account, name);
if (!gc || !buddy)
return;
@@ -944,9 +928,8 @@ static void irc_buddy_status(char *name, struct irc_buddy *ib, struct irc_conn *
void irc_msg_join(struct irc_conn *irc, const char *name, const char *from, char **args)
{
PurpleConnection *gc = purple_account_get_connection(irc->account);
- PurpleConversation *convo;
- PurpleConvChat *chat;
- PurpleConvChatBuddy *cb;
+ PurpleChatConversation *chat;
+ PurpleChatUser *cb;
char *nick, *userhost, *buf;
struct irc_buddy *ib;
@@ -958,17 +941,15 @@ void irc_msg_join(struct irc_conn *irc, const char *name, const char *from, char
if (!purple_utf8_strcasecmp(nick, purple_connection_get_display_name(gc))) {
/* We are joining a channel for the first time */
- serv_got_joined_chat(gc, id++, args[0]);
+ purple_serv_got_joined_chat(gc, id++, args[0]);
g_free(nick);
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- args[0],
- irc->account);
+ chat = purple_conversations_find_chat_with_account(args[0], irc->account);
- if (convo == NULL) {
+ if (chat == NULL) {
purple_debug_error("irc", "tried to join %s but couldn't\n", args[0]);
return;
}
- purple_conversation_set_data(convo, IRC_NAMES_FLAG,
+ g_object_set_data(G_OBJECT(chat), IRC_NAMES_FLAG,
GINT_TO_POINTER(FALSE));
// Get the real name and user host for all participants.
@@ -979,26 +960,25 @@ void irc_msg_join(struct irc_conn *irc, const char *name, const char *from, char
/* Until purple_conversation_present does something that
* one would expect in Pidgin, this call produces buggy
* behavior both for the /join and auto-join cases. */
- /* purple_conversation_present(convo); */
+ /* purple_conversation_present(chat); */
return;
}
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[0], irc->account);
- if (convo == NULL) {
+ chat = purple_conversations_find_chat_with_account(args[0], irc->account);
+ if (chat == NULL) {
purple_debug(PURPLE_DEBUG_ERROR, "irc", "JOIN for %s failed\n", args[0]);
g_free(nick);
return;
}
userhost = irc_mask_userhost(from);
- chat = PURPLE_CONV_CHAT(convo);
- purple_conv_chat_add_user(chat, nick, userhost, PURPLE_CBFLAGS_NONE, TRUE);
+ purple_chat_conversation_add_user(chat, nick, userhost, PURPLE_CHAT_USER_NONE, TRUE);
- cb = purple_conv_chat_cb_find(chat, nick);
+ cb = purple_chat_conversation_find_user(chat, nick);
if (cb) {
- purple_conv_chat_cb_set_attribute(chat, cb, "userhost", userhost);
+ g_object_set_data_full(G_OBJECT(cb), "userhost", userhost, (GDestroyNotify)g_free);
}
if ((ib = g_hash_table_lookup(irc->buddies, nick)) != NULL) {
@@ -1006,21 +986,20 @@ void irc_msg_join(struct irc_conn *irc, const char *name, const char *from, char
irc_buddy_status(nick, ib, irc);
}
- g_free(userhost);
g_free(nick);
}
void irc_msg_kick(struct irc_conn *irc, const char *name, const char *from, char **args)
{
PurpleConnection *gc = purple_account_get_connection(irc->account);
- PurpleConversation *convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[0], irc->account);
+ PurpleChatConversation *chat = purple_conversations_find_chat_with_account(args[0], irc->account);
char *nick, *buf;
g_return_if_fail(gc);
nick = irc_mask_nick(from);
- if (!convo) {
+ if (!chat) {
purple_debug(PURPLE_DEBUG_ERROR, "irc", "Received a KICK for unknown channel %s\n", args[0]);
g_free(nick);
return;
@@ -1028,12 +1007,12 @@ void irc_msg_kick(struct irc_conn *irc, const char *name, const char *from, char
if (!purple_utf8_strcasecmp(purple_connection_get_display_name(gc), args[1])) {
buf = g_strdup_printf(_("You have been kicked by %s: (%s)"), nick, args[2]);
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), args[0], buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(chat), buf, 0);
g_free(buf);
- serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)));
+ purple_serv_got_chat_left(gc, purple_chat_conversation_get_id(chat));
} else {
buf = g_strdup_printf(_("Kicked by %s (%s)"), nick, args[2]);
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), args[1], buf);
+ purple_chat_conversation_remove_user(chat, args[1], buf);
g_free(buf);
}
@@ -1043,24 +1022,25 @@ void irc_msg_kick(struct irc_conn *irc, const char *name, const char *from, char
void irc_msg_mode(struct irc_conn *irc, const char *name, const char *from, char **args)
{
- PurpleConversation *convo;
+ PurpleChatConversation *chat;
char *nick = irc_mask_nick(from), *buf;
if (*args[0] == '#' || *args[0] == '&') { /* Channel */
char *escaped;
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[0], irc->account);
- if (!convo) {
+ chat = purple_conversations_find_chat_with_account(args[0], irc->account);
+ if (!chat) {
purple_debug(PURPLE_DEBUG_ERROR, "irc", "MODE received for %s, which we are not in\n", args[0]);
g_free(nick);
return;
}
escaped = (args[2] != NULL) ? g_markup_escape_text(args[2], -1) : NULL;
buf = g_strdup_printf(_("mode (%s %s) by %s"), args[1], escaped ? escaped : "", nick);
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), args[0], buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(chat), buf, 0);
g_free(escaped);
g_free(buf);
if(args[2]) {
- PurpleConvChatBuddyFlags newflag, flags;
+ PurpleChatUser *cb;
+ PurpleChatUserFlags newflag, flags;
char *mcur, *cur, *end, *user;
gboolean add = FALSE;
mcur = args[1];
@@ -1075,23 +1055,24 @@ void irc_msg_mode(struct irc_conn *irc, const char *name, const char *from, char
if (!end)
end = cur + strlen(cur);
user = g_strndup(cur, end - cur);
- flags = purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(convo), user);
- newflag = PURPLE_CBFLAGS_NONE;
+ cb = purple_chat_conversation_find_user(chat, user);
+ flags = purple_chat_user_get_flags(cb);
+ newflag = PURPLE_CHAT_USER_NONE;
if (*mcur == 'o')
- newflag = PURPLE_CBFLAGS_OP;
+ newflag = PURPLE_CHAT_USER_OP;
else if (*mcur =='h')
- newflag = PURPLE_CBFLAGS_HALFOP;
+ newflag = PURPLE_CHAT_USER_HALFOP;
else if (*mcur == 'v')
- newflag = PURPLE_CBFLAGS_VOICE;
+ newflag = PURPLE_CHAT_USER_VOICE;
else if(irc->mode_chars
&& strchr(irc->mode_chars, '~') && (*mcur == 'q'))
- newflag = PURPLE_CBFLAGS_FOUNDER;
+ newflag = PURPLE_CHAT_USER_FOUNDER;
if (newflag) {
if (add)
flags |= newflag;
else
flags &= ~newflag;
- purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(convo), user, flags);
+ purple_chat_user_set_flags(cb, flags);
}
g_free(user);
cur = end;
@@ -1109,7 +1090,7 @@ void irc_msg_mode(struct irc_conn *irc, const char *name, const char *from, char
void irc_msg_nick(struct irc_conn *irc, const char *name, const char *from, char **args)
{
PurpleConnection *gc = purple_account_get_connection(irc->account);
- PurpleConversation *conv;
+ PurpleIMConversation *im;
GSList *chats;
char *nick = irc_mask_nick(from);
@@ -1119,24 +1100,24 @@ void irc_msg_nick(struct irc_conn *irc, const char *name, const char *from, char
g_free(nick);
return;
}
- chats = gc->buddy_chats;
+ chats = purple_connection_get_active_chats(gc);
if (!purple_utf8_strcasecmp(nick, purple_connection_get_display_name(gc))) {
purple_connection_set_display_name(gc, args[0]);
}
while (chats) {
- PurpleConvChat *chat = PURPLE_CONV_CHAT(chats->data);
+ PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(chats->data);
/* This is ugly ... */
- if (purple_conv_chat_find_user(chat, nick))
- purple_conv_chat_rename_user(chat, nick, args[0]);
+ if (purple_chat_conversation_has_user(chat, nick))
+ purple_chat_conversation_rename_user(chat, nick, args[0]);
chats = chats->next;
}
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, nick,
+ im = purple_conversations_find_im_with_account(nick,
irc->account);
- if (conv != NULL)
- purple_conversation_set_name(conv, args[0]);
+ if (im != NULL)
+ purple_conversation_set_name(PURPLE_CONVERSATION(im), args[0]);
g_free(nick);
}
@@ -1144,13 +1125,14 @@ void irc_msg_nick(struct irc_conn *irc, const char *name, const char *from, char
void irc_msg_badnick(struct irc_conn *irc, const char *name, const char *from, char **args)
{
PurpleConnection *gc = purple_account_get_connection(irc->account);
- if (purple_connection_get_state(gc) == PURPLE_CONNECTED) {
- purple_notify_error(gc, _("Invalid nickname"),
- _("Invalid nickname"),
- _("Your selected nickname was rejected by the server. It probably contains invalid characters."));
+ if (purple_connection_get_state(gc) == PURPLE_CONNECTION_CONNECTED) {
+ purple_notify_error(gc, _("Invalid nickname"), _("Invalid "
+ "nickname"), _("Your selected nickname was rejected by "
+ "the server. It probably contains invalid characters."),
+ purple_request_cpar_from_connection(gc));
} else {
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
_("Your selected account name was rejected by the server. It probably contains invalid characters."));
}
@@ -1161,14 +1143,14 @@ void irc_msg_nickused(struct irc_conn *irc, const char *name, const char *from,
char *newnick, *buf, *end;
PurpleConnection *gc = purple_account_get_connection(irc->account);
- if (gc && purple_connection_get_state(gc) == PURPLE_CONNECTED) {
+ if (gc && purple_connection_get_state(gc) == PURPLE_CONNECTION_CONNECTED) {
/* We only want to do the following dance if the connection
has not been successfully completed. If it has, just
notify the user that their /nick command didn't go. */
buf = g_strdup_printf(_("The nickname \"%s\" is already being used."),
irc->reqnick);
- purple_notify_error(gc, _("Nickname in use"),
- _("Nickname in use"), buf);
+ purple_notify_error(gc, _("Nickname in use"), _("Nickname in "
+ "use"), buf, purple_request_cpar_from_connection(gc));
g_free(buf);
g_free(irc->reqnick);
irc->reqnick = NULL;
@@ -1208,13 +1190,15 @@ void irc_msg_nochangenick(struct irc_conn *irc, const char *name, const char *fr
g_return_if_fail(gc);
- purple_notify_error(gc, _("Cannot change nick"), _("Could not change nick"), args[2]);
+ purple_notify_error(gc, _("Cannot change nick"),
+ _("Could not change nick"), args[2],
+ purple_request_cpar_from_connection(gc));
}
void irc_msg_part(struct irc_conn *irc, const char *name, const char *from, char **args)
{
PurpleConnection *gc = purple_account_get_connection(irc->account);
- PurpleConversation *convo;
+ PurpleChatConversation *chat;
char *nick, *msg, *channel;
g_return_if_fail(gc);
@@ -1223,8 +1207,8 @@ void irc_msg_part(struct irc_conn *irc, const char *name, const char *from, char
* that I can see. This catches that. */
channel = (args[0][0] == ':') ? &args[0][1] : args[0];
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, channel, irc->account);
- if (!convo) {
+ chat = purple_conversations_find_chat_with_account(channel, irc->account);
+ if (!chat) {
purple_debug(PURPLE_DEBUG_INFO, "irc", "Got a PART on %s, which doesn't exist -- probably closed\n", channel);
return;
}
@@ -1236,12 +1220,12 @@ void irc_msg_part(struct irc_conn *irc, const char *name, const char *from, char
(args[1] && *args[1]) ? ": " : "",
(escaped && *escaped) ? escaped : "");
g_free(escaped);
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel, msg, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(chat), msg, 0);
g_free(msg);
- serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)));
+ purple_serv_got_chat_left(gc, purple_chat_conversation_get_id(chat));
} else {
msg = args[1] ? irc_mirc2txt(args[1]) : NULL;
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), nick, msg);
+ purple_chat_conversation_remove_user(chat, nick, msg);
g_free(msg);
}
g_free(nick);
@@ -1276,20 +1260,18 @@ void irc_msg_pong(struct irc_conn *irc, const char *name, const char *from, char
msg = g_strdup_printf(_("PING reply -- Lag: %lu seconds"), time(NULL) - oldstamp);
}
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, parts[0], irc->account);
+ convo = purple_conversations_find_with_account(parts[0], irc->account);
g_strfreev(parts);
if (convo) {
- if (purple_conversation_get_type (convo) == PURPLE_CONV_TYPE_CHAT)
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), "PONG", msg, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL));
- else
- purple_conv_im_write(PURPLE_CONV_IM(convo), "PONG", msg, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(convo, msg, PURPLE_MESSAGE_NO_LOG);
} else {
gc = purple_account_get_connection(irc->account);
if (!gc) {
g_free(msg);
return;
}
- purple_notify_info(gc, NULL, "PONG", msg);
+ purple_notify_info(gc, NULL, "PONG", msg,
+ purple_request_cpar_from_connection(gc));
}
g_free(msg);
}
@@ -1302,7 +1284,7 @@ void irc_msg_privmsg(struct irc_conn *irc, const char *name, const char *from, c
static void irc_msg_handle_privmsg(struct irc_conn *irc, const char *name, const char *from, const char *to, const char *rawmsg, gboolean notice)
{
PurpleConnection *gc = purple_account_get_connection(irc->account);
- PurpleConversation *convo;
+ PurpleChatConversation *chat;
char *tmp;
char *msg;
char *nick;
@@ -1330,12 +1312,13 @@ static void irc_msg_handle_privmsg(struct irc_conn *irc, const char *name, const
}
if (!purple_utf8_strcasecmp(to, purple_connection_get_display_name(gc))) {
- serv_got_im(gc, nick, msg, 0, time(NULL));
+ purple_serv_got_im(gc, nick, msg, 0, time(NULL));
} else {
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, irc_nick_skip_mode(irc, to), irc->account);
- if (convo)
- serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)), nick, 0, msg, time(NULL));
- else
+ chat = purple_conversations_find_chat_with_account(irc_nick_skip_mode(irc, to), irc->account);
+ if (chat) {
+ purple_serv_got_chat_in(gc, purple_chat_conversation_get_id(chat),
+ nick, PURPLE_MESSAGE_RECV, msg, time(NULL));
+ } else
purple_debug_error("irc", "Got a %s on %s, which does not exist\n",
notice ? "NOTICE" : "PRIVMSG", to);
}
@@ -1346,13 +1329,11 @@ static void irc_msg_handle_privmsg(struct irc_conn *irc, const char *name, const
void irc_msg_regonly(struct irc_conn *irc, const char *name, const char *from, char **args)
{
PurpleConnection *gc = purple_account_get_connection(irc->account);
- PurpleConversation *convo;
char *msg;
g_return_if_fail(gc);
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[1], irc->account);
- if (convo) {
+ if (purple_conversations_find_chat_with_account(args[1], irc->account)) {
/* This is a channel we're already in; for some reason,
* freenode feels the need to notify us that in some
* hypothetical other situation this might not have
@@ -1361,7 +1342,8 @@ void irc_msg_regonly(struct irc_conn *irc, const char *name, const char *from, c
}
msg = g_strdup_printf(_("Cannot join %s: Registration is required."), args[1]);
- purple_notify_error(gc, _("Cannot join channel"), msg, args[2]);
+ purple_notify_error(gc, _("Cannot join channel"), msg, args[2],
+ purple_request_cpar_from_connection(gc));
g_free(msg);
}
@@ -1376,7 +1358,8 @@ void irc_msg_quit(struct irc_conn *irc, const char *name, const char *from, char
data[0] = irc_mask_nick(from);
data[1] = args[0];
/* XXX this should have an API, I shouldn't grab this directly */
- g_slist_foreach(gc->buddy_chats, (GFunc)irc_chat_remove_buddy, data);
+ g_slist_foreach(purple_connection_get_active_chats(gc),
+ (GFunc)irc_chat_remove_buddy, data);
if ((ib = g_hash_table_lookup(irc->buddies, data[0])) != NULL) {
ib->new_online_status = FALSE;
@@ -1391,7 +1374,9 @@ void irc_msg_unavailable(struct irc_conn *irc, const char *name, const char *fro
{
PurpleConnection *gc = purple_account_get_connection(irc->account);
- purple_notify_error(gc, NULL, _("Nick or channel is temporarily unavailable."), args[1]);
+ purple_notify_error(gc, NULL, _("Nick or channel is temporarily "
+ "unavailable."), args[1],
+ purple_request_cpar_from_connection(gc));
}
void irc_msg_wallops(struct irc_conn *irc, const char *name, const char *from, char **args)
@@ -1404,7 +1389,8 @@ void irc_msg_wallops(struct irc_conn *irc, const char *name, const char *from, c
nick = irc_mask_nick(from);
msg = g_strdup_printf (_("Wallops from %s"), nick);
g_free(nick);
- purple_notify_info(gc, NULL, msg, args[0]);
+ purple_notify_info(gc, NULL, msg, args[0],
+ purple_request_cpar_from_connection(gc));
g_free(msg);
}
@@ -1417,7 +1403,8 @@ irc_sasl_cb_secret(sasl_conn_t *conn, void *ctx, int id, sasl_secret_t **secret)
const char *pw;
size_t len;
- pw = purple_account_get_password(irc->account);
+ pw = purple_connection_get_password(purple_account_get_connection(
+ irc->account));
if (!conn || !secret || id != SASL_CB_PASS)
return SASL_BADPARAM;
@@ -1474,7 +1461,6 @@ irc_auth_start_cyrus(struct irc_conn *irc)
PurpleAccount *account = irc->account;
PurpleConnection *gc = purple_account_get_connection(account);
- gboolean plaintext;
gboolean again = FALSE;
/* Set up security properties and options */
@@ -1482,6 +1468,8 @@ irc_auth_start_cyrus(struct irc_conn *irc)
secprops.security_flags = SASL_SEC_NOANONYMOUS;
if (!irc->gsc) {
+ gboolean plaintext;
+
secprops.max_ssf = -1;
secprops.maxbufsize = 4096;
plaintext = purple_account_get_bool(account, "auth_plain_in_clear", FALSE);
@@ -1490,7 +1478,6 @@ irc_auth_start_cyrus(struct irc_conn *irc)
} else {
secprops.max_ssf = 0;
secprops.maxbufsize = 0;
- plaintext = TRUE;
}
secprops.property_names = 0;
@@ -1506,13 +1493,13 @@ irc_auth_start_cyrus(struct irc_conn *irc)
purple_debug_error("irc", "sasl_client_new failed: %d\n", ret);
tmp = g_strdup_printf(_("Failed to initialize SASL authentication: %s"),
sasl_errdetail(irc->sasl_conn));
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_OTHER_ERROR, tmp);
g_free(tmp);
return;
}
- sasl_setprop(irc->sasl_conn, SASL_AUTH_EXTERNAL, irc->account->username);
+ sasl_setprop(irc->sasl_conn, SASL_AUTH_EXTERNAL, purple_account_get_username(irc->account));
sasl_setprop(irc->sasl_conn, SASL_SEC_PROPS, &secprops);
ret = sasl_client_start(irc->sasl_conn, irc->sasl_mechs->str, NULL, NULL, NULL, &irc->current_mech);
@@ -1523,7 +1510,7 @@ irc_auth_start_cyrus(struct irc_conn *irc)
irc->mech_works = FALSE;
break;
case SASL_NOMECH:
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
_("SASL authentication failed: No worthy authentication mechanisms found."));
@@ -1532,7 +1519,7 @@ irc_auth_start_cyrus(struct irc_conn *irc)
case SASL_BADPARAM:
case SASL_NOMEM:
tmp = g_strdup_printf(_("SASL authentication failed: %s"), sasl_errdetail(irc->sasl_conn));
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_OTHER_ERROR, tmp);
g_free(tmp);
@@ -1579,7 +1566,7 @@ irc_msg_cap(struct irc_conn *irc, const char *name, const char *from, char **arg
return;
if (strncmp(args[1], "ACK", 4)) {
const char *tmp = _("SASL authentication failed: Server does not support SASL authentication.");
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, tmp);
irc_sasl_finish(irc);
@@ -1588,7 +1575,7 @@ irc_msg_cap(struct irc_conn *irc, const char *name, const char *from, char **arg
if ((ret = sasl_client_init(NULL)) != SASL_OK) {
const char *tmp = _("SASL authentication failed: Initializing SASL failed.");
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_OTHER_ERROR, tmp);
return;
}
@@ -1596,22 +1583,22 @@ irc_msg_cap(struct irc_conn *irc, const char *name, const char *from, char **arg
irc->sasl_cb = g_new0(sasl_callback_t, 5);
irc->sasl_cb[id].id = SASL_CB_AUTHNAME;
- irc->sasl_cb[id].proc = irc_sasl_cb_simple;
+ irc->sasl_cb[id].proc = (int (*)(void))irc_sasl_cb_simple; /* sasl_getsimple_t */
irc->sasl_cb[id].context = (void *)irc;
id++;
irc->sasl_cb[id].id = SASL_CB_USER;
- irc->sasl_cb[id].proc = irc_sasl_cb_simple;
+ irc->sasl_cb[id].proc = (int (*)(void))irc_sasl_cb_simple; /* sasl_getsimple_t */
irc->sasl_cb[id].context = (void *)irc;
id++;
irc->sasl_cb[id].id = SASL_CB_PASS;
- irc->sasl_cb[id].proc = irc_sasl_cb_secret;
+ irc->sasl_cb[id].proc = (int (*)(void))irc_sasl_cb_secret; /* sasl_getsecret_t */
irc->sasl_cb[id].context = (void *)irc;
id++;
irc->sasl_cb[id].id = SASL_CB_LOG;
- irc->sasl_cb[id].proc = irc_sasl_cb_log;
+ irc->sasl_cb[id].proc = (int (*)(void))irc_sasl_cb_log; /* sasl_log_t */
irc->sasl_cb[id].context = (void *)irc;
id++;
@@ -1629,7 +1616,7 @@ irc_msg_cap(struct irc_conn *irc, const char *name, const char *from, char **arg
purple_debug_error("irc", "sasl_client_new failed: %d\n", ret);
tmp = g_strdup_printf(_("Failed to initialize SASL authentication: %s"),
sasl_errdetail(irc->sasl_conn));
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_OTHER_ERROR, tmp);
g_free(tmp);
@@ -1669,7 +1656,7 @@ irc_msg_auth(struct irc_conn *irc, char *arg)
gchar *tmp = g_strdup_printf(_("SASL authentication failed: %s"),
sasl_errdetail(irc->sasl_conn));
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, tmp);
g_free(tmp);
@@ -1716,7 +1703,7 @@ irc_msg_authtryagain(struct irc_conn *irc, const char *name, const char *from, c
* aren't told the server supports no worthy mechanisms.
*/
if (irc->mech_works) {
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect Password"));
irc_sasl_finish(irc);
@@ -1742,7 +1729,7 @@ irc_msg_authtryagain(struct irc_conn *irc, const char *name, const char *from, c
purple_debug_info("irc", "Now trying with %s\n", irc->sasl_mechs->str);
irc_auth_start_cyrus(irc);
} else {
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
_("SASL authentication failed: No worthy mechanisms found"));
@@ -1759,7 +1746,7 @@ irc_msg_authfail(struct irc_conn *irc, const char *name, const char *from, char
if (irc->sasl_conn) {
purple_debug_info("irc", "SASL authentication failed: %s", sasl_errdetail(irc->sasl_conn));
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect Password"));
}
@@ -1783,8 +1770,3 @@ irc_sasl_finish(struct irc_conn *irc)
g_free(buf);
}
#endif
-
-void irc_msg_ignore(struct irc_conn *irc, const char *name, const char *from, char **args)
-{
- return;
-}
diff --git a/libpurple/protocols/irc/parse.c b/libpurple/protocols/irc/parse.c
index 7a755729eb..2d5cf7368c 100644
--- a/libpurple/protocols/irc/parse.c
+++ b/libpurple/protocols/irc/parse.c
@@ -185,11 +185,11 @@ static PurpleCmdRet irc_parse_purple_cmd(PurpleConversation *conv, const gchar *
struct irc_conn *irc;
struct _irc_user_cmd *cmdent;
- gc = purple_conversation_get_gc(conv);
+ gc = purple_conversation_get_connection(conv);
if (!gc)
return PURPLE_CMD_RET_FAILED;
- irc = gc->proto_data;
+ irc = purple_connection_get_protocol_data(gc);
if ((cmdent = g_hash_table_lookup(irc->cmds, cmd)) == NULL)
return PURPLE_CMD_RET_FAILED;
@@ -390,7 +390,7 @@ char *irc_mirc2html(const char *string)
do {
end = strpbrk(cur, "\002\003\007\017\026\037");
- decoded = g_string_append_len(decoded, cur, end ? end - cur : strlen(cur));
+ decoded = g_string_append_len(decoded, cur, (end ? (gssize)(end - cur) : (gssize)strlen(cur)));
cur = end ? end : cur + strlen(cur);
switch (*cur) {
@@ -571,7 +571,9 @@ char *irc_parse_ctcp(struct irc_conn *irc, const char *from, const char *to, con
/* TODO: Should this read in the timestamp as a double? */
if (sscanf(cur, "PING %lu", &timestamp) == 1) {
buf = g_strdup_printf(_("Reply time from %s: %lu seconds"), from, time(NULL) - timestamp);
- purple_notify_info(gc, _("PONG"), _("CTCP PING reply"), buf);
+ purple_notify_info(gc, _("PONG"),
+ _("CTCP PING reply"), buf,
+ purple_request_cpar_from_connection(gc));
g_free(buf);
} else
purple_debug(PURPLE_DEBUG_ERROR, "irc", "Unable to parse PING timestamp");
@@ -688,11 +690,11 @@ void irc_parse_msg(struct irc_conn *irc, char *input)
} else if (!strncmp(input, "ERROR ", 6)) {
if (g_utf8_validate(input, -1, NULL)) {
char *tmp = g_strdup_printf("%s\n%s", _("Disconnected."), input);
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
g_free(tmp);
} else
- purple_connection_error_reason (gc,
+ purple_connection_error (gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Disconnected."));
return;
diff --git a/libpurple/protocols/jabber/Makefile.am b/libpurple/protocols/jabber/Makefile.am
index 7a53f3b19b..8b0c85551e 100644
--- a/libpurple/protocols/jabber/Makefile.am
+++ b/libpurple/protocols/jabber/Makefile.am
@@ -3,7 +3,7 @@ EXTRA_DIST = \
win32/posix.uname.c \
win32/utsname.h
-pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+pkgdir = @PURPLE_PLUGINDIR@
JABBERSOURCES = \
adhoccommands.c \
@@ -33,6 +33,8 @@ JABBERSOURCES = \
google/gmail.h \
google/google.c \
google/google.h \
+ google/google_p2p.c \
+ google/google_p2p.h \
google/google_presence.c \
google/google_presence.h \
google/google_roster.c \
@@ -95,28 +97,51 @@ JABBERSOURCES = \
AM_CFLAGS = $(st)
-libxmpp_la_LDFLAGS = -module -avoid-version
+libxmpp_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+libgtalk_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+libfacebook_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
if USE_CYRUS_SASL
JABBERSOURCES += auth_cyrus.c
endif
+if IS_WIN32
+JABBERSOURCES += win32/posix.uname.c
+endif
+
if STATIC_JABBER
st = -DPURPLE_STATIC_PRPL
noinst_LTLIBRARIES = libjabber.la
-libjabber_la_SOURCES = $(JABBERSOURCES) libxmpp.c
+libjabber_la_SOURCES = $(JABBERSOURCES) libfacebook.c libgtalk.c libxmpp.c
libjabber_la_CFLAGS = $(AM_CFLAGS)
+libjabber_la_LIBADD = @PURPLE_LIBS@
+libjabber_la_LDFLAGS = @PLUGIN_LDFLAGS@
+
else
+if IS_WIN32
+lib_LTLIBRARIES = libjabber.la
+pkg_LTLIBRARIES =
+else
+pkg_LTLIBRARIES = libjabber.la
+endif
+
st =
-pkg_LTLIBRARIES = libjabber.la libxmpp.la
+pkg_LTLIBRARIES += libfacebook.la libgtalk.la libxmpp.la
+
libjabber_la_SOURCES = $(JABBERSOURCES)
-libjabber_la_LIBADD = $(GLIB_LIBS) $(SASL_LIBS) $(LIBXML_LIBS) $(IDN_LIBS)\
- $(FARSIGHT_LIBS) \
- $(GSTREAMER_LIBS) \
- $(GSTINTERFACES_LIBS)
+libjabber_la_LIBADD = @PURPLE_LIBS@ $(SASL_LIBS) $(LIBXML_LIBS) $(IDN_LIBS)\
+ $(FARSTREAM_LIBS) \
+ $(GSTREAMER_LIBS)
+libjabber_la_LDFLAGS = @PLUGIN_LDFLAGS@
+
+libfacebook_la_SOURCES = libfacebook.c
+libfacebook_la_LIBADD = libjabber.la
+
+libgtalk_la_SOURCES = libgtalk.c
+libgtalk_la_LIBADD = libjabber.la
libxmpp_la_SOURCES = libxmpp.c
libxmpp_la_LIBADD = libjabber.la
@@ -130,6 +155,5 @@ AM_CPPFLAGS = \
$(GLIB_CFLAGS) \
$(IDN_CFLAGS) \
$(LIBXML_CFLAGS) \
- $(FARSIGHT_CFLAGS) \
- $(GSTREAMER_CFLAGS) \
- $(GSTINTERFACES_CFLAGS)
+ $(FARSTREAM_CFLAGS) \
+ $(GSTREAMER_CFLAGS)
diff --git a/libpurple/protocols/jabber/Makefile.mingw b/libpurple/protocols/jabber/Makefile.mingw
index ec8029586f..4cb0db56e4 100644
--- a/libpurple/protocols/jabber/Makefile.mingw
+++ b/libpurple/protocols/jabber/Makefile.mingw
@@ -8,6 +8,8 @@ PIDGIN_TREE_TOP := ../../..
include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
TARGET = libjabber
+FACEBOOK_TARGET = libfacebook
+GTALK_TARGET = libgtalk
XMPP_TARGET = libxmpp
TYPE = PLUGIN
@@ -32,16 +34,24 @@ INCLUDE_PATHS += -I. \
-I$(LIBXML2_TOP)/include/libxml2 \
-I$(PURPLE_TOP) \
-I$(PURPLE_TOP)/win32 \
- -I$(PIDGIN_TREE_TOP)
+ -I$(PIDGIN_TREE_TOP) \
+ $(VV_INCLUDE_PATHS)
LIB_PATHS += -L$(GTK_TOP)/lib \
-L$(LIBXML2_TOP)/lib \
-L$(PURPLE_TOP) \
- -L.
+ -L. \
+ $(VV_LIB_PATHS)
##
## SOURCES, OBJECTS
##
+ifeq "$(USE_VV)" "1"
+VV_SRC = google/google_p2p.c
+else
+VV_SRC =
+endif
+
C_SRC = \
adhoccommands.c \
auth.c \
@@ -87,10 +97,17 @@ C_SRC = \
usernick.c \
usertune.c \
xdata.c \
- win32/posix.uname.c
+ win32/posix.uname.c \
+ $(VV_SRC)
OBJECTS = $(C_SRC:%.c=%.o)
+FACEBOOK_C_SRC = libfacebook.c
+FACEBOOK_OBJECTS = $(FACEBOOK_C_SRC:%.c=%.o)
+
+GTALK_C_SRC = libgtalk.c
+GTALK_OBJECTS = $(GTALK_C_SRC:%.c=%.o)
+
XMPP_C_SRC = libxmpp.c
XMPP_OBJECTS = $(XMPP_C_SRC:%.c=%.o)
@@ -100,6 +117,7 @@ XMPP_OBJECTS = $(XMPP_C_SRC:%.c=%.o)
LIBS = \
-lglib-2.0 \
-lgobject-2.0 \
+ $(VV_LIBS) \
-lxml2 \
-lws2_32 \
-lintl \
@@ -118,9 +136,11 @@ include $(PIDGIN_COMMON_RULES)
##
.PHONY: all install clean
-all: $(TARGET).dll $(XMPP_TARGET).dll
+all: $(TARGET).dll $(FACEBOOK_TARGET).dll $(GTALK_TARGET).dll $(XMPP_TARGET).dll
install: all $(DLL_INSTALL_DIR)
+ cp $(FACEBOOK_TARGET).dll $(DLL_INSTALL_DIR)
+ cp $(GTALK_TARGET).dll $(DLL_INSTALL_DIR)
cp $(XMPP_TARGET).dll $(DLL_INSTALL_DIR)
cp $(TARGET).dll $(PURPLE_INSTALL_DIR)
@@ -129,6 +149,12 @@ $(OBJECTS): $(PURPLE_CONFIG_H)
$(TARGET).dll $(TARGET).dll.a: $(PURPLE_DLL).a $(OBJECTS)
$(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -Wl,--out-implib,$(TARGET).dll.a -o $(TARGET).dll
+$(FACEBOOK_TARGET).dll: $(TARGET).dll.a $(FACEBOOK_OBJECTS)
+ $(CC) -shared $(FACEBOOK_OBJECTS) $(LIB_PATHS) $(LIBS) -ljabber $(DLL_LD_FLAGS) -o $(FACEBOOK_TARGET).dll
+
+$(GTALK_TARGET).dll: $(TARGET).dll.a $(GTALK_OBJECTS)
+ $(CC) -shared $(GTALK_OBJECTS) $(LIB_PATHS) $(LIBS) -ljabber $(DLL_LD_FLAGS) -o $(GTALK_TARGET).dll
+
$(XMPP_TARGET).dll: $(TARGET).dll.a $(XMPP_OBJECTS)
$(CC) -shared $(XMPP_OBJECTS) $(LIB_PATHS) $(LIBS) -ljabber $(DLL_LD_FLAGS) -o $(XMPP_TARGET).dll
@@ -137,6 +163,8 @@ $(XMPP_TARGET).dll: $(TARGET).dll.a $(XMPP_OBJECTS)
##
clean:
rm -f $(OBJECTS) $(TARGET).dll $(TARGET).dll.a
+ rm -f $(FACEBOOK_OBJECTS) $(FACEBOOK_TARGET).dll
+ rm -f $(GTALK_OBJECTS) $(GTALK_TARGET).dll
rm -f $(XMPP_OBJECTS) $(XMPP_TARGET).dll
include $(PIDGIN_COMMON_TARGETS)
diff --git a/libpurple/protocols/jabber/adhoccommands.c b/libpurple/protocols/jabber/adhoccommands.c
index 214f5dfdef..34e16a1b8b 100644
--- a/libpurple/protocols/jabber/adhoccommands.c
+++ b/libpurple/protocols/jabber/adhoccommands.c
@@ -42,12 +42,12 @@ typedef struct _JabberAdHocActionInfo {
} JabberAdHocActionInfo;
static void
-jabber_adhoc_got_buddy_list(JabberStream *js, const char *from, xmlnode *query)
+jabber_adhoc_got_buddy_list(JabberStream *js, const char *from, PurpleXmlNode *query)
{
JabberID *jid;
JabberBuddy *jb;
JabberBuddyResource *jbr = NULL;
- xmlnode *item;
+ PurpleXmlNode *item;
if ((jid = jabber_id_new(from))) {
if (jid->resource && (jb = jabber_buddy_find(js, from, TRUE)))
@@ -72,15 +72,15 @@ jabber_adhoc_got_buddy_list(JabberStream *js, const char *from, xmlnode *query)
for(item = query->child; item; item = item->next) {
JabberAdHocCommands *cmd;
- if(item->type != XMLNODE_TYPE_TAG)
+ if(item->type != PURPLE_XMLNODE_TYPE_TAG)
continue;
if(strcmp(item->name, "item"))
continue;
cmd = g_new0(JabberAdHocCommands, 1);
- cmd->jid = g_strdup(xmlnode_get_attrib(item,"jid"));
- cmd->node = g_strdup(xmlnode_get_attrib(item,"node"));
- cmd->name = g_strdup(xmlnode_get_attrib(item,"name"));
+ cmd->jid = g_strdup(purple_xmlnode_get_attrib(item,"jid"));
+ cmd->node = g_strdup(purple_xmlnode_get_attrib(item,"node"));
+ cmd->name = g_strdup(purple_xmlnode_get_attrib(item,"name"));
jbr->commands = g_list_append(jbr->commands,cmd);
}
@@ -89,18 +89,18 @@ jabber_adhoc_got_buddy_list(JabberStream *js, const char *from, xmlnode *query)
void
jabber_adhoc_disco_result_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
- xmlnode *query;
+ PurpleXmlNode *query;
const char *node;
if (type == JABBER_IQ_ERROR)
return;
- query = xmlnode_get_child_with_namespace(packet, "query", NS_DISCO_ITEMS);
+ query = purple_xmlnode_get_child_with_namespace(packet, "query", NS_DISCO_ITEMS);
if (!query)
return;
- node = xmlnode_get_attrib(query, "node");
+ node = purple_xmlnode_get_attrib(query, "node");
if (!purple_strequal(node, "http://jabber.org/protocol/commands"))
return;
@@ -109,29 +109,29 @@ jabber_adhoc_disco_result_cb(JabberStream *js, const char *from,
static void jabber_adhoc_parse(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data);
+ PurpleXmlNode *packet, gpointer data);
-static void do_adhoc_action_cb(JabberStream *js, xmlnode *result, const char *actionhandle, gpointer user_data) {
- xmlnode *command;
+static void do_adhoc_action_cb(JabberStream *js, PurpleXmlNode *result, const char *actionhandle, gpointer user_data) {
+ PurpleXmlNode *command;
GList *action;
JabberAdHocActionInfo *actionInfo = user_data;
JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET);
jabber_iq_set_callback(iq, jabber_adhoc_parse, NULL);
- xmlnode_set_attrib(iq->node, "to", actionInfo->who);
- command = xmlnode_new_child(iq->node,"command");
- xmlnode_set_namespace(command,"http://jabber.org/protocol/commands");
- xmlnode_set_attrib(command,"sessionid",actionInfo->sessionid);
- xmlnode_set_attrib(command,"node",actionInfo->node);
+ purple_xmlnode_set_attrib(iq->node, "to", actionInfo->who);
+ command = purple_xmlnode_new_child(iq->node,"command");
+ purple_xmlnode_set_namespace(command,"http://jabber.org/protocol/commands");
+ purple_xmlnode_set_attrib(command,"sessionid",actionInfo->sessionid);
+ purple_xmlnode_set_attrib(command,"node",actionInfo->node);
/* cancel is handled differently on ad-hoc commands than regular forms */
- if (purple_strequal(xmlnode_get_namespace(result), "jabber:x:data") &&
- purple_strequal(xmlnode_get_attrib(result, "type"), "cancel")) {
- xmlnode_set_attrib(command,"action","cancel");
+ if (purple_strequal(purple_xmlnode_get_namespace(result), "jabber:x:data") &&
+ purple_strequal(purple_xmlnode_get_attrib(result, "type"), "cancel")) {
+ purple_xmlnode_set_attrib(command,"action","cancel");
} else {
if(actionhandle)
- xmlnode_set_attrib(command,"action",actionhandle);
- xmlnode_insert_child(command,result);
+ purple_xmlnode_set_attrib(command,"action",actionhandle);
+ purple_xmlnode_insert_child(command,result);
}
for(action = actionInfo->actionslist; action; action = g_list_next(action)) {
@@ -149,11 +149,11 @@ static void do_adhoc_action_cb(JabberStream *js, xmlnode *result, const char *ac
static void
jabber_adhoc_parse(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
- xmlnode *command = xmlnode_get_child_with_namespace(packet, "command", "http://jabber.org/protocol/commands");
- const char *status = xmlnode_get_attrib(command,"status");
- xmlnode *xdata = xmlnode_get_child_with_namespace(command,"x","jabber:x:data");
+ PurpleXmlNode *command = purple_xmlnode_get_child_with_namespace(packet, "command", "http://jabber.org/protocol/commands");
+ const char *status = purple_xmlnode_get_attrib(command,"status");
+ PurpleXmlNode *xdata = purple_xmlnode_get_child_with_namespace(command,"x","jabber:x:data");
if (type == JABBER_IQ_ERROR) {
char *msg = jabber_parse_error(js, packet, NULL);
@@ -161,7 +161,8 @@ jabber_adhoc_parse(JabberStream *js, const char *from,
msg = g_strdup(_("Unknown Error"));
purple_notify_error(NULL, _("Ad-Hoc Command Failed"),
- _("Ad-Hoc Command Failed"), msg);
+ _("Ad-Hoc Command Failed"), msg,
+ purple_request_cpar_from_connection(js->gc));
g_free(msg);
return;
}
@@ -171,11 +172,12 @@ jabber_adhoc_parse(JabberStream *js, const char *from,
if(!strcmp(status,"completed")) {
/* display result */
- xmlnode *note = xmlnode_get_child(command,"note");
+ PurpleXmlNode *note = purple_xmlnode_get_child(command,"note");
if(note) {
- char *data = xmlnode_get_data(note);
- purple_notify_info(NULL, from, data, NULL);
+ char *data = purple_xmlnode_get_data(note);
+ purple_notify_info(NULL, from, data, NULL,
+ purple_request_cpar_from_connection(js->gc));
g_free(data);
}
@@ -185,24 +187,24 @@ jabber_adhoc_parse(JabberStream *js, const char *from,
}
if(!strcmp(status,"executing")) {
/* this command needs more steps */
- xmlnode *actions, *action;
+ PurpleXmlNode *actions, *action;
int actionindex = 0;
GList *actionslist = NULL;
JabberAdHocActionInfo *actionInfo;
if(!xdata)
return; /* shouldn't happen */
- actions = xmlnode_get_child(command,"actions");
+ actions = purple_xmlnode_get_child(command,"actions");
if(!actions) {
JabberXDataAction *defaultaction = g_new0(JabberXDataAction, 1);
defaultaction->name = g_strdup(_("execute"));
defaultaction->handle = g_strdup("execute");
actionslist = g_list_append(actionslist, defaultaction);
} else {
- const char *defaultactionhandle = xmlnode_get_attrib(actions, "execute");
+ const char *defaultactionhandle = purple_xmlnode_get_attrib(actions, "execute");
int index = 0;
for(action = actions->child; action; action = action->next, ++index) {
- if(action->type == XMLNODE_TYPE_TAG) {
+ if(action->type == PURPLE_XMLNODE_TYPE_TAG) {
JabberXDataAction *newaction = g_new0(JabberXDataAction, 1);
newaction->name = g_strdup(_(action->name));
newaction->handle = g_strdup(action->name);
@@ -214,9 +216,9 @@ jabber_adhoc_parse(JabberStream *js, const char *from,
}
actionInfo = g_new0(JabberAdHocActionInfo, 1);
- actionInfo->sessionid = g_strdup(xmlnode_get_attrib(command,"sessionid"));
+ actionInfo->sessionid = g_strdup(purple_xmlnode_get_attrib(command,"sessionid"));
actionInfo->who = g_strdup(from);
- actionInfo->node = g_strdup(xmlnode_get_attrib(command,"node"));
+ actionInfo->node = g_strdup(purple_xmlnode_get_attrib(command,"node"));
actionInfo->actionslist = actionslist;
jabber_x_data_request_with_actions(js,xdata,actionslist,actionindex,do_adhoc_action_cb,actionInfo);
@@ -224,20 +226,21 @@ jabber_adhoc_parse(JabberStream *js, const char *from,
}
void jabber_adhoc_execute_action(PurpleBlistNode *node, gpointer data) {
- if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ if (PURPLE_IS_BUDDY(node)) {
JabberAdHocCommands *cmd = data;
PurpleBuddy *buddy = (PurpleBuddy *) node;
PurpleAccount *account = purple_buddy_get_account(buddy);
- JabberStream *js = purple_account_get_connection(account)->proto_data;
+ PurpleConnection *gc = purple_account_get_connection(account);
+ JabberStream *js = purple_connection_get_protocol_data(gc);
jabber_adhoc_execute(js, cmd);
}
}
static void
-jabber_adhoc_got_server_list(JabberStream *js, const char *from, xmlnode *query)
+jabber_adhoc_got_server_list(JabberStream *js, const char *from, PurpleXmlNode *query)
{
- xmlnode *item;
+ PurpleXmlNode *item;
if(!query)
return;
@@ -255,14 +258,14 @@ jabber_adhoc_got_server_list(JabberStream *js, const char *from, xmlnode *query)
/* re-fill list */
for(item = query->child; item; item = item->next) {
JabberAdHocCommands *cmd;
- if(item->type != XMLNODE_TYPE_TAG)
+ if(item->type != PURPLE_XMLNODE_TYPE_TAG)
continue;
if(strcmp(item->name, "item"))
continue;
cmd = g_new0(JabberAdHocCommands, 1);
- cmd->jid = g_strdup(xmlnode_get_attrib(item,"jid"));
- cmd->node = g_strdup(xmlnode_get_attrib(item,"node"));
- cmd->name = g_strdup(xmlnode_get_attrib(item,"name"));
+ cmd->jid = g_strdup(purple_xmlnode_get_attrib(item,"jid"));
+ cmd->node = g_strdup(purple_xmlnode_get_attrib(item,"node"));
+ cmd->name = g_strdup(purple_xmlnode_get_attrib(item,"name"));
js->commands = g_list_append(js->commands,cmd);
}
@@ -274,16 +277,16 @@ jabber_adhoc_got_server_list(JabberStream *js, const char *from, xmlnode *query)
static void
jabber_adhoc_server_got_list_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
- xmlnode *query = xmlnode_get_child_with_namespace(packet, "query",
+ PurpleXmlNode *query = purple_xmlnode_get_child_with_namespace(packet, "query",
NS_DISCO_ITEMS);
jabber_adhoc_got_server_list(js, from, query);
}
-void jabber_adhoc_got_list(JabberStream *js, const char *from, xmlnode *query)
+void jabber_adhoc_got_list(JabberStream *js, const char *from, PurpleXmlNode *query)
{
if (purple_strequal(from, js->user->domain)) {
jabber_adhoc_got_server_list(js, from, query);
@@ -294,11 +297,11 @@ void jabber_adhoc_got_list(JabberStream *js, const char *from, xmlnode *query)
void jabber_adhoc_server_get_list(JabberStream *js) {
JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_DISCO_ITEMS);
- xmlnode *query = xmlnode_get_child_with_namespace(iq->node, "query",
+ PurpleXmlNode *query = purple_xmlnode_get_child_with_namespace(iq->node, "query",
NS_DISCO_ITEMS);
- xmlnode_set_attrib(iq->node,"to",js->user->domain);
- xmlnode_set_attrib(query,"node","http://jabber.org/protocol/commands");
+ purple_xmlnode_set_attrib(iq->node,"to",js->user->domain);
+ purple_xmlnode_set_attrib(query,"node","http://jabber.org/protocol/commands");
jabber_iq_set_callback(iq,jabber_adhoc_server_got_list_cb,NULL);
jabber_iq_send(iq);
@@ -306,11 +309,11 @@ void jabber_adhoc_server_get_list(JabberStream *js) {
void jabber_adhoc_execute(JabberStream *js, JabberAdHocCommands *cmd) {
JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET);
- xmlnode *command = xmlnode_new_child(iq->node,"command");
- xmlnode_set_attrib(iq->node,"to",cmd->jid);
- xmlnode_set_namespace(command,"http://jabber.org/protocol/commands");
- xmlnode_set_attrib(command,"node",cmd->node);
- xmlnode_set_attrib(command,"action","execute");
+ PurpleXmlNode *command = purple_xmlnode_new_child(iq->node,"command");
+ purple_xmlnode_set_attrib(iq->node,"to",cmd->jid);
+ purple_xmlnode_set_namespace(command,"http://jabber.org/protocol/commands");
+ purple_xmlnode_set_attrib(command,"node",cmd->node);
+ purple_xmlnode_set_attrib(command,"action","execute");
jabber_iq_set_callback(iq,jabber_adhoc_parse,NULL);
@@ -321,7 +324,7 @@ static void jabber_adhoc_server_execute(PurplePluginAction *action) {
JabberAdHocCommands *cmd = action->user_data;
if(cmd) {
PurpleConnection *gc = (PurpleConnection *) action->context;
- JabberStream *js = gc->proto_data;
+ JabberStream *js = purple_connection_get_protocol_data(gc);
jabber_adhoc_execute(js, cmd);
}
diff --git a/libpurple/protocols/jabber/adhoccommands.h b/libpurple/protocols/jabber/adhoccommands.h
index 67958c09bb..4052f0a385 100644
--- a/libpurple/protocols/jabber/adhoccommands.h
+++ b/libpurple/protocols/jabber/adhoccommands.h
@@ -30,13 +30,13 @@
void jabber_adhoc_disco_result_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data);
+ PurpleXmlNode *packet, gpointer data);
void jabber_adhoc_execute(JabberStream *js, JabberAdHocCommands *cmd);
void jabber_adhoc_execute_action(PurpleBlistNode *node, gpointer data);
-void jabber_adhoc_got_list(JabberStream *js, const char *from, xmlnode *query);
+void jabber_adhoc_got_list(JabberStream *js, const char *from, PurpleXmlNode *query);
void jabber_adhoc_server_get_list(JabberStream *js);
diff --git a/libpurple/protocols/jabber/auth.c b/libpurple/protocols/jabber/auth.c
index dfabe4fb0d..db0e6ed020 100644
--- a/libpurple/protocols/jabber/auth.c
+++ b/libpurple/protocols/jabber/auth.c
@@ -24,7 +24,6 @@
#include "account.h"
#include "debug.h"
-#include "cipher.h"
#include "core.h"
#include "conversation.h"
#include "request.h"
@@ -39,25 +38,28 @@
#include "iq.h"
#include "notify.h"
+#include "ciphers/hmaccipher.h"
+#include "ciphers/md5hash.h"
+
static GSList *auth_mechs = NULL;
static void auth_old_result_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data);
+ PurpleXmlNode *packet, gpointer data);
static void finish_plaintext_authentication(JabberStream *js)
{
JabberIq *iq;
- xmlnode *query, *x;
+ PurpleXmlNode *query, *x;
iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth");
- query = xmlnode_get_child(iq->node, "query");
- x = xmlnode_new_child(query, "username");
- xmlnode_insert_data(x, js->user->node, -1);
- x = xmlnode_new_child(query, "resource");
- xmlnode_insert_data(x, js->user->resource, -1);
- x = xmlnode_new_child(query, "password");
- xmlnode_insert_data(x, purple_connection_get_password(js->gc), -1);
+ query = purple_xmlnode_get_child(iq->node, "query");
+ x = purple_xmlnode_new_child(query, "username");
+ purple_xmlnode_insert_data(x, js->user->node, -1);
+ x = purple_xmlnode_new_child(query, "resource");
+ purple_xmlnode_insert_data(x, js->user->resource, -1);
+ x = purple_xmlnode_new_child(query, "password");
+ purple_xmlnode_insert_data(x, purple_connection_get_password(js->gc), -1);
jabber_iq_set_callback(iq, auth_old_result_cb, NULL);
jabber_iq_send(iq);
}
@@ -77,7 +79,7 @@ static void allow_plaintext_auth(PurpleAccount *account)
static void disallow_plaintext_auth(PurpleAccount *account)
{
- purple_connection_error_reason(purple_account_get_connection(account),
+ purple_connection_error(purple_account_get_connection(account),
PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
_("Server requires plaintext authentication over an unencrypted stream"));
}
@@ -91,9 +93,8 @@ auth_old_pass_cb(PurpleConnection *gc, PurpleRequestFields *fields)
const char *entry;
gboolean remember;
- /* The password prompt dialog doesn't get disposed if the account disconnects */
- if (!PURPLE_CONNECTION_IS_VALID(gc))
- return;
+ /* TODO: the password prompt dialog doesn't get disposed if the account disconnects */
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
account = purple_connection_get_account(gc);
js = purple_connection_get_protocol_data(gc);
@@ -103,14 +104,16 @@ auth_old_pass_cb(PurpleConnection *gc, PurpleRequestFields *fields)
if (!entry || !*entry)
{
- purple_notify_error(account, NULL, _("Password is required to sign on."), NULL);
+ purple_notify_error(account, NULL,
+ _("Password is required to sign on."), NULL,
+ purple_request_cpar_from_connection(gc));
return;
}
if (remember)
purple_account_set_remember_password(account, TRUE);
- purple_account_set_password(account, entry);
+ purple_account_set_password(account, entry, NULL, NULL);
/* Restart our connection */
jabber_auth_start_old(js);
@@ -119,9 +122,8 @@ auth_old_pass_cb(PurpleConnection *gc, PurpleRequestFields *fields)
static void
auth_no_pass_cb(PurpleConnection *gc, PurpleRequestFields *fields)
{
- /* The password prompt dialog doesn't get disposed if the account disconnects */
- if (!PURPLE_CONNECTION_IS_VALID(gc))
- return;
+ /* TODO: the password prompt dialog doesn't get disposed if the account disconnects */
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
/* Disable the account as the user has cancelled connecting */
purple_account_set_enabled(purple_connection_get_account(gc), purple_core_get_ui(), FALSE);
@@ -129,12 +131,12 @@ auth_no_pass_cb(PurpleConnection *gc, PurpleRequestFields *fields)
#endif
void
-jabber_auth_start(JabberStream *js, xmlnode *packet)
+jabber_auth_start(JabberStream *js, PurpleXmlNode *packet)
{
GSList *mechanisms = NULL;
GSList *l;
- xmlnode *response = NULL;
- xmlnode *mechs, *mechnode;
+ PurpleXmlNode *response = NULL;
+ PurpleXmlNode *mechs, *mechnode;
JabberSaslState state;
char *msg = NULL;
@@ -143,18 +145,18 @@ jabber_auth_start(JabberStream *js, xmlnode *packet)
return;
}
- mechs = xmlnode_get_child(packet, "mechanisms");
+ mechs = purple_xmlnode_get_child(packet, "mechanisms");
if(!mechs) {
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Invalid response from server"));
return;
}
- for(mechnode = xmlnode_get_child(mechs, "mechanism"); mechnode;
- mechnode = xmlnode_get_next_twin(mechnode))
+ for(mechnode = purple_xmlnode_get_child(mechs, "mechanism"); mechnode;
+ mechnode = purple_xmlnode_get_next_twin(mechnode))
{
- char *mech_name = xmlnode_get_data(mechnode);
+ char *mech_name = purple_xmlnode_get_data(mechnode);
if (mech_name && *mech_name)
mechanisms = g_slist_prepend(mechanisms, mech_name);
@@ -186,7 +188,7 @@ jabber_auth_start(JabberStream *js, xmlnode *packet)
if (js->auth_mech == NULL) {
/* Found no good mechanisms... */
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
_("Server does not use any supported authentication method"));
return;
@@ -194,12 +196,12 @@ jabber_auth_start(JabberStream *js, xmlnode *packet)
state = js->auth_mech->start(js, mechs, &response, &msg);
if (state == JABBER_SASL_STATE_FAIL) {
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
msg ? msg : _("Unknown Error"));
} else if (response) {
jabber_send(js, response);
- xmlnode_free(response);
+ purple_xmlnode_free(response);
}
g_free(msg);
@@ -207,7 +209,7 @@ jabber_auth_start(JabberStream *js, xmlnode *packet)
static void auth_old_result_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
if (type == JABBER_IQ_RESULT) {
jabber_stream_set_state(js, JABBER_STREAM_POST_AUTH);
@@ -216,94 +218,99 @@ static void auth_old_result_cb(JabberStream *js, const char *from,
PurpleAccount *account;
PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
char *msg = jabber_parse_error(js, packet, &reason);
- xmlnode *error;
+ PurpleXmlNode *error;
const char *err_code;
account = purple_connection_get_account(js->gc);
/* FIXME: Why is this not in jabber_parse_error? */
- if((error = xmlnode_get_child(packet, "error")) &&
- (err_code = xmlnode_get_attrib(error, "code")) &&
+ if((error = purple_xmlnode_get_child(packet, "error")) &&
+ (err_code = purple_xmlnode_get_attrib(error, "code")) &&
g_str_equal(err_code, "401")) {
reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
/* Clear the pasword if it isn't being saved */
if (!purple_account_get_remember_password(account))
- purple_account_set_password(account, NULL);
+ purple_account_set_password(account, NULL, NULL, NULL);
}
- purple_connection_error_reason(js->gc, reason, msg);
+ purple_connection_error(js->gc, reason, msg);
g_free(msg);
}
}
static void auth_old_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
JabberIq *iq;
- xmlnode *query, *x;
+ PurpleXmlNode *query, *x;
const char *pw = purple_connection_get_password(js->gc);
if (type == JABBER_IQ_ERROR) {
PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
char *msg = jabber_parse_error(js, packet, &reason);
- purple_connection_error_reason(js->gc, reason, msg);
+ purple_connection_error(js->gc, reason, msg);
g_free(msg);
} else if (type == JABBER_IQ_RESULT) {
- query = xmlnode_get_child(packet, "query");
+ query = purple_xmlnode_get_child(packet, "query");
if (js->stream_id && *js->stream_id &&
- xmlnode_get_child(query, "digest")) {
+ purple_xmlnode_get_child(query, "digest")) {
char *s, *hash;
iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth");
- query = xmlnode_get_child(iq->node, "query");
- x = xmlnode_new_child(query, "username");
- xmlnode_insert_data(x, js->user->node, -1);
- x = xmlnode_new_child(query, "resource");
- xmlnode_insert_data(x, js->user->resource, -1);
+ query = purple_xmlnode_get_child(iq->node, "query");
+ x = purple_xmlnode_new_child(query, "username");
+ purple_xmlnode_insert_data(x, js->user->node, -1);
+ x = purple_xmlnode_new_child(query, "resource");
+ purple_xmlnode_insert_data(x, js->user->resource, -1);
- x = xmlnode_new_child(query, "digest");
+ x = purple_xmlnode_new_child(query, "digest");
s = g_strdup_printf("%s%s", js->stream_id, pw);
hash = jabber_calculate_data_hash(s, strlen(s), "sha1");
- xmlnode_insert_data(x, hash, -1);
+ purple_xmlnode_insert_data(x, hash, -1);
g_free(hash);
g_free(s);
jabber_iq_set_callback(iq, auth_old_result_cb, NULL);
jabber_iq_send(iq);
- } else if ((x = xmlnode_get_child(query, "crammd5"))) {
+ } else if ((x = purple_xmlnode_get_child(query, "crammd5"))) {
/* For future reference, this appears to be a custom OS X extension
* to non-SASL authentication.
*/
const char *challenge;
gchar digest[33];
- PurpleCipherContext *hmac;
+ PurpleCipher *hmac;
+ PurpleHash *md5;
+ gssize diglen;
/* Calculate the MHAC-MD5 digest */
- challenge = xmlnode_get_attrib(x, "challenge");
- hmac = purple_cipher_context_new_by_name("hmac", NULL);
- purple_cipher_context_set_option(hmac, "hash", "md5");
- purple_cipher_context_set_key(hmac, (guchar *)pw);
- purple_cipher_context_append(hmac, (guchar *)challenge, strlen(challenge));
- purple_cipher_context_digest_to_str(hmac, 33, digest, NULL);
- purple_cipher_context_destroy(hmac);
+ md5 = purple_md5_hash_new();
+ hmac = purple_hmac_cipher_new(md5);
+ challenge = purple_xmlnode_get_attrib(x, "challenge");
+ purple_cipher_set_key(hmac, (guchar *)pw, strlen(pw));
+ purple_cipher_append(hmac, (guchar *)challenge, strlen(challenge));
+ diglen = purple_cipher_digest_to_str(hmac, digest, 33);
+ g_object_unref(hmac);
+ g_object_unref(md5);
+
+ g_return_if_fail(diglen > 0);
/* Create the response query */
iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth");
- query = xmlnode_get_child(iq->node, "query");
+ query = purple_xmlnode_get_child(iq->node, "query");
- x = xmlnode_new_child(query, "username");
- xmlnode_insert_data(x, js->user->node, -1);
- x = xmlnode_new_child(query, "resource");
- xmlnode_insert_data(x, js->user->resource, -1);
+ x = purple_xmlnode_new_child(query, "username");
+ purple_xmlnode_insert_data(x, js->user->node, -1);
+ x = purple_xmlnode_new_child(query, "resource");
+ purple_xmlnode_insert_data(x, js->user->resource, -1);
- x = xmlnode_new_child(query, "crammd5");
+ x = purple_xmlnode_new_child(query, "crammd5");
- xmlnode_insert_data(x, digest, 32);
+ purple_xmlnode_insert_data(x, digest, 32);
jabber_iq_set_callback(iq, auth_old_result_cb, NULL);
jabber_iq_send(iq);
- } else if(xmlnode_get_child(query, "password")) {
+ } else if(purple_xmlnode_get_child(query, "password")) {
PurpleAccount *account = purple_connection_get_account(js->gc);
if(!jabber_stream_is_ssl(js) && !purple_account_get_bool(account,
"auth_plain_in_clear", FALSE)) {
@@ -313,7 +320,7 @@ static void auth_old_cb(JabberStream *js, const char *from,
_("Plaintext Authentication"),
msg,
1,
- account, NULL, NULL,
+ purple_request_cpar_from_account(account),
account, allow_plaintext_auth,
disallow_plaintext_auth);
g_free(msg);
@@ -321,7 +328,7 @@ static void auth_old_cb(JabberStream *js, const char *from,
}
finish_plaintext_authentication(js);
} else {
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
_("Server does not use any supported authentication method"));
return;
@@ -333,7 +340,7 @@ void jabber_auth_start_old(JabberStream *js)
{
PurpleAccount *account;
JabberIq *iq;
- xmlnode *query, *username;
+ PurpleXmlNode *query, *username;
account = purple_connection_get_account(js->gc);
@@ -345,7 +352,7 @@ void jabber_auth_start_old(JabberStream *js)
if (!jabber_stream_is_ssl(js) &&
g_str_equal("require_tls",
purple_account_get_string(account, "connection_security", JABBER_DEFAULT_REQUIRE_TLS))) {
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
_("You require encryption, but it is not available on this server."));
return;
@@ -372,16 +379,16 @@ void jabber_auth_start_old(JabberStream *js)
* password prompting here
*/
- if (!purple_account_get_password(account)) {
+ if (!purple_connection_get_password(js->gc)) {
purple_account_request_password(account, G_CALLBACK(auth_old_pass_cb), G_CALLBACK(auth_no_pass_cb), js->gc);
return;
}
#endif
iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:auth");
- query = xmlnode_get_child(iq->node, "query");
- username = xmlnode_new_child(query, "username");
- xmlnode_insert_data(username, js->user->node, -1);
+ query = purple_xmlnode_get_child(iq->node, "query");
+ username = purple_xmlnode_new_child(query, "username");
+ purple_xmlnode_insert_data(username, js->user->node, -1);
jabber_iq_set_callback(iq, auth_old_cb, NULL);
@@ -389,28 +396,28 @@ void jabber_auth_start_old(JabberStream *js)
}
void
-jabber_auth_handle_challenge(JabberStream *js, xmlnode *packet)
+jabber_auth_handle_challenge(JabberStream *js, PurpleXmlNode *packet)
{
- const char *ns = xmlnode_get_namespace(packet);
+ const char *ns = purple_xmlnode_get_namespace(packet);
if (!purple_strequal(ns, NS_XMPP_SASL)) {
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Invalid response from server"));
return;
}
if (js->auth_mech && js->auth_mech->handle_challenge) {
- xmlnode *response = NULL;
+ PurpleXmlNode *response = NULL;
char *msg = NULL;
JabberSaslState state = js->auth_mech->handle_challenge(js, packet, &response, &msg);
if (state == JABBER_SASL_STATE_FAIL) {
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
msg ? msg : _("Invalid challenge from server"));
} else if (response) {
jabber_send(js, response);
- xmlnode_free(response);
+ purple_xmlnode_free(response);
}
g_free(msg);
@@ -418,12 +425,12 @@ jabber_auth_handle_challenge(JabberStream *js, xmlnode *packet)
purple_debug_warning("jabber", "Received unexpected (and unhandled) <challenge/>\n");
}
-void jabber_auth_handle_success(JabberStream *js, xmlnode *packet)
+void jabber_auth_handle_success(JabberStream *js, PurpleXmlNode *packet)
{
- const char *ns = xmlnode_get_namespace(packet);
+ const char *ns = purple_xmlnode_get_namespace(packet);
if (!purple_strequal(ns, NS_XMPP_SASL)) {
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Invalid response from server"));
return;
@@ -434,12 +441,12 @@ void jabber_auth_handle_success(JabberStream *js, xmlnode *packet)
JabberSaslState state = js->auth_mech->handle_success(js, packet, &msg);
if (state == JABBER_SASL_STATE_FAIL) {
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
msg ? msg : _("Invalid response from server"));
return;
} else if (state == JABBER_SASL_STATE_CONTINUE) {
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
msg ? msg : _("Server thinks authentication is complete, but client does not"));
return;
@@ -456,19 +463,19 @@ void jabber_auth_handle_success(JabberStream *js, xmlnode *packet)
jabber_stream_set_state(js, JABBER_STREAM_POST_AUTH);
}
-void jabber_auth_handle_failure(JabberStream *js, xmlnode *packet)
+void jabber_auth_handle_failure(JabberStream *js, PurpleXmlNode *packet)
{
PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
char *msg = NULL;
if (js->auth_mech && js->auth_mech->handle_failure) {
- xmlnode *stanza = NULL;
+ PurpleXmlNode *stanza = NULL;
JabberSaslState state = js->auth_mech->handle_failure(js, packet, &stanza, &msg);
if (state != JABBER_SASL_STATE_FAIL) {
if (stanza) {
jabber_send(js, stanza);
- xmlnode_free(stanza);
+ purple_xmlnode_free(stanza);
}
return;
@@ -479,11 +486,11 @@ void jabber_auth_handle_failure(JabberStream *js, xmlnode *packet)
msg = jabber_parse_error(js, packet, &reason);
if (!msg) {
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Invalid response from server"));
} else {
- purple_connection_error_reason(js->gc, reason, msg);
+ purple_connection_error(js->gc, reason, msg);
g_free(msg);
}
}
diff --git a/libpurple/protocols/jabber/auth.h b/libpurple/protocols/jabber/auth.h
index b226fb5733..946d3a3b1b 100644
--- a/libpurple/protocols/jabber/auth.h
+++ b/libpurple/protocols/jabber/auth.h
@@ -38,18 +38,18 @@ typedef enum {
struct _JabberSaslMech {
gint8 priority; /* Higher priority will be tried before lower priority */
const gchar *name;
- JabberSaslState (*start)(JabberStream *js, xmlnode *mechanisms, xmlnode **reply, char **msg);
- JabberSaslState (*handle_challenge)(JabberStream *js, xmlnode *packet, xmlnode **reply, char **msg);
- JabberSaslState (*handle_success)(JabberStream *js, xmlnode *packet, char **msg);
- JabberSaslState (*handle_failure)(JabberStream *js, xmlnode *packet, xmlnode **reply, char **msg);
+ JabberSaslState (*start)(JabberStream *js, PurpleXmlNode *mechanisms, PurpleXmlNode **reply, char **msg);
+ JabberSaslState (*handle_challenge)(JabberStream *js, PurpleXmlNode *packet, PurpleXmlNode **reply, char **msg);
+ JabberSaslState (*handle_success)(JabberStream *js, PurpleXmlNode *packet, char **msg);
+ JabberSaslState (*handle_failure)(JabberStream *js, PurpleXmlNode *packet, PurpleXmlNode **reply, char **msg);
void (*dispose)(JabberStream *js);
};
-void jabber_auth_start(JabberStream *js, xmlnode *packet);
+void jabber_auth_start(JabberStream *js, PurpleXmlNode *packet);
void jabber_auth_start_old(JabberStream *js);
-void jabber_auth_handle_challenge(JabberStream *js, xmlnode *packet);
-void jabber_auth_handle_success(JabberStream *js, xmlnode *packet);
-void jabber_auth_handle_failure(JabberStream *js, xmlnode *packet);
+void jabber_auth_handle_challenge(JabberStream *js, PurpleXmlNode *packet);
+void jabber_auth_handle_success(JabberStream *js, PurpleXmlNode *packet);
+void jabber_auth_handle_failure(JabberStream *js, PurpleXmlNode *packet);
JabberSaslMech *jabber_auth_get_plain_mech(void);
JabberSaslMech *jabber_auth_get_digest_md5_mech(void);
diff --git a/libpurple/protocols/jabber/auth_cyrus.c b/libpurple/protocols/jabber/auth_cyrus.c
index 0c4278c656..611025a42d 100644
--- a/libpurple/protocols/jabber/auth_cyrus.c
+++ b/libpurple/protocols/jabber/auth_cyrus.c
@@ -28,13 +28,13 @@
#include "auth.h"
#include "jabber.h"
-static JabberSaslState jabber_auth_start_cyrus(JabberStream *js, xmlnode **reply,
+static JabberSaslState jabber_auth_start_cyrus(JabberStream *js, PurpleXmlNode **reply,
char **error);
static void jabber_sasl_build_callbacks(JabberStream *);
static void disallow_plaintext_auth(PurpleAccount *account)
{
- purple_connection_error_reason(purple_account_get_connection(account),
+ purple_connection_error(purple_account_get_connection(account),
PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
_("Server may require plaintext authentication over an unencrypted stream"));
}
@@ -42,17 +42,17 @@ static void disallow_plaintext_auth(PurpleAccount *account)
static void start_cyrus_wrapper(JabberStream *js)
{
char *error = NULL;
- xmlnode *response = NULL;
+ PurpleXmlNode *response = NULL;
JabberSaslState state = jabber_auth_start_cyrus(js, &response, &error);
if (state == JABBER_SASL_STATE_FAIL) {
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
error);
g_free(error);
} else if (response) {
jabber_send(js, response);
- xmlnode_free(response);
+ purple_xmlnode_free(response);
}
}
@@ -91,12 +91,10 @@ static int jabber_sasl_cb_simple(void *ctx, int id, const char **res, unsigned *
static int jabber_sasl_cb_secret(sasl_conn_t *conn, void *ctx, int id, sasl_secret_t **secret)
{
JabberStream *js = ctx;
- PurpleAccount *account;
const char *pw;
size_t len;
- account = purple_connection_get_account(js->gc);
- pw = purple_account_get_password(account);
+ pw = purple_connection_get_password(js->gc);
if (!conn || !secret || id != SASL_CB_PASS)
return SASL_BADPARAM;
@@ -135,9 +133,8 @@ static void auth_pass_cb(PurpleConnection *gc, PurpleRequestFields *fields)
const char *entry;
gboolean remember;
- /* The password prompt dialog doesn't get disposed if the account disconnects */
- if (!PURPLE_CONNECTION_IS_VALID(gc))
- return;
+ /* TODO: the password prompt dialog doesn't get disposed if the account disconnects */
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
account = purple_connection_get_account(gc);
js = purple_connection_get_protocol_data(gc);
@@ -147,14 +144,16 @@ static void auth_pass_cb(PurpleConnection *gc, PurpleRequestFields *fields)
if (!entry || !*entry)
{
- purple_notify_error(account, NULL, _("Password is required to sign on."), NULL);
+ purple_notify_error(account, NULL,
+ _("Password is required to sign on."), NULL,
+ purple_request_cpar_from_connection(gc));
return;
}
if (remember)
purple_account_set_remember_password(account, TRUE);
- purple_account_set_password(account, entry);
+ purple_account_set_password(account, entry, NULL, NULL);
/* Rebuild our callbacks as we now have a password to offer */
jabber_sasl_build_callbacks(js);
@@ -168,9 +167,8 @@ auth_no_pass_cb(PurpleConnection *gc, PurpleRequestFields *fields)
{
PurpleAccount *account;
- /* The password prompt dialog doesn't get disposed if the account disconnects */
- if (!PURPLE_CONNECTION_IS_VALID(gc))
- return;
+ /* TODO: the password prompt dialog doesn't get disposed if the account disconnects */
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
account = purple_connection_get_account(gc);
@@ -181,7 +179,7 @@ auth_no_pass_cb(PurpleConnection *gc, PurpleRequestFields *fields)
static gboolean remove_current_mech(JabberStream *js) {
char *pos;
if ((pos = strstr(js->sasl_mechs->str, js->current_mech))) {
- int len = strlen(js->current_mech);
+ size_t len = strlen(js->current_mech);
/* Clean up space that separated this Mech from the one before or after it */
if (pos > js->sasl_mechs->str && *(pos - 1) == ' ') {
/* Handle removing space before when current_mech isn't the first mech in the list */
@@ -198,7 +196,7 @@ static gboolean remove_current_mech(JabberStream *js) {
}
static JabberSaslState
-jabber_auth_start_cyrus(JabberStream *js, xmlnode **reply, char **error)
+jabber_auth_start_cyrus(JabberStream *js, PurpleXmlNode **reply, char **error)
{
PurpleAccount *account;
const char *clientout = NULL;
@@ -249,7 +247,7 @@ jabber_auth_start_cyrus(JabberStream *js, xmlnode **reply, char **error)
* to get one
*/
- if (!purple_account_get_password(account)) {
+ if (!purple_connection_get_password(js->gc)) {
purple_account_request_password(account, G_CALLBACK(auth_pass_cb), G_CALLBACK(auth_no_pass_cb), js->gc);
return JABBER_SASL_STATE_CONTINUE;
@@ -264,7 +262,7 @@ jabber_auth_start_cyrus(JabberStream *js, xmlnode **reply, char **error)
purple_request_yes_no(js->gc, _("Plaintext Authentication"),
_("Plaintext Authentication"),
msg,
- 1, account, NULL, NULL, account,
+ 1, purple_request_cpar_from_account(account), account,
allow_cyrus_plaintext_auth,
disallow_plaintext_auth);
g_free(msg);
@@ -328,19 +326,19 @@ jabber_auth_start_cyrus(JabberStream *js, xmlnode **reply, char **error)
} while (again);
if (js->sasl_state == SASL_CONTINUE || js->sasl_state == SASL_OK) {
- xmlnode *auth = xmlnode_new("auth");
- xmlnode_set_namespace(auth, NS_XMPP_SASL);
- xmlnode_set_attrib(auth, "mechanism", js->current_mech);
+ PurpleXmlNode *auth = purple_xmlnode_new("auth");
+ purple_xmlnode_set_namespace(auth, NS_XMPP_SASL);
+ purple_xmlnode_set_attrib(auth, "mechanism", js->current_mech);
- xmlnode_set_attrib(auth, "xmlns:ga", "http://www.google.com/talk/protocol/auth");
- xmlnode_set_attrib(auth, "ga:client-uses-full-bind-result", "true");
+ purple_xmlnode_set_attrib(auth, "xmlns:ga", "http://www.google.com/talk/protocol/auth");
+ purple_xmlnode_set_attrib(auth, "ga:client-uses-full-bind-result", "true");
if (clientout) {
if (coutlen == 0) {
- xmlnode_insert_data(auth, "=", -1);
+ purple_xmlnode_insert_data(auth, "=", -1);
} else {
enc_out = purple_base64_encode((unsigned char*)clientout, coutlen);
- xmlnode_insert_data(auth, enc_out, -1);
+ purple_xmlnode_insert_data(auth, enc_out, -1);
g_free(enc_out);
}
}
@@ -364,7 +362,6 @@ jabber_sasl_cb_log(void *context, int level, const char *message)
static void
jabber_sasl_build_callbacks(JabberStream *js)
{
- PurpleAccount *account;
int id;
/* Set up our callbacks structure */
@@ -373,30 +370,29 @@ jabber_sasl_build_callbacks(JabberStream *js)
id = 0;
js->sasl_cb[id].id = SASL_CB_GETREALM;
- js->sasl_cb[id].proc = jabber_sasl_cb_realm;
+ js->sasl_cb[id].proc = (void *)jabber_sasl_cb_realm;
js->sasl_cb[id].context = (void *)js;
id++;
js->sasl_cb[id].id = SASL_CB_AUTHNAME;
- js->sasl_cb[id].proc = jabber_sasl_cb_simple;
+ js->sasl_cb[id].proc = (void *)jabber_sasl_cb_simple;
js->sasl_cb[id].context = (void *)js;
id++;
js->sasl_cb[id].id = SASL_CB_USER;
- js->sasl_cb[id].proc = jabber_sasl_cb_simple;
+ js->sasl_cb[id].proc = (void *)jabber_sasl_cb_simple;
js->sasl_cb[id].context = (void *)js;
id++;
- account = purple_connection_get_account(js->gc);
- if (purple_account_get_password(account) != NULL ) {
+ if (purple_connection_get_password(js->gc) != NULL) {
js->sasl_cb[id].id = SASL_CB_PASS;
- js->sasl_cb[id].proc = jabber_sasl_cb_secret;
+ js->sasl_cb[id].proc = (void *)jabber_sasl_cb_secret;
js->sasl_cb[id].context = (void *)js;
id++;
}
js->sasl_cb[id].id = SASL_CB_LOG;
- js->sasl_cb[id].proc = jabber_sasl_cb_log;
+ js->sasl_cb[id].proc = (void *)jabber_sasl_cb_log;
js->sasl_cb[id].context = (void*)js;
id++;
@@ -404,18 +400,18 @@ jabber_sasl_build_callbacks(JabberStream *js)
}
static JabberSaslState
-jabber_cyrus_start(JabberStream *js, xmlnode *mechanisms,
- xmlnode **reply, char **error)
+jabber_cyrus_start(JabberStream *js, PurpleXmlNode *mechanisms,
+ PurpleXmlNode **reply, char **error)
{
- xmlnode *mechnode;
+ PurpleXmlNode *mechnode;
JabberSaslState ret;
js->sasl_mechs = g_string_new("");
- for(mechnode = xmlnode_get_child(mechanisms, "mechanism"); mechnode;
- mechnode = xmlnode_get_next_twin(mechnode))
+ for(mechnode = purple_xmlnode_get_child(mechanisms, "mechanism"); mechnode;
+ mechnode = purple_xmlnode_get_next_twin(mechnode))
{
- char *mech_name = xmlnode_get_data(mechnode);
+ char *mech_name = purple_xmlnode_get_data(mechnode);
/* Ignore blank mechanisms and EXTERNAL. External isn't
* supported, and Cyrus SASL's mechanism returns
@@ -452,10 +448,10 @@ jabber_cyrus_start(JabberStream *js, xmlnode *mechanisms,
}
static JabberSaslState
-jabber_cyrus_handle_challenge(JabberStream *js, xmlnode *packet,
- xmlnode **reply, char **error)
+jabber_cyrus_handle_challenge(JabberStream *js, PurpleXmlNode *packet,
+ PurpleXmlNode **reply, char **error)
{
- char *enc_in = xmlnode_get_data(packet);
+ char *enc_in = purple_xmlnode_get_data(packet);
unsigned char *dec_in;
char *enc_out;
const char *c_out;
@@ -476,8 +472,8 @@ jabber_cyrus_handle_challenge(JabberStream *js, xmlnode *packet,
*error = tmp;
return JABBER_SASL_STATE_FAIL;
} else {
- xmlnode *response = xmlnode_new("response");
- xmlnode_set_namespace(response, NS_XMPP_SASL);
+ PurpleXmlNode *response = purple_xmlnode_new("response");
+ purple_xmlnode_set_namespace(response, NS_XMPP_SASL);
if (clen > 0) {
/* Cyrus SASL 2.1.22 appears to contain code to add the charset
* to the response for DIGEST-MD5 but there is no possibility
@@ -497,7 +493,7 @@ jabber_cyrus_handle_challenge(JabberStream *js, xmlnode *packet,
g_free(tmp);
}
- xmlnode_insert_data(response, enc_out, -1);
+ purple_xmlnode_insert_data(response, enc_out, -1);
g_free(enc_out);
}
@@ -507,7 +503,7 @@ jabber_cyrus_handle_challenge(JabberStream *js, xmlnode *packet,
}
static JabberSaslState
-jabber_cyrus_handle_success(JabberStream *js, xmlnode *packet,
+jabber_cyrus_handle_success(JabberStream *js, PurpleXmlNode *packet,
char **error)
{
const void *x;
@@ -516,7 +512,7 @@ jabber_cyrus_handle_success(JabberStream *js, xmlnode *packet,
* should try one more round against it
*/
if (js->sasl_state != SASL_OK) {
- char *enc_in = xmlnode_get_data(packet);
+ char *enc_in = purple_xmlnode_get_data(packet);
unsigned char *dec_in = NULL;
const char *c_out;
unsigned int clen;
@@ -553,8 +549,8 @@ jabber_cyrus_handle_success(JabberStream *js, xmlnode *packet,
}
static JabberSaslState
-jabber_cyrus_handle_failure(JabberStream *js, xmlnode *packet,
- xmlnode **reply, char **error)
+jabber_cyrus_handle_failure(JabberStream *js, PurpleXmlNode *packet,
+ PurpleXmlNode **reply, char **error)
{
if (js->auth_fail_count++ < 5) {
if (js->current_mech && *js->current_mech) {
diff --git a/libpurple/protocols/jabber/auth_digest_md5.c b/libpurple/protocols/jabber/auth_digest_md5.c
index b4ee7f8fda..3de3fc7d15 100644
--- a/libpurple/protocols/jabber/auth_digest_md5.c
+++ b/libpurple/protocols/jabber/auth_digest_md5.c
@@ -23,7 +23,7 @@
#include "internal.h"
#include "debug.h"
-#include "cipher.h"
+#include "ciphers/md5hash.h"
#include "util.h"
#include "xmlnode.h"
@@ -32,12 +32,12 @@
#include "jabber.h"
static JabberSaslState
-digest_md5_start(JabberStream *js, xmlnode *packet, xmlnode **response,
+digest_md5_start(JabberStream *js, PurpleXmlNode *packet, PurpleXmlNode **response,
char **error)
{
- xmlnode *auth = xmlnode_new("auth");
- xmlnode_set_namespace(auth, NS_XMPP_SASL);
- xmlnode_set_attrib(auth, "mechanism", "DIGEST-MD5");
+ PurpleXmlNode *auth = purple_xmlnode_new("auth");
+ purple_xmlnode_set_namespace(auth, NS_XMPP_SASL);
+ purple_xmlnode_set_attrib(auth, "mechanism", "DIGEST-MD5");
*response = auth;
return JABBER_SASL_STATE_CONTINUE;
@@ -106,8 +106,7 @@ static char *
generate_response_value(JabberID *jid, const char *passwd, const char *nonce,
const char *cnonce, const char *a2, const char *realm)
{
- PurpleCipher *cipher;
- PurpleCipherContext *context;
+ PurpleHash *hash;
guchar result[16];
size_t a1len;
@@ -122,35 +121,34 @@ generate_response_value(JabberID *jid, const char *passwd, const char *nonce,
convpasswd = g_strdup(passwd);
}
- cipher = purple_ciphers_find_cipher("md5");
- context = purple_cipher_context_new(cipher, NULL);
+ hash = purple_md5_hash_new();
x = g_strdup_printf("%s:%s:%s", convnode, realm, convpasswd ? convpasswd : "");
- purple_cipher_context_append(context, (const guchar *)x, strlen(x));
- purple_cipher_context_digest(context, sizeof(result), result, NULL);
+ purple_hash_append(hash, (const guchar *)x, strlen(x));
+ purple_hash_digest(hash, result, sizeof(result));
a1 = g_strdup_printf("xxxxxxxxxxxxxxxx:%s:%s", nonce, cnonce);
a1len = strlen(a1);
g_memmove(a1, result, 16);
- purple_cipher_context_reset(context, NULL);
- purple_cipher_context_append(context, (const guchar *)a1, a1len);
- purple_cipher_context_digest(context, sizeof(result), result, NULL);
+ purple_hash_reset(hash);
+ purple_hash_append(hash, (const guchar *)a1, a1len);
+ purple_hash_digest(hash, result, sizeof(result));
ha1 = purple_base16_encode(result, 16);
- purple_cipher_context_reset(context, NULL);
- purple_cipher_context_append(context, (const guchar *)a2, strlen(a2));
- purple_cipher_context_digest(context, sizeof(result), result, NULL);
+ purple_hash_reset(hash);
+ purple_hash_append(hash, (const guchar *)a2, strlen(a2));
+ purple_hash_digest(hash, result, sizeof(result));
ha2 = purple_base16_encode(result, 16);
kd = g_strdup_printf("%s:%s:00000001:%s:auth:%s", ha1, nonce, cnonce, ha2);
- purple_cipher_context_reset(context, NULL);
- purple_cipher_context_append(context, (const guchar *)kd, strlen(kd));
- purple_cipher_context_digest(context, sizeof(result), result, NULL);
- purple_cipher_context_destroy(context);
+ purple_hash_reset(hash);
+ purple_hash_append(hash, (const guchar *)kd, strlen(kd));
+ purple_hash_digest(hash, result, sizeof(result));
+ g_object_unref(hash);
z = purple_base16_encode(result, 16);
@@ -166,11 +164,11 @@ generate_response_value(JabberID *jid, const char *passwd, const char *nonce,
}
static JabberSaslState
-digest_md5_handle_challenge(JabberStream *js, xmlnode *packet,
- xmlnode **response, char **msg)
+digest_md5_handle_challenge(JabberStream *js, PurpleXmlNode *packet,
+ PurpleXmlNode **response, char **msg)
{
- xmlnode *reply = NULL;
- char *enc_in = xmlnode_get_data(packet);
+ PurpleXmlNode *reply = NULL;
+ char *enc_in = purple_xmlnode_get_data(packet);
char *dec_in;
char *enc_out;
GHashTable *parts;
@@ -194,8 +192,8 @@ digest_md5_handle_challenge(JabberStream *js, xmlnode *packet,
char *expected_rspauth = js->auth_mech_data;
if (rspauth && purple_strequal(rspauth, expected_rspauth)) {
- reply = xmlnode_new("response");
- xmlnode_set_namespace(reply, NS_XMPP_SASL);
+ reply = purple_xmlnode_new("response");
+ purple_xmlnode_set_namespace(reply, NS_XMPP_SASL);
} else {
*msg = g_strdup(_("Invalid challenge from server"));
state = JABBER_SASL_STATE_FAIL;
@@ -262,9 +260,9 @@ digest_md5_handle_challenge(JabberStream *js, xmlnode *packet,
G_GSIZE_FORMAT "): %s\n",
response->len, response->str);
- reply = xmlnode_new("response");
- xmlnode_set_namespace(reply, NS_XMPP_SASL);
- xmlnode_insert_data(reply, enc_out, -1);
+ reply = purple_xmlnode_new("response");
+ purple_xmlnode_set_namespace(reply, NS_XMPP_SASL);
+ purple_xmlnode_insert_data(reply, enc_out, -1);
g_free(enc_out);
diff --git a/libpurple/protocols/jabber/auth_plain.c b/libpurple/protocols/jabber/auth_plain.c
index 402b0c285a..1df6b77837 100644
--- a/libpurple/protocols/jabber/auth_plain.c
+++ b/libpurple/protocols/jabber/auth_plain.c
@@ -31,17 +31,17 @@
#include "jabber.h"
#include "auth.h"
-static xmlnode *finish_plaintext_authentication(JabberStream *js)
+static PurpleXmlNode *finish_plaintext_authentication(JabberStream *js)
{
- xmlnode *auth;
+ PurpleXmlNode *auth;
GString *response;
gchar *enc_out;
- auth = xmlnode_new("auth");
- xmlnode_set_namespace(auth, NS_XMPP_SASL);
+ auth = purple_xmlnode_new("auth");
+ purple_xmlnode_set_namespace(auth, NS_XMPP_SASL);
- xmlnode_set_attrib(auth, "xmlns:ga", "http://www.google.com/talk/protocol/auth");
- xmlnode_set_attrib(auth, "ga:client-uses-full-bind-result", "true");
+ purple_xmlnode_set_attrib(auth, "xmlns:ga", "http://www.google.com/talk/protocol/auth");
+ purple_xmlnode_set_attrib(auth, "ga:client-uses-full-bind-result", "true");
response = g_string_new("");
response = g_string_append_c(response, '\0');
@@ -52,8 +52,8 @@ static xmlnode *finish_plaintext_authentication(JabberStream *js)
enc_out = purple_base64_encode((guchar *)response->str, response->len);
- xmlnode_set_attrib(auth, "mechanism", "PLAIN");
- xmlnode_insert_data(auth, enc_out, -1);
+ purple_xmlnode_set_attrib(auth, "mechanism", "PLAIN");
+ purple_xmlnode_insert_data(auth, enc_out, -1);
g_free(enc_out);
g_string_free(response, TRUE);
@@ -64,24 +64,24 @@ static void allow_plaintext_auth(PurpleAccount *account)
{
PurpleConnection *gc = purple_account_get_connection(account);
JabberStream *js = purple_connection_get_protocol_data(gc);
- xmlnode *response;
+ PurpleXmlNode *response;
purple_account_set_bool(account, "auth_plain_in_clear", TRUE);
response = finish_plaintext_authentication(js);
jabber_send(js, response);
- xmlnode_free(response);
+ purple_xmlnode_free(response);
}
static void disallow_plaintext_auth(PurpleAccount *account)
{
- purple_connection_error_reason(purple_account_get_connection(account),
+ purple_connection_error(purple_account_get_connection(account),
PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
_("Server requires plaintext authentication over an unencrypted stream"));
}
static JabberSaslState
-jabber_plain_start(JabberStream *js, xmlnode *packet, xmlnode **response, char **error)
+jabber_plain_start(JabberStream *js, PurpleXmlNode *packet, PurpleXmlNode **response, char **error)
{
PurpleAccount *account = purple_connection_get_account(js->gc);
char *msg;
@@ -97,7 +97,7 @@ jabber_plain_start(JabberStream *js, xmlnode *packet, xmlnode **response, char *
_("Plaintext Authentication"),
msg,
1,
- account, NULL, NULL,
+ purple_request_cpar_from_account(account),
account, allow_plaintext_auth, disallow_plaintext_auth);
g_free(msg);
return JABBER_SASL_STATE_CONTINUE;
diff --git a/libpurple/protocols/jabber/auth_scram.c b/libpurple/protocols/jabber/auth_scram.c
index a971f098c1..2eac86bd89 100644
--- a/libpurple/protocols/jabber/auth_scram.c
+++ b/libpurple/protocols/jabber/auth_scram.c
@@ -25,16 +25,17 @@
#include "auth.h"
#include "auth_scram.h"
-#include "cipher.h"
+#include "ciphers/hmaccipher.h"
+#include "ciphers/sha1hash.h"
#include "debug.h"
static const JabberScramHash hashes[] = {
- { "-SHA-1", "sha1", 20 },
+ { "-SHA-1", purple_sha1_hash_new, 20 },
};
static const JabberScramHash *mech_to_hash(const char *mech)
{
- int i;
+ gsize i;
g_return_val_if_fail(mech != NULL && *mech != '\0', NULL);
@@ -76,7 +77,8 @@ static const struct {
guchar *jabber_scram_hi(const JabberScramHash *hash, const GString *str,
GString *salt, guint iterations)
{
- PurpleCipherContext *context;
+ PurpleHash *hasher;
+ PurpleCipher *cipher;
guchar *result;
guint i;
guchar *prev, *tmp;
@@ -90,27 +92,28 @@ guchar *jabber_scram_hi(const JabberScramHash *hash, const GString *str,
tmp = g_new0(guint8, hash->size);
result = g_new0(guint8, hash->size);
- context = purple_cipher_context_new_by_name("hmac", NULL);
+ hasher = hash->new_cipher();
+ cipher = purple_hmac_cipher_new(hasher);
+ g_object_unref(G_OBJECT(hasher));
/* Append INT(1), a four-octet encoding of the integer 1, most significant
* octet first. */
g_string_append_len(salt, "\0\0\0\1", 4);
/* Compute U0 */
- purple_cipher_context_set_option(context, "hash", (gpointer)hash->name);
- purple_cipher_context_set_key_with_len(context, (guchar *)str->str, str->len);
- purple_cipher_context_append(context, (guchar *)salt->str, salt->len);
- purple_cipher_context_digest(context, hash->size, result, NULL);
+ purple_cipher_set_key(cipher, (guchar *)str->str, str->len);
+ purple_cipher_append(cipher, (guchar *)salt->str, salt->len);
+ purple_cipher_digest(cipher, result, hash->size);
memcpy(prev, result, hash->size);
/* Compute U1...Ui */
for (i = 1; i < iterations; ++i) {
guint j;
- purple_cipher_context_set_option(context, "hash", (gpointer)hash->name);
- purple_cipher_context_set_key_with_len(context, (guchar *)str->str, str->len);
- purple_cipher_context_append(context, prev, hash->size);
- purple_cipher_context_digest(context, hash->size, tmp, NULL);
+ purple_cipher_reset(cipher);
+ purple_cipher_set_key(cipher, (guchar *)str->str, str->len);
+ purple_cipher_append(cipher, prev, hash->size);
+ purple_cipher_digest(cipher, tmp, hash->size);
for (j = 0; j < hash->size; ++j)
result[j] ^= tmp[j];
@@ -118,7 +121,7 @@ guchar *jabber_scram_hi(const JabberScramHash *hash, const GString *str,
memcpy(prev, tmp, hash->size);
}
- purple_cipher_context_destroy(context);
+ g_object_unref(G_OBJECT(cipher));
g_free(tmp);
g_free(prev);
return result;
@@ -136,25 +139,27 @@ guchar *jabber_scram_hi(const JabberScramHash *hash, const GString *str,
static void
hmac(const JabberScramHash *hash, guchar *out, const guchar *key, const gchar *str)
{
- PurpleCipherContext *context;
-
- context = purple_cipher_context_new_by_name("hmac", NULL);
- purple_cipher_context_set_option(context, "hash", (gpointer)hash->name);
- purple_cipher_context_set_key_with_len(context, key, hash->size);
- purple_cipher_context_append(context, (guchar *)str, strlen(str));
- purple_cipher_context_digest(context, hash->size, out, NULL);
- purple_cipher_context_destroy(context);
+ PurpleHash *hasher;
+ PurpleCipher *cipher;
+
+ hasher = hash->new_cipher();
+ cipher = purple_hmac_cipher_new(hasher);
+ g_object_unref(G_OBJECT(hasher));
+ purple_cipher_set_key(cipher, key, hash->size);
+ purple_cipher_append(cipher, (guchar *)str, strlen(str));
+ purple_cipher_digest(cipher, out, hash->size);
+ g_object_unref(G_OBJECT(cipher));
}
static void
hash(const JabberScramHash *hash, guchar *out, const guchar *data)
{
- PurpleCipherContext *context;
+ PurpleHash *hasher;
- context = purple_cipher_context_new_by_name(hash->name, NULL);
- purple_cipher_context_append(context, data, hash->size);
- purple_cipher_context_digest(context, hash->size, out, NULL);
- purple_cipher_context_destroy(context);
+ hasher = hash->new_cipher();
+ purple_hash_append(hasher, data, hash->size);
+ purple_hash_digest(hasher, out, hash->size);
+ g_object_unref(G_OBJECT(hasher));
}
gboolean
@@ -168,11 +173,6 @@ jabber_scram_calc_proofs(JabberScramData *data, GString *salt, guint iterations)
guchar *salted_password;
guchar *client_key, *stored_key, *client_signature, *server_key;
- client_key = g_new0(guchar, hash_len);
- stored_key = g_new0(guchar, hash_len);
- client_signature = g_new0(guchar, hash_len);
- server_key = g_new0(guchar, hash_len);
-
data->client_proof = g_string_sized_new(hash_len);
data->client_proof->len = hash_len;
data->server_signature = g_string_sized_new(hash_len);
@@ -186,6 +186,11 @@ jabber_scram_calc_proofs(JabberScramData *data, GString *salt, guint iterations)
if (!salted_password)
return FALSE;
+ client_key = g_new0(guchar, hash_len);
+ stored_key = g_new0(guchar, hash_len);
+ client_signature = g_new0(guchar, hash_len);
+ server_key = g_new0(guchar, hash_len);
+
/* client_key = HMAC(salted_password, "Client Key") */
hmac(data->hash, client_key, salted_password, "Client Key");
/* server_key = HMAC(salted_password, "Server Key") */
@@ -381,9 +386,9 @@ static gchar *escape_username(const gchar *in)
}
static JabberSaslState
-scram_start(JabberStream *js, xmlnode *mechanisms, xmlnode **out, char **error)
+scram_start(JabberStream *js, PurpleXmlNode *mechanisms, PurpleXmlNode **out, char **error)
{
- xmlnode *reply;
+ PurpleXmlNode *reply;
JabberScramData *data;
guint64 cnonce;
#ifdef CHANNEL_BINDING
@@ -428,16 +433,16 @@ scram_start(JabberStream *js, xmlnode *mechanisms, xmlnode **out, char **error)
data->step = 1;
- reply = xmlnode_new("auth");
- xmlnode_set_namespace(reply, NS_XMPP_SASL);
- xmlnode_set_attrib(reply, "mechanism", js->auth_mech->name);
+ reply = purple_xmlnode_new("auth");
+ purple_xmlnode_set_namespace(reply, NS_XMPP_SASL);
+ purple_xmlnode_set_attrib(reply, "mechanism", js->auth_mech->name);
/* TODO: Channel binding */
dec_out = g_strdup_printf("%c,,%s", 'n', data->auth_message->str);
enc_out = purple_base64_encode((guchar *)dec_out, strlen(dec_out));
purple_debug_misc("jabber", "initial SCRAM message '%s'\n", dec_out);
- xmlnode_insert_data(reply, enc_out, -1);
+ purple_xmlnode_insert_data(reply, enc_out, -1);
g_free(enc_out);
g_free(dec_out);
@@ -447,19 +452,19 @@ scram_start(JabberStream *js, xmlnode *mechanisms, xmlnode **out, char **error)
}
static JabberSaslState
-scram_handle_challenge(JabberStream *js, xmlnode *challenge, xmlnode **out, char **error)
+scram_handle_challenge(JabberStream *js, PurpleXmlNode *challenge, PurpleXmlNode **out, char **error)
{
JabberScramData *data = js->auth_mech_data;
- xmlnode *reply;
+ PurpleXmlNode *reply;
gchar *enc_in, *dec_in = NULL;
gchar *enc_out = NULL, *dec_out = NULL;
gsize len;
JabberSaslState state = JABBER_SASL_STATE_FAIL;
- enc_in = xmlnode_get_data(challenge);
+ enc_in = purple_xmlnode_get_data(challenge);
if (!enc_in || *enc_in == '\0') {
- reply = xmlnode_new("abort");
- xmlnode_set_namespace(reply, NS_XMPP_SASL);
+ reply = purple_xmlnode_new("abort");
+ purple_xmlnode_set_namespace(reply, NS_XMPP_SASL);
data->step = -1;
*error = g_strdup(_("Invalid challenge from server"));
goto out;
@@ -468,8 +473,8 @@ scram_handle_challenge(JabberStream *js, xmlnode *challenge, xmlnode **out, char
dec_in = (gchar *)purple_base64_decode(enc_in, &len);
if (!dec_in || len != strlen(dec_in)) {
/* Danger afoot; SCRAM shouldn't contain NUL bytes */
- reply = xmlnode_new("abort");
- xmlnode_set_namespace(reply, NS_XMPP_SASL);
+ reply = purple_xmlnode_new("abort");
+ purple_xmlnode_set_namespace(reply, NS_XMPP_SASL);
data->step = -1;
*error = g_strdup(_("Malicious challenge from server"));
goto out;
@@ -478,8 +483,8 @@ scram_handle_challenge(JabberStream *js, xmlnode *challenge, xmlnode **out, char
purple_debug_misc("jabber", "decoded challenge: %s\n", dec_in);
if (!jabber_scram_feed_parser(data, dec_in, &dec_out)) {
- reply = xmlnode_new("abort");
- xmlnode_set_namespace(reply, NS_XMPP_SASL);
+ reply = purple_xmlnode_new("abort");
+ purple_xmlnode_set_namespace(reply, NS_XMPP_SASL);
data->step = -1;
*error = g_strdup(_("Invalid challenge from server"));
goto out;
@@ -487,13 +492,13 @@ scram_handle_challenge(JabberStream *js, xmlnode *challenge, xmlnode **out, char
data->step += 1;
- reply = xmlnode_new("response");
- xmlnode_set_namespace(reply, NS_XMPP_SASL);
+ reply = purple_xmlnode_new("response");
+ purple_xmlnode_set_namespace(reply, NS_XMPP_SASL);
purple_debug_misc("jabber", "decoded response: %s\n", dec_out ? dec_out : "(null)");
if (dec_out) {
enc_out = purple_base64_encode((guchar *)dec_out, strlen(dec_out));
- xmlnode_insert_data(reply, enc_out, -1);
+ purple_xmlnode_insert_data(reply, enc_out, -1);
}
state = JABBER_SASL_STATE_CONTINUE;
@@ -509,14 +514,14 @@ out:
}
static JabberSaslState
-scram_handle_success(JabberStream *js, xmlnode *packet, char **error)
+scram_handle_success(JabberStream *js, PurpleXmlNode *packet, char **error)
{
JabberScramData *data = js->auth_mech_data;
char *enc_in, *dec_in;
char *dec_out = NULL;
gsize len;
- enc_in = xmlnode_get_data(packet);
+ enc_in = purple_xmlnode_get_data(packet);
if (data->step != 3 && (!enc_in || *enc_in == '\0')) {
*error = g_strdup(_("Invalid challenge from server"));
g_free(enc_in);
diff --git a/libpurple/protocols/jabber/auth_scram.h b/libpurple/protocols/jabber/auth_scram.h
index e1f52d1cbe..9073bb2b78 100644
--- a/libpurple/protocols/jabber/auth_scram.h
+++ b/libpurple/protocols/jabber/auth_scram.h
@@ -29,12 +29,14 @@
* DO NOT USE ANYTHING HERE OR YOU WILL BE SENT TO THE PIT OF DESPAIR.
*/
+#include "cipher.h"
+
/* Per-connection state stored between messages.
* This is stored in js->auth_data_mech.
*/
typedef struct {
const char *mech_substr;
- const char *name;
+ PurpleHash *(*new_cipher)(void);
guint size;
} JabberScramHash;
diff --git a/libpurple/protocols/jabber/bosh.c b/libpurple/protocols/jabber/bosh.c
index dd2c2b589d..5dd0b81eb0 100644
--- a/libpurple/protocols/jabber/bosh.c
+++ b/libpurple/protocols/jabber/bosh.c
@@ -17,1100 +17,501 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
*/
#include "internal.h"
-#include "circbuffer.h"
#include "core.h"
-#include "cipher.h"
#include "debug.h"
-#include "prpl.h"
-#include "util.h"
-#include "xmlnode.h"
+#include "http.h"
#include "bosh.h"
-/* The number of HTTP connections to use. This MUST be at least 2. */
-#define NUM_HTTP_CONNECTIONS 2
-/* How many failed connection attempts before it becomes a fatal error */
-#define MAX_FAILED_CONNECTIONS 3
-/* How long in seconds to queue up outgoing messages */
-#define BUFFER_SEND_IN_SECS 1
-
-typedef struct _PurpleHTTPConnection PurpleHTTPConnection;
+/*
+TODO: test, what happens, if the http server (BOSH server) doesn't support
+keep-alive (sends connection: close).
+*/
-typedef void (*PurpleBOSHConnectionConnectFunction)(PurpleBOSHConnection *conn);
-typedef void (*PurpleBOSHConnectionReceiveFunction)(PurpleBOSHConnection *conn, xmlnode *node);
+#define JABBER_BOSH_SEND_DELAY 250
-static char *bosh_useragent = NULL;
+#define JABBER_BOSH_TIMEOUT 10
-typedef enum {
- PACKET_NORMAL,
- PACKET_TERMINATE,
- PACKET_FLUSH,
-} PurpleBOSHPacketType;
+static gchar *jabber_bosh_useragent = NULL;
-struct _PurpleBOSHConnection {
+struct _PurpleJabberBOSHConnection {
JabberStream *js;
- PurpleHTTPConnection *connections[NUM_HTTP_CONNECTIONS];
-
- PurpleCircBuffer *pending;
- PurpleBOSHConnectionConnectFunction connect_cb;
- PurpleBOSHConnectionReceiveFunction receive_cb;
-
- /* Must be big enough to hold 2^53 - 1 */
- char *sid;
- guint64 rid;
+ PurpleHttpKeepalivePool *kapool;
+ PurpleHttpConnection *sc_req; /* Session Creation Request */
+ PurpleHttpConnectionSet *payload_reqs;
- /* decoded URL */
- char *host;
- char *path;
- guint16 port;
+ gchar *url;
+ gboolean is_ssl;
+ gboolean is_terminating;
- gboolean pipelining;
- gboolean ssl;
-
- enum {
- BOSH_CONN_OFFLINE,
- BOSH_CONN_BOOTING,
- BOSH_CONN_ONLINE
- } state;
- guint8 failed_connections;
-
- int wait;
-
- int max_requests;
- int requests;
+ gchar *sid;
+ guint64 rid; /* Must be big enough to hold 2^53 - 1 */
+ GString *send_buff;
guint send_timer;
};
-struct _PurpleHTTPConnection {
- PurpleBOSHConnection *bosh;
- PurpleSslConnection *psc;
-
- PurpleCircBuffer *write_buf;
- GString *read_buf;
-
- gsize handled_len;
- gsize body_len;
-
- int fd;
- guint readh;
- guint writeh;
-
- enum {
- HTTP_CONN_OFFLINE,
- HTTP_CONN_CONNECTING,
- HTTP_CONN_CONNECTED
- } state;
- int requests; /* number of outstanding HTTP requests */
-
- gboolean headers_done;
- gboolean close;
-};
-
+static PurpleHttpRequest *
+jabber_bosh_connection_http_request_new(PurpleJabberBOSHConnection *conn,
+ const GString *data);
static void
-debug_dump_http_connections(PurpleBOSHConnection *conn)
-{
- int i;
-
- g_return_if_fail(conn != NULL);
-
- for (i = 0; i < NUM_HTTP_CONNECTIONS; ++i) {
- PurpleHTTPConnection *httpconn = conn->connections[i];
- if (httpconn == NULL)
- purple_debug_misc("jabber", "BOSH %p->connections[%d] = (nil)\n",
- conn, i);
- else
- purple_debug_misc("jabber", "BOSH %p->connections[%d] = %p, state = %d"
- ", requests = %d\n", conn, i, httpconn,
- httpconn->state, httpconn->requests);
- }
-}
-
-static void http_connection_connect(PurpleHTTPConnection *conn);
-static void http_connection_send_request(PurpleHTTPConnection *conn,
- const GString *req);
-static gboolean send_timer_cb(gpointer data);
+jabber_bosh_connection_session_create(PurpleJabberBOSHConnection *conn);
+static void
+jabber_bosh_connection_send_now(PurpleJabberBOSHConnection *conn);
-void jabber_bosh_init(void)
+void
+jabber_bosh_init(void)
{
GHashTable *ui_info = purple_core_get_ui_info();
- const char *ui_name = NULL;
- const char *ui_version = NULL;
+ const gchar *ui_name = NULL;
+ const gchar *ui_version = NULL;
if (ui_info) {
ui_name = g_hash_table_lookup(ui_info, "name");
ui_version = g_hash_table_lookup(ui_info, "version");
}
- if (ui_name)
- bosh_useragent = g_strdup_printf("%s%s%s (libpurple " VERSION ")",
- ui_name, ui_version ? " " : "",
- ui_version ? ui_version : "");
- else
- bosh_useragent = g_strdup("libpurple " VERSION);
+ if (ui_name) {
+ jabber_bosh_useragent = g_strdup_printf(
+ "%s%s%s (libpurple " VERSION ")", ui_name,
+ ui_version ? " " : "", ui_version ? ui_version : "");
+ } else
+ jabber_bosh_useragent = g_strdup("libpurple " VERSION);
}
void jabber_bosh_uninit(void)
{
- g_free(bosh_useragent);
- bosh_useragent = NULL;
-}
-
-static PurpleHTTPConnection*
-jabber_bosh_http_connection_init(PurpleBOSHConnection *bosh)
-{
- PurpleHTTPConnection *conn = g_new0(PurpleHTTPConnection, 1);
- conn->bosh = bosh;
- conn->fd = -1;
- conn->state = HTTP_CONN_OFFLINE;
-
- conn->write_buf = purple_circ_buffer_new(0 /* default grow size */);
-
- return conn;
-}
-
-static void
-jabber_bosh_http_connection_destroy(PurpleHTTPConnection *conn)
-{
- if (conn->read_buf)
- g_string_free(conn->read_buf, TRUE);
-
- if (conn->write_buf)
- purple_circ_buffer_destroy(conn->write_buf);
- if (conn->readh)
- purple_input_remove(conn->readh);
- if (conn->writeh)
- purple_input_remove(conn->writeh);
- if (conn->psc)
- purple_ssl_close(conn->psc);
- if (conn->fd >= 0)
- close(conn->fd);
-
- purple_proxy_connect_cancel_with_handle(conn);
-
- g_free(conn);
+ g_free(jabber_bosh_useragent);
+ jabber_bosh_useragent = NULL;
}
-PurpleBOSHConnection*
-jabber_bosh_connection_init(JabberStream *js, const char *url)
+PurpleJabberBOSHConnection*
+jabber_bosh_connection_new(JabberStream *js, const gchar *url)
{
- PurpleBOSHConnection *conn;
- char *host, *path, *user, *passwd;
- int port;
+ PurpleJabberBOSHConnection *conn;
+ PurpleHttpURL *url_p;
- if (!purple_url_parse(url, &host, &port, &path, &user, &passwd)) {
- purple_debug_info("jabber", "Unable to parse given URL.\n");
+ url_p = purple_http_url_parse(url);
+ if (!url_p) {
+ purple_debug_error("jabber-bosh", "Unable to parse given URL.\n");
return NULL;
}
- conn = g_new0(PurpleBOSHConnection, 1);
- conn->host = host;
- conn->port = port;
- conn->path = g_strdup_printf("/%s", path);
- g_free(path);
- conn->pipelining = TRUE;
-
- if (purple_ip_address_is_valid(host))
- js->serverFQDN = g_strdup(js->user->domain);
- else
- js->serverFQDN = g_strdup(host);
-
- if ((user && user[0] != '\0') || (passwd && passwd[0] != '\0')) {
- purple_debug_info("jabber", "Ignoring unexpected username and password "
- "in BOSH URL.\n");
- }
-
- g_free(user);
- g_free(passwd);
-
+ conn = g_new0(PurpleJabberBOSHConnection, 1);
+ conn->kapool = purple_http_keepalive_pool_new();
+ conn->payload_reqs = purple_http_connection_set_new();
+ purple_http_keepalive_pool_set_limit_per_host(conn->kapool, 2);
+ conn->url = g_strdup(url);
conn->js = js;
+ conn->is_ssl = (g_ascii_strcasecmp("https",
+ purple_http_url_get_protocol(url_p)) == 0);
+ conn->send_buff = g_string_new(NULL);
/*
* Random 64-bit integer masked off by 2^52 - 1.
*
* This should produce a random integer in the range [0, 2^52). It's
- * unlikely we'll send enough packets in one session to overflow the rid.
+ * unlikely we'll send enough packets in one session to overflow the
+ * rid.
*/
- conn->rid = ((guint64)g_random_int() << 32) | g_random_int();
+ conn->rid = (((guint64)g_random_int() << 32) | g_random_int());
conn->rid &= 0xFFFFFFFFFFFFFLL;
- conn->pending = purple_circ_buffer_new(0 /* default grow size */);
-
- conn->state = BOSH_CONN_OFFLINE;
- if (purple_strcasestr(url, "https://") != NULL)
- conn->ssl = TRUE;
+ if (purple_ip_address_is_valid(purple_http_url_get_host(url_p)))
+ js->serverFQDN = g_strdup(js->user->domain);
else
- conn->ssl = FALSE;
+ js->serverFQDN = g_strdup(purple_http_url_get_host(url_p));
- conn->connections[0] = jabber_bosh_http_connection_init(conn);
+ purple_http_url_free(url_p);
+
+ jabber_bosh_connection_session_create(conn);
return conn;
}
void
-jabber_bosh_connection_destroy(PurpleBOSHConnection *conn)
+jabber_bosh_connection_destroy(PurpleJabberBOSHConnection *conn)
{
- int i;
+ if (conn == NULL || conn->is_terminating)
+ return;
+ conn->is_terminating = TRUE;
- g_free(conn->host);
- g_free(conn->path);
+ if (conn->sid != NULL) {
+ purple_debug_info("jabber-bosh",
+ "Terminating a session for %p\n", conn);
+ jabber_bosh_connection_send_now(conn);
+ }
+
+ purple_http_connection_set_destroy(conn->payload_reqs);
+ conn->payload_reqs = NULL;
if (conn->send_timer)
purple_timeout_remove(conn->send_timer);
- purple_circ_buffer_destroy(conn->pending);
+ purple_http_conn_cancel(conn->sc_req);
+ conn->sc_req = NULL;
- for (i = 0; i < NUM_HTTP_CONNECTIONS; ++i) {
- if (conn->connections[i])
- jabber_bosh_http_connection_destroy(conn->connections[i]);
- }
+ purple_http_keepalive_pool_unref(conn->kapool);
+ conn->kapool = NULL;
+ g_string_free(conn->send_buff, TRUE);
+ conn->send_buff = NULL;
+
+ g_free(conn->sid);
+ conn->sid = NULL;
+ g_free(conn->url);
+ conn->url = NULL;
g_free(conn);
}
-gboolean jabber_bosh_connection_is_ssl(PurpleBOSHConnection *conn)
+gboolean
+jabber_bosh_connection_is_ssl(const PurpleJabberBOSHConnection *conn)
{
- return conn->ssl;
+ return conn->is_ssl;
}
-static PurpleHTTPConnection *
-find_available_http_connection(PurpleBOSHConnection *conn)
+static PurpleXmlNode *
+jabber_bosh_connection_parse(PurpleJabberBOSHConnection *conn,
+ PurpleHttpResponse *response)
{
- int i;
-
- if (purple_debug_is_verbose())
- debug_dump_http_connections(conn);
-
- /* Easy solution: Does everyone involved support pipelining? Hooray! Just use
- * one TCP connection! */
- if (conn->pipelining)
- return conn->connections[0]->state == HTTP_CONN_CONNECTED ?
- conn->connections[0] : NULL;
-
- /* First loop, look for a connection that's ready */
- for (i = 0; i < NUM_HTTP_CONNECTIONS; ++i) {
- if (conn->connections[i] &&
- conn->connections[i]->state == HTTP_CONN_CONNECTED &&
- conn->connections[i]->requests == 0)
- return conn->connections[i];
- }
+ PurpleXmlNode *root;
+ const gchar *data;
+ size_t data_len;
+ const gchar *type;
+
+ g_return_val_if_fail(conn != NULL, NULL);
+ g_return_val_if_fail(response != NULL, NULL);
- /* Second loop, is something currently connecting? If so, just queue up. */
- for (i = 0; i < NUM_HTTP_CONNECTIONS; ++i) {
- if (conn->connections[i] &&
- conn->connections[i]->state == HTTP_CONN_CONNECTING)
- return NULL;
+ if (conn->is_terminating || purple_account_is_disconnecting(
+ purple_connection_get_account(conn->js->gc)))
+ {
+ return NULL;
}
- /* Third loop, is something offline that we can connect? */
- for (i = 0; i < NUM_HTTP_CONNECTIONS; ++i) {
- if (conn->connections[i] &&
- conn->connections[i]->state == HTTP_CONN_OFFLINE) {
- purple_debug_info("jabber", "bosh: Reconnecting httpconn "
- "(%i, %p)\n", i, conn->connections[i]);
- http_connection_connect(conn->connections[i]);
- return NULL;
- }
+ if (!purple_http_response_is_successful(response)) {
+ purple_connection_error(conn->js->gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Unable to connect"));
+ return NULL;
}
- /* Fourth loop, look for one that's NULL and create a new connection */
- for (i = 0; i < NUM_HTTP_CONNECTIONS; ++i) {
- if (!conn->connections[i]) {
- conn->connections[i] = jabber_bosh_http_connection_init(conn);
- purple_debug_info("jabber", "bosh: Creating and connecting new httpconn "
- "(%i, %p)\n", i, conn->connections[i]);
+ data = purple_http_response_get_data(response, &data_len);
+ root = purple_xmlnode_from_str(data, data_len);
- http_connection_connect(conn->connections[i]);
- return NULL;
- }
+ type = purple_xmlnode_get_attrib(root, "type");
+ if (g_strcmp0(type, "terminate") == 0) {
+ purple_connection_error(conn->js->gc,
+ PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("The BOSH "
+ "connection manager terminated your session."));
+ purple_xmlnode_free(root);
+ return NULL;
}
- purple_debug_warning("jabber", "Could not find a HTTP connection!\n");
-
- /* None available. */
- return NULL;
+ return root;
}
static void
-jabber_bosh_connection_send(PurpleBOSHConnection *conn,
- const PurpleBOSHPacketType type, const char *data)
+jabber_bosh_connection_recv(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _bosh_conn)
{
- PurpleHTTPConnection *chosen;
- GString *packet = NULL;
-
- if (type != PACKET_FLUSH && type != PACKET_TERMINATE) {
- /*
- * Unless this is a flush (or session terminate, which needs to be
- * sent immediately), queue up the data and start a timer to flush
- * the buffer.
- */
- if (data)
- purple_circ_buffer_append(conn->pending, data, strlen(data));
-
- if (purple_debug_is_verbose())
- purple_debug_misc("jabber", "bosh: %p has %" G_GSIZE_FORMAT " bytes in "
- "the buffer.\n", conn, conn->pending->bufused);
- if (conn->send_timer == 0)
- conn->send_timer = purple_timeout_add_seconds(BUFFER_SEND_IN_SECS,
- send_timer_cb, conn);
- return;
- }
-
- chosen = find_available_http_connection(conn);
+ PurpleJabberBOSHConnection *bosh_conn = _bosh_conn;
+ PurpleXmlNode *node, *child;
- if (!chosen) {
- if (type == PACKET_FLUSH)
- return;
- /*
- * For non-ordinary traffic, we can't 'buffer' it, so use the
- * first connection.
- */
- chosen = conn->connections[0];
-
- if (chosen->state != HTTP_CONN_CONNECTED) {
- purple_debug_warning("jabber", "Unable to find a ready BOSH "
- "connection. Ignoring send of type 0x%02x.\n", type);
- return;
- }
- }
-
- /* We're flushing the send buffer, so remove the send timer */
- if (conn->send_timer != 0) {
- purple_timeout_remove(conn->send_timer);
- conn->send_timer = 0;
+ if (purple_debug_is_verbose() && purple_debug_is_unsafe()) {
+ purple_debug_misc("jabber-bosh", "received: %s\n",
+ purple_http_response_get_data(response, NULL));
}
- packet = g_string_new(NULL);
-
- g_string_printf(packet, "<body "
- "rid='%" G_GUINT64_FORMAT "' "
- "sid='%s' "
- "to='%s' "
- "xml:lang='en' "
- "xmlns='" NS_BOSH "' "
- "xmlns:xmpp='" NS_XMPP_BOSH "'",
- ++conn->rid,
- conn->sid,
- conn->js->user->domain);
-
- if (conn->js->reinit) {
- packet = g_string_append(packet, " xmpp:restart='true'/>");
- /* TODO: Do we need to wait for a response? */
- conn->js->reinit = FALSE;
- } else {
- gsize read_amt;
- if (type == PACKET_TERMINATE)
- packet = g_string_append(packet, " type='terminate'");
+ node = jabber_bosh_connection_parse(bosh_conn, response);
+ if (node == NULL)
+ return;
- packet = g_string_append_c(packet, '>');
+ child = node->child;
+ while (child != NULL) {
+ /* jabber_process_packet might free child */
+ PurpleXmlNode *next = child->next;
+ const gchar *xmlns;
- while ((read_amt = purple_circ_buffer_get_max_read(conn->pending)) > 0) {
- packet = g_string_append_len(packet, conn->pending->outptr, read_amt);
- purple_circ_buffer_mark_read(conn->pending, read_amt);
+ if (child->type != PURPLE_XMLNODE_TYPE_TAG) {
+ child = next;
+ continue;
}
- if (data)
- packet = g_string_append(packet, data);
- packet = g_string_append(packet, "</body>");
- }
-
- http_connection_send_request(chosen, packet);
-}
-
-void jabber_bosh_connection_close(PurpleBOSHConnection *conn)
-{
- if (conn->state == BOSH_CONN_ONLINE)
- jabber_bosh_connection_send(conn, PACKET_TERMINATE, NULL);
-}
-
-static gboolean jabber_bosh_connection_error_check(PurpleBOSHConnection *conn, xmlnode *node) {
- const char *type;
+ /* Workaround for non-compliant servers that don't stamp
+ * the right xmlns on these packets. See #11315.
+ */
+ xmlns = purple_xmlnode_get_namespace(child);
+ if ((xmlns == NULL || g_strcmp0(xmlns, NS_BOSH) == 0) &&
+ (g_strcmp0(child->name, "iq") == 0 ||
+ g_strcmp0(child->name, "message") == 0 ||
+ g_strcmp0(child->name, "presence") == 0))
+ {
+ purple_xmlnode_set_namespace(child, NS_XMPP_CLIENT);
+ }
- type = xmlnode_get_attrib(node, "type");
+ jabber_process_packet(bosh_conn->js, &child);
- if (type != NULL && !strcmp(type, "terminate")) {
- conn->state = BOSH_CONN_OFFLINE;
- purple_connection_error_reason(conn->js->gc,
- PURPLE_CONNECTION_ERROR_OTHER_ERROR,
- _("The BOSH connection manager terminated your session."));
- return TRUE;
+ child = next;
}
- return FALSE;
-}
-
-static gboolean
-send_timer_cb(gpointer data)
-{
- PurpleBOSHConnection *bosh;
-
- bosh = data;
- bosh->send_timer = 0;
-
- jabber_bosh_connection_send(bosh, PACKET_FLUSH, NULL);
- return FALSE;
-}
-
-void
-jabber_bosh_connection_send_keepalive(PurpleBOSHConnection *bosh)
-{
- if (bosh->send_timer != 0)
- purple_timeout_remove(bosh->send_timer);
-
- /* clears bosh->send_timer */
- send_timer_cb(bosh);
+ jabber_bosh_connection_send(bosh_conn, NULL);
}
static void
-jabber_bosh_disable_pipelining(PurpleBOSHConnection *bosh)
+jabber_bosh_connection_send_now(PurpleJabberBOSHConnection *conn)
{
- /* Do nothing if it's already disabled */
- if (!bosh->pipelining)
- return;
-
- purple_debug_info("jabber", "BOSH: Disabling pipelining on conn %p\n",
- bosh);
- bosh->pipelining = FALSE;
- if (bosh->connections[1] == NULL) {
- bosh->connections[1] = jabber_bosh_http_connection_init(bosh);
- http_connection_connect(bosh->connections[1]);
- } else {
- /* Shouldn't happen; this should be the only place pipelining
- * is turned off.
- */
- g_warn_if_reached();
- }
-}
+ PurpleHttpRequest *req;
+ GString *data;
-static void jabber_bosh_connection_received(PurpleBOSHConnection *conn, xmlnode *node) {
- xmlnode *child;
- JabberStream *js = conn->js;
-
- g_return_if_fail(node != NULL);
- if (jabber_bosh_connection_error_check(conn, node))
- return;
-
- child = node->child;
- while (child != NULL) {
- /* jabber_process_packet might free child */
- xmlnode *next = child->next;
- if (child->type == XMLNODE_TYPE_TAG) {
- const char *xmlns = xmlnode_get_namespace(child);
- /*
- * Workaround for non-compliant servers that don't stamp
- * the right xmlns on these packets. See #11315.
- */
- if ((xmlns == NULL /* shouldn't happen, but is equally wrong */ ||
- g_str_equal(xmlns, NS_BOSH)) &&
- (g_str_equal(child->name, "iq") ||
- g_str_equal(child->name, "message") ||
- g_str_equal(child->name, "presence"))) {
- xmlnode_set_namespace(child, NS_XMPP_CLIENT);
- }
- jabber_process_packet(js, &child);
- }
+ g_return_if_fail(conn != NULL);
- child = next;
+ if (conn->send_timer != 0) {
+ purple_timeout_remove(conn->send_timer);
+ conn->send_timer = 0;
}
-}
-
-static void boot_response_cb(PurpleBOSHConnection *conn, xmlnode *node) {
- JabberStream *js = conn->js;
- const char *sid, *version;
- const char *inactivity, *requests;
- xmlnode *packet;
- g_return_if_fail(node != NULL);
- if (jabber_bosh_connection_error_check(conn, node))
+ if (conn->sid == NULL)
return;
- sid = xmlnode_get_attrib(node, "sid");
- version = xmlnode_get_attrib(node, "ver");
+ data = g_string_new(NULL);
- inactivity = xmlnode_get_attrib(node, "inactivity");
- requests = xmlnode_get_attrib(node, "requests");
+ /* missing parameters: route, from, ack */
+ g_string_printf(data, "<body "
+ "rid='%" G_GUINT64_FORMAT "' "
+ "sid='%s' "
+ "xmlns='" NS_BOSH "' "
+ "xmlns:xmpp='" NS_XMPP_BOSH "' ",
+ ++conn->rid, conn->sid);
- if (sid) {
- conn->sid = g_strdup(sid);
+ if (conn->js->reinit && !conn->is_terminating) {
+ g_string_append(data, "xmpp:restart='true'/>");
+ conn->js->reinit = FALSE;
} else {
- purple_connection_error_reason(js->gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("No session ID given"));
- return;
+ if (conn->is_terminating)
+ g_string_append(data, "type='terminate' ");
+ g_string_append_c(data, '>');
+ g_string_append_len(data, conn->send_buff->str,
+ conn->send_buff->len);
+ g_string_append(data, "</body>");
+ g_string_set_size(conn->send_buff, 0);
}
- if (version) {
- const char *dot = strchr(version, '.');
- int major, minor = 0;
+ if (purple_debug_is_verbose() && purple_debug_is_unsafe())
+ purple_debug_misc("jabber-bosh", "sending: %s\n", data->str);
- purple_debug_info("jabber", "BOSH connection manager version %s\n", version);
+ req = jabber_bosh_connection_http_request_new(conn, data);
+ g_string_free(data, TRUE);
- major = atoi(version);
- if (dot)
- minor = atoi(dot + 1);
-
- if (major != 1 || minor < 6) {
- purple_connection_error_reason(js->gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Unsupported version of BOSH protocol"));
- return;
- }
+ if (conn->is_terminating) {
+ purple_http_request(NULL, req, NULL, NULL);
+ g_free(conn->sid);
+ conn->sid = NULL;
} else {
- purple_debug_info("jabber", "Missing version in BOSH initiation\n");
- }
-
- if (inactivity) {
- js->max_inactivity = atoi(inactivity);
- if (js->max_inactivity <= 5) {
- purple_debug_warning("jabber", "Ignoring bogusly small inactivity: %s\n",
- inactivity);
- /* Leave it at the default */
- } else {
- /* TODO: Can this check fail? It shouldn't */
- js->max_inactivity -= 5; /* rounding */
-
- if (js->inactivity_timer == 0) {
- purple_debug_misc("jabber", "Starting BOSH inactivity timer "
- "for %d secs (compensating for rounding)\n",
- js->max_inactivity);
- jabber_stream_restart_inactivity_timer(js);
- }
- }
+ purple_http_connection_set_add(conn->payload_reqs,
+ purple_http_request(conn->js->gc, req,
+ jabber_bosh_connection_recv, conn));
}
- if (requests)
- conn->max_requests = atoi(requests);
-
- jabber_stream_set_state(js, JABBER_STREAM_AUTHENTICATING);
-
- /* FIXME: Depending on receiving features might break with some hosts */
- packet = xmlnode_get_child(node, "features");
- conn->state = BOSH_CONN_ONLINE;
- conn->receive_cb = jabber_bosh_connection_received;
- jabber_stream_features_parse(js, packet);
-}
-
-static void jabber_bosh_connection_boot(PurpleBOSHConnection *conn) {
- GString *buf = g_string_new(NULL);
-
- g_string_printf(buf, "<body content='text/xml; charset=utf-8' "
- "secure='true' "
- "to='%s' "
- "xml:lang='en' "
- "xmpp:version='1.0' "
- "ver='1.6' "
- "xmlns:xmpp='" NS_XMPP_BOSH "' "
- "rid='%" G_GUINT64_FORMAT "' "
-/* TODO: This should be adjusted/adjustable automatically according to
- * realtime network behavior */
- "wait='60' "
- "hold='1' "
- "xmlns='" NS_BOSH "'/>",
- conn->js->user->domain,
- ++conn->rid);
-
- purple_debug_misc("jabber", "SendBOSH Boot %s(%" G_GSIZE_FORMAT "): %s\n",
- conn->ssl ? "(ssl)" : "", buf->len, buf->str);
- conn->receive_cb = boot_response_cb;
- http_connection_send_request(conn->connections[0], buf);
- g_string_free(buf, TRUE);
+ purple_http_request_unref(req);
}
-/**
- * Handle one complete BOSH response. This is a <body> node containing
- * any number of XMPP stanzas.
- */
-static void
-http_received_cb(const char *data, int len, PurpleBOSHConnection *conn)
+static gboolean
+jabber_bosh_connection_send_delayed(gpointer _conn)
{
- xmlnode *node;
- gchar *message;
-
- if (conn->failed_connections)
- /* We've got some data, so reset the number of failed connections */
- conn->failed_connections = 0;
-
- g_return_if_fail(conn->receive_cb);
-
- node = xmlnode_from_str(data, len);
-
- message = g_strndup(data, len);
- purple_debug_info("jabber", "RecvBOSH %s(%d): %s\n",
- conn->ssl ? "(ssl)" : "", len, message);
- g_free(message);
-
- if (node) {
- conn->receive_cb(conn, node);
- xmlnode_free(node);
- } else {
- purple_debug_warning("jabber", "BOSH: Received invalid XML\n");
- }
-}
+ PurpleJabberBOSHConnection *conn = _conn;
-void jabber_bosh_connection_send_raw(PurpleBOSHConnection *conn,
- const char *data)
-{
- jabber_bosh_connection_send(conn, PACKET_NORMAL, data);
-}
+ conn->send_timer = 0;
+ jabber_bosh_connection_send_now(conn);
-static void
-connection_common_established_cb(PurpleHTTPConnection *conn)
-{
- purple_debug_misc("jabber", "bosh: httpconn %p re-connected\n", conn);
-
- /* Indicate we're ready and reset some variables */
- conn->state = HTTP_CONN_CONNECTED;
- if (conn->requests != 0)
- purple_debug_error("jabber", "bosh: httpconn %p has %d requests, != 0\n",
- conn, conn->requests);
-
- conn->requests = 0;
- if (conn->read_buf) {
- g_string_free(conn->read_buf, TRUE);
- conn->read_buf = NULL;
- }
- conn->close = FALSE;
- conn->headers_done = FALSE;
- conn->handled_len = conn->body_len = 0;
-
- if (purple_debug_is_verbose())
- debug_dump_http_connections(conn->bosh);
-
- if (conn->bosh->js->reinit)
- jabber_bosh_connection_send(conn->bosh, PACKET_NORMAL, NULL);
- else if (conn->bosh->state == BOSH_CONN_ONLINE) {
- purple_debug_info("jabber", "BOSH session already exists. Trying to reuse it.\n");
- if (conn->bosh->requests == 0 || conn->bosh->pending->bufused > 0) {
- /* Send the pending data */
- jabber_bosh_connection_send(conn->bosh, PACKET_FLUSH, NULL);
- }
- } else
- jabber_bosh_connection_boot(conn->bosh);
+ return FALSE;
}
-static void http_connection_disconnected(PurpleHTTPConnection *conn)
+void
+jabber_bosh_connection_send(PurpleJabberBOSHConnection *conn,
+ const gchar *data)
{
- gboolean had_requests = FALSE;
- /*
- * Well, then. Fine! I never liked you anyway, server! I was cheating on you
- * with AIM!
- */
- conn->state = HTTP_CONN_OFFLINE;
- if (conn->psc) {
- purple_ssl_close(conn->psc);
- conn->psc = NULL;
- } else if (conn->fd >= 0) {
- close(conn->fd);
- conn->fd = -1;
- }
-
- if (conn->readh) {
- purple_input_remove(conn->readh);
- conn->readh = 0;
- }
-
- if (conn->writeh) {
- purple_input_remove(conn->writeh);
- conn->writeh = 0;
- }
-
- had_requests = (conn->requests > 0);
- if (had_requests && conn->read_buf->len == 0) {
- purple_debug_error("jabber", "bosh: Adjusting BOSHconn requests (%d) to %d\n",
- conn->bosh->requests, conn->bosh->requests - conn->requests);
- conn->bosh->requests -= conn->requests;
- conn->requests = 0;
- }
-
- if (conn->bosh->pipelining) {
- /* Hmmmm, fall back to multiple connections */
- jabber_bosh_disable_pipelining(conn->bosh);
- }
+ g_return_if_fail(conn != NULL);
- if (!had_requests)
- /* If the server disconnected us without any requests, let's
- * just wait until we have something to send before we reconnect
- */
- return;
+ if (data)
+ g_string_append(conn->send_buff, data);
- if (++conn->bosh->failed_connections == MAX_FAILED_CONNECTIONS) {
- purple_connection_error_reason(conn->bosh->js->gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Unable to establish a connection with the server"));
- } else {
- /* No! Please! Take me back. It was me, not you! I was weak! */
- http_connection_connect(conn);
+ if (conn->send_timer == 0) {
+ conn->send_timer = purple_timeout_add(
+ JABBER_BOSH_SEND_DELAY,
+ jabber_bosh_connection_send_delayed, conn);
}
}
-void jabber_bosh_connection_connect(PurpleBOSHConnection *bosh) {
- PurpleHTTPConnection *conn = bosh->connections[0];
-
- g_return_if_fail(bosh->state == BOSH_CONN_OFFLINE);
- bosh->state = BOSH_CONN_BOOTING;
+void
+jabber_bosh_connection_send_keepalive(PurpleJabberBOSHConnection *conn)
+{
+ g_return_if_fail(conn != NULL);
- http_connection_connect(conn);
+ jabber_bosh_connection_send_now(conn);
}
-/**
- * @return TRUE if we want to be called again immediately. This happens when
- * we parse an HTTP response AND there is more data in read_buf. FALSE
- * if we should not be called again unless more data has been read.
- */
static gboolean
-jabber_bosh_http_connection_process(PurpleHTTPConnection *conn)
+jabber_bosh_version_check(const gchar *version, int major_req, int minor_min)
{
- const char *cursor;
-
- cursor = conn->read_buf->str + conn->handled_len;
-
- if (purple_debug_is_verbose())
- purple_debug_misc("jabber", "BOSH server sent: %s\n", cursor);
+ const gchar *dot;
+ int major, minor = 0;
- /* TODO: Chunked encoding and check response version :/ */
- if (!conn->headers_done) {
- const char *content_length = purple_strcasestr(cursor, "\r\nContent-Length:");
- const char *connection = purple_strcasestr(cursor, "\r\nConnection:");
- const char *end_of_headers = strstr(cursor, "\r\n\r\n");
-
- /* Make sure Content-Length is in headers, not body */
- if (content_length && (!end_of_headers || content_length < end_of_headers)) {
- int len;
-
- if (strstr(content_length, "\r\n") == NULL)
- /*
- * The packet ends in the middle of the Content-Length line.
- * We'll try again later when we have more.
- */
- return FALSE;
-
- len = atoi(content_length + strlen("\r\nContent-Length:"));
- if (len == 0)
- purple_debug_warning("jabber", "Found mangled Content-Length header, or server returned 0-length response.\n");
-
- conn->body_len = len;
- }
-
- if (connection && (!end_of_headers || connection < end_of_headers)) {
- const char *tmp;
- if (strstr(connection, "\r\n") == NULL)
- return FALSE;
-
-
- tmp = connection + strlen("\r\nConnection:");
- while (*tmp && (*tmp == ' ' || *tmp == '\t'))
- ++tmp;
-
- if (!g_ascii_strncasecmp(tmp, "close", strlen("close"))) {
- conn->close = TRUE;
- jabber_bosh_disable_pipelining(conn->bosh);
- }
- }
+ if (version == NULL)
+ return FALSE;
- if (end_of_headers) {
- conn->headers_done = TRUE;
- conn->handled_len = end_of_headers - conn->read_buf->str + 4;
- } else {
- conn->handled_len = conn->read_buf->len;
- return FALSE;
- }
- }
+ major = atoi(version);
+ dot = strchr(version, '.');
+ if (dot)
+ minor = atoi(dot + 1);
- /* Have we handled everything in the buffer? */
- if (conn->handled_len >= conn->read_buf->len)
+ if (major != major_req)
return FALSE;
-
- /* Have we read all that the Content-Length promised us? */
- if (conn->read_buf->len - conn->handled_len < conn->body_len)
+ if (minor < minor_min)
return FALSE;
-
- --conn->requests;
- --conn->bosh->requests;
-
- http_received_cb(conn->read_buf->str + conn->handled_len, conn->body_len,
- conn->bosh);
-
- /* Is there another response in the buffer ? */
- if (conn->read_buf->len > conn->body_len + conn->handled_len) {
- g_string_erase(conn->read_buf, 0, conn->handled_len + conn->body_len);
- conn->headers_done = FALSE;
- conn->handled_len = conn->body_len = 0;
- return TRUE;
- }
-
- /* Connection: Close? */
- if (conn->close && conn->state == HTTP_CONN_CONNECTED) {
- if (purple_debug_is_verbose())
- purple_debug_misc("jabber", "bosh (%p), server sent Connection: "
- "close\n", conn);
- http_connection_disconnected(conn);
- }
-
- if (conn->bosh->state == BOSH_CONN_ONLINE &&
- (conn->bosh->requests == 0 || conn->bosh->pending->bufused > 0)) {
- purple_debug_misc("jabber", "BOSH: Sending an empty request\n");
- jabber_bosh_connection_send(conn->bosh, PACKET_NORMAL, NULL);
- }
-
- g_string_free(conn->read_buf, TRUE);
- conn->read_buf = NULL;
- conn->headers_done = FALSE;
- conn->handled_len = conn->body_len = 0;
-
- return FALSE;
+ return TRUE;
}
-/*
- * Common code for reading, called from http_connection_read_cb_ssl and
- * http_connection_read_cb.
- */
static void
-http_connection_read(PurpleHTTPConnection *conn)
+jabber_bosh_connection_session_created(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _bosh_conn)
{
- char buffer[1025];
- int cnt;
+ PurpleJabberBOSHConnection *bosh_conn = _bosh_conn;
+ PurpleXmlNode *node, *features;
+ const gchar *sid, *ver, *inactivity_str;
+ int inactivity = 0;
+
+ bosh_conn->sc_req = NULL;
- if (!conn->read_buf)
- conn->read_buf = g_string_new(NULL);
+ if (purple_debug_is_verbose() && purple_debug_is_unsafe()) {
+ purple_debug_misc("jabber-bosh",
+ "received (session creation): %s\n",
+ purple_http_response_get_data(response, NULL));
+ }
- do {
- if (conn->psc)
- cnt = purple_ssl_read(conn->psc, buffer, sizeof(buffer));
- else
- cnt = read(conn->fd, buffer, sizeof(buffer));
+ node = jabber_bosh_connection_parse(bosh_conn, response);
+ if (node == NULL)
+ return;
- if (cnt > 0) {
- g_string_append_len(conn->read_buf, buffer, cnt);
- }
- } while (cnt > 0);
-
- if (cnt == 0 || (cnt < 0 && errno != EAGAIN)) {
- if (cnt < 0)
- purple_debug_info("jabber", "BOSH (%p) read=%d, errno=%d, error=%s\n",
- conn, cnt, errno, g_strerror(errno));
- else
- purple_debug_info("jabber", "BOSH server closed the connection (%p)\n",
- conn);
-
- /*
- * If the socket is closed, the processing really needs to know about
- * it. Handle that now.
- */
- http_connection_disconnected(conn);
+ sid = purple_xmlnode_get_attrib(node, "sid");
+ ver = purple_xmlnode_get_attrib(node, "ver");
+ inactivity_str = purple_xmlnode_get_attrib(node, "inactivity");
+ /* requests = purple_xmlnode_get_attrib(node, "requests"); */
- /* Process what we do have */
+ if (!sid) {
+ purple_connection_error(bosh_conn->js->gc,
+ PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+ _("No BOSH session ID given"));
+ purple_xmlnode_free(node);
+ return;
}
- if (conn->read_buf->len > 0) {
- while (jabber_bosh_http_connection_process(conn));
+ if (ver == NULL) {
+ purple_debug_info("jabber-bosh", "Missing version in BOSH initiation\n");
+ } else if (!jabber_bosh_version_check(ver, 1, 6)) {
+ purple_debug_error("jabber-bosh",
+ "Unsupported BOSH version: %s\n", ver);
+ purple_connection_error(bosh_conn->js->gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Unsupported version of BOSH protocol"));
+ purple_xmlnode_free(node);
+ return;
}
-}
-static void
-http_connection_read_cb(gpointer data, gint fd, PurpleInputCondition condition)
-{
- PurpleHTTPConnection *conn = data;
+ purple_debug_misc("jabber-bosh", "Session created for %p\n", bosh_conn);
- http_connection_read(conn);
-}
+ bosh_conn->sid = g_strdup(sid);
-static void
-http_connection_read_cb_ssl(gpointer data, PurpleSslConnection *psc,
- PurpleInputCondition cond)
-{
- PurpleHTTPConnection *conn = data;
+ if (inactivity_str)
+ inactivity = atoi(inactivity_str);
+ if (inactivity < 0 || inactivity > 3600) {
+ purple_debug_warning("jabber-bosh", "Ignoring invalid "
+ "inactivity value: %s\n", inactivity_str);
+ inactivity = 0;
+ }
+ if (inactivity > 0) {
+ inactivity -= 5; /* rounding */
+ if (inactivity <= 0)
+ inactivity = 1;
+ bosh_conn->js->max_inactivity = inactivity;
+ if (bosh_conn->js->inactivity_timer == 0) {
+ purple_debug_misc("jabber-bosh", "Starting inactivity "
+ "timer for %d secs (compensating for "
+ "rounding)\n", inactivity);
+ jabber_stream_restart_inactivity_timer(bosh_conn->js);
+ }
+ }
- http_connection_read(conn);
-}
+ jabber_stream_set_state(bosh_conn->js, JABBER_STREAM_AUTHENTICATING);
-static void
-ssl_connection_established_cb(gpointer data, PurpleSslConnection *psc,
- PurpleInputCondition cond)
-{
- PurpleHTTPConnection *conn = data;
+ /* FIXME: Depending on receiving features might break with some hosts */
+ features = purple_xmlnode_get_child(node, "features");
+ jabber_stream_features_parse(bosh_conn->js, features);
- purple_ssl_input_add(psc, http_connection_read_cb_ssl, conn);
- connection_common_established_cb(conn);
+ purple_xmlnode_free(node);
+
+ jabber_bosh_connection_send(bosh_conn, NULL);
}
static void
-ssl_connection_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error,
- gpointer data)
+jabber_bosh_connection_session_create(PurpleJabberBOSHConnection *conn)
{
- PurpleHTTPConnection *conn = data;
-
- /* sslconn frees the connection on error */
- conn->psc = NULL;
+ PurpleHttpRequest *req;
+ GString *data;
- purple_connection_ssl_error(conn->bosh->js->gc, error);
-}
+ g_return_if_fail(conn != NULL);
-static void
-connection_established_cb(gpointer data, gint source, const gchar *error)
-{
- PurpleHTTPConnection *conn = data;
- PurpleConnection *gc = conn->bosh->js->gc;
-
- if (source < 0) {
- gchar *tmp;
- tmp = g_strdup_printf(_("Unable to establish a connection with the server: %s"),
- error);
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
- g_free(tmp);
+ if (conn->sid || conn->sc_req)
return;
- }
- conn->fd = source;
- conn->readh = purple_input_add(conn->fd, PURPLE_INPUT_READ,
- http_connection_read_cb, conn);
- connection_common_established_cb(conn);
-}
+ purple_debug_misc("jabber-bosh", "Requesting Session Create for %p\n",
+ conn);
-static void http_connection_connect(PurpleHTTPConnection *conn)
-{
- PurpleBOSHConnection *bosh = conn->bosh;
- PurpleConnection *gc = bosh->js->gc;
- PurpleAccount *account = purple_connection_get_account(gc);
-
- conn->state = HTTP_CONN_CONNECTING;
-
- if (bosh->ssl) {
- if (purple_ssl_is_supported()) {
- conn->psc = purple_ssl_connect(account, bosh->host, bosh->port,
- ssl_connection_established_cb,
- ssl_connection_error_cb,
- conn);
- if (!conn->psc) {
- purple_connection_error_reason(gc,
- PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
- _("Unable to establish SSL connection"));
- }
- } else {
- purple_connection_error_reason(gc,
- PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
- _("SSL support unavailable"));
- }
- } else if (purple_proxy_connect(conn, account, bosh->host, bosh->port,
- connection_established_cb, conn) == NULL) {
- purple_connection_error_reason(gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Unable to connect"));
- }
-}
+ data = g_string_new(NULL);
-static int
-http_connection_do_send(PurpleHTTPConnection *conn, const char *data, int len)
-{
- int ret;
+ /* missing optional parameters: route, from, ack */
+ g_string_printf(data, "<body content='text/xml; charset=utf-8' "
+ "rid='%" G_GUINT64_FORMAT "' "
+ "to='%s' "
+ "xml:lang='en' "
+ "ver='1.10' "
+ "wait='%d' "
+ "hold='1' "
+ "xmlns='" NS_BOSH "' "
+ "xmpp:version='1.0' "
+ "xmlns:xmpp='urn:xmpp:xbosh' "
+ "/>",
+ ++conn->rid, conn->js->user->domain, JABBER_BOSH_TIMEOUT);
- if (conn->psc)
- ret = purple_ssl_write(conn->psc, data, len);
- else
- ret = write(conn->fd, data, len);
+ req = jabber_bosh_connection_http_request_new(conn, data);
+ g_string_free(data, TRUE);
- if (purple_debug_is_verbose())
- purple_debug_misc("jabber", "BOSH (%p): wrote %d bytes\n", conn, ret);
+ conn->sc_req = purple_http_request(conn->js->gc, req,
+ jabber_bosh_connection_session_created, conn);
- return ret;
+ purple_http_request_unref(req);
}
-static void
-http_connection_send_cb(gpointer data, gint source, PurpleInputCondition cond)
+static PurpleHttpRequest *
+jabber_bosh_connection_http_request_new(PurpleJabberBOSHConnection *conn,
+ const GString *data)
{
- PurpleHTTPConnection *conn = data;
- int ret;
- int writelen = purple_circ_buffer_get_max_read(conn->write_buf);
-
- if (writelen == 0) {
- purple_input_remove(conn->writeh);
- conn->writeh = 0;
- return;
- }
-
- ret = http_connection_do_send(conn, conn->write_buf->outptr, writelen);
-
- if (ret < 0 && errno == EAGAIN)
- return;
- else if (ret <= 0) {
- /*
- * TODO: Handle this better. Probably requires a PurpleBOSHConnection
- * buffer that stores what is "being sent" until the
- * PurpleHTTPConnection reports it is fully sent.
- */
- gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
- g_strerror(errno));
- purple_connection_error_reason(conn->bosh->js->gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- tmp);
- g_free(tmp);
- return;
- }
+ PurpleHttpRequest *req;
- purple_circ_buffer_mark_read(conn->write_buf, ret);
-}
+ jabber_stream_restart_inactivity_timer(conn->js);
-static void
-http_connection_send_request(PurpleHTTPConnection *conn, const GString *req)
-{
- char *data;
- int ret;
- size_t len;
-
- /* Sending something to the server, restart the inactivity timer */
- jabber_stream_restart_inactivity_timer(conn->bosh->js);
-
- data = g_strdup_printf("POST %s HTTP/1.1\r\n"
- "Host: %s\r\n"
- "User-Agent: %s\r\n"
- "Content-Encoding: text/xml; charset=utf-8\r\n"
- "Content-Length: %" G_GSIZE_FORMAT "\r\n\r\n"
- "%s",
- conn->bosh->path, conn->bosh->host, bosh_useragent,
- req->len, req->str);
-
- len = strlen(data);
-
- ++conn->requests;
- ++conn->bosh->requests;
-
- if (purple_debug_is_unsafe() && purple_debug_is_verbose())
- /* Will contain passwords for SASL PLAIN and is verbose */
- purple_debug_misc("jabber", "BOSH (%p): Sending %s\n", conn, data);
- else if (purple_debug_is_verbose())
- purple_debug_misc("jabber", "BOSH (%p): Sending request of "
- "%" G_GSIZE_FORMAT " bytes.\n", conn, len);
-
- if (conn->writeh == 0)
- ret = http_connection_do_send(conn, data, len);
- else {
- ret = -1;
- errno = EAGAIN;
- }
+ req = purple_http_request_new(conn->url);
+ purple_http_request_set_keepalive_pool(req, conn->kapool);
+ purple_http_request_set_method(req, "POST");
+ purple_http_request_set_timeout(req, JABBER_BOSH_TIMEOUT + 2);
+ purple_http_request_header_set(req, "User-Agent",
+ jabber_bosh_useragent);
+ purple_http_request_header_set(req, "Content-Encoding",
+ "text/xml; charset=utf-8");
+ purple_http_request_set_contents(req, data->str, data->len);
- if (ret < 0 && errno != EAGAIN) {
- /*
- * TODO: Handle this better. Probably requires a PurpleBOSHConnection
- * buffer that stores what is "being sent" until the
- * PurpleHTTPConnection reports it is fully sent.
- */
- gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
- g_strerror(errno));
- purple_connection_error_reason(conn->bosh->js->gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- tmp);
- g_free(tmp);
- return;
- } else if (ret < len) {
- if (ret < 0)
- ret = 0;
- if (conn->writeh == 0)
- conn->writeh = purple_input_add(conn->psc ? conn->psc->fd : conn->fd,
- PURPLE_INPUT_WRITE, http_connection_send_cb, conn);
- purple_circ_buffer_append(conn->write_buf, data + ret, len - ret);
- }
+ return req;
}
-
diff --git a/libpurple/protocols/jabber/bosh.h b/libpurple/protocols/jabber/bosh.h
index 0648ec0b8f..dda6161131 100644
--- a/libpurple/protocols/jabber/bosh.h
+++ b/libpurple/protocols/jabber/bosh.h
@@ -1,5 +1,6 @@
/**
- * @file bosh.h Bidirectional-streams over Synchronous HTTP (BOSH) (XEP-0124 and XEP-0206)
+ * @file bosh.h Bidirectional-streams over Synchronous HTTP (BOSH)
+ * (XEP-0124 and XEP-0206)
*
* purple
*
@@ -19,25 +20,35 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#ifndef PURPLE_JABBER_BOSH_H_
#define PURPLE_JABBER_BOSH_H_
-typedef struct _PurpleBOSHConnection PurpleBOSHConnection;
+typedef struct _PurpleJabberBOSHConnection PurpleJabberBOSHConnection;
#include "jabber.h"
-void jabber_bosh_init(void);
-void jabber_bosh_uninit(void);
+void
+jabber_bosh_init(void);
-PurpleBOSHConnection* jabber_bosh_connection_init(JabberStream *js, const char *url);
-void jabber_bosh_connection_destroy(PurpleBOSHConnection *conn);
+void
+jabber_bosh_uninit(void);
-gboolean jabber_bosh_connection_is_ssl(PurpleBOSHConnection *conn);
-void jabber_bosh_connection_send_keepalive(PurpleBOSHConnection *conn);
+PurpleJabberBOSHConnection *
+jabber_bosh_connection_new(JabberStream *js, const gchar *url);
+
+void
+jabber_bosh_connection_destroy(PurpleJabberBOSHConnection *conn);
+
+gboolean
+jabber_bosh_connection_is_ssl(const PurpleJabberBOSHConnection *conn);
+
+void
+jabber_bosh_connection_send(PurpleJabberBOSHConnection *conn,
+ const gchar *data);
+
+void
+jabber_bosh_connection_send_keepalive(PurpleJabberBOSHConnection *conn);
-void jabber_bosh_connection_connect(PurpleBOSHConnection *conn);
-void jabber_bosh_connection_close(PurpleBOSHConnection *conn);
-void jabber_bosh_connection_send_raw(PurpleBOSHConnection *conn, const char *data);
#endif /* PURPLE_JABBER_BOSH_H_ */
diff --git a/libpurple/protocols/jabber/buddy.c b/libpurple/protocols/jabber/buddy.c
index cfe7f3e06d..b8de834d29 100644
--- a/libpurple/protocols/jabber/buddy.c
+++ b/libpurple/protocols/jabber/buddy.c
@@ -22,7 +22,7 @@
*/
#include "internal.h"
#include "debug.h"
-#include "imgstore.h"
+#include "image-store.h"
#include "prpl.h"
#include "notify.h"
#include "request.h"
@@ -51,7 +51,7 @@ typedef struct {
GSList *ids;
GHashTable *resources;
guint timeout_handle;
- GSList *vcard_imgids;
+ GSList *vcard_images;
PurpleNotifyUserInfo *user_info;
long last_seconds;
gchar *last_message;
@@ -387,7 +387,7 @@ struct tag_attr {
/*
- * Insert a tag node into an xmlnode tree, recursively inserting parent tag
+ * Insert a tag node into an PurpleXmlNode tree, recursively inserting parent tag
* nodes as necessary
*
* Returns pointer to inserted node
@@ -395,9 +395,9 @@ struct tag_attr {
* Note to hackers: this code is designed to be re-entrant (it's recursive--it
* calls itself), so don't put any "static"s in here!
*/
-static xmlnode *insert_tag_to_parent_tag(xmlnode *start, const char *parent_tag, const char *new_tag)
+static PurpleXmlNode *insert_tag_to_parent_tag(PurpleXmlNode *start, const char *parent_tag, const char *new_tag)
{
- xmlnode *x = NULL;
+ PurpleXmlNode *x = NULL;
/*
* If the parent tag wasn't specified, see if we can get it
@@ -422,7 +422,7 @@ static xmlnode *insert_tag_to_parent_tag(xmlnode *start, const char *parent_tag,
/*
* Try to get the parent node for a tag
*/
- if((x = xmlnode_get_child(start, parent_tag)) == NULL) {
+ if((x = purple_xmlnode_get_child(start, parent_tag)) == NULL) {
/*
* Descend?
*/
@@ -433,7 +433,7 @@ static xmlnode *insert_tag_to_parent_tag(xmlnode *start, const char *parent_tag,
*(parent++) = '\0';
x = insert_tag_to_parent_tag(start, grand_parent, parent);
} else {
- x = xmlnode_new_child(start, grand_parent);
+ x = purple_xmlnode_new_child(start, grand_parent);
}
g_free(grand_parent);
} else {
@@ -441,8 +441,8 @@ static xmlnode *insert_tag_to_parent_tag(xmlnode *start, const char *parent_tag,
* We found *something* to be the parent node.
* Note: may be the "root" node!
*/
- xmlnode *y;
- if((y = xmlnode_get_child(x, new_tag)) != NULL) {
+ PurpleXmlNode *y;
+ if((y = purple_xmlnode_get_child(x, new_tag)) != NULL) {
return(y);
}
}
@@ -451,7 +451,7 @@ static xmlnode *insert_tag_to_parent_tag(xmlnode *start, const char *parent_tag,
/*
* insert the new tag into its parent node
*/
- return(xmlnode_new_child((x == NULL? start : x), new_tag));
+ return(purple_xmlnode_new_child((x == NULL? start : x), new_tag));
}
/*
@@ -459,16 +459,28 @@ static xmlnode *insert_tag_to_parent_tag(xmlnode *start, const char *parent_tag,
*/
void jabber_set_info(PurpleConnection *gc, const char *info)
{
- PurpleStoredImage *img;
+ PurpleImage *img;
JabberIq *iq;
JabberStream *js = purple_connection_get_protocol_data(gc);
- xmlnode *vc_node;
+ PurpleXmlNode *vc_node;
const struct tag_attr *tag_attr;
- /* if we have't grabbed the remote vcard yet, we can't
+ /* if we haven't grabbed the remote vcard yet, we can't
* assume that what we have here is correct */
- if(!js->vcard_fetched)
+ if(!js->vcard_fetched) {
+ PurpleImage *image;
+ g_free(js->initial_avatar_hash);
+ image = purple_buddy_icons_find_account_icon(purple_connection_get_account(gc));
+ if (image != NULL) {
+ js->initial_avatar_hash = jabber_calculate_data_hash(
+ purple_image_get_data(image),
+ purple_image_get_size(image), "sha1");
+ g_object_unref(image);
+ } else {
+ js->initial_avatar_hash = NULL;
+ }
return;
+ }
if (js->vcard_timer) {
purple_timeout_remove(js->vcard_timer);
@@ -481,58 +493,58 @@ void jabber_set_info(PurpleConnection *gc, const char *info)
/*
* Send only if there's actually any *information* to send
*/
- vc_node = info ? xmlnode_from_str(info, -1) : NULL;
+ vc_node = info ? purple_xmlnode_from_str(info, -1) : NULL;
if (vc_node && (!vc_node->name ||
g_ascii_strncasecmp(vc_node->name, "vCard", 5))) {
- xmlnode_free(vc_node);
+ purple_xmlnode_free(vc_node);
vc_node = NULL;
}
- if ((img = purple_buddy_icons_find_account_icon(gc->account))) {
+ if ((img = purple_buddy_icons_find_account_icon(purple_connection_get_account(gc)))) {
gconstpointer avatar_data;
gsize avatar_len;
- xmlnode *photo, *binval, *type;
+ PurpleXmlNode *photo, *binval, *type;
gchar *enc;
if(!vc_node) {
- vc_node = xmlnode_new("vCard");
+ vc_node = purple_xmlnode_new("vCard");
for(tag_attr = vcard_tag_attr_list; tag_attr->attr != NULL; ++tag_attr)
- xmlnode_set_attrib(vc_node, tag_attr->attr, tag_attr->value);
+ purple_xmlnode_set_attrib(vc_node, tag_attr->attr, tag_attr->value);
}
- avatar_data = purple_imgstore_get_data(img);
- avatar_len = purple_imgstore_get_size(img);
+ avatar_data = purple_image_get_data(img);
+ avatar_len = purple_image_get_size(img);
/* Get rid of an old PHOTO if one exists.
* TODO: This may want to be modified to remove all old PHOTO
* children, at the moment some people have managed to get
* multiple PHOTO entries in their vCard. */
- if((photo = xmlnode_get_child(vc_node, "PHOTO"))) {
- xmlnode_free(photo);
+ if((photo = purple_xmlnode_get_child(vc_node, "PHOTO"))) {
+ purple_xmlnode_free(photo);
}
- photo = xmlnode_new_child(vc_node, "PHOTO");
- type = xmlnode_new_child(photo, "TYPE");
- xmlnode_insert_data(type, "image/png", -1);
- binval = xmlnode_new_child(photo, "BINVAL");
+ photo = purple_xmlnode_new_child(vc_node, "PHOTO");
+ type = purple_xmlnode_new_child(photo, "TYPE");
+ purple_xmlnode_insert_data(type, "image/png", -1);
+ binval = purple_xmlnode_new_child(photo, "BINVAL");
enc = purple_base64_encode(avatar_data, avatar_len);
js->avatar_hash =
jabber_calculate_data_hash(avatar_data, avatar_len, "sha1");
- xmlnode_insert_data(binval, enc, -1);
+ purple_xmlnode_insert_data(binval, enc, -1);
g_free(enc);
- purple_imgstore_unref(img);
+ g_object_unref(img);
} else if (vc_node) {
- xmlnode *photo;
+ PurpleXmlNode *photo;
/* TODO: Remove all PHOTO children? (see above note) */
- if ((photo = xmlnode_get_child(vc_node, "PHOTO"))) {
- xmlnode_free(photo);
+ if ((photo = purple_xmlnode_get_child(vc_node, "PHOTO"))) {
+ purple_xmlnode_free(photo);
}
}
if (vc_node != NULL) {
iq = jabber_iq_new(js, JABBER_IQ_SET);
- xmlnode_insert_child(iq->node, vc_node);
+ purple_xmlnode_insert_child(iq->node, vc_node);
jabber_iq_send(iq);
/* Send presence to update vcard-temp:x:update */
@@ -540,12 +552,12 @@ void jabber_set_info(PurpleConnection *gc, const char *info)
}
}
-void jabber_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
+void jabber_set_buddy_icon(PurpleConnection *gc, PurpleImage *img)
{
PurpleAccount *account = purple_connection_get_account(gc);
/* Publish the avatar as specified in XEP-0084 */
- jabber_avatar_set(gc->proto_data, img);
+ jabber_avatar_set(purple_connection_get_protocol_data(gc), img);
/* Set the image in our vCard */
jabber_set_info(gc, purple_account_get_user_info(account));
@@ -563,17 +575,17 @@ void jabber_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
static void
jabber_format_info(PurpleConnection *gc, PurpleRequestFields *fields)
{
- xmlnode *vc_node;
+ PurpleXmlNode *vc_node;
PurpleRequestField *field;
const char *text;
char *p;
const struct vcard_template *vc_tp;
const struct tag_attr *tag_attr;
- vc_node = xmlnode_new("vCard");
+ vc_node = purple_xmlnode_new("vCard");
for(tag_attr = vcard_tag_attr_list; tag_attr->attr != NULL; ++tag_attr)
- xmlnode_set_attrib(vc_node, tag_attr->attr, tag_attr->value);
+ purple_xmlnode_set_attrib(vc_node, tag_attr->attr, tag_attr->value);
for (vc_tp = vcard_template_data; vc_tp->label != NULL; vc_tp++) {
if (*vc_tp->label == '\0')
@@ -584,23 +596,23 @@ jabber_format_info(PurpleConnection *gc, PurpleRequestFields *fields)
if (text != NULL && *text != '\0') {
- xmlnode *xp;
+ PurpleXmlNode *xp;
purple_debug_info("jabber", "Setting %s to '%s'\n", vc_tp->tag, text);
if ((xp = insert_tag_to_parent_tag(vc_node,
NULL, vc_tp->tag)) != NULL) {
- xmlnode_insert_data(xp, text, -1);
+ purple_xmlnode_insert_data(xp, text, -1);
}
}
}
- p = xmlnode_to_str(vc_node, NULL);
- xmlnode_free(vc_node);
+ p = purple_xmlnode_to_str(vc_node, NULL);
+ purple_xmlnode_free(vc_node);
purple_account_set_user_info(purple_connection_get_account(gc), p);
- serv_set_info(gc, p);
+ purple_serv_set_info(gc, p);
g_free(p);
}
@@ -621,7 +633,7 @@ void jabber_setup_set_info(PurplePluginAction *action)
const struct vcard_template *vc_tp;
const char *user_info;
char *cdata = NULL;
- xmlnode *x_vc_data = NULL;
+ PurpleXmlNode *x_vc_data = NULL;
fields = purple_request_fields_new();
group = purple_request_field_group_new(NULL);
@@ -630,27 +642,27 @@ void jabber_setup_set_info(PurplePluginAction *action)
/*
* Get existing, XML-formatted, user info
*/
- if((user_info = purple_account_get_user_info(gc->account)) != NULL)
- x_vc_data = xmlnode_from_str(user_info, -1);
+ if((user_info = purple_account_get_user_info(purple_connection_get_account(gc))) != NULL)
+ x_vc_data = purple_xmlnode_from_str(user_info, -1);
/*
* Set up GSLists for edit with labels from "template," data from user info
*/
for(vc_tp = vcard_template_data; vc_tp->label != NULL; ++vc_tp) {
- xmlnode *data_node;
+ PurpleXmlNode *data_node;
if((vc_tp->label)[0] == '\0')
continue;
if (x_vc_data != NULL) {
if(vc_tp->ptag == NULL) {
- data_node = xmlnode_get_child(x_vc_data, vc_tp->tag);
+ data_node = purple_xmlnode_get_child(x_vc_data, vc_tp->tag);
} else {
gchar *tag = g_strdup_printf("%s/%s", vc_tp->ptag, vc_tp->tag);
- data_node = xmlnode_get_child(x_vc_data, tag);
+ data_node = purple_xmlnode_get_child(x_vc_data, tag);
g_free(tag);
}
if(data_node)
- cdata = xmlnode_get_data(data_node);
+ cdata = purple_xmlnode_get_data(data_node);
}
if(strcmp(vc_tp->tag, "DESC") == 0) {
@@ -670,7 +682,7 @@ void jabber_setup_set_info(PurplePluginAction *action)
}
if(x_vc_data != NULL)
- xmlnode_free(x_vc_data);
+ purple_xmlnode_free(x_vc_data);
purple_request_fields(gc, _("Edit XMPP vCard"),
_("Edit XMPP vCard"),
@@ -679,7 +691,7 @@ void jabber_setup_set_info(PurplePluginAction *action)
fields,
_("Save"), G_CALLBACK(jabber_format_info),
_("Cancel"), NULL,
- purple_connection_get_account(gc), NULL, NULL,
+ purple_request_cpar_from_connection(gc),
gc);
}
@@ -719,11 +731,16 @@ add_jbr_info(JabberBuddyInfo *jbi, const char *resource,
g_strdup_printf("%s%s%s", jbr->client.name,
(jbr->client.version ? " " : ""),
(jbr->client.version ? jbr->client.version : ""));
- purple_notify_user_info_prepend_pair(user_info, _("Client"), tmp);
+ /* TODO: Check whether it's correct to call prepend_pair_html,
+ or if we should be using prepend_pair_plaintext */
+ purple_notify_user_info_prepend_pair_html(user_info, _("Client"), tmp);
g_free(tmp);
- if (jbr->client.os)
- purple_notify_user_info_prepend_pair(user_info, _("Operating System"), jbr->client.os);
+ if (jbr->client.os) {
+ /* TODO: Check whether it's correct to call prepend_pair_html,
+ or if we should be using prepend_pair_plaintext */
+ purple_notify_user_info_prepend_pair_html(user_info, _("Operating System"), jbr->client.os);
+ }
}
if (jbr && jbr->tz_off != PURPLE_NO_TZ_OFF) {
@@ -739,13 +756,13 @@ add_jbr_info(JabberBuddyInfo *jbi, const char *resource,
jbr->tz_off < 0 ? '-' : '+',
abs(jbr->tz_off / (60*60)),
abs((jbr->tz_off % (60*60)) / 60));
- purple_notify_user_info_prepend_pair(user_info, _("Local Time"), timestamp);
+ purple_notify_user_info_prepend_pair_plaintext(user_info, _("Local Time"), timestamp);
g_free(timestamp);
}
if (jbir && jbir->idle_seconds > 0) {
char *idle = purple_str_seconds_to_string(jbir->idle_seconds);
- purple_notify_user_info_prepend_pair(user_info, _("Idle"), idle);
+ purple_notify_user_info_prepend_pair_plaintext(user_info, _("Idle"), idle);
g_free(idle);
}
@@ -767,15 +784,15 @@ add_jbr_info(JabberBuddyInfo *jbi, const char *resource,
tmp = g_strdup_printf("%s%s%s", (status_name ? status_name : ""),
((status_name && purdy) ? ": " : ""),
(purdy ? purdy : ""));
- purple_notify_user_info_prepend_pair(user_info, _("Status"), tmp);
+ purple_notify_user_info_prepend_pair_html(user_info, _("Status"), tmp);
g_snprintf(priority, sizeof(priority), "%d", jbr->priority);
- purple_notify_user_info_prepend_pair(user_info, _("Priority"), priority);
+ purple_notify_user_info_prepend_pair_plaintext(user_info, _("Priority"), priority);
g_free(tmp);
g_free(purdy);
} else {
- purple_notify_user_info_prepend_pair(user_info, _("Status"), _("Unknown"));
+ purple_notify_user_info_prepend_pair_plaintext(user_info, _("Status"), _("Unknown"));
}
}
@@ -794,7 +811,7 @@ static void jabber_buddy_info_show_if_ready(JabberBuddyInfo *jbi)
resource_name = jabber_get_resource(jbi->jid);
/* If we have one or more pairs from the vcard, put a section break above it */
- if (purple_notify_user_info_get_entries(user_info))
+ if (g_queue_get_length(purple_notify_user_info_get_entries(user_info)))
purple_notify_user_info_prepend_section_break(user_info);
/* Add the information about the user's resource(s) */
@@ -815,8 +832,11 @@ static void jabber_buddy_info_show_if_ready(JabberBuddyInfo *jbi)
add_jbr_info(jbi, jbr->name, jbr);
- if (jbr->name)
- purple_notify_user_info_prepend_pair(user_info, _("Resource"), jbr->name);
+ if (jbr->name) {
+ /* TODO: Check whether it's correct to call prepend_pair_html,
+ or if we should be using prepend_pair_plaintext */
+ purple_notify_user_info_prepend_pair_html(user_info, _("Resource"), jbr->name);
+ }
}
}
@@ -836,7 +856,7 @@ static void jabber_buddy_info_show_if_ready(JabberBuddyInfo *jbi)
title = _("Logged Off");
message = g_strdup_printf(_("%s ago"), last);
}
- purple_notify_user_info_prepend_pair(user_info, title, message);
+ purple_notify_user_info_prepend_pair_plaintext(user_info, title, message);
g_free(last);
g_free(message);
}
@@ -846,7 +866,9 @@ static void jabber_buddy_info_show_if_ready(JabberBuddyInfo *jbi)
g_strdup_printf("%s%s%s", _("Offline"),
jbi->last_message ? ": " : "",
jbi->last_message ? jbi->last_message : "");
- purple_notify_user_info_prepend_pair(user_info, _("Status"), status);
+ /* TODO: Check whether it's correct to call prepend_pair_html,
+ or if we should be using prepend_pair_plaintext */
+ purple_notify_user_info_prepend_pair_html(user_info, _("Status"), status);
g_free(status);
}
}
@@ -855,9 +877,9 @@ static void jabber_buddy_info_show_if_ready(JabberBuddyInfo *jbi)
purple_notify_userinfo(jbi->js->gc, jbi->jid, user_info, NULL, NULL);
- while (jbi->vcard_imgids) {
- purple_imgstore_unref_by_id(GPOINTER_TO_INT(jbi->vcard_imgids->data));
- jbi->vcard_imgids = g_slist_delete_link(jbi->vcard_imgids, jbi->vcard_imgids);
+ while (jbi->vcard_images) {
+ g_object_unref(jbi->vcard_images->data);
+ jbi->vcard_images = g_slist_delete_link(jbi->vcard_images, jbi->vcard_images);
}
jbi->js->pending_buddy_info_requests = g_slist_remove(jbi->js->pending_buddy_info_requests, jbi);
@@ -899,27 +921,27 @@ set_own_vcard_cb(gpointer data)
static void jabber_vcard_save_mine(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
- xmlnode *vcard, *photo, *binval;
+ PurpleXmlNode *vcard, *photo, *binval;
char *txt, *vcard_hash = NULL;
PurpleAccount *account;
if (type == JABBER_IQ_ERROR) {
- xmlnode *error;
+ PurpleXmlNode *error;
purple_debug_warning("jabber", "Server returned error while retrieving vCard\n");
- error = xmlnode_get_child(packet, "error");
- if (!error || !xmlnode_get_child(error, "item-not-found"))
+ error = purple_xmlnode_get_child(packet, "error");
+ if (!error || !purple_xmlnode_get_child(error, "item-not-found"))
return;
}
account = purple_connection_get_account(js->gc);
- if((vcard = xmlnode_get_child(packet, "vCard")) ||
- (vcard = xmlnode_get_child_with_namespace(packet, "query", "vcard-temp")))
+ if((vcard = purple_xmlnode_get_child(packet, "vCard")) ||
+ (vcard = purple_xmlnode_get_child_with_namespace(packet, "query", "vcard-temp")))
{
- txt = xmlnode_to_str(vcard, NULL);
+ txt = purple_xmlnode_to_str(vcard, NULL);
purple_account_set_user_info(account, txt);
g_free(txt);
} else {
@@ -928,10 +950,10 @@ static void jabber_vcard_save_mine(JabberStream *js, const char *from,
js->vcard_fetched = TRUE;
- if (vcard && (photo = xmlnode_get_child(vcard, "PHOTO")) &&
- (binval = xmlnode_get_child(photo, "BINVAL"))) {
+ if (vcard && (photo = purple_xmlnode_get_child(vcard, "PHOTO")) &&
+ (binval = purple_xmlnode_get_child(photo, "BINVAL"))) {
gsize size;
- char *bintext = xmlnode_get_data(binval);
+ char *bintext = purple_xmlnode_get_data(binval);
if (bintext) {
guchar *data = purple_base64_decode(bintext, &size);
g_free(bintext);
@@ -972,8 +994,8 @@ void jabber_vcard_fetch_mine(JabberStream *js)
{
JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET);
- xmlnode *vcard = xmlnode_new_child(iq->node, "vCard");
- xmlnode_set_namespace(vcard, "vcard-temp");
+ PurpleXmlNode *vcard = purple_xmlnode_new_child(iq->node, "vCard");
+ purple_xmlnode_set_namespace(vcard, "vcard-temp");
jabber_iq_set_callback(iq, jabber_vcard_save_mine, NULL);
jabber_iq_send(iq);
@@ -981,12 +1003,12 @@ void jabber_vcard_fetch_mine(JabberStream *js)
static void jabber_vcard_parse(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
char *bare_jid;
char *text;
char *serverside_alias = NULL;
- xmlnode *vcard;
+ PurpleXmlNode *vcard;
PurpleAccount *account;
JabberBuddyInfo *jbi = data;
PurpleNotifyUserInfo *user_info;
@@ -1006,17 +1028,17 @@ static void jabber_vcard_parse(JabberStream *js, const char *from,
bare_jid = jabber_get_bare_jid(from ? from : purple_account_get_username(account));
/* TODO: Is the query xmlns='vcard-temp' version of this still necessary? */
- if((vcard = xmlnode_get_child(packet, "vCard")) ||
- (vcard = xmlnode_get_child_with_namespace(packet, "query", "vcard-temp"))) {
- xmlnode *child;
+ if((vcard = purple_xmlnode_get_child(packet, "vCard")) ||
+ (vcard = purple_xmlnode_get_child_with_namespace(packet, "query", "vcard-temp"))) {
+ PurpleXmlNode *child;
for(child = vcard->child; child; child = child->next)
{
- xmlnode *child2;
+ PurpleXmlNode *child2;
- if(child->type != XMLNODE_TYPE_TAG)
+ if(child->type != PURPLE_XMLNODE_TYPE_TAG)
continue;
- text = xmlnode_get_data(child);
+ text = purple_xmlnode_get_data(child);
if(text && !strcmp(child->name, "FN")) {
if (!serverside_alias)
serverside_alias = g_strdup(text);
@@ -1027,10 +1049,10 @@ static void jabber_vcard_parse(JabberStream *js, const char *from,
{
char *text2;
- if(child2->type != XMLNODE_TYPE_TAG)
+ if(child2->type != PURPLE_XMLNODE_TYPE_TAG)
continue;
- text2 = xmlnode_get_data(child2);
+ text2 = purple_xmlnode_get_data(child2);
if(text2 && !strcmp(child2->name, "FAMILY")) {
purple_notify_user_info_add_pair_plaintext(user_info, _("Family Name"), text2);
} else if(text2 && !strcmp(child2->name, "GIVEN")) {
@@ -1058,10 +1080,10 @@ static void jabber_vcard_parse(JabberStream *js, const char *from,
{
char *text2;
- if(child2->type != XMLNODE_TYPE_TAG)
+ if(child2->type != PURPLE_XMLNODE_TYPE_TAG)
continue;
- text2 = xmlnode_get_data(child2);
+ text2 = purple_xmlnode_get_data(child2);
if (text2 == NULL)
continue;
@@ -1102,14 +1124,14 @@ static void jabber_vcard_parse(JabberStream *js, const char *from,
} else if(!strcmp(child->name, "TEL")) {
char *number;
- if((child2 = xmlnode_get_child(child, "NUMBER"))) {
+ if((child2 = purple_xmlnode_get_child(child, "NUMBER"))) {
/* show what kind of number it is */
- number = xmlnode_get_data(child2);
+ number = purple_xmlnode_get_data(child2);
if(number) {
purple_notify_user_info_add_pair_plaintext(user_info, _("Telephone"), number);
g_free(number);
}
- } else if((number = xmlnode_get_data(child))) {
+ } else if((number = purple_xmlnode_get_data(child))) {
/* lots of clients (including purple) do this, but it's
* out of spec */
purple_notify_user_info_add_pair_plaintext(user_info, _("Telephone"), number);
@@ -1117,27 +1139,27 @@ static void jabber_vcard_parse(JabberStream *js, const char *from,
}
} else if(!strcmp(child->name, "EMAIL")) {
char *userid, *escaped;
- if((child2 = xmlnode_get_child(child, "USERID"))) {
+ if((child2 = purple_xmlnode_get_child(child, "USERID"))) {
/* show what kind of email it is */
- userid = xmlnode_get_data(child2);
+ userid = purple_xmlnode_get_data(child2);
if(userid) {
char *mailto;
escaped = g_markup_escape_text(userid, -1);
mailto = g_strdup_printf("<a href=\"mailto:%s\">%s</a>", escaped, escaped);
- purple_notify_user_info_add_pair(user_info, _("Email"), mailto);
+ purple_notify_user_info_add_pair_html(user_info, _("Email"), mailto);
g_free(mailto);
g_free(escaped);
g_free(userid);
}
- } else if((userid = xmlnode_get_data(child))) {
+ } else if((userid = purple_xmlnode_get_data(child))) {
/* lots of clients (including purple) do this, but it's
* out of spec */
char *mailto;
escaped = g_markup_escape_text(userid, -1);
mailto = g_strdup_printf("<a href=\"mailto:%s\">%s</a>", escaped, escaped);
- purple_notify_user_info_add_pair(user_info, _("Email"), mailto);
+ purple_notify_user_info_add_pair_html(user_info, _("Email"), mailto);
g_free(mailto);
g_free(escaped);
@@ -1148,10 +1170,10 @@ static void jabber_vcard_parse(JabberStream *js, const char *from,
{
char *text2;
- if(child2->type != XMLNODE_TYPE_TAG)
+ if(child2->type != PURPLE_XMLNODE_TYPE_TAG)
continue;
- text2 = xmlnode_get_data(child2);
+ text2 = purple_xmlnode_get_data(child2);
if(text2 && !strcmp(child2->name, "ORGNAME")) {
purple_notify_user_info_add_pair_plaintext(user_info, _("Organization Name"), text2);
} else if(text2 && !strcmp(child2->name, "ORGUNIT")) {
@@ -1168,23 +1190,29 @@ static void jabber_vcard_parse(JabberStream *js, const char *from,
} else if(!strcmp(child->name, "PHOTO") ||
!strcmp(child->name, "LOGO")) {
char *bintext = NULL;
- xmlnode *binval;
+ PurpleXmlNode *binval;
- if ((binval = xmlnode_get_child(child, "BINVAL")) &&
- (bintext = xmlnode_get_data(binval))) {
+ if ((binval = purple_xmlnode_get_child(child, "BINVAL")) &&
+ (bintext = purple_xmlnode_get_data(binval))) {
gsize size;
guchar *data;
gboolean photo = (strcmp(child->name, "PHOTO") == 0);
data = purple_base64_decode(bintext, &size);
if (data) {
+ PurpleImage *img;
+ guint img_id;
char *img_text;
char *hash;
- jbi->vcard_imgids = g_slist_prepend(jbi->vcard_imgids, GINT_TO_POINTER(purple_imgstore_add_with_id(g_memdup(data, size), size, "logo.png")));
- img_text = g_strdup_printf("<img id='%d'>", GPOINTER_TO_INT(jbi->vcard_imgids->data));
+ img = purple_image_new_from_data(g_memdup(data, size), size);
+ img_id = purple_image_store_add(img);
+
+ jbi->vcard_images = g_slist_prepend(jbi->vcard_images, img);
+ img_text = g_strdup_printf("<img src='"
+ PURPLE_IMAGE_STORE_PROTOCOL "%u'>", img_id);
- purple_notify_user_info_add_pair(user_info, (photo ? _("Photo") : _("Logo")), img_text);
+ purple_notify_user_info_add_pair_html(user_info, (photo ? _("Photo") : _("Logo")), img_text);
hash = jabber_calculate_data_hash(data, size, "sha1");
purple_buddy_icons_set_for_user(account, bare_jid, data, size, hash);
@@ -1201,8 +1229,8 @@ static void jabber_vcard_parse(JabberStream *js, const char *from,
if (serverside_alias) {
PurpleBuddy *b;
/* If we found a serverside alias, set it and tell the core */
- serv_got_alias(js->gc, bare_jid, serverside_alias);
- b = purple_find_buddy(account, bare_jid);
+ purple_serv_got_alias(js->gc, bare_jid, serverside_alias);
+ b = purple_blist_find_buddy(account, bare_jid);
if (b) {
purple_blist_node_set_string((PurpleBlistNode*)b, "servernick", serverside_alias);
}
@@ -1239,10 +1267,10 @@ static gboolean jbir_equal(gconstpointer v1, gconstpointer v2)
static void jabber_version_parse(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
JabberBuddyInfo *jbi = data;
- xmlnode *query;
+ PurpleXmlNode *query;
char *resource_name;
g_return_if_fail(jbi != NULL);
@@ -1256,18 +1284,18 @@ static void jabber_version_parse(JabberStream *js, const char *from,
if(resource_name) {
if (type == JABBER_IQ_RESULT) {
- if((query = xmlnode_get_child(packet, "query"))) {
+ if((query = purple_xmlnode_get_child(packet, "query"))) {
JabberBuddyResource *jbr = jabber_buddy_find_resource(jbi->jb, resource_name);
if(jbr) {
- xmlnode *node;
- if((node = xmlnode_get_child(query, "name"))) {
- jbr->client.name = xmlnode_get_data(node);
+ PurpleXmlNode *node;
+ if((node = purple_xmlnode_get_child(query, "name"))) {
+ jbr->client.name = purple_xmlnode_get_data(node);
}
- if((node = xmlnode_get_child(query, "version"))) {
- jbr->client.version = xmlnode_get_data(node);
+ if((node = purple_xmlnode_get_child(query, "version"))) {
+ jbr->client.version = purple_xmlnode_get_data(node);
}
- if((node = xmlnode_get_child(query, "os"))) {
- jbr->client.os = xmlnode_get_data(node);
+ if((node = purple_xmlnode_get_child(query, "os"))) {
+ jbr->client.os = purple_xmlnode_get_data(node);
}
}
}
@@ -1280,10 +1308,10 @@ static void jabber_version_parse(JabberStream *js, const char *from,
static void jabber_last_parse(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
JabberBuddyInfo *jbi = data;
- xmlnode *query;
+ PurpleXmlNode *query;
char *resource_name;
const char *seconds;
@@ -1298,8 +1326,8 @@ static void jabber_last_parse(JabberStream *js, const char *from,
if(resource_name) {
if (type == JABBER_IQ_RESULT) {
- if((query = xmlnode_get_child(packet, "query"))) {
- seconds = xmlnode_get_attrib(query, "seconds");
+ if((query = purple_xmlnode_get_child(packet, "query"))) {
+ seconds = purple_xmlnode_get_attrib(query, "seconds");
if(seconds) {
char *end = NULL;
long sec = strtol(seconds, &end, 10);
@@ -1338,7 +1366,7 @@ static void jabber_last_parse(JabberStream *js, const char *from,
if (jbr ==
jabber_buddy_find_resource(jb, NULL)) {
- purple_prpl_got_user_idle(js->gc->account,
+ purple_prpl_got_user_idle(purple_connection_get_account(js->gc),
buddy_name, jbr->idle, jbr->idle);
}
}
@@ -1358,10 +1386,10 @@ static void jabber_last_parse(JabberStream *js, const char *from,
static void jabber_last_offline_parse(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
JabberBuddyInfo *jbi = data;
- xmlnode *query;
+ PurpleXmlNode *query;
const char *seconds;
g_return_if_fail(jbi != NULL);
@@ -1369,8 +1397,8 @@ static void jabber_last_offline_parse(JabberStream *js, const char *from,
jabber_buddy_info_remove_id(jbi, id);
if (type == JABBER_IQ_RESULT) {
- if((query = xmlnode_get_child(packet, "query"))) {
- seconds = xmlnode_get_attrib(query, "seconds");
+ if((query = purple_xmlnode_get_child(packet, "query"))) {
+ seconds = purple_xmlnode_get_attrib(query, "seconds");
if(seconds) {
char *end = NULL;
long sec = strtol(seconds, &end, 10);
@@ -1378,7 +1406,7 @@ static void jabber_last_offline_parse(JabberStream *js, const char *from,
jbi->last_seconds = sec;
}
}
- jbi->last_message = xmlnode_get_data(query);
+ jbi->last_message = purple_xmlnode_get_data(query);
}
}
@@ -1387,7 +1415,7 @@ static void jabber_last_offline_parse(JabberStream *js, const char *from,
static void jabber_time_parse(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
JabberBuddyInfo *jbi = data;
JabberBuddyResource *jbr;
@@ -1405,9 +1433,9 @@ static void jabber_time_parse(JabberStream *js, const char *from,
g_free(resource_name);
if (jbr) {
if (type == JABBER_IQ_RESULT) {
- xmlnode *time = xmlnode_get_child(packet, "time");
- xmlnode *tzo = time ? xmlnode_get_child(time, "tzo") : NULL;
- char *tzo_data = tzo ? xmlnode_get_data(tzo) : NULL;
+ PurpleXmlNode *time = purple_xmlnode_get_child(packet, "time");
+ PurpleXmlNode *tzo = time ? purple_xmlnode_get_child(time, "tzo") : NULL;
+ char *tzo_data = tzo ? purple_xmlnode_get_data(tzo) : NULL;
if (tzo_data) {
char *c = tzo_data;
int hours, minutes;
@@ -1516,7 +1544,7 @@ dispatch_queries_for_resource(JabberStream *js, JabberBuddyInfo *jbi,
if(!jbr->client.name) {
iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:version");
- xmlnode_set_attrib(iq->node, "to", to);
+ purple_xmlnode_set_attrib(iq->node, "to", to);
jabber_iq_set_callback(iq, jabber_version_parse, jbi);
jbi->ids = g_slist_prepend(jbi->ids, g_strdup(iq->id));
jabber_iq_send(iq);
@@ -1529,7 +1557,7 @@ dispatch_queries_for_resource(JabberStream *js, JabberBuddyInfo *jbi,
* office. */
if(!_client_is_blacklisted(jbr, NS_LAST_ACTIVITY)) {
iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_LAST_ACTIVITY);
- xmlnode_set_attrib(iq->node, "to", to);
+ purple_xmlnode_set_attrib(iq->node, "to", to);
jabber_iq_set_callback(iq, jabber_last_parse, jbi);
jbi->ids = g_slist_prepend(jbi->ids, g_strdup(iq->id));
jabber_iq_send(iq);
@@ -1538,11 +1566,11 @@ dispatch_queries_for_resource(JabberStream *js, JabberBuddyInfo *jbi,
if (jbr->tz_off == PURPLE_NO_TZ_OFF &&
(!jbr->caps.info ||
jabber_resource_has_capability(jbr, NS_ENTITY_TIME))) {
- xmlnode *child;
+ PurpleXmlNode *child;
iq = jabber_iq_new(js, JABBER_IQ_GET);
- xmlnode_set_attrib(iq->node, "to", to);
- child = xmlnode_new_child(iq->node, "time");
- xmlnode_set_namespace(child, NS_ENTITY_TIME);
+ purple_xmlnode_set_attrib(iq->node, "to", to);
+ child = purple_xmlnode_new_child(iq->node, "time");
+ purple_xmlnode_set_namespace(child, NS_ENTITY_TIME);
jabber_iq_set_callback(iq, jabber_time_parse, jbi);
jbi->ids = g_slist_prepend(jbi->ids, g_strdup(iq->id));
jabber_iq_send(iq);
@@ -1554,7 +1582,7 @@ dispatch_queries_for_resource(JabberStream *js, JabberBuddyInfo *jbi,
static void jabber_buddy_get_info_for_jid(JabberStream *js, const char *jid)
{
JabberIq *iq;
- xmlnode *vcard;
+ PurpleXmlNode *vcard;
GList *resources;
JabberBuddy *jb;
JabberBuddyInfo *jbi;
@@ -1579,9 +1607,9 @@ static void jabber_buddy_get_info_for_jid(JabberStream *js, const char *jid)
iq = jabber_iq_new(js, JABBER_IQ_GET);
- xmlnode_set_attrib(iq->node, "to", jid);
- vcard = xmlnode_new_child(iq->node, "vCard");
- xmlnode_set_namespace(vcard, "vcard-temp");
+ purple_xmlnode_set_attrib(iq->node, "to", jid);
+ vcard = purple_xmlnode_new_child(iq->node, "vCard");
+ purple_xmlnode_set_namespace(vcard, "vcard-temp");
jabber_iq_set_callback(iq, jabber_vcard_parse, jbi);
jbi->ids = g_slist_prepend(jbi->ids, g_strdup(iq->id));
@@ -1597,7 +1625,7 @@ static void jabber_buddy_get_info_for_jid(JabberStream *js, const char *jid)
} else {
/* user is offline, send a jabber:iq:last to find out last time online */
iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_LAST_ACTIVITY);
- xmlnode_set_attrib(iq->node, "to", jid);
+ purple_xmlnode_set_attrib(iq->node, "to", jid);
jabber_iq_set_callback(iq, jabber_last_offline_parse, jbi);
jbi->ids = g_slist_prepend(jbi->ids, g_strdup(iq->id));
jabber_iq_send(iq);
@@ -1643,7 +1671,7 @@ static void jabber_buddy_set_invisibility(JabberStream *js, const char *who,
PurpleAccount *account;
PurpleStatus *status;
JabberBuddy *jb = jabber_buddy_find(js, who, TRUE);
- xmlnode *presence;
+ PurpleXmlNode *presence;
JabberBuddyState state;
char *msg;
int priority;
@@ -1657,16 +1685,16 @@ static void jabber_buddy_set_invisibility(JabberStream *js, const char *who,
g_free(msg);
- xmlnode_set_attrib(presence, "to", who);
+ purple_xmlnode_set_attrib(presence, "to", who);
if(invisible) {
- xmlnode_set_attrib(presence, "type", "invisible");
+ purple_xmlnode_set_attrib(presence, "type", "invisible");
jb->invisible |= JABBER_INVIS_BUDDY;
} else {
jb->invisible &= ~JABBER_INVIS_BUDDY;
}
jabber_send(js, presence);
- xmlnode_free(presence);
+ purple_xmlnode_free(presence);
}
static void jabber_buddy_make_invisible(PurpleBlistNode *node, gpointer data)
@@ -1675,7 +1703,7 @@ static void jabber_buddy_make_invisible(PurpleBlistNode *node, gpointer data)
PurpleConnection *gc;
JabberStream *js;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *) node;
gc = purple_account_get_connection(purple_buddy_get_account(buddy));
@@ -1690,7 +1718,7 @@ static void jabber_buddy_make_visible(PurpleBlistNode *node, gpointer data)
PurpleConnection *gc;
JabberStream *js;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *) node;
gc = purple_account_get_connection(purple_buddy_get_account(buddy));
@@ -1722,7 +1750,7 @@ jabber_buddy_cancel_presence_notification(PurpleBlistNode *node,
const gchar *name;
char *msg;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *) node;
name = purple_buddy_get_name(buddy);
@@ -1732,7 +1760,7 @@ jabber_buddy_cancel_presence_notification(PurpleBlistNode *node,
msg = g_strdup_printf(_("%s will no longer be able to see your status "
"updates. Do you want to continue?"), name);
purple_request_yes_no(gc, NULL, _("Cancel Presence Notification"),
- msg, 0 /* Yes */, account, name, NULL, buddy,
+ msg, 0 /* Yes */, purple_request_cpar_from_account(account), buddy,
cancel_presence_notification, NULL /* Do nothing */);
g_free(msg);
}
@@ -1743,7 +1771,7 @@ static void jabber_buddy_rerequest_auth(PurpleBlistNode *node, gpointer data)
PurpleConnection *gc;
JabberStream *js;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *) node;
gc = purple_account_get_connection(purple_buddy_get_account(buddy));
@@ -1759,7 +1787,7 @@ static void jabber_buddy_unsubscribe(PurpleBlistNode *node, gpointer data)
PurpleConnection *gc;
JabberStream *js;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *) node;
gc = purple_account_get_connection(purple_buddy_get_account(buddy));
@@ -1769,7 +1797,7 @@ static void jabber_buddy_unsubscribe(PurpleBlistNode *node, gpointer data)
}
static void jabber_buddy_login(PurpleBlistNode *node, gpointer data) {
- if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ if(PURPLE_IS_BUDDY(node)) {
/* simply create a directed presence of the current status */
PurpleBuddy *buddy = (PurpleBuddy *) node;
PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy));
@@ -1777,7 +1805,7 @@ static void jabber_buddy_login(PurpleBlistNode *node, gpointer data) {
PurpleAccount *account = purple_connection_get_account(gc);
PurplePresence *gpresence = purple_account_get_presence(account);
PurpleStatus *status = purple_presence_get_active_status(gpresence);
- xmlnode *presence;
+ PurpleXmlNode *presence;
JabberBuddyState state;
char *msg;
int priority;
@@ -1787,27 +1815,27 @@ static void jabber_buddy_login(PurpleBlistNode *node, gpointer data) {
g_free(msg);
- xmlnode_set_attrib(presence, "to", purple_buddy_get_name(buddy));
+ purple_xmlnode_set_attrib(presence, "to", purple_buddy_get_name(buddy));
jabber_send(js, presence);
- xmlnode_free(presence);
+ purple_xmlnode_free(presence);
}
}
static void jabber_buddy_logout(PurpleBlistNode *node, gpointer data) {
- if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ if(PURPLE_IS_BUDDY(node)) {
/* simply create a directed unavailable presence */
PurpleBuddy *buddy = (PurpleBuddy *) node;
PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy));
JabberStream *js = purple_connection_get_protocol_data(gc);
- xmlnode *presence;
+ PurpleXmlNode *presence;
presence = jabber_presence_create_js(js, JABBER_BUDDY_STATE_UNAVAILABLE, NULL, 0);
- xmlnode_set_attrib(presence, "to", purple_buddy_get_name(buddy));
+ purple_xmlnode_set_attrib(presence, "to", purple_buddy_get_name(buddy));
jabber_send(js, presence);
- xmlnode_free(presence);
+ purple_xmlnode_free(presence);
}
}
@@ -1909,7 +1937,7 @@ static GList *jabber_buddy_menu(PurpleBuddy *buddy)
GList *
jabber_blist_node_menu(PurpleBlistNode *node)
{
- if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ if(PURPLE_IS_BUDDY(node)) {
return jabber_buddy_menu((PurpleBuddy *) node);
} else {
return NULL;
@@ -1926,42 +1954,42 @@ static void user_search_result_add_buddy_cb(PurpleConnection *gc, GList *row, vo
static void user_search_result_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
PurpleNotifySearchResults *results;
PurpleNotifySearchColumn *column;
- xmlnode *x, *query, *item, *field;
+ PurpleXmlNode *x, *query, *item, *field;
/* XXX error checking? */
- if(!(query = xmlnode_get_child(packet, "query")))
+ if(!(query = purple_xmlnode_get_child(packet, "query")))
return;
results = purple_notify_searchresults_new();
- if((x = xmlnode_get_child_with_namespace(query, "x", "jabber:x:data"))) {
- xmlnode *reported;
+ if((x = purple_xmlnode_get_child_with_namespace(query, "x", "jabber:x:data"))) {
+ PurpleXmlNode *reported;
GSList *column_vars = NULL;
purple_debug_info("jabber", "new-skool\n");
- if((reported = xmlnode_get_child(x, "reported"))) {
- xmlnode *field = xmlnode_get_child(reported, "field");
+ if((reported = purple_xmlnode_get_child(x, "reported"))) {
+ PurpleXmlNode *field = purple_xmlnode_get_child(reported, "field");
while(field) {
- const char *var = xmlnode_get_attrib(field, "var");
- const char *label = xmlnode_get_attrib(field, "label");
+ const char *var = purple_xmlnode_get_attrib(field, "var");
+ const char *label = purple_xmlnode_get_attrib(field, "label");
if(var) {
column = purple_notify_searchresults_column_new(label ? label : var);
purple_notify_searchresults_column_add(results, column);
column_vars = g_slist_append(column_vars, (char *)var);
}
- field = xmlnode_get_next_twin(field);
+ field = purple_xmlnode_get_next_twin(field);
}
}
- item = xmlnode_get_child(x, "item");
+ item = purple_xmlnode_get_child(x, "item");
while(item) {
GList *row = NULL;
GSList *l;
- xmlnode *valuenode;
+ PurpleXmlNode *valuenode;
const char *var;
for (l = column_vars; l != NULL; l = l->next) {
@@ -1969,15 +1997,15 @@ static void user_search_result_cb(JabberStream *js, const char *from,
* Build a row containing the strings that correspond
* to each column of the search results.
*/
- for (field = xmlnode_get_child(item, "field");
+ for (field = purple_xmlnode_get_child(item, "field");
field != NULL;
- field = xmlnode_get_next_twin(field))
+ field = purple_xmlnode_get_next_twin(field))
{
- if ((var = xmlnode_get_attrib(field, "var")) &&
+ if ((var = purple_xmlnode_get_attrib(field, "var")) &&
!strcmp(var, l->data) &&
- (valuenode = xmlnode_get_child(field, "value")))
+ (valuenode = purple_xmlnode_get_child(field, "value")))
{
- char *value = xmlnode_get_data(valuenode);
+ char *value = purple_xmlnode_get_data(valuenode);
row = g_list_append(row, value);
break;
}
@@ -1987,7 +2015,7 @@ static void user_search_result_cb(JabberStream *js, const char *from,
row = g_list_append(row, NULL);
}
purple_notify_searchresults_row_add(results, row);
- item = xmlnode_get_next_twin(item);
+ item = purple_xmlnode_get_next_twin(item);
}
g_slist_free(column_vars);
@@ -2006,23 +2034,23 @@ static void user_search_result_cb(JabberStream *js, const char *from,
column = purple_notify_searchresults_column_new(_("Email"));
purple_notify_searchresults_column_add(results, column);
- for(item = xmlnode_get_child(query, "item"); item; item = xmlnode_get_next_twin(item)) {
+ for(item = purple_xmlnode_get_child(query, "item"); item; item = purple_xmlnode_get_next_twin(item)) {
const char *jid;
- xmlnode *node;
+ PurpleXmlNode *node;
GList *row = NULL;
- if(!(jid = xmlnode_get_attrib(item, "jid")))
+ if(!(jid = purple_xmlnode_get_attrib(item, "jid")))
continue;
row = g_list_append(row, g_strdup(jid));
- node = xmlnode_get_child(item, "first");
- row = g_list_append(row, node ? xmlnode_get_data(node) : NULL);
- node = xmlnode_get_child(item, "last");
- row = g_list_append(row, node ? xmlnode_get_data(node) : NULL);
- node = xmlnode_get_child(item, "nick");
- row = g_list_append(row, node ? xmlnode_get_data(node) : NULL);
- node = xmlnode_get_child(item, "email");
- row = g_list_append(row, node ? xmlnode_get_data(node) : NULL);
+ node = purple_xmlnode_get_child(item, "first");
+ row = g_list_append(row, node ? purple_xmlnode_get_data(node) : NULL);
+ node = purple_xmlnode_get_child(item, "last");
+ row = g_list_append(row, node ? purple_xmlnode_get_data(node) : NULL);
+ node = purple_xmlnode_get_child(item, "nick");
+ row = g_list_append(row, node ? purple_xmlnode_get_data(node) : NULL);
+ node = purple_xmlnode_get_child(item, "email");
+ row = g_list_append(row, node ? purple_xmlnode_get_data(node) : NULL);
purple_debug_info("jabber", "row=%p\n", row);
purple_notify_searchresults_row_add(results, row);
}
@@ -2034,9 +2062,9 @@ static void user_search_result_cb(JabberStream *js, const char *from,
purple_notify_searchresults(js->gc, NULL, NULL, _("The following are the results of your search"), results, NULL, NULL);
}
-static void user_search_x_data_cb(JabberStream *js, xmlnode *result, gpointer data)
+static void user_search_x_data_cb(JabberStream *js, PurpleXmlNode *result, gpointer data)
{
- xmlnode *query;
+ PurpleXmlNode *query;
JabberIq *iq;
char *dir_server = data;
const char *type;
@@ -2044,19 +2072,19 @@ static void user_search_x_data_cb(JabberStream *js, xmlnode *result, gpointer da
/* if they've cancelled the search, we're
* just going to get an error if we send
* a cancel, so skip it */
- type = xmlnode_get_attrib(result, "type");
+ type = purple_xmlnode_get_attrib(result, "type");
if(type && !strcmp(type, "cancel")) {
g_free(dir_server);
return;
}
iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:search");
- query = xmlnode_get_child(iq->node, "query");
+ query = purple_xmlnode_get_child(iq->node, "query");
- xmlnode_insert_child(query, result);
+ purple_xmlnode_insert_child(query, result);
jabber_iq_set_callback(iq, user_search_result_cb, NULL);
- xmlnode_set_attrib(iq->node, "to", dir_server);
+ purple_xmlnode_set_attrib(iq->node, "to", dir_server);
jabber_iq_send(iq);
g_free(dir_server);
}
@@ -2076,11 +2104,11 @@ static void user_search_cb(struct user_search_info *usi, PurpleRequestFields *fi
{
JabberStream *js = usi->js;
JabberIq *iq;
- xmlnode *query;
+ PurpleXmlNode *query;
GList *groups, *flds;
iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:search");
- query = xmlnode_get_child(iq->node, "query");
+ query = purple_xmlnode_get_child(iq->node, "query");
for(groups = purple_request_fields_get_groups(fields); groups; groups = groups->next) {
for(flds = purple_request_field_group_get_fields(groups->data);
@@ -2090,14 +2118,14 @@ static void user_search_cb(struct user_search_info *usi, PurpleRequestFields *fi
const char *value = purple_request_field_string_get_value(field);
if(value && (!strcmp(id, "first") || !strcmp(id, "last") || !strcmp(id, "nick") || !strcmp(id, "email"))) {
- xmlnode *y = xmlnode_new_child(query, id);
- xmlnode_insert_data(y, value, -1);
+ PurpleXmlNode *y = purple_xmlnode_new_child(query, id);
+ purple_xmlnode_insert_data(y, value, -1);
}
}
}
jabber_iq_set_callback(iq, user_search_result_cb, NULL);
- xmlnode_set_attrib(iq->node, "to", usi->directory_server);
+ purple_xmlnode_set_attrib(iq->node, "to", usi->directory_server);
jabber_iq_send(iq);
g_free(usi->directory_server);
@@ -2124,9 +2152,9 @@ static const char * jabber_user_dir_comments [] = {
static void user_search_fields_result_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
- xmlnode *query, *x;
+ PurpleXmlNode *query, *x;
if (!from)
return;
@@ -2138,22 +2166,23 @@ static void user_search_fields_result_cb(JabberStream *js, const char *from,
msg = g_strdup(_("Unknown error"));
purple_notify_error(js->gc, _("Directory Query Failed"),
- _("Could not query the directory server."), msg);
+ _("Could not query the directory server."), msg,
+ purple_request_cpar_from_connection(js->gc));
g_free(msg);
return;
}
- if(!(query = xmlnode_get_child(packet, "query")))
+ if(!(query = purple_xmlnode_get_child(packet, "query")))
return;
- if((x = xmlnode_get_child_with_namespace(query, "x", "jabber:x:data"))) {
+ if((x = purple_xmlnode_get_child_with_namespace(query, "x", "jabber:x:data"))) {
jabber_x_data_request(js, x, user_search_x_data_cb, g_strdup(from));
return;
} else {
struct user_search_info *usi;
- xmlnode *instnode;
+ PurpleXmlNode *instnode;
char *instructions = NULL;
PurpleRequestFields *fields;
PurpleRequestFieldGroup *group;
@@ -2164,9 +2193,9 @@ static void user_search_fields_result_cb(JabberStream *js, const char *from,
group = purple_request_field_group_new(NULL);
purple_request_fields_add_group(fields, group);
- if((instnode = xmlnode_get_child(query, "instructions")))
+ if((instnode = purple_xmlnode_get_child(query, "instructions")))
{
- char *tmp = xmlnode_get_data(instnode);
+ char *tmp = purple_xmlnode_get_data(instnode);
if(tmp)
{
@@ -2183,22 +2212,22 @@ static void user_search_fields_result_cb(JabberStream *js, const char *from,
"for any matching XMPP users."));
}
- if(xmlnode_get_child(query, "first")) {
+ if(purple_xmlnode_get_child(query, "first")) {
field = purple_request_field_string_new("first", _("First Name"),
NULL, FALSE);
purple_request_field_group_add_field(group, field);
}
- if(xmlnode_get_child(query, "last")) {
+ if(purple_xmlnode_get_child(query, "last")) {
field = purple_request_field_string_new("last", _("Last Name"),
NULL, FALSE);
purple_request_field_group_add_field(group, field);
}
- if(xmlnode_get_child(query, "nick")) {
+ if(purple_xmlnode_get_child(query, "nick")) {
field = purple_request_field_string_new("nick", _("Nickname"),
NULL, FALSE);
purple_request_field_group_add_field(group, field);
}
- if(xmlnode_get_child(query, "email")) {
+ if(purple_xmlnode_get_child(query, "email")) {
field = purple_request_field_string_new("email", _("Email Address"),
NULL, FALSE);
purple_request_field_group_add_field(group, field);
@@ -2212,7 +2241,7 @@ static void user_search_fields_result_cb(JabberStream *js, const char *from,
_("Search for XMPP users"), instructions, fields,
_("Search"), G_CALLBACK(user_search_cb),
_("Cancel"), G_CALLBACK(user_search_cancel_cb),
- purple_connection_get_account(js->gc), NULL, NULL,
+ purple_request_cpar_from_connection(js->gc),
usi);
g_free(instructions);
@@ -2225,7 +2254,9 @@ void jabber_user_search(JabberStream *js, const char *directory)
/* XXX: should probably better validate the directory we're given */
if(!directory || !*directory) {
- purple_notify_error(js->gc, _("Invalid Directory"), _("Invalid Directory"), NULL);
+ purple_notify_error(js->gc, _("Invalid Directory"),
+ _("Invalid Directory"), NULL,
+ purple_request_cpar_from_connection(js->gc));
return;
}
@@ -2233,14 +2264,14 @@ void jabber_user_search(JabberStream *js, const char *directory)
make sure we aren't persisting an old value */
if(js->user_directories && js->user_directories->data &&
!strcmp(directory, js->user_directories->data)) {
- purple_account_set_string(js->gc->account, "user_directory", "");
+ purple_account_set_string(purple_connection_get_account(js->gc), "user_directory", "");
}
else {
- purple_account_set_string(js->gc->account, "user_directory", directory);
+ purple_account_set_string(purple_connection_get_account(js->gc), "user_directory", directory);
}
iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:search");
- xmlnode_set_attrib(iq->node, "to", directory);
+ purple_xmlnode_set_attrib(iq->node, "to", directory);
jabber_iq_set_callback(iq, user_search_fields_result_cb, NULL);
@@ -2251,7 +2282,7 @@ void jabber_user_search_begin(PurplePluginAction *action)
{
PurpleConnection *gc = (PurpleConnection *) action->context;
JabberStream *js = purple_connection_get_protocol_data(gc);
- const char *def_val = purple_account_get_string(js->gc->account, "user_directory", "");
+ const char *def_val = purple_account_get_string(purple_connection_get_account(js->gc), "user_directory", "");
if(!*def_val && js->user_directories)
def_val = js->user_directories->data;
@@ -2261,8 +2292,7 @@ void jabber_user_search_begin(PurplePluginAction *action)
FALSE, FALSE, NULL,
_("Search Directory"), PURPLE_CALLBACK(jabber_user_search),
_("Cancel"), NULL,
- NULL, NULL, NULL,
- js);
+ NULL, js);
}
gboolean
diff --git a/libpurple/protocols/jabber/buddy.h b/libpurple/protocols/jabber/buddy.h
index 3574c8e524..0012ce1667 100644
--- a/libpurple/protocols/jabber/buddy.h
+++ b/libpurple/protocols/jabber/buddy.h
@@ -104,7 +104,7 @@ GList *jabber_blist_node_menu(PurpleBlistNode *node);
void jabber_set_info(PurpleConnection *gc, const char *info);
void jabber_setup_set_info(PurplePluginAction *action);
-void jabber_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img);
+void jabber_set_buddy_icon(PurpleConnection *gc, PurpleImage *img);
void jabber_user_search(JabberStream *js, const char *directory);
void jabber_user_search_begin(PurplePluginAction *);
diff --git a/libpurple/protocols/jabber/caps.c b/libpurple/protocols/jabber/caps.c
index ead8b1a002..e7d88fa7fb 100644
--- a/libpurple/protocols/jabber/caps.c
+++ b/libpurple/protocols/jabber/caps.c
@@ -25,12 +25,14 @@
#include "debug.h"
#include "caps.h"
-#include "cipher.h"
#include "iq.h"
#include "presence.h"
#include "util.h"
#include "xdata.h"
+#include "ciphers/md5hash.h"
+#include "ciphers/sha1hash.h"
+
#define JABBER_CAPS_FILENAME "xmpp-caps.xml"
typedef struct _JabberDataFormField {
@@ -117,7 +119,7 @@ jabber_caps_client_info_destroy(JabberCapsClientInfo *info)
free_string_glist(info->features);
while (info->forms) {
- xmlnode_free(info->forms->data);
+ purple_xmlnode_free(info->forms->data);
info->forms = g_list_delete_link(info->forms, info->forms);
}
@@ -151,49 +153,49 @@ exts_to_xmlnode(gconstpointer key, gconstpointer value, gpointer user_data)
{
const char *identifier = key;
const GList *features = value, *node;
- xmlnode *client = user_data, *ext, *feature;
+ PurpleXmlNode *client = user_data, *ext, *feature;
- ext = xmlnode_new_child(client, "ext");
- xmlnode_set_attrib(ext, "identifier", identifier);
+ ext = purple_xmlnode_new_child(client, "ext");
+ purple_xmlnode_set_attrib(ext, "identifier", identifier);
for (node = features; node; node = node->next) {
- feature = xmlnode_new_child(ext, "feature");
- xmlnode_set_attrib(feature, "var", (const gchar *)node->data);
+ feature = purple_xmlnode_new_child(ext, "feature");
+ purple_xmlnode_set_attrib(feature, "var", (const gchar *)node->data);
}
}
static void jabber_caps_store_client(gpointer key, gpointer value, gpointer user_data) {
const JabberCapsTuple *tuple = key;
const JabberCapsClientInfo *props = value;
- xmlnode *root = user_data;
- xmlnode *client = xmlnode_new_child(root, "client");
+ PurpleXmlNode *root = user_data;
+ PurpleXmlNode *client = purple_xmlnode_new_child(root, "client");
GList *iter;
- xmlnode_set_attrib(client, "node", tuple->node);
- xmlnode_set_attrib(client, "ver", tuple->ver);
+ purple_xmlnode_set_attrib(client, "node", tuple->node);
+ purple_xmlnode_set_attrib(client, "ver", tuple->ver);
if (tuple->hash)
- xmlnode_set_attrib(client, "hash", tuple->hash);
+ purple_xmlnode_set_attrib(client, "hash", tuple->hash);
for(iter = props->identities; iter; iter = g_list_next(iter)) {
JabberIdentity *id = iter->data;
- xmlnode *identity = xmlnode_new_child(client, "identity");
- xmlnode_set_attrib(identity, "category", id->category);
- xmlnode_set_attrib(identity, "type", id->type);
+ PurpleXmlNode *identity = purple_xmlnode_new_child(client, "identity");
+ purple_xmlnode_set_attrib(identity, "category", id->category);
+ purple_xmlnode_set_attrib(identity, "type", id->type);
if (id->name)
- xmlnode_set_attrib(identity, "name", id->name);
+ purple_xmlnode_set_attrib(identity, "name", id->name);
if (id->lang)
- xmlnode_set_attrib(identity, "lang", id->lang);
+ purple_xmlnode_set_attrib(identity, "lang", id->lang);
}
for(iter = props->features; iter; iter = g_list_next(iter)) {
const char *feat = iter->data;
- xmlnode *feature = xmlnode_new_child(client, "feature");
- xmlnode_set_attrib(feature, "var", feat);
+ PurpleXmlNode *feature = purple_xmlnode_new_child(client, "feature");
+ purple_xmlnode_set_attrib(feature, "var", feat);
}
for(iter = props->forms; iter; iter = g_list_next(iter)) {
/* FIXME: See #7814 */
- xmlnode *xdata = iter->data;
- xmlnode_insert_child(client, xmlnode_copy(xdata));
+ PurpleXmlNode *xdata = iter->data;
+ purple_xmlnode_insert_child(client, purple_xmlnode_copy(xdata));
}
/* TODO: Ideally, only save this once-per-node... */
@@ -206,10 +208,10 @@ do_jabber_caps_store(gpointer data)
{
char *str;
int length = 0;
- xmlnode *root = xmlnode_new("capabilities");
+ PurpleXmlNode *root = purple_xmlnode_new("capabilities");
g_hash_table_foreach(capstable, jabber_caps_store_client, root);
- str = xmlnode_to_formatted_str(root, &length);
- xmlnode_free(root);
+ str = purple_xmlnode_to_formatted_str(root, &length);
+ purple_xmlnode_free(root);
purple_util_write_data_to_file(JABBER_CAPS_FILENAME, str, length);
g_free(str);
@@ -227,46 +229,46 @@ schedule_caps_save(void)
static void
jabber_caps_load(void)
{
- xmlnode *capsdata = purple_util_read_xml_from_file(JABBER_CAPS_FILENAME, "XMPP capabilities cache");
- xmlnode *client;
+ PurpleXmlNode *capsdata = purple_util_read_xml_from_file(JABBER_CAPS_FILENAME, "XMPP capabilities cache");
+ PurpleXmlNode *client;
if(!capsdata)
return;
if (!g_str_equal(capsdata->name, "capabilities")) {
- xmlnode_free(capsdata);
+ purple_xmlnode_free(capsdata);
return;
}
for (client = capsdata->child; client; client = client->next) {
- if (client->type != XMLNODE_TYPE_TAG)
+ if (client->type != PURPLE_XMLNODE_TYPE_TAG)
continue;
if (g_str_equal(client->name, "client")) {
JabberCapsClientInfo *value = g_new0(JabberCapsClientInfo, 1);
JabberCapsTuple *key = (JabberCapsTuple*)&value->tuple;
- xmlnode *child;
+ PurpleXmlNode *child;
JabberCapsNodeExts *exts = NULL;
- key->node = g_strdup(xmlnode_get_attrib(client,"node"));
- key->ver = g_strdup(xmlnode_get_attrib(client,"ver"));
- key->hash = g_strdup(xmlnode_get_attrib(client,"hash"));
+ key->node = g_strdup(purple_xmlnode_get_attrib(client,"node"));
+ key->ver = g_strdup(purple_xmlnode_get_attrib(client,"ver"));
+ key->hash = g_strdup(purple_xmlnode_get_attrib(client,"hash"));
/* v1.3 capabilities */
if (key->hash == NULL)
exts = jabber_caps_find_exts_by_node(key->node);
for (child = client->child; child; child = child->next) {
- if (child->type != XMLNODE_TYPE_TAG)
+ if (child->type != PURPLE_XMLNODE_TYPE_TAG)
continue;
if (g_str_equal(child->name, "feature")) {
- const char *var = xmlnode_get_attrib(child, "var");
+ const char *var = purple_xmlnode_get_attrib(child, "var");
if(!var)
continue;
value->features = g_list_append(value->features,g_strdup(var));
} else if (g_str_equal(child->name, "identity")) {
- const char *category = xmlnode_get_attrib(child, "category");
- const char *type = xmlnode_get_attrib(child, "type");
- const char *name = xmlnode_get_attrib(child, "name");
- const char *lang = xmlnode_get_attrib(child, "lang");
+ const char *category = purple_xmlnode_get_attrib(child, "category");
+ const char *type = purple_xmlnode_get_attrib(child, "type");
+ const char *name = purple_xmlnode_get_attrib(child, "name");
+ const char *lang = purple_xmlnode_get_attrib(child, "lang");
JabberIdentity *id;
if (!category || !type)
@@ -283,25 +285,25 @@ jabber_caps_load(void)
/* TODO: See #7814 -- this might cause problems if anyone
* ever actually specifies forms. In fact, for this to
* work properly, that bug needs to be fixed in
- * xmlnode_from_str, not the output version... */
- value->forms = g_list_append(value->forms, xmlnode_copy(child));
+ * purple_xmlnode_from_str, not the output version... */
+ value->forms = g_list_append(value->forms, purple_xmlnode_copy(child));
} else if (g_str_equal(child->name, "ext")) {
if (key->hash != NULL)
purple_debug_warning("jabber", "Ignoring exts when reading new-style caps\n");
else {
/* TODO: Do we care about reading in the identities listed here? */
- const char *identifier = xmlnode_get_attrib(child, "identifier");
- xmlnode *node;
+ const char *identifier = purple_xmlnode_get_attrib(child, "identifier");
+ PurpleXmlNode *node;
GList *features = NULL;
if (!identifier)
continue;
for (node = child->child; node; node = node->next) {
- if (node->type != XMLNODE_TYPE_TAG)
+ if (node->type != PURPLE_XMLNODE_TYPE_TAG)
continue;
if (g_str_equal(node->name, "feature")) {
- const char *var = xmlnode_get_attrib(node, "var");
+ const char *var = purple_xmlnode_get_attrib(node, "var");
if (!var)
continue;
features = g_list_prepend(features, g_strdup(var));
@@ -323,7 +325,7 @@ jabber_caps_load(void)
}
}
- xmlnode_free(capsdata);
+ purple_xmlnode_free(capsdata);
}
void jabber_caps_init(void)
@@ -434,9 +436,9 @@ jabber_caps_get_info_complete(jabber_caps_cbplususerdata *userdata)
static void
jabber_caps_client_iqcb(JabberStream *js, const char *from, JabberIqType type,
- const char *id, xmlnode *packet, gpointer data)
+ const char *id, PurpleXmlNode *packet, gpointer data)
{
- xmlnode *query = xmlnode_get_child_with_namespace(packet, "query",
+ PurpleXmlNode *query = purple_xmlnode_get_child_with_namespace(packet, "query",
NS_DISCO_INFO);
jabber_caps_cbplususerdata *userdata = data;
JabberCapsClientInfo *info = NULL, *value;
@@ -455,21 +457,24 @@ jabber_caps_client_iqcb(JabberStream *js, const char *from, JabberIqType type,
/* Only validate if these are v1.5 capabilities */
if (userdata->hash) {
gchar *hash = NULL;
+ PurpleHash *hasher = NULL;
/*
* TODO: If you add *any* hash here, make sure the checksum buffer
* size in jabber_caps_calculate_hash is large enough. The cipher API
* doesn't seem to offer a "Get the hash size" function(?).
*/
if (g_str_equal(userdata->hash, "sha-1")) {
- hash = jabber_caps_calculate_hash(info, "sha1");
+ hasher = purple_sha1_hash_new();
} else if (g_str_equal(userdata->hash, "md5")) {
- hash = jabber_caps_calculate_hash(info, "md5");
+ hasher = purple_md5_hash_new();
}
+ hash = jabber_caps_calculate_hash(info, hasher);
+ g_object_unref(hasher);
if (!hash || !g_str_equal(hash, userdata->ver)) {
purple_debug_warning("jabber", "Could not validate caps info from "
"%s. Expected %s, got %s\n",
- xmlnode_get_attrib(packet, "from"),
+ purple_xmlnode_get_attrib(packet, "from"),
userdata->ver, hash ? hash : "(null)");
userdata->cb(NULL, NULL, userdata->cb_data);
@@ -500,6 +505,13 @@ jabber_caps_client_iqcb(JabberStream *js, const char *from, JabberIqType type,
info = value;
} else {
JabberCapsTuple *n_key = (JabberCapsTuple *)&info->tuple;
+
+ if (G_UNLIKELY(n_key == NULL)) {
+ g_warn_if_reached();
+ jabber_caps_client_info_destroy(info);
+ return;
+ }
+
n_key->node = userdata->node;
n_key->ver = userdata->ver;
n_key->hash = userdata->hash;
@@ -525,11 +537,11 @@ typedef struct {
static void
jabber_caps_ext_iqcb(JabberStream *js, const char *from, JabberIqType type,
- const char *id, xmlnode *packet, gpointer data)
+ const char *id, PurpleXmlNode *packet, gpointer data)
{
- xmlnode *query = xmlnode_get_child_with_namespace(packet, "query",
+ PurpleXmlNode *query = purple_xmlnode_get_child_with_namespace(packet, "query",
NS_DISCO_INFO);
- xmlnode *child;
+ PurpleXmlNode *child;
ext_iq_data *userdata = data;
GList *features = NULL;
JabberCapsNodeExts *node_exts;
@@ -571,9 +583,9 @@ jabber_caps_ext_iqcb(JabberStream *js, const char *from, JabberIqType type,
*/
--userdata->data->extOutstanding;
- for (child = xmlnode_get_child(query, "feature"); child;
- child = xmlnode_get_next_twin(child)) {
- const char *var = xmlnode_get_attrib(child, "var");
+ for (child = purple_xmlnode_get_child(query, "feature"); child;
+ child = purple_xmlnode_get_next_twin(child)) {
+ const char *var = purple_xmlnode_get_attrib(child, "var");
if (var)
features = g_list_prepend(features, g_strdup(var));
}
@@ -632,16 +644,16 @@ void jabber_caps_get_info(JabberStream *js, const char *who, const char *node,
/* If we don't have the basic information about the client, we need
* to fetch it. */
JabberIq *iq;
- xmlnode *query;
+ PurpleXmlNode *query;
char *nodever;
iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_DISCO_INFO);
- query = xmlnode_get_child_with_namespace(iq->node, "query",
+ query = purple_xmlnode_get_child_with_namespace(iq->node, "query",
NS_DISCO_INFO);
nodever = g_strdup_printf("%s#%s", node, ver);
- xmlnode_set_attrib(query, "node", nodever);
+ purple_xmlnode_set_attrib(query, "node", nodever);
g_free(nodever);
- xmlnode_set_attrib(iq->node, "to", who);
+ purple_xmlnode_set_attrib(iq->node, "to", who);
cbplususerdata_ref(userdata);
@@ -668,7 +680,7 @@ void jabber_caps_get_info(JabberStream *js, const char *who, const char *node,
/* Look it up if we don't already know what it means */
if (!g_hash_table_lookup(node_exts->exts, exts[i])) {
JabberIq *iq;
- xmlnode *query;
+ PurpleXmlNode *query;
char *nodeext;
ext_iq_data *cbdata = g_new(ext_iq_data, 1);
@@ -676,12 +688,12 @@ void jabber_caps_get_info(JabberStream *js, const char *who, const char *node,
cbdata->data = cbplususerdata_ref(userdata);
iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_DISCO_INFO);
- query = xmlnode_get_child_with_namespace(iq->node, "query",
+ query = purple_xmlnode_get_child_with_namespace(iq->node, "query",
NS_DISCO_INFO);
nodeext = g_strdup_printf("%s#%s", node, exts[i]);
- xmlnode_set_attrib(query, "node", nodeext);
+ purple_xmlnode_set_attrib(query, "node", nodeext);
g_free(nodeext);
- xmlnode_set_attrib(iq->node, "to", who);
+ purple_xmlnode_set_attrib(iq->node, "to", who);
jabber_iq_set_callback(iq, jabber_caps_ext_iqcb, cbdata);
jabber_iq_send(iq);
@@ -708,8 +720,8 @@ void jabber_caps_get_info(JabberStream *js, const char *who, const char *node,
static gint
jabber_xdata_compare(gconstpointer a, gconstpointer b)
{
- const xmlnode *aformtypefield = a;
- const xmlnode *bformtypefield = b;
+ const PurpleXmlNode *aformtypefield = a;
+ const PurpleXmlNode *bformtypefield = b;
char *aformtype;
char *bformtype;
int result;
@@ -723,9 +735,9 @@ jabber_xdata_compare(gconstpointer a, gconstpointer b)
return result;
}
-JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query)
+JabberCapsClientInfo *jabber_caps_parse_client_info(PurpleXmlNode *query)
{
- xmlnode *child;
+ PurpleXmlNode *child;
JabberCapsClientInfo *info;
if (!query || !g_str_equal(query->name, "query") ||
@@ -735,14 +747,14 @@ JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query)
info = g_new0(JabberCapsClientInfo, 1);
for(child = query->child; child; child = child->next) {
- if (child->type != XMLNODE_TYPE_TAG)
+ if (child->type != PURPLE_XMLNODE_TYPE_TAG)
continue;
if (g_str_equal(child->name, "identity")) {
/* parse identity */
- const char *category = xmlnode_get_attrib(child, "category");
- const char *type = xmlnode_get_attrib(child, "type");
- const char *name = xmlnode_get_attrib(child, "name");
- const char *lang = xmlnode_get_attrib(child, "lang");
+ const char *category = purple_xmlnode_get_attrib(child, "category");
+ const char *type = purple_xmlnode_get_attrib(child, "type");
+ const char *name = purple_xmlnode_get_attrib(child, "name");
+ const char *lang = purple_xmlnode_get_attrib(child, "lang");
JabberIdentity *id;
if (!category || !type)
@@ -757,13 +769,13 @@ JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query)
info->identities = g_list_append(info->identities, id);
} else if (g_str_equal(child->name, "feature")) {
/* parse feature */
- const char *var = xmlnode_get_attrib(child, "var");
+ const char *var = purple_xmlnode_get_attrib(child, "var");
if (var)
info->features = g_list_prepend(info->features, g_strdup(var));
} else if (g_str_equal(child->name, "x")) {
if (purple_strequal(child->xmlns, "jabber:x:data")) {
/* x-data form */
- xmlnode *dataform = xmlnode_copy(child);
+ PurpleXmlNode *dataform = purple_xmlnode_copy(child);
info->forms = g_list_append(info->forms, dataform);
}
}
@@ -779,26 +791,26 @@ static gint jabber_caps_xdata_field_compare(gconstpointer a, gconstpointer b)
return strcmp(ac->var, bc->var);
}
-static GList* jabber_caps_xdata_get_fields(const xmlnode *x)
+static GList* jabber_caps_xdata_get_fields(const PurpleXmlNode *x)
{
GList *fields = NULL;
- xmlnode *field;
+ PurpleXmlNode *field;
if (!x)
return NULL;
- for (field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
- xmlnode *value;
+ for (field = purple_xmlnode_get_child(x, "field"); field; field = purple_xmlnode_get_next_twin(field)) {
+ PurpleXmlNode *value;
JabberDataFormField *xdatafield = g_new0(JabberDataFormField, 1);
- xdatafield->var = g_strdup(xmlnode_get_attrib(field, "var"));
+ xdatafield->var = g_strdup(purple_xmlnode_get_attrib(field, "var"));
- for (value = xmlnode_get_child(field, "value"); value; value = xmlnode_get_next_twin(value)) {
- gchar *val = xmlnode_get_data(value);
- xdatafield->values = g_list_append(xdatafield->values, val);
+ for (value = purple_xmlnode_get_child(field, "value"); value; value = purple_xmlnode_get_next_twin(value)) {
+ gchar *val = purple_xmlnode_get_data(value);
+ xdatafield->values = g_list_prepend(xdatafield->values, val);
}
xdatafield->values = g_list_sort(xdatafield->values, (GCompareFunc)strcmp);
- fields = g_list_append(fields, xdatafield);
+ fields = g_list_prepend(fields, xdatafield);
}
fields = g_list_sort(fields, jabber_caps_xdata_field_compare);
@@ -806,26 +818,27 @@ static GList* jabber_caps_xdata_get_fields(const xmlnode *x)
}
static void
-append_escaped_string(PurpleCipherContext *context, const gchar *str)
+append_escaped_string(PurpleHash *hash, const gchar *str)
{
+ g_return_if_fail(hash != NULL);
+
if (str && *str) {
char *tmp = g_markup_escape_text(str, -1);
- purple_cipher_context_append(context, (const guchar *)tmp, strlen(tmp));
+ purple_hash_append(hash, (const guchar *)tmp, strlen(tmp));
g_free(tmp);
}
- purple_cipher_context_append(context, (const guchar *)"<", 1);
+ purple_hash_append(hash, (const guchar *)"<", 1);
}
-gchar *jabber_caps_calculate_hash(JabberCapsClientInfo *info, const char *hash)
+gchar *jabber_caps_calculate_hash(JabberCapsClientInfo *info, PurpleHash *hash)
{
GList *node;
- PurpleCipherContext *context;
guint8 checksum[20];
gsize checksum_size = 20;
gboolean success;
- if (!info || !(context = purple_cipher_context_new_by_name(hash, NULL)))
+ if (!info || !hash)
return NULL;
/* sort identities, features and x-data forms */
@@ -850,7 +863,7 @@ gchar *jabber_caps_calculate_hash(JabberCapsClientInfo *info, const char *hash)
tmp = g_strconcat(category, "/", type, "/", lang ? lang : "",
"/", name ? name : "", "<", NULL);
- purple_cipher_context_append(context, (const guchar *)tmp, strlen(tmp));
+ purple_hash_append(hash, (const guchar *)tmp, strlen(tmp));
g_free(tmp);
g_free(category);
@@ -861,51 +874,54 @@ gchar *jabber_caps_calculate_hash(JabberCapsClientInfo *info, const char *hash)
/* concat features to the verification string */
for (node = info->features; node; node = node->next) {
- append_escaped_string(context, node->data);
+ append_escaped_string(hash, node->data);
}
/* concat x-data forms to the verification string */
for(node = info->forms; node; node = node->next) {
- xmlnode *data = (xmlnode *)node->data;
+ PurpleXmlNode *data = (PurpleXmlNode *)node->data;
gchar *formtype = jabber_x_data_get_formtype(data);
GList *fields = jabber_caps_xdata_get_fields(data);
/* append FORM_TYPE's field value to the verification string */
- append_escaped_string(context, formtype);
+ append_escaped_string(hash, formtype);
g_free(formtype);
while (fields) {
- GList *value;
JabberDataFormField *field = (JabberDataFormField*)fields->data;
if (!g_str_equal(field->var, "FORM_TYPE")) {
/* Append the "var" attribute */
- append_escaped_string(context, field->var);
+ append_escaped_string(hash, field->var);
/* Append <value/> elements' cdata */
- for (value = field->values; value; value = value->next) {
- append_escaped_string(context, value->data);
- g_free(value->data);
+ while (field->values) {
+ append_escaped_string(hash, field->values->data);
+ g_free(field->values->data);
+ field->values = g_list_delete_link(field->values,
+ field->values);
}
+ } else {
+ g_list_foreach(field->values, (GFunc) g_free, NULL);
+ g_list_free(field->values);
}
g_free(field->var);
- g_list_free(field->values);
+ g_free(field);
fields = g_list_delete_link(fields, fields);
}
}
/* generate hash */
- success = purple_cipher_context_digest(context, checksum_size,
- checksum, &checksum_size);
-
- purple_cipher_context_destroy(context);
+ success = purple_hash_digest(hash, checksum, checksum_size);
+ checksum_size = purple_hash_get_digest_size(hash);
return (success ? purple_base64_encode(checksum, checksum_size) : NULL);
}
void jabber_caps_calculate_own_hash(JabberStream *js) {
JabberCapsClientInfo info;
+ PurpleHash *hasher;
GList *iter = 0;
GList *features = 0;
@@ -936,7 +952,9 @@ void jabber_caps_calculate_own_hash(JabberStream *js) {
info.forms = NULL;
g_free(js->caps_hash);
- js->caps_hash = jabber_caps_calculate_hash(&info, "sha1");
+ hasher = purple_sha1_hash_new();
+ js->caps_hash = jabber_caps_calculate_hash(&info, hasher);
+ g_object_unref(hasher);
g_list_free(info.identities);
g_list_free(info.features);
}
@@ -958,7 +976,7 @@ void jabber_caps_broadcast_change()
const char *prpl_id = purple_account_get_protocol_id(account);
if (g_str_equal("prpl-jabber", prpl_id) && purple_account_is_connected(account)) {
PurpleConnection *gc = purple_account_get_connection(account);
- jabber_presence_send(gc->proto_data, TRUE);
+ jabber_presence_send(purple_connection_get_protocol_data(gc), TRUE);
}
}
diff --git a/libpurple/protocols/jabber/caps.h b/libpurple/protocols/jabber/caps.h
index 897754f6d9..cc29a24a1d 100644
--- a/libpurple/protocols/jabber/caps.h
+++ b/libpurple/protocols/jabber/caps.h
@@ -27,6 +27,7 @@
typedef struct _JabberCapsClientInfo JabberCapsClientInfo;
#include "jabber.h"
+#include "cipher.h"
/* Implementation of XEP-0115 - Entity Capabilities */
@@ -41,7 +42,7 @@ typedef struct _JabberCapsTuple {
struct _JabberCapsClientInfo {
GList *identities; /* JabberIdentity */
GList *features; /* char * */
- GList *forms; /* xmlnode * */
+ GList *forms; /* PurpleXmlNode * */
JabberCapsNodeExts *exts;
const JabberCapsTuple tuple;
@@ -98,7 +99,7 @@ void jabber_caps_get_info(JabberStream *js, const char *who, const char *node,
* @param hash Hash cipher to be used. Either sha-1 or md5.
* @return The base64 encoded SHA-1 hash; must be freed by caller
*/
-gchar *jabber_caps_calculate_hash(JabberCapsClientInfo *info, const char *hash);
+gchar *jabber_caps_calculate_hash(JabberCapsClientInfo *info, PurpleHash *hash);
/**
* Calculate SHA1 hash for own featureset.
@@ -124,6 +125,6 @@ void jabber_caps_broadcast_change(void);
* @param query The 'query' element from an IQ reply stanza.
* @returns A JabberCapsClientInfo struct, or NULL on error
*/
-JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query);
+JabberCapsClientInfo *jabber_caps_parse_client_info(PurpleXmlNode *query);
#endif /* PURPLE_JABBER_CAPS_H_ */
diff --git a/libpurple/protocols/jabber/chat.c b/libpurple/protocols/jabber/chat.c
index b7cb226181..c58f1ad073 100644
--- a/libpurple/protocols/jabber/chat.c
+++ b/libpurple/protocols/jabber/chat.c
@@ -70,7 +70,7 @@ GList *jabber_chat_info(PurpleConnection *gc)
GHashTable *jabber_chat_info_defaults(PurpleConnection *gc, const char *chat_name)
{
GHashTable *defaults;
- JabberStream *js = gc->proto_data;
+ JabberStream *js = purple_connection_get_protocol_data(gc);
defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
@@ -138,47 +138,47 @@ JabberChat *jabber_chat_find_by_id(JabberStream *js, int id)
return chat;
}
-JabberChat *jabber_chat_find_by_conv(PurpleConversation *conv)
+JabberChat *jabber_chat_find_by_conv(PurpleChatConversation *conv)
{
- PurpleAccount *account = purple_conversation_get_account(conv);
+ PurpleAccount *account = purple_conversation_get_account(PURPLE_CONVERSATION(conv));
PurpleConnection *gc = purple_account_get_connection(account);
JabberStream *js;
int id;
if (!gc)
return NULL;
- js = gc->proto_data;
- id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv));
+ js = purple_connection_get_protocol_data(gc);
+ id = purple_chat_conversation_get_id(conv);
return jabber_chat_find_by_id(js, id);
}
void jabber_chat_invite(PurpleConnection *gc, int id, const char *msg,
const char *name)
{
- JabberStream *js = gc->proto_data;
+ JabberStream *js = purple_connection_get_protocol_data(gc);
JabberChat *chat;
- xmlnode *message, *body, *x, *invite;
+ PurpleXmlNode *message, *body, *x, *invite;
char *room_jid;
chat = jabber_chat_find_by_id(js, id);
if(!chat)
return;
- message = xmlnode_new("message");
+ message = purple_xmlnode_new("message");
room_jid = g_strdup_printf("%s@%s", chat->room, chat->server);
if(chat->muc) {
- xmlnode_set_attrib(message, "to", room_jid);
- x = xmlnode_new_child(message, "x");
- xmlnode_set_namespace(x, "http://jabber.org/protocol/muc#user");
- invite = xmlnode_new_child(x, "invite");
- xmlnode_set_attrib(invite, "to", name);
+ purple_xmlnode_set_attrib(message, "to", room_jid);
+ x = purple_xmlnode_new_child(message, "x");
+ purple_xmlnode_set_namespace(x, "http://jabber.org/protocol/muc#user");
+ invite = purple_xmlnode_new_child(x, "invite");
+ purple_xmlnode_set_attrib(invite, "to", name);
if (msg) {
- body = xmlnode_new_child(invite, "reason");
- xmlnode_insert_data(body, msg, -1);
+ body = purple_xmlnode_new_child(invite, "reason");
+ purple_xmlnode_insert_data(body, msg, -1);
}
} else {
- xmlnode_set_attrib(message, "to", name);
+ purple_xmlnode_set_attrib(message, "to", name);
/*
* Putting the reason into the body was an 'undocumented protocol,
* ...not part of "groupchat 1.0"'.
@@ -187,21 +187,21 @@ void jabber_chat_invite(PurpleConnection *gc, int id, const char *msg,
* Left here for compatibility.
*/
if (msg) {
- body = xmlnode_new_child(message, "body");
- xmlnode_insert_data(body, msg, -1);
+ body = purple_xmlnode_new_child(message, "body");
+ purple_xmlnode_insert_data(body, msg, -1);
}
- x = xmlnode_new_child(message, "x");
- xmlnode_set_attrib(x, "jid", room_jid);
+ x = purple_xmlnode_new_child(message, "x");
+ purple_xmlnode_set_attrib(x, "jid", room_jid);
/* The better place for it! XEP-0249 style. */
if (msg)
- xmlnode_set_attrib(x, "reason", msg);
- xmlnode_set_namespace(x, "jabber:x:conference");
+ purple_xmlnode_set_attrib(x, "reason", msg);
+ purple_xmlnode_set_namespace(x, "jabber:x:conference");
}
jabber_send(js, message);
- xmlnode_free(message);
+ purple_xmlnode_free(message);
g_free(room_jid);
}
@@ -274,7 +274,7 @@ JabberChat *jabber_join_chat(JabberStream *js, const char *room,
PurpleAccount *account;
PurpleStatus *status;
- xmlnode *presence, *x;
+ PurpleXmlNode *presence, *x;
JabberBuddyState state;
char *msg;
int priority;
@@ -302,7 +302,7 @@ JabberChat *jabber_join_chat(JabberStream *js, const char *room,
g_free(msg);
jid = g_strdup_printf("%s@%s/%s", room, server, handle);
- xmlnode_set_attrib(presence, "to", jid);
+ purple_xmlnode_set_attrib(presence, "to", jid);
g_free(jid);
history_maxchars = g_hash_table_lookup(data, "history_maxchars");
@@ -321,12 +321,12 @@ JabberChat *jabber_join_chat(JabberStream *js, const char *room,
}
}
- x = xmlnode_new_child(presence, "x");
- xmlnode_set_namespace(x, "http://jabber.org/protocol/muc");
+ x = purple_xmlnode_new_child(presence, "x");
+ purple_xmlnode_set_namespace(x, "http://jabber.org/protocol/muc");
if (password && *password) {
- xmlnode *p = xmlnode_new_child(x, "password");
- xmlnode_insert_data(p, password, -1);
+ PurpleXmlNode *p = purple_xmlnode_new_child(x, "password");
+ purple_xmlnode_insert_data(p, password, -1);
}
if ((history_maxchars && *history_maxchars)
@@ -334,24 +334,24 @@ JabberChat *jabber_join_chat(JabberStream *js, const char *room,
|| (history_seconds && *history_seconds)
|| (history_since_string && *history_since_string)) {
- xmlnode *history = xmlnode_new_child(x, "history");
+ PurpleXmlNode *history = purple_xmlnode_new_child(x, "history");
if (history_maxchars && *history_maxchars) {
- xmlnode_set_attrib(history, "maxchars", history_maxchars);
+ purple_xmlnode_set_attrib(history, "maxchars", history_maxchars);
}
if (history_maxstanzas && *history_maxstanzas) {
- xmlnode_set_attrib(history, "maxstanzas", history_maxstanzas);
+ purple_xmlnode_set_attrib(history, "maxstanzas", history_maxstanzas);
}
if (history_seconds && *history_seconds) {
- xmlnode_set_attrib(history, "seconds", history_seconds);
+ purple_xmlnode_set_attrib(history, "seconds", history_seconds);
}
if (history_since_string && *history_since_string) {
- xmlnode_set_attrib(history, "since", history_since_string);
+ purple_xmlnode_set_attrib(history, "since", history_since_string);
}
}
jabber_send(js, presence);
- xmlnode_free(presence);
+ purple_xmlnode_free(presence);
return chat;
}
@@ -360,7 +360,7 @@ void jabber_chat_join(PurpleConnection *gc, GHashTable *data)
{
char *room, *server, *handle, *passwd;
JabberID *jid;
- JabberStream *js = gc->proto_data;
+ JabberStream *js = purple_connection_get_protocol_data(gc);
char *tmp;
room = g_hash_table_lookup(data, "room");
@@ -377,21 +377,23 @@ void jabber_chat_join(PurpleConnection *gc, GHashTable *data)
if(!jabber_nodeprep_validate(room)) {
char *buf = g_strdup_printf(_("%s is not a valid room name"), room);
purple_notify_error(gc, _("Invalid Room Name"), _("Invalid Room Name"),
- buf);
+ buf, purple_request_cpar_from_connection(gc));
purple_serv_got_join_chat_failed(gc, data);
g_free(buf);
return;
} else if(!jabber_domain_validate(server)) {
char *buf = g_strdup_printf(_("%s is not a valid server name"), server);
purple_notify_error(gc, _("Invalid Server Name"),
- _("Invalid Server Name"), buf);
+ _("Invalid Server Name"), buf,
+ purple_request_cpar_from_connection(gc));
purple_serv_got_join_chat_failed(gc, data);
g_free(buf);
return;
} else if(!jabber_resourceprep_validate(handle)) {
char *buf = g_strdup_printf(_("%s is not a valid room handle"), handle);
purple_notify_error(gc, _("Invalid Room Handle"),
- _("Invalid Room Handle"), buf);
+ _("Invalid Room Handle"), buf,
+ purple_request_cpar_from_connection(gc));
purple_serv_got_join_chat_failed(gc, data);
g_free(buf);
return;
@@ -418,10 +420,9 @@ void jabber_chat_join(PurpleConnection *gc, GHashTable *data)
void jabber_chat_leave(PurpleConnection *gc, int id)
{
- JabberStream *js = gc->proto_data;
+ JabberStream *js = purple_connection_get_protocol_data(gc);
JabberChat *chat = jabber_chat_find_by_id(js, id);
-
if(!chat)
return;
@@ -452,14 +453,14 @@ void jabber_chat_free(JabberChat *chat)
g_free(chat);
}
-gboolean jabber_chat_find_buddy(PurpleConversation *conv, const char *name)
+gboolean jabber_chat_find_buddy(PurpleChatConversation *conv, const char *name)
{
- return purple_conv_chat_find_user(PURPLE_CONV_CHAT(conv), name);
+ return purple_chat_conversation_has_user(conv, name);
}
-char *jabber_chat_buddy_real_name(PurpleConnection *gc, int id, const char *who)
+char *jabber_chat_user_real_name(PurpleConnection *gc, int id, const char *who)
{
- JabberStream *js = gc->proto_data;
+ JabberStream *js = purple_connection_get_protocol_data(gc);
JabberChat *chat;
JabberChatMember *jcm;
@@ -476,29 +477,29 @@ char *jabber_chat_buddy_real_name(PurpleConnection *gc, int id, const char *who)
return g_strdup_printf("%s@%s/%s", chat->room, chat->server, who);
}
-static void jabber_chat_room_configure_x_data_cb(JabberStream *js, xmlnode *result, gpointer data)
+static void jabber_chat_room_configure_x_data_cb(JabberStream *js, PurpleXmlNode *result, gpointer data)
{
JabberChat *chat = data;
- xmlnode *query;
+ PurpleXmlNode *query;
JabberIq *iq;
char *to = g_strdup_printf("%s@%s", chat->room, chat->server);
iq = jabber_iq_new_query(js, JABBER_IQ_SET, "http://jabber.org/protocol/muc#owner");
- xmlnode_set_attrib(iq->node, "to", to);
+ purple_xmlnode_set_attrib(iq->node, "to", to);
g_free(to);
- query = xmlnode_get_child(iq->node, "query");
+ query = purple_xmlnode_get_child(iq->node, "query");
- xmlnode_insert_child(query, result);
+ purple_xmlnode_insert_child(query, result);
jabber_iq_send(iq);
}
static void jabber_chat_room_configure_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
- xmlnode *query, *x;
+ PurpleXmlNode *query, *x;
char *msg;
JabberChat *chat;
JabberID *jid;
@@ -518,12 +519,12 @@ static void jabber_chat_room_configure_cb(JabberStream *js, const char *from,
if(!chat)
return;
- if(!(query = xmlnode_get_child(packet, "query")))
+ if(!(query = purple_xmlnode_get_child(packet, "query")))
return;
- for(x = xmlnode_get_child(query, "x"); x; x = xmlnode_get_next_twin(x)) {
+ for(x = purple_xmlnode_get_child(query, "x"); x; x = purple_xmlnode_get_next_twin(x)) {
const char *xmlns;
- if(!(xmlns = xmlnode_get_namespace(x)))
+ if(!(xmlns = purple_xmlnode_get_namespace(x)))
continue;
if(!strcmp(xmlns, "jabber:x:data")) {
@@ -535,7 +536,9 @@ static void jabber_chat_room_configure_cb(JabberStream *js, const char *from,
} else if (type == JABBER_IQ_ERROR) {
char *msg = jabber_parse_error(js, packet, NULL);
- purple_notify_error(js->gc, _("Configuration error"), _("Configuration error"), msg);
+ purple_notify_error(js->gc, _("Configuration error"),
+ _("Configuration error"), msg,
+ purple_request_cpar_from_connection(js->gc));
if(msg)
g_free(msg);
@@ -544,7 +547,9 @@ static void jabber_chat_room_configure_cb(JabberStream *js, const char *from,
msg = g_strdup_printf("Unable to configure room %s", from);
- purple_notify_info(js->gc, _("Unable to configure"), _("Unable to configure"), msg);
+ purple_notify_info(js->gc, _("Unable to configure"),
+ _("Unable to configure"), msg,
+ purple_request_cpar_from_connection(js->gc));
g_free(msg);
}
@@ -559,8 +564,10 @@ void jabber_chat_request_room_configure(JabberChat *chat) {
chat->config_dialog_handle = NULL;
if(!chat->muc) {
- purple_notify_error(chat->js->gc, _("Room Configuration Error"), _("Room Configuration Error"),
- _("This room is not capable of being configured"));
+ purple_notify_error(chat->js->gc, _("Room Configuration Error"),
+ _("Room Configuration Error"),
+ _("This room is not capable of being configured"),
+ purple_request_cpar_from_connection(chat->js->gc));
return;
}
@@ -568,7 +575,7 @@ void jabber_chat_request_room_configure(JabberChat *chat) {
"http://jabber.org/protocol/muc#owner");
room_jid = g_strdup_printf("%s@%s", chat->room, chat->server);
- xmlnode_set_attrib(iq->node, "to", room_jid);
+ purple_xmlnode_set_attrib(iq->node, "to", room_jid);
jabber_iq_set_callback(iq, jabber_chat_room_configure_cb, NULL);
@@ -579,7 +586,7 @@ void jabber_chat_request_room_configure(JabberChat *chat) {
void jabber_chat_create_instant_room(JabberChat *chat) {
JabberIq *iq;
- xmlnode *query, *x;
+ PurpleXmlNode *query, *x;
char *room_jid;
if(!chat)
@@ -589,13 +596,13 @@ void jabber_chat_create_instant_room(JabberChat *chat) {
iq = jabber_iq_new_query(chat->js, JABBER_IQ_SET,
"http://jabber.org/protocol/muc#owner");
- query = xmlnode_get_child(iq->node, "query");
- x = xmlnode_new_child(query, "x");
+ query = purple_xmlnode_get_child(iq->node, "query");
+ x = purple_xmlnode_new_child(query, "x");
room_jid = g_strdup_printf("%s@%s", chat->room, chat->server);
- xmlnode_set_attrib(iq->node, "to", room_jid);
- xmlnode_set_namespace(x, "jabber:x:data");
- xmlnode_set_attrib(x, "type", "submit");
+ purple_xmlnode_set_attrib(iq->node, "to", room_jid);
+ purple_xmlnode_set_namespace(x, "jabber:x:data");
+ purple_xmlnode_set_attrib(x, "type", "submit");
jabber_iq_send(iq);
@@ -605,12 +612,14 @@ void jabber_chat_create_instant_room(JabberChat *chat) {
static void
jabber_chat_register_x_data_result_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
if (type == JABBER_IQ_ERROR) {
char *msg = jabber_parse_error(js, packet, NULL);
- purple_notify_error(js->gc, _("Registration error"), _("Registration error"), msg);
+ purple_notify_error(js->gc, _("Registration error"),
+ _("Registration error"), msg,
+ purple_request_cpar_from_connection(js->gc));
if(msg)
g_free(msg);
@@ -618,20 +627,20 @@ jabber_chat_register_x_data_result_cb(JabberStream *js, const char *from,
}
}
-static void jabber_chat_register_x_data_cb(JabberStream *js, xmlnode *result, gpointer data)
+static void jabber_chat_register_x_data_cb(JabberStream *js, PurpleXmlNode *result, gpointer data)
{
JabberChat *chat = data;
- xmlnode *query;
+ PurpleXmlNode *query;
JabberIq *iq;
char *to = g_strdup_printf("%s@%s", chat->room, chat->server);
iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:register");
- xmlnode_set_attrib(iq->node, "to", to);
+ purple_xmlnode_set_attrib(iq->node, "to", to);
g_free(to);
- query = xmlnode_get_child(iq->node, "query");
+ query = purple_xmlnode_get_child(iq->node, "query");
- xmlnode_insert_child(query, result);
+ purple_xmlnode_insert_child(query, result);
jabber_iq_set_callback(iq, jabber_chat_register_x_data_result_cb, NULL);
@@ -640,9 +649,9 @@ static void jabber_chat_register_x_data_cb(JabberStream *js, xmlnode *result, gp
static void jabber_chat_register_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
- xmlnode *query, *x;
+ PurpleXmlNode *query, *x;
char *msg;
JabberChat *chat;
JabberID *jid;
@@ -662,13 +671,13 @@ static void jabber_chat_register_cb(JabberStream *js, const char *from,
if(!chat)
return;
- if(!(query = xmlnode_get_child(packet, "query")))
+ if(!(query = purple_xmlnode_get_child(packet, "query")))
return;
- for(x = xmlnode_get_child(query, "x"); x; x = xmlnode_get_next_twin(x)) {
+ for(x = purple_xmlnode_get_child(query, "x"); x; x = purple_xmlnode_get_next_twin(x)) {
const char *xmlns;
- if(!(xmlns = xmlnode_get_namespace(x)))
+ if(!(xmlns = purple_xmlnode_get_namespace(x)))
continue;
if(!strcmp(xmlns, "jabber:x:data")) {
@@ -679,7 +688,9 @@ static void jabber_chat_register_cb(JabberStream *js, const char *from,
} else if (type == JABBER_IQ_ERROR) {
char *msg = jabber_parse_error(js, packet, NULL);
- purple_notify_error(js->gc, _("Registration error"), _("Registration error"), msg);
+ purple_notify_error(js->gc, _("Registration error"),
+ _("Registration error"), msg,
+ purple_request_cpar_from_connection(js->gc));
if(msg)
g_free(msg);
@@ -688,7 +699,8 @@ static void jabber_chat_register_cb(JabberStream *js, const char *from,
msg = g_strdup_printf("Unable to configure room %s", from);
- purple_notify_info(js->gc, _("Unable to configure"), _("Unable to configure"), msg);
+ purple_notify_info(js->gc, _("Unable to configure"), _("Unable to "
+ "configure"), msg, purple_request_cpar_from_connection(js->gc));
g_free(msg);
}
@@ -704,7 +716,7 @@ void jabber_chat_register(JabberChat *chat)
room_jid = g_strdup_printf("%s@%s", chat->room, chat->server);
iq = jabber_iq_new_query(chat->js, JABBER_IQ_GET, "jabber:iq:register");
- xmlnode_set_attrib(iq->node, "to", room_jid);
+ purple_xmlnode_set_attrib(iq->node, "to", room_jid);
g_free(room_jid);
jabber_iq_set_callback(iq, jabber_chat_register_cb, NULL);
@@ -745,7 +757,7 @@ void jabber_chat_set_topic(PurpleConnection *gc, int id, const char *topic)
gboolean jabber_chat_change_nick(JabberChat *chat, const char *nick)
{
- xmlnode *presence;
+ PurpleXmlNode *presence;
char *full_jid;
PurpleAccount *account;
PurpleStatus *status;
@@ -754,9 +766,9 @@ gboolean jabber_chat_change_nick(JabberChat *chat, const char *nick)
int priority;
if(!chat->muc) {
- purple_conv_chat_write(PURPLE_CONV_CHAT(chat->conv), "",
- _("Nick changing not supported in non-MUC chatrooms"),
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(chat->conv),
+ _("Nick changing not supported in non-MUC chatrooms"), 0);
return FALSE;
}
@@ -767,12 +779,12 @@ gboolean jabber_chat_change_nick(JabberChat *chat, const char *nick)
presence = jabber_presence_create_js(chat->js, state, msg, priority);
full_jid = g_strdup_printf("%s@%s/%s", chat->room, chat->server, nick);
- xmlnode_set_attrib(presence, "to", full_jid);
+ purple_xmlnode_set_attrib(presence, "to", full_jid);
g_free(full_jid);
g_free(msg);
jabber_send(chat->js, presence);
- xmlnode_free(presence);
+ purple_xmlnode_free(presence);
return TRUE;
}
@@ -780,29 +792,29 @@ gboolean jabber_chat_change_nick(JabberChat *chat, const char *nick)
void jabber_chat_part(JabberChat *chat, const char *msg)
{
char *room_jid;
- xmlnode *presence;
+ PurpleXmlNode *presence;
room_jid = g_strdup_printf("%s@%s/%s", chat->room, chat->server,
chat->handle);
- presence = xmlnode_new("presence");
- xmlnode_set_attrib(presence, "to", room_jid);
- xmlnode_set_attrib(presence, "type", "unavailable");
+ presence = purple_xmlnode_new("presence");
+ purple_xmlnode_set_attrib(presence, "to", room_jid);
+ purple_xmlnode_set_attrib(presence, "type", "unavailable");
if(msg) {
- xmlnode *status = xmlnode_new_child(presence, "status");
- xmlnode_insert_data(status, msg, -1);
+ PurpleXmlNode *status = purple_xmlnode_new_child(presence, "status");
+ purple_xmlnode_insert_data(status, msg, -1);
}
jabber_send(chat->js, presence);
- xmlnode_free(presence);
+ purple_xmlnode_free(presence);
g_free(room_jid);
}
static void roomlist_disco_result_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
- xmlnode *query;
- xmlnode *item;
+ PurpleXmlNode *query;
+ PurpleXmlNode *item;
if(!js->roomlist)
return;
@@ -810,34 +822,36 @@ static void roomlist_disco_result_cb(JabberStream *js, const char *from,
if (type == JABBER_IQ_ERROR) {
char *err = jabber_parse_error(js, packet, NULL);
purple_notify_error(js->gc, _("Error"),
- _("Error retrieving room list"), err);
+ _("Error retrieving room list"), err,
+ purple_request_cpar_from_connection(js->gc));
purple_roomlist_set_in_progress(js->roomlist, FALSE);
- purple_roomlist_unref(js->roomlist);
+ g_object_unref(js->roomlist);
js->roomlist = NULL;
g_free(err);
return;
}
- if(!(query = xmlnode_get_child(packet, "query"))) {
+ if(!(query = purple_xmlnode_get_child(packet, "query"))) {
char *err = jabber_parse_error(js, packet, NULL);
purple_notify_error(js->gc, _("Error"),
- _("Error retrieving room list"), err);
+ _("Error retrieving room list"), err,
+ purple_request_cpar_from_connection(js->gc));
purple_roomlist_set_in_progress(js->roomlist, FALSE);
- purple_roomlist_unref(js->roomlist);
+ g_object_unref(js->roomlist);
js->roomlist = NULL;
g_free(err);
return;
}
- for(item = xmlnode_get_child(query, "item"); item;
- item = xmlnode_get_next_twin(item)) {
+ for(item = purple_xmlnode_get_child(query, "item"); item;
+ item = purple_xmlnode_get_next_twin(item)) {
const char *name;
PurpleRoomlistRoom *room;
JabberID *jid;
- if(!(jid = jabber_id_new(xmlnode_get_attrib(item, "jid"))))
+ if(!(jid = jabber_id_new(purple_xmlnode_get_attrib(item, "jid"))))
continue;
- name = xmlnode_get_attrib(item, "name");
+ name = purple_xmlnode_get_attrib(item, "name");
room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, jid->node, NULL);
@@ -849,14 +863,14 @@ static void roomlist_disco_result_cb(JabberStream *js, const char *from,
jabber_id_free(jid);
}
purple_roomlist_set_in_progress(js->roomlist, FALSE);
- purple_roomlist_unref(js->roomlist);
+ g_object_unref(js->roomlist);
js->roomlist = NULL;
}
static void roomlist_cancel_cb(JabberStream *js, const char *server) {
if(js->roomlist) {
purple_roomlist_set_in_progress(js->roomlist, FALSE);
- purple_roomlist_unref(js->roomlist);
+ g_object_unref(js->roomlist);
js->roomlist = NULL;
}
}
@@ -869,7 +883,9 @@ static void roomlist_ok_cb(JabberStream *js, const char *server)
return;
if(!server || !*server) {
- purple_notify_error(js->gc, _("Invalid Server"), _("Invalid Server"), NULL);
+ purple_notify_error(js->gc, _("Invalid Server"),
+ _("Invalid Server"), NULL,
+ purple_request_cpar_from_connection(js->gc));
purple_roomlist_set_in_progress(js->roomlist, FALSE);
return;
}
@@ -878,7 +894,7 @@ static void roomlist_ok_cb(JabberStream *js, const char *server)
iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_DISCO_ITEMS);
- xmlnode_set_attrib(iq->node, "to", server);
+ purple_xmlnode_set_attrib(iq->node, "to", server);
jabber_iq_set_callback(iq, roomlist_disco_result_cb, NULL);
@@ -887,18 +903,18 @@ static void roomlist_ok_cb(JabberStream *js, const char *server)
char *jabber_roomlist_room_serialize(PurpleRoomlistRoom *room)
{
-
- return g_strdup_printf("%s@%s", (char*)room->fields->data, (char*)room->fields->next->data);
+ GList *fields = purple_roomlist_room_get_fields(room);
+ return g_strdup_printf("%s@%s", (char*)fields->data, (char*)fields->next->data);
}
PurpleRoomlist *jabber_roomlist_get_list(PurpleConnection *gc)
{
- JabberStream *js = gc->proto_data;
+ JabberStream *js = purple_connection_get_protocol_data(gc);
GList *fields = NULL;
PurpleRoomlistField *f;
if(js->roomlist)
- purple_roomlist_unref(js->roomlist);
+ g_object_unref(js->roomlist);
js->roomlist = purple_roomlist_new(purple_connection_get_account(js->gc));
@@ -920,7 +936,7 @@ PurpleRoomlist *jabber_roomlist_get_list(PurpleConnection *gc)
FALSE, FALSE, NULL,
_("Find Rooms"), PURPLE_CALLBACK(roomlist_ok_cb),
_("Cancel"), PURPLE_CALLBACK(roomlist_cancel_cb),
- purple_connection_get_account(gc), NULL, NULL,
+ purple_request_cpar_from_connection(gc),
js);
return js->roomlist;
@@ -928,17 +944,19 @@ PurpleRoomlist *jabber_roomlist_get_list(PurpleConnection *gc)
void jabber_roomlist_cancel(PurpleRoomlist *list)
{
+ PurpleAccount *account;
PurpleConnection *gc;
JabberStream *js;
- gc = purple_account_get_connection(list->account);
- js = gc->proto_data;
+ account = purple_roomlist_get_account(list);
+ gc = purple_account_get_connection(account);
+ js = purple_connection_get_protocol_data(gc);
purple_roomlist_set_in_progress(list, FALSE);
if (js->roomlist == list) {
js->roomlist = NULL;
- purple_roomlist_unref(list);
+ g_object_unref(list);
}
}
@@ -973,7 +991,7 @@ gboolean jabber_chat_ban_user(JabberChat *chat, const char *who, const char *why
const char *jid;
char *to;
JabberIq *iq;
- xmlnode *query, *item, *reason;
+ PurpleXmlNode *query, *item, *reason;
jcm = g_hash_table_lookup(chat->members, who);
if (jcm && jcm->jid)
@@ -987,16 +1005,16 @@ gboolean jabber_chat_ban_user(JabberChat *chat, const char *who, const char *why
"http://jabber.org/protocol/muc#admin");
to = g_strdup_printf("%s@%s", chat->room, chat->server);
- xmlnode_set_attrib(iq->node, "to", to);
+ purple_xmlnode_set_attrib(iq->node, "to", to);
g_free(to);
- query = xmlnode_get_child(iq->node, "query");
- item = xmlnode_new_child(query, "item");
- xmlnode_set_attrib(item, "jid", jid);
- xmlnode_set_attrib(item, "affiliation", "outcast");
+ query = purple_xmlnode_get_child(iq->node, "query");
+ item = purple_xmlnode_new_child(query, "item");
+ purple_xmlnode_set_attrib(item, "jid", jid);
+ purple_xmlnode_set_attrib(item, "affiliation", "outcast");
if(why) {
- reason = xmlnode_new_child(item, "reason");
- xmlnode_insert_data(reason, why, -1);
+ reason = purple_xmlnode_new_child(item, "reason");
+ purple_xmlnode_insert_data(reason, why, -1);
}
jabber_iq_send(iq);
@@ -1010,7 +1028,7 @@ gboolean jabber_chat_affiliate_user(JabberChat *chat, const char *who, const cha
const char *jid;
char *to;
JabberIq *iq;
- xmlnode *query, *item;
+ PurpleXmlNode *query, *item;
jcm = g_hash_table_lookup(chat->members, who);
if (jcm && jcm->jid)
@@ -1024,13 +1042,13 @@ gboolean jabber_chat_affiliate_user(JabberChat *chat, const char *who, const cha
"http://jabber.org/protocol/muc#admin");
to = g_strdup_printf("%s@%s", chat->room, chat->server);
- xmlnode_set_attrib(iq->node, "to", to);
+ purple_xmlnode_set_attrib(iq->node, "to", to);
g_free(to);
- query = xmlnode_get_child(iq->node, "query");
- item = xmlnode_new_child(query, "item");
- xmlnode_set_attrib(item, "jid", jid);
- xmlnode_set_attrib(item, "affiliation", affiliation);
+ query = purple_xmlnode_get_child(iq->node, "query");
+ item = purple_xmlnode_new_child(query, "item");
+ purple_xmlnode_set_attrib(item, "jid", jid);
+ purple_xmlnode_set_attrib(item, "affiliation", affiliation);
jabber_iq_send(iq);
@@ -1040,10 +1058,10 @@ gboolean jabber_chat_affiliate_user(JabberChat *chat, const char *who, const cha
static void
jabber_chat_affiliation_list_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
JabberChat *chat;
- xmlnode *query, *item;
+ PurpleXmlNode *query, *item;
int chat_id = GPOINTER_TO_INT(data);
GString *buf;
@@ -1053,16 +1071,16 @@ jabber_chat_affiliation_list_cb(JabberStream *js, const char *from,
if (type == JABBER_IQ_ERROR)
return;
- if(!(query = xmlnode_get_child(packet, "query")))
+ if(!(query = purple_xmlnode_get_child(packet, "query")))
return;
buf = g_string_new(_("Affiliations:"));
- item = xmlnode_get_child(query, "item");
+ item = purple_xmlnode_get_child(query, "item");
if (item) {
- for( ; item; item = xmlnode_get_next_twin(item)) {
- const char *jid = xmlnode_get_attrib(item, "jid");
- const char *affiliation = xmlnode_get_attrib(item, "affiliation");
+ for( ; item; item = purple_xmlnode_get_next_twin(item)) {
+ const char *jid = purple_xmlnode_get_attrib(item, "jid");
+ const char *affiliation = purple_xmlnode_get_attrib(item, "affiliation");
if (jid && affiliation)
g_string_append_printf(buf, "\n%s %s", jid, affiliation);
}
@@ -1071,8 +1089,8 @@ jabber_chat_affiliation_list_cb(JabberStream *js, const char *from,
buf = g_string_append_len(buf, _("No users found"), -1);
}
- purple_conv_chat_write(PURPLE_CONV_CHAT(chat->conv), "", buf->str,
- PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(chat->conv),
+ buf->str, PURPLE_MESSAGE_NO_LOG);
g_string_free(buf, TRUE);
}
@@ -1081,17 +1099,17 @@ gboolean jabber_chat_affiliation_list(JabberChat *chat, const char *affiliation)
{
JabberIq *iq;
char *room_jid;
- xmlnode *query, *item;
+ PurpleXmlNode *query, *item;
iq = jabber_iq_new_query(chat->js, JABBER_IQ_GET,
"http://jabber.org/protocol/muc#admin");
room_jid = g_strdup_printf("%s@%s", chat->room, chat->server);
- xmlnode_set_attrib(iq->node, "to", room_jid);
+ purple_xmlnode_set_attrib(iq->node, "to", room_jid);
- query = xmlnode_get_child(iq->node, "query");
- item = xmlnode_new_child(query, "item");
- xmlnode_set_attrib(item, "affiliation", affiliation);
+ query = purple_xmlnode_get_child(iq->node, "query");
+ item = purple_xmlnode_new_child(query, "item");
+ purple_xmlnode_set_attrib(item, "affiliation", affiliation);
jabber_iq_set_callback(iq, jabber_chat_affiliation_list_cb, GINT_TO_POINTER(chat->id));
jabber_iq_send(iq);
@@ -1104,7 +1122,7 @@ gboolean jabber_chat_role_user(JabberChat *chat, const char *who,
{
char *to;
JabberIq *iq;
- xmlnode *query, *item;
+ PurpleXmlNode *query, *item;
JabberChatMember *jcm;
jcm = g_hash_table_lookup(chat->members, who);
@@ -1116,16 +1134,16 @@ gboolean jabber_chat_role_user(JabberChat *chat, const char *who,
"http://jabber.org/protocol/muc#admin");
to = g_strdup_printf("%s@%s", chat->room, chat->server);
- xmlnode_set_attrib(iq->node, "to", to);
+ purple_xmlnode_set_attrib(iq->node, "to", to);
g_free(to);
- query = xmlnode_get_child(iq->node, "query");
- item = xmlnode_new_child(query, "item");
- xmlnode_set_attrib(item, "nick", jcm->handle);
- xmlnode_set_attrib(item, "role", role);
+ query = purple_xmlnode_get_child(iq->node, "query");
+ item = purple_xmlnode_new_child(query, "item");
+ purple_xmlnode_set_attrib(item, "nick", jcm->handle);
+ purple_xmlnode_set_attrib(item, "role", role);
if (why) {
- xmlnode *reason = xmlnode_new_child(item, "reason");
- xmlnode_insert_data(reason, why, -1);
+ PurpleXmlNode *reason = purple_xmlnode_new_child(item, "reason");
+ purple_xmlnode_insert_data(reason, why, -1);
}
jabber_iq_send(iq);
@@ -1135,10 +1153,10 @@ gboolean jabber_chat_role_user(JabberChat *chat, const char *who,
static void jabber_chat_role_list_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
JabberChat *chat;
- xmlnode *query, *item;
+ PurpleXmlNode *query, *item;
int chat_id = GPOINTER_TO_INT(data);
GString *buf;
@@ -1148,16 +1166,16 @@ static void jabber_chat_role_list_cb(JabberStream *js, const char *from,
if (type == JABBER_IQ_ERROR)
return;
- if(!(query = xmlnode_get_child(packet, "query")))
+ if(!(query = purple_xmlnode_get_child(packet, "query")))
return;
buf = g_string_new(_("Roles:"));
- item = xmlnode_get_child(query, "item");
+ item = purple_xmlnode_get_child(query, "item");
if (item) {
- for( ; item; item = xmlnode_get_next_twin(item)) {
- const char *jid = xmlnode_get_attrib(item, "jid");
- const char *role = xmlnode_get_attrib(item, "role");
+ for( ; item; item = purple_xmlnode_get_next_twin(item)) {
+ const char *jid = purple_xmlnode_get_attrib(item, "jid");
+ const char *role = purple_xmlnode_get_attrib(item, "role");
if (jid && role)
g_string_append_printf(buf, "\n%s %s", jid, role);
}
@@ -1166,8 +1184,8 @@ static void jabber_chat_role_list_cb(JabberStream *js, const char *from,
buf = g_string_append_len(buf, _("No users found"), -1);
}
- purple_conv_chat_write(PURPLE_CONV_CHAT(chat->conv), "", buf->str,
- PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(chat->conv),
+ buf->str, PURPLE_MESSAGE_NO_LOG);
g_string_free(buf, TRUE);
}
@@ -1176,17 +1194,17 @@ gboolean jabber_chat_role_list(JabberChat *chat, const char *role)
{
JabberIq *iq;
char *room_jid;
- xmlnode *query, *item;
+ PurpleXmlNode *query, *item;
iq = jabber_iq_new_query(chat->js, JABBER_IQ_GET,
"http://jabber.org/protocol/muc#admin");
room_jid = g_strdup_printf("%s@%s", chat->room, chat->server);
- xmlnode_set_attrib(iq->node, "to", room_jid);
+ purple_xmlnode_set_attrib(iq->node, "to", room_jid);
- query = xmlnode_get_child(iq->node, "query");
- item = xmlnode_new_child(query, "item");
- xmlnode_set_attrib(item, "role", role);
+ query = purple_xmlnode_get_child(iq->node, "query");
+ item = purple_xmlnode_new_child(query, "item");
+ purple_xmlnode_set_attrib(item, "role", role);
jabber_iq_set_callback(iq, jabber_chat_role_list_cb, GINT_TO_POINTER(chat->id));
jabber_iq_send(iq);
@@ -1196,11 +1214,11 @@ gboolean jabber_chat_role_list(JabberChat *chat, const char *role)
static void jabber_chat_disco_traffic_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
JabberChat *chat;
#if 0
- xmlnode *query, *x;
+ PurpleXmlNode *query, *x;
#endif
int chat_id = GPOINTER_TO_INT(data);
@@ -1218,13 +1236,13 @@ static void jabber_chat_disco_traffic_cb(JabberStream *js, const char *from,
return;
}
- if(!(query = xmlnode_get_child(packet, "query")))
+ if(!(query = purple_xmlnode_get_child(packet, "query")))
return;
chat->xhtml = FALSE;
- for(x = xmlnode_get_child(query, "feature"); x; x = xmlnode_get_next_twin(x)) {
- const char *var = xmlnode_get_attrib(x, "var");
+ for(x = purple_xmlnode_get_child(query, "feature"); x; x = purple_xmlnode_get_next_twin(x)) {
+ const char *var = purple_xmlnode_get_attrib(x, "var");
if(var && !strcmp(var, NS_XHTML_IM)) {
chat->xhtml = TRUE;
@@ -1236,18 +1254,18 @@ static void jabber_chat_disco_traffic_cb(JabberStream *js, const char *from,
void jabber_chat_disco_traffic(JabberChat *chat)
{
JabberIq *iq;
- xmlnode *query;
+ PurpleXmlNode *query;
char *room_jid;
room_jid = g_strdup_printf("%s@%s", chat->room, chat->server);
iq = jabber_iq_new_query(chat->js, JABBER_IQ_GET, NS_DISCO_INFO);
- xmlnode_set_attrib(iq->node, "to", room_jid);
+ purple_xmlnode_set_attrib(iq->node, "to", room_jid);
- query = xmlnode_get_child(iq->node, "query");
+ query = purple_xmlnode_get_child(iq->node, "query");
- xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/muc#traffic");
+ purple_xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/muc#traffic");
jabber_iq_set_callback(iq, jabber_chat_disco_traffic_cb, GINT_TO_POINTER(chat->id));
diff --git a/libpurple/protocols/jabber/chat.h b/libpurple/protocols/jabber/chat.h
index 203b242734..71174e192b 100644
--- a/libpurple/protocols/jabber/chat.h
+++ b/libpurple/protocols/jabber/chat.h
@@ -45,7 +45,7 @@ typedef struct _JabberChat {
char *handle;
GHashTable *components;
int id;
- PurpleConversation *conv;
+ PurpleChatConversation *conv;
gboolean muc;
gboolean xhtml;
PurpleRequestType config_dialog_type;
@@ -77,14 +77,14 @@ void jabber_chat_join(PurpleConnection *gc, GHashTable *data);
JabberChat *jabber_chat_find(JabberStream *js, const char *room,
const char *server);
JabberChat *jabber_chat_find_by_id(JabberStream *js, int id);
-JabberChat *jabber_chat_find_by_conv(PurpleConversation *conv);
+JabberChat *jabber_chat_find_by_conv(PurpleChatConversation *conv);
void jabber_chat_destroy(JabberChat *chat);
void jabber_chat_free(JabberChat *chat);
-gboolean jabber_chat_find_buddy(PurpleConversation *conv, const char *name);
+gboolean jabber_chat_find_buddy(PurpleChatConversation *conv, const char *name);
void jabber_chat_invite(PurpleConnection *gc, int id, const char *message,
const char *name);
void jabber_chat_leave(PurpleConnection *gc, int id);
-char *jabber_chat_buddy_real_name(PurpleConnection *gc, int id, const char *who);
+char *jabber_chat_user_real_name(PurpleConnection *gc, int id, const char *who);
void jabber_chat_request_room_configure(JabberChat *chat);
void jabber_chat_create_instant_room(JabberChat *chat);
void jabber_chat_register(JabberChat *chat);
diff --git a/libpurple/protocols/jabber/data.c b/libpurple/protocols/jabber/data.c
index 9a47633825..6c3878c48a 100644
--- a/libpurple/protocols/jabber/data.c
+++ b/libpurple/protocols/jabber/data.c
@@ -1,5 +1,5 @@
/*
- * purple - Jabber Service Discovery
+ * purple - Handling of XEP-0231: Bits of Binary.
*
* 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
@@ -78,7 +78,7 @@ jabber_data_delete(gpointer cbdata)
JabberData *
-jabber_data_create_from_xml(xmlnode *tag)
+jabber_data_create_from_xml(PurpleXmlNode *tag)
{
JabberData *data;
gchar *raw_data = NULL;
@@ -92,15 +92,15 @@ jabber_data_create_from_xml(xmlnode *tag)
return NULL;
}
- cid = xmlnode_get_attrib(tag, "cid");
- type = xmlnode_get_attrib(tag, "type");
+ cid = purple_xmlnode_get_attrib(tag, "cid");
+ type = purple_xmlnode_get_attrib(tag, "type");
if (!cid || !type) {
purple_debug_error("jabber", "cid or type missing\n");
return NULL;
}
- raw_data = xmlnode_get_data(tag);
+ raw_data = purple_xmlnode_get_data(tag);
if (raw_data == NULL || *raw_data == '\0') {
purple_debug_error("jabber", "data element was empty");
@@ -165,54 +165,54 @@ jabber_data_get_data(const JabberData *data)
return data->data;
}
-xmlnode *
+PurpleXmlNode *
jabber_data_get_xml_definition(const JabberData *data)
{
- xmlnode *tag;
+ PurpleXmlNode *tag;
char *base64data;
g_return_val_if_fail(data != NULL, NULL);
- tag = xmlnode_new("data");
+ tag = purple_xmlnode_new("data");
base64data = purple_base64_encode(data->data, data->size);
- xmlnode_set_namespace(tag, NS_BOB);
- xmlnode_set_attrib(tag, "cid", data->cid);
- xmlnode_set_attrib(tag, "type", data->type);
+ purple_xmlnode_set_namespace(tag, NS_BOB);
+ purple_xmlnode_set_attrib(tag, "cid", data->cid);
+ purple_xmlnode_set_attrib(tag, "type", data->type);
- xmlnode_insert_data(tag, base64data, -1);
+ purple_xmlnode_insert_data(tag, base64data, -1);
g_free(base64data);
return tag;
}
-xmlnode *
+PurpleXmlNode *
jabber_data_get_xhtml_im(const JabberData *data, const gchar *alt)
{
- xmlnode *img;
+ PurpleXmlNode *img;
char *src;
g_return_val_if_fail(data != NULL, NULL);
g_return_val_if_fail(alt != NULL, NULL);
- img = xmlnode_new("img");
- xmlnode_set_attrib(img, "alt", alt);
+ img = purple_xmlnode_new("img");
+ purple_xmlnode_set_attrib(img, "alt", alt);
src = g_strconcat("cid:", data->cid, NULL);
- xmlnode_set_attrib(img, "src", src);
+ purple_xmlnode_set_attrib(img, "src", src);
g_free(src);
return img;
}
-static xmlnode *
+static PurpleXmlNode *
jabber_data_get_xml_request(const gchar *cid)
{
- xmlnode *tag = xmlnode_new("data");
+ PurpleXmlNode *tag = purple_xmlnode_new("data");
- xmlnode_set_namespace(tag, NS_BOB);
- xmlnode_set_attrib(tag, "cid", cid);
+ purple_xmlnode_set_namespace(tag, NS_BOB);
+ purple_xmlnode_set_attrib(tag, "cid", cid);
return tag;
}
@@ -276,7 +276,7 @@ typedef struct {
static void
jabber_data_request_cb(JabberStream *js, const char *from,
- JabberIqType type, const char *id, xmlnode *packet, gpointer data)
+ JabberIqType type, const char *id, PurpleXmlNode *packet, gpointer data)
{
JabberDataRequestData *request_data = (JabberDataRequestData *) data;
gpointer userdata = request_data->userdata;
@@ -284,8 +284,8 @@ jabber_data_request_cb(JabberStream *js, const char *from,
gboolean ephemeral = request_data->ephemeral;
JabberDataRequestCallback *cb = request_data->cb;
- xmlnode *data_element = xmlnode_get_child(packet, "data");
- xmlnode *item_not_found = xmlnode_get_child(packet, "item-not-found");
+ PurpleXmlNode *data_element = purple_xmlnode_get_child(packet, "data");
+ PurpleXmlNode *item_not_found = purple_xmlnode_get_child(packet, "item-not-found");
/* did we get a data element as result? */
if (data_element && type == JABBER_IQ_RESULT) {
@@ -313,7 +313,7 @@ jabber_data_request(JabberStream *js, const gchar *cid, const gchar *who,
gpointer userdata)
{
JabberIq *request;
- xmlnode *data_request;
+ PurpleXmlNode *data_request;
JabberDataRequestData *data;
g_return_if_fail(cid != NULL);
@@ -329,9 +329,9 @@ jabber_data_request(JabberStream *js, const gchar *cid, const gchar *who,
data->ephemeral = ephemeral;
data->cb = cb;
- xmlnode_set_attrib(request->node, "to", who);
+ purple_xmlnode_set_attrib(request->node, "to", who);
jabber_iq_set_callback(request, jabber_data_request_cb, data);
- xmlnode_insert_child(request->node, data_request);
+ purple_xmlnode_insert_child(request->node, data_request);
jabber_iq_send(request);
}
@@ -404,26 +404,26 @@ jabber_data_associate_remote(JabberStream *js, const gchar *who, JabberData *dat
void
jabber_data_parse(JabberStream *js, const char *who, JabberIqType type,
- const char *id, xmlnode *data_node)
+ const char *id, PurpleXmlNode *data_node)
{
JabberIq *result = NULL;
- const char *cid = xmlnode_get_attrib(data_node, "cid");
+ const char *cid = purple_xmlnode_get_attrib(data_node, "cid");
const JabberData *data = cid ? jabber_data_find_local_by_cid(cid) : NULL;
if (!data) {
- xmlnode *item_not_found = xmlnode_new("item-not-found");
+ PurpleXmlNode *item_not_found = purple_xmlnode_new("item-not-found");
result = jabber_iq_new(js, JABBER_IQ_ERROR);
if (who)
- xmlnode_set_attrib(result->node, "to", who);
- xmlnode_set_attrib(result->node, "id", id);
- xmlnode_insert_child(result->node, item_not_found);
+ purple_xmlnode_set_attrib(result->node, "to", who);
+ purple_xmlnode_set_attrib(result->node, "id", id);
+ purple_xmlnode_insert_child(result->node, item_not_found);
} else {
result = jabber_iq_new(js, JABBER_IQ_RESULT);
if (who)
- xmlnode_set_attrib(result->node, "to", who);
- xmlnode_set_attrib(result->node, "id", id);
- xmlnode_insert_child(result->node,
+ purple_xmlnode_set_attrib(result->node, "to", who);
+ purple_xmlnode_set_attrib(result->node, "id", id);
+ purple_xmlnode_insert_child(result->node,
jabber_data_get_xml_definition(data));
/* if the data object is temporary, destroy it and remove the references
to it */
@@ -437,7 +437,8 @@ jabber_data_parse(JabberStream *js, const char *who, JabberIqType type,
void
jabber_data_init(void)
{
- purple_debug_info("jabber", "creating hash tables for data objects\n");
+ if (purple_debug_is_verbose())
+ purple_debug_misc("jabber", "creating hash tables for data objects");
local_data_by_alt = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, NULL);
local_data_by_cid = g_hash_table_new_full(g_str_hash, g_str_equal,
@@ -451,7 +452,8 @@ jabber_data_init(void)
void
jabber_data_uninit(void)
{
- purple_debug_info("jabber", "destroying hash tables for data objects\n");
+ if (purple_debug_is_verbose())
+ purple_debug_info("jabber", "destroying hash tables for data objects");
g_hash_table_destroy(local_data_by_alt);
g_hash_table_destroy(local_data_by_cid);
g_hash_table_destroy(remote_data_by_cid);
diff --git a/libpurple/protocols/jabber/data.h b/libpurple/protocols/jabber/data.h
index 84c200f633..1595c31e4a 100644
--- a/libpurple/protocols/jabber/data.h
+++ b/libpurple/protocols/jabber/data.h
@@ -1,5 +1,5 @@
/*
- * purple - Jabber Service Discovery
+ * purple - Handling of XEP-0231: Bits of Binary.
*
* 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
@@ -50,7 +50,7 @@ JabberData *jabber_data_create_from_data(gconstpointer data, gsize size,
/* create a JabberData instance from an XML "data" element (as defined by
XEP 0231 */
-JabberData *jabber_data_create_from_xml(xmlnode *tag);
+JabberData *jabber_data_create_from_xml(PurpleXmlNode *tag);
/* destroy a JabberData instance, NOT to be used on data that has been
associated, since they get "owned" */
@@ -63,10 +63,10 @@ gsize jabber_data_get_size(const JabberData *data);
gpointer jabber_data_get_data(const JabberData *data);
/* returns the XML definition for the data element */
-xmlnode *jabber_data_get_xml_definition(const JabberData *data);
+PurpleXmlNode *jabber_data_get_xml_definition(const JabberData *data);
/* returns an XHTML-IM "img" tag given a data instance */
-xmlnode *jabber_data_get_xhtml_im(const JabberData *data, const gchar *alt);
+PurpleXmlNode *jabber_data_get_xhtml_im(const JabberData *data, const gchar *alt);
void jabber_data_request(JabberStream *js, const gchar *cid, const gchar *who,
gchar *alt, gboolean ephemeral, JabberDataRequestCallback cb,
@@ -85,7 +85,7 @@ void jabber_data_associate_remote(JabberStream *js, const gchar *who,
/* handles iq requests */
void jabber_data_parse(JabberStream *js, const char *who, JabberIqType type,
- const char *id, xmlnode *data_node);
+ const char *id, PurpleXmlNode *data_node);
void jabber_data_init(void);
void jabber_data_uninit(void);
diff --git a/libpurple/protocols/jabber/disco.c b/libpurple/protocols/jabber/disco.c
index e66a0cb05d..5548161e51 100644
--- a/libpurple/protocols/jabber/disco.c
+++ b/libpurple/protocols/jabber/disco.c
@@ -52,32 +52,32 @@ struct _jabber_disco_items_cb_data {
};
#define SUPPORT_FEATURE(x) { \
- feature = xmlnode_new_child(query, "feature"); \
- xmlnode_set_attrib(feature, "var", x); \
+ feature = purple_xmlnode_new_child(query, "feature"); \
+ purple_xmlnode_set_attrib(feature, "var", x); \
}
static void
jabber_disco_bytestream_server_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
JabberBytestreamsStreamhost *sh = data;
- xmlnode *query = xmlnode_get_child_with_namespace(packet, "query",
+ PurpleXmlNode *query = purple_xmlnode_get_child_with_namespace(packet, "query",
NS_BYTESTREAMS);
if (from && !strcmp(from, sh->jid) && query != NULL) {
- xmlnode *sh_node = xmlnode_get_child(query, "streamhost");
+ PurpleXmlNode *sh_node = purple_xmlnode_get_child(query, "streamhost");
if (sh_node) {
- const char *jid = xmlnode_get_attrib(sh_node, "jid");
- const char *port = xmlnode_get_attrib(sh_node, "port");
+ const char *jid = purple_xmlnode_get_attrib(sh_node, "jid");
+ const char *port = purple_xmlnode_get_attrib(sh_node, "port");
if (jid == NULL || strcmp(jid, from) != 0)
purple_debug_error("jabber", "Invalid jid(%s) for bytestream.\n",
jid ? jid : "(null)");
- sh->host = g_strdup(xmlnode_get_attrib(sh_node, "host"));
- sh->zeroconf = g_strdup(xmlnode_get_attrib(sh_node, "zeroconf"));
+ sh->host = g_strdup(purple_xmlnode_get_attrib(sh_node, "host"));
+ sh->zeroconf = g_strdup(purple_xmlnode_get_attrib(sh_node, "zeroconf"));
if (port != NULL)
sh->port = atoi(port);
}
@@ -101,12 +101,12 @@ jabber_disco_bytestream_server_cb(JabberStream *js, const char *from,
void jabber_disco_info_parse(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *in_query)
+ PurpleXmlNode *in_query)
{
if(type == JABBER_IQ_GET) {
- xmlnode *query, *identity, *feature;
+ PurpleXmlNode *query, *identity, *feature;
JabberIq *iq;
- const char *node = xmlnode_get_attrib(in_query, "node");
+ const char *node = purple_xmlnode_get_attrib(in_query, "node");
char *node_uri = NULL;
/* create custom caps node URI */
@@ -117,29 +117,29 @@ void jabber_disco_info_parse(JabberStream *js, const char *from,
jabber_iq_set_id(iq, id);
if (from)
- xmlnode_set_attrib(iq->node, "to", from);
- query = xmlnode_get_child(iq->node, "query");
+ purple_xmlnode_set_attrib(iq->node, "to", from);
+ query = purple_xmlnode_get_child(iq->node, "query");
if(node)
- xmlnode_set_attrib(query, "node", node);
+ purple_xmlnode_set_attrib(query, "node", node);
if(!node || g_str_equal(node, node_uri)) {
GList *features, *identities;
for(identities = jabber_identities; identities; identities = identities->next) {
JabberIdentity *ident = (JabberIdentity*)identities->data;
- identity = xmlnode_new_child(query, "identity");
- xmlnode_set_attrib(identity, "category", ident->category);
- xmlnode_set_attrib(identity, "type", ident->type);
+ identity = purple_xmlnode_new_child(query, "identity");
+ purple_xmlnode_set_attrib(identity, "category", ident->category);
+ purple_xmlnode_set_attrib(identity, "type", ident->type);
if (ident->lang)
- xmlnode_set_attrib(identity, "xml:lang", ident->lang);
+ purple_xmlnode_set_attrib(identity, "xml:lang", ident->lang);
if (ident->name)
- xmlnode_set_attrib(identity, "name", ident->name);
+ purple_xmlnode_set_attrib(identity, "name", ident->name);
}
for(features = jabber_features; features; features = features->next) {
JabberFeature *feat = (JabberFeature*)features->data;
if (!feat->is_enabled || feat->is_enabled(js, feat->namespace)) {
- feature = xmlnode_new_child(query, "feature");
- xmlnode_set_attrib(feature, "var", feat->namespace);
+ feature = purple_xmlnode_new_child(query, "feature");
+ purple_xmlnode_set_attrib(feature, "var", feat->namespace);
}
}
#ifdef USE_VV
@@ -153,8 +153,8 @@ void jabber_disco_info_parse(JabberStream *js, const char *from,
* node='http://mail.google.com/xmpp/client/caps', ver='1.1'
* advertises as 'voice-v1'.
*/
- xmlnode *feature = xmlnode_new_child(query, "feature");
- xmlnode_set_attrib(feature, "var", NS_GOOGLE_VOICE);
+ PurpleXmlNode *feature = purple_xmlnode_new_child(query, "feature");
+ purple_xmlnode_set_attrib(feature, "var", NS_GOOGLE_VOICE);
} else if (g_str_equal(node, CAPS0115_NODE "#" "video-v1")) {
/*
* HUGE HACK! We advertise this ext (see jabber_presence_create_js
@@ -165,8 +165,8 @@ void jabber_disco_info_parse(JabberStream *js, const char *from,
* node='http://mail.google.com/xmpp/client/caps', ver='1.1'
* advertises as 'video-v1'.
*/
- xmlnode *feature = xmlnode_new_child(query, "feature");
- xmlnode_set_attrib(feature, "var", NS_GOOGLE_VIDEO);
+ PurpleXmlNode *feature = purple_xmlnode_new_child(query, "feature");
+ purple_xmlnode_set_attrib(feature, "var", NS_GOOGLE_VIDEO);
} else if (g_str_equal(node, CAPS0115_NODE "#" "camera-v1")) {
/*
* HUGE HACK! We advertise this ext (see jabber_presence_create_js
@@ -177,40 +177,40 @@ void jabber_disco_info_parse(JabberStream *js, const char *from,
* node='http://mail.google.com/xmpp/client/caps', ver='1.1'
* advertises as 'camera-v1'.
*/
- xmlnode *feature = xmlnode_new_child(query, "feature");
- xmlnode_set_attrib(feature, "var", NS_GOOGLE_CAMERA);
+ PurpleXmlNode *feature = purple_xmlnode_new_child(query, "feature");
+ purple_xmlnode_set_attrib(feature, "var", NS_GOOGLE_CAMERA);
#endif
} else {
- xmlnode *error, *inf;
+ PurpleXmlNode *error, *inf;
/* XXX: gross hack, implement jabber_iq_set_type or something */
- xmlnode_set_attrib(iq->node, "type", "error");
+ purple_xmlnode_set_attrib(iq->node, "type", "error");
iq->type = JABBER_IQ_ERROR;
- error = xmlnode_new_child(query, "error");
- xmlnode_set_attrib(error, "code", "404");
- xmlnode_set_attrib(error, "type", "cancel");
- inf = xmlnode_new_child(error, "item-not-found");
- xmlnode_set_namespace(inf, NS_XMPP_STANZAS);
+ error = purple_xmlnode_new_child(query, "error");
+ purple_xmlnode_set_attrib(error, "code", "404");
+ purple_xmlnode_set_attrib(error, "type", "cancel");
+ inf = purple_xmlnode_new_child(error, "item-not-found");
+ purple_xmlnode_set_namespace(inf, NS_XMPP_STANZAS);
}
g_free(node_uri);
jabber_iq_send(iq);
} else if (type == JABBER_IQ_SET) {
/* wtf? seriously. wtf‽ */
JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR);
- xmlnode *error, *bad_request;
+ PurpleXmlNode *error, *bad_request;
/* Free the <query/> */
- xmlnode_free(xmlnode_get_child(iq->node, "query"));
+ purple_xmlnode_free(purple_xmlnode_get_child(iq->node, "query"));
/* Add an error */
- error = xmlnode_new_child(iq->node, "error");
- xmlnode_set_attrib(error, "type", "modify");
- bad_request = xmlnode_new_child(error, "bad-request");
- xmlnode_set_namespace(bad_request, NS_XMPP_STANZAS);
+ error = purple_xmlnode_new_child(iq->node, "error");
+ purple_xmlnode_set_attrib(error, "type", "modify");
+ bad_request = purple_xmlnode_new_child(error, "bad-request");
+ purple_xmlnode_set_namespace(bad_request, NS_XMPP_STANZAS);
jabber_iq_set_id(iq, id);
if (from)
- xmlnode_set_attrib(iq->node, "to", from);
+ purple_xmlnode_set_attrib(iq->node, "to", from);
jabber_iq_send(iq);
}
@@ -218,15 +218,15 @@ void jabber_disco_info_parse(JabberStream *js, const char *from,
static void jabber_disco_info_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
struct _jabber_disco_info_cb_data *jdicd = data;
- xmlnode *query;
+ PurpleXmlNode *query;
- query = xmlnode_get_child_with_namespace(packet, "query", NS_DISCO_INFO);
+ query = purple_xmlnode_get_child_with_namespace(packet, "query", NS_DISCO_INFO);
if (type == JABBER_IQ_RESULT && query) {
- xmlnode *child;
+ PurpleXmlNode *child;
JabberID *jid;
JabberBuddy *jb;
JabberBuddyResource *jbr = NULL;
@@ -242,12 +242,12 @@ static void jabber_disco_info_cb(JabberStream *js, const char *from,
capabilities = jbr->capabilities;
for(child = query->child; child; child = child->next) {
- if(child->type != XMLNODE_TYPE_TAG)
+ if(child->type != PURPLE_XMLNODE_TYPE_TAG)
continue;
if(!strcmp(child->name, "identity")) {
- const char *category = xmlnode_get_attrib(child, "category");
- const char *type = xmlnode_get_attrib(child, "type");
+ const char *category = purple_xmlnode_get_attrib(child, "category");
+ const char *type = purple_xmlnode_get_attrib(child, "type");
if(!category || !type)
continue;
@@ -271,13 +271,13 @@ static void jabber_disco_info_cb(JabberStream *js, const char *from,
iq = jabber_iq_new_query(js, JABBER_IQ_GET,
NS_BYTESTREAMS);
- xmlnode_set_attrib(iq->node, "to", sh->jid);
+ purple_xmlnode_set_attrib(iq->node, "to", sh->jid);
jabber_iq_set_callback(iq, jabber_disco_bytestream_server_cb, sh);
jabber_iq_send(iq);
}
} else if(!strcmp(child->name, "feature")) {
- const char *var = xmlnode_get_attrib(child, "var");
+ const char *var = purple_xmlnode_get_attrib(child, "var");
if(!var)
continue;
@@ -338,22 +338,22 @@ static void jabber_disco_info_cb(JabberStream *js, const char *from,
void jabber_disco_items_parse(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *query)
+ PurpleXmlNode *query)
{
if(type == JABBER_IQ_GET) {
JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_RESULT,
NS_DISCO_ITEMS);
/* preserve node */
- xmlnode *iq_query = xmlnode_get_child(iq->node, "query");
- const char *node = xmlnode_get_attrib(query, "node");
+ PurpleXmlNode *iq_query = purple_xmlnode_get_child(iq->node, "query");
+ const char *node = purple_xmlnode_get_attrib(query, "node");
if(node)
- xmlnode_set_attrib(iq_query,"node",node);
+ purple_xmlnode_set_attrib(iq_query,"node",node);
jabber_iq_set_id(iq, id);
if (from)
- xmlnode_set_attrib(iq->node, "to", from);
+ purple_xmlnode_set_attrib(iq->node, "to", from);
jabber_iq_send(iq);
}
}
@@ -387,7 +387,7 @@ jabber_disco_finish_server_info_result_cb(JabberStream *js)
}
/* If there are manually specified bytestream proxies, query them */
- ft_proxies = purple_account_get_string(js->gc->account, "ft_proxies", NULL);
+ ft_proxies = purple_account_get_string(purple_connection_get_account(js->gc), "ft_proxies", NULL);
if (ft_proxies) {
JabberIq *iq;
JabberBytestreamsStreamhost *sh;
@@ -409,7 +409,7 @@ jabber_disco_finish_server_info_result_cb(JabberStream *js)
js->bs_proxies = g_list_prepend(js->bs_proxies, sh);
iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_BYTESTREAMS);
- xmlnode_set_attrib(iq->node, "to", sh->jid);
+ purple_xmlnode_set_attrib(iq->node, "to", sh->jid);
jabber_iq_set_callback(iq, jabber_disco_bytestream_server_cb, sh);
jabber_iq_send(iq);
}
@@ -436,18 +436,20 @@ jabber_disco_stun_lookup_cb(GSList *hosts, gpointer data,
}
if (hosts && g_slist_next(hosts)) {
- struct sockaddr *addr = g_slist_next(hosts)->data;
+ common_sockaddr_t addr;
char dst[INET6_ADDRSTRLEN];
int port;
- if (addr->sa_family == AF_INET6) {
- inet_ntop(addr->sa_family, &((struct sockaddr_in6 *) addr)->sin6_addr,
+ memcpy(&addr, g_slist_next(hosts)->data, sizeof(addr));
+
+ if (addr.sa.sa_family == AF_INET6) {
+ inet_ntop(addr.sa.sa_family, &addr.in6.sin6_addr,
dst, sizeof(dst));
- port = ntohs(((struct sockaddr_in6 *) addr)->sin6_port);
+ port = ntohs(addr.in6.sin6_port);
} else {
- inet_ntop(addr->sa_family, &((struct sockaddr_in *) addr)->sin_addr,
+ inet_ntop(addr.sa.sa_family, &addr.in.sin_addr,
dst, sizeof(dst));
- port = ntohs(((struct sockaddr_in *) addr)->sin_port);
+ port = ntohs(addr.in.sin_port);
}
if (js->stun_ip)
@@ -485,7 +487,7 @@ jabber_disco_stun_srv_resolve_cb(PurpleSrvResponse *resp, int results, gpointer
resp[0].hostname, resp[0].port);
account = purple_connection_get_account(js->gc);
js->stun_query =
- purple_dnsquery_a_account(account, resp[0].hostname, resp[0].port,
+ purple_dnsquery_a(account, resp[0].hostname, resp[0].port,
jabber_disco_stun_lookup_cb, js);
}
}
@@ -494,9 +496,9 @@ jabber_disco_stun_srv_resolve_cb(PurpleSrvResponse *resp, int results, gpointer
static void
jabber_disco_server_info_result_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
- xmlnode *query, *child;
+ PurpleXmlNode *query, *child;
if (!from || strcmp(from, js->user->domain)) {
jabber_disco_finish_server_info_result_cb(js);
@@ -509,30 +511,32 @@ jabber_disco_server_info_result_cb(JabberStream *js, const char *from,
return;
}
- query = xmlnode_get_child(packet, "query");
+ query = purple_xmlnode_get_child(packet, "query");
if (!query) {
jabber_disco_finish_server_info_result_cb(js);
return;
}
- for (child = xmlnode_get_child(query, "identity"); child;
- child = xmlnode_get_next_twin(child)) {
+ for (child = purple_xmlnode_get_child(query, "identity"); child;
+ child = purple_xmlnode_get_next_twin(child)) {
const char *category, *type, *name;
- category = xmlnode_get_attrib(child, "category");
- type = xmlnode_get_attrib(child, "type");
+ category = purple_xmlnode_get_attrib(child, "category");
+ type = purple_xmlnode_get_attrib(child, "type");
if(category && type && !strcmp(category, "pubsub") && !strcmp(type,"pep")) {
PurpleConnection *gc = js->gc;
js->pep = TRUE;
- gc->flags |= PURPLE_CONNECTION_SUPPORT_MOODS |
- PURPLE_CONNECTION_SUPPORT_MOOD_MESSAGES;
+ purple_connection_set_flags(gc,
+ purple_connection_get_flags(gc)
+ | PURPLE_CONNECTION_FLAG_SUPPORT_MOODS
+ | PURPLE_CONNECTION_FLAG_SUPPORT_MOOD_MESSAGES);
}
if (!category || strcmp(category, "server"))
continue;
if (!type || strcmp(type, "im"))
continue;
- name = xmlnode_get_attrib(child, "name");
+ name = purple_xmlnode_get_attrib(child, "name");
if (!name)
continue;
@@ -550,7 +554,7 @@ jabber_disco_server_info_result_cb(JabberStream *js, const char *from,
} else if (purple_network_get_stun_ip() == NULL ||
purple_strequal(purple_network_get_stun_ip(), "")) {
js->srv_query_data =
- purple_srv_resolve_account(
+ purple_srv_resolve(
purple_connection_get_account(js->gc), "stun", "udp",
js->user->domain,
jabber_disco_stun_srv_resolve_cb, js);
@@ -558,10 +562,10 @@ jabber_disco_server_info_result_cb(JabberStream *js, const char *from,
}
}
- for (child = xmlnode_get_child(query, "feature"); child;
- child = xmlnode_get_next_twin(child)) {
+ for (child = purple_xmlnode_get_child(query, "feature"); child;
+ child = purple_xmlnode_get_next_twin(child)) {
const char *var;
- var = xmlnode_get_attrib(child, "var");
+ var = purple_xmlnode_get_attrib(child, "var");
if (!var)
continue;
@@ -583,9 +587,9 @@ jabber_disco_server_info_result_cb(JabberStream *js, const char *from,
static void
jabber_disco_server_items_result_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
- xmlnode *query, *child;
+ PurpleXmlNode *query, *child;
if (!from || strcmp(from, js->user->domain) != 0)
return;
@@ -598,23 +602,23 @@ jabber_disco_server_items_result_cb(JabberStream *js, const char *from,
js->chat_servers = g_list_delete_link(js->chat_servers, js->chat_servers);
}
- query = xmlnode_get_child(packet, "query");
+ query = purple_xmlnode_get_child(packet, "query");
- for(child = xmlnode_get_child(query, "item"); child;
- child = xmlnode_get_next_twin(child)) {
+ for(child = purple_xmlnode_get_child(query, "item"); child;
+ child = purple_xmlnode_get_next_twin(child)) {
JabberIq *iq;
const char *jid;
- if(!(jid = xmlnode_get_attrib(child, "jid")))
+ if(!(jid = purple_xmlnode_get_attrib(child, "jid")))
continue;
/* we don't actually care about the specific nodes,
* so we won't query them */
- if(xmlnode_get_attrib(child, "node") != NULL)
+ if(purple_xmlnode_get_attrib(child, "node") != NULL)
continue;
iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_DISCO_INFO);
- xmlnode_set_attrib(iq->node, "to", jid);
+ purple_xmlnode_set_attrib(iq->node, "to", jid);
jabber_iq_set_callback(iq, jabber_disco_info_cb, NULL);
jabber_iq_send(iq);
}
@@ -624,13 +628,13 @@ void jabber_disco_items_server(JabberStream *js)
{
JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_DISCO_ITEMS);
- xmlnode_set_attrib(iq->node, "to", js->user->domain);
+ purple_xmlnode_set_attrib(iq->node, "to", js->user->domain);
jabber_iq_set_callback(iq, jabber_disco_server_items_result_cb, NULL);
jabber_iq_send(iq);
iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_DISCO_INFO);
- xmlnode_set_attrib(iq->node, "to", js->user->domain);
+ purple_xmlnode_set_attrib(iq->node, "to", js->user->domain);
jabber_iq_set_callback(iq, jabber_disco_server_info_result_cb, NULL);
jabber_iq_send(iq);
}
@@ -659,7 +663,7 @@ void jabber_disco_info_do(JabberStream *js, const char *who, JabberDiscoInfoCall
jdicd->callback = callback;
iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_DISCO_INFO);
- xmlnode_set_attrib(iq->node, "to", who);
+ purple_xmlnode_set_attrib(iq->node, "to", who);
jabber_iq_set_callback(iq, jabber_disco_info_cb, jdicd);
jabber_iq_send(iq);
diff --git a/libpurple/protocols/jabber/disco.h b/libpurple/protocols/jabber/disco.h
index 588c62fd8b..02e0577c78 100644
--- a/libpurple/protocols/jabber/disco.h
+++ b/libpurple/protocols/jabber/disco.h
@@ -39,9 +39,9 @@ typedef void (JabberDiscoItemsCallback)(JabberStream *js, const char *jid,
const char *node, GSList *items, gpointer data);
void jabber_disco_info_parse(JabberStream *js, const char *from,
- JabberIqType type, const char *id, xmlnode *in_query);
+ JabberIqType type, const char *id, PurpleXmlNode *in_query);
void jabber_disco_items_parse(JabberStream *js, const char *from,
- JabberIqType type, const char *id, xmlnode *query);
+ JabberIqType type, const char *id, PurpleXmlNode *query);
void jabber_disco_items_server(JabberStream *js);
diff --git a/libpurple/protocols/jabber/facebook_roster.c b/libpurple/protocols/jabber/facebook_roster.c
index 6a966e0531..c46858f756 100644
--- a/libpurple/protocols/jabber/facebook_roster.c
+++ b/libpurple/protocols/jabber/facebook_roster.c
@@ -24,13 +24,11 @@
#include "roster.h"
-#include "internal.h"
-
void
-jabber_facebook_roster_cleanup(JabberStream *js, xmlnode *query)
+jabber_facebook_roster_cleanup(JabberStream *js, PurpleXmlNode *query)
{
PurpleAccount *account = purple_connection_get_account(js->gc);
- xmlnode *item;
+ PurpleXmlNode *item;
GSList *local_buddies;
GHashTable *remove_buddies;
GHashTableIter it;
@@ -44,7 +42,7 @@ jabber_facebook_roster_cleanup(JabberStream *js, xmlnode *query)
/* mark all local buddies as "to be removed" */
remove_buddies = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, NULL);
- local_buddies = purple_find_buddies(account, NULL);
+ local_buddies = purple_blist_find_buddies(account, NULL);
for (; local_buddies; local_buddies = g_slist_delete_link(
local_buddies, local_buddies))
{
@@ -55,10 +53,10 @@ jabber_facebook_roster_cleanup(JabberStream *js, xmlnode *query)
}
/* un-mark all remote buddies */
- for (item = xmlnode_get_child(query, "item"); item;
- item = xmlnode_get_next_twin(item))
+ for (item = purple_xmlnode_get_child(query, "item"); item;
+ item = purple_xmlnode_get_next_twin(item))
{
- jid = xmlnode_get_attrib(item, "jid");
+ jid = purple_xmlnode_get_attrib(item, "jid");
g_hash_table_remove(remove_buddies,
jabber_normalize(account, jid));
@@ -67,67 +65,68 @@ jabber_facebook_roster_cleanup(JabberStream *js, xmlnode *query)
/* remove all not-remote buddies */
g_hash_table_iter_init(&it, remove_buddies);
while (g_hash_table_iter_next(&it, (gpointer*)&jid, (gpointer*)&buddy)) {
- const gchar *alias = purple_buddy_get_local_buddy_alias(buddy);
- item = xmlnode_new_child(query, "item");
- xmlnode_set_namespace(item, xmlnode_get_namespace(query));
- xmlnode_set_attrib(item, "jid", jid);
- xmlnode_set_attrib(item, "subscription", "remove");
+ const gchar *alias = purple_buddy_get_local_alias(buddy);
+ item = purple_xmlnode_new_child(query, "item");
+ purple_xmlnode_set_namespace(item,
+ purple_xmlnode_get_namespace(query));
+ purple_xmlnode_set_attrib(item, "jid", jid);
+ purple_xmlnode_set_attrib(item, "subscription", "remove");
if (alias)
- xmlnode_set_attrib(item, "name", alias);
+ purple_xmlnode_set_attrib(item, "name", alias);
}
g_hash_table_destroy(remove_buddies);
}
gboolean
-jabber_facebook_roster_incoming(JabberStream *js, xmlnode *item)
+jabber_facebook_roster_incoming(JabberStream *js, PurpleXmlNode *item)
{
PurpleAccount *account = purple_connection_get_account(js->gc);
const gchar *jid, *subscription;
gchar *jid_norm;
PurpleBuddy *buddy;
PurpleGroup *buddy_group;
- xmlnode *group;
+ PurpleXmlNode *group;
const gchar *alias;
- subscription = xmlnode_get_attrib(item, "subscription");
+ subscription = purple_xmlnode_get_attrib(item, "subscription");
/* Skip entries added with jabber_facebook_roster_cleanup */
if (g_strcmp0(subscription, "remove") == 0)
return TRUE;
- jid = xmlnode_get_attrib(item, "jid");
+ jid = purple_xmlnode_get_attrib(item, "jid");
jid_norm = g_strdup(jabber_normalize(account, jid));
- buddy = purple_find_buddy(account, jid);
+ buddy = purple_blist_find_buddy(account, jid);
g_free(jid_norm);
/* Facebook forces "Facebook Friends" group */
- while ((group = xmlnode_get_child(item, "group")) != NULL)
- xmlnode_free(group);
- group = xmlnode_new_child(item, "group");
- xmlnode_set_namespace(group, xmlnode_get_namespace(item));
+ while ((group = purple_xmlnode_get_child(item, "group")) != NULL)
+ purple_xmlnode_free(group);
+ group = purple_xmlnode_new_child(item, "group");
+ purple_xmlnode_set_namespace(group, purple_xmlnode_get_namespace(item));
/* We don't have that buddy on the list, add him to the default group */
if (!buddy) {
- xmlnode_insert_data(group,
+ purple_xmlnode_insert_data(group,
JABBER_ROSTER_DEFAULT_GROUP, -1);
return TRUE;
}
/* Facebook forces buddy real name as alias */
- alias = purple_buddy_get_local_buddy_alias(buddy);
+ alias = purple_buddy_get_local_alias(buddy);
if (alias)
- xmlnode_set_attrib(item, "name", alias);
+ purple_xmlnode_set_attrib(item, "name", alias);
/* Add buddy to his group */
buddy_group = purple_buddy_get_group(buddy);
- if (g_strcmp0(purple_group_get_name(buddy_group), _("Buddies")) == 0)
+ if (buddy_group == purple_blist_get_default_group())
buddy_group = NULL;
if (buddy_group) {
- xmlnode_insert_data(group,
+ purple_xmlnode_insert_data(group,
purple_group_get_name(buddy_group), -1);
} else {
- xmlnode_insert_data(group,
+ purple_xmlnode_insert_data(group,
JABBER_ROSTER_DEFAULT_GROUP, -1);
}
diff --git a/libpurple/protocols/jabber/facebook_roster.h b/libpurple/protocols/jabber/facebook_roster.h
index 66d2bcf824..af3aed475f 100644
--- a/libpurple/protocols/jabber/facebook_roster.h
+++ b/libpurple/protocols/jabber/facebook_roster.h
@@ -26,10 +26,10 @@
/* removes deleted buddies from the list */
void
-jabber_facebook_roster_cleanup(JabberStream *js, xmlnode *query);
+jabber_facebook_roster_cleanup(JabberStream *js, PurpleXmlNode *query);
/* ignores facebook roster quirks */
gboolean
-jabber_facebook_roster_incoming(JabberStream *js, xmlnode *item);
+jabber_facebook_roster_incoming(JabberStream *js, PurpleXmlNode *item);
#endif /* PURPLE_XMPP_FACEBOOK_ROSTER_H_ */
diff --git a/libpurple/protocols/jabber/google/gmail.c b/libpurple/protocols/jabber/google/gmail.c
index 5f86b8efac..3d857642e4 100644
--- a/libpurple/protocols/jabber/google/gmail.c
+++ b/libpurple/protocols/jabber/google/gmail.c
@@ -26,10 +26,10 @@
static void
jabber_gmail_parse(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer nul)
+ PurpleXmlNode *packet, gpointer nul)
{
- xmlnode *child;
- xmlnode *message;
+ PurpleXmlNode *child;
+ PurpleXmlNode *message;
const char *to, *url;
const char *in_str;
char *to_name;
@@ -42,18 +42,18 @@ jabber_gmail_parse(JabberStream *js, const char *from,
if (type == JABBER_IQ_ERROR)
return;
- child = xmlnode_get_child(packet, "mailbox");
+ child = purple_xmlnode_get_child(packet, "mailbox");
if (!child)
return;
- in_str = xmlnode_get_attrib(child, "total-matched");
+ in_str = purple_xmlnode_get_attrib(child, "total-matched");
if (in_str && *in_str)
count = atoi(in_str);
/* If Gmail doesn't tell us who the mail is to, let's use our JID */
- to = xmlnode_get_attrib(packet, "to");
+ to = purple_xmlnode_get_attrib(packet, "to");
- message = xmlnode_get_child(child, "mail-thread-info");
+ message = purple_xmlnode_get_child(child, "mail-thread-info");
if (count == 0 || !message) {
if (count > 0) {
@@ -71,51 +71,51 @@ jabber_gmail_parse(JabberStream *js, const char *from,
/* Loop once to see how many messages were returned so we can allocate arrays
* accordingly */
- for (returned_count = 0; message; returned_count++, message=xmlnode_get_next_twin(message));
+ for (returned_count = 0; message; returned_count++, message=purple_xmlnode_get_next_twin(message));
froms = g_new0(const char* , returned_count + 1);
tos = g_new0(const char* , returned_count + 1);
subjects = g_new0(char* , returned_count + 1);
urls = g_new0(const char* , returned_count + 1);
- to = xmlnode_get_attrib(packet, "to");
+ to = purple_xmlnode_get_attrib(packet, "to");
to_name = jabber_get_bare_jid(to);
- url = xmlnode_get_attrib(child, "url");
+ url = purple_xmlnode_get_attrib(child, "url");
if (!url || !*url)
url = "http://www.gmail.com";
- message= xmlnode_get_child(child, "mail-thread-info");
- for (i=0; message; message = xmlnode_get_next_twin(message), i++) {
- xmlnode *sender_node, *subject_node;
+ message= purple_xmlnode_get_child(child, "mail-thread-info");
+ for (i=0; message; message = purple_xmlnode_get_next_twin(message), i++) {
+ PurpleXmlNode *sender_node, *subject_node;
const char *from, *tid;
char *subject;
- subject_node = xmlnode_get_child(message, "subject");
- sender_node = xmlnode_get_child(message, "senders");
- sender_node = xmlnode_get_child(sender_node, "sender");
+ subject_node = purple_xmlnode_get_child(message, "subject");
+ sender_node = purple_xmlnode_get_child(message, "senders");
+ sender_node = purple_xmlnode_get_child(sender_node, "sender");
- while (sender_node && (!xmlnode_get_attrib(sender_node, "unread") ||
- !strcmp(xmlnode_get_attrib(sender_node, "unread"),"0")))
- sender_node = xmlnode_get_next_twin(sender_node);
+ while (sender_node && (!purple_xmlnode_get_attrib(sender_node, "unread") ||
+ !strcmp(purple_xmlnode_get_attrib(sender_node, "unread"),"0")))
+ sender_node = purple_xmlnode_get_next_twin(sender_node);
if (!sender_node) {
i--;
continue;
}
- from = xmlnode_get_attrib(sender_node, "name");
+ from = purple_xmlnode_get_attrib(sender_node, "name");
if (!from || !*from)
- from = xmlnode_get_attrib(sender_node, "address");
- subject = xmlnode_get_data(subject_node);
+ from = purple_xmlnode_get_attrib(sender_node, "address");
+ subject = purple_xmlnode_get_data(subject_node);
/*
- * url = xmlnode_get_attrib(message, "url");
+ * url = purple_xmlnode_get_attrib(message, "url");
*/
tos[i] = (to_name != NULL ? to_name : "");
froms[i] = (from != NULL ? from : "");
subjects[i] = (subject != NULL ? subject : g_strdup(""));
urls[i] = url;
- tid = xmlnode_get_attrib(message, "tid");
+ tid = purple_xmlnode_get_attrib(message, "tid");
if (tid &&
(js->gmail_last_tid == NULL || strcmp(tid, js->gmail_last_tid) > 0)) {
g_free(js->gmail_last_tid);
@@ -135,7 +135,7 @@ jabber_gmail_parse(JabberStream *js, const char *from,
g_free(subjects);
g_free(urls);
- in_str = xmlnode_get_attrib(child, "result-time");
+ in_str = purple_xmlnode_get_attrib(child, "result-time");
if (in_str && *in_str) {
g_free(js->gmail_last_time);
js->gmail_last_time = g_strdup(in_str);
@@ -144,13 +144,13 @@ jabber_gmail_parse(JabberStream *js, const char *from,
void
jabber_gmail_poke(JabberStream *js, const char *from, JabberIqType type,
- const char *id, xmlnode *new_mail)
+ const char *id, PurpleXmlNode *new_mail)
{
- xmlnode *query;
+ PurpleXmlNode *query;
JabberIq *iq;
/* bail if the user isn't interested */
- if (!purple_account_get_check_mail(js->gc->account))
+ if (!purple_account_get_check_mail(purple_connection_get_account(js->gc)))
return;
/* Is this an initial incoming mail notification? If so, send a request for more info */
@@ -160,8 +160,8 @@ jabber_gmail_poke(JabberStream *js, const char *from, JabberIqType type,
/* Acknowledge the notification */
iq = jabber_iq_new(js, JABBER_IQ_RESULT);
if (from)
- xmlnode_set_attrib(iq->node, "to", from);
- xmlnode_set_attrib(iq->node, "id", id);
+ purple_xmlnode_set_attrib(iq->node, "to", from);
+ purple_xmlnode_set_attrib(iq->node, "id", id);
jabber_iq_send(iq);
purple_debug_misc("jabber",
@@ -169,12 +169,12 @@ jabber_gmail_poke(JabberStream *js, const char *from, JabberIqType type,
iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_GOOGLE_MAIL_NOTIFY);
jabber_iq_set_callback(iq, jabber_gmail_parse, NULL);
- query = xmlnode_get_child(iq->node, "query");
+ query = purple_xmlnode_get_child(iq->node, "query");
if (js->gmail_last_time)
- xmlnode_set_attrib(query, "newer-than-time", js->gmail_last_time);
+ purple_xmlnode_set_attrib(query, "newer-than-time", js->gmail_last_time);
if (js->gmail_last_tid)
- xmlnode_set_attrib(query, "newer-than-tid", js->gmail_last_tid);
+ purple_xmlnode_set_attrib(query, "newer-than-tid", js->gmail_last_tid);
jabber_iq_send(iq);
return;
@@ -182,23 +182,23 @@ jabber_gmail_poke(JabberStream *js, const char *from, JabberIqType type,
void jabber_gmail_init(JabberStream *js) {
JabberIq *iq;
- xmlnode *usersetting, *mailnotifications;
+ PurpleXmlNode *usersetting, *mailnotifications;
if (!purple_account_get_check_mail(purple_connection_get_account(js->gc)))
return;
/*
- * Quoting http://code.google.com/apis/talk/jep_extensions/usersettings.html:
+ * Quoting https://developers.google.com/talk/jep_extensions/usersettings:
* To ensure better compatibility with other clients, rather than
* setting this value to "false" to turn off notifications, it is
* recommended that a client set this to "true" and filter incoming
* email notifications itself.
*/
iq = jabber_iq_new(js, JABBER_IQ_SET);
- usersetting = xmlnode_new_child(iq->node, "usersetting");
- xmlnode_set_namespace(usersetting, "google:setting");
- mailnotifications = xmlnode_new_child(usersetting, "mailnotifications");
- xmlnode_set_attrib(mailnotifications, "value", "true");
+ usersetting = purple_xmlnode_new_child(iq->node, "usersetting");
+ purple_xmlnode_set_namespace(usersetting, "google:setting");
+ mailnotifications = purple_xmlnode_new_child(usersetting, "mailnotifications");
+ purple_xmlnode_set_attrib(mailnotifications, "value", "true");
jabber_iq_send(iq);
iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_GOOGLE_MAIL_NOTIFY);
diff --git a/libpurple/protocols/jabber/google/gmail.h b/libpurple/protocols/jabber/google/gmail.h
index 2058802ea2..713e5e573e 100644
--- a/libpurple/protocols/jabber/google/gmail.h
+++ b/libpurple/protocols/jabber/google/gmail.h
@@ -25,6 +25,6 @@
void jabber_gmail_init(JabberStream *js);
void jabber_gmail_poke(JabberStream *js, const char *from, JabberIqType type,
- const char *id, xmlnode *new_mail);
+ const char *id, PurpleXmlNode *new_mail);
#endif /* PURPLE_JABBER_GOOGLE_GMAIL_H_ */
diff --git a/libpurple/protocols/jabber/google/google.c b/libpurple/protocols/jabber/google/google.c
index 55cb68cab5..ba79112c14 100644
--- a/libpurple/protocols/jabber/google/google.c
+++ b/libpurple/protocols/jabber/google/google.c
@@ -152,7 +152,7 @@ void google_buddy_node_chat(PurpleBlistNode *node, gpointer data)
gchar *room;
gchar *uuid = purple_uuid_random();
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = PURPLE_BUDDY(node);
gc = purple_account_get_connection(purple_buddy_get_account(buddy));
diff --git a/libpurple/protocols/jabber/google/google_p2p.c b/libpurple/protocols/jabber/google/google_p2p.c
new file mode 100644
index 0000000000..6b0f13d3f2
--- /dev/null
+++ b/libpurple/protocols/jabber/google/google_p2p.c
@@ -0,0 +1,452 @@
+/**
+ * @file google_p2p.c
+ *
+ * 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 "glibcompat.h"
+
+#include "google_p2p.h"
+#include "jingle/jingle.h"
+#include "debug.h"
+
+#include <string.h>
+
+struct _JingleGoogleP2PPrivate
+{
+ GList *local_candidates;
+ GList *remote_candidates;
+};
+
+#define JINGLE_GOOGLE_P2P_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), JINGLE_TYPE_GOOGLE_P2P, JingleGoogleP2PPrivate))
+
+static void jingle_google_p2p_class_init (JingleGoogleP2PClass *klass);
+static void jingle_google_p2p_init (JingleGoogleP2P *google_p2p);
+static void jingle_google_p2p_finalize (GObject *object);
+static void jingle_google_p2p_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
+static void jingle_google_p2p_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
+static JingleTransport *jingle_google_p2p_parse_internal(PurpleXmlNode *google_p2p);
+static PurpleXmlNode *jingle_google_p2p_to_xml_internal(JingleTransport *transport, PurpleXmlNode *content, JingleActionType action);
+static void jingle_google_p2p_add_local_candidate(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate);
+static GList *jingle_google_p2p_get_remote_candidates(JingleTransport *transport);
+
+enum {
+ PROP_0,
+ PROP_LOCAL_CANDIDATES,
+ PROP_REMOTE_CANDIDATES,
+ PROP_LAST
+};
+
+static JingleTransportClass *parent_class = NULL;
+static GParamSpec *properties[PROP_LAST];
+
+static JingleGoogleP2PCandidate *
+jingle_google_p2p_candidate_copy(JingleGoogleP2PCandidate *candidate)
+{
+ JingleGoogleP2PCandidate *new_candidate = g_new0(JingleGoogleP2PCandidate, 1);
+ new_candidate->id = g_strdup(candidate->id);
+ new_candidate->address = g_strdup(candidate->address);
+ new_candidate->port = candidate->port;
+ new_candidate->preference = candidate->preference;
+ new_candidate->type = g_strdup(candidate->type);
+ new_candidate->protocol = g_strdup(candidate->protocol);
+ new_candidate->username = g_strdup(candidate->username);
+ new_candidate->password = g_strdup(candidate->password);
+ new_candidate->generation = candidate->generation;
+
+ new_candidate->rem_known = candidate->rem_known;
+
+ return new_candidate;
+}
+
+static void
+jingle_google_p2p_candidate_free(JingleGoogleP2PCandidate *candidate)
+{
+ g_free(candidate->id);
+ g_free(candidate->address);
+ g_free(candidate->type);
+ g_free(candidate->protocol);
+ g_free(candidate->username);
+ g_free(candidate->password);
+}
+
+GType
+jingle_google_p2p_candidate_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("JingleGoogleP2PCandidate",
+ (GBoxedCopyFunc)jingle_google_p2p_candidate_copy,
+ (GBoxedFreeFunc)jingle_google_p2p_candidate_free);
+ }
+ return type;
+}
+
+JingleGoogleP2PCandidate *
+jingle_google_p2p_candidate_new(const gchar *id, guint generation,
+ const gchar *address, guint port, guint preference,
+ const gchar *type, const gchar *protocol,
+ const gchar *username, const gchar *password)
+{
+ JingleGoogleP2PCandidate *candidate = g_new0(JingleGoogleP2PCandidate, 1);
+ candidate->id = g_strdup(id);
+ candidate->address = g_strdup(address);
+ candidate->port = port;
+ candidate->preference = preference;
+ candidate->type = g_strdup(type);
+ candidate->protocol = g_strdup(protocol);
+ candidate->username = g_strdup(username);
+ candidate->password = g_strdup(password);
+ candidate->generation = generation;
+
+ candidate->rem_known = FALSE;
+ return candidate;
+}
+
+GType
+jingle_google_p2p_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ static const GTypeInfo info = {
+ sizeof(JingleGoogleP2PClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) jingle_google_p2p_class_init,
+ NULL,
+ NULL,
+ sizeof(JingleGoogleP2P),
+ 0,
+ (GInstanceInitFunc) jingle_google_p2p_init,
+ NULL
+ };
+ type = g_type_register_static(JINGLE_TYPE_TRANSPORT, "JingleGoogleP2P", &info, 0);
+ }
+ return type;
+}
+
+static void
+jingle_google_p2p_class_init(JingleGoogleP2PClass *klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *)klass;
+ parent_class = g_type_class_peek_parent(klass);
+
+ gobject_class->finalize = jingle_google_p2p_finalize;
+ gobject_class->set_property = jingle_google_p2p_set_property;
+ gobject_class->get_property = jingle_google_p2p_get_property;
+ klass->parent_class.to_xml = jingle_google_p2p_to_xml_internal;
+ klass->parent_class.parse = jingle_google_p2p_parse_internal;
+ klass->parent_class.transport_type = NS_GOOGLE_TRANSPORT_P2P;
+ klass->parent_class.add_local_candidate = jingle_google_p2p_add_local_candidate;
+ klass->parent_class.get_remote_candidates = jingle_google_p2p_get_remote_candidates;
+
+ g_type_class_add_private(klass, sizeof(JingleGoogleP2PPrivate));
+
+ properties[PROP_LOCAL_CANDIDATES] = g_param_spec_pointer("local-candidates",
+ "Local candidates",
+ "The local candidates for this transport.",
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_REMOTE_CANDIDATES] = g_param_spec_pointer("remote-candidates",
+ "Remote candidates",
+ "The remote candidates for this transport.",
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(gobject_class, PROP_LAST, properties);
+}
+
+static void
+jingle_google_p2p_init(JingleGoogleP2P *google_p2p)
+{
+ google_p2p->priv = JINGLE_GOOGLE_P2P_GET_PRIVATE(google_p2p);
+ google_p2p->priv->local_candidates = NULL;
+ google_p2p->priv->remote_candidates = NULL;
+}
+
+static void
+jingle_google_p2p_finalize(GObject *google_p2p)
+{
+/* JingleGoogleP2PPrivate *priv = JINGLE_GOOGLE_P2P_GET_PRIVATE(google_p2p); */
+ purple_debug_info("jingle","jingle_google_p2p_finalize\n");
+
+ G_OBJECT_CLASS(parent_class)->finalize(google_p2p);
+}
+
+static void
+jingle_google_p2p_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+ JingleGoogleP2P *google_p2p;
+
+ g_return_if_fail(object != NULL);
+ g_return_if_fail(JINGLE_IS_GOOGLE_P2P(object));
+
+ google_p2p = JINGLE_GOOGLE_P2P(object);
+
+ switch (prop_id) {
+ case PROP_LOCAL_CANDIDATES:
+ google_p2p->priv->local_candidates = g_value_get_pointer(value);
+ break;
+ case PROP_REMOTE_CANDIDATES:
+ google_p2p->priv->remote_candidates = g_value_get_pointer(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+jingle_google_p2p_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ JingleGoogleP2P *google_p2p;
+
+ g_return_if_fail(object != NULL);
+ g_return_if_fail(JINGLE_IS_GOOGLE_P2P(object));
+
+ google_p2p = JINGLE_GOOGLE_P2P(object);
+
+ switch (prop_id) {
+ case PROP_LOCAL_CANDIDATES:
+ g_value_set_pointer(value, google_p2p->priv->local_candidates);
+ break;
+ case PROP_REMOTE_CANDIDATES:
+ g_value_set_pointer(value, google_p2p->priv->remote_candidates);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+jingle_google_p2p_add_local_candidate(JingleTransport *transport, const gchar *id,
+ guint generation, PurpleMediaCandidate *candidate)
+{
+ JingleGoogleP2P *google_p2p = JINGLE_GOOGLE_P2P(transport);
+ JingleGoogleP2PCandidate *google_p2p_candidate;
+ gchar *ip;
+ gchar *username;
+ gchar *password;
+ PurpleMediaCandidateType type;
+ PurpleMediaNetworkProtocol protocol;
+ GList *iter;
+
+ ip = purple_media_candidate_get_ip(candidate);
+ username = purple_media_candidate_get_username(candidate);
+ password = purple_media_candidate_get_password(candidate);
+ type = purple_media_candidate_get_candidate_type(candidate);
+ protocol = purple_media_candidate_get_protocol(candidate);
+
+ google_p2p_candidate = jingle_google_p2p_candidate_new(id, generation,
+ ip, purple_media_candidate_get_port(candidate),
+ purple_media_candidate_get_priority(candidate),
+ type == PURPLE_MEDIA_CANDIDATE_TYPE_HOST ? "host" :
+ type == PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX ? "srflx" :
+ type == PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX ? "prflx" :
+ type == PURPLE_MEDIA_CANDIDATE_TYPE_RELAY ? "relay" :
+ "",
+ protocol == PURPLE_MEDIA_NETWORK_PROTOCOL_UDP ? "udp" : "tcp",
+ username, password);
+
+ g_free(password);
+ g_free(username);
+ g_free(ip);
+
+ for (iter = google_p2p->priv->local_candidates; iter; iter = g_list_next(iter)) {
+ JingleGoogleP2PCandidate *c = iter->data;
+ if (!strcmp(c->id, id)) {
+ generation = c->generation + 1;
+
+ g_boxed_free(JINGLE_TYPE_GOOGLE_P2P_CANDIDATE, c);
+ google_p2p->priv->local_candidates = g_list_delete_link(
+ google_p2p->priv->local_candidates, iter);
+
+ google_p2p_candidate->generation = generation;
+
+ google_p2p->priv->local_candidates = g_list_append(
+ google_p2p->priv->local_candidates, candidate);
+
+ g_object_notify_by_pspec(G_OBJECT(google_p2p), properties[PROP_LOCAL_CANDIDATES]);
+
+ return;
+ }
+ }
+
+ google_p2p->priv->local_candidates = g_list_append(
+ google_p2p->priv->local_candidates, google_p2p_candidate);
+
+ g_object_notify_by_pspec(G_OBJECT(google_p2p), properties[PROP_LOCAL_CANDIDATES]);
+}
+
+static GList *
+jingle_google_p2p_get_remote_candidates(JingleTransport *transport)
+{
+ JingleGoogleP2P *google_p2p = JINGLE_GOOGLE_P2P(transport);
+ GList *candidates = google_p2p->priv->remote_candidates;
+ GList *ret = NULL;
+
+ for (; candidates; candidates = g_list_next(candidates)) {
+ JingleGoogleP2PCandidate *candidate = candidates->data;
+ PurpleMediaCandidate *new_candidate = purple_media_candidate_new("", 0,
+ !strcmp(candidate->type, "host") ?
+ PURPLE_MEDIA_CANDIDATE_TYPE_HOST :
+ !strcmp(candidate->type, "srflx") ?
+ PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX :
+ !strcmp(candidate->type, "prflx") ?
+ PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX :
+ !strcmp(candidate->type, "relay") ?
+ PURPLE_MEDIA_CANDIDATE_TYPE_RELAY : 0,
+ !strcmp(candidate->protocol, "udp") ?
+ PURPLE_MEDIA_NETWORK_PROTOCOL_UDP :
+ PURPLE_MEDIA_NETWORK_PROTOCOL_TCP,
+ candidate->address, candidate->port);
+ g_object_set(new_candidate,
+ "username", candidate->username,
+ "password", candidate->password,
+ "priority", candidate->preference,
+ NULL);
+ ret = g_list_append(ret, new_candidate);
+ }
+
+ return ret;
+}
+
+static JingleGoogleP2PCandidate *
+jingle_google_p2p_get_remote_candidate_by_id(JingleGoogleP2P *google_p2p,
+ const gchar *id)
+{
+ GList *iter = google_p2p->priv->remote_candidates;
+ for (; iter; iter = g_list_next(iter)) {
+ JingleGoogleP2PCandidate *candidate = iter->data;
+ if (!strcmp(candidate->id, id)) {
+ return candidate;
+ }
+ }
+ return NULL;
+}
+
+static void
+jingle_google_p2p_add_remote_candidate(JingleGoogleP2P *google_p2p, JingleGoogleP2PCandidate *candidate)
+{
+ JingleGoogleP2PPrivate *priv = JINGLE_GOOGLE_P2P_GET_PRIVATE(google_p2p);
+ JingleGoogleP2PCandidate *google_p2p_candidate =
+ jingle_google_p2p_get_remote_candidate_by_id(google_p2p,
+ candidate->id);
+ if (google_p2p_candidate != NULL) {
+ priv->remote_candidates = g_list_remove(priv->remote_candidates,
+ google_p2p_candidate);
+ g_boxed_free(JINGLE_TYPE_GOOGLE_P2P_CANDIDATE, google_p2p_candidate);
+ }
+ priv->remote_candidates = g_list_append(priv->remote_candidates, candidate);
+
+ g_object_notify_by_pspec(G_OBJECT(google_p2p), properties[PROP_REMOTE_CANDIDATES]);
+}
+
+static JingleTransport *
+jingle_google_p2p_parse_internal(PurpleXmlNode *google_p2p)
+{
+ JingleTransport *transport = parent_class->parse(google_p2p);
+ PurpleXmlNode *candidate = purple_xmlnode_get_child(google_p2p, "candidate");
+ JingleGoogleP2PCandidate *google_p2p_candidate = NULL;
+
+ for (; candidate; candidate = purple_xmlnode_get_next_twin(candidate)) {
+ const gchar *generation = purple_xmlnode_get_attrib(candidate, "generation");
+ const gchar *id = purple_xmlnode_get_attrib(candidate, "name");
+ const gchar *address = purple_xmlnode_get_attrib(candidate, "address");
+ const gchar *port = purple_xmlnode_get_attrib(candidate, "port");
+ const gchar *preference = purple_xmlnode_get_attrib(candidate, "preference");
+ const gchar *type = purple_xmlnode_get_attrib(candidate, "type");
+ const gchar *protocol = purple_xmlnode_get_attrib(candidate, "protocol");
+ const gchar *username = purple_xmlnode_get_attrib(candidate, "username");
+ const gchar *password = purple_xmlnode_get_attrib(candidate, "password");
+
+ if (!generation || !id || !address || !port || !preference ||
+ !type || !protocol || !username || !password)
+ continue;
+
+ google_p2p_candidate = jingle_google_p2p_candidate_new(id,
+ atoi(generation),
+ address,
+ atoi(port),
+ atoi(preference),
+ type,
+ protocol,
+ username, password);
+ google_p2p_candidate->rem_known = TRUE;
+ jingle_google_p2p_add_remote_candidate(JINGLE_GOOGLE_P2P(transport), google_p2p_candidate);
+ }
+
+ return transport;
+}
+
+static PurpleXmlNode *
+jingle_google_p2p_to_xml_internal(JingleTransport *transport, PurpleXmlNode *content, JingleActionType action)
+{
+ PurpleXmlNode *node = parent_class->to_xml(transport, content, action);
+
+ if (action == JINGLE_SESSION_INITIATE ||
+ action == JINGLE_SESSION_ACCEPT ||
+ action == JINGLE_TRANSPORT_INFO ||
+ action == JINGLE_CONTENT_ADD ||
+ action == JINGLE_TRANSPORT_REPLACE) {
+ JingleGoogleP2PPrivate *priv = JINGLE_GOOGLE_P2P_GET_PRIVATE(transport);
+ GList *iter = priv->local_candidates;
+
+ for (; iter; iter = g_list_next(iter)) {
+ JingleGoogleP2PCandidate *candidate = iter->data;
+ PurpleXmlNode *xmltransport;
+ gchar *generation, *network, *port, *preference;
+
+ if (candidate->rem_known == TRUE)
+ continue;
+
+ candidate->rem_known = TRUE;
+
+ xmltransport = purple_xmlnode_new_child(node, "candidate");
+ generation = g_strdup_printf("%d", candidate->generation);
+ network = g_strdup_printf("%d", candidate->network);
+ port = g_strdup_printf("%d", candidate->port);
+ preference = g_strdup_printf("%d", candidate->preference);
+
+ purple_xmlnode_set_attrib(xmltransport, "generation", generation);
+ purple_xmlnode_set_attrib(xmltransport, "name", candidate->id);
+ purple_xmlnode_set_attrib(xmltransport, "address", candidate->address);
+ purple_xmlnode_set_attrib(xmltransport, "network", network);
+ purple_xmlnode_set_attrib(xmltransport, "port", port);
+ purple_xmlnode_set_attrib(xmltransport, "preference", preference);
+ purple_xmlnode_set_attrib(xmltransport, "protocol", candidate->protocol);
+ purple_xmlnode_set_attrib(xmltransport, "type", candidate->type);
+ purple_xmlnode_set_attrib(xmltransport, "username", candidate->username);
+ purple_xmlnode_set_attrib(xmltransport, "password", candidate->password);
+
+ g_free(generation);
+ g_free(network);
+ g_free(port);
+ g_free(preference);
+ }
+ }
+
+ return node;
+}
+
diff --git a/libpurple/protocols/jabber/google/google_p2p.h b/libpurple/protocols/jabber/google/google_p2p.h
new file mode 100644
index 0000000000..dcf22ff283
--- /dev/null
+++ b/libpurple/protocols/jabber/google/google_p2p.h
@@ -0,0 +1,102 @@
+/**
+ * @file google_p2p.h
+ *
+ * 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 PURPLE_JABBER_JINGLE_GOOGLE_P2P_H
+#define PURPLE_JABBER_JINGLE_GOOGLE_P2P_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "jingle/transport.h"
+
+G_BEGIN_DECLS
+
+#define JINGLE_TYPE_GOOGLE_P2P (jingle_google_p2p_get_type())
+#define JINGLE_TYPE_GOOGLE_P2P_CANDIDATE (jingle_google_p2p_candidate_get_type())
+#define JINGLE_GOOGLE_P2P(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), JINGLE_TYPE_GOOGLE_P2P, JingleGoogleP2P))
+#define JINGLE_GOOGLE_P2P_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), JINGLE_TYPE_GOOGLE_P2P, JingleGoogleP2PClass))
+#define JINGLE_IS_GOOGLE_P2P(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), JINGLE_TYPE_GOOGLE_P2P))
+#define JINGLE_IS_GOOGLE_P2P_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), JINGLE_TYPE_GOOGLE_P2P))
+#define JINGLE_GOOGLE_P2P_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), JINGLE_TYPE_GOOGLE_P2P, JingleGoogleP2PClass))
+
+/** @copydoc _JingleGoogleP2P */
+typedef struct _JingleGoogleP2P JingleGoogleP2P;
+/** @copydoc _JingleGoogleP2PClass */
+typedef struct _JingleGoogleP2PClass JingleGoogleP2PClass;
+/** @copydoc _JingleGoogleP2PPrivate */
+typedef struct _JingleGoogleP2PPrivate JingleGoogleP2PPrivate;
+/** @copydoc _JingleGoogleP2PCandidate */
+typedef struct _JingleGoogleP2PCandidate JingleGoogleP2PCandidate;
+
+/** The Google P2P class */
+struct _JingleGoogleP2PClass
+{
+ JingleTransportClass parent_class; /**< The parent class. */
+
+ PurpleXmlNode *(*to_xml) (JingleTransport *transport, PurpleXmlNode *content, JingleActionType action);
+ JingleTransport *(*parse) (PurpleXmlNode *transport);
+};
+
+/** The Google P2P class's private data */
+struct _JingleGoogleP2P
+{
+ JingleTransport parent; /**< The parent of this object. */
+ JingleGoogleP2PPrivate *priv; /**< The private data of this object. */
+};
+
+struct _JingleGoogleP2PCandidate
+{
+ gchar *id;
+ gchar *address;
+ guint port;
+ guint preference;
+ gchar *type;
+ gchar *protocol;
+ guint network;
+ gchar *username;
+ gchar *password;
+ guint generation;
+
+ gboolean rem_known; /* TRUE if the remote side knows
+ * about this candidate */
+};
+
+GType jingle_google_p2p_candidate_get_type(void);
+
+/**
+ * Gets the Google P2P class's GType
+ *
+ * @return The Google P2P class's GType.
+ */
+GType jingle_google_p2p_get_type(void);
+
+JingleGoogleP2PCandidate *jingle_google_p2p_candidate_new(const gchar *id,
+ guint generation, const gchar *address, guint port, guint preference,
+ const gchar *type, const gchar *protocol,
+ const gchar *username, const gchar *password);
+
+G_END_DECLS
+
+#endif /* PURPLE_JABBER_JINGLE_GOOGLE_P2P_H */
+
diff --git a/libpurple/protocols/jabber/google/google_presence.c b/libpurple/protocols/jabber/google/google_presence.c
index c5ec55d946..b0095adcd4 100644
--- a/libpurple/protocols/jabber/google/google_presence.c
+++ b/libpurple/protocols/jabber/google/google_presence.c
@@ -27,12 +27,12 @@ void jabber_google_presence_incoming(JabberStream *js, const char *user, JabberB
if (!js->googletalk)
return;
if (jbr->status && purple_str_has_prefix(jbr->status, "♫ ")) {
- purple_prpl_got_user_status(js->gc->account, user, "tune",
+ purple_prpl_got_user_status(purple_connection_get_account(js->gc), user, "tune",
PURPLE_TUNE_TITLE, jbr->status + strlen("♫ "), NULL);
g_free(jbr->status);
jbr->status = NULL;
} else {
- purple_prpl_got_user_status_deactive(js->gc->account, user, "tune");
+ purple_prpl_got_user_status_deactive(purple_connection_get_account(js->gc), user, "tune");
}
}
diff --git a/libpurple/protocols/jabber/google/google_roster.c b/libpurple/protocols/jabber/google/google_roster.c
index d0de12cc4a..7ed238d96a 100644
--- a/libpurple/protocols/jabber/google/google_roster.c
+++ b/libpurple/protocols/jabber/google/google_roster.c
@@ -26,35 +26,35 @@
#include "xmlnode.h"
#include "roster.h"
-void jabber_google_roster_outgoing(JabberStream *js, xmlnode *query, xmlnode *item)
+void jabber_google_roster_outgoing(JabberStream *js, PurpleXmlNode *query, PurpleXmlNode *item)
{
PurpleAccount *account = purple_connection_get_account(js->gc);
- GSList *list = account->deny;
- const char *jid = xmlnode_get_attrib(item, "jid");
+ GSList *list = purple_account_privacy_get_denied(account);
+ const char *jid = purple_xmlnode_get_attrib(item, "jid");
char *jid_norm = (char *)jabber_normalize(account, jid);
while (list) {
if (!strcmp(jid_norm, (char*)list->data)) {
- xmlnode_set_attrib(query, "xmlns:gr", NS_GOOGLE_ROSTER);
- xmlnode_set_attrib(query, "gr:ext", "2");
- xmlnode_set_attrib(item, "gr:t", "B");
+ purple_xmlnode_set_attrib(query, "xmlns:gr", NS_GOOGLE_ROSTER);
+ purple_xmlnode_set_attrib(query, "gr:ext", "2");
+ purple_xmlnode_set_attrib(item, "gr:t", "B");
return;
}
list = list->next;
}
}
-gboolean jabber_google_roster_incoming(JabberStream *js, xmlnode *item)
+gboolean jabber_google_roster_incoming(JabberStream *js, PurpleXmlNode *item)
{
PurpleAccount *account = purple_connection_get_account(js->gc);
- const char *jid = xmlnode_get_attrib(item, "jid");
+ const char *jid = purple_xmlnode_get_attrib(item, "jid");
gboolean on_block_list = FALSE;
char *jid_norm;
- const char *grt = xmlnode_get_attrib_with_namespace(item, "t", NS_GOOGLE_ROSTER);
- const char *subscription = xmlnode_get_attrib(item, "subscription");
- const char *ask = xmlnode_get_attrib(item, "ask");
+ const char *grt = purple_xmlnode_get_attrib_with_namespace(item, "t", NS_GOOGLE_ROSTER);
+ const char *subscription = purple_xmlnode_get_attrib(item, "subscription");
+ const char *ask = purple_xmlnode_get_attrib(item, "ask");
if ((!subscription || !strcmp(subscription, "none")) && !ask) {
/* The Google Talk servers will automatically add people from your Gmail address book
@@ -63,20 +63,22 @@ gboolean jabber_google_roster_incoming(JabberStream *js, xmlnode *item)
return FALSE;
}
- jid_norm = g_strdup(jabber_normalize(account, jid));
+ jid_norm = g_strdup(jabber_normalize(account, jid));
- on_block_list = NULL != g_slist_find_custom(account->deny, jid_norm,
- (GCompareFunc)strcmp);
+ on_block_list = NULL != g_slist_find_custom(purple_account_privacy_get_denied(account),
+ jid_norm, (GCompareFunc)strcmp);
if (grt && (*grt == 'H' || *grt == 'h')) {
/* Hidden; don't show this buddy. */
- GSList *buddies = purple_find_buddies(account, jid_norm);
- if (buddies)
+ GSList *buddies = purple_blist_find_buddies(account, jid_norm);
+ if (buddies) {
purple_debug_info("jabber", "Removing %s from local buddy list\n",
jid_norm);
- for ( ; buddies; buddies = g_slist_delete_link(buddies, buddies)) {
- purple_blist_remove_buddy(buddies->data);
+ do {
+ purple_blist_remove_buddy(buddies->data);
+ buddies = g_slist_delete_link(buddies, buddies);
+ } while (buddies);
}
g_free(jid_norm);
@@ -85,10 +87,10 @@ gboolean jabber_google_roster_incoming(JabberStream *js, xmlnode *item)
if (!on_block_list && (grt && (*grt == 'B' || *grt == 'b'))) {
purple_debug_info("jabber", "Blocking %s\n", jid_norm);
- purple_privacy_deny_add(account, jid_norm, TRUE);
+ purple_account_privacy_deny_add(account, jid_norm, TRUE);
} else if (on_block_list && (!grt || (*grt != 'B' && *grt != 'b' ))){
purple_debug_info("jabber", "Unblocking %s\n", jid_norm);
- purple_privacy_deny_remove(account, jid_norm, TRUE);
+ purple_account_privacy_deny_remove(account, jid_norm, TRUE);
}
g_free(jid_norm);
@@ -100,9 +102,9 @@ void jabber_google_roster_add_deny(JabberStream *js, const char *who)
PurpleAccount *account;
GSList *buddies;
JabberIq *iq;
- xmlnode *query;
- xmlnode *item;
- xmlnode *group;
+ PurpleXmlNode *query;
+ PurpleXmlNode *item;
+ PurpleXmlNode *group;
PurpleBuddy *b;
JabberBuddy *jb;
const char *balias;
@@ -110,36 +112,34 @@ void jabber_google_roster_add_deny(JabberStream *js, const char *who)
jb = jabber_buddy_find(js, who, TRUE);
account = purple_connection_get_account(js->gc);
- buddies = purple_find_buddies(account, who);
+ buddies = purple_blist_find_buddies(account, who);
if(!buddies)
return;
- b = buddies->data;
-
iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:roster");
- query = xmlnode_get_child(iq->node, "query");
- item = xmlnode_new_child(query, "item");
+ query = purple_xmlnode_get_child(iq->node, "query");
+ item = purple_xmlnode_new_child(query, "item");
- while(buddies) {
+ do {
PurpleGroup *g;
b = buddies->data;
g = purple_buddy_get_group(b);
- group = xmlnode_new_child(item, "group");
- xmlnode_insert_data(group,
+ group = purple_xmlnode_new_child(item, "group");
+ purple_xmlnode_insert_data(group,
jabber_roster_group_get_global_name(g), -1);
- buddies = buddies->next;
- }
+ buddies = g_slist_delete_link(buddies, buddies);
+ } while (buddies);
- balias = purple_buddy_get_local_buddy_alias(b);
- xmlnode_set_attrib(item, "jid", who);
- xmlnode_set_attrib(item, "name", balias ? balias : "");
- xmlnode_set_attrib(item, "gr:t", "B");
- xmlnode_set_attrib(query, "xmlns:gr", NS_GOOGLE_ROSTER);
- xmlnode_set_attrib(query, "gr:ext", "2");
+ balias = purple_buddy_get_local_alias(b);
+ purple_xmlnode_set_attrib(item, "jid", who);
+ purple_xmlnode_set_attrib(item, "name", balias ? balias : "");
+ purple_xmlnode_set_attrib(item, "gr:t", "B");
+ purple_xmlnode_set_attrib(query, "xmlns:gr", NS_GOOGLE_ROSTER);
+ purple_xmlnode_set_attrib(query, "gr:ext", "2");
jabber_iq_send(iq);
@@ -165,41 +165,39 @@ void jabber_google_roster_rem_deny(JabberStream *js, const char *who)
{
GSList *buddies;
JabberIq *iq;
- xmlnode *query;
- xmlnode *item;
- xmlnode *group;
+ PurpleXmlNode *query;
+ PurpleXmlNode *item;
+ PurpleXmlNode *group;
PurpleBuddy *b;
const char *balias;
- buddies = purple_find_buddies(purple_connection_get_account(js->gc), who);
+ buddies = purple_blist_find_buddies(purple_connection_get_account(js->gc), who);
if(!buddies)
return;
- b = buddies->data;
-
iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:roster");
- query = xmlnode_get_child(iq->node, "query");
- item = xmlnode_new_child(query, "item");
+ query = purple_xmlnode_get_child(iq->node, "query");
+ item = purple_xmlnode_new_child(query, "item");
- while(buddies) {
+ do {
PurpleGroup *g;
b = buddies->data;
g = purple_buddy_get_group(b);
- group = xmlnode_new_child(item, "group");
- xmlnode_insert_data(group,
+ group = purple_xmlnode_new_child(item, "group");
+ purple_xmlnode_insert_data(group,
jabber_roster_group_get_global_name(g), -1);
- buddies = buddies->next;
- }
+ buddies = g_slist_delete_link(buddies, buddies);
+ } while (buddies);
- balias = purple_buddy_get_local_buddy_alias(b);
- xmlnode_set_attrib(item, "jid", who);
- xmlnode_set_attrib(item, "name", balias ? balias : "");
- xmlnode_set_attrib(query, "xmlns:gr", NS_GOOGLE_ROSTER);
- xmlnode_set_attrib(query, "gr:ext", "2");
+ balias = purple_buddy_get_local_alias(b);
+ purple_xmlnode_set_attrib(item, "jid", who);
+ purple_xmlnode_set_attrib(item, "name", balias ? balias : "");
+ purple_xmlnode_set_attrib(query, "xmlns:gr", NS_GOOGLE_ROSTER);
+ purple_xmlnode_set_attrib(query, "gr:ext", "2");
jabber_iq_send(iq);
diff --git a/libpurple/protocols/jabber/google/google_roster.h b/libpurple/protocols/jabber/google/google_roster.h
index 34f6fa5d8c..4ba0265447 100644
--- a/libpurple/protocols/jabber/google/google_roster.h
+++ b/libpurple/protocols/jabber/google/google_roster.h
@@ -23,12 +23,12 @@
#include "jabber.h"
-void jabber_google_roster_outgoing(JabberStream *js, xmlnode *query, xmlnode *item);
+void jabber_google_roster_outgoing(JabberStream *js, PurpleXmlNode *query, PurpleXmlNode *item);
/* Returns FALSE if this should short-circuit processing of this roster item, or TRUE
* if this roster item should continue to be processed
*/
-gboolean jabber_google_roster_incoming(JabberStream *js, xmlnode *item);
+gboolean jabber_google_roster_incoming(JabberStream *js, PurpleXmlNode *item);
void jabber_google_roster_add_deny(JabberStream *js, const char *who);
void jabber_google_roster_rem_deny(JabberStream *js, const char *who);
diff --git a/libpurple/protocols/jabber/google/google_session.c b/libpurple/protocols/jabber/google/google_session.c
index 05665aa9b7..f4f2a91f46 100644
--- a/libpurple/protocols/jabber/google/google_session.c
+++ b/libpurple/protocols/jabber/google/google_session.c
@@ -61,20 +61,20 @@ google_session_destroy(GoogleSession *session)
purple_media_candidate_list_free(session_data->remote_video_candidates);
if (session->description)
- xmlnode_free(session->description);
+ purple_xmlnode_free(session->description);
g_free(session->session_data);
g_free(session);
}
-static xmlnode *
+static PurpleXmlNode *
google_session_create_xmlnode(GoogleSession *session, const char *type)
{
- xmlnode *node = xmlnode_new("session");
- xmlnode_set_namespace(node, NS_GOOGLE_SESSION);
- xmlnode_set_attrib(node, "id", session->id.id);
- xmlnode_set_attrib(node, "initiator", session->id.initiator);
- xmlnode_set_attrib(node, "type", type);
+ PurpleXmlNode *node = purple_xmlnode_new("session");
+ purple_xmlnode_set_namespace(node, NS_GOOGLE_SESSION);
+ purple_xmlnode_set_attrib(node, "id", session->id.id);
+ purple_xmlnode_set_attrib(node, "initiator", session->id.initiator);
+ purple_xmlnode_set_attrib(node, "type", type);
return node;
}
@@ -99,8 +99,8 @@ google_session_send_candidates(PurpleMedia *media, gchar *session_id,
gchar *ip, *port, *username, *password;
gchar pref[16];
PurpleMediaCandidateType type;
- xmlnode *sess;
- xmlnode *candidate;
+ PurpleXmlNode *sess;
+ PurpleXmlNode *candidate;
guint component_id;
transport = PURPLE_MEDIA_CANDIDATE(iter->data);
component_id = purple_media_candidate_get_component_id(
@@ -108,10 +108,10 @@ google_session_send_candidates(PurpleMedia *media, gchar *session_id,
iq = jabber_iq_new(session->js, JABBER_IQ_SET);
sess = google_session_create_xmlnode(session, "candidates");
- xmlnode_insert_child(iq->node, sess);
- xmlnode_set_attrib(iq->node, "to", session->remote_jid);
+ purple_xmlnode_insert_child(iq->node, sess);
+ purple_xmlnode_set_attrib(iq->node, "to", session->remote_jid);
- candidate = xmlnode_new("candidate");
+ candidate = purple_xmlnode_new("candidate");
ip = purple_media_candidate_get_ip(transport);
port = g_strdup_printf("%d",
@@ -122,36 +122,36 @@ google_session_send_candidates(PurpleMedia *media, gchar *session_id,
password = purple_media_candidate_get_password(transport);
type = purple_media_candidate_get_candidate_type(transport);
- xmlnode_set_attrib(candidate, "address", ip);
- xmlnode_set_attrib(candidate, "port", port);
- xmlnode_set_attrib(candidate, "name",
+ purple_xmlnode_set_attrib(candidate, "address", ip);
+ purple_xmlnode_set_attrib(candidate, "port", port);
+ purple_xmlnode_set_attrib(candidate, "name",
component_id == PURPLE_MEDIA_COMPONENT_RTP ?
video ? "video_rtp" : "rtp" :
component_id == PURPLE_MEDIA_COMPONENT_RTCP ?
video ? "video_rtcp" : "rtcp" : "none");
- xmlnode_set_attrib(candidate, "username", username);
+ purple_xmlnode_set_attrib(candidate, "username", username);
/*
* As of this writing, Farsight 2 in Google compatibility
* mode doesn't provide a password. The Gmail client
* requires this to be set.
*/
- xmlnode_set_attrib(candidate, "password",
+ purple_xmlnode_set_attrib(candidate, "password",
password != NULL ? password : "");
- xmlnode_set_attrib(candidate, "preference", pref);
- xmlnode_set_attrib(candidate, "protocol",
+ purple_xmlnode_set_attrib(candidate, "preference", pref);
+ purple_xmlnode_set_attrib(candidate, "protocol",
purple_media_candidate_get_protocol(transport)
== PURPLE_MEDIA_NETWORK_PROTOCOL_UDP ?
"udp" : "tcp");
- xmlnode_set_attrib(candidate, "type", type ==
+ purple_xmlnode_set_attrib(candidate, "type", type ==
PURPLE_MEDIA_CANDIDATE_TYPE_HOST ? "local" :
type ==
PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX ? "stun" :
type ==
PURPLE_MEDIA_CANDIDATE_TYPE_RELAY ? "relay" :
NULL);
- xmlnode_set_attrib(candidate, "generation", "0");
- xmlnode_set_attrib(candidate, "network", "0");
- xmlnode_insert_child(sess, candidate);
+ purple_xmlnode_set_attrib(candidate, "generation", "0");
+ purple_xmlnode_set_attrib(candidate, "network", "0");
+ purple_xmlnode_insert_child(sess, candidate);
g_free(ip);
g_free(port);
@@ -177,7 +177,7 @@ google_session_ready(GoogleSession *session)
session->js->user->domain,
session->js->user->resource);
JabberIq *iq;
- xmlnode *sess, *desc, *payload;
+ PurpleXmlNode *sess, *desc, *payload;
GList *codecs, *iter;
gboolean is_initiator = !strcmp(session->id.initiator, me);
@@ -190,8 +190,8 @@ google_session_ready(GoogleSession *session)
iq = jabber_iq_new(session->js, JABBER_IQ_SET);
if (is_initiator) {
- xmlnode_set_attrib(iq->node, "to", session->remote_jid);
- xmlnode_set_attrib(iq->node, "from", session->id.initiator);
+ purple_xmlnode_set_attrib(iq->node, "to", session->remote_jid);
+ purple_xmlnode_set_attrib(iq->node, "from", session->id.initiator);
sess = google_session_create_xmlnode(session, "initiate");
} else {
google_session_send_candidates(media,
@@ -200,16 +200,16 @@ google_session_ready(GoogleSession *session)
google_session_send_candidates(media,
"google-video", session->remote_jid,
session);
- xmlnode_set_attrib(iq->node, "to", session->remote_jid);
- xmlnode_set_attrib(iq->node, "from", me);
+ purple_xmlnode_set_attrib(iq->node, "to", session->remote_jid);
+ purple_xmlnode_set_attrib(iq->node, "from", me);
sess = google_session_create_xmlnode(session, "accept");
}
- xmlnode_insert_child(iq->node, sess);
- desc = xmlnode_new_child(sess, "description");
+ purple_xmlnode_insert_child(iq->node, sess);
+ desc = purple_xmlnode_new_child(sess, "description");
if (video)
- xmlnode_set_namespace(desc, NS_GOOGLE_SESSION_VIDEO);
+ purple_xmlnode_set_namespace(desc, NS_GOOGLE_SESSION_VIDEO);
else
- xmlnode_set_namespace(desc, NS_GOOGLE_SESSION_PHONE);
+ purple_xmlnode_set_namespace(desc, NS_GOOGLE_SESSION_PHONE);
codecs = purple_media_get_codecs(media, "google-video");
@@ -219,12 +219,12 @@ google_session_ready(GoogleSession *session)
purple_media_codec_get_id(codec));
gchar *encoding_name =
purple_media_codec_get_encoding_name(codec);
- payload = xmlnode_new_child(desc, "payload-type");
- xmlnode_set_attrib(payload, "id", id);
- xmlnode_set_attrib(payload, "name", encoding_name);
- xmlnode_set_attrib(payload, "width", "320");
- xmlnode_set_attrib(payload, "height", "200");
- xmlnode_set_attrib(payload, "framerate", "30");
+ payload = purple_xmlnode_new_child(desc, "payload-type");
+ purple_xmlnode_set_attrib(payload, "id", id);
+ purple_xmlnode_set_attrib(payload, "name", encoding_name);
+ purple_xmlnode_set_attrib(payload, "width", "320");
+ purple_xmlnode_set_attrib(payload, "height", "200");
+ purple_xmlnode_set_attrib(payload, "framerate", "30");
g_free(encoding_name);
g_free(id);
}
@@ -240,19 +240,19 @@ google_session_ready(GoogleSession *session)
purple_media_codec_get_encoding_name(codec);
gchar *clock_rate = g_strdup_printf("%d",
purple_media_codec_get_clock_rate(codec));
- payload = xmlnode_new_child(desc, "payload-type");
+ payload = purple_xmlnode_new_child(desc, "payload-type");
if (video)
- xmlnode_set_namespace(payload, NS_GOOGLE_SESSION_PHONE);
- xmlnode_set_attrib(payload, "id", id);
+ purple_xmlnode_set_namespace(payload, NS_GOOGLE_SESSION_PHONE);
+ purple_xmlnode_set_attrib(payload, "id", id);
/*
* Hack to make Gmail accept speex as the codec.
* It shouldn't have to be case sensitive.
*/
if (purple_strequal(encoding_name, "SPEEX"))
- xmlnode_set_attrib(payload, "name", "speex");
+ purple_xmlnode_set_attrib(payload, "name", "speex");
else
- xmlnode_set_attrib(payload, "name", encoding_name);
- xmlnode_set_attrib(payload, "clockrate", clock_rate);
+ purple_xmlnode_set_attrib(payload, "name", encoding_name);
+ purple_xmlnode_set_attrib(payload, "clockrate", clock_rate);
g_free(clock_rate);
g_free(encoding_name);
g_free(id);
@@ -295,21 +295,21 @@ google_session_stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type,
return;
if (type == PURPLE_MEDIA_INFO_HANGUP) {
- xmlnode *sess;
+ PurpleXmlNode *sess;
JabberIq *iq = jabber_iq_new(session->js, JABBER_IQ_SET);
- xmlnode_set_attrib(iq->node, "to", session->remote_jid);
+ purple_xmlnode_set_attrib(iq->node, "to", session->remote_jid);
sess = google_session_create_xmlnode(session, "terminate");
- xmlnode_insert_child(iq->node, sess);
+ purple_xmlnode_insert_child(iq->node, sess);
jabber_iq_send(iq);
} else if (type == PURPLE_MEDIA_INFO_REJECT) {
- xmlnode *sess;
+ PurpleXmlNode *sess;
JabberIq *iq = jabber_iq_new(session->js, JABBER_IQ_SET);
- xmlnode_set_attrib(iq->node, "to", session->remote_jid);
+ purple_xmlnode_set_attrib(iq->node, "to", session->remote_jid);
sess = google_session_create_xmlnode(session, "reject");
- xmlnode_insert_child(iq->node, sess);
+ purple_xmlnode_insert_child(iq->node, sess);
jabber_iq_send(iq);
} else if (type == PURPLE_MEDIA_INFO_ACCEPT && local == TRUE) {
@@ -454,7 +454,7 @@ jabber_google_relay_response_session_handle_initiate_cb(GoogleSession *session,
GParameter *params;
guint num_params;
JabberStream *js = session->js;
- xmlnode *codec_element;
+ PurpleXmlNode *codec_element;
const gchar *xmlns;
PurpleMediaCodec *codec;
GList *video_codecs = NULL;
@@ -499,7 +499,7 @@ jabber_google_relay_response_session_handle_initiate_cb(GoogleSession *session,
g_free(params);
- for (codec_element = xmlnode_get_child(session->description, "payload-type");
+ for (codec_element = purple_xmlnode_get_child(session->description, "payload-type");
codec_element; codec_element = codec_element->next) {
const char *id, *encoding_name, *clock_rate;
gboolean video;
@@ -507,19 +507,19 @@ jabber_google_relay_response_session_handle_initiate_cb(GoogleSession *session,
strcmp(codec_element->name, "payload-type"))
continue;
- xmlns = xmlnode_get_namespace(codec_element);
- encoding_name = xmlnode_get_attrib(codec_element, "name");
- id = xmlnode_get_attrib(codec_element, "id");
+ xmlns = purple_xmlnode_get_namespace(codec_element);
+ encoding_name = purple_xmlnode_get_attrib(codec_element, "name");
+ id = purple_xmlnode_get_attrib(codec_element, "id");
if (!session_data->video ||
(xmlns && !strcmp(xmlns, NS_GOOGLE_SESSION_PHONE))) {
- clock_rate = xmlnode_get_attrib(
+ clock_rate = purple_xmlnode_get_attrib(
codec_element, "clockrate");
video = FALSE;
} else {
- /*width = xmlnode_get_attrib(codec_element, "width");
- height = xmlnode_get_attrib(codec_element, "height");
- framerate = xmlnode_get_attrib(
+ /*width = purple_xmlnode_get_attrib(codec_element, "width");
+ height = purple_xmlnode_get_attrib(codec_element, "height");
+ framerate = purple_xmlnode_get_attrib(
codec_element, "framerate");*/
clock_rate = "90000";
video = TRUE;
@@ -550,12 +550,12 @@ jabber_google_relay_response_session_handle_initiate_cb(GoogleSession *session,
result = jabber_iq_new(js, JABBER_IQ_RESULT);
jabber_iq_set_id(result, session->iq_id);
- xmlnode_set_attrib(result->node, "to", session->remote_jid);
+ purple_xmlnode_set_attrib(result->node, "to", session->remote_jid);
jabber_iq_send(result);
}
static gboolean
-google_session_handle_initiate(JabberStream *js, GoogleSession *session, xmlnode *sess, const char *iq_id)
+google_session_handle_initiate(JabberStream *js, GoogleSession *session, PurpleXmlNode *sess, const char *iq_id)
{
const gchar *xmlns;
GoogleAVSessionData *session_data =
@@ -566,8 +566,8 @@ google_session_handle_initiate(JabberStream *js, GoogleSession *session, xmlnode
return FALSE;
}
- session->description = xmlnode_copy(xmlnode_get_child(sess, "description"));
- xmlns = xmlnode_get_namespace(session->description);
+ session->description = purple_xmlnode_copy(purple_xmlnode_get_child(sess, "description"));
+ xmlns = purple_xmlnode_get_namespace(session->description);
if (purple_strequal(xmlns, NS_GOOGLE_SESSION_PHONE))
session_data->video = FALSE;
@@ -611,25 +611,25 @@ google_session_handle_initiate(JabberStream *js, GoogleSession *session, xmlnode
static void
-google_session_handle_candidates(JabberStream *js, GoogleSession *session, xmlnode *sess, const char *iq_id)
+google_session_handle_candidates(JabberStream *js, GoogleSession *session, PurpleXmlNode *sess, const char *iq_id)
{
JabberIq *result;
GList *list = NULL, *video_list = NULL;
- xmlnode *cand;
+ PurpleXmlNode *cand;
static int name = 0;
char n[4];
GoogleAVSessionData *session_data =
(GoogleAVSessionData *) session->session_data;
- for (cand = xmlnode_get_child(sess, "candidate"); cand;
- cand = xmlnode_get_next_twin(cand)) {
+ for (cand = purple_xmlnode_get_child(sess, "candidate"); cand;
+ cand = purple_xmlnode_get_next_twin(cand)) {
PurpleMediaCandidate *info;
- const gchar *cname = xmlnode_get_attrib(cand, "name");
- const gchar *type = xmlnode_get_attrib(cand, "type");
- const gchar *protocol = xmlnode_get_attrib(cand, "protocol");
- const gchar *address = xmlnode_get_attrib(cand, "address");
- const gchar *port = xmlnode_get_attrib(cand, "port");
- const gchar *preference = xmlnode_get_attrib(cand, "preference");
+ const gchar *cname = purple_xmlnode_get_attrib(cand, "name");
+ const gchar *type = purple_xmlnode_get_attrib(cand, "type");
+ const gchar *protocol = purple_xmlnode_get_attrib(cand, "protocol");
+ const gchar *address = purple_xmlnode_get_attrib(cand, "address");
+ const gchar *port = purple_xmlnode_get_attrib(cand, "port");
+ const gchar *preference = purple_xmlnode_get_attrib(cand, "preference");
guint component_id;
if (cname && type && address && port) {
@@ -660,8 +660,8 @@ google_session_handle_candidates(JabberStream *js, GoogleSession *session, xmln
PURPLE_MEDIA_NETWORK_PROTOCOL_TCP,
address,
atoi(port));
- g_object_set(info, "username", xmlnode_get_attrib(cand, "username"),
- "password", xmlnode_get_attrib(cand, "password"),
+ g_object_set(info, "username", purple_xmlnode_get_attrib(cand, "username"),
+ "password", purple_xmlnode_get_attrib(cand, "password"),
"priority", prio, NULL);
if (!strncmp(cname, "video_", 6)) {
if (session_data->added_streams) {
@@ -696,19 +696,19 @@ google_session_handle_candidates(JabberStream *js, GoogleSession *session, xmln
result = jabber_iq_new(js, JABBER_IQ_RESULT);
jabber_iq_set_id(result, iq_id);
- xmlnode_set_attrib(result->node, "to", session->remote_jid);
+ purple_xmlnode_set_attrib(result->node, "to", session->remote_jid);
jabber_iq_send(result);
}
static void
-google_session_handle_accept(JabberStream *js, GoogleSession *session, xmlnode *sess, const char *iq_id)
+google_session_handle_accept(JabberStream *js, GoogleSession *session, PurpleXmlNode *sess, const char *iq_id)
{
- xmlnode *desc_element = xmlnode_get_child(sess, "description");
- xmlnode *codec_element = xmlnode_get_child(
+ PurpleXmlNode *desc_element = purple_xmlnode_get_child(sess, "description");
+ PurpleXmlNode *codec_element = purple_xmlnode_get_child(
desc_element, "payload-type");
GList *codecs = NULL, *video_codecs = NULL;
JabberIq *result = NULL;
- const gchar *xmlns = xmlnode_get_namespace(desc_element);
+ const gchar *xmlns = purple_xmlnode_get_namespace(desc_element);
gboolean video = (xmlns && !strcmp(xmlns, NS_GOOGLE_SESSION_VIDEO));
GoogleAVSessionData *session_data =
(GoogleAVSessionData *) session->session_data;
@@ -721,18 +721,18 @@ google_session_handle_accept(JabberStream *js, GoogleSession *session, xmlnode *
if (!purple_strequal(codec_element->name, "payload-type"))
continue;
- xmlns = xmlnode_get_namespace(codec_element);
- encoding_name = xmlnode_get_attrib(codec_element, "name");
- id = xmlnode_get_attrib(codec_element, "id");
+ xmlns = purple_xmlnode_get_namespace(codec_element);
+ encoding_name = purple_xmlnode_get_attrib(codec_element, "name");
+ id = purple_xmlnode_get_attrib(codec_element, "id");
if (!video || purple_strequal(xmlns, NS_GOOGLE_SESSION_PHONE))
- clock_rate = xmlnode_get_attrib(
+ clock_rate = purple_xmlnode_get_attrib(
codec_element, "clockrate");
else {
clock_rate = "90000";
- /*width = xmlnode_get_attrib(codec_element, "width");
- height = xmlnode_get_attrib(codec_element, "height");
- framerate = xmlnode_get_attrib(
+ /*width = purple_xmlnode_get_attrib(codec_element, "width");
+ height = purple_xmlnode_get_attrib(codec_element, "height");
+ framerate = purple_xmlnode_get_attrib(
codec_element, "framerate");*/
video_codec = TRUE;
}
@@ -763,12 +763,12 @@ google_session_handle_accept(JabberStream *js, GoogleSession *session, xmlnode *
result = jabber_iq_new(js, JABBER_IQ_RESULT);
jabber_iq_set_id(result, iq_id);
- xmlnode_set_attrib(result->node, "to", session->remote_jid);
+ purple_xmlnode_set_attrib(result->node, "to", session->remote_jid);
jabber_iq_send(result);
}
static void
-google_session_handle_reject(JabberStream *js, GoogleSession *session, xmlnode *sess)
+google_session_handle_reject(JabberStream *js, GoogleSession *session, PurpleXmlNode *sess)
{
GoogleAVSessionData *session_data =
(GoogleAVSessionData *) session->session_data;
@@ -776,7 +776,7 @@ google_session_handle_reject(JabberStream *js, GoogleSession *session, xmlnode *
}
static void
-google_session_handle_terminate(JabberStream *js, GoogleSession *session, xmlnode *sess)
+google_session_handle_terminate(JabberStream *js, GoogleSession *session, PurpleXmlNode *sess)
{
GoogleAVSessionData *session_data =
(GoogleAVSessionData *) session->session_data;
@@ -784,9 +784,9 @@ google_session_handle_terminate(JabberStream *js, GoogleSession *session, xmlnod
}
static void
-google_session_parse_iq(JabberStream *js, GoogleSession *session, xmlnode *sess, const char *iq_id)
+google_session_parse_iq(JabberStream *js, GoogleSession *session, PurpleXmlNode *sess, const char *iq_id)
{
- const char *type = xmlnode_get_attrib(sess, "type");
+ const char *type = purple_xmlnode_get_attrib(sess, "type");
if (!strcmp(type, "initiate")) {
google_session_handle_initiate(js, session, sess, iq_id);
@@ -804,23 +804,23 @@ google_session_parse_iq(JabberStream *js, GoogleSession *session, xmlnode *sess,
void
jabber_google_session_parse(JabberStream *js, const char *from,
JabberIqType type, const char *iq_id,
- xmlnode *session_node)
+ PurpleXmlNode *session_node)
{
GoogleSession *session = NULL;
GoogleSessionId id;
- xmlnode *desc_node;
+ PurpleXmlNode *desc_node;
GList *iter = NULL;
if (type != JABBER_IQ_SET)
return;
- id.id = (gchar*)xmlnode_get_attrib(session_node, "id");
+ id.id = (gchar*)purple_xmlnode_get_attrib(session_node, "id");
if (!id.id)
return;
- id.initiator = (gchar*)xmlnode_get_attrib(session_node, "initiator");
+ id.initiator = (gchar*)purple_xmlnode_get_attrib(session_node, "initiator");
if (!id.initiator)
return;
@@ -845,9 +845,9 @@ jabber_google_session_parse(JabberStream *js, const char *from,
}
/* If the session doesn't exist, this has to be an initiate message */
- if (strcmp(xmlnode_get_attrib(session_node, "type"), "initiate"))
+ if (strcmp(purple_xmlnode_get_attrib(session_node, "type"), "initiate"))
return;
- desc_node = xmlnode_get_child(session_node, "description");
+ desc_node = purple_xmlnode_get_child(session_node, "description");
if (!desc_node)
return;
session = g_new0(GoogleSession, 1);
diff --git a/libpurple/protocols/jabber/google/google_session.h b/libpurple/protocols/jabber/google/google_session.h
index 756ddf2fd3..ad5155c32c 100644
--- a/libpurple/protocols/jabber/google/google_session.h
+++ b/libpurple/protocols/jabber/google/google_session.h
@@ -42,7 +42,7 @@ typedef struct {
JabberStream *js;
char *remote_jid;
char *iq_id;
- xmlnode *description; /* store incoming description through
+ PurpleXmlNode *description; /* store incoming description through
relay credential fetching */
gpointer session_data;
} GoogleSession;
@@ -51,6 +51,6 @@ gboolean jabber_google_session_initiate(JabberStream *js, const gchar *who,
PurpleMediaSessionType type);
void jabber_google_session_parse(JabberStream *js, const char *from,
- JabberIqType type, const char *iq, xmlnode *session);
+ JabberIqType type, const char *iq, PurpleXmlNode *session);
#endif /* PURPLE_JABBER_GOOGLE_SESSION_H_ */ \ No newline at end of file
diff --git a/libpurple/protocols/jabber/google/jingleinfo.c b/libpurple/protocols/jabber/google/jingleinfo.c
index b49642a40e..5ed9bf56f3 100644
--- a/libpurple/protocols/jabber/google/jingleinfo.c
+++ b/libpurple/protocols/jabber/google/jingleinfo.c
@@ -37,18 +37,20 @@ jabber_google_stun_lookup_cb(GSList *hosts, gpointer data,
}
if (hosts && g_slist_next(hosts)) {
- struct sockaddr *addr = g_slist_next(hosts)->data;
+ common_sockaddr_t addr;
char dst[INET6_ADDRSTRLEN];
int port;
- if (addr->sa_family == AF_INET6) {
- inet_ntop(addr->sa_family, &((struct sockaddr_in6 *) addr)->sin6_addr,
+ memcpy(&addr, g_slist_next(hosts)->data, sizeof(addr));
+
+ if (addr.sa.sa_family == AF_INET6) {
+ inet_ntop(addr.sa.sa_family, &addr.in6.sin6_addr,
dst, sizeof(dst));
- port = ntohs(((struct sockaddr_in6 *) addr)->sin6_port);
+ port = ntohs(addr.in6.sin6_port);
} else {
- inet_ntop(addr->sa_family, &((struct sockaddr_in *) addr)->sin_addr,
+ inet_ntop(addr.sa.sa_family, &addr.in.sin_addr,
dst, sizeof(dst));
- port = ntohs(((struct sockaddr_in *) addr)->sin_port);
+ port = ntohs(addr.in.sin_port);
}
if (js->stun_ip)
@@ -73,15 +75,15 @@ jabber_google_stun_lookup_cb(GSList *hosts, gpointer data,
static void
jabber_google_jingle_info_common(JabberStream *js, const char *from,
- JabberIqType type, xmlnode *query)
+ JabberIqType type, PurpleXmlNode *query)
{
- const xmlnode *stun = xmlnode_get_child(query, "stun");
- const xmlnode *relay = xmlnode_get_child(query, "relay");
+ const PurpleXmlNode *stun = purple_xmlnode_get_child(query, "stun");
+ const PurpleXmlNode *relay = purple_xmlnode_get_child(query, "relay");
gchar *my_bare_jid;
/*
* Make sure that random people aren't sending us STUN servers. Per
- * http://code.google.com/apis/talk/jep_extensions/jingleinfo.html, these
+ * https://developers.google.com/talk/jep_extensions/jingleinfo, these
* stanzas are stamped from our bare JID.
*/
if (from) {
@@ -102,11 +104,11 @@ jabber_google_jingle_info_common(JabberStream *js, const char *from,
purple_debug_info("jabber", "got google:jingleinfo\n");
if (stun) {
- xmlnode *server = xmlnode_get_child(stun, "server");
+ PurpleXmlNode *server = purple_xmlnode_get_child(stun, "server");
if (server) {
- const gchar *host = xmlnode_get_attrib(server, "host");
- const gchar *udp = xmlnode_get_attrib(server, "udp");
+ const gchar *host = purple_xmlnode_get_attrib(server, "host");
+ const gchar *udp = purple_xmlnode_get_attrib(server, "udp");
if (host && udp) {
PurpleAccount *account;
@@ -117,26 +119,26 @@ jabber_google_jingle_info_common(JabberStream *js, const char *from,
purple_dnsquery_destroy(js->stun_query);
account = purple_connection_get_account(js->gc);
- js->stun_query = purple_dnsquery_a_account(account, host, port,
+ js->stun_query = purple_dnsquery_a(account, host, port,
jabber_google_stun_lookup_cb, js);
}
}
}
if (relay) {
- xmlnode *token = xmlnode_get_child(relay, "token");
- xmlnode *server = xmlnode_get_child(relay, "server");
+ PurpleXmlNode *token = purple_xmlnode_get_child(relay, "token");
+ PurpleXmlNode *server = purple_xmlnode_get_child(relay, "server");
if (token) {
- gchar *relay_token = xmlnode_get_data(token);
+ gchar *relay_token = purple_xmlnode_get_data(token);
- /* we let js own the string returned from xmlnode_get_data */
+ /* we let js own the string returned from purple_xmlnode_get_data */
js->google_relay_token = relay_token;
}
if (server) {
js->google_relay_host =
- g_strdup(xmlnode_get_attrib(server, "host"));
+ g_strdup(purple_xmlnode_get_attrib(server, "host"));
}
}
}
@@ -144,9 +146,9 @@ jabber_google_jingle_info_common(JabberStream *js, const char *from,
static void
jabber_google_jingle_info_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
- xmlnode *query = xmlnode_get_child_with_namespace(packet, "query",
+ PurpleXmlNode *query = purple_xmlnode_get_child_with_namespace(packet, "query",
NS_GOOGLE_JINGLE_INFO);
if (query)
@@ -158,7 +160,7 @@ jabber_google_jingle_info_cb(JabberStream *js, const char *from,
void
jabber_google_handle_jingle_info(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *child)
+ PurpleXmlNode *child)
{
jabber_google_jingle_info_common(js, from, type, child);
}
diff --git a/libpurple/protocols/jabber/google/jingleinfo.h b/libpurple/protocols/jabber/google/jingleinfo.h
index e5f4da232b..929ab4b18d 100644
--- a/libpurple/protocols/jabber/google/jingleinfo.h
+++ b/libpurple/protocols/jabber/google/jingleinfo.h
@@ -25,7 +25,7 @@
void jabber_google_handle_jingle_info(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *child);
+ PurpleXmlNode *child);
void jabber_google_send_jingle_info(JabberStream *js);
diff --git a/libpurple/protocols/jabber/google/relay.c b/libpurple/protocols/jabber/google/relay.c
index 4b48a8034e..50cfd31d67 100644
--- a/libpurple/protocols/jabber/google/relay.c
+++ b/libpurple/protocols/jabber/google/relay.c
@@ -20,6 +20,7 @@
#include "internal.h"
#include "debug.h"
+#include "http.h"
#include "relay.h"
@@ -61,29 +62,12 @@ jabber_google_relay_parse_response(const gchar *response, gchar **ip,
}
static void
-jabber_google_relay_remove_url_data(JabberStream *js,
- PurpleUtilFetchUrlData *url_data)
-{
- GList *iter = js->google_relay_requests;
-
- while (iter) {
- if (iter->data == url_data) {
- js->google_relay_requests =
- g_list_delete_link(js->google_relay_requests, iter);
- break;
- }
- }
-}
-
-static void
-jabber_google_relay_fetch_cb(PurpleUtilFetchUrlData *url_data,
- gpointer user_data, const gchar *url_text, gsize len,
- const gchar *error_message)
+jabber_google_relay_fetch_cb(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer user_data)
{
JabberGoogleRelayCallbackData *data =
(JabberGoogleRelayCallbackData *) user_data;
GoogleSession *session = data->session;
- JabberStream *js = session->js;
JabberGoogleRelayCallback *cb = data->cb;
gchar *relay_ip = NULL;
guint relay_udp = 0;
@@ -94,16 +78,14 @@ jabber_google_relay_fetch_cb(PurpleUtilFetchUrlData *url_data,
g_free(data);
- if (url_data) {
- jabber_google_relay_remove_url_data(js, url_data);
- }
-
purple_debug_info("jabber", "got response on HTTP request to relay server\n");
- if (url_text && len > 0) {
+ if (purple_http_response_is_successful(response)) {
+ const gchar *got_data =
+ purple_http_response_get_data(response, NULL);
purple_debug_info("jabber", "got Google relay request response:\n%s\n",
- url_text);
- jabber_google_relay_parse_response(url_text, &relay_ip, &relay_udp,
+ got_data);
+ jabber_google_relay_parse_response(got_data, &relay_ip, &relay_udp,
&relay_tcp, &relay_ssltcp, &relay_username, &relay_password);
}
@@ -120,32 +102,20 @@ void
jabber_google_do_relay_request(JabberStream *js, GoogleSession *session,
JabberGoogleRelayCallback cb)
{
- PurpleUtilFetchUrlData *url_data = NULL;
- gchar *url = g_strdup_printf("http://%s", js->google_relay_host);
- /* yes, the relay token is included twice as different request headers,
- this is apparently needed to make Google's relay servers work... */
- gchar *request =
- g_strdup_printf("GET /create_session HTTP/1.0\r\n"
- "Host: %s\r\n"
- "X-Talk-Google-Relay-Auth: %s\r\n"
- "X-Google-Relay-Auth: %s\r\n\r\n",
- js->google_relay_host, js->google_relay_token, js->google_relay_token);
+ PurpleHttpRequest *req;
JabberGoogleRelayCallbackData *data = g_new0(JabberGoogleRelayCallbackData, 1);
data->session = session;
data->cb = cb;
- purple_debug_info("jabber",
- "sending Google relay request %s to %s\n", request, url);
- url_data =
- purple_util_fetch_url_request(url, FALSE, NULL, FALSE, request, FALSE,
- jabber_google_relay_fetch_cb, data);
- if (url_data) {
- js->google_relay_requests =
- g_list_prepend(js->google_relay_requests, url_data);
- } else {
- purple_debug_error("jabber", "unable to create Google relay request\n");
- jabber_google_relay_fetch_cb(NULL, data, NULL, 0, NULL);
- }
- g_free(url);
- g_free(request);
-} \ No newline at end of file
+ purple_debug_info("jabber", "sending Google relay request\n");
+
+ req = purple_http_request_new(NULL);
+ purple_http_request_set_url_printf(req, "http://%s/create_session",
+ js->google_relay_host);
+ /* yes, the relay token is included twice as different request headers,
+ this is apparently needed to make Google's relay servers work... */
+ purple_http_request_header_set(req, "X-Talk-Google-Relay-Auth", js->google_relay_token);
+ purple_http_request_header_set(req, "X-Google-Relay-Auth", js->google_relay_token);
+ purple_http_request(js->gc, req, jabber_google_relay_fetch_cb, data);
+ purple_http_request_unref(req);
+}
diff --git a/libpurple/protocols/jabber/ibb.c b/libpurple/protocols/jabber/ibb.c
index bfece07087..8131b296ef 100644
--- a/libpurple/protocols/jabber/ibb.c
+++ b/libpurple/protocols/jabber/ibb.c
@@ -1,5 +1,5 @@
/*
- * purple - Jabber Service Discovery
+ * purple - Handling of XEP-0047: In-Band Bytestreams.
*
* 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
@@ -54,11 +54,11 @@ jabber_ibb_session_create(JabberStream *js, const gchar *sid, const gchar *who,
JabberIBBSession *
jabber_ibb_session_create_from_xmlnode(JabberStream *js, const char *from,
- const char *id, xmlnode *open, gpointer user_data)
+ const char *id, PurpleXmlNode *open, gpointer user_data)
{
JabberIBBSession *sess = NULL;
- const gchar *sid = xmlnode_get_attrib(open, "sid");
- const gchar *block_size = xmlnode_get_attrib(open, "block-size");
+ const gchar *sid = purple_xmlnode_get_attrib(open, "sid");
+ const gchar *block_size = purple_xmlnode_get_attrib(open, "block-size");
if (!open) {
return NULL;
@@ -210,7 +210,7 @@ jabber_ibb_session_set_error_callback(JabberIBBSession *sess,
static void
jabber_ibb_session_opened_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
JabberIBBSession *sess = (JabberIBBSession *) data;
@@ -233,16 +233,16 @@ jabber_ibb_session_open(JabberIBBSession *sess)
"jabber_ibb_session called on an already open stream\n");
} else {
JabberIq *set = jabber_iq_new(sess->js, JABBER_IQ_SET);
- xmlnode *open = xmlnode_new("open");
+ PurpleXmlNode *open = purple_xmlnode_new("open");
gchar block_size[10];
- xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess));
- xmlnode_set_namespace(open, NS_IBB);
- xmlnode_set_attrib(open, "sid", jabber_ibb_session_get_sid(sess));
+ purple_xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess));
+ purple_xmlnode_set_namespace(open, NS_IBB);
+ purple_xmlnode_set_attrib(open, "sid", jabber_ibb_session_get_sid(sess));
g_snprintf(block_size, sizeof(block_size), "%" G_GSIZE_FORMAT,
jabber_ibb_session_get_block_size(sess));
- xmlnode_set_attrib(open, "block-size", block_size);
- xmlnode_insert_child(set->node, open);
+ purple_xmlnode_set_attrib(open, "block-size", block_size);
+ purple_xmlnode_insert_child(set->node, open);
jabber_iq_set_callback(set, jabber_ibb_session_opened_cb, sess);
@@ -262,12 +262,12 @@ jabber_ibb_session_close(JabberIBBSession *sess)
} else {
JabberIq *set = jabber_iq_new(jabber_ibb_session_get_js(sess),
JABBER_IQ_SET);
- xmlnode *close = xmlnode_new("close");
+ PurpleXmlNode *close = purple_xmlnode_new("close");
- xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess));
- xmlnode_set_namespace(close, NS_IBB);
- xmlnode_set_attrib(close, "sid", jabber_ibb_session_get_sid(sess));
- xmlnode_insert_child(set->node, close);
+ purple_xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess));
+ purple_xmlnode_set_namespace(close, NS_IBB);
+ purple_xmlnode_set_attrib(close, "sid", jabber_ibb_session_get_sid(sess));
+ purple_xmlnode_insert_child(set->node, close);
jabber_iq_send(set);
sess->state = JABBER_IBB_SESSION_CLOSED;
}
@@ -279,7 +279,7 @@ jabber_ibb_session_accept(JabberIBBSession *sess)
JabberIq *result = jabber_iq_new(jabber_ibb_session_get_js(sess),
JABBER_IQ_RESULT);
- xmlnode_set_attrib(result->node, "to", jabber_ibb_session_get_who(sess));
+ purple_xmlnode_set_attrib(result->node, "to", jabber_ibb_session_get_who(sess));
jabber_iq_set_id(result, sess->id);
jabber_iq_send(result);
sess->state = JABBER_IBB_SESSION_OPENED;
@@ -288,7 +288,7 @@ jabber_ibb_session_accept(JabberIBBSession *sess)
static void
jabber_ibb_session_send_acknowledge_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
JabberIBBSession *sess = (JabberIBBSession *) data;
@@ -336,24 +336,24 @@ jabber_ibb_session_send_data(JabberIBBSession *sess, gconstpointer data,
} else {
JabberIq *set = jabber_iq_new(jabber_ibb_session_get_js(sess),
JABBER_IQ_SET);
- xmlnode *data_element = xmlnode_new("data");
+ PurpleXmlNode *data_element = purple_xmlnode_new("data");
char *base64 = purple_base64_encode(data, size);
char seq[10];
g_snprintf(seq, sizeof(seq), "%u", jabber_ibb_session_get_send_seq(sess));
- xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess));
- xmlnode_set_namespace(data_element, NS_IBB);
- xmlnode_set_attrib(data_element, "sid", jabber_ibb_session_get_sid(sess));
- xmlnode_set_attrib(data_element, "seq", seq);
- xmlnode_insert_data(data_element, base64, -1);
+ purple_xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess));
+ purple_xmlnode_set_namespace(data_element, NS_IBB);
+ purple_xmlnode_set_attrib(data_element, "sid", jabber_ibb_session_get_sid(sess));
+ purple_xmlnode_set_attrib(data_element, "seq", seq);
+ purple_xmlnode_insert_data(data_element, base64, -1);
- xmlnode_insert_child(set->node, data_element);
+ purple_xmlnode_insert_child(set->node, data_element);
purple_debug_info("jabber",
"IBB: setting send <iq/> callback for session %p %s\n", sess,
sess->sid);
jabber_iq_set_callback(set, jabber_ibb_session_send_acknowledge_cb, sess);
- sess->last_iq_id = g_strdup(xmlnode_get_attrib(set->node, "id"));
+ sess->last_iq_id = g_strdup(purple_xmlnode_get_attrib(set->node, "id"));
purple_debug_info("jabber", "IBB: set sess->last_iq_id: %s\n",
sess->last_iq_id);
jabber_iq_send(set);
@@ -367,30 +367,30 @@ static void
jabber_ibb_send_error_response(JabberStream *js, const char *to, const char *id)
{
JabberIq *result = jabber_iq_new(js, JABBER_IQ_ERROR);
- xmlnode *error = xmlnode_new("error");
- xmlnode *item_not_found = xmlnode_new("item-not-found");
+ PurpleXmlNode *error = purple_xmlnode_new("error");
+ PurpleXmlNode *item_not_found = purple_xmlnode_new("item-not-found");
- xmlnode_set_namespace(item_not_found, NS_XMPP_STANZAS);
- xmlnode_set_attrib(error, "code", "440");
- xmlnode_set_attrib(error, "type", "cancel");
+ purple_xmlnode_set_namespace(item_not_found, NS_XMPP_STANZAS);
+ purple_xmlnode_set_attrib(error, "code", "440");
+ purple_xmlnode_set_attrib(error, "type", "cancel");
jabber_iq_set_id(result, id);
- xmlnode_set_attrib(result->node, "to", to);
- xmlnode_insert_child(error, item_not_found);
- xmlnode_insert_child(result->node, error);
+ purple_xmlnode_set_attrib(result->node, "to", to);
+ purple_xmlnode_insert_child(error, item_not_found);
+ purple_xmlnode_insert_child(result->node, error);
jabber_iq_send(result);
}
void
jabber_ibb_parse(JabberStream *js, const char *who, JabberIqType type,
- const char *id, xmlnode *child)
+ const char *id, PurpleXmlNode *child)
{
const char *name = child->name;
gboolean data = g_str_equal(name, "data");
gboolean close = g_str_equal(name, "close");
gboolean open = g_str_equal(name, "open");
const gchar *sid = (data || close) ?
- xmlnode_get_attrib(child, "sid") : NULL;
+ purple_xmlnode_get_attrib(child, "sid") : NULL;
JabberIBBSession *sess =
sid ? g_hash_table_lookup(jabber_ibb_sessions, sid) : NULL;
@@ -402,7 +402,7 @@ jabber_ibb_parse(JabberStream *js, const char *who, JabberIqType type,
purple_debug_error("jabber",
"Got IBB iq from wrong JID, ignoring\n");
} else if (data) {
- const gchar *seq_attr = xmlnode_get_attrib(child, "seq");
+ const gchar *seq_attr = purple_xmlnode_get_attrib(child, "seq");
guint16 seq = (seq_attr ? atoi(seq_attr) : 0);
/* reject the data, and set the session in error if we get an
@@ -412,10 +412,10 @@ jabber_ibb_parse(JabberStream *js, const char *who, JabberIqType type,
JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT);
jabber_iq_set_id(result, id);
- xmlnode_set_attrib(result->node, "to", who);
+ purple_xmlnode_set_attrib(result->node, "to", who);
if (sess->data_received_cb) {
- gchar *base64 = xmlnode_get_data(child);
+ gchar *base64 = purple_xmlnode_get_data(child);
gsize size;
gpointer rawdata = purple_base64_decode(base64, &size);
@@ -484,7 +484,7 @@ jabber_ibb_parse(JabberStream *js, const char *who, JabberIqType type,
if (handler(js, who, id, child)) {
result = jabber_iq_new(js, JABBER_IQ_RESULT);
- xmlnode_set_attrib(result->node, "to", who);
+ purple_xmlnode_set_attrib(result->node, "to", who);
jabber_iq_set_id(result, id);
jabber_iq_send(result);
return;
diff --git a/libpurple/protocols/jabber/ibb.h b/libpurple/protocols/jabber/ibb.h
index 4b2e711f68..d6d501fe7a 100644
--- a/libpurple/protocols/jabber/ibb.h
+++ b/libpurple/protocols/jabber/ibb.h
@@ -1,5 +1,5 @@
/*
- * purple - Jabber Service Discovery
+ * purple - Handling of XEP-0047: In-Band Bytestreams.
*
* 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
@@ -38,7 +38,7 @@ typedef void (JabberIBBErrorCallback)(JabberIBBSession *);
typedef void (JabberIBBSentCallback)(JabberIBBSession *);
typedef gboolean (JabberIBBOpenHandler)(JabberStream *js, const char *from,
- const char *id, xmlnode *open);
+ const char *id, PurpleXmlNode *open);
typedef enum {
JABBER_IBB_SESSION_NOT_OPENED,
@@ -77,7 +77,7 @@ struct _JabberIBBSession {
JabberIBBSession *jabber_ibb_session_create(JabberStream *js, const gchar *sid,
const gchar *who, gpointer user_data);
JabberIBBSession *jabber_ibb_session_create_from_xmlnode(JabberStream *js,
- const gchar *from, const gchar *id, xmlnode *open, gpointer user_data);
+ const gchar *from, const gchar *id, PurpleXmlNode *open, gpointer user_data);
void jabber_ibb_session_destroy(JabberIBBSession *sess);
@@ -118,7 +118,7 @@ gpointer jabber_ibb_session_get_user_data(JabberIBBSession *sess);
/* handle incoming packet */
void jabber_ibb_parse(JabberStream *js, const char *who, JabberIqType type,
- const char *id, xmlnode *child);
+ const char *id, PurpleXmlNode *child);
/* add a handler for open session */
void jabber_ibb_register_open_handler(JabberIBBOpenHandler *cb);
diff --git a/libpurple/protocols/jabber/iq.c b/libpurple/protocols/jabber/iq.c
index 5818a49e5f..c27fc9b5e7 100644
--- a/libpurple/protocols/jabber/iq.c
+++ b/libpurple/protocols/jabber/iq.c
@@ -43,7 +43,7 @@
#include "ibb.h"
#ifdef _WIN32
-#include "utsname.h"
+#include "win32/utsname.h"
#endif
static GHashTable *iq_handlers = NULL;
@@ -69,19 +69,19 @@ JabberIq *jabber_iq_new(JabberStream *js, JabberIqType type)
iq->type = type;
- iq->node = xmlnode_new("iq");
+ iq->node = purple_xmlnode_new("iq");
switch(iq->type) {
case JABBER_IQ_SET:
- xmlnode_set_attrib(iq->node, "type", "set");
+ purple_xmlnode_set_attrib(iq->node, "type", "set");
break;
case JABBER_IQ_GET:
- xmlnode_set_attrib(iq->node, "type", "get");
+ purple_xmlnode_set_attrib(iq->node, "type", "get");
break;
case JABBER_IQ_ERROR:
- xmlnode_set_attrib(iq->node, "type", "error");
+ purple_xmlnode_set_attrib(iq->node, "type", "error");
break;
case JABBER_IQ_RESULT:
- xmlnode_set_attrib(iq->node, "type", "result");
+ purple_xmlnode_set_attrib(iq->node, "type", "result");
break;
case JABBER_IQ_NONE:
/* this shouldn't ever happen */
@@ -92,7 +92,7 @@ JabberIq *jabber_iq_new(JabberStream *js, JabberIqType type)
if(type == JABBER_IQ_GET || type == JABBER_IQ_SET) {
iq->id = jabber_get_next_id(js);
- xmlnode_set_attrib(iq->node, "id", iq->id);
+ purple_xmlnode_set_attrib(iq->node, "id", iq->id);
}
return iq;
@@ -102,10 +102,10 @@ JabberIq *jabber_iq_new_query(JabberStream *js, JabberIqType type,
const char *xmlns)
{
JabberIq *iq = jabber_iq_new(js, type);
- xmlnode *query;
+ PurpleXmlNode *query;
- query = xmlnode_new_child(iq->node, "query");
- xmlnode_set_namespace(query, xmlns);
+ query = purple_xmlnode_new_child(iq->node, "query");
+ purple_xmlnode_set_namespace(query, xmlns);
return iq;
}
@@ -122,10 +122,10 @@ void jabber_iq_set_id(JabberIq *iq, const char *id)
g_free(iq->id);
if(id) {
- xmlnode_set_attrib(iq->node, "id", id);
+ purple_xmlnode_set_attrib(iq->node, "id", id);
iq->id = g_strdup(id);
} else {
- xmlnode_remove_attrib(iq->node, "id");
+ purple_xmlnode_remove_attrib(iq->node, "id");
iq->id = NULL;
}
}
@@ -141,7 +141,7 @@ void jabber_iq_send(JabberIq *iq)
jcd = g_new0(JabberIqCallbackData, 1);
jcd->callback = iq->callback;
jcd->data = iq->callback_data;
- jcd->to = jabber_id_new(xmlnode_get_attrib(iq->node, "to"));
+ jcd->to = jabber_id_new(purple_xmlnode_get_attrib(iq->node, "to"));
g_hash_table_insert(iq->js->iq_callbacks, g_strdup(iq->id), jcd);
}
@@ -154,28 +154,28 @@ void jabber_iq_free(JabberIq *iq)
g_return_if_fail(iq != NULL);
g_free(iq->id);
- xmlnode_free(iq->node);
+ purple_xmlnode_free(iq->node);
g_free(iq);
}
static void jabber_iq_last_parse(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet)
+ PurpleXmlNode *packet)
{
JabberIq *iq;
- xmlnode *query;
+ PurpleXmlNode *query;
char *idle_time;
if(type == JABBER_IQ_GET) {
iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, NS_LAST_ACTIVITY);
jabber_iq_set_id(iq, id);
if (from)
- xmlnode_set_attrib(iq->node, "to", from);
+ purple_xmlnode_set_attrib(iq->node, "to", from);
- query = xmlnode_get_child(iq->node, "query");
+ query = purple_xmlnode_get_child(iq->node, "query");
idle_time = g_strdup_printf("%ld", js->idle ? time(NULL) - js->idle : 0);
- xmlnode_set_attrib(query, "seconds", idle_time);
+ purple_xmlnode_set_attrib(query, "seconds", idle_time);
g_free(idle_time);
jabber_iq_send(iq);
@@ -184,7 +184,7 @@ static void jabber_iq_last_parse(JabberStream *js, const char *from,
static void jabber_time_parse(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *child)
+ PurpleXmlNode *child)
{
JabberIq *iq;
time_t now_t;
@@ -193,28 +193,28 @@ static void jabber_time_parse(JabberStream *js, const char *from,
time(&now_t);
if(type == JABBER_IQ_GET) {
- xmlnode *tzo, *utc;
+ PurpleXmlNode *tzo, *utc;
const char *date, *tz;
iq = jabber_iq_new(js, JABBER_IQ_RESULT);
jabber_iq_set_id(iq, id);
if (from)
- xmlnode_set_attrib(iq->node, "to", from);
+ purple_xmlnode_set_attrib(iq->node, "to", from);
- child = xmlnode_new_child(iq->node, child->name);
- xmlnode_set_namespace(child, NS_ENTITY_TIME);
+ child = purple_xmlnode_new_child(iq->node, child->name);
+ purple_xmlnode_set_namespace(child, NS_ENTITY_TIME);
/* <tzo>-06:00</tzo> */
tm = localtime(&now_t);
tz = purple_get_tzoff_str(tm, TRUE);
- tzo = xmlnode_new_child(child, "tzo");
- xmlnode_insert_data(tzo, tz, -1);
+ tzo = purple_xmlnode_new_child(child, "tzo");
+ purple_xmlnode_insert_data(tzo, tz, -1);
/* <utc>2006-12-19T17:58:35Z</utc> */
tm = gmtime(&now_t);
date = purple_utf8_strftime("%Y-%m-%dT%H:%M:%SZ", tm);
- utc = xmlnode_new_child(child, "utc");
- xmlnode_insert_data(utc, date, -1);
+ utc = purple_xmlnode_new_child(child, "utc");
+ purple_xmlnode_insert_data(utc, date, -1);
jabber_iq_send(iq);
} else {
@@ -224,10 +224,10 @@ static void jabber_time_parse(JabberStream *js, const char *from,
static void jabber_iq_version_parse(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet)
+ PurpleXmlNode *packet)
{
JabberIq *iq;
- xmlnode *query;
+ PurpleXmlNode *query;
if(type == JABBER_IQ_GET) {
GHashTable *ui_info;
@@ -245,10 +245,10 @@ static void jabber_iq_version_parse(JabberStream *js, const char *from,
iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "jabber:iq:version");
if (from)
- xmlnode_set_attrib(iq->node, "to", from);
+ purple_xmlnode_set_attrib(iq->node, "to", from);
jabber_iq_set_id(iq, id);
- query = xmlnode_get_child(iq->node, "query");
+ query = purple_xmlnode_get_child(iq->node, "query");
ui_info = purple_core_get_ui_info();
@@ -259,17 +259,17 @@ static void jabber_iq_version_parse(JabberStream *js, const char *from,
if(NULL != ui_name && NULL != ui_version) {
char *version_complete = g_strdup_printf("%s (libpurple " VERSION ")", ui_version);
- xmlnode_insert_data(xmlnode_new_child(query, "name"), ui_name, -1);
- xmlnode_insert_data(xmlnode_new_child(query, "version"), version_complete, -1);
+ purple_xmlnode_insert_data(purple_xmlnode_new_child(query, "name"), ui_name, -1);
+ purple_xmlnode_insert_data(purple_xmlnode_new_child(query, "version"), version_complete, -1);
g_free(version_complete);
} else {
- xmlnode_insert_data(xmlnode_new_child(query, "name"), "libpurple", -1);
- xmlnode_insert_data(xmlnode_new_child(query, "version"), VERSION, -1);
+ purple_xmlnode_insert_data(purple_xmlnode_new_child(query, "name"), "libpurple", -1);
+ purple_xmlnode_insert_data(purple_xmlnode_new_child(query, "version"), VERSION, -1);
}
#if 0
if(os) {
- xmlnode_insert_data(xmlnode_new_child(query, "os"), os, -1);
+ purple_xmlnode_insert_data(purple_xmlnode_new_child(query, "os"), os, -1);
g_free(os);
}
#endif
@@ -329,19 +329,19 @@ static gboolean does_reply_from_match_request_to(JabberStream *js, JabberID *to,
return FALSE;
}
-void jabber_iq_parse(JabberStream *js, xmlnode *packet)
+void jabber_iq_parse(JabberStream *js, PurpleXmlNode *packet)
{
JabberIqCallbackData *jcd;
- xmlnode *child, *error, *x;
+ PurpleXmlNode *child, *error, *x;
const char *xmlns;
const char *iq_type, *id, *from;
JabberIqType type = JABBER_IQ_NONE;
gboolean signal_return;
JabberID *from_id;
- from = xmlnode_get_attrib(packet, "from");
- id = xmlnode_get_attrib(packet, "id");
- iq_type = xmlnode_get_attrib(packet, "type");
+ from = purple_xmlnode_get_attrib(packet, "from");
+ id = purple_xmlnode_get_attrib(packet, "id");
+ iq_type = purple_xmlnode_get_attrib(packet, "type");
/*
* Ensure the 'from' attribute is valid. No point in handling a stanza
@@ -361,7 +361,7 @@ void jabber_iq_parse(JabberStream *js, xmlnode *packet)
* being) sufficient.
*/
for (child = packet->child; child; child = child->next) {
- if (child->type == XMLNODE_TYPE_TAG)
+ if (child->type == PURPLE_XMLNODE_TYPE_TAG)
break;
}
@@ -389,21 +389,21 @@ void jabber_iq_parse(JabberStream *js, xmlnode *packet)
if(type == JABBER_IQ_SET || type == JABBER_IQ_GET) {
JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR);
- xmlnode_free(iq->node);
- iq->node = xmlnode_copy(packet);
+ purple_xmlnode_free(iq->node);
+ iq->node = purple_xmlnode_copy(packet);
if (from) {
- xmlnode_set_attrib(iq->node, "to", from);
- xmlnode_remove_attrib(iq->node, "from");
+ purple_xmlnode_set_attrib(iq->node, "to", from);
+ purple_xmlnode_remove_attrib(iq->node, "from");
}
- xmlnode_set_attrib(iq->node, "type", "error");
+ purple_xmlnode_set_attrib(iq->node, "type", "error");
/* This id is clearly not useful, but we must put something there for a valid stanza */
iq->id = jabber_get_next_id(js);
- xmlnode_set_attrib(iq->node, "id", iq->id);
- error = xmlnode_new_child(iq->node, "error");
- xmlnode_set_attrib(error, "type", "modify");
- x = xmlnode_new_child(error, "bad-request");
- xmlnode_set_namespace(x, NS_XMPP_STANZAS);
+ purple_xmlnode_set_attrib(iq->node, "id", iq->id);
+ error = purple_xmlnode_new_child(iq->node, "error");
+ purple_xmlnode_set_attrib(error, "type", "modify");
+ x = purple_xmlnode_new_child(error, "bad-request");
+ purple_xmlnode_set_namespace(x, NS_XMPP_STANZAS);
jabber_iq_send(iq);
} else
@@ -450,7 +450,7 @@ void jabber_iq_parse(JabberStream *js, xmlnode *packet)
* Apparently not, so let's see if we have a pre-defined handler
* or if an outside plugin is interested.
*/
- if(child && (xmlns = xmlnode_get_namespace(child))) {
+ if(child && (xmlns = purple_xmlnode_get_namespace(child))) {
char *key = g_strdup_printf("%s %s", child->name, xmlns);
JabberIqHandler *jih = g_hash_table_lookup(iq_handlers, key);
int signal_ref = GPOINTER_TO_INT(g_hash_table_lookup(signal_iq_handlers, key));
@@ -478,19 +478,19 @@ void jabber_iq_parse(JabberStream *js, xmlnode *packet)
if(type == JABBER_IQ_SET || type == JABBER_IQ_GET) {
JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR);
- xmlnode_free(iq->node);
- iq->node = xmlnode_copy(packet);
+ purple_xmlnode_free(iq->node);
+ iq->node = purple_xmlnode_copy(packet);
if (from) {
- xmlnode_set_attrib(iq->node, "to", from);
- xmlnode_remove_attrib(iq->node, "from");
+ purple_xmlnode_set_attrib(iq->node, "to", from);
+ purple_xmlnode_remove_attrib(iq->node, "from");
}
- xmlnode_set_attrib(iq->node, "type", "error");
- error = xmlnode_new_child(iq->node, "error");
- xmlnode_set_attrib(error, "type", "cancel");
- xmlnode_set_attrib(error, "code", "501");
- x = xmlnode_new_child(error, "feature-not-implemented");
- xmlnode_set_namespace(x, NS_XMPP_STANZAS);
+ purple_xmlnode_set_attrib(iq->node, "type", "error");
+ error = purple_xmlnode_new_child(iq->node, "error");
+ purple_xmlnode_set_attrib(error, "type", "cancel");
+ purple_xmlnode_set_attrib(error, "code", "501");
+ x = purple_xmlnode_new_child(error, "feature-not-implemented");
+ purple_xmlnode_set_namespace(x, NS_XMPP_STANZAS);
jabber_iq_send(iq);
}
diff --git a/libpurple/protocols/jabber/iq.h b/libpurple/protocols/jabber/iq.h
index 0012c7eb3a..0e68a9bd2c 100644
--- a/libpurple/protocols/jabber/iq.h
+++ b/libpurple/protocols/jabber/iq.h
@@ -59,7 +59,7 @@ typedef struct _JabberIqCallbackData JabberIqCallbackData;
*/
typedef void (JabberIqHandler)(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *child);
+ PurpleXmlNode *child);
/**
* A JabberIqCallback is called to process the results of a GET or SET that
@@ -78,12 +78,12 @@ typedef void (JabberIqHandler)(JabberStream *js, const char *from,
*/
typedef void (JabberIqCallback)(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data);
+ PurpleXmlNode *packet, gpointer data);
struct _JabberIq {
JabberIqType type;
char *id;
- xmlnode *node;
+ PurpleXmlNode *node;
JabberIqCallback *callback;
gpointer callback_data;
@@ -95,7 +95,7 @@ JabberIq *jabber_iq_new(JabberStream *js, JabberIqType type);
JabberIq *jabber_iq_new_query(JabberStream *js, JabberIqType type,
const char *xmlns);
-void jabber_iq_parse(JabberStream *js, xmlnode *packet);
+void jabber_iq_parse(JabberStream *js, PurpleXmlNode *packet);
void jabber_iq_callbackdata_free(JabberIqCallbackData *jcd);
void jabber_iq_remove_callback_by_id(JabberStream *js, const char *id);
diff --git a/libpurple/protocols/jabber/jabber.c b/libpurple/protocols/jabber/jabber.c
index 95e88bc3c4..84dc5c8748 100644
--- a/libpurple/protocols/jabber/jabber.c
+++ b/libpurple/protocols/jabber/jabber.c
@@ -24,18 +24,17 @@
#include "account.h"
#include "accountopt.h"
-#include "blist.h"
+#include "buddylist.h"
#include "core.h"
#include "cmds.h"
#include "connection.h"
#include "conversation.h"
#include "debug.h"
#include "dnssrv.h"
-#include "imgstore.h"
+#include "http.h"
#include "message.h"
#include "notify.h"
#include "pluginpref.h"
-#include "privacy.h"
#include "proxy.h"
#include "prpl.h"
#include "request.h"
@@ -112,14 +111,14 @@ static void jabber_stream_init(JabberStream *js)
static void
jabber_session_initialized_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
if (type == JABBER_IQ_RESULT) {
jabber_disco_items_server(js);
if(js->unregistration)
jabber_unregister_account_cb(js);
} else {
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
("Error initializing session"));
}
@@ -128,32 +127,32 @@ jabber_session_initialized_cb(JabberStream *js, const char *from,
static void jabber_session_init(JabberStream *js)
{
JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET);
- xmlnode *session;
+ PurpleXmlNode *session;
jabber_iq_set_callback(iq, jabber_session_initialized_cb, NULL);
- session = xmlnode_new_child(iq->node, "session");
- xmlnode_set_namespace(session, NS_XMPP_SESSION);
+ session = purple_xmlnode_new_child(iq->node, "session");
+ purple_xmlnode_set_namespace(session, NS_XMPP_SESSION);
jabber_iq_send(iq);
}
static void jabber_bind_result_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
- xmlnode *bind;
+ PurpleXmlNode *bind;
if (type == JABBER_IQ_RESULT &&
- (bind = xmlnode_get_child_with_namespace(packet, "bind", NS_XMPP_BIND))) {
- xmlnode *jid;
+ (bind = purple_xmlnode_get_child_with_namespace(packet, "bind", NS_XMPP_BIND))) {
+ PurpleXmlNode *jid;
char *full_jid;
- if((jid = xmlnode_get_child(bind, "jid")) && (full_jid = xmlnode_get_data(jid))) {
+ if((jid = purple_xmlnode_get_child(bind, "jid")) && (full_jid = purple_xmlnode_get_data(jid))) {
jabber_id_free(js->user);
js->user = jabber_id_new(full_jid);
if (js->user == NULL) {
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Invalid response from server"));
g_free(full_jid);
@@ -170,7 +169,7 @@ static void jabber_bind_result_cb(JabberStream *js, const char *from,
} else {
PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
char *msg = jabber_parse_error(js, packet, &reason);
- purple_connection_error_reason(js->gc, reason, msg);
+ purple_connection_error(js->gc, reason, msg);
g_free(msg);
return;
@@ -213,10 +212,10 @@ static char *jabber_prep_resource(char *input) {
}
static gboolean
-jabber_process_starttls(JabberStream *js, xmlnode *packet)
+jabber_process_starttls(JabberStream *js, PurpleXmlNode *packet)
{
PurpleAccount *account;
- xmlnode *starttls;
+ PurpleXmlNode *starttls;
account = purple_connection_get_account(js->gc);
@@ -245,16 +244,16 @@ jabber_process_starttls(JabberStream *js, xmlnode *packet)
}
#endif
- starttls = xmlnode_get_child(packet, "starttls");
- if(xmlnode_get_child(starttls, "required")) {
- purple_connection_error_reason(js->gc,
+ starttls = purple_xmlnode_get_child(packet, "starttls");
+ if(purple_xmlnode_get_child(starttls, "required")) {
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
_("Server requires TLS/SSL, but no TLS/SSL support was found."));
return TRUE;
}
if (g_str_equal("require_tls", purple_account_get_string(account, "connection_security", JABBER_DEFAULT_REQUIRE_TLS))) {
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
_("You require encryption, but no TLS/SSL support was found."));
return TRUE;
@@ -263,19 +262,19 @@ jabber_process_starttls(JabberStream *js, xmlnode *packet)
return FALSE;
}
-void jabber_stream_features_parse(JabberStream *js, xmlnode *packet)
+void jabber_stream_features_parse(JabberStream *js, PurpleXmlNode *packet)
{
PurpleAccount *account = purple_connection_get_account(js->gc);
const char *connection_security =
purple_account_get_string(account, "connection_security", JABBER_DEFAULT_REQUIRE_TLS);
- if (xmlnode_get_child(packet, "starttls")) {
+ if (purple_xmlnode_get_child(packet, "starttls")) {
if (jabber_process_starttls(js, packet)) {
jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION);
return;
}
} else if (g_str_equal(connection_security, "require_tls") && !jabber_stream_is_ssl(js)) {
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
_("You require encryption, but it is not available on this server."));
return;
@@ -283,29 +282,29 @@ void jabber_stream_features_parse(JabberStream *js, xmlnode *packet)
if(js->registration) {
jabber_register_start(js);
- } else if(xmlnode_get_child(packet, "mechanisms")) {
+ } else if(purple_xmlnode_get_child(packet, "mechanisms")) {
jabber_stream_set_state(js, JABBER_STREAM_AUTHENTICATING);
jabber_auth_start(js, packet);
- } else if(xmlnode_get_child(packet, "bind")) {
- xmlnode *bind, *resource;
+ } else if(purple_xmlnode_get_child(packet, "bind")) {
+ PurpleXmlNode *bind, *resource;
char *requested_resource;
JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET);
- bind = xmlnode_new_child(iq->node, "bind");
- xmlnode_set_namespace(bind, NS_XMPP_BIND);
+ bind = purple_xmlnode_new_child(iq->node, "bind");
+ purple_xmlnode_set_namespace(bind, NS_XMPP_BIND);
requested_resource = jabber_prep_resource(js->user->resource);
if (requested_resource != NULL) {
- resource = xmlnode_new_child(bind, "resource");
- xmlnode_insert_data(resource, requested_resource, -1);
+ resource = purple_xmlnode_new_child(bind, "resource");
+ purple_xmlnode_insert_data(resource, requested_resource, -1);
g_free(requested_resource);
}
jabber_iq_set_callback(iq, jabber_bind_result_cb, NULL);
jabber_iq_send(iq);
- } else if (xmlnode_get_child_with_namespace(packet, "ver", NS_ROSTER_VERSIONING)) {
+ } else if (purple_xmlnode_get_child_with_namespace(packet, "ver", NS_ROSTER_VERSIONING)) {
js->server_caps |= JABBER_CAP_ROSTER_VERSIONING;
- } else /* if(xmlnode_get_child_with_namespace(packet, "auth")) */ {
+ } else /* if(purple_xmlnode_get_child_with_namespace(packet, "auth")) */ {
/* If we get an empty stream:features packet, or we explicitly get
* an auth feature with namespace http://jabber.org/features/iq-auth
* we should revert back to iq:auth authentication, even though we're
@@ -315,19 +314,19 @@ void jabber_stream_features_parse(JabberStream *js, xmlnode *packet)
}
}
-static void jabber_stream_handle_error(JabberStream *js, xmlnode *packet)
+static void jabber_stream_handle_error(JabberStream *js, PurpleXmlNode *packet)
{
PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
char *msg = jabber_parse_error(js, packet, &reason);
- purple_connection_error_reason(js->gc, reason, msg);
+ purple_connection_error(js->gc, reason, msg);
g_free(msg);
}
static void tls_init(JabberStream *js);
-void jabber_process_packet(JabberStream *js, xmlnode **packet)
+void jabber_process_packet(JabberStream *js, PurpleXmlNode **packet)
{
const char *name;
const char *xmlns;
@@ -339,7 +338,7 @@ void jabber_process_packet(JabberStream *js, xmlnode **packet)
return;
name = (*packet)->name;
- xmlns = xmlnode_get_namespace(*packet);
+ xmlns = purple_xmlnode_get_namespace(*packet);
if(!strcmp((*packet)->name, "iq")) {
jabber_iq_parse(js, *packet);
@@ -391,8 +390,11 @@ static int jabber_do_send(JabberStream *js, const char *data, int len)
static void jabber_send_cb(gpointer data, gint source, PurpleInputCondition cond)
{
JabberStream *js = data;
+ const gchar *output = NULL;
int ret, writelen;
- writelen = purple_circ_buffer_get_max_read(js->write_buffer);
+
+ writelen = purple_circular_buffer_get_max_read(js->write_buffer);
+ output = purple_circular_buffer_get_output(js->write_buffer);
if (writelen == 0) {
purple_input_remove(js->writeh);
@@ -400,20 +402,20 @@ static void jabber_send_cb(gpointer data, gint source, PurpleInputCondition cond
return;
}
- ret = jabber_do_send(js, js->write_buffer->outptr, writelen);
+ ret = jabber_do_send(js, output, writelen);
if (ret < 0 && errno == EAGAIN)
return;
else if (ret <= 0) {
gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
g_strerror(errno));
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
g_free(tmp);
return;
}
- purple_circ_buffer_mark_read(js->write_buffer, ret);
+ purple_circular_buffer_mark_read(js->write_buffer, ret);
}
static gboolean do_jabber_send_raw(JabberStream *js, const char *data, int len)
@@ -440,10 +442,10 @@ static gboolean do_jabber_send_raw(JabberStream *js, const char *data, int len)
* we're disconnecting, don't generate (possibly another) error that
* (for some UIs) would mask the first.
*/
- if (!account->disconnecting) {
+ if (!purple_account_is_disconnecting(account)) {
gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
g_strerror(errno));
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
g_free(tmp);
}
@@ -456,7 +458,7 @@ static gboolean do_jabber_send_raw(JabberStream *js, const char *data, int len)
js->writeh = purple_input_add(
js->gsc ? js->gsc->fd : js->fd,
PURPLE_INPUT_WRITE, jabber_send_cb, js);
- purple_circ_buffer_append(js->write_buffer,
+ purple_circular_buffer_append(js->write_buffer,
data + ret, len - ret);
}
@@ -546,7 +548,7 @@ void jabber_send_raw(JabberStream *js, const char *data, int len)
purple_debug_error("jabber",
"sasl_encode error %d: %s\n", rc,
sasl_errdetail(js->sasl));
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
error);
g_free(error);
@@ -565,7 +567,7 @@ void jabber_send_raw(JabberStream *js, const char *data, int len)
#endif
if (js->bosh)
- jabber_bosh_connection_send_raw(js->bosh, data);
+ jabber_bosh_connection_send(js->bosh, data);
else
do_jabber_send_raw(js, data, len);
}
@@ -581,10 +583,10 @@ int jabber_prpl_send_raw(PurpleConnection *gc, const char *buf, int len)
*/
jabber_send_raw(js, buf, len);
- return (len < 0 ? strlen(buf) : len);
+ return (len < 0 ? (int)strlen(buf) : len);
}
-void jabber_send_signal_cb(PurpleConnection *pc, xmlnode **packet,
+void jabber_send_signal_cb(PurpleConnection *pc, PurpleXmlNode **packet,
gpointer unused)
{
JabberStream *js;
@@ -594,7 +596,7 @@ void jabber_send_signal_cb(PurpleConnection *pc, xmlnode **packet,
if (NULL == packet)
return;
- g_return_if_fail(PURPLE_CONNECTION_IS_VALID(pc));
+ PURPLE_ASSERT_CONNECTION_IS_VALID(pc);
js = purple_connection_get_protocol_data(pc);
@@ -605,21 +607,21 @@ void jabber_send_signal_cb(PurpleConnection *pc, xmlnode **packet,
if (g_str_equal((*packet)->name, "message") ||
g_str_equal((*packet)->name, "iq") ||
g_str_equal((*packet)->name, "presence"))
- xmlnode_set_namespace(*packet, NS_XMPP_CLIENT);
- txt = xmlnode_to_str(*packet, &len);
+ purple_xmlnode_set_namespace(*packet, NS_XMPP_CLIENT);
+ txt = purple_xmlnode_to_str(*packet, &len);
jabber_send_raw(js, txt, len);
g_free(txt);
}
-void jabber_send(JabberStream *js, xmlnode *packet)
+void jabber_send(JabberStream *js, PurpleXmlNode *packet)
{
purple_signal_emit(purple_connection_get_prpl(js->gc), "jabber-sending-xmlnode", js->gc, &packet);
}
static gboolean jabber_keepalive_timeout(PurpleConnection *gc)
{
- JabberStream *js = gc->proto_data;
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ JabberStream *js = purple_connection_get_protocol_data(gc);
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Ping timed out"));
js->keepalive_timeout = 0;
return FALSE;
@@ -644,20 +646,16 @@ jabber_recv_cb_ssl(gpointer data, PurpleSslConnection *gsc,
PurpleInputCondition cond)
{
PurpleConnection *gc = data;
- JabberStream *js = gc->proto_data;
+ JabberStream *js = purple_connection_get_protocol_data(gc);
int len;
static char buf[4096];
- /* TODO: It should be possible to make this check unnecessary */
- if(!PURPLE_CONNECTION_IS_VALID(gc)) {
- purple_ssl_close(gsc);
- g_return_if_reached();
- }
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
while((len = purple_ssl_read(gsc, buf, sizeof(buf) - 1)) > 0) {
- gc->last_received = time(NULL);
+ purple_connection_update_last_received(gc);
buf[len] = '\0';
- purple_debug_info("jabber", "Recv (ssl)(%d): %s\n", len, buf);
+ purple_debug_misc("jabber", "Recv (ssl)(%d): %s", len, buf);
jabber_parser_process(js, buf, len);
if(js->reinit)
jabber_stream_init(js);
@@ -672,7 +670,7 @@ jabber_recv_cb_ssl(gpointer data, PurpleSslConnection *gsc,
else
tmp = g_strdup_printf(_("Lost connection with server: %s"),
g_strerror(errno));
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
g_free(tmp);
}
@@ -686,10 +684,10 @@ jabber_recv_cb(gpointer data, gint source, PurpleInputCondition condition)
int len;
static char buf[4096];
- g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc));
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
if((len = read(js->fd, buf, sizeof(buf) - 1)) > 0) {
- gc->last_received = time(NULL);
+ purple_connection_update_last_received(gc);
#ifdef HAVE_CYRUS_SASL
if (js->sasl_maxbuf > 0) {
const char *out;
@@ -704,7 +702,7 @@ jabber_recv_cb(gpointer data, gint source, PurpleInputCondition condition)
purple_debug_error("jabber",
"sasl_decode_error %d: %s\n", rc,
sasl_errdetail(js->sasl));
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
error);
} else if (olen > 0) {
@@ -717,7 +715,7 @@ jabber_recv_cb(gpointer data, gint source, PurpleInputCondition condition)
}
#endif
buf[len] = '\0';
- purple_debug_info("jabber", "Recv (%d): %s\n", len, buf);
+ purple_debug_misc("jabber", "Recv (%d): %s", len, buf);
jabber_parser_process(js, buf, len);
if(js->reinit)
jabber_stream_init(js);
@@ -730,7 +728,7 @@ jabber_recv_cb(gpointer data, gint source, PurpleInputCondition condition)
else
tmp = g_strdup_printf(_("Lost connection with server: %s"),
g_strerror(errno));
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
g_free(tmp);
}
@@ -743,13 +741,9 @@ jabber_login_callback_ssl(gpointer data, PurpleSslConnection *gsc,
PurpleConnection *gc = data;
JabberStream *js;
- /* TODO: It should be possible to make this check unnecessary */
- if(!PURPLE_CONNECTION_IS_VALID(gc)) {
- purple_ssl_close(gsc);
- g_return_if_reached();
- }
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
- js = gc->proto_data;
+ js = purple_connection_get_protocol_data(gc);
if(js->state == JABBER_STREAM_CONNECTING)
jabber_send_raw(js, "<?xml version='1.0' ?>", -1);
@@ -774,7 +768,7 @@ txt_resolved_cb(GList *responses, gpointer data)
token = g_strsplit(purple_txt_response_get_content(resp), "=", 2);
if (!strcmp(token[0], "_xmpp-client-xbosh")) {
purple_debug_info("jabber","Found alternative connection method using %s at %s.\n", token[0], token[1]);
- js->bosh = jabber_bosh_connection_init(js, token[1]);
+ js->bosh = jabber_bosh_connection_new(js, token[1]);
g_strfreev(token);
break;
}
@@ -783,15 +777,13 @@ txt_resolved_cb(GList *responses, gpointer data)
responses = g_list_delete_link(responses, responses);
}
- if (js->bosh) {
+ if (js->bosh)
found = TRUE;
- jabber_bosh_connection_connect(js->bosh);
- }
if (!found) {
purple_debug_warning("jabber", "Unable to find alternative XMPP connection "
"methods after failing to connect directly.\n");
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to connect"));
return;
@@ -815,7 +807,7 @@ jabber_login_callback(gpointer data, gint source, const gchar *error)
try_srv_connect(js);
} else {
purple_debug_info("jabber","Couldn't connect directly to %s. Trying to find alternative connection methods, like BOSH.\n", js->user->domain);
- js->srv_query_data = purple_txt_resolve_account(
+ js->srv_query_data = purple_txt_resolve(
purple_connection_get_account(gc), "_xmppconnect",
js->user->domain, txt_resolved_cb, js);
}
@@ -831,7 +823,7 @@ jabber_login_callback(gpointer data, gint source, const gchar *error)
jabber_send_raw(js, "<?xml version='1.0' ?>", -1);
jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING);
- gc->inpa = purple_input_add(js->fd, PURPLE_INPUT_READ, jabber_recv_cb, gc);
+ js->inpa = purple_input_add(js->fd, PURPLE_INPUT_READ, jabber_recv_cb, gc);
}
static void
@@ -841,10 +833,9 @@ jabber_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error,
PurpleConnection *gc = data;
JabberStream *js;
- /* If the connection is already disconnected, we don't need to do anything else */
- g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc));
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
- js = gc->proto_data;
+ js = purple_connection_get_protocol_data(gc);
js->gsc = NULL;
purple_connection_ssl_error (gc, error);
@@ -852,9 +843,9 @@ jabber_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error,
static void tls_init(JabberStream *js)
{
- purple_input_remove(js->gc->inpa);
- js->gc->inpa = 0;
- js->gsc = purple_ssl_connect_with_host_fd(js->gc->account, js->fd,
+ purple_input_remove(js->inpa);
+ js->inpa = 0;
+ js->gsc = purple_ssl_connect_with_host_fd(purple_connection_get_account(js->gc), js->fd,
jabber_login_callback_ssl, jabber_ssl_connect_failure, js->certificate_CN, js->gc);
/* The fd is no longer our concern */
js->fd = -1;
@@ -876,7 +867,7 @@ static gboolean jabber_login_connect(JabberStream *js, const char *domain, const
if (purple_proxy_connect(js->gc, purple_connection_get_account(js->gc),
host, port, jabber_login_callback, js->gc) == NULL) {
if (fatal_failure) {
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to connect"));
}
@@ -930,15 +921,11 @@ jabber_stream_new(PurpleAccount *account)
gchar *user;
gchar *slash;
- js = gc->proto_data = g_new0(JabberStream, 1);
+ js = g_new0(JabberStream, 1);
+ purple_connection_set_protocol_data(gc, js);
js->gc = gc;
js->fd = -1;
-
- if (g_strcmp0("prpl-facebook-xmpp",
- purple_account_get_protocol_id(account)) == 0)
- {
- js->server_caps |= JABBER_CAP_FACEBOOK;
- }
+ js->http_conns = purple_http_connection_set_new();
user = g_strdup(purple_account_get_username(account));
/* jabber_id_new doesn't accept "user@domain/" as valid */
@@ -948,7 +935,7 @@ jabber_stream_new(PurpleAccount *account)
js->user = jabber_id_new(user);
if (!js->user) {
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
_("Invalid XMPP ID"));
g_free(user);
@@ -957,7 +944,7 @@ jabber_stream_new(PurpleAccount *account)
}
if (!js->user->node || *(js->user->node) == '\0') {
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
_("Invalid XMPP ID. Username portion must be set."));
g_free(user);
@@ -966,7 +953,7 @@ jabber_stream_new(PurpleAccount *account)
}
if (!js->user->domain || *(js->user->domain) == '\0') {
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
_("Invalid XMPP ID. Domain must be set."));
g_free(user);
@@ -986,7 +973,7 @@ jabber_stream_new(PurpleAccount *account)
g_free(user);
if (!js->user_jb) {
/* This basically *can't* fail, but for good measure... */
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
_("Invalid XMPP ID"));
/* Destroying the connection will free the JabberStream */
@@ -1000,7 +987,7 @@ jabber_stream_new(PurpleAccount *account)
js->chats = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, (GDestroyNotify)jabber_chat_free);
js->next_id = g_random_int();
- js->write_buffer = purple_circ_buffer_new(512);
+ js->write_buffer = purple_circular_buffer_new(512);
js->old_length = 0;
js->keepalive_timeout = 0;
js->max_inactivity = DEFAULT_INACTIVITY_TIME;
@@ -1013,7 +1000,6 @@ jabber_stream_new(PurpleAccount *account)
js->stun_query = NULL;
js->google_relay_token = NULL;
js->google_relay_host = NULL;
- js->google_relay_requests = NULL;
/* if we are idle, set idle-ness on the stream (this could happen if we get
disconnected and the reconnects while being idle. I don't think it makes
@@ -1041,11 +1027,9 @@ jabber_stream_connect(JabberStream *js)
* attached to that choice, though.
*/
if (*bosh_url) {
- js->bosh = jabber_bosh_connection_init(js, bosh_url);
- if (js->bosh)
- jabber_bosh_connection_connect(js->bosh);
- else {
- purple_connection_error_reason(gc,
+ js->bosh = jabber_bosh_connection_new(js, bosh_url);
+ if (!js->bosh) {
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
_("Malformed BOSH URL"));
}
@@ -1062,12 +1046,12 @@ jabber_stream_connect(JabberStream *js)
purple_account_get_int(account, "port", 5223),
jabber_login_callback_ssl, jabber_ssl_connect_failure, gc);
if (!js->gsc) {
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
_("Unable to establish SSL connection"));
}
} else {
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
_("SSL support unavailable"));
}
@@ -1081,7 +1065,7 @@ jabber_stream_connect(JabberStream *js)
jabber_login_connect(js, js->user->domain, connect_server,
purple_account_get_int(account, "port", 5222), TRUE);
} else {
- js->srv_query_data = purple_srv_resolve_account(account, "xmpp-client",
+ js->srv_query_data = purple_srv_resolve(account, "xmpp-client",
"tcp", js->user->domain, srv_resolved_cb, js);
}
}
@@ -1091,10 +1075,11 @@ jabber_login(PurpleAccount *account)
{
PurpleConnection *gc = purple_account_get_connection(account);
JabberStream *js;
- PurpleStoredImage *image;
+ PurpleImage *image;
- gc->flags |= PURPLE_CONNECTION_HTML |
- PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY;
+ purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_HTML |
+ PURPLE_CONNECTION_FLAG_ALLOW_CUSTOM_SMILEY |
+ PURPLE_CONNECTION_FLAG_NO_IMAGES);
js = jabber_stream_new(account);
if (js == NULL)
return;
@@ -1110,10 +1095,10 @@ jabber_login(PurpleAccount *account)
*/
image = purple_buddy_icons_find_account_icon(account);
if (image != NULL) {
- js->initial_avatar_hash =
- jabber_calculate_data_hash(purple_imgstore_get_data(image),
- purple_imgstore_get_size(image), "sha1");
- purple_imgstore_unref(image);
+ js->initial_avatar_hash = jabber_calculate_data_hash(
+ purple_image_get_data(image),
+ purple_image_get_size(image), "sha1");
+ g_object_unref(image);
}
jabber_stream_connect(js);
@@ -1130,19 +1115,21 @@ conn_close_cb(gpointer data)
purple_account_disconnect(account);
+ js->conn_close_timeout = 0;
+
return FALSE;
}
static void
jabber_connection_schedule_close(JabberStream *js)
{
- purple_timeout_add(0, conn_close_cb, js);
+ js->conn_close_timeout = purple_timeout_add(0, conn_close_cb, js);
}
static void
jabber_registration_result_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
PurpleAccount *account = purple_connection_get_account(js->gc);
char *buf;
@@ -1152,15 +1139,15 @@ jabber_registration_result_cb(JabberStream *js, const char *from,
if(js->registration) {
buf = g_strdup_printf(_("Registration of %s@%s successful"),
js->user->node, js->user->domain);
- if(account->registration_cb)
- (account->registration_cb)(account, TRUE, account->registration_cb_user_data);
+ purple_account_register_completed(account, TRUE);
} else {
g_return_if_fail(to != NULL);
buf = g_strdup_printf(_("Registration to %s successful"),
to);
}
purple_notify_info(NULL, _("Registration Successful"),
- _("Registration Successful"), buf);
+ _("Registration Successful"), buf,
+ purple_request_cpar_from_connection(js->gc));
g_free(buf);
} else {
char *msg = jabber_parse_error(js, packet, NULL);
@@ -1169,10 +1156,10 @@ jabber_registration_result_cb(JabberStream *js, const char *from,
msg = g_strdup(_("Unknown Error"));
purple_notify_error(NULL, _("Registration Failed"),
- _("Registration Failed"), msg);
+ _("Registration Failed"), msg,
+ purple_request_cpar_from_connection(js->gc));
g_free(msg);
- if(account->registration_cb)
- (account->registration_cb)(account, FALSE, account->registration_cb_user_data);
+ purple_account_register_completed(account, FALSE);
}
g_free(to);
if(js->registration)
@@ -1182,7 +1169,7 @@ jabber_registration_result_cb(JabberStream *js, const char *from,
static void
jabber_unregistration_result_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
char *buf;
char *to = data;
@@ -1195,7 +1182,8 @@ jabber_unregistration_result_cb(JabberStream *js, const char *from,
buf = g_strdup_printf(_("Registration from %s successfully removed"),
to);
purple_notify_info(NULL, _("Unregistration Successful"),
- _("Unregistration Successful"), buf);
+ _("Unregistration Successful"), buf,
+ purple_request_cpar_from_connection(js->gc));
g_free(buf);
} else {
char *msg = jabber_parse_error(js, packet, NULL);
@@ -1204,7 +1192,8 @@ jabber_unregistration_result_cb(JabberStream *js, const char *from,
msg = g_strdup(_("Unknown Error"));
purple_notify_error(NULL, _("Unregistration Failed"),
- _("Unregistration Failed"), msg);
+ _("Unregistration Failed"), msg,
+ purple_request_cpar_from_connection(js->gc));
g_free(msg);
}
g_free(to);
@@ -1219,14 +1208,14 @@ static void
jabber_register_cb(JabberRegisterCBData *cbdata, PurpleRequestFields *fields)
{
GList *groups, *flds;
- xmlnode *query, *y;
+ PurpleXmlNode *query, *y;
JabberIq *iq;
char *username;
iq = jabber_iq_new_query(cbdata->js, JABBER_IQ_SET, "jabber:iq:register");
- query = xmlnode_get_child(iq->node, "query");
+ query = purple_xmlnode_get_child(iq->node, "query");
if (cbdata->who)
- xmlnode_set_attrib(iq->node, "to", cbdata->who);
+ purple_xmlnode_set_attrib(iq->node, "to", cbdata->who);
for(groups = purple_request_fields_get_groups(fields); groups;
groups = groups->next) {
@@ -1238,13 +1227,13 @@ jabber_register_cb(JabberRegisterCBData *cbdata, PurpleRequestFields *fields)
gboolean value = purple_request_field_bool_get_value(field);
if(value) {
/* unregister from service. this doesn't include any of the fields, so remove them from the stanza by recreating it
- (there's no "remove child" function for xmlnode) */
+ (there's no "remove child" function for PurpleXmlNode) */
jabber_iq_free(iq);
iq = jabber_iq_new_query(cbdata->js, JABBER_IQ_SET, "jabber:iq:register");
- query = xmlnode_get_child(iq->node, "query");
+ query = purple_xmlnode_get_child(iq->node, "query");
if (cbdata->who)
- xmlnode_set_attrib(iq->node,"to",cbdata->who);
- xmlnode_new_child(query, "remove");
+ purple_xmlnode_set_attrib(iq->node,"to",cbdata->who);
+ purple_xmlnode_new_child(query, "remove");
jabber_iq_set_callback(iq, jabber_unregistration_result_cb, cbdata->who);
@@ -1265,14 +1254,14 @@ jabber_register_cb(JabberRegisterCBData *cbdata, PurpleRequestFields *fields)
if (!ids[i])
continue;
- y = xmlnode_new_child(query, ids[i]);
- xmlnode_insert_data(y, value, -1);
+ y = purple_xmlnode_new_child(query, ids[i]);
+ purple_xmlnode_insert_data(y, value, -1);
if(cbdata->js->registration && !strcmp(id, "username")) {
g_free(cbdata->js->user->node);
cbdata->js->user->node = g_strdup(value);
}
if(cbdata->js->registration && !strcmp(id, "password"))
- purple_account_set_password(cbdata->js->gc->account, value);
+ purple_account_set_password(purple_connection_get_account(cbdata->js->gc), value, NULL, NULL);
}
}
}
@@ -1281,7 +1270,7 @@ jabber_register_cb(JabberRegisterCBData *cbdata, PurpleRequestFields *fields)
username = g_strdup_printf("%s@%s%s%s", cbdata->js->user->node, cbdata->js->user->domain,
cbdata->js->user->resource ? "/" : "",
cbdata->js->user->resource ? cbdata->js->user->resource : "");
- purple_account_set_username(cbdata->js->gc->account, username);
+ purple_account_set_username(purple_connection_get_account(cbdata->js->gc), username);
g_free(username);
}
@@ -1296,26 +1285,25 @@ jabber_register_cancel_cb(JabberRegisterCBData *cbdata, PurpleRequestFields *fie
{
PurpleAccount *account = purple_connection_get_account(cbdata->js->gc);
if(account && cbdata->js->registration) {
- if(account->registration_cb)
- (account->registration_cb)(account, FALSE, account->registration_cb_user_data);
+ purple_account_register_completed(account, FALSE);
jabber_connection_schedule_close(cbdata->js);
}
g_free(cbdata->who);
g_free(cbdata);
}
-static void jabber_register_x_data_cb(JabberStream *js, xmlnode *result, gpointer data)
+static void jabber_register_x_data_cb(JabberStream *js, PurpleXmlNode *result, gpointer data)
{
- xmlnode *query;
+ PurpleXmlNode *query;
JabberIq *iq;
char *to = data;
iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:register");
- query = xmlnode_get_child(iq->node, "query");
+ query = purple_xmlnode_get_child(iq->node, "query");
if (to)
- xmlnode_set_attrib(iq->node,"to",to);
+ purple_xmlnode_set_attrib(iq->node,"to",to);
- xmlnode_insert_child(query, result);
+ purple_xmlnode_insert_child(query, result);
jabber_iq_set_callback(iq, jabber_registration_result_cb, to);
jabber_iq_send(iq);
@@ -1340,13 +1328,13 @@ static const struct {
};
void jabber_register_parse(JabberStream *js, const char *from, JabberIqType type,
- const char *id, xmlnode *query)
+ const char *id, PurpleXmlNode *query)
{
PurpleAccount *account = purple_connection_get_account(js->gc);
PurpleRequestFields *fields;
PurpleRequestFieldGroup *group;
PurpleRequestField *field;
- xmlnode *x, *y, *node;
+ PurpleXmlNode *x, *y, *node;
char *instructions;
JabberRegisterCBData *cbdata;
gboolean registered = FALSE;
@@ -1357,39 +1345,40 @@ void jabber_register_parse(JabberStream *js, const char *from, JabberIqType type
if(js->registration) {
/* get rid of the login thingy */
- purple_connection_set_state(js->gc, PURPLE_CONNECTED);
+ purple_connection_set_state(js->gc, PURPLE_CONNECTION_CONNECTED);
}
- if(xmlnode_get_child(query, "registered")) {
+ if(purple_xmlnode_get_child(query, "registered")) {
registered = TRUE;
if(js->registration) {
purple_notify_error(NULL, _("Already Registered"),
- _("Already Registered"), NULL);
- if(account->registration_cb)
- (account->registration_cb)(account, FALSE, account->registration_cb_user_data);
+ _("Already Registered"), NULL,
+ purple_request_cpar_from_connection(js->gc));
+ purple_account_register_completed(account, FALSE);
jabber_connection_schedule_close(js);
return;
}
}
- if((x = xmlnode_get_child_with_namespace(query, "x", "jabber:x:data"))) {
+ if((x = purple_xmlnode_get_child_with_namespace(query, "x", "jabber:x:data"))) {
jabber_x_data_request(js, x, jabber_register_x_data_cb, g_strdup(from));
return;
- } else if((x = xmlnode_get_child_with_namespace(query, "x", NS_OOB_X_DATA))) {
- xmlnode *url;
+ } else if((x = purple_xmlnode_get_child_with_namespace(query, "x", NS_OOB_X_DATA))) {
+ PurpleXmlNode *url;
- if((url = xmlnode_get_child(x, "url"))) {
+ if((url = purple_xmlnode_get_child(x, "url"))) {
char *href;
- if((href = xmlnode_get_data(url))) {
+ if((href = purple_xmlnode_get_data(url))) {
purple_notify_uri(NULL, href);
g_free(href);
if(js->registration) {
- js->gc->wants_to_die = TRUE;
- if(account->registration_cb) /* succeeded, but we have no login info */
- (account->registration_cb)(account, TRUE, account->registration_cb_user_data);
+ /* succeeded, but we have no login info */
+ purple_account_register_completed(account, TRUE);
+ purple_connection_error(js->gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+ _("Registration completed successfully. Please reconnect to continue."));
jabber_connection_schedule_close(js);
}
return;
@@ -1403,8 +1392,8 @@ void jabber_register_parse(JabberStream *js, const char *from, JabberIqType type
group = purple_request_field_group_new(NULL);
purple_request_fields_add_group(fields, group);
- if((node = xmlnode_get_child(query, "username"))) {
- char *data = xmlnode_get_data(node);
+ if((node = purple_xmlnode_get_child(query, "username"))) {
+ char *data = purple_xmlnode_get_data(node);
if(js->registration)
field = purple_request_field_string_new("username", _("Username"), data ? data : js->user->node, FALSE);
else
@@ -1413,12 +1402,12 @@ void jabber_register_parse(JabberStream *js, const char *from, JabberIqType type
purple_request_field_group_add_field(group, field);
g_free(data);
}
- if((node = xmlnode_get_child(query, "password"))) {
+ if((node = purple_xmlnode_get_child(query, "password"))) {
if(js->registration)
field = purple_request_field_string_new("password", _("Password"),
purple_connection_get_password(js->gc), FALSE);
else {
- char *data = xmlnode_get_data(node);
+ char *data = purple_xmlnode_get_data(node);
field = purple_request_field_string_new("password", _("Password"), data, FALSE);
g_free(data);
}
@@ -1427,12 +1416,12 @@ void jabber_register_parse(JabberStream *js, const char *from, JabberIqType type
purple_request_field_group_add_field(group, field);
}
- if((node = xmlnode_get_child(query, "name"))) {
+ if((node = purple_xmlnode_get_child(query, "name"))) {
if(js->registration)
field = purple_request_field_string_new("name", _("Name"),
- purple_account_get_alias(js->gc->account), FALSE);
+ purple_account_get_private_alias(purple_connection_get_account(js->gc)), FALSE);
else {
- char *data = xmlnode_get_data(node);
+ char *data = purple_xmlnode_get_data(node);
field = purple_request_field_string_new("name", _("Name"), data, FALSE);
g_free(data);
}
@@ -1440,8 +1429,8 @@ void jabber_register_parse(JabberStream *js, const char *from, JabberIqType type
}
for (i = 0; registration_fields[i].name != NULL; ++i) {
- if ((node = xmlnode_get_child(query, registration_fields[i].name))) {
- char *data = xmlnode_get_data(node);
+ if ((node = purple_xmlnode_get_child(query, registration_fields[i].name))) {
+ char *data = purple_xmlnode_get_data(node);
field = purple_request_field_string_new(registration_fields[i].name,
_(registration_fields[i].label),
data, FALSE);
@@ -1455,8 +1444,8 @@ void jabber_register_parse(JabberStream *js, const char *from, JabberIqType type
purple_request_field_group_add_field(group, field);
}
- if((y = xmlnode_get_child(query, "instructions")))
- instructions = xmlnode_get_data(y);
+ if((y = purple_xmlnode_get_child(query, "instructions")))
+ instructions = purple_xmlnode_get_data(y);
else if(registered)
instructions = g_strdup(_("Please fill out the information below "
"to change your account registration."));
@@ -1473,19 +1462,18 @@ void jabber_register_parse(JabberStream *js, const char *from, JabberIqType type
_("Register New XMPP Account"), instructions, fields,
_("Register"), G_CALLBACK(jabber_register_cb),
_("Cancel"), G_CALLBACK(jabber_register_cancel_cb),
- purple_connection_get_account(js->gc), NULL, NULL,
+ purple_request_cpar_from_connection(js->gc),
cbdata);
else {
char *title;
g_return_if_fail(from != NULL);
title = registered ? g_strdup_printf(_("Change Account Registration at %s"), from)
:g_strdup_printf(_("Register New Account at %s"), from);
- purple_request_fields(js->gc, title,
- title, instructions, fields,
- (registered ? _("Change Registration") : _("Register")), G_CALLBACK(jabber_register_cb),
- _("Cancel"), G_CALLBACK(jabber_register_cancel_cb),
- purple_connection_get_account(js->gc), NULL, NULL,
- cbdata);
+ purple_request_fields(js->gc, title, title, instructions,
+ fields, (registered ? _("Change Registration") :
+ _("Register")), G_CALLBACK(jabber_register_cb),
+ _("Cancel"), G_CALLBACK(jabber_register_cancel_cb),
+ purple_request_cpar_from_connection(js->gc), cbdata);
g_free(title);
}
@@ -1504,7 +1492,7 @@ void jabber_register_gateway(JabberStream *js, const char *gateway) {
JabberIq *iq;
iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:register");
- xmlnode_set_attrib(iq->node, "to", gateway);
+ purple_xmlnode_set_attrib(iq->node, "to", gateway);
jabber_iq_send(iq);
}
@@ -1523,7 +1511,7 @@ void jabber_register_account(PurpleAccount *account)
static void
jabber_unregister_account_iq_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
PurpleAccount *account = purple_connection_get_account(js->gc);
@@ -1531,13 +1519,15 @@ jabber_unregister_account_iq_cb(JabberStream *js, const char *from,
char *msg = jabber_parse_error(js, packet, NULL);
purple_notify_error(js->gc, _("Error unregistering account"),
- _("Error unregistering account"), msg);
+ _("Error unregistering account"), msg,
+ purple_request_cpar_from_connection(js->gc));
g_free(msg);
if(js->unregistration_cb)
js->unregistration_cb(account, FALSE, js->unregistration_user_data);
} else {
- purple_notify_info(js->gc, _("Account successfully unregistered"),
- _("Account successfully unregistered"), NULL);
+ purple_notify_info(js->gc, _("Account successfully "
+ "unregistered"), _("Account successfully unregistered"),
+ NULL, purple_request_cpar_from_connection(js->gc));
if(js->unregistration_cb)
js->unregistration_cb(account, TRUE, js->unregistration_user_data);
}
@@ -1545,16 +1535,16 @@ jabber_unregister_account_iq_cb(JabberStream *js, const char *from,
static void jabber_unregister_account_cb(JabberStream *js) {
JabberIq *iq;
- xmlnode *query;
+ PurpleXmlNode *query;
g_return_if_fail(js->unregistration);
iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:register");
- query = xmlnode_get_child_with_namespace(iq->node, "query", "jabber:iq:register");
+ query = purple_xmlnode_get_child_with_namespace(iq->node, "query", "jabber:iq:register");
- xmlnode_new_child(query, "remove");
- xmlnode_set_attrib(iq->node, "to", js->user->domain);
+ purple_xmlnode_new_child(query, "remove");
+ purple_xmlnode_set_attrib(iq->node, "to", js->user->domain);
jabber_iq_set_callback(iq, jabber_unregister_account_iq_cb, NULL);
jabber_iq_send(iq);
@@ -1564,17 +1554,17 @@ void jabber_unregister_account(PurpleAccount *account, PurpleAccountUnregistrati
PurpleConnection *gc = purple_account_get_connection(account);
JabberStream *js;
- if(gc->state != PURPLE_CONNECTED) {
- if(gc->state != PURPLE_CONNECTING)
+ if (purple_connection_get_state(gc) != PURPLE_CONNECTION_CONNECTED) {
+ if (purple_connection_get_state(gc) != PURPLE_CONNECTION_CONNECTING)
jabber_login(account);
- js = gc->proto_data;
+ js = purple_connection_get_protocol_data(gc);
js->unregistration = TRUE;
js->unregistration_cb = cb;
js->unregistration_user_data = user_data;
return;
}
- js = gc->proto_data;
+ js = purple_connection_get_protocol_data(gc);
if (js->unregistration) {
purple_debug_error("jabber", "Unregistration in process; ignoring duplicate request.\n");
@@ -1600,25 +1590,25 @@ void jabber_close(PurpleConnection *gc)
/* Close all of the open Jingle sessions on this stream */
jingle_terminate_sessions(js);
- if (js->bosh)
- jabber_bosh_connection_close(js->bosh);
- else if ((js->gsc && js->gsc->fd > 0) || js->fd > 0)
+ if (js->bosh) {
+ jabber_bosh_connection_destroy(js->bosh);
+ js->bosh = NULL;
+ } else if ((js->gsc && js->gsc->fd > 0) || js->fd > 0)
jabber_send_raw(js, "</stream:stream>", -1);
if (js->srv_query_data)
- purple_srv_cancel(js->srv_query_data);
+ purple_srv_txt_query_destroy(js->srv_query_data);
if(js->gsc) {
purple_ssl_close(js->gsc);
} else if (js->fd > 0) {
- if(js->gc->inpa)
- purple_input_remove(js->gc->inpa);
+ if(js->inpa) {
+ purple_input_remove(js->inpa);
+ js->inpa = 0;
+ }
close(js->fd);
}
- if (js->bosh)
- jabber_bosh_connection_destroy(js->bosh);
-
jabber_buddy_remove_all_pending_buddy_info_requests(js);
jabber_parser_free(js);
@@ -1649,10 +1639,7 @@ void jabber_close(PurpleConnection *gc)
js->bs_proxies = g_list_delete_link(js->bs_proxies, js->bs_proxies);
}
- while(js->url_datas) {
- purple_util_fetch_url_cancel(js->url_datas->data);
- js->url_datas = g_slist_delete_link(js->url_datas, js->url_datas);
- }
+ purple_http_connection_set_destroy(js->http_conns);
g_free(js->stream_id);
if(js->user)
@@ -1662,7 +1649,7 @@ void jabber_close(PurpleConnection *gc)
g_free(js->caps_hash);
if (js->write_buffer)
- purple_circ_buffer_destroy(js->write_buffer);
+ g_object_unref(G_OBJECT(js->write_buffer));
if(js->writeh)
purple_input_remove(js->writeh);
if (js->auth_mech && js->auth_mech->dispose)
@@ -1704,6 +1691,8 @@ void jabber_close(PurpleConnection *gc)
purple_timeout_remove(js->keepalive_timeout);
if (js->inactivity_timer != 0)
purple_timeout_remove(js->inactivity_timer);
+ if (js->conn_close_timeout != 0)
+ purple_timeout_remove(js->conn_close_timeout);
g_free(js->srv_rec);
js->srv_rec = NULL;
@@ -1720,21 +1709,10 @@ void jabber_close(PurpleConnection *gc)
/* remove Google relay-related stuff */
g_free(js->google_relay_token);
g_free(js->google_relay_host);
- if (js->google_relay_requests) {
- while (js->google_relay_requests) {
- PurpleUtilFetchUrlData *url_data =
- (PurpleUtilFetchUrlData *) js->google_relay_requests->data;
- purple_util_fetch_url_cancel(url_data);
- g_free(url_data);
- js->google_relay_requests =
- g_list_delete_link(js->google_relay_requests,
- js->google_relay_requests);
- }
- }
g_free(js);
- gc->proto_data = NULL;
+ purple_connection_set_protocol_data(gc, NULL);
}
void jabber_stream_set_state(JabberStream *js, JabberStreamState state)
@@ -1773,7 +1751,7 @@ void jabber_stream_set_state(JabberStream *js, JabberStreamState state)
/* Start up the inactivity timer */
jabber_stream_restart_inactivity_timer(js);
- purple_connection_set_state(js->gc, PURPLE_CONNECTED);
+ purple_connection_set_state(js->gc, PURPLE_CONNECTION_CONNECTED);
break;
}
@@ -1788,7 +1766,7 @@ char *jabber_get_next_id(JabberStream *js)
void jabber_idle_set(PurpleConnection *gc, int idle)
{
- JabberStream *js = gc->proto_data;
+ JabberStream *js = purple_connection_get_protocol_data(gc);
js->idle = idle ? time(NULL) - idle : idle;
@@ -1799,24 +1777,25 @@ void jabber_idle_set(PurpleConnection *gc, int idle)
void jabber_blocklist_parse_push(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *child)
+ PurpleXmlNode *child)
{
JabberIq *result;
- xmlnode *item;
+ PurpleXmlNode *item;
PurpleAccount *account;
gboolean is_block;
+ GSList *deny;
if (!jabber_is_own_account(js, from)) {
- xmlnode *error, *x;
+ PurpleXmlNode *error, *x;
result = jabber_iq_new(js, JABBER_IQ_ERROR);
- xmlnode_set_attrib(result->node, "id", id);
+ purple_xmlnode_set_attrib(result->node, "id", id);
if (from)
- xmlnode_set_attrib(result->node, "to", from);
+ purple_xmlnode_set_attrib(result->node, "to", from);
- error = xmlnode_new_child(result->node, "error");
- xmlnode_set_attrib(error, "type", "cancel");
- x = xmlnode_new_child(error, "not-allowed");
- xmlnode_set_namespace(x, NS_XMPP_STANZAS);
+ error = purple_xmlnode_new_child(result->node, "error");
+ purple_xmlnode_set_attrib(error, "type", "cancel");
+ x = purple_xmlnode_new_child(error, "not-allowed");
+ purple_xmlnode_set_namespace(x, NS_XMPP_STANZAS);
jabber_iq_send(result);
return;
@@ -1825,53 +1804,54 @@ void jabber_blocklist_parse_push(JabberStream *js, const char *from,
account = purple_connection_get_account(js->gc);
is_block = g_str_equal(child->name, "block");
- item = xmlnode_get_child(child, "item");
+ item = purple_xmlnode_get_child(child, "item");
if (!is_block && item == NULL) {
/* Unblock everyone */
purple_debug_info("jabber", "Received unblock push. Unblocking everyone.\n");
- while (account->deny != NULL) {
- purple_privacy_deny_remove(account, account->deny->data, TRUE);
+ while ((deny = purple_account_privacy_get_denied(account)) != NULL) {
+ purple_account_privacy_deny_remove(account, deny->data, TRUE);
}
} else if (item == NULL) {
/* An empty <block/> is bogus */
- xmlnode *error, *x;
+ PurpleXmlNode *error, *x;
result = jabber_iq_new(js, JABBER_IQ_ERROR);
- xmlnode_set_attrib(result->node, "id", id);
+ purple_xmlnode_set_attrib(result->node, "id", id);
- error = xmlnode_new_child(result->node, "error");
- xmlnode_set_attrib(error, "type", "modify");
- x = xmlnode_new_child(error, "bad-request");
- xmlnode_set_namespace(x, NS_XMPP_STANZAS);
+ error = purple_xmlnode_new_child(result->node, "error");
+ purple_xmlnode_set_attrib(error, "type", "modify");
+ x = purple_xmlnode_new_child(error, "bad-request");
+ purple_xmlnode_set_namespace(x, NS_XMPP_STANZAS);
jabber_iq_send(result);
return;
} else {
- for ( ; item; item = xmlnode_get_next_twin(item)) {
- const char *jid = xmlnode_get_attrib(item, "jid");
+ for ( ; item; item = purple_xmlnode_get_next_twin(item)) {
+ const char *jid = purple_xmlnode_get_attrib(item, "jid");
if (jid == NULL || *jid == '\0')
continue;
if (is_block)
- purple_privacy_deny_add(account, jid, TRUE);
+ purple_account_privacy_deny_add(account, jid, TRUE);
else
- purple_privacy_deny_remove(account, jid, TRUE);
+ purple_account_privacy_deny_remove(account, jid, TRUE);
}
}
result = jabber_iq_new(js, JABBER_IQ_RESULT);
- xmlnode_set_attrib(result->node, "id", id);
+ purple_xmlnode_set_attrib(result->node, "id", id);
jabber_iq_send(result);
}
static void jabber_blocklist_parse(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
- xmlnode *blocklist, *item;
+ PurpleXmlNode *blocklist, *item;
PurpleAccount *account;
+ GSList *deny;
- blocklist = xmlnode_get_child_with_namespace(packet,
+ blocklist = purple_xmlnode_get_child_with_namespace(packet,
"blocklist", NS_SIMPLE_BLOCKING);
account = purple_connection_get_account(js->gc);
@@ -1879,33 +1859,32 @@ static void jabber_blocklist_parse(JabberStream *js, const char *from,
return;
/* This is the only privacy method supported by XEP-0191 */
- if (account->perm_deny != PURPLE_PRIVACY_DENY_USERS)
- account->perm_deny = PURPLE_PRIVACY_DENY_USERS;
+ purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_DENY_USERS);
/*
* TODO: When account->deny is something more than a hash table, this can
* be re-written to find the set intersection and difference.
*/
- while (account->deny)
- purple_privacy_deny_remove(account, account->deny->data, TRUE);
+ while ((deny = purple_account_privacy_get_denied(account)))
+ purple_account_privacy_deny_remove(account, deny->data, TRUE);
- item = xmlnode_get_child(blocklist, "item");
+ item = purple_xmlnode_get_child(blocklist, "item");
while (item != NULL) {
- const char *jid = xmlnode_get_attrib(item, "jid");
- purple_privacy_deny_add(account, jid, TRUE);
- item = xmlnode_get_next_twin(item);
+ const char *jid = purple_xmlnode_get_attrib(item, "jid");
+ purple_account_privacy_deny_add(account, jid, TRUE);
+ item = purple_xmlnode_get_next_twin(item);
}
}
void jabber_request_block_list(JabberStream *js)
{
JabberIq *iq;
- xmlnode *blocklist;
+ PurpleXmlNode *blocklist;
iq = jabber_iq_new(js, JABBER_IQ_GET);
- blocklist = xmlnode_new_child(iq->node, "blocklist");
- xmlnode_set_namespace(blocklist, NS_SIMPLE_BLOCKING);
+ blocklist = purple_xmlnode_new_child(iq->node, "blocklist");
+ purple_xmlnode_set_namespace(blocklist, NS_SIMPLE_BLOCKING);
jabber_iq_set_callback(iq, jabber_blocklist_parse, NULL);
@@ -1916,7 +1895,7 @@ void jabber_add_deny(PurpleConnection *gc, const char *who)
{
JabberStream *js;
JabberIq *iq;
- xmlnode *block, *item;
+ PurpleXmlNode *block, *item;
g_return_if_fail(who != NULL && *who != '\0');
@@ -1933,17 +1912,18 @@ void jabber_add_deny(PurpleConnection *gc, const char *who)
if (!(js->server_caps & JABBER_CAP_BLOCKING))
{
purple_notify_error(NULL, _("Server doesn't support blocking"),
- _("Server doesn't support blocking"), NULL);
+ _("Server doesn't support blocking"), NULL,
+ purple_request_cpar_from_connection(gc));
return;
}
iq = jabber_iq_new(js, JABBER_IQ_SET);
- block = xmlnode_new_child(iq->node, "block");
- xmlnode_set_namespace(block, NS_SIMPLE_BLOCKING);
+ block = purple_xmlnode_new_child(iq->node, "block");
+ purple_xmlnode_set_namespace(block, NS_SIMPLE_BLOCKING);
- item = xmlnode_new_child(block, "item");
- xmlnode_set_attrib(item, "jid", who);
+ item = purple_xmlnode_new_child(block, "item");
+ purple_xmlnode_set_attrib(item, "jid", who);
jabber_iq_send(iq);
}
@@ -1952,7 +1932,7 @@ void jabber_rem_deny(PurpleConnection *gc, const char *who)
{
JabberStream *js;
JabberIq *iq;
- xmlnode *unblock, *item;
+ PurpleXmlNode *unblock, *item;
g_return_if_fail(who != NULL && *who != '\0');
@@ -1971,11 +1951,11 @@ void jabber_rem_deny(PurpleConnection *gc, const char *who)
iq = jabber_iq_new(js, JABBER_IQ_SET);
- unblock = xmlnode_new_child(iq->node, "unblock");
- xmlnode_set_namespace(unblock, NS_SIMPLE_BLOCKING);
+ unblock = purple_xmlnode_new_child(iq->node, "unblock");
+ purple_xmlnode_set_namespace(unblock, NS_SIMPLE_BLOCKING);
- item = xmlnode_new_child(unblock, "item");
- xmlnode_set_attrib(item, "jid", who);
+ item = purple_xmlnode_new_child(unblock, "item");
+ purple_xmlnode_set_attrib(item, "jid", who);
jabber_iq_send(iq);
}
@@ -2141,7 +2121,7 @@ const char* jabber_list_emblem(PurpleBuddy *b)
if(!gc)
return NULL;
- js = gc->proto_data;
+ js = purple_connection_get_protocol_data(gc);
if(js)
jb = jabber_buddy_find(js, purple_buddy_get_name(b), FALSE);
@@ -2182,8 +2162,8 @@ char *jabber_status_text(PurpleBuddy *b)
PurpleAccount *account = purple_buddy_get_account(b);
PurpleConnection *gc = purple_account_get_connection(account);
- if (gc && gc->proto_data)
- jb = jabber_buddy_find(gc->proto_data, purple_buddy_get_name(b), FALSE);
+ if (gc && purple_connection_get_protocol_data(gc))
+ jb = jabber_buddy_find(purple_connection_get_protocol_data(gc), purple_buddy_get_name(b), FALSE);
if(jb && !PURPLE_BUDDY_IS_ONLINE(b) && (jb->subscription & JABBER_SUB_PENDING || !(jb->subscription & JABBER_SUB_TO))) {
ret = g_strdup(_("Not Authorized"));
@@ -2233,7 +2213,7 @@ jabber_tooltip_add_resource_text(JabberBuddyResource *jbr,
label = g_strdup_printf("%s%s", _("Status"), (res ? res : ""));
value = g_strdup_printf("%s%s%s", state, (text ? ": " : ""), (text ? text : ""));
- purple_notify_user_info_add_pair(user_info, label, value);
+ purple_notify_user_info_add_pair_html(user_info, label, value);
g_free(label);
g_free(value);
g_free(text);
@@ -2246,7 +2226,7 @@ jabber_tooltip_add_resource_text(JabberBuddyResource *jbr,
gchar *idle_str =
purple_str_seconds_to_string(time(NULL) - jbr->idle);
label = g_strdup_printf("%s%s", _("Idle"), (res ? res : ""));
- purple_notify_user_info_add_pair(user_info, label, idle_str);
+ purple_notify_user_info_add_pair_plaintext(user_info, label, idle_str);
g_free(idle_str);
g_free(label);
}
@@ -2258,6 +2238,7 @@ void jabber_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboole
JabberBuddy *jb;
PurpleAccount *account;
PurpleConnection *gc;
+ JabberStream *js;
g_return_if_fail(b != NULL);
@@ -2266,9 +2247,11 @@ void jabber_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboole
gc = purple_account_get_connection(account);
g_return_if_fail(gc != NULL);
- g_return_if_fail(gc->proto_data != NULL);
- jb = jabber_buddy_find(gc->proto_data, purple_buddy_get_name(b), FALSE);
+ js = purple_connection_get_protocol_data(gc);
+ g_return_if_fail(js != NULL);
+
+ jb = jabber_buddy_find(js, purple_buddy_get_name(b), FALSE);
if(jb) {
JabberBuddyResource *jbr = NULL;
@@ -2318,10 +2301,10 @@ void jabber_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboole
char *moodplustext =
g_strdup_printf("%s (%s)", description ? _(description) : mood, moodtext);
- purple_notify_user_info_add_pair(user_info, _("Mood"), moodplustext);
+ purple_notify_user_info_add_pair_html(user_info, _("Mood"), moodplustext);
g_free(moodplustext);
} else
- purple_notify_user_info_add_pair(user_info, _("Mood"),
+ purple_notify_user_info_add_pair_html(user_info, _("Mood"),
description ? _(description) : mood);
}
if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {
@@ -2331,7 +2314,7 @@ void jabber_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboole
const char *album = purple_status_get_attr_string(tune, PURPLE_TUNE_ALBUM);
char *playing = purple_util_format_song_info(title, artist, album, NULL);
if (playing) {
- purple_notify_user_info_add_pair(user_info, _("Now Listening"), playing);
+ purple_notify_user_info_add_pair_html(user_info, _("Now Listening"), playing);
g_free(playing);
}
}
@@ -2352,12 +2335,12 @@ void jabber_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboole
sub = _("None");
}
- purple_notify_user_info_add_pair(user_info, _("Subscription"), sub);
+ purple_notify_user_info_add_pair_html(user_info, _("Subscription"), sub);
}
if(!PURPLE_BUDDY_IS_ONLINE(b) && jb->error_msg) {
- purple_notify_user_info_add_pair(user_info, _("Error"), jb->error_msg);
+ purple_notify_user_info_add_pair_html(user_info, _("Error"), jb->error_msg);
}
}
}
@@ -2366,21 +2349,21 @@ GList *jabber_status_types(PurpleAccount *account)
{
PurpleStatusType *type;
GList *types = NULL;
- PurpleValue *priority_value;
- PurpleValue *buzz_enabled;
+ GValue *priority_value;
+ GValue *buzz_enabled;
- priority_value = purple_value_new(PURPLE_TYPE_INT);
- purple_value_set_int(priority_value, 1);
- buzz_enabled = purple_value_new(PURPLE_TYPE_BOOLEAN);
- purple_value_set_boolean(buzz_enabled, TRUE);
+ priority_value = purple_value_new(G_TYPE_INT);
+ g_value_set_int(priority_value, 1);
+ buzz_enabled = purple_value_new(G_TYPE_BOOLEAN);
+ g_value_set_boolean(buzz_enabled, TRUE);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_ONLINE),
NULL, TRUE, TRUE, FALSE,
"priority", _("Priority"), priority_value,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
- "mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
- "moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
- "nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
+ "message", _("Message"), purple_value_new(G_TYPE_STRING),
+ "mood", _("Mood"), purple_value_new(G_TYPE_STRING),
+ "moodtext", _("Mood Text"), purple_value_new(G_TYPE_STRING),
+ "nick", _("Nickname"), purple_value_new(G_TYPE_STRING),
"buzz", _("Allow Buzz"), buzz_enabled,
NULL);
types = g_list_prepend(types, type);
@@ -2388,69 +2371,69 @@ GList *jabber_status_types(PurpleAccount *account)
type = purple_status_type_new_with_attrs(PURPLE_STATUS_MOOD,
"mood", NULL, TRUE, TRUE, TRUE,
- PURPLE_MOOD_NAME, _("Mood Name"), purple_value_new(PURPLE_TYPE_STRING),
- PURPLE_MOOD_COMMENT, _("Mood Comment"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_MOOD_NAME, _("Mood Name"), purple_value_new(G_TYPE_STRING),
+ PURPLE_MOOD_COMMENT, _("Mood Comment"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_prepend(types, type);
- priority_value = purple_value_new(PURPLE_TYPE_INT);
- purple_value_set_int(priority_value, 1);
- buzz_enabled = purple_value_new(PURPLE_TYPE_BOOLEAN);
- purple_value_set_boolean(buzz_enabled, TRUE);
+ priority_value = purple_value_new(G_TYPE_INT);
+ g_value_set_int(priority_value, 1);
+ buzz_enabled = purple_value_new(G_TYPE_BOOLEAN);
+ g_value_set_boolean(buzz_enabled, TRUE);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_CHAT),
_("Chatty"), TRUE, TRUE, FALSE,
"priority", _("Priority"), priority_value,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
- "mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
- "moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
- "nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
+ "message", _("Message"), purple_value_new(G_TYPE_STRING),
+ "mood", _("Mood"), purple_value_new(G_TYPE_STRING),
+ "moodtext", _("Mood Text"), purple_value_new(G_TYPE_STRING),
+ "nick", _("Nickname"), purple_value_new(G_TYPE_STRING),
"buzz", _("Allow Buzz"), buzz_enabled,
NULL);
types = g_list_prepend(types, type);
- priority_value = purple_value_new(PURPLE_TYPE_INT);
- purple_value_set_int(priority_value, 0);
- buzz_enabled = purple_value_new(PURPLE_TYPE_BOOLEAN);
- purple_value_set_boolean(buzz_enabled, TRUE);
+ priority_value = purple_value_new(G_TYPE_INT);
+ g_value_set_int(priority_value, 0);
+ buzz_enabled = purple_value_new(G_TYPE_BOOLEAN);
+ g_value_set_boolean(buzz_enabled, TRUE);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY,
jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_AWAY),
NULL, TRUE, TRUE, FALSE,
"priority", _("Priority"), priority_value,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
- "mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
- "moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
- "nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
+ "message", _("Message"), purple_value_new(G_TYPE_STRING),
+ "mood", _("Mood"), purple_value_new(G_TYPE_STRING),
+ "moodtext", _("Mood Text"), purple_value_new(G_TYPE_STRING),
+ "nick", _("Nickname"), purple_value_new(G_TYPE_STRING),
"buzz", _("Allow Buzz"), buzz_enabled,
NULL);
types = g_list_prepend(types, type);
- priority_value = purple_value_new(PURPLE_TYPE_INT);
- purple_value_set_int(priority_value, 0);
- buzz_enabled = purple_value_new(PURPLE_TYPE_BOOLEAN);
- purple_value_set_boolean(buzz_enabled, TRUE);
+ priority_value = purple_value_new(G_TYPE_INT);
+ g_value_set_int(priority_value, 0);
+ buzz_enabled = purple_value_new(G_TYPE_BOOLEAN);
+ g_value_set_boolean(buzz_enabled, TRUE);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_EXTENDED_AWAY,
jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_XA),
NULL, TRUE, TRUE, FALSE,
"priority", _("Priority"), priority_value,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
- "mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
- "moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
- "nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
+ "message", _("Message"), purple_value_new(G_TYPE_STRING),
+ "mood", _("Mood"), purple_value_new(G_TYPE_STRING),
+ "moodtext", _("Mood Text"), purple_value_new(G_TYPE_STRING),
+ "nick", _("Nickname"), purple_value_new(G_TYPE_STRING),
"buzz", _("Allow Buzz"), buzz_enabled,
NULL);
types = g_list_prepend(types, type);
- priority_value = purple_value_new(PURPLE_TYPE_INT);
- purple_value_set_int(priority_value, 0);
+ priority_value = purple_value_new(G_TYPE_INT);
+ g_value_set_int(priority_value, 0);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE,
jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_DND),
_("Do Not Disturb"), TRUE, TRUE, FALSE,
"priority", _("Priority"), priority_value,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
- "mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
- "moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
- "nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
+ "message", _("Message"), purple_value_new(G_TYPE_STRING),
+ "mood", _("Mood"), purple_value_new(G_TYPE_STRING),
+ "moodtext", _("Mood Text"), purple_value_new(G_TYPE_STRING),
+ "nick", _("Nickname"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_prepend(types, type);
@@ -2462,21 +2445,21 @@ GList *jabber_status_types(PurpleAccount *account)
type = purple_status_type_new_with_attrs(PURPLE_STATUS_OFFLINE,
jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_UNAVAILABLE),
NULL, TRUE, TRUE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_prepend(types, type);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_TUNE,
"tune", NULL, FALSE, TRUE, TRUE,
- PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
- PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
- PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
- PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
- PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
- PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
- PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
- PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
- PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(G_TYPE_STRING),
+ PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(G_TYPE_STRING),
+ PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(G_TYPE_STRING),
+ PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(G_TYPE_STRING),
+ PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(G_TYPE_STRING),
+ PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(G_TYPE_STRING),
+ PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(G_TYPE_INT),
+ PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(G_TYPE_INT),
+ PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_prepend(types, type);
@@ -2486,18 +2469,20 @@ GList *jabber_status_types(PurpleAccount *account)
static void
jabber_password_change_result_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
if (type == JABBER_IQ_RESULT) {
- purple_notify_info(js->gc, _("Password Changed"), _("Password Changed"),
- _("Your password has been changed."));
+ purple_notify_info(js->gc, _("Password Changed"), _("Password "
+ "Changed"), _("Your password has been changed."),
+ purple_request_cpar_from_connection(js->gc));
- purple_account_set_password(js->gc->account, (char *)data);
+ purple_account_set_password(purple_connection_get_account(js->gc), (const char *)data, NULL, NULL);
} else {
char *msg = jabber_parse_error(js, packet, NULL);
purple_notify_error(js->gc, _("Error changing password"),
- _("Error changing password"), msg);
+ _("Error changing password"), msg,
+ purple_request_cpar_from_connection(js->gc));
g_free(msg);
}
@@ -2509,26 +2494,28 @@ static void jabber_password_change_cb(JabberStream *js,
{
const char *p1, *p2;
JabberIq *iq;
- xmlnode *query, *y;
+ PurpleXmlNode *query, *y;
p1 = purple_request_fields_get_string(fields, "password1");
p2 = purple_request_fields_get_string(fields, "password2");
if(strcmp(p1, p2)) {
- purple_notify_error(js->gc, NULL, _("New passwords do not match."), NULL);
+ purple_notify_error(js->gc, NULL,
+ _("New passwords do not match."), NULL,
+ purple_request_cpar_from_connection(js->gc));
return;
}
iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:register");
- xmlnode_set_attrib(iq->node, "to", js->user->domain);
+ purple_xmlnode_set_attrib(iq->node, "to", js->user->domain);
- query = xmlnode_get_child(iq->node, "query");
+ query = purple_xmlnode_get_child(iq->node, "query");
- y = xmlnode_new_child(query, "username");
- xmlnode_insert_data(y, js->user->node, -1);
- y = xmlnode_new_child(query, "password");
- xmlnode_insert_data(y, p1, -1);
+ y = purple_xmlnode_new_child(query, "username");
+ purple_xmlnode_insert_data(y, js->user->node, -1);
+ y = purple_xmlnode_new_child(query, "password");
+ purple_xmlnode_insert_data(y, p1, -1);
jabber_iq_set_callback(iq, jabber_password_change_result_cb, g_strdup(p1));
@@ -2539,7 +2526,7 @@ static void jabber_password_change(PurplePluginAction *action)
{
PurpleConnection *gc = (PurpleConnection *) action->context;
- JabberStream *js = gc->proto_data;
+ JabberStream *js = purple_connection_get_protocol_data(gc);
PurpleRequestFields *fields;
PurpleRequestFieldGroup *group;
PurpleRequestField *field;
@@ -2564,14 +2551,13 @@ static void jabber_password_change(PurplePluginAction *action)
_("Change XMPP Password"), _("Please enter your new password"),
fields, _("OK"), G_CALLBACK(jabber_password_change_cb),
_("Cancel"), NULL,
- purple_connection_get_account(gc), NULL, NULL,
- js);
+ purple_request_cpar_from_connection(gc), js);
}
GList *jabber_actions(PurplePlugin *plugin, gpointer context)
{
PurpleConnection *gc = (PurpleConnection *) context;
- JabberStream *js = gc->proto_data;
+ JabberStream *js = purple_connection_get_protocol_data(gc);
GList *m = NULL;
PurplePluginAction *act;
@@ -2616,7 +2602,7 @@ PurpleChat *jabber_find_blist_chat(PurpleAccount *account, const char *name)
PurpleChat *chat = (PurpleChat*)cnode;
const char *room, *server;
GHashTable *components;
- if(!PURPLE_BLIST_NODE_IS_CHAT(cnode))
+ if(!PURPLE_IS_CHAT(cnode))
continue;
if (purple_chat_get_account(chat) != account)
@@ -2642,7 +2628,7 @@ PurpleChat *jabber_find_blist_chat(PurpleAccount *account, const char *name)
void jabber_convo_closed(PurpleConnection *gc, const char *who)
{
- JabberStream *js = gc->proto_data;
+ JabberStream *js = purple_connection_get_protocol_data(gc);
JabberID *jid;
JabberBuddy *jb;
JabberBuddyResource *jbr;
@@ -2663,94 +2649,94 @@ void jabber_convo_closed(PurpleConnection *gc, const char *who)
char *jabber_parse_error(JabberStream *js,
- xmlnode *packet,
+ PurpleXmlNode *packet,
PurpleConnectionError *reason)
{
- xmlnode *error;
+ PurpleXmlNode *error;
const char *code = NULL, *text = NULL;
- const char *xmlns = xmlnode_get_namespace(packet);
+ const char *xmlns = purple_xmlnode_get_namespace(packet);
char *cdata = NULL;
#define SET_REASON(x) \
if(reason != NULL) { *reason = x; }
- if((error = xmlnode_get_child(packet, "error"))) {
- xmlnode *t = xmlnode_get_child_with_namespace(error, "text", NS_XMPP_STANZAS);
+ if((error = purple_xmlnode_get_child(packet, "error"))) {
+ PurpleXmlNode *t = purple_xmlnode_get_child_with_namespace(error, "text", NS_XMPP_STANZAS);
if (t)
- cdata = xmlnode_get_data(t);
+ cdata = purple_xmlnode_get_data(t);
#if 0
- cdata = xmlnode_get_data(error);
+ cdata = purple_xmlnode_get_data(error);
#endif
- code = xmlnode_get_attrib(error, "code");
+ code = purple_xmlnode_get_attrib(error, "code");
/* Stanza errors */
- if(xmlnode_get_child(error, "bad-request")) {
+ if(purple_xmlnode_get_child(error, "bad-request")) {
text = _("Bad Request");
- } else if(xmlnode_get_child(error, "conflict")) {
+ } else if(purple_xmlnode_get_child(error, "conflict")) {
SET_REASON(PURPLE_CONNECTION_ERROR_NAME_IN_USE);
text = _("Conflict");
- } else if(xmlnode_get_child(error, "feature-not-implemented")) {
+ } else if(purple_xmlnode_get_child(error, "feature-not-implemented")) {
text = _("Feature Not Implemented");
- } else if(xmlnode_get_child(error, "forbidden")) {
+ } else if(purple_xmlnode_get_child(error, "forbidden")) {
text = _("Forbidden");
- } else if(xmlnode_get_child(error, "gone")) {
+ } else if(purple_xmlnode_get_child(error, "gone")) {
text = _("Gone");
- } else if(xmlnode_get_child(error, "internal-server-error")) {
+ } else if(purple_xmlnode_get_child(error, "internal-server-error")) {
text = _("Internal Server Error");
- } else if(xmlnode_get_child(error, "item-not-found")) {
+ } else if(purple_xmlnode_get_child(error, "item-not-found")) {
text = _("Item Not Found");
- } else if(xmlnode_get_child(error, "jid-malformed")) {
+ } else if(purple_xmlnode_get_child(error, "jid-malformed")) {
text = _("Malformed XMPP ID");
- } else if(xmlnode_get_child(error, "not-acceptable")) {
+ } else if(purple_xmlnode_get_child(error, "not-acceptable")) {
text = _("Not Acceptable");
- } else if(xmlnode_get_child(error, "not-allowed")) {
+ } else if(purple_xmlnode_get_child(error, "not-allowed")) {
text = _("Not Allowed");
- } else if(xmlnode_get_child(error, "not-authorized")) {
+ } else if(purple_xmlnode_get_child(error, "not-authorized")) {
text = _("Not Authorized");
- } else if(xmlnode_get_child(error, "payment-required")) {
+ } else if(purple_xmlnode_get_child(error, "payment-required")) {
text = _("Payment Required");
- } else if(xmlnode_get_child(error, "recipient-unavailable")) {
+ } else if(purple_xmlnode_get_child(error, "recipient-unavailable")) {
text = _("Recipient Unavailable");
- } else if(xmlnode_get_child(error, "redirect")) {
+ } else if(purple_xmlnode_get_child(error, "redirect")) {
/* XXX */
- } else if(xmlnode_get_child(error, "registration-required")) {
+ } else if(purple_xmlnode_get_child(error, "registration-required")) {
text = _("Registration Required");
- } else if(xmlnode_get_child(error, "remote-server-not-found")) {
+ } else if(purple_xmlnode_get_child(error, "remote-server-not-found")) {
text = _("Remote Server Not Found");
- } else if(xmlnode_get_child(error, "remote-server-timeout")) {
+ } else if(purple_xmlnode_get_child(error, "remote-server-timeout")) {
text = _("Remote Server Timeout");
- } else if(xmlnode_get_child(error, "resource-constraint")) {
+ } else if(purple_xmlnode_get_child(error, "resource-constraint")) {
text = _("Server Overloaded");
- } else if(xmlnode_get_child(error, "service-unavailable")) {
+ } else if(purple_xmlnode_get_child(error, "service-unavailable")) {
text = _("Service Unavailable");
- } else if(xmlnode_get_child(error, "subscription-required")) {
+ } else if(purple_xmlnode_get_child(error, "subscription-required")) {
text = _("Subscription Required");
- } else if(xmlnode_get_child(error, "unexpected-request")) {
+ } else if(purple_xmlnode_get_child(error, "unexpected-request")) {
text = _("Unexpected Request");
- } else if(xmlnode_get_child(error, "undefined-condition")) {
+ } else if(purple_xmlnode_get_child(error, "undefined-condition")) {
text = _("Unknown Error");
}
} else if(xmlns && !strcmp(xmlns, NS_XMPP_SASL)) {
/* Most common reason can be the default */
SET_REASON(PURPLE_CONNECTION_ERROR_NETWORK_ERROR);
- if(xmlnode_get_child(packet, "aborted")) {
+ if(purple_xmlnode_get_child(packet, "aborted")) {
text = _("Authorization Aborted");
- } else if(xmlnode_get_child(packet, "incorrect-encoding")) {
+ } else if(purple_xmlnode_get_child(packet, "incorrect-encoding")) {
text = _("Incorrect encoding in authorization");
- } else if(xmlnode_get_child(packet, "invalid-authzid")) {
+ } else if(purple_xmlnode_get_child(packet, "invalid-authzid")) {
text = _("Invalid authzid");
- } else if(xmlnode_get_child(packet, "invalid-mechanism")) {
+ } else if(purple_xmlnode_get_child(packet, "invalid-mechanism")) {
text = _("Invalid Authorization Mechanism");
- } else if(xmlnode_get_child(packet, "mechanism-too-weak")) {
+ } else if(purple_xmlnode_get_child(packet, "mechanism-too-weak")) {
SET_REASON(PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE);
text = _("Authorization mechanism too weak");
- } else if(xmlnode_get_child(packet, "not-authorized")) {
+ } else if(purple_xmlnode_get_child(packet, "not-authorized")) {
SET_REASON(PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED);
/* Clear the pasword if it isn't being saved */
- if (!purple_account_get_remember_password(js->gc->account))
- purple_account_set_password(js->gc->account, NULL);
+ if (!purple_account_get_remember_password(purple_connection_get_account(js->gc)))
+ purple_account_set_password(purple_connection_get_account(js->gc), NULL, NULL, NULL);
text = _("Not Authorized");
- } else if(xmlnode_get_child(packet, "temporary-auth-failure")) {
+ } else if(purple_xmlnode_get_child(packet, "temporary-auth-failure")) {
text = _("Temporary Authentication Failure");
} else {
SET_REASON(PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED);
@@ -2761,54 +2747,54 @@ char *jabber_parse_error(JabberStream *js,
!strcmp(xmlns, NS_XMPP_STREAMS))) {
/* Most common reason as default: */
SET_REASON(PURPLE_CONNECTION_ERROR_NETWORK_ERROR);
- if(xmlnode_get_child(packet, "bad-format")) {
+ if(purple_xmlnode_get_child(packet, "bad-format")) {
text = _("Bad Format");
- } else if(xmlnode_get_child(packet, "bad-namespace-prefix")) {
+ } else if(purple_xmlnode_get_child(packet, "bad-namespace-prefix")) {
text = _("Bad Namespace Prefix");
- } else if(xmlnode_get_child(packet, "conflict")) {
+ } else if(purple_xmlnode_get_child(packet, "conflict")) {
SET_REASON(PURPLE_CONNECTION_ERROR_NAME_IN_USE);
text = _("Resource Conflict");
- } else if(xmlnode_get_child(packet, "connection-timeout")) {
+ } else if(purple_xmlnode_get_child(packet, "connection-timeout")) {
text = _("Connection Timeout");
- } else if(xmlnode_get_child(packet, "host-gone")) {
+ } else if(purple_xmlnode_get_child(packet, "host-gone")) {
text = _("Host Gone");
- } else if(xmlnode_get_child(packet, "host-unknown")) {
+ } else if(purple_xmlnode_get_child(packet, "host-unknown")) {
text = _("Host Unknown");
- } else if(xmlnode_get_child(packet, "improper-addressing")) {
+ } else if(purple_xmlnode_get_child(packet, "improper-addressing")) {
text = _("Improper Addressing");
- } else if(xmlnode_get_child(packet, "internal-server-error")) {
+ } else if(purple_xmlnode_get_child(packet, "internal-server-error")) {
text = _("Internal Server Error");
- } else if(xmlnode_get_child(packet, "invalid-id")) {
+ } else if(purple_xmlnode_get_child(packet, "invalid-id")) {
text = _("Invalid ID");
- } else if(xmlnode_get_child(packet, "invalid-namespace")) {
+ } else if(purple_xmlnode_get_child(packet, "invalid-namespace")) {
text = _("Invalid Namespace");
- } else if(xmlnode_get_child(packet, "invalid-xml")) {
+ } else if(purple_xmlnode_get_child(packet, "invalid-xml")) {
text = _("Invalid XML");
- } else if(xmlnode_get_child(packet, "nonmatching-hosts")) {
+ } else if(purple_xmlnode_get_child(packet, "nonmatching-hosts")) {
text = _("Non-matching Hosts");
- } else if(xmlnode_get_child(packet, "not-authorized")) {
+ } else if(purple_xmlnode_get_child(packet, "not-authorized")) {
text = _("Not Authorized");
- } else if(xmlnode_get_child(packet, "policy-violation")) {
+ } else if(purple_xmlnode_get_child(packet, "policy-violation")) {
text = _("Policy Violation");
- } else if(xmlnode_get_child(packet, "remote-connection-failed")) {
+ } else if(purple_xmlnode_get_child(packet, "remote-connection-failed")) {
text = _("Remote Connection Failed");
- } else if(xmlnode_get_child(packet, "resource-constraint")) {
+ } else if(purple_xmlnode_get_child(packet, "resource-constraint")) {
text = _("Resource Constraint");
- } else if(xmlnode_get_child(packet, "restricted-xml")) {
+ } else if(purple_xmlnode_get_child(packet, "restricted-xml")) {
text = _("Restricted XML");
- } else if(xmlnode_get_child(packet, "see-other-host")) {
+ } else if(purple_xmlnode_get_child(packet, "see-other-host")) {
text = _("See Other Host");
- } else if(xmlnode_get_child(packet, "system-shutdown")) {
+ } else if(purple_xmlnode_get_child(packet, "system-shutdown")) {
text = _("System Shutdown");
- } else if(xmlnode_get_child(packet, "undefined-condition")) {
+ } else if(purple_xmlnode_get_child(packet, "undefined-condition")) {
text = _("Undefined Condition");
- } else if(xmlnode_get_child(packet, "unsupported-encoding")) {
+ } else if(purple_xmlnode_get_child(packet, "unsupported-encoding")) {
text = _("Unsupported Encoding");
- } else if(xmlnode_get_child(packet, "unsupported-stanza-type")) {
+ } else if(purple_xmlnode_get_child(packet, "unsupported-stanza-type")) {
text = _("Unsupported Stanza Type");
- } else if(xmlnode_get_child(packet, "unsupported-version")) {
+ } else if(purple_xmlnode_get_child(packet, "unsupported-version")) {
text = _("Unsupported Version");
- } else if(xmlnode_get_child(packet, "xml-not-well-formed")) {
+ } else if(purple_xmlnode_get_child(packet, "xml-not-well-formed")) {
text = _("XML Not Well Formed");
} else {
text = _("Stream Error");
@@ -2830,7 +2816,7 @@ char *jabber_parse_error(JabberStream *js,
static PurpleCmdRet jabber_cmd_chat_config(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
- JabberChat *chat = jabber_chat_find_by_conv(conv);
+ JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv));
if (!chat)
return PURPLE_CMD_RET_FAILED;
@@ -2842,7 +2828,7 @@ static PurpleCmdRet jabber_cmd_chat_config(PurpleConversation *conv,
static PurpleCmdRet jabber_cmd_chat_register(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
- JabberChat *chat = jabber_chat_find_by_conv(conv);
+ JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv));
if (!chat)
return PURPLE_CMD_RET_FAILED;
@@ -2854,7 +2840,7 @@ static PurpleCmdRet jabber_cmd_chat_register(PurpleConversation *conv,
static PurpleCmdRet jabber_cmd_chat_topic(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
- JabberChat *chat = jabber_chat_find_by_conv(conv);
+ JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv));
if (!chat)
return PURPLE_CMD_RET_FAILED;
@@ -2862,7 +2848,7 @@ static PurpleCmdRet jabber_cmd_chat_topic(PurpleConversation *conv,
if (args && args[0] && *args[0])
jabber_chat_change_topic(chat, args[0]);
else {
- const char *cur = purple_conv_chat_get_topic(PURPLE_CONV_CHAT(conv));
+ const char *cur = purple_chat_conversation_get_topic(PURPLE_CHAT_CONVERSATION(conv));
char *buf, *tmp, *tmp2;
if (cur) {
@@ -2873,8 +2859,7 @@ static PurpleCmdRet jabber_cmd_chat_topic(PurpleConversation *conv,
g_free(tmp2);
} else
buf = g_strdup(_("No topic is set"));
- purple_conv_chat_write(PURPLE_CONV_CHAT(conv), "", buf,
- PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(conv, buf, PURPLE_MESSAGE_NO_LOG);
g_free(buf);
}
@@ -2884,7 +2869,7 @@ static PurpleCmdRet jabber_cmd_chat_topic(PurpleConversation *conv,
static PurpleCmdRet jabber_cmd_chat_nick(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
- JabberChat *chat = jabber_chat_find_by_conv(conv);
+ JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv));
if(!chat || !args || !args[0])
return PURPLE_CMD_RET_FAILED;
@@ -2903,7 +2888,7 @@ static PurpleCmdRet jabber_cmd_chat_nick(PurpleConversation *conv,
static PurpleCmdRet jabber_cmd_chat_part(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
- JabberChat *chat = jabber_chat_find_by_conv(conv);
+ JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv));
if (!chat)
return PURPLE_CMD_RET_FAILED;
@@ -2915,7 +2900,7 @@ static PurpleCmdRet jabber_cmd_chat_part(PurpleConversation *conv,
static PurpleCmdRet jabber_cmd_chat_ban(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
- JabberChat *chat = jabber_chat_find_by_conv(conv);
+ JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv));
if(!chat || !args || !args[0])
return PURPLE_CMD_RET_FAILED;
@@ -2931,7 +2916,7 @@ static PurpleCmdRet jabber_cmd_chat_ban(PurpleConversation *conv,
static PurpleCmdRet jabber_cmd_chat_affiliate(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
- JabberChat *chat = jabber_chat_find_by_conv(conv);
+ JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv));
if (!chat || !args || !args[0])
return PURPLE_CMD_RET_FAILED;
@@ -2967,7 +2952,7 @@ static PurpleCmdRet jabber_cmd_chat_affiliate(PurpleConversation *conv,
static PurpleCmdRet jabber_cmd_chat_role(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
- JabberChat *chat = jabber_chat_find_by_conv(conv);
+ JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv));
if (!chat || !args || !args[0])
return PURPLE_CMD_RET_FAILED;
@@ -3005,8 +2990,8 @@ static PurpleCmdRet jabber_cmd_chat_invite(PurpleConversation *conv,
if(!args || !args[0])
return PURPLE_CMD_RET_FAILED;
- jabber_chat_invite(purple_conversation_get_gc(conv),
- purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), args[1] ? args[1] : "",
+ jabber_chat_invite(purple_conversation_get_connection(conv),
+ purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv)), args[1] ? args[1] : "",
args[0]);
return PURPLE_CMD_RET_OK;
@@ -3015,7 +3000,7 @@ static PurpleCmdRet jabber_cmd_chat_invite(PurpleConversation *conv,
static PurpleCmdRet jabber_cmd_chat_join(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
- JabberChat *chat = jabber_chat_find_by_conv(conv);
+ JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv));
GHashTable *components;
JabberID *jid = NULL;
const char *room = NULL, *server = NULL, *handle = NULL;
@@ -3052,7 +3037,7 @@ static PurpleCmdRet jabber_cmd_chat_join(PurpleConversation *conv,
if (args[1])
g_hash_table_insert(components, "password", args[1]);
- jabber_chat_join(purple_conversation_get_gc(conv), components);
+ jabber_chat_join(purple_conversation_get_connection(conv), components);
g_hash_table_destroy(components);
jabber_id_free(jid);
@@ -3062,7 +3047,7 @@ static PurpleCmdRet jabber_cmd_chat_join(PurpleConversation *conv,
static PurpleCmdRet jabber_cmd_chat_kick(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
- JabberChat *chat = jabber_chat_find_by_conv(conv);
+ JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv));
if(!chat || !args || !args[0])
return PURPLE_CMD_RET_FAILED;
@@ -3078,7 +3063,7 @@ static PurpleCmdRet jabber_cmd_chat_kick(PurpleConversation *conv,
static PurpleCmdRet jabber_cmd_chat_msg(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
- JabberChat *chat = jabber_chat_find_by_conv(conv);
+ JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv));
char *who;
if (!chat)
@@ -3086,7 +3071,8 @@ static PurpleCmdRet jabber_cmd_chat_msg(PurpleConversation *conv,
who = g_strdup_printf("%s@%s/%s", chat->room, chat->server, args[0]);
- jabber_message_send_im(purple_conversation_get_gc(conv), who, args[1], 0);
+ jabber_message_send_im(purple_conversation_get_connection(conv),
+ purple_message_new_outgoing(who, args[1], 0));
g_free(who);
return PURPLE_CMD_RET_OK;
@@ -3118,7 +3104,7 @@ static gboolean _jabber_send_buzz(JabberStream *js, const char *username, char *
JabberBuddyResource *jbr;
PurpleConnection *gc = js->gc;
PurpleBuddy *buddy =
- purple_find_buddy(purple_connection_get_account(gc), username);
+ purple_blist_find_buddy(purple_connection_get_account(gc), username);
const gchar *alias =
buddy ? purple_buddy_get_contact_alias(buddy) : username;
@@ -3140,21 +3126,21 @@ static gboolean _jabber_send_buzz(JabberStream *js, const char *username, char *
}
if (jabber_resource_has_capability(jbr, NS_ATTENTION)) {
- xmlnode *buzz, *msg = xmlnode_new("message");
+ PurpleXmlNode *buzz, *msg = purple_xmlnode_new("message");
gchar *to;
to = g_strdup_printf("%s/%s", username, jbr->name);
- xmlnode_set_attrib(msg, "to", to);
+ purple_xmlnode_set_attrib(msg, "to", to);
g_free(to);
/* avoid offline storage */
- xmlnode_set_attrib(msg, "type", "headline");
+ purple_xmlnode_set_attrib(msg, "type", "headline");
- buzz = xmlnode_new_child(msg, "attention");
- xmlnode_set_namespace(buzz, NS_ATTENTION);
+ buzz = purple_xmlnode_new_child(msg, "attention");
+ purple_xmlnode_set_namespace(buzz, NS_ATTENTION);
jabber_send(js, msg);
- xmlnode_free(msg);
+ purple_xmlnode_free(msg);
return TRUE;
} else {
@@ -3167,17 +3153,18 @@ static gboolean _jabber_send_buzz(JabberStream *js, const char *username, char *
static PurpleCmdRet jabber_cmd_buzz(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
- JabberStream *js = conv->account->gc->proto_data;
+ PurpleAccount *account = purple_conversation_get_account(conv);
+ JabberStream *js = purple_connection_get_protocol_data(purple_account_get_connection(account));
const gchar *who;
gchar *description;
PurpleBuddy *buddy;
const char *alias;
PurpleAttentionType *attn =
- purple_get_attention_type_from_code(conv->account, 0);
+ purple_get_attention_type_from_code(account, 0);
if (!args || !args[0]) {
/* use the buddy from conversation, if it's a one-to-one conversation */
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
who = purple_conversation_get_name(conv);
} else {
return PURPLE_CMD_RET_FAILED;
@@ -3186,7 +3173,7 @@ static PurpleCmdRet jabber_cmd_buzz(PurpleConversation *conv,
who = args[0];
}
- buddy = purple_find_buddy(conv->account, who);
+ buddy = purple_blist_find_buddy(account, who);
if (buddy != NULL)
alias = purple_buddy_get_contact_alias(buddy);
else
@@ -3194,8 +3181,8 @@ static PurpleCmdRet jabber_cmd_buzz(PurpleConversation *conv,
description =
g_strdup_printf(purple_attention_type_get_outgoing_desc(attn), alias);
- purple_conversation_write(conv, NULL, description,
- PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(conv, description,
+ PURPLE_MESSAGE_NOTIFY);
g_free(description);
return _jabber_send_buzz(js, who, error) ? PURPLE_CMD_RET_OK : PURPLE_CMD_RET_FAILED;
}
@@ -3214,18 +3201,18 @@ GList *jabber_attention_types(PurpleAccount *account)
gboolean jabber_send_attention(PurpleConnection *gc, const char *username, guint code)
{
- JabberStream *js = gc->proto_data;
+ JabberStream *js = purple_connection_get_protocol_data(gc);
gchar *error = NULL;
if (!_jabber_send_buzz(js, username, &error)) {
PurpleAccount *account = purple_connection_get_account(gc);
PurpleConversation *conv =
- purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, username, account);
+ purple_conversations_find_with_account(username, account);
purple_debug_error("jabber", "jabber_send_attention: jabber_cmd_buzz failed with error: %s\n", error ? error : "(NULL)");
if (conv) {
- purple_conversation_write(conv, username, error, PURPLE_MESSAGE_ERROR,
- time(NULL));
+ purple_conversation_write_system_message(conv,
+ error, PURPLE_MESSAGE_ERROR);
}
g_free(error);
@@ -3280,10 +3267,8 @@ jabber_media_ok_cb(JabberMediaRequest *request, PurpleRequestFields *fields)
{
PurpleRequestField *field =
purple_request_fields_get_field(fields, "resource");
- int selected_id = purple_request_field_choice_get_value(field);
- GList *labels = purple_request_field_choice_get_labels(field);
- gchar *who = g_strdup_printf("%s/%s", request->who,
- (gchar*)g_list_nth_data(labels, selected_id));
+ const gchar *selected = purple_request_field_choice_get_value(field);
+ gchar *who = g_strdup_printf("%s/%s", request->who, selected);
jabber_initiate_media(request->account, who, request->type);
g_free(who);
@@ -3297,8 +3282,8 @@ jabber_initiate_media(PurpleAccount *account, const char *who,
PurpleMediaSessionType type)
{
#ifdef USE_VV
- JabberStream *js = (JabberStream *)
- purple_account_get_connection(account)->proto_data;
+ PurpleConnection *gc = purple_account_get_connection(account);
+ JabberStream *js = purple_connection_get_protocol_data(gc);
JabberBuddy *jb;
JabberBuddyResource *jbr = NULL;
char *resource = NULL;
@@ -3309,7 +3294,6 @@ jabber_initiate_media(PurpleAccount *account, const char *who,
return FALSE;
}
-
jb = jabber_buddy_find(js, who, FALSE);
if(!jb || !jb->resources ||
@@ -3331,7 +3315,8 @@ jabber_initiate_media(PurpleAccount *account, const char *who,
}
purple_notify_error(account, _("Media Initiation Failed"),
- _("Media Initiation Failed"), msg);
+ _("Media Initiation Failed"), msg,
+ purple_request_cpar_from_connection(gc));
g_free(msg);
g_free(resource);
return FALSE;
@@ -3369,6 +3354,8 @@ jabber_initiate_media(PurpleAccount *account, const char *who,
PurpleRequestFieldGroup *group;
JabberMediaRequest *request;
+ purple_request_field_choice_set_data_destructor(field, g_free);
+
for(l = jb->resources; l; l = l->next)
{
JabberBuddyResource *ljbr = l->data;
@@ -3382,19 +3369,19 @@ jabber_initiate_media(PurpleAccount *account, const char *who,
(type & PURPLE_MEDIA_VIDEO)) {
if (caps & PURPLE_MEDIA_CAPS_AUDIO_VIDEO) {
jbr = ljbr;
- purple_request_field_choice_add(
- field, jbr->name);
+ purple_request_field_choice_add(field,
+ jbr->name, g_strdup(jbr->name));
}
} else if (type & (PURPLE_MEDIA_AUDIO) &&
(caps & PURPLE_MEDIA_CAPS_AUDIO)) {
jbr = ljbr;
- purple_request_field_choice_add(
- field, jbr->name);
+ purple_request_field_choice_add(field,
+ jbr->name, g_strdup(jbr->name));
}else if (type & (PURPLE_MEDIA_VIDEO) &&
(caps & PURPLE_MEDIA_CAPS_VIDEO)) {
jbr = ljbr;
- purple_request_field_choice_add(
- field, jbr->name);
+ purple_request_field_choice_add(field,
+ jbr->name, g_strdup(jbr->name));
}
}
@@ -3404,8 +3391,8 @@ jabber_initiate_media(PurpleAccount *account, const char *who,
return FALSE;
}
- if (g_list_length(purple_request_field_choice_get_labels(
- field)) <= 1) {
+ if (g_list_length(purple_request_field_choice_get_elements(
+ field)) <= 2) {
gchar *name;
gboolean result;
purple_request_field_destroy(field);
@@ -3429,7 +3416,8 @@ jabber_initiate_media(PurpleAccount *account, const char *who,
NULL, fields, _("Initiate Media"),
G_CALLBACK(jabber_media_ok_cb), _("Cancel"),
G_CALLBACK(jabber_media_cancel_cb),
- account, who, NULL, request);
+ purple_request_cpar_from_account(account),
+ request);
g_free(msg);
return TRUE;
@@ -3441,8 +3429,8 @@ jabber_initiate_media(PurpleAccount *account, const char *who,
PurpleMediaCaps jabber_get_media_caps(PurpleAccount *account, const char *who)
{
#ifdef USE_VV
- JabberStream *js = (JabberStream *)
- purple_account_get_connection(account)->proto_data;
+ PurpleConnection *gc = purple_account_get_connection(account);
+ JabberStream *js = purple_connection_get_protocol_data(gc);
JabberBuddy *jb;
JabberBuddyResource *jbr;
PurpleMediaCaps total = PURPLE_MEDIA_CAPS_NONE;
@@ -3530,7 +3518,7 @@ PurpleMediaCaps jabber_get_media_caps(PurpleAccount *account, const char *who)
gboolean jabber_can_receive_file(PurpleConnection *gc, const char *who)
{
- JabberStream *js = gc->proto_data;
+ JabberStream *js = purple_connection_get_protocol_data(gc);
if (js) {
JabberBuddy *jb = jabber_buddy_find(js, who, FALSE);
@@ -3580,148 +3568,157 @@ static PurpleCmdRet
jabber_cmd_mood(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
- JabberStream *js = conv->account->gc->proto_data;
+ PurpleAccount *account = purple_conversation_get_account(conv);
+ JabberStream *js = purple_connection_get_protocol_data(purple_account_get_connection(account));
if (js->pep) {
- /* if no argument was given, unset mood */
+ gboolean ret;
+
if (!args || !args[0]) {
- jabber_mood_set(js, NULL, NULL);
- } else if (!args[1]) {
- jabber_mood_set(js, args[0], NULL);
+ /* No arguments; unset mood */
+ ret = jabber_mood_set(js, NULL, NULL);
} else {
- jabber_mood_set(js, args[0], args[1]);
+ /* At least one argument. Relying on the list of arguments
+ * being NULL-terminated.
+ */
+ ret = jabber_mood_set(js, args[0], args[1]);
+ if (!ret) {
+ /* Let's try again */
+ char *tmp = g_strjoin(" ", args[0], args[1], NULL);
+ ret = jabber_mood_set(js, "undefined", tmp);
+ g_free(tmp);
+ }
}
- return PURPLE_CMD_RET_OK;
+ if (ret) {
+ return PURPLE_CMD_RET_OK;
+ } else {
+ purple_conversation_write_system_message(conv,
+ _("Failed to specify mood"),
+ PURPLE_MESSAGE_ERROR);
+ return PURPLE_CMD_RET_FAILED;
+ }
} else {
/* account does not support PEP, can't set a mood */
- purple_conversation_write(conv, NULL,
+ purple_conversation_write_system_message(conv,
_("Account does not support PEP, can't set mood"),
- PURPLE_MESSAGE_ERROR, time(NULL));
+ PURPLE_MESSAGE_ERROR);
return PURPLE_CMD_RET_FAILED;
}
}
-static void jabber_register_commands(PurplePlugin *plugin)
+static void
+jabber_register_commands(PurplePlugin *plugin)
{
GSList *commands = NULL;
PurpleCmdId id;
+ const gchar *plid = purple_plugin_get_id(plugin);
+
id = purple_cmd_register("config", "", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
- "prpl-jabber", jabber_cmd_chat_config,
- _("config: Configure a chat room."), NULL);
+ PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, plid,
+ jabber_cmd_chat_config, _("config: Configure a chat room."),
+ NULL);
commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
id = purple_cmd_register("configure", "", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
- "prpl-jabber", jabber_cmd_chat_config,
- _("configure: Configure a chat room."), NULL);
+ PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, plid,
+ jabber_cmd_chat_config, _("configure: Configure a chat room."),
+ NULL);
commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
id = purple_cmd_register("nick", "s", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
- "prpl-jabber", jabber_cmd_chat_nick,
- _("nick &lt;new nickname&gt;: Change your nickname."),
- NULL);
+ PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, plid,
+ jabber_cmd_chat_nick, _("nick &lt;new nickname&gt;: "
+ "Change your nickname."), NULL);
commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
id = purple_cmd_register("part", "s", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber",
- jabber_cmd_chat_part, _("part [message]: Leave the room."),
- NULL);
+ PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, plid, jabber_cmd_chat_part,
+ _("part [message]: Leave the room."), NULL);
commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
id = purple_cmd_register("register", "", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
- "prpl-jabber", jabber_cmd_chat_register,
- _("register: Register with a chat room."), NULL);
+ PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, plid,
+ jabber_cmd_chat_register,
+ _("register: Register with a chat room."), NULL);
commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
/* XXX: there needs to be a core /topic cmd, methinks */
id = purple_cmd_register("topic", "s", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber",
- jabber_cmd_chat_topic,
- _("topic [new topic]: View or change the topic."),
- NULL);
+ PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, plid, jabber_cmd_chat_topic,
+ _("topic [new topic]: View or change the topic."), NULL);
commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
id = purple_cmd_register("ban", "ws", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber",
- jabber_cmd_chat_ban,
- _("ban &lt;user&gt; [reason]: Ban a user from the room."),
- NULL);
+ PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, plid, jabber_cmd_chat_ban,
+ _("ban &lt;user&gt; [reason]: Ban a user from the room."),
+ NULL);
commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
id = purple_cmd_register("affiliate", "ws", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber",
- jabber_cmd_chat_affiliate,
- _("affiliate &lt;owner|admin|member|outcast|none&gt; [nick1] [nick2] ...: Get the users with an affiliation or set users' affiliation with the room."),
- NULL);
+ PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, plid,
+ jabber_cmd_chat_affiliate, _("affiliate "
+ "&lt;owner|admin|member|outcast|none&gt; [nick1] [nick2] ...: "
+ "Get the users with an affiliation or set users' affiliation "
+ "with the room."), NULL);
commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
id = purple_cmd_register("role", "ws", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber",
- jabber_cmd_chat_role,
- _("role &lt;moderator|participant|visitor|none&gt; [nick1] [nick2] ...: Get the users with a role or set users' role with the room."),
- NULL);
+ PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, plid, jabber_cmd_chat_role,
+ _("role &lt;moderator|participant|visitor|none&gt; [nick1] "
+ "[nick2] ...: Get the users with a role or set users' role "
+ "with the room."), NULL);
commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
id = purple_cmd_register("invite", "ws", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber",
- jabber_cmd_chat_invite,
- _("invite &lt;user&gt; [message]: Invite a user to the room."),
- NULL);
+ PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, plid, jabber_cmd_chat_invite,
+ _("invite &lt;user&gt; [message]: Invite a user to the room."),
+ NULL);
commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
id = purple_cmd_register("join", "ws", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber",
- jabber_cmd_chat_join,
- _("join: &lt;room[@server]&gt; [password]: Join a chat."),
- NULL);
+ PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, plid, jabber_cmd_chat_join,
+ _("join: &lt;room[@server]&gt; [password]: Join a chat."),
+ NULL);
commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
id = purple_cmd_register("kick", "ws", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber",
- jabber_cmd_chat_kick,
- _("kick &lt;user&gt; [reason]: Kick a user from the room."),
- NULL);
+ PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, plid, jabber_cmd_chat_kick,
+ _("kick &lt;user&gt; [reason]: Kick a user from the room."),
+ NULL);
commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
id = purple_cmd_register("msg", "ws", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
- "prpl-jabber", jabber_cmd_chat_msg,
- _("msg &lt;user&gt; &lt;message&gt;: Send a private message to another user."),
- NULL);
+ PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, plid,
+ jabber_cmd_chat_msg, _("msg &lt;user&gt; &lt;message&gt;: "
+ "Send a private message to another user."), NULL);
commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
id = purple_cmd_register("ping", "w", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM |
- PURPLE_CMD_FLAG_PRPL_ONLY,
- "prpl-jabber", jabber_cmd_ping,
- _("ping &lt;jid&gt;: Ping a user/component/server."),
- NULL);
+ PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM |
+ PURPLE_CMD_FLAG_PRPL_ONLY, plid, jabber_cmd_ping,
+ _("ping &lt;jid&gt;: Ping a user/component/server."), NULL);
commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
id = purple_cmd_register("buzz", "w", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
- "prpl-jabber", jabber_cmd_buzz,
- _("buzz: Buzz a user to get their attention"), NULL);
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, plid, jabber_cmd_buzz,
+ _("buzz: Buzz a user to get their attention"), NULL);
commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
id = purple_cmd_register("mood", "ws", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM |
- PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
- "prpl-jabber", jabber_cmd_mood,
- _("mood: Set current user mood"), NULL);
+ PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM |
+ PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
+ plid, jabber_cmd_mood,
+ _("mood &lt;mood&gt; [text]: Set current user mood"), NULL);
commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
g_hash_table_insert(jabber_cmds, plugin, commands);
@@ -3764,7 +3761,7 @@ jabber_ipc_contact_has_feature(PurpleAccount *account, const gchar *jid,
if (!purple_account_is_connected(account))
return FALSE;
- js = gc->proto_data;
+ js = purple_connection_get_protocol_data(gc);
if (!(resource = jabber_get_resource(jid)) ||
!(jb = jabber_buddy_find(js, jid, FALSE)) ||
@@ -3812,7 +3809,7 @@ jabber_do_init(void)
if (!sasl_initialized) {
sasl_initialized = TRUE;
#ifdef _WIN32
- sasldir = g_build_filename(wpurple_install_dir(), "sasl2", NULL);
+ sasldir = g_build_filename(wpurple_bin_dir(), "sasl2", NULL);
sasl_set_path(SASL_PATH_TYPE_PLUGIN, sasldir);
g_free(sasldir);
/* Suppress error popups for failing to load sasl plugins */
@@ -3948,41 +3945,38 @@ void jabber_plugin_init(PurplePlugin *plugin)
/* IPC functions */
purple_plugin_ipc_register(plugin, "contact_has_feature", PURPLE_CALLBACK(jabber_ipc_contact_has_feature),
purple_marshal_BOOLEAN__POINTER_POINTER_POINTER,
- purple_value_new(PURPLE_TYPE_BOOLEAN), 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_STRING));
+ G_TYPE_BOOLEAN, 3,
+ PURPLE_TYPE_ACCOUNT, G_TYPE_STRING, G_TYPE_STRING);
purple_plugin_ipc_register(plugin, "add_feature", PURPLE_CALLBACK(jabber_ipc_add_feature),
purple_marshal_VOID__POINTER,
- NULL, 1,
- purple_value_new(PURPLE_TYPE_STRING));
+ G_TYPE_NONE, 1, G_TYPE_STRING);
purple_plugin_ipc_register(plugin, "register_namespace_watcher",
PURPLE_CALLBACK(jabber_iq_signal_register),
purple_marshal_VOID__POINTER_POINTER,
- NULL, 2,
- purple_value_new(PURPLE_TYPE_STRING), /* node */
- purple_value_new(PURPLE_TYPE_STRING)); /* namespace */
+ G_TYPE_NONE, 2,
+ G_TYPE_STRING, /* node */
+ G_TYPE_STRING); /* namespace */
purple_plugin_ipc_register(plugin, "unregister_namespace_watcher",
PURPLE_CALLBACK(jabber_iq_signal_unregister),
purple_marshal_VOID__POINTER_POINTER,
- NULL, 2,
- purple_value_new(PURPLE_TYPE_STRING), /* node */
- purple_value_new(PURPLE_TYPE_STRING)); /* namespace */
+ G_TYPE_NONE, 2,
+ G_TYPE_STRING, /* node */
+ G_TYPE_STRING); /* namespace */
purple_signal_register(plugin, "jabber-register-namespace-watcher",
purple_marshal_VOID__POINTER_POINTER,
- NULL, 2,
- purple_value_new(PURPLE_TYPE_STRING), /* node */
- purple_value_new(PURPLE_TYPE_STRING)); /* namespace */
+ G_TYPE_NONE, 2,
+ G_TYPE_STRING, /* node */
+ G_TYPE_STRING); /* namespace */
purple_signal_register(plugin, "jabber-unregister-namespace-watcher",
purple_marshal_VOID__POINTER_POINTER,
- NULL, 2,
- purple_value_new(PURPLE_TYPE_STRING), /* node */
- purple_value_new(PURPLE_TYPE_STRING)); /* namespace */
+ G_TYPE_NONE, 2,
+ G_TYPE_STRING, /* node */
+ G_TYPE_STRING); /* namespace */
purple_signal_connect(plugin, "jabber-register-namespace-watcher",
plugin, PURPLE_CALLBACK(jabber_iq_signal_register), NULL);
@@ -3991,14 +3985,14 @@ void jabber_plugin_init(PurplePlugin *plugin)
purple_signal_register(plugin, "jabber-receiving-xmlnode",
- purple_marshal_VOID__POINTER_POINTER, NULL, 2,
- purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
- purple_value_new_outgoing(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ PURPLE_TYPE_CONNECTION,
+ G_TYPE_POINTER); /* pointer to a PurpleXmlNode* */
purple_signal_register(plugin, "jabber-sending-xmlnode",
- purple_marshal_VOID__POINTER_POINTER, NULL, 2,
- purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
- purple_value_new_outgoing(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ PURPLE_TYPE_CONNECTION,
+ G_TYPE_POINTER); /* pointer to a PurpleXmlNode* */
/*
* Do not remove this or the plugin will fail. Completely. You have been
@@ -4009,45 +4003,45 @@ void jabber_plugin_init(PurplePlugin *plugin)
NULL, PURPLE_SIGNAL_PRIORITY_HIGHEST);
purple_signal_register(plugin, "jabber-sending-text",
- purple_marshal_VOID__POINTER_POINTER, NULL, 2,
- purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
- purple_value_new_outgoing(PURPLE_TYPE_STRING));
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ PURPLE_TYPE_CONNECTION,
+ G_TYPE_POINTER); /* pointer to a string */
purple_signal_register(plugin, "jabber-receiving-message",
purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER_POINTER,
- purple_value_new(PURPLE_TYPE_BOOLEAN), 6,
- purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
- purple_value_new(PURPLE_TYPE_STRING), /* type */
- purple_value_new(PURPLE_TYPE_STRING), /* id */
- purple_value_new(PURPLE_TYPE_STRING), /* from */
- purple_value_new(PURPLE_TYPE_STRING), /* to */
- purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));
+ G_TYPE_BOOLEAN, 6,
+ PURPLE_TYPE_CONNECTION,
+ G_TYPE_STRING, /* type */
+ G_TYPE_STRING, /* id */
+ G_TYPE_STRING, /* from */
+ G_TYPE_STRING, /* to */
+ PURPLE_TYPE_XMLNODE);
purple_signal_register(plugin, "jabber-receiving-iq",
purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
- purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
- purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
- purple_value_new(PURPLE_TYPE_STRING), /* type */
- purple_value_new(PURPLE_TYPE_STRING), /* id */
- purple_value_new(PURPLE_TYPE_STRING), /* from */
- purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));
+ G_TYPE_BOOLEAN, 5,
+ PURPLE_TYPE_CONNECTION,
+ G_TYPE_STRING, /* type */
+ G_TYPE_STRING, /* id */
+ G_TYPE_STRING, /* from */
+ PURPLE_TYPE_XMLNODE);
purple_signal_register(plugin, "jabber-watched-iq",
purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
- purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
- purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
- purple_value_new(PURPLE_TYPE_STRING), /* type */
- purple_value_new(PURPLE_TYPE_STRING), /* id */
- purple_value_new(PURPLE_TYPE_STRING), /* from */
- purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); /* child */
+ G_TYPE_BOOLEAN, 5,
+ PURPLE_TYPE_CONNECTION,
+ G_TYPE_STRING, /* type */
+ G_TYPE_STRING, /* id */
+ G_TYPE_STRING, /* from */
+ PURPLE_TYPE_XMLNODE); /* child */
purple_signal_register(plugin, "jabber-receiving-presence",
purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER,
- purple_value_new(PURPLE_TYPE_BOOLEAN), 4,
- purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
- purple_value_new(PURPLE_TYPE_STRING), /* type */
- purple_value_new(PURPLE_TYPE_STRING), /* from */
- purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));
+ G_TYPE_BOOLEAN, 4,
+ PURPLE_TYPE_CONNECTION,
+ G_TYPE_STRING, /* type */
+ G_TYPE_STRING, /* from */
+ PURPLE_TYPE_XMLNODE);
}
void jabber_plugin_uninit(PurplePlugin *plugin)
diff --git a/libpurple/protocols/jabber/jabber.h b/libpurple/protocols/jabber/jabber.h
index e90b06bf9e..dda2c1c1e7 100644
--- a/libpurple/protocols/jabber/jabber.h
+++ b/libpurple/protocols/jabber/jabber.h
@@ -37,7 +37,7 @@ typedef enum {
JABBER_CAP_IQ_REGISTER = 1 << 8,
/* Google Talk extensions:
- * http://code.google.com/apis/talk/jep_extensions/extensions.html
+ * https://developers.google.com/talk/jep_extensions/extensions
*/
JABBER_CAP_GMAIL_NOTIFY = 1 << 9,
JABBER_CAP_GOOGLE_ROSTER = 1 << 10,
@@ -58,10 +58,11 @@ typedef struct _JabberStream JabberStream;
#include <libxml/parser.h>
#include <glib.h>
-#include "circbuffer.h"
+#include "circularbuffer.h"
#include "connection.h"
#include "dnsquery.h"
#include "dnssrv.h"
+#include "http.h"
#include "media.h"
#include "mediamanager.h"
#include "roomlist.h"
@@ -80,7 +81,7 @@ typedef struct _JabberStream JabberStream;
#include <sasl/sasl.h>
#endif
-#define CAPS0115_NODE "http://pidgin.im/"
+#define CAPS0115_NODE "https://pidgin.im/"
#define JABBER_DEFAULT_REQUIRE_TLS "require_starttls"
#define JABBER_DEFAULT_FT_PROXIES "proxy.eu.jabber.org"
@@ -101,11 +102,12 @@ typedef enum {
struct _JabberStream
{
int fd;
+ guint inpa;
PurpleSrvTxtQueryData *srv_query_data;
xmlParserCtxt *context;
- xmlnode *current;
+ PurpleXmlNode *current;
struct {
guint8 major;
@@ -151,9 +153,9 @@ struct _JabberStream
* when we receive a roster push.
*
* See these bug reports:
- * http://trac.adiumx.com/ticket/8834
- * http://developer.pidgin.im/ticket/5484
- * http://developer.pidgin.im/ticket/6188
+ * https://trac.adium.im/ticket/8834
+ * https://developer.pidgin.im/ticket/5484
+ * https://developer.pidgin.im/ticket/6188
*/
gboolean currently_parsing_roster_push;
@@ -191,7 +193,7 @@ struct _JabberStream
GSList *pending_buddy_info_requests;
- PurpleCircBuffer *write_buffer;
+ PurpleCircularBuffer *write_buffer;
guint writeh;
gboolean reinit;
@@ -257,19 +259,15 @@ struct _JabberStream
guint keepalive_timeout;
guint max_inactivity;
guint inactivity_timer;
+ guint conn_close_timeout;
PurpleSrvResponse *srv_rec;
guint srv_rec_idx;
guint max_srv_rec_idx;
- /* BOSH stuff */
- PurpleBOSHConnection *bosh;
+ PurpleJabberBOSHConnection *bosh;
- /**
- * This linked list contains PurpleUtilFetchUrlData structs
- * for when we lookup buddy icons from a url
- */
- GSList *url_datas;
+ PurpleHttpConnectionSet *http_conns;
/* keep a hash table of JingleSessions */
GHashTable *sessions;
@@ -282,8 +280,6 @@ struct _JabberStream
/* stuff for Google's relay handling */
gchar *google_relay_token;
gchar *google_relay_host;
- GList *google_relay_requests; /* the HTTP requests to get */
- /* relay info */
/* facebook quirks */
gboolean facebook_roster_cleanup_performed;
@@ -308,7 +304,7 @@ typedef struct _JabberIdentity
typedef struct _JabberBytestreamsStreamhost {
char *jid;
char *host;
- int port;
+ guint16 port;
char *zeroconf;
} JabberBytestreamsStreamhost;
@@ -319,17 +315,17 @@ extern GList *jabber_features;
*/
extern GList *jabber_identities;
-void jabber_stream_features_parse(JabberStream *js, xmlnode *packet);
-void jabber_process_packet(JabberStream *js, xmlnode **packet);
-void jabber_send(JabberStream *js, xmlnode *data);
+void jabber_stream_features_parse(JabberStream *js, PurpleXmlNode *packet);
+void jabber_process_packet(JabberStream *js, PurpleXmlNode **packet);
+void jabber_send(JabberStream *js, PurpleXmlNode *data);
void jabber_send_raw(JabberStream *js, const char *data, int len);
-void jabber_send_signal_cb(PurpleConnection *pc, xmlnode **packet,
+void jabber_send_signal_cb(PurpleConnection *pc, PurpleXmlNode **packet,
gpointer unused);
void jabber_stream_set_state(JabberStream *js, JabberStreamState state);
void jabber_register_parse(JabberStream *js, const char *from,
- JabberIqType type, const char *id, xmlnode *query);
+ JabberIqType type, const char *id, PurpleXmlNode *query);
void jabber_register_start(JabberStream *js);
char *jabber_get_next_id(JabberStream *js);
@@ -341,7 +337,7 @@ char *jabber_get_next_id(JabberStream *js);
* @param reason where to store the disconnection reason, or @c NULL if you
* don't care or you don't intend to close the connection.
*/
-char *jabber_parse_error(JabberStream *js, xmlnode *packet, PurpleConnectionError *reason);
+char *jabber_parse_error(JabberStream *js, PurpleXmlNode *packet, PurpleConnectionError *reason);
/**
* Add a feature to the list of features advertised via disco#info. If you
@@ -357,7 +353,7 @@ void jabber_remove_feature(const gchar *namespace);
/** Adds an identity to this jabber library instance. For list of valid values
* visit the website of the XMPP Registrar
- * (http://www.xmpp.org/registrar/disco-categories.html#client).
+ * (http://xmpp.org/registrar/disco-categories.html#client)
*
* Like with jabber_add_feature, if you call this while accounts are connected,
* Bad Things will happen.
@@ -399,7 +395,7 @@ void jabber_close(PurpleConnection *gc);
void jabber_idle_set(PurpleConnection *gc, int idle);
void jabber_blocklist_parse_push(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *child);
+ PurpleXmlNode *child);
void jabber_request_block_list(JabberStream *js);
void jabber_add_deny(PurpleConnection *gc, const char *who);
void jabber_rem_deny(PurpleConnection *gc, const char *who);
diff --git a/libpurple/protocols/jabber/jingle/content.c b/libpurple/protocols/jabber/jingle/content.c
index f19d186cb4..b60e0cd908 100644
--- a/libpurple/protocols/jabber/jingle/content.c
+++ b/libpurple/protocols/jabber/jingle/content.c
@@ -23,6 +23,7 @@
*/
#include "internal.h"
+#include "glibcompat.h"
#include "debug.h"
#include "content.h"
@@ -49,10 +50,8 @@ static void jingle_content_init (JingleContent *content);
static void jingle_content_finalize (GObject *object);
static void jingle_content_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
static void jingle_content_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
-static xmlnode *jingle_content_to_xml_internal(JingleContent *content, xmlnode *jingle, JingleActionType action);
-static JingleContent *jingle_content_parse_internal(xmlnode *content);
-
-static GObjectClass *parent_class = NULL;
+static PurpleXmlNode *jingle_content_to_xml_internal(JingleContent *content, PurpleXmlNode *jingle, JingleActionType action);
+static JingleContent *jingle_content_parse_internal(PurpleXmlNode *content);
enum {
PROP_0,
@@ -63,8 +62,12 @@ enum {
PROP_SENDERS,
PROP_TRANSPORT,
PROP_PENDING_TRANSPORT,
+ PROP_LAST
};
+static GObjectClass *parent_class = NULL;
+static GParamSpec *properties[PROP_LAST];
+
GType
jingle_content_get_type()
{
@@ -100,56 +103,51 @@ jingle_content_class_init (JingleContentClass *klass)
klass->to_xml = jingle_content_to_xml_internal;
klass->parse = jingle_content_parse_internal;
- g_object_class_install_property(gobject_class, PROP_SESSION,
- g_param_spec_object("session",
+ g_type_class_add_private(klass, sizeof(JingleContentPrivate));
+
+ properties[PROP_SESSION] = g_param_spec_object("session",
"Jingle Session",
"The jingle session parent of this content.",
JINGLE_TYPE_SESSION,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property(gobject_class, PROP_CREATOR,
- g_param_spec_string("creator",
+ properties[PROP_CREATOR] = g_param_spec_string("creator",
"Creator",
"The participant that created this content.",
NULL,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property(gobject_class, PROP_DISPOSITION,
- g_param_spec_string("disposition",
+ properties[PROP_DISPOSITION] = g_param_spec_string("disposition",
"Disposition",
"The disposition of the content.",
NULL,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property(gobject_class, PROP_NAME,
- g_param_spec_string("name",
+ properties[PROP_NAME] = g_param_spec_string("name",
"Name",
"The name of this content.",
NULL,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property(gobject_class, PROP_SENDERS,
- g_param_spec_string("senders",
+ properties[PROP_SENDERS] = g_param_spec_string("senders",
"Senders",
"The sender of this content.",
NULL,
- G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property(gobject_class, PROP_TRANSPORT,
- g_param_spec_object("transport",
+ properties[PROP_TRANSPORT] = g_param_spec_object("transport",
"transport",
"The transport of this content.",
JINGLE_TYPE_TRANSPORT,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property(gobject_class, PROP_PENDING_TRANSPORT,
- g_param_spec_object("pending-transport",
+ properties[PROP_PENDING_TRANSPORT] = g_param_spec_object("pending-transport",
"Pending transport",
"The pending transport contained within this content",
JINGLE_TYPE_TRANSPORT,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_type_class_add_private(klass, sizeof(JingleContentPrivate));
+ g_object_class_install_properties(gobject_class, PROP_LAST, properties);
}
static void
@@ -343,7 +341,7 @@ JingleTransport *
jingle_content_get_pending_transport(JingleContent *content)
{
JingleTransport *pending_transport;
- g_object_get(content, "pending_transport", &pending_transport, NULL);
+ g_object_get(content, "pending-transport", &pending_transport, NULL);
return pending_transport;
}
@@ -356,10 +354,19 @@ jingle_content_set_pending_transport(JingleContent *content, JingleTransport *tr
void
jingle_content_accept_transport(JingleContent *content)
{
+ GObject *obj;
+
if (content->priv->transport)
g_object_unref(content->priv->transport);
+
content->priv->transport = content->priv->pending_transport;
content->priv->pending_transport = NULL;
+
+ obj = G_OBJECT(content);
+ g_object_freeze_notify(obj);
+ g_object_notify_by_pspec(obj, properties[PROP_TRANSPORT]);
+ g_object_notify_by_pspec(obj, properties[PROP_PENDING_TRANSPORT]);
+ g_object_thaw_notify(obj);
}
void
@@ -369,6 +376,8 @@ jingle_content_remove_pending_transport(JingleContent *content)
g_object_unref(content->priv->pending_transport);
content->priv->pending_transport = NULL;
}
+
+ g_object_notify_by_pspec(G_OBJECT(content), properties[PROP_PENDING_TRANSPORT]);
}
void
@@ -378,16 +387,16 @@ jingle_content_modify(JingleContent *content, const gchar *senders)
}
static JingleContent *
-jingle_content_parse_internal(xmlnode *content)
+jingle_content_parse_internal(PurpleXmlNode *content)
{
- xmlnode *description = xmlnode_get_child(content, "description");
- const gchar *type = xmlnode_get_namespace(description);
- const gchar *creator = xmlnode_get_attrib(content, "creator");
- const gchar *disposition = xmlnode_get_attrib(content, "disposition");
- const gchar *senders = xmlnode_get_attrib(content, "senders");
- const gchar *name = xmlnode_get_attrib(content, "name");
+ PurpleXmlNode *description = purple_xmlnode_get_child(content, "description");
+ const gchar *type = purple_xmlnode_get_namespace(description);
+ const gchar *creator = purple_xmlnode_get_attrib(content, "creator");
+ const gchar *disposition = purple_xmlnode_get_attrib(content, "disposition");
+ const gchar *senders = purple_xmlnode_get_attrib(content, "senders");
+ const gchar *name = purple_xmlnode_get_attrib(content, "name");
JingleTransport *transport =
- jingle_transport_parse(xmlnode_get_child(content, "transport"));
+ jingle_transport_parse(purple_xmlnode_get_child(content, "transport"));
if (transport == NULL)
return NULL;
@@ -398,9 +407,9 @@ jingle_content_parse_internal(xmlnode *content)
}
JingleContent *
-jingle_content_parse(xmlnode *content)
+jingle_content_parse(PurpleXmlNode *content)
{
- const gchar *type = xmlnode_get_namespace(xmlnode_get_child(content, "description"));
+ const gchar *type = purple_xmlnode_get_namespace(purple_xmlnode_get_child(content, "description"));
GType jingle_type = jingle_get_type(type);
if (jingle_type != G_TYPE_NONE) {
@@ -410,20 +419,20 @@ jingle_content_parse(xmlnode *content)
}
}
-static xmlnode *
-jingle_content_to_xml_internal(JingleContent *content, xmlnode *jingle, JingleActionType action)
+static PurpleXmlNode *
+jingle_content_to_xml_internal(JingleContent *content, PurpleXmlNode *jingle, JingleActionType action)
{
- xmlnode *node = xmlnode_new_child(jingle, "content");
+ PurpleXmlNode *node = purple_xmlnode_new_child(jingle, "content");
gchar *creator = jingle_content_get_creator(content);
gchar *name = jingle_content_get_name(content);
gchar *senders = jingle_content_get_senders(content);
gchar *disposition = jingle_content_get_disposition(content);
- xmlnode_set_attrib(node, "creator", creator);
- xmlnode_set_attrib(node, "name", name);
- xmlnode_set_attrib(node, "senders", senders);
+ purple_xmlnode_set_attrib(node, "creator", creator);
+ purple_xmlnode_set_attrib(node, "name", name);
+ purple_xmlnode_set_attrib(node, "senders", senders);
if (strcmp("session", disposition))
- xmlnode_set_attrib(node, "disposition", disposition);
+ purple_xmlnode_set_attrib(node, "disposition", disposition);
g_free(disposition);
g_free(senders);
@@ -437,9 +446,9 @@ jingle_content_to_xml_internal(JingleContent *content, xmlnode *jingle, JingleAc
action != JINGLE_TRANSPORT_INFO &&
action != JINGLE_TRANSPORT_REJECT &&
action != JINGLE_TRANSPORT_REPLACE) {
- xmlnode *description = xmlnode_new_child(node, "description");
+ PurpleXmlNode *description = purple_xmlnode_new_child(node, "description");
- xmlnode_set_namespace(description,
+ purple_xmlnode_set_namespace(description,
jingle_content_get_description_type(content));
}
@@ -455,8 +464,8 @@ jingle_content_to_xml_internal(JingleContent *content, xmlnode *jingle, JingleAc
return node;
}
-xmlnode *
-jingle_content_to_xml(JingleContent *content, xmlnode *jingle, JingleActionType action)
+PurpleXmlNode *
+jingle_content_to_xml(JingleContent *content, PurpleXmlNode *jingle, JingleActionType action)
{
g_return_val_if_fail(content != NULL, NULL);
g_return_val_if_fail(JINGLE_IS_CONTENT(content), NULL);
@@ -464,7 +473,7 @@ jingle_content_to_xml(JingleContent *content, xmlnode *jingle, JingleActionType
}
void
-jingle_content_handle_action(JingleContent *content, xmlnode *xmlcontent, JingleActionType action)
+jingle_content_handle_action(JingleContent *content, PurpleXmlNode *xmlcontent, JingleActionType action)
{
g_return_if_fail(content != NULL);
g_return_if_fail(JINGLE_IS_CONTENT(content));
diff --git a/libpurple/protocols/jabber/jingle/content.h b/libpurple/protocols/jabber/jingle/content.h
index 91dc4e86ca..bbefb2751e 100644
--- a/libpurple/protocols/jabber/jingle/content.h
+++ b/libpurple/protocols/jabber/jingle/content.h
@@ -55,9 +55,9 @@ struct _JingleContentClass
{
GObjectClass parent_class; /**< The parent class. */
- xmlnode *(*to_xml) (JingleContent *content, xmlnode *jingle, JingleActionType action);
- JingleContent *(*parse) (xmlnode *content);
- void (*handle_action) (JingleContent *content, xmlnode *xmlcontent, JingleActionType action);
+ PurpleXmlNode *(*to_xml) (JingleContent *content, PurpleXmlNode *jingle, JingleActionType action);
+ JingleContent *(*parse) (PurpleXmlNode *content);
+ void (*handle_action) (JingleContent *content, PurpleXmlNode *xmlcontent, JingleActionType action);
const gchar *description_type;
};
@@ -68,10 +68,6 @@ struct _JingleContent
JingleContentPrivate *priv; /**< The private data of this object. */
};
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* Gets the content class's GType
*
@@ -107,13 +103,9 @@ void jingle_content_modify(JingleContent *content, const gchar *senders);
#define jingle_content_create_content_remove(session) \
jingle_session_to_packet(session, JINGLE_CONTENT_REMOVE)
-JingleContent *jingle_content_parse(xmlnode *content);
-xmlnode *jingle_content_to_xml(JingleContent *content, xmlnode *jingle, JingleActionType action);
-void jingle_content_handle_action(JingleContent *content, xmlnode *xmlcontent, JingleActionType action);
-
-#ifdef __cplusplus
-}
-#endif
+JingleContent *jingle_content_parse(PurpleXmlNode *content);
+PurpleXmlNode *jingle_content_to_xml(JingleContent *content, PurpleXmlNode *jingle, JingleActionType action);
+void jingle_content_handle_action(JingleContent *content, PurpleXmlNode *xmlcontent, JingleActionType action);
G_END_DECLS
diff --git a/libpurple/protocols/jabber/jingle/iceudp.c b/libpurple/protocols/jabber/jingle/iceudp.c
index 54c0d91d03..c081cd11e2 100644
--- a/libpurple/protocols/jabber/jingle/iceudp.c
+++ b/libpurple/protocols/jabber/jingle/iceudp.c
@@ -23,6 +23,7 @@
*/
#include "internal.h"
+#include "glibcompat.h"
#include "iceudp.h"
#include "jingle.h"
@@ -43,25 +44,29 @@ static void jingle_iceudp_init (JingleIceUdp *iceudp);
static void jingle_iceudp_finalize (GObject *object);
static void jingle_iceudp_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
static void jingle_iceudp_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
-static JingleTransport *jingle_iceudp_parse_internal(xmlnode *iceudp);
-static xmlnode *jingle_iceudp_to_xml_internal(JingleTransport *transport, xmlnode *content, JingleActionType action);
-
-static JingleTransportClass *parent_class = NULL;
+static JingleTransport *jingle_iceudp_parse_internal(PurpleXmlNode *iceudp);
+static PurpleXmlNode *jingle_iceudp_to_xml_internal(JingleTransport *transport, PurpleXmlNode *content, JingleActionType action);
+static void jingle_iceudp_add_local_candidate(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate);
+static GList *jingle_iceudp_get_remote_candidates(JingleTransport *transport);
enum {
PROP_0,
PROP_LOCAL_CANDIDATES,
PROP_REMOTE_CANDIDATES,
+ PROP_LAST
};
+static JingleTransportClass *parent_class = NULL;
+static GParamSpec *properties[PROP_LAST];
+
static JingleIceUdpCandidate *
jingle_iceudp_candidate_copy(JingleIceUdpCandidate *candidate)
{
JingleIceUdpCandidate *new_candidate = g_new0(JingleIceUdpCandidate, 1);
+ new_candidate->id = g_strdup(candidate->id);
new_candidate->component = candidate->component;
new_candidate->foundation = g_strdup(candidate->foundation);
new_candidate->generation = candidate->generation;
- new_candidate->id = g_strdup(candidate->id);
new_candidate->ip = g_strdup(candidate->ip);
new_candidate->network = candidate->network;
new_candidate->port = candidate->port;
@@ -105,17 +110,18 @@ jingle_iceudp_candidate_get_type()
}
JingleIceUdpCandidate *
-jingle_iceudp_candidate_new(guint component, const gchar *foundation,
- guint generation, const gchar *id, const gchar *ip,
+jingle_iceudp_candidate_new(const gchar *id,
+ guint component, const gchar *foundation,
+ guint generation, const gchar *ip,
guint network, guint port, guint priority,
const gchar *protocol, const gchar *type,
const gchar *username, const gchar *password)
{
JingleIceUdpCandidate *candidate = g_new0(JingleIceUdpCandidate, 1);
+ candidate->id = g_strdup(id);
candidate->component = component;
candidate->foundation = g_strdup(foundation);
candidate->generation = generation;
- candidate->id = g_strdup(id);
candidate->ip = g_strdup(ip);
candidate->network = network;
candidate->port = port;
@@ -165,20 +171,22 @@ jingle_iceudp_class_init (JingleIceUdpClass *klass)
klass->parent_class.to_xml = jingle_iceudp_to_xml_internal;
klass->parent_class.parse = jingle_iceudp_parse_internal;
klass->parent_class.transport_type = JINGLE_TRANSPORT_ICEUDP;
+ klass->parent_class.add_local_candidate = jingle_iceudp_add_local_candidate;
+ klass->parent_class.get_remote_candidates = jingle_iceudp_get_remote_candidates;
- g_object_class_install_property(gobject_class, PROP_LOCAL_CANDIDATES,
- g_param_spec_pointer("local-candidates",
+ g_type_class_add_private(klass, sizeof(JingleIceUdpPrivate));
+
+ properties[PROP_LOCAL_CANDIDATES] = g_param_spec_pointer("local-candidates",
"Local candidates",
"The local candidates for this transport.",
- G_PARAM_READABLE));
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property(gobject_class, PROP_REMOTE_CANDIDATES,
- g_param_spec_pointer("remote-candidates",
+ properties[PROP_REMOTE_CANDIDATES] = g_param_spec_pointer("remote-candidates",
"Remote candidates",
"The remote candidates for this transport.",
- G_PARAM_READABLE));
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_type_class_add_private(klass, sizeof(JingleIceUdpPrivate));
+ g_object_class_install_properties(gobject_class, PROP_LAST, properties);
}
static void
@@ -246,36 +254,98 @@ jingle_iceudp_get_property (GObject *object, guint prop_id, GValue *value, GPara
}
}
-void
-jingle_iceudp_add_local_candidate(JingleIceUdp *iceudp, JingleIceUdpCandidate *candidate)
+static void
+jingle_iceudp_add_local_candidate(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate)
{
- GList *iter = iceudp->priv->local_candidates;
-
- for (; iter; iter = g_list_next(iter)) {
+ JingleIceUdp *iceudp = JINGLE_ICEUDP(transport);
+ PurpleMediaCandidateType type;
+ gchar *ip;
+ gchar *username;
+ gchar *password;
+ JingleIceUdpCandidate *iceudp_candidate;
+ GList *iter;
+
+ ip = purple_media_candidate_get_ip(candidate);
+ username = purple_media_candidate_get_username(candidate);
+ password = purple_media_candidate_get_password(candidate);
+ type = purple_media_candidate_get_candidate_type(candidate);
+
+ iceudp_candidate = jingle_iceudp_candidate_new(id,
+ purple_media_candidate_get_component_id(candidate),
+ purple_media_candidate_get_foundation(candidate),
+ generation, ip, 0,
+ purple_media_candidate_get_port(candidate),
+ purple_media_candidate_get_priority(candidate), "udp",
+ type == PURPLE_MEDIA_CANDIDATE_TYPE_HOST ? "host" :
+ type == PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX ? "srflx" :
+ type == PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX ? "prflx" :
+ type == PURPLE_MEDIA_CANDIDATE_TYPE_RELAY ? "relay" :
+ "", username, password);
+ iceudp_candidate->reladdr = purple_media_candidate_get_base_ip(candidate);
+ iceudp_candidate->relport = purple_media_candidate_get_base_port(candidate);
+
+ g_free(password);
+ g_free(username);
+ g_free(ip);
+
+ for (iter = iceudp->priv->local_candidates; iter; iter = g_list_next(iter)) {
JingleIceUdpCandidate *c = iter->data;
- if (!strcmp(c->id, candidate->id)) {
- guint generation = c->generation + 1;
+ if (!strcmp(c->id, id)) {
+ generation = c->generation + 1;
g_boxed_free(JINGLE_TYPE_ICEUDP_CANDIDATE, c);
iceudp->priv->local_candidates = g_list_delete_link(
iceudp->priv->local_candidates, iter);
- candidate->generation = generation;
+ iceudp_candidate->generation = generation;
iceudp->priv->local_candidates = g_list_append(
- iceudp->priv->local_candidates, candidate);
+ iceudp->priv->local_candidates, iceudp_candidate);
+
+ g_object_notify_by_pspec(G_OBJECT(iceudp), properties[PROP_LOCAL_CANDIDATES]);
+
return;
}
}
iceudp->priv->local_candidates = g_list_append(
- iceudp->priv->local_candidates, candidate);
+ iceudp->priv->local_candidates, iceudp_candidate);
+
+ g_object_notify_by_pspec(G_OBJECT(iceudp), properties[PROP_LOCAL_CANDIDATES]);
}
-GList *
-jingle_iceudp_get_remote_candidates(JingleIceUdp *iceudp)
+static GList *
+jingle_iceudp_get_remote_candidates(JingleTransport *transport)
{
- return g_list_copy(iceudp->priv->remote_candidates);
+ JingleIceUdp *iceudp = JINGLE_ICEUDP(transport);
+ GList *candidates = iceudp->priv->remote_candidates;
+ GList *ret = NULL;
+
+ for (; candidates; candidates = g_list_next(candidates)) {
+ JingleIceUdpCandidate *candidate = candidates->data;
+ PurpleMediaCandidate *new_candidate = purple_media_candidate_new(
+ candidate->foundation, candidate->component,
+ !strcmp(candidate->type, "host") ?
+ PURPLE_MEDIA_CANDIDATE_TYPE_HOST :
+ !strcmp(candidate->type, "srflx") ?
+ PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX :
+ !strcmp(candidate->type, "prflx") ?
+ PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX :
+ !strcmp(candidate->type, "relay") ?
+ PURPLE_MEDIA_CANDIDATE_TYPE_RELAY : 0,
+ PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
+ candidate->ip, candidate->port);
+ g_object_set(new_candidate,
+ "base-ip", candidate->reladdr,
+ "base-port", candidate->relport,
+ "username", candidate->username,
+ "password", candidate->password,
+ "priority", candidate->priority,
+ NULL);
+ ret = g_list_append(ret, new_candidate);
+ }
+
+ return ret;
}
static JingleIceUdpCandidate *
@@ -305,40 +375,42 @@ jingle_iceudp_add_remote_candidate(JingleIceUdp *iceudp, JingleIceUdpCandidate *
g_boxed_free(JINGLE_TYPE_ICEUDP_CANDIDATE, iceudp_candidate);
}
priv->remote_candidates = g_list_append(priv->remote_candidates, candidate);
+
+ g_object_notify_by_pspec(G_OBJECT(iceudp), properties[PROP_REMOTE_CANDIDATES]);
}
static JingleTransport *
-jingle_iceudp_parse_internal(xmlnode *iceudp)
+jingle_iceudp_parse_internal(PurpleXmlNode *iceudp)
{
JingleTransport *transport = parent_class->parse(iceudp);
- xmlnode *candidate = xmlnode_get_child(iceudp, "candidate");
+ PurpleXmlNode *candidate = purple_xmlnode_get_child(iceudp, "candidate");
JingleIceUdpCandidate *iceudp_candidate = NULL;
- const gchar *username = xmlnode_get_attrib(iceudp, "ufrag");
- const gchar *password = xmlnode_get_attrib(iceudp, "pwd");
-
- for (; candidate; candidate = xmlnode_get_next_twin(candidate)) {
- const gchar *relport = xmlnode_get_attrib(candidate, "rel-port");
- const gchar *component = xmlnode_get_attrib(candidate, "component");
- const gchar *foundation = xmlnode_get_attrib(candidate, "foundation");
- const gchar *generation = xmlnode_get_attrib(candidate, "generation");
- const gchar *id = xmlnode_get_attrib(candidate, "id");
- const gchar *ip = xmlnode_get_attrib(candidate, "ip");
- const gchar *network = xmlnode_get_attrib(candidate, "network");
- const gchar *port = xmlnode_get_attrib(candidate, "port");
- const gchar *priority = xmlnode_get_attrib(candidate, "priority");
- const gchar *protocol = xmlnode_get_attrib(candidate, "protocol");
- const gchar *type = xmlnode_get_attrib(candidate, "type");
+ const gchar *username = purple_xmlnode_get_attrib(iceudp, "ufrag");
+ const gchar *password = purple_xmlnode_get_attrib(iceudp, "pwd");
+
+ for (; candidate; candidate = purple_xmlnode_get_next_twin(candidate)) {
+ const gchar *relport = purple_xmlnode_get_attrib(candidate, "rel-port");
+ const gchar *component = purple_xmlnode_get_attrib(candidate, "component");
+ const gchar *foundation = purple_xmlnode_get_attrib(candidate, "foundation");
+ const gchar *generation = purple_xmlnode_get_attrib(candidate, "generation");
+ const gchar *id = purple_xmlnode_get_attrib(candidate, "id");
+ const gchar *ip = purple_xmlnode_get_attrib(candidate, "ip");
+ const gchar *network = purple_xmlnode_get_attrib(candidate, "network");
+ const gchar *port = purple_xmlnode_get_attrib(candidate, "port");
+ const gchar *priority = purple_xmlnode_get_attrib(candidate, "priority");
+ const gchar *protocol = purple_xmlnode_get_attrib(candidate, "protocol");
+ const gchar *type = purple_xmlnode_get_attrib(candidate, "type");
if (!component || !foundation || !generation || !id || !ip ||
!network || !port || !priority || !protocol || !type)
continue;
iceudp_candidate = jingle_iceudp_candidate_new(
+ id,
atoi(component),
foundation,
atoi(generation),
- id,
ip,
atoi(network),
atoi(port),
@@ -347,7 +419,7 @@ jingle_iceudp_parse_internal(xmlnode *iceudp)
type,
username, password);
iceudp_candidate->reladdr = g_strdup(
- xmlnode_get_attrib(candidate, "rel-addr"));
+ purple_xmlnode_get_attrib(candidate, "rel-addr"));
iceudp_candidate->relport =
relport != NULL ? atoi(relport) : 0;
iceudp_candidate->rem_known = TRUE;
@@ -357,10 +429,10 @@ jingle_iceudp_parse_internal(xmlnode *iceudp)
return transport;
}
-static xmlnode *
-jingle_iceudp_to_xml_internal(JingleTransport *transport, xmlnode *content, JingleActionType action)
+static PurpleXmlNode *
+jingle_iceudp_to_xml_internal(JingleTransport *transport, PurpleXmlNode *content, JingleActionType action)
{
- xmlnode *node = parent_class->to_xml(transport, content, action);
+ PurpleXmlNode *node = parent_class->to_xml(transport, content, action);
if (action == JINGLE_SESSION_INITIATE ||
action == JINGLE_SESSION_ACCEPT ||
@@ -373,7 +445,7 @@ jingle_iceudp_to_xml_internal(JingleTransport *transport, xmlnode *content, Jing
for (; iter; iter = g_list_next(iter)) {
JingleIceUdpCandidate *candidate = iter->data;
- xmlnode *xmltransport;
+ PurpleXmlNode *xmltransport;
gchar *component, *generation, *network,
*port, *priority;
@@ -383,7 +455,7 @@ jingle_iceudp_to_xml_internal(JingleTransport *transport, xmlnode *content, Jing
used_candidate = TRUE;
candidate->rem_known = TRUE;
- xmltransport = xmlnode_new_child(node, "candidate");
+ xmltransport = purple_xmlnode_new_child(node, "candidate");
component = g_strdup_printf("%d", candidate->component);
generation = g_strdup_printf("%d",
candidate->generation);
@@ -391,29 +463,29 @@ jingle_iceudp_to_xml_internal(JingleTransport *transport, xmlnode *content, Jing
port = g_strdup_printf("%d", candidate->port);
priority = g_strdup_printf("%d", candidate->priority);
- xmlnode_set_attrib(xmltransport, "component", component);
- xmlnode_set_attrib(xmltransport, "foundation", candidate->foundation);
- xmlnode_set_attrib(xmltransport, "generation", generation);
- xmlnode_set_attrib(xmltransport, "id", candidate->id);
- xmlnode_set_attrib(xmltransport, "ip", candidate->ip);
- xmlnode_set_attrib(xmltransport, "network", network);
- xmlnode_set_attrib(xmltransport, "port", port);
- xmlnode_set_attrib(xmltransport, "priority", priority);
- xmlnode_set_attrib(xmltransport, "protocol", candidate->protocol);
+ purple_xmlnode_set_attrib(xmltransport, "component", component);
+ purple_xmlnode_set_attrib(xmltransport, "foundation", candidate->foundation);
+ purple_xmlnode_set_attrib(xmltransport, "generation", generation);
+ purple_xmlnode_set_attrib(xmltransport, "id", candidate->id);
+ purple_xmlnode_set_attrib(xmltransport, "ip", candidate->ip);
+ purple_xmlnode_set_attrib(xmltransport, "network", network);
+ purple_xmlnode_set_attrib(xmltransport, "port", port);
+ purple_xmlnode_set_attrib(xmltransport, "priority", priority);
+ purple_xmlnode_set_attrib(xmltransport, "protocol", candidate->protocol);
if (candidate->reladdr != NULL &&
(strcmp(candidate->ip, candidate->reladdr) ||
(candidate->port != candidate->relport))) {
gchar *relport = g_strdup_printf("%d",
candidate->relport);
- xmlnode_set_attrib(xmltransport, "rel-addr",
+ purple_xmlnode_set_attrib(xmltransport, "rel-addr",
candidate->reladdr);
- xmlnode_set_attrib(xmltransport, "rel-port",
+ purple_xmlnode_set_attrib(xmltransport, "rel-port",
relport);
g_free(relport);
}
- xmlnode_set_attrib(xmltransport, "type", candidate->type);
+ purple_xmlnode_set_attrib(xmltransport, "type", candidate->type);
g_free(component);
g_free(generation);
@@ -425,8 +497,8 @@ jingle_iceudp_to_xml_internal(JingleTransport *transport, xmlnode *content, Jing
if (used_candidate == TRUE) {
JingleIceUdpCandidate *candidate =
priv->local_candidates->data;
- xmlnode_set_attrib(node, "pwd", candidate->password);
- xmlnode_set_attrib(node, "ufrag", candidate->username);
+ purple_xmlnode_set_attrib(node, "pwd", candidate->password);
+ purple_xmlnode_set_attrib(node, "ufrag", candidate->username);
}
}
diff --git a/libpurple/protocols/jabber/jingle/iceudp.h b/libpurple/protocols/jabber/jingle/iceudp.h
index 95e81be674..8bcf8c3869 100644
--- a/libpurple/protocols/jabber/jingle/iceudp.h
+++ b/libpurple/protocols/jabber/jingle/iceudp.h
@@ -54,8 +54,8 @@ struct _JingleIceUdpClass
{
JingleTransportClass parent_class; /**< The parent class. */
- xmlnode *(*to_xml) (JingleTransport *transport, xmlnode *content, JingleActionType action);
- JingleTransport *(*parse) (xmlnode *transport);
+ PurpleXmlNode *(*to_xml) (JingleTransport *transport, PurpleXmlNode *content, JingleActionType action);
+ JingleTransport *(*parse) (PurpleXmlNode *transport);
};
/** The iceudp class's private data */
@@ -67,10 +67,10 @@ struct _JingleIceUdp
struct _JingleIceUdpCandidate
{
+ gchar *id;
guint component;
gchar *foundation;
guint generation;
- gchar *id;
gchar *ip;
guint network;
guint port;
@@ -87,10 +87,6 @@ struct _JingleIceUdpCandidate
* about this candidate */
};
-#ifdef __cplusplus
-extern "C" {
-#endif
-
GType jingle_iceudp_candidate_get_type(void);
/**
@@ -100,17 +96,11 @@ GType jingle_iceudp_candidate_get_type(void);
*/
GType jingle_iceudp_get_type(void);
-JingleIceUdpCandidate *jingle_iceudp_candidate_new(guint component,
- const gchar *foundation, guint generation, const gchar *id,
+JingleIceUdpCandidate *jingle_iceudp_candidate_new(const gchar *id,
+ guint component, const gchar *foundation, guint generation,
const gchar *ip, guint network, guint port, guint priority,
const gchar *protocol, const gchar *type,
const gchar *username, const gchar *password);
-void jingle_iceudp_add_local_candidate(JingleIceUdp *iceudp, JingleIceUdpCandidate *candidate);
-GList *jingle_iceudp_get_remote_candidates(JingleIceUdp *iceudp);
-
-#ifdef __cplusplus
-}
-#endif
G_END_DECLS
diff --git a/libpurple/protocols/jabber/jingle/jingle.c b/libpurple/protocols/jabber/jingle/jingle.c
index eeaabf037e..0b697ec8e3 100644
--- a/libpurple/protocols/jabber/jingle/jingle.c
+++ b/libpurple/protocols/jabber/jingle/jingle.c
@@ -24,11 +24,13 @@
*/
#include "internal.h"
+#include "glibcompat.h"
#include "network.h"
#include "content.h"
#include "debug.h"
#include "jingle.h"
+#include "google/google_p2p.h"
#include "session.h"
#include "iceudp.h"
#include "rawudp.h"
@@ -58,6 +60,8 @@ jingle_get_type(const gchar *type)
#ifdef USE_VV
else if (!strcmp(type, JINGLE_APP_RTP))
return JINGLE_TYPE_RTP;
+ else if (!strcmp(type, NS_GOOGLE_TRANSPORT_P2P))
+ return JINGLE_TYPE_GOOGLE_P2P;
#endif
#if 0
else if (!strcmp(type, JINGLE_APP_FT))
@@ -70,32 +74,32 @@ jingle_get_type(const gchar *type)
}
static void
-jingle_handle_unknown_type(JingleSession *session, xmlnode *jingle)
+jingle_handle_unknown_type(JingleSession *session, PurpleXmlNode *jingle)
{
/* Send error */
}
static void
-jingle_handle_content_accept(JingleSession *session, xmlnode *jingle)
+jingle_handle_content_accept(JingleSession *session, PurpleXmlNode *jingle)
{
- xmlnode *content = xmlnode_get_child(jingle, "content");
+ PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content");
jabber_iq_send(jingle_session_create_ack(session, jingle));
- for (; content; content = xmlnode_get_next_twin(content)) {
- const gchar *name = xmlnode_get_attrib(content, "name");
- const gchar *creator = xmlnode_get_attrib(content, "creator");
+ for (; content; content = purple_xmlnode_get_next_twin(content)) {
+ const gchar *name = purple_xmlnode_get_attrib(content, "name");
+ const gchar *creator = purple_xmlnode_get_attrib(content, "creator");
jingle_session_accept_content(session, name, creator);
/* signal here */
}
}
static void
-jingle_handle_content_add(JingleSession *session, xmlnode *jingle)
+jingle_handle_content_add(JingleSession *session, PurpleXmlNode *jingle)
{
- xmlnode *content = xmlnode_get_child(jingle, "content");
+ PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content");
jabber_iq_send(jingle_session_create_ack(session, jingle));
- for (; content; content = xmlnode_get_next_twin(content)) {
+ for (; content; content = purple_xmlnode_get_next_twin(content)) {
JingleContent *pending_content =
jingle_content_parse(content);
if (pending_content == NULL) {
@@ -113,18 +117,18 @@ jingle_handle_content_add(JingleSession *session, xmlnode *jingle)
}
static void
-jingle_handle_content_modify(JingleSession *session, xmlnode *jingle)
+jingle_handle_content_modify(JingleSession *session, PurpleXmlNode *jingle)
{
- xmlnode *content = xmlnode_get_child(jingle, "content");
+ PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content");
jabber_iq_send(jingle_session_create_ack(session, jingle));
- for (; content; content = xmlnode_get_next_twin(content)) {
- const gchar *name = xmlnode_get_attrib(content, "name");
- const gchar *creator = xmlnode_get_attrib(content, "creator");
+ for (; content; content = purple_xmlnode_get_next_twin(content)) {
+ const gchar *name = purple_xmlnode_get_attrib(content, "name");
+ const gchar *creator = purple_xmlnode_get_attrib(content, "creator");
JingleContent *local_content = jingle_session_find_content(session, name, creator);
if (local_content != NULL) {
- const gchar *senders = xmlnode_get_attrib(content, "senders");
+ const gchar *senders = purple_xmlnode_get_attrib(content, "senders");
gchar *local_senders = jingle_content_get_senders(local_content);
if (!purple_strequal(senders, local_senders))
jingle_content_modify(local_content, senders);
@@ -138,45 +142,45 @@ jingle_handle_content_modify(JingleSession *session, xmlnode *jingle)
}
static void
-jingle_handle_content_reject(JingleSession *session, xmlnode *jingle)
+jingle_handle_content_reject(JingleSession *session, PurpleXmlNode *jingle)
{
- xmlnode *content = xmlnode_get_child(jingle, "content");
+ PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content");
jabber_iq_send(jingle_session_create_ack(session, jingle));
- for (; content; content = xmlnode_get_next_twin(content)) {
- const gchar *name = xmlnode_get_attrib(content, "name");
- const gchar *creator = xmlnode_get_attrib(content, "creator");
+ for (; content; content = purple_xmlnode_get_next_twin(content)) {
+ const gchar *name = purple_xmlnode_get_attrib(content, "name");
+ const gchar *creator = purple_xmlnode_get_attrib(content, "creator");
jingle_session_remove_pending_content(session, name, creator);
/* signal here */
}
}
static void
-jingle_handle_content_remove(JingleSession *session, xmlnode *jingle)
+jingle_handle_content_remove(JingleSession *session, PurpleXmlNode *jingle)
{
- xmlnode *content = xmlnode_get_child(jingle, "content");
+ PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content");
jabber_iq_send(jingle_session_create_ack(session, jingle));
- for (; content; content = xmlnode_get_next_twin(content)) {
- const gchar *name = xmlnode_get_attrib(content, "name");
- const gchar *creator = xmlnode_get_attrib(content, "creator");
+ for (; content; content = purple_xmlnode_get_next_twin(content)) {
+ const gchar *name = purple_xmlnode_get_attrib(content, "name");
+ const gchar *creator = purple_xmlnode_get_attrib(content, "creator");
jingle_session_remove_content(session, name, creator);
}
}
static void
-jingle_handle_description_info(JingleSession *session, xmlnode *jingle)
+jingle_handle_description_info(JingleSession *session, PurpleXmlNode *jingle)
{
- xmlnode *content = xmlnode_get_child(jingle, "content");
+ PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content");
jabber_iq_send(jingle_session_create_ack(session, jingle));
jingle_session_accept_session(session);
- for (; content; content = xmlnode_get_next_twin(content)) {
- const gchar *name = xmlnode_get_attrib(content, "name");
- const gchar *creator = xmlnode_get_attrib(content, "creator");
+ for (; content; content = purple_xmlnode_get_next_twin(content)) {
+ const gchar *name = purple_xmlnode_get_attrib(content, "name");
+ const gchar *creator = purple_xmlnode_get_attrib(content, "creator");
JingleContent *parsed_content =
jingle_session_find_content(session, name, creator);
if (parsed_content == NULL) {
@@ -191,23 +195,23 @@ jingle_handle_description_info(JingleSession *session, xmlnode *jingle)
}
static void
-jingle_handle_security_info(JingleSession *session, xmlnode *jingle)
+jingle_handle_security_info(JingleSession *session, PurpleXmlNode *jingle)
{
jabber_iq_send(jingle_session_create_ack(session, jingle));
}
static void
-jingle_handle_session_accept(JingleSession *session, xmlnode *jingle)
+jingle_handle_session_accept(JingleSession *session, PurpleXmlNode *jingle)
{
- xmlnode *content = xmlnode_get_child(jingle, "content");
+ PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content");
jabber_iq_send(jingle_session_create_ack(session, jingle));
jingle_session_accept_session(session);
- for (; content; content = xmlnode_get_next_twin(content)) {
- const gchar *name = xmlnode_get_attrib(content, "name");
- const gchar *creator = xmlnode_get_attrib(content, "creator");
+ for (; content; content = purple_xmlnode_get_next_twin(content)) {
+ const gchar *name = purple_xmlnode_get_attrib(content, "name");
+ const gchar *creator = purple_xmlnode_get_attrib(content, "creator");
JingleContent *parsed_content =
jingle_session_find_content(session, name, creator);
if (parsed_content == NULL) {
@@ -222,18 +226,18 @@ jingle_handle_session_accept(JingleSession *session, xmlnode *jingle)
}
static void
-jingle_handle_session_info(JingleSession *session, xmlnode *jingle)
+jingle_handle_session_info(JingleSession *session, PurpleXmlNode *jingle)
{
jabber_iq_send(jingle_session_create_ack(session, jingle));
/* XXX: call signal */
}
static void
-jingle_handle_session_initiate(JingleSession *session, xmlnode *jingle)
+jingle_handle_session_initiate(JingleSession *session, PurpleXmlNode *jingle)
{
- xmlnode *content = xmlnode_get_child(jingle, "content");
+ PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content");
- for (; content; content = xmlnode_get_next_twin(content)) {
+ for (; content; content = purple_xmlnode_get_next_twin(content)) {
JingleContent *parsed_content = jingle_content_parse(content);
if (parsed_content == NULL) {
purple_debug_error("jingle", "Error parsing content\n");
@@ -250,7 +254,7 @@ jingle_handle_session_initiate(JingleSession *session, xmlnode *jingle)
}
static void
-jingle_handle_session_terminate(JingleSession *session, xmlnode *jingle)
+jingle_handle_session_terminate(JingleSession *session, PurpleXmlNode *jingle)
{
jabber_iq_send(jingle_session_create_ack(session, jingle));
@@ -261,30 +265,30 @@ jingle_handle_session_terminate(JingleSession *session, xmlnode *jingle)
}
static void
-jingle_handle_transport_accept(JingleSession *session, xmlnode *jingle)
+jingle_handle_transport_accept(JingleSession *session, PurpleXmlNode *jingle)
{
- xmlnode *content = xmlnode_get_child(jingle, "content");
+ PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content");
jabber_iq_send(jingle_session_create_ack(session, jingle));
- for (; content; content = xmlnode_get_next_twin(content)) {
- const gchar *name = xmlnode_get_attrib(content, "name");
- const gchar *creator = xmlnode_get_attrib(content, "creator");
+ for (; content; content = purple_xmlnode_get_next_twin(content)) {
+ const gchar *name = purple_xmlnode_get_attrib(content, "name");
+ const gchar *creator = purple_xmlnode_get_attrib(content, "creator");
JingleContent *content = jingle_session_find_content(session, name, creator);
jingle_content_accept_transport(content);
}
}
static void
-jingle_handle_transport_info(JingleSession *session, xmlnode *jingle)
+jingle_handle_transport_info(JingleSession *session, PurpleXmlNode *jingle)
{
- xmlnode *content = xmlnode_get_child(jingle, "content");
+ PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content");
jabber_iq_send(jingle_session_create_ack(session, jingle));
- for (; content; content = xmlnode_get_next_twin(content)) {
- const gchar *name = xmlnode_get_attrib(content, "name");
- const gchar *creator = xmlnode_get_attrib(content, "creator");
+ for (; content; content = purple_xmlnode_get_next_twin(content)) {
+ const gchar *name = purple_xmlnode_get_attrib(content, "name");
+ const gchar *creator = purple_xmlnode_get_attrib(content, "creator");
JingleContent *parsed_content =
jingle_session_find_content(session, name, creator);
if (parsed_content == NULL) {
@@ -299,31 +303,31 @@ jingle_handle_transport_info(JingleSession *session, xmlnode *jingle)
}
static void
-jingle_handle_transport_reject(JingleSession *session, xmlnode *jingle)
+jingle_handle_transport_reject(JingleSession *session, PurpleXmlNode *jingle)
{
- xmlnode *content = xmlnode_get_child(jingle, "content");
+ PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content");
jabber_iq_send(jingle_session_create_ack(session, jingle));
- for (; content; content = xmlnode_get_next_twin(content)) {
- const gchar *name = xmlnode_get_attrib(content, "name");
- const gchar *creator = xmlnode_get_attrib(content, "creator");
+ for (; content; content = purple_xmlnode_get_next_twin(content)) {
+ const gchar *name = purple_xmlnode_get_attrib(content, "name");
+ const gchar *creator = purple_xmlnode_get_attrib(content, "creator");
JingleContent *content = jingle_session_find_content(session, name, creator);
jingle_content_remove_pending_transport(content);
}
}
static void
-jingle_handle_transport_replace(JingleSession *session, xmlnode *jingle)
+jingle_handle_transport_replace(JingleSession *session, PurpleXmlNode *jingle)
{
- xmlnode *content = xmlnode_get_child(jingle, "content");
+ PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content");
jabber_iq_send(jingle_session_create_ack(session, jingle));
- for (; content; content = xmlnode_get_next_twin(content)) {
- const gchar *name = xmlnode_get_attrib(content, "name");
- const gchar *creator = xmlnode_get_attrib(content, "creator");
- xmlnode *xmltransport = xmlnode_get_child(content, "transport");
+ for (; content; content = purple_xmlnode_get_next_twin(content)) {
+ const gchar *name = purple_xmlnode_get_attrib(content, "name");
+ const gchar *creator = purple_xmlnode_get_attrib(content, "creator");
+ PurpleXmlNode *xmltransport = purple_xmlnode_get_child(content, "transport");
JingleTransport *transport = jingle_transport_parse(xmltransport);
JingleContent *content = jingle_session_find_content(session, name, creator);
@@ -333,7 +337,7 @@ jingle_handle_transport_replace(JingleSession *session, xmlnode *jingle)
typedef struct {
const char *name;
- void (*handler)(JingleSession*, xmlnode*);
+ void (*handler)(JingleSession*, PurpleXmlNode*);
} JingleAction;
static const JingleAction jingle_actions[] = {
@@ -377,7 +381,7 @@ jingle_get_action_type(const gchar *action)
void
jingle_parse(JabberStream *js, const char *from, JabberIqType type,
- const char *id, xmlnode *jingle)
+ const char *id, PurpleXmlNode *jingle)
{
const gchar *action;
const gchar *sid;
@@ -389,7 +393,7 @@ jingle_parse(JabberStream *js, const char *from, JabberIqType type,
return;
}
- if (!(action = xmlnode_get_attrib(jingle, "action"))) {
+ if (!(action = purple_xmlnode_get_attrib(jingle, "action"))) {
/* TODO: send iq error here */
return;
}
@@ -399,7 +403,7 @@ jingle_parse(JabberStream *js, const char *from, JabberIqType type,
purple_debug_info("jabber", "got Jingle package action = %s\n",
action);
- if (!(sid = xmlnode_get_attrib(jingle, "sid"))) {
+ if (!(sid = purple_xmlnode_get_attrib(jingle, "sid"))) {
/* send iq error here */
return;
}
@@ -429,18 +433,14 @@ jingle_parse(JabberStream *js, const char *from, JabberIqType type,
jingle_actions[action_type].handler(session, jingle);
}
-static void
-jingle_terminate_sessions_gh(gpointer key, gpointer value, gpointer user_data)
-{
- g_object_unref(value);
-}
-
void
jingle_terminate_sessions(JabberStream *js)
{
- if (js->sessions)
- g_hash_table_foreach(js->sessions,
- jingle_terminate_sessions_gh, NULL);
+ if (js->sessions) {
+ GList *list = g_hash_table_get_values(js->sessions);
+ for (; list; list = g_list_delete_link(list, list))
+ g_object_unref(list->data);
+ }
}
#ifdef USE_VV
@@ -456,8 +456,7 @@ jingle_create_relay_info(const gchar *ip, guint port, const gchar *username,
"password", G_TYPE_STRING, password,
"relay-type", G_TYPE_STRING, relay_type,
NULL);
- purple_debug_info("jabber", "created gst_structure %p\n",
- turn_setup);
+ purple_debug_info("jabber", "created gst_structure %p\n", turn_setup);
if (turn_setup) {
memset(&value, 0, sizeof(GValue));
g_value_init(&value, GST_TYPE_STRUCTURE);
diff --git a/libpurple/protocols/jabber/jingle/jingle.h b/libpurple/protocols/jabber/jingle/jingle.h
index acef9b6519..9d82f191f6 100644
--- a/libpurple/protocols/jabber/jingle/jingle.h
+++ b/libpurple/protocols/jabber/jingle/jingle.h
@@ -30,10 +30,6 @@
G_BEGIN_DECLS
-#ifdef __cplusplus
-extern "C" {
-#endif
-
#define JINGLE "urn:xmpp:jingle:1"
#define JINGLE_ERROR "urn:xmpp:jingle:errors:0"
#define JINGLE_APP_FT "urn:xmpp:jingle:apps:file-transfer:1"
@@ -74,7 +70,7 @@ JingleActionType jingle_get_action_type(const gchar *action);
GType jingle_get_type(const gchar *type);
void jingle_parse(JabberStream *js, const char *from, JabberIqType type,
- const char *id, xmlnode *child);
+ const char *id, PurpleXmlNode *child);
void jingle_terminate_sessions(JabberStream *js);
@@ -86,10 +82,6 @@ GParameter *jingle_get_params(JabberStream *js, const gchar *relay_ip,
const gchar *relay_username, const gchar *relay_password, guint *num_params);
#endif
-#ifdef __cplusplus
-}
-#endif
-
G_END_DECLS
#endif /* PURPLE_JABBER_JINGLE_H */
diff --git a/libpurple/protocols/jabber/jingle/rawudp.c b/libpurple/protocols/jabber/jingle/rawudp.c
index b50e5a79a4..a6c971d0a1 100644
--- a/libpurple/protocols/jabber/jingle/rawudp.c
+++ b/libpurple/protocols/jabber/jingle/rawudp.c
@@ -23,6 +23,7 @@
*/
#include "internal.h"
+#include "glibcompat.h"
#include "rawudp.h"
#include "jingle.h"
@@ -43,17 +44,21 @@ static void jingle_rawudp_init (JingleRawUdp *rawudp);
static void jingle_rawudp_finalize (GObject *object);
static void jingle_rawudp_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
static void jingle_rawudp_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
-static JingleTransport *jingle_rawudp_parse_internal(xmlnode *rawudp);
-static xmlnode *jingle_rawudp_to_xml_internal(JingleTransport *transport, xmlnode *content, JingleActionType action);
-
-static JingleTransportClass *parent_class = NULL;
+static JingleTransport *jingle_rawudp_parse_internal(PurpleXmlNode *rawudp);
+static PurpleXmlNode *jingle_rawudp_to_xml_internal(JingleTransport *transport, PurpleXmlNode *content, JingleActionType action);
+static void jingle_rawudp_add_local_candidate(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate);
+static GList *jingle_rawudp_get_remote_candidates(JingleTransport *transport);
enum {
PROP_0,
PROP_LOCAL_CANDIDATES,
PROP_REMOTE_CANDIDATES,
+ PROP_LAST
};
+static JingleTransportClass *parent_class = NULL;
+static GParamSpec *properties[PROP_LAST];
+
static JingleRawUdpCandidate *
jingle_rawudp_candidate_copy(JingleRawUdpCandidate *candidate)
{
@@ -137,20 +142,22 @@ jingle_rawudp_class_init (JingleRawUdpClass *klass)
klass->parent_class.to_xml = jingle_rawudp_to_xml_internal;
klass->parent_class.parse = jingle_rawudp_parse_internal;
klass->parent_class.transport_type = JINGLE_TRANSPORT_RAWUDP;
+ klass->parent_class.add_local_candidate = jingle_rawudp_add_local_candidate;
+ klass->parent_class.get_remote_candidates = jingle_rawudp_get_remote_candidates;
- g_object_class_install_property(gobject_class, PROP_LOCAL_CANDIDATES,
- g_param_spec_pointer("local-candidates",
+ g_type_class_add_private(klass, sizeof(JingleRawUdpPrivate));
+
+ properties[PROP_LOCAL_CANDIDATES] = g_param_spec_pointer("local-candidates",
"Local candidates",
"The local candidates for this transport.",
- G_PARAM_READABLE));
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property(gobject_class, PROP_REMOTE_CANDIDATES,
- g_param_spec_pointer("remote-candidates",
+ properties[PROP_REMOTE_CANDIDATES] = g_param_spec_pointer("remote-candidates",
"Remote candidates",
"The remote candidates for this transport.",
- G_PARAM_READABLE));
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_type_class_add_private(klass, sizeof(JingleRawUdpPrivate));
+ g_object_class_install_properties(gobject_class, PROP_LAST, properties);
}
static void
@@ -218,36 +225,63 @@ jingle_rawudp_get_property (GObject *object, guint prop_id, GValue *value, GPara
}
}
-void
-jingle_rawudp_add_local_candidate(JingleRawUdp *rawudp, JingleRawUdpCandidate *candidate)
+static void
+jingle_rawudp_add_local_candidate(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate)
{
- GList *iter = rawudp->priv->local_candidates;
-
- for (; iter; iter = g_list_next(iter)) {
+ JingleRawUdp *rawudp = JINGLE_RAWUDP(transport);
+ gchar *ip;
+ JingleRawUdpCandidate *rawudp_candidate;
+ GList *iter;
+
+ ip = purple_media_candidate_get_ip(candidate);
+ rawudp_candidate = jingle_rawudp_candidate_new(id, generation,
+ purple_media_candidate_get_component_id(candidate),
+ ip, purple_media_candidate_get_port(candidate));
+ g_free(ip);
+
+ for (iter = rawudp->priv->local_candidates; iter; iter = g_list_next(iter)) {
JingleRawUdpCandidate *c = iter->data;
- if (!strcmp(c->id, candidate->id)) {
- guint generation = c->generation + 1;
+ if (!strcmp(c->id, id)) {
+ generation = c->generation + 1;
g_boxed_free(JINGLE_TYPE_RAWUDP_CANDIDATE, c);
rawudp->priv->local_candidates = g_list_delete_link(
rawudp->priv->local_candidates, iter);
- candidate->generation = generation;
+ rawudp_candidate->generation = generation;
rawudp->priv->local_candidates = g_list_append(
- rawudp->priv->local_candidates, candidate);
+ rawudp->priv->local_candidates, rawudp_candidate);
+
+ g_object_notify_by_pspec(G_OBJECT(rawudp), properties[PROP_LOCAL_CANDIDATES]);
+
return;
}
}
rawudp->priv->local_candidates = g_list_append(
- rawudp->priv->local_candidates, candidate);
+ rawudp->priv->local_candidates, rawudp_candidate);
+
+ g_object_notify_by_pspec(G_OBJECT(rawudp), properties[PROP_LOCAL_CANDIDATES]);
}
-GList *
-jingle_rawudp_get_remote_candidates(JingleRawUdp *rawudp)
+static GList *
+jingle_rawudp_get_remote_candidates(JingleTransport *transport)
{
- return g_list_copy(rawudp->priv->remote_candidates);
+ JingleRawUdp *rawudp = JINGLE_RAWUDP(transport);
+ GList *candidates = rawudp->priv->remote_candidates;
+ GList *ret = NULL;
+
+ for (; candidates; candidates = g_list_next(candidates)) {
+ JingleRawUdpCandidate *candidate = candidates->data;
+ ret = g_list_append(ret, purple_media_candidate_new("",
+ candidate->component,
+ PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX,
+ PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
+ candidate->ip, candidate->port));
+ }
+
+ return ret;
}
static JingleRawUdpCandidate *
@@ -275,22 +309,24 @@ jingle_rawudp_add_remote_candidate(JingleRawUdp *rawudp, JingleRawUdpCandidate *
g_boxed_free(JINGLE_TYPE_RAWUDP_CANDIDATE, rawudp_candidate);
}
priv->remote_candidates = g_list_append(priv->remote_candidates, candidate);
+
+ g_object_notify_by_pspec(G_OBJECT(rawudp), properties[PROP_REMOTE_CANDIDATES]);
}
static JingleTransport *
-jingle_rawudp_parse_internal(xmlnode *rawudp)
+jingle_rawudp_parse_internal(PurpleXmlNode *rawudp)
{
JingleTransport *transport = parent_class->parse(rawudp);
JingleRawUdpPrivate *priv = JINGLE_RAWUDP_GET_PRIVATE(transport);
- xmlnode *candidate = xmlnode_get_child(rawudp, "candidate");
+ PurpleXmlNode *candidate = purple_xmlnode_get_child(rawudp, "candidate");
JingleRawUdpCandidate *rawudp_candidate = NULL;
- for (; candidate; candidate = xmlnode_get_next_twin(candidate)) {
- const gchar *id = xmlnode_get_attrib(candidate, "id");
- const gchar *generation = xmlnode_get_attrib(candidate, "generation");
- const gchar *component = xmlnode_get_attrib(candidate, "component");
- const gchar *ip = xmlnode_get_attrib(candidate, "ip");
- const gchar *port = xmlnode_get_attrib(candidate, "port");
+ for (; candidate; candidate = purple_xmlnode_get_next_twin(candidate)) {
+ const gchar *id = purple_xmlnode_get_attrib(candidate, "id");
+ const gchar *generation = purple_xmlnode_get_attrib(candidate, "generation");
+ const gchar *component = purple_xmlnode_get_attrib(candidate, "component");
+ const gchar *ip = purple_xmlnode_get_attrib(candidate, "ip");
+ const gchar *port = purple_xmlnode_get_attrib(candidate, "port");
if (!id || !generation || !component || !ip || !port)
continue;
@@ -318,10 +354,10 @@ jingle_rawudp_parse_internal(xmlnode *rawudp)
return transport;
}
-static xmlnode *
-jingle_rawudp_to_xml_internal(JingleTransport *transport, xmlnode *content, JingleActionType action)
+static PurpleXmlNode *
+jingle_rawudp_to_xml_internal(JingleTransport *transport, PurpleXmlNode *content, JingleActionType action)
{
- xmlnode *node = parent_class->to_xml(transport, content, action);
+ PurpleXmlNode *node = parent_class->to_xml(transport, content, action);
if (action == JINGLE_SESSION_INITIATE ||
action == JINGLE_TRANSPORT_INFO ||
@@ -331,23 +367,23 @@ jingle_rawudp_to_xml_internal(JingleTransport *transport, xmlnode *content, Jing
for (; iter; iter = g_list_next(iter)) {
JingleRawUdpCandidate *candidate = iter->data;
- xmlnode *xmltransport;
+ PurpleXmlNode *xmltransport;
gchar *generation, *component, *port;
if (candidate->rem_known == TRUE)
continue;
candidate->rem_known = TRUE;
- xmltransport = xmlnode_new_child(node, "candidate");
+ xmltransport = purple_xmlnode_new_child(node, "candidate");
generation = g_strdup_printf("%d", candidate->generation);
component = g_strdup_printf("%d", candidate->component);
port = g_strdup_printf("%d", candidate->port);
- xmlnode_set_attrib(xmltransport, "generation", generation);
- xmlnode_set_attrib(xmltransport, "component", component);
- xmlnode_set_attrib(xmltransport, "id", candidate->id);
- xmlnode_set_attrib(xmltransport, "ip", candidate->ip);
- xmlnode_set_attrib(xmltransport, "port", port);
+ purple_xmlnode_set_attrib(xmltransport, "generation", generation);
+ purple_xmlnode_set_attrib(xmltransport, "component", component);
+ purple_xmlnode_set_attrib(xmltransport, "id", candidate->id);
+ purple_xmlnode_set_attrib(xmltransport, "ip", candidate->ip);
+ purple_xmlnode_set_attrib(xmltransport, "port", port);
g_free(port);
g_free(generation);
diff --git a/libpurple/protocols/jabber/jingle/rawudp.h b/libpurple/protocols/jabber/jingle/rawudp.h
index 785ba3ddf6..9db1bc816c 100644
--- a/libpurple/protocols/jabber/jingle/rawudp.h
+++ b/libpurple/protocols/jabber/jingle/rawudp.h
@@ -54,8 +54,8 @@ struct _JingleRawUdpClass
{
JingleTransportClass parent_class; /**< The parent class. */
- xmlnode *(*to_xml) (JingleTransport *transport, xmlnode *content, JingleActionType action);
- JingleTransport *(*parse) (xmlnode *transport);
+ PurpleXmlNode *(*to_xml) (JingleTransport *transport, PurpleXmlNode *content, JingleActionType action);
+ JingleTransport *(*parse) (PurpleXmlNode *transport);
};
/** The rawudp class's private data */
@@ -77,10 +77,6 @@ struct _JingleRawUdpCandidate
* about this candidate */
};
-#ifdef __cplusplus
-extern "C" {
-#endif
-
GType jingle_rawudp_candidate_get_type(void);
/**
@@ -92,12 +88,6 @@ GType jingle_rawudp_get_type(void);
JingleRawUdpCandidate *jingle_rawudp_candidate_new(const gchar *id,
guint generation, guint component, const gchar *ip, guint port);
-void jingle_rawudp_add_local_candidate(JingleRawUdp *rawudp, JingleRawUdpCandidate *candidate);
-GList *jingle_rawudp_get_remote_candidates(JingleRawUdp *rawudp);
-
-#ifdef __cplusplus
-}
-#endif
G_END_DECLS
diff --git a/libpurple/protocols/jabber/jingle/rtp.c b/libpurple/protocols/jabber/jingle/rtp.c
index 57783abe41..ad77365f4d 100644
--- a/libpurple/protocols/jabber/jingle/rtp.c
+++ b/libpurple/protocols/jabber/jingle/rtp.c
@@ -22,12 +22,14 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#include "config.h"
+#include "internal.h"
+#include "glibcompat.h"
#ifdef USE_VV
#include "jabber.h"
#include "jingle.h"
+#include "google/google_p2p.h"
#include "media.h"
#include "mediamanager.h"
#include "iceudp.h"
@@ -51,13 +53,12 @@ static void jingle_rtp_init (JingleRtp *rtp);
static void jingle_rtp_finalize (GObject *object);
static void jingle_rtp_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
static void jingle_rtp_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
-static JingleContent *jingle_rtp_parse_internal(xmlnode *rtp);
-static xmlnode *jingle_rtp_to_xml_internal(JingleContent *rtp, xmlnode *content, JingleActionType action);
-static void jingle_rtp_handle_action_internal(JingleContent *content, xmlnode *jingle, JingleActionType action);
+static JingleContent *jingle_rtp_parse_internal(PurpleXmlNode *rtp);
+static PurpleXmlNode *jingle_rtp_to_xml_internal(JingleContent *rtp, PurpleXmlNode *content, JingleActionType action);
+static void jingle_rtp_handle_action_internal(JingleContent *content, PurpleXmlNode *jingle, JingleActionType action);
static PurpleMedia *jingle_rtp_get_media(JingleSession *session);
-static JingleContentClass *parent_class = NULL;
#if 0
enum {
LAST_SIGNAL
@@ -69,8 +70,12 @@ enum {
PROP_0,
PROP_MEDIA_TYPE,
PROP_SSRC,
+ PROP_LAST
};
+static JingleContentClass *parent_class = NULL;
+static GParamSpec *properties[PROP_LAST];
+
GType
jingle_rtp_get_type()
{
@@ -108,20 +113,21 @@ jingle_rtp_class_init (JingleRtpClass *klass)
klass->parent_class.description_type = JINGLE_APP_RTP;
klass->parent_class.handle_action = jingle_rtp_handle_action_internal;
- g_object_class_install_property(gobject_class, PROP_MEDIA_TYPE,
- g_param_spec_string("media-type",
+ g_type_class_add_private(klass, sizeof(JingleRtpPrivate));
+
+ properties[PROP_MEDIA_TYPE] = g_param_spec_string("media-type",
"Media Type",
"The media type (\"audio\" or \"video\") for this rtp session.",
NULL,
- G_PARAM_READWRITE));
- g_object_class_install_property(gobject_class, PROP_SSRC,
- g_param_spec_string("ssrc",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_SSRC] = g_param_spec_string("ssrc",
"ssrc",
"The ssrc for this rtp session.",
NULL,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_type_class_add_private(klass, sizeof(JingleRtpPrivate));
+ g_object_class_install_properties(gobject_class, PROP_LAST, properties);
}
static void
@@ -226,134 +232,23 @@ jingle_rtp_get_media(JingleSession *session)
return media;
}
-static JingleRawUdpCandidate *
-jingle_rtp_candidate_to_rawudp(JingleSession *session, guint generation,
- PurpleMediaCandidate *candidate)
-{
- gchar *id = jabber_get_next_id(jingle_session_get_js(session));
- gchar *ip = purple_media_candidate_get_ip(candidate);
- JingleRawUdpCandidate *rawudp_candidate =
- jingle_rawudp_candidate_new(id, generation,
- purple_media_candidate_get_component_id(candidate),
- ip, purple_media_candidate_get_port(candidate));
- g_free(ip);
- g_free(id);
- return rawudp_candidate;
-}
-
-static JingleIceUdpCandidate *
-jingle_rtp_candidate_to_iceudp(JingleSession *session, guint generation,
- PurpleMediaCandidate *candidate)
-{
- gchar *id = jabber_get_next_id(jingle_session_get_js(session));
- gchar *ip = purple_media_candidate_get_ip(candidate);
- gchar *username = purple_media_candidate_get_username(candidate);
- gchar *password = purple_media_candidate_get_password(candidate);
- PurpleMediaCandidateType type =
- purple_media_candidate_get_candidate_type(candidate);
-
- JingleIceUdpCandidate *iceudp_candidate = jingle_iceudp_candidate_new(
- purple_media_candidate_get_component_id(candidate),
- purple_media_candidate_get_foundation(candidate),
- generation, id, ip, 0,
- purple_media_candidate_get_port(candidate),
- purple_media_candidate_get_priority(candidate), "udp",
- type == PURPLE_MEDIA_CANDIDATE_TYPE_HOST ? "host" :
- type == PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX ? "srflx" :
- type == PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX ? "prflx" :
- type == PURPLE_MEDIA_CANDIDATE_TYPE_RELAY ? "relay" :
- "", username, password);
- iceudp_candidate->reladdr =
- purple_media_candidate_get_base_ip(candidate);
- iceudp_candidate->relport =
- purple_media_candidate_get_base_port(candidate);
- g_free(password);
- g_free(username);
- g_free(ip);
- g_free(id);
- return iceudp_candidate;
-}
-
static JingleTransport *
-jingle_rtp_candidates_to_transport(JingleSession *session, GType type, guint generation, GList *candidates)
-{
- if (type == JINGLE_TYPE_RAWUDP) {
- JingleTransport *transport = jingle_transport_create(JINGLE_TRANSPORT_RAWUDP);
- JingleRawUdpCandidate *rawudp_candidate;
- for (; candidates; candidates = g_list_next(candidates)) {
- PurpleMediaCandidate *candidate = candidates->data;
- rawudp_candidate = jingle_rtp_candidate_to_rawudp(
- session, generation, candidate);
- jingle_rawudp_add_local_candidate(
- JINGLE_RAWUDP(transport),
- rawudp_candidate);
- }
- return transport;
- } else if (type == JINGLE_TYPE_ICEUDP) {
- JingleTransport *transport = jingle_transport_create(JINGLE_TRANSPORT_ICEUDP);
- JingleIceUdpCandidate *iceudp_candidate;
- for (; candidates; candidates = g_list_next(candidates)) {
- PurpleMediaCandidate *candidate = candidates->data;
- iceudp_candidate = jingle_rtp_candidate_to_iceudp(
- session, generation, candidate);
- jingle_iceudp_add_local_candidate(
- JINGLE_ICEUDP(transport),
- iceudp_candidate);
- }
- return transport;
- } else {
- return NULL;
- }
-}
-
-static GList *
-jingle_rtp_transport_to_candidates(JingleTransport *transport)
+jingle_rtp_candidates_to_transport(JingleSession *session, const gchar *type, guint generation, GList *candidates)
{
- const gchar *type = jingle_transport_get_transport_type(transport);
- GList *ret = NULL;
- if (!strcmp(type, JINGLE_TRANSPORT_RAWUDP)) {
- GList *candidates = jingle_rawudp_get_remote_candidates(JINGLE_RAWUDP(transport));
-
- for (; candidates; candidates = g_list_delete_link(candidates, candidates)) {
- JingleRawUdpCandidate *candidate = candidates->data;
- ret = g_list_append(ret, purple_media_candidate_new(
- "", candidate->component,
- PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX,
- PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
- candidate->ip, candidate->port));
- }
-
- return ret;
- } else if (!strcmp(type, JINGLE_TRANSPORT_ICEUDP)) {
- GList *candidates = jingle_iceudp_get_remote_candidates(JINGLE_ICEUDP(transport));
-
- for (; candidates; candidates = g_list_delete_link(candidates, candidates)) {
- JingleIceUdpCandidate *candidate = candidates->data;
- PurpleMediaCandidate *new_candidate = purple_media_candidate_new(
- candidate->foundation, candidate->component,
- !strcmp(candidate->type, "host") ?
- PURPLE_MEDIA_CANDIDATE_TYPE_HOST :
- !strcmp(candidate->type, "srflx") ?
- PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX :
- !strcmp(candidate->type, "prflx") ?
- PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX :
- !strcmp(candidate->type, "relay") ?
- PURPLE_MEDIA_CANDIDATE_TYPE_RELAY : 0,
- PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
- candidate->ip, candidate->port);
- g_object_set(new_candidate,
- "base-ip", candidate->reladdr,
- "base-port", candidate->relport,
- "username", candidate->username,
- "password", candidate->password,
- "priority", candidate->priority, NULL);
- ret = g_list_append(ret, new_candidate);
- }
+ JingleTransport *transport;
- return ret;
- } else {
+ transport = jingle_transport_create(type);
+ if (!transport)
return NULL;
+
+ for (; candidates; candidates = g_list_next(candidates)) {
+ PurpleMediaCandidate *candidate = candidates->data;
+ gchar *id = jabber_get_next_id(jingle_session_get_js(session));
+ jingle_transport_add_local_candidate(transport, id, generation, candidate);
+ g_free(id);
}
+
+ return transport;
}
static void jingle_rtp_ready(JingleSession *session);
@@ -378,10 +273,9 @@ jingle_rtp_candidates_prepared_cb(PurpleMedia *media,
oldtransport = jingle_content_get_transport(content);
candidates = purple_media_get_local_candidates(media, sid, name);
- transport = JINGLE_TRANSPORT(jingle_rtp_candidates_to_transport(
- session, JINGLE_IS_RAWUDP(oldtransport) ?
- JINGLE_TYPE_RAWUDP : JINGLE_TYPE_ICEUDP,
- 0, candidates));
+ transport = jingle_rtp_candidates_to_transport(
+ session, jingle_transport_get_transport_type(oldtransport),
+ 0, candidates);
g_list_free(candidates);
g_object_unref(oldtransport);
@@ -404,9 +298,9 @@ jingle_rtp_codecs_changed_cb(PurpleMedia *media, gchar *sid,
static void
jingle_rtp_new_candidate_cb(PurpleMedia *media, gchar *sid, gchar *name, PurpleMediaCandidate *candidate, JingleSession *session)
{
- JingleContent *content = jingle_session_find_content(
- session, sid, NULL);
+ JingleContent *content = jingle_session_find_content(session, sid, NULL);
JingleTransport *transport;
+ gchar *id;
purple_debug_info("jingle-rtp", "jingle_rtp_new_candidate_cb\n");
@@ -419,29 +313,23 @@ jingle_rtp_new_candidate_cb(PurpleMedia *media, gchar *sid, gchar *name, PurpleM
transport = jingle_content_get_transport(content);
- if (JINGLE_IS_ICEUDP(transport))
- jingle_iceudp_add_local_candidate(JINGLE_ICEUDP(transport),
- jingle_rtp_candidate_to_iceudp(
- session, 1, candidate));
- else if (JINGLE_IS_RAWUDP(transport))
- jingle_rawudp_add_local_candidate(JINGLE_RAWUDP(transport),
- jingle_rtp_candidate_to_rawudp(
- session, 1, candidate));
+ id = jabber_get_next_id(jingle_session_get_js(session));
+ jingle_transport_add_local_candidate(transport, id, 1, candidate);
+ g_free(id);
g_object_unref(transport);
- jabber_iq_send(jingle_session_to_packet(session,
- JINGLE_TRANSPORT_INFO));
+ jabber_iq_send(jingle_session_to_packet(session, JINGLE_TRANSPORT_INFO));
}
static void
jingle_rtp_initiate_ack_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
JingleSession *session = data;
- if (type == JABBER_IQ_ERROR || xmlnode_get_child(packet, "error")) {
+ if (type == JABBER_IQ_ERROR || purple_xmlnode_get_child(packet, "error")) {
purple_media_end(jingle_rtp_get_media(session), NULL, NULL);
g_object_unref(session);
return;
@@ -604,6 +492,8 @@ jingle_rtp_init_media(JingleContent *content)
transmitter = "rawudp";
else if (JINGLE_IS_ICEUDP(transport))
transmitter = "nice";
+ else if (JINGLE_IS_GOOGLE_P2P(transport))
+ transmitter = "nice";
else
transmitter = "notransmitter";
g_object_unref(transport);
@@ -661,13 +551,13 @@ jingle_rtp_init_media(JingleContent *content)
}
static GList *
-jingle_rtp_parse_codecs(xmlnode *description)
+jingle_rtp_parse_codecs(PurpleXmlNode *description)
{
GList *codecs = NULL;
- xmlnode *codec_element = NULL;
+ PurpleXmlNode *codec_element = NULL;
const char *encoding_name,*id, *clock_rate;
PurpleMediaCodec *codec;
- const gchar *media = xmlnode_get_attrib(description, "media");
+ const gchar *media = purple_xmlnode_get_attrib(description, "media");
PurpleMediaSessionType type;
if (media == NULL) {
@@ -685,25 +575,25 @@ jingle_rtp_parse_codecs(xmlnode *description)
return NULL;
}
- for (codec_element = xmlnode_get_child(description, "payload-type") ;
+ for (codec_element = purple_xmlnode_get_child(description, "payload-type") ;
codec_element ;
- codec_element = xmlnode_get_next_twin(codec_element)) {
- xmlnode *param;
+ codec_element = purple_xmlnode_get_next_twin(codec_element)) {
+ PurpleXmlNode *param;
gchar *codec_str;
- encoding_name = xmlnode_get_attrib(codec_element, "name");
+ encoding_name = purple_xmlnode_get_attrib(codec_element, "name");
- id = xmlnode_get_attrib(codec_element, "id");
- clock_rate = xmlnode_get_attrib(codec_element, "clockrate");
+ id = purple_xmlnode_get_attrib(codec_element, "id");
+ clock_rate = purple_xmlnode_get_attrib(codec_element, "clockrate");
codec = purple_media_codec_new(atoi(id), encoding_name,
type,
clock_rate ? atoi(clock_rate) : 0);
- for (param = xmlnode_get_child(codec_element, "parameter");
- param; param = xmlnode_get_next_twin(param)) {
+ for (param = purple_xmlnode_get_child(codec_element, "parameter");
+ param; param = purple_xmlnode_get_next_twin(param)) {
purple_media_codec_add_optional_parameter(codec,
- xmlnode_get_attrib(param, "name"),
- xmlnode_get_attrib(param, "value"));
+ purple_xmlnode_get_attrib(param, "name"),
+ purple_xmlnode_get_attrib(param, "value"));
}
codec_str = purple_media_codec_to_string(codec);
@@ -716,12 +606,12 @@ jingle_rtp_parse_codecs(xmlnode *description)
}
static JingleContent *
-jingle_rtp_parse_internal(xmlnode *rtp)
+jingle_rtp_parse_internal(PurpleXmlNode *rtp)
{
JingleContent *content = parent_class->parse(rtp);
- xmlnode *description = xmlnode_get_child(rtp, "description");
- const gchar *media_type = xmlnode_get_attrib(description, "media");
- const gchar *ssrc = xmlnode_get_attrib(description, "ssrc");
+ PurpleXmlNode *description = purple_xmlnode_get_child(rtp, "description");
+ const gchar *media_type = purple_xmlnode_get_attrib(description, "media");
+ const gchar *ssrc = purple_xmlnode_get_attrib(description, "ssrc");
purple_debug_info("jingle-rtp", "rtp parse\n");
g_object_set(content, "media-type", media_type, NULL);
if (ssrc != NULL)
@@ -730,14 +620,14 @@ jingle_rtp_parse_internal(xmlnode *rtp)
}
static void
-jingle_rtp_add_payloads(xmlnode *description, GList *codecs)
+jingle_rtp_add_payloads(PurpleXmlNode *description, GList *codecs)
{
for (; codecs ; codecs = codecs->next) {
PurpleMediaCodec *codec = (PurpleMediaCodec*)codecs->data;
GList *iter = purple_media_codec_get_optional_parameters(codec);
gchar *id, *name, *clockrate, *channels;
gchar *codec_str;
- xmlnode *payload = xmlnode_new_child(description, "payload-type");
+ PurpleXmlNode *payload = purple_xmlnode_new_child(description, "payload-type");
id = g_strdup_printf("%d",
purple_media_codec_get_id(codec));
@@ -747,10 +637,10 @@ jingle_rtp_add_payloads(xmlnode *description, GList *codecs)
channels = g_strdup_printf("%d",
purple_media_codec_get_channels(codec));
- xmlnode_set_attrib(payload, "name", name);
- xmlnode_set_attrib(payload, "id", id);
- xmlnode_set_attrib(payload, "clockrate", clockrate);
- xmlnode_set_attrib(payload, "channels", channels);
+ purple_xmlnode_set_attrib(payload, "name", name);
+ purple_xmlnode_set_attrib(payload, "id", id);
+ purple_xmlnode_set_attrib(payload, "clockrate", clockrate);
+ purple_xmlnode_set_attrib(payload, "channels", channels);
g_free(channels);
g_free(clockrate);
@@ -759,9 +649,9 @@ jingle_rtp_add_payloads(xmlnode *description, GList *codecs)
for (; iter; iter = g_list_next(iter)) {
PurpleKeyValuePair *mparam = iter->data;
- xmlnode *param = xmlnode_new_child(payload, "parameter");
- xmlnode_set_attrib(param, "name", mparam->key);
- xmlnode_set_attrib(param, "value", mparam->value);
+ PurpleXmlNode *param = purple_xmlnode_new_child(payload, "parameter");
+ purple_xmlnode_set_attrib(param, "name", mparam->key);
+ purple_xmlnode_set_attrib(param, "value", mparam->value);
}
codec_str = purple_media_codec_to_string(codec);
@@ -770,11 +660,11 @@ jingle_rtp_add_payloads(xmlnode *description, GList *codecs)
}
}
-static xmlnode *
-jingle_rtp_to_xml_internal(JingleContent *rtp, xmlnode *content, JingleActionType action)
+static PurpleXmlNode *
+jingle_rtp_to_xml_internal(JingleContent *rtp, PurpleXmlNode *content, JingleActionType action)
{
- xmlnode *node = parent_class->to_xml(rtp, content, action);
- xmlnode *description = xmlnode_get_child(node, "description");
+ PurpleXmlNode *node = parent_class->to_xml(rtp, content, action);
+ PurpleXmlNode *description = purple_xmlnode_get_child(node, "description");
if (description != NULL) {
JingleSession *session = jingle_content_get_session(rtp);
PurpleMedia *media = jingle_rtp_get_media(session);
@@ -783,10 +673,10 @@ jingle_rtp_to_xml_internal(JingleContent *rtp, xmlnode *content, JingleActionTyp
gchar *name = jingle_content_get_name(rtp);
GList *codecs = purple_media_get_codecs(media, name);
- xmlnode_set_attrib(description, "media", media_type);
+ purple_xmlnode_set_attrib(description, "media", media_type);
if (ssrc != NULL)
- xmlnode_set_attrib(description, "ssrc", ssrc);
+ purple_xmlnode_set_attrib(description, "ssrc", ssrc);
g_free(media_type);
g_free(name);
@@ -799,14 +689,14 @@ jingle_rtp_to_xml_internal(JingleContent *rtp, xmlnode *content, JingleActionTyp
}
static void
-jingle_rtp_handle_action_internal(JingleContent *content, xmlnode *xmlcontent, JingleActionType action)
+jingle_rtp_handle_action_internal(JingleContent *content, PurpleXmlNode *xmlcontent, JingleActionType action)
{
switch (action) {
case JINGLE_SESSION_ACCEPT:
case JINGLE_SESSION_INITIATE: {
JingleSession *session;
JingleTransport *transport;
- xmlnode *description;
+ PurpleXmlNode *description;
GList *candidates;
GList *codecs;
gchar *name;
@@ -825,9 +715,9 @@ jingle_rtp_handle_action_internal(JingleContent *content, xmlnode *xmlcontent, J
}
transport = jingle_transport_parse(
- xmlnode_get_child(xmlcontent, "transport"));
- description = xmlnode_get_child(xmlcontent, "description");
- candidates = jingle_rtp_transport_to_candidates(transport);
+ purple_xmlnode_get_child(xmlcontent, "transport"));
+ description = purple_xmlnode_get_child(xmlcontent, "description");
+ candidates = jingle_transport_get_remote_candidates(transport);
codecs = jingle_rtp_parse_codecs(description);
name = jingle_content_get_name(content);
remote_jid = jingle_session_get_remote_jid(session);
@@ -862,8 +752,8 @@ jingle_rtp_handle_action_internal(JingleContent *content, xmlnode *xmlcontent, J
case JINGLE_TRANSPORT_INFO: {
JingleSession *session = jingle_content_get_session(content);
JingleTransport *transport = jingle_transport_parse(
- xmlnode_get_child(xmlcontent, "transport"));
- GList *candidates = jingle_rtp_transport_to_candidates(transport);
+ purple_xmlnode_get_child(xmlcontent, "transport"));
+ GList *candidates = jingle_transport_get_remote_candidates(transport);
gchar *name = jingle_content_get_name(content);
gchar *remote_jid =
jingle_session_get_remote_jid(session);
@@ -880,7 +770,7 @@ jingle_rtp_handle_action_internal(JingleContent *content, xmlnode *xmlcontent, J
case JINGLE_DESCRIPTION_INFO: {
JingleSession *session =
jingle_content_get_session(content);
- xmlnode *description = xmlnode_get_child(
+ PurpleXmlNode *description = purple_xmlnode_get_child(
xmlcontent, "description");
GList *codecs, *iter, *iter2, *remote_codecs =
jingle_rtp_parse_codecs(description);
@@ -974,6 +864,8 @@ jingle_rtp_initiate_media(JabberStream *js, const gchar *who,
transport_type = JINGLE_TRANSPORT_ICEUDP;
} else if (jabber_resource_has_capability(jbr, JINGLE_TRANSPORT_RAWUDP)) {
transport_type = JINGLE_TRANSPORT_RAWUDP;
+ } else if (jabber_resource_has_capability(jbr, NS_GOOGLE_TRANSPORT_P2P)) {
+ transport_type = NS_GOOGLE_TRANSPORT_P2P;
} else {
purple_debug_error("jingle-rtp", "Resource doesn't support "
"the same transport types\n");
@@ -995,6 +887,7 @@ jingle_rtp_initiate_media(JabberStream *js, const gchar *who,
jingle_session_add_content(session, content);
JINGLE_RTP(content)->priv->media_type = g_strdup("audio");
jingle_rtp_init_media(content);
+ g_object_notify_by_pspec(G_OBJECT(content), properties[PROP_MEDIA_TYPE]);
}
if (type & PURPLE_MEDIA_VIDEO) {
transport = jingle_transport_create(transport_type);
@@ -1003,6 +896,7 @@ jingle_rtp_initiate_media(JabberStream *js, const gchar *who,
jingle_session_add_content(session, content);
JINGLE_RTP(content)->priv->media_type = g_strdup("video");
jingle_rtp_init_media(content);
+ g_object_notify_by_pspec(G_OBJECT(content), properties[PROP_MEDIA_TYPE]);
}
g_free(me);
diff --git a/libpurple/protocols/jabber/jingle/rtp.h b/libpurple/protocols/jabber/jingle/rtp.h
index a372711a3f..2dcd528be7 100644
--- a/libpurple/protocols/jabber/jingle/rtp.h
+++ b/libpurple/protocols/jabber/jingle/rtp.h
@@ -65,10 +65,6 @@ struct _JingleRtp
JingleRtpPrivate *priv; /**< The private data of this object. */
};
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* Gets the rtp class's GType
*
@@ -84,10 +80,6 @@ gboolean jingle_rtp_initiate_media(JabberStream *js,
PurpleMediaSessionType type);
void jingle_rtp_terminate_session(JabberStream *js, const gchar *who);
-#ifdef __cplusplus
-}
-#endif
-
G_END_DECLS
#endif /* USE_VV */
diff --git a/libpurple/protocols/jabber/jingle/session.c b/libpurple/protocols/jabber/jingle/session.c
index 5d612aa476..2b8708fa1b 100644
--- a/libpurple/protocols/jabber/jingle/session.c
+++ b/libpurple/protocols/jabber/jingle/session.c
@@ -23,6 +23,7 @@
*/
#include "internal.h"
+#include "glibcompat.h"
#include "content.h"
#include "debug.h"
@@ -51,8 +52,6 @@ static void jingle_session_finalize (GObject *object);
static void jingle_session_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
static void jingle_session_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
-static GObjectClass *parent_class = NULL;
-
enum {
PROP_0,
PROP_SID,
@@ -63,8 +62,12 @@ enum {
PROP_STATE,
PROP_CONTENTS,
PROP_PENDING_CONTENTS,
+ PROP_LAST
};
+static GObjectClass *parent_class = NULL;
+static GParamSpec *properties[PROP_LAST];
+
GType
jingle_session_get_type()
{
@@ -98,60 +101,54 @@ jingle_session_class_init (JingleSessionClass *klass)
gobject_class->set_property = jingle_session_set_property;
gobject_class->get_property = jingle_session_get_property;
- g_object_class_install_property(gobject_class, PROP_SID,
- g_param_spec_string("sid",
+ g_type_class_add_private(klass, sizeof(JingleSessionPrivate));
+
+ properties[PROP_SID] = g_param_spec_string("sid",
"Session ID",
"The unique session ID of the Jingle Session.",
NULL,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property(gobject_class, PROP_JS,
- g_param_spec_pointer("js",
+ properties[PROP_JS] = g_param_spec_pointer("js",
"JabberStream",
"The Jabber stream associated with this session.",
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property(gobject_class, PROP_REMOTE_JID,
- g_param_spec_string("remote-jid",
+ properties[PROP_REMOTE_JID] = g_param_spec_string("remote-jid",
"Remote JID",
"The JID of the remote participant.",
NULL,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property(gobject_class, PROP_LOCAL_JID,
- g_param_spec_string("local-jid",
+ properties[PROP_LOCAL_JID] = g_param_spec_string("local-jid",
"Local JID",
"The JID of the local participant.",
NULL,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property(gobject_class, PROP_IS_INITIATOR,
- g_param_spec_boolean("is-initiator",
+ properties[PROP_IS_INITIATOR] = g_param_spec_boolean("is-initiator",
"Is Initiator",
"Whether or not the local JID is the initiator of the session.",
FALSE,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property(gobject_class, PROP_STATE,
- g_param_spec_boolean("state",
+ properties[PROP_STATE] = g_param_spec_boolean("state",
"State",
"The state of the session (PENDING=FALSE, ACTIVE=TRUE).",
FALSE,
- G_PARAM_READABLE));
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property(gobject_class, PROP_CONTENTS,
- g_param_spec_pointer("contents",
+ properties[PROP_CONTENTS] = g_param_spec_pointer("contents",
"Contents",
"The active contents contained within this session",
- G_PARAM_READABLE));
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property(gobject_class, PROP_PENDING_CONTENTS,
- g_param_spec_pointer("pending-contents",
+ properties[PROP_PENDING_CONTENTS] = g_param_spec_pointer("pending-contents",
"Pending contents",
"The pending contents contained within this session",
- G_PARAM_READABLE));
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_type_class_add_private(klass, sizeof(JingleSessionPrivate));
+ g_object_class_install_properties(gobject_class, PROP_LAST, properties);
}
static void
@@ -402,29 +399,29 @@ jingle_session_find_by_jid(JabberStream *js, const gchar *jid)
find_by_jid_ghr, (gpointer)jid) : NULL;
}
-static xmlnode *
+static PurpleXmlNode *
jingle_add_jingle_packet(JingleSession *session,
JabberIq *iq, JingleActionType action)
{
- xmlnode *jingle = iq ?
- xmlnode_new_child(iq->node, "jingle") :
- xmlnode_new("jingle");
+ PurpleXmlNode *jingle = iq ?
+ purple_xmlnode_new_child(iq->node, "jingle") :
+ purple_xmlnode_new("jingle");
gchar *local_jid = jingle_session_get_local_jid(session);
gchar *remote_jid = jingle_session_get_remote_jid(session);
gchar *sid = jingle_session_get_sid(session);
- xmlnode_set_namespace(jingle, JINGLE);
- xmlnode_set_attrib(jingle, "action", jingle_get_action_name(action));
+ purple_xmlnode_set_namespace(jingle, JINGLE);
+ purple_xmlnode_set_attrib(jingle, "action", jingle_get_action_name(action));
if (jingle_session_is_initiator(session)) {
- xmlnode_set_attrib(jingle, "initiator", local_jid);
- xmlnode_set_attrib(jingle, "responder", remote_jid);
+ purple_xmlnode_set_attrib(jingle, "initiator", local_jid);
+ purple_xmlnode_set_attrib(jingle, "responder", remote_jid);
} else {
- xmlnode_set_attrib(jingle, "initiator", remote_jid);
- xmlnode_set_attrib(jingle, "responder", local_jid);
+ purple_xmlnode_set_attrib(jingle, "initiator", remote_jid);
+ purple_xmlnode_set_attrib(jingle, "responder", local_jid);
}
- xmlnode_set_attrib(jingle, "sid", sid);
+ purple_xmlnode_set_attrib(jingle, "sid", sid);
g_free(local_jid);
g_free(remote_jid);
@@ -434,15 +431,15 @@ jingle_add_jingle_packet(JingleSession *session,
}
JabberIq *
-jingle_session_create_ack(JingleSession *session, const xmlnode *jingle)
+jingle_session_create_ack(JingleSession *session, const PurpleXmlNode *jingle)
{
JabberIq *result = jabber_iq_new(
jingle_session_get_js(session),
JABBER_IQ_RESULT);
- xmlnode *packet = xmlnode_get_parent(jingle);
- jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
- xmlnode_set_attrib(result->node, "from", xmlnode_get_attrib(packet, "to"));
- xmlnode_set_attrib(result->node, "to", xmlnode_get_attrib(packet, "from"));
+ PurpleXmlNode *packet = purple_xmlnode_get_parent(jingle);
+ jabber_iq_set_id(result, purple_xmlnode_get_attrib(packet, "id"));
+ purple_xmlnode_set_attrib(result->node, "from", purple_xmlnode_get_attrib(packet, "to"));
+ purple_xmlnode_set_attrib(result->node, "to", purple_xmlnode_get_attrib(packet, "from"));
return result;
}
@@ -454,16 +451,16 @@ jingle_create_iq(JingleSession *session)
gchar *from = jingle_session_get_local_jid(session);
gchar *to = jingle_session_get_remote_jid(session);
- xmlnode_set_attrib(result->node, "from", from);
- xmlnode_set_attrib(result->node, "to", to);
+ purple_xmlnode_set_attrib(result->node, "from", from);
+ purple_xmlnode_set_attrib(result->node, "to", to);
g_free(from);
g_free(to);
return result;
}
-xmlnode *
-jingle_session_to_xml(JingleSession *session, xmlnode *jingle, JingleActionType action)
+PurpleXmlNode *
+jingle_session_to_xml(JingleSession *session, PurpleXmlNode *jingle, JingleActionType action)
{
if (action != JINGLE_SESSION_INFO && action != JINGLE_SESSION_TERMINATE) {
GList *iter;
@@ -485,12 +482,12 @@ JabberIq *
jingle_session_to_packet(JingleSession *session, JingleActionType action)
{
JabberIq *iq = jingle_create_iq(session);
- xmlnode *jingle = jingle_add_jingle_packet(session, iq, action);
+ PurpleXmlNode *jingle = jingle_add_jingle_packet(session, iq, action);
jingle_session_to_xml(session, jingle, action);
return iq;
}
-void jingle_session_handle_action(JingleSession *session, xmlnode *jingle, JingleActionType action)
+void jingle_session_handle_action(JingleSession *session, PurpleXmlNode *jingle, JingleActionType action)
{
GList *iter;
if (action == JINGLE_CONTENT_ADD || action == JINGLE_CONTENT_REMOVE)
@@ -563,6 +560,8 @@ jingle_session_add_content(JingleSession *session, JingleContent* content)
session->priv->contents =
g_list_append(session->priv->contents, content);
jingle_content_set_session(content, session);
+
+ g_object_notify_by_pspec(G_OBJECT(session), properties[PROP_CONTENTS]);
}
void
@@ -575,6 +574,8 @@ jingle_session_remove_content(JingleSession *session, const gchar *name, const g
session->priv->contents =
g_list_remove(session->priv->contents, content);
g_object_unref(content);
+
+ g_object_notify_by_pspec(G_OBJECT(session), properties[PROP_CONTENTS]);
}
}
@@ -584,6 +585,8 @@ jingle_session_add_pending_content(JingleSession *session, JingleContent* conten
session->priv->pending_contents =
g_list_append(session->priv->pending_contents, content);
jingle_content_set_session(content, session);
+
+ g_object_notify_by_pspec(G_OBJECT(session), properties[PROP_PENDING_CONTENTS]);
}
void
@@ -595,6 +598,8 @@ jingle_session_remove_pending_content(JingleSession *session, const gchar *name,
session->priv->pending_contents =
g_list_remove(session->priv->pending_contents, content);
g_object_unref(content);
+
+ g_object_notify_by_pspec(G_OBJECT(session), properties[PROP_PENDING_CONTENTS]);
}
}
@@ -614,6 +619,8 @@ void
jingle_session_accept_session(JingleSession *session)
{
session->priv->state = TRUE;
+
+ g_object_notify_by_pspec(G_OBJECT(session), properties[PROP_STATE]);
}
JabberIq *
@@ -621,12 +628,12 @@ jingle_session_terminate_packet(JingleSession *session, const gchar *reason)
{
JabberIq *iq = jingle_session_to_packet(session,
JINGLE_SESSION_TERMINATE);
- xmlnode *jingle = xmlnode_get_child(iq->node, "jingle");
+ PurpleXmlNode *jingle = purple_xmlnode_get_child(iq->node, "jingle");
if (reason != NULL) {
- xmlnode *reason_node;
- reason_node = xmlnode_new_child(jingle, "reason");
- xmlnode_new_child(reason_node, reason);
+ PurpleXmlNode *reason_node;
+ reason_node = purple_xmlnode_new_child(jingle, "reason");
+ purple_xmlnode_new_child(reason_node, reason);
}
return iq;
}
@@ -636,17 +643,17 @@ jingle_session_redirect_packet(JingleSession *session, const gchar *sid)
{
JabberIq *iq = jingle_session_terminate_packet(session,
"alternative-session");
- xmlnode *alt_session;
+ PurpleXmlNode *alt_session;
if (sid == NULL)
return iq;
- alt_session = xmlnode_get_child(iq->node,
+ alt_session = purple_xmlnode_get_child(iq->node,
"jingle/reason/alternative-session");
if (alt_session != NULL) {
- xmlnode *sid_node = xmlnode_new_child(alt_session, "sid");
- xmlnode_insert_data(sid_node, sid, -1);
+ PurpleXmlNode *sid_node = purple_xmlnode_new_child(alt_session, "sid");
+ purple_xmlnode_insert_data(sid_node, sid, -1);
}
return iq;
}
diff --git a/libpurple/protocols/jabber/jingle/session.h b/libpurple/protocols/jabber/jingle/session.h
index 51e5a71e95..c5cb0c7c31 100644
--- a/libpurple/protocols/jabber/jingle/session.h
+++ b/libpurple/protocols/jabber/jingle/session.h
@@ -62,10 +62,6 @@ struct _JingleSession
struct _JingleContent;
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* Gets the session class's GType
*
@@ -89,11 +85,11 @@ GList *jingle_session_get_pending_contents(JingleSession *session);
JingleSession *jingle_session_find_by_sid(JabberStream *js, const gchar *sid);
JingleSession *jingle_session_find_by_jid(JabberStream *js, const gchar *jid);
-JabberIq *jingle_session_create_ack(JingleSession *session, const xmlnode *jingle);
-xmlnode *jingle_session_to_xml(JingleSession *session, xmlnode *parent, JingleActionType action);
+JabberIq *jingle_session_create_ack(JingleSession *session, const PurpleXmlNode *jingle);
+PurpleXmlNode *jingle_session_to_xml(JingleSession *session, PurpleXmlNode *parent, JingleActionType action);
JabberIq *jingle_session_to_packet(JingleSession *session, JingleActionType action);
-void jingle_session_handle_action(JingleSession *session, xmlnode *jingle, JingleActionType action);
+void jingle_session_handle_action(JingleSession *session, PurpleXmlNode *jingle, JingleActionType action);
struct _JingleContent *jingle_session_find_content(JingleSession *session,
const gchar *name, const gchar *creator);
@@ -109,10 +105,6 @@ void jingle_session_accept_session(JingleSession *session);
JabberIq *jingle_session_terminate_packet(JingleSession *session, const gchar *reason);
JabberIq *jingle_session_redirect_packet(JingleSession *session, const gchar *sid);
-#ifdef __cplusplus
-}
-#endif
-
G_END_DECLS
#endif /* PURPLE_JABBER_JINGLE_SESSION_H */
diff --git a/libpurple/protocols/jabber/jingle/transport.c b/libpurple/protocols/jabber/jingle/transport.c
index 7ca867085c..eb24b0ef46 100644
--- a/libpurple/protocols/jabber/jingle/transport.c
+++ b/libpurple/protocols/jabber/jingle/transport.c
@@ -42,8 +42,10 @@ static void jingle_transport_init (JingleTransport *transport);
static void jingle_transport_finalize (GObject *object);
static void jingle_transport_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
static void jingle_transport_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
-JingleTransport *jingle_transport_parse_internal(xmlnode *transport);
-xmlnode *jingle_transport_to_xml_internal(JingleTransport *transport, xmlnode *content, JingleActionType action);
+JingleTransport *jingle_transport_parse_internal(PurpleXmlNode *transport);
+PurpleXmlNode *jingle_transport_to_xml_internal(JingleTransport *transport, PurpleXmlNode *content, JingleActionType action);
+static void jingle_transport_add_local_candidate_internal(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate);
+static GList *jingle_transport_get_remote_candidates_internal(JingleTransport *transport);
static GObjectClass *parent_class = NULL;
@@ -85,6 +87,8 @@ jingle_transport_class_init (JingleTransportClass *klass)
gobject_class->get_property = jingle_transport_get_property;
klass->to_xml = jingle_transport_to_xml_internal;
klass->parse = jingle_transport_parse_internal;
+ klass->add_local_candidate = jingle_transport_add_local_candidate_internal;
+ klass->get_remote_candidates = jingle_transport_get_remote_candidates_internal;
g_type_class_add_private(klass, sizeof(JingleTransportPrivate));
}
@@ -143,25 +147,51 @@ jingle_transport_get_transport_type(JingleTransport *transport)
return JINGLE_TRANSPORT_GET_CLASS(transport)->transport_type;
}
+void
+jingle_transport_add_local_candidate(JingleTransport *transport, const gchar *id,
+ guint generation, PurpleMediaCandidate *candidate)
+{
+ JINGLE_TRANSPORT_GET_CLASS(transport)->add_local_candidate(transport, id,
+ generation, candidate);
+}
+
+void
+jingle_transport_add_local_candidate_internal(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate)
+{
+ /* Nothing to do */
+}
+
+GList *
+jingle_transport_get_remote_candidates(JingleTransport *transport)
+{
+ return JINGLE_TRANSPORT_GET_CLASS(transport)->get_remote_candidates(transport);
+}
+
+static GList *
+jingle_transport_get_remote_candidates_internal(JingleTransport *transport)
+{
+ return NULL;
+}
+
JingleTransport *
-jingle_transport_parse_internal(xmlnode *transport)
+jingle_transport_parse_internal(PurpleXmlNode *transport)
{
- const gchar *type = xmlnode_get_namespace(transport);
+ const gchar *type = purple_xmlnode_get_namespace(transport);
return jingle_transport_create(type);
}
-xmlnode *
-jingle_transport_to_xml_internal(JingleTransport *transport, xmlnode *content, JingleActionType action)
+PurpleXmlNode *
+jingle_transport_to_xml_internal(JingleTransport *transport, PurpleXmlNode *content, JingleActionType action)
{
- xmlnode *node = xmlnode_new_child(content, "transport");
- xmlnode_set_namespace(node, jingle_transport_get_transport_type(transport));
+ PurpleXmlNode *node = purple_xmlnode_new_child(content, "transport");
+ purple_xmlnode_set_namespace(node, jingle_transport_get_transport_type(transport));
return node;
}
JingleTransport *
-jingle_transport_parse(xmlnode *transport)
+jingle_transport_parse(PurpleXmlNode *transport)
{
- const gchar *type_name = xmlnode_get_namespace(transport);
+ const gchar *type_name = purple_xmlnode_get_namespace(transport);
GType type = jingle_get_type(type_name);
if (type == G_TYPE_NONE)
return NULL;
@@ -169,8 +199,8 @@ jingle_transport_parse(xmlnode *transport)
return JINGLE_TRANSPORT_CLASS(g_type_class_ref(type))->parse(transport);
}
-xmlnode *
-jingle_transport_to_xml(JingleTransport *transport, xmlnode *content, JingleActionType action)
+PurpleXmlNode *
+jingle_transport_to_xml(JingleTransport *transport, PurpleXmlNode *content, JingleActionType action)
{
g_return_val_if_fail(transport != NULL, NULL);
g_return_val_if_fail(JINGLE_IS_TRANSPORT(transport), NULL);
diff --git a/libpurple/protocols/jabber/jingle/transport.h b/libpurple/protocols/jabber/jingle/transport.h
index c2827d461b..aec1cda6d9 100644
--- a/libpurple/protocols/jabber/jingle/transport.h
+++ b/libpurple/protocols/jabber/jingle/transport.h
@@ -53,8 +53,10 @@ struct _JingleTransportClass
GObjectClass parent_class; /**< The parent class. */
const gchar *transport_type;
- xmlnode *(*to_xml) (JingleTransport *transport, xmlnode *content, JingleActionType action);
- JingleTransport *(*parse) (xmlnode *transport);
+ PurpleXmlNode *(*to_xml) (JingleTransport *transport, PurpleXmlNode *content, JingleActionType action);
+ JingleTransport *(*parse) (PurpleXmlNode *transport);
+ void (*add_local_candidate) (JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate);
+ GList *(*get_remote_candidates) (JingleTransport *transport);
};
/** The transport class's private data */
@@ -64,10 +66,6 @@ struct _JingleTransport
JingleTransportPrivate *priv; /**< The private data of this object. */
};
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* Gets the transport class's GType
*
@@ -77,14 +75,12 @@ GType jingle_transport_get_type(void);
JingleTransport *jingle_transport_create(const gchar *type);
const gchar *jingle_transport_get_transport_type(JingleTransport *transport);
-void jingle_transport_add_candidate();
-JingleTransport *jingle_transport_parse(xmlnode *transport);
-xmlnode *jingle_transport_to_xml(JingleTransport *transport, xmlnode *content, JingleActionType action);
+void jingle_transport_add_local_candidate(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate);
+GList *jingle_transport_get_remote_candidates(JingleTransport *transport);
-#ifdef __cplusplus
-}
-#endif
+JingleTransport *jingle_transport_parse(PurpleXmlNode *transport);
+PurpleXmlNode *jingle_transport_to_xml(JingleTransport *transport, PurpleXmlNode *content, JingleActionType action);
G_END_DECLS
diff --git a/libpurple/protocols/jabber/jutil.c b/libpurple/protocols/jabber/jutil.c
index 6ad62b0ea7..2358a38b59 100644
--- a/libpurple/protocols/jabber/jutil.c
+++ b/libpurple/protocols/jabber/jutil.c
@@ -22,7 +22,6 @@
*/
#include "internal.h"
#include "account.h"
-#include "cipher.h"
#include "conversation.h"
#include "debug.h"
#include "server.h"
@@ -33,6 +32,10 @@
#include "presence.h"
#include "jutil.h"
+#include "ciphers/sha1hash.h"
+#include "ciphers/sha256hash.h"
+#include "ciphers/md5hash.h"
+
#ifdef USE_IDN
#include <idna.h>
#include <stringprep.h>
@@ -626,11 +629,16 @@ jabber_id_new(const char *str)
const char *jabber_normalize(const PurpleAccount *account, const char *in)
{
- PurpleConnection *gc = account ? account->gc : NULL;
- JabberStream *js = gc ? gc->proto_data : NULL;
+ PurpleConnection *gc = NULL;
+ JabberStream *js = NULL;
static char buf[3072]; /* maximum legal length of a jabber jid */
JabberID *jid;
+ if (account)
+ gc = purple_account_get_connection(account);
+ if (gc)
+ js = purple_connection_get_protocol_data(gc);
+
jid = jabber_id_new_internal(in, TRUE);
if(!jid)
return NULL;
@@ -711,7 +719,7 @@ static const struct {
const char *
jabber_buddy_state_get_name(const JabberBuddyState state)
{
- int i;
+ gsize i;
for (i = 0; i < G_N_ELEMENTS(jabber_statuses); ++i)
if (jabber_statuses[i].state == state)
return _(jabber_statuses[i].readable);
@@ -722,7 +730,7 @@ jabber_buddy_state_get_name(const JabberBuddyState state)
JabberBuddyState
jabber_buddy_status_id_get_state(const char *id)
{
- int i;
+ gsize i;
if (!id)
return JABBER_BUDDY_STATE_UNKNOWN;
@@ -735,7 +743,7 @@ jabber_buddy_status_id_get_state(const char *id)
JabberBuddyState jabber_buddy_show_get_state(const char *id)
{
- int i;
+ gsize i;
g_return_val_if_fail(id != NULL, JABBER_BUDDY_STATE_UNKNOWN);
@@ -751,7 +759,7 @@ JabberBuddyState jabber_buddy_show_get_state(const char *id)
const char *
jabber_buddy_state_get_show(JabberBuddyState state)
{
- int i;
+ gsize i;
for (i = 0; i < G_N_ELEMENTS(jabber_statuses); ++i)
if (state == jabber_statuses[i].state)
return jabber_statuses[i].show;
@@ -762,7 +770,7 @@ jabber_buddy_state_get_show(JabberBuddyState state)
const char *
jabber_buddy_state_get_status_id(JabberBuddyState state)
{
- int i;
+ gsize i;
for (i = 0; i < G_N_ELEMENTS(jabber_statuses); ++i)
if (state == jabber_statuses[i].state)
return jabber_statuses[i].status_id;
@@ -774,25 +782,31 @@ char *
jabber_calculate_data_hash(gconstpointer data, size_t len,
const gchar *hash_algo)
{
- PurpleCipherContext *context;
+ PurpleHash *hash = NULL;
static gchar digest[129]; /* 512 bits hex + \0 */
- context = purple_cipher_context_new_by_name(hash_algo, NULL);
- if (context == NULL)
+ if (g_str_equal(hash_algo, "sha1"))
+ hash = purple_sha1_hash_new();
+ else if (g_str_equal(hash_algo, "sha256"))
+ hash = purple_sha256_hash_new();
+ else if (g_str_equal(hash_algo, "md5"))
+ hash = purple_md5_hash_new();
+
+ if (hash == NULL)
{
- purple_debug_error("jabber", "Could not find %s cipher\n", hash_algo);
+ purple_debug_error("jabber", "Unexpected hashing algorithm %s requested\n", hash_algo);
g_return_val_if_reached(NULL);
}
/* Hash the data */
- purple_cipher_context_append(context, data, len);
- if (!purple_cipher_context_digest_to_str(context, sizeof(digest), digest, NULL))
+ purple_hash_append(hash, data, len);
+ if (!purple_hash_digest_to_str(hash, digest, sizeof(digest)))
{
purple_debug_error("jabber", "Failed to get digest for %s cipher.\n",
hash_algo);
g_return_val_if_reached(NULL);
}
- purple_cipher_context_destroy(context);
+ g_object_unref(G_OBJECT(hash));
return g_strdup(digest);
}
diff --git a/libpurple/protocols/jabber/libfacebook.c b/libpurple/protocols/jabber/libfacebook.c
new file mode 100644
index 0000000000..fbe9f7cf02
--- /dev/null
+++ b/libpurple/protocols/jabber/libfacebook.c
@@ -0,0 +1,254 @@
+/* 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
+ *
+ */
+
+/* libfacebook is the Facebook XMPP protocol plugin. It is linked against
+ * libjabbercommon, which may be used to support other protocols (Bonjour)
+ * which may need to share code.
+ */
+
+#include "internal.h"
+
+#include "accountopt.h"
+#include "core.h"
+#include "debug.h"
+#include "version.h"
+
+#include "iq.h"
+#include "jabber.h"
+#include "chat.h"
+#include "disco.h"
+#include "message.h"
+#include "roster.h"
+#include "si.h"
+#include "message.h"
+#include "presence.h"
+#include "google/google.h"
+#include "pep.h"
+#include "usermood.h"
+#include "usertune.h"
+#include "caps.h"
+#include "data.h"
+#include "ibb.h"
+
+static const char *
+facebook_list_icon(PurpleAccount *a, PurpleBuddy *b)
+{
+ return "facebook";
+}
+
+static void
+facebook_login(PurpleAccount *account)
+{
+ PurpleConnection *gc;
+ JabberStream *js;
+
+ jabber_login(account);
+
+ gc = purple_account_get_connection(account);
+ if (!gc)
+ return;
+
+ purple_connection_set_flags(gc, 0);
+
+ js = purple_connection_get_protocol_data(gc);
+ if (!js)
+ return;
+
+ js->server_caps |= JABBER_CAP_FACEBOOK;
+}
+
+static PurplePlugin *my_protocol = NULL;
+
+static PurplePluginProtocolInfo prpl_info =
+{
+ sizeof(PurplePluginProtocolInfo), /* struct_size */
+ 0, /* PurpleProtocolOptions */
+ NULL, /* user_splits */
+ NULL, /* protocol_options */
+ NO_BUDDY_ICONS, /* icon_spec */
+ facebook_list_icon, /* list_icon */
+ jabber_list_emblem, /* list_emblems */
+ jabber_status_text, /* status_text */
+ jabber_tooltip_text, /* tooltip_text */
+ jabber_status_types, /* status_types */
+ NULL, /* blist_node_menu */
+ NULL, /* chat_info */
+ NULL, /* chat_info_defaults */
+ facebook_login, /* login */
+ jabber_close, /* close */
+ jabber_message_send_im, /* send_im */
+ NULL, /* set_info */
+ jabber_send_typing, /* send_typing */
+ jabber_buddy_get_info, /* get_info */
+ jabber_set_status, /* set_status */
+ jabber_idle_set, /* set_idle */
+ NULL, /* change_passwd */
+ NULL, /* add_buddy */
+ NULL, /* add_buddies */
+ NULL, /* remove_buddy */
+ NULL, /* remove_buddies */
+ NULL, /* add_permit */
+ NULL, /* add_deny */
+ NULL, /* rem_permit */
+ NULL, /* rem_deny */
+ NULL, /* set_permit_deny */
+ NULL, /* join_chat */
+ NULL, /* reject_chat */
+ NULL, /* get_chat_name */
+ NULL, /* chat_invite */
+ NULL, /* chat_leave */
+ NULL, /* chat_send */
+ jabber_keepalive, /* keepalive */
+ NULL, /* register_user */
+ NULL, /* get_cb_info */
+ NULL, /* alias_buddy */
+ NULL, /* group_buddy */
+ NULL, /* rename_group */
+ NULL, /* buddy_free */
+ jabber_convo_closed, /* convo_closed */
+ jabber_normalize, /* normalize */
+ NULL, /* set_buddy_icon */
+ NULL, /* remove_group */
+ NULL, /* get_cb_real_name */
+ NULL, /* set_chat_topic */
+ NULL, /* find_blist_chat */
+ NULL, /* roomlist_get_list */
+ NULL, /* roomlist_cancel */
+ NULL, /* roomlist_expand_category */
+ NULL, /* can_receive_file */
+ NULL, /* send_file */
+ NULL, /* new_xfer */
+ jabber_offline_message, /* offline_message */
+ NULL, /* whiteboard_prpl_ops */
+ jabber_prpl_send_raw, /* send_raw */
+ jabber_roomlist_room_serialize, /* roomlist_room_serialize */
+ NULL, /* unregister_user */
+ NULL, /* send_attention */
+ NULL, /* attention_types */
+ NULL, /* get_account_text_table */
+ NULL, /* initiate_media */
+ NULL, /* get_media_caps */
+ NULL, /* get_moods */
+ NULL, /* set_public_alias */
+ NULL, /* get_public_alias */
+ NULL /* get_max_message_size */
+};
+
+static gboolean load_plugin(PurplePlugin *plugin)
+{
+ jabber_plugin_init(plugin);
+
+ return TRUE;
+}
+
+static gboolean unload_plugin(PurplePlugin *plugin)
+{
+ jabber_plugin_uninit(plugin);
+
+ return TRUE;
+}
+
+static PurplePluginInfo info =
+{
+ PURPLE_PLUGIN_MAGIC,
+ PURPLE_MAJOR_VERSION,
+ PURPLE_MINOR_VERSION,
+ PURPLE_PLUGIN_PROTOCOL, /**< type */
+ NULL, /**< ui_requirement */
+ 0, /**< flags */
+ NULL, /**< dependencies */
+ PURPLE_PRIORITY_DEFAULT, /**< priority */
+
+ "prpl-facebook-xmpp", /**< id */
+ "Facebook (XMPP)", /**< name */
+ DISPLAY_VERSION, /**< version */
+ /** summary */
+ N_("Facebook XMPP Protocol Plugin"),
+ /** description */
+ N_("Facebook XMPP Protocol Plugin"),
+ NULL, /**< author */
+ PURPLE_WEBSITE, /**< homepage */
+
+ load_plugin, /**< load */
+ unload_plugin, /**< unload */
+ NULL, /**< destroy */
+
+ NULL, /**< ui_info */
+ &prpl_info, /**< extra_info */
+ NULL, /**< prefs_info */
+ NULL, /**< actions */
+
+ /* padding */
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static void
+init_plugin(PurplePlugin *plugin)
+{
+ PurpleAccountUserSplit *split;
+ PurpleAccountOption *option;
+ GList *encryption_values = NULL;
+
+ /* Translators: 'domain' is used here in the context of Internet domains, e.g. pidgin.im */
+ split = purple_account_user_split_new(_("Domain"), "chat.facebook.com", '@');
+ purple_account_user_split_set_reverse(split, FALSE);
+ purple_account_user_split_set_constant(split, TRUE);
+ prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
+
+ split = purple_account_user_split_new(_("Resource"), "", '/');
+ purple_account_user_split_set_reverse(split, FALSE);
+ purple_account_user_split_set_constant(split, TRUE);
+ prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
+
+#define ADD_VALUE(list, desc, v) { \
+ PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1); \
+ kvp->key = g_strdup((desc)); \
+ kvp->value = g_strdup((v)); \
+ list = g_list_prepend(list, kvp); \
+}
+
+ ADD_VALUE(encryption_values, _("Use encryption if available"), "opportunistic_tls");
+ ADD_VALUE(encryption_values, _("Require encryption"), "require_tls");
+ ADD_VALUE(encryption_values, _("Use old-style SSL"), "old_ssl");
+#if 0
+ ADD_VALUE(encryption_values, "None", "none");
+#endif
+ encryption_values = g_list_reverse(encryption_values);
+
+#undef ADD_VALUE
+
+ option = purple_account_option_list_new(_("Connection security"), "connection_security", encryption_values);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
+ option);
+
+ option = purple_account_option_string_new(_("BOSH URL"),
+ "bosh_url", NULL);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
+ option);
+
+ my_protocol = plugin;
+}
+
+PURPLE_INIT_PLUGIN(facebookxmpp, init_plugin, info);
diff --git a/libpurple/protocols/jabber/libgtalk.c b/libpurple/protocols/jabber/libgtalk.c
new file mode 100644
index 0000000000..8c53036eaf
--- /dev/null
+++ b/libpurple/protocols/jabber/libgtalk.c
@@ -0,0 +1,334 @@
+/* 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
+ *
+ */
+
+/* libgtalk is the Google Talk XMPP protocol plugin. It is linked against
+ * libjabbercommon, which may be used to support other protocols (Bonjour) which
+ * may need to share code.
+ */
+
+#include "internal.h"
+
+#include "accountopt.h"
+#include "core.h"
+#include "debug.h"
+#include "version.h"
+
+#include "iq.h"
+#include "jabber.h"
+#include "chat.h"
+#include "disco.h"
+#include "message.h"
+#include "roster.h"
+#include "si.h"
+#include "message.h"
+#include "presence.h"
+#include "google/google.h"
+#include "pep.h"
+#include "usermood.h"
+#include "usertune.h"
+#include "caps.h"
+#include "data.h"
+#include "ibb.h"
+
+static const char *
+gtalk_list_icon(PurpleAccount *a, PurpleBuddy *b)
+{
+ return "google-talk";
+}
+
+static PurplePlugin *my_protocol = NULL;
+
+static PurplePluginProtocolInfo prpl_info =
+{
+ sizeof(PurplePluginProtocolInfo), /* struct_size */
+ OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_MAIL_CHECK |
+#ifdef HAVE_CYRUS_SASL
+ OPT_PROTO_PASSWORD_OPTIONAL |
+#endif
+ OPT_PROTO_SLASH_COMMANDS_NATIVE,
+ NULL, /* user_splits */
+ NULL, /* protocol_options */
+ {"png", 32, 32, 96, 96, 0, PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
+ gtalk_list_icon, /* list_icon */
+ jabber_list_emblem, /* list_emblems */
+ jabber_status_text, /* status_text */
+ jabber_tooltip_text, /* tooltip_text */
+ jabber_status_types, /* status_types */
+ jabber_blist_node_menu, /* blist_node_menu */
+ jabber_chat_info, /* chat_info */
+ jabber_chat_info_defaults, /* chat_info_defaults */
+ jabber_login, /* login */
+ jabber_close, /* close */
+ jabber_message_send_im, /* send_im */
+ jabber_set_info, /* set_info */
+ jabber_send_typing, /* send_typing */
+ jabber_buddy_get_info, /* get_info */
+ jabber_set_status, /* set_status */
+ jabber_idle_set, /* set_idle */
+ NULL, /* change_passwd */
+ jabber_roster_add_buddy, /* add_buddy */
+ NULL, /* add_buddies */
+ jabber_roster_remove_buddy, /* remove_buddy */
+ NULL, /* remove_buddies */
+ NULL, /* add_permit */
+ jabber_add_deny, /* add_deny */
+ NULL, /* rem_permit */
+ jabber_rem_deny, /* rem_deny */
+ NULL, /* set_permit_deny */
+ jabber_chat_join, /* join_chat */
+ NULL, /* reject_chat */
+ jabber_get_chat_name, /* get_chat_name */
+ jabber_chat_invite, /* chat_invite */
+ jabber_chat_leave, /* chat_leave */
+ jabber_message_send_chat, /* chat_send */
+ jabber_keepalive, /* keepalive */
+ NULL, /* register_user */
+ NULL, /* get_cb_info */
+ jabber_roster_alias_change, /* alias_buddy */
+ jabber_roster_group_change, /* group_buddy */
+ jabber_roster_group_rename, /* rename_group */
+ NULL, /* buddy_free */
+ jabber_convo_closed, /* convo_closed */
+ jabber_normalize, /* normalize */
+ jabber_set_buddy_icon, /* set_buddy_icon */
+ NULL, /* remove_group */
+ jabber_chat_user_real_name, /* get_cb_real_name */
+ jabber_chat_set_topic, /* set_chat_topic */
+ jabber_find_blist_chat, /* find_blist_chat */
+ jabber_roomlist_get_list, /* roomlist_get_list */
+ jabber_roomlist_cancel, /* roomlist_cancel */
+ NULL, /* roomlist_expand_category */
+ jabber_can_receive_file, /* can_receive_file */
+ jabber_si_xfer_send, /* send_file */
+ jabber_si_new_xfer, /* new_xfer */
+ jabber_offline_message, /* offline_message */
+ NULL, /* whiteboard_prpl_ops */
+ jabber_prpl_send_raw, /* send_raw */
+ jabber_roomlist_room_serialize, /* roomlist_room_serialize */
+ NULL, /* unregister_user */
+ jabber_send_attention, /* send_attention */
+ jabber_attention_types, /* attention_types */
+ NULL, /* get_account_text_table */
+ jabber_initiate_media, /* initiate_media */
+ jabber_get_media_caps, /* get_media_caps */
+ jabber_get_moods, /* get_moods */
+ NULL, /* set_public_alias */
+ NULL, /* get_public_alias */
+ NULL /* get_max_message_size */
+};
+
+static gboolean load_plugin(PurplePlugin *plugin)
+{
+ jabber_plugin_init(plugin);
+
+ return TRUE;
+}
+
+static gboolean unload_plugin(PurplePlugin *plugin)
+{
+ jabber_plugin_uninit(plugin);
+
+ return TRUE;
+}
+
+static PurplePluginInfo info =
+{
+ PURPLE_PLUGIN_MAGIC,
+ PURPLE_MAJOR_VERSION,
+ PURPLE_MINOR_VERSION,
+ PURPLE_PLUGIN_PROTOCOL, /**< type */
+ NULL, /**< ui_requirement */
+ 0, /**< flags */
+ NULL, /**< dependencies */
+ PURPLE_PRIORITY_DEFAULT, /**< priority */
+
+ "prpl-gtalk", /**< id */
+ "Google Talk (XMPP)", /**< name */
+ DISPLAY_VERSION, /**< version */
+ /** summary */
+ N_("Google Talk Protocol Plugin"),
+ /** description */
+ N_("Google Talk Protocol Plugin"),
+ NULL, /**< author */
+ PURPLE_WEBSITE, /**< homepage */
+
+ load_plugin, /**< load */
+ unload_plugin, /**< unload */
+ NULL, /**< destroy */
+
+ NULL, /**< ui_info */
+ &prpl_info, /**< extra_info */
+ NULL, /**< prefs_info */
+ jabber_actions,
+
+ /* padding */
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static PurpleAccount *find_acct(const char *prpl, const char *acct_id)
+{
+ PurpleAccount *acct = NULL;
+
+ /* If we have a specific acct, use it */
+ if (acct_id) {
+ acct = purple_accounts_find(acct_id, prpl);
+ if (acct && !purple_account_is_connected(acct))
+ acct = NULL;
+ } else { /* Otherwise find an active account for the protocol */
+ GList *l = purple_accounts_get_all();
+ while (l) {
+ if (!strcmp(prpl, purple_account_get_protocol_id(l->data))
+ && purple_account_is_connected(l->data)) {
+ acct = l->data;
+ break;
+ }
+ l = l->next;
+ }
+ }
+
+ return acct;
+}
+
+static gboolean xmpp_uri_handler(const char *proto, const char *user, GHashTable *params)
+{
+ char *acct_id = params ? g_hash_table_lookup(params, "account") : NULL;
+ PurpleAccount *acct;
+
+ if (g_ascii_strcasecmp(proto, "xmpp"))
+ return FALSE;
+
+ acct = find_acct(purple_plugin_get_id(my_protocol), acct_id);
+
+ if (!acct)
+ return FALSE;
+
+ /* xmpp:romeo@montague.net?message;subject=Test%20Message;body=Here%27s%20a%20test%20message */
+ /* params is NULL if the URI has no '?' (or anything after it) */
+ if (!params || g_hash_table_lookup_extended(params, "message", NULL, NULL)) {
+ char *body = g_hash_table_lookup(params, "body");
+ if (user && *user) {
+ PurpleIMConversation *im =
+ purple_im_conversation_new(acct, user);
+ purple_conversation_present(PURPLE_CONVERSATION(im));
+ if (body && *body)
+ purple_conversation_send_confirm(PURPLE_CONVERSATION(im), body);
+ }
+ } else if (g_hash_table_lookup_extended(params, "roster", NULL, NULL)) {
+ char *name = g_hash_table_lookup(params, "name");
+ if (user && *user)
+ purple_blist_request_add_buddy(acct, user, NULL, name);
+ } else if (g_hash_table_lookup_extended(params, "join", NULL, NULL)) {
+ PurpleConnection *gc = purple_account_get_connection(acct);
+ if (user && *user) {
+ GHashTable *params = jabber_chat_info_defaults(gc, user);
+ jabber_chat_join(gc, params);
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void
+init_plugin(PurplePlugin *plugin)
+{
+ PurpleAccountUserSplit *split;
+ PurpleAccountOption *option;
+ GList *encryption_values = NULL;
+
+ /* Translators: 'domain' is used here in the context of Internet domains, e.g. pidgin.im */
+ split = purple_account_user_split_new(_("Domain"), "gmail.com", '@');
+ purple_account_user_split_set_reverse(split, FALSE);
+ prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
+
+ split = purple_account_user_split_new(_("Resource"), "", '/');
+ purple_account_user_split_set_reverse(split, FALSE);
+ prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
+
+#define ADD_VALUE(list, desc, v) { \
+ PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1); \
+ kvp->key = g_strdup((desc)); \
+ kvp->value = g_strdup((v)); \
+ list = g_list_prepend(list, kvp); \
+}
+
+ ADD_VALUE(encryption_values, _("Require encryption"), "require_tls");
+ ADD_VALUE(encryption_values, _("Use encryption if available"), "opportunistic_tls");
+ ADD_VALUE(encryption_values, _("Use old-style SSL"), "old_ssl");
+#if 0
+ ADD_VALUE(encryption_values, "None", "none");
+#endif
+ encryption_values = g_list_reverse(encryption_values);
+
+#undef ADD_VALUE
+
+ option = purple_account_option_list_new(_("Connection security"), "connection_security", encryption_values);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
+ option);
+
+ option = purple_account_option_bool_new(
+ _("Allow plaintext auth over unencrypted streams"),
+ "auth_plain_in_clear", FALSE);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
+ option);
+
+ option = purple_account_option_int_new(_("Connect port"), "port", 5222);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
+ option);
+
+ option = purple_account_option_string_new(_("Connect server"),
+ "connect_server", NULL);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
+ option);
+
+ option = purple_account_option_string_new(_("File transfer proxies"),
+ "ft_proxies",
+ /* TODO: Is this an acceptable default?
+ * Also, keep this in sync as they add more servers */
+ JABBER_DEFAULT_FT_PROXIES);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
+ option);
+
+ option = purple_account_option_string_new(_("BOSH URL"),
+ "bosh_url", NULL);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
+ option);
+
+ /* this should probably be part of global smiley theme settings later on,
+ shared with MSN */
+ option = purple_account_option_bool_new(_("Show Custom Smileys"),
+ "custom_smileys", TRUE);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
+ option);
+
+ my_protocol = plugin;
+
+ purple_signal_connect(purple_get_core(), "uri-handler", plugin,
+ PURPLE_CALLBACK(xmpp_uri_handler), NULL);
+}
+
+PURPLE_INIT_PLUGIN(gtalk, init_plugin, info);
+
diff --git a/libpurple/protocols/jabber/libxmpp.c b/libpurple/protocols/jabber/libxmpp.c
index f34052b6e4..c97bc80641 100644
--- a/libpurple/protocols/jabber/libxmpp.c
+++ b/libpurple/protocols/jabber/libxmpp.c
@@ -53,6 +53,7 @@ static PurplePlugin *my_protocol = NULL;
static PurplePluginProtocolInfo prpl_info =
{
+ sizeof(PurplePluginProtocolInfo), /* struct_size */
OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_MAIL_CHECK |
#ifdef HAVE_CYRUS_SASL
OPT_PROTO_PASSWORD_OPTIONAL |
@@ -92,12 +93,10 @@ static PurplePluginProtocolInfo prpl_info =
jabber_get_chat_name, /* get_chat_name */
jabber_chat_invite, /* chat_invite */
jabber_chat_leave, /* chat_leave */
- NULL, /* chat_whisper */
jabber_message_send_chat, /* chat_send */
jabber_keepalive, /* keepalive */
jabber_register_account, /* register_user */
NULL, /* get_cb_info */
- NULL, /* get_cb_away */
jabber_roster_alias_change, /* alias_buddy */
jabber_roster_group_change, /* group_buddy */
jabber_roster_group_rename, /* rename_group */
@@ -106,7 +105,7 @@ static PurplePluginProtocolInfo prpl_info =
jabber_normalize, /* normalize */
jabber_set_buddy_icon, /* set_buddy_icon */
NULL, /* remove_group */
- jabber_chat_buddy_real_name, /* get_cb_real_name */
+ jabber_chat_user_real_name, /* get_cb_real_name */
jabber_chat_set_topic, /* set_chat_topic */
jabber_find_blist_chat, /* find_blist_chat */
jabber_roomlist_get_list, /* roomlist_get_list */
@@ -122,16 +121,13 @@ static PurplePluginProtocolInfo prpl_info =
jabber_unregister_account, /* unregister_user */
jabber_send_attention, /* send_attention */
jabber_attention_types, /* attention_types */
-
- sizeof(PurplePluginProtocolInfo), /* struct_size */
NULL, /* get_account_text_table */
jabber_initiate_media, /* initiate_media */
jabber_get_media_caps, /* get_media_caps */
jabber_get_moods, /* get_moods */
NULL, /* set_public_alias */
NULL, /* get_public_alias */
- NULL, /* add_buddy_with_invite */
- NULL /* add_buddies_with_invite */
+ NULL /* get_max_message_size */
};
static gboolean load_plugin(PurplePlugin *plugin)
@@ -227,11 +223,11 @@ static gboolean xmpp_uri_handler(const char *proto, const char *user, GHashTable
if (!params || g_hash_table_lookup_extended(params, "message", NULL, NULL)) {
char *body = g_hash_table_lookup(params, "body");
if (user && *user) {
- PurpleConversation *conv =
- purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, user);
- purple_conversation_present(conv);
+ PurpleIMConversation *im =
+ purple_im_conversation_new(acct, user);
+ purple_conversation_present(PURPLE_CONVERSATION(im));
if (body && *body)
- purple_conv_send_confirm(conv, body);
+ purple_conversation_send_confirm(PURPLE_CONVERSATION(im), body);
}
} else if (g_hash_table_lookup_extended(params, "roster", NULL, NULL)) {
char *name = g_hash_table_lookup(params, "name");
diff --git a/libpurple/protocols/jabber/message.c b/libpurple/protocols/jabber/message.c
index 609be702f5..9883f8f30e 100644
--- a/libpurple/protocols/jabber/message.c
+++ b/libpurple/protocols/jabber/message.c
@@ -24,6 +24,8 @@
#include "debug.h"
#include "notify.h"
+#include "smiley-custom.h"
+#include "smiley-parser.h"
#include "server.h"
#include "util.h"
#include "adhoccommands.h"
@@ -79,19 +81,19 @@ static void handle_chat(JabberMessage *jm)
jbr->chat_states = JABBER_CHAT_STATES_SUPPORTED;
if(JM_STATE_COMPOSING == jm->chat_state) {
- serv_got_typing(gc, jm->from, 0, PURPLE_TYPING);
+ purple_serv_got_typing(gc, jm->from, 0, PURPLE_IM_TYPING);
} else if(JM_STATE_PAUSED == jm->chat_state) {
- serv_got_typing(gc, jm->from, 0, PURPLE_TYPED);
+ purple_serv_got_typing(gc, jm->from, 0, PURPLE_IM_TYPED);
} else if(JM_STATE_GONE == jm->chat_state) {
- PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+ PurpleIMConversation *im = purple_conversations_find_im_with_account(
jm->from, account);
- if (conv && jid->node && jid->domain) {
+ if (im && jid->node && jid->domain) {
char buf[256];
PurpleBuddy *buddy;
g_snprintf(buf, sizeof(buf), "%s@%s", jid->node, jid->domain);
- if ((buddy = purple_find_buddy(account, buf))) {
+ if ((buddy = purple_blist_find_buddy(account, buf))) {
const char *who;
char *escaped;
@@ -105,14 +107,14 @@ static void handle_chat(JabberMessage *jm)
/* At some point when we restructure PurpleConversation,
* this should be able to be implemented by removing the
* user from the conversation like we do with chats now. */
- purple_conversation_write(conv, "", buf,
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(im), buf, 0);
}
}
- serv_got_typing_stopped(gc, jm->from);
+ purple_serv_got_typing_stopped(gc, jm->from);
} else {
- serv_got_typing_stopped(gc, jm->from);
+ purple_serv_got_typing_stopped(gc, jm->from);
}
} else {
if (jid->resource) {
@@ -122,18 +124,17 @@ static void handle_chat(JabberMessage *jm)
* resource (i.e. bind/lock the conversation to this
* resource).
*
- * This works because purple_conv_im_send gets the name
+ * This works because purple_im_conversation_send gets the name
* from purple_conversation_get_name()
*/
- PurpleConversation *conv;
+ PurpleIMConversation *im;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
- jm->from, account);
- if (conv && !g_str_equal(jm->from,
- purple_conversation_get_name(conv))) {
+ im = purple_conversations_find_im_with_account(jm->from, account);
+ if (im && !g_str_equal(jm->from,
+ purple_conversation_get_name(PURPLE_CONVERSATION(im)))) {
purple_debug_info("jabber", "Binding conversation to %s\n",
jm->from);
- purple_conversation_set_name(conv, jm->from);
+ purple_conversation_set_name(PURPLE_CONVERSATION(im), jm->from);
}
}
@@ -156,7 +157,7 @@ static void handle_chat(JabberMessage *jm)
jm->body = jabber_google_format_to_html(jm->body);
g_free(tmp);
}
- serv_got_im(gc, jm->from, jm->xhtml ? jm->xhtml : jm->body, 0, jm->sent);
+ purple_serv_got_im(gc, jm->from, jm->xhtml ? jm->xhtml : jm->body, 0, jm->sent);
}
jabber_id_free(jid);
@@ -180,20 +181,20 @@ static void handle_headline(JabberMessage *jm)
g_string_append(body, jm->body);
for(etc = jm->etc; etc; etc = etc->next) {
- xmlnode *x = etc->data;
- const char *xmlns = xmlnode_get_namespace(x);
+ PurpleXmlNode *x = etc->data;
+ const char *xmlns = purple_xmlnode_get_namespace(x);
if(xmlns && !strcmp(xmlns, NS_OOB_X_DATA)) {
- xmlnode *url, *desc;
+ PurpleXmlNode *url, *desc;
char *urltxt, *desctxt;
- url = xmlnode_get_child(x, "url");
- desc = xmlnode_get_child(x, "desc");
+ url = purple_xmlnode_get_child(x, "url");
+ desc = purple_xmlnode_get_child(x, "desc");
if(!url || !desc)
continue;
- urltxt = xmlnode_get_data(url);
- desctxt = xmlnode_get_data(desc);
+ urltxt = purple_xmlnode_get_data(url);
+ desctxt = purple_xmlnode_get_data(desc);
/* I'm all about ugly hacks */
if(body->len && jm->body && !strcmp(body->str, jm->body))
@@ -219,6 +220,7 @@ static void handle_groupchat(JabberMessage *jm)
{
JabberID *jid = jabber_id_new(jm->from);
JabberChat *chat;
+ PurpleMessageFlags messageFlags = 0;
if(!jid)
return;
@@ -229,8 +231,9 @@ static void handle_groupchat(JabberMessage *jm)
return;
if(jm->subject) {
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(chat->conv), jid->resource,
+ purple_chat_conversation_set_topic(chat->conv, jid->resource,
jm->subject);
+ messageFlags |= PURPLE_MESSAGE_NO_LOG;
if(!jm->xhtml && !jm->body) {
char *msg, *tmp, *tmp2;
tmp = g_markup_escape_text(jm->subject, -1);
@@ -239,7 +242,8 @@ static void handle_groupchat(JabberMessage *jm)
msg = g_strdup_printf(_("%s has set the topic to: %s"), jid->resource, tmp2);
else
msg = g_strdup_printf(_("The topic is: %s"), tmp2);
- purple_conv_chat_write(PURPLE_CONV_CHAT(chat->conv), "", msg, PURPLE_MESSAGE_SYSTEM, jm->sent);
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(chat->conv),
+ msg, messageFlags);
g_free(tmp);
g_free(tmp2);
g_free(msg);
@@ -248,13 +252,13 @@ static void handle_groupchat(JabberMessage *jm)
if(jm->xhtml || jm->body) {
if(jid->resource)
- serv_got_chat_in(jm->js->gc, chat->id, jid->resource,
- jm->delayed ? PURPLE_MESSAGE_DELAYED : 0,
+ purple_serv_got_chat_in(jm->js->gc, chat->id, jid->resource,
+ messageFlags | (jm->delayed ? PURPLE_MESSAGE_DELAYED : 0),
jm->xhtml ? jm->xhtml : jm->body, jm->sent);
else if(chat->muc)
- purple_conv_chat_write(PURPLE_CONV_CHAT(chat->conv), "",
- jm->xhtml ? jm->xhtml : jm->body,
- PURPLE_MESSAGE_SYSTEM, jm->sent);
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(chat->conv),
+ jm->xhtml ? jm->xhtml : jm->body, messageFlags);
}
jabber_id_free(jid);
@@ -276,7 +280,7 @@ static void handle_groupchat_invite(JabberMessage *jm)
g_hash_table_replace(components, "password", g_strdup(jm->password));
jabber_id_free(jid);
- serv_got_chat_invite(jm->js->gc, jm->to, jm->from, jm->body, components);
+ purple_serv_got_chat_invite(jm->js->gc, jm->to, jm->from, jm->body, components);
}
static void handle_error(JabberMessage *jm)
@@ -308,7 +312,7 @@ static void handle_buzz(JabberMessage *jm) {
account = purple_connection_get_account(jm->js->gc);
- if (purple_find_buddy(account, jm->from) == NULL)
+ if (purple_blist_find_buddy(account, jm->from) == NULL)
return; /* Do not accept buzzes from unknown people */
/* xmpp only has 1 attention type, so index is 0 */
@@ -323,14 +327,14 @@ typedef struct {
static void
-jabber_message_get_refs_from_xmlnode_internal(const xmlnode *message,
+jabber_message_get_refs_from_xmlnode_internal(const PurpleXmlNode *message,
GHashTable *table)
{
- xmlnode *child;
+ PurpleXmlNode *child;
- for (child = xmlnode_get_child(message, "img") ; child ;
- child = xmlnode_get_next_twin(child)) {
- const gchar *src = xmlnode_get_attrib(child, "src");
+ for (child = purple_xmlnode_get_child(message, "img") ; child ;
+ child = purple_xmlnode_get_next_twin(child)) {
+ const gchar *src = purple_xmlnode_get_attrib(child, "src");
if (g_str_has_prefix(src, "cid:")) {
const gchar *cid = src + 4;
@@ -340,7 +344,7 @@ jabber_message_get_refs_from_xmlnode_internal(const xmlnode *message,
/* take a copy of the cid and let the SmileyRef own it... */
gchar *temp_cid = g_strdup(cid);
JabberSmileyRef *ref = g_new0(JabberSmileyRef, 1);
- const gchar *alt = xmlnode_get_attrib(child, "alt");
+ const gchar *alt = purple_xmlnode_get_attrib(child, "alt");
ref->cid = temp_cid;
/* if there is no "alt" string, use the cid...
include the entire src, eg. "cid:.." to avoid linkification */
@@ -378,7 +382,7 @@ jabber_message_get_refs_steal(gpointer key, gpointer value, gpointer user_data)
}
static GList *
-jabber_message_get_refs_from_xmlnode(const xmlnode *message)
+jabber_message_get_refs_from_xmlnode(const PurpleXmlNode *message)
{
GList *refs = NULL;
GHashTable *unique_refs = g_hash_table_new(g_str_hash, g_str_equal);
@@ -391,9 +395,9 @@ jabber_message_get_refs_from_xmlnode(const xmlnode *message)
}
static gchar *
-jabber_message_xml_to_string_strip_img_smileys(xmlnode *xhtml)
+jabber_message_xml_to_string_strip_img_smileys(PurpleXmlNode *xhtml)
{
- gchar *markup = xmlnode_to_str(xhtml, NULL);
+ gchar *markup = purple_xmlnode_to_str(xhtml, NULL);
int len = strlen(markup);
int pos = 0;
GString *out = g_string_new(NULL);
@@ -403,7 +407,7 @@ jabber_message_xml_to_string_strip_img_smileys(xmlnode *xhtml)
we need to find all <img> tags within the XHTML and replace those
tags with the value of their "alt" attributes */
if (g_str_has_prefix(&(markup[pos]), "<img")) {
- xmlnode *img = NULL;
+ PurpleXmlNode *img = NULL;
int pos2 = pos;
const gchar *src;
@@ -419,14 +423,14 @@ jabber_message_xml_to_string_strip_img_smileys(xmlnode *xhtml)
/* note, if the above loop didn't find the end of the <img> tag,
it the parsed string will be until the end of the input string,
- in which case xmlnode_from_str will bail out and return NULL,
+ in which case purple_xmlnode_from_str will bail out and return NULL,
in this case the "if" statement below doesn't trigger and the
text is copied unchanged */
- img = xmlnode_from_str(&(markup[pos]), pos2 - pos);
- src = xmlnode_get_attrib(img, "src");
+ img = purple_xmlnode_from_str(&(markup[pos]), pos2 - pos);
+ src = purple_xmlnode_get_attrib(img, "src");
if (g_str_has_prefix(src, "cid:")) {
- const gchar *alt = xmlnode_get_attrib(img, "alt");
+ const gchar *alt = purple_xmlnode_get_attrib(img, "alt");
/* if the "alt" attribute is empty, put the cid as smiley string */
if (alt && alt[0] != '\0') {
/* if the "alt" is the same as the CID, as Jabbim does,
@@ -436,7 +440,9 @@ jabber_message_xml_to_string_strip_img_smileys(xmlnode *xhtml)
out = g_string_append(out, safe_alt);
g_free(safe_alt);
} else {
- out = g_string_append(out, alt);
+ gchar *alt_escaped = g_markup_escape_text(alt, -1);
+ out = g_string_append(out, alt_escaped);
+ g_free(alt_escaped);
}
} else {
out = g_string_append(out, src);
@@ -447,7 +453,7 @@ jabber_message_xml_to_string_strip_img_smileys(xmlnode *xhtml)
pos++;
}
- xmlnode_free(img);
+ purple_xmlnode_free(img);
} else {
out = g_string_append_c(out, markup[pos]);
@@ -461,13 +467,13 @@ jabber_message_xml_to_string_strip_img_smileys(xmlnode *xhtml)
static void
jabber_message_add_remote_smileys(JabberStream *js, const gchar *who,
- const xmlnode *message)
+ const PurpleXmlNode *message)
{
- xmlnode *data_tag;
- for (data_tag = xmlnode_get_child_with_namespace(message, "data", NS_BOB) ;
+ PurpleXmlNode *data_tag;
+ for (data_tag = purple_xmlnode_get_child_with_namespace(message, "data", NS_BOB) ;
data_tag ;
- data_tag = xmlnode_get_next_twin(data_tag)) {
- const gchar *cid = xmlnode_get_attrib(data_tag, "cid");
+ data_tag = purple_xmlnode_get_next_twin(data_tag)) {
+ const gchar *cid = purple_xmlnode_get_attrib(data_tag, "cid");
const JabberData *data = jabber_data_find_remote_by_cid(js, who, cid);
if (!data && cid != NULL) {
@@ -482,32 +488,77 @@ jabber_message_add_remote_smileys(JabberStream *js, const gchar *who,
}
static void
-jabber_message_request_data_cb(JabberData *data, gchar *alt,
- gpointer userdata)
+jabber_message_remote_smiley_got(JabberData *data, gchar *alt, gpointer _smiley)
{
- PurpleConversation *conv = (PurpleConversation *) userdata;
+ PurpleSmiley *smiley = _smiley;
+ PurpleImage *image = purple_smiley_get_image(smiley);
+
+ g_free(alt); /* we really don't need it */
if (data) {
- purple_conv_custom_smiley_write(conv, alt,
- jabber_data_get_data(data),
- jabber_data_get_size(data));
- purple_conv_custom_smiley_close(conv, alt);
+ purple_debug_info("jabber",
+ "smiley data retrieved successfully");
+ purple_image_transfer_write(image, jabber_data_get_data(data),
+ jabber_data_get_size(data));
+ purple_image_transfer_close(image);
+ } else {
+ purple_debug_error("jabber", "failed retrieving smiley data");
+ purple_image_transfer_failed(image);
}
- g_free(alt);
+ g_object_unref(smiley);
}
-void jabber_message_parse(JabberStream *js, xmlnode *packet)
+static void
+jabber_message_remote_smiley_add(JabberStream *js, PurpleConversation *conv,
+ const gchar *from, const gchar *shortcut, const gchar *cid)
+{
+ PurpleSmiley *smiley;
+ const JabberData *jdata;
+
+ purple_debug_misc("jabber", "about to add remote smiley %s to the conv",
+ shortcut);
+
+ smiley = purple_conversation_add_remote_smiley(conv, shortcut);
+ if (!smiley) {
+ purple_debug_misc("jabber", "smiley was already present");
+ return;
+ }
+
+ /* TODO: cache lookup by "cid" */
+
+ jdata = jabber_data_find_remote_by_cid(js, from, cid);
+ if (jdata) {
+ PurpleImage *image = purple_smiley_get_image(smiley);
+
+ purple_debug_info("jabber", "smiley data is already known");
+
+ purple_image_transfer_write(image, jabber_data_get_data(jdata),
+ jabber_data_get_size(jdata));
+ purple_image_transfer_close(image);
+ } else {
+ gchar *alt = g_strdup(shortcut); /* it it really necessary? */
+
+ purple_debug_info("jabber", "smiley data is unknown, "
+ "need to request it");
+
+ g_object_ref(smiley);
+ jabber_data_request(js, cid, from, alt, FALSE,
+ jabber_message_remote_smiley_got, smiley);
+ }
+}
+
+void jabber_message_parse(JabberStream *js, PurpleXmlNode *packet)
{
JabberMessage *jm;
const char *id, *from, *to, *type;
- xmlnode *child;
+ PurpleXmlNode *child;
gboolean signal_return;
- from = xmlnode_get_attrib(packet, "from");
- id = xmlnode_get_attrib(packet, "id");
- to = xmlnode_get_attrib(packet, "to");
- type = xmlnode_get_attrib(packet, "type");
+ from = purple_xmlnode_get_attrib(packet, "from");
+ id = purple_xmlnode_get_attrib(packet, "id");
+ to = purple_xmlnode_get_attrib(packet, "to");
+ type = purple_xmlnode_get_attrib(packet, "type");
signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_prpl(js->gc),
"jabber-receiving-message", js->gc, type, id, from, to, packet));
@@ -542,19 +593,19 @@ void jabber_message_parse(JabberStream *js, xmlnode *packet)
jm->id = g_strdup(id);
for(child = packet->child; child; child = child->next) {
- const char *xmlns = xmlnode_get_namespace(child);
- if(child->type != XMLNODE_TYPE_TAG)
+ const char *xmlns = purple_xmlnode_get_namespace(child);
+ if(child->type != PURPLE_XMLNODE_TYPE_TAG)
continue;
if(!strcmp(child->name, "error")) {
- const char *code = xmlnode_get_attrib(child, "code");
+ const char *code = purple_xmlnode_get_attrib(child, "code");
char *code_txt = NULL;
- char *text = xmlnode_get_data(child);
+ char *text = purple_xmlnode_get_data(child);
if (!text) {
- xmlnode *enclosed_text_node;
+ PurpleXmlNode *enclosed_text_node;
- if ((enclosed_text_node = xmlnode_get_child(child, "text")))
- text = xmlnode_get_data(enclosed_text_node);
+ if ((enclosed_text_node = purple_xmlnode_get_child(child, "text")))
+ text = purple_xmlnode_get_data(enclosed_text_node);
}
if(code)
@@ -577,29 +628,29 @@ void jabber_message_parse(JabberStream *js, xmlnode *packet)
continue;
} else if(!strcmp(child->name, "subject") && !strcmp(xmlns, NS_XMPP_CLIENT)) {
if(!jm->subject) {
- jm->subject = xmlnode_get_data(child);
+ jm->subject = purple_xmlnode_get_data(child);
if(!jm->subject)
jm->subject = g_strdup("");
}
} else if(!strcmp(child->name, "thread") && !strcmp(xmlns, NS_XMPP_CLIENT)) {
if(!jm->thread_id)
- jm->thread_id = xmlnode_get_data(child);
+ jm->thread_id = purple_xmlnode_get_data(child);
} else if(!strcmp(child->name, "body") && !strcmp(xmlns, NS_XMPP_CLIENT)) {
if(!jm->body) {
- char *msg = xmlnode_get_data(child);
+ char *msg = purple_xmlnode_get_data(child);
char *escaped = purple_markup_escape_text(msg, -1);
jm->body = purple_strdup_withhtml(escaped);
g_free(escaped);
g_free(msg);
}
} else if(!strcmp(child->name, "html") && !strcmp(xmlns, NS_XHTML_IM)) {
- if(!jm->xhtml && xmlnode_get_child(child, "body")) {
+ if(!jm->xhtml && purple_xmlnode_get_child(child, "body")) {
char *c;
const PurpleConnection *gc = js->gc;
PurpleAccount *account = purple_connection_get_account(gc);
PurpleConversation *conv = NULL;
- GList *smiley_refs = NULL;
+ GList *smiley_refs = NULL, *it;
gchar *reformatted_xhtml;
if (purple_account_get_bool(account, "custom_smileys", TRUE)) {
@@ -617,19 +668,17 @@ void jabber_message_parse(JabberStream *js, xmlnode *packet)
if (jid) {
chat = jabber_chat_find(js, jid->node, jid->domain);
if (chat)
- conv = chat->conv;
+ conv = PURPLE_CONVERSATION(chat->conv);
jabber_id_free(jid);
}
} else if (jm->type == JABBER_MESSAGE_NORMAL ||
jm->type == JABBER_MESSAGE_CHAT) {
conv =
- purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
- from, account);
+ purple_conversations_find_with_account(from, account);
if (!conv) {
/* we need to create the conversation here */
- conv =
- purple_conversation_new(PURPLE_CONV_TYPE_IM,
- account, from);
+ conv = PURPLE_CONVERSATION(
+ purple_im_conversation_new(account, from));
}
}
}
@@ -638,6 +687,8 @@ void jabber_message_parse(JabberStream *js, xmlnode *packet)
jabber_message_add_remote_smileys(js, to, packet);
}
+ purple_xmlnode_strip_prefixes(child);
+
/* reformat xhtml so that img tags with a "cid:" src gets
translated to the bare text of the emoticon (the "alt" attrib) */
/* this is done also when custom smiley retrieval is turned off,
@@ -651,37 +702,19 @@ void jabber_message_parse(JabberStream *js, xmlnode *packet)
/* note: if there were no smileys in the incoming message, or
if receiving custom smileys is turned off, smiley_refs will
be NULL */
- for (; conv && smiley_refs ; smiley_refs = g_list_delete_link(smiley_refs, smiley_refs)) {
- JabberSmileyRef *ref = (JabberSmileyRef *) smiley_refs->data;
- const gchar *cid = ref->cid;
- gchar *alt = g_strdup(ref->alt);
-
- purple_debug_info("jabber",
- "about to add custom smiley %s to the conv\n", alt);
- if (purple_conv_custom_smiley_add(conv, alt, "cid", cid,
- TRUE)) {
- const JabberData *data =
- jabber_data_find_remote_by_cid(js, from, cid);
- /* if data is already known, we write it immediatly */
- if (data) {
- purple_debug_info("jabber",
- "data is already known\n");
- purple_conv_custom_smiley_write(conv, alt,
- jabber_data_get_data(data),
- jabber_data_get_size(data));
- purple_conv_custom_smiley_close(conv, alt);
- } else {
- /* we need to request the smiley (data) */
- purple_debug_info("jabber",
- "data is unknown, need to request it\n");
- jabber_data_request(js, cid, from, alt, FALSE,
- jabber_message_request_data_cb, conv);
- }
+ for (it = smiley_refs; it; it = g_list_next(it)) {
+ JabberSmileyRef *ref = it->data;
+
+ if (conv) {
+ jabber_message_remote_smiley_add(js,
+ conv, from, ref->alt, ref->cid);
}
+
g_free(ref->cid);
g_free(ref->alt);
g_free(ref);
}
+ g_list_free(smiley_refs);
/* Convert all newlines to whitespace. Technically, even regular, non-XML HTML is supposed to ignore newlines, but Pidgin has, as convention
* treated \n as a newline for compatibility with other protocols
@@ -702,30 +735,30 @@ void jabber_message_parse(JabberStream *js, xmlnode *packet)
} else if(!strcmp(child->name, "gone") && !strcmp(xmlns,"http://jabber.org/protocol/chatstates")) {
jm->chat_state = JM_STATE_GONE;
} else if(!strcmp(child->name, "event") && !strcmp(xmlns,"http://jabber.org/protocol/pubsub#event")) {
- xmlnode *items;
+ PurpleXmlNode *items;
jm->type = JABBER_MESSAGE_EVENT;
- for(items = xmlnode_get_child(child,"items"); items; items = items->next)
+ for(items = purple_xmlnode_get_child(child,"items"); items; items = items->next)
jm->eventitems = g_list_append(jm->eventitems, items);
} else if(!strcmp(child->name, "attention") && !strcmp(xmlns, NS_ATTENTION)) {
jm->hasBuzz = TRUE;
} else if(!strcmp(child->name, "delay") && !strcmp(xmlns, NS_DELAYED_DELIVERY)) {
- const char *timestamp = xmlnode_get_attrib(child, "stamp");
+ const char *timestamp = purple_xmlnode_get_attrib(child, "stamp");
jm->delayed = TRUE;
if(timestamp)
jm->sent = purple_str_to_time(timestamp, TRUE, NULL, NULL, NULL);
} else if(!strcmp(child->name, "x")) {
if(!strcmp(xmlns, NS_DELAYED_DELIVERY_LEGACY)) {
- const char *timestamp = xmlnode_get_attrib(child, "stamp");
+ const char *timestamp = purple_xmlnode_get_attrib(child, "stamp");
jm->delayed = TRUE;
if(timestamp)
jm->sent = purple_str_to_time(timestamp, TRUE, NULL, NULL, NULL);
} else if(!strcmp(xmlns, "jabber:x:conference") &&
jm->type != JABBER_MESSAGE_GROUPCHAT_INVITE &&
jm->type != JABBER_MESSAGE_ERROR) {
- const char *jid = xmlnode_get_attrib(child, "jid");
+ const char *jid = purple_xmlnode_get_attrib(child, "jid");
if(jid) {
- const char *reason = xmlnode_get_attrib(child, "reason");
- const char *password = xmlnode_get_attrib(child, "password");
+ const char *reason = purple_xmlnode_get_attrib(child, "reason");
+ const char *password = purple_xmlnode_get_attrib(child, "password");
jm->type = JABBER_MESSAGE_GROUPCHAT_INVITE;
g_free(jm->to);
@@ -743,20 +776,20 @@ void jabber_message_parse(JabberStream *js, xmlnode *packet)
}
} else if(!strcmp(xmlns, "http://jabber.org/protocol/muc#user") &&
jm->type != JABBER_MESSAGE_ERROR) {
- xmlnode *invite = xmlnode_get_child(child, "invite");
+ PurpleXmlNode *invite = purple_xmlnode_get_child(child, "invite");
if(invite) {
- xmlnode *reason, *password;
- const char *jid = xmlnode_get_attrib(invite, "from");
+ PurpleXmlNode *reason, *password;
+ const char *jid = purple_xmlnode_get_attrib(invite, "from");
g_free(jm->to);
jm->to = jm->from;
jm->from = g_strdup(jid);
- if((reason = xmlnode_get_child(invite, "reason"))) {
+ if((reason = purple_xmlnode_get_child(invite, "reason"))) {
g_free(jm->body);
- jm->body = xmlnode_get_data(reason);
+ jm->body = purple_xmlnode_get_data(reason);
}
- if((password = xmlnode_get_child(child, "password"))) {
+ if((password = purple_xmlnode_get_child(child, "password"))) {
g_free(jm->password);
- jm->password = xmlnode_get_data(password);
+ jm->password = purple_xmlnode_get_data(password);
}
jm->type = JABBER_MESSAGE_GROUPCHAT_INVITE;
@@ -765,7 +798,7 @@ void jabber_message_parse(JabberStream *js, xmlnode *packet)
jm->etc = g_list_append(jm->etc, child);
}
} else if (g_str_equal(child->name, "query")) {
- const char *node = xmlnode_get_attrib(child, "node");
+ const char *node = purple_xmlnode_get_attrib(child, "node");
if (purple_strequal(xmlns, NS_DISCO_ITEMS)
&& purple_strequal(node, "http://jabber.org/protocol/commands")) {
jabber_adhoc_got_list(js, jm->from, child);
@@ -804,205 +837,164 @@ void jabber_message_parse(JabberStream *js, xmlnode *packet)
jabber_message_free(jm);
}
-static const gchar *
-jabber_message_get_mimetype_from_ext(const gchar *ext)
+static gboolean
+jabber_conv_support_custom_smileys(JabberStream *js,
+ PurpleConversation *conv,
+ const gchar *who)
{
- if (strcmp(ext, "png") == 0) {
- return "image/png";
- } else if (strcmp(ext, "gif") == 0) {
- return "image/gif";
- } else if (strcmp(ext, "jpg") == 0) {
- return "image/jpeg";
- } else if (strcmp(ext, "tif") == 0) {
- return "image/tif";
+ JabberBuddy *jb;
+ JabberChat *chat;
+
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
+ jb = jabber_buddy_find(js, who, FALSE);
+ if (jb) {
+ return jabber_buddy_has_capability(jb, NS_BOB);
+ } else {
+ return FALSE;
+ }
+ } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
+ chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv));
+ if (chat) {
+ /* do not attempt to send custom smileys in a MUC with more than
+ 10 people, to avoid getting too many BoB requests */
+ return jabber_chat_get_num_participants(chat) <= 10 &&
+ jabber_chat_all_participants_have_capability(chat,
+ NS_BOB);
+ } else {
+ return FALSE;
+ }
} else {
- return "image/x-icon"; /* or something... */
+ return FALSE;
}
}
-static GList *
-jabber_message_xhtml_find_smileys(const char *xhtml)
+static gboolean
+jabber_message_smileyify_cb(GString *out, PurpleSmiley *smiley,
+ PurpleConversation *_empty, gpointer _unused)
{
- GList *smileys = purple_smileys_get_all();
- GList *found_smileys = NULL;
+ const gchar *shortcut;
+ const JabberData *data;
+ PurpleXmlNode *smiley_node;
+ gchar *node_xml;
- for (; smileys ; smileys = g_list_delete_link(smileys, smileys)) {
- PurpleSmiley *smiley = (PurpleSmiley *) smileys->data;
+ shortcut = purple_smiley_get_shortcut(smiley);
+ data = jabber_data_find_local_by_alt(shortcut);
- const gchar *shortcut = purple_smiley_get_shortcut(smiley);
- const gssize len = strlen(shortcut);
+ if (!data)
+ return FALSE;
- gchar *escaped = g_markup_escape_text(shortcut, len);
- const char *pos = strstr(xhtml, escaped);
+ smiley_node = jabber_data_get_xhtml_im(data, shortcut);
+ node_xml = purple_xmlnode_to_str(smiley_node, NULL);
- if (pos) {
- found_smileys = g_list_append(found_smileys, smiley);
- }
+ g_string_append(out, node_xml);
- g_free(escaped);
- }
+ purple_xmlnode_free(smiley_node);
+ g_free(node_xml);
- return found_smileys;
+ return TRUE;
}
-static gchar *
-jabber_message_get_smileyfied_xhtml(const gchar *xhtml, const GList *smileys)
+static char *
+jabber_message_smileyfy_xhtml(JabberMessage *jm, const char *xhtml)
{
- /* create XML element for all smileys (img tags) */
- GString *result = g_string_new(NULL);
- int pos = 0;
- int length = strlen(xhtml);
-
- while (pos < length) {
- const GList *iterator;
- gboolean found_smiley = FALSE;
-
- for (iterator = smileys ; iterator ;
- iterator = g_list_next(iterator)) {
- const PurpleSmiley *smiley = (PurpleSmiley *) iterator->data;
- const gchar *shortcut = purple_smiley_get_shortcut(smiley);
- const gssize len = strlen(shortcut);
- gchar *escaped = g_markup_escape_text(shortcut, len);
-
- if (g_str_has_prefix(&(xhtml[pos]), escaped)) {
- /* we found the current smiley at this position */
- const JabberData *data =
- jabber_data_find_local_by_alt(shortcut);
- xmlnode *img = jabber_data_get_xhtml_im(data, shortcut);
- int len;
- gchar *img_text = xmlnode_to_str(img, &len);
-
- found_smiley = TRUE;
- result = g_string_append(result, img_text);
- g_free(img_text);
- pos += strlen(escaped);
- g_free(escaped);
- xmlnode_free(img);
- break;
- } else {
- /* cleanup from the before the next round... */
- g_free(escaped);
- }
- }
- if (!found_smiley) {
- /* there was no smiley here, just copy one byte */
- result = g_string_append_c(result, xhtml[pos]);
- pos++;
+ PurpleAccount *account = purple_connection_get_account(jm->js->gc);
+ GList *found_smileys, *it, *it_next;
+ PurpleConversation *conv;
+ gboolean has_too_large_smiley = FALSE;
+ gchar *smileyfied_xhtml = NULL;
+
+ conv = purple_conversations_find_with_account(jm->to, account);
+
+ if (!jabber_conv_support_custom_smileys(jm->js, conv, jm->to))
+ return NULL;
+
+ found_smileys = purple_smiley_parser_find(
+ purple_smiley_custom_get_list(), xhtml, TRUE);
+ if (!found_smileys)
+ return NULL;
+
+ for (it = found_smileys; it; it = it_next) {
+ PurpleSmiley *smiley = it->data;
+ PurpleImage *smiley_image;
+ gboolean valid = TRUE;
+
+ it_next = g_list_next(it);
+
+ smiley_image = purple_smiley_get_image(smiley);
+ if (!smiley_image) {
+ valid = FALSE;
+ purple_debug_warning("jabber", "broken smiley %s",
+ purple_smiley_get_shortcut(smiley));
+ } else if (purple_image_get_size(smiley_image) >
+ JABBER_DATA_MAX_SIZE)
+ {
+ has_too_large_smiley = TRUE;
+ valid = FALSE;
+ purple_debug_warning("jabber", "Refusing to send "
+ "smiley %s (too large, max is %d)",
+ purple_smiley_get_shortcut(smiley),
+ JABBER_DATA_MAX_SIZE);
}
- }
- return g_string_free(result, FALSE);
-}
-
-static gboolean
-jabber_conv_support_custom_smileys(JabberStream *js,
- PurpleConversation *conv,
- const gchar *who)
-{
- JabberBuddy *jb;
- JabberChat *chat;
+ if (!valid)
+ found_smileys = g_list_delete_link(found_smileys, it);
+ }
- switch (purple_conversation_get_type(conv)) {
- case PURPLE_CONV_TYPE_IM:
- jb = jabber_buddy_find(js, who, FALSE);
- if (jb) {
- return jabber_buddy_has_capability(jb, NS_BOB);
- } else {
- return FALSE;
- }
- break;
- case PURPLE_CONV_TYPE_CHAT:
- chat = jabber_chat_find_by_conv(conv);
- if (chat) {
- /* do not attempt to send custom smileys in a MUC with more than
- 10 people, to avoid getting too many BoB requests */
- return jabber_chat_get_num_participants(chat) <= 10 &&
- jabber_chat_all_participants_have_capability(chat,
- NS_BOB);
- } else {
- return FALSE;
- }
- break;
- default:
- return FALSE;
- break;
+ if (has_too_large_smiley) {
+ purple_conversation_write_system_message(conv,
+ _("A custom smiley in the message is too large to send."),
+ PURPLE_MESSAGE_ERROR);
}
-}
-static char *
-jabber_message_smileyfy_xhtml(JabberMessage *jm, const char *xhtml)
-{
- PurpleAccount *account = purple_connection_get_account(jm->js->gc);
- PurpleConversation *conv =
- purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, jm->to,
- account);
-
- if (jabber_conv_support_custom_smileys(jm->js, conv, jm->to)) {
- GList *found_smileys = jabber_message_xhtml_find_smileys(xhtml);
-
- if (found_smileys) {
- gchar *smileyfied_xhtml = NULL;
- const GList *iterator;
- GList *valid_smileys = NULL;
- gboolean has_too_large_smiley = FALSE;
-
- for (iterator = found_smileys; iterator ;
- iterator = g_list_next(iterator)) {
- PurpleSmiley *smiley = (PurpleSmiley *) iterator->data;
- const gchar *shortcut = purple_smiley_get_shortcut(smiley);
- const JabberData *data =
- jabber_data_find_local_by_alt(shortcut);
- PurpleStoredImage *image = purple_smiley_get_stored_image(smiley);
-
- if (purple_imgstore_get_size(image) <= JABBER_DATA_MAX_SIZE) {
- /* the object has not been sent before */
- if (!data) {
- const gchar *ext = purple_imgstore_get_extension(image);
- JabberStream *js = jm->js;
-
- JabberData *new_data =
- jabber_data_create_from_data(purple_imgstore_get_data(image),
- purple_imgstore_get_size(image),
- jabber_message_get_mimetype_from_ext(ext), FALSE, js);
- purple_debug_info("jabber",
- "cache local smiley alt = %s, cid = %s\n",
- shortcut, jabber_data_get_cid(new_data));
- jabber_data_associate_local(new_data, shortcut);
- }
- valid_smileys = g_list_append(valid_smileys, smiley);
- } else {
- has_too_large_smiley = TRUE;
- purple_debug_warning("jabber", "Refusing to send smiley %s "
- "(too large, max is %d)\n",
- purple_smiley_get_shortcut(smiley),
- JABBER_DATA_MAX_SIZE);
- }
- }
+ if (!found_smileys)
+ return NULL;
- if (has_too_large_smiley) {
- purple_conversation_write(conv, NULL,
- _("A custom smiley in the message is too large to send."),
- PURPLE_MESSAGE_ERROR, time(NULL));
- }
+ for (it = found_smileys; it; it = g_list_next(it)) {
+ PurpleSmiley *smiley = it->data;
+ PurpleImage *smiley_image;
+ const gchar *shortcut = purple_smiley_get_shortcut(smiley);
+ const gchar *mimetype;
+ JabberData *jdata;
+
+ /* the object has been sent before */
+ if (jabber_data_find_local_by_alt(shortcut))
+ continue;
- smileyfied_xhtml =
- jabber_message_get_smileyfied_xhtml(xhtml, valid_smileys);
- g_list_free(found_smileys);
- g_list_free(valid_smileys);
+ smiley_image = purple_smiley_get_image(smiley);
+ g_assert(smiley_image != NULL);
- return smileyfied_xhtml;
+ mimetype = purple_image_get_mimetype(smiley_image);
+ if (!mimetype) {
+ purple_debug_error("jabber",
+ "unknown mime type for image");
+ continue;
}
+
+ jdata = jabber_data_create_from_data(
+ purple_image_get_data(smiley_image),
+ purple_image_get_size(smiley_image),
+ mimetype, FALSE, jm->js);
+
+ purple_debug_info("jabber", "cache local smiley alt=%s, cid=%s",
+ shortcut, jabber_data_get_cid(jdata));
+ jabber_data_associate_local(jdata, shortcut);
}
- return NULL;
+ g_list_free(found_smileys);
+
+ smileyfied_xhtml = purple_smiley_parser_replace(
+ purple_smiley_custom_get_list(), xhtml,
+ jabber_message_smileyify_cb, NULL);
+
+ return smileyfied_xhtml;
}
void jabber_message_send(JabberMessage *jm)
{
- xmlnode *message, *child;
+ PurpleXmlNode *message, *child;
const char *type = NULL;
- message = xmlnode_new("message");
+ message = purple_xmlnode_new("message");
switch(jm->type) {
case JABBER_MESSAGE_NORMAL:
@@ -1028,56 +1020,56 @@ void jabber_message_send(JabberMessage *jm)
}
if(type)
- xmlnode_set_attrib(message, "type", type);
+ purple_xmlnode_set_attrib(message, "type", type);
if (jm->id)
- xmlnode_set_attrib(message, "id", jm->id);
+ purple_xmlnode_set_attrib(message, "id", jm->id);
- xmlnode_set_attrib(message, "to", jm->to);
+ purple_xmlnode_set_attrib(message, "to", jm->to);
if(jm->thread_id) {
- child = xmlnode_new_child(message, "thread");
- xmlnode_insert_data(child, jm->thread_id, -1);
+ child = purple_xmlnode_new_child(message, "thread");
+ purple_xmlnode_insert_data(child, jm->thread_id, -1);
}
child = NULL;
switch(jm->chat_state)
{
case JM_STATE_ACTIVE:
- child = xmlnode_new_child(message, "active");
+ child = purple_xmlnode_new_child(message, "active");
break;
case JM_STATE_COMPOSING:
- child = xmlnode_new_child(message, "composing");
+ child = purple_xmlnode_new_child(message, "composing");
break;
case JM_STATE_PAUSED:
- child = xmlnode_new_child(message, "paused");
+ child = purple_xmlnode_new_child(message, "paused");
break;
case JM_STATE_INACTIVE:
- child = xmlnode_new_child(message, "inactive");
+ child = purple_xmlnode_new_child(message, "inactive");
break;
case JM_STATE_GONE:
- child = xmlnode_new_child(message, "gone");
+ child = purple_xmlnode_new_child(message, "gone");
break;
case JM_STATE_NONE:
/* yep, nothing */
break;
}
if(child)
- xmlnode_set_namespace(child, "http://jabber.org/protocol/chatstates");
+ purple_xmlnode_set_namespace(child, "http://jabber.org/protocol/chatstates");
if(jm->subject) {
- child = xmlnode_new_child(message, "subject");
- xmlnode_insert_data(child, jm->subject, -1);
+ child = purple_xmlnode_new_child(message, "subject");
+ purple_xmlnode_insert_data(child, jm->subject, -1);
}
if(jm->body) {
- child = xmlnode_new_child(message, "body");
- xmlnode_insert_data(child, jm->body, -1);
+ child = purple_xmlnode_new_child(message, "body");
+ purple_xmlnode_insert_data(child, jm->body, -1);
}
if(jm->xhtml) {
- if ((child = xmlnode_from_str(jm->xhtml, -1))) {
- xmlnode_insert_child(message, child);
+ if ((child = purple_xmlnode_from_str(jm->xhtml, -1))) {
+ purple_xmlnode_insert_child(message, child);
} else {
purple_debug_error("jabber",
"XHTML translation/validation failed, returning: %s\n",
@@ -1087,7 +1079,7 @@ void jabber_message_send(JabberMessage *jm)
jabber_send(jm->js, message);
- xmlnode_free(message);
+ purple_xmlnode_free(message);
}
/*
@@ -1127,8 +1119,7 @@ jabber_xhtml_plain_equal(const char *xhtml_escaped,
return ret;
}
-int jabber_message_send_im(PurpleConnection *gc, const char *who, const char *msg,
- PurpleMessageFlags flags)
+int jabber_message_send_im(PurpleConnection *gc, PurpleMessage *msg)
{
JabberMessage *jm;
JabberBuddy *jb;
@@ -1136,28 +1127,23 @@ int jabber_message_send_im(PurpleConnection *gc, const char *who, const char *ms
char *xhtml;
char *tmp;
char *resource;
+ const gchar *rcpt = purple_message_get_recipient(msg);
- if(!who || !msg)
+ if (!rcpt || purple_message_is_empty(msg))
return 0;
- if (purple_debug_is_verbose()) {
- /* TODO: Maybe we need purple_debug_is_really_verbose? :) */
- purple_debug_misc("jabber", "jabber_message_send_im: who='%s'\n"
- "\tmsg='%s'\n", who, msg);
- }
-
- resource = jabber_get_resource(who);
+ resource = jabber_get_resource(rcpt);
- jb = jabber_buddy_find(gc->proto_data, who, TRUE);
+ jb = jabber_buddy_find(purple_connection_get_protocol_data(gc), rcpt, TRUE);
jbr = jabber_buddy_find_resource(jb, resource);
g_free(resource);
jm = g_new0(JabberMessage, 1);
- jm->js = gc->proto_data;
+ jm->js = purple_connection_get_protocol_data(gc);
jm->type = JABBER_MESSAGE_CHAT;
jm->chat_state = JM_STATE_ACTIVE;
- jm->to = g_strdup(who);
+ jm->to = g_strdup(rcpt);
jm->id = jabber_get_next_id(jm->js);
if(jbr) {
@@ -1172,7 +1158,7 @@ int jabber_message_send_im(PurpleConnection *gc, const char *who, const char *ms
}
}
- tmp = purple_utf8_strip_unprintables(msg);
+ tmp = purple_utf8_strip_unprintables(purple_message_get_contents(msg));
purple_markup_html_to_xhtml(tmp, &xhtml, &jm->body);
g_free(tmp);
@@ -1200,7 +1186,7 @@ int jabber_message_send_im(PurpleConnection *gc, const char *who, const char *ms
return 1;
}
-int jabber_message_send_chat(PurpleConnection *gc, int id, const char *msg, PurpleMessageFlags flags)
+int jabber_message_send_chat(PurpleConnection *gc, int id, PurpleMessage *msg)
{
JabberChat *chat;
JabberMessage *jm;
@@ -1208,22 +1194,22 @@ int jabber_message_send_chat(PurpleConnection *gc, int id, const char *msg, Purp
char *xhtml;
char *tmp;
- if(!msg || !gc)
+ if (!gc || purple_message_is_empty(msg))
return 0;
- js = gc->proto_data;
+ js = purple_connection_get_protocol_data(gc);
chat = jabber_chat_find_by_id(js, id);
if(!chat)
return 0;
jm = g_new0(JabberMessage, 1);
- jm->js = gc->proto_data;
+ jm->js = purple_connection_get_protocol_data(gc);
jm->type = JABBER_MESSAGE_GROUPCHAT;
jm->to = g_strdup_printf("%s@%s", chat->room, chat->server);
jm->id = jabber_get_next_id(jm->js);
- tmp = purple_utf8_strip_unprintables(msg);
+ tmp = purple_utf8_strip_unprintables(purple_message_get_contents(msg));
purple_markup_html_to_xhtml(tmp, &xhtml, &jm->body);
g_free(tmp);
tmp = jabber_message_smileyfy_xhtml(jm, xhtml);
@@ -1244,7 +1230,7 @@ int jabber_message_send_chat(PurpleConnection *gc, int id, const char *msg, Purp
return 1;
}
-unsigned int jabber_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state)
+unsigned int jabber_send_typing(PurpleConnection *gc, const char *who, PurpleIMTypingState state)
{
JabberStream *js;
JabberMessage *jm;
@@ -1278,9 +1264,9 @@ unsigned int jabber_send_typing(PurpleConnection *gc, const char *who, PurpleTyp
jm->to = g_strdup(who);
jm->id = jabber_get_next_id(jm->js);
- if(PURPLE_TYPING == state)
+ if(PURPLE_IM_TYPING == state)
jm->chat_state = JM_STATE_COMPOSING;
- else if(PURPLE_TYPED == state)
+ else if(PURPLE_IM_TYPED == state)
jm->chat_state = JM_STATE_PAUSED;
else
jm->chat_state = JM_STATE_ACTIVE;
diff --git a/libpurple/protocols/jabber/message.h b/libpurple/protocols/jabber/message.h
index 555f8f94b7..a1496e2beb 100644
--- a/libpurple/protocols/jabber/message.h
+++ b/libpurple/protocols/jabber/message.h
@@ -68,15 +68,14 @@ void jabber_message_free(JabberMessage *jm);
void jabber_message_send(JabberMessage *jm);
-void jabber_message_parse(JabberStream *js, xmlnode *packet);
-int jabber_message_send_im(PurpleConnection *gc, const char *who, const char *msg,
- PurpleMessageFlags flags);
-int jabber_message_send_chat(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags);
+void jabber_message_parse(JabberStream *js, PurpleXmlNode *packet);
+int jabber_message_send_im(PurpleConnection *gc, PurpleMessage *msg);
+int jabber_message_send_chat(PurpleConnection *gc, int id, PurpleMessage *msg);
-unsigned int jabber_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state);
+unsigned int jabber_send_typing(PurpleConnection *gc, const char *who, PurpleIMTypingState state);
gboolean jabber_buzz_isenabled(JabberStream *js, const gchar *namespace);
-gboolean jabber_custom_smileys_isenabled(JabberStream *js, const const gchar *namespace);
+gboolean jabber_custom_smileys_isenabled(JabberStream *js, const gchar *namespace);
#endif /* PURPLE_JABBER_MESSAGE_H_ */
diff --git a/libpurple/protocols/jabber/namespaces.h b/libpurple/protocols/jabber/namespaces.h
index 12c42411c1..042077544e 100644
--- a/libpurple/protocols/jabber/namespaces.h
+++ b/libpurple/protocols/jabber/namespaces.h
@@ -109,4 +109,9 @@
#define NS_GOOGLE_SESSION_PHONE "http://www.google.com/session/phone"
#define NS_GOOGLE_SESSION_VIDEO "http://www.google.com/session/video"
+#define NS_GOOGLE_TRANSPORT_P2P "http://www.google.com/transport/p2p"
+
+/* Apple extension(s) */
+#define NS_APPLE_IDLE "http://www.apple.com/xmpp/idle"
+
#endif /* PURPLE_JABBER_NAMESPACES_H_ */
diff --git a/libpurple/protocols/jabber/oob.c b/libpurple/protocols/jabber/oob.c
index c59c4ce936..f77797ce84 100644
--- a/libpurple/protocols/jabber/oob.c
+++ b/libpurple/protocols/jabber/oob.c
@@ -22,7 +22,8 @@
*/
#include "internal.h"
#include "debug.h"
-#include "ft.h"
+#include "xfer.h"
+#include "http.h"
#include "util.h"
#include "jabber.h"
@@ -30,53 +31,37 @@
#include "oob.h"
typedef struct _JabberOOBXfer {
- char *address;
- int port;
- char *page;
-
- GString *headers;
-
- char *iq_id;
-
JabberStream *js;
-
- gchar *write_buffer;
- gsize written_len;
- guint writeh;
-
+ gchar *iq_id;
+ gchar *url;
+ PurpleHttpConnection *hc;
} JabberOOBXfer;
static void jabber_oob_xfer_init(PurpleXfer *xfer)
{
- JabberOOBXfer *jox = xfer->data;
- purple_xfer_start(xfer, -1, jox->address, jox->port);
+ purple_xfer_start(xfer, -1, NULL, 0);
}
static void jabber_oob_xfer_free(PurpleXfer *xfer)
{
- JabberOOBXfer *jox = xfer->data;
+ 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_string_free(jox->headers, TRUE);
- g_free(jox->address);
- g_free(jox->page);
g_free(jox->iq_id);
- g_free(jox->write_buffer);
- if(jox->writeh)
- purple_input_remove(jox->writeh);
+ g_free(jox->url);
g_free(jox);
-
- xfer->data = NULL;
}
static void jabber_oob_xfer_end(PurpleXfer *xfer)
{
- JabberOOBXfer *jox = xfer->data;
+ JabberOOBXfer *jox = purple_xfer_get_protocol_data(xfer);
JabberIq *iq;
iq = jabber_iq_new(jox->js, JABBER_IQ_RESULT);
- xmlnode_set_attrib(iq->node, "to", xfer->who);
+ purple_xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer));
jabber_iq_set_id(iq, jox->iq_id);
jabber_iq_send(iq);
@@ -84,102 +69,88 @@ static void jabber_oob_xfer_end(PurpleXfer *xfer)
jabber_oob_xfer_free(xfer);
}
-static void jabber_oob_xfer_request_send(gpointer data, gint source, PurpleInputCondition cond) {
- PurpleXfer *xfer = data;
- JabberOOBXfer *jox = xfer->data;
- int len, total_len = strlen(jox->write_buffer);
-
- len = write(xfer->fd, jox->write_buffer + jox->written_len,
- total_len - jox->written_len);
+static void
+jabber_oob_xfer_got(PurpleHttpConnection *hc, PurpleHttpResponse *response,
+ gpointer _xfer)
+{
+ PurpleXfer *xfer = _xfer;
+ JabberOOBXfer *jox;
- if(len < 0 && errno == EAGAIN)
+ if (purple_xfer_is_cancelled(xfer))
return;
- else if(len < 0) {
- purple_debug_error("jabber", "Write error on oob xfer!\n");
- purple_input_remove(jox->writeh);
- purple_xfer_cancel_local(xfer);
- }
- jox->written_len += len;
- if(jox->written_len == total_len) {
- purple_input_remove(jox->writeh);
- g_free(jox->write_buffer);
- jox->write_buffer = NULL;
+ jox = purple_xfer_get_protocol_data(xfer);
+ jox->hc = NULL;
+
+ if (!purple_http_response_is_successful(response) ||
+ purple_xfer_get_bytes_remaining(xfer) > 0)
+ {
+ purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE);
+ purple_xfer_end(xfer);
+ } else {
+ purple_xfer_set_completed(xfer, TRUE);
+ purple_xfer_end(xfer);
}
}
-static void jabber_oob_xfer_start(PurpleXfer *xfer)
+static void
+jabber_oob_xfer_progress_watcher(PurpleHttpConnection *hc,
+ gboolean reading_state, int processed, int total, gpointer _xfer)
{
- JabberOOBXfer *jox = xfer->data;
+ PurpleXfer *xfer = _xfer;
- if(jox->write_buffer == NULL) {
- jox->write_buffer = g_strdup_printf(
- "GET /%s HTTP/1.1\r\nHost: %s\r\n\r\n",
- jox->page, jox->address);
- jox->written_len = 0;
- }
-
- jox->writeh = purple_input_add(xfer->fd, PURPLE_INPUT_WRITE,
- jabber_oob_xfer_request_send, xfer);
+ if (!reading_state)
+ return;
- jabber_oob_xfer_request_send(xfer, xfer->fd, PURPLE_INPUT_WRITE);
+ purple_xfer_set_size(xfer, total);
+ purple_xfer_update_progress(xfer);
}
-static gssize jabber_oob_xfer_read(guchar **buffer, PurpleXfer *xfer) {
- JabberOOBXfer *jox = xfer->data;
- char test[2048];
- char *tmp, *lenstr;
- int len;
-
- if((len = read(xfer->fd, test, sizeof(test))) > 0) {
- jox->headers = g_string_append_len(jox->headers, test, len);
- if((tmp = strstr(jox->headers->str, "\r\n\r\n"))) {
- *tmp = '\0';
- lenstr = strstr(jox->headers->str, "Content-Length: ");
- if(lenstr) {
- gsize size;
- if (sscanf(lenstr, "Content-Length: %" G_GSIZE_FORMAT, &size) == 1)
- purple_xfer_set_size(xfer, size);
- else {
- purple_debug_error("jabber", "Unable to parse Content-Length!\n");
- purple_xfer_cancel_local(xfer);
- return 0;
- }
- }
- purple_xfer_set_read_fnc(xfer, NULL);
-
- tmp += 4;
-
- *buffer = (unsigned char*) g_strdup(tmp);
- return strlen(tmp);
- }
- return 0;
- } else if (errno != EAGAIN) {
- purple_debug_error("jabber", "Read error on oob xfer!\n");
- purple_xfer_cancel_local(xfer);
- }
+static gboolean
+jabber_oob_xfer_writer(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, const gchar *buffer, size_t offset,
+ size_t length, gpointer _xfer)
+{
+ PurpleXfer *xfer = _xfer;
- return 0;
+ return purple_xfer_write_file(xfer, (const guchar*)buffer, length);
+}
+
+static void jabber_oob_xfer_start(PurpleXfer *xfer)
+{
+ PurpleHttpRequest *req;
+ JabberOOBXfer *jox = purple_xfer_get_protocol_data(xfer);
+
+ req = purple_http_request_new(jox->url);
+ purple_http_request_set_timeout(req, -1);
+ purple_http_request_set_max_len(req, -1);
+ purple_http_request_set_response_writer(req, jabber_oob_xfer_writer,
+ xfer);
+ jox->hc = purple_http_request(jox->js->gc, req, jabber_oob_xfer_got,
+ xfer);
+
+ purple_http_conn_set_progress_watcher(jox->hc,
+ jabber_oob_xfer_progress_watcher, xfer, -1);
}
static void jabber_oob_xfer_recv_error(PurpleXfer *xfer, const char *code) {
- JabberOOBXfer *jox = xfer->data;
+ JabberOOBXfer *jox = purple_xfer_get_protocol_data(xfer);
JabberIq *iq;
- xmlnode *y, *z;
+ PurpleXmlNode *y, *z;
iq = jabber_iq_new(jox->js, JABBER_IQ_ERROR);
- xmlnode_set_attrib(iq->node, "to", xfer->who);
+ purple_xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer));
jabber_iq_set_id(iq, jox->iq_id);
- y = xmlnode_new_child(iq->node, "error");
- xmlnode_set_attrib(y, "code", code);
+ y = purple_xmlnode_new_child(iq->node, "error");
+ purple_xmlnode_set_attrib(y, "code", code);
if(!strcmp(code, "406")) {
- z = xmlnode_new_child(y, "not-acceptable");
- xmlnode_set_attrib(y, "type", "modify");
- xmlnode_set_namespace(z, NS_XMPP_STANZAS);
+ z = purple_xmlnode_new_child(y, "not-acceptable");
+ purple_xmlnode_set_attrib(y, "type", "modify");
+ purple_xmlnode_set_namespace(z, NS_XMPP_STANZAS);
} else if(!strcmp(code, "404")) {
- z = xmlnode_new_child(y, "not-found");
- xmlnode_set_attrib(y, "type", "cancel");
- xmlnode_set_namespace(z, NS_XMPP_STANZAS);
+ z = purple_xmlnode_new_child(y, "not-found");
+ purple_xmlnode_set_attrib(y, "type", "cancel");
+ purple_xmlnode_set_namespace(z, NS_XMPP_STANZAS);
}
jabber_iq_send(iq);
@@ -191,16 +162,19 @@ static void jabber_oob_xfer_recv_denied(PurpleXfer *xfer) {
}
static void jabber_oob_xfer_recv_cancelled(PurpleXfer *xfer) {
+ JabberOOBXfer *jox = purple_xfer_get_protocol_data(xfer);
+
+ purple_http_conn_cancel(jox->hc);
jabber_oob_xfer_recv_error(xfer, "404");
}
void jabber_oob_parse(JabberStream *js, const char *from, JabberIqType type,
- const char *id, xmlnode *querynode) {
+ const char *id, PurpleXmlNode *querynode) {
JabberOOBXfer *jox;
PurpleXfer *xfer;
- char *filename;
- char *url;
- xmlnode *urlnode;
+ const gchar *filename, *slash;
+ gchar *url;
+ PurpleXmlNode *urlnode;
if(type != JABBER_IQ_SET)
return;
@@ -208,44 +182,40 @@ void jabber_oob_parse(JabberStream *js, const char *from, JabberIqType type,
if(!from)
return;
- if(!(urlnode = xmlnode_get_child(querynode, "url")))
+ if(!(urlnode = purple_xmlnode_get_child(querynode, "url")))
return;
- url = xmlnode_get_data(urlnode);
+ url = purple_xmlnode_get_data(urlnode);
+ if (!url)
+ return;
- jox = g_new0(JabberOOBXfer, 1);
- if (!purple_url_parse(url, &jox->address, &jox->port, &jox->page, NULL, NULL)) {
+ xfer = purple_xfer_new(purple_connection_get_account(js->gc),
+ PURPLE_XFER_TYPE_RECEIVE, from);
+ if (!xfer) {
g_free(url);
return;
}
- g_free(url);
- jox->js = js;
- jox->headers = g_string_new("");
- jox->iq_id = g_strdup(id);
- xfer = purple_xfer_new(js->gc->account, PURPLE_XFER_RECEIVE, from);
- if (xfer)
- {
- xfer->data = jox;
-
- if(!(filename = g_strdup(g_strrstr(jox->page, "/"))))
- filename = g_strdup(jox->page);
-
- purple_xfer_set_filename(xfer, filename);
+ jox = g_new0(JabberOOBXfer, 1);
+ jox->iq_id = g_strdup(id);
+ jox->js = js;
+ jox->url = url;
+ purple_xfer_set_protocol_data(xfer, jox);
- g_free(filename);
+ slash = strrchr(url, '/');
+ if (slash == NULL)
+ filename = url;
+ else
+ filename = slash + 1;
+ purple_xfer_set_filename(xfer, filename);
- 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_read_fnc(xfer, jabber_oob_xfer_read);
- purple_xfer_set_start_fnc(xfer, jabber_oob_xfer_start);
+ 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);
- js->oob_file_transfers = g_list_append(js->oob_file_transfers, xfer);
+ js->oob_file_transfers = g_list_append(js->oob_file_transfers, xfer);
- purple_xfer_request(xfer);
- }
+ purple_xfer_request(xfer);
}
-
-
diff --git a/libpurple/protocols/jabber/oob.h b/libpurple/protocols/jabber/oob.h
index 89724232da..7d25898891 100644
--- a/libpurple/protocols/jabber/oob.h
+++ b/libpurple/protocols/jabber/oob.h
@@ -27,6 +27,6 @@
#include "jabber.h"
void jabber_oob_parse(JabberStream *js, const char *from, JabberIqType type,
- const char *id, xmlnode *querynode);
+ const char *id, PurpleXmlNode *querynode);
#endif /* PURPLE_JABBER_OOB_H_ */
diff --git a/libpurple/protocols/jabber/parser.c b/libpurple/protocols/jabber/parser.c
index 5e3aa950ef..a7e69039f4 100644
--- a/libpurple/protocols/jabber/parser.c
+++ b/libpurple/protocols/jabber/parser.c
@@ -38,7 +38,7 @@ jabber_parser_element_start_libxml(void *user_data,
int nb_attributes, int nb_defaulted, const xmlChar **attributes)
{
JabberStream *js = user_data;
- xmlnode *node;
+ PurpleXmlNode *node;
int i, j;
if(!element_name) {
@@ -52,7 +52,7 @@ jabber_parser_element_start_libxml(void *user_data,
*/
purple_debug_error("jabber", "Expecting stream header, got %s with "
"xmlns %s\n", element_name, namespace);
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
_("XMPP stream header missing"));
return;
@@ -73,7 +73,7 @@ jabber_parser_element_start_libxml(void *user_data,
if (js->protocol_version.major > 1) {
/* TODO: Send <unsupported-version/> error */
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
_("XMPP Version Mismatch"));
g_free(attrib);
@@ -98,7 +98,7 @@ jabber_parser_element_start_libxml(void *user_data,
/* This was underspecified in rfc3920 as only being a SHOULD, so
* we cannot rely on it. See #12331 and Oracle's server.
*/
- purple_connection_error_reason(js->gc,
+ purple_connection_error(js->gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
_("XMPP stream missing ID"));
#else
@@ -115,11 +115,11 @@ jabber_parser_element_start_libxml(void *user_data,
} else {
if(js->current)
- node = xmlnode_new_child(js->current, (const char*) element_name);
+ node = purple_xmlnode_new_child(js->current, (const char*) element_name);
else
- node = xmlnode_new((const char*) element_name);
- xmlnode_set_namespace(node, (const char*) namespace);
- xmlnode_set_prefix(node, (const char *)prefix);
+ node = purple_xmlnode_new((const char*) element_name);
+ purple_xmlnode_set_namespace(node, (const char*) namespace);
+ purple_xmlnode_set_prefix(node, (const char *)prefix);
if (nb_namespaces != 0) {
node->namespace_map = g_hash_table_new_full(
@@ -143,7 +143,7 @@ jabber_parser_element_start_libxml(void *user_data,
txt = attrib;
attrib = purple_unescape_text(txt);
g_free(txt);
- xmlnode_set_attrib_full(node, name, attrib_ns, prefix, attrib);
+ purple_xmlnode_set_attrib_full(node, name, attrib_ns, prefix, attrib);
g_free(attrib);
}
@@ -164,11 +164,11 @@ jabber_parser_element_end_libxml(void *user_data, const xmlChar *element_name,
if(!xmlStrcmp((xmlChar*) js->current->name, element_name))
js->current = js->current->parent;
} else {
- xmlnode *packet = js->current;
+ PurpleXmlNode *packet = js->current;
js->current = NULL;
jabber_process_packet(js, &packet);
if (packet != NULL)
- xmlnode_free(packet);
+ purple_xmlnode_free(packet);
}
}
@@ -183,7 +183,7 @@ jabber_parser_element_text_libxml(void *user_data, const xmlChar *text, int text
if(!text || !text_len)
return;
- xmlnode_insert_data(js->current, (const char*) text, text_len);
+ purple_xmlnode_insert_data(js->current, (const char*) text, text_len);
}
static void
@@ -300,7 +300,7 @@ void jabber_parser_process(JabberStream *js, const char *buf, int len)
break;
case XML_ERR_FATAL:
purple_debug_error("jabber", "xmlParseChunk returned fatal %i\n", ret);
- purple_connection_error_reason (js->gc,
+ purple_connection_error (js->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("XML Parse error"));
break;
@@ -308,7 +308,7 @@ void jabber_parser_process(JabberStream *js, const char *buf, int len)
}
if (js->protocol_version.major == 0 && js->protocol_version.minor == 9 &&
- !js->gc->disconnect_timeout &&
+ !purple_connection_get_error_info(js->gc) &&
(js->state == JABBER_STREAM_INITIALIZING ||
js->state == JABBER_STREAM_INITIALIZING_ENCRYPTION)) {
/*
diff --git a/libpurple/protocols/jabber/pep.c b/libpurple/protocols/jabber/pep.c
index 0063f9e611..b974aa68d1 100644
--- a/libpurple/protocols/jabber/pep.c
+++ b/libpurple/protocols/jabber/pep.c
@@ -69,16 +69,16 @@ void jabber_pep_register_handler(const char *xmlns, JabberPEPHandler handlerfunc
static void
do_pep_iq_request_item_callback(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
- xmlnode *pubsub;
- xmlnode *items = NULL;
+ PurpleXmlNode *pubsub;
+ PurpleXmlNode *items = NULL;
JabberPEPHandler *cb = data;
if (type == JABBER_IQ_RESULT) {
- pubsub = xmlnode_get_child_with_namespace(packet, "pubsub", "http://jabber.org/protocol/pubsub");
+ pubsub = purple_xmlnode_get_child_with_namespace(packet, "pubsub", "http://jabber.org/protocol/pubsub");
if(pubsub)
- items = xmlnode_get_child(pubsub, "items");
+ items = purple_xmlnode_get_child(pubsub, "items");
}
cb(js, from, items);
@@ -86,23 +86,23 @@ do_pep_iq_request_item_callback(JabberStream *js, const char *from,
void jabber_pep_request_item(JabberStream *js, const char *to, const char *node, const char *id, JabberPEPHandler cb) {
JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET);
- xmlnode *pubsub, *items;
+ PurpleXmlNode *pubsub, *items;
if (to)
- xmlnode_set_attrib(iq->node, "to", to);
+ purple_xmlnode_set_attrib(iq->node, "to", to);
- pubsub = xmlnode_new_child(iq->node,"pubsub");
- xmlnode_set_namespace(pubsub, "http://jabber.org/protocol/pubsub");
+ pubsub = purple_xmlnode_new_child(iq->node,"pubsub");
+ purple_xmlnode_set_namespace(pubsub, "http://jabber.org/protocol/pubsub");
- items = xmlnode_new_child(pubsub, "items");
- xmlnode_set_attrib(items,"node",node);
+ items = purple_xmlnode_new_child(pubsub, "items");
+ purple_xmlnode_set_attrib(items,"node",node);
if (id) {
- xmlnode *item = xmlnode_new_child(items, "item");
- xmlnode_set_attrib(item, "id", id);
+ PurpleXmlNode *item = purple_xmlnode_new_child(items, "item");
+ purple_xmlnode_set_attrib(item, "id", id);
} else
/* Most recent item */
- xmlnode_set_attrib(items, "max_items", "1");
+ purple_xmlnode_set_attrib(items, "max_items", "1");
jabber_iq_set_callback(iq,do_pep_iq_request_item_callback,(gpointer)cb);
@@ -125,8 +125,8 @@ void jabber_handle_event(JabberMessage *jm) {
jid = jabber_get_bare_jid(jm->from);
for(itemslist = jm->eventitems; itemslist; itemslist = itemslist->next) {
- xmlnode *items = (xmlnode*)itemslist->data;
- const char *nodename = xmlnode_get_attrib(items,"node");
+ PurpleXmlNode *items = (PurpleXmlNode*)itemslist->data;
+ const char *nodename = purple_xmlnode_get_attrib(items,"node");
if(nodename && (jph = g_hash_table_lookup(pep_handlers, nodename)))
jph(jm->js, jid, items);
@@ -139,40 +139,40 @@ void jabber_handle_event(JabberMessage *jm) {
void jabber_pep_delete_node(JabberStream *js, const gchar *node)
{
JabberIq *iq;
- xmlnode *pubsub, *del;
+ PurpleXmlNode *pubsub, *del;
g_return_if_fail(node != NULL);
g_return_if_fail(js->pep);
iq = jabber_iq_new(js, JABBER_IQ_SET);
- pubsub = xmlnode_new_child(iq->node, "pubsub");
- xmlnode_set_namespace(pubsub, "http://jabber.org/protocol/pubsub#owner");
+ pubsub = purple_xmlnode_new_child(iq->node, "pubsub");
+ purple_xmlnode_set_namespace(pubsub, "http://jabber.org/protocol/pubsub#owner");
- del = xmlnode_new_child(pubsub, "delete");
- xmlnode_set_attrib(del, "node", node);
+ del = purple_xmlnode_new_child(pubsub, "delete");
+ purple_xmlnode_set_attrib(del, "node", node);
jabber_iq_send(iq);
}
-void jabber_pep_publish(JabberStream *js, xmlnode *publish) {
+void jabber_pep_publish(JabberStream *js, PurpleXmlNode *publish) {
JabberIq *iq;
- xmlnode *pubsub;
+ PurpleXmlNode *pubsub;
if(js->pep != TRUE) {
/* ignore when there's no PEP support on the server */
- xmlnode_free(publish);
+ purple_xmlnode_free(publish);
return;
}
iq = jabber_iq_new(js, JABBER_IQ_SET);
- pubsub = xmlnode_new("pubsub");
- xmlnode_set_namespace(pubsub, "http://jabber.org/protocol/pubsub");
+ pubsub = purple_xmlnode_new("pubsub");
+ purple_xmlnode_set_namespace(pubsub, "http://jabber.org/protocol/pubsub");
- xmlnode_insert_child(pubsub, publish);
+ purple_xmlnode_insert_child(pubsub, publish);
- xmlnode_insert_child(iq->node, pubsub);
+ purple_xmlnode_insert_child(iq->node, pubsub);
jabber_iq_send(iq);
}
diff --git a/libpurple/protocols/jabber/pep.h b/libpurple/protocols/jabber/pep.h
index 30b5529ece..0a2ada7345 100644
--- a/libpurple/protocols/jabber/pep.h
+++ b/libpurple/protocols/jabber/pep.h
@@ -39,7 +39,7 @@ void jabber_pep_init_actions(GList **m);
* @parameter js The JabberStream this item was received on
* @parameter items The &lt;items/>-tag with the &lt;item/>-children
*/
-typedef void (JabberPEPHandler)(JabberStream *js, const char *from, xmlnode *items);
+typedef void (JabberPEPHandler)(JabberStream *js, const char *from, PurpleXmlNode *items);
/*
* Registers a callback for PEP events. Also automatically announces this receiving capability via disco#info.
@@ -86,6 +86,6 @@ void jabber_pep_delete_node(JabberStream *js, const gchar *node);
* @parameter js The JabberStream associated with the connection this event should be published
* @parameter publish The publish node. This could be for example &lt;publish node='http://jabber.org/protocol/tune'/> with an &lt;item/> as subnode
*/
-void jabber_pep_publish(JabberStream *js, xmlnode *publish);
+void jabber_pep_publish(JabberStream *js, PurpleXmlNode *publish);
#endif /* PURPLE_JABBER_PEP_H_ */
diff --git a/libpurple/protocols/jabber/ping.c b/libpurple/protocols/jabber/ping.c
index e271a39f15..8f1251cf45 100644
--- a/libpurple/protocols/jabber/ping.c
+++ b/libpurple/protocols/jabber/ping.c
@@ -32,7 +32,7 @@
static void jabber_keepalive_pong_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
if (js->keepalive_timeout != 0) {
purple_timeout_remove(js->keepalive_timeout);
@@ -42,14 +42,14 @@ static void jabber_keepalive_pong_cb(JabberStream *js, const char *from,
void
jabber_ping_parse(JabberStream *js, const char *from,
- JabberIqType type, const char *id, xmlnode *ping)
+ JabberIqType type, const char *id, PurpleXmlNode *ping)
{
if (type == JABBER_IQ_GET) {
JabberIq *iq = jabber_iq_new(js, JABBER_IQ_RESULT);
if (from)
- xmlnode_set_attrib(iq->node, "to", from);
- xmlnode_set_attrib(iq->node, "id", id);
+ purple_xmlnode_set_attrib(iq->node, "to", from);
+ purple_xmlnode_set_attrib(iq->node, "id", id);
jabber_iq_send(iq);
} else if (type == JABBER_IQ_SET) {
@@ -59,7 +59,7 @@ jabber_ping_parse(JabberStream *js, const char *from,
static void jabber_ping_result_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
if (type == JABBER_IQ_RESULT)
purple_debug_info("jabber", "PONG!\n");
@@ -70,11 +70,11 @@ static void jabber_ping_result_cb(JabberStream *js, const char *from,
void jabber_keepalive_ping(JabberStream *js)
{
JabberIq *iq;
- xmlnode *ping;
+ PurpleXmlNode *ping;
iq = jabber_iq_new(js, JABBER_IQ_GET);
- ping = xmlnode_new_child(iq->node, "ping");
- xmlnode_set_namespace(ping, NS_PING);
+ ping = purple_xmlnode_new_child(iq->node, "ping");
+ purple_xmlnode_set_namespace(ping, NS_PING);
jabber_iq_set_callback(iq, jabber_keepalive_pong_cb, NULL);
jabber_iq_send(iq);
@@ -83,14 +83,14 @@ void jabber_keepalive_ping(JabberStream *js)
gboolean jabber_ping_jid(JabberStream *js, const char *jid)
{
JabberIq *iq;
- xmlnode *ping;
+ PurpleXmlNode *ping;
iq = jabber_iq_new(js, JABBER_IQ_GET);
if (jid)
- xmlnode_set_attrib(iq->node, "to", jid);
+ purple_xmlnode_set_attrib(iq->node, "to", jid);
- ping = xmlnode_new_child(iq->node, "ping");
- xmlnode_set_namespace(ping, NS_PING);
+ ping = purple_xmlnode_new_child(iq->node, "ping");
+ purple_xmlnode_set_namespace(ping, NS_PING);
jabber_iq_set_callback(iq, jabber_ping_result_cb, NULL);
jabber_iq_send(iq);
diff --git a/libpurple/protocols/jabber/ping.h b/libpurple/protocols/jabber/ping.h
index 5420ea531c..a7175cf632 100644
--- a/libpurple/protocols/jabber/ping.h
+++ b/libpurple/protocols/jabber/ping.h
@@ -29,7 +29,7 @@
#include "xmlnode.h"
void jabber_ping_parse(JabberStream *js, const char *from,
- JabberIqType, const char *id, xmlnode *child);
+ JabberIqType, const char *id, PurpleXmlNode *child);
gboolean jabber_ping_jid(JabberStream *js, const char *jid);
void jabber_keepalive_ping(JabberStream *js);
diff --git a/libpurple/protocols/jabber/presence.c b/libpurple/protocols/jabber/presence.c
index 7e0a11b93b..13944023a0 100644
--- a/libpurple/protocols/jabber/presence.c
+++ b/libpurple/protocols/jabber/presence.c
@@ -63,7 +63,7 @@ static const struct {
static JabberPresenceType
str_to_presence_type(const char *type)
{
- int i;
+ gsize i;
if (type == NULL)
return JABBER_PRESENCE_AVAILABLE;
@@ -80,7 +80,7 @@ static void chats_send_presence_foreach(gpointer key, gpointer val,
gpointer user_data)
{
JabberChat *chat = val;
- xmlnode *presence = user_data;
+ PurpleXmlNode *presence = user_data;
char *chat_full_jid;
if(!chat->conv || chat->left)
@@ -89,7 +89,7 @@ static void chats_send_presence_foreach(gpointer key, gpointer val,
chat_full_jid = g_strdup_printf("%s@%s/%s", chat->room, chat->server,
chat->handle);
- xmlnode_set_attrib(presence, "to", chat_full_jid);
+ purple_xmlnode_set_attrib(presence, "to", chat_full_jid);
jabber_send(chat->js, presence);
g_free(chat_full_jid);
}
@@ -130,7 +130,7 @@ void jabber_presence_fake_to_self(JabberStream *js, PurpleStatus *status)
* While we need to track the status of this resource, the core
* only cares if we're on our own buddy list.
*/
- if (purple_find_buddy(account, username)) {
+ if (purple_blist_find_buddy(account, username)) {
jbr = jabber_buddy_find_resource(jb, NULL);
if (jbr) {
purple_prpl_got_user_status(account, username,
@@ -166,7 +166,7 @@ void jabber_set_status(PurpleAccount *account, PurpleStatus *status)
js = purple_connection_get_protocol_data(gc);
/* it's a mood update */
- if (purple_status_type_get_primitive(purple_status_get_type(status)) == PURPLE_STATUS_MOOD) {
+ if (purple_status_type_get_primitive(purple_status_get_status_type(status)) == PURPLE_STATUS_MOOD) {
const char *mood =
purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
const char *mood_text =
@@ -181,7 +181,7 @@ void jabber_set_status(PurpleAccount *account, PurpleStatus *status)
void jabber_presence_send(JabberStream *js, gboolean force)
{
PurpleAccount *account;
- xmlnode *presence, *x, *photo;
+ PurpleXmlNode *presence, *x, *photo;
char *stripped = NULL;
JabberBuddyState state;
int priority;
@@ -224,8 +224,8 @@ void jabber_presence_send(JabberStream *js, gboolean force)
presence = jabber_presence_create_js(js, state, stripped, priority);
/* Per XEP-0153 4.1, we must always send the <x> */
- x = xmlnode_new_child(presence, "x");
- xmlnode_set_namespace(x, "vcard-temp:x:update");
+ x = purple_xmlnode_new_child(presence, "x");
+ purple_xmlnode_set_namespace(x, "vcard-temp:x:update");
/*
* FIXME: Per XEP-0153 4.3.2 bullet 2, we must not publish our
* image hash if another resource has logged in and updated the
@@ -233,15 +233,15 @@ void jabber_presence_send(JabberStream *js, gboolean force)
*/
if (js->vcard_fetched) {
/* Always publish a <photo>; it's empty if we have no image. */
- photo = xmlnode_new_child(x, "photo");
+ photo = purple_xmlnode_new_child(x, "photo");
if (js->avatar_hash)
- xmlnode_insert_data(photo, js->avatar_hash, -1);
+ purple_xmlnode_insert_data(photo, js->avatar_hash, -1);
}
jabber_send(js, presence);
g_hash_table_foreach(js->chats, chats_send_presence_foreach, presence);
- xmlnode_free(presence);
+ purple_xmlnode_free(presence);
/* update old values */
@@ -299,64 +299,59 @@ void jabber_presence_send(JabberStream *js, gboolean force)
jabber_presence_fake_to_self(js, status);
}
-xmlnode *jabber_presence_create(JabberBuddyState state, const char *msg, int priority)
+PurpleXmlNode *jabber_presence_create_js(JabberStream *js, JabberBuddyState state, const char *msg, int priority)
{
- return jabber_presence_create_js(NULL, state, msg, priority);
-}
-
-xmlnode *jabber_presence_create_js(JabberStream *js, JabberBuddyState state, const char *msg, int priority)
-{
- xmlnode *show, *status, *presence, *pri, *c;
+ PurpleXmlNode *show, *status, *presence, *pri, *c;
const char *show_string = NULL;
#ifdef USE_VV
gboolean audio_enabled, video_enabled;
#endif
- presence = xmlnode_new("presence");
+ presence = purple_xmlnode_new("presence");
if(state == JABBER_BUDDY_STATE_UNAVAILABLE)
- xmlnode_set_attrib(presence, "type", "unavailable");
+ purple_xmlnode_set_attrib(presence, "type", "unavailable");
else if(state != JABBER_BUDDY_STATE_ONLINE &&
state != JABBER_BUDDY_STATE_UNKNOWN &&
state != JABBER_BUDDY_STATE_ERROR)
show_string = jabber_buddy_state_get_show(state);
if(show_string) {
- show = xmlnode_new_child(presence, "show");
- xmlnode_insert_data(show, show_string, -1);
+ show = purple_xmlnode_new_child(presence, "show");
+ purple_xmlnode_insert_data(show, show_string, -1);
}
if(msg) {
- status = xmlnode_new_child(presence, "status");
- xmlnode_insert_data(status, msg, -1);
+ status = purple_xmlnode_new_child(presence, "status");
+ purple_xmlnode_insert_data(status, msg, -1);
}
if(priority) {
char *pstr = g_strdup_printf("%d", priority);
- pri = xmlnode_new_child(presence, "priority");
- xmlnode_insert_data(pri, pstr, -1);
+ pri = purple_xmlnode_new_child(presence, "priority");
+ purple_xmlnode_insert_data(pri, pstr, -1);
g_free(pstr);
}
/* if we are idle and not offline, include idle */
if (js->idle && state != JABBER_BUDDY_STATE_UNAVAILABLE) {
- xmlnode *query = xmlnode_new_child(presence, "query");
+ PurpleXmlNode *query = purple_xmlnode_new_child(presence, "query");
gchar seconds[10];
g_snprintf(seconds, 10, "%d", (int) (time(NULL) - js->idle));
- xmlnode_set_namespace(query, NS_LAST_ACTIVITY);
- xmlnode_set_attrib(query, "seconds", seconds);
+ purple_xmlnode_set_namespace(query, NS_LAST_ACTIVITY);
+ purple_xmlnode_set_attrib(query, "seconds", seconds);
}
/* JEP-0115 */
/* calculate hash */
jabber_caps_calculate_own_hash(js);
/* create xml */
- c = xmlnode_new_child(presence, "c");
- xmlnode_set_namespace(c, "http://jabber.org/protocol/caps");
- xmlnode_set_attrib(c, "node", CAPS0115_NODE);
- xmlnode_set_attrib(c, "hash", "sha-1");
- xmlnode_set_attrib(c, "ver", jabber_caps_get_own_hash(js));
+ c = purple_xmlnode_new_child(presence, "c");
+ purple_xmlnode_set_namespace(c, "http://jabber.org/protocol/caps");
+ purple_xmlnode_set_attrib(c, "node", CAPS0115_NODE);
+ purple_xmlnode_set_attrib(c, "hash", "sha-1");
+ purple_xmlnode_set_attrib(c, "ver", jabber_caps_get_own_hash(js));
#ifdef USE_VV
/*
@@ -373,11 +368,11 @@ xmlnode *jabber_presence_create_js(JabberStream *js, JabberBuddyState state, con
video_enabled = jabber_video_enabled(js, NULL /* unused */);
if (audio_enabled && video_enabled)
- xmlnode_set_attrib(c, "ext", "voice-v1 camera-v1 video-v1");
+ purple_xmlnode_set_attrib(c, "ext", "voice-v1 camera-v1 video-v1");
else if (audio_enabled)
- xmlnode_set_attrib(c, "ext", "voice-v1");
+ purple_xmlnode_set_attrib(c, "ext", "voice-v1");
else if (video_enabled)
- xmlnode_set_attrib(c, "ext", "camera-v1 video-v1");
+ purple_xmlnode_set_attrib(c, "ext", "camera-v1 video-v1");
#endif
return presence;
@@ -389,22 +384,28 @@ struct _jabber_add_permit {
char *who;
};
-static void authorize_add_cb(gpointer data)
+static void authorize_add_cb(const char *message, gpointer data)
{
struct _jabber_add_permit *jap = data;
- if(PURPLE_CONNECTION_IS_VALID(jap->gc))
- jabber_presence_subscription_set(jap->gc->proto_data,
- jap->who, "subscribed");
+
+ PURPLE_ASSERT_CONNECTION_IS_VALID(jap->gc);
+
+ jabber_presence_subscription_set(purple_connection_get_protocol_data(jap->gc),
+ jap->who, "subscribed");
+
g_free(jap->who);
g_free(jap);
}
-static void deny_add_cb(gpointer data)
+static void deny_add_cb(const char *message, gpointer data)
{
struct _jabber_add_permit *jap = data;
- if(PURPLE_CONNECTION_IS_VALID(jap->gc))
- jabber_presence_subscription_set(jap->gc->proto_data,
- jap->who, "unsubscribed");
+
+ PURPLE_ASSERT_CONNECTION_IS_VALID(jap->gc);
+
+ jabber_presence_subscription_set(purple_connection_get_protocol_data(jap->gc),
+ jap->who, "unsubscribed");
+
g_free(jap->who);
g_free(jap);
}
@@ -412,10 +413,10 @@ static void deny_add_cb(gpointer data)
static void
jabber_vcard_parse_avatar(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer blah)
+ PurpleXmlNode *packet, gpointer blah)
{
JabberBuddy *jb = NULL;
- xmlnode *vcard, *photo, *binval, *fn, *nick;
+ PurpleXmlNode *vcard, *photo, *binval, *fn, *nick;
char *text;
if(!from)
@@ -425,16 +426,16 @@ jabber_vcard_parse_avatar(JabberStream *js, const char *from,
js->pending_avatar_requests = g_slist_remove(js->pending_avatar_requests, jb);
- if((vcard = xmlnode_get_child(packet, "vCard")) ||
- (vcard = xmlnode_get_child_with_namespace(packet, "query", "vcard-temp"))) {
+ if((vcard = purple_xmlnode_get_child(packet, "vCard")) ||
+ (vcard = purple_xmlnode_get_child_with_namespace(packet, "query", "vcard-temp"))) {
/* The logic here regarding the nickname and full name is copied from
* buddy.c:jabber_vcard_parse. */
gchar *nickname = NULL;
- if ((fn = xmlnode_get_child(vcard, "FN")))
- nickname = xmlnode_get_data(fn);
+ if ((fn = purple_xmlnode_get_child(vcard, "FN")))
+ nickname = purple_xmlnode_get_data(fn);
- if ((nick = xmlnode_get_child(vcard, "NICKNAME"))) {
- char *tmp = xmlnode_get_data(nick);
+ if ((nick = purple_xmlnode_get_child(vcard, "NICKNAME"))) {
+ char *tmp = purple_xmlnode_get_data(nick);
char *bare_jid = jabber_get_bare_jid(from);
if (tmp && strstr(bare_jid, tmp) == NULL) {
g_free(nickname);
@@ -446,17 +447,17 @@ jabber_vcard_parse_avatar(JabberStream *js, const char *from,
}
if (nickname) {
- serv_got_alias(js->gc, from, nickname);
+ purple_serv_got_alias(js->gc, from, nickname);
g_free(nickname);
}
- if ((photo = xmlnode_get_child(vcard, "PHOTO"))) {
+ if ((photo = purple_xmlnode_get_child(vcard, "PHOTO"))) {
guchar *data = NULL;
gchar *hash = NULL;
gsize size = 0;
- if ((binval = xmlnode_get_child(photo, "BINVAL")) &&
- (text = xmlnode_get_data(binval))) {
+ if ((binval = purple_xmlnode_get_child(photo, "BINVAL")) &&
+ (text = purple_xmlnode_get_data(binval))) {
data = purple_base64_decode(text, &size);
g_free(text);
@@ -464,7 +465,7 @@ jabber_vcard_parse_avatar(JabberStream *js, const char *from,
hash = jabber_calculate_data_hash(data, size, "sha1");
}
- purple_buddy_icons_set_for_user(js->gc->account, from, data, size, hash);
+ purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, data, size, hash);
g_free(hash);
}
@@ -515,9 +516,9 @@ jabber_presence_set_capabilities(JabberCapsClientInfo *info, GList *exts,
if (!jbr->commands_fetched && jabber_resource_has_capability(jbr, "http://jabber.org/protocol/commands")) {
JabberIq *iq = jabber_iq_new_query(userdata->js, JABBER_IQ_GET, NS_DISCO_ITEMS);
- xmlnode *query = xmlnode_get_child_with_namespace(iq->node, "query", NS_DISCO_ITEMS);
- xmlnode_set_attrib(iq->node, "to", userdata->from);
- xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/commands");
+ PurpleXmlNode *query = purple_xmlnode_get_child_with_namespace(iq->node, "query", NS_DISCO_ITEMS);
+ purple_xmlnode_set_attrib(iq->node, "to", userdata->from);
+ purple_xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/commands");
jabber_iq_set_callback(iq, jabber_adhoc_disco_result_cb, NULL);
jabber_iq_send(iq);
@@ -542,10 +543,10 @@ out:
}
static gboolean
-handle_presence_chat(JabberStream *js, JabberPresence *presence, xmlnode *packet)
+handle_presence_chat(JabberStream *js, JabberPresence *presence, PurpleXmlNode *packet)
{
static int i = 1;
- PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_NONE;
+ PurpleChatUserFlags flags = PURPLE_CHAT_USER_NONE;
JabberChat *chat = presence->chat;
if (presence->state == JABBER_BUDDY_STATE_ERROR) {
@@ -557,9 +558,10 @@ handle_presence_chat(JabberStream *js, JabberPresence *presence, xmlnode *packet
} else {
title = g_strdup_printf(_("Error in chat %s"), presence->from);
if (g_hash_table_size(chat->members) == 0)
- serv_got_chat_left(js->gc, chat->id);
+ purple_serv_got_chat_left(js->gc, chat->id);
}
- purple_notify_error(js->gc, title, title, msg);
+ purple_notify_error(js->gc, title, title, msg,
+ purple_request_cpar_from_connection(js->gc));
g_free(title);
g_free(msg);
@@ -585,9 +587,9 @@ handle_presence_chat(JabberStream *js, JabberPresence *presence, xmlnode *packet
return FALSE;
if (presence->chat_info.item) {
- jid = xmlnode_get_attrib(presence->chat_info.item, "jid");
- affiliation = xmlnode_get_attrib(presence->chat_info.item, "affiliation");
- role = xmlnode_get_attrib(presence->chat_info.item, "role");
+ jid = purple_xmlnode_get_attrib(presence->chat_info.item, "jid");
+ affiliation = purple_xmlnode_get_attrib(presence->chat_info.item, "affiliation");
+ role = purple_xmlnode_get_attrib(presence->chat_info.item, "role");
}
if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(110)) ||
@@ -605,7 +607,7 @@ handle_presence_chat(JabberStream *js, JabberPresence *presence, xmlnode *packet
" you like to configure it, or"
" accept the default settings?"),
/* Default Action */ 1,
- purple_connection_get_account(js->gc), NULL, chat->conv,
+ purple_request_cpar_from_conversation(PURPLE_CONVERSATION(chat->conv)),
chat, 2,
_("_Configure Room"), G_CALLBACK(jabber_chat_request_room_configure),
_("_Accept Defaults"), G_CALLBACK(jabber_chat_create_instant_room));
@@ -618,19 +620,19 @@ handle_presence_chat(JabberStream *js, JabberPresence *presence, xmlnode *packet
}
if (purple_strequal(affiliation, "owner"))
- flags |= PURPLE_CBFLAGS_FOUNDER;
+ flags |= PURPLE_CHAT_USER_FOUNDER;
if (role) {
if (g_str_equal(role, "moderator"))
- flags |= PURPLE_CBFLAGS_OP;
+ flags |= PURPLE_CHAT_USER_OP;
else if (g_str_equal(role, "participant"))
- flags |= PURPLE_CBFLAGS_VOICE;
+ flags |= PURPLE_CHAT_USER_VOICE;
}
if(!chat->conv) {
char *room_jid = g_strdup_printf("%s@%s", presence->jid_from->node, presence->jid_from->domain);
chat->id = i++;
- chat->conv = serv_got_joined_chat(js->gc, chat->id, room_jid);
- purple_conv_chat_set_nick(PURPLE_CONV_CHAT(chat->conv), chat->handle);
+ chat->conv = purple_serv_got_joined_chat(js->gc, chat->id, room_jid);
+ purple_chat_conversation_set_nick(chat->conv, chat->handle);
jabber_chat_disco_traffic(chat);
g_free(room_jid);
@@ -642,10 +644,10 @@ handle_presence_chat(JabberStream *js, JabberPresence *presence, xmlnode *packet
jabber_chat_track_handle(chat, presence->jid_from->resource, jid, affiliation, role);
if(!jabber_chat_find_buddy(chat->conv, presence->jid_from->resource))
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(chat->conv), presence->jid_from->resource,
+ purple_chat_conversation_add_user(chat->conv, presence->jid_from->resource,
jid, flags, chat->joined > 0 && ((!presence->delayed) || (presence->sent > chat->joined)));
else
- purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(chat->conv), presence->jid_from->resource,
+ purple_chat_user_set_flags(purple_chat_conversation_find_user(chat->conv, presence->jid_from->resource),
flags);
if (is_our_resource && chat->joined == 0)
@@ -674,7 +676,7 @@ handle_presence_chat(JabberStream *js, JabberPresence *presence, xmlnode *packet
jabber_buddy_remove_resource(presence->jb, presence->jid_from->resource);
if (presence->chat_info.item)
- jid = xmlnode_get_attrib(presence->chat_info.item, "jid");
+ jid = purple_xmlnode_get_attrib(presence->chat_info.item, "jid");
if (chat->muc) {
if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(110))) {
@@ -690,7 +692,7 @@ handle_presence_chat(JabberStream *js, JabberPresence *presence, xmlnode *packet
if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(303))) {
const char *nick = NULL;
if (presence->chat_info.item)
- nick = xmlnode_get_attrib(presence->chat_info.item, "nick");
+ nick = purple_xmlnode_get_attrib(presence->chat_info.item, "nick");
/* nick change */
if (!nick) {
@@ -705,7 +707,7 @@ handle_presence_chat(JabberStream *js, JabberPresence *presence, xmlnode *packet
chat->handle = g_strdup(nick);
}
- purple_conv_chat_rename_user(PURPLE_CONV_CHAT(chat->conv),
+ purple_chat_conversation_rename_user(chat->conv,
presence->jid_from->resource,
nick);
jabber_chat_remove_handle(chat,
@@ -722,14 +724,14 @@ handle_presence_chat(JabberStream *js, JabberPresence *presence, xmlnode *packet
kick = TRUE;
if (presence->chat_info.item) {
- xmlnode *node;
+ PurpleXmlNode *node;
- node = xmlnode_get_child(presence->chat_info.item, "actor");
+ node = purple_xmlnode_get_child(presence->chat_info.item, "actor");
if (node)
- actor = xmlnode_get_attrib(node, "jid");
- node = xmlnode_get_child(presence->chat_info.item, "reason");
+ actor = purple_xmlnode_get_attrib(node, "jid");
+ node = purple_xmlnode_get_child(presence->chat_info.item, "reason");
if (node)
- reason = xmlnode_get_data(node);
+ reason = purple_xmlnode_get_data(node);
}
if (reason == NULL)
@@ -787,14 +789,17 @@ handle_presence_chat(JabberStream *js, JabberPresence *presence, xmlnode *packet
if(!nick_change) {
if (is_our_resource) {
- if (kick)
- purple_conv_chat_write(PURPLE_CONV_CHAT(chat->conv), presence->jid_from->resource,
- presence->status, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ if (kick) {
+ gchar *msg = g_strdup_printf("%s: %s",
+ presence->jid_from->resource,
+ presence->status);
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(chat->conv), msg, 0);
+ }
- serv_got_chat_left(js->gc, chat->id);
+ purple_serv_got_chat_left(js->gc, chat->id);
jabber_chat_destroy(chat);
} else {
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(chat->conv), presence->jid_from->resource,
+ purple_chat_conversation_remove_user(chat->conv, presence->jid_from->resource,
presence->status);
jabber_chat_remove_handle(chat, presence->jid_from->resource);
}
@@ -811,24 +816,23 @@ handle_presence_contact(JabberStream *js, JabberPresence *presence)
PurpleAccount *account;
PurpleBuddy *b;
char *buddy_name;
- PurpleConversation *conv;
+ PurpleIMConversation *im;
buddy_name = jabber_id_get_bare_jid(presence->jid_from);
account = purple_connection_get_account(js->gc);
- b = purple_find_buddy(account, buddy_name);
+ b = purple_blist_find_buddy(account, buddy_name);
/*
* Unbind/unlock from sending messages to a specific resource on
* presence changes. This is locked to a specific resource when
* receiving a message (in message.c).
*/
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
- buddy_name, account);
- if (conv) {
+ im = purple_conversations_find_im_with_account(buddy_name, account);
+ if (im) {
purple_debug_info("jabber", "Changed conversation binding from %s to %s\n",
- purple_conversation_get_name(conv), buddy_name);
- purple_conversation_set_name(conv, buddy_name);
+ purple_conversation_get_name(PURPLE_CONVERSATION(im)), buddy_name);
+ purple_conversation_set_name(PURPLE_CONVERSATION(im), buddy_name);
}
if (b == NULL) {
@@ -854,15 +858,15 @@ handle_presence_contact(JabberStream *js, JabberPresence *presence)
* but i'm too tired to write that right now */
if(!g_slist_find(js->pending_avatar_requests, presence->jb)) {
JabberIq *iq;
- xmlnode *vcard;
+ PurpleXmlNode *vcard;
js->pending_avatar_requests =
g_slist_prepend(js->pending_avatar_requests, presence->jb);
iq = jabber_iq_new(js, JABBER_IQ_GET);
- xmlnode_set_attrib(iq->node, "to", buddy_name);
- vcard = xmlnode_new_child(iq->node, "vCard");
- xmlnode_set_namespace(vcard, "vcard-temp");
+ purple_xmlnode_set_attrib(iq->node, "to", buddy_name);
+ vcard = purple_xmlnode_new_child(iq->node, "vCard");
+ purple_xmlnode_set_namespace(vcard, "vcard-temp");
jabber_iq_set_callback(iq, jabber_vcard_parse_avatar, NULL);
jabber_iq_send(iq);
@@ -892,7 +896,7 @@ handle_presence_contact(JabberStream *js, JabberPresence *presence)
purple_prpl_got_user_idle(account, buddy_name,
jbr->idle, jbr->idle);
if (presence->nickname)
- serv_got_alias(js->gc, buddy_name, presence->nickname);
+ purple_serv_got_alias(js->gc, buddy_name, presence->nickname);
} else {
purple_prpl_got_user_status(account, buddy_name,
jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_UNAVAILABLE),
@@ -904,22 +908,22 @@ handle_presence_contact(JabberStream *js, JabberPresence *presence)
return TRUE;
}
-void jabber_presence_parse(JabberStream *js, xmlnode *packet)
+void jabber_presence_parse(JabberStream *js, PurpleXmlNode *packet)
{
const char *type;
JabberBuddyResource *jbr = NULL;
gboolean signal_return, ret;
JabberPresence presence;
- xmlnode *child;
+ PurpleXmlNode *child;
memset(&presence, 0, sizeof(presence));
/* defaults */
presence.state = JABBER_BUDDY_STATE_UNKNOWN;
presence.sent = time(NULL);
/* interesting values */
- presence.from = xmlnode_get_attrib(packet, "from");
- presence.to = xmlnode_get_attrib(packet, "to");
- type = xmlnode_get_attrib(packet, "type");
+ presence.from = purple_xmlnode_get_attrib(packet, "from");
+ presence.to = purple_xmlnode_get_attrib(packet, "to");
+ type = purple_xmlnode_get_attrib(packet, "type");
presence.type = str_to_presence_type(type);
presence.jb = jabber_buddy_find(js, presence.from, TRUE);
@@ -960,13 +964,13 @@ void jabber_presence_parse(JabberStream *js, xmlnode *packet)
gboolean onlist = FALSE;
PurpleAccount *account;
PurpleBuddy *buddy;
- xmlnode *nick;
+ PurpleXmlNode *nick;
account = purple_connection_get_account(js->gc);
- buddy = purple_find_buddy(account, presence.from);
- nick = xmlnode_get_child_with_namespace(packet, "nick", "http://jabber.org/protocol/nick");
+ buddy = purple_blist_find_buddy(account, presence.from);
+ nick = purple_xmlnode_get_child_with_namespace(packet, "nick", "http://jabber.org/protocol/nick");
if (nick)
- presence.nickname = xmlnode_get_data(nick);
+ presence.nickname = purple_xmlnode_get_data(nick);
if (buddy) {
if ((presence.jb->subscription & (JABBER_SUB_TO | JABBER_SUB_PENDING)))
@@ -1011,10 +1015,10 @@ void jabber_presence_parse(JabberStream *js, xmlnode *packet)
const char *xmlns;
char *key;
JabberPresenceHandler *pih;
- if (child->type != XMLNODE_TYPE_TAG)
+ if (child->type != PURPLE_XMLNODE_TYPE_TAG)
continue;
- xmlns = xmlnode_get_namespace(child);
+ xmlns = purple_xmlnode_get_namespace(child);
key = g_strdup_printf("%s %s", child->name, xmlns ? xmlns : "");
pih = g_hash_table_lookup(presence_handlers, key);
g_free(key);
@@ -1022,7 +1026,7 @@ void jabber_presence_parse(JabberStream *js, xmlnode *packet)
pih(js, &presence, child);
}
- if (presence.delayed && presence.idle) {
+ if (presence.delayed && presence.idle && presence.adjust_idle_for_delay) {
/* Delayed and idle, so update idle time */
presence.idle = presence.idle + (time(NULL) - presence.sent);
}
@@ -1038,10 +1042,10 @@ void jabber_presence_parse(JabberStream *js, xmlnode *packet)
if (presence.caps && presence.type == JABBER_PRESENCE_AVAILABLE) {
/* handle Entity Capabilities (XEP-0115) */
- const char *node = xmlnode_get_attrib(presence.caps, "node");
- const char *ver = xmlnode_get_attrib(presence.caps, "ver");
- const char *hash = xmlnode_get_attrib(presence.caps, "hash");
- const char *ext = xmlnode_get_attrib(presence.caps, "ext");
+ const char *node = purple_xmlnode_get_attrib(presence.caps, "node");
+ const char *ver = purple_xmlnode_get_attrib(presence.caps, "ver");
+ const char *hash = purple_xmlnode_get_attrib(presence.caps, "hash");
+ const char *ext = purple_xmlnode_get_attrib(presence.caps, "ext");
/* v1.3 uses: node, ver, and optionally ext.
* v1.5 uses: node, ver, and hash. */
@@ -1083,13 +1087,13 @@ out:
void jabber_presence_subscription_set(JabberStream *js, const char *who, const char *type)
{
- xmlnode *presence = xmlnode_new("presence");
+ PurpleXmlNode *presence = purple_xmlnode_new("presence");
- xmlnode_set_attrib(presence, "to", who);
- xmlnode_set_attrib(presence, "type", type);
+ purple_xmlnode_set_attrib(presence, "to", who);
+ purple_xmlnode_set_attrib(presence, "type", type);
jabber_send(js, presence);
- xmlnode_free(presence);
+ purple_xmlnode_free(presence);
}
void purple_status_to_jabber(const PurpleStatus *status, JabberBuddyState *state, char **msg, int *priority)
@@ -1124,9 +1128,9 @@ void purple_status_to_jabber(const PurpleStatus *status, JabberBuddyState *state
/* Incoming presence handlers */
static void
-parse_priority(JabberStream *js, JabberPresence *presence, xmlnode *priority)
+parse_priority(JabberStream *js, JabberPresence *presence, PurpleXmlNode *priority)
{
- char *p = xmlnode_get_data(priority);
+ char *p = purple_xmlnode_get_data(priority);
if (presence->priority != 0)
purple_debug_warning("jabber", "presence stanza received with multiple "
@@ -1140,7 +1144,7 @@ parse_priority(JabberStream *js, JabberPresence *presence, xmlnode *priority)
}
static void
-parse_show(JabberStream *js, JabberPresence *presence, xmlnode *show)
+parse_show(JabberStream *js, JabberPresence *presence, PurpleXmlNode *show)
{
char *cdata;
@@ -1150,7 +1154,7 @@ parse_show(JabberStream *js, JabberPresence *presence, xmlnode *show)
return;
}
- cdata = xmlnode_get_data(show);
+ cdata = purple_xmlnode_get_data(show);
if (cdata) {
presence->state = jabber_buddy_show_get_state(cdata);
g_free(cdata);
@@ -1160,28 +1164,50 @@ parse_show(JabberStream *js, JabberPresence *presence, xmlnode *show)
}
static void
-parse_status(JabberStream *js, JabberPresence *presence, xmlnode *status)
+parse_status(JabberStream *js, JabberPresence *presence, PurpleXmlNode *status)
{
/* TODO: Check/track language attribute? */
g_free(presence->status);
- presence->status = xmlnode_get_data(status);
+ presence->status = purple_xmlnode_get_data(status);
}
static void
-parse_delay(JabberStream *js, JabberPresence *presence, xmlnode *delay)
+parse_delay(JabberStream *js, JabberPresence *presence, PurpleXmlNode *delay)
{
- const char *stamp = xmlnode_get_attrib(delay, "stamp");
+ const char *stamp = purple_xmlnode_get_attrib(delay, "stamp");
presence->delayed = TRUE;
presence->sent = purple_str_to_time(stamp, TRUE, NULL, NULL, NULL);
}
static void
-parse_idle(JabberStream *js, JabberPresence *presence, xmlnode *query)
+parse_apple_idle(JabberStream *js, JabberPresence *presence, PurpleXmlNode *x)
+{
+ PurpleXmlNode *since = purple_xmlnode_get_child(x, "idle-since");
+ if (since) {
+ char *stamp = purple_xmlnode_get_data_unescaped(since);
+ if (stamp) {
+ time_t tstamp = purple_str_to_time(stamp, TRUE, NULL, NULL, NULL);
+ if (tstamp != 0) {
+ presence->idle = time(NULL) - tstamp;
+ presence->adjust_idle_for_delay = FALSE;
+ if(presence->idle < 0) {
+ purple_debug_warning("jabber", "Received bogus idle timestamp %s\n", stamp);
+ presence->idle = 0;
+ }
+ }
+ }
+ g_free(stamp);
+ }
+}
+
+static void
+parse_idle(JabberStream *js, JabberPresence *presence, PurpleXmlNode *query)
{
- const gchar *seconds = xmlnode_get_attrib(query, "seconds");
+ const gchar *seconds = purple_xmlnode_get_attrib(query, "seconds");
if (seconds) {
presence->idle = atoi(seconds);
+ presence->adjust_idle_for_delay = TRUE;
if (presence->idle < 0) {
purple_debug_warning("jabber", "Received bogus idle time %s\n", seconds);
presence->idle = 0;
@@ -1190,7 +1216,7 @@ parse_idle(JabberStream *js, JabberPresence *presence, xmlnode *query)
}
static void
-parse_caps(JabberStream *js, JabberPresence *presence, xmlnode *c)
+parse_caps(JabberStream *js, JabberPresence *presence, PurpleXmlNode *c)
{
/* TODO: Move the rest of the caps handling in here, after changing the
* the "do we have details about this (node, ver) and exts" to not
@@ -1200,19 +1226,19 @@ parse_caps(JabberStream *js, JabberPresence *presence, xmlnode *c)
}
static void
-parse_nickname(JabberStream *js, JabberPresence *presence, xmlnode *nick)
+parse_nickname(JabberStream *js, JabberPresence *presence, PurpleXmlNode *nick)
{
g_free(presence->nickname);
- presence->nickname = xmlnode_get_data(nick);
+ presence->nickname = purple_xmlnode_get_data(nick);
}
static void
-parse_vcard_avatar(JabberStream *js, JabberPresence *presence, xmlnode *x)
+parse_vcard_avatar(JabberStream *js, JabberPresence *presence, PurpleXmlNode *x)
{
- xmlnode *photo = xmlnode_get_child(x, "photo");
+ PurpleXmlNode *photo = purple_xmlnode_get_child(x, "photo");
if (photo) {
- char *hash_tmp = xmlnode_get_data(photo);
+ char *hash_tmp = purple_xmlnode_get_data(photo);
g_free(presence->vcard_avatar_hash);
presence->vcard_avatar_hash =
hash_tmp ? hash_tmp : g_strdup("");
@@ -1220,9 +1246,9 @@ parse_vcard_avatar(JabberStream *js, JabberPresence *presence, xmlnode *x)
}
static void
-parse_muc_user(JabberStream *js, JabberPresence *presence, xmlnode *x)
+parse_muc_user(JabberStream *js, JabberPresence *presence, PurpleXmlNode *x)
{
- xmlnode *status;
+ PurpleXmlNode *status;
if (presence->chat == NULL) {
purple_debug_warning("jabber", "Ignoring MUC gloop on non-MUC presence\n");
@@ -1232,9 +1258,9 @@ parse_muc_user(JabberStream *js, JabberPresence *presence, xmlnode *x)
if (presence->chat->conv == NULL)
presence->chat->muc = TRUE;
- for (status = xmlnode_get_child(x, "status"); status;
- status = xmlnode_get_next_twin(status)) {
- const char *code = xmlnode_get_attrib(status, "code");
+ for (status = purple_xmlnode_get_child(x, "status"); status;
+ status = purple_xmlnode_get_next_twin(status)) {
+ const char *code = purple_xmlnode_get_attrib(status, "code");
int val;
if (!code)
continue;
@@ -1249,7 +1275,7 @@ parse_muc_user(JabberStream *js, JabberPresence *presence, xmlnode *x)
presence->chat_info.codes = g_slist_prepend(presence->chat_info.codes, GINT_TO_POINTER(val));
}
- presence->chat_info.item = xmlnode_get_child(x, "item");
+ presence->chat_info.item = purple_xmlnode_get_child(x, "item");
}
void jabber_presence_register_handler(const char *node, const char *xmlns,
@@ -1281,6 +1307,9 @@ void jabber_presence_init(void)
jabber_presence_register_handler("x", NS_DELAYED_DELIVERY_LEGACY, parse_delay);
jabber_presence_register_handler("x", "http://jabber.org/protocol/muc#user", parse_muc_user);
jabber_presence_register_handler("x", "vcard-temp:x:update", parse_vcard_avatar);
+
+ /* Apple idle */
+ jabber_presence_register_handler("x", NS_APPLE_IDLE, parse_apple_idle);
}
void jabber_presence_uninit(void)
diff --git a/libpurple/protocols/jabber/presence.h b/libpurple/protocols/jabber/presence.h
index e83309278a..9318fff0ca 100644
--- a/libpurple/protocols/jabber/presence.h
+++ b/libpurple/protocols/jabber/presence.h
@@ -46,7 +46,7 @@ typedef struct _JabberPresence JabberPresence;
struct _JabberPresenceChatInfo {
GSList *codes;
- xmlnode *item;
+ PurpleXmlNode *item;
};
struct _JabberPresence {
@@ -59,7 +59,7 @@ struct _JabberPresence {
JabberBuddy *jb;
JabberChat *chat;
JabberPresenceChatInfo chat_info;
- xmlnode *caps; /* TODO: Temporary, see presence.c:parse_caps */
+ PurpleXmlNode *caps; /* TODO: Temporary, see presence.c:parse_caps */
JabberBuddyState state;
gchar *status;
@@ -71,10 +71,11 @@ struct _JabberPresence {
gboolean delayed;
time_t sent;
int idle;
+ gboolean adjust_idle_for_delay;
};
typedef void (JabberPresenceHandler)(JabberStream *js, JabberPresence *presence,
- xmlnode *child);
+ PurpleXmlNode *child);
void jabber_presence_register_handler(const char *node, const char *xmlns,
JabberPresenceHandler *handler);
@@ -92,9 +93,8 @@ void jabber_set_status(PurpleAccount *account, PurpleStatus *status);
*/
void jabber_presence_send(JabberStream *js, gboolean force);
-xmlnode *jabber_presence_create(JabberBuddyState state, const char *msg, int priority); /* DEPRECATED */
-xmlnode *jabber_presence_create_js(JabberStream *js, JabberBuddyState state, const char *msg, int priority);
-void jabber_presence_parse(JabberStream *js, xmlnode *packet);
+PurpleXmlNode *jabber_presence_create_js(JabberStream *js, JabberBuddyState state, const char *msg, int priority);
+void jabber_presence_parse(JabberStream *js, PurpleXmlNode *packet);
void jabber_presence_subscription_set(JabberStream *js, const char *who,
const char *type);
void jabber_presence_fake_to_self(JabberStream *js, PurpleStatus *status);
diff --git a/libpurple/protocols/jabber/roster.c b/libpurple/protocols/jabber/roster.c
index 8c00b0eb09..9295745e34 100644
--- a/libpurple/protocols/jabber/roster.c
+++ b/libpurple/protocols/jabber/roster.c
@@ -51,9 +51,9 @@ static gchar *roster_groups_join(GSList *list)
static void roster_request_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
- xmlnode *query;
+ PurpleXmlNode *query;
if (type == JABBER_IQ_ERROR) {
/*
@@ -66,7 +66,7 @@ static void roster_request_cb(JabberStream *js, const char *from,
return;
}
- query = xmlnode_get_child(packet, "query");
+ query = purple_xmlnode_get_child(packet, "query");
if (query == NULL) {
jabber_stream_set_state(js, JABBER_STREAM_CONNECTED);
return;
@@ -79,14 +79,14 @@ static void roster_request_cb(JabberStream *js, const char *from,
void jabber_roster_request(JabberStream *js)
{
JabberIq *iq;
- xmlnode *query;
+ PurpleXmlNode *query;
iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:roster");
- query = xmlnode_get_child(iq->node, "query");
+ query = purple_xmlnode_get_child(iq->node, "query");
if (js->server_caps & JABBER_CAP_GOOGLE_ROSTER) {
- xmlnode_set_attrib(query, "xmlns:gr", NS_GOOGLE_ROSTER);
- xmlnode_set_attrib(query, "gr:ext", "2");
+ purple_xmlnode_set_attrib(query, "xmlns:gr", NS_GOOGLE_ROSTER);
+ purple_xmlnode_set_attrib(query, "gr:ext", "2");
}
jabber_iq_set_callback(iq, roster_request_cb, NULL);
@@ -97,7 +97,7 @@ static void remove_purple_buddies(JabberStream *js, const char *jid)
{
GSList *buddies, *l;
- buddies = purple_find_buddies(js->gc->account, jid);
+ buddies = purple_blist_find_buddies(purple_connection_get_account(js->gc), jid);
for(l = buddies; l; l = l->next)
purple_blist_remove_buddy(l->data);
@@ -111,12 +111,12 @@ static void add_purple_buddy_to_groups(JabberStream *js, const char *jid,
GSList *buddies, *l;
PurpleAccount *account = purple_connection_get_account(js->gc);
- buddies = purple_find_buddies(js->gc->account, jid);
+ buddies = purple_blist_find_buddies(purple_connection_get_account(js->gc), jid);
if(!groups) {
- if(!buddies)
+ if(!buddies) {
groups = g_slist_append(groups, g_strdup(JABBER_ROSTER_DEFAULT_GROUP));
- else {
+ } else {
/* TODO: What should we do here? Removing the local buddies
* is wrong, but so is letting the group state get out of sync with
* the server.
@@ -140,11 +140,15 @@ static void add_purple_buddy_to_groups(JabberStream *js, const char *jid,
*/
l = g_slist_find_custom(groups, purple_group_get_name(g),
(GCompareFunc)purple_utf8_strcasecmp);
- if (!l && g_strcmp0(purple_group_get_name(g), _("Buddies")) == 0) {
+ if (!l && g == purple_blist_get_default_group()) {
l = g_slist_find_custom(groups, JABBER_ROSTER_DEFAULT_GROUP,
(GCompareFunc)purple_utf8_strcasecmp);
}
- /* XXX: in 3.0.0 we have localized default name here too */
+ if (!l && g == purple_blist_get_default_group()) {
+ l = g_slist_find_custom(groups,
+ _purple_blist_get_localized_default_group_name(),
+ (GCompareFunc)purple_utf8_strcasecmp);
+ }
if (l) {
/* The buddy is already on the local list. Update info. */
@@ -152,20 +156,20 @@ static void add_purple_buddy_to_groups(JabberStream *js, const char *jid,
/* Previously stored serverside / buddy-supplied alias */
if((servernick = purple_blist_node_get_string((PurpleBlistNode*)b, "servernick")))
- serv_got_alias(js->gc, jid, servernick);
+ purple_serv_got_alias(js->gc, jid, servernick);
/* Alias from our roster retrieval */
- balias = purple_buddy_get_local_buddy_alias(b);
+ balias = purple_buddy_get_local_alias(b);
if(alias && !purple_strequal(alias, balias))
purple_serv_got_private_alias(js->gc, jid, alias);
g_free(l->data);
groups = g_slist_delete_link(groups, l);
} else {
/* This buddy isn't in the group on the server anymore */
- purple_debug_info("jabber", "jabber_roster_parse(): Removing %s "
- "from group '%s' on the local list\n",
- purple_buddy_get_name(b),
- jabber_roster_group_get_global_name(g));
+ purple_debug_info("jabber", "jabber_roster_parse(): "
+ "Removing %s from group '%s' on the local list",
+ purple_buddy_get_name(b),
+ jabber_roster_group_get_global_name(g));
purple_blist_remove_buddy(b);
}
}
@@ -178,7 +182,7 @@ static void add_purple_buddy_to_groups(JabberStream *js, const char *jid,
}
while(groups) {
- PurpleGroup *g = purple_find_group(groups->data);
+ PurpleGroup *g = purple_blist_find_group(groups->data);
PurpleBuddy *b = purple_buddy_new(account, jid, alias);
if(!g) {
@@ -187,7 +191,7 @@ static void add_purple_buddy_to_groups(JabberStream *js, const char *jid,
}
purple_blist_add_buddy(b, NULL, g, NULL);
- purple_blist_alias_buddy(b, alias);
+ purple_buddy_set_local_alias(b, alias);
g_free(groups->data);
groups = g_slist_delete_link(groups, groups);
@@ -197,9 +201,9 @@ static void add_purple_buddy_to_groups(JabberStream *js, const char *jid,
}
void jabber_roster_parse(JabberStream *js, const char *from,
- JabberIqType type, const char *id, xmlnode *query)
+ JabberIqType type, const char *id, PurpleXmlNode *query)
{
- xmlnode *item, *group;
+ PurpleXmlNode *item, *group;
#if 0
const char *ver;
#endif
@@ -215,7 +219,7 @@ void jabber_roster_parse(JabberStream *js, const char *from,
if (js->server_caps & JABBER_CAP_FACEBOOK)
jabber_facebook_roster_cleanup(js, query);
- for(item = xmlnode_get_child(query, "item"); item; item = xmlnode_get_next_twin(item))
+ for(item = purple_xmlnode_get_child(query, "item"); item; item = purple_xmlnode_get_next_twin(item))
{
const char *jid, *name, *subscription, *ask;
JabberBuddy *jb;
@@ -224,10 +228,10 @@ void jabber_roster_parse(JabberStream *js, const char *from,
if (!jabber_facebook_roster_incoming(js, item))
continue;
- subscription = xmlnode_get_attrib(item, "subscription");
- jid = xmlnode_get_attrib(item, "jid");
- name = xmlnode_get_attrib(item, "name");
- ask = xmlnode_get_attrib(item, "ask");
+ subscription = purple_xmlnode_get_attrib(item, "subscription");
+ jid = purple_xmlnode_get_attrib(item, "jid");
+ name = purple_xmlnode_get_attrib(item, "name");
+ ask = purple_xmlnode_get_attrib(item, "ask");
if(!jid)
continue;
@@ -264,15 +268,12 @@ void jabber_roster_parse(JabberStream *js, const char *from,
if (!jabber_google_roster_incoming(js, item))
continue;
- for(group = xmlnode_get_child(item, "group"); group; group = xmlnode_get_next_twin(group)) {
- char *group_name = xmlnode_get_data(group);
+ for(group = purple_xmlnode_get_child(item, "group"); group; group = purple_xmlnode_get_next_twin(group)) {
+ char *group_name = purple_xmlnode_get_data(group);
- if (group_name == NULL || *group_name == '\0' ||
- g_strcmp0(group_name, _("Buddies")) == 0)
- {
+ if (group_name == NULL || *group_name == '\0')
/* Changing this string? Look in add_purple_buddy_to_groups */
group_name = g_strdup(JABBER_ROSTER_DEFAULT_GROUP);
- }
/*
* See the note in add_purple_buddy_to_groups; the core handles
@@ -293,7 +294,7 @@ void jabber_roster_parse(JabberStream *js, const char *from,
}
#if 0
- ver = xmlnode_get_attrib(query, "ver");
+ ver = purple_xmlnode_get_attrib(query, "ver");
if (ver) {
PurpleAccount *account = purple_connection_get_account(js->gc);
purple_account_set_string(account, "roster_ver", ver);
@@ -317,13 +318,13 @@ static void jabber_roster_update(JabberStream *js, const char *name,
PurpleGroup *g;
GSList *l;
JabberIq *iq;
- xmlnode *query, *item, *group;
+ PurpleXmlNode *query, *item, *group;
const char *balias;
if (js->currently_parsing_roster_push)
return;
- if(!(b = purple_find_buddy(js->gc->account, name)))
+ if(!(b = purple_blist_find_buddy(purple_connection_get_account(js->gc), name)))
return;
if (groups) {
@@ -333,7 +334,7 @@ static void jabber_roster_update(JabberStream *js, const char *name,
"groups]: groups: %s\n", name, tmp);
g_free(tmp);
} else {
- GSList *buddies = purple_find_buddies(js->gc->account, name);
+ GSList *buddies = purple_blist_find_buddies(purple_connection_get_account(js->gc), name);
char *tmp;
if(!buddies)
@@ -354,33 +355,33 @@ static void jabber_roster_update(JabberStream *js, const char *name,
iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:roster");
- query = xmlnode_get_child(iq->node, "query");
- item = xmlnode_new_child(query, "item");
+ query = purple_xmlnode_get_child(iq->node, "query");
+ item = purple_xmlnode_new_child(query, "item");
- xmlnode_set_attrib(item, "jid", name);
+ purple_xmlnode_set_attrib(item, "jid", name);
- balias = purple_buddy_get_local_buddy_alias(b);
- xmlnode_set_attrib(item, "name", balias ? balias : "");
+ balias = purple_buddy_get_local_alias(b);
+ purple_xmlnode_set_attrib(item, "name", balias ? balias : "");
for(l = groups; l; l = l->next) {
- group = xmlnode_new_child(item, "group");
- xmlnode_insert_data(group, l->data, -1);
+ group = purple_xmlnode_new_child(item, "group");
+ purple_xmlnode_insert_data(group, l->data, -1);
}
g_slist_free(groups);
if (js->server_caps & JABBER_CAP_GOOGLE_ROSTER) {
jabber_google_roster_outgoing(js, query, item);
- xmlnode_set_attrib(query, "xmlns:gr", NS_GOOGLE_ROSTER);
- xmlnode_set_attrib(query, "gr:ext", "2");
+ purple_xmlnode_set_attrib(query, "xmlns:gr", NS_GOOGLE_ROSTER);
+ purple_xmlnode_set_attrib(query, "gr:ext", "2");
}
jabber_iq_send(iq);
}
void jabber_roster_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
- PurpleGroup *group)
+ PurpleGroup *group, const char *message)
{
- JabberStream *js = gc->proto_data;
+ JabberStream *js = purple_connection_get_protocol_data(gc);
char *who;
JabberID *jid;
JabberBuddy *jb;
@@ -398,7 +399,7 @@ void jabber_roster_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
return;
}
- /* Adding a chat room or a chat buddy to the roster is *not* supported. */
+ /* Adding a chat room or a chat user to the roster is *not* supported. */
if (jid->node && jabber_chat_find(js, jid->node, jid->domain) != NULL) {
/*
* This is the same thing Bonjour does. If it causes problems, move
@@ -419,7 +420,7 @@ void jabber_roster_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
* If the buddy name added contains a resource, strip that off and
* rename the buddy.
*/
- purple_blist_rename_buddy(buddy, who);
+ purple_buddy_set_name(buddy, who);
}
jb = jabber_buddy_find(js, who, FALSE);
@@ -433,7 +434,7 @@ void jabber_roster_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
} else if(!jb || !(jb->subscription & JABBER_SUB_TO)) {
jabber_presence_subscription_set(js, who, "subscribe");
} else if((jbr =jabber_buddy_find_resource(jb, NULL))) {
- purple_prpl_got_user_status(gc->account, who,
+ purple_prpl_got_user_status(purple_connection_get_account(gc), who,
jabber_buddy_state_get_status_id(jbr->state),
"priority", jbr->priority, jbr->status ? "message" : NULL, jbr->status, NULL);
}
@@ -443,15 +444,15 @@ void jabber_roster_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
void jabber_roster_alias_change(PurpleConnection *gc, const char *name, const char *alias)
{
- PurpleBuddy *b = purple_find_buddy(gc->account, name);
+ PurpleBuddy *b = purple_blist_find_buddy(purple_connection_get_account(gc), name);
if(b != NULL) {
- purple_blist_alias_buddy(b, alias);
+ purple_buddy_set_local_alias(b, alias);
purple_debug_info("jabber", "jabber_roster_alias_change(): Aliased %s to %s\n",
name, alias ? alias : "(null)");
- jabber_roster_update(gc->proto_data, name, NULL);
+ jabber_roster_update(purple_connection_get_protocol_data(gc), name, NULL);
}
}
@@ -464,7 +465,7 @@ void jabber_roster_group_change(PurpleConnection *gc, const char *name,
if(!old_group || !new_group || !strcmp(old_group, new_group))
return;
- buddies = purple_find_buddies(gc->account, name);
+ buddies = purple_blist_find_buddies(purple_connection_get_account(gc), name);
while(buddies) {
b = buddies->data;
groups = g_slist_append(groups, (char*)new_group);
@@ -474,7 +475,7 @@ void jabber_roster_group_change(PurpleConnection *gc, const char *name,
purple_debug_info("jabber", "jabber_roster_group_change(): Moving %s from %s to %s\n",
name, old_group, new_group);
- jabber_roster_update(gc->proto_data, name, groups);
+ jabber_roster_update(purple_connection_get_protocol_data(gc), name, groups);
}
void jabber_roster_group_rename(PurpleConnection *gc, const char *old_name,
@@ -491,7 +492,7 @@ void jabber_roster_group_rename(PurpleConnection *gc, const char *old_name,
void jabber_roster_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
PurpleGroup *group) {
const char *name = purple_buddy_get_name(buddy);
- GSList *buddies = purple_find_buddies(purple_connection_get_account(gc), name);
+ GSList *buddies = purple_blist_find_buddies(purple_connection_get_account(gc), name);
buddies = g_slist_remove(buddies, buddy);
if(buddies != NULL) {
@@ -508,18 +509,18 @@ void jabber_roster_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
}
purple_debug_info("jabber", "jabber_roster_remove_buddy(): "
- "Removing %s from %s\n", purple_buddy_get_name(buddy),
+ "Removing %s from %s", purple_buddy_get_name(buddy),
jabber_roster_group_get_global_name(group));
- jabber_roster_update(gc->proto_data, name, groups);
+ jabber_roster_update(purple_connection_get_protocol_data(gc), name, groups);
} else {
- JabberIq *iq = jabber_iq_new_query(gc->proto_data, JABBER_IQ_SET,
+ JabberIq *iq = jabber_iq_new_query(purple_connection_get_protocol_data(gc), JABBER_IQ_SET,
"jabber:iq:roster");
- xmlnode *query = xmlnode_get_child(iq->node, "query");
- xmlnode *item = xmlnode_new_child(query, "item");
+ PurpleXmlNode *query = purple_xmlnode_get_child(iq->node, "query");
+ PurpleXmlNode *item = purple_xmlnode_new_child(query, "item");
- xmlnode_set_attrib(item, "jid", name);
- xmlnode_set_attrib(item, "subscription", "remove");
+ purple_xmlnode_set_attrib(item, "jid", name);
+ purple_xmlnode_set_attrib(item, "subscription", "remove");
purple_debug_info("jabber", "jabber_roster_remove_buddy(): Removing %s\n",
purple_buddy_get_name(buddy));
@@ -538,9 +539,10 @@ jabber_roster_group_get_global_name(PurpleGroup *group)
if (name == NULL)
name = JABBER_ROSTER_DEFAULT_GROUP;
- else if (g_strcmp0(name, _("Buddies")) == 0)
+ else if (g_strcmp0(name, PURPLE_BLIST_DEFAULT_GROUP_NAME) == 0)
+ name = JABBER_ROSTER_DEFAULT_GROUP;
+ else if (g_strcmp0(name, _purple_blist_get_localized_default_group_name()) == 0)
name = JABBER_ROSTER_DEFAULT_GROUP;
- /* XXX: in 3.0.0 we have localized default name here too */
return name;
}
diff --git a/libpurple/protocols/jabber/roster.h b/libpurple/protocols/jabber/roster.h
index f04f2b416e..a73537fc88 100644
--- a/libpurple/protocols/jabber/roster.h
+++ b/libpurple/protocols/jabber/roster.h
@@ -32,10 +32,10 @@
void jabber_roster_request(JabberStream *js);
void jabber_roster_parse(JabberStream *js, const char *from,
- JabberIqType type, const char *id, xmlnode *query);
+ JabberIqType type, const char *id, PurpleXmlNode *query);
void jabber_roster_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
- PurpleGroup *group);
+ PurpleGroup *group, const char *message);
void jabber_roster_alias_change(PurpleConnection *gc, const char *name,
const char *alias);
void jabber_roster_group_change(PurpleConnection *gc, const char *name,
diff --git a/libpurple/protocols/jabber/si.c b/libpurple/protocols/jabber/si.c
index 46dea124bb..c9e96321f2 100644
--- a/libpurple/protocols/jabber/si.c
+++ b/libpurple/protocols/jabber/si.c
@@ -24,9 +24,9 @@
#include "internal.h"
-#include "blist.h"
+#include "buddylist.h"
#include "debug.h"
-#include "ft.h"
+#include "xfer.h"
#include "request.h"
#include "network.h"
#include "notify.h"
@@ -71,7 +71,7 @@ typedef struct _JabberSIXfer {
JabberIBBSession *ibb_session;
guint ibb_timeout_handle;
- PurpleCircBuffer *ibb_buffer;
+ PurpleCircularBuffer *ibb_buffer;
} JabberSIXfer;
/* some forward declarations */
@@ -87,9 +87,9 @@ jabber_si_xfer_find(JabberStream *js, const char *sid, const char *from)
for(xfers = js->file_transfers; xfers; xfers = xfers->next) {
PurpleXfer *xfer = xfers->data;
- JabberSIXfer *jsx = xfer->data;
- if(jsx->stream_id && xfer->who &&
- !strcmp(jsx->stream_id, sid) && !strcmp(xfer->who, from))
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
+ if(jsx->stream_id && purple_xfer_get_remote_user(xfer) &&
+ !strcmp(jsx->stream_id, sid) && !strcmp(purple_xfer_get_remote_user(xfer), from))
return xfer;
}
@@ -118,9 +118,9 @@ static void
jabber_si_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_message)
{
PurpleXfer *xfer = data;
- JabberSIXfer *jsx = xfer->data;
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
JabberIq *iq;
- xmlnode *query, *su;
+ PurpleXmlNode *query, *su;
JabberBytestreamsStreamhost *streamhost = jsx->streamhosts->data;
purple_proxy_info_destroy(jsx->gpi);
@@ -143,26 +143,26 @@ jabber_si_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_
}
/* unknown file transfer type is assumed to be RECEIVE */
- if(xfer->type == PURPLE_XFER_SEND)
+ if(purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND)
{
- xmlnode *activate;
+ PurpleXmlNode *activate;
iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET, NS_BYTESTREAMS);
- xmlnode_set_attrib(iq->node, "to", streamhost->jid);
- query = xmlnode_get_child(iq->node, "query");
- xmlnode_set_attrib(query, "sid", jsx->stream_id);
- activate = xmlnode_new_child(query, "activate");
- xmlnode_insert_data(activate, xfer->who, -1);
+ purple_xmlnode_set_attrib(iq->node, "to", streamhost->jid);
+ query = purple_xmlnode_get_child(iq->node, "query");
+ purple_xmlnode_set_attrib(query, "sid", jsx->stream_id);
+ activate = purple_xmlnode_new_child(query, "activate");
+ purple_xmlnode_insert_data(activate, purple_xfer_get_remote_user(xfer), -1);
/* TODO: We need to wait for an activation result before starting */
}
else
{
iq = jabber_iq_new_query(jsx->js, JABBER_IQ_RESULT, NS_BYTESTREAMS);
- xmlnode_set_attrib(iq->node, "to", xfer->who);
+ purple_xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer));
jabber_iq_set_id(iq, jsx->iq_id);
- query = xmlnode_get_child(iq->node, "query");
- su = xmlnode_new_child(query, "streamhost-used");
- xmlnode_set_attrib(su, "jid", streamhost->jid);
+ query = purple_xmlnode_get_child(iq->node, "query");
+ su = purple_xmlnode_new_child(query, "streamhost-used");
+ purple_xmlnode_set_attrib(su, "jid", streamhost->jid);
}
jabber_iq_send(iq);
@@ -174,7 +174,7 @@ static gboolean
connect_timeout_cb(gpointer data)
{
PurpleXfer *xfer = data;
- JabberSIXfer *jsx = xfer->data;
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
purple_debug_info("jabber", "Streamhost connection timeout of %d seconds exceeded.\n", STREAMHOST_CONNECT_TIMEOUT);
@@ -203,7 +203,7 @@ static gboolean
jabber_si_bytestreams_ibb_timeout_cb(gpointer data)
{
PurpleXfer *xfer = (PurpleXfer *) data;
- JabberSIXfer *jsx = xfer->data;
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
if (jsx && !jsx->ibb_session) {
purple_debug_info("jabber",
@@ -218,23 +218,23 @@ jabber_si_bytestreams_ibb_timeout_cb(gpointer data)
static void jabber_si_bytestreams_attempt_connect(PurpleXfer *xfer)
{
- JabberSIXfer *jsx = xfer->data;
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
JabberBytestreamsStreamhost *streamhost;
JabberID *dstjid;
if(!jsx->streamhosts) {
JabberIq *iq = jabber_iq_new(jsx->js, JABBER_IQ_ERROR);
- xmlnode *error, *inf;
+ PurpleXmlNode *error, *inf;
if(jsx->iq_id)
jabber_iq_set_id(iq, jsx->iq_id);
- xmlnode_set_attrib(iq->node, "to", xfer->who);
- error = xmlnode_new_child(iq->node, "error");
- xmlnode_set_attrib(error, "code", "404");
- xmlnode_set_attrib(error, "type", "cancel");
- inf = xmlnode_new_child(error, "item-not-found");
- xmlnode_set_namespace(inf, NS_XMPP_STANZAS);
+ purple_xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer));
+ error = purple_xmlnode_new_child(iq->node, "error");
+ purple_xmlnode_set_attrib(error, "code", "404");
+ purple_xmlnode_set_attrib(error, "type", "cancel");
+ inf = purple_xmlnode_new_child(error, "item-not-found");
+ purple_xmlnode_set_namespace(inf, NS_XMPP_STANZAS);
jabber_iq_send(iq);
@@ -247,7 +247,7 @@ static void jabber_si_bytestreams_attempt_connect(PurpleXfer *xfer)
/* 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... */
- if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND
+ if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND
&& !jsx->ibb_session) {
jabber_si_xfer_ibb_send_init(jsx->js, xfer);
} else {
@@ -266,12 +266,21 @@ static void jabber_si_bytestreams_attempt_connect(PurpleXfer *xfer)
streamhost = jsx->streamhosts->data;
- jsx->connect_data = NULL;
+ if (jsx->connect_data) {
+ purple_debug_info("jabber",
+ "jabber_si_bytestreams_attempt_connect: "
+ "cancelling existing connection attempt and restarting\n");
+ purple_proxy_connect_cancel(jsx->connect_data);
+ jsx->connect_data = NULL;
+ if (jsx->connect_timeout > 0)
+ purple_timeout_remove(jsx->connect_timeout);
+ jsx->connect_timeout = 0;
+ }
if (jsx->gpi != NULL)
purple_proxy_info_destroy(jsx->gpi);
jsx->gpi = NULL;
- dstjid = jabber_id_new(xfer->who);
+ dstjid = jabber_id_new(purple_xfer_get_remote_user(xfer));
/* TODO: Deal with zeroconf */
@@ -279,12 +288,12 @@ static void jabber_si_bytestreams_attempt_connect(PurpleXfer *xfer)
char *dstaddr, *hash;
PurpleAccount *account;
jsx->gpi = purple_proxy_info_new();
- purple_proxy_info_set_type(jsx->gpi, PURPLE_PROXY_SOCKS5);
+ purple_proxy_info_set_proxy_type(jsx->gpi, PURPLE_PROXY_SOCKS5);
purple_proxy_info_set_host(jsx->gpi, streamhost->host);
purple_proxy_info_set_port(jsx->gpi, streamhost->port);
/* unknown file transfer type is assumed to be RECEIVE */
- if(xfer->type == PURPLE_XFER_SEND)
+ if(purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND)
dstaddr = g_strdup_printf("%s%s@%s/%s%s@%s/%s", jsx->stream_id, jsx->js->user->node, jsx->js->user->domain,
jsx->js->user->resource, dstjid->node, dstjid->domain, dstjid->resource);
else
@@ -302,7 +311,7 @@ static void jabber_si_bytestreams_attempt_connect(PurpleXfer *xfer)
g_free(dstaddr);
/* When selecting a streamhost, timeout after STREAMHOST_CONNECT_TIMEOUT seconds, otherwise it takes forever */
- if (xfer->type != PURPLE_XFER_SEND && jsx->connect_data != NULL)
+ if (purple_xfer_get_xfer_type(xfer) != PURPLE_XFER_TYPE_SEND && jsx->connect_data != NULL)
jsx->connect_timeout = purple_timeout_add_seconds(
STREAMHOST_CONNECT_TIMEOUT, connect_timeout_cb, xfer);
@@ -318,11 +327,11 @@ static void jabber_si_bytestreams_attempt_connect(PurpleXfer *xfer)
}
void jabber_bytestreams_parse(JabberStream *js, const char *from,
- JabberIqType type, const char *id, xmlnode *query)
+ JabberIqType type, const char *id, PurpleXmlNode *query)
{
PurpleXfer *xfer;
JabberSIXfer *jsx;
- xmlnode *streamhost;
+ PurpleXmlNode *streamhost;
const char *sid;
if(type != JABBER_IQ_SET)
@@ -331,13 +340,13 @@ void jabber_bytestreams_parse(JabberStream *js, const char *from,
if(!from)
return;
- if(!(sid = xmlnode_get_attrib(query, "sid")))
+ if(!(sid = purple_xmlnode_get_attrib(query, "sid")))
return;
if(!(xfer = jabber_si_xfer_find(js, sid, from)))
return;
- jsx = xfer->data;
+ jsx = purple_xfer_get_protocol_data(xfer);
if(!jsx->accepted)
return;
@@ -346,15 +355,15 @@ void jabber_bytestreams_parse(JabberStream *js, const char *from,
g_free(jsx->iq_id);
jsx->iq_id = g_strdup(id);
- for(streamhost = xmlnode_get_child(query, "streamhost"); streamhost;
- streamhost = xmlnode_get_next_twin(streamhost)) {
+ for(streamhost = purple_xmlnode_get_child(query, "streamhost"); streamhost;
+ streamhost = purple_xmlnode_get_next_twin(streamhost)) {
const char *jid, *host = NULL, *port, *zeroconf;
int portnum = 0;
- if((jid = xmlnode_get_attrib(streamhost, "jid")) &&
- ((zeroconf = xmlnode_get_attrib(streamhost, "zeroconf")) ||
- ((host = xmlnode_get_attrib(streamhost, "host")) &&
- (port = xmlnode_get_attrib(streamhost, "port")) &&
+ if((jid = purple_xmlnode_get_attrib(streamhost, "jid")) &&
+ ((zeroconf = purple_xmlnode_get_attrib(streamhost, "zeroconf")) ||
+ ((host = purple_xmlnode_get_attrib(streamhost, "host")) &&
+ (port = purple_xmlnode_get_attrib(streamhost, "port")) &&
(portnum = atoi(port))))) {
JabberBytestreamsStreamhost *sh = g_new0(JabberBytestreamsStreamhost, 1);
sh->jid = g_strdup(jid);
@@ -375,15 +384,13 @@ jabber_si_xfer_bytestreams_send_read_again_resp_cb(gpointer data, gint source,
PurpleInputCondition cond)
{
PurpleXfer *xfer = data;
- JabberSIXfer *jsx = xfer->data;
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
int len;
len = write(source, jsx->rxqueue + jsx->rxlen, jsx->rxmaxlen - jsx->rxlen);
if (len < 0 && errno == EAGAIN)
return;
else if (len < 0) {
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
g_free(jsx->rxqueue);
jsx->rxqueue = NULL;
close(source);
@@ -395,8 +402,8 @@ jabber_si_xfer_bytestreams_send_read_again_resp_cb(gpointer data, gint source,
if (jsx->rxlen < jsx->rxmaxlen)
return;
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
+ purple_input_remove(purple_xfer_get_watcher(xfer));
+ purple_xfer_set_watcher(xfer, 0);
g_free(jsx->rxqueue);
jsx->rxqueue = NULL;
@@ -412,7 +419,7 @@ jabber_si_xfer_bytestreams_send_read_again_cb(gpointer data, gint source,
PurpleInputCondition cond)
{
PurpleXfer *xfer = data;
- JabberSIXfer *jsx = xfer->data;
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
char buffer[42]; /* 40 for DST.ADDR + 2 bytes for port number*/
int len;
char *dstaddr, *hash;
@@ -426,8 +433,6 @@ jabber_si_xfer_bytestreams_send_read_again_cb(gpointer data, gint source,
if(len < 0 && errno == EAGAIN)
return;
else if(len <= 0) {
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
close(source);
purple_xfer_cancel_remote(xfer);
return;
@@ -441,12 +446,10 @@ jabber_si_xfer_bytestreams_send_read_again_cb(gpointer data, gint source,
purple_debug_info("jabber", "Invalid socks5 conn req. header[0x%x,0x%x,0x%x,0x%x,0x%x]\n",
jsx->rxqueue[0], jsx->rxqueue[1], jsx->rxqueue[2],
jsx->rxqueue[3], jsx->rxqueue[4]);
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
close(source);
purple_xfer_cancel_remote(xfer);
return;
- } else if(jsx->rxlen - 5 < jsx->rxqueue[4] + 2) {
+ } else if(jsx->rxlen - 5 < (size_t)jsx->rxqueue[4] + 2) {
/* Upper-bound of 257 (jsx->rxlen = 5, jsx->rxqueue[4] = 0xFF) */
unsigned short to_read = jsx->rxqueue[4] + 2 - (jsx->rxlen - 5);
purple_debug_info("jabber", "reading %u bytes for DST.ADDR + port num (trying to read %hu now)\n",
@@ -455,8 +458,6 @@ jabber_si_xfer_bytestreams_send_read_again_cb(gpointer data, gint source,
if(len < 0 && errno == EAGAIN)
return;
else if(len <= 0) {
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
close(source);
purple_xfer_cancel_remote(xfer);
return;
@@ -467,15 +468,15 @@ jabber_si_xfer_bytestreams_send_read_again_cb(gpointer data, gint source,
}
/* Have we not read all of DST.ADDR and the following 2-byte port number? */
- if(jsx->rxlen - 5 < jsx->rxqueue[4] + 2)
+ if(jsx->rxlen - 5 < (size_t)jsx->rxqueue[4] + 2)
return;
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
+ purple_input_remove(purple_xfer_get_watcher(xfer));
+ purple_xfer_set_watcher(xfer, 0);
dstaddr = g_strdup_printf("%s%s@%s/%s%s", jsx->stream_id,
jsx->js->user->node, jsx->js->user->domain,
- jsx->js->user->resource, xfer->who);
+ jsx->js->user->resource, purple_xfer_get_remote_user(xfer));
/* Per XEP-0065, the 'host' must be SHA1(SID + from JID + to JID) */
hash = jabber_calculate_data_hash(dstaddr, strlen(dstaddr), "sha1");
@@ -516,8 +517,8 @@ jabber_si_xfer_bytestreams_send_read_again_cb(gpointer data, gint source,
jsx->rxqueue[5+strlen(host)] = 0x00;
jsx->rxqueue[6+strlen(host)] = 0x00;
- xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
- jabber_si_xfer_bytestreams_send_read_again_resp_cb, xfer);
+ purple_xfer_set_watcher(xfer, purple_input_add(source, PURPLE_INPUT_WRITE,
+ jabber_si_xfer_bytestreams_send_read_again_resp_cb, xfer));
jabber_si_xfer_bytestreams_send_read_again_resp_cb(xfer, source,
PURPLE_INPUT_WRITE);
}
@@ -527,15 +528,13 @@ jabber_si_xfer_bytestreams_send_read_response_cb(gpointer data, gint source,
PurpleInputCondition cond)
{
PurpleXfer *xfer = data;
- JabberSIXfer *jsx = xfer->data;
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
int len;
len = write(source, jsx->rxqueue + jsx->rxlen, jsx->rxmaxlen - jsx->rxlen);
if (len < 0 && errno == EAGAIN)
return;
else if (len < 0) {
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
g_free(jsx->rxqueue);
jsx->rxqueue = NULL;
close(source);
@@ -547,13 +546,11 @@ jabber_si_xfer_bytestreams_send_read_response_cb(gpointer data, gint source,
if (jsx->rxlen < jsx->rxmaxlen)
return;
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
-
/* If we sent a "Success", wait for a response, otherwise give up and cancel */
if (jsx->rxqueue[1] == 0x00) {
- xfer->watcher = purple_input_add(source, PURPLE_INPUT_READ,
- jabber_si_xfer_bytestreams_send_read_again_cb, xfer);
+ purple_input_remove(purple_xfer_get_watcher(xfer));
+ purple_xfer_set_watcher(xfer, purple_input_add(source, PURPLE_INPUT_READ,
+ jabber_si_xfer_bytestreams_send_read_again_cb, xfer));
g_free(jsx->rxqueue);
jsx->rxqueue = NULL;
jsx->rxlen = 0;
@@ -568,14 +565,14 @@ jabber_si_xfer_bytestreams_send_read_cb(gpointer data, gint source,
PurpleInputCondition cond)
{
PurpleXfer *xfer = data;
- JabberSIXfer *jsx = xfer->data;
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
int i;
int len;
char buffer[256];
purple_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_read_cb\n");
- xfer->fd = source;
+ purple_xfer_set_fd(xfer, source);
/** Try to read the SOCKS5 header */
if(jsx->rxlen < 2) {
@@ -584,9 +581,6 @@ jabber_si_xfer_bytestreams_send_read_cb(gpointer data, gint source,
if(len < 0 && errno == EAGAIN)
return;
else if(len <= 0) {
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
- close(source);
purple_xfer_cancel_remote(xfer);
return;
}
@@ -594,7 +588,7 @@ jabber_si_xfer_bytestreams_send_read_cb(gpointer data, gint source,
memcpy(jsx->rxqueue + jsx->rxlen, buffer, len);
jsx->rxlen += len;
return;
- } else if(jsx->rxlen - 2 < jsx->rxqueue[1]) {
+ } else if(jsx->rxlen - 2 < (size_t)jsx->rxqueue[1]) {
/* Has a maximum value of 255 (jsx->rxlen = 2, jsx->rxqueue[1] = 0xFF) */
unsigned short to_read = jsx->rxqueue[1] - (jsx->rxlen - 2);
purple_debug_info("jabber", "reading %u bytes for auth methods (trying to read %hu now)\n",
@@ -603,9 +597,6 @@ jabber_si_xfer_bytestreams_send_read_cb(gpointer data, gint source,
if(len < 0 && errno == EAGAIN)
return;
else if(len <= 0) {
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
- close(source);
purple_xfer_cancel_remote(xfer);
return;
}
@@ -615,25 +606,21 @@ jabber_si_xfer_bytestreams_send_read_cb(gpointer data, gint source,
}
/* Have we not read all the auth. method bytes? */
- if(jsx->rxlen -2 < jsx->rxqueue[1])
+ if(jsx->rxlen -2 < (size_t)jsx->rxqueue[1])
return;
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
-
purple_debug_info("jabber", "checking to make sure we're socks FIVE\n");
if(jsx->rxqueue[0] != 0x05) {
- close(source);
purple_xfer_cancel_remote(xfer);
return;
}
- purple_debug_info("jabber", "going to test %hhu different methods\n", jsx->rxqueue[1]);
+ purple_debug_info("jabber", "going to test %u different methods\n", (guint)jsx->rxqueue[1]);
for(i=0; i<jsx->rxqueue[1]; i++) {
- purple_debug_info("jabber", "testing %hhu\n", jsx->rxqueue[i+2]);
+ purple_debug_info("jabber", "testing %u\n", (guint)jsx->rxqueue[i+2]);
if(jsx->rxqueue[i+2] == 0x00) {
g_free(jsx->rxqueue);
jsx->rxlen = 0;
@@ -641,9 +628,10 @@ jabber_si_xfer_bytestreams_send_read_cb(gpointer data, gint source,
jsx->rxqueue = g_malloc(jsx->rxmaxlen);
jsx->rxqueue[0] = 0x05;
jsx->rxqueue[1] = 0x00;
- xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
+ purple_input_remove(purple_xfer_get_watcher(xfer));
+ purple_xfer_set_watcher(xfer, purple_input_add(source, PURPLE_INPUT_WRITE,
jabber_si_xfer_bytestreams_send_read_response_cb,
- xfer);
+ xfer));
jabber_si_xfer_bytestreams_send_read_response_cb(xfer,
source, PURPLE_INPUT_WRITE);
jsx->rxqueue = NULL;
@@ -658,8 +646,9 @@ jabber_si_xfer_bytestreams_send_read_cb(gpointer data, gint source,
jsx->rxqueue = g_malloc(jsx->rxmaxlen);
jsx->rxqueue[0] = 0x05;
jsx->rxqueue[1] = 0xFF;
- xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
- jabber_si_xfer_bytestreams_send_read_response_cb, xfer);
+ purple_input_remove(purple_xfer_get_watcher(xfer));
+ purple_xfer_set_watcher(xfer, purple_input_add(source, PURPLE_INPUT_WRITE,
+ jabber_si_xfer_bytestreams_send_read_response_cb, xfer));
jabber_si_xfer_bytestreams_send_read_response_cb(xfer,
source, PURPLE_INPUT_WRITE);
}
@@ -680,7 +669,7 @@ jabber_si_xfer_bytestreams_send_connected_cb(gpointer data, gint source,
PurpleInputCondition cond)
{
PurpleXfer *xfer = data;
- JabberSIXfer *jsx = xfer->data;
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
int acceptfd;
purple_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_connected_cb\n");
@@ -694,24 +683,24 @@ jabber_si_xfer_bytestreams_send_connected_cb(gpointer data, gint source,
return;
}
- purple_input_remove(xfer->watcher);
+ purple_input_remove(purple_xfer_get_watcher(xfer));
close(source);
jsx->local_streamhost_fd = -1;
_purple_network_set_common_socket_flags(acceptfd);
- xfer->watcher = purple_input_add(acceptfd, PURPLE_INPUT_READ,
- jabber_si_xfer_bytestreams_send_read_cb, xfer);
+ purple_xfer_set_watcher(xfer, purple_input_add(acceptfd, PURPLE_INPUT_READ,
+ jabber_si_xfer_bytestreams_send_read_cb, xfer));
}
static void
jabber_si_connect_proxy_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
PurpleXfer *xfer = data;
JabberSIXfer *jsx;
- xmlnode *query, *streamhost_used;
+ PurpleXmlNode *query, *streamhost_used;
const char *jid;
GList *matched;
@@ -723,12 +712,12 @@ jabber_si_connect_proxy_cb(JabberStream *js, const char *from,
return;
}
+ jsx = purple_xfer_get_protocol_data(xfer);
+
/* In the case of a direct file transfer, this is expected to return */
- if(!xfer->data)
+ if(!jsx)
return;
- jsx = xfer->data;
-
if(type != JABBER_IQ_RESULT) {
purple_debug_info("jabber",
"jabber_si_xfer_connect_proxy_cb: type = error\n");
@@ -741,7 +730,7 @@ jabber_si_connect_proxy_cb(JabberStream *js, const char *from,
/* if we are the sender and haven't already opened an IBB
session, do so now (we might already have failed to open
the bytestream proxy ourselves when receiving this <iq/> */
- if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND
+ if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND
&& !jsx->ibb_session) {
jabber_si_xfer_ibb_send_init(js, xfer);
} else {
@@ -759,13 +748,13 @@ jabber_si_connect_proxy_cb(JabberStream *js, const char *from,
if (!from)
return;
- if(!(query = xmlnode_get_child(packet, "query")))
+ if(!(query = purple_xmlnode_get_child(packet, "query")))
return;
- if(!(streamhost_used = xmlnode_get_child(query, "streamhost-used")))
+ if(!(streamhost_used = purple_xmlnode_get_child(query, "streamhost-used")))
return;
- if(!(jid = xmlnode_get_attrib(streamhost_used, "jid")))
+ if(!(jid = purple_xmlnode_get_attrib(streamhost_used, "jid")))
return;
purple_debug_info("jabber", "jabber_si_connect_proxy_cb() will be looking at jsx %p: jsx->streamhosts is %p and jid is %s\n",
@@ -777,13 +766,13 @@ jabber_si_connect_proxy_cb(JabberStream *js, const char *from,
jsx->js->user->domain, jsx->js->user->resource);
if (!strcmp(jid, my_jid)) {
purple_debug_info("jabber", "Got local SOCKS5 streamhost-used.\n");
- purple_xfer_start(xfer, xfer->fd, NULL, -1);
+ purple_xfer_start(xfer, purple_xfer_get_fd(xfer), NULL, -1);
} else {
/* if available, try to revert to IBB... */
if (jsx->stream_method & STREAM_METHOD_IBB) {
purple_debug_info("jabber",
"jabber_si_connect_proxy_cb: trying to revert to IBB\n");
- if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
+ if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND) {
jabber_si_xfer_ibb_send_init(jsx->js, xfer);
} else {
jsx->ibb_timeout_handle = purple_timeout_add_seconds(30,
@@ -801,9 +790,9 @@ jabber_si_connect_proxy_cb(JabberStream *js, const char *from,
}
/* Clean up the local streamhost - it isn't going to be used.*/
- if (xfer->watcher > 0) {
- purple_input_remove(xfer->watcher);
- xfer->watcher = 0;
+ if (purple_xfer_get_watcher(xfer) > 0) {
+ purple_input_remove(purple_xfer_get_watcher(xfer));
+ purple_xfer_set_watcher(xfer, 0);
}
if (jsx->local_streamhost_fd >= 0) {
close(jsx->local_streamhost_fd);
@@ -825,29 +814,29 @@ jabber_si_xfer_bytestreams_listen_cb(int sock, gpointer data)
PurpleXfer *xfer = data;
JabberSIXfer *jsx;
JabberIq *iq;
- xmlnode *query, *streamhost;
+ PurpleXmlNode *query, *streamhost;
char port[6];
GList *tmp;
JabberBytestreamsStreamhost *sh, *sh2;
int streamhost_count = 0;
- jsx = xfer->data;
+ jsx = purple_xfer_get_protocol_data(xfer);
jsx->listen_data = NULL;
/* I'm not sure under which conditions this can happen
* (it seems like it shouldn't be possible */
if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) {
- purple_xfer_unref(xfer);
+ g_object_unref(xfer);
return;
}
- purple_xfer_unref(xfer);
+ g_object_unref(xfer);
iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET, NS_BYTESTREAMS);
- xmlnode_set_attrib(iq->node, "to", xfer->who);
- query = xmlnode_get_child(iq->node, "query");
+ purple_xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer));
+ query = purple_xmlnode_get_child(iq->node, "query");
- xmlnode_set_attrib(query, "sid", jsx->stream_id);
+ purple_xmlnode_set_attrib(query, "sid", jsx->stream_id);
/* If we successfully started listening locally */
if (sock >= 0) {
@@ -861,8 +850,8 @@ jabber_si_xfer_bytestreams_listen_cb(int sock, gpointer data)
jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node,
jsx->js->user->domain, jsx->js->user->resource);
- xfer->local_port = purple_network_get_port_from_fd(sock);
- g_snprintf(port, sizeof(port), "%hu", (guint16)xfer->local_port);
+ purple_xfer_set_local_port(xfer, purple_network_get_port_from_fd(sock));
+ g_snprintf(port, sizeof(port), "%hu", purple_xfer_get_local_port(xfer));
public_ip = purple_network_get_my_ip(jsx->js->fd);
@@ -870,10 +859,10 @@ jabber_si_xfer_bytestreams_listen_cb(int sock, gpointer data)
while (local_ips) {
gchar *local_ip = local_ips->data;
streamhost_count++;
- streamhost = xmlnode_new_child(query, "streamhost");
- xmlnode_set_attrib(streamhost, "jid", jid);
- xmlnode_set_attrib(streamhost, "host", local_ip);
- xmlnode_set_attrib(streamhost, "port", port);
+ streamhost = purple_xmlnode_new_child(query, "streamhost");
+ purple_xmlnode_set_attrib(streamhost, "jid", jid);
+ purple_xmlnode_set_attrib(streamhost, "host", local_ip);
+ purple_xmlnode_set_attrib(streamhost, "port", port);
if (purple_strequal(local_ip, public_ip))
has_public_ip = TRUE;
g_free(local_ip);
@@ -883,17 +872,17 @@ jabber_si_xfer_bytestreams_listen_cb(int sock, gpointer data)
/* Include the public IP (assuming that there is a port mapped somehow) */
if (!has_public_ip && strcmp(public_ip, "0.0.0.0") != 0) {
streamhost_count++;
- streamhost = xmlnode_new_child(query, "streamhost");
- xmlnode_set_attrib(streamhost, "jid", jid);
- xmlnode_set_attrib(streamhost, "host", public_ip);
- xmlnode_set_attrib(streamhost, "port", port);
+ streamhost = purple_xmlnode_new_child(query, "streamhost");
+ purple_xmlnode_set_attrib(streamhost, "jid", jid);
+ purple_xmlnode_set_attrib(streamhost, "host", public_ip);
+ purple_xmlnode_set_attrib(streamhost, "port", port);
}
g_free(jid);
/* The listener for the local proxy */
- xfer->watcher = purple_input_add(sock, PURPLE_INPUT_READ,
- jabber_si_xfer_bytestreams_send_connected_cb, xfer);
+ purple_xfer_set_watcher(xfer, purple_input_add(sock, PURPLE_INPUT_READ,
+ jabber_si_xfer_bytestreams_send_connected_cb, xfer));
}
for (tmp = jsx->js->bs_proxies; tmp; tmp = tmp->next) {
@@ -910,11 +899,11 @@ jabber_si_xfer_bytestreams_listen_cb(int sock, gpointer data)
continue;
streamhost_count++;
- streamhost = xmlnode_new_child(query, "streamhost");
- xmlnode_set_attrib(streamhost, "jid", sh->jid);
- xmlnode_set_attrib(streamhost, "host", sh->host);
- g_snprintf(port, sizeof(port), "%hu", (guint16)sh->port);
- xmlnode_set_attrib(streamhost, "port", port);
+ streamhost = purple_xmlnode_new_child(query, "streamhost");
+ purple_xmlnode_set_attrib(streamhost, "jid", sh->jid);
+ purple_xmlnode_set_attrib(streamhost, "host", sh->host);
+ g_snprintf(port, sizeof(port), "%hu", sh->port);
+ purple_xmlnode_set_attrib(streamhost, "port", port);
sh2 = g_new0(JabberBytestreamsStreamhost, 1);
sh2->jid = g_strdup(sh->jid);
@@ -933,7 +922,7 @@ jabber_si_xfer_bytestreams_listen_cb(int sock, gpointer data)
if (jsx->stream_method & STREAM_METHOD_IBB) {
purple_debug_info("jabber",
"jabber_si_xfer_bytestreams_listen_cb: trying to revert to IBB\n");
- if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
+ if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND) {
/* if we are the sender, init the IBB session... */
jabber_si_xfer_ibb_send_init(jsx->js, xfer);
} else {
@@ -963,19 +952,19 @@ jabber_si_xfer_bytestreams_send_init(PurpleXfer *xfer)
JabberSIXfer *jsx;
PurpleProxyType proxy_type;
- purple_xfer_ref(xfer);
+ g_object_ref(xfer);
- jsx = xfer->data;
+ jsx = purple_xfer_get_protocol_data(xfer);
/* TODO: This should probably be done with an account option instead of
* piggy-backing on the TOR proxy type. */
- proxy_type = purple_proxy_info_get_type(
+ proxy_type = purple_proxy_info_get_proxy_type(
purple_proxy_get_setup(purple_connection_get_account(jsx->js->gc)));
if (proxy_type == PURPLE_PROXY_TOR) {
purple_debug_info("jabber", "Skipping attempting local streamhost.\n");
jsx->listen_data = NULL;
} else
- jsx->listen_data = purple_network_listen_range(0, 0, SOCK_STREAM,
+ jsx->listen_data = purple_network_listen_range(0, 0, AF_UNSPEC, SOCK_STREAM, TRUE,
jabber_si_xfer_bytestreams_listen_cb, xfer);
if (jsx->listen_data == NULL) {
@@ -1013,12 +1002,12 @@ jabber_si_xfer_ibb_recv_data_cb(JabberIBBSession *sess, gpointer data,
gsize size)
{
PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
- JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
- if (size <= purple_xfer_get_bytes_remaining(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",
size);
- purple_circ_buffer_append(jsx->ibb_buffer, data, size);
+ purple_circular_buffer_append(jsx->ibb_buffer, data, size);
purple_xfer_prpl_ready(xfer);
} else {
/* trying to write past size of file transfers negotiated size,
@@ -1031,19 +1020,19 @@ jabber_si_xfer_ibb_recv_data_cb(JabberIBBSession *sess, gpointer data,
}
static gssize
-jabber_si_xfer_ibb_read(guchar **out_buffer, PurpleXfer *xfer)
+jabber_si_xfer_ibb_read(guchar **out_buffer, size_t buf_size, PurpleXfer *xfer)
{
- JabberSIXfer *jsx = xfer->data;
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
guchar *buffer;
- gsize size;
+ gsize size = purple_circular_buffer_get_used(jsx->ibb_buffer);
gsize tmp;
- size = jsx->ibb_buffer->bufused;
*out_buffer = buffer = g_malloc(size);
- while ((tmp = purple_circ_buffer_get_max_read(jsx->ibb_buffer))) {
- memcpy(buffer, jsx->ibb_buffer->outptr, tmp);
+ while ((tmp = purple_circular_buffer_get_max_read(jsx->ibb_buffer))) {
+ const gchar *output = purple_circular_buffer_get_output(jsx->ibb_buffer);
+ memcpy(buffer, output, tmp);
buffer += tmp;
- purple_circ_buffer_mark_read(jsx->ibb_buffer, tmp);
+ purple_circular_buffer_mark_read(jsx->ibb_buffer, tmp);
}
return size;
@@ -1051,12 +1040,12 @@ jabber_si_xfer_ibb_read(guchar **out_buffer, PurpleXfer *xfer)
static gboolean
jabber_si_xfer_ibb_open_cb(JabberStream *js, const char *who, const char *id,
- xmlnode *open)
+ PurpleXmlNode *open)
{
- const gchar *sid = xmlnode_get_attrib(open, "sid");
+ const gchar *sid = purple_xmlnode_get_attrib(open, "sid");
PurpleXfer *xfer = jabber_si_xfer_find(js, sid, who);
if (xfer) {
- JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
JabberIBBSession *sess =
jabber_ibb_session_create_from_xmlnode(js, who, id, open, xfer);
@@ -1076,7 +1065,7 @@ jabber_si_xfer_ibb_open_cb(JabberStream *js, const char *who, const char *id,
clients interpreting the block-size attribute as that
(see also remark in ibb.c) */
jsx->ibb_buffer =
- purple_circ_buffer_new(jabber_ibb_session_get_block_size(sess));
+ 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);
@@ -1101,7 +1090,7 @@ jabber_si_xfer_ibb_open_cb(JabberStream *js, const char *who, const char *id,
static gssize
jabber_si_xfer_ibb_write(const guchar *buffer, size_t len, PurpleXfer *xfer)
{
- JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(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);
@@ -1115,7 +1104,7 @@ static void
jabber_si_xfer_ibb_sent_cb(JabberIBBSession *sess)
{
PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
- gsize remaining = purple_xfer_get_bytes_remaining(xfer);
+ goffset remaining = purple_xfer_get_bytes_remaining(xfer);
if (remaining == 0) {
/* close the session */
@@ -1145,7 +1134,7 @@ jabber_si_xfer_ibb_opened_cb(JabberIBBSession *sess)
static void
jabber_si_xfer_ibb_send_init(JabberStream *js, PurpleXfer *xfer)
{
- JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
jsx->ibb_session = jabber_ibb_session_create(js, jsx->stream_id,
purple_xfer_get_remote_user(xfer), xfer);
@@ -1164,7 +1153,7 @@ jabber_si_xfer_ibb_send_init(JabberStream *js, PurpleXfer *xfer)
purple_xfer_set_write_fnc(xfer, jabber_si_xfer_ibb_write);
jsx->ibb_buffer =
- purple_circ_buffer_new(jabber_ibb_session_get_max_data_size(jsx->ibb_session));
+ purple_circular_buffer_new(jabber_ibb_session_get_max_data_size(jsx->ibb_session));
/* open the IBB session */
jabber_ibb_session_open(jsx->ibb_session);
@@ -1179,34 +1168,34 @@ jabber_si_xfer_ibb_send_init(JabberStream *js, PurpleXfer *xfer)
static void jabber_si_xfer_send_method_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
- xmlnode *packet, gpointer data)
+ PurpleXmlNode *packet, gpointer data)
{
PurpleXfer *xfer = data;
- xmlnode *si, *feature, *x, *field, *value;
+ PurpleXmlNode *si, *feature, *x, *field, *value;
gboolean found_method = FALSE;
- if(!(si = xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si"))) {
+ if(!(si = purple_xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si"))) {
purple_xfer_cancel_remote(xfer);
return;
}
- if(!(feature = xmlnode_get_child_with_namespace(si, "feature", "http://jabber.org/protocol/feature-neg"))) {
+ if(!(feature = purple_xmlnode_get_child_with_namespace(si, "feature", "http://jabber.org/protocol/feature-neg"))) {
purple_xfer_cancel_remote(xfer);
return;
}
- if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data"))) {
+ if(!(x = purple_xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data"))) {
purple_xfer_cancel_remote(xfer);
return;
}
- for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
- const char *var = xmlnode_get_attrib(field, "var");
- JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+ 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);
if(var && !strcmp(var, "stream-method")) {
- if((value = xmlnode_get_child(field, "value"))) {
- char *val = xmlnode_get_data(value);
+ if((value = purple_xmlnode_get_child(field, "value"))) {
+ char *val = purple_xmlnode_get_data(value);
if(val && !strcmp(val, NS_BYTESTREAMS)) {
jabber_si_xfer_bytestreams_send_init(xfer);
jsx->stream_method |= STREAM_METHOD_BYTESTREAMS;
@@ -1233,9 +1222,9 @@ static void jabber_si_xfer_send_method_cb(JabberStream *js, const char *from,
static void jabber_si_xfer_send_request(PurpleXfer *xfer)
{
- JabberSIXfer *jsx = xfer->data;
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
JabberIq *iq;
- xmlnode *si, *file, *feature, *x, *field, *option, *value;
+ PurpleXmlNode *si, *file, *feature, *x, *field, *option, *value;
char buf[32];
#if ENABLE_FT_THUMBNAILS
gconstpointer thumb;
@@ -1243,21 +1232,21 @@ static void jabber_si_xfer_send_request(PurpleXfer *xfer)
purple_xfer_prepare_thumbnail(xfer, "jpeg,png");
#endif
- xfer->filename = g_path_get_basename(xfer->local_filename);
+ purple_xfer_set_filename(xfer, g_path_get_basename(purple_xfer_get_local_filename(xfer)));
iq = jabber_iq_new(jsx->js, JABBER_IQ_SET);
- xmlnode_set_attrib(iq->node, "to", xfer->who);
- si = xmlnode_new_child(iq->node, "si");
- xmlnode_set_namespace(si, "http://jabber.org/protocol/si");
+ purple_xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer));
+ si = purple_xmlnode_new_child(iq->node, "si");
+ purple_xmlnode_set_namespace(si, "http://jabber.org/protocol/si");
jsx->stream_id = jabber_get_next_id(jsx->js);
- xmlnode_set_attrib(si, "id", jsx->stream_id);
- xmlnode_set_attrib(si, "profile", NS_SI_FILE_TRANSFER);
-
- file = xmlnode_new_child(si, "file");
- xmlnode_set_namespace(file, NS_SI_FILE_TRANSFER);
- xmlnode_set_attrib(file, "name", xfer->filename);
- g_snprintf(buf, sizeof(buf), "%" G_GSIZE_FORMAT, xfer->size);
- xmlnode_set_attrib(file, "size", buf);
+ purple_xmlnode_set_attrib(si, "id", jsx->stream_id);
+ purple_xmlnode_set_attrib(si, "profile", NS_SI_FILE_TRANSFER);
+
+ file = purple_xmlnode_new_child(si, "file");
+ purple_xmlnode_set_namespace(file, NS_SI_FILE_TRANSFER);
+ purple_xmlnode_set_attrib(file, "name", purple_xfer_get_filename(xfer));
+ g_snprintf(buf, sizeof(buf), "%" G_GOFFSET_FORMAT, purple_xfer_get_size(xfer));
+ purple_xmlnode_set_attrib(file, "size", buf);
/* maybe later we'll do hash and date attribs */
#if ENABLE_FT_THUMBNAILS
@@ -1267,32 +1256,32 @@ static void jabber_si_xfer_send_request(PurpleXfer *xfer)
JabberData *thumbnail_data =
jabber_data_create_from_data(thumb, thumb_size,
mimetype, TRUE, jsx->js);
- xmlnode *thumbnail = xmlnode_new_child(file, "thumbnail");
- xmlnode_set_namespace(thumbnail, NS_THUMBS);
- xmlnode_set_attrib(thumbnail, "cid",
+ PurpleXmlNode *thumbnail = purple_xmlnode_new_child(file, "thumbnail");
+ purple_xmlnode_set_namespace(thumbnail, NS_THUMBS);
+ purple_xmlnode_set_attrib(thumbnail, "cid",
jabber_data_get_cid(thumbnail_data));
- xmlnode_set_attrib(thumbnail, "mime-type", mimetype);
+ purple_xmlnode_set_attrib(thumbnail, "mime-type", mimetype);
/* cache data */
jabber_data_associate_local(thumbnail_data, NULL);
}
#endif
- feature = xmlnode_new_child(si, "feature");
- xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
- x = xmlnode_new_child(feature, "x");
- xmlnode_set_namespace(x, "jabber:x:data");
- xmlnode_set_attrib(x, "type", "form");
- field = xmlnode_new_child(x, "field");
- xmlnode_set_attrib(field, "var", "stream-method");
- xmlnode_set_attrib(field, "type", "list-single");
+ feature = purple_xmlnode_new_child(si, "feature");
+ purple_xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
+ x = purple_xmlnode_new_child(feature, "x");
+ purple_xmlnode_set_namespace(x, "jabber:x:data");
+ purple_xmlnode_set_attrib(x, "type", "form");
+ field = purple_xmlnode_new_child(x, "field");
+ purple_xmlnode_set_attrib(field, "var", "stream-method");
+ purple_xmlnode_set_attrib(field, "type", "list-single");
/* maybe we should add an option to always skip bytestreams for people
behind troublesome firewalls */
- option = xmlnode_new_child(field, "option");
- value = xmlnode_new_child(option, "value");
- xmlnode_insert_data(value, NS_BYTESTREAMS, -1);
- option = xmlnode_new_child(field, "option");
- value = xmlnode_new_child(option, "value");
- xmlnode_insert_data(value, NS_IBB, -1);
+ option = purple_xmlnode_new_child(field, "option");
+ value = purple_xmlnode_new_child(option, "value");
+ purple_xmlnode_insert_data(value, NS_BYTESTREAMS, -1);
+ option = purple_xmlnode_new_child(field, "option");
+ value = purple_xmlnode_new_child(option, "value");
+ purple_xmlnode_insert_data(value, NS_IBB, -1);
jabber_iq_set_callback(iq, jabber_si_xfer_send_method_cb, xfer);
@@ -1305,7 +1294,7 @@ static void jabber_si_xfer_send_request(PurpleXfer *xfer)
static void jabber_si_xfer_free(PurpleXfer *xfer)
{
- JabberSIXfer *jsx = xfer->data;
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
if (jsx) {
JabberStream *js = jsx->js;
@@ -1320,10 +1309,9 @@ static void jabber_si_xfer_free(PurpleXfer *xfer)
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_type(xfer) == PURPLE_XFER_SEND &&
- xfer->fd >= 0) {
+ 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(xfer->fd);
+ purple_network_remove_port_mapping(purple_xfer_get_fd(xfer));
}
if (jsx->connect_timeout > 0)
purple_timeout_remove(jsx->connect_timeout);
@@ -1342,7 +1330,7 @@ static void jabber_si_xfer_free(PurpleXfer *xfer)
}
if (jsx->ibb_buffer) {
- purple_circ_buffer_destroy(jsx->ibb_buffer);
+ g_object_unref(G_OBJECT(jsx->ibb_buffer));
}
purple_debug_info("jabber", "jabber_si_xfer_free(): freeing jsx %p\n", jsx);
@@ -1352,7 +1340,7 @@ static void jabber_si_xfer_free(PurpleXfer *xfer)
/* XXX: free other stuff */
g_free(jsx->rxqueue);
g_free(jsx);
- xfer->data = NULL;
+ purple_xfer_set_protocol_data(xfer, NULL);
}
}
@@ -1363,7 +1351,7 @@ static void jabber_si_xfer_free(PurpleXfer *xfer)
*/
static void jabber_si_xfer_cancel_send(PurpleXfer *xfer)
{
- JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
/* if there is an IBB session active, send close on that */
if (jsx->ibb_session) {
@@ -1376,7 +1364,7 @@ static void jabber_si_xfer_cancel_send(PurpleXfer *xfer)
static void jabber_si_xfer_request_denied(PurpleXfer *xfer)
{
- JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
JabberStream *js = jsx->js;
/*
@@ -1385,18 +1373,18 @@ static void jabber_si_xfer_request_denied(PurpleXfer *xfer)
*/
if (jsx->iq_id && !jsx->accepted) {
JabberIq *iq;
- xmlnode *error, *child;
+ PurpleXmlNode *error, *child;
iq = jabber_iq_new(js, JABBER_IQ_ERROR);
- xmlnode_set_attrib(iq->node, "to", xfer->who);
+ purple_xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer));
jabber_iq_set_id(iq, jsx->iq_id);
- error = xmlnode_new_child(iq->node, "error");
- xmlnode_set_attrib(error, "type", "cancel");
- child = xmlnode_new_child(error, "forbidden");
- xmlnode_set_namespace(child, NS_XMPP_STANZAS);
- child = xmlnode_new_child(error, "text");
- xmlnode_set_namespace(child, NS_XMPP_STANZAS);
- xmlnode_insert_data(child, "Offer Declined", -1);
+ error = purple_xmlnode_new_child(iq->node, "error");
+ purple_xmlnode_set_attrib(error, "type", "cancel");
+ child = purple_xmlnode_new_child(error, "forbidden");
+ purple_xmlnode_set_namespace(child, NS_XMPP_STANZAS);
+ child = purple_xmlnode_new_child(error, "text");
+ purple_xmlnode_set_namespace(child, NS_XMPP_STANZAS);
+ purple_xmlnode_insert_data(child, "Offer Declined", -1);
jabber_iq_send(iq);
}
@@ -1408,7 +1396,7 @@ static void jabber_si_xfer_request_denied(PurpleXfer *xfer)
static void jabber_si_xfer_cancel_recv(PurpleXfer *xfer)
{
- JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
/* if there is an IBB session active, send close */
if (jsx->ibb_session) {
jabber_ibb_session_close(jsx->ibb_session);
@@ -1428,7 +1416,7 @@ static void jabber_si_xfer_send_disco_cb(JabberStream *js, const char *who,
JabberCapabilities capabilities, gpointer data)
{
PurpleXfer *xfer = (PurpleXfer *) data;
- JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
if (capabilities & JABBER_CAP_IBB) {
purple_debug_info("jabber",
@@ -1441,7 +1429,8 @@ static void jabber_si_xfer_send_disco_cb(JabberStream *js, const char *who,
} else {
char *msg = g_strdup_printf(_("Unable to send file to %s, user does not support file transfers"), who);
purple_notify_error(js->gc, _("File Send Failed"),
- _("File Send Failed"), msg);
+ _("File Send Failed"), msg,
+ purple_request_cpar_from_connection(js->gc));
g_free(msg);
purple_xfer_cancel_local(xfer);
}
@@ -1454,8 +1443,8 @@ static void resource_select_cancel_cb(PurpleXfer *xfer, PurpleRequestFields *fie
static void do_transfer_send(PurpleXfer *xfer, const char *resource)
{
- JabberSIXfer *jsx = xfer->data;
- char **who_v = g_strsplit(xfer->who, "/", 2);
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
+ char **who_v = g_strsplit(purple_xfer_get_remote_user(xfer), "/", 2);
char *who;
JabberBuddy *jb;
JabberBuddyResource *jbr = NULL;
@@ -1467,8 +1456,7 @@ static void do_transfer_send(PurpleXfer *xfer, const char *resource)
who = g_strdup_printf("%s/%s", who_v[0], resource);
g_strfreev(who_v);
- g_free(xfer->who);
- xfer->who = who;
+ purple_xfer_set_remote_user(xfer, who);
if (jbr && jabber_resource_know_capabilities(jbr)) {
char *msg;
@@ -1477,42 +1465,43 @@ static void do_transfer_send(PurpleXfer *xfer, const char *resource)
jsx->stream_method |= STREAM_METHOD_IBB;
if (jabber_resource_has_capability(jbr, NS_SI_FILE_TRANSFER)) {
jabber_si_xfer_send_request(xfer);
+ g_free(who);
return;
}
msg = g_strdup_printf(_("Unable to send file to %s, user does not support file transfers"), who);
purple_notify_error(jsx->js->gc, _("File Send Failed"),
- _("File Send Failed"), msg);
+ _("File Send Failed"), msg,
+ purple_request_cpar_from_connection(jsx->js->gc));
g_free(msg);
purple_xfer_cancel_local(xfer);
} else {
jabber_disco_info_do(jsx->js, who,
jabber_si_xfer_send_disco_cb, xfer);
}
+
+ g_free(who);
}
static void resource_select_ok_cb(PurpleXfer *xfer, PurpleRequestFields *fields)
{
PurpleRequestField *field = purple_request_fields_get_field(fields, "resource");
- int selected_id = purple_request_field_choice_get_value(field);
- GList *labels = purple_request_field_choice_get_labels(field);
-
- const char *selected_label = g_list_nth_data(labels, selected_id);
+ const char *selected_label = purple_request_field_choice_get_value(field);
do_transfer_send(xfer, selected_label);
}
static void jabber_si_xfer_init(PurpleXfer *xfer)
{
- JabberSIXfer *jsx = xfer->data;
+ JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
JabberIq *iq;
- if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
+ if(purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND) {
JabberBuddy *jb;
JabberBuddyResource *jbr = NULL;
char *resource;
GList *resources = NULL;
- if(NULL != (resource = jabber_get_resource(xfer->who))) {
+ if(NULL != (resource = jabber_get_resource(purple_xfer_get_remote_user(xfer)))) {
/* they've specified a resource, no need to ask or
* default or anything, just do it */
@@ -1521,7 +1510,7 @@ static void jabber_si_xfer_init(PurpleXfer *xfer)
return;
}
- jb = jabber_buddy_find(jsx->js, xfer->who, TRUE);
+ jb = jabber_buddy_find(jsx->js, purple_xfer_get_remote_user(xfer), TRUE);
if (jb) {
GList *l;
@@ -1545,14 +1534,16 @@ static void jabber_si_xfer_init(PurpleXfer *xfer)
char *msg;
if(!jb) {
- msg = g_strdup_printf(_("Unable to send file to %s, invalid JID"), xfer->who);
+ msg = g_strdup_printf(_("Unable to send file to %s, invalid JID"), purple_xfer_get_remote_user(xfer));
} else if(jb->subscription & JABBER_SUB_TO) {
- msg = g_strdup_printf(_("Unable to send file to %s, user is not online"), xfer->who);
+ msg = g_strdup_printf(_("Unable to send file to %s, user is not online"), purple_xfer_get_remote_user(xfer));
} else {
- msg = g_strdup_printf(_("Unable to send file to %s, not subscribed to user presence"), xfer->who);
+ msg = g_strdup_printf(_("Unable to send file to %s, not subscribed to user presence"), purple_xfer_get_remote_user(xfer));
}
- purple_notify_error(jsx->js->gc, _("File Send Failed"), _("File Send Failed"), msg);
+ purple_notify_error(jsx->js->gc, _("File Send Failed"),
+ _("File Send Failed"), msg,
+ purple_request_cpar_from_connection(jsx->js->gc));
g_free(msg);
} else if (g_list_length(resources) == 1) {
/* only 1 resource online (probably our most common case)
@@ -1562,14 +1553,16 @@ static void jabber_si_xfer_init(PurpleXfer *xfer)
} else {
/* we've got multiple resources, we need to pick one to send to */
GList *l;
- char *msg = g_strdup_printf(_("Please select the resource of %s to which you would like to send a file"), xfer->who);
+ char *msg = g_strdup_printf(_("Please select the resource of %s to which you would like to send a file"), purple_xfer_get_remote_user(xfer));
PurpleRequestFields *fields = purple_request_fields_new();
PurpleRequestField *field = purple_request_field_choice_new("resource", _("Resource"), 0);
PurpleRequestFieldGroup *group = purple_request_field_group_new(NULL);
+ purple_request_field_choice_set_data_destructor(field, g_free);
+
for(l = resources; l; l = l->next) {
jbr = l->data;
- purple_request_field_choice_add(field, jbr->name);
+ purple_request_field_choice_add(field, jbr->name, g_strdup(jbr->name));
}
purple_request_field_group_add_field(group, field);
@@ -1578,17 +1571,17 @@ static void jabber_si_xfer_init(PurpleXfer *xfer)
purple_request_fields(jsx->js->gc, _("Select a Resource"), msg, NULL, fields,
_("Send File"), G_CALLBACK(resource_select_ok_cb), _("Cancel"), G_CALLBACK(resource_select_cancel_cb),
- jsx->js->gc->account, xfer->who, NULL, xfer);
+ purple_request_cpar_from_connection(jsx->js->gc), xfer);
g_free(msg);
}
g_list_free(resources);
} else {
- xmlnode *si, *feature, *x, *field, *value;
+ PurpleXmlNode *si, *feature, *x, *field, *value;
iq = jabber_iq_new(jsx->js, JABBER_IQ_RESULT);
- xmlnode_set_attrib(iq->node, "to", xfer->who);
+ purple_xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer));
if(jsx->iq_id)
jabber_iq_set_id(iq, jsx->iq_id);
else
@@ -1596,17 +1589,17 @@ static void jabber_si_xfer_init(PurpleXfer *xfer)
jsx->accepted = TRUE;
- si = xmlnode_new_child(iq->node, "si");
- xmlnode_set_namespace(si, "http://jabber.org/protocol/si");
+ si = purple_xmlnode_new_child(iq->node, "si");
+ purple_xmlnode_set_namespace(si, "http://jabber.org/protocol/si");
- feature = xmlnode_new_child(si, "feature");
- xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
+ feature = purple_xmlnode_new_child(si, "feature");
+ purple_xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
- x = xmlnode_new_child(feature, "x");
- xmlnode_set_namespace(x, "jabber:x:data");
- xmlnode_set_attrib(x, "type", "submit");
- field = xmlnode_new_child(x, "field");
- xmlnode_set_attrib(field, "var", "stream-method");
+ x = purple_xmlnode_new_child(feature, "x");
+ purple_xmlnode_set_namespace(x, "jabber:x:data");
+ purple_xmlnode_set_attrib(x, "type", "submit");
+ field = purple_xmlnode_new_child(x, "field");
+ purple_xmlnode_set_attrib(field, "var", "stream-method");
/* we should maybe "remember" if bytestreams has failed before (in the
same session) with this JID, and only present IBB as an option to
@@ -1614,11 +1607,11 @@ static void jabber_si_xfer_init(PurpleXfer *xfer)
/* maybe we should have an account option to always just try IBB
for people who know their firewalls are very restrictive */
if (jsx->stream_method & STREAM_METHOD_BYTESTREAMS) {
- value = xmlnode_new_child(field, "value");
- xmlnode_insert_data(value, NS_BYTESTREAMS, -1);
+ value = purple_xmlnode_new_child(field, "value");
+ purple_xmlnode_insert_data(value, NS_BYTESTREAMS, -1);
} else if(jsx->stream_method & STREAM_METHOD_IBB) {
- value = xmlnode_new_child(field, "value");
- xmlnode_insert_data(value, NS_IBB, -1);
+ value = purple_xmlnode_new_child(field, "value");
+ purple_xmlnode_insert_data(value, NS_IBB, -1);
}
jabber_iq_send(iq);
@@ -1632,12 +1625,13 @@ PurpleXfer *jabber_si_new_xfer(PurpleConnection *gc, const char *who)
PurpleXfer *xfer;
JabberSIXfer *jsx;
- js = gc->proto_data;
+ js = purple_connection_get_protocol_data(gc);
- xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who);
+ xfer = purple_xfer_new(purple_connection_get_account(gc), PURPLE_XFER_TYPE_SEND, who);
if (xfer)
{
- xfer->data = jsx = g_new0(JabberSIXfer, 1);
+ jsx = g_new0(JabberSIXfer, 1);
+ purple_xfer_set_protocol_data(xfer, jsx);
jsx->js = js;
jsx->local_streamhost_fd = -1;
@@ -1683,54 +1677,37 @@ jabber_si_thumbnail_cb(JabberData *data, gchar *alt, gpointer userdata)
#endif
void jabber_si_parse(JabberStream *js, const char *from, JabberIqType type,
- const char *id, xmlnode *si)
+ const char *id, PurpleXmlNode *si)
{
JabberSIXfer *jsx;
PurpleXfer *xfer;
- xmlnode *file, *feature, *x, *field, *option, *value;
+ PurpleXmlNode *file, *feature, *x, *field, *option, *value;
#if ENABLE_FT_THUMBNAILS
- xmlnode *thumbnail;
+ PurpleXmlNode *thumbnail;
#endif
const char *stream_id, *filename, *filesize_c, *profile;
- guint64 filesize_64 = 0;
- size_t filesize = 0;
+ goffset filesize = 0;
- if(!(profile = xmlnode_get_attrib(si, "profile")) ||
+ if(!(profile = purple_xmlnode_get_attrib(si, "profile")) ||
strcmp(profile, NS_SI_FILE_TRANSFER))
return;
- if(!(stream_id = xmlnode_get_attrib(si, "id")))
+ if(!(stream_id = purple_xmlnode_get_attrib(si, "id")))
return;
- if(!(file = xmlnode_get_child(si, "file")))
+ if(!(file = purple_xmlnode_get_child(si, "file")))
return;
- if(!(filename = xmlnode_get_attrib(file, "name")))
+ if(!(filename = purple_xmlnode_get_attrib(file, "name")))
return;
- if((filesize_c = xmlnode_get_attrib(file, "size")))
- filesize_64 = g_ascii_strtoull(filesize_c, NULL, 10);
-
-#ifndef __COVERITY__
- /* TODO 3.0.0: When the core uses a guint64, this is redundant.
- * See #8477.
- *
- * It may not be necessary on 64-bit machine.
- * It raises result_independent_of_operands coverity false positive.
- */
- if (filesize_64 > G_MAXSIZE) {
- /* Should this pop up a warning? */
- purple_debug_warning("jabber", "Unable to transfer file (too large)"
- " -- see #8477 for more details.");
- return;
- }
-#endif
- filesize = filesize_64;
+ if((filesize_c = purple_xmlnode_get_attrib(file, "size")))
+ filesize = g_ascii_strtoull(filesize_c, NULL, 10);
- if(!(feature = xmlnode_get_child(si, "feature")))
+ if(!(feature = purple_xmlnode_get_child(si, "feature")))
return;
- if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data")))
+ if(!(x = purple_xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data")))
return;
if(!from)
@@ -1747,14 +1724,14 @@ void jabber_si_parse(JabberStream *js, const char *from, JabberIqType type,
jsx->ibb_session = NULL;
- for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
- const char *var = xmlnode_get_attrib(field, "var");
+ 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");
if(var && !strcmp(var, "stream-method")) {
- for(option = xmlnode_get_child(field, "option"); option;
- option = xmlnode_get_next_twin(option)) {
- if((value = xmlnode_get_child(option, "value"))) {
+ for(option = purple_xmlnode_get_child(field, "option"); option;
+ option = purple_xmlnode_get_next_twin(option)) {
+ if((value = purple_xmlnode_get_child(option, "value"))) {
char *val;
- if((val = xmlnode_get_data(value))) {
+ if((val = purple_xmlnode_get_data(value))) {
if(!strcmp(val, NS_BYTESTREAMS)) {
jsx->stream_method |= STREAM_METHOD_BYTESTREAMS;
} else if(!strcmp(val, NS_IBB)) {
@@ -1776,10 +1753,10 @@ void jabber_si_parse(JabberStream *js, const char *from, JabberIqType type,
jsx->stream_id = g_strdup(stream_id);
jsx->iq_id = g_strdup(id);
- xfer = purple_xfer_new(js->gc->account, PURPLE_XFER_RECEIVE, from);
+ xfer = purple_xfer_new(purple_connection_get_account(js->gc), PURPLE_XFER_TYPE_RECEIVE, from);
g_return_if_fail(xfer != NULL);
- xfer->data = jsx;
+ purple_xfer_set_protocol_data(xfer, jsx);
purple_xfer_set_filename(xfer, filename);
if(filesize > 0)
@@ -1794,9 +1771,9 @@ void jabber_si_parse(JabberStream *js, const char *from, JabberIqType type,
#if ENABLE_FT_THUMBNAILS
/* if there is a thumbnail, we should request it... */
- if ((thumbnail = xmlnode_get_child_with_namespace(file, "thumbnail",
+ if ((thumbnail = purple_xmlnode_get_child_with_namespace(file, "thumbnail",
NS_THUMBS))) {
- const char *cid = xmlnode_get_attrib(thumbnail, "cid");
+ 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);
diff --git a/libpurple/protocols/jabber/si.h b/libpurple/protocols/jabber/si.h
index 95794799e0..e8bf48e07c 100644
--- a/libpurple/protocols/jabber/si.h
+++ b/libpurple/protocols/jabber/si.h
@@ -24,14 +24,14 @@
#ifndef PURPLE_JABBER_SI_H_
#define PURPLE_JABBER_SI_H_
-#include "ft.h"
+#include "xfer.h"
#include "jabber.h"
void jabber_bytestreams_parse(JabberStream *js, const char *from,
- JabberIqType type, const char *id, xmlnode *query);
+ JabberIqType type, const char *id, PurpleXmlNode *query);
void jabber_si_parse(JabberStream *js, const char *from, JabberIqType type,
- const char *id, xmlnode *si);
+ const char *id, PurpleXmlNode *si);
PurpleXfer *jabber_si_new_xfer(PurpleConnection *gc, const char *who);
void jabber_si_xfer_send(PurpleConnection *gc, const char *who, const char *file);
void jabber_si_init(void);
diff --git a/libpurple/protocols/jabber/useravatar.c b/libpurple/protocols/jabber/useravatar.c
index a629de4516..2f48f8b5e5 100644
--- a/libpurple/protocols/jabber/useravatar.c
+++ b/libpurple/protocols/jabber/useravatar.c
@@ -23,13 +23,14 @@
#include "internal.h"
+#include "http.h"
#include "useravatar.h"
#include "pep.h"
#include "debug.h"
#define MAX_HTTP_BUDDYICON_BYTES (200 * 1024)
-static void update_buddy_metadata(JabberStream *js, const char *from, xmlnode *items);
+static void update_buddy_metadata(JabberStream *js, const char *from, PurpleXmlNode *items);
void jabber_avatar_init(void)
{
@@ -49,18 +50,18 @@ remove_avatar_0_12_nodes(JabberStream *js)
/* See note below for why this is #if 0'd */
/* Publish an empty avatar according to the XEP-0084 v0.12 semantics */
- xmlnode *publish, *item, *metadata;
+ PurpleXmlNode *publish, *item, *metadata;
/* publish the metadata */
- publish = xmlnode_new("publish");
- xmlnode_set_attrib(publish, "node", NS_AVATAR_0_12_METADATA);
+ publish = purple_xmlnode_new("publish");
+ purple_xmlnode_set_attrib(publish, "node", NS_AVATAR_0_12_METADATA);
- item = xmlnode_new_child(publish, "item");
- xmlnode_set_attrib(item, "id", "stop");
+ item = purple_xmlnode_new_child(publish, "item");
+ purple_xmlnode_set_attrib(item, "id", "stop");
- metadata = xmlnode_new_child(item, "metadata");
- xmlnode_set_namespace(metadata, NS_AVATAR_0_12_METADATA);
+ metadata = purple_xmlnode_new_child(item, "metadata");
+ purple_xmlnode_set_namespace(metadata, NS_AVATAR_0_12_METADATA);
- xmlnode_new_child(metadata, "stop");
+ purple_xmlnode_new_child(metadata, "stop");
/* publish */
jabber_pep_publish(js, publish);
@@ -86,9 +87,9 @@ remove_avatar_0_12_nodes(JabberStream *js)
jabber_pep_delete_node(js, NS_AVATAR_0_12_DATA);
}
-void jabber_avatar_set(JabberStream *js, PurpleStoredImage *img)
+void jabber_avatar_set(JabberStream *js, PurpleImage *img)
{
- xmlnode *publish, *metadata, *item;
+ PurpleXmlNode *publish, *metadata, *item;
if (!js->pep)
return;
@@ -97,12 +98,12 @@ void jabber_avatar_set(JabberStream *js, PurpleStoredImage *img)
remove_avatar_0_12_nodes(js);
if (!img) {
- publish = xmlnode_new("publish");
- xmlnode_set_attrib(publish, "node", NS_AVATAR_1_1_METADATA);
+ publish = purple_xmlnode_new("publish");
+ purple_xmlnode_set_attrib(publish, "node", NS_AVATAR_1_1_METADATA);
- item = xmlnode_new_child(publish, "item");
- metadata = xmlnode_new_child(item, "metadata");
- xmlnode_set_namespace(metadata, NS_AVATAR_1_1_METADATA);
+ item = purple_xmlnode_new_child(publish, "item");
+ metadata = purple_xmlnode_new_child(item, "metadata");
+ purple_xmlnode_set_namespace(metadata, NS_AVATAR_1_1_METADATA);
/* publish */
jabber_pep_publish(js, publish);
@@ -129,8 +130,8 @@ void jabber_avatar_set(JabberStream *js, PurpleStoredImage *img)
} ihdr;
} *png = NULL;
- if (purple_imgstore_get_size(img) > sizeof(*png))
- png = purple_imgstore_get_data(img);
+ if (purple_image_get_size(img) > sizeof(*png))
+ png = purple_image_get_data(img);
/* check if the data is a valid png file (well, at least to some extent) */
if(png && png->signature[0] == 0x89 &&
@@ -149,52 +150,53 @@ void jabber_avatar_set(JabberStream *js, PurpleStoredImage *img)
/* parse PNG header to get the size of the image (yes, this is required) */
guint32 width = ntohl(png->ihdr.width);
guint32 height = ntohl(png->ihdr.height);
- xmlnode *data, *info;
+ PurpleXmlNode *data, *info;
char *lengthstring, *widthstring, *heightstring;
/* compute the sha1 hash */
- char *hash = jabber_calculate_data_hash(purple_imgstore_get_data(img),
- purple_imgstore_get_size(img),
- "sha1");
- char *base64avatar = purple_base64_encode(purple_imgstore_get_data(img),
- purple_imgstore_get_size(img));
+ char *hash = jabber_calculate_data_hash(
+ purple_image_get_data(img),
+ purple_image_get_size(img), "sha1");
+ char *base64avatar = purple_base64_encode(
+ purple_image_get_data(img),
+ purple_image_get_size(img));
- publish = xmlnode_new("publish");
- xmlnode_set_attrib(publish, "node", NS_AVATAR_1_1_DATA);
+ publish = purple_xmlnode_new("publish");
+ purple_xmlnode_set_attrib(publish, "node", NS_AVATAR_1_1_DATA);
- item = xmlnode_new_child(publish, "item");
- xmlnode_set_attrib(item, "id", hash);
+ item = purple_xmlnode_new_child(publish, "item");
+ purple_xmlnode_set_attrib(item, "id", hash);
- data = xmlnode_new_child(item, "data");
- xmlnode_set_namespace(data, NS_AVATAR_1_1_DATA);
+ data = purple_xmlnode_new_child(item, "data");
+ purple_xmlnode_set_namespace(data, NS_AVATAR_1_1_DATA);
- xmlnode_insert_data(data, base64avatar, -1);
+ purple_xmlnode_insert_data(data, base64avatar, -1);
/* publish the avatar itself */
jabber_pep_publish(js, publish);
g_free(base64avatar);
lengthstring = g_strdup_printf("%" G_GSIZE_FORMAT,
- purple_imgstore_get_size(img));
+ purple_image_get_size(img));
widthstring = g_strdup_printf("%u", width);
heightstring = g_strdup_printf("%u", height);
/* publish the metadata */
- publish = xmlnode_new("publish");
- xmlnode_set_attrib(publish, "node", NS_AVATAR_1_1_METADATA);
+ publish = purple_xmlnode_new("publish");
+ purple_xmlnode_set_attrib(publish, "node", NS_AVATAR_1_1_METADATA);
- item = xmlnode_new_child(publish, "item");
- xmlnode_set_attrib(item, "id", hash);
+ item = purple_xmlnode_new_child(publish, "item");
+ purple_xmlnode_set_attrib(item, "id", hash);
- metadata = xmlnode_new_child(item, "metadata");
- xmlnode_set_namespace(metadata, NS_AVATAR_1_1_METADATA);
+ metadata = purple_xmlnode_new_child(item, "metadata");
+ purple_xmlnode_set_namespace(metadata, NS_AVATAR_1_1_METADATA);
- info = xmlnode_new_child(metadata, "info");
- xmlnode_set_attrib(info, "id", hash);
- xmlnode_set_attrib(info, "type", "image/png");
- xmlnode_set_attrib(info, "bytes", lengthstring);
- xmlnode_set_attrib(info, "width", widthstring);
- xmlnode_set_attrib(info, "height", heightstring);
+ info = purple_xmlnode_new_child(metadata, "info");
+ purple_xmlnode_set_attrib(info, "id", hash);
+ purple_xmlnode_set_attrib(info, "type", "image/png");
+ purple_xmlnode_set_attrib(info, "bytes", lengthstring);
+ purple_xmlnode_set_attrib(info, "width", widthstring);
+ purple_xmlnode_set_attrib(info, "height", heightstring);
jabber_pep_publish(js, publish);
@@ -209,7 +211,7 @@ void jabber_avatar_set(JabberStream *js, PurpleStoredImage *img)
}
static void
-do_got_own_avatar_0_12_cb(JabberStream *js, const char *from, xmlnode *items)
+do_got_own_avatar_0_12_cb(JabberStream *js, const char *from, PurpleXmlNode *items)
{
if (items)
/* It wasn't an error (i.e. 'item-not-found') */
@@ -217,16 +219,16 @@ do_got_own_avatar_0_12_cb(JabberStream *js, const char *from, xmlnode *items)
}
static void
-do_got_own_avatar_cb(JabberStream *js, const char *from, xmlnode *items)
+do_got_own_avatar_cb(JabberStream *js, const char *from, PurpleXmlNode *items)
{
- xmlnode *item = NULL, *metadata = NULL, *info = NULL;
+ PurpleXmlNode *item = NULL, *metadata = NULL, *info = NULL;
PurpleAccount *account = purple_connection_get_account(js->gc);
const char *server_hash = NULL;
- if (items && (item = xmlnode_get_child(items, "item")) &&
- (metadata = xmlnode_get_child(item, "metadata")) &&
- (info = xmlnode_get_child(metadata, "info"))) {
- server_hash = xmlnode_get_attrib(info, "id");
+ if (items && (item = purple_xmlnode_get_child(items, "item")) &&
+ (metadata = purple_xmlnode_get_child(item, "metadata")) &&
+ (info = purple_xmlnode_get_child(metadata, "info"))) {
+ server_hash = purple_xmlnode_get_attrib(info, "id");
}
/*
@@ -236,9 +238,10 @@ do_got_own_avatar_cb(JabberStream *js, const char *from, xmlnode *items)
*/
if ((!items || !metadata) ||
!purple_strequal(server_hash, js->initial_avatar_hash)) {
- PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account);
+ PurpleImage *img = purple_buddy_icons_find_account_icon(account);
jabber_avatar_set(js, img);
- purple_imgstore_unref(img);
+ if (img)
+ g_object_unref(img);
}
}
@@ -259,22 +262,24 @@ typedef struct _JabberBuddyAvatarUpdateURLInfo {
} JabberBuddyAvatarUpdateURLInfo;
static void
-do_buddy_avatar_update_fromurl(PurpleUtilFetchUrlData *url_data,
- gpointer user_data, const gchar *url_text,
- gsize len, const gchar *error_message)
+do_buddy_avatar_update_fromurl(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _info)
{
- JabberBuddyAvatarUpdateURLInfo *info = user_data;
+ JabberBuddyAvatarUpdateURLInfo *info = _info;
gpointer icon_data;
+ const gchar *got_data;
+ size_t got_len;
- if(!url_text) {
- purple_debug_error("jabber",
- "do_buddy_avatar_update_fromurl got error \"%s\"",
- error_message);
+ if (!purple_http_response_is_successful(response)) {
+ purple_debug_error("jabber", "do_buddy_avatar_update_fromurl "
+ "got error \"%s\"",
+ purple_http_response_get_error(response));
goto out;
}
- icon_data = g_memdup(url_text, len);
- purple_buddy_icons_set_for_user(purple_connection_get_account(info->js->gc), info->from, icon_data, len, info->id);
+ got_data = purple_http_response_get_data(response, &got_len);
+ icon_data = g_memdup(got_data, got_len);
+ purple_buddy_icons_set_for_user(purple_connection_get_account(info->js->gc), info->from, icon_data, got_len, info->id);
out:
g_free(info->from);
@@ -283,9 +288,9 @@ out:
}
static void
-do_buddy_avatar_update_data(JabberStream *js, const char *from, xmlnode *items)
+do_buddy_avatar_update_data(JabberStream *js, const char *from, PurpleXmlNode *items)
{
- xmlnode *item, *data;
+ PurpleXmlNode *item, *data;
const char *checksum;
char *b64data;
void *img;
@@ -293,19 +298,19 @@ do_buddy_avatar_update_data(JabberStream *js, const char *from, xmlnode *items)
if(!items)
return;
- item = xmlnode_get_child(items, "item");
+ item = purple_xmlnode_get_child(items, "item");
if(!item)
return;
- data = xmlnode_get_child(item, "data");
+ data = purple_xmlnode_get_child(item, "data");
if(!data)
return;
- checksum = xmlnode_get_attrib(item,"id");
+ checksum = purple_xmlnode_get_attrib(item,"id");
if(!checksum)
return;
- b64data = xmlnode_get_data(data);
+ b64data = purple_xmlnode_get_data(data);
if(!b64data)
return;
@@ -320,41 +325,41 @@ do_buddy_avatar_update_data(JabberStream *js, const char *from, xmlnode *items)
}
static void
-update_buddy_metadata(JabberStream *js, const char *from, xmlnode *items)
+update_buddy_metadata(JabberStream *js, const char *from, PurpleXmlNode *items)
{
- PurpleBuddy *buddy = purple_find_buddy(purple_connection_get_account(js->gc), from);
+ PurpleBuddy *buddy = purple_blist_find_buddy(purple_connection_get_account(js->gc), from);
const char *checksum;
- xmlnode *item, *metadata;
+ PurpleXmlNode *item, *metadata;
if(!buddy)
return;
if (!items)
return;
- item = xmlnode_get_child(items,"item");
+ item = purple_xmlnode_get_child(items,"item");
if (!item)
return;
- metadata = xmlnode_get_child(item, "metadata");
+ metadata = purple_xmlnode_get_child(item, "metadata");
if(!metadata)
return;
checksum = purple_buddy_icons_get_checksum_for_user(buddy);
/* <stop/> was the pre-v1.1 method of publishing an empty avatar */
- if(xmlnode_get_child(metadata, "stop")) {
+ if(purple_xmlnode_get_child(metadata, "stop")) {
purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, NULL, 0, NULL);
} else {
- xmlnode *info, *goodinfo = NULL;
+ PurpleXmlNode *info, *goodinfo = NULL;
gboolean has_children = FALSE;
/* iterate over all info nodes to get one we can use */
for(info = metadata->child; info; info = info->next) {
- if(info->type == XMLNODE_TYPE_TAG)
+ if(info->type == PURPLE_XMLNODE_TYPE_TAG)
has_children = TRUE;
- if(info->type == XMLNODE_TYPE_TAG && !strcmp(info->name,"info")) {
- const char *type = xmlnode_get_attrib(info,"type");
- const char *id = xmlnode_get_attrib(info,"id");
+ if(info->type == PURPLE_XMLNODE_TYPE_TAG && !strcmp(info->name,"info")) {
+ const char *type = purple_xmlnode_get_attrib(info,"type");
+ const char *id = purple_xmlnode_get_attrib(info,"id");
if(checksum && id && !strcmp(id, checksum)) {
/* we already have that avatar, so we don't have to do anything */
@@ -369,28 +374,27 @@ update_buddy_metadata(JabberStream *js, const char *from, xmlnode *items)
if(has_children == FALSE) {
purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, NULL, 0, NULL);
} else if(goodinfo) {
- const char *url = xmlnode_get_attrib(goodinfo, "url");
- const char *id = xmlnode_get_attrib(goodinfo,"id");
+ const char *url = purple_xmlnode_get_attrib(goodinfo, "url");
+ const char *id = purple_xmlnode_get_attrib(goodinfo,"id");
/* the avatar might either be stored in a pep node, or on a HTTP(S) URL */
if(!url) {
jabber_pep_request_item(js, from, NS_AVATAR_1_1_DATA, id,
do_buddy_avatar_update_data);
} else {
- PurpleUtilFetchUrlData *url_data;
+ PurpleHttpRequest *req;
JabberBuddyAvatarUpdateURLInfo *info = g_new0(JabberBuddyAvatarUpdateURLInfo, 1);
info->js = js;
- url_data = purple_util_fetch_url_len(url, TRUE, NULL, TRUE,
- MAX_HTTP_BUDDYICON_BYTES,
- do_buddy_avatar_update_fromurl, info);
- if (url_data) {
- info->from = g_strdup(from);
- info->id = g_strdup(id);
- js->url_datas = g_slist_prepend(js->url_datas, url_data);
- } else
- g_free(info);
+ req = purple_http_request_new(url);
+ purple_http_request_set_max_len(req, MAX_HTTP_BUDDYICON_BYTES);
+ purple_http_connection_set_add(js->http_conns,
+ purple_http_request(js->gc, req,
+ do_buddy_avatar_update_fromurl, info));
+ purple_http_request_unref(req);
+ info->from = g_strdup(from);
+ info->id = g_strdup(id);
}
}
}
diff --git a/libpurple/protocols/jabber/useravatar.h b/libpurple/protocols/jabber/useravatar.h
index 080e279b3c..b18080c508 100644
--- a/libpurple/protocols/jabber/useravatar.h
+++ b/libpurple/protocols/jabber/useravatar.h
@@ -25,12 +25,12 @@
#define _PURPLE_JABBER_USERAVATAR_H_
#include "jabber.h"
-#include "imgstore.h"
+#include "image.h"
/* Implementation of XEP-0084 */
void jabber_avatar_init(void);
-void jabber_avatar_set(JabberStream *js, PurpleStoredImage *img);
+void jabber_avatar_set(JabberStream *js, PurpleImage *img);
void jabber_avatar_fetch_mine(JabberStream *js);
diff --git a/libpurple/protocols/jabber/usermood.c b/libpurple/protocols/jabber/usermood.c
index 6d9b30f6af..5c4b196b6d 100644
--- a/libpurple/protocols/jabber/usermood.c
+++ b/libpurple/protocols/jabber/usermood.c
@@ -115,50 +115,64 @@ static PurpleMood moods[] = {
{"undefined", N_("Undefined"), NULL},
{"weak", N_("Weak"), NULL},
{"worried", N_("Worried"), NULL},
- /* Mark the last record. */
+ /* Mar last record. */
{NULL, NULL, NULL}
};
-static void jabber_mood_cb(JabberStream *js, const char *from, xmlnode *items) {
+static const PurpleMood*
+find_mood_by_name(const gchar *name)
+{
+ int i;
+
+ g_return_val_if_fail(name && *name, NULL);
+
+ for (i = 0; moods[i].mood != NULL; ++i) {
+ if (g_str_equal(name, moods[i].mood)) {
+ return &moods[i];
+ }
+ }
+
+ return NULL;
+}
+
+static void jabber_mood_cb(JabberStream *js, const char *from, PurpleXmlNode *items) {
/* it doesn't make sense to have more than one item here, so let's just pick the first one */
- xmlnode *item = xmlnode_get_child(items, "item");
+ PurpleXmlNode *item = purple_xmlnode_get_child(items, "item");
const char *newmood = NULL;
char *moodtext = NULL;
JabberBuddy *buddy = jabber_buddy_find(js, from, FALSE);
- xmlnode *moodinfo, *mood;
+ PurpleXmlNode *moodinfo, *mood;
/* ignore the mood of people not on our buddy list */
if (!buddy || !item)
return;
- mood = xmlnode_get_child_with_namespace(item, "mood", "http://jabber.org/protocol/mood");
+ mood = purple_xmlnode_get_child_with_namespace(item, "mood", "http://jabber.org/protocol/mood");
if (!mood)
return;
for (moodinfo = mood->child; moodinfo; moodinfo = moodinfo->next) {
- if (moodinfo->type == XMLNODE_TYPE_TAG) {
+ if (moodinfo->type == PURPLE_XMLNODE_TYPE_TAG) {
if (!strcmp(moodinfo->name, "text")) {
if (!moodtext) /* only pick the first one */
- moodtext = xmlnode_get_data(moodinfo);
+ moodtext = purple_xmlnode_get_data(moodinfo);
} else {
- int i;
- for (i = 0; moods[i].mood; ++i) {
- /* verify that the mood is known (valid) */
- if (!strcmp(moodinfo->name, moods[i].mood)) {
- newmood = moods[i].mood;
- break;
- }
- }
+ const PurpleMood *target_mood;
+
+ /* verify that the mood is known (valid) */
+ target_mood = find_mood_by_name(moodinfo->name);
+ newmood = target_mood ? target_mood->mood : NULL;
}
+
}
if (newmood != NULL && moodtext != NULL)
break;
}
if (newmood != NULL) {
- purple_prpl_got_user_status(js->gc->account, from, "mood",
+ purple_prpl_got_user_status(purple_connection_get_account(js->gc), from, "mood",
PURPLE_MOOD_NAME, newmood,
PURPLE_MOOD_COMMENT, moodtext,
NULL);
} else {
- purple_prpl_got_user_status_deactive(js->gc->account, from, "mood");
+ purple_prpl_got_user_status_deactive(purple_connection_get_account(js->gc), from, "mood");
}
g_free(moodtext);
}
@@ -168,26 +182,41 @@ void jabber_mood_init(void) {
jabber_pep_register_handler("http://jabber.org/protocol/mood", jabber_mood_cb);
}
-void jabber_mood_set(JabberStream *js, const char *mood, const char *text) {
- xmlnode *publish, *moodnode;
+gboolean
+jabber_mood_set(JabberStream *js, const char *mood, const char *text)
+{
+ const PurpleMood *target_mood = NULL;
+ PurpleXmlNode *publish, *moodnode;
- publish = xmlnode_new("publish");
- xmlnode_set_attrib(publish,"node","http://jabber.org/protocol/mood");
- moodnode = xmlnode_new_child(xmlnode_new_child(publish, "item"), "mood");
- xmlnode_set_namespace(moodnode, "http://jabber.org/protocol/mood");
if (mood && *mood) {
- /* if mood is NULL, set an empty mood node, meaning: unset mood */
- xmlnode_new_child(moodnode, mood);
+ target_mood = find_mood_by_name(mood);
+ /* Mood specified, but is invalid --
+ * fail so that the command can handle this.
+ */
+ if (!target_mood)
+ return FALSE;
}
- if (text && *text) {
- xmlnode *textnode = xmlnode_new_child(moodnode, "text");
- xmlnode_insert_data(textnode, text, -1);
+ publish = purple_xmlnode_new("publish");
+ purple_xmlnode_set_attrib(publish,"node","http://jabber.org/protocol/mood");
+ moodnode = purple_xmlnode_new_child(purple_xmlnode_new_child(publish, "item"), "mood");
+ purple_xmlnode_set_namespace(moodnode, "http://jabber.org/protocol/mood");
+
+ if (target_mood) {
+ /* If target_mood is not NULL, then
+ * target_mood->mood == mood, and is a valid element name.
+ */
+ purple_xmlnode_new_child(moodnode, mood);
+
+ /* Only set text when setting a mood */
+ if (text && *text) {
+ PurpleXmlNode *textnode = purple_xmlnode_new_child(moodnode, "text");
+ purple_xmlnode_insert_data(textnode, text, -1);
+ }
}
jabber_pep_publish(js, publish);
- /* publish is freed by jabber_pep_publish -> jabber_iq_send -> jabber_iq_free
- (yay for well-defined memory management rules) */
+ return TRUE;
}
PurpleMood *jabber_get_moods(PurpleAccount *account)
diff --git a/libpurple/protocols/jabber/usermood.h b/libpurple/protocols/jabber/usermood.h
index ad90ce3b1d..eced59f116 100644
--- a/libpurple/protocols/jabber/usermood.h
+++ b/libpurple/protocols/jabber/usermood.h
@@ -30,9 +30,20 @@
void jabber_mood_init(void);
-void jabber_mood_set(JabberStream *js,
- const char *mood, /* must be one of the valid strings defined in the XEP */
- const char *text /* might be NULL */);
+/**
+ * Sets / unsets the mood for the specified account. The mood passed in
+ * must either be NULL, "", or one of the moods returned by
+ * jabber_get_moods().
+ *
+ * @param js The JabberStream object.
+ * @param mood The mood to set, NULL, or ""
+ * @param text Optional text that goes along with a mood. Only used when
+ * setting a mood (not when unsetting a mood).
+ *
+ * @return FALSE if an invalid mood was specified, or TRUE otherwise.
+ */
+gboolean
+jabber_mood_set(JabberStream *js, const char *mood, const char *text);
PurpleMood *jabber_get_moods(PurpleAccount *account);
diff --git a/libpurple/protocols/jabber/usernick.c b/libpurple/protocols/jabber/usernick.c
index dc3e7e4e1d..92f64d7302 100644
--- a/libpurple/protocols/jabber/usernick.c
+++ b/libpurple/protocols/jabber/usernick.c
@@ -30,58 +30,58 @@
#include "request.h"
#include "status.h"
-static void jabber_nick_cb(JabberStream *js, const char *from, xmlnode *items) {
+static void jabber_nick_cb(JabberStream *js, const char *from, PurpleXmlNode *items) {
/* it doesn't make sense to have more than one item here, so let's just pick the first one */
- xmlnode *item = xmlnode_get_child(items, "item");
+ PurpleXmlNode *item = purple_xmlnode_get_child(items, "item");
JabberBuddy *buddy = jabber_buddy_find(js, from, FALSE);
- xmlnode *nick;
+ PurpleXmlNode *nick;
char *nickname = NULL;
/* ignore the nick of people not on our buddy list */
if (!buddy || !item)
return;
- nick = xmlnode_get_child_with_namespace(item, "nick", "http://jabber.org/protocol/nick");
+ nick = purple_xmlnode_get_child_with_namespace(item, "nick", "http://jabber.org/protocol/nick");
if (!nick)
return;
- nickname = xmlnode_get_data(nick);
- serv_got_alias(js->gc, from, nickname);
+ nickname = purple_xmlnode_get_data(nick);
+ purple_serv_got_alias(js->gc, from, nickname);
g_free(nickname);
}
static void do_nick_set(JabberStream *js, const char *nick) {
- xmlnode *publish, *nicknode;
+ PurpleXmlNode *publish, *nicknode;
- publish = xmlnode_new("publish");
- xmlnode_set_attrib(publish,"node","http://jabber.org/protocol/nick");
- nicknode = xmlnode_new_child(xmlnode_new_child(publish, "item"), "nick");
- xmlnode_set_namespace(nicknode, "http://jabber.org/protocol/nick");
+ publish = purple_xmlnode_new("publish");
+ purple_xmlnode_set_attrib(publish,"node","http://jabber.org/protocol/nick");
+ nicknode = purple_xmlnode_new_child(purple_xmlnode_new_child(publish, "item"), "nick");
+ purple_xmlnode_set_namespace(nicknode, "http://jabber.org/protocol/nick");
if(nick && nick[0] != '\0')
- xmlnode_insert_data(nicknode, nick, -1);
+ purple_xmlnode_insert_data(nicknode, nick, -1);
jabber_pep_publish(js, publish);
/* publish is freed by jabber_pep_publish -> jabber_iq_send -> jabber_iq_free
(yay for well-defined memory management rules) */
}
-static void do_nick_got_own_nick_cb(JabberStream *js, const char *from, xmlnode *items) {
+static void do_nick_got_own_nick_cb(JabberStream *js, const char *from, PurpleXmlNode *items) {
char *oldnickname = NULL;
- xmlnode *item = NULL;
+ PurpleXmlNode *item = NULL;
if (items)
- item = xmlnode_get_child(items,"item");
+ item = purple_xmlnode_get_child(items,"item");
if(item) {
- xmlnode *nick = xmlnode_get_child_with_namespace(item,"nick","http://jabber.org/protocol/nick");
+ PurpleXmlNode *nick = purple_xmlnode_get_child_with_namespace(item,"nick","http://jabber.org/protocol/nick");
if(nick)
- oldnickname = xmlnode_get_data(nick);
+ oldnickname = purple_xmlnode_get_data(nick);
}
purple_request_input(js->gc, _("Set User Nickname"), _("Please specify a new nickname for you."),
_("This information is visible to all contacts on your contact list, so choose something appropriate."),
oldnickname, FALSE, FALSE, NULL, _("Set"), PURPLE_CALLBACK(do_nick_set), _("Cancel"), NULL,
- purple_connection_get_account(js->gc), NULL, NULL, js);
+ purple_request_cpar_from_connection(js->gc), js);
g_free(oldnickname);
}
diff --git a/libpurple/protocols/jabber/usertune.c b/libpurple/protocols/jabber/usertune.c
index 03e485ef1b..cb3d08eb30 100644
--- a/libpurple/protocols/jabber/usertune.c
+++ b/libpurple/protocols/jabber/usertune.c
@@ -30,11 +30,11 @@
#include "request.h"
#include "status.h"
-static void jabber_tune_cb(JabberStream *js, const char *from, xmlnode *items) {
+static void jabber_tune_cb(JabberStream *js, const char *from, PurpleXmlNode *items) {
/* it doesn't make sense to have more than one item here, so let's just pick the first one */
- xmlnode *item = xmlnode_get_child(items, "item");
+ PurpleXmlNode *item = purple_xmlnode_get_child(items, "item");
JabberBuddy *buddy = jabber_buddy_find(js, from, FALSE);
- xmlnode *tuneinfo, *tune;
+ PurpleXmlNode *tuneinfo, *tune;
PurpleJabberTuneInfo tuneinfodata;
JabberBuddyResource *resource;
gboolean valid = FALSE;
@@ -50,21 +50,21 @@ static void jabber_tune_cb(JabberStream *js, const char *from, xmlnode *items) {
tuneinfodata.time = -1;
tuneinfodata.url = NULL;
- tune = xmlnode_get_child_with_namespace(item, "tune", "http://jabber.org/protocol/tune");
+ tune = purple_xmlnode_get_child_with_namespace(item, "tune", "http://jabber.org/protocol/tune");
if (!tune)
return;
resource = jabber_buddy_find_resource(buddy, NULL);
if(!resource)
return; /* huh? */
for (tuneinfo = tune->child; tuneinfo; tuneinfo = tuneinfo->next) {
- if (tuneinfo->type == XMLNODE_TYPE_TAG) {
+ if (tuneinfo->type == PURPLE_XMLNODE_TYPE_TAG) {
if (!strcmp(tuneinfo->name, "artist")) {
if (tuneinfodata.artist == NULL) /* only pick the first one */
- tuneinfodata.artist = xmlnode_get_data(tuneinfo);
+ tuneinfodata.artist = purple_xmlnode_get_data(tuneinfo);
valid = TRUE;
} else if (!strcmp(tuneinfo->name, "length")) {
if (tuneinfodata.time == -1) {
- char *length = xmlnode_get_data(tuneinfo);
+ char *length = purple_xmlnode_get_data(tuneinfo);
if (length)
tuneinfodata.time = strtol(length, NULL, 10);
g_free(length);
@@ -73,26 +73,26 @@ static void jabber_tune_cb(JabberStream *js, const char *from, xmlnode *items) {
}
} else if (!strcmp(tuneinfo->name, "source")) {
if (tuneinfodata.album == NULL) /* only pick the first one */
- tuneinfodata.album = xmlnode_get_data(tuneinfo);
+ tuneinfodata.album = purple_xmlnode_get_data(tuneinfo);
valid = TRUE;
} else if (!strcmp(tuneinfo->name, "title")) {
if (tuneinfodata.title == NULL) /* only pick the first one */
- tuneinfodata.title = xmlnode_get_data(tuneinfo);
+ tuneinfodata.title = purple_xmlnode_get_data(tuneinfo);
valid = TRUE;
} else if (!strcmp(tuneinfo->name, "track")) {
if (tuneinfodata.track == NULL) /* only pick the first one */
- tuneinfodata.track = xmlnode_get_data(tuneinfo);
+ tuneinfodata.track = purple_xmlnode_get_data(tuneinfo);
valid = TRUE;
} else if (!strcmp(tuneinfo->name, "uri")) {
if (tuneinfodata.url == NULL) /* only pick the first one */
- tuneinfodata.url = xmlnode_get_data(tuneinfo);
+ tuneinfodata.url = purple_xmlnode_get_data(tuneinfo);
valid = TRUE;
}
}
}
if (valid) {
- purple_prpl_got_user_status(js->gc->account, from, "tune",
+ purple_prpl_got_user_status(purple_connection_get_account(js->gc), from, "tune",
PURPLE_TUNE_ARTIST, tuneinfodata.artist,
PURPLE_TUNE_TITLE, tuneinfodata.title,
PURPLE_TUNE_ALBUM, tuneinfodata.album,
@@ -100,7 +100,7 @@ static void jabber_tune_cb(JabberStream *js, const char *from, xmlnode *items) {
PURPLE_TUNE_TIME, tuneinfodata.time,
PURPLE_TUNE_URL, tuneinfodata.url, NULL);
} else {
- purple_prpl_got_user_status_deactive(js->gc->account, from, "tune");
+ purple_prpl_got_user_status_deactive(purple_connection_get_account(js->gc), from, "tune");
}
g_free(tuneinfodata.artist);
@@ -116,30 +116,30 @@ void jabber_tune_init(void) {
}
void jabber_tune_set(PurpleConnection *gc, const PurpleJabberTuneInfo *tuneinfo) {
- xmlnode *publish, *tunenode;
- JabberStream *js = gc->proto_data;
+ PurpleXmlNode *publish, *tunenode;
+ JabberStream *js = purple_connection_get_protocol_data(gc);
- publish = xmlnode_new("publish");
- xmlnode_set_attrib(publish,"node","http://jabber.org/protocol/tune");
- tunenode = xmlnode_new_child(xmlnode_new_child(publish, "item"), "tune");
- xmlnode_set_namespace(tunenode, "http://jabber.org/protocol/tune");
+ publish = purple_xmlnode_new("publish");
+ purple_xmlnode_set_attrib(publish,"node","http://jabber.org/protocol/tune");
+ tunenode = purple_xmlnode_new_child(purple_xmlnode_new_child(publish, "item"), "tune");
+ purple_xmlnode_set_namespace(tunenode, "http://jabber.org/protocol/tune");
if(tuneinfo) {
if(tuneinfo->artist && tuneinfo->artist[0] != '\0')
- xmlnode_insert_data(xmlnode_new_child(tunenode, "artist"),tuneinfo->artist,-1);
+ purple_xmlnode_insert_data(purple_xmlnode_new_child(tunenode, "artist"),tuneinfo->artist,-1);
if(tuneinfo->title && tuneinfo->title[0] != '\0')
- xmlnode_insert_data(xmlnode_new_child(tunenode, "title"),tuneinfo->title,-1);
+ purple_xmlnode_insert_data(purple_xmlnode_new_child(tunenode, "title"),tuneinfo->title,-1);
if(tuneinfo->album && tuneinfo->album[0] != '\0')
- xmlnode_insert_data(xmlnode_new_child(tunenode, "source"),tuneinfo->album,-1);
+ purple_xmlnode_insert_data(purple_xmlnode_new_child(tunenode, "source"),tuneinfo->album,-1);
if(tuneinfo->url && tuneinfo->url[0] != '\0')
- xmlnode_insert_data(xmlnode_new_child(tunenode, "uri"),tuneinfo->url,-1);
+ purple_xmlnode_insert_data(purple_xmlnode_new_child(tunenode, "uri"),tuneinfo->url,-1);
if(tuneinfo->time > 0) {
char *length = g_strdup_printf("%d", tuneinfo->time);
- xmlnode_insert_data(xmlnode_new_child(tunenode, "length"),length,-1);
+ purple_xmlnode_insert_data(purple_xmlnode_new_child(tunenode, "length"),length,-1);
g_free(length);
}
if(tuneinfo->track && tuneinfo->track[0] != '\0')
- xmlnode_insert_data(xmlnode_new_child(tunenode, "track"),tuneinfo->track,-1);
+ purple_xmlnode_insert_data(purple_xmlnode_new_child(tunenode, "track"),tuneinfo->track,-1);
}
jabber_pep_publish(js, publish);
diff --git a/libpurple/protocols/jabber/xdata.c b/libpurple/protocols/jabber/xdata.c
index e681694396..140eb879f5 100644
--- a/libpurple/protocols/jabber/xdata.c
+++ b/libpurple/protocols/jabber/xdata.c
@@ -47,7 +47,7 @@ struct jabber_x_data_data {
};
static void jabber_x_data_ok_cb(struct jabber_x_data_data *data, PurpleRequestFields *fields) {
- xmlnode *result = xmlnode_new("x");
+ PurpleXmlNode *result = purple_xmlnode_new("x");
jabber_x_data_action_cb cb = data->cb;
gpointer user_data = data->user_data;
JabberStream *js = data->js;
@@ -55,8 +55,8 @@ static void jabber_x_data_ok_cb(struct jabber_x_data_data *data, PurpleRequestFi
char *actionhandle = NULL;
gboolean hasActions = (data->actions != NULL);
- xmlnode_set_namespace(result, "jabber:x:data");
- xmlnode_set_attrib(result, "type", "submit");
+ purple_xmlnode_set_namespace(result, "jabber:x:data");
+ purple_xmlnode_set_attrib(result, "type", "submit");
for(groups = purple_request_fields_get_groups(fields); groups; groups = groups->next) {
if(groups->data == data->actiongroup) {
@@ -66,14 +66,14 @@ static void jabber_x_data_ok_cb(struct jabber_x_data_data *data, PurpleRequestFi
int handleindex;
if(strcmp(id, "libpurple:jabber:xdata:actions"))
continue;
- handleindex = purple_request_field_choice_get_value(field);
+ handleindex = GPOINTER_TO_INT(purple_request_field_choice_get_value(field));
actionhandle = g_strdup(g_list_nth_data(data->actions, handleindex));
break;
}
continue;
}
for(flds = purple_request_field_group_get_fields(groups->data); flds; flds = flds->next) {
- xmlnode *fieldnode, *valuenode;
+ PurpleXmlNode *fieldnode, *valuenode;
PurpleRequestField *field = flds->data;
const char *id = purple_request_field_get_id(field);
jabber_x_data_field_type type = GPOINTER_TO_INT(g_hash_table_lookup(data->fields, id));
@@ -85,11 +85,11 @@ static void jabber_x_data_ok_cb(struct jabber_x_data_data *data, PurpleRequestFi
const char *value = purple_request_field_string_get_value(field);
if (value == NULL)
break;
- fieldnode = xmlnode_new_child(result, "field");
- xmlnode_set_attrib(fieldnode, "var", id);
- valuenode = xmlnode_new_child(fieldnode, "value");
+ fieldnode = purple_xmlnode_new_child(result, "field");
+ purple_xmlnode_set_attrib(fieldnode, "var", id);
+ valuenode = purple_xmlnode_new_child(fieldnode, "value");
if(value)
- xmlnode_insert_data(valuenode, value, -1);
+ purple_xmlnode_insert_data(valuenode, value, -1);
break;
}
case JABBER_X_DATA_TEXT_MULTI:
@@ -98,13 +98,13 @@ static void jabber_x_data_ok_cb(struct jabber_x_data_data *data, PurpleRequestFi
const char *value = purple_request_field_string_get_value(field);
if (value == NULL)
break;
- fieldnode = xmlnode_new_child(result, "field");
- xmlnode_set_attrib(fieldnode, "var", id);
+ fieldnode = purple_xmlnode_new_child(result, "field");
+ purple_xmlnode_set_attrib(fieldnode, "var", id);
pieces = g_strsplit(value, "\n", -1);
for(p = pieces; *p != NULL; p++) {
- valuenode = xmlnode_new_child(fieldnode, "value");
- xmlnode_insert_data(valuenode, *p, -1);
+ valuenode = purple_xmlnode_new_child(fieldnode, "value");
+ purple_xmlnode_insert_data(valuenode, *p, -1);
}
g_strfreev(pieces);
}
@@ -114,26 +114,26 @@ static void jabber_x_data_ok_cb(struct jabber_x_data_data *data, PurpleRequestFi
{
GList *selected = purple_request_field_list_get_selected(field);
char *value;
- fieldnode = xmlnode_new_child(result, "field");
- xmlnode_set_attrib(fieldnode, "var", id);
+ fieldnode = purple_xmlnode_new_child(result, "field");
+ purple_xmlnode_set_attrib(fieldnode, "var", id);
while(selected) {
value = purple_request_field_list_get_data(field, selected->data);
- valuenode = xmlnode_new_child(fieldnode, "value");
+ valuenode = purple_xmlnode_new_child(fieldnode, "value");
if(value)
- xmlnode_insert_data(valuenode, value, -1);
+ purple_xmlnode_insert_data(valuenode, value, -1);
selected = selected->next;
}
}
break;
case JABBER_X_DATA_BOOLEAN:
- fieldnode = xmlnode_new_child(result, "field");
- xmlnode_set_attrib(fieldnode, "var", id);
- valuenode = xmlnode_new_child(fieldnode, "value");
+ fieldnode = purple_xmlnode_new_child(result, "field");
+ purple_xmlnode_set_attrib(fieldnode, "var", id);
+ valuenode = purple_xmlnode_new_child(fieldnode, "value");
if(purple_request_field_bool_get_value(field))
- xmlnode_insert_data(valuenode, "1", -1);
+ purple_xmlnode_insert_data(valuenode, "1", -1);
else
- xmlnode_insert_data(valuenode, "0", -1);
+ purple_xmlnode_insert_data(valuenode, "0", -1);
break;
case JABBER_X_DATA_IGNORE:
break;
@@ -164,7 +164,7 @@ static void jabber_x_data_ok_cb(struct jabber_x_data_data *data, PurpleRequestFi
}
static void jabber_x_data_cancel_cb(struct jabber_x_data_data *data, PurpleRequestFields *fields) {
- xmlnode *result = xmlnode_new("x");
+ PurpleXmlNode *result = purple_xmlnode_new("x");
jabber_x_data_action_cb cb = data->cb;
gpointer user_data = data->user_data;
JabberStream *js = data->js;
@@ -184,8 +184,8 @@ static void jabber_x_data_cancel_cb(struct jabber_x_data_data *data, PurpleReque
}
g_free(data);
- xmlnode_set_namespace(result, "jabber:x:data");
- xmlnode_set_attrib(result, "type", "cancel");
+ purple_xmlnode_set_namespace(result, "jabber:x:data");
+ purple_xmlnode_set_attrib(result, "type", "cancel");
if (hasActions)
cb(js, result, NULL, user_data);
@@ -193,15 +193,15 @@ static void jabber_x_data_cancel_cb(struct jabber_x_data_data *data, PurpleReque
((jabber_x_data_cb)cb)(js, result, user_data);
}
-void *jabber_x_data_request(JabberStream *js, xmlnode *packet, jabber_x_data_cb cb, gpointer user_data)
+void *jabber_x_data_request(JabberStream *js, PurpleXmlNode *packet, jabber_x_data_cb cb, gpointer user_data)
{
return jabber_x_data_request_with_actions(js, packet, NULL, 0, (jabber_x_data_action_cb)cb, user_data);
}
-void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GList *actions, int defaultaction, jabber_x_data_action_cb cb, gpointer user_data)
+void *jabber_x_data_request_with_actions(JabberStream *js, PurpleXmlNode *packet, GList *actions, int defaultaction, jabber_x_data_action_cb cb, gpointer user_data)
{
void *handle;
- xmlnode *fn, *x;
+ PurpleXmlNode *fn, *x;
PurpleRequestFields *fields;
PurpleRequestFieldGroup *group;
PurpleRequestField *field = NULL;
@@ -220,11 +220,11 @@ void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GLis
group = purple_request_field_group_new(NULL);
purple_request_fields_add_group(fields, group);
- for(fn = xmlnode_get_child(packet, "field"); fn; fn = xmlnode_get_next_twin(fn)) {
- xmlnode *valuenode;
- const char *type = xmlnode_get_attrib(fn, "type");
- const char *label = xmlnode_get_attrib(fn, "label");
- const char *var = xmlnode_get_attrib(fn, "var");
+ for(fn = purple_xmlnode_get_child(packet, "field"); fn; fn = purple_xmlnode_get_next_twin(fn)) {
+ PurpleXmlNode *valuenode;
+ const char *type = purple_xmlnode_get_attrib(fn, "type");
+ const char *label = purple_xmlnode_get_attrib(fn, "label");
+ const char *var = purple_xmlnode_get_attrib(fn, "var");
char *value = NULL;
if(!type)
@@ -236,8 +236,8 @@ void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GLis
label = var;
if(!strcmp(type, "text-private")) {
- if((valuenode = xmlnode_get_child(fn, "value")))
- value = xmlnode_get_data(valuenode);
+ if((valuenode = purple_xmlnode_get_child(fn, "value")))
+ value = purple_xmlnode_get_data(valuenode);
field = purple_request_field_string_new(var, label,
value ? value : "", FALSE);
@@ -250,10 +250,10 @@ void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GLis
} else if(!strcmp(type, "text-multi") || !strcmp(type, "jid-multi")) {
GString *str = g_string_new("");
- for(valuenode = xmlnode_get_child(fn, "value"); valuenode;
- valuenode = xmlnode_get_next_twin(valuenode)) {
+ for(valuenode = purple_xmlnode_get_child(fn, "value"); valuenode;
+ valuenode = purple_xmlnode_get_next_twin(valuenode)) {
- if(!(value = xmlnode_get_data(valuenode)))
+ if(!(value = purple_xmlnode_get_data(valuenode)))
continue;
g_string_append_printf(str, "%s\n", value);
@@ -268,7 +268,7 @@ void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GLis
g_string_free(str, TRUE);
} else if(!strcmp(type, "list-single") || !strcmp(type, "list-multi")) {
- xmlnode *optnode;
+ PurpleXmlNode *optnode;
GList *selected = NULL;
field = purple_request_field_list_new(var, label);
@@ -282,25 +282,25 @@ void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GLis
GINT_TO_POINTER(JABBER_X_DATA_LIST_SINGLE));
}
- for(valuenode = xmlnode_get_child(fn, "value"); valuenode;
- valuenode = xmlnode_get_next_twin(valuenode)) {
- char *data = xmlnode_get_data(valuenode);
+ for(valuenode = purple_xmlnode_get_child(fn, "value"); valuenode;
+ valuenode = purple_xmlnode_get_next_twin(valuenode)) {
+ char *data = purple_xmlnode_get_data(valuenode);
if (data != NULL) {
selected = g_list_prepend(selected, data);
}
}
- for(optnode = xmlnode_get_child(fn, "option"); optnode;
- optnode = xmlnode_get_next_twin(optnode)) {
+ for(optnode = purple_xmlnode_get_child(fn, "option"); optnode;
+ optnode = purple_xmlnode_get_next_twin(optnode)) {
const char *lbl;
- if(!(valuenode = xmlnode_get_child(optnode, "value")))
+ if(!(valuenode = purple_xmlnode_get_child(optnode, "value")))
continue;
- if(!(value = xmlnode_get_data(valuenode)))
+ if(!(value = purple_xmlnode_get_data(valuenode)))
continue;
- if(!(lbl = xmlnode_get_attrib(optnode, "label")))
+ if(!(lbl = purple_xmlnode_get_attrib(optnode, "label")))
lbl = value;
data->values = g_slist_prepend(data->values, value);
@@ -319,8 +319,8 @@ void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GLis
} else if(!strcmp(type, "boolean")) {
gboolean def = FALSE;
- if((valuenode = xmlnode_get_child(fn, "value")))
- value = xmlnode_get_data(valuenode);
+ if((valuenode = purple_xmlnode_get_child(fn, "value")))
+ value = purple_xmlnode_get_data(valuenode);
if(value && (!g_ascii_strcasecmp(value, "yes") ||
!g_ascii_strcasecmp(value, "true") || !g_ascii_strcasecmp(value, "1")))
@@ -333,8 +333,8 @@ void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GLis
g_free(value);
} else if(!strcmp(type, "fixed")) {
- if((valuenode = xmlnode_get_child(fn, "value")))
- value = xmlnode_get_data(valuenode);
+ if((valuenode = purple_xmlnode_get_child(fn, "value")))
+ value = purple_xmlnode_get_data(valuenode);
if(value != NULL) {
field = purple_request_field_label_new("", value);
@@ -343,8 +343,8 @@ void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GLis
g_free(value);
}
} else if(!strcmp(type, "hidden")) {
- if((valuenode = xmlnode_get_child(fn, "value")))
- value = xmlnode_get_data(valuenode);
+ if((valuenode = purple_xmlnode_get_child(fn, "value")))
+ value = purple_xmlnode_get_data(valuenode);
field = purple_request_field_string_new(var, "", value ? value : "",
FALSE);
@@ -355,8 +355,8 @@ void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GLis
g_free(value);
} else { /* text-single, jid-single, and the default */
- if((valuenode = xmlnode_get_child(fn, "value")))
- value = xmlnode_get_data(valuenode);
+ if((valuenode = purple_xmlnode_get_child(fn, "value")))
+ value = purple_xmlnode_get_data(valuenode);
field = purple_request_field_string_new(var, label,
value ? value : "", FALSE);
@@ -372,37 +372,40 @@ void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GLis
g_free(value);
}
- if(field && xmlnode_get_child(fn, "required"))
+ if(field && purple_xmlnode_get_child(fn, "required"))
purple_request_field_set_required(field,TRUE);
}
if(actions != NULL) {
PurpleRequestField *actionfield;
GList *action;
+ int i;
+
data->actiongroup = group = purple_request_field_group_new(_("Actions"));
purple_request_fields_add_group(fields, group);
- actionfield = purple_request_field_choice_new("libpurple:jabber:xdata:actions", _("Select an action"), defaultaction);
+ actionfield = purple_request_field_choice_new("libpurple:jabber:xdata:actions", _("Select an action"), GINT_TO_POINTER(defaultaction));
+ purple_request_field_choice_set_data_destructor(actionfield, g_free);
- for(action = actions; action; action = g_list_next(action)) {
+ for(i = 0, action = actions; action; action = g_list_next(action), i++) {
JabberXDataAction *a = action->data;
- purple_request_field_choice_add(actionfield, a->name);
+ purple_request_field_choice_add(actionfield, a->name, GINT_TO_POINTER(i));
data->actions = g_list_append(data->actions, g_strdup(a->handle));
}
purple_request_field_set_required(actionfield,TRUE);
purple_request_field_group_add_field(group, actionfield);
}
- if((x = xmlnode_get_child(packet, "title")))
- title = xmlnode_get_data(x);
+ if((x = purple_xmlnode_get_child(packet, "title")))
+ title = purple_xmlnode_get_data(x);
- if((x = xmlnode_get_child(packet, "instructions")))
- instructions = xmlnode_get_data(x);
+ if((x = purple_xmlnode_get_child(packet, "instructions")))
+ instructions = purple_xmlnode_get_data(x);
handle = purple_request_fields(js->gc, title, title, instructions, fields,
_("OK"), G_CALLBACK(jabber_x_data_ok_cb),
_("Cancel"), G_CALLBACK(jabber_x_data_cancel_cb),
- purple_connection_get_account(js->gc), /* XXX Do we have a who here? */ NULL, NULL,
+ purple_request_cpar_from_connection(js->gc),
data);
g_free(title);
@@ -412,19 +415,19 @@ void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GLis
}
gchar *
-jabber_x_data_get_formtype(const xmlnode *form)
+jabber_x_data_get_formtype(const PurpleXmlNode *form)
{
- xmlnode *field;
+ PurpleXmlNode *field;
g_return_val_if_fail(form != NULL, NULL);
- for (field = xmlnode_get_child((xmlnode *)form, "field"); field;
- field = xmlnode_get_next_twin(field)) {
- const char *var = xmlnode_get_attrib(field, "var");
+ for (field = purple_xmlnode_get_child((PurpleXmlNode *)form, "field"); field;
+ field = purple_xmlnode_get_next_twin(field)) {
+ const char *var = purple_xmlnode_get_attrib(field, "var");
if (purple_strequal(var, "FORM_TYPE")) {
- xmlnode *value = xmlnode_get_child(field, "value");
+ PurpleXmlNode *value = purple_xmlnode_get_child(field, "value");
if (value)
- return xmlnode_get_data(value);
+ return purple_xmlnode_get_data(value);
else
/* An interesting corner case... Looking for a second
* FORM_TYPE would be more considerate, but I'm in favor
diff --git a/libpurple/protocols/jabber/xdata.h b/libpurple/protocols/jabber/xdata.h
index 7ebc50e008..855b0460c6 100644
--- a/libpurple/protocols/jabber/xdata.h
+++ b/libpurple/protocols/jabber/xdata.h
@@ -32,10 +32,10 @@ typedef struct _JabberXDataAction {
char *handle;
} JabberXDataAction;
-typedef void (*jabber_x_data_cb)(JabberStream *js, xmlnode *result, gpointer user_data);
-typedef void (*jabber_x_data_action_cb)(JabberStream *js, xmlnode *result, const char *actionhandle, gpointer user_data);
-void *jabber_x_data_request(JabberStream *js, xmlnode *packet, jabber_x_data_cb cb, gpointer user_data);
-void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GList *actions, int defaultaction, jabber_x_data_action_cb cb, gpointer user_data);
+typedef void (*jabber_x_data_cb)(JabberStream *js, PurpleXmlNode *result, gpointer user_data);
+typedef void (*jabber_x_data_action_cb)(JabberStream *js, PurpleXmlNode *result, const char *actionhandle, gpointer user_data);
+void *jabber_x_data_request(JabberStream *js, PurpleXmlNode *packet, jabber_x_data_cb cb, gpointer user_data);
+void *jabber_x_data_request_with_actions(JabberStream *js, PurpleXmlNode *packet, GList *actions, int defaultaction, jabber_x_data_action_cb cb, gpointer user_data);
/*
* Return the form type (the CDATA of the value child of the FORM_TYPE
@@ -47,9 +47,9 @@ void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GLis
* </field>
* </x>
*
- * @param form The xmlnode for the form (the 'x' element)
+ * @param form The PurpleXmlNode for the form (the 'x' element)
* @returns The FORM_TYPE. Must be freed by caller.
*/
-gchar *jabber_x_data_get_formtype(const xmlnode *form);
+gchar *jabber_x_data_get_formtype(const PurpleXmlNode *form);
#endif /* PURPLE_JABBER_XDATA_H_ */
diff --git a/libpurple/protocols/msn/Makefile.am b/libpurple/protocols/msn/Makefile.am
index f2edc6454e..f6ad14f1fd 100644
--- a/libpurple/protocols/msn/Makefile.am
+++ b/libpurple/protocols/msn/Makefile.am
@@ -3,7 +3,7 @@ EXTRA_DIST = \
directconn.h \
Makefile.mingw
-pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+pkgdir = @PURPLE_PLUGINDIR@
MSNSOURCES = \
cmdproc.c \
@@ -16,6 +16,8 @@ MSNSOURCES = \
directconn.h \
error.c \
error.h \
+ ft.c \
+ ft.h \
group.c \
group.h \
history.c \
@@ -70,14 +72,12 @@ MSNSOURCES = \
user.h \
userlist.c \
userlist.h \
- xfer.c \
- xfer.h \
msnutils.c \
msnutils.h
AM_CFLAGS = $(st)
-libmsn_la_LDFLAGS = -module -avoid-version
+libmsn_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
if STATIC_MSN
@@ -91,7 +91,7 @@ else
st =
pkg_LTLIBRARIES = libmsn.la
libmsn_la_SOURCES = $(MSNSOURCES)
-libmsn_la_LIBADD = $(GLIB_LIBS)
+libmsn_la_LIBADD = @PURPLE_LIBS@
endif
diff --git a/libpurple/protocols/msn/Makefile.mingw b/libpurple/protocols/msn/Makefile.mingw
index 5d3072f97a..c88df4e582 100644
--- a/libpurple/protocols/msn/Makefile.mingw
+++ b/libpurple/protocols/msn/Makefile.mingw
@@ -42,6 +42,7 @@ C_SRC = cmdproc.c \
contact.c\
directconn.c \
error.c \
+ ft.c \
group.c \
history.c \
httpconn.c \
@@ -69,7 +70,6 @@ C_SRC = cmdproc.c \
transaction.c \
user.c \
userlist.c \
- xfer.c \
msnutils.c
OBJECTS = $(C_SRC:%.c=%.o)
@@ -79,6 +79,7 @@ OBJECTS = $(C_SRC:%.c=%.o)
##
LIBS = \
-lglib-2.0 \
+ -lgobject-2.0 \
-lintl \
-lws2_32 \
-lpurple
diff --git a/libpurple/protocols/msn/command.c b/libpurple/protocols/msn/command.c
index 36f7c3005f..0be77b4630 100644
--- a/libpurple/protocols/msn/command.c
+++ b/libpurple/protocols/msn/command.c
@@ -57,7 +57,7 @@ msn_command_from_string(const char *string)
if (cmd->params != NULL)
{
- int c;
+ guint c;
for (c = 0; cmd->params[c] && cmd->params[c][0]; c++);
cmd->param_count = c;
diff --git a/libpurple/protocols/msn/command.h b/libpurple/protocols/msn/command.h
index 481a807883..d5d2d3a75d 100644
--- a/libpurple/protocols/msn/command.h
+++ b/libpurple/protocols/msn/command.h
@@ -41,7 +41,7 @@ struct _MsnCommand
char *command;
char **params;
- int param_count;
+ guint param_count;
guint ref_count;
diff --git a/libpurple/protocols/msn/contact.c b/libpurple/protocols/msn/contact.c
index 46f7feb8cf..a488c59295 100644
--- a/libpurple/protocols/msn/contact.c
+++ b/libpurple/protocols/msn/contact.c
@@ -96,7 +96,7 @@ msn_callback_state_free(MsnCallbackState *state)
g_free(state->old_group_name);
g_free(state->new_group_name);
g_free(state->guid);
- xmlnode_free(state->body);
+ purple_xmlnode_free(state->body);
g_free(state);
}
@@ -203,9 +203,9 @@ msn_contact_request_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
gpointer data)
{
MsnCallbackState *state = data;
- xmlnode *fault;
+ PurpleXmlNode *fault;
char *faultcode_str;
- xmlnode *cachekey;
+ PurpleXmlNode *cachekey;
char *changed;
if (resp == NULL) {
@@ -218,13 +218,13 @@ msn_contact_request_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
}
/* Update CacheKey if necessary */
- cachekey = xmlnode_get_child(resp->xml, "Header/ServiceHeader/CacheKeyChanged");
+ cachekey = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Header/ServiceHeader/CacheKeyChanged");
if (cachekey != NULL) {
- changed = xmlnode_get_data(cachekey);
+ changed = purple_xmlnode_get_data(cachekey);
if (changed && !strcmp(changed, "true")) {
- cachekey = xmlnode_get_child(resp->xml, "Header/ServiceHeader/CacheKey");
+ cachekey = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Header/ServiceHeader/CacheKey");
g_free(state->session->abch_cachekey);
- state->session->abch_cachekey = xmlnode_get_data(cachekey);
+ state->session->abch_cachekey = purple_xmlnode_get_data(cachekey);
purple_debug_info("msn", "Updated CacheKey for %s to '%s'.\n",
purple_account_get_username(state->session->account),
state->session->abch_cachekey);
@@ -232,7 +232,7 @@ msn_contact_request_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
g_free(changed);
}
- fault = xmlnode_get_child(resp->xml, "Body/Fault");
+ fault = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault");
if (fault == NULL) {
/* No errors */
@@ -242,7 +242,7 @@ msn_contact_request_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
return;
}
- faultcode_str = xmlnode_get_data(xmlnode_get_child(fault, "faultcode"));
+ faultcode_str = purple_xmlnode_get_data(purple_xmlnode_get_child(fault, "faultcode"));
if (faultcode_str && g_str_equal(faultcode_str, "q0:BadContextToken")) {
purple_debug_info("msn",
@@ -259,7 +259,7 @@ msn_contact_request_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
state->cb(req, resp, data);
} else {
/* We don't know how to respond to this faultcode, so log it */
- char *str = xmlnode_to_str(fault, NULL);
+ char *str = purple_xmlnode_to_str(fault, NULL);
purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
msn_contact_operation_str(state->action), str);
g_free(str);
@@ -273,23 +273,23 @@ msn_contact_request_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
static gboolean
msn_contact_request(MsnCallbackState *state)
{
- xmlnode *cachekey = xmlnode_get_child(state->body,
+ PurpleXmlNode *cachekey = purple_xmlnode_get_child(state->body,
"Header/ABApplicationHeader/CacheKey");
if (cachekey != NULL)
- xmlnode_free(cachekey);
+ purple_xmlnode_free(cachekey);
if (state->session->abch_cachekey != NULL) {
- cachekey = xmlnode_new_child(xmlnode_get_child(state->body, "Header/ABApplicationHeader"), "CacheKey");
- xmlnode_insert_data(cachekey, state->session->abch_cachekey, -1);
+ cachekey = purple_xmlnode_new_child(purple_xmlnode_get_child(state->body, "Header/ABApplicationHeader"), "CacheKey");
+ purple_xmlnode_insert_data(cachekey, state->session->abch_cachekey, -1);
}
if (state->token == NULL)
- state->token = xmlnode_get_child(state->body,
+ state->token = purple_xmlnode_get_child(state->body,
"Header/ABAuthHeader/TicketToken");
/* delete old & replace with new token */
- xmlnode_free(state->token->child);
- xmlnode_insert_data(state->token,
+ purple_xmlnode_free(state->token->child);
+ purple_xmlnode_insert_data(state->token,
msn_nexus_get_token_str(state->session->nexus, MSN_AUTH_CONTACTS), -1);
- msn_soap_message_send(state->session,
- msn_soap_message_new(state->post_action, xmlnode_copy(state->body)),
+ msn_soap_service_send_message(state->session->soap,
+ msn_soap_message_new(state->post_action, purple_xmlnode_copy(state->body)),
MSN_CONTACT_SERVER, state->post_url, FALSE,
msn_contact_request_cb, state);
return FALSE;
@@ -322,7 +322,7 @@ static void
msn_create_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
{
MsnCallbackState *state = data;
- if (resp && xmlnode_get_child(resp->xml, "Body/Fault") == NULL) {
+ if (resp && purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault") == NULL) {
purple_debug_info("msn", "Address Book successfully created!\n");
msn_get_address_book(state->session, MSN_PS_INITIAL, NULL, NULL);
} else {
@@ -346,7 +346,7 @@ msn_create_address_book(MsnSession *session)
session->user->passport);
state = msn_callback_state_new(session);
- state->body = xmlnode_from_str(body, -1);
+ state->body = purple_xmlnode_from_str(body, -1);
state->post_action = MSN_ADD_ADDRESSBOOK_SOAP_ACTION;
state->post_url = MSN_ADDRESS_BOOK_POST_URL;
state->cb = msn_create_address_cb;
@@ -356,33 +356,33 @@ msn_create_address_book(MsnSession *session)
}
static void
-msn_parse_each_member(MsnSession *session, xmlnode *member, const char *node,
+msn_parse_each_member(MsnSession *session, PurpleXmlNode *member, const char *node,
MsnListId list)
{
char *passport;
char *type;
char *member_id;
MsnUser *user;
- xmlnode *annotation;
+ PurpleXmlNode *annotation;
guint nid = MSN_NETWORK_UNKNOWN;
char *invite = NULL;
- passport = xmlnode_get_data(xmlnode_get_child(member, node));
+ passport = purple_xmlnode_get_data(purple_xmlnode_get_child(member, node));
if (!msn_email_is_valid(passport)) {
g_free(passport);
return;
}
- type = xmlnode_get_data(xmlnode_get_child(member, "Type"));
- member_id = xmlnode_get_data(xmlnode_get_child(member, "MembershipId"));
+ type = purple_xmlnode_get_data(purple_xmlnode_get_child(member, "Type"));
+ member_id = purple_xmlnode_get_data(purple_xmlnode_get_child(member, "MembershipId"));
user = msn_userlist_find_add_user(session->userlist, passport, NULL);
- for (annotation = xmlnode_get_child(member, "Annotations/Annotation");
+ for (annotation = purple_xmlnode_get_child(member, "Annotations/Annotation");
annotation;
- annotation = xmlnode_get_next_twin(annotation)) {
- char *name = xmlnode_get_data(xmlnode_get_child(annotation, "Name"));
- char *value = xmlnode_get_data(xmlnode_get_child(annotation, "Value"));
+ annotation = purple_xmlnode_get_next_twin(annotation)) {
+ char *name = purple_xmlnode_get_data(purple_xmlnode_get_child(annotation, "Name"));
+ char *value = purple_xmlnode_get_data(purple_xmlnode_get_child(annotation, "Value"));
if (name && value) {
if (!strcmp(name, "MSN.IM.BuddyType")) {
nid = strtoul(value, NULL, 10);
@@ -420,39 +420,39 @@ msn_parse_each_member(MsnSession *session, xmlnode *member, const char *node,
}
static void
-msn_parse_each_service(MsnSession *session, xmlnode *service)
+msn_parse_each_service(MsnSession *session, PurpleXmlNode *service)
{
- xmlnode *type;
+ PurpleXmlNode *type;
- if ((type = xmlnode_get_child(service, "Info/Handle/Type"))) {
- char *type_str = xmlnode_get_data(type);
+ if ((type = purple_xmlnode_get_child(service, "Info/Handle/Type"))) {
+ char *type_str = purple_xmlnode_get_data(type);
if (g_str_equal(type_str, "Profile")) {
/* Process Windows Live 'Messenger Roaming Identity' */
} else if (g_str_equal(type_str, "Messenger")) {
- xmlnode *lastchange = xmlnode_get_child(service, "LastChange");
- char *lastchange_str = xmlnode_get_data(lastchange);
- xmlnode *membership;
+ PurpleXmlNode *lastchange = purple_xmlnode_get_child(service, "LastChange");
+ char *lastchange_str = purple_xmlnode_get_data(lastchange);
+ PurpleXmlNode *membership;
purple_debug_info("msn", "CL last change: %s\n", lastchange_str);
purple_account_set_string(session->account, "CLLastChange",
lastchange_str);
- for (membership = xmlnode_get_child(service,
+ for (membership = purple_xmlnode_get_child(service,
"Memberships/Membership");
- membership; membership = xmlnode_get_next_twin(membership)) {
+ membership; membership = purple_xmlnode_get_next_twin(membership)) {
- xmlnode *role = xmlnode_get_child(membership, "MemberRole");
- char *role_str = xmlnode_get_data(role);
+ PurpleXmlNode *role = purple_xmlnode_get_child(membership, "MemberRole");
+ char *role_str = purple_xmlnode_get_data(role);
MsnListId list = msn_get_memberrole(role_str);
- xmlnode *member;
+ PurpleXmlNode *member;
purple_debug_info("msn", "CL MemberRole role: %s, list: %d\n",
role_str, list);
- for (member = xmlnode_get_child(membership, "Members/Member");
- member; member = xmlnode_get_next_twin(member)) {
- const char *member_type = xmlnode_get_attrib(member, "type");
+ for (member = purple_xmlnode_get_child(membership, "Members/Member");
+ member; member = purple_xmlnode_get_next_twin(member)) {
+ const char *member_type = purple_xmlnode_get_attrib(member, "type");
if (g_str_equal(member_type, "PassportMember")) {
msn_parse_each_member(session, member, "PassportName",
list);
@@ -475,9 +475,9 @@ msn_parse_each_service(MsnSession *session, xmlnode *service)
/*parse contact list*/
static gboolean
-msn_parse_contact_list(MsnSession *session, xmlnode *node)
+msn_parse_contact_list(MsnSession *session, PurpleXmlNode *node)
{
- xmlnode *fault, *faultnode;
+ PurpleXmlNode *fault, *faultnode;
/* we may get a response if our cache data is too old:
*
@@ -486,15 +486,15 @@ msn_parse_contact_list(MsnSession *session, xmlnode *node)
*
* this is not handled yet
*/
- if ((fault = xmlnode_get_child(node, "Body/Fault"))) {
- if ((faultnode = xmlnode_get_child(fault, "faultstring"))) {
- char *faultstring = xmlnode_get_data(faultnode);
+ if ((fault = purple_xmlnode_get_child(node, "Body/Fault"))) {
+ if ((faultnode = purple_xmlnode_get_child(fault, "faultstring"))) {
+ char *faultstring = purple_xmlnode_get_data(faultnode);
purple_debug_info("msn", "Retrieving contact list failed: %s\n",
faultstring);
g_free(faultstring);
}
- if ((faultnode = xmlnode_get_child(fault, "detail/errorcode"))) {
- char *errorcode = xmlnode_get_data(faultnode);
+ if ((faultnode = purple_xmlnode_get_child(fault, "detail/errorcode"))) {
+ char *errorcode = purple_xmlnode_get_data(faultnode);
if (g_str_equal(errorcode, "ABDoesNotExist")) {
msn_create_address_book(session);
@@ -508,11 +508,11 @@ msn_parse_contact_list(MsnSession *session, xmlnode *node)
msn_get_contact_list(session, MSN_PS_INITIAL, NULL);
return FALSE;
} else {
- xmlnode *service;
+ PurpleXmlNode *service;
- for (service = xmlnode_get_child(node, "Body/FindMembershipResponse/"
+ for (service = purple_xmlnode_get_child(node, "Body/FindMembershipResponse/"
"FindMembershipResult/Services/Service");
- service; service = xmlnode_get_next_twin(service)) {
+ service; service = purple_xmlnode_get_next_twin(service)) {
msn_parse_each_service(session, service);
}
return TRUE;
@@ -536,7 +536,7 @@ msn_get_contact_list_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
purple_debug_misc("msn", "Got the contact list!\n");
- if (msn_parse_contact_list(session, resp->xml)) {
+ if (msn_parse_contact_list(session, msn_soap_message_get_xml(resp))) {
#ifdef MSN_PARTIAL_LISTS
abLastChange = purple_account_get_string(session->account,
"ablastChange", NULL);
@@ -580,7 +580,7 @@ msn_get_contact_list(MsnSession *session,
state = msn_callback_state_new(session);
state->partner_scenario = partner_scenario;
- state->body = xmlnode_from_str(body, -1);
+ state->body = purple_xmlnode_from_str(body, -1);
state->post_action = MSN_GET_CONTACT_SOAP_ACTION;
state->post_url = MSN_GET_CONTACT_POST_URL;
state->cb = msn_get_contact_list_cb;
@@ -591,21 +591,21 @@ msn_get_contact_list(MsnSession *session,
}
static void
-msn_parse_addressbook_groups(MsnSession *session, xmlnode *node)
+msn_parse_addressbook_groups(MsnSession *session, PurpleXmlNode *node)
{
- xmlnode *group;
+ PurpleXmlNode *group;
purple_debug_info("msn", "msn_parse_addressbook_groups()\n");
- for(group = xmlnode_get_child(node, "Group"); group;
- group = xmlnode_get_next_twin(group)){
- xmlnode *groupId, *groupInfo, *groupname;
+ for(group = purple_xmlnode_get_child(node, "Group"); group;
+ group = purple_xmlnode_get_next_twin(group)){
+ PurpleXmlNode *groupId, *groupInfo, *groupname;
char *group_id = NULL, *group_name = NULL;
- if ((groupId = xmlnode_get_child(group, "groupId")))
- group_id = xmlnode_get_data(groupId);
- if ((groupInfo = xmlnode_get_child(group, "groupInfo")) && (groupname = xmlnode_get_child(groupInfo, "name")))
- group_name = xmlnode_get_data(groupname);
+ if ((groupId = purple_xmlnode_get_child(group, "groupId")))
+ group_id = purple_xmlnode_get_data(groupId);
+ if ((groupInfo = purple_xmlnode_get_child(group, "groupInfo")) && (groupname = purple_xmlnode_get_child(groupInfo, "name")))
+ group_name = purple_xmlnode_get_data(groupname);
if (group_id == NULL) {
/* Group of ungroupped buddies */
@@ -616,7 +616,7 @@ msn_parse_addressbook_groups(MsnSession *session, xmlnode *node)
msn_group_new(session->userlist, group_id, group_name);
purple_debug_info("msn", "AB group_id: %s, name: %s\n", group_id, group_name ? group_name : "(null)");
- if ((purple_find_group(group_name)) == NULL) {
+ if ((purple_blist_find_group(group_name)) == NULL) {
PurpleGroup *g = purple_group_new(group_name);
purple_blist_add_group(g, NULL);
}
@@ -626,42 +626,42 @@ msn_parse_addressbook_groups(MsnSession *session, xmlnode *node)
}
static gboolean
-msn_parse_addressbook_mobile(xmlnode *contactInfo, char **inout_mobile_number)
+msn_parse_addressbook_mobile(PurpleXmlNode *contactInfo, char **inout_mobile_number)
{
- xmlnode *phones;
+ PurpleXmlNode *phones;
char *mobile_number = NULL;
gboolean mobile = FALSE;
*inout_mobile_number = NULL;
- if ((phones = xmlnode_get_child(contactInfo, "phones"))) {
- xmlnode *contact_phone;
+ if ((phones = purple_xmlnode_get_child(contactInfo, "phones"))) {
+ PurpleXmlNode *contact_phone;
char *phone_type = NULL;
- for (contact_phone = xmlnode_get_child(phones, "ContactPhone");
+ for (contact_phone = purple_xmlnode_get_child(phones, "ContactPhone");
contact_phone;
- contact_phone = xmlnode_get_next_twin(contact_phone)) {
- xmlnode *contact_phone_type;
+ contact_phone = purple_xmlnode_get_next_twin(contact_phone)) {
+ PurpleXmlNode *contact_phone_type;
if (!(contact_phone_type =
- xmlnode_get_child(contact_phone, "contactPhoneType")))
+ purple_xmlnode_get_child(contact_phone, "contactPhoneType")))
continue;
- phone_type = xmlnode_get_data(contact_phone_type);
+ phone_type = purple_xmlnode_get_data(contact_phone_type);
if (phone_type && !strcmp(phone_type, "ContactPhoneMobile")) {
- xmlnode *number;
+ PurpleXmlNode *number;
- if ((number = xmlnode_get_child(contact_phone, "number"))) {
- xmlnode *messenger_enabled;
+ if ((number = purple_xmlnode_get_child(contact_phone, "number"))) {
+ PurpleXmlNode *messenger_enabled;
char *is_messenger_enabled = NULL;
g_free(mobile_number);
- mobile_number = xmlnode_get_data(number);
+ mobile_number = purple_xmlnode_get_data(number);
if (mobile_number &&
- (messenger_enabled = xmlnode_get_child(contact_phone, "isMessengerEnabled"))
- && (is_messenger_enabled = xmlnode_get_data(messenger_enabled))
+ (messenger_enabled = purple_xmlnode_get_child(contact_phone, "isMessengerEnabled"))
+ && (is_messenger_enabled = purple_xmlnode_get_data(messenger_enabled))
&& !strcmp(is_messenger_enabled, "true"))
mobile = TRUE;
@@ -678,17 +678,17 @@ msn_parse_addressbook_mobile(xmlnode *contactInfo, char **inout_mobile_number)
}
static void
-msn_parse_addressbook_contacts(MsnSession *session, xmlnode *node)
+msn_parse_addressbook_contacts(MsnSession *session, PurpleXmlNode *node)
{
- xmlnode *contactNode;
+ PurpleXmlNode *contactNode;
char *passport = NULL, *Name = NULL, *uid = NULL, *type = NULL, *mobile_number = NULL, *alias = NULL;
gboolean mobile = FALSE;
PurpleConnection *pc = purple_account_get_connection(session->account);
- for(contactNode = xmlnode_get_child(node, "Contact"); contactNode;
- contactNode = xmlnode_get_next_twin(contactNode)) {
- xmlnode *contactId, *contactInfo, *contactType, *passportName, *displayName, *guid, *groupIds;
- xmlnode *annotation;
+ for(contactNode = purple_xmlnode_get_child(node, "Contact"); contactNode;
+ contactNode = purple_xmlnode_get_next_twin(contactNode)) {
+ PurpleXmlNode *contactId, *contactInfo, *contactType, *passportName, *displayName, *guid, *groupIds;
+ PurpleXmlNode *annotation;
MsnUser *user;
g_free(passport);
@@ -698,34 +698,33 @@ msn_parse_addressbook_contacts(MsnSession *session, xmlnode *node)
g_free(mobile_number);
g_free(alias);
passport = Name = uid = type = mobile_number = alias = NULL;
- mobile = FALSE;
- if (!(contactId = xmlnode_get_child(contactNode,"contactId"))
- || !(contactInfo = xmlnode_get_child(contactNode, "contactInfo"))
- || !(contactType = xmlnode_get_child(contactInfo, "contactType")))
+ if (!(contactId = purple_xmlnode_get_child(contactNode,"contactId"))
+ || !(contactInfo = purple_xmlnode_get_child(contactNode, "contactInfo"))
+ || !(contactType = purple_xmlnode_get_child(contactInfo, "contactType")))
continue;
- uid = xmlnode_get_data(contactId);
- type = xmlnode_get_data(contactType);
+ uid = purple_xmlnode_get_data(contactId);
+ type = purple_xmlnode_get_data(contactType);
/* Find out our settings */
if (type && !strcmp(type, "Me")) {
/* setup the Display Name */
if (purple_connection_get_display_name(pc) == NULL) {
char *friendly = NULL;
- if ((displayName = xmlnode_get_child(contactInfo, "displayName")))
- friendly = xmlnode_get_data(displayName);
+ if ((displayName = purple_xmlnode_get_child(contactInfo, "displayName")))
+ friendly = purple_xmlnode_get_data(displayName);
purple_connection_set_display_name(pc,
friendly ? purple_url_decode(friendly) : NULL);
g_free(friendly);
}
- for (annotation = xmlnode_get_child(contactInfo, "annotations/Annotation");
+ for (annotation = purple_xmlnode_get_child(contactInfo, "annotations/Annotation");
annotation;
- annotation = xmlnode_get_next_twin(annotation)) {
+ annotation = purple_xmlnode_get_next_twin(annotation)) {
char *name, *value;
- name = xmlnode_get_data(xmlnode_get_child(annotation, "Name"));
- value = xmlnode_get_data(xmlnode_get_child(annotation, "Value"));
+ name = purple_xmlnode_get_data(purple_xmlnode_get_child(annotation, "Name"));
+ value = purple_xmlnode_get_data(purple_xmlnode_get_child(annotation, "Value"));
if (name && g_str_equal(name, "MSN.IM.MPOP")) {
if (!value || atoi(value) != 0)
session->enable_mpop = TRUE;
@@ -739,29 +738,43 @@ msn_parse_addressbook_contacts(MsnSession *session, xmlnode *node)
continue; /* Not adding own account as buddy to buddylist */
}
- passportName = xmlnode_get_child(contactInfo, "passportName");
+ passportName = purple_xmlnode_get_child(contactInfo, "passportName");
+ if (passportName != NULL) {
+ PurpleXmlNode *messenger_user;
+ /* ignore non-messenger contacts */
+ if ((messenger_user = purple_xmlnode_get_child(contactInfo, "isMessengerUser"))) {
+ char *is_messenger_user = purple_xmlnode_get_data(messenger_user);
+
+ if (is_messenger_user && !strcmp(is_messenger_user, "false")) {
+ passportName = NULL;
+ }
+
+ g_free(is_messenger_user);
+ }
+ }
+
if (passportName == NULL) {
- xmlnode *emailsNode, *contactEmailNode, *emailNode;
- xmlnode *messengerEnabledNode;
+ PurpleXmlNode *emailsNode, *contactEmailNode, *emailNode;
+ PurpleXmlNode *messengerEnabledNode;
char *msnEnabled;
/*TODO: add it to the non-instant Messenger group and recognize as email Membership*/
/* Yahoo/Federated User? */
- emailsNode = xmlnode_get_child(contactInfo, "emails");
+ emailsNode = purple_xmlnode_get_child(contactInfo, "emails");
if (emailsNode == NULL) {
/*TODO: need to support the Mobile type*/
continue;
}
- for (contactEmailNode = xmlnode_get_child(emailsNode, "ContactEmail");
+ for (contactEmailNode = purple_xmlnode_get_child(emailsNode, "ContactEmail");
contactEmailNode;
- contactEmailNode = xmlnode_get_next_twin(contactEmailNode)) {
- if ((messengerEnabledNode = xmlnode_get_child(contactEmailNode, "isMessengerEnabled"))) {
+ contactEmailNode = purple_xmlnode_get_next_twin(contactEmailNode)) {
+ if ((messengerEnabledNode = purple_xmlnode_get_child(contactEmailNode, "isMessengerEnabled"))) {
- msnEnabled = xmlnode_get_data(messengerEnabledNode);
+ msnEnabled = purple_xmlnode_get_data(messengerEnabledNode);
if (msnEnabled && !strcmp(msnEnabled, "true")) {
- if ((emailNode = xmlnode_get_child(contactEmailNode, "email")))
- passport = xmlnode_get_data(emailNode);
+ if ((emailNode = purple_xmlnode_get_child(contactEmailNode, "email")))
+ passport = purple_xmlnode_get_data(emailNode);
/* Messenger enabled, Get the Passport*/
purple_debug_info("msn", "AB Yahoo/Federated User %s\n", passport ? passport : "(null)");
@@ -773,20 +786,7 @@ msn_parse_addressbook_contacts(MsnSession *session, xmlnode *node)
}
}
} else {
- xmlnode *messenger_user;
- /* ignore non-messenger contacts */
- if ((messenger_user = xmlnode_get_child(contactInfo, "isMessengerUser"))) {
- char *is_messenger_user = xmlnode_get_data(messenger_user);
-
- if (is_messenger_user && !strcmp(is_messenger_user, "false")) {
- g_free(is_messenger_user);
- continue;
- }
-
- g_free(is_messenger_user);
- }
-
- passport = xmlnode_get_data(passportName);
+ passport = purple_xmlnode_get_data(passportName);
}
/* Couldn't find anything */
@@ -796,20 +796,20 @@ msn_parse_addressbook_contacts(MsnSession *session, xmlnode *node)
if (!msn_email_is_valid(passport))
continue;
- if ((displayName = xmlnode_get_child(contactInfo, "displayName")))
- Name = xmlnode_get_data(displayName);
+ if ((displayName = purple_xmlnode_get_child(contactInfo, "displayName")))
+ Name = purple_xmlnode_get_data(displayName);
else
Name = g_strdup(passport);
- for (annotation = xmlnode_get_child(contactInfo, "annotations/Annotation");
+ for (annotation = purple_xmlnode_get_child(contactInfo, "annotations/Annotation");
annotation;
- annotation = xmlnode_get_next_twin(annotation)) {
+ annotation = purple_xmlnode_get_next_twin(annotation)) {
char *name;
- name = xmlnode_get_data(xmlnode_get_child(annotation, "Name"));
+ name = purple_xmlnode_get_data(purple_xmlnode_get_child(annotation, "Name"));
if (!name)
continue;
if (!strcmp(name, "AB.NickName"))
- alias = xmlnode_get_data(xmlnode_get_child(annotation, "Value"));
+ alias = purple_xmlnode_get_data(purple_xmlnode_get_child(annotation, "Value"));
else if (!strcmp(name, "MSN.IM.HasSharedFolder"))
; /* Do nothing yet... */
else if (!strcmp(name, "AB.Spouse"))
@@ -832,11 +832,11 @@ msn_parse_addressbook_contacts(MsnSession *session, xmlnode *node)
msn_user_set_uid(user, uid);
msn_user_set_mobile_phone(user, mobile_number);
- groupIds = xmlnode_get_child(contactInfo, "groupIds");
+ groupIds = purple_xmlnode_get_child(contactInfo, "groupIds");
if (groupIds) {
- for (guid = xmlnode_get_child(groupIds, "guid"); guid;
- guid = xmlnode_get_next_twin(guid)) {
- char *group_id = xmlnode_get_data(guid);
+ for (guid = purple_xmlnode_get_child(groupIds, "guid"); guid;
+ guid = purple_xmlnode_get_next_twin(guid)) {
+ char *group_id = purple_xmlnode_get_data(guid);
msn_user_add_group_id(user, group_id);
purple_debug_misc("msn", "AB guid:%s\n", group_id ? group_id : "(null)");
g_free(group_id);
@@ -868,41 +868,41 @@ msn_parse_addressbook_contacts(MsnSession *session, xmlnode *node)
}
static void
-msn_parse_addressbook_circles(MsnSession *session, xmlnode *node)
+msn_parse_addressbook_circles(MsnSession *session, PurpleXmlNode *node)
{
- xmlnode *ticket;
+ PurpleXmlNode *ticket;
/* TODO: Parse groups */
- ticket = xmlnode_get_child(node, "CircleTicket");
+ ticket = purple_xmlnode_get_child(node, "CircleTicket");
if (ticket) {
- char *data = xmlnode_get_data(ticket);
+ char *data = purple_xmlnode_get_data(ticket);
msn_notification_send_circle_auth(session, data);
g_free(data);
}
}
static gboolean
-msn_parse_addressbook(MsnSession *session, xmlnode *node)
+msn_parse_addressbook(MsnSession *session, PurpleXmlNode *node)
{
- xmlnode *result;
- xmlnode *groups;
- xmlnode *contacts;
- xmlnode *abNode;
- xmlnode *circleNode;
- xmlnode *fault;
-
- if ((fault = xmlnode_get_child(node, "Body/Fault"))) {
- xmlnode *faultnode;
-
- if ((faultnode = xmlnode_get_child(fault, "faultstring"))) {
- gchar *faultstring = xmlnode_get_data(faultnode);
+ PurpleXmlNode *result;
+ PurpleXmlNode *groups;
+ PurpleXmlNode *contacts;
+ PurpleXmlNode *abNode;
+ PurpleXmlNode *circleNode;
+ PurpleXmlNode *fault;
+
+ if ((fault = purple_xmlnode_get_child(node, "Body/Fault"))) {
+ PurpleXmlNode *faultnode;
+
+ if ((faultnode = purple_xmlnode_get_child(fault, "faultstring"))) {
+ gchar *faultstring = purple_xmlnode_get_data(faultnode);
purple_debug_info("msn", "AB Faultstring: %s\n", faultstring);
g_free(faultstring);
}
- if ((faultnode = xmlnode_get_child(fault, "detail/errorcode"))) {
- gchar *errorcode = xmlnode_get_data(faultnode);
+ if ((faultnode = purple_xmlnode_get_child(fault, "detail/errorcode"))) {
+ gchar *errorcode = purple_xmlnode_get_data(faultnode);
purple_debug_info("msn", "AB Error Code: %s\n", errorcode);
@@ -916,7 +916,7 @@ msn_parse_addressbook(MsnSession *session, xmlnode *node)
return FALSE;
}
- result = xmlnode_get_child(node, "Body/ABFindContactsPagedResponse/ABFindContactsPagedResult");
+ result = purple_xmlnode_get_child(node, "Body/ABFindContactsPagedResponse/ABFindContactsPagedResult");
if (result == NULL) {
purple_debug_misc("msn", "Received no address book update\n");
return TRUE;
@@ -925,7 +925,7 @@ msn_parse_addressbook(MsnSession *session, xmlnode *node)
/* I don't see this "groups" tag documented on msnpiki, need to find out
if they are really there, and update msnpiki */
/*Process Group List*/
- groups = xmlnode_get_child(result, "Groups");
+ groups = purple_xmlnode_get_child(result, "Groups");
if (groups != NULL) {
msn_parse_addressbook_groups(session, groups);
}
@@ -935,7 +935,7 @@ msn_parse_addressbook(MsnSession *session, xmlnode *node)
MSN_INDIVIDUALS_GROUP_NAME);
purple_debug_misc("msn", "AB group_id:%s name:%s\n",
MSN_INDIVIDUALS_GROUP_ID, MSN_INDIVIDUALS_GROUP_NAME);
- if ((purple_find_group(MSN_INDIVIDUALS_GROUP_NAME)) == NULL){
+ if ((purple_blist_find_group(MSN_INDIVIDUALS_GROUP_NAME)) == NULL){
PurpleGroup *g = purple_group_new(MSN_INDIVIDUALS_GROUP_NAME);
purple_blist_add_group(g, NULL);
}
@@ -943,37 +943,37 @@ msn_parse_addressbook(MsnSession *session, xmlnode *node)
/* Add a "Non-IM Contacts" group */
msn_group_new(session->userlist, MSN_NON_IM_GROUP_ID, MSN_NON_IM_GROUP_NAME);
purple_debug_misc("msn", "AB group_id:%s name:%s\n", MSN_NON_IM_GROUP_ID, MSN_NON_IM_GROUP_NAME);
- if ((purple_find_group(MSN_NON_IM_GROUP_NAME)) == NULL) {
+ if ((purple_blist_find_group(MSN_NON_IM_GROUP_NAME)) == NULL) {
PurpleGroup *g = purple_group_new(MSN_NON_IM_GROUP_NAME);
purple_blist_add_group(g, NULL);
}
/*Process contact List*/
purple_debug_info("msn", "Process contact list...\n");
- contacts = xmlnode_get_child(result, "Contacts");
+ contacts = purple_xmlnode_get_child(result, "Contacts");
if (contacts != NULL) {
msn_parse_addressbook_contacts(session, contacts);
}
- abNode = xmlnode_get_child(result, "Ab");
+ abNode = purple_xmlnode_get_child(result, "Ab");
if (abNode != NULL) {
- xmlnode *node2;
+ PurpleXmlNode *node2;
char *tmp = NULL;
- if ((node2 = xmlnode_get_child(abNode, "lastChange")))
- tmp = xmlnode_get_data(node2);
+ if ((node2 = purple_xmlnode_get_child(abNode, "lastChange")))
+ tmp = purple_xmlnode_get_data(node2);
purple_debug_info("msn", "AB lastchanged Time:{%s}\n", tmp ? tmp : "(null)");
purple_account_set_string(session->account, "ablastChange", tmp);
g_free(tmp); tmp = NULL;
- if ((node2 = xmlnode_get_child(abNode, "DynamicItemLastChanged")))
- tmp = xmlnode_get_data(node2);
+ if ((node2 = purple_xmlnode_get_child(abNode, "DynamicItemLastChanged")))
+ tmp = purple_xmlnode_get_data(node2);
purple_debug_info("msn", "AB DynamicItemLastChanged :{%s}\n", tmp ? tmp : "(null)");
purple_account_set_string(session->account, "DynamicItemLastChanged", tmp);
g_free(tmp);
}
- circleNode = xmlnode_get_child(result, "CircleResult");
+ circleNode = purple_xmlnode_get_child(result, "CircleResult");
if (circleNode != NULL) {
msn_parse_addressbook_circles(session, circleNode);
}
@@ -991,8 +991,8 @@ msn_get_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
purple_debug_misc("msn", "Got the Address Book!\n");
- if (msn_parse_addressbook(session, resp->xml)) {
- msn_send_privacy(session->account->gc);
+ if (msn_parse_addressbook(session, msn_soap_message_get_xml(resp))) {
+ msn_send_privacy(purple_account_get_connection(session->account));
msn_notification_dump_contact(session);
} else {
/* This is making us loop infinitely when we fail to parse the
@@ -1028,7 +1028,7 @@ msn_get_address_book(MsnSession *session,
update_str ? update_str : "");
state = msn_callback_state_new(session);
- state->body = xmlnode_from_str(body, -1);
+ state->body = purple_xmlnode_from_str(body, -1);
state->post_action = MSN_GET_ADDRESS_SOAP_ACTION;
state->post_url = MSN_ADDRESS_BOOK_POST_URL;
state->cb = msn_get_address_cb;
@@ -1051,25 +1051,28 @@ msn_add_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
MsnUserList *userlist;
MsnUser *user;
- xmlnode *guid;
+ PurpleXmlNode *guid;
- xmlnode *fault;
+ PurpleXmlNode *fault;
g_return_if_fail(session != NULL);
userlist = session->userlist;
- fault = xmlnode_get_child(resp->xml, "Body/Fault");
+ fault = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault");
if (fault != NULL) {
- char *errorcode = xmlnode_get_data(xmlnode_get_child(fault, "detail/errorcode"));
+ char *errorcode = purple_xmlnode_get_data(purple_xmlnode_get_child(fault, "detail/errorcode"));
if (errorcode && !strcmp(errorcode, "EmailDomainIsFederated")) {
/* Do something special! */
purple_debug_error("msn", "Contact is from a federated domain, but don't know what to do yet!\n");
} else if (errorcode && !strcmp(errorcode, "InvalidPassportUser")) {
- PurpleBuddy *buddy = purple_find_buddy(session->account, state->who);
+ PurpleBuddy *buddy = purple_blist_find_buddy(session->account, state->who);
char *str = g_strdup_printf(_("Unable to add \"%s\"."), state->who);
- purple_notify_error(state->session, _("Buddy Add error"), str,
- _("The username specified does not exist."));
+ purple_notify_error(state->session,
+ _("Buddy Add error"), str,
+ _("The username specified does not exist."),
+ purple_request_cpar_from_account(
+ session->account));
g_free(str);
msn_userlist_rem_buddy(userlist, state->who);
if (buddy != NULL)
@@ -1077,7 +1080,7 @@ msn_add_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
} else {
/* We don't know how to respond to this faultcode, so log it */
- char *fault_str = xmlnode_to_str(fault, NULL);
+ char *fault_str = purple_xmlnode_to_str(fault, NULL);
if (fault_str != NULL) {
purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
msn_contact_operation_str(state->action), fault_str);
@@ -1095,10 +1098,10 @@ msn_add_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
user = msn_userlist_find_add_user(userlist, state->who, state->who);
msn_user_add_group_id(user, state->guid);
- guid = xmlnode_get_child(resp->xml,
+ guid = purple_xmlnode_get_child(msn_soap_message_get_xml(resp),
"Body/ABContactAddResponse/ABContactAddResult/guid");
if (guid != NULL) {
- char *uid = xmlnode_get_data(guid);
+ char *uid = purple_xmlnode_get_data(guid);
msn_user_set_uid(user, uid);
purple_debug_info("msn", "Set %s guid to %s.\n", state->who, uid);
g_free(uid);
@@ -1132,7 +1135,7 @@ msn_add_contact(MsnSession *session, MsnCallbackState *state, const char *passpo
}
body = g_strdup_printf(MSN_ADD_CONTACT_TEMPLATE, contact_xml);
- state->body = xmlnode_from_str(body, -1);
+ state->body = purple_xmlnode_from_str(body, -1);
state->post_action = MSN_CONTACT_ADD_SOAP_ACTION;
state->post_url = MSN_ADDRESS_BOOK_POST_URL;
state->cb = msn_add_contact_read_cb;
@@ -1149,23 +1152,25 @@ msn_add_contact_to_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
MsnCallbackState *state = data;
MsnSession *session = state->session;
MsnUserList *userlist;
- xmlnode *fault;
+ PurpleXmlNode *fault;
g_return_if_fail(session != NULL);
userlist = session->userlist;
- fault = xmlnode_get_child(resp->xml, "Body/Fault");
+ fault = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault");
if (fault != NULL) {
- char *errorcode = xmlnode_get_data(xmlnode_get_child(fault, "detail/errorcode"));
+ char *errorcode = purple_xmlnode_get_data(purple_xmlnode_get_child(fault, "detail/errorcode"));
if (errorcode && !strcmp(errorcode, "EmailDomainIsFederated")) {
/* Do something special! */
purple_debug_error("msn", "Contact is from a federated domain, but don't know what to do yet!\n");
} else if (errorcode && !strcmp(errorcode, "InvalidPassportUser")) {
- PurpleBuddy *buddy = purple_find_buddy(session->account, state->who);
+ PurpleBuddy *buddy = purple_blist_find_buddy(session->account, state->who);
char *str = g_strdup_printf(_("Unable to add \"%s\"."), state->who);
purple_notify_error(session, _("Buddy Add error"), str,
- _("The username specified does not exist."));
+ _("The username specified does not exist."),
+ purple_request_cpar_from_account(
+ session->account));
g_free(str);
msn_userlist_rem_buddy(userlist, state->who);
if (buddy != NULL)
@@ -1173,7 +1178,7 @@ msn_add_contact_to_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
} else {
/* We don't know how to respond to this faultcode, so log it */
- char *fault_str = xmlnode_to_str(fault, NULL);
+ char *fault_str = purple_xmlnode_to_str(fault, NULL);
if (fault_str != NULL) {
purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
msn_contact_operation_str(state->action), fault_str);
@@ -1192,11 +1197,11 @@ msn_add_contact_to_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
if (state->action & MSN_ADD_BUDDY) {
MsnUser *user = msn_userlist_find_user(userlist, state->who);
- xmlnode *guid = xmlnode_get_child(resp->xml,
+ PurpleXmlNode *guid = purple_xmlnode_get_child(msn_soap_message_get_xml(resp),
"Body/ABGroupContactAddResponse/ABGroupContactAddResult/guid");
if (guid != NULL) {
- char *uid = xmlnode_get_data(guid);
+ char *uid = purple_xmlnode_get_data(guid);
msn_user_set_uid(user, uid);
purple_debug_info("msn", "Set %s guid to %s.\n", state->who, uid);
g_free(uid);
@@ -1275,7 +1280,7 @@ msn_add_contact_to_group(MsnSession *session, MsnCallbackState *state,
body = g_markup_escape_text(user->invite_message, -1);
/* Ignore the cast, we treat it as const anyway. */
- tmp = (char *)purple_connection_get_display_name(session->account->gc);
+ tmp = (char *)purple_connection_get_display_name(purple_account_get_connection(session->account));
tmp = tmp ? g_markup_escape_text(tmp, -1) : g_strdup("");
invite = g_strdup_printf(MSN_CONTACT_INVITE_MESSAGE_XML, body, tmp);
@@ -1293,7 +1298,7 @@ msn_add_contact_to_group(MsnSession *session, MsnCallbackState *state,
body = g_strdup_printf(MSN_ADD_CONTACT_GROUP_TEMPLATE, groupId, contact_xml, invite);
- state->body = xmlnode_from_str(body, -1);
+ state->body = purple_xmlnode_from_str(body, -1);
state->post_action = MSN_ADD_CONTACT_GROUP_SOAP_ACTION;
state->post_url = MSN_ADDRESS_BOOK_POST_URL;
state->cb = msn_add_contact_to_group_read_cb;
@@ -1311,12 +1316,12 @@ msn_delete_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
MsnCallbackState *state = data;
MsnUserList *userlist = state->session->userlist;
MsnUser *user = msn_userlist_find_user_with_id(userlist, state->uid);
- xmlnode *fault;
+ PurpleXmlNode *fault;
/* We don't know how to respond to this faultcode, so log it */
- fault = xmlnode_get_child(resp->xml, "Body/Fault");
+ fault = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault");
if (fault != NULL) {
- char *fault_str = xmlnode_to_str(fault, NULL);
+ char *fault_str = purple_xmlnode_to_str(fault, NULL);
purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
msn_contact_operation_str(state->action), fault_str);
g_free(fault_str);
@@ -1352,7 +1357,7 @@ msn_delete_contact(MsnSession *session, MsnUser *user)
/* build SOAP request */
body = g_strdup_printf(MSN_DEL_CONTACT_TEMPLATE, contact_id_xml);
- state->body = xmlnode_from_str(body, -1);
+ state->body = purple_xmlnode_from_str(body, -1);
state->post_action = MSN_CONTACT_DEL_SOAP_ACTION;
state->post_url = MSN_ADDRESS_BOOK_POST_URL;
state->cb = msn_delete_contact_read_cb;
@@ -1367,12 +1372,12 @@ msn_del_contact_from_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
gpointer data)
{
MsnCallbackState *state = data;
- xmlnode *fault;
+ PurpleXmlNode *fault;
/* We don't know how to respond to this faultcode, so log it */
- fault = xmlnode_get_child(resp->xml, "Body/Fault");
+ fault = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault");
if (fault != NULL) {
- char *fault_str = xmlnode_to_str(fault, NULL);
+ char *fault_str = purple_xmlnode_to_str(fault, NULL);
purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
msn_contact_operation_str(state->action), fault_str);
g_free(fault_str);
@@ -1433,7 +1438,7 @@ msn_del_contact_from_group(MsnSession *session, const char *passport, const char
contact_id_xml = g_strdup_printf(MSN_CONTACT_XML, passport);
body = g_strdup_printf(MSN_CONTACT_DEL_GROUP_TEMPLATE, contact_id_xml, groupId);
- state->body = xmlnode_from_str(body, -1);
+ state->body = purple_xmlnode_from_str(body, -1);
state->post_action = MSN_CONTACT_DEL_GROUP_SOAP_ACTION;
state->post_url = MSN_ADDRESS_BOOK_POST_URL;
state->cb = msn_del_contact_from_group_read_cb;
@@ -1449,12 +1454,12 @@ msn_update_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
gpointer data)
{
MsnCallbackState *state = (MsnCallbackState *)data;
- xmlnode *fault;
+ PurpleXmlNode *fault;
/* We don't know how to respond to this faultcode, so log it */
- fault = xmlnode_get_child(resp->xml, "Body/Fault");
+ fault = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault");
if (fault != NULL) {
- char *fault_str = xmlnode_to_str(fault, NULL);
+ char *fault_str = purple_xmlnode_to_str(fault, NULL);
purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
msn_contact_operation_str(state->action), fault_str);
g_free(fault_str);
@@ -1469,9 +1474,9 @@ void
msn_update_contact(MsnSession *session, const char *passport, MsnContactUpdateType type, const char* value)
{
MsnCallbackState *state;
- xmlnode *contact;
- xmlnode *contact_info;
- xmlnode *changes;
+ PurpleXmlNode *contact;
+ PurpleXmlNode *contact_info;
+ PurpleXmlNode *changes;
MsnUser *user = NULL;
purple_debug_info("msn", "Update contact information for %s with new %s: %s\n",
@@ -1486,28 +1491,28 @@ msn_update_contact(MsnSession *session, const char *passport, MsnContactUpdateTy
return;
}
- contact_info = xmlnode_new("contactInfo");
- changes = xmlnode_new("propertiesChanged");
+ contact_info = purple_xmlnode_new("contactInfo");
+ changes = purple_xmlnode_new("propertiesChanged");
switch (type) {
- xmlnode *annotations;
- xmlnode *display;
- xmlnode *a, *n, *v;
+ PurpleXmlNode *annotations;
+ PurpleXmlNode *display;
+ PurpleXmlNode *a, *n, *v;
case MSN_UPDATE_DISPLAY:
- display = xmlnode_new_child(contact_info, "displayName");
- xmlnode_insert_data(display, value, -1);
- xmlnode_insert_data(changes, "DisplayName", -1);
+ display = purple_xmlnode_new_child(contact_info, "displayName");
+ purple_xmlnode_insert_data(display, value, -1);
+ purple_xmlnode_insert_data(changes, "DisplayName", -1);
break;
case MSN_UPDATE_ALIAS:
- annotations = xmlnode_new_child(contact_info, "annotations");
- xmlnode_insert_data(changes, "Annotation ", -1);
-
- a = xmlnode_new_child(annotations, "Annotation");
- n = xmlnode_new_child(a, "Name");
- xmlnode_insert_data(n, "AB.NickName", -1);
- v = xmlnode_new_child(a, "Value");
- xmlnode_insert_data(v, value, -1);
+ annotations = purple_xmlnode_new_child(contact_info, "annotations");
+ purple_xmlnode_insert_data(changes, "Annotation ", -1);
+
+ a = purple_xmlnode_new_child(annotations, "Annotation");
+ n = purple_xmlnode_new_child(a, "Name");
+ purple_xmlnode_insert_data(n, "AB.NickName", -1);
+ v = purple_xmlnode_new_child(a, "Value");
+ purple_xmlnode_insert_data(v, value, -1);
break;
default:
@@ -1516,27 +1521,27 @@ msn_update_contact(MsnSession *session, const char *passport, MsnContactUpdateTy
state = msn_callback_state_new(session);
- state->body = xmlnode_from_str(MSN_CONTACT_UPDATE_TEMPLATE, -1);
+ state->body = purple_xmlnode_from_str(MSN_CONTACT_UPDATE_TEMPLATE, -1);
state->action = MSN_UPDATE_INFO;
state->post_action = MSN_CONTACT_UPDATE_SOAP_ACTION;
state->post_url = MSN_ADDRESS_BOOK_POST_URL;
state->cb = msn_update_contact_read_cb;
- contact = xmlnode_get_child(state->body, "Body/ABContactUpdate/contacts/Contact");
- xmlnode_insert_child(contact, contact_info);
- xmlnode_insert_child(contact, changes);
+ contact = purple_xmlnode_get_child(state->body, "Body/ABContactUpdate/contacts/Contact");
+ purple_xmlnode_insert_child(contact, contact_info);
+ purple_xmlnode_insert_child(contact, changes);
- xmlnode_insert_data(xmlnode_get_child(state->body,
+ purple_xmlnode_insert_data(purple_xmlnode_get_child(state->body,
"Header/ABApplicationHeader/PartnerScenario"),
MsnSoapPartnerScenarioText[MSN_PS_SAVE_CONTACT], -1);
if (user) {
- xmlnode *contactId = xmlnode_new_child(contact, "contactId");
+ PurpleXmlNode *contactId = purple_xmlnode_new_child(contact, "contactId");
msn_callback_state_set_uid(state, user->uid);
- xmlnode_insert_data(contactId, state->uid, -1);
+ purple_xmlnode_insert_data(contactId, state->uid, -1);
} else {
- xmlnode *contactType = xmlnode_new_child(contact_info, "contactType");
- xmlnode_insert_data(contactType, "Me", -1);
+ PurpleXmlNode *contactType = purple_xmlnode_new_child(contact_info, "contactType");
+ purple_xmlnode_insert_data(contactType, "Me", -1);
}
msn_contact_request(state);
@@ -1547,12 +1552,12 @@ msn_annotate_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
gpointer data)
{
MsnCallbackState *state = (MsnCallbackState *)data;
- xmlnode *fault;
+ PurpleXmlNode *fault;
/* We don't know how to respond to this faultcode, so log it */
- fault = xmlnode_get_child(resp->xml, "Body/Fault");
+ fault = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault");
if (fault != NULL) {
- char *fault_str = xmlnode_to_str(fault, NULL);
+ char *fault_str = purple_xmlnode_to_str(fault, NULL);
purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
msn_contact_operation_str(state->action), fault_str);
g_free(fault_str);
@@ -1568,9 +1573,9 @@ msn_annotate_contact(MsnSession *session, const char *passport, ...)
{
va_list params;
MsnCallbackState *state;
- xmlnode *contact;
- xmlnode *contact_info;
- xmlnode *annotations;
+ PurpleXmlNode *contact;
+ PurpleXmlNode *contact_info;
+ PurpleXmlNode *annotations;
MsnUser *user = NULL;
g_return_if_fail(passport != NULL);
@@ -1581,14 +1586,14 @@ msn_annotate_contact(MsnSession *session, const char *passport, ...)
return;
}
- contact_info = xmlnode_new("contactInfo");
- annotations = xmlnode_new_child(contact_info, "annotations");
+ contact_info = purple_xmlnode_new("contactInfo");
+ annotations = purple_xmlnode_new_child(contact_info, "annotations");
va_start(params, passport);
while (TRUE) {
const char *name;
const char *value;
- xmlnode *a, *n, *v;
+ PurpleXmlNode *a, *n, *v;
name = va_arg(params, const char *);
if (!name)
@@ -1598,36 +1603,36 @@ msn_annotate_contact(MsnSession *session, const char *passport, ...)
if (!value)
break;
- a = xmlnode_new_child(annotations, "Annotation");
- n = xmlnode_new_child(a, "Name");
- xmlnode_insert_data(n, name, -1);
- v = xmlnode_new_child(a, "Value");
- xmlnode_insert_data(v, value, -1);
+ a = purple_xmlnode_new_child(annotations, "Annotation");
+ n = purple_xmlnode_new_child(a, "Name");
+ purple_xmlnode_insert_data(n, name, -1);
+ v = purple_xmlnode_new_child(a, "Value");
+ purple_xmlnode_insert_data(v, value, -1);
}
va_end(params);
state = msn_callback_state_new(session);
- state->body = xmlnode_from_str(MSN_CONTACT_ANNOTATE_TEMPLATE, -1);
+ state->body = purple_xmlnode_from_str(MSN_CONTACT_ANNOTATE_TEMPLATE, -1);
state->action = MSN_ANNOTATE_USER;
state->post_action = MSN_CONTACT_ANNOTATE_SOAP_ACTION;
state->post_url = MSN_ADDRESS_BOOK_POST_URL;
state->cb = msn_annotate_contact_read_cb;
- xmlnode_insert_data(xmlnode_get_child(state->body,
+ purple_xmlnode_insert_data(purple_xmlnode_get_child(state->body,
"Header/ABApplicationHeader/PartnerScenario"),
MsnSoapPartnerScenarioText[MSN_PS_SAVE_CONTACT], -1);
- contact = xmlnode_get_child(state->body, "Body/ABContactUpdate/contacts/Contact");
- xmlnode_insert_child(contact, contact_info);
+ contact = purple_xmlnode_get_child(state->body, "Body/ABContactUpdate/contacts/Contact");
+ purple_xmlnode_insert_child(contact, contact_info);
if (user) {
- xmlnode *contactId = xmlnode_new_child(contact, "contactId");
+ PurpleXmlNode *contactId = purple_xmlnode_new_child(contact, "contactId");
msn_callback_state_set_uid(state, user->uid);
- xmlnode_insert_data(contactId, state->uid, -1);
+ purple_xmlnode_insert_data(contactId, state->uid, -1);
} else {
- xmlnode *contactType = xmlnode_new_child(contact_info, "contactType");
- xmlnode_insert_data(contactType, "Me", -1);
+ PurpleXmlNode *contactType = purple_xmlnode_new_child(contact_info, "contactType");
+ purple_xmlnode_insert_data(contactType, "Me", -1);
}
msn_contact_request(state);
@@ -1639,12 +1644,12 @@ msn_del_contact_from_list_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
{
MsnCallbackState *state = data;
MsnSession *session = state->session;
- xmlnode *fault;
+ PurpleXmlNode *fault;
/* We don't know how to respond to this faultcode, so log it */
- fault = xmlnode_get_child(resp->xml, "Body/Fault");
+ fault = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault");
if (fault != NULL) {
- char *fault_str = xmlnode_to_str(fault, NULL);
+ char *fault_str = purple_xmlnode_to_str(fault, NULL);
purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
msn_contact_operation_str(state->action), fault_str);
g_free(fault_str);
@@ -1663,10 +1668,10 @@ msn_del_contact_from_list_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
msn_add_contact_to_list(session, new_state, state->who, MSN_LIST_RL);
return;
} else if (state->list_id == MSN_LIST_AL) {
- purple_privacy_permit_remove(session->account, state->who, TRUE);
+ purple_account_privacy_permit_remove(session->account, state->who, TRUE);
msn_add_contact_to_list(session, NULL, state->who, MSN_LIST_BL);
} else if (state->list_id == MSN_LIST_BL) {
- purple_privacy_deny_remove(session->account, state->who, TRUE);
+ purple_account_privacy_deny_remove(session->account, state->who, TRUE);
msn_add_contact_to_list(session, NULL, state->who, MSN_LIST_AL);
}
@@ -1723,7 +1728,7 @@ msn_del_contact_from_list(MsnSession *session, MsnCallbackState *state,
MsnSoapPartnerScenarioText[partner_scenario],
MsnMemberRole[list], member);
- state->body = xmlnode_from_str(body, -1);
+ state->body = purple_xmlnode_from_str(body, -1);
state->post_action = MSN_DELETE_MEMBER_FROM_LIST_SOAP_ACTION;
state->post_url = MSN_SHARE_POST_URL;
state->cb = msn_del_contact_from_list_read_cb;
@@ -1738,12 +1743,12 @@ msn_add_contact_to_list_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
gpointer data)
{
MsnCallbackState *state = data;
- xmlnode *fault;
+ PurpleXmlNode *fault;
/* We don't know how to respond to this faultcode, so log it */
- fault = xmlnode_get_child(resp->xml, "Body/Fault");
+ fault = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault");
if (fault != NULL) {
- char *fault_str = xmlnode_to_str(fault, NULL);
+ char *fault_str = purple_xmlnode_to_str(fault, NULL);
purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
msn_contact_operation_str(state->action), fault_str);
g_free(fault_str);
@@ -1764,9 +1769,9 @@ msn_add_contact_to_list_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
if (state->action & MSN_DENIED_BUDDY) {
msn_add_contact_to_list(state->session, NULL, state->who, MSN_LIST_BL);
} else if (state->list_id == MSN_LIST_AL) {
- purple_privacy_permit_add(state->session->account, state->who, TRUE);
+ purple_account_privacy_permit_add(state->session->account, state->who, TRUE);
} else if (state->list_id == MSN_LIST_BL) {
- purple_privacy_deny_add(state->session->account, state->who, TRUE);
+ purple_account_privacy_deny_add(state->session->account, state->who, TRUE);
}
}
}
@@ -1807,7 +1812,7 @@ msn_add_contact_to_list(MsnSession *session, MsnCallbackState *state,
MsnSoapPartnerScenarioText[partner_scenario],
MsnMemberRole[list], member);
- state->body = xmlnode_from_str(body, -1);
+ state->body = purple_xmlnode_from_str(body, -1);
state->post_action = MSN_ADD_MEMBER_TO_LIST_SOAP_ACTION;
state->post_url = MSN_SHARE_POST_URL;
state->cb = msn_add_contact_to_list_read_cb;
@@ -1833,7 +1838,7 @@ msn_get_gleams(MsnSession *session)
purple_debug_info("msn", "msn get gleams info...\n");
state = msn_callback_state_new(session);
- state->body = xmlnode_from_str(MSN_GLEAMS_TEMPLATE, -1);
+ state->body = purple_xmlnode_from_str(MSN_GLEAMS_TEMPLATE, -1);
state->post_action = MSN_GET_GLEAMS_SOAP_ACTION;
state->post_url = MSN_ADDRESS_BOOK_POST_URL;
state->cb = msn_gleams_read_cb;
@@ -1852,12 +1857,12 @@ msn_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
MsnCallbackState *state = data;
MsnSession *session;
MsnUserList *userlist;
- xmlnode *fault;
+ PurpleXmlNode *fault;
/* We don't know how to respond to this faultcode, so log it */
- fault = xmlnode_get_child(resp->xml, "Body/Fault");
+ fault = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault");
if (fault != NULL) {
- char *fault_str = xmlnode_to_str(fault, NULL);
+ char *fault_str = purple_xmlnode_to_str(fault, NULL);
purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
msn_contact_operation_str(state->action), fault_str);
g_free(fault_str);
@@ -1882,11 +1887,11 @@ msn_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
/* the response is taken from
http://telepathy.freedesktop.org/wiki/Pymsn/MSNP/ContactListActions
should copy it to msnpiki some day */
- xmlnode *guid_node = xmlnode_get_child(resp->xml,
+ PurpleXmlNode *guid_node = purple_xmlnode_get_child(msn_soap_message_get_xml(resp),
"Body/ABGroupAddResponse/ABGroupAddResult/guid");
if (guid_node) {
- char *guid = xmlnode_get_data(guid_node);
+ char *guid = purple_xmlnode_get_data(guid_node);
/* create and add the new group to the userlist */
purple_debug_info("msn", "Adding group %s with guid = %s to the userlist\n", state->new_group_name, guid);
@@ -1945,7 +1950,7 @@ msn_add_group(MsnSession *session, MsnCallbackState *state, const char* group_na
escaped_group_name = g_markup_escape_text(group_name, -1);
body = g_strdup_printf(MSN_GROUP_ADD_TEMPLATE, escaped_group_name);
- state->body = xmlnode_from_str(body, -1);
+ state->body = purple_xmlnode_from_str(body, -1);
state->post_action = MSN_GROUP_ADD_SOAP_ACTION;
state->post_url = MSN_ADDRESS_BOOK_POST_URL;
state->cb = msn_group_read_cb;
@@ -1989,7 +1994,7 @@ msn_del_group(MsnSession *session, const gchar *group_name)
body = g_strdup_printf(MSN_GROUP_DEL_TEMPLATE, guid);
- state->body = xmlnode_from_str(body, -1);
+ state->body = purple_xmlnode_from_str(body, -1);
state->post_action = MSN_GROUP_DEL_SOAP_ACTION;
state->post_url = MSN_ADDRESS_BOOK_POST_URL;
state->cb = msn_group_read_cb;
@@ -2033,7 +2038,7 @@ msn_contact_rename_group(MsnSession *session, const char *old_group_name, const
escaped_group_name = g_markup_escape_text(new_group_name, -1);
body = g_strdup_printf(MSN_GROUP_RENAME_TEMPLATE, guid, escaped_group_name);
- state->body = xmlnode_from_str(body, -1);
+ state->body = purple_xmlnode_from_str(body, -1);
state->post_action = MSN_GROUP_RENAME_SOAP_ACTION;
state->post_url = MSN_ADDRESS_BOOK_POST_URL;
state->cb = msn_group_read_cb;
diff --git a/libpurple/protocols/msn/contact.h b/libpurple/protocols/msn/contact.h
index 33985281a4..c204afd5a7 100644
--- a/libpurple/protocols/msn/contact.h
+++ b/libpurple/protocols/msn/contact.h
@@ -684,8 +684,8 @@ struct _MsnCallbackState
MsnListId list_id;
MsnCallbackAction action;
MsnSession *session;
- xmlnode *body;
- xmlnode *token;
+ PurpleXmlNode *body;
+ PurpleXmlNode *token;
const gchar *post_action;
const gchar *post_url;
MsnSoapCallback cb;
diff --git a/libpurple/protocols/msn/directconn.c b/libpurple/protocols/msn/directconn.c
index 63653def73..cc0a90d794 100644
--- a/libpurple/protocols/msn/directconn.c
+++ b/libpurple/protocols/msn/directconn.c
@@ -23,7 +23,7 @@
*/
#include "internal.h"
-#include "cipher.h"
+#include "ciphers/sha1hash.h"
#include "debug.h"
#include "msn.h"
@@ -44,11 +44,10 @@ msn_dc_calculate_nonce_hash(MsnDirectConnNonceType type,
guchar digest[20];
if (type == DC_NONCE_SHA1) {
- PurpleCipher *cipher = purple_ciphers_find_cipher("sha1");
- PurpleCipherContext *context = purple_cipher_context_new(cipher, NULL);
- purple_cipher_context_append(context, nonce, nonce_len);
- purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
- purple_cipher_context_destroy(context);
+ PurpleHash *hash = purple_sha1_hash_new();
+ purple_hash_append(hash, nonce, nonce_len);
+ purple_hash_digest(hash, digest, sizeof(digest));
+ g_object_unref(hash);
} else if (type == DC_NONCE_PLAIN) {
memcpy(digest, nonce, nonce_len);
} else {
@@ -85,12 +84,13 @@ msn_dc_calculate_nonce_hash(MsnDirectConnNonceType type,
static void
msn_dc_generate_nonce(MsnDirectConn *dc)
{
- guint32 *nonce;
int i;
- nonce = (guint32 *)&dc->nonce;
- for (i = 0; i < 4; i++)
- nonce[i] = rand();
+ for (i = 0; i < 4; i++) {
+ guint32 randval = g_random_int();
+ memcpy(dc->nonce + sizeof(guint32) * i,
+ &randval, sizeof(guint32));
+ }
msn_dc_calculate_nonce_hash(dc->nonce_type, dc->nonce, sizeof(dc->nonce), dc->nonce_hash);
@@ -406,7 +406,7 @@ msn_dc_send_cb(gpointer data, gint fd, PurpleInputCondition cond)
dc->progress = TRUE;
dc->msg_pos += bytes_sent;
- if (dc->msg_pos == p->length) {
+ if ((guint32)dc->msg_pos == p->length) {
if (p->sent_cb != NULL)
p->sent_cb(p);
@@ -662,7 +662,8 @@ msn_dc_recv_cb(gpointer data, gint fd, PurpleInputCondition cond)
/* Wait for packet length */
while (dc->in_pos >= 4) {
- packet_length = GUINT32_FROM_LE(*((guint32*)dc->in_buffer));
+ memcpy(&packet_length, dc->in_buffer, sizeof(packet_length));
+ packet_length = GUINT32_FROM_LE(packet_length);
if (packet_length > DC_MAX_PACKET_SIZE) {
/* Oversized packet */
@@ -671,7 +672,7 @@ msn_dc_recv_cb(gpointer data, gint fd, PurpleInputCondition cond)
}
/* Wait for the whole packet to arrive */
- if (dc->in_pos < 4 + packet_length)
+ if ((guint32)dc->in_pos < 4 + packet_length)
return;
switch (msn_dc_process_packet(dc, packet_length)) {
@@ -685,7 +686,7 @@ msn_dc_recv_cb(gpointer data, gint fd, PurpleInputCondition cond)
}
- if (dc->in_pos > packet_length + 4) {
+ if ((guint32)dc->in_pos > packet_length + 4) {
g_memmove(dc->in_buffer, dc->in_buffer + 4 + packet_length, dc->in_pos - packet_length - 4);
}
diff --git a/libpurple/protocols/msn/directconn.h b/libpurple/protocols/msn/directconn.h
index 41a05d274e..ffffcc43e6 100644
--- a/libpurple/protocols/msn/directconn.h
+++ b/libpurple/protocols/msn/directconn.h
@@ -28,7 +28,7 @@ typedef struct _MsnDirectConn MsnDirectConn;
#include "network.h"
#include "proxy.h"
-#include "circbuffer.h"
+#include "circularbuffer.h"
#include "slp.h"
#include "slplink.h"
diff --git a/libpurple/protocols/msn/error.c b/libpurple/protocols/msn/error.c
index 68690ef384..379f61f261 100644
--- a/libpurple/protocols/msn/error.c
+++ b/libpurple/protocols/msn/error.c
@@ -273,8 +273,12 @@ msn_error_handle(MsnSession *session, unsigned int type)
msn_error_get_text(type, &debug));
if (debug)
purple_debug_warning("msn", "error %d: %s\n", type, buf);
- else
- purple_notify_error(session->account->gc, NULL, buf, NULL);
+ else {
+ purple_notify_error(
+ purple_account_get_connection(session->account), NULL,
+ buf, NULL, purple_request_cpar_from_account(
+ session->account));
+ }
g_free(buf);
}
@@ -290,12 +294,12 @@ msn_complete_sync_issue(MsnAddRemData *data)
PurpleGroup *group = NULL;
if (data->group != NULL)
- group = purple_find_group(data->group);
+ group = purple_blist_find_group(data->group);
if (group != NULL)
- buddy = purple_find_buddy_in_group(data->session->account, data->who, group);
+ buddy = purple_blist_find_buddy_in_group(data->session->account, data->who, group);
else
- buddy = purple_find_buddy(data->session->account, data->who);
+ buddy = purple_blist_find_buddy(data->session->account, data->who);
if (buddy != NULL)
purple_blist_remove_buddy(buddy);
@@ -373,7 +377,7 @@ msn_error_sync_issue(MsnSession *session, const char *passport,
}
purple_request_action(gc, NULL, msg, reason, PURPLE_DEFAULT_ACTION_NONE,
- account, data->who, NULL,
+ purple_request_cpar_from_account(account),
data, 2,
_("Yes"), G_CALLBACK(msn_add_cb),
_("No"), G_CALLBACK(msn_rem_cb));
diff --git a/libpurple/protocols/msn/xfer.c b/libpurple/protocols/msn/ft.c
index 4da276dacd..2816fba3bd 100644
--- a/libpurple/protocols/msn/xfer.c
+++ b/libpurple/protocols/msn/ft.c
@@ -1,5 +1,5 @@
/**
- * @file xfer.c MSN File Transfer functions
+ * @file ft.c MSN File Transfer functions
*
* purple
*
@@ -27,7 +27,7 @@
#include "msnutils.h"
#include "sbconn.h"
-#include "xfer.h"
+#include "ft.h"
/**************************************************************************
* Xfer
@@ -42,7 +42,7 @@ msn_xfer_init(PurpleXfer *xfer)
purple_debug_info("msn", "xfer_init\n");
- slpcall = xfer->data;
+ slpcall = purple_xfer_get_protocol_data(xfer);
/* Send Ok */
content = g_strdup_printf("SessionID: %lu\r\n\r\n",
@@ -62,9 +62,9 @@ msn_xfer_cancel(PurpleXfer *xfer)
char *content;
g_return_if_fail(xfer != NULL);
- g_return_if_fail(xfer->data != NULL);
- slpcall = xfer->data;
+ slpcall = purple_xfer_get_protocol_data(xfer);
+ g_return_if_fail(slpcall != NULL);
if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL)
{
@@ -83,7 +83,7 @@ msn_xfer_cancel(PurpleXfer *xfer)
g_free(content);
msn_slplink_send_queued_slpmsgs(slpcall->slplink);
- if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND)
+ if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND)
slpcall->wasted = TRUE;
else
msn_slpcall_destroy(slpcall);
@@ -100,9 +100,9 @@ msn_xfer_write(const guchar *data, gsize len, PurpleXfer *xfer)
g_return_val_if_fail(data != NULL, -1);
g_return_val_if_fail(len > 0, -1);
- g_return_val_if_fail(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND, -1);
+ g_return_val_if_fail(purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND, -1);
- slpcall = xfer->data;
+ slpcall = purple_xfer_get_protocol_data(xfer);
/* Not sure I trust it'll be there */
g_return_val_if_fail(slpcall != NULL, -1);
@@ -116,7 +116,7 @@ msn_xfer_write(const guchar *data, gsize len, PurpleXfer *xfer)
}
gssize
-msn_xfer_read(guchar **data, PurpleXfer *xfer)
+msn_xfer_read(guchar **data, gsize size, PurpleXfer *xfer)
{
MsnSlpCall *slpcall;
gsize len;
@@ -124,9 +124,9 @@ msn_xfer_read(guchar **data, PurpleXfer *xfer)
g_return_val_if_fail(xfer != NULL, -1);
g_return_val_if_fail(data != NULL, -1);
- g_return_val_if_fail(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE, -1);
+ g_return_val_if_fail(purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE, -1);
- slpcall = xfer->data;
+ slpcall = purple_xfer_get_protocol_data(xfer);
/* Not sure I trust it'll be there */
g_return_val_if_fail(slpcall != NULL, -1);
@@ -166,7 +166,7 @@ msn_file_context_to_wire(MsnFileContext *context)
{
gchar *ret, *tmp;
- tmp = ret = g_new(gchar, MSN_FILE_CONTEXT_SIZE + context->preview_len + 1);
+ tmp = ret = g_new(gchar, MSN_FILE_CONTEXT_SIZE_V2 + context->preview_len + 1);
msn_push32le(tmp, context->length);
msn_push32le(tmp, context->version);
@@ -174,9 +174,15 @@ msn_file_context_to_wire(MsnFileContext *context)
msn_push32le(tmp, context->type);
memcpy(tmp, context->file_name, MAX_FILE_NAME_LEN * 2);
tmp += MAX_FILE_NAME_LEN * 2;
+#if 0
memcpy(tmp, context->unknown1, sizeof(context->unknown1));
tmp += sizeof(context->unknown1);
msn_push32le(tmp, context->unknown2);
+#else
+ memset(tmp, 0, sizeof(gchar[30]));
+ tmp += sizeof(gchar[30]);
+ msn_push32le(tmp, 0xffffffff);
+#endif
if (context->preview) {
memcpy(tmp, context->preview, context->preview_len);
}
@@ -190,21 +196,30 @@ msn_file_context_from_wire(const char *buf, gsize len)
{
MsnFileContext *context;
- if (!buf || len < MSN_FILE_CONTEXT_SIZE)
+ if (!buf || len < MSN_FILE_CONTEXT_SIZE_V0)
return NULL;
context = g_new(MsnFileContext, 1);
context->length = msn_pop32le(buf);
context->version = msn_pop32le(buf);
- if (context->version == 2) {
+ if (context->version == 0) {
+ if (context->length != MSN_FILE_CONTEXT_SIZE_V0) {
+ g_free(context);
+ return NULL;
+ }
+ } else if (context->version == 2) {
/* The length field is broken for this version. No check. */
- context->length = MSN_FILE_CONTEXT_SIZE;
+ context->length = MSN_FILE_CONTEXT_SIZE_V2;
+ if (len < MSN_FILE_CONTEXT_SIZE_V2) {
+ g_free(context);
+ return NULL;
+ }
} else if (context->version == 3) {
- if (context->length != MSN_FILE_CONTEXT_SIZE + 63) {
+ if (context->length != MSN_FILE_CONTEXT_SIZE_V3) {
g_free(context);
return NULL;
- } else if (len < MSN_FILE_CONTEXT_SIZE + 63) {
+ } else if (len < MSN_FILE_CONTEXT_SIZE_V3) {
g_free(context);
return NULL;
}
@@ -218,9 +233,15 @@ msn_file_context_from_wire(const char *buf, gsize len)
context->type = msn_pop32le(buf);
memcpy(context->file_name, buf, MAX_FILE_NAME_LEN * 2);
buf += MAX_FILE_NAME_LEN * 2;
- memcpy(context->unknown1, buf, sizeof(context->unknown1));
- buf += sizeof(context->unknown1);
- context->unknown2 = msn_pop32le(buf);
+ if (context->version > 0) {
+#if 0
+ memcpy(context->unknown1, buf, sizeof(context->unknown1));
+ buf += sizeof(context->unknown1);
+ context->unknown2 = msn_pop32le(buf);
+#else
+ buf += sizeof(gchar[30]) + sizeof(guint32);
+#endif
+ }
if (context->type == 0 && len > context->length) {
context->preview_len = len - context->length;
diff --git a/libpurple/protocols/msn/xfer.h b/libpurple/protocols/msn/ft.h
index 23e19f37c3..ac5bb353a1 100644
--- a/libpurple/protocols/msn/xfer.h
+++ b/libpurple/protocols/msn/ft.h
@@ -1,5 +1,5 @@
/**
- * @file xfer.h MSN File Transfer functions
+ * @file ft.h MSN File Transfer functions
*
* purple
*
@@ -22,8 +22,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#ifndef MSN_XFER_H
-#define MSN_XFER_H
+#ifndef MSN_FT_H
+#define MSN_FT_H
#include "slpcall.h"
@@ -39,19 +39,23 @@ typedef struct
guint64 file_size; /*< Size of file */
guint32 type; /*< Transfer type */
gunichar2 file_name[MAX_FILE_NAME_LEN]; /*< Self-explanatory */
+#if 0
gchar unknown1[30]; /*< Used somehow for background sharing */
guint32 unknown2; /*< Possibly for background sharing as well */
+#endif
gchar *preview; /*< File preview data, 96x96 PNG */
gsize preview_len;
} MsnFileContext;
-#define MSN_FILE_CONTEXT_SIZE (4*4 + 1*8 + 2*MAX_FILE_NAME_LEN + 30)
+#define MSN_FILE_CONTEXT_SIZE_V0 (4*3 + 1*8 + 2*MAX_FILE_NAME_LEN)
+#define MSN_FILE_CONTEXT_SIZE_V2 (MSN_FILE_CONTEXT_SIZE_V0 + 4*1 + 30)
+#define MSN_FILE_CONTEXT_SIZE_V3 (MSN_FILE_CONTEXT_SIZE_V2 + 63)
void msn_xfer_init(PurpleXfer *xfer);
void msn_xfer_cancel(PurpleXfer *xfer);
gssize msn_xfer_write(const guchar *data, gsize len, PurpleXfer *xfer);
-gssize msn_xfer_read(guchar **data, PurpleXfer *xfer);
+gssize msn_xfer_read(guchar **data, gsize size, PurpleXfer *xfer);
void msn_xfer_completed_cb(MsnSlpCall *slpcall,
const guchar *body, gsize size);
@@ -63,5 +67,5 @@ msn_file_context_to_wire(MsnFileContext *context);
MsnFileContext *
msn_file_context_from_wire(const char *buf, gsize len);
-#endif /* MSN_XFER_H */
+#endif /* MSN_FT_H */
diff --git a/libpurple/protocols/msn/group.h b/libpurple/protocols/msn/group.h
index 620d24124f..1f11a72ce7 100644
--- a/libpurple/protocols/msn/group.h
+++ b/libpurple/protocols/msn/group.h
@@ -50,7 +50,7 @@ struct _MsnGroup
};
/**************************************************************************
- ** @name Group API *
+ ** Group API *
**************************************************************************/
/*@{*/
diff --git a/libpurple/protocols/msn/history.c b/libpurple/protocols/msn/history.c
index 82488ce102..2b23af5ba1 100644
--- a/libpurple/protocols/msn/history.c
+++ b/libpurple/protocols/msn/history.c
@@ -68,7 +68,7 @@ void
msn_history_add(MsnHistory *history, MsnTransaction *trans)
{
GQueue *queue;
- int max_elems;
+ gsize max_elems;
g_return_if_fail(history != NULL);
g_return_if_fail(trans != NULL);
diff --git a/libpurple/protocols/msn/httpconn.c b/libpurple/protocols/msn/httpconn.c
index 5bdcd61c15..5d386befef 100644
--- a/libpurple/protocols/msn/httpconn.c
+++ b/libpurple/protocols/msn/httpconn.c
@@ -1,5 +1,5 @@
/**
- * @file httpconn.c HTTP connection method
+ * @file httpconn.h HTTP-tunnelled connections
*
* purple
*
@@ -19,721 +19,389 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#include "msn.h"
-#include "debug.h"
-#include "httpconn.h"
-
-typedef struct
-{
- MsnHttpConn *httpconn;
- char *body;
- size_t body_len;
-} MsnHttpQueueData;
-
-static void
-msn_httpconn_process_queue(MsnHttpConn *httpconn)
-{
- httpconn->waiting_response = FALSE;
-
- if (httpconn->queue != NULL)
- {
- MsnHttpQueueData *queue_data;
- queue_data = (MsnHttpQueueData *)httpconn->queue->data;
+#include "httpconn.h"
- httpconn->queue = g_list_remove(httpconn->queue, queue_data);
+#include "debug.h"
+#include "glibcompat.h"
+#include "http.h"
- msn_httpconn_write(queue_data->httpconn,
- queue_data->body,
- queue_data->body_len);
+#include "msn.h"
- g_free(queue_data->body);
- g_free(queue_data);
- }
-}
+typedef struct _MsnHttpConnWriteQueueElement MsnHttpConnWriteQueueElement;
-static gboolean
-msn_httpconn_parse_data(MsnHttpConn *httpconn, const char *buf,
- size_t size, char **ret_buf, size_t *ret_size,
- gboolean *error)
+struct _MsnHttpConn
{
- const char *s, *c;
- char *header, *body;
- const char *body_start;
- char *tmp;
- size_t body_len = 0;
-
- g_return_val_if_fail(httpconn != NULL, FALSE);
- g_return_val_if_fail(buf != NULL, FALSE);
- g_return_val_if_fail(size > 0, FALSE);
- g_return_val_if_fail(ret_buf != NULL, FALSE);
- g_return_val_if_fail(ret_size != NULL, FALSE);
- g_return_val_if_fail(error != NULL, FALSE);
-
-#if 0
- purple_debug_info("msn", "HTTP: parsing data {%s}\n", buf);
-#endif
-
- /* Healthy defaults. */
- body = NULL;
-
- *ret_buf = NULL;
- *ret_size = 0;
- *error = FALSE;
-
- /* First, some tests to see if we have a full block of stuff. */
- if (((strncmp(buf, "HTTP/1.1 200 OK\r\n", 17) != 0) &&
- (strncmp(buf, "HTTP/1.1 100 Continue\r\n", 23) != 0)) &&
- ((strncmp(buf, "HTTP/1.0 200 OK\r\n", 17) != 0) &&
- (strncmp(buf, "HTTP/1.0 100 Continue\r\n", 23) != 0)))
- {
- *error = TRUE;
-
- return FALSE;
- }
-
- if (strncmp(buf, "HTTP/1.1 100 Continue\r\n", 23) == 0)
- {
- if ((s = strstr(buf, "\r\n\r\n")) == NULL)
- return FALSE;
-
- s += 4;
-
- if (*s == '\0')
- {
- *ret_buf = g_strdup("");
- *ret_size = 0;
-
- msn_httpconn_process_queue(httpconn);
-
- return TRUE;
- }
-
- size -= (s - buf);
- buf = s;
- }
-
- if ((s = strstr(buf, "\r\n\r\n")) == NULL)
- /* Need to wait for the full HTTP header to arrive */
- return FALSE;
-
- s += 4; /* Skip \r\n\r\n */
- header = g_strndup(buf, s - buf);
- body_start = s;
- body_len = size - (body_start - buf);
-
- if ((s = purple_strcasestr(header, "Content-Length: ")) != NULL)
- {
- int tmp_len;
-
- s += strlen("Content-Length: ");
-
- if ((c = strchr(s, '\r')) == NULL)
- {
- g_free(header);
-
- return FALSE;
- }
-
- tmp = g_strndup(s, c - s);
- tmp_len = atoi(tmp);
- g_free(tmp);
-
- if (body_len != tmp_len)
- {
- /* Need to wait for the full packet to arrive */
-
- g_free(header);
-
-#if 0
- purple_debug_warning("msn",
- "body length (%d) != content length (%d)\n",
- body_len, tmp_len);
-#endif
-
- return FALSE;
- }
- }
-
- body = g_malloc(body_len + 1);
- memcpy(body, body_start, body_len);
- body[body_len] = '\0';
-
- if (purple_debug_is_verbose())
- purple_debug_misc("msn", "Incoming HTTP buffer (header): {%s}\n",
- header);
-
- /* Now we should be able to process the data. */
- if ((s = purple_strcasestr(header, "X-MSN-Messenger: ")) != NULL)
- {
- gchar *full_session_id = NULL, *gw_ip = NULL, *session_action = NULL;
- char *t, *session_id;
- char **elems, **cur, **tokens;
-
- full_session_id = gw_ip = session_action = NULL;
-
- s += strlen("X-MSN-Messenger: ");
-
- if ((c = strchr(s, '\r')) == NULL)
- {
- msn_session_set_error(httpconn->session,
- MSN_ERROR_HTTP_MALFORMED, NULL);
- purple_debug_error("msn", "Malformed X-MSN-Messenger field.\n{%s}\n",
- buf);
-
- g_free(header);
- g_free(body);
- return FALSE;
- }
-
- tmp = g_strndup(s, c - s);
-
- elems = g_strsplit(tmp, "; ", 0);
-
- for (cur = elems; *cur != NULL; cur++)
- {
- tokens = g_strsplit(*cur, "=", 2);
-
- if (strcmp(tokens[0], "SessionID") == 0) {
- g_free(full_session_id);
- full_session_id = tokens[1];
- } else if (strcmp(tokens[0], "GW-IP") == 0) {
- g_free(gw_ip);
- gw_ip = tokens[1];
- } else if (strcmp(tokens[0], "Session") == 0) {
- g_free(session_action);
- session_action = tokens[1];
- } else
- g_free(tokens[1]);
-
- g_free(tokens[0]);
- /* Don't free each of the tokens, only the array. */
- g_free(tokens);
- }
-
- g_strfreev(elems);
-
- g_free(tmp);
-
- t = full_session_id ? strchr(full_session_id, '.') : NULL;
- if (t != NULL)
- session_id = g_strndup(full_session_id, t - full_session_id);
- else {
- purple_debug_error("msn", "Malformed full_session_id[%s]\n",
- full_session_id ? full_session_id : NULL);
- session_id = g_strdup(full_session_id);
- }
-
- if (session_action == NULL || strcmp(session_action, "close") != 0)
- {
- g_free(httpconn->full_session_id);
- httpconn->full_session_id = full_session_id;
-
- g_free(httpconn->session_id);
- httpconn->session_id = session_id;
-
- g_free(httpconn->host);
- httpconn->host = gw_ip;
- }
- else
- {
- /* I'll be honest, I don't fully understand all this, but this
- * causes crashes, Stu. */
-#if 0
- MsnServConn *servconn;
+ MsnServConn *servconn;
- /* It's going to die. */
- /* poor thing */
+ gboolean connected;
+ gboolean is_disconnecting;
+ gboolean is_externally_disconnected;
- servconn = httpconn->servconn;
+ gchar *host_dest;
+ gchar *host_gw;
+ int port;
- if (servconn != NULL)
- servconn->wasted = TRUE;
-#endif
+ gchar *session_id; /* changes after every request */
- g_free(full_session_id);
- g_free(session_id);
- g_free(gw_ip);
- }
+ PurpleHttpConnection *current_request;
+ int polling_timer;
+ int finish_timer;
+ PurpleHttpKeepalivePool *keepalive_pool;
+ GSList *write_queue;
+};
- g_free(session_action);
- }
+struct _MsnHttpConnWriteQueueElement
+{
+ gchar *data;
+ size_t len;
+};
- g_free(header);
+static MsnHttpConnWriteQueueElement *
+msn_httpconn_writequeueelement_new(const gchar *data, size_t len)
+{
+ MsnHttpConnWriteQueueElement *wqe;
- *ret_buf = body;
- *ret_size = body_len;
+ g_return_val_if_fail(!(len > 0 && data == NULL), NULL);
- msn_httpconn_process_queue(httpconn);
+ wqe = g_new(MsnHttpConnWriteQueueElement, 1);
+ wqe->data = g_memdup(data, len);
+ wqe->len = len;
- return TRUE;
+ return wqe;
}
static void
-read_cb(gpointer data, gint source, PurpleInputCondition cond)
+msn_httpconn_writequeueelement_free(MsnHttpConnWriteQueueElement *wqe)
{
- MsnHttpConn *httpconn;
- MsnServConn *servconn;
- char buf[MSN_BUF_LEN];
- gssize len;
- char *result_msg = NULL;
- size_t result_len = 0;
- gboolean error = FALSE;
-
- httpconn = data;
- servconn = httpconn->servconn;
-
- if (servconn->type == MSN_SERVCONN_NS)
- servconn->session->account->gc->last_received = time(NULL);
-
- len = read(httpconn->fd, buf, sizeof(buf) - 1);
- if (len < 0 && errno == EAGAIN)
- return;
- if (len <= 0) {
- purple_debug_error("msn", "HTTP: servconn %03d read error, "
- "len: %" G_GSSIZE_FORMAT ", errno: %d, error: %s\n",
- servconn->num, len, error, g_strerror(errno));
- msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_READ, NULL);
-
- return;
- }
-
- buf[len] = '\0';
-
- httpconn->rx_buf = g_realloc(httpconn->rx_buf, len + httpconn->rx_len + 1);
- memcpy(httpconn->rx_buf + httpconn->rx_len, buf, len + 1);
- httpconn->rx_len += len;
-
- if (!msn_httpconn_parse_data(httpconn, httpconn->rx_buf, httpconn->rx_len,
- &result_msg, &result_len, &error))
- {
- /* Either we must wait for more input, or something went wrong */
- if (error)
- msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_READ, NULL);
-
- return;
- }
-
- if (error)
- {
- purple_debug_error("msn", "HTTP: Special error\n");
- msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_READ, NULL);
-
- return;
- }
-
- g_free(httpconn->rx_buf);
- httpconn->rx_buf = NULL;
- httpconn->rx_len = 0;
-
- if (result_len == 0)
- {
- /* Nothing to do here */
-#if 0
- purple_debug_info("msn", "HTTP: nothing to do here\n");
-#endif
- g_free(result_msg);
- return;
- }
-
- g_free(servconn->rx_buf);
- servconn->rx_buf = result_msg;
- servconn->rx_len = result_len;
-
- msn_servconn_process_data(servconn);
+ g_free(wqe->data);
+ g_free(wqe);
}
static void
-httpconn_write_cb(gpointer data, gint source, PurpleInputCondition cond)
+msn_httpconn_writequeueelement_process(MsnHttpConn *httpconn)
{
- MsnHttpConn *httpconn;
- gssize ret;
- int writelen;
+ MsnHttpConnWriteQueueElement *wqe;
- httpconn = data;
- writelen = purple_circ_buffer_get_max_read(httpconn->tx_buf);
+ g_return_if_fail(httpconn != NULL);
- if (writelen == 0)
+ if (httpconn->current_request != NULL || httpconn->finish_timer > 0)
{
- purple_input_remove(httpconn->tx_handler);
- httpconn->tx_handler = 0;
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("msn", "cannot process queue: "
+ "there is a request already running\n");
+ }
return;
}
-
- ret = write(httpconn->fd, httpconn->tx_buf->outptr, writelen);
- if (ret <= 0)
- {
- if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
- /* No worries */
- return;
-
- /* Error! */
- msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_WRITE, NULL);
+ if (httpconn->write_queue == NULL)
return;
- }
- purple_circ_buffer_mark_read(httpconn->tx_buf, ret);
-
- /* TODO: I don't think these 2 lines are needed. Remove them? */
- if (ret == writelen)
- httpconn_write_cb(data, source, cond);
+ wqe = httpconn->write_queue->data;
+ httpconn->write_queue = g_slist_remove(httpconn->write_queue, wqe);
+ msn_httpconn_write(httpconn, wqe->data, wqe->len);
+ msn_httpconn_writequeueelement_free(wqe);
}
-static gboolean
-write_raw(MsnHttpConn *httpconn, const char *data, size_t data_len)
+MsnHttpConn *
+msn_httpconn_new(MsnServConn *servconn)
{
- gssize res; /* result of the write operation */
+ MsnHttpConn *httpconn;
- if (httpconn->tx_handler == 0)
- res = write(httpconn->fd, data, data_len);
- else
- {
- res = -1;
- errno = EAGAIN;
- }
+ g_return_val_if_fail(servconn != NULL, NULL);
- if ((res <= 0) && ((errno != EAGAIN) && (errno != EWOULDBLOCK)))
- {
- msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_WRITE, NULL);
- return FALSE;
- }
+ httpconn = g_new0(MsnHttpConn, 1);
- if (res < 0 || res < data_len)
- {
- if (res < 0)
- res = 0;
- if (httpconn->tx_handler == 0 && httpconn->fd)
- httpconn->tx_handler = purple_input_add(httpconn->fd,
- PURPLE_INPUT_WRITE, httpconn_write_cb, httpconn);
- purple_circ_buffer_append(httpconn->tx_buf, data + res,
- data_len - res);
- }
+ httpconn->servconn = servconn;
- return TRUE;
+ return httpconn;
}
-static char *
-msn_httpconn_proxy_auth(MsnHttpConn *httpconn)
+void
+msn_httpconn_destroy(MsnHttpConn *httpconn)
{
- PurpleAccount *account;
- PurpleProxyInfo *gpi;
- const char *username, *password;
- char *auth = NULL;
-
- account = httpconn->session->account;
-
- gpi = purple_proxy_get_setup(account);
-
- if (gpi == NULL || !(purple_proxy_info_get_type(gpi) == PURPLE_PROXY_HTTP ||
- purple_proxy_info_get_type(gpi) == PURPLE_PROXY_USE_ENVVAR))
- return NULL;
-
- username = purple_proxy_info_get_username(gpi);
- password = purple_proxy_info_get_password(gpi);
+ if (httpconn == NULL)
+ return;
- if (username != NULL) {
- char *tmp;
- auth = g_strdup_printf("%s:%s", username, password ? password : "");
- tmp = purple_base64_encode((const guchar *)auth, strlen(auth));
- g_free(auth);
- auth = g_strdup_printf("Proxy-Authorization: Basic %s\r\n", tmp);
- g_free(tmp);
- }
+ if (httpconn->connected)
+ msn_httpconn_disconnect(httpconn);
- return auth;
+ g_free(httpconn);
}
static gboolean
-msn_httpconn_poll(gpointer data)
+msn_httpconn_poll(gpointer _httpconn)
{
- MsnHttpConn *httpconn;
- char *header;
- char *auth;
+ MsnHttpConn *httpconn = _httpconn;
- httpconn = data;
+ if (!httpconn->connected) {
+ purple_debug_error("msn", "msn_httpconn_poll: invalid state\n");
+ return FALSE;
+ }
- g_return_val_if_fail(httpconn != NULL, FALSE);
+ /* There's no need to poll if the session is not fully established. */
+ if (!httpconn->session_id)
+ return TRUE;
- if ((httpconn->host == NULL) || (httpconn->full_session_id == NULL))
- {
- /* There's no need to poll if the session is not fully established */
+ /* There's no need to poll if we're already waiting for a response. */
+ if (httpconn->current_request || httpconn->finish_timer > 0)
return TRUE;
- }
- if (httpconn->waiting_response)
- {
- /* There's no need to poll if we're already waiting for a response */
+ if (httpconn->write_queue) {
+ purple_debug_warning("msn", "There are pending packets, but no "
+ "request running\n");
return TRUE;
}
- auth = msn_httpconn_proxy_auth(httpconn);
-
- header = g_strdup_printf(
- "POST http://%s/gateway/gateway.dll?Action=poll&SessionID=%s HTTP/1.1\r\n"
- "Accept: */*\r\n"
- "Accept-Language: en-us\r\n"
- "User-Agent: MSMSGS\r\n"
- "Host: %s\r\n"
- "Proxy-Connection: Keep-Alive\r\n"
- "%s" /* Proxy auth */
- "Connection: Keep-Alive\r\n"
- "Pragma: no-cache\r\n"
- "Content-Type: application/x-msn-messenger\r\n"
- "Content-Length: 0\r\n\r\n",
- httpconn->host,
- httpconn->full_session_id,
- httpconn->host,
- auth ? auth : "");
-
- g_free(auth);
-
- if (write_raw(httpconn, header, strlen(header)))
- httpconn->waiting_response = TRUE;
-
- g_free(header);
+ msn_httpconn_write(httpconn, NULL, 0);
return TRUE;
}
-gssize
-msn_httpconn_write(MsnHttpConn *httpconn, const char *body, size_t body_len)
+void
+msn_httpconn_connect(MsnHttpConn *httpconn, const gchar *host, int port)
{
- char *params;
- char *data;
- int header_len;
- char *auth;
- const char *server_types[] = { "NS", "SB" };
- const char *server_type;
- char *host;
- MsnServConn *servconn;
-
- /* TODO: remove http data from servconn */
+ g_return_if_fail(httpconn != NULL);
+ g_return_if_fail(host != NULL);
- g_return_val_if_fail(httpconn != NULL, 0);
- g_return_val_if_fail(body != NULL, 0);
- g_return_val_if_fail(body_len > 0, 0);
+ if (httpconn->connected) {
+ purple_debug_warning("msn", "http already (virtually) "
+ "connected\n");
+ return;
+ }
- servconn = httpconn->servconn;
+ httpconn->host_dest = g_strdup(host);
+ httpconn->port = port;
+ httpconn->connected = TRUE;
- if (httpconn->waiting_response)
- {
- MsnHttpQueueData *queue_data = g_new0(MsnHttpQueueData, 1);
+ httpconn->polling_timer = purple_timeout_add_seconds(2,
+ msn_httpconn_poll, httpconn);
+ httpconn->keepalive_pool = purple_http_keepalive_pool_new();
- queue_data->httpconn = httpconn;
- queue_data->body = g_memdup(body, body_len);
- queue_data->body_len = body_len;
+ msn_httpconn_writequeueelement_process(httpconn);
+}
- httpconn->queue = g_list_append(httpconn->queue, queue_data);
+void
+msn_httpconn_disconnect(MsnHttpConn *httpconn)
+{
+ g_return_if_fail(httpconn != NULL);
- return body_len;
+ if (!httpconn->connected) {
+ purple_debug_warning("msn", "http is not connected\n");
+ return;
}
+ httpconn->connected = FALSE;
+ httpconn->is_disconnecting = TRUE;
- server_type = server_types[servconn->type];
+ g_slist_free_full(httpconn->write_queue,
+ (GDestroyNotify)msn_httpconn_writequeueelement_free);
+ httpconn->write_queue = NULL;
- if (httpconn->virgin)
- {
- /* QuLogic: This doesn't look right to me, but it still seems to work */
- host = MSN_HTTPCONN_SERVER;
-
- /* The first time servconn->host is the host we should connect to. */
- params = g_strdup_printf("Action=open&Server=%s&IP=%s",
- server_type,
- servconn->host);
- httpconn->virgin = FALSE;
+ if (httpconn->polling_timer > 0) {
+ purple_timeout_remove(httpconn->polling_timer);
+ httpconn->polling_timer = 0;
}
- else
- {
- /* The rest of the times servconn->host is the gateway host. */
- host = httpconn->host;
-
- if (host == NULL || httpconn->full_session_id == NULL)
- {
- purple_debug_warning("msn", "Attempted HTTP write before session is established\n");
- return -1;
- }
- params = g_strdup_printf("SessionID=%s",
- httpconn->full_session_id);
+ if (httpconn->finish_timer > 0) {
+ purple_timeout_remove(httpconn->finish_timer);
+ httpconn->finish_timer = 0;
}
- auth = msn_httpconn_proxy_auth(httpconn);
-
- data = g_strdup_printf(
- "POST http://%s/gateway/gateway.dll?%s HTTP/1.1\r\n"
- "Accept: */*\r\n"
- "Accept-Language: en-us\r\n"
- "User-Agent: MSMSGS\r\n"
- "Host: %s\r\n"
- "Proxy-Connection: Keep-Alive\r\n"
- "%s" /* Proxy auth */
- "Connection: Keep-Alive\r\n"
- "Pragma: no-cache\r\n"
- "Content-Type: application/x-msn-messenger\r\n"
- "Content-Length: %d\r\n\r\n",
- host,
- params,
- host,
- auth ? auth : "",
- (int) body_len);
-
- g_free(params);
-
- g_free(auth);
+ purple_http_conn_cancel(httpconn->current_request);
+ httpconn->current_request = NULL;
- header_len = strlen(data);
- data = g_realloc(data, header_len + body_len);
- memcpy(data + header_len, body, body_len);
-
- if (write_raw(httpconn, data, header_len + body_len))
- httpconn->waiting_response = TRUE;
-
- g_free(data);
+ g_free(httpconn->host_dest);
+ httpconn->host_dest = NULL;
+ g_free(httpconn->host_gw);
+ httpconn->host_gw = NULL;
+ g_free(httpconn->session_id);
+ httpconn->session_id = NULL;
- return body_len;
+ purple_http_keepalive_pool_unref(httpconn->keepalive_pool);
+ httpconn->keepalive_pool = NULL;
+ httpconn->is_disconnecting = FALSE;
}
-MsnHttpConn *
-msn_httpconn_new(MsnServConn *servconn)
+static void
+msn_httpconn_parse_header(MsnHttpConn *httpconn, const gchar *header)
{
- MsnHttpConn *httpconn;
+ gchar *session_id = NULL, *gw_ip = NULL, *session_action = NULL;
+ gchar **elems;
+ int i;
- g_return_val_if_fail(servconn != NULL, NULL);
+ elems = g_strsplit(header, ";", 10);
- httpconn = g_new0(MsnHttpConn, 1);
+ for (i = 0; elems[i] != NULL; i++) {
+ gchar **tokens = g_strsplit(elems[i], "=", 2);
+ if (tokens[0] == NULL || tokens[1] == NULL) {
+ g_strfreev(tokens);
+ continue;
+ }
- purple_debug_info("msn", "new httpconn (%p)\n", httpconn);
+ g_strstrip(tokens[0]);
+ g_strstrip(tokens[1]);
- /* TODO: Remove this */
- httpconn->session = servconn->session;
+ if (g_strcmp0(tokens[0], "SessionID") == 0) {
+ g_free(session_id);
+ session_id = g_strdup(tokens[1]);
+ } else if (g_strcmp0(tokens[0], "GW-IP") == 0) {
+ g_free(gw_ip);
+ gw_ip = g_strdup(tokens[1]);
+ } else if (g_strcmp0(tokens[0], "Session") == 0) {
+ g_free(session_action);
+ session_action = g_strdup(tokens[1]);
+ }
- httpconn->servconn = servconn;
+ g_strfreev(tokens);
+ }
- httpconn->tx_buf = purple_circ_buffer_new(MSN_BUF_LEN);
- httpconn->tx_handler = 0;
+ g_strfreev(elems);
- httpconn->fd = -1;
+ if (session_id != NULL) {
+ g_free(httpconn->session_id);
+ httpconn->session_id = session_id;
+ }
- return httpconn;
+ if (gw_ip != NULL) {
+ g_free(httpconn->host_gw);
+ httpconn->host_gw = gw_ip;
+ }
+
+ if (g_strcmp0(session_action, "close") == 0) {
+ g_free(httpconn->session_id);
+ httpconn->session_id = NULL;
+ g_free(httpconn->host_gw);
+ httpconn->host_gw = NULL;
+ httpconn->is_externally_disconnected = TRUE;
+ }
+ g_free(session_action);
}
-void
-msn_httpconn_destroy(MsnHttpConn *httpconn)
+static gboolean
+msn_httpconn_read_finish(gpointer _httpconn)
{
- g_return_if_fail(httpconn != NULL);
-
- purple_debug_info("msn", "destroy httpconn (%p)\n", httpconn);
-
- if (httpconn->connected)
- msn_httpconn_disconnect(httpconn);
-
- g_free(httpconn->full_session_id);
-
- g_free(httpconn->session_id);
+ MsnHttpConn *httpconn = _httpconn;
- g_free(httpconn->host);
+ httpconn->finish_timer = 0;
- while (httpconn->queue != NULL) {
- MsnHttpQueueData *queue_data;
-
- queue_data = (MsnHttpQueueData *) httpconn->queue->data;
-
- httpconn->queue = g_list_delete_link(httpconn->queue, httpconn->queue);
-
- g_free(queue_data->body);
- g_free(queue_data);
+ if (httpconn->servconn->rx_len > 0) {
+ if (msn_servconn_process_data(httpconn->servconn) == NULL)
+ return FALSE;
}
- purple_circ_buffer_destroy(httpconn->tx_buf);
- if (httpconn->tx_handler > 0)
- purple_input_remove(httpconn->tx_handler);
+ msn_httpconn_writequeueelement_process(httpconn);
- g_free(httpconn);
+ return FALSE;
}
static void
-connect_cb(gpointer data, gint source, const gchar *error_message)
+msn_httpconn_read(PurpleHttpConnection *phc, PurpleHttpResponse *response,
+ gpointer _httpconn)
{
- MsnHttpConn *httpconn;
-
- httpconn = data;
- httpconn->connect_data = NULL;
- httpconn->fd = source;
-
- if (source >= 0)
- {
- httpconn->inpa = purple_input_add(httpconn->fd, PURPLE_INPUT_READ,
- read_cb, data);
-
- httpconn->timer = purple_timeout_add_seconds(2, msn_httpconn_poll, httpconn);
-
- msn_httpconn_process_queue(httpconn);
- }
- else
- {
- purple_debug_error("msn", "HTTP: Connection error: %s\n",
- error_message ? error_message : "(null)");
- msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_CONNECT, error_message);
+ MsnHttpConn *httpconn = _httpconn;
+ const gchar *msn_header;
+ const gchar *got_data;
+ size_t got_len;
+
+ if (!purple_http_response_is_successful(response)) {
+ httpconn->current_request = NULL;
+ msn_servconn_got_error(httpconn->servconn,
+ MSN_SERVCONN_ERROR_READ,
+ purple_http_response_get_error(response));
+ return;
}
-}
-gboolean
-msn_httpconn_connect(MsnHttpConn *httpconn, const char *host, int port)
-{
- g_return_val_if_fail(httpconn != NULL, FALSE);
- g_return_val_if_fail(host != NULL, FALSE);
- g_return_val_if_fail(port > 0, FALSE);
+ if (httpconn->servconn->type == MSN_SERVCONN_NS) {
+ purple_connection_update_last_received(
+ purple_account_get_connection(httpconn->servconn->
+ session->account));
+ }
- if (httpconn->connected)
- msn_httpconn_disconnect(httpconn);
+ msn_header = purple_http_response_get_header(response,
+ "X-MSN-Messenger");
+ if (msn_header != NULL)
+ msn_httpconn_parse_header(httpconn, msn_header);
- httpconn->connect_data = purple_proxy_connect(NULL, httpconn->session->account,
- host, 80, connect_cb, httpconn);
+ got_data = purple_http_response_get_data(response, &got_len);
- if (httpconn->connect_data != NULL)
- {
- httpconn->waiting_response = TRUE;
- httpconn->connected = TRUE;
- }
+ g_free(httpconn->servconn->rx_buf);
+ httpconn->servconn->rx_buf = g_memdup(got_data, got_len);
+ httpconn->servconn->rx_len = got_len;
- return httpconn->connected;
+ httpconn->current_request = NULL;
+ httpconn->finish_timer = purple_timeout_add(0, msn_httpconn_read_finish,
+ httpconn);
}
-void
-msn_httpconn_disconnect(MsnHttpConn *httpconn)
+ssize_t
+msn_httpconn_write(MsnHttpConn *httpconn, const gchar *data, size_t data_len)
{
- g_return_if_fail(httpconn != NULL);
-
- if (!httpconn->connected)
- return;
+ PurpleAccount *account;
+ PurpleHttpRequest *req;
+ static const gchar *server_types[] = { "NS", "SB" };
+ const gchar *server_type;
+ const gchar *host;
+ gchar *params;
+
+ g_return_val_if_fail(httpconn != NULL, -1);
+ g_return_val_if_fail(!(data == NULL && data_len > 0), -1);
+
+ if (httpconn->is_disconnecting) {
+ purple_debug_warning("msn", "http connection is in "
+ "disconnecting state\n");
+ return -1;
+ }
- if (httpconn->connect_data != NULL)
- {
- purple_proxy_connect_cancel(httpconn->connect_data);
- httpconn->connect_data = NULL;
+ if (httpconn->is_externally_disconnected) {
+ purple_debug_warning("msn", "http connection was externally "
+ "disconnected\n");
+ return -1;
}
- if (httpconn->timer)
- {
- purple_timeout_remove(httpconn->timer);
- httpconn->timer = 0;
+ if (httpconn->current_request != NULL) {
+ httpconn->write_queue = g_slist_append(httpconn->write_queue,
+ msn_httpconn_writequeueelement_new(data, data_len));
+ return data_len;
}
- if (httpconn->inpa > 0)
- {
- purple_input_remove(httpconn->inpa);
- httpconn->inpa = 0;
+ server_type = server_types[httpconn->servconn->type];
+
+ if (httpconn->session_id == NULL || httpconn->host_gw == NULL) {
+ /* I'm not sure, if this shouldn't be MSN_HTTPCONN_SERVER */
+ host = httpconn->host_dest;
+ params = g_strdup_printf("Action=open&Server=%s&IP=%s",
+ server_type, httpconn->host_dest);
+ } else {
+ host = httpconn->host_gw;
+ params = g_strdup_printf("SessionID=%s", httpconn->session_id);
}
- close(httpconn->fd);
- httpconn->fd = -1;
+ if (data_len == 0) {
+ gchar *tmp = params;
+ params = g_strdup_printf("Action=poll&%s", params);
+ g_free(tmp);
+ }
- g_free(httpconn->rx_buf);
- httpconn->rx_buf = NULL;
- httpconn->rx_len = 0;
+ account = httpconn->servconn->session->account;
+
+ req = purple_http_request_new(NULL);
+ purple_http_request_set_keepalive_pool(req, httpconn->keepalive_pool);
+ purple_http_request_set_url_printf(req,
+ "http://%s/gateway/gateway.dll?%s", host, params);
+ purple_http_request_set_method(req, "POST");
+ purple_http_request_header_set(req, "Accept-Language", "en-us");
+ purple_http_request_header_set(req, "User-Agent", "MSMSGS");
+ purple_http_request_header_set(req, "Pragma", "no-cache");
+ purple_http_request_header_set(req, "Content-Type",
+ "application/x-msn-messenger");
+ purple_http_request_set_contents(req, data, data_len);
+ httpconn->current_request = purple_http_request(
+ purple_account_get_connection(account), req,
+ msn_httpconn_read, httpconn);
+ purple_http_request_unref(req);
- httpconn->connected = FALSE;
+ g_free(params);
- /* msn_servconn_disconnect(httpconn->servconn); */
+ return data_len;
}
diff --git a/libpurple/protocols/msn/httpconn.h b/libpurple/protocols/msn/httpconn.h
index 381a27b85c..d3de04e77b 100644
--- a/libpurple/protocols/msn/httpconn.h
+++ b/libpurple/protocols/msn/httpconn.h
@@ -1,5 +1,5 @@
/**
- * @file httpconn.h HTTP connection
+ * @file httpconn.h HTTP-tunnelled connections
*
* purple
*
@@ -19,94 +19,63 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#ifndef MSN_HTTPCONN_H
#define MSN_HTTPCONN_H
typedef struct _MsnHttpConn MsnHttpConn;
-#include "circbuffer.h"
-#include "servconn.h"
-#include "session.h"
+#include "internal.h"
-/**
- * An HTTP Connection.
- */
-struct _MsnHttpConn
-{
- MsnSession *session; /**< The MSN Session. */
- MsnServConn *servconn; /**< The connection object. */
-
- PurpleProxyConnectData *connect_data;
-
- char *full_session_id; /**< The full session id. */
- char *session_id; /**< The trimmed session id. */
-
- int timer; /**< The timer for polling. */
-
- gboolean waiting_response; /**< The flag that states if we are waiting
- a response from the server. */
- gboolean connected; /**< The flag that states if the connection is on. */
- gboolean virgin; /**< The flag that states if this connection
- should specify the host (not gateway) to
- connect to. */
-
- char *host; /**< The HTTP gateway host. */
- GList *queue; /**< The queue of data chunks to write. */
-
- int fd; /**< The connection's file descriptor. */
- guint inpa; /**< The connection's input handler. */
-
- char *rx_buf; /**< The receive buffer. */
- int rx_len; /**< The receive buffer length. */
-
- PurpleCircBuffer *tx_buf;
- guint tx_handler;
-};
+#include "msn.h"
/**
* Creates a new HTTP connection object.
*
* @param servconn The connection object.
*
- * @return The new object.
+ * @return The new HTTP connection object.
*/
-MsnHttpConn *msn_httpconn_new(MsnServConn *servconn);
+MsnHttpConn *
+msn_httpconn_new(MsnServConn *servconn);
/**
* Destroys an HTTP connection object.
*
* @param httpconn The HTTP connection object.
*/
-void msn_httpconn_destroy(MsnHttpConn *httpconn);
+void
+msn_httpconn_destroy(MsnHttpConn *httpconn);
/**
- * Writes a chunk of data to the HTTP connection.
- *
- * @param servconn The server connection.
- * @param data The data to write.
- * @param data_len The size of the data to write.
+ * Virtually estabilishes the HTTP connection with a host.
*
- * @return The number of bytes written.
+ * @param httpconn The HTTP connection object.
+ * @param host The host to connect to.
+ * @param port The port to connect to (currently ignored).
*/
-gssize msn_httpconn_write(MsnHttpConn *httpconn, const char *data, size_t data_len);
+void
+msn_httpconn_connect(MsnHttpConn *httpconn, const gchar *host, int port);
/**
- * Connects the HTTP connection object to a host.
+ * Disconnects the HTTP connection object.
*
* @param httpconn The HTTP connection object.
- * @param host The host to connect to.
- * @param port The port to connect to.
*/
-gboolean msn_httpconn_connect(MsnHttpConn *httpconn,
- const char *host, int port);
+void
+msn_httpconn_disconnect(MsnHttpConn *httpconn);
/**
- * Disconnects the HTTP connection object.
+ * Writes a chunk of data to the HTTP connection.
*
- * @param httpconn The HTTP connection object.
+ * @param servconn The server connection.
+ * @param data The data to write.
+ * @param data_len The size of the data to write.
+ *
+ * @return The number of bytes written.
*/
-void msn_httpconn_disconnect(MsnHttpConn *httpconn);
+ssize_t
+msn_httpconn_write(MsnHttpConn *httpconn, const gchar *data, size_t data_len);
#endif /* MSN_HTTPCONN_H */
diff --git a/libpurple/protocols/msn/msg.c b/libpurple/protocols/msn/msg.c
index f22b434823..c40ddec71a 100644
--- a/libpurple/protocols/msn/msg.c
+++ b/libpurple/protocols/msn/msg.c
@@ -323,37 +323,30 @@ msn_message_new_from_cmd(MsnSession *session, MsnCommand *cmd)
char *
msn_message_gen_payload(MsnMessage *msg, size_t *ret_size)
{
+ GString *payload;
GList *l;
- char *n, *base, *end;
- int len;
- size_t body_len = 0;
+ size_t body_len;
const void *body;
g_return_val_if_fail(msg != NULL, NULL);
- len = MSN_BUF_LEN;
-
- base = n = end = g_malloc(len + 1);
- end += len;
+ /* 8192 is a reasonable guess at a large enough buffer to avoid realloc */
+ payload = g_string_sized_new(8192);
- /* Standard header. */
- if (msg->charset == NULL)
- {
- g_snprintf(n, len,
- "MIME-Version: 1.0\r\n"
- "Content-Type: %s\r\n",
- msg->content_type);
- }
- else
- {
- g_snprintf(n, len,
- "MIME-Version: 1.0\r\n"
- "Content-Type: %s; charset=%s\r\n",
- msg->content_type, msg->charset);
+ /* Standard header */
+ if (msg->charset == NULL) {
+ g_string_append_printf(payload,
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: %s\r\n",
+ msg->content_type);
+ } else {
+ g_string_append_printf(payload,
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: %s; charset=%s\r\n",
+ msg->content_type, msg->charset);
}
- n += strlen(n);
-
+ /* Headers */
for (l = msg->header_list; l != NULL; l = l->next)
{
const char *key;
@@ -362,31 +355,25 @@ msn_message_gen_payload(MsnMessage *msg, size_t *ret_size)
key = l->data;
value = msn_message_get_header_value(msg, key);
- g_snprintf(n, end - n, "%s: %s\r\n", key, value);
- n += strlen(n);
+ g_string_append_printf(payload, "%s: %s\r\n", key, value);
}
- if ((end - n) > 2)
- n += g_strlcpy(n, "\r\n", end - n);
+ /* End of headers */
+ g_string_append(payload, "\r\n");
+ /* Body */
body = msn_message_get_bin_data(msg, &body_len);
-
- if (body != NULL && (end - n) > body_len)
- {
- memcpy(n, body, body_len);
- n += body_len;
- *n = '\0';
+ if (body != NULL) {
+ g_string_append_len(payload, body, body_len);
}
- if (ret_size != NULL)
- {
- *ret_size = n - base;
-
- if (*ret_size > 1664)
- *ret_size = 1664;
+ if (ret_size != NULL) {
+ /* Use MIN to truncate the payload to 1664 bytes? Why do we do this?
+ It seems like it will lead to brokenness. */
+ *ret_size = MIN(payload->len, 1664);
}
- return base;
+ return g_string_free(payload, FALSE);
}
void
@@ -415,8 +402,7 @@ msn_message_set_bin_data(MsnMessage *msg, const void *data, size_t len)
if (len > 1664)
len = 1664;
- if (msg->body != NULL)
- g_free(msg->body);
+ g_free(msg->body);
if (data != NULL && len > 0)
{
@@ -663,7 +649,7 @@ msn_plain_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
const char *passport;
const char *value;
- gc = cmdproc->session->account->gc;
+ gc = purple_account_get_connection(cmdproc->session->account);
body = msn_message_get_bin_data(msg, &body_len);
body_enc = g_markup_escape_text(body, body_len);
@@ -708,7 +694,7 @@ msn_plain_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
if (swboard->current_users > 1 ||
((swboard->conv != NULL) &&
- purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT))
+ PURPLE_IS_CHAT_CONVERSATION(swboard->conv)))
{
/* If current_users is always ok as it should then there is no need to
* check if this is a chat. */
@@ -716,28 +702,28 @@ msn_plain_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
purple_debug_misc("msn", "plain_msg: current_users(%d)\n",
swboard->current_users);
- serv_got_chat_in(gc, swboard->chat_id, passport, 0, body_final,
- time(NULL));
+ purple_serv_got_chat_in(gc, swboard->chat_id, passport,
+ PURPLE_MESSAGE_RECV, body_final, time(NULL));
if (swboard->conv == NULL)
{
- swboard->conv = purple_find_chat(gc, swboard->chat_id);
+ swboard->conv = PURPLE_CONVERSATION(purple_conversations_find_chat(gc, swboard->chat_id));
swboard->flag |= MSN_SB_FLAG_IM;
}
}
- else if (!g_str_equal(passport, purple_account_get_username(gc->account)))
+ else if (!g_str_equal(passport, purple_account_get_username(purple_connection_get_account(gc))))
{
/* Don't im ourselves ... */
- serv_got_im(gc, passport, body_final, 0, time(NULL));
+ purple_serv_got_im(gc, passport, body_final, 0, time(NULL));
if (swboard->conv == NULL)
{
- swboard->conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
- passport, purple_connection_get_account(gc));
+ swboard->conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(
+ passport, purple_connection_get_account(gc)));
swboard->flag |= MSN_SB_FLAG_IM;
}
}
} else {
- serv_got_im(gc, passport, body_final, 0, time(NULL));
+ purple_serv_got_im(gc, passport, body_final, 0, time(NULL));
}
g_free(body_final);
@@ -749,7 +735,7 @@ msn_control_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
PurpleConnection *gc;
char *passport;
- gc = cmdproc->session->account->gc;
+ gc = purple_account_get_connection(cmdproc->session->account);
passport = msg->remote_user;
if (msn_message_get_header_value(msg, "TypingUser") == NULL)
@@ -760,13 +746,13 @@ msn_control_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
if (swboard->current_users == 1)
{
- serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT,
- PURPLE_TYPING);
+ purple_serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT,
+ PURPLE_IM_TYPING);
}
} else {
- serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT,
- PURPLE_TYPING);
+ purple_serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT,
+ PURPLE_IM_TYPING);
}
}
@@ -783,7 +769,7 @@ datacast_inform_user(MsnSwitchBoard *swboard, const char *who,
account = swboard->session->account;
pc = purple_account_get_connection(account);
- if ((b = purple_find_buddy(account, who)) != NULL)
+ if ((b = purple_blist_find_buddy(account, who)) != NULL)
username = g_markup_escape_text(purple_buddy_get_alias(b), -1);
else
username = g_markup_escape_text(who, -1);
@@ -798,22 +784,23 @@ datacast_inform_user(MsnSwitchBoard *swboard, const char *who,
if (swboard->conv == NULL) {
if (chat)
- swboard->conv = purple_find_chat(account->gc, swboard->chat_id);
+ swboard->conv = PURPLE_CONVERSATION(purple_conversations_find_chat(
+ purple_account_get_connection(account), swboard->chat_id));
else {
- swboard->conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
- who, account);
+ swboard->conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(
+ who, account));
if (swboard->conv == NULL)
- swboard->conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, who);
+ swboard->conv = PURPLE_CONVERSATION(purple_im_conversation_new(account, who));
}
}
if (chat)
- serv_got_chat_in(pc,
- purple_conv_chat_get_id(PURPLE_CONV_CHAT(swboard->conv)),
+ purple_serv_got_chat_in(pc,
+ purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(swboard->conv)),
who, PURPLE_MESSAGE_RECV|PURPLE_MESSAGE_SYSTEM, str,
time(NULL));
else
- serv_got_im(pc, who, str, PURPLE_MESSAGE_RECV|PURPLE_MESSAGE_SYSTEM,
+ purple_serv_got_im(pc, who, str, PURPLE_MESSAGE_RECV|PURPLE_MESSAGE_SYSTEM,
time(NULL));
g_free(str);
@@ -910,25 +897,33 @@ msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
}
static void
-got_emoticon(MsnSlpCall *slpcall,
- const guchar *data, gsize size)
+got_emoticon(MsnSlpCall *slpcall, const guchar *data, gsize size)
{
PurpleConversation *conv;
+ PurpleSmiley *smiley;
+ PurpleImage *image;
MsnSwitchBoard *swboard;
+ const gchar *shortcut;
swboard = slpcall->slplink->swboard;
conv = swboard->conv;
+ shortcut = slpcall->data_info;
- if (conv) {
- /* FIXME: it would be better if we wrote the data as we received it
- instead of all at once, calling write multiple times and
- close once at the very end
- */
- purple_conv_custom_smiley_write(conv, slpcall->data_info, data, size);
- purple_conv_custom_smiley_close(conv, slpcall->data_info );
- }
- if (purple_debug_is_verbose())
- purple_debug_info("msn", "Got smiley: %s\n", slpcall->data_info);
+ purple_debug_info("msn", "got smiley: %s", shortcut);
+
+ if (!conv)
+ return;
+
+ smiley = purple_conversation_get_remote_smiley(conv, shortcut);
+ g_return_if_fail(smiley);
+ image = purple_smiley_get_image(smiley);
+
+ /* FIXME: it would be better if we wrote the data as we received it
+ * instead of all at once, calling write multiple times and close once
+ * at the very end.
+ */
+ purple_image_transfer_write(image, (gpointer)data, size);
+ purple_image_transfer_close(image);
}
void msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
@@ -939,7 +934,7 @@ void msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
MsnObject *obj;
char **tokens;
char *smile, *body_str;
- const char *body, *who, *sha1;
+ const char *body, *who; /*, *sha1;*/
guint tok;
size_t body_len;
@@ -965,6 +960,8 @@ void msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
g_free(body_str);
for (tok = 0; tok < 9; tok += 2) {
+ PurpleSmiley *smiley;
+
if (tokens[tok] == NULL || tokens[tok + 1] == NULL) {
break;
}
@@ -976,7 +973,9 @@ void msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
break;
who = msn_object_get_creator(obj);
+#if 0
sha1 = msn_object_get_sha1(obj);
+#endif
slplink = msn_session_get_slplink(session, who);
if (slplink->swboard != swboard) {
@@ -995,21 +994,28 @@ void msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
/* If the conversation doesn't exist then this is a custom smiley
* used in the first message in a MSN conversation: we need to create
* the conversation now, otherwise the custom smiley won't be shown.
- * This happens because every GtkIMHtml has its own smiley tree: if
- * the conversation doesn't exist then we cannot associate the new
- * smiley with its GtkIMHtml widget. */
+ * This happens because every PurpleConversation has its own smiley
+ * tree: if the conversation doesn't exist then we cannot associate
+ * the new smiley with its PurpleConversation. */
if (!conv) {
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, session->account, who);
+ conv = PURPLE_CONVERSATION(purple_im_conversation_new(session->account, who));
}
- if (purple_conv_custom_smiley_add(conv, smile, "sha1", sha1, TRUE)) {
+ smiley = purple_conversation_add_remote_smiley(conv, smile);
+ if (smiley) { /* if not - it was already present */
+ /* TODO: cache lookup by "sha1" */
+
+ /* XXX: maybe handle end_cb and smiley download failures? */
+ purple_debug_info("msn", "requesting smiley: %s", smile);
msn_slplink_request_object(slplink, smile, got_emoticon, NULL, obj);
}
- msn_object_destroy(obj);
+ msn_object_destroy(obj, FALSE);
obj = NULL;
who = NULL;
+#if 0
sha1 = NULL;
+#endif
}
g_strfreev(tokens);
}
@@ -1027,21 +1033,23 @@ msn_datacast_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
/* Nudge */
PurpleAccount *account;
const char *user;
+ PurpleConnection *gc;
account = cmdproc->session->account;
user = msg->remote_user;
+ gc = purple_account_get_connection(account);
if (cmdproc->servconn->type == MSN_SERVCONN_SB) {
MsnSwitchBoard *swboard = cmdproc->data;
if (swboard->current_users > 1 ||
((swboard->conv != NULL) &&
- purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT))
- purple_prpl_got_attention_in_chat(account->gc, swboard->chat_id, user, MSN_NUDGE);
+ PURPLE_IS_CHAT_CONVERSATION(swboard->conv)))
+ purple_prpl_got_attention_in_chat(gc, swboard->chat_id, user, MSN_NUDGE);
else
- purple_prpl_got_attention(account->gc, user, MSN_NUDGE);
+ purple_prpl_got_attention(gc, user, MSN_NUDGE);
} else {
- purple_prpl_got_attention(account->gc, user, MSN_NUDGE);
+ purple_prpl_got_attention(gc, user, MSN_NUDGE);
}
} else if (!strcmp(id, "2")) {
@@ -1061,7 +1069,7 @@ msn_datacast_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
slplink = msn_session_get_slplink(session, who);
msn_slplink_request_object(slplink, data, got_wink_cb, NULL, obj);
- msn_object_destroy(obj);
+ msn_object_destroy(obj, FALSE);
} else if (!strcmp(id, "3")) {
@@ -1081,7 +1089,7 @@ msn_datacast_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
slplink = msn_session_get_slplink(session, who);
msn_slplink_request_object(slplink, data, got_voiceclip_cb, NULL, obj);
- msn_object_destroy(obj);
+ msn_object_destroy(obj, FALSE);
} else if (!strcmp(id, "4")) {
/* Action */
@@ -1141,24 +1149,22 @@ msn_invite_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
purple_debug_info("msn", "Computer call\n");
if (cmdproc->session) {
- PurpleConversation *conv = NULL;
+ PurpleIMConversation *im = NULL;
gchar *from = msg->remote_user;
gchar *buf = NULL;
if (from)
- conv = purple_find_conversation_with_account(
- PURPLE_CONV_TYPE_IM, from,
- cmdproc->session->account);
- if (conv)
+ im = purple_conversations_find_im_with_account(
+ from, cmdproc->session->account);
+ if (im)
buf = g_strdup_printf(
_("%s sent you a voice chat "
"invite, which is not yet "
"supported."), from);
if (buf) {
- purple_conversation_write(conv, NULL, buf,
- PURPLE_MESSAGE_SYSTEM |
- PURPLE_MESSAGE_NOTIFY,
- time(NULL));
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(im), buf,
+ PURPLE_MESSAGE_NOTIFY);
g_free(buf);
}
}
diff --git a/libpurple/protocols/msn/msn.c b/libpurple/protocols/msn/msn.c
index 1269cf2c4d..1ed6a039dd 100644
--- a/libpurple/protocols/msn/msn.c
+++ b/libpurple/protocols/msn/msn.c
@@ -26,6 +26,7 @@
#include "internal.h"
#include "debug.h"
+#include "http.h"
#include "request.h"
#include "accountopt.h"
@@ -36,6 +37,8 @@
#include "prefs.h"
#include "session.h"
#include "smiley.h"
+#include "smiley-custom.h"
+#include "smiley-parser.h"
#include "state.h"
#include "util.h"
#include "cmds.h"
@@ -52,7 +55,7 @@
#if PHOTO_SUPPORT
#define MAX_HTTP_BUDDYICON_BYTES (200 * 1024)
-#include "imgstore.h"
+#include "image-store.h"
#endif
typedef struct
@@ -119,7 +122,7 @@ msn_send_attention(PurpleConnection *gc, const char *username, guint type)
MsnSwitchBoard *swboard;
msg = msn_message_new_nudge();
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
swboard = msn_session_get_swboard(session, username, MSN_SB_FLAG_IM);
msn_switchboard_send_msg(swboard, msg, TRUE);
@@ -181,6 +184,8 @@ set_public_alias_length_error(gpointer data)
PurpleSetPublicAliasFailureCallback failure_cb = closure->failure_cb;
failure_cb(closure->account, _("Your new MSN friendly name is too long."));
+
+ g_object_unref(closure->account);
g_free(closure);
return FALSE;
@@ -253,13 +258,13 @@ msn_set_public_alias(PurpleConnection *pc, const char *alias,
if (failure_cb) {
struct public_alias_closure *closure =
g_new0(struct public_alias_closure, 1);
- closure->account = account;
+ closure->account = g_object_ref(account);
closure->failure_cb = failure_cb;
purple_timeout_add(0, set_public_alias_length_error, closure);
} else {
- purple_notify_error(pc, NULL,
- _("Your new MSN friendly name is too long."),
- NULL);
+ purple_notify_error(pc, NULL, _("Your new MSN "
+ "friendly name is too long."), NULL,
+ purple_request_cpar_from_connection(pc));
}
return;
}
@@ -295,6 +300,8 @@ get_public_alias_cb(gpointer data)
alias = purple_account_get_string(closure->account, "display-name",
purple_account_get_username(closure->account));
success_cb(closure->account, alias);
+
+ g_object_unref(closure->account);
g_free(closure);
return FALSE;
@@ -308,7 +315,7 @@ msn_get_public_alias(PurpleConnection *pc,
struct public_alias_closure *closure = g_new0(struct public_alias_closure, 1);
PurpleAccount *account = purple_connection_get_account(pc);
- closure->account = account;
+ closure->account = g_object_ref(account);
closure->success_cb = success_cb;
purple_timeout_add(0, get_public_alias_cb, closure);
}
@@ -326,7 +333,7 @@ msn_set_prp(PurpleConnection *gc, const char *type, const char *entry)
MsnSession *session;
MsnTransaction *trans;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
cmdproc = session->notification->cmdproc;
if (entry == NULL || *entry == '\0')
@@ -384,7 +391,7 @@ send_to_mobile(PurpleConnection *gc, const char *who, const char *entry)
const char *mobile_number = NULL;
gsize payload_len;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
cmdproc = session->notification->cmdproc;
page = msn_page_new();
@@ -450,7 +457,7 @@ msn_show_set_friendly_name(PurplePluginAction *action)
purple_connection_get_display_name(gc), FALSE, FALSE, NULL,
_("OK"), G_CALLBACK(msn_act_id),
_("Cancel"), NULL,
- account, NULL, NULL,
+ purple_request_cpar_from_connection(gc),
gc);
g_free(tmp);
}
@@ -486,7 +493,7 @@ update_endpoint_cb(MsnLocationData *data, PurpleRequestFields *fields)
others;
others = g_list_next(others)) {
PurpleRequestField *field = others->data;
- if (purple_request_field_get_type(field) != PURPLE_REQUEST_FIELD_BOOLEAN)
+ if (purple_request_field_get_field_type(field) != PURPLE_REQUEST_FIELD_BOOLEAN)
continue;
if (purple_request_field_bool_get_value(field)) {
const char *id = purple_request_field_get_id(field);
@@ -571,12 +578,9 @@ msn_show_locations(PurplePluginAction *action)
data->session = session;
data->group = group;
- purple_request_fields(pc, NULL, NULL, NULL,
- fields,
- _("OK"), G_CALLBACK(update_endpoint_cb),
- _("Cancel"), G_CALLBACK(g_free),
- account, NULL, NULL,
- data);
+ purple_request_fields(pc, NULL, NULL, NULL, fields, _("OK"),
+ G_CALLBACK(update_endpoint_cb), _("Cancel"), G_CALLBACK(g_free),
+ purple_request_cpar_from_connection(pc), data);
}
static void
@@ -633,7 +637,7 @@ msn_show_set_mpop(PurplePluginAction *action)
_("Do you want to allow or disallow connecting from "
"multiple locations simultaneously?"),
PURPLE_DEFAULT_ACTION_NONE,
- purple_connection_get_account(pc), NULL, NULL,
+ purple_request_cpar_from_connection(pc),
pc, 3,
_("Allow"), G_CALLBACK(enable_mpop_cb),
_("Disallow"), G_CALLBACK(disable_mpop_cb),
@@ -647,13 +651,13 @@ msn_show_set_home_phone(PurplePluginAction *action)
MsnSession *session;
gc = (PurpleConnection *) action->context;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
purple_request_input(gc, NULL, _("Set your home phone number."), NULL,
msn_user_get_home_phone(session->user), FALSE, FALSE, NULL,
_("OK"), G_CALLBACK(msn_set_home_phone_cb),
_("Cancel"), NULL,
- purple_connection_get_account(gc), NULL, NULL,
+ purple_request_cpar_from_connection(gc),
gc);
}
@@ -664,13 +668,13 @@ msn_show_set_work_phone(PurplePluginAction *action)
MsnSession *session;
gc = (PurpleConnection *) action->context;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
purple_request_input(gc, NULL, _("Set your work phone number."), NULL,
msn_user_get_work_phone(session->user), FALSE, FALSE, NULL,
_("OK"), G_CALLBACK(msn_set_work_phone_cb),
_("Cancel"), NULL,
- purple_connection_get_account(gc), NULL, NULL,
+ purple_request_cpar_from_connection(gc),
gc);
}
@@ -681,13 +685,13 @@ msn_show_set_mobile_phone(PurplePluginAction *action)
MsnSession *session;
gc = (PurpleConnection *) action->context;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
purple_request_input(gc, NULL, _("Set your mobile phone number."), NULL,
msn_user_get_mobile_phone(session->user), FALSE, FALSE, NULL,
_("OK"), G_CALLBACK(msn_set_mobile_phone_cb),
_("Cancel"), NULL,
- purple_connection_get_account(gc), NULL, NULL,
+ purple_request_cpar_from_connection(gc),
gc);
}
@@ -703,7 +707,7 @@ msn_show_set_mobile_pages(PurplePluginAction *action)
"your buddy list to send you MSN Mobile pages "
"to your cell phone or other mobile device?"),
PURPLE_DEFAULT_ACTION_NONE,
- purple_connection_get_account(gc), NULL, NULL,
+ purple_request_cpar_from_connection(gc),
gc, 3,
_("Allow"), G_CALLBACK(enable_msn_pages_cb),
_("Disallow"), G_CALLBACK(disable_msn_pages_cb),
@@ -719,7 +723,7 @@ msn_show_blocked_text(PurplePluginAction *action)
MsnSession *session;
char *title;
- session = pc->proto_data;
+ session = purple_connection_get_protocol_data(pc);
title = g_strdup_printf(_("Blocked Text for %s"), session->account->username);
if (session->blocked_text == NULL) {
@@ -743,11 +747,12 @@ msn_show_hotmail_inbox(PurplePluginAction *action)
MsnSession *session;
gc = (PurpleConnection *) action->context;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
if (!session->passport_info.email_enabled) {
- purple_notify_error(gc, NULL,
- _("This account does not have email enabled."), NULL);
+ purple_notify_error(gc, NULL, _("This account does not have "
+ "email enabled."), NULL,
+ purple_request_cpar_from_connection(gc));
return;
}
@@ -777,7 +782,7 @@ show_send_to_mobile_cb(PurpleBlistNode *node, gpointer ignored)
PurpleAccount *account;
const char *name;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *) node;
account = purple_buddy_get_account(buddy);
@@ -792,7 +797,7 @@ show_send_to_mobile_cb(PurpleBlistNode *node, gpointer ignored)
NULL, TRUE, FALSE, NULL,
_("Page"), G_CALLBACK(send_to_mobile_cb),
_("Close"), G_CALLBACK(close_mobile_page_cb),
- account, name, NULL,
+ purple_request_cpar_from_connection(gc),
data);
}
@@ -810,11 +815,11 @@ msn_send_privacy(PurpleConnection *gc)
MsnTransaction *trans;
account = purple_connection_get_account(gc);
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
cmdproc = session->notification->cmdproc;
- if (account->perm_deny == PURPLE_PRIVACY_ALLOW_ALL ||
- account->perm_deny == PURPLE_PRIVACY_DENY_USERS)
+ if (purple_account_get_privacy_type(account) == PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL ||
+ purple_account_get_privacy_type(account) == PURPLE_ACCOUNT_PRIVACY_DENY_USERS)
trans = msn_transaction_new(cmdproc, "BLP", "%s", "AL");
else
trans = msn_transaction_new(cmdproc, "BLP", "%s", "BL");
@@ -834,13 +839,13 @@ initiate_chat_cb(PurpleBlistNode *node, gpointer data)
const char *alias;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *) node;
account = purple_buddy_get_account(buddy);
gc = purple_account_get_connection(account);
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
swboard = msn_switchboard_new(session);
msn_switchboard_request(swboard);
@@ -848,16 +853,17 @@ initiate_chat_cb(PurpleBlistNode *node, gpointer data)
/* TODO: This might move somewhere else, after USR might be */
swboard->chat_id = msn_switchboard_get_chat_id();
- swboard->conv = serv_got_joined_chat(gc, swboard->chat_id, "MSN Chat");
+ swboard->conv = PURPLE_CONVERSATION(purple_serv_got_joined_chat(gc,
+ swboard->chat_id, "MSN Chat"));
swboard->flag = MSN_SB_FLAG_IM;
/* Local alias > Display name > Username */
- if ((alias = purple_account_get_alias(account)) == NULL)
+ if ((alias = purple_account_get_private_alias(account)) == NULL)
if ((alias = purple_connection_get_display_name(gc)) == NULL)
alias = purple_account_get_username(account);
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv),
- alias, NULL, PURPLE_CBFLAGS_NONE, TRUE);
+ purple_chat_conversation_add_user(PURPLE_CHAT_CONVERSATION(swboard->conv),
+ alias, NULL, PURPLE_CHAT_USER_NONE, TRUE);
}
static void
@@ -869,7 +875,7 @@ t_msn_xfer_init(PurpleXfer *xfer)
static void
t_msn_xfer_cancel_send(PurpleXfer *xfer)
{
- MsnSlpLink *slplink = xfer->data;
+ MsnSlpLink *slplink = purple_xfer_get_protocol_data(xfer);
msn_slplink_unref(slplink);
}
@@ -879,13 +885,13 @@ msn_new_xfer(PurpleConnection *gc, const char *who)
MsnSession *session;
PurpleXfer *xfer;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
- xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who);
+ xfer = purple_xfer_new(purple_connection_get_account(gc), PURPLE_XFER_TYPE_SEND, who);
g_return_val_if_fail(xfer != NULL, NULL);
- xfer->data = msn_slplink_ref(msn_session_get_slplink(session, who));
+ purple_xfer_set_protocol_data(xfer, msn_slplink_ref(msn_session_get_slplink(session, who)));
purple_xfer_set_init_fnc(xfer, t_msn_xfer_init);
purple_xfer_set_cancel_send_fnc(xfer, t_msn_xfer_cancel_send);
@@ -918,7 +924,7 @@ msn_can_receive_file(PurpleConnection *gc, const char *who)
g_free(normal);
if (ret) {
- MsnSession *session = gc->proto_data;
+ MsnSession *session = purple_connection_get_protocol_data(gc);
if (session) {
MsnUser *user = msn_userlist_find_user(session->userlist, who);
if (user) {
@@ -1078,7 +1084,7 @@ msn_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean f
if (psm != NULL && *psm) {
purple_notify_user_info_add_pair_plaintext(user_info, tmp2, psm);
} else {
- purple_notify_user_info_add_pair(user_info, _("Status"), tmp2);
+ purple_notify_user_info_add_pair_html(user_info, _("Status"), tmp2);
}
g_free(tmp2);
@@ -1091,17 +1097,17 @@ msn_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean f
}
} else {
if (purple_presence_is_idle(presence)) {
- purple_notify_user_info_add_pair(user_info, _("Status"),
- _("Idle"));
+ purple_notify_user_info_add_pair_plaintext(user_info,
+ _("Status"), _("Idle"));
} else {
- purple_notify_user_info_add_pair(user_info, _("Status"),
- purple_status_get_name(status));
+ purple_notify_user_info_add_pair_plaintext(user_info,
+ _("Status"), purple_status_get_name(status));
}
}
}
if (currentmedia) {
- purple_notify_user_info_add_pair(user_info, mediatype, currentmedia);
+ purple_notify_user_info_add_pair_html(user_info, mediatype, currentmedia);
g_free(currentmedia);
}
}
@@ -1119,23 +1125,26 @@ msn_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean f
{
const char *phone;
- purple_notify_user_info_add_pair(user_info, _("Has you"),
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Has you"),
((user->list_op & (1 << MSN_LIST_RL)) ? _("Yes") : _("No")));
- purple_notify_user_info_add_pair(user_info, _("Blocked"),
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Blocked"),
((user->list_op & (1 << MSN_LIST_BL)) ? _("Yes") : _("No")));
phone = msn_user_get_home_phone(user);
- if (phone != NULL)
- purple_notify_user_info_add_pair(user_info, _("Home Phone Number"), phone);
+ if (phone != NULL) {
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Home Phone Number"), phone);
+ }
phone = msn_user_get_work_phone(user);
- if (phone != NULL)
- purple_notify_user_info_add_pair(user_info, _("Work Phone Number"), phone);
+ if (phone != NULL) {
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Work Phone Number"), phone);
+ }
phone = msn_user_get_mobile_phone(user);
- if (phone != NULL)
- purple_notify_user_info_add_pair(user_info, _("Mobile Phone Number"), phone);
+ if (phone != NULL) {
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Mobile Phone Number"), phone);
+ }
}
}
@@ -1147,35 +1156,35 @@ msn_status_types(PurpleAccount *account)
status = purple_status_type_new_with_attrs(
PURPLE_STATUS_AVAILABLE, NULL, NULL, TRUE, TRUE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_append(types, status);
status = purple_status_type_new_with_attrs(
PURPLE_STATUS_AWAY, NULL, NULL, TRUE, TRUE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_append(types, status);
status = purple_status_type_new_with_attrs(
PURPLE_STATUS_AWAY, "brb", _("Be Right Back"), TRUE, TRUE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_append(types, status);
status = purple_status_type_new_with_attrs(
PURPLE_STATUS_UNAVAILABLE, "busy", _("Busy"), TRUE, TRUE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_append(types, status);
status = purple_status_type_new_with_attrs(
PURPLE_STATUS_UNAVAILABLE, "phone", _("On the Phone"), TRUE, TRUE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_append(types, status);
status = purple_status_type_new_with_attrs(
PURPLE_STATUS_AWAY, "lunch", _("Out to Lunch"), TRUE, TRUE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_append(types, status);
@@ -1193,11 +1202,11 @@ msn_status_types(PurpleAccount *account)
status = purple_status_type_new_with_attrs(PURPLE_STATUS_TUNE,
"tune", NULL, FALSE, TRUE, TRUE,
- PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
- PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
- PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
- "game", _("Game Title"), purple_value_new(PURPLE_TYPE_STRING),
- "office", _("Office Title"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(G_TYPE_STRING),
+ PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(G_TYPE_STRING),
+ PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(G_TYPE_STRING),
+ "game", _("Game Title"), purple_value_new(G_TYPE_STRING),
+ "office", _("Office Title"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_append(types, status);
@@ -1213,7 +1222,7 @@ msn_actions(PurplePlugin *plugin, gpointer context)
PurplePluginAction *act;
gc = (PurpleConnection *) context;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
act = purple_plugin_action_new(_("Set Friendly Name..."),
msn_show_set_friendly_name);
@@ -1309,7 +1318,7 @@ msn_buddy_menu(PurpleBuddy *buddy)
static GList *
msn_blist_node_menu(PurpleBlistNode *node)
{
- if(PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if(PURPLE_IS_BUDDY(node))
{
return msn_buddy_menu((PurpleBuddy *) node);
}
@@ -1333,7 +1342,7 @@ msn_login(PurpleAccount *account)
if (!purple_ssl_is_supported())
{
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
_("SSL support is needed for MSN. Please install a supported "
"SSL library."));
@@ -1350,9 +1359,15 @@ msn_login(PurpleAccount *account)
session = msn_session_new(account);
- gc->proto_data = session;
- gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_FORMATTING_WBFO | PURPLE_CONNECTION_NO_BGCOLOR |
- PURPLE_CONNECTION_NO_FONTSIZE | PURPLE_CONNECTION_NO_URLDESC | PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY;
+ purple_connection_set_protocol_data(gc, session);
+ purple_connection_set_flags(gc,
+ PURPLE_CONNECTION_FLAG_HTML |
+ PURPLE_CONNECTION_FLAG_FORMATTING_WBFO |
+ PURPLE_CONNECTION_FLAG_NO_BGCOLOR |
+ PURPLE_CONNECTION_FLAG_NO_FONTSIZE |
+ PURPLE_CONNECTION_FLAG_NO_URLDESC |
+ PURPLE_CONNECTION_FLAG_ALLOW_CUSTOM_SMILEY |
+ PURPLE_CONNECTION_FLAG_NO_IMAGES);
msn_session_set_login_step(session, MSN_LOGIN_STEP_START);
@@ -1374,7 +1389,7 @@ msn_login(PurpleAccount *account)
}
if (!msn_session_connect(session, host, port, http_method))
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to connect"));
}
@@ -1384,22 +1399,25 @@ msn_close(PurpleConnection *gc)
{
MsnSession *session;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
g_return_if_fail(session != NULL);
msn_session_destroy(session);
- gc->proto_data = NULL;
+ purple_connection_set_protocol_data(gc, NULL);
}
static gboolean
msn_send_me_im(gpointer data)
{
MsnIMData *imdata = data;
- serv_got_im(imdata->gc, imdata->who, imdata->msg, imdata->flags, imdata->when);
+ purple_serv_got_im(imdata->gc, imdata->who, imdata->msg, imdata->flags, imdata->when);
+
+ g_object_unref(imdata->gc);
g_free(imdata->msg);
g_free(imdata);
+
return FALSE;
}
@@ -1450,45 +1468,38 @@ msn_send_emoticons(MsnSwitchBoard *swboard, GString *body)
static void msn_emoticon_destroy(MsnEmoticon *emoticon)
{
if (emoticon->obj)
- msn_object_destroy(emoticon->obj);
+ msn_object_destroy(emoticon->obj, FALSE);
g_free(emoticon->smile);
g_free(emoticon);
}
static GSList* msn_msg_grab_emoticons(const char *msg, const char *username)
{
- GSList *list;
- GList *smileys;
- PurpleSmiley *smiley;
- PurpleStoredImage *img;
- char *ptr;
+ GSList *list = NULL;
+ GList *smileys, *it;
MsnEmoticon *emoticon;
- int length;
- list = NULL;
- smileys = purple_smileys_get_all();
- length = strlen(msg);
+ smileys = purple_smiley_parser_find(purple_smiley_custom_get_list(),
+ msg, FALSE);
- for (; smileys; smileys = g_list_delete_link(smileys, smileys)) {
- smiley = smileys->data;
+ for (it = smileys; it; it = g_list_next(it)) {
+ PurpleSmiley *smiley = it->data;
+ PurpleImage *img;
- ptr = g_strstr_len(msg, length, purple_smiley_get_shortcut(smiley));
-
- if (!ptr)
- continue;
-
- img = purple_smiley_get_stored_image(smiley);
+ img = purple_smiley_get_image(smiley);
emoticon = g_new0(MsnEmoticon, 1);
emoticon->smile = g_strdup(purple_smiley_get_shortcut(smiley));
emoticon->ps = smiley;
+ /* TODO: we are leaking file location, consider using
+ * purple_image_get_friendly_filename. */
emoticon->obj = msn_object_new_from_image(img,
- purple_imgstore_get_filename(img),
- username, MSN_OBJECT_EMOTICON);
+ purple_image_get_path(purple_smiley_get_image(smiley)),
+ username, MSN_OBJECT_EMOTICON);
- purple_imgstore_unref(img);
list = g_slist_prepend(list, emoticon);
}
+ g_list_free(smileys);
return list;
}
@@ -1519,11 +1530,9 @@ msn_send_im_message(MsnSession *session, MsnMessage *msg)
}
static int
-msn_send_im(PurpleConnection *gc, const char *who, const char *message,
- PurpleMessageFlags flags)
+msn_send_im(PurpleConnection *gc, PurpleMessage *pmsg)
{
PurpleAccount *account;
- PurpleBuddy *buddy = purple_find_buddy(gc->account, who);
MsnSession *session;
MsnSwitchBoard *swboard;
MsnMessage *msg;
@@ -1531,17 +1540,20 @@ msn_send_im(PurpleConnection *gc, const char *who, const char *message,
char *msgtext;
size_t msglen;
const char *username;
+ const gchar *rcpt = purple_message_get_recipient(pmsg);
+ PurpleMessageFlags flags = purple_message_get_flags(pmsg);
+ PurpleBuddy *buddy = purple_blist_find_buddy(purple_connection_get_account(gc), rcpt);
+ const gchar *cont = purple_message_get_contents(pmsg);
- purple_debug_info("msn", "send IM {%s} to %s\n", message, who);
account = purple_connection_get_account(gc);
username = purple_account_get_username(account);
- session = gc->proto_data;
- swboard = msn_session_find_swboard(session, who);
+ session = purple_connection_get_protocol_data(gc);
+ swboard = msn_session_find_swboard(session, rcpt);
- if (!strncmp("tel:+", who, 5)) {
- char *text = purple_markup_strip_html(message);
- send_to_mobile(gc, who, text);
+ if (!strncmp("tel:+", rcpt, 5)) {
+ char *text = purple_markup_strip_html(cont);
+ send_to_mobile(gc, rcpt, text);
g_free(text);
return 1;
}
@@ -1549,14 +1561,14 @@ msn_send_im(PurpleConnection *gc, const char *who, const char *message,
if (buddy) {
PurplePresence *p = purple_buddy_get_presence(buddy);
if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) {
- char *text = purple_markup_strip_html(message);
- send_to_mobile(gc, who, text);
+ char *text = purple_markup_strip_html(cont);
+ send_to_mobile(gc, rcpt, text);
g_free(text);
return 1;
}
}
- msn_import_html(message, &msgformat, &msgtext);
+ msn_import_html(cont, &msgformat, &msgtext);
msglen = strlen(msgtext);
if (msglen == 0) {
/* Stuff like <hr> will be ignored. Don't send an empty message
@@ -1576,20 +1588,20 @@ msn_send_im(PurpleConnection *gc, const char *who, const char *message,
}
msg = msn_message_new_plain(msgtext);
- msg->remote_user = g_strdup(who);
+ msg->remote_user = g_strdup(rcpt);
msn_message_set_header(msg, "X-MMS-IM-Format", msgformat);
g_free(msgformat);
g_free(msgtext);
purple_debug_info("msn", "prepare to send online Message\n");
- if (g_ascii_strcasecmp(who, username))
+ if (g_ascii_strcasecmp(rcpt, username))
{
if (flags & PURPLE_MESSAGE_AUTO_RESP) {
msn_message_set_flag(msg, 'U');
}
- if (msn_user_is_yahoo(account, who) || !(msn_user_is_online(account, who) || swboard != NULL)) {
+ if (msn_user_is_yahoo(account, rcpt) || !(msn_user_is_online(account, rcpt) || swboard != NULL)) {
/*we send the online and offline Message to Yahoo User via UBM*/
purple_debug_info("msn", "send to Yahoo User\n");
msn_notification_send_uum(session, msg);
@@ -1619,9 +1631,9 @@ msn_send_im(PurpleConnection *gc, const char *who, const char *message,
g_free(pre);
g_free(post);
- serv_got_typing_stopped(gc, who);
- imdata->gc = gc;
- imdata->who = who;
+ purple_serv_got_typing_stopped(gc, rcpt);
+ imdata->gc = g_object_ref(gc);
+ imdata->who = rcpt;
imdata->msg = body_str;
imdata->flags = flags & ~PURPLE_MESSAGE_SEND;
imdata->when = time(NULL);
@@ -1634,7 +1646,7 @@ msn_send_im(PurpleConnection *gc, const char *who, const char *message,
}
static unsigned int
-msn_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state)
+msn_send_typing(PurpleConnection *gc, const char *who, PurpleIMTypingState state)
{
PurpleAccount *account;
MsnSession *session;
@@ -1642,20 +1654,20 @@ msn_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state)
MsnMessage *msg;
account = purple_connection_get_account(gc);
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
/*
- * TODO: I feel like this should be "if (state != PURPLE_TYPING)"
+ * TODO: I feel like this should be "if (state != PURPLE_IM_TYPING)"
* but this is how it was before, and I don't want to break
* anything. --KingAnt
*/
- if (state == PURPLE_NOT_TYPING)
+ if (state == PURPLE_IM_NOT_TYPING)
return 0;
if (!g_ascii_strcasecmp(who, purple_account_get_username(account)))
{
/* We'll just fake it, since we're sending to ourself. */
- serv_got_typing(gc, who, MSN_TYPING_RECV_TIMEOUT, PURPLE_TYPING);
+ purple_serv_got_typing(gc, who, MSN_TYPING_RECV_TIMEOUT, PURPLE_IM_TYPING);
return MSN_TYPING_SEND_TIMEOUT;
}
@@ -1691,7 +1703,7 @@ msn_set_status(PurpleAccount *account, PurpleStatus *status)
if (gc != NULL)
{
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
msn_change_status(session);
}
}
@@ -1701,7 +1713,7 @@ msn_set_idle(PurpleConnection *gc, int idle)
{
MsnSession *session;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
msn_change_status(session);
}
@@ -1780,8 +1792,10 @@ msn_add_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group, cons
if (!msn_email_is_valid(bname)) {
gchar *buf;
buf = g_strdup_printf(_("Unable to add the buddy %s because the username is invalid. Usernames must be valid email addresses."), bname);
- if (!purple_conv_present_error(bname, account, buf))
- purple_notify_error(pc, NULL, _("Unable to Add"), buf);
+ if (!purple_conversation_present_error(bname, account, buf)) {
+ purple_notify_error(pc, NULL, _("Unable to Add"), buf,
+ purple_request_cpar_from_connection(pc));
+ }
g_free(buf);
/* Remove from local list */
@@ -1791,7 +1805,7 @@ msn_add_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group, cons
}
/* Make sure name is normalized */
- purple_blist_rename_buddy(buddy, bname);
+ purple_buddy_set_name(buddy, bname);
userlist = session->userlist;
user = msn_userlist_find_user(userlist, bname);
@@ -1831,7 +1845,7 @@ msn_rem_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
MsnSession *session;
MsnUserList *userlist;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
userlist = session->userlist;
if (!session->logged_in)
@@ -1848,7 +1862,7 @@ msn_add_permit(PurpleConnection *gc, const char *who)
MsnUserList *userlist;
MsnUser *user;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
userlist = session->userlist;
user = msn_userlist_find_user(userlist, who);
@@ -1876,7 +1890,7 @@ msn_add_deny(PurpleConnection *gc, const char *who)
MsnUserList *userlist;
MsnUser *user;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
userlist = session->userlist;
user = msn_userlist_find_user(userlist, who);
@@ -1903,7 +1917,7 @@ msn_rem_permit(PurpleConnection *gc, const char *who)
MsnUserList *userlist;
MsnUser *user;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
userlist = session->userlist;
if (!session->logged_in)
@@ -1926,7 +1940,7 @@ msn_rem_deny(PurpleConnection *gc, const char *who)
MsnUserList *userlist;
MsnUser *user;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
userlist = session->userlist;
if (!session->logged_in)
@@ -1955,7 +1969,7 @@ msn_chat_invite(PurpleConnection *gc, int id, const char *msg,
MsnSession *session;
MsnSwitchBoard *swboard;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
swboard = msn_session_find_swboard_with_id(session, id);
@@ -1965,7 +1979,7 @@ msn_chat_invite(PurpleConnection *gc, int id, const char *msg,
swboard = msn_switchboard_new(session);
msn_switchboard_request(swboard);
swboard->chat_id = id;
- swboard->conv = purple_find_chat(gc, id);
+ swboard->conv = PURPLE_CONVERSATION(purple_conversations_find_chat(gc, id));
}
swboard->flag |= MSN_SB_FLAG_IM;
@@ -1980,7 +1994,7 @@ msn_chat_leave(PurpleConnection *gc, int id)
MsnSwitchBoard *swboard;
PurpleConversation *conv;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
swboard = msn_session_find_swboard_with_id(session, id);
@@ -2002,7 +2016,7 @@ msn_chat_leave(PurpleConnection *gc, int id)
}
static int
-msn_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags)
+msn_chat_send(PurpleConnection *gc, int id, PurpleMessage *pmsg)
{
PurpleAccount *account;
MsnSession *session;
@@ -2012,12 +2026,9 @@ msn_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFl
char *msgformat;
char *msgtext;
size_t msglen;
- MsnEmoticon *smile;
- GSList *smileys;
- GString *emoticons = NULL;
account = purple_connection_get_account(gc);
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
username = purple_account_get_username(account);
swboard = msn_session_find_swboard_with_id(session, id);
@@ -2029,7 +2040,7 @@ msn_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFl
swboard->flag |= MSN_SB_FLAG_IM;
- msn_import_html(message, &msgformat, &msgtext);
+ msn_import_html(purple_message_get_contents(pmsg), &msgformat, &msgtext);
msglen = strlen(msgtext);
if ((msglen == 0) || (msglen + strlen(msgformat) + strlen(VERSION) > 1564))
@@ -2043,36 +2054,14 @@ msn_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFl
msg = msn_message_new_plain(msgtext);
msn_message_set_header(msg, "X-MMS-IM-Format", msgformat);
- smileys = msn_msg_grab_emoticons(msg->body, username);
- while (smileys) {
- smile = (MsnEmoticon *)smileys->data;
- emoticons = msn_msg_emoticon_add(emoticons, smile);
- if (purple_conv_custom_smiley_add(swboard->conv, smile->smile,
- "sha1", purple_smiley_get_checksum(smile->ps),
- FALSE)) {
- gconstpointer data;
- size_t len;
- data = purple_smiley_get_data(smile->ps, &len);
- purple_conv_custom_smiley_write(swboard->conv, smile->smile, data, len);
- purple_conv_custom_smiley_close(swboard->conv, smile->smile);
- }
- msn_emoticon_destroy(smile);
- smileys = g_slist_delete_link(smileys, smileys);
- }
-
- if (emoticons) {
- msn_send_emoticons(swboard, emoticons);
- g_string_free(emoticons, TRUE);
- }
-
msn_switchboard_send_msg(swboard, msg, FALSE);
msn_message_unref(msg);
g_free(msgformat);
g_free(msgtext);
- serv_got_chat_in(gc, id, purple_account_get_username(account), flags,
- message, time(NULL));
+ purple_serv_got_chat_in(gc, id, username, purple_message_get_flags(pmsg),
+ purple_message_get_contents(pmsg), time(NULL));
return 0;
}
@@ -2083,7 +2072,7 @@ msn_keepalive(PurpleConnection *gc)
MsnSession *session;
MsnTransaction *trans;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
if (!session->http_method)
{
@@ -2101,7 +2090,7 @@ static void msn_alias_buddy(PurpleConnection *pc, const char *name, const char *
{
MsnSession *session;
- session = pc->proto_data;
+ session = purple_connection_get_protocol_data(pc);
msn_update_contact(session, name, MSN_UPDATE_ALIAS, alias);
}
@@ -2113,7 +2102,7 @@ msn_group_buddy(PurpleConnection *gc, const char *who,
MsnSession *session;
MsnUserList *userlist;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
userlist = session->userlist;
msn_userlist_move_buddy(userlist, who, old_group_name, new_group_name);
@@ -2126,7 +2115,7 @@ msn_rename_group(PurpleConnection *gc, const char *old_name,
MsnSession *session;
const char *gname;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
g_return_if_fail(session != NULL);
g_return_if_fail(session->userlist != NULL);
@@ -2150,7 +2139,7 @@ msn_convo_closed(PurpleConnection *gc, const char *who)
MsnSwitchBoard *swboard;
PurpleConversation *conv;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
swboard = msn_session_find_swboard(session, who);
@@ -2181,12 +2170,12 @@ msn_convo_closed(PurpleConnection *gc, const char *who)
}
static void
-msn_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
+msn_set_buddy_icon(PurpleConnection *gc, PurpleImage *img)
{
MsnSession *session;
MsnUser *user;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
user = session->user;
msn_user_set_buddy_icon(user, img);
@@ -2200,7 +2189,7 @@ msn_remove_group(PurpleConnection *gc, PurpleGroup *group)
MsnSession *session;
const char *gname;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
gname = purple_group_get_name(group);
purple_debug_info("msn", "Remove group %s\n", gname);
@@ -2223,7 +2212,7 @@ msn_tooltip_extract_info_text(PurpleNotifyUserInfo *user_info, MsnGetInfoData *i
{
PurpleBuddy *b;
- b = purple_find_buddy(purple_connection_get_account(info_data->gc),
+ b = purple_blist_find_buddy(purple_connection_get_account(info_data->gc),
info_data->name);
if (b)
@@ -2231,7 +2220,7 @@ msn_tooltip_extract_info_text(PurpleNotifyUserInfo *user_info, MsnGetInfoData *i
char *tmp;
const char *alias;
- alias = purple_buddy_get_local_buddy_alias(b);
+ alias = purple_buddy_get_local_alias(b);
if (alias && alias[0])
{
purple_notify_user_info_add_pair_plaintext(user_info, _("Alias"), alias);
@@ -2241,7 +2230,7 @@ msn_tooltip_extract_info_text(PurpleNotifyUserInfo *user_info, MsnGetInfoData *i
{
char *nicktext = g_markup_escape_text(alias, -1);
tmp = g_strdup_printf("<font sml=\"msn\">%s</font>", nicktext);
- purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp);
+ purple_notify_user_info_add_pair_html(user_info, _("Nickname"), tmp);
g_free(tmp);
g_free(nicktext);
}
@@ -2272,8 +2261,8 @@ msn_get_photo_url(const char *url_text)
return NULL;
}
-static void msn_got_photo(PurpleUtilFetchUrlData *url_data, gpointer data,
- const gchar *url_text, gsize len, const gchar *error_message);
+static void msn_got_photo(PurpleHttpConnection *http_conn, PurpleHttpResponse *response,
+ gpointer _info2_data);
#endif
@@ -2310,10 +2299,10 @@ msn_info_strip_search_link(const char *field, size_t len)
}
static void
-msn_got_info(PurpleUtilFetchUrlData *url_data, gpointer data,
- const gchar *url_text, size_t len, const gchar *error_message)
+msn_got_info(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _info_data)
{
- MsnGetInfoData *info_data = (MsnGetInfoData *)data;
+ MsnGetInfoData *info_data = _info_data;
MsnSession *session;
PurpleNotifyUserInfo *user_info;
char *stripped, *p, *q, *tmp;
@@ -2325,22 +2314,21 @@ msn_got_info(PurpleUtilFetchUrlData *url_data, gpointer data,
gboolean has_contact_info = FALSE;
char *url_buffer;
int stripped_len;
+ const gchar *got_data;
+ size_t got_len;
#if PHOTO_SUPPORT
char *photo_url_text = NULL;
MsnGetInfoStepTwoData *info2_data = NULL;
#endif
- purple_debug_info("msn", "In msn_got_info,url_text:{%s}\n",url_text);
-
session = purple_connection_get_protocol_data(info_data->gc);
- session->url_datas = g_slist_remove(session->url_datas, url_data);
user_info = purple_notify_user_info_new();
has_tooltip_text = msn_tooltip_extract_info_text(user_info, info_data);
- if (error_message != NULL || url_text == NULL || strcmp(url_text, "") == 0)
+ if (!purple_http_response_is_successful(response))
{
- purple_notify_user_info_add_pair(user_info,
+ purple_notify_user_info_add_pair_html(user_info,
_("Error retrieving profile"), NULL);
purple_notify_userinfo(info_data->gc, info_data->name, user_info, NULL, NULL);
@@ -2351,12 +2339,16 @@ msn_got_info(PurpleUtilFetchUrlData *url_data, gpointer data,
return;
}
- url_buffer = g_strdup(url_text);
+ got_data = purple_http_response_get_data(response, &got_len);
+
+ purple_debug_info("msn", "In msn_got_info,url_text:{%s}\n", got_data);
+
+ url_buffer = g_strdup(got_data);
/* If they have a homepage link, MSN masks it such that we need to
* fetch the url out before purple_markup_strip_html() nukes it */
/* I don't think this works with the new spaces profiles - Stu 3/2/06 */
- if ((p = strstr(url_text,
+ if ((p = strstr(url_buffer,
"Take a look at my </font><A class=viewDesc title=\"")) != NULL)
{
p += 50;
@@ -2519,11 +2511,14 @@ msn_got_info(PurpleUtilFetchUrlData *url_data, gpointer data,
if (sect_info)
{
has_info = TRUE;
- sect_info = FALSE;
has_contact_info = TRUE;
+#if 0
+ /* it's true, but we don't need these assignments */
+ sect_info = FALSE;
+#endif
}
- else
- {
+ else
+ {
/* Remove the section header */
purple_notify_user_info_remove_last_item(user_info);
}
@@ -2659,7 +2654,7 @@ msn_got_info(PurpleUtilFetchUrlData *url_data, gpointer data,
if (user_url != NULL)
{
tmp = g_strdup_printf("<a href=\"%s\">%s</a>", user_url, user_url);
- purple_notify_user_info_add_pair(user_info, _("Homepage"), tmp);
+ purple_notify_user_info_add_pair_html(user_info, _("Homepage"), tmp);
g_free(tmp);
g_free(user_url);
@@ -2680,11 +2675,11 @@ msn_got_info(PurpleUtilFetchUrlData *url_data, gpointer data,
char *p = strstr(url_buffer, "form id=\"SpacesSearch\" name=\"SpacesSearch\"");
* Let's see how long this one holds out for ... */
char *p = strstr(url_buffer, "<form id=\"profile_form\" name=\"profile_form\" action=\"http&#58;&#47;&#47;spaces.live.com&#47;profile.aspx&#63;cid&#61;0\"");
- PurpleBuddy *b = purple_find_buddy
+ PurpleBuddy *b = purple_blist_find_buddy
(purple_connection_get_account(info_data->gc), info_data->name);
- purple_notify_user_info_add_pair(user_info,
+ purple_notify_user_info_add_pair_html(user_info,
_("Error retrieving profile"), NULL);
- purple_notify_user_info_add_pair(user_info, NULL,
+ purple_notify_user_info_add_pair_plaintext(user_info, NULL,
((p && b) ? _("The user has not created a public profile.") :
(p ? _("MSN reported not being able to find the user's profile. "
"This either means that the user does not exist, "
@@ -2699,12 +2694,12 @@ msn_got_info(PurpleUtilFetchUrlData *url_data, gpointer data,
purple_notify_user_info_add_section_break(user_info);
tmp = g_strdup_printf("<a href=\"%s%s\">%s</a>",
PROFILE_URL, info_data->name, _("View web profile"));
- purple_notify_user_info_add_pair(user_info, NULL, tmp);
+ purple_notify_user_info_add_pair_html(user_info, NULL, tmp);
g_free(tmp);
#if PHOTO_SUPPORT
/* Find the URL to the photo; must be before the marshalling [Bug 994207] */
- photo_url_text = msn_get_photo_url(url_text);
+ photo_url_text = msn_get_photo_url(got_data);
purple_debug_info("msn", "photo url:{%s}\n", photo_url_text ? photo_url_text : "(null)");
/* Marshall the existing state */
@@ -2718,24 +2713,27 @@ msn_got_info(PurpleUtilFetchUrlData *url_data, gpointer data,
/* Try to put the photo in there too, if there's one */
if (photo_url_text)
{
- url_data = purple_util_fetch_url_len(photo_url_text, FALSE, NULL, FALSE,
- MAX_HTTP_BUDDYICON_BYTES,
- msn_got_photo, info2_data);
- session->url_datas = g_slist_prepend(session->url_datas, url_data);
+ PurpleHttpRequest *req;
+
+ req = purple_http_request_new(photo_url_text);
+ purple_http_request_set_max_len(req, MAX_HTTP_BUDDYICON_BYTES);
+ purple_http_connection_set_add(session->http_reqs,
+ purple_http_request(info_data->gc, req, msn_got_photo,
+ info2_data));
+ purple_http_request_unref(req);
}
else
{
/* Finish the Get Info and show the user something */
- msn_got_photo(NULL, info2_data, NULL, 0, NULL);
+ msn_got_photo(NULL, NULL, info2_data);
}
}
static void
-msn_got_photo(PurpleUtilFetchUrlData *url_data, gpointer user_data,
- const gchar *url_text, gsize len, const gchar *error_message)
+msn_got_photo(PurpleHttpConnection *http_conn, PurpleHttpResponse *response,
+ gpointer _info2_data)
{
- MsnGetInfoStepTwoData *info2_data = (MsnGetInfoStepTwoData *)user_data;
- int id = -1;
+ MsnGetInfoStepTwoData *info2_data = _info2_data;
/* Unmarshall the saved state */
MsnGetInfoData *info_data = info2_data->info_data;
@@ -2744,44 +2742,25 @@ msn_got_photo(PurpleUtilFetchUrlData *url_data, gpointer user_data,
PurpleNotifyUserInfo *user_info = info2_data->user_info;
char *photo_url_text = info2_data->photo_url_text;
- if (url_data) {
- MsnSession *session = purple_connection_get_protocol_data(info_data->gc);
- session->url_datas = g_slist_remove(session->url_datas, url_data);
- }
-
- if (url_text && error_message)
+ /* Try to put the photo in there too, if there's one and is readable */
+ if (response && purple_http_response_is_successful(response))
{
- purple_debug_warning("msn", "invalid connection. ignoring buddy photo info.\n");
- g_free(stripped);
- g_free(url_buffer);
- purple_notify_user_info_destroy(user_info);
- g_free(info_data->name);
- g_free(info_data);
- g_free(photo_url_text);
- g_free(info2_data);
+ PurpleImage *img;
+ char buf[1024];
+ const gchar *photo_data;
+ size_t len;
+ guint img_id;
- return;
- }
+ photo_data = purple_http_response_get_data(response, &len);
+ purple_debug_info("msn", "%s is %" G_GSIZE_FORMAT " bytes\n", photo_url_text, len);
- /* Try to put the photo in there too, if there's one and is readable */
- if (url_text && len != 0)
- {
- if (strstr(url_text, "400 Bad Request")
- || strstr(url_text, "403 Forbidden")
- || strstr(url_text, "404 Not Found"))
- {
+ img = purple_image_new_from_data(g_memdup(photo_data, len), len);
+ img_id = purple_image_store_add_temporary(img);
+ g_object_unref(img);
- purple_debug_info("msn", "Error getting %s: %s\n",
- photo_url_text, url_text);
- }
- else
- {
- char buf[1024];
- purple_debug_info("msn", "%s is %" G_GSIZE_FORMAT " bytes\n", photo_url_text, len);
- id = purple_imgstore_add_with_id(g_memdup(url_text, len), len, NULL);
- g_snprintf(buf, sizeof(buf), "<img id=\"%d\"><br>", id);
- purple_notify_user_info_prepend_pair(user_info, NULL, buf);
- }
+ g_snprintf(buf, sizeof(buf), "<img id=\""
+ PURPLE_IMAGE_STORE_PROTOCOL "%u\"><br>", img_id);
+ purple_notify_user_info_prepend_pair_html(user_info, NULL, buf);
}
/* We continue here from msn_got_info, as if nothing has happened */
@@ -2796,8 +2775,6 @@ msn_got_photo(PurpleUtilFetchUrlData *url_data, gpointer user_data,
#if PHOTO_SUPPORT
g_free(photo_url_text);
g_free(info2_data);
- if (id != -1)
- purple_imgstore_unref_by_id(id);
#endif
}
@@ -2806,21 +2783,14 @@ msn_get_info(PurpleConnection *gc, const char *name)
{
MsnSession *session = purple_connection_get_protocol_data(gc);
MsnGetInfoData *data;
- char *url;
- PurpleUtilFetchUrlData *url_data;
data = g_new0(MsnGetInfoData, 1);
data->gc = gc;
data->name = g_strdup(name);
- url = g_strdup_printf("%s%s", PROFILE_URL, name);
-
- url_data = purple_util_fetch_url(url, FALSE,
- "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)",
- TRUE, msn_got_info, data);
- session->url_datas = g_slist_prepend(session->url_datas, url_data);
-
- g_free(url);
+ purple_http_connection_set_add(session->http_reqs,
+ purple_http_get_printf(gc, msn_got_info, data, "%s%s",
+ PROFILE_URL, name));
}
static gboolean msn_load(PurplePlugin *plugin)
@@ -2880,11 +2850,11 @@ static gboolean msn_uri_handler(const char *proto, const char *cmd, GHashTable *
if (!g_ascii_strcasecmp(cmd, "Chat")) {
char *sname = g_hash_table_lookup(params, "contact");
if (sname) {
- PurpleConversation *conv = purple_find_conversation_with_account(
- PURPLE_CONV_TYPE_IM, sname, acct);
- if (conv == NULL)
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, sname);
- purple_conversation_present(conv);
+ PurpleIMConversation *im = purple_conversations_find_im_with_account(
+ sname, acct);
+ if (im == NULL)
+ im = purple_im_conversation_new(acct, sname);
+ purple_conversation_present(PURPLE_CONVERSATION(im));
}
/*else
**If pidgindialogs_im() was in the core, we could use it here.
@@ -2903,9 +2873,16 @@ static gboolean msn_uri_handler(const char *proto, const char *cmd, GHashTable *
return FALSE;
}
+static gssize
+msn_get_max_message_size(PurpleConversation *conv)
+{
+ /* XXX: pidgin-otr says 1409. Verify and document it. */
+ return 1525 - strlen(VERSION);
+}
static PurplePluginProtocolInfo prpl_info =
{
+ sizeof(PurplePluginProtocolInfo), /* struct_size */
OPT_PROTO_MAIL_CHECK|OPT_PROTO_INVITE_MESSAGE,
NULL, /* user_splits */
NULL, /* protocol_options */
@@ -2927,7 +2904,7 @@ static PurplePluginProtocolInfo prpl_info =
msn_set_status, /* set_away */
msn_set_idle, /* set_idle */
NULL, /* change_passwd */
- NULL, /* add_buddy */
+ msn_add_buddy, /* add_buddy */
NULL, /* add_buddies */
msn_rem_buddy, /* remove_buddy */
NULL, /* remove_buddies */
@@ -2941,12 +2918,10 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* get_chat_name */
msn_chat_invite, /* chat_invite */
msn_chat_leave, /* chat_leave */
- NULL, /* chat_whisper */
msn_chat_send, /* chat_send */
msn_keepalive, /* keepalive */
NULL, /* register_user */
NULL, /* get_cb_info */
- NULL, /* get_cb_away */
msn_alias_buddy, /* alias_buddy */
msn_group_buddy, /* group_buddy */
msn_rename_group, /* rename_group */
@@ -2971,15 +2946,13 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* unregister_user */
msn_send_attention, /* send_attention */
msn_attention_types, /* attention_types */
- sizeof(PurplePluginProtocolInfo), /* struct_size */
msn_get_account_text_table, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* get_media_caps */
NULL, /* get_moods */
msn_set_public_alias, /* set_public_alias */
msn_get_public_alias, /* get_public_alias */
- msn_add_buddy, /* add_buddy_with_invite */
- NULL /* add_buddies_with_invite */
+ msn_get_max_message_size /* get_max_message_size */
};
static PurplePluginInfo info =
diff --git a/libpurple/protocols/msn/msnutils.c b/libpurple/protocols/msn/msnutils.c
index d2edd36fa0..4fac197de6 100644
--- a/libpurple/protocols/msn/msnutils.c
+++ b/libpurple/protocols/msn/msnutils.c
@@ -27,7 +27,7 @@
#include "msn.h"
#include "msnutils.h"
-#include "cipher.h"
+#include "ciphers/md5hash.h"
/**************************************************************************
* Util
@@ -536,22 +536,20 @@ msn_email_is_valid(const char *passport)
/*
* Handle MSN Challenge computation
* This algorithm references
- * http://imfreedom.org/wiki/index.php/MSN:NS/Challenges
+ * https://imfreedom.org/wiki/MSN:NS/Challenges
*/
#define BUFSIZE 256
void
msn_handle_chl(char *input, char *output)
{
- PurpleCipher *cipher;
- PurpleCipherContext *context;
+ PurpleHash *hash;
const guchar productKey[] = MSNP15_WLM_PRODUCT_KEY;
const guchar productID[] = MSNP15_WLM_PRODUCT_ID;
const char hexChars[] = "0123456789abcdef";
char buf[BUFSIZE];
- unsigned char md5Hash[16];
+ guint32 md5Parts[4];
unsigned char *newHash;
- unsigned int *md5Parts;
- unsigned int *chlStringParts;
+ guint32 chlStringParts[BUFSIZE / sizeof(guint32)];
unsigned int newHashParts[5];
long long nHigh = 0, nLow = 0;
@@ -560,16 +558,14 @@ msn_handle_chl(char *input, char *output)
int i;
/* Create the MD5 hash by using Purple MD5 algorithm */
- cipher = purple_ciphers_find_cipher("md5");
- context = purple_cipher_context_new(cipher, NULL);
+ hash = purple_md5_hash_new();
- purple_cipher_context_append(context, (guchar *)input, strlen(input));
- purple_cipher_context_append(context, productKey, sizeof(productKey) - 1);
- purple_cipher_context_digest(context, sizeof(md5Hash), md5Hash, NULL);
- purple_cipher_context_destroy(context);
+ purple_hash_append(hash, (guchar *)input, strlen(input));
+ purple_hash_append(hash, productKey, sizeof(productKey) - 1);
+ purple_hash_digest(hash, (guchar *)md5Parts, sizeof(md5Parts));
+ g_object_unref(hash);
/* Split it into four integers */
- md5Parts = (unsigned int *)md5Hash;
for (i = 0; i < 4; i++) {
/* adjust endianess */
md5Parts[i] = GUINT_TO_LE(md5Parts[i]);
@@ -591,7 +587,7 @@ msn_handle_chl(char *input, char *output)
}
/* split into integers */
- chlStringParts = (unsigned int *)buf;
+ memcpy(&chlStringParts, &buf, sizeof(chlStringParts));
/* this is magic */
for (i = 0; i < (len / 4); i += 2) {
@@ -642,78 +638,96 @@ msn_read8(const char *buf)
guint16
msn_read16le(const char *buf)
{
- return GUINT16_FROM_LE(*(guint16 *)buf);
+ guint16 val;
+ memcpy(&val, buf, sizeof(val));
+ return GUINT16_FROM_LE(val);
}
guint16
msn_read16be(const char *buf)
{
- return GUINT16_FROM_BE(*(guint16 *)buf);
+ guint16 val;
+ memcpy(&val, buf, sizeof(val));
+ return GUINT16_FROM_BE(val);
}
guint32
msn_read32le(const char *buf)
{
- return GUINT32_FROM_LE(*(guint32 *)buf);
+ guint32 val;
+ memcpy(&val, buf, sizeof(val));
+ return GUINT32_FROM_LE(val);
}
guint32
msn_read32be(const char *buf)
{
- return GUINT32_FROM_BE(*(guint32 *)buf);
+ guint32 val;
+ memcpy(&val, buf, sizeof(val));
+ return GUINT32_FROM_BE(val);
}
guint64
msn_read64le(const char *buf)
{
- return GUINT64_FROM_LE(*(guint64 *)buf);
+ guint64 val;
+ memcpy(&val, buf, sizeof(val));
+ return GUINT64_FROM_LE(val);
}
guint64
msn_read64be(const char *buf)
{
- return GUINT64_FROM_BE(*(guint64 *)buf);
+ guint64 val;
+ memcpy(&val, buf, sizeof(val));
+ return GUINT64_FROM_BE(val);
}
void
msn_write8(char *buf, guint8 data)
{
- *(guint8 *)buf = data;
+ memcpy(buf, &data, sizeof(data));
}
void
msn_write16le(char *buf, guint16 data)
{
- *(guint16 *)buf = GUINT16_TO_LE(data);
+ data = GUINT16_TO_LE(data);
+ memcpy(buf, &data, sizeof(data));
}
void
msn_write16be(char *buf, guint16 data)
{
- *(guint16 *)buf = GUINT16_TO_BE(data);
+ data = GUINT16_TO_BE(data);
+ memcpy(buf, &data, sizeof(data));
}
void
msn_write32le(char *buf, guint32 data)
{
- *(guint32 *)buf = GUINT32_TO_LE(data);
+ data = GUINT32_TO_LE(data);
+ memcpy(buf, &data, sizeof(data));
}
void
msn_write32be(char *buf, guint32 data)
{
- *(guint32 *)buf = GUINT32_TO_BE(data);
+ data = GUINT32_TO_BE(data);
+ memcpy(buf, &data, sizeof(data));
}
void
msn_write64le(char *buf, guint64 data)
{
- *(guint64 *)buf = GUINT64_TO_LE(data);
+ data = GUINT64_TO_LE(data);
+ memcpy(buf, &data, sizeof(data));
}
void
msn_write64be(char *buf, guint64 data)
{
- *(guint64 *)buf = GUINT64_TO_BE(data);
+ data = GUINT64_TO_BE(data);
+ memcpy(buf, &data, sizeof(data));
}
diff --git a/libpurple/protocols/msn/msnutils.h b/libpurple/protocols/msn/msnutils.h
index c4ed8fcbdf..b1651e82ce 100644
--- a/libpurple/protocols/msn/msnutils.h
+++ b/libpurple/protocols/msn/msnutils.h
@@ -96,7 +96,7 @@ gboolean msn_email_is_valid(const char *passport);
/**
* Handle MSN Challenge Computation
* This algorithm references
- * http://imfreedom.org/wiki/index.php/MSN:NS/Challenges
+ * https://imfreedom.org/wiki/MSN:NS/Challenges
*
* @param input Challenge input.
* @param output Callenge output.
diff --git a/libpurple/protocols/msn/nexus.c b/libpurple/protocols/msn/nexus.c
index 693b6db559..96e814ce1a 100644
--- a/libpurple/protocols/msn/nexus.c
+++ b/libpurple/protocols/msn/nexus.c
@@ -23,7 +23,6 @@
*/
#include "internal.h"
-#include "cipher.h"
#include "debug.h"
#include "msnutils.h"
@@ -31,6 +30,10 @@
#include "nexus.h"
#include "notification.h"
+#include "ciphers/des3cipher.h"
+#include "ciphers/hmaccipher.h"
+#include "ciphers/sha1hash.h"
+
/**************************************************************************
* Valid Ticket Tokens
**************************************************************************/
@@ -56,7 +59,7 @@ MsnNexus *
msn_nexus_new(MsnSession *session)
{
MsnNexus *nexus;
- int i;
+ gsize i;
nexus = g_new0(MsnNexus, 1);
nexus->session = session;
@@ -74,7 +77,7 @@ msn_nexus_new(MsnSession *session)
void
msn_nexus_destroy(MsnNexus *nexus)
{
- int i;
+ gsize i;
for (i = 0; i < nexus->token_len; i++) {
g_hash_table_destroy(nexus->tokens[i].token);
g_free(nexus->tokens[i].secret);
@@ -99,41 +102,37 @@ rps_create_key(const char *key, int key_len, const char *data, size_t data_len)
const guchar magic[] = "WS-SecureConversation";
const int magic_len = sizeof(magic) - 1;
- PurpleCipherContext *hmac;
+ PurpleCipher *hmac;
+ PurpleHash *hash;
guchar hash1[20], hash2[20], hash3[20], hash4[20];
char *result;
- hmac = purple_cipher_context_new_by_name("hmac", NULL);
-
- purple_cipher_context_set_option(hmac, "hash", "sha1");
- purple_cipher_context_set_key_with_len(hmac, (guchar *)key, key_len);
- purple_cipher_context_append(hmac, magic, magic_len);
- purple_cipher_context_append(hmac, (guchar *)data, data_len);
- purple_cipher_context_digest(hmac, sizeof(hash1), hash1, NULL);
-
- purple_cipher_context_reset(hmac, NULL);
- purple_cipher_context_set_option(hmac, "hash", "sha1");
- purple_cipher_context_set_key_with_len(hmac, (guchar *)key, key_len);
- purple_cipher_context_append(hmac, hash1, 20);
- purple_cipher_context_append(hmac, magic, magic_len);
- purple_cipher_context_append(hmac, (guchar *)data, data_len);
- purple_cipher_context_digest(hmac, sizeof(hash2), hash2, NULL);
-
- purple_cipher_context_reset(hmac, NULL);
- purple_cipher_context_set_option(hmac, "hash", "sha1");
- purple_cipher_context_set_key_with_len(hmac, (guchar *)key, key_len);
- purple_cipher_context_append(hmac, hash1, 20);
- purple_cipher_context_digest(hmac, sizeof(hash3), hash3, NULL);
-
- purple_cipher_context_reset(hmac, NULL);
- purple_cipher_context_set_option(hmac, "hash", "sha1");
- purple_cipher_context_set_key_with_len(hmac, (guchar *)key, key_len);
- purple_cipher_context_append(hmac, hash3, sizeof(hash3));
- purple_cipher_context_append(hmac, magic, magic_len);
- purple_cipher_context_append(hmac, (guchar *)data, data_len);
- purple_cipher_context_digest(hmac, sizeof(hash4), hash4, NULL);
-
- purple_cipher_context_destroy(hmac);
+ hash = purple_sha1_hash_new();
+ hmac = purple_hmac_cipher_new(hash);
+ purple_cipher_set_key(hmac, (guchar *)key, key_len);
+
+ purple_cipher_append(hmac, magic, magic_len);
+ purple_cipher_append(hmac, (guchar *)data, data_len);
+ purple_cipher_digest(hmac, hash1, sizeof(hash1));
+
+ purple_cipher_reset_state(hmac);
+ purple_cipher_append(hmac, hash1, 20);
+ purple_cipher_append(hmac, magic, magic_len);
+ purple_cipher_append(hmac, (guchar *)data, data_len);
+ purple_cipher_digest(hmac, hash2, sizeof(hash2));
+
+ purple_cipher_reset_state(hmac);
+ purple_cipher_append(hmac, hash1, 20);
+ purple_cipher_digest(hmac, hash3, sizeof(hash3));
+
+ purple_cipher_reset_state(hmac);
+ purple_cipher_append(hmac, hash3, sizeof(hash3));
+ purple_cipher_append(hmac, magic, magic_len);
+ purple_cipher_append(hmac, (guchar *)data, data_len);
+ purple_cipher_digest(hmac, hash4, sizeof(hash4));
+
+ g_object_unref(hmac);
+ g_object_unref(hash);
result = g_malloc(24);
memcpy(result, hash2, sizeof(hash2));
@@ -145,22 +144,23 @@ rps_create_key(const char *key, int key_len, const char *data, size_t data_len)
static char *
des3_cbc(const char *key, const char *iv, const char *data, int len, gboolean decrypt)
{
- PurpleCipherContext *des3;
+ PurpleCipher *des3;
char *out;
- size_t outlen;
+ gssize ciph_size;
- des3 = purple_cipher_context_new_by_name("des3", NULL);
- purple_cipher_context_set_key(des3, (guchar *)key);
- purple_cipher_context_set_batch_mode(des3, PURPLE_CIPHER_BATCH_MODE_CBC);
- purple_cipher_context_set_iv(des3, (guchar *)iv, 8);
+ des3 = purple_des3_cipher_new();
+ purple_cipher_set_key(des3, (guchar *)key, 24);
+ purple_cipher_set_batch_mode(des3, PURPLE_CIPHER_BATCH_MODE_CBC);
+ purple_cipher_set_iv(des3, (guchar *)iv, 8);
out = g_malloc(len);
if (decrypt)
- purple_cipher_context_decrypt(des3, (guchar *)data, len, (guchar *)out, &outlen);
+ ciph_size = purple_cipher_decrypt(des3, (guchar *)data, len, (guchar *)out, len);
else
- purple_cipher_context_encrypt(des3, (guchar *)data, len, (guchar *)out, &outlen);
+ ciph_size = purple_cipher_encrypt(des3, (guchar *)data, len, (guchar *)out, len);
+ g_warn_if_fail(ciph_size == len);
- purple_cipher_context_destroy(des3);
+ g_object_unref(des3);
return out;
}
@@ -175,7 +175,8 @@ msn_rps_encrypt(MsnNexus *nexus)
char usr_key_base[MSN_USER_KEY_SIZE], *usr_key;
const char magic1[] = "SESSION KEY HASH";
const char magic2[] = "SESSION KEY ENCRYPTION";
- PurpleCipherContext *hmac;
+ PurpleCipher *hmac;
+ PurpleHash *hasher;
size_t len;
guchar *hash;
char *key1, *key2, *key3;
@@ -206,12 +207,13 @@ msn_rps_encrypt(MsnNexus *nexus)
key3 = rps_create_key(key1, key1_len, magic2, sizeof(magic2) - 1);
len = strlen(nexus->nonce);
- hmac = purple_cipher_context_new_by_name("hmac", NULL);
- purple_cipher_context_set_option(hmac, "hash", "sha1");
- purple_cipher_context_set_key_with_len(hmac, (guchar *)key2, 24);
- purple_cipher_context_append(hmac, (guchar *)nexus->nonce, len);
- purple_cipher_context_digest(hmac, 20, hash, NULL);
- purple_cipher_context_destroy(hmac);
+ hasher = purple_sha1_hash_new();
+ hmac = purple_hmac_cipher_new(hasher);
+ purple_cipher_set_key(hmac, (guchar *)key2, 24);
+ purple_cipher_append(hmac, (guchar *)nexus->nonce, len);
+ purple_cipher_digest(hmac, hash, 20);
+ g_object_unref(hmac);
+ g_object_unref(hasher);
/* We need to pad this to 72 bytes, apparently */
nonce_fixed = g_malloc(len + 8);
@@ -250,30 +252,30 @@ struct _MsnNexusUpdateCallback {
};
static gboolean
-nexus_parse_token(MsnNexus *nexus, int id, xmlnode *node)
+nexus_parse_token(MsnNexus *nexus, int id, PurpleXmlNode *node)
{
char *token_str, *expiry_str;
const char *id_str;
char **elems, **cur, **tokens;
- xmlnode *token = xmlnode_get_child(node, "RequestedSecurityToken/BinarySecurityToken");
- xmlnode *secret = xmlnode_get_child(node, "RequestedProofToken/BinarySecret");
- xmlnode *expires = xmlnode_get_child(node, "LifeTime/Expires");
+ PurpleXmlNode *token = purple_xmlnode_get_child(node, "RequestedSecurityToken/BinarySecurityToken");
+ PurpleXmlNode *secret = purple_xmlnode_get_child(node, "RequestedProofToken/BinarySecret");
+ PurpleXmlNode *expires = purple_xmlnode_get_child(node, "LifeTime/Expires");
if (!token)
return FALSE;
/* Use the ID that the server sent us */
if (id == -1) {
- id_str = xmlnode_get_attrib(token, "Id");
+ id_str = purple_xmlnode_get_attrib(token, "Id");
if (id_str == NULL)
return FALSE;
id = atol(id_str + 7) - 1; /* 'Compact#' or 'PPToken#' */
- if (id >= nexus->token_len)
+ if (id < 0 || (gsize)id >= nexus->token_len)
return FALSE; /* Where did this come from? */
}
- token_str = xmlnode_get_data(token);
+ token_str = purple_xmlnode_get_data(token);
if (token_str == NULL)
return FALSE;
@@ -291,12 +293,12 @@ nexus_parse_token(MsnNexus *nexus, int id, xmlnode *node)
g_free(token_str);
if (secret)
- nexus->tokens[id].secret = xmlnode_get_data(secret);
+ nexus->tokens[id].secret = purple_xmlnode_get_data(secret);
else
nexus->tokens[id].secret = NULL;
/* Yay for MS using ISO-8601 */
- expiry_str = xmlnode_get_data(expires);
+ expiry_str = purple_xmlnode_get_data(expires);
nexus->tokens[id].expiry = purple_str_to_time(expiry_str,
FALSE, NULL, NULL, NULL);
g_free(expiry_str);
@@ -308,30 +310,30 @@ nexus_parse_token(MsnNexus *nexus, int id, xmlnode *node)
}
static gboolean
-nexus_parse_collection(MsnNexus *nexus, int id, xmlnode *collection)
+nexus_parse_collection(MsnNexus *nexus, int id, PurpleXmlNode *collection)
{
- xmlnode *node;
+ PurpleXmlNode *node;
gboolean result;
- node = xmlnode_get_child(collection, "RequestSecurityTokenResponse");
+ node = purple_xmlnode_get_child(collection, "RequestSecurityTokenResponse");
if (!node)
return FALSE;
result = TRUE;
for (; node && result; node = node->next) {
- xmlnode *endpoint = xmlnode_get_child(node, "AppliesTo/EndpointReference/Address");
- char *address = xmlnode_get_data(endpoint);
+ PurpleXmlNode *endpoint = purple_xmlnode_get_child(node, "AppliesTo/EndpointReference/Address");
+ char *address = purple_xmlnode_get_data(endpoint);
if (g_str_equal(address, "http://Passport.NET/tb")) {
/* This node contains the stuff for updating tokens. */
char *data;
- xmlnode *cipher = xmlnode_get_child(node, "RequestedSecurityToken/EncryptedData/CipherData/CipherValue");
- xmlnode *secret = xmlnode_get_child(node, "RequestedProofToken/BinarySecret");
+ PurpleXmlNode *cipher = purple_xmlnode_get_child(node, "RequestedSecurityToken/EncryptedData/CipherData/CipherValue");
+ PurpleXmlNode *secret = purple_xmlnode_get_child(node, "RequestedProofToken/BinarySecret");
g_free(nexus->cipher);
- nexus->cipher = xmlnode_get_data(cipher);
- data = xmlnode_get_data(secret);
+ nexus->cipher = purple_xmlnode_get_data(cipher);
+ data = purple_xmlnode_get_data(secret);
g_free(nexus->secret);
nexus->secret = (char *)purple_base64_decode(data, NULL);
g_free(data);
@@ -359,7 +361,7 @@ nexus_got_response_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
}
if (!nexus_parse_collection(nexus, -1,
- xmlnode_get_child(resp->xml,
+ purple_xmlnode_get_child(msn_soap_message_get_xml(resp),
"Body/RequestSecurityTokenResponseCollection"))) {
msn_session_set_error(session, MSN_ERROR_SERVCONN, _("Windows Live ID authentication:Invalid response"));
return;
@@ -381,15 +383,13 @@ msn_nexus_connect(MsnNexus *nexus)
char *password_xml;
GString *domains;
char *request;
- int i;
-
- MsnSoapMessage *soap;
+ gsize i;
purple_debug_info("msn", "Starting Windows Live ID authentication\n");
msn_session_set_login_step(session, MSN_LOGIN_STEP_GET_COOKIE);
username = purple_account_get_username(session->account);
- password = purple_connection_get_password(session->account->gc);
+ password = purple_connection_get_password(purple_account_get_connection(session->account));
if (g_utf8_strlen(password, -1) > 16) {
/* max byte size for 16 utf8 characters is 64 + 1 for the null */
gchar truncated[65];
@@ -405,7 +405,7 @@ msn_nexus_connect(MsnNexus *nexus)
domains = g_string_new(NULL);
for (i = 0; i < nexus->token_len; i++) {
g_string_append_printf(domains, MSN_SSO_RST_TEMPLATE,
- i+1,
+ (int)i+1,
ticket_domains[i][SSO_VALID_TICKET_DOMAIN],
ticket_domains[i][SSO_VALID_TICKET_POLICY] != NULL ?
ticket_domains[i][SSO_VALID_TICKET_POLICY] :
@@ -416,10 +416,11 @@ msn_nexus_connect(MsnNexus *nexus)
g_free(password_xml);
g_string_free(domains, TRUE);
- soap = msn_soap_message_new(NULL, xmlnode_from_str(request, -1));
+ msn_soap_service_send_message(session->soap,
+ msn_soap_message_new(NULL, purple_xmlnode_from_str(request, -1)),
+ MSN_SSO_SERVER, SSO_POST_URL, TRUE,
+ nexus_got_response_cb, nexus);
g_free(request);
- msn_soap_message_send(session, soap, MSN_SSO_SERVER, SSO_POST_URL, TRUE,
- nexus_got_response_cb, nexus);
}
static void
@@ -428,7 +429,7 @@ nexus_got_update_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
MsnNexusUpdateData *ud = data;
MsnNexus *nexus = ud->nexus;
char iv[8] = {0,0,0,0,0,0,0,0};
- xmlnode *enckey;
+ PurpleXmlNode *enckey;
char *tmp;
char *nonce;
gsize len;
@@ -445,18 +446,18 @@ nexus_got_update_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
purple_debug_info("msn", "Got Update Response for %s.\n", ticket_domains[ud->id][SSO_VALID_TICKET_DOMAIN]);
- enckey = xmlnode_get_child(resp->xml, "Header/Security/DerivedKeyToken");
+ enckey = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Header/Security/DerivedKeyToken");
while (enckey) {
- if (g_str_equal(xmlnode_get_attrib(enckey, "Id"), "EncKey"))
+ if (g_str_equal(purple_xmlnode_get_attrib(enckey, "Id"), "EncKey"))
break;
- enckey = xmlnode_get_next_twin(enckey);
+ enckey = purple_xmlnode_get_next_twin(enckey);
}
if (!enckey) {
purple_debug_error("msn", "Invalid response in token update.\n");
return;
}
- tmp = xmlnode_get_data(xmlnode_get_child(enckey, "Nonce"));
+ tmp = purple_xmlnode_get_data(purple_xmlnode_get_child(enckey, "Nonce"));
nonce = (char *)purple_base64_decode(tmp, &len);
key = rps_create_key(nexus->secret, 24, nonce, len);
g_free(tmp);
@@ -464,7 +465,7 @@ nexus_got_update_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
#if 0
/* Don't know what this is for yet */
- tmp = xmlnode_get_data(xmlnode_get_child(resp->xml,
+ tmp = purple_xmlnode_get_data(purple_xmlnode_get_child(resp->xml,
"Header/EncryptedPP/EncryptedData/CipherData/CipherValue"));
if (tmp) {
decrypted_pp = des3_cbc(key, iv, tmp, len, TRUE);
@@ -474,11 +475,11 @@ nexus_got_update_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
}
#endif
- tmp = xmlnode_get_data(xmlnode_get_child(resp->xml,
+ tmp = purple_xmlnode_get_data(purple_xmlnode_get_child(msn_soap_message_get_xml(resp),
"Body/EncryptedData/CipherData/CipherValue"));
if (tmp) {
char *unescaped;
- xmlnode *rstresponse;
+ PurpleXmlNode *rstresponse;
unescaped = (char *)purple_base64_decode(tmp, &len);
g_free(tmp);
@@ -487,7 +488,7 @@ nexus_got_update_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
g_free(unescaped);
purple_debug_info("msn", "Got Response Body EncryptedData: %s\n", decrypted_data);
- rstresponse = xmlnode_from_str(decrypted_data, -1);
+ rstresponse = purple_xmlnode_from_str(decrypted_data, -1);
if (g_str_equal(rstresponse->name, "RequestSecurityTokenResponse"))
nexus_parse_token(nexus, ud->id, rstresponse);
else
@@ -515,8 +516,8 @@ msn_nexus_update_token(MsnNexus *nexus, int id, GSourceFunc cb, gpointer data)
MsnSession *session = nexus->session;
MsnNexusUpdateData *ud;
MsnNexusUpdateCallback *update;
- PurpleCipherContext *sha1;
- PurpleCipherContext *hmac;
+ PurpleHash *sha1;
+ PurpleCipher *hmac;
char *key;
@@ -539,7 +540,6 @@ msn_nexus_update_token(MsnNexus *nexus, int id, GSourceFunc cb, gpointer data)
guchar signature[20];
char *request;
- MsnSoapMessage *soap;
update = g_new0(MsnNexusUpdateCallback, 1);
update->cb = cb;
@@ -567,7 +567,7 @@ msn_nexus_update_token(MsnNexus *nexus, int id, GSourceFunc cb, gpointer data)
ud->nexus = nexus;
ud->id = id;
- sha1 = purple_cipher_context_new_by_name("sha1", NULL);
+ sha1 = purple_sha1_hash_new();
domain = g_strdup_printf(MSN_SSO_RST_TEMPLATE,
id,
@@ -575,8 +575,8 @@ msn_nexus_update_token(MsnNexus *nexus, int id, GSourceFunc cb, gpointer data)
ticket_domains[id][SSO_VALID_TICKET_POLICY] != NULL ?
ticket_domains[id][SSO_VALID_TICKET_POLICY] :
nexus->policy);
- purple_cipher_context_append(sha1, (guchar *)domain, strlen(domain));
- purple_cipher_context_digest(sha1, 20, digest, NULL);
+ purple_hash_append(sha1, (guchar *)domain, strlen(domain));
+ purple_hash_digest(sha1, digest, 20);
domain_b64 = purple_base64_encode(digest, 20);
now = time(NULL);
@@ -587,13 +587,13 @@ msn_nexus_update_token(MsnNexus *nexus, int id, GSourceFunc cb, gpointer data)
timestamp = g_strdup_printf(MSN_SSO_TIMESTAMP_TEMPLATE,
now_str,
purple_utf8_strftime("%Y-%m-%dT%H:%M:%SZ", tm));
- purple_cipher_context_reset(sha1, NULL);
- purple_cipher_context_append(sha1, (guchar *)timestamp, strlen(timestamp));
- purple_cipher_context_digest(sha1, 20, digest, NULL);
+ purple_hash_reset(sha1);
+ purple_hash_append(sha1, (guchar *)timestamp, strlen(timestamp));
+ purple_hash_digest(sha1, digest, 20);
timestamp_b64 = purple_base64_encode(digest, 20);
g_free(now_str);
- purple_cipher_context_destroy(sha1);
+ purple_hash_reset(sha1);
signedinfo = g_strdup_printf(MSN_SSO_SIGNEDINFO_TEMPLATE,
id,
@@ -605,12 +605,14 @@ msn_nexus_update_token(MsnNexus *nexus, int id, GSourceFunc cb, gpointer data)
nonce_b64 = purple_base64_encode((guchar *)&nonce, sizeof(nonce));
key = rps_create_key(nexus->secret, 24, (char *)nonce, sizeof(nonce));
- hmac = purple_cipher_context_new_by_name("hmac", NULL);
- purple_cipher_context_set_option(hmac, "hash", "sha1");
- purple_cipher_context_set_key_with_len(hmac, (guchar *)key, 24);
- purple_cipher_context_append(hmac, (guchar *)signedinfo, strlen(signedinfo));
- purple_cipher_context_digest(hmac, 20, signature, NULL);
- purple_cipher_context_destroy(hmac);
+ hmac = purple_hmac_cipher_new(sha1);
+ purple_cipher_set_key(hmac, (guchar *)key, 24);
+ purple_cipher_append(hmac, (guchar *)signedinfo, strlen(signedinfo));
+ purple_cipher_digest(hmac, signature, 20);
+
+ g_object_unref(hmac);
+ g_object_unref(sha1);
+
signature_b64 = purple_base64_encode(signature, 20);
request = g_strdup_printf(MSN_SSO_TOKEN_UPDATE_TEMPLATE,
@@ -630,10 +632,10 @@ msn_nexus_update_token(MsnNexus *nexus, int id, GSourceFunc cb, gpointer data)
g_free(signedinfo);
g_free(domain);
- soap = msn_soap_message_new(NULL, xmlnode_from_str(request, -1));
g_free(request);
- msn_soap_message_send(session, soap, MSN_SSO_SERVER, SSO_POST_URL, TRUE,
- nexus_got_update_cb, ud);
+ msn_soap_service_send_message(session->soap,
+ msn_soap_message_new(NULL, purple_xmlnode_from_str(request, -1)),
+ MSN_SSO_SERVER, SSO_POST_URL, TRUE, nexus_got_update_cb, ud);
}
GHashTable *
diff --git a/libpurple/protocols/msn/nexus.h b/libpurple/protocols/msn/nexus.h
index d824b8f624..f6150780e9 100644
--- a/libpurple/protocols/msn/nexus.h
+++ b/libpurple/protocols/msn/nexus.h
@@ -206,7 +206,7 @@ struct _MsnNexus
char *cipher;
char *secret;
MsnTicketToken *tokens;
- int token_len;
+ gsize token_len;
};
void msn_nexus_connect(MsnNexus *nexus);
diff --git a/libpurple/protocols/msn/notification.c b/libpurple/protocols/msn/notification.c
index 5dadc3ee7d..d3f7998e80 100644
--- a/libpurple/protocols/msn/notification.c
+++ b/libpurple/protocols/msn/notification.c
@@ -23,7 +23,7 @@
*/
#include "internal.h"
-#include "cipher.h"
+#include "ciphers/md5hash.h"
#include "core.h"
#include "debug.h"
@@ -240,7 +240,7 @@ ver_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
MsnTransaction *trans;
PurpleAccount *account;
gboolean protocol_supported = FALSE;
- int proto_ver;
+ guint proto_ver;
size_t i;
session = cmdproc->session;
@@ -249,7 +249,7 @@ ver_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
session->protocol_ver = 0;
for (i = 1; i < cmd->param_count; i++)
{
- if (sscanf(cmd->params[i], "MSNP%d", &proto_ver) == 1) {
+ if (sscanf(cmd->params[i], "MSNP%u", &proto_ver) == 1) {
if (proto_ver >= WLM_MIN_PROTOCOL
&& proto_ver <= WLM_MAX_PROTOCOL
&& proto_ver > session->protocol_ver) {
@@ -434,11 +434,11 @@ typedef struct MsnFqyCbData {
gpointer data;
} MsnFqyCbData;
-/* add contact to xmlnode */
+/* add contact to PurpleXmlNode */
static void
-msn_add_contact_xml(xmlnode *mlNode, const char *passport, MsnListOp list_op, MsnNetwork networkId)
+msn_add_contact_xml(PurpleXmlNode *mlNode, const char *passport, MsnListOp list_op, MsnNetwork networkId)
{
- xmlnode *d_node,*c_node;
+ PurpleXmlNode *d_node,*c_node;
char **tokens;
const char *email,*domain;
char fmt_str[3];
@@ -457,9 +457,9 @@ msn_add_contact_xml(xmlnode *mlNode, const char *passport, MsnListOp list_op, Ms
}
/*find a domain Node*/
- for (d_node = xmlnode_get_child(mlNode, "d"); d_node;
- d_node = xmlnode_get_next_twin(d_node)) {
- const char *attr = xmlnode_get_attrib(d_node,"n");
+ for (d_node = purple_xmlnode_get_child(mlNode, "d"); d_node;
+ d_node = purple_xmlnode_get_next_twin(d_node)) {
+ const char *attr = purple_xmlnode_get_attrib(d_node,"n");
if (attr == NULL)
continue;
if (!strcmp(attr, domain))
@@ -469,29 +469,29 @@ msn_add_contact_xml(xmlnode *mlNode, const char *passport, MsnListOp list_op, Ms
if (d_node == NULL) {
/*domain not found, create a new domain Node*/
purple_debug_info("msn", "Didn't find existing domain node, adding one.\n");
- d_node = xmlnode_new("d");
- xmlnode_set_attrib(d_node, "n", domain);
- xmlnode_insert_child(mlNode, d_node);
+ d_node = purple_xmlnode_new("d");
+ purple_xmlnode_set_attrib(d_node, "n", domain);
+ purple_xmlnode_insert_child(mlNode, d_node);
}
/*create contact node*/
- c_node = xmlnode_new("c");
- xmlnode_set_attrib(c_node, "n", email);
+ c_node = purple_xmlnode_new("c");
+ purple_xmlnode_set_attrib(c_node, "n", email);
if (list_op != 0) {
purple_debug_info("msn", "list_op: %d\n", list_op);
g_snprintf(fmt_str, sizeof(fmt_str), "%d", list_op);
- xmlnode_set_attrib(c_node, "l", fmt_str);
+ purple_xmlnode_set_attrib(c_node, "l", fmt_str);
}
if (networkId != MSN_NETWORK_UNKNOWN) {
g_snprintf(fmt_str, sizeof(fmt_str), "%d", networkId);
/*mobile*/
/*type_str = g_strdup_printf("4");*/
- xmlnode_set_attrib(c_node, "t", fmt_str);
+ purple_xmlnode_set_attrib(c_node, "t", fmt_str);
}
- xmlnode_insert_child(d_node, c_node);
+ purple_xmlnode_insert_child(d_node, c_node);
g_strfreev(tokens);
}
@@ -562,20 +562,20 @@ update_contact_network(MsnSession *session, const char *passport, MsnNetwork net
/* TODO: Also figure out how to update membership lists */
user = msn_userlist_find_user(session->userlist, passport);
if (user) {
- xmlnode *adl_node;
+ PurpleXmlNode *adl_node;
char *payload;
int payload_len;
msn_user_set_network(user, network);
- adl_node = xmlnode_new("ml");
- xmlnode_set_attrib(adl_node, "l", "1");
+ adl_node = purple_xmlnode_new("ml");
+ purple_xmlnode_set_attrib(adl_node, "l", "1");
msn_add_contact_xml(adl_node, passport,
user->list_op & MSN_LIST_OP_MASK, network);
- payload = xmlnode_to_str(adl_node, &payload_len);
+ payload = purple_xmlnode_to_str(adl_node, &payload_len);
msn_notification_post_adl(session->notification->cmdproc, payload, payload_len);
g_free(payload);
- xmlnode_free(adl_node);
+ purple_xmlnode_free(adl_node);
} else {
purple_debug_error("msn",
"Got FQY update for unknown user %s on network %d.\n",
@@ -589,8 +589,8 @@ msn_notification_dump_contact(MsnSession *session)
{
MsnUser *user;
GList *l;
- xmlnode *adl_node;
- xmlnode *fqy_node;
+ PurpleXmlNode *adl_node;
+ PurpleXmlNode *fqy_node;
char *payload;
int payload_len;
int adl_count = 0;
@@ -598,10 +598,10 @@ msn_notification_dump_contact(MsnSession *session)
PurpleConnection *pc;
const char *display_name;
- adl_node = xmlnode_new("ml");
+ adl_node = purple_xmlnode_new("ml");
adl_node->child = NULL;
- xmlnode_set_attrib(adl_node, "l", "1");
- fqy_node = xmlnode_new("ml");
+ purple_xmlnode_set_attrib(adl_node, "l", "1");
+ fqy_node = purple_xmlnode_new("ml");
/*get the userlist*/
for (l = session->userlist->users; l != NULL; l = l->next) {
@@ -634,7 +634,7 @@ msn_notification_dump_contact(MsnSession *session)
/* each ADL command may contain up to 150 contacts */
if (++adl_count % 150 == 0) {
- payload = xmlnode_to_str(adl_node, &payload_len);
+ payload = purple_xmlnode_to_str(adl_node, &payload_len);
/* ADL's are returned all-together */
session->adl_fqy++;
@@ -646,11 +646,11 @@ msn_notification_dump_contact(MsnSession *session)
payload, payload_len);
g_free(payload);
- xmlnode_free(adl_node);
+ purple_xmlnode_free(adl_node);
- adl_node = xmlnode_new("ml");
+ adl_node = purple_xmlnode_new("ml");
adl_node->child = NULL;
- xmlnode_set_attrib(adl_node, "l", "1");
+ purple_xmlnode_set_attrib(adl_node, "l", "1");
}
} else {
/* FQY's are returned one-at-a-time */
@@ -663,21 +663,21 @@ msn_notification_dump_contact(MsnSession *session)
/* each FQY command may contain up to 150 contacts, probably */
if (++fqy_count % 150 == 0) {
- payload = xmlnode_to_str(fqy_node, &payload_len);
+ payload = purple_xmlnode_to_str(fqy_node, &payload_len);
msn_notification_send_fqy(session, payload, payload_len,
update_contact_network, NULL);
g_free(payload);
- xmlnode_free(fqy_node);
- fqy_node = xmlnode_new("ml");
+ purple_xmlnode_free(fqy_node);
+ fqy_node = purple_xmlnode_new("ml");
}
}
}
/* Send the rest, or just an empty one to let the server set us online */
if (adl_count == 0 || adl_count % 150 != 0) {
- payload = xmlnode_to_str(adl_node, &payload_len);
+ payload = purple_xmlnode_to_str(adl_node, &payload_len);
/* ADL's are returned all-together */
session->adl_fqy++;
@@ -691,7 +691,7 @@ msn_notification_dump_contact(MsnSession *session)
}
if (fqy_count % 150 != 0) {
- payload = xmlnode_to_str(fqy_node, &payload_len);
+ payload = purple_xmlnode_to_str(fqy_node, &payload_len);
msn_notification_send_fqy(session, payload, payload_len,
update_contact_network, NULL);
@@ -699,8 +699,8 @@ msn_notification_dump_contact(MsnSession *session)
g_free(payload);
}
- xmlnode_free(adl_node);
- xmlnode_free(fqy_node);
+ purple_xmlnode_free(adl_node);
+ purple_xmlnode_free(fqy_node);
msn_session_activate_login_timeout(session);
@@ -723,30 +723,30 @@ static void
adl_cmd_parse(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
size_t len)
{
- xmlnode *root, *domain_node;
+ PurpleXmlNode *root, *domain_node;
purple_debug_misc("msn", "Parsing received ADL XML data\n");
g_return_if_fail(payload != NULL);
- root = xmlnode_from_str(payload, (gssize) len);
+ root = purple_xmlnode_from_str(payload, (gssize) len);
if (root == NULL) {
purple_debug_info("msn", "Invalid XML in ADL!\n");
return;
}
- for (domain_node = xmlnode_get_child(root, "d");
+ for (domain_node = purple_xmlnode_get_child(root, "d");
domain_node;
- domain_node = xmlnode_get_next_twin(domain_node)) {
- xmlnode *contact_node = NULL;
+ domain_node = purple_xmlnode_get_next_twin(domain_node)) {
+ PurpleXmlNode *contact_node = NULL;
- for (contact_node = xmlnode_get_child(domain_node, "c");
+ for (contact_node = purple_xmlnode_get_child(domain_node, "c");
contact_node;
- contact_node = xmlnode_get_next_twin(contact_node)) {
+ contact_node = purple_xmlnode_get_next_twin(contact_node)) {
const gchar *list;
gint list_op = 0;
- list = xmlnode_get_attrib(contact_node, "l");
+ list = purple_xmlnode_get_attrib(contact_node, "l");
if (list != NULL) {
list_op = atoi(list);
}
@@ -758,7 +758,7 @@ adl_cmd_parse(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
}
}
- xmlnode_free(root);
+ purple_xmlnode_free(root);
}
static void
@@ -805,26 +805,26 @@ adl_error_parse(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
in the local list, but not the server list, and that we should add
those buddies to the addressbook. For now I will just notify the user
about the raw payload, because I am lazy */
- xmlnode *adl = xmlnode_from_str(payload, len);
+ PurpleXmlNode *adl = purple_xmlnode_from_str(payload, len);
GString *emails = g_string_new(NULL);
- xmlnode *domain = xmlnode_get_child(adl, "d");
+ PurpleXmlNode *domain = purple_xmlnode_get_child(adl, "d");
while (domain) {
- const char *domain_str = xmlnode_get_attrib(domain, "n");
- xmlnode *contact = xmlnode_get_child(domain, "c");
+ const char *domain_str = purple_xmlnode_get_attrib(domain, "n");
+ PurpleXmlNode *contact = purple_xmlnode_get_child(domain, "c");
while (contact) {
g_string_append_printf(emails, "%s@%s\n",
- xmlnode_get_attrib(contact, "n"), domain_str);
- contact = xmlnode_get_next_twin(contact);
+ purple_xmlnode_get_attrib(contact, "n"), domain_str);
+ contact = purple_xmlnode_get_next_twin(contact);
}
- domain = xmlnode_get_next_twin(domain);
+ domain = purple_xmlnode_get_next_twin(domain);
}
purple_notify_error(gc, NULL,
_("The following users are missing from your addressbook"),
- emails->str);
+ emails->str, purple_request_cpar_from_connection(gc));
g_string_free(emails, TRUE);
- xmlnode_free(adl);
+ purple_xmlnode_free(adl);
}
else
{
@@ -833,7 +833,8 @@ adl_error_parse(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
error, adl);
g_free(adl);
- purple_notify_error(gc, NULL, _("Unable to add user"), reason);
+ purple_notify_error(gc, NULL, _("Unable to add user"), reason,
+ purple_request_cpar_from_connection(gc));
g_free(reason);
}
}
@@ -857,7 +858,8 @@ adl_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
cmd->payload_cbdata = GINT_TO_POINTER(error);
} else {
char *reason = g_strdup_printf(_("Unknown error (%d)"), error);
- purple_notify_error(gc, NULL, _("Unable to add user"), reason);
+ purple_notify_error(gc, NULL, _("Unable to add user"), reason,
+ purple_request_cpar_from_connection(gc));
g_free(reason);
}
}
@@ -880,7 +882,8 @@ rml_error_parse(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
error, adl);
g_free(adl);
- purple_notify_error(gc, NULL, _("Unable to remove user"), reason);
+ purple_notify_error(gc, NULL, _("Unable to remove user"), reason,
+ purple_request_cpar_from_connection(gc));
g_free(reason);
}
@@ -903,7 +906,8 @@ rml_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
cmd->payload_cbdata = GINT_TO_POINTER(error);
} else {
char *reason = g_strdup_printf(_("Unknown error (%d)"), error);
- purple_notify_error(gc, NULL, _("Unable to remove user"), reason);
+ purple_notify_error(gc, NULL, _("Unable to remove user"),
+ reason, purple_request_cpar_from_connection(gc));
g_free(reason);
}
}
@@ -913,7 +917,7 @@ fqy_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
size_t len)
{
MsnSession *session;
- xmlnode *ml, *d, *c;
+ PurpleXmlNode *ml, *d, *c;
const char *domain;
const char *local;
const char *type;
@@ -924,16 +928,16 @@ fqy_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
/* FQY response:
<ml><d n="domain.com"><c n="local-node" t="network" /></d></ml> */
- ml = xmlnode_from_str(payload, len);
- for (d = xmlnode_get_child(ml, "d");
+ ml = purple_xmlnode_from_str(payload, len);
+ for (d = purple_xmlnode_get_child(ml, "d");
d != NULL;
- d = xmlnode_get_next_twin(d)) {
- domain = xmlnode_get_attrib(d, "n");
- for (c = xmlnode_get_child(d, "c");
+ d = purple_xmlnode_get_next_twin(d)) {
+ domain = purple_xmlnode_get_attrib(d, "n");
+ for (c = purple_xmlnode_get_child(d, "c");
c != NULL;
- c = xmlnode_get_next_twin(c)) {
- local = xmlnode_get_attrib(c, "n");
- type = xmlnode_get_attrib(c, "t");
+ c = purple_xmlnode_get_next_twin(c)) {
+ local = purple_xmlnode_get_attrib(c, "n");
+ type = purple_xmlnode_get_attrib(c, "t");
passport = g_strdup_printf("%s@%s", local, domain);
@@ -955,7 +959,7 @@ fqy_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
}
}
- xmlnode_free(ml);
+ purple_xmlnode_free(ml);
}
static void
@@ -1130,7 +1134,7 @@ ipg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
const char *who = NULL;
char *text = NULL;
const char *id = NULL;
- xmlnode *payloadNode, *from, *msg, *textNode;
+ PurpleXmlNode *payloadNode, *from, *msg, *textNode;
purple_debug_misc("msn", "Incoming Page: {%s}\n", payload);
@@ -1172,21 +1176,21 @@ ipg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
</NOTIFICATION>
*/
- payloadNode = xmlnode_from_str(payload, len);
+ payloadNode = purple_xmlnode_from_str(payload, len);
if (!payloadNode)
return;
- if (!(from = xmlnode_get_child(payloadNode, "FROM")) ||
- !(msg = xmlnode_get_child(payloadNode, "MSG")) ||
- !(textNode = xmlnode_get_child(msg, "BODY/TEXT"))) {
- xmlnode_free(payloadNode);
+ if (!(from = purple_xmlnode_get_child(payloadNode, "FROM")) ||
+ !(msg = purple_xmlnode_get_child(payloadNode, "MSG")) ||
+ !(textNode = purple_xmlnode_get_child(msg, "BODY/TEXT"))) {
+ purple_xmlnode_free(payloadNode);
return;
}
- who = xmlnode_get_attrib(from, "name");
+ who = purple_xmlnode_get_attrib(from, "name");
if (!who) return;
- text = xmlnode_get_data(textNode);
+ text = purple_xmlnode_get_data(textNode);
/* Match number to user's mobile number, FROM is a phone number if the
other side page you using your phone number */
@@ -1198,12 +1202,11 @@ ipg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
who = user->passport;
}
- id = xmlnode_get_attrib(msg, "id");
+ id = purple_xmlnode_get_attrib(msg, "id");
if (id && strcmp(id, "1")) {
PurpleConversation *conv
- = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
- who, gc->account);
+ = purple_conversations_find_with_account(who, purple_connection_get_account(gc));
if (conv != NULL) {
const char *error;
if (!strcmp(id, "407"))
@@ -1211,10 +1214,10 @@ ipg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
else
error = _("Mobile message was not sent because an unknown error occurred.");
- purple_conversation_write(conv, NULL, error,
- PURPLE_MESSAGE_ERROR, time(NULL));
+ purple_conversation_write_system_message(conv, error,
+ PURPLE_MESSAGE_ERROR);
- if ((id = xmlnode_get_attrib(payloadNode, "id")) != NULL) {
+ if ((id = purple_xmlnode_get_attrib(payloadNode, "id")) != NULL) {
unsigned int trId = atol(id);
MsnTransaction *trans;
@@ -1226,8 +1229,10 @@ ipg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
char *body_str = msn_message_to_string(msg);
char *body_enc = g_markup_escape_text(body_str, -1);
- purple_conversation_write(conv, NULL, body_enc,
- PURPLE_MESSAGE_RAW, time(NULL));
+ /* TODO: mark outgoing message as not delivered
+ * (API to be implemented) */
+ purple_conversation_write_system_message(conv,
+ body_enc, PURPLE_MESSAGE_RAW);
g_free(body_str);
g_free(body_enc);
@@ -1238,11 +1243,11 @@ ipg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
}
}
} else {
- serv_got_im(gc, who, text, 0, time(NULL));
+ purple_serv_got_im(gc, who, text, 0, time(NULL));
}
g_free(text);
- xmlnode_free(payloadNode);
+ purple_xmlnode_free(payloadNode);
}
static void
@@ -1394,15 +1399,16 @@ url_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
PurpleAccount *account;
const char *rru;
const char *url;
- PurpleCipherContext *cipher;
+ PurpleHash *hash;
gchar creds[33];
char *buf;
+ gboolean digest_ok;
gulong tmp_timestamp;
session = cmdproc->session;
account = session->account;
- gc = account->gc;
+ gc = purple_account_get_connection(account);
rru = cmd->params[1];
url = cmd->params[2];
@@ -1415,12 +1421,14 @@ url_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
tmp_timestamp,
purple_connection_get_password(gc));
- cipher = purple_cipher_context_new_by_name("md5", NULL);
- purple_cipher_context_append(cipher, (const guchar *)buf, strlen(buf));
- purple_cipher_context_digest_to_str(cipher, sizeof(creds), creds, NULL);
- purple_cipher_context_destroy(cipher);
+ hash = purple_md5_hash_new();
+ purple_hash_append(hash, (const guchar *)buf, strlen(buf));
+ digest_ok = purple_hash_digest_to_str(hash, creds, sizeof(creds));
+ g_object_unref(hash);
g_free(buf);
+ g_return_if_fail(digest_ok);
+
g_free(session->passport_info.mail_url);
session->passport_info.mail_url =
g_strdup_printf("%s&auth=%s&creds=%s&sl=%ld&username=%s&mode=ttl&sid=%s&id=2&rru=%s&svc=mail&js=yes",
@@ -1510,12 +1518,12 @@ gcf_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
{
/* QuLogic: Disabled until confirmed correct. */
#if 0
- xmlnode *root;
- xmlnode *policy;
+ PurpleXmlNode *root;
+ PurpleXmlNode *policy;
g_return_if_fail(cmd->payload != NULL);
- if ( (root = xmlnode_from_str(cmd->payload, cmd->payload_len)) == NULL)
+ if ( (root = purple_xmlnode_from_str(cmd->payload, cmd->payload_len)) == NULL)
{
purple_debug_error("msn", "Unable to parse GCF payload into a XML tree\n");
return;
@@ -1526,28 +1534,28 @@ gcf_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
cmdproc->session->blocked_text = NULL;
/* We need a get_child with attrib... */
- policy = xmlnode_get_child(root, "Policy");
+ policy = purple_xmlnode_get_child(root, "Policy");
while (policy) {
- if (g_str_equal(xmlnode_get_attrib(policy, "type"), "SHIELDS"))
+ if (g_str_equal(purple_xmlnode_get_attrib(policy, "type"), "SHIELDS"))
break;
- policy = xmlnode_get_next_twin(policy);
+ policy = purple_xmlnode_get_next_twin(policy);
}
if (policy) {
GString *blocked = g_string_new(NULL);
- xmlnode *imtext = xmlnode_get_child(policy,
+ PurpleXmlNode *imtext = purple_xmlnode_get_child(policy,
"config/block/regexp/imtext");
while (imtext) {
- const char *value = xmlnode_get_attrib(imtext, "value");
+ const char *value = purple_xmlnode_get_attrib(imtext, "value");
g_string_append_printf(blocked, "%s<br/>\n",
purple_base64_decode(value, NULL));
- imtext = xmlnode_get_next_twin(imtext);
+ imtext = purple_xmlnode_get_next_twin(imtext);
}
cmdproc->session->blocked_text = g_string_free(blocked, FALSE);
}
- xmlnode_free(root);
+ purple_xmlnode_free(root);
#endif
}
@@ -1568,10 +1576,10 @@ sbs_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
}
static void
-parse_user_endpoints(MsnUser *user, xmlnode *payloadNode)
+parse_user_endpoints(MsnUser *user, PurpleXmlNode *payloadNode)
{
MsnSession *session;
- xmlnode *epNode, *capsNode;
+ PurpleXmlNode *epNode, *capsNode;
MsnUserEndpoint data;
const char *id;
char *caps, *tmp;
@@ -1583,16 +1591,16 @@ parse_user_endpoints(MsnUser *user, xmlnode *payloadNode)
is_me = (user == session->user);
msn_user_clear_endpoints(user);
- for (epNode = xmlnode_get_child(payloadNode, "EndpointData");
+ for (epNode = purple_xmlnode_get_child(payloadNode, "EndpointData");
epNode;
- epNode = xmlnode_get_next_twin(epNode)) {
- id = xmlnode_get_attrib(epNode, "id");
- capsNode = xmlnode_get_child(epNode, "Capabilities");
+ epNode = purple_xmlnode_get_next_twin(epNode)) {
+ id = purple_xmlnode_get_attrib(epNode, "id");
+ capsNode = purple_xmlnode_get_child(epNode, "Capabilities");
/* Disconnect others, if MPOP is disabled */
if (is_me
&& !session->enable_mpop
- && strncasecmp(id + 1, session->guid, 36) != 0) {
+ && g_ascii_strncasecmp(id + 1, session->guid, 36) != 0) {
purple_debug_info("msn", "Disconnecting Endpoint %s\n", id);
tmp = g_strdup_printf("%s;%s", user->passport, id);
@@ -1600,7 +1608,7 @@ parse_user_endpoints(MsnUser *user, xmlnode *payloadNode)
g_free(tmp);
} else {
if (capsNode != NULL) {
- caps = xmlnode_get_data(capsNode);
+ caps = purple_xmlnode_get_data(capsNode);
data.clientid = strtoul(caps, &tmp, 10);
if (tmp && *tmp)
@@ -1619,11 +1627,11 @@ parse_user_endpoints(MsnUser *user, xmlnode *payloadNode)
}
if (is_me && session->enable_mpop) {
- for (epNode = xmlnode_get_child(payloadNode, "PrivateEndpointData");
+ for (epNode = purple_xmlnode_get_child(payloadNode, "PrivateEndpointData");
epNode;
- epNode = xmlnode_get_next_twin(epNode)) {
+ epNode = purple_xmlnode_get_next_twin(epNode)) {
MsnUserEndpoint *ep;
- xmlnode *nameNode, *clientNode;
+ PurpleXmlNode *nameNode, *clientNode;
/* <PrivateEndpointData id='{GUID}'>
<EpName>Endpoint Name</EpName>
@@ -1632,19 +1640,19 @@ parse_user_endpoints(MsnUser *user, xmlnode *payloadNode)
<State>NLN</State>
</PrivateEndpointData>
*/
- id = xmlnode_get_attrib(epNode, "id");
+ id = purple_xmlnode_get_attrib(epNode, "id");
ep = msn_user_get_endpoint_data(user, id);
if (ep != NULL) {
- nameNode = xmlnode_get_child(epNode, "EpName");
+ nameNode = purple_xmlnode_get_child(epNode, "EpName");
if (nameNode != NULL) {
g_free(ep->name);
- ep->name = xmlnode_get_data(nameNode);
+ ep->name = purple_xmlnode_get_data(nameNode);
}
- clientNode = xmlnode_get_child(epNode, "ClientType");
+ clientNode = purple_xmlnode_get_child(epNode, "ClientType");
if (clientNode != NULL) {
- tmp = xmlnode_get_data(clientNode);
+ tmp = purple_xmlnode_get_data(clientNode);
ep->type = strtoul(tmp, NULL, 10);
g_free(tmp);
}
@@ -1719,7 +1727,7 @@ ubx_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
MsnUser *user;
char *passport;
int network;
- xmlnode *payloadNode;
+ PurpleXmlNode *payloadNode;
char *psm_str, *str;
session = cmdproc->session;
@@ -1750,7 +1758,7 @@ ubx_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
}
if (len != 0) {
- payloadNode = xmlnode_from_str(payload, len);
+ payloadNode = purple_xmlnode_from_str(payload, len);
if (!payloadNode) {
purple_debug_error("msn", "UBX XML parse Error!\n");
@@ -1770,7 +1778,7 @@ ubx_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
parse_user_endpoints(user, payloadNode);
- xmlnode_free(payloadNode);
+ purple_xmlnode_free(payloadNode);
} else {
msn_user_set_statusline(user, NULL);
@@ -1820,48 +1828,48 @@ msn_notification_send_uux(MsnSession *session, const char *payload)
void msn_notification_send_uux_endpointdata(MsnSession *session)
{
- xmlnode *epDataNode;
- xmlnode *capNode;
+ PurpleXmlNode *epDataNode;
+ PurpleXmlNode *capNode;
char *caps;
char *payload;
int length;
- epDataNode = xmlnode_new("EndpointData");
+ epDataNode = purple_xmlnode_new("EndpointData");
- capNode = xmlnode_new_child(epDataNode, "Capabilities");
+ capNode = purple_xmlnode_new_child(epDataNode, "Capabilities");
caps = g_strdup_printf("%d:%02d", MSN_CLIENT_ID_CAPABILITIES, MSN_CLIENT_ID_EXT_CAPS);
- xmlnode_insert_data(capNode, caps, -1);
+ purple_xmlnode_insert_data(capNode, caps, -1);
g_free(caps);
- payload = xmlnode_to_str(epDataNode, &length);
+ payload = purple_xmlnode_to_str(epDataNode, &length);
msn_notification_send_uux(session, payload);
- xmlnode_free(epDataNode);
+ purple_xmlnode_free(epDataNode);
g_free(payload);
}
void msn_notification_send_uux_private_endpointdata(MsnSession *session)
{
- xmlnode *private;
+ PurpleXmlNode *private;
const char *name;
- xmlnode *epname;
- xmlnode *idle;
+ PurpleXmlNode *epname;
+ PurpleXmlNode *idle;
GHashTable *ui_info;
const gchar *ui_type;
- xmlnode *client_type;
- xmlnode *state;
+ PurpleXmlNode *client_type;
+ PurpleXmlNode *state;
char *payload;
int length;
- private = xmlnode_new("PrivateEndpointData");
+ private = purple_xmlnode_new("PrivateEndpointData");
name = purple_account_get_string(session->account, "endpoint-name", NULL);
- epname = xmlnode_new_child(private, "EpName");
- xmlnode_insert_data(epname, name, -1);
+ epname = purple_xmlnode_new_child(private, "EpName");
+ purple_xmlnode_insert_data(epname, name, -1);
- idle = xmlnode_new_child(private, "Idle");
- xmlnode_insert_data(idle, "false", -1);
+ idle = purple_xmlnode_new_child(private, "Idle");
+ purple_xmlnode_insert_data(idle, "false", -1);
/* ClientType info (from amsn guys):
0: None
@@ -1872,32 +1880,32 @@ void msn_notification_send_uux_private_endpointdata(MsnSession *session)
9: MsnGroup
32: Email member, currently Yahoo!
*/
- client_type = xmlnode_new_child(private, "ClientType");
+ client_type = purple_xmlnode_new_child(private, "ClientType");
ui_info = purple_core_get_ui_info();
ui_type = ui_info ? g_hash_table_lookup(ui_info, "client_type") : NULL;
if (ui_type) {
if (strcmp(ui_type, "pc") == 0)
- xmlnode_insert_data(client_type, "1", -1);
+ purple_xmlnode_insert_data(client_type, "1", -1);
else if (strcmp(ui_type, "web") == 0)
- xmlnode_insert_data(client_type, "2", -1);
+ purple_xmlnode_insert_data(client_type, "2", -1);
else if (strcmp(ui_type, "phone") == 0)
- xmlnode_insert_data(client_type, "3", -1);
+ purple_xmlnode_insert_data(client_type, "3", -1);
else if (strcmp(ui_type, "handheld") == 0)
- xmlnode_insert_data(client_type, "3", -1);
+ purple_xmlnode_insert_data(client_type, "3", -1);
else
- xmlnode_insert_data(client_type, "1", -1);
+ purple_xmlnode_insert_data(client_type, "1", -1);
}
else
- xmlnode_insert_data(client_type, "1", -1);
+ purple_xmlnode_insert_data(client_type, "1", -1);
- state = xmlnode_new_child(private, "State");
- xmlnode_insert_data(state, msn_state_get_text(msn_state_from_account(session->account)), -1);
+ state = purple_xmlnode_new_child(private, "State");
+ purple_xmlnode_insert_data(state, msn_state_get_text(msn_state_from_account(session->account)), -1);
- payload = xmlnode_to_str(private, &length);
+ payload = purple_xmlnode_to_str(private, &length);
msn_notification_send_uux(session, payload);
- xmlnode_free(private);
+ purple_xmlnode_free(private);
g_free(payload);
}
@@ -2041,7 +2049,7 @@ initial_email_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
const char *unread;
session = cmdproc->session;
- gc = session->account->gc;
+ gc = purple_account_get_connection(session->account);
if (strcmp(msg->remote_user, "Hotmail"))
/* This isn't an official message. */
@@ -2092,7 +2100,7 @@ initial_mdata_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
const char *mdata, *unread;
session = cmdproc->session;
- gc = session->account->gc;
+ gc = purple_account_get_connection(session->account);
if (strcmp(msg->remote_user, "Hotmail"))
/* This isn't an official message. */
@@ -2164,7 +2172,7 @@ email_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
char *from, *subject, *tmp;
session = cmdproc->session;
- gc = session->account->gc;
+ gc = purple_account_get_connection(session->account);
if (strcmp(msg->remote_user, "Hotmail"))
/* This isn't an official message. */
@@ -2249,7 +2257,10 @@ system_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
}
if (*buf != '\0')
- purple_notify_info(cmdproc->session->account->gc, NULL, buf, NULL);
+ purple_notify_info(purple_account_get_connection(
+ cmdproc->session->account),
+ NULL, buf, NULL, purple_request_cpar_from_account(
+ cmdproc->session->account));
}
g_hash_table_destroy(table);
@@ -2271,7 +2282,7 @@ modify_unknown_buddy_on_list(MsnSession *session, const char *passport,
{
MsnAddRemoveListData *addrem = data;
MsnCmdProc *cmdproc;
- xmlnode *node;
+ PurpleXmlNode *node;
char *payload;
int payload_len;
@@ -2280,13 +2291,13 @@ modify_unknown_buddy_on_list(MsnSession *session, const char *passport,
/* Update user first */
msn_user_set_network(addrem->user, network);
- node = xmlnode_new("ml");
+ node = purple_xmlnode_new("ml");
node->child = NULL;
msn_add_contact_xml(node, passport, addrem->list_op, network);
- payload = xmlnode_to_str(node, &payload_len);
- xmlnode_free(node);
+ payload = purple_xmlnode_to_str(node, &payload_len);
+ purple_xmlnode_free(node);
if (addrem->add)
msn_notification_post_adl(cmdproc, payload, payload_len);
@@ -2304,19 +2315,19 @@ msn_notification_add_buddy_to_list(MsnNotification *notification, MsnListId list
MsnAddRemoveListData *addrem;
MsnCmdProc *cmdproc;
MsnListOp list_op = 1 << list_id;
- xmlnode *adl_node;
+ PurpleXmlNode *adl_node;
char *payload;
int payload_len;
cmdproc = notification->servconn->cmdproc;
- adl_node = xmlnode_new("ml");
+ adl_node = purple_xmlnode_new("ml");
adl_node->child = NULL;
msn_add_contact_xml(adl_node, user->passport, list_op, user->networkid);
- payload = xmlnode_to_str(adl_node, &payload_len);
- xmlnode_free(adl_node);
+ payload = purple_xmlnode_to_str(adl_node, &payload_len);
+ purple_xmlnode_free(adl_node);
if (user->networkid != MSN_NETWORK_UNKNOWN) {
msn_notification_post_adl(cmdproc, payload, payload_len);
@@ -2342,19 +2353,19 @@ msn_notification_rem_buddy_from_list(MsnNotification *notification, MsnListId li
MsnAddRemoveListData *addrem;
MsnCmdProc *cmdproc;
MsnListOp list_op = 1 << list_id;
- xmlnode *rml_node;
+ PurpleXmlNode *rml_node;
char *payload;
int payload_len;
cmdproc = notification->servconn->cmdproc;
- rml_node = xmlnode_new("ml");
+ rml_node = purple_xmlnode_new("ml");
rml_node->child = NULL;
msn_add_contact_xml(rml_node, user->passport, list_op, user->networkid);
- payload = xmlnode_to_str(rml_node, &payload_len);
- xmlnode_free(rml_node);
+ payload = purple_xmlnode_to_str(rml_node, &payload_len);
+ purple_xmlnode_free(rml_node);
if (user->networkid != MSN_NETWORK_UNKNOWN) {
msn_notification_post_rml(cmdproc, payload, payload_len);
diff --git a/libpurple/protocols/msn/object.c b/libpurple/protocols/msn/object.c
index bee264109a..14a479af15 100644
--- a/libpurple/protocols/msn/object.c
+++ b/libpurple/protocols/msn/object.c
@@ -26,7 +26,7 @@
#include "object.h"
#include "debug.h"
/* Sha1 stuff */
-#include "cipher.h"
+#include "ciphers/sha1hash.h"
/* Base64 stuff */
#include "util.h"
@@ -103,7 +103,7 @@ msn_object_new_from_string(const char *str)
if (obj->creator == NULL || obj->size == 0 || obj->type == 0
|| obj->sha1d == NULL) {
purple_debug_error("msn", "Discarding invalid msnobj: '%s'\n", str);
- msn_object_destroy(obj);
+ msn_object_destroy(obj, FALSE);
return NULL;
}
@@ -111,12 +111,12 @@ msn_object_new_from_string(const char *str)
/* Location/friendly are required for non-buddyicon objects */
if (obj->type != MSN_OBJECT_USERTILE) {
purple_debug_error("msn", "Discarding invalid msnobj: '%s'\n", str);
- msn_object_destroy(obj);
+ msn_object_destroy(obj, FALSE);
return NULL;
/* Buddy icon object can contain Url/Url1 instead */
} else if (obj->url == NULL || obj->url1 == NULL) {
purple_debug_error("msn", "Discarding invalid msnobj: '%s'\n", str);
- msn_object_destroy(obj);
+ msn_object_destroy(obj, FALSE);
return NULL;
}
}
@@ -125,12 +125,12 @@ msn_object_new_from_string(const char *str)
}
MsnObject*
-msn_object_new_from_image(PurpleStoredImage *img, const char *location,
+msn_object_new_from_image(PurpleImage *img, const char *location,
const char *creator, MsnObjectType type)
{
MsnObject *msnobj;
- PurpleCipherContext *ctx;
+ PurpleHash *hash;
char *buf;
gconstpointer data;
size_t size;
@@ -140,10 +140,10 @@ msn_object_new_from_image(PurpleStoredImage *img, const char *location,
msnobj = NULL;
if (img == NULL)
- return msnobj;
+ return NULL;
- size = purple_imgstore_get_size(img);
- data = purple_imgstore_get_data(img);
+ size = purple_image_get_size(img);
+ data = purple_image_get_data(img);
/* New object */
msnobj = msn_object_new();
@@ -157,9 +157,9 @@ msn_object_new_from_image(PurpleStoredImage *img, const char *location,
/* Compute the SHA1D field. */
memset(digest, 0, sizeof(digest));
- ctx = purple_cipher_context_new_by_name("sha1", NULL);
- purple_cipher_context_append(ctx, data, size);
- purple_cipher_context_digest(ctx, sizeof(digest), digest, NULL);
+ hash = purple_sha1_hash_new();
+ purple_hash_append(hash, data, size);
+ purple_hash_digest(hash, digest, sizeof(digest));
base64 = purple_base64_encode(digest, sizeof(digest));
msn_object_set_sha1d(msnobj, base64);
@@ -179,10 +179,10 @@ msn_object_new_from_image(PurpleStoredImage *img, const char *location,
memset(digest, 0, sizeof(digest));
- purple_cipher_context_reset(ctx, NULL);
- purple_cipher_context_append(ctx, (const guchar *)buf, strlen(buf));
- purple_cipher_context_digest(ctx, sizeof(digest), digest, NULL);
- purple_cipher_context_destroy(ctx);
+ purple_hash_reset(hash);
+ purple_hash_append(hash, (const guchar *)buf, strlen(buf));
+ purple_hash_digest(hash, digest, sizeof(digest));
+ g_object_unref(hash);
g_free(buf);
base64 = purple_base64_encode(digest, sizeof(digest));
@@ -193,10 +193,13 @@ msn_object_new_from_image(PurpleStoredImage *img, const char *location,
}
void
-msn_object_destroy(MsnObject *obj)
+msn_object_destroy(MsnObject *obj, gboolean only_remote)
{
g_return_if_fail(obj != NULL);
+ if (only_remote && obj->local)
+ return;
+
g_free(obj->creator);
g_free(obj->location);
g_free(obj->friendly);
@@ -205,7 +208,8 @@ msn_object_destroy(MsnObject *obj)
g_free(obj->url);
g_free(obj->url1);
- purple_imgstore_unref(obj->img);
+ if (obj->img)
+ g_object_unref(obj->img);
if (obj->local)
local_objs = g_list_remove(local_objs, obj);
@@ -432,18 +436,20 @@ msn_object_set_local(MsnObject *obj)
}
void
-msn_object_set_image(MsnObject *obj, PurpleStoredImage *img)
+msn_object_set_image(MsnObject *obj, PurpleImage *img)
{
g_return_if_fail(obj != NULL);
g_return_if_fail(img != NULL);
/* obj->local = TRUE; */
- purple_imgstore_unref(obj->img);
- obj->img = purple_imgstore_ref(img);
+ if (obj->img)
+ g_object_unref(obj->img);
+ g_object_ref(img);
+ obj->img = img;
}
-PurpleStoredImage *
+PurpleImage *
msn_object_get_image(const MsnObject *obj)
{
MsnObject *local_obj;
diff --git a/libpurple/protocols/msn/object.h b/libpurple/protocols/msn/object.h
index 81572dc799..9a1d7d00f9 100644
--- a/libpurple/protocols/msn/object.h
+++ b/libpurple/protocols/msn/object.h
@@ -36,8 +36,6 @@ typedef enum
#include "internal.h"
-#include "imgstore.h"
-
typedef struct
{
gboolean local;
@@ -45,7 +43,7 @@ typedef struct
char *creator;
int size;
MsnObjectType type;
- PurpleStoredImage *img;
+ PurpleImage *img;
char *location;
char *friendly;
char *sha1d;
@@ -80,15 +78,16 @@ MsnObject *msn_object_new_from_string(const char *str);
*
* @return A new MsnObject structure
*/
-MsnObject *msn_object_new_from_image(PurpleStoredImage *img,
+MsnObject *msn_object_new_from_image(PurpleImage *img,
const char *location, const char *creator, MsnObjectType type);
/**
* Destroys an MsnObject structure.
*
- * @param obj The object structure.
+ * @param obj The object structure.
+ * @param only_remote Only destroy non-local objects.
*/
-void msn_object_destroy(MsnObject *obj);
+void msn_object_destroy(MsnObject *obj, gboolean only_remote);
/**
* Outputs a string representation of an MsnObject.
@@ -154,7 +153,7 @@ void msn_object_set_sha1c(MsnObject *obj, const char *sha1c);
* @param obj The object.
* @param img The image to associate.
*/
-void msn_object_set_image(MsnObject *obj, PurpleStoredImage *img);
+void msn_object_set_image(MsnObject *obj, PurpleImage *img);
/**
* Sets the url field in a MsnObject.
@@ -249,7 +248,7 @@ const char *msn_object_get_sha1(const MsnObject *obj);
*
* @return The associated image.
*/
-PurpleStoredImage *msn_object_get_image(const MsnObject *obj);
+PurpleImage *msn_object_get_image(const MsnObject *obj);
/**
* Returns a MsnObject's url value.
diff --git a/libpurple/protocols/msn/oim.c b/libpurple/protocols/msn/oim.c
index 54b5a6648a..8007bb190e 100644
--- a/libpurple/protocols/msn/oim.c
+++ b/libpurple/protocols/msn/oim.c
@@ -44,7 +44,7 @@ typedef struct {
} MsnOimRecvData;
/*Local Function Prototype*/
-static void msn_parse_oim_xml(MsnOim *oim, xmlnode *node);
+static void msn_parse_oim_xml(MsnOim *oim, PurpleXmlNode *node);
static void msn_oim_free_send_req(MsnOimSendReq *req);
static void msn_oim_recv_data_free(MsnOimRecvData *data);
static void msn_oim_post_single_get_msg(MsnOim *oim, MsnOimRecvData *data);
@@ -151,7 +151,7 @@ typedef struct _MsnOimRequestData {
const char *action;
const char *host;
const char *url;
- xmlnode *body;
+ PurpleXmlNode *body;
MsnSoapCallback cb;
gpointer cb_data;
} MsnOimRequestData;
@@ -163,14 +163,14 @@ msn_oim_request_cb(MsnSoapMessage *request, MsnSoapMessage *response,
gpointer req_data)
{
MsnOimRequestData *data = (MsnOimRequestData *)req_data;
- xmlnode *fault = NULL;
- xmlnode *faultcode = NULL;
+ PurpleXmlNode *fault = NULL;
+ PurpleXmlNode *faultcode = NULL;
if (response != NULL)
- fault = xmlnode_get_child(response->xml, "Body/Fault");
+ fault = purple_xmlnode_get_child(msn_soap_message_get_xml(response), "Body/Fault");
- if (fault && (faultcode = xmlnode_get_child(fault, "faultcode"))) {
- gchar *faultcode_str = xmlnode_get_data(faultcode);
+ if (fault && (faultcode = purple_xmlnode_get_child(fault, "faultcode"))) {
+ gchar *faultcode_str = purple_xmlnode_get_data(faultcode);
gboolean need_token_update = FALSE;
if (faultcode_str) {
@@ -179,7 +179,7 @@ msn_oim_request_cb(MsnSoapMessage *request, MsnSoapMessage *response,
g_str_equal(faultcode_str, "s:AuthenticationFailed"))
need_token_update = TRUE;
else if (g_str_equal(faultcode_str, "q0:AuthenticationFailed") &&
- xmlnode_get_child(fault, "detail/RequiredAuthPolicy") != NULL)
+ purple_xmlnode_get_child(fault, "detail/RequiredAuthPolicy") != NULL)
need_token_update = TRUE;
}
@@ -198,7 +198,7 @@ msn_oim_request_cb(MsnSoapMessage *request, MsnSoapMessage *response,
if (data->cb)
data->cb(request, response, data->cb_data);
- xmlnode_free(data->body);
+ purple_xmlnode_free(data->body);
g_free(data);
}
@@ -209,16 +209,16 @@ msn_oim_request_helper(MsnOimRequestData *data)
if (data->send) {
/* The Sending of OIM's uses a different token for some reason. */
- xmlnode *ticket;
- ticket = xmlnode_get_child(data->body, "Header/Ticket");
- xmlnode_set_attrib(ticket, "passport",
+ PurpleXmlNode *ticket;
+ ticket = purple_xmlnode_get_child(data->body, "Header/Ticket");
+ purple_xmlnode_set_attrib(ticket, "passport",
msn_nexus_get_token_str(session->nexus, MSN_AUTH_LIVE_SECURE));
}
else
{
- xmlnode *passport;
- xmlnode *xml_t;
- xmlnode *xml_p;
+ PurpleXmlNode *passport;
+ PurpleXmlNode *xml_t;
+ PurpleXmlNode *xml_p;
GHashTable *token;
const char *msn_t;
const char *msn_p;
@@ -232,22 +232,21 @@ msn_oim_request_helper(MsnOimRequestData *data)
g_return_val_if_fail(msn_t != NULL, FALSE);
g_return_val_if_fail(msn_p != NULL, FALSE);
- passport = xmlnode_get_child(data->body, "Header/PassportCookie");
- xml_t = xmlnode_get_child(passport, "t");
- xml_p = xmlnode_get_child(passport, "p");
+ passport = purple_xmlnode_get_child(data->body, "Header/PassportCookie");
+ xml_t = purple_xmlnode_get_child(passport, "t");
+ xml_p = purple_xmlnode_get_child(passport, "p");
/* frees old token text, or the 'EMPTY' text if first time */
- xmlnode_free(xml_t->child);
- xmlnode_free(xml_p->child);
+ purple_xmlnode_free(xml_t->child);
+ purple_xmlnode_free(xml_p->child);
- xmlnode_insert_data(xml_t, msn_t, -1);
- xmlnode_insert_data(xml_p, msn_p, -1);
+ purple_xmlnode_insert_data(xml_t, msn_t, -1);
+ purple_xmlnode_insert_data(xml_p, msn_p, -1);
}
- msn_soap_message_send(session,
- msn_soap_message_new(data->action, xmlnode_copy(data->body)),
- data->host, data->url, FALSE,
- msn_oim_request_cb, data);
+ msn_soap_service_send_message(session->soap,
+ msn_soap_message_new(data->action, purple_xmlnode_copy(data->body)),
+ data->host, data->url, FALSE, msn_oim_request_cb, data);
return FALSE;
}
@@ -255,7 +254,7 @@ msn_oim_request_helper(MsnOimRequestData *data)
static void
msn_oim_make_request(MsnOim *oim, gboolean send, const char *action,
- const char *host, const char *url, xmlnode *body, MsnSoapCallback cb,
+ const char *host, const char *url, PurpleXmlNode *body, MsnSoapCallback cb,
gpointer cb_data)
{
MsnOimRequestData *data = g_new0(MsnOimRequestData, 1);
@@ -282,7 +281,7 @@ msn_oim_get_metadata_cb(MsnSoapMessage *request, MsnSoapMessage *response,
if (response) {
msn_parse_oim_xml(oim,
- xmlnode_get_child(response->xml, "Body/GetMetadataResponse/MD"));
+ purple_xmlnode_get_child(msn_soap_message_get_xml(response), "Body/GetMetadataResponse/MD"));
}
}
@@ -292,7 +291,7 @@ msn_oim_get_metadata(MsnOim *oim)
{
msn_oim_make_request(oim, FALSE, MSN_OIM_GET_METADATA_ACTION,
MSN_OIM_RETRIEVE_HOST, MSN_OIM_RETRIEVE_URL,
- xmlnode_from_str(MSN_OIM_GET_METADATA_TEMPLATE, -1),
+ purple_xmlnode_from_str(MSN_OIM_GET_METADATA_TEMPLATE, -1),
msn_oim_get_metadata_cb, oim);
}
@@ -351,23 +350,23 @@ msn_oim_send_read_cb(MsnSoapMessage *request, MsnSoapMessage *response,
if (response == NULL) {
purple_debug_info("msn", "cannot send OIM: %s\n", msg->oim_msg);
} else {
- xmlnode *faultNode = xmlnode_get_child(response->xml, "Body/Fault");
+ PurpleXmlNode *faultNode = purple_xmlnode_get_child(msn_soap_message_get_xml(response), "Body/Fault");
if (faultNode == NULL) {
/*Send OK! return*/
purple_debug_info("msn", "sent OIM: %s\n", msg->oim_msg);
} else {
- xmlnode *faultcode = xmlnode_get_child(faultNode, "faultcode");
+ PurpleXmlNode *faultcode = purple_xmlnode_get_child(faultNode, "faultcode");
if (faultcode) {
- char *faultcode_str = xmlnode_get_data(faultcode);
+ char *faultcode_str = purple_xmlnode_get_data(faultcode);
if (faultcode_str && g_str_equal(faultcode_str, "q0:AuthenticationFailed")) {
- xmlnode *challengeNode = xmlnode_get_child(faultNode,
+ PurpleXmlNode *challengeNode = purple_xmlnode_get_child(faultNode,
"detail/LockKeyChallenge");
char *challenge = NULL;
- if (challengeNode == NULL || (challenge = xmlnode_get_data(challengeNode)) == NULL) {
+ if (challengeNode == NULL || (challenge = purple_xmlnode_get_data(challengeNode)) == NULL) {
if (oim->challenge) {
g_free(oim->challenge);
oim->challenge = NULL;
@@ -479,7 +478,7 @@ msn_oim_send_msg(MsnOim *oim)
msg_body);
msn_oim_make_request(oim, TRUE, MSN_OIM_SEND_SOAP_ACTION, MSN_OIM_SEND_HOST,
- MSN_OIM_SEND_URL, xmlnode_from_str(soap_body, -1), msn_oim_send_read_cb,
+ MSN_OIM_SEND_URL, purple_xmlnode_from_str(soap_body, -1), msn_oim_send_read_cb,
oim);
/*increase the offline Sequence control*/
@@ -500,7 +499,7 @@ msn_oim_delete_read_cb(MsnSoapMessage *request, MsnSoapMessage *response,
{
MsnOimRecvData *rdata = data;
- if (response && xmlnode_get_child(response->xml, "Body/Fault") == NULL)
+ if (response && purple_xmlnode_get_child(msn_soap_message_get_xml(response), "Body/Fault") == NULL)
purple_debug_info("msn", "Delete OIM success\n");
else
purple_debug_info("msn", "Delete OIM failed\n");
@@ -521,7 +520,7 @@ msn_oim_post_delete_msg(MsnOimRecvData *rdata)
soap_body = g_strdup_printf(MSN_OIM_DEL_TEMPLATE, msgid);
msn_oim_make_request(oim, FALSE, MSN_OIM_DEL_SOAP_ACTION, MSN_OIM_RETRIEVE_HOST,
- MSN_OIM_RETRIEVE_URL, xmlnode_from_str(soap_body, -1), msn_oim_delete_read_cb, rdata);
+ MSN_OIM_RETRIEVE_URL, purple_xmlnode_from_str(soap_body, -1), msn_oim_delete_read_cb, rdata);
g_free(soap_body);
}
@@ -739,7 +738,7 @@ msn_oim_report_to_user(MsnOimRecvData *rdata, const char *msg_str)
purple_debug_info("msn", "oim Date:{%s},passport{%s}\n",
date, passport);
- serv_got_im(purple_account_get_connection(rdata->oim->session->account), passport, clean_msg, 0,
+ purple_serv_got_im(purple_account_get_connection(rdata->oim->session->account), passport, clean_msg, 0,
stamp);
/*Now get the oim message ID from the oim_list.
@@ -762,15 +761,15 @@ msn_oim_get_read_cb(MsnSoapMessage *request, MsnSoapMessage *response,
MsnOimRecvData *rdata = data;
if (response != NULL) {
- xmlnode *msg_node = xmlnode_get_child(response->xml,
+ PurpleXmlNode *msg_node = purple_xmlnode_get_child(msn_soap_message_get_xml(response),
"Body/GetMessageResponse/GetMessageResult");
if (msg_node) {
- char *msg_str = xmlnode_get_data(msg_node);
+ char *msg_str = purple_xmlnode_get_data(msg_node);
msn_oim_report_to_user(rdata, msg_str);
g_free(msg_str);
} else {
- char *str = xmlnode_to_str(response->xml, NULL);
+ char *str = purple_xmlnode_to_str(msn_soap_message_get_xml(response), NULL);
purple_debug_info("msn", "Unknown OIM response: %s\n", str);
g_free(str);
msn_oim_recv_data_free(rdata);
@@ -788,7 +787,7 @@ msn_oim_get_read_cb(MsnSoapMessage *request, MsnSoapMessage *response,
void
msn_parse_oim_msg(MsnOim *oim,const char *xmlmsg)
{
- xmlnode *node;
+ PurpleXmlNode *node;
purple_debug_info("msn", "%s\n", xmlmsg);
@@ -796,61 +795,61 @@ msn_parse_oim_msg(MsnOim *oim,const char *xmlmsg)
/* Too many OIM's to send via NS, so we need to request them via SOAP. */
msn_oim_get_metadata(oim);
} else {
- node = xmlnode_from_str(xmlmsg, -1);
+ node = purple_xmlnode_from_str(xmlmsg, -1);
msn_parse_oim_xml(oim, node);
- xmlnode_free(node);
+ purple_xmlnode_free(node);
}
}
static void
-msn_parse_oim_xml(MsnOim *oim, xmlnode *node)
+msn_parse_oim_xml(MsnOim *oim, PurpleXmlNode *node)
{
- xmlnode *mNode;
- xmlnode *iu_node;
+ PurpleXmlNode *mNode;
+ PurpleXmlNode *iu_node;
MsnSession *session = oim->session;
g_return_if_fail(node != NULL);
if (strcmp(node->name, "MD") != 0) {
- char *xmlmsg = xmlnode_to_str(node, NULL);
+ char *xmlmsg = purple_xmlnode_to_str(node, NULL);
purple_debug_info("msn", "WTF is this? %s\n", xmlmsg);
g_free(xmlmsg);
return;
}
- iu_node = xmlnode_get_child(node, "E/IU");
+ iu_node = purple_xmlnode_get_child(node, "E/IU");
if (iu_node != NULL && purple_account_get_check_mail(session->account))
{
- char *unread = xmlnode_get_data(iu_node);
+ char *unread = purple_xmlnode_get_data(iu_node);
const char *passports[2] = { msn_user_get_passport(session->user) };
const char *urls[2] = { session->passport_info.mail_url };
int count;
/* XXX/khc: pretty sure this is wrong */
if (unread && (count = atoi(unread)) > 0)
- purple_notify_emails(session->account->gc, count, FALSE, NULL,
+ purple_notify_emails(purple_account_get_connection(session->account), count, FALSE, NULL,
NULL, passports, urls, NULL, NULL);
g_free(unread);
}
- for(mNode = xmlnode_get_child(node, "M"); mNode;
- mNode = xmlnode_get_next_twin(mNode)){
+ for(mNode = purple_xmlnode_get_child(node, "M"); mNode;
+ mNode = purple_xmlnode_get_next_twin(mNode)){
char *passport, *msgid, *nickname, *rtime = NULL;
- xmlnode *e_node, *i_node, *n_node, *rt_node;
+ PurpleXmlNode *e_node, *i_node, *n_node, *rt_node;
- e_node = xmlnode_get_child(mNode, "E");
- passport = xmlnode_get_data(e_node);
+ e_node = purple_xmlnode_get_child(mNode, "E");
+ passport = purple_xmlnode_get_data(e_node);
- i_node = xmlnode_get_child(mNode, "I");
- msgid = xmlnode_get_data(i_node);
+ i_node = purple_xmlnode_get_child(mNode, "I");
+ msgid = purple_xmlnode_get_data(i_node);
- n_node = xmlnode_get_child(mNode, "N");
- nickname = xmlnode_get_data(n_node);
+ n_node = purple_xmlnode_get_child(mNode, "N");
+ nickname = purple_xmlnode_get_data(n_node);
- rt_node = xmlnode_get_child(mNode, "RT");
+ rt_node = purple_xmlnode_get_child(mNode, "RT");
if (rt_node != NULL) {
- rtime = xmlnode_get_data(rt_node);
+ rtime = purple_xmlnode_get_data(rt_node);
}
/* purple_debug_info("msn", "E:{%s},I:{%s},rTime:{%s}\n",passport,msgid,rTime); */
@@ -878,7 +877,7 @@ msn_oim_post_single_get_msg(MsnOim *oim, MsnOimRecvData *data)
soap_body = g_strdup_printf(MSN_OIM_GET_TEMPLATE, data->msg_id);
msn_oim_make_request(oim, FALSE, MSN_OIM_GET_SOAP_ACTION, MSN_OIM_RETRIEVE_HOST,
- MSN_OIM_RETRIEVE_URL, xmlnode_from_str(soap_body, -1), msn_oim_get_read_cb,
+ MSN_OIM_RETRIEVE_URL, purple_xmlnode_from_str(soap_body, -1), msn_oim_get_read_cb,
data);
g_free(soap_body);
diff --git a/libpurple/protocols/msn/p2p.c b/libpurple/protocols/msn/p2p.c
index 9b028e27bd..c4187056f0 100644
--- a/libpurple/protocols/msn/p2p.c
+++ b/libpurple/protocols/msn/p2p.c
@@ -133,7 +133,9 @@ msn_p2p_header_from_wire(MsnP2PInfo *info, const char *wire, size_t max_len)
header->opcode = msn_pop8(wire);
header->message_len = msn_pop16be(wire);
header->base_id = msn_pop32be(wire);
- if (header->header_len + header->message_len + P2P_PACKET_FOOTER_SIZE > max_len) {
+ if ((gsize)header->header_len + header->message_len +
+ P2P_PACKET_FOOTER_SIZE > max_len)
+ {
/* Invalid header and data length */
len = 0;
break;
@@ -159,7 +161,9 @@ msn_p2p_header_from_wire(MsnP2PInfo *info, const char *wire, size_t max_len)
if (header->data_header_len > 8) {
header->data_tlv = msn_tlvlist_read(wire, header->data_header_len - 8);
+#if 0
wire += header->data_header_len - 8;
+#endif
}
}
@@ -207,13 +211,13 @@ msn_p2p_header_to_wire(MsnP2PInfo *info, size_t *len)
char *header_wire = NULL;
char *data_header_wire = NULL;
- if (header->header_tlv != NULL)
- header_wire = msn_tlvlist_write(header->header_tlv, (size_t *)&header->header_len);
- else
+ if (header->header_tlv != NULL) {
+ header_wire = msn_tlvlist_write(header->header_tlv, &header->header_len);
+ } else
header->header_len = 0;
if (header->data_tlv != NULL)
- data_header_wire = msn_tlvlist_write(header->data_tlv, (size_t *)&header->data_header_len);
+ data_header_wire = msn_tlvlist_write(header->data_tlv, &header->data_header_len);
else
header->data_header_len = 0;
@@ -236,7 +240,9 @@ msn_p2p_header_to_wire(MsnP2PInfo *info, size_t *len)
if (data_header_wire != NULL) {
memcpy(tmp, data_header_wire, header->data_header_len);
+#if 0
tmp += header->data_header_len;
+#endif
}
if (len)
diff --git a/libpurple/protocols/msn/sbconn.c b/libpurple/protocols/msn/sbconn.c
index a9a6288844..3acb5dd217 100644
--- a/libpurple/protocols/msn/sbconn.c
+++ b/libpurple/protocols/msn/sbconn.c
@@ -121,6 +121,8 @@ release_msg(MsnSwitchBoard *swboard, MsnMessage *msg)
}
#endif
}
+ } else {
+ msn_transaction_set_saveable(trans, FALSE);
}
trans->payload = payload;
diff --git a/libpurple/protocols/msn/servconn.c b/libpurple/protocols/msn/servconn.c
index 617216cee2..8427a8564f 100644
--- a/libpurple/protocols/msn/servconn.c
+++ b/libpurple/protocols/msn/servconn.c
@@ -53,7 +53,7 @@ msn_servconn_new(MsnSession *session, MsnServConnType type)
servconn->num = session->servconns_count++;
- servconn->tx_buf = purple_circ_buffer_new(MSN_BUF_LEN);
+ servconn->tx_buf = purple_circular_buffer_new(MSN_BUF_LEN);
servconn->tx_handler = 0;
servconn->timeout_sec = 0;
servconn->timeout_handle = 0;
@@ -84,7 +84,7 @@ msn_servconn_destroy(MsnServConn *servconn)
g_free(servconn->host);
- purple_circ_buffer_destroy(servconn->tx_buf);
+ g_object_unref(G_OBJECT(servconn->tx_buf));
if (servconn->tx_handler > 0)
purple_input_remove(servconn->tx_handler);
if (servconn->timeout_handle > 0)
@@ -225,12 +225,9 @@ msn_servconn_connect(MsnServConn *servconn, const char *host, int port, gboolean
{
/* HTTP Connection. */
- if (!servconn->httpconn->connected || force)
- if (!msn_httpconn_connect(servconn->httpconn, host, port))
- return FALSE;
+ msn_httpconn_connect(servconn->httpconn, host, port);
servconn->connected = TRUE;
- servconn->httpconn->virgin = TRUE;
servconn_timeout_renew(servconn);
/* Someone wants to know we connected. */
@@ -334,8 +331,10 @@ servconn_write_cb(gpointer data, gint source, PurpleInputCondition cond)
MsnServConn *servconn = data;
gssize ret;
int writelen;
+ const gchar *output = NULL;
- writelen = purple_circ_buffer_get_max_read(servconn->tx_buf);
+ writelen = purple_circular_buffer_get_max_read(servconn->tx_buf);
+ output = purple_circular_buffer_get_output(servconn->tx_buf);
if (writelen == 0) {
purple_input_remove(servconn->tx_handler);
@@ -343,7 +342,7 @@ servconn_write_cb(gpointer data, gint source, PurpleInputCondition cond)
return;
}
- ret = write(servconn->fd, servconn->tx_buf->outptr, writelen);
+ ret = write(servconn->fd, output, writelen);
if (ret < 0 && errno == EAGAIN)
return;
@@ -352,7 +351,7 @@ servconn_write_cb(gpointer data, gint source, PurpleInputCondition cond)
return;
}
- purple_circ_buffer_mark_read(servconn->tx_buf, ret);
+ purple_circular_buffer_mark_read(servconn->tx_buf, ret);
servconn_timeout_renew(servconn);
}
@@ -389,12 +388,12 @@ msn_servconn_write(MsnServConn *servconn, const char *buf, size_t len)
if (ret < 0 && errno == EAGAIN)
ret = 0;
- if (ret >= 0 && ret < len) {
+ if (ret >= 0 && (size_t)ret < len) {
if (servconn->tx_handler == 0)
servconn->tx_handler = purple_input_add(
servconn->fd, PURPLE_INPUT_WRITE,
servconn_write_cb, servconn);
- purple_circ_buffer_append(servconn->tx_buf, buf + ret,
+ purple_circular_buffer_append(servconn->tx_buf, buf + ret,
len - ret);
}
}
@@ -421,8 +420,10 @@ read_cb(gpointer data, gint source, PurpleInputCondition cond)
servconn = data;
- if (servconn->type == MSN_SERVCONN_NS)
- servconn->session->account->gc->last_received = time(NULL);
+ if (servconn->type == MSN_SERVCONN_NS) {
+ PurpleConnection *gc = purple_account_get_connection(servconn->session->account);
+ purple_connection_update_last_received(gc);
+ }
len = read(servconn->fd, buf, sizeof(buf) - 1);
if (len < 0 && errno == EAGAIN)
@@ -462,9 +463,12 @@ MsnServConn *msn_servconn_process_data(MsnServConn *servconn)
if (servconn->payload_len)
{
- if (servconn->payload_len > servconn->rx_len)
+ if (servconn->rx_len < 0 || servconn->payload_len >
+ (gsize)servconn->rx_len)
+ {
/* The payload is still not complete. */
break;
+ }
cur_len = servconn->payload_len;
end += cur_len;
diff --git a/libpurple/protocols/msn/servconn.h b/libpurple/protocols/msn/servconn.h
index b075fd08f2..833a457137 100644
--- a/libpurple/protocols/msn/servconn.h
+++ b/libpurple/protocols/msn/servconn.h
@@ -24,6 +24,8 @@
#ifndef MSN_SERVCONN_H
#define MSN_SERVCONN_H
+#include "circularbuffer.h"
+
typedef struct _MsnServConn MsnServConn;
/**
@@ -85,7 +87,7 @@ struct _MsnServConn
It's only set when we've received a command that
has a payload. */
- PurpleCircBuffer *tx_buf;
+ PurpleCircularBuffer *tx_buf;
guint tx_handler;
guint timeout_sec;
guint timeout_handle;
diff --git a/libpurple/protocols/msn/session.c b/libpurple/protocols/msn/session.c
index 1775c0acf1..e94b354f19 100644
--- a/libpurple/protocols/msn/session.c
+++ b/libpurple/protocols/msn/session.c
@@ -24,6 +24,7 @@
#include "internal.h"
#include "debug.h"
+#include "http.h"
#include "error.h"
#include "msnutils.h"
@@ -40,6 +41,8 @@ msn_session_new(PurpleAccount *account)
session = g_new0(MsnSession, 1);
+ session->http_reqs = purple_http_connection_set_new();
+
session->account = account;
session->notification = msn_notification_new(session);
session->userlist = msn_userlist_new(session);
@@ -54,6 +57,8 @@ msn_session_new(PurpleAccount *account)
session->guid = rand_guid();
+ session->soap = msn_soap_service_new(session);
+
return session;
}
@@ -64,19 +69,12 @@ msn_session_destroy(MsnSession *session)
session->destroying = TRUE;
- while (session->url_datas) {
- purple_util_fetch_url_cancel(session->url_datas->data);
- session->url_datas = g_slist_delete_link(session->url_datas, session->url_datas);
- }
+ purple_http_connection_set_destroy(session->http_reqs);
if (session->connected)
msn_session_disconnect(session);
- if (session->soap_cleanup_handle)
- purple_timeout_remove(session->soap_cleanup_handle);
-
- if (session->soap_table != NULL)
- g_hash_table_destroy(session->soap_table);
+ msn_soap_service_destroy(session->soap);
while (session->slplinks != NULL)
msn_slplink_unref(session->slplinks->data);
@@ -176,21 +174,20 @@ msn_session_find_swboard(MsnSession *session, const char *username)
return NULL;
}
-static PurpleConversation *
-msn_session_get_conv(MsnSession *session,const char *passport)
+static PurpleIMConversation *
+msn_session_get_im(MsnSession *session,const char *passport)
{
PurpleAccount *account;
- PurpleConversation * conv;
+ PurpleIMConversation * im;
g_return_val_if_fail(session != NULL, NULL);
account = session->account;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
- passport, account);
- if(conv == NULL){
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, passport);
+ im = purple_conversations_find_im_with_account(passport, account);
+ if(im == NULL){
+ im = purple_im_conversation_new(account, passport);
}
- return conv;
+ return im;
}
/* put Message to User Conversation
@@ -200,10 +197,11 @@ msn_session_get_conv(MsnSession *session,const char *passport)
void
msn_session_report_user(MsnSession *session,const char *passport,const char *msg,PurpleMessageFlags flags)
{
- PurpleConversation * conv;
+ PurpleIMConversation * im;
- if ((conv = msn_session_get_conv(session,passport)) != NULL){
- purple_conversation_write(conv, NULL, msg, flags, time(NULL));
+ if ((im = msn_session_get_im(session,passport)) != NULL){
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(im), msg, flags);
}
}
@@ -311,7 +309,7 @@ msn_session_sync_users(MsnSession *session)
* being logged in. This no longer happens, so we manually iterate
* over the whole buddy list to identify sync issues.
*/
- for (buddies = purple_find_buddies(session->account, NULL); buddies;
+ for (buddies = purple_blist_find_buddies(session->account, NULL); buddies;
buddies = g_slist_delete_link(buddies, buddies)) {
PurpleBuddy *buddy = buddies->data;
const gchar *buddy_name = purple_buddy_get_name(buddy);
@@ -384,7 +382,7 @@ msn_session_set_error(MsnSession *session, MsnErrorType error,
reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE;
msg = g_strdup(_("You have signed on from another location"));
if (!purple_account_get_remember_password(session->account))
- purple_account_set_password(session->account, NULL);
+ purple_account_set_password(session->account, NULL, NULL, NULL);
break;
case MSN_ERROR_SERV_UNAVAILABLE:
reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
@@ -404,7 +402,7 @@ msn_session_set_error(MsnSession *session, MsnErrorType error,
_("Unknown error") : info);
/* Clear the password if it isn't being saved */
if (!purple_account_get_remember_password(session->account))
- purple_account_set_password(session->account, NULL);
+ purple_account_set_password(session->account, NULL, NULL, NULL);
break;
case MSN_ERROR_BAD_BLIST:
reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
@@ -420,7 +418,7 @@ msn_session_set_error(MsnSession *session, MsnErrorType error,
msn_session_disconnect(session);
- purple_connection_error_reason(gc, reason, msg);
+ purple_connection_error(gc, reason, msg);
g_free(msg);
}
@@ -459,7 +457,7 @@ msn_session_set_login_step(MsnSession *session, MsnLoginStep step)
if (session->logged_in)
return;
- gc = session->account->gc;
+ gc = purple_account_get_connection(session->account);
session->login_step = step;
@@ -472,7 +470,7 @@ msn_session_finish_login(MsnSession *session)
{
PurpleAccount *account;
PurpleConnection *gc;
- PurpleStoredImage *img;
+ PurpleImage *img;
if (!session->logged_in) {
account = session->account;
@@ -482,10 +480,10 @@ msn_session_finish_login(MsnSession *session)
/* TODO: Do we really want to call this if img is NULL? */
msn_user_set_buddy_icon(session->user, img);
if (img != NULL)
- purple_imgstore_unref(img);
+ g_object_unref(img);
session->logged_in = TRUE;
- purple_connection_set_state(gc, PURPLE_CONNECTED);
+ purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
/* Sync users */
msn_session_sync_users(session);
diff --git a/libpurple/protocols/msn/session.h b/libpurple/protocols/msn/session.h
index e94b3b73ac..83cbfd3406 100644
--- a/libpurple/protocols/msn/session.h
+++ b/libpurple/protocols/msn/session.h
@@ -63,9 +63,12 @@ typedef enum
#define MSN_LOGIN_FQY_TIMEOUT 30
+#include "http.h"
+
#include "nexus.h"
#include "notification.h"
#include "oim.h"
+#include "soap.h"
#include "switchboard.h"
#include "user.h"
#include "userlist.h"
@@ -116,11 +119,10 @@ struct _MsnSession
gboolean email_enabled;
} passport_info;
- GHashTable *soap_table;
- guint soap_cleanup_handle;
+ MsnSoapService *soap;
char *guid;
- GSList *url_datas; /**< PurpleUtilFetchUrlData to be cancelled on exit */
+ PurpleHttpConnectionSet *http_reqs;
};
/**
diff --git a/libpurple/protocols/msn/slp.c b/libpurple/protocols/msn/slp.c
index 85c78b260a..43a544e28f 100644
--- a/libpurple/protocols/msn/slp.c
+++ b/libpurple/protocols/msn/slp.c
@@ -24,6 +24,7 @@
#include "internal.h"
#include "debug.h"
+#include "http.h"
#include "slp.h"
#include "slpcall.h"
@@ -35,7 +36,7 @@
#include "sbconn.h"
#include "directconn.h"
#include "p2p.h"
-#include "xfer.h"
+#include "ft.h"
/* seconds to delay between sending buddy icon requests to the server. */
#define BUDDY_ICON_DELAY 20
@@ -170,23 +171,25 @@ end_user_display(MsnSlpCall *slpcall, MsnSession *session)
}
static void
-fetched_user_display(PurpleUtilFetchUrlData *url_data, gpointer user_data,
- const gchar *url_text, gsize len, const gchar *error_message)
+fetched_user_display(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _data)
{
- MsnFetchUserDisplayData *data = user_data;
+ MsnFetchUserDisplayData *data = _data;
MsnSession *session = data->session;
- session->url_datas = g_slist_remove(session->url_datas, url_data);
+ if (purple_http_response_is_successful(response)) {
+ size_t len;
+ const gchar *icon_data;
- if (url_text) {
- purple_buddy_icons_set_for_user(session->account, data->remote_user,
- g_memdup(url_text, len), len,
- data->sha1);
+ icon_data = purple_http_response_get_data(response, &len);
+ purple_buddy_icons_set_for_user(session->account,
+ data->remote_user, g_memdup(icon_data, len), len,
+ data->sha1);
}
end_user_display(NULL, session);
- g_free(user_data);
+ g_free(data);
}
static void
@@ -207,9 +210,9 @@ request_own_user_display(MsnUser *user)
my_obj = msn_user_get_object(user);
if (my_obj != NULL) {
- PurpleStoredImage *img = msn_object_get_image(my_obj);
- data = purple_imgstore_get_data(img);
- len = purple_imgstore_get_size(img);
+ PurpleImage *img = msn_object_get_image(my_obj);
+ data = purple_image_get_data(img);
+ len = purple_image_get_size(img);
info = msn_object_get_sha1(my_obj);
}
@@ -248,14 +251,18 @@ msn_request_user_display(MsnUser *user)
{
const char *url = msn_object_get_url1(obj);
if (url) {
+ PurpleHttpRequest *req;
MsnFetchUserDisplayData *data = g_new0(MsnFetchUserDisplayData, 1);
- PurpleUtilFetchUrlData *url_data;
data->session = session;
data->remote_user = user->passport;
data->sha1 = info;
- url_data = purple_util_fetch_url_len(url, TRUE, NULL, TRUE, 200*1024,
- fetched_user_display, data);
- session->url_datas = g_slist_prepend(session->url_datas, url_data);
+
+ req = purple_http_request_new(url);
+ purple_http_request_set_max_len(req, 200*1024);
+ purple_http_connection_set_add(session->http_reqs,
+ purple_http_request(NULL, req,
+ fetched_user_display, data));
+ purple_http_request_unref(req);
} else {
msn_slplink_request_object(slplink, info, got_user_display,
end_user_display, obj);
@@ -275,13 +282,13 @@ send_file_cb(MsnSlpCall *slpcall)
if (purple_xfer_get_status(xfer) >= PURPLE_XFER_STATUS_STARTED)
return;
- purple_xfer_ref(xfer);
+ g_object_ref(xfer);
purple_xfer_start(xfer, -1, NULL, 0);
if (purple_xfer_get_status(xfer) != PURPLE_XFER_STATUS_STARTED) {
- purple_xfer_unref(xfer);
+ g_object_unref(xfer);
return;
}
- purple_xfer_unref(xfer);
+ g_object_unref(xfer);
slpmsg = msn_slpmsg_file_new(slpcall, purple_xfer_get_size(xfer));
@@ -291,7 +298,7 @@ send_file_cb(MsnSlpCall *slpcall)
static gchar *
gen_context(PurpleXfer *xfer, const char *file_name, const char *file_path)
{
- gsize size = 0;
+ goffset size = 0;
MsnFileContext context;
gchar *u8 = NULL;
gchar *ret;
@@ -322,7 +329,7 @@ gen_context(PurpleXfer *xfer, const char *file_name, const char *file_path)
preview = purple_xfer_get_thumbnail(xfer, &preview_len);
- context.length = MSN_FILE_CONTEXT_SIZE;
+ context.length = MSN_FILE_CONTEXT_SIZE_V2;
context.version = 2; /* V.3 contains additional unnecessary data */
context.file_size = size;
if (preview)
@@ -336,15 +343,17 @@ gen_context(PurpleXfer *xfer, const char *file_name, const char *file_path)
}
memset(&context.file_name[currentChar], 0x00, (MAX_FILE_NAME_LEN - currentChar) * 2);
+#if 0
memset(&context.unknown1, 0, sizeof(context.unknown1));
context.unknown2 = 0xffffffff;
+#endif
/* Mind the cast, as in, don't free it after! */
context.preview = (char *)preview;
context.preview_len = preview_len;
u8 = msn_file_context_to_wire(&context);
- ret = purple_base64_encode((const guchar *)u8, MSN_FILE_CONTEXT_SIZE + preview_len);
+ ret = purple_base64_encode((const guchar *)u8, MSN_FILE_CONTEXT_SIZE_V2 + preview_len);
g_free(uni);
g_free(u8);
@@ -364,7 +373,7 @@ msn_request_ft(PurpleXfer *xfer)
fn = purple_xfer_get_filename(xfer);
fp = purple_xfer_get_local_filename(xfer);
- slplink = xfer->data;
+ slplink = purple_xfer_get_protocol_data(xfer);
g_return_if_fail(slplink != NULL);
g_return_if_fail(fp != NULL);
@@ -376,7 +385,7 @@ msn_request_ft(PurpleXfer *xfer)
slpcall->end_cb = msn_xfer_end_cb;
slpcall->cb = msn_xfer_completed_cb;
slpcall->xfer = xfer;
- purple_xfer_ref(slpcall->xfer);
+ g_object_ref(slpcall->xfer);
slpcall->pending = TRUE;
@@ -384,7 +393,7 @@ msn_request_ft(PurpleXfer *xfer)
purple_xfer_set_read_fnc(xfer, msn_xfer_read);
purple_xfer_set_write_fnc(xfer, msn_xfer_write);
- xfer->data = slpcall;
+ purple_xfer_set_protocol_data(xfer, slpcall);
context = gen_context(xfer, fn, fp);
diff --git a/libpurple/protocols/msn/slp.h b/libpurple/protocols/msn/slp.h
index 0be19f0358..1866c74375 100644
--- a/libpurple/protocols/msn/slp.h
+++ b/libpurple/protocols/msn/slp.h
@@ -25,7 +25,7 @@
#define MSN_SLP_H
#include "internal.h"
-#include "ft.h"
+#include "xfer.h"
#include "session.h"
#include "slpcall.h"
diff --git a/libpurple/protocols/msn/slpcall.c b/libpurple/protocols/msn/slpcall.c
index 2f06124921..435aad243a 100644
--- a/libpurple/protocols/msn/slpcall.c
+++ b/libpurple/protocols/msn/slpcall.c
@@ -25,13 +25,14 @@
#include "internal.h"
#include "debug.h"
#include "smiley.h"
+#include "smiley-custom.h"
#include "msnutils.h"
#include "slpcall.h"
#include "slp.h"
#include "p2p.h"
-#include "xfer.h"
+#include "ft.h"
/**************************************************************************
* Main
@@ -111,10 +112,10 @@ msn_slpcall_destroy(MsnSlpCall *slpcall)
slpcall->end_cb(slpcall, slpcall->slplink->session);
if (slpcall->xfer != NULL) {
- if (purple_xfer_get_type(slpcall->xfer) == PURPLE_XFER_RECEIVE)
+ if (purple_xfer_get_xfer_type(slpcall->xfer) == PURPLE_XFER_TYPE_RECEIVE)
g_byte_array_free(slpcall->u.incoming_data, TRUE);
- slpcall->xfer->data = NULL;
- purple_xfer_unref(slpcall->xfer);
+ purple_xfer_set_protocol_data(slpcall->xfer, NULL);
+ g_object_unref(slpcall->xfer);
}
@@ -231,33 +232,35 @@ get_token(const char *str, const char *start, const char *end)
/* XXX: this could be improved if we tracked custom smileys
* per-protocol, per-account, per-session or (ideally) per-conversation
+ *
+ * Note: it should be tracked on the msn prpl side.
*/
-static PurpleStoredImage *
+static PurpleImage *
find_valid_emoticon(PurpleAccount *account, const char *path)
{
- GList *smileys;
+ GList *smileys, *it;
if (!purple_account_get_bool(account, "custom_smileys", TRUE))
return NULL;
+ smileys = purple_smiley_list_get_unique(
+ purple_smiley_custom_get_list());
- smileys = purple_smileys_get_all();
-
- for (; smileys; smileys = g_list_delete_link(smileys, smileys)) {
- PurpleSmiley *smiley;
- PurpleStoredImage *img;
+ for (it = smileys; it; it = g_list_next(it)) {
+ PurpleSmiley *smiley = it->data;
- smiley = smileys->data;
- img = purple_smiley_get_stored_image(smiley);
+ if (g_strcmp0(path, purple_image_get_path(purple_smiley_get_image(smiley))) == 0) {
+ PurpleImage *img;
- if (purple_strequal(path, purple_imgstore_get_filename(img))) {
g_list_free(smileys);
+ img = purple_smiley_get_image(smiley);
+ g_object_ref(img);
return img;
}
-
- purple_imgstore_unref(img);
}
+ g_list_free(smileys);
+
+ purple_debug_error("msn", "Received illegal request for file %s", path);
- purple_debug_error("msn", "Received illegal request for file %s\n", path);
return NULL;
}
@@ -281,12 +284,18 @@ parse_dc_nonce(const char *content, MsnDirectConnNonceType *ntype)
*ntype = DC_NONCE_PLAIN;
g_free(nonce);
nonce = g_malloc(16);
- *(guint32 *)(nonce + 0) = GUINT32_TO_LE(n1);
- *(guint16 *)(nonce + 4) = GUINT16_TO_LE(n2);
- *(guint16 *)(nonce + 6) = GUINT16_TO_LE(n3);
- *(guint16 *)(nonce + 8) = GUINT16_TO_BE(n4);
- *(guint16 *)(nonce + 10) = GUINT16_TO_BE(n5);
- *(guint32 *)(nonce + 12) = GUINT32_TO_BE(n6);
+ n1 = GUINT32_TO_LE(n1);
+ n2 = GUINT16_TO_LE(n2);
+ n3 = GUINT16_TO_LE(n3);
+ n4 = GUINT16_TO_BE(n4);
+ n5 = GUINT16_TO_BE(n5);
+ n6 = GUINT32_TO_BE(n6);
+ memcpy(nonce + 0, &n1, sizeof(n1));
+ memcpy(nonce + 4, &n2, sizeof(n2));
+ memcpy(nonce + 6, &n3, sizeof(n3));
+ memcpy(nonce + 8, &n4, sizeof(n4));
+ memcpy(nonce + 10, &n5, sizeof(n5));
+ memcpy(nonce + 12, &n6, sizeof(n6));
} else {
/* Invalid nonce, so ignore request */
g_free(nonce);
@@ -460,7 +469,7 @@ got_sessionreq(MsnSlpCall *slpcall, const char *branch,
MsnSlpMessage *slpmsg;
MsnObject *obj;
char *msnobj_data;
- PurpleStoredImage *img = NULL;
+ PurpleImage *img = NULL;
int type;
/* Send Ok */
@@ -483,9 +492,9 @@ got_sessionreq(MsnSlpCall *slpcall, const char *branch,
} else if (type == MSN_OBJECT_USERTILE) {
img = msn_object_get_image(obj);
if (img)
- purple_imgstore_ref(img);
+ g_object_ref(img);
}
- msn_object_destroy(obj);
+ msn_object_destroy(obj, FALSE);
if (img != NULL) {
/* DATA PREP */
@@ -495,7 +504,7 @@ got_sessionreq(MsnSlpCall *slpcall, const char *branch,
/* DATA */
slpmsg = msn_slpmsg_obj_new(slpcall, img);
msn_slplink_queue_slpmsg(slplink, slpmsg);
- purple_imgstore_unref(img);
+ g_object_unref(img);
accepted = TRUE;
@@ -512,7 +521,6 @@ got_sessionreq(MsnSlpCall *slpcall, const char *branch,
MsnFileContext *file_context;
char *buf;
gsize bin_len;
- guint32 file_size;
char *file_name;
account = slpcall->slplink->session->account;
@@ -522,15 +530,13 @@ got_sessionreq(MsnSlpCall *slpcall, const char *branch,
slpcall->pending = TRUE;
- xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE,
+ xfer = purple_xfer_new(account, PURPLE_XFER_TYPE_RECEIVE,
slpcall->slplink->remote_user);
buf = (char *)purple_base64_decode(context, &bin_len);
file_context = msn_file_context_from_wire(buf, bin_len);
if (file_context != NULL) {
- file_size = file_context->file_size;
-
file_name = g_convert((const gchar *)&file_context->file_name,
MAX_FILE_NAME_LEN * 2,
"UTF-8", "UTF-16LE",
@@ -538,7 +544,7 @@ got_sessionreq(MsnSlpCall *slpcall, const char *branch,
purple_xfer_set_filename(xfer, file_name ? file_name : "");
g_free(file_name);
- purple_xfer_set_size(xfer, file_size);
+ purple_xfer_set_size(xfer, file_context->file_size);
purple_xfer_set_init_fnc(xfer, msn_xfer_init);
purple_xfer_set_request_denied_fnc(xfer, msn_xfer_cancel);
purple_xfer_set_cancel_recv_fnc(xfer, msn_xfer_cancel);
@@ -548,9 +554,9 @@ got_sessionreq(MsnSlpCall *slpcall, const char *branch,
slpcall->u.incoming_data = g_byte_array_new();
slpcall->xfer = xfer;
- purple_xfer_ref(slpcall->xfer);
+ g_object_ref(slpcall->xfer);
- xfer->data = slpcall;
+ purple_xfer_set_protocol_data(xfer, slpcall);
if (file_context->preview) {
purple_xfer_set_thumbnail(xfer, file_context->preview,
@@ -569,21 +575,18 @@ got_sessionreq(MsnSlpCall *slpcall, const char *branch,
} else if (!strcmp(euf_guid, MSN_CAM_REQUEST_GUID)) {
purple_debug_info("msn", "Cam request.\n");
if (slpcall->slplink && slpcall->slplink->session) {
- PurpleConversation *conv;
+ PurpleIMConversation *im;
gchar *from = slpcall->slplink->remote_user;
- conv = purple_find_conversation_with_account(
- PURPLE_CONV_TYPE_IM, from,
- slpcall->slplink->session->account);
- if (conv) {
+ im = purple_conversations_find_im_with_account(
+ from, slpcall->slplink->session->account);
+ if (im) {
char *buf;
buf = g_strdup_printf(
_("%s requests to view your "
"webcam, but this request is "
"not yet supported."), from);
- purple_conversation_write(conv, NULL, buf,
- PURPLE_MESSAGE_SYSTEM |
- PURPLE_MESSAGE_NOTIFY,
- time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
+ buf, PURPLE_MESSAGE_NOTIFY);
g_free(buf);
}
}
@@ -591,20 +594,17 @@ got_sessionreq(MsnSlpCall *slpcall, const char *branch,
} else if (!strcmp(euf_guid, MSN_CAM_GUID)) {
purple_debug_info("msn", "Cam invite.\n");
if (slpcall->slplink && slpcall->slplink->session) {
- PurpleConversation *conv;
+ PurpleIMConversation *im;
gchar *from = slpcall->slplink->remote_user;
- conv = purple_find_conversation_with_account(
- PURPLE_CONV_TYPE_IM, from,
- slpcall->slplink->session->account);
- if (conv) {
+ im = purple_conversations_find_im_with_account(
+ from, slpcall->slplink->session->account);
+ if (im) {
char *buf;
buf = g_strdup_printf(
_("%s invited you to view his/her webcam, but "
"this is not yet supported."), from);
- purple_conversation_write(conv, NULL, buf,
- PURPLE_MESSAGE_SYSTEM |
- PURPLE_MESSAGE_NOTIFY,
- time(NULL));
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(im), buf, PURPLE_MESSAGE_NOTIFY);
g_free(buf);
}
}
@@ -731,7 +731,9 @@ got_invite(MsnSlpCall *slpcall,
dc->listen_data = purple_network_listen_range(
0, 0,
+ AF_UNSPEC,
SOCK_STREAM,
+ TRUE,
msn_dc_listen_socket_created_cb,
dc
);
@@ -832,7 +834,9 @@ got_ok(MsnSlpCall *slpcall,
dc->listen_data = purple_network_listen_range(
0, 0,
+ AF_UNSPEC,
SOCK_STREAM,
+ TRUE,
msn_dc_listen_socket_created_cb,
dc
);
@@ -1141,8 +1145,6 @@ msn_slp_process_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
if (slpcall->cb)
slpcall->cb(slpcall, body, body_len);
-
- slpcall->wasted = TRUE;
}
}
else if (msn_p2p_info_is_ack(slpmsg->p2p_info))
diff --git a/libpurple/protocols/msn/slplink.c b/libpurple/protocols/msn/slplink.c
index eea9ee27d8..308db56c86 100644
--- a/libpurple/protocols/msn/slplink.c
+++ b/libpurple/protocols/msn/slplink.c
@@ -289,7 +289,7 @@ msn_slplink_send_msgpart(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
{
MsnSlpMessagePart *part;
MsnP2PInfo *info;
- long long real_size;
+ gsize real_size;
size_t len = 0;
guint64 offset;
@@ -304,7 +304,7 @@ msn_slplink_send_msgpart(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
offset = msn_p2p_info_get_offset(info);
if (offset < real_size)
{
- if (slpmsg->slpcall && slpmsg->slpcall->xfer && purple_xfer_get_type(slpmsg->slpcall->xfer) == PURPLE_XFER_SEND &&
+ if (slpmsg->slpcall && slpmsg->slpcall->xfer && purple_xfer_get_xfer_type(slpmsg->slpcall->xfer) == PURPLE_XFER_TYPE_SEND &&
purple_xfer_get_status(slpmsg->slpcall->xfer) == PURPLE_XFER_STATUS_STARTED)
{
len = MIN(MSN_SBCONN_MAX_SIZE, slpmsg->slpcall->u.outgoing.len);
@@ -422,7 +422,7 @@ msn_slplink_send_ack(MsnSlpLink *slplink, MsnP2PInfo *info)
}
static MsnSlpMessage *
-msn_slplink_message_find(MsnSlpLink *slplink, long session_id, long id)
+msn_slplink_message_find(MsnSlpLink *slplink, guint32 session_id, long id)
{
GList *e;
@@ -462,15 +462,15 @@ init_first_msg(MsnSlpLink *slplink, MsnP2PInfo *info)
slpmsg->ft = TRUE;
slpmsg->slpcall->xfer_msg = slpmsg;
- purple_xfer_ref(xfer);
+ g_object_ref(xfer);
purple_xfer_start(xfer, -1, NULL, 0);
- if (xfer->data == NULL) {
- purple_xfer_unref(xfer);
+ if (purple_xfer_get_protocol_data(xfer) == NULL) {
+ g_object_unref(xfer);
msn_slpmsg_destroy(slpmsg);
g_return_val_if_reached(NULL);
} else {
- purple_xfer_unref(xfer);
+ g_object_unref(xfer);
}
}
}
@@ -546,8 +546,8 @@ slpmsg_add_part(MsnSlpMessage *slpmsg, MsnSlpMessagePart *part)
|| (offset + part->size) > slpmsg->size
|| msn_p2p_info_get_offset(slpmsg->p2p_info) != offset) {
purple_debug_error("msn",
- "Oversized slpmsg - msgsize=%lld offset=%" G_GUINT64_FORMAT " len=%" G_GSIZE_FORMAT "\n",
- slpmsg->size, offset, part->size);
+ "Oversized slpmsg - msgsize=%" G_GSIZE_FORMAT " offset=%" G_GUINT64_FORMAT " len=%" G_GSIZE_FORMAT "\n",
+ (gsize)slpmsg->size, offset, (gsize)part->size);
g_return_if_reached();
} else {
memcpy(slpmsg->buffer + offset, part->buffer, part->size);
diff --git a/libpurple/protocols/msn/slpmsg.c b/libpurple/protocols/msn/slpmsg.c
index 4a75e3abc9..d093f30369 100644
--- a/libpurple/protocols/msn/slpmsg.c
+++ b/libpurple/protocols/msn/slpmsg.c
@@ -68,12 +68,7 @@ msn_slpmsg_destroy(MsnSlpMessage *slpmsg)
slplink = slpmsg->slplink;
- purple_imgstore_unref(slpmsg->img);
-
- /* We don't want to free the data of the PurpleStoredImage,
- * but to avoid code duplication, it's sharing buffer. */
- if (slpmsg->img == NULL)
- g_free(slpmsg->buffer);
+ g_free(slpmsg->buffer);
for (cur = slpmsg->parts; cur != NULL; cur = g_list_delete_link(cur, cur))
{
@@ -113,7 +108,6 @@ msn_slpmsg_set_body(MsnSlpMessage *slpmsg, const char *body,
{
/* We can only have one data source at a time. */
g_return_if_fail(slpmsg->buffer == NULL);
- g_return_if_fail(slpmsg->img == NULL);
g_return_if_fail(slpmsg->ft == FALSE);
if (body != NULL)
@@ -125,16 +119,14 @@ msn_slpmsg_set_body(MsnSlpMessage *slpmsg, const char *body,
}
void
-msn_slpmsg_set_image(MsnSlpMessage *slpmsg, PurpleStoredImage *img)
+msn_slpmsg_set_image(MsnSlpMessage *slpmsg, PurpleImage *img)
{
/* We can only have one data source at a time. */
g_return_if_fail(slpmsg->buffer == NULL);
- g_return_if_fail(slpmsg->img == NULL);
g_return_if_fail(slpmsg->ft == FALSE);
- slpmsg->img = purple_imgstore_ref(img);
- slpmsg->buffer = (guchar *)purple_imgstore_get_data(img);
- slpmsg->size = purple_imgstore_get_size(img);
+ slpmsg->size = purple_image_get_size(img);
+ slpmsg->buffer = g_memdup(purple_image_get_data(img), slpmsg->size);
}
@@ -212,7 +204,7 @@ MsnSlpMessage *msn_slpmsg_ack_new(MsnSlpLink *slplink, MsnP2PInfo *ack_info)
return slpmsg;
}
-MsnSlpMessage *msn_slpmsg_obj_new(MsnSlpCall *slpcall, PurpleStoredImage *img)
+MsnSlpMessage *msn_slpmsg_obj_new(MsnSlpCall *slpcall, PurpleImage *img)
{
MsnSlpMessage *slpmsg;
@@ -239,7 +231,7 @@ MsnSlpMessage *msn_slpmsg_dataprep_new(MsnSlpCall *slpcall)
}
-MsnSlpMessage *msn_slpmsg_file_new(MsnSlpCall *slpcall, size_t size)
+MsnSlpMessage *msn_slpmsg_file_new(MsnSlpCall *slpcall, goffset size)
{
MsnSlpMessage *slpmsg;
diff --git a/libpurple/protocols/msn/slpmsg.h b/libpurple/protocols/msn/slpmsg.h
index 11e9901bc1..30d8f56c19 100644
--- a/libpurple/protocols/msn/slpmsg.h
+++ b/libpurple/protocols/msn/slpmsg.h
@@ -26,8 +26,6 @@
typedef struct _MsnSlpMessage MsnSlpMessage;
-#include "imgstore.h"
-
#include "slpcall.h"
#include "slplink.h"
#include "session.h"
@@ -50,14 +48,13 @@ struct _MsnSlpMessage
long id;
gboolean ft;
- PurpleStoredImage *img;
guchar *buffer;
/**
* This is the size of buffer, unless this is an outgoing file transfer,
* in which case this is the size of the file.
*/
- long long size;
+ gsize size;
GList *parts; /**< A list with the SlpMsgParts */
@@ -90,7 +87,7 @@ void msn_slpmsg_set_slplink(MsnSlpMessage *slpmsg, MsnSlpLink *slplink);
void msn_slpmsg_set_body(MsnSlpMessage *slpmsg, const char *body,
long long size);
-void msn_slpmsg_set_image(MsnSlpMessage *slpmsg, PurpleStoredImage *img);
+void msn_slpmsg_set_image(MsnSlpMessage *slpmsg, PurpleImage *img);
MsnSlpMessage * msn_slpmsg_sip_new(MsnSlpCall *slpcall, int cseq,
const char *header,
const char *branch,
@@ -114,7 +111,7 @@ MsnSlpMessage *msn_slpmsg_ack_new(MsnSlpLink *slplink, MsnP2PInfo *info);
*
* @return A new SlpMessage with MsnObject info.
*/
-MsnSlpMessage *msn_slpmsg_obj_new(MsnSlpCall *slpcall, PurpleStoredImage *img);
+MsnSlpMessage *msn_slpmsg_obj_new(MsnSlpCall *slpcall, PurpleImage *img);
/**
* Create a new SLP message for data preparation.
@@ -133,7 +130,7 @@ MsnSlpMessage *msn_slpmsg_dataprep_new(MsnSlpCall *slpcall);
*
* @return A new SlpMessage with the file transfer info.
*/
-MsnSlpMessage *msn_slpmsg_file_new(MsnSlpCall *slpcall, size_t size);
+MsnSlpMessage *msn_slpmsg_file_new(MsnSlpCall *slpcall, goffset size);
/**
* Serialize the MsnSlpMessage in a way it can be used to be transmited
diff --git a/libpurple/protocols/msn/slpmsg_part.c b/libpurple/protocols/msn/slpmsg_part.c
index dfff1f5c24..add959a73a 100644
--- a/libpurple/protocols/msn/slpmsg_part.c
+++ b/libpurple/protocols/msn/slpmsg_part.c
@@ -172,7 +172,7 @@ msn_slpmsgpart_ack(MsnSlpMessagePart *part, void *data)
{
MsnSlpMessage *slpmsg;
guint64 offset;
- long long real_size;
+ gsize real_size;
slpmsg = data;
diff --git a/libpurple/protocols/msn/soap.c b/libpurple/protocols/msn/soap.c
index 4cb571e51f..7cd8d6b097 100644
--- a/libpurple/protocols/msn/soap.c
+++ b/libpurple/protocols/msn/soap.c
@@ -23,666 +23,275 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
-#include "internal.h"
-
#include "soap.h"
-#include "session.h"
-
#include "debug.h"
-#include "xmlnode.h"
+#include "http.h"
-#include <glib.h>
-#if !defined(_WIN32) || !defined(_WINERROR_)
-#include <error.h>
-#endif
+typedef struct _MsnSoapRequest MsnSoapRequest;
-#define SOAP_TIMEOUT (5 * 60)
+struct _MsnSoapMessage {
+ gchar *action;
+ PurpleXmlNode *xml;
+};
-typedef struct _MsnSoapRequest {
- char *path;
+struct _MsnSoapRequest {
MsnSoapMessage *message;
- gboolean secure;
+ MsnSoapService *soaps;
MsnSoapCallback cb;
gpointer cb_data;
-} MsnSoapRequest;
+ gboolean secure;
+};
-typedef struct _MsnSoapConnection {
+struct _MsnSoapService {
MsnSession *session;
- char *host;
-
- time_t last_used;
- PurpleSslConnection *ssl;
- gboolean connected;
-
- guint event_handle;
- guint run_timer;
- GString *buf;
- gsize handled_len;
- gsize body_len;
- int response_code;
- gboolean headers_done;
- gboolean close_when_done;
-
- MsnSoapMessage *message;
-
- GQueue *queue;
- MsnSoapRequest *current_request;
-} MsnSoapConnection;
-
-static gboolean msn_soap_connection_run(gpointer data);
-
-static MsnSoapConnection *
-msn_soap_connection_new(MsnSession *session, const char *host)
-{
- MsnSoapConnection *conn = g_new0(MsnSoapConnection, 1);
- conn->session = session;
- conn->host = g_strdup(host);
- conn->queue = g_queue_new();
- return conn;
-}
-
-static void
-msn_soap_message_destroy(MsnSoapMessage *message)
-{
- g_slist_foreach(message->headers, (GFunc)g_free, NULL);
- g_slist_free(message->headers);
- g_free(message->action);
- if (message->xml)
- xmlnode_free(message->xml);
- g_free(message);
-}
+ PurpleHttpKeepalivePool *keepalive_pool;
+};
static void
-msn_soap_request_destroy(MsnSoapRequest *req, gboolean keep_message)
-{
- g_free(req->path);
- if (!keep_message)
- msn_soap_message_destroy(req->message);
- g_free(req);
-}
+msn_soap_service_send_message_simple(MsnSoapService *soaps,
+ MsnSoapMessage *message, const gchar *url, gboolean secure,
+ MsnSoapCallback cb, gpointer cb_data);
-static void
-msn_soap_connection_sanitize(MsnSoapConnection *conn, gboolean disconnect)
+MsnSoapMessage *
+msn_soap_message_new(const gchar *action, PurpleXmlNode *xml)
{
- if (conn->event_handle) {
- purple_input_remove(conn->event_handle);
- conn->event_handle = 0;
- }
+ MsnSoapMessage *msg;
- if (conn->run_timer) {
- purple_timeout_remove(conn->run_timer);
- conn->run_timer = 0;
- }
-
- if (conn->message) {
- msn_soap_message_destroy(conn->message);
- conn->message = NULL;
- }
+ g_return_val_if_fail(xml != NULL, NULL);
- if (conn->buf) {
- g_string_free(conn->buf, TRUE);
- conn->buf = NULL;
- }
+ msg = g_new0(MsnSoapMessage, 1);
+ msg->action = g_strdup(action);
+ msg->xml = xml;
- if (conn->ssl && (disconnect || conn->close_when_done)) {
- purple_ssl_close(conn->ssl);
- conn->ssl = NULL;
- }
-
- if (conn->current_request) {
- msn_soap_request_destroy(conn->current_request, FALSE);
- conn->current_request = NULL;
- }
+ return msg;
}
static void
-msn_soap_connection_destroy_foreach_cb(gpointer item, gpointer data)
+msn_soap_message_free(MsnSoapMessage *msg)
{
- MsnSoapRequest *req = item;
-
- req->cb(req->message, NULL, req->cb_data);
+ if (msg == NULL)
+ return;
- msn_soap_request_destroy(req, FALSE);
+ g_free(msg->action);
+ if (msg->xml != NULL)
+ purple_xmlnode_free(msg->xml);
+ g_free(msg);
}
-static void
-msn_soap_connection_destroy(MsnSoapConnection *conn)
+PurpleXmlNode *
+msn_soap_message_get_xml(MsnSoapMessage *message)
{
- if (conn->current_request) {
- MsnSoapRequest *req = conn->current_request;
- conn->current_request = NULL;
- msn_soap_connection_destroy_foreach_cb(req, conn);
- }
-
- msn_soap_connection_sanitize(conn, TRUE);
- g_queue_foreach(conn->queue, msn_soap_connection_destroy_foreach_cb, conn);
- g_queue_free(conn->queue);
+ g_return_val_if_fail(message != NULL, NULL);
- g_free(conn->host);
- g_free(conn);
+ return message->xml;
}
-static gboolean
-msn_soap_cleanup_each(gpointer key, gpointer value, gpointer data)
+static void
+msn_soap_request_free(MsnSoapRequest *sreq)
{
- MsnSoapConnection *conn = value;
- time_t *t = data;
-
- if ((*t - conn->last_used) > SOAP_TIMEOUT * 2) {
- purple_debug_info("soap", "cleaning up soap conn %p\n", conn);
- return TRUE;
- }
+ g_return_if_fail(sreq != NULL);
- return FALSE;
+ msn_soap_message_free(sreq->message);
+ g_free(sreq);
}
-static gboolean
-msn_soap_cleanup_for_session(gpointer data)
+MsnSoapService *
+msn_soap_service_new(MsnSession *session)
{
- MsnSession *sess = data;
- time_t t = time(NULL);
+ MsnSoapService *soaps;
- purple_debug_info("soap", "session cleanup timeout\n");
+ g_return_val_if_fail(session != NULL, NULL);
- if (sess->soap_table) {
- g_hash_table_foreach_remove(sess->soap_table, msn_soap_cleanup_each,
- &t);
-
- if (g_hash_table_size(sess->soap_table) != 0)
- return TRUE;
- }
+ soaps = g_new0(MsnSoapService, 1);
+ soaps->session = session;
+ soaps->keepalive_pool = purple_http_keepalive_pool_new();
+ purple_http_keepalive_pool_set_limit_per_host(soaps->keepalive_pool, 1);
- sess->soap_cleanup_handle = 0;
- return FALSE;
+ return soaps;
}
-static MsnSoapConnection *
-msn_soap_get_connection(MsnSession *session, const char *host)
+void
+msn_soap_service_destroy(MsnSoapService *soaps)
{
- MsnSoapConnection *conn = NULL;
-
- if (session->soap_table) {
- conn = g_hash_table_lookup(session->soap_table, host);
- } else {
- session->soap_table = g_hash_table_new_full(g_str_hash, g_str_equal,
- NULL, (GDestroyNotify)msn_soap_connection_destroy);
- }
-
- if (session->soap_cleanup_handle == 0)
- session->soap_cleanup_handle = purple_timeout_add_seconds(SOAP_TIMEOUT,
- msn_soap_cleanup_for_session, session);
-
- if (conn == NULL) {
- conn = msn_soap_connection_new(session, host);
- g_hash_table_insert(session->soap_table, conn->host, conn);
- }
-
- conn->last_used = time(NULL);
-
- return conn;
-}
+ if (soaps == NULL)
+ return;
-static void
-msn_soap_connection_handle_next(MsnSoapConnection *conn)
-{
- msn_soap_connection_sanitize(conn, FALSE);
+ purple_http_keepalive_pool_unref(soaps->keepalive_pool);
- conn->run_timer = purple_timeout_add(0, msn_soap_connection_run, conn);
+ g_free(soaps);
}
static void
-msn_soap_message_send_internal(MsnSession *session, MsnSoapMessage *message,
- const char *host, const char *path, gboolean secure,
- MsnSoapCallback cb, gpointer cb_data, gboolean first)
+msn_soap_service_recv(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _sreq)
{
- MsnSoapConnection *conn = msn_soap_get_connection(session, host);
- MsnSoapRequest *req = g_new0(MsnSoapRequest, 1);
-
- req->path = g_strdup(path);
- req->message = message;
- req->secure = secure;
- req->cb = cb;
- req->cb_data = cb_data;
-
- if (first) {
- g_queue_push_head(conn->queue, req);
- } else {
- g_queue_push_tail(conn->queue, req);
- }
+ MsnSoapRequest *sreq = _sreq;
+ PurpleXmlNode *xml_root, *xml_body, *xml_fault;
+ const gchar *xml_raw;
+ size_t xml_len;
- if (conn->run_timer == 0)
- conn->run_timer = purple_timeout_add(0, msn_soap_connection_run,
- conn);
-}
+ if (purple_http_response_get_code(response) == 401) {
+ const gchar *errmsg;
-void
-msn_soap_message_send(MsnSession *session, MsnSoapMessage *message,
- const char *host, const char *path, gboolean secure,
- MsnSoapCallback cb, gpointer cb_data)
-{
- g_return_if_fail(message != NULL);
- g_return_if_fail(cb != NULL);
-
- msn_soap_message_send_internal(session, message, host, path, secure,
- cb, cb_data, FALSE);
-}
-
-static gboolean
-msn_soap_handle_redirect(MsnSoapConnection *conn, const char *url)
-{
- char *host;
- char *path;
+ purple_debug_error("msn-soap", "SOAP authentication failed\n");
- if (purple_url_parse(url, &host, NULL, &path, NULL, NULL)) {
- MsnSoapRequest *req = conn->current_request;
- conn->current_request = NULL;
+ errmsg = purple_http_response_get_header(response,
+ "WWW-Authenticate");
- msn_soap_message_send_internal(conn->session, req->message, host, path,
- req->secure, req->cb, req->cb_data, TRUE);
+ msn_session_set_error(sreq->soaps->session, MSN_ERROR_AUTH,
+ errmsg ? purple_url_decode(errmsg) : NULL);
- msn_soap_request_destroy(req, TRUE);
-
- g_free(host);
- g_free(path);
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gboolean
-msn_soap_handle_body(MsnSoapConnection *conn, MsnSoapMessage *response)
-{
- xmlnode *body = xmlnode_get_child(response->xml, "Body");
- xmlnode *fault = xmlnode_get_child(response->xml, "Fault");
-
- if (fault) {
- xmlnode *faultcode = xmlnode_get_child(fault, "faultcode");
-
- if (faultcode != NULL) {
- char *faultdata = xmlnode_get_data(faultcode);
-
- if (faultdata && g_str_equal(faultdata, "psf:Redirect")) {
- xmlnode *url = xmlnode_get_child(fault, "redirectUrl");
-
- if (url) {
- char *urldata = xmlnode_get_data(url);
- if (urldata)
- msn_soap_handle_redirect(conn, urldata);
- g_free(urldata);
- }
-
- g_free(faultdata);
- msn_soap_message_destroy(response);
- return TRUE;
- } else if (faultdata && g_str_equal(faultdata, "wsse:FailedAuthentication")) {
- xmlnode *reason = xmlnode_get_child(fault, "faultstring");
- char *reasondata = NULL;
-
- if (reason)
- reasondata = xmlnode_get_data(reason);
-
- msn_soap_connection_sanitize(conn, TRUE);
- msn_session_set_error(conn->session, MSN_ERROR_AUTH,
- reasondata);
-
- g_free(reasondata);
- g_free(faultdata);
- msn_soap_message_destroy(response);
- return FALSE;
- }
-
- g_free(faultdata);
- }
+ msn_soap_request_free(sreq);
+ return;
}
-
- if (fault || body) {
- if (conn->current_request) {
- MsnSoapRequest *request = conn->current_request;
- conn->current_request = NULL;
- request->cb(request->message, response,
- request->cb_data);
- msn_soap_request_destroy(request, FALSE);
- }
- msn_soap_message_destroy(response);
+ if (!purple_http_response_is_successful(response)) {
+ purple_debug_error("msn-soap", "SOAP request failed\n");
+ msn_session_set_error(sreq->soaps->session,
+ MSN_ERROR_SERV_UNAVAILABLE, NULL);
+ msn_soap_request_free(sreq);
+ return;
}
- return TRUE;
-}
-
-static void
-msn_soap_message_add_header(MsnSoapMessage *message,
- const char *name, const char *value)
-{
- char *header = g_strdup_printf("%s: %s\r\n", name, value);
-
- message->headers = g_slist_prepend(message->headers, header);
-}
-
-static void
-msn_soap_process(MsnSoapConnection *conn)
-{
- gboolean handled = FALSE;
- char *cursor;
- char *linebreak;
-
- cursor = conn->buf->str + conn->handled_len;
-
- if (!conn->headers_done) {
- while ((linebreak = strstr(cursor, "\r\n")) != NULL) {
- conn->handled_len = linebreak - conn->buf->str + 2;
-
- if (conn->response_code == 0) {
- if (sscanf(cursor, "HTTP/1.1 %d", &conn->response_code) != 1) {
- /* something horribly wrong */
- purple_ssl_close(conn->ssl);
- conn->ssl = NULL;
- handled = TRUE;
- break;
- } else if (conn->response_code == 503 && conn->session->login_step < MSN_LOGIN_STEP_END) {
- msn_soap_connection_sanitize(conn, TRUE);
- msn_session_set_error(conn->session, MSN_ERROR_SERV_UNAVAILABLE, NULL);
- return;
- }
- } else if (cursor == linebreak) {
- /* blank line */
- conn->headers_done = TRUE;
- cursor = conn->buf->str + conn->handled_len;
- break;
- } else {
- char *line = g_strndup(cursor, linebreak - cursor);
- char *sep = strstr(line, ": ");
- char *key = line;
- char *value;
-
- if (sep == NULL) {
- purple_debug_info("soap", "ignoring malformed line: %s\n", line);
- g_free(line);
- goto loop_end;
- }
-
- value = sep + 2;
- *sep = '\0';
- msn_soap_message_add_header(conn->message, key, value);
-
- if ((conn->response_code == 301 || conn->response_code == 300)
- && strcmp(key, "Location") == 0) {
-
- msn_soap_handle_redirect(conn, value);
-
- handled = TRUE;
- g_free(line);
- break;
- } else if (conn->response_code == 401 &&
- strcmp(key, "WWW-Authenticate") == 0) {
- char *error = strstr(value, "cbtxt=");
-
- if (error) {
- error += strlen("cbtxt=");
- }
-
- msn_soap_connection_sanitize(conn, TRUE);
- msn_session_set_error(conn->session, MSN_ERROR_AUTH,
- error ? purple_url_decode(error) : NULL);
-
- g_free(line);
- return;
- } else if (strcmp(key, "Content-Length") == 0) {
- if (sscanf(value, "%" G_GSIZE_FORMAT, &(conn->body_len)) != 1)
- purple_debug_error("soap", "Unable to parse Content-Length\n");
- } else if (strcmp(key, "Connection") == 0) {
- if (strcmp(value, "close") == 0) {
- conn->close_when_done = TRUE;
- }
- }
- g_free(line);
- }
+ xml_raw = purple_http_response_get_data(response, &xml_len);
+ xml_root = purple_xmlnode_from_str(xml_raw, xml_len);
- loop_end:
- cursor = conn->buf->str + conn->handled_len;
+ if (purple_debug_is_verbose()) {
+ if (sreq->secure && !purple_debug_is_unsafe()) {
+ purple_debug_misc("msn-soap",
+ "Received secure SOAP request.\n");
+ } else {
+ purple_debug_misc("msn-soap",
+ "Received SOAP request: %s\n", xml_raw);
}
}
- if (!handled && conn->headers_done) {
- if (conn->buf->len - conn->handled_len >=
- conn->body_len) {
- xmlnode *node = xmlnode_from_str(cursor, conn->body_len);
-
- if (node == NULL) {
- purple_debug_info("soap", "Malformed SOAP response: %s\n",
- cursor);
- } else {
- MsnSoapMessage *message = conn->message;
- conn->message = NULL;
- message->xml = node;
-
- if (!msn_soap_handle_body(conn, message)) {
- return;
- }
- }
-
- msn_soap_connection_handle_next(conn);
- }
-
+ if (xml_root == NULL) {
+ purple_debug_error("msn-soap", "SOAP response malformed\n");
+ msn_session_set_error(sreq->soaps->session,
+ MSN_ERROR_HTTP_MALFORMED, NULL);
+ msn_soap_request_free(sreq);
return;
}
- if (handled) {
- msn_soap_connection_handle_next(conn);
- }
-}
+ xml_body = purple_xmlnode_get_child(xml_root, "Body");
+ xml_fault = purple_xmlnode_get_child(xml_root, "Fault");
-static void
-msn_soap_read_cb(gpointer data, gint fd, PurpleInputCondition cond)
-{
- MsnSoapConnection *conn = data;
- int count = 0, cnt, perrno;
- /* This buffer needs to be larger than any packets received from
- login.live.com or Adium will fail to receive the packet
- (something weird with the login.live.com server). With NSS it works
- fine, so I believe it's some bug with OS X */
- char buf[16 * 1024];
- gsize cursor;
-
- if (conn->message == NULL) {
- conn->message = msn_soap_message_new(NULL, NULL);
- }
+ if (xml_fault != NULL) {
+ PurpleXmlNode *xml_faultcode;
+ gchar *faultdata = NULL;
- if (conn->buf == NULL) {
- conn->buf = g_string_new_len(buf, 0);
- }
-
- cursor = conn->buf->len;
- while ((cnt = purple_ssl_read(conn->ssl, buf, sizeof(buf))) > 0) {
- purple_debug_info("soap", "read %d bytes\n", cnt);
- count += cnt;
- g_string_append_len(conn->buf, buf, cnt);
- }
+ xml_faultcode = purple_xmlnode_get_child(xml_fault, "faultcode");
+ if (xml_faultcode != NULL)
+ faultdata = purple_xmlnode_get_data(xml_faultcode);
- perrno = errno;
- if (cnt < 0 && perrno != EAGAIN)
- purple_debug_info("soap", "read: %s\n", g_strerror(perrno));
+ if (g_strcmp0(faultdata, "psf:Redirect") == 0) {
+ PurpleXmlNode *xml_url;
+ gchar *url = NULL;
- if (conn->current_request && conn->current_request->secure &&
- !purple_debug_is_unsafe())
- purple_debug_misc("soap", "Received secure request.\n");
- else if (count != 0)
- purple_debug_misc("soap", "current %s\n", conn->buf->str + cursor);
+ xml_url = purple_xmlnode_get_child(xml_fault, "redirectUrl");
+ if (xml_url != NULL)
+ url = purple_xmlnode_get_data(xml_url);
- /* && count is necessary for Adium, on OS X the last read always
- return an error, so we want to proceed anyway. See #5212 for
- discussion on this and the above buffer size issues */
- if(cnt < 0 && errno == EAGAIN && count == 0)
- return;
+ if (url) {
+ msn_soap_service_send_message_simple(sreq->soaps,
+ sreq->message, url, sreq->secure, sreq->cb,
+ sreq->cb_data);
+ }
- /* msn_soap_process could alter errno */
- msn_soap_process(conn);
+ /* Steal the message, passed to another call. */
+ sreq->message = NULL;
+ msn_soap_request_free(sreq);
- if ((cnt < 0 && perrno != EAGAIN) || cnt == 0) {
- /* It's possible msn_soap_process closed the ssl connection */
- if (conn->ssl) {
- purple_ssl_close(conn->ssl);
- conn->ssl = NULL;
- msn_soap_connection_handle_next(conn);
+ g_free(url);
+ g_free(faultdata);
+ return;
}
- }
-}
+ if (g_strcmp0(faultdata, "wsse:FailedAuthentication") == 0) {
+ PurpleXmlNode *xml_reason =
+ purple_xmlnode_get_child(xml_fault, "faultstring");
+ gchar *reasondata = NULL;
-static gboolean
-msn_soap_write_cb_internal(gpointer data, gint fd, PurpleInputCondition cond,
- gboolean initial)
-{
- MsnSoapConnection *conn = data;
- int written;
-
- if (cond != PURPLE_INPUT_WRITE)
- return TRUE;
-
- written = purple_ssl_write(conn->ssl, conn->buf->str + conn->handled_len,
- conn->buf->len - conn->handled_len);
-
- if (written < 0 && errno == EAGAIN)
- return TRUE;
- else if (written <= 0) {
- purple_ssl_close(conn->ssl);
- conn->ssl = NULL;
- if (!initial)
- msn_soap_connection_handle_next(conn);
- return FALSE;
- }
-
- conn->handled_len += written;
+ if (xml_reason)
+ reasondata = purple_xmlnode_get_data(xml_reason);
- if (conn->handled_len < conn->buf->len)
- return TRUE;
+ msn_session_set_error(sreq->soaps->session, MSN_ERROR_AUTH,
+ reasondata);
- /* we are done! */
- g_string_free(conn->buf, TRUE);
- conn->buf = NULL;
- conn->handled_len = 0;
- conn->body_len = 0;
- conn->response_code = 0;
- conn->headers_done = FALSE;
- conn->close_when_done = FALSE;
-
- purple_input_remove(conn->event_handle);
- conn->event_handle = purple_input_add(conn->ssl->fd, PURPLE_INPUT_READ,
- msn_soap_read_cb, conn);
- return TRUE;
-}
-
-static void
-msn_soap_write_cb(gpointer data, gint fd, PurpleInputCondition cond)
-{
- msn_soap_write_cb_internal(data, fd, cond, FALSE);
-}
+ g_free(reasondata);
+ g_free(faultdata);
+ msn_soap_request_free(sreq);
+ return;
+ }
+ g_free(faultdata);
+ }
-static void
-msn_soap_error_cb(PurpleSslConnection *ssl, PurpleSslErrorType error,
- gpointer data)
-{
- MsnSoapConnection *conn = data;
+ if (xml_fault != NULL || xml_body != NULL) {
+ MsnSoapMessage *resp;
- /* sslconn already frees the connection in case of error */
- conn->ssl = NULL;
+ resp = msn_soap_message_new(NULL, xml_root);
+ sreq->cb(sreq->message, resp, sreq->cb_data);
+ msn_soap_message_free(resp);
+ }
- g_hash_table_remove(conn->session->soap_table, conn->host);
+ /* XXX: shouldn't msn_session_set_error here? */
+ msn_soap_request_free(sreq);
}
static void
-msn_soap_connected_cb(gpointer data, PurpleSslConnection *ssl,
- PurpleInputCondition cond)
-{
- MsnSoapConnection *conn = data;
-
- conn->connected = TRUE;
-
- if (conn->run_timer == 0)
- conn->run_timer = purple_timeout_add(0, msn_soap_connection_run, conn);
-}
-
-MsnSoapMessage *
-msn_soap_message_new(const char *action, xmlnode *xml)
+msn_soap_service_send_message_simple(MsnSoapService *soaps,
+ MsnSoapMessage *message, const gchar *url, gboolean secure,
+ MsnSoapCallback cb, gpointer cb_data)
{
- MsnSoapMessage *message = g_new0(MsnSoapMessage, 1);
-
- message->action = g_strdup(action);
- message->xml = xml;
-
- return message;
+ PurpleHttpRequest *hreq;
+ MsnSoapRequest *sreq;
+ gchar *body;
+ int body_len;
+
+ sreq = g_new0(MsnSoapRequest, 1);
+ sreq->soaps = soaps;
+ sreq->cb = cb;
+ sreq->cb_data = cb_data;
+ sreq->secure = secure;
+ sreq->message = message;
+
+ hreq = purple_http_request_new(url);
+ purple_http_request_set_method(hreq, "POST");
+ purple_http_request_set_keepalive_pool(hreq, soaps->keepalive_pool);
+ purple_http_request_header_set(hreq, "SOAPAction",
+ message->action ? message->action : "");
+ purple_http_request_header_set(hreq, "Content-Type",
+ "text/xml; charset=utf-8");
+ purple_http_request_header_set(hreq, "User-Agent",
+ "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)");
+ purple_http_request_header_set(hreq, "Cache-Control", "no-cache");
+
+ body = purple_xmlnode_to_str(message->xml, &body_len);
+ purple_http_request_set_contents(hreq, body, body_len);
+ g_free(body);
+
+ purple_http_request(purple_account_get_connection(
+ soaps->session->account), hreq, msn_soap_service_recv, sreq);
+ purple_http_request_unref(hreq);
}
-static gboolean
-msn_soap_connection_run(gpointer data)
+void
+msn_soap_service_send_message(MsnSoapService *soaps, MsnSoapMessage *message,
+ const gchar *host, const gchar *path, gboolean secure,
+ MsnSoapCallback cb, gpointer cb_data)
{
- MsnSoapConnection *conn = data;
- MsnSoapRequest *req = g_queue_peek_head(conn->queue);
-
- conn->run_timer = 0;
-
- if (req) {
- if (conn->ssl == NULL) {
- conn->ssl = purple_ssl_connect(conn->session->account, conn->host,
- 443, msn_soap_connected_cb, msn_soap_error_cb, conn);
- } else if (conn->connected) {
- int len = -1;
- char *body = xmlnode_to_str(req->message->xml, &len);
- GSList *iter;
-
- g_queue_pop_head(conn->queue);
-
- conn->buf = g_string_new("");
-
- g_string_append_printf(conn->buf,
- "POST /%s HTTP/1.1\r\n"
- "SOAPAction: %s\r\n"
- "Content-Type:text/xml; charset=utf-8\r\n"
- "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n"
- "Accept: */*\r\n"
- "Host: %s\r\n"
- "Content-Length: %d\r\n"
- "Connection: Keep-Alive\r\n"
- "Cache-Control: no-cache\r\n",
- req->path, req->message->action ? req->message->action : "",
- conn->host, len);
-
- for (iter = req->message->headers; iter; iter = iter->next) {
- g_string_append(conn->buf, (char *)iter->data);
- g_string_append(conn->buf, "\r\n");
- }
-
- g_string_append(conn->buf, "\r\n");
- g_string_append(conn->buf, body);
-
- if (req->secure && !purple_debug_is_unsafe())
- purple_debug_misc("soap", "Sending secure request.\n");
- else
- purple_debug_misc("soap", "%s\n", conn->buf->str);
+ gchar *url;
- conn->handled_len = 0;
- conn->current_request = req;
+ g_return_if_fail(host != NULL);
+ g_return_if_fail(path != NULL);
- if (conn->event_handle)
- purple_input_remove(conn->event_handle);
- conn->event_handle = purple_input_add(conn->ssl->fd,
- PURPLE_INPUT_WRITE, msn_soap_write_cb, conn);
- if (!msn_soap_write_cb_internal(conn, conn->ssl->fd, PURPLE_INPUT_WRITE, TRUE)) {
- /* Not connected => reconnect and retry */
- purple_debug_info("soap", "not connected, reconnecting\n");
+ if (path[0] == '/')
+ path = &path[1];
- conn->connected = FALSE;
- conn->current_request = NULL;
- msn_soap_connection_sanitize(conn, FALSE);
+ url = g_strdup_printf("https://%s/%s", host, path);
- g_queue_push_head(conn->queue, req);
- conn->run_timer = purple_timeout_add(0, msn_soap_connection_run, conn);
- }
-
- g_free(body);
- }
- }
+ msn_soap_service_send_message_simple(soaps, message, url, secure,
+ cb, cb_data);
- return FALSE;
+ g_free(url);
}
diff --git a/libpurple/protocols/msn/soap.h b/libpurple/protocols/msn/soap.h
index de4d50e3dd..76d6d5041c 100644
--- a/libpurple/protocols/msn/soap.h
+++ b/libpurple/protocols/msn/soap.h
@@ -1,6 +1,5 @@
/**
- * @file soap.h
- * header file for SOAP connection related process
+ * @file soap.h SOAP handling
*
* purple
*
@@ -20,33 +19,37 @@
*
* 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 02110-1301, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef MSN_SOAP_H
#define MSN_SOAP_H
+#include "xmlnode.h"
+
typedef struct _MsnSoapMessage MsnSoapMessage;
-#include <glib.h>
+typedef struct _MsnSoapService MsnSoapService;
-#include "xmlnode.h"
+typedef void (*MsnSoapCallback)(MsnSoapMessage *request,
+ MsnSoapMessage *response, gpointer cb_data);
#include "session.h"
-#include "sslconn.h"
-typedef void (*MsnSoapCallback)(MsnSoapMessage *request,
- MsnSoapMessage *response, gpointer cb_data);
+MsnSoapMessage *
+msn_soap_message_new(const gchar *action, PurpleXmlNode *xml);
+
+PurpleXmlNode *
+msn_soap_message_get_xml(MsnSoapMessage *message);
-struct _MsnSoapMessage {
- char *action;
- xmlnode *xml;
- GSList *headers;
-};
+MsnSoapService *
+msn_soap_service_new(MsnSession *session);
-MsnSoapMessage *msn_soap_message_new(const char *action, xmlnode *xml);
+void
+msn_soap_service_destroy(MsnSoapService *soaps);
-void msn_soap_message_send(MsnSession *session, MsnSoapMessage *message,
- const char *host, const char *path, gboolean secure,
+void
+msn_soap_service_send_message(MsnSoapService *soaps, MsnSoapMessage *message,
+ const gchar *host, const gchar *path, gboolean secure,
MsnSoapCallback cb, gpointer cb_data);
#endif /* MSN_SOAP_H */
diff --git a/libpurple/protocols/msn/state.c b/libpurple/protocols/msn/state.c
index 7157cfcd13..6a196edf70 100644
--- a/libpurple/protocols/msn/state.c
+++ b/libpurple/protocols/msn/state.c
@@ -55,73 +55,73 @@ static const char *away_text[] =
static char *
msn_build_psm(const char *psmstr,const char *mediastr, const char *guidstr, guint protocol_ver)
{
- xmlnode *dataNode,*psmNode,*mediaNode,*guidNode;
+ PurpleXmlNode *dataNode,*psmNode,*mediaNode,*guidNode;
char *result;
int length;
- dataNode = xmlnode_new("Data");
+ dataNode = purple_xmlnode_new("Data");
- psmNode = xmlnode_new("PSM");
+ psmNode = purple_xmlnode_new("PSM");
if(psmstr != NULL){
- xmlnode_insert_data(psmNode, psmstr, -1);
+ purple_xmlnode_insert_data(psmNode, psmstr, -1);
}
- xmlnode_insert_child(dataNode, psmNode);
+ purple_xmlnode_insert_child(dataNode, psmNode);
- mediaNode = xmlnode_new("CurrentMedia");
+ mediaNode = purple_xmlnode_new("CurrentMedia");
if(mediastr != NULL){
- xmlnode_insert_data(mediaNode, mediastr, -1);
+ purple_xmlnode_insert_data(mediaNode, mediastr, -1);
}
- xmlnode_insert_child(dataNode, mediaNode);
+ purple_xmlnode_insert_child(dataNode, mediaNode);
- guidNode = xmlnode_new("MachineGuid");
+ guidNode = purple_xmlnode_new("MachineGuid");
if(guidstr != NULL){
- xmlnode_insert_data(guidNode, guidstr, -1);
+ purple_xmlnode_insert_data(guidNode, guidstr, -1);
}
- xmlnode_insert_child(dataNode, guidNode);
+ purple_xmlnode_insert_child(dataNode, guidNode);
if (protocol_ver >= 16) {
/* TODO: What is this for? */
- xmlnode *ddpNode = xmlnode_new("DDP");
- xmlnode_insert_child(dataNode, ddpNode);
+ PurpleXmlNode *ddpNode = purple_xmlnode_new("DDP");
+ purple_xmlnode_insert_child(dataNode, ddpNode);
}
- result = xmlnode_to_str(dataNode, &length);
- xmlnode_free(dataNode);
+ result = purple_xmlnode_to_str(dataNode, &length);
+ purple_xmlnode_free(dataNode);
return result;
}
/* get the CurrentMedia info from the XML node */
char *
-msn_get_currentmedia(xmlnode *payloadNode)
+msn_get_currentmedia(PurpleXmlNode *payloadNode)
{
- xmlnode *currentmediaNode;
+ PurpleXmlNode *currentmediaNode;
char *currentmedia;
purple_debug_info("msn", "Get CurrentMedia\n");
- currentmediaNode = xmlnode_get_child(payloadNode, "CurrentMedia");
+ currentmediaNode = purple_xmlnode_get_child(payloadNode, "CurrentMedia");
if (currentmediaNode == NULL) {
purple_debug_info("msn", "No CurrentMedia Node\n");
return NULL;
}
- currentmedia = xmlnode_get_data(currentmediaNode);
+ currentmedia = purple_xmlnode_get_data(currentmediaNode);
return currentmedia;
}
/* Get the PSM info from the XML node */
char *
-msn_get_psm(xmlnode *payloadNode)
+msn_get_psm(PurpleXmlNode *payloadNode)
{
- xmlnode *psmNode;
+ PurpleXmlNode *psmNode;
char *psm;
purple_debug_info("msn", "msn get PSM\n");
- psmNode = xmlnode_get_child(payloadNode, "PSM");
+ psmNode = purple_xmlnode_get_child(payloadNode, "PSM");
if (psmNode == NULL) {
purple_debug_info("msn", "No PSM status Node\n");
return NULL;
}
- psm = xmlnode_get_data(psmNode);
+ psm = purple_xmlnode_get_data(psmNode);
return psm;
}
diff --git a/libpurple/protocols/msn/state.h b/libpurple/protocols/msn/state.h
index eca877729c..5ab015fe7a 100644
--- a/libpurple/protocols/msn/state.h
+++ b/libpurple/protocols/msn/state.h
@@ -59,10 +59,10 @@ const char *msn_away_get_text(MsnAwayType type);
const char *msn_state_get_text(MsnAwayType state);
/* Get the CurrentMedia info from the XML node */
-char *msn_get_currentmedia(xmlnode *payloadNode);
+char *msn_get_currentmedia(PurpleXmlNode *payloadNode);
/* Get the PSM info from the XML node */
-char *msn_get_psm(xmlnode *payloadNode);
+char *msn_get_psm(PurpleXmlNode *payloadNode);
MsnAwayType msn_state_from_account(PurpleAccount *account);
diff --git a/libpurple/protocols/msn/switchboard.c b/libpurple/protocols/msn/switchboard.c
index cc4ef75b5a..0793b228dc 100644
--- a/libpurple/protocols/msn/switchboard.c
+++ b/libpurple/protocols/msn/switchboard.c
@@ -24,6 +24,8 @@
#include "internal.h"
#include "debug.h"
+#include "image.h"
+#include "image-store.h"
#include "msnutils.h"
#include "switchboard.h"
@@ -288,18 +290,17 @@ msn_switchboard_add_user(MsnSwitchBoard *swboard, const char *user)
return;
}
- if ((swboard->conv != NULL) &&
- (purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT))
+ if ((swboard->conv != NULL) && (PURPLE_IS_CHAT_CONVERSATION(swboard->conv)))
{
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv), msnuser->passport, NULL,
- PURPLE_CBFLAGS_NONE, TRUE);
+ purple_chat_conversation_add_user(PURPLE_CHAT_CONVERSATION(swboard->conv),
+ msnuser->passport, NULL, PURPLE_CHAT_USER_NONE, TRUE);
msn_servconn_set_idle_timeout(swboard->servconn, 0);
}
else if (swboard->current_users > 1)
{
msn_servconn_set_idle_timeout(swboard->servconn, 0);
if (swboard->conv == NULL ||
- purple_conversation_get_type(swboard->conv) != PURPLE_CONV_TYPE_CHAT)
+ !PURPLE_IS_CHAT_CONVERSATION(swboard->conv))
{
GList *l;
@@ -312,9 +313,9 @@ msn_switchboard_add_user(MsnSwitchBoard *swboard, const char *user)
swboard->chat_id = msn_switchboard_get_chat_id();
swboard->flag |= MSN_SB_FLAG_IM;
- swboard->conv = serv_got_joined_chat(account->gc,
+ swboard->conv = PURPLE_CONVERSATION(purple_serv_got_joined_chat(purple_account_get_connection(account),
swboard->chat_id,
- "MSN Chat");
+ "MSN Chat"));
for (l = swboard->users; l != NULL; l = l->next)
{
@@ -322,13 +323,13 @@ msn_switchboard_add_user(MsnSwitchBoard *swboard, const char *user)
tmp_user = ((MsnUser*)l->data)->passport;
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv),
- tmp_user, NULL, PURPLE_CBFLAGS_NONE, TRUE);
+ purple_chat_conversation_add_user(PURPLE_CHAT_CONVERSATION(swboard->conv),
+ tmp_user, NULL, PURPLE_CHAT_USER_NONE, TRUE);
}
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv),
+ purple_chat_conversation_add_user(PURPLE_CHAT_CONVERSATION(swboard->conv),
purple_account_get_username(account),
- NULL, PURPLE_CBFLAGS_NONE, TRUE);
+ NULL, PURPLE_CHAT_USER_NONE, TRUE);
g_free(swboard->im_user);
swboard->im_user = NULL;
@@ -336,8 +337,8 @@ msn_switchboard_add_user(MsnSwitchBoard *swboard, const char *user)
}
else if (swboard->conv == NULL)
{
- swboard->conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
- msnuser->passport, account);
+ swboard->conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(
+ msnuser->passport, account));
}
else
{
@@ -359,8 +360,8 @@ msn_switchboard_get_conv(MsnSwitchBoard *swboard)
account = swboard->session->account;
- return (swboard->conv = purple_conversation_new(PURPLE_CONV_TYPE_IM,
- account, swboard->im_user));
+ return (swboard->conv = PURPLE_CONVERSATION(purple_im_conversation_new(
+ account, swboard->im_user)));
}
static void
@@ -373,7 +374,7 @@ msn_switchboard_report_user(MsnSwitchBoard *swboard, PurpleMessageFlags flags, c
if ((conv = msn_switchboard_get_conv(swboard)) != NULL)
{
- purple_conversation_write(conv, NULL, msg, flags, time(NULL));
+ purple_conversation_write_system_message(conv, msg, flags);
}
}
@@ -618,11 +619,11 @@ bye_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
msn_switchboard_destroy(swboard);
}
else if ((swboard->current_users > 1) ||
- (purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT))
+ PURPLE_IS_CHAT_CONVERSATION(swboard->conv))
{
GList *passport;
/* This is a switchboard used for a chat */
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(swboard->conv), user, NULL);
+ purple_chat_conversation_remove_user(PURPLE_CHAT_CONVERSATION(swboard->conv), user, NULL);
passport = g_list_find_custom(swboard->users, user, (GCompareFunc)strcmp);
if (passport)
@@ -747,11 +748,11 @@ out_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
PurpleConnection *gc;
MsnSwitchBoard *swboard;
- gc = cmdproc->session->account->gc;
+ gc = purple_account_get_connection(cmdproc->session->account);
swboard = cmdproc->data;
if (swboard->current_users > 1)
- serv_got_chat_left(gc, swboard->chat_id);
+ purple_serv_got_chat_left(gc, swboard->chat_id);
msn_switchboard_disconnect(swboard);
}
@@ -808,7 +809,8 @@ msn_switchboard_show_ink(MsnSwitchBoard *swboard, const char *passport,
PurpleConnection *gc;
guchar *image_data;
size_t image_len;
- int imgid;
+ PurpleImage *img;
+ guint imgid;
char *image_msg;
if (!purple_str_has_prefix(data, "base64:"))
@@ -827,18 +829,20 @@ msn_switchboard_show_ink(MsnSwitchBoard *swboard, const char *passport,
return;
}
- imgid = purple_imgstore_add_with_id(image_data, image_len, NULL);
- image_msg = g_strdup_printf("<IMG ID='%d'>", imgid);
+ img = purple_image_new_from_data(image_data, image_len);
+ imgid = purple_image_store_add_temporary(img);
+ g_object_unref(img);
+ image_msg = g_strdup_printf("<img src=\"" PURPLE_IMAGE_STORE_PROTOCOL
+ "%u\">", imgid);
if (swboard->current_users > 1 ||
((swboard->conv != NULL) &&
- purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT))
- serv_got_chat_in(gc, swboard->chat_id, passport, 0, image_msg,
- time(NULL));
+ PURPLE_IS_CHAT_CONVERSATION(swboard->conv)))
+ purple_serv_got_chat_in(gc, swboard->chat_id, passport,
+ PURPLE_MESSAGE_RECV, image_msg, time(NULL));
else
- serv_got_im(gc, passport, image_msg, 0, time(NULL));
+ purple_serv_got_im(gc, passport, image_msg, 0, time(NULL));
- purple_imgstore_unref_by_id(imgid);
g_free(image_msg);
}
diff --git a/libpurple/protocols/msn/tlv.c b/libpurple/protocols/msn/tlv.c
index 48400f35ac..d05cf4b9e2 100644
--- a/libpurple/protocols/msn/tlv.c
+++ b/libpurple/protocols/msn/tlv.c
@@ -326,7 +326,7 @@ msn_tlvlist_remove(GSList **list, const guint8 type)
}
char *
-msn_tlvlist_write(GSList *list, size_t *out_len)
+msn_tlvlist_write(GSList *list, guint8 *out_len)
{
char *buf;
char *tmp;
@@ -339,7 +339,7 @@ msn_tlvlist_write(GSList *list, size_t *out_len)
for (; list; list = g_slist_next(list)) {
msn_tlv_t *tlv = (msn_tlv_t *)list->data;
- if (G_UNLIKELY(tlv->length + 2 > bytes_left)) {
+ if (G_UNLIKELY((gsize)tlv->length + 2 > bytes_left)) {
buf = g_realloc(buf, total_len + 256);
bytes_left += 256;
total_len += 256;
diff --git a/libpurple/protocols/msn/tlv.h b/libpurple/protocols/msn/tlv.h
index c2032f79bc..065dee3388 100644
--- a/libpurple/protocols/msn/tlv.h
+++ b/libpurple/protocols/msn/tlv.h
@@ -52,7 +52,7 @@ GSList *msn_tlvlist_copy(GSList *orig);
int msn_tlvlist_count(GSList *list);
size_t msn_tlvlist_size(GSList *list);
gboolean msn_tlvlist_equal(GSList *one, GSList *two);
-char *msn_tlvlist_write(GSList *list, size_t *out_len);
+char *msn_tlvlist_write(GSList *list, guint8 *out_len);
void msn_tlvlist_free(GSList *list);
int msn_tlvlist_add_raw(GSList **list, const guint8 type, const guint8 length, const char *value);
diff --git a/libpurple/protocols/msn/transaction.c b/libpurple/protocols/msn/transaction.c
index e77b13553b..57f3464553 100644
--- a/libpurple/protocols/msn/transaction.c
+++ b/libpurple/protocols/msn/transaction.c
@@ -158,7 +158,7 @@ msn_transaction_unqueue(MsnTransaction *trans, MsnCmdProc *cmdproc)
void
msn_transaction_set_payload(MsnTransaction *trans,
- const char *payload, int payload_len)
+ const char *payload, gsize payload_len)
{
g_return_if_fail(trans != NULL);
g_return_if_fail(payload != NULL);
diff --git a/libpurple/protocols/msn/transaction.h b/libpurple/protocols/msn/transaction.h
index 7d4764145e..10fe8fc0c3 100644
--- a/libpurple/protocols/msn/transaction.h
+++ b/libpurple/protocols/msn/transaction.h
@@ -75,7 +75,7 @@ char *msn_transaction_to_string(MsnTransaction *trans);
void msn_transaction_queue_cmd(MsnTransaction *trans, MsnCommand *cmd);
void msn_transaction_unqueue_cmd(MsnTransaction *trans, MsnCmdProc *cmdproc);
void msn_transaction_set_payload(MsnTransaction *trans,
- const char *payload, int payload_len);
+ const char *payload, gsize payload_len);
void msn_transaction_set_data(MsnTransaction *trans, void *data);
void msn_transaction_set_data_free(MsnTransaction *trans, GDestroyNotify fn);
void msn_transaction_set_saveable(MsnTransaction *trans, gboolean saveable);
diff --git a/libpurple/protocols/msn/user.c b/libpurple/protocols/msn/user.c
index a9565be27c..72c8d67e80 100644
--- a/libpurple/protocols/msn/user.c
+++ b/libpurple/protocols/msn/user.c
@@ -76,7 +76,7 @@ msn_user_destroy(MsnUser *user)
}
if (user->msnobj != NULL)
- msn_object_destroy(user->msnobj);
+ msn_object_destroy(user->msnobj, FALSE);
g_free(user->passport);
g_free(user->friendly_name);
@@ -233,7 +233,7 @@ msn_user_set_friendly_name(MsnUser *user, const char *name)
g_free(user->friendly_name);
user->friendly_name = g_strdup(name);
- serv_got_alias(purple_account_get_connection(user->userlist->session->account),
+ purple_serv_got_alias(purple_account_get_connection(user->userlist->session->account),
user->passport, name);
return TRUE;
}
@@ -330,7 +330,7 @@ msn_user_unset_op(MsnUser *user, MsnListOp list_op)
}
void
-msn_user_set_buddy_icon(MsnUser *user, PurpleStoredImage *img)
+msn_user_set_buddy_icon(MsnUser *user, PurpleImage *img)
{
MsnObject *msnobj;
@@ -369,7 +369,7 @@ msn_user_add_group_id(MsnUser *user, const char* group_id)
purple_debug_info("msn", "User: group id:%s,name:%s,user:%s\n", group_id, group_name, passport);
- g = purple_find_group(group_name);
+ g = purple_blist_find_group(group_name);
if ((group_id == NULL) && (g == NULL))
{
@@ -377,7 +377,7 @@ msn_user_add_group_id(MsnUser *user, const char* group_id)
purple_blist_add_group(g, NULL);
}
- b = purple_find_buddy_in_group(account, passport, g);
+ b = purple_blist_find_buddy_in_group(account, passport, g);
if (b == NULL)
{
b = purple_buddy_new(account, passport, NULL);
@@ -393,7 +393,7 @@ msn_user_is_online(PurpleAccount *account, const char *name)
{
PurpleBuddy *buddy;
- buddy = purple_find_buddy(account, name);
+ buddy = purple_blist_find_buddy(account, name);
return PURPLE_BUDDY_IS_ONLINE(buddy);
}
@@ -406,7 +406,7 @@ msn_user_is_yahoo(PurpleAccount *account, const char *name)
gc = purple_account_get_connection(account);
if (gc != NULL)
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(gc);
if ((session != NULL) && (user = msn_userlist_find_user(session->userlist, name)) != NULL)
{
@@ -530,7 +530,7 @@ buddy_icon_cached(PurpleConnection *gc, MsnObject *obj)
account = purple_connection_get_account(gc);
- buddy = purple_find_buddy(account, msn_object_get_creator(obj));
+ buddy = purple_blist_find_buddy(account, msn_object_get_creator(obj));
if (buddy == NULL)
return FALSE;
@@ -566,7 +566,7 @@ queue_buddy_icon_request(MsnUser *user)
return;
}
- if (!buddy_icon_cached(account->gc, obj)) {
+ if (!buddy_icon_cached(purple_account_get_connection(account), obj)) {
MsnUserList *userlist;
userlist = user->userlist;
@@ -588,8 +588,8 @@ msn_user_set_object(MsnUser *user, MsnObject *obj)
{
g_return_if_fail(user != NULL);
- if (user->msnobj != NULL && !msn_object_find_local(msn_object_get_sha1(obj)))
- msn_object_destroy(user->msnobj);
+ if (user->msnobj != NULL)
+ msn_object_destroy(user->msnobj, TRUE);
user->msnobj = obj;
@@ -761,17 +761,7 @@ msn_user_passport_cmp(MsnUser *user, const char *passport)
str = purple_normalize_nocase(NULL, msn_user_get_passport(user));
pass = g_strdup(str);
-#if GLIB_CHECK_VERSION(2,16,0)
result = g_strcmp0(pass, purple_normalize_nocase(NULL, passport));
-#else
- str = purple_normalize_nocase(NULL, passport);
- if (!pass)
- result = -(pass != str);
- else if (!str)
- result = pass != str;
- else
- result = strcmp(pass, str);
-#endif /* GLIB < 2.16.0 */
g_free(pass);
diff --git a/libpurple/protocols/msn/user.h b/libpurple/protocols/msn/user.h
index 727c717a8f..5985a5e573 100644
--- a/libpurple/protocols/msn/user.h
+++ b/libpurple/protocols/msn/user.h
@@ -53,6 +53,7 @@ typedef enum
CURRENT_MEDIA_OFFICE
} CurrentMediaType;
+#include "image.h"
#include "object.h"
#include "session.h"
#include "userlist.h"
@@ -138,7 +139,7 @@ typedef struct MsnUserEndpoint {
} MsnUserEndpoint;
/**************************************************************************
- ** @name User API *
+ ** User API *
**************************************************************************/
/*@{*/
@@ -220,7 +221,7 @@ gboolean msn_user_set_friendly_name(MsnUser *user, const char *name);
* @param user The user.
* @param img The buddy icon image
*/
-void msn_user_set_buddy_icon(MsnUser *user, PurpleStoredImage *img);
+void msn_user_set_buddy_icon(MsnUser *user, PurpleImage *img);
/**
* Sets the group ID list for a user.
diff --git a/libpurple/protocols/msn/userlist.c b/libpurple/protocols/msn/userlist.c
index 8ad73813ff..b1623bc6ef 100644
--- a/libpurple/protocols/msn/userlist.c
+++ b/libpurple/protocols/msn/userlist.c
@@ -46,24 +46,26 @@ typedef struct
* Callbacks
**************************************************************************/
static void
-msn_accept_add_cb(gpointer data)
+msn_accept_add_cb(const char *message, gpointer data)
{
+ MsnSession *session;
+ MsnUserList *userlist;
+ PurpleAccount *account;
MsnPermitAdd *pa = data;
purple_debug_misc("msn", "Accepted the new buddy: %s\n", pa->who);
- if (PURPLE_CONNECTION_IS_VALID(pa->gc))
- {
- MsnSession *session = pa->gc->proto_data;
- MsnUserList *userlist = session->userlist;
- PurpleAccount *account = purple_connection_get_account(pa->gc);
+ PURPLE_ASSERT_CONNECTION_IS_VALID(pa->gc);
- msn_userlist_add_buddy_to_list(userlist, pa->who, MSN_LIST_AL);
- purple_privacy_deny_remove(account, pa->who, TRUE);
- purple_privacy_permit_add(account, pa->who, TRUE);
+ session = purple_connection_get_protocol_data(pa->gc);
+ userlist = session->userlist;
+ account = purple_connection_get_account(pa->gc);
- msn_del_contact_from_list(session, NULL, pa->who, MSN_LIST_PL);
- }
+ msn_userlist_add_buddy_to_list(userlist, pa->who, MSN_LIST_AL);
+ purple_account_privacy_deny_remove(account, pa->who, TRUE);
+ purple_account_privacy_permit_add(account, pa->who, TRUE);
+
+ msn_del_contact_from_list(session, NULL, pa->who, MSN_LIST_PL);
g_free(pa->who);
g_free(pa->friendly);
@@ -71,23 +73,25 @@ msn_accept_add_cb(gpointer data)
}
static void
-msn_cancel_add_cb(gpointer data)
+msn_cancel_add_cb(const char *message, gpointer data)
{
+ MsnSession *session;
+ MsnUserList *userlist;
+ MsnCallbackState *state;
MsnPermitAdd *pa = data;
purple_debug_misc("msn", "Denied the new buddy: %s\n", pa->who);
- if (PURPLE_CONNECTION_IS_VALID(pa->gc))
- {
- MsnSession *session = pa->gc->proto_data;
- MsnUserList *userlist = session->userlist;
- MsnCallbackState *state = msn_callback_state_new(session);
+ PURPLE_ASSERT_CONNECTION_IS_VALID(pa->gc);
- msn_callback_state_set_action(state, MSN_DENIED_BUDDY);
+ session = purple_connection_get_protocol_data(pa->gc);
+ userlist = session->userlist;
+ state = msn_callback_state_new(session);
- msn_userlist_add_buddy_to_list(userlist, pa->who, MSN_LIST_BL);
- msn_del_contact_from_list(session, state, pa->who, MSN_LIST_PL);
- }
+ msn_callback_state_set_action(state, MSN_DENIED_BUDDY);
+
+ msn_userlist_add_buddy_to_list(userlist, pa->who, MSN_LIST_BL);
+ msn_del_contact_from_list(session, state, pa->who, MSN_LIST_PL);
g_free(pa->who);
g_free(pa->friendly);
@@ -107,7 +111,7 @@ got_new_entry(PurpleConnection *gc, const char *passport, const char *friendly,
acct = purple_connection_get_account(gc);
purple_account_request_authorization(acct, passport, NULL, friendly, message,
- purple_find_buddy(acct, passport) != NULL,
+ purple_blist_find_buddy(acct, passport) != NULL,
msn_accept_add_cb, msn_cancel_add_cb, pa);
}
@@ -146,21 +150,21 @@ msn_got_lst_user(MsnSession *session, MsnUser *user,
/* FIXME: It might be a real alias */
/* Umm, what? This might fix bug #1385130 */
- serv_got_alias(gc, passport, store);
+ purple_serv_got_alias(gc, passport, store);
}
if (list_op & MSN_LIST_AL_OP)
{
/* These are users who are allowed to see our status. */
- purple_privacy_deny_remove(account, passport, TRUE);
- purple_privacy_permit_add(account, passport, TRUE);
+ purple_account_privacy_deny_remove(account, passport, TRUE);
+ purple_account_privacy_permit_add(account, passport, TRUE);
}
if (list_op & MSN_LIST_BL_OP)
{
/* These are users who are not allowed to see our status. */
- purple_privacy_permit_remove(account, passport, TRUE);
- purple_privacy_deny_add(account, passport, TRUE);
+ purple_account_privacy_permit_remove(account, passport, TRUE);
+ purple_account_privacy_deny_add(account, passport, TRUE);
}
if (list_op & MSN_LIST_RL_OP)
@@ -468,8 +472,7 @@ msn_userlist_rem_buddy(MsnUserList *userlist, const char *who)
purple_request_yes_no(userlist->session->account,
_("Delete Buddy from Address Book?"),
_("Do you want to delete this buddy from your address book as well?"),
- user->passport, 0, userlist->session->account, user->passport,
- NULL, ab,
+ user->passport, 0, purple_request_cpar_from_account(userlist->session->account), ab,
G_CALLBACK(userlist_ab_delete_cb), G_CALLBACK(userlist_ab_delete_cb));
} else
msn_delete_contact(userlist->session, user);
@@ -522,8 +525,9 @@ msn_userlist_add_buddy(MsnUserList *userlist, const char *who, const char *group
char *str = g_strdup_printf(_("Unable to add \"%s\"."), who);
- purple_notify_error(NULL, NULL, str,
- _("The username specified is invalid."));
+ purple_notify_error(NULL, NULL, str, _("The username specified "
+ "is invalid."), purple_request_cpar_from_account(
+ userlist->session->account));
g_free(str);
return;
@@ -733,7 +737,7 @@ msn_userlist_load(MsnSession *session)
g_return_if_fail(gc != NULL);
- for (l = purple_find_buddies(account, NULL); l != NULL;
+ for (l = purple_blist_find_buddies(account, NULL); l != NULL;
l = g_slist_delete_link(l, l)) {
PurpleBuddy *buddy = l->data;
@@ -742,13 +746,13 @@ msn_userlist_load(MsnSession *session)
purple_buddy_set_protocol_data(buddy, user);
msn_user_set_op(user, MSN_LIST_FL_OP);
}
- for (l = session->account->permit; l != NULL; l = l->next)
+ for (l = purple_account_privacy_get_permitted(session->account); l != NULL; l = l->next)
{
user = msn_userlist_find_add_user(session->userlist,
(char *)l->data,NULL);
msn_user_set_op(user, MSN_LIST_AL_OP);
}
- for (l = session->account->deny; l != NULL; l = l->next)
+ for (l = purple_account_privacy_get_denied(session->account); l != NULL; l = l->next)
{
user = msn_userlist_find_add_user(session->userlist,
(char *)l->data,NULL);
diff --git a/libpurple/protocols/mxit/Makefile.am b/libpurple/protocols/mxit/Makefile.am
index 84ac6867f9..c8209af90e 100644
--- a/libpurple/protocols/mxit/Makefile.am
+++ b/libpurple/protocols/mxit/Makefile.am
@@ -1,23 +1,19 @@
EXTRA_DIST = \
Makefile.mingw
-pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+pkgdir = @PURPLE_PLUGINDIR@
MXITSOURCES = \
actions.c \
actions.h \
- aes.c \
- aes.h \
chunk.c \
chunk.h \
- cipher.c \
- cipher.h \
+ cipher-mxit.c \
+ cipher-mxit.h \
filexfer.c \
filexfer.h \
formcmds.c \
formcmds.h \
- http.c \
- http.h \
login.c \
login.h \
markup.c \
@@ -40,7 +36,7 @@ MXITSOURCES = \
AM_CFLAGS = $(st)
-libmxit_la_LDFLAGS = -module -avoid-version
+libmxit_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
if STATIC_MXIT
@@ -54,7 +50,7 @@ else
st =
pkg_LTLIBRARIES = libmxit.la
libmxit_la_SOURCES = $(MXITSOURCES)
-libmxit_la_LIBADD = $(GLIB_LIBS)
+libmxit_la_LIBADD = @PURPLE_LIBS@
endif
diff --git a/libpurple/protocols/mxit/Makefile.mingw b/libpurple/protocols/mxit/Makefile.mingw
index b7574d079f..bb51e1d373 100644
--- a/libpurple/protocols/mxit/Makefile.mingw
+++ b/libpurple/protocols/mxit/Makefile.mingw
@@ -38,12 +38,10 @@ LIB_PATHS += -L$(GTK_TOP)/lib \
## SOURCES, OBJECTS
##
C_SRC = actions.c \
- aes.c \
chunk.c \
- cipher.c \
+ cipher-mxit.c \
filexfer.c \
formcmds.c \
- http.c \
login.c \
markup.c \
multimx.c \
@@ -61,6 +59,7 @@ OBJECTS = $(C_SRC:%.c=%.o)
##
LIBS = \
-lglib-2.0 \
+ -lgobject-2.0 \
-lintl \
-lws2_32 \
-lpurple
diff --git a/libpurple/protocols/mxit/actions.c b/libpurple/protocols/mxit/actions.c
index 4bf77f3eb1..19d5417d44 100644
--- a/libpurple/protocols/mxit/actions.c
+++ b/libpurple/protocols/mxit/actions.c
@@ -32,7 +32,7 @@
#include "roster.h"
#include "actions.h"
#include "splashscreen.h"
-#include "cipher.h"
+#include "cipher-mxit.h"
#include "profile.h"
@@ -53,10 +53,7 @@ static void mxit_profile_cb( PurpleConnection* gc, PurpleRequestFields* fields )
purple_debug_info( MXIT_PLUGIN_ID, "mxit_profile_cb\n" );
- if ( !PURPLE_CONNECTION_IS_VALID( gc ) ) {
- purple_debug_error( MXIT_PLUGIN_ID, "Unable to update profile; account offline.\n" );
- return;
- }
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
/* validate name */
name = purple_request_fields_get_string( fields, "name" );
@@ -239,12 +236,12 @@ static void mxit_profile_action( PurplePluginAction* action )
field = purple_request_field_string_new( "bday", _( "Birthday" ), profile->birthday, FALSE );
purple_request_field_group_add_field( public_group, field );
if ( profile->flags & CP_PROF_DOBLOCKED )
- purple_request_field_string_set_editable( field, FALSE );
+ purple_request_field_set_sensitive( field, FALSE );
/* gender */
- field = purple_request_field_choice_new( "male", _( "Gender" ), ( profile->male ) ? 1 : 0 );
- purple_request_field_choice_add( field, _( "Female" ) ); /* 0 */
- purple_request_field_choice_add( field, _( "Male" ) ); /* 1 */
+ field = purple_request_field_choice_new( "male", _( "Gender" ), GINT_TO_POINTER(profile->male ? 1 : 0));
+ purple_request_field_choice_add( field, _( "Female" ), GINT_TO_POINTER(0));
+ purple_request_field_choice_add( field, _( "Male" ), GINT_TO_POINTER(1));
purple_request_field_group_add_field( public_group, field );
/* first name */
@@ -311,7 +308,7 @@ static void mxit_profile_action( PurplePluginAction* action )
/* (reference: "libpurple/request.h") */
purple_request_fields( gc, _( "Profile" ), _( "Update your MXit Profile" ), NULL, fields, _( "Set" ),
- G_CALLBACK( mxit_profile_cb ), _( "Cancel" ), NULL, purple_connection_get_account( gc ), NULL, NULL, gc );
+ G_CALLBACK( mxit_profile_cb ), _( "Cancel" ), NULL, purple_request_cpar_from_connection(gc), gc );
}
@@ -330,10 +327,7 @@ static void mxit_change_pin_cb( PurpleConnection* gc, PurpleRequestFields* field
int len;
int i;
- if ( !PURPLE_CONNECTION_IS_VALID( gc ) ) {
- purple_debug_error( MXIT_PLUGIN_ID, "Unable to update PIN; account offline.\n" );
- return;
- }
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
/* validate pin */
pin = purple_request_fields_get_string( fields, "pin" );
@@ -361,7 +355,7 @@ static void mxit_change_pin_cb( PurpleConnection* gc, PurpleRequestFields* field
out:
if ( !err ) {
/* update PIN in account */
- purple_account_set_password( session->acc, pin );
+ purple_account_set_password( session->acc, pin, NULL, NULL );
/* update session object */
g_free( session->encpwd );
@@ -385,7 +379,6 @@ out:
static void mxit_change_pin_action( PurplePluginAction* action )
{
PurpleConnection* gc = (PurpleConnection*) action->context;
- struct MXitSession* session = purple_connection_get_protocol_data( gc );
PurpleRequestFields* fields = NULL;
PurpleRequestFieldGroup* group = NULL;
@@ -398,18 +391,18 @@ static void mxit_change_pin_action( PurplePluginAction* action )
purple_request_fields_add_group( fields, group );
/* pin */
- field = purple_request_field_string_new( "pin", _( "PIN" ), purple_account_get_password( session->acc ), FALSE );
+ field = purple_request_field_string_new( "pin", _( "PIN" ), purple_connection_get_password( gc ), FALSE );
purple_request_field_string_set_masked( field, TRUE );
purple_request_field_group_add_field( group, field );
/* verify pin */
- field = purple_request_field_string_new( "pin2", _( "Verify PIN" ), purple_account_get_password( session->acc ), FALSE );
+ field = purple_request_field_string_new( "pin2", _( "Verify PIN" ), purple_connection_get_password( gc ), FALSE );
purple_request_field_string_set_masked( field, TRUE );
purple_request_field_group_add_field( group, field );
/* (reference: "libpurple/request.h") */
purple_request_fields( gc, _( "Change PIN" ), _( "Change MXit PIN" ), NULL, fields, _( "Set" ),
- G_CALLBACK( mxit_change_pin_cb ), _( "Cancel" ), NULL, purple_connection_get_account( gc ), NULL, NULL, gc );
+ G_CALLBACK( mxit_change_pin_cb ), _( "Cancel" ), NULL, purple_request_cpar_from_connection(gc), gc );
}
@@ -500,7 +493,7 @@ static void mxit_user_search_action( PurplePluginAction* action )
NULL, FALSE, FALSE, NULL,
_( "_Search" ), G_CALLBACK( mxit_user_search_cb ),
_( "_Cancel" ), NULL,
- purple_connection_get_account( gc ), NULL, NULL,
+ purple_request_cpar_from_connection(gc),
gc );
}
diff --git a/libpurple/protocols/mxit/aes.c b/libpurple/protocols/mxit/aes.c
deleted file mode 100644
index 2a2702b1a8..0000000000
--- a/libpurple/protocols/mxit/aes.c
+++ /dev/null
@@ -1,405 +0,0 @@
-
-// advanced encryption standard
-// author: karl malbrain, malbrain@yahoo.com
-
-/*
-This work, including the source code, documentation
-and related data, is placed into the public domain.
-
-The orginal author is Karl Malbrain.
-
-THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY
-OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF
-MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE,
-ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE
-RESULTING FROM THE USE, MODIFICATION, OR
-REDISTRIBUTION OF THIS SOFTWARE.
-*/
-
-#include <string.h>
-#include <memory.h>
-
-#include "aes.h"
-
-// AES only supports Nb=4
-#define Nb 4 // number of columns in the state & expanded key
-
-#define Nk 4 // number of columns in a key
-#define Nr 10 // number of rounds in encryption
-
-static uchar Sbox[256] = { // forward s-box
-0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
-0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
-0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
-0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
-0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
-0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
-0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
-0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
-0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
-0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
-0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
-0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
-0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
-0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
-0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
-0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16};
-
-static uchar InvSbox[256] = { // inverse s-box
-0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
-0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
-0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
-0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
-0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
-0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
-0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
-0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
-0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
-0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
-0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
-0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
-0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
-0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
-0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
-0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d};
-
-// combined Xtimes2[Sbox[]]
-static uchar Xtime2Sbox[256] = {
-0xc6, 0xf8, 0xee, 0xf6, 0xff, 0xd6, 0xde, 0x91, 0x60, 0x02, 0xce, 0x56, 0xe7, 0xb5, 0x4d, 0xec,
-0x8f, 0x1f, 0x89, 0xfa, 0xef, 0xb2, 0x8e, 0xfb, 0x41, 0xb3, 0x5f, 0x45, 0x23, 0x53, 0xe4, 0x9b,
-0x75, 0xe1, 0x3d, 0x4c, 0x6c, 0x7e, 0xf5, 0x83, 0x68, 0x51, 0xd1, 0xf9, 0xe2, 0xab, 0x62, 0x2a,
-0x08, 0x95, 0x46, 0x9d, 0x30, 0x37, 0x0a, 0x2f, 0x0e, 0x24, 0x1b, 0xdf, 0xcd, 0x4e, 0x7f, 0xea,
-0x12, 0x1d, 0x58, 0x34, 0x36, 0xdc, 0xb4, 0x5b, 0xa4, 0x76, 0xb7, 0x7d, 0x52, 0xdd, 0x5e, 0x13,
-0xa6, 0xb9, 0x00, 0xc1, 0x40, 0xe3, 0x79, 0xb6, 0xd4, 0x8d, 0x67, 0x72, 0x94, 0x98, 0xb0, 0x85,
-0xbb, 0xc5, 0x4f, 0xed, 0x86, 0x9a, 0x66, 0x11, 0x8a, 0xe9, 0x04, 0xfe, 0xa0, 0x78, 0x25, 0x4b,
-0xa2, 0x5d, 0x80, 0x05, 0x3f, 0x21, 0x70, 0xf1, 0x63, 0x77, 0xaf, 0x42, 0x20, 0xe5, 0xfd, 0xbf,
-0x81, 0x18, 0x26, 0xc3, 0xbe, 0x35, 0x88, 0x2e, 0x93, 0x55, 0xfc, 0x7a, 0xc8, 0xba, 0x32, 0xe6,
-0xc0, 0x19, 0x9e, 0xa3, 0x44, 0x54, 0x3b, 0x0b, 0x8c, 0xc7, 0x6b, 0x28, 0xa7, 0xbc, 0x16, 0xad,
-0xdb, 0x64, 0x74, 0x14, 0x92, 0x0c, 0x48, 0xb8, 0x9f, 0xbd, 0x43, 0xc4, 0x39, 0x31, 0xd3, 0xf2,
-0xd5, 0x8b, 0x6e, 0xda, 0x01, 0xb1, 0x9c, 0x49, 0xd8, 0xac, 0xf3, 0xcf, 0xca, 0xf4, 0x47, 0x10,
-0x6f, 0xf0, 0x4a, 0x5c, 0x38, 0x57, 0x73, 0x97, 0xcb, 0xa1, 0xe8, 0x3e, 0x96, 0x61, 0x0d, 0x0f,
-0xe0, 0x7c, 0x71, 0xcc, 0x90, 0x06, 0xf7, 0x1c, 0xc2, 0x6a, 0xae, 0x69, 0x17, 0x99, 0x3a, 0x27,
-0xd9, 0xeb, 0x2b, 0x22, 0xd2, 0xa9, 0x07, 0x33, 0x2d, 0x3c, 0x15, 0xc9, 0x87, 0xaa, 0x50, 0xa5,
-0x03, 0x59, 0x09, 0x1a, 0x65, 0xd7, 0x84, 0xd0, 0x82, 0x29, 0x5a, 0x1e, 0x7b, 0xa8, 0x6d, 0x2c
-};
-
-// combined Xtimes3[Sbox[]]
-static uchar Xtime3Sbox[256] = {
-0xa5, 0x84, 0x99, 0x8d, 0x0d, 0xbd, 0xb1, 0x54, 0x50, 0x03, 0xa9, 0x7d, 0x19, 0x62, 0xe6, 0x9a,
-0x45, 0x9d, 0x40, 0x87, 0x15, 0xeb, 0xc9, 0x0b, 0xec, 0x67, 0xfd, 0xea, 0xbf, 0xf7, 0x96, 0x5b,
-0xc2, 0x1c, 0xae, 0x6a, 0x5a, 0x41, 0x02, 0x4f, 0x5c, 0xf4, 0x34, 0x08, 0x93, 0x73, 0x53, 0x3f,
-0x0c, 0x52, 0x65, 0x5e, 0x28, 0xa1, 0x0f, 0xb5, 0x09, 0x36, 0x9b, 0x3d, 0x26, 0x69, 0xcd, 0x9f,
-0x1b, 0x9e, 0x74, 0x2e, 0x2d, 0xb2, 0xee, 0xfb, 0xf6, 0x4d, 0x61, 0xce, 0x7b, 0x3e, 0x71, 0x97,
-0xf5, 0x68, 0x00, 0x2c, 0x60, 0x1f, 0xc8, 0xed, 0xbe, 0x46, 0xd9, 0x4b, 0xde, 0xd4, 0xe8, 0x4a,
-0x6b, 0x2a, 0xe5, 0x16, 0xc5, 0xd7, 0x55, 0x94, 0xcf, 0x10, 0x06, 0x81, 0xf0, 0x44, 0xba, 0xe3,
-0xf3, 0xfe, 0xc0, 0x8a, 0xad, 0xbc, 0x48, 0x04, 0xdf, 0xc1, 0x75, 0x63, 0x30, 0x1a, 0x0e, 0x6d,
-0x4c, 0x14, 0x35, 0x2f, 0xe1, 0xa2, 0xcc, 0x39, 0x57, 0xf2, 0x82, 0x47, 0xac, 0xe7, 0x2b, 0x95,
-0xa0, 0x98, 0xd1, 0x7f, 0x66, 0x7e, 0xab, 0x83, 0xca, 0x29, 0xd3, 0x3c, 0x79, 0xe2, 0x1d, 0x76,
-0x3b, 0x56, 0x4e, 0x1e, 0xdb, 0x0a, 0x6c, 0xe4, 0x5d, 0x6e, 0xef, 0xa6, 0xa8, 0xa4, 0x37, 0x8b,
-0x32, 0x43, 0x59, 0xb7, 0x8c, 0x64, 0xd2, 0xe0, 0xb4, 0xfa, 0x07, 0x25, 0xaf, 0x8e, 0xe9, 0x18,
-0xd5, 0x88, 0x6f, 0x72, 0x24, 0xf1, 0xc7, 0x51, 0x23, 0x7c, 0x9c, 0x21, 0xdd, 0xdc, 0x86, 0x85,
-0x90, 0x42, 0xc4, 0xaa, 0xd8, 0x05, 0x01, 0x12, 0xa3, 0x5f, 0xf9, 0xd0, 0x91, 0x58, 0x27, 0xb9,
-0x38, 0x13, 0xb3, 0x33, 0xbb, 0x70, 0x89, 0xa7, 0xb6, 0x22, 0x92, 0x20, 0x49, 0xff, 0x78, 0x7a,
-0x8f, 0xf8, 0x80, 0x17, 0xda, 0x31, 0xc6, 0xb8, 0xc3, 0xb0, 0x77, 0x11, 0xcb, 0xfc, 0xd6, 0x3a
-};
-
-// modular multiplication tables
-// based on:
-
-// Xtime2[x] = (x & 0x80 ? 0x1b : 0) ^ (x + x)
-// Xtime3[x] = x^Xtime2[x];
-
-#if 0
-static uchar Xtime2[256] = {
-0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
-0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
-0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
-0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
-0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
-0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
-0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
-0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
-0x1b, 0x19, 0x1f, 0x1d, 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05,
-0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, 0x23, 0x21, 0x27, 0x25,
-0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45,
-0x7b, 0x79, 0x7f, 0x7d, 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65,
-0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85,
-0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5,
-0xdb, 0xd9, 0xdf, 0xdd, 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5,
-0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5};
-#endif
-
-static uchar Xtime9[256] = {
-0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
-0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7,
-0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
-0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, 0xc7, 0xce, 0xd5, 0xdc,
-0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49, 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01,
-0xe6, 0xef, 0xf4, 0xfd, 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91,
-0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e, 0x21, 0x28, 0x33, 0x3a,
-0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa,
-0xec, 0xe5, 0xfe, 0xf7, 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b,
-0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, 0x10, 0x19, 0x02, 0x0b,
-0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0,
-0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30,
-0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed,
-0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d,
-0xa1, 0xa8, 0xb3, 0xba, 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6,
-0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46};
-
-static uchar XtimeB[256] = {
-0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69,
-0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9,
-0x7b, 0x70, 0x6d, 0x66, 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12,
-0xcb, 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa, 0x93, 0x98, 0x85, 0x8e, 0xbf, 0xb4, 0xa9, 0xa2,
-0xf6, 0xfd, 0xe0, 0xeb, 0xda, 0xd1, 0xcc, 0xc7, 0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f,
-0x46, 0x4d, 0x50, 0x5b, 0x6a, 0x61, 0x7c, 0x77, 0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f,
-0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, 0xbc, 0xd5, 0xde, 0xc3, 0xc8, 0xf9, 0xf2, 0xef, 0xe4,
-0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c, 0x65, 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54,
-0xf7, 0xfc, 0xe1, 0xea, 0xdb, 0xd0, 0xcd, 0xc6, 0xaf, 0xa4, 0xb9, 0xb2, 0x83, 0x88, 0x95, 0x9e,
-0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76, 0x1f, 0x14, 0x09, 0x02, 0x33, 0x38, 0x25, 0x2e,
-0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd, 0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, 0xe5,
-0x3c, 0x37, 0x2a, 0x21, 0x10, 0x1b, 0x06, 0x0d, 0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55,
-0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44, 0x75, 0x7e, 0x63, 0x68,
-0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80, 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8,
-0x7a, 0x71, 0x6c, 0x67, 0x56, 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13,
-0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3};
-
-static uchar XtimeD[256] = {
-0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b,
-0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b,
-0xbb, 0xb6, 0xa1, 0xac, 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0,
-0x6b, 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48, 0x03, 0x0e, 0x19, 0x14, 0x37, 0x3a, 0x2d, 0x20,
-0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e, 0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26,
-0xbd, 0xb0, 0xa7, 0xaa, 0x89, 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6,
-0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, 0xf5, 0xbe, 0xb3, 0xa4, 0xa9, 0x8a, 0x87, 0x90, 0x9d,
-0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25, 0x6e, 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d,
-0xda, 0xd7, 0xc0, 0xcd, 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91,
-0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75, 0x56, 0x5b, 0x4c, 0x41,
-0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42, 0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, 0x2a,
-0xb1, 0xbc, 0xab, 0xa6, 0x85, 0x88, 0x9f, 0x92, 0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa,
-0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc,
-0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44, 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c,
-0x0c, 0x01, 0x16, 0x1b, 0x38, 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47,
-0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97};
-
-static uchar XtimeE[256] = {
-0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a,
-0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba,
-0xdb, 0xd5, 0xc7, 0xc9, 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81,
-0x3b, 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11, 0x4b, 0x45, 0x57, 0x59, 0x73, 0x7d, 0x6f, 0x61,
-0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87, 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7,
-0x4d, 0x43, 0x51, 0x5f, 0x75, 0x7b, 0x69, 0x67, 0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17,
-0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, 0x5c, 0x06, 0x08, 0x1a, 0x14, 0x3e, 0x30, 0x22, 0x2c,
-0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc, 0xe6, 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc,
-0x41, 0x4f, 0x5d, 0x53, 0x79, 0x77, 0x65, 0x6b, 0x31, 0x3f, 0x2d, 0x23, 0x09, 0x07, 0x15, 0x1b,
-0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b, 0xd1, 0xdf, 0xcd, 0xc3, 0xe9, 0xe7, 0xf5, 0xfb,
-0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0, 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0,
-0x7a, 0x74, 0x66, 0x68, 0x42, 0x4c, 0x5e, 0x50, 0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20,
-0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e, 0xa4, 0xaa, 0xb8, 0xb6,
-0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26, 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56,
-0x37, 0x39, 0x2b, 0x25, 0x0f, 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d,
-0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d};
-
-// exchanges columns in each of 4 rows
-// row0 - unchanged, row1- shifted left 1,
-// row2 - shifted left 2 and row3 - shifted left 3
-static void ShiftRows (uchar *state)
-{
-uchar tmp;
-
- // just substitute row 0
- state[0] = Sbox[state[0]], state[4] = Sbox[state[4]];
- state[8] = Sbox[state[8]], state[12] = Sbox[state[12]];
-
- // rotate row 1
- tmp = Sbox[state[1]], state[1] = Sbox[state[5]];
- state[5] = Sbox[state[9]], state[9] = Sbox[state[13]], state[13] = tmp;
-
- // rotate row 2
- tmp = Sbox[state[2]], state[2] = Sbox[state[10]], state[10] = tmp;
- tmp = Sbox[state[6]], state[6] = Sbox[state[14]], state[14] = tmp;
-
- // rotate row 3
- tmp = Sbox[state[15]], state[15] = Sbox[state[11]];
- state[11] = Sbox[state[7]], state[7] = Sbox[state[3]], state[3] = tmp;
-}
-
-// restores columns in each of 4 rows
-// row0 - unchanged, row1- shifted right 1,
-// row2 - shifted right 2 and row3 - shifted right 3
-static void InvShiftRows (uchar *state)
-{
-uchar tmp;
-
- // restore row 0
- state[0] = InvSbox[state[0]], state[4] = InvSbox[state[4]];
- state[8] = InvSbox[state[8]], state[12] = InvSbox[state[12]];
-
- // restore row 1
- tmp = InvSbox[state[13]], state[13] = InvSbox[state[9]];
- state[9] = InvSbox[state[5]], state[5] = InvSbox[state[1]], state[1] = tmp;
-
- // restore row 2
- tmp = InvSbox[state[2]], state[2] = InvSbox[state[10]], state[10] = tmp;
- tmp = InvSbox[state[6]], state[6] = InvSbox[state[14]], state[14] = tmp;
-
- // restore row 3
- tmp = InvSbox[state[3]], state[3] = InvSbox[state[7]];
- state[7] = InvSbox[state[11]], state[11] = InvSbox[state[15]], state[15] = tmp;
-}
-
-// recombine and mix each row in a column
-static void MixSubColumns (uchar *state)
-{
-uchar tmp[4 * Nb];
-
- // mixing column 0
- tmp[0] = Xtime2Sbox[state[0]] ^ Xtime3Sbox[state[5]] ^ Sbox[state[10]] ^ Sbox[state[15]];
- tmp[1] = Sbox[state[0]] ^ Xtime2Sbox[state[5]] ^ Xtime3Sbox[state[10]] ^ Sbox[state[15]];
- tmp[2] = Sbox[state[0]] ^ Sbox[state[5]] ^ Xtime2Sbox[state[10]] ^ Xtime3Sbox[state[15]];
- tmp[3] = Xtime3Sbox[state[0]] ^ Sbox[state[5]] ^ Sbox[state[10]] ^ Xtime2Sbox[state[15]];
-
- // mixing column 1
- tmp[4] = Xtime2Sbox[state[4]] ^ Xtime3Sbox[state[9]] ^ Sbox[state[14]] ^ Sbox[state[3]];
- tmp[5] = Sbox[state[4]] ^ Xtime2Sbox[state[9]] ^ Xtime3Sbox[state[14]] ^ Sbox[state[3]];
- tmp[6] = Sbox[state[4]] ^ Sbox[state[9]] ^ Xtime2Sbox[state[14]] ^ Xtime3Sbox[state[3]];
- tmp[7] = Xtime3Sbox[state[4]] ^ Sbox[state[9]] ^ Sbox[state[14]] ^ Xtime2Sbox[state[3]];
-
- // mixing column 2
- tmp[8] = Xtime2Sbox[state[8]] ^ Xtime3Sbox[state[13]] ^ Sbox[state[2]] ^ Sbox[state[7]];
- tmp[9] = Sbox[state[8]] ^ Xtime2Sbox[state[13]] ^ Xtime3Sbox[state[2]] ^ Sbox[state[7]];
- tmp[10] = Sbox[state[8]] ^ Sbox[state[13]] ^ Xtime2Sbox[state[2]] ^ Xtime3Sbox[state[7]];
- tmp[11] = Xtime3Sbox[state[8]] ^ Sbox[state[13]] ^ Sbox[state[2]] ^ Xtime2Sbox[state[7]];
-
- // mixing column 3
- tmp[12] = Xtime2Sbox[state[12]] ^ Xtime3Sbox[state[1]] ^ Sbox[state[6]] ^ Sbox[state[11]];
- tmp[13] = Sbox[state[12]] ^ Xtime2Sbox[state[1]] ^ Xtime3Sbox[state[6]] ^ Sbox[state[11]];
- tmp[14] = Sbox[state[12]] ^ Sbox[state[1]] ^ Xtime2Sbox[state[6]] ^ Xtime3Sbox[state[11]];
- tmp[15] = Xtime3Sbox[state[12]] ^ Sbox[state[1]] ^ Sbox[state[6]] ^ Xtime2Sbox[state[11]];
-
- memcpy (state, tmp, sizeof(tmp));
-}
-
-// restore and un-mix each row in a column
-static void InvMixSubColumns (uchar *state)
-{
-uchar tmp[4 * Nb];
-int i;
-
- // restore column 0
- tmp[0] = XtimeE[state[0]] ^ XtimeB[state[1]] ^ XtimeD[state[2]] ^ Xtime9[state[3]];
- tmp[5] = Xtime9[state[0]] ^ XtimeE[state[1]] ^ XtimeB[state[2]] ^ XtimeD[state[3]];
- tmp[10] = XtimeD[state[0]] ^ Xtime9[state[1]] ^ XtimeE[state[2]] ^ XtimeB[state[3]];
- tmp[15] = XtimeB[state[0]] ^ XtimeD[state[1]] ^ Xtime9[state[2]] ^ XtimeE[state[3]];
-
- // restore column 1
- tmp[4] = XtimeE[state[4]] ^ XtimeB[state[5]] ^ XtimeD[state[6]] ^ Xtime9[state[7]];
- tmp[9] = Xtime9[state[4]] ^ XtimeE[state[5]] ^ XtimeB[state[6]] ^ XtimeD[state[7]];
- tmp[14] = XtimeD[state[4]] ^ Xtime9[state[5]] ^ XtimeE[state[6]] ^ XtimeB[state[7]];
- tmp[3] = XtimeB[state[4]] ^ XtimeD[state[5]] ^ Xtime9[state[6]] ^ XtimeE[state[7]];
-
- // restore column 2
- tmp[8] = XtimeE[state[8]] ^ XtimeB[state[9]] ^ XtimeD[state[10]] ^ Xtime9[state[11]];
- tmp[13] = Xtime9[state[8]] ^ XtimeE[state[9]] ^ XtimeB[state[10]] ^ XtimeD[state[11]];
- tmp[2] = XtimeD[state[8]] ^ Xtime9[state[9]] ^ XtimeE[state[10]] ^ XtimeB[state[11]];
- tmp[7] = XtimeB[state[8]] ^ XtimeD[state[9]] ^ Xtime9[state[10]] ^ XtimeE[state[11]];
-
- // restore column 3
- tmp[12] = XtimeE[state[12]] ^ XtimeB[state[13]] ^ XtimeD[state[14]] ^ Xtime9[state[15]];
- tmp[1] = Xtime9[state[12]] ^ XtimeE[state[13]] ^ XtimeB[state[14]] ^ XtimeD[state[15]];
- tmp[6] = XtimeD[state[12]] ^ Xtime9[state[13]] ^ XtimeE[state[14]] ^ XtimeB[state[15]];
- tmp[11] = XtimeB[state[12]] ^ XtimeD[state[13]] ^ Xtime9[state[14]] ^ XtimeE[state[15]];
-
- for( i=0; i < 4 * Nb; i++ )
- state[i] = InvSbox[tmp[i]];
-}
-
-// encrypt/decrypt columns of the key
-// n.b. you can replace this with
-// byte-wise xor if you wish.
-
-static void AddRoundKey (unsigned *state, unsigned *key)
-{
-int idx;
-
- for( idx = 0; idx < 4; idx++ )
- state[idx] ^= key[idx];
-}
-
-static uchar Rcon[11] = {
-0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36};
-
-// produce Nb bytes for each round
-void ExpandKey (uchar *key, uchar *expkey)
-{
-uchar tmp0, tmp1, tmp2, tmp3, tmp4;
-unsigned idx;
-
- memcpy (expkey, key, Nk * 4);
-
- for( idx = Nk; idx < Nb * (Nr + 1); idx++ ) {
- tmp0 = expkey[4*idx - 4];
- tmp1 = expkey[4*idx - 3];
- tmp2 = expkey[4*idx - 2];
- tmp3 = expkey[4*idx - 1];
- if( !(idx % Nk) ) {
- tmp4 = tmp3;
- tmp3 = Sbox[tmp0];
- tmp0 = Sbox[tmp1] ^ Rcon[idx/Nk];
- tmp1 = Sbox[tmp2];
- tmp2 = Sbox[tmp4];
- } else if( Nk > 6 && idx % Nk == 4 ) {
- tmp0 = Sbox[tmp0];
- tmp1 = Sbox[tmp1];
- tmp2 = Sbox[tmp2];
- tmp3 = Sbox[tmp3];
- }
-
- expkey[4*idx+0] = expkey[4*idx - 4*Nk + 0] ^ tmp0;
- expkey[4*idx+1] = expkey[4*idx - 4*Nk + 1] ^ tmp1;
- expkey[4*idx+2] = expkey[4*idx - 4*Nk + 2] ^ tmp2;
- expkey[4*idx+3] = expkey[4*idx - 4*Nk + 3] ^ tmp3;
- }
-}
-
-// encrypt one 128 bit block
-void Encrypt (uchar *in, uchar *expkey, uchar *out)
-{
-uchar state[Nb * 4];
-unsigned round;
-
- memcpy (state, in, Nb * 4);
- AddRoundKey ((unsigned *)state, (unsigned *)expkey);
-
- for( round = 1; round < Nr + 1; round++ ) {
- if( round < Nr )
- MixSubColumns (state);
- else
- ShiftRows (state);
-
- AddRoundKey ((unsigned *)state, (unsigned *)expkey + round * Nb);
- }
-
- memcpy (out, state, sizeof(state));
-}
-
-void Decrypt (uchar *in, uchar *expkey, uchar *out)
-{
-uchar state[Nb * 4];
-unsigned round;
-
- memcpy (state, in, sizeof(state));
-
- AddRoundKey ((unsigned *)state, (unsigned *)expkey + Nr * Nb);
- InvShiftRows(state);
-
- for( round = Nr; round--; )
- {
- AddRoundKey ((unsigned *)state, (unsigned *)expkey + round * Nb);
- if( round )
- InvMixSubColumns (state);
- }
-
- memcpy (out, state, sizeof(state));
-}
diff --git a/libpurple/protocols/mxit/aes.h b/libpurple/protocols/mxit/aes.h
deleted file mode 100644
index 831a30d3c9..0000000000
--- a/libpurple/protocols/mxit/aes.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// advanced encryption standard
-// author: karl malbrain, malbrain@yahoo.com
-
-/*
-This work, including the source code, documentation
-and related data, is placed into the public domain.
-
-The orginal author is Karl Malbrain.
-
-THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY
-OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF
-MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE,
-ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE
-RESULTING FROM THE USE, MODIFICATION, OR
-REDISTRIBUTION OF THIS SOFTWARE.
-*/
-
-
-#ifndef AES_MALBRAIN
-#define AES_MALBRAIN
-
-
-// AES only supports Nb=4
-#define Nb 4 // number of columns in the state & expanded key
-
-#define Nk 4 // number of columns in a key
-#define Nr 10 // number of rounds in encryption
-
-
-typedef unsigned char uchar;
-
-
-void ExpandKey (uchar *key, uchar *expkey);
-void Encrypt (uchar *in, uchar *expkey, uchar *out);
-void Decrypt (uchar *in, uchar *expkey, uchar *out);
-
-
-#endif /* AES_MALBRAIN */
-
diff --git a/libpurple/protocols/mxit/chunk.c b/libpurple/protocols/mxit/chunk.c
index a575bcdd75..3d2ce69e27 100644
--- a/libpurple/protocols/mxit/chunk.c
+++ b/libpurple/protocols/mxit/chunk.c
@@ -170,9 +170,13 @@ static int get_int8( const char* chunkdata, char* value )
*/
static int get_int16( const char* chunkdata, short* value )
{
- *value = ntohs( *( (const short*) chunkdata ) ); /* host byte-order */
+ gint16 value_v;
- return sizeof( short );
+ memcpy(&value_v, chunkdata, sizeof(value_v));
+
+ *value = ntohs(value_v); /* host byte-order */
+
+ return sizeof(value_v);
}
/*------------------------------------------------------------------------
@@ -184,9 +188,13 @@ static int get_int16( const char* chunkdata, short* value )
*/
static int get_int32( const char* chunkdata, int* value )
{
- *value = ntohl( *( (const int*) chunkdata ) ); /* host byte-order */
+ gint32 value_v;
- return sizeof( int );
+ memcpy(&value_v, chunkdata, sizeof(value_v));
+
+ *value = ntohl(value_v); /* host byte-order */
+
+ return sizeof(value_v);
}
#if 0
@@ -472,6 +480,9 @@ void mxit_chunk_parse_offer( char* chunkdata, int datalen, struct offerfile_chun
/* mime type [UTF-8] */
pos += get_utf8_string( &chunkdata[pos], offer->mimetype, sizeof( offer->mimetype ) );
+ if (pos > datalen)
+ purple_debug_warning(MXIT_PLUGIN_ID, "pos > datalen");
+
/* timestamp [8 bytes] */
/* not used by libPurple */
@@ -635,6 +646,9 @@ void mxit_chunk_parse_sendfile( char* chunkdata, int datalen, struct sendfile_ch
/* status message [UTF-8 string] */
pos += get_utf8_string( &chunkdata[pos], sendfile->statusmsg, sizeof( sendfile->statusmsg ) );
+
+ if (pos != datalen)
+ purple_debug_misc(MXIT_PLUGIN_ID, "pos != datalen");
}
diff --git a/libpurple/protocols/mxit/chunk.h b/libpurple/protocols/mxit/chunk.h
index 6ef094f638..f3d3803d43 100644
--- a/libpurple/protocols/mxit/chunk.h
+++ b/libpurple/protocols/mxit/chunk.h
@@ -84,7 +84,8 @@ static inline void set_chunk_type( gchar* chunkheader, guint type )
static inline guint32 chunk_length( gchar* chunkheader )
{
- guint32 length = *( (const guint32*) &chunkheader[1] );
+ guint32 length;
+ memcpy(&length, &chunkheader[1], sizeof(guint32));
return htonl( length );
}
diff --git a/libpurple/protocols/mxit/cipher-mxit.c b/libpurple/protocols/mxit/cipher-mxit.c
new file mode 100644
index 0000000000..7f04ba89e2
--- /dev/null
+++ b/libpurple/protocols/mxit/cipher-mxit.c
@@ -0,0 +1,83 @@
+/*
+ * 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 "cipher-mxit.h"
+
+#include "cipher.h"
+#include "ciphers/aescipher.h"
+#include "debug.h"
+#include "internal.h"
+
+#define INITIAL_KEY "6170383452343567"
+#define SECRET_HEADER "<mxit/>"
+
+/**
+ * Encrypt the user's cleartext password using the AES 128-bit (ECB)
+ * encryption algorithm.
+ *
+ * @param session The MXit session object
+ *
+ * @return The encrypted & encoded password. Must be g_free'd when
+ * no longer needed.
+ */
+gchar *
+mxit_encrypt_password(struct MXitSession* session)
+{
+ guchar key[16];
+ size_t clientkey_len, header_len, pass_len, plaintext_len;
+ const gchar *plaintext_passwd;
+ guchar *plaintext;
+ guchar encrypted[64]; /* shouldn't be longer than 17 */
+ PurpleCipher *cipher;
+ ssize_t encrypted_size;
+
+ purple_debug_info(MXIT_PLUGIN_ID, "mxit_encrypt_password");
+
+ /* build the AES encryption key */
+ g_assert(strlen(INITIAL_KEY) == sizeof(key));
+ memcpy(key, INITIAL_KEY, sizeof(key));
+ clientkey_len = strlen(session->clientkey);
+ if (clientkey_len > sizeof(key))
+ clientkey_len = sizeof(key);
+ memcpy(key, session->clientkey, clientkey_len);
+
+ /* build the secret data to be encrypted: SECRET_HEADER + password */
+ plaintext_passwd = purple_connection_get_password(session->con);
+ g_return_val_if_fail(plaintext_passwd, NULL);
+ pass_len = strlen(plaintext_passwd);
+ header_len = strlen(SECRET_HEADER);
+ /* Trailing NUL, just to be safe. But PKCS#7 seems to be enough. */
+ plaintext_len = header_len + pass_len + 1;
+ plaintext = g_new0(guchar, plaintext_len);
+ memcpy(plaintext, SECRET_HEADER, header_len);
+ memcpy(plaintext + header_len, plaintext_passwd, pass_len);
+
+ /* encrypt */
+ cipher = purple_aes_cipher_new();
+ purple_cipher_set_key(cipher, key, sizeof(key));
+ purple_cipher_set_batch_mode(cipher, PURPLE_CIPHER_BATCH_MODE_ECB);
+ encrypted_size = purple_cipher_encrypt(cipher,
+ plaintext, plaintext_len, encrypted, sizeof(encrypted));
+ g_return_val_if_fail(encrypted_size > 0, NULL);
+
+ return purple_base64_encode(encrypted, encrypted_size);
+}
diff --git a/libpurple/protocols/myspace/markup.h b/libpurple/protocols/mxit/cipher-mxit.h
index dfbbebad1b..259dc5d00c 100644
--- a/libpurple/protocols/myspace/markup.h
+++ b/libpurple/protocols/mxit/cipher-mxit.h
@@ -1,6 +1,9 @@
-/* MySpaceIM Protocol Plugin - markup
+/*
+ * purple
*
- * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im>
+ * 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
@@ -14,14 +17,15 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#ifndef _MYSPACE_MARKUP_H
-#define _MYSPACE_MARKUP_H
+#ifndef _MXIT_CIPHER_H_
+#define _MXIT_CIPHER_H_
-/* High-level msim markup <=> Purple html conversion functions. */
-gchar *msim_markup_to_html(MsimSession *, const gchar *raw);
-gchar *html_to_msim_markup(MsimSession *, const gchar *raw);
+#include "mxit.h"
-#endif /* !_MYSPACE_MARKUP_H */
+gchar *
+mxit_encrypt_password(struct MXitSession* session);
+
+#endif /* _MXIT_CIPHER_H_ */
diff --git a/libpurple/protocols/mxit/cipher.c b/libpurple/protocols/mxit/cipher.c
deleted file mode 100644
index 856dfe36d4..0000000000
--- a/libpurple/protocols/mxit/cipher.c
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * MXit Protocol libPurple Plugin
- *
- * -- encryption --
- *
- * Pieter Loubser <libpurple@mxit.com>
- *
- * (C) Copyright 2009 MXit Lifestyle (Pty) Ltd.
- * <http://www.mxitlifestyle.com>
- *
- * 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 "mxit.h"
-#include "cipher.h"
-#include "aes.h"
-
-
-/* encryption */
-#define INITIAL_KEY "6170383452343567"
-#define SECRET_HEADER "<mxit/>"
-#define ENCRYPT_HEADER "<mxitencrypted ver=\"5.2\"/>"
-
-
-/*------------------------------------------------------------------------
- * Add ISO10126 Padding to the data.
- *
- * @param data The data to pad.
- */
-static void padding_add( GString* data )
-{
- unsigned int blocks = ( data->len / 16 ) + 1;
- unsigned int padding = ( blocks * 16 ) - data->len;
-
- g_string_set_size( data, blocks * 16 );
- data->str[data->len - 1] = padding;
-}
-
-
-/*------------------------------------------------------------------------
- * Remove ISO10126 Padding from the data.
- *
- * @param data The data from which to remove padding.
- */
-static void padding_remove( GString* data )
-{
- unsigned int padding;
-
- if ( data->len == 0 )
- return;
-
- padding = data->str[data->len - 1];
- g_string_truncate( data, data->len - padding );
-}
-
-
-/*------------------------------------------------------------------------
- * Generate the Transport-Layer crypto key.
- * (Note: this function is not-thread safe)
- *
- * @param session The MXit Session object
- * @return The transport-layer crypto key.
- */
-static char* transport_layer_key( struct MXitSession* session )
-{
- static char key[16 + 1];
- const char* password = purple_account_get_password( session->acc );
- int passlen = strlen( password );
-
- /* initialize with initial key */
- g_strlcpy( key, INITIAL_KEY, sizeof( key ) );
-
- /* client key (8 bytes) */
- memcpy( key, session->clientkey, strlen( session->clientkey ) );
-
- /* add last 8 characters of the PIN (no padding if less characters) */
- if ( passlen <= 8 )
- memcpy( key + 8, password, passlen );
- else
- memcpy( key + 8, password + ( passlen - 8 ), 8 );
-
- return key;
-}
-
-
-/*------------------------------------------------------------------------
- * Encrypt the user's cleartext password using the AES 128-bit (ECB)
- * encryption algorithm.
- *
- * @param session The MXit session object
- * @return The encrypted & encoded password. Must be g_free'd when no longer needed.
- */
-char* mxit_encrypt_password( struct MXitSession* session )
-{
- char key[16 + 1];
- char exkey[512];
- GString* pass = NULL;
- GString* encrypted = NULL;
- char* base64;
- unsigned int i;
-
- purple_debug_info( MXIT_PLUGIN_ID, "mxit_encrypt_password\n" );
-
- /* build the AES encryption key */
- g_strlcpy( key, INITIAL_KEY, sizeof( key ) );
- memcpy( key, session->clientkey, strlen( session->clientkey ) );
- ExpandKey( (unsigned char*) key, (unsigned char*) exkey );
-
- /* build the secret data to be encrypted: SECRET_HEADER + password */
- pass = g_string_new( SECRET_HEADER );
- g_string_append( pass, purple_account_get_password( session->acc) );
- padding_add( pass ); /* add ISO10126 padding */
-
- /* now encrypt the secret. we encrypt each block separately (ECB mode) */
- encrypted = g_string_sized_new( pass->len );
- for ( i = 0; i < pass->len; i += 16 ) {
- char block[16];
-
- Encrypt( (unsigned char*) pass->str + i, (unsigned char*) exkey, (unsigned char*) block );
- g_string_append_len( encrypted, block, 16 );
- }
-
- /* now base64 encode the encrypted password */
- base64 = purple_base64_encode( (unsigned char*) encrypted->str, encrypted->len );
- g_string_free( encrypted, TRUE );
-
- g_string_free( pass, TRUE );
-
- return base64;
-}
-
-
-/*------------------------------------------------------------------------
- * Decrypt a message using transport-layer encryption.
- *
- * @param session The MXit session object
- * @param message The encrypted message data (is base64-encoded).
- * @return The decrypted message. Must be g_free'd when no longer needed.
- */
-char* mxit_decrypt_message( struct MXitSession* session, char* message )
-{
- guchar* raw_message;
- gsize raw_len;
- char exkey[512];
- GString* decoded = NULL;
- unsigned int i;
-
- /* remove optional header: <mxitencrypted ver="5.2"/> */
- if ( strncmp( message, ENCRYPT_HEADER, strlen( ENCRYPT_HEADER ) ) == 0 )
- message += strlen( ENCRYPT_HEADER );
-
- /* base64 decode the message */
- raw_message = purple_base64_decode( message, &raw_len );
-
- /* AES-encrypted data is always blocks of 16 bytes */
- if ( ( raw_len == 0 ) || ( raw_len % 16 != 0 ) )
- return NULL;
-
- /* build the AES key */
- ExpandKey( (unsigned char*) transport_layer_key( session ), (unsigned char*) exkey );
-
- /* AES decrypt each block */
- decoded = g_string_sized_new( raw_len );
- for ( i = 0; i < raw_len; i += 16 ) {
- char block[16];
-
- Decrypt( (unsigned char*) raw_message + i, (unsigned char*) exkey, (unsigned char*) block );
- g_string_append_len( decoded, block, 16 );
- }
- g_free( raw_message );
-
- /* check that the decrypted message starts with header: <mxit/> */
- if ( strncmp( decoded->str, SECRET_HEADER, strlen( SECRET_HEADER ) != 0 ) ) {
- g_string_free( decoded, TRUE );
- return NULL; /* message could not be decrypted */
- }
-
- /* remove ISO10126 padding */
- padding_remove( decoded );
-
- /* remove encryption header */
- g_string_erase( decoded, 0, strlen( SECRET_HEADER ) );
-
- return g_string_free( decoded, FALSE );
-}
-
-
-/*------------------------------------------------------------------------
- * Encrypt a message using transport-layer encryption.
- *
- * @param session The MXit session object
- * @param message The message data.
- * @return The encrypted message. Must be g_free'd when no longer needed.
- */
-char* mxit_encrypt_message( struct MXitSession* session, char* message )
-{
- GString* raw_message = NULL;
- char exkey[512];
- GString* encoded = NULL;
- gchar* base64;
- unsigned int i;
-
- purple_debug_info( MXIT_PLUGIN_ID, "encrypt message: '%s'\n", message );
-
- /* append encryption header to message data */
- raw_message = g_string_new( SECRET_HEADER );
- g_string_append( raw_message, message );
- padding_add( raw_message ); /* add ISO10126 padding */
-
- /* build the AES key */
- ExpandKey( (unsigned char*) transport_layer_key( session ), (unsigned char*) exkey );
-
- /* AES encrypt each block */
- encoded = g_string_sized_new( raw_message->len );
- for ( i = 0; i < raw_message->len; i += 16 ) {
- char block[16];
-
- Encrypt( (unsigned char*) raw_message->str + i, (unsigned char*) exkey, (unsigned char*) block );
- g_string_append_len( encoded, block, 16 );
- }
- g_string_free( raw_message, TRUE );
-
- /* base64 encode the encrypted message */
- base64 = purple_base64_encode( (unsigned char *) encoded->str, encoded->len );
- g_string_free( encoded, TRUE );
-
- purple_debug_info( MXIT_PLUGIN_ID, "encrypted message: '%s'\n", base64 );
-
- return base64;
-}
diff --git a/libpurple/protocols/mxit/filexfer.c b/libpurple/protocols/mxit/filexfer.c
index 5eef9cb140..a842baff58 100644
--- a/libpurple/protocols/mxit/filexfer.c
+++ b/libpurple/protocols/mxit/filexfer.c
@@ -98,11 +98,11 @@ const char* file_mime_type( const char* filename, const char* buf, int buflen )
*/
static void mxit_xfer_free( PurpleXfer* xfer )
{
- struct mxitxfer* mx = (struct mxitxfer*) xfer->data;;
+ struct mxitxfer* mx = purple_xfer_get_protocol_data( xfer );
if ( mx ) {
+ purple_xfer_set_protocol_data( xfer, NULL );
g_free( mx );
- xfer->data = NULL;
}
}
@@ -118,16 +118,16 @@ static void mxit_xfer_free( PurpleXfer* xfer )
*/
static void mxit_xfer_init( PurpleXfer* xfer )
{
- struct mxitxfer* mx = (struct mxitxfer*) xfer->data;
+ struct mxitxfer* mx = purple_xfer_get_protocol_data( xfer );
purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_init\n" );
- if ( purple_xfer_get_type( xfer ) == PURPLE_XFER_SEND ) {
+ if ( purple_xfer_get_xfer_type( xfer ) == PURPLE_XFER_TYPE_SEND ) {
/* we are trying to send a file to MXit */
if ( purple_xfer_get_size( xfer ) > CP_MAX_FILESIZE ) {
/* the file is too big */
- purple_xfer_error( purple_xfer_get_type( xfer ), purple_xfer_get_account( xfer ), purple_xfer_get_remote_user( xfer ), _( "The file you are trying to send is too large!" ) );
+ purple_xfer_error( purple_xfer_get_xfer_type( xfer ), purple_xfer_get_account( xfer ), purple_xfer_get_remote_user( xfer ), _( "The file you are trying to send is too large!" ) );
purple_xfer_cancel_local( xfer );
return;
}
@@ -140,7 +140,7 @@ static void mxit_xfer_init( PurpleXfer* xfer )
* we have just accepted a file transfer request from MXit. send a confirmation
* to the MXit server so that can send us the file
*/
- mxit_send_file_accept( mx->session, mx->fileid, purple_xfer_get_size( xfer ), 0 );
+ mxit_send_file_accept( mx->session, mx->fileid, (int) purple_xfer_get_size( xfer ), 0 );
}
}
@@ -158,7 +158,7 @@ static void mxit_xfer_start( PurpleXfer* xfer )
purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_start\n" );
- if ( purple_xfer_get_type( xfer ) == PURPLE_XFER_SEND ) {
+ if ( purple_xfer_get_xfer_type( xfer ) == PURPLE_XFER_TYPE_SEND ) {
/*
* the user wants to send a file to one of his contacts. we need to create
* a buffer and copy the file data into memory and then we can send it to
@@ -167,14 +167,14 @@ static void mxit_xfer_start( PurpleXfer* xfer )
filesize = purple_xfer_get_bytes_remaining( xfer );
buffer = g_malloc( filesize );
- if (fread(buffer, filesize, 1, xfer->dest_fp) == 1) {
+ if (purple_xfer_read_file(xfer, buffer, filesize) == filesize) {
/* send data */
wrote = purple_xfer_write( xfer, buffer, filesize );
if ( wrote > 0 )
purple_xfer_set_bytes_sent( xfer, wrote );
} else {
/* file read error */
- purple_xfer_error( purple_xfer_get_type( xfer ), purple_xfer_get_account( xfer ), purple_xfer_get_remote_user( xfer ), _( "Unable to access the local file" ) );
+ purple_xfer_error( purple_xfer_get_xfer_type( xfer ), purple_xfer_get_account( xfer ), purple_xfer_get_remote_user( xfer ), _( "Unable to access the local file" ) );
purple_xfer_cancel_local( xfer );
}
@@ -223,7 +223,7 @@ static void mxit_xfer_cancel_send( PurpleXfer* xfer )
*/
static gssize mxit_xfer_write( const guchar* buffer, size_t size, PurpleXfer* xfer )
{
- struct mxitxfer* mx = (struct mxitxfer*) xfer->data;
+ struct mxitxfer* mx = purple_xfer_get_protocol_data( xfer );
purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_write\n" );
@@ -231,7 +231,7 @@ static gssize mxit_xfer_write( const guchar* buffer, size_t size, PurpleXfer* xf
purple_debug_warning( MXIT_PLUGIN_ID, "mxit_xfer_write: invalid internal mxit xfer data\n" );
return -1;
}
- else if ( purple_xfer_get_type( xfer ) != PURPLE_XFER_SEND ) {
+ else if ( purple_xfer_get_xfer_type( xfer ) != PURPLE_XFER_TYPE_SEND ) {
purple_debug_warning( MXIT_PLUGIN_ID, "mxit_xfer_write: wrong xfer type received\n" );
return -1;
}
@@ -253,7 +253,7 @@ static gssize mxit_xfer_write( const guchar* buffer, size_t size, PurpleXfer* xf
*/
static void mxit_xfer_request_denied( PurpleXfer* xfer )
{
- struct mxitxfer* mx = (struct mxitxfer*) xfer->data;
+ struct mxitxfer* mx = purple_xfer_get_protocol_data( xfer );
purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_request_denied\n" );
@@ -307,15 +307,15 @@ PurpleXfer* mxit_xfer_new( PurpleConnection* gc, const char* who )
PurpleXfer* xfer = NULL;
struct mxitxfer* mx = NULL;
- /* (reference: "libpurple/ft.h") */
- xfer = purple_xfer_new( session->acc, PURPLE_XFER_SEND, who );
+ /* (reference: "libpurple/xfer.h") */
+ xfer = purple_xfer_new( session->acc, PURPLE_XFER_TYPE_SEND, who );
/* create file info and attach it to the file transfer */
mx = g_new0( struct mxitxfer, 1 );
mx->session = session;
- xfer->data = mx;
+ purple_xfer_set_protocol_data( xfer, mx );
- /* configure callbacks (reference: "libpurple/ft.h") */
+ /* configure callbacks (reference: "libpurple/xfer.h") */
purple_xfer_set_init_fnc( xfer, mxit_xfer_init );
purple_xfer_set_start_fnc( xfer, mxit_xfer_start );
purple_xfer_set_end_fnc( xfer, mxit_xfer_end );
@@ -364,13 +364,13 @@ void mxit_xfer_rx_offer( struct MXitSession* session, const char* username, cons
purple_debug_info( MXIT_PLUGIN_ID, "File Offer: file=%s, from=%s, size=%i\n", filename, username, filesize );
- xfer = purple_xfer_new( session->acc, PURPLE_XFER_RECEIVE, username );
+ xfer = purple_xfer_new( session->acc, PURPLE_XFER_TYPE_RECEIVE, username );
if ( xfer ) {
/* create a new mxit xfer struct for internal use */
mx = g_new0( struct mxitxfer, 1 );
mx->session = session;
memcpy( mx->fileid, fileid, MXIT_CHUNK_FILEID_LEN );
- xfer->data = mx;
+ purple_xfer_set_protocol_data( xfer, mx );
purple_xfer_set_filename( xfer, filename );
if( filesize > 0 )
@@ -405,7 +405,7 @@ static PurpleXfer* find_mxit_xfer( struct MXitSession* session, const char* file
if ( purple_xfer_get_account( xfer ) == session->acc ) {
/* transfer is associated with this MXit account */
- struct mxitxfer* mx = xfer->data;
+ struct mxitxfer* mx = purple_xfer_get_protocol_data( xfer );
/* does the fileid match? */
if ( ( mx ) && ( memcmp( mx->fileid, fileid, MXIT_CHUNK_FILEID_LEN ) == 0 ) )
@@ -439,11 +439,11 @@ void mxit_xfer_rx_file( struct MXitSession* session, const char* fileid, const c
xfer = find_mxit_xfer( session, fileid );
if ( xfer ) {
/* this is the transfer we have been looking for */
- purple_xfer_ref( xfer );
+ g_object_ref( xfer );
purple_xfer_start( xfer, -1, NULL, 0 );
- if ( fwrite( data, datalen, 1, xfer->dest_fp ) > 0 ) {
- purple_xfer_unref( xfer );
+ if ( purple_xfer_write_file( xfer, (const guchar *)data, datalen ) ) {
+ g_object_unref( xfer );
purple_xfer_set_completed( xfer, TRUE );
purple_xfer_end( xfer );
@@ -452,7 +452,7 @@ void mxit_xfer_rx_file( struct MXitSession* session, const char* fileid, const c
}
else {
/* file write error */
- purple_xfer_error( purple_xfer_get_type( xfer ), purple_xfer_get_account( xfer ), purple_xfer_get_remote_user( xfer ), _( "Unable to save the file" ) );
+ purple_xfer_error( purple_xfer_get_xfer_type( xfer ), purple_xfer_get_account( xfer ), purple_xfer_get_remote_user( xfer ), _( "Unable to save the file" ) );
purple_xfer_cancel_local( xfer );
}
}
diff --git a/libpurple/protocols/mxit/formcmds.c b/libpurple/protocols/mxit/formcmds.c
index 0e60a6a02a..cd8f0ead83 100644
--- a/libpurple/protocols/mxit/formcmds.c
+++ b/libpurple/protocols/mxit/formcmds.c
@@ -26,6 +26,8 @@
#include "internal.h"
#include "debug.h"
+#include "http.h"
+#include "image-store.h"
#include "protocol.h"
#include "mxit.h"
@@ -74,42 +76,36 @@ struct ii_url_request
/*------------------------------------------------------------------------
* Callback function invoked when an inline image request to a web site completes.
- *
- * @param url_data
- * @param user_data The Markup message object
- * @param url_text The data returned from the WAP site
- * @param len The length of the data returned
- * @param error_message Descriptive error message
*/
-static void mxit_cb_ii_returned(PurpleUtilFetchUrlData* url_data, gpointer user_data, const gchar* url_text, gsize len, const gchar* error_message)
+static void
+mxit_cb_ii_returned(PurpleHttpConnection *http_conn, PurpleHttpResponse *response,
+ gpointer _iireq)
{
- struct ii_url_request* iireq = (struct ii_url_request*) user_data;
- int* intptr = NULL;
- int id;
+ struct ii_url_request* iireq = _iireq;
+ PurpleImage *img;
+ const gchar* data;
+ size_t len;
#ifdef MXIT_DEBUG_COMMANDS
purple_debug_info(MXIT_PLUGIN_ID, "Inline Image returned from %s\n", iireq->url);
#endif
- if (!url_text) {
+ if (!purple_http_response_is_successful(response)) {
/* no reply from the WAP site */
purple_debug_error(MXIT_PLUGIN_ID, "Error downloading Inline Image from %s.\n", iireq->url);
goto done;
}
/* lets first see if we don't have the inline image already in cache */
- if (g_hash_table_lookup(iireq->mx->session->iimages, iireq->url)) {
+ if (g_hash_table_lookup(iireq->mx->session->inline_images, iireq->url)) {
/* inline image found in the cache, so we just ignore this reply */
goto done;
}
/* we now have the inline image, store a copy in the imagestore */
- id = purple_imgstore_add_with_id(g_memdup(url_text, len), len, NULL);
-
- /* map the inline image id to purple image id */
- intptr = g_malloc(sizeof(int));
- *intptr = id;
- g_hash_table_insert(iireq->mx->session->iimages, iireq->url, intptr);
+ data = purple_http_response_get_data(response, &len);
+ img = purple_image_new_from_data(g_memdup(data, len), len);
+ g_hash_table_insert(iireq->mx->session->inline_images, iireq->url, img);
iireq->mx->flags |= PURPLE_MESSAGE_IMAGES;
@@ -227,11 +223,11 @@ static GHashTable* command_tokenize(char* cmd)
*/
static void command_clear(struct MXitSession* session, const char* from, GHashTable* hash)
{
- PurpleConversation *conv;
+ PurpleIMConversation *im;
char* clearmsgscreen;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, from, session->acc);
- if (conv == NULL) {
+ im = purple_conversations_find_im_with_account(from, session->acc);
+ if (im == NULL) {
purple_debug_error(MXIT_PLUGIN_ID, _( "Conversation with '%s' not found\n" ), from);
return;
}
@@ -239,7 +235,7 @@ static void command_clear(struct MXitSession* session, const char* from, GHashTa
clearmsgscreen = g_hash_table_lookup(hash, "clearmsgscreen");
if ( (clearmsgscreen) && (strcmp(clearmsgscreen, "true") == 0) ) {
/* this is a command to clear the chat screen */
- purple_conversation_clear_message_history(conv);
+ purple_conversation_clear_message_history(PURPLE_CONVERSATION(im));
}
}
@@ -326,14 +322,18 @@ static void command_image(struct RXMsgData* mx, GHashTable* hash, GString* msg)
const char* reply;
guchar* rawimg;
gsize rawimglen;
- int imgid;
img = g_hash_table_lookup(hash, "dat");
if (img) {
+ PurpleImage *pimg;
+ guint pimg_id;
+
rawimg = purple_base64_decode(img, &rawimglen);
//purple_util_write_data_to_file_absolute("/tmp/mxitinline.png", (char*) rawimg, rawimglen);
- imgid = purple_imgstore_add_with_id(rawimg, rawimglen, NULL);
- g_string_append_printf(msg, "<img id=\"%i\">", imgid);
+ pimg = purple_image_new_from_data(rawimg, rawimglen);
+ pimg_id = purple_image_store_add(pimg);
+ g_string_append_printf(msg, "<img src=\""
+ PURPLE_IMAGE_STORE_PROTOCOL "%u\">", pimg_id);
mx->flags |= PURPLE_MESSAGE_IMAGES;
}
else {
@@ -349,7 +349,7 @@ static void command_image(struct RXMsgData* mx, GHashTable* hash, GString* msg)
mx->got_img = TRUE;
/* lets first see if we don't have the inline image already in cache */
- if (g_hash_table_lookup(mx->session->iimages, iireq->url)) {
+ if (g_hash_table_lookup(mx->session->inline_images, iireq->url)) {
/* inline image found in the cache, so we do not have to request it from the web */
g_free(iireq);
}
@@ -357,8 +357,7 @@ static void command_image(struct RXMsgData* mx, GHashTable* hash, GString* msg)
/* send the request for the inline image */
purple_debug_info(MXIT_PLUGIN_ID, "sending request for inline image '%s'\n", iireq->url);
- /* request the image (reference: "libpurple/util.h") */
- purple_util_fetch_url_request(iireq->url, TRUE, NULL, TRUE, NULL, FALSE, mxit_cb_ii_returned, iireq);
+ purple_http_get(mx->session->con, mxit_cb_ii_returned, iireq, iireq->url);
mx->img_count++;
}
}
diff --git a/libpurple/protocols/mxit/http.c b/libpurple/protocols/mxit/http.c
deleted file mode 100644
index 9a91775366..0000000000
--- a/libpurple/protocols/mxit/http.c
+++ /dev/null
@@ -1,341 +0,0 @@
-/*
- * MXit Protocol libPurple Plugin
- *
- * -- MXit client protocol implementation --
- *
- * Pieter Loubser <libpurple@mxit.com>
- *
- * (C) Copyright 2009 MXit Lifestyle (Pty) Ltd.
- * <http://www.mxitlifestyle.com>
- *
- * 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 "mxit.h"
-#include "protocol.h"
-#include "http.h"
-
-
-/* HTTP constants */
-#define HTTP_11_200_OK "HTTP/1.1 200 OK\r\n"
-#define HTTP_11_100_CONT "HTTP/1.1 100 Continue\r\n"
-#define HTTP_11_SEPERATOR "\r\n\r\n"
-#define HTTP_CONTENT_LEN "Content-Length: "
-
-
-/* define to enable HTTP debugging */
-#define DEBUG_HTTP
-
-
-/*------------------------------------------------------------------------
- * This will freeup the memory used by a HTTP request structure
- *
- * @param req The HTTP structure's resources should be freed up
- */
-static void free_http_request( struct http_request* req )
-{
- g_free( req->host );
- g_free( req->data );
- g_free( req );
-}
-
-
-/*------------------------------------------------------------------------
- * Write the request to the HTTP server.
- *
- * @param fd The file descriptor
- * @param pktdata The packet data
- * @param pktlen The length of the packet data
- * @return Return -1 on error, otherwise 0
- */
-static int mxit_http_raw_write( int fd, const char* pktdata, int pktlen )
-{
- int written;
- int res;
-
- written = 0;
- while ( written < pktlen ) {
- res = write( fd, &pktdata[written], pktlen - written );
- if ( res <= 0 ) {
- /* error on socket */
- if ( errno == EAGAIN )
- continue;
-
- purple_debug_error( MXIT_PLUGIN_ID, "Error while writing packet to HTTP server (%i)\n", res );
- return -1;
- }
- written += res;
- }
-
- return 0;
-}
-
-
-/*------------------------------------------------------------------------
- * Callback when data is received from the HTTP server.
- *
- * @param user_data The MXit session object
- * @param source The file-descriptor on which data was received
- * @param cond Condition which caused the callback (PURPLE_INPUT_READ)
- */
-static void mxit_cb_http_read( gpointer user_data, gint source, PurpleInputCondition cond )
-{
- struct MXitSession* session = (struct MXitSession*) user_data;
- char buf[256];
- int buflen;
- char* body;
- int bodylen;
- char* ch;
- int len;
- char* tmp;
- int res;
-#if 0
- char* next;
-#endif
-
- purple_debug_info( MXIT_PLUGIN_ID, "mxit_cb_http_read\n" );
-
- if ( session->rx_state == RX_STATE_RLEN ) {
- /* we are reading in the HTTP headers */
-
- /* copy partial headers if we have any part saved */
- memcpy( buf, session->rx_dbuf, session->rx_i );
- buflen = session->rx_i;
-
- /* read bytes from the socket */
- len = read( session->fd, buf + buflen, sizeof( buf ) - ( buflen + 1 ) );
- if ( len <= 0 ) {
- /* connection has been terminated, or error occurred */
- goto done;
- }
- buf[buflen+len] = '\0';
-
-#if 0
-nextpacket:
-#endif
-
-#ifdef DEBUG_HTTP
- purple_debug_info( MXIT_PLUGIN_ID, "HTTP POST READ 1: (%i)\n", len );
- dump_bytes( session, buf + buflen, len );
-#endif
-
- /* see if we have all the HTTP headers yet */
- ch = strstr( buf, HTTP_11_SEPERATOR );
- if ( !ch ) {
- /* we need to wait for more input, so save what we have */
- session->rx_i = buflen + len;
- memcpy( session->rx_dbuf, buf, session->rx_i );
- return;
- }
- buflen += len;
-
- /* we have the header's end now skip over the http separator to get the body offset */
- ch += strlen( HTTP_11_SEPERATOR );
- *(ch - 1) = '\0';
- body = ch;
-
- res = buflen - ( ch - buf );
- if ( res > 0 ) {
- /* we read more bytes than just the header so copy it over */
- memcpy( session->rx_dbuf, ch, res );
- session->rx_i = res;
- }
- else {
- session->rx_i = 0;
- }
-
- /* test for a good response */
- if ( ( strncmp( buf, HTTP_11_200_OK, strlen( HTTP_11_200_OK ) ) != 0 ) && ( strncmp( buf, HTTP_11_100_CONT, strlen( HTTP_11_100_CONT ) ) != 0 ) ) {
- /* bad result */
- purple_debug_error( MXIT_PLUGIN_ID, "HTTP error: %s\n", ch );
- goto done;
- }
-
- /* find the content-length */
- ch = (char*) purple_strcasestr( buf, HTTP_CONTENT_LEN );
- if ( !ch ) {
- /* bad request. it does not contain a content-length header */
- purple_debug_error( MXIT_PLUGIN_ID, "HTTP reply received without content-length header (ignoring packet)\n" );
- goto done;
- }
-
- /* parse the content-length */
- ch += strlen( HTTP_CONTENT_LEN );
- tmp = strchr( ch, '\r' );
- if ( !tmp ) {
- purple_debug_error( MXIT_PLUGIN_ID, "Received bad HTTP reply packet (ignoring packet)\n" );
- goto done;
- }
- tmp = g_strndup( ch, tmp - ch );
- bodylen = atoi( tmp );
- g_free( tmp );
- tmp = NULL;
-
- if ( buflen + bodylen >= CP_MAX_PACKET ) {
- /* this packet is way to big */
- goto done;
- }
- else if ( buflen > ( ( body - buf ) + bodylen ) ) {
- /* we have a second packet here */
-#if 0
- next = body + bodylen;
-#endif
- session->rx_res = 0;
- }
- else {
- session->rx_res = bodylen - session->rx_i;
- }
-
- if ( session->rx_res == 0 ) {
- /* we have read all the data */
- session->rx_i = bodylen;
- session->rx_state = RX_STATE_PROC;
- }
- else {
- /* there is still some data outstanding */
- session->rx_state = RX_STATE_DATA;
- }
- }
- else if ( session->rx_state == RX_STATE_DATA ) {
- /* we are reading the HTTP content (body) */
-
- /* read bytes from the socket */
- len = read( session->fd, &session->rx_dbuf[session->rx_i], session->rx_res );
- if ( len <= 0 ) {
- /* connection has been terminated, or error occurred */
- goto done;
- }
-
-#ifdef DEBUG_HTTP
- purple_debug_info( MXIT_PLUGIN_ID, "HTTP POST READ 2: (%i)\n", len );
- dump_bytes( session, &session->rx_dbuf[session->rx_i], len );
-#endif
- session->rx_i += len;
- session->rx_res -= len;
-
- if ( session->rx_res == 0 ) {
- /* ok, so now we have read in the whole packet */
- session->rx_state = RX_STATE_PROC;
- }
- }
-
- if ( session->rx_state == RX_STATE_PROC ) {
- mxit_parse_packet( session );
-
-#if 0
- if ( next ) {
- /* there is another packet of which we read some data */
-
- /* reset input */
- session->rx_state = RX_STATE_RLEN;
- session->rx_lbuf[0] = '\0';
- session->rx_i = 0;
- session->rx_res = 0;
-
- /* move read data */
- len = next - buf;
- buflen = len;
- memcpy( buf, next, len );
- goto nextpacket;
- }
-#endif
-
- /* we are done */
- goto done;
- }
-
- return;
-done:
- close( session->fd );
- purple_input_remove( session->http_handler );
- session->http_handler = 0;
-}
-
-
-/*------------------------------------------------------------------------
- * Callback invoked once the connection has been established to the HTTP server,
- * or on connection failure.
- *
- * @param user_data The MXit session object
- * @param source The file-descriptor associated with the connection
- * @param error_message Message explaining why the connection failed
- */
-static void mxit_cb_http_connect( gpointer user_data, gint source, const gchar* error_message )
-{
- struct http_request* req = (struct http_request*) user_data;
-
- purple_debug_info( MXIT_PLUGIN_ID, "mxit_cb_http_connect\n" );
-
- /* source is the file descriptor of the new connection */
- if ( source < 0 ) {
- purple_debug_info( MXIT_PLUGIN_ID, "mxit_cb_http_connect failed: %s\n", error_message );
- purple_connection_error( req->session->con, _( "Unable to connect to the MXit HTTP server. Please check your server settings." ) );
- return;
- }
-
- /* we now have an open and active TCP connection to the mxit server */
- req->session->fd = source;
-
- /* reset the receive buffer */
- req->session->rx_state = RX_STATE_RLEN;
- req->session->rx_lbuf[0] = '\0';
- req->session->rx_i = 0;
- req->session->rx_res = 0;
-
- /* start listening on the open connection for messages from the server (reference: "libpurple/eventloop.h") */
- req->session->http_handler = purple_input_add( req->session->fd, PURPLE_INPUT_READ, mxit_cb_http_read, req->session );
-
- /* actually send the request to the HTTP server */
- mxit_http_raw_write( req->session->fd, req->data, req->datalen );
-
- /* free up resources */
- free_http_request( req );
- req = NULL;
-}
-
-
-/*------------------------------------------------------------------------
- * Create HTTP connection for sending a HTTP request
- *
- * @param session The MXit session object
- * @param host The server name to connect to
- * @param port The port number to connect to
- * @param data The HTTP request data (including HTTP headers etc.)
- * @param datalen The HTTP request data length
- */
-void mxit_http_send_request( struct MXitSession* session, char* host, int port, const char* data, int datalen )
-{
- PurpleProxyConnectData* con = NULL;
- struct http_request* req;
-
- /* build the http request */
- req = g_new0( struct http_request, 1 );
- req->session = session;
- req->host = host;
- req->port = port;
- req->data = g_malloc0( datalen );
- memcpy( req->data, data, datalen );
- req->datalen = datalen;
-
- /* open connection to the HTTP server */
- con = purple_proxy_connect( NULL, session->acc, host, port, mxit_cb_http_connect, req );
- if ( !con ) {
- purple_connection_error_reason( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Unable to connect" ) );
- }
-}
-
diff --git a/libpurple/protocols/mxit/login.c b/libpurple/protocols/mxit/login.c
index 239715438d..1259974875 100644
--- a/libpurple/protocols/mxit/login.c
+++ b/libpurple/protocols/mxit/login.c
@@ -25,12 +25,13 @@
#include "internal.h"
#include "debug.h"
+#include "http.h"
#include "request.h"
#include "version.h"
#include "protocol.h"
#include "mxit.h"
-#include "cipher.h"
+#include "cipher-mxit.h"
#include "login.h"
#include "profile.h"
@@ -71,7 +72,12 @@ static struct MXitSession* mxit_create_object( PurpleAccount* account )
/* configure the connection (reference: "libpurple/connection.h") */
purple_connection_set_protocol_data( con, session );
- con->flags |= PURPLE_CONNECTION_NO_BGCOLOR | PURPLE_CONNECTION_NO_URLDESC | PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_SUPPORT_MOODS;
+ purple_connection_set_flags( con,
+ PURPLE_CONNECTION_FLAG_NO_BGCOLOR
+ | PURPLE_CONNECTION_FLAG_NO_URLDESC
+ | PURPLE_CONNECTION_FLAG_HTML
+ | PURPLE_CONNECTION_FLAG_SUPPORT_MOODS
+ );
/* configure the session (reference: "libpurple/account.h") */
g_strlcpy( session->server, purple_account_get_string( account, MXIT_CONFIG_SERVER_ADDR, DEFAULT_SERVER ), sizeof( session->server ) );
@@ -81,10 +87,11 @@ static struct MXitSession* mxit_create_object( PurpleAccount* account )
g_strlcpy( session->clientkey, purple_account_get_string( account, MXIT_CONFIG_CLIENTKEY, "" ), sizeof( session->clientkey ) );
g_strlcpy( session->dialcode, purple_account_get_string( account, MXIT_CONFIG_DIALCODE, "" ), sizeof( session->dialcode ) );
session->http = purple_account_get_bool( account, MXIT_CONFIG_USE_HTTP, FALSE );
- session->iimages = g_hash_table_new( g_str_hash, g_str_equal );
+ session->inline_images = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref);
session->rx_state = RX_STATE_RLEN;
session->http_interval = MXIT_HTTP_POLL_MIN;
session->http_last_poll = mxit_now_milli();
+ session->async_http_reqs = purple_http_connection_set_new();
return session;
}
@@ -165,7 +172,7 @@ static void mxit_cb_connect( gpointer user_data, gint source, const gchar* error
/* source is the file descriptor of the new connection */
if ( source < 0 ) {
purple_debug_info( MXIT_PLUGIN_ID, "mxit_cb_connect failed: %s\n", error_message );
- purple_connection_error( session->con, _( "Unable to connect to the MXit server. Please check your server settings." ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Unable to connect to the MXit server. Please check your server settings." ) );
return;
}
@@ -173,7 +180,7 @@ static void mxit_cb_connect( gpointer user_data, gint source, const gchar* error
session->fd = source;
/* start listening on the open connection for messages from the server (reference: "libpurple/eventloop.h") */
- session->con->inpa = purple_input_add( session->fd, PURPLE_INPUT_READ, mxit_cb_rx, session );
+ session->inpa = purple_input_add( session->fd, PURPLE_INPUT_READ, mxit_cb_rx, session );
mxit_connected( session );
}
@@ -202,7 +209,7 @@ static void mxit_login_connect( struct MXitSession* session )
/* socket connection */
data = purple_proxy_connect( session->con, session->acc, session->server, session->port, mxit_cb_connect, session );
if ( !data ) {
- purple_connection_error( session->con, _( "Unable to connect to the MXit server. Please check your server settings." ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Unable to connect to the MXit server. Please check your server settings." ) );
return;
}
}
@@ -231,10 +238,7 @@ static void mxit_cb_register_ok( PurpleConnection *gc, PurpleRequestFields *fiel
purple_debug_info( MXIT_PLUGIN_ID, "mxit_cb_register_ok\n" );
- if ( !PURPLE_CONNECTION_IS_VALID( gc ) ) {
- purple_debug_error( MXIT_PLUGIN_ID, "Unable to register; account offline.\n" );
- return;
- }
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
/* nickname */
str = purple_request_fields_get_string( fields, "nickname" );
@@ -281,7 +285,7 @@ static void mxit_cb_register_ok( PurpleConnection *gc, PurpleRequestFields *fiel
out:
if ( !err ) {
- purple_account_set_password( session->acc, session->profile->pin );
+ purple_account_set_password( session->acc, session->profile->pin, NULL, NULL );
mxit_login_connect( session );
}
else {
@@ -331,7 +335,7 @@ static void mxit_register_view( struct MXitSession* session )
/* mxit login name */
field = purple_request_field_string_new( "loginname", _( "MXit ID" ), purple_account_get_username( session->acc ), FALSE );
- purple_request_field_string_set_editable( field, FALSE );
+ purple_request_field_set_sensitive( field, FALSE );
purple_request_field_group_add_field( group, field );
/* nick name (required) */
@@ -346,9 +350,9 @@ static void mxit_register_view( struct MXitSession* session )
purple_request_field_group_add_field( group, field );
/* gender */
- field = purple_request_field_choice_new( "male", _( "Gender" ), ( profile->male ) ? 1 : 0 );
- purple_request_field_choice_add( field, _( "Female" ) ); /* 0 */
- purple_request_field_choice_add( field, _( "Male" ) ); /* 1 */
+ field = purple_request_field_choice_new( "male", _( "Gender" ), GINT_TO_POINTER(profile->male ? 1 : 0) );
+ purple_request_field_choice_add( field, _( "Female" ), GINT_TO_POINTER(0) );
+ purple_request_field_choice_add( field, _( "Male" ), GINT_TO_POINTER(1) );
purple_request_field_group_add_field( group, field );
/* pin (required) */
@@ -362,48 +366,51 @@ static void mxit_register_view( struct MXitSession* session )
purple_request_field_group_add_field( group, field );
/* show the form to the user to complete */
- purple_request_fields( session->con, _( "Register New MXit Account" ), _( "Register New MXit Account" ), _( "Please fill in the following fields:" ), fields, _( "OK" ), G_CALLBACK( mxit_cb_register_ok ), _( "Cancel" ), G_CALLBACK( mxit_cb_register_cancel ), session->acc, NULL, NULL, session->con );
+ purple_request_fields(session->con, _("Register New MXit Account"),
+ _("Register New MXit Account"),
+ _("Please fill in the following fields:"), fields, _("OK"),
+ G_CALLBACK(mxit_cb_register_ok), _("Cancel"),
+ G_CALLBACK(mxit_cb_register_cancel),
+ purple_request_cpar_from_account(session->acc), session->con);
}
-/*------------------------------------------------------------------------
- * Callback function invoked once the Authorization information has been submitted
- * to the MXit WAP site.
+/**
+ * Callback function invoked once the Authorization information has been
+ * submitted to the MXit WAP site.
*
- * @param url_data libPurple internal object (see purple_util_fetch_url_request)
- * @param user_data The MXit session object
- * @param url_text The data returned from the WAP site
- * @param len The length of the data returned
- * @param error_message Descriptive error message
+ * @param http_conn See http.h.
+ * @param response See http.h.
+ * @param _session The mxit session.
*/
-static void mxit_cb_clientinfo2( PurpleUtilFetchUrlData* url_data, gpointer user_data, const gchar* url_text, gsize len, const gchar* error_message )
+static void
+mxit_cb_clientinfo2(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _session)
{
- struct MXitSession* session = (struct MXitSession*) user_data;
+ struct MXitSession *session = _session;
gchar** parts;
gchar** host;
int state;
purple_debug_info( MXIT_PLUGIN_ID, "mxit_clientinfo_cb2\n" );
-#ifdef DEBUG_PROTOCOL
- purple_debug_info( MXIT_PLUGIN_ID, "HTTP RESPONSE: '%s'\n", url_text );
-#endif
-
- /* remove request from the async outstanding calls list */
- session->async_calls = g_slist_remove( session->async_calls, url_data );
-
- if ( !url_text ) {
+ if (!purple_http_response_is_successful(response)) {
/* no reply from the WAP site */
- purple_connection_error( session->con, _( "Error contacting the MXit WAP site. Please try again later." ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Error contacting the MXit WAP site. Please try again later." ) );
return;
}
+#ifdef DEBUG_PROTOCOL
+ purple_debug_info(MXIT_PLUGIN_ID, "HTTP RESPONSE: '%s'\n",
+ purple_http_response_get_data(response, NULL));
+#endif
+
/* explode the response from the WAP site into an array */
- parts = g_strsplit( url_text, ";", 15 );
+ parts = g_strsplit(purple_http_response_get_data(response, NULL), ";", 15);
if ( !parts ) {
/* wapserver error */
- purple_connection_error( session->con, _( "MXit is currently unable to process the request. Please try again later." ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "MXit is currently unable to process the request. Please try again later." ) );
return;
}
@@ -413,26 +420,26 @@ static void mxit_cb_clientinfo2( PurpleUtilFetchUrlData* url_data, gpointer user
/* valid reply! */
break;
case '1' :
- purple_connection_error( session->con, _( "Wrong security code entered. Please try again later." ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Wrong security code entered. Please try again later." ) );
return;
case '2' :
- purple_connection_error( session->con, _( "Your session has expired. Please try again later." ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Your session has expired. Please try again later." ) );
return;
case '5' :
- purple_connection_error( session->con, _( "Invalid country selected. Please try again." ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Invalid country selected. Please try again." ) );
return;
case '6' :
- purple_connection_error( session->con, _( "The MXit ID you entered is not registered. Please register first." ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "The MXit ID you entered is not registered. Please register first." ) );
return;
case '7' :
- purple_connection_error( session->con, _( "The MXit ID you entered is already registered. Please choose another." ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "The MXit ID you entered is already registered. Please choose another." ) );
/* this user's account already exists, so we need to change the registration login flag to be login */
purple_account_set_int( session->acc, MXIT_CONFIG_STATE, MXIT_STATE_LOGIN );
return;
case '3' :
case '4' :
default :
- purple_connection_error( session->con, _( "Internal error. Please try again later." ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Internal error. Please try again later." ) );
return;
}
@@ -510,13 +517,12 @@ static void free_logindata( struct login_data* data )
*/
static void mxit_cb_captcha_ok( PurpleConnection* gc, PurpleRequestFields* fields )
{
+ PurpleHttpRequest *req;
struct MXitSession* session = purple_connection_get_protocol_data( gc );
- PurpleUtilFetchUrlData* url_data;
PurpleRequestField* field;
const char* captcha_resp;
GList* entries;
GList* entry;
- char* url;
int state;
/* get the captcha response */
@@ -550,31 +556,24 @@ static void mxit_cb_captcha_ok( PurpleConnection* gc, PurpleRequestFields* field
/* get state */
state = purple_account_get_int( session->acc, MXIT_CONFIG_STATE, MXIT_STATE_LOGIN );
- url = g_strdup_printf( "%s?type=getpid&sessionid=%s&login=%s&ver=%i.%i.%i&clientid=%s&cat=%s&chalresp=%s&cc=%s&loc=%s&path=%i&brand=%s&model=%s&h=%i&w=%i&ts=%li",
- session->logindata->wapserver,
- session->logindata->sessionid,
- purple_url_encode( purple_account_get_username( session->acc ) ),
- PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_MICRO_VERSION,
- MXIT_CLIENT_ID,
- MXIT_CP_ARCH,
- captcha_resp,
- session->logindata->cc,
- session->logindata->locale,
- ( state == MXIT_STATE_REGISTER1 ) ? 0 : 1,
- MXIT_CP_PLATFORM,
- MXIT_CP_OS,
- MXIT_CAPTCHA_HEIGHT,
- MXIT_CAPTCHA_WIDTH,
- time( NULL )
- );
- url_data = purple_util_fetch_url_request( url, TRUE, MXIT_HTTP_USERAGENT, TRUE, NULL, FALSE, mxit_cb_clientinfo2, session );
- if ( url_data )
- session->async_calls = g_slist_prepend( session->async_calls, url_data );
-
-#ifdef DEBUG_PROTOCOL
- purple_debug_info( MXIT_PLUGIN_ID, "HTTP REQUEST: '%s'\n", url );
-#endif
- g_free( url );
+ req = purple_http_request_new(NULL);
+ purple_http_request_set_url_printf(req, "%s?type=getpid&sessionid=%s&"
+ "login=%s&ver=%i.%i.%i&clientid=%s&cat=%s&chalresp=%s&cc=%s&"
+ "loc=%s&path=%i&brand=%s&model=%s&h=%i&w=%i&ts=%li",
+ session->logindata->wapserver, session->logindata->sessionid,
+ purple_url_encode(purple_account_get_username(session->acc)),
+ PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION,
+ PURPLE_MICRO_VERSION, MXIT_CLIENT_ID, MXIT_CP_ARCH,
+ captcha_resp, session->logindata->cc,
+ session->logindata->locale,
+ (state == MXIT_STATE_REGISTER1) ? 0 : 1, MXIT_CP_PLATFORM,
+ MXIT_CP_OS, MXIT_CAPTCHA_HEIGHT, MXIT_CAPTCHA_WIDTH,
+ time(NULL));
+ purple_http_request_header_set(req, "User-Agent", MXIT_HTTP_USERAGENT);
+ purple_http_connection_set_add(session->async_http_reqs,
+ purple_http_request(session->con, req, mxit_cb_clientinfo2,
+ session));
+ purple_http_request_unref(req);
/* free up the login resources */
free_logindata( session->logindata );
@@ -599,19 +598,20 @@ static void mxit_cb_captcha_cancel( PurpleConnection* gc, PurpleRequestFields* f
}
-/*------------------------------------------------------------------------
+/**
* Callback function invoked once the client information has been retrieved from
- * the MXit WAP site. Display page where user can select their authorization information.
+ * the MXit WAP site. Display page where user can select their authorization
+ * information.
*
- * @param url_data libPurple internal object (see purple_util_fetch_url_request)
- * @param user_data The MXit session object
- * @param url_text The data returned from the WAP site
- * @param len The length of the data returned
- * @param error_message Descriptive error message
+ * @param http_conn See http.h.
+ * @param response See http.h.
+ * @param _session The mxit session data.
*/
-static void mxit_cb_clientinfo1( PurpleUtilFetchUrlData* url_data, gpointer user_data, const gchar* url_text, gsize len, const gchar* error_message )
+static void
+mxit_cb_clientinfo1(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _session)
{
- struct MXitSession* session = (struct MXitSession*) user_data;
+ struct MXitSession *session = _session;
struct login_data* logindata;
PurpleRequestFields* fields;
PurpleRequestFieldGroup* group = NULL;
@@ -624,24 +624,22 @@ static void mxit_cb_clientinfo1( PurpleUtilFetchUrlData* url_data, gpointer user
purple_debug_info( MXIT_PLUGIN_ID, "mxit_clientinfo_cb1\n" );
#ifdef DEBUG_PROTOCOL
- purple_debug_info( MXIT_PLUGIN_ID, "RESPONSE: %s\n", url_text );
+ purple_debug_info( MXIT_PLUGIN_ID, "RESPONSE: %s\n",
+ purple_http_response_get_data(response, NULL));
#endif
- /* remove request from the async outstanding calls list */
- session->async_calls = g_slist_remove( session->async_calls, url_data );
-
- if ( !url_text ) {
+ if (!purple_http_response_is_successful(response)) {
/* no reply from the WAP site */
- purple_connection_error( session->con, _( "Error contacting the MXit WAP site. Please try again later." ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Error contacting the MXit WAP site. Please try again later." ) );
return;
}
/* explode the response from the WAP site into an array */
- parts = g_strsplit( url_text, ";", 15 );
+ parts = g_strsplit(purple_http_response_get_data(response, NULL), ";", 15);
if ( ( !parts ) || ( parts[0][0] != '0' ) ) {
/* server could not find the user */
- purple_connection_error( session->con, _( "MXit is currently unable to process the request. Please try again later." ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "MXit is currently unable to process the request. Please try again later." ) );
return;
}
@@ -679,7 +677,7 @@ static void mxit_cb_clientinfo1( PurpleUtilFetchUrlData* url_data, gpointer user
/* oops, this is not good, time to bail */
break;
}
- purple_request_field_list_add( field, country[1], g_strdup( country[0] ) );
+ purple_request_field_list_add_icon( field, country[1], NULL, g_strdup( country[0] ) );
if ( strcmp( country[1], parts[6] ) == 0 ) {
/* based on the user's IP, this is his current country code, so we default to it */
purple_request_field_list_add_selected( field, country[1] );
@@ -700,7 +698,7 @@ static void mxit_cb_clientinfo1( PurpleUtilFetchUrlData* url_data, gpointer user
/* oops, this is not good, time to bail */
break;
}
- purple_request_field_list_add( field, locale[1], g_strdup( locale[0] ) );
+ purple_request_field_list_add_icon( field, locale[1], NULL, g_strdup( locale[0] ) );
g_strfreev( locale );
}
purple_request_field_list_add_selected( field, "English" );
@@ -708,42 +706,46 @@ static void mxit_cb_clientinfo1( PurpleUtilFetchUrlData* url_data, gpointer user
/* display the form to the user and wait for his/her input */
purple_request_fields( session->con, "MXit", _( "MXit Authorization" ), _( "MXit account validation" ), fields,
- _( "Continue" ), G_CALLBACK( mxit_cb_captcha_ok ), _( "Cancel" ), G_CALLBACK( mxit_cb_captcha_cancel ), session->acc, NULL, NULL, session->con );
+ _( "Continue" ), G_CALLBACK( mxit_cb_captcha_ok ), _( "Cancel" ), G_CALLBACK( mxit_cb_captcha_cancel ), purple_request_cpar_from_account(session->acc), session->con );
/* freeup the memory */
g_strfreev( parts );
}
-/*------------------------------------------------------------------------
- * Initiate a request for the client information (distribution code, client key, etc)
- * required for logging in from the MXit WAP site.
+/**
+ * Initiate a request for the client information (distribution code, client
+ * key, etc) required for logging in from the MXit WAP site.
*
- * @param session The MXit session object
+ * @param session The MXit session object.
*/
-static void get_clientinfo( struct MXitSession* session )
+static void
+get_clientinfo(struct MXitSession* session)
{
- PurpleUtilFetchUrlData* url_data;
- const char* wapserver;
- char* url;
-
- purple_debug_info( MXIT_PLUGIN_ID, "get_clientinfo\n" );
-
- purple_connection_update_progress( session->con, _( "Retrieving User Information..." ), 0, 4 );
+ PurpleHttpRequest *req;
+ const char *wapserver;
- /* get the WAP site as was configured by the user in the advanced settings */
- wapserver = purple_account_get_string( session->acc, MXIT_CONFIG_WAPSERVER, DEFAULT_WAPSITE );
+ purple_debug_info(MXIT_PLUGIN_ID, "get_clientinfo\n");
- /* reference: "libpurple/util.h" */
- url = g_strdup_printf( "%s/res/?type=challenge&getcountries=true&getlanguage=true&getimage=true&h=%i&w=%i&ts=%li", wapserver, MXIT_CAPTCHA_HEIGHT, MXIT_CAPTCHA_WIDTH, time( NULL ) );
- url_data = purple_util_fetch_url_request( url, TRUE, MXIT_HTTP_USERAGENT, TRUE, NULL, FALSE, mxit_cb_clientinfo1, session );
- if ( url_data )
- session->async_calls = g_slist_prepend( session->async_calls, url_data );
+ purple_connection_update_progress(session->con,
+ _("Retrieving User Information..."), 0, 4);
-#ifdef DEBUG_PROTOCOL
- purple_debug_info( MXIT_PLUGIN_ID, "HTTP REQUEST: '%s'\n", url );
-#endif
- g_free( url );
+ /* get the WAP site as was configured by the user in the advanced
+ * settings
+ */
+ wapserver = purple_account_get_string(session->acc,
+ MXIT_CONFIG_WAPSERVER, DEFAULT_WAPSITE);
+
+ req = purple_http_request_new(NULL);
+ purple_http_request_set_url_printf(req, "%s/res/?type=challenge&"
+ "getcountries=true&getlanguage=true&getimage=true&h=%i&w=%i"
+ "&ts=%li", wapserver, MXIT_CAPTCHA_HEIGHT, MXIT_CAPTCHA_WIDTH,
+ time(NULL));
+ purple_http_request_header_set(req, "User-Agent", MXIT_HTTP_USERAGENT);
+ purple_http_connection_set_add(session->async_http_reqs,
+ purple_http_request(session->con, req, mxit_cb_clientinfo1,
+ session));
+ purple_http_request_unref(req);
}
@@ -766,7 +768,7 @@ void mxit_login( PurpleAccount* account )
* if we don't have any info saved from a previous login, we need to get it from the MXit WAP site.
* we do cache it, so this step is only done on the very first login for each account.
*/
- if ( strlen( session->distcode ) == 0 ) {
+ if ( !*session->distcode ) {
/* this must be the very first login, so we need to retrieve the user information */
get_clientinfo( session );
}
@@ -787,9 +789,9 @@ void mxit_reconnect( struct MXitSession* session )
purple_debug_info( MXIT_PLUGIN_ID, "mxit_reconnect\n" );
/* remove the input cb function */
- if ( session->con->inpa ) {
- purple_input_remove( session->con->inpa );
- session->con->inpa = 0;
+ if ( session->inpa ) {
+ purple_input_remove( session->inpa );
+ session->inpa = 0;
}
/* close existing connection */
diff --git a/libpurple/protocols/mxit/markup.c b/libpurple/protocols/mxit/markup.c
index 4e249fdd2e..117217d9b5 100644
--- a/libpurple/protocols/mxit/markup.c
+++ b/libpurple/protocols/mxit/markup.c
@@ -25,6 +25,8 @@
#include "internal.h"
#include "debug.h"
+#include "http.h"
+#include "image-store.h"
#include "protocol.h"
#include "mxit.h"
@@ -332,7 +334,7 @@ static void mxit_show_split_message( struct RXMsgData* mx )
}
/* push message to pidgin */
- serv_got_im( mx->session->con, mx->from, msg->str, mx->flags, mx->timestamp );
+ purple_serv_got_im( mx->session->con, mx->from, msg->str, mx->flags, mx->timestamp );
g_string_free( msg, TRUE );
msg = NULL;
@@ -356,7 +358,7 @@ static void mxit_show_split_message( struct RXMsgData* mx )
ch[pos] = '\n';
/* push message to pidgin */
- serv_got_im( mx->session->con, mx->from, msg->str, mx->flags, mx->timestamp );
+ purple_serv_got_im( mx->session->con, mx->from, msg->str, mx->flags, mx->timestamp );
g_string_free( msg, TRUE );
msg = NULL;
}
@@ -377,12 +379,13 @@ void mxit_show_message( struct RXMsgData* mx )
int emo_ofs;
char* ii;
char tag[64];
- int* img_id;
if ( mx->got_img ) {
/* search and replace all emoticon tags with proper image tags */
while ( ( pos = strstr( mx->msg->str, MXIT_II_TAG ) ) != NULL ) {
+ PurpleImage *img;
+
start = pos - mx->msg->str; /* offset at which MXIT_II_TAG starts */
emo_ofs = start + strlen( MXIT_II_TAG ); /* offset at which EMO's ID starts */
end = emo_ofs + 1; /* offset at which MXIT_II_TAG ends */
@@ -399,14 +402,18 @@ void mxit_show_message( struct RXMsgData* mx )
g_string_erase( mx->msg, start, ( end - start ) + 1 );
/* find the image entry */
- img_id = (int*) g_hash_table_lookup( mx->session->iimages, ii );
- if ( !img_id ) {
+ img = g_hash_table_lookup(mx->session->inline_images, ii);
+ if (img == NULL) {
/* inline image not found, so we will just skip it */
purple_debug_error( MXIT_PLUGIN_ID, "inline image NOT found (%s)\n", ii );
- }
- else {
+ } else {
+ guint img_id;
+
+ img_id = purple_image_store_add_temporary(img);
/* insert img tag */
- g_snprintf( tag, sizeof( tag ), "<img id=\"%i\">", *img_id );
+ g_snprintf(tag, sizeof(tag), "<img src=\""
+ PURPLE_IMAGE_STORE_PROTOCOL "%u\">",
+ img_id);
g_string_insert( mx->msg, start, tag );
}
@@ -427,7 +434,7 @@ void mxit_show_message( struct RXMsgData* mx )
}
else {
/* this is a multimx message */
- serv_got_chat_in( mx->session->con, mx->chatid, mx->from, mx->flags, mx->msg->str, mx->timestamp);
+ purple_serv_got_chat_in( mx->session->con, mx->chatid, mx->from, mx->flags, mx->msg->str, mx->timestamp);
}
/* freeup resource */
@@ -464,37 +471,31 @@ static void parse_emoticon_str( const char* message, char* emid )
/*------------------------------------------------------------------------
* Callback function invoked when a custom emoticon request to the WAP site completes.
- *
- * @param url_data
- * @param user_data The Markup message object
- * @param url_text The data returned from the WAP site
- * @param len The length of the data returned
- * @param error_message Descriptive error message
*/
-static void emoticon_returned( PurpleUtilFetchUrlData* url_data, gpointer user_data, const gchar* url_text, gsize len, const gchar* error_message )
+static void emoticon_returned(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer user_data)
{
+ PurpleImage *img;
struct RXMsgData* mx = (struct RXMsgData*) user_data;
- const gchar* data = url_text;
+ const gchar* data;
+ size_t len;
unsigned int pos = 0;
- int id;
char* str;
int em_size = 0;
char* em_data = NULL;
char* em_id = NULL;
- int* intptr = NULL;
int res;
purple_debug_info( MXIT_PLUGIN_ID, "emoticon_returned\n" );
- /* remove request from the async outstanding calls list */
- mx->session->async_calls = g_slist_remove( mx->session->async_calls, url_data );
-
- if ( !url_text ) {
+ if (!purple_http_response_is_successful(response)) {
/* no reply from the WAP site */
purple_debug_error( MXIT_PLUGIN_ID, "Error contacting the MXit WAP site. Please try again later (emoticon).\n" );
goto done;
}
+ data = purple_http_response_get_data(response, &len);
+
#ifdef MXIT_DEBUG_EMO
hex_dump( data, len );
#endif
@@ -578,7 +579,7 @@ static void emoticon_returned( PurpleUtilFetchUrlData* url_data, gpointer user_d
strcpy( em_id, emo );
}
- if ( g_hash_table_lookup( mx->session->iimages, em_id ) ) {
+ if (g_hash_table_lookup(mx->session->inline_images, em_id)) {
/* emoticon found in the table, so ignore this one */
g_free( em_id );
goto done;
@@ -588,13 +589,9 @@ static void emoticon_returned( PurpleUtilFetchUrlData* url_data, gpointer user_d
em_data = g_malloc( em_size );
memcpy( em_data, &data[pos], em_size );
- /* we now have the emoticon, store it in the imagestore */
- id = purple_imgstore_add_with_id( em_data, em_size, NULL );
-
- /* map the mxit emoticon id to purple image id */
- intptr = g_malloc( sizeof( int ) );
- *intptr = id;
- g_hash_table_insert( mx->session->iimages, em_id, intptr );
+ /* map the mxit emoticon id to purple image */
+ img = purple_image_new_from_data(em_data, em_size);
+ g_hash_table_insert(mx->session->inline_images, em_id, img);
mx->flags |= PURPLE_MESSAGE_IMAGES;
done:
@@ -617,21 +614,16 @@ done:
*/
static void emoticon_request( struct RXMsgData* mx, const char* id )
{
- PurpleUtilFetchUrlData* url_data;
const char* wapserver;
- char* url;
purple_debug_info( MXIT_PLUGIN_ID, "sending request for emoticon '%s'\n", id );
wapserver = purple_account_get_string( mx->session->acc, MXIT_CONFIG_WAPSERVER, DEFAULT_WAPSITE );
- /* reference: "libpurple/util.h" */
- url = g_strdup_printf( "%s/res/?type=emo&mlh=%i&sc=%s&ts=%li", wapserver, MXIT_EMOTICON_SIZE, id, time( NULL ) );
- url_data = purple_util_fetch_url_request( url, TRUE, NULL, TRUE, NULL, FALSE, emoticon_returned, mx );
- if ( url_data )
- mx->session->async_calls = g_slist_prepend( mx->session->async_calls, url_data );
-
- g_free( url );
+ purple_http_connection_set_add(mx->session->async_http_reqs,
+ purple_http_get_printf(mx->session->con, emoticon_returned, mx,
+ "%s/res/?type=emo&mlh=%i&sc=%s&ts=%li", wapserver,
+ MXIT_EMOTICON_SIZE, id, time( NULL ) ));
}
@@ -893,7 +885,7 @@ void mxit_parse_markup( struct RXMsgData* mx, char* message, int len, short msgt
if ( tmpstr1[0] != '\0' ) {
mx->got_img = TRUE;
- if ( g_hash_table_lookup( mx->session->iimages, tmpstr1 ) ) {
+ if (g_hash_table_lookup(mx->session->inline_images, tmpstr1)) {
/* emoticon found in the cache, so we do not have to request it from the WAPsite */
}
else {
@@ -1013,22 +1005,18 @@ void mxit_parse_markup( struct RXMsgData* mx, char* message, int len, short msgt
/*------------------------------------------------------------------------
* Insert an inline image command.
*
- * @param mx The message text as processed so far.
- * @oaram id The imgstore ID of the inline image.
+ * @param mx The message text as processed so far.
+ * @oaram image The PurpleImage of the inline image.
*/
-static void inline_image_add( GString* mx, int id )
+static void
+inline_image_add(GString* mx, PurpleImage *image)
{
- PurpleStoredImage *image;
gconstpointer img_data;
gsize img_size;
gchar* enc;
- image = purple_imgstore_find_by_id( id );
- if ( image == NULL )
- return;
-
- img_data = purple_imgstore_get_data( image );
- img_size = purple_imgstore_get_size( image );
+ img_data = purple_image_get_data(image);
+ img_size = purple_image_get_size(image);
enc = purple_base64_encode( img_data, img_size );
@@ -1073,7 +1061,7 @@ char* mxit_convert_markup_tx( const char* message, int* msgtype )
* Font colour: <font color=#">...</font>
* Links: <a href="">...</a>
* Newline: <br>
- * Inline image: <IMG ID="">
+ * Inline image: <IMG SRC="">
* The following characters are also encoded:
* &amp; &quot; &lt; &gt;
*/
@@ -1139,13 +1127,14 @@ char* mxit_convert_markup_tx( const char* message, int* msgtype )
tagstack = g_list_remove( tagstack, tag );
g_free( tag );
}
- }
- else if ( purple_str_has_prefix( &message[i], "<IMG ID=" ) ) {
+ } else if (purple_str_has_prefix(&message[i], "<img src=\"")) {
/* inline image */
- int imgid;
+ PurpleImage *img;
+ img = purple_image_store_get_from_uri(
+ &message[i + sizeof("<img src=\"") - 1]);
- if ( sscanf( &message[i+9], "%i", &imgid ) ) {
- inline_image_add( mx, imgid );
+ if (img) {
+ inline_image_add(mx, img);
*msgtype = CP_MSGTYPE_COMMAND; /* inline image must be sent as a MXit command */
}
}
@@ -1196,35 +1185,11 @@ char* mxit_convert_markup_tx( const char* message, int* msgtype )
/*------------------------------------------------------------------------
- * Free an emoticon entry.
- *
- * @param key MXit emoticon ID
- * @param value Imagestore ID for emoticon
- * @param user_data NULL (unused)
- * @return TRUE
- */
-static gboolean emoticon_entry_free( gpointer key, gpointer value, gpointer user_data )
-{
- int* imgid = value;
-
- /* key is a string */
- g_free( key );
-
- /* value is 'id' in imagestore */
- purple_imgstore_unref_by_id( *imgid );
- g_free( value );
-
- return TRUE;
-}
-
-
-/*------------------------------------------------------------------------
* Free all entries in the emoticon cache.
*
* @param session The MXit session object
*/
void mxit_free_emoticon_cache( struct MXitSession* session )
{
- g_hash_table_foreach_remove( session->iimages, emoticon_entry_free, NULL );
- g_hash_table_destroy ( session->iimages );
+ g_hash_table_destroy(session->inline_images);
}
diff --git a/libpurple/protocols/mxit/multimx.c b/libpurple/protocols/mxit/multimx.c
index 606fe40253..442a3891b4 100644
--- a/libpurple/protocols/mxit/multimx.c
+++ b/libpurple/protocols/mxit/multimx.c
@@ -179,11 +179,11 @@ static void room_remove(struct MXitSession* session, struct multimx* multimx)
* @param convo The Conversation object
* @param nickname The nickname of the user who joined the room
*/
-static void member_added(PurpleConversation* convo, const char* nickname)
+static void member_added(PurpleChatConversation* chat, const char* nickname)
{
purple_debug_info(MXIT_PLUGIN_ID, "member_added: '%s'\n", nickname);
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(convo), nickname, NULL, PURPLE_CBFLAGS_NONE, TRUE);
+ purple_chat_conversation_add_user(chat, nickname, NULL, PURPLE_CHAT_USER_NONE, TRUE);
}
@@ -193,11 +193,11 @@ static void member_added(PurpleConversation* convo, const char* nickname)
* @param convo The Conversation object
* @param nickname The nickname of the user who left the room
*/
-static void member_removed(PurpleConversation* convo, const char* nickname)
+static void member_removed(PurpleChatConversation* chat, const char* nickname)
{
purple_debug_info(MXIT_PLUGIN_ID, "member_removed: '%s'\n", nickname);
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), nickname, NULL);
+ purple_chat_conversation_remove_user(chat, nickname, NULL);
}
@@ -207,11 +207,11 @@ static void member_removed(PurpleConversation* convo, const char* nickname)
* @param convo The Conversation object
* @param nickname The nickname of the user who was kicked
*/
-static void member_kicked(PurpleConversation* convo, const char* nickname)
+static void member_kicked(PurpleChatConversation* chat, const char* nickname)
{
purple_debug_info(MXIT_PLUGIN_ID, "member_kicked: '%s'\n", nickname);
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), nickname, _("was kicked"));
+ purple_chat_conversation_remove_user(chat, nickname, _("was kicked"));
}
@@ -222,13 +222,14 @@ static void member_kicked(PurpleConversation* convo, const char* nickname)
* @param session The MXit session object
* @param multimx The MultiMX room object
*/
-static void you_kicked(PurpleConversation* convo, struct MXitSession* session, struct multimx* multimx)
+static void you_kicked(PurpleChatConversation* chat, struct MXitSession* session, struct multimx* multimx)
{
purple_debug_info(MXIT_PLUGIN_ID, "you_kicked\n");
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), "MXit", _("You have been kicked from this MultiMX."), PURPLE_MESSAGE_SYSTEM, time(NULL));
- purple_conv_chat_clear_users(PURPLE_CONV_CHAT(convo));
- serv_got_chat_left(session->con, multimx->chatid);
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(chat),
+ _("You have been kicked from this MultiMX."), 0);
+ purple_chat_conversation_clear_users(chat);
+ purple_serv_got_chat_left(session->con, multimx->chatid);
}
@@ -238,7 +239,7 @@ static void you_kicked(PurpleConversation* convo, struct MXitSession* session, s
* @param convo The Conversation object
* @param data The nicknames of the users in the room (separated by \n)
*/
-static void member_update(PurpleConversation* convo, char* data)
+static void member_update(PurpleChatConversation* chat, char* data)
{
gchar** userlist;
int i = 0;
@@ -246,14 +247,14 @@ static void member_update(PurpleConversation* convo, char* data)
purple_debug_info(MXIT_PLUGIN_ID, "member_update: '%s'\n", data);
/* Clear list */
- purple_conv_chat_clear_users(PURPLE_CONV_CHAT(convo));
+ purple_chat_conversation_clear_users(chat);
/* Add each member */
data = g_strstrip(data); /* string leading & trailing whitespace */
userlist = g_strsplit(data, "\n", 0); /* tokenize string */
while (userlist[i] != NULL) {
purple_debug_info(MXIT_PLUGIN_ID, "member_update - adding: '%s'\n", userlist[i]);
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(convo), userlist[i], NULL, PURPLE_CBFLAGS_NONE, FALSE);
+ purple_chat_conversation_add_user(chat, userlist[i], NULL, PURPLE_CHAT_USER_NONE, FALSE);
i++;
}
g_strfreev(userlist);
@@ -288,7 +289,7 @@ void multimx_invite(struct MXitSession* session, struct contact* contact, const
g_hash_table_insert(components, g_strdup("room"), g_strdup(contact->alias));
/* Call libpurple - will trigger either 'mxit_chat_join' or 'mxit_chat_reject' */
- serv_got_chat_invite(session->con, contact->alias, creator, NULL, components);
+ purple_serv_got_chat_invite(session->con, contact->alias, creator, NULL, components);
}
@@ -316,7 +317,7 @@ void multimx_created(struct MXitSession* session, struct contact* contact)
}
/* Call libpurple - will trigger 'mxit_chat_join' */
- serv_got_joined_chat(gc, multimx->chatid, multimx->roomname);
+ purple_serv_got_joined_chat(gc, multimx->chatid, multimx->roomname);
/* Send ".list" command to GroupChat server to retrieve current member-list */
mxit_send_message(session, multimx->roomid, ".list", FALSE, FALSE);
@@ -377,8 +378,8 @@ void multimx_message_received(struct RXMsgData* mx, char* msg, int msglen, short
/* Must be a service message */
char* ofs;
- PurpleConversation* convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, multimx->roomname, mx->session->acc);
- if (convo == NULL) {
+ PurpleChatConversation* chat = purple_conversations_find_chat_with_account(multimx->roomname, mx->session->acc);
+ if (chat == NULL) {
purple_debug_error(MXIT_PLUGIN_ID, "Conversation '%s' not found\n", multimx->roomname);
return;
}
@@ -387,33 +388,33 @@ void multimx_message_received(struct RXMsgData* mx, char* msg, int msglen, short
if ((ofs = strstr(msg, " has joined")) != NULL) {
/* Somebody has joined */
*ofs = '\0';
- member_added(convo, msg);
+ member_added(chat, msg);
mx->processed = TRUE;
}
else if ((ofs = strstr(msg, " has left")) != NULL) {
/* Somebody has left */
*ofs = '\0';
- member_removed(convo, msg);
+ member_removed(chat, msg);
mx->processed = TRUE;
}
else if ((ofs = strstr(msg, " has been kicked")) != NULL) {
/* Somebody has been kicked */
*ofs = '\0';
- member_kicked(convo, msg);
+ member_kicked(chat, msg);
mx->processed = TRUE;
}
else if (strcmp(msg, "You have been kicked.") == 0) {
/* You have been kicked */
- you_kicked(convo, mx->session, multimx);
+ you_kicked(chat, mx->session, multimx);
mx->processed = TRUE;
}
else if (g_str_has_prefix(msg, "The following users are in this MultiMx:") == TRUE) {
- member_update(convo, msg + strlen("The following users are in this MultiMx:") + 1);
+ member_update(chat, msg + strlen("The following users are in this MultiMx:") + 1);
mx->processed = TRUE;
}
else {
/* Display server message in chat window */
- serv_got_chat_in(mx->session->con, multimx->chatid, "MXit", PURPLE_MESSAGE_SYSTEM, msg, mx->timestamp);
+ purple_serv_got_chat_in(mx->session->con, multimx->chatid, "MXit", PURPLE_MESSAGE_SYSTEM, msg, mx->timestamp);
mx->processed = TRUE;
}
}
@@ -480,7 +481,7 @@ void mxit_chat_join(PurpleConnection *gc, GHashTable *components)
/* Join existing room */
purple_debug_info(MXIT_PLUGIN_ID, "Groupchat %i rejoined\n", multimx->chatid);
- serv_got_joined_chat(gc, multimx->chatid, multimx->roomname);
+ purple_serv_got_joined_chat(gc, multimx->chatid, multimx->roomname);
}
}
else {
@@ -544,7 +545,7 @@ void mxit_chat_invite(PurpleConnection *gc, int id, const char *msg, const char
struct MXitSession* session = purple_connection_get_protocol_data(gc);
struct multimx* multimx = NULL;
PurpleBuddy* buddy;
- PurpleConversation *convo;
+ PurpleChatConversation *chat;
char* tmp;
purple_debug_info(MXIT_PLUGIN_ID, "Groupchat invite to '%s'\n", username);
@@ -559,22 +560,22 @@ void mxit_chat_invite(PurpleConnection *gc, int id, const char *msg, const char
/* Send invite to MXit */
mxit_send_groupchat_invite(session, multimx->roomid, 1, &username);
- /* Find the buddy information for this contact (reference: "libpurple/blist.h") */
- buddy = purple_find_buddy(session->acc, username);
+ /* Find the buddy information for this contact (reference: "libpurple/buddylist.h") */
+ buddy = purple_blist_find_buddy(session->acc, username);
if (!buddy) {
purple_debug_warning(MXIT_PLUGIN_ID, "mxit_chat_invite: unable to find the buddy '%s'\n", username);
return;
}
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, multimx->roomname, session->acc);
- if (convo == NULL) {
+ chat = purple_conversations_find_chat_with_account(multimx->roomname, session->acc);
+ if (chat == NULL) {
purple_debug_error(MXIT_PLUGIN_ID, "Conversation '%s' not found\n", multimx->roomname);
return;
}
/* Display system message in chat window */
tmp = g_strdup_printf("%s: %s", _("You have invited"), purple_buddy_get_alias(buddy));
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), "MXit", tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(chat), tmp, 0);
g_free(tmp);
}
@@ -612,18 +613,15 @@ void mxit_chat_leave(PurpleConnection *gc, int id)
*
* @param gc The connection object
* @param id The chat room ID
- * @param message The sent message data
- * @param flags The message flags
+ * @param msg The sent message data
* @return Indicates success / failure
*/
-int mxit_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags)
+int mxit_chat_send(PurpleConnection *gc, int id, PurpleMessage *msg)
{
struct MXitSession* session = purple_connection_get_protocol_data(gc);
struct multimx* multimx = NULL;
const char* nickname;
- purple_debug_info(MXIT_PLUGIN_ID, "Groupchat %i message send: '%s'\n", id, message);
-
/* Find matching MultiMX group */
multimx = find_room_by_id(session, id);
if (multimx == NULL) {
@@ -632,16 +630,18 @@ int mxit_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMess
}
/* Send packet to MXit */
- mxit_send_message(session, multimx->roomid, message, TRUE, FALSE);
+ mxit_send_message(session, multimx->roomid,
+ purple_message_get_contents(msg), TRUE, FALSE);
/* Determine our nickname to display */
if (multimx->nickname)
nickname = multimx->nickname;
else
- nickname = purple_account_get_alias(purple_connection_get_account(gc)); /* local alias */
+ nickname = purple_account_get_private_alias(purple_connection_get_account(gc)); /* local alias */
/* Display message in chat window */
- serv_got_chat_in(gc, id, nickname, flags, message, time(NULL));
+ purple_serv_got_chat_in(gc, id, nickname, purple_message_get_flags(msg),
+ purple_message_get_contents(msg), time(NULL));
return 0;
}
diff --git a/libpurple/protocols/mxit/multimx.h b/libpurple/protocols/mxit/multimx.h
index e9470b6665..36f77a87cd 100644
--- a/libpurple/protocols/mxit/multimx.h
+++ b/libpurple/protocols/mxit/multimx.h
@@ -99,7 +99,7 @@ void mxit_chat_leave(PurpleConnection *gc, int id);
/*
* User has entered a message in a chatroom window, send it to the MXit server.
*/
-int mxit_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags);
+int mxit_chat_send(PurpleConnection *gc, int id, PurpleMessage *msg);
#endif /* _MXIT_MULTIMX_H_ */
diff --git a/libpurple/protocols/mxit/mxit.c b/libpurple/protocols/mxit/mxit.c
index 7855499cb8..c381110345 100644
--- a/libpurple/protocols/mxit/mxit.c
+++ b/libpurple/protocols/mxit/mxit.c
@@ -175,12 +175,12 @@ static void mxit_cb_chat_created( PurpleConversation* conv, struct MXitSession*
const char* who;
char* tmp;
- gc = purple_conversation_get_gc( conv );
+ gc = purple_conversation_get_connection( conv );
if ( session->con != gc ) {
/* not our conversation */
return;
}
- else if ( purple_conversation_get_type( conv ) != PURPLE_CONV_TYPE_IM ) {
+ else if ( !PURPLE_IS_IM_CONVERSATION( conv ) ) {
/* wrong type of conversation */
return;
}
@@ -193,7 +193,7 @@ static void mxit_cb_chat_created( PurpleConversation* conv, struct MXitSession*
purple_debug_info( MXIT_PLUGIN_ID, "Conversation started with '%s'\n", who );
/* find the buddy object */
- buddy = purple_find_buddy( session->acc, who );
+ buddy = purple_blist_find_buddy( session->acc, who );
if ( !buddy )
return;
@@ -212,7 +212,7 @@ static void mxit_cb_chat_created( PurpleConversation* conv, struct MXitSession*
case MXIT_TYPE_GALLERY :
case MXIT_TYPE_INFO :
tmp = g_strdup_printf("<font color=\"#999999\">%s</font>\n", _( "Loading menu..." ));
- serv_got_im( session->con, who, tmp, PURPLE_MESSAGE_NOTIFY, time( NULL ) );
+ purple_serv_got_im( session->con, who, tmp, PURPLE_MESSAGE_NOTIFY, time( NULL ) );
g_free( tmp );
mxit_send_message( session, who, " ", FALSE, FALSE );
default :
@@ -339,23 +339,26 @@ static void mxit_tooltip( PurpleBuddy* buddy, PurpleNotifyUserInfo* info, gboole
/* status (reference: "libpurple/notify.h") */
if ( contact->presence != MXIT_PRESENCE_OFFLINE )
- purple_notify_user_info_add_pair( info, _( "Status" ), mxit_convert_presence_to_name( contact->presence ) );
+ purple_notify_user_info_add_pair_plaintext( info, _( "Status" ), mxit_convert_presence_to_name( contact->presence ) );
/* status message */
- if ( contact->statusMsg )
- purple_notify_user_info_add_pair( info, _( "Status Message" ), contact->statusMsg );
+ if ( contact->statusMsg ) {
+ /* 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( info, _( "Status Message" ), contact->statusMsg );
+ }
/* mood */
if ( contact->mood != MXIT_MOOD_NONE )
- purple_notify_user_info_add_pair( info, _( "Mood" ), mxit_convert_mood_to_name( contact->mood ) );
+ purple_notify_user_info_add_pair_plaintext( info, _( "Mood" ), mxit_convert_mood_to_name( contact->mood ) );
/* subscription type */
if ( contact->subtype != 0 )
- purple_notify_user_info_add_pair( info, _( "Subscription" ), mxit_convert_subtype_to_name( contact->subtype ) );
+ purple_notify_user_info_add_pair_plaintext( info, _( "Subscription" ), mxit_convert_subtype_to_name( contact->subtype ) );
/* rejection message */
if ( ( contact->subtype == MXIT_SUBTYPE_REJECTED ) && ( contact->msg != NULL ) )
- purple_notify_user_info_add_pair( info, _( "Rejection Message" ), contact->msg );
+ purple_notify_user_info_add_pair_plaintext( info, _( "Rejection Message" ), contact->msg );
}
@@ -398,11 +401,11 @@ static void mxit_close( PurpleConnection* gc )
Zero (success, no echo)
Negative value (error)
*/
-static int mxit_send_im( PurpleConnection* gc, const char* who, const char* message, PurpleMessageFlags flags )
+static int mxit_send_im(PurpleConnection* gc, PurpleMessage *msg)
{
- purple_debug_info( MXIT_PLUGIN_ID, "Sending message '%s' to buddy '%s'\n", message, who );
-
- mxit_send_message( purple_connection_get_protocol_data( gc ), who, message, TRUE, FALSE );
+ mxit_send_message(purple_connection_get_protocol_data(gc),
+ purple_message_get_recipient(msg), purple_message_get_contents(msg),
+ TRUE, FALSE);
return 1; /* echo to conversation window */
}
@@ -423,7 +426,7 @@ static void mxit_set_status( PurpleAccount* account, PurpleStatus* status )
char* statusmsg2;
/* Handle mood changes */
- if ( purple_status_type_get_primitive( purple_status_get_type( status ) ) == PURPLE_STATUS_MOOD ) {
+ if ( purple_status_type_get_primitive( purple_status_get_status_type( status ) ) == PURPLE_STATUS_MOOD ) {
const char* moodid = purple_status_get_attr_string( status, PURPLE_MOOD_NAME );
int mood;
@@ -484,8 +487,6 @@ static void mxit_free_buddy( PurpleBuddy* buddy )
{
struct contact* contact;
- purple_debug_info( MXIT_PLUGIN_ID, "mxit_free_buddy\n" );
-
contact = purple_buddy_get_protocol_data( buddy );
if ( contact ) {
if ( contact->statusMsg )
@@ -494,6 +495,8 @@ static void mxit_free_buddy( PurpleBuddy* buddy )
g_free( contact->avatarId );
if ( contact->msg )
g_free( contact->msg );
+ if (contact->image)
+ g_object_unref(contact->image);
g_free( contact );
}
@@ -535,14 +538,17 @@ static void mxit_keepalive( PurpleConnection *gc )
* @param gc The connection object
* @param img The buddy icon data
*/
-static void mxit_set_buddy_icon( PurpleConnection *gc, PurpleStoredImage *img )
+static void
+mxit_set_buddy_icon(PurpleConnection *gc, PurpleImage *img)
{
- struct MXitSession* session = purple_connection_get_protocol_data( gc );
+ struct MXitSession* session = purple_connection_get_protocol_data(gc);
- if ( img == NULL )
- mxit_set_avatar( session, NULL, 0 );
- else
- mxit_set_avatar( session, purple_imgstore_get_data( img ), purple_imgstore_get_size( img ) );
+ if (img == NULL)
+ mxit_set_avatar(session, NULL, 0);
+ else {
+ mxit_set_avatar(session, purple_image_get_data(img),
+ purple_image_get_size(img));
+ }
}
@@ -563,8 +569,8 @@ static void mxit_get_info( PurpleConnection *gc, const char *who )
purple_debug_info( MXIT_PLUGIN_ID, "mxit_get_info: '%s'\n", who );
- /* find the buddy information for this contact (reference: "libpurple/blist.h") */
- buddy = purple_find_buddy( session->acc, who );
+ /* find the buddy information for this contact (reference: "libpurple/buddylist.h") */
+ buddy = purple_blist_find_buddy( session->acc, who );
if ( buddy ) {
/* user is in our contact-list, so it's not an invite */
contact = purple_buddy_get_protocol_data( buddy );
@@ -632,7 +638,7 @@ static GList* mxit_blist_menu( PurpleBlistNode *node )
GList* m = NULL;
PurpleMenuAction* act;
- if ( !PURPLE_BLIST_NODE_IS_BUDDY( node ) )
+ if ( !PURPLE_IS_BUDDY( node ) )
return NULL;
buddy = (PurpleBuddy *) node;
@@ -668,7 +674,7 @@ static GHashTable *mxit_chat_info_defaults( PurpleConnection *gc, const char *ch
* @param name The username of the contact
* @param state The typing state to be reported.
*/
-static unsigned int mxit_send_typing( PurpleConnection *gc, const char *name, PurpleTypingState state )
+static unsigned int mxit_send_typing( PurpleConnection *gc, const char *name, PurpleIMTypingState state )
{
PurpleAccount* account = purple_connection_get_account( gc );
struct MXitSession* session = purple_connection_get_protocol_data( gc );
@@ -676,8 +682,8 @@ static unsigned int mxit_send_typing( PurpleConnection *gc, const char *name, Pu
struct contact* contact;
gchar* messageId = NULL;
- /* find the buddy information for this contact (reference: "libpurple/blist.h") */
- buddy = purple_find_buddy( account, name );
+ /* find the buddy information for this contact (reference: "libpurple/buddylist.h") */
+ buddy = purple_blist_find_buddy( account, name );
if ( !buddy ) {
purple_debug_warning( MXIT_PLUGIN_ID, "mxit_send_typing: unable to find the buddy '%s'\n", name );
return 0;
@@ -694,12 +700,12 @@ static unsigned int mxit_send_typing( PurpleConnection *gc, const char *name, Pu
messageId = purple_uuid_random(); /* generate a unique message id */
switch ( state ) {
- case PURPLE_TYPING : /* currently typing */
+ case PURPLE_IM_TYPING : /* currently typing */
mxit_send_msgevent( session, name, messageId, CP_MSGEVENT_TYPING );
break;
- case PURPLE_TYPED : /* stopped typing */
- case PURPLE_NOT_TYPING : /* not typing / erased all text */
+ case PURPLE_IM_TYPED : /* stopped typing */
+ case PURPLE_IM_NOT_TYPING : /* not typing / erased all text */
mxit_send_msgevent( session, name, messageId, CP_MSGEVENT_STOPPED );
break;
@@ -716,7 +722,9 @@ static unsigned int mxit_send_typing( PurpleConnection *gc, const char *name, Pu
/*========================================================================================================================*/
static PurplePluginProtocolInfo proto_info = {
- OPT_PROTO_REGISTER_NOSCREENNAME | OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_IM_IMAGE | OPT_PROTO_INVITE_MESSAGE, /* options */
+ sizeof( PurplePluginProtocolInfo ), /* struct_size */
+ OPT_PROTO_REGISTER_NOSCREENNAME | OPT_PROTO_UNIQUE_CHATNAME |
+ OPT_PROTO_INVITE_MESSAGE | OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE, /* options */
NULL, /* user_splits */
NULL, /* protocol_options */
{ /* icon_spec */
@@ -743,7 +751,7 @@ static PurplePluginProtocolInfo proto_info = {
mxit_set_status, /* set_status */
NULL, /* set_idle */
NULL, /* change_passwd */
- NULL, /* add_buddy [roster.c] */
+ mxit_add_buddy, /* add_buddy [roster.c] */
NULL, /* add_buddies */
mxit_remove_buddy, /* remove_buddy [roster.c] */
NULL, /* remove_buddies */
@@ -757,12 +765,10 @@ static PurplePluginProtocolInfo proto_info = {
mxit_chat_name, /* get_chat_name [multimx.c] */
mxit_chat_invite, /* chat_invite [multimx.c] */
mxit_chat_leave, /* chat_leave [multimx.c] */
- NULL, /* chat_whisper */
mxit_chat_send, /* chat_send [multimx.c] */
mxit_keepalive, /* keepalive */
mxit_register, /* register_user */
NULL, /* get_cb_info */
- NULL, /* get_cb_away */
mxit_buddy_alias, /* alias_buddy [roster.c] */
mxit_buddy_group, /* group_buddy [roster.c] */
mxit_rename_group, /* rename_group [roster.c] */
@@ -787,15 +793,13 @@ static PurplePluginProtocolInfo proto_info = {
NULL, /* unregister_user */
NULL, /* send_attention */
NULL, /* attention_types */
- sizeof( PurplePluginProtocolInfo ), /* struct_size */
mxit_get_text_table, /* get_account_text_table */
mxit_media_initiate, /* initiate_media */
mxit_media_caps, /* get_media_caps */
mxit_get_moods, /* get_moods */
NULL, /* set_public_alias */
NULL, /* get_public_alias */
- mxit_add_buddy, /* add_buddy_with_invite */
- NULL /* add_buddies_with_invite */
+ NULL /* get_max_message_size */
};
@@ -844,8 +848,6 @@ static void init_plugin( PurplePlugin* plugin )
{
PurpleAccountOption* option;
- purple_debug_info( MXIT_PLUGIN_ID, "Loading MXit libPurple plugin...\n" );
-
/* Configuration options */
/* WAP server (reference: "libpurple/accountopt.h") */
diff --git a/libpurple/protocols/mxit/mxit.h b/libpurple/protocols/mxit/mxit.h
index 138d1d85d4..e16fff1d37 100644
--- a/libpurple/protocols/mxit/mxit.h
+++ b/libpurple/protocols/mxit/mxit.h
@@ -28,7 +28,7 @@
#include "internal.h"
-
+#include "http.h"
#if defined( __APPLE__ )
/* apple architecture */
@@ -40,7 +40,6 @@
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 512
#endif
-#include "libc_interface.h"
#elif defined( __linux__ )
/* linux architecture */
#include <net/if.h>
@@ -160,6 +159,7 @@ struct MXitSession {
/* libpurple */
PurpleAccount* acc; /* pointer to the libpurple internal account struct */
PurpleConnection* con; /* pointer to the libpurple internal connection struct */
+ guint inpa; /* the input watcher */
/* transmit */
struct tx_queue queue; /* transmit packet queue (FIFO mode) */
@@ -167,7 +167,7 @@ struct MXitSession {
int outack; /* outstanding ack packet */
guint q_slow_timer_id; /* timer handle for slow tx queue */
guint q_fast_timer_id; /* timer handle for fast tx queue */
- GSList* async_calls; /* list of current outstanding async calls */
+ PurpleHttpConnectionSet* async_http_reqs; /* list of current outstanding async http requests */
/* receive */
char rx_lbuf[16]; /* receive byte buffer (socket packet length) */
@@ -183,7 +183,7 @@ struct MXitSession {
GList* rooms; /* active groupchat rooms */
/* inline images */
- GHashTable* iimages; /* table which maps inline images (including emoticons) to purple's imgstore id's */
+ GHashTable *inline_images; /* table which maps inline images (including emoticons) to PurpleImages */
};
diff --git a/libpurple/protocols/mxit/profile.c b/libpurple/protocols/mxit/profile.c
index bef948e474..4b6be298c0 100644
--- a/libpurple/protocols/mxit/profile.c
+++ b/libpurple/protocols/mxit/profile.c
@@ -27,6 +27,7 @@
#include <time.h>
#include "internal.h"
+#include "image-store.h"
#include "mxit.h"
#include "profile.h"
@@ -148,7 +149,7 @@ static int calculateAge( const char* date )
struct tm now, bdate;
int age;
- if ( ( !date ) || ( strlen( date ) == 0 ) )
+ if ( ( !date ) || ( !*date ) )
return 0;
/* current time */
@@ -201,30 +202,32 @@ void mxit_show_profile( struct MXitSession* session, const char* username, struc
PurpleBuddy* buddy;
gchar* tmp = NULL;
- buddy = purple_find_buddy( session->acc, username );
+ buddy = purple_blist_find_buddy( session->acc, username );
if ( buddy ) {
- purple_notify_user_info_add_pair( info, _( "Alias" ), purple_buddy_get_alias( buddy ) );
+ purple_notify_user_info_add_pair_plaintext( info, _( "Alias" ), purple_buddy_get_alias( buddy ) );
purple_notify_user_info_add_section_break( info );
contact = purple_buddy_get_protocol_data( buddy );
}
- purple_notify_user_info_add_pair( info, _( "Display Name" ), profile->nickname );
+ purple_notify_user_info_add_pair_plaintext( info, _( "Display Name" ), profile->nickname );
tmp = g_strdup_printf("%s (%i)", profile->birthday, calculateAge( profile->birthday ) );
- purple_notify_user_info_add_pair( info, _( "Birthday" ), tmp );
+ purple_notify_user_info_add_pair_plaintext( info, _( "Birthday" ), tmp );
g_free( tmp );
- purple_notify_user_info_add_pair( info, _( "Gender" ), profile->male ? _( "Male" ) : _( "Female" ) );
+ purple_notify_user_info_add_pair_plaintext( info, _( "Gender" ), profile->male ? _( "Male" ) : _( "Female" ) );
/* optional information */
- purple_notify_user_info_add_pair( info, _( "First Name" ), profile->firstname );
- purple_notify_user_info_add_pair( info, _( "Last Name" ), profile->lastname );
- purple_notify_user_info_add_pair( info, _( "Country" ), profile->regcountry );
+ purple_notify_user_info_add_pair_plaintext( info, _( "First Name" ), profile->firstname );
+ purple_notify_user_info_add_pair_plaintext( info, _( "Last Name" ), profile->lastname );
- if ( strlen( profile->aboutme ) > 0 )
- purple_notify_user_info_add_pair( info, _( "About Me" ), profile->aboutme );
- if ( strlen( profile->whereami ) > 0 )
- purple_notify_user_info_add_pair( info, _( "Where I Live" ), profile->whereami );
+ purple_notify_user_info_add_pair_plaintext( info, _( "Country" ), profile->regcountry );
+
+ if ( *profile->aboutme )
+ purple_notify_user_info_add_pair_plaintext( info, _( "About Me" ), profile->aboutme );
+
+ if ( *profile->whereami )
+ purple_notify_user_info_add_pair_plaintext( info, _( "Where I Live" ), profile->whereami );
purple_notify_user_info_add_pair_plaintext( info, _( "Relationship Status" ), mxit_relationship_to_name( profile->relationship ) );
@@ -232,24 +235,27 @@ void mxit_show_profile( struct MXitSession* session, const char* username, struc
if ( contact ) {
/* presence */
- purple_notify_user_info_add_pair( info, _( "Status" ), mxit_convert_presence_to_name( contact->presence ) );
+ purple_notify_user_info_add_pair_plaintext( info, _( "Status" ), mxit_convert_presence_to_name( contact->presence ) );
/* last online */
if ( contact->presence == MXIT_PRESENCE_OFFLINE )
- purple_notify_user_info_add_pair( info, _( "Last Online" ), ( profile->lastonline == 0 ) ? _( "Unknown" ) : datetime( profile->lastonline ) );
+ purple_notify_user_info_add_pair_plaintext( info, _( "Last Online" ), ( profile->lastonline == 0 ) ? _( "Unknown" ) : datetime( profile->lastonline ) );
/* mood */
if ( contact->mood != MXIT_MOOD_NONE )
- purple_notify_user_info_add_pair( info, _( "Mood" ), mxit_convert_mood_to_name( contact->mood ) );
+ purple_notify_user_info_add_pair_plaintext( info, _( "Mood" ), mxit_convert_mood_to_name( contact->mood ) );
else
- purple_notify_user_info_add_pair( info, _( "Mood" ), _( "None" ) );
+ purple_notify_user_info_add_pair_plaintext( info, _( "Mood" ), _( "None" ) );
/* status message */
- if ( contact->statusMsg )
- purple_notify_user_info_add_pair( info, _( "Status Message" ), contact->statusMsg );
+ if ( contact->statusMsg ) {
+ /* 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( info, _( "Status Message" ), contact->statusMsg );
+ }
/* subscription type */
- purple_notify_user_info_add_pair( info, _( "Subscription" ), mxit_convert_subtype_to_name( contact->subtype ) );
+ purple_notify_user_info_add_pair_plaintext( info, _( "Subscription" ), mxit_convert_subtype_to_name( contact->subtype ) );
}
else {
/* this is an invite */
@@ -258,18 +264,25 @@ void mxit_show_profile( struct MXitSession* session, const char* username, struc
/* invite found */
if ( contact->msg )
- purple_notify_user_info_add_pair( info, _( "Invite Message" ), contact->msg );
+ purple_notify_user_info_add_pair_plaintext( info, _( "Invite Message" ), contact->msg );
- if ( contact->imgid ) {
+ if (contact->image) {
/* this invite has a avatar */
char* img_text;
- img_text = g_strdup_printf( "<img id='%d'>", contact->imgid );
- purple_notify_user_info_add_pair( info, _( "Photo" ), img_text );
+ guint img_id;
+
+ img_id = purple_image_store_add_temporary(contact->image);
+ img_text = g_strdup_printf("<img src=\""
+ PURPLE_IMAGE_STORE_PROTOCOL "%u\">", img_id);
+ purple_notify_user_info_add_pair_html( info, _( "Photo" ), img_text );
g_free( img_text );
}
- if ( contact->statusMsg )
- purple_notify_user_info_add_pair( info, _( "Status Message" ), contact->statusMsg );
+ if ( contact->statusMsg ) {
+ /* 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( info, _( "Status Message" ), contact->statusMsg );
+ }
}
}
@@ -317,6 +330,7 @@ void mxit_show_search_results( struct MXitSession* session, int searchType, int
/* define columns */
column = purple_notify_searchresults_column_new( _( "UserId" ) );
+ purple_notify_searchresult_column_set_visible( column, FALSE );
purple_notify_searchresults_column_add( results, column );
column = purple_notify_searchresults_column_new( _( "Display Name" ) );
purple_notify_searchresults_column_add( results, column );
diff --git a/libpurple/protocols/mxit/protocol.c b/libpurple/protocols/mxit/protocol.c
index 88f52b3866..6d1c0573eb 100644
--- a/libpurple/protocols/mxit/protocol.c
+++ b/libpurple/protocols/mxit/protocol.c
@@ -74,7 +74,7 @@ gint64 mxit_now_milli( void )
void mxit_popup( int type, const char* heading, const char* message )
{
/* (reference: "libpurple/notify.h") */
- purple_notify_message( NULL, type, _( MXIT_POPUP_WIN_NAME ), heading, message, NULL, NULL );
+ purple_notify_message( NULL, type, _( MXIT_POPUP_WIN_NAME ), heading, message, NULL, NULL, NULL );
}
@@ -256,126 +256,97 @@ static int mxit_write_sock_packet( int fd, const char* pktdata, int pktlen )
}
-/*------------------------------------------------------------------------
+/**
* Callback called for handling a HTTP GET response
*
- * @param url_data libPurple internal object (see purple_util_fetch_url_request)
- * @param user_data The MXit session object
- * @param url_text The data returned (could be NULL if error)
- * @param len The length of the data returned (0 if error)
- * @param error_message Descriptive error message
+ * @param http_conn http api object (see http.h)
+ * @param response http api object (see http.h)
+ * @param _session The MXit session object
*/
-static void mxit_cb_http_rx( PurpleUtilFetchUrlData* url_data, gpointer user_data, const gchar* url_text, gsize len, const gchar* error_message )
+static void
+mxit_cb_http_rx(PurpleHttpConnection *http_conn, PurpleHttpResponse *response,
+ gpointer _session)
{
- struct MXitSession* session = (struct MXitSession*) user_data;
+ struct MXitSession *session = _session;
+ const gchar *got_data;
+ size_t got_len;
- /* clear outstanding request */
- session->async_calls = g_slist_remove( session->async_calls, url_data );
-
- if ( ( !url_text ) || ( len == 0 ) ) {
- /* error with request */
- purple_debug_error( MXIT_PLUGIN_ID, "HTTP response error (%s)\n", error_message );
+ if (!purple_http_response_is_successful(response)) {
+ purple_debug_error(MXIT_PLUGIN_ID, "HTTP response error (%s)\n",
+ purple_http_response_get_error(response));
return;
}
/* convert the HTTP result */
- memcpy( session->rx_dbuf, url_text, len );
- session->rx_i = len;
+ got_data = purple_http_response_get_data(response, &got_len);
+ memcpy(session->rx_dbuf, got_data, got_len);
+ session->rx_i = got_len;
- mxit_parse_packet( session );
+ mxit_parse_packet(session);
}
-/*------------------------------------------------------------------------
+/**
* TX Step 3: Write the packet data to the HTTP connection (GET style).
*
- * @param session The MXit session object
- * @param pktdata The packet data
- * @param pktlen The length of the packet data
- * @return Return -1 on error, otherwise 0
+ * @param session The MXit session object
+ * @param packet The packet data
*/
-static void mxit_write_http_get( struct MXitSession* session, struct tx_packet* packet )
+static void
+mxit_write_http_get(struct MXitSession* session, struct tx_packet* packet)
{
- PurpleUtilFetchUrlData* url_data;
- char* part = NULL;
- char* url = NULL;
+ PurpleHttpRequest *req;
+ char *part = NULL;
- if ( packet->datalen > 0 ) {
- char* tmp = NULL;
+ if (packet->datalen > 0) {
+ char *tmp;
- tmp = g_strndup( packet->data, packet->datalen );
- part = g_strdup( purple_url_encode( tmp ) );
- g_free( tmp );
+ tmp = g_strndup(packet->data, packet->datalen);
+ part = g_strdup(purple_url_encode(tmp));
+ g_free(tmp);
}
- url = g_strdup_printf( "%s?%s%s", session->http_server, purple_url_encode( packet->header ), ( !part ) ? "" : part );
+ req = purple_http_request_new(NULL);
+ purple_http_request_set_url_printf(req, "%s?%s%s", session->http_server,
+ purple_url_encode(packet->header), part ? part : "");
+ purple_http_request_header_set(req, "User-Agent", MXIT_HTTP_USERAGENT);
+ purple_http_connection_set_add(session->async_http_reqs,
+ purple_http_request(session->con, req, mxit_cb_http_rx,
+ session));
+ purple_http_request_unref(req);
-#ifdef DEBUG_PROTOCOL
- purple_debug_info( MXIT_PLUGIN_ID, "HTTP GET: '%s'\n", url );
-#endif
-
- /* send the HTTP request */
- url_data = purple_util_fetch_url_request( url, TRUE, MXIT_HTTP_USERAGENT, TRUE, NULL, FALSE, mxit_cb_http_rx, session );
- if ( url_data )
- session->async_calls = g_slist_prepend( session->async_calls, url_data );
-
- g_free( url );
- if ( part )
- g_free( part );
+ g_free(part);
}
-/*------------------------------------------------------------------------
+/**
* TX Step 3: Write the packet data to the HTTP connection (POST style).
*
- * @param session The MXit session object
- * @param pktdata The packet data
- * @param pktlen The length of the packet data
- * @return Return -1 on error, otherwise 0
+ * @param session The MXit session object
+ * @param packet The packet data
*/
-static void mxit_write_http_post( struct MXitSession* session, struct tx_packet* packet )
+static void
+mxit_write_http_post(struct MXitSession* session, struct tx_packet* packet)
{
- char request[256 + packet->datalen];
- int reqlen;
- char* host_name;
- int host_port;
- gboolean ok;
-
- /* extract the HTTP host name and host port number to connect to */
- ok = purple_url_parse( session->http_server, &host_name, &host_port, NULL, NULL, NULL );
- if ( !ok ) {
- purple_debug_error( MXIT_PLUGIN_ID, "HTTP POST error: (host name '%s' not valid)\n", session->http_server );
- }
+ PurpleHttpRequest *req;
/* strip off the last '&' from the header */
packet->header[packet->headerlen - 1] = '\0';
packet->headerlen--;
- /* build the HTTP request packet */
- reqlen = g_snprintf( request, 256,
- "POST %s?%s HTTP/1.1\r\n"
- "User-Agent: " MXIT_HTTP_USERAGENT "\r\n"
- "Content-Type: application/octet-stream\r\n"
- "Host: %s\r\n"
- "Content-Length: %d\r\n"
- "\r\n",
- session->http_server,
- purple_url_encode( packet->header ),
- host_name,
- packet->datalen - MXIT_MS_OFFSET
- );
-
- /* copy over the packet body data (could be binary) */
- memcpy( request + reqlen, packet->data + MXIT_MS_OFFSET, packet->datalen - MXIT_MS_OFFSET );
- reqlen += packet->datalen;
-
-#ifdef DEBUG_PROTOCOL
- purple_debug_info( MXIT_PLUGIN_ID, "HTTP POST:\n" );
- dump_bytes( session, request, reqlen );
-#endif
-
- /* send the request to the HTTP server */
- mxit_http_send_request( session, host_name, host_port, request, reqlen );
+ req = purple_http_request_new(NULL);
+ purple_http_request_set_url_printf(req, "%s?%s", session->http_server,
+ purple_url_encode(packet->header));
+ purple_http_request_set_method(req, "POST");
+ purple_http_request_header_set(req, "User-Agent", MXIT_HTTP_USERAGENT);
+ purple_http_request_header_set(req, "Content-Type",
+ "application/octet-stream");
+ purple_http_request_set_contents(req, packet->data + MXIT_MS_OFFSET,
+ packet->datalen - MXIT_MS_OFFSET);
+ purple_http_connection_set_add(session->async_http_reqs,
+ purple_http_request(session->con, req, mxit_cb_http_rx,
+ session));
+ purple_http_request_unref(req);
}
@@ -414,7 +385,7 @@ static void mxit_send_packet( struct MXitSession* session, struct tx_packet* pac
res = mxit_write_sock_packet( session->fd, data, datalen );
if ( res < 0 ) {
/* we must have lost the connection, so terminate it so that we can reconnect */
- purple_connection_error( session->con, _( "We have lost the connection to MXit. Please reconnect." ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "We have lost the connection to MXit. Please reconnect." ) );
}
}
else {
@@ -536,7 +507,7 @@ static void mxit_manage_queue( struct MXitSession* session )
if ( session->last_tx <= mxit_now_milli() - ( MXIT_ACK_TIMEOUT * 1000 ) ) {
/* ack timeout! so we close the connection here */
purple_debug_info( MXIT_PLUGIN_ID, "mxit_manage_queue: Timeout awaiting ACK for command '%i'\n", session->outack );
- purple_connection_error( session->con, _( "Timeout while waiting for a response from the MXit server." ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Timeout while waiting for a response from the MXit server." ) );
}
return;
}
@@ -1473,7 +1444,7 @@ static void mxit_parse_cmd_login( struct MXitSession* session, struct record** r
/* we were not yet logged in so we need to complete the login sequence here */
session->flags |= MXIT_FLAG_LOGGEDIN;
purple_connection_update_progress( session->con, _( "Successfully Logged In..." ), 3, 4 );
- purple_connection_set_state( session->con, PURPLE_CONNECTED );
+ purple_connection_set_state( session->con, PURPLE_CONNECTION_CONNECTED );
/* save extra info if this is a HTTP connection */
if ( session->http ) {
@@ -1564,7 +1535,7 @@ static void mxit_parse_cmd_message( struct MXitSession* session, struct record**
const char* name;
char msg[128];
- buddy = purple_find_buddy( session->acc, sender );
+ buddy = purple_blist_find_buddy( session->acc, sender );
if ( buddy )
name = purple_buddy_get_alias( buddy );
else
@@ -1574,13 +1545,12 @@ static void mxit_parse_cmd_message( struct MXitSession* session, struct record**
return;
}
else if ( msgflags & CP_MSG_TL_ENCRYPTED ) {
- /* this is a transport-layer encrypted message. */
- message = mxit_decrypt_message( session, message );
- if ( !message ) {
- /* could not be decrypted */
- serv_got_im( session->con, sender, _( "An encrypted message was received which could not be decrypted." ), PURPLE_MESSAGE_ERROR, time( NULL ) );
- return;
- }
+ /* This is a transport-layer encrypted message. We don't support
+ * it anymore, because original client doesn't look like it was. */
+ purple_serv_got_im(session->con, sender,
+ _("An encrypted message was received which could not be decrypted."),
+ PURPLE_MESSAGE_ERROR, time(NULL));
+ return;
}
if ( msgflags & CP_MSG_NOTIFY_DELIVERY ) {
@@ -1664,7 +1634,7 @@ static void mxit_parse_cmd_new_sub( struct MXitSession* session, struct record**
if ( rec->fcount >= 5 ) {
/* there is a personal invite message attached */
- if ( ( rec->fields[4]->data ) && ( strlen( rec->fields[4]->data ) > 0 ) )
+ if ( ( rec->fields[4]->data ) && ( *rec->fields[4]->data ) )
contact->msg = strdup( rec->fields[4]->data );
}
@@ -1934,7 +1904,7 @@ static void mxit_parse_cmd_extprofile( struct MXitSession* session, struct recor
contact = get_mxit_invite_contact( session, mxitId );
if ( contact ) {
/* this is an invite, so update its profile info */
- if ( ( statusMsg ) && ( strlen( statusMsg ) > 0 ) ) {
+ if ( ( statusMsg ) && ( *statusMsg ) ) {
/* update the status message */
if ( contact->statusMsg )
g_free( contact->statusMsg );
@@ -1945,7 +1915,7 @@ static void mxit_parse_cmd_extprofile( struct MXitSession* session, struct recor
if ( contact->profile )
g_free( contact->profile );
contact->profile = profile;
- if ( ( avatarId ) && ( strlen( avatarId ) > 0 ) ) {
+ if ( ( avatarId ) && ( *avatarId ) ) {
/* avatar must be requested for this invite before we can display it */
mxit_get_avatar( session, mxitId, avatarId );
if ( contact->avatarId )
@@ -1963,11 +1933,11 @@ static void mxit_parse_cmd_extprofile( struct MXitSession* session, struct recor
if ( avatarId )
mxit_update_buddy_avatar( session, mxitId, avatarId );
- if ( ( statusMsg ) && ( strlen( statusMsg ) > 0 ) ) {
+ if ( ( statusMsg ) && ( *statusMsg ) ) {
/* update the status message */
PurpleBuddy* buddy = NULL;
- buddy = purple_find_buddy( session->acc, mxitId );
+ buddy = purple_blist_find_buddy( session->acc, mxitId );
if ( buddy ) {
contact = purple_buddy_get_protocol_data( buddy );
if ( contact ) {
@@ -2097,11 +2067,11 @@ static void mxit_parse_cmd_msgevent( struct MXitSession* session, struct record*
switch ( event ) {
case CP_MSGEVENT_TYPING : /* user is typing */
case CP_MSGEVENT_ANGRY : /* user is typing angrily */
- serv_got_typing( session->con, records[0]->fields[0]->data, 0, PURPLE_TYPING );
+ purple_serv_got_typing( session->con, records[0]->fields[0]->data, 0, PURPLE_IM_TYPING );
break;
case CP_MSGEVENT_STOPPED : /* user has stopped typing */
- serv_got_typing_stopped( session->con, records[0]->fields[0]->data );
+ purple_serv_got_typing_stopped( session->con, records[0]->fields[0]->data );
break;
case CP_MSGEVENT_ERASING : /* user is erasing text */
@@ -2123,11 +2093,12 @@ static void mxit_parse_cmd_msgevent( struct MXitSession* session, struct record*
*/
static int get_chunk_len( const char* chunkdata )
{
- int* sizeptr;
+ guint32 size_val;
- sizeptr = (int*) &chunkdata[1]; /* we skip the first byte (type field) */
+ /* we skip the first byte (type field) */
+ memcpy(&size_val, &chunkdata[1], sizeof(size_val));
- return ntohl( *sizeptr );
+ return ntohl(size_val);
}
@@ -2221,7 +2192,10 @@ static void mxit_parse_cmd_media( struct MXitSession* session, struct record** r
contact = get_mxit_invite_contact( session, chunk.mxitid );
if ( contact ) {
/* this is an invite (add image to the internal image store) */
- contact->imgid = purple_imgstore_add_with_id( g_memdup( chunk.data, chunk.length ), chunk.length, NULL );
+ if (contact->image)
+ g_object_unref(contact->image);
+ contact->image = purple_image_new_from_data(
+ g_memdup(chunk.data, chunk.length), chunk.length);
/* show the profile */
mxit_show_profile( session, chunk.mxitid, contact->profile );
}
@@ -2288,7 +2262,7 @@ static void mxit_perform_redirect( struct MXitSession* session, const char* url
session->port = atoi( host[2] );
}
else {
- purple_connection_error( session->con, _( "Cannot perform redirect using the specified protocol" ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Cannot perform redirect using the specified protocol" ) );
goto redirect_fail;
}
@@ -2462,7 +2436,7 @@ static int process_error_response( struct MXitSession* session, struct rx_packet
if ( packet->errcode == MXIT_ERRCODE_LOGGEDOUT ) {
/* we are not currently logged in, so we need to reconnect */
- purple_connection_error( session->con, _( errdesc ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( errdesc ) );
}
/* packet command */
@@ -2476,12 +2450,12 @@ static int process_error_response( struct MXitSession* session, struct rx_packet
}
else {
g_snprintf( errmsg, sizeof( errmsg ), _( "Login error: %s (%i)" ), errdesc, packet->errcode );
- purple_connection_error( session->con, errmsg );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, errmsg );
return -1;
}
case CP_CMD_LOGOUT :
g_snprintf( errmsg, sizeof( errmsg ), _( "Logout error: %s (%i)" ), errdesc, packet->errcode );
- purple_connection_error_reason( session->con, PURPLE_CONNECTION_ERROR_NAME_IN_USE, _( errmsg ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NAME_IN_USE, _( errmsg ) );
return -1;
case CP_CMD_CONTACT :
mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Contact Error" ), _( errdesc ) );
@@ -2744,7 +2718,7 @@ int mxit_parse_packet( struct MXitSession* session )
if ( packet.rcount < 2 ) {
/* bad packet */
- purple_connection_error( session->con, _( "Invalid packet received from MXit." ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Invalid packet received from MXit." ) );
free_rx_packet( &packet );
continue;
}
@@ -2809,12 +2783,12 @@ void mxit_cb_rx( gpointer user_data, gint source, PurpleInputCondition cond )
len = read( session->fd, &ch, 1 );
if ( len < 0 ) {
/* connection error */
- purple_connection_error( session->con, _( "A connection error occurred to MXit. (read stage 0x01)" ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x01)" ) );
return;
}
else if ( len == 0 ) {
/* connection closed */
- purple_connection_error( session->con, _( "A connection error occurred to MXit. (read stage 0x02)" ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x02)" ) );
return;
}
else {
@@ -2824,7 +2798,7 @@ void mxit_cb_rx( gpointer user_data, gint source, PurpleInputCondition cond )
session->rx_lbuf[session->rx_i] = '\0';
session->rx_res = atoi( &session->rx_lbuf[3] );
if ( session->rx_res > CP_MAX_PACKET ) {
- purple_connection_error( session->con, _( "A connection error occurred to MXit. (read stage 0x03)" ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x03)" ) );
}
session->rx_state = RX_STATE_DATA;
session->rx_i = 0;
@@ -2835,7 +2809,7 @@ void mxit_cb_rx( gpointer user_data, gint source, PurpleInputCondition cond )
session->rx_i++;
if ( session->rx_i >= sizeof( session->rx_lbuf ) ) {
/* malformed packet length record (too long) */
- purple_connection_error( session->con, _( "A connection error occurred to MXit. (read stage 0x04)" ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x04)" ) );
return;
}
}
@@ -2846,12 +2820,12 @@ void mxit_cb_rx( gpointer user_data, gint source, PurpleInputCondition cond )
len = read( session->fd, &session->rx_dbuf[session->rx_i], session->rx_res );
if ( len < 0 ) {
/* connection error */
- purple_connection_error( session->con, _( "A connection error occurred to MXit. (read stage 0x05)" ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x05)" ) );
return;
}
else if ( len == 0 ) {
/* connection closed */
- purple_connection_error( session->con, _( "A connection error occurred to MXit. (read stage 0x06)" ) );
+ purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x06)" ) );
return;
}
else {
@@ -2903,15 +2877,13 @@ void mxit_close_connection( struct MXitSession* session )
session->flags &= ~MXIT_FLAG_CONNECTED;
/* cancel all outstanding async calls */
- while ( session->async_calls ) {
- purple_util_fetch_url_cancel( session->async_calls->data );
- session->async_calls = g_slist_delete_link( session->async_calls, session->async_calls );
- }
+ purple_http_connection_set_destroy(session->async_http_reqs);
+ session->async_http_reqs = NULL;
/* remove the input cb function */
- if ( session->con->inpa ) {
- purple_input_remove( session->con->inpa );
- session->con->inpa = 0;
+ if ( session->inpa ) {
+ purple_input_remove( session->inpa );
+ session->inpa = 0;
}
/* remove HTTP poll timer */
@@ -2960,6 +2932,8 @@ void mxit_close_connection( struct MXitSession* session )
g_free( contact->statusMsg );
if ( contact->profile )
g_free( contact->profile );
+ if (contact->image)
+ g_object_unref(contact->image);
g_free( contact );
}
g_list_free( session->invites );
diff --git a/libpurple/protocols/mxit/roster.c b/libpurple/protocols/mxit/roster.c
index e8f6c06ae4..657ebffabd 100644
--- a/libpurple/protocols/mxit/roster.c
+++ b/libpurple/protocols/mxit/roster.c
@@ -75,7 +75,7 @@ GList* mxit_status_types( PurpleAccount* account )
/* add mxit status (reference: "libpurple/status.h") */
type = purple_status_type_new_with_attrs( status->primitive, status->id, _( status->name ), TRUE, TRUE, FALSE,
- "message", _( "Message" ), purple_value_new( PURPLE_TYPE_STRING ),
+ "message", _( "Message" ), purple_value_new( G_TYPE_STRING ),
NULL );
statuslist = g_list_append( statuslist, type );
@@ -83,7 +83,7 @@ GList* mxit_status_types( PurpleAccount* account )
/* add Mood option */
type = purple_status_type_new_with_attrs( PURPLE_STATUS_MOOD, "mood", NULL, FALSE, TRUE, TRUE,
- PURPLE_MOOD_NAME, _( "Mood Name" ), purple_value_new( PURPLE_TYPE_STRING ),
+ PURPLE_MOOD_NAME, _( "Mood Name" ), purple_value_new( G_TYPE_STRING ),
NULL );
statuslist = g_list_append( statuslist, type );
@@ -377,17 +377,17 @@ void mxit_update_contact( struct MXitSession* session, struct contact* contact )
}
/* find or create a group for this contact */
- group = purple_find_group( contact->groupname );
+ group = purple_blist_find_group( contact->groupname );
if ( !group )
group = purple_group_new( contact->groupname );
/* see if the buddy is not in the group already */
- buddy = purple_find_buddy_in_group( session->acc, contact->username, group );
+ buddy = purple_blist_find_buddy_in_group( session->acc, contact->username, group );
if ( !buddy ) {
/* buddy not found in the group */
/* lets try finding him in all groups */
- buddy = purple_find_buddy( session->acc, contact->username );
+ buddy = purple_blist_find_buddy( session->acc, contact->username );
if ( buddy ) {
/* ok, so we found him in another group. to switch him between groups we must delete him and add him again. */
purple_blist_remove_buddy( buddy );
@@ -407,7 +407,7 @@ void mxit_update_contact( struct MXitSession* session, struct contact* contact )
gpointer data = NULL;
/* now update the buddy's alias */
- purple_blist_alias_buddy( buddy, contact->alias );
+ purple_buddy_set_local_alias( buddy, contact->alias );
/* replace the buddy's contact struct */
if ( ( data = purple_buddy_get_protocol_data( buddy ) ) )
@@ -458,8 +458,8 @@ void mxit_update_buddy_presence( struct MXitSession* session, const char* userna
return; /* ignore packet */
}
- /* find the buddy information for this contact (reference: "libpurple/blist.h") */
- buddy = purple_find_buddy( session->acc, username );
+ /* find the buddy information for this contact (reference: "libpurple/buddylist.h") */
+ buddy = purple_blist_find_buddy( session->acc, username );
if ( !buddy ) {
purple_debug_warning( MXIT_PLUGIN_ID, "mxit_update_buddy_presence: unable to find the buddy '%s'\n", username );
return;
@@ -517,8 +517,8 @@ void mxit_update_buddy_avatar( struct MXitSession* session, const char* username
purple_debug_info( MXIT_PLUGIN_ID, "mxit_update_buddy_avatar: user='%s' avatar='%s'\n", username, avatarId );
- /* find the buddy information for this contact (reference: "libpurple/blist.h") */
- buddy = purple_find_buddy( session->acc, username );
+ /* find the buddy information for this contact (reference: "libpurple/buddylist.h") */
+ buddy = purple_blist_find_buddy( session->acc, username );
if ( !buddy ) {
purple_debug_warning( MXIT_PLUGIN_ID, "mxit_update_buddy_presence: unable to find the buddy '%s'\n", username );
return;
@@ -558,7 +558,7 @@ void mxit_update_blist( struct MXitSession* session )
/* remove all buddies we did not receive a roster update for.
* these contacts must have been removed from another client */
- list = purple_find_buddies( session->acc, NULL );
+ list = purple_blist_find_buddies( session->acc, NULL );
for ( i = 0; i < g_slist_length( list ); i++ ) {
buddy = g_slist_nth_data( list, i );
@@ -583,7 +583,7 @@ void mxit_update_blist( struct MXitSession* session )
*
* @param user_data Object associated with the invite
*/
-static void mxit_cb_buddy_auth( gpointer user_data )
+static void mxit_cb_buddy_auth( const char *message, gpointer user_data )
{
struct contact_invite* invite = (struct contact_invite*) user_data;
@@ -602,6 +602,8 @@ static void mxit_cb_buddy_auth( gpointer user_data )
g_free( invite->contact->statusMsg );
if ( invite->contact->profile )
g_free( invite->contact->profile );
+ if (invite->contact->image)
+ g_object_unref(invite->contact->image);
g_free( invite->contact );
g_free( invite );
}
@@ -612,14 +614,14 @@ static void mxit_cb_buddy_auth( gpointer user_data )
*
* @param user_data Object associated with the invite
*/
-static void mxit_cb_buddy_deny( gpointer user_data )
+static void mxit_cb_buddy_deny( const char *message, gpointer user_data )
{
struct contact_invite* invite = (struct contact_invite*) user_data;
purple_debug_info( MXIT_PLUGIN_ID, "mxit_cb_buddy_deny '%s'\n", invite->contact->username );
/* send a deny subscription packet to MXit */
- mxit_send_deny_sub( invite->session, invite->contact->username, NULL );
+ mxit_send_deny_sub( invite->session, invite->contact->username, message );
/* remove the invite from our internal invites list */
invite->session->invites = g_list_remove( invite->session->invites, invite->contact );
@@ -631,6 +633,8 @@ static void mxit_cb_buddy_deny( gpointer user_data )
g_free( invite->contact->statusMsg );
if ( invite->contact->profile )
g_free( invite->contact->profile );
+ if (invite->contact->image)
+ g_object_unref(invite->contact->image);
g_free( invite->contact );
g_free( invite );
}
@@ -700,7 +704,7 @@ gboolean is_mxit_chatroom_contact( struct MXitSession* session, const char* user
struct contact* contact = NULL;
/* find the buddy */
- buddy = purple_find_buddy( session->acc, username );
+ buddy = purple_blist_find_buddy( session->acc, username );
if ( !buddy ) {
purple_debug_warning( MXIT_PLUGIN_ID, "is_mxit_chatroom_contact: unable to find the buddy '%s'\n", username );
return FALSE;
@@ -738,7 +742,7 @@ void mxit_add_buddy( PurpleConnection* gc, PurpleBuddy* buddy, PurpleGroup* grou
purple_debug_info( MXIT_PLUGIN_ID, "mxit_add_buddy '%s' (group='%s')\n", buddy_name, group_name );
- list = purple_find_buddies( session->acc, buddy_name );
+ list = purple_blist_find_buddies( session->acc, buddy_name );
if ( g_slist_length( list ) == 1 ) {
purple_debug_info( MXIT_PLUGIN_ID, "mxit_add_buddy (scenario 1) (list:%i)\n", g_slist_length( list ) );
/*
@@ -773,7 +777,7 @@ void mxit_add_buddy( PurpleConnection* gc, PurpleBuddy* buddy, PurpleGroup* grou
/* this is our REAL MXit buddy! */
/* now update the buddy's alias */
- purple_blist_alias_buddy( mxbuddy, buddy_alias );
+ purple_buddy_set_local_alias( mxbuddy, buddy_alias );
/* now update the buddy's group */
// mxbuddy = mxit_update_buddy_group( session, mxbuddy, group );
@@ -831,7 +835,7 @@ void mxit_buddy_alias( PurpleConnection* gc, const char* who, const char* alias
purple_debug_info( MXIT_PLUGIN_ID, "mxit_buddy_alias '%s' to '%s\n", who, alias );
/* find the buddy */
- buddy = purple_find_buddy( session->acc, who );
+ buddy = purple_blist_find_buddy( session->acc, who );
if ( !buddy ) {
purple_debug_warning( MXIT_PLUGIN_ID, "mxit_buddy_alias: unable to find the buddy '%s'\n", who );
return;
@@ -864,7 +868,7 @@ void mxit_buddy_group( PurpleConnection* gc, const char* who, const char* old_gr
purple_debug_info( MXIT_PLUGIN_ID, "mxit_buddy_group from '%s' to '%s'\n", old_group, new_group );
/* find the buddy */
- buddy = purple_find_buddy( session->acc, who );
+ buddy = purple_blist_find_buddy( session->acc, who );
if ( !buddy ) {
purple_debug_warning( MXIT_PLUGIN_ID, "mxit_buddy_group: unable to find the buddy '%s'\n", who );
return;
diff --git a/libpurple/protocols/mxit/roster.h b/libpurple/protocols/mxit/roster.h
index 055bc22969..067447e75a 100644
--- a/libpurple/protocols/mxit/roster.h
+++ b/libpurple/protocols/mxit/roster.h
@@ -125,7 +125,8 @@ struct contact {
/* invites only */
void* profile; /* user's profile (if available) */
- int imgid; /* avatar image id in the imgstore */
+
+ PurpleImage *image; /* avatar image */
};
/* Presence / Status */
diff --git a/libpurple/protocols/mxit/splashscreen.c b/libpurple/protocols/mxit/splashscreen.c
index 655cfd4879..4ffbfb9346 100644
--- a/libpurple/protocols/mxit/splashscreen.c
+++ b/libpurple/protocols/mxit/splashscreen.c
@@ -25,7 +25,6 @@
#include "internal.h"
#include "debug.h"
-#include "imgstore.h"
#include "request.h"
#include "protocol.h"
@@ -157,7 +156,6 @@ static void splash_click_ok(PurpleConnection* gc, PurpleRequestFields* fields)
mxit_send_splashclick(session, splashId);
}
-
/*------------------------------------------------------------------------
* Display the current splash-screen.
*
@@ -169,7 +167,6 @@ void splash_display(struct MXitSession* session)
char* filename;
gchar* imgdata;
gsize imglen;
- int imgid = -1;
/* Get current splash ID */
splashId = splash_current(session);
@@ -181,13 +178,20 @@ void splash_display(struct MXitSession* session)
/* Load splash-screen image from file */
filename = g_strdup_printf("%s" G_DIR_SEPARATOR_S "mxit" G_DIR_SEPARATOR_S "%s.png", purple_user_dir(), splashId);
if (g_file_get_contents(filename, &imgdata, &imglen, NULL)) {
+#if 0
+ PurpleImage *img;
+ guint img_id;
char buf[128];
/* Add splash-image to imagestore */
- imgid = purple_imgstore_add_with_id(g_memdup(imgdata, imglen), imglen, NULL);
+ img = purple_image_new_from_data(g_memdup(imgdata, imglen), imglen);
+ img_id = purple_image_store_add_temporary(img);
+ g_object_unref(img);
/* Generate and display message */
- g_snprintf(buf, sizeof(buf), "<img id=\"%d\">", imgid);
+ g_snprintf(buf, sizeof(buf), "<img src=\""
+ PURPLE_IMAGE_STORE_PROTOCOL "%u\">", img_id);
+#endif
/* Open a request-type popup to display the image */
{
@@ -204,17 +208,14 @@ void splash_display(struct MXitSession* session)
if (splash_clickable(session)) {
purple_request_fields(session->con, _("MXit Advertising"), NULL, NULL, fields,
- _("More Information"), G_CALLBACK(splash_click_ok), _("Close"), NULL, session->acc, NULL, NULL, session->con);
+ _("More Information"), G_CALLBACK(splash_click_ok), _("Close"), NULL, purple_request_cpar_from_account(session->acc), session->con);
}
else {
purple_request_fields(session->con, _("MXit Advertising"), NULL, NULL, fields,
- _("Continue"), G_CALLBACK(splash_click_ok), _("Close"), NULL, session->acc, NULL, NULL, session->con);
+ _("Continue"), G_CALLBACK(splash_click_ok), _("Close"), NULL, purple_request_cpar_from_account(session->acc), session->con);
}
}
- /* Release reference to image */
- purple_imgstore_unref_by_id(imgid);
-
g_free(imgdata);
}
diff --git a/libpurple/protocols/mxit/voicevideo.c b/libpurple/protocols/mxit/voicevideo.c
index 635aadb0a3..a254044b85 100644
--- a/libpurple/protocols/mxit/voicevideo.c
+++ b/libpurple/protocols/mxit/voicevideo.c
@@ -72,11 +72,11 @@ PurpleMediaCaps mxit_media_caps(PurpleAccount *account, const char *who)
purple_debug_info(MXIT_PLUGIN_ID, "mxit_media_caps: buddy '%s'\n", who);
/* We need to have a voice/video server */
- if (strlen(session->voip_server) == 0)
+ if (!*session->voip_server)
return PURPLE_MEDIA_CAPS_NONE;
- /* find the buddy information for this contact (reference: "libpurple/blist.h") */
- buddy = purple_find_buddy(account, who);
+ /* find the buddy information for this contact (reference: "libpurple/buddylist.h") */
+ buddy = purple_blist_find_buddy(account, who);
if (!buddy) {
purple_debug_warning(MXIT_PLUGIN_ID, "mxit_media_caps: unable to find the buddy '%s'\n", who);
return PURPLE_MEDIA_CAPS_NONE;
diff --git a/libpurple/protocols/myspace/Makefile.am b/libpurple/protocols/myspace/Makefile.am
deleted file mode 100644
index 715cabc875..0000000000
--- a/libpurple/protocols/myspace/Makefile.am
+++ /dev/null
@@ -1,44 +0,0 @@
-EXTRA_DIST = \
- Makefile.mingw
-
-pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
-
-MSIMSOURCES = markup.c \
- markup.h \
- message.c \
- message.h \
- myspace.c \
- myspace.h \
- persist.h \
- session.c \
- session.h \
- user.c \
- user.h \
- zap.c \
- zap.h
-
-AM_CFLAGS = $(st)
-
-libmyspace_la_LDFLAGS = -module -avoid-version
-
-if STATIC_MYSPACE
-
-st = -DPURPLE_STATIC_PRPL
-noinst_LTLIBRARIES = libmyspace.la
-libmyspace_la_SOURCES = $(MSIMSOURCES)
-libmyspace_la_CFLAGS = $(AM_CFLAGS)
-
-else
-
-st =
-pkg_LTLIBRARIES = libmyspace.la
-libmyspace_la_SOURCES = $(MSIMSOURCES)
-libmyspace_la_LIBADD = $(GLIB_LIBS)
-
-endif
-
-AM_CPPFLAGS = \
- -I$(top_srcdir)/libpurple \
- -I$(top_builddir)/libpurple \
- $(GLIB_CFLAGS) \
- $(DEBUG_CFLAGS)
diff --git a/libpurple/protocols/myspace/Makefile.mingw b/libpurple/protocols/myspace/Makefile.mingw
deleted file mode 100644
index 0fc7534760..0000000000
--- a/libpurple/protocols/myspace/Makefile.mingw
+++ /dev/null
@@ -1,81 +0,0 @@
-#
-# Makefile.mingw
-#
-# Description: Makefile for win32 (mingw) version of libmyspace
-#
-
-PIDGIN_TREE_TOP := ../../..
-include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
-
-TARGET = libmyspace
-TYPE = PLUGIN
-
-# Static or Plugin...
-ifeq ($(TYPE),STATIC)
- DEFINES += -DSTATIC
- DLL_INSTALL_DIR = $(PURPLE_INSTALL_DIR)
-else
-ifeq ($(TYPE),PLUGIN)
- DLL_INSTALL_DIR = $(PURPLE_INSTALL_PLUGINS_DIR)
-endif
-endif
-
-##
-## INCLUDE PATHS
-##
-INCLUDE_PATHS += -I. \
- -I$(GTK_TOP)/include \
- -I$(GTK_TOP)/include/glib-2.0 \
- -I$(GTK_TOP)/lib/glib-2.0/include \
- -I$(PURPLE_TOP) \
- -I$(PURPLE_TOP)/win32 \
- -I$(PIDGIN_TREE_TOP)
-
-LIB_PATHS = -L$(GTK_TOP)/lib \
- -L$(PURPLE_TOP)
-
-##
-## SOURCES, OBJECTS
-##
-C_SRC = myspace.c message.c zap.c session.c markup.c user.c
-
-OBJECTS = $(C_SRC:%.c=%.o)
-
-##
-## LIBRARIES
-##
-LIBS = \
- -lglib-2.0 \
- -lws2_32 \
- -lintl \
- -lpurple
-
-include $(PIDGIN_COMMON_RULES)
-
-##
-## TARGET DEFINITIONS
-##
-.PHONY: all install clean
-
-all: $(TARGET).dll
-
-install: all $(DLL_INSTALL_DIR)
- cp $(TARGET).dll $(DLL_INSTALL_DIR)
-
-$(OBJECTS): $(PURPLE_CONFIG_H)
-
-##
-## BUILD DLL
-##
-$(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS)
- $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET).dll
-
-##
-## CLEAN RULES
-##
-
-clean:
- rm -f $(OBJECTS)
- rm -f $(TARGET).dll
-
-include $(PIDGIN_COMMON_TARGETS)
diff --git a/libpurple/protocols/myspace/README b/libpurple/protocols/myspace/README
deleted file mode 100644
index f9f062c9e6..0000000000
--- a/libpurple/protocols/myspace/README
+++ /dev/null
@@ -1,27 +0,0 @@
-MySpaceIM Protocol Plugin for libpurple by Jeff Connelly 2007-08-07
-
-Greetings. This package contains a plugin for libpurple (as used in
-Pidgin, formerly Gaim) to connect to the new MySpaceIM instant messaging
-network and send/receive messages. Functionality is only basic as of yet,
-and this code should be considered alpha quality.
-
-This code was initially developed under Google Summer of Code 2007.
-
-For features and TODO, see http://developer.pidgin.im/wiki/MySpaceIM
-
-Usage:
-
-Login using your _email address_ you use to login to myspace.com. You can't
-login using your numeric ID or alias.
-
-To test it out, send a message to yourself (by your username or numeric
-uid (email not yet supported)) or tom (6221). In either case you should
-get a reply. You should also be able to talk to other MySpaceIM users if
-you desire. Replies will always be shown as coming from a user's username,
-even if you IM by email or userid.
-
-Feedback welcome. You can IM my test account at "msimprpl" if you feel like it.
-
-Enjoy,
--Jeff Connelly
-msimprpl@xyzzy.cjb.net
diff --git a/libpurple/protocols/myspace/markup.c b/libpurple/protocols/myspace/markup.c
deleted file mode 100644
index 05d473e130..0000000000
--- a/libpurple/protocols/myspace/markup.c
+++ /dev/null
@@ -1,763 +0,0 @@
-/* MySpaceIM Protocol Plugin - markup
- *
- * Copyright (C) 2007, Jeff Connelly <jeff2@soc.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
- */
-
-#include "myspace.h"
-
-typedef int (*MSIM_XMLNODE_CONVERT)(MsimSession *, xmlnode *, gchar **, gchar **);
-
-/* Globals */
-
-/* The names in in emoticon_names (for <i n=whatever>) map to corresponding
- * entries in emoticon_symbols (for the ASCII representation of the emoticon).
- *
- * Multiple emoticon symbols in Pidgin can map to one name. List the
- * canonical form, as inserted by the "Smile!" dialog, first. For example,
- * :) comes before :-), because although both are recognized as 'happy',
- * the first is inserted by the smiley button (first symbol in theme).
- *
- * Note that symbols are case-sensitive in Pidgin -- :-X is not :-x. */
-static struct MSIM_EMOTICON
-{
- gchar *name;
- gchar *symbol;
-} msim_emoticons[] = {
- /* Unfortunately, this list duplicates much of the file
- * pidgin/pidgin/pixmaps/emotes/default/22/default.theme.in, because
- * that file is part of Pidgin, but we're part of libpurple.
- */
- { "bigsmile", ":D" },
- { "bigsmile", ":-D" },
- { "devil", "}:)" },
- { "frazzled", ":Z" },
- { "geek", "B)" },
- { "googles", "%)" },
- { "growl", ":E" },
- { "laugh", ":))" }, /* Must be before ':)' */
- { "happy", ":)" },
- { "happy", ":-)" },
- { "happi", ":)" },
- { "heart", ":X" },
- { "mohawk", "-:" },
- { "mad", "X(" },
- { "messed", "X)" },
- { "nerd", "Q)" },
- { "oops", ":G" },
- { "pirate", "P)" },
- { "scared", ":O" },
- { "sidefrown", ":{" },
- { "sinister", ":B" },
- { "smirk", ":," },
- { "straight", ":|" },
- { "tongue", ":P" },
- { "tongue", ":p" },
- { "tongy", ":P" },
- { "upset", "B|" },
- { "wink", ";-)" },
- { "wink", ";)" },
- { "winc", ";)" },
- { "worried", ":[" },
- { "kiss", ":x" },
- { NULL, NULL }
-};
-
-/* Indexes of this array + 1 map HTML font size to scale of normal font size. *
- * Based on _point_sizes from libpurple/gtkimhtml.c
- * 1 2 3 4 5 6 7 */
-static gdouble _font_scale[] = { .85, .95, 1, 1.2, 1.44, 1.728, 2.0736 };
-
-/* Purple maximum font size. Equivalent to sizeof(_font_scale) / sizeof(_font_scale[0]) */
-#define MAX_FONT_SIZE 7
-
-#define POINTS_PER_INCH 72 /* How many pt's in an inch */
-
-/* Text formatting bits for <f s=#> */
-#define MSIM_TEXT_BOLD 1
-#define MSIM_TEXT_ITALIC 2
-#define MSIM_TEXT_UNDERLINE 4
-
-/* Default baseline size of purple's fonts, in points. What is size 3 in points.
- * _font_scale specifies scaling factor relative to this point size. Note this
- * is only the default; it is configurable in account options. */
-#define MSIM_BASE_FONT_POINT_SIZE 8
-
-/* Default display's DPI. 96 is common but it can differ. Also configurable
- * in account options. */
-#define MSIM_DEFAULT_DPI 96
-
-/* round is part of C99, but sometimes is unavailable before then.
- * Based on http://forums.belution.com/en/cpp/000/050/13.shtml
- */
-static double msim_round(double value)
-{
- if (value < 0) {
- return -(floor(-value + 0.5));
- } else {
- return floor( value + 0.5);
- }
-}
-
-/**
- * Convert typographical font point size to HTML font size.
- * Based on libpurple/gtkimhtml.c
- */
-static guint
-msim_point_to_purple_size(MsimSession *session, guint point)
-{
- guint size, this_point, base;
-
- base = purple_account_get_int(session->account, "base_font_size", MSIM_BASE_FONT_POINT_SIZE);
-
- for (size = 0; size < MAX_FONT_SIZE; ++size) {
- this_point = (guint)msim_round(base * _font_scale[size]);
-
- if (this_point >= point) {
- purple_debug_info("msim", "msim_point_to_purple_size: %d pt -> size=%d\n",
- point, size);
- return size;
- }
- }
-
- /* No HTML font size was this big; return largest possible. */
- return this_point;
-}
-
-/**
- * Convert HTML font size to point size.
- */
-static guint
-msim_purple_size_to_point(MsimSession *session, guint size)
-{
- gdouble scale;
- guint point;
- guint base;
-
- scale = _font_scale[CLAMP(size, 1, MAX_FONT_SIZE) - 1];
-
- base = purple_account_get_int(session->account, "base_font_size", MSIM_BASE_FONT_POINT_SIZE);
-
- point = (guint)msim_round(scale * base);
-
- purple_debug_info("msim", "msim_purple_size_to_point: size=%d -> %d pt\n",
- size, point);
-
- return point;
-}
-
-/**
- * Convert a msim markup font pixel height to the more usual point size, for incoming messages.
- */
-static guint
-msim_height_to_point(MsimSession *session, guint height)
-{
- guint dpi;
-
- dpi = purple_account_get_int(session->account, "dpi", MSIM_DEFAULT_DPI);
-
- return (guint)msim_round((POINTS_PER_INCH * 1. / dpi) * height);
-
- /* See also: libpurple/protocols/bonjour/jabber.c
- * _font_size_ichat_to_purple */
-}
-
-/**
- * Convert point size to msim pixel height font size specification, for outgoing messages.
- */
-static guint
-msim_point_to_height(MsimSession *session, guint point)
-{
- guint dpi;
-
- dpi = purple_account_get_int(session->account, "dpi", MSIM_DEFAULT_DPI);
-
- return (guint)msim_round((dpi * 1. / POINTS_PER_INCH) * point);
-}
-
-/**
- * Convert the msim markup <f> (font) tag into HTML.
- */
-static void
-msim_markup_f_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
-{
- const gchar *face, *height_str, *decor_str;
- GString *gs_end, *gs_begin;
- guint decor, height;
-
- face = xmlnode_get_attrib(root, "f");
- height_str = xmlnode_get_attrib(root, "h");
- decor_str = xmlnode_get_attrib(root, "s");
-
- /* Validate the font face, to avoid constructing invalid HTML later */
- if (face != NULL && strchr(face, '\'') != NULL)
- face = NULL;
-
- height = height_str != NULL ? atol(height_str) : 12;
- decor = decor_str != NULL ? atol(decor_str) : 0;
-
- /*
- * The HTML we're constructing here is a bit redudant. Ideally we
- * would use only the font-family and font-size CSS span, but Pidgin
- * doesn't support it (it's included for other UIs). For Pidgin we
- * wrap the whole thing in an ugly font tag, and Pidgin will happily
- * ignore the <span>.
- */
- gs_begin = g_string_new("");
- if (height && !face) {
- guint point_size = msim_height_to_point(session, height);
- g_string_printf(gs_begin,
- "<font size='%d'><span style='font-size: %dpt'>",
- msim_point_to_purple_size(session, point_size),
- point_size);
- } else if (height && face) {
- guint point_size = msim_height_to_point(session, height);
- g_string_printf(gs_begin,
- "<font face='%s' size='%d'><span style='font-family: %s; font-size: %dpt'>",
- face, msim_point_to_purple_size(session, point_size),
- face, point_size);
- } else {
- g_string_printf(gs_begin, "<font><span>");
- }
-
- gs_end = g_string_new("</span></font>");
-
- if (decor & MSIM_TEXT_BOLD) {
- g_string_append(gs_begin, "<b>");
- g_string_prepend(gs_end, "</b>");
- }
-
- if (decor & MSIM_TEXT_ITALIC) {
- g_string_append(gs_begin, "<i>");
- g_string_append(gs_end, "</i>");
- }
-
- if (decor & MSIM_TEXT_UNDERLINE) {
- g_string_append(gs_begin, "<u>");
- g_string_append(gs_end, "</u>");
- }
-
- *begin = g_string_free(gs_begin, FALSE);
- *end = g_string_free(gs_end, FALSE);
-}
-
-/**
- * Convert a msim markup color to a color suitable for libpurple.
- *
- * @param msim Either a color name, or an rgb(x,y,z) code.
- *
- * @return A new string, either a color name or #rrggbb code. Must g_free().
- */
-static char *
-msim_color_to_purple(const char *msim)
-{
- guint red, green, blue;
-
- if (!msim) {
- return g_strdup("black");
- }
-
- if (sscanf(msim, "rgb(%d,%d,%d)", &red, &green, &blue) != 3) {
- /* Color name. */
- return g_strdup(msim);
- }
- /* TODO: rgba (alpha). */
-
- return g_strdup_printf("#%.2x%.2x%.2x", red, green, blue);
-}
-
-/**
- * Convert the msim markup <a> (anchor) tag into HTML.
- */
-static void
-msim_markup_a_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
-{
- const gchar *href;
-
- href = xmlnode_get_attrib(root, "h");
- if (!href) {
- href = "";
- }
-
- *begin = g_strdup_printf("<a href=\"%s\">%s", href, href);
- *end = g_strdup("</a>");
-}
-
-/**
- * Convert the msim markup <p> (paragraph) tag into HTML.
- */
-static void
-msim_markup_p_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
-{
- /* Just pass through unchanged.
- *
- * Note: attributes currently aren't passed, if there are any. */
- *begin = g_strdup("<p>");
- *end = g_strdup("</p>");
-}
-
-/**
- * Convert the msim markup <c> tag (text color) into HTML.
- */
-static void
-msim_markup_c_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
-{
- const gchar *color;
- gchar *purple_color;
-
- color = xmlnode_get_attrib(root, "v");
- if (!color) {
- purple_debug_info("msim", "msim_markup_c_to_html: <c> tag w/o v attr\n");
- *begin = g_strdup("");
- *end = g_strdup("");
- /* TODO: log as unrecognized */
- return;
- }
-
- purple_color = msim_color_to_purple(color);
-
-#ifdef USE_CSS_FORMATTING
- *begin = g_strdup_printf("<span style='color: %s'>", purple_color);
- *end = g_strdup("</span>");
-#else
- *begin = g_strdup_printf("<font color='%s'>", purple_color);
- *end = g_strdup("</font>");
-#endif
-
- g_free(purple_color);
-}
-
-/**
- * Convert the msim markup <b> tag (background color) into HTML.
- */
-static void
-msim_markup_b_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
-{
- const gchar *color;
- gchar *purple_color;
-
- color = xmlnode_get_attrib(root, "v");
- if (!color) {
- *begin = g_strdup("");
- *end = g_strdup("");
- purple_debug_info("msim", "msim_markup_b_to_html: <b> w/o v attr\n");
- /* TODO: log as unrecognized. */
- return;
- }
-
- purple_color = msim_color_to_purple(color);
-
-#ifdef USE_CSS_FORMATTING
- *begin = g_strdup_printf("<span style='background-color: %s'>", purple_color);
- *end = g_strdup("</span>");
-#else
- *begin = g_strdup_printf("<body bgcolor='%s'>", purple_color);
- *end = g_strdup("</body>");
-#endif
-
- g_free(purple_color);
-}
-
-/**
- * Convert the msim markup <i> tag (emoticon image) into HTML.
- */
-static void
-msim_markup_i_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
-{
- const gchar *name;
- guint i;
- struct MSIM_EMOTICON *emote;
-
- name = xmlnode_get_attrib(root, "n");
- if (!name) {
- purple_debug_info("msim", "msim_markup_i_to_html: <i> w/o n\n");
- *begin = g_strdup("");
- *end = g_strdup("");
- /* TODO: log as unrecognized */
- return;
- }
-
- /* Find and use canonical form of smiley symbol. */
- for (i = 0; (emote = &msim_emoticons[i]) && emote->name != NULL; ++i) {
- if (g_str_equal(name, emote->name)) {
- *begin = g_strdup(emote->symbol);
- *end = g_strdup("");
- return;
- }
- }
-
- /* Couldn't find it, sorry. Try to degrade gracefully. */
- *begin = g_strdup_printf("**%s**", name);
- *end = g_strdup("");
-}
-
-/**
- * Convert an individual msim markup tag to HTML.
- */
-static int
-msim_markup_tag_to_html(MsimSession *session, xmlnode *root, gchar **begin,
- gchar **end)
-{
- g_return_val_if_fail(root != NULL, 0);
-
- if (g_str_equal(root->name, "f")) {
- msim_markup_f_to_html(session, root, begin, end);
- } else if (g_str_equal(root->name, "a")) {
- msim_markup_a_to_html(session, root, begin, end);
- } else if (g_str_equal(root->name, "p")) {
- msim_markup_p_to_html(session, root, begin, end);
- } else if (g_str_equal(root->name, "c")) {
- msim_markup_c_to_html(session, root, begin, end);
- } else if (g_str_equal(root->name, "b")) {
- msim_markup_b_to_html(session, root, begin, end);
- } else if (g_str_equal(root->name, "i")) {
- msim_markup_i_to_html(session, root, begin, end);
- } else {
- purple_debug_info("msim", "msim_markup_tag_to_html: "
- "unknown tag name=%s, ignoring\n",
- root->name ? root->name : "(NULL)");
- *begin = g_strdup("");
- *end = g_strdup("");
- }
- return 0;
-}
-
-/**
- * Convert an individual HTML tag to msim markup.
- */
-static int
-html_tag_to_msim_markup(MsimSession *session, xmlnode *root, gchar **begin,
- gchar **end)
-{
- int ret = 0;
-
- if (!purple_utf8_strcasecmp(root->name, "root") ||
- !purple_utf8_strcasecmp(root->name, "html")) {
- *begin = g_strdup("");
- *end = g_strdup("");
- /* TODO: Coalesce nested tags into one <f> tag!
- * Currently, the 's' value will be overwritten when b/i/u is nested
- * within another one, and only the inner-most formatting will be
- * applied to the text. */
- } else if (!purple_utf8_strcasecmp(root->name, "b")) {
- if (root->child->type == XMLNODE_TYPE_DATA) {
- *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_BOLD);
- *end = g_strdup("</f>");
- } else {
- if (!purple_utf8_strcasecmp(root->child->name,"i")) {
- ret++;
- if (root->child->child->type == XMLNODE_TYPE_DATA) {
- *begin = g_strdup_printf("<f s='%d'>", (MSIM_TEXT_BOLD + MSIM_TEXT_ITALIC));
- *end = g_strdup("</f>");
- } else {
- if (!purple_utf8_strcasecmp(root->child->child->name,"u")) {
- ret++;
- *begin = g_strdup_printf("<f s='%d'>", (MSIM_TEXT_BOLD + MSIM_TEXT_ITALIC + MSIM_TEXT_UNDERLINE));
- *end = g_strdup("</f>");
- }
- }
- } else if (!purple_utf8_strcasecmp(root->child->name,"u")) {
- ret++;
- *begin = g_strdup_printf("<f s='%d'>", (MSIM_TEXT_BOLD + MSIM_TEXT_UNDERLINE));
- *end = g_strdup("</f>");
- }
- }
- } else if (!purple_utf8_strcasecmp(root->name, "i")) {
- if (root->child->type == XMLNODE_TYPE_DATA) {
- *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_ITALIC);
- *end = g_strdup("</f>");
- } else {
- if (!purple_utf8_strcasecmp(root->child->name,"u")) {
- ret++;
- *begin = g_strdup_printf("<f s='%d'>", (MSIM_TEXT_ITALIC + MSIM_TEXT_UNDERLINE));
- *end = g_strdup("</f>");
- }
- }
- } else if (!purple_utf8_strcasecmp(root->name, "u")) {
- *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_UNDERLINE);
- *end = g_strdup("</f>");
- } else if (!purple_utf8_strcasecmp(root->name, "a")) {
- const gchar *href;
- gchar *link_text;
-
- href = xmlnode_get_attrib(root, "href");
-
- if (!href) {
- href = xmlnode_get_attrib(root, "HREF");
- }
-
- link_text = xmlnode_get_data(root);
-
- if (href) {
- if (g_str_equal(link_text, href)) {
- /* Purple gives us: <a href="URL">URL</a>
- * Translate to <a h='URL' />
- * Displayed as text of URL with link to URL
- */
- *begin = g_strdup_printf("<a h='%s' />", href);
- } else {
- /* But if we get: <a href="URL">text</a>
- * Translate to: text: <a h='URL' />
- *
- * Because official client only supports self-closed <a>
- * tags; you can't change the link text.
- */
- *begin = g_strdup_printf("%s: <a h='%s' />", link_text, href);
- }
- } else {
- *begin = g_strdup("<a />");
- }
-
- /* Sorry, kid. MySpace doesn't support you within <a> tags. */
- xmlnode_free(root->child);
- g_free(link_text);
- root->child = NULL;
-
- *end = g_strdup("");
- } else if (!purple_utf8_strcasecmp(root->name, "font")) {
- GString *tmpbegin, *tmpend;
- const gchar *size;
- const gchar *face;
- const gchar *color;
-
- size = xmlnode_get_attrib(root, "size");
- face = xmlnode_get_attrib(root, "face");
- color = xmlnode_get_attrib(root, "color");
-
- tmpbegin = g_string_new("<f");
- tmpend = g_string_new("</f>");
-
- if (face != NULL)
- g_string_append_printf(tmpbegin, " f='%s'", face);
-
- if (size != NULL)
- g_string_append_printf(tmpbegin, " h='%d'",
- msim_point_to_height(session,
- msim_purple_size_to_point(session, atoi(size))));
-
- /* Close the <f> tag */
- g_string_append(tmpbegin, ">");
-
- if (color != NULL) {
- g_string_append_printf(tmpbegin, "<c v='%s'>", color);
- g_string_prepend(tmpend, "</c>");
- }
-
- *begin = g_string_free(tmpbegin, FALSE);
- *end = g_string_free(tmpend, FALSE);
-
- } else if (!purple_utf8_strcasecmp(root->name, "body")) {
- const gchar *bgcolor;
-
- bgcolor = xmlnode_get_attrib(root, "bgcolor");
-
- if (bgcolor != NULL) {
- *begin = g_strdup_printf("<b v='%s'>", bgcolor);
- *end = g_strdup("</b>");
- }
-
- } else {
- gchar *err;
-
-#ifdef MSIM_MARKUP_SHOW_UNKNOWN_TAGS
- *begin = g_strdup_printf("[%s]", root->name);
- *end = g_strdup_printf("[/%s]", root->name);
-#else
- *begin = g_strdup("");
- *end = g_strdup("");
-#endif
-
- err = g_strdup_printf("html_tag_to_msim_markup: unrecognized "
- "HTML tag %s was sent by the IM client; ignoring",
- root->name ? root->name : "(NULL)");
- msim_unrecognized(NULL, NULL, err);
- g_free(err);
- }
- return ret;
-}
-
-/**
- * Convert an xmlnode of msim markup or HTML to an HTML string or msim markup.
- *
- * @param f Function to convert tags.
- *
- * @return An HTML string. Caller frees.
- */
-static void
-msim_convert_xmlnode(MsimSession *session, GString *out, xmlnode *root, MSIM_XMLNODE_CONVERT f, int nodes_processed)
-{
- xmlnode *node;
- gchar *begin, *end, *tmp;
- int descended = nodes_processed;
-
- if (!root || !root->name)
- return;
-
- purple_debug_info("msim", "msim_convert_xmlnode: got root=%s\n",
- root->name);
-
- begin = end = NULL;
-
- if (descended == 0) /* We've not formatted this yet.. :) */
- descended = f(session, root, &begin, &end); /* Get the value that our format function has already descended for us */
-
- g_string_append(out, begin);
- g_free(begin);
-
- /* Loop over all child nodes. */
- for (node = root->child; node != NULL; node = node->next) {
- switch (node->type) {
- case XMLNODE_TYPE_ATTRIB:
- /* Attributes handled above. */
- break;
-
- case XMLNODE_TYPE_TAG:
- /* A tag or tag with attributes. Recursively descend. */
- msim_convert_xmlnode(session, out, node, f, descended);
-
- purple_debug_info("msim", " ** node name=%s\n",
- node->name ? node->name : "(NULL)");
- break;
-
- case XMLNODE_TYPE_DATA:
- /* Literal text. */
- /*
- * TODO: Why is it necessary to escape here? I thought
- * node->data was already escaped?
- */
- tmp = g_markup_escape_text(node->data, node->data_sz);
- g_string_append(out, tmp);
- g_free(tmp);
- break;
-
- default:
- purple_debug_warning("msim",
- "msim_convert_xmlnode: unknown node type\n");
- }
- }
-
- /* TODO: Note that msim counts each piece of text enclosed by <f> as
- * a paragraph and will display each on its own line. You actually have
- * to _nest_ <f> tags to intersperse different text in one paragraph!
- * Comment out this line below to see. */
- g_string_append(out, end);
- g_free(end);
-}
-
-/**
- * Convert XML to something based on MSIM_XMLNODE_CONVERT.
- */
-static gchar *
-msim_convert_xml(MsimSession *session, const gchar *raw, MSIM_XMLNODE_CONVERT f)
-{
- xmlnode *root;
- GString *str;
- gchar *enclosed_raw;
-
- g_return_val_if_fail(raw != NULL, NULL);
-
- /* Enclose text in one root tag, to try to make it valid XML for parsing. */
- enclosed_raw = g_strconcat("<root>", raw, "</root>", NULL);
-
- root = xmlnode_from_str(enclosed_raw, -1);
-
- if (!root) {
- purple_debug_warning("msim", "msim_markup_to_html: couldn't parse "
- "%s as XML, returning raw: %s\n", enclosed_raw, raw);
- /* TODO: msim_unrecognized */
- g_free(enclosed_raw);
- return g_strdup(raw);
- }
-
- g_free(enclosed_raw);
-
- str = g_string_new(NULL);
- msim_convert_xmlnode(session, str, root, f, 0);
- xmlnode_free(root);
-
- purple_debug_info("msim", "msim_markup_to_html: returning %s\n", str->str);
-
- return g_string_free(str, FALSE);
-}
-
-/**
- * Convert plaintext smileys to <i> markup tags.
- *
- * @param before Original text with ASCII smileys. Will be freed.
- * @return A new string with <i> tags, if applicable. Must be g_free()'d.
- */
-static gchar *
-msim_convert_smileys_to_markup(gchar *before)
-{
- gchar *old, *new, *replacement;
- guint i;
- struct MSIM_EMOTICON *emote;
-
- old = before;
- new = NULL;
-
- for (i = 0; (emote = &msim_emoticons[i]) && emote->name != NULL; ++i) {
- gchar *name, *symbol;
-
- name = emote->name;
- symbol = emote->symbol;
-
- replacement = g_strdup_printf("<i n=\"%s\"/>", name);
-
- purple_debug_info("msim", "msim_convert_smileys_to_markup: %s->%s\n",
- symbol ? symbol : "(NULL)",
- replacement ? replacement : "(NULL)");
- new = purple_strreplace(old, symbol, replacement);
-
- g_free(replacement);
- g_free(old);
-
- old = new;
- }
-
- return new;
-}
-
-/**
- * High-level function to convert MySpaceIM markup to Purple (HTML) markup.
- *
- * @return Purple markup string, must be g_free()'d. */
-gchar *
-msim_markup_to_html(MsimSession *session, const gchar *raw)
-{
- return msim_convert_xml(session, raw, msim_markup_tag_to_html);
-}
-
-/**
- * High-level function to convert Purple (HTML) to MySpaceIM markup.
- *
- * TODO: consider using purple_markup_html_to_xhtml() to make valid XML.
- *
- * @return HTML markup string, must be g_free()'d. */
-gchar *
-html_to_msim_markup(MsimSession *session, const gchar *raw)
-{
- gchar *markup;
-
- markup = msim_convert_xml(session, raw, html_tag_to_msim_markup);
-
- if (purple_account_get_bool(session->account, "emoticons", TRUE)) {
- /* Frees markup and allocates a new one. */
- markup = msim_convert_smileys_to_markup(markup);
- }
-
- return markup;
-}
diff --git a/libpurple/protocols/myspace/message.c b/libpurple/protocols/myspace/message.c
deleted file mode 100644
index 9eb44b3ade..0000000000
--- a/libpurple/protocols/myspace/message.c
+++ /dev/null
@@ -1,1413 +0,0 @@
-/** MySpaceIM protocol messages
- *
- * \author Jeff Connelly
- *
- * Copyright (C) 2007, Jeff Connelly <jeff2@soc.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
- */
-
-#include "myspace.h"
-#include "message.h"
-
-static void msim_msg_debug_string_element(gpointer data, gpointer user_data);
-
-/**
- * Escape codes and associated replacement text, used for protocol message
- * escaping and unescaping.
- */
-static struct MSIM_ESCAPE_REPLACEMENT {
- gchar *code;
- gchar text;
-} msim_escape_replacements[] = {
- { "/1", '/' },
- { "/2", '\\' },
- /* { "/3", "|" }, */ /* Not used here -- only for within arrays */
- { NULL, 0 }
-};
-
-/**
- * Escape a protocol message.
- *
- * @return The escaped message. Caller must g_free().
- */
-gchar *
-msim_escape(const gchar *msg)
-{
- GString *gs;
- guint i, j;
- guint msg_len;
-
- gs = g_string_new("");
- msg_len = strlen(msg);
-
- for (i = 0; i < msg_len; ++i) {
- struct MSIM_ESCAPE_REPLACEMENT *replacement;
- gchar *replace;
-
- replace = NULL;
-
- /* Check for characters that need to be escaped, and escape them. */
- for (j = 0; (replacement = &msim_escape_replacements[j]) &&
- replacement->code != NULL; ++j) {
- if (msg[i] == replacement->text) {
- replace = replacement->code;
- break;
- }
- }
-
- if (replace) {
- g_string_append(gs, replace);
- } else {
- g_string_append_c(gs, msg[i]);
- }
- }
-
-#ifdef MSIM_DEBUG_ESCAPE
- purple_debug_info("msim", "msim_escape: msg=%s, ret=%s\n", msg, gs->str);
-#endif
-
- return g_string_free(gs, FALSE);
-}
-
-/**
- * Unescape a protocol message.
- *
- * @return The unescaped message, caller must g_free().
- */
-gchar *
-msim_unescape(const gchar *msg)
-{
- GString *gs;
- guint i, j;
- guint msg_len;
-
- gs = g_string_new("");
- msg_len = strlen(msg);
-
- for (i = 0; i < msg_len; ++i) {
- struct MSIM_ESCAPE_REPLACEMENT *replacement;
- gchar replace;
-
- replace = msg[i];
-
- for (j = 0; (replacement = &msim_escape_replacements[j]) &&
- replacement->code != NULL; ++j) {
- if (msg[i] == replacement->code[0] &&
- i + 1 < msg_len &&
- msg[i + 1] == replacement->code[1]) {
- replace = replacement->text;
- ++i;
- break;
- }
- }
-
- g_string_append_c(gs, replace);
- }
-
-#ifdef MSIM_DEBUG_ESCAPE
- purple_debug_info("msim", "msim_unescape: msg=%s, ret=%s\n", msg, gs->str);
-#endif
-
- return g_string_free(gs, FALSE);
-}
-
-/**
- * Create a new message from va_list and its first argument.
- *
- * @param first_key The first argument (a key), or NULL to take all arguments
- * from argp.
- * @param argp A va_list of variadic arguments, already started with va_start().
- * @return New MsimMessage *, must be freed with msim_msg_free().
- *
- * For internal use - users probably want msim_msg_new() or msim_send().
- */
-static MsimMessage *
-msim_msg_new_v(gchar *first_key, va_list argp)
-{
- gchar *key, *value;
- MsimMessageType type;
- MsimMessage *msg;
- gboolean first;
-
- GString *gs;
- GList *gl;
- MsimMessage *dict;
-
- /* Begin with an empty message. */
- msg = NULL;
-
- /* First parameter can be given explicitly. */
- first = first_key != NULL;
-
- /* Read key, type, value triplets until NULL. */
- do {
- if (first) {
- key = first_key;
- first = FALSE;
- } else {
- key = va_arg(argp, gchar *);
- if (!key) {
- break;
- }
- }
-
- type = va_arg(argp, int);
-
- /* Interpret variadic arguments. */
- switch (type) {
- case MSIM_TYPE_INTEGER:
- case MSIM_TYPE_BOOLEAN:
- msg = msim_msg_append(msg, key, type, GUINT_TO_POINTER(va_arg(argp, int)));
- break;
-
- case MSIM_TYPE_STRING:
- value = va_arg(argp, char *);
-
- g_return_val_if_fail(value != NULL, FALSE);
-
- msg = msim_msg_append(msg, key, type, value);
- break;
-
- case MSIM_TYPE_BINARY:
- gs = va_arg(argp, GString *);
-
- g_return_val_if_fail(gs != NULL, FALSE);
-
- /* msim_msg_free() will free this GString the caller created. */
- msg = msim_msg_append(msg, key, type, gs);
- break;
-
- case MSIM_TYPE_LIST:
- gl = va_arg(argp, GList *);
-
- g_return_val_if_fail(gl != NULL, FALSE);
-
- msg = msim_msg_append(msg, key, type, gl);
- break;
-
- case MSIM_TYPE_DICTIONARY:
- dict = va_arg(argp, MsimMessage *);
-
- g_return_val_if_fail(dict != NULL, FALSE);
-
- msg = msim_msg_append(msg, key, type, dict);
- break;
-
- default:
- purple_debug_info("msim", "msim_send: unknown type %d\n", type);
- break;
- }
- } while(key);
-
- return msg;
-}
-
-/**
- * Create a new MsimMessage.
- *
- * @param first_key The first key in the sequence, or NULL for an empty message.
- * @param ... A sequence of gchar* key/type/value triplets, terminated with NULL.
- *
- * See msim_msg_append() documentation for details on types.
- */
-MsimMessage *
-msim_msg_new(gchar *first_key, ...)
-{
- MsimMessage *ret = NULL;
- va_list argp;
-
- if (first_key) {
- va_start(argp, first_key);
- ret = msim_msg_new_v(first_key, argp);
- va_end(argp);
- }
-
- return ret;
-}
-
-/**
- * Pack a string using the given GFunc and seperator.
- * Used by msim_msg_dump() and msim_msg_pack().
- */
-static gchar *
-msim_msg_pack_using(MsimMessage *msg,
- GFunc gf,
- const gchar *sep,
- const gchar *begin, const gchar *end)
-{
- int num_items;
- gchar **strings;
- gchar **strings_tmp;
- gchar *joined;
- gchar *final;
- int i;
-
- g_return_val_if_fail(msg != NULL, NULL);
-
- num_items = g_list_length(msg);
-
- /* Add one for NULL terminator for g_strjoinv(). */
- strings = (gchar **)g_new0(gchar *, num_items + 1);
-
- strings_tmp = strings;
- g_list_foreach(msg, gf, &strings_tmp);
-
- joined = g_strjoinv(sep, strings);
- final = g_strconcat(begin, joined, end, NULL);
- g_free(joined);
-
- /* Clean up. */
- for (i = 0; i < num_items; ++i) {
- g_free(strings[i]);
- }
-
- g_free(strings);
-
- return final;
-}
-
-/**
- * Return a human-readable string of the message.
- *
- * @return A new gchar *, must be g_free()'d.
- */
-static gchar *
-msim_msg_dump_to_str(MsimMessage *msg)
-{
- gchar *debug_str;
-
- if (!msg) {
- debug_str = g_strdup("<MsimMessage: empty>");
- } else {
- debug_str = msim_msg_pack_using(msg, msim_msg_debug_string_element,
- "\n", "<MsimMessage: \n", "\n/MsimMessage>");
- }
-
- return debug_str;
-}
-
-/**
- * Store a human-readable string describing the element.
- *
- * @param data Pointer to an MsimMessageElement.
- * @param user_data
- */
-static void
-msim_msg_debug_string_element(gpointer data, gpointer user_data)
-{
- MsimMessageElement *elem;
- gchar *string;
- GString *gs;
- gchar *binary;
- gchar ***items; /* wow, a pointer to a pointer to a pointer */
-
- gchar *s;
- GList *gl;
- guint i;
-
- elem = (MsimMessageElement *)data;
- items = user_data;
-
- switch (elem->type) {
- case MSIM_TYPE_INTEGER:
- string = g_strdup_printf("%s(integer): %d", elem->name,
- GPOINTER_TO_UINT(elem->data));
- break;
-
- case MSIM_TYPE_RAW:
- string = g_strdup_printf("%s(raw): %s", elem->name,
- elem->data ? (gchar *)elem->data : "(NULL)");
- break;
-
- case MSIM_TYPE_STRING:
- string = g_strdup_printf("%s(string): %s", elem->name,
- elem->data ? (gchar *)elem->data : "(NULL)");
- break;
-
- case MSIM_TYPE_BINARY:
- gs = (GString *)elem->data;
- binary = purple_base64_encode((guchar*)gs->str, gs->len);
- string = g_strdup_printf("%s(binary, %d bytes): %s", elem->name, (int)gs->len, binary);
- g_free(binary);
- break;
-
- case MSIM_TYPE_BOOLEAN:
- string = g_strdup_printf("%s(boolean): %s", elem->name,
- elem->data ? "TRUE" : "FALSE");
- break;
-
- case MSIM_TYPE_DICTIONARY:
- if (!elem->data) {
- s = g_strdup("(NULL)");
- } else {
- s = msim_msg_dump_to_str((MsimMessage *)elem->data);
- }
-
- if (!s) {
- s = g_strdup("(NULL, couldn't msim_msg_dump_to_str)");
- }
-
- string = g_strdup_printf("%s(dict): %s", elem->name, s);
-
- g_free(s);
- break;
-
- case MSIM_TYPE_LIST:
- gs = g_string_new("");
- g_string_append_printf(gs, "%s(list): \n", elem->name);
-
- i = 0;
- for (gl = (GList *)elem->data; gl != NULL; gl = g_list_next(gl)) {
- g_string_append_printf(gs, " %d. %s\n", i, (gchar *)(gl->data));
- ++i;
- }
-
- string = g_string_free(gs, FALSE);
- break;
-
- default:
- string = g_strdup_printf("%s(unknown type %d",
- elem->name ? elem->name : "(NULL)", elem->type);
- break;
- }
-
- **items = string;
- ++(*items);
-}
-
-/**
- * Search for and return the node in msg, matching name, or NULL.
- *
- * @param msg Message to search within.
- * @param name Field name to search for.
- *
- * @return The GList * node for the MsimMessageElement with the given name, or NULL if not found or name is NULL.
- *
- * For internal use - users probably want to use msim_msg_get() to
- * access the MsimMessageElement *, instead of the GList * container.
- *
- */
-static GList *
-msim_msg_get_node(const MsimMessage *msg, const gchar *name)
-{
- GList *node;
-
- if (!name || !msg) {
- return NULL;
- }
-
- /* Linear search for the given name. O(n) but n is small. */
- for (node = (GList*)msg; node != NULL; node = g_list_next(node)) {
- MsimMessageElement *elem;
-
- elem = (MsimMessageElement *)node->data;
-
- g_return_val_if_fail(elem != NULL, NULL);
- g_return_val_if_fail(elem->name != NULL, NULL);
-
- if (strcmp(elem->name, name) == 0) {
- return node;
- }
- }
- return NULL;
-}
-
-/**
- * Create a new MsimMessageElement * - must be g_free()'d.
- *
- * For internal use; users probably want msim_msg_append() or msim_msg_insert_before().
- *
- * @param dynamic_name Whether 'name' should be freed when the message is destroyed.
- */
-static MsimMessageElement *
-msim_msg_element_new(const gchar *name, MsimMessageType type, gpointer data, gboolean dynamic_name)
-{
- MsimMessageElement *elem;
-
- elem = g_new0(MsimMessageElement, 1);
-
- elem->name = name;
- elem->dynamic_name = dynamic_name;
- elem->type = type;
- elem->data = data;
-
- return elem;
-}
-
-/**
- * Append a new element to a message.
- *
- * @param name Textual name of element (static string, neither copied nor freed).
- * @param type An MSIM_TYPE_* code.
- * @param data Pointer to data, see below.
- *
- * @return The new message - must be assigned to as with GList*. For example:
- *
- * msg = msim_msg_append(msg, ...)
- *
- * The data parameter depends on the type given:
- *
- * * MSIM_TYPE_INTEGER: Use GUINT_TO_POINTER(x).
- *
- * * MSIM_TYPE_BINARY: Same as integer, non-zero is TRUE and zero is FALSE.
- *
- * * MSIM_TYPE_STRING: gchar *. The data WILL BE FREED - use g_strdup() if needed.
- *
- * * MSIM_TYPE_RAW: gchar *. The data WILL BE FREED - use g_strdup() if needed.
- *
- * * MSIM_TYPE_BINARY: g_string_new_len(data, length). The data AND GString will be freed.
- *
- * * MSIM_TYPE_DICTIONARY: An MsimMessage *. Freed when message is destroyed.
- *
- * * MSIM_TYPE_LIST: GList * of gchar *. Again, everything will be freed.
- *
- * */
-MsimMessage *
-msim_msg_append(MsimMessage *msg, const gchar *name,
- MsimMessageType type, gpointer data)
-{
- return g_list_append(msg, msim_msg_element_new(name, type, data, FALSE));
-}
-
-/**
- * Append a new element, but with a dynamically-allocated name.
- * Exactly the same as msim_msg_append(), except 'name' will be freed when
- * the message is destroyed. Normally, it isn't, because a static string is given.
- */
-static MsimMessage *
-msim_msg_append_dynamic_name(MsimMessage *msg, gchar *name,
- MsimMessageType type, gpointer data)
-{
- return g_list_append(msg, msim_msg_element_new(name, type, data, TRUE));
-}
-
-/**
- * Insert a new element into a message, before the given element name.
- *
- * @param name_before Name of the element to insert the new element before. If
- * could not be found or NULL, new element will be inserted at end.
- *
- * See msim_msg_append() for usage of other parameters, and an important note about return value.
- */
-MsimMessage *
-msim_msg_insert_before(MsimMessage *msg, const gchar *name_before,
- const gchar *name, MsimMessageType type, gpointer data)
-{
- MsimMessageElement *new_elem;
- GList *node_before;
-
- new_elem = msim_msg_element_new(name, type, data, FALSE);
-
- node_before = msim_msg_get_node(msg, name_before);
-
- return g_list_insert_before(msg, node_before, new_elem);
-}
-
-/**
- * Perform a deep copy on a GList * of gchar * strings. Free with msim_msg_list_free().
- */
-static GList *
-msim_msg_list_copy(const GList *old)
-{
- GList *new_list;
-
- new_list = NULL;
-
- /* Deep copy (g_list_copy is shallow). Copy each string. */
- for (; old != NULL; old = g_list_next(old)) {
- new_list = g_list_append(new_list, g_strdup(old->data));
- }
-
- return new_list;
-}
-
-/**
- * Clone an individual element.
- *
- * @param data MsimMessageElement * to clone.
- * @param user_data Pointer to MsimMessage * to add cloned element to.
- */
-static void
-msim_msg_clone_element(gpointer data, gpointer user_data)
-{
- MsimMessageElement *elem;
- MsimMessage **new;
- gpointer new_data;
-
- GString *gs;
- MsimMessage *dict;
-
- elem = (MsimMessageElement *)data;
- new = (MsimMessage **)user_data;
-
- switch (elem->type) {
- case MSIM_TYPE_BOOLEAN:
- case MSIM_TYPE_INTEGER:
- new_data = elem->data;
- break;
-
- case MSIM_TYPE_RAW:
- case MSIM_TYPE_STRING:
- new_data = g_strdup((gchar *)elem->data);
- break;
-
- case MSIM_TYPE_LIST:
- new_data = (gpointer)msim_msg_list_copy((GList *)(elem->data));
- break;
-
- case MSIM_TYPE_BINARY:
- gs = (GString *)elem->data;
-
- new_data = g_string_new_len(gs->str, gs->len);
- break;
- case MSIM_TYPE_DICTIONARY:
- dict = (MsimMessage *)elem->data;
-
- new_data = msim_msg_clone(dict);
- break;
-
- default:
- purple_debug_info("msim", "msim_msg_clone_element: unknown type %d\n", elem->type);
- g_return_if_reached();
- }
-
- /* Append cloned data. Note that the 'name' field is a static string, so it
- * never needs to be copied nor freed. */
- if (elem->dynamic_name)
- *new = msim_msg_append_dynamic_name(*new, g_strdup(elem->name), elem->type, new_data);
- else
- *new = msim_msg_append(*new, elem->name, elem->type, new_data);
-}
-
-/**
- * Clone an existing MsimMessage.
- *
- * @return Cloned message; caller should free with msim_msg_free().
- */
-MsimMessage *
-msim_msg_clone(MsimMessage *old)
-{
- MsimMessage *new;
-
- if (old == NULL) {
- return NULL;
- }
-
- new = msim_msg_new(FALSE);
-
- g_list_foreach(old, msim_msg_clone_element, &new);
-
- return new;
-}
-
-/**
- * Free the data of a message element.
- *
- * @param elem The MsimMessageElement *
- *
- * Note this only frees the element data; you may also want to free the
- * element itself with g_free() (see msim_msg_free_element()).
- */
-void
-msim_msg_free_element_data(MsimMessageElement *elem)
-{
- switch (elem->type) {
- case MSIM_TYPE_BOOLEAN:
- case MSIM_TYPE_INTEGER:
- /* Integer value stored in gpointer - no need to free(). */
- break;
-
- case MSIM_TYPE_RAW:
- case MSIM_TYPE_STRING:
- /* Always free strings - caller should have g_strdup()'d if
- * string was static or temporary and not to be freed. */
- g_free(elem->data);
- break;
-
- case MSIM_TYPE_BINARY:
- /* Free the GString itself and the binary data. */
- g_string_free((GString *)elem->data, TRUE);
- break;
-
- case MSIM_TYPE_DICTIONARY:
- msim_msg_free((MsimMessage *)elem->data);
- break;
-
- case MSIM_TYPE_LIST:
- g_list_free((GList *)elem->data);
- break;
-
- default:
- purple_debug_info("msim", "msim_msg_free_element_data: "
- "not freeing unknown type %d\n", elem->type);
- break;
- }
-}
-
-/**
- * Free a GList * of MsimMessageElement *'s.
- */
-void
-msim_msg_list_free(GList *l)
-{
-
- for (; l != NULL; l = g_list_next(l)) {
- MsimMessageElement *elem;
-
- elem = (MsimMessageElement *)l->data;
-
- /* Note that name is almost never dynamically allocated elsewhere;
- * it is usually a static string, but not in lists. So cast it. */
- g_free((gchar *)elem->name);
- g_free(elem->data);
- g_free(elem);
- }
- g_list_free(l);
-}
-
-/**
- * Free an individual message element.
- *
- * @param data MsimMessageElement * to free.
- * @param user_data Not used; required to match g_list_foreach() callback prototype.
- *
- * Frees both the element data and the element itself.
- * Also frees the name if dynamic_name is TRUE.
- */
-static void
-msim_msg_free_element(gpointer data, gpointer user_data)
-{
- MsimMessageElement *elem;
-
- elem = (MsimMessageElement *)data;
-
- msim_msg_free_element_data(elem);
-
- if (elem->dynamic_name)
- /* Need to cast to remove const-ness, because
- * elem->name is almost always a constant, static
- * string, but not in this case. */
- g_free((gchar *)elem->name);
-
- g_free(elem);
-}
-
-/**
- * Free a complete message.
- */
-void
-msim_msg_free(MsimMessage *msg)
-{
- if (!msg) {
- /* already free as can be */
- return;
- }
-
- g_list_foreach(msg, msim_msg_free_element, NULL);
- g_list_free(msg);
-}
-
-/**
- * Pack an element into its protocol representation.
- *
- * @param data Pointer to an MsimMessageElement.
- * @param user_data Pointer to a gchar ** array of string items.
- *
- * Called by msim_msg_pack(). Will pack the MsimMessageElement into
- * a part of the protocol string and append it to the array. Caller
- * is responsible for creating array to correct dimensions, and
- * freeing each string element of the array added by this function.
- */
-static void
-msim_msg_pack_element(gpointer data, gpointer user_data)
-{
- MsimMessageElement *elem;
- gchar *string, *data_string;
- gchar ***items;
-
- elem = (MsimMessageElement *)data;
- items = (gchar ***)user_data;
-
- /* Exclude elements beginning with '_' from packed protocol messages. */
- if (elem->name[0] == '_') {
- return;
- }
-
- data_string = msim_msg_pack_element_data(elem);
-
- switch (elem->type) {
- /* These types are represented by key name/value pairs (converted above). */
- case MSIM_TYPE_INTEGER:
- case MSIM_TYPE_RAW:
- case MSIM_TYPE_STRING:
- case MSIM_TYPE_BINARY:
- case MSIM_TYPE_DICTIONARY:
- case MSIM_TYPE_LIST:
- string = g_strconcat(elem->name, "\\", data_string, NULL);
- break;
-
- /* Boolean is represented by absence or presence of name. */
- case MSIM_TYPE_BOOLEAN:
- if (GPOINTER_TO_UINT(elem->data)) {
- /* True - leave in, with blank value. */
- string = g_strdup_printf("%s\\", elem->name);
- } else {
- /* False - leave out. */
- string = g_strdup("");
- }
- break;
-
- default:
- g_free(data_string);
- g_return_if_reached();
- break;
- }
-
- g_free(data_string);
-
- **items = string;
- ++(*items);
-}
-
-/**
- * Pack an element into its protcol representation inside a dictionary.
- *
- * See msim_msg_pack_element().
- */
-static void
-msim_msg_pack_element_dict(gpointer data, gpointer user_data)
-{
- MsimMessageElement *elem;
- gchar *string, *data_string, ***items;
-
- elem = (MsimMessageElement *)data;
- items = (gchar ***)user_data;
-
- /* Exclude elements beginning with '_' from packed protocol messages. */
- if (elem->name[0] == '_') {
- return;
- }
-
- data_string = msim_msg_pack_element_data(elem);
-
- g_return_if_fail(data_string != NULL);
-
- switch (elem->type) {
- /* These types are represented by key name/value pairs (converted above). */
- case MSIM_TYPE_INTEGER:
- case MSIM_TYPE_RAW:
- case MSIM_TYPE_STRING:
- case MSIM_TYPE_BINARY:
- case MSIM_TYPE_DICTIONARY:
- case MSIM_TYPE_LIST:
- case MSIM_TYPE_BOOLEAN: /* Boolean is On or Off */
- string = g_strconcat(elem->name, "=", data_string, NULL);
- break;
-
- default:
- g_free(data_string);
- g_return_if_fail(FALSE);
- break;
- }
-
- g_free(data_string);
-
- **items = string;
- ++(*items);
-}
-
-/**
- * Return a packed string of a message suitable for sending over the wire.
- *
- * @return A string. Caller must g_free().
- */
-gchar *
-msim_msg_pack(MsimMessage *msg)
-{
- g_return_val_if_fail(msg != NULL, NULL);
-
- return msim_msg_pack_using(msg, msim_msg_pack_element, "\\", "\\", "\\final\\");
-}
-
-/**
- * Return a packed string of a dictionary, suitable for embedding in MSIM_TYPE_DICTIONARY.
- *
- * @return A string; caller must g_free().
- */
-static gchar *
-msim_msg_pack_dict(MsimMessage *msg)
-{
- g_return_val_if_fail(msg != NULL, NULL);
-
- return msim_msg_pack_using(msg, msim_msg_pack_element_dict, "\034", "", "");
-}
-
-/**
- * Send an existing MsimMessage.
- */
-gboolean
-msim_msg_send(MsimSession *session, MsimMessage *msg)
-{
- gchar *raw;
- gboolean success;
-
- raw = msim_msg_pack(msg);
- g_return_val_if_fail(raw != NULL, FALSE);
- success = msim_send_raw(session, raw);
- g_free(raw);
-
- return success;
-}
-
-/**
- * Return a message element data as a new string for a raw protocol message,
- * converting from other types (integer, etc.) if necessary.
- *
- * @return const gchar * The data as a string, or NULL. Caller must g_free().
- *
- * Returns a string suitable for inclusion in a raw protocol message, not necessarily
- * optimal for human consumption. For example, strings are escaped. Use
- * msim_msg_get_string() if you want a string, which in some cases is same as this.
- */
-gchar *
-msim_msg_pack_element_data(MsimMessageElement *elem)
-{
- GString *gs;
- GList *gl;
-
- g_return_val_if_fail(elem != NULL, NULL);
-
- switch (elem->type) {
- case MSIM_TYPE_INTEGER:
- return g_strdup_printf("%d", GPOINTER_TO_UINT(elem->data));
-
- case MSIM_TYPE_RAW:
- /* Not un-escaped - this is a raw element, already escaped if necessary. */
- return (gchar *)g_strdup((gchar *)elem->data);
-
- case MSIM_TYPE_STRING:
- /* Strings get escaped. msim_escape() creates a new string. */
- g_return_val_if_fail(elem->data != NULL, NULL);
- return elem->data ? msim_escape((gchar *)elem->data) :
- g_strdup("(NULL)");
-
- case MSIM_TYPE_BINARY:
- gs = (GString *)elem->data;
- /* Do not escape! */
- return purple_base64_encode((guchar *)gs->str, gs->len);
-
- case MSIM_TYPE_BOOLEAN:
- /* Not used by messages in the wire protocol * -- see msim_msg_pack_element.
- * Only used by dictionaries, see msim_msg_pack_element_dict. */
- return elem->data ? g_strdup("On") : g_strdup("Off");
-
- case MSIM_TYPE_DICTIONARY:
- return msim_msg_pack_dict((MsimMessage *)elem->data);
-
- case MSIM_TYPE_LIST:
- /* Pack using a|b|c|d|... */
- gs = g_string_new("");
-
- for (gl = (GList *)elem->data; gl != NULL; gl = g_list_next(gl)) {
- g_string_append_printf(gs, "%s", (gchar*)(gl->data));
-
- /* All but last element is separated by a bar. */
- if (g_list_next(gl))
- g_string_append(gs, "|");
- }
-
- return g_string_free(gs, FALSE);
-
- default:
- purple_debug_info("msim", "field %s, unknown type %d\n",
- elem->name ? elem->name : "(NULL)",
- elem->type);
- return NULL;
- }
-}
-
-/**
- * Send a message to the server, whose contents is specified using
- * variable arguments.
- *
- * @param session
- * @param ... A sequence of gchar* key/type/value triplets, terminated with NULL.
- *
- * This function exists for coding convenience: it allows a message to be created
- * and sent in one line of code. Internally it calls msim_msg_send().
- *
- * IMPORTANT: See msim_msg_append() documentation for details on element types.
- *
- */
-gboolean
-msim_send(MsimSession *session, ...)
-{
- gboolean success;
- MsimMessage *msg;
- va_list argp;
-
- va_start(argp, session);
- msg = msim_msg_new_v(NULL, argp);
- va_end(argp);
-
- /* Actually send the message. */
- success = msim_msg_send(session, msg);
-
- /* Cleanup. */
- msim_msg_free(msg);
-
- return success;
-}
-
-/**
- * Print a human-readable string of the message to Purple's debug log.
- *
- * @param fmt_string A static string, in which '%s' will be replaced.
- */
-void
-msim_msg_dump(const gchar *fmt_string, MsimMessage *msg)
-{
- gchar *debug_str;
-
- g_return_if_fail(fmt_string != NULL);
-
- debug_str = msim_msg_dump_to_str(msg);
-
- g_return_if_fail(debug_str != NULL);
-
- purple_debug_info("msim", fmt_string, debug_str);
-
- g_free(debug_str);
-}
-
-/**
- * Parse a raw protocol message string into a MsimMessage *.
- *
- * @param raw The raw message string to parse, will be g_free()'d.
- *
- * @return MsimMessage *. Caller should msim_msg_free() when done.
- */
-MsimMessage *
-msim_parse(const gchar *raw)
-{
- MsimMessage *msg;
- gchar *token;
- gchar **tokens;
- gchar *key;
- gchar *value;
- int i;
-
- g_return_val_if_fail(raw != NULL, NULL);
-
- purple_debug_info("msim", "msim_parse: got <%s>\n", raw);
-
- key = NULL;
-
- /* All messages begin with a \. */
- if (raw[0] != '\\' || raw[1] == 0) {
- purple_debug_info("msim", "msim_parse: incomplete/bad string, "
- "missing initial backslash: <%s>\n", raw);
- /* XXX: Should we try to recover, and read to first backslash? */
-
- return NULL;
- }
-
- msg = msim_msg_new(FALSE);
-
- for (tokens = g_strsplit(raw + 1, "\\", 0), i = 0;
- (token = tokens[i]);
- i++) {
-#ifdef MSIM_DEBUG_PARSE
- purple_debug_info("msim", "tok=<%s>, i%2=%d\n", token, i % 2);
-#endif
- if (i % 2) {
- /* Odd-numbered ordinal is a value. */
-
- value = token;
-
- /* Incoming protocol messages get tagged as MSIM_TYPE_RAW, which
- * represents an untyped piece of data. msim_msg_get_* will
- * convert to appropriate types for caller, and handle unescaping if needed. */
- msg = msim_msg_append_dynamic_name(msg, g_strdup(key), MSIM_TYPE_RAW, g_strdup(value));
-#ifdef MSIM_DEBUG_PARSE
- purple_debug_info("msim", "insert string: |%s|=|%s|\n", key, value);
-#endif
- } else {
- /* Even numbered indexes are key names. */
- key = token;
- }
- }
- g_strfreev(tokens);
-
- return msg;
-}
-
-/**
- * Return the first MsimMessageElement * with given name in the MsimMessage *.
- *
- * @param name Name to search for.
- *
- * @return MsimMessageElement * matching name, or NULL.
- *
- * Note: useful fields of MsimMessageElement are 'data' and 'type', which
- * you can access directly. But it is often more convenient to use
- * another msim_msg_get_* that converts the data to what type you want.
- */
-MsimMessageElement *
-msim_msg_get(const MsimMessage *msg, const gchar *name)
-{
- GList *node;
-
- node = msim_msg_get_node(msg, name);
- if (node) {
- return (MsimMessageElement *)node->data;
- } else {
- return NULL;
- }
-}
-
-gchar *
-msim_msg_get_string_from_element(MsimMessageElement *elem)
-{
- g_return_val_if_fail(elem != NULL, NULL);
- switch (elem->type) {
- case MSIM_TYPE_INTEGER:
- return g_strdup_printf("%d", GPOINTER_TO_UINT(elem->data));
-
- case MSIM_TYPE_RAW:
- /* Raw element from incoming message - if its a string, it'll
- * be escaped. */
- return msim_unescape((gchar *)elem->data);
-
- case MSIM_TYPE_STRING:
- /* Already unescaped. */
- return g_strdup((gchar *)elem->data);
-
- default:
- purple_debug_info("msim", "msim_msg_get_string_element: type %d unknown, name %s\n",
- elem->type, elem->name ? elem->name : "(NULL)");
- return NULL;
- }
-}
-
-/**
- * Return the data of an element of a given name, as a string.
- *
- * @param name Name of element.
- *
- * @return gchar * The data as a string, or NULL if not found.
- * Caller must g_free().
- *
- * Note that msim_msg_pack_element_data() is similar, but returns a string
- * for inclusion into a raw protocol string (escaped and everything).
- * This function unescapes the string for you, if needed.
- */
-gchar *
-msim_msg_get_string(const MsimMessage *msg, const gchar *name)
-{
- MsimMessageElement *elem;
-
- elem = msim_msg_get(msg, name);
- if (!elem) {
- return NULL;
- }
-
- return msim_msg_get_string_from_element(elem);
-}
-
-/**
- * Parse a |-separated string into a new GList. Free with msim_msg_list_free().
- */
-static GList *
-msim_msg_list_parse(const gchar *raw)
-{
- gchar **array;
- GList *list;
- guint i;
-
- array = g_strsplit(raw, "|", 0);
- list = NULL;
-
- /* TODO: escape/unescape /3 <-> | within list elements */
-
- for (i = 0; array[i] != NULL; ++i) {
- MsimMessageElement *elem;
-
- /* Freed in msim_msg_list_free() */
- elem = g_new0(MsimMessageElement, 1);
-
- /* Give the element a name for debugging purposes.
- * Not supposed to be looked up by this name; instead,
- * lookup the elements by indexing the array. */
- elem->name = g_strdup_printf("(list item #%d)", i);
- elem->type = MSIM_TYPE_RAW;
- elem->data = g_strdup(array[i]);
-
- list = g_list_append(list, elem);
- }
-
- g_strfreev(array);
-
- return list;
-}
-
-static GList *
-msim_msg_get_list_from_element(MsimMessageElement *elem)
-{
- g_return_val_if_fail(elem != NULL, NULL);
- switch (elem->type) {
- case MSIM_TYPE_LIST:
- return msim_msg_list_copy((GList *)elem->data);
-
- case MSIM_TYPE_RAW:
- return msim_msg_list_parse((gchar *)elem->data);
-
- default:
- purple_debug_info("msim_msg_get_list", "type %d unknown, name %s\n",
- elem->type, elem->name ? elem->name : "(NULL)");
- return NULL;
- }
-}
-
-/**
- * Return an element as a new list. Caller frees with msim_msg_list_free().
- */
-GList *
-msim_msg_get_list(const MsimMessage *msg, const gchar *name)
-{
- MsimMessageElement *elem;
-
- elem = msim_msg_get(msg, name);
- if (!elem) {
- return NULL;
- }
-
- return msim_msg_get_list_from_element(elem);
-}
-
-/**
- * Parse a \x1c-separated "dictionary" of key=value pairs into a hash table.
- *
- * @param raw The text of the dictionary to parse. Often the
- * value for the 'body' field.
- *
- * @return A new MsimMessage *. Must msim_msg_free() when done.
- */
-static MsimMessage *
-msim_msg_dictionary_parse(const gchar *raw)
-{
- MsimMessage *dict;
- gchar *item;
- gchar **items;
- gchar **elements;
- guint i;
-
- g_return_val_if_fail(raw != NULL, NULL);
-
- dict = msim_msg_new(NULL);
-
- for (items = g_strsplit(raw, "\x1c", 0), i = 0;
- (item = items[i]);
- i++) {
- gchar *key, *value;
-
- elements = g_strsplit(item, "=", 2);
-
- key = elements[0];
- if (!key) {
- purple_debug_info("msim", "msim_msg_dictionary_parse(%s): null key\n",
- raw);
- g_strfreev(elements);
- break;
- }
-
- value = elements[1];
- if (!value) {
- purple_debug_info("msim", "msim_msg_dictionary_prase(%s): null value\n",
- raw);
- g_strfreev(elements);
- break;
- }
-
-#ifdef MSIM_DEBUG_PARSE
- purple_debug_info("msim_msg_dictionary_parse","-- %s: %s\n", key ? key : "(NULL)",
- value ? value : "(NULL)");
-#endif
- /* Append with _dynamic_name since g_strdup(key) is dynamic, and
- * needs to be freed when the message is destroyed. It isn't static as usual. */
- dict = msim_msg_append_dynamic_name(dict, g_strdup(key), MSIM_TYPE_RAW, g_strdup(value));
-
- g_strfreev(elements);
- }
-
- g_strfreev(items);
-
- return dict;
-}
-
-static MsimMessage *
-msim_msg_get_dictionary_from_element(MsimMessageElement *elem)
-{
- g_return_val_if_fail(elem != NULL, NULL);
- switch (elem->type) {
- case MSIM_TYPE_DICTIONARY:
- return msim_msg_clone((MsimMessage *)elem->data);
-
- case MSIM_TYPE_RAW:
- return msim_msg_dictionary_parse(elem->data);
-
- default:
- purple_debug_info("msim_msg_get_dictionary", "type %d unknown, name %s\n",
- elem->type, elem->name ? elem->name : "(NULL)");
- return NULL;
- }
-}
-
-/**
- * Return an element as a new dictionary. Caller frees with msim_msg_free().
- */
-MsimMessage *
-msim_msg_get_dictionary(const MsimMessage *msg, const gchar *name)
-{
- MsimMessageElement *elem;
-
- elem = msim_msg_get(msg, name);
- if (!elem) {
- return NULL;
- }
-
- return msim_msg_get_dictionary_from_element(elem);
-}
-
-guint
-msim_msg_get_integer_from_element(MsimMessageElement *elem)
-{
- g_return_val_if_fail(elem != NULL, 0);
- switch (elem->type) {
- case MSIM_TYPE_INTEGER:
- return GPOINTER_TO_UINT(elem->data);
-
- case MSIM_TYPE_RAW:
- case MSIM_TYPE_STRING:
- /* TODO: find out if we need larger integers */
- return (guint)atoi((gchar *)elem->data);
-
- default:
- return 0;
- }
-}
-
-/**
- * Return the data of an element of a given name, as an unsigned integer.
- *
- * @param name Name of element.
- *
- * @return guint Numeric representation of data, or 0 if could not be converted / not found.
- *
- * Useful to obtain an element's data if you know it should be an integer,
- * even if it is not stored as an MSIM_TYPE_INTEGER. MSIM_TYPE_STRING will
- * be converted handled correctly, for example.
- */
-guint
-msim_msg_get_integer(const MsimMessage *msg, const gchar *name)
-{
- MsimMessageElement *elem;
-
- elem = msim_msg_get(msg, name);
-
- if (!elem) {
- return 0;
- }
-
- return msim_msg_get_integer_from_element(elem);
-}
-
-static gboolean
-msim_msg_get_binary_from_element(MsimMessageElement *elem, gchar **binary_data, gsize *binary_length)
-{
- GString *gs;
-
- g_return_val_if_fail(elem != NULL, FALSE);
-
- switch (elem->type) {
- case MSIM_TYPE_RAW:
- /* Incoming messages are tagged with MSIM_TYPE_RAW, and
- * converted appropriately. They can still be "strings", just they won't
- * be tagged as MSIM_TYPE_STRING (as MSIM_TYPE_STRING is intended to be used
- * by msimprpl code for things like instant messages - stuff that should be
- * escaped if needed). DWIM.
- */
-
- /* Previously, incoming messages were stored as MSIM_TYPE_STRING.
- * This was fine for integers and strings, since they can easily be
- * converted in msim_get_*, as desirable. However, it does not work
- * well for binary strings. Consider:
- *
- * If incoming base64'd elements were tagged as MSIM_TYPE_STRING.
- * msim_msg_get_binary() sees MSIM_TYPE_STRING, base64 decodes, returns.
- * everything is fine.
- * But then, msim_send() is called on the incoming message, which has
- * a base64'd MSIM_TYPE_STRING that really is encoded binary. The values
- * will be escaped since strings are escaped, and / becomes /2; no good.
- *
- */
- *binary_data = (gchar *)purple_base64_decode((const gchar *)elem->data, binary_length);
- return ((*binary_data) != NULL);
-
- case MSIM_TYPE_BINARY:
- gs = (GString *)elem->data;
-
- /* Duplicate data, so caller can g_free() it. */
- *binary_data = g_memdup(gs->str, gs->len);
- *binary_length = gs->len;
-
- return TRUE;
-
-
- /* Rejected because if it isn't already a GString, have to g_new0 it and
- * then caller has to ALSO free the GString!
- *
- * return (GString *)elem->data; */
-
- default:
- purple_debug_info("msim", "msim_msg_get_binary: unhandled type %d for key %s\n",
- elem->type, elem->name ? elem->name : "(NULL)");
- return FALSE;
- }
-}
-
-/**
- * Return the data of an element of a given name, as a binary GString.
- *
- * @param binary_data A pointer to a new pointer, which will be filled in with the binary data. CALLER MUST g_free().
- *
- * @param binary_length A pointer to an integer, which will be set to the binary data length.
- *
- * @return TRUE if successful, FALSE if not.
- */
-gboolean
-msim_msg_get_binary(const MsimMessage *msg, const gchar *name,
- gchar **binary_data, gsize *binary_length)
-{
- MsimMessageElement *elem;
-
- elem = msim_msg_get(msg, name);
- if (!elem) {
- return FALSE;
- }
-
- return msim_msg_get_binary_from_element(elem, binary_data, binary_length);
-}
diff --git a/libpurple/protocols/myspace/message.h b/libpurple/protocols/myspace/message.h
deleted file mode 100644
index 5f1bcc852b..0000000000
--- a/libpurple/protocols/myspace/message.h
+++ /dev/null
@@ -1,109 +0,0 @@
-/** MySpaceIM protocol messages
- *
- * \author Jeff Connelly
- *
- * Copyright (C) 2007, Jeff Connelly <jeff2@soc.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
- */
-
-#ifndef _MYSPACE_MESSAGE_H
-#define _MYSPACE_MESSAGE_H
-
-#include <glib.h>
-
-#define MsimMessage GList /* #define instead of typedef to avoid casting */
-typedef gchar MsimMessageType;
-typedef struct _MsimMessageElement MsimMessageElement;
-
-#include "session.h"
-
-/* Types */
-struct _MsimMessageElement
-{
- const gchar *name; /**< Textual name of element. */
- gboolean dynamic_name; /**< TRUE if 'name' is a dynamic string to be freed, not static. */
- guint type; /**< MSIM_TYPE_* code. */
- gpointer data; /**< Pointer to data, or GUINT_TO_POINTER for int/bool. */
-};
-
-#define msim_msg_get_next_element_node(msg) ((MsimMessage *)(msg->next))
-
-/* Protocol field types */
-#define MSIM_TYPE_RAW '-'
-#define MSIM_TYPE_INTEGER 'i'
-#define MSIM_TYPE_STRING 's'
-#define MSIM_TYPE_BINARY 'b'
-#define MSIM_TYPE_BOOLEAN 'f'
-#define MSIM_TYPE_DICTIONARY 'd'
-#define MSIM_TYPE_LIST 'l'
-
-gchar *msim_escape(const gchar *msg);
-gchar *msim_unescape(const gchar *msg);
-
-MsimMessage *msim_msg_new(gchar *first_key, ...);
-/* No sentinel attribute, because can leave off varargs if not_empty is FALSE. */
-
-MsimMessage *msim_msg_clone(MsimMessage *old);
-void msim_msg_free_element_data(MsimMessageElement *elem);
-void msim_msg_free(MsimMessage *msg);
-MsimMessage *msim_msg_append(MsimMessage *msg, const gchar *name, MsimMessageType type, gpointer data);
-MsimMessage *msim_msg_insert_before(MsimMessage *msg, const gchar *name_before, const gchar *name, MsimMessageType type, gpointer data);
-gchar *msim_msg_pack_element_data(MsimMessageElement *elem);
-void msim_msg_dump(const char *fmt_string, MsimMessage *msg);
-gchar *msim_msg_pack(MsimMessage *msg);
-
-void msim_msg_list_free(GList *l);
-
-/* Based on http://permalink.gmane.org/gmane.comp.parsers.sparse/695
- * Define macros for useful gcc attributes. */
-#ifdef __GNUC__
-#define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__)
-#define FORMAT_ATTR(pos) __attribute__ ((__format__ (__printf__, pos, pos+1)))
-#define NORETURN_ATTR __attribute__ ((__noreturn__))
-/* __sentinel__ attribute was introduced in gcc 3.5 */
-#if (GCC_VERSION >= 3005)
- #define SENTINEL_ATTR __attribute__ ((__sentinel__(0)))
-#else
- #define SENTINEL_ATTR
-#endif /* gcc >= 3.5 */
-#else
- #define FORMAT_ATTR(pos)
- #define NORETURN_ATTR
- #define SENTINEL_ATTR
-#endif
-
-/* Cause gcc to emit "a missing sentinel in function call" if forgot
- * to write NULL as last, terminating parameter. */
-gboolean msim_send(struct _MsimSession *session, ...) SENTINEL_ATTR;
-
-gboolean msim_msg_send(struct _MsimSession *session, MsimMessage *msg);
-
-MsimMessage *msim_parse(const gchar *raw);
-
-MsimMessageElement *msim_msg_get(const MsimMessage *msg, const gchar *name);
-
-/* Retrieve data by name */
-gchar *msim_msg_get_string(const MsimMessage *msg, const gchar *name);
-GList *msim_msg_get_list(const MsimMessage *msg, const gchar *name);
-MsimMessage *msim_msg_get_dictionary(const MsimMessage *msg, const gchar *name);
-guint msim_msg_get_integer(const MsimMessage *msg, const gchar *name);
-gboolean msim_msg_get_binary(const MsimMessage *msg, const gchar *name, gchar **binary_data, gsize *binary_length);
-
-/* Retrieve data by element (MsimMessageElement *), returned from msim_msg_get() */
-gchar *msim_msg_get_string_from_element(MsimMessageElement *elem);
-guint msim_msg_get_integer_from_element(MsimMessageElement *elem);
-
-#endif /* _MYSPACE_MESSAGE_H */
diff --git a/libpurple/protocols/myspace/myspace.c b/libpurple/protocols/myspace/myspace.c
deleted file mode 100644
index f0a9021444..0000000000
--- a/libpurple/protocols/myspace/myspace.c
+++ /dev/null
@@ -1,3667 +0,0 @@
-/**
- * MySpaceIM Protocol Plugin
- *
- * \author Jeff Connelly
- *
- * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im>
- *
- * Based on Purple's "C Plugin HOWTO" hello world example.
- *
- * Code also drawn from mockprpl:
- * http://snarfed.org/space/purple+mock+protocol+plugin
- * Copyright (C) 2004-2007, Ryan Barrett <mockprpl@ryanb.org>
- *
- * and some constructs also based on existing Purple plugins, which are:
- * Copyright (C) 2003, Robbert Haarman <purple@inglorion.net>
- * Copyright (C) 2003, Ethan Blanton <eblanton@cs.purdue.edu>
- * Copyright (C) 2000-2003, Rob Flynn <rob@tgflinux.com>
- * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
- *
- * 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
- */
-
-#define PURPLE_PLUGIN
-
-#include "myspace.h"
-
-#include "privacy.h"
-
-static void msim_set_status(PurpleAccount *account, PurpleStatus *status);
-static void msim_set_idle(PurpleConnection *gc, int time);
-
-/**
- * Perform actual postprocessing on a message, adding userid as specified.
- *
- * @param msg The message to postprocess.
- * @param uid_before Name of field where to insert new field before, or NULL for end.
- * @param uid_field_name Name of field to add uid to.
- * @param uid The userid to insert.
- *
- * If the field named by uid_field_name already exists, then its string contents will
- * be used for the field, except "<uid>" will be replaced by the userid.
- *
- * If the field named by uid_field_name does not exist, it will be added before the
- * field named by uid_before, as an integer, with the userid.
- *
- * Does not handle sending, or scheduling userid lookup. For that, see msim_postprocess_outgoing().
- */
-static MsimMessage *
-msim_do_postprocessing(MsimMessage *msg, const gchar *uid_before,
- const gchar *uid_field_name, guint uid)
-{
- MsimMessageElement *elem;
-
- /* First, check - if the field already exists, replace <uid> within it */
- if ((elem = msim_msg_get(msg, uid_field_name)) != NULL) {
- gchar *fmt_string;
- gchar *uid_str, *new_str;
-
- /* Get the packed element, flattening it. This allows <uid> to be
- * replaced within nested data structures, since the replacement is done
- * on the linear, packed data, not on a complicated data structure.
- *
- * For example, if the field was originally a dictionary or a list, you
- * would have to iterate over all the items in it to see what needs to
- * be replaced. But by packing it first, the <uid> marker is easily replaced
- * just by a string replacement.
- */
- fmt_string = msim_msg_pack_element_data(elem);
-
- uid_str = g_strdup_printf("%d", uid);
- new_str = purple_strreplace(fmt_string, "<uid>", uid_str);
- g_free(uid_str);
- g_free(fmt_string);
-
- /* Free the old element data */
- msim_msg_free_element_data(elem->data);
-
- /* Replace it with our new data */
- elem->data = new_str;
- elem->type = MSIM_TYPE_RAW;
-
- } else {
- /* Otherwise, insert new field into outgoing message. */
- msg = msim_msg_insert_before(msg, uid_before, uid_field_name, MSIM_TYPE_INTEGER, GUINT_TO_POINTER(uid));
- }
-
- return msg;
-}
-
-/**
- * Callback for msim_postprocess_outgoing() to add a userid to a message, and send it (once receiving userid).
- *
- * @param session
- * @param userinfo The user information reply message, containing the user ID
- * @param data The message to postprocess and send.
- *
- * The data message should contain these fields:
- *
- * _uid_field_name: string, name of field to add with userid from userinfo message
- * _uid_before: string, name of field before field to insert, or NULL for end
- */
-static void
-msim_postprocess_outgoing_cb(MsimSession *session, const MsimMessage *userinfo,
- gpointer data)
-{
- gchar *uid_field_name, *uid_before, *username;
- guint uid;
- MsimMessage *msg, *body;
-
- msg = (MsimMessage *)data;
-
- /* Obtain userid from userinfo message. */
- body = msim_msg_get_dictionary(userinfo, "body");
- g_return_if_fail(body != NULL);
-
- uid = msim_msg_get_integer(body, "UserID");
- msim_msg_free(body);
-
- username = msim_msg_get_string(msg, "_username");
-
- if (!uid) {
- gchar *msg;
-
- msg = g_strdup_printf(_("No such user: %s"), username);
- if (!purple_conv_present_error(username, session->account, msg)) {
- purple_notify_error(NULL, NULL, _("User lookup"), msg);
- }
-
- g_free(msg);
- g_free(username);
- /* TODO: free
- * msim_msg_free(msg);
- */
- return;
- }
-
- uid_field_name = msim_msg_get_string(msg, "_uid_field_name");
- uid_before = msim_msg_get_string(msg, "_uid_before");
-
- msg = msim_do_postprocessing(msg, uid_before, uid_field_name, uid);
-
- /* Send */
- if (!msim_msg_send(session, msg)) {
- msim_msg_dump("msim_postprocess_outgoing_cb: sending failed for message: %s\n", msg);
- }
-
-
- /* Free field names AFTER sending message, because MsimMessage does NOT copy
- * field names - instead, treats them as static strings (which they usually are).
- */
- g_free(uid_field_name);
- g_free(uid_before);
- g_free(username);
- /* TODO: free
- * msim_msg_free(msg);
- */
-}
-
-/**
- * Postprocess and send a message.
- *
- * @param session
- * @param msg Message to postprocess. Will NOT be freed.
- * @param username Username to resolve. Assumed to be a static string (will not be freed or copied).
- * @param uid_field_name Name of new field to add, containing uid of username. Static string.
- * @param uid_before Name of existing field to insert username field before. Static string.
- *
- * @return TRUE if successful.
- */
-static gboolean
-msim_postprocess_outgoing(MsimSession *session, MsimMessage *msg,
- const gchar *username, const gchar *uid_field_name,
- const gchar *uid_before)
-{
- PurpleBuddy *buddy;
- guint uid;
- gboolean rc;
-
- g_return_val_if_fail(msg != NULL, FALSE);
-
- /* Store information for msim_postprocess_outgoing_cb(). */
- msg = msim_msg_append(msg, "_username", MSIM_TYPE_STRING, g_strdup(username));
- msg = msim_msg_append(msg, "_uid_field_name", MSIM_TYPE_STRING, g_strdup(uid_field_name));
- msg = msim_msg_append(msg, "_uid_before", MSIM_TYPE_STRING, g_strdup(uid_before));
-
- /* First, try the most obvious. If numeric userid is given, use that directly. */
- if (msim_is_userid(username)) {
- uid = atol(username);
- } else {
- /* Next, see if on buddy list and know uid. */
- buddy = purple_find_buddy(session->account, username);
- if (buddy) {
- uid = purple_blist_node_get_int(PURPLE_BLIST_NODE(buddy), "UserID");
- } else {
- uid = 0;
- }
-
- if (!buddy || !uid) {
- /* Don't have uid offhand - need to ask for it, and wait until hear back before sending. */
- purple_debug_info("msim", ">>> msim_postprocess_outgoing: couldn't find username %s in blist\n",
- username ? username : "(NULL)");
- msim_lookup_user(session, username, msim_postprocess_outgoing_cb, msim_msg_clone(msg));
- return TRUE; /* not sure of status yet - haven't sent! */
- }
- }
-
- /* Already have uid, postprocess and send msg immediately. */
- purple_debug_info("msim", "msim_postprocess_outgoing: found username %s has uid %d\n",
- username ? username : "(NULL)", uid);
-
- msg = msim_do_postprocessing(msg, uid_before, uid_field_name, uid);
-
- rc = msim_msg_send(session, msg);
-
- /* TODO: free
- * msim_msg_free(msg);
- */
-
- return rc;
-}
-
-/**
- * Send a buddy message of a given type.
- *
- * @param session
- * @param who Username to send message to.
- * @param text Message text to send. Not freed; will be copied.
- * @param type A MSIM_BM_* constant.
- *
- * @return TRUE if success, FALSE if fail.
- *
- * Buddy messages ('bm') include instant messages, action messages, status messages, etc.
- */
-gboolean
-msim_send_bm(MsimSession *session, const gchar *who, const gchar *text,
- int type)
-{
- gboolean rc;
- MsimMessage *msg;
- const gchar *from_username;
-
- g_return_val_if_fail(who != NULL, FALSE);
- g_return_val_if_fail(text != NULL, FALSE);
-
- from_username = session->account->username;
-
- g_return_val_if_fail(from_username != NULL, FALSE);
-
- purple_debug_info("msim", "sending %d message from %s to %s: %s\n",
- type, from_username, who, text);
-
- msg = msim_msg_new(
- "bm", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(type),
- "sesskey", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(session->sesskey),
- /* 't' will be inserted here */
- "cv", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(MSIM_CLIENT_VERSION),
- "msg", MSIM_TYPE_STRING, g_strdup(text),
- NULL);
-
- rc = msim_postprocess_outgoing(session, msg, who, "t", "cv");
-
- msim_msg_free(msg);
-
- return rc;
-}
-
-/**
- * Lookup a username by userid, from buddy list.
- *
- * @param wanted_uid
- *
- * @return Username of wanted_uid, if on blist, or NULL.
- * This is a static string, so don't free it. Copy it if needed.
- *
- */
-static const gchar *
-msim_uid2username_from_blist(PurpleAccount *account, guint wanted_uid)
-{
- GSList *buddies, *cur;
- const gchar *ret;
-
- buddies = purple_find_buddies(account, NULL);
-
- if (!buddies)
- {
- purple_debug_info("msim", "msim_uid2username_from_blist: no buddies?\n");
- return NULL;
- }
-
- ret = NULL;
-
- for (cur = buddies; cur != NULL; cur = g_slist_next(cur))
- {
- PurpleBuddy *buddy;
- guint uid;
- const gchar *name;
-
- /* See finch/gnthistory.c */
- buddy = cur->data;
-
- uid = purple_blist_node_get_int(PURPLE_BLIST_NODE(buddy), "UserID");
- name = purple_buddy_get_name(buddy);
-
- if (uid == wanted_uid)
- {
- ret = name;
- break;
- }
- }
-
- g_slist_free(buddies);
- return ret;
-}
-
-/**
- * Setup a callback, to be called when a reply is received with the returned rid.
- *
- * @param cb The callback, an MSIM_USER_LOOKUP_CB.
- * @param data Arbitrary user data to be passed to callback (probably an MsimMessage *).
- *
- * @return The request/reply ID, used to link replies with requests, or -1.
- * Put the rid in your request, 'rid' field.
- *
- * TODO: Make more generic and more specific:
- * 1) MSIM_USER_LOOKUP_CB - make it for PERSIST_REPLY, not just user lookup
- * 2) data - make it an MsimMessage?
- */
-guint
-msim_new_reply_callback(MsimSession *session, MSIM_USER_LOOKUP_CB cb,
- gpointer data)
-{
- guint rid;
-
- rid = session->next_rid++;
-
- g_hash_table_insert(session->user_lookup_cb, GUINT_TO_POINTER(rid), cb);
- g_hash_table_insert(session->user_lookup_cb_data, GUINT_TO_POINTER(rid), data);
-
- return rid;
-}
-
-/**
- * Return the icon name for a buddy and account.
- *
- * @param acct The account to find the icon for, or NULL for protocol icon.
- * @param buddy The buddy to find the icon for, or NULL for the account icon.
- *
- * @return The base icon name string.
- */
-static const gchar *
-msim_list_icon(PurpleAccount *acct, PurpleBuddy *buddy)
-{
- /* Use a MySpace icon submitted by hbons at
- * http://developer.pidgin.im/wiki/MySpaceIM. */
- return "myspace";
-}
-
-/**
- * Obtain the status text for a buddy.
- *
- * @param buddy The buddy to obtain status text for.
- *
- * @return Status text, or NULL if error. Caller g_free()'s.
- */
-static char *
-msim_status_text(PurpleBuddy *buddy)
-{
- MsimUser *user;
- const gchar *display_name = NULL, *headline = NULL;
- PurpleAccount *account;
-
- g_return_val_if_fail(buddy != NULL, NULL);
-
- account = purple_buddy_get_account(buddy);
-
- user = msim_get_user_from_buddy(buddy, FALSE);
- if (user != NULL) {
- /* Retrieve display name and/or headline, depending on user preference. */
- if (purple_account_get_bool(account, "show_headline", TRUE)) {
- headline = user->headline;
- }
-
- if (purple_account_get_bool(account, "show_display_name", FALSE)) {
- display_name = user->display_name;
- }
- }
-
- /* Return appropriate combination of display name and/or headline, or neither. */
-
- if (display_name && headline) {
- return g_strconcat(display_name, " ", headline, NULL);
- } else if (display_name) {
- return g_strdup(display_name);
- } else if (headline) {
- return g_strdup(headline);
- }
-
- return NULL;
-}
-
-/**
- * Obtain the tooltip text for a buddy.
- *
- * @param buddy Buddy to obtain tooltip text on.
- * @param user_info Variable modified to have the tooltip text.
- * @param full TRUE if should obtain full tooltip text.
- */
-static void
-msim_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info,
- gboolean full)
-{
- MsimUser *user;
-
- g_return_if_fail(buddy != NULL);
- g_return_if_fail(user_info != NULL);
-
- user = msim_get_user_from_buddy(buddy, TRUE);
-
- if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
- MsimSession *session;
- PurpleAccount *account = purple_buddy_get_account(buddy);
- PurpleConnection *gc = purple_account_get_connection(account);
-
- session = (MsimSession *)gc->proto_data;
-
- /* TODO: if (full), do something different? */
-
- /* TODO: request information? have to figure out how to do
- * the asynchronous lookup like oscar does (tooltip shows
- * 'retrieving...' if not yet available, then changes when it is).
- *
- * Right now, only show what we have on hand.
- */
-
- /* Show abbreviated user info. */
- msim_append_user_info(session, user_info, user, FALSE);
- }
-}
-
-/**
- * Get possible user status types. Based on mockprpl.
- *
- * @return GList of status types.
- */
-static GList *
-msim_status_types(PurpleAccount *acct)
-{
- GList *types;
- PurpleStatusType *status;
-
- purple_debug_info("myspace", "returning status types\n");
-
- types = NULL;
-
- /* Statuses are almost all the same. Define a macro to reduce code repetition. */
-#define _MSIM_ADD_NEW_STATUS(prim) status = \
- purple_status_type_new_with_attrs( \
- prim, /* PurpleStatusPrimitive */ \
- NULL, /* id - use default */ \
- NULL, /* name - use default */ \
- TRUE, /* saveable */ \
- TRUE, /* user_settable */ \
- FALSE, /* not independent */ \
- \
- /* Attributes - each status can have a message. */ \
- "message", \
- _("Message"), \
- purple_value_new(PURPLE_TYPE_STRING), \
- NULL); \
- \
- \
- types = g_list_append(types, status)
-
-
- _MSIM_ADD_NEW_STATUS(PURPLE_STATUS_AVAILABLE);
- _MSIM_ADD_NEW_STATUS(PURPLE_STATUS_AWAY);
- _MSIM_ADD_NEW_STATUS(PURPLE_STATUS_OFFLINE);
- _MSIM_ADD_NEW_STATUS(PURPLE_STATUS_INVISIBLE);
-
- /* Except tune status is different... */
- status = purple_status_type_new_with_attrs(
- PURPLE_STATUS_TUNE, /* primitive */
- "tune", /* ID */
- NULL, /* name - use default */
- FALSE, /* saveable */
- TRUE, /* should be user_settable some day */
- TRUE, /* independent */
-
- PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
- PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
- NULL);
-
- types = g_list_append(types, status);
-
- return types;
-}
-
-/*
- * TODO: This define is stolen from oscar.h.
- * It's also in yahoo.h.
- * It should be in libpurple/util.c
- */
-#define msim_put32(buf, data) ( \
- (*((buf)) = (unsigned char)((data)>>24)&0xff), \
- (*((buf)+1) = (unsigned char)((data)>>16)&0xff), \
- (*((buf)+2) = (unsigned char)((data)>>8)&0xff), \
- (*((buf)+3) = (unsigned char)(data)&0xff), \
- 4)
-
-/**
- * Compute the base64'd login challenge response based on username, password, nonce, and IPs.
- *
- * @param nonce The base64 encoded nonce ('nc') field from the server.
- * @param email User's email address (used as login name).
- * @param password User's cleartext password.
- * @param response_len Will be written with response length.
- *
- * @return Binary login challenge response, ready to send to the server.
- * Must be g_free()'d when finished. NULL if error.
- */
-static gchar *
-msim_compute_login_response(const gchar nonce[2 * NONCE_SIZE],
- const gchar *email, const gchar *password, guint *response_len)
-{
- PurpleCipherContext *key_context;
- PurpleCipher *sha1;
- PurpleCipherContext *rc4;
-
- guchar hash_pw[HASH_SIZE];
- guchar key[HASH_SIZE];
- gchar *password_truncated, *password_utf16le, *password_utf8_lc;
- GString *data;
- guchar *data_out;
- size_t data_out_len;
- gsize conv_bytes_read, conv_bytes_written;
- GError *conv_error;
-#ifdef MSIM_DEBUG_LOGIN_CHALLENGE
- int i;
-#endif
-
- g_return_val_if_fail(nonce != NULL, NULL);
- g_return_val_if_fail(email != NULL, NULL);
- g_return_val_if_fail(password != NULL, NULL);
- g_return_val_if_fail(response_len != NULL, NULL);
-
- /*
- * Truncate password to 10 characters. Their "change password"
- * web page doesn't let you enter more than 10 characters, but you
- * can enter more than 10 when logging in on myspace.com and they
- * truncate it.
- */
- password_truncated = g_strndup(password, 10);
-
- /* Convert password to lowercase (required for passwords containing
- * uppercase characters). MySpace passwords are lowercase,
- * see ticket #2066. */
- password_utf8_lc = g_utf8_strdown(password_truncated, -1);
- g_free(password_truncated);
-
- /* Convert ASCII password to UTF16 little endian */
- purple_debug_info("msim", "converting password to UTF-16LE\n");
- conv_error = NULL;
- password_utf16le = g_convert(password_utf8_lc, -1, "UTF-16LE", "UTF-8",
- &conv_bytes_read, &conv_bytes_written, &conv_error);
- g_free(password_utf8_lc);
-
- if (conv_error != NULL) {
- purple_debug_error("msim",
- "g_convert password UTF8->UTF16LE failed: %s",
- conv_error->message);
- g_error_free(conv_error);
- return NULL;
- }
-
- /* Compute password hash */
- purple_cipher_digest_region("sha1", (guchar *)password_utf16le,
- conv_bytes_written, sizeof(hash_pw), hash_pw, NULL);
- g_free(password_utf16le);
-
-#ifdef MSIM_DEBUG_LOGIN_CHALLENGE
- purple_debug_info("msim", "pwhash = ");
- for (i = 0; i < sizeof(hash_pw); i++)
- purple_debug_info("msim", "%.2x ", hash_pw[i]);
- purple_debug_info("msim", "\n");
-#endif
-
- /* key = sha1(sha1(pw) + nonce2) */
- sha1 = purple_ciphers_find_cipher("sha1");
- key_context = purple_cipher_context_new(sha1, NULL);
- purple_cipher_context_append(key_context, hash_pw, HASH_SIZE);
- purple_cipher_context_append(key_context, (guchar *)(nonce + NONCE_SIZE), NONCE_SIZE);
- purple_cipher_context_digest(key_context, sizeof(key), key, NULL);
- purple_cipher_context_destroy(key_context);
-
-#ifdef MSIM_DEBUG_LOGIN_CHALLENGE
- purple_debug_info("msim", "key = ");
- for (i = 0; i < sizeof(key); i++) {
- purple_debug_info("msim", "%.2x ", key[i]);
- }
- purple_debug_info("msim", "\n");
-#endif
-
- rc4 = purple_cipher_context_new_by_name("rc4", NULL);
-
- /* Note: 'key' variable is 0x14 bytes (from SHA-1 hash),
- * but only first 0x10 used for the RC4 key. */
- purple_cipher_context_set_option(rc4, "key_len", (gpointer)0x10);
- purple_cipher_context_set_key(rc4, key);
-
- /* rc4 encrypt:
- * nonce1+email+IP list */
-
- data = g_string_new(NULL);
- g_string_append_len(data, nonce, NONCE_SIZE);
-
- /* Include the null terminator */
- g_string_append_len(data, email, strlen(email) + 1);
-
- while (data->len % 4 != 0)
- g_string_append_c(data, 0xfb);
-
-#ifdef SEND_OUR_IP_ADDRESSES
- /* TODO: Obtain IPs of network interfaces instead of using this hardcoded value */
- g_string_set_size(data, data->len + 4);
- (void)msim_put32(data->str + data->len - 4, MSIM_LOGIN_IP_LIST_LEN);
- g_string_append_len(data, MSIM_LOGIN_IP_LIST, MSIM_LOGIN_IP_LIST_LEN);
-#else
- g_string_set_size(data, data->len + 4);
- (void)msim_put32(data->str + data->len - 4, 0);
-#endif /* !SEND_OUR_IP_ADDRESSES */
-
- data_out = g_new0(guchar, data->len);
-
- purple_cipher_context_encrypt(rc4, (const guchar *)data->str,
- data->len, data_out, &data_out_len);
- purple_cipher_context_destroy(rc4);
-
- if (data_out_len != data->len) {
- purple_debug_info("msim", "msim_compute_login_response: "
- "data length mismatch: %" G_GSIZE_FORMAT " != %"
- G_GSIZE_FORMAT "\n", data_out_len, data->len);
- }
-
- g_string_free(data, TRUE);
-
-#ifdef MSIM_DEBUG_LOGIN_CHALLENGE
- purple_debug_info("msim", "response=<%s>\n", data_out);
-#endif
-
- *response_len = data_out_len;
-
- return (gchar *)data_out;
-}
-
-/**
- * Process a login challenge, sending a response.
- *
- * @param session
- * @param msg Login challenge message.
- *
- * @return TRUE if successful, FALSE if not
- */
-static gboolean
-msim_login_challenge(MsimSession *session, MsimMessage *msg)
-{
- PurpleAccount *account;
- gchar *response;
- guint response_len;
- gchar *nc;
- gsize nc_len;
- gboolean ret;
-
- g_return_val_if_fail(msg != NULL, FALSE);
-
- g_return_val_if_fail(msim_msg_get_binary(msg, "nc", &nc, &nc_len), FALSE);
-
- account = session->account;
-
- g_return_val_if_fail(account != NULL, FALSE);
-
- purple_connection_update_progress(session->gc, _("Reading challenge"), 1, 4);
-
- purple_debug_info("msim", "nc is %" G_GSIZE_FORMAT
- " bytes, decoded\n", nc_len);
-
- if (nc_len != MSIM_AUTH_CHALLENGE_LENGTH) {
- purple_debug_info("msim", "bad nc length: %" G_GSIZE_MODIFIER
- "x != 0x%x\n", nc_len, MSIM_AUTH_CHALLENGE_LENGTH);
- purple_connection_error_reason (session->gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Unexpected challenge length from server"));
- return FALSE;
- }
-
- purple_connection_update_progress(session->gc, _("Logging in"), 2, 4);
-
- response_len = 0;
- response = msim_compute_login_response(nc, account->username, account->password, &response_len);
-
- g_free(nc);
-
- ret = msim_send(session,
- "login2", MSIM_TYPE_INTEGER, MSIM_AUTH_ALGORITHM,
- /* This is actually user's email address. */
- "username", MSIM_TYPE_STRING, g_strdup(account->username),
- /* GString will be freed in msim_msg_free() in msim_send(). */
- "response", MSIM_TYPE_BINARY, g_string_new_len(response, response_len),
- "clientver", MSIM_TYPE_INTEGER, MSIM_CLIENT_VERSION,
- "langid", MSIM_TYPE_INTEGER, MSIM_LANGUAGE_ID_ENGLISH,
- "imlang", MSIM_TYPE_STRING, g_strdup(MSIM_LANGUAGE_NAME_ENGLISH),
- "reconn", MSIM_TYPE_INTEGER, 0,
- "status", MSIM_TYPE_INTEGER, 100,
- "id", MSIM_TYPE_INTEGER, 1,
- NULL);
-
- g_free(response);
-
- return ret;
-}
-
-/**
- * Process unrecognized information.
- *
- * @param session
- * @param msg An MsimMessage that was unrecognized, or NULL.
- * @param note Information on what was unrecognized, or NULL.
- */
-void
-msim_unrecognized(MsimSession *session, MsimMessage *msg, gchar *note)
-{
- /* TODO: Some more context, outwardly equivalent to a backtrace,
- * for helping figure out what this msg is for. What was going on?
- * But not too much information so that a user
- * posting this dump reveals confidential information.
- */
-
- /* TODO: dump unknown msgs to file, so user can send them to me
- * if they wish, to help add support for new messages (inspired
- * by Alexandr Shutko, who maintains OSCAR protocol documentation).
- *
- * Filed enhancement ticket for libpurple as #4688.
- */
-
- purple_debug_info("msim", "Unrecognized data on account for %s\n",
- (session && session->account && session->account->username) ?
- session->account->username : "(NULL)");
- if (note) {
- purple_debug_info("msim", "(Note: %s)\n", note);
- }
-
- if (msg) {
- msim_msg_dump("Unrecognized message dump: %s\n", msg);
- }
-}
-
-/** Called when the session key arrives to check whether the user
- * has a username, and set one if desired. */
-static gboolean
-msim_is_username_set(MsimSession *session, MsimMessage *msg)
-{
- g_return_val_if_fail(msg != NULL, FALSE);
- g_return_val_if_fail(session->gc != NULL, FALSE);
-
- session->sesskey = msim_msg_get_integer(msg, "sesskey");
- purple_debug_info("msim", "SESSKEY=<%d>\n", session->sesskey);
-
- /* What is proof? Used to be uid, but now is 52 base64'd bytes... */
-
- /* Comes with: proof,profileid,userid,uniquenick -- all same values
- * some of the time, but can vary. This is our own user ID. */
- session->userid = msim_msg_get_integer(msg, "userid");
-
- /* Save uid to account so this account can be looked up by uid. */
- purple_account_set_int(session->account, "uid", session->userid);
-
- /* Not sure what profileid is used for. */
- if (msim_msg_get_integer(msg, "profileid") != session->userid) {
- msim_unrecognized(session, msg,
- "Profile ID didn't match user ID, don't know why");
- }
-
- /* We now know are our own username, only after we're logged in..
- * which is weird, but happens because you login with your email
- * address and not username. Will be freed in msim_session_destroy(). */
- session->username = msim_msg_get_string(msg, "uniquenick");
-
- /* If user lacks a username, help them get one. */
- if (msim_msg_get_integer(msg, "uniquenick") == session->userid) {
- purple_debug_info("msim_is_username_set", "no username is set\n");
- purple_request_yes_no(session->gc,
- _("MySpaceIM - No Username Set"),
- _("You appear to have no MySpace username."),
- _("Would you like to set one now? (Note: THIS CANNOT BE CHANGED!)"),
- 0,
- session->account,
- NULL,
- NULL,
- session->gc,
- G_CALLBACK(msim_set_username_cb),
- G_CALLBACK(msim_do_not_set_username_cb));
- purple_debug_info("msim_is_username_set","'username not set' alert prompted\n");
- return FALSE;
- }
- return TRUE;
-}
-
-#ifdef MSIM_USE_KEEPALIVE
-/**
- * Check if the connection is still alive, based on last communication.
- */
-static gboolean
-msim_check_alive(gpointer data)
-{
- MsimSession *session;
- time_t delta;
-
- session = (MsimSession *)data;
-
- delta = time(NULL) - session->last_comm;
-
- /* purple_debug_info("msim", "msim_check_alive: delta=%d\n", delta); */
- if (delta >= MSIM_KEEPALIVE_INTERVAL) {
- purple_debug_info("msim",
- "msim_check_alive: %zu > interval of %d, presumed dead\n",
- delta, MSIM_KEEPALIVE_INTERVAL);
- purple_connection_error_reason(session->gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Lost connection with server"));
-
- return FALSE;
- }
-
- return TRUE;
-}
-#endif
-
-/**
- * Handle mail reply checks.
- */
-static void
-msim_check_inbox_cb(MsimSession *session, const MsimMessage *reply, gpointer data)
-{
- MsimMessage *body;
- guint i, n;
- /* Information for each new inbox message type. */
- static struct
- {
- const gchar *key;
- guint bit;
- const gchar *url;
- const gchar *text;
- } message_types[] = {
- { "Mail", MSIM_INBOX_MAIL, "http://messaging.myspace.com/index.cfm?fuseaction=mail.inbox", NULL },
- { "BlogComment", MSIM_INBOX_BLOG_COMMENT, "http://blog.myspace.com/index.cfm?fuseaction=blog", NULL },
- { "ProfileComment", MSIM_INBOX_PROFILE_COMMENT, "http://home.myspace.com/index.cfm?fuseaction=user", NULL },
- { "FriendRequest", MSIM_INBOX_FRIEND_REQUEST, "http://messaging.myspace.com/index.cfm?fuseaction=mail.friendRequests", NULL },
- { "PictureComment", MSIM_INBOX_PICTURE_COMMENT, "http://home.myspace.com/index.cfm?fuseaction=user", NULL }
- };
- const gchar *froms[G_N_ELEMENTS(message_types) + 1] = { "" },
- *tos[G_N_ELEMENTS(message_types) + 1] = { "" },
- *urls[G_N_ELEMENTS(message_types) + 1] = { "" },
- *subjects[G_N_ELEMENTS(message_types) + 1] = { "" };
-
- g_return_if_fail(reply != NULL);
-
- /* Can't write _()'d strings in array initializers. Workaround. */
- /* khc: then use N_() in the array initializer and use _() when they are
- used */
- message_types[0].text = _("New mail messages");
- message_types[1].text = _("New blog comments");
- message_types[2].text = _("New profile comments");
- message_types[3].text = _("New friend requests!");
- message_types[4].text = _("New picture comments");
-
- body = msim_msg_get_dictionary(reply, "body");
-
- if (body == NULL)
- return;
-
- n = 0;
-
- for (i = 0; i < G_N_ELEMENTS(message_types); ++i) {
- const gchar *key;
- guint bit;
-
- key = message_types[i].key;
- bit = message_types[i].bit;
-
- if (msim_msg_get(body, key)) {
- /* Notify only on when _changes_ from no mail -> has mail
- * (edge triggered) */
- if (!(session->inbox_status & bit)) {
- purple_debug_info("msim", "msim_check_inbox_cb: got %s, at %d\n",
- key ? key : "(NULL)", n);
-
- subjects[n] = message_types[i].text;
- froms[n] = _("MySpace");
- tos[n] = session->username;
- /* TODO: append token, web challenge, so automatically logs in.
- * Would also need to free strings because they won't be static
- */
- urls[n] = message_types[i].url;
-
- ++n;
- } else {
- purple_debug_info("msim",
- "msim_check_inbox_cb: already notified of %s\n",
- key ? key : "(NULL)");
- }
-
- session->inbox_status |= bit;
- }
- }
-
- if (n) {
- purple_debug_info("msim",
- "msim_check_inbox_cb: notifying of %d\n", n);
-
- /* TODO: free strings with callback _if_ change to dynamic (w/ token) */
- purple_notify_emails(session->gc, /* handle */
- n, /* count */
- TRUE, /* detailed */
- subjects, froms, tos, urls,
- NULL, /* PurpleNotifyCloseCallback cb */
- NULL); /* gpointer user_data */
-
- }
-
- msim_msg_free(body);
-}
-
-/**
- * Send request to check if there is new mail.
- */
-static gboolean
-msim_check_inbox(gpointer data)
-{
- MsimSession *session;
-
- session = (MsimSession *)data;
-
- purple_debug_info("msim", "msim_check_inbox: checking mail\n");
- g_return_val_if_fail(msim_send(session,
- "persist", MSIM_TYPE_INTEGER, 1,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_GET,
- "dsn", MSIM_TYPE_INTEGER, MG_CHECK_MAIL_DSN,
- "lid", MSIM_TYPE_INTEGER, MG_CHECK_MAIL_LID,
- "uid", MSIM_TYPE_INTEGER, session->userid,
- "rid", MSIM_TYPE_INTEGER,
- msim_new_reply_callback(session, msim_check_inbox_cb, NULL),
- "body", MSIM_TYPE_STRING, g_strdup(""),
- NULL), TRUE);
-
- /* Always return true, so that we keep checking for mail. */
- return TRUE;
-}
-
-/**
- * Add contact from server to buddy list, after looking up username.
- * Callback from msim_add_contact_from_server().
- *
- * @param data An MsimMessage * of the contact information. Will be freed.
- */
-static void
-msim_add_contact_from_server_cb(MsimSession *session, const MsimMessage *user_lookup_info, gpointer data)
-{
- MsimMessage *contact_info, *user_lookup_info_body;
- PurpleGroup *group;
- PurpleBuddy *buddy;
- MsimUser *user;
- gchar *username, *group_name, *display_name;
- guint uid, visibility;
-
- contact_info = (MsimMessage *)data;
- purple_debug_info("msim_add_contact_from_server_cb", "contact_info addr=%p\n", contact_info);
- uid = msim_msg_get_integer(contact_info, "ContactID");
-
- if (!user_lookup_info) {
- username = g_strdup(msim_uid2username_from_blist(session->account, uid));
- display_name = NULL;
- g_return_if_fail(username != NULL);
- } else {
- user_lookup_info_body = msim_msg_get_dictionary(user_lookup_info, "body");
- username = msim_msg_get_string(user_lookup_info_body, "UserName");
- display_name = msim_msg_get_string(user_lookup_info_body, "DisplayName");
- msim_msg_free(user_lookup_info_body);
- g_return_if_fail(username != NULL);
- }
-
- purple_debug_info("msim_add_contact_from_server_cb",
- "*** about to add/update username=%s\n", username);
-
- /* 1. Creates a new group, or gets existing group if it exists (or so
- * the documentation claims). */
- group_name = msim_msg_get_string(contact_info, "GroupName");
- if (!group_name || (*group_name == '\0')) {
- g_free(group_name);
- group_name = g_strdup(_("IM Friends"));
- purple_debug_info("myspace", "No GroupName specified, defaulting to '%s'.\n", group_name);
- }
- group = purple_find_group(group_name);
- if (!group) {
- group = purple_group_new(group_name);
- /* Add group to beginning. See #2752. */
- purple_blist_add_group(group, NULL);
- }
- g_free(group_name);
-
- visibility = msim_msg_get_integer(contact_info, "Visibility");
- if (visibility == 2) {
- /* This buddy is blocked (and therefore not on our buddy list */
- purple_privacy_deny_add(session->account, username, TRUE);
- msim_msg_free(contact_info);
- g_free(username);
- g_free(display_name);
- return;
- }
-
- /* 2. Get or create buddy */
- buddy = purple_find_buddy(session->account, username);
- if (!buddy) {
- purple_debug_info("msim_add_contact_from_server_cb",
- "creating new buddy: %s\n", username);
- buddy = purple_buddy_new(session->account, username, NULL);
- }
-
- /* TODO: use 'Position' in contact_info to take into account where buddy is */
- purple_blist_add_buddy(buddy, NULL, group, NULL /* insertion point */);
-
- if (strtol(username, NULL, 10) == uid) {
- /*
- * This user has not set their username! Set their server
- * alias to their display name so that we don't see a bunch
- * of numbers in the buddy list.
- */
- if (display_name != NULL) {
- purple_blist_node_set_string(PURPLE_BLIST_NODE(buddy), "DisplayName", display_name);
- serv_got_alias(session->gc, username, display_name);
- } else {
- serv_got_alias(session->gc, username,
- purple_blist_node_get_string(PURPLE_BLIST_NODE(buddy), "DisplayName"));
- }
- }
- g_free(display_name);
-
- /* 3. Update buddy information */
- user = msim_get_user_from_buddy(buddy, TRUE);
-
- user->id = uid;
- /* Keep track of the user ID across sessions */
- purple_blist_node_set_int(PURPLE_BLIST_NODE(buddy), "UserID", uid);
-
- /* Stores a few fields in the MsimUser, relevant to the buddy itself.
- * AvatarURL, Headline, ContactID. */
- msim_store_user_info(session, contact_info, NULL);
-
- /* TODO: other fields, store in 'user' */
- msim_msg_free(contact_info);
-
- g_free(username);
-}
-
-/**
- * Add first ContactID in contact_info to buddy's list. Used to add
- * server-side buddies to client-side list.
- *
- * @return TRUE if added.
- */
-static gboolean
-msim_add_contact_from_server(MsimSession *session, MsimMessage *contact_info)
-{
- guint uid;
- const gchar *username;
-
- uid = msim_msg_get_integer(contact_info, "ContactID");
- g_return_val_if_fail(uid != 0, FALSE);
-
- /* Lookup the username, since NickName and IMName is unreliable */
- username = msim_uid2username_from_blist(session->account, uid);
- if (!username) {
- gchar *uid_str;
-
- uid_str = g_strdup_printf("%d", uid);
- purple_debug_info("msim_add_contact_from_server",
- "contact_info addr=%p\n", contact_info);
- msim_lookup_user(session, uid_str, msim_add_contact_from_server_cb, (gpointer)msim_msg_clone(contact_info));
- g_free(uid_str);
- } else {
- msim_add_contact_from_server_cb(session, NULL, (gpointer)msim_msg_clone(contact_info));
- }
-
- /* Say that the contact was added, even if we're still looking up
- * their username. */
- return TRUE;
-}
-
-/**
- * Called when contact list is received from server.
- */
-static void
-msim_got_contact_list(MsimSession *session, const MsimMessage *reply, gpointer user_data)
-{
- MsimMessage *body, *body_node;
- gchar *msg;
- guint buddy_count;
-
- body = msim_msg_get_dictionary(reply, "body");
-
- buddy_count = 0;
-
- for (body_node = body;
- body_node != NULL;
- body_node = msim_msg_get_next_element_node(body_node))
- {
- MsimMessageElement *elem;
-
- elem = (MsimMessageElement *)body_node->data;
-
- if (g_str_equal(elem->name, "ContactID"))
- {
- /* Will look for first contact in body_node */
- if (msim_add_contact_from_server(session, body_node)) {
- ++buddy_count;
- }
- }
- }
-
- switch (GPOINTER_TO_UINT(user_data)) {
- case MSIM_CONTACT_LIST_IMPORT_ALL_FRIENDS:
- msg = g_strdup_printf(ngettext("%d buddy was added or updated from the server (including buddies already on the server-side list)",
- "%d buddies were added or updated from the server (including buddies already on the server-side list)",
- buddy_count),
- buddy_count);
- purple_notify_info(session->account, _("Add contacts from server"), msg, NULL);
- g_free(msg);
- break;
-
- case MSIM_CONTACT_LIST_IMPORT_TOP_FRIENDS:
- /* TODO */
- break;
-
- case MSIM_CONTACT_LIST_INITIAL_FRIENDS:
- /* The session is now set up, ready to be connected. This emits the
- * signedOn signal, so clients can now do anything with msimprpl, and
- * we're ready for it (session key, userid, username all setup). */
- purple_connection_update_progress(session->gc, _("Connected"), 3, 4);
- purple_connection_set_state(session->gc, PURPLE_CONNECTED);
- break;
- }
-
- msim_msg_free(body);
-}
-
-/**
- * Get contact list, calling msim_got_contact_list() with
- * what_to_do_after as user_data gpointer.
- *
- * @param what_to_do_after should be one of the MSIM_CONTACT_LIST_* #defines.
- */
-static gboolean
-msim_get_contact_list(MsimSession *session, int what_to_do_after)
-{
- return msim_send(session,
- "persist", MSIM_TYPE_INTEGER, 1,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_GET,
- "dsn", MSIM_TYPE_INTEGER, MG_LIST_ALL_CONTACTS_DSN,
- "lid", MSIM_TYPE_INTEGER, MG_LIST_ALL_CONTACTS_LID,
- "uid", MSIM_TYPE_INTEGER, session->userid,
- "rid", MSIM_TYPE_INTEGER,
- msim_new_reply_callback(session, msim_got_contact_list, GUINT_TO_POINTER(what_to_do_after)),
- "body", MSIM_TYPE_STRING, g_strdup(""),
- NULL);
-}
-
-/** Called after username is set, if necessary and we're open for business. */
-gboolean msim_we_are_logged_on(MsimSession *session)
-{
- MsimMessage *body;
-
- /* Set display name to username (otherwise will show email address) */
- purple_connection_set_display_name(session->gc, session->username);
-
- body = msim_msg_new(
- "UserID", MSIM_TYPE_INTEGER, session->userid,
- NULL);
-
- /* Request IM info about ourself. */
- msim_send(session,
- "persist", MSIM_TYPE_INTEGER, 1,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_GET,
- "dsn", MSIM_TYPE_INTEGER, MG_OWN_MYSPACE_INFO_DSN,
- "lid", MSIM_TYPE_INTEGER, MG_OWN_MYSPACE_INFO_LID,
- "rid", MSIM_TYPE_INTEGER, session->next_rid++,
- "UserID", MSIM_TYPE_INTEGER, session->userid,
- "body", MSIM_TYPE_DICTIONARY, body,
- NULL);
-
- /* Request MySpace info about ourself. */
- msim_send(session,
- "persist", MSIM_TYPE_INTEGER, 1,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_GET,
- "dsn", MSIM_TYPE_INTEGER, MG_OWN_IM_INFO_DSN,
- "lid", MSIM_TYPE_INTEGER, MG_OWN_IM_INFO_LID,
- "rid", MSIM_TYPE_INTEGER, session->next_rid++,
- "body", MSIM_TYPE_STRING, g_strdup(""),
- NULL);
-
- /* TODO: set options (persist cmd=514,dsn=1,lid=10) */
- /* TODO: set blocklist */
-
- /* Notify servers of our current status. */
- purple_debug_info("msim", "msim_we_are_logged_on: notifying servers of status\n");
- msim_set_status(session->account,
- purple_account_get_active_status(session->account));
-
- /* TODO: setinfo */
- /*
- body = msim_msg_new(
- "TotalFriends", MSIM_TYPE_INTEGER, 666,
- NULL);
- msim_send(session,
- "setinfo", MSIM_TYPE_BOOLEAN, TRUE,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- "info", MSIM_TYPE_DICTIONARY, body,
- NULL);
- */
-
- /* Disable due to problems with timeouts. TODO: fix. */
-#ifdef MSIM_USE_KEEPALIVE
- purple_timeout_add_seconds(MSIM_KEEPALIVE_INTERVAL_CHECK,
- (GSourceFunc)msim_check_alive, session);
-#endif
-
- /* Check mail if they want to. */
- if (purple_account_get_check_mail(session->account)) {
- session->inbox_handle = purple_timeout_add(MSIM_MAIL_INTERVAL_CHECK,
- (GSourceFunc)msim_check_inbox, session);
- msim_check_inbox(session);
- }
-
- msim_get_contact_list(session, MSIM_CONTACT_LIST_INITIAL_FRIENDS);
-
- return TRUE;
-}
-
-/**
- * Record the client version in the buddy list, from an incoming message.
- */
-static gboolean
-msim_incoming_bm_record_cv(MsimSession *session, MsimMessage *msg)
-{
- gchar *username, *cv;
- gboolean ret;
- MsimUser *user;
-
- username = msim_msg_get_string(msg, "_username");
- cv = msim_msg_get_string(msg, "cv");
-
- g_return_val_if_fail(username != NULL, FALSE);
- if (!cv) {
- /* No client version to record, don't worry about it. */
- g_free(username);
- return FALSE;
- }
-
- user = msim_find_user(session, username);
-
- if (user) {
- user->client_cv = atol(cv);
- ret = TRUE;
- } else {
- ret = FALSE;
- }
-
- g_free(username);
- g_free(cv);
-
- return ret;
-}
-
-#ifdef MSIM_SEND_CLIENT_VERSION
-/**
- * Send our client version to another unofficial client that understands it.
- */
-static gboolean
-msim_send_unofficial_client(MsimSession *session, gchar *username)
-{
- gchar *our_info;
- gboolean ret;
-
- our_info = g_strdup_printf("Libpurple %d.%d.%d - msimprpl %s",
- PURPLE_MAJOR_VERSION,
- PURPLE_MINOR_VERSION,
- PURPLE_MICRO_VERSION,
- MSIM_PRPL_VERSION_STRING);
-
- ret = msim_send_bm(session, username, our_info, MSIM_BM_UNOFFICIAL_CLIENT);
-
- return ret;
-}
-#endif
-/**
- * Process incoming status mood messages.
- *
- * @param session
- * @param msg Status mood update message. Caller frees.
- *
- * @return TRUE if successful.
- */
-static gboolean
-msim_incoming_status_mood(MsimSession *session, MsimMessage *msg) {
- /* TODO: I dont know too much about this yet,
- * so until I see how the official client handles
- * this and decide if libpurple should as well,
- * well just say we used it
- */
- gchar *ss;
- ss = msim_msg_get_string(msg, "msg");
- purple_debug_info("msim", "Incoming Status Message: %s", ss ? ss : "(NULL)");
- g_free(ss);
- return TRUE;
-}
-
-/**
- * Process incoming status messages.
- *
- * @param session
- * @param msg Status update message. Caller frees.
- *
- * @return TRUE if successful.
- */
-static gboolean
-msim_incoming_status(MsimSession *session, MsimMessage *msg)
-{
- MsimUser *user;
- GList *list;
- gchar *status_headline, *status_headline_escaped;
- gint status_code, purple_status_code;
- gchar *username;
- gchar *unrecognized_msg;
-
- g_return_val_if_fail(msg != NULL, FALSE);
-
- /* Helpfully looked up by msim_incoming_resolve() for us. */
- username = msim_msg_get_string(msg, "_username");
- g_return_val_if_fail(username != NULL, FALSE);
-
- {
- gchar *ss;
-
- ss = msim_msg_get_string(msg, "msg");
- purple_debug_info("msim",
- "msim_status: updating status for <%s> to <%s>\n",
- username, ss ? ss : "(NULL)");
- g_free(ss);
- }
-
- /* Example fields:
- * |s|0|ss|Offline
- * |s|1|ss|:-)|ls||ip|0|p|0
- */
- list = msim_msg_get_list(msg, "msg");
-
- status_code = msim_msg_get_integer_from_element(g_list_nth_data(list, MSIM_STATUS_ORDINAL_ONLINE));
- purple_debug_info("msim", "msim_status: %s's status code = %d\n", username, status_code);
- status_headline = msim_msg_get_string_from_element(g_list_nth_data(list, MSIM_STATUS_ORDINAL_HEADLINE));
-
- /* Add buddy if not found.
- * TODO: Could this be responsible for #3444? */
- user = msim_find_user(session, username);
- if (!user) {
- PurpleBuddy *buddy;
-
- purple_debug_info("msim",
- "msim_status: making new buddy for %s\n", username);
- buddy = purple_buddy_new(session->account, username, NULL);
- purple_blist_add_buddy(buddy, NULL, NULL, NULL);
-
- user = msim_get_user_from_buddy(buddy, TRUE);
- user->id = msim_msg_get_integer(msg, "f");
-
- /* Keep track of the user ID across sessions */
- purple_blist_node_set_int(PURPLE_BLIST_NODE(buddy), "UserID", user->id);
-
- msim_store_user_info(session, msg, NULL);
- } else {
- purple_debug_info("msim", "msim_status: found buddy %s\n", username);
- }
-
- if (status_headline && strcmp(status_headline, "") != 0) {
- /* The status headline is plaintext, but libpurple treats it as HTML,
- * so escape any HTML characters to their entity equivalents. */
- status_headline_escaped = g_markup_escape_text(status_headline, -1);
- } else {
- status_headline_escaped = NULL;
- }
-
- g_free(status_headline);
-
- /* don't copy; let the MsimUser own the headline, memory-wise */
- g_free(user->headline);
- user->headline = status_headline_escaped;
-
- /* Set user status */
- switch (status_code) {
- case MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN:
- purple_status_code = PURPLE_STATUS_OFFLINE;
- break;
-
- case MSIM_STATUS_CODE_ONLINE:
- purple_status_code = PURPLE_STATUS_AVAILABLE;
- break;
-
- case MSIM_STATUS_CODE_AWAY:
- purple_status_code = PURPLE_STATUS_AWAY;
- break;
-
- case MSIM_STATUS_CODE_IDLE:
- /* Treat idle as an available status. */
- purple_status_code = PURPLE_STATUS_AVAILABLE;
- break;
-
- default:
- purple_debug_info("msim", "msim_incoming_status for %s, unknown status code %d, treating as available\n",
- username, status_code);
- purple_status_code = PURPLE_STATUS_AVAILABLE;
-
- unrecognized_msg = g_strdup_printf("msim_incoming_status, unrecognized status code: %d\n",
- status_code);
- msim_unrecognized(session, NULL, unrecognized_msg);
- g_free(unrecognized_msg);
- }
-
- purple_prpl_got_user_status(session->account, username, purple_primitive_get_id_from_type(purple_status_code), NULL);
-
- if (status_code == MSIM_STATUS_CODE_IDLE) {
- purple_debug_info("msim", "msim_status: got idle: %s\n", username);
- purple_prpl_got_user_idle(session->account, username, TRUE, 0);
- } else {
- /* All other statuses indicate going back to non-idle. */
- purple_prpl_got_user_idle(session->account, username, FALSE, 0);
- }
-
-#ifdef MSIM_SEND_CLIENT_VERSION
- if (status_code == MSIM_STATUS_CODE_ONLINE) {
- /* Secretly whisper to unofficial clients our own version as they come online */
- msim_send_unofficial_client(session, username);
- }
-#endif
-
- if (status_code != MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN) {
- /* Get information when they come online.
- * TODO: periodically refresh?
- */
- purple_debug_info("msim_incoming_status", "%s came online, looking up\n", username);
- msim_lookup_user(session, username, NULL, NULL);
- }
-
- g_free(username);
- msim_msg_list_free(list);
-
- return TRUE;
-}
-
-/**
- * Handle an incoming instant message.
- *
- * @param session The session
- * @param msg Message from the server, containing 'f' (userid from) and 'msg'.
- * Should also contain username in _username from preprocessing.
- *
- * @return TRUE if successful.
- */
-static gboolean
-msim_incoming_im(MsimSession *session, MsimMessage *msg, const gchar *username)
-{
- gchar *msg_msim_markup, *msg_purple_markup;
- gchar *userid;
- time_t time_received;
- PurpleConversation *conv;
-
- /* I know this isn't really a string... but we need it to be one for
- * purple_find_conversation_with_account(). */
- userid = msim_msg_get_string(msg, "f");
-
- purple_debug_info("msim_incoming_im", "UserID is %s", userid);
-
- if (msim_is_userid(username)) {
- purple_debug_info("msim", "Ignoring message from spambot (%s) on account %s\n",
- username, purple_account_get_username(session->account));
- return FALSE;
- }
-
- /* See if a conversation with their UID already exists...*/
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, userid, session->account);
- if (conv) {
- /* Since the conversation exists... We need to normalize it */
- purple_conversation_set_name(conv, username);
- }
-
- msg_msim_markup = msim_msg_get_string(msg, "msg");
- g_return_val_if_fail(msg_msim_markup != NULL, FALSE);
-
- msg_purple_markup = msim_markup_to_html(session, msg_msim_markup);
- g_free(msg_msim_markup);
-
- time_received = msim_msg_get_integer(msg, "date");
- if (!time_received) {
- purple_debug_info("msim_incoming_im", "date in message not set.\n");
- time_received = time(NULL);
- }
-
- serv_got_im(session->gc, username, msg_purple_markup, PURPLE_MESSAGE_RECV, time_received);
-
- g_free(msg_purple_markup);
-
- return TRUE;
-}
-
-/**
- * Handle an incoming action message or an IM.
- *
- * @param session
- * @param msg
- *
- * @return TRUE if successful.
- */
-static gboolean
-msim_incoming_action_or_im(MsimSession *session, MsimMessage *msg)
-{
- gchar *msg_text, *username;
- gboolean rc;
-
- g_return_val_if_fail(msg != NULL, FALSE);
-
- msg_text = msim_msg_get_string(msg, "msg");
- g_return_val_if_fail(msg_text != NULL, FALSE);
-
- username = msim_msg_get_string(msg, "_username");
- g_return_val_if_fail(username != NULL, FALSE);
-
- purple_debug_info("msim",
- "msim_incoming_action_or_im: action <%s> from <%s>\n",
- msg_text, username);
-
- if (g_str_equal(msg_text, "%typing%")) {
- serv_got_typing(session->gc, username, 0, PURPLE_TYPING);
- rc = TRUE;
- } else if (g_str_equal(msg_text, "%stoptyping%")) {
- serv_got_typing_stopped(session->gc, username);
- rc = TRUE;
- } else if (strstr(msg_text, "!!!ZAP_SEND!!!=RTE_BTN_ZAPS_")) {
- rc = msim_incoming_zap(session, msg);
- } else if (strstr(msg_text, "!!!GroupCount=")) {
- /* TODO: support group chats. I think the number in msg_text has
- * something to do with the 'gid' field. */
- purple_debug_info("msim",
- "msim_incoming_action_or_im: "
- "TODO: implement #4691, group chats: %s\n", msg_text);
-
- rc = TRUE;
- } else if (strstr(msg_text, "!!!Offline=")) {
- /* TODO: support group chats. This one might mean a user
- * went offline or exited the chat. */
- purple_debug_info("msim", "msim_incoming_action_or_im: "
- "TODO: implement #4691, group chats: %s\n", msg_text);
-
- rc = TRUE;
- } else if (msim_msg_get_integer(msg, "aid") != 0) {
- purple_debug_info("msim", "TODO: implement #4691, group chat from %d on %d: %s\n",
- msim_msg_get_integer(msg, "aid"),
- msim_msg_get_integer(msg, "f"),
- msg_text);
-
- rc = TRUE;
- } else {
- rc = msim_incoming_im(session, msg, username);
- }
-
- g_free(msg_text);
- g_free(username);
-
- return rc;
-}
-
-/**
- * Process an incoming media (message background?) message.
- */
-static gboolean
-msim_incoming_media(MsimSession *session, MsimMessage *msg)
-{
- gchar *username, *text;
-
- username = msim_msg_get_string(msg, "_username");
- text = msim_msg_get_string(msg, "msg");
-
- g_return_val_if_fail(username != NULL, FALSE);
- g_return_val_if_fail(text != NULL, FALSE);
-
- purple_debug_info("msim", "msim_incoming_media: from %s, got msg=%s\n", username, text);
-
- /* Media messages are sent when the user opens a window to someone.
- * Tell libpurple they started typing and stopped typing, to inform the Psychic
- * Mode plugin so it too can open a window to the user. */
- serv_got_typing(session->gc, username, 0, PURPLE_TYPING);
- serv_got_typing_stopped(session->gc, username);
-
- g_free(username);
-
- return TRUE;
-}
-
-/**
- * Process an incoming "unofficial client" message. The plugin for
- * Miranda IM sends this message with the plugin information.
- */
-static gboolean
-msim_incoming_unofficial_client(MsimSession *session, MsimMessage *msg)
-{
- MsimUser *user;
- gchar *username, *client_info;
-
- username = msim_msg_get_string(msg, "_username");
- client_info = msim_msg_get_string(msg, "msg");
-
- g_return_val_if_fail(username != NULL, FALSE);
- g_return_val_if_fail(client_info != NULL, FALSE);
-
- purple_debug_info("msim", "msim_incoming_unofficial_client: %s is using client %s\n",
- username, client_info);
-
- user = msim_find_user(session, username);
-
- g_return_val_if_fail(user != NULL, FALSE);
-
- if (user->client_info) {
- g_free(user->client_info);
- }
- user->client_info = client_info;
-
- g_free(username);
- /* Do not free client_info - the MsimUser now owns it. */
-
- return TRUE;
-}
-
-/**
- * Handle an incoming buddy message.
- */
-static gboolean
-msim_incoming_bm(MsimSession *session, MsimMessage *msg)
-{
- guint bm;
-
- bm = msim_msg_get_integer(msg, "bm");
-
- msim_incoming_bm_record_cv(session, msg);
-
- switch (bm) {
- case MSIM_BM_STATUS:
- return msim_incoming_status(session, msg);
- case MSIM_BM_ACTION_OR_IM_DELAYABLE:
- case MSIM_BM_ACTION_OR_IM_INSTANT:
- return msim_incoming_action_or_im(session, msg);
- case MSIM_BM_MEDIA:
- return msim_incoming_media(session, msg);
- case MSIM_BM_UNOFFICIAL_CLIENT:
- return msim_incoming_unofficial_client(session, msg);
- case MSIM_BM_STATUS_MOOD:
- return msim_incoming_status_mood(session, msg);
- default:
- /*
- * Unknown message type! We used to call
- * msim_incoming_action_or_im(session, msg);
- * for these, but that doesn't help anything, and it means
- * we'll show broken gibberish if MySpace starts sending us
- * other message types.
- */
- purple_debug_warning("myspace", "Received unknown imcoming "
- "message, bm=%u\n", bm);
- return TRUE;
- }
-}
-
-/**
- * Process the initial server information from the server.
- */
-static gboolean
-msim_process_server_info(MsimSession *session, MsimMessage *msg)
-{
- MsimMessage *body;
-
- body = msim_msg_get_dictionary(msg, "body");
- g_return_val_if_fail(body != NULL, FALSE);
-
- /* Example body:
-AdUnitRefreshInterval=10.
-AlertPollInterval=360.
-AllowChatRoomEmoticonSharing=False.
-ChatRoomUserIDs=78744676;163733130;1300326231;123521495;142663391.
-CurClientVersion=673.
-EnableIMBrowse=True.
-EnableIMStuffAvatars=False.
-EnableIMStuffZaps=False.
-MaxAddAllFriends=100.
-MaxContacts=1000.
-MinClientVersion=594.
-MySpaceIM_ENGLISH=78744676.
-MySpaceNowTimer=720.
-PersistenceDataTimeout=900.
-UseWebChallenge=1.
-WebTicketGoHome=False
-
- Anything useful? TODO: use what is useful, and use it.
-*/
- purple_debug_info("msim_process_server_info",
- "maximum contacts: %d\n",
- msim_msg_get_integer(body, "MaxContacts"));
-
- session->server_info = body;
- /* session->server_info freed in msim_session_destroy */
-
- return TRUE;
-}
-
-/**
- * Process a web challenge, used to login to the web site.
- */
-static gboolean
-msim_web_challenge(MsimSession *session, MsimMessage *msg)
-{
- /* TODO: web challenge, store token. #2659. */
- return FALSE;
-}
-
-/**
- * Process a persistance message reply from the server.
- *
- * @param session
- * @param msg Message reply from server.
- *
- * @return TRUE if successful.
- *
- * msim_lookup_user sets callback for here
- */
-static gboolean
-msim_process_reply(MsimSession *session, MsimMessage *msg)
-{
- MSIM_USER_LOOKUP_CB cb;
- gpointer data;
- guint rid, cmd, dsn, lid;
-
- g_return_val_if_fail(msg != NULL, FALSE);
-
- msim_store_user_info(session, msg, NULL);
-
- rid = msim_msg_get_integer(msg, "rid");
- cmd = msim_msg_get_integer(msg, "cmd");
- dsn = msim_msg_get_integer(msg, "dsn");
- lid = msim_msg_get_integer(msg, "lid");
-
- /* Unsolicited messages */
- if (cmd == (MSIM_CMD_BIT_REPLY | MSIM_CMD_GET)) {
- if (dsn == MG_SERVER_INFO_DSN && lid == MG_SERVER_INFO_LID) {
- return msim_process_server_info(session, msg);
- } else if (dsn == MG_WEB_CHALLENGE_DSN && lid == MG_WEB_CHALLENGE_LID) {
- return msim_web_challenge(session, msg);
- }
- }
-
- /* If a callback is registered for this userid lookup, call it. */
- cb = g_hash_table_lookup(session->user_lookup_cb, GUINT_TO_POINTER(rid));
- data = g_hash_table_lookup(session->user_lookup_cb_data, GUINT_TO_POINTER(rid));
-
- if (cb) {
- purple_debug_info("msim", "msim_process_reply: calling callback now\n");
- /* Clone message, so that the callback 'cb' can use it (needs to free it also). */
- cb(session, msg, data);
- g_hash_table_remove(session->user_lookup_cb, GUINT_TO_POINTER(rid));
- g_hash_table_remove(session->user_lookup_cb_data, GUINT_TO_POINTER(rid));
- } else {
- purple_debug_info("msim",
- "msim_process_reply: no callback for rid %d\n", rid);
- }
-
- return TRUE;
-}
-
-/**
- * Handle an error from the server.
- *
- * @param session
- * @param msg The message.
- *
- * @return TRUE if successfully reported error.
- */
-static gboolean
-msim_error(MsimSession *session, MsimMessage *msg)
-{
- gchar *errmsg, *full_errmsg;
- guint err;
-
- g_return_val_if_fail(msg != NULL, FALSE);
-
- err = msim_msg_get_integer(msg, "err");
- errmsg = msim_msg_get_string(msg, "errmsg");
-
- full_errmsg = g_strdup_printf(_("Protocol error, code %d: %s"), err,
- errmsg ? errmsg : "no 'errmsg' given");
-
- g_free(errmsg);
-
- purple_debug_info("msim", "msim_error (sesskey=%d): %s\n",
- session->sesskey, full_errmsg);
-
- /* Destroy session if fatal. */
- if (msim_msg_get(msg, "fatal")) {
- PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
- purple_debug_info("msim", "fatal error, closing\n");
-
- switch (err) {
- case MSIM_ERROR_INCORRECT_PASSWORD: /* Incorrect password */
- reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
- if (!purple_account_get_remember_password(session->account))
- purple_account_set_password(session->account, NULL);
-#ifdef MSIM_MAX_PASSWORD_LENGTH
- if (session->account->password && (strlen(session->account->password) > MSIM_MAX_PASSWORD_LENGTH)) {
- gchar *suggestion;
-
- suggestion = g_strdup_printf(_("%s Your password is "
- "%zu characters, which is longer than the "
- "maximum length of %d. Please shorten your "
- "password at http://profileedit.myspace.com/index.cfm?fuseaction=accountSettings.changePassword and try again."),
- full_errmsg,
- strlen(session->account->password),
- MSIM_MAX_PASSWORD_LENGTH);
-
- /* Replace full_errmsg. */
- g_free(full_errmsg);
- full_errmsg = suggestion;
- } else {
- g_free(full_errmsg);
- full_errmsg = g_strdup(_("Incorrect username or password"));
- }
-#endif
- break;
- case MSIM_ERROR_LOGGED_IN_ELSEWHERE: /* Logged in elsewhere */
- reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE;
- if (!purple_account_get_remember_password(session->account))
- purple_account_set_password(session->account, NULL);
- break;
- }
- purple_connection_error_reason(session->gc, reason, full_errmsg);
- } else {
- purple_notify_error(session->account, _("MySpaceIM Error"), full_errmsg, NULL);
- }
-
- g_free(full_errmsg);
-
- return TRUE;
-}
-
-/**
- * Process a message.
- *
- * @param session
- * @param msg A message from the server, ready for processing (possibly with resolved username information attached). Caller frees.
- *
- * @return TRUE if successful. FALSE if processing failed.
- */
-static gboolean
-msim_process(MsimSession *session, MsimMessage *msg)
-{
- g_return_val_if_fail(session != NULL, FALSE);
- g_return_val_if_fail(msg != NULL, FALSE);
-
- if (msim_msg_get_integer(msg, "lc") == 1) {
- return msim_login_challenge(session, msg);
- } else if (msim_msg_get_integer(msg, "lc") == 2) {
- /* return msim_we_are_logged_on(session, msg); */
- if (msim_is_username_set(session, msg)) {
- return msim_we_are_logged_on(session);
- } else {
- /* No username is set... We'll wait for the callbacks to do their work */
- /* When they're all done, the last one will call msim_we_are_logged_on() and pick up where we left off */
- return FALSE;
- }
- } else if (msim_msg_get(msg, "bm")) {
- return msim_incoming_bm(session, msg);
- } else if (msim_msg_get(msg, "rid")) {
- return msim_process_reply(session, msg);
- } else if (msim_msg_get(msg, "error")) {
- return msim_error(session, msg);
- } else if (msim_msg_get(msg, "ka")) {
- return TRUE;
- } else {
- msim_unrecognized(session, msg, "in msim_process");
- return FALSE;
- }
-}
-
-/**
- * After a uid is resolved to username, tag it with the username and submit for processing.
- *
- * @param session
- * @param userinfo Response messsage to resolving request.
- * @param data MsimMessage *, the message to attach information to.
- */
-static void
-msim_incoming_resolved(MsimSession *session, const MsimMessage *userinfo,
- gpointer data)
-{
- gchar *username;
- MsimMessage *msg, *body;
-
- g_return_if_fail(userinfo != NULL);
-
- body = msim_msg_get_dictionary(userinfo, "body");
- g_return_if_fail(body != NULL);
-
- username = msim_msg_get_string(body, "UserName");
- g_return_if_fail(username != NULL);
- /* Note: username will be owned by 'msg' below. */
-
- msg = (MsimMessage *)data;
- g_return_if_fail(msg != NULL);
-
- /* TODO: more elegant solution than below. attach whole message? */
- /* Special elements name beginning with '_', we'll use internally within the
- * program (did not come directly from the wire). */
- msg = msim_msg_append(msg, "_username", MSIM_TYPE_STRING, username); /* This makes 'msg' the owner of 'username' */
-
- /* TODO: attach more useful information, like ImageURL */
-
- msim_process(session, msg);
-
- msim_msg_free(msg);
- msim_msg_free(body);
-}
-
-/**
- * Preprocess incoming messages, resolving as needed, calling
- * msim_process() when ready to process.
- *
- * @param session
- * @param msg MsimMessage *, freed by caller.
- */
-static gboolean
-msim_preprocess_incoming(MsimSession *session, MsimMessage *msg)
-{
- g_return_val_if_fail(msg != NULL, FALSE);
-
- if (msim_msg_get(msg, "bm") && msim_msg_get(msg, "f")) {
- guint uid;
- const gchar *username;
-
- /* 'f' = userid message is from, in buddy messages */
- uid = msim_msg_get_integer(msg, "f");
-
- username = msim_uid2username_from_blist(session->account, uid);
-
- if (username) {
- /* Know username already, use it. */
- purple_debug_info("msim", "msim_preprocess_incoming: tagging with _username=%s\n",
- username);
- msg = msim_msg_append(msg, "_username", MSIM_TYPE_STRING, g_strdup(username));
- return msim_process(session, msg);
-
- } else {
- gchar *from;
-
- /* Send lookup request. */
- /* XXX: where is msim_msg_get_string() freed? make _strdup and _nonstrdup. */
- purple_debug_info("msim", "msim_incoming: sending lookup, setting up callback\n");
- from = msim_msg_get_string(msg, "f");
- msim_lookup_user(session, from, msim_incoming_resolved, msim_msg_clone(msg));
- g_free(from);
-
- /* indeterminate */
- return TRUE;
- }
- } else {
- /* Nothing to resolve - send directly to processing. */
- return msim_process(session, msg);
- }
-}
-
-/**
- * Callback when input available.
- *
- * @param gc_uncasted A PurpleConnection pointer.
- * @param source File descriptor.
- * @param cond PURPLE_INPUT_READ
- *
- * Reads the input, and calls msim_preprocess_incoming() to handle it.
- */
-static void
-msim_input_cb(gpointer gc_uncasted, gint source, PurpleInputCondition cond)
-{
- PurpleConnection *gc;
- MsimSession *session;
- gchar *end;
- int n;
-
- g_return_if_fail(gc_uncasted != NULL);
- g_return_if_fail(source >= 0); /* Note: 0 is a valid fd */
-
- gc = (PurpleConnection *)(gc_uncasted);
- session = gc->proto_data;
-
- /* libpurple/eventloop.h only defines these two */
- if (cond != PURPLE_INPUT_READ && cond != PURPLE_INPUT_WRITE) {
- purple_debug_info("msim_input_cb", "unknown condition=%d\n", cond);
- purple_connection_error_reason (gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Invalid input condition"));
- return;
- }
-
- g_return_if_fail(cond == PURPLE_INPUT_READ);
-
- /* Mark down that we got data, so we don't timeout. */
- session->last_comm = time(NULL);
-
- /* If approaching end of buffer, reallocate some more memory. */
- if (session->rxsize < session->rxoff + MSIM_READ_BUF_SIZE) {
- purple_debug_info("msim",
- "msim_input_cb: %d-byte read buffer full, rxoff=%d, " "growing by %d bytes\n",
- session->rxsize, session->rxoff, MSIM_READ_BUF_SIZE);
- session->rxsize += MSIM_READ_BUF_SIZE;
- session->rxbuf = g_realloc(session->rxbuf, session->rxsize);
-
- return;
- }
-
- purple_debug_info("msim", "dynamic buffer at %d (max %d), reading up to %d\n",
- session->rxoff, session->rxsize,
- MSIM_READ_BUF_SIZE - session->rxoff - 1);
-
- /* Read into buffer. On Win32, need recv() not read(). session->fd also holds
- * the file descriptor, but it sometimes differs from the 'source' parameter.
- */
- n = recv(session->fd,
- session->rxbuf + session->rxoff,
- session->rxsize - session->rxoff - 1, 0);
-
- if (n < 0) {
- gchar *tmp;
-
- if (errno == EAGAIN)
- /* No worries */
- return;
-
- tmp = g_strdup_printf(_("Lost connection with server: %s"),
- g_strerror(errno));
- purple_connection_error_reason(gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
- g_free(tmp);
- return;
- } else if (n == 0) {
- purple_connection_error_reason(gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Server closed the connection"));
- return;
- }
-
- /* Null terminate */
- purple_debug_info("msim", "msim_input_cb: going to null terminate "
- "at n=%d\n", n);
- session->rxbuf[session->rxoff + n] = 0;
-
-#ifdef MSIM_CHECK_EMBEDDED_NULLS
- /* Check for embedded NULs. I don't handle them, and they shouldn't occur. */
- if (strlen(session->rxbuf + session->rxoff) != n) {
- /* Occurs after login, but it is not a null byte. */
- purple_debug_info("msim", "msim_input_cb: strlen=%d, but read %d bytes"
- "--null byte encountered?\n",
- strlen(session->rxbuf + session->rxoff), n);
- /*purple_connection_error_reason (gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- "Invalid message - null byte on input"); */
- return;
- }
-#endif
-
- session->rxoff += n;
- purple_debug_info("msim", "msim_input_cb: read=%d\n", n);
-
-#ifdef MSIM_DEBUG_RXBUF
- purple_debug_info("msim", "buf=<%s>\n", session->rxbuf);
-#endif
-
- /* Look for \\final\\ end markers. If found, process message. */
- while((end = strstr(session->rxbuf, MSIM_FINAL_STRING))) {
- MsimMessage *msg;
-
-#ifdef MSIM_DEBUG_RXBUF
- purple_debug_info("msim", "in loop: buf=<%s>\n", session->rxbuf);
-#endif
- *end = 0;
- msg = msim_parse(session->rxbuf);
- if (!msg) {
- purple_debug_info("msim", "msim_input_cb: couldn't parse rxbuf\n");
- purple_connection_error_reason (gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Unable to parse message"));
- break;
- } else {
- /* Process message and then free it (processing function should
- * clone message if it wants to keep it afterwards.) */
- if (!msim_preprocess_incoming(session, msg)) {
- msim_msg_dump("msim_input_cb: preprocessing message failed on msg: %s\n", msg);
- }
- msim_msg_free(msg);
- }
-
- /* Move remaining part of buffer to beginning. */
- session->rxoff -= strlen(session->rxbuf) + strlen(MSIM_FINAL_STRING);
- memmove(session->rxbuf, end + strlen(MSIM_FINAL_STRING),
- session->rxsize - (end + strlen(MSIM_FINAL_STRING) - session->rxbuf));
-
- /* Clear end of buffer
- * memset(end, 0, MSIM_READ_BUF_SIZE - (end - session->rxbuf));
- */
- }
-}
-
-/**
- * Callback when connected. Sets up input handlers.
- *
- * @param data A PurpleConnection pointer.
- * @param source File descriptor.
- * @param error_message
- */
-static void
-msim_connect_cb(gpointer data, gint source, const gchar *error_message)
-{
- PurpleConnection *gc;
- MsimSession *session;
-
- g_return_if_fail(data != NULL);
-
- gc = (PurpleConnection *)data;
- session = (MsimSession *)gc->proto_data;
-
- if (source < 0) {
- gchar *tmp = g_strdup_printf(_("Unable to connect: %s"),
- error_message);
- purple_connection_error_reason (gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
- g_free(tmp);
- return;
- }
-
- session->fd = source;
-
- gc->inpa = purple_input_add(source, PURPLE_INPUT_READ, msim_input_cb, gc);
-}
-
-/**
- * Start logging in to the MSIM servers.
- *
- * @param acct Account information to use to login.
- */
-static void
-msim_login(PurpleAccount *acct)
-{
- PurpleConnection *gc;
- const gchar *host;
- int port;
-
- g_return_if_fail(acct != NULL);
- g_return_if_fail(acct->username != NULL);
-
- purple_debug_info("msim", "logging in %s\n", acct->username);
-
- gc = purple_account_get_connection(acct);
- gc->proto_data = msim_session_new(acct);
- gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_NO_URLDESC;
-
- /*
- * Lets wipe out our local list of blocked buddies. We'll get a
- * list of all blocked buddies from the server, and we shouldn't
- * have stuff in the local list that isn't on the server list.
- */
- while (acct->deny != NULL)
- purple_privacy_deny_remove(acct, acct->deny->data, TRUE);
-
- /* 1. connect to server */
- purple_connection_update_progress(gc, _("Connecting"),
- 0, /* which connection step this is */
- 4); /* total number of steps */
-
- host = purple_account_get_string(acct, "server", MSIM_SERVER);
- port = purple_account_get_int(acct, "port", MSIM_PORT);
-
- /* From purple.sf.net/api:
- * """Note that this function name can be misleading--although it is called
- * "proxy connect," it is used for establishing any outgoing TCP connection,
- * whether through a proxy or not.""" */
-
- /* Calls msim_connect_cb when connected. */
- if (!purple_proxy_connect(gc, acct, host, port, msim_connect_cb, gc)) {
- /* TODO: try other ports if in auto mode, then save
- * working port and try that first next time. */
- purple_connection_error_reason (gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Unable to connect"));
- return;
- }
-}
-
-static void
-msim_buddy_free(PurpleBuddy *buddy)
-{
- msim_user_free(purple_buddy_get_protocol_data(buddy));
- purple_buddy_set_protocol_data(buddy, NULL);
-}
-
-/**
- * Close the connection.
- *
- * @param gc The connection.
- */
-static void
-msim_close(PurpleConnection *gc)
-{
- GSList *buddies;
- MsimSession *session;
-
- if (gc == NULL) {
- return;
- }
-
- /*
- * Free our protocol-specific buddy data. It almost seems like libpurple
- * should call our buddy_free prpl callback so that we don't need to do
- * this... but it doesn't, so we do.
- */
- buddies = purple_find_buddies(purple_connection_get_account(gc), NULL);
- while (buddies != NULL) {
- msim_buddy_free(buddies->data);
- buddies = g_slist_delete_link(buddies, buddies);
- }
-
- session = (MsimSession *)gc->proto_data;
- if (session == NULL)
- return;
-
- gc->proto_data = NULL;
-
- if (session->gc->inpa) {
- purple_input_remove(session->gc->inpa);
- }
- if (session->fd >= 0) {
- close(session->fd);
- session->fd = -1;
- }
-
- msim_session_destroy(session);
-}
-
-/**
- * Schedule an IM to be sent once the user ID is looked up.
- *
- * @param gc Connection.
- * @param who A user id, email, or username to send the message to.
- * @param message Instant message text to send.
- * @param flags Flags.
- *
- * @return 1 if successful or postponed, -1 if failed
- *
- * Allows sending to a user by username, email address, or userid. If
- * a username or email address is given, the userid must be looked up.
- * This function does that by calling msim_postprocess_outgoing().
- */
-static int
-msim_send_im(PurpleConnection *gc, const gchar *who, const gchar *message,
- PurpleMessageFlags flags)
-{
- MsimSession *session;
- gchar *message_msim;
- int rc;
-
- g_return_val_if_fail(gc != NULL, -1);
- g_return_val_if_fail(who != NULL, -1);
- g_return_val_if_fail(message != NULL, -1);
-
- /* 'flags' has many options, not used here. */
-
- session = (MsimSession *)gc->proto_data;
-
- message_msim = html_to_msim_markup(session, message);
-
- if (msim_send_bm(session, who, message_msim, MSIM_BM_ACTION_OR_IM_DELAYABLE)) {
- /* Return 1 to have Purple show this IM as being sent, 0 to not. I always
- * return 1 even if the message could not be sent, since I don't know if
- * it has failed yet--because the IM is only sent after the userid is
- * retrieved from the server (which happens after this function returns).
- * If an error does occur, it should be logged to the IM window.
- */
- rc = 1;
- } else {
- rc = -1;
- }
-
- g_free(message_msim);
-
- return rc;
-}
-
-/**
- * Handle when our user starts or stops typing to another user.
- *
- * @param gc
- * @param name The buddy name to which our user is typing to
- * @param state PURPLE_TYPING, PURPLE_TYPED, PURPLE_NOT_TYPING
- *
- * @return 0
- */
-static unsigned int
-msim_send_typing(PurpleConnection *gc, const gchar *name,
- PurpleTypingState state)
-{
- const gchar *typing_str;
- MsimSession *session;
-
- g_return_val_if_fail(gc != NULL, 0);
- g_return_val_if_fail(name != NULL, 0);
-
- session = (MsimSession *)gc->proto_data;
-
- switch (state) {
- case PURPLE_TYPING:
- typing_str = "%typing%";
- break;
-
- case PURPLE_TYPED:
- case PURPLE_NOT_TYPING:
- default:
- typing_str = "%stoptyping%";
- break;
- }
-
- purple_debug_info("msim", "msim_send_typing(%s): %d (%s)\n", name, state, typing_str);
- msim_send_bm(session, name, typing_str, MSIM_BM_ACTION_OR_IM_INSTANT);
- return 0;
-}
-
-/**
- * Callback for msim_get_info(), for when user info is received.
- */
-static void
-msim_get_info_cb(MsimSession *session, const MsimMessage *user_info_msg,
- gpointer data)
-{
- MsimMessage *msg;
- gchar *username;
- PurpleNotifyUserInfo *user_info;
- MsimUser *user;
-
- /* Get user{name,id} from msim_get_info, passed as an MsimMessage for
- orthogonality. */
- msg = (MsimMessage *)data;
- g_return_if_fail(msg != NULL);
-
- username = msim_msg_get_string(msg, "user");
- if (!username) {
- purple_debug_info("msim", "msim_get_info_cb: no 'user' in msg\n");
- return;
- }
-
- msim_msg_free(msg);
- purple_debug_info("msim", "msim_get_info_cb: got for user: %s\n", username);
-
- user = msim_find_user(session, username);
-
- if (!user) {
- /* User isn't on blist, create a temporary user to store info. */
- user = g_new0(MsimUser, 1);
- user->temporary_user = TRUE;
- }
-
- /* Update user structure with new information */
- msim_store_user_info(session, user_info_msg, user);
-
- user_info = purple_notify_user_info_new();
-
- /* Append data from MsimUser to PurpleNotifyUserInfo for display, full */
- msim_append_user_info(session, user_info, user, TRUE);
-
- purple_notify_userinfo(session->gc, username, user_info, NULL, NULL);
- purple_debug_info("msim", "msim_get_info_cb: username=%s\n", username);
-
- purple_notify_user_info_destroy(user_info);
-
- if (user->temporary_user)
- msim_user_free(user);
- g_free(username);
-}
-
-/**
- * Retrieve a user's profile.
- * @param username Username, user ID, or email address to lookup.
- */
-static void
-msim_get_info(PurpleConnection *gc, const gchar *username)
-{
- MsimSession *session;
- MsimUser *user;
- gchar *user_to_lookup;
- MsimMessage *user_msg;
-
- g_return_if_fail(gc != NULL);
- g_return_if_fail(username != NULL);
-
- session = (MsimSession *)gc->proto_data;
-
- /* Obtain uid of buddy. */
- user = msim_find_user(session, username);
-
- /* If is on buddy list, lookup by uid since it is faster. */
- if (user && user->id) {
- user_to_lookup = g_strdup_printf("%d", user->id);
- } else {
- /* Looking up buddy not on blist. Lookup by whatever user entered. */
- user_to_lookup = g_strdup(username);
- }
-
- /* Pass the username to msim_get_info_cb(), because since we lookup
- * by userid, the userinfo message will only contain the uid (not
- * the username) but it would be useful to display the username too.
- */
- user_msg = msim_msg_new(
- "user", MSIM_TYPE_STRING, g_strdup(username),
- NULL);
- purple_debug_info("msim", "msim_get_info, setting up lookup, user=%s\n", username);
-
- msim_lookup_user(session, user_to_lookup, msim_get_info_cb, user_msg);
-
- g_free(user_to_lookup);
-}
-
-/**
- * Set status using an MSIM_STATUS_CODE_* value.
- * @param status_code An MSIM_STATUS_CODE_* value.
- * @param statstring Status string, must be a dynamic string (will be freed by msim_send).
- */
-static void
-msim_set_status_code(MsimSession *session, guint status_code, gchar *statstring)
-{
- g_return_if_fail(statstring != NULL);
-
- purple_debug_info("msim", "msim_set_status_code: going to set status to code=%d,str=%s\n",
- status_code, statstring);
-
- if (!msim_send(session,
- "status", MSIM_TYPE_INTEGER, status_code,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- "statstring", MSIM_TYPE_STRING, statstring,
- "locstring", MSIM_TYPE_STRING, g_strdup(""),
- NULL))
- {
- purple_debug_info("msim", "msim_set_status: failed to set status\n");
- }
-}
-
-/**
- * Set your status - callback for when user manually sets it.
- */
-static void
-msim_set_status(PurpleAccount *account, PurpleStatus *status)
-{
- PurpleStatusType *type;
- PurplePresence *pres;
- MsimSession *session;
- guint status_code;
- const gchar *message;
- gchar *stripped;
- gchar *unrecognized_msg;
-
- session = (MsimSession *)account->gc->proto_data;
-
- type = purple_status_get_type(status);
- pres = purple_status_get_presence(status);
-
- switch (purple_status_type_get_primitive(type)) {
- case PURPLE_STATUS_AVAILABLE:
- purple_debug_info("msim", "msim_set_status: available (%d->%d)\n", PURPLE_STATUS_AVAILABLE,
- MSIM_STATUS_CODE_ONLINE);
- status_code = MSIM_STATUS_CODE_ONLINE;
- break;
-
- case PURPLE_STATUS_INVISIBLE:
- purple_debug_info("msim", "msim_set_status: invisible (%d->%d)\n", PURPLE_STATUS_INVISIBLE,
- MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN);
- status_code = MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN;
- break;
-
- case PURPLE_STATUS_AWAY:
- purple_debug_info("msim", "msim_set_status: away (%d->%d)\n", PURPLE_STATUS_AWAY,
- MSIM_STATUS_CODE_AWAY);
- status_code = MSIM_STATUS_CODE_AWAY;
- break;
-
- default:
- purple_debug_info("msim", "msim_set_status: unknown "
- "status interpreting as online");
- status_code = MSIM_STATUS_CODE_ONLINE;
-
- unrecognized_msg = g_strdup_printf("msim_set_status, unrecognized status type: %d\n",
- purple_status_type_get_primitive(type));
- msim_unrecognized(session, NULL, unrecognized_msg);
- g_free(unrecognized_msg);
-
- break;
- }
-
- message = purple_status_get_attr_string(status, "message");
-
- /* Status strings are plain text. */
- if (message != NULL)
- stripped = purple_markup_strip_html(message);
- else
- stripped = g_strdup("");
-
- msim_set_status_code(session, status_code, stripped);
-
- /* If we should be idle, set that status. Time is irrelevant here. */
- if (purple_presence_is_idle(pres) && status_code != MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN)
- msim_set_idle(account->gc, 1);
-}
-
-/**
- * Go idle.
- */
-static void
-msim_set_idle(PurpleConnection *gc, int time)
-{
- MsimSession *session;
- PurpleStatus *status;
-
- g_return_if_fail(gc != NULL);
-
- session = (MsimSession *)gc->proto_data;
-
- status = purple_account_get_active_status(session->account);
-
- if (time == 0) {
- /* Going back from idle. In msim, idle is mutually exclusive
- * from the other states (you can only be away or idle, but not
- * both, for example), so by going non-idle I go back to what
- * libpurple says I should be.
- */
- msim_set_status(session->account, status);
- } else {
- const gchar *message;
- gchar *stripped;
-
- /* Set the idle message to the status message from the real
- * current status.
- */
- message = purple_status_get_attr_string(status, "message");
- if (message != NULL)
- stripped = purple_markup_strip_html(message);
- else
- stripped = g_strdup("");
-
- /* msim doesn't support idle time, so just go idle */
- msim_set_status_code(session, MSIM_STATUS_CODE_IDLE, stripped);
- }
-}
-
-/**
- * @return TRUE if everything was ok, FALSE if something went awry.
- */
-static gboolean
-msim_update_blocklist_for_buddy(MsimSession *session, const char *name, gboolean allow, gboolean block)
-{
- MsimMessage *msg;
- GList *list;
-
- list = NULL;
- list = g_list_prepend(list, allow ? "a+" : "a-");
- list = g_list_prepend(list, "<uid>");
- list = g_list_prepend(list, block ? "b+" : "b-");
- list = g_list_prepend(list, "<uid>");
- list = g_list_reverse(list);
-
- msg = msim_msg_new(
- "blocklist", MSIM_TYPE_BOOLEAN, TRUE,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- /* TODO: MsimMessage lists. Currently <uid> isn't replaced in lists. */
- /* "idlist", MSIM_TYPE_STRING, g_strdup("a-|<uid>|b-|<uid>"), */
- "idlist", MSIM_TYPE_LIST, list,
- NULL);
-
- if (!msim_postprocess_outgoing(session, msg, name, "idlist", NULL)) {
- purple_debug_error("myspace",
- "blocklist command failed for %s, allow=%d, block=%d\n",
- name, allow, block);
- msim_msg_free(msg);
- return FALSE;
- }
-
- msim_msg_free(msg);
-
- return TRUE;
-}
-
-/**
- * Add a buddy to user's buddy list.
- */
-static void
-msim_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
-{
- MsimSession *session;
- MsimMessage *msg;
- MsimMessage *msg_persist;
- MsimMessage *body;
- const char *name, *gname;
-
- session = (MsimSession *)gc->proto_data;
- name = purple_buddy_get_name(buddy);
- gname = group ? purple_group_get_name(group) : NULL;
-
- if (msim_get_user_from_buddy(buddy, FALSE) != NULL)
- return;
-
- purple_debug_info("msim", "msim_add_buddy: want to add %s to %s\n",
- name, gname ? gname : "(no group)");
-
- msg = msim_msg_new(
- "addbuddy", MSIM_TYPE_BOOLEAN, TRUE,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- /* "newprofileid" will be inserted here with uid. */
- "reason", MSIM_TYPE_STRING, g_strdup(""),
- NULL);
-
- if (!msim_postprocess_outgoing(session, msg, name, "newprofileid", "reason")) {
- purple_notify_error(NULL, NULL, _("Failed to add buddy"), _("'addbuddy' command failed."));
- msim_msg_free(msg);
- return;
- }
- msim_msg_free(msg);
-
- /* TODO: if addbuddy fails ('error' message is returned), delete added buddy from
- * buddy list since Purple adds it locally. */
-
- body = msim_msg_new(
- "ContactID", MSIM_TYPE_STRING, g_strdup("<uid>"),
- "GroupName", MSIM_TYPE_STRING, g_strdup(gname),
- "Position", MSIM_TYPE_INTEGER, 1000,
- "Visibility", MSIM_TYPE_INTEGER, 1,
- "NickName", MSIM_TYPE_STRING, g_strdup(""),
- "NameSelect", MSIM_TYPE_INTEGER, 0,
- NULL);
-
- /* TODO: Update blocklist. */
-
- msg_persist = msim_msg_new(
- "persist", MSIM_TYPE_INTEGER, 1,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_BIT_ACTION | MSIM_CMD_PUT,
- "dsn", MSIM_TYPE_INTEGER, MC_CONTACT_INFO_DSN,
- "uid", MSIM_TYPE_INTEGER, session->userid,
- "lid", MSIM_TYPE_INTEGER, MC_CONTACT_INFO_LID,
- /* TODO: Use msim_new_reply_callback to get rid. */
- "rid", MSIM_TYPE_INTEGER, session->next_rid++,
- "body", MSIM_TYPE_DICTIONARY, body,
- NULL);
-
- if (!msim_postprocess_outgoing(session, msg_persist, name, "body", NULL))
- {
- purple_notify_error(NULL, NULL, _("Failed to add buddy"), _("persist command failed"));
- msim_msg_free(msg_persist);
- return;
- }
- msim_msg_free(msg_persist);
-
- /* Add to allow list, remove from block list */
- msim_update_blocklist_for_buddy(session, name, TRUE, FALSE);
-}
-
-/**
- * Remove a buddy from the user's buddy list.
- */
-static void
-msim_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
-{
- MsimSession *session;
- MsimMessage *delbuddy_msg;
- MsimMessage *persist_msg;
- const char *name;
-
- session = (MsimSession *)gc->proto_data;
- name = purple_buddy_get_name(buddy);
-
- delbuddy_msg = msim_msg_new(
- "delbuddy", MSIM_TYPE_BOOLEAN, TRUE,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- /* 'delprofileid' with uid will be inserted here. */
- NULL);
-
- if (!msim_postprocess_outgoing(session, delbuddy_msg, name, "delprofileid", NULL)) {
- purple_notify_error(NULL, NULL, _("Failed to remove buddy"), _("'delbuddy' command failed"));
- msim_msg_free(delbuddy_msg);
- return;
- }
- msim_msg_free(delbuddy_msg);
-
- persist_msg = msim_msg_new(
- "persist", MSIM_TYPE_INTEGER, 1,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_BIT_ACTION | MSIM_CMD_DELETE,
- "dsn", MSIM_TYPE_INTEGER, MD_DELETE_BUDDY_DSN,
- "lid", MSIM_TYPE_INTEGER, MD_DELETE_BUDDY_LID,
- "uid", MSIM_TYPE_INTEGER, session->userid,
- "rid", MSIM_TYPE_INTEGER, session->next_rid++,
- /* <uid> will be replaced by postprocessing */
- "body", MSIM_TYPE_STRING, g_strdup("ContactID=<uid>"),
- NULL);
-
- if (!msim_postprocess_outgoing(session, persist_msg, name, "body", NULL)) {
- purple_notify_error(NULL, NULL, _("Failed to remove buddy"), _("persist command failed"));
- msim_msg_free(persist_msg);
- return;
- }
- msim_msg_free(persist_msg);
-
- /*
- * Remove from our approve list and from our block list (this
- * doesn't seem like it would be necessary, but the official client
- * does it)
- */
- if (!msim_update_blocklist_for_buddy(session, name, FALSE, FALSE)) {
- purple_notify_error(NULL, NULL,
- _("Failed to remove buddy"), _("blocklist command failed"));
- return;
- }
- msim_buddy_free(buddy);
-}
-
-/**
- * Remove a buddy from the user's buddy list and add them to the block list.
- */
-static void
-msim_add_deny(PurpleConnection *gc, const char *name)
-{
- MsimSession *session;
- MsimMessage *msg, *body;
-
- session = (MsimSession *)gc->proto_data;
-
- /* Remove from buddy list */
- msg = msim_msg_new(
- "delbuddy", MSIM_TYPE_BOOLEAN, TRUE,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- /* 'delprofileid' with uid will be inserted here. */
- NULL);
- if (!msim_postprocess_outgoing(session, msg, name, "delprofileid", NULL))
- purple_debug_error("myspace", "delbuddy command failed\n");
- msim_msg_free(msg);
-
- /* Remove from our approve list and add to our block list */
- msim_update_blocklist_for_buddy(session, name, FALSE, TRUE);
-
- /*
- * Add the buddy to our list of blocked contacts, so we know they
- * are blocked if we log in with another client
- */
- body = msim_msg_new(
- "ContactID", MSIM_TYPE_STRING, g_strdup("<uid>"),
- "Visibility", MSIM_TYPE_INTEGER, 2,
- NULL);
- msg = msim_msg_new(
- "persist", MSIM_TYPE_INTEGER, 1,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_BIT_ACTION | MSIM_CMD_PUT,
- "dsn", MSIM_TYPE_INTEGER, MC_CONTACT_INFO_DSN,
- "lid", MSIM_TYPE_INTEGER, MC_CONTACT_INFO_LID,
- "rid", MSIM_TYPE_INTEGER, session->next_rid++,
- "body", MSIM_TYPE_DICTIONARY, body,
- NULL);
- if (!msim_postprocess_outgoing(session, msg, name, "body", NULL))
- purple_debug_error("myspace", "add to block list command failed\n");
- msim_msg_free(msg);
-
- /*
- * TODO: MySpace doesn't allow blocked buddies on our buddy list,
- * do they? If not then we need to remove the buddy from
- * libpurple's buddy list.
- */
-}
-
-/**
- * Remove a buddy from the user's block list.
- */
-static void
-msim_rem_deny(PurpleConnection *gc, const char *name)
-{
- MsimSession *session;
- MsimMessage *msg, *body;
-
- session = (MsimSession *)gc->proto_data;
-
- /*
- * Remove from our list of blocked contacts, so we know they
- * are no longer blocked if we log in with another client
- */
- body = msim_msg_new(
- "ContactID", MSIM_TYPE_STRING, g_strdup("<uid>"),
- NULL);
- msg = msim_msg_new(
- "persist", MSIM_TYPE_INTEGER, 1,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_BIT_ACTION | MSIM_CMD_DELETE,
- "dsn", MSIM_TYPE_INTEGER, MC_DELETE_CONTACT_INFO_DSN,
- "lid", MSIM_TYPE_INTEGER, MC_DELETE_CONTACT_INFO_LID,
- "rid", MSIM_TYPE_INTEGER, session->next_rid++,
- "body", MSIM_TYPE_DICTIONARY, body,
- NULL);
- if (!msim_postprocess_outgoing(session, msg, name, "body", NULL))
- purple_debug_error("myspace", "remove from block list command failed\n");
- msim_msg_free(msg);
-
- /* Remove from our approve list and our block list */
- msim_update_blocklist_for_buddy(session, name, FALSE, FALSE);
-}
-
-/**
- * Returns a string of a username in canonical form. Basically removes all the
- * spaces, lowercases the string, and looks up user IDs to usernames.
- * Normalizing tom, TOM, Tom, and 6221 wil all return 'tom'.
- *
- * Borrowed this code from oscar_normalize. Added checking for
- * "if userid, get name before normalizing"
- */
-static const char *msim_normalize(const PurpleAccount *account, const char *str) {
- static char normalized[BUF_LEN];
- char *tmp1, *tmp2;
- int i, j;
- guint id;
-
- g_return_val_if_fail(str != NULL, NULL);
-
- if (msim_is_userid(str)) {
- /* Have user ID, we need to get their username first :) */
- const char *username;
-
- /* If the account does not exist, we can't look up the user. */
- if (!account || !account->gc)
- return str;
-
- id = atol(str);
- username = msim_uid2username_from_blist((PurpleAccount *)account, id);
- if (!username) {
- /* Not in buddy list... scheisse... TODO: Manual Lookup! Bug #4631 */
- /* Note: manual lookup using msim_lookup_user() is a problem inside
- * msim_normalize(), because msim_lookup_user() calls a callback function
- * when the user information has been looked up, but msim_normalize() expects
- * the result immediately. */
- strncpy(normalized, str, BUF_LEN);
- } else {
- strncpy(normalized, username, BUF_LEN);
- }
- } else {
- /* Have username. */
- strncpy(normalized, str, BUF_LEN);
- }
-
- /* Strip spaces. */
- for (i=0, j=0; normalized[j]; j++) {
- if (normalized[j] != ' ')
- normalized[i++] = normalized[j];
- }
- normalized[i] = '\0';
-
- /* Lowercase and perform UTF-8 normalization. */
- tmp1 = g_utf8_strdown(normalized, -1);
- tmp2 = g_utf8_normalize(tmp1, -1, G_NORMALIZE_DEFAULT);
- g_snprintf(normalized, sizeof(normalized), "%s", tmp2);
- g_free(tmp2);
- g_free(tmp1);
-
- /* TODO: re-add caps and spacing back to what the user wanted.
- * User can format their own names, for example 'msimprpl' is shown
- * as 'MsIm PrPl' in the official client.
- *
- * TODO: file a ticket to add this enhancement.
- */
-
- return normalized;
-}
-
-/**
- * Return whether the buddy can be messaged while offline.
- *
- * The protocol supports offline messages in just the same way as online
- * messages.
- */
-static gboolean
-msim_offline_message(const PurpleBuddy *buddy)
-{
- return TRUE;
-}
-
-/**
- * Send raw data to the server, possibly with embedded NULs.
- *
- * Used in prpl_info struct, so that plugins can have the most possible
- * control of what is sent over the connection. Inside this prpl,
- * msim_send_raw() is used, since it sends NUL-terminated strings (easier).
- *
- * @param gc PurpleConnection
- * @param buf Buffer to send
- * @param total_bytes Size of buffer to send
- *
- * @return Bytes successfully sent, or -1 on error.
- */
-/*
- * TODO: This needs to do non-blocking writes and use a watcher to check
- * when the fd is available to be written to.
- */
-static int
-msim_send_really_raw(PurpleConnection *gc, const char *buf, int total_bytes)
-{
- int total_bytes_sent;
- MsimSession *session;
-
- g_return_val_if_fail(gc != NULL, -1);
- g_return_val_if_fail(buf != NULL, -1);
- g_return_val_if_fail(total_bytes >= 0, -1);
-
- session = (MsimSession *)gc->proto_data;
-
- /* Loop until all data is sent, or a failure occurs. */
- total_bytes_sent = 0;
- do {
- int bytes_sent;
-
- bytes_sent = send(session->fd, buf + total_bytes_sent,
- total_bytes - total_bytes_sent, 0);
-
- if (bytes_sent < 0) {
- purple_debug_info("msim", "msim_send_raw(%s): send() failed: %s\n",
- buf, g_strerror(errno));
- return total_bytes_sent;
- }
- total_bytes_sent += bytes_sent;
-
- } while(total_bytes_sent < total_bytes);
-
- return total_bytes_sent;
-}
-
-/**
- * Send raw data (given as a NUL-terminated string) to the server.
- *
- * @param session
- * @param msg The raw data to send, in a NUL-terminated string.
- *
- * @return TRUE if succeeded, FALSE if not.
- *
- */
-gboolean
-msim_send_raw(MsimSession *session, const gchar *msg)
-{
- size_t len;
-
- g_return_val_if_fail(msg != NULL, FALSE);
-
- purple_debug_info("msim", "msim_send_raw: writing <%s>\n", msg);
- len = strlen(msg);
-
- return msim_send_really_raw(session->gc, msg, len) == len;
-}
-
-static GHashTable *
-msim_get_account_text_table(PurpleAccount *unused)
-{
- GHashTable *table;
-
- table = g_hash_table_new(g_str_hash, g_str_equal);
-
- g_hash_table_insert(table, "login_label", (gpointer)_("Email Address..."));
-
- return table;
-}
-
-/**
- * Callbacks called by Purple, to access this plugin.
- */
-static PurplePluginProtocolInfo prpl_info = {
- /* options */
- OPT_PROTO_USE_POINTSIZE /* specify font size in sane point size */
- | OPT_PROTO_MAIL_CHECK,
-
- /* | OPT_PROTO_IM_IMAGE - TODO: direct images. */
- NULL, /* user_splits */
- NULL, /* protocol_options */
- NO_BUDDY_ICONS, /* icon_spec - TODO: eventually should add this */
- msim_list_icon, /* list_icon */
- NULL, /* list_emblems */
- msim_status_text, /* status_text */
- msim_tooltip_text, /* tooltip_text */
- msim_status_types, /* status_types */
- msim_blist_node_menu, /* blist_node_menu */
- NULL, /* chat_info */
- NULL, /* chat_info_defaults */
- msim_login, /* login */
- msim_close, /* close */
- msim_send_im, /* send_im */
- NULL, /* set_info */
- msim_send_typing, /* send_typing */
- msim_get_info, /* get_info */
- msim_set_status, /* set_status */
- msim_set_idle, /* set_idle */
- NULL, /* change_passwd */
- msim_add_buddy, /* add_buddy */
- NULL, /* add_buddies */
- msim_remove_buddy, /* remove_buddy */
- NULL, /* remove_buddies */
- NULL, /* add_permit */
- msim_add_deny, /* add_deny */
- NULL, /* rem_permit */
- msim_rem_deny, /* rem_deny */
- NULL, /* set_permit_deny */
- NULL, /* join_chat */
- NULL, /* reject chat invite */
- NULL, /* get_chat_name */
- NULL, /* chat_invite */
- NULL, /* chat_leave */
- NULL, /* chat_whisper */
- NULL, /* chat_send */
- NULL, /* keepalive */
- NULL, /* register_user */
- NULL, /* get_cb_info */
- NULL, /* get_cb_away */
- NULL, /* alias_buddy */
- NULL, /* group_buddy */
- NULL, /* rename_group */
- msim_buddy_free, /* buddy_free */
- NULL, /* convo_closed */
- msim_normalize, /* normalize */
- NULL, /* set_buddy_icon */
- NULL, /* remove_group */
- NULL, /* get_cb_real_name */
- NULL, /* set_chat_topic */
- NULL, /* find_blist_chat */
- NULL, /* roomlist_get_list */
- NULL, /* roomlist_cancel */
- NULL, /* roomlist_expand_category */
- NULL, /* can_receive_file */
- NULL, /* send_file */
- NULL, /* new_xfer */
- msim_offline_message, /* offline_message */
- NULL, /* whiteboard_prpl_ops */
- msim_send_really_raw, /* send_raw */
- NULL, /* roomlist_room_serialize */
- NULL, /* unregister_user */
- msim_send_attention, /* send_attention */
- msim_attention_types, /* attention_types */
- sizeof(PurplePluginProtocolInfo), /* struct_size */
- msim_get_account_text_table, /* get_account_text_table */
- NULL, /* initiate_media */
- NULL, /* get_media_caps */
- NULL, /* get_moods */
- NULL, /* set_public_alias */
- NULL, /* get_public_alias */
- NULL, /* add_buddy_with_invite */
- NULL /* add_buddies_with_invite */
-};
-
-/**
- * Load the plugin.
- */
-static gboolean
-msim_load(PurplePlugin *plugin)
-{
- /* If compiled to use RC4 from libpurple, check if it is really there. */
- if (!purple_ciphers_find_cipher("rc4")) {
- purple_debug_error("msim", "rc4 not in libpurple, but it is required - not loading MySpaceIM plugin!\n");
- purple_notify_error(plugin, _("Missing Cipher"),
- _("The RC4 cipher could not be found"),
- _("Upgrade "
- "to a libpurple with RC4 support (>= 2.0.1). MySpaceIM "
- "plugin will not be loaded."));
- return FALSE;
- }
- return TRUE;
-}
-
-/**
- * Called when friends have been imported to buddy list on server.
- */
-static void
-msim_import_friends_cb(MsimSession *session, const MsimMessage *reply, gpointer user_data)
-{
- MsimMessage *body;
- gchar *completed;
-
- /* Check if the friends were imported successfully. */
- body = msim_msg_get_dictionary(reply, "body");
- g_return_if_fail(body != NULL);
- completed = msim_msg_get_string(body, "Completed");
- msim_msg_free(body);
- g_return_if_fail(completed != NULL);
- if (!g_str_equal(completed, "True"))
- {
- purple_debug_info("msim_import_friends_cb",
- "failed to import friends: %s", completed);
- purple_notify_error(session->account, _("Add friends from MySpace.com"),
- _("Importing friends failed"), NULL);
- g_free(completed);
- return;
- }
- g_free(completed);
-
- purple_debug_info("msim_import_friends_cb",
- "added friends to server-side buddy list, requesting new contacts from server");
-
- msim_get_contact_list(session, MSIM_CONTACT_LIST_IMPORT_ALL_FRIENDS);
-
- /* TODO: show, X friends have been added */
-}
-
-/**
- * Import friends from myspace.com.
- */
-static void msim_import_friends(PurplePluginAction *action)
-{
- PurpleConnection *gc;
- MsimSession *session;
- gchar *group_name;
-
- gc = (PurpleConnection *)action->context;
- session = (MsimSession *)gc->proto_data;
-
- group_name = "MySpace Friends";
-
- g_return_if_fail(msim_send(session,
- "persist", MSIM_TYPE_INTEGER, 1,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_PUT,
- "dsn", MSIM_TYPE_INTEGER, MC_IMPORT_ALL_FRIENDS_DSN,
- "lid", MSIM_TYPE_INTEGER, MC_IMPORT_ALL_FRIENDS_LID,
- "uid", MSIM_TYPE_INTEGER, session->userid,
- "rid", MSIM_TYPE_INTEGER,
- msim_new_reply_callback(session, msim_import_friends_cb, NULL),
- "body", MSIM_TYPE_STRING,
- g_strdup_printf("GroupName=%s", group_name),
- NULL));
-}
-
-/**
- * Actions menu for account.
- */
-static GList *
-msim_actions(PurplePlugin *plugin, gpointer context /* PurpleConnection* */)
-{
- GList *menu;
- PurplePluginAction *act;
-
- menu = NULL;
-
-#if 0
- /* TODO: find out how */
- act = purple_plugin_action_new(_("Find people..."), msim_);
- menu = g_list_append(menu, act);
-
- act = purple_plugin_action_new(_("Change IM name..."), NULL);
- menu = g_list_append(menu, act);
-#endif
-
- act = purple_plugin_action_new(_("Add friends from MySpace.com"), msim_import_friends);
- menu = g_list_append(menu, act);
-
- return menu;
-}
-
-/**
- * Based on MSN's plugin info comments.
- */
-static PurplePluginInfo info = {
- PURPLE_PLUGIN_MAGIC,
- PURPLE_MAJOR_VERSION,
- PURPLE_MINOR_VERSION,
- PURPLE_PLUGIN_PROTOCOL, /**< type */
- NULL, /**< ui_requirement */
- 0, /**< flags */
- NULL, /**< dependencies */
- PURPLE_PRIORITY_DEFAULT, /**< priority */
-
- "prpl-myspace", /**< id */
- "MySpaceIM", /**< name */
- MSIM_PRPL_VERSION_STRING, /**< version */
- /** summary */
- "MySpaceIM Protocol Plugin",
- /** description */
- "MySpaceIM Protocol Plugin",
- "Jeff Connelly <jeff2@soc.pidgin.im>", /**< author */
- "http://developer.pidgin.im/wiki/MySpaceIM/", /**< homepage */
-
- msim_load, /**< load */
- NULL, /**< unload */
- NULL, /**< destroy */
- NULL, /**< ui_info */
- &prpl_info, /**< extra_info */
- NULL, /**< prefs_info */
- msim_actions, /**< msim_actions */
- NULL, /**< reserved1 */
- NULL, /**< reserved2 */
- NULL, /**< reserved3 */
- NULL /**< reserved4 */
-};
-
-#ifdef MSIM_SELF_TEST
-/*
- * Test functions.
- * Used to test or try out the internal workings of msimprpl. If you're reading
- * this code for the first time, these functions can be instructive in learning
- * how msimprpl is architected.
- */
-
-/**
- * Test MsimMessage for basic functionality.
- */
-static int
-msim_test_msg(void)
-{
- MsimMessage *msg, *msg_cloned, *msg2;
- GList *list;
- gchar *packed, *packed_expected, *packed_cloned;
- guint failures;
-
- failures = 0;
-
- purple_debug_info("msim", "\n\nTesting MsimMessage\n");
- msg = msim_msg_new(NULL); /* Create a new, empty message. */
-
- /* Append some new elements. */
- msg = msim_msg_append(msg, "bx", MSIM_TYPE_BINARY, g_string_new_len("XXX", 3));
- msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, g_strdup("v1"));
- msg = msim_msg_append(msg, "k1", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(42));
- msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, g_strdup("v43"));
- msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, g_strdup("v52/xxx\\yyy"));
- msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, g_strdup("v7"));
- msim_msg_dump("msg debug str=%s\n", msg);
- packed = msim_msg_pack(msg);
-
- purple_debug_info("msim", "msg packed=%s\n", packed);
-
- packed_expected = "\\bx\\WFhY\\k1\\v1\\k1\\42\\k1"
- "\\v43\\k1\\v52/1xxx/2yyy\\k1\\v7\\final\\";
-
- if (!g_str_equal(packed, packed_expected)) {
- purple_debug_info("msim", "!!!(%d), msim_msg_pack not what expected: %s != %s\n",
- ++failures, packed, packed_expected);
- }
-
-
- msg_cloned = msim_msg_clone(msg);
- packed_cloned = msim_msg_pack(msg_cloned);
-
- purple_debug_info("msim", "msg cloned=%s\n", packed_cloned);
- if (!g_str_equal(packed, packed_cloned)) {
- purple_debug_info("msim", "!!!(%d), msim_msg_pack on cloned message not equal to original: %s != %s\n",
- ++failures, packed_cloned, packed);
- }
-
- g_free(packed);
- g_free(packed_cloned);
- msim_msg_free(msg_cloned);
- msim_msg_free(msg);
-
- /* Try some of the more advanced functionality */
- list = NULL;
-
- list = g_list_prepend(list, "item3");
- list = g_list_prepend(list, "item2");
- list = g_list_prepend(list, "item1");
- list = g_list_prepend(list, "item0");
-
- msg = msim_msg_new(NULL);
- msg = msim_msg_append(msg, "string", MSIM_TYPE_STRING, g_strdup("string value"));
- msg = msim_msg_append(msg, "raw", MSIM_TYPE_RAW, g_strdup("raw value"));
- msg = msim_msg_append(msg, "integer", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(3140));
- msg = msim_msg_append(msg, "boolean", MSIM_TYPE_BOOLEAN, GUINT_TO_POINTER(FALSE));
- msg = msim_msg_append(msg, "list", MSIM_TYPE_LIST, list);
-
- msim_msg_dump("msg with list=%s\n", msg);
- purple_debug_info("msim", "msg with list packed=%s\n", msim_msg_pack(msg));
-
- msg2 = msim_msg_new(NULL);
- msg2 = msim_msg_append(msg2, "outer", MSIM_TYPE_STRING, g_strdup("outer value"));
- msg2 = msim_msg_append(msg2, "body", MSIM_TYPE_DICTIONARY, msg);
- msim_msg_dump("msg with dict=%s\n", msg2); /* msg2 now 'owns' msg */
- purple_debug_info("msim", "msg with dict packed=%s\n", msim_msg_pack(msg2));
-
- msim_msg_free(msg2);
-
- return failures;
-}
-
-/**
- * Test protocol-level escaping/unescaping.
- */
-static int
-msim_test_escaping(void)
-{
- guint failures;
- gchar *raw, *escaped, *unescaped, *expected;
-
- failures = 0;
-
- purple_debug_info("msim", "\n\nTesting escaping\n");
-
- raw = "hello/world\\hello/world";
-
- escaped = msim_escape(raw);
- purple_debug_info("msim", "msim_test_escaping: raw=%s, escaped=%s\n", raw, escaped);
- expected = "hello/1world/2hello/1world";
- if (!g_str_equal(escaped, expected)) {
- purple_debug_info("msim", "!!!(%d), msim_escape failed: %s != %s\n",
- ++failures, escaped, expected);
- }
-
-
- unescaped = msim_unescape(escaped);
- g_free(escaped);
- purple_debug_info("msim", "msim_test_escaping: unescaped=%s\n", unescaped);
- if (!g_str_equal(raw, unescaped)) {
- purple_debug_info("msim", "!!!(%d), msim_unescape failed: %s != %s\n",
- ++failures, raw, unescaped);
- }
-
- return failures;
-}
-
-static void
-msim_test_all(void)
-{
- guint failures;
-
- failures = 0;
- failures += msim_test_msg();
- failures += msim_test_escaping();
-
- if (failures) {
- purple_debug_info("msim", "msim_test_all HAD FAILURES: %d\n", failures);
- } else {
- purple_debug_info("msim", "msim_test_all - all tests passed!\n");
- }
- exit(0);
-}
-#endif
-
-#ifdef MSIM_CHECK_NEWER_VERSION
-/**
- * Callback for when a currentversion.txt has been downloaded.
- */
-static void
-msim_check_newer_version_cb(PurpleUtilFetchUrlData *url_data,
- gpointer user_data,
- const gchar *url_text,
- gsize len,
- const gchar *error_message)
-{
- GKeyFile *keyfile;
- GError *error;
- GString *data;
- gchar *newest_filever;
-
- if (!url_text) {
- purple_debug_info("msim_check_newer_version_cb",
- "got error: %s\n", error_message);
- return;
- }
-
- purple_debug_info("msim_check_newer_version_cb",
- "url_text=%s\n", url_text ? url_text : "(NULL)");
-
- /* Prepend [group] so that GKeyFile can parse it (requires a group). */
- data = g_string_new(url_text);
- purple_debug_info("msim", "data=%s\n", data->str
- ? data->str : "(NULL)");
- data = g_string_prepend(data, "[group]\n");
-
- purple_debug_info("msim", "data=%s\n", data->str
- ? data->str : "(NULL)");
-
- /* url_text is variable=data\n...†*/
-
- /* Check FILEVER, 1.0.716.0. 716 is build, MSIM_CLIENT_VERSION */
- /* New (english) version can be downloaded from SETUPURL+SETUPFILE */
-
- error = NULL;
- keyfile = g_key_file_new();
-
- /* Default list seperator is ;, but currentversion.txt doesn't have
- * these, so set to an unused character to avoid parsing problems. */
- g_key_file_set_list_separator(keyfile, '\0');
-
- g_key_file_load_from_data(keyfile, data->str, data->len,
- G_KEY_FILE_NONE, &error);
- g_string_free(data, TRUE);
-
- if (error != NULL) {
- purple_debug_info("msim_check_newer_version_cb",
- "couldn't parse, error: %d %d %s\n",
- error->domain, error->code, error->message);
- g_error_free(error);
- return;
- }
-
- gchar **ks;
- guint n;
- ks = g_key_file_get_keys(keyfile, "group", &n, NULL);
- purple_debug_info("msim", "n=%d\n", n);
- guint i;
- for (i = 0; ks[i] != NULL; ++i)
- {
- purple_debug_info("msim", "%d=%s\n", i, ks[i]);
- }
-
- newest_filever = g_key_file_get_string(keyfile, "group",
- "FILEVER", &error);
-
- purple_debug_info("msim_check_newer_version_cb",
- "newest filever: %s\n", newest_filever ?
- newest_filever : "(NULL)");
- if (error != NULL) {
- purple_debug_info("msim_check_newer_version_cb",
- "error: %d %d %s\n",
- error->domain, error->code, error->message);
- g_error_free(error);
- }
-
- g_key_file_free(keyfile);
-
- exit(0);
-}
-#endif
-
-/**
- Handle a myim:addContact command, after username has been looked up.
- */
-static void
-msim_uri_handler_addContact_cb(MsimSession *session, MsimMessage *userinfo, gpointer data)
-{
- MsimMessage *body;
- gchar *username;
-
- body = msim_msg_get_dictionary(userinfo, "body");
- username = msim_msg_get_string(body, "UserName");
- msim_msg_free(body);
-
- if (!username) {
- guint uid;
-
- uid = msim_msg_get_integer(userinfo, "UserID");
- g_return_if_fail(uid != 0);
-
- username = g_strdup_printf("%d", uid);
- }
-
- purple_blist_request_add_buddy(session->account, username, _("Buddies"), NULL);
-
- g_free(username);
-}
-
-/* TODO: move uid->username resolving to IM sending and buddy adding functions,
- * so that user can manually add or IM by userid and username automatically
- * looked up if possible? */
-
-/**
- * Handle a myim:sendIM URI command, after username has been looked up.
- */
-static void
-msim_uri_handler_sendIM_cb(MsimSession *session, MsimMessage *userinfo, gpointer data)
-{
- PurpleConversation *conv;
- MsimMessage *body;
- gchar *username;
-
- body = msim_msg_get_dictionary(userinfo, "body");
- username = msim_msg_get_string(body, "UserName");
- msim_msg_free(body);
-
- if (!username) {
- guint uid;
-
- uid = msim_msg_get_integer(userinfo, "UserID");
- g_return_if_fail(uid != 0);
-
- username = g_strdup_printf("%d", uid);
- }
-
-
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, username, session->account);
- if (!conv) {
- purple_debug_info("msim_uri_handler", "creating new conversation for %s\n", username);
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, session->account, username);
- }
-
- /* Just open the window so the user can send an IM. */
- purple_conversation_present(conv);
-
- g_free(username);
-}
-
-static gboolean
-msim_uri_handler(const gchar *proto, const gchar *cmd, GHashTable *params)
-{
- PurpleAccount *account;
- MsimSession *session;
- GList *l;
- gchar *uid_str, *cid_str;
- guint uid, cid;
-
- if (g_ascii_strcasecmp(proto, "myim"))
- return FALSE;
-
- /* Parameters are case-insensitive. */
- uid_str = g_hash_table_lookup(params, "uid");
- cid_str = g_hash_table_lookup(params, "cid");
-
- uid = uid_str ? atol(uid_str) : 0;
- cid = cid_str ? atol(cid_str) : 0;
-
- /* Need a contact. */
- g_return_val_if_fail(cid != 0, FALSE);
-
- /* TODO: if auto=true, "Add all the people on this page to my IM List!", on
- * http://collect.myspace.com/index.cfm?fuseaction=im.friendslist. Don't need a cid. */
-
- /* Convert numeric contact ID back to a string. Needed for looking up. Don't just
- * directly use cid directly from parameters, because it might not be numeric.
- * It is trivial to change this to allow cID to be a username, but that's not how
- * the official MySpaceIM client works, so don't provide that functionality. */
- cid_str = g_strdup_printf("%d", cid);
-
-
- /* Find our account with specified user id, or use first connected account if uid=0. */
- account = NULL;
- l = purple_accounts_get_all();
- while (l) {
- if (purple_account_is_connected(l->data) &&
- (uid == 0 || purple_account_get_int(l->data, "uid", 0) == uid)) {
- account = l->data;
- break;
- }
- l = l->next;
- }
-
- if (!account) {
- purple_notify_error(NULL, _("myim URL handler"),
- _("No suitable MySpaceIM account could be found to open this myim URL."),
- _("Enable the proper MySpaceIM account and try again."));
- g_free(cid_str);
- return FALSE;
- }
-
- session = (MsimSession *)account->gc->proto_data;
- g_return_val_if_fail(session != NULL, FALSE);
-
- /* Lookup userid to username. TODO: push this down, to IM sending/contact
- * adding functions. */
-
- /* myim:sendIM?uID=USERID&cID=CONTACTID */
- if (!g_ascii_strcasecmp(cmd, "sendIM")) {
- msim_lookup_user(session, cid_str, (MSIM_USER_LOOKUP_CB)msim_uri_handler_sendIM_cb, NULL);
- g_free(cid_str);
- return TRUE;
-
- /* myim:addContact?uID=USERID&cID=CONTACTID */
- } else if (!g_ascii_strcasecmp(cmd, "addContact")) {
- msim_lookup_user(session, cid_str, (MSIM_USER_LOOKUP_CB)msim_uri_handler_addContact_cb, NULL);
- g_free(cid_str);
- return TRUE;
- }
-
- return FALSE;
-}
-
-/**
- * Initialize plugin.
- */
-static void
-init_plugin(PurplePlugin *plugin)
-{
-#ifdef MSIM_SELF_TEST
- msim_test_all();
- exit(0);
-#endif /* MSIM_SELF_TEST */
-
- PurpleAccountOption *option;
- static gboolean initialized = FALSE;
-
-#ifdef MSIM_CHECK_NEWER_VERSION
- /* PROBLEM: MySpace's servers always return Content-Location, and
- * libpurple redirects to it, infinitely, even though it is the same
- * location we requested! */
- purple_util_fetch_url("http://im.myspace.com/nsis/currentversion.txt",
- FALSE, /* not full URL */
- "MSIMAutoUpdateAgent", /* user agent */
- TRUE, /* use HTTP/1.1 */
- msim_check_newer_version_cb, NULL);
-#endif
-
- /* TODO: default to automatically try different ports. Make the user be
- * able to set the first port to try (like LastConnectedPort in Windows client). */
- option = purple_account_option_string_new(_("Connect server"), "server", MSIM_SERVER);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
- option = purple_account_option_int_new(_("Connect port"), "port", MSIM_PORT);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
-#ifdef MSIM_USER_WANTS_TO_CONFIGURE_STATUS_TEXT
- option = purple_account_option_bool_new(_("Show display name in status text"), "show_display_name", TRUE);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
- option = purple_account_option_bool_new(_("Show headline in status text"), "show_headline", TRUE);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-#endif
-
-#ifdef MSIM_USER_WANTS_TO_DISABLE_EMOTICONS
- option = purple_account_option_bool_new(_("Send emoticons"), "emoticons", TRUE);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-#endif
-
-#ifdef MSIM_USER_REALLY_CARES_ABOUT_PRECISE_FONT_SIZES
- option = purple_account_option_int_new(_("Screen resolution (dots per inch)"), "dpi", MSIM_DEFAULT_DPI);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
- option = purple_account_option_int_new(_("Base font size (points)"), "base_font_size", MSIM_BASE_FONT_POINT_SIZE);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-#endif
-
- /* Code below only runs once. Based on oscar.c's oscar_init(). */
- if (initialized)
- return;
-
- initialized = TRUE;
-
- purple_signal_connect(purple_get_core(), "uri-handler", &initialized,
- PURPLE_CALLBACK(msim_uri_handler), NULL);
-}
-
-PURPLE_INIT_PLUGIN(myspace, init_plugin, info);
diff --git a/libpurple/protocols/myspace/myspace.h b/libpurple/protocols/myspace/myspace.h
deleted file mode 100644
index eecfc20db9..0000000000
--- a/libpurple/protocols/myspace/myspace.h
+++ /dev/null
@@ -1,203 +0,0 @@
-/* MySpaceIM Protocol Plugin, header file
- *
- * Copyright (C) 2007, Jeff Connelly <jeff2@soc.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
- */
-
-#ifndef _MYSPACE_MYSPACE_H
-#define _MYSPACE_MYSPACE_H
-
-#include "internal.h"
-
-/* Other includes */
-#include <string.h>
-#include <errno.h>/* for EAGAIN */
-#include <stdarg.h>
-#include <math.h>
-
-#include <glib.h>
-
-#ifdef _WIN32
-#include "win32dep.h"
-#else
-/* For recv() and send(); needed to match Win32 */
-#include <sys/types.h>
-#include <sys/socket.h>
-#endif
-
-#include "notify.h"
-#include "plugin.h"
-#include "accountopt.h"
-#include "version.h"
-#include "cipher.h" /* for SHA-1 */
-#include "util.h" /* for base64 */
-#include "debug.h" /* for purple_debug_info */
-#include "request.h" /* For dialogs used in setting the username */
-#include "xmlnode.h"
-#include "core.h"
-#include "conversation.h" /* For late normalization */
-
-/* MySpaceIM includes */
-#include "persist.h"
-#include "message.h"
-#include "session.h"
-#include "zap.h"
-#include "markup.h"
-#include "user.h"
-
-/* Conditional compilation options */
-/* Send third-party client version? (Recognized by us and Miranda's plugin) */
-/*#define MSIM_SEND_CLIENT_VERSION */
-
-/* Debugging options */
-/* Low-level and rarely needed */
-/*#define MSIM_DEBUG_PARSE */
-/*#define MSIM_DEBUG_LOGIN_CHALLENGE*/
-/*#define MSIM_DEBUG_RXBUF */
-
-/* Encode unknown HTML tags from IM clients in messages as [tag], instead of
- * ignoring. Useful for debugging */
-/*#define MSIM_MARKUP_SHOW_UNKNOWN_TAGS */
-
-/* Define to cause init_plugin() to run some tests and print
- * the results to the Purple debug log, then exit. Useful to
- * run with 'pidgin -d' to see the output. Don't define if
- * you want to actually use the plugin! */
-/*#define MSIM_SELF_TEST */
-
-/* Constants */
-
-/* Maximum length of a password that is acceptable. This is the limit
- * on the official client (build 679) and on the 'new password' field at
- * http://settings.myspace.com/index.cfm?fuseaction=user.changepassword
- * (though curiously, not on the 'current password' field). */
-
-/* After login fails, if password is greater than this many characters,
- * warn user that it may be too long. */
-#define MSIM_MAX_PASSWORD_LENGTH 10
-
-/* Maximum length of usernames, when setting. */
-#define MSIM_MAX_USERNAME_LENGTH 25
-
-/* Build version of MySpaceIM to report to servers (1.0.xxx.0) */
-#define MSIM_CLIENT_VERSION 697
-
-/* Check for a newer official MySpaceIM client on startup?
- * (Mostly useful for developers) */
-/*#define MSIM_CHECK_NEWER_VERSION*/
-
-/* Language codes from http://www.microsoft.com/globaldev/reference/oslocversion.mspx */
-#define MSIM_LANGUAGE_ID_ENGLISH 1033
-#define MSIM_LANGUAGE_NAME_ENGLISH "ENGLISH"
-
-/* msimprpl version string of this plugin */
-#define MSIM_PRPL_VERSION_STRING "0.18"
-
-/* Default server */
-#define MSIM_SERVER "im.myspace.akadns.net"
-#define MSIM_PORT 1863 /* TODO: alternate ports and automatic */
-
-/* Time between keepalives (seconds) - if no data within this time, is dead. */
-#define MSIM_KEEPALIVE_INTERVAL (3 * 60)
-/*#define MSIM_USE_KEEPALIVE*/
-
-/* Time to check if alive (seconds) */
-#define MSIM_KEEPALIVE_INTERVAL_CHECK 30
-
-/* Time to check for new mail (milliseconds) */
-#define MSIM_MAIL_INTERVAL_CHECK (60 * 1000)
-
-/* Constants */
-#define HASH_SIZE 0x14 /**< Size of SHA-1 hash for login */
-#define NONCE_SIZE 0x20 /**< Half of decoded 'nc' field */
-#define MSIM_READ_BUF_SIZE (15 * 1024) /**< Receive buffer size */
-#define MSIM_FINAL_STRING "\\final\\" /**< Message end marker */
-
-/* Messages */
-#define MSIM_BM_ACTION_OR_IM_DELAYABLE 1
-#define MSIM_BM_STATUS 100
-#define MSIM_BM_ACTION_OR_IM_INSTANT 121
-#define MSIM_BM_MEDIA 122
-#define MSIM_BM_PROFILE 124
-#define MSIM_BM_STATUS_MOOD 126
-#define MSIM_BM_UNOFFICIAL_CLIENT 200
-
-/* Authentication algorithm for login2 */
-#define MSIM_AUTH_ALGORITHM 196610
-
-/* Recognized challenge length */
-#define MSIM_AUTH_CHALLENGE_LENGTH 0x40
-
-#ifdef SEND_OUR_IP_ADDRESSES
-/* TODO: obtain IPs of network interfaces from user's machine, instead of
- * hardcoding these values below (used in msim_compute_login_response).
- * This is not immediately
- * important because you can still connect and perform basic
- * functions of the protocol. There is also a high chance that the addreses
- * are RFC1918 private, so the servers couldn't do anything with them
- * anyways except make note of that fact. Probably important for any
- * kind of direct connection, or file transfer functionality.
- */
-
-#define MSIM_LOGIN_IP_LIST "\x00\x00\x00\x00\x05\x7f\x00\x00\x01\x00\x00\x00\x00\x0a\x00\x00\x40\xc0\xa8\x58\x01\xc0\xa8\x3c\x01"
-#define MSIM_LOGIN_IP_LIST_LEN 25
-#endif /* SEND_OUR_IP_ADDRESSES */
-
-/* Indexes into status string (0|1|2|3|..., but 0 always empty) */
-#define MSIM_STATUS_ORDINAL_EMPTY 0
-#define MSIM_STATUS_ORDINAL_UNKNOWNs 1
-#define MSIM_STATUS_ORDINAL_ONLINE 2
-#define MSIM_STATUS_ORDINAL_UNKNOWNss 3
-#define MSIM_STATUS_ORDINAL_HEADLINE 4
-#define MSIM_STATUS_ORDINAL_UNKNOWNls 5
-#define MSIM_STATUS_ORDINAL_UNKNOWN 6
-#define MSIM_STATUS_ORDINAL_UNKNOWN1 7
-#define MSIM_STATUS_ORDINAL_UNKNOWNp 8
-#define MSIM_STATUS_ORDINAL_UNKNOWN2 9
-
-/* Status codes - states a buddy (or you!) can be in. */
-#define MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN 0
-#define MSIM_STATUS_CODE_ONLINE 1
-#define MSIM_STATUS_CODE_IDLE 2
-#define MSIM_STATUS_CODE_AWAY 5
-
-/* Inbox status bitfield values for MsimSession.inbox_status. */
-#define MSIM_INBOX_MAIL (1 << 0)
-#define MSIM_INBOX_BLOG_COMMENT (1 << 1)
-#define MSIM_INBOX_PROFILE_COMMENT (1 << 2)
-#define MSIM_INBOX_FRIEND_REQUEST (1 << 3)
-#define MSIM_INBOX_PICTURE_COMMENT (1 << 4)
-
-/* Codes for msim_got_contact_list(), to tell what to do afterwards. */
-#define MSIM_CONTACT_LIST_INITIAL_FRIENDS 0
-#define MSIM_CONTACT_LIST_IMPORT_ALL_FRIENDS 1
-#define MSIM_CONTACT_LIST_IMPORT_TOP_FRIENDS 2
-
-/* Error codes */
-#define MSIM_ERROR_INCORRECT_PASSWORD 260
-#define MSIM_ERROR_LOGGED_IN_ELSEWHERE 6
-
-/* Functions */
-gboolean msim_send_raw(MsimSession *session, const gchar *msg);
-
-gboolean msim_send_bm(MsimSession *session, const gchar *who, const gchar *text, int type);
-
-gboolean msim_we_are_logged_on(MsimSession *session);
-
-void msim_unrecognized(MsimSession *session, MsimMessage *msg, gchar *note);
-guint msim_new_reply_callback(MsimSession *session, MSIM_USER_LOOKUP_CB cb, gpointer data);
-
-#endif /* !_MYSPACE_MYSPACE_H */
diff --git a/libpurple/protocols/myspace/persist.h b/libpurple/protocols/myspace/persist.h
deleted file mode 100644
index 09712c9143..0000000000
--- a/libpurple/protocols/myspace/persist.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/* MySpaceIM Protocol Plugin, persist commands
- *
- * Copyright (C) 2007, Jeff Connelly <jeff2@soc.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
- */
-
-#ifndef _MYSPACE_PERSIST_H
-#define _MYSPACE_PERSIST_H
-
-/** Command codes */
-#define MSIM_CMD_GET 1
-#define MSIM_CMD_PUT 2
-#define MSIM_CMD_DELETE 3
-
-/** Command bit fields */
-#define MSIM_CMD_BIT_CODE 255 /*< Bits specifying command code */
-#define MSIM_CMD_BIT_REPLY 256 /**< 1=reply, 0=request */
-#define MSIM_CMD_BIT_ACTION 512 /**< 1=action, 0=information */
-#define MSIM_CMD_BIT_ERROR 1024 /**< 1=error, 0=normal */
-
-/** Macros to read cmd bitfield. */
-#define MSIM_CMD_GET_CODE(x) (x & MSIM_CMD_BIT_CODE)
-#define MSIM_CMD_IS_REPLY(x) (x & MSIM_CMD_BIT_REPLY)
-#define MSIM_CMD_IS_REQUEST(x) !(x & MSIM_CMD_BIT_REPLY)
-#define MSIM_CMD_IS_ACTION(x) (x & MSIM_CMD_BIT_ACTION)
-#define MSIM_CMD_IS_INFO(x) !(x & MSIM_CMD_BIT_ACTION)
-#define MSIM_CMD_IS_ERROR(x) (x & MSIM_CMD_BIT_ERROR)
-#define MSIM_CMD_IS_NORMAL(x) !(x & MSIM_CMD_BIT_ERROR)
-
-/** Define a set of _DSN and _LID constants for a persistance request. */
-#define MSIM_PERSIST_DSN_LID(name,dsn,lid) \
- static const int name##_DSN = dsn; \
- static const int name##_LID = lid;
-
-/* Can't do this, errors:
- * persist.h:51:3: error: '#' is not followed by a macro parameter
- * In file included from myspace.c:37:
- * persist.h:56: error: expected ')' before numeric constant
- * So instead, I define const ints above.
-#define MSIM_PERSIST_DSN_LID(name,dsn,lid) \
- #define name##_DSN dsn \
- #define name##_LID lid
-#endif
-*/
-
-/** Messages to Get information dsn lid */
-MSIM_PERSIST_DSN_LID(MG_LIST_ALL_CONTACTS, 0, 1)
-MSIM_PERSIST_DSN_LID(MG_USER_INFO_BY_ID, 0, 2)
-MSIM_PERSIST_DSN_LID(MG_OWN_IM_INFO, 1, 4)
-MSIM_PERSIST_DSN_LID(MG_IM_INFO_BY_ID, 1, 17)
-MSIM_PERSIST_DSN_LID(MG_LIST_ALL_GROUPS, 2, 6)
-MSIM_PERSIST_DSN_LID(MG_MYSPACE_INFO_BY_ID, 4, 3)
-MSIM_PERSIST_DSN_LID(MG_OWN_MYSPACE_INFO, 4, 5)
-MSIM_PERSIST_DSN_LID(MG_MYSPACE_INFO_BY_STRING, 5, 7)
-MSIM_PERSIST_DSN_LID(MG_CHECK_MAIL, 7, 18)
-MSIM_PERSIST_DSN_LID(MG_WEB_CHALLENGE, 17, 26)
-MSIM_PERSIST_DSN_LID(MG_USER_SONG, 21, 28)
-MSIM_PERSIST_DSN_LID(MG_SERVER_INFO, 101, 20)
-
-/** Messages to Change/send information */
-MSIM_PERSIST_DSN_LID(MC_USER_PREFERENCES, 1, 10)
-MSIM_PERSIST_DSN_LID(MC_DELETE_CONTACT_INFO, 0, 8)
-MSIM_PERSIST_DSN_LID(MC_CONTACT_INFO, 0, 9)
-MSIM_PERSIST_DSN_LID(MC_SET_USERNAME, 9, 14)
-MSIM_PERSIST_DSN_LID(MC_IMPORT_ALL_FRIENDS, 14, 21)
-MSIM_PERSIST_DSN_LID(MC_INVITE, 16, 25)
-
-/** Messages to Delete information */
-MSIM_PERSIST_DSN_LID(MD_DELETE_BUDDY, 0, 8)
-
-/** Error codes */
-#define MERR_PARSE 1
-#define MERR_NOT_LOGGED_IN 2
-#define MERR_ANOTHER_LOGIN 6
-#define MERR_BAD_EMAIL 259
-#define MERR_BAD_PASSWORD 260
-#define MERR_BAD_UID_IN_PERSISTR 4352
-
-#endif /* !_MYSPACE_PERSIST_H */
diff --git a/libpurple/protocols/myspace/session.c b/libpurple/protocols/myspace/session.c
deleted file mode 100644
index 2d6e6c98d4..0000000000
--- a/libpurple/protocols/myspace/session.c
+++ /dev/null
@@ -1,98 +0,0 @@
-/* MySpaceIM Protocol Plugin, session
- *
- * Copyright (C) 2007, Jeff Connelly <jeff2@soc.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
- */
-
-#include "myspace.h"
-
-/* Session methods */
-
-/**
- * Create a new MSIM session.
- *
- * @param acct The account to create the session from.
- *
- * @return Pointer to a new session. Free with msim_session_destroy.
- */
-MsimSession *
-msim_session_new(PurpleAccount *acct)
-{
- MsimSession *session;
-
- g_return_val_if_fail(acct != NULL, NULL);
-
- session = g_new0(MsimSession, 1);
-
- session->magic = MSIM_SESSION_STRUCT_MAGIC;
- session->account = acct;
- session->gc = purple_account_get_connection(acct);
- session->sesskey = 0;
- session->userid = 0;
- session->username = NULL;
- session->fd = -1;
-
- /* TODO: Remove. */
- session->user_lookup_cb = g_hash_table_new_full(g_direct_hash,
- g_direct_equal, NULL, NULL); /* do NOT free function pointers! (values) */
- session->user_lookup_cb_data = g_hash_table_new_full(g_direct_hash,
- g_direct_equal, NULL, NULL);/* TODO: we don't know what the values are,
- they could be integers inside gpointers
- or strings, so I don't freed them.
- Figure this out, once free cache. */
-
- /* Created in msim_process_server_info() */
- session->server_info = NULL;
-
- session->rxoff = 0;
- session->rxsize = MSIM_READ_BUF_SIZE;
- session->rxbuf = g_new0(gchar, session->rxsize);
- session->next_rid = 1;
- session->last_comm = time(NULL);
- session->inbox_status = 0;
- session->inbox_handle = 0;
-
- return session;
-}
-
-/**
- * Free a session.
- *
- * @param session The session to destroy.
- */
-void
-msim_session_destroy(MsimSession *session)
-{
- session->magic = -1;
-
- g_free(session->rxbuf);
- g_free(session->username);
-
- /* TODO: Remove. */
- g_hash_table_destroy(session->user_lookup_cb);
- g_hash_table_destroy(session->user_lookup_cb_data);
-
- if (session->server_info) {
- msim_msg_free(session->server_info);
- }
-
- /* Stop checking the inbox at the end of the session. */
- if (session->inbox_handle) {
- purple_timeout_remove(session->inbox_handle);
- }
-
- g_free(session);
-}
diff --git a/libpurple/protocols/myspace/session.h b/libpurple/protocols/myspace/session.h
deleted file mode 100644
index 34e1ba96cd..0000000000
--- a/libpurple/protocols/myspace/session.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/* MySpaceIM Protocol Plugin, session
- *
- * Copyright (C) 2007, Jeff Connelly <jeff2@soc.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
- */
-
-#ifndef _MYSPACE_SESSION_H
-#define _MYSPACE_SESSION_H
-
-#include "account.h"
-
-/* Random number in every MsimSession, to ensure it is valid. */
-#define MSIM_SESSION_STRUCT_MAGIC 0xe4a6752b
-
-/* Everything needed to keep track of a session (proto_data field in PurpleConnection) */
-typedef struct _MsimSession
-{
- guint magic; /**< MSIM_SESSION_STRUCT_MAGIC */
- PurpleAccount *account;
- PurpleConnection *gc;
- guint sesskey; /**< Session key from server */
- guint userid; /**< This user's numeric user ID */
- gchar *username; /**< This user's unique username */
- gboolean show_only_to_list;
- int privacy_mode; /**< This is a bitmask */
- int offline_message_mode;
- gint fd; /**< File descriptor to/from server */
-
- /* TODO: Remove. */
- GHashTable *user_lookup_cb; /**< Username -> userid lookup callback */
- GHashTable *user_lookup_cb_data; /**< Username -> userid lookup callback data */
-
- MsimMessage *server_info; /**< Parameters from server */
-
- gchar *rxbuf; /**< Receive buffer */
- guint rxoff; /**< Receive buffer offset */
- guint rxsize; /**< Receive buffer size */
- guint next_rid; /**< Next request/response ID */
- time_t last_comm; /**< Time received last communication */
- guint inbox_status; /**< Bit field of inbox notifications */
- guint inbox_handle; /**< The handle for the mail check timer */
-} MsimSession;
-
-MsimSession *msim_session_new(PurpleAccount *acct);
-void msim_session_destroy(MsimSession *session);
-
-#endif /* !_MYSPACE_SESSION_H */
diff --git a/libpurple/protocols/myspace/user.c b/libpurple/protocols/myspace/user.c
deleted file mode 100644
index 7fdaaaba10..0000000000
--- a/libpurple/protocols/myspace/user.c
+++ /dev/null
@@ -1,892 +0,0 @@
-/* MySpaceIM Protocol Plugin, header file
- *
- * Copyright (C) 2007, Jeff Connelly <jeff2@soc.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
- */
-
-#include "myspace.h"
-
-static void msim_check_username_availability_cb(PurpleConnection *gc, const char *username_to_check);
-
-static char *msim_username_to_set;
-
-/**
- * Format the "now playing" indicator, showing the artist and song.
- *
- * @return Return a new string (must be g_free()'d), or NULL.
- */
-static gchar *
-msim_format_now_playing(const gchar *band, const gchar *song)
-{
- if ((band && *band) || (song && *song)) {
- return g_strdup_printf("%s - %s",
- (band && *band) ? band : "Unknown Artist",
- (song && *song) ? song : "Unknown Song");
- } else {
- return NULL;
- }
-}
-
-/**
- * Get the MsimUser from a PurpleBuddy, optionally creating it if needed.
- */
-MsimUser *
-msim_get_user_from_buddy(PurpleBuddy *buddy, gboolean create)
-{
- MsimUser *user;
-
- if (!buddy) {
- return NULL;
- }
-
- user = purple_buddy_get_protocol_data(buddy);
- if (create && !user) {
- PurpleBlistNode *node = PURPLE_BLIST_NODE(buddy);
-
- /* No MsimUser for this buddy; make one. */
-
- user = g_new0(MsimUser, 1);
- user->buddy = buddy;
- user->id = purple_blist_node_get_int(node, "UserID");
- purple_buddy_set_protocol_data(buddy, user);
- }
-
- return user;
-}
-
-void msim_user_free(MsimUser *user)
-{
- if (!user)
- return;
-
- if (user->url_data != NULL)
- purple_util_fetch_url_cancel(user->url_data);
-
- g_free(user->client_info);
- g_free(user->gender);
- g_free(user->location);
- g_free(user->headline);
- g_free(user->display_name);
- g_free(user->username);
- g_free(user->band_name);
- g_free(user->song_name);
- g_free(user->image_url);
- g_free(user);
-}
-
-/**
- * Find and return an MsimUser * representing a user on the buddy list, or NULL.
- */
-MsimUser *
-msim_find_user(MsimSession *session, const gchar *username)
-{
- PurpleBuddy *buddy;
-
- buddy = purple_find_buddy(session->account, username);
- if (!buddy) {
- return NULL;
- }
-
- return msim_get_user_from_buddy(buddy, TRUE);
-}
-
-/**
- * Append user information to a PurpleNotifyUserInfo, given an MsimUser.
- * Used by msim_tooltip_text() and msim_get_info_cb() to show a user's profile.
- */
-void
-msim_append_user_info(MsimSession *session, PurpleNotifyUserInfo *user_info, MsimUser *user, gboolean full)
-{
- PurplePresence *presence;
- gchar *str;
- guint cv;
-
- /* Useful to identify the account the tooltip refers to.
- * Other prpls show this. */
- if (user->username) {
- purple_notify_user_info_add_pair(user_info, _("User"), user->username);
- }
-
- /* a/s/l...the vitals */
- if (user->age) {
- char age[16];
- g_snprintf(age, sizeof(age), "%d", user->age);
- purple_notify_user_info_add_pair(user_info, _("Age"), age);
- }
-
- if (user->gender && *user->gender) {
- purple_notify_user_info_add_pair(user_info, _("Gender"), user->gender);
- }
-
- if (user->location && *user->location) {
- purple_notify_user_info_add_pair(user_info, _("Location"), user->location);
- }
-
- /* Other information */
- if (user->headline && *user->headline) {
- purple_notify_user_info_add_pair(user_info, _("Headline"), user->headline);
- }
-
- if (user->buddy != NULL) {
- presence = purple_buddy_get_presence(user->buddy);
-
- if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {
- PurpleStatus *status;
- const char *artist, *title;
-
- status = purple_presence_get_status(presence, "tune");
- title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE);
- artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST);
-
- str = msim_format_now_playing(artist, title);
- if (str && *str) {
- purple_notify_user_info_add_pair(user_info, _("Song"), str);
- }
- g_free(str);
- }
- }
-
- /* Note: total friends only available if looked up by uid, not username. */
- if (user->total_friends) {
- char friends[16];
- g_snprintf(friends, sizeof(friends), "%d", user->total_friends);
- purple_notify_user_info_add_pair(user_info, _("Total Friends"), friends);
- }
-
- if (full) {
- /* Client information */
- char *client = NULL;
-
- str = user->client_info;
- cv = user->client_cv;
-
- if (str && cv != 0) {
- client = g_strdup_printf("%s (build %d)", str, cv);
- } else if (str) {
- client = g_strdup(str);
- } else if (cv) {
- client = g_strdup_printf("Build %d", cv);
- }
- if (client && *client)
- purple_notify_user_info_add_pair(user_info, _("Client Version"), client);
- g_free(client);
- }
-
- if (full && user->id) {
- /* TODO: link to username, if available */
- char *profile;
- purple_notify_user_info_add_section_break(user_info);
- if (user->buddy != NULL)
- profile = g_strdup_printf("<a href=\"http://myspace.com/%s\">%s</a>",
- purple_buddy_get_name(user->buddy), _("View web profile"));
- else
- profile = g_strdup_printf("<a href=\"http://myspace.com/%d\">%s</a>",
- user->id, _("View web profile"));
- purple_notify_user_info_add_pair(user_info, NULL, profile);
- g_free(profile);
- }
-}
-
-/**
- * Callback for when a buddy icon finished being downloaded.
- */
-static void
-msim_downloaded_buddy_icon(PurpleUtilFetchUrlData *url_data,
- gpointer user_data,
- const gchar *url_text,
- gsize len,
- const gchar *error_message)
-{
- MsimUser *user = (MsimUser *)user_data;
- const char *name = purple_buddy_get_name(user->buddy);
- PurpleAccount *account;
-
- user->url_data = NULL;
-
- purple_debug_info("msim_downloaded_buddy_icon",
- "Downloaded %" G_GSIZE_FORMAT " bytes\n", len);
-
- if (!url_text) {
- purple_debug_info("msim_downloaded_buddy_icon",
- "failed to download icon for %s",
- name);
- return;
- }
-
- account = purple_buddy_get_account(user->buddy);
- purple_buddy_icons_set_for_user(account, name,
- g_memdup((gchar *)url_text, len), len,
- /* Use URL itself as buddy icon "checksum" (TODO: ETag) */
- user->image_url); /* checksum */
-}
-
-/**
- * Set the currently playing song artist and or title.
- *
- * @param user User associated with the now playing information.
- *
- * @param new_artist New artist to set, or NULL/empty to not change artist.
- *
- * @param new_title New title to set, or NULL/empty to not change title.
- *
- * If new_artist and new_title are NULL/empty, deactivate PURPLE_STATUS_TUNE.
- *
- * This function is useful because it lets you set the artist or title
- * individually, which purple_prpl_got_user_status() doesn't do.
- */
-static void msim_set_artist_or_title(MsimUser *user, const char *new_artist, const char *new_title)
-{
- PurplePresence *presence;
- PurpleAccount *account;
- const char *prev_artist, *prev_title;
- const char *name;
-
- if (user->buddy == NULL)
- /* User not on buddy list so nothing to do */
- return;
-
- prev_artist = NULL;
- prev_title = NULL;
-
- if (new_artist && !*new_artist)
- new_artist = NULL;
- if (new_title && !*new_title)
- new_title = NULL;
-
- account = purple_buddy_get_account(user->buddy);
- name = purple_buddy_get_name(user->buddy);
-
- if (!new_artist && !new_title) {
- purple_prpl_got_user_status_deactive(account, name, "tune");
- return;
- }
-
- presence = purple_buddy_get_presence(user->buddy);
-
- if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {
- PurpleStatus *status;
-
- status = purple_presence_get_status(presence, "tune");
- prev_title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE);
- prev_artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST);
- }
-
- if (!new_artist)
- new_artist = prev_artist;
-
- if (!new_title)
- new_title = prev_title;
-
- purple_prpl_got_user_status(account, name, "tune",
- PURPLE_TUNE_TITLE, new_title,
- PURPLE_TUNE_ARTIST, new_artist,
- NULL);
-}
-
-/**
- * Store a field of information about a buddy.
- *
- * @param key_str Key to store.
- * @param value_str Value string, either user takes ownership of this string
- * or it is freed if MsimUser doesn't store the string.
- * @param user User to store data in. Existing data will be replaced.
- */
-static void
-msim_store_user_info_each(const gchar *key_str, gchar *value_str, MsimUser *user)
-{
- const char *name = user->buddy ? purple_buddy_get_name(user->buddy) : NULL;
-
- if (g_str_equal(key_str, "UserID") || g_str_equal(key_str, "ContactID")) {
- /* Save to buddy list, if it exists, for quick cached uid lookup with msim_uid2username_from_blist(). */
- user->id = atol(value_str);
- g_free(value_str);
- if (user->buddy)
- {
- purple_debug_info("msim", "associating uid %s with username %s\n", key_str, name);
- purple_blist_node_set_int(PURPLE_BLIST_NODE(user->buddy), "UserID", user->id);
- }
- /* Need to store in MsimUser, too? What if not on blist? */
- } else if (g_str_equal(key_str, "Age")) {
- user->age = atol(value_str);
- g_free(value_str);
- } else if (g_str_equal(key_str, "Gender")) {
- g_free(user->gender);
- user->gender = value_str;
- } else if (g_str_equal(key_str, "Location")) {
- g_free(user->location);
- user->location = value_str;
- } else if (g_str_equal(key_str, "TotalFriends")) {
- user->total_friends = atol(value_str);
- g_free(value_str);
- } else if (g_str_equal(key_str, "DisplayName")) {
- g_free(user->display_name);
- user->display_name = value_str;
- } else if (g_str_equal(key_str, "BandName")) {
- msim_set_artist_or_title(user, value_str, NULL);
- g_free(value_str);
- } else if (g_str_equal(key_str, "SongName")) {
- msim_set_artist_or_title(user, NULL, value_str);
- g_free(value_str);
- } else if (g_str_equal(key_str, "UserName") || g_str_equal(key_str, "IMName") || g_str_equal(key_str, "NickName")) {
- /* Ignore because PurpleBuddy knows this already */
- g_free(value_str);
- } else if (g_str_equal(key_str, "ImageURL") || g_str_equal(key_str, "AvatarURL")) {
- const gchar *previous_url;
-
- if (user->temporary_user) {
- /* This user will be destroyed soon; don't try to look up its image or avatar,
- * since that won't return immediately and we will end up accessing freed data.
- */
- g_free(value_str);
- return;
- }
-
- g_free(user->image_url);
-
- user->image_url = value_str;
-
- /* Instead of showing 'no photo' picture, show nothing. */
- if (g_str_equal(user->image_url, "http://x.myspace.com/images/no_pic.gif"))
- {
- purple_buddy_icons_set_for_user(purple_buddy_get_account(user->buddy),
- name, NULL, 0, NULL);
- return;
- }
-
- /* TODO: use ETag for checksum */
- previous_url = purple_buddy_icons_get_checksum_for_user(user->buddy);
-
- /* Only download if URL changed */
- if (!previous_url || !g_str_equal(previous_url, user->image_url)) {
- if (user->url_data != NULL)
- purple_util_fetch_url_cancel(user->url_data);
- user->url_data = purple_util_fetch_url(user->image_url, TRUE, NULL, TRUE, msim_downloaded_buddy_icon, (gpointer)user);
- }
- } else if (g_str_equal(key_str, "LastImageUpdated")) {
- /* TODO: use somewhere */
- user->last_image_updated = atol(value_str);
- g_free(value_str);
- } else if (g_str_equal(key_str, "Headline")) {
- g_free(user->headline);
- user->headline = value_str;
- } else {
- /* TODO: other fields in MsimUser */
- gchar *msg;
-
- msg = g_strdup_printf("msim_store_user_info_each: unknown field %s=%s",
- key_str, value_str);
- g_free(value_str);
-
- msim_unrecognized(NULL, NULL, msg);
-
- g_free(msg);
- }
-}
-
-/**
- * Save buddy information to the buddy list from a user info reply message.
- *
- * @param session
- * @param msg The user information reply, with any amount of information.
- * @param user The structure to save to, or NULL to save in PurpleBuddy->proto_data.
- *
- * Variable information is saved to the passed MsimUser structure. Permanent
- * information (UserID) is stored in the blist node of the buddy list (and
- * ends up in blist.xml, persisted to disk) if it exists.
- *
- * If the function has no buddy information, this function
- * is a no-op (and returns FALSE).
- */
-gboolean
-msim_store_user_info(MsimSession *session, const MsimMessage *msg, MsimUser *user)
-{
- gchar *username;
- MsimMessage *body, *body_node;
-
- g_return_val_if_fail(msg != NULL, FALSE);
-
- body = msim_msg_get_dictionary(msg, "body");
- if (!body) {
- return FALSE;
- }
-
- if (msim_msg_get_integer(msg, "dsn") == MG_OWN_IM_INFO_DSN &&
- msim_msg_get_integer(msg, "lid") == MG_OWN_IM_INFO_LID)
- {
- /*
- * Some of this info will be available on the buddy list if the
- * user has themselves as their own buddy.
- *
- * Much of the info is already available in MsimSession,
- * stored in msim_we_are_logged_on().
- */
- gchar *tmpstr;
-
- tmpstr = msim_msg_get_string(body, "ShowOnlyToList");
- if (tmpstr != NULL) {
- session->show_only_to_list = g_str_equal(tmpstr, "True");
- g_free(tmpstr);
- }
-
- session->privacy_mode = msim_msg_get_integer(body, "PrivacyMode");
- session->offline_message_mode = msim_msg_get_integer(body, "OfflineMessageMode");
-
- msim_send(session,
- "blocklist", MSIM_TYPE_BOOLEAN, TRUE,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- "idlist", MSIM_TYPE_STRING,
- g_strdup_printf("w%d|c%d",
- session->show_only_to_list ? 1 : 0,
- session->privacy_mode & 1),
- NULL);
- } else if (msim_msg_get_integer(msg, "dsn") == MG_OWN_MYSPACE_INFO_DSN &&
- msim_msg_get_integer(msg, "lid") == MG_OWN_MYSPACE_INFO_LID) {
- /* TODO: same as above, but for MySpace info. */
- }
-
- username = msim_msg_get_string(body, "UserName");
-
- if (!username) {
- purple_debug_info("msim",
- "msim_process_reply: not caching body, no UserName\n");
- msim_msg_free(body);
- g_free(username);
- return FALSE;
- }
-
- /* Null user = find and store in PurpleBuddy's proto_data */
- if (!user) {
- user = msim_find_user(session, username);
- if (!user) {
- msim_msg_free(body);
- g_free(username);
- return FALSE;
- }
- }
-
- /* TODO: make looping over MsimMessage's easier. */
- for (body_node = body;
- body_node != NULL;
- body_node = msim_msg_get_next_element_node(body_node))
- {
- const gchar *key_str;
- gchar *value_str;
- MsimMessageElement *elem;
-
- elem = (MsimMessageElement *)body_node->data;
- key_str = elem->name;
-
- value_str = msim_msg_get_string_from_element(elem);
- msim_store_user_info_each(key_str, value_str, user);
- }
-
- msim_msg_free(body);
- g_free(username);
-
- return TRUE;
-}
-
-#if 0
-/**
- * Return whether a given username is syntactically valid.
- * Note: does not actually check that the user exists.
- */
-static gboolean
-msim_is_valid_username(const gchar *user)
-{
- return !msim_is_userid(user) && /* Not all numeric */
- strlen(user) <= MSIM_MAX_USERNAME_LENGTH
- && strspn(user, "0123456789"
- "abcdefghijklmnopqrstuvwxyz"
- "_"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ") == strlen(user);
-}
-#endif
-
-/**
- * Check if a string is a userid (all numeric).
- *
- * @param user The user id, email, or name.
- *
- * @return TRUE if is userid, FALSE if not.
- */
-gboolean
-msim_is_userid(const gchar *user)
-{
- g_return_val_if_fail(user != NULL, FALSE);
-
- return strspn(user, "0123456789") == strlen(user);
-}
-
-/**
- * Check if a string is an email address (contains an @).
- *
- * @param user The user id, email, or name.
- *
- * @return TRUE if is an email, FALSE if not.
- *
- * This function is not intended to be used as a generic
- * means of validating email addresses, but to distinguish
- * between a user represented by an email address from
- * other forms of identification.
- */
-static gboolean
-msim_is_email(const gchar *user)
-{
- g_return_val_if_fail(user != NULL, FALSE);
-
- return strchr(user, '@') != NULL;
-}
-
-/**
- * Asynchronously lookup user information, calling callback when receive result.
- *
- * @param session
- * @param user The user id, email address, or username. Not freed.
- * @param cb Callback, called with user information when available.
- * @param data An arbitray data pointer passed to the callback.
- */
-/* TODO: change to not use callbacks */
-void
-msim_lookup_user(MsimSession *session, const gchar *user, MSIM_USER_LOOKUP_CB cb, gpointer data)
-{
- MsimMessage *body;
- gchar *field_name;
- guint rid, dsn, lid;
-
- g_return_if_fail(user != NULL);
- /* Callback can be null to not call anything, just lookup & store information. */
- /*g_return_if_fail(cb != NULL);*/
-
- purple_debug_info("msim", "msim_lookup_userid: "
- "asynchronously looking up <%s>\n", user);
-
- /* Setup callback. Response will be associated with request using 'rid'. */
- rid = msim_new_reply_callback(session, cb, data);
-
- /* Send request */
-
- if (msim_is_userid(user)) {
- field_name = "UserID";
- dsn = MG_MYSPACE_INFO_BY_ID_DSN;
- lid = MG_MYSPACE_INFO_BY_ID_LID;
- } else if (msim_is_email(user)) {
- field_name = "Email";
- dsn = MG_MYSPACE_INFO_BY_STRING_DSN;
- lid = MG_MYSPACE_INFO_BY_STRING_LID;
- } else {
- field_name = "UserName";
- dsn = MG_MYSPACE_INFO_BY_STRING_DSN;
- lid = MG_MYSPACE_INFO_BY_STRING_LID;
- }
-
- body = msim_msg_new(
- field_name, MSIM_TYPE_STRING, g_strdup(user),
- NULL);
-
- g_return_if_fail(msim_send(session,
- "persist", MSIM_TYPE_INTEGER, 1,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_GET,
- "dsn", MSIM_TYPE_INTEGER, dsn,
- "uid", MSIM_TYPE_INTEGER, session->userid,
- "lid", MSIM_TYPE_INTEGER, lid,
- "rid", MSIM_TYPE_INTEGER, rid,
- "body", MSIM_TYPE_DICTIONARY, body,
- NULL));
-}
-
-/**
- * Called after username is set.
- */
-static void msim_username_is_set_cb(MsimSession *session, const MsimMessage *userinfo, gpointer data)
-{
- gchar *username;
- const gchar *errmsg;
- MsimMessage *body;
-
- guint rid;
- gint cmd,dsn,lid,code;
- /* \persistr\\cmd\258\dsn\9\uid\204084363\lid\14\rid\369\body\UserName=TheAlbinoRhino1.Code=0\final\ */
-
- purple_debug_info("msim","username_is_set made\n");
-
- cmd = msim_msg_get_integer(userinfo, "cmd");
- dsn = msim_msg_get_integer(userinfo, "dsn");
-#if 0
- uid = msim_msg_get_integer(userinfo, "uid");
-#endif
- lid = msim_msg_get_integer(userinfo, "lid");
- body = msim_msg_get_dictionary(userinfo, "body");
- errmsg = _("An error occurred while trying to set the username. "
- "Please try again, or visit http://editprofile.myspace.com/index.cfm?"
- "fuseaction=profile.username to set your username.");
-
- if (!body) {
- purple_debug_info("msim_username_is_set_cb", "No body");
- /* Error: No body! */
- purple_connection_error_reason(session->gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, errmsg);
- }
- username = msim_msg_get_string(body, "UserName");
- code = msim_msg_get_integer(body,"Code");
-
- msim_msg_free(body);
-
- purple_debug_info("msim_username_is_set_cb",
- "cmd = %d, dsn = %d, lid = %d, code = %d, username = %s\n",
- cmd, dsn, lid, code, username);
-
- if (cmd == (MSIM_CMD_BIT_REPLY | MSIM_CMD_PUT)
- && dsn == MC_SET_USERNAME_DSN
- && lid == MC_SET_USERNAME_LID)
- {
- purple_debug_info("msim_username_is_set_cb", "Proper cmd,dsn,lid for username_is_set!\n");
- purple_debug_info("msim_username_is_set_cb", "Username Set with return code %d\n",code);
- if (code == 0) {
- /* Good! */
- session->username = username;
- msim_we_are_logged_on(session);
- } else {
- purple_debug_info("msim_username_is_set", "code is %d",code);
- /* TODO: what to do here? */
- }
- } else if (cmd == (MSIM_CMD_BIT_REPLY | MSIM_CMD_GET)
- && dsn == MG_MYSPACE_INFO_BY_STRING_DSN
- && lid == MG_MYSPACE_INFO_BY_STRING_LID) {
- /* Not quite done... ONE MORE STEP :) */
- rid = msim_new_reply_callback(session, msim_username_is_set_cb, data);
- body = msim_msg_new("UserName", MSIM_TYPE_STRING, g_strdup(username), NULL);
- if (!msim_send(session, "persist", MSIM_TYPE_INTEGER, 1,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_PUT,
- "dsn", MSIM_TYPE_INTEGER, MC_SET_USERNAME_DSN,
- "uid", MSIM_TYPE_INTEGER, session->userid,
- "lid", MSIM_TYPE_INTEGER, MC_SET_USERNAME_LID,
- "rid", MSIM_TYPE_INTEGER, rid,
- "body", MSIM_TYPE_DICTIONARY, body,
- NULL)) {
- /* Error! */
- /* Can't set... Disconnect */
- purple_connection_error_reason(session->gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, errmsg);
- }
-
- } else {
- /* Error! */
- purple_debug_info("msim","username_is_set Error: Invalid cmd/dsn/lid combination");
- purple_connection_error_reason(session->gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, errmsg);
- }
-}
-
-/**
- * Asynchronously set new username, calling callback when receive result.
- *
- * @param session
- * @param username The username we're setting for ourselves. Not freed.
- * @param cb Callback, called with user information when available.
- * @param data An arbitray data pointer passed to the callback.
- */
-static void
-msim_set_username(MsimSession *session, const gchar *username,
- MSIM_USER_LOOKUP_CB cb, gpointer data)
-{
- MsimMessage *body;
- guint rid;
-
- g_return_if_fail(username != NULL);
- g_return_if_fail(cb != NULL);
-
- purple_debug_info("msim", "msim_set_username: "
- "Setting username %s\n", username);
-
- /* Setup callback. Response will be associated with request using 'rid'. */
- rid = msim_new_reply_callback(session, cb, data);
-
- /* TODO: I dont know if the ContactType is -/ALWAYS/- 1 */
-
- body = msim_msg_new("UserName", MSIM_TYPE_STRING, g_strdup(username),NULL);
-/* \setinfo\\sesskey\469958979\info\Age=21.AvatarUrl=.BandName=.ContactType=1.DisplayName=Msim.Gender=M.ImageURL=http:/1/1x.myspace.com/1images/1no_pic.gif.LastLogin=128335268400000000.Location=US.ShowAvatar=False.SongName=.TotalFriends=1.UserName=msimprpl2\final\
-*/
-
- /* Send request */
- g_return_if_fail(msim_send(session,
- "setinfo", MSIM_TYPE_BOOLEAN, TRUE,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- "info", MSIM_TYPE_DICTIONARY, body,
- NULL));
- body = msim_msg_new("UserName", MSIM_TYPE_STRING, g_strdup(username),NULL);
- g_return_if_fail(msim_send(session,
- "persist", MSIM_TYPE_INTEGER, 1,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_GET,
- "dsn", MSIM_TYPE_INTEGER, MG_MYSPACE_INFO_BY_STRING_DSN,
- "uid", MSIM_TYPE_INTEGER, session->userid,
- "lid", MSIM_TYPE_INTEGER, MG_MYSPACE_INFO_BY_STRING_LID,
- "rid", MSIM_TYPE_INTEGER, rid,
- "body", MSIM_TYPE_DICTIONARY, body,
- NULL));
-}
-
-/**
- * They've confirmed that username that was available, Lets make the call to set it
- */
-static void msim_set_username_confirmed_cb(PurpleConnection *gc)
-{
- MsimMessage *user_msg;
- MsimSession *session;
-
- g_return_if_fail(gc != NULL);
-
- session = (MsimSession *)gc->proto_data;
-
- user_msg = msim_msg_new(
- "user", MSIM_TYPE_STRING, g_strdup(msim_username_to_set),
- NULL);
-
- purple_debug_info("msim_set_username_confirmed_cb", "Setting username to %s\n", msim_username_to_set);
-
- /* Sets our username... keep your fingers crossed :) */
- msim_set_username(session, msim_username_to_set, msim_username_is_set_cb, user_msg);
- g_free(msim_username_to_set);
-}
-
-/**
- * This is where we do a bit more than merely prompt the user.
- * Now we have some real data to tell us the state of their requested username
- * \persistr\\cmd\257\dsn\5\uid\204084363\lid\7\rid\367\body\UserName=TheAlbinoRhino1\final\
- */
-static void msim_username_is_available_cb(MsimSession *session, const MsimMessage *userinfo, gpointer data)
-{
- MsimMessage *msg;
- gchar *username;
- MsimMessage *body;
- gint userid;
-
- purple_debug_info("msim_username_is_available_cb", "Look up username callback made\n");
-
- msg = (MsimMessage *)data;
- g_return_if_fail(msg != NULL);
-
- username = msim_msg_get_string(msg, "user");
- body = msim_msg_get_dictionary(userinfo, "body");
-
- if (!body) {
- purple_debug_info("msim_username_is_available_cb", "No body for %s?!\n", username);
- purple_connection_error_reason(session->gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
- _("An error occurred while trying to set the username. "
- "Please try again, or visit http://editprofile.myspace.com/index.cfm?"
- "fuseaction=profile.username to set your username."));
- return;
- }
-
- userid = msim_msg_get_integer(body, "UserID");
-
- purple_debug_info("msim_username_is_available_cb", "Returned username is %s and userid is %d\n", username, userid);
- msim_msg_free(body);
- msim_msg_free(msg);
-
- /* The response for a free username will ONLY have the UserName in it..
- * thus making UserID return 0 when we msg_get_integer it */
- if (userid == 0) {
- /* This username is currently unused */
- purple_debug_info("msim_username_is_available_cb", "Username available. Prompting to Confirm.\n");
- msim_username_to_set = g_strdup(username);
- g_free(username);
- purple_request_yes_no(session->gc,
- _("MySpaceIM - Username Available"),
- _("This username is available. Would you like to set it?"),
- _("ONCE SET, THIS CANNOT BE CHANGED!"),
- 0,
- session->account,
- NULL,
- NULL,
- session->gc,
- G_CALLBACK(msim_set_username_confirmed_cb),
- G_CALLBACK(msim_do_not_set_username_cb));
- } else {
- /* Looks like its in use or we have an invalid response */
- purple_debug_info("msim_username_is_available_cb", "Username unavaiable. Prompting for new entry.\n");
- purple_request_input(session->gc, _("MySpaceIM - Please Set a Username"),
- _("This username is unavailable."),
- _("Please try another username:"),
- "", FALSE, FALSE, NULL,
- _("OK"), G_CALLBACK(msim_check_username_availability_cb),
- _("Cancel"), G_CALLBACK(msim_do_not_set_username_cb),
- session->account,
- NULL,
- NULL,
- session->gc);
- }
-}
-
-/**
- * Once they've submitted their desired new username,
- * check if it is available here.
- */
-static void msim_check_username_availability_cb(PurpleConnection *gc, const char *username_to_check)
-{
- MsimMessage *user_msg;
- MsimSession *session;
-
- g_return_if_fail(gc != NULL);
-
- session = (MsimSession *)gc->proto_data;
-
- purple_debug_info("msim_check_username_availability_cb", "Checking username: %s\n", username_to_check);
-
- user_msg = msim_msg_new(
- "user", MSIM_TYPE_STRING, g_strdup(username_to_check),
- NULL);
-
- /* 25 characters: letters, numbers, underscores */
- /* TODO: VERIFY ABOVE */
-
- /* \persist\1\sesskey\288500516\cmd\1\dsn\5\uid\204084363\lid\7\rid\367\body\UserName=Jaywalker\final\ */
- /* Official client uses a standard lookup... So do we! */
- msim_lookup_user(session, username_to_check, msim_username_is_available_cb, user_msg);
-}
-
-/***
- * If they hit cancel or no at any point in the Setting Username process,
- * we come here. Currently we're safe letting them get by without
- * setting it, unless we hear otherwise. So for now give them a menu.
- * If this becomes an issue with the official client then boot them here.
- */
-void msim_do_not_set_username_cb(PurpleConnection *gc)
-{
- purple_debug_info("msim", "Don't set username");
-
- /* Protocol won't log in now without a username set.. Disconnect */
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("No username set"));
-}
-
-/**
- * They've decided to set a username! Yay!
- */
-void msim_set_username_cb(PurpleConnection *gc)
-{
- g_return_if_fail(gc != NULL);
- purple_debug_info("msim","Set username\n");
- purple_request_input(gc, _("MySpaceIM - Please Set a Username"),
- _("Please enter a username to check its availability:"),
- NULL,
- "", FALSE, FALSE, NULL,
- _("OK"), G_CALLBACK(msim_check_username_availability_cb),
- _("Cancel"), G_CALLBACK(msim_do_not_set_username_cb),
- purple_connection_get_account(gc),
- NULL,
- NULL,
- gc);
-}
diff --git a/libpurple/protocols/myspace/user.h b/libpurple/protocols/myspace/user.h
deleted file mode 100644
index acc0c2604d..0000000000
--- a/libpurple/protocols/myspace/user.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/* MySpaceIM Protocol Plugin, header file
- *
- * Copyright (C) 2007, Jeff Connelly <jeff2@soc.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
- */
-
-#ifndef _MYSPACE_USER_H
-#define _MYSPACE_USER_H
-
-/* Hold ephemeral information about buddies, for proto_data of PurpleBuddy. */
-/* GHashTable? */
-typedef struct _MsimUser
-{
- PurpleBuddy *buddy;
- /* Note: id is also &buddy->node (set_blist_node_int), when buddy is non-NULL */
- int id;
- guint client_cv;
- gchar *client_info;
- guint age;
- gchar *gender;
- gchar *location;
- guint total_friends;
- gchar *headline;
- gchar *display_name;
- gchar *username;
- gchar *band_name, *song_name;
- gchar *image_url;
- guint last_image_updated;
- gboolean temporary_user;
- PurpleUtilFetchUrlData *url_data;
-} MsimUser;
-
-/* Callback function pointer type for when a user's information is received,
- * initiated from a user lookup. */
-typedef void (*MSIM_USER_LOOKUP_CB)(MsimSession *session, const MsimMessage *userinfo, gpointer data);
-
-MsimUser *msim_get_user_from_buddy(PurpleBuddy *buddy, gboolean create);
-void msim_user_free(MsimUser *user);
-MsimUser *msim_find_user(MsimSession *session, const gchar *username);
-void msim_append_user_info(MsimSession *session, PurpleNotifyUserInfo *user_info, MsimUser *user, gboolean full);
-gboolean msim_store_user_info(MsimSession *session, const MsimMessage *msg, MsimUser *user);
-gboolean msim_is_userid(const gchar *user);
-void msim_lookup_user(MsimSession *session, const gchar *user, MSIM_USER_LOOKUP_CB cb, gpointer data);
-void msim_set_username_cb(PurpleConnection *gc);
-void msim_do_not_set_username_cb(PurpleConnection *gc);
-
-#endif /* !_MYSPACE_USER_H */
diff --git a/libpurple/protocols/myspace/zap.c b/libpurple/protocols/myspace/zap.c
deleted file mode 100644
index 509b48949d..0000000000
--- a/libpurple/protocols/myspace/zap.c
+++ /dev/null
@@ -1,245 +0,0 @@
-/* MySpaceIM Protocol Plugin - zap support
- *
- * Copyright (C) 2007, Jeff Connelly <jeff2@soc.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
- */
-
-#include "myspace.h"
-#include "zap.h"
-
-/** Get zap types. */
-GList *
-msim_attention_types(PurpleAccount *acct)
-{
- static GList *types = NULL;
- PurpleAttentionType* attn;
-
- if (!types) {
-#define _MSIM_ADD_NEW_ATTENTION(icn, ulname, nme, incoming, outgoing) \
- attn = purple_attention_type_new(ulname, nme, incoming, outgoing); \
- purple_attention_type_set_icon_name(attn, icn); \
- types = g_list_append(types, attn);
-
- /* TODO: icons for each zap */
-
- /* Lots of comments for translators: */
-
- /* Zap means "to strike suddenly and forcefully as if with a
- * projectile or weapon." This term often has an electrical
- * connotation, for example, "he was zapped by electricity when
- * he put a fork in the toaster." */
- _MSIM_ADD_NEW_ATTENTION(NULL, "Zap", _("Zap"), _("%s has zapped you!"),
- _("Zapping %s..."));
-
- /* Whack means "to hit or strike someone with a sharp blow" */
- _MSIM_ADD_NEW_ATTENTION(NULL, "Whack", _("Whack"),
- _("%s has whacked you!"), _("Whacking %s..."));
-
- /* Torch means "to set on fire." Don't worry, this doesn't
- * make a whole lot of sense in English, either. Feel free
- * to translate it literally. */
- _MSIM_ADD_NEW_ATTENTION(NULL, "Torch", _("Torch"),
- _("%s has torched you!"), _("Torching %s..."));
-
- /* Smooch means "to kiss someone, often enthusiastically" */
- _MSIM_ADD_NEW_ATTENTION(NULL, "Smooch", _("Smooch"),
- _("%s has smooched you!"), _("Smooching %s..."));
-
- /* A hug is a display of affection; wrapping your arms around someone */
- _MSIM_ADD_NEW_ATTENTION(NULL, "Hug", _("Hug"), _("%s has hugged you!"),
- _("Hugging %s..."));
-
- /* Slap means "to hit someone with an open/flat hand" */
- _MSIM_ADD_NEW_ATTENTION(NULL, "Slap", _("Slap"),
- _("%s has slapped you!"), _("Slapping %s..."));
-
- /* Goose means "to pinch someone on their butt" */
- _MSIM_ADD_NEW_ATTENTION(NULL, "Goose", _("Goose"),
- _("%s has goosed you!"), _("Goosing %s..."));
-
- /* A high-five is when two people's hands slap each other
- * in the air above their heads. It is done to celebrate
- * something, often a victory, or to congratulate someone. */
- _MSIM_ADD_NEW_ATTENTION(NULL, "High-five", _("High-five"),
- _("%s has high-fived you!"), _("High-fiving %s..."));
-
- /* We're not entirely sure what the MySpace people mean by
- * this... but we think it's the equivalent of "prank." Or, for
- * someone to perform a mischievous trick or practical joke. */
- _MSIM_ADD_NEW_ATTENTION(NULL, "Punk", _("Punk"),
- _("%s has punk'd you!"), _("Punking %s..."));
-
- /* Raspberry is a slang term for the vibrating sound made
- * when you stick your tongue out of your mouth with your
- * lips closed and blow. It is typically done when
- * gloating or bragging. Nowadays it's a pretty silly
- * gesture, so it does not carry a harsh negative
- * connotation. It is generally used in a playful tone
- * with friends. */
- _MSIM_ADD_NEW_ATTENTION(NULL, "Raspberry", _("Raspberry"),
- _("%s has raspberried you!"), _("Raspberrying %s..."));
- }
-
- return types;
-}
-
-/** Send a zap to a user. */
-static gboolean
-msim_send_zap(MsimSession *session, const gchar *username, guint code)
-{
- gchar *zap_string;
- gboolean rc;
-
- g_return_val_if_fail(session != NULL, FALSE);
- g_return_val_if_fail(username != NULL, FALSE);
-
- /* Construct and send the actual zap command. */
- zap_string = g_strdup_printf("!!!ZAP_SEND!!!=RTE_BTN_ZAPS_%d", code);
-
- if (!msim_send_bm(session, username, zap_string, MSIM_BM_ACTION_OR_IM_INSTANT)) {
- purple_debug_info("msim_send_zap",
- "msim_send_bm failed: zapping %s with %s\n",
- username, zap_string);
- rc = FALSE;
- } else {
- rc = TRUE;
- }
-
- g_free(zap_string);
-
- return rc;
-}
-
-/** Send a zap */
-gboolean
-msim_send_attention(PurpleConnection *gc, const gchar *username, guint code)
-{
- GList *types;
- MsimSession *session;
- PurpleAttentionType *attn;
- PurpleBuddy *buddy;
-
- session = (MsimSession *)gc->proto_data;
-
- /* Look for this attention type, by the code index given. */
- types = msim_attention_types(gc->account);
- attn = (PurpleAttentionType *)g_list_nth_data(types, code);
-
- if (!attn) {
- purple_debug_info("msim_send_attention", "got invalid zap code %d\n", code);
- return FALSE;
- }
-
- buddy = purple_find_buddy(session->account, username);
- if (!buddy) {
- return FALSE;
- }
-
- msim_send_zap(session, username, code);
-
- return TRUE;
-}
-
-/** Zap someone. Callback from msim_blist_node_menu zap menu. */
-static void
-msim_send_zap_from_menu(PurpleBlistNode *node, gpointer zap_num_ptr)
-{
- PurpleBuddy *buddy;
- PurpleAccount *account;
- PurpleConnection *gc;
- MsimSession *session;
- guint zap;
-
- if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) {
- /* Only know about buddies for now. */
- return;
- }
-
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
-
- buddy = (PurpleBuddy *)node;
-
- /* Find the session */
- account = purple_buddy_get_account(buddy);
- gc = purple_account_get_connection(account);
- session = (MsimSession *)gc->proto_data;
-
- zap = GPOINTER_TO_INT(zap_num_ptr);
-
- purple_prpl_send_attention(session->gc, purple_buddy_get_name(buddy), zap);
-}
-
-/** Return menu, if any, for a buddy list node. */
-GList *
-msim_blist_node_menu(PurpleBlistNode *node)
-{
- GList *menu, *zap_menu;
- GList *types;
- PurpleMenuAction *act;
- guint i;
-
- if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) {
- /* Only know about buddies for now. */
- return NULL;
- }
-
- zap_menu = NULL;
-
- /* TODO: get rid of once is accessible directly in GUI */
- types = msim_attention_types(NULL);
- i = 0;
- for (; types; types = g_list_next(types)) {
- PurpleAttentionType *attn;
-
- attn = (PurpleAttentionType *)types->data;
-
- act = purple_menu_action_new(purple_attention_type_get_name(attn),
- PURPLE_CALLBACK(msim_send_zap_from_menu), GUINT_TO_POINTER(i), NULL);
- zap_menu = g_list_append(zap_menu, act);
-
- ++i;
- }
-
- act = purple_menu_action_new(_("Zap"), NULL, NULL, zap_menu);
- menu = g_list_append(NULL, act);
-
- return menu;
-}
-
-/** Process an incoming zap. */
-gboolean
-msim_incoming_zap(MsimSession *session, MsimMessage *msg)
-{
- gchar *msg_text, *username;
- gint zap;
-
- msg_text = msim_msg_get_string(msg, "msg");
- username = msim_msg_get_string(msg, "_username");
-
- g_return_val_if_fail(msg_text != NULL, FALSE);
- g_return_val_if_fail(username != NULL, FALSE);
-
- g_return_val_if_fail(sscanf(msg_text, "!!!ZAP_SEND!!!=RTE_BTN_ZAPS_%d", &zap) == 1, FALSE);
-
- zap = CLAMP(zap, 0, 9);
-
- purple_prpl_got_attention(session->gc, username, zap);
-
- g_free(msg_text);
- g_free(username);
-
- return TRUE;
-}
diff --git a/libpurple/protocols/myspace/zap.h b/libpurple/protocols/myspace/zap.h
deleted file mode 100644
index d6bf52c364..0000000000
--- a/libpurple/protocols/myspace/zap.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* MySpaceIM Protocol Plugin - zap support
- *
- * Copyright (C) 2007, Jeff Connelly <jeff2@soc.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
- */
-
-#ifndef _MYSPACE_ZAP_H
-#define _MYSPACE_ZAP_H
-
-GList *msim_attention_types(PurpleAccount *acct);
-gboolean msim_send_attention(PurpleConnection *gc, const gchar *username, guint code);
-GList *msim_blist_node_menu(PurpleBlistNode *node);
-gboolean msim_incoming_zap(MsimSession *session, MsimMessage *msg);
-
-#endif /* !_MYSPACE_ZAP_H */
diff --git a/libpurple/protocols/novell/Makefile.am b/libpurple/protocols/novell/Makefile.am
index c4c7f12a26..6f32defbb3 100644
--- a/libpurple/protocols/novell/Makefile.am
+++ b/libpurple/protocols/novell/Makefile.am
@@ -1,7 +1,7 @@
EXTRA_DIST = \
Makefile.mingw
-pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+pkgdir = @PURPLE_PLUGINDIR@
NOVELLSOURCES = \
nmfield.h \
@@ -28,7 +28,7 @@ NOVELLSOURCES = \
AM_CFLAGS = $(st)
-libnovell_la_LDFLAGS = -module -avoid-version
+libnovell_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
if STATIC_NOVELL
@@ -42,7 +42,7 @@ else
st =
pkg_LTLIBRARIES = libnovell.la
libnovell_la_SOURCES = $(NOVELLSOURCES)
-libnovell_la_LIBADD = $(GLIB_LIBS)
+libnovell_la_LIBADD = @PURPLE_LIBS@
endif
diff --git a/libpurple/protocols/novell/Makefile.mingw b/libpurple/protocols/novell/Makefile.mingw
index f653b65018..f22da0f622 100644
--- a/libpurple/protocols/novell/Makefile.mingw
+++ b/libpurple/protocols/novell/Makefile.mingw
@@ -57,6 +57,7 @@ OBJECTS = $(C_SRC:%.c=%.o)
##
LIBS = \
-lglib-2.0 \
+ -lgobject-2.0 \
-lws2_32 \
-lintl \
-lpurple
diff --git a/libpurple/protocols/novell/novell.c b/libpurple/protocols/novell/novell.c
index 817974f7db..489b0ef18a 100644
--- a/libpurple/protocols/novell/novell.c
+++ b/libpurple/protocols/novell/novell.c
@@ -29,7 +29,6 @@
#include "sslconn.h"
#include "request.h"
#include "network.h"
-#include "privacy.h"
#include "status.h"
#include "version.h"
@@ -102,16 +101,16 @@ _login_resp_cb(NMUser * user, NMERR_T ret_code,
if (ret_code == NM_OK) {
/* Set alias for user if not set (use Full Name) */
- alias = purple_account_get_alias(user->client_data);
+ alias = purple_account_get_private_alias(user->client_data);
if (alias == NULL || *alias == '\0') {
alias = nm_user_record_get_full_name(user->user_record);
if (alias)
- purple_account_set_alias(user->client_data, alias);
+ purple_account_set_private_alias(user->client_data, alias);
}
/* Tell Purple that we are connected */
- purple_connection_set_state(gc, PURPLE_CONNECTED);
+ purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
_sync_contact_list(user);
@@ -131,8 +130,8 @@ _login_resp_cb(NMUser * user, NMERR_T ret_code,
/* Don't attempt to auto-reconnect if our
* password was invalid.
*/
- if (!purple_account_get_remember_password(gc->account))
- purple_account_set_password(gc->account, NULL);
+ if (!purple_account_get_remember_password(purple_connection_get_account(gc)))
+ purple_account_set_password(purple_connection_get_account(gc), NULL, NULL, NULL);
reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
break;
default:
@@ -140,7 +139,7 @@ _login_resp_cb(NMUser * user, NMERR_T ret_code,
reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
}
- purple_connection_error_reason(gc, reason, err);
+ purple_connection_error(gc, reason, err);
g_free(err);
}
}
@@ -165,7 +164,7 @@ _get_status_resp_cb(NMUser * user, NMERR_T ret_code,
const char *name = nm_user_record_get_display_id(user_record);
if (name) {
- buddies = purple_find_buddies((PurpleAccount *) user->client_data, name);
+ buddies = purple_blist_find_buddies((PurpleAccount *) user->client_data, name);
for (bnode = buddies; bnode; bnode = bnode->next) {
buddy = (PurpleBuddy *) bnode->data;
if (buddy) {
@@ -217,9 +216,8 @@ _get_details_resp_send_msg(NMUser * user, NMERR_T ret_code,
if (user_record) {
/* Set the title for the conversation */
- /* XXX - Should this be PURPLE_CONV_TYPE_IM? */
- gconv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
- nm_user_record_get_display_id(user_record),
+ /* XXX - Should this be find_im_with_account? */
+ gconv = purple_conversations_find_with_account(nm_user_record_get_display_id(user_record),
(PurpleAccount *) user->client_data);
if (gconv) {
@@ -256,7 +254,8 @@ _get_details_resp_send_msg(NMUser * user, NMERR_T ret_code,
" Could not get details for user (%s)."),
nm_error_to_string (ret_code));
- purple_notify_error(gc, NULL, err, NULL);
+ purple_notify_error(gc, NULL, err, NULL,
+ purple_request_cpar_from_connection(gc));
g_free(err);
}
@@ -289,12 +288,12 @@ _get_details_resp_setup_buddy(NMUser * user, NMERR_T ret_code,
nm_contact_set_user_record(contact, user_record);
/* Set the display id */
- purple_blist_rename_buddy(buddy,
+ purple_buddy_set_name(buddy,
nm_user_record_get_display_id(user_record));
alias = purple_buddy_get_alias(buddy);
if (alias == NULL || *alias == '\0' || (strcmp(alias, purple_buddy_get_name(buddy)) == 0)) {
- purple_blist_alias_buddy(buddy,
+ purple_buddy_set_local_alias(buddy,
nm_user_record_get_full_name(user_record));
/* Tell the server about the new display name */
@@ -351,7 +350,7 @@ _create_contact_resp_cb(NMUser * user, NMERR_T ret_code,
folder_name = NM_ROOT_FOLDER_NAME;
/* Re-add the buddy now that we got the okay from the server */
- if (folder_name && (group = purple_find_group(folder_name))) {
+ if (folder_name && (group = purple_blist_find_group(folder_name))) {
const char *alias = nm_contact_get_display_name(tmp_contact);
const char *display_id = nm_contact_get_display_id(new_contact);
@@ -373,7 +372,7 @@ _create_contact_resp_cb(NMUser * user, NMERR_T ret_code,
}
/* Add it to the purple buddy list if it is not there */
- buddy = purple_find_buddy_in_group(user->client_data, display_id, group);
+ buddy = purple_blist_find_buddy_in_group(user->client_data, display_id, group);
if (buddy == NULL) {
buddy = purple_buddy_new(user->client_data, display_id, alias);
purple_blist_add_buddy(buddy, NULL, group, NULL);
@@ -403,7 +402,8 @@ _create_contact_resp_cb(NMUser * user, NMERR_T ret_code,
err =
g_strdup_printf(_("Unable to add %s to your buddy list (%s)."),
name, nm_error_to_string (ret_code));
- purple_notify_error(gc, NULL, err, NULL);
+ purple_notify_error(gc, NULL, err, NULL,
+ purple_request_cpar_from_connection(gc));
g_free(err);
}
@@ -429,7 +429,8 @@ _send_message_resp_cb(NMUser * user, NMERR_T ret_code,
/* TODO: Improve this! message to who or for what conference? */
err = g_strdup_printf(_("Unable to send message (%s)."),
nm_error_to_string (ret_code));
- purple_notify_error(gc, NULL, err, NULL);
+ purple_notify_error(gc, NULL, err, NULL,
+ purple_request_cpar_from_connection(gc));
g_free(err);
}
}
@@ -499,7 +500,8 @@ _sendinvite_resp_cb(NMUser *user, NMERR_T ret_code,
if (ret_code != NM_OK) {
gc = purple_account_get_connection(user->client_data);
err = g_strdup_printf(_("Unable to invite user (%s)."), nm_error_to_string(ret_code));
- purple_notify_error(gc, NULL, err, NULL);
+ purple_notify_error(gc, NULL, err, NULL,
+ purple_request_cpar_from_connection(gc));
g_free(err);
purple_debug(PURPLE_DEBUG_INFO, "novell",
@@ -546,7 +548,8 @@ _createconf_resp_send_msg(NMUser * user, NMERR_T ret_code,
" Could not create the conference (%s)."),
nm_error_to_string (ret_code));
- purple_notify_error(gc, NULL, err, NULL);
+ purple_notify_error(gc, NULL, err, NULL,
+ purple_request_cpar_from_connection(gc));
g_free(err);
}
@@ -596,7 +599,8 @@ _create_folder_resp_move_contact(NMUser * user, NMERR_T ret_code,
folder_name,
nm_error_to_string (ret_code));
- purple_notify_error(gc, NULL, err, NULL);
+ purple_notify_error(gc, NULL, err, NULL,
+ purple_request_cpar_from_connection(gc));
g_free(err);
}
@@ -641,7 +645,8 @@ _create_folder_resp_add_contact(NMUser * user, NMERR_T ret_code,
" Error creating folder in server side list (%s)."),
name, nm_error_to_string (ret_code));
- purple_notify_error(gc, NULL, err, NULL);
+ purple_notify_error(gc, NULL, err, NULL,
+ purple_request_cpar_from_connection(gc));
nm_release_contact(contact);
g_free(err);
@@ -654,7 +659,7 @@ static void
_join_conf_resp_cb(NMUser * user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
- PurpleConversation *chat;
+ PurpleChatConversation *chat;
PurpleConnection *gc;
NMUserRecord *ur;
NMConference *conference = user_data;
@@ -668,7 +673,7 @@ _join_conf_resp_cb(NMUser * user, NMERR_T ret_code,
if (ret_code == NM_OK) {
conf_name = _get_conference_name(++user->conference_count);
- chat = serv_got_joined_chat(gc, user->conference_count, conf_name);
+ chat = purple_serv_got_joined_chat(gc, user->conference_count, conf_name);
if (chat) {
nm_conference_set_data(conference, (gpointer) chat);
@@ -678,8 +683,8 @@ _join_conf_resp_cb(NMUser * user, NMERR_T ret_code,
ur = nm_conference_get_participant(conference, i);
if (ur) {
name = nm_user_record_get_display_id(ur);
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(chat), name, NULL,
- PURPLE_CBFLAGS_NONE, TRUE);
+ purple_chat_conversation_add_user(chat, name, NULL,
+ PURPLE_CHAT_USER_NONE, TRUE);
}
}
}
@@ -712,7 +717,8 @@ _get_details_resp_show_info(NMUser * user, NMERR_T ret_code,
err =
g_strdup_printf(_("Could not get details for user %s (%s)."),
name, nm_error_to_string (ret_code));
- purple_notify_error(gc, NULL, err, NULL);
+ purple_notify_error(gc, NULL, err, NULL,
+ purple_request_cpar_from_connection(gc));
g_free(err);
}
@@ -726,6 +732,7 @@ _get_details_resp_add_privacy_item(NMUser *user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
PurpleConnection *gc;
+ PurpleAccount *account;
NMUserRecord *user_record = resp_data;
char *err;
gboolean allowed = GPOINTER_TO_INT(user_data);
@@ -736,21 +743,22 @@ _get_details_resp_add_privacy_item(NMUser *user, NMERR_T ret_code,
gc = purple_account_get_connection(user->client_data);
display_id = nm_user_record_get_display_id(user_record);
+ account = purple_connection_get_account(gc);
if (ret_code == NM_OK) {
if (allowed) {
- if (!g_slist_find_custom(gc->account->permit,
+ if (!g_slist_find_custom(purple_account_privacy_get_denied(account),
display_id, (GCompareFunc)purple_utf8_strcasecmp)) {
- purple_privacy_permit_add(gc->account, display_id, TRUE);
+ purple_account_privacy_permit_add(account, display_id, TRUE);
}
} else {
- if (!g_slist_find_custom(gc->account->permit,
+ if (!g_slist_find_custom(purple_account_privacy_get_denied(account),
display_id, (GCompareFunc)purple_utf8_strcasecmp)) {
- purple_privacy_deny_add(gc->account, display_id, TRUE);
+ purple_account_privacy_deny_add(account, display_id, TRUE);
}
}
@@ -758,7 +766,8 @@ _get_details_resp_add_privacy_item(NMUser *user, NMERR_T ret_code,
err = g_strdup_printf(_("Unable to add user to privacy list (%s)."),
nm_error_to_string(ret_code));
- purple_notify_error(gc, NULL, err, NULL);
+ purple_notify_error(gc, NULL, err, NULL,
+ purple_request_cpar_from_connection(gc));
g_free(err);
}
@@ -770,6 +779,7 @@ _create_privacy_item_deny_resp_cb(NMUser *user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
PurpleConnection *gc;
+ PurpleAccount *account;
NMUserRecord *user_record;
char *who = user_data;
char *err;
@@ -780,6 +790,7 @@ _create_privacy_item_deny_resp_cb(NMUser *user, NMERR_T ret_code,
return;
gc = purple_account_get_connection(user->client_data);
+ account = purple_connection_get_account(gc);
if (ret_code == NM_OK) {
@@ -789,10 +800,10 @@ _create_privacy_item_deny_resp_cb(NMUser *user, NMERR_T ret_code,
if (display_id) {
- if (!g_slist_find_custom(gc->account->deny,
+ if (!g_slist_find_custom(purple_account_privacy_get_denied(account),
display_id, (GCompareFunc)purple_utf8_strcasecmp)) {
- purple_privacy_deny_add(gc->account, display_id, TRUE);
+ purple_account_privacy_deny_add(account, display_id, TRUE);
}
} else {
@@ -805,7 +816,8 @@ _create_privacy_item_deny_resp_cb(NMUser *user, NMERR_T ret_code,
err = g_strdup_printf(_("Unable to add %s to deny list (%s)."),
who, nm_error_to_string(ret_code));
- purple_notify_error(gc, NULL, err, NULL);
+ purple_notify_error(gc, NULL, err, NULL,
+ purple_request_cpar_from_connection(gc));
g_free(err);
}
@@ -821,6 +833,7 @@ _create_privacy_item_permit_resp_cb(NMUser *user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
PurpleConnection *gc;
+ PurpleAccount *account;
NMUserRecord *user_record;
char *who = user_data;
char *err;
@@ -831,6 +844,7 @@ _create_privacy_item_permit_resp_cb(NMUser *user, NMERR_T ret_code,
return;
gc = purple_account_get_connection(user->client_data);
+ account = purple_connection_get_account(gc);
if (ret_code == NM_OK) {
@@ -840,11 +854,11 @@ _create_privacy_item_permit_resp_cb(NMUser *user, NMERR_T ret_code,
if (display_id) {
- if (!g_slist_find_custom(gc->account->permit,
+ if (!g_slist_find_custom(purple_account_privacy_get_permitted(account),
display_id,
(GCompareFunc)purple_utf8_strcasecmp)) {
- purple_privacy_permit_add(gc->account, display_id, TRUE);
+ purple_account_privacy_permit_add(account, display_id, TRUE);
}
} else {
@@ -858,7 +872,8 @@ _create_privacy_item_permit_resp_cb(NMUser *user, NMERR_T ret_code,
err = g_strdup_printf(_("Unable to add %s to permit list (%s)."), who,
nm_error_to_string(ret_code));
- purple_notify_error(gc, NULL, err, NULL);
+ purple_notify_error(gc, NULL, err, NULL,
+ purple_request_cpar_from_connection(gc));
g_free(err);
}
@@ -904,7 +919,8 @@ _get_details_send_privacy_create(NMUser *user, NMERR_T ret_code,
err = g_strdup_printf(_("Unable to add user to privacy list (%s)."),
nm_error_to_string(ret_code));
- purple_notify_error(gc, NULL, err, NULL);
+ purple_notify_error(gc, NULL, err, NULL,
+ purple_request_cpar_from_connection(gc));
g_free(err);
}
@@ -926,7 +942,8 @@ _remove_privacy_item_resp_cb(NMUser *user, NMERR_T ret_code,
gc = purple_account_get_connection(user->client_data);
err = g_strdup_printf(_("Unable to remove %s from privacy list (%s)."), who,
nm_error_to_string(ret_code));
- purple_notify_error(gc, NULL, err, NULL);
+ purple_notify_error(gc, NULL, err, NULL,
+ purple_request_cpar_from_connection(gc));
g_free(err);
}
@@ -949,7 +966,8 @@ _set_privacy_default_resp_cb(NMUser *user, NMERR_T ret_code,
gc = purple_account_get_connection(user->client_data);
err = g_strdup_printf(_("Unable to change server side privacy settings (%s)."),
nm_error_to_string(ret_code));
- purple_notify_error(gc, NULL, err, NULL);
+ purple_notify_error(gc, NULL, err, NULL,
+ purple_request_cpar_from_connection(gc));
g_free(err);
}
@@ -979,7 +997,7 @@ _get_details_resp_send_invite(NMUser *user, NMERR_T ret_code,
for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) {
conference = cnode->data;
if (conference && (chat = nm_conference_get_data(conference))) {
- if (purple_conv_chat_get_id(PURPLE_CONV_CHAT(chat)) == id) {
+ if (purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(chat)) == id) {
rc = nm_send_conference_invite(user, conference, user_record,
NULL, _sendinvite_resp_cb, NULL);
_check_for_disconnect(user, rc);
@@ -991,7 +1009,8 @@ _get_details_resp_send_invite(NMUser *user, NMERR_T ret_code,
} else {
err = g_strdup_printf(_("Unable to invite user (%s)."), nm_error_to_string(ret_code));
- purple_notify_error(gc, NULL, err, NULL);
+ purple_notify_error(gc, NULL, err, NULL,
+ purple_request_cpar_from_connection(gc));
g_free(err);
}
@@ -1019,7 +1038,8 @@ _createconf_resp_send_invite(NMUser * user, NMERR_T ret_code,
} else {
err = g_strdup_printf(_("Unable to create conference (%s)."), nm_error_to_string(ret_code));
gc = purple_account_get_connection(user->client_data);
- purple_notify_error(gc, NULL, err, NULL);
+ purple_notify_error(gc, NULL, err, NULL,
+ purple_request_cpar_from_connection(gc));
g_free(err);
}
}
@@ -1126,7 +1146,7 @@ _check_for_disconnect(NMUser * user, NMERR_T err)
if (_is_disconnect_error(err)) {
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Error communicating with server. Closing connection."));
return TRUE;
@@ -1236,19 +1256,19 @@ _remove_purple_buddies(NMUser *user)
for (gnode = purple_blist_get_root(); gnode;
gnode = purple_blist_node_get_sibling_next(gnode)) {
- if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
+ if (!PURPLE_IS_GROUP(gnode))
continue;
group = (PurpleGroup *) gnode;
gname = purple_group_get_name(group);
for (cnode = purple_blist_node_get_first_child(gnode);
cnode;
cnode = purple_blist_node_get_sibling_next(cnode)) {
- if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
+ if (!PURPLE_IS_CONTACT(cnode))
continue;
for (bnode = purple_blist_node_get_first_child(cnode);
bnode;
bnode = purple_blist_node_get_sibling_next(bnode)) {
- if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
+ if (!PURPLE_IS_BUDDY(bnode))
continue;
buddy = (PurpleBuddy *) bnode;
if (purple_buddy_get_account(buddy) == user->client_data) {
@@ -1294,7 +1314,7 @@ _add_contacts_to_purple_blist(NMUser * user, NMFolder * folder)
}
/* Does the Purple group exist already? */
- group = purple_find_group(fname);
+ group = purple_blist_find_group(fname);
if (group == NULL) {
group = purple_group_new(fname);
purple_blist_add_group(group, NULL);
@@ -1309,7 +1329,7 @@ _add_contacts_to_purple_blist(NMUser * user, NMFolder * folder)
name = nm_contact_get_display_id(contact);
if (name) {
- buddy = purple_find_buddy_in_group(user->client_data, name, group);
+ buddy = purple_blist_find_buddy_in_group(user->client_data, name, group);
if (buddy == NULL) {
/* Add it to the purple buddy list */
buddy = purple_buddy_new(user->client_data,
@@ -1386,6 +1406,7 @@ _sync_privacy_lists(NMUser *user)
{
GSList *node = NULL, *rem_list = NULL;
PurpleConnection *gc;
+ PurpleAccount *account;
const char *name, *dn;
NMUserRecord *user_record;
@@ -1396,18 +1417,20 @@ _sync_privacy_lists(NMUser *user)
if (gc == NULL)
return;
+ account = purple_connection_get_account(gc);
+
/* Set the Purple privacy setting */
if (user->default_deny) {
if (user->allow_list == NULL) {
- gc->account->perm_deny = PURPLE_PRIVACY_DENY_ALL;
+ purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_DENY_ALL);
} else {
- gc->account->perm_deny = PURPLE_PRIVACY_ALLOW_USERS;
+ purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS);
}
} else {
if (user->deny_list == NULL) {
- gc->account->perm_deny = PURPLE_PRIVACY_ALLOW_ALL;
+ purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL);
} else {
- gc->account->perm_deny = PURPLE_PRIVACY_DENY_USERS;
+ purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_DENY_USERS);
}
}
@@ -1419,9 +1442,9 @@ _sync_privacy_lists(NMUser *user)
else
name =(char *)node->data;
- if (!g_slist_find_custom(gc->account->permit,
+ if (!g_slist_find_custom(purple_account_privacy_get_permitted(account),
name, (GCompareFunc)purple_utf8_strcasecmp)) {
- purple_privacy_permit_add(gc->account, name , TRUE);
+ purple_account_privacy_permit_add(account, name , TRUE);
}
}
@@ -1432,15 +1455,15 @@ _sync_privacy_lists(NMUser *user)
else
name =(char *)node->data;
- if (!g_slist_find_custom(gc->account->deny,
+ if (!g_slist_find_custom(purple_account_privacy_get_denied(account),
name, (GCompareFunc)purple_utf8_strcasecmp)) {
- purple_privacy_deny_add(gc->account, name, TRUE);
+ purple_account_privacy_deny_add(account, name, TRUE);
}
}
/* Remove stuff */
- for (node = gc->account->permit; node; node = node->next) {
+ for (node = purple_account_privacy_get_permitted(account); node; node = node->next) {
dn = nm_lookup_dn(user, (char *)node->data);
if (dn != NULL &&
!g_slist_find_custom(user->allow_list,
@@ -1451,13 +1474,13 @@ _sync_privacy_lists(NMUser *user)
if (rem_list) {
for (node = rem_list; node; node = node->next) {
- purple_privacy_permit_remove(gc->account, (char *)node->data, TRUE);
+ purple_account_privacy_permit_remove(account, (char *)node->data, TRUE);
}
g_slist_free(rem_list);
rem_list = NULL;
}
- for (node = gc->account->deny; node; node = node->next) {
+ for (node = purple_account_privacy_get_denied(account); node; node = node->next) {
dn = nm_lookup_dn(user, (char *)node->data);
if (dn != NULL &&
!g_slist_find_custom(user->deny_list,
@@ -1468,7 +1491,7 @@ _sync_privacy_lists(NMUser *user)
if (rem_list) {
for (node = rem_list; node; node = node->next) {
- purple_privacy_deny_remove(gc->account, (char *)node->data, TRUE);
+ purple_account_privacy_deny_remove(account, (char *)node->data, TRUE);
}
g_slist_free(rem_list);
}
@@ -1510,20 +1533,27 @@ _show_info(PurpleConnection * gc, NMUserRecord * user_record, char * name)
tag = _("User ID");
value = nm_user_record_get_userid(user_record);
if (value) {
- purple_notify_user_info_add_pair(user_info, tag, value);
+ /* 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, tag, value);
}
-/* tag = _("DN");
+#if 0
+ tag = _("DN");
value = nm_user_record_get_dn(user_record);
if (value) {
- purple_notify_user_info_add_pair(user_info, tag, value);
+ /* 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, tag, value);
}
-*/
+#endif /* if 0 */
tag = _("Full name");
value = nm_user_record_get_full_name(user_record);
if (value) {
- purple_notify_user_info_add_pair(user_info, tag, value);
+ /* 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, tag, value);
}
count = nm_user_record_get_property_count(user_record);
@@ -1533,7 +1563,9 @@ _show_info(PurpleConnection * gc, NMUserRecord * user_record, char * name)
tag = _map_property_tag(nm_property_get_tag(property));
value = nm_property_get_value(property);
if (tag && value) {
- purple_notify_user_info_add_pair(user_info, tag, value);
+ /* 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, tag, value);
}
nm_release_property(property);
}
@@ -1606,16 +1638,16 @@ _initiate_conference_cb(PurpleBlistNode *node, gpointer ignored)
NMUser *user;
const char *conf_name;
- PurpleConversation *chat = NULL;
+ PurpleChatConversation *chat = NULL;
NMUserRecord *user_record;
NMConference *conference;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *) node;
gc = purple_account_get_connection(purple_buddy_get_account(buddy));
- user = gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
@@ -1625,7 +1657,7 @@ _initiate_conference_cb(PurpleBlistNode *node, gpointer ignored)
return;
conf_name = _get_conference_name(++user->conference_count);
- chat = serv_got_joined_chat(gc, user->conference_count, conf_name);
+ chat = purple_serv_got_joined_chat(gc, user->conference_count, conf_name);
if (chat) {
conference = nm_create_conference(NULL);
@@ -1655,7 +1687,8 @@ _show_privacy_locked_error(PurpleConnection *gc, NMUser *user)
err = g_strdup_printf(_("Unable to change server side privacy settings (%s)."),
nm_error_to_string(NMERR_ADMIN_LOCKED));
- purple_notify_error(gc, NULL, err, NULL);
+ purple_notify_error(gc, NULL, err, NULL,
+ purple_request_cpar_from_connection(gc));
g_free(err);
}
@@ -1671,7 +1704,7 @@ novell_ssl_connect_error(PurpleSslConnection * gsc,
NMUser *user;
gc = data;
- user = gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
user->conn->ssl_conn->data = NULL;
purple_connection_ssl_error (gc, error);
@@ -1688,7 +1721,7 @@ novell_ssl_recv_cb(gpointer data, PurpleSslConnection * gsc,
if (gc == NULL)
return;
- user = gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
@@ -1697,7 +1730,7 @@ novell_ssl_recv_cb(gpointer data, PurpleSslConnection * gsc,
if (_is_disconnect_error(rc)) {
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Error communicating with server. Closing connection."));
} else {
@@ -1722,7 +1755,7 @@ novell_ssl_connected_cb(gpointer data, PurpleSslConnection * gsc,
if (gc == NULL || gsc == NULL)
return;
- user = gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if ((user == NULL) || (conn = user->conn) == NULL)
return;
@@ -1738,7 +1771,7 @@ novell_ssl_connected_cb(gpointer data, PurpleSslConnection * gsc,
conn->connected = TRUE;
purple_ssl_input_add(gsc, novell_ssl_recv_cb, gc);
} else {
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to connect"));
}
@@ -1758,7 +1791,7 @@ _evt_receive_message(NMUser * user, NMEvent * event)
{
NMUserRecord *user_record = NULL;
NMContact *contact = NULL;
- PurpleConversation *gconv;
+ PurpleIMConversation *im;
NMConference *conference;
PurpleMessageFlags flags;
char *text = NULL;
@@ -1768,7 +1801,7 @@ _evt_receive_message(NMUser * user, NMEvent * event)
conference = nm_event_get_conference(event);
if (conference) {
- PurpleConversation *chat = nm_conference_get_data(conference);
+ PurpleChatConversation *chat = nm_conference_get_data(conference);
/* Is this a single person 'conversation' or a conference? */
if (chat == NULL && nm_conference_get_participant_count(conference) == 1) {
@@ -1780,21 +1813,21 @@ _evt_receive_message(NMUser * user, NMEvent * event)
if (nm_event_get_type(event) == NMEVT_RECEIVE_AUTOREPLY)
flags |= PURPLE_MESSAGE_AUTO_RESP;
- serv_got_im(purple_account_get_connection(user->client_data),
+ purple_serv_got_im(purple_account_get_connection(user->client_data),
nm_user_record_get_display_id(user_record),
text, flags,
nm_event_get_gmt(event));
- gconv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+ im = purple_conversations_find_im_with_account(
nm_user_record_get_display_id(user_record),
(PurpleAccount *) user->client_data);
- if (gconv) {
+ if (im) {
contact = nm_find_contact(user, nm_event_get_source(event));
if (contact) {
- purple_conversation_set_title(
- gconv, nm_contact_get_display_name(contact));
+ purple_conversation_set_title(PURPLE_CONVERSATION(im),
+ nm_contact_get_display_name(contact));
} else {
@@ -1805,7 +1838,7 @@ _evt_receive_message(NMUser * user, NMEvent * event)
if (name == NULL)
name = nm_user_record_get_userid(user_record);
- purple_conversation_set_title(gconv, name);
+ purple_conversation_set_title(PURPLE_CONVERSATION(im), name);
}
}
@@ -1835,9 +1868,9 @@ _evt_receive_message(NMUser * user, NMEvent * event)
name = nm_user_record_get_display_id(user_record);
}
- serv_got_chat_in(purple_account_get_connection(user->client_data),
- purple_conv_chat_get_id(PURPLE_CONV_CHAT(chat)),
- name, 0, text, nm_event_get_gmt(event));
+ purple_serv_got_chat_in(purple_account_get_connection(user->client_data),
+ purple_chat_conversation_get_id(chat),
+ name, PURPLE_MESSAGE_RECV, text, nm_event_get_gmt(event));
}
}
}
@@ -1848,7 +1881,7 @@ _evt_receive_message(NMUser * user, NMEvent * event)
static void
_evt_conference_left(NMUser * user, NMEvent * event)
{
- PurpleConversation *chat;
+ PurpleChatConversation *chat;
NMConference *conference;
conference = nm_event_get_conference(event);
@@ -1859,7 +1892,7 @@ _evt_conference_left(NMUser * user, NMEvent * event)
nm_event_get_source(event));
if (ur)
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(chat),
+ purple_chat_conversation_remove_user(chat,
nm_user_record_get_display_id(ur),
NULL);
}
@@ -1880,8 +1913,7 @@ _evt_conference_invite_notify(NMUser * user, NMEvent * event)
gconv = nm_conference_get_data(conference);
str = g_strdup_printf(_("%s has been invited to this conversation."),
nm_user_record_get_display_id(user_record));
- purple_conversation_write(gconv, NULL, str,
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(gconv, str, 0);
g_free(str);
}
}
@@ -1920,11 +1952,11 @@ _evt_conference_invite(NMUser * user, NMEvent * event)
parms = g_slist_append(parms, nm_event_get_conference(event));
/* Prompt the user */
- /* TODO: Would it be better to use serv_got_chat_invite() here? */
+ /* TODO: Would it be better to use purple_serv_got_chat_invite() here? */
gc = purple_account_get_connection(user->client_data);
purple_request_action(gc, title, primary, secondary,
PURPLE_DEFAULT_ACTION_NONE,
- purple_connection_get_account(gc), name, NULL,
+ purple_request_cpar_from_connection(gc),
parms, 2,
_("Yes"), G_CALLBACK(_join_conference_cb),
_("No"), G_CALLBACK(_reject_conference_cb));
@@ -1936,7 +1968,7 @@ _evt_conference_invite(NMUser * user, NMEvent * event)
static void
_evt_conference_joined(NMUser * user, NMEvent * event)
{
- PurpleConversation *chat = NULL;
+ PurpleChatConversation *chat = NULL;
PurpleConnection *gc;
NMConference *conference = NULL;
NMUserRecord *ur = NULL;
@@ -1955,14 +1987,14 @@ _evt_conference_joined(NMUser * user, NMEvent * event)
if (ur) {
conf_name = _get_conference_name(++user->conference_count);
chat =
- serv_got_joined_chat(gc, user->conference_count, conf_name);
+ purple_serv_got_joined_chat(gc, user->conference_count, conf_name);
if (chat) {
nm_conference_set_data(conference, (gpointer) chat);
name = nm_user_record_get_display_id(ur);
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(chat), name, NULL,
- PURPLE_CBFLAGS_NONE, TRUE);
+ purple_chat_conversation_add_user(chat, name, NULL,
+ PURPLE_CHAT_USER_NONE, TRUE);
}
}
@@ -1972,9 +2004,9 @@ _evt_conference_joined(NMUser * user, NMEvent * event)
ur = nm_find_user_record(user, nm_event_get_source(event));
if (ur) {
name = nm_user_record_get_display_id(ur);
- if (!purple_conv_chat_find_user(PURPLE_CONV_CHAT(chat), name)) {
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(chat), name, NULL,
- PURPLE_CBFLAGS_NONE, TRUE);
+ if (!purple_chat_conversation_has_user(chat, name)) {
+ purple_chat_conversation_add_user(chat, name, NULL,
+ PURPLE_CHAT_USER_NONE, TRUE);
}
}
}
@@ -1999,7 +2031,7 @@ _evt_status_change(NMUser * user, NMEvent * event)
/* Update status for buddy in all folders */
display_id = nm_user_record_get_display_id(user_record);
- buddies = purple_find_buddies(user->client_data, display_id);
+ buddies = purple_blist_find_buddies(user->client_data, display_id);
for (bnode = buddies; bnode; bnode = bnode->next) {
buddy = (PurpleBuddy *) bnode->data;
if (buddy) {
@@ -2022,8 +2054,8 @@ _evt_user_disconnect(NMUser * user, NMEvent * event)
if (gc)
{
if (!purple_account_get_remember_password(account))
- purple_account_set_password(account, NULL);
- purple_connection_error_reason(gc,
+ purple_account_set_password(account, NULL, NULL, NULL);
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NAME_IN_USE,
_("You have signed on from another location"));
}
@@ -2039,8 +2071,8 @@ _evt_user_typing(NMUser * user, NMEvent * event)
if (gc) {
user_record = nm_find_user_record(user, nm_event_get_source(event));
if (user_record) {
- serv_got_typing(gc, nm_user_record_get_display_id(user_record),
- 30, PURPLE_TYPING);
+ purple_serv_got_typing(gc, nm_user_record_get_display_id(user_record),
+ 30, PURPLE_IM_TYPING);
}
}
}
@@ -2055,7 +2087,7 @@ _evt_user_not_typing(NMUser * user, NMEvent * event)
if (gc) {
user_record = nm_find_user_record(user, nm_event_get_source(event));
if (user_record) {
- serv_got_typing_stopped(gc,
+ purple_serv_got_typing_stopped(gc,
nm_user_record_get_display_id(user_record));
}
}
@@ -2072,8 +2104,7 @@ _evt_undeliverable_status(NMUser * user, NMEvent * event)
if (ur) {
/* XXX - Should this be PURPLE_CONV_TYPE_IM? */
gconv =
- purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
- nm_user_record_get_display_id(ur),
+ purple_conversations_find_with_account(nm_user_record_get_display_id(ur),
user->client_data);
if (gconv) {
const char *name = nm_user_record_get_full_name(ur);
@@ -2083,8 +2114,7 @@ _evt_undeliverable_status(NMUser * user, NMEvent * event)
}
str = g_strdup_printf(_("%s appears to be offline and did not receive"
" the message that you just sent."), name);
- purple_conversation_write(gconv, NULL, str,
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(gconv, str, 0);
g_free(str);
}
}
@@ -2171,6 +2201,8 @@ novell_login(PurpleAccount * account)
if (gc == NULL)
return;
+ purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_NO_IMAGES);
+
server = purple_account_get_string(account, "server", NULL);
if (server == NULL || *server == '\0') {
@@ -2179,7 +2211,7 @@ novell_login(PurpleAccount * account)
*/
/* ...but for now just error out with a nice message. */
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
_("Unable to connect to server. Please enter the "
"address of the server to which you wish to connect."));
@@ -2192,7 +2224,7 @@ novell_login(PurpleAccount * account)
user = nm_initialize_user(name, server, port, account, _event_callback);
if (user && user->conn) {
/* save user */
- gc->proto_data = user;
+ purple_connection_set_protocol_data(gc, user);
/* connect to the server */
purple_connection_update_progress(gc, _("Connecting"),
@@ -2208,7 +2240,7 @@ novell_login(PurpleAccount * account)
user->conn->addr, user->conn->port,
novell_ssl_connected_cb, novell_ssl_connect_error, gc);
if (user->conn->ssl_conn->data == NULL) {
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
_("SSL support unavailable"));
}
@@ -2224,7 +2256,7 @@ novell_close(PurpleConnection * gc)
if (gc == NULL)
return;
- user = gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user) {
conn = user->conn;
if (conn && conn->ssl_conn) {
@@ -2232,12 +2264,11 @@ novell_close(PurpleConnection * gc)
}
nm_deinitialize_user(user);
}
- gc->proto_data = NULL;
+ purple_connection_set_protocol_data(gc, NULL);
}
static int
-novell_send_im(PurpleConnection * gc, const char *name,
- const char *message_body, PurpleMessageFlags flags)
+novell_send_im(PurpleConnection *gc, PurpleMessage *msg)
{
NMUserRecord *user_record = NULL;
NMConference *conf = NULL;
@@ -2247,17 +2278,17 @@ novell_send_im(PurpleConnection * gc, const char *name,
char *plain;
gboolean done = TRUE, created_conf = FALSE;
NMERR_T rc = NM_OK;
+ const gchar *name = purple_message_get_recipient(msg);
- if (gc == NULL || name == NULL ||
- message_body == NULL || *message_body == '\0')
+ if (gc == NULL || name == NULL || purple_message_is_empty(msg))
return 0;
- user = gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return 0;
/* Create a new message */
- plain = purple_unescape_html(message_body);
+ plain = purple_unescape_html(purple_message_get_contents(msg));
message = nm_create_message(plain);
g_free(plain);
@@ -2328,7 +2359,7 @@ novell_send_im(PurpleConnection * gc, const char *name,
}
static unsigned int
-novell_send_typing(PurpleConnection * gc, const char *name, PurpleTypingState state)
+novell_send_typing(PurpleConnection * gc, const char *name, PurpleIMTypingState state)
{
NMConference *conf = NULL;
NMUser *user;
@@ -2338,7 +2369,7 @@ novell_send_typing(PurpleConnection * gc, const char *name, PurpleTypingState st
if (gc == NULL || name == NULL)
return 0;
- user = gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return 0;
@@ -2351,7 +2382,7 @@ novell_send_typing(PurpleConnection * gc, const char *name, PurpleTypingState st
if (conf) {
rc = nm_send_typing(user, conf,
- ((state == PURPLE_TYPING) ? TRUE : FALSE), NULL);
+ ((state == PURPLE_IM_TYPING) ? TRUE : FALSE), NULL);
_check_for_disconnect(user, rc);
}
@@ -2372,7 +2403,7 @@ novell_convo_closed(PurpleConnection * gc, const char *who)
if (gc == NULL || who == NULL)
return;
- user = gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user && (dn = nm_lookup_dn(user, who))) {
conf = nm_find_conversation(user, dn);
if (conf) {
@@ -2387,21 +2418,21 @@ novell_chat_leave(PurpleConnection * gc, int id)
{
NMConference *conference;
NMUser *user;
- PurpleConversation *chat;
+ PurpleChatConversation *chat;
GSList *cnode;
NMERR_T rc = NM_OK;
if (gc == NULL)
return;
- user = gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) {
conference = cnode->data;
if (conference && (chat = nm_conference_get_data(conference))) {
- if (purple_conv_chat_get_id(PURPLE_CONV_CHAT(chat)) == id) {
+ if (purple_chat_conversation_get_id(chat) == id) {
rc = nm_send_leave_conference(user, conference, NULL, NULL);
_check_for_disconnect(user, rc);
break;
@@ -2409,7 +2440,7 @@ novell_chat_leave(PurpleConnection * gc, int id)
}
}
- serv_got_chat_left(gc, id);
+ purple_serv_got_chat_left(gc, id);
}
static void
@@ -2418,7 +2449,7 @@ novell_chat_invite(PurpleConnection *gc, int id,
{
NMConference *conference;
NMUser *user;
- PurpleConversation *chat;
+ PurpleChatConversation *chat;
GSList *cnode;
NMERR_T rc = NM_OK;
NMUserRecord *user_record = NULL;
@@ -2426,7 +2457,7 @@ novell_chat_invite(PurpleConnection *gc, int id,
if (gc == NULL)
return;
- user = gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
@@ -2440,7 +2471,7 @@ novell_chat_invite(PurpleConnection *gc, int id,
for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) {
conference = cnode->data;
if (conference && (chat = nm_conference_get_data(conference))) {
- if (purple_conv_chat_get_id(PURPLE_CONV_CHAT(chat)) == id) {
+ if (purple_chat_conversation_get_id(chat) == id) {
rc = nm_send_conference_invite(user, conference, user_record,
message, _sendinvite_resp_cb, NULL);
_check_for_disconnect(user, rc);
@@ -2451,10 +2482,10 @@ novell_chat_invite(PurpleConnection *gc, int id,
}
static int
-novell_chat_send(PurpleConnection * gc, int id, const char *text, PurpleMessageFlags flags)
+novell_chat_send(PurpleConnection * gc, int id, PurpleMessage *msg)
{
NMConference *conference;
- PurpleConversation *chat;
+ PurpleChatConversation *chat;
GSList *cnode;
NMMessage *message;
NMUser *user;
@@ -2462,21 +2493,21 @@ novell_chat_send(PurpleConnection * gc, int id, const char *text, PurpleMessageF
const char *name;
char *str, *plain;
- if (gc == NULL || text == NULL)
+ if (gc == NULL || purple_message_is_empty(msg))
return -1;
- user = gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return -1;
- plain = purple_unescape_html(text);
+ plain = purple_unescape_html(purple_message_get_contents(msg));
message = nm_create_message(plain);
g_free(plain);
for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) {
conference = cnode->data;
if (conference && (chat = nm_conference_get_data(conference))) {
- if (purple_conv_chat_get_id(PURPLE_CONV_CHAT(chat)) == id) {
+ if (purple_chat_conversation_get_id(chat) == id) {
nm_message_set_conference(message, conference);
@@ -2493,7 +2524,7 @@ novell_chat_send(PurpleConnection * gc, int id, const char *text, PurpleMessageF
if (!_check_for_disconnect(user, rc)) {
/* Use the account alias if it is set */
- name = purple_account_get_alias(user->client_data);
+ name = purple_account_get_private_alias(user->client_data);
if (name == NULL || *name == '\0') {
/* If there is no account alias, try full name */
@@ -2505,7 +2536,9 @@ novell_chat_send(PurpleConnection * gc, int id, const char *text, PurpleMessageF
}
}
- serv_got_chat_in(gc, id, name, flags, text, time(NULL));
+ purple_serv_got_chat_in(gc, id, name,
+ purple_message_get_flags(msg),
+ purple_message_get_contents(msg), time(NULL));
return 0;
} else
return -1;
@@ -2516,11 +2549,11 @@ novell_chat_send(PurpleConnection * gc, int id, const char *text, PurpleMessageF
/* The conference was not found, must be closed */
- chat = purple_find_chat(gc, id);
+ chat = purple_conversations_find_chat(gc, id);
if (chat) {
str = g_strdup(_("This conference has been closed."
" No more messages can be sent."));
- purple_conversation_write(chat, NULL, str, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(chat), str, 0);
g_free(str);
}
@@ -2531,7 +2564,7 @@ novell_chat_send(PurpleConnection * gc, int id, const char *text, PurpleMessageF
}
static void
-novell_add_buddy(PurpleConnection * gc, PurpleBuddy *buddy, PurpleGroup * group)
+novell_add_buddy(PurpleConnection * gc, PurpleBuddy *buddy, PurpleGroup * group, const char *message)
{
NMFolder *folder = NULL;
NMContact *contact;
@@ -2605,7 +2638,7 @@ novell_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group
if (gc == NULL || buddy == NULL || group == NULL)
return;
- user = (NMUser *) gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user && (dn = nm_lookup_dn(user, purple_buddy_get_name(buddy)))) {
gname = purple_group_get_name(group);
if (strcmp(gname, NM_ROOT_FOLDER_NAME) == 0) {
@@ -2637,7 +2670,7 @@ novell_remove_group(PurpleConnection * gc, PurpleGroup *group)
if (gc == NULL || group == NULL)
return;
- user = (NMUser *) gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user) {
NMFolder *folder = nm_find_folder(user, purple_group_get_name(group));
@@ -2662,7 +2695,7 @@ novell_alias_buddy(PurpleConnection * gc, const char *name, const char *alias)
if (gc == NULL || name == NULL || alias == NULL)
return;
- user = (NMUser *) gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user && (dn = nm_lookup_dn(user, name))) {
/* Alias all of instances of the contact */
@@ -2682,16 +2715,16 @@ novell_alias_buddy(PurpleConnection * gc, const char *name, const char *alias)
if (*fname == '\0') {
fname = NM_ROOT_FOLDER_NAME;
}
- group = purple_find_group(fname);
+ group = purple_blist_find_group(fname);
}
if (group) {
const char *balias;
- buddy = purple_find_buddy_in_group(user->client_data,
+ buddy = purple_blist_find_buddy_in_group(user->client_data,
name, group);
- balias = buddy ? purple_buddy_get_local_buddy_alias(buddy) : NULL;
+ balias = buddy ? purple_buddy_get_local_alias(buddy) : NULL;
if (balias && strcmp(balias, alias))
- purple_blist_alias_buddy(buddy, alias);
+ purple_buddy_set_local_alias(buddy, alias);
}
/* Tell the server to alias the contact */
@@ -2721,7 +2754,7 @@ novell_group_buddy(PurpleConnection * gc,
old_group_name == NULL || new_group_name == NULL)
return;
- user = (NMUser *) gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user && (dn = nm_lookup_dn(user, name))) {
/* Find the old folder */
@@ -2779,12 +2812,12 @@ novell_rename_group(PurpleConnection * gc, const char *old_name,
return;
}
- user = gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user) {
const char *gname = purple_group_get_name(group);
/* Does new folder exist already? */
if (nm_find_folder(user, gname)) {
- /* purple_blist_rename_group() adds the buddies
+ /* purple_group_set_name() adds the buddies
* to the new group and removes the old group...
* so there is nothing more to do here.
*/
@@ -2825,7 +2858,7 @@ novell_tooltip_text(PurpleBuddy * buddy, PurpleNotifyUserInfo * user_info, gbool
return;
gc = purple_account_get_connection(purple_buddy_get_account(buddy));
- if (gc == NULL || (user = gc->proto_data) == NULL)
+ if (gc == NULL || (user = purple_connection_get_protocol_data(gc)) == NULL)
return;
if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
@@ -2855,10 +2888,13 @@ novell_tooltip_text(PurpleBuddy * buddy, PurpleNotifyUserInfo * user_info, gbool
break;
}
- purple_notify_user_info_add_pair(user_info, _("Status"), status_str);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), status_str);
- if (text)
- purple_notify_user_info_add_pair(user_info, _("Message"), text);
+ if (text) {
+ /* 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, _("Message"), text);
+ }
}
}
}
@@ -2874,7 +2910,7 @@ novell_set_idle(PurpleConnection * gc, int time)
if (gc == NULL)
return;
- user = gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
@@ -2903,7 +2939,7 @@ novell_get_info(PurpleConnection * gc, const char *name)
if (gc == NULL || name == NULL)
return;
- user = (NMUser *) gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user) {
user_record = nm_find_user_record(user, name);
@@ -2932,17 +2968,19 @@ novell_status_text(PurpleBuddy * buddy)
if (buddy && account) {
PurpleConnection *gc = purple_account_get_connection(account);
- if (gc && gc->proto_data) {
- NMUser *user = gc->proto_data;
+ if (gc) {
+ NMUser *user = purple_connection_get_protocol_data(gc);
- dn = nm_lookup_dn(user, purple_buddy_get_name(buddy));
- if (dn) {
- NMUserRecord *user_record = nm_find_user_record(user, dn);
+ if (user) {
+ dn = nm_lookup_dn(user, purple_buddy_get_name(buddy));
+ if (dn) {
+ NMUserRecord *user_record = nm_find_user_record(user, dn);
- if (user_record) {
- text = nm_user_record_get_status_text(user_record);
- if (text)
- return g_strdup(text);
+ if (user_record) {
+ text = nm_user_record_get_status_text(user_record);
+ if (text)
+ return g_strdup(text);
+ }
}
}
}
@@ -2961,19 +2999,19 @@ novell_status_types(PurpleAccount *account)
type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, NOVELL_STATUS_TYPE_AVAILABLE,
NULL, TRUE, TRUE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
status_types = g_list_append(status_types, type);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, NOVELL_STATUS_TYPE_AWAY,
NULL, TRUE, TRUE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
status_types = g_list_append(status_types, type);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE, NOVELL_STATUS_TYPE_BUSY,
_("Busy"), TRUE, TRUE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
status_types = g_list_append(status_types, type);
@@ -3003,7 +3041,7 @@ novell_set_status(PurpleAccount *account, PurpleStatus *status)
connected = purple_account_is_connected(account);
presence = purple_status_get_presence(status);
- type = purple_status_get_type(status);
+ type = purple_status_get_status_type(status);
primitive = purple_status_type_get_primitive(type);
/*
@@ -3018,7 +3056,7 @@ novell_set_status(PurpleAccount *account, PurpleStatus *status)
return;
gc = purple_account_get_connection(account);
- user = gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
@@ -3065,14 +3103,14 @@ novell_add_permit(PurpleConnection *gc, const char *who)
if (gc == NULL || who == NULL)
return;
- user = gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
/* Remove first -- we will add it back in when we get
* the okay from the server
*/
- purple_privacy_permit_remove(gc->account, who, TRUE);
+ purple_account_privacy_permit_remove(purple_connection_get_account(gc), who, TRUE);
if (nm_user_is_privacy_locked(user)) {
_show_privacy_locked_error(gc, user);
@@ -3109,14 +3147,14 @@ novell_add_deny(PurpleConnection *gc, const char *who)
if (gc == NULL || who == NULL)
return;
- user = gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
/* Remove first -- we will add it back in when we get
* the okay from the server
*/
- purple_privacy_deny_remove(gc->account, who, TRUE);
+ purple_account_privacy_deny_remove(purple_connection_get_account(gc), who, TRUE);
if (nm_user_is_privacy_locked(user)) {
_show_privacy_locked_error(gc, user);
@@ -3153,7 +3191,7 @@ novell_rem_permit(PurpleConnection *gc, const char *who)
if (gc == NULL || who == NULL)
return;
- user = gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
@@ -3183,7 +3221,7 @@ novell_rem_deny(PurpleConnection *gc, const char *who)
if (gc == NULL || who == NULL)
return;
- user = gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
@@ -3214,11 +3252,14 @@ novell_set_permit_deny(PurpleConnection *gc)
int i, j, num_contacts, num_folders;
NMContact *contact;
NMFolder *folder = NULL;
+ PurpleAccount *account;
if (gc == NULL)
return;
- user = gc->proto_data;
+ account = purple_connection_get_account(gc);
+
+ user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
@@ -3234,9 +3275,9 @@ novell_set_permit_deny(PurpleConnection *gc)
return;
}
- switch (gc->account->perm_deny) {
+ switch (purple_account_get_privacy_type(account)) {
- case PURPLE_PRIVACY_ALLOW_ALL:
+ case PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL:
rc = nm_send_set_privacy_default(user, FALSE,
_set_privacy_default_resp_cb, NULL);
_check_for_disconnect(user, rc);
@@ -3256,7 +3297,7 @@ novell_set_permit_deny(PurpleConnection *gc)
}
break;
- case PURPLE_PRIVACY_DENY_ALL:
+ case PURPLE_ACCOUNT_PRIVACY_DENY_ALL:
rc = nm_send_set_privacy_default(user, TRUE,
_set_privacy_default_resp_cb, NULL);
_check_for_disconnect(user, rc);
@@ -3276,7 +3317,7 @@ novell_set_permit_deny(PurpleConnection *gc)
}
break;
- case PURPLE_PRIVACY_ALLOW_USERS:
+ case PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS:
rc = nm_send_set_privacy_default(user, TRUE,
_set_privacy_default_resp_cb, NULL);
@@ -3290,14 +3331,14 @@ novell_set_permit_deny(PurpleConnection *gc)
if (user_record) {
name = nm_user_record_get_display_id(user_record);
- if (!g_slist_find_custom(gc->account->permit,
+ if (!g_slist_find_custom(purple_account_privacy_get_permitted(account),
name, (GCompareFunc)purple_utf8_strcasecmp)) {
- purple_privacy_permit_add(gc->account, name , TRUE);
+ purple_account_privacy_permit_add(account, name , TRUE);
}
}
}
- for (node = gc->account->permit; node; node = node->next) {
+ for (node = purple_account_privacy_get_permitted(account); node; node = node->next) {
dn = nm_lookup_dn(user, (char *)node->data);
if (dn) {
@@ -3306,15 +3347,16 @@ novell_set_permit_deny(PurpleConnection *gc)
rc = nm_send_create_privacy_item(user, dn, TRUE,
_create_privacy_item_deny_resp_cb,
g_strdup(dn));
+ _check_for_disconnect(user, rc);
}
} else {
- purple_privacy_permit_remove(gc->account, (char *)node->data, TRUE);
+ purple_account_privacy_permit_remove(account, (char *)node->data, TRUE);
}
}
}
break;
- case PURPLE_PRIVACY_DENY_USERS:
+ case PURPLE_ACCOUNT_PRIVACY_DENY_USERS:
/* set to default allow */
rc = nm_send_set_privacy_default(user, FALSE,
@@ -3329,14 +3371,14 @@ novell_set_permit_deny(PurpleConnection *gc)
if (user_record) {
name = nm_user_record_get_display_id(user_record);
- if (!g_slist_find_custom(gc->account->deny,
+ if (!g_slist_find_custom(purple_account_privacy_get_denied(account),
name, (GCompareFunc)purple_utf8_strcasecmp)) {
- purple_privacy_deny_add(gc->account, name , TRUE);
+ purple_account_privacy_deny_add(account, name , TRUE);
}
}
}
- for (node = gc->account->deny; node; node = node->next) {
+ for (node = purple_account_privacy_get_denied(account); node; node = node->next) {
name = NULL;
dn = nm_lookup_dn(user, (char *)node->data);
@@ -3349,16 +3391,17 @@ novell_set_permit_deny(PurpleConnection *gc)
rc = nm_send_create_privacy_item(user, dn, FALSE,
_create_privacy_item_deny_resp_cb,
g_strdup(name));
+ _check_for_disconnect(user, rc);
}
} else {
- purple_privacy_deny_remove(gc->account, (char *)node->data, TRUE);
+ purple_account_privacy_deny_remove(account, (char *)node->data, TRUE);
}
}
}
break;
- case PURPLE_PRIVACY_ALLOW_BUDDYLIST:
+ case PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST:
/* remove users from allow list that are not in buddy list */
copy = g_slist_copy(user->allow_list);
@@ -3424,7 +3467,7 @@ novell_blist_node_menu(PurpleBlistNode *node)
GList *list = NULL;
PurpleMenuAction *act;
- if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ if(PURPLE_IS_BUDDY(node)) {
act = purple_menu_action_new(_("Initiate _Chat"),
PURPLE_CALLBACK(_initiate_conference_cb),
NULL, NULL);
@@ -3443,7 +3486,7 @@ novell_keepalive(PurpleConnection *gc)
if (gc == NULL)
return;
- user = gc->proto_data;
+ user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
@@ -3451,7 +3494,15 @@ novell_keepalive(PurpleConnection *gc)
_check_for_disconnect(user, rc);
}
+static gssize
+novell_get_max_message_size(PurpleConversation *conv)
+{
+ /* XXX: got from pidgin-otr - verify and document it */
+ return 1792;
+}
+
static PurplePluginProtocolInfo prpl_info = {
+ sizeof(PurplePluginProtocolInfo), /* struct_size */
0,
NULL, /* user_splits */
NULL, /* protocol_options */
@@ -3487,12 +3538,10 @@ static PurplePluginProtocolInfo prpl_info = {
NULL, /* get_chat_name */
novell_chat_invite, /* chat_invite */
novell_chat_leave, /* chat_leave */
- NULL, /* chat_whisper */
novell_chat_send, /* chat_send */
novell_keepalive, /* keepalive */
NULL, /* register_user */
NULL, /* get_cb_info */
- NULL, /* get_cb_away */
novell_alias_buddy, /* alias_buddy */
novell_group_buddy, /* group_buddy */
novell_rename_group, /* rename_group */
@@ -3517,15 +3566,13 @@ static PurplePluginProtocolInfo prpl_info = {
NULL, /* unregister_user */
NULL, /* send_attention */
NULL, /* get_attention_types */
- sizeof(PurplePluginProtocolInfo), /* struct_size */
NULL, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* get_media_caps */
NULL, /* get_moods */
NULL, /* set_public_alias */
NULL, /* get_public_alias */
- NULL, /* add_buddy_with_invite */
- NULL /* add_buddies_with_invite */
+ novell_get_max_message_size /* get_max_message_size */
};
static PurplePluginInfo info = {
diff --git a/libpurple/protocols/null/Makefile.am b/libpurple/protocols/null/Makefile.am
index 3bc76ee2a3..73dfc6b12e 100644
--- a/libpurple/protocols/null/Makefile.am
+++ b/libpurple/protocols/null/Makefile.am
@@ -2,19 +2,19 @@ EXTRA_DIST = \
Makefile.mingw \
README
-pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+pkgdir = @PURPLE_PLUGINDIR@
NULLSOURCES = nullprpl.c
AM_CFLAGS = $(st)
-libnull_la_LDFLAGS = -module -avoid-version
+libnull_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
# nullprpl isn't built by default. when it is built, it's dynamically linked.
st =
pkg_LTLIBRARIES = libnull.la
libnull_la_SOURCES = $(NULLSOURCES)
-libnull_la_LIBADD = $(GLIB_LIBS)
+libnull_la_LIBADD = @PURPLE_LIBS@
AM_CPPFLAGS = \
-I$(top_srcdir)/libpurple \
diff --git a/libpurple/protocols/null/Makefile.mingw b/libpurple/protocols/null/Makefile.mingw
index f7c3b4fe5b..31d41c2cc7 100644
--- a/libpurple/protocols/null/Makefile.mingw
+++ b/libpurple/protocols/null/Makefile.mingw
@@ -46,6 +46,7 @@ OBJECTS = $(C_SRC:%.c=%.o)
##
LIBS = \
-lglib-2.0 \
+ -lgobject-2.0 \
-lintl \
-lws2_32 \
-lpurple
diff --git a/libpurple/protocols/null/nullprpl.c b/libpurple/protocols/null/nullprpl.c
index 1fdb0e4fb1..bbb446a9dc 100644
--- a/libpurple/protocols/null/nullprpl.c
+++ b/libpurple/protocols/null/nullprpl.c
@@ -54,13 +54,12 @@
#include "account.h"
#include "accountopt.h"
-#include "blist.h"
+#include "buddylist.h"
#include "cmds.h"
#include "conversation.h"
#include "connection.h"
#include "debug.h"
#include "notify.h"
-#include "privacy.h"
#include "prpl.h"
#include "roomlist.h"
#include "status.h"
@@ -104,7 +103,7 @@ typedef struct {
static PurpleConnection *get_nullprpl_gc(const char *username) {
PurpleAccount *acct = purple_accounts_find(username, NULLPRPL_ID);
if (acct && purple_account_is_connected(acct))
- return acct->gc;
+ return purple_account_get_connection(acct);
else
return NULL;
}
@@ -113,7 +112,7 @@ static void call_if_nullprpl(gpointer data, gpointer userdata) {
PurpleConnection *gc = (PurpleConnection *)(data);
GcFuncData *gcfdata = (GcFuncData *)userdata;
- if (!strcmp(gc->account->protocol_id, NULLPRPL_ID))
+ if (!strcmp(purple_account_get_protocol_id(purple_connection_get_account(gc)), NULLPRPL_ID))
gcfdata->fn(gcfdata->from, gc, gcfdata->userdata);
}
@@ -125,12 +124,12 @@ static void foreach_nullprpl_gc(GcFunc fn, PurpleConnection *from,
}
-typedef void(*ChatFunc)(PurpleConvChat *from, PurpleConvChat *to,
+typedef void(*ChatFunc)(PurpleChatConversation *from, PurpleChatConversation *to,
int id, const char *room, gpointer userdata);
typedef struct {
ChatFunc fn;
- PurpleConvChat *from_chat;
+ PurpleChatConversation *from_chat;
gpointer userdata;
} ChatFuncData;
@@ -138,19 +137,18 @@ static void call_chat_func(gpointer data, gpointer userdata) {
PurpleConnection *to = (PurpleConnection *)data;
ChatFuncData *cfdata = (ChatFuncData *)userdata;
- int id = cfdata->from_chat->id;
- PurpleConversation *conv = purple_find_chat(to, id);
- if (conv) {
- PurpleConvChat *chat = purple_conversation_get_chat_data(conv);
- cfdata->fn(cfdata->from_chat, chat, id, conv->name, cfdata->userdata);
- }
+ int id = purple_chat_conversation_get_id(cfdata->from_chat);
+ PurpleChatConversation *chat = purple_conversations_find_chat(to, id);
+ if (chat)
+ cfdata->fn(cfdata->from_chat, chat, id,
+ purple_conversation_get_name(PURPLE_CONVERSATION(chat)), cfdata->userdata);
}
static void foreach_gc_in_chat(ChatFunc fn, PurpleConnection *from,
int id, gpointer userdata) {
- PurpleConversation *conv = purple_find_chat(from, id);
+ PurpleChatConversation *chat = purple_conversations_find_chat(from, id);
ChatFuncData cfdata = { fn,
- purple_conversation_get_chat_data(conv),
+ chat,
userdata };
g_list_foreach(purple_connections_get_all(), call_chat_func,
@@ -160,11 +158,11 @@ static void foreach_gc_in_chat(ChatFunc fn, PurpleConnection *from,
static void discover_status(PurpleConnection *from, PurpleConnection *to,
gpointer userdata) {
- const char *from_username = from->account->username;
- const char *to_username = to->account->username;
+ const char *from_username = purple_account_get_username(purple_connection_get_account(from));
+ const char *to_username = purple_account_get_username(purple_connection_get_account(to));
- if (purple_find_buddy(from->account, to_username)) {
- PurpleStatus *status = purple_account_get_active_status(to->account);
+ if (purple_blist_find_buddy(purple_connection_get_account(from), to_username)) {
+ PurpleStatus *status = purple_account_get_active_status(purple_connection_get_account(to));
const char *status_id = purple_status_get_id(status);
const char *message = purple_status_get_attr_string(status, "message");
@@ -173,7 +171,7 @@ static void discover_status(PurpleConnection *from, PurpleConnection *to,
!strcmp(status_id, NULL_STATUS_OFFLINE)) {
purple_debug_info("nullprpl", "%s sees that %s is %s: %s\n",
from_username, to_username, status_id, message);
- purple_prpl_got_user_status(from->account, to_username, status_id,
+ purple_prpl_got_user_status(purple_connection_get_account(from), to_username, status_id,
(message) ? "message" : NULL, message, NULL);
} else {
purple_debug_error("nullprpl",
@@ -186,7 +184,7 @@ static void discover_status(PurpleConnection *from, PurpleConnection *to,
static void report_status_change(PurpleConnection *from, PurpleConnection *to,
gpointer userdata) {
purple_debug_info("nullprpl", "notifying %s that %s changed status\n",
- to->account->username, from->account->username);
+ purple_account_get_username(purple_connection_get_account(to)), purple_account_get_username(purple_connection_get_account(from)));
discover_status(to, from, NULL);
}
@@ -199,7 +197,7 @@ static void nullprpl_input_user_info(PurplePluginAction *action)
PurpleConnection *gc = (PurpleConnection *)action->context;
PurpleAccount *acct = purple_connection_get_account(gc);
purple_debug_info("nullprpl", "showing 'Set User Info' dialog for %s\n",
- acct->username);
+ purple_account_get_username(acct));
purple_account_request_change_user_info(acct);
}
@@ -225,25 +223,27 @@ static const char *nullprpl_list_icon(PurpleAccount *acct, PurpleBuddy *buddy)
static char *nullprpl_status_text(PurpleBuddy *buddy) {
purple_debug_info("nullprpl", "getting %s's status text for %s\n",
- buddy->name, buddy->account->username);
+ purple_buddy_get_name(buddy),
+ purple_account_get_username(purple_buddy_get_account(buddy)));
- if (purple_find_buddy(buddy->account, buddy->name)) {
+ if (purple_blist_find_buddy(purple_buddy_get_account(buddy), purple_buddy_get_name(buddy))) {
PurplePresence *presence = purple_buddy_get_presence(buddy);
PurpleStatus *status = purple_presence_get_active_status(presence);
const char *name = purple_status_get_name(status);
const char *message = purple_status_get_attr_string(status, "message");
char *text;
- if (message && strlen(message) > 0)
+ if (message && *message)
text = g_strdup_printf("%s: %s", name, message);
else
text = g_strdup(name);
- purple_debug_info("nullprpl", "%s's status text is %s\n", buddy->name, text);
+ purple_debug_info("nullprpl", "%s's status text is %s\n",
+ purple_buddy_get_name(buddy), text);
return text;
} else {
- purple_debug_info("nullprpl", "...but %s is not logged in\n", buddy->name);
+ purple_debug_info("nullprpl", "...but %s is not logged in\n", purple_buddy_get_name(buddy));
return g_strdup("Not logged in");
}
}
@@ -251,30 +251,34 @@ static char *nullprpl_status_text(PurpleBuddy *buddy) {
static void nullprpl_tooltip_text(PurpleBuddy *buddy,
PurpleNotifyUserInfo *info,
gboolean full) {
- PurpleConnection *gc = get_nullprpl_gc(buddy->name);
+ PurpleConnection *gc = get_nullprpl_gc(purple_buddy_get_name(buddy));
if (gc) {
/* they're logged in */
PurplePresence *presence = purple_buddy_get_presence(buddy);
PurpleStatus *status = purple_presence_get_active_status(presence);
char *msg = nullprpl_status_text(buddy);
- purple_notify_user_info_add_pair(info, purple_status_get_name(status),
+ /* 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(info, purple_status_get_name(status),
msg);
g_free(msg);
if (full) {
- const char *user_info = purple_account_get_user_info(gc->account);
+ const char *user_info = purple_account_get_user_info(purple_connection_get_account(gc));
if (user_info)
- purple_notify_user_info_add_pair(info, _("User info"), user_info);
+ /* 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(info, _("User info"), user_info);
}
} else {
/* they're not logged in */
- purple_notify_user_info_add_pair(info, _("User info"), _("not logged in"));
+ purple_notify_user_info_add_pair_plaintext(info, _("User info"), _("not logged in"));
}
purple_debug_info("nullprpl", "showing %s tooltip for %s\n",
- (full) ? "full" : "short", buddy->name);
+ (full) ? "full" : "short", purple_buddy_get_name(buddy));
}
static GList *nullprpl_status_types(PurpleAccount *acct)
@@ -283,24 +287,24 @@ static GList *nullprpl_status_types(PurpleAccount *acct)
PurpleStatusType *type;
purple_debug_info("nullprpl", "returning status types for %s: %s, %s, %s\n",
- acct->username,
+ purple_account_get_username(acct),
NULL_STATUS_ONLINE, NULL_STATUS_AWAY, NULL_STATUS_OFFLINE);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
NULL_STATUS_ONLINE, NULL, TRUE, TRUE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_prepend(types, type);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY,
NULL_STATUS_AWAY, NULL, TRUE, TRUE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_prepend(types, type);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_OFFLINE,
NULL_STATUS_OFFLINE, NULL, TRUE, TRUE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_prepend(types, type);
@@ -309,18 +313,19 @@ static GList *nullprpl_status_types(PurpleAccount *acct)
static void blist_example_menu_item(PurpleBlistNode *node, gpointer userdata) {
purple_debug_info("nullprpl", "example menu item clicked on user %s\n",
- ((PurpleBuddy *)node)->name);
+ purple_buddy_get_name(PURPLE_BUDDY(node)));
purple_notify_info(NULL, /* plugin handle or PurpleConnection */
_("Primary title"),
_("Secondary title"),
- _("This is the callback for the nullprpl menu item."));
+ _("This is the callback for the nullprpl menu item."),
+ NULL);
}
static GList *nullprpl_blist_node_menu(PurpleBlistNode *node) {
purple_debug_info("nullprpl", "providing buddy list context menu item\n");
- if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ if (PURPLE_IS_BUDDY(node)) {
PurpleMenuAction *action = purple_menu_action_new(
_("Nullprpl example menu item"),
PURPLE_CALLBACK(blist_example_menu_item),
@@ -362,7 +367,9 @@ static void nullprpl_login(PurpleAccount *acct)
PurpleConnection *gc = purple_account_get_connection(acct);
GList *offline_messages;
- purple_debug_info("nullprpl", "logging in %s\n", acct->username);
+ purple_debug_info("nullprpl", "logging in %s\n", purple_account_get_username(acct));
+
+ purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_NO_IMAGES);
purple_connection_update_progress(gc, _("Connecting"),
0, /* which connection step this is */
@@ -371,7 +378,7 @@ static void nullprpl_login(PurpleAccount *acct)
purple_connection_update_progress(gc, _("Connected"),
1, /* which connection step this is */
2); /* total number of steps */
- purple_connection_set_state(gc, PURPLE_CONNECTED);
+ purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
/* tell purple about everyone on our buddy list who's connected */
foreach_nullprpl_gc(discover_status, gc, NULL);
@@ -381,13 +388,13 @@ static void nullprpl_login(PurpleAccount *acct)
/* fetch stored offline messages */
purple_debug_info("nullprpl", "checking for offline messages for %s\n",
- acct->username);
- offline_messages = g_hash_table_lookup(goffline_messages, acct->username);
+ purple_account_get_username(acct));
+ 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",
- acct->username, message->message);
- serv_got_im(gc, message->from, message->message, message->flags,
+ purple_account_get_username(acct), message->message);
+ purple_serv_got_im(gc, message->from, message->message, message->flags,
message->mtime);
offline_messages = g_list_next(offline_messages);
@@ -397,7 +404,7 @@ static void nullprpl_login(PurpleAccount *acct)
}
g_list_free(offline_messages);
- g_hash_table_remove(goffline_messages, &acct->username);
+ g_hash_table_remove(goffline_messages, purple_account_get_username(acct));
}
static void nullprpl_close(PurpleConnection *gc)
@@ -406,26 +413,29 @@ static void nullprpl_close(PurpleConnection *gc)
foreach_nullprpl_gc(report_status_change, gc, NULL);
}
-static int nullprpl_send_im(PurpleConnection *gc, const char *who,
- const char *message, PurpleMessageFlags flags)
+static int nullprpl_send_im(PurpleConnection *gc, PurpleMessage *msg)
{
- const char *from_username = gc->account->username;
- PurpleMessageFlags receive_flags = ((flags & ~PURPLE_MESSAGE_SEND)
- | PURPLE_MESSAGE_RECV);
+ const char *from_username = purple_account_get_username(purple_connection_get_account(gc));
+ const gchar *who = purple_message_get_recipient(msg);
+ PurpleMessageFlags receive_flags;
PurpleAccount *to_acct = purple_accounts_find(who, NULLPRPL_ID);
PurpleConnection *to;
+ const gchar *message = purple_message_get_contents(msg);
+
+ receive_flags = ((purple_message_get_flags(msg) & ~PURPLE_MESSAGE_SEND) | PURPLE_MESSAGE_RECV);
purple_debug_info("nullprpl", "sending message from %s to %s: %s\n",
from_username, who, message);
/* is the sender blocked by the recipient's privacy settings? */
- if (to_acct && !purple_privacy_check(to_acct, gc->account->username)) {
+ if (to_acct &&
+ !purple_account_privacy_check(to_acct, purple_account_get_username(purple_connection_get_account(gc)))) {
char *msg = g_strdup_printf(
_("Your message was blocked by %s's privacy settings."), who);
purple_debug_info("nullprpl",
"discarding; %s is blocked by %s's privacy settings\n",
from_username, who);
- purple_conv_present_error(who, gc->account, msg);
+ purple_conversation_present_error(who, purple_connection_get_account(gc), msg);
g_free(msg);
return 0;
}
@@ -433,7 +443,7 @@ static int nullprpl_send_im(PurpleConnection *gc, const char *who,
/* is the recipient online? */
to = get_nullprpl_gc(who);
if (to) { /* yes, send */
- serv_got_im(to, from_username, message, receive_flags, time(NULL));
+ purple_serv_got_im(to, from_username, message, receive_flags, time(NULL));
} else { /* nope, store as an offline message */
GOfflineMessage *offline_message;
@@ -457,35 +467,35 @@ static int nullprpl_send_im(PurpleConnection *gc, const char *who,
static void nullprpl_set_info(PurpleConnection *gc, const char *info) {
purple_debug_info("nullprpl", "setting %s's user info to %s\n",
- gc->account->username, info);
+ purple_account_get_username(purple_connection_get_account(gc)), info);
}
-static const char *typing_state_to_string(PurpleTypingState typing) {
+static const char *typing_state_to_string(PurpleIMTypingState typing) {
switch (typing) {
- case PURPLE_NOT_TYPING: return "is not typing";
- case PURPLE_TYPING: return "is typing";
- case PURPLE_TYPED: return "stopped typing momentarily";
+ case PURPLE_IM_NOT_TYPING: return "is not typing";
+ case PURPLE_IM_TYPING: return "is typing";
+ case PURPLE_IM_TYPED: return "stopped typing momentarily";
default: return "unknown typing state";
}
}
static void notify_typing(PurpleConnection *from, PurpleConnection *to,
gpointer typing) {
- const char *from_username = from->account->username;
- const char *action = typing_state_to_string((PurpleTypingState)typing);
+ const char *from_username = purple_account_get_username(purple_connection_get_account(from));
+ const char *action = typing_state_to_string((PurpleIMTypingState)typing);
purple_debug_info("nullprpl", "notifying %s that %s %s\n",
- to->account->username, from_username, action);
+ purple_account_get_username(purple_connection_get_account(to)), from_username, action);
- serv_got_typing(to,
+ purple_serv_got_typing(to,
from_username,
0, /* if non-zero, a timeout in seconds after which to
- * reset the typing status to PURPLE_NOT_TYPING */
- (PurpleTypingState)typing);
+ * reset the typing status to PURPLE_IM_NOT_TYPING */
+ (PurpleIMTypingState)typing);
}
static unsigned int nullprpl_send_typing(PurpleConnection *gc, const char *name,
- PurpleTypingState typing) {
- purple_debug_info("nullprpl", "%s %s\n", gc->account->username,
+ PurpleIMTypingState typing) {
+ purple_debug_info("nullprpl", "%s %s\n", purple_account_get_username(purple_connection_get_account(gc)),
typing_state_to_string(typing));
foreach_nullprpl_gc(notify_typing, gc, (gpointer)typing);
return 0;
@@ -497,20 +507,24 @@ static void nullprpl_get_info(PurpleConnection *gc, const char *username) {
PurpleAccount *acct;
purple_debug_info("nullprpl", "Fetching %s's user info for %s\n", username,
- gc->account->username);
+ purple_account_get_username(purple_connection_get_account(gc)));
+
+ acct = purple_accounts_find(username, NULLPRPL_ID);
if (!get_nullprpl_gc(username)) {
char *msg = g_strdup_printf(_("%s is not logged in."), username);
- purple_notify_error(gc, _("User Info"), _("User info not available. "), msg);
+ purple_notify_error(gc, _("User Info"), _("User info not available. "), msg,
+ purple_request_cpar_from_account(acct));
g_free(msg);
}
- acct = purple_accounts_find(username, NULLPRPL_ID);
if (acct)
body = purple_account_get_user_info(acct);
else
body = _("No user info.");
- purple_notify_user_info_add_pair(info, "Info", body);
+ /* 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(info, "Info", body);
/* show a buddy's user info in a nice dialog box */
purple_notify_userinfo(gc, /* connection the buddy info came through */
@@ -523,62 +537,62 @@ static void nullprpl_get_info(PurpleConnection *gc, const char *username) {
static void nullprpl_set_status(PurpleAccount *acct, PurpleStatus *status) {
const char *msg = purple_status_get_attr_string(status, "message");
purple_debug_info("nullprpl", "setting %s's status to %s: %s\n",
- acct->username, purple_status_get_name(status), msg);
+ purple_account_get_username(acct), purple_status_get_name(status), msg);
- foreach_nullprpl_gc(report_status_change, get_nullprpl_gc(acct->username),
+ foreach_nullprpl_gc(report_status_change, get_nullprpl_gc(purple_account_get_username(acct)),
NULL);
}
static void nullprpl_set_idle(PurpleConnection *gc, int idletime) {
purple_debug_info("nullprpl",
"purple reports that %s has been idle for %d seconds\n",
- gc->account->username, idletime);
+ purple_account_get_username(purple_connection_get_account(gc)), idletime);
}
static void nullprpl_change_passwd(PurpleConnection *gc, const char *old_pass,
const char *new_pass) {
purple_debug_info("nullprpl", "%s wants to change their password\n",
- gc->account->username);
+ purple_account_get_username(purple_connection_get_account(gc)));
}
static void nullprpl_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
- PurpleGroup *group)
+ PurpleGroup *group, const char *message)
{
- const char *username = gc->account->username;
- PurpleConnection *buddy_gc = get_nullprpl_gc(buddy->name);
+ const char *username = purple_account_get_username(purple_connection_get_account(gc));
+ PurpleConnection *buddy_gc = get_nullprpl_gc(purple_buddy_get_name(buddy));
- purple_debug_info("nullprpl", "adding %s to %s's buddy list\n", buddy->name,
+ purple_debug_info("nullprpl", "adding %s to %s's buddy list\n", purple_buddy_get_name(buddy),
username);
if (buddy_gc) {
- PurpleAccount *buddy_acct = buddy_gc->account;
+ PurpleAccount *buddy_acct = purple_connection_get_account(buddy_gc);
discover_status(gc, buddy_gc, NULL);
- if (purple_find_buddy(buddy_acct, username)) {
+ if (purple_blist_find_buddy(buddy_acct, username)) {
purple_debug_info("nullprpl", "%s is already on %s's buddy list\n",
- username, buddy->name);
+ username, purple_buddy_get_name(buddy));
} else {
purple_debug_info("nullprpl", "asking %s if they want to add %s\n",
- buddy->name, username);
+ purple_buddy_get_name(buddy), username);
purple_account_request_add(buddy_acct,
username,
NULL, /* local account id (rarely used) */
NULL, /* alias */
- NULL); /* message */
+ message); /* message */
}
}
}
static void nullprpl_add_buddies(PurpleConnection *gc, GList *buddies,
- GList *groups) {
+ GList *groups, const char *message) {
GList *buddy = buddies;
GList *group = groups;
purple_debug_info("nullprpl", "adding multiple buddies\n");
while (buddy && group) {
- nullprpl_add_buddy(gc, (PurpleBuddy *)buddy->data, (PurpleGroup *)group->data);
+ nullprpl_add_buddy(gc, (PurpleBuddy *)buddy->data, (PurpleGroup *)group->data, message);
buddy = g_list_next(buddy);
group = g_list_next(group);
}
@@ -588,7 +602,8 @@ static void nullprpl_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
PurpleGroup *group)
{
purple_debug_info("nullprpl", "removing %s from %s's buddy list\n",
- buddy->name, gc->account->username);
+ purple_buddy_get_name(buddy),
+ purple_account_get_username(purple_connection_get_account(gc)));
}
static void nullprpl_remove_buddies(PurpleConnection *gc, GList *buddies,
@@ -614,22 +629,22 @@ static void nullprpl_remove_buddies(PurpleConnection *gc, GList *buddies,
*/
static void nullprpl_add_permit(PurpleConnection *gc, const char *name) {
purple_debug_info("nullprpl", "%s adds %s to their allowed list\n",
- gc->account->username, name);
+ purple_account_get_username(purple_connection_get_account(gc)), name);
}
static void nullprpl_add_deny(PurpleConnection *gc, const char *name) {
purple_debug_info("nullprpl", "%s adds %s to their blocked list\n",
- gc->account->username, name);
+ purple_account_get_username(purple_connection_get_account(gc)), name);
}
static void nullprpl_rem_permit(PurpleConnection *gc, const char *name) {
purple_debug_info("nullprpl", "%s removes %s from their allowed list\n",
- gc->account->username, name);
+ purple_account_get_username(purple_connection_get_account(gc)), name);
}
static void nullprpl_rem_deny(PurpleConnection *gc, const char *name) {
purple_debug_info("nullprpl", "%s removes %s from their blocked list\n",
- gc->account->username, name);
+ purple_account_get_username(purple_connection_get_account(gc)), name);
}
static void nullprpl_set_permit_deny(PurpleConnection *gc) {
@@ -638,37 +653,37 @@ static void nullprpl_set_permit_deny(PurpleConnection *gc) {
*/
}
-static void joined_chat(PurpleConvChat *from, PurpleConvChat *to,
+static void joined_chat(PurpleChatConversation *from, PurpleChatConversation *to,
int id, const char *room, gpointer userdata) {
/* tell their chat window that we joined */
purple_debug_info("nullprpl", "%s sees that %s joined chat room %s\n",
- to->nick, from->nick, room);
- purple_conv_chat_add_user(to,
- from->nick,
+ purple_chat_conversation_get_nick(to), purple_chat_conversation_get_nick(from), room);
+ purple_chat_conversation_add_user(to,
+ purple_chat_conversation_get_nick(from),
NULL, /* user-provided join message, IRC style */
- PURPLE_CBFLAGS_NONE,
+ PURPLE_CHAT_USER_NONE,
TRUE); /* show a join message */
if (from != to) {
/* add them to our chat window */
purple_debug_info("nullprpl", "%s sees that %s is in chat room %s\n",
- from->nick, to->nick, room);
- purple_conv_chat_add_user(from,
- to->nick,
+ purple_chat_conversation_get_nick(from), purple_chat_conversation_get_nick(to), room);
+ purple_chat_conversation_add_user(from,
+ purple_chat_conversation_get_nick(to),
NULL, /* user-provided join message, IRC style */
- PURPLE_CBFLAGS_NONE,
+ PURPLE_CHAT_USER_NONE,
FALSE); /* show a join message */
}
}
static void nullprpl_join_chat(PurpleConnection *gc, GHashTable *components) {
- const char *username = gc->account->username;
+ const char *username = purple_account_get_username(purple_connection_get_account(gc));
const char *room = g_hash_table_lookup(components, "room");
int chat_id = g_str_hash(room);
purple_debug_info("nullprpl", "%s is joining chat room %s\n", username, room);
- if (!purple_find_chat(gc, chat_id)) {
- serv_got_joined_chat(gc, chat_id, room);
+ if (!purple_conversations_find_chat(gc, chat_id)) {
+ purple_serv_got_joined_chat(gc, chat_id, room);
/* tell everyone that we joined, and add them if they're already there */
foreach_gc_in_chat(joined_chat, gc, chat_id, NULL);
@@ -678,7 +693,8 @@ static void nullprpl_join_chat(PurpleConnection *gc, GHashTable *components) {
room);
purple_debug_info("nullprpl", "%s is already in chat room %s\n", username,
room);
- purple_notify_info(gc, _("Join chat"), _("Join chat"), tmp);
+ purple_notify_info(gc, _("Join chat"), _("Join chat"), tmp,
+ purple_request_cpar_from_connection(gc));
g_free(tmp);
}
}
@@ -686,7 +702,7 @@ static void nullprpl_join_chat(PurpleConnection *gc, GHashTable *components) {
static void nullprpl_reject_chat(PurpleConnection *gc, GHashTable *components) {
const char *invited_by = g_hash_table_lookup(components, "invited_by");
const char *room = g_hash_table_lookup(components, "room");
- const char *username = gc->account->username;
+ const char *username = purple_account_get_username(purple_connection_get_account(gc));
PurpleConnection *invited_by_gc = get_nullprpl_gc(invited_by);
char *message = g_strdup_printf(
"%s %s %s.",
@@ -701,7 +717,8 @@ static void nullprpl_reject_chat(PurpleConnection *gc, GHashTable *components) {
purple_notify_info(invited_by_gc,
_("Chat invitation rejected"),
_("Chat invitation rejected"),
- message);
+ message,
+ purple_request_cpar_from_connection(gc));
g_free(message);
}
@@ -713,140 +730,78 @@ static char *nullprpl_get_chat_name(GHashTable *components) {
static void nullprpl_chat_invite(PurpleConnection *gc, int id,
const char *message, const char *who) {
- const char *username = gc->account->username;
- PurpleConversation *conv = purple_find_chat(gc, id);
- const char *room = conv->name;
+ const char *username = purple_account_get_username(purple_connection_get_account(gc));
+ PurpleChatConversation *chat = purple_conversations_find_chat(gc, id);
+ const char *room = purple_conversation_get_name(PURPLE_CONVERSATION(chat));
PurpleAccount *to_acct = purple_accounts_find(who, NULLPRPL_ID);
purple_debug_info("nullprpl", "%s is inviting %s to join chat room %s\n",
username, who, room);
if (to_acct) {
- PurpleConversation *to_conv = purple_find_chat(to_acct->gc, id);
+ PurpleChatConversation *to_conv = purple_conversations_find_chat(purple_account_get_connection(to_acct), id);
if (to_conv) {
char *tmp = g_strdup_printf("%s is already in chat room %s.", who, room);
purple_debug_info("nullprpl",
"%s is already in chat room %s; "
"ignoring invitation from %s\n",
who, room, username);
- purple_notify_info(gc, _("Chat invitation"), _("Chat invitation"), tmp);
+ purple_notify_info(gc, _("Chat invitation"), _("Chat invitation"), tmp,
+ purple_request_cpar_from_conversation(PURPLE_CONVERSATION(to_conv)));
g_free(tmp);
} else {
GHashTable *components;
components = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
g_hash_table_replace(components, "room", g_strdup(room));
g_hash_table_replace(components, "invited_by", g_strdup(username));
- serv_got_chat_invite(to_acct->gc, room, username, message, components);
+ purple_serv_got_chat_invite(purple_account_get_connection(to_acct), room, username, message, components);
}
}
}
-static void left_chat_room(PurpleConvChat *from, PurpleConvChat *to,
+static void left_chat_room(PurpleChatConversation *from, PurpleChatConversation *to,
int id, const char *room, gpointer userdata) {
if (from != to) {
/* tell their chat window that we left */
purple_debug_info("nullprpl", "%s sees that %s left chat room %s\n",
- to->nick, from->nick, room);
- purple_conv_chat_remove_user(to,
- from->nick,
+ purple_chat_conversation_get_nick(to), purple_chat_conversation_get_nick(from), room);
+ purple_chat_conversation_remove_user(to,
+ purple_chat_conversation_get_nick(from),
NULL); /* user-provided message, IRC style */
}
}
static void nullprpl_chat_leave(PurpleConnection *gc, int id) {
- PurpleConversation *conv = purple_find_chat(gc, id);
+ PurpleChatConversation *chat = purple_conversations_find_chat(gc, id);
purple_debug_info("nullprpl", "%s is leaving chat room %s\n",
- gc->account->username, conv->name);
+ purple_account_get_username(purple_connection_get_account(gc)),
+ purple_conversation_get_name(PURPLE_CONVERSATION(chat)));
/* tell everyone that we left */
foreach_gc_in_chat(left_chat_room, gc, id, NULL);
}
-static PurpleCmdRet send_whisper(PurpleConversation *conv, const gchar *cmd,
- gchar **args, gchar **error, void *userdata) {
- const char *to_username;
- const char *message;
- const char *from_username;
- PurpleConvChat *chat;
- PurpleConvChatBuddy *chat_buddy;
- PurpleConnection *to;
-
- /* parse args */
- to_username = args[0];
- message = args[1];
-
- if (!to_username || strlen(to_username) == 0) {
- *error = g_strdup(_("Whisper is missing recipient."));
- return PURPLE_CMD_RET_FAILED;
- } else if (!message || strlen(message) == 0) {
- *error = g_strdup(_("Whisper is missing message."));
- return PURPLE_CMD_RET_FAILED;
- }
-
- from_username = conv->account->username;
- purple_debug_info("nullprpl", "%s whispers to %s in chat room %s: %s\n",
- from_username, to_username, conv->name, message);
-
- chat = purple_conversation_get_chat_data(conv);
- chat_buddy = purple_conv_chat_cb_find(chat, to_username);
- to = get_nullprpl_gc(to_username);
-
- if (!chat_buddy) {
- /* this will be freed by the caller */
- *error = g_strdup_printf(_("%s is not logged in."), to_username);
- return PURPLE_CMD_RET_FAILED;
- } else if (!to) {
- *error = g_strdup_printf(_("%s is not in this chat room."), to_username);
- return PURPLE_CMD_RET_FAILED;
- } else {
- /* write the whisper in the sender's chat window */
- char *message_to = g_strdup_printf("%s (to %s)", message, to_username);
- purple_conv_chat_write(chat, from_username, message_to,
- PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_WHISPER,
- time(NULL));
- g_free(message_to);
-
- /* send the whisper */
- serv_chat_whisper(to, chat->id, from_username, message);
-
- return PURPLE_CMD_RET_OK;
- }
-}
-
-static void nullprpl_chat_whisper(PurpleConnection *gc, int id, const char *who,
- const char *message) {
- const char *username = gc->account->username;
- PurpleConversation *conv = purple_find_chat(gc, id);
- purple_debug_info("nullprpl",
- "%s receives whisper from %s in chat room %s: %s\n",
- username, who, conv->name, message);
-
- /* receive whisper on recipient's account */
- serv_got_chat_in(gc, id, who, PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_WHISPER,
- message, time(NULL));
-}
-
-static void receive_chat_message(PurpleConvChat *from, PurpleConvChat *to,
+static void receive_chat_message(PurpleChatConversation *from, PurpleChatConversation *to,
int id, const char *room, gpointer userdata) {
const char *message = (const char *)userdata;
- PurpleConnection *to_gc = get_nullprpl_gc(to->nick);
+ PurpleConnection *to_gc = get_nullprpl_gc(purple_chat_conversation_get_nick(to));
purple_debug_info("nullprpl",
"%s receives message from %s in chat room %s: %s\n",
- to->nick, from->nick, room, message);
- serv_got_chat_in(to_gc, id, from->nick, PURPLE_MESSAGE_RECV, message,
+ purple_chat_conversation_get_nick(to), purple_chat_conversation_get_nick(from), room, message);
+ purple_serv_got_chat_in(to_gc, id, purple_chat_conversation_get_nick(from), PURPLE_MESSAGE_RECV, message,
time(NULL));
}
-static int nullprpl_chat_send(PurpleConnection *gc, int id, const char *message,
- PurpleMessageFlags flags) {
- const char *username = gc->account->username;
- PurpleConversation *conv = purple_find_chat(gc, id);
+static int nullprpl_chat_send(PurpleConnection *gc, int id, PurpleMessage *msg) {
+ const char *username = purple_account_get_username(purple_connection_get_account(gc));
+ PurpleChatConversation *chat = purple_conversations_find_chat(gc, id);
+ const gchar *message = purple_message_get_contents(msg);
- if (conv) {
+ if (chat) {
purple_debug_info("nullprpl",
"%s is sending message to chat room %s: %s\n", username,
- conv->name, message);
+ purple_conversation_get_name(PURPLE_CONVERSATION(chat)), message);
/* send message to everyone in the chat room */
foreach_gc_in_chat(receive_chat_message, gc, id, (gpointer)message);
@@ -862,14 +817,15 @@ static int nullprpl_chat_send(PurpleConnection *gc, int id, const char *message,
static void nullprpl_register_user(PurpleAccount *acct) {
purple_debug_info("nullprpl", "registering account for %s\n",
- acct->username);
+ purple_account_get_username(acct));
}
static void nullprpl_get_cb_info(PurpleConnection *gc, int id, const char *who) {
- PurpleConversation *conv = purple_find_chat(gc, id);
+ PurpleChatConversation *chat = purple_conversations_find_chat(gc, id);
purple_debug_info("nullprpl",
"retrieving %s's info for %s in chat room %s\n", who,
- gc->account->username, conv->name);
+ purple_account_get_username(purple_connection_get_account(gc)),
+ purple_conversation_get_name(PURPLE_CONVERSATION(chat)));
nullprpl_get_info(gc, who);
}
@@ -877,25 +833,26 @@ static void nullprpl_get_cb_info(PurpleConnection *gc, int id, const char *who)
static void nullprpl_alias_buddy(PurpleConnection *gc, const char *who,
const char *alias) {
purple_debug_info("nullprpl", "%s sets %s's alias to %s\n",
- gc->account->username, who, alias);
+ purple_account_get_username(purple_connection_get_account(gc)), who, alias);
}
static void nullprpl_group_buddy(PurpleConnection *gc, const char *who,
const char *old_group,
const char *new_group) {
purple_debug_info("nullprpl", "%s has moved %s from group %s to group %s\n",
- gc->account->username, who, old_group, new_group);
+ purple_account_get_username(purple_connection_get_account(gc)), who, old_group, new_group);
}
static void nullprpl_rename_group(PurpleConnection *gc, const char *old_name,
PurpleGroup *group, GList *moved_buddies) {
purple_debug_info("nullprpl", "%s has renamed group %s to %s\n",
- gc->account->username, old_name, group->name);
+ purple_account_get_username(purple_connection_get_account(gc)), old_name,
+ purple_group_get_name(group));
}
static void nullprpl_convo_closed(PurpleConnection *gc, const char *who) {
purple_debug_info("nullprpl", "%s's conversation with %s was closed\n",
- gc->account->username, who);
+ purple_account_get_username(purple_connection_get_account(gc)), who);
}
/* normalize a username (e.g. remove whitespace, add default domain, etc.)
@@ -906,51 +863,51 @@ static const char *nullprpl_normalize(const PurpleAccount *acct,
return NULL;
}
-static void nullprpl_set_buddy_icon(PurpleConnection *gc,
- PurpleStoredImage *img) {
- purple_debug_info("nullprpl", "setting %s's buddy icon to %s\n",
- gc->account->username,
- img ? purple_imgstore_get_filename(img) : "(null)");
+static void
+nullprpl_set_buddy_icon(PurpleConnection *gc, 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_image_get_path(img) : "(null)");
}
static void nullprpl_remove_group(PurpleConnection *gc, PurpleGroup *group) {
purple_debug_info("nullprpl", "%s has removed group %s\n",
- gc->account->username, group->name);
+ purple_account_get_username(purple_connection_get_account(gc)),
+ purple_group_get_name(group));
}
-static void set_chat_topic_fn(PurpleConvChat *from, PurpleConvChat *to,
+static void set_chat_topic_fn(PurpleChatConversation *from, PurpleChatConversation *to,
int id, const char *room, gpointer userdata) {
const char *topic = (const char *)userdata;
- const char *username = from->conv->account->username;
+ const char *username = purple_account_get_username(purple_conversation_get_account(PURPLE_CONVERSATION(from)));
char *msg;
- purple_conv_chat_set_topic(to, username, topic);
+ purple_chat_conversation_set_topic(to, username, topic);
- if (topic && strlen(topic) > 0)
+ if (topic && *topic)
msg = g_strdup_printf(_("%s sets topic to: %s"), username, topic);
else
msg = g_strdup_printf(_("%s clears topic"), username);
- purple_conv_chat_write(to, username, msg,
- PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG,
- time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(to),
+ msg, PURPLE_MESSAGE_NO_LOG);
g_free(msg);
}
static void nullprpl_set_chat_topic(PurpleConnection *gc, int id,
const char *topic) {
- PurpleConversation *conv = purple_find_chat(gc, id);
- PurpleConvChat *chat = purple_conversation_get_chat_data(conv);
+ PurpleChatConversation *chat = purple_conversations_find_chat(gc, id);
const char *last_topic;
if (!chat)
return;
purple_debug_info("nullprpl", "%s sets topic of chat room '%s' to '%s'\n",
- gc->account->username, conv->name, topic);
+ purple_account_get_username(purple_connection_get_account(gc)),
+ purple_conversation_get_name(PURPLE_CONVERSATION(chat)), topic);
- last_topic = purple_conv_chat_get_topic(chat);
+ last_topic = purple_chat_conversation_get_topic(chat);
if ((!topic && !last_topic) ||
(topic && last_topic && !strcmp(topic, last_topic)))
return; /* topic is unchanged, this is a noop */
@@ -959,13 +916,15 @@ static void nullprpl_set_chat_topic(PurpleConnection *gc, int id,
}
static gboolean nullprpl_finish_get_roomlist(gpointer roomlist) {
- purple_roomlist_set_in_progress((PurpleRoomlist *)roomlist, FALSE);
+ purple_roomlist_set_in_progress(PURPLE_ROOMLIST(roomlist), FALSE);
+ g_object_unref(roomlist);
+
return FALSE;
}
static PurpleRoomlist *nullprpl_roomlist_get_list(PurpleConnection *gc) {
- const char *username = gc->account->username;
- PurpleRoomlist *roomlist = purple_roomlist_new(gc->account);
+ const char *username = purple_account_get_username(purple_connection_get_account(gc));
+ PurpleRoomlist *roomlist = purple_roomlist_new(purple_connection_get_account(gc));
GList *fields = NULL;
PurpleRoomlistField *field;
GList *chats;
@@ -985,11 +944,11 @@ static PurpleRoomlist *nullprpl_roomlist_get_list(PurpleConnection *gc) {
/* add each chat room. the chat ids are cached in seen_ids so that each room
* is only returned once, even if multiple users are in it. */
- for (chats = purple_get_chats(); chats; chats = g_list_next(chats)) {
- PurpleConversation *conv = (PurpleConversation *)chats->data;
+ for (chats = purple_conversations_get_chats(); chats; chats = g_list_next(chats)) {
+ PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(chats->data);
PurpleRoomlistRoom *room;
- const char *name = conv->name;
- int id = purple_conversation_get_chat_data(conv)->id;
+ const char *name = purple_conversation_get_name(PURPLE_CONVERSATION(chat));
+ int id = purple_chat_conversation_get_id(chat);
/* have we already added this room? */
if (g_list_find_custom(seen_ids, name, (GCompareFunc)strcmp))
@@ -1008,19 +967,22 @@ static PurpleRoomlist *nullprpl_roomlist_get_list(PurpleConnection *gc) {
}
g_list_free(seen_ids);
- purple_timeout_add(1 /* ms */, nullprpl_finish_get_roomlist, roomlist);
+ purple_timeout_add(1 /* ms */, nullprpl_finish_get_roomlist, g_object_ref(roomlist));
return roomlist;
}
static void nullprpl_roomlist_cancel(PurpleRoomlist *list) {
+ PurpleAccount *account = purple_roomlist_get_account(list);
purple_debug_info("nullprpl", "%s asked to cancel room list request\n",
- list->account->username);
+ purple_account_get_username(account));
}
static void nullprpl_roomlist_expand_category(PurpleRoomlist *list,
PurpleRoomlistRoom *category) {
+ PurpleAccount *account = purple_roomlist_get_account(list);
purple_debug_info("nullprpl", "%s asked to expand room list category %s\n",
- list->account->username, category->name);
+ purple_account_get_username(account),
+ purple_roomlist_room_get_name(category));
}
/* nullprpl doesn't support file transfer...yet... */
@@ -1032,7 +994,7 @@ static gboolean nullprpl_can_receive_file(PurpleConnection *gc,
static gboolean nullprpl_offline_message(const PurpleBuddy *buddy) {
purple_debug_info("nullprpl",
"reporting that offline messages are supported for %s\n",
- buddy->name);
+ purple_buddy_get_name(buddy));
return TRUE;
}
@@ -1043,6 +1005,7 @@ static gboolean nullprpl_offline_message(const PurpleBuddy *buddy) {
static PurplePluginProtocolInfo prpl_info =
{
+ sizeof(PurplePluginProtocolInfo), /* struct_size */
OPT_PROTO_NO_PASSWORD | OPT_PROTO_CHAT_TOPIC, /* options */
NULL, /* user_splits, initialized in nullprpl_init() */
NULL, /* protocol_options, initialized in nullprpl_init() */
@@ -1086,12 +1049,10 @@ static PurplePluginProtocolInfo prpl_info =
nullprpl_get_chat_name, /* get_chat_name */
nullprpl_chat_invite, /* chat_invite */
nullprpl_chat_leave, /* chat_leave */
- nullprpl_chat_whisper, /* chat_whisper */
nullprpl_chat_send, /* chat_send */
NULL, /* keepalive */
nullprpl_register_user, /* register_user */
nullprpl_get_cb_info, /* get_cb_info */
- NULL, /* get_cb_away */
nullprpl_alias_buddy, /* alias_buddy */
nullprpl_group_buddy, /* group_buddy */
nullprpl_rename_group, /* rename_group */
@@ -1116,15 +1077,13 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* unregister_user */
NULL, /* send_attention */
NULL, /* get_attention_types */
- sizeof(PurplePluginProtocolInfo), /* struct_size */
NULL, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* get_media_caps */
NULL, /* get_moods */
NULL, /* set_public_alias */
NULL, /* get_public_alias */
- NULL, /* add_buddy_with_invite */
- NULL /* add_buddies_with_invite */
+ NULL /* get_max_message_size */
};
static void nullprpl_init(PurplePlugin *plugin)
@@ -1144,16 +1103,6 @@ static void nullprpl_init(PurplePlugin *plugin)
prpl_info.user_splits = g_list_append(NULL, split);
prpl_info.protocol_options = g_list_append(NULL, option);
- /* register whisper chat command, /msg */
- purple_cmd_register("msg",
- "ws", /* args: recipient and message */
- PURPLE_CMD_P_DEFAULT, /* priority */
- PURPLE_CMD_FLAG_CHAT,
- "prpl-null",
- send_whisper,
- "msg &lt;username&gt; &lt;message&gt;: send a private message, aka a whisper",
- NULL); /* userdata */
-
/* get ready to store offline messages */
goffline_messages = g_hash_table_new_full(g_str_hash, /* hash fn */
g_str_equal, /* key comparison fn */
diff --git a/libpurple/protocols/oscar/Makefile.am b/libpurple/protocols/oscar/Makefile.am
index fc78e47b0c..be192f66a1 100644
--- a/libpurple/protocols/oscar/Makefile.am
+++ b/libpurple/protocols/oscar/Makefile.am
@@ -3,7 +3,7 @@ EXTRA_DIST = \
AUTHORS \
Makefile.mingw
-pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+pkgdir = @PURPLE_PLUGINDIR@
OSCARSOURCES = \
authorization.c \
@@ -50,21 +50,32 @@ OSCARSOURCES = \
AM_CFLAGS = $(st)
-libaim_la_LDFLAGS = -module -avoid-version
-libicq_la_LDFLAGS = -module -avoid-version
+libaim_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+libicq_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
if STATIC_OSCAR
st = -DPURPLE_STATIC_PRPL
noinst_LTLIBRARIES = liboscar.la
liboscar_la_SOURCES = $(OSCARSOURCES) libaim.c libicq.c
liboscar_la_CFLAGS = $(AM_CFLAGS)
+liboscar_la_LIBADD = @PURPLE_LIBS@
+liboscar_la_LDFLAGS = @PLUGIN_LDFLAGS@
else
+if IS_WIN32
+lib_LTLIBRARIES = liboscar.la
+pkg_LTLIBRARIES =
+else
+pkg_LTLIBRARIES = liboscar.la
+endif
+
st =
-pkg_LTLIBRARIES = liboscar.la libaim.la libicq.la
+pkg_LTLIBRARIES += libaim.la libicq.la
+
liboscar_la_SOURCES = $(OSCARSOURCES)
-liboscar_la_LIBADD = $(GLIB_LIBS)
+liboscar_la_LIBADD = @PURPLE_LIBS@
+liboscar_la_LDFLAGS = @PLUGIN_LDFLAGS@
libaim_la_SOURCES = libaim.c
libaim_la_LIBADD = liboscar.la
diff --git a/libpurple/protocols/oscar/Makefile.mingw b/libpurple/protocols/oscar/Makefile.mingw
index fa70dd433f..09b62cf82b 100644
--- a/libpurple/protocols/oscar/Makefile.mingw
+++ b/libpurple/protocols/oscar/Makefile.mingw
@@ -90,6 +90,7 @@ ICQ_OBJECTS = $(ICQ_C_SRC:%.c=%.o)
##
LIBS = \
-lglib-2.0 \
+ -lgobject-2.0 \
-lintl \
-lws2_32 \
-lpurple
diff --git a/libpurple/protocols/oscar/authorization.c b/libpurple/protocols/oscar/authorization.c
index 29b285d58c..22a43b65b4 100644
--- a/libpurple/protocols/oscar/authorization.c
+++ b/libpurple/protocols/oscar/authorization.c
@@ -37,7 +37,7 @@ oscar_auth_sendrequest(PurpleConnection *gc, const char *bname, const char *msg)
od = purple_connection_get_protocol_data(gc);
account = purple_connection_get_account(gc);
- buddy = purple_find_buddy(account, bname);
+ buddy = purple_blist_find_buddy(account, bname);
if (buddy != NULL)
group = purple_buddy_get_group(buddy);
else
@@ -49,7 +49,7 @@ oscar_auth_sendrequest(PurpleConnection *gc, const char *bname, const char *msg)
purple_debug_info("oscar", "ssi: adding buddy %s to group %s\n",
bname, gname);
aim_ssi_sendauthrequest(od, bname, msg ? msg : _("Please authorize me so I can add you to my buddy list."));
- if (!aim_ssi_itemlist_finditem(od->ssi.local, gname, bname, AIM_SSI_TYPE_BUDDY))
+ if (!aim_ssi_itemlist_finditem(&od->ssi.local, gname, bname, AIM_SSI_TYPE_BUDDY))
{
aim_ssi_addbuddy(od, bname, gname, NULL, purple_buddy_get_alias_only(buddy), NULL, NULL, TRUE);
@@ -67,7 +67,7 @@ oscar_auth_sendrequest(PurpleConnection *gc, const char *bname, const char *msg)
}
static void
-oscar_auth_grant(gpointer cbdata)
+oscar_auth_grant(const char *message, gpointer cbdata)
{
struct name_data *data = cbdata;
PurpleConnection *gc = data->gc;
@@ -79,8 +79,9 @@ oscar_auth_grant(gpointer cbdata)
}
static void
-oscar_auth_dontgrant(struct name_data *data, char *msg)
+oscar_auth_dontgrant(const char *msg, gpointer cbdata)
{
+ struct name_data *data = cbdata;
PurpleConnection *gc = data->gc;
OscarData *od = purple_connection_get_protocol_data(gc);
@@ -89,25 +90,13 @@ oscar_auth_dontgrant(struct name_data *data, char *msg)
oscar_free_name_data(data);
}
-static void
-oscar_auth_dontgrant_msgprompt(gpointer cbdata)
-{
- struct name_data *data = cbdata;
- purple_request_input(data->gc, NULL, _("Authorization Denied Message:"),
- NULL, _("No reason given."), TRUE, FALSE, NULL,
- _("_OK"), G_CALLBACK(oscar_auth_dontgrant),
- _("_Cancel"), G_CALLBACK(oscar_free_name_data),
- purple_connection_get_account(data->gc), data->name, NULL,
- data);
-}
-
void
oscar_auth_sendrequest_menu(PurpleBlistNode *node, gpointer ignored)
{
PurpleBuddy *buddy;
PurpleConnection *gc;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *) node;
gc = purple_account_get_connection(purple_buddy_get_account(buddy));
@@ -126,6 +115,6 @@ oscar_auth_recvrequest(PurpleConnection *gc, gchar *name, gchar *nick, gchar *re
data->nick = nick;
purple_account_request_authorization(account, data->name, NULL, data->nick,
- reason, purple_find_buddy(account, data->name) != NULL,
- oscar_auth_grant, oscar_auth_dontgrant_msgprompt, data);
+ reason, purple_blist_find_buddy(account, data->name) != NULL,
+ oscar_auth_grant, oscar_auth_dontgrant, data);
}
diff --git a/libpurple/protocols/oscar/bstream.c b/libpurple/protocols/oscar/bstream.c
index 31acac2724..e58940fc22 100644
--- a/libpurple/protocols/oscar/bstream.c
+++ b/libpurple/protocols/oscar/bstream.c
@@ -49,8 +49,11 @@ void byte_stream_destroy(ByteStream *bs)
g_free(bs->data);
}
-int byte_stream_bytes_left(ByteStream *bs)
+size_t byte_stream_bytes_left(ByteStream *bs)
{
+ g_return_val_if_fail(bs != NULL, 0);
+ g_return_val_if_fail(bs->len >= bs->offset, 0);
+
return bs->len - bs->offset;
}
@@ -79,7 +82,7 @@ void byte_stream_rewind(ByteStream *bs)
int byte_stream_advance(ByteStream *bs, int n)
{
g_return_val_if_fail(byte_stream_curpos(bs) + n >= 0, 0);
- g_return_val_if_fail(n <= byte_stream_bytes_left(bs), 0);
+ g_return_val_if_fail((gsize)n <= byte_stream_bytes_left(bs), 0);
bs->offset += n;
return n;
diff --git a/libpurple/protocols/oscar/clientlogin.c b/libpurple/protocols/oscar/clientlogin.c
index a6e37fc2df..6409dc9719 100644
--- a/libpurple/protocols/oscar/clientlogin.c
+++ b/libpurple/protocols/oscar/clientlogin.c
@@ -38,10 +38,11 @@
#include "oscar.h"
#include "oscarcommon.h"
-
-#include "cipher.h"
#include "core.h"
+#include "ciphers/hmaccipher.h"
+#include "ciphers/sha256hash.h"
+
#define AIM_LOGIN_HOST "api.screenname.aol.com"
#define ICQ_LOGIN_HOST "api.login.icq.net"
@@ -90,26 +91,26 @@ static const char *get_client_key(OscarData *od)
DEFAULT_CLIENT_KEY);
}
-static gchar *generate_error_message(xmlnode *resp, const char *url)
+static gchar *generate_error_message(PurpleXmlNode *resp, const char *url)
{
- xmlnode *text;
- xmlnode *status_code_node;
+ PurpleXmlNode *text;
+ PurpleXmlNode *status_code_node;
gchar *status_code;
gboolean have_error_code = TRUE;
gchar *err = NULL;
gchar *details = NULL;
- status_code_node = xmlnode_get_child(resp, "statusCode");
+ status_code_node = purple_xmlnode_get_child(resp, "statusCode");
if (status_code_node) {
/* We can get 200 OK here if the server omitted something we think it shouldn't have (see #12783).
* No point in showing the "Ok" string to the user.
*/
- if ((status_code = xmlnode_get_data_unescaped(status_code_node)) && strcmp(status_code, "200") == 0) {
+ if ((status_code = purple_xmlnode_get_data_unescaped(status_code_node)) && strcmp(status_code, "200") == 0) {
have_error_code = FALSE;
}
}
- if (have_error_code && resp && (text = xmlnode_get_child(resp, "statusText"))) {
- details = xmlnode_get_data(text);
+ if (have_error_code && resp && (text = purple_xmlnode_get_child(resp, "statusText"))) {
+ details = purple_xmlnode_get_data(text);
}
if (details && *details) {
@@ -128,15 +129,17 @@ static gchar *generate_error_message(xmlnode *resp, const char *url)
*/
static gchar *hmac_sha256(const char *key, const char *message)
{
- PurpleCipherContext *context;
+ PurpleCipher *cipher;
+ PurpleHash *hash;
guchar digest[32];
- context = purple_cipher_context_new_by_name("hmac", NULL);
- purple_cipher_context_set_option(context, "hash", "sha256");
- purple_cipher_context_set_key(context, (guchar *)key);
- purple_cipher_context_append(context, (guchar *)message, strlen(message));
- purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
- purple_cipher_context_destroy(context);
+ hash = purple_sha256_hash_new();
+ cipher = purple_hmac_cipher_new(hash);
+ purple_cipher_set_key(cipher, (guchar *)key, strlen(key));
+ purple_cipher_append(cipher, (guchar *)message, strlen(message));
+ purple_cipher_digest(cipher, digest, sizeof(digest));
+ g_object_unref(cipher);
+ g_object_unref(hash);
return purple_base64_encode(digest, sizeof(digest));
}
@@ -166,14 +169,14 @@ static gchar *generate_signature(const char *method, const char *url, const char
static gboolean parse_start_oscar_session_response(PurpleConnection *gc, const gchar *response, gsize response_len, char **host, unsigned short *port, char **cookie, char **tls_certname)
{
OscarData *od = purple_connection_get_protocol_data(gc);
- xmlnode *response_node, *tmp_node, *data_node;
- xmlnode *host_node = NULL, *port_node = NULL, *cookie_node = NULL, *tls_node = NULL;
+ PurpleXmlNode *response_node, *tmp_node, *data_node;
+ PurpleXmlNode *host_node = NULL, *port_node = NULL, *cookie_node = NULL, *tls_node = NULL;
char *tmp;
guint code;
const gchar *encryption_type = purple_account_get_string(purple_connection_get_account(gc), "encryption", OSCAR_DEFAULT_ENCRYPTION);
/* Parse the response as XML */
- response_node = xmlnode_from_str(response, response_len);
+ response_node = purple_xmlnode_from_str(response, response_len);
if (response_node == NULL)
{
char *msg;
@@ -182,32 +185,32 @@ static gboolean parse_start_oscar_session_response(PurpleConnection *gc, const g
/* Note to translators: %s in this string is a URL */
msg = generate_error_message(response_node,
get_start_oscar_session_url(od));
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
g_free(msg);
return FALSE;
}
/* Grab the necessary XML nodes */
- tmp_node = xmlnode_get_child(response_node, "statusCode");
- data_node = xmlnode_get_child(response_node, "data");
+ tmp_node = purple_xmlnode_get_child(response_node, "statusCode");
+ data_node = purple_xmlnode_get_child(response_node, "data");
if (data_node != NULL) {
- host_node = xmlnode_get_child(data_node, "host");
- port_node = xmlnode_get_child(data_node, "port");
- cookie_node = xmlnode_get_child(data_node, "cookie");
+ host_node = purple_xmlnode_get_child(data_node, "host");
+ port_node = purple_xmlnode_get_child(data_node, "port");
+ cookie_node = purple_xmlnode_get_child(data_node, "cookie");
}
/* Make sure we have a status code */
- if (tmp_node == NULL || (tmp = xmlnode_get_data_unescaped(tmp_node)) == NULL) {
+ if (tmp_node == NULL || (tmp = purple_xmlnode_get_data_unescaped(tmp_node)) == NULL) {
char *msg;
purple_debug_error("oscar", "startOSCARSession response was "
"missing statusCode: %s\n", response);
msg = generate_error_message(response_node,
get_start_oscar_session_url(od));
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
g_free(msg);
- xmlnode_free(response_node);
+ purple_xmlnode_free(response_node);
return FALSE;
}
@@ -215,13 +218,13 @@ static gboolean parse_start_oscar_session_response(PurpleConnection *gc, const g
code = atoi(tmp);
if (code != 200)
{
- xmlnode *status_detail_node;
+ PurpleXmlNode *status_detail_node;
guint status_detail = 0;
- status_detail_node = xmlnode_get_child(response_node,
+ status_detail_node = purple_xmlnode_get_child(response_node,
"statusDetailCode");
if (status_detail_node) {
- gchar *data = xmlnode_get_data(status_detail_node);
+ gchar *data = purple_xmlnode_get_data(status_detail_node);
if (data) {
status_detail = atoi(data);
g_free(data);
@@ -232,7 +235,7 @@ static gboolean parse_start_oscar_session_response(PurpleConnection *gc, const g
"was %s: %s\n", tmp, response);
if ((code == 401 && status_detail != 1014) || code == 607)
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_OTHER_ERROR,
_("You have been connecting and disconnecting too "
"frequently. Wait ten minutes and try again. If "
@@ -242,13 +245,13 @@ static gboolean parse_start_oscar_session_response(PurpleConnection *gc, const g
char *msg;
msg = generate_error_message(response_node,
get_start_oscar_session_url(od));
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_OTHER_ERROR, msg);
g_free(msg);
}
g_free(tmp);
- xmlnode_free(response_node);
+ purple_xmlnode_free(response_node);
return FALSE;
}
g_free(tmp);
@@ -261,36 +264,36 @@ static gboolean parse_start_oscar_session_response(PurpleConnection *gc, const g
"something: %s\n", response);
msg = generate_error_message(response_node,
get_start_oscar_session_url(od));
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
g_free(msg);
- xmlnode_free(response_node);
+ purple_xmlnode_free(response_node);
return FALSE;
}
if (strcmp(encryption_type, OSCAR_NO_ENCRYPTION) != 0) {
- tls_node = xmlnode_get_child(data_node, "tlsCertName");
+ tls_node = purple_xmlnode_get_child(data_node, "tlsCertName");
if (tls_node != NULL) {
- *tls_certname = xmlnode_get_data_unescaped(tls_node);
+ *tls_certname = purple_xmlnode_get_data_unescaped(tls_node);
} else {
if (strcmp(encryption_type, OSCAR_OPPORTUNISTIC_ENCRYPTION) == 0) {
purple_debug_warning("oscar", "We haven't received a tlsCertName to use. We will not do SSL to BOS.\n");
} else {
purple_debug_error("oscar", "startOSCARSession was missing tlsCertName: %s\n", response);
- purple_connection_error_reason(
+ purple_connection_error(
gc,
PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
_("You required encryption in your account settings, but one of the servers doesn't support it."));
- xmlnode_free(response_node);
+ purple_xmlnode_free(response_node);
return FALSE;
}
}
}
/* Extract data from the XML */
- *host = xmlnode_get_data_unescaped(host_node);
- tmp = xmlnode_get_data_unescaped(port_node);
- *cookie = xmlnode_get_data_unescaped(cookie_node);
+ *host = purple_xmlnode_get_data_unescaped(host_node);
+ tmp = purple_xmlnode_get_data_unescaped(port_node);
+ *cookie = purple_xmlnode_get_data_unescaped(cookie_node);
if (*host == NULL || **host == '\0' || tmp == NULL || *tmp == '\0' || *cookie == NULL || **cookie == '\0')
{
@@ -299,13 +302,13 @@ static gboolean parse_start_oscar_session_response(PurpleConnection *gc, const g
"something: %s\n", response);
msg = generate_error_message(response_node,
get_start_oscar_session_url(od));
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
g_free(msg);
g_free(*host);
g_free(tmp);
g_free(*cookie);
- xmlnode_free(response_node);
+ purple_xmlnode_free(response_node);
return FALSE;
}
@@ -315,35 +318,39 @@ static gboolean parse_start_oscar_session_response(PurpleConnection *gc, const g
return TRUE;
}
-static void start_oscar_session_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
+static void
+start_oscar_session_cb(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _od)
{
- OscarData *od;
+ OscarData *od = _od;
PurpleConnection *gc;
char *host, *cookie;
char *tls_certname = NULL;
unsigned short port;
guint8 *cookiedata;
gsize cookiedata_len = 0;
+ const gchar *got_data;
+ size_t got_len;
- od = user_data;
gc = od->gc;
- od->url_data = NULL;
+ od->hc = NULL;
- if (error_message != NULL || len == 0) {
+ if (!purple_http_response_is_successful(response)) {
gchar *tmp;
/* Note to translators: The first %s is a URL, the second is an
error message. */
tmp = g_strdup_printf(_("Error requesting %s: %s"),
- get_start_oscar_session_url(od), error_message ?
- error_message : _("The server returned an empty response"));
- purple_connection_error_reason(gc,
+ get_start_oscar_session_url(od),
+ purple_http_response_get_error(response));
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
g_free(tmp);
return;
}
- if (!parse_start_oscar_session_response(gc, url_text, len, &host, &port, &cookie, &tls_certname))
+ got_data = purple_http_response_get_data(response, &got_len);
+ if (!parse_start_oscar_session_response(gc, got_data, got_len, &host, &port, &cookie, &tls_certname))
return;
cookiedata = purple_base64_decode(cookie, &cookiedata_len);
@@ -357,7 +364,7 @@ static void start_oscar_session_cb(PurpleUtilFetchUrlData *url_data, gpointer us
static void send_start_oscar_session(OscarData *od, const char *token, const char *session_key, time_t hosttime)
{
- char *query_string, *signature, *url;
+ char *query_string, *signature;
PurpleAccount *account = purple_connection_get_account(od->gc);
const gchar *encryption_type = purple_account_get_string(account, "encryption", OSCAR_DEFAULT_ENCRYPTION);
@@ -378,16 +385,13 @@ static void send_start_oscar_session(OscarData *od, const char *token, const cha
strcmp(encryption_type, OSCAR_NO_ENCRYPTION) != 0 ? 1 : 0);
signature = generate_signature("GET", get_start_oscar_session_url(od),
query_string, session_key);
- url = g_strdup_printf("%s?%s&sig_sha256=%s", get_start_oscar_session_url(od),
- query_string, signature);
+
+ od->hc = purple_http_get_printf(od->gc, start_oscar_session_cb, od,
+ "%s?%s&sig_sha256=%s", get_start_oscar_session_url(od),
+ query_string, signature);
+
g_free(query_string);
g_free(signature);
-
- /* Make the request */
- od->url_data = purple_util_fetch_url_request_len_with_account(account,
- url, TRUE, NULL, FALSE, NULL, FALSE, -1,
- start_oscar_session_cb, od);
- g_free(url);
}
/**
@@ -395,7 +399,7 @@ static void send_start_oscar_session(OscarData *od, const char *token, const cha
* and extracts the useful information.
*
* @param gc The PurpleConnection. If the response data does
- * not indicate then purple_connection_error_reason()
+ * not indicate then purple_connection_error()
* will be called to close this connection.
* @param response The response data from the clientLogin request.
* @param response_len The length of the above response, or -1 if
@@ -418,12 +422,12 @@ static void send_start_oscar_session(OscarData *od, const char *token, const cha
static gboolean parse_client_login_response(PurpleConnection *gc, const gchar *response, gsize response_len, char **token, char **secret, time_t *hosttime)
{
OscarData *od = purple_connection_get_protocol_data(gc);
- xmlnode *response_node, *tmp_node, *data_node;
- xmlnode *secret_node = NULL, *hosttime_node = NULL, *token_node = NULL, *tokena_node = NULL;
+ PurpleXmlNode *response_node, *tmp_node, *data_node;
+ PurpleXmlNode *secret_node = NULL, *hosttime_node = NULL, *token_node = NULL, *tokena_node = NULL;
char *tmp;
/* Parse the response as XML */
- response_node = xmlnode_from_str(response, response_len);
+ response_node = purple_xmlnode_from_str(response, response_len);
if (response_node == NULL)
{
char *msg;
@@ -431,34 +435,34 @@ static gboolean parse_client_login_response(PurpleConnection *gc, const gchar *r
"response as XML: %s\n", response);
msg = generate_error_message(response_node,
get_client_login_url(od));
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
g_free(msg);
return FALSE;
}
/* Grab the necessary XML nodes */
- tmp_node = xmlnode_get_child(response_node, "statusCode");
- data_node = xmlnode_get_child(response_node, "data");
+ tmp_node = purple_xmlnode_get_child(response_node, "statusCode");
+ data_node = purple_xmlnode_get_child(response_node, "data");
if (data_node != NULL) {
- secret_node = xmlnode_get_child(data_node, "sessionSecret");
- hosttime_node = xmlnode_get_child(data_node, "hostTime");
- token_node = xmlnode_get_child(data_node, "token");
+ secret_node = purple_xmlnode_get_child(data_node, "sessionSecret");
+ hosttime_node = purple_xmlnode_get_child(data_node, "hostTime");
+ token_node = purple_xmlnode_get_child(data_node, "token");
if (token_node != NULL)
- tokena_node = xmlnode_get_child(token_node, "a");
+ tokena_node = purple_xmlnode_get_child(token_node, "a");
}
/* Make sure we have a status code */
- if (tmp_node == NULL || (tmp = xmlnode_get_data_unescaped(tmp_node)) == NULL) {
+ if (tmp_node == NULL || (tmp = purple_xmlnode_get_data_unescaped(tmp_node)) == NULL) {
char *msg;
purple_debug_error("oscar", "clientLogin response was "
"missing statusCode: %s\n", response);
msg = generate_error_message(response_node,
get_client_login_url(od));
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
g_free(msg);
- xmlnode_free(response_node);
+ purple_xmlnode_free(response_node);
return FALSE;
}
@@ -469,8 +473,8 @@ static gboolean parse_client_login_response(PurpleConnection *gc, const gchar *r
status_code = atoi(tmp);
g_free(tmp);
- tmp_node = xmlnode_get_child(response_node, "statusDetailCode");
- if (tmp_node != NULL && (tmp = xmlnode_get_data_unescaped(tmp_node)) != NULL) {
+ tmp_node = purple_xmlnode_get_child(response_node, "statusDetailCode");
+ if (tmp_node != NULL && (tmp = purple_xmlnode_get_data_unescaped(tmp_node)) != NULL) {
status_detail_code = atoi(tmp);
g_free(tmp);
}
@@ -481,29 +485,29 @@ static gboolean parse_client_login_response(PurpleConnection *gc, const gchar *r
if (status_code == 330 && status_detail_code == 3011) {
PurpleAccount *account = purple_connection_get_account(gc);
if (!purple_account_get_remember_password(account))
- purple_account_set_password(account, NULL);
- purple_connection_error_reason(gc,
+ purple_account_set_password(account, NULL, NULL, NULL);
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
_("Incorrect password"));
} else if (status_code == 330 && status_detail_code == 3015) {
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
_("Server requested that you fill out a CAPTCHA in order to "
"sign in, but this client does not currently support CAPTCHAs."));
} else if (status_code == 401 && status_detail_code == 3019) {
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_OTHER_ERROR,
_("AOL does not allow your screen name to authenticate here"));
} else {
char *msg;
msg = generate_error_message(response_node,
get_client_login_url(od));
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_OTHER_ERROR, msg);
g_free(msg);
}
- xmlnode_free(response_node);
+ purple_xmlnode_free(response_node);
return FALSE;
}
g_free(tmp);
@@ -517,17 +521,17 @@ static gboolean parse_client_login_response(PurpleConnection *gc, const gchar *r
"something: %s\n", response);
msg = generate_error_message(response_node,
get_client_login_url(od));
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
g_free(msg);
- xmlnode_free(response_node);
+ purple_xmlnode_free(response_node);
return FALSE;
}
/* Extract data from the XML */
- *token = xmlnode_get_data_unescaped(tokena_node);
- *secret = xmlnode_get_data_unescaped(secret_node);
- tmp = xmlnode_get_data_unescaped(hosttime_node);
+ *token = purple_xmlnode_get_data_unescaped(tokena_node);
+ *secret = purple_xmlnode_get_data_unescaped(secret_node);
+ tmp = purple_xmlnode_get_data_unescaped(hosttime_node);
if (*token == NULL || **token == '\0' || *secret == NULL || **secret == '\0' || tmp == NULL || *tmp == '\0')
{
char *msg;
@@ -535,51 +539,58 @@ static gboolean parse_client_login_response(PurpleConnection *gc, const gchar *r
"something: %s\n", response);
msg = generate_error_message(response_node,
get_client_login_url(od));
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
g_free(msg);
g_free(*token);
g_free(*secret);
g_free(tmp);
- xmlnode_free(response_node);
+ purple_xmlnode_free(response_node);
return FALSE;
}
*hosttime = strtol(tmp, NULL, 10);
g_free(tmp);
- xmlnode_free(response_node);
+ purple_xmlnode_free(response_node);
return TRUE;
}
-static void client_login_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
+static void
+client_login_cb(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _od)
{
- OscarData *od;
+ OscarData *od = _od;
PurpleConnection *gc;
char *token, *secret, *session_key;
time_t hosttime;
int password_len;
char *password;
+ const gchar *got_data;
+ size_t got_len;
- od = user_data;
gc = od->gc;
- od->url_data = NULL;
+ od->hc = NULL;
- if (error_message != NULL || len == 0) {
+ if (!purple_http_response_is_successful(response)) {
gchar *tmp;
tmp = g_strdup_printf(_("Error requesting %s: %s"),
- get_client_login_url(od), error_message ?
- error_message : _("The server returned an empty response"));
- purple_connection_error_reason(gc,
+ get_client_login_url(od),
+ purple_http_response_get_error(response));
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
g_free(tmp);
return;
}
- if (!parse_client_login_response(gc, url_text, len, &token, &secret, &hosttime))
+ got_data = purple_http_response_get_data(response, &got_len);
+ if (!parse_client_login_response(gc, got_data, got_len, &token, &secret,
+ &hosttime))
+ {
return;
+ }
password_len = strlen(purple_connection_get_password(gc));
password = g_strdup_printf("%.*s",
@@ -604,7 +615,8 @@ static void client_login_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data
void send_client_login(OscarData *od, const char *username)
{
PurpleConnection *gc;
- GString *request, *body;
+ PurpleHttpRequest *req;
+ GString *body;
const char *tmp;
char *password;
int password_len;
@@ -634,21 +646,13 @@ void send_client_login(OscarData *od, const char *username)
g_string_append_printf(body, "&s=%s", purple_url_encode(username));
g_free(password);
- /* Construct an HTTP POST request */
- request = g_string_new("POST /auth/clientLogin HTTP/1.0\r\n"
- "Connection: close\r\n"
- "Accept: */*\r\n");
+ req = purple_http_request_new(get_client_login_url(od));
+ purple_http_request_set_method(req, "POST");
+ purple_http_request_header_set(req, "Content-Type",
+ "application/x-www-form-urlencoded; charset=UTF-8");
+ purple_http_request_set_contents(req, body->str, body->len);
+ od->hc = purple_http_request(gc, req, client_login_cb, od);
+ purple_http_request_unref(req);
- /* Tack on the body */
- g_string_append_printf(request, "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n");
- g_string_append_printf(request, "Content-Length: %" G_GSIZE_FORMAT "\r\n\r\n", body->len);
- g_string_append_len(request, body->str, body->len);
g_string_free(body, TRUE);
-
- /* Send the POST request */
- od->url_data = purple_util_fetch_url_request_len_with_account(
- purple_connection_get_account(gc), get_client_login_url(od),
- TRUE, NULL, FALSE, request->str, FALSE, -1,
- client_login_cb, od);
- g_string_free(request, TRUE);
}
diff --git a/libpurple/protocols/oscar/family_auth.c b/libpurple/protocols/oscar/family_auth.c
index ef3bad5c3d..51a643583b 100644
--- a/libpurple/protocols/oscar/family_auth.c
+++ b/libpurple/protocols/oscar/family_auth.c
@@ -27,10 +27,11 @@
*/
#include "oscar.h"
+#include "oscarcommon.h"
#include <ctype.h>
-#include "cipher.h"
+#include "ciphers/md5hash.h"
/* #define USE_XOR_FOR_ICQ */
@@ -74,14 +75,14 @@ aim_encode_password(const char *password, guint8 *encoded)
static int
aim_encode_password_md5(const char *password, size_t password_len, const char *key, guint8 *digest)
{
- PurpleCipherContext *context;
+ PurpleHash *hash;
- context = purple_cipher_context_new_by_name("md5", NULL);
- purple_cipher_context_append(context, (const guchar *)key, strlen(key));
- purple_cipher_context_append(context, (const guchar *)password, password_len);
- purple_cipher_context_append(context, (const guchar *)AIM_MD5_STRING, strlen(AIM_MD5_STRING));
- purple_cipher_context_digest(context, 16, digest, NULL);
- purple_cipher_context_destroy(context);
+ hash = purple_md5_hash_new();
+ purple_hash_append(hash, (const guchar *)key, strlen(key));
+ purple_hash_append(hash, (const guchar *)password, password_len);
+ purple_hash_append(hash, (const guchar *)AIM_MD5_STRING, strlen(AIM_MD5_STRING));
+ purple_hash_digest(hash, 16, digest, NULL);
+ g_object_unref(hash);
return 0;
}
@@ -89,23 +90,19 @@ aim_encode_password_md5(const char *password, size_t password_len, const char *k
static int
aim_encode_password_md5(const char *password, size_t password_len, const char *key, guint8 *digest)
{
- PurpleCipher *cipher;
- PurpleCipherContext *context;
+ PurpleHash *hash;
guchar passdigest[16];
- cipher = purple_ciphers_find_cipher("md5");
+ hash = purple_md5_hash_new();
+ purple_hash_append(hash, (const guchar *)password, password_len);
+ purple_hash_digest(hash, passdigest, sizeof(passdigest));
+ purple_hash_reset(hash);
- context = purple_cipher_context_new(cipher, NULL);
- purple_cipher_context_append(context, (const guchar *)password, password_len);
- purple_cipher_context_digest(context, 16, passdigest, NULL);
- purple_cipher_context_destroy(context);
-
- context = purple_cipher_context_new(cipher, NULL);
- purple_cipher_context_append(context, (const guchar *)key, strlen(key));
- purple_cipher_context_append(context, passdigest, 16);
- purple_cipher_context_append(context, (const guchar *)AIM_MD5_STRING, strlen(AIM_MD5_STRING));
- purple_cipher_context_digest(context, 16, digest, NULL);
- purple_cipher_context_destroy(context);
+ purple_hash_append(hash, (const guchar *)key, strlen(key));
+ purple_hash_append(hash, passdigest, 16);
+ purple_hash_append(hash, (const guchar *)AIM_MD5_STRING, strlen(AIM_MD5_STRING));
+ purple_hash_digest(hash, digest, 16);
+ g_object_unref(hash);
return 0;
}
@@ -506,14 +503,29 @@ aim_request_login(OscarData *od, FlapConnection *conn, const char *sn)
static int
keyparse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
{
- int keylen, ret = 1;
- aim_rxcallback_t userfunc;
+ int keylen;
char *keystr;
GSList *tlvlist;
gboolean truncate_pass;
+ PurpleConnection *gc;
+ PurpleAccount *account;
+ ClientInfo aiminfo = CLIENTINFO_PURPLE_AIM;
+ ClientInfo icqinfo = CLIENTINFO_PURPLE_ICQ;
+
+ gc = od->gc;
+ account = purple_connection_get_account(gc);
keylen = byte_stream_get16(bs);
keystr = byte_stream_getstr(bs, keylen);
+ if (!g_utf8_validate(keystr, -1, NULL)) {
+ purple_debug_warning("oscar", "Received SNAC %04hx/%04hx with "
+ "invalid UTF-8 keystr.\n", snac->family, snac->subtype);
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+ _("Received unexpected response from server"));
+ g_free(keystr);
+ return 1;
+ }
+
tlvlist = aim_tlvlist_read(bs);
/*
@@ -527,13 +539,18 @@ keyparse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *fram
* for the netscape network. This SNAC had a type 0x0058 TLV with length 10.
* Data is 0x0007 0004 3e19 ae1e 0006 0004 0000 0005 */
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame, keystr, (int)truncate_pass);
+ aim_send_login(od, conn, purple_account_get_username(account),
+ purple_connection_get_password(gc), truncate_pass,
+ od->icq ? &icqinfo : &aiminfo, keystr,
+ purple_account_get_bool(account, "allow_multiple_logins", OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS));
+
+ purple_connection_update_progress(gc,
+ _("Password sent"), 2, OSCAR_CONNECT_STEPS);
g_free(keystr);
aim_tlvlist_free(tlvlist);
- return ret;
+ return 1;
}
/**
diff --git a/libpurple/protocols/oscar/family_bart.c b/libpurple/protocols/oscar/family_bart.c
index f652d032ba..e8fb1cb7a6 100644
--- a/libpurple/protocols/oscar/family_bart.c
+++ b/libpurple/protocols/oscar/family_bart.c
@@ -141,6 +141,12 @@ parseicon(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *fra
guint8 iconcsumtype, iconcsumlen, *iconcsum, *icon;
bn = byte_stream_getstr(bs, byte_stream_get8(bs));
+ if (!g_utf8_validate(bn, -1, NULL)) {
+ purple_debug_warning("oscar", "Received SNAC %04hx/%04hx with "
+ "invalid UTF-8 buddy name.\n", snac->family, snac->subtype);
+ g_free(bn);
+ return 1;
+ }
byte_stream_get16(bs); /* flags */
iconcsumtype = byte_stream_get8(bs);
iconcsumlen = byte_stream_get8(bs);
diff --git a/libpurple/protocols/oscar/family_buddy.c b/libpurple/protocols/oscar/family_buddy.c
index a58232a058..3d38323032 100644
--- a/libpurple/protocols/oscar/family_buddy.c
+++ b/libpurple/protocols/oscar/family_buddy.c
@@ -113,7 +113,7 @@ buddychange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
if (snac->subtype == SNAC_SUBTYPE_BUDDY_ONCOMING &&
userinfo.capabilities & OSCAR_CAPABILITY_XTRAZ) {
PurpleAccount *account = purple_connection_get_account(od->gc);
- PurpleBuddy *buddy = purple_find_buddy(account, userinfo.bn);
+ PurpleBuddy *buddy = purple_blist_find_buddy(account, userinfo.bn);
if (buddy) {
PurplePresence *presence = purple_buddy_get_presence(buddy);
diff --git a/libpurple/protocols/oscar/family_chat.c b/libpurple/protocols/oscar/family_chat.c
index b1b3ab72df..ee11a1e940 100644
--- a/libpurple/protocols/oscar/family_chat.c
+++ b/libpurple/protocols/oscar/family_chat.c
@@ -50,7 +50,7 @@ flap_connection_destroy_chat(OscarData *od, FlapConnection *conn)
int
aim_chat_readroominfo(ByteStream *bs, struct aim_chat_roominfo *outinfo)
{
- if (!bs || !outinfo)
+ if (!outinfo)
return 0;
outinfo->exchange = byte_stream_get16(bs);
diff --git a/libpurple/protocols/oscar/family_chatnav.c b/libpurple/protocols/oscar/family_chatnav.c
index c8527b7b0c..a81a07733f 100644
--- a/libpurple/protocols/oscar/family_chatnav.c
+++ b/libpurple/protocols/oscar/family_chatnav.c
@@ -63,7 +63,9 @@ error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame,
"Could not join room, error=0x%04hx, chatnav_error=0x%04hx\n",
error, chatnav_error);
purple_notify_error(od->gc, NULL, _("Could not join chat room"),
- chatnav_error == 0x0033 ? _("Invalid chat room name") : _("Unknown error"));
+ chatnav_error == 0x0033 ? _("Invalid chat room name") :
+ _("Unknown error"),
+ purple_request_cpar_from_connection(od->gc));
ret = 1;
}
diff --git a/libpurple/protocols/oscar/family_feedbag.c b/libpurple/protocols/oscar/family_feedbag.c
index 26dfa21aaa..7f40d82c34 100644
--- a/libpurple/protocols/oscar/family_feedbag.c
+++ b/libpurple/protocols/oscar/family_feedbag.c
@@ -21,9 +21,9 @@
/*
* Family 0x0013 - Server-Side/Stored Information.
*
- * Relatively new facility that allows certain types of information, such as
- * a user's buddy list, permit/deny list, and permit/deny preferences, to be
- * stored on the server, so that they can be accessed from any client.
+ * Deals with storing certain types of information, such as a user's buddy
+ * list, permit/deny list, and permit/deny preferences, on the server, so
+ * that they can be accessed from any client.
*
* We keep 2 copies of SSI data:
* 1) An exact copy of what is stored on the AIM servers.
@@ -40,14 +40,41 @@
*
* This is entirely too complicated.
* You don't know the half of it.
- *
*/
#include "oscar.h"
+#include "oscarcommon.h"
#include "debug.h"
static int aim_ssi_addmoddel(OscarData *od);
+static void aim_ssi_item_free(struct aim_ssi_item *item)
+{
+ g_free(item->name);
+ aim_tlvlist_free(item->data);
+ g_free(item);
+}
+
+static void aim_ssi_item_set_name(struct aim_ssi_itemlist *list, struct aim_ssi_item *item, const char *name)
+{
+ gchar key[3000];
+
+ if (item->name) {
+ /* Remove old name from hash table */
+ snprintf(key, sizeof(key), "%hx%s", item->type, oscar_normalize(NULL, item->name));
+ g_hash_table_remove(list->idx_all_named_items, key);
+ }
+
+ g_free(item->name);
+ item->name = g_strdup(name);
+
+ if (name) {
+ /* Add new name to hash table */
+ snprintf(key, sizeof(key), "%hx%s", item->type, oscar_normalize(NULL, item->name));
+ g_hash_table_insert(list->idx_all_named_items, g_strdup(key), item);
+ }
+}
+
/**
* List types based on http://dev.aol.com/aim/oscar/#FEEDBAG (archive.org)
* and http://iserverd.khstu.ru/oscar/ssi_item.html
@@ -80,7 +107,7 @@ aim_ssi_type_to_string(guint16 type)
{ 0x0020, "ICQ-MDir" },
{ 0x0029, "Facebook" },
};
- int i;
+ size_t i;
for (i = 0; i < G_N_ELEMENTS(type_strings); i++) {
if (type_strings[i].type == type) {
return type_strings[i].string;
@@ -113,7 +140,7 @@ aim_ssi_item_debug_append(GString *str, char *prefix, struct aim_ssi_item *item)
* @return Return a pointer to the modified item.
*/
static void
-aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item *list, const char *name)
+aim_ssi_itemlist_rebuildgroup(struct aim_ssi_itemlist *list, const char *name)
{
int newlen;
struct aim_ssi_item *cur, *group;
@@ -125,11 +152,11 @@ aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item *list, const char *name)
/* Find the length for the new additional data */
newlen = 0;
if (group->gid == 0x0000) {
- for (cur=list; cur; cur=cur->next)
+ for (cur=list->data; cur; cur=cur->next)
if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000))
newlen += 2;
} else {
- for (cur=list; cur; cur=cur->next)
+ for (cur=list->data; cur; cur=cur->next)
if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
newlen += 2;
}
@@ -138,14 +165,14 @@ aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item *list, const char *name)
if (newlen > 0) {
guint8 *newdata;
- newdata = (guint8 *)g_malloc((newlen)*sizeof(guint8));
+ newdata = g_new(guint8, newlen);
newlen = 0;
if (group->gid == 0x0000) {
- for (cur=list; cur; cur=cur->next)
+ for (cur=list->data; cur; cur=cur->next)
if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000))
newlen += aimutil_put16(newdata+newlen, cur->gid);
} else {
- for (cur=list; cur; cur=cur->next)
+ for (cur=list->data; cur; cur=cur->next)
if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
newlen += aimutil_put16(newdata+newlen, cur->bid);
}
@@ -167,15 +194,12 @@ aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item *list, const char *name)
* @param data The additional data for the new item.
* @return A pointer to the newly created item.
*/
-static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, const char *name, guint16 gid, guint16 bid, guint16 type, GSList *data)
+static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_itemlist *list, const char *name, guint16 gid, guint16 bid, guint16 type, GSList *data)
{
gboolean exists;
struct aim_ssi_item *cur, *new;
- new = g_new(struct aim_ssi_item, 1);
-
- /* Set the name */
- new->name = g_strdup(name);
+ new = g_new0(struct aim_ssi_item, 1);
/* Set the group ID# and buddy ID# */
new->gid = gid;
@@ -185,7 +209,7 @@ static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, con
do {
new->gid += 0x0001;
exists = FALSE;
- for (cur = *list; cur != NULL; cur = cur->next)
+ for (cur = list->data; cur != NULL; cur = cur->next)
if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid == new->gid)) {
exists = TRUE;
break;
@@ -202,7 +226,7 @@ static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, con
do {
new->bid += 0x0001;
exists = FALSE;
- for (cur = *list; cur != NULL; cur = cur->next)
+ for (cur = list->data; cur != NULL; cur = cur->next)
if (cur->bid == new->bid || cur->gid == new->bid) {
exists = TRUE;
break;
@@ -214,7 +238,7 @@ static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, con
do {
new->bid += 0x0001;
exists = FALSE;
- for (cur = *list; cur != NULL; cur = cur->next)
+ for (cur = list->data; cur != NULL; cur = cur->next)
if (cur->bid == new->bid && cur->gid == new->gid) {
exists = TRUE;
break;
@@ -226,23 +250,29 @@ static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, con
/* Set the type */
new->type = type;
+ /* Add it to the gid+bid hashtable */
+ g_hash_table_insert(list->idx_gid_bid, GINT_TO_POINTER((new->gid << 16) + new->bid), new);
+
+ /* Set the name - do this *AFTER* setting the type because type is used for the key */
+ aim_ssi_item_set_name(list, new, name);
+
/* Set the TLV list */
new->data = aim_tlvlist_copy(data);
/* Add the item to the list in the correct numerical position. Fancy, eh? */
- if (*list) {
- if ((new->gid < (*list)->gid) || ((new->gid == (*list)->gid) && (new->bid < (*list)->bid))) {
- new->next = *list;
- *list = new;
+ if (list->data) {
+ if ((new->gid < list->data->gid) || ((new->gid == list->data->gid) && (new->bid < list->data->bid))) {
+ new->next = list->data;
+ list->data = new;
} else {
struct aim_ssi_item *prev;
- for ((prev=*list, cur=(*list)->next); (cur && ((new->gid > cur->gid) || ((new->gid == cur->gid) && (new->bid > cur->bid)))); prev=cur, cur=cur->next);
+ for ((prev=list->data, cur=list->data->next); (cur && ((new->gid > cur->gid) || ((new->gid == cur->gid) && (new->bid > cur->bid)))); prev=cur, cur=cur->next);
new->next = prev->next;
prev->next = new;
}
} else {
- new->next = *list;
- *list = new;
+ new->next = list->data;
+ list->data = new;
}
return new;
@@ -255,25 +285,31 @@ static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, con
* @param del A pointer to the item you want to remove from the list.
* @return Return 0 if no errors, otherwise return the error number.
*/
-static int aim_ssi_itemlist_del(struct aim_ssi_item **list, struct aim_ssi_item *del)
+static int aim_ssi_itemlist_del(struct aim_ssi_itemlist *list, struct aim_ssi_item *del)
{
- if (!(*list) || !del)
+ gchar key[3000];
+
+ if (!(list->data) || !del)
return -EINVAL;
/* Remove the item from the list */
- if (*list == del) {
- *list = (*list)->next;
+ if (list->data == del) {
+ list->data = list->data->next;
} else {
struct aim_ssi_item *cur;
- for (cur=*list; (cur->next && (cur->next!=del)); cur=cur->next);
+ for (cur=list->data; (cur->next && (cur->next!=del)); cur=cur->next);
if (cur->next)
cur->next = del->next;
}
+ /* Remove from the hashtables */
+ g_hash_table_remove(list->idx_gid_bid, GINT_TO_POINTER((del->gid << 16) + del->bid));
+
+ snprintf(key, sizeof(key), "%hx%s", del->type, oscar_normalize(NULL, del->name));
+ g_hash_table_remove(list->idx_all_named_items, key);
+
/* Free the removed item */
- g_free(del->name);
- aim_tlvlist_free(del->data);
- g_free(del);
+ aim_ssi_item_free(del);
return 0;
}
@@ -320,10 +356,10 @@ static int aim_ssi_itemlist_cmp(struct aim_ssi_item *cur1, struct aim_ssi_item *
return 0;
}
-static gboolean aim_ssi_itemlist_valid(struct aim_ssi_item *list, struct aim_ssi_item *item)
+static gboolean aim_ssi_itemlist_valid(struct aim_ssi_itemlist *list, struct aim_ssi_item *item)
{
struct aim_ssi_item *cur;
- for (cur=list; cur; cur=cur->next)
+ for (cur=list->data; cur; cur=cur->next)
if (cur == item)
return TRUE;
return FALSE;
@@ -337,13 +373,10 @@ static gboolean aim_ssi_itemlist_valid(struct aim_ssi_item *list, struct aim_ssi
* @param bid The buddy ID# of the desired item.
* @return Return a pointer to the item if found, else return NULL;
*/
-struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, guint16 gid, guint16 bid)
+struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_itemlist *list, guint16 gid, guint16 bid)
{
- struct aim_ssi_item *cur;
- for (cur=list; cur; cur=cur->next)
- if ((cur->gid == gid) && (cur->bid == bid))
- return cur;
- return NULL;
+ guint32 id_key = (gid << 16) + bid;
+ return g_hash_table_lookup(list->idx_gid_bid, GINT_TO_POINTER(id_key));
}
/**
@@ -356,37 +389,30 @@ struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, guint16 gi
* @param type The type of the desired item.
* @return Return a pointer to the item if found, else return NULL.
*/
-struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *bn, guint16 type)
+struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_itemlist *list, const char *gn, const char *bn, guint16 type)
{
struct aim_ssi_item *cur;
- if (!list)
+ gchar key[3000];
+
+ if (!list->data)
return NULL;
if (gn && bn) { /* For finding buddies in groups */
- for (cur=list; cur; cur=cur->next)
+ g_return_val_if_fail(type == AIM_SSI_TYPE_BUDDY, NULL);
+ for (cur=list->data; cur; cur=cur->next)
if ((cur->type == type) && (cur->name) && !(oscar_util_name_compare(cur->name, bn))) {
struct aim_ssi_item *curg;
- for (curg=list; curg; curg=curg->next)
+ for (curg=list->data; curg; curg=curg->next)
if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid) && (curg->name) && !(oscar_util_name_compare(curg->name, gn)))
return cur;
}
- } else if (gn) { /* For finding groups */
- for (cur=list; cur; cur=cur->next) {
- if ((cur->type == type) && (cur->bid == 0x0000) && (cur->name) && !(oscar_util_name_compare(cur->name, gn))) {
- return cur;
- }
- }
-
- } else if (bn) { /* For finding permits, denies, and ignores */
- for (cur=list; cur; cur=cur->next) {
- if ((cur->type == type) && (cur->name) && !(oscar_util_name_compare(cur->name, bn))) {
- return cur;
- }
- }
+ } else if (gn || bn) { /* For finding groups, permits, denies and ignores */
+ snprintf(key, sizeof(key), "%hx%s", type, oscar_normalize(NULL, gn ? gn : bn));
+ return g_hash_table_lookup(list->idx_all_named_items, key);
/* For stuff without names--permit deny setting, visibility mask, etc. */
- } else for (cur=list; cur; cur=cur->next) {
+ } else for (cur=list->data; cur; cur=cur->next) {
if ((cur->type == type) && (!cur->name))
return cur;
}
@@ -401,7 +427,7 @@ struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const
* @param bn The group name of the desired item.
* @return Return a pointer to the name of the item if found, else return NULL;
*/
-struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *bn)
+struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_itemlist *list, const char *bn)
{
if (!bn)
return NULL;
@@ -415,10 +441,10 @@ struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const ch
* @param bn The buddy name of the desired item.
* @return Return a pointer to the name of the item if found, else return NULL;
*/
-char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *bn)
+char *aim_ssi_itemlist_findparentname(struct aim_ssi_itemlist *list, const char *bn)
{
struct aim_ssi_item *cur, *curg;
- if (!list || !bn)
+ if (!list->data || !bn)
return NULL;
if (!(cur = aim_ssi_itemlist_exists(list, bn)))
return NULL;
@@ -433,7 +459,7 @@ char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *bn)
* @param list A pointer to the current list of items.
* @return Return the current SSI permit deny setting, or 0 if no setting was found.
*/
-int aim_ssi_getpermdeny(struct aim_ssi_item *list)
+int aim_ssi_getpermdeny(struct aim_ssi_itemlist *list)
{
struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PDINFO);
if (cur) {
@@ -451,7 +477,7 @@ int aim_ssi_getpermdeny(struct aim_ssi_item *list)
* @param list A pointer to the current list of items.
* @return Return the current set of preferences.
*/
-guint32 aim_ssi_getpresence(struct aim_ssi_item *list)
+guint32 aim_ssi_getpresence(struct aim_ssi_itemlist *list)
{
struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS);
if (cur) {
@@ -472,17 +498,23 @@ guint32 aim_ssi_getpresence(struct aim_ssi_item *list)
* alias, or NULL if the buddy has no alias. You should free
* this returned value!
*/
-char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *bn)
+char *aim_ssi_getalias(struct aim_ssi_itemlist *list, const char *gn, const char *bn)
{
- struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, bn, AIM_SSI_TYPE_BUDDY);
- if (cur) {
- aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x0131, 1);
- if (tlv && tlv->length)
- return g_strndup((const gchar *)tlv->value, tlv->length);
+ struct aim_ssi_item *item = aim_ssi_itemlist_finditem(list, gn, bn, AIM_SSI_TYPE_BUDDY);
+ if (item) {
+ return aim_ssi_getalias_from_item(item);
}
return NULL;
}
+char *aim_ssi_getalias_from_item(struct aim_ssi_item *item)
+{
+ aim_tlv_t *tlv = aim_tlv_gettlv(item->data, 0x0131, 1);
+ if (tlv && tlv->length)
+ return g_strndup((const gchar *)tlv->value, tlv->length);
+ return NULL;
+}
+
/**
* Locally find the comment of the given buddy.
*
@@ -493,7 +525,7 @@ char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *bn
* comment, or NULL if the buddy has no comment. You should free
* this returned value!
*/
-char *aim_ssi_getcomment(struct aim_ssi_item *list, const char *gn, const char *bn)
+char *aim_ssi_getcomment(struct aim_ssi_itemlist *list, const char *gn, const char *bn)
{
struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, bn, AIM_SSI_TYPE_BUDDY);
if (cur) {
@@ -513,7 +545,7 @@ char *aim_ssi_getcomment(struct aim_ssi_item *list, const char *gn, const char *
* @param bn The name of the buddy.
* @return 1 if you are waiting for authorization; 0 if you are not
*/
-gboolean aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *bn)
+gboolean aim_ssi_waitingforauth(struct aim_ssi_itemlist *list, const char *gn, const char *bn)
{
struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, bn, AIM_SSI_TYPE_BUDDY);
if (cur) {
@@ -561,8 +593,8 @@ static int aim_ssi_sync(OscarData *od)
/* Deletions */
if (!od->ssi.pending) {
- for (cur1=od->ssi.official; cur1 && (n < 15); cur1=cur1->next) {
- if (!aim_ssi_itemlist_find(od->ssi.local, cur1->gid, cur1->bid)) {
+ for (cur1=od->ssi.official.data; cur1 && (n < 15); cur1=cur1->next) {
+ if (!aim_ssi_itemlist_find(&od->ssi.local, cur1->gid, cur1->bid)) {
n++;
new = g_new(struct aim_ssi_tmp, 1);
new->action = SNAC_SUBTYPE_FEEDBAG_DEL;
@@ -573,17 +605,18 @@ static int aim_ssi_sync(OscarData *od)
if (od->ssi.pending) {
for (cur=od->ssi.pending; cur->next; cur=cur->next);
cur->next = new;
- } else
+ } else {
od->ssi.pending = new;
- aim_ssi_item_debug_append(debugstr, "Deleting item ", cur1);
+ }
+ aim_ssi_item_debug_append(debugstr, "Deleting item ", cur1);
}
}
}
/* Additions */
if (!od->ssi.pending) {
- for (cur1=od->ssi.local; cur1 && (n < 15); cur1=cur1->next) {
- if (!aim_ssi_itemlist_find(od->ssi.official, cur1->gid, cur1->bid)) {
+ for (cur1=od->ssi.local.data; cur1 && (n < 15); cur1=cur1->next) {
+ if (!aim_ssi_itemlist_find(&od->ssi.official, cur1->gid, cur1->bid)) {
n++;
new = g_new(struct aim_ssi_tmp, 1);
new->action = SNAC_SUBTYPE_FEEDBAG_ADD;
@@ -594,17 +627,18 @@ static int aim_ssi_sync(OscarData *od)
if (od->ssi.pending) {
for (cur=od->ssi.pending; cur->next; cur=cur->next);
cur->next = new;
- } else
+ } else {
od->ssi.pending = new;
- aim_ssi_item_debug_append(debugstr, "Adding item ", cur1);
+ }
+ aim_ssi_item_debug_append(debugstr, "Adding item ", cur1);
}
}
}
/* Modifications */
if (!od->ssi.pending) {
- for (cur1=od->ssi.local; cur1 && (n < 15); cur1=cur1->next) {
- cur2 = aim_ssi_itemlist_find(od->ssi.official, cur1->gid, cur1->bid);
+ for (cur1=od->ssi.local.data; cur1 && (n < 15); cur1=cur1->next) {
+ cur2 = aim_ssi_itemlist_find(&od->ssi.official, cur1->gid, cur1->bid);
if (cur2 && (aim_ssi_itemlist_cmp(cur1, cur2))) {
n++;
new = g_new(struct aim_ssi_tmp, 1);
@@ -616,20 +650,23 @@ static int aim_ssi_sync(OscarData *od)
if (od->ssi.pending) {
for (cur=od->ssi.pending; cur->next; cur=cur->next);
cur->next = new;
- } else
+ } else {
od->ssi.pending = new;
- aim_ssi_item_debug_append(debugstr, "Modifying item ", cur1);
+ }
+ aim_ssi_item_debug_append(debugstr, "Modifying item ", cur1);
}
}
}
if (debugstr->len > 0) {
purple_debug_info("oscar", "%s", debugstr->str);
if (purple_debug_is_verbose()) {
- g_string_truncate(debugstr, 0);
- for (cur1 = od->ssi.local; cur1; cur1 = cur1->next)
+ PurpleAccount *account = purple_connection_get_account(od->gc);
+ g_string_truncate(debugstr, 0);
+ for (cur1 = od->ssi.local.data; cur1; cur1 = cur1->next) {
aim_ssi_item_debug_append(debugstr, "\t", cur1);
+ }
purple_debug_misc("oscar", "Dumping item list of account %s:\n%s",
- purple_connection_get_account(od->gc)->username, debugstr->str);
+ purple_account_get_username(account), debugstr->str);
}
}
g_string_free(debugstr, TRUE);
@@ -673,22 +710,18 @@ aim_ssi_freelist(OscarData *od)
struct aim_ssi_item *cur, *del;
struct aim_ssi_tmp *curtmp, *deltmp;
- cur = od->ssi.official;
+ cur = od->ssi.official.data;
while (cur) {
del = cur;
cur = cur->next;
- g_free(del->name);
- aim_tlvlist_free(del->data);
- g_free(del);
+ aim_ssi_item_free(del);
}
- cur = od->ssi.local;
+ cur = od->ssi.local.data;
while (cur) {
del = cur;
cur = cur->next;
- g_free(del->name);
- aim_tlvlist_free(del->data);
- g_free(del);
+ aim_ssi_item_free(del);
}
curtmp = od->ssi.pending;
@@ -699,22 +732,49 @@ aim_ssi_freelist(OscarData *od)
}
od->ssi.numitems = 0;
- od->ssi.official = NULL;
- od->ssi.local = NULL;
+ od->ssi.official.data = NULL;
+ od->ssi.local.data = NULL;
od->ssi.pending = NULL;
od->ssi.timestamp = (time_t)0;
}
/**
- * This "cleans" the ssi list. It does the following:
- * 1) Makes sure all buddies, permits, and denies have names.
- * 2) Makes sure that all buddies are in a group that exist.
- * 3) Deletes any empty groups
+ * Look up the given TLV type in the item's data. If the value of
+ * the TLV is not a valid UTF-8 string then use purple_utf8_salvage()
+ * to replace invalid bytes with question marks.
+ */
+static void cleanlist_ensure_utf8_data(struct aim_ssi_item *item, guint16 tlvtype)
+{
+ aim_tlv_t *tlv;
+ gchar *value, *salvaged;
+
+ tlv = aim_tlv_gettlv(item->data, tlvtype, 1);
+ if (tlv && tlv->length && !g_utf8_validate((const gchar *)tlv->value, tlv->length, NULL)) {
+ purple_debug_warning("oscar", "cleanlist found invalid UTF-8 "
+ "for 0x%04hx field of 0x%04hx item with name %s. "
+ "Attempting to repair.\n",
+ tlvtype, item->type, item->name ? item->name : "(null)");
+ value = g_strndup((const gchar *)tlv->value, tlv->length);
+ salvaged = purple_utf8_salvage(value);
+ g_free(value);
+ if (*salvaged)
+ aim_tlvlist_replace_str(&item->data, tlvtype, salvaged);
+ else
+ aim_tlvlist_remove(&item->data, tlvtype);
+ g_free(salvaged);
+ }
+}
+
+/**
+ * This "cleans" the ssi list. It does things like:
+ * - Makes sure all buddies, permits, and denies have names
+ * - Makes sure all buddies are in a group that exist
+ * - Makes sure strings are valid UTF-8
*
* @param od The oscar odion.
* @return Return 0 if no errors, otherwise return the error number.
*/
-int aim_ssi_cleanlist(OscarData *od)
+static int aim_ssi_cleanlist(OscarData *od)
{
struct aim_ssi_item *cur, *next;
@@ -726,7 +786,7 @@ int aim_ssi_cleanlist(OscarData *od)
/* DESTROY any buddies that are directly in the master group. */
/* Do the same for buddies that are in a non-existant group. */
/* This will kind of mess up if you hit the item limit, but this function isn't too critical */
- cur = od->ssi.local;
+ cur = od->ssi.local.data;
while (cur) {
next = cur->next;
if (!cur->name) {
@@ -734,29 +794,43 @@ int aim_ssi_cleanlist(OscarData *od)
aim_ssi_delbuddy(od, NULL, NULL);
else if (cur->type == AIM_SSI_TYPE_PERMIT || cur->type == AIM_SSI_TYPE_DENY || cur->type == AIM_SSI_TYPE_ICQDENY)
aim_ssi_del_from_private_list(od, NULL, cur->type);
- } else if ((cur->type == AIM_SSI_TYPE_BUDDY) && ((cur->gid == 0x0000) || (!aim_ssi_itemlist_find(od->ssi.local, cur->gid, 0x0000)))) {
- char *alias = aim_ssi_getalias(od->ssi.local, NULL, cur->name);
- aim_ssi_addbuddy(od, cur->name, "orphans", NULL, alias, NULL, NULL, FALSE);
+ } else if ((cur->type == AIM_SSI_TYPE_BUDDY) && ((cur->gid == 0x0000) || (!aim_ssi_itemlist_find(&od->ssi.local, cur->gid, 0x0000)))) {
+ char *alias = aim_ssi_getalias(&od->ssi.local, NULL, cur->name);
+ aim_ssi_addbuddy(od, cur->name, PURPLE_BLIST_DEFAULT_GROUP_NAME,
+ NULL, alias, NULL, NULL, FALSE);
aim_ssi_delbuddy(od, cur->name, NULL);
g_free(alias);
}
cur = next;
}
- /* Make sure there aren't any duplicate buddies in a group, or duplicate permits or denies */
- cur = od->ssi.local;
+ cur = od->ssi.local.data;
while (cur) {
if ((cur->type == AIM_SSI_TYPE_BUDDY) || (cur->type == AIM_SSI_TYPE_PERMIT) || (cur->type == AIM_SSI_TYPE_DENY))
{
struct aim_ssi_item *cur2, *next2;
+
+ /* Make sure there aren't any duplicate permits or denies, or
+ duplicate buddies within a group */
cur2 = cur->next;
while (cur2) {
next2 = cur2->next;
- if ((cur->type == cur2->type) && (cur->gid == cur2->gid) && (cur->name != NULL) && (cur2->name != NULL) && (!oscar_util_name_compare(cur->name, cur2->name))) {
+ if (cur->type == cur2->type
+ && cur->gid == cur2->gid
+ && cur->name
+ && cur2->name
+ && !oscar_util_name_compare(cur->name, cur2->name))
+ {
aim_ssi_itemlist_del(&od->ssi.local, cur2);
}
cur2 = next2;
}
+
+ /* Make sure alias is valid UTF-8 */
+ cleanlist_ensure_utf8_data(cur, 0x0131);
+
+ /* Make sure comment is valid UTF-8 */
+ cleanlist_ensure_utf8_data(cur, 0x013c);
}
cur = cur->next;
}
@@ -785,16 +859,16 @@ int aim_ssi_addbuddy(OscarData *od, const char *name, const char *group, GSList
return -EINVAL;
/* Find the parent */
- if (!(parent = aim_ssi_itemlist_finditem(od->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP))) {
+ if (!(parent = aim_ssi_itemlist_finditem(&od->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP))) {
/* Find the parent's parent (the master group) */
- if (aim_ssi_itemlist_find(od->ssi.local, 0x0000, 0x0000) == NULL)
+ if (aim_ssi_itemlist_find(&od->ssi.local, 0x0000, 0x0000) == NULL)
aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
/* Add the parent */
parent = aim_ssi_itemlist_add(&od->ssi.local, group, 0xFFFF, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
/* Modify the parent's parent (the master group) */
- aim_ssi_itemlist_rebuildgroup(od->ssi.local, NULL);
+ aim_ssi_itemlist_rebuildgroup(&od->ssi.local, NULL);
}
/* Create a TLV list for the new buddy */
@@ -812,7 +886,7 @@ int aim_ssi_addbuddy(OscarData *od, const char *name, const char *group, GSList
aim_tlvlist_free(data);
/* Modify the parent group */
- aim_ssi_itemlist_rebuildgroup(od->ssi.local, group);
+ aim_ssi_itemlist_rebuildgroup(&od->ssi.local, group);
/* Sync our local list with the server list */
return aim_ssi_sync(od);
@@ -824,7 +898,7 @@ aim_ssi_add_to_private_list(OscarData *od, const char* name, guint16 list_type)
if (!od || !name || !od->ssi.received_data)
return -EINVAL;
- if (aim_ssi_itemlist_find(od->ssi.local, 0x0000, 0x0000) == NULL)
+ if (aim_ssi_itemlist_find(&od->ssi.local, 0x0000, 0x0000) == NULL)
aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
aim_ssi_itemlist_add(&od->ssi.local, name, 0x0000, 0xFFFF, list_type, NULL);
@@ -839,7 +913,7 @@ aim_ssi_del_from_private_list(OscarData* od, const char* name, guint16 list_type
if (!od)
return -EINVAL;
- if (!(del = aim_ssi_itemlist_finditem(od->ssi.local, NULL, name, list_type)))
+ if (!(del = aim_ssi_itemlist_finditem(&od->ssi.local, NULL, name, list_type)))
return -EINVAL;
aim_ssi_itemlist_del(&od->ssi.local, del);
@@ -862,14 +936,14 @@ int aim_ssi_delbuddy(OscarData *od, const char *name, const char *group)
return -EINVAL;
/* Find the buddy */
- if (!(del = aim_ssi_itemlist_finditem(od->ssi.local, group, name, AIM_SSI_TYPE_BUDDY)))
+ if (!(del = aim_ssi_itemlist_finditem(&od->ssi.local, group, name, AIM_SSI_TYPE_BUDDY)))
return -EINVAL;
/* Remove the item from the list */
aim_ssi_itemlist_del(&od->ssi.local, del);
/* Modify the parent group */
- aim_ssi_itemlist_rebuildgroup(od->ssi.local, group);
+ aim_ssi_itemlist_rebuildgroup(&od->ssi.local, group);
/* Sync our local list with the server list */
return aim_ssi_sync(od);
@@ -891,7 +965,7 @@ int aim_ssi_delgroup(OscarData *od, const char *group)
return -EINVAL;
/* Find the group */
- if (!(del = aim_ssi_itemlist_finditem(od->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP)))
+ if (!(del = aim_ssi_itemlist_finditem(&od->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP)))
return -EINVAL;
/* Don't delete the group if it's not empty */
@@ -903,7 +977,7 @@ int aim_ssi_delgroup(OscarData *od, const char *group)
aim_ssi_itemlist_del(&od->ssi.local, del);
/* Modify the parent group */
- aim_ssi_itemlist_rebuildgroup(od->ssi.local, group);
+ aim_ssi_itemlist_rebuildgroup(&od->ssi.local, NULL);
/* Sync our local list with the server list */
return aim_ssi_sync(od);
@@ -925,7 +999,7 @@ int aim_ssi_movebuddy(OscarData *od, const char *oldgn, const char *newgn, const
GSList *data;
/* Find the buddy */
- buddy = aim_ssi_itemlist_finditem(od->ssi.local, oldgn, bn, AIM_SSI_TYPE_BUDDY);
+ buddy = aim_ssi_itemlist_finditem(&od->ssi.local, oldgn, bn, AIM_SSI_TYPE_BUDDY);
if (buddy == NULL)
return -EINVAL;
@@ -958,11 +1032,11 @@ int aim_ssi_aliasbuddy(OscarData *od, const char *gn, const char *bn, const char
if (!od || !gn || !bn)
return -EINVAL;
- if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, gn, bn, AIM_SSI_TYPE_BUDDY)))
+ if (!(tmp = aim_ssi_itemlist_finditem(&od->ssi.local, gn, bn, AIM_SSI_TYPE_BUDDY)))
return -EINVAL;
/* Either add or remove the 0x0131 TLV from the TLV chain */
- if ((alias != NULL) && (strlen(alias) > 0))
+ if (alias && *alias)
aim_tlvlist_replace_str(&tmp->data, 0x0131, alias);
else
aim_tlvlist_remove(&tmp->data, 0x0131);
@@ -988,11 +1062,11 @@ int aim_ssi_editcomment(OscarData *od, const char *gn, const char *bn, const cha
if (!od || !gn || !bn)
return -EINVAL;
- if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, gn, bn, AIM_SSI_TYPE_BUDDY)))
+ if (!(tmp = aim_ssi_itemlist_finditem(&od->ssi.local, gn, bn, AIM_SSI_TYPE_BUDDY)))
return -EINVAL;
/* Either add or remove the 0x0131 TLV from the TLV chain */
- if ((comment != NULL) && (strlen(comment) > 0))
+ if (comment && *comment)
aim_tlvlist_replace_str(&tmp->data, 0x013c, comment);
else
aim_tlvlist_remove(&tmp->data, 0x013c);
@@ -1016,11 +1090,10 @@ int aim_ssi_rename_group(OscarData *od, const char *oldgn, const char *newgn)
if (!od || !oldgn || !newgn)
return -EINVAL;
- if (!(group = aim_ssi_itemlist_finditem(od->ssi.local, oldgn, NULL, AIM_SSI_TYPE_GROUP)))
+ if (!(group = aim_ssi_itemlist_finditem(&od->ssi.local, oldgn, NULL, AIM_SSI_TYPE_GROUP)))
return -EINVAL;
- g_free(group->name);
- group->name = g_strdup(newgn);
+ aim_ssi_item_set_name(&od->ssi.local, group, newgn);
/* Sync our local list with the server list */
return aim_ssi_sync(od);
@@ -1047,9 +1120,9 @@ int aim_ssi_setpermdeny(OscarData *od, guint8 permdeny)
return -EINVAL;
/* Find the PDINFO item, or add it if it does not exist */
- if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, NULL, NULL, AIM_SSI_TYPE_PDINFO))) {
+ if (!(tmp = aim_ssi_itemlist_finditem(&od->ssi.local, NULL, NULL, AIM_SSI_TYPE_PDINFO))) {
/* Make sure the master group exists */
- if (aim_ssi_itemlist_find(od->ssi.local, 0x0000, 0x0000) == NULL)
+ if (aim_ssi_itemlist_find(&od->ssi.local, 0x0000, 0x0000) == NULL)
aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
tmp = aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PDINFO, NULL);
@@ -1079,9 +1152,9 @@ int aim_ssi_seticon(OscarData *od, const guint8 *iconsum, guint8 iconsumlen)
return -EINVAL;
/* Find the ICONINFO item, or add it if it does not exist */
- if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, NULL, "1", AIM_SSI_TYPE_ICONINFO))) {
+ if (!(tmp = aim_ssi_itemlist_finditem(&od->ssi.local, NULL, "1", AIM_SSI_TYPE_ICONINFO))) {
/* Make sure the master group exists */
- if (aim_ssi_itemlist_find(od->ssi.local, 0x0000, 0x0000) == NULL)
+ if (aim_ssi_itemlist_find(&od->ssi.local, 0x0000, 0x0000) == NULL)
aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
tmp = aim_ssi_itemlist_add(&od->ssi.local, "1", 0x0000, 0xFFFF, AIM_SSI_TYPE_ICONINFO, NULL);
@@ -1139,9 +1212,9 @@ int aim_ssi_setpresence(OscarData *od, guint32 presence) {
return -EINVAL;
/* Find the PRESENCEPREFS item, or add it if it does not exist */
- if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS))) {
+ if (!(tmp = aim_ssi_itemlist_finditem(&od->ssi.local, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS))) {
/* Make sure the master group exists */
- if (aim_ssi_itemlist_find(od->ssi.local, 0x0000, 0x0000) == NULL)
+ if (aim_ssi_itemlist_find(&od->ssi.local, 0x0000, 0x0000) == NULL)
aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
tmp = aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PRESENCEPREFS, NULL);
@@ -1257,7 +1330,7 @@ static int parsedata(OscarData *od, FlapConnection *conn, aim_module_t *mod, Fla
aim_tlvlist_free(data);
}
purple_debug_misc("oscar", "Reading items from tlvlist for account %s:\n%s",
- purple_connection_get_account(od->gc)->username, debugstr->str);
+ purple_account_get_username(purple_connection_get_account(od->gc)), debugstr->str);
g_string_free(debugstr, TRUE);
/* Read in the timestamp */
@@ -1266,9 +1339,12 @@ static int parsedata(OscarData *od, FlapConnection *conn, aim_module_t *mod, Fla
if (!(snac->flags & 0x0001)) {
/* Make a copy of the list */
struct aim_ssi_item *cur;
- for (cur=od->ssi.official; cur; cur=cur->next)
+ for (cur=od->ssi.official.data; cur; cur=cur->next)
aim_ssi_itemlist_add(&od->ssi.local, cur->name, cur->gid, cur->bid, cur->type, cur->data);
+ /* Clean the buddy list */
+ aim_ssi_cleanlist(od);
+
od->ssi.received_data = TRUE;
if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
@@ -1416,18 +1492,16 @@ static int parsemod(OscarData *od, FlapConnection *conn, aim_module_t *mod, Flap
data = NULL;
/* Replace the 2 local items with the given one */
- if ((item = aim_ssi_itemlist_find(od->ssi.local, gid, bid))) {
+ if ((item = aim_ssi_itemlist_find(&od->ssi.local, gid, bid))) {
item->type = type;
- g_free(item->name);
- item->name = g_strdup(name);
+ aim_ssi_item_set_name(&od->ssi.local, item, name);
aim_tlvlist_free(item->data);
item->data = aim_tlvlist_copy(data);
}
- if ((item = aim_ssi_itemlist_find(od->ssi.official, gid, bid))) {
+ if ((item = aim_ssi_itemlist_find(&od->ssi.official, gid, bid))) {
item->type = type;
- g_free(item->name);
- item->name = g_strdup(name);
+ aim_ssi_item_set_name(&od->ssi.official, item, name);
aim_tlvlist_free(item->data);
item->data = aim_tlvlist_copy(data);
}
@@ -1461,9 +1535,9 @@ static int parsedel(OscarData *od, FlapConnection *conn, aim_module_t *mod, Flap
byte_stream_get16(bs);
byte_stream_advance(bs, byte_stream_get16(bs));
- if ((del = aim_ssi_itemlist_find(od->ssi.local, gid, bid)))
+ if ((del = aim_ssi_itemlist_find(&od->ssi.local, gid, bid)))
aim_ssi_itemlist_del(&od->ssi.local, del);
- if ((del = aim_ssi_itemlist_find(od->ssi.official, gid, bid)))
+ if ((del = aim_ssi_itemlist_find(&od->ssi.official, gid, bid)))
aim_ssi_itemlist_del(&od->ssi.official, del);
if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
@@ -1504,7 +1578,8 @@ static int parseack(OscarData *od, FlapConnection *conn, aim_module_t *mod, Flap
if (cur->action == SNAC_SUBTYPE_FEEDBAG_ADD) {
/* Remove the item from the local list */
/* Make sure cur->item is still valid memory */
- if (aim_ssi_itemlist_valid(od->ssi.local, cur->item)) {
+ /* TODO: "Still valid memory"? That's bad form. */
+ if (aim_ssi_itemlist_valid(&od->ssi.local, cur->item)) {
cur->name = g_strdup(cur->item->name);
aim_ssi_itemlist_del(&od->ssi.local, cur->item);
}
@@ -1512,11 +1587,10 @@ static int parseack(OscarData *od, FlapConnection *conn, aim_module_t *mod, Flap
} else if (cur->action == SNAC_SUBTYPE_FEEDBAG_MOD) {
/* Replace the local item with the item from the official list */
- if (aim_ssi_itemlist_valid(od->ssi.local, cur->item)) {
+ if (aim_ssi_itemlist_valid(&od->ssi.local, cur->item)) {
struct aim_ssi_item *cur1;
- if ((cur1 = aim_ssi_itemlist_find(od->ssi.official, cur->item->gid, cur->item->bid))) {
- g_free(cur->item->name);
- cur->item->name = g_strdup(cur1->name);
+ if ((cur1 = aim_ssi_itemlist_find(&od->ssi.official, cur->item->gid, cur->item->bid))) {
+ aim_ssi_item_set_name(&od->ssi.official, cur->item, cur1->name);
aim_tlvlist_free(cur->item->data);
cur->item->data = aim_tlvlist_copy(cur1->data);
}
@@ -1525,7 +1599,7 @@ static int parseack(OscarData *od, FlapConnection *conn, aim_module_t *mod, Flap
} else if (cur->action == SNAC_SUBTYPE_FEEDBAG_DEL) {
/* Add the item back into the local list */
- if (aim_ssi_itemlist_valid(od->ssi.official, cur->item)) {
+ if (aim_ssi_itemlist_valid(&od->ssi.official, cur->item)) {
aim_ssi_itemlist_add(&od->ssi.local, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data);
} else
cur->item = NULL;
@@ -1535,18 +1609,17 @@ static int parseack(OscarData *od, FlapConnection *conn, aim_module_t *mod, Flap
/* Do the exact opposite */
if (cur->action == SNAC_SUBTYPE_FEEDBAG_ADD) {
/* Add the local item to the official list */
- if (aim_ssi_itemlist_valid(od->ssi.local, cur->item)) {
+ if (aim_ssi_itemlist_valid(&od->ssi.local, cur->item)) {
aim_ssi_itemlist_add(&od->ssi.official, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data);
} else
cur->item = NULL;
} else if (cur->action == SNAC_SUBTYPE_FEEDBAG_MOD) {
/* Replace the official item with the item from the local list */
- if (aim_ssi_itemlist_valid(od->ssi.local, cur->item)) {
+ if (aim_ssi_itemlist_valid(&od->ssi.local, cur->item)) {
struct aim_ssi_item *cur1;
- if ((cur1 = aim_ssi_itemlist_find(od->ssi.official, cur->item->gid, cur->item->bid))) {
- g_free(cur1->name);
- cur1->name = g_strdup(cur->item->name);
+ if ((cur1 = aim_ssi_itemlist_find(&od->ssi.official, cur->item->gid, cur->item->bid))) {
+ aim_ssi_item_set_name(&od->ssi.official, cur1, cur->item->name);
aim_tlvlist_free(cur1->data);
cur1->data = aim_tlvlist_copy(cur->item->data);
}
@@ -1555,7 +1628,7 @@ static int parseack(OscarData *od, FlapConnection *conn, aim_module_t *mod, Flap
} else if (cur->action == SNAC_SUBTYPE_FEEDBAG_DEL) {
/* Remove the item from the official list */
- if (aim_ssi_itemlist_valid(od->ssi.official, cur->item))
+ if (aim_ssi_itemlist_valid(&od->ssi.official, cur->item))
aim_ssi_itemlist_del(&od->ssi.official, cur->item);
cur->item = NULL;
}
@@ -1683,6 +1756,8 @@ static int receiveauthgrant(OscarData *od, FlapConnection *conn, aim_module_t *m
/* Unknown */
tmp = byte_stream_get16(bs);
+ if (!tmp)
+ purple_debug_warning("oscar", "unknown field missing");
if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
ret = userfunc(od, conn, frame, bn, msg);
@@ -1774,6 +1849,8 @@ static int receiveauthrequest(OscarData *od, FlapConnection *conn, aim_module_t
/* Unknown */
tmp = byte_stream_get16(bs);
+ if (!tmp)
+ purple_debug_warning("oscar", "unknown field missing");
if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
ret = userfunc(od, conn, frame, bn, msg);
@@ -1878,6 +1955,8 @@ static int receiveauthreply(OscarData *od, FlapConnection *conn, aim_module_t *m
/* Unknown */
tmp = byte_stream_get16(bs);
+ if (!tmp)
+ purple_debug_warning("oscar", "unknown field missing");
if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
ret = userfunc(od, conn, frame, bn, reply, msg);
diff --git a/libpurple/protocols/oscar/family_icbm.c b/libpurple/protocols/oscar/family_icbm.c
index 55800e05a1..24dec2e455 100644
--- a/libpurple/protocols/oscar/family_icbm.c
+++ b/libpurple/protocols/oscar/family_icbm.c
@@ -48,10 +48,6 @@
#include "oscar.h"
#include "peer.h"
-#ifdef _WIN32
-#include "win32dep.h"
-#endif
-
#include "util.h"
static const char * const errcodereason[] = {
@@ -174,7 +170,7 @@ error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame,
else
buf = g_strdup_printf(_("Unable to send message: %s"), reason_str);
- if (!purple_conv_present_error(bn, purple_connection_get_account(gc), buf)) {
+ if (!purple_conversation_present_error(bn, purple_connection_get_account(gc), buf)) {
g_free(buf);
if (errcode != 0 && errcode < errcodereasonlen)
buf = g_strdup_printf(_("Unable to send message to %s: %s (%s)"),
@@ -183,7 +179,8 @@ error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame,
else
buf = g_strdup_printf(_("Unable to send message to %s: %s"),
bn ? bn : "(unknown)", reason_str);
- purple_notify_error(od->gc, NULL, buf, reason_str);
+ purple_notify_error(od->gc, NULL, buf, reason_str,
+ purple_request_cpar_from_connection(od->gc));
}
g_free(buf);
@@ -650,6 +647,9 @@ aim_im_sendch2_odc_requestdirect(OscarData *od, guchar *cookie, const char *bn,
GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
ByteStream hdrbs;
+ g_return_if_fail(bn != NULL);
+ g_return_if_fail(ip != NULL);
+
conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
if (conn == NULL)
return;
@@ -1726,7 +1726,7 @@ static int clientautoresp(OscarData *od, FlapConnection *conn, aim_module_t *mod
if (*unescaped_xstatus) {
purple_debug_misc("oscar", "X-Status reply: %s\n", unescaped_xstatus);
account = purple_connection_get_account(od->gc);
- buddy = purple_find_buddy(account, bn);
+ buddy = purple_blist_find_buddy(account, bn);
presence = purple_buddy_get_presence(buddy);
status = purple_presence_get_status(presence, "mood");
if (status) {
@@ -1946,7 +1946,7 @@ int icq_im_xstatus_request(OscarData *od, const char *sn)
account = purple_connection_get_account(od->gc);
- statxml = g_strdup_printf(fmt, account->username);
+ statxml = g_strdup_printf(fmt, purple_account_get_username(account));
xmllen = strlen(statxml);
aim_icbm_makecookie(cookie);
@@ -2040,7 +2040,7 @@ int icq_relay_xstatus(OscarData *od, const char *sn, const guchar *cookie)
/* if (!strcmp(account->username, sn))
icq_im_xstatus_request(od, sn); */
- status = purple_presence_get_active_status(account->presence);
+ status = purple_presence_get_active_status(purple_account_get_presence(account));
if (!status)
return -EINVAL;
@@ -2056,7 +2056,7 @@ int icq_relay_xstatus(OscarData *od, const char *sn, const guchar *cookie)
if (!msg)
return -EINVAL;
- statxml = g_strdup_printf(fmt, account->username, title, msg);
+ statxml = g_strdup_printf(fmt, purple_account_get_username(account), title, msg);
len = strlen(statxml);
purple_debug_misc("oscar", "X-Status AutoReply: %s, %s\n", formatted_msg, msg);
@@ -2097,6 +2097,12 @@ static int mtn_receive(OscarData *od, FlapConnection *conn, aim_module_t *mod, F
channel = byte_stream_get16(bs);
bnlen = byte_stream_get8(bs);
bn = byte_stream_getstr(bs, bnlen);
+ if (!g_utf8_validate(bn, -1, NULL)) {
+ purple_debug_warning("oscar", "Received SNAC %04hx/%04hx with "
+ "invalid UTF-8 buddy name.\n", snac->family, snac->subtype);
+ g_free(bn);
+ return 1;
+ }
event = byte_stream_get16(bs);
if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
diff --git a/libpurple/protocols/oscar/family_icq.c b/libpurple/protocols/oscar/family_icq.c
index dac6b10b54..fc84019872 100644
--- a/libpurple/protocols/oscar/family_icq.c
+++ b/libpurple/protocols/oscar/family_icq.c
@@ -23,6 +23,8 @@
*
*/
+#include "glibcompat.h"
+
#include "encoding.h"
#include "oscar.h"
@@ -423,8 +425,8 @@ gotalias(OscarData *od, struct aim_icq_info *info)
if (utf8 && *utf8) {
gchar who[16];
g_snprintf(who, sizeof(who), "%u", info->uin);
- serv_got_alias(gc, who, utf8);
- if ((b = purple_find_buddy(account, who))) {
+ purple_serv_got_alias(gc, who, utf8);
+ if ((b = purple_blist_find_buddy(account, who))) {
purple_blist_node_set_string((PurpleBlistNode*)b, "servernick", utf8);
}
}
@@ -624,7 +626,7 @@ icqresponse(OscarData *od, aim_modsnac_t *snac, ByteStream *bs)
PurpleStatus *status;
account = purple_connection_get_account(od->gc);
- buddy = purple_find_buddy(account, uin);
+ buddy = purple_blist_find_buddy(account, uin);
presence = purple_buddy_get_presence(buddy);
status = purple_presence_get_active_status(presence);
@@ -771,10 +773,7 @@ snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
static void
icq_shutdown(OscarData *od, aim_module_t *mod)
{
- GSList *cur;
- for (cur = od->icq_info; cur; cur = cur->next)
- aim_icq_freeinfo(cur->data);
- g_slist_free(od->icq_info);
+ g_slist_free_full(od->icq_info, (GDestroyNotify)aim_icq_freeinfo);
}
int
diff --git a/libpurple/protocols/oscar/family_locate.c b/libpurple/protocols/oscar/family_locate.c
index 00bde7ac70..ad0a246475 100644
--- a/libpurple/protocols/oscar/family_locate.c
+++ b/libpurple/protocols/oscar/family_locate.c
@@ -27,9 +27,6 @@
*/
#include "oscar.h"
-#ifdef _WIN32
-#include "win32dep.h"
-#endif
/* Define to log unknown TLVs */
/* #define LOG_UNKNOWN_TLV */
diff --git a/libpurple/protocols/oscar/family_oservice.c b/libpurple/protocols/oscar/family_oservice.c
index d7f1cbda71..f6b8f3a7d1 100644
--- a/libpurple/protocols/oscar/family_oservice.c
+++ b/libpurple/protocols/oscar/family_oservice.c
@@ -25,8 +25,6 @@
#include "oscar.h"
-#include "cipher.h"
-
/*
* Each time we make a FLAP connection to an oscar server the server gives
* us a list of rate classes. Each rate class has different properties for
@@ -896,151 +894,6 @@ aim_srv_set_dc_info(OscarData *od)
byte_stream_destroy(&bs);
}
-/**
- * Starting this past week (26 Mar 2001, say), AOL has started sending
- * this nice little extra SNAC. AFAIK, it has never been used until now.
- *
- * The request contains eight bytes. The first four are an offset, the
- * second four are a length.
- *
- * The offset is an offset into aim.exe when it is mapped during execution
- * on Win32. So far, AOL has only been requesting bytes in static regions
- * of memory. (I won't put it past them to start requesting data in
- * less static regions -- regions that are initialized at run time, but still
- * before the client receives this request.)
- *
- * When the client receives the request, it adds it to the current ds
- * (0x00400000) and dereferences it, copying the data into a buffer which
- * it then runs directly through the MD5 hasher. The 16 byte output of
- * the hash is then sent back to the server.
- *
- * If the client does not send any data back, or the data does not match
- * the data that the specific client should have, the client will get the
- * following message from "AOL Instant Messenger":
- * "You have been disconnected from the AOL Instant Message Service (SM)
- * for accessing the AOL network using unauthorized software. You can
- * download a FREE, fully featured, and authorized client, here
- * http://www.aol.com/aim/download2.html"
- * The connection is then closed, receiving disconnect code 1, URL
- * http://www.aim.aol.com/errors/USER_LOGGED_OFF_NEW_LOGIN.html.
- *
- * Note, however, that numerous inconsistencies can cause the above error,
- * not just sending back a bad hash. Do not immediatly suspect this code
- * if you get disconnected. AOL and the open/free software community have
- * played this game for a couple years now, generating the above message
- * on numerous ocassions.
- *
- * Anyway, neener. We win again.
- *
- */
-/* Subtype 0x001f - Client verification */
-static int
-memrequest(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
-{
- int ret = 0;
- aim_rxcallback_t userfunc;
- guint32 offset, len;
- GSList *tlvlist;
- char *modname;
-
- offset = byte_stream_get32(bs);
- len = byte_stream_get32(bs);
- tlvlist = aim_tlvlist_read(bs);
-
- modname = aim_tlv_getstr(tlvlist, 0x0001, 1);
-
- purple_debug_info("oscar", "Got memory request for data at 0x%08x (%u bytes) of requested %s\n", offset, len, modname ? modname : "aim.exe");
-
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame, offset, len, modname);
-
- g_free(modname);
- aim_tlvlist_free(tlvlist);
-
- return ret;
-}
-
-/* Subtype 0x0020 - Client verification reply */
-int
-aim_sendmemblock(OscarData *od, FlapConnection *conn, guint32 offset, guint32 len, const guint8 *buf, guint8 flag)
-{
- ByteStream bs;
- aim_snacid_t snacid;
-
- if (!od || !conn)
- return -EINVAL;
-
- byte_stream_new(&bs, 2+16);
-
- byte_stream_put16(&bs, 0x0010); /* md5 is always 16 bytes */
-
- if ((flag == AIM_SENDMEMBLOCK_FLAG_ISHASH) && buf && (len == 0x10)) { /* we're getting a hash */
-
- byte_stream_putraw(&bs, buf, 0x10);
-
- } else if (buf && (len > 0)) { /* use input buffer */
- PurpleCipherContext *context;
- guchar digest[16];
-
- context = purple_cipher_context_new_by_name("md5", NULL);
- purple_cipher_context_append(context, buf, len);
- purple_cipher_context_digest(context, 16, digest, NULL);
- purple_cipher_context_destroy(context);
-
- byte_stream_putraw(&bs, digest, 0x10);
-
- } else if (len == 0) { /* no length, just hash NULL (buf is optional) */
- PurpleCipherContext *context;
- guchar digest[16];
- guint8 nil = '\0';
-
- /*
- * I'm not sure if we really need the empty append with the
- * new MD5 functions, so I'll leave it in, just in case.
- */
- context = purple_cipher_context_new_by_name("md5", NULL);
- purple_cipher_context_append(context, &nil, 0);
- purple_cipher_context_digest(context, 16, digest, NULL);
- purple_cipher_context_destroy(context);
-
- byte_stream_putraw(&bs, digest, 0x10);
-
- } else {
-
- /*
- * This data is correct for AIM 3.5.1670.
- *
- * Using these blocks is as close to "legal" as you can get
- * without using an AIM binary.
- *
- */
- if ((offset == 0x03ffffff) && (len == 0x03ffffff)) {
-
-#if 1 /* with "AnrbnrAqhfzcd" */
- byte_stream_put32(&bs, 0x44a95d26);
- byte_stream_put32(&bs, 0xd2490423);
- byte_stream_put32(&bs, 0x93b8821f);
- byte_stream_put32(&bs, 0x51c54b01);
-#else /* no filename */
- byte_stream_put32(&bs, 0x1df8cbae);
- byte_stream_put32(&bs, 0x5523b839);
- byte_stream_put32(&bs, 0xa0e10db3);
- byte_stream_put32(&bs, 0xa46d3b39);
-#endif
-
- } else
- purple_debug_warning("oscar", "sendmemblock: unknown hash request\n");
-
- }
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0020, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0020, snacid, &bs);
-
- byte_stream_destroy(&bs);
-
- return 0;
-}
-
/*
* Subtype 0x0021 - Receive our extended status
*
@@ -1066,26 +919,27 @@ aim_parse_extstatus(OscarData *od, FlapConnection *conn, aim_module_t *mod, Flap
aim_srv_requestnew(od, SNAC_FAMILY_BART);
} else {
PurpleAccount *account = purple_connection_get_account(od->gc);
- PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account);
+ PurpleImage *img = purple_buddy_icons_find_account_icon(account);
if (img == NULL) {
aim_ssi_delicon(od);
} else {
purple_debug_info("oscar",
"Uploading icon to icon server\n");
- aim_bart_upload(od, purple_imgstore_get_data(img),
- purple_imgstore_get_size(img));
- purple_imgstore_unref(img);
+ aim_bart_upload(od,
+ purple_image_get_data(img),
+ purple_image_get_size(img));
+ g_object_unref(img);
}
}
} else if (flags == 0x81) {
PurpleAccount *account = purple_connection_get_account(od->gc);
- PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account);
+ PurpleImage *img = purple_buddy_icons_find_account_icon(account);
if (img == NULL)
aim_ssi_delicon(od);
else {
aim_ssi_seticon(od, md5, length);
- purple_imgstore_unref(img);
+ g_object_unref(img);
}
}
@@ -1120,8 +974,6 @@ snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
return motd(od, conn, mod, frame, snac, bs);
else if (snac->subtype == 0x0018)
return hostversions(od, conn, mod, frame, snac, bs);
- else if (snac->subtype == 0x001f)
- return memrequest(od, conn, mod, frame, snac, bs);
else if (snac->subtype == 0x0021)
return aim_parse_extstatus(od, conn, mod, frame, snac, bs);
diff --git a/libpurple/protocols/oscar/flap_connection.c b/libpurple/protocols/oscar/flap_connection.c
index 3aa62973d2..b84faa6d0d 100644
--- a/libpurple/protocols/oscar/flap_connection.c
+++ b/libpurple/protocols/oscar/flap_connection.c
@@ -29,10 +29,6 @@
#include <netinet/in.h>
#endif
-#ifdef _WIN32
-#include "win32dep.h"
-#endif
-
/**
* This sends a channel 1 SNAC containing the FLAP version.
* The FLAP version is sent by itself at the beginning of every
@@ -336,7 +332,7 @@ flap_connection_new(OscarData *od, int type)
conn = g_new0(FlapConnection, 1);
conn->od = od;
- conn->buffer_outgoing = purple_circ_buffer_new(0);
+ conn->buffer_outgoing = purple_circular_buffer_new(0);
conn->fd = -1;
conn->subtype = -1;
conn->type = type;
@@ -410,7 +406,7 @@ flap_connection_close(OscarData *od, FlapConnection *conn)
g_free(conn->buffer_incoming.data.data);
conn->buffer_incoming.data.data = NULL;
- purple_circ_buffer_destroy(conn->buffer_outgoing);
+ g_object_unref(G_OBJECT(conn->buffer_outgoing));
conn->buffer_outgoing = NULL;
}
@@ -456,7 +452,7 @@ flap_connection_destroy_cb(gpointer data)
* TODO: If we don't have a SNAC_FAMILY_LOCATE connection then
* we should try to request one instead of disconnecting.
*/
- if (!account->disconnecting && ((od->oscar_connections == NULL)
+ if (!purple_account_is_disconnecting(account) && ((od->oscar_connections == NULL)
|| (!flap_connection_getbytype(od, SNAC_FAMILY_LOCATE))))
{
/* No more FLAP connections! Sign off this PurpleConnection! */
@@ -467,7 +463,7 @@ flap_connection_destroy_cb(gpointer data)
reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE;
tmp = g_strdup(_("You have signed on from another location"));
if (!purple_account_get_remember_password(account))
- purple_account_set_password(account, NULL);
+ purple_account_set_password(account, NULL, NULL, NULL);
} else if (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_CLOSED)
tmp = g_strdup(_("Server closed the connection"));
else if (conn->disconnect_reason == OSCAR_DISCONNECT_LOST_CONNECTION)
@@ -487,7 +483,7 @@ flap_connection_destroy_cb(gpointer data)
if (tmp != NULL)
{
- purple_connection_error_reason(od->gc, reason, tmp);
+ purple_connection_error(od->gc, reason, tmp);
g_free(tmp);
}
}
@@ -921,7 +917,7 @@ flap_connection_recv(FlapConnection *conn)
OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
break;
}
- conn->od->gc->last_received = time(NULL);
+ purple_connection_update_last_received(conn->od->gc);
/* If we don't even have a complete FLAP header then do nothing */
conn->header_received += read;
@@ -1020,9 +1016,11 @@ send_cb(gpointer data, gint source, PurpleInputCondition cond)
{
FlapConnection *conn;
int writelen, ret;
+ const gchar *output = NULL;
conn = data;
- writelen = purple_circ_buffer_get_max_read(conn->buffer_outgoing);
+ writelen = purple_circular_buffer_get_max_read(conn->buffer_outgoing);
+ output = purple_circular_buffer_get_output(conn->buffer_outgoing);
if (writelen == 0)
{
@@ -1032,10 +1030,9 @@ send_cb(gpointer data, gint source, PurpleInputCondition cond)
}
if (conn->gsc)
- ret = purple_ssl_write(conn->gsc, conn->buffer_outgoing->outptr,
- writelen);
+ ret = purple_ssl_write(conn->gsc, output, writelen);
else
- ret = send(conn->fd, conn->buffer_outgoing->outptr, writelen, 0);
+ ret = send(conn->fd, output, writelen, 0);
if (ret <= 0)
{
if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
@@ -1057,7 +1054,7 @@ send_cb(gpointer data, gint source, PurpleInputCondition cond)
return;
}
- purple_circ_buffer_mark_read(conn->buffer_outgoing, ret);
+ purple_circular_buffer_mark_read(conn->buffer_outgoing, ret);
}
static void
@@ -1074,7 +1071,7 @@ flap_connection_send_byte_stream(ByteStream *bs, FlapConnection *conn, size_t co
return;
/* Add everything to our outgoing buffer */
- purple_circ_buffer_append(conn->buffer_outgoing, bs->data, count);
+ purple_circular_buffer_append(conn->buffer_outgoing, bs->data, count);
/* If we haven't already started writing stuff, then start the cycle */
if (conn->watcher_outgoing == 0)
diff --git a/libpurple/protocols/oscar/libaim.c b/libpurple/protocols/oscar/libaim.c
index 0a169e9534..6d65906486 100644
--- a/libpurple/protocols/oscar/libaim.c
+++ b/libpurple/protocols/oscar/libaim.c
@@ -29,7 +29,8 @@
static PurplePluginProtocolInfo prpl_info =
{
- OPT_PROTO_MAIL_CHECK | OPT_PROTO_IM_IMAGE | OPT_PROTO_INVITE_MESSAGE,
+ sizeof(PurplePluginProtocolInfo), /* struct_size */
+ OPT_PROTO_MAIL_CHECK | OPT_PROTO_INVITE_MESSAGE | OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE,
NULL, /* user_splits */
NULL, /* protocol_options */
{"gif,jpeg,bmp,ico", 0, 0, 64, 64, 7168, PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
@@ -50,7 +51,7 @@ static PurplePluginProtocolInfo prpl_info =
oscar_set_status, /* set_status */
oscar_set_idle, /* set_idle */
oscar_change_passwd, /* change_passwd */
- NULL, /* add_buddy */
+ oscar_add_buddy, /* add_buddy */
NULL, /* add_buddies */
oscar_remove_buddy, /* remove_buddy */
NULL, /* remove_buddies */
@@ -64,12 +65,10 @@ static PurplePluginProtocolInfo prpl_info =
oscar_get_chat_name, /* get_chat_name */
oscar_chat_invite, /* chat_invite */
oscar_chat_leave, /* chat_leave */
- NULL, /* chat_whisper */
oscar_send_chat, /* chat_send */
oscar_keepalive, /* keepalive */
NULL, /* register_user */
NULL, /* get_cb_info */
- NULL, /* get_cb_away */
oscar_alias_buddy, /* alias_buddy */
oscar_move_buddy, /* group_buddy */
oscar_rename_group, /* rename_group */
@@ -94,15 +93,13 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* unregister_user */
NULL, /* send_attention */
NULL, /* get_attention_types */
- sizeof(PurplePluginProtocolInfo), /* struct_size */
NULL, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* get_media_caps */
NULL, /* get_moods */
NULL, /* set_public_alias */
NULL, /* get_public_alias */
- oscar_add_buddy, /* add_buddy_with_invite */
- NULL /* add_buddies_with_invite */
+ oscar_get_max_message_size /* get_max_message_size */
};
static PurplePluginInfo info =
diff --git a/libpurple/protocols/oscar/libicq.c b/libpurple/protocols/oscar/libicq.c
index 18f5451e59..b651747e1b 100644
--- a/libpurple/protocols/oscar/libicq.c
+++ b/libpurple/protocols/oscar/libicq.c
@@ -36,9 +36,17 @@ icq_get_account_text_table(PurpleAccount *account)
return table;
}
+static gssize
+icq_get_max_message_size(PurpleConversation *conv)
+{
+ /* XXX: got from pidgin-otr - verify and document it */
+ return 2346;
+}
+
static PurplePluginProtocolInfo prpl_info =
{
- OPT_PROTO_MAIL_CHECK | OPT_PROTO_IM_IMAGE | OPT_PROTO_INVITE_MESSAGE,
+ sizeof(PurplePluginProtocolInfo), /* struct_size */
+ OPT_PROTO_MAIL_CHECK | OPT_PROTO_INVITE_MESSAGE | OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE,
NULL, /* user_splits */
NULL, /* protocol_options */
{"gif,jpeg,bmp,ico", 0, 0, 64, 64, 7168, PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
@@ -59,7 +67,7 @@ static PurplePluginProtocolInfo prpl_info =
oscar_set_status, /* set_status */
oscar_set_idle, /* set_idle */
oscar_change_passwd, /* change_passwd */
- NULL, /* add_buddy */
+ oscar_add_buddy, /* add_buddy */
NULL, /* add_buddies */
oscar_remove_buddy, /* remove_buddy */
NULL, /* remove_buddies */
@@ -73,12 +81,10 @@ static PurplePluginProtocolInfo prpl_info =
oscar_get_chat_name, /* get_chat_name */
oscar_chat_invite, /* chat_invite */
oscar_chat_leave, /* chat_leave */
- NULL, /* chat_whisper */
oscar_send_chat, /* chat_send */
oscar_keepalive, /* keepalive */
NULL, /* register_user */
NULL, /* get_cb_info */
- NULL, /* get_cb_away */
oscar_alias_buddy, /* alias_buddy */
oscar_move_buddy, /* group_buddy */
oscar_rename_group, /* rename_group */
@@ -103,16 +109,13 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* unregister_user */
NULL, /* send_attention */
NULL, /* get_attention_types */
-
- sizeof(PurplePluginProtocolInfo), /* struct_size */
icq_get_account_text_table, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* can_do_media */
oscar_get_purple_moods, /* get_moods */
NULL, /* set_public_alias */
NULL, /* get_public_alias */
- oscar_add_buddy, /* add_buddy_with_invite */
- NULL /* add_buddies_with_invite */
+ icq_get_max_message_size /* get_max_message_size */
};
static PurplePluginInfo info =
diff --git a/libpurple/protocols/oscar/odc.c b/libpurple/protocols/oscar/odc.c
index 17454d86ea..af81c4e8b4 100644
--- a/libpurple/protocols/oscar/odc.c
+++ b/libpurple/protocols/oscar/odc.c
@@ -25,7 +25,7 @@
/* From Purple */
#include "conversation.h"
-#include "imgstore.h"
+#include "image-store.h"
#include "util.h"
#define DIRECTIM_MAX_FILESIZE 52428800
@@ -60,11 +60,12 @@ peer_odc_close(PeerConnection *conn)
if (tmp != NULL)
{
PurpleAccount *account;
- PurpleConversation *conv;
+ PurpleIMConversation *im;
account = purple_connection_get_account(conn->od->gc);
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
- purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ im = purple_im_conversation_new(account, conn->bn);
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(im), tmp, 0);
g_free(tmp);
}
@@ -149,16 +150,16 @@ peer_odc_send_cookie(PeerConnection *conn)
* Send client-to-client typing notification over an established direct connection.
*/
void
-peer_odc_send_typing(PeerConnection *conn, PurpleTypingState typing)
+peer_odc_send_typing(PeerConnection *conn, PurpleIMTypingState typing)
{
OdcFrame frame;
memset(&frame, 0, sizeof(OdcFrame));
frame.type = 0x0001;
frame.subtype = 0x0006;
- if (typing == PURPLE_TYPING)
+ if (typing == PURPLE_IM_TYPING)
frame.flags = 0x0002 | 0x0008;
- else if (typing == PURPLE_TYPED)
+ else if (typing == PURPLE_IM_TYPED)
frame.flags = 0x0002 | 0x0004;
else
frame.flags = 0x0002;
@@ -209,10 +210,10 @@ struct embedded_data
* function is passed a long chunk of data which contains the IM with any
* data chunks (images) appended to it.
*
- * This function rips out all the data chunks and creates an imgstore for
+ * This function rips out all the data chunks and creates a PurpleImage (?) for
* each one. In order to do this, it first goes through the IM and takes
* out all the IMG tags. When doing so, it rewrites the original IMG tag
- * with one compatible with the imgstore Purple core code. For each one, we
+ * with one compatible with the PurpleImage (?) code. For each one, we
* then read in chunks of data from the end of the message and actually
* create the img store using the given data.
*
@@ -243,7 +244,7 @@ peer_odc_handle_payload(PeerConnection *conn, const char *msg, size_t len, int e
GData *attributes;
GHashTable *embedded_datas;
struct embedded_data *embedded_data;
- GSList *images;
+ gboolean any_images = FALSE;
gchar *utf8;
GString *newmsg;
PurpleMessageFlags imflags;
@@ -331,12 +332,11 @@ peer_odc_handle_payload(PeerConnection *conn, const char *msg, size_t len, int e
* Loop through the message, replacing OSCAR img tags with the
* equivalent Purple img tag.
*/
- images = NULL;
newmsg = g_string_new("");
tmp = msg;
while (purple_markup_find_tag("img", tmp, &start, &end, &attributes))
{
- int imgid = 0;
+ PurpleImage *image = NULL;
idstr = g_datalist_get_data(&attributes, "id");
src = g_datalist_get_data(&attributes, "src");
@@ -354,10 +354,10 @@ peer_odc_handle_payload(PeerConnection *conn, const char *msg, size_t len, int e
if ((embedded_data != NULL) && (embedded_data->size == size))
{
- imgid = purple_imgstore_add_with_id(g_memdup(embedded_data->data, size), size, src);
-
- /* Record the image number */
- images = g_slist_append(images, GINT_TO_POINTER(imgid));
+ image = purple_image_new_from_data(
+ g_memdup(embedded_data->data, size),
+ size);
+ purple_image_set_friendly_filename(image, src);
}
}
@@ -371,10 +371,17 @@ peer_odc_handle_payload(PeerConnection *conn, const char *msg, size_t len, int e
g_free(utf8);
}
- if (imgid != 0)
+ if (image)
{
+ guint img_id;
+
+ img_id = purple_image_store_add_temporary(image);
+ g_object_unref(image);
+ any_images = TRUE;
+
/* Write the new image tag */
- g_string_append_printf(newmsg, "<IMG ID=\"%d\">", imgid);
+ g_string_append_printf(newmsg, "<img src=\""
+ PURPLE_IMAGE_STORE_PROTOCOL "%u\">", img_id);
}
/* Continue from the end of the tag */
@@ -393,22 +400,13 @@ peer_odc_handle_payload(PeerConnection *conn, const char *msg, size_t len, int e
/* Display the message we received */
imflags = 0;
- if (images != NULL)
+ if (any_images)
imflags |= PURPLE_MESSAGE_IMAGES;
if (autoreply)
imflags |= PURPLE_MESSAGE_AUTO_RESP;
- serv_got_im(gc, conn->bn, newmsg->str, imflags, time(NULL));
+ purple_serv_got_im(gc, conn->bn, newmsg->str, imflags, time(NULL));
g_string_free(newmsg, TRUE);
- /* unref any images we allocated */
- if (images)
- {
- GSList *l;
- for (l = images; l != NULL; l = l->next)
- purple_imgstore_unref_by_id(GPOINTER_TO_INT(l->data));
- g_slist_free(images);
- }
-
/* Delete our list of pointers to embedded images */
g_hash_table_destroy(embedded_datas);
}
@@ -518,7 +516,7 @@ peer_odc_recv_frame(PeerConnection *conn, ByteStream *bs)
*/
PurpleAccount *account;
- PurpleConversation *conv;
+ PurpleIMConversation *im;
if (conn->flags & PEER_CONNECTION_FLAG_IS_INCOMING)
{
@@ -559,9 +557,9 @@ peer_odc_recv_frame(PeerConnection *conn, ByteStream *bs)
/* Tell the local user that we are connected */
account = purple_connection_get_account(gc);
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
- purple_conversation_write(conv, NULL, _("Direct IM established"),
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ im = purple_im_conversation_new(account, conn->bn);
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(im), _("Direct IM established"), 0);
}
if ((frame->type != 0x0001) && (frame->subtype != 0x0006))
@@ -578,15 +576,15 @@ peer_odc_recv_frame(PeerConnection *conn, ByteStream *bs)
purple_debug_info("oscar", "ohmigod! %s has started typing "
"(DirectIM). He's going to send you a message! "
"*squeal*\n", conn->bn);
- serv_got_typing(gc, conn->bn, 0, PURPLE_TYPING);
+ purple_serv_got_typing(gc, conn->bn, 0, PURPLE_IM_TYPING);
}
else if (frame->flags & 0x0004)
{
- serv_got_typing(gc, conn->bn, 0, PURPLE_TYPED);
+ purple_serv_got_typing(gc, conn->bn, 0, PURPLE_IM_TYPED);
}
else
{
- serv_got_typing_stopped(gc, conn->bn);
+ purple_serv_got_typing_stopped(gc, conn->bn);
}
if (frame->payload.len > 0)
@@ -595,7 +593,7 @@ peer_odc_recv_frame(PeerConnection *conn, ByteStream *bs)
{
gchar *tmp, *size1, *size2;
PurpleAccount *account;
- PurpleConversation *conv;
+ PurpleIMConversation *im;
size1 = purple_str_size_to_units(frame->payload.len);
size2 = purple_str_size_to_units(DIRECTIM_MAX_FILESIZE);
@@ -604,8 +602,9 @@ peer_odc_recv_frame(PeerConnection *conn, ByteStream *bs)
g_free(size2);
account = purple_connection_get_account(conn->od->gc);
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
- purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ im = purple_im_conversation_new(account, conn->bn);
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(im), tmp, 0);
g_free(tmp);
peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
diff --git a/libpurple/protocols/oscar/oft.c b/libpurple/protocols/oscar/oft.c
index 2b99ae4212..ce5b929742 100644
--- a/libpurple/protocols/oscar/oft.c
+++ b/libpurple/protocols/oscar/oft.c
@@ -360,12 +360,12 @@ start_transfer_when_done_sending_data(gpointer data)
conn = data;
- if (purple_circ_buffer_get_max_read(conn->buffer_outgoing) == 0)
+ if (purple_circular_buffer_get_max_read(conn->buffer_outgoing) == 0)
{
+ int fd = conn->fd;
conn->sending_data_timer = 0;
- conn->xfer->fd = conn->fd;
conn->fd = -1;
- purple_xfer_start(conn->xfer, conn->xfer->fd, NULL, 0);
+ purple_xfer_start(conn->xfer, fd, NULL, 0);
return FALSE;
}
@@ -385,7 +385,7 @@ destroy_connection_when_done_sending_data(gpointer data)
conn = data;
- if (purple_circ_buffer_get_max_read(conn->buffer_outgoing) == 0)
+ if (purple_circular_buffer_get_max_read(conn->buffer_outgoing) == 0)
{
conn->sending_data_timer = 0;
peer_connection_destroy(conn, conn->disconnect_reason, NULL);
@@ -508,7 +508,7 @@ peer_oft_recv_frame_done(PeerConnection *conn, OftFrame *frame)
purple_input_remove(conn->watcher_incoming);
conn->watcher_incoming = 0;
- conn->xfer->fd = conn->fd;
+ purple_xfer_set_fd(conn->xfer, conn->fd);
conn->fd = -1;
conn->disconnect_reason = OSCAR_DISCONNECT_DONE;
peer_connection_schedule_destroy(conn, conn->disconnect_reason, NULL);
@@ -589,7 +589,7 @@ peer_oft_recvcb_init(PurpleXfer *xfer)
{
PeerConnection *conn;
- conn = xfer->data;
+ conn = purple_xfer_get_protocol_data(xfer);
conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
peer_connection_trynext(conn);
}
@@ -599,11 +599,11 @@ peer_oft_recvcb_end(PurpleXfer *xfer)
{
PeerConnection *conn;
- conn = xfer->data;
+ conn = purple_xfer_get_protocol_data(xfer);
/* Tell the other person that we've received everything */
- conn->fd = conn->xfer->fd;
- conn->xfer->fd = -1;
+ conn->fd = purple_xfer_get_fd(conn->xfer);
+ purple_xfer_set_fd(conn->xfer, -1);
peer_oft_send_done(conn);
conn->disconnect_reason = OSCAR_DISCONNECT_DONE;
@@ -617,7 +617,7 @@ peer_oft_recvcb_ack_recv(PurpleXfer *xfer, const guchar *buffer, size_t size)
PeerConnection *conn;
/* Update our rolling checksum. Like Walmart, yo. */
- conn = xfer->data;
+ conn = purple_xfer_get_protocol_data(xfer);
conn->xferdata.recvcsum = peer_oft_checksum_chunk(buffer,
size, conn->xferdata.recvcsum, purple_xfer_get_bytes_sent(xfer) & 1);
}
@@ -651,9 +651,9 @@ void
peer_oft_sendcb_init(PurpleXfer *xfer)
{
PeerConnection *conn;
- size_t size;
+ goffset size;
- conn = xfer->data;
+ conn = purple_xfer_get_protocol_data(xfer);
conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
/* Make sure the file size can be represented in 32 bits */
@@ -665,9 +665,9 @@ peer_oft_sendcb_init(PurpleXfer *xfer)
size2 = purple_str_size_to_units(G_MAXUINT32);
tmp = g_strdup_printf(_("File %s is %s, which is larger than "
"the maximum size of %s."),
- xfer->local_filename, size1, size2);
- purple_xfer_error(purple_xfer_get_type(xfer),
- purple_xfer_get_account(xfer), xfer->who, tmp);
+ purple_xfer_get_local_filename(xfer), size1, size2);
+ purple_xfer_error(purple_xfer_get_xfer_type(xfer),
+ purple_xfer_get_account(xfer), purple_xfer_get_remote_user(xfer), tmp);
g_free(size1);
g_free(size2);
g_free(tmp);
@@ -689,9 +689,9 @@ peer_oft_sendcb_init(PurpleXfer *xfer)
strncpy((gchar *)conn->xferdata.idstring, "Cool FileXfer", 31);
conn->xferdata.modtime = 0;
conn->xferdata.cretime = 0;
- xfer->filename = g_path_get_basename(xfer->local_filename);
- conn->xferdata.name_length = MAX(64, strlen(xfer->filename) + 1);
- conn->xferdata.name = (guchar *)g_strndup(xfer->filename, conn->xferdata.name_length - 1);
+ purple_xfer_set_filename(xfer, g_path_get_basename(purple_xfer_get_local_filename(xfer)));
+ conn->xferdata.name_length = MAX(64, strlen(purple_xfer_get_filename(xfer)) + 1);
+ conn->xferdata.name = (guchar *)g_strndup(purple_xfer_get_filename(xfer), conn->xferdata.name_length - 1);
peer_oft_checksum_file(conn, xfer,
peer_oft_checksum_calculated_cb, G_MAXUINT32);
@@ -713,7 +713,7 @@ peer_oft_sendcb_ack(PurpleXfer *xfer, const guchar *buffer, size_t size)
{
PeerConnection *conn;
- conn = xfer->data;
+ conn = purple_xfer_get_protocol_data(xfer);
/*
* If we're done sending, intercept the socket from the core ft code
@@ -721,9 +721,9 @@ peer_oft_sendcb_ack(PurpleXfer *xfer, const guchar *buffer, size_t size)
*/
if (purple_xfer_get_bytes_remaining(xfer) <= 0)
{
- purple_input_remove(xfer->watcher);
- conn->fd = xfer->fd;
- xfer->fd = -1;
+ purple_input_remove(purple_xfer_get_watcher(xfer));
+ conn->fd = purple_xfer_get_fd(xfer);
+ purple_xfer_set_fd(xfer, -1);
conn->watcher_incoming = purple_input_add(conn->fd,
PURPLE_INPUT_READ, peer_connection_recv_cb, conn);
}
@@ -742,7 +742,7 @@ peer_oft_cb_generic_cancel(PurpleXfer *xfer)
{
PeerConnection *conn;
- conn = xfer->data;
+ conn = purple_xfer_get_protocol_data(xfer);
if (conn == NULL)
return;
diff --git a/libpurple/protocols/oscar/oscar.c b/libpurple/protocols/oscar/oscar.c
index 6b7ba92cef..a6684b7682 100644
--- a/libpurple/protocols/oscar/oscar.c
+++ b/libpurple/protocols/oscar/oscar.c
@@ -33,15 +33,14 @@
#include "account.h"
#include "accountopt.h"
#include "buddyicon.h"
-#include "cipher.h"
+#include "ciphers/md5hash.h"
#include "conversation.h"
#include "core.h"
#include "debug.h"
#include "encoding.h"
-#include "imgstore.h"
+#include "image-store.h"
#include "network.h"
#include "notify.h"
-#include "privacy.h"
#include "prpl.h"
#include "proxy.h"
#include "request.h"
@@ -53,10 +52,6 @@
#include "oscar.h"
#include "peer.h"
-#define AIMHASHDATA "http://pidgin.im/aim_data.php3"
-
-#define OSCAR_CONNECT_STEPS 6
-
static guint64 purple_caps =
OSCAR_CAPABILITY_CHAT
| OSCAR_CAPABILITY_BUDDYICON
@@ -89,7 +84,6 @@ struct oscar_ask_directim_data
/* Only used when connecting with the old-style BUCP login */
static int purple_parse_auth_resp (OscarData *, FlapConnection *, FlapFrame *, ...);
-static int purple_parse_login (OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_parse_auth_securid_request(OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_handle_redirect (OscarData *, FlapConnection *, FlapFrame *, ...);
@@ -102,10 +96,10 @@ static int purple_parse_misses (OscarData *, FlapConnection *, FlapFrame *,
static int purple_parse_clientauto (OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_parse_motd (OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_chatnav_info (OscarData *, FlapConnection *, FlapFrame *, ...);
-static int purple_conv_chat_join (OscarData *, FlapConnection *, FlapFrame *, ...);
-static int purple_conv_chat_leave (OscarData *, FlapConnection *, FlapFrame *, ...);
-static int purple_conv_chat_info_update (OscarData *, FlapConnection *, FlapFrame *, ...);
-static int purple_conv_chat_incoming_msg(OscarData *, FlapConnection *, FlapFrame *, ...);
+static int purple_chat_conversation_join (OscarData *, FlapConnection *, FlapFrame *, ...);
+static int purple_chat_conversation_left (OscarData *, FlapConnection *, FlapFrame *, ...);
+static int purple_chat_conversation_info_update (OscarData *, FlapConnection *, FlapFrame *, ...);
+static int purple_chat_conversation_incoming_msg(OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_email_parseupdate(OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_icon_parseicon (OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_parse_searcherror(OscarData *, FlapConnection *, FlapFrame *, ...);
@@ -116,7 +110,6 @@ static int purple_parse_mtn (OscarData *, FlapConnection *, FlapFrame *,
static int purple_parse_locaterights(OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_parse_buddyrights(OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_parse_genericerr (OscarData *, FlapConnection *, FlapFrame *, ...);
-static int purple_memrequest (OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_selfinfo (OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_popup (OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_ssi_parseerr (OscarData *, FlapConnection *, FlapFrame *, ...);
@@ -249,7 +242,7 @@ find_oscar_chat_by_conn(PurpleConnection *gc, FlapConnection *conn)
}
static struct chat_connection *
-find_oscar_chat_by_conv(PurpleConnection *gc, PurpleConversation *conv)
+find_oscar_chat_by_conv(PurpleConnection *gc, PurpleChatConversation *conv)
{
OscarData *od = purple_connection_get_protocol_data(gc);
GSList *cur;
@@ -279,7 +272,7 @@ oscar_chat_kill(PurpleConnection *gc, struct chat_connection *cc)
OscarData *od = purple_connection_get_protocol_data(gc);
/* Notify the conversation window that we've left the chat */
- serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(cc->conv)));
+ purple_serv_got_chat_left(gc, purple_chat_conversation_get_id(cc->conv));
/* Destroy the chat_connection */
od->oscar_chats = g_slist_remove(od->oscar_chats, cc);
@@ -308,7 +301,7 @@ connection_common_error_cb(FlapConnection *conn, const gchar *error_message)
gchar *msg;
msg = g_strdup_printf(_("Unable to connect to authentication server: %s"),
error_message);
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
g_free(msg);
}
else if (conn->type == SNAC_FAMILY_LOCATE)
@@ -316,7 +309,7 @@ connection_common_error_cb(FlapConnection *conn, const gchar *error_message)
gchar *msg;
msg = g_strdup_printf(_("Unable to connect to BOS server: %s"),
error_message);
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
g_free(msg);
}
else
@@ -514,7 +507,7 @@ flap_connection_established_chat(OscarData *od, FlapConnection *conn)
chatcon = find_oscar_chat_by_conn(gc, conn);
if (chatcon) {
chatcon->id = id;
- chatcon->conv = serv_got_joined_chat(gc, id++, chatcon->show);
+ chatcon->conv = purple_serv_got_joined_chat(gc, id++, chatcon->show);
}
}
@@ -578,7 +571,7 @@ idle_reporting_pref_cb(const char *name, PurplePrefType type,
gc = data;
od = purple_connection_get_protocol_data(gc);
report_idle = strcmp((const char *)value, "none") != 0;
- presence = aim_ssi_getpresence(od->ssi.local);
+ presence = aim_ssi_getpresence(&od->ssi.local);
if (report_idle)
aim_ssi_setpresence(od, presence | AIM_SSI_PRESENCE_FLAG_SHOWIDLE);
@@ -600,7 +593,7 @@ recent_buddies_pref_cb(const char *name, PurplePrefType type,
gc = data;
od = purple_connection_get_protocol_data(gc);
- presence = aim_ssi_getpresence(od->ssi.local);
+ presence = aim_ssi_getpresence(&od->ssi.local);
if (value)
aim_ssi_setpresence(od, presence & ~AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES);
@@ -636,15 +629,6 @@ compare_handlers(gconstpointer a, gconstpointer b)
return subtype1 - subtype2;
}
-#if !GLIB_CHECK_VERSION(2,14,0)
-static void hash_table_get_list_of_keys(gpointer key, gpointer value, gpointer user_data)
-{
- GList **handlers = (GList **)user_data;
-
- *handlers = g_list_prepend(*handlers, key);
-}
-#endif /* GLIB < 2.14.0 */
-
void
oscar_login(PurpleAccount *account)
{
@@ -655,6 +639,7 @@ oscar_login(PurpleAccount *account)
GList *sorted_handlers;
GList *cur;
GString *msg = g_string_new("");
+ PurpleConnectionFlags flags;
gc = purple_account_get_connection(account);
od = oscar_data_new();
@@ -672,7 +657,6 @@ oscar_login(PurpleAccount *account)
/* These are only needed when connecting with the old-style BUCP login */
oscar_data_addhandler(od, SNAC_FAMILY_AUTH, 0x0003, purple_parse_auth_resp, 0);
- oscar_data_addhandler(od, SNAC_FAMILY_AUTH, 0x0007, purple_parse_login, 0);
oscar_data_addhandler(od, SNAC_FAMILY_AUTH, SNAC_SUBTYPE_AUTH_SECURID_REQUEST, purple_parse_auth_securid_request, 0);
oscar_data_addhandler(od, SNAC_FAMILY_BART, SNAC_SUBTYPE_BART_RESPONSE, purple_icon_parseicon, 0);
@@ -683,10 +667,10 @@ oscar_login(PurpleAccount *account)
oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, SNAC_SUBTYPE_BUDDY_ONCOMING, purple_parse_oncoming, 0);
oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, SNAC_SUBTYPE_BUDDY_OFFGOING, purple_parse_offgoing, 0);
oscar_data_addhandler(od, SNAC_FAMILY_CHAT, 0x0001, purple_parse_genericerr, 0);
- oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_USERJOIN, purple_conv_chat_join, 0);
- oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_USERLEAVE, purple_conv_chat_leave, 0);
- oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_ROOMINFOUPDATE, purple_conv_chat_info_update, 0);
- oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_INCOMINGMSG, purple_conv_chat_incoming_msg, 0);
+ oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_USERJOIN, purple_chat_conversation_join, 0);
+ oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_USERLEAVE, purple_chat_conversation_left, 0);
+ oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_ROOMINFOUPDATE, purple_chat_conversation_info_update, 0);
+ oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_INCOMINGMSG, purple_chat_conversation_incoming_msg, 0);
oscar_data_addhandler(od, SNAC_FAMILY_CHATNAV, 0x0001, purple_parse_genericerr, 0);
oscar_data_addhandler(od, SNAC_FAMILY_CHATNAV, SNAC_SUBTYPE_CHATNAV_INFO, purple_chatnav_info, 0);
oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_ERROR, purple_ssi_parseerr, 0);
@@ -706,7 +690,6 @@ oscar_login(PurpleAccount *account)
oscar_data_addhandler(od, SNAC_FAMILY_LOCATE, SNAC_SUBTYPE_LOCATE_RIGHTSINFO, purple_parse_locaterights, 0);
oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x0001, purple_parse_genericerr, 0);
oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x000f, purple_selfinfo, 0);
- oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x001f, purple_memrequest, 0);
oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, SNAC_SUBTYPE_OSERVICE_REDIRECT, purple_handle_redirect, 0);
oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, SNAC_SUBTYPE_OSERVICE_MOTD, purple_parse_motd, 0);
oscar_data_addhandler(od, SNAC_FAMILY_POPUP, 0x0002, purple_popup, 0);
@@ -714,12 +697,7 @@ oscar_login(PurpleAccount *account)
oscar_data_addhandler(od, SNAC_FAMILY_USERLOOKUP, 0x0003, purple_parse_searchreply, 0);
g_string_append(msg, "Registered handlers: ");
-#if GLIB_CHECK_VERSION(2,14,0)
handlers = g_hash_table_get_keys(od->handlerlist);
-#else
- handlers = NULL;
- g_hash_table_foreach(od->handlerlist, hash_table_get_list_of_keys, &handlers);
-#endif /* GLIB < 2.14.0 */
sorted_handlers = g_list_sort(g_list_copy(handlers), compare_handlers);
for (cur = sorted_handlers; cur; cur = cur->next) {
guint x = GPOINTER_TO_UINT(cur->data);
@@ -735,28 +713,30 @@ oscar_login(PurpleAccount *account)
if (!oscar_util_valid_name(purple_account_get_username(account))) {
gchar *buf;
buf = g_strdup_printf(_("Unable to sign on as %s because the username is invalid. Usernames must be a valid email address, or start with a letter and contain only letters, numbers and spaces, or contain only numbers."), purple_account_get_username(account));
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, buf);
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, buf);
g_free(buf);
return;
}
- gc->flags |= PURPLE_CONNECTION_HTML;
+ flags = PURPLE_CONNECTION_FLAG_HTML;
if (g_str_equal(purple_account_get_protocol_id(account), "prpl-icq")) {
od->icq = TRUE;
} else {
- gc->flags |= PURPLE_CONNECTION_AUTO_RESP;
+ flags |= PURPLE_CONNECTION_FLAG_AUTO_RESP;
}
/* Set this flag based on the protocol_id rather than the username,
because that is what's tied to the get_moods prpl callback. */
if (g_str_equal(purple_account_get_protocol_id(account), "prpl-icq"))
- gc->flags |= PURPLE_CONNECTION_SUPPORT_MOODS;
+ flags |= PURPLE_CONNECTION_FLAG_SUPPORT_MOODS;
+
+ purple_connection_set_flags(gc, flags);
od->default_port = purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT);
encryption_type = purple_account_get_string(account, "encryption", OSCAR_DEFAULT_ENCRYPTION);
if (!purple_ssl_is_supported() && strcmp(encryption_type, OSCAR_REQUIRE_ENCRYPTION) == 0) {
- purple_connection_error_reason(
+ purple_connection_error(
gc,
PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
_("You required encryption in your account settings, but encryption is not supported by your system."));
@@ -825,7 +805,7 @@ oscar_login(PurpleAccount *account)
}
if (newconn->gsc == NULL && newconn->connect_data == NULL) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to connect"));
return;
}
@@ -862,162 +842,6 @@ oscar_close(PurpleConnection *gc)
purple_debug_info("oscar", "Signed off.\n");
}
-/* XXX - Should use purple_util_fetch_url for the below stuff */
-struct pieceofcrap {
- PurpleConnection *gc;
- unsigned long offset;
- unsigned long len;
- char *modname;
- int fd;
- FlapConnection *conn;
- unsigned int inpa;
-};
-
-static void damn_you(gpointer data, gint source, PurpleInputCondition c)
-{
- struct pieceofcrap *pos = data;
- OscarData *od = purple_connection_get_protocol_data(pos->gc);
- char in = '\0';
- int x = 0;
- unsigned char m[17];
- GString *msg;
-
- while (read(pos->fd, &in, 1) == 1) {
- if (in == '\n')
- x++;
- else if (in != '\r')
- x = 0;
- if (x == 2)
- break;
- in = '\0';
- }
- if (in != '\n') {
- char buf[256];
- g_snprintf(buf, sizeof(buf), _("You may be disconnected shortly. "
- "If so, check %s for updates."),
- oscar_get_ui_info_string("website", PURPLE_WEBSITE));
- purple_notify_warning(pos->gc, NULL,
- _("Unable to get a valid AIM login hash."),
- buf);
- purple_input_remove(pos->inpa);
- close(pos->fd);
- g_free(pos);
- return;
- }
- if (read(pos->fd, m, 16) != 16)
- {
- purple_debug_warning("oscar", "Could not read full AIM login hash "
- "from " AIMHASHDATA "--that's bad.\n");
- }
- m[16] = '\0';
-
- msg = g_string_new("Sending hash: ");
- for (x = 0; x < 16; x++)
- g_string_append_printf(msg, "%02hhx ", (unsigned char)m[x]);
- g_string_append(msg, "\n");
- purple_debug_misc("oscar", "%s", msg->str);
- g_string_free(msg, TRUE);
-
- purple_input_remove(pos->inpa);
- close(pos->fd);
- aim_sendmemblock(od, pos->conn, 0, 16, m, AIM_SENDMEMBLOCK_FLAG_ISHASH);
- g_free(pos);
-}
-
-static void
-straight_to_hell(gpointer data, gint source, const gchar *error_message)
-{
- struct pieceofcrap *pos = data;
- gchar *buf;
- gssize result;
-
- pos->fd = source;
-
- if (source < 0) {
- buf = g_strdup_printf(_("You may be disconnected shortly. "
- "If so, check %s for updates."),
- oscar_get_ui_info_string("website", PURPLE_WEBSITE));
- purple_notify_warning(pos->gc, NULL,
- _("Unable to get a valid AIM login hash."),
- buf);
- g_free(buf);
- g_free(pos->modname);
- g_free(pos);
- return;
- }
-
- buf = g_strdup_printf("GET " AIMHASHDATA "?offset=%ld&len=%ld&modname=%s HTTP/1.0\n\n",
- pos->offset, pos->len, pos->modname ? pos->modname : "");
- result = send(pos->fd, buf, strlen(buf), 0);
- if (result != strlen(buf)) {
- if (result < 0)
- purple_debug_error("oscar", "Error writing %" G_GSIZE_FORMAT
- " bytes to fetch AIM hash data: %s\n",
- strlen(buf), g_strerror(errno));
- else
- purple_debug_error("oscar", "Tried to write %"
- G_GSIZE_FORMAT " bytes to fetch AIM hash data but "
- "instead wrote %" G_GSSIZE_FORMAT " bytes\n",
- strlen(buf), result);
- }
- g_free(buf);
- g_free(pos->modname);
- pos->inpa = purple_input_add(pos->fd, PURPLE_INPUT_READ, damn_you, pos);
- return;
-}
-
-/* size of icbmui.ocm, the largest module in AIM 3.5 */
-#define AIM_MAX_FILE_SIZE 98304
-
-static int purple_memrequest(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
-{
- va_list ap;
- struct pieceofcrap *pos;
- guint32 offset, len;
- char *modname;
-
- va_start(ap, fr);
- offset = va_arg(ap, guint32);
- len = va_arg(ap, guint32);
- modname = va_arg(ap, char *);
- va_end(ap);
-
- purple_debug_misc("oscar", "offset: %u, len: %u, file: %s\n",
- offset, len, (modname ? modname : "aim.exe"));
-
- if (len == 0) {
- purple_debug_misc("oscar", "len is 0, hashing NULL\n");
- aim_sendmemblock(od, conn, offset, len, NULL,
- AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
- return 1;
- }
-
- pos = g_new0(struct pieceofcrap, 1);
- pos->gc = od->gc;
- pos->conn = conn;
-
- pos->offset = offset;
- pos->len = len;
- pos->modname = g_strdup(modname);
-
- if (purple_proxy_connect(pos->gc, pos->gc->account, "pidgin.im", 80,
- straight_to_hell, pos) == NULL)
- {
- char buf[256];
- g_free(pos->modname);
- g_free(pos);
-
- g_snprintf(buf, sizeof(buf), _("You may be disconnected shortly. "
- "If so, check %s for updates."),
- oscar_get_ui_info_string("website", PURPLE_WEBSITE));
- purple_notify_warning(pos->gc, NULL,
- _("Unable to get a valid login hash."),
- buf);
- }
-
- return 1;
-}
-
int oscar_connect_to_bos(PurpleConnection *gc, OscarData *od, const char *host, guint16 port, guint8 *cookie, guint16 cookielen, const char *tls_certname)
{
PurpleAccount *account;
@@ -1048,7 +872,7 @@ int oscar_connect_to_bos(PurpleConnection *gc, OscarData *od, const char *host,
if (conn->gsc == NULL && conn->connect_data == NULL)
{
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to connect"));
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to connect"));
return 0;
}
@@ -1068,7 +892,7 @@ purple_parse_auth_resp(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
PurpleConnection *gc = od->gc;
PurpleAccount *account = purple_connection_get_account(gc);
char *host; int port;
- int i;
+ gsize i;
FlapConnection *newconn;
va_list ap;
struct aim_authresp_info *info;
@@ -1087,41 +911,41 @@ purple_parse_auth_resp(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
switch (info->errorcode) {
case 0x01:
/* Unregistered username */
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_INVALID_USERNAME, _("Username does not exist"));
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_INVALID_USERNAME, _("Username does not exist"));
break;
case 0x05:
/* Incorrect password */
if (!purple_account_get_remember_password(account))
- purple_account_set_password(account, NULL);
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password"));
+ purple_account_set_password(account, NULL, NULL, NULL);
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password"));
break;
case 0x11:
/* Suspended account */
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Your account is currently suspended"));
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Your account is currently suspended"));
break;
case 0x02:
case 0x14:
/* service temporarily unavailable */
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("The AOL Instant Messenger service is temporarily unavailable."));
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("The AOL Instant Messenger service is temporarily unavailable."));
break;
case 0x18:
/* username connecting too frequently */
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("Your username has been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."));
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("Your username has been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."));
break;
case 0x1c:
{
/* client too old */
g_snprintf(buf, sizeof(buf), _("The client version you are using is too old. Please upgrade at %s"),
oscar_get_ui_info_string("website", PURPLE_WEBSITE));
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, buf);
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, buf);
break;
}
case 0x1d:
/* IP address connecting too frequently */
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("Your IP address has been connecting and disconnecting too frequently. Wait a minute and try again. If you continue to try, you will need to wait even longer."));
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("Your IP address has been connecting and disconnecting too frequently. Wait a minute and try again. If you continue to try, you will need to wait even longer."));
break;
default:
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Unknown reason"));
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Unknown reason"));
break;
}
purple_debug_info("oscar", "Login Error Code 0x%04hx\n", info->errorcode);
@@ -1169,7 +993,7 @@ purple_parse_auth_resp(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
g_free(host);
if (newconn->gsc == NULL && newconn->connect_data == NULL)
{
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to connect"));
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to connect"));
return 0;
}
@@ -1199,7 +1023,7 @@ purple_parse_auth_securid_request_no_cb(gpointer user_data, const char *value)
PurpleConnection *gc = user_data;
/* Disconnect */
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
_("The SecurID key entered is invalid"));
}
@@ -1222,45 +1046,13 @@ purple_parse_auth_securid_request(OscarData *od, FlapConnection *conn, FlapFrame
FALSE, FALSE, NULL,
_("_OK"), G_CALLBACK(purple_parse_auth_securid_request_yes_cb),
_("_Cancel"), G_CALLBACK(purple_parse_auth_securid_request_no_cb),
- account, NULL, NULL,
+ purple_request_cpar_from_connection(gc),
gc);
g_free(primary);
return 1;
}
-/**
- * Only used when connecting with the old-style BUCP login.
- */
-static int
-purple_parse_login(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
-{
- PurpleConnection *gc;
- PurpleAccount *account;
- ClientInfo aiminfo = CLIENTINFO_PURPLE_AIM;
- ClientInfo icqinfo = CLIENTINFO_PURPLE_ICQ;
- va_list ap;
- char *key;
- gboolean truncate_pass;
-
- gc = od->gc;
- account = purple_connection_get_account(gc);
-
- va_start(ap, fr);
- key = va_arg(ap, char *);
- truncate_pass = va_arg(ap, int);
- va_end(ap);
-
- aim_send_login(od, conn, purple_account_get_username(account),
- purple_connection_get_password(gc), truncate_pass,
- od->icq ? &icqinfo : &aiminfo, key,
- purple_account_get_bool(account, "allow_multiple_logins", OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS));
-
- purple_connection_update_progress(gc, _("Password sent"), 2, OSCAR_CONNECT_STEPS);
-
- return 1;
-}
-
static int
purple_handle_redirect(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
{
@@ -1291,8 +1083,8 @@ purple_handle_redirect(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
if (strcmp(encryption_type, OSCAR_OPPORTUNISTIC_ENCRYPTION) == 0) {
purple_debug_warning("oscar", "We won't use SSL for FLAP type 0x%04hx.\n", redir->group);
} else if (strcmp(encryption_type, OSCAR_REQUIRE_ENCRYPTION) == 0) {
- purple_debug_error("oscar", "FLAP server %s:%d of type 0x%04hx doesn't support encryption.", host, port, redir->group);
- purple_connection_error_reason(
+ purple_debug_error("oscar", "FLAP server %s:%d of type 0x%04hx doesn't support encryption.\n", host, port, redir->group);
+ purple_connection_error(
gc,
PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
_("You required encryption in your account settings, but one of the servers doesn't support it."));
@@ -1383,7 +1175,7 @@ static int purple_parse_oncoming(OscarData *od, FlapConnection *conn, FlapFrame
g_return_val_if_fail(info != NULL, 1);
g_return_val_if_fail(info->bn != NULL, 1);
- buddy = purple_find_buddy(account, info->bn);
+ buddy = purple_blist_find_buddy(account, info->bn);
if (buddy) {
previous_status = purple_presence_get_active_status(purple_buddy_get_presence(buddy));
}
@@ -1401,7 +1193,7 @@ static int purple_parse_oncoming(OscarData *od, FlapConnection *conn, FlapFrame
break;
}
}
- serv_got_alias(gc, info->bn,
+ purple_serv_got_alias(gc, info->bn,
bn_has_formatting ? info->bn : NULL);
}
@@ -1512,7 +1304,7 @@ static int purple_parse_oncoming(OscarData *od, FlapConnection *conn, FlapFrame
PurpleBuddy *b = NULL;
b16 = purple_base16_encode(info->iconcsum, info->iconcsumlen);
- b = purple_find_buddy(account, info->bn);
+ b = purple_blist_find_buddy(account, info->bn);
if (b != NULL)
saved_b16 = purple_buddy_icons_get_checksum_for_user(b);
@@ -1547,7 +1339,7 @@ static int purple_parse_offgoing(OscarData *od, FlapConnection *conn, FlapFrame
purple_prpl_got_user_status(account, info->bn, OSCAR_STATUS_ID_OFFLINE, NULL);
purple_prpl_got_user_status_deactive(account, info->bn, OSCAR_STATUS_ID_MOBILE);
- g_hash_table_remove(od->buddyinfo, purple_normalize(gc->account, info->bn));
+ g_hash_table_remove(od->buddyinfo, purple_normalize(purple_connection_get_account(gc), info->bn));
return 1;
}
@@ -1557,7 +1349,7 @@ static int incomingim_chan1(OscarData *od, FlapConnection *conn, aim_userinfo_t
PurpleAccount *account = purple_connection_get_account(gc);
PurpleMessageFlags flags = 0;
struct buddyinfo *bi;
- PurpleStoredImage *img;
+ PurpleImage *img;
gchar *tmp;
const char *start, *end;
GData *attribs;
@@ -1591,8 +1383,8 @@ static int incomingim_chan1(OscarData *od, FlapConnection *conn, aim_userinfo_t
img = purple_buddy_icons_find_account_icon(account);
if ((img != NULL) &&
(args->icbmflags & AIM_IMFLAGS_BUDDYREQ) && !bi->ico_sent && bi->ico_informed) {
- gconstpointer data = purple_imgstore_get_data(img);
- size_t len = purple_imgstore_get_size(img);
+ gconstpointer data = purple_image_get_data(img);
+ size_t len = purple_image_get_size(img);
purple_debug_info("oscar",
"Sending buddy icon to %s (%" G_GSIZE_FORMAT " bytes)\n",
userinfo->bn, len);
@@ -1600,7 +1392,7 @@ static int incomingim_chan1(OscarData *od, FlapConnection *conn, aim_userinfo_t
purple_buddy_icons_get_account_icon_timestamp(account),
aimutil_iconsum(data, len));
}
- purple_imgstore_unref(img);
+ g_object_unref(img);
tmp = g_strdup(args->msg);
@@ -1686,7 +1478,7 @@ static int incomingim_chan1(OscarData *od, FlapConnection *conn, aim_userinfo_t
tmp = tmp2;
}
- serv_got_im(gc, userinfo->bn, tmp, flags, (args->icbmflags & AIM_IMFLAGS_OFFLINE) ? args->timestamp : time(NULL));
+ purple_serv_got_im(gc, userinfo->bn, tmp, flags, (args->icbmflags & AIM_IMFLAGS_OFFLINE) ? args->timestamp : time(NULL));
g_free(tmp);
return 1;
@@ -1741,7 +1533,7 @@ incomingim_chan2(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
g_hash_table_replace(components, g_strdup("room"), utf8name);
g_hash_table_replace(components, g_strdup("exchange"),
g_strdup_printf("%d", args->info.chat.roominfo.exchange));
- serv_got_chat_invite(gc,
+ purple_serv_got_chat_invite(gc,
utf8name,
userinfo->bn,
message,
@@ -1828,7 +1620,7 @@ incomingim_chan2(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
tmp2 = purple_strreplace(tmp, "\r\n", "<br>");
g_free(tmp);
- serv_got_im(gc, userinfo->bn, tmp2, flags, time(NULL));
+ purple_serv_got_im(gc, userinfo->bn, tmp2, flags, time(NULL));
aim_im_send_icq_confirmation(od, userinfo->bn, args->cookie);
g_free(tmp2);
}
@@ -1871,7 +1663,7 @@ incomingim_chan4(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
return 1;
purple_debug_info("oscar",
- "Received a channel 4 message of type 0x%02hx.\n",
+ "Received a channel 4 message of type 0x%02hx.",
(guint16)args->type);
/*
@@ -1919,9 +1711,9 @@ incomingim_chan4(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
t -= timezone;
# endif
#endif
- serv_got_im(gc, uin, tmp, 0, t);
+ purple_serv_got_im(gc, uin, tmp, 0, t);
} else { /* This is a message from MacICQ/Miranda */
- serv_got_im(gc, uin, tmp, 0, time(NULL));
+ purple_serv_got_im(gc, uin, tmp, 0, time(NULL));
}
g_free(uin);
g_free(tmp);
@@ -1935,7 +1727,7 @@ incomingim_chan4(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
gchar *message = g_strdup_printf("<A HREF=\"%s\">%s</A>",
msg2[1],
(msg2[0] && msg2[0][0]) ? msg2[0] : msg2[1]);
- serv_got_im(gc, uin, message, 0, time(NULL));
+ purple_serv_got_im(gc, uin, message, 0, time(NULL));
g_free(uin);
g_free(message);
}
@@ -1962,23 +1754,21 @@ incomingim_chan4(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
case 0x07: { /* Someone has denied you authorization */
if (i >= 1) {
gchar *dialog_msg = g_strdup_printf(_("The user %u has denied your request to add them to your buddy list for the following reason:\n%s"), args->uin, msg2[0] ? msg2[0] : _("No reason given."));
- purple_notify_info(gc, NULL, _("ICQ authorization denied."),
- dialog_msg);
+ purple_notify_info(gc, NULL, _("ICQ authorization denied."), dialog_msg, purple_request_cpar_from_connection(gc));
g_free(dialog_msg);
}
} break;
case 0x08: { /* Someone has granted you authorization */
gchar *dialog_msg = g_strdup_printf(_("The user %u has granted your request to add them to your buddy list."), args->uin);
- purple_notify_info(gc, NULL, "ICQ authorization accepted.",
- dialog_msg);
+ purple_notify_info(gc, NULL, "ICQ authorization accepted.", dialog_msg, purple_request_cpar_from_connection(gc));
g_free(dialog_msg);
} break;
case 0x09: { /* Message from the Godly ICQ server itself, I think */
if (i >= 5) {
gchar *dialog_msg = g_strdup_printf(_("You have received a special message\n\nFrom: %s [%s]\n%s"), msg2[0], msg2[3], msg2[5]);
- purple_notify_info(gc, NULL, "ICQ Server Message", dialog_msg);
+ purple_notify_info(gc, NULL, "ICQ Server Message", dialog_msg, purple_request_cpar_from_connection(gc));
g_free(dialog_msg);
}
} break;
@@ -1986,7 +1776,7 @@ incomingim_chan4(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
case 0x0d: { /* Someone has sent you a pager message from http://www.icq.com/your_uin */
if (i >= 6) {
gchar *dialog_msg = g_strdup_printf(_("You have received an ICQ page\n\nFrom: %s [%s]\n%s"), msg2[0], msg2[3], msg2[5]);
- purple_notify_info(gc, NULL, "ICQ Page", dialog_msg);
+ purple_notify_info(gc, NULL, "ICQ Page", dialog_msg, purple_request_cpar_from_connection(gc));
g_free(dialog_msg);
}
} break;
@@ -1994,7 +1784,7 @@ incomingim_chan4(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
case 0x0e: { /* Someone has emailed you at your_uin@pager.icq.com */
if (i >= 6) {
gchar *dialog_msg = g_strdup_printf(_("You have received an ICQ email from %s [%s]\n\nMessage is:\n%s"), msg2[0], msg2[3], msg2[5]);
- purple_notify_info(gc, NULL, "ICQ Email", dialog_msg);
+ purple_notify_info(gc, NULL, "ICQ Email", dialog_msg, purple_request_cpar_from_connection(gc));
g_free(dialog_msg);
}
} break;
@@ -2038,7 +1828,7 @@ incomingim_chan4(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
_("Do you want to add this buddy "
"to your buddy list?"),
PURPLE_DEFAULT_ACTION_NONE,
- purple_connection_get_account(gc), data->name, NULL,
+ purple_request_cpar_from_connection(gc),
data, 2,
_("_Add"), G_CALLBACK(purple_icq_buddyadd),
_("_Decline"), G_CALLBACK(oscar_free_name_data));
@@ -2059,7 +1849,7 @@ incomingim_chan4(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
guint16 smstype;
guint32 taglen, smslen;
char *tagstr = NULL, *smsmsg = NULL;
- xmlnode *xmlroot = NULL, *xmltmp = NULL;
+ PurpleXmlNode *xmlroot = NULL, *xmltmp = NULL;
gchar *uin = NULL, *message = NULL;
/* From libicq2000-0.3.2/src/ICQ.cpp */
@@ -2093,23 +1883,23 @@ incomingim_chan4(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
/* Check if this is an SMS being sent from server */
if ((smstype == 0) && (!strcmp(tagstr, "ICQSMS")) && (smsmsg != NULL))
{
- xmlroot = xmlnode_from_str(smsmsg, -1);
+ xmlroot = purple_xmlnode_from_str(smsmsg, -1);
if (xmlroot != NULL)
{
- xmltmp = xmlnode_get_child(xmlroot, "sender");
+ xmltmp = purple_xmlnode_get_child(xmlroot, "sender");
if (xmltmp != NULL)
- uin = xmlnode_get_data(xmltmp);
+ uin = purple_xmlnode_get_data(xmltmp);
- xmltmp = xmlnode_get_child(xmlroot, "text");
+ xmltmp = purple_xmlnode_get_child(xmlroot, "text");
if (xmltmp != NULL)
- message = xmlnode_get_data(xmltmp);
+ message = purple_xmlnode_get_data(xmltmp);
if ((uin != NULL) && (message != NULL))
- serv_got_im(gc, uin, message, 0, time(NULL));
+ purple_serv_got_im(gc, uin, message, 0, time(NULL));
g_free(uin);
g_free(message);
- xmlnode_free(xmlroot);
+ purple_xmlnode_free(xmlroot);
}
}
g_free(tagstr);
@@ -2119,7 +1909,7 @@ incomingim_chan4(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
default: {
purple_debug_info("oscar",
"Received a channel 4 message of unknown type "
- "(type 0x%02hhx).\n", args->type);
+ "(type 0x%02x).\n", args->type & 0xFF);
} break;
}
@@ -2242,8 +2032,10 @@ static int purple_parse_misses(OscarData *od, FlapConnection *conn, FlapFrame *f
break;
}
- if (!purple_conv_present_error(userinfo->bn, account, buf))
- purple_notify_error(od->gc, NULL, buf, NULL);
+ if (!purple_conversation_present_error(userinfo->bn, account, buf)) {
+ purple_notify_error(od->gc, NULL, buf, NULL,
+ purple_request_cpar_from_connection(od->gc));
+ }
g_free(buf);
return 1;
@@ -2278,7 +2070,7 @@ purple_parse_clientauto_ch2(OscarData *od, const char *who, guint16 reason, cons
return 0;
}
-static int purple_parse_clientauto_ch4(OscarData *od, char *who, guint16 reason, guint32 state, char *msg) {
+static int purple_parse_clientauto_ch4(OscarData *od, const char *who, guint16 reason, guint32 state, char *msg) {
PurpleConnection *gc = od->gc;
switch(reason) {
@@ -2286,16 +2078,20 @@ static int purple_parse_clientauto_ch4(OscarData *od, char *who, guint16 reason,
char *statusmsg, **splitmsg;
PurpleNotifyUserInfo *user_info;
- /* Split at (carriage return/newline)'s, then rejoin later with BRs between. */
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(user_info, _("UIN"), who);
- purple_notify_user_info_add_pair(user_info, _("Status"), statusmsg);
+ 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(user_info, NULL, g_strjoinv("<BR>", splitmsg));
+ purple_notify_user_info_add_pair_html(user_info, NULL, g_strjoinv("<BR>", splitmsg));
g_free(statusmsg);
g_strfreev(splitmsg);
@@ -2309,16 +2105,20 @@ static int purple_parse_clientauto_ch4(OscarData *od, char *who, guint16 reason,
char *statusmsg, **splitmsg;
PurpleNotifyUserInfo *user_info;
- /* Split at (carriage return/newline)'s, then rejoin later with BRs between. */
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(user_info, _("UIN"), who);
- purple_notify_user_info_add_pair(user_info, _("Status"), statusmsg);
+ 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(user_info, NULL, g_strjoinv("<BR>", splitmsg));
+ purple_notify_user_info_add_pair_html(user_info, NULL, g_strjoinv("<BR>", splitmsg));
g_free(statusmsg);
g_strfreev(splitmsg);
@@ -2394,19 +2194,19 @@ static int purple_parse_mtn(OscarData *od, FlapConnection *conn, FlapFrame *fr,
switch (event) {
case 0x0000: { /* Text has been cleared */
- serv_got_typing_stopped(gc, bn);
+ purple_serv_got_typing_stopped(gc, bn);
} break;
case 0x0001: { /* Paused typing */
- serv_got_typing(gc, bn, 0, PURPLE_TYPED);
+ purple_serv_got_typing(gc, bn, 0, PURPLE_IM_TYPED);
} break;
case 0x0002: { /* Typing */
- serv_got_typing(gc, bn, 0, PURPLE_TYPING);
+ purple_serv_got_typing(gc, bn, 0, PURPLE_IM_TYPING);
} break;
case 0x000f: { /* Closed IM window */
- serv_got_typing_stopped(gc, bn);
+ purple_serv_got_typing_stopped(gc, bn);
} break;
default: {
@@ -2432,9 +2232,11 @@ static int purple_parse_motd(OscarData *od, FlapConnection *conn, FlapFrame *fr,
purple_debug_misc("oscar",
"MOTD: %s (%hu)\n", msg ? msg : "Unknown", id);
- if (id < 4)
+ if (id < 4) {
purple_notify_warning(od->gc, NULL,
- _("Your AIM connection may be lost."), NULL);
+ _("Your AIM connection may be lost."), NULL,
+ purple_request_cpar_from_connection(od->gc));
+ }
return 1;
}
@@ -2457,7 +2259,7 @@ static int purple_chatnav_info(OscarData *od, FlapConnection *conn, FlapFrame *f
exchangecount = va_arg(ap, int);
exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
- g_string_append_printf(msg, "chat info: Max Concurrent Rooms: %hhd, Exchange List (%d total): ", maxrooms, exchangecount);
+ g_string_append_printf(msg, "chat info: Max Concurrent Rooms: %d, Exchange List (%d total): ", (int)maxrooms, exchangecount);
for (i = 0; i < exchangecount; i++) {
g_string_append_printf(msg, "%hu", exchanges[i].number);
if (exchanges[i].name) {
@@ -2498,9 +2300,9 @@ static int purple_chatnav_info(OscarData *od, FlapConnection *conn, FlapFrame *f
ck = va_arg(ap, char *);
purple_debug_misc("oscar",
- "created room: %s %hu %hu %hu %u %hu %hu %hhu %hu %s %s\n",
+ "created room: %s %hu %hu %hu %u %hu %hu %u %hu %s %s\n",
fqcn ? fqcn : "(null)", exchange, instance, flags, createtime,
- maxmsglen, maxoccupancy, createperms, unknown,
+ maxmsglen, maxoccupancy, (guint)createperms, unknown,
name ? name : "(null)", ck);
aim_chat_join(od, exchange, ck, instance);
}
@@ -2516,7 +2318,7 @@ static int purple_chatnav_info(OscarData *od, FlapConnection *conn, FlapFrame *f
return 1;
}
-static int purple_conv_chat_join(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
+static int purple_chat_conversation_join(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
va_list ap;
int count, i;
aim_userinfo_t *info;
@@ -2534,12 +2336,12 @@ static int purple_conv_chat_join(OscarData *od, FlapConnection *conn, FlapFrame
return 1;
for (i = 0; i < count; i++)
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(c->conv), info[i].bn, NULL, PURPLE_CBFLAGS_NONE, TRUE);
+ purple_chat_conversation_add_user(c->conv, info[i].bn, NULL, PURPLE_CHAT_USER_NONE, TRUE);
return 1;
}
-static int purple_conv_chat_leave(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
+static int purple_chat_conversation_left(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
va_list ap;
int count, i;
aim_userinfo_t *info;
@@ -2557,12 +2359,12 @@ static int purple_conv_chat_leave(OscarData *od, FlapConnection *conn, FlapFrame
return 1;
for (i = 0; i < count; i++)
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(c->conv), info[i].bn, NULL);
+ purple_chat_conversation_remove_user(c->conv, info[i].bn, NULL);
return 1;
}
-static int purple_conv_chat_info_update(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
+static int purple_chat_conversation_info_update(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
va_list ap;
guint16 maxmsglen, maxvisiblemsglen;
PurpleConnection *gc = od->gc;
@@ -2586,7 +2388,7 @@ static int purple_conv_chat_info_update(OscarData *od, FlapConnection *conn, Fla
return 1;
}
-static int purple_conv_chat_incoming_msg(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
+static int purple_chat_conversation_incoming_msg(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
PurpleConnection *gc = od->gc;
struct chat_connection *ccon = find_oscar_chat_by_conn(gc, conn);
gchar *utf8;
@@ -2607,7 +2409,8 @@ static int purple_conv_chat_incoming_msg(OscarData *od, FlapConnection *conn, Fl
va_end(ap);
utf8 = oscar_encoding_to_utf8(charset, msg, len);
- serv_got_chat_in(gc, ccon->id, info->bn, 0, utf8, time(NULL));
+ purple_serv_got_chat_in(gc, ccon->id, info->bn,
+ PURPLE_MESSAGE_RECV, utf8, time(NULL));
g_free(utf8);
return 1;
@@ -2698,15 +2501,15 @@ purple_icons_fetch(PurpleConnection *gc)
if (od->set_icon) {
PurpleAccount *account = purple_connection_get_account(gc);
- PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account);
+ PurpleImage *img = purple_buddy_icons_find_account_icon(account);
if (img == NULL) {
aim_ssi_delicon(od);
} else {
purple_debug_info("oscar",
- "Uploading icon to icon server\n");
- aim_bart_upload(od, purple_imgstore_get_data(img),
- purple_imgstore_get_size(img));
- purple_imgstore_unref(img);
+ "Uploading icon to icon server");
+ aim_bart_upload(od, purple_image_get_data(img),
+ purple_image_get_size(img));
+ g_object_unref(img);
}
od->set_icon = FALSE;
}
@@ -2755,14 +2558,14 @@ static int purple_connerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ..
if (conn->type == SNAC_FAMILY_CHAT) {
struct chat_connection *cc;
- PurpleConversation *conv = NULL;
+ PurpleChatConversation *chat = NULL;
cc = find_oscar_chat_by_conn(gc, conn);
if (cc != NULL)
{
- conv = purple_find_chat(gc, cc->id);
+ chat = purple_conversations_find_chat(gc, cc->id);
- if (conv != NULL)
+ if (chat != NULL)
{
/*
* TOOD: Have flap_connection_destroy_cb() send us the
@@ -2772,7 +2575,9 @@ static int purple_connerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ..
gchar *buf;
buf = g_strdup_printf(_("You have been disconnected from chat "
"room %s."), cc->name);
- purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_ERROR, time(NULL));
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(chat), buf,
+ PURPLE_MESSAGE_ERROR);
g_free(buf);
}
oscar_chat_kill(gc, cc);
@@ -2799,7 +2604,7 @@ static int purple_parse_locaterights(OscarData *od, FlapConnection *conn, FlapFr
od->rights.maxsiglen = od->rights.maxawaymsglen = (guint)maxsiglen;
aim_locate_setcaps(od, purple_caps);
- oscar_set_info_and_status(account, TRUE, account->user_info, TRUE,
+ oscar_set_info_and_status(account, TRUE, purple_account_get_user_info(account), TRUE,
purple_account_get_active_status(account));
return 1;
@@ -2841,7 +2646,8 @@ static void oscar_format_username(PurpleConnection *gc, const char *new_display_
username = purple_account_get_username(purple_connection_get_account(gc));
if (oscar_util_name_compare(username, new_display_name)) {
purple_notify_error(gc, NULL, _("The new formatting is invalid."),
- _("Username formatting can change only capitalization and whitespace."));
+ _("Username formatting can change only capitalization and whitespace."),
+ purple_request_cpar_from_connection(gc));
return;
}
@@ -2898,7 +2704,7 @@ static int purple_bosrights(OscarData *od, FlapConnection *conn, FlapFrame *fr,
purple_debug_info("oscar", "buddy list loaded\n");
if (purple_account_get_user_info(account) != NULL)
- serv_set_info(gc, purple_account_get_user_info(account));
+ purple_serv_set_info(gc, purple_account_get_user_info(account));
username = purple_account_get_username(account);
if (!od->icq && strcmp(username, purple_connection_get_display_name(gc)) != 0) {
@@ -2952,7 +2758,7 @@ static int purple_bosrights(OscarData *od, FlapConnection *conn, FlapFrame *fr,
/* Request offline messages for AIM and ICQ */
aim_im_reqofflinemsgs(od);
- purple_connection_set_state(gc, PURPLE_CONNECTED);
+ purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
}
return 1;
@@ -3007,9 +2813,9 @@ static int purple_parse_searchreply(OscarData *od, FlapConnection *conn, FlapFra
if (results == NULL) {
purple_debug_error("oscar", "purple_parse_searchreply: "
"Unable to display the search results.\n");
- purple_notify_error(gc, NULL,
- _("Unable to display the search results."),
- NULL);
+ purple_notify_error(gc, NULL, _("Unable to display the search "
+ "results."), NULL,
+ purple_request_cpar_from_connection(gc));
return 1;
}
@@ -3046,7 +2852,8 @@ static int purple_parse_searcherror(OscarData *od, FlapConnection *conn, FlapFra
va_end(ap);
buf = g_strdup_printf(_("No results found for email address %s"), email);
- purple_notify_error(od->gc, NULL, buf, NULL);
+ purple_notify_error(od->gc, NULL, buf, NULL,
+ purple_request_cpar_from_connection(od->gc));
g_free(buf);
return 1;
@@ -3068,7 +2875,8 @@ static int purple_account_confirm(OscarData *od, FlapConnection *conn, FlapFrame
if (!status) {
g_snprintf(msg, sizeof(msg), _("You should receive an email asking to confirm %s."),
purple_account_get_username(purple_connection_get_account(gc)));
- purple_notify_info(gc, NULL, _("Account Confirmation Requested"), msg);
+ purple_notify_info(gc, NULL, _("Account Confirmation Requested"),
+ msg, purple_request_cpar_from_connection(gc));
}
return 1;
@@ -3114,8 +2922,8 @@ static int purple_info_change(OscarData *od, FlapConnection *conn, FlapFrame *fr
dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because the given address is invalid."), err);
else
dialog_msg = g_strdup_printf(_("Error 0x%04x: Unknown error."), err);
- purple_notify_error(gc, NULL,
- _("Error Changing Account Info"), dialog_msg);
+ purple_notify_error(gc, NULL, _("Error Changing Account Info"),
+ dialog_msg, purple_request_cpar_from_connection(gc));
g_free(dialog_msg);
return 1;
}
@@ -3123,7 +2931,8 @@ static int purple_info_change(OscarData *od, FlapConnection *conn, FlapFrame *fr
if (email != NULL) {
char *dialog_msg = g_strdup_printf(_("The email address for %s is %s"),
purple_account_get_username(purple_connection_get_account(gc)), email);
- purple_notify_info(gc, NULL, _("Account Info"), dialog_msg);
+ purple_notify_info(gc, NULL, _("Account Info"), dialog_msg,
+ purple_request_cpar_from_connection(gc));
g_free(dialog_msg);
}
@@ -3143,7 +2952,7 @@ oscar_keepalive(PurpleConnection *gc)
}
unsigned int
-oscar_send_typing(PurpleConnection *gc, const char *name, PurpleTypingState state)
+oscar_send_typing(PurpleConnection *gc, const char *name, PurpleIMTypingState state)
{
OscarData *od;
PeerConnection *conn;
@@ -3157,14 +2966,16 @@ oscar_send_typing(PurpleConnection *gc, const char *name, PurpleTypingState stat
}
else {
/* Don't send if this turkey is in our deny list */
+ PurpleAccount *account = purple_connection_get_account(gc);
GSList *list;
- for (list=gc->account->deny; (list && oscar_util_name_compare(name, list->data)); list=list->next);
+
+ for (list=purple_account_privacy_get_denied(account); (list && oscar_util_name_compare(name, list->data)); list=list->next);
if (!list) {
- struct buddyinfo *bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(gc->account, name));
+ struct buddyinfo *bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, name));
if (bi && bi->typingnot) {
- if (state == PURPLE_TYPING)
+ if (state == PURPLE_IM_TYPING)
aim_im_sendmtn(od, 0x0001, name, 0x0002);
- else if (state == PURPLE_TYPED)
+ else if (state == PURPLE_IM_TYPED)
aim_im_sendmtn(od, 0x0001, name, 0x0001);
else
aim_im_sendmtn(od, 0x0001, name, 0x0000);
@@ -3194,21 +3005,23 @@ purple_odc_send_im(PeerConnection *conn, const char *message, PurpleMessageFlags
/* for each valid IMG tag... */
while (last && *last && purple_markup_find_tag("img", last, &start, &end, &attribs))
{
- PurpleStoredImage *image = NULL;
- const char *id;
+ PurpleImage *image = NULL;
+ const gchar *src;
if (start - last) {
g_string_append_len(msg, last, start - last);
}
- id = g_datalist_get_data(&attribs, "id");
+ src = g_datalist_get_data(&attribs, "src");
+ if (src)
+ image = purple_image_store_get_from_uri(src);
/* ... if it refers to a valid purple image ... */
- if (id && (image = purple_imgstore_find_by_id(atoi(id)))) {
+ if (image) {
/* ... append the message from start to the tag ... */
- unsigned long size = purple_imgstore_get_size(image);
- const char *filename = purple_imgstore_get_filename(image);
- gconstpointer imgdata = purple_imgstore_get_data(image);
+ unsigned long size = purple_image_get_size(image);
+ const gchar *filename = purple_image_get_friendly_filename(image);
+ gconstpointer imgdata = purple_image_get_data(image);
oscar_id++;
@@ -3263,7 +3076,7 @@ purple_odc_send_im(PeerConnection *conn, const char *message, PurpleMessageFlags
}
int
-oscar_send_im(PurpleConnection *gc, const char *name, const char *message, PurpleMessageFlags imflags)
+oscar_send_im(PurpleConnection *gc, PurpleMessage *msg)
{
OscarData *od;
PurpleAccount *account;
@@ -3271,7 +3084,12 @@ oscar_send_im(PurpleConnection *gc, const char *name, const char *message, Purpl
int ret;
char *tmp1, *tmp2;
gboolean is_sms, is_html;
+ const gchar *name, *message;
+ PurpleMessageFlags imflags;
+ name = purple_message_get_recipient(msg);
+ message = purple_message_get_contents(msg);
+ imflags = purple_message_get_flags(msg);
od = purple_connection_get_protocol_data(gc);
account = purple_connection_get_account(gc);
ret = 0;
@@ -3303,19 +3121,19 @@ oscar_send_im(PurpleConnection *gc, const char *name, const char *message, Purpl
} else {
struct buddyinfo *bi;
struct aim_sendimext_args args;
- PurpleConversation *conv;
- PurpleStoredImage *img;
+ PurpleIMConversation *im;
+ PurpleImage *img;
PurpleBuddy *buddy;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, account);
+ im = purple_conversations_find_im_with_account(name, account);
- if (strstr(tmp1, "<IMG "))
- purple_conversation_write(conv, "",
- _("Your IM Image was not sent. "
- "You must be Direct Connected to send IM Images."),
- PURPLE_MESSAGE_ERROR, time(NULL));
+ if (strstr(tmp1, "<img "))
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
+ _("Your IM Image was not sent. "
+ "You must be Direct Connected to send IM Images."),
+ PURPLE_MESSAGE_ERROR);
- buddy = purple_find_buddy(account, name);
+ buddy = purple_blist_find_buddy(account, name);
bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, name));
if (!bi) {
@@ -3348,8 +3166,8 @@ oscar_send_im(PurpleConnection *gc, const char *name, const char *message, Purpl
img = purple_buddy_icons_find_account_icon(account);
if (img) {
- gconstpointer data = purple_imgstore_get_data(img);
- args.iconlen = purple_imgstore_get_size(img);
+ gconstpointer data = purple_image_get_data(img);
+ args.iconlen = purple_image_get_size(img);
args.iconsum = aimutil_iconsum(data, args.iconlen);
args.iconstamp = purple_buddy_icons_get_account_icon_timestamp(account);
@@ -3375,7 +3193,7 @@ oscar_send_im(PurpleConnection *gc, const char *name, const char *message, Purpl
bi->ico_informed = TRUE;
}
- purple_imgstore_unref(img);
+ g_object_unref(img);
}
args.destbn = name;
@@ -3529,7 +3347,7 @@ oscar_set_info_and_status(PurpleAccount *account, gboolean setinfo, const char *
char *status_text = NULL;
const char *itmsurl = NULL;
- status_type = purple_status_get_type(status);
+ status_type = purple_status_get_status_type(status);
primitive = purple_status_type_get_primitive(status_type);
if (!setinfo)
@@ -3542,7 +3360,8 @@ oscar_set_info_and_status(PurpleAccount *account, gboolean setinfo, const char *
_("You have probably requested to set your "
"profile before the login procedure completed. "
"Your profile remains unset; try setting it "
- "again when you are fully connected."));
+ "again when you are fully connected."),
+ purple_request_cpar_from_connection(gc));
}
else if (rawinfo != NULL)
{
@@ -3558,7 +3377,8 @@ oscar_set_info_and_status(PurpleAccount *account, gboolean setinfo, const char *
"The maximum profile length of %d bytes "
"has been exceeded. It has been truncated for you.",
od->rights.maxsiglen), od->rights.maxsiglen);
- purple_notify_warning(gc, NULL, _("Profile too long."), errstr);
+ purple_notify_warning(gc, NULL, _("Profile too long."),
+ errstr, purple_request_cpar_from_connection(gc));
g_free(errstr);
}
}
@@ -3592,7 +3412,9 @@ oscar_set_info_and_status(PurpleAccount *account, gboolean setinfo, const char *
"The maximum away message length of %d bytes "
"has been exceeded. It has been truncated for you.",
od->rights.maxawaymsglen), od->rights.maxawaymsglen);
- purple_notify_warning(gc, NULL, _("Away message too long."), errstr);
+ purple_notify_warning(gc, NULL,
+ _("Away message too long."), errstr,
+ purple_request_cpar_from_connection(gc));
g_free(errstr);
}
}
@@ -3636,12 +3458,12 @@ oscar_set_icq_permdeny(PurpleAccount *account)
/*
* For ICQ the permit/deny setting controls who can see you
- * online. Mimicking the official client's behavior, we use PURPLE_PRIVACY_ALLOW_USERS
- * when our status is "invisible" and PURPLE_PRIVACY_DENY_USERS otherwise.
+ * online. Mimicking the official client's behavior, we use PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS
+ * when our status is "invisible" and PURPLE_ACCOUNT_PRIVACY_DENY_USERS otherwise.
* In the former case, we are visible only to buddies on our "permanently visible" list.
* In the latter, we are invisible only to buddies on our "permanently invisible" list.
*/
- aim_ssi_setpermdeny(od, invisible ? PURPLE_PRIVACY_ALLOW_USERS : PURPLE_PRIVACY_DENY_USERS);
+ aim_ssi_setpermdeny(od, invisible ? PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS : PURPLE_ACCOUNT_PRIVACY_DENY_USERS);
}
void
@@ -3664,7 +3486,7 @@ oscar_set_status(PurpleAccount *account, PurpleStatus *status)
od = purple_connection_get_protocol_data(pc);
/* There's no need to do the stuff below for mood updates. */
- if (purple_status_type_get_primitive(purple_status_get_type(status)) == PURPLE_STATUS_MOOD) {
+ if (purple_status_type_get_primitive(purple_status_get_status_type(status)) == PURPLE_STATUS_MOOD) {
aim_locate_setcaps(od, purple_caps);
return;
}
@@ -3693,8 +3515,8 @@ oscar_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, co
if (!oscar_util_valid_name(bname)) {
gchar *buf;
buf = g_strdup_printf(_("Unable to add the buddy %s because the username is invalid. Usernames must be a valid email address, or start with a letter and contain only letters, numbers and spaces, or contain only numbers."), bname);
- if (!purple_conv_present_error(bname, account, buf))
- purple_notify_error(gc, NULL, _("Unable to Add"), buf);
+ if (!purple_conversation_present_error(bname, account, buf))
+ purple_notify_error(gc, NULL, _("Unable to Add"), buf, purple_request_cpar_from_connection(gc));
g_free(buf);
/* Remove from local list */
@@ -3704,7 +3526,7 @@ oscar_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, co
}
if (od->ssi.received_data) {
- if (!aim_ssi_itemlist_finditem(od->ssi.local, gname, bname, AIM_SSI_TYPE_BUDDY)) {
+ if (!aim_ssi_itemlist_finditem(&od->ssi.local, gname, bname, AIM_SSI_TYPE_BUDDY)) {
purple_debug_info("oscar",
"ssi: adding buddy %s to group %s\n", bname, gname);
aim_ssi_addbuddy(od, bname, gname, NULL, purple_buddy_get_alias_only(buddy), NULL, NULL, 0);
@@ -3716,8 +3538,8 @@ oscar_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, co
purple_prpl_got_user_status(account, bname,
OSCAR_STATUS_ID_MOBILE, NULL);
}
- } else if (aim_ssi_waitingforauth(od->ssi.local,
- aim_ssi_itemlist_findparentname(od->ssi.local, bname),
+ } else if (aim_ssi_waitingforauth(&od->ssi.local,
+ aim_ssi_itemlist_findparentname(&od->ssi.local, bname),
bname)) {
/* Not authorized -- Re-request authorization */
oscar_auth_sendrequest(gc, bname, msg);
@@ -3755,7 +3577,7 @@ void oscar_alias_buddy(PurpleConnection *gc, const char *name, const char *alias
OscarData *od = purple_connection_get_protocol_data(gc);
if (od->ssi.received_data) {
- char *gname = aim_ssi_itemlist_findparentname(od->ssi.local, name);
+ char *gname = aim_ssi_itemlist_findparentname(&od->ssi.local, name);
if (gname) {
purple_debug_info("oscar",
"ssi: changing the alias for buddy %s to %s\n", name, alias ? alias : "(none)");
@@ -3772,7 +3594,7 @@ void oscar_rename_group(PurpleConnection *gc, const char *old_name, PurpleGroup
if (od->ssi.received_data) {
const char *gname = purple_group_get_name(group);
- if (aim_ssi_itemlist_finditem(od->ssi.local, gname, NULL, AIM_SSI_TYPE_GROUP)) {
+ if (aim_ssi_itemlist_finditem(&od->ssi.local, gname, NULL, AIM_SSI_TYPE_GROUP)) {
GList *cur, *groups = NULL;
PurpleAccount *account = purple_connection_get_account(gc);
@@ -3786,7 +3608,7 @@ void oscar_rename_group(PurpleConnection *gc, const char *old_name, PurpleGroup
}
purple_account_remove_buddies(account, moved_buddies, groups);
- purple_account_add_buddies(account, moved_buddies);
+ purple_account_add_buddies(account, moved_buddies, NULL);
g_list_free(groups);
purple_debug_info("oscar",
"ssi: moved all buddies from group %s to %s\n", old_name, gname);
@@ -3831,7 +3653,8 @@ static int purple_ssi_parseerr(OscarData *od, FlapConnection *conn, FlapFrame *f
_("Unable to Retrieve Buddy List"),
_("The AIM servers were temporarily unable to send "
"your buddy list. Your buddy list is not lost, and "
- "will probably become available in a few minutes."));
+ "will probably become available in a few minutes."),
+ purple_request_cpar_from_connection(gc));
od->getblisttimer = purple_timeout_add_seconds(30, purple_ssi_rerequestdata, od);
return 1;
}
@@ -3879,7 +3702,7 @@ static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *
GSList *cur, *next, *buddies;
struct aim_ssi_item *curitem;
guint32 tmp;
- PurpleStoredImage *img;
+ PurpleImage *img;
va_list ap;
guint16 deny_entry_type = aim_ssi_getdenyentrytype(od);
@@ -3901,14 +3724,11 @@ static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *
purple_debug_info("oscar", "ssi: syncing local list and server list\n");
- /* Clean the buddy list */
- aim_ssi_cleanlist(od);
-
/*** Begin code for pruning buddies from local list if they're not in server list ***/
/* Buddies */
cur = NULL;
- for (buddies = purple_find_buddies(account, NULL);
+ for (buddies = purple_blist_find_buddies(account, NULL);
buddies;
buddies = g_slist_delete_link(buddies, buddies))
{
@@ -3921,17 +3741,17 @@ static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *
gname = purple_group_get_name(g);
bname = purple_buddy_get_name(b);
- if (aim_ssi_itemlist_exists(od->ssi.local, bname)) {
+ if (aim_ssi_itemlist_exists(&od->ssi.local, bname)) {
/* If the buddy is an ICQ user then load his nickname */
const char *servernick = purple_blist_node_get_string((PurpleBlistNode*)b, "servernick");
char *alias;
const char *balias;
if (servernick)
- serv_got_alias(gc, bname, servernick);
+ purple_serv_got_alias(gc, bname, servernick);
/* Store local alias on server */
- alias = aim_ssi_getalias(od->ssi.local, gname, bname);
- balias = purple_buddy_get_local_buddy_alias(b);
+ alias = aim_ssi_getalias(&od->ssi.local, gname, bname);
+ balias = purple_buddy_get_local_alias(b);
if (!alias && balias && *balias)
aim_ssi_aliasbuddy(od, gname, bname, balias);
g_free(alias);
@@ -3949,32 +3769,32 @@ static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *
/* Permit list (ICQ doesn't have one) */
if (!od->icq) {
- next = account->permit;
+ next = purple_account_privacy_get_permitted(account);
while (next != NULL) {
cur = next;
next = next->next;
- if (!aim_ssi_itemlist_finditem(od->ssi.local, NULL, cur->data, AIM_SSI_TYPE_PERMIT)) {
+ if (!aim_ssi_itemlist_finditem(&od->ssi.local, NULL, cur->data, AIM_SSI_TYPE_PERMIT)) {
purple_debug_info("oscar",
"ssi: removing permit %s from local list\n", (const char *)cur->data);
- purple_privacy_permit_remove(account, cur->data, TRUE);
+ purple_account_privacy_permit_remove(account, cur->data, TRUE);
}
}
}
/* Deny list */
- next = account->deny;
+ next = purple_account_privacy_get_denied(account);
while (next != NULL) {
cur = next;
next = next->next;
- if (!aim_ssi_itemlist_finditem(od->ssi.local, NULL, cur->data, deny_entry_type)) {
+ if (!aim_ssi_itemlist_finditem(&od->ssi.local, NULL, cur->data, deny_entry_type)) {
purple_debug_info("oscar",
"ssi: removing deny %s from local list\n", (const char *)cur->data);
- purple_privacy_deny_remove(account, cur->data, TRUE);
+ purple_account_privacy_deny_remove(account, cur->data, TRUE);
}
}
/* Presence settings (idle time visibility) */
- tmp = aim_ssi_getpresence(od->ssi.local);
+ tmp = aim_ssi_getpresence(&od->ssi.local);
if (tmp != 0xFFFFFFFF) {
const char *idle_reporting_pref;
gboolean report_idle;
@@ -3992,7 +3812,7 @@ static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *
/*** Begin code for adding from server list to local list ***/
- for (curitem=od->ssi.local; curitem; curitem=curitem->next) {
+ for (curitem=od->ssi.local.data; curitem; curitem=curitem->next) {
if (curitem->name && !g_utf8_validate(curitem->name, -1, NULL)) {
/* Got node with invalid UTF-8 in the name. Skip it. */
purple_debug_warning("oscar", "ssi: server list contains item of "
@@ -4006,23 +3826,23 @@ static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *
struct aim_ssi_item *groupitem;
char *gname, *gname_utf8, *alias, *alias_utf8;
- groupitem = aim_ssi_itemlist_find(od->ssi.local, curitem->gid, 0x0000);
+ groupitem = aim_ssi_itemlist_find(&od->ssi.local, curitem->gid, 0x0000);
gname = groupitem ? groupitem->name : NULL;
gname_utf8 = oscar_utf8_try_convert(account, od, gname);
- g = purple_find_group(gname_utf8 ? gname_utf8 : _("Orphans"));
+ g = purple_blist_find_group(gname_utf8);
if (g == NULL) {
- g = purple_group_new(gname_utf8 ? gname_utf8 : _("Orphans"));
+ g = purple_group_new(gname_utf8);
purple_blist_add_group(g, NULL);
}
- alias = aim_ssi_getalias(od->ssi.local, gname, curitem->name);
+ alias = aim_ssi_getalias_from_item(curitem);
alias_utf8 = oscar_utf8_try_convert(account, od, alias);
- b = purple_find_buddy_in_group(account, curitem->name, g);
+ b = purple_blist_find_buddy_in_group(account, curitem->name, g);
if (b) {
/* Get server stored alias */
- purple_blist_alias_buddy(b, alias_utf8);
+ purple_buddy_set_local_alias(b, alias_utf8);
} else {
b = purple_buddy_new(account, curitem->name, alias_utf8);
@@ -4048,7 +3868,7 @@ static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *
} break;
case AIM_SSI_TYPE_GROUP: { /* Group */
- if (curitem->name != NULL && purple_find_group(curitem->name) == NULL) {
+ if (curitem->name != NULL && purple_blist_find_group(curitem->name) == NULL) {
g = purple_group_new(curitem->name);
purple_blist_add_group(g, NULL);
}
@@ -4056,11 +3876,11 @@ static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *
case AIM_SSI_TYPE_PERMIT: { /* Permit buddy (unless we're on ICQ) */
if (!od->icq && curitem->name) {
- for (cur = account->permit; (cur && oscar_util_name_compare(curitem->name, cur->data)); cur = cur->next);
+ for (cur = purple_account_privacy_get_permitted(account); (cur && oscar_util_name_compare(curitem->name, cur->data)); cur = cur->next);
if (!cur) {
purple_debug_info("oscar",
"ssi: adding permit buddy %s to local list\n", curitem->name);
- purple_privacy_permit_add(account, curitem->name, TRUE);
+ purple_account_privacy_permit_add(account, curitem->name, TRUE);
}
}
} break;
@@ -4068,11 +3888,11 @@ static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *
case AIM_SSI_TYPE_ICQDENY:
case AIM_SSI_TYPE_DENY: { /* Deny buddy */
if (curitem->type == deny_entry_type && curitem->name) {
- for (cur = account->deny; (cur && oscar_util_name_compare(curitem->name, cur->data)); cur = cur->next);
+ for (cur = purple_account_privacy_get_denied(account); (cur && oscar_util_name_compare(curitem->name, cur->data)); cur = cur->next);
if (!cur) {
purple_debug_info("oscar",
"ssi: adding deny buddy %s to local list\n", curitem->name);
- purple_privacy_deny_add(account, curitem->name, TRUE);
+ purple_account_privacy_deny_add(account, curitem->name, TRUE);
}
}
} break;
@@ -4085,12 +3905,12 @@ static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *
* a part of your status and not really related to blocking.
*/
if (!od->icq && curitem->data) {
- guint8 perm_deny = aim_ssi_getpermdeny(od->ssi.local);
- if (perm_deny != 0 && perm_deny != account->perm_deny)
+ guint8 perm_deny = aim_ssi_getpermdeny(&od->ssi.local);
+ if (perm_deny != 0 && perm_deny != purple_account_get_privacy_type(account))
{
purple_debug_info("oscar",
- "ssi: changing permdeny from %d to %hhu\n", account->perm_deny, perm_deny);
- account->perm_deny = perm_deny;
+ "ssi: changing permdeny from %d to %u\n", purple_account_get_privacy_type(account), (guint)perm_deny);
+ purple_account_set_privacy_type(account, perm_deny);
}
}
} break;
@@ -4123,7 +3943,7 @@ static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *
*/
img = purple_buddy_icons_find_account_icon(account);
oscar_set_icon(gc, img);
- purple_imgstore_unref(img);
+ g_object_unref(img);
/*
* If we've already received our bos rights then we're not waiting on
@@ -4135,7 +3955,7 @@ static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *
/* Request offline messages for AIM and ICQ */
aim_im_reqofflinemsgs(od);
- purple_connection_set_state(gc, PURPLE_CONNECTED);
+ purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
}
return 1;
@@ -4162,8 +3982,8 @@ static int purple_ssi_parseack(OscarData *od, FlapConnection *conn, FlapFrame *f
case 0x000c: { /* you are over the limit, the cheat is to the limit, come on fhqwhgads */
gchar *buf;
buf = g_strdup_printf(_("Unable to add the buddy %s because you have too many buddies in your buddy list. Please remove one and try again."), (retval->name ? retval->name : _("(no name)")));
- if ((retval->name != NULL) && !purple_conv_present_error(retval->name, purple_connection_get_account(gc), buf))
- purple_notify_error(gc, NULL, _("Unable to Add"), buf);
+ if ((retval->name != NULL) && !purple_conversation_present_error(retval->name, purple_connection_get_account(gc), buf))
+ purple_notify_error(gc, NULL, _("Unable to Add"), buf, purple_request_cpar_from_connection(gc));
g_free(buf);
} break;
@@ -4177,8 +3997,8 @@ static int purple_ssi_parseack(OscarData *od, FlapConnection *conn, FlapFrame *f
purple_debug_error("oscar", "ssi: Action 0x%04hx was unsuccessful with error 0x%04hx\n", retval->action, retval->ack);
buf = g_strdup_printf(_("Unable to add the buddy %s for an unknown reason."),
(retval->name ? retval->name : _("(no name)")));
- if ((retval->name != NULL) && !purple_conv_present_error(retval->name, purple_connection_get_account(gc), buf))
- purple_notify_error(gc, NULL, _("Unable to Add"), buf);
+ if ((retval->name != NULL) && !purple_conversation_present_error(retval->name, purple_connection_get_account(gc), buf))
+ purple_notify_error(gc, NULL, _("Unable to Add"), buf, purple_request_cpar_from_connection(gc));
g_free(buf);
} break;
}
@@ -4214,21 +4034,21 @@ purple_ssi_parseaddmod(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
if ((type != 0x0000) || (name == NULL))
return 1;
- gname = aim_ssi_itemlist_findparentname(od->ssi.local, name);
+ gname = aim_ssi_itemlist_findparentname(&od->ssi.local, name);
gname_utf8 = gname ? oscar_utf8_try_convert(account, od, gname) : NULL;
- alias = aim_ssi_getalias(od->ssi.local, gname, name);
+ alias = aim_ssi_getalias(&od->ssi.local, gname, name);
alias_utf8 = oscar_utf8_try_convert(account, od, alias);
g_free(alias);
- b = purple_find_buddy(account, name);
+ b = purple_blist_find_buddy(account, name);
if (b) {
/*
* You're logged in somewhere else and you aliased one
* of your buddies, so update our local buddy list with
* the person's new alias.
*/
- purple_blist_alias_buddy(b, alias_utf8);
+ purple_buddy_set_local_alias(b, alias_utf8);
} else if (snac_subtype == 0x0008) {
/*
* You're logged in somewhere else and you added a buddy to
@@ -4236,13 +4056,13 @@ purple_ssi_parseaddmod(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
*/
b = purple_buddy_new(account, name, alias_utf8);
- if (!(g = purple_find_group(gname_utf8 ? gname_utf8 : _("Orphans")))) {
- g = purple_group_new(gname_utf8 ? gname_utf8 : _("Orphans"));
+ if (!(g = purple_blist_find_group(gname_utf8))) {
+ g = purple_group_new(gname_utf8);
purple_blist_add_group(g, NULL);
}
- purple_debug_info("oscar",
- "ssi: adding buddy %s to group %s to local list\n", name, gname_utf8 ? gname_utf8 : _("Orphans"));
+ purple_debug_info("oscar", "ssi: adding buddy %s to group %s to"
+ " local list", name, gname_utf8 ? gname_utf8 : "(default)");
purple_blist_add_buddy(b, NULL, g, NULL);
/* Mobile users should always be online */
@@ -4255,7 +4075,7 @@ purple_ssi_parseaddmod(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
}
- ssi_item = aim_ssi_itemlist_finditem(od->ssi.local,
+ ssi_item = aim_ssi_itemlist_finditem(&od->ssi.local,
gname, name, AIM_SSI_TYPE_BUDDY);
if (ssi_item == NULL)
{
@@ -4286,7 +4106,7 @@ static int purple_ssi_authgiven(OscarData *od, FlapConnection *conn, FlapFrame *
purple_debug_info("oscar",
"ssi: %s has given you permission to add him to your buddy list\n", bn);
- buddy = purple_find_buddy(purple_connection_get_account(gc), bn);
+ buddy = purple_blist_find_buddy(purple_connection_get_account(gc), bn);
if (buddy && (purple_buddy_get_alias_only(buddy)))
nombre = g_strdup_printf("%s (%s)", bn, purple_buddy_get_alias_only(buddy));
else
@@ -4302,7 +4122,7 @@ static int purple_ssi_authgiven(OscarData *od, FlapConnection *conn, FlapFrame *
purple_request_yes_no(gc, NULL, _("Authorization Given"), dialog_msg,
PURPLE_DEFAULT_ACTION_NONE,
- purple_connection_get_account(gc), bn, NULL,
+ purple_request_cpar_from_connection(gc),
data,
G_CALLBACK(purple_icq_buddyadd),
G_CALLBACK(oscar_free_name_data));
@@ -4353,9 +4173,9 @@ static int purple_ssi_authreply(OscarData *od, FlapConnection *conn, FlapFrame *
va_end(ap);
purple_debug_info("oscar",
- "ssi: received authorization reply from %s. Reply is 0x%04hhx\n", bn, reply);
+ "ssi: received authorization reply from %s. Reply is 0x%02hx\n", bn, (guint16)reply);
- buddy = purple_find_buddy(purple_connection_get_account(gc), bn);
+ buddy = purple_blist_find_buddy(purple_connection_get_account(gc), bn);
if (buddy && (purple_buddy_get_alias_only(buddy)))
nombre = g_strdup_printf("%s (%s)", bn, purple_buddy_get_alias_only(buddy));
else
@@ -4364,11 +4184,13 @@ static int purple_ssi_authreply(OscarData *od, FlapConnection *conn, FlapFrame *
if (reply) {
/* Granted */
dialog_msg = g_strdup_printf(_("The user %s has granted your request to add them to your buddy list."), nombre);
- purple_notify_info(gc, NULL, _("Authorization Granted"), dialog_msg);
+ purple_notify_info(gc, NULL, _("Authorization Granted"),
+ dialog_msg, purple_request_cpar_from_connection(gc));
} else {
/* Denied */
dialog_msg = g_strdup_printf(_("The user %s has denied your request to add them to your buddy list for the following reason:\n%s"), nombre, msg ? msg : _("No reason given."));
- purple_notify_info(gc, NULL, _("Authorization Denied"), dialog_msg);
+ purple_notify_info(gc, NULL, _("Authorization Denied"),
+ dialog_msg, purple_request_cpar_from_connection(gc));
}
g_free(dialog_msg);
g_free(nombre);
@@ -4387,7 +4209,7 @@ static int purple_ssi_gotadded(OscarData *od, FlapConnection *conn, FlapFrame *f
bn = va_arg(ap, char *);
va_end(ap);
- buddy = purple_find_buddy(account, bn);
+ buddy = purple_blist_find_buddy(account, bn);
purple_debug_info("oscar", "ssi: %s added you to their buddy list\n", bn);
purple_account_notify_added(account, bn, NULL,
(buddy ? purple_buddy_get_alias_only(buddy) : NULL), NULL);
@@ -4487,32 +4309,33 @@ oscar_chat_invite(PurpleConnection *gc, int id, const char *message, const char
void
oscar_chat_leave(PurpleConnection *gc, int id)
{
- PurpleConversation *conv;
+ PurpleChatConversation *conv;
struct chat_connection *cc;
- conv = purple_find_chat(gc, id);
+ conv = purple_conversations_find_chat(gc, id);
g_return_if_fail(conv != NULL);
purple_debug_info("oscar", "Leaving chat room %s\n",
- purple_conversation_get_name(conv));
+ purple_conversation_get_name(PURPLE_CONVERSATION(conv)));
- cc = find_oscar_chat(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)));
+ cc = find_oscar_chat(gc, purple_chat_conversation_get_id(conv));
flap_connection_schedule_destroy(cc->conn, OSCAR_DISCONNECT_DONE, NULL);
oscar_chat_kill(gc, cc);
}
-int oscar_send_chat(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags)
+int oscar_send_chat(PurpleConnection *gc, int id, PurpleMessage *msg)
{
OscarData *od = purple_connection_get_protocol_data(gc);
- PurpleConversation *conv = NULL;
+ PurpleChatConversation *conv = NULL;
struct chat_connection *c = NULL;
char *buf, *buf2, *buf3;
guint16 charset;
char *charsetstr;
gsize len;
+ const gchar *message = purple_message_get_contents(msg);
- if (!(conv = purple_find_chat(gc, id)))
+ if (!(conv = purple_conversations_find_chat(gc, id)))
return -EINVAL;
if (!(c = find_oscar_chat_by_conv(gc, conv)))
@@ -4520,11 +4343,12 @@ int oscar_send_chat(PurpleConnection *gc, int id, const char *message, PurpleMes
buf = purple_strdup_withhtml(message);
- if (strstr(buf, "<IMG "))
- purple_conversation_write(conv, "",
+ if (strstr(buf, "<img ")) {
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(conv),
_("Your IM Image was not sent. "
"You cannot send IM Images in AIM chats."),
- PURPLE_MESSAGE_ERROR, time(NULL));
+ PURPLE_MESSAGE_ERROR);
+ }
buf2 = oscar_encode_im(buf, &len, &charset, &charsetstr);
/*
@@ -4610,8 +4434,8 @@ const char *oscar_list_emblem(PurpleBuddy *b)
if (purple_presence_is_online(presence) == FALSE) {
char *gname;
if ((name) && (od) && (od->ssi.received_data) &&
- (gname = aim_ssi_itemlist_findparentname(od->ssi.local, name)) &&
- (aim_ssi_waitingforauth(od->ssi.local, gname, name))) {
+ (gname = aim_ssi_itemlist_findparentname(&od->ssi.local, name)) &&
+ (aim_ssi_waitingforauth(&od->ssi.local, gname, name))) {
return "not-authorized";
}
}
@@ -4676,8 +4500,8 @@ char *oscar_status_text(PurpleBuddy *b)
if ((od != NULL) && !purple_presence_is_online(presence))
{
const char *name = purple_buddy_get_name(b);
- char *gname = aim_ssi_itemlist_findparentname(od->ssi.local, name);
- if (aim_ssi_waitingforauth(od->ssi.local, gname, name))
+ char *gname = aim_ssi_itemlist_findparentname(&od->ssi.local, name);
+ if (aim_ssi_waitingforauth(&od->ssi.local, gname, name))
ret = g_strdup(_("Not Authorized"));
else
ret = g_strdup(_("Offline"));
@@ -4713,7 +4537,7 @@ void oscar_set_aim_permdeny(PurpleConnection *gc) {
* values of libpurple's PurplePrivacyType and the values used
* by the oscar protocol.
*/
- aim_ssi_setpermdeny(od, account->perm_deny);
+ aim_ssi_setpermdeny(od, purple_account_get_privacy_type(account));
}
void oscar_add_permit(PurpleConnection *gc, const char *who) {
@@ -4758,16 +4582,16 @@ oscar_status_types(PurpleAccount *account)
OSCAR_STATUS_ID_AVAILABLE,
NULL, TRUE, TRUE, FALSE,
"message", _("Message"),
- purple_value_new(PURPLE_TYPE_STRING),
+ purple_value_new(G_TYPE_STRING),
"itmsurl", _("iTunes Music Store Link"),
- purple_value_new(PURPLE_TYPE_STRING), NULL);
+ purple_value_new(G_TYPE_STRING), NULL);
status_types = g_list_prepend(status_types, type);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
OSCAR_STATUS_ID_FREE4CHAT,
_("Free For Chat"), TRUE, is_icq, FALSE,
"message", _("Message"),
- purple_value_new(PURPLE_TYPE_STRING), NULL);
+ purple_value_new(G_TYPE_STRING), NULL);
status_types = g_list_prepend(status_types, type);
@@ -4775,7 +4599,7 @@ oscar_status_types(PurpleAccount *account)
OSCAR_STATUS_ID_EVIL,
_("Evil"), TRUE, is_icq, FALSE,
"message", _("Message"),
- purple_value_new(PURPLE_TYPE_STRING), NULL);
+ purple_value_new(G_TYPE_STRING), NULL);
status_types = g_list_prepend(status_types, type);
@@ -4783,7 +4607,7 @@ oscar_status_types(PurpleAccount *account)
OSCAR_STATUS_ID_DEPRESSION,
_("Depression"), TRUE, is_icq, FALSE,
"message", _("Message"),
- purple_value_new(PURPLE_TYPE_STRING), NULL);
+ purple_value_new(G_TYPE_STRING), NULL);
status_types = g_list_prepend(status_types, type);
@@ -4791,7 +4615,7 @@ oscar_status_types(PurpleAccount *account)
OSCAR_STATUS_ID_ATHOME,
_("At home"), TRUE, is_icq, FALSE,
"message", _("Message"),
- purple_value_new(PURPLE_TYPE_STRING), NULL);
+ purple_value_new(G_TYPE_STRING), NULL);
status_types = g_list_prepend(status_types, type);
@@ -4799,7 +4623,7 @@ oscar_status_types(PurpleAccount *account)
OSCAR_STATUS_ID_ATWORK,
_("At work"), TRUE, is_icq, FALSE,
"message", _("Message"),
- purple_value_new(PURPLE_TYPE_STRING), NULL);
+ purple_value_new(G_TYPE_STRING), NULL);
status_types = g_list_prepend(status_types, type);
@@ -4808,7 +4632,7 @@ oscar_status_types(PurpleAccount *account)
OSCAR_STATUS_ID_LUNCH,
_("Lunch"), TRUE, is_icq, FALSE,
"message", _("Message"),
- purple_value_new(PURPLE_TYPE_STRING), NULL);
+ purple_value_new(G_TYPE_STRING), NULL);
status_types = g_list_prepend(status_types, type);
@@ -4816,14 +4640,14 @@ oscar_status_types(PurpleAccount *account)
OSCAR_STATUS_ID_AWAY,
NULL, TRUE, TRUE, FALSE,
"message", _("Message"),
- purple_value_new(PURPLE_TYPE_STRING), NULL);
+ purple_value_new(G_TYPE_STRING), NULL);
status_types = g_list_prepend(status_types, type);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_INVISIBLE,
OSCAR_STATUS_ID_INVISIBLE,
NULL, TRUE, TRUE, FALSE,
"message", _("Message"),
- purple_value_new(PURPLE_TYPE_STRING), NULL);
+ purple_value_new(G_TYPE_STRING), NULL);
status_types = g_list_prepend(status_types, type);
@@ -4835,21 +4659,21 @@ oscar_status_types(PurpleAccount *account)
OSCAR_STATUS_ID_OCCUPIED,
_("Occupied"), TRUE, is_icq, FALSE,
"message", _("Message"),
- purple_value_new(PURPLE_TYPE_STRING), NULL);
+ purple_value_new(G_TYPE_STRING), NULL);
status_types = g_list_prepend(status_types, type);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE,
OSCAR_STATUS_ID_DND,
_("Do Not Disturb"), TRUE, is_icq, FALSE,
"message", _("Message"),
- purple_value_new(PURPLE_TYPE_STRING), NULL);
+ purple_value_new(G_TYPE_STRING), NULL);
status_types = g_list_prepend(status_types, type);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_EXTENDED_AWAY,
OSCAR_STATUS_ID_NA,
_("Not Available"), TRUE, is_icq, FALSE,
"message", _("Message"),
- purple_value_new(PURPLE_TYPE_STRING), NULL);
+ purple_value_new(G_TYPE_STRING), NULL);
status_types = g_list_prepend(status_types, type);
type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
@@ -4859,8 +4683,8 @@ oscar_status_types(PurpleAccount *account)
type = purple_status_type_new_with_attrs(PURPLE_STATUS_MOOD,
"mood", NULL, TRUE, is_icq, TRUE,
- PURPLE_MOOD_NAME, _("Mood Name"), purple_value_new(PURPLE_TYPE_STRING),
- PURPLE_MOOD_COMMENT, _("Mood Comment"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_MOOD_NAME, _("Mood Name"), purple_value_new(G_TYPE_STRING),
+ PURPLE_MOOD_COMMENT, _("Mood Comment"), purple_value_new(G_TYPE_STRING),
NULL);
status_types = g_list_prepend(status_types, type);
@@ -4878,7 +4702,7 @@ static void oscar_ssi_editcomment(struct name_data *data, const char *text) {
od = purple_connection_get_protocol_data(gc);
account = purple_connection_get_account(gc);
- b = purple_find_buddy(account, data->name);
+ b = purple_blist_find_buddy(account, data->name);
if (b == NULL) {
oscar_free_name_data(data);
return;
@@ -4907,7 +4731,7 @@ static void oscar_buddycb_edit_comment(PurpleBlistNode *node, gpointer ignore) {
PurpleAccount *account;
const char *name;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *) node;
name = purple_buddy_get_name(buddy);
@@ -4920,7 +4744,7 @@ static void oscar_buddycb_edit_comment(PurpleBlistNode *node, gpointer ignore) {
data = g_new(struct name_data, 1);
- comment = aim_ssi_getcomment(od->ssi.local, purple_group_get_name(g), name);
+ comment = aim_ssi_getcomment(&od->ssi.local, purple_group_get_name(g), name);
comment_utf8 = comment ? oscar_utf8_try_convert(account, od, comment) : NULL;
data->gc = gc;
@@ -4932,7 +4756,7 @@ static void oscar_buddycb_edit_comment(PurpleBlistNode *node, gpointer ignore) {
comment_utf8, TRUE, FALSE, NULL,
_("_OK"), G_CALLBACK(oscar_ssi_editcomment),
_("_Cancel"), G_CALLBACK(oscar_free_name_data),
- account, data->name, NULL,
+ purple_request_cpar_from_connection(gc),
data);
g_free(title);
@@ -4968,7 +4792,7 @@ oscar_ask_directim(gpointer object, gpointer ignored)
node = object;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *)node;
account = purple_buddy_get_account(buddy);
@@ -4985,7 +4809,7 @@ oscar_ask_directim(gpointer object, gpointer ignored)
"may be considered a security risk. Do you "
"wish to continue?"),
0, /* Default action is "connect" */
- account, data->who, NULL,
+ purple_request_cpar_from_account(account),
data, 2,
_("C_onnect"), G_CALLBACK(oscar_ask_directim_yes_cb),
_("_Cancel"), G_CALLBACK(oscar_ask_directim_no_cb));
@@ -4999,20 +4823,20 @@ oscar_close_directim(gpointer object, gpointer ignored)
PurpleBuddy *buddy;
PurpleAccount *account;
PurpleConnection *gc;
- PurpleConversation *conv;
+ PurpleIMConversation *im;
OscarData *od;
PeerConnection *conn;
const char *name;
node = object;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy*)node;
name = purple_buddy_get_name(buddy);
account = purple_buddy_get_account(buddy);
gc = purple_account_get_connection(account);
- od = gc->proto_data;
+ od = purple_connection_get_protocol_data(gc);
conn = peer_connection_find_by_type(od, name, OSCAR_CAPABILITY_DIRECTIM);
if (conn != NULL)
@@ -5024,9 +4848,9 @@ oscar_close_directim(gpointer object, gpointer ignored)
/* OSCAR_DISCONNECT_LOCAL_CLOSED doesn't write anything to the convo
* window. Let the user know that we cancelled the Direct IM. */
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, name);
- purple_conversation_write(conv, NULL, _("You closed the connection."),
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ im = purple_im_conversation_new(account, name);
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(im), _("You closed the connection."), 0);
}
}
@@ -5038,7 +4862,7 @@ static void oscar_get_icqxstatusmsg(PurpleBlistNode *node, gpointer ignore)
PurpleAccount *account;
const char *bname;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *)node;
bname = purple_buddy_get_name(buddy);
@@ -5058,7 +4882,7 @@ oscar_get_aim_info_cb(PurpleBlistNode *node, gpointer ignore)
PurpleBuddy *buddy;
PurpleConnection *gc;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *)node;
gc = purple_account_get_connection(purple_buddy_get_account(buddy));
@@ -5141,8 +4965,8 @@ oscar_buddy_menu(PurpleBuddy *buddy) {
* waiting for authorization.
*/
char *gname;
- gname = aim_ssi_itemlist_findparentname(od->ssi.local, bname);
- if (gname && aim_ssi_waitingforauth(od->ssi.local, gname, bname))
+ gname = aim_ssi_itemlist_findparentname(&od->ssi.local, bname);
+ if (gname && aim_ssi_waitingforauth(&od->ssi.local, gname, bname))
{
act = purple_menu_action_new(_("Re-request Authorization"),
PURPLE_CALLBACK(oscar_auth_sendrequest_menu),
@@ -5158,7 +4982,7 @@ oscar_buddy_menu(PurpleBuddy *buddy) {
GList *oscar_blist_node_menu(PurpleBlistNode *node) {
- if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ if(PURPLE_IS_BUDDY(node)) {
return oscar_buddy_menu((PurpleBuddy *) node);
} else {
return NULL;
@@ -5215,7 +5039,7 @@ oscar_show_icq_privacy_opts(PurplePluginAction *action)
NULL, fields,
_("OK"), G_CALLBACK(oscar_icq_privacy_opts),
_("Cancel"), NULL,
- purple_connection_get_account(gc), NULL, NULL,
+ purple_request_cpar_from_connection(gc),
gc);
}
@@ -5272,7 +5096,7 @@ static void oscar_show_change_email(PurplePluginAction *action)
FALSE, FALSE, NULL,
_("_OK"), G_CALLBACK(oscar_change_email),
_("_Cancel"), NULL,
- purple_connection_get_account(gc), NULL, NULL,
+ purple_request_cpar_from_connection(gc),
gc);
}
@@ -5284,7 +5108,7 @@ static void oscar_show_awaitingauth(PurplePluginAction *action)
GSList *buddies, *filtered_buddies, *cur;
gchar *text;
- buddies = purple_find_buddies(account, NULL);
+ buddies = purple_blist_find_buddies(account, NULL);
filtered_buddies = NULL;
for (cur = buddies; cur != NULL; cur = cur->next) {
PurpleBuddy *buddy;
@@ -5293,7 +5117,7 @@ static void oscar_show_awaitingauth(PurplePluginAction *action)
buddy = cur->data;
bname = purple_buddy_get_name(buddy);
gname = purple_group_get_name(purple_buddy_get_group(buddy));
- if (aim_ssi_waitingforauth(od->ssi.local, gname, bname)) {
+ if (aim_ssi_waitingforauth(&od->ssi.local, gname, bname)) {
filtered_buddies = g_slist_prepend(filtered_buddies, buddy);
}
}
@@ -5329,7 +5153,7 @@ static void oscar_show_find_email(PurplePluginAction *action)
NULL, FALSE, FALSE, NULL,
_("_Search"), G_CALLBACK(search_by_email_cb),
_("_Cancel"), NULL,
- purple_connection_get_account(gc), NULL, NULL,
+ purple_request_cpar_from_connection(gc),
gc);
}
@@ -5369,22 +5193,22 @@ static void oscar_show_imforwardingurl(PurplePluginAction *action)
purple_notify_uri(gc, "http://mymobile.aol.com/dbreg/register?action=imf&clientID=1");
}
-void oscar_set_icon(PurpleConnection *gc, PurpleStoredImage *img)
+void oscar_set_icon(PurpleConnection *gc, PurpleImage *img)
{
OscarData *od = purple_connection_get_protocol_data(gc);
if (img == NULL) {
aim_ssi_delicon(od);
} else {
- PurpleCipherContext *context;
+ PurpleHash *hash;
guchar md5[16];
- gconstpointer data = purple_imgstore_get_data(img);
- size_t len = purple_imgstore_get_size(img);
+ gconstpointer data = purple_image_get_data(img);
+ size_t len = purple_image_get_size(img);
- context = purple_cipher_context_new_by_name("md5", NULL);
- purple_cipher_context_append(context, data, len);
- purple_cipher_context_digest(context, 16, md5, NULL);
- purple_cipher_context_destroy(context);
+ hash = purple_md5_hash_new();
+ purple_hash_append(hash, data, len);
+ purple_hash_digest(hash, md5, sizeof(md5));
+ g_object_unref(hash);
aim_ssi_seticon(od, md5, 16);
}
@@ -5434,10 +5258,9 @@ oscar_new_xfer(PurpleConnection *gc, const char *who)
od = purple_connection_get_protocol_data(gc);
account = purple_connection_get_account(gc);
- xfer = purple_xfer_new(account, PURPLE_XFER_SEND, who);
+ xfer = purple_xfer_new(account, PURPLE_XFER_TYPE_SEND, who);
if (xfer)
{
- purple_xfer_ref(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);
@@ -5448,7 +5271,7 @@ oscar_new_xfer(PurpleConnection *gc, const char *who)
conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
aim_icbm_makecookie(conn->cookie);
conn->xfer = xfer;
- xfer->data = conn;
+ purple_xfer_set_protocol_data(xfer, conn);
}
return xfer;
@@ -5633,6 +5456,13 @@ oscar_offline_message(const PurpleBuddy *buddy)
return TRUE;
}
+gssize
+oscar_get_max_message_size(PurpleConversation *conv)
+{
+ /* XXX: got from pidgin-otr - verify and document it */
+ return 2343;
+}
+
/* TODO: Find somewhere to put this instead of including it in a bunch of places.
* Maybe just change purple_accounts_find() to return anything for the prpl if there is no acct_id.
*/
@@ -5683,16 +5513,16 @@ static gboolean oscar_uri_handler(const char *proto, const char *cmd, GHashTable
if (bname) {
char *message = g_hash_table_lookup(params, "message");
- PurpleConversation *conv = purple_find_conversation_with_account(
- PURPLE_CONV_TYPE_IM, bname, acct);
- if (conv == NULL)
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, bname);
- purple_conversation_present(conv);
+ PurpleIMConversation *im = purple_conversations_find_im_with_account(
+ bname, acct);
+ if (im == NULL)
+ im = purple_im_conversation_new(acct, bname);
+ purple_conversation_present(PURPLE_CONVERSATION(im));
if (message) {
/* Spaces are encoded as '+' */
g_strdelimit(message, "+", ' ');
- purple_conv_send_confirm(conv, message);
+ purple_conversation_send_confirm(PURPLE_CONVERSATION(im), message);
}
}
/*else
@@ -5709,7 +5539,7 @@ static gboolean oscar_uri_handler(const char *proto, const char *cmd, GHashTable
/* This is somewhat hacky, but the params aren't useful after this command */
g_hash_table_insert(params, g_strdup("exchange"), g_strdup("4"));
g_hash_table_insert(params, g_strdup("room"), g_strdup(rname));
- serv_join_chat(purple_account_get_connection(acct), params);
+ purple_serv_join_chat(purple_account_get_connection(acct), params);
}
/*else
** Same as above (except that this would have to be re-written using purple_request_*)
diff --git a/libpurple/protocols/oscar/oscar.h b/libpurple/protocols/oscar/oscar.h
index fe01cecc48..1ad5b1c1d6 100644
--- a/libpurple/protocols/oscar/oscar.h
+++ b/libpurple/protocols/oscar/oscar.h
@@ -30,9 +30,10 @@
#define _OSCAR_H_
#include "internal.h"
-#include "circbuffer.h"
+#include "circularbuffer.h"
#include "debug.h"
#include "eventloop.h"
+#include "http.h"
#include "proxy.h"
#include "sslconn.h"
@@ -51,8 +52,6 @@
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
-#else
-#include "libc_interface.h"
#endif
typedef struct _ByteStream ByteStream;
@@ -114,6 +113,8 @@ extern "C" {
#define AIM_MD5_STRING "AOL Instant Messenger (SM)"
+#define OSCAR_CONNECT_STEPS 6
+
/*
* Client info. Filled in by the client and passed in to
* aim_send_login(). The information ends up getting passed to OSCAR
@@ -277,7 +278,7 @@ struct _FlapConnection
guint8 header[6];
gssize header_received;
FlapFrame buffer_incoming;
- PurpleCircBuffer *buffer_outgoing;
+ PurpleCircularBuffer *buffer_outgoing;
guint watcher_incoming;
guint watcher_outgoing;
@@ -308,14 +309,19 @@ struct _IcbmCookie
#include "peer.h"
-/*
- * AIM Session: The main client-data interface.
- *
+struct aim_ssi_itemlist {
+ struct aim_ssi_item *data;
+ GHashTable *idx_gid_bid;
+ GHashTable *idx_all_named_items;
+};
+
+/**
+ * The main client-data interface.
*/
struct _OscarData
{
/** Only used when connecting with clientLogin */
- PurpleUtilFetchUrlData *url_data;
+ PurpleHttpConnection *hc;
gboolean iconconnecting;
gboolean set_icon;
@@ -387,8 +393,8 @@ struct _OscarData
struct {
gboolean received_data;
guint16 numitems;
- struct aim_ssi_item *official;
- struct aim_ssi_item *local;
+ struct aim_ssi_itemlist official;
+ struct aim_ssi_itemlist local;
struct aim_ssi_tmp *pending;
time_t timestamp;
gboolean waiting_for_ack;
@@ -594,9 +600,9 @@ struct chat_connection
FlapConnection *conn;
int id;
PurpleConnection *gc;
- PurpleConversation *conv;
- int maxlen;
- int maxvis;
+ PurpleChatConversation *conv;
+ guint16 maxlen;
+ guint16 maxvis;
};
/*
@@ -805,11 +811,6 @@ typedef struct aim_userinfo_s
struct aim_userinfo_s *next;
} aim_userinfo_t;
-#define AIM_SENDMEMBLOCK_FLAG_ISREQUEST 0
-#define AIM_SENDMEMBLOCK_FLAG_ISHASH 1
-
-int aim_sendmemblock(OscarData *od, FlapConnection *conn, guint32 offset, guint32 len, const guint8 *buf, guint8 flag);
-
struct aim_invite_priv
{
char *bn;
@@ -916,15 +917,16 @@ struct aim_ssi_tmp
/* 0x001a */ int aim_ssi_sendauthreply(OscarData *od, const char *bn, guint8 reply, const char *msg);
/* Client functions for retrieving SSI data */
-struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, guint16 gid, guint16 bid);
-struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *bn, guint16 type);
-struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *bn);
-char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *bn);
-int aim_ssi_getpermdeny(struct aim_ssi_item *list);
-guint32 aim_ssi_getpresence(struct aim_ssi_item *list);
-char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *bn);
-char *aim_ssi_getcomment(struct aim_ssi_item *list, const char *gn, const char *bn);
-gboolean aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *bn);
+struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_itemlist *list, guint16 gid, guint16 bid);
+struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_itemlist *list, const char *gn, const char *bn, guint16 type);
+struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_itemlist *list, const char *bn);
+char *aim_ssi_itemlist_findparentname(struct aim_ssi_itemlist *list, const char *bn);
+int aim_ssi_getpermdeny(struct aim_ssi_itemlist *list);
+guint32 aim_ssi_getpresence(struct aim_ssi_itemlist *list);
+char *aim_ssi_getalias(struct aim_ssi_itemlist *list, const char *gn, const char *bn);
+char *aim_ssi_getalias_from_item(struct aim_ssi_item *item);
+char *aim_ssi_getcomment(struct aim_ssi_itemlist *list, const char *gn, const char *bn);
+gboolean aim_ssi_waitingforauth(struct aim_ssi_itemlist *list, const char *gn, const char *bn);
/* Client functions for changing SSI data */
int aim_ssi_addbuddy(OscarData *od, const char *name, const char *group, GSList *tlvlist, const char *alias, const char *comment, const char *smsnum, gboolean needauth);
@@ -934,7 +936,6 @@ int aim_ssi_movebuddy(OscarData *od, const char *oldgn, const char *newgn, const
int aim_ssi_aliasbuddy(OscarData *od, const char *gn, const char *bn, const char *alias);
int aim_ssi_editcomment(OscarData *od, const char *gn, const char *bn, const char *alias);
int aim_ssi_rename_group(OscarData *od, const char *oldgn, const char *newgn);
-int aim_ssi_cleanlist(OscarData *od);
int aim_ssi_deletelist(OscarData *od);
int aim_ssi_setpermdeny(OscarData *od, guint8 permdeny);
int aim_ssi_setpresence(OscarData *od, guint32 presence);
@@ -1070,7 +1071,7 @@ GSList *aim_tlvlist_readlen(ByteStream *bs, guint16 len);
GSList *aim_tlvlist_copy(GSList *orig);
int aim_tlvlist_count(GSList *list);
-int aim_tlvlist_size(GSList *list);
+size_t aim_tlvlist_size(GSList *list);
int aim_tlvlist_cmp(GSList *one, GSList *two);
int aim_tlvlist_write(ByteStream *bs, GSList **list);
void aim_tlvlist_free(GSList *list);
@@ -1212,7 +1213,7 @@ void aim_genericreq_l(OscarData *od, FlapConnection *conn, guint16 family, guint
int byte_stream_new(ByteStream *bs, size_t len);
int byte_stream_init(ByteStream *bs, guint8 *data, size_t len);
void byte_stream_destroy(ByteStream *bs);
-int byte_stream_bytes_left(ByteStream *bs);
+size_t byte_stream_bytes_left(ByteStream *bs);
int byte_stream_curpos(ByteStream *bs);
int byte_stream_setpos(ByteStream *bs, size_t off);
void byte_stream_rewind(ByteStream *bs);
diff --git a/libpurple/protocols/oscar/oscar_data.c b/libpurple/protocols/oscar/oscar_data.c
index 5498bba986..ef54956eb2 100644
--- a/libpurple/protocols/oscar/oscar_data.c
+++ b/libpurple/protocols/oscar/oscar_data.c
@@ -47,6 +47,12 @@ oscar_data_new(void)
od->buddyinfo = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
od->handlerlist = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
+ od->ssi.local.idx_gid_bid = g_hash_table_new(g_direct_hash, g_direct_equal);
+ od->ssi.local.idx_all_named_items = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+
+ od->ssi.official.idx_gid_bid = g_hash_table_new(g_direct_hash, g_direct_equal);
+ od->ssi.official.idx_all_named_items = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+
/*
* Register all the modules for this session...
*/
@@ -100,8 +106,7 @@ oscar_data_destroy(OscarData *od)
aim_cleansnacs(od, -1);
/* Only used when connecting with clientLogin */
- if (od->url_data != NULL)
- purple_util_fetch_url_cancel(od->url_data);
+ purple_http_conn_cancel(od->hc);
while (od->requesticon)
{
@@ -126,6 +131,12 @@ oscar_data_destroy(OscarData *od)
g_hash_table_destroy(od->buddyinfo);
g_hash_table_destroy(od->handlerlist);
+ g_hash_table_destroy(od->ssi.local.idx_gid_bid);
+ g_hash_table_destroy(od->ssi.local.idx_all_named_items);
+
+ g_hash_table_destroy(od->ssi.official.idx_gid_bid);
+ g_hash_table_destroy(od->ssi.official.idx_all_named_items);
+
g_free(od);
}
diff --git a/libpurple/protocols/oscar/oscarcommon.h b/libpurple/protocols/oscar/oscarcommon.h
index a0647dfe44..be980d9d2d 100644
--- a/libpurple/protocols/oscar/oscarcommon.h
+++ b/libpurple/protocols/oscar/oscarcommon.h
@@ -72,9 +72,9 @@ GList *oscar_chat_info(PurpleConnection *gc);
GHashTable *oscar_chat_info_defaults(PurpleConnection *gc, const char *chat_name);
void oscar_login(PurpleAccount *account);
void oscar_close(PurpleConnection *gc);
-int oscar_send_im(PurpleConnection *gc, const char *name, const char *message, PurpleMessageFlags imflags);
+int oscar_send_im(PurpleConnection *gc, PurpleMessage *msg);
void oscar_set_info(PurpleConnection *gc, const char *rawinfo);
-unsigned int oscar_send_typing(PurpleConnection *gc, const char *name, PurpleTypingState state);
+unsigned int oscar_send_typing(PurpleConnection *gc, const char *name, PurpleIMTypingState state);
void oscar_get_info(PurpleConnection *gc, const char *name);
void oscar_set_status(PurpleAccount *account, PurpleStatus *status);
void oscar_set_idle(PurpleConnection *gc, int time);
@@ -89,18 +89,19 @@ void oscar_join_chat(PurpleConnection *gc, GHashTable *data);
char *oscar_get_chat_name(GHashTable *data);
void oscar_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name);
void oscar_chat_leave(PurpleConnection *gc, int id);
-int oscar_send_chat(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags);
+int oscar_send_chat(PurpleConnection *gc, int id, PurpleMessage *msg);
void oscar_keepalive(PurpleConnection *gc);
void oscar_alias_buddy(PurpleConnection *gc, const char *name, const char *alias);
void oscar_move_buddy(PurpleConnection *gc, const char *name, const char *old_group, const char *new_group);
void oscar_rename_group(PurpleConnection *gc, const char *old_name, PurpleGroup *group, GList *moved_buddies);
void oscar_convo_closed(PurpleConnection *gc, const char *who);
const char *oscar_normalize(const PurpleAccount *account, const char *str);
-void oscar_set_icon(PurpleConnection *gc, PurpleStoredImage *img);
+void oscar_set_icon(PurpleConnection *gc, PurpleImage *img);
void oscar_remove_group(PurpleConnection *gc, PurpleGroup *group);
gboolean oscar_can_receive_file(PurpleConnection *gc, const char *who);
void oscar_send_file(PurpleConnection *gc, const char *who, const char *file);
PurpleXfer *oscar_new_xfer(PurpleConnection *gc, const char *who);
gboolean oscar_offline_message(const PurpleBuddy *buddy);
+gssize oscar_get_max_message_size(PurpleConversation *conv);
GList *oscar_actions(PurplePlugin *plugin, gpointer context);
void oscar_init(PurplePlugin *plugin, gboolean is_icq);
diff --git a/libpurple/protocols/oscar/peer.c b/libpurple/protocols/oscar/peer.c
index 15db99d0cc..5dc14d5403 100644
--- a/libpurple/protocols/oscar/peer.c
+++ b/libpurple/protocols/oscar/peer.c
@@ -35,7 +35,7 @@
/* From Purple */
#include "conversation.h"
-#include "ft.h"
+#include "xfer.h"
#include "network.h"
#include "notify.h"
#include "request.h"
@@ -50,10 +50,6 @@
#include <limits.h> /* for UINT_MAX */
#endif
-#ifdef _WIN32
-#include "win32dep.h"
-#endif
-
/*
* I really want to switch all our networking code to using IPv6 only,
* but that really isn't a good idea at all. Evan S. of Adium says
@@ -115,7 +111,7 @@ peer_connection_new(OscarData *od, guint64 type, const char *bn)
conn->od = od;
conn->type = type;
conn->bn = g_strdup(bn);
- conn->buffer_outgoing = purple_circ_buffer_new(0);
+ conn->buffer_outgoing = purple_circular_buffer_new(0);
conn->listenerfd = -1;
conn->fd = -1;
conn->lastactivity = time(NULL);
@@ -189,8 +185,8 @@ peer_connection_close(PeerConnection *conn)
conn->buffer_incoming.len = 0;
conn->buffer_incoming.offset = 0;
- purple_circ_buffer_destroy(conn->buffer_outgoing);
- conn->buffer_outgoing = purple_circ_buffer_new(0);
+ g_object_unref(G_OBJECT(conn->buffer_outgoing));
+ conn->buffer_outgoing = purple_circular_buffer_new(0);
conn->flags &= ~PEER_CONNECTION_FLAG_IS_INCOMING;
}
@@ -211,8 +207,8 @@ peer_connection_destroy_cb(gpointer data)
if (conn->xfer != NULL)
{
- PurpleXferStatusType status;
- conn->xfer->data = 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) &&
@@ -224,7 +220,7 @@ peer_connection_destroy_cb(gpointer data)
else
purple_xfer_cancel_local(conn->xfer);
}
- purple_xfer_unref(conn->xfer);
+ g_object_unref(conn->xfer);
conn->xfer = NULL;
}
@@ -234,7 +230,7 @@ peer_connection_destroy_cb(gpointer data)
g_free(conn->clientip);
g_free(conn->verifiedip);
g_free(conn->xferdata.name);
- purple_circ_buffer_destroy(conn->buffer_outgoing);
+ g_object_unref(G_OBJECT(conn->buffer_outgoing));
conn->od->peer_connections = g_slist_remove(conn->od->peer_connections, conn);
@@ -408,9 +404,10 @@ send_cb(gpointer data, gint source, PurpleInputCondition cond)
PeerConnection *conn;
gsize writelen;
gssize wrotelen;
+ const gchar *output = NULL;
conn = data;
- writelen = purple_circ_buffer_get_max_read(conn->buffer_outgoing);
+ writelen = purple_circular_buffer_get_max_read(conn->buffer_outgoing);
if (writelen == 0)
{
@@ -433,12 +430,13 @@ send_cb(gpointer data, gint source, PurpleInputCondition cond)
* file transfer. Somebody should teach those guys how to
* write good TCP code.
*/
- conn->buffer_outgoing->inptr = conn->buffer_outgoing->buffer;
- conn->buffer_outgoing->outptr = conn->buffer_outgoing->buffer;
+ purple_circular_buffer_reset(conn->buffer_outgoing);
return;
}
- wrotelen = send(conn->fd, conn->buffer_outgoing->outptr, writelen, 0);
+ output = purple_circular_buffer_get_output(conn->buffer_outgoing);
+
+ wrotelen = send(conn->fd, output, writelen, 0);
if (wrotelen <= 0)
{
if (wrotelen < 0 && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
@@ -465,7 +463,7 @@ send_cb(gpointer data, gint source, PurpleInputCondition cond)
return;
}
- purple_circ_buffer_mark_read(conn->buffer_outgoing, wrotelen);
+ purple_circular_buffer_mark_read(conn->buffer_outgoing, wrotelen);
conn->lastactivity = time(NULL);
}
@@ -478,7 +476,7 @@ void
peer_connection_send(PeerConnection *conn, ByteStream *bs)
{
/* Add everything to our outgoing buffer */
- purple_circ_buffer_append(conn->buffer_outgoing, bs->data, bs->len);
+ purple_circular_buffer_append(conn->buffer_outgoing, bs->data, bs->len);
/* If we haven't already started writing stuff, then start the cycle */
if ((conn->watcher_outgoing == 0) && (conn->fd >= 0))
@@ -515,7 +513,7 @@ peer_connection_finalize_connection(PeerConnection *conn)
}
else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
{
- if (purple_xfer_get_type(conn->xfer) == PURPLE_XFER_SEND)
+ if (purple_xfer_get_xfer_type(conn->xfer) == PURPLE_XFER_TYPE_SEND)
{
peer_oft_send_prompt(conn);
}
@@ -637,6 +635,40 @@ peer_connection_listen_cb(gpointer data, gint source, PurpleInputCondition cond)
}
/**
+ * Converts a dot-decimal IP address to an array of unsigned
+ * chars. For example, converts 192.168.0.1 to a 4 byte
+ * array containing 192, 168, 0 and 1.
+ *
+ * @param ip An IP address in dot-decimal notiation.
+ * @return An array of 4 bytes containing an IP addresses
+ * equivalent to the given parameter, or NULL if
+ * the given IP address is invalid. This value
+ * is statically allocated and should not be
+ * freed.
+ */
+static const unsigned char *
+peer_ip_atoi(const char *ip)
+{
+ static unsigned char ret[4];
+ gchar *delimiter = ".";
+ gchar **split;
+ int i;
+
+ g_return_val_if_fail(ip != NULL, NULL);
+
+ split = g_strsplit(ip, delimiter, 4);
+ for (i = 0; split[i] != NULL; i++)
+ ret[i] = atoi(split[i]);
+ g_strfreev(split);
+
+ /* i should always be 4 */
+ if (i != 4)
+ return NULL;
+
+ return ret;
+}
+
+/**
* We've just opened a listener socket, so we send the remote
* user an ICBM and ask them to connect to us.
*/
@@ -647,7 +679,7 @@ peer_connection_establish_listener_cb(int listenerfd, gpointer data)
OscarData *od;
PurpleConnection *gc;
PurpleAccount *account;
- PurpleConversation *conv;
+ PurpleIMConversation *im;
char *tmp;
FlapConnection *bos_conn;
const char *listener_ip;
@@ -687,13 +719,13 @@ peer_connection_establish_listener_cb(int listenerfd, gpointer data)
else
listener_ip = purple_network_get_my_ip(bos_conn->fd);
- ip_atoi = purple_network_ip_atoi(listener_ip);
+ ip_atoi = peer_ip_atoi(listener_ip);
if (ip_atoi == NULL) {
/* Could not convert IP to 4 byte array--weird, but this does
happen for some users (#4829, Adium #15839). Maybe they're
connecting with IPv6...? Maybe through a proxy? */
purple_debug_error("oscar", "Can't ask peer to connect to us "
- "because purple_network_ip_atoi(%s) returned NULL. "
+ "because peer_ip_atoi(%s) returned NULL. "
"fd=%d. is_ssl=%d\n",
listener_ip ? listener_ip : "(null)",
bos_conn->gsc ? bos_conn->gsc->fd : bos_conn->fd,
@@ -711,10 +743,11 @@ peer_connection_establish_listener_cb(int listenerfd, gpointer data)
listener_port, ++conn->lastrequestnumber);
/* Print a message to a local conversation window */
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
+ im = purple_im_conversation_new(account, conn->bn);
tmp = g_strdup_printf(_("Asking %s to connect to us at %s:%hu for "
"Direct IM."), conn->bn, listener_ip, listener_port);
- purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(im), tmp, 0);
g_free(tmp);
}
else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
@@ -796,12 +829,12 @@ peer_connection_trynext(PeerConnection *conn)
if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
{
gchar *tmp;
- PurpleConversation *conv;
+ PurpleIMConversation *im;
tmp = g_strdup_printf(_("Attempting to connect to %s:%hu."),
conn->verifiedip, conn->port);
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
- purple_conversation_write(conv, NULL, tmp,
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ im = purple_im_conversation_new(account, conn->bn);
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(im), tmp, 0);
g_free(tmp);
}
@@ -842,7 +875,7 @@ peer_connection_trynext(PeerConnection *conn)
*/
conn->flags |= PEER_CONNECTION_FLAG_IS_INCOMING;
- conn->listen_data = purple_network_listen_range(5190, 5290, SOCK_STREAM,
+ conn->listen_data = purple_network_listen_range(5190, 5290, AF_UNSPEC, SOCK_STREAM, TRUE,
peer_connection_establish_listener_cb, conn);
if (conn->listen_data != NULL)
{
@@ -870,11 +903,11 @@ peer_connection_trynext(PeerConnection *conn)
if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
{
gchar *tmp;
- PurpleConversation *conv;
+ PurpleIMConversation *im;
tmp = g_strdup(_("Attempting to connect via proxy server."));
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
- purple_conversation_write(conv, NULL, tmp,
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ im = purple_im_conversation_new(account, conn->bn);
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(im), tmp, 0);
g_free(tmp);
}
@@ -911,15 +944,14 @@ peer_connection_propose(OscarData *od, guint64 type, const char *bn)
if (conn->ready)
{
PurpleAccount *account;
- PurpleConversation *conv;
+ PurpleIMConversation *im;
purple_debug_info("oscar", "Already have a direct IM "
"session with %s.\n", bn);
account = purple_connection_get_account(od->gc);
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
- bn, account);
- if (conv != NULL)
- purple_conversation_present(conv);
+ im = purple_conversations_find_im_with_account(bn, account);
+ if (im != NULL)
+ purple_conversation_present(PURPLE_CONVERSATION(im));
return;
}
@@ -1060,7 +1092,7 @@ peer_connection_got_proposition(OscarData *od, const gchar *bn, const gchar *mes
"revealed, this may be considered a privacy "
"risk."),
PURPLE_DEFAULT_ACTION_NONE,
- account, bn, NULL,
+ purple_request_cpar_from_account(account),
conn, 2,
_("C_onnect"), G_CALLBACK(peer_connection_got_proposition_yes_cb),
_("Cancel"), G_CALLBACK(peer_connection_got_proposition_no_cb));
@@ -1069,11 +1101,10 @@ peer_connection_got_proposition(OscarData *od, const gchar *bn, const gchar *mes
{
gchar *filename;
- conn->xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE, bn);
+ conn->xfer = purple_xfer_new(account, PURPLE_XFER_TYPE_RECEIVE, bn);
if (conn->xfer)
{
- conn->xfer->data = conn;
- purple_xfer_ref(conn->xfer);
+ purple_xfer_set_protocol_data(conn->xfer, conn);
purple_xfer_set_size(conn->xfer, args->info.sendfile.totsize);
/* Set the file name */
diff --git a/libpurple/protocols/oscar/peer.h b/libpurple/protocols/oscar/peer.h
index a706c3ff31..e1cfcc3a15 100644
--- a/libpurple/protocols/oscar/peer.h
+++ b/libpurple/protocols/oscar/peer.h
@@ -25,7 +25,7 @@
#ifndef _PEER_H_
#define _PEER_H_
-#include "ft.h"
+#include "xfer.h"
#include "network.h"
#include "proxy.h"
@@ -184,7 +184,7 @@ struct _PeerConnection
guint8 proxy_header[12];
gssize proxy_header_received;
ByteStream buffer_incoming;
- PurpleCircBuffer *buffer_outgoing;
+ PurpleCircularBuffer *buffer_outgoing;
guint watcher_incoming;
guint watcher_outgoing;
@@ -251,7 +251,7 @@ void peer_connection_got_proposition(OscarData *od, const gchar *bn, const gchar
void peer_odc_close(PeerConnection *conn);
void peer_odc_recv_frame(PeerConnection *conn, ByteStream *bs);
void peer_odc_send_cookie(PeerConnection *conn);
-void peer_odc_send_typing(PeerConnection *conn, PurpleTypingState typing);
+void peer_odc_send_typing(PeerConnection *conn, PurpleIMTypingState typing);
void peer_odc_send_im(PeerConnection *conn, const char *msg, int len, int encoding, gboolean autoreply);
/*
diff --git a/libpurple/protocols/oscar/tlv.c b/libpurple/protocols/oscar/tlv.c
index bf2be748e5..04163b78f6 100644
--- a/libpurple/protocols/oscar/tlv.c
+++ b/libpurple/protocols/oscar/tlv.c
@@ -267,10 +267,10 @@ int aim_tlvlist_count(GSList *list)
* @return The number of bytes that would be needed to
* write the passed TLV chain to a data buffer.
*/
-int aim_tlvlist_size(GSList *list)
+size_t aim_tlvlist_size(GSList *list)
{
GSList *cur;
- int size;
+ size_t size;
if (list == NULL)
return 0;
@@ -658,7 +658,7 @@ void aim_tlvlist_remove(GSList **list, const guint16 type)
*/
int aim_tlvlist_write(ByteStream *bs, GSList **list)
{
- int goodbuflen;
+ size_t goodbuflen;
GSList *cur;
aim_tlv_t *tlv;
diff --git a/libpurple/protocols/oscar/userinfo.c b/libpurple/protocols/oscar/userinfo.c
index fad89dd763..195ef37a99 100644
--- a/libpurple/protocols/oscar/userinfo.c
+++ b/libpurple/protocols/oscar/userinfo.c
@@ -131,21 +131,16 @@ oscar_caps_to_string(guint64 caps)
}
static void
-oscar_user_info_add_pair(PurpleNotifyUserInfo *user_info, const char *name, const char *value)
-{
- if (value && value[0]) {
- purple_notify_user_info_add_pair(user_info, name, value);
- }
-}
-
-static void
oscar_user_info_convert_and_add(PurpleAccount *account, OscarData *od, PurpleNotifyUserInfo *user_info,
const char *name, const char *value)
{
gchar *utf8;
if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, od, value))) {
- purple_notify_user_info_add_pair(user_info, name, utf8);
+ /* TODO: Check whether it's correct to call add_pair_html,
+ or if we should be using add_pair_plaintext. Will
+ need to check callers of this function. */
+ purple_notify_user_info_add_pair_html(user_info, name, utf8);
g_free(utf8);
}
}
@@ -158,7 +153,10 @@ oscar_user_info_convert_and_add_hyperlink(PurpleAccount *account, OscarData *od,
if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, od, value))) {
gchar *tmp = g_strdup_printf("<a href=\"%s%s\">%s</a>", url_prefix, utf8, utf8);
- purple_notify_user_info_add_pair(user_info, name, tmp);
+ /* TODO: Check whether it's correct to call add_pair_html,
+ or if we should be using add_pair_plaintext. Will
+ need to check callers of this function. */
+ purple_notify_user_info_add_pair_html(user_info, name, tmp);
g_free(utf8);
g_free(tmp);
}
@@ -191,7 +189,7 @@ oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_i
return;
if (b == NULL)
- b = purple_find_buddy(purple_connection_get_account(gc), userinfo->bn);
+ b = purple_blist_find_buddy(purple_connection_get_account(gc), userinfo->bn);
else
userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
@@ -263,8 +261,8 @@ oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_i
message = tmp;
}
- } else if (aim_ssi_waitingforauth(od->ssi.local,
- aim_ssi_itemlist_findparentname(od->ssi.local, purple_buddy_get_name(b)),
+ } else if (aim_ssi_waitingforauth(&od->ssi.local,
+ aim_ssi_itemlist_findparentname(&od->ssi.local, purple_buddy_get_name(b)),
purple_buddy_get_name(b)))
{
/* Note if an offline buddy is not authorized */
@@ -295,12 +293,12 @@ oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_i
} else {
description = g_strdup(_(mood));
}
- purple_notify_user_info_add_pair(user_info, _("Mood"), description);
+ purple_notify_user_info_add_pair_html(user_info, _("Mood"), description);
g_free(description);
}
}
- purple_notify_user_info_add_pair(user_info, _("Status"), message);
+ purple_notify_user_info_add_pair_html(user_info, _("Status"), message);
g_free(message);
}
@@ -324,7 +322,7 @@ oscar_user_info_append_extra_info(PurpleConnection *gc, PurpleNotifyUserInfo *us
userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
if (b == NULL)
- b = purple_find_buddy(account, userinfo->bn);
+ b = purple_blist_find_buddy(account, userinfo->bn);
if (b != NULL) {
bname = purple_buddy_get_name(b);
@@ -336,23 +334,23 @@ oscar_user_info_append_extra_info(PurpleConnection *gc, PurpleNotifyUserInfo *us
bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->bn));
if ((bi != NULL) && (bi->ipaddr != 0)) {
- tmp = g_strdup_printf("%u.%u.%u.%u",
+ char tmp2[40];
+ sprintf(tmp2, "%u.%u.%u.%u",
0xFF & ((bi->ipaddr & 0xff000000) >> 24),
0xFF & ((bi->ipaddr & 0x00ff0000) >> 16),
0xFF & ((bi->ipaddr & 0x0000ff00) >> 8),
0xFF & (bi->ipaddr & 0x000000ff));
- oscar_user_info_add_pair(user_info, _("IP Address"), tmp);
- g_free(tmp);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("IP Address"), tmp2);
}
if ((userinfo != NULL) && (userinfo->warnlevel != 0)) {
- tmp = g_strdup_printf("%d", (int)(userinfo->warnlevel/10.0 + .5));
- oscar_user_info_add_pair(user_info, _("Warning Level"), tmp);
- g_free(tmp);
+ char tmp2[12];
+ sprintf(tmp2, "%d", (int)(userinfo->warnlevel/10.0 + .5));
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Warning Level"), tmp2);
}
if ((b != NULL) && (bname != NULL) && (g != NULL) && (gname != NULL)) {
- tmp = aim_ssi_getcomment(od->ssi.local, gname, bname);
+ tmp = aim_ssi_getcomment(&od->ssi.local, gname, bname);
if (tmp != NULL) {
char *tmp2 = g_markup_escape_text(tmp, strlen(tmp));
g_free(tmp);
@@ -368,11 +366,15 @@ oscar_user_info_display_error(OscarData *od, guint16 error_reason, gchar *buddy)
{
PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
gchar *buf = g_strdup_printf(_("User information not available: %s"), oscar_get_msgerr_reason(error_reason));
- purple_notify_user_info_add_pair(user_info, NULL, buf);
+ purple_notify_user_info_add_pair_plaintext(user_info, NULL, buf);
purple_notify_userinfo(od->gc, buddy, user_info, NULL, NULL);
purple_notify_user_info_destroy(user_info);
- if (!purple_conv_present_error(buddy, purple_connection_get_account(od->gc), buf))
- purple_notify_error(od->gc, NULL, buf, NULL);
+ if (!purple_conversation_present_error(buddy,
+ purple_connection_get_account(od->gc), buf))
+ {
+ purple_notify_error(od->gc, NULL, buf, NULL,
+ purple_request_cpar_from_connection(od->gc));
+ }
g_free(buf);
}
@@ -392,22 +394,22 @@ oscar_user_info_display_icq(OscarData *od, struct aim_icq_info *info)
user_info = purple_notify_user_info_new();
g_snprintf(who, sizeof(who), "%u", info->uin);
- buddy = purple_find_buddy(account, who);
+ buddy = purple_blist_find_buddy(account, who);
if (buddy != NULL)
bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, purple_buddy_get_name(buddy)));
else
bi = NULL;
- purple_notify_user_info_add_pair(user_info, _("UIN"), who);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("UIN"), who);
oscar_user_info_convert_and_add(account, od, user_info, _("Nick"), info->nick);
if ((bi != NULL) && (bi->ipaddr != 0)) {
- char *tstr = g_strdup_printf("%u.%u.%u.%u",
+ char tstr[40];
+ sprintf(tstr, "%u.%u.%u.%u",
0xFF & ((bi->ipaddr & 0xff000000) >> 24),
0xFF & ((bi->ipaddr & 0x00ff0000) >> 16),
0xFF & ((bi->ipaddr & 0x0000ff00) >> 8),
0xFF & (bi->ipaddr & 0x000000ff));
- purple_notify_user_info_add_pair(user_info, _("IP Address"), tstr);
- g_free(tstr);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("IP Address"), tstr);
}
oscar_user_info_convert_and_add(account, od, user_info, _("First Name"), info->first);
oscar_user_info_convert_and_add(account, od, user_info, _("Last Name"), info->last);
@@ -421,7 +423,7 @@ oscar_user_info_display_icq(OscarData *od, struct aim_icq_info *info)
oscar_user_info_convert_and_add(account, od, user_info, _("Mobile Phone"), info->mobile);
if (info->gender != 0)
- purple_notify_user_info_add_pair(user_info, _("Gender"), (info->gender == 1 ? _("Female") : _("Male")));
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Gender"), (info->gender == 1 ? _("Female") : _("Male")));
if ((info->birthyear > 1900) && (info->birthmonth > 0) && (info->birthday > 0)) {
/* Initialize the struct properly or strftime() will crash
@@ -433,7 +435,7 @@ oscar_user_info_display_icq(OscarData *od, struct aim_icq_info *info)
tm->tm_mon = (int)info->birthmonth - 1;
tm->tm_year = (int)info->birthyear - 1900;
- /* Ignore dst setting of today to avoid timezone shift between
+ /* Ignore dst setting of today to avoid timezone shift between
* dates in summer and winter time. */
tm->tm_isdst = -1;
@@ -447,8 +449,9 @@ oscar_user_info_display_icq(OscarData *od, struct aim_icq_info *info)
if ((info->age > 0) && (info->age < 255)) {
char age[5];
snprintf(age, sizeof(age), "%hhd", info->age);
- purple_notify_user_info_add_pair(user_info, _("Age"), age);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Age"), age);
}
+ /* TODO: Is it correct to pass info->email here...? */
oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Personal Web Page"), info->email, "");
if (buddy != NULL)
oscar_user_info_append_status(gc, user_info, buddy, /* aim_userinfo_t */ NULL, /* use_html_status */ TRUE);
@@ -478,6 +481,7 @@ oscar_user_info_display_icq(OscarData *od, struct aim_icq_info *info)
oscar_user_info_convert_and_add(account, od, user_info, _("Company"), info->workcompany);
oscar_user_info_convert_and_add(account, od, user_info, _("Division"), info->workdivision);
oscar_user_info_convert_and_add(account, od, user_info, _("Position"), info->workposition);
+ /* TODO: Is it correct to pass info->email here...? */
oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Web Page"), info->email, "");
}
@@ -497,7 +501,7 @@ oscar_user_info_display_aim(OscarData *od, aim_userinfo_t *userinfo)
if ((userinfo->present & AIM_USERINFO_PRESENT_IDLE) && userinfo->idletime != 0) {
tmp = purple_str_seconds_to_string(userinfo->idletime*60);
- oscar_user_info_add_pair(user_info, _("Idle"), tmp);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Idle"), tmp);
g_free(tmp);
}
@@ -506,17 +510,18 @@ oscar_user_info_display_aim(OscarData *od, aim_userinfo_t *userinfo)
if ((userinfo->present & AIM_USERINFO_PRESENT_ONLINESINCE) && !oscar_util_valid_name_sms(userinfo->bn)) {
/* An SMS contact is always online; its Online Since value is not useful */
time_t t = userinfo->onlinesince;
- oscar_user_info_add_pair(user_info, _("Online Since"), purple_date_format_full(localtime(&t)));
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Online Since"), purple_date_format_full(localtime(&t)));
}
if (userinfo->present & AIM_USERINFO_PRESENT_MEMBERSINCE) {
time_t t = userinfo->membersince;
- oscar_user_info_add_pair(user_info, _("Member Since"), purple_date_format_full(localtime(&t)));
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Member Since"), purple_date_format_full(localtime(&t)));
}
if (userinfo->capabilities != 0) {
tmp = oscar_caps_to_string(userinfo->capabilities);
- oscar_user_info_add_pair(user_info, _("Capabilities"), tmp);
+ if (tmp && *tmp)
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Capabilities"), tmp);
g_free(tmp);
}
@@ -525,7 +530,11 @@ oscar_user_info_display_aim(OscarData *od, aim_userinfo_t *userinfo)
info_utf8 = oscar_encoding_to_utf8(userinfo->info_encoding, userinfo->info, userinfo->info_len);
tmp = oscar_util_format_string(info_utf8, purple_account_get_username(account));
purple_notify_user_info_add_section_break(user_info);
- oscar_user_info_add_pair(user_info, _("Profile"), tmp);
+ if (tmp && *tmp) {
+ /* 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, _("Profile"), tmp);
+ }
g_free(tmp);
g_free(info_utf8);
}
@@ -534,7 +543,7 @@ oscar_user_info_display_aim(OscarData *od, aim_userinfo_t *userinfo)
base_profile_url = oscar_util_valid_name_icq(userinfo->bn) ? "http://www.icq.com/people" : "http://profiles.aim.com";
tmp = g_strdup_printf("<a href=\"%s/%s\">%s</a>",
base_profile_url, purple_normalize(account, userinfo->bn), _("View web profile"));
- purple_notify_user_info_add_pair(user_info, NULL, tmp);
+ purple_notify_user_info_add_pair_html(user_info, NULL, tmp);
g_free(tmp);
purple_notify_userinfo(gc, userinfo->bn, user_info, NULL, NULL);
diff --git a/libpurple/protocols/oscar/util.c b/libpurple/protocols/oscar/util.c
index 1fa176e382..3d3923aeb0 100644
--- a/libpurple/protocols/oscar/util.c
+++ b/libpurple/protocols/oscar/util.c
@@ -31,10 +31,6 @@
#include <ctype.h>
-#ifdef _WIN32
-#include "win32dep.h"
-#endif
-
static const char * const msgerrreason[] = {
N_("Invalid error"),
N_("Invalid SNAC"),
@@ -62,7 +58,7 @@ static const char * const msgerrreason[] = {
N_("Queue full"),
N_("Not while on AOL")
};
-static const int msgerrreasonlen = G_N_ELEMENTS(msgerrreason);
+static const gsize msgerrreasonlen = G_N_ELEMENTS(msgerrreason);
const char *oscar_get_msgerr_reason(size_t reason)
{
diff --git a/libpurple/protocols/oscar/visibility.c b/libpurple/protocols/oscar/visibility.c
index d6aa589d8c..123f14a595 100644
--- a/libpurple/protocols/oscar/visibility.c
+++ b/libpurple/protocols/oscar/visibility.c
@@ -51,7 +51,7 @@ get_buddy_list_type(OscarData *od)
static gboolean
is_buddy_on_list(OscarData *od, const char *bname)
{
- return aim_ssi_itemlist_finditem(od->ssi.local, NULL, bname, get_buddy_list_type(od)) != NULL;
+ return aim_ssi_itemlist_finditem(&od->ssi.local, NULL, bname, get_buddy_list_type(od)) != NULL;
}
static void
@@ -94,7 +94,7 @@ show_private_list(PurplePluginAction *action, guint16 list_type, const gchar *ti
GSList *buddies, *filtered_buddies, *cur;
gchar *text, *secondary;
- buddies = purple_find_buddies(account, NULL);
+ buddies = purple_blist_find_buddies(account, NULL);
filtered_buddies = NULL;
for (cur = buddies; cur != NULL; cur = cur->next) {
PurpleBuddy *buddy;
@@ -102,7 +102,7 @@ show_private_list(PurplePluginAction *action, guint16 list_type, const gchar *ti
buddy = cur->data;
bname = purple_buddy_get_name(buddy);
- if (aim_ssi_itemlist_finditem(od->ssi.local, NULL, bname, list_type)) {
+ if (aim_ssi_itemlist_finditem(&od->ssi.local, NULL, bname, list_type)) {
filtered_buddies = g_slist_prepend(filtered_buddies, buddy);
}
}
diff --git a/libpurple/protocols/sametime/Makefile.am b/libpurple/protocols/sametime/Makefile.am
index 8ebc49c7e6..300b83262b 100644
--- a/libpurple/protocols/sametime/Makefile.am
+++ b/libpurple/protocols/sametime/Makefile.am
@@ -1,7 +1,7 @@
EXTRA_DIST = \
Makefile.mingw
-pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+pkgdir = @PURPLE_PLUGINDIR@
noinst_HEADERS = sametime.h
@@ -24,8 +24,8 @@ pkg_LTLIBRARIES = libsametime.la
endif
libsametime_la_SOURCES = $(SAMETIMESOURCES)
-libsametime_la_LDFLAGS = -module -avoid-version
-libsametime_la_LIBADD = $(GLIB_LIBS) $(MEANWHILE_LIBS)
+libsametime_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+libsametime_la_LIBADD = @PURPLE_LIBS@ $(MEANWHILE_LIBS)
AM_CPPFLAGS = \
-I$(top_srcdir)/libpurple \
diff --git a/libpurple/protocols/sametime/Makefile.mingw b/libpurple/protocols/sametime/Makefile.mingw
index ded2f24b41..18719b920f 100644
--- a/libpurple/protocols/sametime/Makefile.mingw
+++ b/libpurple/protocols/sametime/Makefile.mingw
@@ -8,7 +8,6 @@ PIDGIN_TREE_TOP := ../../..
include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
TARGET = libsametime
-NEEDED_DLLS = $(MEANWHILE_TOP)/bin/libmeanwhile-1.dll
TYPE = PLUGIN
# Static or Plugin...
@@ -51,6 +50,7 @@ OBJECTS = $(C_SRC:%.c=%.o)
##
LIBS = \
-lglib-2.0 \
+ -lgobject-2.0 \
-lintl \
-lws2_32 \
-lmeanwhile \
@@ -67,7 +67,6 @@ all: $(TARGET).dll
install: all $(DLL_INSTALL_DIR) $(PURPLE_INSTALL_DIR)
cp $(TARGET).dll $(DLL_INSTALL_DIR)
- cp $(NEEDED_DLLS) $(PURPLE_INSTALL_DIR)
$(OBJECTS): $(PURPLE_CONFIG_H)
diff --git a/libpurple/protocols/sametime/sametime.c b/libpurple/protocols/sametime/sametime.c
index aa252d89ff..e2cb54cda7 100644
--- a/libpurple/protocols/sametime/sametime.c
+++ b/libpurple/protocols/sametime/sametime.c
@@ -33,15 +33,14 @@
/* purple includes */
#include "account.h"
#include "accountopt.h"
-#include "circbuffer.h"
+#include "circularbuffer.h"
#include "conversation.h"
#include "debug.h"
-#include "ft.h"
-#include "imgstore.h"
+#include "image-store.h"
+#include "xfer.h"
#include "mime.h"
#include "notify.h"
#include "plugin.h"
-#include "privacy.h"
#include "prpl.h"
#include "request.h"
#include "util.h"
@@ -197,7 +196,7 @@ static guint log_handler[2] = { 0, 0 };
/** the purple plugin data.
- available as gc->proto_data and mwSession_getClientData */
+ available as purple_connection_get_protocol_data(gc) and mwSession_getClientData */
struct mwPurplePluginData {
struct mwSession *session;
@@ -217,10 +216,11 @@ struct mwPurplePluginData {
/** socket fd */
int socket;
+ guint inpa; /* input watcher */
gint outpa; /* like inpa, but the other way */
/** circular buffer for outgoing data */
- PurpleCircBuffer *sock_buf;
+ PurpleCircularBuffer *sock_buf;
PurpleConnection *gc;
};
@@ -295,7 +295,7 @@ static void convo_data_free(struct convo_data *conv);
static void convo_features(struct mwConversation *conv);
-static PurpleConversation *convo_get_gconv(struct mwConversation *conv);
+static PurpleIMConversation *convo_get_im(struct mwConversation *conv);
/* name and id */
@@ -320,7 +320,7 @@ static struct mwSession *gc_to_session(PurpleConnection *gc) {
g_return_val_if_fail(gc != NULL, NULL);
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
g_return_val_if_fail(pd != NULL, NULL);
return pd->session;
@@ -342,7 +342,7 @@ static PurpleConnection *session_to_gc(struct mwSession *session) {
static void write_cb(gpointer data, gint source, PurpleInputCondition cond) {
struct mwPurplePluginData *pd = data;
- PurpleCircBuffer *circ = pd->sock_buf;
+ PurpleCircularBuffer *circ = pd->sock_buf;
gsize avail;
int ret;
@@ -350,17 +350,17 @@ static void write_cb(gpointer data, gint source, PurpleInputCondition cond) {
g_return_if_fail(circ != NULL);
- avail = purple_circ_buffer_get_max_read(circ);
+ avail = purple_circular_buffer_get_max_read(circ);
if(BUF_LONG < avail) avail = BUF_LONG;
while(avail) {
- ret = write(pd->socket, circ->outptr, avail);
+ ret = write(pd->socket, purple_circular_buffer_get_output(circ), avail);
if(ret <= 0)
break;
- purple_circ_buffer_mark_read(circ, ret);
- avail = purple_circ_buffer_get_max_read(circ);
+ purple_circular_buffer_mark_read(circ, ret);
+ avail = purple_circular_buffer_get_max_read(circ);
if(BUF_LONG < avail) avail = BUF_LONG;
}
@@ -385,7 +385,7 @@ static int mw_session_io_write(struct mwSession *session,
if(pd->outpa) {
DEBUG_INFO("already pending INPUT_WRITE, buffering\n");
- purple_circ_buffer_append(pd->sock_buf, buf, len);
+ purple_circular_buffer_append(pd->sock_buf, buf, len);
return 0;
}
@@ -405,7 +405,7 @@ static int mw_session_io_write(struct mwSession *session,
if(err == EAGAIN) {
/* append remainder to circular buffer */
DEBUG_INFO("EAGAIN\n");
- purple_circ_buffer_append(pd->sock_buf, buf, len);
+ purple_circular_buffer_append(pd->sock_buf, buf, len);
pd->outpa = purple_input_add(pd->socket, PURPLE_INPUT_WRITE, write_cb, pd);
} else if(len > 0) {
@@ -413,7 +413,7 @@ static int mw_session_io_write(struct mwSession *session,
g_strerror(errno));
DEBUG_ERROR("write returned %" G_GSSIZE_FORMAT ", %" G_GSIZE_FORMAT
" bytes left unwritten\n", ret, len);
- purple_connection_error_reason(pd->gc,
+ purple_connection_error(pd->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
tmp);
g_free(tmp);
@@ -432,13 +432,10 @@ static int mw_session_io_write(struct mwSession *session,
static void mw_session_io_close(struct mwSession *session) {
struct mwPurplePluginData *pd;
- PurpleConnection *gc;
pd = mwSession_getClientData(session);
g_return_if_fail(pd != NULL);
- gc = pd->gc;
-
if(pd->outpa) {
purple_input_remove(pd->outpa);
pd->outpa = 0;
@@ -449,9 +446,9 @@ static void mw_session_io_close(struct mwSession *session) {
pd->socket = 0;
}
- if(gc->inpa) {
- purple_input_remove(gc->inpa);
- gc->inpa = 0;
+ if(pd->inpa) {
+ purple_input_remove(pd->inpa);
+ pd->inpa = 0;
}
}
@@ -479,7 +476,7 @@ static void blist_resolve_alias_cb(struct mwServiceResolve *srvc,
match = result->matches->data;
g_return_if_fail(match != NULL);
- purple_blist_server_alias_buddy(data, match->name);
+ purple_buddy_set_server_alias(data, match->name);
purple_blist_node_set_string(data, BUDDY_KEY_NAME, match->name);
}
@@ -499,7 +496,7 @@ static void mw_aware_list_on_aware(struct mwAwareList *list,
gc = mwAwareList_getClientData(list);
acct = purple_connection_get_account(gc);
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
idle = aware->status.time;
stat = aware->status.status;
id = aware->id.user;
@@ -571,7 +568,7 @@ static void mw_aware_list_on_aware(struct mwAwareList *list,
PurpleBlistNode *bnode;
group = g_hash_table_lookup(pd->group_list_map, list);
- buddy = purple_find_buddy_in_group(acct, id, group);
+ buddy = purple_blist_find_buddy_in_group(acct, id, group);
bnode = (PurpleBlistNode *) buddy;
if(! buddy) {
@@ -679,7 +676,7 @@ static void blist_export(PurpleConnection *gc, struct mwSametimeList *stlist) {
enum mwSametimeGroupType gtype;
gboolean gopen;
- if(! PURPLE_BLIST_NODE_IS_GROUP(gn)) continue;
+ if(! PURPLE_IS_GROUP(gn)) continue;
grp = (PurpleGroup *) gn;
/* the group's type (normal or dynamic) */
@@ -715,13 +712,13 @@ static void blist_export(PurpleConnection *gc, struct mwSametimeList *stlist) {
for(cn = purple_blist_node_get_first_child(gn);
cn;
cn = purple_blist_node_get_sibling_next(cn)) {
- if(! PURPLE_BLIST_NODE_IS_CONTACT(cn)) continue;
+ if(! PURPLE_IS_CONTACT(cn)) continue;
for(bn = purple_blist_node_get_first_child(cn);
bn;
bn = purple_blist_node_get_sibling_next(bn)) {
- if(! PURPLE_BLIST_NODE_IS_BUDDY(bn)) continue;
- if(! PURPLE_BLIST_NODE_SHOULD_SAVE(bn)) continue;
+ if(! PURPLE_IS_BUDDY(bn)) continue;
+ if(purple_blist_node_is_transient(bn)) continue;
bdy = (PurpleBuddy *) bn;
@@ -736,7 +733,7 @@ static void blist_export(PurpleConnection *gc, struct mwSametimeList *stlist) {
stu = mwSametimeUser_new(stg, utype, &idb);
mwSametimeUser_setShortName(stu, purple_buddy_get_server_alias(bdy));
- mwSametimeUser_setAlias(stu, purple_buddy_get_local_buddy_alias(bdy));
+ mwSametimeUser_setAlias(stu, purple_buddy_get_local_alias(bdy));
}
}
}
@@ -851,7 +848,7 @@ static void buddy_add(struct mwPurplePluginData *pd,
static PurpleBuddy *buddy_ensure(PurpleConnection *gc, PurpleGroup *group,
struct mwSametimeUser *stuser) {
- struct mwPurplePluginData *pd = gc->proto_data;
+ struct mwPurplePluginData *pd = purple_connection_get_protocol_data(gc);
PurpleBuddy *buddy;
PurpleAccount *acct = purple_connection_get_account(gc);
@@ -861,9 +858,9 @@ static PurpleBuddy *buddy_ensure(PurpleConnection *gc, PurpleGroup *group,
enum mwSametimeUserType type = mwSametimeUser_getType(stuser);
g_return_val_if_fail(id != NULL, NULL);
- g_return_val_if_fail(strlen(id) > 0, NULL);
+ g_return_val_if_fail(*id, NULL);
- buddy = purple_find_buddy_in_group(acct, id, group);
+ buddy = purple_blist_find_buddy_in_group(acct, id, group);
if(! buddy) {
buddy = purple_buddy_new(acct, id, alias);
@@ -871,8 +868,8 @@ static PurpleBuddy *buddy_ensure(PurpleConnection *gc, PurpleGroup *group,
buddy_add(pd, buddy);
}
- purple_blist_alias_buddy(buddy, alias);
- purple_blist_server_alias_buddy(buddy, name);
+ purple_buddy_set_local_alias(buddy, alias);
+ purple_buddy_set_server_alias(buddy, name);
purple_blist_node_set_string((PurpleBlistNode *) buddy, BUDDY_KEY_NAME, name);
purple_blist_node_set_int((PurpleBlistNode *) buddy, BUDDY_KEY_TYPE, type);
@@ -915,7 +912,7 @@ static PurpleGroup *group_ensure(PurpleConnection *gc,
acct = purple_connection_get_account(gc);
owner = purple_account_get_username(acct);
- blist = purple_get_blist();
+ blist = purple_blist_get_buddy_list();
g_return_val_if_fail(blist != NULL, NULL);
name = mwSametimeGroup_getName(stgroup);
@@ -939,7 +936,7 @@ static PurpleGroup *group_ensure(PurpleConnection *gc,
for(gn = purple_blist_get_root(); gn;
gn = purple_blist_node_get_sibling_next(gn)) {
const char *n, *o;
- if(! PURPLE_BLIST_NODE_IS_GROUP(gn)) continue;
+ 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);
@@ -957,7 +954,7 @@ static PurpleGroup *group_ensure(PurpleConnection *gc,
/* try again, by alias */
if(! group) {
DEBUG_INFO("searching for group by alias %s\n", NSTR(alias));
- group = purple_find_group(alias);
+ group = purple_blist_find_group(alias);
}
/* oh well, no such group. Let's create it! */
@@ -973,7 +970,7 @@ static PurpleGroup *group_ensure(PurpleConnection *gc,
if(type == mwSametimeGroup_DYNAMIC) {
purple_blist_node_set_string(gn, GROUP_KEY_OWNER, owner);
- group_add(gc->proto_data, group);
+ group_add(purple_connection_get_protocol_data(gc), group);
}
return group;
@@ -1026,14 +1023,14 @@ static void group_clear(PurpleGroup *group, PurpleAccount *acct, gboolean del) {
for(cn = purple_blist_node_get_first_child(gn);
cn;
cn = purple_blist_node_get_sibling_next(cn)) {
- if(! PURPLE_BLIST_NODE_IS_CONTACT(cn)) continue;
+ if(! PURPLE_IS_CONTACT(cn)) continue;
for(bn = purple_blist_node_get_first_child(cn);
bn;
bn = purple_blist_node_get_sibling_next(bn)) {
PurpleBuddy *gb = (PurpleBuddy *) bn;
- if(! PURPLE_BLIST_NODE_IS_BUDDY(bn)) continue;
+ 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)));
@@ -1053,7 +1050,7 @@ static void group_clear(PurpleGroup *group, PurpleAccount *acct, gboolean del) {
DEBUG_INFO("cleared buddies\n");
/* optionally remove group from blist */
- if(del && !purple_blist_get_group_size(group, TRUE)) {
+ if(del && !purple_counting_node_get_total_size(PURPLE_COUNTING_NODE(group))) {
DEBUG_INFO("removing empty group\n");
purple_blist_remove_group(group);
}
@@ -1095,14 +1092,14 @@ static void group_prune(PurpleConnection *gc, PurpleGroup *group,
for(cn = purple_blist_node_get_first_child(gn);
cn;
cn = purple_blist_node_get_sibling_next(cn)) {
- if(! PURPLE_BLIST_NODE_IS_CONTACT(cn)) continue;
+ if(! PURPLE_IS_CONTACT(cn)) continue;
for(bn = purple_blist_node_get_first_child(cn);
bn;
bn = purple_blist_node_get_sibling_next(bn)) {
PurpleBuddy *gb = (PurpleBuddy *) bn;
- if(! PURPLE_BLIST_NODE_IS_BUDDY(bn)) continue;
+ if(! PURPLE_IS_BUDDY(bn)) continue;
/* if the account is correct and they're not in our table, mark
them for pruning */
@@ -1148,7 +1145,7 @@ static void blist_sync(PurpleConnection *gc, struct mwSametimeList *stlist) {
acct_n = purple_account_get_username(acct);
- blist = purple_get_blist();
+ blist = purple_blist_get_buddy_list();
g_return_if_fail(blist != NULL);
/* build a hash table for quick lookup while pruning the local
@@ -1169,7 +1166,7 @@ static void blist_sync(PurpleConnection *gc, struct mwSametimeList *stlist) {
const char *gname, *owner;
struct mwSametimeGroup *stgrp;
- if(! PURPLE_BLIST_NODE_IS_GROUP(gn)) continue;
+ if(! PURPLE_IS_GROUP(gn)) continue;
/* group not belonging to this account */
if(! purple_group_on_account(grp, acct))
@@ -1278,11 +1275,11 @@ static void conversation_created_cb(PurpleConversation *g_conv,
struct mwIdBlock who = { 0, 0 };
struct mwConversation *conv;
- gc = purple_conversation_get_gc(g_conv);
+ gc = purple_conversation_get_connection(g_conv);
if(pd->gc != gc)
return; /* not ours */
- if(purple_conversation_get_type(g_conv) != PURPLE_CONV_TYPE_IM)
+ if(!PURPLE_IS_IM_CONVERSATION(g_conv))
return; /* wrong type */
who.user = (char *) purple_conversation_get_name(g_conv);
@@ -1310,7 +1307,7 @@ static void blist_menu_nab(PurpleBlistNode *node, gpointer data) {
gc = pd->gc;
g_return_if_fail(gc != NULL);
- g_return_if_fail(PURPLE_BLIST_NODE_IS_GROUP(node));
+ g_return_if_fail(PURPLE_IS_GROUP(node));
str = g_string_new(NULL);
@@ -1340,7 +1337,7 @@ static void blist_node_menu_cb(PurpleBlistNode *node,
PurpleMenuAction *act;
/* we only want groups */
- if(! PURPLE_BLIST_NODE_IS_GROUP(node)) return;
+ if(! PURPLE_IS_GROUP(node)) return;
acct = purple_connection_get_account(pd->gc);
g_return_if_fail(acct != NULL);
@@ -1351,6 +1348,7 @@ static void blist_node_menu_cb(PurpleBlistNode *node,
#if 0
/* if there's anyone in the group for this acct, offer to invite
them all to a conference */
+ group = (PurpleGroup *) node;
if(purple_group_on_account(group, acct)) {
act = purple_menu_action_new(_("Invite Group to Conference..."),
PURPLE_CALLBACK(blist_menu_group_invite),
@@ -1377,18 +1375,18 @@ static void blist_init(PurpleAccount *acct) {
for(gnode = purple_blist_get_root(); gnode;
gnode = purple_blist_node_get_sibling_next(gnode)) {
- if(! PURPLE_BLIST_NODE_IS_GROUP(gnode)) continue;
+ if(! PURPLE_IS_GROUP(gnode)) continue;
for(cnode = purple_blist_node_get_first_child(gnode);
cnode;
cnode = purple_blist_node_get_sibling_next(cnode)) {
- if(! PURPLE_BLIST_NODE_IS_CONTACT(cnode))
+ if(! PURPLE_IS_CONTACT(cnode))
continue;
for(bnode = purple_blist_node_get_first_child(cnode);
bnode;
bnode = purple_blist_node_get_sibling_next(bnode)) {
PurpleBuddy *b;
- if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
+ if(!PURPLE_IS_BUDDY(bnode))
continue;
b = (PurpleBuddy *)bnode;
@@ -1400,7 +1398,7 @@ static void blist_init(PurpleAccount *acct) {
}
if(add_buds) {
- purple_account_add_buddies(acct, add_buds);
+ purple_account_add_buddies(acct, add_buds, NULL);
g_list_free(add_buds);
}
}
@@ -1428,7 +1426,7 @@ static void services_starting(struct mwPurplePluginData *pd) {
enum mwSametimeGroupType gt;
const char *owner;
- if(! PURPLE_BLIST_NODE_IS_GROUP(l)) continue;
+ if(! PURPLE_IS_GROUP(l)) continue;
/* if the group is ownerless, or has an owner and we're not it,
skip it */
@@ -1576,7 +1574,7 @@ static void mw_session_stateChange(struct mwSession *session,
msg = _("Connected");
purple_connection_update_progress(gc, msg, 10, MW_CONNECT_STEPS);
- purple_connection_set_state(gc, PURPLE_CONNECTED);
+ purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
break;
case mwSession_STOPPING:
@@ -1616,7 +1614,7 @@ static void mw_session_stateChange(struct mwSession *session,
default:
reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
}
- purple_connection_error_reason(gc, reason, err);
+ purple_connection_error(gc, reason, err);
g_free(err);
}
break;
@@ -1636,7 +1634,7 @@ static void mw_session_setPrivacyInfo(struct mwSession *session) {
PurpleConnection *gc;
PurpleAccount *acct;
struct mwPrivacyInfo *privacy;
- GSList *l, **ll;
+ GSList *list;
guint count;
DEBUG_INFO("privacy information set from server\n");
@@ -1655,16 +1653,25 @@ static void mw_session_setPrivacyInfo(struct mwSession *session) {
privacy = mwSession_getPrivacyInfo(session);
count = privacy->count;
- ll = (privacy->deny)? &acct->deny: &acct->permit;
- for(l = *ll; l; l = l->next) g_free(l->data);
- g_slist_free(*ll);
- l = *ll = NULL;
-
- while(count--) {
- struct mwUserItem *u = privacy->users + count;
- l = g_slist_prepend(l, g_strdup(u->id));
+ if (privacy->deny) {
+ while ((list = purple_account_privacy_get_denied(acct))) {
+ g_free(list->data);
+ purple_account_privacy_deny_remove(acct, list->data, TRUE);
+ }
+ while (count--) {
+ struct mwUserItem *u = privacy->users + count;
+ purple_account_privacy_deny_add(acct, u->id, TRUE);
+ }
+ } else {
+ while ((list = purple_account_privacy_get_permitted(acct))) {
+ g_free(list->data);
+ purple_account_privacy_permit_remove(acct, list->data, TRUE);
+ }
+ while (count--) {
+ struct mwUserItem *u = privacy->users + count;
+ purple_account_privacy_permit_add(acct, u->id, TRUE);
+ }
}
- *ll = l;
}
@@ -1712,7 +1719,8 @@ static void mw_session_admin(struct mwSession *session,
purple_notify_message(gc, PURPLE_NOTIFY_MSG_INFO,
_("Sametime Administrator Announcement"),
- prim, text, NULL, NULL);
+ prim, text, NULL, NULL,
+ purple_request_cpar_from_connection(gc));
g_free(prim);
}
@@ -1758,14 +1766,14 @@ static void read_cb(gpointer data, gint source, PurpleInputCondition cond) {
pd->socket = 0;
}
- if(pd->gc->inpa) {
- purple_input_remove(pd->gc->inpa);
- pd->gc->inpa = 0;
+ if(pd->inpa) {
+ purple_input_remove(pd->inpa);
+ pd->inpa = 0;
}
if(! ret) {
DEBUG_INFO("connection reset\n");
- purple_connection_error_reason(pd->gc,
+ purple_connection_error(pd->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Server closed the connection"));
@@ -1776,7 +1784,7 @@ static void read_cb(gpointer data, gint source, PurpleInputCondition cond) {
DEBUG_INFO("error in read callback: %s\n", err_str);
msg = g_strdup_printf(_("Lost connection with server: %s"), err_str);
- purple_connection_error_reason(pd->gc,
+ purple_connection_error(pd->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
msg);
g_free(msg);
@@ -1789,7 +1797,6 @@ static void read_cb(gpointer data, gint source, PurpleInputCondition cond) {
static void connect_cb(gpointer data, gint source, const gchar *error_message) {
struct mwPurplePluginData *pd = data;
- PurpleConnection *gc = pd->gc;
if(source < 0) {
/* connection failed */
@@ -1802,7 +1809,7 @@ static void connect_cb(gpointer data, gint source, const gchar *error_message) {
/* this is a regular connect, error out */
gchar *tmp = g_strdup_printf(_("Unable to connect: %s"),
error_message);
- purple_connection_error_reason(pd->gc,
+ purple_connection_error(pd->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
tmp);
g_free(tmp);
@@ -1817,7 +1824,7 @@ static void connect_cb(gpointer data, gint source, const gchar *error_message) {
}
pd->socket = source;
- gc->inpa = purple_input_add(source, PURPLE_INPUT_READ,
+ pd->inpa = purple_input_add(source, PURPLE_INPUT_READ,
read_cb, pd);
mwSession_start(pd->session);
@@ -1830,25 +1837,30 @@ static void mw_session_announce(struct mwSession *s,
const char *text) {
struct mwPurplePluginData *pd;
PurpleAccount *acct;
- PurpleConversation *conv;
+ PurpleIMConversation *im;
PurpleBuddy *buddy;
char *who = from->user_id;
- char *msg;
+ char *msg, *msg2;
pd = mwSession_getClientData(s);
acct = purple_connection_get_account(pd->gc);
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, acct);
- if(! conv) conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, who);
+ im = purple_conversations_find_im_with_account(who, acct);
+ if(! im) im = purple_im_conversation_new(acct, who);
- buddy = purple_find_buddy(acct, who);
+ buddy = purple_blist_find_buddy(acct, who);
if(buddy) who = (char *) purple_buddy_get_contact_alias(buddy);
who = g_strdup_printf(_("Announcement from %s"), who);
msg = purple_markup_linkify(text);
+ if (msg && msg[0])
+ msg2 = g_strdup_printf("%s: %s", who, msg);
+ else
+ msg2 = g_strdup(who);
- purple_conversation_write(conv, who, msg ? msg : "", PURPLE_MESSAGE_RECV, time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(im), msg2, 0);
g_free(who);
g_free(msg);
+ g_free(msg2);
}
@@ -1928,18 +1940,18 @@ static void mw_conf_invited(struct mwConference *conf,
if(! c_topic) c_topic = "(no title)";
if(! c_invitation) c_invitation = "(no message)";
- serv_got_chat_invite(gc, c_topic, c_inviter, c_invitation, ht);
+ purple_serv_got_chat_invite(gc, c_topic, c_inviter, c_invitation, ht);
}
-/* The following mess helps us relate a mwConference to a PurpleConvChat
+/* The following mess helps us relate a mwConference to a PurpleChatConversation
in the various forms by which either may be indicated */
#define CONF_TO_ID(conf) (GPOINTER_TO_INT(conf))
#define ID_TO_CONF(pd, id) (conf_find_by_id((pd), (id)))
-#define CHAT_TO_ID(chat) (purple_conv_chat_get_id(chat))
-#define ID_TO_CHAT(id) (purple_find_chat(id))
+#define CHAT_TO_ID(chat) (purple_chat_conversation_get_id(chat))
+#define ID_TO_CHAT(id) (purple_conversations_find_chat(id))
#define CHAT_TO_CONF(pd, chat) (ID_TO_CONF((pd), CHAT_TO_ID(chat)))
#define CONF_TO_CHAT(conf) (ID_TO_CHAT(CONF_TO_ID(conf)))
@@ -1955,7 +1967,7 @@ conf_find_by_id(struct mwPurplePluginData *pd, int id) {
ll = mwServiceConference_getConferences(srvc);
for(l = ll; l; l = l->next) {
struct mwConference *c = l->data;
- PurpleConvChat *h = mwConference_getClientData(c);
+ PurpleChatConversation *h = mwConference_getClientData(c);
if(CHAT_TO_ID(h) == id) {
conf = c;
@@ -1973,7 +1985,7 @@ static void mw_conf_opened(struct mwConference *conf, GList *members) {
struct mwSession *session;
struct mwPurplePluginData *pd;
PurpleConnection *gc;
- PurpleConversation *g_conf;
+ PurpleChatConversation *g_conf;
const char *n = mwConference_getName(conf);
const char *t = mwConference_getTitle(conf);
@@ -1987,14 +1999,14 @@ static void mw_conf_opened(struct mwConference *conf, GList *members) {
gc = pd->gc;
if(! t) t = "(no title)";
- g_conf = serv_got_joined_chat(gc, CONF_TO_ID(conf), t);
+ g_conf = purple_serv_got_joined_chat(gc, CONF_TO_ID(conf), t);
- mwConference_setClientData(conf, PURPLE_CONV_CHAT(g_conf), NULL);
+ mwConference_setClientData(conf, g_conf, NULL);
for(; members; members = members->next) {
struct mwLoginInfo *peer = members->data;
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(g_conf), peer->user_id,
- NULL, PURPLE_CBFLAGS_NONE, FALSE);
+ purple_chat_conversation_add_user(g_conf, peer->user_id,
+ NULL, PURPLE_CHAT_USER_NONE, FALSE);
}
}
@@ -2015,9 +2027,10 @@ static void mw_conf_closed(struct mwConference *conf, guint32 reason) {
pd = mwSession_getClientData(session);
gc = pd->gc;
- serv_got_chat_left(gc, CONF_TO_ID(conf));
+ purple_serv_got_chat_left(gc, CONF_TO_ID(conf));
- purple_notify_error(gc, _("Conference Closed"), NULL, msg);
+ purple_notify_error(gc, _("Conference Closed"), NULL, msg,
+ purple_request_cpar_from_connection(gc));
g_free(msg);
}
@@ -2025,7 +2038,7 @@ static void mw_conf_closed(struct mwConference *conf, guint32 reason) {
static void mw_conf_peer_joined(struct mwConference *conf,
struct mwLoginInfo *peer) {
- PurpleConvChat *g_conf;
+ PurpleChatConversation *g_conf;
const char *n = mwConference_getName(conf);
@@ -2034,15 +2047,15 @@ static void mw_conf_peer_joined(struct mwConference *conf,
g_conf = mwConference_getClientData(conf);
g_return_if_fail(g_conf != NULL);
- purple_conv_chat_add_user(g_conf, peer->user_id,
- NULL, PURPLE_CBFLAGS_NONE, TRUE);
+ purple_chat_conversation_add_user(g_conf, peer->user_id,
+ NULL, PURPLE_CHAT_USER_NONE, TRUE);
}
static void mw_conf_peer_parted(struct mwConference *conf,
struct mwLoginInfo *peer) {
- PurpleConvChat *g_conf;
+ PurpleChatConversation *g_conf;
const char *n = mwConference_getName(conf);
@@ -2051,7 +2064,7 @@ static void mw_conf_peer_parted(struct mwConference *conf,
g_conf = mwConference_getClientData(conf);
g_return_if_fail(g_conf != NULL);
- purple_conv_chat_remove_user(g_conf, peer->user_id, NULL);
+ purple_chat_conversation_remove_user(g_conf, peer->user_id, NULL);
}
@@ -2072,7 +2085,7 @@ static void mw_conf_text(struct mwConference *conf,
gc = pd->gc;
esc = g_markup_escape_text(text, -1);
- serv_got_chat_in(gc, CONF_TO_ID(conf), who->user_id, 0, esc, time(NULL));
+ purple_serv_got_chat_in(gc, CONF_TO_ID(conf), who->user_id, PURPLE_MESSAGE_RECV, esc, time(NULL));
g_free(esc);
}
@@ -2124,7 +2137,7 @@ static struct mwServiceConference *mw_srvc_conf_new(struct mwSession *s) {
static void ft_incoming_cancel(PurpleXfer *xfer) {
/* incoming transfer rejected or cancelled in-progress */
- struct mwFileTransfer *ft = xfer->data;
+ struct mwFileTransfer *ft = purple_xfer_get_protocol_data(xfer);
if(ft) mwFileTransfer_reject(ft);
}
@@ -2138,17 +2151,10 @@ static void ft_incoming_init(PurpleXfer *xfer) {
*/
struct mwFileTransfer *ft;
- FILE *fp;
- ft = xfer->data;
+ ft = purple_xfer_get_protocol_data(xfer);
- fp = g_fopen(xfer->local_filename, "wb");
- if(! fp) {
- mwFileTransfer_cancel(ft);
- return;
- }
-
- xfer->dest_fp = fp;
+ purple_xfer_start(xfer, -1, NULL, 0);
mwFileTransfer_accept(ft);
}
@@ -2182,12 +2188,12 @@ static void mw_ft_offered(struct mwFileTransfer *ft) {
DEBUG_INFO(" size: %u\n", mwFileTransfer_getFileSize(ft));
DEBUG_INFO(" text: %s\n", NSTR(mwFileTransfer_getMessage(ft)));
- xfer = purple_xfer_new(acct, PURPLE_XFER_RECEIVE, who);
+ xfer = purple_xfer_new(acct, PURPLE_XFER_TYPE_RECEIVE, who);
if (xfer)
{
- purple_xfer_ref(xfer);
- mwFileTransfer_setClientData(ft, xfer, (GDestroyNotify) purple_xfer_unref);
- xfer->data = ft;
+ 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);
@@ -2202,7 +2208,7 @@ static void mw_ft_offered(struct mwFileTransfer *ft) {
}
-static void ft_send(struct mwFileTransfer *ft, FILE *fp) {
+static void ft_send(struct mwFileTransfer *ft) {
guchar buf[MW_FT_LEN];
struct mwOpaque o = { MW_FT_LEN, buf };
guint32 rem;
@@ -2213,11 +2219,10 @@ static void ft_send(struct mwFileTransfer *ft, FILE *fp) {
rem = mwFileTransfer_getRemaining(ft);
if(rem < MW_FT_LEN) o.len = rem;
- if (fread(buf, (size_t)o.len, 1, fp) == 1) {
+ if(purple_xfer_read_file(xfer, buf, (size_t) o.len) == (gssize)o.len) {
/* calculate progress and display it */
- xfer->bytes_sent += o.len;
- xfer->bytes_remaining -= o.len;
+ purple_xfer_set_bytes_sent(xfer, purple_xfer_get_bytes_sent(xfer) + o.len);
purple_xfer_update_progress(xfer);
mwFileTransfer_send(ft, &o);
@@ -2248,10 +2253,9 @@ static void mw_ft_opened(struct mwFileTransfer *ft) {
g_return_if_reached();
}
- if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
- xfer->dest_fp = g_fopen(xfer->local_filename, "rb");
- if (xfer->dest_fp)
- ft_send(ft, xfer->dest_fp);
+ if(purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND) {
+ purple_xfer_start(xfer, -1, NULL, 0);
+ ft_send(ft);
}
}
@@ -2267,7 +2271,7 @@ static void mw_ft_closed(struct mwFileTransfer *ft, guint32 code) {
xfer = mwFileTransfer_getClientData(ft);
if(xfer) {
- xfer->data = NULL;
+ purple_xfer_set_protocol_data(xfer, NULL);
if(! mwFileTransfer_getRemaining(ft)) {
purple_xfer_set_completed(xfer, TRUE);
@@ -2284,7 +2288,7 @@ static void mw_ft_closed(struct mwFileTransfer *ft, guint32 code) {
purple_xfer_cancel_remote(xfer);
/* drop the stolen reference */
- purple_xfer_unref(xfer);
+ g_object_unref(xfer);
return;
}
}
@@ -2302,26 +2306,19 @@ static void mw_ft_recv(struct mwFileTransfer *ft,
*/
PurpleXfer *xfer;
- FILE *fp;
- size_t wc;
xfer = mwFileTransfer_getClientData(ft);
g_return_if_fail(xfer != NULL);
- fp = xfer->dest_fp;
- g_return_if_fail(fp != NULL);
-
/* we must collect and save our precious data */
- wc = fwrite(data->data, 1, data->len, fp);
- if (wc != data->len) {
+ if (!purple_xfer_write_file(xfer, data->data, data->len)) {
DEBUG_ERROR("failed to write data\n");
purple_xfer_cancel_local(xfer);
return;
}
/* update the progress */
- xfer->bytes_sent += data->len;
- xfer->bytes_remaining -= data->len;
+ purple_xfer_set_bytes_sent(xfer, purple_xfer_get_bytes_sent(xfer) + data->len);
purple_xfer_update_progress(xfer);
/* let the other side know we got it, and to send some more */
@@ -2334,14 +2331,14 @@ static void mw_ft_ack(struct mwFileTransfer *ft) {
xfer = mwFileTransfer_getClientData(ft);
g_return_if_fail(xfer != NULL);
- g_return_if_fail(xfer->watcher == 0);
+ g_return_if_fail(purple_xfer_get_watcher(xfer) == 0);
if(! mwFileTransfer_getRemaining(ft)) {
purple_xfer_set_completed(xfer, TRUE);
purple_xfer_end(xfer);
} else if(mwFileTransfer_isOpen(ft)) {
- ft_send(ft, xfer->dest_fp);
+ ft_send(ft);
}
}
@@ -2406,7 +2403,7 @@ static void convo_data_new(struct mwConversation *conv) {
}
-static PurpleConversation *convo_get_gconv(struct mwConversation *conv) {
+static PurpleIMConversation *convo_get_im(struct mwConversation *conv) {
struct mwServiceIm *srvc;
struct mwSession *session;
struct mwPurplePluginData *pd;
@@ -2423,8 +2420,7 @@ static PurpleConversation *convo_get_gconv(struct mwConversation *conv) {
idb = mwConversation_getTarget(conv);
- return purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
- idb->user, acct);
+ return purple_conversations_find_im_with_account(idb->user, acct);
}
@@ -2458,7 +2454,8 @@ static void convo_queue(struct mwConversation *conv,
/* Does what it takes to get an error displayed for a conversation */
static void convo_error(struct mwConversation *conv, guint32 err) {
- PurpleConversation *gconv;
+ PurpleIMConversation *im;
+ PurpleConnection *pc;
char *tmp, *text;
struct mwIdBlock *idb;
@@ -2467,14 +2464,16 @@ static void convo_error(struct mwConversation *conv, guint32 err) {
tmp = mwError(err);
text = g_strconcat(_("Unable to send message: "), tmp, NULL);
- gconv = convo_get_gconv(conv);
- if(gconv && !purple_conv_present_error(idb->user, gconv->account, text)) {
+ im = convo_get_im(conv);
+ if(im && !purple_conversation_present_error(idb->user,
+ purple_conversation_get_account(PURPLE_CONVERSATION(im)), text)) {
g_free(text);
text = g_strdup_printf(_("Unable to send message to %s:"),
(idb->user)? idb->user: "(unknown)");
- purple_notify_error(purple_account_get_connection(gconv->account),
- NULL, text, tmp);
+ pc = purple_account_get_connection(purple_conversation_get_account(
+ PURPLE_CONVERSATION(im)));
+ purple_notify_error(pc, NULL, text, tmp, purple_request_cpar_from_connection(pc));
}
g_free(tmp);
@@ -2505,16 +2504,17 @@ static void convo_queue_send(struct mwConversation *conv) {
inform the purple conversation that it's unsafe to offer any *cool*
features. */
static void convo_nofeatures(struct mwConversation *conv) {
- PurpleConversation *gconv;
+ PurpleIMConversation *im;
PurpleConnection *gc;
- gconv = convo_get_gconv(conv);
- if(! gconv) return;
+ im = convo_get_im(conv);
+ if(! im) return;
- gc = purple_conversation_get_gc(gconv);
+ gc = purple_conversation_get_connection(PURPLE_CONVERSATION(im));
if(! gc) return;
- purple_conversation_set_features(gconv, gc->flags);
+ purple_conversation_set_features(PURPLE_CONVERSATION(im),
+ purple_connection_get_flags(gc));
}
@@ -2522,29 +2522,29 @@ static void convo_nofeatures(struct mwConversation *conv) {
to inform the purple conversation of what features to offer the
user */
static void convo_features(struct mwConversation *conv) {
- PurpleConversation *gconv;
+ PurpleIMConversation *im;
PurpleConnectionFlags feat;
- gconv = convo_get_gconv(conv);
- if(! gconv) return;
+ im = convo_get_im(conv);
+ if(! im) return;
- feat = purple_conversation_get_features(gconv);
+ feat = purple_conversation_get_features(PURPLE_CONVERSATION(im));
if(mwConversation_isOpen(conv)) {
if(mwConversation_supports(conv, mwImSend_HTML)) {
- feat |= PURPLE_CONNECTION_HTML;
+ feat |= PURPLE_CONNECTION_FLAG_HTML;
} else {
- feat &= ~PURPLE_CONNECTION_HTML;
+ feat &= ~PURPLE_CONNECTION_FLAG_HTML;
}
if(mwConversation_supports(conv, mwImSend_MIME)) {
- feat &= ~PURPLE_CONNECTION_NO_IMAGES;
+ feat &= ~PURPLE_CONNECTION_FLAG_NO_IMAGES;
} else {
- feat |= PURPLE_CONNECTION_NO_IMAGES;
+ feat |= PURPLE_CONNECTION_FLAG_NO_IMAGES;
}
DEBUG_INFO("conversation features set to 0x%04x\n", feat);
- purple_conversation_set_features(gconv, feat);
+ purple_conversation_set_features(PURPLE_CONVERSATION(im), feat);
} else {
convo_nofeatures(conv);
@@ -2572,7 +2572,7 @@ static void mw_conversation_opened(struct mwConversation *conv) {
if(cd) {
convo_queue_send(conv);
- if(! convo_get_gconv(conv)) {
+ if(! convo_get_im(conv)) {
mwConversation_free(conv);
return;
}
@@ -2586,7 +2586,7 @@ static void mw_conversation_opened(struct mwConversation *conv) {
struct mwLoginInfo *info;
info = mwConversation_getTargetInfo(conv);
- buddy = purple_find_buddy(acct, info->user_id);
+ buddy = purple_blist_find_buddy(acct, info->user_id);
if(buddy) {
purple_blist_node_set_int((PurpleBlistNode *) buddy,
BUDDY_KEY_CLIENT, info->type);
@@ -2643,7 +2643,7 @@ static void im_recv_text(struct mwConversation *conv,
t = txt? txt: msg;
esc = g_markup_escape_text(t, -1);
- serv_got_im(pd->gc, idb->user, esc, 0, time(NULL));
+ purple_serv_got_im(pd->gc, idb->user, esc, 0, time(NULL));
g_free(esc);
g_free(txt);
@@ -2657,8 +2657,8 @@ static void im_recv_typing(struct mwConversation *conv,
struct mwIdBlock *idb;
idb = mwConversation_getTarget(conv);
- serv_got_typing(pd->gc, idb->user, 0,
- typing? PURPLE_TYPING: PURPLE_NOT_TYPING);
+ purple_serv_got_typing(pd->gc, idb->user, 0,
+ typing? PURPLE_IM_TYPING: PURPLE_IM_NOT_TYPING);
}
@@ -2679,7 +2679,7 @@ static void im_recv_html(struct mwConversation *conv,
t2 = purple_utf8_ncr_decode(t);
t = t2? t2: t;
- serv_got_im(pd->gc, idb->user, t, 0, time(NULL));
+ purple_serv_got_im(pd->gc, idb->user, t, 0, time(NULL));
g_free(t1);
g_free(t2);
@@ -2720,15 +2720,13 @@ static void im_recv_mime(struct mwConversation *conv,
const char *data) {
GHashTable *img_by_cid;
- GList *images;
GString *str;
PurpleMimeDocument *doc;
GList *parts;
- img_by_cid = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
- images = NULL;
+ img_by_cid = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref);
/* don't want the contained string to ever be NULL */
str = g_string_new("");
@@ -2753,7 +2751,7 @@ static void im_recv_mime(struct mwConversation *conv,
guchar *d_dat;
gsize d_len;
char *cid;
- int img;
+ PurpleImage *image;
/* obtain and unencode the data */
purple_mime_part_get_data_decoded(part, &d_dat, &d_len);
@@ -2763,14 +2761,11 @@ static void im_recv_mime(struct mwConversation *conv,
cid = make_cid(cid);
/* add image to the purple image store */
- img = purple_imgstore_add_with_id(d_dat, d_len, cid);
+ image = purple_image_new_from_data(d_dat, d_len);
+ purple_image_set_friendly_filename(image, cid);
/* map the cid to the image store identifier */
- g_hash_table_insert(img_by_cid, cid, GINT_TO_POINTER(img));
-
- /* recall the image for dereferencing later */
- images = g_list_append(images, GINT_TO_POINTER(img));
-
+ g_hash_table_insert(img_by_cid, cid, image);
} else if(purple_str_has_prefix(type, "text")) {
/* concatenate all the text parts together */
@@ -2816,7 +2811,8 @@ static void im_recv_mime(struct mwConversation *conv,
if(align) g_string_append_printf(atstr, " align=\"%s\"", align);
if(border) g_string_append_printf(atstr, " border=\"%s\"", border);
- mov = g_snprintf(start, len, "<img%s id=\"%i\"", atstr->str, img);
+ mov = g_snprintf(start, len, "<img src=\"" PURPLE_IMAGE_STORE_PROTOCOL
+ "%u\"%s", img, atstr->str);
while(mov < len) start[mov++] = ' ';
g_string_free(atstr, TRUE);
@@ -2833,12 +2829,6 @@ static void im_recv_mime(struct mwConversation *conv,
/* clean up the cid table */
g_hash_table_destroy(img_by_cid);
-
- /* dereference all the imgages */
- while(images) {
- purple_imgstore_unref_by_id(GPOINTER_TO_INT(images->data));
- images = g_list_delete_link(images, images);
- }
}
@@ -2906,7 +2896,7 @@ static void mw_place_invite(struct mwConversation *conv,
if(! title) title = "(no title)";
if(! message) message = "(no message)";
- serv_got_chat_invite(pd->gc, title, idb->user, message, ht);
+ purple_serv_got_chat_invite(pd->gc, title, idb->user, message, ht);
mwConversation_close(conv, ERR_SUCCESS);
mwConversation_free(conv);
@@ -2935,7 +2925,7 @@ static struct mwServiceIm *mw_srvc_im_new(struct mwSession *s) {
}
-/* The following helps us relate a mwPlace to a PurpleConvChat in the
+/* The following helps us relate a mwPlace to a PurpleChatConversation in the
various forms by which either may be indicated. Uses some of
the similar macros from the conference service above */
@@ -2955,7 +2945,7 @@ place_find_by_id(struct mwPurplePluginData *pd, int id) {
l = (GList *) mwServicePlace_getPlaces(srvc);
for(; l; l = l->next) {
struct mwPlace *p = l->data;
- PurpleConvChat *h = PURPLE_CONV_CHAT(mwPlace_getClientData(p));
+ PurpleChatConversation *h = mwPlace_getClientData(p);
if(CHAT_TO_ID(h) == id) {
place = p;
@@ -2972,7 +2962,7 @@ static void mw_place_opened(struct mwPlace *place) {
struct mwSession *session;
struct mwPurplePluginData *pd;
PurpleConnection *gc;
- PurpleConversation *gconf;
+ PurpleChatConversation *gconf;
GList *members, *l;
@@ -2990,14 +2980,14 @@ static void mw_place_opened(struct mwPlace *place) {
NSTR(n), g_list_length(members));
if(! t) t = "(no title)";
- gconf = serv_got_joined_chat(gc, PLACE_TO_ID(place), t);
+ gconf = purple_serv_got_joined_chat(gc, PLACE_TO_ID(place), t);
mwPlace_setClientData(place, gconf, NULL);
for(l = members; l; l = l->next) {
struct mwIdBlock *idb = l->data;
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(gconf), idb->user,
- NULL, PURPLE_CBFLAGS_NONE, FALSE);
+ purple_chat_conversation_add_user(gconf, idb->user,
+ NULL, PURPLE_CHAT_USER_NONE, FALSE);
}
g_list_free(members);
}
@@ -3019,16 +3009,17 @@ static void mw_place_closed(struct mwPlace *place, guint32 code) {
pd = mwSession_getClientData(session);
gc = pd->gc;
- serv_got_chat_left(gc, PLACE_TO_ID(place));
+ purple_serv_got_chat_left(gc, PLACE_TO_ID(place));
- purple_notify_error(gc, _("Place Closed"), NULL, msg);
+ purple_notify_error(gc, _("Place Closed"), NULL, msg,
+ purple_request_cpar_from_connection(gc));
g_free(msg);
}
static void mw_place_peerJoined(struct mwPlace *place,
const struct mwIdBlock *peer) {
- PurpleConversation *gconf;
+ PurpleChatConversation *gconf;
const char *n = mwPlace_getName(place);
@@ -3037,14 +3028,14 @@ static void mw_place_peerJoined(struct mwPlace *place,
gconf = mwPlace_getClientData(place);
g_return_if_fail(gconf != NULL);
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(gconf), peer->user,
- NULL, PURPLE_CBFLAGS_NONE, TRUE);
+ purple_chat_conversation_add_user(gconf, peer->user,
+ NULL, PURPLE_CHAT_USER_NONE, TRUE);
}
static void mw_place_peerParted(struct mwPlace *place,
const struct mwIdBlock *peer) {
- PurpleConversation *gconf;
+ PurpleChatConversation *gconf;
const char *n = mwPlace_getName(place);
@@ -3053,7 +3044,7 @@ static void mw_place_peerParted(struct mwPlace *place,
gconf = mwPlace_getClientData(place);
g_return_if_fail(gconf != NULL);
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(gconf), peer->user, NULL);
+ purple_chat_conversation_remove_user(gconf, peer->user, NULL);
}
@@ -3088,7 +3079,8 @@ static void mw_place_message(struct mwPlace *place,
gc = pd->gc;
esc = g_markup_escape_text(msg, -1);
- serv_got_chat_in(gc, PLACE_TO_ID(place), who->user, 0, esc, time(NULL));
+ purple_serv_got_chat_in(gc, PLACE_TO_ID(place), who->user,
+ PURPLE_MESSAGE_RECV, esc, time(NULL));
g_free(esc);
}
@@ -3148,7 +3140,7 @@ static struct mwPurplePluginData *mwPurplePluginData_new(PurpleConnection *gc) {
pd->srvc_resolve = mw_srvc_resolve_new(pd->session);
pd->srvc_store = mw_srvc_store_new(pd->session);
pd->group_list_map = g_hash_table_new(g_direct_hash, g_direct_equal);
- pd->sock_buf = purple_circ_buffer_new(0);
+ pd->sock_buf = purple_circular_buffer_new(0);
mwSession_addService(pd->session, MW_SERVICE(pd->srvc_aware));
mwSession_addService(pd->session, MW_SERVICE(pd->srvc_conf));
@@ -3162,7 +3154,7 @@ static struct mwPurplePluginData *mwPurplePluginData_new(PurpleConnection *gc) {
mwSession_addCipher(pd->session, mwCipher_new_RC2_128(pd->session));
mwSession_setClientData(pd->session, pd, NULL);
- gc->proto_data = pd;
+ purple_connection_set_protocol_data(gc, pd);
return pd;
}
@@ -3171,7 +3163,7 @@ static struct mwPurplePluginData *mwPurplePluginData_new(PurpleConnection *gc) {
static void mwPurplePluginData_free(struct mwPurplePluginData *pd) {
g_return_if_fail(pd != NULL);
- pd->gc->proto_data = NULL;
+ purple_connection_set_protocol_data(pd->gc, NULL);
mwSession_removeService(pd->session, mwService_AWARE);
mwSession_removeService(pd->session, mwService_CONFERENCE);
@@ -3195,7 +3187,7 @@ static void mwPurplePluginData_free(struct mwPurplePluginData *pd) {
mwSession_free(pd->session);
g_hash_table_destroy(pd->group_list_map);
- purple_circ_buffer_destroy(pd->sock_buf);
+ g_object_unref(G_OBJECT(pd->sock_buf));
g_free(pd);
}
@@ -3233,7 +3225,7 @@ static char *mw_prpl_status_text(PurpleBuddy *b) {
const char *ret = NULL;
if ((gc = purple_account_get_connection(purple_buddy_get_account(b)))
- && (pd = gc->proto_data))
+ && (pd = purple_connection_get_protocol_data(gc)))
ret = mwServiceAware_getText(pd->srvc_aware, &t);
return (ret && g_utf8_validate(ret, -1, NULL)) ? g_markup_escape_text(ret, -1): NULL;
@@ -3296,29 +3288,27 @@ static void mw_prpl_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info
char *tmp;
if ((gc = purple_account_get_connection(purple_buddy_get_account(b)))
- && (pd = gc->proto_data))
+ && (pd = purple_connection_get_protocol_data(gc)))
message = mwServiceAware_getText(pd->srvc_aware, &idb);
status = status_text(b);
if(message != NULL && g_utf8_validate(message, -1, NULL) && purple_utf8_strcasecmp(status, message)) {
- tmp = g_markup_escape_text(message, -1);
- purple_notify_user_info_add_pair(user_info, status, tmp);
- g_free(tmp);
+ purple_notify_user_info_add_pair_plaintext(user_info, status, message);
} else {
- purple_notify_user_info_add_pair(user_info, _("Status"), status);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), status);
}
if(full && pd != NULL) {
tmp = user_supports_text(pd->srvc_aware, purple_buddy_get_name(b));
if(tmp) {
- purple_notify_user_info_add_pair(user_info, _("Supports"), tmp);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Supports"), tmp);
g_free(tmp);
}
if(buddy_is_external(b)) {
- purple_notify_user_info_add_pair(user_info, NULL, _("External User"));
+ purple_notify_user_info_add_pair_plaintext(user_info, NULL, _("External User"));
}
}
}
@@ -3330,19 +3320,19 @@ static GList *mw_prpl_status_types(PurpleAccount *acct)
type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
MW_STATE_ACTIVE, NULL, TRUE, TRUE, FALSE,
- MW_STATE_MESSAGE, _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ MW_STATE_MESSAGE, _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_append(types, type);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY,
MW_STATE_AWAY, NULL, TRUE, TRUE, FALSE,
- MW_STATE_MESSAGE, _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ MW_STATE_MESSAGE, _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_append(types, type);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE,
MW_STATE_BUSY, _("Do Not Disturb"), TRUE, TRUE, FALSE,
- MW_STATE_MESSAGE, _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ MW_STATE_MESSAGE, _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_append(types, type);
@@ -3375,7 +3365,7 @@ static void conf_create_prompt_join(PurpleBuddy *buddy,
acct = purple_buddy_get_account(buddy);
gc = purple_account_get_connection(acct);
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
srvc = pd->srvc_conf;
f = purple_request_fields_get_field(fields, CHAT_KEY_TOPIC);
@@ -3433,7 +3423,7 @@ static void blist_menu_conf_create(PurpleBuddy *buddy, const char *msg) {
msgA, msg1, fields,
_("Create"), G_CALLBACK(conf_create_prompt_join),
_("Cancel"), G_CALLBACK(conf_create_prompt_cancel),
- acct, purple_buddy_get_name(buddy), NULL,
+ purple_request_cpar_from_account(acct),
buddy);
g_free(msg1);
}
@@ -3519,7 +3509,7 @@ static void blist_menu_conf_list(PurpleBuddy *buddy,
msgA, msg, fields,
_("Invite"), G_CALLBACK(conf_select_prompt_invite),
_("Cancel"), G_CALLBACK(conf_select_prompt_cancel),
- acct, purple_buddy_get_name(buddy), NULL,
+ purple_request_cpar_from_account(acct),
buddy);
g_free(msg);
}
@@ -3533,7 +3523,7 @@ static void blist_menu_conf(PurpleBlistNode *node, gpointer data) {
GList *l;
g_return_if_fail(node != NULL);
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
acct = purple_buddy_get_account(buddy);
g_return_if_fail(acct != NULL);
@@ -3541,7 +3531,7 @@ static void blist_menu_conf(PurpleBlistNode *node, gpointer data) {
gc = purple_account_get_connection(acct);
g_return_if_fail(gc != NULL);
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
g_return_if_fail(pd != NULL);
/*
@@ -3572,7 +3562,7 @@ static void blist_menu_announce(PurpleBlistNode *node, gpointer data) {
GList *rcpt;
g_return_if_fail(node != NULL);
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
acct = buddy->account;
g_return_if_fail(acct != NULL);
@@ -3580,7 +3570,7 @@ static void blist_menu_announce(PurpleBlistNode *node, gpointer data) {
gc = purple_account_get_connection(acct);
g_return_if_fail(gc != NULL);
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
g_return_if_fail(pd != NULL);
rcpt_name = g_strdup_printf("@U %s", buddy->name);
@@ -3601,7 +3591,7 @@ static GList *mw_prpl_blist_node_menu(PurpleBlistNode *node) {
GList *l = NULL;
PurpleMenuAction *act;
- if(! PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if(! PURPLE_IS_BUDDY(node))
return l;
l = g_list_append(l, NULL);
@@ -3668,7 +3658,7 @@ static void mw_prpl_login(PurpleAccount *account) {
pd = mwPurplePluginData_new(gc);
/* while we do support images, the default is to not offer it */
- gc->flags |= PURPLE_CONNECTION_NO_IMAGES;
+ purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_NO_IMAGES);
user = g_strdup(purple_account_get_username(account));
@@ -3688,13 +3678,13 @@ static void mw_prpl_login(PurpleAccount *account) {
/* somehow, we don't have a host to connect to. Well, we need one
to actually continue, so let's ask the user directly. */
g_free(user);
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
_("A server is required to connect this account"));
return;
}
- pass = g_strdup(purple_account_get_password(account));
+ pass = g_strdup(purple_connection_get_password(gc));
port = purple_account_get_int(account, MW_KEY_PORT, MW_PLUGIN_DEFAULT_PORT);
DEBUG_INFO("user: '%s'\n", user);
@@ -3733,7 +3723,7 @@ static void mw_prpl_login(PurpleAccount *account) {
purple_connection_update_progress(gc, _("Connecting"), 1, MW_CONNECT_STEPS);
if (purple_proxy_connect(gc, account, host, port, connect_cb, pd) == NULL) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to connect"));
}
}
@@ -3744,7 +3734,7 @@ static void mw_prpl_close(PurpleConnection *gc) {
g_return_if_fail(gc != NULL);
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
g_return_if_fail(pd != NULL);
/* get rid of the blist save timeout */
@@ -3758,12 +3748,12 @@ static void mw_prpl_close(PurpleConnection *gc) {
mwSession_stop(pd->session, 0x00);
/* no longer necessary */
- gc->proto_data = NULL;
+ purple_connection_set_protocol_data(gc, NULL);
/* stop watching the socket */
- if(gc->inpa) {
- purple_input_remove(gc->inpa);
- gc->inpa = 0;
+ if(pd->inpa) {
+ purple_input_remove(pd->inpa);
+ pd->inpa = 0;
}
/* clean up the rest */
@@ -3796,40 +3786,26 @@ static char *im_mime_content_type(void) {
mw_rand() & 0xfff, mw_rand() & 0xffff);
}
+/** determine content type from contents */
+static gchar *
+im_mime_img_content_type(PurpleImage *img)
+{
+ const gchar *mimetype;
-/** determine content type from extension. Not so happy about this,
- but I don't want to actually write image type detection */
-static char *im_mime_img_content_type(PurpleStoredImage *img) {
- const char *fn = purple_imgstore_get_filename(img);
- const char *ct = NULL;
-
- ct = strrchr(fn, '.');
- if(! ct) {
- ct = "image";
-
- } else if(! strcmp(".png", ct)) {
- ct = "image/png";
-
- } else if(! strcmp(".jpg", ct)) {
- ct = "image/jpeg";
-
- } else if(! strcmp(".jpeg", ct)) {
- ct = "image/jpeg";
-
- } else if(! strcmp(".gif", ct)) {
- ct = "image/gif";
+ mimetype = purple_image_get_mimetype(img);
- } else {
- ct = "image";
- }
+ if (!mimetype)
+ mimetype = "image";
- return g_strdup_printf("%s; name=\"%s\"", ct, fn);
+ return g_strdup_printf("%s; name=\"%s\"", mimetype,
+ purple_image_get_friendly_filename(img));
}
-static char *im_mime_img_content_disp(PurpleStoredImage *img) {
- const char *fn = purple_imgstore_get_filename(img);
- return g_strdup_printf("attachment; filename=\"%s\"", fn);
+static char *
+im_mime_img_content_disp(PurpleImage *img) {
+ return g_strdup_printf("attachment; filename=\"%s\"",
+ purple_image_get_friendly_filename(img));
}
@@ -3858,23 +3834,22 @@ static char *im_mime_convert(PurpleConnection *gc,
tmp = (char *) message;
while(*tmp && purple_markup_find_tag("img", tmp, (const char **) &start,
(const char **) &end, &attr)) {
- char *id;
- PurpleStoredImage *img = NULL;
+ gchar *uri;
+ PurpleImage *img = NULL;
gsize len = (start - tmp);
/* append the in-between-tags text */
if(len) g_string_append_len(str, tmp, len);
- /* find the imgstore data by the id tag */
- id = g_datalist_get_data(&attr, "id");
- if(id && *id)
- img = purple_imgstore_find_by_id(atoi(id));
+ uri = g_datalist_get_data(&attr, "src");
+ if (uri)
+ img = purple_image_store_get_from_uri(uri);
if(img) {
char *cid;
gpointer data;
- size_t size;
+ gsize size;
part = purple_mime_part_new(doc);
@@ -3895,8 +3870,8 @@ static char *im_mime_convert(PurpleConnection *gc,
/* obtain and base64 encode the image data, and put it in the
mime part */
- size = purple_imgstore_get_size(img);
- data = purple_base64_encode(purple_imgstore_get_data(img), (gsize) size);
+ size = purple_image_get_size(img);
+ data = purple_base64_encode(purple_image_get_data(img), size);
purple_mime_part_set_data(part, data);
g_free(data);
@@ -3906,7 +3881,7 @@ static char *im_mime_convert(PurpleConnection *gc,
} else {
/* append the literal image tag, since we couldn't find a
- relative imgstore object */
+ relative PurpleImage object */
gsize len = (end - start) + 1;
g_string_append_len(str, start, len);
}
@@ -3939,20 +3914,24 @@ static char *im_mime_convert(PurpleConnection *gc,
}
-static int mw_prpl_send_im(PurpleConnection *gc,
- const char *name,
- const char *message,
- PurpleMessageFlags flags) {
+static int mw_prpl_send_im(PurpleConnection *gc, PurpleMessage *msg) {
+ gchar name[1000];
struct mwPurplePluginData *pd;
- struct mwIdBlock who = { (char *) name, NULL };
+ struct mwIdBlock who = { name, NULL };
struct mwConversation *conv;
+ const gchar *message;
+ PurpleMessageFlags flags;
g_return_val_if_fail(gc != NULL, 0);
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
g_return_val_if_fail(pd != NULL, 0);
+ g_strlcpy(name, purple_message_get_recipient(msg), sizeof(name));
+ message = purple_message_get_contents(msg);
+ flags = purple_message_get_flags(msg);
+
conv = mwServiceIm_getConversation(pd->srvc_im, &who);
/* this detection of features to determine how to send the message
@@ -4015,7 +3994,7 @@ static int mw_prpl_send_im(PurpleConnection *gc,
static unsigned int mw_prpl_send_typing(PurpleConnection *gc,
const char *name,
- PurpleTypingState state) {
+ PurpleIMTypingState state) {
struct mwPurplePluginData *pd;
struct mwIdBlock who = { (char *) name, NULL };
@@ -4024,7 +4003,7 @@ static unsigned int mw_prpl_send_typing(PurpleConnection *gc,
gpointer t = GINT_TO_POINTER(!! state);
g_return_val_if_fail(gc != NULL, 0);
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
g_return_val_if_fail(pd != NULL, 0);
@@ -4033,7 +4012,7 @@ static unsigned int mw_prpl_send_typing(PurpleConnection *gc,
if(mwConversation_isOpen(conv)) {
mwConversation_send(conv, mwImSend_TYPING, t);
- } else if((state == PURPLE_TYPING) || (state == PURPLE_TYPED)) {
+ } else if((state == PURPLE_IM_TYPING) || (state == PURPLE_IM_TYPED)) {
/* only open a channel for sending typing notification, not for
when typing has stopped. There's no point in re-opening a
channel just to tell someone that this side isn't typing. */
@@ -4120,53 +4099,54 @@ static void mw_prpl_get_info(PurpleConnection *gc, const char *who) {
g_return_if_fail(who != NULL);
g_return_if_fail(*who != '\0');
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
acct = purple_connection_get_account(gc);
- b = purple_find_buddy(acct, who);
+ b = purple_blist_find_buddy(acct, who);
user_info = purple_notify_user_info_new();
if(purple_str_has_prefix(who, "@E ")) {
- purple_notify_user_info_add_pair(user_info, _("External User"), NULL);
+ purple_notify_user_info_add_pair_html(user_info, _("External User"), NULL);
}
- purple_notify_user_info_add_pair(user_info, _("User ID"), who);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("User ID"), who);
if(b) {
guint32 type;
if(purple_buddy_get_server_alias(b)) {
- purple_notify_user_info_add_pair(user_info, _("Full Name"), purple_buddy_get_server_alias(b));
+ /* 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, _("Full Name"), purple_buddy_get_server_alias(b));
}
type = purple_blist_node_get_int((PurpleBlistNode *) b, BUDDY_KEY_CLIENT);
if(type) {
- tmp = g_strdup(mw_client_name(type));
- if (!tmp)
+ tmp2 = mw_client_name(type);
+ if (tmp2) {
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Last Known Client"), tmp2);
+ } else {
tmp = g_strdup_printf(_("Unknown (0x%04x)<br>"), type);
-
- purple_notify_user_info_add_pair(user_info, _("Last Known Client"), tmp);
-
- g_free(tmp);
+ purple_notify_user_info_add_pair_html(user_info, _("Last Known Client"), tmp);
+ g_free(tmp);
+ }
}
}
tmp = user_supports_text(pd->srvc_aware, who);
if(tmp) {
- purple_notify_user_info_add_pair(user_info, _("Supports"), tmp);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Supports"), tmp);
g_free(tmp);
}
if(b) {
- purple_notify_user_info_add_pair(user_info, _("Status"), status_text(b));
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), status_text(b));
/* XXX Is this adding a status message in its own section rather than with the "Status" label? */
tmp2 = mwServiceAware_getText(pd->srvc_aware, &idb);
if(tmp2 && g_utf8_validate(tmp2, -1, NULL)) {
- tmp = g_markup_escape_text(tmp2, -1);
purple_notify_user_info_add_section_break(user_info);
- purple_notify_user_info_add_pair(user_info, NULL, tmp);
- g_free(tmp);
+ purple_notify_user_info_add_pair_plaintext(user_info, NULL, tmp2);
}
}
@@ -4265,14 +4245,14 @@ static void mw_prpl_set_idle(PurpleConnection *gc, int t) {
static void notify_im(PurpleConnection *gc, GList *row, void *user_data) {
PurpleAccount *acct;
- PurpleConversation *conv;
+ PurpleIMConversation *im;
char *id;
acct = purple_connection_get_account(gc);
id = g_list_nth_data(row, 1);
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, id, acct);
- if(! conv) conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, id);
- purple_conversation_present(conv);
+ im = purple_conversations_find_im_with_account(id, acct);
+ if(! im) im = purple_im_conversation_new(acct, id);
+ purple_conversation_present(PURPLE_CONVERSATION(im));
}
@@ -4364,7 +4344,7 @@ static void add_buddy_resolved(struct mwServiceResolve *srvc,
buddy = data->buddy;
gc = purple_account_get_connection(purple_buddy_get_account(buddy));
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
if(results)
res = results->data;
@@ -4384,7 +4364,7 @@ static void add_buddy_resolved(struct mwServiceResolve *srvc,
} else {
/* same person, set the server alias */
- purple_blist_server_alias_buddy(buddy, match->name);
+ purple_buddy_set_server_alias(buddy, match->name);
purple_blist_node_set_string((PurpleBlistNode *) buddy,
BUDDY_KEY_NAME, match->name);
@@ -4443,9 +4423,10 @@ static void add_buddy_resolved(struct mwServiceResolve *srvc,
static void mw_prpl_add_buddy(PurpleConnection *gc,
PurpleBuddy *buddy,
- PurpleGroup *group) {
+ PurpleGroup *group,
+ const char *message) {
- struct mwPurplePluginData *pd = gc->proto_data;
+ struct mwPurplePluginData *pd = purple_connection_get_protocol_data(gc);
struct mwServiceResolve *srvc;
GList *query;
enum mwResolveFlag flags;
@@ -4490,13 +4471,14 @@ static void foreach_add_buddies(PurpleGroup *group, GList *buddies,
static void mw_prpl_add_buddies(PurpleConnection *gc,
GList *buddies,
- GList *groups) {
+ GList *groups,
+ const char *message) {
struct mwPurplePluginData *pd;
GHashTable *group_sets;
struct mwAwareIdBlock *idbs, *idb;
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
/* map PurpleGroup:GList of mwAwareIdBlock */
group_sets = g_hash_table_new(g_direct_hash, g_direct_equal);
@@ -4513,7 +4495,7 @@ static void mw_prpl_add_buddies(PurpleConnection *gc,
/* nab the saved server alias and stick it on the buddy */
fn = purple_blist_node_get_string((PurpleBlistNode *) b, BUDDY_KEY_NAME);
- purple_blist_server_alias_buddy(b, fn);
+ purple_buddy_set_server_alias(b, fn);
/* convert PurpleBuddy into a mwAwareIdBlock */
idb->type = mwAware_USER;
@@ -4547,7 +4529,7 @@ static void mw_prpl_remove_buddy(PurpleConnection *gc,
GList *rem = g_list_prepend(NULL, &idb);
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
group = purple_buddy_get_group(buddy);
list = list_ensure(pd, group);
@@ -4594,37 +4576,37 @@ static void mw_prpl_set_permit_deny(PurpleConnection *gc) {
acct = purple_connection_get_account(gc);
g_return_if_fail(acct != NULL);
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
g_return_if_fail(pd != NULL);
session = pd->session;
g_return_if_fail(session != NULL);
- switch(acct->perm_deny) {
- case PURPLE_PRIVACY_DENY_USERS:
- DEBUG_INFO("PURPLE_PRIVACY_DENY_USERS\n");
- privacy_fill(&privacy, acct->deny);
+ switch(purple_account_get_privacy_type(acct)) {
+ case PURPLE_ACCOUNT_PRIVACY_DENY_USERS:
+ DEBUG_INFO("PURPLE_ACCOUNT_PRIVACY_DENY_USERS\n");
+ privacy_fill(&privacy, purple_account_privacy_get_denied(acct));
privacy.deny = TRUE;
break;
- case PURPLE_PRIVACY_ALLOW_ALL:
- DEBUG_INFO("PURPLE_PRIVACY_ALLOW_ALL\n");
+ case PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL:
+ DEBUG_INFO("PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL\n");
privacy.deny = TRUE;
break;
- case PURPLE_PRIVACY_ALLOW_USERS:
- DEBUG_INFO("PURPLE_PRIVACY_ALLOW_USERS\n");
- privacy_fill(&privacy, acct->permit);
+ case PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS:
+ DEBUG_INFO("PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS\n");
+ privacy_fill(&privacy, purple_account_privacy_get_permitted(acct));
privacy.deny = FALSE;
break;
- case PURPLE_PRIVACY_DENY_ALL:
- DEBUG_INFO("PURPLE_PRIVACY_DENY_ALL\n");
+ case PURPLE_ACCOUNT_PRIVACY_DENY_ALL:
+ DEBUG_INFO("PURPLE_ACCOUNT_PRIVACY_DENY_ALL\n");
privacy.deny = FALSE;
break;
default:
- DEBUG_INFO("acct->perm_deny is 0x%x\n", acct->perm_deny);
+ DEBUG_INFO("acct->perm_deny is 0x%x\n", purple_account_get_privacy_type(acct));
return;
}
@@ -4678,7 +4660,7 @@ static void mw_prpl_join_chat(PurpleConnection *gc,
struct mwPurplePluginData *pd;
char *c, *t;
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
c = g_hash_table_lookup(components, CHAT_KEY_NAME);
t = g_hash_table_lookup(components, CHAT_KEY_TOPIC);
@@ -4720,7 +4702,7 @@ static void mw_prpl_reject_chat(PurpleConnection *gc,
struct mwServiceConference *srvc;
char *c;
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
srvc = pd->srvc_conf;
if(g_hash_table_lookup(components, CHAT_KEY_IS_PLACE)) {
@@ -4752,7 +4734,7 @@ static void mw_prpl_chat_invite(PurpleConnection *gc,
struct mwPlace *place;
struct mwIdBlock idb = { (char *) who, NULL };
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
g_return_if_fail(pd != NULL);
conf = ID_TO_CONF(pd, id);
@@ -4776,7 +4758,7 @@ static void mw_prpl_chat_leave(PurpleConnection *gc,
struct mwPurplePluginData *pd;
struct mwConference *conf;
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
g_return_if_fail(pd != NULL);
conf = ID_TO_CONF(pd, id);
@@ -4793,31 +4775,19 @@ static void mw_prpl_chat_leave(PurpleConnection *gc,
}
-static void mw_prpl_chat_whisper(PurpleConnection *gc,
- int id,
- const char *who,
- const char *message) {
-
- mw_prpl_send_im(gc, who, message, 0);
-}
-
-
-static int mw_prpl_chat_send(PurpleConnection *gc,
- int id,
- const char *message,
- PurpleMessageFlags flags) {
-
+static int mw_prpl_chat_send(PurpleConnection *gc, int id, PurpleMessage *pmsg)
+{
struct mwPurplePluginData *pd;
struct mwConference *conf;
char *msg;
int ret;
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
g_return_val_if_fail(pd != NULL, 0);
conf = ID_TO_CONF(pd, id);
- msg = purple_markup_strip_html(message);
+ msg = purple_markup_strip_html(purple_message_get_contents(pmsg));
if(conf) {
ret = ! mwConference_sendText(conf, msg);
@@ -4850,7 +4820,7 @@ static void mw_prpl_alias_buddy(PurpleConnection *gc,
const char *who,
const char *alias) {
- struct mwPurplePluginData *pd = gc->proto_data;
+ struct mwPurplePluginData *pd = purple_connection_get_protocol_data(gc);
g_return_if_fail(pd != NULL);
/* it's a change to the buddy list, so we've gotta reflect that in
@@ -4868,17 +4838,17 @@ static void mw_prpl_group_buddy(PurpleConnection *gc,
struct mwAwareIdBlock idb = { mwAware_USER, (char *) who, NULL };
GList *gl = g_list_prepend(NULL, &idb);
- struct mwPurplePluginData *pd = gc->proto_data;
+ struct mwPurplePluginData *pd = purple_connection_get_protocol_data(gc);
PurpleGroup *group;
struct mwAwareList *list;
/* add who to new_group's aware list */
- group = purple_find_group(new_group);
+ group = purple_blist_find_group(new_group);
list = list_ensure(pd, group);
mwAwareList_addAware(list, gl);
/* remove who from old_group's aware list */
- group = purple_find_group(old_group);
+ group = purple_blist_find_group(old_group);
list = list_ensure(pd, group);
mwAwareList_removeAware(list, gl);
@@ -4894,7 +4864,7 @@ static void mw_prpl_rename_group(PurpleConnection *gc,
PurpleGroup *group,
GList *buddies) {
- struct mwPurplePluginData *pd = gc->proto_data;
+ struct mwPurplePluginData *pd = purple_connection_get_protocol_data(gc);
g_return_if_fail(pd != NULL);
/* it's a change in the buddy list, so we've gotta reflect that in
@@ -4914,7 +4884,7 @@ static void mw_prpl_buddy_free(PurpleBuddy *buddy) {
static void mw_prpl_convo_closed(PurpleConnection *gc, const char *who) {
- struct mwPurplePluginData *pd = gc->proto_data;
+ struct mwPurplePluginData *pd = purple_connection_get_protocol_data(gc);
struct mwServiceIm *srvc;
struct mwConversation *conv;
struct mwIdBlock idb = { (char *) who, NULL };
@@ -4949,7 +4919,7 @@ static void mw_prpl_remove_group(PurpleConnection *gc, PurpleGroup *group) {
struct mwPurplePluginData *pd;
struct mwAwareList *list;
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
g_return_if_fail(pd != NULL);
g_return_if_fail(pd->group_list_map != NULL);
@@ -4973,7 +4943,7 @@ static gboolean mw_prpl_can_receive_file(PurpleConnection *gc,
g_return_val_if_fail(gc != NULL, FALSE);
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
g_return_val_if_fail(pd != NULL, FALSE);
srvc = pd->srvc_aware;
@@ -4982,7 +4952,7 @@ static gboolean mw_prpl_can_receive_file(PurpleConnection *gc,
acct = purple_connection_get_account(gc);
g_return_val_if_fail(acct != NULL, FALSE);
- return purple_find_buddy(acct, who) &&
+ return purple_blist_find_buddy(acct, who) &&
user_supports(srvc, who, mwAttribute_FILE_TRANSFER);
}
@@ -4998,6 +4968,7 @@ static void ft_outgoing_init(PurpleXfer *xfer) {
const char *filename;
gsize filesize;
FILE *fp;
+ char *remote_user = NULL;
struct mwIdBlock idb = { NULL, NULL };
@@ -5005,12 +4976,14 @@ static void ft_outgoing_init(PurpleXfer *xfer) {
acct = purple_xfer_get_account(xfer);
gc = purple_account_get_connection(acct);
- pd = gc->proto_data;
+ 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 = xfer->who;
+ idb.user = remote_user;
purple_xfer_update_progress(xfer);
@@ -5019,8 +4992,9 @@ static void ft_outgoing_init(PurpleXfer *xfer) {
if(! fp) {
char *msg = g_strdup_printf(_("Error reading file %s: \n%s\n"),
filename, g_strerror(errno));
- purple_xfer_error(purple_xfer_get_type(xfer), acct, xfer->who, msg);
+ 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);
@@ -5032,16 +5006,17 @@ static void ft_outgoing_init(PurpleXfer *xfer) {
ft = mwFileTransfer_new(srvc, &idb, NULL, filename, filesize);
- purple_xfer_ref(xfer);
- mwFileTransfer_setClientData(ft, xfer, (GDestroyNotify) purple_xfer_unref);
- xfer->data = ft;
+ 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 = xfer->data;
+ struct mwFileTransfer *ft = purple_xfer_get_protocol_data(xfer);
DEBUG_INFO("ft_outgoing_cancel called\n");
@@ -5055,7 +5030,7 @@ static PurpleXfer *mw_prpl_new_xfer(PurpleConnection *gc, const char *who) {
acct = purple_connection_get_account(gc);
- xfer = purple_xfer_new(acct, PURPLE_XFER_SEND, who);
+ xfer = purple_xfer_new(acct, PURPLE_XFER_TYPE_SEND, who);
if (xfer)
{
purple_xfer_set_init_fnc(xfer, ft_outgoing_init);
@@ -5082,7 +5057,8 @@ static void mw_prpl_send_file(PurpleConnection *gc,
static PurplePluginProtocolInfo mw_prpl_info = {
- OPT_PROTO_IM_IMAGE,
+ sizeof(PurplePluginProtocolInfo),
+ 0,
NULL, /*< set in mw_plugin_init */
NULL, /*< set in mw_plugin_init */
NO_BUDDY_ICONS,
@@ -5117,12 +5093,10 @@ static PurplePluginProtocolInfo mw_prpl_info = {
mw_prpl_get_chat_name,
mw_prpl_chat_invite,
mw_prpl_chat_leave,
- mw_prpl_chat_whisper,
mw_prpl_chat_send,
mw_prpl_keepalive,
NULL,
NULL,
- NULL,
mw_prpl_alias_buddy,
mw_prpl_group_buddy,
mw_prpl_rename_group,
@@ -5147,8 +5121,6 @@ static PurplePluginProtocolInfo mw_prpl_info = {
NULL,
NULL,
NULL,
- sizeof(PurplePluginProtocolInfo),
- NULL,
NULL,
NULL,
NULL,
@@ -5158,7 +5130,7 @@ static PurplePluginProtocolInfo mw_prpl_info = {
NULL
};
-
+#if 0
static PurplePluginPrefFrame *
mw_plugin_get_plugin_pref_frame(PurplePlugin *plugin) {
PurplePluginPrefFrame *frame;
@@ -5173,7 +5145,7 @@ mw_plugin_get_plugin_pref_frame(PurplePlugin *plugin) {
pref = purple_plugin_pref_new_with_name(MW_PRPL_OPT_BLIST_ACTION);
purple_plugin_pref_set_label(pref, _("Buddy List Storage Mode"));
- purple_plugin_pref_set_type(pref, PURPLE_PLUGIN_PREF_CHOICE);
+ purple_plugin_pref_set_pref_type(pref, PURPLE_PLUGIN_PREF_CHOICE);
purple_plugin_pref_add_choice(pref, _("Local Buddy List Only"),
GINT_TO_POINTER(blist_choice_LOCAL));
purple_plugin_pref_add_choice(pref, _("Merge List from Server"),
@@ -5187,18 +5159,7 @@ mw_plugin_get_plugin_pref_frame(PurplePlugin *plugin) {
return frame;
}
-
-
-static PurplePluginUiInfo mw_plugin_ui_info = {
- mw_plugin_get_plugin_pref_frame,
- 0, /* page_num */
- NULL, /* frame */
- NULL,
- NULL,
- NULL,
- NULL
-};
-
+#endif
static void st_import_action_cb(PurpleConnection *gc, char *filename) {
struct mwSametimeList *l;
@@ -5240,7 +5201,7 @@ static void st_import_action(PurplePluginAction *act) {
purple_request_file(gc, title, NULL, FALSE,
G_CALLBACK(st_import_action_cb), NULL,
- account, NULL, NULL,
+ purple_request_cpar_from_connection(gc),
gc);
g_free(title);
@@ -5280,7 +5241,7 @@ static void st_export_action(PurplePluginAction *act) {
purple_request_file(gc, title, NULL, TRUE,
G_CALLBACK(st_export_action_cb), NULL,
- account, NULL, NULL,
+ purple_request_cpar_from_connection(gc),
gc);
g_free(title);
@@ -5323,7 +5284,7 @@ static void remote_group_done(struct mwPurplePluginData *pd,
acct = purple_connection_get_account(gc);
/* collision checking */
- group = purple_find_group(name);
+ group = purple_blist_find_group(name);
if(group) {
const char *msgA;
const char *msgB;
@@ -5333,7 +5294,8 @@ static void remote_group_done(struct mwPurplePluginData *pd,
msgB = _("A group named '%s' already exists in your buddy list.");
msg = g_strdup_printf(msgB, name);
- purple_notify_error(gc, _("Unable to add group"), msgA, msg);
+ purple_notify_error(gc, _("Unable to add group"), msgA, msg,
+ purple_request_cpar_from_connection(gc));
g_free(msg);
return;
@@ -5418,7 +5380,7 @@ static void remote_group_multi(struct mwResolveResult *result,
msgA, msg, fields,
_("Add Group"), G_CALLBACK(remote_group_multi_cb),
_("Cancel"), G_CALLBACK(remote_group_multi_cleanup),
- purple_connection_get_account(gc), result->name, NULL,
+ purple_request_cpar_from_connection(gc),
pd);
g_free(msg);
@@ -5463,7 +5425,8 @@ static void remote_group_resolved(struct mwServiceResolve *srvc,
" groups in your Sametime community.");
msg = g_strdup_printf(msgB, res->name);
- purple_notify_error(gc, _("Unable to add group"), msgA, msg);
+ purple_notify_error(gc, _("Unable to add group"), msgA, msg,
+ purple_request_cpar_from_connection(gc));
g_free(msg);
}
@@ -5477,7 +5440,7 @@ static void remote_group_action_cb(PurpleConnection *gc, const char *name) {
enum mwResolveFlag flags;
guint32 req;
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
srvc = pd->srvc_resolve;
query = g_list_prepend(NULL, (char *) name);
@@ -5508,7 +5471,7 @@ static void remote_group_action(PurplePluginAction *act) {
FALSE, FALSE, NULL,
_("Add"), G_CALLBACK(remote_group_action_cb),
_("Cancel"), NULL,
- purple_connection_get_account(gc), NULL, NULL,
+ purple_request_cpar_from_connection(gc),
gc);
}
@@ -5588,7 +5551,8 @@ static void search_resolved(struct mwServiceResolve *srvc,
" Sametime community.");
msg = g_strdup_printf(msgB, (res && res->name) ? NSTR(res->name) : "");
- purple_notify_error(gc, _("No Matches"), msgA, msg);
+ purple_notify_error(gc, _("No Matches"), msgA, msg,
+ purple_request_cpar_from_connection(gc));
g_free(msg);
}
@@ -5602,7 +5566,7 @@ static void search_action_cb(PurpleConnection *gc, const char *name) {
enum mwResolveFlag flags;
guint32 req;
- pd = gc->proto_data;
+ pd = purple_connection_get_protocol_data(gc);
srvc = pd->srvc_resolve;
query = g_list_prepend(NULL, (char *) name);
@@ -5633,7 +5597,7 @@ static void search_action(PurplePluginAction *act) {
FALSE, FALSE, NULL,
_("Search"), G_CALLBACK(search_action_cb),
_("Cancel"), NULL,
- purple_connection_get_account(gc), NULL, NULL,
+ purple_request_cpar_from_connection(gc),
gc);
}
@@ -5702,7 +5666,7 @@ static PurplePluginInfo mw_plugin_info =
NULL, /**< ui_info */
&mw_prpl_info, /**< extra_info */
- &mw_plugin_ui_info, /**< prefs_info */
+ NULL, /**< prefs_info */
mw_plugin_actions,
/* padding */
diff --git a/libpurple/protocols/silc/Makefile.am b/libpurple/protocols/silc/Makefile.am
index 91e5d7e2d6..05bae9b4bf 100644
--- a/libpurple/protocols/silc/Makefile.am
+++ b/libpurple/protocols/silc/Makefile.am
@@ -3,7 +3,7 @@ EXTRA_DIST = \
README \
TODO
-pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+pkgdir = @PURPLE_PLUGINDIR@
SILCSOURCES = \
buddy.c \
@@ -19,7 +19,7 @@ SILCSOURCES = \
AM_CFLAGS = $(st)
-libsilcpurple_la_LDFLAGS = -module -avoid-version
+libsilcpurple_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
if STATIC_SILC
@@ -27,17 +27,17 @@ st = -DPURPLE_STATIC_PRPL $(SILC_CFLAGS)
noinst_LTLIBRARIES = libsilcpurple.la
libsilcpurple_la_SOURCES = $(SILCSOURCES)
libsilcpurple_la_CFLAGS = $(AM_CFLAGS)
-libsilcpurple_la_LIBADD = $(SILC_LIBS)
else
st = $(SILC_CFLAGS)
pkg_LTLIBRARIES = libsilcpurple.la
libsilcpurple_la_SOURCES = $(SILCSOURCES)
-libsilcpurple_la_LIBADD = $(GLIB_LIBS) $(SILC_LIBS)
endif
+libsilcpurple_la_LIBADD = @PURPLE_LIBS@ $(SILC_LIBS)
+
AM_CPPFLAGS = \
-I$(top_srcdir)/libpurple \
-I$(top_builddir)/libpurple \
diff --git a/libpurple/protocols/silc/Makefile.mingw b/libpurple/protocols/silc/Makefile.mingw
index 15f2ee84cf..aa00bca89f 100644
--- a/libpurple/protocols/silc/Makefile.mingw
+++ b/libpurple/protocols/silc/Makefile.mingw
@@ -10,8 +10,6 @@ include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
DEFINES := $(subst -DWIN32_LEAN_AND_MEAN,,$(DEFINES))
TARGET = libsilc
-NEEDED_DLLS = $(SILC_TOOLKIT)/bin/libsilc-1-1-2.dll \
- $(SILC_TOOLKIT)/bin/libsilcclient-1-1-3.dll
TYPE = PLUGIN
# Static or Plugin...
@@ -59,6 +57,7 @@ OBJECTS = $(C_SRC:%.c=%.o)
##
LIBS = \
-lglib-2.0 \
+ -lgobject-2.0 \
-lws2_32 \
-lintl \
-lpurple \
@@ -76,7 +75,6 @@ all: $(TARGET).dll
install: all $(DLL_INSTALL_DIR) $(PURPLE_INSTALL_DIR)
cp $(TARGET).dll $(DLL_INSTALL_DIR)
- cp $(NEEDED_DLLS) $(PURPLE_INSTALL_DIR)
$(OBJECTS): $(PURPLE_CONFIG_H)
diff --git a/libpurple/protocols/silc/buddy.c b/libpurple/protocols/silc/buddy.c
index 4360a7e336..6bc1b43fa9 100644
--- a/libpurple/protocols/silc/buddy.c
+++ b/libpurple/protocols/silc/buddy.c
@@ -18,7 +18,10 @@
*/
#include "internal.h"
+#include "glibcompat.h"
+PURPLE_BEGIN_IGNORE_CAST_ALIGN
#include "silc.h"
+PURPLE_END_IGNORE_CAST_ALIGN
#include "silcclient.h"
#include "silcpurple.h"
#include "wb.h"
@@ -52,7 +55,8 @@ silcpurple_buddy_keyagr_resolved(SilcClient client,
g_snprintf(tmp, sizeof(tmp),
_("User %s is not present in the network"), r->nick);
purple_notify_error(gc, _("Key Agreement"),
- _("Cannot perform the key agreement"), tmp);
+ _("Cannot perform the key agreement"), tmp,
+ purple_request_cpar_from_connection(gc));
g_free(r->nick);
silc_free(r);
return;
@@ -72,7 +76,7 @@ silcpurple_buddy_keyagr_cb(SilcClient client,
void *context)
{
PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
if (!sg->conn)
return;
@@ -80,7 +84,7 @@ silcpurple_buddy_keyagr_cb(SilcClient client,
switch (status) {
case SILC_KEY_AGREEMENT_OK:
{
- PurpleConversation *convo;
+ PurpleIMConversation *im;
char tmp[128];
/* Set the private key for this client */
@@ -91,50 +95,56 @@ silcpurple_buddy_keyagr_cb(SilcClient client,
/* Open IM window */
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+ im = purple_conversations_find_im_with_account(
client_entry->nickname, sg->account);
- if (convo) {
+ if (im) {
/* we don't have windows in the core anymore...but we may want to
* provide some method for asking the UI to show the window
- purple_conv_window_show(purple_conversation_get_window(convo));
+ purple_conversation_window_show(purple_conversation_get_window(im));
*/
} else {
- convo = purple_conversation_new(PURPLE_CONV_TYPE_IM, sg->account,
+ im = purple_im_conversation_new(sg->account,
client_entry->nickname);
}
g_snprintf(tmp, sizeof(tmp), "%s [private key]", client_entry->nickname);
- purple_conversation_set_title(convo, tmp);
+ purple_conversation_set_title(PURPLE_CONVERSATION(im), tmp);
}
break;
case SILC_KEY_AGREEMENT_ERROR:
purple_notify_error(gc, _("Key Agreement"),
- _("Error occurred during key agreement"), NULL);
+ _("Error occurred during key agreement"), NULL,
+ purple_request_cpar_from_connection(gc));
break;
case SILC_KEY_AGREEMENT_FAILURE:
- purple_notify_error(gc, _("Key Agreement"), _("Key Agreement failed"), NULL);
+ purple_notify_error(gc, _("Key Agreement"),
+ _("Key Agreement failed"), NULL,
+ purple_request_cpar_from_connection(gc));
break;
case SILC_KEY_AGREEMENT_TIMEOUT:
purple_notify_error(gc, _("Key Agreement"),
- _("Timeout during key agreement"), NULL);
+ _("Timeout during key agreement"), NULL,
+ purple_request_cpar_from_connection(gc));
break;
case SILC_KEY_AGREEMENT_ABORTED:
purple_notify_error(gc, _("Key Agreement"),
- _("Key agreement was aborted"), NULL);
+ _("Key agreement was aborted"), NULL,
+ purple_request_cpar_from_connection(gc));
break;
case SILC_KEY_AGREEMENT_ALREADY_STARTED:
- purple_notify_error(gc, _("Key Agreement"),
- _("Key agreement is already started"), NULL);
+ purple_notify_error(gc, _("Key Agreement"), _("Key agreement is"
+ " already started"), NULL,
+ purple_request_cpar_from_connection(gc));
break;
case SILC_KEY_AGREEMENT_SELF_DENIED:
- purple_notify_error(gc, _("Key Agreement"),
- _("Key agreement cannot be started with yourself"),
- NULL);
+ purple_notify_error(gc, _("Key Agreement"), _("Key agreement "
+ "cannot be started with yourself"), NULL,
+ purple_request_cpar_from_connection(gc));
break;
default:
@@ -146,7 +156,7 @@ static void
silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name,
gboolean force_local)
{
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcDList clients;
SilcClientEntry client_entry;
SilcClientConnectionParams params;
@@ -254,7 +264,7 @@ silcpurple_buddy_keyagr_request_cb(SilcPurpleKeyAgrAsk a, gint id)
if (!client_entry) {
purple_notify_error(a->client->application, _("Key Agreement"),
_("The remote user is not present in the network any more"),
- NULL);
+ NULL, NULL);
goto out;
}
@@ -314,8 +324,8 @@ void silcpurple_buddy_keyagr_request(SilcClient client,
a->port = port;
purple_request_action(client->application, _("Key Agreement Request"), tmp,
- hostname ? tmp2 : NULL, 1, gc->account, client_entry->nickname,
- NULL, a, 2, _("Yes"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb),
+ hostname ? tmp2 : NULL, 1, purple_request_cpar_from_connection(gc),
+ a, 2, _("Yes"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb),
_("No"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb));
}
@@ -342,11 +352,11 @@ silcpurple_buddy_resetkey(PurpleBlistNode *node, gpointer data)
SilcPurple sg;
SilcDList clients;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
b = (PurpleBuddy *) node;
gc = purple_account_get_connection(purple_buddy_get_account(b));
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
/* Find client entry */
clients = silc_client_get_clients_local(sg->client, sg->conn,
@@ -385,7 +395,7 @@ silcpurple_buddy_privkey_cb(SilcPurplePrivkey p, const char *passphrase)
if (!client_entry) {
purple_notify_error(p->client->application, _("IM With Password"),
_("The remote user is not present in the network any more"),
- NULL);
+ NULL, NULL);
silc_free(p);
return;
}
@@ -414,7 +424,7 @@ silcpurple_buddy_privkey_resolved(SilcClient client,
_("User %s is not present in the network"),
(const char *)context);
purple_notify_error(client->application, _("IM With Password"),
- _("Cannot set IM key"), tmp);
+ _("Cannot set IM key"), tmp, NULL);
g_free(context);
return;
}
@@ -426,7 +436,7 @@ silcpurple_buddy_privkey_resolved(SilcClient client,
static void
silcpurple_buddy_privkey(PurpleConnection *gc, const char *name)
{
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcPurplePrivkey p;
SilcDList clients;
SilcClientEntry client_entry;
@@ -457,7 +467,7 @@ silcpurple_buddy_privkey(PurpleConnection *gc, const char *name)
_("Set IM Password"), NULL, FALSE, TRUE, NULL,
_("OK"), G_CALLBACK(silcpurple_buddy_privkey_cb),
_("Cancel"), G_CALLBACK(silcpurple_buddy_privkey_cb),
- gc->account, NULL, NULL, p);
+ purple_request_cpar_from_connection(gc), p);
silc_client_list_free(sg->client, sg->conn, clients);
}
@@ -468,7 +478,7 @@ silcpurple_buddy_privkey_menu(PurpleBlistNode *node, gpointer data)
PurpleBuddy *buddy;
PurpleConnection *gc;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *) node;
gc = purple_account_get_connection(purple_buddy_get_account(buddy));
@@ -499,7 +509,7 @@ silcpurple_buddy_getkey_cb(SilcClient client, SilcClientConnection conn,
if (status != SILC_STATUS_OK) {
purple_notify_error(g->client->application, _("Get Public Key"),
_("The remote user is not present in the network any more"),
- NULL);
+ NULL, NULL);
silc_free(g);
return FALSE;
}
@@ -510,7 +520,7 @@ silcpurple_buddy_getkey_cb(SilcClient client, SilcClientConnection conn,
if (!client_entry) {
purple_notify_error(g->client->application, _("Get Public Key"),
_("The remote user is not present in the network any more"),
- NULL);
+ NULL, NULL);
silc_free(g);
return FALSE;
}
@@ -542,7 +552,7 @@ silcpurple_buddy_getkey_resolved(SilcClient client,
_("User %s is not present in the network"),
(const char *)context);
purple_notify_error(client->application, _("Get Public Key"),
- _("Cannot fetch the public key"), tmp);
+ _("Cannot fetch the public key"), tmp, NULL);
g_free(context);
return;
}
@@ -554,7 +564,7 @@ silcpurple_buddy_getkey_resolved(SilcClient client,
static void
silcpurple_buddy_getkey(PurpleConnection *gc, const char *name)
{
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcClient client = sg->client;
SilcClientConnection conn = sg->conn;
SilcClientEntry client_entry;
@@ -597,7 +607,7 @@ silcpurple_buddy_getkey_menu(PurpleBlistNode *node, gpointer data)
PurpleBuddy *buddy;
PurpleConnection *gc;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *) node;
gc = purple_account_get_connection(purple_buddy_get_account(buddy));
@@ -614,17 +624,17 @@ silcpurple_buddy_showkey(PurpleBlistNode *node, gpointer data)
SilcPublicKey public_key;
const char *pkfile;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
b = (PurpleBuddy *) node;
gc = purple_account_get_connection(purple_buddy_get_account(b));
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
pkfile = purple_blist_node_get_string(node, "public-key");
if (!silc_pkcs_load_public_key(pkfile, &public_key)) {
- purple_notify_error(gc,
- _("Show Public Key"),
- _("Could not load public key"), NULL);
+ purple_notify_error(gc, _("Show Public Key"),
+ _("Could not load public key"), NULL,
+ purple_request_cpar_from_connection(gc));
return;
}
@@ -669,7 +679,7 @@ silcpurple_add_buddy_resolved(SilcClient client,
void silcpurple_get_info(PurpleConnection *gc, const char *who)
{
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcClient client = sg->client;
SilcClientConnection conn = sg->conn;
SilcClientEntry client_entry;
@@ -686,7 +696,7 @@ void silcpurple_get_info(PurpleConnection *gc, const char *who)
if (strlen(who) > 2 && who[0] == '*' && who[1] == '@')
nick = who + 2;
- b = purple_find_buddy(gc->account, nick);
+ b = purple_blist_find_buddy(purple_connection_get_account(gc), nick);
if (b) {
/* See if we have this buddy's public key. If we do use that
to search the details. */
@@ -704,7 +714,8 @@ void silcpurple_get_info(PurpleConnection *gc, const char *who)
g_snprintf(tmp, sizeof(tmp),
_("User %s is not present in the network"), purple_buddy_get_name(b));
purple_notify_error(gc, _("User Information"),
- _("Cannot get user information"), tmp);
+ _("Cannot get user information"), tmp,
+ purple_request_cpar_from_connection(gc));
return;
}
@@ -730,7 +741,8 @@ silcpurple_add_buddy_pk_no(SilcPurpleBuddyRes r)
purple_notify_error(r->client->application, _("Add Buddy"), tmp,
_("You cannot receive buddy notifications until you "
"import his/her public key. You can use the Get Public Key "
- "command to get the public key."));
+ "command to get the public key."),
+ purple_request_cpar_from_account(purple_buddy_get_account(r->b)));
purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL);
}
@@ -750,7 +762,7 @@ silcpurple_add_buddy_save(SilcBool success, void *context)
char filename[512], filename2[512], *fingerprint = NULL, *tmp;
SilcUInt32 len;
SilcHash hash;
- int i;
+ gsize i;
if (!success) {
/* The user did not trust the public key. */
@@ -1025,8 +1037,10 @@ silcpurple_add_buddy_ask_import(void *user_data, const char *name)
/* Load the public key */
if (!silc_pkcs_load_public_key(name, &r->public_key)) {
silcpurple_add_buddy_ask_pk_cb(r, 0);
- purple_notify_error(r->client->application,
- _("Add Buddy"), _("Could not load public key"), NULL);
+ purple_notify_error(r->client->application, _("Add Buddy"),
+ _("Could not load public key"), NULL,
+ purple_request_cpar_from_account(
+ purple_buddy_get_account(r->b)));
return;
}
@@ -1061,7 +1075,7 @@ silcpurple_add_buddy_ask_pk_cb(SilcPurpleBuddyRes r, gint id)
purple_request_file(r->client->application, _("Open..."), NULL, FALSE,
G_CALLBACK(silcpurple_add_buddy_ask_import),
G_CALLBACK(silcpurple_add_buddy_ask_pk_cancel),
- purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
+ purple_request_cpar_from_account(purple_buddy_get_account(r->b)), r);
}
@@ -1074,7 +1088,7 @@ silcpurple_add_buddy_ask_pk(SilcPurpleBuddyRes r)
purple_request_action(r->client->application, _("Add Buddy"), tmp,
_("To add the buddy you must import his/her public key. "
"Press Import to import a public key."), 0,
- purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r, 2,
+ purple_request_cpar_from_account(purple_buddy_get_account(r->b)), r, 2,
_("Cancel"), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb),
_("_Import..."), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb));
}
@@ -1191,7 +1205,7 @@ silcpurple_add_buddy_select(SilcPurpleBuddyRes r, SilcDList clients)
fields,
_("OK"), G_CALLBACK(silcpurple_add_buddy_select_cb),
_("Cancel"), G_CALLBACK(silcpurple_add_buddy_select_cancel),
- purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
+ purple_request_cpar_from_account(purple_buddy_get_account(r->b)), r);
}
static void
@@ -1270,6 +1284,8 @@ silcpurple_add_buddy_resolved(SilcClient client,
}
}
+ g_return_if_fail(client_entry != NULL);
+
/* The client was found. Now get its public key and verify
that before adding the buddy. */
memset(&userpk, 0, sizeof(userpk));
@@ -1332,7 +1348,7 @@ silcpurple_add_buddy_resolved(SilcClient client,
static void
silcpurple_add_buddy_i(PurpleConnection *gc, PurpleBuddy *b, gboolean init)
{
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcClient client = sg->client;
SilcClientConnection conn = sg->conn;
SilcPurpleBuddyRes r;
@@ -1390,7 +1406,7 @@ silcpurple_add_buddy_i(PurpleConnection *gc, PurpleBuddy *b, gboolean init)
silc_buffer_free(attrs);
}
-void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
+void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *message)
{
/* Don't add if the buddy is already on the list.
*
@@ -1407,7 +1423,7 @@ void silcpurple_send_buddylist(PurpleConnection *gc)
account = purple_connection_get_account(gc);
- for (buddies = purple_find_buddies(account, NULL); buddies;
+ for (buddies = purple_blist_find_buddies(account, NULL); buddies;
buddies = g_slist_delete_link(buddies, buddies))
{
PurpleBuddy *buddy = buddies->data;
@@ -1431,7 +1447,7 @@ void silcpurple_idle_set(PurpleConnection *gc, int idle)
const char *server;
int port;
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
if (sg == NULL)
return;
@@ -1461,7 +1477,7 @@ char *silcpurple_status_text(PurpleBuddy *b)
{
PurpleAccount *account = purple_buddy_get_account(b);
PurpleConnection *gc = purple_account_get_connection(account);
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcClient client = sg->client;
SilcClientConnection conn = sg->conn;
SilcClientID *client_id = purple_buddy_get_protocol_data(b);
@@ -1527,7 +1543,7 @@ void silcpurple_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gb
{
PurpleAccount *account = purple_buddy_get_account(b);
PurpleConnection *gc = purple_account_get_connection(account);
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcClient client = sg->client;
SilcClientConnection conn = sg->conn;
SilcClientID *client_id = purple_buddy_get_protocol_data(b);
@@ -1540,52 +1556,69 @@ void silcpurple_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gb
if (!client_entry)
return;
- purple_notify_user_info_add_pair(user_info, _("Nickname"),
- client_entry->nickname);
+ /* 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, _("Nickname"), client_entry->nickname);
g_snprintf(tmp, sizeof(tmp), "%s@%s", client_entry->username, client_entry->hostname);
- purple_notify_user_info_add_pair(user_info, _("Username"), tmp);
+ /* 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, _("Username"), tmp);
if (client_entry->mode) {
memset(tmp, 0, sizeof(tmp));
silcpurple_get_umode_string(client_entry->mode,
tmp, sizeof(tmp) - strlen(tmp));
- purple_notify_user_info_add_pair(user_info, _("User Modes"), tmp);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("User Modes"), tmp);
}
silcpurple_parse_attrs(client_entry->attrs, &moodstr, &statusstr, &contactstr, &langstr, &devicestr, &tzstr, &geostr);
if (statusstr) {
- purple_notify_user_info_add_pair(user_info, _("Message"), statusstr);
+ /* 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, _("Message"), statusstr);
g_free(statusstr);
}
if (full) {
if (moodstr) {
- purple_notify_user_info_add_pair(user_info, _("Mood"), moodstr);
+ /* 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, _("Mood"), moodstr);
g_free(moodstr);
}
if (contactstr) {
- purple_notify_user_info_add_pair(user_info, _("Preferred Contact"), contactstr);
+ /* 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, _("Preferred Contact"), contactstr);
g_free(contactstr);
}
if (langstr) {
- purple_notify_user_info_add_pair(user_info, _("Preferred Language"), langstr);
+ /* 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, _("Preferred Language"), langstr);
g_free(langstr);
}
if (devicestr) {
- purple_notify_user_info_add_pair(user_info, _("Device"), devicestr);
+ /* 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, _("Device"), devicestr);
g_free(devicestr);
}
if (tzstr) {
- purple_notify_user_info_add_pair(user_info, _("Timezone"), tzstr);
+ /* 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, _("Timezone"), tzstr);
g_free(tzstr);
}
if (geostr) {
- purple_notify_user_info_add_pair(user_info, _("Geolocation"), geostr);
+ /* 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, _("Geolocation"), geostr);
g_free(geostr);
}
}
@@ -1598,11 +1631,11 @@ silcpurple_buddy_kill(PurpleBlistNode *node, gpointer data)
PurpleConnection *gc;
SilcPurple sg;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
b = (PurpleBuddy *) node;
gc = purple_account_get_connection(purple_buddy_get_account(b));
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
/* Call KILL */
silc_client_command_call(sg->client, sg->conn, NULL, "KILL",
@@ -1626,7 +1659,7 @@ GList *silcpurple_buddy_menu(PurpleBuddy *buddy)
{
PurpleAccount *account = purple_buddy_get_account(buddy);
PurpleConnection *gc = purple_account_get_connection(account);
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcClientConnection conn = sg->conn;
const char *pkfile = NULL;
SilcClientEntry client_entry = NULL;
@@ -1690,14 +1723,13 @@ GList *silcpurple_buddy_menu(PurpleBuddy *buddy)
return m;
}
-void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleStoredImage *img)
+void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleImage *img)
{
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcClient client = sg->client;
SilcClientConnection conn = sg->conn;
SilcMime mime;
- char type[32];
- const char *t;
+ const gchar *type;
/* Remove */
if (!img) {
@@ -1706,24 +1738,23 @@ void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleStoredImage *img)
return;
}
+ type = purple_image_get_mimetype(img);
+ if (type == NULL)
+ return;
+ if (g_strcmp0(purple_image_get_extension(img), "ico") == 0)
+ return;
+
/* Add */
mime = silc_mime_alloc();
if (!mime)
return;
- t = purple_imgstore_get_extension(img);
- if (!t || !strcmp(t, "icon")) {
- silc_mime_free(mime);
- return;
- }
- if (!strcmp(t, "jpg"))
- t = "jpeg";
- g_snprintf(type, sizeof(type), "image/%s", t);
silc_mime_add_field(mime, "Content-Type", type);
- silc_mime_add_data(mime, purple_imgstore_get_data(img), purple_imgstore_get_size(img));
+ silc_mime_add_data(mime, purple_image_get_data(img),
+ purple_image_get_size(img));
- silc_client_attribute_add(client, conn,
- SILC_ATTRIBUTE_USER_ICON, mime, sizeof(*mime));
+ silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_USER_ICON,
+ mime, sizeof(*mime));
silc_mime_free(mime);
}
diff --git a/libpurple/protocols/silc/chat.c b/libpurple/protocols/silc/chat.c
index 91dda7ba9c..540474daa9 100644
--- a/libpurple/protocols/silc/chat.c
+++ b/libpurple/protocols/silc/chat.c
@@ -18,7 +18,9 @@
*/
#include "internal.h"
+PURPLE_BEGIN_IGNORE_CAST_ALIGN
#include "silc.h"
+PURPLE_END_IGNORE_CAST_ALIGN
#include "silcclient.h"
#include "silcpurple.h"
#include "wb.h"
@@ -80,7 +82,8 @@ silcpurple_chat_getinfo_res(SilcClient client,
g_snprintf(tmp, sizeof(tmp),
_("Channel %s does not exist in the network"), chname);
purple_notify_error(gc, _("Channel Information"),
- _("Cannot get channel information"), tmp);
+ _("Cannot get channel information"), tmp,
+ purple_request_cpar_from_connection(gc));
return;
}
@@ -91,7 +94,7 @@ silcpurple_chat_getinfo_res(SilcClient client,
static void
silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components)
{
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
const char *chname;
char tmp[256], *tmp2;
GString *s;
@@ -238,7 +241,7 @@ silcpurple_chat_chpk_add(void *user_data, const char *name)
silc_free(sgc);
purple_notify_error(client->application,
_("Add Channel Public Key"),
- _("Could not load public key"), NULL);
+ _("Could not load public key"), NULL, NULL);
return;
}
@@ -308,7 +311,7 @@ silcpurple_chat_chpk_cb(SilcPurpleChauth sgc, PurpleRequestFields *fields)
purple_request_file(sg->gc, _("Open Public Key..."), NULL, FALSE,
G_CALLBACK(silcpurple_chat_chpk_add),
G_CALLBACK(silcpurple_chat_chpk_cancel),
- purple_connection_get_account(sg->gc), NULL, NULL, sgc);
+ purple_request_cpar_from_connection(sg->gc), sgc);
return;
}
@@ -449,7 +452,7 @@ void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel,
_("Channel Authentication"), t, fields,
_("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
_("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
- purple_connection_get_account(sg->gc), NULL, NULL, sgc);
+ purple_request_cpar_from_connection(sg->gc), sgc);
if (channel_pubkeys)
silc_dlist_uninit(channel_pubkeys);
return;
@@ -486,7 +489,7 @@ void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel,
_("Channel Authentication"), t, fields,
_("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
_("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
- purple_connection_get_account(sg->gc), NULL, NULL, sgc);
+ purple_request_cpar_from_connection(sg->gc), sgc);
}
static void
@@ -496,11 +499,11 @@ silcpurple_chat_chauth(PurpleBlistNode *node, gpointer data)
PurpleConnection *gc;
SilcPurple sg;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+ g_return_if_fail(PURPLE_IS_CHAT(node));
chat = (PurpleChat *) node;
gc = purple_account_get_connection(purple_chat_get_account(chat));
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
@@ -583,11 +586,11 @@ silcpurple_chat_prv(PurpleBlistNode *node, gpointer data)
PurpleRequestField *f;
char tmp[512];
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+ g_return_if_fail(PURPLE_IS_CHAT(node));
chat = (PurpleChat *) node;
gc = purple_account_get_connection(purple_chat_get_account(chat));
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
p = silc_calloc(1, sizeof(*p));
if (!p)
@@ -620,7 +623,7 @@ silcpurple_chat_prv(PurpleBlistNode *node, gpointer data)
purple_request_fields(gc, _("Add Channel Private Group"), NULL, tmp, fields,
_("Add"), G_CALLBACK(silcpurple_chat_prv_add),
_("Cancel"), G_CALLBACK(silcpurple_chat_prv_cancel),
- purple_connection_get_account(gc), NULL, NULL, p);
+ purple_request_cpar_from_connection(gc), p);
}
@@ -633,11 +636,11 @@ silcpurple_chat_permanent_reset(PurpleBlistNode *node, gpointer data)
PurpleConnection *gc;
SilcPurple sg;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+ g_return_if_fail(PURPLE_IS_CHAT(node));
chat = (PurpleChat *) node;
gc = purple_account_get_connection(purple_chat_get_account(chat));
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
@@ -652,11 +655,11 @@ silcpurple_chat_permanent(PurpleBlistNode *node, gpointer data)
SilcPurple sg;
const char *channel;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+ g_return_if_fail(PURPLE_IS_CHAT(node));
chat = (PurpleChat *) node;
gc = purple_account_get_connection(purple_chat_get_account(chat));
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
if (!sg->conn)
return;
@@ -680,14 +683,14 @@ static void
silcpurple_chat_ulimit_cb(SilcPurpleChatInput s, const char *limit)
{
SilcChannelEntry channel;
- int ulimit = 0;
+ guint ulimit = 0;
channel = silc_client_get_channel(s->sg->client, s->sg->conn,
(char *)s->channel);
if (!channel)
return;
if (limit)
- ulimit = atoi(limit);
+ ulimit = strtoul(limit, NULL, 10);
if (!limit || !(*limit) || *limit == '0') {
if (limit && ulimit == channel->user_limit) {
@@ -729,11 +732,11 @@ silcpurple_chat_ulimit(PurpleBlistNode *node, gpointer data)
char *ch;
char tmp[32];
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+ g_return_if_fail(PURPLE_IS_CHAT(node));
chat = (PurpleChat *) node;
gc = purple_account_get_connection(purple_chat_get_account(chat));
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
if (!sg->conn)
return;
@@ -754,7 +757,7 @@ silcpurple_chat_ulimit(PurpleBlistNode *node, gpointer data)
tmp, FALSE, FALSE, NULL,
_("OK"), G_CALLBACK(silcpurple_chat_ulimit_cb),
_("Cancel"), G_CALLBACK(silcpurple_chat_ulimit_cb),
- purple_connection_get_account(gc), NULL, NULL, s);
+ purple_request_cpar_from_connection(gc), s);
}
static void
@@ -764,11 +767,11 @@ silcpurple_chat_resettopic(PurpleBlistNode *node, gpointer data)
PurpleConnection *gc;
SilcPurple sg;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+ g_return_if_fail(PURPLE_IS_CHAT(node));
chat = (PurpleChat *) node;
gc = purple_account_get_connection(purple_chat_get_account(chat));
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
@@ -782,11 +785,11 @@ silcpurple_chat_settopic(PurpleBlistNode *node, gpointer data)
PurpleConnection *gc;
SilcPurple sg;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+ g_return_if_fail(PURPLE_IS_CHAT(node));
chat = (PurpleChat *) node;
gc = purple_account_get_connection(purple_chat_get_account(chat));
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
@@ -800,11 +803,11 @@ silcpurple_chat_resetprivate(PurpleBlistNode *node, gpointer data)
PurpleConnection *gc;
SilcPurple sg;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+ g_return_if_fail(PURPLE_IS_CHAT(node));
chat = (PurpleChat *) node;
gc = purple_account_get_connection(purple_chat_get_account(chat));
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
@@ -818,11 +821,11 @@ silcpurple_chat_setprivate(PurpleBlistNode *node, gpointer data)
PurpleConnection *gc;
SilcPurple sg;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+ g_return_if_fail(PURPLE_IS_CHAT(node));
chat = (PurpleChat *) node;
gc = purple_account_get_connection(purple_chat_get_account(chat));
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
@@ -836,11 +839,11 @@ silcpurple_chat_resetsecret(PurpleBlistNode *node, gpointer data)
PurpleConnection *gc;
SilcPurple sg;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+ g_return_if_fail(PURPLE_IS_CHAT(node));
chat = (PurpleChat *) node;
gc = purple_account_get_connection(purple_chat_get_account(chat));
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
@@ -854,11 +857,11 @@ silcpurple_chat_setsecret(PurpleBlistNode *node, gpointer data)
PurpleConnection *gc;
SilcPurple sg;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+ g_return_if_fail(PURPLE_IS_CHAT(node));
chat = (PurpleChat *) node;
gc = purple_account_get_connection(purple_chat_get_account(chat));
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
@@ -882,7 +885,7 @@ GList *silcpurple_chat_menu(PurpleChat *chat)
{
GHashTable *components = purple_chat_get_components(chat);
PurpleConnection *gc = purple_account_get_connection(purple_chat_get_account(chat));
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcClientConnection conn = sg->conn;
const char *chname = NULL;
SilcChannelEntry channel = NULL;
@@ -1020,7 +1023,7 @@ char *silcpurple_get_chat_name(GHashTable *data)
void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data)
{
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcClient client = sg->client;
SilcClientConnection conn = sg->conn;
const char *channel, *passphrase, *parentch;
@@ -1053,7 +1056,8 @@ void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data)
_("You have to join the %s channel before you are "
"able to join the private group"), parentch);
purple_notify_error(gc, _("Join Private Group"),
- _("Cannot join private group"), tmp);
+ _("Cannot join private group"), tmp,
+ purple_request_cpar_from_connection(gc));
return;
}
@@ -1075,7 +1079,7 @@ void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data)
grp->channel = channel;
grp->key = key;
sg->grps = g_list_append(sg->grps, grp);
- serv_got_joined_chat(gc, grp->id, channel);
+ purple_serv_got_joined_chat(gc, grp->id, channel);
return;
}
@@ -1103,7 +1107,7 @@ void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data)
void silcpurple_chat_invite(PurpleConnection *gc, int id, const char *msg,
const char *name)
{
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcClient client = sg->client;
SilcClientConnection conn = sg->conn;
SilcHashTableList htl;
@@ -1120,7 +1124,7 @@ void silcpurple_chat_invite(PurpleConnection *gc, int id, const char *msg,
SilcPurplePrvgrp prv;
for (l = sg->grps; l; l = l->next)
- if (((SilcPurplePrvgrp)l->data)->id == id)
+ if (((SilcPurplePrvgrp)l->data)->id == (gulong)id)
break;
if (!l)
return;
@@ -1131,7 +1135,7 @@ void silcpurple_chat_invite(PurpleConnection *gc, int id, const char *msg,
/* Find channel by id */
silc_hash_table_list(conn->local_entry->channels, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- if (SILC_PTR_TO_32(chu->channel->context) == id ) {
+ if (SILC_PTR_TO_32(chu->channel->context) == (gulong)id ) {
found = TRUE;
break;
}
@@ -1148,7 +1152,7 @@ void silcpurple_chat_invite(PurpleConnection *gc, int id, const char *msg,
void silcpurple_chat_leave(PurpleConnection *gc, int id)
{
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcClient client = sg->client;
SilcClientConnection conn = sg->conn;
SilcHashTableList htl;
@@ -1165,7 +1169,7 @@ void silcpurple_chat_leave(PurpleConnection *gc, int id)
SilcChannelEntry channel;
for (l = sg->grps; l; l = l->next)
- if (((SilcPurplePrvgrp)l->data)->id == id)
+ if (((SilcPurplePrvgrp)l->data)->id == (gulong)id)
break;
if (!l)
return;
@@ -1178,14 +1182,14 @@ void silcpurple_chat_leave(PurpleConnection *gc, int id)
channel, prv->key);
silc_free(prv);
sg->grps = g_list_remove(sg->grps, prv);
- serv_got_chat_left(gc, id);
+ purple_serv_got_chat_left(gc, id);
return;
}
/* Find channel by id */
silc_hash_table_list(conn->local_entry->channels, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- if (SILC_PTR_TO_32(chu->channel->context) == id ) {
+ if (SILC_PTR_TO_32(chu->channel->context) == (gulong)id ) {
found = TRUE;
break;
}
@@ -1198,16 +1202,16 @@ void silcpurple_chat_leave(PurpleConnection *gc, int id)
silc_client_command_call(client, conn, NULL, "LEAVE",
chu->channel->channel_name, NULL);
- serv_got_chat_left(gc, id);
+ purple_serv_got_chat_left(gc, id);
/* Leave from private groups on this channel as well */
for (l = sg->grps; l; l = l->next)
- if (((SilcPurplePrvgrp)l->data)->chid == id) {
+ if (((SilcPurplePrvgrp)l->data)->chid == (gulong)id) {
prv = l->data;
silc_client_del_channel_private_key(client, conn,
chu->channel,
prv->key);
- serv_got_chat_left(gc, prv->id);
+ purple_serv_got_chat_left(gc, prv->id);
silc_free(prv);
sg->grps = g_list_remove(sg->grps, prv);
if (!sg->grps)
@@ -1215,10 +1219,9 @@ void silcpurple_chat_leave(PurpleConnection *gc, int id)
}
}
-int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg,
- PurpleMessageFlags msgflags)
+int silcpurple_chat_send(PurpleConnection *gc, int id, PurpleMessage *pmsg)
{
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcClient client = sg->client;
SilcClientConnection conn = sg->conn;
SilcHashTableList htl;
@@ -1227,10 +1230,12 @@ int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg,
SilcChannelPrivateKey key = NULL;
SilcMessageFlags flags;
int ret = 0;
+ const gchar *msg = purple_message_get_contents(pmsg);
char *msg2, *tmp;
gboolean found = FALSE;
gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE);
SilcDList list;
+ PurpleMessageFlags msgflags = purple_message_get_flags(pmsg);
if (!msg || !conn)
return 0;
@@ -1248,9 +1253,11 @@ int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg,
}
flags |= SILC_MESSAGE_FLAG_ACTION;
} else if (strlen(msg) > 1 && msg[0] == '/') {
- if (!silc_client_command_call(client, conn, msg + 1))
- purple_notify_error(gc, _("Call Command"), _("Cannot call command"),
- _("Unknown command"));
+ if (!silc_client_command_call(client, conn, msg + 1)) {
+ purple_notify_error(gc, _("Call Command"),
+ _("Cannot call command"), _("Unknown command"),
+ purple_request_cpar_from_connection(gc));
+ }
g_free(tmp);
return 0;
}
@@ -1266,7 +1273,7 @@ int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg,
SilcPurplePrvgrp prv;
for (l = sg->grps; l; l = l->next)
- if (((SilcPurplePrvgrp)l->data)->id == id)
+ if (((SilcPurplePrvgrp)l->data)->id == (gulong)id)
break;
if (!l) {
g_free(tmp);
@@ -1286,7 +1293,7 @@ int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg,
/* Find channel by id */
silc_hash_table_list(conn->local_entry->channels, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- if (SILC_PTR_TO_32(chu->channel->context) == id ) {
+ if (SILC_PTR_TO_32(chu->channel->context) == (gulong)id ) {
found = TRUE;
break;
}
@@ -1319,7 +1326,7 @@ int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg,
g_free(tmp);
if (ret)
- serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), msgflags, msg, time(NULL));
+ purple_serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), msgflags, msg, time(NULL));
return ret;
}
}
@@ -1330,7 +1337,7 @@ int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg,
(unsigned char *)msg2,
strlen(msg2));
if (ret) {
- serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), msgflags, msg,
+ purple_serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), msgflags, msg,
time(NULL));
}
g_free(tmp);
@@ -1340,7 +1347,7 @@ int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg,
void silcpurple_chat_set_topic(PurpleConnection *gc, int id, const char *topic)
{
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcClient client = sg->client;
SilcClientConnection conn = sg->conn;
SilcHashTableList htl;
@@ -1357,7 +1364,7 @@ void silcpurple_chat_set_topic(PurpleConnection *gc, int id, const char *topic)
SilcPurplePrvgrp prv;
for (l = sg->grps; l; l = l->next)
- if (((SilcPurplePrvgrp)l->data)->id == id)
+ if (((SilcPurplePrvgrp)l->data)->id == (gulong)id)
break;
if (!l)
return;
@@ -1368,7 +1375,7 @@ void silcpurple_chat_set_topic(PurpleConnection *gc, int id, const char *topic)
/* Find channel by id */
silc_hash_table_list(conn->local_entry->channels, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- if (SILC_PTR_TO_32(chu->channel->context) == id ) {
+ if (SILC_PTR_TO_32(chu->channel->context) == (gulong)id ) {
found = TRUE;
break;
}
@@ -1384,7 +1391,7 @@ void silcpurple_chat_set_topic(PurpleConnection *gc, int id, const char *topic)
PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc)
{
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcClient client = sg->client;
SilcClientConnection conn = sg->conn;
GList *fields = NULL;
@@ -1394,7 +1401,7 @@ PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc)
return NULL;
if (sg->roomlist)
- purple_roomlist_unref(sg->roomlist);
+ g_object_unref(sg->roomlist);
sg->roomlist_cancelled = FALSE;
@@ -1419,16 +1426,17 @@ PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc)
void silcpurple_roomlist_cancel(PurpleRoomlist *list)
{
- PurpleConnection *gc = purple_account_get_connection(list->account);
+ PurpleAccount *account = purple_roomlist_get_account(list);
+ PurpleConnection *gc = purple_account_get_connection(account);
SilcPurple sg;
if (!gc)
return;
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
purple_roomlist_set_in_progress(list, FALSE);
if (sg->roomlist == list) {
- purple_roomlist_unref(sg->roomlist);
+ g_object_unref(sg->roomlist);
sg->roomlist = NULL;
sg->roomlist_cancelled = TRUE;
}
diff --git a/libpurple/protocols/silc/ft.c b/libpurple/protocols/silc/ft.c
index 2bd0a7fb64..de837f2ed8 100644
--- a/libpurple/protocols/silc/ft.c
+++ b/libpurple/protocols/silc/ft.c
@@ -18,7 +18,9 @@
*/
#include "internal.h"
+PURPLE_BEGIN_IGNORE_CAST_ALIGN
#include "silc.h"
+PURPLE_END_IGNORE_CAST_ALIGN
#include "silcclient.h"
#include "silcpurple.h"
@@ -76,17 +78,17 @@ silcpurple_ftp_monitor(SilcClient client,
if (status == SILC_CLIENT_FILE_MONITOR_CLOSED) {
/* All started sessions terminate here */
- xfer->xfer->data = NULL;
- purple_xfer_unref(xfer->xfer);
+ purple_xfer_set_protocol_data(xfer->xfer, NULL);
+ g_object_unref(xfer->xfer);
silc_free(xfer);
return;
}
if (status == SILC_CLIENT_FILE_MONITOR_DISCONNECT) {
- purple_notify_error(gc, _("Secure File Transfer"),
- _("Error during file transfer"),
- _("Remote disconnected"));
- xfer->xfer->status = PURPLE_XFER_STATUS_CANCEL_REMOTE;
+ purple_notify_error(gc, _("Secure File Transfer"), _("Error "
+ "during file transfer"), _("Remote disconnected"),
+ purple_request_cpar_from_connection(gc));
+ purple_xfer_set_status(xfer->xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE);
purple_xfer_update_progress(xfer->xfer);
silc_client_file_close(client, conn, session_id);
return;
@@ -100,29 +102,35 @@ silcpurple_ftp_monitor(SilcClient client,
g_snprintf(tmp, sizeof(tmp), "No such file %s",
filepath ? filepath : "[N/A]");
purple_notify_error(gc, _("Secure File Transfer"),
- _("Error during file transfer"), tmp);
+ _("Error during file transfer"), tmp,
+ purple_request_cpar_from_connection(gc));
} else if (error == SILC_CLIENT_FILE_PERMISSION_DENIED) {
purple_notify_error(gc, _("Secure File Transfer"),
- _("Error during file transfer"),
- _("Permission denied"));
+ _("Error during file transfer"),
+ _("Permission denied"),
+ purple_request_cpar_from_connection(gc));
} else if (error == SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED) {
purple_notify_error(gc, _("Secure File Transfer"),
- _("Error during file transfer"),
- _("Key agreement failed"));
+ _("Error during file transfer"),
+ _("Key agreement failed"),
+ purple_request_cpar_from_connection(gc));
} else if (error == SILC_CLIENT_FILE_TIMEOUT) {
purple_notify_error(gc, _("Secure File Transfer"),
- _("Error during file transfer"),
- _("Connection timed out"));
+ _("Error during file transfer"),
+ _("Connection timed out"),
+ purple_request_cpar_from_connection(gc));
} else if (error == SILC_CLIENT_FILE_CONNECT_FAILED) {
purple_notify_error(gc, _("Secure File Transfer"),
- _("Error during file transfer"),
- _("Creating connection failed"));
+ _("Error during file transfer"),
+ _("Creating connection failed"),
+ purple_request_cpar_from_connection(gc));
} else if (error == SILC_CLIENT_FILE_UNKNOWN_SESSION) {
purple_notify_error(gc, _("Secure File Transfer"),
- _("Error during file transfer"),
- _("File transfer session does not exist"));
+ _("Error during file transfer"),
+ _("File transfer session does not exist"),
+ purple_request_cpar_from_connection(gc));
}
- xfer->xfer->status = PURPLE_XFER_STATUS_CANCEL_REMOTE;
+ purple_xfer_set_status(xfer->xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE);
purple_xfer_update_progress(xfer->xfer);
silc_client_file_close(client, conn, session_id);
return;
@@ -132,8 +140,7 @@ silcpurple_ftp_monitor(SilcClient client,
if (!offset && filesize)
purple_xfer_set_size(xfer->xfer, filesize);
if (offset && filesize) {
- xfer->xfer->bytes_sent = offset;
- xfer->xfer->bytes_remaining = filesize - offset;
+ purple_xfer_set_bytes_sent(xfer->xfer, offset);
}
purple_xfer_update_progress(xfer->xfer);
@@ -150,12 +157,12 @@ silcpurple_ftp_monitor(SilcClient client,
static void
silcpurple_ftp_cancel(PurpleXfer *x)
{
- SilcPurpleXfer xfer = x->data;
+ SilcPurpleXfer xfer = purple_xfer_get_protocol_data(x);
if (!xfer)
return;
- xfer->xfer->status = PURPLE_XFER_STATUS_CANCEL_LOCAL;
+ purple_xfer_set_status(xfer->xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL);
purple_xfer_update_progress(xfer->xfer);
silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
}
@@ -163,7 +170,7 @@ silcpurple_ftp_cancel(PurpleXfer *x)
static void
silcpurple_ftp_ask_name_cancel(PurpleXfer *x)
{
- SilcPurpleXfer xfer = x->data;
+ SilcPurpleXfer xfer = purple_xfer_get_protocol_data(x);
if (!xfer)
return;
@@ -176,7 +183,7 @@ silcpurple_ftp_ask_name_cancel(PurpleXfer *x)
static void
silcpurple_ftp_ask_name_ok(PurpleXfer *x)
{
- SilcPurpleXfer xfer = x->data;
+ SilcPurpleXfer xfer = purple_xfer_get_protocol_data(x);
const char *name;
if (!xfer)
@@ -212,7 +219,7 @@ silcpurple_ftp_ask_name(SilcClient client,
static void
silcpurple_ftp_request_result(PurpleXfer *x)
{
- SilcPurpleXfer xfer = x->data;
+ SilcPurpleXfer xfer = purple_xfer_get_protocol_data(x);
SilcClientFileError status;
PurpleConnection *gc = xfer->sg->gc;
SilcClientConnectionParams params;
@@ -269,28 +276,31 @@ silcpurple_ftp_request_result(PurpleXfer *x)
case SILC_CLIENT_FILE_UNKNOWN_SESSION:
purple_notify_error(gc, _("Secure File Transfer"),
- _("No file transfer session active"), NULL);
+ _("No file transfer session active"), NULL,
+ purple_request_cpar_from_connection(gc));
break;
case SILC_CLIENT_FILE_ALREADY_STARTED:
purple_notify_error(gc, _("Secure File Transfer"),
- _("File transfer already started"), NULL);
+ _("File transfer already started"), NULL,
+ purple_request_cpar_from_connection(gc));
break;
case SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED:
purple_notify_error(gc, _("Secure File Transfer"),
- _("Could not perform key agreement for file transfer"),
- NULL);
+ _("Could not perform key agreement for file transfer"),
+ NULL, purple_request_cpar_from_connection(gc));
break;
default:
purple_notify_error(gc, _("Secure File Transfer"),
- _("Could not start the file transfer"), NULL);
+ _("Could not start the file transfer"), NULL,
+ purple_request_cpar_from_connection(gc));
break;
}
/* Error */
- purple_xfer_unref(xfer->xfer);
+ g_object_unref(xfer->xfer);
g_free(xfer->hostname);
silc_free(xfer);
silc_free(local_ip);
@@ -308,7 +318,7 @@ void silcpurple_ftp_request(SilcClient client, SilcClientConnection conn,
const char *hostname, SilcUInt16 port)
{
PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcPurpleXfer xfer;
xfer = silc_calloc(1, sizeof(*xfer));
@@ -322,7 +332,7 @@ void silcpurple_ftp_request(SilcClient client, SilcClientConnection conn,
xfer->session_id = session_id;
xfer->hostname = g_strdup(hostname);
xfer->port = port;
- xfer->xfer = purple_xfer_new(xfer->sg->account, PURPLE_XFER_RECEIVE,
+ xfer->xfer = purple_xfer_new(xfer->sg->account, PURPLE_XFER_TYPE_RECEIVE,
xfer->client_entry->nickname);
if (!xfer->xfer) {
silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
@@ -333,9 +343,8 @@ void silcpurple_ftp_request(SilcClient client, SilcClientConnection conn,
purple_xfer_set_init_fnc(xfer->xfer, silcpurple_ftp_request_result);
purple_xfer_set_request_denied_fnc(xfer->xfer, silcpurple_ftp_request_denied);
purple_xfer_set_cancel_recv_fnc(xfer->xfer, silcpurple_ftp_cancel);
- xfer->xfer->remote_ip = g_strdup(hostname);
- xfer->xfer->remote_port = port;
- xfer->xfer->data = xfer;
+ purple_xfer_start(xfer->xfer, -1, hostname, port);
+ purple_xfer_set_protocol_data(xfer->xfer, xfer);
/* File transfer request */
purple_xfer_request(xfer->xfer);
@@ -344,7 +353,7 @@ void silcpurple_ftp_request(SilcClient client, SilcClientConnection conn,
static void
silcpurple_ftp_send_cancel(PurpleXfer *x)
{
- SilcPurpleXfer xfer = x->data;
+ SilcPurpleXfer xfer = purple_xfer_get_protocol_data(x);
if (!xfer)
return;
@@ -356,7 +365,7 @@ silcpurple_ftp_send_cancel(PurpleXfer *x)
static void
silcpurple_ftp_send(PurpleXfer *x)
{
- SilcPurpleXfer xfer = x->data;
+ SilcPurpleXfer xfer = purple_xfer_get_protocol_data(x);
const char *name;
char *local_ip = NULL, *remote_ip = NULL;
gboolean local = TRUE;
@@ -422,7 +431,8 @@ silcpurple_ftp_send_file_resolved(SilcClient client,
_("User %s is not present in the network"),
(const char *)context);
purple_notify_error(gc, _("Secure File Transfer"),
- _("Cannot send file"), tmp);
+ _("Cannot send file"), tmp,
+ purple_request_cpar_from_connection(gc));
g_free(context);
return;
}
@@ -433,7 +443,7 @@ silcpurple_ftp_send_file_resolved(SilcClient client,
PurpleXfer *silcpurple_ftp_new_xfer(PurpleConnection *gc, const char *name)
{
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcClient client = sg->client;
SilcClientConnection conn = sg->conn;
SilcDList clients;
@@ -456,7 +466,7 @@ PurpleXfer *silcpurple_ftp_new_xfer(PurpleConnection *gc, const char *name)
xfer->sg = sg;
xfer->client_entry = silc_dlist_get(clients);
- xfer->xfer = purple_xfer_new(xfer->sg->account, PURPLE_XFER_SEND,
+ xfer->xfer = purple_xfer_new(xfer->sg->account, PURPLE_XFER_TYPE_SEND,
xfer->client_entry->nickname);
if (!xfer->xfer) {
silc_free(xfer);
@@ -465,7 +475,7 @@ PurpleXfer *silcpurple_ftp_new_xfer(PurpleConnection *gc, const char *name)
purple_xfer_set_init_fnc(xfer->xfer, silcpurple_ftp_send);
purple_xfer_set_request_denied_fnc(xfer->xfer, silcpurple_ftp_request_denied);
purple_xfer_set_cancel_send_fnc(xfer->xfer, silcpurple_ftp_send_cancel);
- xfer->xfer->data = xfer;
+ purple_xfer_set_protocol_data(xfer->xfer, xfer);
silc_free(clients);
diff --git a/libpurple/protocols/silc/ops.c b/libpurple/protocols/silc/ops.c
index c328ffa754..6646a9bc52 100644
--- a/libpurple/protocols/silc/ops.c
+++ b/libpurple/protocols/silc/ops.c
@@ -18,10 +18,12 @@
*/
#include "internal.h"
+#include "image-store.h"
+PURPLE_BEGIN_IGNORE_CAST_ALIGN
#include "silc.h"
+PURPLE_END_IGNORE_CAST_ALIGN
#include "silcclient.h"
#include "silcpurple.h"
-#include "imgstore.h"
#include "wb.h"
static void
@@ -72,9 +74,9 @@ void silc_say(SilcClient client, SilcClientConnection conn,
gc = client->application;
if (gc != NULL)
- purple_connection_error_reason(gc, reason, tmp);
+ purple_connection_error(gc, reason, tmp);
else
- purple_notify_error(NULL, _("Error"), _("Error occurred"), tmp);
+ purple_notify_error(NULL, _("Error"), _("Error occurred"), tmp, NULL);
}
/* Processes incoming MIME message. Can be private message or channel
@@ -88,12 +90,12 @@ silcpurple_mime_message(SilcClient client, SilcClientConnection conn,
gboolean recursive)
{
PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
const char *type;
const unsigned char *data;
SilcUInt32 data_len;
PurpleMessageFlags cflags = 0;
- PurpleConversation *convo = NULL;
+ PurpleChatConversation *chat = NULL;
SilcBool ret = FALSE;
if (!mime)
@@ -180,14 +182,12 @@ silcpurple_mime_message(SilcClient client, SilcClientConnection conn,
}
/* Image */
- if (strstr(type, "image/png") ||
- strstr(type, "image/jpeg") ||
- strstr(type, "image/gif") ||
- strstr(type, "image/tiff")) {
+ if (purple_str_has_prefix(type, "image/")) {
char tmp[32];
- int imgid;
+ PurpleImage *img;
+ guint img_id;
- /* Get channel convo (if message is for channel) */
+ /* Get channel chat (if message is for channel) */
if (key && channel) {
GList *l;
SilcPurplePrvgrp prv;
@@ -195,34 +195,37 @@ silcpurple_mime_message(SilcClient client, SilcClientConnection conn,
for (l = sg->grps; l; l = l->next)
if (((SilcPurplePrvgrp)l->data)->key == key) {
prv = l->data;
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chat = purple_conversations_find_chat_with_account(
prv->channel, sg->account);
break;
}
}
- if (channel && !convo)
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ if (channel && !chat)
+ chat = purple_conversations_find_chat_with_account(
channel->channel_name, sg->account);
- if (channel && !convo)
+ if (channel && !chat)
goto out;
- imgid = purple_imgstore_add_with_id(g_memdup(data, data_len), data_len, "");
- if (imgid) {
- cflags |= PURPLE_MESSAGE_IMAGES | PURPLE_MESSAGE_RECV;
- g_snprintf(tmp, sizeof(tmp), "<IMG ID=\"%d\">", imgid);
+ img = purple_image_new_from_data(g_memdup(data, data_len), data_len);
+ if (!img)
+ goto out;
+ img_id = purple_image_store_add_temporary(img);
+ if (!img_id)
+ goto out;
- if (channel)
- serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
- sender->nickname, cflags,
- tmp, time(NULL));
- else
- serv_got_im(gc, sender->nickname,
- tmp, cflags, time(NULL));
+ cflags |= PURPLE_MESSAGE_IMAGES | PURPLE_MESSAGE_RECV;
+ g_snprintf(tmp, sizeof(tmp), "<img src=\""
+ PURPLE_IMAGE_STORE_PROTOCOL "%u\">", img_id);
- purple_imgstore_unref_by_id(imgid);
- cflags = 0;
- ret = TRUE;
+ if (channel) {
+ purple_serv_got_chat_in(gc,
+ purple_chat_conversation_get_id(chat),
+ sender->nickname, cflags, tmp, time(NULL));
+ } else {
+ purple_serv_got_im(gc, sender->nickname,
+ tmp, cflags, time(NULL));
}
+
goto out;
}
@@ -260,8 +263,8 @@ silc_channel_message(SilcClient client, SilcClientConnection conn,
SilcUInt32 message_len)
{
PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
- PurpleConversation *convo = NULL;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
+ PurpleChatConversation *chat = NULL;
char *msg, *tmp;
if (!message)
@@ -274,15 +277,15 @@ silc_channel_message(SilcClient client, SilcClientConnection conn,
for (l = sg->grps; l; l = l->next)
if (((SilcPurplePrvgrp)l->data)->key == key) {
prv = l->data;
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chat = purple_conversations_find_chat_with_account(
prv->channel, sg->account);
break;
}
}
- if (!convo)
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ if (!chat)
+ chat = purple_conversations_find_chat_with_account(
channel->channel_name, sg->account);
- if (!convo)
+ if (!chat)
return;
if (flags & SILC_MESSAGE_FLAG_SIGNED &&
@@ -307,8 +310,8 @@ silc_channel_message(SilcClient client, SilcClientConnection conn,
tmp = g_markup_escape_text(msg, -1);
/* Send to Purple */
- serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
- sender->nickname, 0, tmp, time(NULL));
+ purple_serv_got_chat_in(gc, purple_chat_conversation_get_id(chat),
+ sender->nickname, PURPLE_MESSAGE_RECV, tmp, time(NULL));
g_free(tmp);
g_free(msg);
return;
@@ -321,8 +324,8 @@ silc_channel_message(SilcClient client, SilcClientConnection conn,
return;
/* Send to Purple */
- purple_conversation_write(convo, NULL, (const char *)msg,
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(chat), msg, 0);
g_free(msg);
return;
}
@@ -336,8 +339,8 @@ silc_channel_message(SilcClient client, SilcClientConnection conn,
}
tmp = g_markup_escape_text(msg, -1);
/* Send to Purple */
- serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
- sender->nickname, 0, tmp, time(NULL));
+ purple_serv_got_chat_in(gc, purple_chat_conversation_get_id(chat),
+ sender->nickname, PURPLE_MESSAGE_RECV, tmp, time(NULL));
g_free(salvaged);
g_free(tmp);
}
@@ -357,7 +360,7 @@ silc_private_message(SilcClient client, SilcClientConnection conn,
SilcUInt32 message_len)
{
PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
PurpleConversation *convo;
char *msg, *tmp;
@@ -365,7 +368,7 @@ silc_private_message(SilcClient client, SilcClientConnection conn,
return;
/* XXX - Should this be PURPLE_CONV_TYPE_IM? */
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
+ convo = purple_conversations_find_with_account(
sender->nickname, sg->account);
if (flags & SILC_MESSAGE_FLAG_SIGNED &&
@@ -390,7 +393,7 @@ silc_private_message(SilcClient client, SilcClientConnection conn,
/* Send to Purple */
tmp = g_markup_escape_text(msg, -1);
- serv_got_im(gc, sender->nickname, tmp, 0, time(NULL));
+ purple_serv_got_im(gc, sender->nickname, tmp, 0, time(NULL));
g_free(msg);
g_free(tmp);
return;
@@ -403,8 +406,7 @@ silc_private_message(SilcClient client, SilcClientConnection conn,
return;
/* Send to Purple */
- purple_conversation_write(convo, NULL, (const char *)msg,
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(convo, msg, 0);
g_free(msg);
return;
}
@@ -418,7 +420,7 @@ silc_private_message(SilcClient client, SilcClientConnection conn,
}
tmp = g_markup_escape_text(msg, -1);
/* Send to Purple */
- serv_got_im(gc, sender->nickname, tmp, 0, time(NULL));
+ purple_serv_got_im(gc, sender->nickname, tmp, 0, time(NULL));
g_free(salvaged);
g_free(tmp);
}
@@ -440,9 +442,9 @@ silc_notify(SilcClient client, SilcClientConnection conn,
{
va_list va;
PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
PurpleAccount *account = purple_connection_get_account(gc);
- PurpleConversation *convo;
+ PurpleChatConversation *chat;
SilcClientEntry client_entry, client_entry2;
SilcChannelEntry channel;
SilcServerEntry server_entry;
@@ -455,7 +457,6 @@ silc_notify(SilcClient client, SilcClientConnection conn,
SilcNotifyType notify;
PurpleBuddy *b;
SilcDList list;
- int i;
va_start(va, type);
memset(buf, 0, sizeof(buf));
@@ -474,7 +475,7 @@ silc_notify(SilcClient client, SilcClientConnection conn,
components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
g_hash_table_insert(components, g_strdup("channel"), g_strdup(name));
- serv_got_chat_invite(gc, name, client_entry->nickname, NULL, components);
+ purple_serv_got_chat_invite(gc, name, client_entry->nickname, NULL, components);
}
break;
@@ -486,16 +487,16 @@ silc_notify(SilcClient client, SilcClientConnection conn,
if (client_entry == conn->local_entry)
break;
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chat = purple_conversations_find_chat_with_account(
channel->channel_name, sg->account);
- if (!convo)
+ if (!chat)
break;
/* Join user to channel */
g_snprintf(buf, sizeof(buf), "%s@%s",
client_entry->username, client_entry->hostname);
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(convo),
- client_entry->nickname, buf, PURPLE_CBFLAGS_NONE, TRUE);
+ purple_chat_conversation_add_user(chat,
+ client_entry->nickname, buf, PURPLE_CHAT_USER_NONE, TRUE);
break;
@@ -503,13 +504,13 @@ silc_notify(SilcClient client, SilcClientConnection conn,
client_entry = va_arg(va, SilcClientEntry);
channel = va_arg(va, SilcChannelEntry);
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chat = purple_conversations_find_chat_with_account(
channel->channel_name, sg->account);
- if (!convo)
+ if (!chat)
break;
/* Remove user from channel */
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
+ purple_chat_conversation_remove_user(chat,
client_entry->nickname, NULL);
break;
@@ -521,11 +522,11 @@ silc_notify(SilcClient client, SilcClientConnection conn,
/* Remove from all channels */
silc_hash_table_list(client_entry->channels, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chat = purple_conversations_find_chat_with_account(
chu->channel->channel_name, sg->account);
- if (!convo)
+ if (!chat)
continue;
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
+ purple_chat_conversation_remove_user(chat,
client_entry->nickname,
tmp);
}
@@ -541,9 +542,9 @@ silc_notify(SilcClient client, SilcClientConnection conn,
tmp = va_arg(va, char *);
channel = va_arg(va, SilcChannelEntry);
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chat = purple_conversations_find_chat_with_account(
channel->channel_name, sg->account);
- if (!convo)
+ if (!chat)
break;
if (!tmp)
@@ -558,30 +559,30 @@ silc_notify(SilcClient client, SilcClientConnection conn,
g_snprintf(buf, sizeof(buf),
_("%s has changed the topic of <I>%s</I> to: %s"),
client_entry->nickname, channel->channel_name, tmp2);
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname,
- buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo),
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(chat), buf, 0);
+ purple_chat_conversation_set_topic(chat,
client_entry->nickname, tmp);
} else if (idtype == SILC_ID_SERVER) {
server_entry = (SilcServerEntry)entry;
g_snprintf(buf, sizeof(buf),
_("%s has changed the topic of <I>%s</I> to: %s"),
server_entry->server_name, channel->channel_name, tmp2);
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), server_entry->server_name,
- buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo),
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(chat), buf, 0);
+ purple_chat_conversation_set_topic(chat,
server_entry->server_name, tmp);
} else if (idtype == SILC_ID_CHANNEL) {
channel = (SilcChannelEntry)entry;
g_snprintf(buf, sizeof(buf),
_("%s has changed the topic of <I>%s</I> to: %s"),
channel->channel_name, channel->channel_name, tmp2);
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
- buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo),
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(chat), buf, 0);
+ purple_chat_conversation_set_topic(chat,
channel->channel_name, tmp);
} else {
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, tmp);
+ purple_chat_conversation_set_topic(chat, NULL, tmp);
}
g_free(tmp2);
@@ -600,12 +601,12 @@ silc_notify(SilcClient client, SilcClientConnection conn,
/* Change nick on all channels */
silc_hash_table_list(client_entry->channels, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chat = purple_conversations_find_chat_with_account(
chu->channel->channel_name, sg->account);
- if (!convo)
+ if (!chat)
continue;
- if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(convo), client_entry->nickname))
- purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo),
+ if (purple_chat_conversation_has_user(chat, client_entry->nickname))
+ purple_chat_conversation_rename_user(chat,
tmp, name);
}
silc_hash_table_list_reset(&htl);
@@ -623,9 +624,9 @@ silc_notify(SilcClient client, SilcClientConnection conn,
(void)va_arg(va, SilcDList);
channel = va_arg(va, SilcChannelEntry);
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chat = purple_conversations_find_chat_with_account(
channel->channel_name, sg->account);
- if (!convo)
+ if (!chat)
break;
if (idtype == SILC_ID_CLIENT)
@@ -647,22 +648,21 @@ silc_notify(SilcClient client, SilcClientConnection conn,
_("<I>%s</I> removed all channel <I>%s</I> modes"), name,
channel->channel_name);
}
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
- buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(chat), buf, 0);
break;
case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
{
- PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_NONE;
+ PurpleChatUserFlags flags = PURPLE_CHAT_USER_NONE;
idtype = va_arg(va, int);
entry = va_arg(va, void *);
mode = va_arg(va, SilcUInt32);
client_entry2 = va_arg(va, SilcClientEntry);
channel = va_arg(va, SilcChannelEntry);
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chat = purple_conversations_find_chat_with_account(
channel->channel_name, sg->account);
- if (!convo)
+ if (!chat)
break;
if (idtype == SILC_ID_CLIENT)
@@ -680,17 +680,17 @@ silc_notify(SilcClient client, SilcClientConnection conn,
_("<I>%s</I> set <I>%s's</I> modes to: %s"), name,
client_entry2->nickname, buf2);
if (mode & SILC_CHANNEL_UMODE_CHANFO)
- flags |= PURPLE_CBFLAGS_FOUNDER;
+ flags |= PURPLE_CHAT_USER_FOUNDER;
if (mode & SILC_CHANNEL_UMODE_CHANOP)
- flags |= PURPLE_CBFLAGS_OP;
+ flags |= PURPLE_CHAT_USER_OP;
} else {
g_snprintf(buf, sizeof(buf),
_("<I>%s</I> removed all <I>%s's</I> modes"), name,
client_entry2->nickname);
}
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
- buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
- purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(convo), client_entry2->nickname, flags);
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(chat), buf, 0);
+ purple_chat_user_set_flags(purple_chat_conversation_find_user(
+ chat, client_entry2->nickname), flags);
break;
}
@@ -706,9 +706,9 @@ silc_notify(SilcClient client, SilcClientConnection conn,
client_entry2 = va_arg(va, SilcClientEntry);
channel = va_arg(va, SilcChannelEntry);
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chat = purple_conversations_find_chat_with_account(
channel->channel_name, sg->account);
- if (!convo)
+ if (!chat)
break;
if (client_entry == conn->local_entry) {
@@ -717,14 +717,14 @@ silc_notify(SilcClient client, SilcClientConnection conn,
_("You have been kicked off <I>%s</I> by <I>%s</I> (%s)"),
channel->channel_name, client_entry2->nickname,
tmp ? tmp : "");
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname,
- buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
- serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(chat),
+ buf, 0);
+ purple_serv_got_chat_left(gc, purple_chat_conversation_get_id(chat));
} else {
/* Remove user from channel */
g_snprintf(buf, sizeof(buf), _("Kicked by %s (%s)"),
client_entry2->nickname, tmp ? tmp : "");
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
+ purple_chat_conversation_remove_user(chat,
client_entry->nickname,
buf);
}
@@ -758,13 +758,13 @@ silc_notify(SilcClient client, SilcClientConnection conn,
/* Remove us from all channels */
silc_hash_table_list(client_entry->channels, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chat = purple_conversations_find_chat_with_account(
chu->channel->channel_name, sg->account);
- if (!convo)
+ if (!chat)
continue;
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname,
- buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
- serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)));
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(chat), buf, 0);
+ purple_serv_got_chat_left(gc, purple_chat_conversation_get_id(chat));
}
silc_hash_table_list_reset(&htl);
@@ -789,11 +789,11 @@ silc_notify(SilcClient client, SilcClientConnection conn,
/* Remove user from all channels */
silc_hash_table_list(client_entry->channels, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chat = purple_conversations_find_chat_with_account(
chu->channel->channel_name, sg->account);
- if (!convo)
+ if (!chat)
continue;
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
+ purple_chat_conversation_remove_user(chat,
client_entry->nickname, tmp);
}
silc_hash_table_list_reset(&htl);
@@ -813,11 +813,11 @@ silc_notify(SilcClient client, SilcClientConnection conn,
/* Remove from all channels */
silc_hash_table_list(client_entry->channels, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chat = purple_conversations_find_chat_with_account(
chu->channel->channel_name, sg->account);
- if (!convo)
+ if (!chat)
continue;
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
+ purple_chat_conversation_remove_user(chat,
client_entry->nickname,
_("Server signoff"));
}
@@ -830,7 +830,7 @@ silc_notify(SilcClient client, SilcClientConnection conn,
SilcStatus error = va_arg(va, int);
purple_notify_error(gc, "Error Notify",
silc_get_status_message(error),
- NULL);
+ NULL, purple_request_cpar_from_connection(gc));
}
break;
@@ -851,6 +851,7 @@ silc_notify(SilcClient client, SilcClientConnection conn,
if (public_key) {
GSList *buddies;
const char *f;
+ gsize i;
pk = silc_pkcs_public_key_encode(public_key, &pk_len);
if (!pk)
@@ -867,7 +868,7 @@ silc_notify(SilcClient client, SilcClientConnection conn,
silc_free(pk);
/* Find buddy by associated public key */
- for (buddies = purple_find_buddies(account, NULL); buddies;
+ for (buddies = purple_blist_find_buddies(account, NULL); buddies;
buddies = g_slist_delete_link(buddies, buddies)) {
b = buddies->data;
f = purple_blist_node_get_string(PURPLE_BLIST_NODE(b), "public-key");
@@ -879,7 +880,7 @@ silc_notify(SilcClient client, SilcClientConnection conn,
cont:
if (!b) {
/* Find buddy by nickname */
- b = purple_find_buddy(sg->account, client_entry->nickname);
+ b = purple_blist_find_buddy(sg->account, client_entry->nickname);
if (!b) {
purple_debug_warning("silc", "WATCH for %s, unknown buddy\n",
client_entry->nickname);
@@ -950,7 +951,7 @@ silc_command(SilcClient client, SilcClientConnection conn,
SilcUInt32 argc, unsigned char **argv)
{
PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
switch (command) {
@@ -1087,14 +1088,14 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
SilcStatus error, va_list ap)
{
PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
- PurpleConversation *convo;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
+ PurpleChatConversation *chat;
switch (command) {
case SILC_COMMAND_JOIN:
{
SilcChannelEntry channel;
- PurpleConversation *convo;
+ PurpleChatConversation *chat;
SilcHashTableList *user_list;
SilcChannelUser chu;
GList *users = NULL, *flags = NULL;
@@ -1102,7 +1103,8 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("Join Chat"), _("Cannot join channel"),
- silc_get_status_message(error));
+ silc_get_status_message(error),
+ purple_request_cpar_from_connection(gc));
return;
}
@@ -1114,21 +1116,21 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
/* Add channel to Purple */
channel->context = SILC_32_TO_PTR(++sg->channel_ids);
- serv_got_joined_chat(gc, sg->channel_ids, channel->channel_name);
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ purple_serv_got_joined_chat(gc, sg->channel_ids, channel->channel_name);
+ chat = purple_conversations_find_chat_with_account(
channel->channel_name, sg->account);
- if (!convo)
+ if (!chat)
return;
/* Add all users to channel */
while (silc_hash_table_get(user_list, NULL, (void *)&chu)) {
- PurpleConvChatBuddyFlags f = PURPLE_CBFLAGS_NONE;
+ PurpleChatUserFlags f = PURPLE_CHAT_USER_NONE;
chu->context = SILC_32_TO_PTR(sg->channel_ids);
if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
- f |= PURPLE_CBFLAGS_FOUNDER;
+ f |= PURPLE_CHAT_USER_FOUNDER;
if (chu->mode & SILC_CHANNEL_UMODE_CHANOP)
- f |= PURPLE_CBFLAGS_OP;
+ f |= PURPLE_CHAT_USER_OP;
users = g_list_append(users, chu->client->nickname);
flags = g_list_append(flags, GINT_TO_POINTER(f));
@@ -1142,21 +1144,21 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
_("Channel founder on <I>%s</I> is <I>%s</I>"),
channel->channel_name, chu->client->nickname);
- purple_conversation_write(convo, NULL, tmp,
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(chat), tmp, 0);
}
}
- purple_conv_chat_add_users(PURPLE_CONV_CHAT(convo), users, NULL, flags, FALSE);
+ purple_chat_conversation_add_users(chat, users, NULL, flags, FALSE);
g_list_free(users);
g_list_free(flags);
/* Set topic */
if (topic)
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, topic);
+ purple_chat_conversation_set_topic(chat, NULL, topic);
/* Set nick */
- purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), conn->local_entry->nickname);
+ purple_chat_conversation_set_nick(chat, conn->local_entry->nickname);
}
break;
@@ -1171,14 +1173,15 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
SilcUInt32 *user_modes;
SilcDList channels;
SilcClientEntry client_entry;
- char tmp[1024], *tmp2;
+ char tmp[1024];
char *moodstr, *statusstr, *contactstr, *langstr, *devicestr, *tzstr, *geostr;
PurpleNotifyUserInfo *user_info;
if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("User Information"),
_("Cannot get user information"),
- silc_get_status_message(error));
+ silc_get_status_message(error),
+ purple_request_cpar_from_connection(gc));
break;
}
@@ -1188,76 +1191,85 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
(void)va_arg(ap, char *);
channels = va_arg(ap, SilcDList);
(void)va_arg(ap, SilcUInt32);
- va_arg(ap, SilcUInt32); /* idle */
+ (void)va_arg(ap, SilcUInt32); /* idle */
(void)va_arg(ap, unsigned char *);
user_modes = va_arg(ap, SilcUInt32 *);
user_info = purple_notify_user_info_new();
- tmp2 = g_markup_escape_text(client_entry->nickname, -1);
- purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp2);
- g_free(tmp2);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Nickname"), client_entry->nickname);
if (client_entry->realname) {
- tmp2 = g_markup_escape_text(client_entry->realname, -1);
- purple_notify_user_info_add_pair(user_info, _("Real Name"), tmp2);
- g_free(tmp2);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Real Name"), client_entry->realname);
}
- tmp2 = g_markup_escape_text(client_entry->username, -1);
if (*client_entry->hostname) {
- gchar *tmp3;
- tmp3 = g_strdup_printf("%s@%s", tmp2, client_entry->hostname);
- purple_notify_user_info_add_pair(user_info, _("Username"), tmp3);
- g_free(tmp3);
+ gchar *tmp2;
+ tmp2 = g_strdup_printf("%s@%s", client_entry->username, client_entry->hostname);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Username"), tmp2);
+ g_free(tmp2);
} else
- purple_notify_user_info_add_pair(user_info, _("Username"), tmp2);
- g_free(tmp2);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Username"), client_entry->username);
if (client_entry->mode) {
memset(tmp, 0, sizeof(tmp));
silcpurple_get_umode_string(client_entry->mode,
tmp, sizeof(tmp) - strlen(tmp));
- purple_notify_user_info_add_pair(user_info, _("User Modes"), tmp);
+ /* 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, _("User Modes"), tmp);
}
silcpurple_parse_attrs(client_entry->attrs, &moodstr, &statusstr, &contactstr, &langstr, &devicestr, &tzstr, &geostr);
if (moodstr) {
- purple_notify_user_info_add_pair(user_info, _("Mood"), moodstr);
+ /* 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, _("Mood"), moodstr);
g_free(moodstr);
}
if (statusstr) {
- tmp2 = g_markup_escape_text(statusstr, -1);
- purple_notify_user_info_add_pair(user_info, _("Status Text"), tmp2);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Status Text"), statusstr);
g_free(statusstr);
- g_free(tmp2);
}
if (contactstr) {
- purple_notify_user_info_add_pair(user_info, _("Preferred Contact"), contactstr);
+ /* 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, _("Preferred Contact"), contactstr);
g_free(contactstr);
}
if (langstr) {
- purple_notify_user_info_add_pair(user_info, _("Preferred Language"), langstr);
+ /* 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, _("Preferred Language"), langstr);
g_free(langstr);
}
if (devicestr) {
- purple_notify_user_info_add_pair(user_info, _("Device"), devicestr);
+ /* 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, _("Device"), devicestr);
g_free(devicestr);
}
if (tzstr) {
- purple_notify_user_info_add_pair(user_info, _("Timezone"), tzstr);
+ /* 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, _("Timezone"), tzstr);
g_free(tzstr);
}
if (geostr) {
- purple_notify_user_info_add_pair(user_info, _("Geolocation"), geostr);
+ /* 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, _("Geolocation"), geostr);
g_free(geostr);
}
- if (*client_entry->server)
- purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server);
+ if (*client_entry->server) {
+ /* 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, _("Server"), client_entry->server);
+ }
if (channels && user_modes) {
SilcChannelPayload entry;
@@ -1275,9 +1287,7 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
silc_strncat(tmp, sizeof(tmp) - 1, " ", 1);
silc_free(m);
}
- tmp2 = g_markup_escape_text(tmp, -1);
- purple_notify_user_info_add_pair(user_info, _("Currently on"), tmp2);
- g_free(tmp2);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Currently on"), tmp);
}
if (client_entry->public_key) {
@@ -1288,8 +1298,8 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
if (pk) {
fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
- purple_notify_user_info_add_pair(user_info, _("Public Key Fingerprint"), fingerprint);
- purple_notify_user_info_add_pair(user_info, _("Public Key Babbleprint"), babbleprint);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Public Key Fingerprint"), fingerprint);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Public Key Babbleprint"), babbleprint);
silc_free(fingerprint);
silc_free(babbleprint);
silc_free(pk);
@@ -1313,13 +1323,14 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
case SILC_COMMAND_WHOWAS:
{
SilcClientEntry client_entry;
- char *nickname, *realname, *username, *tmp;
+ char *nickname, *realname, *username;
PurpleNotifyUserInfo *user_info;
if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("User Information"),
_("Cannot get user information"),
- silc_get_status_message(error));
+ silc_get_status_message(error),
+ purple_request_cpar_from_connection(gc));
break;
}
@@ -1331,27 +1342,23 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
break;
user_info = purple_notify_user_info_new();
- tmp = g_markup_escape_text(nickname, -1);
- purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp);
- g_free(tmp);
- if (realname) {
- tmp = g_markup_escape_text(realname, -1);
- purple_notify_user_info_add_pair(user_info, _("Real Name"), tmp);
- g_free(tmp);
- }
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Nickname"), nickname);
+ if (realname)
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Real Name"), realname);
if (username) {
- tmp = g_markup_escape_text(username, -1);
if (client_entry && *client_entry->hostname) {
- gchar *tmp3;
- tmp3 = g_strdup_printf("%s@%s", tmp, client_entry->hostname);
- purple_notify_user_info_add_pair(user_info, _("Username"), tmp3);
- g_free(tmp3);
+ gchar *tmp;
+ tmp = g_strdup_printf("%s@%s", username, client_entry->hostname);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Username"), tmp);
+ g_free(tmp);
} else
- purple_notify_user_info_add_pair(user_info, _("Username"), tmp);
- g_free(tmp);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Username"), username);
+ }
+ if (client_entry && *client_entry->server) {
+ /* 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, _("Server"), client_entry->server);
}
- if (client_entry && *client_entry->server)
- purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server);
if (client_entry && client_entry->public_key) {
@@ -1362,8 +1369,8 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
if (pk) {
fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
- purple_notify_user_info_add_pair(user_info, _("Public Key Fingerprint"), fingerprint);
- purple_notify_user_info_add_pair(user_info, _("Public Key Babbleprint"), babbleprint);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Public Key Fingerprint"), fingerprint);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Public Key Babbleprint"), babbleprint);
silc_free(fingerprint);
silc_free(babbleprint);
silc_free(pk);
@@ -1382,7 +1389,8 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("Detach From Server"), _("Cannot detach"),
- silc_get_status_message(error));
+ silc_get_status_message(error),
+ purple_request_cpar_from_connection(gc));
return;
}
@@ -1402,15 +1410,16 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("Topic"), _("Cannot set topic"),
- silc_get_status_message(error));
+ silc_get_status_message(error),
+ purple_request_cpar_from_connection(gc));
return;
}
channel = va_arg(ap, SilcChannelEntry);
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chat = purple_conversations_find_chat_with_account(
channel->channel_name, sg->account);
- if (!convo) {
+ if (!chat) {
purple_debug_error("silc", "Got a topic for %s, which doesn't exist\n",
channel->channel_name);
break;
@@ -1418,7 +1427,7 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
/* Set topic */
if (channel->topic)
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, channel->topic);
+ purple_chat_conversation_set_topic(chat, NULL, channel->topic);
}
break;
@@ -1431,7 +1440,8 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("Nick"), _("Failed to change nickname"),
- silc_get_status_message(error));
+ silc_get_status_message(error),
+ purple_request_cpar_from_connection(gc));
return;
}
@@ -1441,15 +1451,18 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
/* Change nick on all channels */
silc_hash_table_list(local_entry->channels, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chat = purple_conversations_find_chat_with_account(
chu->channel->channel_name, sg->account);
- if (!convo)
+ if (!chat)
continue;
- oldnick = purple_conv_chat_get_nick(PURPLE_CONV_CHAT(convo));
- if (strcmp(oldnick, purple_normalize(purple_conversation_get_account(convo), newnick))) {
- purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo),
+ oldnick = purple_chat_conversation_get_nick(chat);
+ if (strcmp(oldnick,
+ purple_normalize(purple_conversation_get_account
+ (PURPLE_CONVERSATION(chat)), newnick))) {
+
+ purple_chat_conversation_rename_user(chat,
oldnick, newnick);
- purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), newnick);
+ purple_chat_conversation_set_nick(chat, newnick);
}
}
silc_hash_table_list_reset(&htl);
@@ -1469,9 +1482,10 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
if (error != SILC_STATUS_OK) {
purple_notify_error(gc, _("Error"), _("Error retrieving room list"),
- silc_get_status_message(error));
+ silc_get_status_message(error),
+ purple_request_cpar_from_connection(gc));
purple_roomlist_set_in_progress(sg->roomlist, FALSE);
- purple_roomlist_unref(sg->roomlist);
+ g_object_unref(sg->roomlist);
sg->roomlist = NULL;
return;
}
@@ -1480,9 +1494,10 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
name = va_arg(ap, char *);
if (!name) {
purple_notify_error(gc, _("Roomlist"), _("Cannot get room list"),
- _("Network is empty"));
+ _("Network is empty"),
+ purple_request_cpar_from_connection(gc));
purple_roomlist_set_in_progress(sg->roomlist, FALSE);
- purple_roomlist_unref(sg->roomlist);
+ g_object_unref(sg->roomlist);
sg->roomlist = NULL;
return;
}
@@ -1500,7 +1515,7 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
if (status == SILC_STATUS_LIST_END ||
status == SILC_STATUS_OK) {
purple_roomlist_set_in_progress(sg->roomlist, FALSE);
- purple_roomlist_unref(sg->roomlist);
+ g_object_unref(sg->roomlist);
sg->roomlist = NULL;
}
}
@@ -1513,7 +1528,8 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("Get Public Key"),
_("Cannot fetch the public key"),
- silc_get_status_message(error));
+ silc_get_status_message(error),
+ purple_request_cpar_from_connection(gc));
return;
}
@@ -1524,7 +1540,8 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
if (!public_key)
purple_notify_error(gc, _("Get Public Key"),
_("Cannot fetch the public key"),
- _("No public key was received"));
+ _("No public key was received"),
+ purple_request_cpar_from_connection(gc));
}
break;
@@ -1538,7 +1555,8 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("Server Information"),
_("Cannot get server information"),
- silc_get_status_message(error));
+ silc_get_status_message(error),
+ purple_request_cpar_from_connection(gc));
return;
}
@@ -1549,7 +1567,8 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
if (server_name && server_info) {
g_snprintf(tmp, sizeof(tmp), "Server: %s\n%s",
server_name, server_info);
- purple_notify_info(gc, NULL, _("Server Information"), tmp);
+ purple_notify_info(gc, NULL, _("Server Information"), tmp,
+ purple_request_cpar_from_connection(gc));
}
}
break;
@@ -1562,7 +1581,8 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("Server Statistics"),
_("Cannot get server statistics"),
- silc_get_status_message(error));
+ silc_get_status_message(error),
+ purple_request_cpar_from_connection(gc));
return;
}
@@ -1600,7 +1620,8 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
(int)stats->router_ops);
purple_notify_info(gc, NULL,
- _("Network Statistics"), msg);
+ _("Network Statistics"), msg,
+ purple_request_cpar_from_connection(gc));
g_free(msg);
}
break;
@@ -1609,12 +1630,13 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
{
if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("Ping"), _("Ping failed"),
- silc_get_status_message(error));
+ silc_get_status_message(error),
+ purple_request_cpar_from_connection(gc));
return;
}
purple_notify_info(gc, _("Ping"), _("Ping reply received from server"),
- NULL);
+ NULL, purple_request_cpar_from_connection(gc));
}
break;
@@ -1622,7 +1644,8 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("Kill User"),
_("Could not kill user"),
- silc_get_status_message(error));
+ silc_get_status_message(error),
+ purple_request_cpar_from_connection(gc));
return;
}
break;
@@ -1661,7 +1684,8 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
case SILC_COMMAND_WATCH:
if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("WATCH"), _("Cannot watch user"),
- silc_get_status_message(error));
+ silc_get_status_message(error),
+ purple_request_cpar_from_connection(gc));
return;
}
break;
@@ -1730,7 +1754,7 @@ silc_get_auth_method(SilcClient client, SilcClientConnection conn,
SilcGetAuthMeth completion, void *context)
{
PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcPurpleAskPassphrase internal;
const char *password;
@@ -1781,7 +1805,7 @@ silc_verify_public_key(SilcClient client, SilcClientConnection conn,
SilcVerifyPublicKey completion, void *context)
{
PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
if (!sg->conn && (conn_type == SILC_CONN_SERVER ||
conn_type == SILC_CONN_ROUTER)) {
@@ -1829,7 +1853,7 @@ silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
_("Passphrase required"), NULL, FALSE, TRUE, NULL,
_("OK"), G_CALLBACK(silc_ask_passphrase_cb),
_("Cancel"), G_CALLBACK(silc_ask_passphrase_cb),
- purple_connection_get_account(gc), NULL, NULL, internal);
+ purple_request_cpar_from_connection(gc), internal);
}
diff --git a/libpurple/protocols/silc/pk.c b/libpurple/protocols/silc/pk.c
index 6d25db910d..b62db3aeac 100644
--- a/libpurple/protocols/silc/pk.c
+++ b/libpurple/protocols/silc/pk.c
@@ -18,7 +18,10 @@
*/
#include "internal.h"
+#include "glibcompat.h"
+PURPLE_BEGIN_IGNORE_CAST_ALIGN
#include "silc.h"
+PURPLE_END_IGNORE_CAST_ALIGN
#include "silcclient.h"
#include "silcpurple.h"
@@ -79,7 +82,7 @@ static void silcpurple_verify_details_cb(PublicKeyVerify verify)
static void silcpurple_verify_details(PublicKeyVerify verify, gint id)
{
PurpleConnection *gc = verify->client->application;
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
silcpurple_show_public_key(sg, verify->entity_name, verify->public_key,
G_CALLBACK(silcpurple_verify_details_cb),
@@ -110,7 +113,7 @@ static void silcpurple_verify_ask(const char *entity,
purple_request_action(gc, _("Verify Public Key"), tmp, tmp2,
PURPLE_DEFAULT_ACTION_NONE,
- purple_connection_get_account(gc), entity, NULL, verify, 3,
+ purple_request_cpar_from_connection(gc), verify, 3,
_("Yes"), G_CALLBACK(silcpurple_verify_cb),
_("No"), G_CALLBACK(silcpurple_verify_cb),
_("_View..."), G_CALLBACK(silcpurple_verify_details));
@@ -122,11 +125,11 @@ void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn,
SilcVerifyPublicKey completion, void *context)
{
PurpleConnection *gc = client->application;
- int i;
+ gsize i;
char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
char *fingerprint, *babbleprint;
struct passwd *pw;
- struct stat st;
+ GStatBuf st;
char *entity = ((conn_type == SILC_CONN_SERVER ||
conn_type == SILC_CONN_ROUTER) ?
"server" : "client");
@@ -138,7 +141,8 @@ void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn,
if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) {
purple_notify_error(gc, _("Verify Public Key"),
- _("Unsupported public key type"), NULL);
+ _("Unsupported public key type"), NULL,
+ purple_request_cpar_from_connection(gc));
if (completion)
completion(FALSE, context);
return;
diff --git a/libpurple/protocols/silc/silc.c b/libpurple/protocols/silc/silc.c
index 3084d278aa..b02a268920 100644
--- a/libpurple/protocols/silc/silc.c
+++ b/libpurple/protocols/silc/silc.c
@@ -18,7 +18,9 @@
*/
#include "internal.h"
+PURPLE_BEGIN_IGNORE_CAST_ALIGN
#include "silc.h"
+PURPLE_END_IGNORE_CAST_ALIGN
#include "silcclient.h"
#include "silcpurple.h"
#include "version.h"
@@ -26,7 +28,6 @@
#include "core.h"
extern SilcClientOperations ops;
-static PurplePlugin *silc_plugin = NULL;
/* Error log message callback */
@@ -78,7 +79,7 @@ silcpurple_set_status(PurpleAccount *account, PurpleStatus *status)
const char *state;
if (gc != NULL)
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
if (status == NULL)
return;
@@ -125,7 +126,7 @@ silcpurple_set_status(PurpleAccount *account, PurpleStatus *status)
static void
silcpurple_keepalive(PurpleConnection *gc)
{
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
silc_packet_send(sg->conn->stream, SILC_PACKET_HEARTBEAT, 0,
NULL, 0);
}
@@ -180,7 +181,7 @@ silcpurple_scheduler(SilcSchedule schedule,
{
SilcClient client = (SilcClient)context;
PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcPurpleTask ptask = NULL;
if (added) {
@@ -247,12 +248,12 @@ silcpurple_connect_cb(SilcClient client, SilcClientConnection conn,
SilcPurple sg;
SilcUInt32 mask;
char tz[16];
- PurpleStoredImage *img;
+ PurpleImage *img;
#ifdef HAVE_SYS_UTSNAME_H
struct utsname u;
#endif
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
switch (status) {
case SILC_CLIENT_CONN_SUCCESS:
@@ -260,7 +261,7 @@ silcpurple_connect_cb(SilcClient client, SilcClientConnection conn,
sg->conn = conn;
/* Connection created successfully */
- purple_connection_set_state(gc, PURPLE_CONNECTED);
+ purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
/* Send the server our buddy list */
silcpurple_send_buddylist(gc);
@@ -304,7 +305,7 @@ silcpurple_connect_cb(SilcClient client, SilcClientConnection conn,
/* Set our buddy icon */
img = purple_buddy_icons_find_account_icon(sg->account);
silcpurple_buddy_set_icon(gc, img);
- purple_imgstore_unref(img);
+ g_object_unref(img);
return;
break;
@@ -316,7 +317,7 @@ silcpurple_connect_cb(SilcClient client, SilcClientConnection conn,
/* Close the connection */
if (!sg->detaching)
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Disconnected by server"));
else
@@ -325,30 +326,30 @@ silcpurple_connect_cb(SilcClient client, SilcClientConnection conn,
break;
case SILC_CLIENT_CONN_ERROR:
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Error connecting to SILC Server"));
g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
break;
case SILC_CLIENT_CONN_ERROR_KE:
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
_("Key Exchange failed"));
break;
case SILC_CLIENT_CONN_ERROR_AUTH:
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
_("Authentication failed"));
break;
case SILC_CLIENT_CONN_ERROR_RESUME:
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
_("Resuming detached session failed. "
"Press Reconnect to create new connection."));
g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
break;
case SILC_CLIENT_CONN_ERROR_TIMEOUT:
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Connection timed out"));
break;
}
@@ -367,16 +368,16 @@ silcpurple_stream_created(SilcSocketStreamStatus status, SilcStream stream,
SilcClientConnectionParams params;
const char *dfile;
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
if (status != SILC_SOCKET_OK) {
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Connection failed"));
silc_pkcs_public_key_free(sg->public_key);
silc_pkcs_private_key_free(sg->private_key);
silc_free(sg);
- gc->proto_data = NULL;
+ purple_connection_set_protocol_data(gc, NULL);
return;
}
@@ -415,15 +416,15 @@ silcpurple_login_connected(gpointer data, gint source, const gchar *error_messag
g_return_if_fail(gc != NULL);
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
if (source < 0) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Connection failed"));
silc_pkcs_public_key_free(sg->public_key);
silc_pkcs_private_key_free(sg->private_key);
silc_free(sg);
- gc->proto_data = NULL;
+ purple_connection_set_protocol_data(gc, NULL);
return;
}
@@ -447,9 +448,9 @@ static void silcpurple_continue_running(SilcPurple sg)
purple_account_get_int(account, "port", 706),
silcpurple_login_connected, gc) == NULL)
{
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to connect"));
- gc->proto_data = NULL;
+ purple_connection_set_protocol_data(gc, NULL);
silc_free(sg);
return;
}
@@ -457,23 +458,24 @@ static void silcpurple_continue_running(SilcPurple sg)
static void silcpurple_got_password_cb(PurpleConnection *gc, PurpleRequestFields *fields)
{
- SilcPurple sg = (SilcPurple)gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
PurpleAccount *account = purple_connection_get_account(gc);
char pkd[256], prd[256];
const char *password;
gboolean remember;
- /* The password prompt dialog doesn't get disposed if the account disconnects */
- if (!PURPLE_CONNECTION_IS_VALID(gc))
- return;
+ /* TODO: the password prompt dialog doesn't get disposed if the account disconnects */
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
password = purple_request_fields_get_string(fields, "password");
remember = purple_request_fields_get_bool(fields, "remember");
if (!password || !*password)
{
- purple_notify_error(gc, NULL, _("Password is required to sign on."), NULL);
- gc->proto_data = NULL;
+ purple_notify_error(gc, NULL,
+ _("Password is required to sign on."), NULL,
+ purple_request_cpar_from_connection(gc));
+ purple_connection_set_protocol_data(gc, NULL);
silc_free(sg);
return;
}
@@ -481,7 +483,7 @@ static void silcpurple_got_password_cb(PurpleConnection *gc, PurpleRequestFields
if (remember)
purple_account_set_remember_password(account, TRUE);
- purple_account_set_password(account, password);
+ purple_account_set_password(account, password, NULL, NULL);
/* Load SILC key pair */
g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
@@ -490,9 +492,9 @@ static void silcpurple_got_password_cb(PurpleConnection *gc, PurpleRequestFields
(char *)purple_account_get_string(account, "private-key", prd),
password,
&sg->public_key, &sg->private_key)) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
_("Unable to load SILC key pair"));
- gc->proto_data = NULL;
+ purple_connection_set_protocol_data(gc, NULL);
silc_free(sg);
return;
}
@@ -502,13 +504,14 @@ static void silcpurple_got_password_cb(PurpleConnection *gc, PurpleRequestFields
static void silcpurple_no_password_cb(PurpleConnection *gc, PurpleRequestFields *fields)
{
SilcPurple sg;
- /* The password prompt dialog doesn't get disposed if the account disconnects */
- if (!PURPLE_CONNECTION_IS_VALID(gc))
- return;
- sg = gc->proto_data;
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+
+ /* TODO: the password prompt dialog doesn't get disposed if the account disconnects */
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
+
+ sg = purple_connection_get_protocol_data(gc);
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
_("Unable to load SILC key pair"));
- gc->proto_data = NULL;
+ purple_connection_set_protocol_data(gc, NULL);
silc_free(sg);
}
@@ -528,16 +531,16 @@ static void silcpurple_running(SilcClient client, void *context)
g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
if (!silc_load_key_pair((char *)purple_account_get_string(account, "public-key", pkd),
(char *)purple_account_get_string(account, "private-key", prd),
- (gc->password == NULL) ? "" : gc->password,
+ (purple_connection_get_password(gc) == NULL) ? "" : purple_connection_get_password(gc),
&sg->public_key, &sg->private_key)) {
- if (!purple_account_get_password(account)) {
+ if (!purple_connection_get_password(gc)) {
purple_account_request_password(account, G_CALLBACK(silcpurple_got_password_cb),
G_CALLBACK(silcpurple_no_password_cb), gc);
return;
}
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
_("Unable to load SILC key pair"));
- gc->proto_data = NULL;
+ purple_connection_set_protocol_data(gc, NULL);
silc_free(sg);
return;
}
@@ -555,10 +558,10 @@ silcpurple_login(PurpleAccount *account)
char *username, *hostname, *realname, **up;
int i;
- gc = account->gc;
+ gc = purple_account_get_connection(account);
if (!gc)
return;
- gc->proto_data = NULL;
+ purple_connection_set_protocol_data(gc, NULL);
memset(&params, 0, sizeof(params));
strcat(params.nickname_format, "%n#a");
@@ -566,7 +569,7 @@ silcpurple_login(PurpleAccount *account)
/* Allocate SILC client */
client = silc_client_alloc(&ops, &params, gc, NULL);
if (!client) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
_("Out of memory"));
return;
}
@@ -612,14 +615,14 @@ silcpurple_login(PurpleAccount *account)
sg->client = client;
sg->gc = gc;
sg->account = account;
- gc->proto_data = sg;
+ purple_connection_set_protocol_data(gc, sg);
/* Init SILC client */
if (!silc_client_init(client, username, hostname, realname,
silcpurple_running, sg)) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
_("Unable to initialize SILC protocol"));
- gc->proto_data = NULL;
+ purple_connection_set_protocol_data(gc, NULL);
silc_free(sg);
silc_free(hostname);
g_free(username);
@@ -630,9 +633,9 @@ silcpurple_login(PurpleAccount *account)
/* Check the ~/.silc dir and create it, and new key pair if necessary. */
if (!silcpurple_check_silc_dir(gc)) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
_("Error loading SILC key pair"));
- gc->proto_data = NULL;
+ purple_connection_set_protocol_data(gc, NULL);
silc_free(sg);
return;
}
@@ -669,7 +672,7 @@ silcpurple_close_final(gpointer *context)
static void
silcpurple_close(PurpleConnection *gc)
{
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
#if __SILC_TOOLKIT_VERSION >= SILC_VERSION(1,1,1)
SilcPurpleTask task;
#endif /* __SILC_TOOLKIT_VERSION */
@@ -715,7 +718,8 @@ silcpurple_close(PurpleConnection *gc)
silc_dlist_uninit(sg->tasks);
#endif /* __SILC_TOOLKIT_VERSION */
- purple_timeout_remove(sg->scheduler);
+ if (sg->scheduler)
+ purple_timeout_remove(sg->scheduler);
purple_debug_info("silc", "Scheduling destruction of SilcPurple %p\n", sg);
purple_timeout_add(1, (GSourceFunc)silcpurple_close_final, sg);
@@ -733,7 +737,7 @@ silcpurple_attrs_cancel(PurpleConnection *gc, PurpleRequestFields *fields)
static void
silcpurple_attrs_cb(PurpleConnection *gc, PurpleRequestFields *fields)
{
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcClient client = sg->client;
SilcClientConnection conn = sg->conn;
PurpleRequestField *f;
@@ -744,7 +748,7 @@ silcpurple_attrs_cb(PurpleConnection *gc, PurpleRequestFields *fields)
SilcVCardStruct vcard;
const char *val;
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
if (!sg)
return;
@@ -905,7 +909,7 @@ static void
silcpurple_attrs(PurplePluginAction *action)
{
PurpleConnection *gc = (PurpleConnection *) action->context;
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcClient client = sg->client;
SilcClientConnection conn = sg->conn;
PurpleRequestFields *fields;
@@ -919,10 +923,12 @@ silcpurple_attrs(PurplePluginAction *action)
mbored = FALSE, mexcited = FALSE, manxious = FALSE;
gboolean cemail = FALSE, ccall = FALSE, csms = FALSE,
cmms = FALSE, cchat = TRUE, cvideo = FALSE;
+#ifdef HAVE_SYS_UTSNAME_H
gboolean device = TRUE;
+#endif
char status[1024], tz[16];
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
if (!sg)
return;
@@ -967,10 +973,12 @@ silcpurple_attrs(PurplePluginAction *action)
NULL, (void *)&attr))
silc_attribute_get_object(attr, &status, sizeof(status));
+#ifdef HAVE_SYS_UTSNAME_H
if (!silc_hash_table_find(attrs,
SILC_32_TO_PTR(SILC_ATTRIBUTE_DEVICE_INFO),
NULL, (void *)&attr))
device = FALSE;
+#endif
}
fields = purple_request_fields_new();
@@ -1061,7 +1069,7 @@ silcpurple_attrs(PurplePluginAction *action)
fields,
_("OK"), G_CALLBACK(silcpurple_attrs_cb),
_("Cancel"), G_CALLBACK(silcpurple_attrs_cancel),
- gc->account, NULL, NULL, gc);
+ purple_request_cpar_from_connection(gc), gc);
}
static void
@@ -1072,7 +1080,7 @@ silcpurple_detach(PurplePluginAction *action)
if (!gc)
return;
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
if (!sg)
return;
@@ -1090,14 +1098,15 @@ silcpurple_view_motd(PurplePluginAction *action)
if (!gc)
return;
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
if (!sg)
return;
if (!sg->motd) {
- purple_notify_error(
- gc, _("Message of the Day"), _("No Message of the Day available"),
- _("There is no Message of the Day associated with this connection"));
+ purple_notify_error(gc, _("Message of the Day"), _("No Message "
+ "of the Day available"), _("There is no Message of the "
+ "Day associated with this connection"),
+ purple_request_cpar_from_connection(gc));
return;
}
@@ -1125,7 +1134,7 @@ silcpurple_create_keypair_cb(PurpleConnection *gc, PurpleRequestFields *fields)
int keylen = SILCPURPLE_DEF_PKCS_LEN;
SilcPublicKey public_key;
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
if (!sg)
return;
@@ -1147,8 +1156,9 @@ silcpurple_create_keypair_cb(PurpleConnection *gc, PurpleRequestFields *fields)
pass2 = "";
if (strcmp(pass1, pass2)) {
- purple_notify_error(
- gc, _("Create New SILC Key Pair"), _("Passphrases do not match"), NULL);
+ purple_notify_error(gc, _("Create New SILC Key Pair"),
+ _("Passphrases do not match"), NULL,
+ purple_request_cpar_from_connection(gc));
return;
}
@@ -1193,8 +1203,9 @@ silcpurple_create_keypair_cb(PurpleConnection *gc, PurpleRequestFields *fields)
if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS, keylen, pkfile, prfile,
identifier, pass1, &public_key, NULL,
FALSE)) {
- purple_notify_error(
- gc, _("Create New SILC Key Pair"), _("Key Pair Generation failed"), NULL);
+ purple_notify_error(gc, _("Create New SILC Key Pair"),
+ _("Key Pair Generation failed"), NULL,
+ purple_request_cpar_from_connection(gc));
return;
}
@@ -1208,7 +1219,7 @@ static void
silcpurple_create_keypair(PurplePluginAction *action)
{
PurpleConnection *gc = (PurpleConnection *) action->context;
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
PurpleRequestFields *fields;
PurpleRequestFieldGroup *g;
PurpleRequestField *f;
@@ -1226,9 +1237,9 @@ silcpurple_create_keypair(PurplePluginAction *action)
g_snprintf(pkd2, sizeof(pkd2), "%s" G_DIR_SEPARATOR_S"public_key.pub", silcpurple_silcdir());
g_snprintf(prd2, sizeof(prd2), "%s" G_DIR_SEPARATOR_S"private_key.prv", silcpurple_silcdir());
g_snprintf(pkd, sizeof(pkd) - 1, "%s",
- purple_account_get_string(gc->account, "public-key", pkd2));
+ purple_account_get_string(purple_connection_get_account(gc), "public-key", pkd2));
g_snprintf(prd, sizeof(prd) - 1, "%s",
- purple_account_get_string(gc->account, "private-key", prd2));
+ purple_account_get_string(purple_connection_get_account(gc), "private-key", prd2));
fields = purple_request_fields_new();
@@ -1269,7 +1280,7 @@ silcpurple_create_keypair(PurplePluginAction *action)
_("Create New SILC Key Pair"), NULL, fields,
_("Generate Key Pair"), G_CALLBACK(silcpurple_create_keypair_cb),
_("Cancel"), G_CALLBACK(silcpurple_create_keypair_cancel),
- gc->account, NULL, NULL, gc);
+ purple_request_cpar_from_connection(gc), gc);
g_strfreev(u);
silc_free(hostname);
@@ -1287,7 +1298,7 @@ silcpurple_change_passwd(PurpleConnection *gc, const char *old, const char *new)
{
char prd[256];
g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.pub", silcpurple_silcdir());
- silc_change_private_key_passphrase(purple_account_get_string(gc->account,
+ silc_change_private_key_passphrase(purple_account_get_string(purple_connection_get_account(gc),
"private-key",
prd), old ? old : "", new ? new : "");
}
@@ -1356,15 +1367,15 @@ silcpurple_send_im_resolved(SilcClient client,
void *context)
{
PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcPurpleIM im = context;
- PurpleConversation *convo;
+ PurpleIMConversation *convo;
char tmp[256];
SilcClientEntry client_entry;
SilcDList list;
gboolean free_list = FALSE;
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, im->nick,
+ convo = purple_conversations_find_im_with_account(im->nick,
sg->account);
if (!convo)
return;
@@ -1401,8 +1412,9 @@ silcpurple_send_im_resolved(SilcClient client,
buf->data,
silc_buffer_len(buf));
silc_mime_partial_free(list);
- purple_conv_im_write(PURPLE_CONV_IM(convo), conn->local_entry->nickname,
- im->message, 0, time(NULL));
+ purple_conversation_write_message(PURPLE_CONVERSATION(convo),
+ purple_message_new_outgoing(
+ conn->local_entry->nickname, im->message, 0));
goto out;
}
}
@@ -1410,14 +1422,15 @@ silcpurple_send_im_resolved(SilcClient client,
/* Send the message */
silc_client_send_private_message(client, conn, client_entry, im->flags,
sg->sha1hash, (unsigned char *)im->message, im->message_len);
- purple_conv_im_write(PURPLE_CONV_IM(convo), conn->local_entry->nickname,
- im->message, 0, time(NULL));
+ purple_conversation_write_message(PURPLE_CONVERSATION(convo),
+ purple_message_new_outgoing(conn->local_entry->nickname, im->message, 0));
goto out;
err:
g_snprintf(tmp, sizeof(tmp),
_("User <I>%s</I> is not present in the network"), im->nick);
- purple_conversation_write(convo, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(convo), tmp, 0);
out:
if (free_list) {
@@ -1429,10 +1442,9 @@ silcpurple_send_im_resolved(SilcClient client,
}
static int
-silcpurple_send_im(PurpleConnection *gc, const char *who, const char *message,
- PurpleMessageFlags flags)
+silcpurple_send_im(PurpleConnection *gc, PurpleMessage *pmsg)
{
- SilcPurple sg = gc->proto_data;
+ SilcPurple sg = purple_connection_get_protocol_data(gc);
SilcClient client = sg->client;
SilcClientConnection conn = sg->conn;
SilcDList clients;
@@ -1442,8 +1454,11 @@ silcpurple_send_im(PurpleConnection *gc, const char *who, const char *message,
int ret = 0;
gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE);
SilcDList list;
+ const gchar *rcpt = purple_message_get_recipient(pmsg);
+ const gchar *message = purple_message_get_contents(pmsg);
+ PurpleMessageFlags flags = purple_message_get_flags(pmsg);
- if (!who || !message)
+ if (!rcpt || purple_message_is_empty(pmsg))
return 0;
mflags = SILC_MESSAGE_FLAG_UTF8;
@@ -1461,7 +1476,8 @@ silcpurple_send_im(PurpleConnection *gc, const char *who, const char *message,
if (!silc_client_command_call(client, conn, msg + 1))
purple_notify_error(gc, _("Call Command"),
_("Cannot call command"),
- _("Unknown command"));
+ _("Unknown command"),
+ purple_request_cpar_from_connection(gc));
g_free(tmp);
return 0;
}
@@ -1470,7 +1486,7 @@ silcpurple_send_im(PurpleConnection *gc, const char *who, const char *message,
mflags |= SILC_MESSAGE_FLAG_SIGNED;
/* Find client entry */
- clients = silc_client_get_clients_local(client, conn, who, FALSE);
+ clients = silc_client_get_clients_local(client, conn, rcpt, FALSE);
if (!clients) {
/* Resolve unknown user */
SilcPurpleIM im = silc_calloc(1, sizeof(*im));
@@ -1478,17 +1494,16 @@ silcpurple_send_im(PurpleConnection *gc, const char *who, const char *message,
g_free(tmp);
return 0;
}
- im->nick = g_strdup(who);
+ im->nick = g_strdup(rcpt);
im->message = g_strdup(message);
im->message_len = strlen(im->message);
im->flags = mflags;
im->gflags = flags;
- silc_client_get_clients(client, conn, who, NULL,
+ silc_client_get_clients(client, conn, rcpt, NULL,
silcpurple_send_im_resolved, im);
g_free(tmp);
return 0;
}
-
silc_dlist_start(clients);
client_entry = silc_dlist_get(clients);
@@ -1529,9 +1544,9 @@ silcpurple_send_im(PurpleConnection *gc, const char *who, const char *message,
static GList *silcpurple_blist_node_menu(PurpleBlistNode *node) {
/* split this single menu building function back into the two
original: one for buddies and one for chats */
- if(PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ if(PURPLE_IS_CHAT(node)) {
return silcpurple_chat_menu((PurpleChat *) node);
- } else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ } else if(PURPLE_IS_BUDDY(node)) {
return silcpurple_buddy_menu((PurpleBuddy *) node);
} else {
g_return_val_if_reached(NULL);
@@ -1544,20 +1559,20 @@ static PurpleCmdRet silcpurple_cmd_chat_part(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
PurpleConnection *gc;
- PurpleConversation *convo = conv;
+ PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(conv);
int id = 0;
- gc = purple_conversation_get_gc(conv);
+ gc = purple_conversation_get_connection(conv);
if (gc == NULL)
return PURPLE_CMD_RET_FAILED;
if(args && args[0])
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[0],
- gc->account);
+ chat = purple_conversations_find_chat_with_account(args[0],
+ purple_connection_get_account(gc));
- if (convo != NULL)
- id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo));
+ if (chat != NULL)
+ id = purple_chat_conversation_get_id(chat);
if (id == 0)
return PURPLE_CMD_RET_FAILED;
@@ -1576,14 +1591,14 @@ static PurpleCmdRet silcpurple_cmd_chat_topic(PurpleConversation *conv,
char *buf, *tmp, *tmp2;
const char *topic;
- gc = purple_conversation_get_gc(conv);
- id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv));
+ gc = purple_conversation_get_connection(conv);
+ id = purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv));
if (gc == NULL || id == 0)
return PURPLE_CMD_RET_FAILED;
if (!args || !args[0]) {
- topic = purple_conv_chat_get_topic (PURPLE_CONV_CHAT(conv));
+ topic = purple_chat_conversation_get_topic(PURPLE_CHAT_CONVERSATION(conv));
if (topic) {
tmp = g_markup_escape_text(topic, -1);
tmp2 = purple_markup_linkify(tmp);
@@ -1592,8 +1607,8 @@ static PurpleCmdRet silcpurple_cmd_chat_topic(PurpleConversation *conv,
g_free(tmp2);
} else
buf = g_strdup(_("No topic is set"));
- purple_conv_chat_write(PURPLE_CONV_CHAT(conv), gc->account->username, buf,
- PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(conv,
+ buf, PURPLE_MESSAGE_NO_LOG);
g_free(buf);
}
@@ -1622,7 +1637,7 @@ static PurpleCmdRet silcpurple_cmd_chat_join(PurpleConversation *conv,
if(args[1])
g_hash_table_replace(comp, "passphrase", args[1]);
- silcpurple_chat_join(purple_conversation_get_gc(conv), comp);
+ silcpurple_chat_join(purple_conversation_get_connection(conv), comp);
g_hash_table_destroy(comp);
return PURPLE_CMD_RET_OK;
@@ -1632,7 +1647,7 @@ static PurpleCmdRet silcpurple_cmd_chat_list(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
PurpleConnection *gc;
- gc = purple_conversation_get_gc(conv);
+ gc = purple_conversation_get_connection(conv);
purple_roomlist_show_with_account(purple_connection_get_account(gc));
return PURPLE_CMD_RET_OK;
}
@@ -1642,7 +1657,7 @@ static PurpleCmdRet silcpurple_cmd_whois(PurpleConversation *conv,
{
PurpleConnection *gc;
- gc = purple_conversation_get_gc(conv);
+ gc = purple_conversation_get_connection(conv);
if (gc == NULL)
return PURPLE_CMD_RET_FAILED;
@@ -1658,12 +1673,13 @@ static PurpleCmdRet silcpurple_cmd_msg(PurpleConversation *conv,
int ret;
PurpleConnection *gc;
- gc = purple_conversation_get_gc(conv);
+ gc = purple_conversation_get_connection(conv);
if (gc == NULL)
return PURPLE_CMD_RET_FAILED;
- ret = silcpurple_send_im(gc, args[0], args[1], PURPLE_MESSAGE_SEND);
+ ret = silcpurple_send_im(gc,
+ purple_message_new_outgoing(args[0], args[1], 0));
if (ret)
return PURPLE_CMD_RET_OK;
@@ -1675,7 +1691,7 @@ static PurpleCmdRet silcpurple_cmd_query(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
int ret = 1;
- PurpleConversation *convo;
+ PurpleIMConversation *im;
PurpleConnection *gc;
PurpleAccount *account;
@@ -1684,19 +1700,21 @@ static PurpleCmdRet silcpurple_cmd_query(PurpleConversation *conv,
return PURPLE_CMD_RET_FAILED;
}
- gc = purple_conversation_get_gc(conv);
+ gc = purple_conversation_get_connection(conv);
if (gc == NULL)
return PURPLE_CMD_RET_FAILED;
account = purple_connection_get_account(gc);
- convo = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, args[0]);
+ im = purple_im_conversation_new(account, args[0]);
if (args[1]) {
- ret = silcpurple_send_im(gc, args[0], args[1], PURPLE_MESSAGE_SEND);
- purple_conv_im_write(PURPLE_CONV_IM(convo), purple_connection_get_display_name(gc),
- args[1], PURPLE_MESSAGE_SEND, time(NULL));
+ PurpleMessage *msg = purple_message_new_outgoing(
+ args[0], args[1], 0);
+
+ ret = silcpurple_send_im(gc, msg);
+ purple_conversation_write_message(PURPLE_CONVERSATION(im), msg);
}
if (ret)
@@ -1712,12 +1730,12 @@ static PurpleCmdRet silcpurple_cmd_motd(PurpleConversation *conv,
SilcPurple sg;
char *tmp;
- gc = purple_conversation_get_gc(conv);
+ gc = purple_conversation_get_connection(conv);
if (gc == NULL)
return PURPLE_CMD_RET_FAILED;
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
if (sg == NULL)
return PURPLE_CMD_RET_FAILED;
@@ -1741,12 +1759,12 @@ static PurpleCmdRet silcpurple_cmd_detach(PurpleConversation *conv,
PurpleConnection *gc;
SilcPurple sg;
- gc = purple_conversation_get_gc(conv);
+ gc = purple_conversation_get_connection(conv);
if (gc == NULL)
return PURPLE_CMD_RET_FAILED;
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
if (sg == NULL)
return PURPLE_CMD_RET_FAILED;
@@ -1766,12 +1784,12 @@ static PurpleCmdRet silcpurple_cmd_cmode(PurpleConversation *conv,
char *silccmd, *silcargs, *msg, tmp[256];
const char *chname;
- gc = purple_conversation_get_gc(conv);
+ gc = purple_conversation_get_connection(conv);
- if (gc == NULL || !args || gc->proto_data == NULL)
+ if (gc == NULL || !args || purple_connection_get_protocol_data(gc) == NULL)
return PURPLE_CMD_RET_FAILED;
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
if (args[0])
chname = args[0];
@@ -1791,8 +1809,7 @@ static PurpleCmdRet silcpurple_cmd_cmode(PurpleConversation *conv,
} else {
msg = g_strdup_printf(_("no channel modes are set on %s"), chname);
}
- purple_conv_chat_write(PURPLE_CONV_CHAT(conv), "",
- msg, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(conv, msg, PURPLE_MESSAGE_NO_LOG);
g_free(msg);
return PURPLE_CMD_RET_OK;
}
@@ -1817,12 +1834,12 @@ static PurpleCmdRet silcpurple_cmd_generic(PurpleConversation *conv,
SilcPurple sg;
char *silccmd, *silcargs;
- gc = purple_conversation_get_gc(conv);
+ gc = purple_conversation_get_connection(conv);
if (gc == NULL)
return PURPLE_CMD_RET_FAILED;
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
if (sg == NULL)
return PURPLE_CMD_RET_FAILED;
@@ -1849,12 +1866,12 @@ static PurpleCmdRet silcpurple_cmd_quit(PurpleConversation *conv,
const char *ui_name = NULL, *ui_website = NULL;
char *quit_msg;
- gc = purple_conversation_get_gc(conv);
+ gc = purple_conversation_get_connection(conv);
if (gc == NULL)
return PURPLE_CMD_RET_FAILED;
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
if (sg == NULL)
return PURPLE_CMD_RET_FAILED;
@@ -1886,12 +1903,12 @@ static PurpleCmdRet silcpurple_cmd_call(PurpleConversation *conv,
PurpleConnection *gc;
SilcPurple sg;
- gc = purple_conversation_get_gc(conv);
+ gc = purple_conversation_get_connection(conv);
if (gc == NULL)
return PURPLE_CMD_RET_FAILED;
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
if (sg == NULL)
return PURPLE_CMD_RET_FAILED;
@@ -2049,8 +2066,9 @@ static PurpleWhiteboardPrplOps silcpurple_wb_ops =
static PurplePluginProtocolInfo prpl_info =
{
+ sizeof(PurplePluginProtocolInfo), /* struct_size */
OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME |
- OPT_PROTO_PASSWORD_OPTIONAL | OPT_PROTO_IM_IMAGE |
+ OPT_PROTO_PASSWORD_OPTIONAL |
OPT_PROTO_SLASH_COMMANDS_NATIVE,
NULL, /* user_splits */
NULL, /* protocol_options */
@@ -2086,12 +2104,10 @@ static PurplePluginProtocolInfo prpl_info =
silcpurple_get_chat_name, /* get_chat_name */
silcpurple_chat_invite, /* chat_invite */
silcpurple_chat_leave, /* chat_leave */
- NULL, /* chat_whisper */
silcpurple_chat_send, /* chat_send */
silcpurple_keepalive, /* keepalive */
NULL, /* register_user */
NULL, /* get_cb_info */
- NULL, /* get_cb_away */
NULL, /* alias_buddy */
NULL, /* group_buddy */
NULL, /* rename_group */
@@ -2116,15 +2132,13 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* unregister_user */
NULL, /* send_attention */
NULL, /* get_attention_types */
- sizeof(PurplePluginProtocolInfo), /* struct_size */
NULL, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* get_media_caps */
NULL, /* get_moods */
NULL, /* set_public_alias */
NULL, /* get_public_alias */
- NULL, /* add_buddy_with_invite */
- NULL /* add_buddies_with_invite */
+ NULL /* get_max_message_size */
};
static PurplePluginInfo info =
@@ -2183,8 +2197,6 @@ init_plugin(PurplePlugin *plugin)
PurpleKeyValuePair *kvp;
GList *list = NULL;
- silc_plugin = plugin;
-
split = purple_account_user_split_new(_("Network"), "silcnet.org", '@');
prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
diff --git a/libpurple/protocols/silc/silcpurple.h b/libpurple/protocols/silc/silcpurple.h
index 4ea031777b..f196664d35 100644
--- a/libpurple/protocols/silc/silcpurple.h
+++ b/libpurple/protocols/silc/silcpurple.h
@@ -27,7 +27,7 @@
#include "cmds.h"
#include "conversation.h"
#include "debug.h"
-#include "ft.h"
+#include "xfer.h"
#include "notify.h"
#include "prpl.h"
#include "request.h"
@@ -105,7 +105,7 @@ void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn,
SilcVerifyPublicKey completion,
void *context);
GList *silcpurple_buddy_menu(PurpleBuddy *buddy);
-void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
+void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *message);
void silcpurple_send_buddylist(PurpleConnection *gc);
void silcpurple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
void silcpurple_buddy_keyagr_request(SilcClient client,
@@ -142,7 +142,7 @@ char *silcpurple_get_chat_name(GHashTable *data);
void silcpurple_chat_invite(PurpleConnection *gc, int id, const char *msg,
const char *name);
void silcpurple_chat_leave(PurpleConnection *gc, int id);
-int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg, PurpleMessageFlags flags);
+int silcpurple_chat_send(PurpleConnection *gc, int id, PurpleMessage *msg);
void silcpurple_chat_set_topic(PurpleConnection *gc, int id, const char *topic);
PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc);
void silcpurple_roomlist_cancel(PurpleRoomlist *list);
@@ -151,8 +151,7 @@ void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel,
void silcpurple_parse_attrs(SilcDList attrs, char **moodstr, char **statusstr,
char **contactstr, char **langstr, char **devicestr,
char **tzstr, char **geostr);
-void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleStoredImage *img);
-char *silcpurple_file2mime(const char *filename);
+void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleImage *img);
SilcDList silcpurple_image_message(const char *msg, SilcMessageFlags *mflags);
#ifdef _WIN32
diff --git a/libpurple/protocols/silc/util.c b/libpurple/protocols/silc/util.c
index 88e22d5a50..f03d277813 100644
--- a/libpurple/protocols/silc/util.c
+++ b/libpurple/protocols/silc/util.c
@@ -18,10 +18,14 @@
*/
#include "internal.h"
+#include "glibcompat.h"
+#include "image-store.h"
+
+PURPLE_BEGIN_IGNORE_CAST_ALIGN
#include "silc.h"
+PURPLE_END_IGNORE_CAST_ALIGN
#include "silcclient.h"
#include "silcpurple.h"
-#include "imgstore.h"
/**************************** Utility Routines *******************************/
@@ -74,7 +78,7 @@ gboolean silcpurple_check_silc_dir(PurpleConnection *gc)
char filename[256], file_public_key[256], file_private_key[256];
char servfilename[256], clientfilename[256], friendsfilename[256];
char pkd[256], prd[256];
- struct stat st;
+ GStatBuf st;
struct passwd *pw;
int fd;
@@ -149,9 +153,9 @@ gboolean silcpurple_check_silc_dir(PurpleConnection *gc)
g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
g_snprintf(file_public_key, sizeof(file_public_key) - 1, "%s",
- purple_account_get_string(gc->account, "public-key", pkd));
+ purple_account_get_string(purple_connection_get_account(gc), "public-key", pkd));
g_snprintf(file_private_key, sizeof(file_public_key) - 1, "%s",
- purple_account_get_string(gc->account, "private-key", prd));
+ purple_account_get_string(purple_connection_get_account(gc), "private-key", prd));
if ((g_stat(file_public_key, &st)) == -1) {
/* If file doesn't exist */
@@ -161,10 +165,9 @@ gboolean silcpurple_check_silc_dir(PurpleConnection *gc)
SILCPURPLE_DEF_PKCS_LEN,
file_public_key,
file_private_key, NULL,
- (gc->password == NULL)
- ? "" : gc->password,
+ (purple_connection_get_password(gc) == NULL) ? "" : purple_connection_get_password(gc),
NULL, NULL, FALSE)) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
_("Unable to create SILC key pair"));
return FALSE;
}
@@ -190,7 +193,7 @@ gboolean silcpurple_check_silc_dir(PurpleConnection *gc)
#endif
if ((fd = g_open(file_private_key, O_RDONLY, 0)) != -1) {
- if ((fstat(fd, &st)) == -1) {
+ if (_purple_fstat(fd, &st) == -1) {
purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
file_private_key, g_strerror(errno));
close(fd);
@@ -204,16 +207,15 @@ gboolean silcpurple_check_silc_dir(PurpleConnection *gc)
SILCPURPLE_DEF_PKCS_LEN,
file_public_key,
file_private_key, NULL,
- (gc->password == NULL)
- ? "" : gc->password,
+ (purple_connection_get_password(gc) == NULL) ? "" : purple_connection_get_password(gc),
NULL, NULL, FALSE)) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
_("Unable to create SILC key pair"));
return FALSE;
}
if ((fd = g_open(file_private_key, O_RDONLY, 0)) != -1) {
- if ((fstat(fd, &st)) == -1) {
+ if (_purple_fstat(fd, &st) == -1) {
purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
file_private_key, g_strerror(errno));
close(fd);
@@ -260,6 +262,13 @@ gboolean silcpurple_check_silc_dir(PurpleConnection *gc)
if (fd != -1)
close(fd);
+#ifdef _WIN32
+ /* on win32, we calloc pw so pass it to free
+ * (see the getpwuid code below)
+ */
+ free(pw);
+#endif
+
return TRUE;
}
@@ -331,8 +340,8 @@ void silcpurple_show_public_key(SilcPurple sg,
purple_request_action(sg->gc, _("Public Key Information"),
_("Public Key Information"),
- s->str, 0, purple_connection_get_account(sg->gc),
- NULL, NULL, context, 1, _("Close"), callback);
+ s->str, 0, purple_request_cpar_from_connection(sg->gc),
+ context, 1, _("Close"), callback);
g_string_free(s, TRUE);
silc_free(fingerprint);
@@ -569,29 +578,6 @@ silcpurple_parse_attrs(SilcDList attrs, char **moodstr, char **statusstr,
geo.accuracy ? geo.accuracy : "");
}
-/* Returns MIME type of filetype */
-
-char *silcpurple_file2mime(const char *filename)
-{
- const char *ct;
-
- ct = strrchr(filename, '.');
- if (!ct)
- return NULL;
- else if (!g_ascii_strcasecmp(".png", ct))
- return g_strdup("image/png");
- else if (!g_ascii_strcasecmp(".jpg", ct))
- return g_strdup("image/jpeg");
- else if (!g_ascii_strcasecmp(".jpeg", ct))
- return g_strdup("image/jpeg");
- else if (!g_ascii_strcasecmp(".gif", ct))
- return g_strdup("image/gif");
- else if (!g_ascii_strcasecmp(".tiff", ct))
- return g_strdup("image/tiff");
-
- return NULL;
-}
-
/* Checks if message has images, and assembles MIME message if it has.
If only one image is present, creates simple MIME image message. If
there are multiple images and/or text with images multipart MIME
@@ -603,14 +589,13 @@ SilcDList silcpurple_image_message(const char *msg, SilcMessageFlags *mflags)
SilcDList list, parts = NULL;
const char *start, *end, *last;
GData *attribs;
- char *type;
gboolean images = FALSE;
last = msg;
while (last && *last && purple_markup_find_tag("img", last, &start,
&end, &attribs)) {
- PurpleStoredImage *image = NULL;
- const char *id;
+ PurpleImage *image = NULL;
+ const gchar *uri;
/* Check if there is text before image */
if (start - last) {
@@ -634,22 +619,24 @@ SilcDList silcpurple_image_message(const char *msg, SilcMessageFlags *mflags)
silc_dlist_add(parts, p);
}
- id = g_datalist_get_data(&attribs, "id");
- if (id && (image = purple_imgstore_find_by_id(atoi(id)))) {
- unsigned long imglen = purple_imgstore_get_size(image);
- gconstpointer img = purple_imgstore_get_data(image);
+ uri = g_datalist_get_data(&attribs, "src");
+ if (uri)
+ image = purple_image_store_get_from_uri(uri);
+ if (uri) {
+ unsigned long imglen = purple_image_get_size(image);
+ gconstpointer img = purple_image_get_data(image);
+ const gchar *type;
p = silc_mime_alloc();
/* Add content type */
- type = silcpurple_file2mime(purple_imgstore_get_filename(image));
+ type = purple_image_get_mimetype(image);
if (!type) {
g_datalist_clear(&attribs);
last = end + 1;
continue;
}
silc_mime_add_field(p, "Content-Type", type);
- g_free(type);
/* Add content transfer encoding */
silc_mime_add_field(p, "Content-Transfer-Encoding", "binary");
diff --git a/libpurple/protocols/silc/wb.c b/libpurple/protocols/silc/wb.c
index 6abb3012ce..01a916237b 100644
--- a/libpurple/protocols/silc/wb.c
+++ b/libpurple/protocols/silc/wb.c
@@ -18,7 +18,9 @@
*/
#include "internal.h"
+PURPLE_BEGIN_IGNORE_CAST_ALIGN
#include "silc.h"
+PURPLE_END_IGNORE_CAST_ALIGN
#include "silcclient.h"
#include "silcpurple.h"
#include "wb.h"
@@ -110,11 +112,11 @@ PurpleWhiteboard *silcpurple_wb_init(SilcPurple sg, SilcClientEntry client_entry
wb = purple_whiteboard_get_session(sg->account, client_entry->nickname);
if (!wb)
- wb = purple_whiteboard_create(sg->account, client_entry->nickname, 0);
+ wb = purple_whiteboard_new(sg->account, client_entry->nickname, 0);
if (!wb)
return NULL;
- if (!wb->proto_data) {
+ if (!purple_whiteboard_get_protocol_data(wb)) {
wbs = silc_calloc(1, sizeof(*wbs));
if (!wbs)
return NULL;
@@ -124,7 +126,7 @@ PurpleWhiteboard *silcpurple_wb_init(SilcPurple sg, SilcClientEntry client_entry
wbs->height = SILCPURPLE_WB_HEIGHT;
wbs->brush_size = SILCPURPLE_WB_BRUSH_SMALL;
wbs->brush_color = SILCPURPLE_WB_COLOR_BLACK;
- wb->proto_data = wbs;
+ purple_whiteboard_set_protocol_data(wb, wbs);
/* Start the whiteboard */
purple_whiteboard_start(wb);
@@ -141,11 +143,11 @@ PurpleWhiteboard *silcpurple_wb_init_ch(SilcPurple sg, SilcChannelEntry channel)
wb = purple_whiteboard_get_session(sg->account, channel->channel_name);
if (!wb)
- wb = purple_whiteboard_create(sg->account, channel->channel_name, 0);
+ wb = purple_whiteboard_new(sg->account, channel->channel_name, 0);
if (!wb)
return NULL;
- if (!wb->proto_data) {
+ if (!purple_whiteboard_get_protocol_data(wb)) {
wbs = silc_calloc(1, sizeof(*wbs));
if (!wbs)
return NULL;
@@ -155,7 +157,7 @@ PurpleWhiteboard *silcpurple_wb_init_ch(SilcPurple sg, SilcChannelEntry channel)
wbs->height = SILCPURPLE_WB_HEIGHT;
wbs->brush_size = SILCPURPLE_WB_BRUSH_SMALL;
wbs->brush_color = SILCPURPLE_WB_COLOR_BLACK;
- wb->proto_data = wbs;
+ purple_whiteboard_set_protocol_data(wb, wbs);
/* Start the whiteboard */
purple_whiteboard_start(wb);
@@ -166,9 +168,10 @@ PurpleWhiteboard *silcpurple_wb_init_ch(SilcPurple sg, SilcChannelEntry channel)
}
static void
-silcpurple_wb_parse(SilcPurpleWb wbs, PurpleWhiteboard *wb,
+silcpurple_wb_parse(PurpleWhiteboard *wb,
unsigned char *message, SilcUInt32 message_len)
{
+ SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
SilcUInt8 command;
SilcUInt16 width, height, brush_size;
SilcUInt32 brush_color, x, y, dx, dy;
@@ -236,15 +239,15 @@ silcpurple_wb_request_cb(SilcPurpleWbRequest req, gint id)
{
PurpleWhiteboard *wb;
- if (id != 1)
- goto out;
+ if (id != 1)
+ goto out;
if (!req->channel)
wb = silcpurple_wb_init(req->sg, req->sender);
else
wb = silcpurple_wb_init_ch(req->sg, req->channel);
- silcpurple_wb_parse(wb->proto_data, wb, req->message, req->message_len);
+ silcpurple_wb_parse(wb, req->message, req->message_len);
out:
silc_free(req->message);
@@ -262,7 +265,7 @@ silcpurple_wb_request(SilcClient client, const unsigned char *message,
SilcPurple sg;
gc = client->application;
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
/* Open whiteboard automatically if requested */
if (purple_account_get_bool(sg->account, "open-wb", FALSE)) {
@@ -273,7 +276,7 @@ silcpurple_wb_request(SilcClient client, const unsigned char *message,
else
wb = silcpurple_wb_init_ch(sg, channel);
- silcpurple_wb_parse(wb->proto_data, wb,
+ silcpurple_wb_parse(wb,
(unsigned char *)message,
message_len);
return;
@@ -303,7 +306,7 @@ silcpurple_wb_request(SilcClient client, const unsigned char *message,
req->sg = sg;
purple_request_action(gc, _("Whiteboard"), tmp, NULL, 1,
- sg->account, sender->nickname, NULL, req, 2,
+ purple_request_cpar_from_connection(gc), req, 2,
_("Yes"), G_CALLBACK(silcpurple_wb_request_cb),
_("No"), G_CALLBACK(silcpurple_wb_request_cb));
}
@@ -316,12 +319,11 @@ void silcpurple_wb_receive(SilcClient client, SilcClientConnection conn,
SilcUInt32 message_len)
{
SilcPurple sg;
- PurpleConnection *gc;
+ PurpleConnection *gc;
PurpleWhiteboard *wb;
- SilcPurpleWb wbs;
gc = client->application;
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
wb = purple_whiteboard_get_session(sg->account, sender->nickname);
if (!wb) {
@@ -331,8 +333,7 @@ void silcpurple_wb_receive(SilcClient client, SilcClientConnection conn,
return;
}
- wbs = wb->proto_data;
- silcpurple_wb_parse(wbs, wb, (unsigned char *)message, message_len);
+ silcpurple_wb_parse(wb, (unsigned char *)message, message_len);
}
/* Process incoming whiteboard message on channel */
@@ -345,12 +346,11 @@ void silcpurple_wb_receive_ch(SilcClient client, SilcClientConnection conn,
SilcUInt32 message_len)
{
SilcPurple sg;
- PurpleConnection *gc;
+ PurpleConnection *gc;
PurpleWhiteboard *wb;
- SilcPurpleWb wbs;
gc = client->application;
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
wb = purple_whiteboard_get_session(sg->account, channel->channel_name);
if (!wb) {
@@ -360,25 +360,24 @@ void silcpurple_wb_receive_ch(SilcClient client, SilcClientConnection conn,
return;
}
- wbs = wb->proto_data;
- silcpurple_wb_parse(wbs, wb, (unsigned char *)message, message_len);
+ silcpurple_wb_parse(wb, (unsigned char *)message, message_len);
}
/* Send whiteboard message */
void silcpurple_wb_send(PurpleWhiteboard *wb, GList *draw_list)
{
- SilcPurpleWb wbs = wb->proto_data;
+ SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
SilcBuffer packet;
GList *list;
int len;
- PurpleConnection *gc;
- SilcPurple sg;
+ PurpleConnection *gc;
+ SilcPurple sg;
g_return_if_fail(draw_list);
- gc = purple_account_get_connection(wb->account);
+ gc = purple_account_get_connection(purple_whiteboard_get_account(wb));
g_return_if_fail(gc);
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
g_return_if_fail(sg);
len = SILCPURPLE_WB_HEADER;
@@ -433,20 +432,22 @@ void silcpurple_wb_start(PurpleWhiteboard *wb)
void silcpurple_wb_end(PurpleWhiteboard *wb)
{
- silc_free(wb->proto_data);
- wb->proto_data = NULL;
+ SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
+
+ silc_free(wbs);
+ purple_whiteboard_set_protocol_data(wb, NULL);
}
void silcpurple_wb_get_dimensions(const PurpleWhiteboard *wb, int *width, int *height)
{
- SilcPurpleWb wbs = wb->proto_data;
+ SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
*width = wbs->width;
*height = wbs->height;
}
void silcpurple_wb_set_dimensions(PurpleWhiteboard *wb, int width, int height)
{
- SilcPurpleWb wbs = wb->proto_data;
+ SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
wbs->width = width > SILCPURPLE_WB_WIDTH_MAX ? SILCPURPLE_WB_WIDTH_MAX :
width;
wbs->height = height > SILCPURPLE_WB_HEIGHT_MAX ? SILCPURPLE_WB_HEIGHT_MAX :
@@ -458,14 +459,14 @@ void silcpurple_wb_set_dimensions(PurpleWhiteboard *wb, int width, int height)
void silcpurple_wb_get_brush(const PurpleWhiteboard *wb, int *size, int *color)
{
- SilcPurpleWb wbs = wb->proto_data;
+ SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
*size = wbs->brush_size;
*color = wbs->brush_color;
}
void silcpurple_wb_set_brush(PurpleWhiteboard *wb, int size, int color)
{
- SilcPurpleWb wbs = wb->proto_data;
+ SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
wbs->brush_size = size;
wbs->brush_color = color;
@@ -475,15 +476,15 @@ void silcpurple_wb_set_brush(PurpleWhiteboard *wb, int size, int color)
void silcpurple_wb_clear(PurpleWhiteboard *wb)
{
- SilcPurpleWb wbs = wb->proto_data;
+ SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
SilcBuffer packet;
int len;
- PurpleConnection *gc;
- SilcPurple sg;
+ PurpleConnection *gc;
+ SilcPurple sg;
- gc = purple_account_get_connection(wb->account);
+ gc = purple_account_get_connection(purple_whiteboard_get_account(wb));
g_return_if_fail(gc);
- sg = gc->proto_data;
+ sg = purple_connection_get_protocol_data(gc);
g_return_if_fail(sg);
len = SILCPURPLE_WB_HEADER;
diff --git a/libpurple/protocols/silc10/Makefile.am b/libpurple/protocols/silc10/Makefile.am
deleted file mode 100644
index 87db235724..0000000000
--- a/libpurple/protocols/silc10/Makefile.am
+++ /dev/null
@@ -1,36 +0,0 @@
-EXTRA_DIST = \
- Makefile.mingw \
- README \
- TODO
-
-pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
-
-SILCSOURCES = silc.c silcpurple.h buddy.c chat.c ft.c ops.c pk.c util.c wb.c wb.h
-
-AM_CFLAGS = $(st)
-
-libsilcpurple_la_LDFLAGS = -module -avoid-version
-
-if STATIC_SILC
-
-st = -DPURPLE_STATIC_PRPL $(SILC_CFLAGS)
-noinst_LTLIBRARIES = libsilcpurple.la
-libsilcpurple_la_SOURCES = $(SILCSOURCES)
-libsilcpurple_la_CFLAGS = $(AM_CFLAGS)
-libsilcpurple_la_LIBADD = $(SILC_LIBS)
-
-else
-
-st = $(SILC_CFLAGS)
-pkg_LTLIBRARIES = libsilcpurple.la
-libsilcpurple_la_SOURCES = $(SILCSOURCES)
-libsilcpurple_la_LIBADD = $(GLIB_LIBS) $(SILC_LIBS)
-
-endif
-
-AM_CPPFLAGS = \
- -I$(top_srcdir)/libpurple \
- -I$(top_builddir)/libpurple \
- $(DEBUG_CFLAGS) \
- $(GLIB_CFLAGS) \
- $(SILC_CFLAGS)
diff --git a/libpurple/protocols/silc10/Makefile.mingw b/libpurple/protocols/silc10/Makefile.mingw
deleted file mode 100644
index d087b331a8..0000000000
--- a/libpurple/protocols/silc10/Makefile.mingw
+++ /dev/null
@@ -1,93 +0,0 @@
-#
-# Makefile.mingw
-#
-# Description: Makefile for win32 (mingw) version of libsilc protocol plugin
-#
-
-PIDGIN_TREE_TOP := ../../..
-include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
-
-DEFINES := $(subst -DWIN32_LEAN_AND_MEAN,,$(DEFINES))
-
-TARGET = libsilc
-NEEDED_DLLS = $(SILC_TOOLKIT)/lib/silc.dll \
- $(SILC_TOOLKIT)/lib/silcclient.dll
-TYPE = PLUGIN
-
-# Static or Plugin...
-ifeq ($(TYPE),STATIC)
- DEFINES += -DSTATIC
- DLL_INSTALL_DIR = $(PURPLE_INSTALL_DIR)
-else
-ifeq ($(TYPE),PLUGIN)
- DLL_INSTALL_DIR = $(PURPLE_INSTALL_PLUGINS_DIR)
-endif
-endif
-
-##
-## INCLUDE PATHS
-##
-INCLUDE_PATHS += -I. \
- -I$(GTK_TOP)/include \
- -I$(GTK_TOP)/include/glib-2.0 \
- -I$(GTK_TOP)/lib/glib-2.0/include \
- -I$(PURPLE_TOP) \
- -I$(PURPLE_TOP)/win32 \
- -I$(PIDGIN_TREE_TOP) \
- -I$(SILC_TOOLKIT)/include
-
-LIB_PATHS += -L$(GTK_TOP)/lib \
- -L$(PURPLE_TOP) \
- -L$(SILC_TOOLKIT)/lib
-
-##
-## SOURCES, OBJECTS
-##
-C_SRC = silc.c \
- buddy.c \
- chat.c \
- ft.c \
- ops.c \
- pk.c \
- util.c \
- wb.c
-
-OBJECTS = $(C_SRC:%.c=%.o)
-
-##
-## LIBRARIES
-##
-LIBS = \
- -lglib-2.0 \
- -lws2_32 \
- -lintl \
- -lpurple \
- -lsilc \
- -lsilcclient
-
-include $(PIDGIN_COMMON_RULES)
-
-##
-## TARGET DEFINITIONS
-##
-.PHONY: all install clean
-
-all: $(TARGET).dll
-
-install: all $(DLL_INSTALL_DIR) $(PURPLE_INSTALL_DIR)
- cp $(TARGET).dll $(DLL_INSTALL_DIR)
- cp $(NEEDED_DLLS) $(PURPLE_INSTALL_DIR)
-
-$(OBJECTS): $(PURPLE_CONFIG_H)
-
-$(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS)
- $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -Wl,--image-base,0x64000000 -o $(TARGET).dll
-
-##
-## CLEAN RULES
-##
-clean:
- rm -f $(OBJECTS)
- rm -f $(TARGET).dll
-
-include $(PIDGIN_COMMON_TARGETS)
diff --git a/libpurple/protocols/silc10/README b/libpurple/protocols/silc10/README
deleted file mode 100644
index 90cbdb5025..0000000000
--- a/libpurple/protocols/silc10/README
+++ /dev/null
@@ -1,31 +0,0 @@
-SILC Purple Plugin
-==================
-
-This is the Purple protocol plugin of the protocol called Secure Internet
-Live Conferencing (SILC). The implementation will use the SILC Toolkit,
-freely available from the http://silcnet.org/ site, for the actual SILC
-protocol implementation.
-
-To include SILC into Purple, one needs to first compile and install
-the SILC Toolkit. It is done as follows:
-
- ./configure --enable-shared
- make
- make install
-
-This will compile shared libraries of the SILC Toolkit. If the --prefix
-is not given to ./configure, the binaries are installed into the
-/usr/local/silc directory.
-
-Once the Toolkit is installed one needs to tell Purple's ./configure
-script where the SILC Toolkit is located. It is done as simply as:
-
- ./configure
-
-if pkg-config is installed in your system. If it is isn't it's done as:
-
- ./configure --with-silc-libs=/path/to/silc/lib
- --with-silc-includes=/path/to/silc/include
-
-If the SILC Toolkit cannot be found then the SILC protocol plugin will
-not be compiled.
diff --git a/libpurple/protocols/silc10/TODO b/libpurple/protocols/silc10/TODO
deleted file mode 100644
index 285ca58485..0000000000
--- a/libpurple/protocols/silc10/TODO
+++ /dev/null
@@ -1,8 +0,0 @@
-Features TODO (maybe)
-=====================
-
-Preferences
- - Add joined channels to buddy list automatically (during
- session)
- - Add joined channels to buddy list automatically permanently
-
diff --git a/libpurple/protocols/silc10/buddy.c b/libpurple/protocols/silc10/buddy.c
deleted file mode 100644
index aaedee6088..0000000000
--- a/libpurple/protocols/silc10/buddy.c
+++ /dev/null
@@ -1,1748 +0,0 @@
-/*
-
- silcpurple_buddy.c
-
- Author: Pekka Riikonen <priikone@silcnet.org>
-
- Copyright (C) 2004 Pekka Riikonen
-
- 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; version 2 of the License.
-
- 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.
-
-*/
-
-#include "silcincludes.h"
-#include "silcclient.h"
-#include "silcpurple.h"
-#include "wb.h"
-
-/***************************** Key Agreement *********************************/
-
-static void
-silcpurple_buddy_keyagr(PurpleBlistNode *node, gpointer data);
-
-static void
-silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name,
- gboolean force_local);
-
-typedef struct {
- char *nick;
- PurpleConnection *gc;
-} *SilcPurpleResolve;
-
-static void
-silcpurple_buddy_keyagr_resolved(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry *clients,
- SilcUInt32 clients_count,
- void *context)
-{
- PurpleConnection *gc = client->application;
- SilcPurpleResolve r = context;
- char tmp[256];
-
- if (!clients) {
- g_snprintf(tmp, sizeof(tmp),
- _("User %s is not present in the network"), r->nick);
- purple_notify_error(gc, _("Key Agreement"),
- _("Cannot perform the key agreement"), tmp);
- silc_free(r->nick);
- silc_free(r);
- return;
- }
-
- silcpurple_buddy_keyagr_do(gc, r->nick, FALSE);
- silc_free(r->nick);
- silc_free(r);
-}
-
-typedef struct {
- gboolean responder;
-} *SilcPurpleKeyAgr;
-
-static void
-silcpurple_buddy_keyagr_cb(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry client_entry,
- SilcKeyAgreementStatus status,
- SilcSKEKeyMaterial *key,
- void *context)
-{
- PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
- SilcPurpleKeyAgr a = context;
-
- if (!sg->conn)
- return;
-
- switch (status) {
- case SILC_KEY_AGREEMENT_OK:
- {
- PurpleConversation *convo;
- char tmp[128];
-
- /* Set the private key for this client */
- silc_client_del_private_message_key(client, conn, client_entry);
- silc_client_add_private_message_key_ske(client, conn, client_entry,
- NULL, NULL, key, a->responder);
- silc_ske_free_key_material(key);
-
-
- /* Open IM window */
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
- client_entry->nickname, sg->account);
- if (convo) {
- /* we don't have windows in the core anymore...but we may want to
- * provide some method for asking the UI to show the window
- purple_conv_window_show(purple_conversation_get_window(convo));
- */
- } else {
- convo = purple_conversation_new(PURPLE_CONV_TYPE_IM, sg->account,
- client_entry->nickname);
- }
- g_snprintf(tmp, sizeof(tmp), "%s [private key]", client_entry->nickname);
- purple_conversation_set_title(convo, tmp);
- }
- break;
-
- case SILC_KEY_AGREEMENT_ERROR:
- purple_notify_error(gc, _("Key Agreement"),
- _("Error occurred during key agreement"), NULL);
- break;
-
- case SILC_KEY_AGREEMENT_FAILURE:
- purple_notify_error(gc, _("Key Agreement"), _("Key Agreement failed"), NULL);
- break;
-
- case SILC_KEY_AGREEMENT_TIMEOUT:
- purple_notify_error(gc, _("Key Agreement"),
- _("Timeout during key agreement"), NULL);
- break;
-
- case SILC_KEY_AGREEMENT_ABORTED:
- purple_notify_error(gc, _("Key Agreement"),
- _("Key agreement was aborted"), NULL);
- break;
-
- case SILC_KEY_AGREEMENT_ALREADY_STARTED:
- purple_notify_error(gc, _("Key Agreement"),
- _("Key agreement is already started"), NULL);
- break;
-
- case SILC_KEY_AGREEMENT_SELF_DENIED:
- purple_notify_error(gc, _("Key Agreement"),
- _("Key agreement cannot be started with yourself"),
- NULL);
- break;
-
- default:
- break;
- }
-
- silc_free(a);
-}
-
-static void
-silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name,
- gboolean force_local)
-{
- SilcPurple sg = gc->proto_data;
- SilcClientEntry *clients;
- SilcUInt32 clients_count;
- char *local_ip = NULL, *remote_ip = NULL;
- gboolean local = TRUE;
- char *nickname;
- SilcPurpleKeyAgr a;
-
- if (!sg->conn || !name)
- return;
-
- if (!silc_parse_userfqdn(name, &nickname, NULL))
- return;
-
- /* Find client entry */
- clients = silc_client_get_clients_local(sg->client, sg->conn, nickname, name,
- &clients_count);
- if (!clients) {
- /* Resolve unknown user */
- SilcPurpleResolve r = silc_calloc(1, sizeof(*r));
- if (!r)
- return;
- r->nick = g_strdup(name);
- r->gc = gc;
- silc_client_get_clients(sg->client, sg->conn, nickname, NULL,
- silcpurple_buddy_keyagr_resolved, r);
- silc_free(nickname);
- return;
- }
-
- /* Resolve the local IP from the outgoing socket connection. We resolve
- it to check whether we have a private range IP address or public IP
- address. If we have public then we will assume that we are not behind
- NAT and will provide automatically the point of connection to the
- agreement. If we have private range address we assume that we are
- behind NAT and we let the responder provide the point of connection.
-
- The algorithm also checks the remote IP address of server connection.
- If it is private range address and we have private range address we
- assume that we are chatting in LAN and will provide the point of
- connection.
-
- Naturally this algorithm does not always get things right. */
-
- if (silc_net_check_local_by_sock(sg->conn->sock->sock, NULL, &local_ip)) {
- /* Check if the IP is private */
- if (!force_local && silcpurple_ip_is_private(local_ip)) {
- local = FALSE;
-
- /* Local IP is private, resolve the remote server IP to see whether
- we are talking to Internet or just on LAN. */
- if (silc_net_check_host_by_sock(sg->conn->sock->sock, NULL,
- &remote_ip))
- if (silcpurple_ip_is_private(remote_ip))
- /* We assume we are in LAN. Let's provide
- the connection point. */
- local = TRUE;
- }
- }
-
- if (force_local)
- local = TRUE;
-
- if (local && !local_ip)
- local_ip = silc_net_localip();
-
- a = silc_calloc(1, sizeof(*a));
- if (!a)
- return;
- a->responder = local;
-
- /* Send the key agreement request */
- silc_client_send_key_agreement(sg->client, sg->conn, clients[0],
- local ? local_ip : NULL, NULL, 0, 60,
- silcpurple_buddy_keyagr_cb, a);
-
- silc_free(local_ip);
- silc_free(remote_ip);
- silc_free(clients);
-}
-
-typedef struct {
- SilcClient client;
- SilcClientConnection conn;
- SilcClientID client_id;
- char *hostname;
- SilcUInt16 port;
-} *SilcPurpleKeyAgrAsk;
-
-static void
-silcpurple_buddy_keyagr_request_cb(SilcPurpleKeyAgrAsk a, gint id)
-{
- SilcPurpleKeyAgr ai;
- SilcClientEntry client_entry;
-
- if (id != 1)
- goto out;
-
- /* Get the client entry. */
- client_entry = silc_client_get_client_by_id(a->client, a->conn,
- &a->client_id);
- if (!client_entry) {
- purple_notify_error(a->client->application, _("Key Agreement"),
- _("The remote user is not present in the network any more"),
- NULL);
- goto out;
- }
-
- /* If the hostname was provided by the requestor perform the key agreement
- now. Otherwise, we will send him a request to connect to us. */
- if (a->hostname) {
- ai = silc_calloc(1, sizeof(*ai));
- if (!ai)
- goto out;
- ai->responder = FALSE;
- silc_client_perform_key_agreement(a->client, a->conn, client_entry,
- a->hostname, a->port,
- silcpurple_buddy_keyagr_cb, ai);
- } else {
- /* Send request. Force us as the point of connection since requestor
- did not provide the point of connection. */
- silcpurple_buddy_keyagr_do(a->client->application,
- client_entry->nickname, TRUE);
- }
-
- out:
- silc_free(a->hostname);
- silc_free(a);
-}
-
-void silcpurple_buddy_keyagr_request(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry client_entry,
- const char *hostname, SilcUInt16 port)
-{
- char tmp[128], tmp2[128];
- SilcPurpleKeyAgrAsk a;
- PurpleConnection *gc = client->application;
-
- g_snprintf(tmp, sizeof(tmp),
- _("Key agreement request received from %s. Would you like to "
- "perform the key agreement?"), client_entry->nickname);
- if (hostname)
- g_snprintf(tmp2, sizeof(tmp2),
- _("The remote user is waiting key agreement on:\n"
- "Remote host: %s\nRemote port: %d"), hostname, port);
-
- a = silc_calloc(1, sizeof(*a));
- if (!a)
- return;
- a->client = client;
- a->conn = conn;
- a->client_id = *client_entry->id;
- if (hostname)
- a->hostname = strdup(hostname);
- a->port = port;
-
- purple_request_action(client->application, _("Key Agreement Request"), tmp,
- hostname ? tmp2 : NULL, 1, gc->account, client_entry->nickname,
- NULL, a, 2, _("Yes"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb),
- _("No"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb));
-}
-
-static void
-silcpurple_buddy_keyagr(PurpleBlistNode *node, gpointer data)
-{
- PurpleBuddy *buddy;
-
- buddy = (PurpleBuddy *)node;
- silcpurple_buddy_keyagr_do(buddy->account->gc, buddy->name, FALSE);
-}
-
-
-/**************************** Static IM Key **********************************/
-
-static void
-silcpurple_buddy_resetkey(PurpleBlistNode *node, gpointer data)
-{
- PurpleBuddy *b;
- PurpleConnection *gc;
- SilcPurple sg;
- char *nickname;
- SilcClientEntry *clients;
- SilcUInt32 clients_count;
-
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
-
- b = (PurpleBuddy *) node;
- gc = purple_account_get_connection(b->account);
- sg = gc->proto_data;
-
- if (!silc_parse_userfqdn(b->name, &nickname, NULL))
- return;
-
- /* Find client entry */
- clients = silc_client_get_clients_local(sg->client, sg->conn,
- nickname, b->name,
- &clients_count);
- if (!clients) {
- silc_free(nickname);
- return;
- }
-
- clients[0]->prv_resp = FALSE;
- silc_client_del_private_message_key(sg->client, sg->conn,
- clients[0]);
- silc_free(clients);
- silc_free(nickname);
-}
-
-typedef struct {
- SilcClient client;
- SilcClientConnection conn;
- SilcClientID client_id;
-} *SilcPurplePrivkey;
-
-static void
-silcpurple_buddy_privkey(PurpleConnection *gc, const char *name);
-
-static void
-silcpurple_buddy_privkey_cb(SilcPurplePrivkey p, const char *passphrase)
-{
- SilcClientEntry client_entry;
-
- if (!passphrase || !(*passphrase)) {
- silc_free(p);
- return;
- }
-
- /* Get the client entry. */
- client_entry = silc_client_get_client_by_id(p->client, p->conn,
- &p->client_id);
- if (!client_entry) {
- purple_notify_error(p->client->application, _("IM With Password"),
- _("The remote user is not present in the network any more"),
- NULL);
- silc_free(p);
- return;
- }
-
- /* Set the private message key */
- silc_client_del_private_message_key(p->client, p->conn,
- client_entry);
- silc_client_add_private_message_key(p->client, p->conn,
- client_entry, NULL, NULL,
- (unsigned char *)passphrase,
- strlen(passphrase), FALSE,
- client_entry->prv_resp);
- if (!client_entry->prv_resp)
- silc_client_send_private_message_key_request(p->client,
- p->conn,
- client_entry);
- silc_free(p);
-}
-
-static void
-silcpurple_buddy_privkey_resolved(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry *clients,
- SilcUInt32 clients_count,
- void *context)
-{
- char tmp[256];
-
- if (!clients) {
- g_snprintf(tmp, sizeof(tmp),
- _("User %s is not present in the network"),
- (const char *)context);
- purple_notify_error(client->application, _("IM With Password"),
- _("Cannot set IM key"), tmp);
- g_free(context);
- return;
- }
-
- silcpurple_buddy_privkey(client->application, context);
- silc_free(context);
-}
-
-static void
-silcpurple_buddy_privkey(PurpleConnection *gc, const char *name)
-{
- SilcPurple sg = gc->proto_data;
- char *nickname;
- SilcPurplePrivkey p;
- SilcClientEntry *clients;
- SilcUInt32 clients_count;
-
- if (!name)
- return;
- if (!silc_parse_userfqdn(name, &nickname, NULL))
- return;
-
- /* Find client entry */
- clients = silc_client_get_clients_local(sg->client, sg->conn,
- nickname, name,
- &clients_count);
- if (!clients) {
- silc_client_get_clients(sg->client, sg->conn, nickname, NULL,
- silcpurple_buddy_privkey_resolved,
- g_strdup(name));
- silc_free(nickname);
- return;
- }
-
- p = silc_calloc(1, sizeof(*p));
- if (!p)
- return;
- p->client = sg->client;
- p->conn = sg->conn;
- p->client_id = *clients[0]->id;
- purple_request_input(gc, _("IM With Password"), NULL,
- _("Set IM Password"), NULL, FALSE, TRUE, NULL,
- _("OK"), G_CALLBACK(silcpurple_buddy_privkey_cb),
- _("Cancel"), G_CALLBACK(silcpurple_buddy_privkey_cb),
- gc->account, NULL, NULL, p);
-
- silc_free(clients);
- silc_free(nickname);
-}
-
-static void
-silcpurple_buddy_privkey_menu(PurpleBlistNode *node, gpointer data)
-{
- PurpleBuddy *buddy;
- PurpleConnection *gc;
-
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
-
- buddy = (PurpleBuddy *) node;
- gc = purple_account_get_connection(buddy->account);
-
- silcpurple_buddy_privkey(gc, buddy->name);
-}
-
-
-/**************************** Get Public Key *********************************/
-
-typedef struct {
- SilcClient client;
- SilcClientConnection conn;
- SilcClientID client_id;
-} *SilcPurpleBuddyGetkey;
-
-static void
-silcpurple_buddy_getkey(PurpleConnection *gc, const char *name);
-
-static void
-silcpurple_buddy_getkey_cb(SilcPurpleBuddyGetkey g,
- SilcClientCommandReplyContext cmd)
-{
- SilcClientEntry client_entry;
- unsigned char *pk;
- SilcUInt32 pk_len;
-
- /* Get the client entry. */
- client_entry = silc_client_get_client_by_id(g->client, g->conn,
- &g->client_id);
- if (!client_entry) {
- purple_notify_error(g->client->application, _("Get Public Key"),
- _("The remote user is not present in the network any more"),
- NULL);
- silc_free(g);
- return;
- }
-
- if (!client_entry->public_key) {
- silc_free(g);
- return;
- }
-
- /* Now verify the public key */
- pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
- silcpurple_verify_public_key(g->client, g->conn, client_entry->nickname,
- SILC_SOCKET_TYPE_CLIENT,
- pk, pk_len, SILC_SKE_PK_TYPE_SILC,
- NULL, NULL);
- silc_free(pk);
- silc_free(g);
-}
-
-static void
-silcpurple_buddy_getkey_resolved(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry *clients,
- SilcUInt32 clients_count,
- void *context)
-{
- char tmp[256];
-
- if (!clients) {
- g_snprintf(tmp, sizeof(tmp),
- _("User %s is not present in the network"),
- (const char *)context);
- purple_notify_error(client->application, _("Get Public Key"),
- _("Cannot fetch the public key"), tmp);
- g_free(context);
- return;
- }
-
- silcpurple_buddy_getkey(client->application, context);
- silc_free(context);
-}
-
-static void
-silcpurple_buddy_getkey(PurpleConnection *gc, const char *name)
-{
- SilcPurple sg = gc->proto_data;
- SilcClient client = sg->client;
- SilcClientConnection conn = sg->conn;
- SilcClientEntry *clients;
- SilcUInt32 clients_count;
- SilcPurpleBuddyGetkey g;
- char *nickname;
-
- if (!name)
- return;
-
- if (!silc_parse_userfqdn(name, &nickname, NULL))
- return;
-
- /* Find client entry */
- clients = silc_client_get_clients_local(client, conn, nickname, name,
- &clients_count);
- if (!clients) {
- silc_client_get_clients(client, conn, nickname, NULL,
- silcpurple_buddy_getkey_resolved,
- g_strdup(name));
- silc_free(nickname);
- return;
- }
-
- /* Call GETKEY */
- g = silc_calloc(1, sizeof(*g));
- if (!g)
- return;
- g->client = client;
- g->conn = conn;
- g->client_id = *clients[0]->id;
- silc_client_command_call(client, conn, NULL, "GETKEY",
- clients[0]->nickname, NULL);
- silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
- conn->cmd_ident,
- (SilcCommandCb)silcpurple_buddy_getkey_cb, g);
- silc_free(clients);
- silc_free(nickname);
-}
-
-static void
-silcpurple_buddy_getkey_menu(PurpleBlistNode *node, gpointer data)
-{
- PurpleBuddy *buddy;
- PurpleConnection *gc;
-
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
-
- buddy = (PurpleBuddy *) node;
- gc = purple_account_get_connection(buddy->account);
-
- silcpurple_buddy_getkey(gc, buddy->name);
-}
-
-static void
-silcpurple_buddy_showkey(PurpleBlistNode *node, gpointer data)
-{
- PurpleBuddy *b;
- PurpleConnection *gc;
- SilcPurple sg;
- SilcPublicKey public_key;
- const char *pkfile;
-
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
-
- b = (PurpleBuddy *) node;
- gc = purple_account_get_connection(b->account);
- sg = gc->proto_data;
-
- pkfile = purple_blist_node_get_string(node, "public-key");
- if (!silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_PEM) &&
- !silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_BIN)) {
- purple_notify_error(gc,
- _("Show Public Key"),
- _("Could not load public key"), NULL);
- return;
- }
-
- silcpurple_show_public_key(sg, b->name, public_key, NULL, NULL);
- silc_pkcs_public_key_free(public_key);
-}
-
-
-/**************************** Buddy routines *********************************/
-
-/* The buddies are implemented by using the WHOIS and WATCH commands that
- can be used to search users by their public key. Since nicknames aren't
- unique in SILC we cannot trust the buddy list using their nickname. We
- associate public keys to buddies and use those to search and watch
- in the network.
-
- The problem is that Purple does not return PurpleBuddy contexts to the
- callbacks but the buddy names. Naturally, this is not going to work
- with SILC. But, for now, we have to do what we can... */
-
-typedef struct {
- SilcClient client;
- SilcClientConnection conn;
- SilcClientID client_id;
- PurpleBuddy *b;
- unsigned char *offline_pk;
- SilcUInt32 offline_pk_len;
- unsigned int offline : 1;
- unsigned int pubkey_search : 1;
- unsigned int init : 1;
-} *SilcPurpleBuddyRes;
-
-static void
-silcpurple_add_buddy_ask_pk_cb(SilcPurpleBuddyRes r, gint id);
-static void
-silcpurple_add_buddy_resolved(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry *clients,
- SilcUInt32 clients_count,
- void *context);
-
-void silcpurple_get_info(PurpleConnection *gc, const char *who)
-{
- SilcPurple sg = gc->proto_data;
- SilcClient client = sg->client;
- SilcClientConnection conn = sg->conn;
- SilcClientEntry client_entry;
- PurpleBuddy *b;
- const char *filename, *nick = who;
- char tmp[256];
-
- if (!who)
- return;
- if (strlen(who) > 1 && who[0] == '@')
- nick = who + 1;
- if (strlen(who) > 1 && who[0] == '*')
- nick = who + 1;
- if (strlen(who) > 2 && who[0] == '*' && who[1] == '@')
- nick = who + 2;
-
- b = purple_find_buddy(gc->account, nick);
- if (b) {
- /* See if we have this buddy's public key. If we do use that
- to search the details. */
- filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key");
- if (filename) {
- /* Call WHOIS. The user info is displayed in the WHOIS
- command reply. */
- silc_client_command_call(client, conn, NULL, "WHOIS",
- "-details", "-pubkey", filename, NULL);
- return;
- }
-
- if (!b->proto_data) {
- g_snprintf(tmp, sizeof(tmp),
- _("User %s is not present in the network"), b->name);
- purple_notify_error(gc, _("User Information"),
- _("Cannot get user information"), tmp);
- return;
- }
-
- client_entry = silc_client_get_client_by_id(client, conn, b->proto_data);
- if (client_entry) {
- /* Call WHOIS. The user info is displayed in the WHOIS
- command reply. */
- silc_client_command_call(client, conn, NULL, "WHOIS",
- client_entry->nickname, "-details", NULL);
- }
- } else {
- /* Call WHOIS just with nickname. */
- silc_client_command_call(client, conn, NULL, "WHOIS", nick, NULL);
- }
-}
-
-static void
-silcpurple_add_buddy_pk_no(SilcPurpleBuddyRes r)
-{
- char tmp[512];
- g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not trusted"),
- r->b->name);
- purple_notify_error(r->client->application, _("Add Buddy"), tmp,
- _("You cannot receive buddy notifications until you "
- "import his/her public key. You can use the Get Public Key "
- "command to get the public key."));
- purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL);
-}
-
-static void
-silcpurple_add_buddy_save(bool success, void *context)
-{
- SilcPurpleBuddyRes r = context;
- PurpleBuddy *b = r->b;
- SilcClient client = r->client;
- SilcClientEntry client_entry;
- SilcAttributePayload attr;
- SilcAttribute attribute;
- SilcVCardStruct vcard;
- SilcAttributeObjMime message, extension;
-#ifdef SILC_ATTRIBUTE_USER_ICON
- SilcAttributeObjMime usericon;
-#endif
- SilcAttributeObjPk serverpk, usersign, serversign;
- gboolean usign_success = TRUE, ssign_success = TRUE;
- char filename[512], filename2[512], *fingerprint = NULL, *tmp;
- SilcUInt32 len;
- int i;
-
- if (!success) {
- /* The user did not trust the public key. */
- silcpurple_add_buddy_pk_no(r);
- silc_free(r);
- return;
- }
-
- if (r->offline) {
- /* User is offline. Associate the imported public key with
- this user. */
- fingerprint = silc_hash_fingerprint(NULL, r->offline_pk,
- r->offline_pk_len);
- for (i = 0; i < strlen(fingerprint); i++)
- if (fingerprint[i] == ' ')
- fingerprint[i] = '_';
- g_snprintf(filename, sizeof(filename) - 1,
- "%s" G_DIR_SEPARATOR_S "clientkeys" G_DIR_SEPARATOR_S "clientkey_%s.pub",
- silcpurple_silcdir(), fingerprint);
- purple_blist_node_set_string((PurpleBlistNode *)b, "public-key", filename);
- purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL);
- silc_free(fingerprint);
- silc_free(r->offline_pk);
- silc_free(r);
- return;
- }
-
- /* Get the client entry. */
- client_entry = silc_client_get_client_by_id(r->client, r->conn,
- &r->client_id);
- if (!client_entry) {
- silc_free(r);
- return;
- }
-
- memset(&vcard, 0, sizeof(vcard));
- memset(&message, 0, sizeof(message));
- memset(&extension, 0, sizeof(extension));
-#ifdef SILC_ATTRIBUTE_USER_ICON
- memset(&usericon, 0, sizeof(usericon));
-#endif
- memset(&serverpk, 0, sizeof(serverpk));
- memset(&usersign, 0, sizeof(usersign));
- memset(&serversign, 0, sizeof(serversign));
-
- /* Now that we have the public key and we trust it now we
- save the attributes of the buddy and update its status. */
-
- if (client_entry->attrs) {
- silc_dlist_start(client_entry->attrs);
- while ((attr = silc_dlist_get(client_entry->attrs))
- != SILC_LIST_END) {
- attribute = silc_attribute_get_attribute(attr);
-
- switch (attribute) {
- case SILC_ATTRIBUTE_USER_INFO:
- if (!silc_attribute_get_object(attr, (void *)&vcard,
- sizeof(vcard)))
- continue;
- break;
-
- case SILC_ATTRIBUTE_STATUS_MESSAGE:
- if (!silc_attribute_get_object(attr, (void *)&message,
- sizeof(message)))
- continue;
- break;
-
- case SILC_ATTRIBUTE_EXTENSION:
- if (!silc_attribute_get_object(attr, (void *)&extension,
- sizeof(extension)))
- continue;
- break;
-
-#ifdef SILC_ATTRIBUTE_USER_ICON
- case SILC_ATTRIBUTE_USER_ICON:
- if (!silc_attribute_get_object(attr, (void *)&usericon,
- sizeof(usericon)))
- continue;
- break;
-#endif
-
- case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY:
- if (serverpk.type)
- continue;
- if (!silc_attribute_get_object(attr, (void *)&serverpk,
- sizeof(serverpk)))
- continue;
- break;
-
- case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE:
- if (usersign.data)
- continue;
- if (!silc_attribute_get_object(attr, (void *)&usersign,
- sizeof(usersign)))
- continue;
- break;
-
- case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE:
- if (serversign.data)
- continue;
- if (!silc_attribute_get_object(attr, (void *)&serversign,
- sizeof(serversign)))
- continue;
- break;
-
- default:
- break;
- }
- }
- }
-
- /* Verify the attribute signatures */
-
- if (usersign.data) {
- SilcPKCS pkcs;
- unsigned char *verifyd;
- SilcUInt32 verify_len;
-
- silc_pkcs_alloc((unsigned char*)"rsa", &pkcs);
- verifyd = silc_attribute_get_verify_data(client_entry->attrs,
- FALSE, &verify_len);
- if (verifyd && silc_pkcs_public_key_set(pkcs, client_entry->public_key)){
- if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash,
- usersign.data,
- usersign.data_len,
- verifyd, verify_len))
- usign_success = FALSE;
- }
- silc_free(verifyd);
- }
-
- if (serversign.data && !strcmp(serverpk.type, "silc-rsa")) {
- SilcPublicKey public_key;
- SilcPKCS pkcs;
- unsigned char *verifyd;
- SilcUInt32 verify_len;
-
- if (silc_pkcs_public_key_decode(serverpk.data, serverpk.data_len,
- &public_key)) {
- silc_pkcs_alloc((unsigned char *)"rsa", &pkcs);
- verifyd = silc_attribute_get_verify_data(client_entry->attrs,
- TRUE, &verify_len);
- if (verifyd && silc_pkcs_public_key_set(pkcs, public_key)) {
- if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash,
- serversign.data,
- serversign.data_len,
- verifyd, verify_len))
- ssign_success = FALSE;
- }
- silc_pkcs_public_key_free(public_key);
- silc_free(verifyd);
- }
- }
-
- fingerprint = silc_fingerprint(client_entry->fingerprint,
- client_entry->fingerprint_len);
- for (i = 0; i < strlen(fingerprint); i++)
- if (fingerprint[i] == ' ')
- fingerprint[i] = '_';
-
- if (usign_success || ssign_success) {
- struct passwd *pw;
- struct stat st;
-
- memset(filename2, 0, sizeof(filename2));
-
- /* Filename for dir */
- tmp = fingerprint + strlen(fingerprint) - 9;
- g_snprintf(filename, sizeof(filename) - 1,
- "%s" G_DIR_SEPARATOR_S "friends" G_DIR_SEPARATOR_S "%s",
- silcpurple_silcdir(), tmp);
-
- pw = getpwuid(getuid());
- if (!pw)
- return;
-
- /* Create dir if it doesn't exist */
- if ((g_stat(filename, &st)) == -1) {
- if (errno == ENOENT) {
- if (pw->pw_uid == geteuid()) {
- int ret = g_mkdir(filename, 0755);
- if (ret < 0)
- return;
- }
- }
- }
-
- /* Save VCard */
- g_snprintf(filename2, sizeof(filename2) - 1,
- "%s" G_DIR_SEPARATOR_S "vcard", filename);
- if (vcard.full_name) {
- tmp = (char *)silc_vcard_encode(&vcard, &len);
- silc_file_writefile(filename2, tmp, len);
- silc_free(tmp);
- }
-
- /* Save status message */
- if (message.mime) {
- memset(filename2, 0, sizeof(filename2));
- g_snprintf(filename2, sizeof(filename2) - 1,
- "%s" G_DIR_SEPARATOR_S "status_message.mime",
- filename);
- silc_file_writefile(filename2, (char *)message.mime,
- message.mime_len);
- }
-
- /* Save extension data */
- if (extension.mime) {
- memset(filename2, 0, sizeof(filename2));
- g_snprintf(filename2, sizeof(filename2) - 1,
- "%s" G_DIR_SEPARATOR_S "extension.mime",
- filename);
- silc_file_writefile(filename2, (char *)extension.mime,
- extension.mime_len);
- }
-
-#ifdef SILC_ATTRIBUTE_USER_ICON
- /* Save user icon */
- if (usericon.mime) {
- SilcMime m = silc_mime_decode(usericon.mime,
- usericon.mime_len);
- if (m) {
- const char *type = silc_mime_get_field(m, "Content-Type");
- if (!strcmp(type, "image/jpeg") ||
- !strcmp(type, "image/gif") ||
- !strcmp(type, "image/bmp") ||
- !strcmp(type, "image/png")) {
- const unsigned char *data;
- SilcUInt32 data_len;
- data = silc_mime_get_data(m, &data_len);
- if (data) {
- /* TODO: Check if SILC gives us something to use as the checksum instead */
- purple_buddy_icons_set_for_user(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), g_memdup(data, data_len), data_len, NULL);
- }
- }
- silc_mime_free(m);
- }
- }
-#endif
- }
-
- /* Save the public key path to buddy properties, as it is used
- to identify the buddy in the network (and not the nickname). */
- memset(filename, 0, sizeof(filename));
- g_snprintf(filename, sizeof(filename) - 1,
- "%s" G_DIR_SEPARATOR_S "clientkeys" G_DIR_SEPARATOR_S "clientkey_%s.pub",
- silcpurple_silcdir(), fingerprint);
- purple_blist_node_set_string((PurpleBlistNode *)b, "public-key", filename);
-
- /* Update online status */
- purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL);
-
- /* Finally, start watching this user so we receive its status
- changes from the server */
- g_snprintf(filename2, sizeof(filename2) - 1, "+%s", filename);
- silc_client_command_call(r->client, r->conn, NULL, "WATCH", "-pubkey",
- filename2, NULL);
-
- silc_free(fingerprint);
- silc_free(r);
-}
-
-static void
-silcpurple_add_buddy_ask_import(void *user_data, const char *name)
-{
- SilcPurpleBuddyRes r = (SilcPurpleBuddyRes)user_data;
- SilcPublicKey public_key;
-
- /* Load the public key */
- if (!silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_PEM) &&
- !silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_BIN)) {
- silcpurple_add_buddy_ask_pk_cb(r, 0);
- purple_notify_error(r->client->application,
- _("Add Buddy"), _("Could not load public key"), NULL);
- return;
- }
-
- /* Now verify the public key */
- r->offline_pk = silc_pkcs_public_key_encode(public_key, &r->offline_pk_len);
- silcpurple_verify_public_key(r->client, r->conn, r->b->name,
- SILC_SOCKET_TYPE_CLIENT,
- r->offline_pk, r->offline_pk_len,
- SILC_SKE_PK_TYPE_SILC,
- silcpurple_add_buddy_save, r);
-}
-
-static void
-silcpurple_add_buddy_ask_pk_cancel(void *user_data, const char *name)
-{
- SilcPurpleBuddyRes r = (SilcPurpleBuddyRes)user_data;
-
- /* The user did not import public key. The buddy is unusable. */
- silcpurple_add_buddy_pk_no(r);
- silc_free(r);
-}
-
-static void
-silcpurple_add_buddy_ask_pk_cb(SilcPurpleBuddyRes r, gint id)
-{
- if (id != 0) {
- /* The user did not import public key. The buddy is unusable. */
- silcpurple_add_buddy_pk_no(r);
- silc_free(r);
- return;
- }
-
- /* Open file selector to select the public key. */
- purple_request_file(r->client->application, _("Open..."), NULL, FALSE,
- G_CALLBACK(silcpurple_add_buddy_ask_import),
- G_CALLBACK(silcpurple_add_buddy_ask_pk_cancel),
- purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
-
-}
-
-static void
-silcpurple_add_buddy_ask_pk(SilcPurpleBuddyRes r)
-{
- char tmp[512];
- g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not present in the network"),
- r->b->name);
- purple_request_action(r->client->application, _("Add Buddy"), tmp,
- _("To add the buddy you must import his/her public key. "
- "Press Import to import a public key."), 0,
- purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r, 2,
- _("Cancel"), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb),
- _("_Import..."), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb));
-}
-
-static void
-silcpurple_add_buddy_getkey_cb(SilcPurpleBuddyRes r,
- SilcClientCommandReplyContext cmd)
-{
- SilcClientEntry client_entry;
- unsigned char *pk;
- SilcUInt32 pk_len;
-
- /* Get the client entry. */
- client_entry = silc_client_get_client_by_id(r->client, r->conn,
- &r->client_id);
- if (!client_entry || !client_entry->public_key) {
- /* The buddy is offline/nonexistent. We will require user
- to associate a public key with the buddy or the buddy
- cannot be added. */
- r->offline = TRUE;
- silcpurple_add_buddy_ask_pk(r);
- return;
- }
-
- /* Now verify the public key */
- pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
- silcpurple_verify_public_key(r->client, r->conn, client_entry->nickname,
- SILC_SOCKET_TYPE_CLIENT,
- pk, pk_len, SILC_SKE_PK_TYPE_SILC,
- silcpurple_add_buddy_save, r);
- silc_free(pk);
-}
-
-static void
-silcpurple_add_buddy_select_cb(SilcPurpleBuddyRes r, PurpleRequestFields *fields)
-{
- PurpleRequestField *f;
- GList *list;
- SilcClientEntry client_entry;
-
- f = purple_request_fields_get_field(fields, "list");
- list = purple_request_field_list_get_selected(f);
- if (!list) {
- /* The user did not select any user. */
- silcpurple_add_buddy_pk_no(r);
- silc_free(r);
- return;
- }
-
- client_entry = purple_request_field_list_get_data(f, list->data);
- silcpurple_add_buddy_resolved(r->client, r->conn, &client_entry, 1, r);
-}
-
-static void
-silcpurple_add_buddy_select_cancel(SilcPurpleBuddyRes r, PurpleRequestFields *fields)
-{
- /* The user did not select any user. */
- silcpurple_add_buddy_pk_no(r);
- silc_free(r);
-}
-
-static void
-silcpurple_add_buddy_select(SilcPurpleBuddyRes r,
- SilcClientEntry *clients,
- SilcUInt32 clients_count)
-{
- PurpleRequestFields *fields;
- PurpleRequestFieldGroup *g;
- PurpleRequestField *f;
- char tmp[512], tmp2[128];
- int i;
- char *fingerprint;
-
- fields = purple_request_fields_new();
- g = purple_request_field_group_new(NULL);
- f = purple_request_field_list_new("list", NULL);
- purple_request_field_group_add_field(g, f);
- purple_request_field_list_set_multi_select(f, FALSE);
- purple_request_fields_add_group(fields, g);
-
- for (i = 0; i < clients_count; i++) {
- fingerprint = NULL;
- if (clients[i]->fingerprint) {
- fingerprint = silc_fingerprint(clients[i]->fingerprint,
- clients[i]->fingerprint_len);
- g_snprintf(tmp2, sizeof(tmp2), "\n%s", fingerprint);
- }
- g_snprintf(tmp, sizeof(tmp), "%s - %s (%s@%s)%s",
- clients[i]->realname, clients[i]->nickname,
- clients[i]->username, clients[i]->hostname ?
- clients[i]->hostname : "",
- fingerprint ? tmp2 : "");
- purple_request_field_list_add_icon(f, tmp, NULL, clients[i]);
- silc_free(fingerprint);
- }
-
- purple_request_fields(r->client->application, _("Add Buddy"),
- _("Select correct user"),
- r->pubkey_search
- ? _("More than one user was found with the same public key. Select "
- "the correct user from the list to add to the buddy list.")
- : _("More than one user was found with the same name. Select "
- "the correct user from the list to add to the buddy list."),
- fields,
- _("OK"), G_CALLBACK(silcpurple_add_buddy_select_cb),
- _("Cancel"), G_CALLBACK(silcpurple_add_buddy_select_cancel),
- purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
-}
-
-static void
-silcpurple_add_buddy_resolved(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry *clients,
- SilcUInt32 clients_count,
- void *context)
-{
- SilcPurpleBuddyRes r = context;
- PurpleBuddy *b = r->b;
- SilcAttributePayload pub;
- SilcAttributeObjPk userpk;
- unsigned char *pk;
- SilcUInt32 pk_len;
- const char *filename;
-
- filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key");
-
- /* If the buddy is offline/nonexistent, we will require user
- to associate a public key with the buddy or the buddy
- cannot be added. */
- if (!clients_count) {
- if (r->init) {
- silc_free(r);
- return;
- }
-
- r->offline = TRUE;
- /* If the user has already associated a public key, try loading it
- * before prompting the user to load it again */
- if (filename != NULL)
- silcpurple_add_buddy_ask_import(r, filename);
- else
- silcpurple_add_buddy_ask_pk(r);
- return;
- }
-
- /* If more than one client was found with nickname, we need to verify
- from user which one is the correct. */
- if (clients_count > 1 && !r->pubkey_search) {
- if (r->init) {
- silc_free(r);
- return;
- }
-
- silcpurple_add_buddy_select(r, clients, clients_count);
- return;
- }
-
- /* If we searched using public keys and more than one entry was found
- the same person is logged on multiple times. */
- if (clients_count > 1 && r->pubkey_search && b->name) {
- if (r->init) {
- /* Find the entry that closest matches to the
- buddy nickname. */
- int i;
- for (i = 0; i < clients_count; i++) {
- if (!g_ascii_strncasecmp(b->name, clients[i]->nickname,
- strlen(b->name))) {
- clients[0] = clients[i];
- break;
- }
- }
- } else {
- /* Verify from user which one is correct */
- silcpurple_add_buddy_select(r, clients, clients_count);
- return;
- }
- }
-
- /* The client was found. Now get its public key and verify
- that before adding the buddy. */
- memset(&userpk, 0, sizeof(userpk));
- b->proto_data = silc_memdup(clients[0]->id, sizeof(*clients[0]->id));
- r->client_id = *clients[0]->id;
-
- /* Get the public key from attributes, if not present then
- resolve it with GETKEY unless we have it cached already. */
- if (clients[0]->attrs && !clients[0]->public_key) {
- pub = silcpurple_get_attr(clients[0]->attrs,
- SILC_ATTRIBUTE_USER_PUBLIC_KEY);
- if (!pub || !silc_attribute_get_object(pub, (void *)&userpk,
- sizeof(userpk))) {
- /* Get public key with GETKEY */
- silc_client_command_call(client, conn, NULL,
- "GETKEY", clients[0]->nickname, NULL);
- silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
- conn->cmd_ident,
- (SilcCommandCb)silcpurple_add_buddy_getkey_cb,
- r);
- return;
- }
- if (!silc_pkcs_public_key_decode(userpk.data, userpk.data_len,
- &clients[0]->public_key))
- return;
- silc_free(userpk.data);
- } else if (filename && !clients[0]->public_key) {
- if (!silc_pkcs_load_public_key(filename, &clients[0]->public_key,
- SILC_PKCS_FILE_PEM) &&
- !silc_pkcs_load_public_key(filename, &clients[0]->public_key,
- SILC_PKCS_FILE_BIN)) {
- /* Get public key with GETKEY */
- silc_client_command_call(client, conn, NULL,
- "GETKEY", clients[0]->nickname, NULL);
- silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
- conn->cmd_ident,
- (SilcCommandCb)silcpurple_add_buddy_getkey_cb,
- r);
- return;
- }
- } else if (!clients[0]->public_key) {
- /* Get public key with GETKEY */
- silc_client_command_call(client, conn, NULL,
- "GETKEY", clients[0]->nickname, NULL);
- silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
- conn->cmd_ident,
- (SilcCommandCb)silcpurple_add_buddy_getkey_cb,
- r);
- return;
- }
-
- /* We have the public key, verify it. */
- pk = silc_pkcs_public_key_encode(clients[0]->public_key, &pk_len);
- silcpurple_verify_public_key(client, conn, clients[0]->nickname,
- SILC_SOCKET_TYPE_CLIENT,
- pk, pk_len, SILC_SKE_PK_TYPE_SILC,
- silcpurple_add_buddy_save, r);
- silc_free(pk);
-}
-
-static void
-silcpurple_add_buddy_i(PurpleConnection *gc, PurpleBuddy *b, gboolean init)
-{
- SilcPurple sg = gc->proto_data;
- SilcClient client = sg->client;
- SilcClientConnection conn = sg->conn;
- SilcPurpleBuddyRes r;
- SilcBuffer attrs;
- const char *filename, *name = b->name;
-
- r = silc_calloc(1, sizeof(*r));
- if (!r)
- return;
- r->client = client;
- r->conn = conn;
- r->b = b;
- r->init = init;
-
- /* See if we have this buddy's public key. If we do use that
- to search the details. */
- filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key");
- if (filename) {
- SilcPublicKey public_key;
- SilcAttributeObjPk userpk;
-
- if (!silc_pkcs_load_public_key(filename, &public_key,
- SILC_PKCS_FILE_PEM) &&
- !silc_pkcs_load_public_key(filename, &public_key,
- SILC_PKCS_FILE_BIN))
- return;
-
- /* Get all attributes, and use the public key to search user */
- name = NULL;
- attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
- SILC_ATTRIBUTE_SERVICE,
- SILC_ATTRIBUTE_STATUS_MOOD,
- SILC_ATTRIBUTE_STATUS_FREETEXT,
- SILC_ATTRIBUTE_STATUS_MESSAGE,
- SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
- SILC_ATTRIBUTE_PREFERRED_CONTACT,
- SILC_ATTRIBUTE_TIMEZONE,
- SILC_ATTRIBUTE_GEOLOCATION,
-#ifdef SILC_ATTRIBUTE_USER_ICON
- SILC_ATTRIBUTE_USER_ICON,
-#endif
- SILC_ATTRIBUTE_DEVICE_INFO, 0);
- userpk.type = "silc-rsa";
- userpk.data = silc_pkcs_public_key_encode(public_key, &userpk.data_len);
- attrs = silc_attribute_payload_encode(attrs,
- SILC_ATTRIBUTE_USER_PUBLIC_KEY,
- SILC_ATTRIBUTE_FLAG_VALID,
- &userpk, sizeof(userpk));
- silc_free(userpk.data);
- silc_pkcs_public_key_free(public_key);
- r->pubkey_search = TRUE;
- } else {
- /* Get all attributes */
- attrs = silc_client_attributes_request(0);
- }
-
- /* Resolve */
- silc_client_get_clients_whois(client, conn, name, NULL, attrs,
- silcpurple_add_buddy_resolved, r);
- silc_buffer_free(attrs);
-}
-
-void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
-{
- /* Don't add if the buddy is already on the list.
- *
- * SILC doesn't have groups, so we don't need to do anything
- * for a move. */
- if (purple_buddy_get_protocol_data(buddy) == NULL)
- silcpurple_add_buddy_i(gc, buddy, FALSE);
-}
-
-void silcpurple_send_buddylist(PurpleConnection *gc)
-{
- PurpleBuddyList *blist;
- PurpleBlistNode *gnode, *cnode, *bnode;
- PurpleBuddy *buddy;
- PurpleAccount *account;
-
- account = purple_connection_get_account(gc);
-
- if ((blist = purple_get_blist()) != NULL)
- {
- for (gnode = blist->root; gnode != NULL; gnode = gnode->next)
- {
- if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
- continue;
- for (cnode = gnode->child; cnode != NULL; cnode = cnode->next)
- {
- if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
- continue;
- for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
- {
- if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
- continue;
- buddy = (PurpleBuddy *)bnode;
- if (purple_buddy_get_account(buddy) == account)
- silcpurple_add_buddy_i(gc, buddy, TRUE);
- }
- }
- }
- }
-}
-
-void silcpurple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
- PurpleGroup *group)
-{
- silc_free(buddy->proto_data);
-}
-
-void silcpurple_idle_set(PurpleConnection *gc, int idle)
-
-{
- SilcPurple sg;
- SilcClient client;
- SilcClientConnection conn;
- SilcAttributeObjService service;
- const char *server;
- int port;
-
- sg = gc->proto_data;
- if (sg == NULL)
- return;
-
- client = sg->client;
- if (client == NULL)
- return;
-
- conn = sg->conn;
- if (conn == NULL)
- return;
-
- server = purple_account_get_string(sg->account, "server",
- "silc.silcnet.org");
- port = purple_account_get_int(sg->account, "port", 706),
-
- memset(&service, 0, sizeof(service));
- silc_client_attribute_del(client, conn,
- SILC_ATTRIBUTE_SERVICE, NULL);
- service.port = port;
- g_snprintf(service.address, sizeof(service.address), "%s", server);
- service.idle = idle;
- silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_SERVICE,
- &service, sizeof(service));
-}
-
-char *silcpurple_status_text(PurpleBuddy *b)
-{
- SilcPurple sg = b->account->gc->proto_data;
- SilcClient client = sg->client;
- SilcClientConnection conn = sg->conn;
- SilcClientID *client_id = b->proto_data;
- SilcClientEntry client_entry;
- SilcAttributePayload attr;
- SilcAttributeMood mood = 0;
-
- /* Get the client entry. */
- client_entry = silc_client_get_client_by_id(client, conn, client_id);
- if (!client_entry)
- return NULL;
-
- /* If user is online, we show the mood status, if available.
- If user is offline or away that status is indicated. */
-
- if (client_entry->mode & SILC_UMODE_DETACHED)
- return g_strdup(_("Detached"));
- if (client_entry->mode & SILC_UMODE_GONE)
- return g_strdup(_("Away"));
- if (client_entry->mode & SILC_UMODE_INDISPOSED)
- return g_strdup(_("Indisposed"));
- if (client_entry->mode & SILC_UMODE_BUSY)
- return g_strdup(_("Busy"));
- if (client_entry->mode & SILC_UMODE_PAGE)
- return g_strdup(_("Wake Me Up"));
- if (client_entry->mode & SILC_UMODE_HYPER)
- return g_strdup(_("Hyper Active"));
- if (client_entry->mode & SILC_UMODE_ROBOT)
- return g_strdup(_("Robot"));
-
- attr = silcpurple_get_attr(client_entry->attrs, SILC_ATTRIBUTE_STATUS_MOOD);
- if (attr && silc_attribute_get_object(attr, &mood, sizeof(mood))) {
- /* The mood is a bit mask, so we could show multiple moods,
- but let's show only one for now. */
- if (mood & SILC_ATTRIBUTE_MOOD_HAPPY)
- return g_strdup(_("Happy"));
- if (mood & SILC_ATTRIBUTE_MOOD_SAD)
- return g_strdup(_("Sad"));
- if (mood & SILC_ATTRIBUTE_MOOD_ANGRY)
- return g_strdup(_("Angry"));
- if (mood & SILC_ATTRIBUTE_MOOD_JEALOUS)
- return g_strdup(_("Jealous"));
- if (mood & SILC_ATTRIBUTE_MOOD_ASHAMED)
- return g_strdup(_("Ashamed"));
- if (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE)
- return g_strdup(_("Invincible"));
- if (mood & SILC_ATTRIBUTE_MOOD_INLOVE)
- return g_strdup(_("In Love"));
- if (mood & SILC_ATTRIBUTE_MOOD_SLEEPY)
- return g_strdup(_("Sleepy"));
- if (mood & SILC_ATTRIBUTE_MOOD_BORED)
- return g_strdup(_("Bored"));
- if (mood & SILC_ATTRIBUTE_MOOD_EXCITED)
- return g_strdup(_("Excited"));
- if (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS)
- return g_strdup(_("Anxious"));
- }
-
- return NULL;
-}
-
-void silcpurple_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
-{
- SilcPurple sg = b->account->gc->proto_data;
- SilcClient client = sg->client;
- SilcClientConnection conn = sg->conn;
- SilcClientID *client_id = b->proto_data;
- SilcClientEntry client_entry;
- char *moodstr, *statusstr, *contactstr, *langstr, *devicestr, *tzstr, *geostr;
- char tmp[256];
-
- /* Get the client entry. */
- client_entry = silc_client_get_client_by_id(client, conn, client_id);
- if (!client_entry)
- return;
-
- if (client_entry->nickname)
- purple_notify_user_info_add_pair(user_info, _("Nickname"),
- client_entry->nickname);
- if (client_entry->username && client_entry->hostname) {
- g_snprintf(tmp, sizeof(tmp), "%s@%s", client_entry->username, client_entry->hostname);
- purple_notify_user_info_add_pair(user_info, _("Username"), tmp);
- }
- if (client_entry->mode) {
- memset(tmp, 0, sizeof(tmp));
- silcpurple_get_umode_string(client_entry->mode,
- tmp, sizeof(tmp) - strlen(tmp));
- purple_notify_user_info_add_pair(user_info, _("User Modes"), tmp);
- }
-
- silcpurple_parse_attrs(client_entry->attrs, &moodstr, &statusstr, &contactstr, &langstr, &devicestr, &tzstr, &geostr);
-
- if (statusstr) {
- purple_notify_user_info_add_pair(user_info, _("Message"), statusstr);
- g_free(statusstr);
- }
-
- if (full) {
- if (moodstr) {
- purple_notify_user_info_add_pair(user_info, _("Mood"), moodstr);
- g_free(moodstr);
- }
-
- if (contactstr) {
- purple_notify_user_info_add_pair(user_info, _("Preferred Contact"), contactstr);
- g_free(contactstr);
- }
-
- if (langstr) {
- purple_notify_user_info_add_pair(user_info, _("Preferred Language"), langstr);
- g_free(langstr);
- }
-
- if (devicestr) {
- purple_notify_user_info_add_pair(user_info, _("Device"), devicestr);
- g_free(devicestr);
- }
-
- if (tzstr) {
- purple_notify_user_info_add_pair(user_info, _("Timezone"), tzstr);
- g_free(tzstr);
- }
-
- if (geostr) {
- purple_notify_user_info_add_pair(user_info, _("Geolocation"), geostr);
- g_free(geostr);
- }
- }
-}
-
-static void
-silcpurple_buddy_kill(PurpleBlistNode *node, gpointer data)
-{
- PurpleBuddy *b;
- PurpleConnection *gc;
- SilcPurple sg;
-
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
-
- b = (PurpleBuddy *) node;
- gc = purple_account_get_connection(b->account);
- sg = gc->proto_data;
-
- /* Call KILL */
- silc_client_command_call(sg->client, sg->conn, NULL, "KILL",
- b->name, "Killed by operator", NULL);
-}
-
-typedef struct {
- SilcPurple sg;
- SilcClientEntry client_entry;
-} *SilcPurpleBuddyWb;
-
-static void
-silcpurple_buddy_wb(PurpleBlistNode *node, gpointer data)
-{
- SilcPurpleBuddyWb wb = data;
- silcpurple_wb_init(wb->sg, wb->client_entry);
- silc_free(wb);
-}
-
-GList *silcpurple_buddy_menu(PurpleBuddy *buddy)
-{
- PurpleConnection *gc = purple_account_get_connection(buddy->account);
- SilcPurple sg = gc->proto_data;
- SilcClientConnection conn = sg->conn;
- const char *pkfile = NULL;
- SilcClientEntry client_entry = NULL;
- PurpleMenuAction *act;
- GList *m = NULL;
- SilcPurpleBuddyWb wb;
-
- pkfile = purple_blist_node_get_string((PurpleBlistNode *) buddy, "public-key");
- client_entry = silc_client_get_client_by_id(sg->client,
- sg->conn,
- buddy->proto_data);
-
- if (client_entry && client_entry->send_key) {
- act = purple_menu_action_new(_("Reset IM Key"),
- PURPLE_CALLBACK(silcpurple_buddy_resetkey),
- NULL, NULL);
- m = g_list_append(m, act);
-
- } else {
- act = purple_menu_action_new(_("IM with Key Exchange"),
- PURPLE_CALLBACK(silcpurple_buddy_keyagr),
- NULL, NULL);
- m = g_list_append(m, act);
-
- act = purple_menu_action_new(_("IM with Password"),
- PURPLE_CALLBACK(silcpurple_buddy_privkey_menu),
- NULL, NULL);
- m = g_list_append(m, act);
- }
-
- if (pkfile) {
- act = purple_menu_action_new(_("Show Public Key"),
- PURPLE_CALLBACK(silcpurple_buddy_showkey),
- NULL, NULL);
- m = g_list_append(m, act);
-
- } else {
- act = purple_menu_action_new(_("Get Public Key..."),
- PURPLE_CALLBACK(silcpurple_buddy_getkey_menu),
- NULL, NULL);
- m = g_list_append(m, act);
- }
-
- if (conn && conn->local_entry->mode & SILC_UMODE_ROUTER_OPERATOR) {
- act = purple_menu_action_new(_("Kill User"),
- PURPLE_CALLBACK(silcpurple_buddy_kill),
- NULL, NULL);
- m = g_list_append(m, act);
- }
-
- if (client_entry) {
- wb = silc_calloc(1, sizeof(*wb));
- wb->sg = sg;
- wb->client_entry = client_entry;
- act = purple_menu_action_new(_("Draw On Whiteboard"),
- PURPLE_CALLBACK(silcpurple_buddy_wb),
- (void *)wb, NULL);
- m = g_list_append(m, act);
- }
- return m;
-}
-
-#ifdef SILC_ATTRIBUTE_USER_ICON
-void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleStoredImage *img)
-{
- SilcPurple sg = gc->proto_data;
- SilcClient client = sg->client;
- SilcClientConnection conn = sg->conn;
- SilcMime mime;
- char type[32];
- unsigned char *icon;
- const char *t;
- SilcAttributeObjMime obj;
-
- /* Remove */
- if (!img) {
- silc_client_attribute_del(client, conn,
- SILC_ATTRIBUTE_USER_ICON, NULL);
- return;
- }
-
- /* Add */
- mime = silc_mime_alloc();
- if (!mime)
- return;
-
- t = purple_imgstore_get_extension(img);
- if (!t || !strcmp(t, "icon")) {
- silc_mime_free(mime);
- return;
- }
- if (!strcmp(t, "jpg"))
- t = "jpeg";
- g_snprintf(type, sizeof(type), "image/%s", t);
- silc_mime_add_field(mime, "Content-Type", type);
- silc_mime_add_data(mime, purple_imgstore_get_data(img), purple_imgstore_get_size(img));
-
- obj.mime = icon = silc_mime_encode(mime, &obj.mime_len);
- if (obj.mime)
- silc_client_attribute_add(client, conn,
- SILC_ATTRIBUTE_USER_ICON, &obj, sizeof(obj));
-
- silc_free(icon);
- silc_mime_free(mime);
-}
-#endif
diff --git a/libpurple/protocols/silc10/chat.c b/libpurple/protocols/silc10/chat.c
deleted file mode 100644
index cc6f509604..0000000000
--- a/libpurple/protocols/silc10/chat.c
+++ /dev/null
@@ -1,1456 +0,0 @@
-/*
-
- silcpurple_chat.c
-
- Author: Pekka Riikonen <priikone@silcnet.org>
-
- Copyright (C) 2004 Pekka Riikonen
-
- 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; version 2 of the License.
-
- 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.
-
-*/
-
-#include "silcincludes.h"
-#include "silcclient.h"
-#include "silcpurple.h"
-#include "wb.h"
-
-/***************************** Channel Routines ******************************/
-
-GList *silcpurple_chat_info(PurpleConnection *gc)
-{
- GList *ci = NULL;
- struct proto_chat_entry *pce;
-
- pce = g_new0(struct proto_chat_entry, 1);
- pce->label = _("_Channel:");
- pce->identifier = "channel";
- pce->required = TRUE;
- ci = g_list_append(ci, pce);
-
- pce = g_new0(struct proto_chat_entry, 1);
- pce->label = _("_Passphrase:");
- pce->identifier = "passphrase";
- pce->secret = TRUE;
- ci = g_list_append(ci, pce);
-
- return ci;
-}
-
-GHashTable *silcpurple_chat_info_defaults(PurpleConnection *gc, const char *chat_name)
-{
- GHashTable *defaults;
-
- defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
-
- if (chat_name != NULL)
- g_hash_table_insert(defaults, "channel", g_strdup(chat_name));
-
- return defaults;
-}
-
-static void
-silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components);
-
-static void
-silcpurple_chat_getinfo_res(SilcClient client,
- SilcClientConnection conn,
- SilcChannelEntry *channels,
- SilcUInt32 channels_count,
- void *context)
-{
- GHashTable *components = context;
- PurpleConnection *gc = client->application;
- const char *chname;
- char tmp[256];
-
- chname = g_hash_table_lookup(components, "channel");
- if (!chname)
- return;
-
- if (!channels) {
- g_snprintf(tmp, sizeof(tmp),
- _("Channel %s does not exist in the network"), chname);
- purple_notify_error(gc, _("Channel Information"),
- _("Cannot get channel information"), tmp);
- return;
- }
-
- silcpurple_chat_getinfo(gc, components);
-}
-
-
-static void
-silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components)
-{
- SilcPurple sg = gc->proto_data;
- const char *chname;
- char *buf, tmp[256], *tmp2;
- GString *s;
- SilcChannelEntry channel;
- SilcHashTableList htl;
- SilcChannelUser chu;
-
- if (!components)
- return;
-
- chname = g_hash_table_lookup(components, "channel");
- if (!chname)
- return;
- channel = silc_client_get_channel(sg->client, sg->conn,
- (char *)chname);
- if (!channel) {
- silc_client_get_channel_resolve(sg->client, sg->conn,
- (char *)chname,
- silcpurple_chat_getinfo_res,
- components);
- return;
- }
-
- s = g_string_new("");
- tmp2 = g_markup_escape_text(channel->channel_name, -1);
- g_string_append_printf(s, _("<b>Channel Name:</b> %s"), tmp2);
- g_free(tmp2);
- if (channel->user_list && silc_hash_table_count(channel->user_list))
- g_string_append_printf(s, _("<br><b>User Count:</b> %d"),
- (int)silc_hash_table_count(channel->user_list));
-
- silc_hash_table_list(channel->user_list, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) {
- tmp2 = g_markup_escape_text(chu->client->nickname, -1);
- g_string_append_printf(s, _("<br><b>Channel Founder:</b> %s"),
- tmp2);
- g_free(tmp2);
- break;
- }
- }
- silc_hash_table_list_reset(&htl);
-
- if (channel->channel_key)
- g_string_append_printf(s, _("<br><b>Channel Cipher:</b> %s"),
- silc_cipher_get_name(channel->channel_key));
- if (channel->hmac)
- /* Definition of HMAC: http://en.wikipedia.org/wiki/HMAC */
- g_string_append_printf(s, _("<br><b>Channel HMAC:</b> %s"),
- silc_hmac_get_name(channel->hmac));
-
- if (channel->topic) {
- tmp2 = g_markup_escape_text(channel->topic, -1);
- g_string_append_printf(s, _("<br><b>Channel Topic:</b><br>%s"), tmp2);
- g_free(tmp2);
- }
-
- if (channel->mode) {
- g_string_append_printf(s, _("<br><b>Channel Modes:</b> "));
- silcpurple_get_chmode_string(channel->mode, tmp, sizeof(tmp));
- g_string_append(s, tmp);
- }
-
- if (channel->founder_key) {
- char *fingerprint, *babbleprint;
- unsigned char *pk;
- SilcUInt32 pk_len;
- pk = silc_pkcs_public_key_encode(channel->founder_key, &pk_len);
- fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
- babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
-
- g_string_append_printf(s, _("<br><b>Founder Key Fingerprint:</b><br>%s"), fingerprint);
- g_string_append_printf(s, _("<br><b>Founder Key Babbleprint:</b><br>%s"), babbleprint);
-
- silc_free(fingerprint);
- silc_free(babbleprint);
- silc_free(pk);
- }
-
- buf = g_string_free(s, FALSE);
- purple_notify_formatted(gc, NULL, _("Channel Information"), NULL, buf, NULL, NULL);
- g_free(buf);
-}
-
-
-static void
-silcpurple_chat_getinfo_menu(PurpleBlistNode *node, gpointer data)
-{
- PurpleChat *chat = (PurpleChat *)node;
- silcpurple_chat_getinfo(chat->account->gc, chat->components);
-}
-
-
-#if 0 /* XXX For now these are not implemented. We need better
- listview dialog from Purple for these. */
-/************************** Channel Invite List ******************************/
-
-static void
-silcpurple_chat_invitelist(PurpleBlistNode *node, gpointer data);
-{
-
-}
-
-
-/**************************** Channel Ban List *******************************/
-
-static void
-silcpurple_chat_banlist(PurpleBlistNode *node, gpointer data);
-{
-
-}
-#endif
-
-
-/************************* Channel Authentication ****************************/
-
-typedef struct {
- SilcPurple sg;
- SilcChannelEntry channel;
- PurpleChat *c;
- SilcBuffer pubkeys;
-} *SilcPurpleChauth;
-
-static void
-silcpurple_chat_chpk_add(void *user_data, const char *name)
-{
- SilcPurpleChauth sgc = (SilcPurpleChauth)user_data;
- SilcPurple sg = sgc->sg;
- SilcClient client = sg->client;
- SilcClientConnection conn = sg->conn;
- SilcPublicKey public_key;
- SilcBuffer chpks, pk, chidp;
- unsigned char mode[4];
- SilcUInt32 m;
-
- /* Load the public key */
- if (!silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_PEM) &&
- !silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_BIN)) {
- silcpurple_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys);
- silc_buffer_free(sgc->pubkeys);
- silc_free(sgc);
- purple_notify_error(client->application,
- _("Add Channel Public Key"),
- _("Could not load public key"), NULL);
- return;
- }
-
- pk = silc_pkcs_public_key_payload_encode(public_key);
- chpks = silc_buffer_alloc_size(2);
- SILC_PUT16_MSB(1, chpks->head);
- chpks = silc_argument_payload_encode_one(chpks, pk->data,
- pk->len, 0x00);
- silc_buffer_free(pk);
-
- m = sgc->channel->mode;
- m |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
-
- /* Send CMODE */
- SILC_PUT32_MSB(m, mode);
- chidp = silc_id_payload_encode(sgc->channel->id, SILC_ID_CHANNEL);
- silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
- ++conn->cmd_ident, 3,
- 1, chidp->data, chidp->len,
- 2, mode, sizeof(mode),
- 9, chpks->data, chpks->len);
- silc_buffer_free(chpks);
- silc_buffer_free(chidp);
- silc_buffer_free(sgc->pubkeys);
- silc_free(sgc);
-}
-
-static void
-silcpurple_chat_chpk_cancel(void *user_data, const char *name)
-{
- SilcPurpleChauth sgc = (SilcPurpleChauth)user_data;
- silcpurple_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys);
- silc_buffer_free(sgc->pubkeys);
- silc_free(sgc);
-}
-
-static void
-silcpurple_chat_chpk_cb(SilcPurpleChauth sgc, PurpleRequestFields *fields)
-{
- SilcPurple sg = sgc->sg;
- SilcClient client = sg->client;
- SilcClientConnection conn = sg->conn;
- PurpleRequestField *f;
- GList *list;
- SilcPublicKey public_key;
- SilcBuffer chpks, pk, chidp;
- SilcUInt16 c = 0, ct;
- unsigned char mode[4];
- SilcUInt32 m;
-
- f = purple_request_fields_get_field(fields, "list");
- if (!purple_request_field_list_get_selected(f)) {
- /* Add new public key */
- purple_request_file(sg->gc, _("Open Public Key..."), NULL, FALSE,
- G_CALLBACK(silcpurple_chat_chpk_add),
- G_CALLBACK(silcpurple_chat_chpk_cancel),
- purple_connection_get_account(sg->gc), NULL, NULL, sgc);
- return;
- }
-
- list = purple_request_field_list_get_items(f);
- chpks = silc_buffer_alloc_size(2);
-
- for (ct = 0; list; list = list->next, ct++) {
- public_key = purple_request_field_list_get_data(f, list->data);
- if (purple_request_field_list_is_selected(f, list->data)) {
- /* Delete this public key */
- pk = silc_pkcs_public_key_payload_encode(public_key);
- chpks = silc_argument_payload_encode_one(chpks, pk->data,
- pk->len, 0x01);
- silc_buffer_free(pk);
- c++;
- }
- silc_pkcs_public_key_free(public_key);
- }
- if (!c) {
- silc_buffer_free(chpks);
- return;
- }
- SILC_PUT16_MSB(c, chpks->head);
-
- m = sgc->channel->mode;
- if (ct == c)
- m &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
-
- /* Send CMODE */
- SILC_PUT32_MSB(m, mode);
- chidp = silc_id_payload_encode(sgc->channel->id, SILC_ID_CHANNEL);
- silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
- ++conn->cmd_ident, 3,
- 1, chidp->data, chidp->len,
- 2, mode, sizeof(mode),
- 9, chpks->data, chpks->len);
- silc_buffer_free(chpks);
- silc_buffer_free(chidp);
- silc_buffer_free(sgc->pubkeys);
- silc_free(sgc);
-}
-
-static void
-silcpurple_chat_chauth_ok(SilcPurpleChauth sgc, PurpleRequestFields *fields)
-{
- SilcPurple sg = sgc->sg;
- PurpleRequestField *f;
- const char *curpass, *val;
- int set;
-
- f = purple_request_fields_get_field(fields, "passphrase");
- val = purple_request_field_string_get_value(f);
- curpass = purple_blist_node_get_string((PurpleBlistNode *)sgc->c, "passphrase");
-
- if (!val && curpass)
- set = 0;
- else if (val && !curpass)
- set = 1;
- else if (val && curpass && strcmp(val, curpass))
- set = 1;
- else
- set = -1;
-
- if (set == 1) {
- silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
- sgc->channel->channel_name, "+a", val, NULL);
- purple_blist_node_set_string((PurpleBlistNode *)sgc->c, "passphrase", val);
- } else if (set == 0) {
- silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
- sgc->channel->channel_name, "-a", NULL);
- purple_blist_node_remove_setting((PurpleBlistNode *)sgc->c, "passphrase");
- }
-
- silc_buffer_free(sgc->pubkeys);
- silc_free(sgc);
-}
-
-void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel,
- SilcBuffer channel_pubkeys)
-{
- SilcUInt16 argc;
- SilcArgumentPayload chpks;
- unsigned char *pk;
- SilcUInt32 pk_len, type;
- char *fingerprint, *babbleprint;
- SilcPublicKey pubkey;
- SilcPublicKeyIdentifier ident;
- char tmp2[1024], t[512];
- PurpleRequestFields *fields;
- PurpleRequestFieldGroup *g;
- PurpleRequestField *f;
- SilcPurpleChauth sgc;
- const char *curpass = NULL;
-
- sgc = silc_calloc(1, sizeof(*sgc));
- if (!sgc)
- return;
- sgc->sg = sg;
- sgc->channel = channel;
-
- fields = purple_request_fields_new();
-
- if (sgc->c)
- curpass = purple_blist_node_get_string((PurpleBlistNode *)sgc->c, "passphrase");
-
- g = purple_request_field_group_new(NULL);
- f = purple_request_field_string_new("passphrase", _("Channel Passphrase"),
- curpass, FALSE);
- purple_request_field_string_set_masked(f, TRUE);
- purple_request_field_group_add_field(g, f);
- purple_request_fields_add_group(fields, g);
-
- g = purple_request_field_group_new(NULL);
- f = purple_request_field_label_new("l1", _("Channel Public Keys List"));
- purple_request_field_group_add_field(g, f);
- purple_request_fields_add_group(fields, g);
-
- g_snprintf(t, sizeof(t),
- _("Channel authentication is used to secure the channel from "
- "unauthorized access. The authentication may be based on "
- "passphrase and digital signatures. If passphrase is set, it "
- "is required to be able to join. If channel public keys are set "
- "then only users whose public keys are listed are able to join."));
-
- if (!channel_pubkeys) {
- f = purple_request_field_list_new("list", NULL);
- purple_request_field_group_add_field(g, f);
- purple_request_fields(sg->gc, _("Channel Authentication"),
- _("Channel Authentication"), t, fields,
- _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
- _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
- purple_connection_get_account(sg->gc), NULL, NULL, sgc);
- return;
- }
- sgc->pubkeys = silc_buffer_copy(channel_pubkeys);
-
- g = purple_request_field_group_new(NULL);
- f = purple_request_field_list_new("list", NULL);
- purple_request_field_group_add_field(g, f);
- purple_request_fields_add_group(fields, g);
-
- SILC_GET16_MSB(argc, channel_pubkeys->data);
- chpks = silc_argument_payload_parse(channel_pubkeys->data + 2,
- channel_pubkeys->len - 2, argc);
- if (!chpks)
- return;
-
- pk = silc_argument_get_first_arg(chpks, &type, &pk_len);
- while (pk) {
- fingerprint = silc_hash_fingerprint(NULL, pk + 4, pk_len - 4);
- babbleprint = silc_hash_babbleprint(NULL, pk + 4, pk_len - 4);
- silc_pkcs_public_key_payload_decode(pk, pk_len, &pubkey);
- ident = silc_pkcs_decode_identifier(pubkey->identifier);
-
- g_snprintf(tmp2, sizeof(tmp2), "%s\n %s\n %s",
- ident->realname ? ident->realname : ident->username ?
- ident->username : "", fingerprint, babbleprint);
- purple_request_field_list_add_icon(f, tmp2, NULL, pubkey);
-
- silc_free(fingerprint);
- silc_free(babbleprint);
- silc_pkcs_free_identifier(ident);
- pk = silc_argument_get_next_arg(chpks, &type, &pk_len);
- }
-
- purple_request_field_list_set_multi_select(f, FALSE);
- purple_request_fields(sg->gc, _("Channel Authentication"),
- _("Channel Authentication"), t, fields,
- _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
- _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
- purple_connection_get_account(sg->gc), NULL, NULL, sgc);
-
- silc_argument_payload_free(chpks);
-}
-
-static void
-silcpurple_chat_chauth(PurpleBlistNode *node, gpointer data)
-{
- PurpleChat *chat;
- PurpleConnection *gc;
- SilcPurple sg;
-
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
-
- chat = (PurpleChat *) node;
- gc = purple_account_get_connection(chat->account);
- sg = gc->proto_data;
-
- silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
- g_hash_table_lookup(chat->components, "channel"),
- "+C", NULL);
-}
-
-
-/************************** Channel Private Groups **************************/
-
-/* Private groups are "virtual" channels. They are groups inside a channel.
- This is implemented by using channel private keys. By knowing a channel
- private key user becomes part of that group and is able to talk on that
- group. Other users, on the same channel, won't be able to see the
- messages of that group. It is possible to have multiple groups inside
- a channel - and thus having multiple private keys on the channel. */
-
-typedef struct {
- SilcPurple sg;
- PurpleChat *c;
- const char *channel;
-} *SilcPurpleCharPrv;
-
-static void
-silcpurple_chat_prv_add(SilcPurpleCharPrv p, PurpleRequestFields *fields)
-{
- SilcPurple sg = p->sg;
- char tmp[512];
- PurpleRequestField *f;
- const char *name, *passphrase, *alias;
- GHashTable *comp;
- PurpleGroup *g;
- PurpleChat *cn;
-
- f = purple_request_fields_get_field(fields, "name");
- name = purple_request_field_string_get_value(f);
- if (!name) {
- silc_free(p);
- return;
- }
- f = purple_request_fields_get_field(fields, "passphrase");
- passphrase = purple_request_field_string_get_value(f);
- f = purple_request_fields_get_field(fields, "alias");
- alias = purple_request_field_string_get_value(f);
-
- /* Add private group to buddy list */
- g_snprintf(tmp, sizeof(tmp), "%s [Private Group]", name);
- comp = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
- g_hash_table_replace(comp, g_strdup("channel"), g_strdup(tmp));
- g_hash_table_replace(comp, g_strdup("passphrase"), g_strdup(passphrase));
-
- cn = purple_chat_new(sg->account, alias, comp);
- g = (PurpleGroup *)p->c->node.parent;
- purple_blist_add_chat(cn, g, (PurpleBlistNode *)p->c);
-
- /* Associate to a real channel */
- purple_blist_node_set_string((PurpleBlistNode *)cn, "parentch", p->channel);
-
- /* Join the group */
- silcpurple_chat_join(sg->gc, comp);
-
- silc_free(p);
-}
-
-static void
-silcpurple_chat_prv_cancel(SilcPurpleCharPrv p, PurpleRequestFields *fields)
-{
- silc_free(p);
-}
-
-static void
-silcpurple_chat_prv(PurpleBlistNode *node, gpointer data)
-{
- PurpleChat *chat;
- PurpleConnection *gc;
- SilcPurple sg;
-
- SilcPurpleCharPrv p;
- PurpleRequestFields *fields;
- PurpleRequestFieldGroup *g;
- PurpleRequestField *f;
- char tmp[512];
-
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
-
- chat = (PurpleChat *) node;
- gc = purple_account_get_connection(chat->account);
- sg = gc->proto_data;
-
- p = silc_calloc(1, sizeof(*p));
- if (!p)
- return;
- p->sg = sg;
-
- p->channel = g_hash_table_lookup(chat->components, "channel");
- p->c = purple_blist_find_chat(sg->account, p->channel);
-
- fields = purple_request_fields_new();
-
- g = purple_request_field_group_new(NULL);
- f = purple_request_field_string_new("name", _("Group Name"),
- NULL, FALSE);
- purple_request_field_group_add_field(g, f);
-
- f = purple_request_field_string_new("passphrase", _("Passphrase"),
- NULL, FALSE);
- purple_request_field_string_set_masked(f, TRUE);
- purple_request_field_group_add_field(g, f);
-
- f = purple_request_field_string_new("alias", _("Alias"),
- NULL, FALSE);
- purple_request_field_group_add_field(g, f);
- purple_request_fields_add_group(fields, g);
-
- g_snprintf(tmp, sizeof(tmp),
- _("Please enter the %s channel private group name and passphrase."),
- p->channel);
- purple_request_fields(gc, _("Add Channel Private Group"), NULL, tmp, fields,
- _("Add"), G_CALLBACK(silcpurple_chat_prv_add),
- _("Cancel"), G_CALLBACK(silcpurple_chat_prv_cancel),
- purple_connection_get_account(gc), NULL, NULL, p);
-}
-
-
-/****************************** Channel Modes ********************************/
-
-static void
-silcpurple_chat_permanent_reset(PurpleBlistNode *node, gpointer data)
-{
- PurpleChat *chat;
- PurpleConnection *gc;
- SilcPurple sg;
-
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
-
- chat = (PurpleChat *) node;
- gc = purple_account_get_connection(chat->account);
- sg = gc->proto_data;
-
- silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
- g_hash_table_lookup(chat->components, "channel"),
- "-f", NULL);
-}
-
-static void
-silcpurple_chat_permanent(PurpleBlistNode *node, gpointer data)
-{
- PurpleChat *chat;
- PurpleConnection *gc;
- SilcPurple sg;
- const char *channel;
-
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
-
- chat = (PurpleChat *) node;
- gc = purple_account_get_connection(chat->account);
- sg = gc->proto_data;
-
- if (!sg->conn)
- return;
-
- /* XXX we should have ability to define which founder
- key to use. Now we use the user's own public key
- (default key). */
-
- /* Call CMODE */
- channel = g_hash_table_lookup(chat->components, "channel");
- silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", channel,
- "+f", NULL);
-}
-
-typedef struct {
- SilcPurple sg;
- char *channel;
-} *SilcPurpleChatInput;
-
-static void
-silcpurple_chat_ulimit_cb(SilcPurpleChatInput s, const char *limit)
-{
- SilcChannelEntry channel;
- int ulimit = 0;
-
- channel = silc_client_get_channel(s->sg->client, s->sg->conn,
- (char *)s->channel);
- if (!channel)
- return;
- if (limit)
- ulimit = atoi(limit);
-
- if (!limit || !(*limit) || *limit == '0') {
- if (limit && ulimit == channel->user_limit) {
- silc_free(s);
- return;
- }
- silc_client_command_call(s->sg->client, s->sg->conn, NULL, "CMODE",
- s->channel, "-l", NULL);
-
- silc_free(s);
- return;
- }
-
- if (ulimit == channel->user_limit) {
- silc_free(s);
- return;
- }
-
- /* Call CMODE */
- silc_client_command_call(s->sg->client, s->sg->conn, NULL, "CMODE",
- s->channel, "+l", limit, NULL);
-
- silc_free(s);
-}
-
-static void
-silcpurple_chat_ulimit(PurpleBlistNode *node, gpointer data)
-{
- PurpleChat *chat;
- PurpleConnection *gc;
- SilcPurple sg;
-
- SilcPurpleChatInput s;
- SilcChannelEntry channel;
- char *ch;
- char tmp[32];
-
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
-
- chat = (PurpleChat *) node;
- gc = purple_account_get_connection(chat->account);
- sg = gc->proto_data;
-
- if (!sg->conn)
- return;
-
- ch = g_strdup(g_hash_table_lookup(chat->components, "channel"));
- channel = silc_client_get_channel(sg->client, sg->conn, (char *)ch);
- if (!channel)
- return;
-
- s = silc_calloc(1, sizeof(*s));
- if (!s)
- return;
- s->channel = ch;
- s->sg = sg;
- g_snprintf(tmp, sizeof(tmp), "%d", (int)channel->user_limit);
- purple_request_input(gc, _("User Limit"), NULL,
- _("Set user limit on channel. Set to zero to reset user limit."),
- tmp, FALSE, FALSE, NULL,
- _("OK"), G_CALLBACK(silcpurple_chat_ulimit_cb),
- _("Cancel"), G_CALLBACK(silcpurple_chat_ulimit_cb),
- purple_connection_get_account(gc), NULL, NULL, s);
-}
-
-static void
-silcpurple_chat_resettopic(PurpleBlistNode *node, gpointer data)
-{
- PurpleChat *chat;
- PurpleConnection *gc;
- SilcPurple sg;
-
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
-
- chat = (PurpleChat *) node;
- gc = purple_account_get_connection(chat->account);
- sg = gc->proto_data;
-
- silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
- g_hash_table_lookup(chat->components, "channel"),
- "-t", NULL);
-}
-
-static void
-silcpurple_chat_settopic(PurpleBlistNode *node, gpointer data)
-{
- PurpleChat *chat;
- PurpleConnection *gc;
- SilcPurple sg;
-
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
-
- chat = (PurpleChat *) node;
- gc = purple_account_get_connection(chat->account);
- sg = gc->proto_data;
-
- silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
- g_hash_table_lookup(chat->components, "channel"),
- "+t", NULL);
-}
-
-static void
-silcpurple_chat_resetprivate(PurpleBlistNode *node, gpointer data)
-{
- PurpleChat *chat;
- PurpleConnection *gc;
- SilcPurple sg;
-
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
-
- chat = (PurpleChat *) node;
- gc = purple_account_get_connection(chat->account);
- sg = gc->proto_data;
-
- silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
- g_hash_table_lookup(chat->components, "channel"),
- "-p", NULL);
-}
-
-static void
-silcpurple_chat_setprivate(PurpleBlistNode *node, gpointer data)
-{
- PurpleChat *chat;
- PurpleConnection *gc;
- SilcPurple sg;
-
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
-
- chat = (PurpleChat *) node;
- gc = purple_account_get_connection(chat->account);
- sg = gc->proto_data;
-
- silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
- g_hash_table_lookup(chat->components, "channel"),
- "+p", NULL);
-}
-
-static void
-silcpurple_chat_resetsecret(PurpleBlistNode *node, gpointer data)
-{
- PurpleChat *chat;
- PurpleConnection *gc;
- SilcPurple sg;
-
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
-
- chat = (PurpleChat *) node;
- gc = purple_account_get_connection(chat->account);
- sg = gc->proto_data;
-
- silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
- g_hash_table_lookup(chat->components, "channel"),
- "-s", NULL);
-}
-
-static void
-silcpurple_chat_setsecret(PurpleBlistNode *node, gpointer data)
-{
- PurpleChat *chat;
- PurpleConnection *gc;
- SilcPurple sg;
-
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
-
- chat = (PurpleChat *) node;
- gc = purple_account_get_connection(chat->account);
- sg = gc->proto_data;
-
- silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
- g_hash_table_lookup(chat->components, "channel"),
- "+s", NULL);
-}
-
-typedef struct {
- SilcPurple sg;
- SilcChannelEntry channel;
-} *SilcPurpleChatWb;
-
-static void
-silcpurple_chat_wb(PurpleBlistNode *node, gpointer data)
-{
- SilcPurpleChatWb wb = data;
- silcpurple_wb_init_ch(wb->sg, wb->channel);
- silc_free(wb);
-}
-
-GList *silcpurple_chat_menu(PurpleChat *chat)
-{
- GHashTable *components = chat->components;
- PurpleConnection *gc = purple_account_get_connection(chat->account);
- SilcPurple sg = gc->proto_data;
- SilcClientConnection conn = sg->conn;
- const char *chname = NULL;
- SilcChannelEntry channel = NULL;
- SilcChannelUser chu = NULL;
- SilcUInt32 mode = 0;
-
- GList *m = NULL;
- PurpleMenuAction *act;
-
- if (components)
- chname = g_hash_table_lookup(components, "channel");
- if (chname)
- channel = silc_client_get_channel(sg->client, sg->conn,
- (char *)chname);
- if (channel) {
- chu = silc_client_on_channel(channel, conn->local_entry);
- if (chu)
- mode = chu->mode;
- }
-
- if (strstr(chname, "[Private Group]"))
- return NULL;
-
- act = purple_menu_action_new(_("Get Info"),
- PURPLE_CALLBACK(silcpurple_chat_getinfo_menu),
- NULL, NULL);
- m = g_list_append(m, act);
-
-#if 0 /* XXX For now these are not implemented. We need better
- listview dialog from Purple for these. */
- if (mode & SILC_CHANNEL_UMODE_CHANOP) {
- act = purple_menu_action_new(_("Invite List"),
- PURPLE_CALLBACK(silcpurple_chat_invitelist),
- NULL, NULL);
- m = g_list_append(m, act);
-
- act = purple_menu_action_new(_("Ban List"),
- PURPLE_CALLBACK(silcpurple_chat_banlist),
- NULL, NULL);
- m = g_list_append(m, act);
- }
-#endif
-
- if (chu) {
- act = purple_menu_action_new(_("Add Private Group"),
- PURPLE_CALLBACK(silcpurple_chat_prv),
- NULL, NULL);
- m = g_list_append(m, act);
- }
-
- if (mode & SILC_CHANNEL_UMODE_CHANFO) {
- act = purple_menu_action_new(_("Channel Authentication"),
- PURPLE_CALLBACK(silcpurple_chat_chauth),
- NULL, NULL);
- m = g_list_append(m, act);
-
- if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
- act = purple_menu_action_new(_("Reset Permanent"),
- PURPLE_CALLBACK(silcpurple_chat_permanent_reset),
- NULL, NULL);
- m = g_list_append(m, act);
- } else {
- act = purple_menu_action_new(_("Set Permanent"),
- PURPLE_CALLBACK(silcpurple_chat_permanent),
- NULL, NULL);
- m = g_list_append(m, act);
- }
- }
-
- if (mode & SILC_CHANNEL_UMODE_CHANOP) {
- act = purple_menu_action_new(_("Set User Limit"),
- PURPLE_CALLBACK(silcpurple_chat_ulimit),
- NULL, NULL);
- m = g_list_append(m, act);
-
- if (channel->mode & SILC_CHANNEL_MODE_TOPIC) {
- act = purple_menu_action_new(_("Reset Topic Restriction"),
- PURPLE_CALLBACK(silcpurple_chat_resettopic),
- NULL, NULL);
- m = g_list_append(m, act);
- } else {
- act = purple_menu_action_new(_("Set Topic Restriction"),
- PURPLE_CALLBACK(silcpurple_chat_settopic),
- NULL, NULL);
- m = g_list_append(m, act);
- }
-
- if (channel->mode & SILC_CHANNEL_MODE_PRIVATE) {
- act = purple_menu_action_new(_("Reset Private Channel"),
- PURPLE_CALLBACK(silcpurple_chat_resetprivate),
- NULL, NULL);
- m = g_list_append(m, act);
- } else {
- act = purple_menu_action_new(_("Set Private Channel"),
- PURPLE_CALLBACK(silcpurple_chat_setprivate),
- NULL, NULL);
- m = g_list_append(m, act);
- }
-
- if (channel->mode & SILC_CHANNEL_MODE_SECRET) {
- act = purple_menu_action_new(_("Reset Secret Channel"),
- PURPLE_CALLBACK(silcpurple_chat_resetsecret),
- NULL, NULL);
- m = g_list_append(m, act);
- } else {
- act = purple_menu_action_new(_("Set Secret Channel"),
- PURPLE_CALLBACK(silcpurple_chat_setsecret),
- NULL, NULL);
- m = g_list_append(m, act);
- }
- }
-
- if (channel) {
- SilcPurpleChatWb wb;
- wb = silc_calloc(1, sizeof(*wb));
- wb->sg = sg;
- wb->channel = channel;
- act = purple_menu_action_new(_("Draw On Whiteboard"),
- PURPLE_CALLBACK(silcpurple_chat_wb),
- (void *)wb, NULL);
- m = g_list_append(m, act);
- }
-
- return m;
-}
-
-
-/******************************* Joining Etc. ********************************/
-
-void silcpurple_chat_join_done(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry *clients,
- SilcUInt32 clients_count,
- void *context)
-{
- PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
- SilcChannelEntry channel = context;
- PurpleConversation *convo;
- SilcUInt32 retry = SILC_PTR_TO_32(channel->context);
- SilcHashTableList htl;
- SilcChannelUser chu;
- GList *users = NULL, *flags = NULL;
- char tmp[256];
-
- if (!clients && retry < 1) {
- /* Resolving users failed, try again. */
- channel->context = SILC_32_TO_PTR(retry + 1);
- silc_client_get_clients_by_channel(client, conn, channel,
- silcpurple_chat_join_done, channel);
- return;
- }
-
- /* Add channel to Purple */
- channel->context = SILC_32_TO_PTR(++sg->channel_ids);
- serv_got_joined_chat(gc, sg->channel_ids, channel->channel_name);
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- channel->channel_name, sg->account);
- if (!convo)
- return;
-
- /* Add all users to channel */
- silc_hash_table_list(channel->user_list, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- PurpleConvChatBuddyFlags f = PURPLE_CBFLAGS_NONE;
- if (!chu->client->nickname)
- continue;
- chu->context = SILC_32_TO_PTR(sg->channel_ids);
-
- if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
- f |= PURPLE_CBFLAGS_FOUNDER;
- if (chu->mode & SILC_CHANNEL_UMODE_CHANOP)
- f |= PURPLE_CBFLAGS_OP;
- users = g_list_append(users, g_strdup(chu->client->nickname));
- flags = g_list_append(flags, GINT_TO_POINTER(f));
-
- if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) {
- if (chu->client == conn->local_entry)
- g_snprintf(tmp, sizeof(tmp),
- _("You are channel founder on <I>%s</I>"),
- channel->channel_name);
- else
- g_snprintf(tmp, sizeof(tmp),
- _("Channel founder on <I>%s</I> is <I>%s</I>"),
- channel->channel_name, chu->client->nickname);
-
- purple_conversation_write(convo, NULL, tmp,
- PURPLE_MESSAGE_SYSTEM, time(NULL));
-
- }
- }
- silc_hash_table_list_reset(&htl);
-
- purple_conv_chat_add_users(PURPLE_CONV_CHAT(convo), users, NULL, flags, FALSE);
- g_list_free(users);
- g_list_free(flags);
-
- /* Set topic */
- if (channel->topic)
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, channel->topic);
-
- /* Set nick */
- purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), conn->local_entry->nickname);
-}
-
-char *silcpurple_get_chat_name(GHashTable *data)
-{
- return g_strdup(g_hash_table_lookup(data, "channel"));
-}
-
-void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data)
-{
- SilcPurple sg = gc->proto_data;
- SilcClient client = sg->client;
- SilcClientConnection conn = sg->conn;
- const char *channel, *passphrase, *parentch;
-
- if (!conn)
- return;
-
- channel = g_hash_table_lookup(data, "channel");
- passphrase = g_hash_table_lookup(data, "passphrase");
-
- /* Check if we are joining a private group. Handle it
- purely locally as it's not a real channel */
- if (strstr(channel, "[Private Group]")) {
- SilcChannelEntry channel_entry;
- SilcChannelPrivateKey key;
- PurpleChat *c;
- SilcPurplePrvgrp grp;
-
- c = purple_blist_find_chat(sg->account, channel);
- parentch = purple_blist_node_get_string((PurpleBlistNode *)c, "parentch");
- if (!parentch)
- return;
-
- channel_entry = silc_client_get_channel(sg->client, sg->conn,
- (char *)parentch);
- if (!channel_entry ||
- !silc_client_on_channel(channel_entry, sg->conn->local_entry)) {
- char tmp[512];
- g_snprintf(tmp, sizeof(tmp),
- _("You have to join the %s channel before you are "
- "able to join the private group"), parentch);
- purple_notify_error(gc, _("Join Private Group"),
- _("Cannot join private group"), tmp);
- return;
- }
-
- /* Add channel private key */
- if (!silc_client_add_channel_private_key(client, conn,
- channel_entry, channel,
- NULL, NULL,
- (unsigned char *)passphrase,
- strlen(passphrase), &key))
- return;
-
- /* Join the group */
- grp = silc_calloc(1, sizeof(*grp));
- if (!grp)
- return;
- grp->id = ++sg->channel_ids + SILCPURPLE_PRVGRP;
- grp->chid = SILC_PTR_TO_32(channel_entry->context);
- grp->parentch = parentch;
- grp->channel = channel;
- grp->key = key;
- sg->grps = g_list_append(sg->grps, grp);
- serv_got_joined_chat(gc, grp->id, channel);
- return;
- }
-
- /* XXX We should have other properties here as well:
- 1. whether to try to authenticate to the channel
- 1a. with default key,
- 1b. with specific key.
- 2. whether to try to authenticate to become founder.
- 2a. with default key,
- 2b. with specific key.
-
- Since now such variety is not possible in the join dialog
- we always use -founder and -auth options, which try to
- do both 1 and 2 with default keys. */
-
- /* Call JOIN */
- if ((passphrase != NULL) && (*passphrase != '\0'))
- silc_client_command_call(client, conn, NULL, "JOIN",
- channel, passphrase, "-auth", "-founder", NULL);
- else
- silc_client_command_call(client, conn, NULL, "JOIN",
- channel, "-auth", "-founder", NULL);
-}
-
-void silcpurple_chat_invite(PurpleConnection *gc, int id, const char *msg,
- const char *name)
-{
- SilcPurple sg = gc->proto_data;
- SilcClient client = sg->client;
- SilcClientConnection conn = sg->conn;
- SilcHashTableList htl;
- SilcChannelUser chu;
- gboolean found = FALSE;
-
- if (!conn)
- return;
-
- /* See if we are inviting on a private group. Invite
- to the actual channel */
- if (id > SILCPURPLE_PRVGRP) {
- GList *l;
- SilcPurplePrvgrp prv;
-
- for (l = sg->grps; l; l = l->next)
- if (((SilcPurplePrvgrp)l->data)->id == id)
- break;
- if (!l)
- return;
- prv = l->data;
- id = prv->chid;
- }
-
- /* Find channel by id */
- silc_hash_table_list(conn->local_entry->channels, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- if (SILC_PTR_TO_32(chu->channel->context) == id ) {
- found = TRUE;
- break;
- }
- }
- silc_hash_table_list_reset(&htl);
- if (!found)
- return;
-
- /* Call INVITE */
- silc_client_command_call(client, conn, NULL, "INVITE",
- chu->channel->channel_name,
- name, NULL);
-}
-
-void silcpurple_chat_leave(PurpleConnection *gc, int id)
-{
- SilcPurple sg = gc->proto_data;
- SilcClient client = sg->client;
- SilcClientConnection conn = sg->conn;
- SilcHashTableList htl;
- SilcChannelUser chu;
- gboolean found = FALSE;
- GList *l;
- SilcPurplePrvgrp prv;
-
- if (!conn)
- return;
-
- /* See if we are leaving a private group */
- if (id > SILCPURPLE_PRVGRP) {
- SilcChannelEntry channel;
-
- for (l = sg->grps; l; l = l->next)
- if (((SilcPurplePrvgrp)l->data)->id == id)
- break;
- if (!l)
- return;
- prv = l->data;
- channel = silc_client_get_channel(sg->client, sg->conn,
- (char *)prv->parentch);
- if (!channel)
- return;
- silc_client_del_channel_private_key(client, conn,
- channel, prv->key);
- silc_free(prv);
- sg->grps = g_list_remove(sg->grps, prv);
- serv_got_chat_left(gc, id);
- return;
- }
-
- /* Find channel by id */
- silc_hash_table_list(conn->local_entry->channels, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- if (SILC_PTR_TO_32(chu->channel->context) == id ) {
- found = TRUE;
- break;
- }
- }
- silc_hash_table_list_reset(&htl);
- if (!found)
- return;
-
- /* Call LEAVE */
- silc_client_command_call(client, conn, NULL, "LEAVE",
- chu->channel->channel_name, NULL);
-
- serv_got_chat_left(gc, id);
-
- /* Leave from private groups on this channel as well */
- for (l = sg->grps; l; l = l->next)
- if (((SilcPurplePrvgrp)l->data)->chid == id) {
- prv = l->data;
- silc_client_del_channel_private_key(client, conn,
- chu->channel,
- prv->key);
- serv_got_chat_left(gc, prv->id);
- silc_free(prv);
- sg->grps = g_list_remove(sg->grps, prv);
- if (!sg->grps)
- break;
- }
-}
-
-int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg, PurpleMessageFlags msgflags)
-{
- SilcPurple sg = gc->proto_data;
- SilcClient client = sg->client;
- SilcClientConnection conn = sg->conn;
- SilcHashTableList htl;
- SilcChannelUser chu;
- SilcChannelEntry channel = NULL;
- SilcChannelPrivateKey key = NULL;
- SilcUInt32 flags;
- int ret;
- char *msg2, *tmp;
- gboolean found = FALSE;
- gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE);
-
- if (!msg || !conn)
- return 0;
-
- flags = SILC_MESSAGE_FLAG_UTF8;
-
- tmp = msg2 = purple_unescape_html(msg);
-
- if (!g_ascii_strncasecmp(msg2, "/me ", 4))
- {
- msg2 += 4;
- if (!*msg2) {
- g_free(tmp);
- return 0;
- }
- flags |= SILC_MESSAGE_FLAG_ACTION;
- } else if (strlen(msg) > 1 && msg[0] == '/') {
- if (!silc_client_command_call(client, conn, msg + 1))
- purple_notify_error(gc, _("Call Command"), _("Cannot call command"),
- _("Unknown command"));
- g_free(tmp);
- return 0;
- }
-
-
- if (sign)
- flags |= SILC_MESSAGE_FLAG_SIGNED;
-
- /* Get the channel private key if we are sending on
- private group */
- if (id > SILCPURPLE_PRVGRP) {
- GList *l;
- SilcPurplePrvgrp prv;
-
- for (l = sg->grps; l; l = l->next)
- if (((SilcPurplePrvgrp)l->data)->id == id)
- break;
- if (!l) {
- g_free(tmp);
- return 0;
- }
- prv = l->data;
- channel = silc_client_get_channel(sg->client, sg->conn,
- (char *)prv->parentch);
- if (!channel) {
- g_free(tmp);
- return 0;
- }
- key = prv->key;
- }
-
- if (!channel) {
- /* Find channel by id */
- silc_hash_table_list(conn->local_entry->channels, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- if (SILC_PTR_TO_32(chu->channel->context) == id ) {
- found = TRUE;
- break;
- }
- }
- silc_hash_table_list_reset(&htl);
- if (!found) {
- g_free(tmp);
- return 0;
- }
- channel = chu->channel;
- }
-
- /* Send channel message */
- ret = silc_client_send_channel_message(client, conn, channel, key,
- flags, (unsigned char *)msg2,
- strlen(msg2), TRUE);
- if (ret) {
- serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), msgflags, msg,
- time(NULL));
- }
- g_free(tmp);
-
- return ret;
-}
-
-void silcpurple_chat_set_topic(PurpleConnection *gc, int id, const char *topic)
-{
- SilcPurple sg = gc->proto_data;
- SilcClient client = sg->client;
- SilcClientConnection conn = sg->conn;
- SilcHashTableList htl;
- SilcChannelUser chu;
- gboolean found = FALSE;
-
- if (!conn)
- return;
-
- /* See if setting topic on private group. Set it
- on the actual channel */
- if (id > SILCPURPLE_PRVGRP) {
- GList *l;
- SilcPurplePrvgrp prv;
-
- for (l = sg->grps; l; l = l->next)
- if (((SilcPurplePrvgrp)l->data)->id == id)
- break;
- if (!l)
- return;
- prv = l->data;
- id = prv->chid;
- }
-
- /* Find channel by id */
- silc_hash_table_list(conn->local_entry->channels, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- if (SILC_PTR_TO_32(chu->channel->context) == id ) {
- found = TRUE;
- break;
- }
- }
- silc_hash_table_list_reset(&htl);
- if (!found)
- return;
-
- /* Call TOPIC */
- silc_client_command_call(client, conn, NULL, "TOPIC",
- chu->channel->channel_name, topic, NULL);
-}
-
-PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc)
-{
- SilcPurple sg = gc->proto_data;
- SilcClient client = sg->client;
- SilcClientConnection conn = sg->conn;
- GList *fields = NULL;
- PurpleRoomlistField *f;
-
- if (!conn)
- return NULL;
-
- if (sg->roomlist)
- purple_roomlist_unref(sg->roomlist);
-
- sg->roomlist_cancelled = FALSE;
-
- sg->roomlist = purple_roomlist_new(purple_connection_get_account(gc));
- f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", "channel", TRUE);
- fields = g_list_append(fields, f);
- f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_INT,
- _("Users"), "users", FALSE);
- fields = g_list_append(fields, f);
- f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING,
- _("Topic"), "topic", FALSE);
- fields = g_list_append(fields, f);
- purple_roomlist_set_fields(sg->roomlist, fields);
-
- /* Call LIST */
- silc_client_command_call(client, conn, "LIST");
-
- purple_roomlist_set_in_progress(sg->roomlist, TRUE);
-
- return sg->roomlist;
-}
-
-void silcpurple_roomlist_cancel(PurpleRoomlist *list)
-{
- PurpleConnection *gc = purple_account_get_connection(list->account);
- SilcPurple sg;
-
- if (!gc)
- return;
- sg = gc->proto_data;
-
- purple_roomlist_set_in_progress(list, FALSE);
- if (sg->roomlist == list) {
- purple_roomlist_unref(sg->roomlist);
- sg->roomlist = NULL;
- sg->roomlist_cancelled = TRUE;
- }
-}
diff --git a/libpurple/protocols/silc10/ft.c b/libpurple/protocols/silc10/ft.c
deleted file mode 100644
index 39e74495b7..0000000000
--- a/libpurple/protocols/silc10/ft.c
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
-
- silcpurple_ft.c
-
- Author: Pekka Riikonen <priikone@silcnet.org>
-
- Copyright (C) 2004 Pekka Riikonen
-
- 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; version 2 of the License.
-
- 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.
-
-*/
-
-#include "silcincludes.h"
-#include "silcclient.h"
-#include "silcpurple.h"
-
-/****************************** File Transfer ********************************/
-
-/* This implements the secure file transfer protocol (SFTP) using the SILC
- SFTP library implementation. The API we use from the SILC Toolkit is the
- SILC Client file transfer API, as it provides a simple file transfer we
- need in this case. We could use the SILC SFTP API directly, but it would
- be an overkill since we'd effectively re-implement the file transfer what
- the SILC Client's file transfer API already provides.
-
- From Purple we do NOT use the FT API to do the transfer as it is very limiting.
- In fact it does not suite to file transfers like SFTP at all. For example,
- it assumes that read operations are synchronous what they are not in SFTP.
- It also assumes that the file transfer socket is to be handled by the Purple
- eventloop, and this naturally is something we don't want to do in case of
- SILC Toolkit. The FT API suites well to purely stream based file transfers
- like HTTP GET and similar.
-
- For this reason, we directly access the Purple GKT FT API and hack the FT
- API to merely provide the user interface experience and all the magic
- is done in the SILC Toolkit. Ie. we update the statistics information in
- the FT API for user interface, and that's it. A bit dirty but until the
- FT API gets better this is the way to go. Good thing that FT API allowed
- us to do this. */
-
-typedef struct {
- SilcPurple sg;
- SilcClientEntry client_entry;
- SilcUInt32 session_id;
- char *hostname;
- SilcUInt16 port;
- PurpleXfer *xfer;
-
- SilcClientFileName completion;
- void *completion_context;
-} *SilcPurpleXfer;
-
-static void
-silcpurple_ftp_monitor(SilcClient client,
- SilcClientConnection conn,
- SilcClientMonitorStatus status,
- SilcClientFileError error,
- SilcUInt64 offset,
- SilcUInt64 filesize,
- SilcClientEntry client_entry,
- SilcUInt32 session_id,
- const char *filepath,
- void *context)
-{
- SilcPurpleXfer xfer = context;
- PurpleConnection *gc = xfer->sg->gc;
- char tmp[256];
-
- if (status == SILC_CLIENT_FILE_MONITOR_CLOSED) {
- purple_xfer_unref(xfer->xfer);
- silc_free(xfer);
- return;
- }
-
- if (status == SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT)
- return;
-
- if (status == SILC_CLIENT_FILE_MONITOR_ERROR) {
- if (error == SILC_CLIENT_FILE_NO_SUCH_FILE) {
- g_snprintf(tmp, sizeof(tmp), "No such file %s",
- filepath ? filepath : "[N/A]");
- purple_notify_error(gc, _("Secure File Transfer"),
- _("Error during file transfer"), tmp);
- } else if (error == SILC_CLIENT_FILE_PERMISSION_DENIED) {
- purple_notify_error(gc, _("Secure File Transfer"),
- _("Error during file transfer"),
- _("Permission denied"));
- } else if (error == SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED) {
- purple_notify_error(gc, _("Secure File Transfer"),
- _("Error during file transfer"),
- _("Key agreement failed"));
- } else if (error == SILC_CLIENT_FILE_UNKNOWN_SESSION) {
- purple_notify_error(gc, _("Secure File Transfer"),
- _("Error during file transfer"),
- _("File transfer session does not exist"));
- } else {
- purple_notify_error(gc, _("Secure File Transfer"),
- _("Error during file transfer"), NULL);
- }
- silc_client_file_close(client, conn, session_id);
- purple_xfer_unref(xfer->xfer);
- silc_free(xfer);
- return;
- }
-
- /* Update file transfer UI */
- if (!offset && filesize)
- purple_xfer_set_size(xfer->xfer, filesize);
- if (offset && filesize) {
- xfer->xfer->bytes_sent = offset;
- xfer->xfer->bytes_remaining = filesize - offset;
- }
- purple_xfer_update_progress(xfer->xfer);
-
- if (status == SILC_CLIENT_FILE_MONITOR_SEND ||
- status == SILC_CLIENT_FILE_MONITOR_RECEIVE) {
- if (offset == filesize) {
- /* Download finished */
- purple_xfer_set_completed(xfer->xfer, TRUE);
- silc_client_file_close(client, conn, session_id);
- }
- }
-}
-
-static void
-silcpurple_ftp_cancel(PurpleXfer *x)
-{
- SilcPurpleXfer xfer = x->data;
- xfer->xfer->status = PURPLE_XFER_STATUS_CANCEL_LOCAL;
- purple_xfer_update_progress(xfer->xfer);
- silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
-}
-
-static void
-silcpurple_ftp_ask_name_cancel(PurpleXfer *x)
-{
- SilcPurpleXfer xfer = x->data;
-
- /* Cancel the transmission */
- xfer->completion(NULL, xfer->completion_context);
- silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
-}
-
-static void
-silcpurple_ftp_ask_name_ok(PurpleXfer *x)
-{
- SilcPurpleXfer xfer = x->data;
- const char *name;
-
- name = purple_xfer_get_local_filename(x);
- g_unlink(name);
- xfer->completion(name, xfer->completion_context);
-}
-
-static void
-silcpurple_ftp_ask_name(SilcClient client,
- SilcClientConnection conn,
- SilcUInt32 session_id,
- const char *remote_filename,
- SilcClientFileName completion,
- void *completion_context,
- void *context)
-{
- SilcPurpleXfer xfer = context;
-
- xfer->completion = completion;
- xfer->completion_context = completion_context;
-
- purple_xfer_set_init_fnc(xfer->xfer, silcpurple_ftp_ask_name_ok);
- purple_xfer_set_request_denied_fnc(xfer->xfer, silcpurple_ftp_ask_name_cancel);
-
- /* Request to save the file */
- purple_xfer_set_filename(xfer->xfer, remote_filename);
- purple_xfer_request(xfer->xfer);
-}
-
-static void
-silcpurple_ftp_request_result(PurpleXfer *x)
-{
- SilcPurpleXfer xfer = x->data;
- SilcClientFileError status;
- PurpleConnection *gc = xfer->sg->gc;
-
- if (purple_xfer_get_status(x) != PURPLE_XFER_STATUS_ACCEPTED)
- return;
-
- /* Start the file transfer */
- status = silc_client_file_receive(xfer->sg->client, xfer->sg->conn,
- silcpurple_ftp_monitor, xfer,
- NULL, xfer->session_id,
- silcpurple_ftp_ask_name, xfer);
- switch (status) {
- case SILC_CLIENT_FILE_OK:
- return;
- break;
-
- case SILC_CLIENT_FILE_UNKNOWN_SESSION:
- purple_notify_error(gc, _("Secure File Transfer"),
- _("No file transfer session active"), NULL);
- break;
-
- case SILC_CLIENT_FILE_ALREADY_STARTED:
- purple_notify_error(gc, _("Secure File Transfer"),
- _("File transfer already started"), NULL);
- break;
-
- case SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED:
- purple_notify_error(gc, _("Secure File Transfer"),
- _("Could not perform key agreement for file transfer"),
- NULL);
- break;
-
- default:
- purple_notify_error(gc, _("Secure File Transfer"),
- _("Could not start the file transfer"), NULL);
- break;
- }
-
- /* Error */
- purple_xfer_unref(xfer->xfer);
- g_free(xfer->hostname);
- silc_free(xfer);
-}
-
-static void
-silcpurple_ftp_request_denied(PurpleXfer *x)
-{
-
-}
-
-void silcpurple_ftp_request(SilcClient client, SilcClientConnection conn,
- SilcClientEntry client_entry, SilcUInt32 session_id,
- const char *hostname, SilcUInt16 port)
-{
- PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
- SilcPurpleXfer xfer;
-
- xfer = silc_calloc(1, sizeof(*xfer));
- if (!xfer) {
- silc_client_file_close(sg->client, sg->conn, session_id);
- return;
- }
-
- xfer->sg = sg;
- xfer->client_entry = client_entry;
- xfer->session_id = session_id;
- xfer->hostname = g_strdup(hostname);
- xfer->port = port;
- xfer->xfer = purple_xfer_new(xfer->sg->account, PURPLE_XFER_RECEIVE,
- xfer->client_entry->nickname);
- if (!xfer->xfer) {
- silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
- g_free(xfer->hostname);
- silc_free(xfer);
- return;
- }
- purple_xfer_set_init_fnc(xfer->xfer, silcpurple_ftp_request_result);
- purple_xfer_set_request_denied_fnc(xfer->xfer, silcpurple_ftp_request_denied);
- purple_xfer_set_cancel_recv_fnc(xfer->xfer, silcpurple_ftp_cancel);
- xfer->xfer->remote_ip = g_strdup(hostname);
- xfer->xfer->remote_port = port;
- xfer->xfer->data = xfer;
-
- /* File transfer request */
- purple_xfer_request(xfer->xfer);
-}
-
-static void
-silcpurple_ftp_send_cancel(PurpleXfer *x)
-{
- SilcPurpleXfer xfer = x->data;
- silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
- purple_xfer_unref(xfer->xfer);
- g_free(xfer->hostname);
- silc_free(xfer);
-}
-
-static void
-silcpurple_ftp_send(PurpleXfer *x)
-{
- SilcPurpleXfer xfer = x->data;
- const char *name;
- char *local_ip = NULL, *remote_ip = NULL;
- gboolean local = TRUE;
-
- name = purple_xfer_get_local_filename(x);
-
- /* Do the same magic what we do with key agreement (see silcpurple_buddy.c)
- to see if we are behind NAT. */
- if (silc_net_check_local_by_sock(xfer->sg->conn->sock->sock,
- NULL, &local_ip)) {
- /* Check if the IP is private */
- if (silcpurple_ip_is_private(local_ip)) {
- local = FALSE;
- /* Local IP is private, resolve the remote server IP to see whether
- we are talking to Internet or just on LAN. */
- if (silc_net_check_host_by_sock(xfer->sg->conn->sock->sock, NULL,
- &remote_ip))
- if (silcpurple_ip_is_private(remote_ip))
- /* We assume we are in LAN. Let's provide the connection point. */
- local = TRUE;
- }
- }
-
- if (local && !local_ip)
- local_ip = silc_net_localip();
-
- /* Send the file */
- silc_client_file_send(xfer->sg->client, xfer->sg->conn,
- silcpurple_ftp_monitor, xfer,
- local_ip, 0, !local, xfer->client_entry,
- name, &xfer->session_id);
-
- silc_free(local_ip);
- silc_free(remote_ip);
-}
-
-static void
-silcpurple_ftp_send_file_resolved(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry *clients,
- SilcUInt32 clients_count,
- void *context)
-{
- PurpleConnection *gc = client->application;
- char tmp[256];
-
- if (!clients) {
- g_snprintf(tmp, sizeof(tmp),
- _("User %s is not present in the network"),
- (const char *)context);
- purple_notify_error(gc, _("Secure File Transfer"),
- _("Cannot send file"), tmp);
- silc_free(context);
- return;
- }
-
- silcpurple_ftp_send_file(client->application, (const char *)context, NULL);
- silc_free(context);
-}
-
-PurpleXfer *silcpurple_ftp_new_xfer(PurpleConnection *gc, const char *name)
-{
- SilcPurple sg = gc->proto_data;
- SilcClient client = sg->client;
- SilcClientConnection conn = sg->conn;
- SilcClientEntry *clients;
- SilcUInt32 clients_count;
- SilcPurpleXfer xfer;
- char *nickname;
-
- g_return_val_if_fail(name != NULL, NULL);
-
- if (!silc_parse_userfqdn(name, &nickname, NULL))
- return NULL;
-
- /* Find client entry */
- clients = silc_client_get_clients_local(client, conn, nickname, name,
- &clients_count);
- if (!clients) {
- silc_client_get_clients(client, conn, nickname, NULL,
- silcpurple_ftp_send_file_resolved,
- strdup(name));
- silc_free(nickname);
- return NULL;
- }
-
- xfer = silc_calloc(1, sizeof(*xfer));
-
- g_return_val_if_fail(xfer != NULL, NULL);
-
- xfer->sg = sg;
- xfer->client_entry = clients[0];
- xfer->xfer = purple_xfer_new(xfer->sg->account, PURPLE_XFER_SEND,
- xfer->client_entry->nickname);
- if (!xfer->xfer) {
- silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
- g_free(xfer->hostname);
- silc_free(xfer);
- return NULL;
- }
- purple_xfer_set_init_fnc(xfer->xfer, silcpurple_ftp_send);
- purple_xfer_set_request_denied_fnc(xfer->xfer, silcpurple_ftp_request_denied);
- purple_xfer_set_cancel_send_fnc(xfer->xfer, silcpurple_ftp_send_cancel);
- xfer->xfer->data = xfer;
-
- silc_free(clients);
- silc_free(nickname);
-
- return xfer->xfer;
-}
-
-void silcpurple_ftp_send_file(PurpleConnection *gc, const char *name, const char *file)
-{
- PurpleXfer *xfer = silcpurple_ftp_new_xfer(gc, name);
-
- g_return_if_fail(xfer != NULL);
-
- /* Choose file to send */
- if (file)
- purple_xfer_request_accepted(xfer, file);
- else
- purple_xfer_request(xfer);
-}
diff --git a/libpurple/protocols/silc10/ops.c b/libpurple/protocols/silc10/ops.c
deleted file mode 100644
index 6095c49b18..0000000000
--- a/libpurple/protocols/silc10/ops.c
+++ /dev/null
@@ -1,2063 +0,0 @@
-/*
-
- silcpurple_ops.c
-
- Author: Pekka Riikonen <priikone@silcnet.org>
-
- Copyright (C) 2004 Pekka Riikonen
-
- 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; version 2 of the License.
-
- 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.
-
-*/
-
-#include "silcincludes.h"
-#include "silcclient.h"
-#include "silcpurple.h"
-#include "imgstore.h"
-#include "wb.h"
-
-static void
-silc_channel_message(SilcClient client, SilcClientConnection conn,
- SilcClientEntry sender, SilcChannelEntry channel,
- SilcMessagePayload payload, SilcChannelPrivateKey key,
- SilcMessageFlags flags, const unsigned char *message,
- SilcUInt32 message_len);
-static void
-silc_private_message(SilcClient client, SilcClientConnection conn,
- SilcClientEntry sender, SilcMessagePayload payload,
- SilcMessageFlags flags, const unsigned char *message,
- SilcUInt32 message_len);
-
-/* Message sent to the application by library. `conn' associates the
- message to a specific connection. `conn', however, may be NULL.
- The `type' indicates the type of the message sent by the library.
- The application can for example filter the message according the
- type. */
-
-static void
-silc_say(SilcClient client, SilcClientConnection conn,
- SilcClientMessageType type, char *msg, ...)
-{
- /* Nothing */
-}
-
-#ifdef HAVE_SILCMIME_H
-/* Processes incoming MIME message. Can be private message or channel
- message. */
-
-static void
-silcpurple_mime_message(SilcClient client, SilcClientConnection conn,
- SilcClientEntry sender, SilcChannelEntry channel,
- SilcMessagePayload payload, SilcChannelPrivateKey key,
- SilcMessageFlags flags, SilcMime mime,
- gboolean recursive)
-{
- PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
- const char *type;
- const unsigned char *data;
- SilcUInt32 data_len;
- PurpleMessageFlags cflags = 0;
- PurpleConversation *convo = NULL;
-
- if (!mime)
- return;
-
- /* Check for fragmented MIME message */
- if (silc_mime_is_partial(mime)) {
- if (!sg->mimeass)
- sg->mimeass = silc_mime_assembler_alloc();
-
- /* Defragment */
- mime = silc_mime_assemble(sg->mimeass, mime);
- if (!mime)
- /* More fragments to come */
- return;
-
- /* Process the complete message */
- silcpurple_mime_message(client, conn, sender, channel,
- payload, key, flags, mime, FALSE);
- return;
- }
-
- /* Check for multipart message */
- if (silc_mime_is_multipart(mime)) {
- SilcMime p;
- const char *mtype;
- SilcDList parts = silc_mime_get_multiparts(mime, &mtype);
-
- /* Only "mixed" type supported */
- if (strcmp(mtype, "mixed"))
- goto out;
-
- silc_dlist_start(parts);
- while ((p = silc_dlist_get(parts)) != SILC_LIST_END) {
- /* Recursively process parts */
- silcpurple_mime_message(client, conn, sender, channel,
- payload, key, flags, p, TRUE);
- }
- goto out;
- }
-
- /* Get content type and MIME data */
- type = silc_mime_get_field(mime, "Content-Type");
- if (!type)
- goto out;
- data = silc_mime_get_data(mime, &data_len);
- if (!data)
- goto out;
-
- /* Process according to content type */
-
- /* Plain text */
- if (strstr(type, "text/plain")) {
- /* Default is UTF-8, don't check for other charsets */
- if (!strstr(type, "utf-8"))
- goto out;
-
- if (channel)
- silc_channel_message(client, conn, sender, channel,
- payload, key,
- SILC_MESSAGE_FLAG_UTF8, data,
- data_len);
- else
- silc_private_message(client, conn, sender, payload,
- SILC_MESSAGE_FLAG_UTF8, data,
- data_len);
- goto out;
- }
-
- /* Image */
- if (strstr(type, "image/png") ||
- strstr(type, "image/jpeg") ||
- strstr(type, "image/gif") ||
- strstr(type, "image/tiff")) {
- char tmp[32];
- int imgid;
-
- /* Get channel convo (if message is for channel) */
- if (key && channel) {
- GList *l;
- SilcPurplePrvgrp prv;
-
- for (l = sg->grps; l; l = l->next)
- if (((SilcPurplePrvgrp)l->data)->key == key) {
- prv = l->data;
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- prv->channel, sg->account);
- break;
- }
- }
- if (channel && !convo)
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- channel->channel_name, sg->account);
- if (channel && !convo)
- goto out;
-
- imgid = purple_imgstore_add_with_id(g_memdup(data, data_len), data_len, "");
- if (imgid) {
- cflags |= PURPLE_MESSAGE_IMAGES | PURPLE_MESSAGE_RECV;
- g_snprintf(tmp, sizeof(tmp), "<IMG ID=\"%d\">", imgid);
-
- if (channel)
- serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
- sender->nickname ?
- sender->nickname :
- "<unknown>", cflags,
- tmp, time(NULL));
- else
- serv_got_im(gc, sender->nickname ?
- sender->nickname : "<unknown>",
- tmp, cflags, time(NULL));
-
- purple_imgstore_unref_by_id(imgid);
- cflags = 0;
- }
- goto out;
- }
-
- /* Whiteboard message */
- if (strstr(type, "application/x-wb") &&
- !purple_account_get_bool(sg->account, "block-wb", FALSE)) {
- if (channel)
- silcpurple_wb_receive_ch(client, conn, sender, channel,
- payload, flags, data, data_len);
- else
- silcpurple_wb_receive(client, conn, sender, payload,
- flags, data, data_len);
- goto out;
- }
-
- out:
- if (!recursive)
- silc_mime_free(mime);
-}
-#endif /* HAVE_SILCMIME_H */
-
-/* Message for a channel. The `sender' is the sender of the message
- The `channel' is the channel. The `message' is the message. Note
- that `message' maybe NULL. The `flags' indicates message flags
- and it is used to determine how the message can be interpreted
- (like it may tell the message is multimedia message). */
-
-static void
-silc_channel_message(SilcClient client, SilcClientConnection conn,
- SilcClientEntry sender, SilcChannelEntry channel,
- SilcMessagePayload payload, SilcChannelPrivateKey key,
- SilcMessageFlags flags, const unsigned char *message,
- SilcUInt32 message_len)
-{
- PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
- PurpleConversation *convo = NULL;
- char *msg, *tmp;
-
- if (!message)
- return;
-
- if (key) {
- GList *l;
- SilcPurplePrvgrp prv;
-
- for (l = sg->grps; l; l = l->next)
- if (((SilcPurplePrvgrp)l->data)->key == key) {
- prv = l->data;
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- prv->channel, sg->account);
- break;
- }
- }
- if (!convo)
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- channel->channel_name, sg->account);
- if (!convo)
- return;
-
- if (flags & SILC_MESSAGE_FLAG_SIGNED &&
- purple_account_get_bool(sg->account, "sign-verify", FALSE)) {
- /* XXX */
- }
-
- if (flags & SILC_MESSAGE_FLAG_DATA) {
- /* Process MIME message */
-#ifdef HAVE_SILCMIME_H
- SilcMime mime;
- mime = silc_mime_decode(message, message_len);
- silcpurple_mime_message(client, conn, sender, channel, payload,
- key, flags, mime, FALSE);
-#else
- char type[128], enc[128];
- unsigned char *data;
- SilcUInt32 data_len;
-
- memset(type, 0, sizeof(type));
- memset(enc, 0, sizeof(enc));
-
- if (!silc_mime_parse(message, message_len, NULL, 0,
- type, sizeof(type) - 1, enc, sizeof(enc) - 1, &data,
- &data_len))
- return;
-
- if (!strcmp(type, "application/x-wb") &&
- !strcmp(enc, "binary") &&
- !purple_account_get_bool(sg->account, "block-wb", FALSE))
- silcpurple_wb_receive_ch(client, conn, sender, channel,
- payload, flags, data, data_len);
-#endif
- return;
- }
-
- if (flags & SILC_MESSAGE_FLAG_ACTION) {
- msg = g_strdup_printf("/me %s",
- (const char *)message);
- if (!msg)
- return;
-
- tmp = g_markup_escape_text(msg, -1);
- /* Send to Purple */
- serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
- sender->nickname ?
- sender->nickname : "<unknown>", 0,
- tmp, time(NULL));
- g_free(tmp);
- g_free(msg);
- return;
- }
-
- if (flags & SILC_MESSAGE_FLAG_NOTICE) {
- msg = g_strdup_printf("(notice) <I>%s</I> %s",
- sender->nickname ?
- sender->nickname : "<unknown>",
- (const char *)message);
- if (!msg)
- return;
-
- /* Send to Purple */
- purple_conversation_write(convo, NULL, (const char *)msg,
- PURPLE_MESSAGE_SYSTEM, time(NULL));
- g_free(msg);
- return;
- }
-
- if (flags & SILC_MESSAGE_FLAG_UTF8) {
- tmp = g_markup_escape_text((const char *)message, -1);
- /* Send to Purple */
- serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
- sender->nickname ?
- sender->nickname : "<unknown>", 0,
- tmp, time(NULL));
- g_free(tmp);
- }
-}
-
-
-/* Private message to the client. The `sender' is the sender of the
- message. The message is `message'and maybe NULL. The `flags'
- indicates message flags and it is used to determine how the message
- can be interpreted (like it may tell the message is multimedia
- message). */
-
-static void
-silc_private_message(SilcClient client, SilcClientConnection conn,
- SilcClientEntry sender, SilcMessagePayload payload,
- SilcMessageFlags flags, const unsigned char *message,
- SilcUInt32 message_len)
-{
- PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
- PurpleConversation *convo = NULL;
- char *msg, *tmp;
-
- if (!message)
- return;
-
- if (sender->nickname)
- /* XXX - Should this be PURPLE_CONV_TYPE_IM? */
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
- sender->nickname, sg->account);
-
- if (flags & SILC_MESSAGE_FLAG_SIGNED &&
- purple_account_get_bool(sg->account, "sign-verify", FALSE)) {
- /* XXX */
- }
-
- if (flags & SILC_MESSAGE_FLAG_DATA) {
-#ifdef HAVE_SILCMIME_H
- /* Process MIME message */
- SilcMime mime;
- mime = silc_mime_decode(message, message_len);
- silcpurple_mime_message(client, conn, sender, NULL, payload,
- NULL, flags, mime, FALSE);
-#else
- char type[128], enc[128];
- unsigned char *data;
- SilcUInt32 data_len;
-
- memset(type, 0, sizeof(type));
- memset(enc, 0, sizeof(enc));
-
- if (!silc_mime_parse(message, message_len, NULL, 0,
- type, sizeof(type) - 1, enc, sizeof(enc) - 1, &data,
- &data_len))
- return;
-
- if (!strcmp(type, "application/x-wb") &&
- !strcmp(enc, "binary") &&
- !purple_account_get_bool(sg->account, "block-wb", FALSE))
- silcpurple_wb_receive(client, conn, sender, payload,
- flags, data, data_len);
-#endif
- return;
- }
-
- if (flags & SILC_MESSAGE_FLAG_ACTION && convo) {
- msg = g_strdup_printf("/me %s",
- (const char *)message);
- if (!msg)
- return;
-
- tmp = g_markup_escape_text(msg, -1);
- /* Send to Purple */
- serv_got_im(gc, sender->nickname ?
- sender->nickname : "<unknown>",
- tmp, 0, time(NULL));
- g_free(msg);
- g_free(tmp);
- return;
- }
-
- if (flags & SILC_MESSAGE_FLAG_NOTICE && convo) {
- msg = g_strdup_printf("(notice) <I>%s</I> %s",
- sender->nickname ?
- sender->nickname : "<unknown>",
- (const char *)message);
- if (!msg)
- return;
-
- /* Send to Purple */
- purple_conversation_write(convo, NULL, (const char *)msg,
- PURPLE_MESSAGE_SYSTEM, time(NULL));
- g_free(msg);
- return;
- }
-
- if (flags & SILC_MESSAGE_FLAG_UTF8) {
- tmp = g_markup_escape_text((const char *)message, -1);
- /* Send to Purple */
- serv_got_im(gc, sender->nickname ?
- sender->nickname : "<unknown>",
- tmp, 0, time(NULL));
- g_free(tmp);
- }
-}
-
-
-/* Notify message to the client. The notify arguments are sent in the
- same order as servers sends them. The arguments are same as received
- from the server except for ID's. If ID is received application receives
- the corresponding entry to the ID. For example, if Client ID is received
- application receives SilcClientEntry. Also, if the notify type is
- for channel the channel entry is sent to application (even if server
- does not send it because client library gets the channel entry from
- the Channel ID in the packet's header). */
-
-static void
-silc_notify(SilcClient client, SilcClientConnection conn,
- SilcNotifyType type, ...)
-{
- va_list va;
- PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
- PurpleConversation *convo;
- SilcClientEntry client_entry, client_entry2;
- SilcChannelEntry channel;
- SilcServerEntry server_entry;
- SilcIdType idtype;
- void *entry;
- SilcUInt32 mode;
- SilcHashTableList htl;
- SilcChannelUser chu;
- char buf[512], buf2[512], *tmp, *name;
- SilcNotifyType notify;
- PurpleBuddy *b;
- int i;
-
- va_start(va, type);
- memset(buf, 0, sizeof(buf));
-
- switch (type) {
-
- case SILC_NOTIFY_TYPE_NONE:
- break;
-
- case SILC_NOTIFY_TYPE_INVITE:
- {
- GHashTable *components;
- va_arg(va, SilcChannelEntry);
- name = va_arg(va, char *);
- client_entry = va_arg(va, SilcClientEntry);
-
- components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
- g_hash_table_insert(components, strdup("channel"), strdup(name));
- serv_got_chat_invite(gc, name, client_entry->nickname, NULL, components);
- }
- break;
-
- case SILC_NOTIFY_TYPE_JOIN:
- client_entry = va_arg(va, SilcClientEntry);
- channel = va_arg(va, SilcChannelEntry);
-
- /* If we joined channel, do nothing */
- if (client_entry == conn->local_entry)
- break;
-
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- channel->channel_name, sg->account);
- if (!convo)
- break;
-
- /* Join user to channel */
- g_snprintf(buf, sizeof(buf), "%s@%s",
- client_entry->username, client_entry->hostname);
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(convo),
- g_strdup(client_entry->nickname), buf, PURPLE_CBFLAGS_NONE, TRUE);
-
- break;
-
- case SILC_NOTIFY_TYPE_LEAVE:
- client_entry = va_arg(va, SilcClientEntry);
- channel = va_arg(va, SilcChannelEntry);
-
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- channel->channel_name, sg->account);
- if (!convo)
- break;
-
- /* Remove user from channel */
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
- client_entry->nickname, NULL);
-
- break;
-
- case SILC_NOTIFY_TYPE_SIGNOFF:
- client_entry = va_arg(va, SilcClientEntry);
- tmp = va_arg(va, char *);
-
- if (!client_entry->nickname)
- break;
-
- /* Remove from all channels */
- silc_hash_table_list(client_entry->channels, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- chu->channel->channel_name, sg->account);
- if (!convo)
- continue;
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
- client_entry->nickname,
- tmp);
- }
- silc_hash_table_list_reset(&htl);
-
- break;
-
- case SILC_NOTIFY_TYPE_TOPIC_SET:
- {
- char *esc, *tmp2;
- idtype = va_arg(va, int);
- entry = va_arg(va, void *);
- tmp = va_arg(va, char *);
- channel = va_arg(va, SilcChannelEntry);
-
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- channel->channel_name, sg->account);
- if (!convo)
- break;
-
- if (!tmp)
- break;
-
- esc = g_markup_escape_text(tmp, -1);
- tmp2 = purple_markup_linkify(esc);
- g_free(esc);
-
- if (idtype == SILC_ID_CLIENT) {
- client_entry = (SilcClientEntry)entry;
- g_snprintf(buf, sizeof(buf),
- _("%s has changed the topic of <I>%s</I> to: %s"),
- client_entry->nickname, channel->channel_name, tmp2);
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname,
- buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo),
- client_entry->nickname, tmp);
- } else if (idtype == SILC_ID_SERVER) {
- server_entry = (SilcServerEntry)entry;
- g_snprintf(buf, sizeof(buf),
- _("%s has changed the topic of <I>%s</I> to: %s"),
- server_entry->server_name, channel->channel_name, tmp2);
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), server_entry->server_name,
- buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo),
- server_entry->server_name, tmp);
- } else if (idtype == SILC_ID_CHANNEL) {
- channel = (SilcChannelEntry)entry;
- g_snprintf(buf, sizeof(buf),
- _("%s has changed the topic of <I>%s</I> to: %s"),
- channel->channel_name, channel->channel_name, tmp2);
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
- buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo),
- channel->channel_name, tmp);
- } else {
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, tmp);
- }
-
- g_free(tmp2);
-
- break;
-
- }
- case SILC_NOTIFY_TYPE_NICK_CHANGE:
- client_entry = va_arg(va, SilcClientEntry);
- client_entry2 = va_arg(va, SilcClientEntry);
-
- if (!strcmp(client_entry->nickname, client_entry2->nickname))
- break;
-
- /* Change nick on all channels */
- silc_hash_table_list(client_entry2->channels, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- chu->channel->channel_name, sg->account);
- if (!convo)
- continue;
- if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(convo), client_entry->nickname))
- purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo),
- client_entry->nickname,
- client_entry2->nickname);
- }
- silc_hash_table_list_reset(&htl);
-
- break;
-
- case SILC_NOTIFY_TYPE_CMODE_CHANGE:
- idtype = va_arg(va, int);
- entry = va_arg(va, void *);
- mode = va_arg(va, SilcUInt32);
- (void)va_arg(va, char *);
- (void)va_arg(va, char *);
- (void)va_arg(va, char *);
- (void)va_arg(va, SilcPublicKey);
- (void)va_arg(va, SilcBuffer);
- channel = va_arg(va, SilcChannelEntry);
-
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- channel->channel_name, sg->account);
- if (!convo)
- break;
-
- if (idtype == SILC_ID_CLIENT)
- name = ((SilcClientEntry)entry)->nickname;
- else if (idtype == SILC_ID_SERVER)
- name = ((SilcServerEntry)entry)->server_name;
- else
- name = ((SilcChannelEntry)entry)->channel_name;
- if (!name)
- break;
-
- if (mode) {
- silcpurple_get_chmode_string(mode, buf2, sizeof(buf2));
- g_snprintf(buf, sizeof(buf),
- _("<I>%s</I> set channel <I>%s</I> modes to: %s"), name,
- channel->channel_name, buf2);
- } else {
- g_snprintf(buf, sizeof(buf),
- _("<I>%s</I> removed all channel <I>%s</I> modes"), name,
- channel->channel_name);
- }
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
- buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
- break;
-
- case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
- {
- PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_NONE;
- idtype = va_arg(va, int);
- entry = va_arg(va, void *);
- mode = va_arg(va, SilcUInt32);
- client_entry2 = va_arg(va, SilcClientEntry);
- channel = va_arg(va, SilcChannelEntry);
-
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- channel->channel_name, sg->account);
- if (!convo)
- break;
-
- if (idtype == SILC_ID_CLIENT)
- name = ((SilcClientEntry)entry)->nickname;
- else if (idtype == SILC_ID_SERVER)
- name = ((SilcServerEntry)entry)->server_name;
- else
- name = ((SilcChannelEntry)entry)->channel_name;
- if (!name)
- break;
-
- if (mode) {
- silcpurple_get_chumode_string(mode, buf2, sizeof(buf2));
- g_snprintf(buf, sizeof(buf),
- _("<I>%s</I> set <I>%s's</I> modes to: %s"), name,
- client_entry2->nickname, buf2);
- if (mode & SILC_CHANNEL_UMODE_CHANFO)
- flags |= PURPLE_CBFLAGS_FOUNDER;
- if (mode & SILC_CHANNEL_UMODE_CHANOP)
- flags |= PURPLE_CBFLAGS_OP;
- } else {
- g_snprintf(buf, sizeof(buf),
- _("<I>%s</I> removed all <I>%s's</I> modes"), name,
- client_entry2->nickname);
- }
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
- buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
- purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(convo), client_entry2->nickname, flags);
- break;
- }
-
- case SILC_NOTIFY_TYPE_MOTD:
- tmp = va_arg(va, char *);
- silc_free(sg->motd);
- sg->motd = silc_memdup(tmp, strlen(tmp));
- break;
-
- case SILC_NOTIFY_TYPE_KICKED:
- client_entry = va_arg(va, SilcClientEntry);
- tmp = va_arg(va, char *);
- client_entry2 = va_arg(va, SilcClientEntry);
- channel = va_arg(va, SilcChannelEntry);
-
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- channel->channel_name, sg->account);
- if (!convo)
- break;
-
- if (client_entry == conn->local_entry) {
- /* Remove us from channel */
- g_snprintf(buf, sizeof(buf),
- _("You have been kicked off <I>%s</I> by <I>%s</I> (%s)"),
- channel->channel_name, client_entry2->nickname,
- tmp ? tmp : "");
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname,
- buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
- serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)));
- } else {
- /* Remove user from channel */
- g_snprintf(buf, sizeof(buf), _("Kicked by %s (%s)"),
- client_entry2->nickname, tmp ? tmp : "");
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
- client_entry->nickname,
- buf);
- }
-
- break;
-
- case SILC_NOTIFY_TYPE_KILLED:
- client_entry = va_arg(va, SilcClientEntry);
- tmp = va_arg(va, char *);
- idtype = va_arg(va, int);
- entry = va_arg(va, SilcClientEntry);
-
- if (!client_entry->nickname)
- break;
-
- if (client_entry == conn->local_entry) {
- if (idtype == SILC_ID_CLIENT) {
- client_entry2 = (SilcClientEntry)entry;
- g_snprintf(buf, sizeof(buf),
- _("You have been killed by %s (%s)"),
- client_entry2->nickname, tmp ? tmp : "");
- } else if (idtype == SILC_ID_SERVER) {
- server_entry = (SilcServerEntry)entry;
- g_snprintf(buf, sizeof(buf),
- _("You have been killed by %s (%s)"),
- server_entry->server_name, tmp ? tmp : "");
- } else if (idtype == SILC_ID_CHANNEL) {
- channel = (SilcChannelEntry)entry;
- g_snprintf(buf, sizeof(buf),
- _("You have been killed by %s (%s)"),
- channel->channel_name, tmp ? tmp : "");
- }
-
- /* Remove us from all channels */
- silc_hash_table_list(client_entry->channels, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- chu->channel->channel_name, sg->account);
- if (!convo)
- continue;
- purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname,
- buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
- serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)));
- }
- silc_hash_table_list_reset(&htl);
-
- } else {
- if (idtype == SILC_ID_CLIENT) {
- client_entry2 = (SilcClientEntry)entry;
- g_snprintf(buf, sizeof(buf),
- _("Killed by %s (%s)"),
- client_entry2->nickname, tmp ? tmp : "");
- } else if (idtype == SILC_ID_SERVER) {
- server_entry = (SilcServerEntry)entry;
- g_snprintf(buf, sizeof(buf),
- _("Killed by %s (%s)"),
- server_entry->server_name, tmp ? tmp : "");
- } else if (idtype == SILC_ID_CHANNEL) {
- channel = (SilcChannelEntry)entry;
- g_snprintf(buf, sizeof(buf),
- _("Killed by %s (%s)"),
- channel->channel_name, tmp ? tmp : "");
- }
-
- /* Remove user from all channels */
- silc_hash_table_list(client_entry->channels, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- chu->channel->channel_name, sg->account);
- if (!convo)
- continue;
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
- client_entry->nickname, tmp);
- }
- silc_hash_table_list_reset(&htl);
- }
-
- break;
-
- case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
- break;
-
- case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
- {
- int i;
- SilcClientEntry *clients;
- SilcUInt32 clients_count;
-
- (void)va_arg(va, void *);
- clients = va_arg(va, SilcClientEntry *);
- clients_count = va_arg(va, SilcUInt32);
-
- for (i = 0; i < clients_count; i++) {
- if (!clients[i]->nickname)
- break;
-
- /* Remove from all channels */
- silc_hash_table_list(clients[i]->channels, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- convo =
- purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- chu->channel->channel_name, sg->account);
- if (!convo)
- continue;
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
- clients[i]->nickname,
- _("Server signoff"));
- }
- silc_hash_table_list_reset(&htl);
- }
- }
- break;
-
- case SILC_NOTIFY_TYPE_ERROR:
- {
- SilcStatus error = va_arg(va, int);
- purple_notify_error(gc, "Error Notify",
- silc_get_status_message(error),
- NULL);
- }
- break;
-
- case SILC_NOTIFY_TYPE_WATCH:
- {
- SilcPublicKey public_key;
- unsigned char *pk;
- SilcUInt32 pk_len;
- char *fingerprint;
-
- client_entry = va_arg(va, SilcClientEntry);
- (void)va_arg(va, char *);
- mode = va_arg(va, SilcUInt32);
- notify = va_arg(va, int);
- public_key = va_arg(va, SilcPublicKey);
-
- b = NULL;
- if (public_key) {
- PurpleBlistNode *gnode, *cnode, *bnode;
- const char *f;
-
- pk = silc_pkcs_public_key_encode(public_key, &pk_len);
- if (!pk)
- break;
- fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
- for (i = 0; i < strlen(fingerprint); i++)
- if (fingerprint[i] == ' ')
- fingerprint[i] = '_';
- g_snprintf(buf, sizeof(buf) - 1,
- "%s" G_DIR_SEPARATOR_S "clientkeys"
- G_DIR_SEPARATOR_S "clientkey_%s.pub",
- silcpurple_silcdir(), fingerprint);
- silc_free(fingerprint);
- silc_free(pk);
-
- /* Find buddy by associated public key */
- for (gnode = purple_get_blist()->root; gnode;
- gnode = gnode->next) {
- if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
- continue;
- for (cnode = gnode->child; cnode; cnode = cnode->next) {
- if( !PURPLE_BLIST_NODE_IS_CONTACT(cnode))
- continue;
- for (bnode = cnode->child; bnode;
- bnode = bnode->next) {
- if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
- continue;
- b = (PurpleBuddy *)bnode;
- if (b->account != gc->account)
- continue;
- f = purple_blist_node_get_string(bnode, "public-key");
- if (f && !strcmp(f, buf))
- goto cont;
- b = NULL;
- }
- }
- }
- }
- cont:
- if (!b) {
- /* Find buddy by nickname */
- b = purple_find_buddy(sg->account, client_entry->nickname);
- if (!b) {
- purple_debug_warning("silc", "WATCH for %s, unknown buddy\n",
- client_entry->nickname);
- break;
- }
- }
-
- silc_free(b->proto_data);
- b->proto_data = silc_memdup(client_entry->id,
- sizeof(*client_entry->id));
- if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) {
- break;
- } else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) {
- /* See if client was away and is now present */
- if (!(mode & (SILC_UMODE_GONE | SILC_UMODE_INDISPOSED |
- SILC_UMODE_BUSY | SILC_UMODE_PAGE |
- SILC_UMODE_DETACHED)) &&
- (client_entry->mode & SILC_UMODE_GONE ||
- client_entry->mode & SILC_UMODE_INDISPOSED ||
- client_entry->mode & SILC_UMODE_BUSY ||
- client_entry->mode & SILC_UMODE_PAGE ||
- client_entry->mode & SILC_UMODE_DETACHED)) {
- client_entry->mode = mode;
- purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL);
- }
- else if ((mode & SILC_UMODE_GONE) ||
- (mode & SILC_UMODE_INDISPOSED) ||
- (mode & SILC_UMODE_BUSY) ||
- (mode & SILC_UMODE_PAGE) ||
- (mode & SILC_UMODE_DETACHED)) {
- client_entry->mode = mode;
- purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_OFFLINE, NULL);
- }
- } else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
- notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF ||
- notify == SILC_NOTIFY_TYPE_KILLED) {
- client_entry->mode = mode;
- purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_OFFLINE, NULL);
- } else if (notify == SILC_NOTIFY_TYPE_NONE) {
- client_entry->mode = mode;
- purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL);
- }
- }
- break;
-
- default:
- purple_debug_info("silc", "Unhandled notification: %d\n", type);
- break;
- }
-
- va_end(va);
-}
-
-
-/* Command handler. This function is called always in the command function.
- If error occurs it will be called as well. `conn' is the associated
- client connection. `cmd_context' is the command context that was
- originally sent to the command. `success' is FALSE if error occurred
- during command. `command' is the command being processed. It must be
- noted that this is not reply from server. This is merely called just
- after application has called the command. Just to tell application
- that the command really was processed. */
-
-static void
-silc_command(SilcClient client, SilcClientConnection conn,
- SilcClientCommandContext cmd_context, bool success,
- SilcCommand command, SilcStatus status)
-{
- PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
-
- switch (command) {
-
- case SILC_COMMAND_CMODE:
- if (cmd_context->argc == 3 &&
- !strcmp((char *)cmd_context->argv[2], "+C"))
- sg->chpk = TRUE;
- else
- sg->chpk = FALSE;
- break;
-
- default:
- break;
- }
-}
-
-#if 0
-static void
-silcpurple_whois_more(SilcClientEntry client_entry, gint id)
-{
- SilcAttributePayload attr;
- SilcAttribute attribute;
- char *buf;
- GString *s;
- SilcVCardStruct vcard;
- int i;
-
- if (id != 0)
- return;
-
- memset(&vcard, 0, sizeof(vcard));
-
- s = g_string_new("");
-
- silc_dlist_start(client_entry->attrs);
- while ((attr = silc_dlist_get(client_entry->attrs)) != SILC_LIST_END) {
- attribute = silc_attribute_get_attribute(attr);
- switch (attribute) {
-
- case SILC_ATTRIBUTE_USER_INFO:
- if (!silc_attribute_get_object(attr, (void *)&vcard,
- sizeof(vcard)))
- continue;
- g_string_append_printf(s, "%s:\n\n", _("Personal Information"));
- if (vcard.full_name)
- g_string_append_printf(s, "%s:\t\t%s\n",
- _("Full Name"),
- vcard.full_name);
- if (vcard.first_name)
- g_string_append_printf(s, "%s:\t%s\n",
- _("First Name"),
- vcard.first_name);
- if (vcard.middle_names)
- g_string_append_printf(s, "%s:\t%s\n",
- _("Middle Name"),
- vcard.middle_names);
- if (vcard.family_name)
- g_string_append_printf(s, "%s:\t%s\n",
- _("Family Name"),
- vcard.family_name);
- if (vcard.nickname)
- g_string_append_printf(s, "%s:\t\t%s\n",
- _("Nickname"),
- vcard.nickname);
- if (vcard.bday)
- g_string_append_printf(s, "%s:\t\t%s\n",
- _("Birth Day"),
- vcard.bday);
- if (vcard.title)
- g_string_append_printf(s, "%s:\t\t%s\n",
- _("Job Title"),
- vcard.title);
- if (vcard.role)
- g_string_append_printf(s, "%s:\t\t%s\n",
- _("Job Role"),
- vcard.role);
- if (vcard.org_name)
- g_string_append_printf(s, "%s:\t%s\n",
- _("Organization"),
- vcard.org_name);
- if (vcard.org_unit)
- g_string_append_printf(s, "%s:\t\t%s\n",
- _("Unit"),
- vcard.org_unit);
- if (vcard.url)
- g_string_append_printf(s, "%s:\t%s\n",
- _("Homepage"),
- vcard.url);
- if (vcard.label)
- g_string_append_printf(s, "%s:\t%s\n",
- _("Address"),
- vcard.label);
- for (i = 0; i < vcard.num_tels; i++) {
- if (vcard.tels[i].telnum)
- g_string_append_printf(s, "%s:\t\t\t%s\n",
- _("Phone"),
- vcard.tels[i].telnum);
- }
- for (i = 0; i < vcard.num_emails; i++) {
- if (vcard.emails[i].address)
- g_string_append_printf(s, "%s:\t\t%s\n",
- _("Email"),
- vcard.emails[i].address);
- }
- if (vcard.note)
- g_string_append_printf(s, "\n%s:\t\t%s\n",
- _("Note"),
- vcard.note);
- break;
- }
- }
-
- buf = g_string_free(s, FALSE);
- purple_notify_info(NULL, _("User Information"), _("User Information"),
- buf);
- g_free(buf);
-}
-#endif
-
-/* Command reply handler. This function is called always in the command reply
- function. If error occurs it will be called as well. Normal scenario
- is that it will be called after the received command data has been parsed
- and processed. The function is used to pass the received command data to
- the application.
-
- `conn' is the associated client connection. `cmd_payload' is the command
- payload data received from server and it can be ignored. It is provided
- if the application would like to re-parse the received command data,
- however, it must be noted that the data is parsed already by the library
- thus the payload can be ignored. `success' is FALSE if error occurred.
- In this case arguments are not sent to the application. The `status' is
- the command reply status server returned. The `command' is the command
- reply being processed. The function has variable argument list and each
- command defines the number and type of arguments it passes to the
- application (on error they are not sent). */
-
-static void
-silc_command_reply(SilcClient client, SilcClientConnection conn,
- SilcCommandPayload cmd_payload, bool success,
- SilcCommand command, SilcStatus status, ...)
-{
- PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
- PurpleConversation *convo;
- va_list vp;
-
- va_start(vp, status);
-
- switch (command) {
- case SILC_COMMAND_JOIN:
- {
- SilcChannelEntry channel_entry;
-
- if (!success) {
- purple_notify_error(gc, _("Join Chat"), _("Cannot join channel"),
- silc_get_status_message(status));
- return;
- }
-
- (void)va_arg(vp, char *);
- channel_entry = va_arg(vp, SilcChannelEntry);
-
- /* Resolve users on channel */
- silc_client_get_clients_by_channel(client, conn, channel_entry,
- silcpurple_chat_join_done,
- channel_entry);
- }
- break;
-
- case SILC_COMMAND_LEAVE:
- break;
-
- case SILC_COMMAND_USERS:
- break;
-
- case SILC_COMMAND_WHOIS:
- {
- SilcUInt32 idle, mode;
- SilcBuffer channels, user_modes;
- SilcClientEntry client_entry;
- char tmp[1024], *tmp2;
- char *moodstr, *statusstr, *contactstr, *langstr, *devicestr, *tzstr, *geostr;
- PurpleNotifyUserInfo *user_info;
-
- if (!success) {
- purple_notify_error(gc, _("User Information"),
- _("Cannot get user information"),
- silc_get_status_message(status));
- break;
- }
-
- client_entry = va_arg(vp, SilcClientEntry);
- if (!client_entry->nickname)
- break;
- (void)va_arg(vp, char *);
- (void)va_arg(vp, char *);
- (void)va_arg(vp, char *);
- channels = va_arg(vp, SilcBuffer);
- mode = va_arg(vp, SilcUInt32);
- idle = va_arg(vp, SilcUInt32);
- (void)va_arg(vp, unsigned char *);
- user_modes = va_arg(vp, SilcBuffer);
-
- user_info = purple_notify_user_info_new();
- tmp2 = g_markup_escape_text(client_entry->nickname, -1);
- purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp2);
- g_free(tmp2);
- if (client_entry->realname) {
- tmp2 = g_markup_escape_text(client_entry->realname, -1);
- purple_notify_user_info_add_pair(user_info, _("Real Name"), tmp2);
- g_free(tmp2);
- }
- if (client_entry->username) {
- tmp2 = g_markup_escape_text(client_entry->username, -1);
- if (client_entry->hostname) {
- gchar *tmp3;
- tmp3 = g_strdup_printf("%s@%s", tmp2, client_entry->hostname);
- purple_notify_user_info_add_pair(user_info, _("Username"), tmp3);
- g_free(tmp3);
- } else
- purple_notify_user_info_add_pair(user_info, _("Username"), tmp2);
- g_free(tmp2);
- }
-
- if (client_entry->mode) {
- memset(tmp, 0, sizeof(tmp));
- silcpurple_get_umode_string(client_entry->mode,
- tmp, sizeof(tmp) - strlen(tmp));
- purple_notify_user_info_add_pair(user_info, _("User Modes"), tmp);
- }
-
- silcpurple_parse_attrs(client_entry->attrs, &moodstr, &statusstr, &contactstr, &langstr, &devicestr, &tzstr, &geostr);
- if (moodstr) {
- purple_notify_user_info_add_pair(user_info, _("Mood"), moodstr);
- g_free(moodstr);
- }
-
- if (statusstr) {
- tmp2 = g_markup_escape_text(statusstr, -1);
- purple_notify_user_info_add_pair(user_info, _("Status Text"), tmp2);
- g_free(statusstr);
- g_free(tmp2);
- }
-
- if (contactstr) {
- purple_notify_user_info_add_pair(user_info, _("Preferred Contact"), contactstr);
- g_free(contactstr);
- }
-
- if (langstr) {
- purple_notify_user_info_add_pair(user_info, _("Preferred Language"), langstr);
- g_free(langstr);
- }
-
- if (devicestr) {
- purple_notify_user_info_add_pair(user_info, _("Device"), devicestr);
- g_free(devicestr);
- }
-
- if (tzstr) {
- purple_notify_user_info_add_pair(user_info, _("Timezone"), tzstr);
- g_free(tzstr);
- }
-
- if (geostr) {
- purple_notify_user_info_add_pair(user_info, _("Geolocation"), geostr);
- g_free(geostr);
- }
-
- if (client_entry->server)
- purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server);
-
- if (channels && user_modes) {
- SilcUInt32 *umodes;
- SilcDList list =
- silc_channel_payload_parse_list(channels->data,
- channels->len);
- if (list && silc_get_mode_list(user_modes,
- silc_dlist_count(list),
- &umodes)) {
- SilcChannelPayload entry;
- int i = 0;
-
- memset(tmp, 0, sizeof(tmp));
- silc_dlist_start(list);
- while ((entry = silc_dlist_get(list))
- != SILC_LIST_END) {
- SilcUInt32 name_len;
- char *m = silc_client_chumode_char(umodes[i++]);
- char *name = (char *)silc_channel_get_name(entry, &name_len);
- if (m)
- silc_strncat(tmp, sizeof(tmp) - 1, m, strlen(m));
- silc_strncat(tmp, sizeof(tmp) - 1, name, name_len);
- silc_strncat(tmp, sizeof(tmp) - 1, " ", 1);
- silc_free(m);
-
- }
- tmp2 = g_markup_escape_text(tmp, -1);
- purple_notify_user_info_add_pair(user_info, _("Currently on"), tmp2);
- g_free(tmp2);
- silc_free(umodes);
- }
- }
-
- if (client_entry->public_key) {
- char *fingerprint, *babbleprint;
- unsigned char *pk;
- SilcUInt32 pk_len;
- pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
- fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
- babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
- purple_notify_user_info_add_pair(user_info, _("Public Key Fingerprint"), fingerprint);
- purple_notify_user_info_add_pair(user_info, _("Public Key Babbleprint"), babbleprint);
- silc_free(fingerprint);
- silc_free(babbleprint);
- silc_free(pk);
- }
-
-#if 0 /* XXX for now, let's not show attrs here */
- if (client_entry->attrs)
- purple_request_action(gc, _("User Information"),
- _("User Information"),
- buf, 1, client_entry, 2,
- _("OK"), G_CALLBACK(silcpurple_whois_more),
- _("_More..."), G_CALLBACK(silcpurple_whois_more), gc->account, NULL, NULL);
- else
-#endif
- purple_notify_userinfo(gc, client_entry->nickname, user_info, NULL, NULL);
- purple_notify_user_info_destroy(user_info);
- }
- break;
-
- case SILC_COMMAND_WHOWAS:
- {
- SilcClientEntry client_entry;
- char *nickname, *realname, *username, *tmp;
- PurpleNotifyUserInfo *user_info;
-
- if (!success) {
- purple_notify_error(gc, _("User Information"),
- _("Cannot get user information"),
- silc_get_status_message(status));
- break;
- }
-
- client_entry = va_arg(vp, SilcClientEntry);
- nickname = va_arg(vp, char *);
- username = va_arg(vp, char *);
- realname = va_arg(vp, char *);
- if (!nickname)
- break;
-
- user_info = purple_notify_user_info_new();
- tmp = g_markup_escape_text(nickname, -1);
- purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp);
- g_free(tmp);
- if (realname) {
- tmp = g_markup_escape_text(realname, -1);
- purple_notify_user_info_add_pair(user_info, _("Real Name"), tmp);
- g_free(tmp);
- }
- if (username) {
- tmp = g_markup_escape_text(username, -1);
- if (client_entry && client_entry->hostname) {
- gchar *tmp3;
- tmp3 = g_strdup_printf("%s@%s", tmp, client_entry->hostname);
- purple_notify_user_info_add_pair(user_info, _("Username"), tmp3);
- g_free(tmp3);
- } else
- purple_notify_user_info_add_pair(user_info, _("Username"), tmp);
- g_free(tmp);
- }
- if (client_entry && client_entry->server)
- purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server);
-
-
- if (client_entry && client_entry->public_key) {
- char *fingerprint, *babbleprint;
- unsigned char *pk;
- SilcUInt32 pk_len;
- pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
- fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
- babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
- purple_notify_user_info_add_pair(user_info, _("Public Key Fingerprint"), fingerprint);
- purple_notify_user_info_add_pair(user_info, _("Public Key Babbleprint"), babbleprint);
- silc_free(fingerprint);
- silc_free(babbleprint);
- silc_free(pk);
- }
-
- purple_notify_userinfo(gc, nickname, user_info, NULL, NULL);
- purple_notify_user_info_destroy(user_info);
- }
- break;
-
- case SILC_COMMAND_DETACH:
- if (!success) {
- purple_notify_error(gc, _("Detach From Server"), _("Cannot detach"),
- silc_get_status_message(status));
- return;
- }
- break;
-
- case SILC_COMMAND_TOPIC:
- {
- SilcChannelEntry channel;
-
- if (!success) {
- purple_notify_error(gc, _("Topic"), _("Cannot set topic"),
- silc_get_status_message(status));
- return;
- }
-
- channel = va_arg(vp, SilcChannelEntry);
-
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- channel->channel_name, sg->account);
- if (!convo) {
- purple_debug_error("silc", "Got a topic for %s, which doesn't exist\n",
- channel->channel_name);
- break;
- }
-
- /* Set topic */
- if (channel->topic)
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, channel->topic);
- }
- break;
-
- case SILC_COMMAND_NICK:
- {
- /* I don't think we should need to do this because the server should
- * be sending a SILC_NOTIFY_TYPE_NICK_CHANGE when we change our own
- * nick, but it isn't, so we deal with it here instead. Stu. */
- SilcClientEntry local_entry;
- SilcHashTableList htl;
- SilcChannelUser chu;
- const char *oldnick;
-
- if (!success) {
- purple_notify_error(gc, _("Nick"), _("Failed to change nickname"),
- silc_get_status_message(status));
- return;
- }
-
- local_entry = va_arg(vp, SilcClientEntry);
-
- /* Change nick on all channels */
- silc_hash_table_list(local_entry->channels, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- chu->channel->channel_name, sg->account);
- if (!convo)
- continue;
- oldnick = purple_conv_chat_get_nick(PURPLE_CONV_CHAT(convo));
- if (strcmp(oldnick, purple_normalize(purple_conversation_get_account(convo), local_entry->nickname))) {
- purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo),
- oldnick, local_entry->nickname);
- purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), local_entry->nickname);
- }
- }
- silc_hash_table_list_reset(&htl);
-
- purple_connection_set_display_name(gc, local_entry->nickname);
- }
- break;
-
- case SILC_COMMAND_LIST:
- {
- char *topic, *name;
- int usercount;
- PurpleRoomlistRoom *room;
-
- if (sg->roomlist_cancelled)
- break;
-
- if (!success) {
- purple_notify_error(gc, _("Error"), _("Error retrieving room list"),
- silc_get_status_message(status));
- purple_roomlist_set_in_progress(sg->roomlist, FALSE);
- purple_roomlist_unref(sg->roomlist);
- sg->roomlist = NULL;
- return;
- }
-
- (void)va_arg(vp, SilcChannelEntry);
- name = va_arg(vp, char *);
- if (!name) {
- purple_notify_error(gc, _("Roomlist"), _("Cannot get room list"),
- silc_get_status_message(status));
- purple_roomlist_set_in_progress(sg->roomlist, FALSE);
- purple_roomlist_unref(sg->roomlist);
- sg->roomlist = NULL;
- return;
- }
- topic = va_arg(vp, char *);
- usercount = va_arg(vp, int);
-
- room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, name, NULL);
- purple_roomlist_room_add_field(sg->roomlist, room, name);
- purple_roomlist_room_add_field(sg->roomlist, room,
- SILC_32_TO_PTR(usercount));
- purple_roomlist_room_add_field(sg->roomlist, room,
- topic ? topic : "");
- purple_roomlist_room_add(sg->roomlist, room);
-
- if (status == SILC_STATUS_LIST_END ||
- status == SILC_STATUS_OK) {
- purple_roomlist_set_in_progress(sg->roomlist, FALSE);
- purple_roomlist_unref(sg->roomlist);
- sg->roomlist = NULL;
- }
- }
- break;
-
- case SILC_COMMAND_GETKEY:
- {
- SilcPublicKey public_key;
-
- if (!success) {
- purple_notify_error(gc, _("Get Public Key"),
- _("Cannot fetch the public key"),
- silc_get_status_message(status));
- return;
- }
-
- (void)va_arg(vp, SilcUInt32);
- (void)va_arg(vp, void *);
- public_key = va_arg(vp, SilcPublicKey);
-
- if (!public_key)
- purple_notify_error(gc, _("Get Public Key"),
- _("Cannot fetch the public key"),
- _("No public key was received"));
- }
- break;
-
- case SILC_COMMAND_INFO:
- {
-
- char *server_name;
- char *server_info;
- char tmp[256];
-
- if (!success) {
- purple_notify_error(gc, _("Server Information"),
- _("Cannot get server information"),
- silc_get_status_message(status));
- return;
- }
-
- (void)va_arg(vp, SilcServerEntry);
- server_name = va_arg(vp, char *);
- server_info = va_arg(vp, char *);
-
- if (server_name && server_info) {
- g_snprintf(tmp, sizeof(tmp), "Server: %s\n%s",
- server_name, server_info);
- purple_notify_info(gc, NULL, _("Server Information"), tmp);
- }
- }
- break;
-
- case SILC_COMMAND_STATS:
- {
- SilcUInt32 starttime, uptime, my_clients, my_channels, my_server_ops,
- my_router_ops, cell_clients, cell_channels, cell_servers,
- clients, channels, servers, routers, server_ops, router_ops;
- SilcUInt32 buffer_length;
- SilcBufferStruct buf;
-
- unsigned char *server_stats;
- char *msg;
-
- if (!success) {
- purple_notify_error(gc, _("Server Statistics"),
- _("Cannot get server statistics"),
- silc_get_status_message(status));
- return;
- }
-
- server_stats = va_arg(vp, unsigned char *);
- buffer_length = va_arg(vp, SilcUInt32);
- if (!server_stats || !buffer_length) {
- purple_notify_error(gc, _("Server Statistics"),
- _("No server statistics available"), NULL);
- break;
- }
- silc_buffer_set(&buf, server_stats, buffer_length);
- silc_buffer_unformat(&buf,
- SILC_STR_UI_INT(&starttime),
- SILC_STR_UI_INT(&uptime),
- SILC_STR_UI_INT(&my_clients),
- SILC_STR_UI_INT(&my_channels),
- SILC_STR_UI_INT(&my_server_ops),
- SILC_STR_UI_INT(&my_router_ops),
- SILC_STR_UI_INT(&cell_clients),
- SILC_STR_UI_INT(&cell_channels),
- SILC_STR_UI_INT(&cell_servers),
- SILC_STR_UI_INT(&clients),
- SILC_STR_UI_INT(&channels),
- SILC_STR_UI_INT(&servers),
- SILC_STR_UI_INT(&routers),
- SILC_STR_UI_INT(&server_ops),
- SILC_STR_UI_INT(&router_ops),
- SILC_STR_END);
-
- msg = g_strdup_printf(_("Local server start time: %s\n"
- "Local server uptime: %s\n"
- "Local server clients: %d\n"
- "Local server channels: %d\n"
- "Local server operators: %d\n"
- "Local router operators: %d\n"
- "Local cell clients: %d\n"
- "Local cell channels: %d\n"
- "Local cell servers: %d\n"
- "Total clients: %d\n"
- "Total channels: %d\n"
- "Total servers: %d\n"
- "Total routers: %d\n"
- "Total server operators: %d\n"
- "Total router operators: %d\n"),
- silc_get_time(starttime),
- purple_str_seconds_to_string((int)uptime),
- (int)my_clients, (int)my_channels, (int)my_server_ops, (int)my_router_ops,
- (int)cell_clients, (int)cell_channels, (int)cell_servers,
- (int)clients, (int)channels, (int)servers, (int)routers,
- (int)server_ops, (int)router_ops);
-
- purple_notify_info(gc, NULL,
- _("Network Statistics"), msg);
- g_free(msg);
- }
- break;
-
- case SILC_COMMAND_PING:
- {
- if (!success) {
- purple_notify_error(gc, _("Ping"), _("Ping failed"),
- silc_get_status_message(status));
- return;
- }
-
- purple_notify_info(gc, _("Ping"), _("Ping reply received from server"),
- NULL);
- }
- break;
-
- case SILC_COMMAND_KILL:
- if (!success) {
- purple_notify_error(gc, _("Kill User"),
- _("Could not kill user"),
- silc_get_status_message(status));
- return;
- }
- break;
-
- case SILC_COMMAND_CMODE:
- {
- SilcChannelEntry channel_entry;
- SilcBuffer channel_pubkeys;
-
- if (!success)
- return;
-
- channel_entry = va_arg(vp, SilcChannelEntry);
- (void)va_arg(vp, SilcUInt32);
- (void)va_arg(vp, SilcPublicKey);
- channel_pubkeys = va_arg(vp, SilcBuffer);
-
- if (sg->chpk)
- silcpurple_chat_chauth_show(sg, channel_entry, channel_pubkeys);
- }
- break;
-
- default:
- if (success)
- purple_debug_info("silc", "Unhandled command: %d (succeeded)\n", command);
- else
- purple_debug_info("silc", "Unhandled command: %d (failed: %s)\n", command,
- silc_get_status_message(status));
- break;
- }
-
- va_end(vp);
-}
-
-
-/* Called to indicate that connection was either successfully established
- or connecting failed. This is also the first time application receives
- the SilcClientConnection object which it should save somewhere.
- If the `success' is FALSE the application must always call the function
- silc_client_close_connection. */
-
-static void
-silc_connected(SilcClient client, SilcClientConnection conn,
- SilcClientConnectionStatus status)
-{
- PurpleConnection *gc = client->application;
- SilcPurple sg;
-
- if (gc == NULL) {
- silc_client_close_connection(client, conn);
- return;
- }
- sg = gc->proto_data;
-
- switch (status) {
- case SILC_CLIENT_CONN_SUCCESS:
- case SILC_CLIENT_CONN_SUCCESS_RESUME:
- purple_connection_set_state(gc, PURPLE_CONNECTED);
-
- /* Send the server our buddy list */
- silcpurple_send_buddylist(gc);
-
- g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
-
- /* Send any UMODEs configured for account */
- if (purple_account_get_bool(sg->account, "block-ims", FALSE)) {
- silc_client_command_call(sg->client, sg->conn, NULL,
- "UMODE", "+P", NULL);
- }
-
- return;
- break;
- case SILC_CLIENT_CONN_ERROR:
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Error during connecting to SILC Server"));
- g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
- break;
-
- case SILC_CLIENT_CONN_ERROR_KE:
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
- _("Key Exchange failed"));
- break;
-
- case SILC_CLIENT_CONN_ERROR_AUTH:
- purple_connection_error_reason(gc,
- PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
- _("Authentication failed"));
- break;
-
- case SILC_CLIENT_CONN_ERROR_RESUME:
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
- _("Resuming detached session failed. "
- "Press Reconnect to create new connection."));
- g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
- break;
-
- case SILC_CLIENT_CONN_ERROR_TIMEOUT:
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Connection timed out"));
- break;
- }
-
- /* Error */
- sg->conn = NULL;
- silc_client_close_connection(client, conn);
-}
-
-
-/* Called to indicate that connection was disconnected to the server.
- The `status' may tell the reason of the disconnection, and if the
- `message' is non-NULL it may include the disconnection message
- received from server. */
-
-static void
-silc_disconnected(SilcClient client, SilcClientConnection conn,
- SilcStatus status, const char *message)
-{
- PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
-
- if (sg->resuming && !sg->detaching)
- g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
-
- sg->conn = NULL;
-
- /* Close the connection */
- if (!sg->detaching)
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Disconnected by server"));
- else
- /* TODO: Does this work correctly? Maybe we need to set wants_to_die? */
- purple_account_disconnect(purple_connection_get_account(gc));
-}
-
-
-typedef struct {
- SilcGetAuthMeth completion;
- void *context;
-} *SilcPurpleGetAuthMethod;
-
-/* Callback called when we've received the authentication method information
- from the server after we've requested it. */
-
-static void silc_get_auth_method_callback(SilcClient client,
- SilcClientConnection conn,
- SilcAuthMethod auth_meth,
- void *context)
-{
- SilcPurpleGetAuthMethod internal = context;
-
- switch (auth_meth) {
- case SILC_AUTH_NONE:
- /* No authentication required. */
- (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
- break;
-
- case SILC_AUTH_PASSWORD:
- /* By returning NULL here the library will ask the passphrase from us
- by calling the silc_ask_passphrase. */
- (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
- break;
-
- case SILC_AUTH_PUBLIC_KEY:
- /* Do not get the authentication data now, the library will generate
- it using our default key, if we do not provide it here. */
- (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
- break;
- }
-
- silc_free(internal);
-}
-
-/* Find authentication method and authentication data by hostname and
- port. The hostname may be IP address as well. When the authentication
- method has been resolved the `completion' callback with the found
- authentication method and authentication data is called. The `conn'
- may be NULL. */
-
-static void
-silc_get_auth_method(SilcClient client, SilcClientConnection conn,
- char *hostname, SilcUInt16 port,
- SilcGetAuthMeth completion, void *context)
-{
- PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
- SilcPurpleGetAuthMethod internal;
- const char *password;
-
- /* Progress */
- if (sg->resuming)
- purple_connection_update_progress(gc, _("Resuming session"), 4, 5);
- else
- purple_connection_update_progress(gc, _("Authenticating connection"), 4, 5);
-
- /* Check configuration if we have this connection configured. If we
- have then return that data immediately, as it's faster way. */
- if (purple_account_get_bool(sg->account, "pubkey-auth", FALSE)) {
- completion(TRUE, SILC_AUTH_PUBLIC_KEY, NULL, 0, context);
- return;
- }
- password = purple_connection_get_password(gc);
- if (password && *password) {
- completion(TRUE, SILC_AUTH_PASSWORD, (unsigned char *)password, strlen(password), context);
- return;
- }
-
- /* Resolve the authentication method from server, as we may not know it. */
- internal = silc_calloc(1, sizeof(*internal));
- if (!internal)
- return;
- internal->completion = completion;
- internal->context = context;
- silc_client_request_authentication_method(client, conn,
- silc_get_auth_method_callback,
- internal);
-}
-
-
-/* Verifies received public key. The `conn_type' indicates which entity
- (server, client etc.) has sent the public key. If user decides to trust
- the application may save the key as trusted public key for later
- use. The `completion' must be called after the public key has been
- verified. */
-
-static void
-silc_verify_public_key(SilcClient client, SilcClientConnection conn,
- SilcSocketType conn_type, unsigned char *pk,
- SilcUInt32 pk_len, SilcSKEPKType pk_type,
- SilcVerifyPublicKey completion, void *context)
-{
- PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
-
- if (!sg->conn && (conn_type == SILC_SOCKET_TYPE_SERVER ||
- conn_type == SILC_SOCKET_TYPE_ROUTER)) {
- /* Progress */
- if (sg->resuming)
- purple_connection_update_progress(gc, _("Resuming session"), 3, 5);
- else
- purple_connection_update_progress(gc, _("Verifying server public key"),
- 3, 5);
- }
-
- /* Verify public key */
- silcpurple_verify_public_key(client, conn, NULL, conn_type, pk,
- pk_len, pk_type, completion, context);
-}
-
-typedef struct {
- SilcAskPassphrase completion;
- void *context;
-} *SilcPurpleAskPassphrase;
-
-static void
-silc_ask_passphrase_cb(SilcPurpleAskPassphrase internal, const char *passphrase)
-{
- if (!passphrase || !(*passphrase))
- internal->completion(NULL, 0, internal->context);
- else
- internal->completion((unsigned char *)passphrase,
- strlen(passphrase), internal->context);
- silc_free(internal);
-}
-
-/* Ask (interact, that is) a passphrase from user. The passphrase is
- returned to the library by calling the `completion' callback with
- the `context'. The returned passphrase SHOULD be in UTF-8 encoded,
- if not then the library will attempt to encode. */
-
-static void
-silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
- SilcAskPassphrase completion, void *context)
-{
- PurpleConnection *gc = client->application;
- SilcPurpleAskPassphrase internal = silc_calloc(1, sizeof(*internal));
-
- if (!internal)
- return;
- internal->completion = completion;
- internal->context = context;
- purple_request_input(gc, _("Passphrase"), NULL,
- _("Passphrase required"), NULL, FALSE, TRUE, NULL,
- _("OK"), G_CALLBACK(silc_ask_passphrase_cb),
- _("Cancel"), G_CALLBACK(silc_ask_passphrase_cb),
- purple_connection_get_account(gc), NULL, NULL, internal);
-}
-
-
-/* Notifies application that failure packet was received. This is called
- if there is some protocol active in the client. The `protocol' is the
- protocol context. The `failure' is opaque pointer to the failure
- indication. Note, that the `failure' is protocol dependant and
- application must explicitly cast it to correct type. Usually `failure'
- is 32 bit failure type (see protocol specs for all protocol failure
- types). */
-
-static void
-silc_failure(SilcClient client, SilcClientConnection conn,
- SilcProtocol protocol, void *failure)
-{
- PurpleConnection *gc = client->application;
- char buf[128];
-
- memset(buf, 0, sizeof(buf));
-
- if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
- SilcSKEStatus status = (SilcSKEStatus)SILC_PTR_TO_32(failure);
-
- if (status == SILC_SKE_STATUS_BAD_VERSION)
- g_snprintf(buf, sizeof(buf),
- _("Failure: Version mismatch, upgrade your client"));
- if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
- g_snprintf(buf, sizeof(buf),
- _("Failure: Remote does not trust/support your public key"));
- if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
- g_snprintf(buf, sizeof(buf),
- _("Failure: Remote does not support proposed KE group"));
- if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
- g_snprintf(buf, sizeof(buf),
- _("Failure: Remote does not support proposed cipher"));
- if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
- g_snprintf(buf, sizeof(buf),
- _("Failure: Remote does not support proposed PKCS"));
- if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
- g_snprintf(buf, sizeof(buf),
- _("Failure: Remote does not support proposed hash function"));
- if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
- g_snprintf(buf, sizeof(buf),
- _("Failure: Remote does not support proposed HMAC"));
- if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
- g_snprintf(buf, sizeof(buf), _("Failure: Incorrect signature"));
- if (status == SILC_SKE_STATUS_INVALID_COOKIE)
- g_snprintf(buf, sizeof(buf), _("Failure: Invalid cookie"));
-
- /* Show the error on the progress bar. A more generic error message
- is going to be showed to user after this in the silc_connected. */
- purple_connection_update_progress(gc, buf, 2, 5);
- }
-
- if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
- SilcUInt32 err = SILC_PTR_TO_32(failure);
-
- if (err == SILC_AUTH_FAILED)
- g_snprintf(buf, sizeof(buf), _("Failure: Authentication failed"));
-
- /* Show the error on the progress bar. A more generic error message
- is going to be showed to user after this in the silc_connected. */
- purple_connection_update_progress(gc, buf, 4, 5);
- }
-}
-
-/* Asks whether the user would like to perform the key agreement protocol.
- This is called after we have received an key agreement packet or an
- reply to our key agreement packet. This returns TRUE if the user wants
- the library to perform the key agreement protocol and FALSE if it is not
- desired (application may start it later by calling the function
- silc_client_perform_key_agreement). If TRUE is returned also the
- `completion' and `context' arguments must be set by the application. */
-
-static bool
-silc_key_agreement(SilcClient client, SilcClientConnection conn,
- SilcClientEntry client_entry, const char *hostname,
- SilcUInt16 port, SilcKeyAgreementCallback *completion,
- void **context)
-{
- silcpurple_buddy_keyagr_request(client, conn, client_entry, hostname, port);
- *completion = NULL;
- *context = NULL;
- return FALSE;
-}
-
-
-/* Notifies application that file transfer protocol session is being
- requested by the remote client indicated by the `client_entry' from
- the `hostname' and `port'. The `session_id' is the file transfer
- session and it can be used to either accept or reject the file
- transfer request, by calling the silc_client_file_receive or
- silc_client_file_close, respectively. */
-
-static void
-silc_ftp(SilcClient client, SilcClientConnection conn,
- SilcClientEntry client_entry, SilcUInt32 session_id,
- const char *hostname, SilcUInt16 port)
-{
- silcpurple_ftp_request(client, conn, client_entry, session_id,
- hostname, port);
-}
-
-
-/* Delivers SILC session detachment data indicated by `detach_data' to the
- application. If application has issued SILC_COMMAND_DETACH command
- the client session in the SILC network is not quit. The client remains
- in the network but is detached. The detachment data may be used later
- to resume the session in the SILC Network. The appliation is
- responsible of saving the `detach_data', to for example in a file.
-
- The detachment data can be given as argument to the functions
- silc_client_connect_to_server, or silc_client_add_connection when
- creating connection to remote server, inside SilcClientConnectionParams
- structure. If it is provided the client library will attempt to resume
- the session in the network. After the connection is created
- successfully, the application is responsible of setting the user
- interface for user into the same state it was before detaching (showing
- same channels, channel modes, etc). It can do this by fetching the
- information (like joined channels) from the client library. */
-
-static void
-silc_detach(SilcClient client, SilcClientConnection conn,
- const unsigned char *detach_data, SilcUInt32 detach_data_len)
-{
- PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
- const char *file;
-
- /* Save the detachment data to file. */
- file = silcpurple_session_file(purple_account_get_username(sg->account));
- g_unlink(file);
- silc_file_writefile(file, (char *)detach_data, detach_data_len);
-}
-
-SilcClientOperations ops = {
- silc_say,
- silc_channel_message,
- silc_private_message,
- silc_notify,
- silc_command,
- silc_command_reply,
- silc_connected,
- silc_disconnected,
- silc_get_auth_method,
- silc_verify_public_key,
- silc_ask_passphrase,
- silc_failure,
- silc_key_agreement,
- silc_ftp,
- silc_detach
-};
diff --git a/libpurple/protocols/silc10/pk.c b/libpurple/protocols/silc10/pk.c
deleted file mode 100644
index 0bd399d6d5..0000000000
--- a/libpurple/protocols/silc10/pk.c
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
-
- silcpurple_pk.c
-
- Author: Pekka Riikonen <priikone@silcnet.org>
-
- Copyright (C) 2004 Pekka Riikonen
-
- 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; version 2 of the License.
-
- 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.
-
-*/
-
-#include "silcincludes.h"
-#include "silcclient.h"
-#include "silcpurple.h"
-
-/************************* Public Key Verification ***************************/
-
-typedef struct {
- SilcClient client;
- SilcClientConnection conn;
- char *filename;
- char *entity;
- char *entity_name;
- char *fingerprint;
- char *babbleprint;
- unsigned char *pk;
- SilcUInt32 pk_len;
- SilcSKEPKType pk_type;
- SilcVerifyPublicKey completion;
- void *context;
- gboolean changed;
-} *PublicKeyVerify;
-
-static void silcpurple_verify_ask(const char *entity,
- const char *fingerprint,
- const char *babbleprint,
- PublicKeyVerify verify);
-
-static void silcpurple_verify_cb(PublicKeyVerify verify, gint id)
-{
- if (id != 2) {
- if (verify->completion)
- verify->completion(FALSE, verify->context);
- } else {
- if (verify->completion)
- verify->completion(TRUE, verify->context);
-
- /* Save the key for future checking */
- silc_pkcs_save_public_key_data(verify->filename, verify->pk,
- verify->pk_len, SILC_PKCS_FILE_PEM);
- }
-
- silc_free(verify->filename);
- silc_free(verify->entity);
- silc_free(verify->entity_name);
- silc_free(verify->fingerprint);
- silc_free(verify->babbleprint);
- silc_free(verify->pk);
- silc_free(verify);
-}
-
-static void silcpurple_verify_details_cb(PublicKeyVerify verify)
-{
- /* What a hack. We have to display the accept dialog _again_
- because Purple closes the dialog after you press the button. Purple
- should have option for the dialogs whether the buttons close them
- or not. */
- silcpurple_verify_ask(verify->entity, verify->fingerprint,
- verify->babbleprint, verify);
-}
-
-static void silcpurple_verify_details(PublicKeyVerify verify, gint id)
-{
- SilcPublicKey public_key;
- PurpleConnection *gc = verify->client->application;
- SilcPurple sg = gc->proto_data;
-
- silc_pkcs_public_key_decode(verify->pk, verify->pk_len,
- &public_key);
- silcpurple_show_public_key(sg, verify->entity_name, public_key,
- G_CALLBACK(silcpurple_verify_details_cb),
- verify);
- silc_pkcs_public_key_free(public_key);
-}
-
-static void silcpurple_verify_ask(const char *entity,
- const char *fingerprint,
- const char *babbleprint,
- PublicKeyVerify verify)
-{
- PurpleConnection *gc = verify->client->application;
- char tmp[256], tmp2[256];
-
- if (verify->changed) {
- g_snprintf(tmp, sizeof(tmp),
- _("Received %s's public key. Your local copy does not match this "
- "key. Would you still like to accept this public key?"),
- entity);
- } else {
- g_snprintf(tmp, sizeof(tmp),
- _("Received %s's public key. Would you like to accept this "
- "public key?"), entity);
- }
- g_snprintf(tmp2, sizeof(tmp2),
- _("Fingerprint and babbleprint for the %s key are:\n\n"
- "%s\n%s\n"), entity, fingerprint, babbleprint);
-
- purple_request_action(gc, _("Verify Public Key"), tmp, tmp2,
- PURPLE_DEFAULT_ACTION_NONE,
- purple_connection_get_account(gc), entity, NULL, verify, 3,
- _("Yes"), G_CALLBACK(silcpurple_verify_cb),
- _("No"), G_CALLBACK(silcpurple_verify_cb),
- _("_View..."), G_CALLBACK(silcpurple_verify_details));
-}
-
-void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn,
- const char *name, SilcSocketType conn_type,
- unsigned char *pk, SilcUInt32 pk_len,
- SilcSKEPKType pk_type,
- SilcVerifyPublicKey completion, void *context)
-{
- PurpleConnection *gc = client->application;
- int i;
- char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
- char *fingerprint, *babbleprint;
- struct passwd *pw;
- struct stat st;
- char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
- conn_type == SILC_SOCKET_TYPE_ROUTER) ?
- "server" : "client");
- PublicKeyVerify verify;
-
- if (pk_type != SILC_SKE_PK_TYPE_SILC) {
- purple_notify_error(gc, _("Verify Public Key"),
- _("Unsupported public key type"), NULL);
- if (completion)
- completion(FALSE, context);
- return;
- }
-
- pw = getpwuid(getuid());
- if (!pw) {
- if (completion)
- completion(FALSE, context);
- return;
- }
-
- memset(filename, 0, sizeof(filename));
- memset(filename2, 0, sizeof(filename2));
- memset(file, 0, sizeof(file));
-
- if (conn_type == SILC_SOCKET_TYPE_SERVER ||
- conn_type == SILC_SOCKET_TYPE_ROUTER) {
- if (!name) {
- g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
- conn->sock->ip, conn->sock->port);
- g_snprintf(filename, sizeof(filename) - 1,
- "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s",
- silcpurple_silcdir(), entity, file);
-
- g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
- conn->sock->hostname, conn->sock->port);
- g_snprintf(filename2, sizeof(filename2) - 1,
- "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s",
- silcpurple_silcdir(), entity, file);
-
- ipf = filename;
- hostf = filename2;
- } else {
- g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
- name, conn->sock->port);
- g_snprintf(filename, sizeof(filename) - 1,
- "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s",
- silcpurple_silcdir(), entity, file);
-
- ipf = filename;
- }
- } else {
- /* Replace all whitespaces with `_'. */
- fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
- for (i = 0; i < strlen(fingerprint); i++)
- if (fingerprint[i] == ' ')
- fingerprint[i] = '_';
-
- g_snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
- g_snprintf(filename, sizeof(filename) - 1,
- "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s",
- silcpurple_silcdir(), entity, file);
- silc_free(fingerprint);
-
- ipf = filename;
- }
-
- verify = silc_calloc(1, sizeof(*verify));
- if (!verify)
- return;
- verify->client = client;
- verify->conn = conn;
- verify->filename = strdup(ipf);
- verify->entity = strdup(entity);
- verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
- (name ? strdup(name) : strdup(conn->sock->hostname))
- : NULL);
- verify->pk = silc_memdup(pk, pk_len);
- verify->pk_len = pk_len;
- verify->pk_type = pk_type;
- verify->completion = completion;
- verify->context = context;
- fingerprint = verify->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
- babbleprint = verify->babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
-
- /* Check whether this key already exists */
- if (g_stat(ipf, &st) < 0 && (!hostf || g_stat(hostf, &st) < 0)) {
- /* Key does not exist, ask user to verify the key and save it */
- silcpurple_verify_ask(name ? name : entity,
- fingerprint, babbleprint, verify);
- return;
- } else {
- /* The key already exists, verify it. */
- SilcPublicKey public_key;
- unsigned char *encpk;
- SilcUInt32 encpk_len;
-
- /* Load the key file, try for both IP filename and hostname filename */
- if (!silc_pkcs_load_public_key(ipf, &public_key,
- SILC_PKCS_FILE_PEM) &&
- !silc_pkcs_load_public_key(ipf, &public_key,
- SILC_PKCS_FILE_BIN) &&
- (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key,
- SILC_PKCS_FILE_PEM) &&
- !silc_pkcs_load_public_key(hostf, &public_key,
- SILC_PKCS_FILE_BIN)))) {
- silcpurple_verify_ask(name ? name : entity,
- fingerprint, babbleprint, verify);
- return;
- }
-
- /* Encode the key data */
- encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
- if (!encpk) {
- silcpurple_verify_ask(name ? name : entity,
- fingerprint, babbleprint, verify);
- return;
- }
-
- /* Compare the keys */
- if (memcmp(encpk, pk, encpk_len)) {
- /* Ask user to verify the key and save it */
- verify->changed = TRUE;
- silcpurple_verify_ask(name ? name : entity,
- fingerprint, babbleprint, verify);
- return;
- }
-
- /* Local copy matched */
- if (completion)
- completion(TRUE, context);
- silc_free(verify->filename);
- silc_free(verify->entity);
- silc_free(verify->entity_name);
- silc_free(verify->pk);
- silc_free(verify->fingerprint);
- silc_free(verify->babbleprint);
- silc_free(verify);
- }
-}
diff --git a/libpurple/protocols/silc10/silc.c b/libpurple/protocols/silc10/silc.c
deleted file mode 100644
index e29c22ea45..0000000000
--- a/libpurple/protocols/silc10/silc.c
+++ /dev/null
@@ -1,1965 +0,0 @@
-/*
-
- silcpurple.c
-
- Author: Pekka Riikonen <priikone@silcnet.org>
-
- Copyright (C) 2004 - 2005 Pekka Riikonen
-
- 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; version 2 of the License.
-
- 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.
-
-*/
-
-#include "silcincludes.h"
-#include "silcclient.h"
-#include "silcpurple.h"
-#include "version.h"
-#include "wb.h"
-#include "core.h"
-
-extern SilcClientOperations ops;
-static PurplePlugin *silc_plugin = NULL;
-
-static const char *
-silcpurple_list_icon(PurpleAccount *a, PurpleBuddy *b)
-{
- return (const char *)"silc";
-}
-
-static GList *
-silcpurple_away_states(PurpleAccount *account)
-{
- PurpleStatusType *type;
- GList *types = NULL;
-
- type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_AVAILABLE, NULL, TRUE, TRUE, FALSE);
- types = g_list_append(types, type);
- type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_HYPER, _("Hyper Active"), TRUE, TRUE, FALSE);
- types = g_list_append(types, type);
- type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_AWAY, NULL, TRUE, TRUE, FALSE);
- types = g_list_append(types, type);
- type = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, SILCPURPLE_STATUS_ID_BUSY, _("Busy"), TRUE, TRUE, FALSE);
- types = g_list_append(types, type);
- type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_INDISPOSED, _("Indisposed"), TRUE, TRUE, FALSE);
- types = g_list_append(types, type);
- type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_PAGE, _("Wake Me Up"), TRUE, TRUE, FALSE);
- types = g_list_append(types, type);
- type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, SILCPURPLE_STATUS_ID_OFFLINE, NULL, TRUE, TRUE, FALSE);
- types = g_list_append(types, type);
-
- return types;
-}
-
-static void
-silcpurple_set_status(PurpleAccount *account, PurpleStatus *status)
-{
- PurpleConnection *gc = purple_account_get_connection(account);
- SilcPurple sg = NULL;
- SilcUInt32 mode;
- SilcBuffer idp;
- unsigned char mb[4];
- const char *state;
-
- if (gc != NULL)
- sg = gc->proto_data;
-
- if (status == NULL)
- return;
-
- state = purple_status_get_id(status);
-
- if (state == NULL)
- return;
-
- if ((sg == NULL) || (sg->conn == NULL))
- return;
-
- mode = sg->conn->local_entry->mode;
- mode &= ~(SILC_UMODE_GONE |
- SILC_UMODE_HYPER |
- SILC_UMODE_BUSY |
- SILC_UMODE_INDISPOSED |
- SILC_UMODE_PAGE);
-
- if (!strcmp(state, "hyper"))
- mode |= SILC_UMODE_HYPER;
- else if (!strcmp(state, "away"))
- mode |= SILC_UMODE_GONE;
- else if (!strcmp(state, "busy"))
- mode |= SILC_UMODE_BUSY;
- else if (!strcmp(state, "indisposed"))
- mode |= SILC_UMODE_INDISPOSED;
- else if (!strcmp(state, "page"))
- mode |= SILC_UMODE_PAGE;
-
- /* Send UMODE */
- idp = silc_id_payload_encode(sg->conn->local_id, SILC_ID_CLIENT);
- SILC_PUT32_MSB(mode, mb);
- silc_client_command_send(sg->client, sg->conn, SILC_COMMAND_UMODE,
- ++sg->conn->cmd_ident, 2,
- 1, idp->data, idp->len,
- 2, mb, sizeof(mb));
- silc_buffer_free(idp);
-}
-
-
-/*************************** Connection Routines *****************************/
-
-static void
-silcpurple_keepalive(PurpleConnection *gc)
-{
- SilcPurple sg = gc->proto_data;
- silc_client_send_packet(sg->client, sg->conn, SILC_PACKET_HEARTBEAT,
- NULL, 0);
-}
-
-static gboolean
-silcpurple_scheduler(gpointer *context)
-{
- SilcPurple sg = (SilcPurple)context;
- silc_client_run_one(sg->client);
- return TRUE;
-}
-
-static void
-silcpurple_nickname_parse(const char *nickname,
- char **ret_nickname)
-{
- silc_parse_userfqdn(nickname, ret_nickname, NULL);
-}
-
-static void
-silcpurple_login_connected(gpointer data, gint source, const gchar *error_message)
-{
- PurpleConnection *gc = data;
- SilcPurple sg;
- SilcClient client;
- SilcClientConnection conn;
- PurpleAccount *account;
- SilcClientConnectionParams params;
- SilcUInt32 mask;
- const char *dfile, *tmp;
-#ifdef SILC_ATTRIBUTE_USER_ICON
- PurpleStoredImage *img;
-#endif
-#ifdef HAVE_SYS_UTSNAME_H
- struct utsname u;
-#endif
-
-
- g_return_if_fail(gc != NULL);
-
- sg = gc->proto_data;
-
- if (source < 0) {
- purple_connection_error_reason(gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Connection failed"));
- return;
- }
-
- client = sg->client;
- account = sg->account;
-
- /* Get session detachment data, if available */
- memset(&params, 0, sizeof(params));
- dfile = silcpurple_session_file(purple_account_get_username(sg->account));
- params.detach_data = (unsigned char *)silc_file_readfile(dfile, &params.detach_data_len);
- if (params.detach_data)
- params.detach_data[params.detach_data_len] = 0;
-
- /* Add connection to SILC client library */
- conn = silc_client_add_connection(
- sg->client, &params,
- (char *)purple_account_get_string(account, "server",
- "silc.silcnet.org"),
- purple_account_get_int(account, "port", 706), sg);
- if (!conn) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Unable to initialize SILC Client connection"));
- gc->proto_data = NULL;
- return;
- }
- sg->conn = conn;
-
- /* Progress */
- if (params.detach_data) {
- purple_connection_update_progress(gc, _("Resuming session"), 2, 5);
- sg->resuming = TRUE;
- } else {
- purple_connection_update_progress(gc, _("Performing key exchange"), 2, 5);
- }
-
- /* Perform SILC Key Exchange. The "silc_connected" will be called
- eventually. */
- silc_client_start_key_exchange(sg->client, sg->conn, source);
-
- /* Set default attributes */
- mask = SILC_ATTRIBUTE_MOOD_NORMAL;
- silc_client_attribute_add(client, conn,
- SILC_ATTRIBUTE_STATUS_MOOD,
- SILC_32_TO_PTR(mask),
- sizeof(SilcUInt32));
- mask = SILC_ATTRIBUTE_CONTACT_CHAT;
- silc_client_attribute_add(client, conn,
- SILC_ATTRIBUTE_PREFERRED_CONTACT,
- SILC_32_TO_PTR(mask),
- sizeof(SilcUInt32));
-#ifdef HAVE_SYS_UTSNAME_H
- if (!uname(&u)) {
- SilcAttributeObjDevice dev;
- memset(&dev, 0, sizeof(dev));
- dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER;
- dev.version = u.release;
- dev.model = u.sysname;
- silc_client_attribute_add(client, conn,
- SILC_ATTRIBUTE_DEVICE_INFO,
- (void *)&dev, sizeof(dev));
- }
-#endif
-#ifdef _WIN32
- tmp = _tzname[0];
-#else
- tmp = tzname[0];
-#endif
- silc_client_attribute_add(client, conn,
- SILC_ATTRIBUTE_TIMEZONE,
- (void *)tmp, strlen(tmp));
-
-#ifdef SILC_ATTRIBUTE_USER_ICON
- /* Set our buddy icon */
- img = purple_buddy_icons_find_account_icon(account);
- silcpurple_buddy_set_icon(gc, img);
- purple_imgstore_unref(img);
-#endif
-
- silc_free(params.detach_data);
-}
-
-static void
-silcpurple_login(PurpleAccount *account)
-{
- SilcPurple sg;
- SilcClient client;
- SilcClientParams params;
- PurpleConnection *gc;
- char pkd[256], prd[256];
- const char *cipher, *hmac;
- char *realname;
- int i;
-
- gc = account->gc;
- if (!gc)
- return;
- gc->proto_data = NULL;
-
- memset(&params, 0, sizeof(params));
- strcat(params.nickname_format, "%n@%h%a");
- params.nickname_parse = silcpurple_nickname_parse;
- params.ignore_requested_attributes = FALSE;
-
- /* Allocate SILC client */
- client = silc_client_alloc(&ops, &params, gc, NULL);
- if (!client) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
- _("Out of memory"));
- return;
- }
-
- /* Get username, real name and local hostname for SILC library */
- if (purple_account_get_username(account)) {
- const char *u = purple_account_get_username(account);
- char **up = g_strsplit(u, "@", 2);
- client->username = strdup(up[0]);
- g_strfreev(up);
- } else {
- client->username = silc_get_username();
- purple_account_set_username(account, client->username);
- }
- realname = silc_get_real_name();
- if (purple_account_get_user_info(account)) {
- client->realname = strdup(purple_account_get_user_info(account));
- free(realname);
- } else if ((silc_get_real_name() != NULL) && (*realname != '\0')) {
- client->realname = realname;
- purple_account_set_user_info(account, client->realname);
- } else {
- free(realname);
- client->realname = strdup(_("John Noname"));
- }
- client->hostname = silc_net_localhost();
-
- purple_connection_set_display_name(gc, client->username);
-
- /* Register requested cipher and HMAC */
- cipher = purple_account_get_string(account, "cipher", SILC_DEFAULT_CIPHER);
- for (i = 0; silc_default_ciphers[i].name; i++)
- if (!strcmp(silc_default_ciphers[i].name, cipher)) {
- silc_cipher_register(&(silc_default_ciphers[i]));
- break;
- }
- hmac = purple_account_get_string(account, "hmac", SILC_DEFAULT_HMAC);
- for (i = 0; silc_default_hmacs[i].name; i++)
- if (!strcmp(silc_default_hmacs[i].name, hmac)) {
- silc_hmac_register(&(silc_default_hmacs[i]));
- break;
- }
-
- /* Init SILC client */
- if (!silc_client_init(client)) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
- _("Unable to initialize SILC protocol"));
- return;
- }
-
- /* Check the ~/.silc dir and create it, and new key pair if necessary. */
- if (!silcpurple_check_silc_dir(gc)) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
- _("Error loading SILC key pair"));
- return;
- }
-
- /* Progress */
- purple_connection_update_progress(gc, _("Connecting to SILC Server"), 1, 5);
-
- /* Load SILC key pair */
- g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
- g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
- if (!silc_load_key_pair((char *)purple_account_get_string(account, "public-key", pkd),
- (char *)purple_account_get_string(account, "private-key", prd),
- (gc->password == NULL) ? "" : gc->password, &client->pkcs,
- &client->public_key, &client->private_key)) {
- g_snprintf(pkd, sizeof(pkd), _("Unable to load SILC key pair: %s"), g_strerror(errno));
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
- pkd);
- return;
- }
-
- sg = silc_calloc(1, sizeof(*sg));
- if (!sg)
- return;
- memset(sg, 0, sizeof(*sg));
- sg->client = client;
- sg->gc = gc;
- sg->account = account;
- gc->proto_data = sg;
-
- /* Connect to the SILC server */
- if (purple_proxy_connect(gc, account,
- purple_account_get_string(account, "server",
- "silc.silcnet.org"),
- purple_account_get_int(account, "port", 706),
- silcpurple_login_connected, gc) == NULL)
- {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Unable to create connection"));
- return;
- }
-
- /* Schedule SILC using Glib's event loop */
- sg->scheduler = purple_timeout_add(300, (GSourceFunc)silcpurple_scheduler, sg);
-}
-
-static int
-silcpurple_close_final(gpointer *context)
-{
- SilcPurple sg = (SilcPurple)context;
- silc_client_stop(sg->client);
- silc_client_free(sg->client);
-#ifdef HAVE_SILCMIME_H
- if (sg->mimeass)
- silc_mime_assembler_free(sg->mimeass);
-#endif
- silc_free(sg);
- return 0;
-}
-
-static void
-silcpurple_close(PurpleConnection *gc)
-{
- SilcPurple sg = gc->proto_data;
- GHashTable *ui_info;
- const char *ui_name = NULL, *ui_website = NULL;
- char *quit_msg;
-
- g_return_if_fail(sg != NULL);
-
- ui_info = purple_core_get_ui_info();
-
- if(ui_info) {
- ui_name = g_hash_table_lookup(ui_info, "name");
- ui_website = g_hash_table_lookup(ui_info, "website");
- }
-
- if(!ui_name || !ui_website) {
- ui_name = "Pidgin";
- ui_website = PURPLE_WEBSITE;
- }
- quit_msg = g_strdup_printf(_("Download %s: %s"),
- ui_name, ui_website);
-
- /* Send QUIT */
- silc_client_command_call(sg->client, sg->conn, NULL,
- "QUIT", quit_msg, NULL);
- g_free(quit_msg);
-
- if (sg->conn)
- silc_client_close_connection(sg->client, sg->conn);
-
- purple_timeout_remove(sg->scheduler);
- purple_timeout_add(1, (GSourceFunc)silcpurple_close_final, sg);
-}
-
-
-/****************************** Protocol Actions *****************************/
-
-static void
-silcpurple_attrs_cancel(PurpleConnection *gc, PurpleRequestFields *fields)
-{
- /* Nothing */
-}
-
-static void
-silcpurple_attrs_cb(PurpleConnection *gc, PurpleRequestFields *fields)
-{
- SilcPurple sg = gc->proto_data;
- SilcClient client = sg->client;
- SilcClientConnection conn = sg->conn;
- PurpleRequestField *f;
- char *tmp;
- SilcUInt32 tmp_len, mask;
- SilcAttributeObjService service;
- SilcAttributeObjDevice dev;
- SilcVCardStruct vcard;
- const char *val;
-
- sg = gc->proto_data;
- if (!sg)
- return;
-
- memset(&service, 0, sizeof(service));
- memset(&dev, 0, sizeof(dev));
- memset(&vcard, 0, sizeof(vcard));
-
- silc_client_attribute_del(client, conn,
- SILC_ATTRIBUTE_USER_INFO, NULL);
- silc_client_attribute_del(client, conn,
- SILC_ATTRIBUTE_SERVICE, NULL);
- silc_client_attribute_del(client, conn,
- SILC_ATTRIBUTE_STATUS_MOOD, NULL);
- silc_client_attribute_del(client, conn,
- SILC_ATTRIBUTE_STATUS_FREETEXT, NULL);
- silc_client_attribute_del(client, conn,
- SILC_ATTRIBUTE_STATUS_MESSAGE, NULL);
- silc_client_attribute_del(client, conn,
- SILC_ATTRIBUTE_PREFERRED_LANGUAGE, NULL);
- silc_client_attribute_del(client, conn,
- SILC_ATTRIBUTE_PREFERRED_CONTACT, NULL);
- silc_client_attribute_del(client, conn,
- SILC_ATTRIBUTE_TIMEZONE, NULL);
- silc_client_attribute_del(client, conn,
- SILC_ATTRIBUTE_GEOLOCATION, NULL);
- silc_client_attribute_del(client, conn,
- SILC_ATTRIBUTE_DEVICE_INFO, NULL);
-
- /* Set mood */
- mask = 0;
- f = purple_request_fields_get_field(fields, "mood_normal");
- if (f && purple_request_field_bool_get_value(f))
- mask |= SILC_ATTRIBUTE_MOOD_NORMAL;
- f = purple_request_fields_get_field(fields, "mood_happy");
- if (f && purple_request_field_bool_get_value(f))
- mask |= SILC_ATTRIBUTE_MOOD_HAPPY;
- f = purple_request_fields_get_field(fields, "mood_sad");
- if (f && purple_request_field_bool_get_value(f))
- mask |= SILC_ATTRIBUTE_MOOD_SAD;
- f = purple_request_fields_get_field(fields, "mood_angry");
- if (f && purple_request_field_bool_get_value(f))
- mask |= SILC_ATTRIBUTE_MOOD_ANGRY;
- f = purple_request_fields_get_field(fields, "mood_jealous");
- if (f && purple_request_field_bool_get_value(f))
- mask |= SILC_ATTRIBUTE_MOOD_JEALOUS;
- f = purple_request_fields_get_field(fields, "mood_ashamed");
- if (f && purple_request_field_bool_get_value(f))
- mask |= SILC_ATTRIBUTE_MOOD_ASHAMED;
- f = purple_request_fields_get_field(fields, "mood_invincible");
- if (f && purple_request_field_bool_get_value(f))
- mask |= SILC_ATTRIBUTE_MOOD_INVINCIBLE;
- f = purple_request_fields_get_field(fields, "mood_inlove");
- if (f && purple_request_field_bool_get_value(f))
- mask |= SILC_ATTRIBUTE_MOOD_INLOVE;
- f = purple_request_fields_get_field(fields, "mood_sleepy");
- if (f && purple_request_field_bool_get_value(f))
- mask |= SILC_ATTRIBUTE_MOOD_SLEEPY;
- f = purple_request_fields_get_field(fields, "mood_bored");
- if (f && purple_request_field_bool_get_value(f))
- mask |= SILC_ATTRIBUTE_MOOD_BORED;
- f = purple_request_fields_get_field(fields, "mood_excited");
- if (f && purple_request_field_bool_get_value(f))
- mask |= SILC_ATTRIBUTE_MOOD_EXCITED;
- f = purple_request_fields_get_field(fields, "mood_anxious");
- if (f && purple_request_field_bool_get_value(f))
- mask |= SILC_ATTRIBUTE_MOOD_ANXIOUS;
- silc_client_attribute_add(client, conn,
- SILC_ATTRIBUTE_STATUS_MOOD,
- SILC_32_TO_PTR(mask),
- sizeof(SilcUInt32));
-
- /* Set preferred contact */
- mask = 0;
- f = purple_request_fields_get_field(fields, "contact_chat");
- if (f && purple_request_field_bool_get_value(f))
- mask |= SILC_ATTRIBUTE_CONTACT_CHAT;
- f = purple_request_fields_get_field(fields, "contact_email");
- if (f && purple_request_field_bool_get_value(f))
- mask |= SILC_ATTRIBUTE_CONTACT_EMAIL;
- f = purple_request_fields_get_field(fields, "contact_call");
- if (f && purple_request_field_bool_get_value(f))
- mask |= SILC_ATTRIBUTE_CONTACT_CALL;
- f = purple_request_fields_get_field(fields, "contact_sms");
- if (f && purple_request_field_bool_get_value(f))
- mask |= SILC_ATTRIBUTE_CONTACT_SMS;
- f = purple_request_fields_get_field(fields, "contact_mms");
- if (f && purple_request_field_bool_get_value(f))
- mask |= SILC_ATTRIBUTE_CONTACT_MMS;
- f = purple_request_fields_get_field(fields, "contact_video");
- if (f && purple_request_field_bool_get_value(f))
- mask |= SILC_ATTRIBUTE_CONTACT_VIDEO;
- if (mask)
- silc_client_attribute_add(client, conn,
- SILC_ATTRIBUTE_PREFERRED_CONTACT,
- SILC_32_TO_PTR(mask),
- sizeof(SilcUInt32));
-
- /* Set status text */
- val = NULL;
- f = purple_request_fields_get_field(fields, "status_text");
- if (f)
- val = purple_request_field_string_get_value(f);
- if (val && *val)
- silc_client_attribute_add(client, conn,
- SILC_ATTRIBUTE_STATUS_FREETEXT,
- (void *)val, strlen(val));
-
- /* Set vcard */
- val = NULL;
- f = purple_request_fields_get_field(fields, "vcard");
- if (f)
- val = purple_request_field_string_get_value(f);
- if (val && *val) {
- purple_account_set_string(sg->account, "vcard", val);
- tmp = silc_file_readfile(val, &tmp_len);
- if (tmp) {
- tmp[tmp_len] = 0;
- if (silc_vcard_decode((unsigned char *)tmp, tmp_len, &vcard))
- silc_client_attribute_add(client, conn,
- SILC_ATTRIBUTE_USER_INFO,
- (void *)&vcard,
- sizeof(vcard));
- }
- silc_vcard_free(&vcard);
- silc_free(tmp);
- } else {
- purple_account_set_string(sg->account, "vcard", "");
- }
-
-#ifdef HAVE_SYS_UTSNAME_H
- /* Set device info */
- f = purple_request_fields_get_field(fields, "device");
- if (f && purple_request_field_bool_get_value(f)) {
- struct utsname u;
- if (!uname(&u)) {
- dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER;
- dev.version = u.release;
- dev.model = u.sysname;
- silc_client_attribute_add(client, conn,
- SILC_ATTRIBUTE_DEVICE_INFO,
- (void *)&dev, sizeof(dev));
- }
- }
-#endif
-
- /* Set timezone */
- val = NULL;
- f = purple_request_fields_get_field(fields, "timezone");
- if (f)
- val = purple_request_field_string_get_value(f);
- if (val && *val)
- silc_client_attribute_add(client, conn,
- SILC_ATTRIBUTE_TIMEZONE,
- (void *)val, strlen(val));
-}
-
-static void
-silcpurple_attrs(PurplePluginAction *action)
-{
- PurpleConnection *gc = (PurpleConnection *) action->context;
- SilcPurple sg = gc->proto_data;
- SilcClient client = sg->client;
- SilcClientConnection conn = sg->conn;
- PurpleRequestFields *fields;
- PurpleRequestFieldGroup *g;
- PurpleRequestField *f;
- SilcHashTable attrs;
- SilcAttributePayload attr;
- gboolean mnormal = TRUE, mhappy = FALSE, msad = FALSE,
- mangry = FALSE, mjealous = FALSE, mashamed = FALSE,
- minvincible = FALSE, minlove = FALSE, msleepy = FALSE,
- mbored = FALSE, mexcited = FALSE, manxious = FALSE;
- gboolean cemail = FALSE, ccall = FALSE, csms = FALSE,
- cmms = FALSE, cchat = TRUE, cvideo = FALSE;
- gboolean device = TRUE;
- char status[1024];
-
- sg = gc->proto_data;
- if (!sg)
- return;
-
- memset(status, 0, sizeof(status));
-
- attrs = silc_client_attributes_get(client, conn);
- if (attrs) {
- if (silc_hash_table_find(attrs,
- SILC_32_TO_PTR(SILC_ATTRIBUTE_STATUS_MOOD),
- NULL, (void *)&attr)) {
- SilcUInt32 mood = 0;
- silc_attribute_get_object(attr, &mood, sizeof(mood));
- mnormal = !mood;
- mhappy = (mood & SILC_ATTRIBUTE_MOOD_HAPPY);
- msad = (mood & SILC_ATTRIBUTE_MOOD_SAD);
- mangry = (mood & SILC_ATTRIBUTE_MOOD_ANGRY);
- mjealous = (mood & SILC_ATTRIBUTE_MOOD_JEALOUS);
- mashamed = (mood & SILC_ATTRIBUTE_MOOD_ASHAMED);
- minvincible = (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE);
- minlove = (mood & SILC_ATTRIBUTE_MOOD_INLOVE);
- msleepy = (mood & SILC_ATTRIBUTE_MOOD_SLEEPY);
- mbored = (mood & SILC_ATTRIBUTE_MOOD_BORED);
- mexcited = (mood & SILC_ATTRIBUTE_MOOD_EXCITED);
- manxious = (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS);
- }
-
- if (silc_hash_table_find(attrs,
- SILC_32_TO_PTR(SILC_ATTRIBUTE_PREFERRED_CONTACT),
- NULL, (void *)&attr)) {
- SilcUInt32 contact = 0;
- silc_attribute_get_object(attr, &contact, sizeof(contact));
- cemail = (contact & SILC_ATTRIBUTE_CONTACT_EMAIL);
- ccall = (contact & SILC_ATTRIBUTE_CONTACT_CALL);
- csms = (contact & SILC_ATTRIBUTE_CONTACT_SMS);
- cmms = (contact & SILC_ATTRIBUTE_CONTACT_MMS);
- cchat = (contact & SILC_ATTRIBUTE_CONTACT_CHAT);
- cvideo = (contact & SILC_ATTRIBUTE_CONTACT_VIDEO);
- }
-
- if (silc_hash_table_find(attrs,
- SILC_32_TO_PTR(SILC_ATTRIBUTE_STATUS_FREETEXT),
- NULL, (void *)&attr))
- silc_attribute_get_object(attr, &status, sizeof(status));
-
- if (!silc_hash_table_find(attrs,
- SILC_32_TO_PTR(SILC_ATTRIBUTE_DEVICE_INFO),
- NULL, (void *)&attr))
- device = FALSE;
- }
-
- fields = purple_request_fields_new();
-
- g = purple_request_field_group_new(NULL);
- f = purple_request_field_label_new("l3", _("Your Current Mood"));
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_bool_new("mood_normal", _("Normal"), mnormal);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_bool_new("mood_happy", _("Happy"), mhappy);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_bool_new("mood_sad", _("Sad"), msad);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_bool_new("mood_angry", _("Angry"), mangry);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_bool_new("mood_jealous", _("Jealous"), mjealous);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_bool_new("mood_ashamed", _("Ashamed"), mashamed);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_bool_new("mood_invincible", _("Invincible"), minvincible);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_bool_new("mood_inlove", _("In love"), minlove);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_bool_new("mood_sleepy", _("Sleepy"), msleepy);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_bool_new("mood_bored", _("Bored"), mbored);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_bool_new("mood_excited", _("Excited"), mexcited);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_bool_new("mood_anxious", _("Anxious"), manxious);
- purple_request_field_group_add_field(g, f);
-
- f = purple_request_field_label_new("l4", _("\nYour Preferred Contact Methods"));
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_bool_new("contact_chat", _("Chat"), cchat);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_bool_new("contact_email", _("Email"), cemail);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_bool_new("contact_call", _("Phone"), ccall);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_bool_new("contact_sms", _("SMS"), csms);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_bool_new("contact_mms", _("MMS"), cmms);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_bool_new("contact_video", _("Video conferencing"), cvideo);
- purple_request_field_group_add_field(g, f);
- purple_request_fields_add_group(fields, g);
-
- g = purple_request_field_group_new(NULL);
- f = purple_request_field_string_new("status_text", _("Your Current Status"),
- status[0] ? status : NULL, TRUE);
- purple_request_field_group_add_field(g, f);
- purple_request_fields_add_group(fields, g);
-
- g = purple_request_field_group_new(NULL);
-#if 0
- f = purple_request_field_label_new("l2", _("Online Services"));
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_bool_new("services",
- _("Let others see what services you are using"),
- TRUE);
- purple_request_field_group_add_field(g, f);
-#endif
-#ifdef HAVE_SYS_UTSNAME_H
- f = purple_request_field_bool_new("device",
- _("Let others see what computer you are using"),
- device);
- purple_request_field_group_add_field(g, f);
-#endif
- purple_request_fields_add_group(fields, g);
-
- g = purple_request_field_group_new(NULL);
- f = purple_request_field_string_new("vcard", _("Your VCard File"),
- purple_account_get_string(sg->account, "vcard", ""),
- FALSE);
- purple_request_field_group_add_field(g, f);
-#ifdef _WIN32
- f = purple_request_field_string_new("timezone", _("Timezone"), _tzname[0], FALSE);
-#else
- f = purple_request_field_string_new("timezone", _("Timezone"), tzname[0], FALSE);
-#endif
- purple_request_field_group_add_field(g, f);
- purple_request_fields_add_group(fields, g);
-
- purple_request_fields(gc, _("User Online Status Attributes"),
- _("User Online Status Attributes"),
- _("You can let other users see your online status information "
- "and your personal information. Please fill the information "
- "you would like other users to see about yourself."),
- fields,
- _("OK"), G_CALLBACK(silcpurple_attrs_cb),
- _("Cancel"), G_CALLBACK(silcpurple_attrs_cancel),
- gc->account, NULL, NULL, gc);
-}
-
-static void
-silcpurple_detach(PurplePluginAction *action)
-{
- PurpleConnection *gc = (PurpleConnection *) action->context;
- SilcPurple sg;
-
- if (!gc)
- return;
- sg = gc->proto_data;
- if (!sg)
- return;
-
- /* Call DETACH */
- silc_client_command_call(sg->client, sg->conn, "DETACH");
- sg->detaching = TRUE;
-}
-
-static void
-silcpurple_view_motd(PurplePluginAction *action)
-{
- PurpleConnection *gc = (PurpleConnection *) action->context;
- SilcPurple sg;
- char *tmp;
-
- if (!gc)
- return;
- sg = gc->proto_data;
- if (!sg)
- return;
-
- if (!sg->motd) {
- purple_notify_error(
- gc, _("Message of the Day"), _("No Message of the Day available"),
- _("There is no Message of the Day associated with this connection"));
- return;
- }
-
- tmp = g_markup_escape_text(sg->motd, -1);
- purple_notify_formatted(gc, NULL, _("Message of the Day"), NULL,
- tmp, NULL, NULL);
- g_free(tmp);
-}
-
-static void
-silcpurple_create_keypair_cancel(PurpleConnection *gc, PurpleRequestFields *fields)
-{
- /* Nothing */
-}
-
-static void
-silcpurple_create_keypair_cb(PurpleConnection *gc, PurpleRequestFields *fields)
-{
- SilcPurple sg = gc->proto_data;
- PurpleRequestField *f;
- const char *val, *pkfile = NULL, *prfile = NULL;
- const char *pass1 = NULL, *pass2 = NULL, *un = NULL, *hn = NULL;
- const char *rn = NULL, *e = NULL, *o = NULL, *c = NULL;
- char *identifier;
- int keylen = SILCPURPLE_DEF_PKCS_LEN;
- SilcPublicKey public_key;
-
- sg = gc->proto_data;
- if (!sg)
- return;
-
- val = NULL;
- f = purple_request_fields_get_field(fields, "pass1");
- if (f)
- val = purple_request_field_string_get_value(f);
- if (val && *val)
- pass1 = val;
- else
- pass1 = "";
- val = NULL;
- f = purple_request_fields_get_field(fields, "pass2");
- if (f)
- val = purple_request_field_string_get_value(f);
- if (val && *val)
- pass2 = val;
- else
- pass2 = "";
-
- if (strcmp(pass1, pass2)) {
- purple_notify_error(
- gc, _("Create New SILC Key Pair"), _("Passphrases do not match"), NULL);
- return;
- }
-
- val = NULL;
- f = purple_request_fields_get_field(fields, "key");
- if (f)
- val = purple_request_field_string_get_value(f);
- if (val && *val)
- keylen = atoi(val);
- f = purple_request_fields_get_field(fields, "pkfile");
- if (f)
- pkfile = purple_request_field_string_get_value(f);
- f = purple_request_fields_get_field(fields, "prfile");
- if (f)
- prfile = purple_request_field_string_get_value(f);
-
- f = purple_request_fields_get_field(fields, "un");
- if (f)
- un = purple_request_field_string_get_value(f);
- f = purple_request_fields_get_field(fields, "hn");
- if (f)
- hn = purple_request_field_string_get_value(f);
- f = purple_request_fields_get_field(fields, "rn");
- if (f)
- rn = purple_request_field_string_get_value(f);
- f = purple_request_fields_get_field(fields, "e");
- if (f)
- e = purple_request_field_string_get_value(f);
- f = purple_request_fields_get_field(fields, "o");
- if (f)
- o = purple_request_field_string_get_value(f);
- f = purple_request_fields_get_field(fields, "c");
- if (f)
- c = purple_request_field_string_get_value(f);
-
- identifier = silc_pkcs_encode_identifier((char *)un, (char *)hn,
- (char *)rn, (char *)e, (char *)o, (char *)c);
-
- /* Create the key pair */
- if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS, keylen, pkfile, prfile,
- identifier, pass1, NULL, &public_key, NULL,
- FALSE)) {
- purple_notify_error(
- gc, _("Create New SILC Key Pair"), _("Key Pair Generation failed"), NULL);
- return;
- }
-
- silcpurple_show_public_key(sg, NULL, public_key, NULL, NULL);
-
- silc_pkcs_public_key_free(public_key);
- silc_free(identifier);
-}
-
-static void
-silcpurple_create_keypair(PurplePluginAction *action)
-{
- PurpleConnection *gc = (PurpleConnection *) action->context;
- SilcPurple sg = gc->proto_data;
- PurpleRequestFields *fields;
- PurpleRequestFieldGroup *g;
- PurpleRequestField *f;
- const char *username, *realname;
- char *hostname, **u;
- char tmp[256], pkd[256], pkd2[256], prd[256], prd2[256];
-
- username = purple_account_get_username(sg->account);
- u = g_strsplit(username, "@", 2);
- username = u[0];
- realname = purple_account_get_user_info(sg->account);
- hostname = silc_net_localhost();
- g_snprintf(tmp, sizeof(tmp), "%s@%s", username, hostname);
-
- g_snprintf(pkd2, sizeof(pkd2), "%s" G_DIR_SEPARATOR_S"public_key.pub", silcpurple_silcdir());
- g_snprintf(prd2, sizeof(prd2), "%s" G_DIR_SEPARATOR_S"private_key.prv", silcpurple_silcdir());
- g_snprintf(pkd, sizeof(pkd) - 1, "%s",
- purple_account_get_string(gc->account, "public-key", pkd2));
- g_snprintf(prd, sizeof(prd) - 1, "%s",
- purple_account_get_string(gc->account, "private-key", prd2));
-
- fields = purple_request_fields_new();
-
- g = purple_request_field_group_new(NULL);
- f = purple_request_field_string_new("key", _("Key length"), "2048", FALSE);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_string_new("pkfile", _("Public key file"), pkd, FALSE);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_string_new("prfile", _("Private key file"), prd, FALSE);
- purple_request_field_group_add_field(g, f);
- purple_request_fields_add_group(fields, g);
-
- g = purple_request_field_group_new(NULL);
- f = purple_request_field_string_new("un", _("Username"), username ? username : "", FALSE);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_string_new("hn", _("Hostname"), hostname ? hostname : "", FALSE);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_string_new("rn", _("Real name"), realname ? realname : "", FALSE);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_string_new("e", _("Email"), tmp, FALSE);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_string_new("o", _("Organization"), "", FALSE);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_string_new("c", _("Country"), "", FALSE);
- purple_request_field_group_add_field(g, f);
- purple_request_fields_add_group(fields, g);
-
- g = purple_request_field_group_new(NULL);
- f = purple_request_field_string_new("pass1", _("Passphrase"), "", FALSE);
- purple_request_field_string_set_masked(f, TRUE);
- purple_request_field_group_add_field(g, f);
- f = purple_request_field_string_new("pass2", _("Passphrase (retype)"), "", FALSE);
- purple_request_field_string_set_masked(f, TRUE);
- purple_request_field_group_add_field(g, f);
- purple_request_fields_add_group(fields, g);
-
- purple_request_fields(gc, _("Create New SILC Key Pair"),
- _("Create New SILC Key Pair"), NULL, fields,
- _("Generate Key Pair"), G_CALLBACK(silcpurple_create_keypair_cb),
- _("Cancel"), G_CALLBACK(silcpurple_create_keypair_cancel),
- gc->account, NULL, NULL, gc);
-
- g_strfreev(u);
- silc_free(hostname);
-}
-
-static void
-silcpurple_change_pass(PurplePluginAction *action)
-{
- PurpleConnection *gc = (PurpleConnection *) action->context;
- purple_account_request_change_password(purple_connection_get_account(gc));
-}
-
-static void
-silcpurple_change_passwd(PurpleConnection *gc, const char *old, const char *new)
-{
- char prd[256];
- g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.pub", silcpurple_silcdir());
- silc_change_private_key_passphrase(purple_account_get_string(gc->account,
- "private-key",
- prd), old ? old : "", new ? new : "");
-}
-
-static void
-silcpurple_show_set_info(PurplePluginAction *action)
-{
- PurpleConnection *gc = (PurpleConnection *) action->context;
- purple_account_request_change_user_info(purple_connection_get_account(gc));
-}
-
-static void
-silcpurple_set_info(PurpleConnection *gc, const char *text)
-{
-}
-
-static GList *
-silcpurple_actions(PurplePlugin *plugin, gpointer context)
-{
- GList *list = NULL;
- PurplePluginAction *act;
-
- act = purple_plugin_action_new(_("Online Status"),
- silcpurple_attrs);
- list = g_list_append(list, act);
-
- act = purple_plugin_action_new(_("Detach From Server"),
- silcpurple_detach);
- list = g_list_append(list, act);
-
- act = purple_plugin_action_new(_("View Message of the Day"),
- silcpurple_view_motd);
- list = g_list_append(list, act);
-
- act = purple_plugin_action_new(_("Create SILC Key Pair..."),
- silcpurple_create_keypair);
- list = g_list_append(list, act);
-
- act = purple_plugin_action_new(_("Change Password..."),
- silcpurple_change_pass);
- list = g_list_append(list, act);
-
- act = purple_plugin_action_new(_("Set User Info..."),
- silcpurple_show_set_info);
- list = g_list_append(list, act);
-
- return list;
-}
-
-
-/******************************* IM Routines *********************************/
-
-typedef struct {
- char *nick;
- char *message;
- SilcUInt32 message_len;
- SilcMessageFlags flags;
- PurpleMessageFlags gflags;
-} *SilcPurpleIM;
-
-static void
-silcpurple_send_im_resolved(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry *clients,
- SilcUInt32 clients_count,
- void *context)
-{
- PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
- SilcPurpleIM im = context;
- PurpleConversation *convo;
- char tmp[256], *nickname = NULL;
- SilcClientEntry client_entry;
-#ifdef HAVE_SILCMIME_H
- SilcDList list;
-#endif
-
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, im->nick,
- sg->account);
- if (!convo)
- return;
-
- if (!clients)
- goto err;
-
- if (clients_count > 1) {
- silc_parse_userfqdn(im->nick, &nickname, NULL);
-
- /* Find the correct one. The im->nick might be a formatted nick
- so this will find the correct one. */
- clients = silc_client_get_clients_local(client, conn,
- nickname, im->nick,
- &clients_count);
- if (!clients)
- goto err;
- client_entry = clients[0];
- silc_free(clients);
- } else {
- client_entry = clients[0];
- }
-
-#ifdef HAVE_SILCMIME_H
- /* Check for images */
- if (im->gflags & PURPLE_MESSAGE_IMAGES) {
- list = silcpurple_image_message(im->message, (SilcUInt32 *)&im->flags);
- if (list) {
- /* Send one or more MIME message. If more than one, they
- are MIME fragments due to over large message */
- SilcBuffer buf;
-
- silc_dlist_start(list);
- while ((buf = silc_dlist_get(list)) != SILC_LIST_END)
- silc_client_send_private_message(client, conn,
- client_entry, im->flags,
- buf->data, buf->len,
- TRUE);
- silc_mime_partial_free(list);
- purple_conv_im_write(PURPLE_CONV_IM(convo), conn->local_entry->nickname,
- im->message, 0, time(NULL));
- goto out;
- }
- }
-#endif
-
- /* Send the message */
- silc_client_send_private_message(client, conn, client_entry, im->flags,
- (unsigned char *)im->message, im->message_len, TRUE);
- purple_conv_im_write(PURPLE_CONV_IM(convo), conn->local_entry->nickname,
- im->message, 0, time(NULL));
- goto out;
-
- err:
- g_snprintf(tmp, sizeof(tmp),
- _("User <I>%s</I> is not present in the network"), im->nick);
- purple_conversation_write(convo, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
-
- out:
- g_free(im->nick);
- g_free(im->message);
- silc_free(im);
- silc_free(nickname);
-}
-
-static int
-silcpurple_send_im(PurpleConnection *gc, const char *who, const char *message,
- PurpleMessageFlags flags)
-{
- SilcPurple sg = gc->proto_data;
- SilcClient client = sg->client;
- SilcClientConnection conn = sg->conn;
- SilcClientEntry *clients;
- SilcUInt32 clients_count, mflags;
- char *nickname, *msg, *tmp;
- int ret = 0;
- gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE);
-#ifdef HAVE_SILCMIME_H
- SilcDList list;
-#endif
-
- if (!who || !message)
- return 0;
-
- mflags = SILC_MESSAGE_FLAG_UTF8;
-
- tmp = msg = purple_unescape_html(message);
-
- if (!g_ascii_strncasecmp(msg, "/me ", 4)) {
- msg += 4;
- if (!*msg) {
- g_free(tmp);
- return 0;
- }
- mflags |= SILC_MESSAGE_FLAG_ACTION;
- } else if (strlen(msg) > 1 && msg[0] == '/') {
- if (!silc_client_command_call(client, conn, msg + 1))
- purple_notify_error(gc, _("Call Command"), _("Cannot call command"),
- _("Unknown command"));
- g_free(tmp);
- return 0;
- }
-
-
- if (!silc_parse_userfqdn(who, &nickname, NULL)) {
- g_free(tmp);
- return 0;
- }
-
- if (sign)
- mflags |= SILC_MESSAGE_FLAG_SIGNED;
-
- /* Find client entry */
- clients = silc_client_get_clients_local(client, conn, nickname, who,
- &clients_count);
- if (!clients) {
- /* Resolve unknown user */
- SilcPurpleIM im = silc_calloc(1, sizeof(*im));
- if (!im) {
- g_free(tmp);
- return 0;
- }
- im->nick = g_strdup(who);
- im->message = g_strdup(message);
- im->message_len = strlen(im->message);
- im->flags = mflags;
- im->gflags = flags;
- silc_client_get_clients(client, conn, nickname, NULL,
- silcpurple_send_im_resolved, im);
- silc_free(nickname);
- g_free(tmp);
- return 0;
- }
-
-#ifdef HAVE_SILCMIME_H
- /* Check for images */
- if (flags & PURPLE_MESSAGE_IMAGES) {
- list = silcpurple_image_message(message, &mflags);
- if (list) {
- /* Send one or more MIME message. If more than one, they
- are MIME fragments due to over large message */
- SilcBuffer buf;
-
- silc_dlist_start(list);
- while ((buf = silc_dlist_get(list)) != SILC_LIST_END)
- ret =
- silc_client_send_private_message(client, conn,
- clients[0], mflags,
- buf->data, buf->len,
- TRUE);
- silc_mime_partial_free(list);
- g_free(tmp);
- silc_free(nickname);
- silc_free(clients);
- return ret;
- }
- }
-#endif
-
- /* Send private message directly */
- ret = silc_client_send_private_message(client, conn, clients[0],
- mflags,
- (unsigned char *)msg,
- strlen(msg), TRUE);
-
- g_free(tmp);
- silc_free(nickname);
- silc_free(clients);
- return ret;
-}
-
-
-static GList *silcpurple_blist_node_menu(PurpleBlistNode *node) {
- /* split this single menu building function back into the two
- original: one for buddies and one for chats */
-
- if(PURPLE_BLIST_NODE_IS_CHAT(node)) {
- return silcpurple_chat_menu((PurpleChat *) node);
- } else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
- return silcpurple_buddy_menu((PurpleBuddy *) node);
- } else {
- g_return_val_if_reached(NULL);
- }
-}
-
-/********************************* Commands **********************************/
-
-static PurpleCmdRet silcpurple_cmd_chat_part(PurpleConversation *conv,
- const char *cmd, char **args, char **error, void *data)
-{
- PurpleConnection *gc;
- PurpleConversation *convo = conv;
- int id = 0;
-
- gc = purple_conversation_get_gc(conv);
-
- if (gc == NULL)
- return PURPLE_CMD_RET_FAILED;
-
- if(args && args[0])
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[0],
- gc->account);
-
- if (convo != NULL)
- id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo));
-
- if (id == 0)
- return PURPLE_CMD_RET_FAILED;
-
- silcpurple_chat_leave(gc, id);
-
- return PURPLE_CMD_RET_OK;
-
-}
-
-static PurpleCmdRet silcpurple_cmd_chat_topic(PurpleConversation *conv,
- const char *cmd, char **args, char **error, void *data)
-{
- PurpleConnection *gc;
- int id = 0;
- char *buf, *tmp, *tmp2;
- const char *topic;
-
- gc = purple_conversation_get_gc(conv);
- id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv));
-
- if (gc == NULL || id == 0)
- return PURPLE_CMD_RET_FAILED;
-
- if (!args || !args[0]) {
- topic = purple_conv_chat_get_topic (PURPLE_CONV_CHAT(conv));
- if (topic) {
- tmp = g_markup_escape_text(topic, -1);
- tmp2 = purple_markup_linkify(tmp);
- buf = g_strdup_printf(_("current topic is: %s"), tmp2);
- g_free(tmp);
- g_free(tmp2);
- } else
- buf = g_strdup(_("No topic is set"));
- purple_conv_chat_write(PURPLE_CONV_CHAT(conv), gc->account->username, buf,
- PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL));
- g_free(buf);
-
- }
-
- if (args && args[0] && (strlen(args[0]) > 255)) {
- *error = g_strdup(_("Topic too long"));
- return PURPLE_CMD_RET_FAILED;
- }
-
- silcpurple_chat_set_topic(gc, id, args ? args[0] : NULL);
-
- return PURPLE_CMD_RET_OK;
-}
-
-static PurpleCmdRet silcpurple_cmd_chat_join(PurpleConversation *conv,
- const char *cmd, char **args, char **error, void *data)
-{
- GHashTable *comp;
-
- if(!args || !args[0])
- return PURPLE_CMD_RET_FAILED;
-
- comp = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
-
- g_hash_table_replace(comp, "channel", args[0]);
- if(args[1])
- g_hash_table_replace(comp, "passphrase", args[1]);
-
- silcpurple_chat_join(purple_conversation_get_gc(conv), comp);
-
- g_hash_table_destroy(comp);
- return PURPLE_CMD_RET_OK;
-}
-
-static PurpleCmdRet silcpurple_cmd_chat_list(PurpleConversation *conv,
- const char *cmd, char **args, char **error, void *data)
-{
- PurpleConnection *gc;
- gc = purple_conversation_get_gc(conv);
- purple_roomlist_show_with_account(purple_connection_get_account(gc));
- return PURPLE_CMD_RET_OK;
-}
-
-static PurpleCmdRet silcpurple_cmd_whois(PurpleConversation *conv,
- const char *cmd, char **args, char **error, void *data)
-{
- PurpleConnection *gc;
-
- gc = purple_conversation_get_gc(conv);
-
- if (gc == NULL)
- return PURPLE_CMD_RET_FAILED;
-
- silcpurple_get_info(gc, args[0]);
-
- return PURPLE_CMD_RET_OK;
-}
-
-static PurpleCmdRet silcpurple_cmd_msg(PurpleConversation *conv,
- const char *cmd, char **args, char **error, void *data)
-{
- int ret;
- PurpleConnection *gc;
-
- gc = purple_conversation_get_gc(conv);
-
- if (gc == NULL)
- return PURPLE_CMD_RET_FAILED;
-
- ret = silcpurple_send_im(gc, args[0], args[1], PURPLE_MESSAGE_SEND);
-
- if (ret)
- return PURPLE_CMD_RET_OK;
- else
- return PURPLE_CMD_RET_FAILED;
-}
-
-static PurpleCmdRet silcpurple_cmd_query(PurpleConversation *conv,
- const char *cmd, char **args, char **error, void *data)
-{
- int ret = 1;
- PurpleConversation *convo;
- PurpleConnection *gc;
- PurpleAccount *account;
-
- if (!args || !args[0]) {
- *error = g_strdup(_("You must specify a nick"));
- return PURPLE_CMD_RET_FAILED;
- }
-
- gc = purple_conversation_get_gc(conv);
-
- if (gc == NULL)
- return PURPLE_CMD_RET_FAILED;
-
- account = purple_connection_get_account(gc);
-
- convo = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, args[0]);
-
- if (args[1]) {
- ret = silcpurple_send_im(gc, args[0], args[1], PURPLE_MESSAGE_SEND);
- purple_conv_im_write(PURPLE_CONV_IM(convo), purple_connection_get_display_name(gc),
- args[1], PURPLE_MESSAGE_SEND, time(NULL));
- }
-
- if (ret)
- return PURPLE_CMD_RET_OK;
- else
- return PURPLE_CMD_RET_FAILED;
-}
-
-static PurpleCmdRet silcpurple_cmd_motd(PurpleConversation *conv,
- const char *cmd, char **args, char **error, void *data)
-{
- PurpleConnection *gc;
- SilcPurple sg;
- char *tmp;
-
- gc = purple_conversation_get_gc(conv);
-
- if (gc == NULL)
- return PURPLE_CMD_RET_FAILED;
-
- sg = gc->proto_data;
-
- if (sg == NULL)
- return PURPLE_CMD_RET_FAILED;
-
- if (!sg->motd) {
- *error = g_strdup(_("There is no Message of the Day associated with this connection"));
- return PURPLE_CMD_RET_FAILED;
- }
-
- tmp = g_markup_escape_text(sg->motd, -1);
- purple_notify_formatted(gc, NULL, _("Message of the Day"), NULL,
- tmp, NULL, NULL);
- g_free(tmp);
-
- return PURPLE_CMD_RET_OK;
-}
-
-static PurpleCmdRet silcpurple_cmd_detach(PurpleConversation *conv,
- const char *cmd, char **args, char **error, void *data)
-{
- PurpleConnection *gc;
- SilcPurple sg;
-
- gc = purple_conversation_get_gc(conv);
-
- if (gc == NULL)
- return PURPLE_CMD_RET_FAILED;
-
- sg = gc->proto_data;
-
- if (sg == NULL)
- return PURPLE_CMD_RET_FAILED;
-
- silc_client_command_call(sg->client, sg->conn, "DETACH");
- sg->detaching = TRUE;
-
- return PURPLE_CMD_RET_OK;
-}
-
-static PurpleCmdRet silcpurple_cmd_cmode(PurpleConversation *conv,
- const char *cmd, char **args, char **error, void *data)
-{
- PurpleConnection *gc;
- SilcPurple sg;
- SilcChannelEntry channel;
- char *silccmd, *silcargs, *msg, tmp[256];
- const char *chname;
-
- gc = purple_conversation_get_gc(conv);
-
- if (gc == NULL || !args || gc->proto_data == NULL)
- return PURPLE_CMD_RET_FAILED;
-
- sg = gc->proto_data;
-
- if (args[0])
- chname = args[0];
- else
- chname = purple_conversation_get_name(conv);
-
- if (!args[1]) {
- channel = silc_client_get_channel(sg->client, sg->conn,
- (char *)chname);
- if (!channel) {
- *error = g_strdup_printf(_("channel %s not found"), chname);
- return PURPLE_CMD_RET_FAILED;
- }
- if (channel->mode) {
- silcpurple_get_chmode_string(channel->mode, tmp, sizeof(tmp));
- msg = g_strdup_printf(_("channel modes for %s: %s"), chname, tmp);
- } else {
- msg = g_strdup_printf(_("no channel modes are set on %s"), chname);
- }
- purple_conv_chat_write(PURPLE_CONV_CHAT(conv), "",
- msg, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL));
- g_free(msg);
- return PURPLE_CMD_RET_OK;
- }
-
- silcargs = g_strjoinv(" ", args);
- silccmd = g_strconcat(cmd, " ", args ? silcargs : NULL, NULL);
- g_free(silcargs);
- if (!silc_client_command_call(sg->client, sg->conn, silccmd)) {
- g_free(silccmd);
- *error = g_strdup_printf(_("Failed to set cmodes for %s"), args[0]);
- return PURPLE_CMD_RET_FAILED;
- }
- g_free(silccmd);
-
- return PURPLE_CMD_RET_OK;
-}
-
-static PurpleCmdRet silcpurple_cmd_generic(PurpleConversation *conv,
- const char *cmd, char **args, char **error, void *data)
-{
- PurpleConnection *gc;
- SilcPurple sg;
- char *silccmd, *silcargs;
-
- gc = purple_conversation_get_gc(conv);
-
- if (gc == NULL)
- return PURPLE_CMD_RET_FAILED;
-
- sg = gc->proto_data;
-
- if (sg == NULL)
- return PURPLE_CMD_RET_FAILED;
-
- silcargs = g_strjoinv(" ", args);
- silccmd = g_strconcat(cmd, " ", args ? silcargs : NULL, NULL);
- g_free(silcargs);
- if (!silc_client_command_call(sg->client, sg->conn, silccmd)) {
- g_free(silccmd);
- *error = g_strdup_printf(_("Unknown command: %s, (may be a client bug)"), cmd);
- return PURPLE_CMD_RET_FAILED;
- }
- g_free(silccmd);
-
- return PURPLE_CMD_RET_OK;
-}
-
-static PurpleCmdRet silcpurple_cmd_quit(PurpleConversation *conv,
- const char *cmd, char **args, char **error, void *data)
-{
- PurpleConnection *gc;
- SilcPurple sg;
- GHashTable *ui_info;
- const char *ui_name = NULL, *ui_website = NULL;
- char *quit_msg;
-
- gc = purple_conversation_get_gc(conv);
-
- if (gc == NULL)
- return PURPLE_CMD_RET_FAILED;
-
- sg = gc->proto_data;
-
- if (sg == NULL)
- return PURPLE_CMD_RET_FAILED;
-
- ui_info = purple_core_get_ui_info();
-
- if(ui_info) {
- ui_name = g_hash_table_lookup(ui_info, "name");
- ui_website = g_hash_table_lookup(ui_info, "website");
- }
-
- if(!ui_name || !ui_website) {
- ui_name = "Pidgin";
- ui_website = PURPLE_WEBSITE;
- }
- quit_msg = g_strdup_printf(_("Download %s: %s"),
- ui_name, ui_website);
-
- silc_client_command_call(sg->client, sg->conn, NULL,
- "QUIT", (args && args[0]) ? args[0] : quit_msg, NULL);
- g_free(quit_msg);
-
- return PURPLE_CMD_RET_OK;
-}
-
-static PurpleCmdRet silcpurple_cmd_call(PurpleConversation *conv,
- const char *cmd, char **args, char **error, void *data)
-{
- PurpleConnection *gc;
- SilcPurple sg;
-
- gc = purple_conversation_get_gc(conv);
-
- if (gc == NULL)
- return PURPLE_CMD_RET_FAILED;
-
- sg = gc->proto_data;
-
- if (sg == NULL)
- return PURPLE_CMD_RET_FAILED;
-
- if (!silc_client_command_call(sg->client, sg->conn, args[0])) {
- *error = g_strdup_printf(_("Unknown command: %s"), args[0]);
- return PURPLE_CMD_RET_FAILED;
- }
-
- return PURPLE_CMD_RET_OK;
-}
-
-
-/************************** Plugin Initialization ****************************/
-
-static void
-silcpurple_register_commands(void)
-{
- purple_cmd_register("part", "w", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
- PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
- "prpl-silc", silcpurple_cmd_chat_part, _("part [channel]: Leave the chat"), NULL);
- purple_cmd_register("leave", "w", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
- PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
- "prpl-silc", silcpurple_cmd_chat_part, _("leave [channel]: Leave the chat"), NULL);
- purple_cmd_register("topic", "s", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc",
- silcpurple_cmd_chat_topic, _("topic [&lt;new topic&gt;]: View or change the topic"), NULL);
- purple_cmd_register("join", "ws", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
- PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
- "prpl-silc", silcpurple_cmd_chat_join,
- _("join &lt;channel&gt; [&lt;password&gt;]: Join a chat on this network"), NULL);
- purple_cmd_register("list", "", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc",
- silcpurple_cmd_chat_list, _("list: List channels on this network"), NULL);
- purple_cmd_register("whois", "w", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
- "prpl-silc",
- silcpurple_cmd_whois, _("whois &lt;nick&gt;: View nick's information"), NULL);
- purple_cmd_register("msg", "ws", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
- "prpl-silc", silcpurple_cmd_msg,
- _("msg &lt;nick&gt; &lt;message&gt;: Send a private message to a user"), NULL);
- purple_cmd_register("query", "ws", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_query,
- _("query &lt;nick&gt; [&lt;message&gt;]: Send a private message to a user"), NULL);
- purple_cmd_register("motd", "", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_motd,
- _("motd: View the server's Message Of The Day"), NULL);
- purple_cmd_register("detach", "", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
- "prpl-silc", silcpurple_cmd_detach,
- _("detach: Detach this session"), NULL);
- purple_cmd_register("quit", "s", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_quit,
- _("quit [message]: Disconnect from the server, with an optional message"), NULL);
- purple_cmd_register("call", "s", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
- "prpl-silc", silcpurple_cmd_call,
- _("call &lt;command&gt;: Call any silc client command"), NULL);
- /* These below just get passed through for the silc client library to deal
- * with */
- purple_cmd_register("kill", "ws", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
- _("kill &lt;nick&gt; [-pubkey|&lt;reason&gt;]: Kill nick"), NULL);
- purple_cmd_register("nick", "w", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
- "prpl-silc", silcpurple_cmd_generic,
- _("nick &lt;newnick&gt;: Change your nickname"), NULL);
- purple_cmd_register("whowas", "ww", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
- _("whowas &lt;nick&gt;: View nick's information"), NULL);
- purple_cmd_register("cmode", "wws", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_cmode,
- _("cmode &lt;channel&gt; [+|-&lt;modes&gt;] [arguments]: Change or display channel modes"), NULL);
- purple_cmd_register("cumode", "wws", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
- _("cumode &lt;channel&gt; +|-&lt;modes&gt; &lt;nick&gt;: Change nick's modes on channel"), NULL);
- purple_cmd_register("umode", "w", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
- "prpl-silc", silcpurple_cmd_generic,
- _("umode &lt;usermodes&gt;: Set your modes in the network"), NULL);
- purple_cmd_register("oper", "s", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
- "prpl-silc", silcpurple_cmd_generic,
- _("oper &lt;nick&gt; [-pubkey]: Get server operator privileges"), NULL);
- purple_cmd_register("invite", "ws", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
- _("invite &lt;channel&gt; [-|+]&lt;nick&gt;: invite nick or add/remove from channel invite list"), NULL);
- purple_cmd_register("kick", "wws", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
- _("kick &lt;channel&gt; &lt;nick&gt; [comment]: Kick client from channel"), NULL);
- purple_cmd_register("info", "w", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
- _("info [server]: View server administrative details"), NULL);
- purple_cmd_register("ban", "ww", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
- _("ban [&lt;channel&gt; +|-&lt;nick&gt;]: Ban client from channel"), NULL);
- purple_cmd_register("getkey", "w", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
- "prpl-silc", silcpurple_cmd_generic,
- _("getkey &lt;nick|server&gt;: Retrieve client's or server's public key"), NULL);
- purple_cmd_register("stats", "", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
- "prpl-silc", silcpurple_cmd_generic,
- _("stats: View server and network statistics"), NULL);
- purple_cmd_register("ping", "", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
- "prpl-silc", silcpurple_cmd_generic,
- _("ping: Send PING to the connected server"), NULL);
-#if 0 /* Purple doesn't handle these yet */
- purple_cmd_register("users", "w", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
- "prpl-silc", silcpurple_cmd_users,
- _("users &lt;channel&gt;: List users in channel"));
- purple_cmd_register("names", "ww", PURPLE_CMD_P_PRPL,
- PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
- PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_names,
- _("names [-count|-ops|-halfops|-voices|-normal] &lt;channel(s)&gt;: List specific users in channel(s)"));
-#endif
-}
-
-static PurpleWhiteboardPrplOps silcpurple_wb_ops =
-{
- silcpurple_wb_start,
- silcpurple_wb_end,
- silcpurple_wb_get_dimensions,
- silcpurple_wb_set_dimensions,
- silcpurple_wb_get_brush,
- silcpurple_wb_set_brush,
- silcpurple_wb_send,
- silcpurple_wb_clear,
-
- /* padding */
- NULL,
- NULL,
- NULL,
- NULL
-};
-
-static PurplePluginProtocolInfo prpl_info =
-{
-#ifdef HAVE_SILCMIME_H
- OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME |
- OPT_PROTO_PASSWORD_OPTIONAL | OPT_PROTO_IM_IMAGE |
- OPT_PROTO_SLASH_COMMANDS_NATIVE,
-#else
- OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME |
- OPT_PROTO_PASSWORD_OPTIONAL |
- OPT_PROTO_SLASH_COMMANDS_NATIVE,
-#endif
- NULL, /* user_splits */
- NULL, /* protocol_options */
-#ifdef SILC_ATTRIBUTE_USER_ICON
- {"jpeg,gif,png,bmp", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
-#else
- NO_BUDDY_ICONS,
-#endif
- silcpurple_list_icon, /* list_icon */
- NULL, /* list_emblems */
- silcpurple_status_text, /* status_text */
- silcpurple_tooltip_text, /* tooltip_text */
- silcpurple_away_states, /* away_states */
- silcpurple_blist_node_menu, /* blist_node_menu */
- silcpurple_chat_info, /* chat_info */
- silcpurple_chat_info_defaults,/* chat_info_defaults */
- silcpurple_login, /* login */
- silcpurple_close, /* close */
- silcpurple_send_im, /* send_im */
- silcpurple_set_info, /* set_info */
- NULL, /* send_typing */
- silcpurple_get_info, /* get_info */
- silcpurple_set_status, /* set_status */
- silcpurple_idle_set, /* set_idle */
- silcpurple_change_passwd, /* change_passwd */
- silcpurple_add_buddy, /* add_buddy */
- NULL, /* add_buddies */
- silcpurple_remove_buddy, /* remove_buddy */
- NULL, /* remove_buddies */
- NULL, /* add_permit */
- NULL, /* add_deny */
- NULL, /* rem_permit */
- NULL, /* rem_deny */
- NULL, /* set_permit_deny */
- silcpurple_chat_join, /* join_chat */
- NULL, /* reject_chat */
- silcpurple_get_chat_name, /* get_chat_name */
- silcpurple_chat_invite, /* chat_invite */
- silcpurple_chat_leave, /* chat_leave */
- NULL, /* chat_whisper */
- silcpurple_chat_send, /* chat_send */
- silcpurple_keepalive, /* keepalive */
- NULL, /* register_user */
- NULL, /* get_cb_info */
- NULL, /* get_cb_away */
- NULL, /* alias_buddy */
- NULL, /* group_buddy */
- NULL, /* rename_group */
- NULL, /* buddy_free */
- NULL, /* convo_closed */
- NULL, /* normalize */
-#ifdef SILC_ATTRIBUTE_USER_ICON
- silcpurple_buddy_set_icon, /* set_buddy_icon */
-#else
- NULL,
-#endif
- NULL, /* remove_group */
- NULL, /* get_cb_real_name */
- silcpurple_chat_set_topic, /* set_chat_topic */
- NULL, /* find_blist_chat */
- silcpurple_roomlist_get_list, /* roomlist_get_list */
- silcpurple_roomlist_cancel, /* roomlist_cancel */
- NULL, /* roomlist_expand_category */
- NULL, /* can_receive_file */
- silcpurple_ftp_send_file, /* send_file */
- silcpurple_ftp_new_xfer, /* new_xfer */
- NULL, /* offline_message */
- &silcpurple_wb_ops, /* whiteboard_prpl_ops */
- NULL, /* send_raw */
- NULL, /* roomlist_room_serialize */
- NULL, /* unregister_user */
- NULL, /* send_attention */
- NULL, /* get_attention_types */
- sizeof(PurplePluginProtocolInfo), /* struct_size */
- NULL, /* get_account_text_table */
- NULL, /* initiate_media */
- NULL, /* get_media_caps */
- NULL, /* get_moods */
- NULL, /* set_public_alias */
- NULL, /* get_public_alias */
- NULL, /* add_buddy_with_invite */
- NULL /* add_buddies_with_invite */
-};
-
-static PurplePluginInfo info =
-{
- PURPLE_PLUGIN_MAGIC,
- PURPLE_MAJOR_VERSION,
- PURPLE_MINOR_VERSION,
- PURPLE_PLUGIN_PROTOCOL, /**< type */
- NULL, /**< ui_requirement */
- 0, /**< flags */
- NULL, /**< dependencies */
- PURPLE_PRIORITY_DEFAULT, /**< priority */
-
- "prpl-silc", /**< id */
- "SILC", /**< name */
- "1.0", /**< version */
- /** summary */
- N_("SILC Protocol Plugin"),
- /** description */
- N_("Secure Internet Live Conferencing (SILC) Protocol"),
- "Pekka Riikonen", /**< author */
- "http://silcnet.org/", /**< homepage */
-
- NULL, /**< load */
- NULL, /**< unload */
- NULL, /**< destroy */
-
- NULL, /**< ui_info */
- &prpl_info, /**< extra_info */
- NULL, /**< prefs_info */
- silcpurple_actions,
-
- /* padding */
- NULL,
- NULL,
- NULL,
- NULL
-};
-
-static void
-init_plugin(PurplePlugin *plugin)
-{
- PurpleAccountOption *option;
- PurpleAccountUserSplit *split;
- char tmp[256];
- int i;
- PurpleKeyValuePair *kvp;
- GList *list = NULL;
-
- silc_plugin = plugin;
-
- split = purple_account_user_split_new(_("Network"), "silcnet.org", '@');
- prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
-
- /* Account options */
- option = purple_account_option_string_new(_("Connect server"),
- "server",
- "silc.silcnet.org");
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
- option = purple_account_option_int_new(_("Port"), "port", 706);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
- g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
- option = purple_account_option_string_new(_("Public Key file"),
- "public-key", tmp);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
- g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
- option = purple_account_option_string_new(_("Private Key file"),
- "private-key", tmp);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
- for (i = 0; silc_default_ciphers[i].name; i++) {
- kvp = g_new0(PurpleKeyValuePair, 1);
- kvp->key = g_strdup(silc_default_ciphers[i].name);
- kvp->value = g_strdup(silc_default_ciphers[i].name);
- list = g_list_append(list, kvp);
- }
- option = purple_account_option_list_new(_("Cipher"), "cipher", list);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
- list = NULL;
- for (i = 0; silc_default_hmacs[i].name; i++) {
- kvp = g_new0(PurpleKeyValuePair, 1);
- kvp->key = g_strdup(silc_default_hmacs[i].name);
- kvp->value = g_strdup(silc_default_hmacs[i].name);
- list = g_list_append(list, kvp);
- }
- option = purple_account_option_list_new(_("HMAC"), "hmac", list);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
- option = purple_account_option_bool_new(_("Public key authentication"),
- "pubkey-auth", FALSE);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
- option = purple_account_option_bool_new(_("Block IMs without Key Exchange"),
- "block-ims", FALSE);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
- option = purple_account_option_bool_new(_("Block messages to whiteboard"),
- "block-wb", FALSE);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
- option = purple_account_option_bool_new(_("Automatically open whiteboard"),
- "open-wb", FALSE);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
- option = purple_account_option_bool_new(_("Digitally sign and verify all messages"),
- "sign-verify", FALSE);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
- purple_prefs_remove("/plugins/prpl/silc");
-
- silcpurple_register_commands();
-
-#ifdef _WIN32
- silc_net_win32_init();
-#endif
-}
-
-PURPLE_INIT_PLUGIN(silc10, init_plugin, info);
diff --git a/libpurple/protocols/silc10/silcpurple.h b/libpurple/protocols/silc10/silcpurple.h
deleted file mode 100644
index a24d8a0b9d..0000000000
--- a/libpurple/protocols/silc10/silcpurple.h
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
-
- silcpurple.h
-
- Author: Pekka Riikonen <priikone@silcnet.org>
-
- Copyright (C) 2004 Pekka Riikonen
-
- 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; version 2 of the License.
-
- 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.
-
-*/
-
-#ifndef SILCPURPLE_H
-#define SILCPURPLE_H
-
-/* Purple includes */
-#include "internal.h"
-#include "account.h"
-#include "accountopt.h"
-#include "cmds.h"
-#include "conversation.h"
-#include "debug.h"
-#include "ft.h"
-#include "notify.h"
-#include "prpl.h"
-#include "request.h"
-#include "roomlist.h"
-#include "server.h"
-#include "util.h"
-
-/* Default public and private key file names */
-#define SILCPURPLE_PUBLIC_KEY_NAME "public_key.pub"
-#define SILCPURPLE_PRIVATE_KEY_NAME "private_key.prv"
-
-/* Default settings for creating key pair */
-#define SILCPURPLE_DEF_PKCS "rsa"
-#define SILCPURPLE_DEF_PKCS_LEN 2048
-
-#define SILCPURPLE_PRVGRP 0x001fffff
-
-/* Status IDs */
-#define SILCPURPLE_STATUS_ID_OFFLINE "offline"
-#define SILCPURPLE_STATUS_ID_AVAILABLE "available"
-#define SILCPURPLE_STATUS_ID_HYPER "hyper"
-#define SILCPURPLE_STATUS_ID_AWAY "away"
-#define SILCPURPLE_STATUS_ID_BUSY "busy"
-#define SILCPURPLE_STATUS_ID_INDISPOSED "indisposed"
-#define SILCPURPLE_STATUS_ID_PAGE "page"
-
-typedef struct {
- unsigned long id;
- const char *channel;
- unsigned long chid;
- const char *parentch;
- SilcChannelPrivateKey key;
-} *SilcPurplePrvgrp;
-
-/* The SILC Purple plugin context */
-typedef struct SilcPurpleStruct {
- SilcClient client;
- SilcClientConnection conn;
-
- guint scheduler;
- PurpleConnection *gc;
- PurpleAccount *account;
- unsigned long channel_ids;
- GList *grps;
-
- char *motd;
- PurpleRoomlist *roomlist;
-#ifdef HAVE_SILCMIME_H
- SilcMimeAssembler mimeass;
-#endif
- unsigned int detaching : 1;
- unsigned int resuming : 1;
- unsigned int roomlist_cancelled : 1;
- unsigned int chpk : 1;
-} *SilcPurple;
-
-
-gboolean silcpurple_check_silc_dir(PurpleConnection *gc);
-void silcpurple_chat_join_done(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry *clients,
- SilcUInt32 clients_count,
- void *context);
-const char *silcpurple_silcdir(void);
-const char *silcpurple_session_file(const char *account);
-void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn,
- const char *name, SilcSocketType conn_type,
- unsigned char *pk, SilcUInt32 pk_len,
- SilcSKEPKType pk_type,
- SilcVerifyPublicKey completion, void *context);
-GList *silcpurple_buddy_menu(PurpleBuddy *buddy);
-void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
-void silcpurple_send_buddylist(PurpleConnection *gc);
-void silcpurple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
-void silcpurple_buddy_keyagr_request(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry client_entry,
- const char *hostname, SilcUInt16 port);
-void silcpurple_idle_set(PurpleConnection *gc, int idle);
-void silcpurple_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full);
-char *silcpurple_status_text(PurpleBuddy *b);
-gboolean silcpurple_ip_is_private(const char *ip);
-void silcpurple_ftp_send_file(PurpleConnection *gc, const char *name, const char *file);
-PurpleXfer *silcpurple_ftp_new_xfer(PurpleConnection *gc, const char *name);
-void silcpurple_ftp_request(SilcClient client, SilcClientConnection conn,
- SilcClientEntry client_entry, SilcUInt32 session_id,
- const char *hostname, SilcUInt16 port);
-void silcpurple_show_public_key(SilcPurple sg,
- const char *name, SilcPublicKey public_key,
- GCallback callback, void *context);
-void silcpurple_get_info(PurpleConnection *gc, const char *who);
-SilcAttributePayload
-silcpurple_get_attr(SilcDList attrs, SilcAttribute attribute);
-void silcpurple_get_umode_string(SilcUInt32 mode, char *buf,
- SilcUInt32 buf_size);
-void silcpurple_get_chmode_string(SilcUInt32 mode, char *buf,
- SilcUInt32 buf_size);
-void silcpurple_get_chumode_string(SilcUInt32 mode, char *buf,
- SilcUInt32 buf_size);
-GList *silcpurple_chat_info(PurpleConnection *gc);
-GHashTable *silcpurple_chat_info_defaults(PurpleConnection *gc, const char *chat_name);
-GList *silcpurple_chat_menu(PurpleChat *);
-void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data);
-char *silcpurple_get_chat_name(GHashTable *data);
-void silcpurple_chat_invite(PurpleConnection *gc, int id, const char *msg,
- const char *name);
-void silcpurple_chat_leave(PurpleConnection *gc, int id);
-int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg, PurpleMessageFlags flags);
-void silcpurple_chat_set_topic(PurpleConnection *gc, int id, const char *topic);
-PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc);
-void silcpurple_roomlist_cancel(PurpleRoomlist *list);
-void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel,
- SilcBuffer channel_pubkeys);
-void silcpurple_parse_attrs(SilcDList attrs, char **moodstr, char **statusstr,
- char **contactstr, char **langstr, char **devicestr,
- char **tzstr, char **geostr);
-#ifdef SILC_ATTRIBUTE_USER_ICON
-void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleStoredImage *img);
-#endif
-#ifdef HAVE_SILCMIME_H
-char *silcpurple_file2mime(const char *filename);
-SilcDList silcpurple_image_message(const char *msg, SilcUInt32 *mflags);
-#endif
-
-#ifdef _WIN32
-typedef int uid_t;
-
-struct passwd {
- char *pw_name; /* user name */
- char *pw_passwd; /* user password */
- int pw_uid; /* user id */
- int pw_gid; /* group id */
- char *pw_gecos; /* real name */
- char *pw_dir; /* home directory */
- char *pw_shell; /* shell program */
-};
-
-struct passwd *getpwuid(int uid);
-int getuid(void);
-int geteuid(void);
-#endif
-
-#endif /* SILCPURPLE_H */
diff --git a/libpurple/protocols/silc10/util.c b/libpurple/protocols/silc10/util.c
deleted file mode 100644
index 792afdea58..0000000000
--- a/libpurple/protocols/silc10/util.c
+++ /dev/null
@@ -1,776 +0,0 @@
-/*
-
- silcpurple_util.c
-
- Author: Pekka Riikonen <priikone@silcnet.org>
-
- Copyright (C) 2004 - 2005 Pekka Riikonen
-
- 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; version 2 of the License.
-
- 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.
-
-*/
-
-#include "silcincludes.h"
-#include "silcclient.h"
-#include "silcpurple.h"
-#include "imgstore.h"
-
-/**************************** Utility Routines *******************************/
-
-static char str[256], str2[256];
-
-const char *silcpurple_silcdir(void)
-{
- const char *hd = purple_home_dir();
- memset(str, 0, sizeof(str));
- g_snprintf(str, sizeof(str) - 1, "%s" G_DIR_SEPARATOR_S ".silc", hd ? hd : "/tmp");
- return (const char *)str;
-}
-
-const char *silcpurple_session_file(const char *account)
-{
- memset(str2, 0, sizeof(str2));
- g_snprintf(str2, sizeof(str2) - 1, "%s" G_DIR_SEPARATOR_S "%s_session",
- silcpurple_silcdir(), account);
- return (const char *)str2;
-}
-
-gboolean silcpurple_ip_is_private(const char *ip)
-{
- if (silc_net_is_ip4(ip)) {
- if (!strncmp(ip, "10.", 3)) {
- return TRUE;
- } else if (!strncmp(ip, "172.", 4) && strlen(ip) > 6) {
- char tmp[3];
- int s;
- memset(tmp, 0, sizeof(tmp));
- strncpy(tmp, ip + 4, 2);
- s = atoi(tmp);
- if (s >= 16 && s <= 31)
- return TRUE;
- } else if (!strncmp(ip, "192.168.", 8)) {
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-/* This checks stats for various SILC files and directories. First it
- checks if ~/.silc directory exist and is owned by the correct user. If
- it doesn't exist, it will create the directory. After that it checks if
- user's Public and Private key files exists and creates them if needed. */
-
-gboolean silcpurple_check_silc_dir(PurpleConnection *gc)
-{
- char filename[256], file_public_key[256], file_private_key[256];
- char servfilename[256], clientfilename[256], friendsfilename[256];
- char pkd[256], prd[256];
- struct stat st;
- struct passwd *pw;
- int fd;
-
- pw = getpwuid(getuid());
- if (!pw) {
- purple_debug_error("silc", "silc: %s\n", g_strerror(errno));
- return FALSE;
- }
-
- g_snprintf(filename, sizeof(filename) - 1, "%s", silcpurple_silcdir());
- g_snprintf(servfilename, sizeof(servfilename) - 1, "%s" G_DIR_SEPARATOR_S "serverkeys",
- silcpurple_silcdir());
- g_snprintf(clientfilename, sizeof(clientfilename) - 1, "%s" G_DIR_SEPARATOR_S "clientkeys",
- silcpurple_silcdir());
- g_snprintf(friendsfilename, sizeof(friendsfilename) - 1, "%s" G_DIR_SEPARATOR_S "friends",
- silcpurple_silcdir());
-
- /*
- * Check ~/.silc directory
- */
- if ((g_stat(filename, &st)) == -1) {
- /* If dir doesn't exist */
- if (errno == ENOENT) {
- if (pw->pw_uid == geteuid()) {
- if ((g_mkdir(filename, 0755)) == -1) {
- purple_debug_error("silc", "Couldn't create '%s' directory\n", filename);
- return FALSE;
- }
- } else {
- purple_debug_error("silc", "Couldn't create '%s' directory due to a wrong uid!\n",
- filename);
- return FALSE;
- }
- } else {
- purple_debug_error("silc", "Couldn't stat '%s' directory, error: %s\n", filename, g_strerror(errno));
- return FALSE;
- }
- } else {
-#ifndef _WIN32
- /* Check the owner of the dir */
- if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
- purple_debug_error("silc", "You don't seem to own '%s' directory\n",
- filename);
- return FALSE;
- }
-#endif
- }
-
- /*
- * Check ~./silc/serverkeys directory
- */
- if ((g_stat(servfilename, &st)) == -1) {
- /* If dir doesn't exist */
- if (errno == ENOENT) {
- if (pw->pw_uid == geteuid()) {
- if ((g_mkdir(servfilename, 0755)) == -1) {
- purple_debug_error("silc", "Couldn't create '%s' directory\n", servfilename);
- return FALSE;
- }
- } else {
- purple_debug_error("silc", "Couldn't create '%s' directory due to a wrong uid!\n",
- servfilename);
- return FALSE;
- }
- } else {
- purple_debug_error("silc", "Couldn't stat '%s' directory, error: %s\n",
- servfilename, g_strerror(errno));
- return FALSE;
- }
- }
-
- /*
- * Check ~./silc/clientkeys directory
- */
- if ((g_stat(clientfilename, &st)) == -1) {
- /* If dir doesn't exist */
- if (errno == ENOENT) {
- if (pw->pw_uid == geteuid()) {
- if ((g_mkdir(clientfilename, 0755)) == -1) {
- purple_debug_error("silc", "Couldn't create '%s' directory\n", clientfilename);
- return FALSE;
- }
- } else {
- purple_debug_error("silc", "Couldn't create '%s' directory due to a wrong uid!\n",
- clientfilename);
- return FALSE;
- }
- } else {
- purple_debug_error("silc", "Couldn't stat '%s' directory, error: %s\n",
- clientfilename, g_strerror(errno));
- return FALSE;
- }
- }
-
- /*
- * Check ~./silc/friends directory
- */
- if ((g_stat(friendsfilename, &st)) == -1) {
- /* If dir doesn't exist */
- if (errno == ENOENT) {
- if (pw->pw_uid == geteuid()) {
- if ((g_mkdir(friendsfilename, 0755)) == -1) {
- purple_debug_error("silc", "Couldn't create '%s' directory\n", friendsfilename);
- return FALSE;
- }
- } else {
- purple_debug_error("silc", "Couldn't create '%s' directory due to a wrong uid!\n",
- friendsfilename);
- return FALSE;
- }
- } else {
- purple_debug_error("silc", "Couldn't stat '%s' directory, error: %s\n",
- friendsfilename, g_strerror(errno));
- return FALSE;
- }
- }
-
- /*
- * Check Public and Private keys
- */
- g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
- g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
- g_snprintf(file_public_key, sizeof(file_public_key) - 1, "%s",
- purple_account_get_string(gc->account, "public-key", pkd));
- g_snprintf(file_private_key, sizeof(file_public_key) - 1, "%s",
- purple_account_get_string(gc->account, "private-key", prd));
-
- if ((g_stat(file_public_key, &st)) == -1) {
- /* If file doesn't exist */
- if (errno == ENOENT) {
- purple_connection_update_progress(gc, _("Creating SILC key pair..."), 1, 5);
- if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS,
- SILCPURPLE_DEF_PKCS_LEN,
- file_public_key, file_private_key, NULL,
- (gc->password == NULL) ? "" : gc->password,
- NULL, NULL, NULL, FALSE)) {
- purple_debug_error("silc", "Couldn't create key pair\n");
- return FALSE;
- }
-
- if ((g_stat(file_public_key, &st)) == -1) {
- purple_debug_error("silc", "Couldn't stat '%s' public key, error: %s\n",
- file_public_key, g_strerror(errno));
- return FALSE;
- }
- } else {
- purple_debug_error("silc", "Couldn't stat '%s' public key, error: %s\n",
- file_public_key, g_strerror(errno));
- return FALSE;
- }
- }
-
-#ifndef _WIN32
- /* Check the owner of the public key */
- if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
- purple_debug_error("silc", "You don't seem to own your public key!?\n");
- return FALSE;
- }
-#endif
-
- if ((fd = g_open(file_private_key, O_RDONLY, 0)) != -1) {
- if ((fstat(fd, &st)) == -1) {
- purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
- file_private_key, g_strerror(errno));
- close(fd);
- return FALSE;
- }
- } else if ((g_stat(file_private_key, &st)) == -1) {
- /* If file doesn't exist */
- if (errno == ENOENT) {
- purple_connection_update_progress(gc, _("Creating SILC key pair..."), 1, 5);
- if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS,
- SILCPURPLE_DEF_PKCS_LEN,
- file_public_key, file_private_key, NULL,
- (gc->password == NULL) ? "" : gc->password,
- NULL, NULL, NULL, FALSE)) {
- purple_debug_error("silc", "Couldn't create key pair\n");
- return FALSE;
- }
-
- if ((fd = g_open(file_private_key, O_RDONLY, 0)) != -1) {
- if ((fstat(fd, &st)) == -1) {
- purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
- file_private_key, g_strerror(errno));
- close(fd);
- return FALSE;
- }
- }
- /* This shouldn't really happen because silc_create_key_pair()
- * will set the permissions */
- else if ((g_stat(file_private_key, &st)) == -1) {
- purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
- file_private_key, g_strerror(errno));
- return FALSE;
- }
- } else {
- purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
- file_private_key, g_strerror(errno));
- return FALSE;
- }
- }
-
-#ifndef _WIN32
- /* Check the owner of the private key */
- if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
- purple_debug_error("silc", "You don't seem to own your private key!?\n");
- if (fd != -1)
- close(fd);
- return FALSE;
- }
-
- /* Check the permissions for the private key */
- if ((st.st_mode & 0777) != 0600) {
- purple_debug_warning("silc", "Wrong permissions in your private key file `%s'!\n"
- "Trying to change them ...\n", file_private_key);
- if ((fd == -1) || (fchmod(fd, S_IRUSR | S_IWUSR)) == -1) {
- purple_debug_error("silc",
- "Failed to change permissions for private key file!\n"
- "Permissions for your private key file must be 0600.\n");
- if (fd != -1)
- close(fd);
- return FALSE;
- }
- purple_debug_warning("silc", "Done.\n\n");
- }
-#endif
-
- if (fd != -1)
- close(fd);
-
- return TRUE;
-}
-
-#ifdef _WIN32
-struct passwd *getpwuid(uid_t uid) {
- struct passwd *pwd = calloc(1, sizeof(struct passwd));
- return pwd;
-}
-
-uid_t getuid() {
- return 0;
-}
-
-uid_t geteuid() {
- return 0;
-}
-#endif
-
-void silcpurple_show_public_key(SilcPurple sg,
- const char *name, SilcPublicKey public_key,
- GCallback callback, void *context)
-{
- SilcPublicKeyIdentifier ident;
- SilcPKCS pkcs;
- char *fingerprint, *babbleprint;
- unsigned char *pk;
- SilcUInt32 pk_len, key_len = 0;
- GString *s;
- char *buf;
-
- ident = silc_pkcs_decode_identifier(public_key->identifier);
- if (!ident)
- return;
-
- pk = silc_pkcs_public_key_encode(public_key, &pk_len);
- fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
- babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
-
- if (silc_pkcs_alloc((unsigned char *)public_key->name, &pkcs)) {
- key_len = silc_pkcs_public_key_set(pkcs, public_key);
- silc_pkcs_free(pkcs);
- }
-
- s = g_string_new("");
- if (ident->realname)
- /* Hint for translators: Please check the tabulator width here and in
- the next strings (short strings: 2 tabs, longer strings 1 tab,
- sum: 3 tabs or 24 characters) */
- g_string_append_printf(s, _("Real Name: \t%s\n"), ident->realname);
- if (ident->username)
- g_string_append_printf(s, _("User Name: \t%s\n"), ident->username);
- if (ident->email)
- g_string_append_printf(s, _("Email: \t\t%s\n"), ident->email);
- if (ident->host)
- g_string_append_printf(s, _("Host Name: \t%s\n"), ident->host);
- if (ident->org)
- g_string_append_printf(s, _("Organization: \t%s\n"), ident->org);
- if (ident->country)
- g_string_append_printf(s, _("Country: \t%s\n"), ident->country);
- g_string_append_printf(s, _("Algorithm: \t%s\n"), public_key->name);
- g_string_append_printf(s, _("Key Length: \t%d bits\n"), (int)key_len);
- g_string_append_printf(s, "\n");
- g_string_append_printf(s, _("Public Key Fingerprint:\n%s\n\n"), fingerprint);
- g_string_append_printf(s, _("Public Key Babbleprint:\n%s"), babbleprint);
-
- buf = g_string_free(s, FALSE);
-
- purple_request_action(sg->gc, _("Public Key Information"),
- _("Public Key Information"),
- buf, 0, purple_connection_get_account(sg->gc),
- NULL, NULL, context, 1, _("Close"), callback);
-
- g_free(buf);
- silc_free(fingerprint);
- silc_free(babbleprint);
- silc_free(pk);
- silc_pkcs_free_identifier(ident);
-}
-
-SilcAttributePayload
-silcpurple_get_attr(SilcDList attrs, SilcAttribute attribute)
-{
- SilcAttributePayload attr = NULL;
-
- if (!attrs)
- return NULL;
-
- silc_dlist_start(attrs);
- while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END)
- if (attribute == silc_attribute_get_attribute(attr))
- break;
-
- return attr;
-}
-
-void silcpurple_get_umode_string(SilcUInt32 mode, char *buf,
- SilcUInt32 buf_size)
-{
- memset(buf, 0, buf_size);
- if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
- (mode & SILC_UMODE_ROUTER_OPERATOR)) {
- strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
- "[server operator] " :
- (mode & SILC_UMODE_ROUTER_OPERATOR) ?
- "[SILC operator] " : "[unknown mode] ");
- }
- if (mode & SILC_UMODE_GONE)
- strcat(buf, "[away] ");
- if (mode & SILC_UMODE_INDISPOSED)
- strcat(buf, "[indisposed] ");
- if (mode & SILC_UMODE_BUSY)
- strcat(buf, "[busy] ");
- if (mode & SILC_UMODE_PAGE)
- strcat(buf, "[wake me up] ");
- if (mode & SILC_UMODE_HYPER)
- strcat(buf, "[hyperactive] ");
- if (mode & SILC_UMODE_ROBOT)
- strcat(buf, "[robot] ");
- if (mode & SILC_UMODE_ANONYMOUS)
- strcat(buf, "[anonymous] ");
- if (mode & SILC_UMODE_BLOCK_PRIVMSG)
- strcat(buf, "[blocks private messages] ");
- if (mode & SILC_UMODE_DETACHED)
- strcat(buf, "[detached] ");
- if (mode & SILC_UMODE_REJECT_WATCHING)
- strcat(buf, "[rejects watching] ");
- if (mode & SILC_UMODE_BLOCK_INVITE)
- strcat(buf, "[blocks invites] ");
- g_strchomp(buf);
-}
-
-void silcpurple_get_chmode_string(SilcUInt32 mode, char *buf,
- SilcUInt32 buf_size)
-{
- memset(buf, 0, buf_size);
- if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
- strcat(buf, "[permanent] ");
- if (mode & SILC_CHANNEL_MODE_PRIVATE)
- strcat(buf, "[private] ");
- if (mode & SILC_CHANNEL_MODE_SECRET)
- strcat(buf, "[secret] ");
- if (mode & SILC_CHANNEL_MODE_PRIVKEY)
- strcat(buf, "[private key] ");
- if (mode & SILC_CHANNEL_MODE_INVITE)
- strcat(buf, "[invite only] ");
- if (mode & SILC_CHANNEL_MODE_TOPIC)
- strcat(buf, "[topic restricted] ");
- if (mode & SILC_CHANNEL_MODE_ULIMIT)
- strcat(buf, "[user count limit] ");
- if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
- strcat(buf, "[passphrase auth] ");
- if (mode & SILC_CHANNEL_MODE_CHANNEL_AUTH)
- strcat(buf, "[public key auth] ");
- if (mode & SILC_CHANNEL_MODE_SILENCE_USERS)
- strcat(buf, "[users silenced] ");
- if (mode & SILC_CHANNEL_MODE_SILENCE_OPERS)
- strcat(buf, "[operators silenced] ");
- g_strchomp(buf);
-}
-
-void silcpurple_get_chumode_string(SilcUInt32 mode, char *buf,
- SilcUInt32 buf_size)
-{
- memset(buf, 0, buf_size);
- if (mode & SILC_CHANNEL_UMODE_CHANFO)
- strcat(buf, "[founder] ");
- if (mode & SILC_CHANNEL_UMODE_CHANOP)
- strcat(buf, "[operator] ");
- if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES)
- strcat(buf, "[blocks messages] ");
- if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS)
- strcat(buf, "[blocks user messages] ");
- if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS)
- strcat(buf, "[blocks robot messages] ");
- if (mode & SILC_CHANNEL_UMODE_QUIET)
- strcat(buf, "[quieted] ");
- g_strchomp(buf);
-}
-
-void
-silcpurple_parse_attrs(SilcDList attrs, char **moodstr, char **statusstr,
- char **contactstr, char **langstr, char **devicestr,
- char **tzstr, char **geostr)
-{
- SilcAttributePayload attr;
- SilcAttributeMood mood = 0;
- SilcAttributeContact contact;
- SilcAttributeObjDevice device;
- SilcAttributeObjGeo geo;
-
- char tmp[1024];
- GString *s;
-
- *moodstr = NULL;
- *statusstr = NULL;
- *contactstr = NULL;
- *langstr = NULL;
- *devicestr = NULL;
- *tzstr = NULL;
- *geostr = NULL;
-
- if (!attrs)
- return;
-
- s = g_string_new("");
- attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_STATUS_MOOD);
- if (attr && silc_attribute_get_object(attr, &mood, sizeof(mood))) {
- if (mood & SILC_ATTRIBUTE_MOOD_HAPPY)
- g_string_append_printf(s, "[%s] ", _("Happy"));
- if (mood & SILC_ATTRIBUTE_MOOD_SAD)
- g_string_append_printf(s, "[%s] ", _("Sad"));
- if (mood & SILC_ATTRIBUTE_MOOD_ANGRY)
- g_string_append_printf(s, "[%s] ", _("Angry"));
- if (mood & SILC_ATTRIBUTE_MOOD_JEALOUS)
- g_string_append_printf(s, "[%s] ", _("Jealous"));
- if (mood & SILC_ATTRIBUTE_MOOD_ASHAMED)
- g_string_append_printf(s, "[%s] ", _("Ashamed"));
- if (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE)
- g_string_append_printf(s, "[%s] ", _("Invincible"));
- if (mood & SILC_ATTRIBUTE_MOOD_INLOVE)
- g_string_append_printf(s, "[%s] ", _("In Love"));
- if (mood & SILC_ATTRIBUTE_MOOD_SLEEPY)
- g_string_append_printf(s, "[%s] ", _("Sleepy"));
- if (mood & SILC_ATTRIBUTE_MOOD_BORED)
- g_string_append_printf(s, "[%s] ", _("Bored"));
- if (mood & SILC_ATTRIBUTE_MOOD_EXCITED)
- g_string_append_printf(s, "[%s] ", _("Excited"));
- if (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS)
- g_string_append_printf(s, "[%s] ", _("Anxious"));
- }
- if (strlen(s->str)) {
- *moodstr = s->str;
- g_string_free(s, FALSE);
- g_strchomp(*moodstr);
- } else
- g_string_free(s, TRUE);
-
- attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_STATUS_FREETEXT);
- memset(tmp, 0, sizeof(tmp));
- if (attr && silc_attribute_get_object(attr, tmp, sizeof(tmp)))
- *statusstr = g_strdup(tmp);
-
- s = g_string_new("");
- attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_PREFERRED_CONTACT);
- if (attr && silc_attribute_get_object(attr, &contact, sizeof(contact))) {
- if (contact & SILC_ATTRIBUTE_CONTACT_CHAT)
- g_string_append_printf(s, "[%s] ", _("Chat"));
- if (contact & SILC_ATTRIBUTE_CONTACT_EMAIL)
- g_string_append_printf(s, "[%s] ", _("Email"));
- if (contact & SILC_ATTRIBUTE_CONTACT_CALL)
- g_string_append_printf(s, "[%s] ", _("Phone"));
- if (contact & SILC_ATTRIBUTE_CONTACT_PAGE)
- g_string_append_printf(s, "[%s] ", _("Paging"));
- if (contact & SILC_ATTRIBUTE_CONTACT_SMS)
- g_string_append_printf(s, "[%s] ", _("SMS"));
- if (contact & SILC_ATTRIBUTE_CONTACT_MMS)
- g_string_append_printf(s, "[%s] ", _("MMS"));
- if (contact & SILC_ATTRIBUTE_CONTACT_VIDEO)
- g_string_append_printf(s, "[%s] ", _("Video Conferencing"));
- }
- if (strlen(s->str)) {
- *contactstr = s->str;
- g_string_free(s, FALSE);
- g_strchomp(*contactstr);
- } else
- g_string_free(s, TRUE);
-
- attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_PREFERRED_LANGUAGE);
- memset(tmp, 0, sizeof(tmp));
- if (attr && silc_attribute_get_object(attr, tmp, sizeof(tmp)))
- *langstr = g_strdup(tmp);
-
- s = g_string_new("");
- attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_DEVICE_INFO);
- memset(&device, 0, sizeof(device));
- if (attr && silc_attribute_get_object(attr, &device, sizeof(device))) {
- if (device.type == SILC_ATTRIBUTE_DEVICE_COMPUTER)
- g_string_append_printf(s, "%s: ", _("Computer"));
- if (device.type == SILC_ATTRIBUTE_DEVICE_MOBILE_PHONE)
- g_string_append_printf(s, "%s: ", _("Mobile Phone"));
- if (device.type == SILC_ATTRIBUTE_DEVICE_PDA)
- g_string_append_printf(s, "%s: ", _("PDA"));
- if (device.type == SILC_ATTRIBUTE_DEVICE_TERMINAL)
- g_string_append_printf(s, "%s: ", _("Terminal"));
- g_string_append_printf(s, "%s %s %s %s",
- device.manufacturer ? device.manufacturer : "",
- device.version ? device.version : "",
- device.model ? device.model : "",
- device.language ? device.language : "");
- }
- if (strlen(s->str)) {
- *devicestr = s->str;
- g_string_free(s, FALSE);
- } else
- g_string_free(s, TRUE);
-
- attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_TIMEZONE);
- memset(tmp, 0, sizeof(tmp));
- if (attr && silc_attribute_get_object(attr, tmp, sizeof(tmp)))
- *tzstr = g_strdup(tmp);
-
- attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_GEOLOCATION);
- memset(&geo, 0, sizeof(geo));
- if (attr && silc_attribute_get_object(attr, &geo, sizeof(geo)))
- *geostr = g_strdup_printf("%s %s %s (%s)",
- geo.longitude ? geo.longitude : "",
- geo.latitude ? geo.latitude : "",
- geo.altitude ? geo.altitude : "",
- geo.accuracy ? geo.accuracy : "");
-}
-
-#ifdef HAVE_SILCMIME_H
-/* Returns MIME type of filetype */
-
-char *silcpurple_file2mime(const char *filename)
-{
- const char *ct;
-
- ct = strrchr(filename, '.');
- if (!ct)
- return NULL;
- else if (!g_ascii_strcasecmp(".png", ct))
- return strdup("image/png");
- else if (!g_ascii_strcasecmp(".jpg", ct))
- return strdup("image/jpeg");
- else if (!g_ascii_strcasecmp(".jpeg", ct))
- return strdup("image/jpeg");
- else if (!g_ascii_strcasecmp(".gif", ct))
- return strdup("image/gif");
- else if (!g_ascii_strcasecmp(".tiff", ct))
- return strdup("image/tiff");
-
- return NULL;
-}
-
-/* Checks if message has images, and assembles MIME message if it has.
- If only one image is present, creates simple MIME image message. If
- there are multiple images and/or text with images multipart MIME
- message is created. */
-
-SilcDList silcpurple_image_message(const char *msg, SilcUInt32 *mflags)
-{
- SilcMime mime = NULL, p;
- SilcDList list, parts = NULL;
- const char *start, *end, *last;
- GData *attribs;
- char *type;
- gboolean images = FALSE;
-
- last = msg;
- while (last && *last && purple_markup_find_tag("img", last, &start,
- &end, &attribs)) {
- PurpleStoredImage *image = NULL;
- const char *id;
-
- /* Check if there is text before image */
- if (start - last) {
- char *text, *tmp;
- p = silc_mime_alloc();
-
- /* Add content type */
- silc_mime_add_field(p, "Content-Type",
- "text/plain; charset=utf-8");
-
- tmp = g_strndup(last, start - last);
- text = purple_unescape_html(tmp);
- g_free(tmp);
- /* Add text */
- silc_mime_add_data(p, (unsigned char *)text, strlen(text));
- g_free(text);
-
- if (!parts)
- parts = silc_dlist_init();
- silc_dlist_add(parts, p);
- }
-
- id = g_datalist_get_data(&attribs, "id");
- if (id && (image = purple_imgstore_find_by_id(atoi(id)))) {
- unsigned long imglen = purple_imgstore_get_size(image);
- gconstpointer img = purple_imgstore_get_data(image);
-
- p = silc_mime_alloc();
-
- /* Add content type */
- type = silcpurple_file2mime(purple_imgstore_get_filename(image));
- if (!type) {
- g_datalist_clear(&attribs);
- last = end + 1;
- continue;
- }
- silc_mime_add_field(p, "Content-Type", type);
- silc_free(type);
-
- /* Add content transfer encoding */
- silc_mime_add_field(p, "Content-Transfer-Encoding", "binary");
-
- /* Add image data */
- silc_mime_add_data(p, img, imglen);
-
- if (!parts)
- parts = silc_dlist_init();
- silc_dlist_add(parts, p);
- images = TRUE;
- }
-
- g_datalist_clear(&attribs);
-
- /* Continue after tag */
- last = end + 1;
- }
-
- /* Check for text after the image(s) */
- if (images && last && *last) {
- char *tmp = purple_unescape_html(last);
- p = silc_mime_alloc();
-
- /* Add content type */
- silc_mime_add_field(p, "Content-Type",
- "text/plain; charset=utf-8");
-
- /* Add text */
- silc_mime_add_data(p, (unsigned char *)tmp, strlen(tmp));
- g_free(tmp);
-
- if (!parts)
- parts = silc_dlist_init();
- silc_dlist_add(parts, p);
- }
-
- /* If there weren't any images, don't return anything. */
- if (!images) {
- if (parts)
- silc_dlist_uninit(parts);
- return NULL;
- }
-
- if (silc_dlist_count(parts) > 1) {
- /* Multipart MIME message */
- char b[32];
- mime = silc_mime_alloc();
- silc_mime_add_field(mime, "MIME-Version", "1.0");
- g_snprintf(b, sizeof(b), "b%4X%4X",
- (unsigned int)time(NULL),
- silc_dlist_count(parts));
- silc_mime_set_multipart(mime, "mixed", b);
- silc_dlist_start(parts);
- while ((p = silc_dlist_get(parts)) != SILC_LIST_END)
- silc_mime_add_multipart(mime, p);
- } else {
- /* Simple MIME message */
- silc_dlist_start(parts);
- mime = silc_dlist_get(parts);
- silc_mime_add_field(mime, "MIME-Version", "1.0");
- }
-
- *mflags &= ~SILC_MESSAGE_FLAG_UTF8;
- *mflags |= SILC_MESSAGE_FLAG_DATA;
-
- /* Encode message. Fragment if it is too large */
- list = silc_mime_encode_partial(mime, 0xfc00);
-
- silc_dlist_uninit(parts);
-
- /* Added multiparts gets freed here */
- silc_mime_free(mime);
-
- return list;
-}
-
-#endif /* HAVE_SILCMIME_H */
diff --git a/libpurple/protocols/silc10/wb.c b/libpurple/protocols/silc10/wb.c
deleted file mode 100644
index 76afd1d2ec..0000000000
--- a/libpurple/protocols/silc10/wb.c
+++ /dev/null
@@ -1,520 +0,0 @@
-/*
-
- wb.c
-
- Author: Pekka Riikonen <priikone@silcnet.org>
-
- Copyright (C) 2005 Pekka Riikonen
-
- 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; version 2 of the License.
-
- 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.
-
-*/
-
-#include "silcincludes.h"
-#include "silcclient.h"
-#include "silcpurple.h"
-#include "wb.h"
-
-/*
- SILC Whiteboard packet:
-
- 1 byte command
- 2 bytes width
- 2 bytes height
- 4 bytes brush color
- 2 bytes brush size
- n bytes data
-
- Data:
-
- 4 bytes x
- 4 bytes y
-
- Commands:
-
- 0x01 draw
- 0x02 clear
-
- MIME:
-
- MIME-Version: 1.0
- Content-Type: application/x-wb
- Content-Transfer-Encoding: binary
-
-*/
-
-#define SILCPURPLE_WB_MIME "MIME-Version: 1.0\r\nContent-Type: application/x-wb\r\nContent-Transfer-Encoding: binary\r\n\r\n"
-#define SILCPURPLE_WB_HEADER strlen(SILCPURPLE_WB_MIME) + 11
-
-#define SILCPURPLE_WB_WIDTH 500
-#define SILCPURPLE_WB_HEIGHT 400
-#define SILCPURPLE_WB_WIDTH_MAX 1024
-#define SILCPURPLE_WB_HEIGHT_MAX 1024
-
-/* Commands */
-typedef enum {
- SILCPURPLE_WB_DRAW = 0x01,
- SILCPURPLE_WB_CLEAR = 0x02
-} SilcPurpleWbCommand;
-
-/* Brush size */
-typedef enum {
- SILCPURPLE_WB_BRUSH_SMALL = 2,
- SILCPURPLE_WB_BRUSH_MEDIUM = 5,
- SILCPURPLE_WB_BRUSH_LARGE = 10
-} SilcPurpleWbBrushSize;
-
-/* Brush color (XXX Purple should provide default colors) */
-typedef enum {
- SILCPURPLE_WB_COLOR_BLACK = 0,
- SILCPURPLE_WB_COLOR_RED = 13369344,
- SILCPURPLE_WB_COLOR_GREEN = 52224,
- SILCPURPLE_WB_COLOR_BLUE = 204,
- SILCPURPLE_WB_COLOR_YELLOW = 15658496,
- SILCPURPLE_WB_COLOR_ORANGE = 16737792,
- SILCPURPLE_WB_COLOR_CYAN = 52428,
- SILCPURPLE_WB_COLOR_VIOLET = 5381277,
- SILCPURPLE_WB_COLOR_PURPLE = 13369548,
- SILCPURPLE_WB_COLOR_TAN = 12093547,
- SILCPURPLE_WB_COLOR_BROWN = 5256485,
- SILCPURPLE_WB_COLOR_GREY = 11184810,
- SILCPURPLE_WB_COLOR_WHITE = 16777215
-} SilcPurpleWbColor;
-
-typedef struct {
- int type; /* 0 = buddy, 1 = channel */
- union {
- SilcClientEntry client;
- SilcChannelEntry channel;
- } u;
- int width;
- int height;
- int brush_size;
- int brush_color;
-} *SilcPurpleWb;
-
-/* Initialize whiteboard */
-
-PurpleWhiteboard *silcpurple_wb_init(SilcPurple sg, SilcClientEntry client_entry)
-{
- SilcClientConnection conn;
- PurpleWhiteboard *wb;
- SilcPurpleWb wbs;
-
- conn = sg->conn;
- wb = purple_whiteboard_get_session(sg->account, client_entry->nickname);
- if (!wb)
- wb = purple_whiteboard_create(sg->account, client_entry->nickname, 0);
- if (!wb)
- return NULL;
-
- if (!wb->proto_data) {
- wbs = silc_calloc(1, sizeof(*wbs));
- if (!wbs)
- return NULL;
- wbs->type = 0;
- wbs->u.client = client_entry;
- wbs->width = SILCPURPLE_WB_WIDTH;
- wbs->height = SILCPURPLE_WB_HEIGHT;
- wbs->brush_size = SILCPURPLE_WB_BRUSH_SMALL;
- wbs->brush_color = SILCPURPLE_WB_COLOR_BLACK;
- wb->proto_data = wbs;
-
- /* Start the whiteboard */
- purple_whiteboard_start(wb);
- purple_whiteboard_clear(wb);
- }
-
- return wb;
-}
-
-PurpleWhiteboard *silcpurple_wb_init_ch(SilcPurple sg, SilcChannelEntry channel)
-{
- PurpleWhiteboard *wb;
- SilcPurpleWb wbs;
-
- wb = purple_whiteboard_get_session(sg->account, channel->channel_name);
- if (!wb)
- wb = purple_whiteboard_create(sg->account, channel->channel_name, 0);
- if (!wb)
- return NULL;
-
- if (!wb->proto_data) {
- wbs = silc_calloc(1, sizeof(*wbs));
- if (!wbs)
- return NULL;
- wbs->type = 1;
- wbs->u.channel = channel;
- wbs->width = SILCPURPLE_WB_WIDTH;
- wbs->height = SILCPURPLE_WB_HEIGHT;
- wbs->brush_size = SILCPURPLE_WB_BRUSH_SMALL;
- wbs->brush_color = SILCPURPLE_WB_COLOR_BLACK;
- wb->proto_data = wbs;
-
- /* Start the whiteboard */
- purple_whiteboard_start(wb);
- purple_whiteboard_clear(wb);
- }
-
- return wb;
-}
-
-static void
-silcpurple_wb_parse(SilcPurpleWb wbs, PurpleWhiteboard *wb,
- unsigned char *message, SilcUInt32 message_len)
-{
- SilcUInt8 command;
- SilcUInt16 width, height, brush_size;
- SilcUInt32 brush_color, x, y, dx, dy;
- SilcBufferStruct buf;
- int ret;
-
- /* Parse the packet */
- silc_buffer_set(&buf, message, message_len);
- ret = silc_buffer_unformat(&buf,
- SILC_STR_UI_CHAR(&command),
- SILC_STR_UI_SHORT(&width),
- SILC_STR_UI_SHORT(&height),
- SILC_STR_UI_INT(&brush_color),
- SILC_STR_UI_SHORT(&brush_size),
- SILC_STR_END);
- if (ret < 0)
- return;
- silc_buffer_pull(&buf, ret);
-
- /* Update whiteboard if its dimensions changed */
- if (width != wbs->width || height != wbs->height)
- silcpurple_wb_set_dimensions(wb, width, height);
-
- if (command == SILCPURPLE_WB_DRAW) {
- /* Parse data and draw it */
- ret = silc_buffer_unformat(&buf,
- SILC_STR_UI_INT(&dx),
- SILC_STR_UI_INT(&dy),
- SILC_STR_END);
- if (ret < 0)
- return;
- silc_buffer_pull(&buf, 8);
- x = dx;
- y = dy;
- while (buf.len > 0) {
- ret = silc_buffer_unformat(&buf,
- SILC_STR_UI_INT(&dx),
- SILC_STR_UI_INT(&dy),
- SILC_STR_END);
- if (ret < 0)
- return;
- silc_buffer_pull(&buf, 8);
-
- purple_whiteboard_draw_line(wb, x, y, x + dx, y + dy,
- brush_color, brush_size);
- x += dx;
- y += dy;
- }
- }
-
- if (command == SILCPURPLE_WB_CLEAR)
- purple_whiteboard_clear(wb);
-}
-
-typedef struct {
- unsigned char *message;
- SilcUInt32 message_len;
- SilcPurple sg;
- SilcClientEntry sender;
- SilcChannelEntry channel;
-} *SilcPurpleWbRequest;
-
-static void
-silcpurple_wb_request_cb(SilcPurpleWbRequest req, gint id)
-{
- PurpleWhiteboard *wb;
-
- if (id != 1)
- goto out;
-
- if (!req->channel)
- wb = silcpurple_wb_init(req->sg, req->sender);
- else
- wb = silcpurple_wb_init_ch(req->sg, req->channel);
-
- silcpurple_wb_parse(wb->proto_data, wb, req->message, req->message_len);
-
- out:
- silc_free(req->message);
- silc_free(req);
-}
-
-static void
-silcpurple_wb_request(SilcClient client, const unsigned char *message,
- SilcUInt32 message_len, SilcClientEntry sender,
- SilcChannelEntry channel)
-{
- char tmp[128];
- SilcPurpleWbRequest req;
- PurpleConnection *gc;
- SilcPurple sg;
-
- gc = client->application;
- sg = gc->proto_data;
-
- /* Open whiteboard automatically if requested */
- if (purple_account_get_bool(sg->account, "open-wb", FALSE)) {
- PurpleWhiteboard *wb;
-
- if (!channel)
- wb = silcpurple_wb_init(sg, sender);
- else
- wb = silcpurple_wb_init_ch(sg, channel);
-
- silcpurple_wb_parse(wb->proto_data, wb, (unsigned char *)message,
- message_len);
- return;
- }
-
- /* Close any previous unaccepted requests */
- purple_request_close_with_handle(sender);
-
- if (!channel) {
- g_snprintf(tmp, sizeof(tmp),
- _("%s sent message to whiteboard. Would you like "
- "to open the whiteboard?"), sender->nickname);
- } else {
- g_snprintf(tmp, sizeof(tmp),
- _("%s sent message to whiteboard on %s channel. "
- "Would you like to open the whiteboard?"),
- sender->nickname, channel->channel_name);
- }
-
- req = silc_calloc(1, sizeof(*req));
- if (!req)
- return;
- req->message = silc_memdup(message, message_len);
- req->message_len = message_len;
- req->sender = sender;
- req->channel = channel;
- req->sg = sg;
-
- purple_request_action(sender, _("Whiteboard"), tmp, NULL, 1,
- sg->account, sender->nickname, NULL, req, 2,
- _("Yes"), G_CALLBACK(silcpurple_wb_request_cb),
- _("No"), G_CALLBACK(silcpurple_wb_request_cb));
-}
-
-/* Process incoming whiteboard message */
-
-void silcpurple_wb_receive(SilcClient client, SilcClientConnection conn,
- SilcClientEntry sender, SilcMessagePayload payload,
- SilcMessageFlags flags, const unsigned char *message,
- SilcUInt32 message_len)
-{
- SilcPurple sg;
- PurpleConnection *gc;
- PurpleWhiteboard *wb;
- SilcPurpleWb wbs;
-
- gc = client->application;
- sg = gc->proto_data;
-
- wb = purple_whiteboard_get_session(sg->account, sender->nickname);
- if (!wb) {
- /* Ask user if they want to open the whiteboard */
- silcpurple_wb_request(client, message, message_len,
- sender, NULL);
- return;
- }
-
- wbs = wb->proto_data;
- silcpurple_wb_parse(wbs, wb, (unsigned char *)message, message_len);
-}
-
-/* Process incoming whiteboard message on channel */
-
-void silcpurple_wb_receive_ch(SilcClient client, SilcClientConnection conn,
- SilcClientEntry sender, SilcChannelEntry channel,
- SilcMessagePayload payload,
- SilcMessageFlags flags,
- const unsigned char *message,
- SilcUInt32 message_len)
-{
- SilcPurple sg;
- PurpleConnection *gc;
- PurpleWhiteboard *wb;
- SilcPurpleWb wbs;
-
- gc = client->application;
- sg = gc->proto_data;
-
- wb = purple_whiteboard_get_session(sg->account, channel->channel_name);
- if (!wb) {
- /* Ask user if they want to open the whiteboard */
- silcpurple_wb_request(client, message, message_len,
- sender, channel);
- return;
- }
-
- wbs = wb->proto_data;
- silcpurple_wb_parse(wbs, wb, (unsigned char *)message, message_len);
-}
-
-/* Send whiteboard message */
-
-void silcpurple_wb_send(PurpleWhiteboard *wb, GList *draw_list)
-{
- SilcPurpleWb wbs = wb->proto_data;
- SilcBuffer packet;
- GList *list;
- int len;
- PurpleConnection *gc;
- SilcPurple sg;
-
- g_return_if_fail(draw_list);
- gc = purple_account_get_connection(wb->account);
- g_return_if_fail(gc);
- sg = gc->proto_data;
- g_return_if_fail(sg);
-
- len = SILCPURPLE_WB_HEADER;
- for (list = draw_list; list; list = list->next)
- len += 4;
-
- packet = silc_buffer_alloc_size(len);
- if (!packet)
- return;
-
- /* Assmeble packet */
- silc_buffer_format(packet,
- SILC_STR_UI32_STRING(SILCPURPLE_WB_MIME),
- SILC_STR_UI_CHAR(SILCPURPLE_WB_DRAW),
- SILC_STR_UI_SHORT(wbs->width),
- SILC_STR_UI_SHORT(wbs->height),
- SILC_STR_UI_INT(wbs->brush_color),
- SILC_STR_UI_SHORT(wbs->brush_size),
- SILC_STR_END);
- silc_buffer_pull(packet, SILCPURPLE_WB_HEADER);
- for (list = draw_list; list; list = list->next) {
- silc_buffer_format(packet,
- SILC_STR_UI_INT(GPOINTER_TO_INT(list->data)),
- SILC_STR_END);
- silc_buffer_pull(packet, 4);
- }
-
- /* Send the message */
- if (wbs->type == 0) {
- /* Private message */
- silc_client_send_private_message(sg->client, sg->conn,
- wbs->u.client,
- SILC_MESSAGE_FLAG_DATA,
- packet->head, len, TRUE);
- } else if (wbs->type == 1) {
- /* Channel message. Channel private keys are not supported. */
- silc_client_send_channel_message(sg->client, sg->conn,
- wbs->u.channel, NULL,
- SILC_MESSAGE_FLAG_DATA,
- packet->head, len, TRUE);
- }
-
- silc_buffer_free(packet);
-}
-
-/* Purple Whiteboard operations */
-
-void silcpurple_wb_start(PurpleWhiteboard *wb)
-{
- /* Nothing here. Everything is in initialization */
-}
-
-void silcpurple_wb_end(PurpleWhiteboard *wb)
-{
- silc_free(wb->proto_data);
- wb->proto_data = NULL;
-}
-
-void silcpurple_wb_get_dimensions(const PurpleWhiteboard *wb, int *width, int *height)
-{
- SilcPurpleWb wbs = wb->proto_data;
- *width = wbs->width;
- *height = wbs->height;
-}
-
-void silcpurple_wb_set_dimensions(PurpleWhiteboard *wb, int width, int height)
-{
- SilcPurpleWb wbs = wb->proto_data;
- wbs->width = width > SILCPURPLE_WB_WIDTH_MAX ? SILCPURPLE_WB_WIDTH_MAX :
- width;
- wbs->height = height > SILCPURPLE_WB_HEIGHT_MAX ? SILCPURPLE_WB_HEIGHT_MAX :
- height;
-
- /* Update whiteboard */
- purple_whiteboard_set_dimensions(wb, wbs->width, wbs->height);
-}
-
-void silcpurple_wb_get_brush(const PurpleWhiteboard *wb, int *size, int *color)
-{
- SilcPurpleWb wbs = wb->proto_data;
- *size = wbs->brush_size;
- *color = wbs->brush_color;
-}
-
-void silcpurple_wb_set_brush(PurpleWhiteboard *wb, int size, int color)
-{
- SilcPurpleWb wbs = wb->proto_data;
- wbs->brush_size = size;
- wbs->brush_color = color;
-
- /* Update whiteboard */
- purple_whiteboard_set_brush(wb, size, color);
-}
-
-void silcpurple_wb_clear(PurpleWhiteboard *wb)
-{
- SilcPurpleWb wbs = wb->proto_data;
- SilcBuffer packet;
- int len;
- PurpleConnection *gc;
- SilcPurple sg;
-
- gc = purple_account_get_connection(wb->account);
- g_return_if_fail(gc);
- sg = gc->proto_data;
- g_return_if_fail(sg);
-
- len = SILCPURPLE_WB_HEADER;
- packet = silc_buffer_alloc_size(len);
- if (!packet)
- return;
-
- /* Assmeble packet */
- silc_buffer_format(packet,
- SILC_STR_UI32_STRING(SILCPURPLE_WB_MIME),
- SILC_STR_UI_CHAR(SILCPURPLE_WB_CLEAR),
- SILC_STR_UI_SHORT(wbs->width),
- SILC_STR_UI_SHORT(wbs->height),
- SILC_STR_UI_INT(wbs->brush_color),
- SILC_STR_UI_SHORT(wbs->brush_size),
- SILC_STR_END);
-
- /* Send the message */
- if (wbs->type == 0) {
- /* Private message */
- silc_client_send_private_message(sg->client, sg->conn,
- wbs->u.client,
- SILC_MESSAGE_FLAG_DATA,
- packet->head, len, TRUE);
- } else if (wbs->type == 1) {
- /* Channel message */
- silc_client_send_channel_message(sg->client, sg->conn,
- wbs->u.channel, NULL,
- SILC_MESSAGE_FLAG_DATA,
- packet->head, len, TRUE);
- }
-
- silc_buffer_free(packet);
-}
diff --git a/libpurple/protocols/silc10/wb.h b/libpurple/protocols/silc10/wb.h
deleted file mode 100644
index 5ad0bf6fdd..0000000000
--- a/libpurple/protocols/silc10/wb.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
-
- silcpurple.h
-
- Author: Pekka Riikonen <priikone@silcnet.org>
-
- Copyright (C) 2005 Pekka Riikonen
-
- 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; version 2 of the License.
-
- 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.
-
-*/
-
-#ifndef SILCPURPLE_WB_H
-#define SILCPURPLE_WB_H
-
-#include "silcpurple.h"
-#include "whiteboard.h"
-
-PurpleWhiteboard *
-silcpurple_wb_init(SilcPurple sg, SilcClientEntry client_entry);
-PurpleWhiteboard *
-silcpurple_wb_init_ch(SilcPurple sg, SilcChannelEntry channel);
-void silcpurple_wb_receive(SilcClient client, SilcClientConnection conn,
- SilcClientEntry sender, SilcMessagePayload payload,
- SilcMessageFlags flags, const unsigned char *message,
- SilcUInt32 message_len);
-void silcpurple_wb_receive_ch(SilcClient client, SilcClientConnection conn,
- SilcClientEntry sender, SilcChannelEntry channel,
- SilcMessagePayload payload,
- SilcMessageFlags flags,
- const unsigned char *message,
- SilcUInt32 message_len);
-void silcpurple_wb_start(PurpleWhiteboard *wb);
-void silcpurple_wb_end(PurpleWhiteboard *wb);
-void silcpurple_wb_get_dimensions(const PurpleWhiteboard *wb, int *width, int *height);
-void silcpurple_wb_set_dimensions(PurpleWhiteboard *wb, int width, int height);
-void silcpurple_wb_get_brush(const PurpleWhiteboard *wb, int *size, int *color);
-void silcpurple_wb_set_brush(PurpleWhiteboard *wb, int size, int color);
-void silcpurple_wb_send(PurpleWhiteboard *wb, GList *draw_list);
-void silcpurple_wb_clear(PurpleWhiteboard *wb);
-
-#endif /* SILCPURPLE_WB_H */
diff --git a/libpurple/protocols/simple/Makefile.am b/libpurple/protocols/simple/Makefile.am
index 94b21d01e3..666feae4cf 100644
--- a/libpurple/protocols/simple/Makefile.am
+++ b/libpurple/protocols/simple/Makefile.am
@@ -1,7 +1,7 @@
EXTRA_DIST = \
Makefile.mingw
-pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+pkgdir = @PURPLE_PLUGINDIR@
SIMPLESOURCES = \
simple.c \
@@ -11,7 +11,7 @@ SIMPLESOURCES = \
AM_CFLAGS = $(st)
-libsimple_la_LDFLAGS = -module -avoid-version
+libsimple_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
if STATIC_SIMPLE
@@ -25,7 +25,7 @@ else
st =
pkg_LTLIBRARIES = libsimple.la
libsimple_la_SOURCES = $(SIMPLESOURCES)
-libsimple_la_LIBADD = $(GLIB_LIBS)
+libsimple_la_LIBADD = @PURPLE_LIBS@
endif
diff --git a/libpurple/protocols/simple/Makefile.mingw b/libpurple/protocols/simple/Makefile.mingw
index 5af7224ead..8396e63287 100644
--- a/libpurple/protocols/simple/Makefile.mingw
+++ b/libpurple/protocols/simple/Makefile.mingw
@@ -47,6 +47,7 @@ OBJECTS = $(C_SRC:%.c=%.o)
##
LIBS = \
-lglib-2.0 \
+ -lgobject-2.0 \
-lws2_32 \
-lintl \
-lpurple
diff --git a/libpurple/protocols/simple/simple.c b/libpurple/protocols/simple/simple.c
index 516679d6d5..e1cba78a3b 100644
--- a/libpurple/protocols/simple/simple.c
+++ b/libpurple/protocols/simple/simple.c
@@ -27,12 +27,11 @@
#include "internal.h"
#include "accountopt.h"
-#include "blist.h"
+#include "buddylist.h"
#include "conversation.h"
#include "dnsquery.h"
#include "debug.h"
#include "notify.h"
-#include "privacy.h"
#include "prpl.h"
#include "plugin.h"
#include "util.h"
@@ -67,7 +66,7 @@ static const char *simple_list_icon(PurpleAccount *a, PurpleBuddy *b) {
}
static void simple_keep_alive(PurpleConnection *gc) {
- struct simple_account_data *sip = gc->proto_data;
+ struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
if(sip->udp) { /* in case of UDP send a packet only with a 0 byte to
remain in the NAT table */
gchar buf[2] = {0, 0};
@@ -105,14 +104,15 @@ static void do_notifies(struct simple_account_data *sip) {
}
static void simple_set_status(PurpleAccount *account, PurpleStatus *status) {
- PurpleStatusPrimitive primitive = purple_status_type_get_primitive(purple_status_get_type(status));
+ PurpleConnection *gc = purple_account_get_connection(account);
+ PurpleStatusPrimitive primitive = purple_status_type_get_primitive(purple_status_get_status_type(status));
struct simple_account_data *sip = NULL;
if (!purple_status_is_active(status))
return;
- if (account->gc)
- sip = account->gc->proto_data;
+ if (gc)
+ sip = purple_connection_get_protocol_data(gc);
if (sip)
{
@@ -197,14 +197,14 @@ static void connection_free_all(struct simple_account_data *sip) {
}
}
-static void simple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
+static void simple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *message)
{
- struct simple_account_data *sip = (struct simple_account_data *)gc->proto_data;
+ struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
struct simple_buddy *b;
const char *name = purple_buddy_get_name(buddy);
if(strncmp(name, "sip:", 4)) {
gchar *buf = g_strdup_printf("sip:%s", name);
- purple_blist_rename_buddy(buddy, buf);
+ purple_buddy_set_name(buddy, buf);
g_free(buf);
}
if(!g_hash_table_lookup(sip->buddies, name)) {
@@ -224,10 +224,10 @@ static void simple_get_buddies(PurpleConnection *gc) {
purple_debug_info("simple", "simple_get_buddies\n");
account = purple_connection_get_account(gc);
- buddies = purple_find_buddies(account, NULL);
+ buddies = purple_blist_find_buddies(account, NULL);
while (buddies) {
PurpleBuddy *buddy = buddies->data;
- simple_add_buddy(gc, buddy, purple_buddy_get_group(buddy));
+ simple_add_buddy(gc, buddy, purple_buddy_get_group(buddy), NULL);
buddies = g_slist_delete_link(buddies, buddies);
}
@@ -236,7 +236,7 @@ static void simple_get_buddies(PurpleConnection *gc) {
static void simple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
{
const char *name = purple_buddy_get_name(buddy);
- struct simple_account_data *sip = (struct simple_account_data *)gc->proto_data;
+ struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
struct simple_buddy *b = g_hash_table_lookup(sip->buddies, name);
g_hash_table_remove(sip->buddies, name);
g_free(b->name);
@@ -249,7 +249,7 @@ static GList *simple_status_types(PurpleAccount *acc) {
type = purple_status_type_new_with_attrs(
PURPLE_STATUS_AVAILABLE, NULL, NULL, TRUE, TRUE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_append(types, type);
@@ -278,7 +278,7 @@ static gchar *auth_header(struct simple_account_data *sip,
if(auth->type == 1) { /* Digest */
sprintf(noncecount, "%08d", auth->nc++);
- response = purple_cipher_http_digest_calculate_response(
+ response = purple_http_digest_calculate_response(
"md5", method, target, NULL, NULL,
auth->nonce, noncecount, NULL, auth->digest_session_key);
purple_debug(PURPLE_DEBUG_MISC, "simple", "response %s\n", response);
@@ -299,7 +299,7 @@ static gchar *auth_header(struct simple_account_data *sip,
}
sprintf(noncecount, "%08d", auth->nc++);
- response = purple_cipher_http_digest_calculate_response(
+ response = purple_http_digest_calculate_response(
"md5", method, target, NULL, NULL,
auth->nonce, noncecount, NULL, auth->digest_session_key);
purple_debug(PURPLE_DEBUG_MISC, "simple", "response %s\n", response);
@@ -403,7 +403,7 @@ static void fill_auth(struct simple_account_data *sip, const gchar *hdr, struct
auth->realm ? auth->realm : "(null)");
if(auth->realm) {
- auth->digest_session_key = purple_cipher_http_digest_calculate_session_key(
+ auth->digest_session_key = purple_http_digest_calculate_session_key(
"md5", authuser, auth->realm, sip->password, auth->nonce, NULL);
auth->nc = 1;
@@ -417,11 +417,12 @@ static void fill_auth(struct simple_account_data *sip, const gchar *hdr, struct
static void simple_canwrite_cb(gpointer data, gint source, PurpleInputCondition cond) {
PurpleConnection *gc = data;
- struct simple_account_data *sip = gc->proto_data;
+ struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
gsize max_write;
gssize written;
+ const gchar *output = NULL;
- max_write = purple_circ_buffer_get_max_read(sip->txbuf);
+ max_write = purple_circular_buffer_get_max_read(sip->txbuf);
if(max_write == 0) {
purple_input_remove(sip->tx_handler);
@@ -429,7 +430,9 @@ static void simple_canwrite_cb(gpointer data, gint source, PurpleInputCondition
return;
}
- written = write(sip->fd, sip->txbuf->outptr, max_write);
+ output = purple_circular_buffer_get_output(sip->txbuf);
+
+ written = write(sip->fd, output, max_write);
if(written < 0 && errno == EAGAIN)
written = 0;
@@ -437,13 +440,13 @@ static void simple_canwrite_cb(gpointer data, gint source, PurpleInputCondition
/*TODO: do we really want to disconnect on a failure to write?*/
gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
g_strerror(errno));
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
g_free(tmp);
return;
}
- purple_circ_buffer_mark_read(sip->txbuf, written);
+ purple_circular_buffer_mark_read(sip->txbuf, written);
}
static void simple_input_cb(gpointer data, gint source, PurpleInputCondition cond);
@@ -456,20 +459,20 @@ static void send_later_cb(gpointer data, gint source, const gchar *error_message
if(source < 0) {
gchar *tmp = g_strdup_printf(_("Unable to connect: %s"),
error_message);
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
g_free(tmp);
return;
}
- sip = gc->proto_data;
+ sip = purple_connection_get_protocol_data(gc);
sip->fd = source;
sip->connecting = FALSE;
simple_canwrite_cb(gc, sip->fd, PURPLE_INPUT_WRITE);
/* If there is more to write now, we need to register a handler */
- if(sip->txbuf->bufused > 0)
+ if(purple_circular_buffer_get_used(sip->txbuf) > 0)
sip->tx_handler = purple_input_add(sip->fd, PURPLE_INPUT_WRITE,
simple_canwrite_cb, gc);
@@ -479,24 +482,24 @@ static void send_later_cb(gpointer data, gint source, const gchar *error_message
static void sendlater(PurpleConnection *gc, const char *buf) {
- struct simple_account_data *sip = gc->proto_data;
+ struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
if(!sip->connecting) {
purple_debug_info("simple", "connecting to %s port %d\n", sip->realhostname ? sip->realhostname : "{NULL}", sip->realport);
if (purple_proxy_connect(gc, sip->account, sip->realhostname, sip->realport, send_later_cb, gc) == NULL) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to connect"));
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to connect"));
}
sip->connecting = TRUE;
}
- if(purple_circ_buffer_get_max_read(sip->txbuf) > 0)
- purple_circ_buffer_append(sip->txbuf, "\r\n", 2);
+ if(purple_circular_buffer_get_max_read(sip->txbuf) > 0)
+ purple_circular_buffer_append(sip->txbuf, "\r\n", 2);
- purple_circ_buffer_append(sip->txbuf, buf, strlen(buf));
+ purple_circular_buffer_append(sip->txbuf, buf, strlen(buf));
}
static void sendout_pkt(PurpleConnection *gc, const char *buf) {
- struct simple_account_data *sip = gc->proto_data;
+ struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
time_t currtime = time(NULL);
int writelen = strlen(buf);
@@ -533,10 +536,10 @@ static void sendout_pkt(PurpleConnection *gc, const char *buf) {
/* XXX: is it OK to do this? You might get part of a request sent
with part of another. */
- if(sip->txbuf->bufused > 0)
- purple_circ_buffer_append(sip->txbuf, "\r\n", 2);
+ if(purple_circular_buffer_get_used(sip->txbuf) > 0)
+ purple_circular_buffer_append(sip->txbuf, "\r\n", 2);
- purple_circ_buffer_append(sip->txbuf, buf + ret,
+ purple_circular_buffer_append(sip->txbuf, buf + ret,
writelen - ret);
}
}
@@ -634,7 +637,7 @@ static struct transaction *transactions_find(struct simple_account_data *sip, st
static void send_sip_request(PurpleConnection *gc, const gchar *method,
const gchar *url, const gchar *to, const gchar *addheaders,
const gchar *body, struct sip_dialog *dialog, TransCallback tc) {
- struct simple_account_data *sip = gc->proto_data;
+ struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
char *callid = dialog ? g_strdup(dialog->callid) : gencallid();
char *auth = NULL;
const char *addh = "";
@@ -874,7 +877,7 @@ static void simple_unsubscribe(char *name, struct simple_buddy *buddy, struct si
static gboolean simple_add_lcs_contacts(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc) {
const gchar *tmp;
- xmlnode *item, *group, *isc;
+ PurpleXmlNode *item, *group, *isc;
const char *name_group;
PurpleBuddy *b;
PurpleGroup *g = NULL;
@@ -887,47 +890,44 @@ static gboolean simple_add_lcs_contacts(struct simple_account_data *sip, struct
purple_debug_info("simple", "simple_add_lcs_contacts->%s-%d\n", msg->body, len);
/*Convert the contact from XML to Purple Buddies*/
- isc = xmlnode_from_str(msg->body, len);
+ isc = purple_xmlnode_from_str(msg->body, len);
/* ToDo. Find for all groups */
- if ((group = xmlnode_get_child(isc, "group"))) {
- name_group = xmlnode_get_attrib(group, "name");
+ if ((group = purple_xmlnode_get_child(isc, "group"))) {
+ name_group = purple_xmlnode_get_attrib(group, "name");
purple_debug_info("simple", "name_group->%s\n", name_group);
- g = purple_find_group(name_group);
+ g = purple_blist_find_group(name_group);
if(!g)
g = purple_group_new(name_group);
}
- if (!g) {
- g = purple_find_group("Buddies");
- if(!g)
- g = purple_group_new("Buddies");
- }
+ if (!g)
+ g = purple_blist_get_default_group();
- for(item = xmlnode_get_child(isc, "contact"); item; item = xmlnode_get_next_twin(item))
+ for(item = purple_xmlnode_get_child(isc, "contact"); item; item = purple_xmlnode_get_next_twin(item))
{
const char *uri;
char *buddy_name;
- uri = xmlnode_get_attrib(item, "uri");
- /*name = xmlnode_get_attrib(item, "name");
- groups = xmlnode_get_attrib(item, "groups");*/
+ uri = purple_xmlnode_get_attrib(item, "uri");
+ /*name = purple_xmlnode_get_attrib(item, "name");
+ groups = purple_xmlnode_get_attrib(item, "groups");*/
purple_debug_info("simple", "URI->%s\n", uri);
buddy_name = g_strdup_printf("sip:%s", uri);
- b = purple_find_buddy(sip->account, buddy_name);
+ b = purple_blist_find_buddy(sip->account, buddy_name);
if(!b){
b = purple_buddy_new(sip->account, buddy_name, uri);
}
g_free(buddy_name);
purple_blist_add_buddy(b, NULL, g, NULL);
- purple_blist_alias_buddy(b, uri);
+ purple_buddy_set_local_alias(b, uri);
bs = g_new0(struct simple_buddy, 1);
bs->name = g_strdup(purple_buddy_get_name(b));
g_hash_table_insert(sip->buddies, bs->name, bs);
}
- xmlnode_free(isc);
+ purple_xmlnode_free(isc);
}
return 0;
}
@@ -1030,10 +1030,10 @@ static void simple_send_message(struct simple_account_data *sip, const char *to,
g_free(fullto);
}
-static int simple_im_send(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags) {
- struct simple_account_data *sip = gc->proto_data;
- char *to = g_strdup(who);
- char *text = purple_unescape_html(what);
+static int simple_im_send(PurpleConnection *gc, PurpleMessage *msg) {
+ struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
+ char *to = g_strdup(purple_message_get_recipient(msg));
+ char *text = purple_unescape_html(purple_message_get_contents(msg));
simple_send_message(sip, to, text, NULL);
g_free(to);
g_free(text);
@@ -1053,13 +1053,13 @@ static void process_incoming_message(struct simple_account_data *sip, struct sip
contenttype = sipmsg_find_header(msg, "Content-Type");
if(!contenttype || !strncmp(contenttype, "text/plain", 10) || !strncmp(contenttype, "text/html", 9)) {
- serv_got_im(sip->gc, from, msg->body, 0, time(NULL));
+ purple_serv_got_im(sip->gc, from, msg->body, 0, time(NULL));
send_sip_response(sip->gc, msg, 200, "OK", NULL);
found = TRUE;
}
else if(!strncmp(contenttype, "application/im-iscomposing+xml", 30)) {
- xmlnode *isc = xmlnode_from_str(msg->body, msg->bodylen);
- xmlnode *state;
+ PurpleXmlNode *isc = purple_xmlnode_from_str(msg->body, msg->bodylen);
+ PurpleXmlNode *state;
gchar *statedata;
if(!isc) {
@@ -1068,25 +1068,25 @@ static void process_incoming_message(struct simple_account_data *sip, struct sip
return;
}
- state = xmlnode_get_child(isc, "state");
+ state = purple_xmlnode_get_child(isc, "state");
if(!state) {
purple_debug_info("simple", "process_incoming_message: no state found\n");
- xmlnode_free(isc);
+ purple_xmlnode_free(isc);
g_free(from);
return;
}
- statedata = xmlnode_get_data(state);
+ statedata = purple_xmlnode_get_data(state);
if(statedata) {
if(strstr(statedata, "active"))
- serv_got_typing(sip->gc, from, 0, PURPLE_TYPING);
+ purple_serv_got_typing(sip->gc, from, 0, PURPLE_IM_TYPING);
else
- serv_got_typing_stopped(sip->gc, from);
+ purple_serv_got_typing_stopped(sip->gc, from);
g_free(statedata);
}
- xmlnode_free(isc);
+ purple_xmlnode_free(isc);
send_sip_response(sip->gc, msg, 200, "OK", NULL);
found = TRUE;
}
@@ -1109,7 +1109,7 @@ gboolean process_register_response(struct simple_account_data *sip, struct sipms
}
}
sip->registerstatus = SIMPLE_REGISTER_COMPLETE;
- purple_connection_set_state(sip->gc, PURPLE_CONNECTED);
+ purple_connection_set_state(sip->gc, PURPLE_CONNECTION_CONNECTED);
/* get buddies from blist */
simple_get_buddies(sip->gc);
@@ -1125,9 +1125,9 @@ gboolean process_register_response(struct simple_account_data *sip, struct sipms
if(sip->registerstatus != SIMPLE_REGISTER_RETRY) {
purple_debug_info("simple", "REGISTER retries %d\n", sip->registrar.retries);
if(sip->registrar.retries > SIMPLE_REGISTER_RETRY_MAX) {
- if (!purple_account_get_remember_password(sip->gc->account))
- purple_account_set_password(sip->gc->account, NULL);
- purple_connection_error_reason(sip->gc,
+ if (!purple_account_get_remember_password(purple_connection_get_account(sip->gc)))
+ purple_account_set_password(purple_connection_get_account(sip->gc), NULL, NULL, NULL);
+ purple_connection_error(sip->gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
_("Incorrect password"));
return TRUE;
@@ -1142,7 +1142,7 @@ gboolean process_register_response(struct simple_account_data *sip, struct sipms
if (sip->registerstatus != SIMPLE_REGISTER_RETRY) {
purple_debug_info("simple", "Unrecognized return code for REGISTER.\n");
if (sip->registrar.retries > SIMPLE_REGISTER_RETRY_MAX) {
- purple_connection_error_reason(sip->gc,
+ purple_connection_error(sip->gc,
PURPLE_CONNECTION_ERROR_OTHER_ERROR,
_("Unknown server response"));
return TRUE;
@@ -1189,8 +1189,8 @@ static void process_incoming_notify(struct simple_account_data *sip, struct sipm
gchar *from;
const gchar *fromhdr;
gchar *basicstatus_data;
- xmlnode *pidf;
- xmlnode *basicstatus = NULL, *tuple, *status;
+ PurpleXmlNode *pidf;
+ PurpleXmlNode *basicstatus = NULL, *tuple, *status;
gboolean isonline = FALSE;
struct simple_buddy *b = NULL;
const gchar *sshdr = NULL;
@@ -1217,7 +1217,7 @@ static void process_incoming_notify(struct simple_account_data *sip, struct sipm
return;
}
- pidf = xmlnode_from_str(msg->body, msg->bodylen);
+ pidf = purple_xmlnode_from_str(msg->body, msg->bodylen);
if(!pidf) {
purple_debug_info("simple", "process_incoming_notify: no parseable pidf\n");
@@ -1253,22 +1253,22 @@ static void process_incoming_notify(struct simple_account_data *sip, struct sipm
return;
}
- if ((tuple = xmlnode_get_child(pidf, "tuple")))
- if ((status = xmlnode_get_child(tuple, "status")))
- basicstatus = xmlnode_get_child(status, "basic");
+ if ((tuple = purple_xmlnode_get_child(pidf, "tuple")))
+ if ((status = purple_xmlnode_get_child(tuple, "status")))
+ basicstatus = purple_xmlnode_get_child(status, "basic");
if(!basicstatus) {
purple_debug_info("simple", "process_incoming_notify: no basic found\n");
- xmlnode_free(pidf);
+ purple_xmlnode_free(pidf);
g_free(from);
return;
}
- basicstatus_data = xmlnode_get_data(basicstatus);
+ basicstatus_data = purple_xmlnode_get_data(basicstatus);
if(!basicstatus_data) {
purple_debug_info("simple", "process_incoming_notify: no basic data found\n");
- xmlnode_free(pidf);
+ purple_xmlnode_free(pidf);
g_free(from);
return;
}
@@ -1282,15 +1282,15 @@ static void process_incoming_notify(struct simple_account_data *sip, struct sipm
else
purple_prpl_got_user_status(sip->account, from, "offline", NULL);
- xmlnode_free(pidf);
+ purple_xmlnode_free(pidf);
g_free(from);
g_free(basicstatus_data);
send_sip_response(sip->gc, msg, 200, "OK", NULL);
}
-static unsigned int simple_typing(PurpleConnection *gc, const char *name, PurpleTypingState state) {
- struct simple_account_data *sip = gc->proto_data;
+static unsigned int simple_typing(PurpleConnection *gc, const char *name, PurpleIMTypingState state) {
+ struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
gchar *xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<isComposing xmlns=\"urn:ietf:params:xml:ns:im-iscomposing\"\n"
@@ -1301,11 +1301,11 @@ static unsigned int simple_typing(PurpleConnection *gc, const char *name, Purple
"<refresh>60</refresh>\n"
"</isComposing>";
gchar *recv = g_strdup(name);
- if(state == PURPLE_TYPING) {
+ if(state == PURPLE_IM_TYPING) {
gchar *msg = g_strdup_printf(xml, "active");
simple_send_message(sip, recv, msg, "application/im-iscomposing+xml");
g_free(msg);
- } else /* TODO: Only if (state == PURPLE_TYPED) ? */ {
+ } else /* TODO: Only if (state == PURPLE_IM_TYPED) ? */ {
gchar *msg = g_strdup_printf(xml, "idle");
simple_send_message(sip, recv, msg, "application/im-iscomposing+xml");
g_free(msg);
@@ -1313,7 +1313,7 @@ static unsigned int simple_typing(PurpleConnection *gc, const char *name, Purple
g_free(recv);
/*
* TODO: Is this right? It will cause the core to call
- * serv_send_typing(gc, who, PURPLE_TYPING) once every second
+ * purple_serv_send_typing(gc, who, PURPLE_IM_TYPING) once every second
* until the user stops typing. If that's not desired,
* then return 0 instead.
*/
@@ -1456,7 +1456,7 @@ static void process_incoming_subscribe(struct simple_account_data *sip, struct s
if(!watcher) { /* new subscription */
const gchar *acceptheader = sipmsg_find_header(msg, "Accept");
gboolean needsxpidf = FALSE;
- if(!purple_privacy_check(sip->account, from)) {
+ if(!purple_account_privacy_check(sip->account, from)) {
send_sip_response(sip->gc, msg, 202, "Ok", NULL);
goto privend;
}
@@ -1666,7 +1666,7 @@ static void process_input(struct simple_account_data *sip, struct sip_connection
static void simple_udp_process(gpointer data, gint source, PurpleInputCondition con) {
PurpleConnection *gc = data;
- struct simple_account_data *sip = gc->proto_data;
+ struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
struct sipmsg *msg;
int len;
time_t currtime = time(NULL);
@@ -1686,7 +1686,7 @@ static void simple_udp_process(gpointer data, gint source, PurpleInputCondition
static void simple_input_cb(gpointer data, gint source, PurpleInputCondition cond)
{
PurpleConnection *gc = data;
- struct simple_account_data *sip = gc->proto_data;
+ struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
int len;
struct sip_connection *conn = connection_find(sip, source);
if(!conn) {
@@ -1709,7 +1709,7 @@ static void simple_input_cb(gpointer data, gint source, PurpleInputCondition con
if(sip->fd == source) sip->fd = -1;
return;
}
- gc->last_received = time(NULL);
+ purple_connection_update_last_received(gc);
conn->inbufused += len;
conn->inbuf[conn->inbufused] = '\0';
@@ -1719,7 +1719,7 @@ static void simple_input_cb(gpointer data, gint source, PurpleInputCondition con
/* Callback for new connections on incoming TCP port */
static void simple_newconn_cb(gpointer data, gint source, PurpleInputCondition cond) {
PurpleConnection *gc = data;
- struct simple_account_data *sip = gc->proto_data;
+ struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
struct sip_connection *conn;
int newfd;
@@ -1741,13 +1741,13 @@ static void login_cb(gpointer data, gint source, const gchar *error_message) {
if(source < 0) {
gchar *tmp = g_strdup_printf(_("Unable to connect: %s"),
error_message);
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
g_free(tmp);
return;
}
- sip = gc->proto_data;
+ sip = purple_connection_get_protocol_data(gc);
sip->fd = source;
conn = connection_create(sip, source);
@@ -1777,7 +1777,7 @@ static void simple_udp_host_resolved_listen_cb(int listenfd, gpointer data) {
sip->listen_data = NULL;
if(listenfd == -1) {
- purple_connection_error_reason(sip->gc,
+ purple_connection_error(sip->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to create listen socket"));
return;
@@ -1806,7 +1806,7 @@ static void simple_udp_host_resolved(GSList *hosts, gpointer data, const char *e
sip->query_data = NULL;
if (!hosts || !hosts->data) {
- purple_connection_error_reason(sip->gc,
+ purple_connection_error(sip->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to resolve hostname"));
return;
@@ -1824,10 +1824,10 @@ static void simple_udp_host_resolved(GSList *hosts, gpointer data, const char *e
}
/* create socket for incoming connections */
- sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_DGRAM,
+ sip->listen_data = purple_network_listen_range(5060, 5160, AF_UNSPEC, SOCK_DGRAM, TRUE,
simple_udp_host_resolved_listen_cb, sip);
if (sip->listen_data == NULL) {
- purple_connection_error_reason(sip->gc,
+ purple_connection_error(sip->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to create listen socket"));
return;
@@ -1842,7 +1842,7 @@ simple_tcp_connect_listen_cb(int listenfd, gpointer data) {
sip->listenfd = listenfd;
if(sip->listenfd == -1) {
- purple_connection_error_reason(sip->gc,
+ purple_connection_error(sip->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to create listen socket"));
return;
@@ -1857,7 +1857,7 @@ simple_tcp_connect_listen_cb(int listenfd, gpointer data) {
/* open tcp connection to the server */
if (purple_proxy_connect(sip->gc, sip->account, sip->realhostname,
sip->realport, login_cb, sip->gc) == NULL) {
- purple_connection_error_reason(sip->gc,
+ purple_connection_error(sip->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to connect"));
}
@@ -1894,10 +1894,10 @@ static void srvresolved(PurpleSrvResponse *resp, int results, gpointer data) {
/* TCP case */
if(!sip->udp) {
/* create socket for incoming connections */
- sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_STREAM,
+ sip->listen_data = purple_network_listen_range(5060, 5160, AF_UNSPEC, SOCK_STREAM, TRUE,
simple_tcp_connect_listen_cb, sip);
if (sip->listen_data == NULL) {
- purple_connection_error_reason(sip->gc,
+ purple_connection_error(sip->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to create listen socket"));
return;
@@ -1905,10 +1905,10 @@ static void srvresolved(PurpleSrvResponse *resp, int results, gpointer data) {
} else { /* UDP */
purple_debug_info("simple", "using udp with server %s and port %d\n", hostname, port);
- sip->query_data = purple_dnsquery_a_account(sip->account, hostname,
+ sip->query_data = purple_dnsquery_a(sip->account, hostname,
port, simple_udp_host_resolved, sip);
if (sip->query_data == NULL) {
- purple_connection_error_reason(sip->gc,
+ purple_connection_error(sip->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to resolve hostname"));
}
@@ -1925,14 +1925,17 @@ static void simple_login(PurpleAccount *account)
const char *username = purple_account_get_username(account);
gc = purple_account_get_connection(account);
+ purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_NO_IMAGES);
+
if (strpbrk(username, " \t\v\r\n") != NULL) {
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
_("SIP usernames may not contain whitespaces or @ symbols"));
return;
}
- gc->proto_data = sip = g_new0(struct simple_account_data, 1);
+ sip = g_new0(struct simple_account_data, 1);
+ purple_connection_set_protocol_data(gc, sip);
sip->gc = gc;
sip->fd = -1;
sip->listenfd = -1;
@@ -1941,11 +1944,11 @@ static void simple_login(PurpleAccount *account)
sip->udp = purple_account_get_bool(account, "udp", FALSE);
/* TODO: is there a good default grow size? */
if(!sip->udp)
- sip->txbuf = purple_circ_buffer_new(0);
+ sip->txbuf = purple_circular_buffer_new(0);
userserver = g_strsplit(username, "@", 2);
if (userserver[1] == NULL || userserver[1][0] == '\0') {
- purple_connection_error_reason(gc,
+ purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
_("SIP connect server not specified"));
return;
@@ -1970,13 +1973,13 @@ static void simple_login(PurpleAccount *account)
hosttoconnect = purple_account_get_string(account, "proxy", sip->servername);
}
- sip->srv_query_data = purple_srv_resolve_account(account, "sip",
+ sip->srv_query_data = purple_srv_resolve(account, "sip",
sip->udp ? "udp" : "tcp", hosttoconnect, srvresolved, sip);
}
static void simple_close(PurpleConnection *gc)
{
- struct simple_account_data *sip = gc->proto_data;
+ struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
if (!sip)
return;
@@ -2007,7 +2010,7 @@ static void simple_close(PurpleConnection *gc)
purple_dnsquery_destroy(sip->query_data);
if (sip->srv_query_data != NULL)
- purple_srv_cancel(sip->srv_query_data);
+ purple_srv_txt_query_destroy(sip->srv_query_data);
if (sip->listen_data != NULL)
purple_network_listen_cancel(sip->listen_data);
@@ -2037,15 +2040,16 @@ static void simple_close(PurpleConnection *gc)
transactions_remove(sip, sip->transactions->data);
g_free(sip->publish_etag);
if (sip->txbuf)
- purple_circ_buffer_destroy(sip->txbuf);
+ g_object_unref(G_OBJECT(sip->txbuf));
g_free(sip->realhostname);
g_free(sip);
- gc->proto_data = NULL;
+ purple_connection_set_protocol_data(gc, NULL);
}
static PurplePluginProtocolInfo prpl_info =
{
+ sizeof(PurplePluginProtocolInfo), /* struct_size */
0,
NULL, /* user_splits */
NULL, /* protocol_options */
@@ -2081,12 +2085,10 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* get_chat_name */
NULL, /* chat_invite */
NULL, /* chat_leave */
- NULL, /* chat_whisper */
NULL, /* chat_send */
simple_keep_alive, /* keepalive */
NULL, /* register_user */
NULL, /* get_cb_info */
- NULL, /* get_cb_away */
NULL, /* alias_buddy */
NULL, /* group_buddy */
NULL, /* rename_group */
@@ -2111,15 +2113,13 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* unregister_user */
NULL, /* send_attention */
NULL, /* get_attention_types */
- sizeof(PurplePluginProtocolInfo), /* struct_size */
NULL, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* get_media_caps */
NULL, /* get_moods */
NULL, /* set_public_alias */
NULL, /* get_public_alias */
- NULL, /* add_buddy_with_invite */
- NULL /* add_buddies_with_invite */
+ NULL /* get_max_message_size */
};
diff --git a/libpurple/protocols/simple/simple.h b/libpurple/protocols/simple/simple.h
index 8f15e4d792..caffe2fcd4 100644
--- a/libpurple/protocols/simple/simple.h
+++ b/libpurple/protocols/simple/simple.h
@@ -27,7 +27,7 @@
#include <time.h>
#include "cipher.h"
-#include "circbuffer.h"
+#include "circularbuffer.h"
#include "dnsquery.h"
#include "dnssrv.h"
#include "network.h"
@@ -101,7 +101,7 @@ struct simple_account_data {
guint resendtimeout;
gboolean connecting;
PurpleAccount *account;
- PurpleCircBuffer *txbuf;
+ PurpleCircularBuffer *txbuf;
guint tx_handler;
gchar *regcallid;
GSList *transactions;
diff --git a/libpurple/protocols/simple/sipmsg.c b/libpurple/protocols/simple/sipmsg.c
index 966a14bb62..1ee511fdcd 100644
--- a/libpurple/protocols/simple/sipmsg.c
+++ b/libpurple/protocols/simple/sipmsg.c
@@ -23,7 +23,7 @@
#include "internal.h"
#include "accountopt.h"
-#include "blist.h"
+#include "buddylist.h"
#include "conversation.h"
#include "debug.h"
#include "notify.h"
diff --git a/libpurple/protocols/yahoo/Makefile.am b/libpurple/protocols/yahoo/Makefile.am
index 3a010ea33c..ca98c75f8c 100644
--- a/libpurple/protocols/yahoo/Makefile.am
+++ b/libpurple/protocols/yahoo/Makefile.am
@@ -1,7 +1,7 @@
EXTRA_DIST = \
Makefile.mingw
-pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+pkgdir = @PURPLE_PLUGINDIR@
YAHOOSOURCES = \
libymsg.c \
@@ -27,8 +27,8 @@ YAHOOSOURCES = \
AM_CFLAGS = $(st)
-libyahoo_la_LDFLAGS = -module -avoid-version
-libyahoojp_la_LDFLAGS = -module -avoid-version
+libyahoo_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+libyahoojp_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
if STATIC_YAHOO
@@ -36,14 +36,23 @@ st = -DPURPLE_STATIC_PRPL
noinst_LTLIBRARIES = libymsg.la
libymsg_la_SOURCES = $(YAHOOSOURCES) libyahoo.c libyahoojp.c
libymsg_la_CFLAGS = $(AM_CFLAGS)
+libymsg_la_LDFLAGS = @PLUGIN_LDFLAGS@
else
+if IS_WIN32
+lib_LTLIBRARIES = libymsg.la
+pkg_LTLIBRARIES =
+else
+pkg_LTLIBRARIES = libymsg.la
+endif
+
st =
-pkg_LTLIBRARIES = libymsg.la libyahoo.la libyahoojp.la
+pkg_LTLIBRARIES += libyahoo.la libyahoojp.la
libymsg_la_SOURCES = $(YAHOOSOURCES)
-libymsg_la_LIBADD = $(GLIB_LIBS)
+libymsg_la_LIBADD = @PURPLE_LIBS@
+libymsg_la_LDFLAGS = @PLUGIN_LDFLAGS@
libyahoo_la_SOURCES = libyahoo.c
libyahoo_la_LIBADD = libymsg.la
diff --git a/libpurple/protocols/yahoo/Makefile.mingw b/libpurple/protocols/yahoo/Makefile.mingw
index f2aba5bdf6..c2b77bd627 100644
--- a/libpurple/protocols/yahoo/Makefile.mingw
+++ b/libpurple/protocols/yahoo/Makefile.mingw
@@ -65,6 +65,7 @@ YAHOOJP_OBJECTS = $(YAHOOJP_C_SRC:%.c=%.o)
##
LIBS = \
-lglib-2.0 \
+ -lgobject-2.0 \
-lws2_32 \
-lintl \
-lpurple
diff --git a/libpurple/protocols/yahoo/libyahoo.c b/libpurple/protocols/yahoo/libyahoo.c
index 4e3d5ce263..9b9b5c5923 100644
--- a/libpurple/protocols/yahoo/libyahoo.c
+++ b/libpurple/protocols/yahoo/libyahoo.c
@@ -112,16 +112,16 @@ static gboolean yahoo_uri_handler(const char *proto, const char *cmd, GHashTable
if (sname) {
char *message = g_hash_table_lookup(params, "m");
- PurpleConversation *conv = purple_find_conversation_with_account(
- PURPLE_CONV_TYPE_IM, sname, acct);
- if (conv == NULL)
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, sname);
- purple_conversation_present(conv);
+ PurpleIMConversation *im = purple_conversations_find_im_with_account(
+ sname, acct);
+ if (im == NULL)
+ im = purple_im_conversation_new(acct, sname);
+ purple_conversation_present(PURPLE_CONVERSATION(im));
if (message) {
/* Spaces are encoded as '+' */
g_strdelimit(message, "+", ' ');
- purple_conv_send_confirm(conv, message);
+ purple_conversation_send_confirm(PURPLE_CONVERSATION(im), message);
}
}
/* else
@@ -139,7 +139,7 @@ static gboolean yahoo_uri_handler(const char *proto, const char *cmd, GHashTable
/* This is somewhat hacky, but the params aren't useful after this command */
g_hash_table_insert(params, g_strdup("room"), g_strdup(rname));
g_hash_table_insert(params, g_strdup("type"), g_strdup("Chat"));
- serv_join_chat(purple_account_get_connection(acct), params);
+ purple_serv_join_chat(purple_account_get_connection(acct), params);
}
/* else
** Same as above (except that this would have to be re-written using purple_request_*)
@@ -194,7 +194,8 @@ static PurpleWhiteboardPrplOps yahoo_whiteboard_prpl_ops =
static PurplePluginProtocolInfo prpl_info =
{
- OPT_PROTO_MAIL_CHECK | OPT_PROTO_CHAT_TOPIC,
+ sizeof(PurplePluginProtocolInfo), /* struct_size */
+ OPT_PROTO_MAIL_CHECK | OPT_PROTO_CHAT_TOPIC | OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE,
NULL, /* user_splits */
NULL, /* protocol_options */
{"png,gif,jpeg", 96, 96, 96, 96, 0, PURPLE_ICON_SCALE_SEND},
@@ -229,12 +230,10 @@ static PurplePluginProtocolInfo prpl_info =
yahoo_get_chat_name,
yahoo_c_invite,
yahoo_c_leave,
- NULL, /* chat whisper */
yahoo_c_send,
yahoo_keepalive,
NULL, /* register_user */
NULL, /* get_cb_info */
- NULL, /* get_cb_away */
yahoo_update_alias, /* alias_buddy */
yahoo_change_buddys_group,
yahoo_rename_group,
@@ -257,19 +256,15 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* send_raw */
NULL, /* roomlist_room_serialize */
NULL, /* unregister_user */
-
yahoo_send_attention,
yahoo_attention_types,
-
- sizeof(PurplePluginProtocolInfo), /* struct_size */
yahoo_get_account_text_table, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* get_media_caps */
NULL, /* get_moods */
NULL, /* set_public_alias */
NULL, /* get_public_alias */
- NULL, /* add_buddy_with_invite */
- NULL /* add_buddies_with_invite */
+ yahoo_get_max_message_size
};
static PurplePluginInfo info =
@@ -317,9 +312,6 @@ init_plugin(PurplePlugin *plugin)
option = purple_account_option_string_new(_("File transfer server"), "xfer_host", YAHOO_XFER_HOST);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
- option = purple_account_option_int_new(_("File transfer port"), "xfer_port", YAHOO_XFER_PORT);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
option = purple_account_option_string_new(_("Chat room locale"), "room_list_locale", YAHOO_ROOMLIST_LOCALE);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
@@ -329,10 +321,10 @@ init_plugin(PurplePlugin *plugin)
option = purple_account_option_bool_new(_("Ignore conference and chatroom invitations"), "ignore_invites", FALSE);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+#if 0
option = purple_account_option_bool_new(_("Use account proxy for HTTP and HTTPS connections"), "proxy_ssl", FALSE);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-#if 0
option = purple_account_option_string_new(_("Chat room list URL"), "room_list", YAHOO_ROOMLIST_URL);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
#endif
diff --git a/libpurple/protocols/yahoo/libyahoojp.c b/libpurple/protocols/yahoo/libyahoojp.c
index 219603c1fa..b35bf12913 100644
--- a/libpurple/protocols/yahoo/libyahoojp.c
+++ b/libpurple/protocols/yahoo/libyahoojp.c
@@ -90,7 +90,8 @@ static PurpleWhiteboardPrplOps yahoo_whiteboard_prpl_ops =
static PurplePluginProtocolInfo prpl_info =
{
- OPT_PROTO_MAIL_CHECK | OPT_PROTO_CHAT_TOPIC,
+ sizeof(PurplePluginProtocolInfo), /* struct_size */
+ OPT_PROTO_MAIL_CHECK | OPT_PROTO_CHAT_TOPIC | OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE,
NULL, /* user_splits */
NULL, /* protocol_options */
{"png,gif,jpeg", 96, 96, 96, 96, 0, PURPLE_ICON_SCALE_SEND},
@@ -125,12 +126,10 @@ static PurplePluginProtocolInfo prpl_info =
yahoo_get_chat_name,
yahoo_c_invite,
yahoo_c_leave,
- NULL, /* chat whisper */
yahoo_c_send,
yahoo_keepalive,
NULL, /* register_user */
NULL, /* get_cb_info */
- NULL, /* get_cb_away */
yahoo_update_alias, /* alias_buddy */
yahoo_change_buddys_group,
yahoo_rename_group,
@@ -157,15 +156,13 @@ static PurplePluginProtocolInfo prpl_info =
yahoo_send_attention,
yahoo_attention_types,
- sizeof(PurplePluginProtocolInfo), /* struct_size */
yahoojp_get_account_text_table, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* get_media_caps */
NULL, /* get_moods */
NULL, /* set_public_alias */
NULL, /* get_public_alias */
- NULL, /* add_buddy_with_invite */
- NULL /* add_buddies_with_invite */
+ yahoo_get_max_message_size
};
static PurplePluginInfo info =
@@ -213,9 +210,6 @@ init_plugin(PurplePlugin *plugin)
option = purple_account_option_string_new(_("File transfer server"), "xfer_host", YAHOOJP_XFER_HOST);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
- option = purple_account_option_int_new(_("File transfer port"), "xfer_port", YAHOO_XFER_PORT);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
option = purple_account_option_string_new(_("Chat room locale"), "room_list_locale", YAHOOJP_ROOMLIST_LOCALE);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
@@ -225,10 +219,10 @@ init_plugin(PurplePlugin *plugin)
option = purple_account_option_bool_new(_("Ignore conference and chatroom invitations"), "ignore_invites", FALSE);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+#if 0
option = purple_account_option_bool_new(_("Use account proxy for HTTP and HTTPS connections"), "proxy_ssl", FALSE);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-#if 0
option = purple_account_option_string_new(_("Chat room list URL"), "room_list", YAHOO_ROOMLIST_URL);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
#endif
diff --git a/libpurple/protocols/yahoo/libymsg.c b/libpurple/protocols/yahoo/libymsg.c
index e2824ec675..03cf29e2f2 100644
--- a/libpurple/protocols/yahoo/libymsg.c
+++ b/libpurple/protocols/yahoo/libymsg.c
@@ -31,14 +31,14 @@
#include "account.h"
#include "accountopt.h"
-#include "blist.h"
-#include "cipher.h"
+#include "buddylist.h"
+#include "ciphers/md5hash.h"
#include "cmds.h"
#include "core.h"
#include "debug.h"
+#include "http.h"
#include "network.h"
#include "notify.h"
-#include "privacy.h"
#include "prpl.h"
#include "proxy.h"
#include "request.h"
@@ -59,7 +59,10 @@
/* #define YAHOO_DEBUG */
-/* #define TRY_WEBMESSENGER_LOGIN 0 */
+/* It doesn't look like it is working (the previously used host is down, another
+ * one doesn't send us back cookies).
+ */
+#define TRY_WEBMESSENGER_LOGIN 0
/* One hour */
#define PING_TIMEOUT 3600
@@ -67,8 +70,10 @@
/* One minute */
#define KEEPALIVE_TIMEOUT 60
-#ifdef TRY_WEBMESSENGER_LOGIN
-static void yahoo_login_page_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, size_t len, const gchar *error_message);
+#if TRY_WEBMESSENGER_LOGIN
+static void
+yahoo_login_page_cb(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _unused);
#endif /* TRY_WEBMESSENGER_LOGIN */
static gboolean yahoo_is_japan(PurpleAccount *account)
@@ -80,7 +85,7 @@ static void yahoo_update_status(PurpleConnection *gc, const char *name, YahooFri
{
char *status = NULL;
- if (!gc || !name || !f || !purple_find_buddy(purple_connection_get_account(gc), name))
+ if (!gc || !name || !f || !purple_blist_find_buddy(purple_connection_get_account(gc), name))
return;
switch (f->status) {
@@ -164,8 +169,8 @@ static void yahoo_process_status(PurpleConnection *gc, struct yahoo_packet *pkt)
if (pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) {
if (!purple_account_get_remember_password(account))
- purple_account_set_password(account, NULL);
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NAME_IN_USE,
+ purple_account_set_password(account, NULL, NULL, NULL);
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NAME_IN_USE,
_("You have signed on from another location"));
return;
}
@@ -343,12 +348,12 @@ static void yahoo_process_status(PurpleConnection *gc, struct yahoo_packet *pkt)
if (!name)
break;
- b = purple_find_buddy(gc->account, name);
+ b = purple_blist_find_buddy(purple_connection_get_account(gc), name);
if (!cksum || (cksum == -1)) {
if (f)
yahoo_friend_set_buddy_icon_need_request(f, TRUE);
- purple_buddy_icons_set_for_user(gc->account, name, NULL, 0, NULL);
+ purple_buddy_icons_set_for_user(purple_connection_get_account(gc), name, NULL, 0, NULL);
break;
}
@@ -367,7 +372,8 @@ static void yahoo_process_status(PurpleConnection *gc, struct yahoo_packet *pkt)
case 16: /* Custom error message */
{
char *tmp = yahoo_string_decode(gc, pair->value, TRUE);
- purple_notify_error(gc, NULL, tmp, NULL);
+ purple_notify_error(gc, NULL, tmp, NULL,
+ purple_request_cpar_from_connection(gc));
g_free(tmp);
}
break;
@@ -413,7 +419,7 @@ static void yahoo_do_group_check(PurpleAccount *account, GHashTable *ht, const c
if (g_hash_table_lookup_extended(ht, name, (gpointer *)&oname, (gpointer *)&list))
g_hash_table_steal(ht, oname);
else
- list = purple_find_buddies(account, name);
+ list = purple_blist_find_buddies(account, name);
for (i = list; i; i = i->next) {
b = i->data;
@@ -430,7 +436,7 @@ static void yahoo_do_group_check(PurpleAccount *account, GHashTable *ht, const c
if (!onlist) {
purple_debug_misc("yahoo",
"Uhoh, %s isn't on the list (or not in this group), adding him to group %s.\n", name, group);
- if (!(g = purple_find_group(group))) {
+ if (!(g = purple_blist_find_group(group))) {
g = purple_group_new(group);
purple_blist_add_group(g, NULL);
}
@@ -485,12 +491,10 @@ static char *_getcookie(char *rawcookie)
static void yahoo_process_cookie(YahooData *yd, char *c)
{
if (c[0] == 'Y') {
- if (yd->cookie_y)
- g_free(yd->cookie_y);
+ g_free(yd->cookie_y);
yd->cookie_y = _getcookie(c);
} else if (c[0] == 'T') {
- if (yd->cookie_t)
- g_free(yd->cookie_t);
+ g_free(yd->cookie_t);
yd->cookie_t = _getcookie(c);
} else
purple_debug_info("yahoo", "Unrecognized cookie '%c'\n", c[0]);
@@ -502,7 +506,7 @@ static void yahoo_process_list_15(PurpleConnection *gc, struct yahoo_packet *pkt
GSList *l = pkt->hash;
PurpleAccount *account = purple_connection_get_account(gc);
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
GHashTable *ht;
char *norm_bud = NULL;
char *temp = NULL;
@@ -553,10 +557,10 @@ static void yahoo_process_list_15(PurpleConnection *gc, struct yahoo_packet *pkt
if (yd->current_list15_grp) {
/* This buddy is in a group */
f = yahoo_friend_find_or_new(gc, norm_bud);
- if (!purple_find_buddy(account, norm_bud)) {
+ if (!purple_blist_find_buddy(account, norm_bud)) {
PurpleBuddy *b;
PurpleGroup *g;
- if (!(g = purple_find_group(yd->current_list15_grp))) {
+ if (!(g = purple_blist_find_group(yd->current_list15_grp))) {
g = purple_group_new(yd->current_list15_grp);
purple_blist_add_group(g, NULL);
}
@@ -579,8 +583,8 @@ static void yahoo_process_list_15(PurpleConnection *gc, struct yahoo_packet *pkt
yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_DO_NOT_CONNECT);
} else {
/* This buddy is on the ignore list (and therefore in no group) */
- purple_debug_info("yahoo", "%s adding %s to the deny list because of the ignore list / no group was found\n",account->username, norm_bud);
- purple_privacy_deny_add(account, norm_bud, 1);
+ purple_debug_info("yahoo", "%s adding %s to the deny list because of the ignore list / no group was found\n", purple_account_get_username(account), norm_bud);
+ purple_account_privacy_deny_add(account, norm_bud, 1);
}
g_free(norm_bud);
@@ -636,7 +640,7 @@ static void yahoo_process_list_15(PurpleConnection *gc, struct yahoo_packet *pkt
purple_connection_set_display_name(gc, purple_normalize(account, purple_account_get_username(account)));
yd->logged_in = TRUE;
purple_debug_info("yahoo","Authentication: Connection established\n");
- purple_connection_set_state(gc, PURPLE_CONNECTED);
+ purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
if (yd->picture_upload_todo) {
yahoo_buddy_icon_upload(gc, yd->picture_upload_todo);
yd->picture_upload_todo = NULL;
@@ -653,7 +657,7 @@ static void yahoo_process_list(PurpleConnection *gc, struct yahoo_packet *pkt)
gboolean got_serv_list = FALSE;
YahooFriend *f = NULL;
PurpleAccount *account = purple_connection_get_account(gc);
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
GHashTable *ht;
char **lines;
@@ -744,10 +748,10 @@ static void yahoo_process_list(PurpleConnection *gc, struct yahoo_packet *pkt)
norm_bud = g_strdup(purple_normalize(account, *bud));
f = yahoo_friend_find_or_new(gc, norm_bud);
- if (!purple_find_buddy(account, norm_bud)) {
+ if (!purple_blist_find_buddy(account, norm_bud)) {
PurpleBuddy *b;
PurpleGroup *g;
- if (!(g = purple_find_group(grp))) {
+ if (!(g = purple_blist_find_group(grp))) {
g = purple_group_new(grp);
purple_blist_add_group(g, NULL);
}
@@ -779,7 +783,7 @@ static void yahoo_process_list(PurpleConnection *gc, struct yahoo_packet *pkt)
for (bud = buddies; bud && *bud; bud++) {
/* The server is already ignoring the user */
got_serv_list = TRUE;
- purple_privacy_deny_add(account, *bud, 1);
+ purple_account_privacy_deny_add(account, *bud, 1);
}
g_strfreev(buddies);
@@ -788,13 +792,13 @@ static void yahoo_process_list(PurpleConnection *gc, struct yahoo_packet *pkt)
}
if (got_serv_list &&
- ((account->perm_deny != PURPLE_PRIVACY_ALLOW_BUDDYLIST) &&
- (account->perm_deny != PURPLE_PRIVACY_DENY_ALL) &&
- (account->perm_deny != PURPLE_PRIVACY_ALLOW_USERS)))
+ ((purple_account_get_privacy_type(account) != PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST) &&
+ (purple_account_get_privacy_type(account) != PURPLE_ACCOUNT_PRIVACY_DENY_ALL) &&
+ (purple_account_get_privacy_type(account) != PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS)))
{
- account->perm_deny = PURPLE_PRIVACY_DENY_USERS;
- purple_debug_info("yahoo", "%s privacy defaulting to PURPLE_PRIVACY_DENY_USERS.\n",
- account->username);
+ purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_DENY_USERS);
+ purple_debug_info("yahoo", "%s privacy defaulting to PURPLE_ACCOUNT_PRIVACY_DENY_USERS.\n",
+ purple_account_get_username(account));
}
if (yd->tmp_serv_plist) {
@@ -803,7 +807,7 @@ static void yahoo_process_list(PurpleConnection *gc, struct yahoo_packet *pkt)
f = yahoo_friend_find(gc, *bud);
if (f) {
purple_debug_info("yahoo", "%s setting presence for %s to PERM_OFFLINE\n",
- account->username, *bud);
+ purple_account_get_username(account), *bud);
f->presence = YAHOO_PRESENCE_PERM_OFFLINE;
}
}
@@ -827,7 +831,7 @@ static void yahoo_process_notify(PurpleConnection *gc, struct yahoo_packet *pkt,
YahooFriend *f = NULL;
GSList *l = pkt->hash;
gint val_11 = 0;
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
YahooFederation fed = YAHOO_FEDERATION_NONE;
account = purple_connection_get_account(gc);
@@ -841,23 +845,22 @@ static void yahoo_process_notify(PurpleConnection *gc, struct yahoo_packet *pkt,
purple_debug_warning("yahoo", "yahoo_process_notify "
"got non-UTF-8 string for key %d\n", pair->key);
}
- }
- if (pair->key == 49)
+ } else if (pair->key == 49) {
msg = pair->value;
- if (pair->key == 13)
+ } else if (pair->key == 13) {
stat = pair->value;
- if (pair->key == 14) {
+ } else if (pair->key == 14) {
if (g_utf8_validate(pair->value, -1, NULL)) {
game = pair->value;
} else {
purple_debug_warning("yahoo", "yahoo_process_notify "
"got non-UTF-8 string for key %d\n", pair->key);
}
- }
- if (pair->key == 11)
+ } else if (pair->key == 11) {
val_11 = strtol(pair->value, NULL, 10);
- if (pair->key == 241)
+ } else if (pair->key == 241) {
fed = strtol(pair->value, NULL, 10);
+ }
l = l->next;
}
@@ -873,7 +876,7 @@ static void yahoo_process_notify(PurpleConnection *gc, struct yahoo_packet *pkt,
}
if (!g_ascii_strncasecmp(msg, "TYPING", strlen("TYPING"))
- && (purple_privacy_check(account, from)))
+ && (purple_account_privacy_check(account, from)))
{
char *fed_from = from;
switch (fed) {
@@ -895,15 +898,15 @@ static void yahoo_process_notify(PurpleConnection *gc, struct yahoo_packet *pkt,
}
if (stat && *stat == '1')
- serv_got_typing(gc, fed_from, 0, PURPLE_TYPING);
+ purple_serv_got_typing(gc, fed_from, 0, PURPLE_IM_TYPING);
else
- serv_got_typing_stopped(gc, fed_from);
+ purple_serv_got_typing_stopped(gc, fed_from);
if (fed_from != from)
g_free(fed_from);
} else if (!g_ascii_strncasecmp(msg, "GAME", strlen("GAME"))) {
- PurpleBuddy *bud = purple_find_buddy(account, from);
+ PurpleBuddy *bud = purple_blist_find_buddy(account, from);
if (!bud) {
purple_debug_warning("yahoo",
@@ -922,9 +925,10 @@ static void yahoo_process_notify(PurpleConnection *gc, struct yahoo_packet *pkt,
yahoo_update_status(gc, from, f);
}
} else if (!g_ascii_strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE"))) {
- PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, from, account);
+ PurpleIMConversation *im = purple_conversations_find_im_with_account(from, account);
char *buf = g_strdup_printf(_("%s has sent you a webcam invite, which is not yet supported."), from);
- purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NOTIFY, time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
+ buf, PURPLE_MESSAGE_NOTIFY);
g_free(buf);
}
}
@@ -951,7 +955,7 @@ static void yahoo_process_sms_message(PurpleConnection *gc, struct yahoo_packet
char *server_msg = NULL;
char *m;
- yd = gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
account = purple_connection_get_account(gc);
while (l != NULL) {
@@ -966,15 +970,13 @@ static void yahoo_process_sms_message(PurpleConnection *gc, struct yahoo_packet
purple_debug_warning("yahoo", "yahoo_process_sms_message "
"got non-UTF-8 string for key %d\n", pair->key);
}
- }
- if (pair->key == 14) {
+ } else if (pair->key == 14) {
if (sms)
sms->msg = pair->value;
- }
- if (pair->key == 68)
+ } else if (pair->key == 68) {
if(sms)
g_hash_table_insert(yd->sms_carrier, g_strdup(sms->from), g_strdup(pair->value));
- if (pair->key == 16) {
+ } else if (pair->key == 16) {
if (g_utf8_validate(pair->value, -1, NULL)) {
server_msg = pair->value;
} else {
@@ -994,14 +996,18 @@ static void yahoo_process_sms_message(PurpleConnection *gc, struct yahoo_packet
pkt->status = YAHOO_STATUS_DISCONNECTED;
if (pkt->status == YAHOO_STATUS_DISCONNECTED) {
if (server_msg) {
- PurpleConversation *c;
- c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, sms->from, account);
- if (c == NULL)
- c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sms->from);
- purple_conversation_write(c, NULL, server_msg, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ PurpleIMConversation *im;
+ im = purple_conversations_find_im_with_account(sms->from, account);
+ if (im == NULL)
+ im = purple_im_conversation_new(account, sms->from);
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
+ server_msg, 0);
+ }
+ else {
+ purple_notify_error(gc, NULL,
+ _("Your SMS was not delivered"), NULL,
+ purple_request_cpar_from_connection(gc));
}
- else
- purple_notify_error(gc, NULL, _("Your SMS was not delivered"), NULL);
g_free(sms->from);
g_free(sms);
@@ -1014,7 +1020,7 @@ static void yahoo_process_sms_message(PurpleConnection *gc, struct yahoo_packet
}
m = yahoo_string_decode(gc, sms->msg, sms->utf8);
- serv_got_im(gc, sms->from, m, 0, sms->time);
+ purple_serv_got_im(gc, sms->from, m, 0, sms->time);
g_free(m);
g_free(sms->from);
@@ -1025,7 +1031,7 @@ static void yahoo_process_sms_message(PurpleConnection *gc, struct yahoo_packet
static void yahoo_process_message(PurpleConnection *gc, struct yahoo_packet *pkt, yahoo_pkt_type pkt_type)
{
PurpleAccount *account;
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
GSList *l = pkt->hash;
GSList *list = NULL;
struct _yahoo_im *im = NULL;
@@ -1049,23 +1055,21 @@ static void yahoo_process_message(PurpleConnection *gc, struct yahoo_packet *pkt
purple_debug_warning("yahoo", "yahoo_process_message "
"got non-UTF-8 string for key %d\n", pair->key);
}
- }
- if (im && pair->key == 5)
+ } else if (im && pair->key == 5) {
im->active_id = pair->value;
- if (pair->key == 97)
+ } else if (pair->key == 97) {
if (im)
im->utf8 = strtol(pair->value, NULL, 10);
- if (pair->key == 15)
+ } else if (pair->key == 15) {
if (im)
im->time = strtol(pair->value, NULL, 10);
- if (pair->key == 206)
+ } else if (pair->key == 206) {
if (im)
im->buddy_icon = strtol(pair->value, NULL, 10);
- if (pair->key == 14) {
+ } else if (pair->key == 14) {
if (im)
im->msg = pair->value;
- }
- if (im && pair->key == 241) {
+ } else if (im && pair->key == 241) {
im->fed = strtol(pair->value, NULL, 10);
g_free(im->fed_from);
switch (im->fed) {
@@ -1088,9 +1092,8 @@ static void yahoo_process_message(PurpleConnection *gc, struct yahoo_packet *pkt
}
purple_debug_info("yahoo", "Message from federated (%d) buddy %s.\n", im->fed, im->fed_from);
- }
- /* peer session id */
- if (im && (pair->key == 11)) {
+ } else if (im && (pair->key == 11)) {
+ /* peer session id */
/* disconnect the peer if connected through p2p and sends wrong value for session id */
if( (im->fed == YAHOO_FEDERATION_NONE) && (pkt_type == YAHOO_PKT_TYPE_P2P)
&& (yd->session_id != strtol(pair->value, NULL, 10)) )
@@ -1102,10 +1105,9 @@ static void yahoo_process_message(PurpleConnection *gc, struct yahoo_packet *pkt
g_free(im);
return; /* Not sure whether we should process remaining IMs in this packet */
}
- }
- /* IMV key */
- if (im && pair->key == 63 && g_utf8_validate(pair->value, -1, NULL))
- {
+
+ } else if (im && pair->key == 63 && g_utf8_validate(pair->value, -1, NULL)) {
+ /* IMV key */
/* Check for the Doodle IMV, no IMvironment for federated buddies */
if (im->from != NULL && im->fed == YAHOO_FEDERATION_NONE)
{
@@ -1115,7 +1117,7 @@ static void yahoo_process_message(PurpleConnection *gc, struct yahoo_packet *pkt
{
PurpleWhiteboard *wb;
- if (!purple_privacy_check(account, im->from)) {
+ if (!purple_account_privacy_check(account, im->from)) {
purple_debug_info("yahoo", "Doodle request from %s dropped.\n",
im->from);
g_free(im->fed_from);
@@ -1129,9 +1131,9 @@ static void yahoo_process_message(PurpleConnection *gc, struct yahoo_packet *pkt
if(wb == NULL)
{
doodle_session *ds;
- wb = purple_whiteboard_create(account, im->from,
+ wb = purple_whiteboard_new(account, im->from,
DOODLE_STATE_REQUESTED);
- ds = wb->proto_data;
+ ds = purple_whiteboard_get_protocol_data(wb);
ds->imv_key = g_strdup(pair->value);
yahoo_doodle_command_send_request(gc, im->from, pair->value);
@@ -1139,15 +1141,16 @@ static void yahoo_process_message(PurpleConnection *gc, struct yahoo_packet *pkt
}
}
}
- }
- if (pair->key == 429)
+ } else if (pair->key == 429) {
if (im)
im->id = pair->value;
+ }
l = l->next;
}
} else if (pkt->status == 2) {
purple_notify_error(gc, NULL,
- _("Your Yahoo! message did not get sent."), NULL);
+ _("Your Yahoo! message did not get sent."), NULL,
+ purple_request_cpar_from_connection(gc));
}
for (l = list; l; l = l->next) {
@@ -1161,7 +1164,7 @@ static void yahoo_process_message(PurpleConnection *gc, struct yahoo_packet *pkt
continue;
}
- if (!purple_privacy_check(account, im->fed_from)) {
+ if (!purple_account_privacy_check(account, im->fed_from)) {
purple_debug_info("yahoo", "Message from %s dropped.\n", im->fed_from);
return;
}
@@ -1212,7 +1215,7 @@ static void yahoo_process_message(PurpleConnection *gc, struct yahoo_packet *pkt
m2 = yahoo_codes_to_html(m);
g_free(m);
- serv_got_im(gc, im->fed_from, m2, 0, im->time);
+ purple_serv_got_im(gc, im->fed_from, m2, 0, im->time);
g_free(m2);
/* Official clients don't share buddy images with federated buddies */
@@ -1247,8 +1250,7 @@ static void yahoo_process_sysmessage(PurpleConnection *gc, struct yahoo_packet *
purple_debug_warning("yahoo", "yahoo_process_sysmessage "
"got non-UTF-8 string for key %d\n", pair->key);
}
- }
- if (pair->key == 14) {
+ } else if (pair->key == 14) {
if (g_utf8_validate(pair->value, -1, NULL)) {
msg = pair->value;
} else {
@@ -1265,7 +1267,8 @@ static void yahoo_process_sysmessage(PurpleConnection *gc, struct yahoo_packet *
prim = g_strdup_printf(_("Yahoo! system message for %s:"),
me?me:purple_connection_get_display_name(gc));
- purple_notify_info(NULL, NULL, prim, msg);
+ purple_notify_info(NULL, NULL, prim, msg,
+ purple_request_cpar_from_connection(gc));
g_free(prim);
}
@@ -1277,11 +1280,11 @@ struct yahoo_add_request {
};
static void
-yahoo_buddy_add_authorize_cb(gpointer data)
+yahoo_buddy_add_authorize_cb(const char *message, gpointer data)
{
struct yahoo_add_request *add_req = data;
struct yahoo_packet *pkt;
- YahooData *yd = add_req->gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(add_req->gc);
const char *who = add_req->who;
pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15, YAHOO_STATUS_AVAILABLE, yd->session_id);
@@ -1310,15 +1313,16 @@ yahoo_buddy_add_authorize_cb(gpointer data)
}
static void
-yahoo_buddy_add_deny_cb(struct yahoo_add_request *add_req, const char *msg)
+yahoo_buddy_add_deny_cb(const char *msg, gpointer data)
{
- YahooData *yd = add_req->gc->proto_data;
+ struct yahoo_add_request *add_req = data;
+ YahooData *yd = purple_connection_get_protocol_data(add_req->gc);
struct yahoo_packet *pkt;
char *encoded_msg = NULL;
const char *who = add_req->who;
if (msg && *msg)
- encoded_msg = yahoo_string_encode(add_req->gc, msg, NULL);
+ encoded_msg = yahoo_string_encode(add_req->gc, msg, FALSE);
pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15,
YAHOO_STATUS_AVAILABLE, yd->session_id);
@@ -1331,7 +1335,7 @@ yahoo_buddy_add_deny_cb(struct yahoo_add_request *add_req, const char *msg)
241, add_req->fed,
13, 2,
334, 0,
- 97, 1,
+ 97, 1, /* UTF-8 */
14, encoded_msg ? encoded_msg : "");
}
else {
@@ -1340,7 +1344,7 @@ yahoo_buddy_add_deny_cb(struct yahoo_add_request *add_req, const char *msg)
5, who,
13, 2,
334, 0,
- 97, 1,
+ 97, 1, /* UTF-8 */
14, encoded_msg ? encoded_msg : "");
}
@@ -1354,27 +1358,10 @@ yahoo_buddy_add_deny_cb(struct yahoo_add_request *add_req, const char *msg)
g_free(add_req);
}
-static void
-yahoo_buddy_add_deny_noreason_cb(struct yahoo_add_request *add_req, const char*msg)
-{
- yahoo_buddy_add_deny_cb(add_req, NULL);
-}
-
-static void
-yahoo_buddy_add_deny_reason_cb(gpointer data) {
- struct yahoo_add_request *add_req = data;
- purple_request_input(add_req->gc, NULL, _("Authorization denied message:"),
- NULL, _("No reason given."), TRUE, FALSE, NULL,
- _("OK"), G_CALLBACK(yahoo_buddy_add_deny_cb),
- _("Cancel"), G_CALLBACK(yahoo_buddy_add_deny_noreason_cb),
- purple_connection_get_account(add_req->gc), add_req->who, NULL,
- add_req);
-}
-
static void yahoo_buddy_denied_our_add(PurpleConnection *gc, const char *who, const char *reason)
{
char *notify_msg;
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
if (who == NULL)
return;
@@ -1386,7 +1373,8 @@ static void yahoo_buddy_denied_our_add(PurpleConnection *gc, const char *who, co
} else
notify_msg = g_strdup_printf(_("%s has (retroactively) denied your request to add them to your list."), who);
- purple_notify_info(gc, NULL, _("Add buddy rejected"), notify_msg);
+ purple_notify_info(gc, NULL, _("Add buddy rejected"), notify_msg,
+ purple_request_cpar_from_connection(gc));
g_free(notify_msg);
g_hash_table_remove(yd->friends, who);
@@ -1456,7 +1444,7 @@ static void yahoo_buddy_auth_req_15(PurpleConnection *gc, struct yahoo_packet *p
yahoo_buddy_denied_our_add(gc, who, msg);
} else
purple_debug_error("yahoo", "Received unknown authorization response of %d from buddy '%s'.\n", response, who ? who : "(Unknown Buddy)");
- g_free(who);
+ g_free(who);
}
/* Buddy requested authorization to add us. */
else if (pkt->status == 3) {
@@ -1533,11 +1521,11 @@ static void yahoo_buddy_auth_req_15(PurpleConnection *gc, struct yahoo_packet *p
if (add_req->id && add_req->who) {
char *alias = NULL, *dec_msg = NULL;
- if (!purple_privacy_check(account, add_req->who))
+ if (!purple_account_privacy_check(account, add_req->who))
{
purple_debug_misc("yahoo", "Auth. request from %s dropped and automatically denied due to privacy settings!\n",
add_req->who);
- yahoo_buddy_add_deny_cb(add_req, NULL);
+ yahoo_buddy_add_deny_cb(NULL, add_req);
return;
}
@@ -1556,9 +1544,9 @@ static void yahoo_buddy_auth_req_15(PurpleConnection *gc, struct yahoo_packet *p
*/
purple_account_request_authorization(account, add_req->who, add_req->id,
alias, dec_msg,
- purple_find_buddy(account, add_req->who) != NULL,
+ purple_blist_find_buddy(account, add_req->who) != NULL,
yahoo_buddy_add_authorize_cb,
- yahoo_buddy_add_deny_reason_cb,
+ yahoo_buddy_add_deny_cb,
add_req);
g_free(alias);
g_free(dec_msg);
@@ -1616,10 +1604,10 @@ static void yahoo_buddy_added_us(PurpleConnection *gc, struct yahoo_packet *pkt)
if (add_req->id && add_req->who) {
char *dec_msg = NULL;
- if (!purple_privacy_check(account, add_req->who)) {
+ if (!purple_account_privacy_check(account, add_req->who)) {
purple_debug_misc("yahoo", "Auth. request from %s dropped and automatically denied due to privacy settings!\n",
add_req->who);
- yahoo_buddy_add_deny_cb(add_req, NULL);
+ yahoo_buddy_add_deny_cb(NULL, add_req);
return;
}
@@ -1631,9 +1619,9 @@ static void yahoo_buddy_added_us(PurpleConnection *gc, struct yahoo_packet *pkt)
*/
purple_account_request_authorization(account, add_req->who, add_req->id,
NULL, dec_msg,
- purple_find_buddy(account,add_req->who) != NULL,
+ purple_blist_find_buddy(account,add_req->who) != NULL,
yahoo_buddy_add_authorize_cb,
- yahoo_buddy_add_deny_reason_cb, add_req);
+ yahoo_buddy_add_deny_cb, add_req);
g_free(dec_msg);
} else {
g_free(add_req->id);
@@ -1749,7 +1737,7 @@ static char *yahoo_decode(const char *text)
static void yahoo_process_mail(PurpleConnection *gc, struct yahoo_packet *pkt)
{
PurpleAccount *account = purple_connection_get_account(gc);
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
const char *who = NULL;
const char *email = NULL;
const char *subj = NULL;
@@ -1762,9 +1750,9 @@ static void yahoo_process_mail(PurpleConnection *gc, struct yahoo_packet *pkt)
while (l) {
struct yahoo_pair *pair = l->data;
- if (pair->key == 9)
+ if (pair->key == 9) {
count = strtol(pair->value, NULL, 10);
- else if (pair->key == 43) {
+ } else if (pair->key == 43) {
if (g_utf8_validate(pair->value, -1, NULL)) {
who = pair->value;
} else {
@@ -1849,11 +1837,10 @@ static void to_y64(char *out, const unsigned char *in, gsize inlen)
static void yahoo_auth16_stage3(PurpleConnection *gc, const char *crypt)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
PurpleAccount *account = purple_connection_get_account(gc);
const char *name = purple_normalize(account, purple_account_get_username(account));
- PurpleCipher *md5_cipher;
- PurpleCipherContext *md5_ctx;
+ PurpleHash *md5_hash;
guchar md5_digest[16];
gchar base64_string[25];
struct yahoo_packet *pkt;
@@ -1862,10 +1849,9 @@ static void yahoo_auth16_stage3(PurpleConnection *gc, const char *crypt)
g_return_if_fail(crypt != NULL);
- md5_cipher = purple_ciphers_find_cipher("md5");
- md5_ctx = purple_cipher_context_new(md5_cipher, NULL);
- purple_cipher_context_append(md5_ctx, (guchar *)crypt, strlen(crypt));
- purple_cipher_context_digest(md5_ctx, sizeof(md5_digest), md5_digest, NULL);
+ md5_hash = purple_md5_hash_new();
+ purple_hash_append(md5_hash, (guchar *)crypt, strlen(crypt));
+ purple_hash_digest(md5_hash, md5_digest, sizeof(md5_digest));
to_y64(base64_string, md5_digest, 16);
@@ -1903,176 +1889,133 @@ static void yahoo_auth16_stage3(PurpleConnection *gc, const char *crypt)
yahoo_packet_hash_int(pkt, 192, yd->picture_checksum);
yahoo_packet_send_and_free(pkt, yd);
- purple_cipher_context_destroy(md5_ctx);
+ g_object_unref(md5_hash);
}
-static gchar *yahoo_auth16_get_cookie_b(gchar *headers)
+static void yahoo_auth16_stage2(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _auth_data)
{
- gchar **splits = g_strsplit(headers, "\r\n", -1);
- gchar *tmp = NULL, *tmp2 = NULL, *sem = NULL;
- int elements = g_strv_length(splits), i;
-
- if(elements > 1) {
- for(i = 0; i < elements; i++) {
- if(g_ascii_strncasecmp(splits[i], "Set-Cookie: B=", 14) == 0) {
- tmp = &splits[i][14];
- sem = strchr(tmp, ';');
-
- if (sem != NULL) {
- tmp2 = g_strndup(tmp, sem - tmp);
- purple_debug_info("yahoo", "Got needed part of B cookie: %s\n",
- tmp2 ? tmp2 : "(null)");
- break;
- }
- }
- }
- }
-
- g_strfreev(splits);
- return tmp2;
-}
-
-static void yahoo_auth16_stage2(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *ret_data, size_t len, const gchar *error_message)
-{
- struct yahoo_auth_data *auth_data = user_data;
+ struct yahoo_auth_data *auth_data = _auth_data;
PurpleConnection *gc = auth_data->gc;
YahooData *yd = purple_connection_get_protocol_data(gc);
- purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage2\n");
+ int i;
+ gchar **splits;
+ int response_no = -1;
+ char *crumb = NULL;
+ char *crypt = NULL;
+ PurpleHttpCookieJar *cookiejar;
- yd->url_datas = g_slist_remove(yd->url_datas, url_data);
+ purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage2\n");
- if (error_message != NULL) {
+ if (!purple_http_response_is_successful(response)) {
+ const gchar *error_message = purple_http_response_get_error(response);
purple_debug_error("yahoo", "Login Failed, unable to retrieve stage 2 url: %s\n", error_message);
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message);
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message);
g_free(auth_data->seed);
g_free(auth_data);
return;
}
- else if (len > 0 && ret_data && *ret_data) {
- gchar **splits = g_strsplit(ret_data, "\r\n\r\n", -1), **split_data = NULL;
- int totalelements = 0;
- int response_no = -1;
- char *crumb = NULL;
- char *crypt = NULL;
-
- if(g_strv_length(splits) > 1) {
- yd->cookie_b = yahoo_auth16_get_cookie_b(splits[0]);
- split_data = g_strsplit(splits[1], "\r\n", -1);
- totalelements = g_strv_length(split_data);
- }
-
- if (totalelements >= 4) {
- int i;
-
- for(i = 0; i < totalelements; i++) {
- /* I'm not exactly a fan of the magic numbers, but it's obvious,
- * so no sense in wasting a bajillion vars or calls to strlen */
-
- if(g_ascii_isdigit(split_data[i][0])) {
- /* if the current line and the next line both start with numbers,
- * the current line is the length of the body, so skip. If not,
- * then the current line is the response code from the login process. */
- if(!g_ascii_isdigit(split_data[i + 1][0])) {
- response_no = strtol(split_data[i], NULL, 10);
- purple_debug_info("yahoo", "Got auth16 stage 2 response code: %d\n",
- response_no);
- }
- } else if(strncmp(split_data[i], "crumb=", 6) == 0) {
- crumb = g_strdup(&split_data[i][6]);
- if(purple_debug_is_unsafe())
- purple_debug_info("yahoo", "Got crumb: %s\n", crumb);
+ splits = g_strsplit(purple_http_response_get_data(response, NULL),
+ "\r\n", -1);
- } else if(strncmp(split_data[i], "Y=", 2) == 0) {
- yd->cookie_y = g_strdup(&split_data[i][2]);
+ cookiejar = purple_http_conn_get_cookie_jar(http_conn);
+ yd->cookie_b = g_strdup(purple_http_cookie_jar_get(cookiejar, "B"));
+ yd->cookie_t = g_strdup(purple_http_cookie_jar_get(cookiejar, "T"));
+ yd->cookie_y = g_strdup(purple_http_cookie_jar_get(cookiejar, "Y"));
- if(purple_debug_is_unsafe())
- purple_debug_info("yahoo", "Got Y cookie: %s\n", yd->cookie_y);
+ i = 0;
+ while (splits[i]) {
+ /* I'm not exactly a fan of the magic numbers, but it's obvious,
+ * so no sense in wasting a bajillion vars or calls to strlen */
- } else if(strncmp(split_data[i], "T=", 2) == 0) {
- yd->cookie_t = g_strdup(&split_data[i][2]);
+ if (i == 0 && g_ascii_isdigit(splits[i][0])) {
+ response_no = strtol(splits[i], NULL, 10);
+ purple_debug_info("yahoo", "Got auth16 stage 2 response code: %d\n",
+ response_no);
+ } else if (strncmp(splits[i], "crumb=", 6) == 0) {
+ crumb = g_strdup(&splits[i][6]);
- if(purple_debug_is_unsafe())
- purple_debug_info("yahoo", "Got T cookie: %s\n", yd->cookie_t);
- }
- }
+ if (purple_debug_is_unsafe())
+ purple_debug_info("yahoo", "Got crumb: %s\n", crumb);
}
+ i++;
+ }
- g_strfreev(splits);
- g_strfreev(split_data);
+ g_strfreev(splits);
- if (crumb == NULL)
- response_no = -1;
+ if (crumb == NULL)
+ response_no = -1;
- if(response_no != 0) {
- /* Some error in the login process */
- PurpleConnectionError error;
- char *error_reason = NULL;
+ if(response_no != 0) {
+ /* Some error in the login process */
+ PurpleConnectionError error;
+ char *error_reason = NULL;
- switch(response_no) {
- case -1:
- /* Some error in the received stream */
- error_reason = g_strdup(_("Received invalid data"));
- error = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
- break;
- case 100:
- /* Unknown error */
- error_reason = g_strdup(_("Unknown error"));
- error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
- break;
- default:
- /* if we have everything we need, why not try to login irrespective of response */
- if((crumb != NULL) && (yd->cookie_y != NULL) && (yd->cookie_t != NULL)) {
+ switch (response_no) {
+ case -1:
+ /* Some error in the received stream */
+ error_reason = g_strdup(_("Received invalid data"));
+ error = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
+ break;
+ case 100:
+ /* Unknown error */
+ error_reason = g_strdup(_("Unknown error"));
+ error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
+ break;
+ default:
+ /* if we have everything we need, why not try to login irrespective of response */
+ if ((crumb != NULL) && (yd->cookie_y != NULL) && (yd->cookie_t != NULL)) {
#if 0
- try_login_on_error = TRUE;
+ try_login_on_error = TRUE;
#endif
- break;
- }
- error_reason = g_strdup(_("Unknown error"));
- error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
break;
- }
- if(error_reason) {
- purple_debug_error("yahoo", "Authentication error: %s. "
- "Code %d\n", error_reason, response_no);
- purple_connection_error_reason(gc, error, error_reason);
- g_free(error_reason);
- g_free(auth_data->seed);
- g_free(auth_data);
- return;
- }
+ }
+ error_reason = g_strdup(_("Unknown error"));
+ error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
+ break;
+ }
+ if (error_reason) {
+ purple_debug_error("yahoo", "Authentication error: %s. "
+ "Code %d\n", error_reason, response_no);
+ purple_connection_error(gc, error, error_reason);
+ g_free(error_reason);
+ g_free(crumb);
+ g_free(auth_data->seed);
+ g_free(auth_data);
+ return;
}
-
- crypt = g_strconcat(crumb, auth_data->seed, NULL);
- yahoo_auth16_stage3(gc, crypt);
- g_free(crypt);
- g_free(crumb);
}
+
+ crypt = g_strconcat(crumb, auth_data->seed, NULL);
+ yahoo_auth16_stage3(gc, crypt);
+ g_free(crypt);
+ g_free(crumb);
g_free(auth_data->seed);
g_free(auth_data);
}
-static void yahoo_auth16_stage1_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *ret_data, size_t len, const gchar *error_message)
+static void yahoo_auth16_stage1_cb(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _auth_data)
{
- struct yahoo_auth_data *auth_data = user_data;
+ struct yahoo_auth_data *auth_data = _auth_data;
PurpleConnection *gc = auth_data->gc;
YahooData *yd = purple_connection_get_protocol_data(gc);
purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage1_cb\n");
- yd->url_datas = g_slist_remove(yd->url_datas, url_data);
-
- if (error_message != NULL) {
+ if (!purple_http_response_is_successful(response)) {
+ const gchar *error_message = purple_http_response_get_error(response);
purple_debug_error("yahoo", "Login Failed, unable to retrieve login url: %s\n", error_message);
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message);
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message);
g_free(auth_data->seed);
g_free(auth_data);
return;
- }
- else if (len > 0 && ret_data && *ret_data) {
+ } else {
PurpleAccount *account = purple_connection_get_account(gc);
- gchar **split_data = g_strsplit(ret_data, "\r\n", -1);
+ gchar **split_data = g_strsplit(purple_http_response_get_data(
+ response, NULL), "\r\n", -1);
int totalelements = 0;
int response_no = -1;
char *token = NULL;
@@ -2105,7 +2048,7 @@ static void yahoo_auth16_stage1_cb(PurpleUtilFetchUrlData *url_data, gpointer us
/* Password incorrect */
/* Set password to NULL. Avoids account locking. Brings dialog to enter password if clicked on Re-enable account */
if (!purple_account_get_remember_password(account))
- purple_account_set_password(account, NULL);
+ purple_account_set_password(account, NULL, NULL, NULL);
error_reason = g_strdup(_("Incorrect password"));
error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
break;
@@ -2146,7 +2089,7 @@ static void yahoo_auth16_stage1_cb(PurpleUtilFetchUrlData *url_data, gpointer us
}
purple_debug_error("yahoo", "Authentication error: %s. Code %d\n",
error_reason, response_no);
- purple_connection_error_reason(gc, error, error_reason);
+ purple_connection_error(gc, error, error_reason);
g_free(error_reason);
g_free(auth_data->seed);
g_free(auth_data);
@@ -2154,18 +2097,20 @@ static void yahoo_auth16_stage1_cb(PurpleUtilFetchUrlData *url_data, gpointer us
}
else {
/* OK to login, correct information provided */
- PurpleUtilFetchUrlData *url_data = NULL;
- char *url = NULL;
gboolean yahoojp = yahoo_is_japan(account);
- gboolean proxy_ssl = purple_account_get_bool(account, "proxy_ssl", FALSE);
-
- url = g_strdup_printf(yahoojp ? YAHOOJP_LOGIN_URL : YAHOO_LOGIN_URL, token);
- url_data = purple_util_fetch_url_request_len_with_account(
- proxy_ssl ? account : NULL, url, TRUE, YAHOO_CLIENT_USERAGENT,
- TRUE, NULL, TRUE, -1, yahoo_auth16_stage2, auth_data);
- if (url_data)
- yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
- g_free(url);
+ PurpleHttpRequest *req;
+
+ req = purple_http_request_new(NULL);
+ purple_http_request_set_url_printf(req,
+ yahoojp ? YAHOOJP_LOGIN_URL : YAHOO_LOGIN_URL,
+ token);
+ purple_http_request_header_set(req, "User-Agent",
+ YAHOO_CLIENT_USERAGENT);
+ purple_http_connection_set_add(yd->http_reqs,
+ purple_http_request(gc, req,
+ yahoo_auth16_stage2, auth_data));
+ purple_http_request_unref(req);
+
g_free(token);
}
}
@@ -2175,18 +2120,16 @@ static void yahoo_auth16_stage1(PurpleConnection *gc, const char *seed)
{
YahooData *yd = purple_connection_get_protocol_data(gc);
PurpleAccount *account = purple_connection_get_account(gc);
- PurpleUtilFetchUrlData *url_data = NULL;
+ PurpleHttpRequest *req;
struct yahoo_auth_data *auth_data = NULL;
- char *url = NULL;
char *encoded_username;
char *encoded_password;
gboolean yahoojp = yahoo_is_japan(account);
- gboolean proxy_ssl = purple_account_get_bool(account, "proxy_ssl", FALSE);
purple_debug_info("yahoo", "Authentication: In yahoo_auth16_stage1\n");
if(!purple_ssl_is_supported()) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, _("SSL support unavailable"));
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, _("SSL support unavailable"));
return;
}
@@ -2196,19 +2139,18 @@ static void yahoo_auth16_stage1(PurpleConnection *gc, const char *seed)
encoded_username = g_strdup(purple_url_encode(purple_account_get_username(purple_connection_get_account(gc))));
encoded_password = g_strdup(purple_url_encode(purple_connection_get_password(gc)));
- url = g_strdup_printf(yahoojp ? YAHOOJP_TOKEN_URL : YAHOO_TOKEN_URL,
- encoded_username, encoded_password, purple_url_encode(seed));
- g_free(encoded_password);
- g_free(encoded_username);
- url_data = purple_util_fetch_url_request_len_with_account(
- proxy_ssl ? account : NULL, url, TRUE,
- YAHOO_CLIENT_USERAGENT, TRUE, NULL, FALSE, -1,
- yahoo_auth16_stage1_cb, auth_data);
- if (url_data)
- yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
+ req = purple_http_request_new(NULL);
+ purple_http_request_set_url_printf(req,
+ yahoojp ? YAHOOJP_TOKEN_URL : YAHOO_TOKEN_URL,
+ encoded_username, encoded_password, purple_url_encode(seed));
+ purple_http_request_header_set(req, "User-Agent", YAHOO_CLIENT_USERAGENT);
+ purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc,
+ req, yahoo_auth16_stage1_cb, auth_data));
+ purple_http_request_unref(req);
- g_free(url);
+ purple_str_wipe(encoded_password);
+ g_free(encoded_username);
}
static void yahoo_process_auth(PurpleConnection *gc, struct yahoo_packet *pkt)
@@ -2250,8 +2192,9 @@ static void yahoo_process_auth(PurpleConnection *gc, struct yahoo_packet *pkt)
"authentication method. You will probably not be able "
"to successfully sign on to Yahoo. Check %s for updates."),
((ui_info && g_hash_table_lookup(ui_info, "website")) ? (char *)g_hash_table_lookup(ui_info, "website") : PURPLE_WEBSITE));
- purple_notify_error(gc, "", _("Failed Yahoo! Authentication"),
- buf);
+ purple_notify_error(gc, "",
+ _("Failed Yahoo! Authentication"), buf,
+ purple_request_cpar_from_connection(gc));
g_free(buf);
yahoo_auth16_stage1(gc, seed); /* Can't hurt to try it anyway. */
break;
@@ -2276,14 +2219,14 @@ static void ignore_buddy(PurpleBuddy *buddy) {
purple_account_remove_buddy(account, buddy, group);
purple_blist_remove_buddy(buddy);
- serv_add_deny(purple_account_get_connection(account), name);
+ purple_serv_add_deny(purple_account_get_connection(account), name);
g_free(name);
}
static void keep_buddy(PurpleBuddy *b)
{
- purple_privacy_deny_remove(purple_buddy_get_account(b),
+ purple_account_privacy_deny_remove(purple_buddy_get_account(b),
purple_buddy_get_name(b), 1);
}
@@ -2332,15 +2275,15 @@ static void yahoo_process_ignore(PurpleConnection *gc, struct yahoo_packet *pkt)
who, (ignore ? "ignoring" : "unignoring"));
if (ignore) {
- b = purple_find_buddy(gc->account, who);
+ b = purple_blist_find_buddy(purple_connection_get_account(gc), who);
g_snprintf(buf, sizeof(buf), _("You have tried to ignore %s, but the "
"user is on your buddy list. Clicking \"Yes\" "
"will remove and ignore the buddy."), who);
- purple_request_yes_no(gc, NULL, _("Ignore buddy?"), buf, 0,
- gc->account, who, NULL,
- b,
- G_CALLBACK(ignore_buddy),
- G_CALLBACK(keep_buddy));
+ purple_request_yes_no(gc, NULL,
+ _("Ignore buddy?"), buf, 0,
+ purple_request_cpar_from_connection(gc),
+ b, G_CALLBACK(ignore_buddy),
+ G_CALLBACK(keep_buddy));
break;
}
case 2:
@@ -2356,25 +2299,52 @@ static void yahoo_process_ignore(PurpleConnection *gc, struct yahoo_packet *pkt)
}
}
-static void yahoo_process_authresp(PurpleConnection *gc, struct yahoo_packet *pkt)
+#if TRY_WEBMESSENGER_LOGIN
+
+static gboolean
+yahoo_try_webmessenger_login(PurpleConnection *gc)
{
-#ifdef TRY_WEBMESSENGER_LOGIN
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
+ PurpleHttpRequest *req;
+
+ if (yd->wm)
+ return FALSE;
+
+ yd->wm = TRUE;
+ if (yd->fd >= 0)
+ close(yd->fd);
+ if (yd->inpa) {
+ purple_input_remove(yd->inpa);
+ yd->inpa = 0;
+ }
+
+ req = purple_http_request_new(WEBMESSENGER_URL);
+ purple_http_request_header_set(req, "User-Agent", "Purple/" VERSION);
+ purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc,
+ req, yahoo_login_page_cb, NULL));
+ purple_http_request_unref(req);
+
+ return TRUE;
+}
+
#endif /* TRY_WEBMESSENGER_LOGIN */
+
+static void yahoo_process_authresp(PurpleConnection *gc, struct yahoo_packet *pkt)
+{
GSList *l = pkt->hash;
int err = 0;
char *msg;
char *url = NULL;
char *fullmsg;
- PurpleAccount *account = gc->account;
+ PurpleAccount *account = purple_connection_get_account(gc);
PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
while (l) {
struct yahoo_pair *pair = l->data;
- if (pair->key == 66)
+ if (pair->key == 66) {
err = strtol(pair->value, NULL, 10);
- else if (pair->key == 20) {
+ } else if (pair->key == 20) {
if (g_utf8_validate(pair->value, -1, NULL)) {
url = pair->value;
} else {
@@ -2396,23 +2366,14 @@ static void yahoo_process_authresp(PurpleConnection *gc, struct yahoo_packet *pk
reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
break;
case 13:
-#ifdef TRY_WEBMESSENGER_LOGIN
- if (!yd->wm) {
- PurpleUtilFetchUrlData *url_data;
- yd->wm = TRUE;
- if (yd->fd >= 0)
- close(yd->fd);
- if (gc->inpa)
- purple_input_remove(gc->inpa);
- url_data = purple_util_fetch_url(WEBMESSENGER_URL, TRUE,
- "Purple/" VERSION, FALSE, yahoo_login_page_cb, gc);
- if (url_data != NULL)
- yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
+#if TRY_WEBMESSENGER_LOGIN
+ if (yahoo_try_webmessenger_login(gc))
return;
- }
+#else
+ purple_debug_info("yahoo", "Web messenger login is disabled\n");
#endif /* TRY_WEBMESSENGER_LOGIN */
if (!purple_account_get_remember_password(account))
- purple_account_set_password(account, NULL);
+ purple_account_set_password(account, NULL, NULL, NULL);
msg = g_strdup(_("Invalid username or password"));
reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
@@ -2443,7 +2404,7 @@ static void yahoo_process_authresp(PurpleConnection *gc, struct yahoo_packet *pk
else
fullmsg = g_strdup(msg);
- purple_connection_error_reason(gc, reason, fullmsg);
+ purple_connection_error(gc, reason, fullmsg);
g_free(msg);
g_free(fullmsg);
}
@@ -2458,7 +2419,7 @@ static void yahoo_process_addbuddy(PurpleConnection *gc, struct yahoo_packet *pk
char *buf;
YahooFriend *f;
GSList *l = pkt->hash;
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
YahooFederation fed = YAHOO_FEDERATION_NONE;
while (l) {
@@ -2531,8 +2492,9 @@ static void yahoo_process_addbuddy(PurpleConnection *gc, struct yahoo_packet *pk
decoded_group = yahoo_string_decode(gc, group, FALSE);
buf = g_strdup_printf(_("Unable to add buddy %s to group %s to the server list on account %s."),
who, decoded_group, purple_connection_get_display_name(gc));
- if (!purple_conv_present_error(who, purple_connection_get_account(gc), buf))
- purple_notify_error(gc, NULL, _("Unable to add buddy to server list"), buf);
+ if (!purple_conversation_present_error(who, purple_connection_get_account(gc), buf))
+ purple_notify_error(gc, NULL, _("Unable to add buddy to server list"), buf,
+ purple_request_cpar_from_connection(gc));
g_free(buf);
g_free(decoded_group);
g_free(who);
@@ -2542,11 +2504,13 @@ static void yahoo_process_addbuddy(PurpleConnection *gc, struct yahoo_packet *pk
static void yahoo_p2p_write_pkt(gint source, struct yahoo_packet *pkt)
{
size_t pkt_len;
+ gssize written;
guchar *raw_packet;
/*build the raw packet and send it to the host*/
pkt_len = yahoo_packet_build(pkt, 0, 0, 0, &raw_packet);
- if(write(source, raw_packet, pkt_len) != pkt_len)
+ written = write(source, raw_packet, pkt_len);
+ if (written < 0 || (gsize)written != pkt_len)
purple_debug_warning("yahoo","p2p: couldn't write to the source\n");
g_free(raw_packet);
}
@@ -2557,7 +2521,7 @@ static void yahoo_p2p_keepalive_cb(gpointer key, gpointer value, gpointer user_d
PurpleConnection *gc = user_data;
struct yahoo_packet *pkt_to_send;
PurpleAccount *account;
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
account = purple_connection_get_account(gc);
@@ -2576,7 +2540,7 @@ static void yahoo_p2p_keepalive_cb(gpointer key, gpointer value, gpointer user_d
static gboolean yahoo_p2p_keepalive(gpointer data)
{
PurpleConnection *gc = data;
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
g_hash_table_foreach(yd->peers, yahoo_p2p_keepalive_cb, gc);
@@ -2623,7 +2587,7 @@ static void yahoo_p2p_process_p2pfilexfer(gpointer data, gint source, struct yah
if(!(p2p_data = data))
return ;
- yd = p2p_data->gc->proto_data;
+ yd = purple_connection_get_protocol_data(p2p_data->gc);
/* lets see whats in the packet */
while (l) {
@@ -2713,7 +2677,7 @@ static void yahoo_p2p_read_pkt_cb(gpointer data, gint source, PurpleInputConditi
if(!(p2p_data = data))
return ;
- yd = p2p_data->gc->proto_data;
+ yd = purple_connection_get_protocol_data(p2p_data->gc);
len = read(source, buf, sizeof(buf));
if ((len < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
@@ -2805,7 +2769,7 @@ static void yahoo_p2p_server_send_connected_cb(gpointer data, gint source, Purpl
if(!(p2p_data = data))
return ;
- yd = p2p_data->gc->proto_data;
+ yd = purple_connection_get_protocol_data(p2p_data->gc);
acceptfd = accept(source, NULL, 0);
if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
@@ -2845,7 +2809,7 @@ static gboolean yahoo_cancel_p2p_server_listen_cb(gpointer data)
if(!(p2p_data = data))
return FALSE;
- yd = p2p_data->gc->proto_data;
+ yd = purple_connection_get_protocol_data(p2p_data->gc);
purple_debug_warning("yahoo","yahoo p2p server timeout, peer failed to connect\n");
yahoo_p2p_disconnect_destroy_data(data);
@@ -2866,7 +2830,7 @@ static void yahoo_p2p_server_listen_cb(int listenfd, gpointer data)
if(!(p2p_data = data))
return ;
- yd = p2p_data->gc->proto_data;
+ yd = purple_connection_get_protocol_data(p2p_data->gc);
yd->listen_data = NULL;
if(listenfd == -1) {
@@ -2894,7 +2858,7 @@ void yahoo_send_p2p_pkt(PurpleConnection *gc, const char *who, int val_13)
YahooFriend *f;
struct yahoo_packet *pkt;
PurpleAccount *account;
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_p2p_data *p2p_data;
const char *norm_username;
@@ -2958,7 +2922,7 @@ void yahoo_send_p2p_pkt(PurpleConnection *gc, const char *who, int val_13)
if (yd->listen_data)
purple_debug_warning("yahoo","p2p: Failed to create p2p server - server already exists\n");
else {
- yd->listen_data = purple_network_listen(YAHOO_PAGER_PORT_P2P, SOCK_STREAM, yahoo_p2p_server_listen_cb, p2p_data);
+ yd->listen_data = purple_network_listen(YAHOO_PAGER_PORT_P2P, AF_UNSPEC, SOCK_STREAM, TRUE, yahoo_p2p_server_listen_cb, p2p_data);
if (yd->listen_data == NULL)
purple_debug_warning("yahoo","p2p: Failed to created p2p server\n");
}
@@ -2975,7 +2939,7 @@ static void yahoo_p2p_init_cb(gpointer data, gint source, const gchar *error_mes
YahooData *yd;
p2p_data = data;
- yd = p2p_data->gc->proto_data;
+ yd = purple_connection_get_protocol_data(p2p_data->gc);
if(error_message != NULL) {
purple_debug_warning("yahoo","p2p: %s\n",error_message);
@@ -3181,7 +3145,7 @@ static void yahoo_process_audible(PurpleConnection *gc, struct yahoo_packet *pkt
purple_debug_misc("yahoo", "Warning, nonutf8 audible, ignoring!\n");
return;
}
- if (!purple_privacy_check(account, who)) {
+ if (!purple_account_privacy_check(account, who)) {
purple_debug_misc("yahoo", "Audible message from %s for %s dropped!\n",
purple_account_get_username(account), who);
return;
@@ -3192,10 +3156,10 @@ static void yahoo_process_audible(PurpleConnection *gc, struct yahoo_packet *pkt
char *buf = g_strdup_printf(_("[ Audible %s/%s/%s.swf ] %s"), YAHOO_AUDIBLE_URL, audible_locale[1], id, msg);
g_strfreev(audible_locale);
- serv_got_im(gc, who, buf, 0, time(NULL));
+ purple_serv_got_im(gc, who, buf, 0, time(NULL));
g_free(buf);
} else
- serv_got_im(gc, who, msg, 0, time(NULL));
+ purple_serv_got_im(gc, who, msg, 0, time(NULL));
}
static void yahoo_packet_process(PurpleConnection *gc, struct yahoo_packet *pkt)
@@ -3299,7 +3263,8 @@ static void yahoo_packet_process(PurpleConnection *gc, struct yahoo_packet *pkt)
yahoo_process_p2p(gc, pkt); /* P2PFILEXFER handled the same way as process_p2p */
yahoo_process_p2pfilexfer(gc, pkt); /* redundant ??, need to have a break now */
case YAHOO_SERVICE_FILETRANSFER:
- yahoo_process_filetransfer(gc, pkt);
+ purple_debug_error("yahoo", "Legacy file transfers are not "
+ "supported anymore.\n");
break;
case YAHOO_SERVICE_PEERTOPEER:
yahoo_process_p2p(gc, pkt);
@@ -3345,7 +3310,7 @@ static void yahoo_packet_process(PurpleConnection *gc, struct yahoo_packet *pkt)
static void yahoo_pending(gpointer data, gint source, PurpleInputCondition cond)
{
PurpleConnection *gc = data;
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
char buf[1024];
int len;
@@ -3360,15 +3325,15 @@ static void yahoo_pending(gpointer data, gint source, PurpleInputCondition cond)
tmp = g_strdup_printf(_("Lost connection with server: %s"),
g_strerror(errno));
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
g_free(tmp);
return;
} else if (len == 0) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Server closed the connection"));
return;
}
- gc->last_received = time(NULL);
+ purple_connection_update_last_received(gc);
yd->rxqueue = g_realloc(yd->rxqueue, len + yd->rxlen);
memcpy(yd->rxqueue + yd->rxlen, buf, len);
yd->rxlen += len;
@@ -3448,23 +3413,24 @@ static void yahoo_got_connected(gpointer data, gint source, const gchar *error_m
if (source < 0) {
gchar *tmp;
tmp = g_strdup_printf(_("Unable to connect: %s"), error_message);
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
g_free(tmp);
return;
}
- yd = gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
yd->fd = source;
pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, yd->current_status, yd->session_id);
- yahoo_packet_hash_str(pkt, 1, purple_normalize(gc->account, purple_account_get_username(purple_connection_get_account(gc))));
+ yahoo_packet_hash_str(pkt, 1, purple_normalize(purple_connection_get_account(gc), purple_account_get_username(purple_connection_get_account(gc))));
yahoo_packet_send_and_free(pkt, yd);
- gc->inpa = purple_input_add(yd->fd, PURPLE_INPUT_READ, yahoo_pending, gc);
+ yd->inpa = purple_input_add(yd->fd, PURPLE_INPUT_READ, yahoo_pending, gc);
}
-#ifdef TRY_WEBMESSENGER_LOGIN
+#if TRY_WEBMESSENGER_LOGIN
+
static void yahoo_got_web_connected(gpointer data, gint source, const gchar *error_message)
{
PurpleConnection *gc = data;
@@ -3474,160 +3440,71 @@ static void yahoo_got_web_connected(gpointer data, gint source, const gchar *err
if (source < 0) {
gchar *tmp;
tmp = g_strdup_printf(_("Unable to connect: %s"), error_message);
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
g_free(tmp);
return;
}
- yd = gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
yd->fd = source;
pkt = yahoo_packet_new(YAHOO_SERVICE_WEBLOGIN, YAHOO_STATUS_WEBLOGIN, yd->session_id);
yahoo_packet_hash(pkt, "sss", 0,
- purple_normalize(gc->account, purple_account_get_username(purple_connection_get_account(gc))),
- 1, purple_normalize(gc->account, purple_account_get_username(purple_connection_get_account(gc))),
+ purple_normalize(purple_connection_get_account(gc), purple_account_get_username(purple_connection_get_account(gc))),
+ 1, purple_normalize(purple_connection_get_account(gc), purple_account_get_username(purple_connection_get_account(gc))),
6, yd->auth);
yahoo_packet_send_and_free(pkt, yd);
g_free(yd->auth);
- gc->inpa = purple_input_add(yd->fd, PURPLE_INPUT_READ, yahoo_pending, gc);
+ yd->inpa = purple_input_add(yd->fd, PURPLE_INPUT_READ, yahoo_pending, gc);
}
-static void yahoo_web_pending(gpointer data, gint source, PurpleInputCondition cond)
+static void
+yahoo_login_page_got(PurpleHttpConnection *hc, PurpleHttpResponse *resp,
+ gpointer _unused)
{
- PurpleConnection *gc = data;
+ PurpleConnection *gc = purple_http_conn_get_purple_connection(hc);
+ YahooData *yd = purple_connection_get_protocol_data(gc);
PurpleAccount *account = purple_connection_get_account(gc);
- YahooData *yd = gc->proto_data;
- char bufread[2048], *i = bufread, *buf = bufread;
- int len;
- GString *s;
-
- len = read(source, bufread, sizeof(bufread) - 1);
-
- if (len < 0) {
- gchar *tmp;
-
- if (errno == EAGAIN)
- /* No worries */
- return;
-
- tmp = g_strdup_printf(_("Lost connection with server: %s"),
- g_strerror(errno));
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
- g_free(tmp);
- return;
- } else if (len == 0) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Server closed the connection"));
+ PurpleHttpCookieJar *cjar;
+ GString *auth_s;
+ const gchar *cookie;
+
+ if (purple_http_response_get_code(resp) != 302) {
+ purple_connection_error(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Unable to connect"));
return;
}
- if (yd->rxlen > 0 || !g_strstr_len(buf, len, "\r\n\r\n")) {
- yd->rxqueue = g_realloc(yd->rxqueue, yd->rxlen + len + 1);
- memcpy(yd->rxqueue + yd->rxlen, buf, len);
- yd->rxlen += len;
- i = buf = (char *)yd->rxqueue;
- len = yd->rxlen;
- }
- buf[len] = '\0';
-
- if ((strncmp(buf, "HTTP/1.0 302", strlen("HTTP/1.0 302")) &&
- strncmp(buf, "HTTP/1.1 302", strlen("HTTP/1.1 302")))) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Received unexpected HTTP response from server"));
- purple_debug_misc("yahoo", "Unexpected HTTP response: %s\n", buf);
- return;
- }
-
- s = g_string_sized_new(len);
-
- while ((i = strstr(i, "Set-Cookie: "))) {
-
- i += strlen("Set-Cookie: ");
- for (;*i != ';' && *i != '\0'; i++)
- g_string_append_c(s, *i);
-
- g_string_append(s, "; ");
- /* Should these cookies be included too when trying for xfer?
- * It seems to work without these
- */
- }
+ auth_s = g_string_new(NULL);
+ cjar = purple_http_conn_get_cookie_jar(hc);
+ cookie = purple_http_cookie_jar_get(cjar, "B");
+ if (cookie)
+ g_string_append_printf(auth_s, "B=%s; ", cookie);
+ cookie = purple_http_cookie_jar_get(cjar, "T");
+ if (cookie)
+ g_string_append_printf(auth_s, "T=%s; ", cookie);
+ cookie = purple_http_cookie_jar_get(cjar, "Y");
+ if (cookie)
+ g_string_append_printf(auth_s, "Y=%s; ", cookie);
- yd->auth = g_string_free(s, FALSE);
- purple_input_remove(gc->inpa);
- close(source);
- g_free(yd->rxqueue);
- yd->rxqueue = NULL;
- yd->rxlen = 0;
+ yd->auth = g_string_free(auth_s, FALSE);
/* Now we have our cookies to login with. I'll go get the milk. */
- if (purple_proxy_connect(gc, account, "wcs2.msg.dcn.yahoo.com",
- purple_account_get_int(account, "port", YAHOO_PAGER_PORT),
- yahoo_got_web_connected, gc) == NULL) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Unable to connect"));
- return;
- }
-}
-
-static void yahoo_got_cookies_send_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
- PurpleConnection *gc;
- YahooData *yd;
- int written, remaining;
-
- gc = data;
- yd = gc->proto_data;
-
- remaining = strlen(yd->auth) - yd->auth_written;
- written = write(source, yd->auth + yd->auth_written, remaining);
- if (written < 0 && errno == EAGAIN)
- written = 0;
- else if (written <= 0) {
- gchar *tmp;
- g_free(yd->auth);
- yd->auth = NULL;
- if (gc->inpa)
- purple_input_remove(gc->inpa);
- gc->inpa = 0;
- tmp = g_strdup_printf(_("Lost connection with %s: %s"),
- "login.yahoo.com:80", g_strerror(errno));
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
- g_free(tmp);
- return;
- }
-
- if (written < remaining) {
- yd->auth_written += written;
- return;
- }
-
- g_free(yd->auth);
- yd->auth = NULL;
- yd->auth_written = 0;
- purple_input_remove(gc->inpa);
- gc->inpa = purple_input_add(source, PURPLE_INPUT_READ, yahoo_web_pending, gc);
-}
-
-static void yahoo_got_cookies(gpointer data, gint source, const gchar *error_message)
-{
- PurpleConnection *gc = data;
-
- if (source < 0) {
- gchar *tmp;
- tmp = g_strdup_printf(_("Unable to establish a connection with %s: %s"),
- "login.yahoo.com:80", error_message);
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
- g_free(tmp);
- return;
- }
-
- if (gc->inpa == 0)
+ /* XXX: wcs2.msg.dcn.yahoo.com is down, so I used
+ * YAHOO_PAGER_HOST_FALLBACK, but I'm not sure, if it is the correct
+ * host.
+ */
+ if (purple_proxy_connect(gc, account, YAHOO_PAGER_HOST_FALLBACK,
+ purple_account_get_int(account, "port", YAHOO_PAGER_PORT),
+ yahoo_got_web_connected, gc) == NULL)
{
- gc->inpa = purple_input_add(source, PURPLE_INPUT_WRITE,
- yahoo_got_cookies_send_cb, gc);
- yahoo_got_cookies_send_cb(gc, source, PURPLE_INPUT_WRITE);
+ purple_connection_error(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Unable to connect"));
+ return;
}
}
@@ -3682,48 +3559,46 @@ static GHashTable *yahoo_login_page_hash(const char *buf, size_t len)
}
static void
-yahoo_login_page_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
- const gchar *url_text, size_t len, const gchar *error_message)
+yahoo_login_page_cb(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _unused)
{
- PurpleConnection *gc = (PurpleConnection *)user_data;
+ PurpleConnection *gc = purple_http_conn_get_purple_connection(http_conn);
PurpleAccount *account = purple_connection_get_account(gc);
- YahooData *yd = gc->proto_data;
- const char *sn = purple_account_get_username(account);
+ YahooData *yd = purple_connection_get_protocol_data(gc);
const char *pass = purple_connection_get_password(gc);
- GHashTable *hash = yahoo_login_page_hash(url_text, len);
- GString *url = g_string_new("GET http://login.yahoo.com/config/login?login=");
+ size_t len;
+ const gchar *got_data;
+ GHashTable *hash;
+ GString *url;
char md5[33], *hashp = md5, *chal;
int i;
PurpleCipher *cipher;
- PurpleCipherContext *context;
guchar digest[16];
+ PurpleHttpRequest *req;
- yd->url_datas = g_slist_remove(yd->url_datas, url_data);
-
- if (error_message != NULL)
+ if (!purple_http_response_is_successful(response))
{
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- error_message);
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_http_response_get_error(response));
return;
}
- url = g_string_append(url, sn);
- url = g_string_append(url, "&passwd=");
+ got_data = purple_http_response_get_data(response, &len);
+ hash = yahoo_login_page_hash(got_data, len);
- cipher = purple_ciphers_find_cipher("md5");
- context = purple_cipher_context_new(cipher, NULL);
+ cipher = purple_md5_cipher_new();
- purple_cipher_context_append(context, (const guchar *)pass, strlen(pass));
- purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
+ purple_cipher_append(cipher, (const guchar *)pass, strlen(pass));
+ purple_cipher_digest(cipher, digest, sizeof(digest));
for (i = 0; i < 16; ++i) {
g_snprintf(hashp, 3, "%02x", digest[i]);
hashp += 2;
}
chal = g_strconcat(md5, g_hash_table_lookup(hash, ".challenge"), NULL);
- purple_cipher_context_reset(context, NULL);
- purple_cipher_context_append(context, (const guchar *)chal, strlen(chal));
- purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
+ purple_cipher_reset(cipher);
+ purple_cipher_append(cipher, (const guchar *)chal, strlen(chal));
+ purple_cipher_digest(cipher, digest, sizeof(digest));
hashp = md5;
for (i = 0; i < 16; ++i) {
g_snprintf(hashp, 3, "%02x", digest[i]);
@@ -3733,9 +3608,9 @@ yahoo_login_page_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
* I dunno why this is here and commented out.. but in case it's needed
* I updated it..
- purple_cipher_context_reset(context, NULL);
- purple_cipher_context_append(context, md5, strlen(md5));
- purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
+ purple_cipher_reset(cipher);
+ purple_cipher_append(cipher, md5, strlen(md5));
+ purple_cipher_digest(cipher, sizeof(digest), digest, NULL);
hashp = md5;
for (i = 0; i < 16; ++i) {
g_snprintf(hashp, 3, "%02x", digest[i]);
@@ -3744,30 +3619,32 @@ yahoo_login_page_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
*/
g_free(chal);
- url = g_string_append(url, md5);
+ url = g_string_new(NULL);
+ g_string_printf(url, "http://login.yahoo.com/config/login?login=%s&passwd=%s", purple_account_get_username(account), md5);
g_hash_table_foreach(hash, (GHFunc)yahoo_login_page_hash_iter, url);
+ url = g_string_append(url, "&.hash=1&.md5=1");
- url = g_string_append(url, "&.hash=1&.md5=1 HTTP/1.1\r\n"
- "Host: login.yahoo.com\r\n\r\n");
g_hash_table_destroy(hash);
- yd->auth = g_string_free(url, FALSE);
- if (purple_proxy_connect(gc, account, "login.yahoo.com", 80, yahoo_got_cookies, gc) == NULL) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Unable to connect"));
- return;
- }
+ g_object_unref(cipher);
- purple_cipher_context_destroy(context);
+ req = purple_http_request_new(g_string_free(url, FALSE));
+ purple_http_request_set_max_redirects(req, 0);
+ purple_http_connection_set_add(yd->http_reqs,
+ purple_http_request(gc, req, yahoo_login_page_got, NULL));
+ purple_http_request_unref(req);
}
+
#endif /* TRY_WEBMESSENGER_LOGIN */
static void yahoo_picture_check(PurpleAccount *account)
{
PurpleConnection *gc = purple_account_get_connection(account);
- PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account);
+ PurpleImage *img = purple_buddy_icons_find_account_icon(account);
yahoo_set_buddy_icon(gc, img);
- purple_imgstore_unref(img);
+
+ if (img)
+ g_object_unref(img);
}
static int get_yahoo_status_from_purple_status(PurpleStatus *status)
@@ -3814,35 +3691,34 @@ static int get_yahoo_status_from_purple_status(PurpleStatus *status)
}
}
-static void yahoo_got_pager_server(PurpleUtilFetchUrlData *url_data,
- gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
+static void yahoo_got_pager_server(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _yd)
{
- YahooData *yd = user_data;
+ YahooData *yd = _yd;
PurpleConnection *gc = yd->gc;
PurpleAccount *a = purple_connection_get_account(gc);
gchar **strings = NULL, *cs_server = NULL;
int port = purple_account_get_int(a, "port", YAHOO_PAGER_PORT);
int stringslen = 0;
+ const gchar *got_data;
- yd->url_datas = g_slist_remove(yd->url_datas, url_data);
-
- if(error_message != NULL || len == 0) {
- purple_debug_error("yahoo", "Unable to retrieve server info. %"
- G_GSIZE_FORMAT " bytes retrieved with error message: %s\n", len,
- error_message ? error_message : "(null)");
+ if (!purple_http_response_is_successful(response)) {
+ purple_debug_error("yahoo", "Unable to retrieve server info: %s\n",
+ purple_http_response_get_error(response));
if(yahoo_is_japan(a)) { /* We don't know fallback hosts for Yahoo Japan :( */
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to connect: The server returned an empty response."));
} else {
if(purple_proxy_connect(gc, a, YAHOO_PAGER_HOST_FALLBACK, port,
yahoo_got_connected, gc) == NULL) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to connect"));
}
}
} else {
- strings = g_strsplit(url_text, "\r\n", -1);
+ got_data = purple_http_response_get_data(response, NULL);
+ strings = g_strsplit(got_data, "\r\n", -1);
if((stringslen = g_strv_length(strings)) > 1) {
int i;
@@ -3859,20 +3735,20 @@ static void yahoo_got_pager_server(PurpleUtilFetchUrlData *url_data,
if(cs_server) { /* got an address; get on with connecting */
if(purple_proxy_connect(gc, a, cs_server, port, yahoo_got_connected, gc) == NULL)
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to connect"));
} else {
purple_debug_error("yahoo", "No CS address retrieved! Server "
- "response:\n%s\n", url_text ? url_text : "(null)");
+ "response:\n%s\n", got_data);
if(yahoo_is_japan(a)) { /* We don't know fallback hosts for Yahoo Japan :( */
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to connect: The server's response did not contain "
"the necessary information"));
} else
if(purple_proxy_connect(gc, a, YAHOO_PAGER_HOST_FALLBACK, port,
yahoo_got_connected, gc) == NULL) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to connect"));
}
}
@@ -3884,13 +3760,15 @@ static void yahoo_got_pager_server(PurpleUtilFetchUrlData *url_data,
void yahoo_login(PurpleAccount *account) {
PurpleConnection *gc = purple_account_get_connection(account);
- YahooData *yd = gc->proto_data = g_new0(YahooData, 1);
+ PurpleHttpRequest *req;
+ YahooData *yd = g_new0(YahooData, 1);
PurpleStatus *status = purple_account_get_active_status(account);
- gboolean use_whole_url = yahoo_account_use_http_proxy(gc);
- gboolean proxy_ssl = purple_account_get_bool(account, "proxy_ssl", FALSE);
- PurpleUtilFetchUrlData *url_data;
- gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_NO_BGCOLOR | PURPLE_CONNECTION_NO_URLDESC;
+ purple_connection_set_protocol_data(gc, yd);
+ purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_HTML |
+ PURPLE_CONNECTION_FLAG_NO_BGCOLOR |
+ PURPLE_CONNECTION_FLAG_NO_URLDESC |
+ PURPLE_CONNECTION_FLAG_NO_IMAGES);
purple_connection_update_progress(gc, _("Connecting"), 1, 2);
@@ -3902,7 +3780,8 @@ void yahoo_login(PurpleAccount *account) {
yd->fd = -1;
yd->txhandler = 0;
/* TODO: Is there a good grow size for the buffer? */
- yd->txbuf = purple_circ_buffer_new(0);
+ yd->txbuf = purple_circular_buffer_new(0);
+ yd->http_reqs = purple_http_connection_set_new();
yd->friends = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, yahoo_friend_free);
yd->imvironments = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
yd->xfer_peer_idstring_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
@@ -3921,44 +3800,41 @@ void yahoo_login(PurpleAccount *account) {
/* Get the pager server. Actually start connecting in the callback since we
* must have the contents of the HTTP response to proceed. */
- url_data = purple_util_fetch_url_request_len_with_account(
- proxy_ssl ? purple_connection_get_account(gc) : NULL,
- yd->jp ? YAHOOJP_PAGER_HOST_REQ_URL : YAHOO_PAGER_HOST_REQ_URL,
- use_whole_url ? TRUE : FALSE,
- YAHOO_CLIENT_USERAGENT, FALSE, NULL, FALSE, -1,
- yahoo_got_pager_server, yd);
- if (url_data)
- yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
+ req = purple_http_request_new(yd->jp ? YAHOOJP_PAGER_HOST_REQ_URL :
+ YAHOO_PAGER_HOST_REQ_URL);
+ purple_http_request_header_set(req, "User-Agent", YAHOO_CLIENT_USERAGENT);
+ purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc,
+ req, yahoo_got_pager_server, yd));
+ purple_http_request_unref(req);
return;
}
void yahoo_close(PurpleConnection *gc) {
- YahooData *yd = (YahooData *)gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
GSList *l;
- if (gc->inpa)
- purple_input_remove(gc->inpa);
-
- while (yd->url_datas) {
- purple_util_fetch_url_cancel(yd->url_datas->data);
- yd->url_datas = g_slist_delete_link(yd->url_datas, yd->url_datas);
+ if (yd->inpa) {
+ purple_input_remove(yd->inpa);
+ yd->inpa = 0;
}
+ purple_http_connection_set_destroy(yd->http_reqs);
+ yd->http_reqs = NULL;
+
for (l = yd->confs; l; l = l->next) {
- PurpleConversation *conv = l->data;
+ PurpleChatConversation *conv = l->data;
+ GList *users;
- yahoo_conf_leave(yd, purple_conversation_get_name(conv),
- purple_connection_get_display_name(gc),
- purple_conv_chat_get_users(PURPLE_CONV_CHAT(conv)));
+ users = purple_chat_conversation_get_users(conv);
+ yahoo_conf_leave(yd,
+ purple_conversation_get_name(PURPLE_CONVERSATION(conv)),
+ purple_connection_get_display_name(gc), users);
+ g_list_free(users);
}
g_slist_free(yd->confs);
- for (l = yd->cookies; l; l = l->next) {
- g_free(l->data);
- l->data=NULL;
- }
- g_slist_free(yd->cookies);
+ g_slist_free_full(yd->cookies, g_free);
yd->chat_online = FALSE;
if (yd->in_chat)
@@ -3994,7 +3870,7 @@ void yahoo_close(PurpleConnection *gc) {
if (yd->txhandler)
purple_input_remove(yd->txhandler);
- purple_circ_buffer_destroy(yd->txbuf);
+ g_object_unref(G_OBJECT(yd->txbuf));
if (yd->fd >= 0)
close(yd->fd);
@@ -4003,8 +3879,7 @@ void yahoo_close(PurpleConnection *gc) {
yd->rxlen = 0;
g_free(yd->picture_url);
- if (yd->buddy_icon_connect_data)
- purple_proxy_connect_cancel(yd->buddy_icon_connect_data);
+ purple_http_conn_cancel(yd->picture_upload_hc);
if (yd->picture_upload_todo)
yahoo_buddy_icon_upload_data_free(yd->picture_upload_todo);
if (yd->ycht)
@@ -4023,7 +3898,7 @@ void yahoo_close(PurpleConnection *gc) {
g_free(yd->current_list15_grp);
g_free(yd);
- gc->proto_data = NULL;
+ purple_connection_set_protocol_data(gc, NULL);
}
const char *yahoo_list_icon(PurpleAccount *a, PurpleBuddy *b)
@@ -4040,7 +3915,7 @@ const char *yahoo_list_emblem(PurpleBuddy *b)
if (!b || !(account = purple_buddy_get_account(b)) ||
!(gc = purple_account_get_connection(account)) ||
- !gc->proto_data)
+ !purple_connection_get_protocol_data(gc))
return NULL;
f = yahoo_friend_find(gc, purple_buddy_get_name(b));
@@ -4101,11 +3976,11 @@ static void yahoo_initiate_conference(PurpleBlistNode *node, gpointer data) {
YahooData *yd;
int id;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *) node;
gc = purple_account_get_connection(purple_buddy_get_account(buddy));
- yd = gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
id = yd->conf_id;
components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
@@ -4141,7 +4016,7 @@ static void yahoo_game(PurpleBlistNode *node, gpointer data) {
char url[256];
YahooFriend *f;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *) node;
gc = purple_account_get_connection(purple_buddy_get_account(buddy));
@@ -4249,22 +4124,12 @@ void yahoo_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolea
if (f && full) {
YahooPersonalDetails *ypd = &f->ypd;
- int i;
- struct {
- char *id;
- char *text;
- char *value;
- } yfields[] = {
- {"hp", N_("Home Phone Number"), ypd->phone.home},
- {"wp", N_("Work Phone Number"), ypd->phone.work},
- {"mo", N_("Mobile Phone Number"), ypd->phone.mobile},
- {NULL, NULL, NULL}
- };
- for (i = 0; yfields[i].id; i++) {
- if (!yfields[i].value || !*yfields[i].value)
- continue;
- purple_notify_user_info_add_pair(user_info, _(yfields[i].text), yfields[i].value);
- }
+ if (ypd->phone.home && *ypd->phone.home)
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Home Phone Number"), ypd->phone.home);
+ if (ypd->phone.work && *ypd->phone.work)
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Work Phone Number"), ypd->phone.work);
+ if (ypd->phone.mobile && *ypd->phone.mobile)
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Mobile Phone Number"), ypd->phone.mobile);
}
}
@@ -4273,12 +4138,12 @@ static void yahoo_addbuddyfrommenu_cb(PurpleBlistNode *node, gpointer data)
PurpleBuddy *buddy;
PurpleConnection *gc;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *) node;
gc = purple_account_get_connection(purple_buddy_get_account(buddy));
- yahoo_add_buddy(gc, buddy, NULL);
+ yahoo_add_buddy(gc, buddy, NULL, NULL);
}
@@ -4287,7 +4152,7 @@ static void yahoo_chat_goto_menu(PurpleBlistNode *node, gpointer data)
PurpleBuddy *buddy;
PurpleConnection *gc;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *) node;
gc = purple_account_get_connection(purple_buddy_get_account(buddy));
@@ -4298,7 +4163,7 @@ static void yahoo_chat_goto_menu(PurpleBlistNode *node, gpointer data)
static GList *build_presence_submenu(YahooFriend *f, PurpleConnection *gc) {
GList *m = NULL;
PurpleMenuAction *act;
- YahooData *yd = (YahooData *) gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
if (yd->current_status == YAHOO_STATUS_INVISIBLE) {
if (f->presence != YAHOO_PRESENCE_ONLINE) {
@@ -4342,6 +4207,8 @@ static void yahoo_doodle_blist_node(PurpleBlistNode *node, gpointer data)
yahoo_doodle_initiate(gc, purple_buddy_get_name(b));
}
+#if 0
+/* XXX: it doesn't seems to work */
static void
yahoo_userinfo_blist_node(PurpleBlistNode *node, gpointer data)
{
@@ -4351,6 +4218,7 @@ yahoo_userinfo_blist_node(PurpleBlistNode *node, gpointer data)
yahoo_set_userinfo_for_buddy(gc, b);
}
+#endif
static GList *yahoo_buddy_menu(PurpleBuddy *buddy)
{
@@ -4358,7 +4226,7 @@ static GList *yahoo_buddy_menu(PurpleBuddy *buddy)
PurpleMenuAction *act;
PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy));
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
static char buf2[1024];
YahooFriend *f;
@@ -4421,10 +4289,13 @@ static GList *yahoo_buddy_menu(PurpleBuddy *buddy)
m = g_list_append(m, act);
}
+#if 0
+ /* XXX: it doesn't seems to work */
act = purple_menu_action_new(_("Set User Info..."),
PURPLE_CALLBACK(yahoo_userinfo_blist_node),
NULL, NULL);
m = g_list_append(m, act);
+#endif
}
return m;
@@ -4432,7 +4303,7 @@ static GList *yahoo_buddy_menu(PurpleBuddy *buddy)
GList *yahoo_blist_node_menu(PurpleBlistNode *node)
{
- if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ if(PURPLE_IS_BUDDY(node)) {
return yahoo_buddy_menu((PurpleBuddy *) node);
} else {
return NULL;
@@ -4441,8 +4312,8 @@ GList *yahoo_blist_node_menu(PurpleBlistNode *node)
static void yahoo_act_id(PurpleConnection *gc, PurpleRequestFields *fields)
{
- YahooData *yd = gc->proto_data;
- const char *name = yd->profiles[purple_request_fields_get_choice(fields, "id")];
+ YahooData *yd = purple_connection_get_protocol_data(gc);
+ const char *name = yd->profiles[GPOINTER_TO_INT(purple_request_fields_get_choice(fields, "id"))];
struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_IDACT, YAHOO_STATUS_AVAILABLE, yd->session_id);
yahoo_packet_hash_str(pkt, 3, name);
@@ -4452,33 +4323,32 @@ static void yahoo_act_id(PurpleConnection *gc, PurpleRequestFields *fields)
}
static void
-yahoo_get_inbox_token_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
- const gchar *token, size_t len, const gchar *error_message)
+yahoo_get_inbox_token_cb(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _unused)
{
- PurpleConnection *gc = user_data;
- gboolean set_cookie = FALSE;
+ PurpleConnection *gc =
+ purple_http_conn_get_purple_connection(http_conn);
gchar *url;
- YahooData *yd = gc->proto_data;
-
- g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc));
+ YahooData *yd = purple_connection_get_protocol_data(gc);
- yd->url_datas = g_slist_remove(yd->url_datas, url_data);
+ PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
- if (error_message != NULL)
- purple_debug_error("yahoo", "Requesting mail login token failed: %s\n", error_message);
- else if (len > 0 && token && *token) {
- /* Should we not be hardcoding the rd url? */
+ if (!purple_http_response_is_successful(response)) {
+ purple_debug_error("yahoo",
+ "Requesting mail login token failed: %s\n",
+ purple_http_response_get_error(response));
+ url = g_strdup(yd->jp ? YAHOOJP_MAIL_URL : YAHOO_MAIL_URL);
+ } else {
+ /* Should we not be hardcoding the rd url? */
+ gchar *token;
+ token = g_strdup(purple_http_response_get_data(response, NULL));
+ g_strstrip(token);
url = g_strdup_printf(
"http://login.yahoo.com/config/reset_cookies_token?"
".token=%s"
- "&.done=http://us.rd.yahoo.com/messenger/client/%%3fhttp://mail.yahoo.com/",
- token);
- set_cookie = TRUE;
- }
-
- if (!set_cookie) {
- purple_debug_error("yahoo", "No mail login token; forwarding to login screen.\n");
- url = g_strdup(yd->jp ? YAHOOJP_MAIL_URL : YAHOO_MAIL_URL);
+ "&.done=http://us.rd.yahoo.com/messenger/client/%%3f"
+ "http://mail.yahoo.com/", token);
+ purple_str_wipe(token);
}
/* Open the mailbox with the parsed url data */
@@ -4494,43 +4364,31 @@ static void yahoo_show_inbox(PurplePluginAction *action)
/* XXX I have no idea how this will work with Yahoo! Japan. */
PurpleConnection *gc = action->context;
- YahooData *yd = gc->proto_data;
-
- PurpleUtilFetchUrlData *url_data;
- const char* base_url = "http://login.yahoo.com";
- /* use whole URL if using HTTP Proxy */
- gboolean use_whole_url = yahoo_account_use_http_proxy(gc);
- gchar *request = g_strdup_printf(
- "POST %s/config/cookie_token HTTP/1.0\r\n"
- "Cookie: T=%s; path=/; domain=.yahoo.com; Y=%s;\r\n"
- "User-Agent: " YAHOO_CLIENT_USERAGENT "\r\n"
- "Host: login.yahoo.com\r\n"
- "Content-Length: 0\r\n\r\n",
- use_whole_url ? base_url : "",
- yd->cookie_t, yd->cookie_y);
-
- url_data = purple_util_fetch_url_request_len_with_account(
- purple_connection_get_account(gc), base_url, use_whole_url,
- YAHOO_CLIENT_USERAGENT, TRUE, request, FALSE, -1,
- yahoo_get_inbox_token_cb, gc);
-
- g_free(request);
-
- if (url_data != NULL)
- yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
- else {
- const char *yahoo_mail_url = (yd->jp ? YAHOOJP_MAIL_URL : YAHOO_MAIL_URL);
- purple_debug_error("yahoo",
- "Unable to request mail login token; forwarding to login screen.");
- purple_notify_uri(gc, yahoo_mail_url);
- }
+ YahooData *yd = purple_connection_get_protocol_data(gc);
+ PurpleHttpRequest *req;
+ PurpleHttpCookieJar *cookiejar;
+
+ req = purple_http_request_new(
+ "https://login.yahoo.com/config/cookie_token");
+ purple_http_request_set_method(req, "POST");
+ purple_http_request_header_set(req, "User-Agent",
+ YAHOO_CLIENT_USERAGENT);
+ cookiejar = purple_http_request_get_cookie_jar(req);
+ purple_http_cookie_jar_set(cookiejar, "T", yd->cookie_t);
+ purple_http_cookie_jar_set(cookiejar, "Y", yd->cookie_y);
+ purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc,
+ req, yahoo_get_inbox_token_cb, NULL));
+ purple_http_request_unref(req);
}
+#if 0
+/* XXX: it doesn't seems to work */
static void
yahoo_set_userinfo_fn(PurplePluginAction *action)
{
yahoo_set_userinfo(action->context);
}
+#endif
static void yahoo_show_act_id(PurplePluginAction *action)
{
@@ -4549,17 +4407,16 @@ static void yahoo_show_act_id(PurplePluginAction *action)
purple_request_field_group_add_field(group, field);
for (iter = 0; yd->profiles[iter]; iter++) {
- purple_request_field_choice_add(field, yd->profiles[iter]);
+ purple_request_field_choice_add(field, yd->profiles[iter], GINT_TO_POINTER(iter));
if (purple_strequal(yd->profiles[iter], name))
- purple_request_field_choice_set_default_value(field, iter);
+ purple_request_field_choice_set_default_value(field, GINT_TO_POINTER(iter));
}
purple_request_fields(gc, NULL, _("Select the ID you want to activate"), NULL,
fields,
_("OK"), G_CALLBACK(yahoo_act_id),
_("Cancel"), NULL,
- purple_connection_get_account(gc), NULL, NULL,
- gc);
+ purple_request_cpar_from_connection(gc), gc);
}
static void yahoo_show_chat_goto(PurplePluginAction *action)
@@ -4569,7 +4426,7 @@ static void yahoo_show_chat_goto(PurplePluginAction *action)
"", FALSE, FALSE, NULL,
_("OK"), G_CALLBACK(yahoo_chat_goto),
_("Cancel"), NULL,
- purple_connection_get_account(gc), NULL, NULL,
+ purple_request_cpar_from_connection(gc),
gc);
}
@@ -4577,9 +4434,12 @@ GList *yahoo_actions(PurplePlugin *plugin, gpointer context) {
GList *m = NULL;
PurplePluginAction *act;
+#if 0
+ /* XXX: it doesn't seems to work */
act = purple_plugin_action_new(_("Set User Info..."),
yahoo_set_userinfo_fn);
m = g_list_append(m, act);
+#endif
act = purple_plugin_action_new(_("Activate ID..."),
yahoo_show_act_id);
@@ -4603,56 +4463,54 @@ struct yahoo_sms_carrier_cb_data {
char *what;
};
-static void yahoo_get_sms_carrier_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
- const gchar *webdata, size_t len, const gchar *error_message)
+static void yahoo_get_sms_carrier_cb(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _sms_cb_data)
{
- struct yahoo_sms_carrier_cb_data *sms_cb_data = user_data;
+ struct yahoo_sms_carrier_cb_data *sms_cb_data = _sms_cb_data;
PurpleConnection *gc = sms_cb_data->gc;
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
char *status = NULL;
char *carrier = NULL;
PurpleAccount *account = purple_connection_get_account(gc);
- PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, sms_cb_data->who, account);
+ PurpleIMConversation *im = purple_conversations_find_im_with_account(sms_cb_data->who, account);
- yd->url_datas = g_slist_remove(yd->url_datas, url_data);
-
- if (error_message != NULL) {
- purple_conversation_write(conv, NULL, _("Can't send SMS. Unable to obtain mobile carrier."), PURPLE_MESSAGE_SYSTEM, time(NULL));
+ if (!purple_http_response_is_successful(response)) {
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
+ _("Can't send SMS. Unable to obtain mobile carrier."), 0);
g_free(sms_cb_data->who);
g_free(sms_cb_data->what);
g_free(sms_cb_data);
return ;
- }
- else if (len > 0 && webdata && *webdata) {
- xmlnode *validate_data_root = xmlnode_from_str(webdata, -1);
- xmlnode *validate_data_child = xmlnode_get_child(validate_data_root, "mobile_no");
- const char *mobile_no = xmlnode_get_attrib(validate_data_child, "msisdn");
+ } else {
+ const gchar *got_data = purple_http_response_get_data(response, NULL);
+ PurpleXmlNode *validate_data_root = purple_xmlnode_from_str(got_data, -1);
+ PurpleXmlNode *validate_data_child = purple_xmlnode_get_child(validate_data_root, "mobile_no");
+ const char *mobile_no = purple_xmlnode_get_attrib(validate_data_child, "msisdn");
- validate_data_root = xmlnode_copy(validate_data_child);
- validate_data_child = xmlnode_get_child(validate_data_root, "status");
- status = xmlnode_get_data(validate_data_child);
+ validate_data_root = purple_xmlnode_copy(validate_data_child);
+ validate_data_child = purple_xmlnode_get_child(validate_data_root, "status");
+ status = purple_xmlnode_get_data(validate_data_child);
- validate_data_child = xmlnode_get_child(validate_data_root, "carrier");
- carrier = xmlnode_get_data(validate_data_child);
+ validate_data_child = purple_xmlnode_get_child(validate_data_root, "carrier");
+ carrier = purple_xmlnode_get_data(validate_data_child);
- purple_debug_info("yahoo", "SMS validate data: %s\n", webdata);
+ purple_debug_info("yahoo", "SMS validate data: %s\n", got_data);
if (status && g_str_equal(status, "Valid")) {
g_hash_table_insert(yd->sms_carrier,
g_strdup_printf("+%s", mobile_no), g_strdup(carrier));
- yahoo_send_im(sms_cb_data->gc, sms_cb_data->who,
- sms_cb_data->what, PURPLE_MESSAGE_SEND);
+ yahoo_send_im(sms_cb_data->gc, purple_message_new_outgoing(
+ sms_cb_data->who, sms_cb_data->what, 0));
} else {
g_hash_table_insert(yd->sms_carrier,
g_strdup_printf("+%s", mobile_no), g_strdup("Unknown"));
- purple_conversation_write(conv, NULL,
- _("Can't send SMS. Unknown mobile carrier."),
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
+ _("Can't send SMS. Unknown mobile carrier."), 0);
}
- xmlnode_free(validate_data_child);
- xmlnode_free(validate_data_root);
+ purple_xmlnode_free(validate_data_child);
+ purple_xmlnode_free(validate_data_root);
g_free(sms_cb_data->who);
g_free(sms_cb_data->what);
g_free(sms_cb_data);
@@ -4663,71 +4521,53 @@ static void yahoo_get_sms_carrier_cb(PurpleUtilFetchUrlData *url_data, gpointer
static void yahoo_get_sms_carrier(PurpleConnection *gc, gpointer data)
{
- YahooData *yd = gc->proto_data;
- PurpleUtilFetchUrlData *url_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
+ PurpleHttpRequest *req;
+ PurpleHttpCookieJar *cookiejar;
struct yahoo_sms_carrier_cb_data *sms_cb_data;
char *validate_request_str = NULL;
- char *request = NULL;
- gboolean use_whole_url = FALSE;
- xmlnode *validate_request_root = NULL;
- xmlnode *validate_request_child = NULL;
+ PurpleXmlNode *validate_request_root = NULL;
+ PurpleXmlNode *validate_request_child = NULL;
if(!(sms_cb_data = data))
return;
- validate_request_root = xmlnode_new("validate");
- xmlnode_set_attrib(validate_request_root, "intl", "us");
- xmlnode_set_attrib(validate_request_root, "version", YAHOO_CLIENT_VERSION);
- xmlnode_set_attrib(validate_request_root, "qos", "0");
-
- validate_request_child = xmlnode_new_child(validate_request_root, "mobile_no");
- xmlnode_set_attrib(validate_request_child, "msisdn", sms_cb_data->who + 1);
-
- validate_request_str = xmlnode_to_str(validate_request_root, NULL);
-
- xmlnode_free(validate_request_child);
- xmlnode_free(validate_request_root);
-
- request = g_strdup_printf(
- "POST /mobileno?intl=us&version=%s HTTP/1.1\r\n"
- "Cookie: T=%s; path=/; domain=.yahoo.com; Y=%s; path=/; domain=.yahoo.com;\r\n"
- "User-Agent: " YAHOO_CLIENT_USERAGENT "\r\n"
- "Host: validate.msg.yahoo.com\r\n"
- "Content-Length: %" G_GSIZE_FORMAT "\r\n"
- "Cache-Control: no-cache\r\n\r\n%s",
- YAHOO_CLIENT_VERSION, yd->cookie_t, yd->cookie_y, strlen(validate_request_str), validate_request_str);
+ validate_request_root = purple_xmlnode_new("validate");
+ purple_xmlnode_set_attrib(validate_request_root, "intl", "us");
+ purple_xmlnode_set_attrib(validate_request_root, "version", YAHOO_CLIENT_VERSION);
+ purple_xmlnode_set_attrib(validate_request_root, "qos", "0");
+
+ validate_request_child = purple_xmlnode_new_child(validate_request_root, "mobile_no");
+ purple_xmlnode_set_attrib(validate_request_child, "msisdn", sms_cb_data->who + 1);
+
+ validate_request_str = purple_xmlnode_to_str(validate_request_root, NULL);
+
+ purple_xmlnode_free(validate_request_child);
+ purple_xmlnode_free(validate_request_root);
+
+ req = purple_http_request_new(NULL);
+ purple_http_request_set_url_printf(req, "http://validate.msg.yahoo.com"
+ "/mobileno?intl=us&version=%s", YAHOO_CLIENT_VERSION);
+ purple_http_request_set_method(req, "POST");
+ purple_http_request_header_set(req, "User-Agent",
+ YAHOO_CLIENT_USERAGENT);
+ cookiejar = purple_http_request_get_cookie_jar(req);
+ purple_http_cookie_jar_set(cookiejar, "T", yd->cookie_t);
+ purple_http_cookie_jar_set(cookiejar, "Y", yd->cookie_y);
+ purple_http_request_set_contents(req, validate_request_str, -1);
+ purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc,
+ req, yahoo_get_sms_carrier_cb, data));
+ purple_http_request_unref(req);
- /* use whole URL if using HTTP Proxy */
- if ((gc->account->proxy_info) && (gc->account->proxy_info->type == PURPLE_PROXY_HTTP))
- use_whole_url = TRUE;
-
- url_data = purple_util_fetch_url_request_len_with_account(
- purple_connection_get_account(gc), YAHOO_SMS_CARRIER_URL, use_whole_url,
- YAHOO_CLIENT_USERAGENT, TRUE, request, FALSE, -1,
- yahoo_get_sms_carrier_cb, data);
-
- g_free(request);
g_free(validate_request_str);
-
- if (url_data)
- yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
- else {
- PurpleAccount *account = purple_connection_get_account(gc);
- PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, sms_cb_data->who, account);
- purple_conversation_write(conv, NULL, _("Can't send SMS. Unable to obtain mobile carrier."), PURPLE_MESSAGE_SYSTEM, time(NULL));
- g_free(sms_cb_data->who);
- g_free(sms_cb_data->what);
- g_free(sms_cb_data);
- }
}
-int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags)
+int yahoo_send_im(PurpleConnection *gc, PurpleMessage *pmsg)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_packet *pkt = NULL;
- char *msg = yahoo_html_to_codes(what);
+ char *msg = yahoo_html_to_codes(purple_message_get_contents(pmsg));
char *msg2;
- gboolean utf8 = TRUE;
PurpleWhiteboard *wb;
int ret = 1;
const char *fed_who;
@@ -4735,7 +4575,9 @@ int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, Purpl
glong lenc = 0;
struct yahoo_p2p_data *p2p_data;
YahooFederation fed = YAHOO_FEDERATION_NONE;
- msg2 = yahoo_string_encode(gc, msg, &utf8);
+ const gchar *rcpt = purple_message_get_recipient(pmsg);
+
+ msg2 = yahoo_string_encode(gc, msg, TRUE);
if(msg2) {
lenb = strlen(msg2);
@@ -4752,24 +4594,25 @@ int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, Purpl
}
}
- fed = yahoo_get_federation_from_name(who);
+ fed = yahoo_get_federation_from_name(rcpt);
- if (who[0] == '+') {
+ if (rcpt[0] == '+') {
/* we have an sms to be sent */
gchar *carrier = NULL;
const char *alias = NULL;
PurpleAccount *account = purple_connection_get_account(gc);
- PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, account);
+ PurpleIMConversation *im = purple_conversations_find_im_with_account(rcpt, account);
- carrier = g_hash_table_lookup(yd->sms_carrier, who);
+ carrier = g_hash_table_lookup(yd->sms_carrier, rcpt);
if (!carrier) {
struct yahoo_sms_carrier_cb_data *sms_cb_data;
sms_cb_data = g_malloc(sizeof(struct yahoo_sms_carrier_cb_data));
sms_cb_data->gc = gc;
- sms_cb_data->who = g_strdup(who);
- sms_cb_data->what = g_strdup(what);
+ sms_cb_data->who = g_strdup(rcpt);
+ sms_cb_data->what = g_strdup(purple_message_get_contents(pmsg));
- purple_conversation_write(conv, NULL, _("Getting mobile carrier to send the SMS."), PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
+ _("Getting mobile carrier to send the SMS."), 0);
yahoo_get_sms_carrier(gc, sms_cb_data);
@@ -4778,19 +4621,20 @@ int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, Purpl
return ret;
}
else if( strcmp(carrier,"Unknown") == 0 ) {
- purple_conversation_write(conv, NULL, _("Can't send SMS. Unknown mobile carrier."), PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
+ _("Can't send SMS. Unknown mobile carrier."), 0);
g_free(msg);
g_free(msg2);
return -1;
}
- alias = purple_account_get_alias(account);
+ alias = purple_account_get_private_alias(account);
pkt = yahoo_packet_new(YAHOO_SERVICE_SMS_MSG, YAHOO_STATUS_AVAILABLE, yd->session_id);
yahoo_packet_hash(pkt, "sssss",
1, purple_connection_get_display_name(gc),
69, alias,
- 5, who + 1,
+ 5, rcpt + 1,
68, carrier,
14, msg2);
yahoo_packet_send_and_free(pkt, yd);
@@ -4802,7 +4646,7 @@ int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, Purpl
}
pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, yd->session_id);
- fed_who = who;
+ fed_who = rcpt;
switch (fed) {
case YAHOO_FEDERATION_MSN:
case YAHOO_FEDERATION_OCS:
@@ -4818,8 +4662,7 @@ int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, Purpl
if (fed)
yahoo_packet_hash_int(pkt, 241, fed);
- if (utf8)
- yahoo_packet_hash_str(pkt, 97, "1");
+ yahoo_packet_hash_str(pkt, 97, "1"); /* UTF-8 */
yahoo_packet_hash_str(pkt, 14, msg2);
/*
@@ -4834,13 +4677,13 @@ int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, Purpl
*
* If they have not set an IMVironment, then use the default.
*/
- wb = purple_whiteboard_get_session(gc->account, who);
+ wb = purple_whiteboard_get_session(purple_connection_get_account(gc), rcpt);
if (wb)
yahoo_packet_hash_str(pkt, 63, DOODLE_IMV_KEY);
else
{
const char *imv;
- imv = g_hash_table_lookup(yd->imvironments, who);
+ imv = g_hash_table_lookup(yd->imvironments, rcpt);
if (imv != NULL)
yahoo_packet_hash_str(pkt, 63, imv);
else
@@ -4857,14 +4700,14 @@ int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, Purpl
/* We may need to not send any packets over 2000 bytes, but I'm not sure yet. */
if ((YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt)) <= 2000) {
/* if p2p link exists, send through it. To-do: key 15, time value to be sent in case of p2p */
- if( (p2p_data = g_hash_table_lookup(yd->peers, who)) && !fed) {
+ if( (p2p_data = g_hash_table_lookup(yd->peers, rcpt)) && !fed) {
yahoo_packet_hash_int(pkt, 11, p2p_data->session_id);
yahoo_p2p_write_pkt(p2p_data->source, pkt);
}
else {
yahoo_packet_send(pkt, yd);
if(!fed)
- yahoo_send_p2p_pkt(gc, who, 0); /* send p2p packet, with val_13=0 */
+ yahoo_send_p2p_pkt(gc, rcpt, 0); /* send p2p packet, with val_13=0 */
}
}
else
@@ -4878,9 +4721,9 @@ int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, Purpl
return ret;
}
-unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state)
+unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleIMTypingState state)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_p2p_data *p2p_data;
YahooFederation fed = YAHOO_FEDERATION_NONE;
struct yahoo_packet *pkt = NULL;
@@ -4896,7 +4739,7 @@ unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleTypi
/* check to see if p2p link exists, send through it */
if( (p2p_data = g_hash_table_lookup(yd->peers, who)) && !fed) {
yahoo_packet_hash(pkt, "sssssis", 49, "TYPING", 1, purple_connection_get_display_name(gc),
- 14, " ", 13, state == PURPLE_TYPING ? "1" : "0",
+ 14, " ", 13, state == PURPLE_IM_TYPING ? "1" : "0",
5, who, 11, p2p_data->session_id, 1002, "1"); /* To-do: key 15 to be sent in case of p2p */
yahoo_p2p_write_pkt(p2p_data->source, pkt);
yahoo_packet_free(pkt);
@@ -4917,7 +4760,7 @@ unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleTypi
}
yahoo_packet_hash(pkt, "ssssss", 49, "TYPING", 1, purple_connection_get_display_name(gc),
- 14, " ", 13, state == PURPLE_TYPING ? "1" : "0",
+ 14, " ", 13, state == PURPLE_IM_TYPING ? "1" : "0",
5, fed_who, 1002, "1");
if (fed)
yahoo_packet_hash_int(pkt, 241, fed);
@@ -4944,14 +4787,13 @@ void yahoo_set_status(PurpleAccount *account, PurpleStatus *status)
const char *msg = NULL;
char *tmp = NULL;
char *conv_msg = NULL;
- gboolean utf8 = TRUE;
if (!purple_status_is_active(status))
return;
gc = purple_account_get_connection(account);
presence = purple_status_get_presence(status);
- yd = (YahooData *)gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
old_status = yd->current_status;
yd->current_status = get_yahoo_status_from_purple_status(status);
@@ -4961,13 +4803,13 @@ void yahoo_set_status(PurpleAccount *account, PurpleStatus *status)
msg = purple_status_get_attr_string(status, "message");
if (purple_status_is_available(status)) {
- tmp = yahoo_string_encode(gc, msg, &utf8);
+ tmp = yahoo_string_encode(gc, msg, TRUE);
conv_msg = purple_markup_strip_html(tmp);
g_free(tmp);
} else {
if ((msg == NULL) || (*msg == '\0'))
msg = _("Away");
- tmp = yahoo_string_encode(gc, msg, &utf8);
+ tmp = yahoo_string_encode(gc, msg, TRUE);
conv_msg = purple_markup_strip_html(tmp);
g_free(tmp);
}
@@ -4985,7 +4827,7 @@ void yahoo_set_status(PurpleAccount *account, PurpleStatus *status)
yahoo_packet_hash_int(pkt, 10, yd->current_status);
if (yd->current_status == YAHOO_STATUS_CUSTOM) {
- yahoo_packet_hash_str(pkt, 97, utf8 ? "1" : 0);
+ yahoo_packet_hash_str(pkt, 97, "1"); /* UTF-8 */
yahoo_packet_hash_str(pkt, 19, conv_msg);
} else {
yahoo_packet_hash_str(pkt, 19, "");
@@ -5017,7 +4859,7 @@ void yahoo_set_status(PurpleAccount *account, PurpleStatus *status)
void yahoo_set_idle(PurpleConnection *gc, int idle)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_packet *pkt = NULL;
char *msg = NULL, *msg2 = NULL;
PurpleStatus *status = NULL;
@@ -5045,10 +4887,9 @@ void yahoo_set_idle(PurpleConnection *gc, int idle)
status = purple_presence_get_active_status(purple_account_get_presence(purple_connection_get_account(gc)));
tmp = purple_status_get_attr_string(status, "message");
if (tmp != NULL) {
- gboolean utf8 = TRUE;
- msg = yahoo_string_encode(gc, tmp, &utf8);
+ msg = yahoo_string_encode(gc, tmp, TRUE);
msg2 = purple_markup_strip_html(msg);
- yahoo_packet_hash_str(pkt, 97, utf8 ? "1" : 0);
+ yahoo_packet_hash_str(pkt, 97, "1"); /* UTF-8 */
yahoo_packet_hash_str(pkt, 19, msg2);
} else {
/* get_yahoo_status_from_purple_status() returns YAHOO_STATUS_CUSTOM for
@@ -5081,13 +4922,13 @@ GList *yahoo_status_types(PurpleAccount *account)
type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, YAHOO_STATUS_TYPE_AVAILABLE,
NULL, TRUE, TRUE, FALSE,
"message", _("Message"),
- purple_value_new(PURPLE_TYPE_STRING), NULL);
+ purple_value_new(G_TYPE_STRING), NULL);
types = g_list_append(types, type);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_AWAY,
NULL, TRUE, TRUE, FALSE,
"message", _("Message"),
- purple_value_new(PURPLE_TYPE_STRING), NULL);
+ purple_value_new(G_TYPE_STRING), NULL);
types = g_list_append(types, type);
type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_BRB, _("Be Right Back"), TRUE);
@@ -5096,7 +4937,7 @@ GList *yahoo_status_types(PurpleAccount *account)
type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE, YAHOO_STATUS_TYPE_BUSY,
_("Busy"), TRUE, TRUE, FALSE,
"message", _("Message"),
- purple_value_new(PURPLE_TYPE_STRING), NULL);
+ purple_value_new(G_TYPE_STRING), NULL);
types = g_list_append(types, type);
type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTATHOME, _("Not at Home"), TRUE);
@@ -5136,7 +4977,7 @@ GList *yahoo_status_types(PurpleAccount *account)
void yahoo_keepalive(PurpleConnection *gc)
{
struct yahoo_packet *pkt;
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
time_t now = time(NULL);
/* We're only allowed to send a ping once an hour or the servers will boot us */
@@ -5167,9 +5008,9 @@ void yahoo_keepalive(PurpleConnection *gc)
}
-void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g)
+void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g, const char *message)
{
- YahooData *yd = (YahooData *)gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_packet *pkt;
const char *group = NULL;
char *group2;
@@ -5181,26 +5022,22 @@ void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g)
return;
fed_bname = bname = purple_buddy_get_name(buddy);
- if (!purple_privacy_check(purple_connection_get_account(gc), bname))
+ if (!purple_account_privacy_check(purple_connection_get_account(gc), bname))
return;
fed = yahoo_get_federation_from_name(bname);
if (fed != YAHOO_FEDERATION_NONE)
fed_bname += 4;
- g = purple_buddy_get_group(buddy);
- if (g)
- group = purple_group_get_name(g);
- else
- group = "Buddies";
+ group = purple_group_get_name(purple_buddy_get_group(buddy));
- group2 = yahoo_string_encode(gc, group, NULL);
+ group2 = yahoo_string_encode(gc, group, FALSE);
pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
if (fed) {
yahoo_packet_hash(pkt, "sssssssisss",
14, "",
65, group2,
- 97, "1",
+ 97, "1", /* UTF-8 */
1, purple_connection_get_display_name(gc),
302, "319",
300, "319",
@@ -5215,7 +5052,7 @@ void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g)
yahoo_packet_hash(pkt, "ssssssssss",
14, "",
65, group2,
- 97, "1",
+ 97, "1", /* UTF-8 */
1, purple_connection_get_display_name(gc),
302, "319",
300, "319",
@@ -5232,7 +5069,7 @@ void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g)
void yahoo_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
{
- YahooData *yd = (YahooData *)gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_packet *pkt;
GSList *buddies, *l;
PurpleGroup *g;
@@ -5249,7 +5086,7 @@ void yahoo_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g
fed = f->fed;
gname = purple_group_get_name(group);
- buddies = purple_find_buddies(purple_connection_get_account(gc), bname);
+ buddies = purple_blist_find_buddies(purple_connection_get_account(gc), bname);
for (l = buddies; l; l = l->next) {
g = purple_buddy_get_group(l->data);
if (purple_utf8_strcasecmp(gname, purple_group_get_name(g))) {
@@ -5265,7 +5102,7 @@ void yahoo_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g
f = NULL; /* f no longer valid - Just making it clear */
}
- cg = yahoo_string_encode(gc, gname, NULL);
+ cg = yahoo_string_encode(gc, gname, FALSE);
pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
switch (fed) {
@@ -5288,7 +5125,7 @@ void yahoo_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g
}
void yahoo_add_deny(PurpleConnection *gc, const char *who) {
- YahooData *yd = (YahooData *)gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_packet *pkt;
YahooFederation fed = YAHOO_FEDERATION_NONE;
@@ -5311,7 +5148,7 @@ void yahoo_add_deny(PurpleConnection *gc, const char *who) {
}
void yahoo_rem_deny(PurpleConnection *gc, const char *who) {
- YahooData *yd = (YahooData *)gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_packet *pkt;
YahooFederation fed = YAHOO_FEDERATION_NONE;
@@ -5339,18 +5176,18 @@ void yahoo_set_permit_deny(PurpleConnection *gc)
account = purple_connection_get_account(gc);
- switch (account->perm_deny)
+ switch (purple_account_get_privacy_type(account))
{
- case PURPLE_PRIVACY_ALLOW_ALL:
- for (deny = account->deny; deny; deny = deny->next)
+ case PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL:
+ for (deny = purple_account_privacy_get_denied(account); deny; deny = deny->next)
yahoo_rem_deny(gc, deny->data);
break;
- case PURPLE_PRIVACY_ALLOW_BUDDYLIST:
- case PURPLE_PRIVACY_ALLOW_USERS:
- case PURPLE_PRIVACY_DENY_USERS:
- case PURPLE_PRIVACY_DENY_ALL:
- for (deny = account->deny; deny; deny = deny->next)
+ case PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST:
+ case PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS:
+ case PURPLE_ACCOUNT_PRIVACY_DENY_USERS:
+ case PURPLE_ACCOUNT_PRIVACY_DENY_ALL:
+ for (deny = purple_account_privacy_get_denied(account); deny; deny = deny->next)
yahoo_add_deny(gc, deny->data);
break;
}
@@ -5359,7 +5196,7 @@ void yahoo_set_permit_deny(PurpleConnection *gc)
void yahoo_change_buddys_group(PurpleConnection *gc, const char *who,
const char *old_group, const char *new_group)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_packet *pkt;
char *gpn, *gpo;
YahooFriend *f = yahoo_friend_find(gc, who);
@@ -5380,8 +5217,8 @@ void yahoo_change_buddys_group(PurpleConnection *gc, const char *who,
* end up deleting the buddy, which would be bad.
* This might happen because of the charset conversation.
*/
- gpn = yahoo_string_encode(gc, new_group, NULL);
- gpo = yahoo_string_encode(gc, old_group, NULL);
+ gpn = yahoo_string_encode(gc, new_group, FALSE);
+ gpo = yahoo_string_encode(gc, old_group, FALSE);
if (!strcmp(gpn, gpo)) {
g_free(gpn);
g_free(gpo);
@@ -5406,12 +5243,12 @@ void yahoo_change_buddys_group(PurpleConnection *gc, const char *who,
void yahoo_rename_group(PurpleConnection *gc, const char *old_name,
PurpleGroup *group, GList *moved_buddies)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_packet *pkt;
char *gpn, *gpo;
- gpn = yahoo_string_encode(gc, purple_group_get_name(group), NULL);
- gpo = yahoo_string_encode(gc, old_name, NULL);
+ gpn = yahoo_string_encode(gc, purple_group_get_name(group), FALSE);
+ gpo = yahoo_string_encode(gc, old_name, FALSE);
if (!strcmp(gpn, gpo)) {
g_free(gpn);
g_free(gpo);
@@ -5435,7 +5272,7 @@ yahoopurple_cmd_buzz(PurpleConversation *c, const gchar *cmd, gchar **args, gcha
if (*args && args[0])
return PURPLE_CMD_RET_FAILED;
- purple_prpl_send_attention(account->gc, c->name, YAHOO_BUZZ);
+ purple_prpl_send_attention(purple_account_get_connection(account), purple_conversation_get_name(c), YAHOO_BUZZ);
return PURPLE_CMD_RET_OK;
}
@@ -5450,7 +5287,7 @@ yahoopurple_cmd_chat_join(PurpleConversation *conv, const char *cmd,
if (!args || !args[0])
return PURPLE_CMD_RET_FAILED;
- gc = purple_conversation_get_gc(conv);
+ gc = purple_conversation_get_connection(conv);
purple_debug_info("yahoo", "Trying to join %s \n", args[0]);
comp = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
@@ -5481,16 +5318,16 @@ gboolean yahoo_offline_message(const PurpleBuddy *buddy)
gboolean yahoo_send_attention(PurpleConnection *gc, const char *username, guint type)
{
- PurpleConversation *c;
+ PurpleIMConversation *im;
- c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
- username, gc->account);
+ im = purple_conversations_find_im_with_account(username,
+ purple_connection_get_account(gc));
- g_return_val_if_fail(c != NULL, FALSE);
+ g_return_val_if_fail(im != NULL, FALSE);
purple_debug_info("yahoo", "Sending <ding> on account %s to buddy %s.\n",
- username, c->name);
- purple_conv_im_send_with_flags(PURPLE_CONV_IM(c), "<ding>", PURPLE_MESSAGE_INVISIBLE);
+ username, purple_conversation_get_name(PURPLE_CONVERSATION(im)));
+ purple_conversation_send_with_flags(PURPLE_CONVERSATION(im), "<ding>", PURPLE_MESSAGE_INVISIBLE);
return TRUE;
}
@@ -5509,3 +5346,8 @@ GList *yahoo_attention_types(PurpleAccount *account)
return list;
}
+gssize
+yahoo_get_max_message_size(PurpleConversation *conv)
+{
+ return YAHOO_MAX_MESSAGE_LENGTH_CHARS;
+}
diff --git a/libpurple/protocols/yahoo/libymsg.h b/libpurple/protocols/yahoo/libymsg.h
index 6e2688657a..e0af9982ea 100644
--- a/libpurple/protocols/yahoo/libymsg.h
+++ b/libpurple/protocols/yahoo/libymsg.h
@@ -25,8 +25,9 @@
#ifndef _LIBYMSG_H_
#define _LIBYMSG_H_
-#include "circbuffer.h"
+#include "circularbuffer.h"
#include "cmds.h"
+#include "http.h"
#include "prpl.h"
#include "network.h"
@@ -41,7 +42,6 @@
#define YAHOO_PROFILE_URL "http://profiles.yahoo.com/"
#define YAHOO_MAIL_URL "http://rd.yahoo.com/messenger/client/?http://mail.yahoo.com/"
#define YAHOO_XFER_HOST "filetransfer.msg.yahoo.com"
-#define YAHOO_XFER_PORT 80
#define YAHOO_XFER_RELAY_HOST "relay.msg.yahoo.com"
#define YAHOO_XFER_RELAY_PORT 80
#define YAHOO_ROOMLIST_URL "http://insider.msg.yahoo.com/ycontent/"
@@ -129,7 +129,7 @@ enum yahoo_status {
YAHOO_STATUS_WEBLOGIN = 0x5a55aa55,
YAHOO_STATUS_OFFLINE = 0x5a55aa56, /* don't ask */
YAHOO_STATUS_TYPING = 0x16,
- YAHOO_STATUS_DISCONNECTED = 0xffffffff /* in ymsg 15. doesnt mean the normal sense of 'disconnected' */
+ YAHOO_STATUS_DISCONNECTED = -1 /* 0xffffffff; in ymsg 15. doesnt mean the normal sense of 'disconnected' */
};
/*
@@ -149,11 +149,8 @@ typedef enum {
struct yahoo_buddy_icon_upload_data {
PurpleConnection *gc;
- GString *str;
char *filename;
- int pos;
- int fd;
- guint watcher;
+ GString *picture_data;
};
struct yahoo_p2p_data {
@@ -189,9 +186,10 @@ typedef struct _YahooPersonalDetails {
typedef struct {
PurpleConnection *gc;
int fd;
+ guint inpa;
guchar *rxqueue;
int rxlen;
- PurpleCircBuffer *txbuf;
+ PurpleCircularBuffer *txbuf;
guint txhandler;
GHashTable *friends;
@@ -220,7 +218,6 @@ typedef struct {
char *pending_chat_topic;
char *pending_chat_goto;
char *auth;
- gsize auth_written;
char *cookie_y;
char *cookie_t;
char *cookie_b;
@@ -234,15 +231,16 @@ typedef struct {
/* ew. we have to check the icon before we connect,
* but can't upload it til we're connected. */
struct yahoo_buddy_icon_upload_data *picture_upload_todo;
- PurpleProxyConnectData *buddy_icon_connect_data;
+ PurpleHttpConnection *picture_upload_hc;
struct _YchtConn *ycht;
/**
- * This linked list contains PurpleUtilFetchUrlData structs
+ * This set contains HTTP connections
* for when we lookup people profile or photo information.
*/
- GSList *url_datas;
+ PurpleHttpConnectionSet *http_reqs;
+
GHashTable *xfer_peer_idstring_map;/* Hey, i dont know, but putting this HashTable next to friends gives a run time fault... */
GSList *cookies;/* contains all cookies, including _y and _t */
PurpleNetworkListenData *listen_data;
@@ -332,14 +330,12 @@ yahoo_account_use_http_proxy(PurpleConnection *conn);
*
* @param gc The connection handle.
* @param str The null terminated utf8 string to encode.
- * @param utf8 If not @c NULL, whether utf8 is okay or not.
- * Even if it is okay, we may not use it. If we
- * used it, we set this to @c TRUE, else to
- * @c FALSE. If @c NULL, false is assumed, and
- * it is not dereferenced.
- * @return The g_malloced string in the appropriate encoding.
+ * @param utf8 Whether to return a UTF-8 string.
+ * @return A g_malloc'ed string in the appropriate encoding. If jd->jp or
+ * utf8 is true then the string is copied verbatim. Otherwise the
+ * encoding from account settings is used.
*/
-char *yahoo_string_encode(PurpleConnection *gc, const char *str, gboolean *utf8);
+gchar *yahoo_string_encode(PurpleConnection *gc, const char *str, gboolean utf8);
/**
* Decode some text received from the server.
@@ -368,11 +364,11 @@ GList *yahoo_status_types(PurpleAccount *account);
GList *yahoo_blist_node_menu(PurpleBlistNode *node);
void yahoo_login(PurpleAccount *account);
void yahoo_close(PurpleConnection *gc);
-int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags);
-unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state);
+int yahoo_send_im(PurpleConnection *gc, PurpleMessage *msg);
+unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleIMTypingState state);
void yahoo_set_status(PurpleAccount *account, PurpleStatus *status);
void yahoo_set_idle(PurpleConnection *gc, int idle);
-void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g);
+void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g, const char *message);
void yahoo_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
void yahoo_add_deny(PurpleConnection *gc, const char *who);
void yahoo_rem_deny(PurpleConnection *gc, const char *who);
@@ -386,6 +382,7 @@ GList *yahoo_attention_types(PurpleAccount *account);
GList *yahoo_actions(PurplePlugin *plugin, gpointer context);
void yahoopurple_register_commands(void);
+gssize yahoo_get_max_message_size(PurpleConversation *conv);
PurpleCmdRet yahoopurple_cmd_buzz(PurpleConversation *c, const gchar *cmd, gchar **args, gchar **error, void *data);
PurpleCmdRet yahoopurple_cmd_chat_join(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data);
diff --git a/libpurple/protocols/yahoo/util.c b/libpurple/protocols/yahoo/util.c
index 2807265595..f3f8272a90 100644
--- a/libpurple/protocols/yahoo/util.c
+++ b/libpurple/protocols/yahoo/util.c
@@ -38,14 +38,17 @@ yahoo_account_use_http_proxy(PurpleConnection *pc)
PurpleAccount *account = purple_connection_get_account(pc);
PurpleProxyInfo *ppi = NULL;
PurpleProxyType type = PURPLE_PROXY_NONE;
+#if 0
gboolean proxy_ssl = purple_account_get_bool(account, "proxy_ssl", FALSE);
if(proxy_ssl)
ppi = purple_proxy_get_setup(account);
else
ppi = purple_proxy_get_setup(NULL);
-
- type = purple_proxy_info_get_type(ppi);
+#else
+ ppi = purple_proxy_get_setup(account);
+#endif
+ type = purple_proxy_info_get_proxy_type(ppi);
return (type == PURPLE_PROXY_HTTP || type == PURPLE_PROXY_USE_ENVVAR);
}
@@ -63,8 +66,9 @@ gchar* yahoo_get_cookies(PurpleConnection *gc)
char firstflag = 1;
gchar *t1,*t2,*t3;
GSList *tmp;
- GSList *cookies;
- cookies = ((YahooData*)(gc->proto_data))->cookies;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
+ GSList *cookies = yd->cookies;
+
tmp = cookies;
while(tmp)
{
@@ -115,21 +119,9 @@ gchar* yahoo_get_cookies(PurpleConnection *gc)
return ans;
}
-/**
- * Encode some text to send to the yahoo server.
- *
- * @param gc The connection handle.
- * @param str The null terminated utf8 string to encode.
- * @param utf8 If not @c NULL, whether utf8 is okay or not.
- * Even if it is okay, we may not use it. If we
- * used it, we set this to @c TRUE, else to
- * @c FALSE. If @c NULL, false is assumed, and
- * it is not dereferenced.
- * @return The g_malloced string in the appropriate encoding.
- */
-char *yahoo_string_encode(PurpleConnection *gc, const char *str, gboolean *utf8)
+char *yahoo_string_encode(PurpleConnection *gc, const char *str, gboolean utf8)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
char *ret;
const char *to_codeset;
GError *error = NULL;
@@ -137,7 +129,7 @@ char *yahoo_string_encode(PurpleConnection *gc, const char *str, gboolean *utf8)
if (yd->jp)
return g_strdup(str);
- if (utf8 && *utf8) /* FIXME: maybe don't use utf8 if it'll fit in latin1 */
+ if (utf8) /* FIXME: maybe don't use utf8 if it'll fit in latin1 */
return g_strdup(str);
to_codeset = purple_account_get_string(purple_connection_get_account(gc), "local_charset", "ISO-8859-1");
@@ -169,7 +161,7 @@ char *yahoo_string_encode(PurpleConnection *gc, const char *str, gboolean *utf8)
*/
char *yahoo_string_decode(PurpleConnection *gc, const char *str, gboolean utf8)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
char *ret;
const char *from_codeset;
GError *error = NULL;
@@ -403,13 +395,13 @@ static void append_attrs_datalist_foreach_cb(GQuark key_id, gpointer data, gpoin
{
const char *key;
const char *value;
- xmlnode *cur;
+ PurpleXmlNode *cur;
key = g_quark_to_string(key_id);
value = data;
cur = user_data;
- xmlnode_set_attrib(cur, key, value);
+ purple_xmlnode_set_attrib(cur, key, value);
}
/**
@@ -417,14 +409,14 @@ static void append_attrs_datalist_foreach_cb(GQuark key_id, gpointer data, gpoin
* currently building. This will be modified when opening a tag
* or closing an existing tag.
*/
-static void yahoo_codes_to_html_add_tag(xmlnode **cur, const char *tag, gboolean is_closing_tag, const gchar *tag_name, gboolean is_font_tag)
+static void yahoo_codes_to_html_add_tag(PurpleXmlNode **cur, const char *tag, gboolean is_closing_tag, const gchar *tag_name, gboolean is_font_tag)
{
if (is_closing_tag) {
- xmlnode *tmp;
+ PurpleXmlNode *tmp;
GSList *dangling_tags = NULL;
/* Move up the DOM until we find the opening tag */
- for (tmp = *cur; tmp != NULL; tmp = xmlnode_get_parent(tmp)) {
+ for (tmp = *cur; tmp != NULL; tmp = purple_xmlnode_get_parent(tmp)) {
/* Add one to tag_name when doing this comparison because it starts with a / */
if (g_str_equal(tmp->name, tag_name + 1))
/* Found */
@@ -439,7 +431,7 @@ static void yahoo_codes_to_html_add_tag(xmlnode **cur, const char *tag, gboolean
}
/* Move our current position up, now that we've closed a tag */
- *cur = xmlnode_get_parent(tmp);
+ *cur = purple_xmlnode_get_parent(tmp);
/* Re-open any tags that were nested below the tag we just closed */
while (dangling_tags != NULL) {
@@ -448,10 +440,10 @@ static void yahoo_codes_to_html_add_tag(xmlnode **cur, const char *tag, gboolean
/* Create a copy of this tag+attributes (but not child tags or
* data) at our new location */
- *cur = xmlnode_new_child(*cur, tmp->name);
+ *cur = purple_xmlnode_new_child(*cur, tmp->name);
for (tmp = tmp->child; tmp != NULL; tmp = tmp->next)
- if (tmp->type == XMLNODE_TYPE_ATTRIB)
- xmlnode_set_attrib_full(*cur, tmp->name,
+ if (tmp->type == PURPLE_XMLNODE_TYPE_ATTRIB)
+ purple_xmlnode_set_attrib_full(*cur, tmp->name,
tmp->xmlns, tmp->prefix, tmp->data);
}
} else {
@@ -462,7 +454,7 @@ static void yahoo_codes_to_html_add_tag(xmlnode **cur, const char *tag, gboolean
if (!purple_markup_find_tag(tag_name, tag, &start, &end, &attributes))
g_return_if_reached();
- *cur = xmlnode_new_child(*cur, tag_name);
+ *cur = purple_xmlnode_new_child(*cur, tag_name);
if (is_font_tag) {
/* Special case for the font size attribute */
@@ -484,8 +476,8 @@ static void yahoo_codes_to_html_add_tag(xmlnode **cur, const char *tag, gboolean
* style on a span tag.
*/
gchar *tmp = g_strdup_printf("font-size: %spt", fontsize);
- *cur = xmlnode_new_child(*cur, "span");
- xmlnode_set_attrib(*cur, "style", tmp);
+ *cur = purple_xmlnode_new_child(*cur, "span");
+ purple_xmlnode_set_attrib(*cur, "style", tmp);
g_free(tmp);
#else
/*
@@ -502,8 +494,8 @@ static void yahoo_codes_to_html_add_tag(xmlnode **cur, const char *tag, gboolean
size = strtol(fontsize, NULL, 10);
htmlsize = point_to_html(size);
sprintf(tmp, "%u", htmlsize);
- xmlnode_set_attrib(*cur, "size", tmp);
- xmlnode_set_attrib(*cur, "absz", fontsize);
+ purple_xmlnode_set_attrib(*cur, "size", tmp);
+ purple_xmlnode_set_attrib(*cur, "absz", fontsize);
#endif /* !USE_CSS_FORMATTING */
g_free(fontsize);
}
@@ -539,20 +531,20 @@ static gchar *yahoo_markup_get_tag_name(const char *tag, gboolean *is_closing_ta
* Example: <font size="8">size 8 <font size="16">size 16 <font size="8">size 8 again
*
* But we want to send well-formed HTML to the core, so we step through
- * the input string and build an xmlnode tree containing sanitized HTML.
+ * the input string and build an PurpleXmlNode tree containing sanitized HTML.
*/
char *yahoo_codes_to_html(const char *x)
{
size_t x_len;
- xmlnode *html, *cur;
+ PurpleXmlNode *html, *cur;
GString *cdata = g_string_new(NULL);
- int i, j;
+ guint i, j;
gboolean no_more_gt_brackets = FALSE;
const char *match;
gchar *xmlstr1, *xmlstr2, *esc;
x_len = strlen(x);
- html = xmlnode_new("html");
+ html = purple_xmlnode_new("html");
cur = html;
for (i = 0; i < x_len; i++) {
@@ -572,7 +564,7 @@ char *yahoo_codes_to_html(const char *x)
/* Append any character data that belongs in the current node */
if (cdata->len > 0) {
- xmlnode_insert_data(cur, cdata->str, cdata->len);
+ purple_xmlnode_insert_data(cur, cdata->str, cdata->len);
g_string_truncate(cdata, 0);
}
@@ -580,12 +572,12 @@ char *yahoo_codes_to_html(const char *x)
if (code[0] == '#') {
#ifdef USE_CSS_FORMATTING
gchar *tmp = g_strdup_printf("color: %s", code);
- cur = xmlnode_new_child(cur, "span");
- xmlnode_set_attrib(cur, "style", tmp);
+ cur = purple_xmlnode_new_child(cur, "span");
+ purple_xmlnode_set_attrib(cur, "style", tmp);
g_free(tmp);
#else
- cur = xmlnode_new_child(cur, "font");
- xmlnode_set_attrib(cur, "color", code);
+ cur = purple_xmlnode_new_child(cur, "font");
+ purple_xmlnode_set_attrib(cur, "color", code);
#endif /* !USE_CSS_FORMATTING */
} else if ((match = g_hash_table_lookup(esc_codes_ht, code))) {
@@ -657,7 +649,7 @@ char *yahoo_codes_to_html(const char *x)
if (match[0] != '\0') {
/* Append any character data that belongs in the current node */
if (cdata->len > 0) {
- xmlnode_insert_data(cur, cdata->str, cdata->len);
+ purple_xmlnode_insert_data(cur, cdata->str, cdata->len);
g_string_truncate(cdata, 0);
}
if (g_str_equal(tag_name, "font"))
@@ -682,12 +674,12 @@ char *yahoo_codes_to_html(const char *x)
/* Append any remaining character data */
if (cdata->len > 0)
- xmlnode_insert_data(cur, cdata->str, cdata->len);
+ purple_xmlnode_insert_data(cur, cdata->str, cdata->len);
g_string_free(cdata, TRUE);
/* Serialize our HTML */
- xmlstr1 = xmlnode_to_str(html, NULL);
- xmlnode_free(html);
+ xmlstr1 = purple_xmlnode_to_str(html, NULL);
+ purple_xmlnode_free(html);
/* Strip off the outter HTML node */
/* This probably isn't necessary, especially if we made the outter HTML
@@ -803,7 +795,7 @@ char *yahoo_html_to_codes(const char *src)
GSList *tags = NULL;
size_t src_len;
- int i, j;
+ guint i, j;
GString *dest;
char *esc;
gboolean no_more_gt_brackets = FALSE;
@@ -855,9 +847,9 @@ char *yahoo_html_to_codes(const char *src)
/*
* TODO: Ideally we would replace this:
- * <a href="http://pidgin.im/">Pidgin</a>
+ * <a href="https://pidgin.im/">Pidgin</a>
* with this:
- * Pidgin (http://pidgin.im/)
+ * Pidgin (https://pidgin.im/)
*
* Currently we drop the text within the <a> tag and
* just show the URL. Doing it the fancy way is
diff --git a/libpurple/protocols/yahoo/yahoo_aliases.c b/libpurple/protocols/yahoo/yahoo_aliases.c
index 2747a340c4..bdf419a159 100644
--- a/libpurple/protocols/yahoo/yahoo_aliases.c
+++ b/libpurple/protocols/yahoo/yahoo_aliases.c
@@ -26,8 +26,9 @@
#include "account.h"
#include "accountopt.h"
-#include "blist.h"
+#include "buddylist.h"
#include "debug.h"
+#include "http.h"
#include "util.h"
#include "request.h"
#include "version.h"
@@ -71,28 +72,36 @@ void yahoo_personal_details_reset(YahooPersonalDetails *ypd, gboolean all)
**************************************************************************/
static void
-yahoo_fetch_aliases_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, size_t len, const gchar *error_message)
+yahoo_fetch_aliases_cb(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _unused)
{
- PurpleConnection *gc = user_data;
- YahooData *yd = gc->proto_data;
-
- yd->url_datas = g_slist_remove(yd->url_datas, url_data);
+ PurpleConnection *gc =
+ purple_http_conn_get_purple_connection(http_conn);
+ YahooData *yd = purple_connection_get_protocol_data(gc);
- if (len == 0) {
- purple_debug_info("yahoo", "No Aliases to process.%s%s\n",
- error_message ? " Error:" : "", error_message ? error_message : "");
+ if (!purple_http_response_is_successful(response)) {
+ purple_debug_info("yahoo", "yahoo_fetch_aliases_cb error: %s\n",
+ purple_http_response_get_error(response));
} else {
gchar *full_name, *nick_name;
+ const gchar *xml_raw;
const char *yid, *id, *fn, *ln, *nn, *alias, *mn;
const char *hp, *wp, *mo;
YahooFriend *f;
PurpleBuddy *b;
- xmlnode *item, *contacts;
+ PurpleXmlNode *item, *contacts;
PurpleAccount *account;
+ size_t len;
account = purple_connection_get_account(gc);
- /* Put our web response into a xmlnode for easy management */
- contacts = xmlnode_from_str(url_text, -1);
+ /* Put our web response into a PurpleXmlNode for easy management */
+ xml_raw = purple_http_response_get_data(response, &len);
+ contacts = purple_xmlnode_from_str(xml_raw, -1);
+
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("yahoo",
+ "yahoo_fetch_aliases_cb xml:[%s]\n", xml_raw);
+ }
if (contacts == NULL) {
purple_debug_error("yahoo", "Badly formed Alias XML\n");
@@ -102,20 +111,20 @@ yahoo_fetch_aliases_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, con
" bytes of alias data\n", len);
/* Loop around and around and around until we have gone through all the received aliases */
- for(item = xmlnode_get_child(contacts, "ct"); item; item = xmlnode_get_next_twin(item)) {
+ for(item = purple_xmlnode_get_child(contacts, "ct"); item; item = purple_xmlnode_get_next_twin(item)) {
/* Yahoo replies with two types of contact (ct) record, we are only interested in the alias ones */
- if ((yid = xmlnode_get_attrib(item, "yi"))) {
+ if ((yid = purple_xmlnode_get_attrib(item, "yi"))) {
YahooPersonalDetails *ypd = NULL;
/* Grab all the bits of information we can */
- fn = xmlnode_get_attrib(item, "fn");
- ln = xmlnode_get_attrib(item, "ln");
- nn = xmlnode_get_attrib(item, "nn");
- mn = xmlnode_get_attrib(item, "mn");
- id = xmlnode_get_attrib(item, "id");
+ fn = purple_xmlnode_get_attrib(item, "fn");
+ ln = purple_xmlnode_get_attrib(item, "ln");
+ nn = purple_xmlnode_get_attrib(item, "nn");
+ mn = purple_xmlnode_get_attrib(item, "mn");
+ id = purple_xmlnode_get_attrib(item, "id");
- hp = xmlnode_get_attrib(item, "hp");
- wp = xmlnode_get_attrib(item, "wp");
- mo = xmlnode_get_attrib(item, "mo");
+ hp = purple_xmlnode_get_attrib(item, "hp");
+ wp = purple_xmlnode_get_attrib(item, "wp");
+ mo = purple_xmlnode_get_attrib(item, "mo");
full_name = nick_name = NULL;
alias = NULL;
@@ -134,7 +143,7 @@ yahoo_fetch_aliases_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, con
/* Find the local buddy that matches */
f = yahoo_friend_find(gc, yid);
- b = purple_find_buddy(account, yid);
+ b = purple_blist_find_buddy(account, yid);
/* If we don't find a matching buddy, ignore the alias !! */
if (f != NULL && b != NULL) {
@@ -143,7 +152,7 @@ yahoo_fetch_aliases_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, con
/* Finally, if we received an alias, we better update the buddy list */
if (alias != NULL) {
- serv_got_alias(gc, yid, alias);
+ purple_serv_got_alias(gc, yid, alias);
purple_debug_info("yahoo", "Fetched alias '%s' (%s)\n", alias, id);
} else if (buddy_alias && *buddy_alias && !g_str_equal(buddy_alias, yid)) {
/* Or if we have an alias that Yahoo doesn't, send it up */
@@ -179,43 +188,28 @@ yahoo_fetch_aliases_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, con
g_free(nick_name);
}
}
- xmlnode_free(contacts);
+ purple_xmlnode_free(contacts);
}
}
void
yahoo_fetch_aliases(PurpleConnection *gc)
{
- YahooData *yd = gc->proto_data;
- const char *url;
- gchar *request, *webpage, *webaddress;
- PurpleUtilFetchUrlData *url_data;
-
- /* use whole URL if using HTTP Proxy */
- gboolean use_whole_url = yahoo_account_use_http_proxy(gc);
-
- /* Build all the info to make the web request */
- url = yd->jp ? YAHOOJP_ALIAS_FETCH_URL : YAHOO_ALIAS_FETCH_URL;
- purple_url_parse(url, &webaddress, NULL, &webpage, NULL, NULL);
- request = g_strdup_printf("GET %s%s/%s HTTP/1.1\r\n"
- "User-Agent: " YAHOO_CLIENT_USERAGENT_ALIAS "\r\n"
- "Cookie: T=%s; Y=%s\r\n"
- "Host: %s\r\n"
- "Cache-Control: no-cache\r\n\r\n",
- use_whole_url ? "http://" : "", use_whole_url ? webaddress : "", webpage,
- yd->cookie_t, yd->cookie_y,
- webaddress);
-
- /* We have a URL and some header information, let's connect and get some aliases */
- url_data = purple_util_fetch_url_request_len_with_account(purple_connection_get_account(gc),
- url, use_whole_url, NULL, TRUE, request, FALSE, -1,
- yahoo_fetch_aliases_cb, gc);
- if (url_data != NULL)
- yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
-
- g_free(webaddress);
- g_free(webpage);
- g_free(request);
+ YahooData *yd = purple_connection_get_protocol_data(gc);
+ PurpleHttpRequest *req;
+ PurpleHttpCookieJar *cookiejar;
+
+ req = purple_http_request_new(yd->jp ? YAHOOJP_ALIAS_FETCH_URL :
+ YAHOO_ALIAS_FETCH_URL);
+ /* XXX: see the other note about user-agent */
+ purple_http_request_header_set(req, "User-Agent",
+ "Mozilla/4.0 (compatible; MSIE 5.5)");
+ cookiejar = purple_http_request_get_cookie_jar(req);
+ purple_http_cookie_jar_set(cookiejar, "T", yd->cookie_t);
+ purple_http_cookie_jar_set(cookiejar, "Y", yd->cookie_y);
+ purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc,
+ req, yahoo_fetch_aliases_cb, NULL));
+ purple_http_request_unref(req);
}
/**************************************************************************
@@ -223,27 +217,23 @@ yahoo_fetch_aliases(PurpleConnection *gc)
**************************************************************************/
static void
-yahoo_update_alias_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, size_t len, const gchar *error_message)
+yahoo_update_alias_cb(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _cb)
{
- xmlnode *node, *result;
- struct callback_data *cb = user_data;
- PurpleConnection *gc = cb->gc;
- YahooData *yd;
+ PurpleXmlNode *node, *result;
+ struct callback_data *cb = _cb;
- yd = gc->proto_data;
- yd->url_datas = g_slist_remove(yd->url_datas, url_data);
-
- if (len == 0 || error_message != NULL) {
+ if (!purple_http_response_is_successful(response)) {
purple_debug_info("yahoo", "Error updating alias for %s: %s\n",
- cb->who,
- error_message ? error_message : "");
+ cb->who, purple_http_response_get_error(response));
g_free(cb->who);
g_free(cb->id);
g_free(cb);
return;
}
- result = xmlnode_from_str(url_text, -1);
+ result = purple_xmlnode_from_str(
+ purple_http_response_get_data(response, NULL), -1);
if (result == NULL) {
purple_debug_error("yahoo", "Alias update for %s failed: Badly formed response\n",
@@ -254,9 +244,9 @@ yahoo_update_alias_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, cons
return;
}
- if ((node = xmlnode_get_child(result, "ct"))) {
+ if ((node = purple_xmlnode_get_child(result, "ct"))) {
if (cb->id == NULL) {
- const char *new_id = xmlnode_get_attrib(node, "id");
+ const char *new_id = purple_xmlnode_get_attrib(node, "id");
if (new_id != NULL) {
/* We now have an addressbook id for the friend; we should save it */
YahooFriend *f = yahoo_friend_find(cb->gc, cb->who);
@@ -271,7 +261,7 @@ yahoo_update_alias_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, cons
purple_debug_error("yahoo", "Missing new addressbook id in add response for %s (weird).\n",
cb->who);
} else {
- if (g_ascii_strncasecmp(xmlnode_get_attrib(node, "id"), cb->id, strlen(cb->id))==0)
+ if (g_ascii_strncasecmp(purple_xmlnode_get_attrib(node, "id"), cb->id, strlen(cb->id))==0)
purple_debug_info("yahoo", "Alias update for %s succeeded\n", cb->who);
else
purple_debug_error("yahoo", "Alias update for %s failed (Contact record return mismatch)\n",
@@ -283,20 +273,18 @@ yahoo_update_alias_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, cons
g_free(cb->who);
g_free(cb->id);
g_free(cb);
- xmlnode_free(result);
+ purple_xmlnode_free(result);
}
void
yahoo_update_alias(PurpleConnection *gc, const char *who, const char *alias)
{
+ PurpleHttpRequest *req;
+ PurpleHttpCookieJar *cookiejar;
YahooData *yd;
- const char *url;
- gchar *content, *request, *webpage, *webaddress;
+ gchar *content;
struct callback_data *cb;
- PurpleUtilFetchUrlData *url_data;
YahooFriend *f;
- /* use whole URL if using HTTP Proxy */
- gboolean use_whole_url = yahoo_account_use_http_proxy(gc);
g_return_if_fail(who != NULL);
g_return_if_fail(gc != NULL);
@@ -310,7 +298,7 @@ yahoo_update_alias(PurpleConnection *gc, const char *who, const char *alias)
return;
}
- yd = gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
/* Using callback_data so I have access to gc in the callback function */
cb = g_new0(struct callback_data, 1);
@@ -318,10 +306,6 @@ yahoo_update_alias(PurpleConnection *gc, const char *who, const char *alias)
cb->id = g_strdup(yahoo_friend_get_alias_id(f));
cb->gc = gc;
- /* Build all the info to make the web request */
- url = yd->jp ? YAHOOJP_ALIAS_UPDATE_URL: YAHOO_ALIAS_UPDATE_URL;
- purple_url_parse(url, &webaddress, NULL, &webpage, NULL, NULL);
-
if (cb->id == NULL) {
/* No id for this buddy, so create an address book entry */
purple_debug_info("yahoo", "Creating '%s' as new alias for user '%s'\n", alias, who);
@@ -331,7 +315,7 @@ yahoo_update_alias(PurpleConnection *gc, const char *who, const char *alias)
gchar *converted_alias_jp = yahoo_convert_to_numeric(alias_jp);
content = g_strdup_printf("<ab k=\"%s\" cc=\"9\">\n"
"<ct a=\"1\" yi='%s' nn='%s' />\n</ab>\r\n",
- purple_account_get_username(gc->account),
+ purple_account_get_username(purple_connection_get_account(gc)),
who, converted_alias_jp);
g_free(converted_alias_jp);
g_free(alias_jp);
@@ -339,7 +323,7 @@ yahoo_update_alias(PurpleConnection *gc, const char *who, const char *alias)
gchar *escaped_alias = g_markup_escape_text(alias, -1);
content = g_strdup_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?><ab k=\"%s\" cc=\"9\">\n"
"<ct a=\"1\" yi='%s' nn='%s' />\n</ab>\r\n",
- purple_account_get_username(gc->account),
+ purple_account_get_username(purple_connection_get_account(gc)),
who, escaped_alias);
g_free(escaped_alias);
}
@@ -351,7 +335,7 @@ yahoo_update_alias(PurpleConnection *gc, const char *who, const char *alias)
gchar *converted_alias_jp = yahoo_convert_to_numeric(alias_jp);
content = g_strdup_printf("<ab k=\"%s\" cc=\"1\">\n"
"<ct e=\"1\" yi='%s' id='%s' nn='%s' pr='0' />\n</ab>\r\n",
- purple_account_get_username(gc->account),
+ purple_account_get_username(purple_connection_get_account(gc)),
who, cb->id, converted_alias_jp);
g_free(converted_alias_jp);
g_free(alias_jp);
@@ -359,36 +343,29 @@ yahoo_update_alias(PurpleConnection *gc, const char *who, const char *alias)
gchar *escaped_alias = g_markup_escape_text(alias, -1);
content = g_strdup_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?><ab k=\"%s\" cc=\"1\">\n"
"<ct e=\"1\" yi='%s' id='%s' nn='%s' pr='0' />\n</ab>\r\n",
- purple_account_get_username(gc->account),
+ purple_account_get_username(purple_connection_get_account(gc)),
who, cb->id, escaped_alias);
g_free(escaped_alias);
}
}
- request = g_strdup_printf("POST %s%s/%s HTTP/1.1\r\n"
- "User-Agent: " YAHOO_CLIENT_USERAGENT_ALIAS "\r\n"
- "Cookie: T=%s; Y=%s\r\n"
- "Host: %s\r\n"
- "Content-Length: %" G_GSIZE_FORMAT "\r\n"
- "Cache-Control: no-cache\r\n\r\n"
- "%s",
- use_whole_url ? "http://" : "", use_whole_url ? webaddress : "", webpage,
- yd->cookie_t, yd->cookie_y,
- webaddress,
- strlen(content),
- content);
-
- /* We have a URL and some header information, let's connect and update the alias */
- url_data = purple_util_fetch_url_request_len_with_account(
- purple_connection_get_account(gc), url, use_whole_url, NULL, TRUE,
- request, FALSE, -1, yahoo_update_alias_cb, cb);
- if (url_data != NULL)
- yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
-
- g_free(webpage);
- g_free(webaddress);
+ req = purple_http_request_new(yd->jp ? YAHOOJP_ALIAS_UPDATE_URL:
+ YAHOO_ALIAS_UPDATE_URL);
+ purple_http_request_set_method(req, "POST");
+ /* XXX: We get rs="ERROR:-100:No Login", when we set
+ * YAHOO_CLIENT_USERAGENT (Mozilla/5.0) here.
+ */
+ purple_http_request_header_set(req, "User-Agent",
+ "Mozilla/4.0 (compatible; MSIE 5.5)");
+ cookiejar = purple_http_request_get_cookie_jar(req);
+ purple_http_cookie_jar_set(cookiejar, "T", yd->cookie_t);
+ purple_http_cookie_jar_set(cookiejar, "Y", yd->cookie_y);
+ purple_http_request_set_contents(req, content, -1);
+ purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc,
+ req, yahoo_update_alias_cb, cb));
+ purple_http_request_unref(req);
+
g_free(content);
- g_free(request);
}
@@ -450,48 +427,32 @@ yahoo_sent_userinfo_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, con
static void
yahoo_set_userinfo_cb(PurpleConnection *gc, PurpleRequestFields *fields)
{
- xmlnode *node = xmlnode_new("ab");
- xmlnode *ct = xmlnode_new_child(node, "ct");
+ PurpleHttpRequest *req;
+ PurpleHttpCookieJar *cookiejar;
+
+ PurpleXmlNode *node = purple_xmlnode_new("ab");
+ PurpleXmlNode *ct = purple_xmlnode_new_child(node, "ct");
YahooData *yd = purple_connection_get_protocol_data(gc);
- PurpleAccount *account;
- PurpleUtilFetchUrlData *url_data;
- char *webaddress, *webpage;
- char *request, *content;
+ char *content;
int len;
int i;
char * yfields[] = { "fn", "ln", "nn", "mn", "hp", "wp", "mo", NULL };
- account = purple_connection_get_account(gc);
+ purple_xmlnode_set_attrib(node, "k", purple_connection_get_display_name(gc));
+ purple_xmlnode_set_attrib(node, "cc", "1"); /* XXX: ? */
- xmlnode_set_attrib(node, "k", purple_connection_get_display_name(gc));
- xmlnode_set_attrib(node, "cc", "1"); /* XXX: ? */
-
- xmlnode_set_attrib(ct, "e", "1");
- xmlnode_set_attrib(ct, "yi", purple_request_fields_get_string(fields, "yname"));
- xmlnode_set_attrib(ct, "id", purple_request_fields_get_string(fields, "yid"));
- xmlnode_set_attrib(ct, "pr", "0");
+ purple_xmlnode_set_attrib(ct, "e", "1");
+ purple_xmlnode_set_attrib(ct, "yi", purple_request_fields_get_string(fields, "yname"));
+ purple_xmlnode_set_attrib(ct, "id", purple_request_fields_get_string(fields, "yid"));
+ purple_xmlnode_set_attrib(ct, "pr", "0");
for (i = 0; yfields[i]; i++) {
const char *v = purple_request_fields_get_string(fields, yfields[i]);
- xmlnode_set_attrib(ct, yfields[i], v ? v : "");
+ purple_xmlnode_set_attrib(ct, yfields[i], v ? v : "");
}
- content = xmlnode_to_formatted_str(node, &len);
- xmlnode_free(node);
- purple_url_parse(yd->jp ? YAHOOJP_USERINFO_URL : YAHOO_USERINFO_URL, &webaddress, NULL, &webpage, NULL, NULL);
-
- request = g_strdup_printf("POST %s HTTP/1.1\r\n"
- "User-Agent: " YAHOO_CLIENT_USERAGENT_ALIAS "\r\n"
- "Cookie: T=%s; path=/; domain=.yahoo.com; Y=%s;\r\n"
- "Host: %s\r\n"
- "Content-Length: %d\r\n"
- "Cache-Control: no-cache\r\n\r\n"
- "%s\r\n\r\n",
- webpage,
- yd->cookie_t, yd->cookie_y,
- webaddress,
- len + 4,
- content);
+ content = purple_xmlnode_to_formatted_str(node, &len);
+ purple_xmlnode_free(node);
#if 0
{
@@ -501,32 +462,37 @@ yahoo_set_userinfo_cb(PurpleConnection *gc, PurpleRequestFields *fields)
* the receiver's end, which is stupid, and thus not really
* surprising. */
struct yahoo_userinfo *ui = g_new(struct yahoo_userinfo, 1);
- node = xmlnode_new("contact");
+ node = purple_xmlnode_new("contact");
for (i = 0; yfields[i]; i++) {
const char *v = purple_request_fields_get_string(fields, yfields[i]);
if (v) {
- xmlnode *nd = xmlnode_new_child(node, yfields[i]);
- xmlnode_insert_data(nd, v, -1);
+ PurpleXmlNode *nd = purple_xmlnode_new_child(node, yfields[i]);
+ purple_xmlnode_insert_data(nd, v, -1);
}
}
ui->yd = yd;
- ui->xml = xmlnode_to_str(node, NULL);
- xmlnode_free(node);
+ ui->xml = purple_xmlnode_to_str(node, NULL);
+ purple_xmlnode_free(node);
}
#endif
- url_data = purple_util_fetch_url_request_len_with_account(account, webaddress, FALSE,
- YAHOO_CLIENT_USERAGENT_ALIAS, TRUE, request, FALSE, -1,
- yahoo_fetch_aliases_cb, gc);
- if (url_data != NULL)
- yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
+ req = purple_http_request_new(yd->jp ? YAHOOJP_USERINFO_URL :
+ YAHOO_USERINFO_URL);
+ purple_http_request_set_method(req, "POST");
+ /* XXX: see the previous comment for user-agent */
+ purple_http_request_header_set(req, "User-Agent",
+ "Mozilla/4.0 (compatible; MSIE 5.5)");
+ cookiejar = purple_http_request_get_cookie_jar(req);
+ purple_http_cookie_jar_set(cookiejar, "T", yd->cookie_t);
+ purple_http_cookie_jar_set(cookiejar, "Y", yd->cookie_y);
+ purple_http_request_set_contents(req, content, -1);
+ purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc,
+ req, yahoo_fetch_aliases_cb, NULL));
+ purple_http_request_unref(req);
- g_free(webaddress);
- g_free(webpage);
g_free(content);
- g_free(request);
}
static PurpleRequestFields *
@@ -551,6 +517,13 @@ request_fields_from_personal_details(YahooPersonalDetails *ypd, const char *id)
{NULL, NULL, NULL}
};
+ if (ypd->id == NULL) {
+ purple_debug_error("yahoo",
+ "request_fields_from_personal_details:"
+ "ypd->id == NULL (it doesn't seems to work)\n");
+ return NULL;
+ }
+
fields = purple_request_fields_new();
group = purple_request_field_group_new(NULL);
purple_request_fields_add_group(fields, group);
@@ -584,10 +557,12 @@ void yahoo_set_userinfo_for_buddy(PurpleConnection *gc, PurpleBuddy *buddy)
return;
fields = request_fields_from_personal_details(&f->ypd, name);
+ if (fields == NULL)
+ return;
purple_request_fields(gc, NULL, _("Set User Info"), NULL, fields,
_("OK"), G_CALLBACK(yahoo_set_userinfo_cb),
_("Cancel"), NULL,
- purple_connection_get_account(gc), NULL, NULL, gc);
+ purple_request_cpar_from_connection(gc), gc);
}
void yahoo_set_userinfo(PurpleConnection *gc)
@@ -595,29 +570,31 @@ void yahoo_set_userinfo(PurpleConnection *gc)
YahooData *yd = purple_connection_get_protocol_data(gc);
PurpleRequestFields *fields = request_fields_from_personal_details(&yd->ypd,
purple_connection_get_display_name(gc));
+ if (fields == NULL)
+ return;
purple_request_fields(gc, NULL, _("Set User Info"), NULL, fields,
_("OK"), G_CALLBACK(yahoo_set_userinfo_cb),
_("Cancel"), NULL,
- purple_connection_get_account(gc), NULL, NULL, gc);
+ purple_request_cpar_from_connection(gc), gc);
}
static gboolean
parse_contact_details(YahooData *yd, const char *who, const char *xml)
{
- xmlnode *node, *nd;
+ PurpleXmlNode *node, *nd;
YahooFriend *f;
char *yid;
- node = xmlnode_from_str(xml, -1);
+ node = purple_xmlnode_from_str(xml, -1);
if (!node) {
purple_debug_info("yahoo", "Received malformed XML for contact details from '%s':\n%s\n",
who, xml);
return FALSE;
}
- nd = xmlnode_get_child(node, "yi");
- if (!nd || !(yid = xmlnode_get_data(nd))) {
- xmlnode_free(node);
+ nd = purple_xmlnode_get_child(node, "yi");
+ if (!nd || !(yid = purple_xmlnode_get_data(nd))) {
+ purple_xmlnode_free(node);
return FALSE;
}
@@ -631,14 +608,14 @@ parse_contact_details(YahooData *yd, const char *who, const char *xml)
purple_debug_info("yahoo", "Ignoring contact details sent by %s about %s\n",
who, yid);
g_free(yid);
- xmlnode_free(node);
+ purple_xmlnode_free(node);
return FALSE;
}
f = yahoo_friend_find(yd->gc, yid);
if (!f) {
g_free(yid);
- xmlnode_free(node);
+ purple_xmlnode_free(node);
return FALSE;
} else {
int i;
@@ -661,8 +638,8 @@ parse_contact_details(YahooData *yd, const char *who, const char *xml)
yahoo_personal_details_reset(ypd, FALSE);
for (i = 0; details[i].id; i++) {
- nd = xmlnode_get_child(node, details[i].id);
- *details[i].field = nd ? xmlnode_get_data(nd) : NULL;
+ nd = purple_xmlnode_get_child(node, details[i].id);
+ *details[i].field = nd ? purple_xmlnode_get_data(nd) : NULL;
}
if (ypd->names.nick)
@@ -674,13 +651,13 @@ parse_contact_details(YahooData *yd, const char *who, const char *xml)
}
if (alias) {
- serv_got_alias(yd->gc, yid, alias);
+ purple_serv_got_alias(yd->gc, yid, alias);
if (alias != ypd->names.nick)
g_free(alias);
}
}
- xmlnode_free(node);
+ purple_xmlnode_free(node);
g_free(yid);
return TRUE;
}
diff --git a/libpurple/protocols/yahoo/yahoo_aliases.h b/libpurple/protocols/yahoo/yahoo_aliases.h
index 0eb7fef1b3..ef5cb78f77 100644
--- a/libpurple/protocols/yahoo/yahoo_aliases.h
+++ b/libpurple/protocols/yahoo/yahoo_aliases.h
@@ -26,7 +26,7 @@
#include "account.h"
#include "accountopt.h"
-#include "blist.h"
+#include "buddylist.h"
#include "debug.h"
#include "util.h"
#include "version.h"
diff --git a/libpurple/protocols/yahoo/yahoo_doodle.c b/libpurple/protocols/yahoo/yahoo_doodle.c
index 53f3d95ca4..79d52679aa 100644
--- a/libpurple/protocols/yahoo/yahoo_doodle.c
+++ b/libpurple/protocols/yahoo/yahoo_doodle.c
@@ -28,12 +28,11 @@
#include "account.h"
#include "accountopt.h"
-#include "blist.h"
+#include "buddylist.h"
#include "cipher.h"
#include "cmds.h"
#include "debug.h"
#include "notify.h"
-#include "privacy.h"
#include "prpl.h"
#include "proxy.h"
#include "request.h"
@@ -94,8 +93,8 @@ PurpleCmdRet yahoo_doodle_purple_cmd_start(PurpleConversation *conv, const char
/* Write a local message to this conversation showing that a request for a
* Doodle session has been made
*/
- purple_conv_im_write(PURPLE_CONV_IM(conv), "", _("Sent Doodle request."),
- PURPLE_MESSAGE_NICK | PURPLE_MESSAGE_RECV, time(NULL));
+ purple_conversation_write_system_message(conv, _("Sent Doodle request."),
+ PURPLE_MESSAGE_NICK);
return PURPLE_CMD_RET_OK;
}
@@ -115,7 +114,7 @@ void yahoo_doodle_initiate(PurpleConnection *gc, const char *name)
/* Insert this 'session' in the list. At this point, it's only a
* requested session.
*/
- purple_whiteboard_create(account, to, DOODLE_STATE_REQUESTING);
+ purple_whiteboard_new(account, to, DOODLE_STATE_REQUESTING);
}
/* NOTE Perhaps some careful handling of remote assumed established
@@ -158,8 +157,8 @@ static void yahoo_doodle_command_got_request(PurpleConnection *gc, const char *f
dialog_message, NULL, NULL, NULL);
*/
- wb = purple_whiteboard_create(account, from, DOODLE_STATE_REQUESTED);
- ds = wb->proto_data;
+ wb = purple_whiteboard_new(account, from, DOODLE_STATE_REQUESTED);
+ ds = purple_whiteboard_get_protocol_data(wb);
ds->imv_key = g_strdup(imv_key);
yahoo_doodle_command_send_ready(gc, from, imv_key);
@@ -187,19 +186,19 @@ static void yahoo_doodle_command_got_ready(PurpleConnection *gc, const char *fro
if(wb == NULL)
return;
- if(wb->state == DOODLE_STATE_REQUESTING)
+ if(purple_whiteboard_get_state(wb) == DOODLE_STATE_REQUESTING)
{
- doodle_session *ds = wb->proto_data;
+ doodle_session *ds = purple_whiteboard_get_protocol_data(wb);
purple_whiteboard_start(wb);
- wb->state = DOODLE_STATE_ESTABLISHED;
+ purple_whiteboard_set_state(wb, DOODLE_STATE_ESTABLISHED);
yahoo_doodle_command_send_confirm(gc, from, imv_key);
/* Let's steal the imv_key and reuse it */
g_free(ds->imv_key);
ds->imv_key = g_strdup(imv_key);
}
- else if(wb->state == DOODLE_STATE_ESTABLISHED)
+ else if(purple_whiteboard_get_state(wb) == DOODLE_STATE_ESTABLISHED)
{
/* TODO Ask whether to save picture too */
purple_whiteboard_clear(wb);
@@ -209,7 +208,7 @@ static void yahoo_doodle_command_got_ready(PurpleConnection *gc, const char *fro
* already thinks we're in a session with them (when their chat message
* contains the doodle imv key)
*/
- else if(wb->state == DOODLE_STATE_REQUESTED)
+ else if(purple_whiteboard_get_state(wb) == DOODLE_STATE_REQUESTED)
{
/* purple_whiteboard_start(wb); */
yahoo_doodle_command_send_ready(gc, from, imv_key);
@@ -290,7 +289,7 @@ static void yahoo_doodle_command_got_clear(PurpleConnection *gc, const char *fro
if(wb == NULL)
return;
- if(wb->state == DOODLE_STATE_ESTABLISHED)
+ if(purple_whiteboard_get_state(wb) == DOODLE_STATE_ESTABLISHED)
{
/* TODO Ask user whether to save the image before clearing it */
@@ -331,9 +330,9 @@ static void yahoo_doodle_command_got_confirm(PurpleConnection *gc, const char *f
/* TODO Combine the following IF's? */
/* Check if we requested a doodle session */
- /*if(wb->state == DOODLE_STATE_REQUESTING)
+ /*if(purple_whiteboard_get_state(wb) == DOODLE_STATE_REQUESTING)
{
- wb->state = DOODLE_STATE_ESTABLISHED;
+ purple_whiteboard_set_state(wb, DOODLE_STATE_ESTABLISHED);
purple_whiteboard_start(wb);
@@ -341,9 +340,9 @@ static void yahoo_doodle_command_got_confirm(PurpleConnection *gc, const char *f
}*/
/* Check if we accepted a request for a doodle session */
- if(wb->state == DOODLE_STATE_REQUESTED)
+ if(purple_whiteboard_get_state(wb) == DOODLE_STATE_REQUESTED)
{
- wb->state = DOODLE_STATE_ESTABLISHED;
+ purple_whiteboard_set_state(wb, DOODLE_STATE_ESTABLISHED);
purple_whiteboard_start(wb);
}
@@ -370,8 +369,8 @@ void yahoo_doodle_command_got_shutdown(PurpleConnection *gc, const char *from)
/* TODO Ask if user wants to save picture before the session is closed */
- wb->state = DOODLE_STATE_CANCELLED;
- purple_whiteboard_destroy(wb);
+ purple_whiteboard_set_state(wb, DOODLE_STATE_CANCELLED);
+ g_object_unref(wb);
}
static void yahoo_doodle_command_send_generic(const char *type,
@@ -387,12 +386,12 @@ static void yahoo_doodle_command_send_generic(const char *type,
purple_debug_info("yahoo", "doodle: Sent %s (%s)\n", type, to);
- yd = gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
/* Make and send an acknowledge (ready) Doodle packet */
pkt = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id);
yahoo_packet_hash_str(pkt, 49, "IMVIRONMENT");
- yahoo_packet_hash_str(pkt, 1, purple_account_get_username(gc->account));
+ yahoo_packet_hash_str(pkt, 1, purple_account_get_username(purple_connection_get_account(gc)));
yahoo_packet_hash_str(pkt, 14, message);
yahoo_packet_hash_int(pkt, 13, command);
yahoo_packet_hash_str(pkt, 5, to);
@@ -448,21 +447,23 @@ void yahoo_doodle_start(PurpleWhiteboard *wb)
ds->brush_size = DOODLE_BRUSH_SMALL;
ds->brush_color = DOODLE_COLOR_RED;
- wb->proto_data = ds;
+ purple_whiteboard_set_protocol_data(wb, ds);
}
void yahoo_doodle_end(PurpleWhiteboard *wb)
{
- PurpleConnection *gc = purple_account_get_connection(wb->account);
- doodle_session *ds = wb->proto_data;
+ PurpleAccount *account = purple_whiteboard_get_account(wb);
+ PurpleConnection *gc = purple_account_get_connection(account);
+ doodle_session *ds = purple_whiteboard_get_protocol_data(wb);
/* g_debug_debug("yahoo", "doodle: yahoo_doodle_end()\n"); */
- if (gc && wb->state != DOODLE_STATE_CANCELLED)
- yahoo_doodle_command_send_shutdown(gc, wb->who);
+ if (gc && (purple_whiteboard_get_state(wb) != DOODLE_STATE_CANCELLED))
+ yahoo_doodle_command_send_shutdown(gc, purple_whiteboard_get_who(wb));
g_free(ds->imv_key);
- g_free(wb->proto_data);
+ g_free(ds);
+ purple_whiteboard_set_protocol_data(wb, NULL);
}
void yahoo_doodle_get_dimensions(const PurpleWhiteboard *wb, int *width, int *height)
@@ -492,20 +493,25 @@ static char *yahoo_doodle_build_draw_string(doodle_session *ds, GList *draw_list
void yahoo_doodle_send_draw_list(PurpleWhiteboard *wb, GList *draw_list)
{
- doodle_session *ds = wb->proto_data;
+ PurpleAccount *account = purple_whiteboard_get_account(wb);
+ PurpleConnection *gc = purple_account_get_connection(account);
+ doodle_session *ds = purple_whiteboard_get_protocol_data(wb);
char *message;
g_return_if_fail(draw_list != NULL);
message = yahoo_doodle_build_draw_string(ds, draw_list);
- yahoo_doodle_command_send_draw(wb->account->gc, wb->who, message, ds->imv_key);
+ yahoo_doodle_command_send_draw(gc, purple_whiteboard_get_who(wb), message, ds->imv_key);
g_free(message);
}
void yahoo_doodle_clear(PurpleWhiteboard *wb)
{
- doodle_session *ds = wb->proto_data;
- yahoo_doodle_command_send_clear(wb->account->gc, wb->who, ds->imv_key);
+ PurpleAccount *account = purple_whiteboard_get_account(wb);
+ PurpleConnection *gc = purple_account_get_connection(account);
+ doodle_session *ds = purple_whiteboard_get_protocol_data(wb);
+
+ yahoo_doodle_command_send_clear(gc, purple_whiteboard_get_who(wb), ds->imv_key);
}
@@ -558,14 +564,14 @@ void yahoo_doodle_draw_stroke(PurpleWhiteboard *wb, GList *draw_list)
void yahoo_doodle_get_brush(const PurpleWhiteboard *wb, int *size, int *color)
{
- doodle_session *ds = wb->proto_data;
+ doodle_session *ds = purple_whiteboard_get_protocol_data(wb);
*size = ds->brush_size;
*color = ds->brush_color;
}
void yahoo_doodle_set_brush(PurpleWhiteboard *wb, int size, int color)
{
- doodle_session *ds = wb->proto_data;
+ doodle_session *ds = purple_whiteboard_get_protocol_data(wb);
ds->brush_size = size;
ds->brush_color = color;
diff --git a/libpurple/protocols/yahoo/yahoo_filexfer.c b/libpurple/protocols/yahoo/yahoo_filexfer.c
index 65a60ccc4d..081b8df603 100644
--- a/libpurple/protocols/yahoo/yahoo_filexfer.c
+++ b/libpurple/protocols/yahoo/yahoo_filexfer.c
@@ -20,16 +20,19 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+/* TODO: it needs further refactoring */
+
#include "internal.h"
#include "dnsquery.h"
#include "prpl.h"
#include "util.h"
#include "debug.h"
+#include "http.h"
#include "network.h"
#include "notify.h"
#include "proxy.h"
-#include "ft.h"
+#include "xfer.h"
#include "libymsg.h"
#include "yahoo_packet.h"
#include "yahoo_filexfer.h"
@@ -37,55 +40,33 @@
#include "yahoo_friend.h"
struct yahoo_xfer_data {
+ gchar *url;
+ gboolean is_relay;
+ PurpleHttpConnection *hc;
+
gchar *host;
gchar *path;
- int port;
PurpleConnection *gc;
- long expires;
- gboolean started;
- gchar *txbuf;
- gsize txbuflen;
- gsize txbuf_written;
- guint tx_handler;
- gchar *rxqueue;
- guint rxlen;
gchar *xfer_peer_idstring;
gchar *xfer_idstring_for_relay;
- int version; /* 0 for old, 15 for Y7(YMSG 15) */
int info_val_249;
- enum {
- STARTED = 0,
- HEAD_REQUESTED,
- HEAD_REPLY_RECEIVED,
- TRANSFER_PHASE,
- ACCEPTED,
- P2P_HEAD_REQUESTED,
- P2P_HEAD_REPLIED,
- P2P_GET_REQUESTED
- } status_15;
-
/* contains all filenames, in case of multiple transfers, with the first
* one in the list being the current file's name (ymsg15) */
GSList *filename_list;
GSList *size_list; /* corresponds to filename_list, with size as **STRING** */
gboolean firstoflist;
- gchar *xfer_url; /* url of the file, used when we are p2p server */
- int yahoo_local_p2p_ft_server_fd;
- int yahoo_local_p2p_ft_server_port;
- int yahoo_p2p_ft_server_watcher;
- int input_event;
};
-static void yahoo_xfer_data_free(struct yahoo_xfer_data *xd)
+static void
+yahoo_xfer_data_free(struct yahoo_xfer_data *xd)
{
PurpleConnection *gc;
YahooData *yd;
PurpleXfer *xfer;
- GSList *l;
gc = xd->gc;
- yd = gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
/* remove entry from map */
if(xd->xfer_peer_idstring) {
@@ -94,262 +75,72 @@ static void yahoo_xfer_data_free(struct yahoo_xfer_data *xd)
g_hash_table_remove(yd->xfer_peer_idstring_map, xd->xfer_peer_idstring);
}
- /* empty file & filesize list */
- for (l = xd->filename_list; l; l = l->next) {
- g_free(l->data);
- l->data=NULL;
- }
- for (l = xd->size_list; l; l = l->next) {
- g_free(l->data);
- l->data=NULL;
- }
- g_slist_free(xd->filename_list);
- g_slist_free(xd->size_list);
+ g_slist_free_full(xd->filename_list, g_free);
+ g_slist_free_full(xd->size_list, g_free);
g_free(xd->host);
g_free(xd->path);
- g_free(xd->txbuf);
g_free(xd->xfer_peer_idstring);
g_free(xd->xfer_idstring_for_relay);
- if (xd->tx_handler)
- purple_input_remove(xd->tx_handler);
g_free(xd);
}
-static void yahoo_receivefile_send_cb(gpointer data, gint source, PurpleInputCondition condition)
-{
- PurpleXfer *xfer;
- struct yahoo_xfer_data *xd;
- int remaining, written;
-
- xfer = data;
- xd = xfer->data;
-
- remaining = xd->txbuflen - xd->txbuf_written;
- written = write(xfer->fd, xd->txbuf + xd->txbuf_written, remaining);
-
- if (written < 0 && errno == EAGAIN)
- written = 0;
- else if (written <= 0) {
- purple_debug_error("yahoo", "Unable to write in order to start ft errno = %d\n", errno);
- purple_xfer_cancel_remote(xfer);
- return;
- }
-
- if (written < remaining) {
- xd->txbuf_written += written;
- return;
- }
-
- purple_input_remove(xd->tx_handler);
- xd->tx_handler = 0;
- g_free(xd->txbuf);
- xd->txbuf = NULL;
- xd->txbuflen = 0;
-
- purple_xfer_start(xfer, source, NULL, 0);
-
-}
-
-static void yahoo_receivefile_connected(gpointer data, gint source, const gchar *error_message)
-{
- PurpleXfer *xfer;
- struct yahoo_xfer_data *xd;
-
- purple_debug_info("yahoo", "in yahoo_receivefile_connected\n");
-
- if (!(xfer = data))
- return;
- if (!(xd = xfer->data))
- return;
- if ((source < 0) || (xd->path == NULL) || (xd->host == NULL)) {
- purple_xfer_error(PURPLE_XFER_RECEIVE, purple_xfer_get_account(xfer),
- xfer->who, _("Unable to connect."));
- purple_xfer_cancel_remote(xfer);
- return;
- }
-
- xfer->fd = source;
+static void
+yahoo_xfer_start(PurpleXfer *xfer);
- /* The first time we get here, assemble the tx buffer */
- if (xd->txbuflen == 0) {
- xd->txbuf = g_strdup_printf("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n",
- xd->path, xd->host);
- xd->txbuflen = strlen(xd->txbuf);
- xd->txbuf_written = 0;
- }
-
- if (!xd->tx_handler)
- {
- xd->tx_handler = purple_input_add(source, PURPLE_INPUT_WRITE,
- yahoo_receivefile_send_cb, xfer);
- yahoo_receivefile_send_cb(xfer, source, PURPLE_INPUT_WRITE);
- }
-}
+static PurpleXfer *
+yahoo_ft_new_xfer_struct(PurpleConnection *gc, PurpleXferType type,
+ const char *who);
-static void yahoo_sendfile_send_cb(gpointer data, gint source, PurpleInputCondition condition)
+static PurpleHttpRequest *
+yahoo_ft_new_req(struct yahoo_xfer_data *xd)
{
- PurpleXfer *xfer;
- struct yahoo_xfer_data *xd;
- int written, remaining;
+ PurpleHttpRequest *req;
+ YahooData *yd;
- xfer = data;
- xd = xfer->data;
+ g_return_val_if_fail(xd != NULL, NULL);
- remaining = xd->txbuflen - xd->txbuf_written;
- written = write(xfer->fd, xd->txbuf + xd->txbuf_written, remaining);
+ yd = purple_connection_get_protocol_data(xd->gc);
- if (written < 0 && errno == EAGAIN)
- written = 0;
- else if (written <= 0) {
- purple_debug_error("yahoo", "Unable to write in order to start ft errno = %d\n", errno);
- purple_xfer_cancel_remote(xfer);
- return;
- }
+ req = purple_http_request_new(xd->url);
+ purple_http_request_header_set(req, "User-Agent",
+ YAHOO_CLIENT_USERAGENT);
+ purple_http_request_header_set(req, "Cache-Control", "no-cache");
+ if (xd->is_relay) {
+ PurpleHttpCookieJar *cjar;
- if (written < remaining) {
- xd->txbuf_written += written;
- return;
+ cjar = purple_http_request_get_cookie_jar(req);
+ purple_http_cookie_jar_set(cjar, "Y", yd->cookie_y);
+ purple_http_cookie_jar_set(cjar, "T", yd->cookie_t);
}
- purple_input_remove(xd->tx_handler);
- xd->tx_handler = 0;
- g_free(xd->txbuf);
- xd->txbuf = NULL;
- xd->txbuflen = 0;
-
- purple_xfer_start(xfer, source, NULL, 0);
+ return req;
}
-static void yahoo_sendfile_connected(gpointer data, gint source, const gchar *error_message)
+static gchar *
+yahoo_ft_url_gen(PurpleXfer *xfer, const gchar *host)
{
- PurpleXfer *xfer;
- struct yahoo_xfer_data *xd;
- struct yahoo_packet *pkt;
- gchar *size, *filename, *encoded_filename, *header;
- guchar *pkt_buf;
- const char *host;
- int port;
- size_t content_length, header_len, pkt_buf_len;
- PurpleConnection *gc;
+ struct yahoo_xfer_data *xfer_data;
PurpleAccount *account;
- YahooData *yd;
- purple_debug_info("yahoo", "in yahoo_sendfile_connected\n");
+ g_return_val_if_fail(host != NULL, NULL);
- if (!(xfer = data))
- return;
- if (!(xd = xfer->data))
- return;
+ xfer_data = purple_xfer_get_protocol_data(xfer);
+ account = purple_connection_get_account(xfer_data->gc);
- if (source < 0) {
- purple_xfer_error(PURPLE_XFER_RECEIVE, purple_xfer_get_account(xfer),
- xfer->who, _("Unable to connect."));
- purple_xfer_cancel_remote(xfer);
- return;
+ if (!xfer_data->is_relay) {
+ purple_debug_fatal("yahoo", "Non-relay FT aren't tested yet\n");
+ return NULL;
}
- xfer->fd = source;
-
- /* Assemble the tx buffer */
- gc = xd->gc;
- account = purple_connection_get_account(gc);
- yd = gc->proto_data;
-
- pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANSFER,
- YAHOO_STATUS_AVAILABLE, yd->session_id);
-
- size = g_strdup_printf("%" G_GSIZE_FORMAT, purple_xfer_get_size(xfer));
- filename = g_path_get_basename(purple_xfer_get_local_filename(xfer));
- encoded_filename = yahoo_string_encode(gc, filename, NULL);
-
- yahoo_packet_hash(pkt, "sssss", 0, purple_connection_get_display_name(gc),
- 5, xfer->who, 14, "", 27, encoded_filename, 28, size);
- g_free(size);
- g_free(encoded_filename);
- g_free(filename);
-
- content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt);
-
- pkt_buf_len = yahoo_packet_build(pkt, 4, FALSE, yd->jp, &pkt_buf);
- yahoo_packet_free(pkt);
-
- host = purple_account_get_string(account, "xfer_host", YAHOO_XFER_HOST);
- port = purple_account_get_int(account, "xfer_port", YAHOO_XFER_PORT);
- header = g_strdup_printf(
- "POST http://%s:%d/notifyft HTTP/1.0\r\n"
- "Content-length: %" G_GSIZE_FORMAT "\r\n"
- "Host: %s:%d\r\n"
- "Cookie: Y=%s; T=%s\r\n"
- "\r\n",
- host, port, content_length + 4 + purple_xfer_get_size(xfer),
- host, port, yd->cookie_y, yd->cookie_t);
-
- header_len = strlen(header);
-
- xd->txbuflen = header_len + pkt_buf_len + 4;
- xd->txbuf = g_malloc(xd->txbuflen);
-
- memcpy(xd->txbuf, header, header_len);
- g_free(header);
- memcpy(xd->txbuf + header_len, pkt_buf, pkt_buf_len);
- g_free(pkt_buf);
- memcpy(xd->txbuf + header_len + pkt_buf_len, "29\xc0\x80", 4);
-
- xd->txbuf_written = 0;
-
- if (xd->tx_handler == 0)
- {
- xd->tx_handler = purple_input_add(source, PURPLE_INPUT_WRITE,
- yahoo_sendfile_send_cb, xfer);
- yahoo_sendfile_send_cb(xfer, source, PURPLE_INPUT_WRITE);
- }
-}
-
-static void yahoo_xfer_init(PurpleXfer *xfer)
-{
- struct yahoo_xfer_data *xfer_data;
- PurpleConnection *gc;
- PurpleAccount *account;
- YahooData *yd;
-
- xfer_data = xfer->data;
- gc = xfer_data->gc;
- yd = gc->proto_data;
- account = purple_connection_get_account(gc);
-
- if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
- if (yd->jp) {
- if (purple_proxy_connect(gc, account, purple_account_get_string(account, "xferjp_host", YAHOOJP_XFER_HOST),
- purple_account_get_int(account, "xfer_port", YAHOO_XFER_PORT),
- yahoo_sendfile_connected, xfer) == NULL)
- {
- purple_notify_error(gc, NULL, _("File Transfer Failed"),
- _("Unable to establish file descriptor."));
- purple_xfer_cancel_remote(xfer);
- }
- } else {
- if (purple_proxy_connect(gc, account, purple_account_get_string(account, "xfer_host", YAHOO_XFER_HOST),
- purple_account_get_int(account, "xfer_port", YAHOO_XFER_PORT),
- yahoo_sendfile_connected, xfer) == NULL)
- {
- purple_notify_error(gc, NULL, _("File Transfer Failed"),
- _("Unable to establish file descriptor."));
- purple_xfer_cancel_remote(xfer);
- }
- }
- } else {
- xfer->fd = -1;
- if (purple_proxy_connect(gc, account, xfer_data->host, xfer_data->port,
- yahoo_receivefile_connected, xfer) == NULL) {
- purple_notify_error(gc, NULL, _("File Transfer Failed"),
- _("Unable to establish file descriptor."));
- purple_xfer_cancel_remote(xfer);
- }
- }
+ return g_strdup_printf("http://%s/relay?token=%s&sender=%s&recver=%s",
+ host, purple_url_encode(xfer_data->xfer_idstring_for_relay),
+ purple_normalize(account, purple_account_get_username(account)),
+ purple_xfer_get_remote_user(xfer));
}
-static void yahoo_xfer_init_15(PurpleXfer *xfer)
+static void
+yahoo_xfer_init_15(PurpleXfer *xfer)
{
struct yahoo_xfer_data *xfer_data;
PurpleConnection *gc;
@@ -357,12 +148,12 @@ static void yahoo_xfer_init_15(PurpleXfer *xfer)
YahooData *yd;
struct yahoo_packet *pkt;
- xfer_data = xfer->data;
+ xfer_data = purple_xfer_get_protocol_data(xfer);
gc = xfer_data->gc;
- yd = gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
account = purple_connection_get_account(gc);
- if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
+ if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND) {
gchar *filename;
filename = g_path_get_basename(purple_xfer_get_local_filename(xfer));
pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_15,
@@ -370,14 +161,14 @@ static void yahoo_xfer_init_15(PurpleXfer *xfer)
yd->session_id);
yahoo_packet_hash(pkt, "sssiiiisiii",
1, purple_normalize(account, purple_account_get_username(account)),
- 5, xfer->who,
+ 5, purple_xfer_get_remote_user(xfer),
265, xfer_data->xfer_peer_idstring,
222, 1,
266, 1,
302, 268,
300, 268,
27, filename,
- 28, xfer->size,
+ 28, (int)purple_xfer_get_size(xfer),
301, 268,
303, 268);
g_free(filename);
@@ -388,7 +179,7 @@ static void yahoo_xfer_init_15(PurpleXfer *xfer)
yahoo_packet_hash(pkt, "sssi",
1, purple_normalize(account, purple_account_get_username(account)),
- 5, xfer->who,
+ 5, purple_xfer_get_remote_user(xfer),
265, xfer_data->xfer_peer_idstring,
222, 3);
} else {
@@ -397,7 +188,7 @@ static void yahoo_xfer_init_15(PurpleXfer *xfer)
yahoo_packet_hash(pkt, "sssi",
1, purple_normalize(account, purple_account_get_username(account)),
- 5, xfer->who,
+ 5, purple_xfer_get_remote_user(xfer),
265, xfer_data->xfer_peer_idstring,
271, 1);
}
@@ -405,120 +196,14 @@ static void yahoo_xfer_init_15(PurpleXfer *xfer)
yahoo_packet_send_and_free(pkt, yd);
}
-static void yahoo_xfer_start(PurpleXfer *xfer)
-{
- /* We don't need to do anything here, do we? */
-}
-
-static guint calculate_length(const gchar *l, size_t len)
-{
- int i;
-
- for (i = 0; i < len; i++) {
- if (!g_ascii_isdigit(l[i]))
- continue;
- return strtol(l + i, NULL, 10);
- }
- return 0;
-}
-
-static gssize yahoo_xfer_read(guchar **buffer, PurpleXfer *xfer)
-{
- gchar buf[4096];
- gssize len;
- gchar *start = NULL;
- gchar *length;
- gchar *end;
- int filelen;
- struct yahoo_xfer_data *xd = xfer->data;
-
- if (purple_xfer_get_type(xfer) != PURPLE_XFER_RECEIVE) {
- return 0;
- }
-
- len = read(xfer->fd, buf, sizeof(buf));
-
- if (len <= 0) {
- if ((purple_xfer_get_size(xfer) > 0) &&
- (purple_xfer_get_bytes_sent(xfer) >= purple_xfer_get_size(xfer))) {
- purple_xfer_set_completed(xfer, TRUE);
- return 0;
- } else
- return -1;
- }
-
- if (!xd->started) {
- xd->rxqueue = g_realloc(xd->rxqueue, len + xd->rxlen);
- memcpy(xd->rxqueue + xd->rxlen, buf, len);
- xd->rxlen += len;
-
- length = g_strstr_len(xd->rxqueue, len, "Content-length:");
- /* some proxies re-write this header, changing the capitalization :(
- * technically that's allowed since headers are case-insensitive
- * [RFC 2616, section 4.2] */
- if (length == NULL)
- length = g_strstr_len(xd->rxqueue, len, "Content-Length:");
- if (length) {
- end = g_strstr_len(length, length - xd->rxqueue, "\r\n");
- if (!end)
- return 0;
- if ((filelen = calculate_length(length, len - (length - xd->rxqueue))))
- purple_xfer_set_size(xfer, filelen);
- }
- start = g_strstr_len(xd->rxqueue, len, "\r\n\r\n");
- if (start)
- start += 4;
- if (!start || start > (xd->rxqueue + len))
- return 0;
- xd->started = TRUE;
-
- len -= (start - xd->rxqueue);
-
- *buffer = g_malloc(len);
- memcpy(*buffer, start, len);
- g_free(xd->rxqueue);
- xd->rxqueue = NULL;
- xd->rxlen = 0;
- } else {
- *buffer = g_malloc(len);
- memcpy(*buffer, buf, len);
- }
-
- return len;
-}
-
-static gssize yahoo_xfer_write(const guchar *buffer, size_t size, PurpleXfer *xfer)
-{
- gssize len;
- struct yahoo_xfer_data *xd = xfer->data;
-
- if (!xd)
- return -1;
-
- if (purple_xfer_get_type(xfer) != PURPLE_XFER_SEND) {
- return -1;
- }
-
- len = write(xfer->fd, buffer, size);
-
- if (len == -1) {
- if (purple_xfer_get_bytes_sent(xfer) >= purple_xfer_get_size(xfer))
- purple_xfer_set_completed(xfer, TRUE);
- if ((errno != EAGAIN) && (errno != EINTR))
- return -1;
- return 0;
- }
-
- return len;
-}
-
-static void yahoo_xfer_cancel_send(PurpleXfer *xfer)
+static void
+yahoo_xfer_cancel_send(PurpleXfer *xfer)
{
struct yahoo_xfer_data *xfer_data;
- xfer_data = xfer->data;
+ xfer_data = purple_xfer_get_protocol_data(xfer);
- if(purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL && xfer_data->version == 15)
+ if(purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL)
{
PurpleConnection *gc;
PurpleAccount *account;
@@ -526,7 +211,7 @@ static void yahoo_xfer_cancel_send(PurpleXfer *xfer)
struct yahoo_packet *pkt;
gc = xfer_data->gc;
- yd = gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
account = purple_connection_get_account(gc);
if(xfer_data->xfer_idstring_for_relay) /* hack to see if file trans acc/info packet has been received */
{
@@ -535,7 +220,7 @@ static void yahoo_xfer_cancel_send(PurpleXfer *xfer)
yd->session_id);
yahoo_packet_hash(pkt, "sssi",
1, purple_normalize(account, purple_account_get_username(account)),
- 5, xfer->who,
+ 5, purple_xfer_get_remote_user(xfer),
265, xfer_data->xfer_peer_idstring,
66, -1);
}
@@ -546,26 +231,28 @@ static void yahoo_xfer_cancel_send(PurpleXfer *xfer)
yd->session_id);
yahoo_packet_hash(pkt, "sssi",
1, purple_normalize(account, purple_account_get_username(account)),
- 5, xfer->who,
+ 5, purple_xfer_get_remote_user(xfer),
265, xfer_data->xfer_peer_idstring,
222, 2);
}
yahoo_packet_send_and_free(pkt, yd);
}
-
- if (xfer_data)
+ if (xfer_data) {
+ purple_http_conn_cancel(xfer_data->hc);
yahoo_xfer_data_free(xfer_data);
- xfer->data = NULL;
+ }
+ purple_xfer_set_protocol_data(xfer, NULL);
}
-static void yahoo_xfer_cancel_recv(PurpleXfer *xfer)
+static void
+yahoo_xfer_cancel_recv(PurpleXfer *xfer)
{
struct yahoo_xfer_data *xfer_data;
- xfer_data = xfer->data;
+ xfer_data = purple_xfer_get_protocol_data(xfer);
- if(purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL && xfer_data->version == 15)
+ if(purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL)
{
PurpleConnection *gc;
@@ -574,7 +261,7 @@ static void yahoo_xfer_cancel_recv(PurpleXfer *xfer)
struct yahoo_packet *pkt;
gc = xfer_data->gc;
- yd = gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
account = purple_connection_get_account(gc);
if(!xfer_data->xfer_idstring_for_relay) /* hack to see if file trans acc/info packet has been received */
{
@@ -583,7 +270,7 @@ static void yahoo_xfer_cancel_recv(PurpleXfer *xfer)
yd->session_id);
yahoo_packet_hash(pkt, "sssi",
1, purple_normalize(account, purple_account_get_username(account)),
- 5, xfer->who,
+ 5, purple_xfer_get_remote_user(xfer),
265, xfer_data->xfer_peer_idstring,
222, 4);
}
@@ -594,36 +281,18 @@ static void yahoo_xfer_cancel_recv(PurpleXfer *xfer)
yd->session_id);
yahoo_packet_hash(pkt, "sssi",
1, purple_normalize(account, purple_account_get_username(account)),
- 5, xfer->who,
+ 5, purple_xfer_get_remote_user(xfer),
265, xfer_data->xfer_peer_idstring,
66, -1);
}
yahoo_packet_send_and_free(pkt, yd);
}
- if (xfer_data)
+ if (xfer_data) {
+ purple_http_conn_cancel(xfer_data->hc);
yahoo_xfer_data_free(xfer_data);
- xfer->data = NULL;
-}
-
-/* Send HTTP OK after receiving file */
-static void yahoo_p2p_ft_server_send_OK(PurpleXfer *xfer)
-{
- char *tx = NULL;
- int written;
-
- tx = g_strdup_printf("HTTP/1.1 200 OK\r\nContent-Length: 0\r\nContent-Type: application/octet-stream\r\nConnection: close\r\n\r\n");
- written = write(xfer->fd, tx, strlen(tx));
-
- if (written < 0 && errno == EAGAIN)
- written = 0;
- else if (written <= 0)
- purple_debug_info("yahoo", "p2p filetransfer: Unable to write HTTP OK");
-
- /* close connection */
- close(xfer->fd);
- xfer->fd = -1;
- g_free(tx);
+ }
+ purple_xfer_set_protocol_data(xfer, NULL);
}
static void yahoo_xfer_end(PurpleXfer *xfer_old)
@@ -633,15 +302,10 @@ static void yahoo_xfer_end(PurpleXfer *xfer_old)
PurpleConnection *gc;
YahooData *yd;
- xfer_data = xfer_old->data;
- if(xfer_data && xfer_data->version == 15
- && purple_xfer_get_type(xfer_old) == PURPLE_XFER_RECEIVE
+ xfer_data = purple_xfer_get_protocol_data(xfer_old);
+ if(xfer_data
+ && purple_xfer_get_xfer_type(xfer_old) == PURPLE_XFER_TYPE_RECEIVE
&& xfer_data->filename_list) {
-
- /* Send HTTP OK in case of p2p transfer, when we act as server */
- if((xfer_data->xfer_url != NULL) && (xfer_old->fd >=0) && (purple_xfer_get_status(xfer_old) == PURPLE_XFER_STATUS_DONE))
- yahoo_p2p_ft_server_send_OK(xfer_old);
-
/* removing top of filename & size list completely */
g_free( xfer_data->filename_list->data );
g_free( xfer_data->size_list->data );
@@ -656,78 +320,51 @@ static void yahoo_xfer_end(PurpleXfer *xfer_old)
if(xfer_data->filename_list)
{
gchar* filename;
- long filesize;
+ char *utf8_filename;
filename = xfer_data->filename_list->data;
- filesize = atol( xfer_data->size_list->data );
gc = xfer_data->gc;
- yd = gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
/* setting up xfer_data for next file's tranfer */
g_free(xfer_data->host);
g_free(xfer_data->path);
- g_free(xfer_data->txbuf);
- g_free(xfer_data->rxqueue);
g_free(xfer_data->xfer_idstring_for_relay);
- if (xfer_data->tx_handler)
- purple_input_remove(xfer_data->tx_handler);
xfer_data->host = NULL;
xfer_data->host = NULL;
- xfer_data->port = 0;
- xfer_data->expires = 0;
- xfer_data->started = FALSE;
- xfer_data->txbuf = NULL;
- xfer_data->txbuflen = 0;
- xfer_data->txbuf_written = 0;
- xfer_data->tx_handler = 0;
- xfer_data->rxqueue = NULL;
- xfer_data->rxlen = 0;
xfer_data->xfer_idstring_for_relay = NULL;
xfer_data->info_val_249 = 0;
- xfer_data->status_15 = STARTED;
xfer_data->firstoflist = FALSE;
/* Dereference xfer_data from old xfer */
- xfer_old->data = NULL;
+ purple_xfer_set_protocol_data(xfer_old, NULL);
/* Build the file transfer handle. */
- xfer = purple_xfer_new(gc->account, PURPLE_XFER_RECEIVE, xfer_old->who);
-
-
- if (xfer) {
- /* Set the info about the incoming file. */
- char *utf8_filename = yahoo_string_decode(gc, filename, TRUE);
- purple_xfer_set_filename(xfer, utf8_filename);
- g_free(utf8_filename);
- purple_xfer_set_size(xfer, filesize);
-
- xfer->data = xfer_data;
-
- /* Setup our I/O op functions */
- purple_xfer_set_init_fnc(xfer, yahoo_xfer_init_15);
- purple_xfer_set_start_fnc(xfer, yahoo_xfer_start);
- purple_xfer_set_end_fnc(xfer, yahoo_xfer_end);
- purple_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send);
- purple_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv);
- purple_xfer_set_read_fnc(xfer, yahoo_xfer_read);
- purple_xfer_set_write_fnc(xfer, yahoo_xfer_write);
- purple_xfer_set_request_denied_fnc(xfer,yahoo_xfer_cancel_recv);
-
- /* update map to current xfer */
- g_hash_table_remove(yd->xfer_peer_idstring_map, xfer_data->xfer_peer_idstring);
- g_hash_table_insert(yd->xfer_peer_idstring_map, xfer_data->xfer_peer_idstring, xfer);
-
- /* Now perform the request */
- purple_xfer_request(xfer);
- }
+ xfer = yahoo_ft_new_xfer_struct(gc, PURPLE_XFER_TYPE_RECEIVE, purple_xfer_get_remote_user(xfer_old));
+
+ g_return_if_fail(xfer != NULL);
+
+ /* Set the info about the incoming file. */
+ utf8_filename = yahoo_string_decode(gc, filename, TRUE);
+ purple_xfer_set_filename(xfer, utf8_filename);
+ g_free(utf8_filename);
+
+ purple_xfer_set_protocol_data(xfer, xfer_data);
+
+ /* update map to current xfer */
+ g_hash_table_remove(yd->xfer_peer_idstring_map, xfer_data->xfer_peer_idstring);
+ g_hash_table_insert(yd->xfer_peer_idstring_map, xfer_data->xfer_peer_idstring, xfer);
+
+ /* Now perform the request */
+ purple_xfer_request(xfer);
+
return;
}
}
if (xfer_data)
yahoo_xfer_data_free(xfer_data);
- xfer_old->data = NULL;
-
+ purple_xfer_set_protocol_data(xfer_old, NULL);
}
void yahoo_process_p2pfilexfer(PurpleConnection *gc, struct yahoo_packet *pkt)
@@ -818,162 +455,6 @@ void yahoo_process_p2pfilexfer(PurpleConnection *gc, struct yahoo_packet *pkt)
}
}
-void yahoo_process_filetransfer(PurpleConnection *gc, struct yahoo_packet *pkt)
-{
- char *from = NULL;
- char *msg = NULL;
- char *url = NULL;
- char *imv = NULL;
- PurpleXfer *xfer;
- YahooData *yd;
- struct yahoo_xfer_data *xfer_data;
- char *service = NULL;
- char *filename = NULL;
- unsigned long filesize = 0L;
- GSList *l;
-
- yd = gc->proto_data;
-
- for (l = pkt->hash; l; l = l->next) {
- struct yahoo_pair *pair = l->data;
-
- switch (pair->key) {
- case 4:
- if (g_utf8_validate(pair->value, -1, NULL)) {
- from = pair->value;
- } else {
- purple_debug_warning("yahoo", "yahoo_process_filetransfer "
- "got non-UTF-8 string for key %d\n", pair->key);
- }
- break;
- case 5: /* to */
- break;
- case 14:
- if (g_utf8_validate(pair->value, -1, NULL)) {
- msg = pair->value;
- } else {
- purple_debug_warning("yahoo", "yahoo_process_filetransfer "
- "got non-UTF-8 string for key %d\n", pair->key);
- }
- break;
- case 20:
- if (g_utf8_validate(pair->value, -1, NULL)) {
- url = pair->value;
- } else {
- purple_debug_warning("yahoo", "yahoo_process_filetransfer "
- "got non-UTF-8 string for key %d\n", pair->key);
- }
- break;
- case 38: /* expires */
- break;
- case 27:
- filename = pair->value;
- break;
- case 28:
- filesize = atol(pair->value);
- break;
- case 49:
- if (g_utf8_validate(pair->value, -1, NULL)) {
- service = pair->value;
- } else {
- purple_debug_warning("yahoo", "yahoo_process_filetransfer "
- "got non-UTF-8 string for key %d\n", pair->key);
- }
- break;
- case 63:
- if (g_utf8_validate(pair->value, -1, NULL)) {
- imv = pair->value;
- } else {
- purple_debug_warning("yahoo", "yahoo_process_filetransfer "
- "got non-UTF-8 string for key %d\n", pair->key);
- }
- break;
- }
- }
-
- /*
- * The remote user has changed their IMVironment. We
- * record it for later use.
- */
- if (from && imv && service && (strcmp("IMVIRONMENT", service) == 0)) {
- g_hash_table_replace(yd->imvironments, g_strdup(from), g_strdup(imv));
- return;
- }
-
- if (pkt->service == YAHOO_SERVICE_P2PFILEXFER) {
- if (service && (strcmp("FILEXFER", service) != 0)) {
- purple_debug_misc("yahoo", "unhandled service 0x%02x\n", pkt->service);
- return;
- }
- }
-
- if (msg) {
- char *tmp;
- tmp = strchr(msg, '\006');
- if (tmp)
- *tmp = '\0';
- }
-
- if (!url || !from)
- return;
-
- /* Setup the Yahoo-specific file transfer data */
- xfer_data = g_new0(struct yahoo_xfer_data, 1);
- xfer_data->gc = gc;
- if (!purple_url_parse(url, &(xfer_data->host), &(xfer_data->port), &(xfer_data->path), NULL, NULL)) {
- g_free(xfer_data);
- return;
- }
-
- purple_debug_misc("yahoo_filexfer", "Host is %s, port is %d, path is %s, and the full url was %s.\n",
- xfer_data->host, xfer_data->port, xfer_data->path, url);
-
- /* Build the file transfer handle. */
- xfer = purple_xfer_new(gc->account, PURPLE_XFER_RECEIVE, from);
- if (xfer == NULL) {
- g_free(xfer_data);
- g_return_if_reached();
- }
-
- xfer->data = xfer_data;
-
- /* Set the info about the incoming file. */
- if (filename) {
- char *utf8_filename = yahoo_string_decode(gc, filename, TRUE);
- purple_xfer_set_filename(xfer, utf8_filename);
- g_free(utf8_filename);
- } else {
- gchar *start, *end;
- start = g_strrstr(xfer_data->path, "/");
- if (start)
- start++;
- end = g_strrstr(xfer_data->path, "?");
- if (start && *start && end) {
- char *utf8_filename;
- filename = g_strndup(start, end - start);
- utf8_filename = yahoo_string_decode(gc, filename, TRUE);
- g_free(filename);
- purple_xfer_set_filename(xfer, utf8_filename);
- g_free(utf8_filename);
- filename = NULL;
- }
- }
-
- purple_xfer_set_size(xfer, filesize);
-
- /* Setup our I/O op functions */
- purple_xfer_set_init_fnc(xfer, yahoo_xfer_init);
- purple_xfer_set_start_fnc(xfer, yahoo_xfer_start);
- purple_xfer_set_end_fnc(xfer, yahoo_xfer_end);
- purple_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send);
- purple_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv);
- purple_xfer_set_read_fnc(xfer, yahoo_xfer_read);
- purple_xfer_set_write_fnc(xfer, yahoo_xfer_write);
-
- /* Now perform the request */
- purple_xfer_request(xfer);
-}
-
PurpleXfer *yahoo_new_xfer(PurpleConnection *gc, const char *who)
{
PurpleXfer *xfer;
@@ -985,23 +466,14 @@ PurpleXfer *yahoo_new_xfer(PurpleConnection *gc, const char *who)
xfer_data->gc = gc;
/* Build the file transfer handle. */
- xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who);
+ xfer = yahoo_ft_new_xfer_struct(gc, PURPLE_XFER_TYPE_SEND, who);
if (xfer == NULL)
{
g_free(xfer_data);
g_return_val_if_reached(NULL);
}
- xfer->data = xfer_data;
-
- /* Setup our I/O op functions */
- purple_xfer_set_init_fnc(xfer, yahoo_xfer_init);
- purple_xfer_set_start_fnc(xfer, yahoo_xfer_start);
- purple_xfer_set_end_fnc(xfer, yahoo_xfer_end);
- purple_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send);
- purple_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv);
- purple_xfer_set_read_fnc(xfer, yahoo_xfer_read);
- purple_xfer_set_write_fnc(xfer, yahoo_xfer_write);
+ purple_xfer_set_protocol_data(xfer, xfer_data);
return xfer;
}
@@ -1037,16 +509,15 @@ static void yahoo_xfer_dns_connected_15(GSList *hosts, gpointer data, const char
PurpleConnection *gc;
PurpleAccount *account;
YahooData *yd;
- gchar *url;
gchar *filename;
if (!(xfer = data))
return;
- if (!(xd = xfer->data))
+ if (!(xd = purple_xfer_get_protocol_data(xfer)))
return;
gc = xd->gc;
account = purple_connection_get_account(gc);
- yd = gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
if(!hosts)
{
@@ -1074,12 +545,8 @@ static void yahoo_xfer_dns_connected_15(GSList *hosts, gpointer data, const char
b = actaddr & 0xff;
actaddr >>= 8;
a = actaddr & 0xff;
- if(yd->jp)
- xd->port = YAHOOJP_XFER_RELAY_PORT;
- else
- xd->port = YAHOO_XFER_RELAY_PORT;
- url = g_strdup_printf("%u.%u.%u.%u", d, c, b, a);
+ xd->host = g_strdup_printf("%u.%u.%u.%u", d, c, b, a);
/* Free the address... */
g_free(hosts->data);
@@ -1094,19 +561,12 @@ static void yahoo_xfer_dns_connected_15(GSList *hosts, gpointer data, const char
hosts = g_slist_remove(hosts, hosts->data);
}
- if (!purple_url_parse(url, &(xd->host), &(xd->port), &(xd->path), NULL, NULL)) {
- purple_xfer_cancel_remote(xfer);
- g_free(url);
- return;
- }
- g_free(url);
-
pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_INFO_15, YAHOO_STATUS_AVAILABLE, yd->session_id);
filename = g_path_get_basename(purple_xfer_get_local_filename(xfer));
yahoo_packet_hash(pkt, "ssssis",
1, purple_normalize(account, purple_account_get_username(account)),
- 5, xfer->who,
+ 5, purple_xfer_get_remote_user(xfer),
265, xd->xfer_peer_idstring,
27, filename,
249, 3,
@@ -1126,19 +586,18 @@ gboolean yahoo_can_receive_file(PurpleConnection *gc, const char *who)
void yahoo_send_file(PurpleConnection *gc, const char *who, const char *file)
{
struct yahoo_xfer_data *xfer_data;
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
PurpleXfer *xfer = yahoo_new_xfer(gc, who);
g_return_if_fail(xfer != NULL);
+#if 0
/* if we don't have a p2p connection, try establishing it now */
if( !g_hash_table_lookup(yd->peers, who) )
yahoo_send_p2p_pkt(gc, who, 0);
+#endif
- xfer_data = xfer->data;
- xfer_data->status_15 = STARTED;
- purple_xfer_set_init_fnc(xfer, yahoo_xfer_init_15);
- xfer_data->version = 15;
+ xfer_data = purple_xfer_get_protocol_data(xfer);
xfer_data->xfer_peer_idstring = yahoo_xfer_new_xfer_id();
g_hash_table_insert(yd->xfer_peer_idstring_map, xfer_data->xfer_peer_idstring, xfer);
@@ -1149,496 +608,6 @@ void yahoo_send_file(PurpleConnection *gc, const char *who, const char *file)
purple_xfer_request(xfer);
}
-static void yahoo_p2p_ft_server_listen_cb(int listenfd, gpointer data); /* using this in yahoo_xfer_send_cb_15 */
-static void yahoo_xfer_connected_15(gpointer data, gint source, const gchar *error_message);/* using this in recv_cb */
-
-static void yahoo_xfer_recv_cb_15(gpointer data, gint source, PurpleInputCondition condition)
-{
- PurpleXfer *xfer;
- struct yahoo_xfer_data *xd;
- int did;
- gchar* buf;
- gchar* t;
- PurpleAccount *account;
- PurpleConnection *gc;
-
- xfer = data;
- xd = xfer->data;
- account = purple_connection_get_account(xd->gc);
- gc = xd->gc;
-
- buf=g_strnfill(1000, 0);
- while((did = read(source, buf, 998)) > 0)
- {
- xd->txbuflen += did;
- buf[did] = '\0';
- t = xd->txbuf;
- xd->txbuf = g_strconcat(t,buf,NULL);
- g_free(t);
- }
- g_free(buf);
-
- if (did < 0 && errno == EAGAIN)
- return;
- else if (did < 0) {
- purple_debug_error("yahoo", "Unable to write in order to start ft errno = %d\n", errno);
- purple_xfer_cancel_remote(xfer);
- return;
- }
-
- purple_input_remove(xd->tx_handler);
- xd->tx_handler = 0;
- xd->txbuflen = 0;
-
- if(xd->status_15 == HEAD_REQUESTED) {
- xd->status_15 = HEAD_REPLY_RECEIVED;
- close(source);/* Is this required? */
- g_free(xd->txbuf);
- xd->txbuf = NULL;
- if (purple_proxy_connect(gc, account, xd->host, xd->port, yahoo_xfer_connected_15, xfer) == NULL)
- {
- purple_notify_error(gc, NULL, _("File Transfer Failed"),
- _("Unable to establish file descriptor."));
- purple_xfer_cancel_remote(xfer);
- }
- } else {
- purple_debug_error("yahoo","Unrecognized yahoo file transfer mode and stage (ymsg15):%d,%d\n",
- purple_xfer_get_type(xfer),
- xd->status_15);
- return;
- }
-}
-
-static void yahoo_xfer_send_cb_15(gpointer data, gint source, PurpleInputCondition condition)
-{
- PurpleXfer *xfer;
- struct yahoo_xfer_data *xd;
- int remaining, written;
-
- xfer = data;
- xd = xfer->data;
- remaining = xd->txbuflen - xd->txbuf_written;
- written = write(source, xd->txbuf + xd->txbuf_written, remaining);
-
- if (written < 0 && errno == EAGAIN)
- written = 0;
- else if (written <= 0) {
- purple_debug_error("yahoo", "Unable to write in order to start ft errno = %d\n", errno);
- purple_xfer_cancel_remote(xfer);
- return;
- }
-
- if (written < remaining) {
- xd->txbuf_written += written;
- return;
- }
-
- purple_input_remove(xd->tx_handler);
- xd->tx_handler = 0;
- g_free(xd->txbuf);
- xd->txbuf = NULL;
- xd->txbuflen = 0;
- xd->txbuf_written = 0;
-
- if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE && xd->status_15 == STARTED)
- {
- xd->status_15 = HEAD_REQUESTED;
- xd->tx_handler = purple_input_add(source, PURPLE_INPUT_READ, yahoo_xfer_recv_cb_15, xfer);
- yahoo_xfer_recv_cb_15(xfer, source, PURPLE_INPUT_READ);
- }
- else if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE && xd->status_15 == HEAD_REPLY_RECEIVED)
- {
- xd->status_15 = TRANSFER_PHASE;
- xfer->fd = source;
- purple_xfer_start(xfer, source, NULL, 0);
- }
- else if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && (xd->status_15 == ACCEPTED || xd->status_15 == P2P_GET_REQUESTED) )
- {
- xd->status_15 = TRANSFER_PHASE;
- xfer->fd = source;
- /* Remove Read event */
- purple_input_remove(xd->input_event);
- xd->input_event = 0;
- purple_xfer_start(xfer, source, NULL, 0);
- }
- else if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && xd->status_15 == P2P_HEAD_REQUESTED)
- {
- xd->status_15 = P2P_HEAD_REPLIED;
- /* Remove Read event and close descriptor */
- purple_input_remove(xd->input_event);
- xd->input_event = 0;
- close(source);
- xfer->fd = -1;
- /* start local server, listen for connections */
- purple_network_listen(xd->yahoo_local_p2p_ft_server_port, SOCK_STREAM, yahoo_p2p_ft_server_listen_cb, xfer);
- }
- else
- {
- purple_debug_error("yahoo", "Unrecognized yahoo file transfer mode and stage (ymsg15):%d,%d\n", purple_xfer_get_type(xfer), xd->status_15);
- return;
- }
-}
-
-static void yahoo_xfer_connected_15(gpointer data, gint source, const gchar *error_message)
-{
- PurpleXfer *xfer;
- struct yahoo_xfer_data *xd;
- PurpleAccount *account;
- PurpleConnection *gc;
-
- if (!(xfer = data))
- return;
- if (!(xd = xfer->data))
- return;
- gc = xd->gc;
- account = purple_connection_get_account(gc);
- if ((source < 0) || (xd->path == NULL) || (xd->host == NULL)) {
- purple_xfer_error(PURPLE_XFER_RECEIVE, purple_xfer_get_account(xfer),
- xfer->who, _("Unable to connect."));
- purple_xfer_cancel_remote(xfer);
- return;
- }
- /* The first time we get here, assemble the tx buffer */
- if (xd->txbuflen == 0)
- {
- gchar* cookies;
- YahooData *yd = gc->proto_data;
-
- /* cookies = yahoo_get_cookies(gc);
- * This doesn't seem to be working. The function is returning NULL, which yahoo servers don't like
- * For now let us not use this function */
-
- cookies = g_strdup_printf("Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
-
- if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && xd->status_15 == ACCEPTED)
- {
- if(xd->info_val_249 == 2)
- {
- /* sending file via p2p, we are connected as client */
- xd->txbuf = g_strdup_printf("POST /%s HTTP/1.1\r\n"
- "User-Agent: " YAHOO_CLIENT_USERAGENT "\r\n"
- "Host: %s\r\n"
- "Content-Length: %ld\r\n"
- "Cache-Control: no-cache\r\n\r\n",
- xd->path,
- xd->host,
- (long int)xfer->size); /* to do, add Referer */
- }
- else
- {
- /* sending file via relaying */
- xd->txbuf = g_strdup_printf("POST /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\n"
- "Cookie:%s\r\n"
- "User-Agent: " YAHOO_CLIENT_USERAGENT "\r\n"
- "Host: %s\r\n"
- "Content-Length: %ld\r\n"
- "Cache-Control: no-cache\r\n\r\n",
- purple_url_encode(xd->xfer_idstring_for_relay),
- purple_normalize(account, purple_account_get_username(account)),
- xfer->who,
- cookies,
- xd->host,
- (long int)xfer->size);
- }
- }
- else if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE && xd->status_15 == STARTED)
- {
- if(xd->info_val_249 == 1)
- {
- /* receiving file via p2p, connected as client */
- xd->txbuf = g_strdup_printf("HEAD /%s HTTP/1.1\r\n"
- "Accept: */*\r\n"
- "User-Agent: " YAHOO_CLIENT_USERAGENT "\r\n"
- "Host: %s\r\n"
- "Content-Length: 0\r\n"
- "Cache-Control: no-cache\r\n\r\n",
- xd->path,xd->host);
- }
- else
- {
- /* receiving file via relaying */
- xd->txbuf = g_strdup_printf("HEAD /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\n"
- "Accept: */*\r\n"
- "Cookie: %s\r\n"
- "User-Agent: " YAHOO_CLIENT_USERAGENT "\r\n"
- "Host: %s\r\n"
- "Content-Length: 0\r\n"
- "Cache-Control: no-cache\r\n\r\n",
- purple_url_encode(xd->xfer_idstring_for_relay),
- purple_normalize(account, purple_account_get_username(account)),
- xfer->who,
- cookies,
- xd->host);
- }
- }
- else if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE && xd->status_15 == HEAD_REPLY_RECEIVED)
- {
- if(xd->info_val_249 == 1)
- {
- /* receiving file via p2p, connected as client */
- xd->txbuf = g_strdup_printf("GET /%s HTTP/1.1\r\n"
- "User-Agent: " YAHOO_CLIENT_USERAGENT "\r\n"
- "Host: %s\r\n"
- "Connection: Keep-Alive\r\n\r\n",
- xd->path, xd->host);
- }
- else
- {
- /* receiving file via relaying */
- xd->txbuf = g_strdup_printf("GET /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\n"
- "Cookie: %s\r\n"
- "User-Agent: " YAHOO_CLIENT_USERAGENT "\r\n"
- "Host: %s\r\n"
- "Connection: Keep-Alive\r\n\r\n",
- purple_url_encode(xd->xfer_idstring_for_relay),
- purple_normalize(account, purple_account_get_username(account)),
- xfer->who,
- cookies,
- xd->host);
- }
- }
- else
- {
- purple_debug_error("yahoo", "Unrecognized yahoo file transfer mode and stage (ymsg15):%d,%d\n", purple_xfer_get_type(xfer), xd->status_15);
- g_free(cookies);
- return;
- }
- xd->txbuflen = strlen(xd->txbuf);
- xd->txbuf_written = 0;
- g_free(cookies);
- }
-
- if (!xd->tx_handler)
- {
- xd->tx_handler = purple_input_add(source, PURPLE_INPUT_WRITE,
- yahoo_xfer_send_cb_15, xfer);
- yahoo_xfer_send_cb_15(xfer, source, PURPLE_INPUT_WRITE);
- }
-}
-
-static void yahoo_p2p_ft_POST_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
- PurpleXfer *xfer;
- struct yahoo_xfer_data *xd;
-
- xfer = data;
- if (!(xd = xfer->data)) {
- purple_xfer_cancel_remote(xfer);
- return;
- }
-
- purple_input_remove(xd->input_event);
- xd->status_15 = TRANSFER_PHASE;
- xfer->fd = source;
- purple_xfer_start(xfer, source, NULL, 0);
-}
-
-static void yahoo_p2p_ft_HEAD_GET_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
- PurpleXfer *xfer;
- struct yahoo_xfer_data *xd;
- guchar buf[1024];
- int len;
- char *url_head;
- char *url_get;
- time_t unix_time;
- char *time_str;
-
- xfer = data;
- if (!(xd = xfer->data)) {
- purple_xfer_cancel_remote(xfer);
- return;
- }
-
- len = read(source, buf, sizeof(buf));
- if ((len < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
- return ; /* No Worries*/
- else if (len <= 0) {
- purple_debug_warning("yahoo","p2p-ft: Error in connection, or host disconnected\n");
- purple_input_remove(xd->input_event);
- purple_xfer_cancel_remote(xfer);
- return;
- }
-
- url_head = g_strdup_printf("HEAD %s", xd->xfer_url);
- url_get = g_strdup_printf("GET %s", xd->xfer_url);
-
- if( strncmp(url_head, (char *)buf, strlen(url_head)) == 0 )
- xd->status_15 = P2P_HEAD_REQUESTED;
- else if( strncmp(url_get, (char *)buf, strlen(url_get)) == 0 )
- xd->status_15 = P2P_GET_REQUESTED;
- else {
- purple_debug_warning("yahoo","p2p-ft: Wrong HEAD/GET request from peer, disconnecting host\n");
- purple_input_remove(xd->input_event);
- purple_xfer_cancel_remote(xfer);
- g_free(url_head);
- return;
- }
-
- unix_time = time(NULL);
- time_str = ctime(&unix_time);
- time_str[strlen(time_str) - 1] = '\0';
-
- if (xd->txbuflen == 0) {
- xd->txbuf = g_strdup_printf("HTTP/1.0 200 OK\r\n"
- "Date: %s GMT\r\n"
- "Server: Y!/1.0\r\n"
- "MIME-version: 1.0\r\n"
- "Last-modified: %s GMT\r\n"
- "Content-length: %" G_GSIZE_FORMAT "\r\n\r\n",
- time_str, time_str, xfer->size);
- xd->txbuflen = strlen(xd->txbuf);
- xd->txbuf_written = 0;
- }
-
- if (!xd->tx_handler) {
- xd->tx_handler = purple_input_add(source, PURPLE_INPUT_WRITE, yahoo_xfer_send_cb_15, xfer);
- yahoo_xfer_send_cb_15(xfer, source, PURPLE_INPUT_WRITE);
- }
-
- g_free(url_head);
- g_free(url_get);
-}
-
-static void yahoo_p2p_ft_server_send_connected_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
- int acceptfd;
- PurpleXfer *xfer;
- struct yahoo_xfer_data *xd;
-
- xfer = data;
- if (!(xd = xfer->data)) {
- purple_xfer_cancel_remote(xfer);
- return;
- }
-
- acceptfd = accept(source, NULL, 0);
- if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
- return;
- else if(acceptfd == -1) {
- purple_debug_warning("yahoo","yahoo_p2p_server_send_connected_cb: accept: %s\n", g_strerror(errno));
- purple_xfer_cancel_remote(xfer);
- /* remove watcher and close p2p ft server */
- purple_input_remove(xd->yahoo_p2p_ft_server_watcher);
- close(xd->yahoo_local_p2p_ft_server_fd);
- return;
- }
-
- /* remove watcher and close p2p ft server */
- purple_input_remove(xd->yahoo_p2p_ft_server_watcher);
- close(xd->yahoo_local_p2p_ft_server_fd);
-
- /* Add an Input Read event to the file descriptor */
- xfer->fd = acceptfd;
- if(xfer->type == PURPLE_XFER_RECEIVE)
- xd->input_event = purple_input_add(acceptfd, PURPLE_INPUT_READ, yahoo_p2p_ft_POST_cb, data);
- else
- xd->input_event = purple_input_add(acceptfd, PURPLE_INPUT_READ, yahoo_p2p_ft_HEAD_GET_cb, data);
-}
-
-static void yahoo_p2p_ft_server_listen_cb(int listenfd, gpointer data)
-{
- PurpleXfer *xfer;
- struct yahoo_xfer_data *xd;
- struct yahoo_packet *pkt;
- PurpleAccount *account;
- YahooData *yd;
- gchar *filename;
- const char *local_ip;
- gchar *url_to_send = NULL;
- char *filename_without_spaces = NULL;
-
- xfer = data;
- if (!(xd = xfer->data) || (listenfd == -1)) {
- purple_debug_warning("yahoo","p2p: error starting server for p2p file transfer\n");
- purple_xfer_cancel_remote(xfer);
- return;
- }
-
- if( (xfer->type == PURPLE_XFER_RECEIVE) || (xd->status_15 != P2P_HEAD_REPLIED) ) {
- yd = xd->gc->proto_data;
- account = purple_connection_get_account(xd->gc);
- local_ip = purple_network_get_my_ip(listenfd);
- xd->yahoo_local_p2p_ft_server_port = purple_network_get_port_from_fd(listenfd);
-
- filename = g_path_get_basename(purple_xfer_get_local_filename(xfer));
- filename_without_spaces = g_strdup(filename);
- purple_util_chrreplace(filename_without_spaces, ' ', '+');
- xd->xfer_url = g_strdup_printf("/Messenger.%s.%d000%s?AppID=Messenger&UserID=%s&K=lc9lu2u89gz1llmplwksajkjx", xfer->who, (int)time(NULL), filename_without_spaces, xfer->who);
- url_to_send = g_strdup_printf("http://%s:%d%s", local_ip, xd->yahoo_local_p2p_ft_server_port, xd->xfer_url);
-
- if(xfer->type == PURPLE_XFER_RECEIVE) {
- xd->info_val_249 = 2; /* 249=2: we are p2p server, and receiving file */
- pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_ACC_15,
- YAHOO_STATUS_AVAILABLE, yd->session_id);
- yahoo_packet_hash(pkt, "ssssis",
- 1, purple_normalize(account, purple_account_get_username(account)),
- 5, xfer->who,
- 265, xd->xfer_peer_idstring,
- 27, xfer->filename,
- 249, 2,
- 250, url_to_send);
- }
- else {
- xd->info_val_249 = 1; /* 249=1: we are p2p server, and sending file */
- pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_INFO_15, YAHOO_STATUS_AVAILABLE, yd->session_id);
- yahoo_packet_hash(pkt, "ssssis",
- 1, purple_normalize(account, purple_account_get_username(account)),
- 5, xfer->who,
- 265, xd->xfer_peer_idstring,
- 27, filename,
- 249, 1,
- 250, url_to_send);
- }
-
- yahoo_packet_send_and_free(pkt, yd);
-
- g_free(filename);
- g_free(url_to_send);
- g_free(filename_without_spaces);
- }
-
- /* Add an Input Read event to the file descriptor */
- xd->yahoo_local_p2p_ft_server_fd = listenfd;
- xd->yahoo_p2p_ft_server_watcher = purple_input_add(listenfd, PURPLE_INPUT_READ, yahoo_p2p_ft_server_send_connected_cb, data);
-}
-
-/* send (p2p) file transfer information */
-static void yahoo_p2p_client_send_ft_info(PurpleConnection *gc, PurpleXfer *xfer)
-{
- struct yahoo_xfer_data *xd;
- struct yahoo_packet *pkt;
- PurpleAccount *account;
- YahooData *yd;
- gchar *filename;
- struct yahoo_p2p_data *p2p_data;
-
- if (!(xd = xfer->data))
- return;
-
- account = purple_connection_get_account(gc);
- yd = gc->proto_data;
-
- p2p_data = g_hash_table_lookup(yd->peers, xfer->who);
- if( p2p_data->connection_type == YAHOO_P2P_WE_ARE_SERVER )
- if(purple_network_listen_range(0, 0, SOCK_STREAM, yahoo_p2p_ft_server_listen_cb, xfer))
- return;
-
- pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_INFO_15, YAHOO_STATUS_AVAILABLE, yd->session_id);
- filename = g_path_get_basename(purple_xfer_get_local_filename(xfer));
-
- yahoo_packet_hash(pkt, "ssssi",
- 1, purple_normalize(account, purple_account_get_username(account)),
- 5, xfer->who,
- 265, xd->xfer_peer_idstring,
- 27, filename,
- 249, 2); /* 249=2: we are p2p client */
- xd->info_val_249 = 2;
- yahoo_packet_send_and_free(pkt, yd);
-
- g_free(filename);
-}
-
void yahoo_process_filetrans_15(PurpleConnection *gc, struct yahoo_packet *pkt)
{
char *from = NULL;
@@ -1651,13 +620,12 @@ void yahoo_process_filetrans_15(PurpleConnection *gc, struct yahoo_packet *pkt)
char *filename = NULL;
char *xfer_peer_idstring = NULL;
char *utf8_filename;
- unsigned long filesize = 0L;
GSList *l;
GSList *filename_list = NULL;
GSList *size_list = NULL;
int nooffiles = 0;
- yd = gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
for (l = pkt->hash; l; l = l->next) {
struct yahoo_pair *pair = l->data;
@@ -1733,10 +701,15 @@ void yahoo_process_filetrans_15(PurpleConnection *gc, struct yahoo_packet *pkt)
if(val_222 == 3)
{
PurpleAccount *account;
+ struct yahoo_xfer_data *xd;
+
xfer = g_hash_table_lookup(yd->xfer_peer_idstring_map,
xfer_peer_idstring);
if(!xfer)
return;
+
+ xd = purple_xfer_get_protocol_data(xfer);
+
/*
* In the file trans info packet that we must reply with, we are
* supposed to mention the ip address...
@@ -1748,20 +721,21 @@ void yahoo_process_filetrans_15(PurpleConnection *gc, struct yahoo_packet *pkt)
/* To send through p2p */
if( g_hash_table_lookup(yd->peers, from) ) {
/* send p2p file transfer information */
- yahoo_p2p_client_send_ft_info(gc, xfer);
- return;
+ purple_debug_error("yahoo", "p2p file transfers are not supported yet\n");
+ /*xd->is_relay = FALSE;*/
}
+ xd->is_relay = TRUE;
account = purple_connection_get_account(gc);
if (yd->jp)
{
- purple_dnsquery_a_account(account, YAHOOJP_XFER_RELAY_HOST,
+ purple_dnsquery_a(account, YAHOOJP_XFER_RELAY_HOST,
YAHOOJP_XFER_RELAY_PORT,
yahoo_xfer_dns_connected_15, xfer);
}
else
{
- purple_dnsquery_a_account(account, YAHOO_XFER_RELAY_HOST,
+ purple_dnsquery_a(account, YAHOO_XFER_RELAY_HOST,
YAHOO_XFER_RELAY_PORT,
yahoo_xfer_dns_connected_15, xfer);
}
@@ -1792,11 +766,9 @@ void yahoo_process_filetrans_15(PurpleConnection *gc, struct yahoo_packet *pkt)
filename_list = g_slist_reverse(filename_list);
size_list = g_slist_reverse(size_list);
filename = filename_list->data;
- filesize = atol(size_list->data);
if(!from) return;
xfer_data = g_new0(struct yahoo_xfer_data, 1);
- xfer_data->version = 15;
xfer_data->firstoflist = TRUE;
xfer_data->gc = gc;
xfer_data->xfer_peer_idstring = g_strdup(xfer_peer_idstring);
@@ -1804,32 +776,14 @@ void yahoo_process_filetrans_15(PurpleConnection *gc, struct yahoo_packet *pkt)
xfer_data->size_list = size_list;
/* Build the file transfer handle. */
- xfer = purple_xfer_new(gc->account, PURPLE_XFER_RECEIVE, from);
- if (xfer == NULL)
- {
- g_free(xfer_data);
- g_return_if_reached();
- }
-
- xfer->message = NULL;
+ xfer = yahoo_ft_new_xfer_struct(gc, PURPLE_XFER_TYPE_RECEIVE, from);
/* Set the info about the incoming file. */
utf8_filename = yahoo_string_decode(gc, filename, TRUE);
purple_xfer_set_filename(xfer, utf8_filename);
g_free(utf8_filename);
- purple_xfer_set_size(xfer, filesize);
-
- xfer->data = xfer_data;
- /* Setup our I/O op functions */
- purple_xfer_set_init_fnc(xfer, yahoo_xfer_init_15);
- purple_xfer_set_start_fnc(xfer, yahoo_xfer_start);
- purple_xfer_set_end_fnc(xfer, yahoo_xfer_end);
- purple_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send);
- purple_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv);
- purple_xfer_set_read_fnc(xfer, yahoo_xfer_read);
- purple_xfer_set_write_fnc(xfer, yahoo_xfer_write);
- purple_xfer_set_request_denied_fnc(xfer,yahoo_xfer_cancel_recv);
+ purple_xfer_set_protocol_data(xfer, xfer_data);
g_hash_table_insert(yd->xfer_peer_idstring_map,
xfer_data->xfer_peer_idstring,
@@ -1837,7 +791,7 @@ void yahoo_process_filetrans_15(PurpleConnection *gc, struct yahoo_packet *pkt)
if(nooffiles > 1) {
gchar* message;
- message = g_strdup_printf(_("%s is trying to send you a group of %d files.\n"), xfer->who, nooffiles);
+ message = g_strdup_printf(_("%s is trying to send you a group of %d files.\n"), purple_xfer_get_remote_user(xfer), nooffiles);
purple_xfer_conversation_write(xfer, message, FALSE);
g_free(message);
}
@@ -1845,6 +799,157 @@ void yahoo_process_filetrans_15(PurpleConnection *gc, struct yahoo_packet *pkt)
purple_xfer_request(xfer);
}
+static void
+yahoo_process_filetrans_15_sent(PurpleHttpConnection *hc,
+ PurpleHttpResponse *response, gpointer _xfer)
+{
+ PurpleXfer *xfer = _xfer;
+ struct yahoo_xfer_data *xd;
+
+ xd = purple_xfer_get_protocol_data(xfer);
+
+ xd->hc = NULL;
+
+ if (purple_xfer_is_cancelled(xfer))
+ return;
+
+ if (!purple_http_response_is_successful(response) ||
+ purple_xfer_get_bytes_remaining(xfer) > 0)
+ {
+ purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE);
+ purple_xfer_end(xfer);
+ } else {
+ purple_xfer_set_completed(xfer, TRUE);
+ purple_xfer_end(xfer);
+ }
+}
+
+static void
+yahoo_process_filetrans_15_downloaded(PurpleHttpConnection *hc,
+ PurpleHttpResponse *response, gpointer _xfer)
+{
+ PurpleXfer *xfer = _xfer;
+ struct yahoo_xfer_data *xd;
+
+ xd = purple_xfer_get_protocol_data(xfer);
+
+ xd->hc = NULL;
+
+ if (purple_xfer_is_cancelled(xfer))
+ return;
+
+ if (!purple_http_response_is_successful(response) ||
+ purple_xfer_get_bytes_remaining(xfer) > 0)
+ {
+ purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE);
+ purple_xfer_end(xfer);
+ } else {
+ purple_xfer_set_completed(xfer, TRUE);
+ purple_xfer_end(xfer);
+ }
+}
+
+static void
+yahoo_process_filetrans_15_reader(PurpleHttpConnection *hc,
+ gchar *buffer, size_t offset, size_t length, gpointer _xfer,
+ PurpleHttpContentReaderCb cb)
+{
+ PurpleXfer *xfer = _xfer;
+ gssize stored;
+
+ if ((goffset)offset != purple_xfer_get_bytes_sent(xfer)) {
+ purple_debug_warning("yahoo",
+ "offset != purple_xfer_get_bytes_sent(xfer)\n");
+ }
+
+ stored = purple_xfer_read_file(xfer, (guchar*)buffer, length);
+
+ cb(hc, (stored >= 0), (purple_xfer_get_bytes_remaining(xfer) == 0),
+ stored);
+}
+
+static gboolean
+yahoo_process_filetrans_15_writer(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, const gchar *buffer, size_t offset,
+ size_t length, gpointer _xfer)
+{
+ PurpleXfer *xfer = _xfer;
+
+ return purple_xfer_write_file(xfer, (const guchar*)buffer, length);
+}
+
+static void
+yahoo_process_filetrans_15_watcher(PurpleHttpConnection *hc,
+ gboolean reading_state, int processed, int total, gpointer _xfer)
+{
+ PurpleXfer *xfer = _xfer;
+
+ if (reading_state !=
+ (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE))
+ {
+ return;
+ }
+
+ purple_xfer_set_size(xfer, total);
+ purple_xfer_update_progress(xfer);
+}
+
+static void yahoo_xfer_start(PurpleXfer *xfer)
+{
+ PurpleHttpRequest *req;
+ struct yahoo_xfer_data *xd;
+
+ xd = purple_xfer_get_protocol_data(xfer);
+
+ req = yahoo_ft_new_req(xd);
+ purple_http_request_set_timeout(req, -1);
+ if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE) {
+ purple_http_request_set_max_len(req, -1);
+ purple_http_request_set_response_writer(req,
+ yahoo_process_filetrans_15_writer, xfer);
+ xd->hc = purple_http_request(xd->gc, req,
+ yahoo_process_filetrans_15_downloaded, xfer);
+ } else {
+ purple_http_request_set_method(req, "POST");
+ /* YHttpServer quirk: it sets content-length, but doesn't sends
+ * any data. */
+ purple_http_request_set_max_len(req, 0);
+ purple_http_request_set_contents_reader(req,
+ yahoo_process_filetrans_15_reader,
+ purple_xfer_get_size(xfer), xfer);
+ xd->hc = purple_http_request(xd->gc, req,
+ yahoo_process_filetrans_15_sent, xfer);
+ }
+
+ purple_http_conn_set_progress_watcher(xd->hc,
+ yahoo_process_filetrans_15_watcher, xfer, -1);
+ purple_http_request_unref(req);
+}
+
+static void
+yahoo_process_filetrans_info_15_got(PurpleHttpConnection *hc,
+ PurpleHttpResponse *response, gpointer _xfer)
+{
+ PurpleXfer *xfer = _xfer;
+ struct yahoo_xfer_data *xd;
+ YahooData *yd;
+
+ xd = purple_xfer_get_protocol_data(xfer);
+ yd = purple_connection_get_protocol_data(xd->gc);
+
+ xd->hc = NULL;
+
+ if (!purple_http_response_is_successful(response)) {
+ purple_notify_error(yd->gc, NULL, _("File Transfer Failed"),
+ _("Unable to get file header."),
+ purple_request_cpar_from_connection(yd->gc));
+ purple_xfer_cancel_remote(xfer);
+ return;
+ }
+
+ purple_xfer_start(xfer, -1, NULL, 0);
+}
+
void yahoo_process_filetrans_info_15(PurpleConnection *gc, struct yahoo_packet *pkt)
{
char *url = NULL;
@@ -1857,9 +962,8 @@ void yahoo_process_filetrans_info_15(PurpleConnection *gc, struct yahoo_packet *
char *xfer_idstring_for_relay = NULL;
GSList *l;
struct yahoo_packet *pkt_to_send;
- struct yahoo_p2p_data *p2p_data;
- yd = gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
for (l = pkt->hash; l; l = l->next) {
struct yahoo_pair *pair = l->data;
@@ -1888,7 +992,7 @@ void yahoo_process_filetrans_info_15(PurpleConnection *gc, struct yahoo_packet *
break;
case 250:
if (g_utf8_validate(pair->value, -1, NULL)) {
- url = pair->value;
+ url = pair->value; /* TODO: rename to host? what about non-relay? */
} else {
purple_debug_warning("yahoo", "yahoo_process_filetrans_info_15 "
"got non-UTF-8 string for key %d\n", pair->key);
@@ -1918,49 +1022,47 @@ void yahoo_process_filetrans_info_15(PurpleConnection *gc, struct yahoo_packet *
return;
}
- xfer_data = xfer->data;
+ xfer_data = purple_xfer_get_protocol_data(xfer);
xfer_data->info_val_249 = val_249;
xfer_data->xfer_idstring_for_relay = g_strdup(xfer_idstring_for_relay);
if(val_249 == 1 || val_249 == 3) {
+ PurpleHttpRequest *req;
PurpleAccount *account;
- if (!purple_url_parse(url, &(xfer_data->host), &(xfer_data->port), &(xfer_data->path), NULL, NULL)) {
+
+ xfer_data->is_relay = (val_249 == 3);
+
+ if (!xfer_data->is_relay) {
+ purple_debug_error("yahoo", "Non-relay FT aren't tested yet.\n");
+ purple_notify_error(gc, NULL, _("File Transfer Failed"),
+ _("Unsupported method"),
+ purple_request_cpar_from_connection(gc));
purple_xfer_cancel_remote(xfer);
- return;
}
account = purple_connection_get_account(xfer_data->gc);
+ xfer_data->url = yahoo_ft_url_gen(xfer, url);
+
pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_ACC_15,
YAHOO_STATUS_AVAILABLE, yd->session_id);
yahoo_packet_hash(pkt_to_send, "ssssis",
1, purple_normalize(account, purple_account_get_username(account)),
- 5, xfer->who,
+ 5, purple_xfer_get_remote_user(xfer),
265, xfer_data->xfer_peer_idstring,
- 27, xfer->filename,
+ 27, purple_xfer_get_filename(xfer),
249, xfer_data->info_val_249,
251, xfer_data->xfer_idstring_for_relay);
yahoo_packet_send_and_free(pkt_to_send, yd);
- if (purple_proxy_connect(gc, account, xfer_data->host, xfer_data->port,
- yahoo_xfer_connected_15, xfer) == NULL) {
- purple_notify_error(gc, NULL, _("File Transfer Failed"),
- _("Unable to establish file descriptor."));
- purple_xfer_cancel_remote(xfer);
- }
- }
- else if(val_249 == 2) {
- p2p_data = g_hash_table_lookup(yd->peers, xfer->who);
- if( !( p2p_data && (p2p_data->connection_type == YAHOO_P2P_WE_ARE_SERVER) ) ) {
- purple_xfer_cancel_remote(xfer);
- return;
- }
- if(!purple_network_listen_range(0, 0, SOCK_STREAM, yahoo_p2p_ft_server_listen_cb, xfer)) {
- purple_xfer_cancel_remote(xfer);
- return;
- }
+ req = yahoo_ft_new_req(xfer_data);
+ purple_http_request_set_method(req, "HEAD");
+ xfer_data->hc = purple_http_request(gc, req, yahoo_process_filetrans_info_15_got, xfer);
+ purple_http_request_unref(req);
}
+ else if (val_249 == 2)
+ purple_debug_error("yahoo", "p2p file transfers are not supported yet\n");
}
/* TODO: Check filename etc. No probs till some hacker comes in the way */
@@ -1972,12 +1074,11 @@ void yahoo_process_filetrans_acc_15(PurpleConnection *gc, struct yahoo_packet *p
YahooData *yd;
struct yahoo_xfer_data *xfer_data;
GSList *l;
- PurpleAccount *account;
long val_66 = 0;
gchar *url = NULL;
int val_249 = 0;
- yd = gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
for (l = pkt->hash; l; l = l->next) {
struct yahoo_pair *pair = l->data;
@@ -2031,18 +1132,34 @@ void yahoo_process_filetrans_acc_15(PurpleConnection *gc, struct yahoo_packet *p
return;
}
- xfer_data = xfer->data;
- if(url)
- purple_url_parse(url, &(xfer_data->host), &(xfer_data->port), &(xfer_data->path), NULL, NULL);
+ xfer_data = purple_xfer_get_protocol_data(xfer);
+ if (url)
+ xfer_data->host = g_strdup(url);
xfer_data->xfer_idstring_for_relay = g_strdup(xfer_idstring_for_relay);
- xfer_data->status_15 = ACCEPTED;
- account = purple_connection_get_account(gc);
- if (purple_proxy_connect(gc, account, xfer_data->host, xfer_data->port,
- yahoo_xfer_connected_15, xfer) == NULL)
- {
- purple_notify_error(gc, NULL, _("File Transfer Failed"),_("Unable to connect"));
- purple_xfer_cancel_remote(xfer);
- }
+ xfer_data->url = yahoo_ft_url_gen(xfer, xfer_data->host);
+ purple_xfer_start(xfer, -1, NULL, 0);
+}
+
+static PurpleXfer *
+yahoo_ft_new_xfer_struct(PurpleConnection *gc, PurpleXferType type, const char *who)
+{
+ PurpleXfer *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), type, who);
+
+ g_return_val_if_fail(xfer != NULL, NULL);
+
+ purple_xfer_set_init_fnc(xfer, yahoo_xfer_init_15);
+ purple_xfer_set_start_fnc(xfer, yahoo_xfer_start);
+ purple_xfer_set_end_fnc(xfer, yahoo_xfer_end);
+ purple_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send);
+ purple_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv);
+ purple_xfer_set_request_denied_fnc(xfer, yahoo_xfer_cancel_recv);
+
+ return xfer;
}
diff --git a/libpurple/protocols/yahoo/yahoo_filexfer.h b/libpurple/protocols/yahoo/yahoo_filexfer.h
index c31b029245..f0a14addb2 100644
--- a/libpurple/protocols/yahoo/yahoo_filexfer.h
+++ b/libpurple/protocols/yahoo/yahoo_filexfer.h
@@ -22,7 +22,7 @@
#ifndef _YAHOO_FILEXFER_H_
#define _YAHOO_FILEXFER_H_
-#include "ft.h"
+#include "xfer.h"
/**
* Process ymsg events, particular IMViroments like Doodle
@@ -30,11 +30,6 @@
void yahoo_process_p2pfilexfer( PurpleConnection *gc, struct yahoo_packet *pkt );
/**
- * Process ymsg file receive invites.
- */
-void yahoo_process_filetransfer(PurpleConnection *gc, struct yahoo_packet *pkt);
-
-/**
* Create a new PurpleXfer
*
* @param gc The PurpleConnection handle.
diff --git a/libpurple/protocols/yahoo/yahoo_friend.c b/libpurple/protocols/yahoo/yahoo_friend.c
index ff98fcca87..bb91a72b28 100644
--- a/libpurple/protocols/yahoo/yahoo_friend.c
+++ b/libpurple/protocols/yahoo/yahoo_friend.c
@@ -46,9 +46,10 @@ YahooFriend *yahoo_friend_find(PurpleConnection *gc, const char *name)
const char *norm;
g_return_val_if_fail(gc != NULL, NULL);
- g_return_val_if_fail(gc->proto_data != NULL, NULL);
- yd = gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
+ g_return_val_if_fail(yd != NULL, NULL);
+
norm = purple_normalize(purple_connection_get_account(gc), name);
return g_hash_table_lookup(yd->friends, norm);
@@ -61,9 +62,10 @@ YahooFriend *yahoo_friend_find_or_new(PurpleConnection *gc, const char *name)
const char *norm;
g_return_val_if_fail(gc != NULL, NULL);
- g_return_val_if_fail(gc->proto_data != NULL, NULL);
- yd = gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
+ g_return_val_if_fail(yd != NULL, NULL);
+
norm = purple_normalize(purple_connection_get_account(gc), name);
f = g_hash_table_lookup(yd->friends, norm);
@@ -230,7 +232,7 @@ void yahoo_process_presence(PurpleConnection *gc, struct yahoo_packet *pkt)
void yahoo_friend_update_presence(PurpleConnection *gc, const char *name,
YahooPresenceVisibility presence)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_packet *pkt = NULL;
YahooFriend *f;
const char *thirtyone, *thirteen;
diff --git a/libpurple/protocols/yahoo/yahoo_packet.c b/libpurple/protocols/yahoo/yahoo_packet.c
index f029575782..0e08b69de6 100644
--- a/libpurple/protocols/yahoo/yahoo_packet.c
+++ b/libpurple/protocols/yahoo/yahoo_packet.c
@@ -139,7 +139,7 @@ void yahoo_packet_read(struct yahoo_packet *pkt, const guchar *data, int len)
char key[64];
const guchar *delimiter;
gboolean accept;
- int x;
+ guint x;
struct yahoo_pair *pair;
while (pos + 1 < len)
@@ -287,8 +287,9 @@ yahoo_packet_send_can_write(gpointer data, gint source, PurpleInputCondition con
{
YahooData *yd = data;
int ret, writelen;
+ const gchar *output = NULL;
- writelen = purple_circ_buffer_get_max_read(yd->txbuf);
+ writelen = purple_circular_buffer_get_max_read(yd->txbuf);
if (writelen == 0) {
purple_input_remove(yd->txhandler);
@@ -296,18 +297,20 @@ yahoo_packet_send_can_write(gpointer data, gint source, PurpleInputCondition con
return;
}
- ret = write(yd->fd, yd->txbuf->outptr, writelen);
+ output = purple_circular_buffer_get_output(yd->txbuf);
+
+ ret = write(yd->fd, output, writelen);
if (ret < 0 && errno == EAGAIN)
return;
else if (ret < 0) {
/* TODO: what to do here - do we really have to disconnect? */
- purple_connection_error_reason(yd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error(yd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Write Error"));
return;
}
- purple_circ_buffer_mark_read(yd->txbuf, ret);
+ purple_circular_buffer_mark_read(yd->txbuf, ret);
}
@@ -370,11 +373,11 @@ int yahoo_packet_send(struct yahoo_packet *pkt, YahooData *yd)
return ret;
}
- if (ret < len) {
+ if ((gsize)ret < len) {
if (yd->txhandler == 0)
yd->txhandler = purple_input_add(yd->fd, PURPLE_INPUT_WRITE,
yahoo_packet_send_can_write, yd);
- purple_circ_buffer_append(yd->txbuf, data + ret, len - ret);
+ purple_circular_buffer_append(yd->txbuf, data + ret, len - ret);
}
g_free(data);
diff --git a/libpurple/protocols/yahoo/yahoo_packet.h b/libpurple/protocols/yahoo/yahoo_packet.h
index f214351b26..6fdbd3438b 100644
--- a/libpurple/protocols/yahoo/yahoo_packet.h
+++ b/libpurple/protocols/yahoo/yahoo_packet.h
@@ -121,7 +121,7 @@ struct yahoo_pair {
struct yahoo_packet {
guint16 service;
- guint32 status;
+ gint32 status;
guint32 id;
GSList *hash;
};
diff --git a/libpurple/protocols/yahoo/yahoo_picture.c b/libpurple/protocols/yahoo/yahoo_picture.c
index 7f9fa04300..a6440e3bf8 100644
--- a/libpurple/protocols/yahoo/yahoo_picture.c
+++ b/libpurple/protocols/yahoo/yahoo_picture.c
@@ -25,9 +25,9 @@
#include "account.h"
#include "accountopt.h"
-#include "blist.h"
+#include "buddylist.h"
#include "debug.h"
-#include "privacy.h"
+#include "http.h"
#include "prpl.h"
#include "proxy.h"
#include "util.h"
@@ -45,23 +45,24 @@ struct yahoo_fetch_picture_data {
};
static void
-yahoo_fetch_picture_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
- const gchar *pic_data, size_t len, const gchar *error_message)
+yahoo_fetch_picture_cb(PurpleHttpConnection *http_conn, PurpleHttpResponse *response,
+ gpointer _data)
{
- struct yahoo_fetch_picture_data *d;
- YahooData *yd;
-
- d = user_data;
- yd = d->gc->proto_data;
- yd->url_datas = g_slist_remove(yd->url_datas, url_data);
+ struct yahoo_fetch_picture_data *d = _data;
- if (error_message != NULL) {
- purple_debug_error("yahoo", "Fetching buddy icon failed: %s\n", error_message);
- } else if (len == 0) {
- purple_debug_error("yahoo", "Fetched an icon with length 0. Strange.\n");
+ if (!purple_http_response_is_successful(response)) {
+ purple_debug_error("yahoo", "Fetching buddy icon failed: %s\n",
+ purple_http_response_get_error(response));
} else {
char *checksum = g_strdup_printf("%i", d->checksum);
- purple_buddy_icons_set_for_user(purple_connection_get_account(d->gc), d->who, g_memdup(pic_data, len), len, checksum);
+ const gchar *pic_data;
+ size_t pic_len;
+
+ pic_data = purple_http_response_get_data(response, &pic_len);
+
+ purple_buddy_icons_set_for_user(
+ purple_connection_get_account(d->gc), d->who,
+ g_memdup(pic_data, pic_len), pic_len, checksum);
g_free(checksum);
}
@@ -122,7 +123,7 @@ void yahoo_process_picture(PurpleConnection *gc, struct yahoo_packet *pkt)
if (!who)
return;
- if (!purple_privacy_check(purple_connection_get_account(gc), who)) {
+ if (!purple_account_privacy_check(purple_connection_get_account(gc), who)) {
purple_debug_info("yahoo", "Picture packet from %s dropped.\n", who);
return;
}
@@ -130,23 +131,15 @@ void yahoo_process_picture(PurpleConnection *gc, struct yahoo_packet *pkt)
/* Yahoo IM 6 spits out 0.png as the URL if the buddy icon is not set */
if (who && got_icon_info && url && !g_ascii_strncasecmp(url, "http://", 7)) {
/* TODO: make this work p2p, try p2p before the url */
- PurpleUtilFetchUrlData *url_data;
struct yahoo_fetch_picture_data *data;
- /* use whole URL if using HTTP Proxy */
- gboolean use_whole_url = yahoo_account_use_http_proxy(gc);
data = g_new0(struct yahoo_fetch_picture_data, 1);
data->gc = gc;
data->who = g_strdup(who);
data->checksum = checksum;
- /* TODO: Does this need to be MSIE 5.0? */
- url_data = purple_util_fetch_url(url, use_whole_url,
- "Mozilla/4.0 (compatible; MSIE 5.5)", FALSE,
- yahoo_fetch_picture_cb, data);
- if (url_data != NULL) {
- yd = gc->proto_data;
- yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
- }
+ yd = purple_connection_get_protocol_data(gc);
+ purple_http_connection_set_add(yd->http_reqs, purple_http_get(
+ gc, yahoo_fetch_picture_cb, data, url));
} else if (who && send_icon_info) {
yahoo_send_picture_info(gc, who);
}
@@ -181,7 +174,7 @@ void yahoo_process_picture_checksum(PurpleConnection *gc, struct yahoo_packet *p
}
if (who) {
- PurpleBuddy *b = purple_find_buddy(gc->account, who);
+ PurpleBuddy *b = purple_blist_find_buddy(purple_connection_get_account(gc), who);
const char *locksum = NULL;
/* FIXME: Cleanup this strtol() stuff if possible. */
@@ -196,7 +189,7 @@ void yahoo_process_picture_checksum(PurpleConnection *gc, struct yahoo_packet *p
void yahoo_process_picture_upload(PurpleConnection *gc, struct yahoo_packet *pkt)
{
PurpleAccount *account = purple_connection_get_account(gc);
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
GSList *l = pkt->hash;
char *url = NULL;
@@ -272,7 +265,7 @@ void yahoo_process_avatar_update(PurpleConnection *gc, struct yahoo_packet *pkt)
yahoo_send_picture_request(gc, who);
else if ((avatar == 0) || (avatar == 1)) {
YahooFriend *f;
- purple_buddy_icons_set_for_user(gc->account, who, NULL, 0, NULL);
+ purple_buddy_icons_set_for_user(purple_connection_get_account(gc), who, NULL, 0, NULL);
if ((f = yahoo_friend_find(gc, who)))
yahoo_friend_set_buddy_icon_need_request(f, TRUE);
purple_debug_misc("yahoo", "Setting user %s's icon to NULL.\n", who);
@@ -282,7 +275,7 @@ void yahoo_process_avatar_update(PurpleConnection *gc, struct yahoo_packet *pkt)
void yahoo_send_picture_info(PurpleConnection *gc, const char *who)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_packet *pkt;
if (!yd->picture_url) {
@@ -299,7 +292,7 @@ void yahoo_send_picture_info(PurpleConnection *gc, const char *who)
void yahoo_send_picture_request(PurpleConnection *gc, const char *who)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_packet *pkt;
pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YAHOO_STATUS_AVAILABLE, yd->session_id);
@@ -311,7 +304,7 @@ void yahoo_send_picture_request(PurpleConnection *gc, const char *who)
void yahoo_send_picture_checksum(PurpleConnection *gc)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_packet *pkt;
pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_CHECKSUM, YAHOO_STATUS_AVAILABLE, yd->session_id);
@@ -322,7 +315,7 @@ void yahoo_send_picture_checksum(PurpleConnection *gc)
void yahoo_send_picture_update_to_user(PurpleConnection *gc, const char *who, int type)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_packet *pkt;
pkt = yahoo_packet_new(YAHOO_SERVICE_AVATAR_UPDATE, YAHOO_STATUS_AVAILABLE, yd->session_id);
@@ -347,7 +340,7 @@ static void yahoo_send_picture_update_foreach(gpointer key, gpointer value, gpoi
void yahoo_send_picture_update(PurpleConnection *gc, int type)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yspufe data;
data.gc = gc;
@@ -360,176 +353,99 @@ void yahoo_buddy_icon_upload_data_free(struct yahoo_buddy_icon_upload_data *d)
{
purple_debug_misc("yahoo", "In yahoo_buddy_icon_upload_data_free()\n");
- if (d->str)
- g_string_free(d->str, TRUE);
+ if (d->picture_data)
+ g_string_free(d->picture_data, TRUE);
g_free(d->filename);
- if (d->watcher)
- purple_input_remove(d->watcher);
- if (d->fd != -1)
- close(d->fd);
g_free(d);
}
-/* we couldn't care less about the server's response, but yahoo gets grumpy if we close before it sends it */
-static void yahoo_buddy_icon_upload_reading(gpointer data, gint source, PurpleInputCondition condition)
-{
- struct yahoo_buddy_icon_upload_data *d = data;
- PurpleConnection *gc = d->gc;
- char buf[1024];
- int ret;
-
- if (!PURPLE_CONNECTION_IS_VALID(gc)) {
- yahoo_buddy_icon_upload_data_free(d);
- return;
- }
-
- ret = read(d->fd, buf, sizeof(buf));
-
- if (ret < 0 && errno == EAGAIN)
- return;
- else if (ret <= 0) {
- /* There are other problems if d->str->len overflows, so shut up the
- * warning on 64-bit. */
- purple_debug_info("yahoo", "Buddy icon upload response (%" G_GSIZE_FORMAT ") bytes (> ~400 indicates failure):\n%.*s\n",
- d->str->len, (guint)d->str->len, d->str->str);
-
- yahoo_buddy_icon_upload_data_free(d);
- return;
- }
-
- g_string_append_len(d->str, buf, ret);
-}
-
-static void yahoo_buddy_icon_upload_pending(gpointer data, gint source, PurpleInputCondition condition)
+static void
+yahoo_buddy_icon_upload_done(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _d)
{
- struct yahoo_buddy_icon_upload_data *d = data;
+ struct yahoo_buddy_icon_upload_data *d = _d;
PurpleConnection *gc = d->gc;
- gssize wrote;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
- if (!PURPLE_CONNECTION_IS_VALID(gc)) {
- yahoo_buddy_icon_upload_data_free(d);
- return;
- }
+ yd->picture_upload_hc = NULL;
- wrote = write(d->fd, d->str->str + d->pos, d->str->len - d->pos);
- if (wrote < 0 && errno == EAGAIN)
- return;
- if (wrote <= 0) {
+ if (!purple_http_response_is_successful(response))
purple_debug_info("yahoo", "Error uploading buddy icon.\n");
- yahoo_buddy_icon_upload_data_free(d);
- return;
- }
- d->pos += wrote;
- if (d->pos >= d->str->len) {
+ else
purple_debug_misc("yahoo", "Finished uploading buddy icon.\n");
- purple_input_remove(d->watcher);
- /* Clean out the sent buffer and reuse it to read the result */
- g_string_free(d->str, TRUE);
- d->str = g_string_new("");
- d->watcher = purple_input_add(d->fd, PURPLE_INPUT_READ, yahoo_buddy_icon_upload_reading, d);
- }
+
+ yahoo_buddy_icon_upload_data_free(d);
}
-static void yahoo_buddy_icon_upload_connected(gpointer data, gint source, const gchar *error_message)
+static void
+yahoo_buddy_icon_build_packet(struct yahoo_buddy_icon_upload_data *d)
{
- struct yahoo_buddy_icon_upload_data *d = data;
struct yahoo_packet *pkt;
- gchar *tmp, *header;
+ PurpleConnection *gc = d->gc;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
+ gchar *len_str;
guchar *pkt_buf;
- const char *host;
- int port;
gsize pkt_buf_len;
- PurpleConnection *gc = d->gc;
- PurpleAccount *account;
- YahooData *yd;
- /* use whole URL if using HTTP Proxy */
- gboolean use_whole_url = yahoo_account_use_http_proxy(gc);
-
- account = purple_connection_get_account(gc);
- yd = gc->proto_data;
-
- /* Buddy icon connect is now complete; clear the PurpleProxyConnectData */
- yd->buddy_icon_connect_data = NULL;
-
- if (source < 0) {
- purple_debug_error("yahoo", "Buddy icon upload failed: %s\n", error_message);
- yahoo_buddy_icon_upload_data_free(d);
- return;
- }
- pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_UPLOAD, YAHOO_STATUS_AVAILABLE, yd->session_id);
+ pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_UPLOAD,
+ YAHOO_STATUS_AVAILABLE, yd->session_id);
- tmp = g_strdup_printf("%" G_GSIZE_FORMAT, d->str->len);
- /* 1 = me, 38 = expire time(?), 0 = me, 28 = size, 27 = filename, 14 = NULL, 29 = data */
+ len_str = g_strdup_printf("%" G_GSIZE_FORMAT, d->picture_data->len);
+ /* 1 = me, 38 = expire time(?), 0 = me, 28 = size, 27 = filename,
+ * 14 = NULL, 29 = data
+ */
yahoo_packet_hash_str(pkt, 1, purple_connection_get_display_name(gc));
yahoo_packet_hash_str(pkt, 38, "604800"); /* time til expire */
- purple_account_set_int(account, YAHOO_PICEXPIRE_SETTING, time(NULL) + 604800);
+ purple_account_set_int(purple_connection_get_account(gc),
+ YAHOO_PICEXPIRE_SETTING, time(NULL) + 604800);
yahoo_packet_hash_str(pkt, 0, purple_connection_get_display_name(gc));
- yahoo_packet_hash_str(pkt, 28, tmp);
- g_free(tmp);
+ yahoo_packet_hash_str(pkt, 28, len_str);
+ g_free(len_str);
yahoo_packet_hash_str(pkt, 27, d->filename);
yahoo_packet_hash_str(pkt, 14, "");
/* 4 padding for the 29 key name */
pkt_buf_len = yahoo_packet_build(pkt, 4, FALSE, yd->jp, &pkt_buf);
yahoo_packet_free(pkt);
- /* header + packet + "29" + 0xc0 + 0x80) + pictureblob */
-
- host = purple_account_get_string(account, "xfer_host", yd->jp? YAHOOJP_XFER_HOST : YAHOO_XFER_HOST);
- port = purple_account_get_int(account, "xfer_port", YAHOO_XFER_PORT);
- tmp = g_strdup_printf("%s:%d", host, port);
- header = g_strdup_printf("POST %s%s/notifyft HTTP/1.1\r\n"
- "User-Agent: " YAHOO_CLIENT_USERAGENT "\r\n"
- "Cookie: T=%s; Y=%s\r\n"
- "Host: %s\r\n"
- "Content-Length: %" G_GSIZE_FORMAT "\r\n"
- "Cache-Control: no-cache\r\n\r\n",
- use_whole_url ? "http://" : "", use_whole_url ? tmp : "",
- yd->cookie_t, yd->cookie_y,
- tmp,
- pkt_buf_len + 4 + d->str->len);
- g_free(tmp);
-
/* There's no magic here, we just need to prepend in reverse order */
- g_string_prepend(d->str, "29\xc0\x80");
+ g_string_prepend(d->picture_data, "29\xc0\x80");
- g_string_prepend_len(d->str, (char *)pkt_buf, pkt_buf_len);
+ g_string_prepend_len(d->picture_data, (char *)pkt_buf, pkt_buf_len);
g_free(pkt_buf);
-
- g_string_prepend(d->str, header);
- g_free(header);
-
- /* There are other problems if we're uploading over 4GB of data */
- purple_debug_info("yahoo", "Buddy icon upload data:\n%.*s\n", (guint)d->str->len, d->str->str);
-
- d->fd = source;
- d->watcher = purple_input_add(d->fd, PURPLE_INPUT_WRITE, yahoo_buddy_icon_upload_pending, d);
-
- yahoo_buddy_icon_upload_pending(d, d->fd, PURPLE_INPUT_WRITE);
}
-void yahoo_buddy_icon_upload(PurpleConnection *gc, struct yahoo_buddy_icon_upload_data *d)
+void
+yahoo_buddy_icon_upload(PurpleConnection *gc,
+ struct yahoo_buddy_icon_upload_data *d)
{
+ PurpleHttpRequest *req;
+ PurpleHttpCookieJar *cjar;
PurpleAccount *account = purple_connection_get_account(gc);
- YahooData *yd = gc->proto_data;
-
- if (yd->buddy_icon_connect_data != NULL) {
- /* Cancel any in-progress buddy icon upload */
- purple_proxy_connect_cancel(yd->buddy_icon_connect_data);
- yd->buddy_icon_connect_data = NULL;
- }
-
- yd->buddy_icon_connect_data = purple_proxy_connect(NULL, account,
- purple_account_get_string(account, "xfer_host",
- yd->jp? YAHOOJP_XFER_HOST : YAHOO_XFER_HOST),
- purple_account_get_int(account, "xfer_port", YAHOO_XFER_PORT),
- yahoo_buddy_icon_upload_connected, d);
-
- if (yd->buddy_icon_connect_data == NULL)
- {
- purple_debug_error("yahoo", "Uploading our buddy icon failed to connect.\n");
- yahoo_buddy_icon_upload_data_free(d);
- }
+ YahooData *yd = purple_connection_get_protocol_data(gc);
+
+ /* Cancel any in-progress buddy icon upload */
+ purple_http_conn_cancel(yd->picture_upload_hc);
+
+ req = purple_http_request_new(NULL);
+ purple_http_request_set_url_printf(req, "http://%s/notifyft",
+ purple_account_get_string(account, "xfer_host", yd->jp ?
+ YAHOOJP_XFER_HOST : YAHOO_XFER_HOST));
+ purple_http_request_set_method(req, "POST");
+ cjar = purple_http_request_get_cookie_jar(req);
+ purple_http_cookie_jar_set(cjar, "T", yd->cookie_t);
+ purple_http_cookie_jar_set(cjar, "Y", yd->cookie_y);
+ purple_http_request_header_set(req, "Cache-Control", "no-cache");
+ purple_http_request_header_set(req, "User-Agent",
+ YAHOO_CLIENT_USERAGENT);
+
+ yahoo_buddy_icon_build_packet(d);
+ purple_http_request_set_contents(req, d->picture_data->str,
+ d->picture_data->len);
+ g_string_free(d->picture_data, TRUE);
+ d->picture_data = NULL;
+
+ yd->picture_upload_hc = purple_http_request(gc, req,
+ yahoo_buddy_icon_upload_done, d);
}
static int yahoo_buddy_icon_calculate_checksum(const guchar *data, gsize len)
@@ -554,10 +470,10 @@ static int yahoo_buddy_icon_calculate_checksum(const guchar *data, gsize len)
return checksum;
}
-void yahoo_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
+void yahoo_set_buddy_icon(PurpleConnection *gc, PurpleImage *img)
{
- YahooData *yd = gc->proto_data;
- PurpleAccount *account = gc->account;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
+ PurpleAccount *account = purple_connection_get_account(gc);
if (img == NULL) {
g_free(yd->picture_url);
@@ -573,8 +489,8 @@ void yahoo_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
yahoo_send_picture_update(gc, 0);
} else {
- gconstpointer data = purple_imgstore_get_data(img);
- size_t len = purple_imgstore_get_size(img);
+ gconstpointer data = purple_image_get_data(img);
+ size_t len = purple_image_get_size(img);
GString *s = g_string_new_len(data, len);
struct yahoo_buddy_icon_upload_data *d;
int oldcksum = purple_account_get_int(account, YAHOO_PICCKSUM_SETTING, 0);
@@ -596,9 +512,8 @@ void yahoo_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
/* We use this solely for sending a filename to the server */
d = g_new0(struct yahoo_buddy_icon_upload_data, 1);
d->gc = gc;
- d->str = s;
- d->fd = -1;
- d->filename = g_strdup(purple_imgstore_get_filename(img));
+ d->picture_data = s;
+ d->filename = g_strdup(purple_image_get_friendly_filename(img));
if (!yd->logged_in) {
yd->picture_upload_todo = d;
diff --git a/libpurple/protocols/yahoo/yahoo_picture.h b/libpurple/protocols/yahoo/yahoo_picture.h
index cf030f12f3..ea4c3726eb 100644
--- a/libpurple/protocols/yahoo/yahoo_picture.h
+++ b/libpurple/protocols/yahoo/yahoo_picture.h
@@ -36,7 +36,7 @@ void yahoo_process_picture_upload(PurpleConnection *gc, struct yahoo_packet *pkt
void yahoo_process_avatar_update(PurpleConnection *gc, struct yahoo_packet *pkt);
-void yahoo_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img);
+void yahoo_set_buddy_icon(PurpleConnection *gc, PurpleImage *img);
void yahoo_buddy_icon_upload(PurpleConnection *gc, struct yahoo_buddy_icon_upload_data *d);
void yahoo_buddy_icon_upload_data_free(struct yahoo_buddy_icon_upload_data *d);
diff --git a/libpurple/protocols/yahoo/yahoo_profile.c b/libpurple/protocols/yahoo/yahoo_profile.c
index b81b47d89f..b91a447066 100644
--- a/libpurple/protocols/yahoo/yahoo_profile.c
+++ b/libpurple/protocols/yahoo/yahoo_profile.c
@@ -25,10 +25,11 @@
#include "internal.h"
#include "debug.h"
+#include "http.h"
#include "notify.h"
#include "util.h"
#if PHOTO_SUPPORT
-#include "imgstore.h"
+#include "image-store.h"
#endif /* PHOTO_SUPPORT */
#include "libymsg.h"
@@ -695,11 +696,11 @@ static void yahoo_extract_user_info_text(PurpleNotifyUserInfo *user_info, YahooG
PurpleBuddy *b;
YahooFriend *f;
- b = purple_find_buddy(purple_connection_get_account(info_data->gc),
+ b = purple_blist_find_buddy(purple_connection_get_account(info_data->gc),
info_data->name);
if (b) {
- const char *balias = purple_buddy_get_local_buddy_alias(b);
+ const char *balias = purple_buddy_get_local_alias(b);
if(balias && balias[0]) {
purple_notify_user_info_add_pair_plaintext(user_info, _("Alias"), balias);
}
@@ -756,15 +757,16 @@ static char *yahoo_get_photo_url(const char *url_text, const char *name) {
}
static void
-yahoo_got_photo(PurpleUtilFetchUrlData *url_data, gpointer data,
- const gchar *url_text, size_t len, const gchar *error_message);
+yahoo_got_photo(PurpleHttpConnection *http_conn, PurpleHttpResponse *response,
+ gpointer _info2_data);
#endif /* PHOTO_SUPPORT */
-static void yahoo_got_info(PurpleUtilFetchUrlData *url_data, gpointer user_data,
- const gchar *url_text, size_t len, const gchar *error_message)
+static void
+yahoo_got_info(PurpleHttpConnection *http_conn, PurpleHttpResponse *response,
+ gpointer _info_data)
{
- YahooGetInfoData *info_data = (YahooGetInfoData *)user_data;
+ YahooGetInfoData *info_data = _info_data;
PurpleNotifyUserInfo *user_info;
char *p;
#if PHOTO_SUPPORT
@@ -786,11 +788,11 @@ static void yahoo_got_info(PurpleUtilFetchUrlData *url_data, gpointer user_data,
const profile_strings_node_t *strings = NULL;
const char *title;
profile_state_t profile_state = PROFILE_STATE_DEFAULT;
+ const gchar *url_text;
purple_debug_info("yahoo", "In yahoo_got_info\n");
- yd = info_data->gc->proto_data;
- yd->url_datas = g_slist_remove(yd->url_datas, url_data);
+ yd = purple_connection_get_protocol_data(info_data->gc);
user_info = purple_notify_user_info_new();
@@ -804,8 +806,8 @@ static void yahoo_got_info(PurpleUtilFetchUrlData *url_data, gpointer user_data,
* happen except under unusual error conditions, as Yahoo is observed
* to send back HTML, with a 200 status code.
*/
- if (error_message != NULL || url_text == NULL || strcmp(url_text, "") == 0) {
- purple_notify_user_info_add_pair(user_info, _("Error retrieving profile"), NULL);
+ if (!purple_http_response_is_successful(response)) {
+ purple_notify_user_info_add_pair_html(user_info, _("Error retrieving profile"), NULL);
purple_notify_userinfo(info_data->gc, info_data->name,
user_info, NULL, NULL);
purple_notify_user_info_destroy(user_info);
@@ -815,6 +817,8 @@ static void yahoo_got_info(PurpleUtilFetchUrlData *url_data, gpointer user_data,
return;
}
+ url_text = purple_http_response_get_data(response, NULL);
+
/* Construct the correct profile URL */
s = g_string_sized_new(80); /* wild guess */
g_string_printf(s, "%s%s", (yd->jp? YAHOOJP_PROFILE_URL: YAHOO_PROFILE_URL),
@@ -840,7 +844,7 @@ static void yahoo_got_info(PurpleUtilFetchUrlData *url_data, gpointer user_data,
_("If you wish to view this profile, "
"you will need to visit this link in your web browser:"),
profile_url_text, profile_url_text);
- purple_notify_user_info_add_pair(user_info, NULL, tmp);
+ purple_notify_user_info_add_pair_html(user_info, NULL, tmp);
g_free(tmp);
purple_notify_userinfo(info_data->gc, info_data->name,
@@ -931,32 +935,22 @@ static void yahoo_got_info(PurpleUtilFetchUrlData *url_data, gpointer user_data,
/* Try to put the photo in there too, if there's one */
if (photo_url_text) {
- PurpleUtilFetchUrlData *url_data;
- /* use whole URL if using HTTP Proxy */
- gboolean use_whole_url = yahoo_account_use_http_proxy(info_data->gc);
-
- /* User-uploaded photos use a different server that requires the Host
- * header, but Yahoo Japan will use the "chunked" content encoding if
- * we specify HTTP 1.1. So we have to specify 1.0 & fix purple_util_fetch_url
- */
- url_data = purple_util_fetch_url(photo_url_text, use_whole_url, NULL,
- FALSE, yahoo_got_photo, info2_data);
- if (url_data != NULL)
- yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
+ purple_http_connection_set_add(yd->http_reqs, purple_http_get(
+ info_data->gc, yahoo_got_photo, info2_data,
+ photo_url_text));
} else {
/* Emulate a callback */
- yahoo_got_photo(NULL, info2_data, NULL, 0, NULL);
+ yahoo_got_photo(NULL, NULL, info2_data);
}
}
static void
-yahoo_got_photo(PurpleUtilFetchUrlData *url_data, gpointer data,
- const gchar *url_text, size_t len, const gchar *error_message)
+yahoo_got_photo(PurpleHttpConnection *http_conn, PurpleHttpResponse *response,
+ gpointer _info2_data)
{
- YahooGetInfoStepTwoData *info2_data = (YahooGetInfoStepTwoData *)data;
+ YahooGetInfoStepTwoData *info2_data = _info2_data;
YahooData *yd;
gboolean found = FALSE;
- int id = -1;
/* Temporary variables */
char *p = NULL;
@@ -983,8 +977,9 @@ yahoo_got_photo(PurpleUtilFetchUrlData *url_data, gpointer data,
/* in to purple_markup_strip_html*/
char *fudged_buffer;
- yd = info_data->gc->proto_data;
- yd->url_datas = g_slist_remove(yd->url_datas, url_data);
+ g_return_if_fail(strings != NULL);
+
+ yd = purple_connection_get_protocol_data(info_data->gc);
fudged_buffer = purple_strcasereplace(url_buffer, "</dd>", "</dd><br>");
/* nuke the html, it's easier than trying to parse the horrid stuff */
@@ -995,7 +990,7 @@ yahoo_got_photo(PurpleUtilFetchUrlData *url_data, gpointer data,
purple_debug_misc("yahoo", "url_buffer = %p\n", url_buffer);
/* convert to utf8 */
- if (strings && strings->charset) {
+ if (strings->charset) {
p = g_convert(stripped, -1, "utf-8", strings->charset,
NULL, NULL, NULL);
if (!p) {
@@ -1015,7 +1010,7 @@ yahoo_got_photo(PurpleUtilFetchUrlData *url_data, gpointer data,
p = NULL;
/* "Last updated" should also be converted to utf8 and with &nbsp; killed */
- if (strings && strings->charset) {
+ if (strings->charset) {
last_updated_utf8_string = g_convert(last_updated_string, -1, "utf-8",
strings->charset, NULL, NULL, NULL);
yahoo_remove_nonbreaking_spaces(last_updated_utf8_string);
@@ -1023,7 +1018,7 @@ yahoo_got_photo(PurpleUtilFetchUrlData *url_data, gpointer data,
purple_debug_misc("yahoo", "after utf8 conversion: stripped = (%s)\n", stripped);
}
- if (strings && profile_state == PROFILE_STATE_DEFAULT) {
+ if (profile_state == PROFILE_STATE_DEFAULT) {
#if 0
/* extract their Yahoo! ID and put it in. Don't bother marking has_info as
* true, since the Yahoo! ID will always be there */
@@ -1035,22 +1030,24 @@ yahoo_got_photo(PurpleUtilFetchUrlData *url_data, gpointer data,
#if PHOTO_SUPPORT
/* Try to put the photo in there too, if there's one and is readable */
- if (url_text && len != 0) {
- if (strstr(url_text, "400 Bad Request")
- || strstr(url_text, "403 Forbidden")
- || strstr(url_text, "404 Not Found")) {
-
- purple_debug_info("yahoo", "Error getting %s: %s\n",
- photo_url_text, url_text);
- } else {
- purple_debug_info("yahoo", "%s is %" G_GSIZE_FORMAT
- " bytes\n", photo_url_text, len);
- id = purple_imgstore_add_with_id(g_memdup(url_text, len), len, NULL);
-
- tmp = g_strdup_printf("<img id=\"%d\"><br>", id);
- purple_notify_user_info_add_pair(user_info, NULL, tmp);
- g_free(tmp);
- }
+ if (purple_http_response_is_successful(response)) {
+ PurpleImage *img;
+ guchar img_id;
+ const gchar *data;
+ size_t len;
+
+ data = purple_http_response_get_data(response, &len);
+
+ purple_debug_info("yahoo", "%s is %" G_GSIZE_FORMAT " bytes\n",
+ photo_url_text, len);
+ img = purple_image_new_from_data(g_memdup(data, len), len);
+ img_id = purple_image_store_add_temporary(img);
+ g_object_unref(img);
+
+ tmp = g_strdup_printf("<img src=\"" PURPLE_IMAGE_STORE_PROTOCOL
+ "%u\"><br>", img_id);
+ purple_notify_user_info_add_pair_html(user_info, NULL, tmp);
+ g_free(tmp);
}
#endif /* PHOTO_SUPPORT */
@@ -1195,7 +1192,7 @@ yahoo_got_photo(PurpleUtilFetchUrlData *url_data, gpointer data,
const gchar *str;
purple_notify_user_info_add_section_break(user_info);
- purple_notify_user_info_add_pair(user_info,
+ purple_notify_user_info_add_pair_html(user_info,
_("Error retrieving profile"), NULL);
if (profile_state == PROFILE_STATE_UNKNOWN_LANGUAGE) {
@@ -1203,7 +1200,7 @@ yahoo_got_photo(PurpleUtilFetchUrlData *url_data, gpointer data,
"or format that is not supported at this time.");
} else if (profile_state == PROFILE_STATE_NOT_FOUND) {
- PurpleBuddy *b = purple_find_buddy
+ PurpleBuddy *b = purple_blist_find_buddy
(purple_connection_get_account(info_data->gc),
info_data->name);
YahooFriend *f = NULL;
@@ -1228,14 +1225,14 @@ yahoo_got_photo(PurpleUtilFetchUrlData *url_data, gpointer data,
str = _("The user's profile is empty.");
}
- purple_notify_user_info_add_pair(user_info, NULL, str);
+ purple_notify_user_info_add_pair_plaintext(user_info, NULL, str);
}
/* put a link to the actual profile URL */
purple_notify_user_info_add_section_break(user_info);
tmp = g_strdup_printf("<a href=\"%s\">%s</a>",
profile_url_text, _("View web profile"));
- purple_notify_user_info_add_pair(user_info, NULL, tmp);
+ purple_notify_user_info_add_pair_html(user_info, NULL, tmp);
g_free(tmp);
g_free(stripped);
@@ -1255,28 +1252,19 @@ yahoo_got_photo(PurpleUtilFetchUrlData *url_data, gpointer data,
#if PHOTO_SUPPORT
g_free(photo_url_text);
g_free(info2_data);
- if (id != -1)
- purple_imgstore_unref_by_id(id);
#endif /* PHOTO_SUPPORT */
}
void yahoo_get_info(PurpleConnection *gc, const char *name)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
YahooGetInfoData *data;
- char *url;
- PurpleUtilFetchUrlData *url_data;
data = g_new0(YahooGetInfoData, 1);
data->gc = gc;
data->name = g_strdup(name);
- url = g_strdup_printf("%s%s",
- (yd->jp ? YAHOOJP_PROFILE_URL : YAHOO_PROFILE_URL), name);
-
- url_data = purple_util_fetch_url(url, TRUE, NULL, FALSE, yahoo_got_info, data);
- if (url_data != NULL)
- yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
-
- g_free(url);
+ purple_http_connection_set_add(yd->http_reqs, purple_http_get_printf(gc,
+ yahoo_got_info, data, "%s%s",
+ (yd->jp ? YAHOOJP_PROFILE_URL : YAHOO_PROFILE_URL), name));
}
diff --git a/libpurple/protocols/yahoo/yahoochat.c b/libpurple/protocols/yahoo/yahoochat.c
index 3783012982..6f502ed9cc 100644
--- a/libpurple/protocols/yahoo/yahoochat.c
+++ b/libpurple/protocols/yahoo/yahoochat.c
@@ -33,7 +33,7 @@
#endif /* HAVE_CONFIG_H */
#include "debug.h"
-#include "privacy.h"
+#include "http.h"
#include "prpl.h"
#include "conversation.h"
@@ -53,7 +53,7 @@ static void yahoo_chat_leave(PurpleConnection *gc, const char *room, const char
/* special function to log us on to the yahoo chat service */
static void yahoo_chat_online(PurpleConnection *gc)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_packet *pkt;
const char *rll;
@@ -77,35 +77,35 @@ static void yahoo_chat_online(PurpleConnection *gc)
}
/* this is slow, and different from the purple_* version in that it (hopefully) won't add a user twice */
-void yahoo_chat_add_users(PurpleConvChat *chat, GList *newusers)
+void yahoo_chat_add_users(PurpleChatConversation *chat, GList *newusers)
{
GList *i;
for (i = newusers; i; i = i->next) {
- if (purple_conv_chat_find_user(chat, i->data))
+ if (purple_chat_conversation_has_user(chat, i->data))
continue;
- purple_conv_chat_add_user(chat, i->data, NULL, PURPLE_CBFLAGS_NONE, TRUE);
+ purple_chat_conversation_add_user(chat, i->data, NULL, PURPLE_CHAT_USER_NONE, TRUE);
}
}
-void yahoo_chat_add_user(PurpleConvChat *chat, const char *user, const char *reason)
+void yahoo_chat_add_user(PurpleChatConversation *chat, const char *user, const char *reason)
{
- if (purple_conv_chat_find_user(chat, user))
+ if (purple_chat_conversation_has_user(chat, user))
return;
- purple_conv_chat_add_user(chat, user, reason, PURPLE_CBFLAGS_NONE, TRUE);
+ purple_chat_conversation_add_user(chat, user, reason, PURPLE_CHAT_USER_NONE, TRUE);
}
-static PurpleConversation *yahoo_find_conference(PurpleConnection *gc, const char *name)
+static PurpleChatConversation *yahoo_find_conference(PurpleConnection *gc, const char *name)
{
YahooData *yd;
GSList *l;
- yd = gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
for (l = yd->confs; l; l = l->next) {
- PurpleConversation *c = l->data;
- if (!purple_utf8_strcasecmp(purple_conversation_get_name(c), name))
+ PurpleChatConversation *c = l->data;
+ if (!purple_utf8_strcasecmp(purple_conversation_get_name(PURPLE_CONVERSATION(c)), name))
return c;
}
return NULL;
@@ -191,7 +191,7 @@ void yahoo_process_conference_invite(PurpleConnection *gc, struct yahoo_packet *
return;
}
- if (!purple_privacy_check(account, who) ||
+ if (!purple_account_privacy_check(account, who) ||
(purple_account_get_bool(account, "ignore_invites", FALSE)))
{
purple_debug_info("yahoo",
@@ -208,7 +208,7 @@ void yahoo_process_conference_invite(PurpleConnection *gc, struct yahoo_packet *
g_hash_table_replace(components, g_strdup("topic"), msg);
g_hash_table_replace(components, g_strdup("type"), g_strdup("Conference"));
g_hash_table_replace(components, g_strdup("members"), g_string_free(members, FALSE));
- serv_got_chat_invite(gc, room, who, msg, components);
+ purple_serv_got_chat_invite(gc, room, who, msg, components);
}
@@ -218,7 +218,7 @@ void yahoo_process_conference_decline(PurpleConnection *gc, struct yahoo_packet
char *room = NULL;
char *who = NULL;
char *msg = NULL;
- PurpleConversation *c = NULL;
+ PurpleChatConversation *c = NULL;
int utf8 = 0;
for (l = pkt->hash; l; l = l->next) {
@@ -246,7 +246,7 @@ void yahoo_process_conference_decline(PurpleConnection *gc, struct yahoo_packet
break;
}
}
- if (!purple_privacy_check(purple_connection_get_account(gc), who))
+ if (!purple_account_privacy_check(purple_connection_get_account(gc), who))
{
g_free(room);
g_free(msg);
@@ -261,13 +261,15 @@ void yahoo_process_conference_decline(PurpleConnection *gc, struct yahoo_packet
{
msg_tmp = yahoo_string_decode(gc, msg, utf8);
msg = yahoo_codes_to_html(msg_tmp);
- serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(c)), who, 0, msg, time(NULL));
+ purple_serv_got_chat_in(gc, purple_chat_conversation_get_id(c),
+ who, PURPLE_MESSAGE_RECV, msg, time(NULL));
g_free(msg_tmp);
g_free(msg);
}
tmp = g_strdup_printf(_("%s has declined to join."), who);
- purple_conversation_write(c, NULL, tmp, PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY, time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(c),
+ tmp, PURPLE_MESSAGE_NO_LINKIFY);
g_free(tmp);
}
@@ -281,7 +283,7 @@ void yahoo_process_conference_logon(PurpleConnection *gc, struct yahoo_packet *p
GSList *l;
char *room = NULL;
char *who = NULL;
- PurpleConversation *c;
+ PurpleChatConversation *c;
for (l = pkt->hash; l; l = l->next) {
struct yahoo_pair *pair = l->data;
@@ -306,8 +308,8 @@ void yahoo_process_conference_logon(PurpleConnection *gc, struct yahoo_packet *p
c = yahoo_find_conference(gc, room);
if (c)
{ /* Prevent duplicate users in the chat */
- if( !purple_conv_chat_find_user(PURPLE_CONV_CHAT(c), who) )
- yahoo_chat_add_user(PURPLE_CONV_CHAT(c), who, NULL);
+ if( !purple_chat_conversation_has_user(c, who) )
+ yahoo_chat_add_user(c, who, NULL);
}
g_free(room);
}
@@ -318,7 +320,7 @@ void yahoo_process_conference_logoff(PurpleConnection *gc, struct yahoo_packet *
GSList *l;
char *room = NULL;
char *who = NULL;
- PurpleConversation *c;
+ PurpleChatConversation *c;
for (l = pkt->hash; l; l = l->next) {
struct yahoo_pair *pair = l->data;
@@ -342,7 +344,7 @@ void yahoo_process_conference_logoff(PurpleConnection *gc, struct yahoo_packet *
if (who && room) {
c = yahoo_find_conference(gc, room);
if (c)
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(c), who, NULL);
+ purple_chat_conversation_remove_user(c, who, NULL);
g_free(room);
}
}
@@ -354,7 +356,7 @@ void yahoo_process_conference_message(PurpleConnection *gc, struct yahoo_packet
char *who = NULL;
char *msg = NULL;
int utf8 = 0;
- PurpleConversation *c;
+ PurpleChatConversation *c;
for (l = pkt->hash; l; l = l->next) {
struct yahoo_pair *pair = l->data;
@@ -392,7 +394,8 @@ void yahoo_process_conference_message(PurpleConnection *gc, struct yahoo_packet
msg2 = yahoo_string_decode(gc, msg, utf8);
msg = yahoo_codes_to_html(msg2);
- serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(c)), who, 0, msg, time(NULL));
+ purple_serv_got_chat_in(gc, purple_chat_conversation_get_id(c), who,
+ PURPLE_MESSAGE_RECV, msg, time(NULL));
g_free(msg);
g_free(msg2);
}
@@ -402,10 +405,9 @@ void yahoo_process_conference_message(PurpleConnection *gc, struct yahoo_packet
static void yahoo_chat_join(PurpleConnection *gc, const char *dn, const char *room, const char *topic, const char *id)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_packet *pkt;
char *room2;
- gboolean utf8 = TRUE;
if (yd->wm) {
g_return_if_fail(yd->ycht != NULL);
@@ -415,7 +417,7 @@ static void yahoo_chat_join(PurpleConnection *gc, const char *dn, const char *ro
/* apparently room names are always utf8, or else always not utf8,
* so we don't have to actually pass the flag in the packet. Or something. */
- room2 = yahoo_string_encode(gc, room, &utf8);
+ room2 = yahoo_string_encode(gc, room, TRUE);
pkt = yahoo_packet_new(YAHOO_SERVICE_CHATJOIN, YAHOO_STATUS_AVAILABLE, yd->session_id);
yahoo_packet_hash(pkt, "ssss",
@@ -430,7 +432,7 @@ static void yahoo_chat_join(PurpleConnection *gc, const char *dn, const char *ro
/* this is a confirmation of yahoo_chat_online(); */
void yahoo_process_chat_online(PurpleConnection *gc, struct yahoo_packet *pkt)
{
- YahooData *yd = (YahooData *) gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
if (pkt->status == 1) {
yd->chat_online = TRUE;
@@ -462,7 +464,7 @@ void yahoo_process_chat_online(PurpleConnection *gc, struct yahoo_packet *pkt)
/* this is basicly the opposite of chat_online */
void yahoo_process_chat_logout(PurpleConnection *gc, struct yahoo_packet *pkt)
{
- YahooData *yd = (YahooData *) gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
GSList *l;
for (l = pkt->hash; l; l = l->next) {
@@ -492,8 +494,8 @@ void yahoo_process_chat_logout(PurpleConnection *gc, struct yahoo_packet *pkt)
void yahoo_process_chat_join(PurpleConnection *gc, struct yahoo_packet *pkt)
{
PurpleAccount *account = purple_connection_get_account(gc);
- YahooData *yd = (YahooData *) gc->proto_data;
- PurpleConversation *c = NULL;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
+ PurpleChatConversation *c = NULL;
GSList *l;
GList *members = NULL;
GList *roomies = NULL;
@@ -506,17 +508,26 @@ void yahoo_process_chat_join(PurpleConnection *gc, struct yahoo_packet *pkt)
gchar const *failed_to_join = _("Failed to join chat");
switch (atoi(pair->value)) {
case 0xFFFFFFFA: /* -6 */
- purple_notify_error(gc, NULL, failed_to_join, _("Unknown room"));
+ purple_notify_error(gc, NULL, failed_to_join,
+ _("Unknown room"),
+ purple_request_cpar_from_connection(gc));
break;
case 0xFFFFFFF1: /* -15 */
- purple_notify_error(gc, NULL, failed_to_join, _("Maybe the room is full"));
+ purple_notify_error(gc, NULL, failed_to_join,
+ _("Maybe the room is full"),
+ purple_request_cpar_from_connection(gc));
break;
case 0xFFFFFFDD: /* -35 */
- purple_notify_error(gc, NULL, failed_to_join, _("Not available"));
+ purple_notify_error(gc, NULL, failed_to_join,
+ _("Not available"),
+ purple_request_cpar_from_connection(gc));
break;
default:
purple_notify_error(gc, NULL, failed_to_join,
- _("Unknown error. You may need to logout and wait five minutes before being able to rejoin a chatroom"));
+ _("Unknown error. You may need to "
+ "logout and wait five minutes before "
+ "being able to rejoin a chatroom"),
+ purple_request_cpar_from_connection(gc));
}
return;
}
@@ -575,63 +586,63 @@ void yahoo_process_chat_join(PurpleConnection *gc, struct yahoo_packet *pkt)
yahoo_chat_leave(gc, room,
purple_connection_get_display_name(gc), FALSE);
- c = purple_find_chat(gc, YAHOO_CHAT_ID);
+ c = purple_conversations_find_chat(gc, YAHOO_CHAT_ID);
- if (room && (!c || purple_conv_chat_has_left(PURPLE_CONV_CHAT(c))) &&
+ if (room && (!c || purple_chat_conversation_has_left(c)) &&
members && (members->next ||
!g_ascii_strcasecmp(members->data, purple_connection_get_display_name(gc)))) {
GList *l;
GList *flags = NULL;
for (l = members; l; l = l->next)
- flags = g_list_prepend(flags, GINT_TO_POINTER(PURPLE_CBFLAGS_NONE));
- if (c && purple_conv_chat_has_left(PURPLE_CONV_CHAT(c))) {
+ flags = g_list_prepend(flags, GINT_TO_POINTER(PURPLE_CHAT_USER_NONE));
+ if (c && purple_chat_conversation_has_left(c)) {
/* this might be a hack, but oh well, it should nicely */
char *tmpmsg;
- purple_conversation_set_name(c, room);
+ purple_conversation_set_name(PURPLE_CONVERSATION(c), room);
- c = serv_got_joined_chat(gc, YAHOO_CHAT_ID, room);
+ c = purple_serv_got_joined_chat(gc, YAHOO_CHAT_ID, room);
if (topic) {
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(c), NULL, topic);
+ purple_chat_conversation_set_topic(c, NULL, topic);
/* Also print the topic to the backlog so that the captcha link is clickable */
- purple_conv_chat_write(PURPLE_CONV_CHAT(c), "", topic, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(c), topic, 0);
}
yd->in_chat = 1;
yd->chat_name = g_strdup(room);
- purple_conv_chat_add_users(PURPLE_CONV_CHAT(c), members, NULL, flags, FALSE);
+ purple_chat_conversation_add_users(c, members, NULL, flags, FALSE);
tmpmsg = g_strdup_printf(_("You are now chatting in %s."), room);
- purple_conv_chat_write(PURPLE_CONV_CHAT(c), "", tmpmsg, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(c), tmpmsg, 0);
g_free(tmpmsg);
} else {
- c = serv_got_joined_chat(gc, YAHOO_CHAT_ID, room);
+ c = purple_serv_got_joined_chat(gc, YAHOO_CHAT_ID, room);
if (topic) {
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(c), NULL, topic);
+ purple_chat_conversation_set_topic(c, NULL, topic);
/* Also print the topic to the backlog so that the captcha link is clickable */
- purple_conv_chat_write(PURPLE_CONV_CHAT(c), "", topic, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(c), topic, 0);
}
yd->in_chat = 1;
yd->chat_name = g_strdup(room);
- purple_conv_chat_add_users(PURPLE_CONV_CHAT(c), members, NULL, flags, FALSE);
+ purple_chat_conversation_add_users(c, members, NULL, flags, FALSE);
}
g_list_free(flags);
} else if (c) {
if (topic) {
- const char *cur_topic = purple_conv_chat_get_topic(PURPLE_CONV_CHAT(c));
+ const char *cur_topic = purple_chat_conversation_get_topic(c);
if (cur_topic == NULL || strcmp(cur_topic, topic) != 0)
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(c), NULL, topic);
+ purple_chat_conversation_set_topic(c, NULL, topic);
}
- yahoo_chat_add_users(PURPLE_CONV_CHAT(c), members);
+ yahoo_chat_add_users(c, members);
}
- if (account->deny && c) {
- PurpleConversationUiOps *ops = purple_conversation_get_ui_ops(c);
- for (l = account->deny; l != NULL; l = l->next) {
+ if (purple_account_privacy_get_denied(account) && c) {
+ PurpleConversationUiOps *ops = purple_conversation_get_ui_ops(PURPLE_CONVERSATION(c));
+ for (l = purple_account_privacy_get_denied(account); l != NULL; l = l->next) {
for (roomies = members; roomies; roomies = roomies->next) {
if (!purple_utf8_strcasecmp((char *)l->data, roomies->data)) {
purple_debug_info("yahoo", "Ignoring room member %s in room %s\n" , (char *)roomies->data, room ? room : "");
- purple_conv_chat_ignore(PURPLE_CONV_CHAT(c),roomies->data);
- ops->chat_update_user(c, roomies->data);
+ purple_chat_conversation_ignore(c,roomies->data);
+ ops->chat_update_user(purple_chat_conversation_find_user(c, roomies->data));
}
}
}
@@ -654,8 +665,7 @@ void yahoo_process_chat_exit(PurpleConnection *gc, struct yahoo_packet *pkt)
if (pair->key == 104) {
g_free(room);
room = yahoo_string_decode(gc, pair->value, TRUE);
- }
- if (pair->key == 109) {
+ } else if (pair->key == 109) {
if (g_utf8_validate(pair->value, -1, NULL)) {
who = pair->value;
} else {
@@ -666,9 +676,10 @@ void yahoo_process_chat_exit(PurpleConnection *gc, struct yahoo_packet *pkt)
}
if (who && room) {
- PurpleConversation *c = purple_find_chat(gc, YAHOO_CHAT_ID);
- if (c && !purple_utf8_strcasecmp(purple_conversation_get_name(c), room))
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(c), who, NULL);
+ PurpleChatConversation *c = purple_conversations_find_chat(gc, YAHOO_CHAT_ID);
+ if (c && !purple_utf8_strcasecmp(purple_conversation_get_name(
+ PURPLE_CONVERSATION(c)), room))
+ purple_chat_conversation_remove_user(c, who, NULL);
}
g_free(room);
@@ -678,7 +689,7 @@ void yahoo_process_chat_message(PurpleConnection *gc, struct yahoo_packet *pkt)
{
char *room = NULL, *who = NULL, *msg = NULL, *msg2;
int msgtype = 1, utf8 = 1; /* default to utf8 */
- PurpleConversation *c = NULL;
+ PurpleChatConversation *c = NULL;
GSList *l;
for (l = pkt->hash; l; l = l->next) {
@@ -715,7 +726,7 @@ void yahoo_process_chat_message(PurpleConnection *gc, struct yahoo_packet *pkt)
}
}
- c = purple_find_chat(gc, YAHOO_CHAT_ID);
+ c = purple_conversations_find_chat(gc, YAHOO_CHAT_ID);
if (!who || !c) {
if (room)
g_free(room);
@@ -738,7 +749,8 @@ void yahoo_process_chat_message(PurpleConnection *gc, struct yahoo_packet *pkt)
msg = tmp;
}
- serv_got_chat_in(gc, YAHOO_CHAT_ID, who, 0, msg, time(NULL));
+ purple_serv_got_chat_in(gc, YAHOO_CHAT_ID, who,
+ PURPLE_MESSAGE_RECV, msg, time(NULL));
g_free(msg);
g_free(room);
}
@@ -785,7 +797,7 @@ void yahoo_process_chat_addinvite(PurpleConnection *gc, struct yahoo_packet *pkt
if (room && who) {
GHashTable *components;
- if (!purple_privacy_check(account, who) ||
+ if (!purple_account_privacy_check(account, who) ||
(purple_account_get_bool(account, "ignore_invites", FALSE)))
{
purple_debug_info("yahoo", "Invite to room %s from %s has been dropped.\n", room, who);
@@ -796,7 +808,7 @@ void yahoo_process_chat_addinvite(PurpleConnection *gc, struct yahoo_packet *pkt
components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
g_hash_table_replace(components, g_strdup("room"), g_strdup(room));
- serv_got_chat_invite(gc, room, who, msg, components);
+ purple_serv_got_chat_invite(gc, room, who, msg, components);
}
g_free(room);
@@ -805,9 +817,11 @@ void yahoo_process_chat_addinvite(PurpleConnection *gc, struct yahoo_packet *pkt
void yahoo_process_chat_goto(PurpleConnection *gc, struct yahoo_packet *pkt)
{
- if (pkt->status == -1)
+ if (pkt->status == -1) {
purple_notify_error(gc, NULL, _("Failed to join buddy in chat"),
- _("Maybe they're not in a chat?"));
+ _("Maybe they're not in a chat?"),
+ purple_request_cpar_from_connection(gc));
+ }
}
/*
@@ -826,7 +840,7 @@ void yahoo_conf_leave(YahooData *yd, const char *room, const char *dn, GList *wh
yahoo_packet_hash_str(pkt, 1, dn);
for (w = who; w; w = w->next) {
- const char *name = purple_conv_chat_cb_get_name(w->data);
+ const char *name = purple_chat_user_get_name(w->data);
yahoo_packet_hash_str(pkt, 3, name);
}
@@ -837,25 +851,23 @@ void yahoo_conf_leave(YahooData *yd, const char *room, const char *dn, GList *wh
static int yahoo_conf_send(PurpleConnection *gc, const char *dn, const char *room,
GList *members, const char *what)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_packet *pkt;
GList *who;
char *msg, *msg2;
- int utf8 = 1;
msg = yahoo_html_to_codes(what);
- msg2 = yahoo_string_encode(gc, msg, &utf8);
+ msg2 = yahoo_string_encode(gc, msg, TRUE);
pkt = yahoo_packet_new(YAHOO_SERVICE_CONFMSG, YAHOO_STATUS_AVAILABLE, yd->session_id);
yahoo_packet_hash_str(pkt, 1, dn);
for (who = members; who; who = who->next) {
- const char *name = purple_conv_chat_cb_get_name(who->data);
+ const char *name = purple_chat_user_get_name(who->data);
yahoo_packet_hash_str(pkt, 53, name);
}
yahoo_packet_hash(pkt, "ss", 57, room, 14, msg2);
- if (utf8)
- yahoo_packet_hash_str(pkt, 97, "1"); /* utf-8 */
+ yahoo_packet_hash_str(pkt, 97, "1"); /* UTF-8 */
yahoo_packet_send_and_free(pkt, yd);
g_free(msg);
@@ -864,7 +876,7 @@ static int yahoo_conf_send(PurpleConnection *gc, const char *dn, const char *roo
return 0;
}
-static void yahoo_conf_join(YahooData *yd, PurpleConversation *c, const char *dn, const char *room,
+static void yahoo_conf_join(YahooData *yd, PurpleChatConversation *c, const char *dn, const char *room,
const char *topic, const char *members)
{
struct yahoo_packet *pkt;
@@ -882,7 +894,7 @@ static void yahoo_conf_join(YahooData *yd, PurpleConversation *c, const char *dn
if (!strcmp(memarr[i], "") || !strcmp(memarr[i], dn))
continue;
yahoo_packet_hash_str(pkt, 3, memarr[i]);
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(c), memarr[i], NULL, PURPLE_CBFLAGS_NONE, TRUE);
+ purple_chat_conversation_add_user(c, memarr[i], NULL, PURPLE_CHAT_USER_NONE, TRUE);
}
}
yahoo_packet_send_and_free(pkt, yd);
@@ -891,28 +903,29 @@ static void yahoo_conf_join(YahooData *yd, PurpleConversation *c, const char *dn
g_strfreev(memarr);
}
-static void yahoo_conf_invite(PurpleConnection *gc, PurpleConversation *c,
+static void yahoo_conf_invite(PurpleConnection *gc, PurpleChatConversation *c,
const char *dn, const char *buddy, const char *room, const char *msg)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_packet *pkt;
- GList *members;
+ GList *members, *it;
char *msg2 = NULL;
if (msg)
- msg2 = yahoo_string_encode(gc, msg, NULL);
-
- members = purple_conv_chat_get_users(PURPLE_CONV_CHAT(c));
+ msg2 = yahoo_string_encode(gc, msg, FALSE);
pkt = yahoo_packet_new(YAHOO_SERVICE_CONFADDINVITE, YAHOO_STATUS_AVAILABLE, yd->session_id);
yahoo_packet_hash(pkt, "sssss", 1, dn, 51, buddy, 57, room, 58, msg?msg2:"", 13, "0");
- for(; members; members = members->next) {
- const char *name = purple_conv_chat_cb_get_name(members->data);
+
+ members = purple_chat_conversation_get_users(c);
+ for(it = members; it; it = it->next) {
+ const char *name = purple_chat_user_get_name(it->data);
if (!strcmp(name, dn))
continue;
yahoo_packet_hash(pkt, "ss", 52, name, 53, name);
}
+ g_list_free(members);
yahoo_packet_send_and_free(pkt, yd);
g_free(msg2);
@@ -924,11 +937,9 @@ static void yahoo_conf_invite(PurpleConnection *gc, PurpleConversation *c,
static void yahoo_chat_leave(PurpleConnection *gc, const char *room, const char *dn, gboolean logout)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_packet *pkt;
-
char *eroom;
- gboolean utf8 = 1;
if (yd->wm) {
g_return_if_fail(yd->ycht != NULL);
@@ -937,7 +948,7 @@ static void yahoo_chat_leave(PurpleConnection *gc, const char *room, const char
return;
}
- eroom = yahoo_string_encode(gc, room, &utf8);
+ eroom = yahoo_string_encode(gc, room, TRUE);
pkt = yahoo_packet_new(YAHOO_SERVICE_CHATEXIT, YAHOO_STATUS_AVAILABLE, yd->session_id);
yahoo_packet_hash(pkt, "sss", 104, eroom, 109, dn, 108, "1");
@@ -950,8 +961,8 @@ static void yahoo_chat_leave(PurpleConnection *gc, const char *room, const char
yd->chat_name = NULL;
}
- if (purple_find_chat(gc, YAHOO_CHAT_ID) != NULL)
- serv_got_chat_left(gc, YAHOO_CHAT_ID);
+ if (purple_conversations_find_chat(gc, YAHOO_CHAT_ID) != NULL)
+ purple_serv_got_chat_left(gc, YAHOO_CHAT_ID);
if (!logout)
return;
@@ -975,11 +986,10 @@ static void yahoo_chat_leave(PurpleConnection *gc, const char *room, const char
static int yahoo_chat_send(PurpleConnection *gc, const char *dn, const char *room, const char *what, PurpleMessageFlags flags)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_packet *pkt;
int me = 0;
char *msg1, *msg2, *room2;
- gboolean utf8 = TRUE;
if (yd->wm) {
g_return_val_if_fail(yd->ycht != NULL, 1);
@@ -994,9 +1004,9 @@ static int yahoo_chat_send(PurpleConnection *gc, const char *dn, const char *roo
msg2 = yahoo_html_to_codes(msg1);
g_free(msg1);
- msg1 = yahoo_string_encode(gc, msg2, &utf8);
+ msg1 = yahoo_string_encode(gc, msg2, TRUE);
g_free(msg2);
- room2 = yahoo_string_encode(gc, room, NULL);
+ room2 = yahoo_string_encode(gc, room, FALSE);
pkt = yahoo_packet_new(YAHOO_SERVICE_COMMENT, YAHOO_STATUS_AVAILABLE, yd->session_id);
@@ -1006,8 +1016,7 @@ static int yahoo_chat_send(PurpleConnection *gc, const char *dn, const char *roo
else
yahoo_packet_hash_str(pkt, 124, "1");
/* fixme: what about /think? (124=3) */
- if (utf8)
- yahoo_packet_hash_str(pkt, 97, "1");
+ yahoo_packet_hash_str(pkt, 97, "1"); /* UTF-8 */
yahoo_packet_send_and_free(pkt, yd);
g_free(msg1);
@@ -1020,10 +1029,9 @@ static int yahoo_chat_send(PurpleConnection *gc, const char *dn, const char *roo
static void yahoo_chat_invite(PurpleConnection *gc, const char *dn, const char *buddy,
const char *room, const char *msg)
{
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
struct yahoo_packet *pkt;
char *room2, *msg2 = NULL;
- gboolean utf8 = TRUE;
if (yd->wm) {
g_return_if_fail(yd->ycht != NULL);
@@ -1031,9 +1039,9 @@ static void yahoo_chat_invite(PurpleConnection *gc, const char *dn, const char *
return;
}
- room2 = yahoo_string_encode(gc, room, &utf8);
+ room2 = yahoo_string_encode(gc, room, TRUE);
if (msg)
- msg2 = yahoo_string_encode(gc, msg, NULL);
+ msg2 = yahoo_string_encode(gc, msg, FALSE);
pkt = yahoo_packet_new(YAHOO_SERVICE_CHATADDINVITE, YAHOO_STATUS_AVAILABLE, yd->session_id);
yahoo_packet_hash(pkt, "sssss", 1, dn, 118, buddy, 104, room2, 117, (msg2?msg2:""), 129, "0");
@@ -1048,7 +1056,7 @@ void yahoo_chat_goto(PurpleConnection *gc, const char *name)
YahooData *yd;
struct yahoo_packet *pkt;
- yd = gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
if (yd->wm) {
g_return_if_fail(yd->ycht != NULL);
@@ -1080,49 +1088,61 @@ void yahoo_chat_goto(PurpleConnection *gc, const char *name)
void yahoo_c_leave(PurpleConnection *gc, int id)
{
- YahooData *yd = (YahooData *) gc->proto_data;
- PurpleConversation *c;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
+ PurpleChatConversation *c;
if (!yd)
return;
- c = purple_find_chat(gc, id);
+ c = purple_conversations_find_chat(gc, id);
if (!c)
return;
if (id != YAHOO_CHAT_ID) {
- yahoo_conf_leave(yd, purple_conversation_get_name(c),
- purple_connection_get_display_name(gc), purple_conv_chat_get_users(PURPLE_CONV_CHAT(c)));
- yd->confs = g_slist_remove(yd->confs, c);
+ GList *users;
+ users = purple_chat_conversation_get_users(c);
+ yahoo_conf_leave(yd,
+ purple_conversation_get_name(PURPLE_CONVERSATION(c)),
+ purple_connection_get_display_name(gc), users);
+ g_list_free(users);
+ yd->confs = g_slist_remove(yd->confs, c);
} else {
- yahoo_chat_leave(gc, purple_conversation_get_name(c), purple_connection_get_display_name(gc), TRUE);
+ yahoo_chat_leave(gc, purple_conversation_get_name(PURPLE_CONVERSATION(c)),
+ purple_connection_get_display_name(gc), TRUE);
}
- serv_got_chat_left(gc, id);
+ purple_serv_got_chat_left(gc, id);
}
-int yahoo_c_send(PurpleConnection *gc, int id, const char *what, PurpleMessageFlags flags)
+int yahoo_c_send(PurpleConnection *gc, int id, PurpleMessage *msg)
{
- PurpleConversation *c;
+ PurpleChatConversation *c;
int ret;
YahooData *yd;
+ const gchar *what = purple_message_get_contents(msg);
+ PurpleMessageFlags flags = purple_message_get_flags(msg);
- yd = (YahooData *) gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
if (!yd)
return -1;
- c = purple_find_chat(gc, id);
+ c = purple_conversations_find_chat(gc, id);
if (!c)
return -1;
if (id != YAHOO_CHAT_ID) {
- ret = yahoo_conf_send(gc, purple_connection_get_display_name(gc),
- purple_conversation_get_name(c), purple_conv_chat_get_users(PURPLE_CONV_CHAT(c)), what);
+ GList *users;
+ users = purple_chat_conversation_get_users(c);
+ ret = yahoo_conf_send(gc,
+ purple_connection_get_display_name(gc),
+ purple_conversation_get_name(PURPLE_CONVERSATION(c)),
+ users, what);
+ g_list_free(users);
} else {
ret = yahoo_chat_send(gc, purple_connection_get_display_name(gc),
- purple_conversation_get_name(c), what, flags);
+ purple_conversation_get_name(PURPLE_CONVERSATION(c)), what, flags);
if (!ret)
- serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(c)),
+ purple_serv_got_chat_in(gc, purple_chat_conversation_get_id(c),
purple_connection_get_display_name(gc), flags, what, time(NULL));
}
return ret;
@@ -1163,9 +1183,9 @@ void yahoo_c_join(PurpleConnection *gc, GHashTable *data)
{
YahooData *yd;
char *room, *topic, *type;
- PurpleConversation *c;
+ PurpleChatConversation *c;
- yd = (YahooData *) gc->proto_data;
+ yd = purple_connection_get_protocol_data(gc);
if (!yd)
return;
@@ -1181,9 +1201,9 @@ void yahoo_c_join(PurpleConnection *gc, GHashTable *data)
int id;
const char *members = g_hash_table_lookup(data, "members");
id = yd->conf_id++;
- c = serv_got_joined_chat(gc, id, room);
+ c = purple_serv_got_joined_chat(gc, id, room);
yd->confs = g_slist_prepend(yd->confs, c);
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(c), purple_connection_get_display_name(gc), topic);
+ purple_chat_conversation_set_topic(c, purple_connection_get_display_name(gc), topic);
yahoo_conf_join(yd, c, purple_connection_get_display_name(gc), room, topic, members);
return;
} else {
@@ -1214,47 +1234,35 @@ void yahoo_c_join(PurpleConnection *gc, GHashTable *data)
void yahoo_c_invite(PurpleConnection *gc, int id, const char *msg, const char *name)
{
- PurpleConversation *c;
+ PurpleChatConversation *c;
- c = purple_find_chat(gc, id);
- if (!c || !c->name)
+ c = purple_conversations_find_chat(gc, id);
+ if (!c || !purple_conversation_get_name(PURPLE_CONVERSATION(c)))
return;
if (id != YAHOO_CHAT_ID) {
yahoo_conf_invite(gc, c, purple_connection_get_display_name(gc), name,
- purple_conversation_get_name(c), msg);
+ purple_conversation_get_name(PURPLE_CONVERSATION(c)), msg);
} else {
yahoo_chat_invite(gc, purple_connection_get_display_name(gc), name,
- purple_conversation_get_name(c), msg);
+ purple_conversation_get_name(PURPLE_CONVERSATION(c)), msg);
}
}
struct yahoo_roomlist {
- int fd;
- int inpa;
- gchar *txbuf;
- gsize tx_written;
- guchar *rxqueue;
- int rxlen;
- gboolean started;
- char *path;
- char *host;
+ PurpleHttpConnection *hc;
+ gchar *url;
+
PurpleRoomlist *list;
PurpleRoomlistRoom *cat;
PurpleRoomlistRoom *ucat;
- GMarkupParseContext *parse;
};
static void yahoo_roomlist_destroy(struct yahoo_roomlist *yrl)
{
- if (yrl->inpa)
- purple_input_remove(yrl->inpa);
- g_free(yrl->txbuf);
- g_free(yrl->rxqueue);
- g_free(yrl->path);
- g_free(yrl->host);
- if (yrl->parse)
- g_markup_parse_context_free(yrl->parse);
+ purple_http_conn_cancel(yrl->hc);
+ g_free(yrl->url);
+
g_free(yrl);
}
@@ -1426,126 +1434,73 @@ static void yahoo_roomlist_cleanup(PurpleRoomlist *list, struct yahoo_roomlist *
purple_roomlist_set_in_progress(list, FALSE);
if (yrl) {
- list->proto_data = g_list_remove(list->proto_data, yrl);
+ GList *proto_data = purple_roomlist_get_protocol_data(list);
+ proto_data = g_list_remove(proto_data, yrl);
+ purple_roomlist_set_protocol_data(list, proto_data);
yahoo_roomlist_destroy(yrl);
}
- purple_roomlist_unref(list);
+ g_object_unref(list);
}
-static void yahoo_roomlist_pending(gpointer data, gint source, PurpleInputCondition cond)
+static void
+yahoo_roomlist_got(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _yrl)
{
- struct yahoo_roomlist *yrl = data;
- PurpleRoomlist *list = yrl->list;
- char buf[1024];
- int len;
- guchar *start;
- struct yahoo_chatxml_state *s;
+ struct yahoo_roomlist *yrl = _yrl;
+ PurpleConnection *gc;
+ GMarkupParseContext *parse;
- len = read(yrl->fd, buf, sizeof(buf));
+ yrl->hc = NULL;
- if (len < 0 && errno == EAGAIN)
- return;
+ gc = purple_account_get_connection(purple_roomlist_get_account(
+ yrl->list));
- if (len <= 0) {
- if (yrl->parse)
- g_markup_parse_context_end_parse(yrl->parse, NULL);
- yahoo_roomlist_cleanup(list, yrl);
+ if (!purple_http_response_is_successful(response)) {
+ purple_notify_error(gc, NULL, _("Unable to connect"),
+ _("Fetching the room list failed."),
+ purple_request_cpar_from_connection(gc));
+ yahoo_roomlist_cleanup(yrl->list, yrl);
return;
}
- yrl->rxqueue = g_realloc(yrl->rxqueue, len + yrl->rxlen);
- memcpy(yrl->rxqueue + yrl->rxlen, buf, len);
- yrl->rxlen += len;
-
- if (!yrl->started) {
- yrl->started = TRUE;
- start = (guchar *)g_strstr_len((char *)yrl->rxqueue, yrl->rxlen, "\r\n\r\n");
- if (!start || (start - yrl->rxqueue + 4) >= yrl->rxlen)
- return;
- start += 4;
- } else {
- start = yrl->rxqueue;
- }
-
- if (yrl->parse == NULL) {
- s = yahoo_chatxml_state_new(list, yrl);
- yrl->parse = g_markup_parse_context_new(&parser, 0, s,
- (GDestroyNotify)yahoo_chatxml_state_destroy);
- }
-
- if (!g_markup_parse_context_parse(yrl->parse, (char *)start, (yrl->rxlen - (start - yrl->rxqueue)), NULL)) {
+ parse = g_markup_parse_context_new(&parser, 0,
+ yahoo_chatxml_state_new(yrl->list, yrl),
+ (GDestroyNotify)yahoo_chatxml_state_destroy);
- yahoo_roomlist_cleanup(list, yrl);
- return;
- }
-
- yrl->rxlen = 0;
-}
-
-static void yahoo_roomlist_send_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
- struct yahoo_roomlist *yrl;
- PurpleRoomlist *list;
- int written, remaining;
-
- yrl = data;
- list = yrl->list;
-
- remaining = strlen(yrl->txbuf) - yrl->tx_written;
- written = write(yrl->fd, yrl->txbuf + yrl->tx_written, remaining);
-
- if (written < 0 && errno == EAGAIN)
- written = 0;
- else if (written <= 0) {
- purple_input_remove(yrl->inpa);
- yrl->inpa = 0;
- g_free(yrl->txbuf);
- yrl->txbuf = NULL;
- purple_notify_error(purple_account_get_connection(list->account), NULL, _("Unable to connect"), _("Fetching the room list failed."));
- yahoo_roomlist_cleanup(list, yrl);
- return;
- }
-
- if (written < remaining) {
- yrl->tx_written += written;
+ if (!g_markup_parse_context_parse(parse,
+ purple_http_response_get_data(response, NULL),
+ purple_http_response_get_data_len(response), NULL))
+ {
+ purple_debug_error("yahoo", "Couldn't parse the room list\n");
+ yahoo_roomlist_cleanup(yrl->list, yrl);
return;
}
- g_free(yrl->txbuf);
- yrl->txbuf = NULL;
-
- purple_input_remove(yrl->inpa);
- yrl->inpa = purple_input_add(yrl->fd, PURPLE_INPUT_READ,
- yahoo_roomlist_pending, yrl);
-
+ g_markup_parse_context_free(parse);
}
-static void yahoo_roomlist_got_connected(gpointer data, gint source, const gchar *error_message)
+static void
+yahoo_roomlist_make_request(struct yahoo_roomlist *yrl)
{
- struct yahoo_roomlist *yrl = data;
PurpleRoomlist *list = yrl->list;
- YahooData *yd = purple_account_get_connection(list->account)->proto_data;
-
- if (source < 0) {
- purple_notify_error(purple_account_get_connection(list->account), NULL, _("Unable to connect"), _("Fetching the room list failed."));
- yahoo_roomlist_cleanup(list, yrl);
- return;
- }
-
- yrl->fd = source;
-
- yrl->txbuf = g_strdup_printf(
- "GET http://%s/%s HTTP/1.0\r\n"
- "Host: %s\r\n"
- "Cookie: Y=%s; T=%s\r\n\r\n",
- yrl->host, yrl->path, yrl->host, yd->cookie_y,
- yd->cookie_t);
-
-
- yrl->inpa = purple_input_add(yrl->fd, PURPLE_INPUT_WRITE,
- yahoo_roomlist_send_cb, yrl);
- yahoo_roomlist_send_cb(yrl, yrl->fd, PURPLE_INPUT_WRITE);
+ PurpleAccount *account = purple_roomlist_get_account(list);
+ PurpleConnection *pc = purple_account_get_connection(account);
+ YahooData *yd = purple_connection_get_protocol_data(pc);
+ PurpleHttpRequest *req;
+ PurpleHttpCookieJar *cjar;
+ PurpleConnection *gc;
+
+ gc = purple_account_get_connection(purple_roomlist_get_account(
+ yrl->list));
+
+ req = purple_http_request_new(yrl->url);
+ cjar = purple_http_request_get_cookie_jar(req);
+ purple_http_cookie_jar_set(cjar, "Y", yd->cookie_y);
+ purple_http_cookie_jar_set(cjar, "T", yd->cookie_t);
+
+ yrl->hc = purple_http_request(gc, req, yahoo_roomlist_got, yrl);
+ purple_http_request_unref(req);
}
PurpleRoomlist *yahoo_roomlist_get_list(PurpleConnection *gc)
@@ -1557,6 +1512,7 @@ PurpleRoomlist *yahoo_roomlist_get_list(PurpleConnection *gc)
struct yahoo_roomlist *yrl;
const char *rll, *rlurl;
char *url;
+ GList *proto_data;
account = purple_connection_get_account(gc);
@@ -1576,8 +1532,7 @@ PurpleRoomlist *yahoo_roomlist_get_list(PurpleConnection *gc)
rl = purple_roomlist_new(account);
yrl->list = rl;
- purple_url_parse(url, &(yrl->host), NULL, &(yrl->path), NULL, NULL);
- g_free(url);
+ yrl->url = url;
f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", "room", TRUE);
fields = g_list_append(fields, f);
@@ -1599,17 +1554,13 @@ PurpleRoomlist *yahoo_roomlist_get_list(PurpleConnection *gc)
purple_roomlist_set_fields(rl, fields);
- if (purple_proxy_connect(gc, account, yrl->host, 80,
- yahoo_roomlist_got_connected, yrl) == NULL)
- {
- purple_notify_error(gc, NULL, _("Connection problem"), _("Unable to fetch room list."));
- yahoo_roomlist_cleanup(rl, yrl);
- return NULL;
- }
-
- rl->proto_data = g_list_append(rl->proto_data, yrl);
+ proto_data = purple_roomlist_get_protocol_data(rl);
+ proto_data = g_list_append(proto_data, yrl);
+ purple_roomlist_set_protocol_data(rl, proto_data);
+ yahoo_roomlist_make_request(yrl);
purple_roomlist_set_in_progress(rl, TRUE);
+
return rl;
}
@@ -1617,68 +1568,63 @@ void yahoo_roomlist_cancel(PurpleRoomlist *list)
{
GList *l, *k;
- k = l = list->proto_data;
- list->proto_data = NULL;
+ k = l = purple_roomlist_get_protocol_data(list);
+ purple_roomlist_set_protocol_data(list, NULL);
purple_roomlist_set_in_progress(list, FALSE);
for (; l; l = l->next) {
yahoo_roomlist_destroy(l->data);
- purple_roomlist_unref(list);
+ g_object_unref(list);
}
g_list_free(k);
}
void yahoo_roomlist_expand_category(PurpleRoomlist *list, PurpleRoomlistRoom *category)
{
+ PurpleAccount *account;
struct yahoo_roomlist *yrl;
char *url;
char *id;
const char *rll;
+ GList *proto_data;
- if (category->type != PURPLE_ROOMLIST_ROOMTYPE_CATEGORY)
+ if (purple_roomlist_room_get_room_type(category) != PURPLE_ROOMLIST_ROOMTYPE_CATEGORY)
return;
- if (!(id = g_list_nth_data(category->fields, 1))) {
+ if (!(id = g_list_nth_data(purple_roomlist_room_get_fields(category), 1))) {
purple_roomlist_set_in_progress(list, FALSE);
return;
}
- rll = purple_account_get_string(list->account, "room_list_locale",
+ account = purple_roomlist_get_account(list);
+ rll = purple_account_get_string(account, "room_list_locale",
YAHOO_ROOMLIST_LOCALE);
if (rll != NULL && *rll != '\0') {
url = g_strdup_printf("%s?chatroom_%s=0&intl=%s",
- purple_account_get_string(list->account,"room_list",
+ purple_account_get_string(account,"room_list",
YAHOO_ROOMLIST_URL), id, rll);
} else {
url = g_strdup_printf("%s?chatroom_%s=0",
- purple_account_get_string(list->account,"room_list",
+ purple_account_get_string(account,"room_list",
YAHOO_ROOMLIST_URL), id);
}
yrl = g_new0(struct yahoo_roomlist, 1);
yrl->list = list;
yrl->cat = category;
- list->proto_data = g_list_append(list->proto_data, yrl);
- purple_url_parse(url, &(yrl->host), NULL, &(yrl->path), NULL, NULL);
- g_free(url);
+ proto_data = purple_roomlist_get_protocol_data(list);
+ proto_data = g_list_append(proto_data, yrl);
+ purple_roomlist_set_protocol_data(list, proto_data);
+
+ yrl->url = url;
yrl->ucat = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_CATEGORY, _("User Rooms"), yrl->cat);
purple_roomlist_room_add(list, yrl->ucat);
- if (purple_proxy_connect(purple_account_get_connection(list->account),
- list->account, yrl->host, 80,
- yahoo_roomlist_got_connected, yrl) == NULL)
- {
- purple_notify_error(purple_account_get_connection(list->account),
- NULL, _("Connection problem"), _("Unable to fetch room list."));
- purple_roomlist_ref(list);
- yahoo_roomlist_cleanup(list, yrl);
- return;
- }
-
+ yahoo_roomlist_make_request(yrl);
purple_roomlist_set_in_progress(list, TRUE);
- purple_roomlist_ref(list);
+ g_object_ref(list);
}
diff --git a/libpurple/protocols/yahoo/yahoochat.h b/libpurple/protocols/yahoo/yahoochat.h
index 061be7760f..44b7b3144f 100644
--- a/libpurple/protocols/yahoo/yahoochat.h
+++ b/libpurple/protocols/yahoo/yahoochat.h
@@ -43,7 +43,7 @@ void yahoo_process_chat_addinvite(PurpleConnection *gc, struct yahoo_packet *pkt
void yahoo_process_chat_goto(PurpleConnection *gc, struct yahoo_packet *pkt);
void yahoo_c_leave(PurpleConnection *gc, int id);
-int yahoo_c_send(PurpleConnection *gc, int id, const char *what, PurpleMessageFlags flags);
+int yahoo_c_send(PurpleConnection *gc, int id, PurpleMessage *msg);
GList *yahoo_c_info(PurpleConnection *gc);
GHashTable *yahoo_c_info_defaults(PurpleConnection *gc, const char *chat_name);
void yahoo_c_join(PurpleConnection *gc, GHashTable *data);
@@ -60,7 +60,7 @@ void yahoo_roomlist_cancel(PurpleRoomlist *list);
void yahoo_roomlist_expand_category(PurpleRoomlist *list, PurpleRoomlistRoom *category);
/* util */
-void yahoo_chat_add_users(PurpleConvChat *chat, GList *newusers);
-void yahoo_chat_add_user(PurpleConvChat *chat, const char *user, const char *reason);
+void yahoo_chat_add_users(PurpleChatConversation *chat, GList *newusers);
+void yahoo_chat_add_user(PurpleChatConversation *chat, const char *user, const char *reason);
#endif /* _YAHOO_CHAT_H_ */
diff --git a/libpurple/protocols/yahoo/ycht.c b/libpurple/protocols/yahoo/ycht.c
index 5a05e1869d..a5e147b649 100644
--- a/libpurple/protocols/yahoo/ycht.c
+++ b/libpurple/protocols/yahoo/ycht.c
@@ -53,7 +53,7 @@
static void ycht_process_login(YchtConn *ycht, YchtPkt *pkt)
{
PurpleConnection *gc = ycht->gc;
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
if (ycht->logged_in)
return;
@@ -68,7 +68,7 @@ static void ycht_process_login(YchtConn *ycht, YchtPkt *pkt)
static void ycht_process_logout(YchtConn *ycht, YchtPkt *pkt)
{
PurpleConnection *gc = ycht->gc;
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
yd->chat_online = FALSE;
ycht->logged_in = FALSE;
@@ -78,7 +78,7 @@ static void ycht_process_chatjoin(YchtConn *ycht, YchtPkt *pkt)
{
char *room, *topic;
PurpleConnection *gc = ycht->gc;
- PurpleConversation *c = NULL;
+ PurpleChatConversation *c = NULL;
gboolean new_room = FALSE;
char **members;
int i;
@@ -101,23 +101,23 @@ static void ycht_process_chatjoin(YchtConn *ycht, YchtPkt *pkt)
new_room = TRUE;
if (new_room && ycht->changing_rooms) {
- serv_got_chat_left(gc, YAHOO_CHAT_ID);
+ purple_serv_got_chat_left(gc, YAHOO_CHAT_ID);
ycht->changing_rooms = FALSE;
- c = serv_got_joined_chat(gc, YAHOO_CHAT_ID, room);
+ c = purple_serv_got_joined_chat(gc, YAHOO_CHAT_ID, room);
} else {
- c = purple_find_chat(gc, YAHOO_CHAT_ID);
+ c = purple_conversations_find_chat(gc, YAHOO_CHAT_ID);
}
if (topic)
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(c), NULL, topic);
+ purple_chat_conversation_set_topic(c, NULL, topic);
for (i = 0; members[i]; i++) {
if (new_room) {
/*if (!strcmp(members[i], purple_connection_get_display_name(ycht->gc)))
continue;*/
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(c), members[i], NULL, PURPLE_CBFLAGS_NONE, TRUE);
+ purple_chat_conversation_add_user(c, members[i], NULL, PURPLE_CHAT_USER_NONE, TRUE);
} else {
- yahoo_chat_add_user(PURPLE_CONV_CHAT(c), members[i], NULL);
+ yahoo_chat_add_user(c, members[i], NULL);
}
}
@@ -132,9 +132,9 @@ static void ycht_process_chatpart(YchtConn *ycht, YchtPkt *pkt)
who = g_list_nth_data(pkt->data, 1);
if (who && room) {
- PurpleConversation *c = purple_find_chat(ycht->gc, YAHOO_CHAT_ID);
- if (c && !purple_utf8_strcasecmp(purple_conversation_get_name(c), room))
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(c), who, NULL);
+ PurpleChatConversation *c = purple_conversations_find_chat(ycht->gc, YAHOO_CHAT_ID);
+ if (c && !purple_utf8_strcasecmp(purple_conversation_get_name(PURPLE_CONVERSATION(c)), room))
+ purple_chat_conversation_remove_user(c, who, NULL);
}
}
@@ -142,7 +142,7 @@ static void ycht_process_chatpart(YchtConn *ycht, YchtPkt *pkt)
static void ycht_progress_chatmsg(YchtConn *ycht, YchtPkt *pkt)
{
char *who, *what, *msg;
- PurpleConversation *c;
+ PurpleChatConversation *c;
PurpleConnection *gc = ycht->gc;
who = g_list_nth_data(pkt->data, 1);
@@ -151,7 +151,7 @@ static void ycht_progress_chatmsg(YchtConn *ycht, YchtPkt *pkt)
if (!who || !what)
return;
- c = purple_find_chat(gc, YAHOO_CHAT_ID);
+ c = purple_conversations_find_chat(gc, YAHOO_CHAT_ID);
if (!c)
return;
@@ -165,7 +165,7 @@ static void ycht_progress_chatmsg(YchtConn *ycht, YchtPkt *pkt)
what = tmp;
}
- serv_got_chat_in(gc, YAHOO_CHAT_ID, who, 0, what, time(NULL));
+ purple_serv_got_chat_in(gc, YAHOO_CHAT_ID, who, PURPLE_MESSAGE_RECV, what, time(NULL));
g_free(what);
}
@@ -173,7 +173,7 @@ static void ycht_progress_online_friends(YchtConn *ycht, YchtPkt *pkt)
{
#if 0
PurpleConnection *gc = ycht->gc;
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
if (ycht->logged_in)
return;
@@ -267,8 +267,9 @@ static void ycht_packet_send_write_cb(gpointer data, gint source, PurpleInputCon
{
YchtConn *ycht = data;
int ret, writelen;
+ const gchar *output = NULL;
- writelen = purple_circ_buffer_get_max_read(ycht->txbuf);
+ writelen = purple_circular_buffer_get_max_read(ycht->txbuf);
if (writelen == 0) {
purple_input_remove(ycht->tx_handler);
@@ -276,7 +277,9 @@ static void ycht_packet_send_write_cb(gpointer data, gint source, PurpleInputCon
return;
}
- ret = write(ycht->fd, ycht->txbuf->outptr, writelen);
+ output = purple_circular_buffer_get_output(ycht->txbuf);
+
+ ret = write(ycht->fd, output, writelen);
if (ret < 0 && errno == EAGAIN)
return;
@@ -285,14 +288,14 @@ static void ycht_packet_send_write_cb(gpointer data, gint source, PurpleInputCon
/*
gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
g_strerror(errno));
- purple_connection_error_reason(purple_account_get_connection(irc->account),
+ purple_connection_error(purple_account_get_connection(irc->account),
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
g_free(tmp);
*/
return;
}
- purple_circ_buffer_mark_read(ycht->txbuf, ret);
+ purple_circular_buffer_mark_read(ycht->txbuf, ret);
}
@@ -345,7 +348,7 @@ static void ycht_packet_send(YchtConn *ycht, YchtPkt *pkt)
ycht->tx_handler = purple_input_add(ycht->fd,
PURPLE_INPUT_WRITE, ycht_packet_send_write_cb,
ycht);
- purple_circ_buffer_append(ycht->txbuf, buf + written,
+ purple_circular_buffer_append(ycht->txbuf, buf + written,
len - written);
}
@@ -412,13 +415,9 @@ static void ycht_packet_process(YchtConn *ycht, YchtPkt *pkt)
static void ycht_packet_free(YchtPkt *pkt)
{
- GList *l;
-
g_return_if_fail(pkt != NULL);
- for (l = pkt->data; l; l = l->next)
- g_free(l->data);
- g_list_free(pkt->data);
+ g_list_free_full(pkt->data, g_free);
g_free(pkt);
}
@@ -429,7 +428,7 @@ static void ycht_packet_free(YchtPkt *pkt)
void ycht_connection_close(YchtConn *ycht)
{
- YahooData *yd = ycht->gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(ycht->gc);
if (yd) {
yd->ycht = NULL;
@@ -444,7 +443,7 @@ void ycht_connection_close(YchtConn *ycht)
if (ycht->tx_handler)
purple_input_remove(ycht->tx_handler);
- purple_circ_buffer_destroy(ycht->txbuf);
+ g_object_unref(G_OBJECT(ycht->txbuf));
g_free(ycht->rxqueue);
@@ -454,7 +453,9 @@ void ycht_connection_close(YchtConn *ycht)
static void ycht_connection_error(YchtConn *ycht, const gchar *error)
{
- purple_notify_info(ycht->gc, NULL, _("Connection problem with the YCHT server"), error);
+ purple_notify_info(ycht->gc, NULL,
+ _("Connection problem with the YCHT server"), error,
+ purple_request_cpar_from_connection(ycht->gc));
ycht_connection_close(ycht);
}
@@ -490,7 +491,7 @@ static void ycht_pending(gpointer data, gint source, PurpleInputCondition cond)
while (1) {
YchtPkt *pkt;
int pos = 0;
- int pktlen;
+ guint pktlen;
guint service;
guint version;
gint status;
@@ -541,7 +542,7 @@ static void ycht_got_connected(gpointer data, gint source, const gchar *error_me
{
YchtConn *ycht = data;
PurpleConnection *gc = ycht->gc;
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
YchtPkt *pkt;
char *buf;
@@ -568,7 +569,7 @@ static void ycht_got_connected(gpointer data, gint source, const gchar *error_me
void ycht_connection_open(PurpleConnection *gc)
{
YchtConn *ycht;
- YahooData *yd = gc->proto_data;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
PurpleAccount *account = purple_connection_get_account(gc);
ycht = g_new0(YchtConn, 1);
@@ -621,7 +622,7 @@ int ycht_chat_send(YchtConn *ycht, const char *room, const char *what)
pkt = ycht_packet_new(YCHT_VERSION, YCHT_SERVICE_CHATMSG, 0);
msg1 = yahoo_html_to_codes(what);
- msg2 = yahoo_string_encode(ycht->gc, msg1, NULL);
+ msg2 = yahoo_string_encode(ycht->gc, msg1, FALSE);
g_free(msg1);
buf = g_strdup_printf("%s\001%s", ycht->room, msg2);
diff --git a/libpurple/protocols/yahoo/ycht.h b/libpurple/protocols/yahoo/ycht.h
index 325745ceeb..0d4fc7efd9 100644
--- a/libpurple/protocols/yahoo/ycht.h
+++ b/libpurple/protocols/yahoo/ycht.h
@@ -73,7 +73,7 @@ typedef struct _YchtConn {
gboolean changing_rooms;
guchar *rxqueue;
guint rxlen;
- PurpleCircBuffer *txbuf;
+ PurpleCircularBuffer *txbuf;
guint tx_handler;
} YchtConn;
diff --git a/libpurple/protocols/zephyr/Makefile.am b/libpurple/protocols/zephyr/Makefile.am
index 9e2e4e25d6..91d871a0b3 100644
--- a/libpurple/protocols/zephyr/Makefile.am
+++ b/libpurple/protocols/zephyr/Makefile.am
@@ -1,4 +1,4 @@
-pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+pkgdir = @PURPLE_PLUGINDIR@
ZEPHYRSOURCES = \
ZAsyncLocate.c \
@@ -72,7 +72,7 @@ ZEPHYRLIBS = $(KRB4_LDFLAGS) $(KRB4_LIBS)
ZEPHYRLIBSEXT = $(ZEPHYR_LDFLAGS) $(ZEPHYR_LIBS)
-libzephyr_la_LDFLAGS = -module -avoid-version
+libzephyr_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
if STATIC_ZEPHYR
@@ -80,7 +80,7 @@ st = -DPURPLE_STATIC_PRPL -Dlint
noinst_LTLIBRARIES = libzephyr.la
libzephyr_la_SOURCES = $(ZEPHYRSOURCES)
libzephyr_la_CFLAGS = $(AM_CFLAGS)
-libzephyr_la_LIBADD = $(ZEPHYRLIBS)
+libzephyr_la_LIBADD = @PURPLE_LIBS@ $(ZEPHYRLIBS)
else
@@ -89,10 +89,10 @@ pkg_LTLIBRARIES = libzephyr.la
if EXTERNAL_LIBZEPHYR
libzephyr_la_SOURCES = $(ZEPHYRSOURCESEXT)
-libzephyr_la_LIBADD = $(GLIB_LIBS) $(ZEPHYRLIBSEXT)
+libzephyr_la_LIBADD = @PURPLE_LIBS@ $(ZEPHYRLIBSEXT)
else
libzephyr_la_SOURCES = $(ZEPHYRSOURCES)
-libzephyr_la_LIBADD = $(GLIB_LIBS) $(ZEPHYRLIBS)
+libzephyr_la_LIBADD = @PURPLE_LIBS@ $(ZEPHYRLIBS)
endif
@@ -103,7 +103,6 @@ AM_CPPFLAGS = \
-I$(top_srcdir)/libpurple \
-I$(top_builddir)/libpurple \
-I$(top_srcdir)/libpurple/protocols \
- -DCONFDIR=\"$(sysconfdir)\" \
$(GLIB_CFLAGS) \
$(KRB4_CFLAGS) \
$(DEBUG_CFLAGS)
diff --git a/libpurple/protocols/zephyr/Makefile.mingw b/libpurple/protocols/zephyr/Makefile.mingw
index cbb4ff971c..6d3c28e104 100644
--- a/libpurple/protocols/zephyr/Makefile.mingw
+++ b/libpurple/protocols/zephyr/Makefile.mingw
@@ -114,6 +114,7 @@ OBJECTS = $(C_SRC:%.c=%.o)
##
LIBS = \
-lglib-2.0 \
+ -lgobject-2.0 \
-lws2_32 \
-lintl \
-lpurple
diff --git a/libpurple/protocols/zephyr/ZReadAscii.c b/libpurple/protocols/zephyr/ZReadAscii.c
index c90a103a46..deac15c63f 100644
--- a/libpurple/protocols/zephyr/ZReadAscii.c
+++ b/libpurple/protocols/zephyr/ZReadAscii.c
@@ -10,7 +10,7 @@
#include "internal.h"
-#define Z_cnvt_xtoi(c) ((temp=(c)-'0'),(temp<10)?temp:((temp-='A'-'9'-1),(temp<16)?temp:-1))
+#define Z_cnvt_xtoi(c) ((temp=(c)-'0'),(temp<10)?(int)temp:((temp-='A'-'9'-1),(temp<16)?(int)temp:-1))
Code_t ZReadAscii(ptr, len, field, num)
char *ptr;
diff --git a/libpurple/protocols/zephyr/ZRetSubs.c b/libpurple/protocols/zephyr/ZRetSubs.c
index 3a1e9addd0..d08430f021 100644
--- a/libpurple/protocols/zephyr/ZRetSubs.c
+++ b/libpurple/protocols/zephyr/ZRetSubs.c
@@ -132,6 +132,11 @@ static Code_t Z_RetSubs(notice, nsubs, auth_routine)
__subscriptions_num = __subscriptions_num / 3;
+ if (!__subscriptions_num) {
+ ZFreeNotice(&retnotice);
+ continue;
+ }
+
free(__subscriptions_list);
__subscriptions_list = (ZSubscription_t *)
malloc((unsigned)(__subscriptions_num*
diff --git a/libpurple/protocols/zephyr/ZVariables.c b/libpurple/protocols/zephyr/ZVariables.c
index d651f152b4..9f424c8791 100644
--- a/libpurple/protocols/zephyr/ZVariables.c
+++ b/libpurple/protocols/zephyr/ZVariables.c
@@ -35,10 +35,10 @@ char *ZGetVariable(var)
if (ret != ZERR_NONE)
return ret;
-#ifdef WIN32
+#ifdef _WIN32
varfile = g_strdup("C:\\zephyr\\zephyr.var");
#else
- varfile = g_strdup_printf("%s/zephyr.vars", CONFDIR);
+ varfile = g_build_filename(PURPLE_SYSCONFDIR, "zephyr.vars", NULL);
#endif
ret = get_varval(varfile, var);
g_free(varfile);
@@ -206,7 +206,7 @@ static int varline(bfr, var)
#define max(a,b) ((a > b) ? (a) : (b))
#endif
- if (g_ascii_strncasecmp(bfr, var, max(strlen(var), cp - bfr)))
+ if (g_ascii_strncasecmp(bfr, var, max(strlen(var), (gsize)(cp - bfr))))
return(0); /* var is not the var in
bfr ==> no match */
diff --git a/libpurple/protocols/zephyr/Zinternal.c b/libpurple/protocols/zephyr/Zinternal.c
index 8213bcbd94..d4e3e83b30 100644
--- a/libpurple/protocols/zephyr/Zinternal.c
+++ b/libpurple/protocols/zephyr/Zinternal.c
@@ -206,7 +206,7 @@ static struct _Z_InputQ *Z_SearchQueue(ZUnique_Id_t *uid, ZNotice_Kind_t kind)
if (ZCompareUID(uid, &qptr->uid) && qptr->kind == kind)
return (qptr);
next = qptr->next;
- if (qptr->timep && (qptr->timep+Z_NOTICETIMELIMIT < tv.tv_sec))
+ if (qptr->timep && ((time_t)qptr->timep+Z_NOTICETIMELIMIT < tv.tv_sec))
Z_RemQueue(qptr);
qptr = next;
}
@@ -654,7 +654,7 @@ Code_t Z_FormatAuthHeader(notice, buffer, buffer_len, len, cert_routine)
Code_t Z_FormatRawHeader(notice, buffer, buffer_len, len, cstart, cend)
ZNotice_t *notice;
char *buffer;
- int buffer_len;
+ gsize buffer_len;
int *len;
char **cstart, **cend;
{
diff --git a/libpurple/protocols/zephyr/internal.h b/libpurple/protocols/zephyr/internal.h
index 14c06b1703..0a0648d88a 100644
--- a/libpurple/protocols/zephyr/internal.h
+++ b/libpurple/protocols/zephyr/internal.h
@@ -105,7 +105,7 @@ 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*, int,
+Code_t Z_FormatRawHeader __P((ZNotice_t *, char*, gsize,
int*, char **, char **));
Code_t Z_ReadEnqueue __P((void));
Code_t Z_ReadWait __P((void));
diff --git a/libpurple/protocols/zephyr/zephyr.c b/libpurple/protocols/zephyr/zephyr.c
index 1e0fb5b385..059836618c 100644
--- a/libpurple/protocols/zephyr/zephyr.c
+++ b/libpurple/protocols/zephyr/zephyr.c
@@ -35,7 +35,6 @@
#include "server.h"
#include "util.h"
#include "cmds.h"
-#include "privacy.h"
#include "version.h"
#include "internal.h"
@@ -146,7 +145,7 @@ struct _zephyr_triple {
return TRUE;
#define z_call_s(func, err) if (func != ZERR_NONE) {\
- purple_connection_error(gc, err);\
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, err);\
return;\
}
@@ -347,7 +346,7 @@ static gchar *zephyr_recv_convert(PurpleConnection *gc, gchar *string)
{
gchar *utf8;
GError *err = NULL;
- zephyr_account *zephyr = gc->proto_data;
+ zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
if (g_utf8_validate(string, -1, NULL)) {
return g_strdup(string);
} else {
@@ -756,18 +755,20 @@ static void message_failed(PurpleConnection *gc, ZNotice_t *notice, struct socka
_("Unable to send to chat %s,%s,%s"),
notice->z_class, notice->z_class_inst,
notice->z_recipient);
- purple_notify_error(gc,"",chat_failed,NULL);
+ purple_notify_error(gc,"",chat_failed,NULL,
+ purple_request_cpar_from_connection(gc));
g_free(chat_failed);
} else {
purple_notify_error(gc, notice->z_recipient,
- _("User is offline"), NULL);
+ _("User is offline"), NULL,
+ purple_request_cpar_from_connection(gc));
}
}
static void handle_message(PurpleConnection *gc, ZNotice_t *notice_p)
{
ZNotice_t notice;
- zephyr_account* zephyr = gc->proto_data;
+ zephyr_account* zephyr = purple_connection_get_protocol_data(gc);
memcpy(&notice, notice_p, sizeof(notice)); /* TODO - use pointer? */
@@ -784,9 +785,9 @@ static void handle_message(PurpleConnection *gc, ZNotice_t *notice_p)
if (ZParseLocations(&notice, NULL, &nlocs, &user) != ZERR_NONE)
return;
- if ((b = purple_find_buddy(gc->account, user)) == NULL) {
+ if ((b = purple_blist_find_buddy(purple_connection_get_account(gc), user)) == NULL) {
char* stripped_user = zephyr_strip_local_realm(zephyr,user);
- b = purple_find_buddy(gc->account,stripped_user);
+ b = purple_blist_find_buddy(purple_connection_get_account(gc),stripped_user);
g_free(stripped_user);
}
@@ -798,20 +799,23 @@ static void handle_message(PurpleConnection *gc, ZNotice_t *notice_p)
char *tmp;
const char *balias;
- purple_notify_user_info_add_pair(user_info, _("User"), (b ? bname : user));
- balias = purple_buddy_get_local_buddy_alias(b);
+ /* 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, _("User"), (b ? bname : user));
+ balias = purple_buddy_get_local_alias(b);
if (b && balias)
- purple_notify_user_info_add_pair(user_info, _("Alias"), balias);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Alias"), balias);
if (!nlocs) {
- purple_notify_user_info_add_pair(user_info, NULL, _("Hidden or not logged-in"));
+ purple_notify_user_info_add_pair_plaintext(user_info, NULL, _("Hidden or not logged-in"));
}
for (; nlocs > 0; nlocs--) {
/* XXX add real error reporting */
ZGetLocations(&locs, &one);
+ /* TODO: Need to escape locs.host and locs.time? */
tmp = g_strdup_printf(_("<br>At %s since %s"), locs.host, locs.time);
- purple_notify_user_info_add_pair(user_info, _("Location"), tmp);
+ purple_notify_user_info_add_pair_html(user_info, _("Location"), tmp);
g_free(tmp);
}
purple_notify_userinfo(gc, (b ? bname : user),
@@ -819,9 +823,9 @@ static void handle_message(PurpleConnection *gc, ZNotice_t *notice_p)
purple_notify_user_info_destroy(user_info);
} else {
if (nlocs>0)
- purple_prpl_got_user_status(gc->account, b ? bname : user, "available", NULL);
+ purple_prpl_got_user_status(purple_connection_get_account(gc), b ? bname : user, "available", NULL);
else
- purple_prpl_got_user_status(gc->account, b ? bname : user, "offline", NULL);
+ purple_prpl_got_user_status(purple_connection_get_account(gc), b ? bname : user, "offline", NULL);
}
g_free(user);
@@ -829,8 +833,7 @@ static void handle_message(PurpleConnection *gc, ZNotice_t *notice_p)
} else {
char *buf, *buf2, *buf3;
char *send_inst;
- PurpleConversation *gconv1;
- PurpleConvChat *gcc;
+ PurpleChatConversation *gcc;
char *ptr = (char *) notice.z_message + (strlen(notice.z_message) + 1);
int len;
char *stripped_sender;
@@ -867,14 +870,14 @@ static void handle_message(PurpleConnection *gc, ZNotice_t *notice_p)
flags |= PURPLE_MESSAGE_AUTO_RESP;
if (!g_ascii_strcasecmp(notice.z_opcode,"PING"))
- serv_got_typing(gc,stripped_sender,ZEPHYR_TYPING_RECV_TIMEOUT, PURPLE_TYPING);
+ purple_serv_got_typing(gc,stripped_sender,ZEPHYR_TYPING_RECV_TIMEOUT, PURPLE_IM_TYPING);
else
- serv_got_im(gc, stripped_sender, buf3, flags, time(NULL));
+ purple_serv_got_im(gc, stripped_sender, buf3, flags, time(NULL));
} else {
zephyr_triple *zt1, *zt2;
gchar *send_inst_utf8;
- zephyr_account *zephyr = gc->proto_data;
+ zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
zt1 = new_triple(zephyr,notice.z_class, notice.z_class_inst, notice.z_recipient);
zt2 = find_sub_by_triple(zephyr,zt1);
if (!zt2) {
@@ -885,7 +888,7 @@ static void handle_message(PurpleConnection *gc, ZNotice_t *notice_p)
if (!zt2->open) {
zt2->open = TRUE;
- serv_got_joined_chat(gc, zt2->id, zt2->name);
+ purple_serv_got_joined_chat(gc, zt2->id, zt2->name);
zephyr_chat_set_topic(gc,zt2->id,notice.z_class_inst);
}
@@ -901,22 +904,22 @@ static void handle_message(PurpleConnection *gc, ZNotice_t *notice_p)
}
}
- gconv1 = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- zt2->name, gc->account);
- gcc = purple_conversation_get_chat_data(gconv1);
+ gcc = purple_conversations_find_chat_with_account(
+ zt2->name, purple_connection_get_account(gc));
#ifndef INET_ADDRSTRLEN
#define INET_ADDRSTRLEN 16
#endif
- if (!purple_conv_chat_find_user(gcc, stripped_sender)) {
+ if (!purple_chat_conversation_has_user(gcc, stripped_sender)) {
gchar ipaddr[INET_ADDRSTRLEN];
#ifdef HAVE_INET_NTOP
inet_ntop(AF_INET, &notice.z_sender_addr.s_addr, ipaddr, sizeof(ipaddr));
#else
memcpy(ipaddr,inet_ntoa(notice.z_sender_addr),sizeof(ipaddr));
#endif
- purple_conv_chat_add_user(gcc, stripped_sender, ipaddr, PURPLE_CBFLAGS_NONE, TRUE);
+ purple_chat_conversation_add_user(gcc, stripped_sender, ipaddr, PURPLE_CHAT_USER_NONE, TRUE);
}
- serv_got_chat_in(gc, zt2->id, send_inst_utf8, 0, buf3, time(NULL));
+ purple_serv_got_chat_in(gc, zt2->id, send_inst_utf8,
+ PURPLE_MESSAGE_RECV, buf3, time(NULL));
g_free(send_inst_utf8);
free_triple(zt1);
@@ -1087,7 +1090,7 @@ static parse_tree *read_from_tzc(zephyr_account* zephyr){
selected = 1;
if (read(zephyr->fromtzc[ZEPHYR_FD_READ], bufcur, 1) != 1) {
purple_debug_error("zephyr", "couldn't read\n");
- purple_connection_error(purple_account_get_connection(zephyr->account), "couldn't read");
+ purple_connection_error(purple_account_get_connection(zephyr->account), PURPLE_CONNECTION_ERROR_NETWORK_ERROR, "couldn't read");
free(buf);
return NULL;
}
@@ -1114,7 +1117,7 @@ static parse_tree *read_from_tzc(zephyr_account* zephyr){
static gint check_notify_tzc(gpointer data)
{
PurpleConnection *gc = (PurpleConnection *)data;
- zephyr_account* zephyr = gc->proto_data;
+ zephyr_account* zephyr = purple_connection_get_protocol_data(gc);
parse_tree *newparsetree = read_from_tzc(zephyr);
if (newparsetree != NULL) {
gchar *spewtype;
@@ -1161,15 +1164,15 @@ static gint check_notify_tzc(gpointer data)
gchar *locval;
user = tree_child(find_node(newparsetree,"user"),2)->contents;
- if ((b = purple_find_buddy(gc->account, user)) == NULL) {
+ if ((b = purple_blist_find_buddy(purple_connection_get_account(gc), user)) == NULL) {
gchar *stripped_user = zephyr_strip_local_realm(zephyr,user);
- b = purple_find_buddy(gc->account, stripped_user);
+ b = purple_blist_find_buddy(purple_connection_get_account(gc), stripped_user);
g_free(stripped_user);
}
locations = find_node(newparsetree,"locations");
locval = tree_child(tree_child(tree_child(tree_child(locations,2),0),0),2)->contents;
- if (!locval || !g_ascii_strcasecmp(locval," ") || (strlen(locval) == 0)) {
+ if (!locval || !g_ascii_strcasecmp(locval," ") || !*locval) {
nlocs = 0;
} else {
nlocs = 1;
@@ -1181,19 +1184,22 @@ static gint check_notify_tzc(gpointer data)
char *tmp;
const char *balias;
- purple_notify_user_info_add_pair(user_info, _("User"), (b ? bname : user));
+ /* 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, _("User"), (b ? bname : user));
- balias = b ? purple_buddy_get_local_buddy_alias(b) : NULL;
+ balias = b ? purple_buddy_get_local_alias(b) : NULL;
if (balias)
- purple_notify_user_info_add_pair(user_info, _("Alias"), balias);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Alias"), balias);
if (!nlocs) {
- purple_notify_user_info_add_pair(user_info, NULL, _("Hidden or not logged-in"));
+ purple_notify_user_info_add_pair_plaintext(user_info, NULL, _("Hidden or not logged-in"));
} else {
+ /* TODO: Need to escape the two strings that make up tmp? */
tmp = g_strdup_printf(_("<br>At %s since %s"),
tree_child(tree_child(tree_child(tree_child(locations,2),0),0),2)->contents,
tree_child(tree_child(tree_child(tree_child(locations,2),0),2),2)->contents);
- purple_notify_user_info_add_pair(user_info, _("Location"), tmp);
+ purple_notify_user_info_add_pair_html(user_info, _("Location"), tmp);
g_free(tmp);
}
@@ -1202,9 +1208,9 @@ static gint check_notify_tzc(gpointer data)
purple_notify_user_info_destroy(user_info);
} else {
if (nlocs>0)
- purple_prpl_got_user_status(gc->account, b ? bname : user, "available", NULL);
+ purple_prpl_got_user_status(purple_connection_get_account(gc), b ? bname : user, "available", NULL);
else
- purple_prpl_got_user_status(gc->account, b ? bname : user, "offline", NULL);
+ purple_prpl_got_user_status(purple_connection_get_account(gc), b ? bname : user, "offline", NULL);
}
}
else if (!g_ascii_strncasecmp(spewtype,"subscribed",10)) {
@@ -1269,12 +1275,12 @@ static gint check_loc(gpointer data)
GSList *buddies;
ZLocations_t locations;
PurpleConnection *gc = data;
- zephyr_account *zephyr = gc->proto_data;
+ zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
PurpleAccount *account = purple_connection_get_account(gc);
int numlocs;
int one = 1;
- for (buddies = purple_find_buddies(account, NULL); buddies;
+ for (buddies = purple_blist_find_buddies(account, NULL); buddies;
buddies = g_slist_delete_link(buddies, buddies)) {
PurpleBuddy *b = buddies->data;
char *chk;
@@ -1300,7 +1306,7 @@ static gint check_loc(gpointer data)
GSList *buddies;
ZAsyncLocateData_t ald;
PurpleConnection *gc = (PurpleConnection *)data;
- zephyr_account *zephyr = gc->proto_data;
+ zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
PurpleAccount *account = purple_connection_get_account(gc);
if (use_zeph02(zephyr)) {
@@ -1309,7 +1315,7 @@ static gint check_loc(gpointer data)
ald.version = NULL;
}
- for (buddies = purple_find_buddies(account, NULL); buddies;
+ for (buddies = purple_blist_find_buddies(account, NULL); buddies;
buddies = g_slist_delete_link(buddies, buddies)) {
PurpleBuddy *b = buddies->data;
@@ -1439,8 +1445,6 @@ static void process_zsubs(zephyr_account *zephyr)
char *tmp = g_strdup_printf("%s", zephyr->username);
char *atptr;
- z_class = triple[0];
- z_instance = triple[1];
if (triple[2] == NULL) {
recip = g_malloc0(1);
} else if (!g_ascii_strcasecmp(triple[2], "%me%")) {
@@ -1505,6 +1509,7 @@ static void process_zsubs(zephyr_account *zephyr)
}
fclose(f);
}
+ g_free(fname);
}
static void process_anyone(PurpleConnection *gc)
@@ -1515,7 +1520,7 @@ static void process_anyone(PurpleConnection *gc)
PurpleGroup *g;
PurpleBuddy *b;
- if (!(g = purple_find_group(_("Anyone")))) {
+ if (!(g = purple_blist_find_group(_("Anyone")))) {
g = purple_group_new(_("Anyone"));
purple_blist_add_group(g, NULL);
}
@@ -1525,11 +1530,11 @@ static void process_anyone(PurpleConnection *gc)
while (fgets(buff, BUFSIZ, fd)) {
strip_comments(buff);
if (buff[0]) {
- if (!purple_find_buddy(gc->account, buff)) {
+ if (!purple_blist_find_buddy(purple_connection_get_account(gc), buff)) {
char *stripped_user = zephyr_strip_local_realm(zephyr,buff);
purple_debug_info("zephyr","stripped_user %s\n",stripped_user);
- if (!purple_find_buddy(gc->account,stripped_user)){
- b = purple_buddy_new(gc->account, stripped_user, NULL);
+ if (!purple_blist_find_buddy(purple_connection_get_account(gc),stripped_user)) {
+ b = purple_buddy_new(purple_connection_get_account(gc), stripped_user, NULL);
purple_blist_add_buddy(b, NULL, g, NULL);
}
g_free(stripped_user);
@@ -1568,34 +1573,37 @@ static void zephyr_login(PurpleAccount * account)
gchar *exposure;
gc = purple_account_get_connection(account);
- read_anyone = purple_account_get_bool(gc->account,"read_anyone",TRUE);
- read_zsubs = purple_account_get_bool(gc->account,"read_zsubs",TRUE);
- exposure = (gchar *)purple_account_get_string(gc->account, "exposure_level", EXPOSE_REALMVIS);
+ read_anyone = purple_account_get_bool(purple_connection_get_account(gc),"read_anyone",TRUE);
+ read_zsubs = purple_account_get_bool(purple_connection_get_account(gc),"read_zsubs",TRUE);
+ exposure = (gchar *)purple_account_get_string(purple_connection_get_account(gc), "exposure_level", EXPOSE_REALMVIS);
#ifdef WIN32
username = purple_account_get_username(account);
#endif
- gc->flags |= PURPLE_CONNECTION_AUTO_RESP | PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_NO_BGCOLOR | PURPLE_CONNECTION_NO_URLDESC;
- gc->proto_data = zephyr=g_new0(zephyr_account,1);
+ purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_AUTO_RESP |
+ PURPLE_CONNECTION_FLAG_HTML | PURPLE_CONNECTION_FLAG_NO_BGCOLOR |
+ PURPLE_CONNECTION_FLAG_NO_URLDESC | PURPLE_CONNECTION_FLAG_NO_IMAGES);
+ zephyr = g_new0(zephyr_account, 1);
+ purple_connection_set_protocol_data(gc, zephyr);
zephyr->account = account;
/* Make sure that the exposure (visibility) is set to a sane value */
zephyr->exposure=g_strdup(normalize_zephyr_exposure(exposure));
- if (purple_account_get_bool(gc->account,"use_tzc",0)) {
+ if (purple_account_get_bool(purple_connection_get_account(gc),"use_tzc",0)) {
zephyr->connection_type = PURPLE_ZEPHYR_TZC;
} else {
zephyr->connection_type = PURPLE_ZEPHYR_KRB4;
}
- zephyr->encoding = (char *)purple_account_get_string(gc->account, "encoding", ZEPHYR_FALLBACK_CHARSET);
+ zephyr->encoding = (char *)purple_account_get_string(purple_connection_get_account(gc), "encoding", ZEPHYR_FALLBACK_CHARSET);
purple_connection_update_progress(gc, _("Connecting"), 0, 8);
/* XXX z_call_s should actually try to report the com_err determined error */
if (use_tzc(zephyr)) {
pid_t pid;
- /* purple_connection_error(gc,"tzc not supported yet"); */
+ /* purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, "tzc not supported yet"); */
if ((pipe(zephyr->totzc) != 0) || (pipe(zephyr->fromtzc) != 0)) {
purple_debug_error("zephyr", "pipe creation failed. killing\n");
exit(-1);
@@ -1610,7 +1618,7 @@ static void zephyr_login(PurpleAccount * account)
if (pid == 0) {
unsigned int i=0;
gboolean found_ps = FALSE;
- gchar ** tzc_cmd_array = g_strsplit(purple_account_get_string(gc->account,"tzc_command","/usr/bin/tzc -e %s")," ",0);
+ gchar ** tzc_cmd_array = g_strsplit(purple_account_get_string(purple_connection_get_account(gc),"tzc_command","/usr/bin/tzc -e %s")," ",0);
if (close(1) == -1) {
exit(-1);
}
@@ -1693,7 +1701,7 @@ static void zephyr_login(PurpleAccount * account)
select(zephyr->fromtzc[ZEPHYR_FD_READ] + 1, &rfds, NULL, NULL, &tv) > 0) {
if (read(zephyr->fromtzc[ZEPHYR_FD_READ], bufcur, 1) != 1) {
purple_debug_error("zephyr", "couldn't read\n");
- purple_connection_error(gc, "couldn't read");
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, "couldn't read");
free(buf);
return;
}
@@ -1721,7 +1729,7 @@ static void zephyr_login(PurpleAccount * account)
ptr++;
}
if (ptr >=bufcur) {
- purple_connection_error(gc,"invalid output by tzc (or bad parsing code)");
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, "invalid output by tzc (or bad parsing code)");
free(buf);
return;
}
@@ -1787,7 +1795,7 @@ static void zephyr_login(PurpleAccount * account)
if ((realm = strchr(username,'@')))
zephyr->realm = g_strdup_printf("%s",realm+1);
else {
- realm = (gchar *)purple_account_get_string(gc->account,"realm","");
+ realm = (gchar *)purple_account_get_string(purple_connection_get_account(gc),"realm","");
if (!*realm) {
realm = "local-realm";
}
@@ -1824,7 +1832,7 @@ static void zephyr_login(PurpleAccount * account)
z_call_s(ZOpenPort(&(zephyr->port)), "Couldn't open port");
z_call_s(ZSetLocation((char *)zephyr->exposure), "Couldn't set location");
- realm = (gchar *)purple_account_get_string(gc->account,"realm","");
+ realm = (gchar *)purple_account_get_string(purple_connection_get_account(gc),"realm","");
if (!*realm) {
realm = ZGetRealm();
}
@@ -1836,7 +1844,7 @@ static void zephyr_login(PurpleAccount * account)
purple_debug_info("zephyr","realm: %s\n",zephyr->realm);
}
else {
- purple_connection_error(gc,"Only ZEPH0.2 supported currently");
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, "Only ZEPH0.2 supported currently");
return;
}
purple_debug_info("zephyr","does it get here\n");
@@ -1850,12 +1858,13 @@ static void zephyr_login(PurpleAccount * account)
if (zephyr_subscribe_to(zephyr,"MESSAGE","PERSONAL",zephyr->username,NULL) != ZERR_NONE) {
/* XXX don't translate this yet. It could be written better */
/* XXX error messages could be handled with more detail */
- purple_notify_error(account->gc, NULL,
- "Unable to subscribe to messages", "Unable to subscribe to initial messages");
+ purple_notify_error(purple_account_get_connection(account), NULL,
+ "Unable to subscribe to messages", "Unable to subscribe to initial messages",
+ purple_request_cpar_from_connection(gc));
return;
}
- purple_connection_set_state(gc, PURPLE_CONNECTED);
+ purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
if (read_anyone)
process_anyone(gc);
@@ -1954,7 +1963,7 @@ static void write_anyone(zephyr_account *zephyr)
}
account = zephyr->account;
- for (buddies = purple_find_buddies(account, NULL); buddies;
+ for (buddies = purple_blist_find_buddies(account, NULL); buddies;
buddies = g_slist_delete_link(buddies, buddies)) {
PurpleBuddy *b = buddies->data;
gchar *stripped_user = zephyr_strip_local_realm(zephyr, purple_buddy_get_name(b));
@@ -1970,7 +1979,7 @@ static void zephyr_close(PurpleConnection * gc)
{
GList *l;
GSList *s;
- zephyr_account *zephyr = gc->proto_data;
+ zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
pid_t tzc_pid = zephyr->tzc_pid;
l = zephyr->pending_zloc_names;
@@ -1980,10 +1989,10 @@ static void zephyr_close(PurpleConnection * gc)
}
g_list_free(zephyr->pending_zloc_names);
- if (purple_account_get_bool(gc->account, "write_anyone", FALSE))
+ if (purple_account_get_bool(purple_connection_get_account(gc), "write_anyone", FALSE))
write_anyone(zephyr);
- if (purple_account_get_bool(gc->account, "write_zsubs", FALSE))
+ if (purple_account_get_bool(purple_connection_get_account(gc), "write_zsubs", FALSE))
write_zsubs(zephyr);
s = zephyr->subscrips;
@@ -2037,15 +2046,14 @@ static const char * zephyr_get_signature(void)
return sig;
}
-static int zephyr_chat_send(PurpleConnection * gc, int id, const char *im, PurpleMessageFlags flags)
+static int zephyr_chat_send(PurpleConnection * gc, int id, PurpleMessage *msg)
{
zephyr_triple *zt;
const char *sig;
- PurpleConversation *gconv1;
- PurpleConvChat *gcc;
+ PurpleChatConversation *gcc;
char *inst;
char *recipient;
- zephyr_account *zephyr = gc->proto_data;
+ zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
zt = find_sub_by_id(zephyr,id);
if (!zt)
@@ -2054,11 +2062,10 @@ static int zephyr_chat_send(PurpleConnection * gc, int id, const char *im, Purpl
sig = zephyr_get_signature();
- gconv1 = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, zt->name,
- gc->account);
- gcc = purple_conversation_get_chat_data(gconv1);
+ gcc = purple_conversations_find_chat_with_account(zt->name,
+ purple_connection_get_account(gc));
- if (!(inst = (char *)purple_conv_chat_get_topic(gcc)))
+ if (!(inst = (char *)purple_chat_conversation_get_topic(gcc)))
inst = g_strdup("PERSONAL");
if (!g_ascii_strcasecmp(zt->recipient, "*"))
@@ -2066,21 +2073,25 @@ static int zephyr_chat_send(PurpleConnection * gc, int id, const char *im, Purpl
else
recipient = local_zephyr_normalize(zephyr,zt->recipient);
- zephyr_send_message(zephyr,zt->class,inst,recipient,im,sig,"");
+ zephyr_send_message(zephyr, zt->class, inst, recipient,
+ purple_message_get_contents(msg), sig, "");
return 0;
}
-static int zephyr_send_im(PurpleConnection * gc, const char *who, const char *im, PurpleMessageFlags flags)
+static int zephyr_send_im(PurpleConnection *gc, PurpleMessage *msg)
{
const char *sig;
- zephyr_account *zephyr = gc->proto_data;
- if (flags & PURPLE_MESSAGE_AUTO_RESP)
+ zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
+
+ if (purple_message_get_flags(msg) & PURPLE_MESSAGE_AUTO_RESP) {
sig = "Automated reply:";
- else {
+ } else {
sig = zephyr_get_signature();
}
- zephyr_send_message(zephyr,"MESSAGE","PERSONAL",local_zephyr_normalize(zephyr,who),im,sig,"");
+ zephyr_send_message(zephyr, "MESSAGE", "PERSONAL",
+ local_zephyr_normalize(zephyr, purple_message_get_recipient(msg)),
+ purple_message_get_contents(msg), sig, "");
return 1;
}
@@ -2090,11 +2101,10 @@ static int zephyr_send_im(PurpleConnection * gc, const char *who, const char *im
static char* zephyr_tzc_escape_msg(const char *message)
{
- int pos = 0;
- int pos2 = 0;
+ gsize pos = 0, pos2 = 0;
char *newmsg;
- if (message && (strlen(message) > 0)) {
+ if (message && *message) {
newmsg = g_new0(char,1+strlen(message)*2);
while(pos < strlen(message)) {
if (message[pos]=='\\') {
@@ -2122,11 +2132,10 @@ static char* zephyr_tzc_escape_msg(const char *message)
char* zephyr_tzc_deescape_str(const char *message)
{
- int pos = 0;
- int pos2 = 0;
+ gsize pos = 0, pos2 = 0;
char *newmsg;
- if (message && (strlen(message) > 0)) {
+ if (message && *message) {
newmsg = g_new0(char,strlen(message)+1);
while(pos < strlen(message)) {
if (message[pos]=='\\') {
@@ -2174,11 +2183,15 @@ static int zephyr_send_message(zephyr_account *zephyr,char* zclass, char* instan
len = strlen(zsendstr);
result = write(zephyr->totzc[ZEPHYR_FD_WRITE], zsendstr, len);
if (result != len) {
+ g_free(tzc_sig);
+ g_free(tzc_body);
g_free(zsendstr);
g_free(html_buf2);
g_free(html_buf);
return errno;
}
+ g_free(tzc_sig);
+ g_free(tzc_body);
g_free(zsendstr);
} else if (use_zeph02(zephyr)) {
ZNotice_t notice;
@@ -2239,11 +2252,17 @@ static const char *zephyr_normalize(const PurpleAccount *account, const char *wh
PurpleConnection *gc;
char *tmp;
+ if (account == NULL) {
+ if (strlen(who) >= sizeof(buf))
+ return NULL;
+ return who;
+ }
+
gc = purple_account_get_connection(account);
if (gc == NULL)
return NULL;
- tmp = local_zephyr_normalize(gc->proto_data, who);
+ tmp = local_zephyr_normalize(purple_connection_get_protocol_data(gc), who);
if (strlen(tmp) >= sizeof(buf)) {
g_free(tmp);
@@ -2259,7 +2278,7 @@ static const char *zephyr_normalize(const PurpleAccount *account, const char *wh
static void zephyr_zloc(PurpleConnection *gc, const char *who)
{
ZAsyncLocateData_t ald;
- zephyr_account *zephyr = gc->proto_data;
+ zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
gchar* normalized_who = local_zephyr_normalize(zephyr,who);
if (use_zeph02(zephyr)) {
@@ -2286,8 +2305,9 @@ static void zephyr_zloc(PurpleConnection *gc, const char *who)
static void zephyr_set_status(PurpleAccount *account, PurpleStatus *status) {
size_t len;
size_t result;
- zephyr_account *zephyr = purple_account_get_connection(account)->proto_data;
- PurpleStatusPrimitive primitive = purple_status_type_get_primitive(purple_status_get_type(status));
+ PurpleConnection *gc = purple_account_get_connection(account);
+ zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
+ PurpleStatusPrimitive primitive = purple_status_type_get_primitive(purple_status_get_status_type(status));
if (zephyr->away) {
g_free(zephyr->away);
@@ -2354,7 +2374,7 @@ static GList *zephyr_status_types(PurpleAccount *account)
type = purple_status_type_new_with_attrs(
PURPLE_STATUS_AWAY, NULL, NULL, TRUE, TRUE, FALSE,
- "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_append(types, type);
@@ -2395,7 +2415,7 @@ static GList *zephyr_chat_info(PurpleConnection * gc)
static void zephyr_subscribe_failed(PurpleConnection *gc,char * z_class, char *z_instance, char * z_recipient, char* z_galaxy)
{
gchar* subscribe_failed = g_strdup_printf(_("Attempt to subscribe to %s,%s,%s failed"), z_class, z_instance,z_recipient);
- purple_notify_error(gc,"", subscribe_failed, NULL);
+ purple_notify_error(gc,"", subscribe_failed, NULL, purple_request_cpar_from_connection(gc));
g_free(subscribe_failed);
}
@@ -2420,7 +2440,7 @@ static void zephyr_join_chat(PurpleConnection * gc, GHashTable * data)
const char *classname;
const char *instname;
const char *recip;
- zephyr_account *zephyr=gc->proto_data;
+ zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
classname = g_hash_table_lookup(data, "class");
instname = g_hash_table_lookup(data, "instance");
recip = g_hash_table_lookup(data, "recipient");
@@ -2454,7 +2474,7 @@ static void zephyr_join_chat(PurpleConnection * gc, GHashTable * data)
if (!zt2->open) {
if (!g_ascii_strcasecmp(instname,"*"))
instname = "PERSONAL";
- serv_got_joined_chat(gc, zt2->id, zt2->name);
+ purple_serv_got_joined_chat(gc, zt2->id, zt2->name);
zephyr_chat_set_topic(gc,zt2->id,instname);
zt2->open = TRUE;
}
@@ -2474,7 +2494,7 @@ static void zephyr_join_chat(PurpleConnection * gc, GHashTable * data)
zephyr->subscrips = g_slist_append(zephyr->subscrips, zt1);
zt1->open = TRUE;
- serv_got_joined_chat(gc, zt1->id, zt1->name);
+ purple_serv_got_joined_chat(gc, zt1->id, zt1->name);
if (!g_ascii_strcasecmp(instname,"*"))
instname = "PERSONAL";
zephyr_chat_set_topic(gc,zt1->id,instname);
@@ -2483,7 +2503,7 @@ static void zephyr_join_chat(PurpleConnection * gc, GHashTable * data)
static void zephyr_chat_leave(PurpleConnection * gc, int id)
{
zephyr_triple *zt;
- zephyr_account *zephyr = gc->proto_data;
+ zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
zt = find_sub_by_id(zephyr,id);
if (zt) {
@@ -2506,7 +2526,7 @@ static PurpleChat *zephyr_find_blist_chat(PurpleAccount *account, const char *na
char *zclass, *inst, *recip;
char** triple;
GHashTable *components;
- if(!PURPLE_BLIST_NODE_IS_CHAT(cnode))
+ if(!PURPLE_IS_CHAT(cnode))
continue;
if(purple_chat_get_account(chat) != account)
continue;
@@ -2531,13 +2551,13 @@ static const char *zephyr_list_icon(PurpleAccount * a, PurpleBuddy * b)
return "zephyr";
}
-static unsigned int zephyr_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state) {
+static unsigned int zephyr_send_typing(PurpleConnection *gc, const char *who, PurpleIMTypingState state) {
gchar *recipient;
- zephyr_account *zephyr = gc->proto_data;
+ zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
if (use_tzc(zephyr))
return 0;
- if (state == PURPLE_NOT_TYPING)
+ if (state == PURPLE_IM_NOT_TYPING)
return 0;
/* XXX We probably should care if this fails. Or maybe we don't want to */
@@ -2561,7 +2581,7 @@ static unsigned int zephyr_send_typing(PurpleConnection *gc, const char *who, Pu
/*
* TODO: Is this correct? It means we will call
- * serv_send_typing(gc, who, PURPLE_TYPING) once every 15 seconds
+ * purple_serv_send_typing(gc, who, PURPLE_IM_TYPING) once every 15 seconds
* until the Purple user stops typing.
*/
return ZEPHYR_TYPING_SEND_TIMEOUT;
@@ -2572,22 +2592,20 @@ static unsigned int zephyr_send_typing(PurpleConnection *gc, const char *who, Pu
static void zephyr_chat_set_topic(PurpleConnection * gc, int id, const char *topic)
{
zephyr_triple *zt;
- PurpleConversation *gconv;
- PurpleConvChat *gcc;
+ PurpleChatConversation *gcc;
gchar *topic_utf8;
- zephyr_account* zephyr = gc->proto_data;
+ zephyr_account* zephyr = purple_connection_get_protocol_data(gc);
char *sender = (char *)zephyr->username;
zt = find_sub_by_id(zephyr,id);
/* find_sub_by_id can return NULL */
if (!zt)
return;
- gconv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, zt->name,
- gc->account);
- gcc = purple_conversation_get_chat_data(gconv);
+ gcc = purple_conversations_find_chat_with_account(zt->name,
+ purple_connection_get_account(gc));
topic_utf8 = zephyr_recv_convert(gc,(gchar *)topic);
- purple_conv_chat_set_topic(gcc,sender,topic_utf8);
+ purple_chat_conversation_set_topic(gcc,sender,topic_utf8);
g_free(topic_utf8);
return;
}
@@ -2598,25 +2616,31 @@ static PurpleCmdRet zephyr_purple_cmd_msg(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
char *recipient;
- zephyr_account *zephyr = purple_conversation_get_gc(conv)->proto_data;
+ PurpleCmdRet ret;
+ PurpleConnection *gc = purple_conversation_get_connection(conv);
+ zephyr_account *zephyr = purple_connection_get_protocol_data(gc);;
if (!g_ascii_strcasecmp(args[0],"*"))
return PURPLE_CMD_RET_FAILED; /* "*" is not a valid argument */
else
recipient = local_zephyr_normalize(zephyr,args[0]);
- if (strlen(recipient) < 1)
+ if (strlen(recipient) < 1) {
+ g_free(recipient);
return PURPLE_CMD_RET_FAILED; /* a null recipient is a chat message, not an IM */
+ }
if (zephyr_send_message(zephyr,"MESSAGE","PERSONAL",recipient,args[1],zephyr_get_signature(),""))
- return PURPLE_CMD_RET_OK;
+ ret = PURPLE_CMD_RET_OK;
else
- return PURPLE_CMD_RET_FAILED;
+ ret = PURPLE_CMD_RET_FAILED;
+ g_free(recipient);
+ return ret;
}
static PurpleCmdRet zephyr_purple_cmd_zlocate(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
- zephyr_zloc(purple_conversation_get_gc(conv),args[0]);
+ zephyr_zloc(purple_conversation_get_connection(conv),args[0]);
return PURPLE_CMD_RET_OK;
}
@@ -2627,10 +2651,9 @@ static PurpleCmdRet zephyr_purple_cmd_instance(PurpleConversation *conv,
* all. This might not be the best thing to do, though having
* one word isn't ideal either. */
- PurpleConvChat *gcc = purple_conversation_get_chat_data(conv);
- int id = gcc->id;
const char* instance = args[0];
- zephyr_chat_set_topic(purple_conversation_get_gc(conv),id,instance);
+ zephyr_chat_set_topic(purple_conversation_get_connection(conv),
+ purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv)),instance);
return PURPLE_CMD_RET_OK;
}
@@ -2642,7 +2665,7 @@ static PurpleCmdRet zephyr_purple_cmd_joinchat_cir(PurpleConversation *conv,
g_hash_table_insert(triple,"class",args[0]);
g_hash_table_insert(triple,"instance",args[1]);
g_hash_table_insert(triple,"recipient",args[2]);
- zephyr_join_chat(purple_conversation_get_gc(conv),triple);
+ zephyr_join_chat(purple_conversation_get_connection(conv),triple);
return PURPLE_CMD_RET_OK;
}
@@ -2650,7 +2673,8 @@ static PurpleCmdRet zephyr_purple_cmd_zi(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
/* args = instance, message */
- zephyr_account *zephyr = purple_conversation_get_gc(conv)->proto_data;
+ PurpleConnection *gc = purple_conversation_get_connection(conv);
+ zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
if ( zephyr_send_message(zephyr,"message",args[0],"",args[1],zephyr_get_signature(),""))
return PURPLE_CMD_RET_OK;
else
@@ -2661,7 +2685,8 @@ static PurpleCmdRet zephyr_purple_cmd_zci(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
/* args = class, instance, message */
- zephyr_account *zephyr = purple_conversation_get_gc(conv)->proto_data;
+ PurpleConnection *gc = purple_conversation_get_connection(conv);
+ zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
if ( zephyr_send_message(zephyr,args[0],args[1],"",args[2],zephyr_get_signature(),""))
return PURPLE_CMD_RET_OK;
else
@@ -2672,7 +2697,8 @@ static PurpleCmdRet zephyr_purple_cmd_zcir(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
/* args = class, instance, recipient, message */
- zephyr_account *zephyr = purple_conversation_get_gc(conv)->proto_data;
+ PurpleConnection *gc = purple_conversation_get_connection(conv);
+ zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
if ( zephyr_send_message(zephyr,args[0],args[1],args[2],args[3],zephyr_get_signature(),""))
return PURPLE_CMD_RET_OK;
else
@@ -2683,7 +2709,8 @@ static PurpleCmdRet zephyr_purple_cmd_zir(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
/* args = instance, recipient, message */
- zephyr_account *zephyr = purple_conversation_get_gc(conv)->proto_data;
+ PurpleConnection *gc = purple_conversation_get_connection(conv);
+ zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
if ( zephyr_send_message(zephyr,"message",args[0],args[1],args[2],zephyr_get_signature(),""))
return PURPLE_CMD_RET_OK;
else
@@ -2694,7 +2721,8 @@ static PurpleCmdRet zephyr_purple_cmd_zc(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
/* args = class, message */
- zephyr_account *zephyr = purple_conversation_get_gc(conv)->proto_data;
+ PurpleConnection *gc = purple_conversation_get_connection(conv);
+ zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
if ( zephyr_send_message(zephyr,args[0],"PERSONAL","",args[1],zephyr_get_signature(),""))
return PURPLE_CMD_RET_OK;
else
@@ -2775,7 +2803,7 @@ static int zephyr_resubscribe(PurpleConnection *gc)
{
/* Resubscribe to the in-memory list of subscriptions and also
unsubscriptions*/
- zephyr_account *zephyr = gc->proto_data;
+ zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
GSList *s = zephyr->subscrips;
zephyr_triple *zt;
while (s) {
@@ -2800,7 +2828,7 @@ static void zephyr_action_resubscribe(PurplePluginAction *action)
static void zephyr_action_get_subs_from_server(PurplePluginAction *action)
{
PurpleConnection *gc = (PurpleConnection *) action->context;
- zephyr_account *zephyr = gc->proto_data;
+ zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
gchar *title;
int retval, nsubs, one,i;
ZSubscription_t subs;
@@ -2810,10 +2838,12 @@ static void zephyr_action_get_subs_from_server(PurplePluginAction *action)
title = g_strdup_printf("Server subscriptions for %s", zephyr->username);
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;
@@ -2822,6 +2852,7 @@ static void zephyr_action_get_subs_from_server(PurplePluginAction *action)
one = 1;
if ((retval = ZGetSubscriptions(&subs,&one)) != ZERR_NONE) {
/* XXX better error handling */
+ g_free(title);
purple_debug_error("zephyr", "error while retrieving individual subscription\n");
return;
}
@@ -2830,9 +2861,12 @@ static void zephyr_action_get_subs_from_server(PurplePluginAction *action)
subs.zsub_recipient);
}
purple_notify_formatted(gc, title, title, NULL, subout->str, NULL, NULL);
+ g_free(title);
+ g_string_free(subout, TRUE);
} else {
/* XXX fix */
- purple_notify_error(gc,"","tzc doesn't support this action",NULL);
+ purple_notify_error(gc, "", "tzc doesn't support this action",
+ NULL, purple_request_cpar_from_connection(gc));
}
}
@@ -2854,6 +2888,7 @@ static GList *zephyr_actions(PurplePlugin *plugin, gpointer context)
static PurplePlugin *my_protocol = NULL;
static PurplePluginProtocolInfo prpl_info = {
+ sizeof(PurplePluginProtocolInfo), /* struct_size */
OPT_PROTO_CHAT_TOPIC | OPT_PROTO_NO_PASSWORD,
NULL, /* ??? user_splits */
NULL, /* ??? protocol_options */
@@ -2889,12 +2924,10 @@ static PurplePluginProtocolInfo prpl_info = {
zephyr_get_chat_name, /* get_chat_name */
NULL, /* chat_invite -- No chat invites*/
zephyr_chat_leave, /* chat_leave */
- NULL, /* chat_whisper -- No "whispering"*/
zephyr_chat_send, /* chat_send */
NULL, /* keepalive -- Not necessary*/
NULL, /* register_user -- Not supported*/
NULL, /* XXX get_cb_info */
- NULL, /* get_cb_away */
NULL, /* alias_buddy */
NULL, /* group_buddy */
NULL, /* rename_group */
@@ -2920,15 +2953,13 @@ static PurplePluginProtocolInfo prpl_info = {
NULL,
NULL,
NULL,
- sizeof(PurplePluginProtocolInfo), /* struct_size */
NULL, /* get_account_text_table */
NULL, /* initate_media */
NULL, /* get_media_caps */
NULL, /* get_moods */
NULL, /* set_public_alias */
NULL, /* get_public_alias */
- NULL, /* add_buddy_with_invite */
- NULL /* add_buddies_with_invite */
+ NULL /* get_max_message_size */
};
static PurplePluginInfo info = {
diff --git a/libpurple/proxy.c b/libpurple/proxy.c
index 153462b3b1..5650b9b394 100644
--- a/libpurple/proxy.c
+++ b/libpurple/proxy.c
@@ -1,8 +1,3 @@
-/**
- * @file proxy.c Proxy API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -32,15 +27,26 @@
#define _PURPLE_PROXY_C_
#include "internal.h"
-#include "cipher.h"
+#include "ciphers/md5hash.h"
#include "debug.h"
#include "dnsquery.h"
+#include "http.h"
#include "notify.h"
#include "ntlm.h"
#include "prefs.h"
#include "proxy.h"
#include "util.h"
+struct _PurpleProxyInfo
+{
+ PurpleProxyType type; /* The proxy type. */
+
+ char *host; /* The host. */
+ int port; /* The port number. */
+ char *username; /* The username. */
+ char *password; /* The password. */
+};
+
struct _PurpleProxyConnectData {
void *handle;
PurpleProxyConnectFunction connect_cb;
@@ -53,7 +59,7 @@ struct _PurpleProxyConnectData {
PurpleProxyInfo *gpi;
PurpleDnsQueryData *query_data;
- /**
+ /*
* This contains alternating length/char* values. The char*
* values need to be freed when removed from the linked list.
*/
@@ -121,7 +127,7 @@ purple_proxy_info_destroy(PurpleProxyInfo *info)
}
void
-purple_proxy_info_set_type(PurpleProxyInfo *info, PurpleProxyType type)
+purple_proxy_info_set_proxy_type(PurpleProxyInfo *info, PurpleProxyType type)
{
g_return_if_fail(info != NULL);
@@ -164,7 +170,7 @@ purple_proxy_info_set_password(PurpleProxyInfo *info, const char *password)
}
PurpleProxyType
-purple_proxy_info_get_type(const PurpleProxyInfo *info)
+purple_proxy_info_get_proxy_type(const PurpleProxyInfo *info)
{
g_return_val_if_fail(info != NULL, PURPLE_PROXY_NONE);
@@ -247,14 +253,15 @@ static const char* gproxycmds[][2] = {
{ "gconftool-2 -g /system/http_proxy/authentication_password", "gsettings get org.gnome.system.proxy.http authentication-password" },
};
-/**
+/*
+ * purple_gnome_proxy_get_parameter:
+ * @parameter: One of the GNOME_PROXY_x constants defined above
+ * @gnome_version: GNOME2_CMDS or GNOME3_CMDS
+ *
* This is a utility function used to retrieve proxy parameter values from
* GNOME 2/3 environment.
*
- * @param parameter One of the GNOME_PROXY_x constants defined above
- * @param gnome_version GNOME2_CMDS or GNOME3_CMDS
- *
- * @return The value of requested proxy parameter
+ * Returns: The value of requested proxy parameter
*/
static char *
purple_gnome_proxy_get_parameter(guint8 parameter, guint8 gnome_version)
@@ -500,7 +507,7 @@ purple_win32_proxy_get_info(void)
} else
specific = proxy_list;
- purple_proxy_info_set_type(&info, PURPLE_PROXY_HTTP);
+ purple_proxy_info_set_proxy_type(&info, PURPLE_PROXY_HTTP);
_proxy_fill_hostinfo(&info, specific, 80);
/* TODO: is there a way to set the username/password? */
purple_proxy_info_set_username(&info, NULL);
@@ -518,7 +525,7 @@ purple_win32_proxy_get_info(void)
*tmp = '\0';
/* specific now points the proxy server (and port) */
- purple_proxy_info_set_type(&info, PURPLE_PROXY_SOCKS5);
+ purple_proxy_info_set_proxy_type(&info, PURPLE_PROXY_SOCKS5);
_proxy_fill_hostinfo(&info, specific, 1080);
/* TODO: is there a way to set the username/password? */
purple_proxy_info_set_username(&info, NULL);
@@ -532,7 +539,7 @@ purple_win32_proxy_get_info(void)
purple_debug_info("proxy", "Windows Proxy Settings: No supported proxy specified.\n");
- purple_proxy_info_set_type(&info, PURPLE_PROXY_NONE);
+ purple_proxy_info_set_proxy_type(&info, PURPLE_PROXY_NONE);
}
}
@@ -542,7 +549,7 @@ purple_win32_proxy_get_info(void)
g_free(proxy_list);
} else {
purple_debug_info("proxy", "No Windows proxy set.\n");
- purple_proxy_info_set_type(&info, PURPLE_PROXY_NONE);
+ purple_proxy_info_set_proxy_type(&info, PURPLE_PROXY_NONE);
}
if (ie_proxy_config.lpszAutoConfigUrl)
@@ -561,7 +568,7 @@ purple_win32_proxy_get_info(void)
* Proxy API
**************************************************************************/
-/**
+/*
* Whoever calls this needs to have called
* purple_proxy_connect_data_disconnect() beforehand.
*/
@@ -586,7 +593,13 @@ purple_proxy_connect_data_destroy(PurpleProxyConnectData *connect_data)
g_free(connect_data);
}
-/**
+/*
+ * purple_proxy_connect_data_disconnect:
+ * @error_message: An error message explaining why the connection
+ * failed. This will be passed to the callback function
+ * specified in the call to purple_proxy_connect(). If the
+ * connection was successful then pass in null.
+ *
* Free all information dealing with a connection attempt and
* reset the connect_data to prepare for it to try to connect
* to another IP address.
@@ -597,11 +610,6 @@ purple_proxy_connect_data_destroy(PurpleProxyConnectData *connect_data)
* If the connection attempt failed and we have no more hosts
* try try then we call the callback with the given error message,
* then destroy the connect_data.
- *
- * @param error_message An error message explaining why the connection
- * failed. This will be passed to the callback function
- * specified in the call to purple_proxy_connect(). If the
- * connection was successful then pass in null.
*/
static void
purple_proxy_connect_data_disconnect(PurpleProxyConnectData *connect_data, const gchar *error_message)
@@ -645,7 +653,7 @@ purple_proxy_connect_data_disconnect(PurpleProxyConnectData *connect_data, const
}
}
-/**
+/*
* This calls purple_proxy_connect_data_disconnect(), but it lets you
* specify the error_message using a printf()-like syntax.
*/
@@ -740,12 +748,12 @@ clean_connect(gpointer data)
}
static void
-proxy_connect_udp_none(PurpleProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
+proxy_connect_udp_none(PurpleProxyConnectData *connect_data, common_sockaddr_t *addr, socklen_t addrlen)
{
purple_debug_info("proxy", "UDP Connecting to %s:%d with no proxy\n",
connect_data->host, connect_data->port);
- connect_data->fd = socket(addr->sa_family, SOCK_DGRAM, 0);
+ connect_data->fd = socket(addr->sa.sa_family, SOCK_DGRAM, 0);
if (connect_data->fd < 0)
{
purple_proxy_connect_data_disconnect_formatted(connect_data,
@@ -754,11 +762,11 @@ proxy_connect_udp_none(PurpleProxyConnectData *connect_data, struct sockaddr *ad
}
_purple_network_set_common_socket_flags(connect_data->fd);
- if (connect(connect_data->fd, addr, addrlen) != 0)
+ if (connect(connect_data->fd, &addr->sa, addrlen) != 0)
{
if ((errno == EINPROGRESS) || (errno == EINTR))
{
- purple_debug_info("proxy", "UDP Connection in progress\n");
+ purple_debug_info("proxy", "UDP connection in progress\n");
connect_data->inpa = purple_input_add(connect_data->fd,
PURPLE_INPUT_WRITE, socket_ready_cb, connect_data);
}
@@ -795,12 +803,12 @@ proxy_connect_udp_none(PurpleProxyConnectData *connect_data, struct sockaddr *ad
}
static void
-proxy_connect_none(PurpleProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
+proxy_connect_none(PurpleProxyConnectData *connect_data, common_sockaddr_t *addr, socklen_t addrlen)
{
purple_debug_info("proxy", "Connecting to %s:%d with no proxy\n",
connect_data->host, connect_data->port);
- connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0);
+ connect_data->fd = socket(addr->sa.sa_family, SOCK_STREAM, 0);
if (connect_data->fd < 0)
{
purple_proxy_connect_data_disconnect_formatted(connect_data,
@@ -809,7 +817,7 @@ proxy_connect_none(PurpleProxyConnectData *connect_data, struct sockaddr *addr,
}
_purple_network_set_common_socket_flags(connect_data->fd);
- if (connect(connect_data->fd, addr, addrlen) != 0)
+ if (connect(connect_data->fd, &addr->sa, addrlen) != 0)
{
if ((errno == EINPROGRESS) || (errno == EINTR))
{
@@ -849,7 +857,7 @@ proxy_connect_none(PurpleProxyConnectData *connect_data, struct sockaddr *addr,
}
}
-/**
+/*
* This is a utility function used by the HTTP, SOCKS4 and SOCKS5
* connect functions. It writes data from a buffer to a socket.
* When all the data is written it sets up a watcher to read a
@@ -878,7 +886,7 @@ proxy_do_write(gpointer data, gint source, PurpleInputCondition cond)
purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
return;
}
- if (ret < request_len) {
+ if ((gsize)ret < request_len) {
connect_data->written_len += ret;
return;
}
@@ -891,10 +899,7 @@ proxy_do_write(gpointer data, gint source, PurpleInputCondition cond)
PURPLE_INPUT_READ, connect_data->read_cb, connect_data);
}
-#define HTTP_GOODSTRING "HTTP/1.0 200"
-#define HTTP_GOODSTRING2 "HTTP/1.1 200"
-
-/**
+/*
* We're using an HTTP proxy for a non-port 80 tunnel. Read the
* response to the CONNECT request.
*/
@@ -943,7 +948,7 @@ http_canread(gpointer data, gint source, PurpleInputCondition cond)
if (p != NULL) {
*p = '\0';
headers_len = (p - (char *)connect_data->read_buffer) + 4;
- } else if(len == max_read)
+ } else if((gsize)len == max_read)
headers_len = len;
else
return;
@@ -1239,7 +1244,7 @@ http_canwrite(gpointer data, gint source, PurpleInputCondition cond) {
}
static void
-proxy_connect_http(PurpleProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
+proxy_connect_http(PurpleProxyConnectData *connect_data, common_sockaddr_t *addr, socklen_t addrlen)
{
purple_debug_info("proxy",
"Connecting to %s:%d via %s:%d using HTTP\n",
@@ -1247,7 +1252,7 @@ proxy_connect_http(PurpleProxyConnectData *connect_data, struct sockaddr *addr,
(purple_proxy_info_get_host(connect_data->gpi) ? purple_proxy_info_get_host(connect_data->gpi) : "(null)"),
purple_proxy_info_get_port(connect_data->gpi));
- connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0);
+ connect_data->fd = socket(addr->sa.sa_family, SOCK_STREAM, 0);
if (connect_data->fd < 0)
{
purple_proxy_connect_data_disconnect_formatted(connect_data,
@@ -1256,9 +1261,9 @@ proxy_connect_http(PurpleProxyConnectData *connect_data, struct sockaddr *addr,
}
_purple_network_set_common_socket_flags(connect_data->fd);
- if (connect(connect_data->fd, addr, addrlen) != 0) {
+ if (connect(connect_data->fd, &addr->sa, addrlen) != 0) {
if (errno == EINPROGRESS || errno == EINTR) {
- purple_debug_info("proxy", "Connection in progress\n");
+ purple_debug_info("proxy", "HTTP connection in progress\n");
connect_data->inpa = purple_input_add(connect_data->fd,
PURPLE_INPUT_WRITE, http_canwrite, connect_data);
@@ -1309,7 +1314,7 @@ s4_host_resolved(GSList *hosts, gpointer data, const char *error_message)
{
PurpleProxyConnectData *connect_data = data;
unsigned char packet[9];
- struct sockaddr *addr;
+ common_sockaddr_t *addr;
connect_data->query_data = NULL;
@@ -1333,7 +1338,7 @@ s4_host_resolved(GSList *hosts, gpointer data, const char *error_message)
packet[1] = 0x01;
packet[2] = connect_data->port >> 8;
packet[3] = connect_data->port & 0xff;
- memcpy(packet + 4, &((struct sockaddr_in *)addr)->sin_addr.s_addr, 4);
+ memcpy(packet + 4, &addr->in.sin_addr.s_addr, 4);
packet[8] = 0x00;
g_free(addr);
@@ -1414,7 +1419,7 @@ s4_canwrite(gpointer data, gint source, PurpleInputCondition cond)
proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
} else {
- connect_data->query_data = purple_dnsquery_a_account(
+ connect_data->query_data = purple_dnsquery_a(
connect_data->account, connect_data->host,
connect_data->port, s4_host_resolved, connect_data);
@@ -1426,7 +1431,7 @@ s4_canwrite(gpointer data, gint source, PurpleInputCondition cond)
}
static void
-proxy_connect_socks4(PurpleProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
+proxy_connect_socks4(PurpleProxyConnectData *connect_data, common_sockaddr_t *addr, socklen_t addrlen)
{
purple_debug_info("proxy",
"Connecting to %s:%d via %s:%d using SOCKS4\n",
@@ -1434,7 +1439,7 @@ proxy_connect_socks4(PurpleProxyConnectData *connect_data, struct sockaddr *addr
purple_proxy_info_get_host(connect_data->gpi),
purple_proxy_info_get_port(connect_data->gpi));
- connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0);
+ connect_data->fd = socket(addr->sa.sa_family, SOCK_STREAM, 0);
if (connect_data->fd < 0)
{
purple_proxy_connect_data_disconnect_formatted(connect_data,
@@ -1443,11 +1448,11 @@ proxy_connect_socks4(PurpleProxyConnectData *connect_data, struct sockaddr *addr
}
_purple_network_set_common_socket_flags(connect_data->fd);
- if (connect(connect_data->fd, addr, addrlen) != 0)
+ if (connect(connect_data->fd, &addr->sa, addrlen) != 0)
{
if ((errno == EINPROGRESS) || (errno == EINTR))
{
- purple_debug_info("proxy", "Connection in progress.\n");
+ purple_debug_info("proxy", "SOCKS4 connection in progress\n");
connect_data->inpa = purple_input_add(connect_data->fd,
PURPLE_INPUT_WRITE, s4_canwrite, connect_data);
}
@@ -1465,7 +1470,7 @@ proxy_connect_socks4(PurpleProxyConnectData *connect_data, struct sockaddr *addr
}
static gboolean
-s5_ensure_buffer_length(PurpleProxyConnectData *connect_data, int len)
+s5_ensure_buffer_length(PurpleProxyConnectData *connect_data, guint len)
{
if(connect_data->read_len < len) {
if(connect_data->read_buf_len < len) {
@@ -1654,23 +1659,21 @@ s5_readauth(gpointer data, gint source, PurpleInputCondition cond)
static void
hmacmd5_chap(const unsigned char * challenge, int challen, const char * passwd, unsigned char * response)
{
- PurpleCipher *cipher;
- PurpleCipherContext *ctx;
+ PurpleHash *hash;
int i;
unsigned char Kxoripad[65];
unsigned char Kxoropad[65];
size_t pwlen;
- cipher = purple_ciphers_find_cipher("md5");
- ctx = purple_cipher_context_new(cipher, NULL);
+ hash = purple_md5_hash_new();
memset(Kxoripad,0,sizeof(Kxoripad));
memset(Kxoropad,0,sizeof(Kxoropad));
pwlen=strlen(passwd);
if (pwlen>64) {
- purple_cipher_context_append(ctx, (const guchar *)passwd, strlen(passwd));
- purple_cipher_context_digest(ctx, sizeof(Kxoripad), Kxoripad, NULL);
+ purple_hash_append(hash, (const guchar *)passwd, strlen(passwd));
+ purple_hash_digest(hash, Kxoripad, sizeof(Kxoripad));
pwlen=16;
} else {
memcpy(Kxoripad, passwd, pwlen);
@@ -1682,17 +1685,17 @@ hmacmd5_chap(const unsigned char * challenge, int challen, const char * passwd,
Kxoropad[i]^=0x5c;
}
- purple_cipher_context_reset(ctx, NULL);
- purple_cipher_context_append(ctx, Kxoripad, 64);
- purple_cipher_context_append(ctx, challenge, challen);
- purple_cipher_context_digest(ctx, sizeof(Kxoripad), Kxoripad, NULL);
+ purple_hash_reset(hash);
+ purple_hash_append(hash, Kxoripad, 64);
+ purple_hash_append(hash, challenge, challen);
+ purple_hash_digest(hash, Kxoripad, sizeof(Kxoripad));
- purple_cipher_context_reset(ctx, NULL);
- purple_cipher_context_append(ctx, Kxoropad, 64);
- purple_cipher_context_append(ctx, Kxoripad, 16);
- purple_cipher_context_digest(ctx, 16, response, NULL);
+ purple_hash_reset(hash);
+ purple_hash_append(hash, Kxoropad, 64);
+ purple_hash_append(hash, Kxoripad, 16);
+ purple_hash_digest(hash, response, 16);
- purple_cipher_context_destroy(ctx);
+ g_object_unref(hash);
}
static void
@@ -2057,7 +2060,6 @@ s5_canwrite(gpointer data, gint source, PurpleInputCondition cond)
return;
}
- i = 0;
buf[0] = 0x05; /* SOCKS version 5 */
if (purple_proxy_info_get_username(connect_data->gpi) != NULL) {
@@ -2084,7 +2086,7 @@ s5_canwrite(gpointer data, gint source, PurpleInputCondition cond)
}
static void
-proxy_connect_socks5(PurpleProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
+proxy_connect_socks5(PurpleProxyConnectData *connect_data, common_sockaddr_t *addr, socklen_t addrlen)
{
purple_debug_info("proxy",
"Connecting to %s:%d via %s:%d using SOCKS5\n",
@@ -2092,7 +2094,7 @@ proxy_connect_socks5(PurpleProxyConnectData *connect_data, struct sockaddr *addr
purple_proxy_info_get_host(connect_data->gpi),
purple_proxy_info_get_port(connect_data->gpi));
- connect_data->fd = socket(addr->sa_family, SOCK_STREAM, 0);
+ connect_data->fd = socket(addr->sa.sa_family, SOCK_STREAM, 0);
if (connect_data->fd < 0)
{
purple_proxy_connect_data_disconnect_formatted(connect_data,
@@ -2101,11 +2103,11 @@ proxy_connect_socks5(PurpleProxyConnectData *connect_data, struct sockaddr *addr
}
_purple_network_set_common_socket_flags(connect_data->fd);
- if (connect(connect_data->fd, addr, addrlen) != 0)
+ if (connect(connect_data->fd, &addr->sa, addrlen) != 0)
{
if ((errno == EINPROGRESS) || (errno == EINTR))
{
- purple_debug_info("socks5 proxy", "Connection in progress\n");
+ purple_debug_info("proxy", "SOCKS5 connection in progress\n");
connect_data->inpa = purple_input_add(connect_data->fd,
PURPLE_INPUT_WRITE, s5_canwrite, connect_data);
}
@@ -2122,9 +2124,9 @@ proxy_connect_socks5(PurpleProxyConnectData *connect_data, struct sockaddr *addr
}
}
-/**
+/*
* This function attempts to connect to the next IP address in the list
- * of IP addresses returned to us by purple_dnsquery_a() and attemps
+ * of IP addresses returned to us by purple_dnsquery_a() and attempts
* to connect to each one. This is called after the hostname is
* resolved, and each time a connection attempt fails (assuming there
* is another IP address to try).
@@ -2136,7 +2138,7 @@ proxy_connect_socks5(PurpleProxyConnectData *connect_data, struct sockaddr *addr
static void try_connect(PurpleProxyConnectData *connect_data)
{
socklen_t addrlen;
- struct sockaddr *addr;
+ common_sockaddr_t *addr;
char ipaddr[INET6_ADDRSTRLEN];
addrlen = GPOINTER_TO_INT(connect_data->hosts->data);
@@ -2144,15 +2146,14 @@ static void try_connect(PurpleProxyConnectData *connect_data)
addr = connect_data->hosts->data;
connect_data->hosts = g_slist_remove(connect_data->hosts, connect_data->hosts->data);
#ifdef HAVE_INET_NTOP
- if (addr->sa_family == AF_INET)
- inet_ntop(addr->sa_family, &((struct sockaddr_in *)addr)->sin_addr,
+ if (addr->sa.sa_family == AF_INET)
+ inet_ntop(addr->sa.sa_family, &addr->in.sin_addr,
ipaddr, sizeof(ipaddr));
- else if (addr->sa_family == AF_INET6)
- inet_ntop(addr->sa_family, &((struct sockaddr_in6 *)addr)->sin6_addr,
+ else if (addr->sa.sa_family == AF_INET6)
+ inet_ntop(addr->sa.sa_family, &addr->in6.sin6_addr,
ipaddr, sizeof(ipaddr));
#else
- memcpy(ipaddr, inet_ntoa(((struct sockaddr_in *)addr)->sin_addr),
- sizeof(ipaddr));
+ memcpy(ipaddr, inet_ntoa(addr->in.sin_addr), sizeof(ipaddr));
#endif
purple_debug_info("proxy", "Attempting connection to %s\n", ipaddr);
@@ -2162,7 +2163,7 @@ static void try_connect(PurpleProxyConnectData *connect_data)
return;
}
- switch (purple_proxy_info_get_type(connect_data->gpi)) {
+ switch (purple_proxy_info_get_proxy_type(connect_data->gpi)) {
case PURPLE_PROXY_NONE:
proxy_connect_none(connect_data, addr, addrlen);
break;
@@ -2227,12 +2228,12 @@ purple_proxy_get_setup(PurpleAccount *account)
static PurpleProxyInfo *tmp_none_proxy_info = NULL;
if (!tmp_none_proxy_info) {
tmp_none_proxy_info = purple_proxy_info_new();
- purple_proxy_info_set_type(tmp_none_proxy_info, PURPLE_PROXY_NONE);
+ purple_proxy_info_set_proxy_type(tmp_none_proxy_info, PURPLE_PROXY_NONE);
}
if (account && purple_account_get_proxy_info(account) != NULL) {
gpi = purple_account_get_proxy_info(account);
- if (purple_proxy_info_get_type(gpi) == PURPLE_PROXY_USE_GLOBAL)
+ if (purple_proxy_info_get_proxy_type(gpi) == PURPLE_PROXY_USE_GLOBAL)
gpi = NULL;
}
if (gpi == NULL) {
@@ -2242,47 +2243,42 @@ purple_proxy_get_setup(PurpleAccount *account)
gpi = purple_global_proxy_get_info();
}
- if (purple_proxy_info_get_type(gpi) == PURPLE_PROXY_USE_ENVVAR) {
+ if (purple_proxy_info_get_proxy_type(gpi) == PURPLE_PROXY_USE_ENVVAR) {
if ((tmp = g_getenv("HTTP_PROXY")) != NULL ||
(tmp = g_getenv("http_proxy")) != NULL ||
- (tmp = g_getenv("HTTPPROXY")) != NULL) {
- char *proxyhost, *proxyuser, *proxypasswd;
- int proxyport;
+ (tmp = g_getenv("HTTPPROXY")) != NULL)
+ {
+ PurpleHttpURL *url;
/* http_proxy-format:
* export http_proxy="http://user:passwd@your.proxy.server:port/"
*/
- if(purple_url_parse(tmp, &proxyhost, &proxyport, NULL, &proxyuser, &proxypasswd)) {
- purple_proxy_info_set_host(gpi, proxyhost);
- g_free(proxyhost);
-
- purple_proxy_info_set_username(gpi, proxyuser);
- g_free(proxyuser);
-
- purple_proxy_info_set_password(gpi, proxypasswd);
- g_free(proxypasswd);
-
- /* only for backward compatibility */
- if (proxyport == 80 &&
- ((tmp = g_getenv("HTTP_PROXY_PORT")) != NULL ||
- (tmp = g_getenv("http_proxy_port")) != NULL ||
- (tmp = g_getenv("HTTPPROXYPORT")) != NULL))
- proxyport = atoi(tmp);
-
- purple_proxy_info_set_port(gpi, proxyport);
-
- /* XXX: Do we want to skip this step if user/password were part of url? */
- if ((tmp = g_getenv("HTTP_PROXY_USER")) != NULL ||
- (tmp = g_getenv("http_proxy_user")) != NULL ||
- (tmp = g_getenv("HTTPPROXYUSER")) != NULL)
- purple_proxy_info_set_username(gpi, tmp);
-
- if ((tmp = g_getenv("HTTP_PROXY_PASS")) != NULL ||
- (tmp = g_getenv("http_proxy_pass")) != NULL ||
- (tmp = g_getenv("HTTPPROXYPASS")) != NULL)
- purple_proxy_info_set_password(gpi, tmp);
-
+ url = purple_http_url_parse(tmp);
+ if (!url) {
+ purple_debug_warning("proxy", "Couldn't parse URL\n");
+ return gpi;
}
+
+ purple_proxy_info_set_host(gpi, purple_http_url_get_host(url));
+ purple_proxy_info_set_username(gpi, purple_http_url_get_username(url));
+ purple_proxy_info_set_password(gpi, purple_http_url_get_password(url));
+ purple_proxy_info_set_port(gpi, purple_http_url_get_port(url));
+
+ /* XXX: Do we want to skip this step if user/password/port were part of url? */
+ if ((tmp = g_getenv("HTTP_PROXY_USER")) != NULL ||
+ (tmp = g_getenv("http_proxy_user")) != NULL ||
+ (tmp = g_getenv("HTTPPROXYUSER")) != NULL)
+ purple_proxy_info_set_username(gpi, tmp);
+
+ if ((tmp = g_getenv("HTTP_PROXY_PASS")) != NULL ||
+ (tmp = g_getenv("http_proxy_pass")) != NULL ||
+ (tmp = g_getenv("HTTPPROXYPASS")) != NULL)
+ purple_proxy_info_set_password(gpi, tmp);
+
+ if ((tmp = g_getenv("HTTP_PROXY_PORT")) != NULL ||
+ (tmp = g_getenv("http_proxy_port")) != NULL ||
+ (tmp = g_getenv("HTTPPROXYPORT")) != NULL)
+ purple_proxy_info_set_port(gpi, atoi(tmp));
} else {
#ifdef _WIN32
PurpleProxyInfo *wgpi;
@@ -2323,16 +2319,19 @@ purple_proxy_connect(void *handle, PurpleAccount *account,
connect_data->gpi = purple_proxy_get_setup(account);
connect_data->account = account;
- if ((purple_proxy_info_get_type(connect_data->gpi) != PURPLE_PROXY_NONE) &&
+ if ((purple_proxy_info_get_proxy_type(connect_data->gpi) != PURPLE_PROXY_NONE) &&
(purple_proxy_info_get_host(connect_data->gpi) == NULL ||
purple_proxy_info_get_port(connect_data->gpi) <= 0)) {
- purple_notify_error(NULL, NULL, _("Invalid proxy settings"), _("Either the host name or port number specified for your given proxy type is invalid."));
+ purple_notify_error(NULL, NULL, _("Invalid proxy settings"),
+ _("Either the host name or port number specified for "
+ "your given proxy type is invalid."),
+ purple_request_cpar_from_account(account));
purple_proxy_connect_data_destroy(connect_data);
return NULL;
}
- switch (purple_proxy_info_get_type(connect_data->gpi))
+ switch (purple_proxy_info_get_proxy_type(connect_data->gpi))
{
case PURPLE_PROXY_NONE:
break;
@@ -2348,12 +2347,12 @@ purple_proxy_connect(void *handle, PurpleAccount *account,
default:
purple_debug_error("proxy", "Invalid Proxy type (%d) specified.\n",
- purple_proxy_info_get_type(connect_data->gpi));
+ purple_proxy_info_get_proxy_type(connect_data->gpi));
purple_proxy_connect_data_destroy(connect_data);
return NULL;
}
- connect_data->query_data = purple_dnsquery_a_account(account, connecthost,
+ connect_data->query_data = purple_dnsquery_a(account, connecthost,
connectport, connection_host_resolved, connect_data);
if (connect_data->query_data == NULL)
{
@@ -2391,16 +2390,19 @@ purple_proxy_connect_udp(void *handle, PurpleAccount *account,
connect_data->gpi = purple_proxy_get_setup(account);
connect_data->account = account;
- if ((purple_proxy_info_get_type(connect_data->gpi) != PURPLE_PROXY_NONE) &&
+ if ((purple_proxy_info_get_proxy_type(connect_data->gpi) != PURPLE_PROXY_NONE) &&
(purple_proxy_info_get_host(connect_data->gpi) == NULL ||
purple_proxy_info_get_port(connect_data->gpi) <= 0)) {
- purple_notify_error(NULL, NULL, _("Invalid proxy settings"), _("Either the host name or port number specified for your given proxy type is invalid."));
+ purple_notify_error(NULL, NULL, _("Invalid proxy settings"),
+ _("Either the host name or port number specified for "
+ "your given proxy type is invalid."),
+ purple_request_cpar_from_account(account));
purple_proxy_connect_data_destroy(connect_data);
return NULL;
}
- switch (purple_proxy_info_get_type(connect_data->gpi))
+ switch (purple_proxy_info_get_proxy_type(connect_data->gpi))
{
case PURPLE_PROXY_NONE:
break;
@@ -2411,17 +2413,17 @@ purple_proxy_connect_udp(void *handle, PurpleAccount *account,
case PURPLE_PROXY_TOR:
case PURPLE_PROXY_USE_ENVVAR:
purple_debug_info("proxy", "Ignoring Proxy type (%d) for UDP.\n",
- purple_proxy_info_get_type(connect_data->gpi));
+ purple_proxy_info_get_proxy_type(connect_data->gpi));
break;
default:
purple_debug_error("proxy", "Invalid Proxy type (%d) specified.\n",
- purple_proxy_info_get_type(connect_data->gpi));
+ purple_proxy_info_get_proxy_type(connect_data->gpi));
purple_proxy_connect_data_destroy(connect_data);
return NULL;
}
- connect_data->query_data = purple_dnsquery_a_account(account, connecthost,
+ connect_data->query_data = purple_dnsquery_a(account, connecthost,
connectport, connection_host_resolved, connect_data);
if (connect_data->query_data == NULL)
{
@@ -2434,17 +2436,6 @@ purple_proxy_connect_udp(void *handle, PurpleAccount *account,
return connect_data;
}
-PurpleProxyConnectData *
-purple_proxy_connect_socks5(void *handle, PurpleProxyInfo *gpi,
- const char *host, int port,
- PurpleProxyConnectFunction connect_cb,
- gpointer data)
-{
- return purple_proxy_connect_socks5_account(handle, NULL, gpi,
- host, port, connect_cb, data);
-}
-
-
/* This is called when we connect to the SOCKS5 proxy server (through any
* relevant account proxy)
*/
@@ -2585,7 +2576,7 @@ proxy_pref_cb(const char *name, PurplePrefType type,
else
proxytype = -1;
- purple_proxy_info_set_type(info, proxytype);
+ purple_proxy_info_set_proxy_type(info, proxytype);
} else if (purple_strequal(name, "/purple/proxy/host"))
purple_proxy_info_set_host(info, value);
else if (purple_strequal(name, "/purple/proxy/port"))
diff --git a/libpurple/proxy.h b/libpurple/proxy.h
index cf91ee8c71..13c95f388a 100644
--- a/libpurple/proxy.h
+++ b/libpurple/proxy.h
@@ -1,8 +1,3 @@
-/**
- * @file proxy.h Proxy API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -23,40 +18,49 @@
* 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_PROXY_H_
#define _PURPLE_PROXY_H_
+/**
+ * SECTION:proxy
+ * @section_id: libpurple-proxy
+ * @short_description: <filename>proxy.h</filename>
+ * @title: Proxy API
+ */
#include <glib.h>
#include "eventloop.h"
/**
+ * PurpleProxyType:
+ * @PURPLE_PROXY_USE_GLOBAL: Use the global proxy information.
+ * @PURPLE_PROXY_NONE: No proxy.
+ * @PURPLE_PROXY_HTTP: HTTP proxy.
+ * @PURPLE_PROXY_SOCKS4: SOCKS 4 proxy.
+ * @PURPLE_PROXY_SOCKS5: SOCKS 5 proxy.
+ * @PURPLE_PROXY_USE_ENVVAR: Use environmental settings.
+ * @PURPLE_PROXY_TOR: Use a Tor proxy (SOCKS 5 really).
+ *
* A type of proxy connection.
*/
typedef enum
{
- PURPLE_PROXY_USE_GLOBAL = -1, /**< Use the global proxy information. */
- PURPLE_PROXY_NONE = 0, /**< No proxy. */
- PURPLE_PROXY_HTTP, /**< HTTP proxy. */
- PURPLE_PROXY_SOCKS4, /**< SOCKS 4 proxy. */
- PURPLE_PROXY_SOCKS5, /**< SOCKS 5 proxy. */
- PURPLE_PROXY_USE_ENVVAR, /**< Use environmental settings. */
- PURPLE_PROXY_TOR /**< Use a Tor proxy (SOCKS 5 really) */
+ PURPLE_PROXY_USE_GLOBAL = -1,
+ PURPLE_PROXY_NONE = 0,
+ PURPLE_PROXY_HTTP,
+ PURPLE_PROXY_SOCKS4,
+ PURPLE_PROXY_SOCKS5,
+ PURPLE_PROXY_USE_ENVVAR,
+ PURPLE_PROXY_TOR
} PurpleProxyType;
/**
+ * PurpleProxyInfo:
+ *
* Information on proxy settings.
*/
-typedef struct
-{
- PurpleProxyType type; /**< The proxy type. */
-
- char *host; /**< The host. */
- int port; /**< The port number. */
- char *username; /**< The username. */
- char *password; /**< The password. */
-
-} PurpleProxyInfo;
+typedef struct _PurpleProxyInfo PurpleProxyInfo;
typedef struct _PurpleProxyConnectData PurpleProxyConnectData;
@@ -65,190 +69,204 @@ typedef void (*PurpleProxyConnectFunction)(gpointer data, gint source, const gch
#include "account.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name Proxy structure API */
+/* Proxy structure API */
/**************************************************************************/
-/*@{*/
/**
+ * purple_proxy_info_new:
+ *
* Creates a proxy information structure.
*
- * @return The proxy information structure.
+ * Returns: The proxy information structure.
*/
PurpleProxyInfo *purple_proxy_info_new(void);
/**
- * Destroys a proxy information structure.
+ * purple_proxy_info_destroy:
+ * @info: The proxy information structure to destroy.
*
- * @param info The proxy information structure to destroy.
+ * Destroys a proxy information structure.
*/
void purple_proxy_info_destroy(PurpleProxyInfo *info);
/**
- * Sets the type of proxy.
+ * purple_proxy_info_set_proxy_type:
+ * @info: The proxy information.
+ * @type: The proxy type.
*
- * @param info The proxy information.
- * @param type The proxy type.
+ * Sets the type of proxy.
*/
-void purple_proxy_info_set_type(PurpleProxyInfo *info, PurpleProxyType type);
+void purple_proxy_info_set_proxy_type(PurpleProxyInfo *info, PurpleProxyType type);
/**
- * Sets the proxy host.
+ * purple_proxy_info_set_host:
+ * @info: The proxy information.
+ * @host: The host.
*
- * @param info The proxy information.
- * @param host The host.
+ * Sets the proxy host.
*/
void purple_proxy_info_set_host(PurpleProxyInfo *info, const char *host);
/**
- * Sets the proxy port.
+ * purple_proxy_info_set_port:
+ * @info: The proxy information.
+ * @port: The port.
*
- * @param info The proxy information.
- * @param port The port.
+ * Sets the proxy port.
*/
void purple_proxy_info_set_port(PurpleProxyInfo *info, int port);
/**
- * Sets the proxy username.
+ * purple_proxy_info_set_username:
+ * @info: The proxy information.
+ * @username: The username.
*
- * @param info The proxy information.
- * @param username The username.
+ * Sets the proxy username.
*/
void purple_proxy_info_set_username(PurpleProxyInfo *info, const char *username);
/**
- * Sets the proxy password.
+ * purple_proxy_info_set_password:
+ * @info: The proxy information.
+ * @password: The password.
*
- * @param info The proxy information.
- * @param password The password.
+ * Sets the proxy password.
*/
void purple_proxy_info_set_password(PurpleProxyInfo *info, const char *password);
/**
- * Returns the proxy's type.
+ * purple_proxy_info_get_proxy_type:
+ * @info: The proxy information.
*
- * @param info The proxy information.
+ * Returns the proxy's type.
*
- * @return The type.
+ * Returns: The type.
*/
-PurpleProxyType purple_proxy_info_get_type(const PurpleProxyInfo *info);
+PurpleProxyType purple_proxy_info_get_proxy_type(const PurpleProxyInfo *info);
/**
- * Returns the proxy's host.
+ * purple_proxy_info_get_host:
+ * @info: The proxy information.
*
- * @param info The proxy information.
+ * Returns the proxy's host.
*
- * @return The host.
+ * Returns: The host.
*/
const char *purple_proxy_info_get_host(const PurpleProxyInfo *info);
/**
- * Returns the proxy's port.
+ * purple_proxy_info_get_port:
+ * @info: The proxy information.
*
- * @param info The proxy information.
+ * Returns the proxy's port.
*
- * @return The port.
+ * Returns: The port.
*/
int purple_proxy_info_get_port(const PurpleProxyInfo *info);
/**
- * Returns the proxy's username.
+ * purple_proxy_info_get_username:
+ * @info: The proxy information.
*
- * @param info The proxy information.
+ * Returns the proxy's username.
*
- * @return The username.
+ * Returns: The username.
*/
const char *purple_proxy_info_get_username(const PurpleProxyInfo *info);
/**
- * Returns the proxy's password.
+ * purple_proxy_info_get_password:
+ * @info: The proxy information.
*
- * @param info The proxy information.
+ * Returns the proxy's password.
*
- * @return The password.
+ * Returns: The password.
*/
const char *purple_proxy_info_get_password(const PurpleProxyInfo *info);
-/*@}*/
-
/**************************************************************************/
-/** @name Global Proxy API */
+/* Global Proxy API */
/**************************************************************************/
-/*@{*/
/**
+ * purple_global_proxy_get_info:
+ *
* Returns purple's global proxy information.
*
- * @return The global proxy information.
+ * Returns: The global proxy information.
*/
PurpleProxyInfo *purple_global_proxy_get_info(void);
/**
- * Set purple's global proxy information.
+ * purple_global_proxy_set_info:
+ * @info: The proxy information.
*
- * @param info The proxy information.
- * @since 2.6.0
+ * Set purple's global proxy information.
*/
void purple_global_proxy_set_info(PurpleProxyInfo *info);
-/*@}*/
-
/**************************************************************************/
-/** @name Proxy API */
+/* Proxy API */
/**************************************************************************/
-/*@{*/
/**
+ * purple_proxy_get_handle:
+ *
* Returns the proxy subsystem handle.
*
- * @return The proxy subsystem handle.
+ * Returns: The proxy subsystem handle.
*/
void *purple_proxy_get_handle(void);
/**
+ * purple_proxy_init:
+ *
* Initializes the proxy subsystem.
*/
void purple_proxy_init(void);
/**
+ * purple_proxy_uninit:
+ *
* Uninitializes the proxy subsystem.
*/
void purple_proxy_uninit(void);
/**
- * Returns configuration of a proxy.
+ * purple_proxy_get_setup:
+ * @account: The account for which the configuration is needed.
*
- * @param account The account for which the configuration is needed.
+ * Returns configuration of a proxy.
*
- * @return The configuration of a proxy.
+ * Returns: The configuration of a proxy.
*/
PurpleProxyInfo *purple_proxy_get_setup(PurpleAccount *account);
/**
+ * purple_proxy_connect:
+ * @handle: A handle that should be associated with this
+ * connection attempt. The handle can be used
+ * to cancel the connection attempt using the
+ * purple_proxy_connect_cancel_with_handle()
+ * function.
+ * @account: The account making the connection.
+ * @host: The destination host.
+ * @port: The destination port.
+ * @connect_cb: (scope call): The function to call when the connection is
+ * established. If the connection failed then
+ * fd will be -1 and error message will be set
+ * to something descriptive (hopefully).
+ * @data: User-defined data.
+ *
* Makes a connection to the specified host and port. Note that this
* function name can be misleading--although it is called "proxy
* connect," it is used for establishing any outgoing TCP connection,
* whether through a proxy or not.
*
- * @param handle A handle that should be associated with this
- * connection attempt. The handle can be used
- * to cancel the connection attempt using the
- * purple_proxy_connect_cancel_with_handle()
- * function.
- * @param account The account making the connection.
- * @param host The destination host.
- * @param port The destination port.
- * @param connect_cb The function to call when the connection is
- * established. If the connection failed then
- * fd will be -1 and error message will be set
- * to something descriptive (hopefully).
- * @param data User-defined data.
- *
- * @return NULL if there was an error, or a reference to an
+ * Returns: NULL if there was an error, or a reference to an
* opaque data structure that can be used to cancel
* the pending connection, if needed.
*/
@@ -258,26 +276,27 @@ PurpleProxyConnectData *purple_proxy_connect(void *handle,
PurpleProxyConnectFunction connect_cb, gpointer data);
/**
+ * purple_proxy_connect_udp:
+ * @handle: A handle that should be associated with this
+ * connection attempt. The handle can be used
+ * to cancel the connection attempt using the
+ * purple_proxy_connect_cancel_with_handle()
+ * function.
+ * @account: The account making the connection.
+ * @host: The destination host.
+ * @port: The destination port.
+ * @connect_cb: (scope call): The function to call when the connection is
+ * established. If the connection failed then
+ * fd will be -1 and error message will be set
+ * to something descriptive (hopefully).
+ * @data: User-defined data.
+ *
* Makes a connection to the specified host and port. Note that this
* function name can be misleading--although it is called "proxy
* connect," it is used for establishing any outgoing UDP connection,
* whether through a proxy or not.
*
- * @param handle A handle that should be associated with this
- * connection attempt. The handle can be used
- * to cancel the connection attempt using the
- * purple_proxy_connect_cancel_with_handle()
- * function.
- * @param account The account making the connection.
- * @param host The destination host.
- * @param port The destination port.
- * @param connect_cb The function to call when the connection is
- * established. If the connection failed then
- * fd will be -1 and error message will be set
- * to something descriptive (hopefully).
- * @param data User-defined data.
- *
- * @return NULL if there was an error, or a reference to an
+ * Returns: NULL if there was an error, or a reference to an
* opaque data structure that can be used to cancel
* the pending connection, if needed.
*/
@@ -287,27 +306,28 @@ PurpleProxyConnectData *purple_proxy_connect_udp(void *handle,
PurpleProxyConnectFunction connect_cb, gpointer data);
/**
+ * purple_proxy_connect_socks5_account:
+ * @handle: A handle that should be associated with this
+ * connection attempt. The handle can be used
+ * to cancel the connection attempt using the
+ * purple_proxy_connect_cancel_with_handle()
+ * function.
+ * @account: The account making the connection.
+ * @gpi: The PurpleProxyInfo specifying the proxy settings
+ * @host: The destination host.
+ * @port: The destination port.
+ * @connect_cb: (scope call): The function to call when the connection is
+ * established. If the connection failed then
+ * fd will be -1 and error message will be set
+ * to something descriptive (hopefully).
+ * @data: User-defined data.
+ *
* Makes a connection through a SOCKS5 proxy.
*
* Note that if the account that is making the connection uses a proxy, this
* connection to a SOCKS5 proxy will be made through the account proxy.
*
- * @param handle A handle that should be associated with this
- * connection attempt. The handle can be used
- * to cancel the connection attempt using the
- * purple_proxy_connect_cancel_with_handle()
- * function.
- * @param account The account making the connection.
- * @param gpi The PurpleProxyInfo specifying the proxy settings
- * @param host The destination host.
- * @param port The destination port.
- * @param connect_cb The function to call when the connection is
- * established. If the connection failed then
- * fd will be -1 and error message will be set
- * to something descriptive (hopefully).
- * @param data User-defined data.
- *
- * @return NULL if there was an error, or a reference to an
+ * Returns: NULL if there was an error, or a reference to an
* opaque data structure that can be used to cancel
* the pending connection, if needed.
*/
@@ -316,57 +336,27 @@ PurpleProxyConnectData *purple_proxy_connect_socks5_account(void *handle,
const char *host, int port,
PurpleProxyConnectFunction connect_cb, gpointer data);
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_PROXY_C_)
/**
- * Makes a connection through a SOCKS5 proxy.
+ * purple_proxy_connect_cancel:
*
- * @param handle A handle that should be associated with this
- * connection attempt. The handle can be used
- * to cancel the connection attempt using the
- * purple_proxy_connect_cancel_with_handle()
- * function.
- * @param gpi The PurpleProxyInfo specifying the proxy settings
- * @param host The destination host.
- * @param port The destination port.
- * @param connect_cb The function to call when the connection is
- * established. If the connection failed then
- * fd will be -1 and error message will be set
- * to something descriptive (hopefully).
- * @param data User-defined data.
- *
- * @return NULL if there was an error, or a reference to an
- * opaque data structure that can be used to cancel
- * the pending connection, if needed.
- * @deprecated Use purple_proxy_connect_socks5_account instead
- */
-PurpleProxyConnectData *purple_proxy_connect_socks5(void *handle,
- PurpleProxyInfo *gpi,
- const char *host, int port,
- PurpleProxyConnectFunction connect_cb, gpointer data);
-#endif
-
-/**
* Cancel an in-progress connection attempt. This should be called
- * by the PRPL if the user disables an account while it is still
+ * by the protocol if the user disables an account while it is still
* performing the initial sign on. Or when establishing a file
* transfer, if we attempt to connect to a remote user but they
- * are behind a firewall then the PRPL can cancel the connection
+ * are behind a firewall then the protocol can cancel the connection
* attempt early rather than just letting the OS's TCP/IP stack
* time-out the connection.
*/
void purple_proxy_connect_cancel(PurpleProxyConnectData *connect_data);
-/*
- * Closes all proxy connections registered with the specified handle.
+/**
+ * purple_proxy_connect_cancel_with_handle:
+ * @handle: The handle.
*
- * @param handle The handle.
+ * Closes all proxy connections registered with the specified handle.
*/
void purple_proxy_connect_cancel_with_handle(void *handle);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_PROXY_H_ */
diff --git a/libpurple/prpl.c b/libpurple/prpl.c
index 6c1470d45f..491bc9a7ff 100644
--- a/libpurple/prpl.c
+++ b/libpurple/prpl.c
@@ -30,8 +30,19 @@
#include "util.h"
/**************************************************************************/
-/** @name Attention Type API */
+/* Attention Type API */
/**************************************************************************/
+
+struct _PurpleAttentionType
+{
+ const char *name; /* Shown in GUI elements */
+ const char *incoming_description; /* Shown when sent */
+ const char *outgoing_description; /* Shown when receied */
+ const char *icon_name; /* Icon to display (optional) */
+ const char *unlocalized_name; /* Unlocalized name for UIs needing it */
+};
+
+
PurpleAttentionType *
purple_attention_type_new(const char *ulname, const char *name,
const char *inc_desc, const char *out_desc)
@@ -131,7 +142,7 @@ purple_attention_type_get_unlocalized_name(const PurpleAttentionType *type)
}
/**************************************************************************/
-/** @name Protocol Plugin API */
+/* Protocol Plugin API */
/**************************************************************************/
void
purple_prpl_got_account_idle(PurpleAccount *account, gboolean idle,
@@ -203,7 +214,7 @@ purple_prpl_got_user_idle(PurpleAccount *account, const char *name,
g_return_if_fail(name != NULL);
g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account));
- if ((list = purple_find_buddies(account, name)) == NULL)
+ if ((list = purple_blist_find_buddies(account, name)) == NULL)
return;
while (list) {
@@ -223,7 +234,7 @@ purple_prpl_got_user_login_time(PurpleAccount *account, const char *name,
g_return_if_fail(account != NULL);
g_return_if_fail(name != NULL);
- if ((list = purple_find_buddies(account, name)) == NULL)
+ if ((list = purple_blist_find_buddies(account, name)) == NULL)
return;
if (login_time == 0)
@@ -259,7 +270,7 @@ purple_prpl_got_user_status(PurpleAccount *account, const char *name,
g_return_if_fail(status_id != NULL);
g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account));
- if((list = purple_find_buddies(account, name)) == NULL)
+ if((list = purple_blist_find_buddies(account, name)) == NULL)
return;
for(l = list; l != NULL; l = l->next) {
@@ -281,7 +292,7 @@ purple_prpl_got_user_status(PurpleAccount *account, const char *name,
purple_status_set_active_with_attrs(status, TRUE, args);
va_end(args);
- purple_blist_update_buddy_status(buddy, old_status);
+ purple_buddy_update_status(buddy, old_status);
}
g_slist_free(list);
@@ -289,7 +300,7 @@ purple_prpl_got_user_status(PurpleAccount *account, const char *name,
/* The buddy is no longer online, they are therefore by definition not
* still typing to us. */
if (!purple_status_is_online(status)) {
- serv_got_typing_stopped(purple_account_get_connection(account), name);
+ purple_serv_got_typing_stopped(purple_account_get_connection(account), name);
purple_prpl_got_media_caps(account, name);
}
}
@@ -307,7 +318,7 @@ void purple_prpl_got_user_status_deactive(PurpleAccount *account, const char *na
g_return_if_fail(status_id != NULL);
g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account));
- if((list = purple_find_buddies(account, name)) == NULL)
+ if((list = purple_blist_find_buddies(account, name)) == NULL)
return;
for(l = list; l != NULL; l = l->next) {
@@ -321,7 +332,7 @@ void purple_prpl_got_user_status_deactive(PurpleAccount *account, const char *na
if (purple_status_is_active(status)) {
purple_status_set_active(status, FALSE);
- purple_blist_update_buddy_status(buddy, status);
+ purple_buddy_update_status(buddy, status);
}
}
@@ -347,9 +358,10 @@ do_prpl_change_account_status(PurpleAccount *account,
{
if (!purple_account_is_disconnected(account))
purple_account_disconnect(account);
- /* Clear out the unsaved password if we're already disconnected and we switch to offline status */
- else if (!purple_account_get_remember_password(account))
- purple_account_set_password(account, NULL);
+ /* Clear out the unsaved password if we switch to offline status */
+ if (!purple_account_get_remember_password(account))
+ purple_account_set_password(account, NULL, NULL, NULL);
+
return;
}
@@ -423,27 +435,23 @@ void
purple_prpl_send_attention(PurpleConnection *gc, const char *who, guint type_code)
{
PurpleAttentionType *attn;
- PurpleMessageFlags flags;
PurplePlugin *prpl;
- PurpleConversation *conv;
+ PurpleIMConversation *im;
gboolean (*send_attention)(PurpleConnection *, const char *, guint);
PurpleBuddy *buddy;
const char *alias;
gchar *description;
- time_t mtime;
g_return_if_fail(gc != NULL);
g_return_if_fail(who != NULL);
- prpl = purple_find_prpl(purple_account_get_protocol_id(gc->account));
+ prpl = purple_find_prpl(purple_account_get_protocol_id(purple_connection_get_account(gc)));
send_attention = PURPLE_PLUGIN_PROTOCOL_INFO(prpl)->send_attention;
g_return_if_fail(send_attention != NULL);
- mtime = time(NULL);
-
- attn = purple_get_attention_type_from_code(gc->account, type_code);
+ attn = purple_get_attention_type_from_code(purple_connection_get_account(gc), type_code);
- if ((buddy = purple_find_buddy(purple_connection_get_account(gc), who)) != NULL)
+ if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), who)) != NULL)
alias = purple_buddy_get_contact_alias(buddy);
else
alias = who;
@@ -454,17 +462,15 @@ purple_prpl_send_attention(PurpleConnection *gc, const char *who, guint type_cod
description = g_strdup_printf(_("Requesting %s's attention..."), alias);
}
- flags = PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_SYSTEM;
-
purple_debug_info("server", "serv_send_attention: sending '%s' to %s\n",
description, who);
if (!send_attention(gc, who, type_code))
return;
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, gc->account, who);
- purple_conv_im_write(PURPLE_CONV_IM(conv), NULL, description, flags, mtime);
- purple_prpl_attention(conv, who, type_code, PURPLE_MESSAGE_SEND, time(NULL));
+ im = purple_im_conversation_new(purple_connection_get_account(gc), who);
+ purple_conversation_write_system_message(PURPLE_CONVERSATION(im), description, 0);
+ purple_prpl_attention(PURPLE_CONVERSATION(im), who, type_code, PURPLE_MESSAGE_SEND, time(NULL));
g_free(description);
}
@@ -481,7 +487,7 @@ got_attention(PurpleConnection *gc, int id, const char *who, guint type_code)
mtime = time(NULL);
- attn = purple_get_attention_type_from_code(gc->account, type_code);
+ attn = purple_get_attention_type_from_code(purple_connection_get_account(gc), type_code);
/* PURPLE_MESSAGE_NOTIFY is for attention messages. */
flags = PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_RECV;
@@ -489,7 +495,7 @@ got_attention(PurpleConnection *gc, int id, const char *who, guint type_code)
/* TODO: if (attn->icon_name) is non-null, use it to lookup an emoticon and display
* it next to the attention command. And if it is null, display a generic icon. */
- if ((buddy = purple_find_buddy(purple_connection_get_account(gc), who)) != NULL)
+ if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), who)) != NULL)
alias = purple_buddy_get_contact_alias(buddy);
else
alias = who;
@@ -504,9 +510,9 @@ got_attention(PurpleConnection *gc, int id, const char *who, guint type_code)
description, who);
if (id == -1)
- serv_got_im(gc, who, description, flags, mtime);
+ purple_serv_got_im(gc, who, description, flags, mtime);
else
- serv_got_chat_in(gc, id, who, flags, description, mtime);
+ purple_serv_got_chat_in(gc, id, who, flags, description, mtime);
/* TODO: sounds (depending on PurpleAttentionType), shaking, etc. */
@@ -521,7 +527,7 @@ purple_prpl_got_attention(PurpleConnection *gc, const char *who, guint type_code
got_attention(gc, -1, who, type_code);
conv =
- purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, account);
+ purple_conversations_find_with_account(who, account);
if (conv)
purple_prpl_attention(conv, who, type_code, PURPLE_MESSAGE_RECV,
time(NULL));
@@ -590,7 +596,7 @@ purple_prpl_got_media_caps(PurpleAccount *account, const char *name)
g_return_if_fail(account != NULL);
g_return_if_fail(name != NULL);
- if ((list = purple_find_buddies(account, name)) == NULL)
+ if ((list = purple_blist_find_buddies(account, name)) == NULL)
return;
while (list) {
@@ -614,6 +620,23 @@ purple_prpl_got_media_caps(PurpleAccount *account, const char *name)
#endif
}
+gssize
+purple_prpl_get_max_message_size(PurplePlugin *prpl)
+{
+ PurplePluginProtocolInfo *prpl_info;
+
+ g_return_val_if_fail(prpl != NULL, 0);
+ g_return_val_if_fail(PURPLE_IS_PROTOCOL_PLUGIN(prpl), 0);
+
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+ g_return_val_if_fail(prpl_info != NULL, 0);
+
+ if (!PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_max_message_size))
+ return 0;
+
+ return prpl_info->get_max_message_size(NULL);
+}
+
/**************************************************************************
* Protocol Plugin Subsystem API
**************************************************************************/
@@ -626,17 +649,6 @@ purple_find_prpl(const char *id)
g_return_val_if_fail(id != NULL, NULL);
- /* libpurple3 compatibility.
- * prpl-xmpp isn't used yet (it's prpl-jabber),
- * but may be used in the future.
- */
- if (g_strcmp0(id, "prpl-xmpp") == 0 ||
- g_strcmp0(id, "prpl-gtalk") == 0 ||
- g_strcmp0(id, "prpl-facebook-xmpp") == 0)
- {
- id = "prpl-jabber";
- }
-
for (l = purple_plugins_get_protocols(); l != NULL; l = l->next) {
plugin = (PurplePlugin *)l->data;
diff --git a/libpurple/prpl.h b/libpurple/prpl.h
index 1f8888fa87..d96de3315b 100644
--- a/libpurple/prpl.h
+++ b/libpurple/prpl.h
@@ -1,8 +1,3 @@
-/**
- * @file prpl.h Protocol Plugin functions
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -24,34 +19,48 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-/* this file should be all that prpls need to include. therefore, by including
- * this file, they should get glib, proxy, purple_connection, prpl, etc. */
-
#ifndef _PURPLE_PRPL_H_
#define _PURPLE_PRPL_H_
+/**
+ * SECTION:prpl
+ * @section_id: libpurple-prpl
+ * @short_description: <filename>prpl.h</filename>
+ * @title: Protocol Plugin functions
+ */
+
+/* this file should be all that prpls need to include. therefore, by including
+ * this file, they should get glib, proxy, purple_connection, prpl, etc. */
typedef struct _PurplePluginProtocolInfo PurplePluginProtocolInfo;
-/** @copydoc _PurpleAttentionType */
+
+/**
+ * PurpleAttentionType:
+ *
+ * Represents "nudges" and "buzzes" that you may send to a buddy to attract
+ * their attention (or vice-versa).
+ */
typedef struct _PurpleAttentionType PurpleAttentionType;
/**************************************************************************/
-/** @name Basic Protocol Information */
+/* Basic Protocol Information */
/**************************************************************************/
+/**
+ * PurpleIconScaleRules:
+ * @PURPLE_ICON_SCALE_DISPLAY: We scale the icon when we display it
+ * @PURPLE_ICON_SCALE_SEND: We scale the icon before we send it to the server
+ */
typedef enum {
- PURPLE_ICON_SCALE_DISPLAY = 0x01, /**< We scale the icon when we display it */
- PURPLE_ICON_SCALE_SEND = 0x02 /**< We scale the icon before we send it to the server */
+ PURPLE_ICON_SCALE_DISPLAY = 0x01,
+ PURPLE_ICON_SCALE_SEND = 0x02
} PurpleIconScaleRules;
-/**
- * A description of a Buddy Icon specification. This tells Purple what kind of image file
- * it should give this prpl, and what kind of image file it should expect back.
- * Dimensions less than 1 should be ignored and the image not scaled.
- */
typedef struct _PurpleBuddyIconSpec PurpleBuddyIconSpec;
/**
+ * PurpleThumbnailSpec:
+ *
* A description of a file transfer thumbnail specification.
* This tells the UI if and what image formats the prpl support for file
* transfer thumbnails.
@@ -59,6 +68,8 @@ typedef struct _PurpleBuddyIconSpec PurpleBuddyIconSpec;
typedef struct _PurpleThumbnailSpec PurpleThumbnailSpec;
/**
+ * NO_BUDDY_ICONS:
+ *
* This \#define exists just to make it easier to fill out the buddy icon
* field in the prpl info struct for protocols that couldn't care less.
*/
@@ -68,11 +79,12 @@ typedef struct _PurpleThumbnailSpec PurpleThumbnailSpec;
#include <unistd.h>
#endif
-#include "blist.h"
-#include "conversation.h"
-#include "ft.h"
-#include "imgstore.h"
+#include "buddylist.h"
+#include "conversations.h"
+#include "xfer.h"
+#include "image.h"
#include "media.h"
+#include "message.h"
#include "notify.h"
#include "proxy.h"
#include "plugin.h"
@@ -81,155 +93,154 @@ typedef struct _PurpleThumbnailSpec PurpleThumbnailSpec;
#include "whiteboard.h"
-/** @copydoc PurpleBuddyIconSpec */
+/**
+ * PurpleBuddyIconSpec:
+ * @format: This is a comma-delimited list of image formats or %NULL if
+ * icons are not supported. Neither the core nor the prpl will
+ * actually check to see if the data it's given matches this;
+ * it's entirely up to the UI to do what it wants
+ * @min_width: Minimum width of this icon
+ * @min_height: Minimum height of this icon
+ * @max_width: Maximum width of this icon
+ * @max_height: Maximum height of this icon
+ * @max_filesize: Maximum size in bytes
+ * @scale_rules: How to stretch this icon
+ *
+ * A description of a Buddy Icon specification. This tells Purple what kind of
+ * image file it should give this prpl, and what kind of image file it should
+ * expect back. Dimensions less than 1 should be ignored and the image not
+ * scaled.
+ */
struct _PurpleBuddyIconSpec {
- /** This is a comma-delimited list of image formats or @c NULL if icons
- * are not supported. Neither the core nor the prpl will actually
- * check to see if the data it's given matches this; it's entirely up
- * to the UI to do what it wants
- */
char *format;
-
- int min_width; /**< Minimum width of this icon */
- int min_height; /**< Minimum height of this icon */
- int max_width; /**< Maximum width of this icon */
- int max_height; /**< Maximum height of this icon */
- size_t max_filesize; /**< Maximum size in bytes */
- PurpleIconScaleRules scale_rules; /**< How to stretch this icon */
+ int min_width;
+ int min_height;
+ int max_width;
+ int max_height;
+ size_t max_filesize;
+ PurpleIconScaleRules scale_rules;
};
-/** Represents an entry containing information that must be supplied by the
- * user when joining a chat.
+/**
+ * proto_chat_entry:
+ * @label: User-friendly name of the entry
+ * @identifier: Used by the PRPL to identify the option
+ * @required: True if it's required
+ * @is_int: True if the entry expects an integer
+ * @min: Minimum value in case of integer
+ * @max: Maximum value in case of integer
+ * @secret: True if the entry is secret (password)
+ *
+ * Represents an entry containing information that must be supplied by the
+ * user when joining a chat.
*/
struct proto_chat_entry {
- const char *label; /**< User-friendly name of the entry */
- const char *identifier; /**< Used by the PRPL to identify the option */
- gboolean required; /**< True if it's required */
- gboolean is_int; /**< True if the entry expects an integer */
- int min; /**< Minimum value in case of integer */
- int max; /**< Maximum value in case of integer */
- gboolean secret; /**< True if the entry is secret (password) */
-};
-
-/** Represents "nudges" and "buzzes" that you may send to a buddy to attract
- * their attention (or vice-versa).
- */
-struct _PurpleAttentionType
-{
- const char *name; /**< Shown in GUI elements */
- const char *incoming_description; /**< Shown when sent */
- const char *outgoing_description; /**< Shown when receied */
- const char *icon_name; /**< Icon to display (optional) */
- const char *unlocalized_name; /**< Unlocalized name for UIs needing it */
-
- /* Reserved fields for future purposes */
- gpointer _reserved2;
- gpointer _reserved3;
- gpointer _reserved4;
+ const char *label;
+ const char *identifier;
+ gboolean required;
+ gboolean is_int;
+ int min;
+ int max;
+ gboolean secret;
};
/**
+ * PurpleProtocolOptions:
+ * @OPT_PROTO_UNIQUE_CHATNAME: User names are unique to a chat and are not
+ * shared between rooms.<sbr/>
+ * XMPP lets you choose what name you want in chats, so it shouldn't
+ * be pulling the aliases from the buddy list for the chat list; it
+ * gets annoying.
+ * @OPT_PROTO_CHAT_TOPIC: Chat rooms have topics.<sbr/>
+ * IRC and XMPP support this.
+ * @OPT_PROTO_NO_PASSWORD: Don't require passwords for sign-in.<sbr/>
+ * Zephyr doesn't require passwords, so there's no need for a
+ * password prompt.
+ * @OPT_PROTO_MAIL_CHECK: Notify on new mail.<sbr/>
+ * MSN and Yahoo notify you when you have new mail.
+ * @OPT_PROTO_PASSWORD_OPTIONAL: Allow passwords to be optional.<sbr/>
+ * Passwords in IRC are optional, and are needed for certain
+ * functionality.
+ * @OPT_PROTO_USE_POINTSIZE: Allows font size to be specified in sane point
+ * size.<sbr/>
+ * Probably just XMPP and Y!M
+ * @OPT_PROTO_REGISTER_NOSCREENNAME: Set the Register button active even when
+ * the username has not been specified.<sbr/>
+ * Gadu-Gadu doesn't need a username to register new account (because
+ * usernames are assigned by the server).
+ * @OPT_PROTO_SLASH_COMMANDS_NATIVE: Indicates that slash commands are native
+ * to this protocol.<sbr/>
+ * Used as a hint that unknown commands should not be sent as
+ * messages.
+ * @OPT_PROTO_INVITE_MESSAGE: Indicates that this protocol supports sending a
+ * user-supplied message along with an invitation.
+ * @OPT_PROTO_AUTHORIZATION_GRANTED_MESSAGE: Indicates that this protocol
+ * supports sending a user-supplied message along with an
+ * authorization acceptance.
+ * @OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE: Indicates that this protocol
+ * supports sending a user-supplied message along with an
+ * authorization denial.
+ *
* Protocol options
*
- * These should all be stuff that some plugins can do and others can't.
+ * These should all be stuff that some protocols can do and others can't.
*/
-typedef enum
+typedef enum /*< flags >*/
{
- /**
- * User names are unique to a chat and are not shared between rooms.
- *
- * XMPP lets you choose what name you want in chats, so it shouldn't
- * be pulling the aliases from the buddy list for the chat list;
- * it gets annoying.
- */
- OPT_PROTO_UNIQUE_CHATNAME = 0x00000004,
-
- /**
- * Chat rooms have topics.
- *
- * IRC and XMPP support this.
- */
- OPT_PROTO_CHAT_TOPIC = 0x00000008,
-
- /**
- * Don't require passwords for sign-in.
- *
- * Zephyr doesn't require passwords, so there's no
- * need for a password prompt.
- */
- OPT_PROTO_NO_PASSWORD = 0x00000010,
-
- /**
- * Notify on new mail.
- *
- * MSN and Yahoo notify you when you have new mail.
- */
- OPT_PROTO_MAIL_CHECK = 0x00000020,
-
- /**
- * Images in IMs.
- *
- * Oscar lets you send images in direct IMs.
- */
- OPT_PROTO_IM_IMAGE = 0x00000040,
-
- /**
- * Allow passwords to be optional.
- *
- * Passwords in IRC are optional, and are needed for certain
- * functionality.
- */
- OPT_PROTO_PASSWORD_OPTIONAL = 0x00000080,
-
- /**
- * Allows font size to be specified in sane point size
- *
- * Probably just XMPP and Y!M
- */
- OPT_PROTO_USE_POINTSIZE = 0x00000100,
-
- /**
- * Set the Register button active even when the username has not
- * been specified.
- *
- * Gadu-Gadu doesn't need a username to register new account (because
- * usernames are assigned by the server).
- */
- OPT_PROTO_REGISTER_NOSCREENNAME = 0x00000200,
-
- /**
- * Indicates that slash commands are native to this protocol.
- * Used as a hint that unknown commands should not be sent as messages.
- * @since 2.1.0
- */
- OPT_PROTO_SLASH_COMMANDS_NATIVE = 0x00000400,
-
- /**
- * Indicates that this protocol supports sending a user-supplied message
- * along with an invitation.
- * @since 2.8.0
- */
- OPT_PROTO_INVITE_MESSAGE = 0x00000800
+ OPT_PROTO_UNIQUE_CHATNAME = 0x00000004,
+ OPT_PROTO_CHAT_TOPIC = 0x00000008,
+ OPT_PROTO_NO_PASSWORD = 0x00000010,
+ OPT_PROTO_MAIL_CHECK = 0x00000020,
+ OPT_PROTO_PASSWORD_OPTIONAL = 0x00000080,
+ OPT_PROTO_USE_POINTSIZE = 0x00000100,
+ OPT_PROTO_REGISTER_NOSCREENNAME = 0x00000200,
+ OPT_PROTO_SLASH_COMMANDS_NATIVE = 0x00000400,
+ OPT_PROTO_INVITE_MESSAGE = 0x00000800,
+ OPT_PROTO_AUTHORIZATION_GRANTED_MESSAGE = 0x00001000,
+ OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE = 0x00002000
} PurpleProtocolOptions;
/**
+ * PurplePluginProtocolInfo:
+ *
* A protocol plugin information structure.
*
* Every protocol plugin initializes this structure. It is the gateway
* between purple and the protocol plugin. Many of these callbacks can be
- * NULL. If a callback must be implemented, it has a comment indicating so.
+ * %NULL. If a callback must be implemented, it has a comment indicating so.
*/
struct _PurplePluginProtocolInfo
{
- PurpleProtocolOptions options; /**< Protocol options. */
+ /*
+ * The size of the PurplePluginProtocolInfo. This should always be sizeof(PurplePluginProtocolInfo).
+ * This allows adding more functions to this struct without requiring a major version bump.
+ */
+ unsigned long struct_size;
+
+ /* NOTE:
+ * If more functions are added, they should accessed using the following syntax:
+ *
+ * if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl, new_function))
+ * prpl->new_function(...);
+ *
+ * instead of
+ *
+ * if (prpl->new_function != NULL)
+ * prpl->new_function(...);
+ *
+ * The PURPLE_PROTOCOL_PLUGIN_HAS_FUNC macro can be used for the older member
+ * functions (e.g. login, send_im etc.) too.
+ */
- GList *user_splits; /**< A GList of PurpleAccountUserSplit */
- GList *protocol_options; /**< A GList of PurpleAccountOption */
+ PurpleProtocolOptions options; /* Protocol options. */
- PurpleBuddyIconSpec icon_spec; /**< The icon spec. */
+ GList *user_splits; /* A GList of PurpleAccountUserSplit */
+ GList *protocol_options; /* A GList of PurpleAccountOption */
- /**
+ PurpleBuddyIconSpec icon_spec; /* The icon spec. */
+
+ /*
* Returns the base icon name for the given buddy and account.
* If buddy is NULL and the account is non-NULL, it will return the
* name to use for the account's icon. If both are NULL, it will
@@ -239,67 +250,66 @@ struct _PurplePluginProtocolInfo
*/
const char *(*list_icon)(PurpleAccount *account, PurpleBuddy *buddy);
- /**
+ /*
* Fills the four char**'s with string identifiers for "emblems"
* that the UI will interpret and display as relevant
*/
const char *(*list_emblem)(PurpleBuddy *buddy);
- /**
+ /*
* Gets a short string representing this buddy's status. This will
* be shown on the buddy list.
*/
char *(*status_text)(PurpleBuddy *buddy);
- /**
+ /*
* Allows the prpl to add text to a buddy's tooltip.
*/
void (*tooltip_text)(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean full);
- /**
+ /*
* Returns a list of #PurpleStatusType which exist for this account;
* this must be implemented, and must add at least the offline and
* online states.
*/
GList *(*status_types)(PurpleAccount *account);
- /**
+ /*
* Returns a list of #PurpleMenuAction structs, which represent extra
- * actions to be shown in (for example) the right-click menu for @a
- * node.
+ * actions to be shown in (for example) the right-click menu for @node.
*/
GList *(*blist_node_menu)(PurpleBlistNode *node);
- /**
+ /*
* Returns a list of #proto_chat_entry structs, which represent
* information required by the PRPL to join a chat. libpurple will
* call join_chat along with the information filled by the user.
*
- * @return A list of #proto_chat_entry structs
+ * Returns: A list of #proto_chat_entry structs
*/
GList *(*chat_info)(PurpleConnection *);
- /**
+ /*
* Returns a hashtable which maps #proto_chat_entry struct identifiers
* to default options as strings based on chat_name. The resulting
* hashtable should be created with g_hash_table_new_full(g_str_hash,
* g_str_equal, NULL, g_free);. Use #get_chat_name if you instead need
* to extract a chat name from a hashtable.
*
- * @param chat_name The chat name to be turned into components
- * @return Hashtable containing the information extracted from chat_name
+ * @chat_name: The chat name to be turned into components
+ * Returns: Hashtable containing the information extracted from chat_name
*/
GHashTable *(*chat_info_defaults)(PurpleConnection *, const char *chat_name);
/* All the server-related functions */
- /** This must be implemented. */
+ /* This must be implemented. */
void (*login)(PurpleAccount *);
- /** This must be implemented. */
+ /* This must be implemented. */
void (*close)(PurpleConnection *);
- /**
+ /*
* This PRPL function should return a positive value on success.
* If the message is too big to be sent, return -E2BIG. If
* the account is not connected, return -ENOTCONN. If the
@@ -308,24 +318,22 @@ struct _PurplePluginProtocolInfo
* errno values, or just big something. If the message should
* not be echoed to the conversation window, return 0.
*/
- int (*send_im)(PurpleConnection *, const char *who,
- const char *message,
- PurpleMessageFlags flags);
+ int (*send_im)(PurpleConnection *, PurpleMessage *msg);
void (*set_info)(PurpleConnection *, const char *info);
- /**
- * @return If this protocol requires the PURPLE_TYPING message to
- * be sent repeatedly to signify that the user is still
- * typing, then the PRPL should return the number of
- * seconds to wait before sending a subsequent notification.
- * Otherwise the PRPL should return 0.
+ /*
+ * Returns: If this protocol requires the PURPLE_IM_TYPING message to
+ * be sent repeatedly to signify that the user is still
+ * typing, then the PRPL should return the number of
+ * seconds to wait before sending a subsequent notification.
+ * Otherwise the PRPL should return 0.
*/
- unsigned int (*send_typing)(PurpleConnection *, const char *name, PurpleTypingState state);
+ unsigned int (*send_typing)(PurpleConnection *, const char *name, PurpleIMTypingState state);
- /**
+ /*
* Should arrange for purple_notify_userinfo() to be called with
- * @a who's user info.
+ * @who 's user info.
*/
void (*get_info)(PurpleConnection *, const char *who);
void (*set_status)(PurpleAccount *account, PurpleStatus *status);
@@ -333,7 +341,8 @@ struct _PurplePluginProtocolInfo
void (*set_idle)(PurpleConnection *, int idletime);
void (*change_passwd)(PurpleConnection *, const char *old_pass,
const char *new_pass);
- /**
+
+ /*
* Add a buddy to a group on the server.
*
* This PRPL function may be called in situations in which the buddy is
@@ -341,11 +350,10 @@ struct _PurplePluginProtocolInfo
* authorization and the user is not already authorized to see the
* status of \a buddy, \a add_buddy should request authorization.
*
- * @deprecated Since 2.8.0, add_buddy_with_invite is preferred.
- * @see add_buddy_with_invite
+ * If authorization is required, then use the supplied invite message.
*/
- void (*add_buddy)(PurpleConnection *, PurpleBuddy *buddy, PurpleGroup *group);
- void (*add_buddies)(PurpleConnection *, GList *buddies, GList *groups);
+ void (*add_buddy)(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group, const char *message);
+ void (*add_buddies)(PurpleConnection *pc, GList *buddies, GList *groups, const char *message);
void (*remove_buddy)(PurpleConnection *, PurpleBuddy *buddy, PurpleGroup *group);
void (*remove_buddies)(PurpleConnection *, GList *buddies, GList *groups);
void (*add_permit)(PurpleConnection *, const char *name);
@@ -354,63 +362,53 @@ struct _PurplePluginProtocolInfo
void (*rem_deny)(PurpleConnection *, const char *name);
void (*set_permit_deny)(PurpleConnection *);
- /**
+ /*
* Called when the user requests joining a chat. Should arrange for
- * #serv_got_joined_chat to be called.
+ * #purple_serv_got_joined_chat to be called.
*
- * @param components A hashtable containing information required to
- * join the chat as described by the entries returned
- * by #chat_info. It may also be called when accepting
- * an invitation, in which case this matches the
- * data parameter passed to #serv_got_chat_invite.
+ * @components: A hashtable containing information required to
+ * join the chat as described by the entries returned
+ * by #chat_info. It may also be called when accepting
+ * an invitation, in which case this matches the
+ * data parameter passed to #purple_serv_got_chat_invite.
*/
void (*join_chat)(PurpleConnection *, GHashTable *components);
- /**
+ /*
* Called when the user refuses a chat invitation.
*
- * @param components A hashtable containing information required to
- * join the chat as passed to #serv_got_chat_invite.
+ * @components: A hashtable containing information required to
+ * join the chat as passed to #purple_serv_got_chat_invite.
*/
void (*reject_chat)(PurpleConnection *, GHashTable *components);
- /**
+ /*
* Returns a chat name based on the information in components. Use
* #chat_info_defaults if you instead need to generate a hashtable
* from a chat name.
*
- * @param components A hashtable containing information about the chat.
+ * @components: A hashtable containing information about the chat.
*/
char *(*get_chat_name)(GHashTable *components);
- /**
+ /*
* Invite a user to join a chat.
*
- * @param id The id of the chat to invite the user to.
- * @param message A message displayed to the user when the invitation
- * is received.
- * @param who The name of the user to send the invation to.
+ * @id: The id of the chat to invite the user to.
+ * @message: A message displayed to the user when the invitation
+ * is received.
+ * @who: The name of the user to send the invation to.
*/
void (*chat_invite)(PurpleConnection *, int id,
const char *message, const char *who);
- /**
+ /*
* Called when the user requests leaving a chat.
*
- * @param id The id of the chat to leave
+ * @id: The id of the chat to leave
*/
void (*chat_leave)(PurpleConnection *, int id);
- /**
- * Send a whisper to a user in a chat.
- *
- * @param id The id of the chat.
- * @param who The name of the user to send the whisper to.
- * @param message The message of the whisper.
- */
- void (*chat_whisper)(PurpleConnection *, int id,
- const char *who, const char *message);
-
- /**
+ /*
* Send a message to a chat.
* This PRPL function should return a positive value on success.
* If the message is too big to be sent, return -E2BIG. If
@@ -419,45 +417,39 @@ struct _PurplePluginProtocolInfo
* some other negative value. You can use one of the valid
* errno values, or just big something.
*
- * @param id The id of the chat to send the message to.
- * @param message The message to send to the chat.
- * @param flags A bitwise OR of #PurpleMessageFlags representing
- * message flags.
- * @return A positive number or 0 in case of success,
- * a negative error number in case of failure.
+ * @id: The id of the chat to send the message to.
+ * @msg: The message to send to the chat.
+ *
+ * Returns: A positive number or 0 in case of success,
+ * a negative error number in case of failure.
*/
- int (*chat_send)(PurpleConnection *, int id, const char *message, PurpleMessageFlags flags);
+ int (*chat_send)(PurpleConnection *, int id, PurpleMessage *msg);
- /** If implemented, this will be called regularly for this prpl's
- * active connections. You'd want to do this if you need to repeatedly
- * send some kind of keepalive packet to the server to avoid being
- * disconnected. ("Regularly" is defined by
- * <code>KEEPALIVE_INTERVAL</code> in <tt>libpurple/connection.c</tt>.)
+ /* If implemented, this will be called regularly for this prpl's
+ * active connections. You'd want to do this if you need to repeatedly
+ * send some kind of keepalive packet to the server to avoid being
+ * disconnected. ("Regularly" is defined by
+ * KEEPALIVE_INTERVAL in libpurple/connection.c.)
*/
void (*keepalive)(PurpleConnection *);
- /** new user registration */
+ /* new user registration */
void (*register_user)(PurpleAccount *);
- /**
- * @deprecated Use #PurplePluginProtocolInfo.get_info instead.
+ /*
+ * Deprecated: Use PurplePluginProtocolInfo.get_info instead.
*/
void (*get_cb_info)(PurpleConnection *, int, const char *who);
- /**
- * @deprecated Use #PurplePluginProtocolInfo.get_cb_real_name and
- * #PurplePluginProtocolInfo.status_text instead.
- */
- void (*get_cb_away)(PurpleConnection *, int, const char *who);
- /** save/store buddy's alias on server list/roster */
+ /* save/store buddy's alias on server list/roster */
void (*alias_buddy)(PurpleConnection *, const char *who,
const char *alias);
- /** change a buddy's group on a server list/roster */
+ /* change a buddy's group on a server list/roster */
void (*group_buddy)(PurpleConnection *, const char *who,
const char *old_group, const char *new_group);
- /** rename a group on a server list/roster */
+ /* rename a group on a server list/roster */
void (*rename_group)(PurpleConnection *, const char *old_name,
PurpleGroup *group, GList *moved_buddies);
@@ -465,29 +457,39 @@ struct _PurplePluginProtocolInfo
void (*convo_closed)(PurpleConnection *, const char *who);
- /**
- * Convert the username @a who to its canonical form. (For example,
- * AIM treats "fOo BaR" and "foobar" as the same user; this function
- * should return the same normalized string for both of those.)
+ /*
+ * Convert the username @who to its canonical form. Also checks for
+ * validity.
+ *
+ * For example, AIM treats "fOo BaR" and "foobar" as the same user; this
+ * function should return the same normalized string for both of those.
+ * On the other hand, both of these are invalid for protocols with
+ * number-based usernames, so function should return NULL in such case.
+ *
+ * @account: The account the username is related to. Can
+ * be NULL.
+ * @who: The username to convert.
+ * Returns: Normalized username, or NULL, if it's invalid.
*/
- const char *(*normalize)(const PurpleAccount *, const char *who);
+ const char *(*normalize)(const PurpleAccount *account, const char *who);
- /**
- * Set the buddy icon for the given connection to @a img. The prpl
- * does NOT own a reference to @a img; if it needs one, it must
- * #purple_imgstore_ref(@a img) itself.
+ /*
+ * Set the buddy icon for the given connection to @img. The prpl
+ * does NOT own a reference to @img; if it needs one, it must
+ * #g_object_ref(@img) itself.
*/
- void (*set_buddy_icon)(PurpleConnection *, PurpleStoredImage *img);
+ void (*set_buddy_icon)(PurpleConnection *, PurpleImage *img);
void (*remove_group)(PurpleConnection *gc, PurpleGroup *group);
- /** Gets the real name of a participant in a chat. For example, on
- * XMPP this turns a chat room nick <tt>foo</tt> into
- * <tt>room\@server/foo</tt>
- * @param gc the connection on which the room is.
- * @param id the ID of the chat room.
- * @param who the nickname of the chat participant.
- * @return the real name of the participant. This string must be
+ /* Gets the real name of a participant in a chat. For example, on
+ * XMPP this turns a chat room nick foo into room\@server/foo
+ *
+ * @gc: the connection on which the room is.
+ * @id: the ID of the chat room.
+ * @who: the nickname of the chat participant.
+ *
+ * Returns: the real name of the participant. This string must be
* freed by the caller.
*/
char *(*get_cb_real_name)(PurpleConnection *gc, int id, const char *who);
@@ -506,21 +508,21 @@ struct _PurplePluginProtocolInfo
void (*send_file)(PurpleConnection *, const char *who, const char *filename);
PurpleXfer *(*new_xfer)(PurpleConnection *, const char *who);
- /** Checks whether offline messages to @a buddy are supported.
- * @return @c TRUE if @a buddy can be sent messages while they are
- * offline, or @c FALSE if not.
+ /* Checks whether offline messages to @buddy are supported.
+ * Returns: TRUE if @buddy can be sent messages while they are
+ * offline, or FALSE if not.
*/
gboolean (*offline_message)(const PurpleBuddy *buddy);
PurpleWhiteboardPrplOps *whiteboard_prpl_ops;
- /** For use in plugins that may understand the underlying protocol */
+ /* For use in plugins that may understand the underlying protocol */
int (*send_raw)(PurpleConnection *gc, const char *buf, int len);
/* room list serialize */
char *(*roomlist_room_serialize)(PurpleRoomlistRoom *room);
- /** Remove the user from the server. The account can either be
+ /* Remove the user from the server. The account can either be
* connected or disconnected. After the removal is finished, the
* connection will stay open and has to be closed!
*/
@@ -533,67 +535,48 @@ struct _PurplePluginProtocolInfo
gboolean (*send_attention)(PurpleConnection *gc, const char *username, guint type);
GList *(*get_attention_types)(PurpleAccount *acct);
- /**
- * The size of the PurplePluginProtocolInfo. This should always be sizeof(PurplePluginProtocolInfo).
- * This allows adding more functions to this struct without requiring a major version bump.
- */
- unsigned long struct_size;
-
- /* NOTE:
- * If more functions are added, they should accessed using the following syntax:
- *
- * if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl, new_function))
- * prpl->new_function(...);
- *
- * instead of
- *
- * if (prpl->new_function != NULL)
- * prpl->new_function(...);
- *
- * The PURPLE_PROTOCOL_PLUGIN_HAS_FUNC macro can be used for the older member
- * functions (e.g. login, send_im etc.) too.
- */
-
- /** This allows protocols to specify additional strings to be used for
+ /* This allows protocols to specify additional strings to be used for
* various purposes. The idea is to stuff a bunch of strings in this hash
* table instead of expanding the struct for every addition. This hash
* table is allocated every call and MUST be unrefed by the caller.
*
- * @param account The account to specify. This can be NULL.
- * @return The protocol's string hash table. The hash table should be
- * destroyed by the caller when it's no longer needed.
+ * @account: The account to specify. This can be NULL.
+ *
+ * Returns : The protocol's string hash table. The hash table should be
+ * destroyed by the caller when it's no longer needed.
*/
GHashTable *(*get_account_text_table)(PurpleAccount *account);
- /**
+ /*
* Initiate a media session with the given contact.
*
- * @param account The account to initiate the media session on.
- * @param who The remote user to initiate the session with.
- * @param type The type of media session to initiate.
- * @return TRUE if the call succeeded else FALSE. (Doesn't imply the media session or stream will be successfully created)
+ * @account: The account to initiate the media session on.
+ * @who: The remote user to initiate the session with.
+ * @type: The type of media session to initiate.
+ *
+ * Returns: TRUE if the call succeeded else FALSE. (Doesn't imply the media session or stream will be successfully created)
*/
gboolean (*initiate_media)(PurpleAccount *account, const char *who,
PurpleMediaSessionType type);
- /**
+ /*
* Checks to see if the given contact supports the given type of media session.
*
- * @param account The account the contact is on.
- * @param who The remote user to check for media capability with.
- * @return The media caps the contact supports.
+ * @account: The account the contact is on.
+ * @who: The remote user to check for media capability with.
+ *
+ * Returns: The media caps the contact supports.
*/
PurpleMediaCaps (*get_media_caps)(PurpleAccount *account,
const char *who);
- /**
+ /*
* Returns an array of "PurpleMood"s, with the last one having
- * "mood" set to @c NULL.
- * @since 2.7.0
+ * "mood" set to NULL.
*/
PurpleMood *(*get_moods)(PurpleAccount *account);
- /**
+ /*
* Set the user's "friendly name" (or alias or nickname or
* whatever term you want to call it) on the server. The
* protocol plugin should call success_cb or failure_cb
@@ -601,57 +584,60 @@ struct _PurplePluginProtocolInfo
* call one of the callbacks from an idle/0-second timeout) depending
* on if the nickname is set successfully.
*
- * @param gc The connection for which to set an alias
- * @param alias The new server-side alias/nickname for this account,
+ * See purple_account_set_public_alias().
+ *
+ * @gc: The connection for which to set an alias
+ * @alias: The new server-side alias/nickname for this account,
* or NULL to unset the alias/nickname (or return it to
* a protocol-specific "default").
- * @param success_cb Callback to be called if the public alias is set
- * @param failure_cb Callback to be called if setting the public alias
+ * @success_cb: Callback to be called if the public alias is set
+ * @failure_cb: Callback to be called if setting the public alias
* fails
- * @see purple_account_set_public_alias
- * @since 2.7.0
*/
void (*set_public_alias)(PurpleConnection *gc, const char *alias,
PurpleSetPublicAliasSuccessCallback success_cb,
PurpleSetPublicAliasFailureCallback failure_cb);
- /**
+ /*
* Retrieve the user's "friendly name" as set on the server.
* The protocol plugin should call success_cb or failure_cb
* *asynchronously* (even if it knows immediately that the get will fail,
* call one of the callbacks from an idle/0-second timeout) depending
* on if the nickname is retrieved.
*
- * @param gc The connection for which to retireve the alias
- * @param success_cb Callback to be called with the retrieved alias
- * @param failure_cb Callback to be called if the prpl is unable to
- * retrieve the alias
- * @see purple_account_get_public_alias
- * @since 2.7.0
+ * See purple_account_get_public_alias().
+ *
+ * @gc: The connection for which to retireve the alias
+ * @success_cb: Callback to be called with the retrieved alias
+ * @failure_cb: Callback to be called if the prpl is unable to
+ * retrieve the alias
*/
void (*get_public_alias)(PurpleConnection *gc,
PurpleGetPublicAliasSuccessCallback success_cb,
PurpleGetPublicAliasFailureCallback failure_cb);
- /**
- * Add a buddy to a group on the server.
+ /*
+ * Gets the maximum message size in bytes for the conversation.
*
- * This PRPL function may be called in situations in which the buddy is
- * already in the specified group. If the protocol supports
- * authorization and the user is not already authorized to see the
- * status of \a buddy, \a add_buddy should request authorization.
+ * It may depend on connection-specific or conversation-specific
+ * variables, like channel or buddy's name length.
*
- * If authorization is required, then use the supplied invite message.
+ * This value is intended for plaintext message, the exact value may be
+ * lower because of:
+ * - used newlines (some protocols count them as more than one byte),
+ * - formatting,
+ * - used special characters.
+ *
+ * @conv: The conversation to query, or NULL to get safe minimum
+ * for the protocol.
*
- * @since 2.8.0
+ * Returns: Maximum message size, 0 if unspecified, -1 for infinite.
*/
- void (*add_buddy_with_invite)(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group, const char *message);
- void (*add_buddies_with_invite)(PurpleConnection *pc, GList *buddies, GList *groups, const char *message);
+ gssize (*get_max_message_size)(PurpleConversation *conv);
};
#define PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl, member) \
- (((G_STRUCT_OFFSET(PurplePluginProtocolInfo, member) < G_STRUCT_OFFSET(PurplePluginProtocolInfo, struct_size)) \
- || (G_STRUCT_OFFSET(PurplePluginProtocolInfo, member) < prpl->struct_size)) && \
- prpl->member != NULL)
+ ((prpl != NULL) && (G_STRUCT_OFFSET(PurplePluginProtocolInfo, member) < \
+ prpl->struct_size && prpl->member != NULL))
#define PURPLE_IS_PROTOCOL_PLUGIN(plugin) \
@@ -660,356 +646,374 @@ struct _PurplePluginProtocolInfo
#define PURPLE_PLUGIN_PROTOCOL_INFO(plugin) \
((PurplePluginProtocolInfo *)(plugin)->info->extra_info)
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name Attention Type API */
+/* Attention Type API */
/**************************************************************************/
-/*@{*/
/**
+ * purple_attention_type_new:
+ * @ulname: A non-localized string that can be used by UIs in need of such
+ * non-localized strings. This should be the same as @name,
+ * without localization.
+ * @name: A localized string that the UI may display for the event. This
+ * should be the same string as @ulname, with localization.
+ * @inc_desc: A localized description shown when the event is received.
+ * @out_desc: A localized description shown when the event is sent.
+ *
* Creates a new #PurpleAttentionType object and sets its mandatory parameters.
*
- * @param ulname A non-localized string that can be used by UIs in need of such
- * non-localized strings. This should be the same as @a name,
- * without localization.
- * @param name A localized string that the UI may display for the event. This
- * should be the same string as @a ulname, with localization.
- * @param inc_desc A localized description shown when the event is received.
- * @param out_desc A localized description shown when the event is sent.
- * @return A pointer to the new object.
- * @since 2.4.0
+ * Returns: A pointer to the new object.
*/
PurpleAttentionType *purple_attention_type_new(const char *ulname, const char *name,
const char *inc_desc, const char *out_desc);
/**
- * Sets the displayed name of the attention-demanding event.
- *
- * @param type The attention type.
- * @param name The localized name that will be displayed by UIs. This should be
+ * purple_attention_type_set_name:
+ * @type: The attention type.
+ * @name: The localized name that will be displayed by UIs. This should be
* the same string given as the unlocalized name, but with
* localization.
- * @since 2.4.0
+ *
+ * Sets the displayed name of the attention-demanding event.
*/
void purple_attention_type_set_name(PurpleAttentionType *type, const char *name);
/**
+ * purple_attention_type_set_incoming_desc:
+ * @type: The attention type.
+ * @desc: The localized description for incoming events.
+ *
* Sets the description of the attention-demanding event shown in conversations
* when the event is received.
- *
- * @param type The attention type.
- * @param desc The localized description for incoming events.
- * @since 2.4.0
*/
void purple_attention_type_set_incoming_desc(PurpleAttentionType *type, const char *desc);
/**
+ * purple_attention_type_set_outgoing_desc:
+ * @type: The attention type.
+ * @desc: The localized description for outgoing events.
+ *
* Sets the description of the attention-demanding event shown in conversations
* when the event is sent.
- *
- * @param type The attention type.
- * @param desc The localized description for outgoing events.
- * @since 2.4.0
*/
void purple_attention_type_set_outgoing_desc(PurpleAttentionType *type, const char *desc);
/**
+ * purple_attention_type_set_icon_name:
+ * @type: The attention type.
+ * @name: The icon's name.
+ *
* Sets the name of the icon to display for the attention event; this is optional.
*
- * @param type The attention type.
- * @param name The icon's name.
- * @note Icons are optional for attention events.
- * @since 2.4.0
+ * Note: Icons are optional for attention events.
*/
void purple_attention_type_set_icon_name(PurpleAttentionType *type, const char *name);
/**
+ * purple_attention_type_set_unlocalized_name:
+ * @type: The attention type.
+ * @ulname: The unlocalized name. This should be the same string given as
+ * the localized name, but without localization.
+ *
* Sets the unlocalized name of the attention event; some UIs may need this,
* thus it is required.
- *
- * @param type The attention type.
- * @param ulname The unlocalized name. This should be the same string given as
- * the localized name, but without localization.
- * @since 2.4.0
*/
void purple_attention_type_set_unlocalized_name(PurpleAttentionType *type, const char *ulname);
/**
+ * purple_attention_type_get_name:
+ * @type: The attention type.
+ *
* Get the attention type's name as displayed by the UI.
*
- * @param type The attention type.
- * @return The name.
- * @since 2.4.0
+ * Returns: The name.
*/
const char *purple_attention_type_get_name(const PurpleAttentionType *type);
/**
+ * purple_attention_type_get_incoming_desc:
+ * @type: The attention type.
+ *
* Get the attention type's description shown when the event is received.
*
- * @param type The attention type.
- * @return The description.
- * @since 2.4.0
+ * Returns: The description.
*/
const char *purple_attention_type_get_incoming_desc(const PurpleAttentionType *type);
/**
+ * purple_attention_type_get_outgoing_desc:
+ * @type: The attention type.
+ *
* Get the attention type's description shown when the event is sent.
*
- * @param type The attention type.
- * @return The description.
- * @since 2.4.0
+ * Returns: The description.
*/
const char *purple_attention_type_get_outgoing_desc(const PurpleAttentionType *type);
/**
+ * purple_attention_type_get_icon_name:
+ * @type: The attention type.
+ *
* Get the attention type's icon name.
*
- * @param type The attention type.
- * @return The icon name or @c NULL if unset/empty.
- * @note Icons are optional for attention events.
- * @since 2.4.0
+ * Note: Icons are optional for attention events.
+ *
+ * Returns: The icon name or %NULL if unset/empty.
*/
const char *purple_attention_type_get_icon_name(const PurpleAttentionType *type);
/**
+ * purple_attention_type_get_unlocalized_name:
+ * @type: The attention type
+ *
* Get the attention type's unlocalized name; this is useful for some UIs.
*
- * @param type The attention type
- * @return The unlocalized name.
- * @since 2.4.0
+ * Returns: The unlocalized name.
*/
const char *purple_attention_type_get_unlocalized_name(const PurpleAttentionType *type);
-/*@}*/
-
/**************************************************************************/
-/** @name Protocol Plugin API */
+/* Protocol Plugin API */
/**************************************************************************/
-/*@{*/
/**
+ * purple_prpl_got_account_idle:
+ * @account: The account.
+ * @idle: The user's idle state.
+ * @idle_time: The user's idle time.
+ *
* Notifies Purple that our account's idle state and time have changed.
*
* This is meant to be called from protocol plugins.
- *
- * @param account The account.
- * @param idle The user's idle state.
- * @param idle_time The user's idle time.
*/
void purple_prpl_got_account_idle(PurpleAccount *account, gboolean idle,
time_t idle_time);
/**
+ * purple_prpl_got_account_login_time:
+ * @account: The account the user is on.
+ * @login_time: The user's log-in time.
+ *
* Notifies Purple of our account's log-in time.
*
* This is meant to be called from protocol plugins.
- *
- * @param account The account the user is on.
- * @param login_time The user's log-in time.
*/
void purple_prpl_got_account_login_time(PurpleAccount *account, time_t login_time);
/**
+ * purple_prpl_got_account_status:
+ * @account: The account the user is on.
+ * @status_id: The status ID.
+ * @...: A NULL-terminated list of attribute IDs and values,
+ * beginning with the value for @attr_id.
+ *
* Notifies Purple that our account's status has changed.
*
* This is meant to be called from protocol plugins.
- *
- * @param account The account the user is on.
- * @param status_id The status ID.
- * @param ... A NULL-terminated list of attribute IDs and values,
- * beginning with the value for @a attr_id.
*/
void purple_prpl_got_account_status(PurpleAccount *account,
const char *status_id, ...) G_GNUC_NULL_TERMINATED;
/**
+ * purple_prpl_got_account_actions:
+ * @account: The account.
+ *
* Notifies Purple that our account's actions have changed. This is only
* called after the initial connection. Emits the account-actions-changed
* signal.
*
* This is meant to be called from protocol plugins.
*
- * @param account The account.
- *
- * @see account-actions-changed
- * @since 2.6.0
+ * See <link linkend="accounts-account-actions-changed"><literal>"account-actions-changed"</literal></link>
*/
void purple_prpl_got_account_actions(PurpleAccount *account);
/**
- * Notifies Purple that a buddy's idle state and time have changed.
- *
- * This is meant to be called from protocol plugins.
- *
- * @param account The account the user is on.
- * @param name The name of the buddy.
- * @param idle The user's idle state.
- * @param idle_time The user's idle time. This is the time at
+ * purple_prpl_got_user_idle:
+ * @account: The account the user is on.
+ * @name: The name of the buddy.
+ * @idle: The user's idle state.
+ * @idle_time: The user's idle time. This is the time at
* which the user became idle, in seconds since
* the epoch. If the PRPL does not know this value
* then it should pass 0.
+ *
+ * Notifies Purple that a buddy's idle state and time have changed.
+ *
+ * This is meant to be called from protocol plugins.
*/
void purple_prpl_got_user_idle(PurpleAccount *account, const char *name,
gboolean idle, time_t idle_time);
/**
+ * purple_prpl_got_user_login_time:
+ * @account: The account the user is on.
+ * @name: The name of the buddy.
+ * @login_time: The user's log-in time.
+ *
* Notifies Purple of a buddy's log-in time.
*
* This is meant to be called from protocol plugins.
- *
- * @param account The account the user is on.
- * @param name The name of the buddy.
- * @param login_time The user's log-in time.
*/
void purple_prpl_got_user_login_time(PurpleAccount *account, const char *name,
time_t login_time);
/**
+ * purple_prpl_got_user_status:
+ * @account: The account the user is on.
+ * @name: The name of the buddy.
+ * @status_id: The status ID.
+ * @...: A NULL-terminated list of attribute IDs and values,
+ * beginning with the value for @attr_id.
+ *
* Notifies Purple that a buddy's status has been activated.
*
* This is meant to be called from protocol plugins.
- *
- * @param account The account the user is on.
- * @param name The name of the buddy.
- * @param status_id The status ID.
- * @param ... A NULL-terminated list of attribute IDs and values,
- * beginning with the value for @a attr_id.
*/
void purple_prpl_got_user_status(PurpleAccount *account, const char *name,
const char *status_id, ...) G_GNUC_NULL_TERMINATED;
/**
+ * purple_prpl_got_user_status_deactive:
+ * @account: The account the user is on.
+ * @name: The name of the buddy.
+ * @status_id: The status ID.
+ *
* Notifies libpurple that a buddy's status has been deactivated
*
* This is meant to be called from protocol plugins.
- *
- * @param account The account the user is on.
- * @param name The name of the buddy.
- * @param status_id The status ID.
*/
void purple_prpl_got_user_status_deactive(PurpleAccount *account, const char *name,
const char *status_id);
/**
- * Informs the server that our account's status changed.
- *
- * @param account The account the user is on.
- * @param old_status The previous status.
- * @param new_status The status that was activated, or deactivated
+ * purple_prpl_change_account_status:
+ * @account: The account the user is on.
+ * @old_status: The previous status.
+ * @new_status: The status that was activated, or deactivated
* (in the case of independent statuses).
+ *
+ * Informs the server that our account's status changed.
*/
void purple_prpl_change_account_status(PurpleAccount *account,
PurpleStatus *old_status,
PurpleStatus *new_status);
/**
- * Retrieves the list of stock status types from a prpl.
+ * purple_prpl_get_statuses:
+ * @account: The account the user is on.
+ * @presence: The presence for which we're going to get statuses
*
- * @param account The account the user is on.
- * @param presence The presence for which we're going to get statuses
+ * Retrieves the list of stock status types from a prpl.
*
- * @return List of statuses
+ * Returns: List of statuses
*/
GList *purple_prpl_get_statuses(PurpleAccount *account, PurplePresence *presence);
/**
- * Send an attention request message.
- *
- * @param gc The connection to send the message on.
- * @param who Whose attention to request.
- * @param type_code An index into the prpl's attention_types list determining the type
+ * purple_prpl_send_attention:
+ * @gc: The connection to send the message on.
+ * @who: Whose attention to request.
+ * @type_code: An index into the prpl's attention_types list determining the type
* of the attention request command to send. 0 if prpl only defines one
- * (for example, Yahoo and MSN), but some protocols define more (MySpaceIM).
+ * (for example, Yahoo and MSN), but protocols are allowed to define more.
+ *
+ * Send an attention request message.
*
* Note that you can't send arbitrary PurpleAttentionType's, because there is
* only a fixed set of attention commands.
- *
- * @since 2.5.0
*/
void purple_prpl_send_attention(PurpleConnection *gc, const char *who, guint type_code);
/**
- * Process an incoming attention message.
- *
- * @param gc The connection that received the attention message.
- * @param who Who requested your attention.
- * @param type_code An index into the prpl's attention_types list determining the type
+ * purple_prpl_got_attention:
+ * @gc: The connection that received the attention message.
+ * @who: Who requested your attention.
+ * @type_code: An index into the prpl's attention_types list determining the type
* of the attention request command to send.
*
- * @since 2.5.0
+ * Process an incoming attention message.
*/
void purple_prpl_got_attention(PurpleConnection *gc, const char *who, guint type_code);
/**
- * Process an incoming attention message in a chat.
- *
- * @param gc The connection that received the attention message.
- * @param id The chat id.
- * @param who Who requested your attention.
- * @param type_code An index into the prpl's attention_types list determining the type
+ * purple_prpl_got_attention_in_chat:
+ * @gc: The connection that received the attention message.
+ * @id: The chat id.
+ * @who: Who requested your attention.
+ * @type_code: An index into the prpl's attention_types list determining the type
* of the attention request command to send.
*
- * @since 2.5.0
+ * Process an incoming attention message in a chat.
*/
void purple_prpl_got_attention_in_chat(PurpleConnection *gc, int id, const char *who, guint type_code);
/**
- * Determines if the contact supports the given media session type.
+ * purple_prpl_get_media_caps:
+ * @account: The account the user is on.
+ * @who: The name of the contact to check capabilities for.
*
- * @param account The account the user is on.
- * @param who The name of the contact to check capabilities for.
+ * Determines if the contact supports the given media session type.
*
- * @return The media caps the contact supports.
+ * Returns: The media caps the contact supports.
*/
PurpleMediaCaps purple_prpl_get_media_caps(PurpleAccount *account,
const char *who);
/**
- * Initiates a media session with the given contact.
+ * purple_prpl_initiate_media:
+ * @account: The account the user is on.
+ * @who: The name of the contact to start a session with.
+ * @type: The type of media session to start.
*
- * @param account The account the user is on.
- * @param who The name of the contact to start a session with.
- * @param type The type of media session to start.
+ * Initiates a media session with the given contact.
*
- * @return TRUE if the call succeeded else FALSE. (Doesn't imply the media session or stream will be successfully created)
+ * Returns: TRUE if the call succeeded else FALSE. (Doesn't imply the media
+ * session or stream will be successfully created)
*/
gboolean purple_prpl_initiate_media(PurpleAccount *account,
const char *who,
PurpleMediaSessionType type);
/**
+ * purple_prpl_got_media_caps:
+ * @account: The account the user is on.
+ * @who: The name of the contact for which capabilities have been received.
+ *
* Signals that the prpl received capabilities for the given contact.
*
* This function is intended to be used only by prpls.
- *
- * @param account The account the user is on.
- * @param who The name of the contact for which capabilities have been received.
- * @since 2.7.0
*/
void purple_prpl_got_media_caps(PurpleAccount *account, const char *who);
-/*@}*/
+/**
+ * purple_prpl_get_max_message_size:
+ * @prpl: The protocol plugin to query.
+ *
+ * Gets the safe maximum message size in bytes for the protocol plugin.
+ *
+ * See #PurplePluginProtocolInfo's #get_max_message_size
+ *
+ * Returns: Maximum message size, 0 if unspecified, -1 for infinite.
+ */
+gssize
+purple_prpl_get_max_message_size(PurplePlugin *prpl);
/**************************************************************************/
-/** @name Protocol Plugin Subsystem API */
+/* Protocol Plugin Subsystem API */
/**************************************************************************/
-/*@{*/
/**
- * Finds a protocol plugin structure of the specified type.
+ * purple_find_prpl:
+ * @id: The protocol plugin;
*
- * @param id The protocol plugin;
+ * Finds a protocol plugin structure of the specified type.
*/
PurplePlugin *purple_find_prpl(const char *id);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#ifdef __COVERITY__
#undef PURPLE_PROTOCOL_PLUGIN_HAS_FUNC
diff --git a/libpurple/purple-2-uninstalled.pc.in b/libpurple/purple-3-uninstalled.pc.in
index 90e0275cee..90e0275cee 100644
--- a/libpurple/purple-2-uninstalled.pc.in
+++ b/libpurple/purple-3-uninstalled.pc.in
diff --git a/libpurple/purple-2.pc.in b/libpurple/purple-3.pc.in
index 9506b6f5be..281a522a96 100644
--- a/libpurple/purple-2.pc.in
+++ b/libpurple/purple-3.pc.in
@@ -1,7 +1,7 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
-includedir=@includedir@
+includedir=@includedir@/libpurple
datarootdir=@datarootdir@
datadir=@datadir@
sysconfdir=@sysconfdir@
diff --git a/libpurple/purple-client.c b/libpurple/purple-client.c
index 1cada6a6d5..a0d4f48372 100644
--- a/libpurple/purple-client.c
+++ b/libpurple/purple-client.c
@@ -15,7 +15,7 @@ static DBusGProxy *purple_proxy;
static GList *garray_int_to_glist(GArray *array)
{
GList *list = NULL;
- int i;
+ gsize i;
for (i = 0; i < array->len; i++)
list = g_list_append(list, GINT_TO_POINTER(g_array_index(array,gint,i)));
@@ -27,7 +27,7 @@ static GList *garray_int_to_glist(GArray *array)
static GSList *garray_int_to_gslist(GArray *array)
{
GSList *list = NULL;
- int i;
+ gsize i;
for (i = 0; i < array->len; i++)
list = g_slist_append(list, GINT_TO_POINTER(g_array_index(array,gint,i)));
@@ -76,9 +76,9 @@ void purple_init(void)
lose_gerror ("Couldn't connect to session bus", error);
purple_proxy = dbus_g_proxy_new_for_name (bus,
- DBUS_SERVICE_PURPLE,
- DBUS_PATH_PURPLE,
- DBUS_INTERFACE_PURPLE);
+ PURPLE_DBUS_SERVICE,
+ PURPLE_DBUS_PATH,
+ PURPLE_DBUS_INTERFACE);
if (!purple_proxy)
lose_gerror ("Couldn't connect to the Purple Service", error);
diff --git a/libpurple/purple-client.h b/libpurple/purple-client.h
index 87378d35ce..5746dbe841 100644
--- a/libpurple/purple-client.h
+++ b/libpurple/purple-client.h
@@ -2,6 +2,7 @@
#define _PURPLE_CLIENT_H_INCLUDED_
#include <glib.h>
+#include <glib-object.h>
#include "purple-client-bindings.h"
G_BEGIN_DECLS
diff --git a/libpurple/purple-socket.c b/libpurple/purple-socket.c
new file mode 100644
index 0000000000..cf821818f9
--- /dev/null
+++ b/libpurple/purple-socket.c
@@ -0,0 +1,416 @@
+/* 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 "purple-socket.h"
+
+#include "internal.h"
+
+#include "debug.h"
+#include "proxy.h"
+#include "sslconn.h"
+
+typedef enum {
+ PURPLE_SOCKET_STATE_DISCONNECTED = 0,
+ PURPLE_SOCKET_STATE_CONNECTING,
+ PURPLE_SOCKET_STATE_CONNECTED,
+ PURPLE_SOCKET_STATE_ERROR
+} PurpleSocketState;
+
+struct _PurpleSocket
+{
+ PurpleConnection *gc;
+ gchar *host;
+ int port;
+ gboolean is_tls;
+ GHashTable *data;
+
+ PurpleSocketState state;
+
+ PurpleSslConnection *tls_connection;
+ PurpleProxyConnectData *raw_connection;
+ int fd;
+ guint inpa;
+
+ PurpleSocketConnectCb cb;
+ gpointer cb_data;
+};
+
+static GHashTable *handles = NULL;
+
+static void
+handle_add(PurpleSocket *ps)
+{
+ PurpleConnection *gc = ps->gc;
+ GSList *l;
+
+ l = g_hash_table_lookup(handles, gc);
+ l = g_slist_prepend(l, ps);
+ g_hash_table_insert(handles, gc, l);
+}
+
+static void
+handle_remove(PurpleSocket *ps)
+{
+ PurpleConnection *gc = ps->gc;
+ GSList *l;
+
+ l = g_hash_table_lookup(handles, gc);
+ l = g_slist_remove(l, ps);
+ g_hash_table_insert(handles, gc, l);
+}
+
+void
+_purple_socket_init(void)
+{
+ handles = g_hash_table_new(g_direct_hash, g_direct_equal);
+}
+
+void
+_purple_socket_uninit(void)
+{
+ g_hash_table_destroy(handles);
+ handles = NULL;
+}
+
+PurpleSocket *
+purple_socket_new(PurpleConnection *gc)
+{
+ PurpleSocket *ps = g_new0(PurpleSocket, 1);
+
+ ps->gc = gc;
+ ps->fd = -1;
+ ps->port = -1;
+ ps->data = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+
+ handle_add(ps);
+
+ return ps;
+}
+
+PurpleConnection *
+purple_socket_get_connection(PurpleSocket *ps)
+{
+ g_return_val_if_fail(ps != NULL, NULL);
+
+ return ps->gc;
+}
+
+static gboolean
+purple_socket_check_state(PurpleSocket *ps, PurpleSocketState wanted_state)
+{
+ g_return_val_if_fail(ps != NULL, FALSE);
+
+ if (ps->state == wanted_state)
+ return TRUE;
+
+ purple_debug_error("socket", "invalid state: %d (should be: %d)",
+ ps->state, wanted_state);
+ ps->state = PURPLE_SOCKET_STATE_ERROR;
+ return FALSE;
+}
+
+void
+purple_socket_set_tls(PurpleSocket *ps, gboolean is_tls)
+{
+ g_return_if_fail(ps != NULL);
+
+ if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
+ return;
+
+ ps->is_tls = is_tls;
+}
+
+void
+purple_socket_set_host(PurpleSocket *ps, const gchar *host)
+{
+ g_return_if_fail(ps != NULL);
+
+ if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
+ return;
+
+ g_free(ps->host);
+ ps->host = g_strdup(host);
+}
+
+void
+purple_socket_set_port(PurpleSocket *ps, int port)
+{
+ g_return_if_fail(ps != NULL);
+ g_return_if_fail(port >= 0);
+ g_return_if_fail(port <= 65535);
+
+ if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
+ return;
+
+ ps->port = port;
+}
+
+static void
+_purple_socket_connected_raw(gpointer _ps, gint fd, const gchar *error_message)
+{
+ PurpleSocket *ps = _ps;
+
+ ps->raw_connection = NULL;
+
+ if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTING)) {
+ if (fd > 0)
+ close(fd);
+ ps->cb(ps, _("Invalid socket state"), ps->cb_data);
+ return;
+ }
+
+ if (fd <= 0 || error_message != NULL) {
+ if (error_message == NULL)
+ error_message = _("Unknown error");
+ ps->fd = -1;
+ ps->state = PURPLE_SOCKET_STATE_ERROR;
+ ps->cb(ps, error_message, ps->cb_data);
+ return;
+ }
+
+ ps->state = PURPLE_SOCKET_STATE_CONNECTED;
+ ps->fd = fd;
+ ps->cb(ps, NULL, ps->cb_data);
+}
+
+static void
+_purple_socket_connected_tls(gpointer _ps, PurpleSslConnection *tls_connection,
+ PurpleInputCondition cond)
+{
+ PurpleSocket *ps = _ps;
+
+ if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTING)) {
+ purple_ssl_close(tls_connection);
+ ps->tls_connection = NULL;
+ ps->cb(ps, _("Invalid socket state"), ps->cb_data);
+ return;
+ }
+
+ if (ps->tls_connection->fd <= 0) {
+ ps->state = PURPLE_SOCKET_STATE_ERROR;
+ purple_ssl_close(tls_connection);
+ ps->tls_connection = NULL;
+ ps->cb(ps, _("Invalid file descriptor"), ps->cb_data);
+ return;
+ }
+
+ ps->state = PURPLE_SOCKET_STATE_CONNECTED;
+ ps->fd = ps->tls_connection->fd;
+ ps->cb(ps, NULL, ps->cb_data);
+}
+
+static void
+_purple_socket_connected_tls_error(PurpleSslConnection *ssl_connection,
+ PurpleSslErrorType error, gpointer _ps)
+{
+ PurpleSocket *ps = _ps;
+
+ ps->state = PURPLE_SOCKET_STATE_ERROR;
+ ps->tls_connection = NULL;
+ ps->cb(ps, purple_ssl_strerror(error), ps->cb_data);
+}
+
+gboolean
+purple_socket_connect(PurpleSocket *ps, PurpleSocketConnectCb cb,
+ gpointer user_data)
+{
+ PurpleAccount *account = NULL;
+
+ g_return_val_if_fail(ps != NULL, FALSE);
+
+ if (ps->gc && purple_connection_is_disconnecting(ps->gc)) {
+ purple_debug_error("socket", "connection is being destroyed");
+ ps->state = PURPLE_SOCKET_STATE_ERROR;
+ return FALSE;
+ }
+
+ if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
+ return FALSE;
+ ps->state = PURPLE_SOCKET_STATE_CONNECTING;
+
+ if (ps->host == NULL || ps->port < 0) {
+ purple_debug_error("socket", "Host or port is not specified");
+ ps->state = PURPLE_SOCKET_STATE_ERROR;
+ return FALSE;
+ }
+
+ if (ps->gc != NULL)
+ account = purple_connection_get_account(ps->gc);
+
+ ps->cb = cb;
+ ps->cb_data = user_data;
+
+ if (ps->is_tls) {
+ if (!purple_ssl_is_supported()) {
+ purple_debug_error("socket", "TLS is not supported");
+ ps->state = PURPLE_SOCKET_STATE_ERROR;
+ return FALSE;
+ }
+
+ ps->tls_connection = purple_ssl_connect(account, ps->host,
+ ps->port, _purple_socket_connected_tls,
+ _purple_socket_connected_tls_error, ps);
+ } else {
+ ps->raw_connection = purple_proxy_connect(ps->gc, account,
+ ps->host, ps->port, _purple_socket_connected_raw, ps);
+ }
+
+ if (ps->tls_connection == NULL &&
+ ps->raw_connection == NULL)
+ {
+ ps->state = PURPLE_SOCKET_STATE_ERROR;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gssize
+purple_socket_read(PurpleSocket *ps, guchar *buf, size_t len)
+{
+ g_return_val_if_fail(ps != NULL, -1);
+ g_return_val_if_fail(buf != NULL, -1);
+
+ if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
+ return -1;
+
+ if (ps->is_tls)
+ return purple_ssl_read(ps->tls_connection, buf, len);
+ else
+ return read(ps->fd, buf, len);
+}
+
+gssize
+purple_socket_write(PurpleSocket *ps, const guchar *buf, size_t len)
+{
+ g_return_val_if_fail(ps != NULL, -1);
+ g_return_val_if_fail(buf != NULL, -1);
+
+ if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
+ return -1;
+
+ if (ps->is_tls)
+ return purple_ssl_write(ps->tls_connection, buf, len);
+ else
+ return write(ps->fd, buf, len);
+}
+
+void
+purple_socket_watch(PurpleSocket *ps, PurpleInputCondition cond,
+ PurpleInputFunction func, gpointer user_data)
+{
+ g_return_if_fail(ps != NULL);
+
+ if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
+ return;
+
+ if (ps->inpa > 0)
+ purple_input_remove(ps->inpa);
+ ps->inpa = 0;
+
+ g_return_if_fail(ps->fd > 0);
+
+ if (func != NULL)
+ ps->inpa = purple_input_add(ps->fd, cond, func, user_data);
+}
+
+int
+purple_socket_get_fd(PurpleSocket *ps)
+{
+ g_return_val_if_fail(ps != NULL, -1);
+
+ if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
+ return -1;
+
+ g_return_val_if_fail(ps->fd > 0, -1);
+
+ return ps->fd;
+}
+
+void
+purple_socket_set_data(PurpleSocket *ps, const gchar *key, gpointer data)
+{
+ g_return_if_fail(ps != NULL);
+ g_return_if_fail(key != NULL);
+
+ if (data == NULL)
+ g_hash_table_remove(ps->data, key);
+ else
+ g_hash_table_insert(ps->data, g_strdup(key), data);
+}
+
+gpointer
+purple_socket_get_data(PurpleSocket *ps, const gchar *key)
+{
+ g_return_val_if_fail(ps != NULL, NULL);
+ g_return_val_if_fail(key != NULL, NULL);
+
+ return g_hash_table_lookup(ps->data, key);
+}
+
+static void
+purple_socket_cancel(PurpleSocket *ps)
+{
+ if (ps->inpa > 0)
+ purple_input_remove(ps->inpa);
+ ps->inpa = 0;
+
+ if (ps->tls_connection != NULL) {
+ purple_ssl_close(ps->tls_connection);
+ ps->fd = -1;
+ }
+ ps->tls_connection = NULL;
+
+ if (ps->raw_connection != NULL)
+ purple_proxy_connect_cancel(ps->raw_connection);
+ ps->raw_connection = NULL;
+
+ if (ps->fd > 0)
+ close(ps->fd);
+ ps->fd = 0;
+}
+
+void
+purple_socket_destroy(PurpleSocket *ps)
+{
+ if (ps == NULL)
+ return;
+
+ handle_remove(ps);
+
+ purple_socket_cancel(ps);
+
+ g_free(ps->host);
+ g_hash_table_destroy(ps->data);
+ g_free(ps);
+}
+
+void
+_purple_socket_cancel_with_connection(PurpleConnection *gc)
+{
+ GSList *it;
+
+ it = g_hash_table_lookup(handles, gc);
+ for (; it; it = g_slist_next(it)) {
+ PurpleSocket *ps = it->data;
+ purple_socket_cancel(ps);
+ }
+}
diff --git a/libpurple/purple-socket.h b/libpurple/purple-socket.h
new file mode 100644
index 0000000000..b43e512e7c
--- /dev/null
+++ b/libpurple/purple-socket.h
@@ -0,0 +1,217 @@
+/* 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 _PURPLE_SOCKET_H_
+#define _PURPLE_SOCKET_H_
+/**
+ * SECTION:purple-socket
+ * @section_id: libpurple-purple-socket
+ * @short_description: <filename>purple-socket.h</filename>
+ * @title: Generic Sockets
+ */
+
+#include "connection.h"
+
+/**
+ * PurpleSocket:
+ *
+ * A structure holding all resources needed for the TCP connection.
+ */
+typedef struct _PurpleSocket PurpleSocket;
+
+/**
+ * PurpleSocketConnectCb:
+ * @ps: The socket.
+ * @error: Error message, or NULL if connection was successful.
+ * @user_data: The user data passed with callback function.
+ *
+ * A callback fired after (successfully or not) establishing a connection.
+ */
+typedef void (*PurpleSocketConnectCb)(PurpleSocket *ps, const gchar *error,
+ gpointer user_data);
+
+/**
+ * purple_socket_new:
+ * @gc: The connection for which the socket is needed, or NULL.
+ *
+ * Creates new, disconnected socket.
+ *
+ * Passing a PurpleConnection allows for proper proxy handling.
+ *
+ * Returns: The new socket struct.
+ */
+PurpleSocket *
+purple_socket_new(PurpleConnection *gc);
+
+/**
+ * purple_socket_get_connection:
+ * @ps: The socket.
+ *
+ * Gets PurpleConnection tied with specified socket.
+ *
+ * Returns: The PurpleConnection object.
+ */
+PurpleConnection *
+purple_socket_get_connection(PurpleSocket *ps);
+
+/**
+ * purple_socket_set_tls:
+ * @ps: The socket.
+ * @is_tls: TRUE, if TLS should be handled transparently, FALSE otherwise.
+ *
+ * Determines, if socket should handle TLS.
+ */
+void
+purple_socket_set_tls(PurpleSocket *ps, gboolean is_tls);
+
+/**
+ * purple_socket_set_host:
+ * @ps: The socket.
+ * @host: The connection host.
+ *
+ * Sets connection host.
+ */
+void
+purple_socket_set_host(PurpleSocket *ps, const gchar *host);
+
+/**
+ * purple_socket_set_port:
+ * @ps: The socket.
+ * @port: The connection port.
+ *
+ * Sets connection port.
+ */
+void
+purple_socket_set_port(PurpleSocket *ps, int port);
+
+/**
+ * purple_socket_connect:
+ * @ps: The socket.
+ * @cb: The function to call after establishing a connection, or on
+ * error.
+ * @user_data: The user data to be passed to callback function.
+ *
+ * Establishes a connection.
+ *
+ * Returns: TRUE on success (this doesn't mean it's connected yet), FALSE
+ * otherwise.
+ */
+gboolean
+purple_socket_connect(PurpleSocket *ps, PurpleSocketConnectCb cb,
+ gpointer user_data);
+
+/**
+ * purple_socket_read:
+ * @ps: The socket.
+ * @buf: The buffer to write data to.
+ * @len: The buffer size.
+ *
+ * Reads incoming data from socket.
+ *
+ * This function deals with TLS, if the socket is configured to do it.
+ *
+ * Returns: Amount of data written, or -1 on error (errno will be also be set).
+ */
+gssize
+purple_socket_read(PurpleSocket *ps, guchar *buf, size_t len);
+
+/**
+ * purple_socket_write:
+ * @ps: The socket.
+ * @buf: The buffer to read data from.
+ * @len: The amount of data to read and send.
+ *
+ * Sends data through socket.
+ *
+ * This function deals with TLS, if the socket is configured to do it.
+ *
+ * Returns: Amount of data sent, or -1 on error (errno will albo be set).
+ */
+gssize
+purple_socket_write(PurpleSocket *ps, const guchar *buf, size_t len);
+
+/**
+ * purple_socket_watch:
+ * @ps: The socket.
+ * @cond: The condition type.
+ * @func: The callback function for data, or NULL to remove any
+ * existing callbacks.
+ * @user_data: The user data to be passed to callback function.
+ *
+ * Adds an input handler for the socket.
+ *
+ * If the specified socket had input handler already registered, it will be
+ * removed. To remove any input handlers, pass an NULL handler function.
+ */
+void
+purple_socket_watch(PurpleSocket *ps, PurpleInputCondition cond,
+ PurpleInputFunction func, gpointer user_data);
+
+/**
+ * purple_socket_get_fd:
+ * @ps: The socket
+ *
+ * Gets underlying file descriptor for socket.
+ *
+ * It's not meant to read/write data (use purple_socket_read/
+ * purple_socket_write), rather for watching for changes with select().
+ *
+ * Returns: The file descriptor, or -1 on error.
+ */
+int
+purple_socket_get_fd(PurpleSocket *ps);
+
+/**
+ * purple_socket_set_data:
+ * @ps: The socket.
+ * @key: The unique key.
+ * @data: The data to assign, or NULL to remove.
+ *
+ * Sets extra data for a socket.
+ */
+void
+purple_socket_set_data(PurpleSocket *ps, const gchar *key, gpointer data);
+
+/**
+ * purple_socket_get_data:
+ * @ps: The socket.
+ * @key: The unqiue key.
+ *
+ * Returns extra data in a socket.
+ *
+ * Returns: The data associated with the key.
+ */
+gpointer
+purple_socket_get_data(PurpleSocket *ps, const gchar *key);
+
+/**
+ * purple_socket_destroy:
+ * @ps: The socket.
+ *
+ * Destroys the socket, closes connection and frees all resources.
+ *
+ * If file descriptor for the socket was extracted with purple_socket_get_fd and
+ * added to event loop, it have to be removed prior this.
+ */
+void
+purple_socket_destroy(PurpleSocket *ps);
+
+#endif /* _PURPLE_SOCKET_H_ */
diff --git a/libpurple/purple-uninstalled.pc.in b/libpurple/purple-uninstalled.pc.in
deleted file mode 100644
index 11d4da8a0c..0000000000
--- a/libpurple/purple-uninstalled.pc.in
+++ /dev/null
@@ -1,19 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-datarootdir=@datarootdir@
-datadir=@datadir@
-sysconfdir=@sysconfdir@
-
-abs_srcdir=@abs_srcdir@
-abs_builddir=@abs_builddir@
-
-plugindir=${libdir}/purple-@PURPLE_MAJOR_VERSION@
-
-Name: libpurple
-Description: libpurple is a GLib-based instant messenger library.
-Version: @VERSION@
-Requires: glib-2.0
-Cflags: -I${abs_srcdir} -I${abs_builddir}
-Libs: ${abs_builddir}/libpurple.la
diff --git a/libpurple/purple-url-handler b/libpurple/purple-url-handler
index 391ad298a9..7466fee945 100755
--- a/libpurple/purple-url-handler
+++ b/libpurple/purple-url-handler
@@ -244,11 +244,6 @@ def msnim(uri):
elif command.lower() == "add":
addbuddy(account, screenname)
-def myim(uri):
- protocol = "prpl-myspace"
- print "TODO: send uri: ", uri
- assert False, "Not implemented"
-
def sip(uri):
protocol = "prpl-simple"
match = re.match(r"^sip:(.*)", uri)
@@ -375,8 +370,6 @@ def main(argv=sys.argv):
irc(uri)
elif type == "msnim":
msnim(uri)
- elif type == "myim":
- myim(uri)
elif type == "sip":
sip(uri)
elif type == "xmpp":
diff --git a/libpurple/purple.h.in b/libpurple/purple.h.in
index a96b2537ec..ad936ec6e6 100644
--- a/libpurple/purple.h.in
+++ b/libpurple/purple.h.in
@@ -1,14 +1,3 @@
-/**
- * @file purple.h Header files and defines
- * This file contains all the necessary preprocessor directives to include
- * libpurple's headers and other preprocessor directives required for plugins
- * or UIs to build. Including this file eliminates the need to directly
- * include any other libpurple files.
- *
- * @ingroup core libpurple
- * @since 2.3.0
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -32,9 +21,24 @@
#ifndef _PURPLE_PURPLE_H_
#define _PURPLE_PURPLE_H_
+/**
+ * SECTION:purple
+ * @section_id: libpurple-purple
+ * @short_description: <filename>purple.h</filename>
+ * @title: Header files and defines
+ *
+ * This file contains all the necessary preprocessor directives to include
+ * libpurple's headers and other preprocessor directives required for plugins
+ * or UIs to build. Including this file eliminates the need to directly
+ * include any other libpurple files.
+ */
#include <glib.h>
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
#ifndef G_GNUC_NULL_TERMINATED
# if __GNUC__ >= 4
# define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
@@ -45,25 +49,24 @@
@PLUGINS_DEFINE@
-#include <account.h>
+#include <accounts.h>
#include <accountopt.h>
-#include <blist.h>
+#include <buddylist.h>
#include <buddyicon.h>
#include <certificate.h>
#include <cipher.h>
-#include <circbuffer.h>
+#include <circularbuffer.h>
#include <cmds.h>
#include <connection.h>
-#include <conversation.h>
+#include <conversations.h>
#include <core.h>
#include <debug.h>
#include <desktopitem.h>
#include <dnsquery.h>
#include <dnssrv.h>
+#include <enums.h>
#include <eventloop.h>
-#include <ft.h>
#include <idle.h>
-#include <imgstore.h>
#include <log.h>
#include <media.h>
#include <mediamanager.h>
@@ -76,7 +79,7 @@
#include <pluginpref.h>
#include <pounce.h>
#include <prefs.h>
-#include <privacy.h>
+#include <presence.h>
#include <proxy.h>
#include <prpl.h>
#include <request.h>
@@ -97,9 +100,9 @@
#include <theme-manager.h>
#include <upnp.h>
#include <util.h>
-#include <value.h>
#include <version.h>
#include <whiteboard.h>
+#include <xfer.h>
#include <xmlnode.h>
#endif
diff --git a/libpurple/purple.pc.in b/libpurple/purple.pc.in
deleted file mode 100644
index c776f6f26f..0000000000
--- a/libpurple/purple.pc.in
+++ /dev/null
@@ -1,16 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-datarootdir=@datarootdir@
-datadir=@datadir@
-sysconfdir=@sysconfdir@
-
-plugindir=${libdir}/purple-@PURPLE_MAJOR_VERSION@
-
-Name: libpurple
-Description: libpurple is a GLib-based instant messenger library.
-Version: @VERSION@
-Requires: glib-2.0
-Cflags: -I${includedir}/libpurple
-Libs: -L${libdir} -lpurple
diff --git a/libpurple/request-datasheet.c b/libpurple/request-datasheet.c
new file mode 100644
index 0000000000..31713c4647
--- /dev/null
+++ b/libpurple/request-datasheet.c
@@ -0,0 +1,504 @@
+/* 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 "request-datasheet.h"
+
+#include "debug.h"
+#include "glibcompat.h"
+#include "signals.h"
+
+struct _PurpleRequestDatasheet
+{
+ guint col_count;
+ GArray *col_types;
+ GArray *col_titles;
+ GList *actions;
+
+ GList *record_list;
+ GHashTable *record_li_by_key;
+
+ GHashTable *marked_for_rem;
+};
+
+struct _PurpleRequestDatasheetRecord
+{
+ PurpleRequestDatasheet *sheet;
+ gpointer key;
+ gchar **data; /* at this point, there is only string data possible */
+};
+
+struct _PurpleRequestDatasheetAction
+{
+ gchar *label;
+
+ PurpleRequestDatasheetActionCb cb;
+ gpointer cb_data;
+
+ PurpleRequestDatasheetActionCheckCb sens_cb;
+ gpointer sens_data;
+};
+
+static void
+purple_request_datasheet_record_free(PurpleRequestDatasheetRecord *rec);
+
+/***** Datasheet API **********************************************************/
+
+PurpleRequestDatasheet *
+purple_request_datasheet_new(void)
+{
+ PurpleRequestDatasheet *sheet;
+
+ sheet = g_new0(PurpleRequestDatasheet, 1);
+
+ sheet->col_types = g_array_new(FALSE, FALSE,
+ sizeof(PurpleRequestDatasheetColumnType));
+ sheet->col_titles = g_array_new(FALSE, FALSE, sizeof(gchar *));
+ /* XXX: use g_array_set_clear_func when we depend on Glib 2.32 */
+
+ sheet->record_li_by_key = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+ purple_signal_register(sheet, "record-changed",
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ G_TYPE_POINTER, /* (PurpleRequestDatasheet *) */
+ G_TYPE_POINTER); /* NULL for all */
+
+ purple_signal_register(sheet, "destroy",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ G_TYPE_POINTER); /* (PurpleRequestDatasheet *) */
+
+ return sheet;
+}
+
+void
+purple_request_datasheet_free(PurpleRequestDatasheet *sheet)
+{
+ guint i;
+
+ g_return_if_fail(sheet != NULL);
+
+ purple_signal_emit(sheet, "destroy", sheet);
+ purple_signals_unregister_by_instance(sheet);
+
+ for (i = 0; i < sheet->col_titles->len; i++)
+ g_free(g_array_index(sheet->col_titles, gchar *, i));
+
+ g_array_free(sheet->col_titles, TRUE);
+ g_array_free(sheet->col_types, TRUE);
+
+ g_list_free_full(sheet->actions,
+ (GDestroyNotify)purple_request_datasheet_action_free);
+
+ g_hash_table_destroy(sheet->record_li_by_key);
+ g_list_free_full(sheet->record_list,
+ (GDestroyNotify)purple_request_datasheet_record_free);
+
+ if (sheet->marked_for_rem != NULL)
+ g_hash_table_destroy(sheet->marked_for_rem);
+
+ g_free(sheet);
+}
+
+void
+purple_request_datasheet_add_column(PurpleRequestDatasheet *sheet,
+ PurpleRequestDatasheetColumnType type, const gchar *title)
+{
+ gchar *title_clone;
+
+ g_return_if_fail(sheet != NULL);
+
+ if (sheet->record_list != NULL) {
+ purple_debug_error("request-datasheet", "Cannot modify model "
+ "when there is already some data");
+ return;
+ }
+
+ title_clone = g_strdup(title);
+ sheet->col_count++;
+ g_array_append_val(sheet->col_types, type);
+ g_array_append_val(sheet->col_titles, title_clone);
+}
+
+guint
+purple_request_datasheet_get_column_count(PurpleRequestDatasheet *sheet)
+{
+ g_return_val_if_fail(sheet != NULL, 0);
+
+ return sheet->col_count;
+ /*return sheet->col_types->len;*/
+}
+
+PurpleRequestDatasheetColumnType
+purple_request_datasheet_get_column_type(PurpleRequestDatasheet *sheet,
+ guint col_no)
+{
+ g_return_val_if_fail(sheet != NULL, 0);
+
+ return g_array_index(sheet->col_types,
+ PurpleRequestDatasheetColumnType, col_no);
+}
+
+const gchar *
+purple_request_datasheet_get_column_title(PurpleRequestDatasheet *sheet,
+ guint col_no)
+{
+ g_return_val_if_fail(sheet != NULL, 0);
+
+ return g_array_index(sheet->col_titles, gchar *, col_no);
+}
+
+const GList *
+purple_request_datasheet_get_records(PurpleRequestDatasheet *sheet)
+{
+ g_return_val_if_fail(sheet != NULL, NULL);
+
+ return sheet->record_list;
+}
+
+void
+purple_request_datasheet_add_action(PurpleRequestDatasheet *sheet,
+ PurpleRequestDatasheetAction *action)
+{
+ g_return_if_fail(sheet != NULL);
+ g_return_if_fail(action != NULL);
+
+ sheet->actions = g_list_append(sheet->actions, action);
+}
+
+const GList *
+purple_request_datasheet_get_actions(PurpleRequestDatasheet *sheet)
+{
+ g_return_val_if_fail(sheet != NULL, NULL);
+
+ return sheet->actions;
+}
+
+/***** Datasheet actions API **************************************************/
+
+PurpleRequestDatasheetAction *
+purple_request_datasheet_action_new(void)
+{
+ return g_new0(PurpleRequestDatasheetAction, 1);
+}
+
+void
+purple_request_datasheet_action_free(PurpleRequestDatasheetAction *act)
+{
+ g_return_if_fail(act != NULL);
+ g_free(act->label);
+ g_free(act);
+}
+
+void
+purple_request_datasheet_action_set_label(PurpleRequestDatasheetAction *act,
+ const gchar *label)
+{
+ gchar *new_label;
+
+ g_return_if_fail(act != NULL);
+
+ new_label = g_strdup(label);
+ g_free(act->label);
+ act->label = new_label;
+}
+
+const gchar*
+purple_request_datasheet_action_get_label(PurpleRequestDatasheetAction *act)
+{
+ g_return_val_if_fail(act != NULL, NULL);
+
+ return act->label;
+}
+
+void
+purple_request_datasheet_action_set_cb(PurpleRequestDatasheetAction *act,
+ PurpleRequestDatasheetActionCb cb, gpointer user_data)
+{
+ g_return_if_fail(act != NULL);
+
+ act->cb = cb;
+ act->cb_data = user_data;
+}
+
+void
+purple_request_datasheet_action_call(PurpleRequestDatasheetAction *act,
+ PurpleRequestDatasheetRecord *rec)
+{
+ g_return_if_fail(act != NULL);
+
+ if (!act->cb)
+ return;
+
+ if (!purple_request_datasheet_action_is_sensitive(act, rec)) {
+ purple_debug_warning("request-datasheet",
+ "Action is disabled for this record");
+ return;
+ }
+
+ act->cb(rec, act->cb_data);
+}
+
+void
+purple_request_datasheet_action_set_sens_cb(
+ PurpleRequestDatasheetAction *act,
+ PurpleRequestDatasheetActionCheckCb cb, gpointer user_data)
+{
+ g_return_if_fail(act != NULL);
+
+ act->sens_cb = cb;
+ act->sens_data = user_data;
+}
+
+gboolean
+purple_request_datasheet_action_is_sensitive(PurpleRequestDatasheetAction *act,
+ PurpleRequestDatasheetRecord *rec)
+{
+ g_return_val_if_fail(act != NULL, FALSE);
+
+ if (!act->sens_cb)
+ return (rec != NULL);
+
+ return act->sens_cb(rec, act->sens_data);
+}
+
+/***** Datasheet record API ***************************************************/
+
+static PurpleRequestDatasheetRecord *
+purple_request_datasheet_record_new(void)
+{
+ return g_new0(PurpleRequestDatasheetRecord, 1);
+}
+
+static void
+purple_request_datasheet_record_free(PurpleRequestDatasheetRecord *rec)
+{
+ g_strfreev(rec->data);
+ g_free(rec);
+}
+
+gpointer
+purple_request_datasheet_record_get_key(const PurpleRequestDatasheetRecord *rec)
+{
+ g_return_val_if_fail(rec != NULL, NULL);
+
+ return rec->key;
+}
+
+PurpleRequestDatasheet *
+purple_request_datasheet_record_get_datasheet(
+ PurpleRequestDatasheetRecord *rec)
+{
+ g_return_val_if_fail(rec != NULL, NULL);
+
+ return rec->sheet;
+}
+
+PurpleRequestDatasheetRecord *
+purple_request_datasheet_record_find(PurpleRequestDatasheet *sheet,
+ gpointer key)
+{
+ GList *it;
+
+ g_return_val_if_fail(sheet != NULL, NULL);
+
+ it = g_hash_table_lookup(sheet->record_li_by_key, key);
+ if (!it)
+ return NULL;
+
+ return it->data;
+}
+
+PurpleRequestDatasheetRecord *
+purple_request_datasheet_record_add(PurpleRequestDatasheet *sheet,
+ gpointer key)
+{
+ PurpleRequestDatasheetRecord *rec;
+
+ g_return_val_if_fail(sheet != NULL, NULL);
+
+ rec = purple_request_datasheet_record_find(sheet, key);
+ if (rec != NULL) {
+ if (sheet->marked_for_rem != NULL)
+ g_hash_table_remove(sheet->marked_for_rem, key);
+ return rec;
+ }
+
+ rec = purple_request_datasheet_record_new();
+ rec->sheet = sheet;
+ rec->key = key;
+
+ /* we don't allow modifying collumn count when datasheet contains
+ * any records */
+ rec->data = g_new0(gchar*,
+ purple_request_datasheet_get_column_count(sheet) + 1);
+
+ sheet->record_list = g_list_append(sheet->record_list, rec);
+ g_hash_table_insert(sheet->record_li_by_key, key,
+ g_list_find(sheet->record_list, rec));
+
+ purple_signal_emit(sheet, "record-changed", sheet, key);
+
+ return rec;
+}
+
+void
+purple_request_datasheet_record_remove(PurpleRequestDatasheet *sheet,
+ gpointer key)
+{
+ GList *it;
+
+ g_return_if_fail(sheet != NULL);
+
+ it = g_hash_table_lookup(sheet->record_li_by_key, key);
+ if (it == NULL)
+ return;
+
+ purple_request_datasheet_record_free(it->data);
+ sheet->record_list = g_list_delete_link(sheet->record_list, it);
+ g_hash_table_remove(sheet->record_li_by_key, key);
+
+ purple_signal_emit(sheet, "record-changed", sheet, key);
+}
+
+void
+purple_request_datasheet_record_remove_all(PurpleRequestDatasheet *sheet)
+{
+ g_return_if_fail(sheet != NULL);
+
+ g_list_free_full(sheet->record_list,
+ (GDestroyNotify)purple_request_datasheet_record_free);
+ sheet->record_list = NULL;
+ g_hash_table_remove_all(sheet->record_li_by_key);
+
+ purple_signal_emit(sheet, "record-changed", sheet, NULL);
+}
+
+void
+purple_request_datasheet_record_mark_all_for_rem(PurpleRequestDatasheet *sheet)
+{
+ const GList *it;
+
+ if (sheet->marked_for_rem != NULL)
+ g_hash_table_destroy(sheet->marked_for_rem);
+ sheet->marked_for_rem = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+ it = purple_request_datasheet_get_records(sheet);
+ for (; it != NULL; it = g_list_next(it)) {
+ PurpleRequestDatasheetRecord *rec = it->data;
+ gpointer key = purple_request_datasheet_record_get_key(rec);
+
+ g_hash_table_insert(sheet->marked_for_rem, key, key);
+ }
+}
+
+void
+purple_request_datasheet_record_remove_marked(PurpleRequestDatasheet *sheet)
+{
+ GHashTableIter it;
+ gpointer key;
+ GHashTable *rem;
+
+ if (sheet->marked_for_rem == NULL)
+ return;
+ rem = sheet->marked_for_rem;
+ sheet->marked_for_rem = NULL;
+
+ g_hash_table_iter_init(&it, rem);
+ while (g_hash_table_iter_next(&it, &key, NULL))
+ purple_request_datasheet_record_remove(sheet, key);
+
+ g_hash_table_destroy(rem);
+}
+
+static void
+purple_request_datasheet_record_set_common_data(
+ PurpleRequestDatasheetRecord *rec, guint col_no, const gchar *data)
+{
+ g_return_if_fail(rec != NULL);
+ g_return_if_fail(
+ purple_request_datasheet_get_column_count(rec->sheet) > col_no);
+
+ if (g_strcmp0(rec->data[col_no], data) == 0)
+ return;
+
+ /* we assume, model hasn't changed */
+ g_free(rec->data[col_no]);
+ rec->data[col_no] = g_strdup(data);
+
+ purple_signal_emit(rec->sheet, "record-changed", rec->sheet, rec->key);
+}
+
+void
+purple_request_datasheet_record_set_string_data(
+ PurpleRequestDatasheetRecord *rec, guint col_no, const gchar *data)
+{
+ g_return_if_fail(rec != NULL);
+ g_return_if_fail(purple_request_datasheet_get_column_type(rec->sheet,
+ col_no) == PURPLE_REQUEST_DATASHEET_COLUMN_STRING);
+
+ purple_request_datasheet_record_set_common_data(rec, col_no, data);
+}
+
+void
+purple_request_datasheet_record_set_image_data(
+ PurpleRequestDatasheetRecord *rec, guint col_no, const gchar *stock_id)
+{
+ g_return_if_fail(rec != NULL);
+ g_return_if_fail(purple_request_datasheet_get_column_type(rec->sheet,
+ col_no) == PURPLE_REQUEST_DATASHEET_COLUMN_IMAGE);
+
+ purple_request_datasheet_record_set_common_data(rec, col_no, stock_id);
+}
+
+static const gchar *
+purple_request_datasheet_record_get_common_data(
+ const PurpleRequestDatasheetRecord *rec, guint col_no)
+{
+ g_return_val_if_fail(rec != NULL, NULL);
+ g_return_val_if_fail(
+ purple_request_datasheet_get_column_count(rec->sheet) > col_no,
+ NULL);
+
+ return rec->data[col_no];
+}
+
+const gchar *
+purple_request_datasheet_record_get_string_data(
+ const PurpleRequestDatasheetRecord *rec, guint col_no)
+{
+ g_return_val_if_fail(rec != NULL, NULL);
+ g_return_val_if_fail(purple_request_datasheet_get_column_type(
+ rec->sheet, col_no) == PURPLE_REQUEST_DATASHEET_COLUMN_STRING,
+ NULL);
+
+ return purple_request_datasheet_record_get_common_data(rec, col_no);
+}
+
+const gchar *
+purple_request_datasheet_record_get_image_data(
+ const PurpleRequestDatasheetRecord *rec, guint col_no)
+{
+ g_return_val_if_fail(rec != NULL, NULL);
+ g_return_val_if_fail(purple_request_datasheet_get_column_type(
+ rec->sheet, col_no) == PURPLE_REQUEST_DATASHEET_COLUMN_IMAGE,
+ NULL);
+
+ return purple_request_datasheet_record_get_common_data(rec, col_no);
+}
diff --git a/libpurple/request-datasheet.h b/libpurple/request-datasheet.h
new file mode 100644
index 0000000000..f53a1be9da
--- /dev/null
+++ b/libpurple/request-datasheet.h
@@ -0,0 +1,411 @@
+/* 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 _PURPLE_REQUEST_DATA_H_
+#define _PURPLE_REQUEST_DATA_H_
+/**
+ * SECTION:request-datasheet
+ * @section_id: libpurple-request-datasheet
+ * @short_description: <filename>request-datasheet.h</filename>
+ * @title: Request Datasheet API
+ */
+
+#include <glib.h>
+
+typedef struct _PurpleRequestDatasheet PurpleRequestDatasheet;
+typedef struct _PurpleRequestDatasheetRecord PurpleRequestDatasheetRecord;
+typedef struct _PurpleRequestDatasheetAction PurpleRequestDatasheetAction;
+
+typedef void (*PurpleRequestDatasheetActionCb)(
+ PurpleRequestDatasheetRecord *rec, gpointer user_data);
+typedef gboolean (*PurpleRequestDatasheetActionCheckCb)(
+ PurpleRequestDatasheetRecord *rec, gpointer user_data);
+
+typedef enum
+{
+ PURPLE_REQUEST_DATASHEET_COLUMN_STRING,
+ PURPLE_REQUEST_DATASHEET_COLUMN_IMAGE
+} PurpleRequestDatasheetColumnType;
+
+G_BEGIN_DECLS
+
+/**************************************************************************/
+/* Datasheet API */
+/**************************************************************************/
+
+/**
+ * purple_request_datasheet_new:
+ *
+ * Creates new Datasheet.
+ *
+ * Returns: The new datasheet.
+ */
+PurpleRequestDatasheet *
+purple_request_datasheet_new(void);
+
+/**
+ * purple_request_datasheet_free:
+ * @sheet: The datasheet.
+ *
+ * Destroys datasheet with all its contents.
+ */
+void
+purple_request_datasheet_free(PurpleRequestDatasheet *sheet);
+
+/**
+ * purple_request_datasheet_add_column:
+ * @sheet: The datasheet.
+ * @type: The column type.
+ * @title: The column title (may be %NULL).
+ *
+ * Adds a column to the datasheet.
+ *
+ * You cannot add a column if datasheet contains any data.
+ */
+void
+purple_request_datasheet_add_column(PurpleRequestDatasheet *sheet,
+ PurpleRequestDatasheetColumnType type, const gchar *title);
+
+/**
+ * purple_request_datasheet_get_column_count:
+ * @sheet: The datasheet.
+ *
+ * Returns the column count of datasheet.
+ *
+ * Returns: The column count.
+ */
+guint
+purple_request_datasheet_get_column_count(PurpleRequestDatasheet *sheet);
+
+/**
+ * purple_request_datasheet_get_column_type:
+ * @sheet: The datasheet.
+ * @col_no: The column number (0 is the first one).
+ *
+ * Returns the column type for a datasheet.
+ *
+ * Returns: The column type.
+ */
+PurpleRequestDatasheetColumnType
+purple_request_datasheet_get_column_type(PurpleRequestDatasheet *sheet,
+ guint col_no);
+
+/**
+ * purple_request_datasheet_get_column_title:
+ * @sheet: The datasheet.
+ * @col_no: The column number (0 is the first one).
+ *
+ * Returns the column title for a datasheet.
+ *
+ * Returns: The column title.
+ */
+const gchar *
+purple_request_datasheet_get_column_title(PurpleRequestDatasheet *sheet,
+ guint col_no);
+
+/**
+ * purple_request_datasheet_get_records:
+ * @sheet: The datasheet.
+ *
+ * Returns the list of records in a datasheet.
+ *
+ * You shouldn't modify datasheet's data while iterating through it.
+ *
+ * Returns: (transfer none): The list of records.
+ */
+const GList *
+purple_request_datasheet_get_records(PurpleRequestDatasheet *sheet);
+
+/**
+ * purple_request_datasheet_add_action:
+ * @sheet: The datasheet.
+ * @action: The action.
+ *
+ * Adds an action to the datasheet.
+ *
+ * Action object is owned by the datasheet since this call.
+ */
+void
+purple_request_datasheet_add_action(PurpleRequestDatasheet *sheet,
+ PurpleRequestDatasheetAction *action);
+
+/**
+ * purple_request_datasheet_get_actions:
+ * @sheet: The datasheet.
+ *
+ * Returns the list of actions in a datasheet.
+ *
+ * Returns: (transfer none): The list of actions.
+ */
+const GList *
+purple_request_datasheet_get_actions(PurpleRequestDatasheet *sheet);
+
+
+/**************************************************************************/
+/* Datasheet actions API */
+/**************************************************************************/
+
+/**
+ * purple_request_datasheet_action_new:
+ *
+ * Creates new datasheet action.
+ *
+ * Returns: The new action.
+ */
+PurpleRequestDatasheetAction *
+purple_request_datasheet_action_new(void);
+
+/**
+ * purple_request_datasheet_action_free:
+ * @act: The action.
+ *
+ * Destroys the datasheet action.
+ */
+void
+purple_request_datasheet_action_free(PurpleRequestDatasheetAction *act);
+
+/**
+ * purple_request_datasheet_action_set_label:
+ * @act: The action.
+ * @label: The label.
+ *
+ * Sets the localized label for the action.
+ */
+void
+purple_request_datasheet_action_set_label(PurpleRequestDatasheetAction *act,
+ const gchar *label);
+
+/**
+ * purple_request_datasheet_action_get_label:
+ * @act: The action.
+ *
+ * Gets the label of action.
+ *
+ * Returns: The localized label text.
+ */
+const gchar*
+purple_request_datasheet_action_get_label(PurpleRequestDatasheetAction *act);
+
+/**
+ * purple_request_datasheet_action_set_cb:
+ * @act: The action.
+ * @cb: The callback function.
+ * @user_data: The data to be passed to the callback function.
+ *
+ * Sets the callback for the action.
+ */
+void
+purple_request_datasheet_action_set_cb(PurpleRequestDatasheetAction *act,
+ PurpleRequestDatasheetActionCb cb, gpointer user_data);
+
+/**
+ * purple_request_datasheet_action_call:
+ * @act: The action.
+ * @rec: The user selected record.
+ *
+ * Calls the callback of the action.
+ */
+void
+purple_request_datasheet_action_call(PurpleRequestDatasheetAction *act,
+ PurpleRequestDatasheetRecord *rec);
+
+/**
+ * purple_request_datasheet_action_set_sens_cb:
+ * @act: The action.
+ * @cb: The callback function, may be %NULL.
+ * @user_data: The data to be passed to the callback function.
+ *
+ * Sets the sensitivity checker for the action.
+ *
+ * If there is no callback set, default is used: the action is enabled, if any
+ * record is active.
+ */
+void
+purple_request_datasheet_action_set_sens_cb(
+ PurpleRequestDatasheetAction *act,
+ PurpleRequestDatasheetActionCheckCb cb, gpointer user_data);
+
+/**
+ * purple_request_datasheet_action_is_sensitive:
+ * @act: The action.
+ * @rec: The record.
+ *
+ * Checks, if the action is enabled for the active record.
+ *
+ * Returns: %TRUE, if the action is enabled, %FALSE otherwise.
+ */
+gboolean
+purple_request_datasheet_action_is_sensitive(PurpleRequestDatasheetAction *act,
+ PurpleRequestDatasheetRecord *rec);
+
+
+/**************************************************************************/
+/* Datasheet record API */
+/**************************************************************************/
+
+/**
+ * purple_request_datasheet_record_get_key:
+ * @rec: The record.
+ *
+ * Returns the key of a record.
+ *
+ * Returns: The key.
+ */
+gpointer
+purple_request_datasheet_record_get_key(
+ const PurpleRequestDatasheetRecord *rec);
+
+/**
+ * purple_request_datasheet_record_get_datasheet:
+ * @rec: The record.
+ *
+ * Returns the datasheet of a record.
+ *
+ * Returns: The datasheet.
+ */
+PurpleRequestDatasheet *
+purple_request_datasheet_record_get_datasheet(
+ PurpleRequestDatasheetRecord *rec);
+
+/**
+ * purple_request_datasheet_record_find:
+ * @sheet: The datasheet.
+ * @key: The key.
+ *
+ * Looks up for a record in datasheet.
+ *
+ * Returns: The record if found, %NULL otherwise.
+ */
+PurpleRequestDatasheetRecord *
+purple_request_datasheet_record_find(PurpleRequestDatasheet *sheet,
+ gpointer key);
+
+/**
+ * purple_request_datasheet_record_add:
+ * @sheet: The datasheet.
+ * @key: The key.
+ *
+ * Adds a record to the datasheet.
+ *
+ * If the specified key already exists in datasheet, old record is returned.
+ *
+ * Returns: The record.
+ */
+PurpleRequestDatasheetRecord *
+purple_request_datasheet_record_add(PurpleRequestDatasheet *sheet,
+ gpointer key);
+
+/**
+ * purple_request_datasheet_record_remove:
+ * @sheet: The datasheet.
+ * @key: The key.
+ *
+ * Removes a record from a datasheet.
+ */
+void
+purple_request_datasheet_record_remove(PurpleRequestDatasheet *sheet,
+ gpointer key);
+
+/**
+ * purple_request_datasheet_record_remove_all:
+ * @sheet: The datasheet.
+ *
+ * Removes all records from a datasheet.
+ */
+void
+purple_request_datasheet_record_remove_all(PurpleRequestDatasheet *sheet);
+
+/**
+ * purple_request_datasheet_record_mark_all_for_rem:
+ * @sheet: The datasheet.
+ *
+ * Marks all records for removal. Record will be unmarked, when touched with
+ * purple_request_datasheet_record_add.
+ *
+ * See purple_request_datasheet_record_add().
+ */
+void
+purple_request_datasheet_record_mark_all_for_rem(PurpleRequestDatasheet *sheet);
+
+/**
+ * purple_request_datasheet_record_remove_marked:
+ * @sheet: The datasheet.
+ *
+ * Removes all marked records.
+ *
+ * See purple_request_datasheet_record_mark_all_for_rem().
+ */
+void
+purple_request_datasheet_record_remove_marked(PurpleRequestDatasheet *sheet);
+
+/**
+ * purple_request_datasheet_record_set_string_data:
+ * @rec: The record.
+ * @col_no: The column.
+ * @data: The data.
+ *
+ * Sets data for a string column of specified record.
+ */
+void
+purple_request_datasheet_record_set_string_data(
+ PurpleRequestDatasheetRecord *rec, guint col_no, const gchar *data);
+
+/**
+ * purple_request_datasheet_record_set_image_data:
+ * @rec: The record.
+ * @col_no: The column.
+ * @stock_id: The stock identifier of a image.
+ *
+ * Sets data for a image column of specified record.
+ */
+void
+purple_request_datasheet_record_set_image_data(
+ PurpleRequestDatasheetRecord *rec, guint col_no, const gchar *stock_id);
+
+/**
+ * purple_request_datasheet_record_get_string_data:
+ * @rec: The record.
+ * @col_no: The column.
+ *
+ * Returns data for a string column of specified record.
+ *
+ * Returns: The data.
+ */
+const gchar *
+purple_request_datasheet_record_get_string_data(
+ const PurpleRequestDatasheetRecord *rec, guint col_no);
+
+/**
+ * purple_request_datasheet_record_get_image_data:
+ * @rec: The record.
+ * @col_no: The column.
+ *
+ * Returns data for an image column of specified record.
+ *
+ * Returns: The stock id of an image.
+ */
+const gchar *
+purple_request_datasheet_record_get_image_data(
+ const PurpleRequestDatasheetRecord *rec, guint col_no);
+
+G_END_DECLS
+
+#endif /* _PURPLE_REQUEST_DATA_H_ */
diff --git a/libpurple/request.c b/libpurple/request.c
index d61d24182e..a3ff364d9a 100644
--- a/libpurple/request.c
+++ b/libpurple/request.c
@@ -1,8 +1,3 @@
-/**
- * @file request.c Request API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -27,6 +22,7 @@
#include "internal.h"
+#include "glibcompat.h"
#include "notify.h"
#include "request.h"
#include "debug.h"
@@ -36,12 +32,452 @@ static GList *handles = NULL;
typedef struct
{
+ GDestroyNotify cb;
+ gpointer data;
+} PurpleRequestCloseNotified;
+
+typedef struct
+{
PurpleRequestType type;
void *handle;
void *ui_handle;
-
+ GSList *notify_on_close;
} PurpleRequestInfo;
+struct _PurpleRequestField
+{
+ PurpleRequestFieldType type;
+ PurpleRequestFieldGroup *group;
+
+ char *id;
+ char *label;
+ char *type_hint;
+
+ gboolean visible;
+ gboolean required;
+ gboolean sensitive;
+ PurpleRequestFieldSensitivityCb sensitivity_cb;
+
+ union
+ {
+ struct
+ {
+ gboolean multiline;
+ gboolean masked;
+ char *default_value;
+ char *value;
+
+ } string;
+
+ struct
+ {
+ int default_value;
+ int value;
+ int lower_bound;
+ int upper_bound;
+ } integer;
+
+ struct
+ {
+ gboolean default_value;
+ gboolean value;
+
+ } boolean;
+
+ struct
+ {
+ gpointer default_value;
+ gpointer value;
+
+ GList *elements;
+ GDestroyNotify data_destroy;
+ } choice;
+
+ struct
+ {
+ GList *items;
+ GList *icons;
+ GHashTable *item_data;
+ GList *selected;
+ GHashTable *selected_table;
+
+ gboolean multiple_selection;
+
+ } list;
+
+ struct
+ {
+ PurpleAccount *default_account;
+ PurpleAccount *account;
+ gboolean show_all;
+
+ PurpleFilterAccountFunc filter_func;
+
+ } account;
+
+ struct
+ {
+ unsigned int scale_x;
+ unsigned int scale_y;
+ const char *buffer;
+ gsize size;
+ } image;
+
+ struct
+ {
+ PurpleCertificate *cert;
+ } certificate;
+
+ struct
+ {
+ PurpleRequestDatasheet *sheet;
+ } datasheet;
+ } u;
+
+ void *ui_data;
+ char *tooltip;
+
+ PurpleRequestFieldValidator validator;
+ void *validator_data;
+};
+
+struct _PurpleRequestFields
+{
+ GList *groups;
+
+ GHashTable *fields;
+
+ gchar **tab_names;
+
+ GList *required_fields;
+
+ GList *validated_fields;
+
+ GList *autosensitive_fields;
+
+ void *ui_data;
+};
+
+struct _PurpleRequestFieldGroup
+{
+ PurpleRequestFields *fields_list;
+
+ char *title;
+ gint tab_no;
+
+ GList *fields;
+};
+
+struct _PurpleRequestCommonParameters
+{
+ int ref_count;
+
+ PurpleAccount *account;
+ PurpleConversation *conv;
+
+ PurpleRequestIconType icon_type;
+ gconstpointer icon_data;
+ gsize icon_size;
+
+ gboolean html;
+
+ gboolean compact;
+
+ PurpleRequestHelpCb help_cb;
+ gpointer help_data;
+
+ GSList *extra_actions;
+
+ gpointer parent_from;
+};
+
+static void
+purple_request_fields_check_others_sensitivity(PurpleRequestField *field);
+
+PurpleRequestCommonParameters *
+purple_request_cpar_new(void)
+{
+ return g_new0(PurpleRequestCommonParameters, 1);
+}
+
+PurpleRequestCommonParameters *
+purple_request_cpar_from_connection(PurpleConnection *gc)
+{
+ if (gc == NULL)
+ return purple_request_cpar_new();
+ return purple_request_cpar_from_account(
+ purple_connection_get_account(gc));
+}
+
+PurpleRequestCommonParameters *
+purple_request_cpar_from_account(PurpleAccount *account)
+{
+ PurpleRequestCommonParameters *cpar;
+
+ cpar = purple_request_cpar_new();
+ purple_request_cpar_set_account(cpar, account);
+
+ return cpar;
+}
+
+PurpleRequestCommonParameters *
+purple_request_cpar_from_conversation(PurpleConversation *conv)
+{
+ PurpleRequestCommonParameters *cpar;
+ PurpleAccount *account = NULL;
+
+ if (conv != NULL) {
+ account = purple_connection_get_account(
+ purple_conversation_get_connection(conv));
+ }
+
+ cpar = purple_request_cpar_new();
+ purple_request_cpar_set_account(cpar, account);
+ purple_request_cpar_set_conversation(cpar, conv);
+
+ return cpar;
+}
+
+void
+purple_request_cpar_ref(PurpleRequestCommonParameters *cpar)
+{
+ g_return_if_fail(cpar != NULL);
+
+ cpar->ref_count++;
+}
+
+PurpleRequestCommonParameters *
+purple_request_cpar_unref(PurpleRequestCommonParameters *cpar)
+{
+ if (cpar == NULL)
+ return NULL;
+
+ if (--cpar->ref_count > 0)
+ return cpar;
+
+ purple_request_cpar_set_extra_actions(cpar, NULL);
+ g_free(cpar);
+ return NULL;
+}
+
+void
+purple_request_cpar_set_account(PurpleRequestCommonParameters *cpar,
+ PurpleAccount *account)
+{
+ g_return_if_fail(cpar != NULL);
+
+ cpar->account = account;
+}
+
+PurpleAccount *
+purple_request_cpar_get_account(PurpleRequestCommonParameters *cpar)
+{
+ if (cpar == NULL)
+ return NULL;
+
+ return cpar->account;
+}
+
+void
+purple_request_cpar_set_conversation(PurpleRequestCommonParameters *cpar,
+ PurpleConversation *conv)
+{
+ g_return_if_fail(cpar != NULL);
+
+ cpar->conv = conv;
+}
+
+PurpleConversation *
+purple_request_cpar_get_conversation(PurpleRequestCommonParameters *cpar)
+{
+ if (cpar == NULL)
+ return NULL;
+
+ return cpar->conv;
+}
+
+void
+purple_request_cpar_set_icon(PurpleRequestCommonParameters *cpar,
+ PurpleRequestIconType icon_type)
+{
+ g_return_if_fail(cpar != NULL);
+
+ cpar->icon_type = icon_type;
+}
+
+PurpleRequestIconType
+purple_request_cpar_get_icon(PurpleRequestCommonParameters *cpar)
+{
+ if (cpar == NULL)
+ return PURPLE_REQUEST_ICON_DEFAULT;
+
+ return cpar->icon_type;
+}
+
+void
+purple_request_cpar_set_custom_icon(PurpleRequestCommonParameters *cpar,
+ gconstpointer icon_data, gsize icon_size)
+{
+ g_return_if_fail(cpar != NULL);
+ g_return_if_fail((icon_data == NULL) == (icon_size == 0));
+
+ cpar->icon_data = icon_data;
+ cpar->icon_size = icon_size;
+}
+
+gconstpointer
+purple_request_cpar_get_custom_icon(PurpleRequestCommonParameters *cpar,
+ gsize *icon_size)
+{
+ if (cpar == NULL) {
+ if (icon_size != NULL)
+ *icon_size = 0;
+ return NULL;
+ }
+
+ if (icon_size != NULL)
+ *icon_size = cpar->icon_size;
+ return cpar->icon_data;
+}
+
+void
+purple_request_cpar_set_html(PurpleRequestCommonParameters *cpar,
+ gboolean enabled)
+{
+ g_return_if_fail(cpar != NULL);
+
+ cpar->html = enabled;
+}
+
+gboolean
+purple_request_cpar_is_html(PurpleRequestCommonParameters *cpar)
+{
+ if (cpar == NULL)
+ return FALSE;
+
+ return cpar->html;
+}
+
+void
+purple_request_cpar_set_compact(PurpleRequestCommonParameters *cpar,
+ gboolean compact)
+{
+ g_return_if_fail(cpar != NULL);
+
+ cpar->compact = compact;
+}
+
+gboolean
+purple_request_cpar_is_compact(PurpleRequestCommonParameters *cpar)
+{
+ if (cpar == NULL)
+ return FALSE;
+
+ return cpar->compact;
+}
+
+void
+purple_request_cpar_set_help_cb(PurpleRequestCommonParameters *cpar,
+ PurpleRequestHelpCb cb, gpointer user_data)
+{
+ g_return_if_fail(cpar != NULL);
+
+ cpar->help_cb = cb;
+ cpar->help_data = cb ? user_data : NULL;
+}
+
+PurpleRequestHelpCb
+purple_request_cpar_get_help_cb(PurpleRequestCommonParameters *cpar,
+ gpointer *user_data)
+{
+ if (cpar == NULL)
+ return NULL;
+
+ if (user_data != NULL)
+ *user_data = cpar->help_data;
+ return cpar->help_cb;
+}
+
+void
+purple_request_cpar_set_extra_actions(PurpleRequestCommonParameters *cpar, ...)
+{
+ va_list args;
+ GSList *extra = NULL, *it;
+
+ it = cpar->extra_actions;
+ while (it != NULL) {
+ gchar *label = it->data;
+
+ g_free(label);
+ it = g_slist_next(it);
+ if (it == NULL)
+ break;
+ it = g_slist_next(it);
+ }
+
+ va_start(args, cpar);
+
+ while (TRUE) {
+ const gchar *label;
+ PurpleRequestFieldsCb cb;
+
+ label = va_arg(args, const gchar*);
+ if (label == NULL)
+ break;
+ cb = va_arg(args, PurpleRequestFieldsCb);
+
+ extra = g_slist_append(extra, g_strdup(label));
+ extra = g_slist_append(extra, cb);
+ }
+
+ va_end(args);
+
+ cpar->extra_actions = extra;
+}
+
+GSList *
+purple_request_cpar_get_extra_actions(PurpleRequestCommonParameters *cpar)
+{
+ if (cpar == NULL)
+ return NULL;
+
+ return cpar->extra_actions;
+}
+
+void
+purple_request_cpar_set_parent_from(PurpleRequestCommonParameters *cpar,
+ gpointer ui_handle)
+{
+ g_return_if_fail(cpar != NULL);
+
+ cpar->parent_from = ui_handle;
+}
+
+gpointer
+purple_request_cpar_get_parent_from(PurpleRequestCommonParameters *cpar)
+{
+ if (cpar == NULL)
+ return NULL;
+
+ return cpar->parent_from;
+}
+
+static PurpleRequestInfo *
+purple_request_info_from_ui_handle(void *ui_handle)
+{
+ GList *it;
+
+ g_return_val_if_fail(ui_handle != NULL, NULL);
+
+ for (it = handles; it != NULL; it = g_list_next(it)) {
+ PurpleRequestInfo *info = it->data;
+
+ if (info->ui_handle == ui_handle)
+ return info;
+ }
+
+ return NULL;
+}
PurpleRequestFields *
purple_request_fields_new(void)
@@ -61,9 +497,12 @@ purple_request_fields_destroy(PurpleRequestFields *fields)
{
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(fields->required_fields);
+ g_list_free(fields->validated_fields);
+ g_list_free(fields->autosensitive_fields);
g_hash_table_destroy(fields->fields);
g_free(fields);
}
@@ -96,6 +535,15 @@ purple_request_fields_add_group(PurpleRequestFields *fields,
g_list_append(fields->required_fields, field);
}
+ if (purple_request_field_is_validatable(field)) {
+ fields->validated_fields =
+ g_list_append(fields->validated_fields, field);
+ }
+
+ if (field->sensitivity_cb != NULL) {
+ fields->autosensitive_fields =
+ g_list_append(fields->autosensitive_fields, field);
+ }
}
}
@@ -107,6 +555,32 @@ purple_request_fields_get_groups(const PurpleRequestFields *fields)
return fields->groups;
}
+void
+purple_request_fields_set_tab_names(PurpleRequestFields *fields,
+ const gchar **tab_names)
+{
+ guint i, tab_count;
+ gchar **new_names;
+
+ g_return_if_fail(fields != NULL);
+
+ tab_count = (tab_names != NULL) ? g_strv_length((gchar **)tab_names) : 0;
+ new_names = (tab_count > 0) ? g_new0(gchar*, tab_count + 1) : NULL;
+ for (i = 0; i < tab_count; i++)
+ new_names[i] = g_strdup(tab_names[i]);
+
+ g_strfreev(fields->tab_names);
+ fields->tab_names = new_names;
+}
+
+const gchar **
+purple_request_fields_get_tab_names(const PurpleRequestFields *fields)
+{
+ g_return_val_if_fail(fields != NULL, NULL);
+
+ return (const gchar **)fields->tab_names;
+}
+
gboolean
purple_request_fields_exists(const PurpleRequestFields *fields, const char *id)
{
@@ -116,7 +590,7 @@ purple_request_fields_exists(const PurpleRequestFields *fields, const char *id)
return (g_hash_table_lookup(fields->fields, id) != NULL);
}
-GList *
+const GList *
purple_request_fields_get_required(const PurpleRequestFields *fields)
{
g_return_val_if_fail(fields != NULL, NULL);
@@ -124,6 +598,22 @@ purple_request_fields_get_required(const PurpleRequestFields *fields)
return fields->required_fields;
}
+const GList *
+purple_request_fields_get_validatable(const PurpleRequestFields *fields)
+{
+ g_return_val_if_fail(fields != NULL, NULL);
+
+ return fields->validated_fields;
+}
+
+const GList *
+purple_request_fields_get_autosensitive(const PurpleRequestFields *fields)
+{
+ g_return_val_if_fail(fields != NULL, NULL);
+
+ return fields->autosensitive_fields;
+}
+
gboolean
purple_request_fields_is_field_required(const PurpleRequestFields *fields,
const char *id)
@@ -167,23 +657,62 @@ purple_request_fields_all_required_filled(const PurpleRequestFields *fields)
{
PurpleRequestField *field = (PurpleRequestField *)l->data;
- switch (purple_request_field_get_type(field))
- {
- case PURPLE_REQUEST_FIELD_STRING:
- if (purple_request_field_string_get_value(field) == NULL ||
- *(purple_request_field_string_get_value(field)) == '\0')
- return FALSE;
+ if (!purple_request_field_is_filled(field))
+ return FALSE;
+ }
- break;
+ return TRUE;
+}
- default:
- break;
- }
+gboolean
+purple_request_fields_all_valid(const PurpleRequestFields *fields)
+{
+ GList *l;
+
+ g_return_val_if_fail(fields != NULL, FALSE);
+
+ for (l = fields->validated_fields; l != NULL; l = l->next)
+ {
+ PurpleRequestField *field = (PurpleRequestField *)l->data;
+
+ if (!purple_request_field_is_valid(field, NULL))
+ return FALSE;
}
return TRUE;
}
+static void
+purple_request_fields_check_sensitivity(PurpleRequestFields *fields)
+{
+ GList *it;
+
+ g_return_if_fail(fields != NULL);
+
+ for (it = fields->autosensitive_fields; it; it = g_list_next(it)) {
+ PurpleRequestField *field = it->data;
+
+ if (field->sensitivity_cb == NULL) {
+ g_warn_if_reached();
+ continue;
+ }
+
+ purple_request_field_set_sensitive(field,
+ field->sensitivity_cb(field));
+ }
+}
+
+static void
+purple_request_fields_check_others_sensitivity(PurpleRequestField *field)
+{
+ g_return_if_fail(field != NULL);
+
+ if (field->group == NULL || field->group->fields_list == NULL)
+ return;
+
+ purple_request_fields_check_sensitivity(field->group->fields_list);
+}
+
PurpleRequestField *
purple_request_fields_get_field(const PurpleRequestFields *fields, const char *id)
{
@@ -242,16 +771,17 @@ purple_request_fields_get_bool(const PurpleRequestFields *fields, const char *id
return purple_request_field_bool_get_value(field);
}
-int
-purple_request_fields_get_choice(const PurpleRequestFields *fields, const char *id)
+gpointer
+purple_request_fields_get_choice(const PurpleRequestFields *fields,
+ const char *id)
{
PurpleRequestField *field;
- g_return_val_if_fail(fields != NULL, -1);
- g_return_val_if_fail(id != NULL, -1);
+ g_return_val_if_fail(fields != NULL, NULL);
+ g_return_val_if_fail(id != NULL, NULL);
if ((field = purple_request_fields_get_field(fields, id)) == NULL)
- return -1;
+ return NULL;
return purple_request_field_choice_get_value(field);
}
@@ -271,6 +801,20 @@ purple_request_fields_get_account(const PurpleRequestFields *fields,
return purple_request_field_account_get_value(field);
}
+gpointer purple_request_fields_get_ui_data(const PurpleRequestFields *fields)
+{
+ g_return_val_if_fail(fields != NULL, NULL);
+
+ return fields->ui_data;
+}
+
+void purple_request_fields_set_ui_data(PurpleRequestFields *fields, gpointer ui_data)
+{
+ g_return_if_fail(fields != NULL);
+
+ fields->ui_data = ui_data;
+}
+
PurpleRequestFieldGroup *
purple_request_field_group_new(const char *title)
{
@@ -284,6 +828,20 @@ purple_request_field_group_new(const char *title)
}
void
+purple_request_field_group_set_tab(PurpleRequestFieldGroup *group, guint tab_no)
+{
+ g_return_if_fail(group != NULL);
+
+ group->tab_no = tab_no;
+}
+
+guint
+purple_request_field_group_get_tab(const PurpleRequestFieldGroup *group)
+{
+ return group->tab_no;
+}
+
+void
purple_request_field_group_destroy(PurpleRequestFieldGroup *group)
{
g_return_if_fail(group != NULL);
@@ -315,6 +873,18 @@ purple_request_field_group_add_field(PurpleRequestFieldGroup *group,
group->fields_list->required_fields =
g_list_append(group->fields_list->required_fields, field);
}
+
+ if (purple_request_field_is_validatable(field))
+ {
+ group->fields_list->validated_fields =
+ g_list_append(group->fields_list->validated_fields, field);
+ }
+
+ if (field->sensitivity_cb != NULL)
+ {
+ group->fields_list->autosensitive_fields =
+ g_list_append(group->fields_list->autosensitive_fields, field);
+ }
}
field->group = group;
@@ -337,6 +907,14 @@ purple_request_field_group_get_fields(const PurpleRequestFieldGroup *group)
return group->fields;
}
+PurpleRequestFields *
+purple_request_field_group_get_fields_list(const PurpleRequestFieldGroup *group)
+{
+ g_return_val_if_fail(group != NULL, NULL);
+
+ return group->fields_list;
+}
+
PurpleRequestField *
purple_request_field_new(const char *id, const char *text,
PurpleRequestFieldType type)
@@ -353,6 +931,7 @@ purple_request_field_new(const char *id, const char *text,
purple_request_field_set_label(field, text);
purple_request_field_set_visible(field, TRUE);
+ purple_request_field_set_sensitive(field, TRUE);
return field;
}
@@ -365,6 +944,7 @@ purple_request_field_destroy(PurpleRequestField *field)
g_free(field->id);
g_free(field->label);
g_free(field->type_hint);
+ g_free(field->tooltip);
if (field->type == PURPLE_REQUEST_FIELD_STRING)
{
@@ -373,10 +953,21 @@ purple_request_field_destroy(PurpleRequestField *field)
}
else if (field->type == PURPLE_REQUEST_FIELD_CHOICE)
{
- if (field->u.choice.labels != NULL)
+ if (field->u.choice.elements != NULL)
{
- g_list_foreach(field->u.choice.labels, (GFunc)g_free, NULL);
- g_list_free(field->u.choice.labels);
+ GList *it = field->u.choice.elements;
+ while (it != NULL) {
+ g_free(it->data);
+ it = g_list_next(it); /* value */
+ if (it == NULL) {
+ g_warn_if_reached();
+ break;
+ }
+ if (it->data && field->u.choice.data_destroy)
+ field->u.choice.data_destroy(it->data);
+ it = g_list_next(it); /* next label */
+ }
+ g_list_free(field->u.choice.elements);
}
}
else if (field->type == PURPLE_REQUEST_FIELD_LIST)
@@ -396,6 +987,10 @@ purple_request_field_destroy(PurpleRequestField *field)
g_hash_table_destroy(field->u.list.item_data);
g_hash_table_destroy(field->u.list.selected_table);
}
+ else if (field->type == PURPLE_REQUEST_FIELD_DATASHEET)
+ {
+ purple_request_datasheet_free(field->u.datasheet.sheet);
+ }
g_free(field);
}
@@ -428,6 +1023,15 @@ purple_request_field_set_type_hint(PurpleRequestField *field,
}
void
+purple_request_field_set_tooltip(PurpleRequestField *field, const char *tooltip)
+{
+ g_return_if_fail(field != NULL);
+
+ g_free(field->tooltip);
+ field->tooltip = g_strdup(tooltip);
+}
+
+void
purple_request_field_set_required(PurpleRequestField *field, gboolean required)
{
g_return_if_fail(field != NULL);
@@ -455,7 +1059,7 @@ purple_request_field_set_required(PurpleRequestField *field, gboolean required)
}
PurpleRequestFieldType
-purple_request_field_get_type(const PurpleRequestField *field)
+purple_request_field_get_field_type(const PurpleRequestField *field)
{
g_return_val_if_fail(field != NULL, PURPLE_REQUEST_FIELD_NONE);
@@ -495,13 +1099,21 @@ purple_request_field_is_visible(const PurpleRequestField *field)
}
const char *
-purple_request_field_get_type_hint(const PurpleRequestField *field)
+purple_request_field_get_field_type_hint(const PurpleRequestField *field)
{
g_return_val_if_fail(field != NULL, NULL);
return field->type_hint;
}
+const char *
+purple_request_field_get_tooltip(const PurpleRequestField *field)
+{
+ g_return_val_if_fail(field != NULL, NULL);
+
+ return field->tooltip;
+}
+
gboolean
purple_request_field_is_required(const PurpleRequestField *field)
{
@@ -510,6 +1122,112 @@ purple_request_field_is_required(const PurpleRequestField *field)
return field->required;
}
+gboolean
+purple_request_field_is_filled(const PurpleRequestField *field)
+{
+ g_return_val_if_fail(field != NULL, FALSE);
+
+ switch (purple_request_field_get_field_type(field))
+ {
+ case PURPLE_REQUEST_FIELD_STRING:
+ return (purple_request_field_string_get_value(field) != NULL &&
+ *(purple_request_field_string_get_value(field)) != '\0');
+ default:
+ return TRUE;
+ }
+}
+
+void
+purple_request_field_set_validator(PurpleRequestField *field,
+ PurpleRequestFieldValidator validator, void *user_data)
+{
+ g_return_if_fail(field != NULL);
+
+ field->validator = validator;
+ field->validator_data = validator ? user_data : NULL;
+
+ if (field->group != NULL)
+ {
+ PurpleRequestFields *flist = field->group->fields_list;
+ flist->validated_fields = g_list_remove(flist->validated_fields,
+ field);
+ if (validator)
+ {
+ flist->validated_fields = g_list_append(
+ flist->validated_fields, field);
+ }
+ }
+}
+
+gboolean
+purple_request_field_is_validatable(PurpleRequestField *field)
+{
+ g_return_val_if_fail(field != NULL, FALSE);
+
+ return field->validator != NULL;
+}
+
+gboolean
+purple_request_field_is_valid(PurpleRequestField *field, gchar **errmsg)
+{
+ gboolean valid;
+
+ g_return_val_if_fail(field != NULL, FALSE);
+
+ if (!field->validator)
+ return TRUE;
+
+ if (!purple_request_field_is_required(field) &&
+ !purple_request_field_is_filled(field))
+ return TRUE;
+
+ valid = field->validator(field, errmsg, field->validator_data);
+
+ if (valid && errmsg)
+ *errmsg = NULL;
+
+ return valid;
+}
+
+void
+purple_request_field_set_sensitive(PurpleRequestField *field,
+ gboolean sensitive)
+{
+ g_return_if_fail(field != NULL);
+
+ field->sensitive = sensitive;
+}
+
+gboolean
+purple_request_field_is_sensitive(PurpleRequestField *field)
+{
+ g_return_val_if_fail(field != NULL, FALSE);
+
+ return field->sensitive;
+}
+
+void
+purple_request_field_set_sensitivity_cb(PurpleRequestField *field,
+ PurpleRequestFieldSensitivityCb cb)
+{
+ PurpleRequestFields *flist;
+
+ g_return_if_fail(field != NULL);
+
+ field->sensitivity_cb = cb;
+
+ if (!field->group || !field->group->fields_list)
+ return;
+ flist = field->group->fields_list;
+ flist->autosensitive_fields = g_list_remove(flist->autosensitive_fields,
+ field);
+ if (cb != NULL)
+ {
+ flist->autosensitive_fields = g_list_append(
+ flist->autosensitive_fields, field);
+ }
+}
+
PurpleRequestField *
purple_request_field_string_new(const char *id, const char *text,
const char *default_value, gboolean multiline)
@@ -522,7 +1240,6 @@ purple_request_field_string_new(const char *id, const char *text,
field = purple_request_field_new(id, text, PURPLE_REQUEST_FIELD_STRING);
field->u.string.multiline = multiline;
- field->u.string.editable = TRUE;
purple_request_field_string_set_default_value(field, default_value);
purple_request_field_string_set_value(field, default_value);
@@ -549,6 +1266,8 @@ purple_request_field_string_set_value(PurpleRequestField *field, const char *val
g_free(field->u.string.value);
field->u.string.value = g_strdup(value);
+
+ purple_request_fields_check_others_sensitivity(field);
}
void
@@ -560,16 +1279,6 @@ purple_request_field_string_set_masked(PurpleRequestField *field, gboolean maske
field->u.string.masked = masked;
}
-void
-purple_request_field_string_set_editable(PurpleRequestField *field,
- gboolean editable)
-{
- g_return_if_fail(field != NULL);
- g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_STRING);
-
- field->u.string.editable = editable;
-}
-
const char *
purple_request_field_string_get_default_value(const PurpleRequestField *field)
{
@@ -606,18 +1315,9 @@ purple_request_field_string_is_masked(const PurpleRequestField *field)
return field->u.string.masked;
}
-gboolean
-purple_request_field_string_is_editable(const PurpleRequestField *field)
-{
- g_return_val_if_fail(field != NULL, FALSE);
- g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_STRING, FALSE);
-
- return field->u.string.editable;
-}
-
PurpleRequestField *
purple_request_field_int_new(const char *id, const char *text,
- int default_value)
+ int default_value, int lower_bound, int upper_bound)
{
PurpleRequestField *field;
@@ -626,6 +1326,8 @@ purple_request_field_int_new(const char *id, const char *text,
field = purple_request_field_new(id, text, PURPLE_REQUEST_FIELD_INTEGER);
+ purple_request_field_int_set_lower_bound(field, lower_bound);
+ purple_request_field_int_set_upper_bound(field, upper_bound);
purple_request_field_int_set_default_value(field, default_value);
purple_request_field_int_set_value(field, default_value);
@@ -643,12 +1345,40 @@ purple_request_field_int_set_default_value(PurpleRequestField *field,
}
void
+purple_request_field_int_set_lower_bound(PurpleRequestField *field,
+ int lower_bound)
+{
+ g_return_if_fail(field != NULL);
+ g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_INTEGER);
+
+ field->u.integer.lower_bound = lower_bound;
+}
+
+void
+purple_request_field_int_set_upper_bound(PurpleRequestField *field,
+ int upper_bound)
+{
+ g_return_if_fail(field != NULL);
+ g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_INTEGER);
+
+ field->u.integer.upper_bound = upper_bound;
+}
+
+void
purple_request_field_int_set_value(PurpleRequestField *field, int value)
{
g_return_if_fail(field != NULL);
g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_INTEGER);
+ if (value < field->u.integer.lower_bound ||
+ value > field->u.integer.upper_bound) {
+ purple_debug_error("request", "Int value out of bounds\n");
+ return;
+ }
+
field->u.integer.value = value;
+
+ purple_request_fields_check_others_sensitivity(field);
}
int
@@ -661,6 +1391,24 @@ purple_request_field_int_get_default_value(const PurpleRequestField *field)
}
int
+purple_request_field_int_get_lower_bound(const PurpleRequestField *field)
+{
+ g_return_val_if_fail(field != NULL, 0);
+ g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_INTEGER, 0);
+
+ return field->u.integer.lower_bound;
+}
+
+int
+purple_request_field_int_get_upper_bound(const PurpleRequestField *field)
+{
+ g_return_val_if_fail(field != NULL, 0);
+ g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_INTEGER, 0);
+
+ return field->u.integer.upper_bound;
+}
+
+int
purple_request_field_int_get_value(const PurpleRequestField *field)
{
g_return_val_if_fail(field != NULL, 0);
@@ -703,6 +1451,8 @@ purple_request_field_bool_set_value(PurpleRequestField *field, gboolean value)
g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_BOOLEAN);
field->u.boolean.value = value;
+
+ purple_request_fields_check_others_sensitivity(field);
}
gboolean
@@ -725,7 +1475,7 @@ purple_request_field_bool_get_value(const PurpleRequestField *field)
PurpleRequestField *
purple_request_field_choice_new(const char *id, const char *text,
- int default_value)
+ gpointer default_value)
{
PurpleRequestField *field;
@@ -741,19 +1491,22 @@ purple_request_field_choice_new(const char *id, const char *text,
}
void
-purple_request_field_choice_add(PurpleRequestField *field, const char *label)
+purple_request_field_choice_add(PurpleRequestField *field, const char *label,
+ gpointer value)
{
g_return_if_fail(field != NULL);
g_return_if_fail(label != NULL);
g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_CHOICE);
- field->u.choice.labels = g_list_append(field->u.choice.labels,
- g_strdup(label));
+ field->u.choice.elements = g_list_append(field->u.choice.elements,
+ g_strdup(label));
+ field->u.choice.elements = g_list_append(field->u.choice.elements,
+ value);
}
void
purple_request_field_choice_set_default_value(PurpleRequestField *field,
- int default_value)
+ gpointer default_value)
{
g_return_if_fail(field != NULL);
g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_CHOICE);
@@ -762,40 +1515,51 @@ purple_request_field_choice_set_default_value(PurpleRequestField *field,
}
void
-purple_request_field_choice_set_value(PurpleRequestField *field,
- int value)
+purple_request_field_choice_set_value(PurpleRequestField *field, gpointer value)
{
g_return_if_fail(field != NULL);
g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_CHOICE);
field->u.choice.value = value;
+
+ purple_request_fields_check_others_sensitivity(field);
}
-int
+gpointer
purple_request_field_choice_get_default_value(const PurpleRequestField *field)
{
- g_return_val_if_fail(field != NULL, -1);
- g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_CHOICE, -1);
+ g_return_val_if_fail(field != NULL, NULL);
+ g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_CHOICE, NULL);
return field->u.choice.default_value;
}
-int
+gpointer
purple_request_field_choice_get_value(const PurpleRequestField *field)
{
- g_return_val_if_fail(field != NULL, -1);
- g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_CHOICE, -1);
+ g_return_val_if_fail(field != NULL, NULL);
+ g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_CHOICE, NULL);
return field->u.choice.value;
}
GList *
-purple_request_field_choice_get_labels(const PurpleRequestField *field)
+purple_request_field_choice_get_elements(const PurpleRequestField *field)
{
g_return_val_if_fail(field != NULL, NULL);
g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_CHOICE, NULL);
- return field->u.choice.labels;
+ return field->u.choice.elements;
+}
+
+void
+purple_request_field_choice_set_data_destructor(PurpleRequestField *field,
+ GDestroyNotify destroy)
+{
+ g_return_if_fail(field != NULL);
+ g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_CHOICE);
+
+ field->u.choice.data_destroy = destroy;
}
PurpleRequestField *
@@ -847,13 +1611,6 @@ purple_request_field_list_get_data(const PurpleRequestField *field,
}
void
-purple_request_field_list_add(PurpleRequestField *field, const char *item,
- void *data)
-{
- purple_request_field_list_add_icon(field, item, NULL, data);
-}
-
-void
purple_request_field_list_add_icon(PurpleRequestField *field, const char *item, const char* icon_path,
void *data)
{
@@ -1118,6 +1875,8 @@ purple_request_field_account_set_value(PurpleRequestField *field,
g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_ACCOUNT);
field->u.account.account = value;
+
+ purple_request_fields_check_others_sensitivity(field);
}
void
@@ -1194,265 +1953,475 @@ purple_request_field_account_get_filter(const PurpleRequestField *field)
return field->u.account.filter_func;
}
+PurpleRequestField *
+purple_request_field_certificate_new(const char *id, const char *text, PurpleCertificate *cert)
+{
+ PurpleRequestField *field;
+
+ g_return_val_if_fail(id != NULL, NULL);
+ g_return_val_if_fail(text != NULL, NULL);
+ g_return_val_if_fail(cert != NULL, NULL);
+
+ field = purple_request_field_new(id, text, PURPLE_REQUEST_FIELD_CERTIFICATE);
+
+ field->u.certificate.cert = cert;
+
+ return field;
+}
+
+PurpleCertificate *
+purple_request_field_certificate_get_value(const PurpleRequestField *field)
+{
+ g_return_val_if_fail(field != NULL, NULL);
+ g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_CERTIFICATE, NULL);
+
+ return field->u.certificate.cert;
+}
+
+PurpleRequestField *
+purple_request_field_datasheet_new(const char *id,
+ const gchar *text, PurpleRequestDatasheet *sheet)
+{
+ PurpleRequestField *field;
+
+ g_return_val_if_fail(id != NULL, NULL);
+ g_return_val_if_fail(sheet != NULL, NULL);
+
+ field = purple_request_field_new(id, text, PURPLE_REQUEST_FIELD_DATASHEET);
+
+ field->u.datasheet.sheet = sheet;
+
+ return field;
+}
+
+PurpleRequestDatasheet *
+purple_request_field_datasheet_get_sheet(PurpleRequestField *field)
+{
+ g_return_val_if_fail(field != NULL, NULL);
+ g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_DATASHEET, NULL);
+
+ return field->u.datasheet.sheet;
+}
+
+/* -- */
+
+gboolean
+purple_request_field_email_validator(PurpleRequestField *field, gchar **errmsg,
+ void *user_data)
+{
+ const char *value;
+
+ g_return_val_if_fail(field != NULL, FALSE);
+ g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_STRING, FALSE);
+
+ value = purple_request_field_string_get_value(field);
+
+ if (value != NULL && purple_email_is_valid(value))
+ return TRUE;
+
+ if (errmsg)
+ *errmsg = g_strdup(_("Invalid email address"));
+ return FALSE;
+}
+
+gboolean
+purple_request_field_alphanumeric_validator(PurpleRequestField *field,
+ gchar **errmsg, void *allowed_characters)
+{
+ const char *value;
+ gchar invalid_char = '\0';
+
+ g_return_val_if_fail(field != NULL, FALSE);
+ g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_STRING, FALSE);
+
+ value = purple_request_field_string_get_value(field);
+
+ g_return_val_if_fail(value != NULL, FALSE);
+
+ if (allowed_characters)
+ {
+ gchar *value_r = g_strdup(value);
+ g_strcanon(value_r, allowed_characters, '\0');
+ invalid_char = value[strlen(value_r)];
+ g_free(value_r);
+ }
+ else
+ {
+ while (value)
+ {
+ if (!g_ascii_isalnum(*value))
+ {
+ invalid_char = *value;
+ break;
+ }
+ value++;
+ }
+ }
+ if (!invalid_char)
+ return TRUE;
+
+ if (errmsg)
+ *errmsg = g_strdup_printf(_("Invalid character '%c'"),
+ invalid_char);
+ return FALSE;
+}
+
/* -- */
+static gchar *
+purple_request_strip_html_custom(const gchar *html)
+{
+ gchar *tmp, *ret;
+
+ tmp = purple_strreplace(html, "\n", "<br>");
+ ret = purple_markup_strip_html(tmp);
+ g_free(tmp);
+
+ return ret;
+}
+
+static gchar **
+purple_request_strip_html(PurpleRequestCommonParameters *cpar,
+ const char **primary, const char **secondary)
+{
+ PurpleRequestUiOps *ops = purple_request_get_ui_ops();
+ gchar **ret;
+
+ if (!purple_request_cpar_is_html(cpar))
+ return NULL;
+ if (ops->features & PURPLE_REQUEST_FEATURE_HTML)
+ return NULL;
+
+ ret = g_new0(gchar*, 3);
+ *primary = ret[0] = purple_request_strip_html_custom(*primary);
+ *secondary = ret[1] = purple_request_strip_html_custom(*secondary);
+
+ return ret;
+}
+
void *
purple_request_input(void *handle, const char *title, const char *primary,
const char *secondary, const char *default_value,
gboolean multiline, gboolean masked, gchar *hint,
const char *ok_text, GCallback ok_cb,
const char *cancel_text, GCallback cancel_cb,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
+ PurpleRequestCommonParameters *cpar,
void *user_data)
{
PurpleRequestUiOps *ops;
- g_return_val_if_fail(ok_text != NULL, NULL);
- g_return_val_if_fail(ok_cb != NULL, NULL);
+ if (G_UNLIKELY(ok_text == NULL || ok_cb == NULL)) {
+ purple_request_cpar_unref(cpar);
+ g_warn_if_fail(ok_text != NULL);
+ g_warn_if_fail(ok_cb != NULL);
+ g_return_val_if_reached(NULL);
+ }
ops = purple_request_get_ui_ops();
if (ops != NULL && ops->request_input != NULL) {
PurpleRequestInfo *info;
+ gchar **tmp;
+
+ tmp = purple_request_strip_html(cpar, &primary, &secondary);
info = g_new0(PurpleRequestInfo, 1);
info->type = PURPLE_REQUEST_INPUT;
info->handle = handle;
info->ui_handle = ops->request_input(title, primary, secondary,
- default_value,
- multiline, masked, hint,
- ok_text, ok_cb,
- cancel_text, cancel_cb,
- account, who, conv,
- user_data);
+ default_value, multiline, masked, hint, ok_text, ok_cb,
+ cancel_text, cancel_cb, cpar, user_data);
handles = g_list_append(handles, info);
+ g_strfreev(tmp);
+ purple_request_cpar_unref(cpar);
return info->ui_handle;
}
+ purple_request_cpar_unref(cpar);
return NULL;
}
void *
purple_request_choice(void *handle, const char *title, const char *primary,
- const char *secondary, int default_value,
- const char *ok_text, GCallback ok_cb,
- const char *cancel_text, GCallback cancel_cb,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
- void *user_data, ...)
+ const char *secondary, gpointer default_value, const char *ok_text,
+ GCallback ok_cb, const char *cancel_text, GCallback cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data, ...)
{
void *ui_handle;
va_list args;
- g_return_val_if_fail(ok_text != NULL, NULL);
- g_return_val_if_fail(ok_cb != NULL, NULL);
+ if (G_UNLIKELY(ok_text == NULL || ok_cb == NULL)) {
+ purple_request_cpar_unref(cpar);
+ g_warn_if_fail(ok_text != NULL);
+ g_warn_if_fail(ok_cb != NULL);
+ g_return_val_if_reached(NULL);
+ }
va_start(args, user_data);
ui_handle = purple_request_choice_varg(handle, title, primary, secondary,
default_value, ok_text, ok_cb,
cancel_text, cancel_cb,
- account, who, conv, user_data, args);
+ cpar, user_data, args);
va_end(args);
return ui_handle;
}
void *
-purple_request_choice_varg(void *handle, const char *title,
- const char *primary, const char *secondary,
- int default_value,
- const char *ok_text, GCallback ok_cb,
- const char *cancel_text, GCallback cancel_cb,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
- void *user_data, va_list choices)
+purple_request_choice_varg(void *handle, const char *title, const char *primary,
+ const char *secondary, gpointer default_value, const char *ok_text,
+ GCallback ok_cb, const char *cancel_text, GCallback cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data, va_list choices)
{
PurpleRequestUiOps *ops;
- g_return_val_if_fail(ok_text != NULL, NULL);
- g_return_val_if_fail(ok_cb != NULL, NULL);
- g_return_val_if_fail(cancel_text != NULL, NULL);
+ if (G_UNLIKELY(ok_text == NULL || ok_cb == NULL ||
+ cancel_text == NULL))
+ {
+ purple_request_cpar_unref(cpar);
+ g_warn_if_fail(ok_text != NULL);
+ g_warn_if_fail(ok_cb != NULL);
+ g_warn_if_fail(cancel_text != NULL);
+ g_return_val_if_reached(NULL);
+ }
ops = purple_request_get_ui_ops();
if (ops != NULL && ops->request_choice != NULL) {
PurpleRequestInfo *info;
+ gchar **tmp;
+
+ tmp = purple_request_strip_html(cpar, &primary, &secondary);
info = g_new0(PurpleRequestInfo, 1);
info->type = PURPLE_REQUEST_CHOICE;
info->handle = handle;
info->ui_handle = ops->request_choice(title, primary, secondary,
- default_value,
- ok_text, ok_cb,
- cancel_text, cancel_cb,
- account, who, conv,
- user_data, choices);
+ default_value, ok_text, ok_cb, cancel_text, cancel_cb,
+ cpar, user_data, choices);
handles = g_list_append(handles, info);
+ g_strfreev(tmp);
+ purple_request_cpar_unref(cpar);
return info->ui_handle;
}
+ purple_request_cpar_unref(cpar);
return NULL;
}
void *
purple_request_action(void *handle, const char *title, const char *primary,
- const char *secondary, int default_action,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
- void *user_data, size_t action_count, ...)
-{
- void *ui_handle;
- va_list args;
-
- g_return_val_if_fail(action_count > 0, NULL);
-
- va_start(args, action_count);
- ui_handle = purple_request_action_varg(handle, title, primary, secondary,
- default_action, account, who, conv,
- user_data, action_count, args);
- va_end(args);
-
- return ui_handle;
-}
-
-void *
-purple_request_action_with_icon(void *handle, const char *title,
- const char *primary,
- const char *secondary, int default_action,
- PurpleAccount *account, const char *who,
- PurpleConversation *conv, gconstpointer icon_data,
- gsize icon_size, void *user_data, size_t action_count, ...)
+ const char *secondary, int default_action,
+ PurpleRequestCommonParameters *cpar, void *user_data,
+ size_t action_count, ...)
{
void *ui_handle;
va_list args;
- g_return_val_if_fail(action_count > 0, NULL);
-
va_start(args, action_count);
- ui_handle = purple_request_action_with_icon_varg(handle, title, primary,
- secondary, default_action, account, who, conv, icon_data, icon_size,
- user_data, action_count, args);
+ ui_handle = purple_request_action_varg(handle, title, primary,
+ secondary, default_action, cpar, user_data, action_count, args);
va_end(args);
return ui_handle;
}
-
void *
-purple_request_action_varg(void *handle, const char *title,
- const char *primary, const char *secondary,
- int default_action,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
- void *user_data, size_t action_count, va_list actions)
+purple_request_action_varg(void *handle, const char *title, const char *primary,
+ const char *secondary, int default_action,
+ PurpleRequestCommonParameters *cpar, void *user_data,
+ size_t action_count, va_list actions)
{
PurpleRequestUiOps *ops;
- g_return_val_if_fail(action_count > 0, NULL);
-
ops = purple_request_get_ui_ops();
if (ops != NULL && ops->request_action != NULL) {
PurpleRequestInfo *info;
+ gchar **tmp;
+
+ tmp = purple_request_strip_html(cpar, &primary, &secondary);
info = g_new0(PurpleRequestInfo, 1);
info->type = PURPLE_REQUEST_ACTION;
info->handle = handle;
info->ui_handle = ops->request_action(title, primary, secondary,
- default_action, account, who, conv,
- user_data, action_count, actions);
+ default_action, cpar, user_data, action_count, actions);
handles = g_list_append(handles, info);
+ g_strfreev(tmp);
+ purple_request_cpar_unref(cpar);
return info->ui_handle;
}
+ purple_request_cpar_unref(cpar);
return NULL;
}
void *
-purple_request_action_with_icon_varg(void *handle, const char *title,
- const char *primary, const char *secondary,
- int default_action,
- PurpleAccount *account, const char *who,
- PurpleConversation *conv, gconstpointer icon_data,
- gsize icon_size,
- void *user_data, size_t action_count, va_list actions)
+purple_request_wait(void *handle, const char *title, const char *primary,
+ const char *secondary, gboolean with_progress,
+ PurpleRequestCancelCb cancel_cb, PurpleRequestCommonParameters *cpar,
+ void *user_data)
{
PurpleRequestUiOps *ops;
- g_return_val_if_fail(action_count > 0, NULL);
+ if (primary == NULL)
+ primary = _("Please wait...");
ops = purple_request_get_ui_ops();
- if (ops != NULL && ops->request_action_with_icon != NULL) {
+ if (ops != NULL && ops->request_wait != NULL) {
PurpleRequestInfo *info;
+ gchar **tmp;
+
+ tmp = purple_request_strip_html(cpar, &primary, &secondary);
info = g_new0(PurpleRequestInfo, 1);
- info->type = PURPLE_REQUEST_ACTION;
+ info->type = PURPLE_REQUEST_WAIT;
info->handle = handle;
- info->ui_handle = ops->request_action_with_icon(title, primary, secondary,
- default_action, account, who, conv,
- icon_data, icon_size,
- user_data, action_count, actions);
+ info->ui_handle = ops->request_wait(title, primary, secondary,
+ with_progress, cancel_cb, cpar, user_data);
handles = g_list_append(handles, info);
+ g_strfreev(tmp);
+ purple_request_cpar_unref(cpar);
return info->ui_handle;
- } else {
- /* Fall back on the non-icon request if the UI doesn't support icon
- requests */
- return purple_request_action_varg(handle, title, primary, secondary,
- default_action, account, who, conv, user_data, action_count, actions);
}
- return NULL;
+ if (cpar == NULL)
+ cpar = purple_request_cpar_new();
+ if (purple_request_cpar_get_icon(cpar) == PURPLE_REQUEST_ICON_DEFAULT)
+ purple_request_cpar_set_icon(cpar, PURPLE_REQUEST_ICON_WAIT);
+
+ return purple_request_action(handle, title, primary, secondary,
+ PURPLE_DEFAULT_ACTION_NONE, cpar, user_data,
+ cancel_cb ? 1 : 0, _("Cancel"), cancel_cb);
}
+void
+purple_request_wait_pulse(void *ui_handle)
+{
+ PurpleRequestUiOps *ops;
+
+ ops = purple_request_get_ui_ops();
+
+ if (ops == NULL || ops->request_wait_update == NULL)
+ return;
+
+ ops->request_wait_update(ui_handle, TRUE, 0.0);
+}
+
+void
+purple_request_wait_progress(void *ui_handle, gfloat fraction)
+{
+ PurpleRequestUiOps *ops;
+
+ ops = purple_request_get_ui_ops();
+
+ if (ops == NULL || ops->request_wait_update == NULL)
+ return;
+
+ if (fraction < 0.0 || fraction > 1.0) {
+ purple_debug_warning("request", "Fraction parameter out of "
+ "range: %f", fraction);
+ if (fraction < 0.0)
+ fraction = 0.0;
+ else /* if (fraction > 1.0) */
+ fraction = 1.0;
+ }
+
+ ops->request_wait_update(ui_handle, FALSE, fraction);
+}
+
+static void
+purple_request_fields_strip_html(PurpleRequestFields *fields)
+{
+ GList *itg;
+
+ for (itg = fields->groups; itg != NULL; itg = g_list_next(itg)) {
+ PurpleRequestFieldGroup *group = itg->data;
+ GList *itf;
+
+ for (itf = group->fields; itf != NULL; itf = g_list_next(itf)) {
+ PurpleRequestField *field = itf->data;
+ gchar *new_label;
+
+ new_label = purple_request_strip_html_custom(
+ field->label);
+ if (g_strcmp0(new_label, field->label) == 0) {
+ g_free(new_label);
+ continue;
+ }
+ g_free(field->label);
+ field->label = new_label;
+ }
+ }
+}
void *
purple_request_fields(void *handle, const char *title, const char *primary,
- const char *secondary, PurpleRequestFields *fields,
- const char *ok_text, GCallback ok_cb,
- const char *cancel_text, GCallback cancel_cb,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
- void *user_data)
+ const char *secondary, PurpleRequestFields *fields, const char *ok_text,
+ GCallback ok_cb, const char *cancel_text, GCallback cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data)
{
PurpleRequestUiOps *ops;
- g_return_val_if_fail(fields != NULL, NULL);
- g_return_val_if_fail(ok_text != NULL, NULL);
- g_return_val_if_fail(ok_cb != NULL, NULL);
- g_return_val_if_fail(cancel_text != NULL, NULL);
+ if (G_UNLIKELY(fields == NULL ||
+ ((ok_text == NULL) != (ok_cb == NULL)) ||
+ cancel_text == NULL))
+ {
+ purple_request_cpar_unref(cpar);
+ g_warn_if_fail(fields != NULL);
+ g_warn_if_fail((ok_text == NULL) != (ok_cb == NULL));
+ g_warn_if_fail(cancel_text != NULL);
+ g_return_val_if_reached(NULL);
+ }
ops = purple_request_get_ui_ops();
+ if (purple_request_cpar_is_html(cpar) &&
+ !((ops->features & PURPLE_REQUEST_FEATURE_HTML)))
+ {
+ purple_request_fields_strip_html(fields);
+ }
+
+ purple_request_fields_check_sensitivity(fields);
+
if (ops != NULL && ops->request_fields != NULL) {
PurpleRequestInfo *info;
+ gchar **tmp;
+
+ tmp = purple_request_strip_html(cpar, &primary, &secondary);
info = g_new0(PurpleRequestInfo, 1);
info->type = PURPLE_REQUEST_FIELDS;
info->handle = handle;
info->ui_handle = ops->request_fields(title, primary, secondary,
- fields, ok_text, ok_cb,
- cancel_text, cancel_cb,
- account, who, conv,
- user_data);
+ fields, ok_text, ok_cb, cancel_text, cancel_cb,
+ cpar, user_data);
handles = g_list_append(handles, info);
+ g_strfreev(tmp);
+ purple_request_cpar_unref(cpar);
return info->ui_handle;
}
+ purple_request_cpar_unref(cpar);
return NULL;
}
void *
purple_request_file(void *handle, const char *title, const char *filename,
- gboolean savedialog,
- GCallback ok_cb, GCallback cancel_cb,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
- void *user_data)
+ gboolean savedialog, GCallback ok_cb, GCallback cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data)
{
PurpleRequestUiOps *ops;
@@ -1465,20 +2434,21 @@ purple_request_file(void *handle, const char *title, const char *filename,
info->type = PURPLE_REQUEST_FILE;
info->handle = handle;
info->ui_handle = ops->request_file(title, filename, savedialog,
- ok_cb, cancel_cb,
- account, who, conv, user_data);
+ ok_cb, cancel_cb, cpar, user_data);
handles = g_list_append(handles, info);
+
+ purple_request_cpar_unref(cpar);
return info->ui_handle;
}
+ purple_request_cpar_unref(cpar);
return NULL;
}
void *
purple_request_folder(void *handle, const char *title, const char *dirname,
- GCallback ok_cb, GCallback cancel_cb,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
- void *user_data)
+ GCallback ok_cb, GCallback cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data)
{
PurpleRequestUiOps *ops;
@@ -1490,21 +2460,85 @@ purple_request_folder(void *handle, const char *title, const char *dirname,
info = g_new0(PurpleRequestInfo, 1);
info->type = PURPLE_REQUEST_FOLDER;
info->handle = handle;
- info->ui_handle = ops->request_folder(title, dirname,
- ok_cb, cancel_cb,
- account, who, conv,
- user_data);
+ info->ui_handle = ops->request_folder(title, dirname, ok_cb,
+ cancel_cb, cpar, user_data);
handles = g_list_append(handles, info);
+
+ purple_request_cpar_unref(cpar);
return info->ui_handle;
}
+ purple_request_cpar_unref(cpar);
return NULL;
}
+void *
+purple_request_certificate(void *handle, const char *title,
+ const char *primary, const char *secondary,
+ PurpleCertificate *cert,
+ const char *ok_text, GCallback ok_cb,
+ const char *cancel_text, GCallback cancel_cb,
+ void *user_data)
+{
+ PurpleRequestFields *fields;
+ PurpleRequestFieldGroup *group;
+ PurpleRequestField *field;
+
+ fields = purple_request_fields_new();
+ group = purple_request_field_group_new(NULL);
+ purple_request_fields_add_group(fields, group);
+ field = purple_request_field_certificate_new("certificate", "Certificate", cert);
+ purple_request_field_group_add_field(group, field);
+
+ return purple_request_fields(handle, title, primary, secondary, fields,
+ ok_text, ok_cb, cancel_text, cancel_cb,
+ NULL, user_data);
+}
+
+gboolean
+purple_request_is_valid_ui_handle(void *ui_handle, PurpleRequestType *type)
+{
+ PurpleRequestInfo *info;
+
+ if (ui_handle == NULL)
+ return FALSE;
+
+ info = purple_request_info_from_ui_handle(ui_handle);
+
+ if (info == NULL)
+ return FALSE;
+
+ if (type != NULL)
+ *type = info->type;
+
+ return TRUE;
+}
+
+void
+purple_request_add_close_notify(void *ui_handle, GDestroyNotify notify,
+ gpointer notify_data)
+{
+ PurpleRequestInfo *info;
+ PurpleRequestCloseNotified *notified;
+
+ g_return_if_fail(ui_handle != NULL);
+ g_return_if_fail(notify != NULL);
+
+ info = purple_request_info_from_ui_handle(ui_handle);
+ g_return_if_fail(info != NULL);
+
+ notified = g_new0(PurpleRequestCloseNotified, 1);
+ notified->cb = notify;
+ notified->data = notify_data;
+
+ info->notify_on_close = g_slist_append(info->notify_on_close, notified);
+}
+
static void
purple_request_close_info(PurpleRequestInfo *info)
{
PurpleRequestUiOps *ops;
+ GSList *it;
ops = purple_request_get_ui_ops();
@@ -1514,6 +2548,13 @@ purple_request_close_info(PurpleRequestInfo *info)
if (ops != NULL && ops->close_request != NULL)
ops->close_request(info->type, info->ui_handle);
+ for (it = info->notify_on_close; it; it = g_slist_next(it)) {
+ PurpleRequestCloseNotified *notify = it->data;
+
+ notify->cb(notify->data);
+ }
+
+ g_slist_free_full(info->notify_on_close, g_free);
g_free(info);
}
@@ -1565,3 +2606,33 @@ purple_request_get_ui_ops(void)
{
return request_ui_ops;
}
+
+/**************************************************************************
+ * GBoxed code
+ **************************************************************************/
+static PurpleRequestUiOps *
+purple_request_ui_ops_copy(PurpleRequestUiOps *ops)
+{
+ PurpleRequestUiOps *ops_new;
+
+ g_return_val_if_fail(ops != NULL, NULL);
+
+ ops_new = g_new(PurpleRequestUiOps, 1);
+ *ops_new = *ops;
+
+ return ops_new;
+}
+
+GType
+purple_request_ui_ops_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleRequestUiOps",
+ (GBoxedCopyFunc)purple_request_ui_ops_copy,
+ (GBoxedFreeFunc)g_free);
+ }
+
+ return type;
+}
diff --git a/libpurple/request.h b/libpurple/request.h
index 8035ef806b..6bc83c2624 100644
--- a/libpurple/request.h
+++ b/libpurple/request.h
@@ -1,8 +1,3 @@
-/**
- * @file request.h Request API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -23,35 +18,87 @@
* 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_REQUEST_H_
#define _PURPLE_REQUEST_H_
+/**
+ * SECTION:request
+ * @section_id: libpurple-request
+ * @short_description: <filename>request.h</filename>
+ * @title: Request API
+ */
#include <stdlib.h>
#include <glib-object.h>
#include <glib.h>
-/** @copydoc _PurpleRequestField */
+#include "certificate.h"
+#include "conversation.h"
+#include "request-datasheet.h"
+
+#define PURPLE_TYPE_REQUEST_UI_OPS (purple_request_ui_ops_get_type())
+
+/**
+ * PurpleRequestField:
+ *
+ * A request field.
+ */
typedef struct _PurpleRequestField PurpleRequestField;
+/**
+ * PurpleRequestFields:
+ *
+ * Multiple fields request data.
+ */
+typedef struct _PurpleRequestFields PurpleRequestFields;
+
+/**
+ * PurpleRequestFieldGroup:
+ *
+ * A group of fields with a title.
+ */
+typedef struct _PurpleRequestFieldGroup PurpleRequestFieldGroup;
+
+/**
+ * PurpleRequestCommonParameters:
+ *
+ * Common parameters for UI operations.
+ */
+typedef struct _PurpleRequestCommonParameters PurpleRequestCommonParameters;
+
+typedef struct _PurpleRequestUiOps PurpleRequestUiOps;
+
#include "account.h"
#define PURPLE_DEFAULT_ACTION_NONE -1
/**
+ * PurpleRequestType:
+ * @PURPLE_REQUEST_INPUT: Text input request.
+ * @PURPLE_REQUEST_CHOICE: Multiple-choice request.
+ * @PURPLE_REQUEST_ACTION: Action request.
+ * @PURPLE_REQUEST_WAIT: Please wait dialog.
+ * @PURPLE_REQUEST_FIELDS: Multiple fields request.
+ * @PURPLE_REQUEST_FILE: File open or save request.
+ * @PURPLE_REQUEST_FOLDER: Folder selection request.
+ *
* Request types.
*/
typedef enum
{
- PURPLE_REQUEST_INPUT = 0, /**< Text input request. */
- PURPLE_REQUEST_CHOICE, /**< Multiple-choice request. */
- PURPLE_REQUEST_ACTION, /**< Action request. */
- PURPLE_REQUEST_FIELDS, /**< Multiple fields request. */
- PURPLE_REQUEST_FILE, /**< File open or save request. */
- PURPLE_REQUEST_FOLDER /**< Folder selection request. */
+ PURPLE_REQUEST_INPUT = 0,
+ PURPLE_REQUEST_CHOICE,
+ PURPLE_REQUEST_ACTION,
+ PURPLE_REQUEST_WAIT,
+ PURPLE_REQUEST_FIELDS,
+ PURPLE_REQUEST_FILE,
+ PURPLE_REQUEST_FOLDER
} PurpleRequestType;
/**
+ * PurpleRequestFieldType:
+ *
* A type of field.
*/
typedef enum
@@ -64,572 +111,1001 @@ typedef enum
PURPLE_REQUEST_FIELD_LIST,
PURPLE_REQUEST_FIELD_LABEL,
PURPLE_REQUEST_FIELD_IMAGE,
- PURPLE_REQUEST_FIELD_ACCOUNT
+ PURPLE_REQUEST_FIELD_ACCOUNT,
+ PURPLE_REQUEST_FIELD_CERTIFICATE,
+ PURPLE_REQUEST_FIELD_DATASHEET
} PurpleRequestFieldType;
-/**
- * Multiple fields request data.
+typedef enum
+{
+ PURPLE_REQUEST_FEATURE_HTML = 0x00000001
+} PurpleRequestFeature;
+
+typedef enum
+{
+ PURPLE_REQUEST_ICON_DEFAULT = 0,
+ PURPLE_REQUEST_ICON_REQUEST,
+ PURPLE_REQUEST_ICON_DIALOG,
+ PURPLE_REQUEST_ICON_WAIT,
+ PURPLE_REQUEST_ICON_INFO,
+ PURPLE_REQUEST_ICON_WARNING,
+ PURPLE_REQUEST_ICON_ERROR
+} PurpleRequestIconType;
+
+typedef void (*PurpleRequestCancelCb)(gpointer);
+
+/**
+ * PurpleRequestUiOps:
+ * @request_input: See purple_request_input().
+ * @request_choice: See purple_request_choice_varg().
+ * @request_action: See purple_request_action_varg().
+ * @request_wait: See purple_request_wait().
+ * @request_wait_update: See purple_request_wait_pulse(),
+ * purple_request_wait_progress().
+ * @request_fields: See purple_request_fields().
+ * @request_file: See purple_request_file().
+ * @request_folder: See purple_request_folder().
+ *
+ * Request UI operations.
*/
-typedef struct
+struct _PurpleRequestUiOps
{
- GList *groups;
+ PurpleRequestFeature features;
- GHashTable *fields;
+ void *(*request_input)(const char *title, const char *primary,
+ const char *secondary, const char *default_value,
+ gboolean multiline, gboolean masked, gchar *hint,
+ const char *ok_text, GCallback ok_cb,
+ const char *cancel_text, GCallback cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data);
- GList *required_fields;
+ void *(*request_choice)(const char *title, const char *primary,
+ const char *secondary, gpointer default_value,
+ const char *ok_text, GCallback ok_cb, const char *cancel_text,
+ GCallback cancel_cb, PurpleRequestCommonParameters *cpar,
+ void *user_data, va_list choices);
- void *ui_data;
+ void *(*request_action)(const char *title, const char *primary,
+ const char *secondary, int default_action,
+ PurpleRequestCommonParameters *cpar, void *user_data,
+ size_t action_count, va_list actions);
-} PurpleRequestFields;
+ void *(*request_wait)(const char *title, const char *primary,
+ const char *secondary, gboolean with_progress,
+ PurpleRequestCancelCb cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data);
-/**
- * A group of fields with a title.
- */
-typedef struct
-{
- PurpleRequestFields *fields_list;
+ void (*request_wait_update)(void *ui_handle, gboolean pulse,
+ gfloat fraction);
- char *title;
+ void *(*request_fields)(const char *title, const char *primary,
+ const char *secondary, PurpleRequestFields *fields,
+ const char *ok_text, GCallback ok_cb,
+ const char *cancel_text, GCallback cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data);
- GList *fields;
+ void *(*request_file)(const char *title, const char *filename,
+ gboolean savedialog, GCallback ok_cb, GCallback cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data);
-} PurpleRequestFieldGroup;
+ void *(*request_folder)(const char *title, const char *dirname,
+ GCallback ok_cb, GCallback cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data);
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_REQUEST_C_)
-/**
- * A request field.
- */
-struct _PurpleRequestField
-{
- PurpleRequestFieldType type;
- PurpleRequestFieldGroup *group;
+ void (*close_request)(PurpleRequestType type, void *ui_handle);
- char *id;
- char *label;
- char *type_hint;
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
- gboolean visible;
- gboolean required;
+typedef void (*PurpleRequestInputCb)(void *, const char *);
- union
- {
- struct
- {
- gboolean multiline;
- gboolean masked;
- gboolean editable;
- char *default_value;
- char *value;
+typedef gboolean (*PurpleRequestFieldValidator)(PurpleRequestField *field,
+ gchar **errmsg, gpointer user_data);
- } string;
+typedef gboolean (*PurpleRequestFieldSensitivityCb)(PurpleRequestField *field);
- struct
- {
- int default_value;
- int value;
+/**
+ * PurpleRequestActionCb:
+ *
+ * The type of callbacks passed to purple_request_action(). The first
+ * argument is the <literal>user_data</literal> parameter; the second is the
+ * index in the list of actions of the one chosen.
+ */
+typedef void (*PurpleRequestActionCb)(void *, int);
- } integer;
+typedef void (*PurpleRequestChoiceCb)(void *, gpointer);
+typedef void (*PurpleRequestFieldsCb)(void *, PurpleRequestFields *fields);
+typedef void (*PurpleRequestFileCb)(void *, const char *filename);
+typedef void (*PurpleRequestHelpCb)(gpointer);
- struct
- {
- gboolean default_value;
- gboolean value;
+G_BEGIN_DECLS
- } boolean;
+/**************************************************************************/
+/* Common parameters API */
+/**************************************************************************/
- struct
- {
- int default_value;
- int value;
+/**
+ * purple_request_cpar_new:
+ *
+ * Creates new parameters set for the request, which may or may not be used by
+ * the UI to display the request.
+ *
+ * Returns: The new parameters set.
+ */
+PurpleRequestCommonParameters *
+purple_request_cpar_new(void);
- GList *labels;
+/**
+ * purple_request_cpar_from_connection:
+ *
+ * Creates new parameters set initially bound with the #PurpleConnection.
+ *
+ * Returns: The new parameters set.
+ */
+PurpleRequestCommonParameters *
+purple_request_cpar_from_connection(PurpleConnection *gc);
- } choice;
+/**
+ * purple_request_cpar_from_account:
+ *
+ * Creates new parameters set initially bound with the #PurpleAccount.
+ *
+ * Returns: The new parameters set.
+ */
+PurpleRequestCommonParameters *
+purple_request_cpar_from_account(PurpleAccount *account);
- struct
- {
- GList *items;
- GList *icons;
- GHashTable *item_data;
- GList *selected;
- GHashTable *selected_table;
+/**
+ * purple_request_cpar_from_conversation:
+ *
+ * Creates new parameters set initially bound with the #PurpleConversation.
+ *
+ * Returns: The new parameters set.
+ */
+PurpleRequestCommonParameters *
+purple_request_cpar_from_conversation(PurpleConversation *conv);
- gboolean multiple_selection;
+/**
+ * purple_request_cpar_ref:
+ * @cpar: The object to ref.
+ *
+ * Increases the reference count on the parameters set.
+ */
+void
+purple_request_cpar_ref(PurpleRequestCommonParameters *cpar);
- } list;
+/**
+ * purple_request_cpar_unref:
+ * @cpar: The parameters set object to unref and possibly destroy.
+ *
+ * Decreases the reference count on the parameters set.
+ *
+ * The object will be destroyed when this reaches 0.
+ *
+ * Returns: The NULL, if object was destroyed, cpar otherwise.
+ */
+PurpleRequestCommonParameters *
+purple_request_cpar_unref(PurpleRequestCommonParameters *cpar);
- struct
- {
- PurpleAccount *default_account;
- PurpleAccount *account;
- gboolean show_all;
+/**
+ * purple_request_cpar_set_account:
+ * @cpar: The parameters set.
+ * @account: The #PurpleAccount to associate.
+ *
+ * Sets the #PurpleAccount associated with the request, or %NULL, if none is.
+ */
+void
+purple_request_cpar_set_account(PurpleRequestCommonParameters *cpar,
+ PurpleAccount *account);
- PurpleFilterAccountFunc filter_func;
+/**
+ * purple_request_cpar_get_account:
+ * @cpar: The parameters set (may be %NULL).
+ *
+ * Gets the #PurpleAccount associated with the request.
+ *
+ * Returns: The associated #PurpleAccount, or NULL if none is.
+ */
+PurpleAccount *
+purple_request_cpar_get_account(PurpleRequestCommonParameters *cpar);
- } account;
+/**
+ * purple_request_cpar_set_conversation:
+ * @cpar: The parameters set.
+ * @conv: The #PurpleConversation to associate.
+ *
+ * Sets the #PurpleConversation associated with the request, or %NULL, if
+ * none is.
+ */
+void
+purple_request_cpar_set_conversation(PurpleRequestCommonParameters *cpar,
+ PurpleConversation *conv);
- struct
- {
- unsigned int scale_x;
- unsigned int scale_y;
- const char *buffer;
- gsize size;
- } image;
+/**
+ * purple_request_cpar_get_conversation:
+ * @cpar: The parameters set (may be %NULL).
+ *
+ * Gets the #PurpleConversation associated with the request.
+ *
+ * Returns: The associated #PurpleConversation, or NULL if none is.
+ */
+PurpleConversation *
+purple_request_cpar_get_conversation(PurpleRequestCommonParameters *cpar);
- } u;
+/**
+ * purple_request_cpar_set_icon:
+ * @cpar: The parameters set.
+ * @icon_type: The icon type.
+ *
+ * Sets the icon associated with the request.
+ */
+void
+purple_request_cpar_set_icon(PurpleRequestCommonParameters *cpar,
+ PurpleRequestIconType icon_type);
- void *ui_data;
+/**
+ * purple_request_cpar_get_icon:
+ * @cpar: The parameters set.
+ *
+ * Gets the icon associated with the request.
+ *
+ * Returns: icon_type The icon type.
+ */
+PurpleRequestIconType
+purple_request_cpar_get_icon(PurpleRequestCommonParameters *cpar);
-};
-#endif
+/**
+ * purple_request_cpar_set_custom_icon:
+ * @cpar: The parameters set.
+ * @icon_data: The icon image contents (%NULL to reset).
+ * @icon_size: The icon image size.
+ *
+ * Sets the custom icon associated with the request.
+ */
+void
+purple_request_cpar_set_custom_icon(PurpleRequestCommonParameters *cpar,
+ gconstpointer icon_data, gsize icon_size);
/**
- * Request UI operations.
+ * purple_request_cpar_get_custom_icon:
+ * @cpar: The parameters set (may be %NULL).
+ * @icon_size: The pointer to variable, where icon size should be stored
+ * (may be %NULL).
+ *
+ * Gets the custom icon associated with the request.
+ *
+ * Returns: The icon image contents.
*/
-typedef struct
-{
- /** @see purple_request_input(). */
- void *(*request_input)(const char *title, const char *primary,
- const char *secondary, const char *default_value,
- gboolean multiline, gboolean masked, gchar *hint,
- const char *ok_text, GCallback ok_cb,
- const char *cancel_text, GCallback cancel_cb,
- PurpleAccount *account, const char *who,
- PurpleConversation *conv, void *user_data);
-
- /** @see purple_request_choice_varg(). */
- void *(*request_choice)(const char *title, const char *primary,
- const char *secondary, int default_value,
- const char *ok_text, GCallback ok_cb,
- const char *cancel_text, GCallback cancel_cb,
- PurpleAccount *account, const char *who,
- PurpleConversation *conv, void *user_data,
- va_list choices);
-
- /** @see purple_request_action_varg(). */
- void *(*request_action)(const char *title, const char *primary,
- const char *secondary, int default_action,
- PurpleAccount *account, const char *who,
- PurpleConversation *conv, void *user_data,
- size_t action_count, va_list actions);
+gconstpointer
+purple_request_cpar_get_custom_icon(PurpleRequestCommonParameters *cpar,
+ gsize *icon_size);
- /** @see purple_request_fields(). */
- void *(*request_fields)(const char *title, const char *primary,
- const char *secondary, PurpleRequestFields *fields,
- const char *ok_text, GCallback ok_cb,
- const char *cancel_text, GCallback cancel_cb,
- PurpleAccount *account, const char *who,
- PurpleConversation *conv, void *user_data);
+/**
+ * purple_request_cpar_set_html:
+ * @cpar: The parameters set.
+ * @enabled: 1, if the text passed with the request contains HTML,
+ * 0 otherwise. Don't use any other values, as they may be
+ * redefined in the future.
+ *
+ * Switches the request text to be HTML or not.
+ */
+void
+purple_request_cpar_set_html(PurpleRequestCommonParameters *cpar,
+ gboolean enabled);
- /** @see purple_request_file(). */
- void *(*request_file)(const char *title, const char *filename,
- gboolean savedialog, GCallback ok_cb,
- GCallback cancel_cb, PurpleAccount *account,
- const char *who, PurpleConversation *conv,
- void *user_data);
+/**
+ * purple_request_cpar_is_html:
+ * @cpar: The parameters set (may be %NULL).
+ *
+ * Checks, if the text passed to the request is HTML.
+ *
+ * Returns: %TRUE, if the text is HTML, %FALSE otherwise.
+ */
+gboolean
+purple_request_cpar_is_html(PurpleRequestCommonParameters *cpar);
- void (*close_request)(PurpleRequestType type, void *ui_handle);
+/**
+ * purple_request_cpar_set_compact:
+ * @cpar: The parameters set.
+ * @compact: TRUE for compact, FALSE otherwise.
+ *
+ * Sets dialog display mode to compact or default.
+ */
+void
+purple_request_cpar_set_compact(PurpleRequestCommonParameters *cpar,
+ gboolean compact);
- /** @see purple_request_folder(). */
- void *(*request_folder)(const char *title, const char *dirname,
- GCallback ok_cb, GCallback cancel_cb,
- PurpleAccount *account, const char *who,
- PurpleConversation *conv, void *user_data);
-
- /** @see purple_request_action_with_icon_varg(). */
- void *(*request_action_with_icon)(const char *title, const char *primary,
- const char *secondary, int default_action,
- PurpleAccount *account, const char *who,
- PurpleConversation *conv,
- gconstpointer icon_data, gsize icon_size,
- void *user_data,
- size_t action_count, va_list actions);
+/**
+ * purple_request_cpar_is_compact:
+ * @cpar: The parameters set (may be %NULL).
+ *
+ * Gets dialog display mode.
+ *
+ * Returns: TRUE for compact, FALSE for default.
+ */
+gboolean
+purple_request_cpar_is_compact(PurpleRequestCommonParameters *cpar);
- void (*_purple_reserved1)(void);
- void (*_purple_reserved2)(void);
- void (*_purple_reserved3)(void);
-} PurpleRequestUiOps;
+/**
+ * purple_request_cpar_set_help_cb:
+ * @cpar: The parameters set.
+ * @cb: The callback.
+ * @user_data: The data to be passed to the callback.
+ *
+ * Sets the callback for the Help button.
+ */
+void
+purple_request_cpar_set_help_cb(PurpleRequestCommonParameters *cpar,
+ PurpleRequestHelpCb cb, gpointer user_data);
-typedef void (*PurpleRequestInputCb)(void *, const char *);
+/**
+ * purple_request_cpar_get_help_cb:
+ * @cpar: The parameters set (may be %NULL).
+ * @user_data: The pointer to the variable, where user data (to be passed
+ * to callback function) should be stored.
+ *
+ * Gets the callback for the Help button.
+ *
+ * Returns: The callback.
+ */
+PurpleRequestHelpCb
+purple_request_cpar_get_help_cb(PurpleRequestCommonParameters *cpar,
+ gpointer *user_data);
-/** The type of callbacks passed to purple_request_action(). The first
- * argument is the @a user_data parameter; the second is the index in the list
- * of actions of the one chosen.
+/**
+ * purple_request_cpar_set_extra_actions:
+ * @cpar: The parameters set.
+ * @...: A list of actions. These are pairs of arguments. The first of
+ * each pair is the <type>char *</type> label that appears on the
+ * button. It should have an underscore before the letter you want
+ * to use as the accelerator key for the button. The second of each
+ * pair is the #PurpleRequestFieldsCb function to use when the
+ * button is clicked. Should be terminated with the NULL label.
+ *
+ * Sets extra actions for the PurpleRequestFields dialog.
*/
-typedef void (*PurpleRequestActionCb)(void *, int);
-typedef void (*PurpleRequestChoiceCb)(void *, int);
-typedef void (*PurpleRequestFieldsCb)(void *, PurpleRequestFields *fields);
-typedef void (*PurpleRequestFileCb)(void *, const char *filename);
+void
+purple_request_cpar_set_extra_actions(PurpleRequestCommonParameters *cpar, ...);
-#ifdef __cplusplus
-extern "C" {
-#endif
+/**
+ * purple_request_cpar_get_extra_actions:
+ * @cpar: The parameters set (may be %NULL).
+ *
+ * Gets extra actions for the PurpleRequestFields dialog.
+ *
+ * Returns: A list of actions (pairs of arguments, as in setter).
+ */
+GSList *
+purple_request_cpar_get_extra_actions(PurpleRequestCommonParameters *cpar);
+
+/**
+ * purple_request_cpar_set_parent_from:
+ * @cpar: The parameters set.
+ * @ui_handle: The UI handle.
+ *
+ * Sets the same parent window for this dialog, as the parent of specified
+ * Notify API or Request API dialog UI handle.
+ */
+void
+purple_request_cpar_set_parent_from(PurpleRequestCommonParameters *cpar,
+ gpointer ui_handle);
+
+/**
+ * purple_request_cpar_get_parent_from:
+ * @cpar: The parameters set (may be %NULL).
+ *
+ * Gets the parent "donor" for this dialog.
+ *
+ * Returns: The donors UI handle.
+ */
+gpointer
+purple_request_cpar_get_parent_from(PurpleRequestCommonParameters *cpar);
/**************************************************************************/
-/** @name Field List API */
+/* Field List API */
/**************************************************************************/
-/*@{*/
/**
+ * purple_request_fields_new:
+ *
* Creates a list of fields to pass to purple_request_fields().
*
- * @return A PurpleRequestFields structure.
+ * Returns: A PurpleRequestFields structure.
*/
PurpleRequestFields *purple_request_fields_new(void);
/**
- * Destroys a list of fields.
+ * purple_request_fields_destroy:
+ * @fields: The list of fields to destroy.
*
- * @param fields The list of fields to destroy.
+ * Destroys a list of fields.
*/
void purple_request_fields_destroy(PurpleRequestFields *fields);
/**
- * Adds a group of fields to the list.
+ * purple_request_fields_add_group:
+ * @fields: The fields list.
+ * @group: The group to add.
*
- * @param fields The fields list.
- * @param group The group to add.
+ * Adds a group of fields to the list.
*/
void purple_request_fields_add_group(PurpleRequestFields *fields,
PurpleRequestFieldGroup *group);
/**
- * Returns a list of all groups in a field list.
+ * purple_request_fields_get_groups:
+ * @fields: The fields list.
*
- * @param fields The fields list.
+ * Returns a list of all groups in a field list.
*
- * @constreturn A list of groups.
+ * Returns: (transfer none): A list of groups.
*/
GList *purple_request_fields_get_groups(const PurpleRequestFields *fields);
/**
- * Returns whether or not the field with the specified ID exists.
+ * purple_request_fields_set_tab_names:
+ * @fields: The fields list.
+ * @tab_names: NULL-terminated array of localized tab labels,
+ * may be %NULL.
+ *
+ * Set tab names for a field list.
+ */
+void purple_request_fields_set_tab_names(PurpleRequestFields *fields,
+ const gchar **tab_names);
+
+/**
+ * purple_request_fields_get_tab_names:
+ * @fields: The fields list.
+ *
+ * Returns tab names of a field list.
+ *
+ * Returns: NULL-terminated array of localized tab labels, or NULL if tabs
+ * are disabled.
+ */
+const gchar **
+purple_request_fields_get_tab_names(const PurpleRequestFields *fields);
+
+/**
+ * purple_request_fields_exists:
+ * @fields: The fields list.
+ * @id: The ID of the field.
*
- * @param fields The fields list.
- * @param id The ID of the field.
+ * Returns whether or not the field with the specified ID exists.
*
- * @return TRUE if the field exists, or FALSE.
+ * Returns: TRUE if the field exists, or FALSE.
*/
gboolean purple_request_fields_exists(const PurpleRequestFields *fields,
const char *id);
/**
+ * purple_request_fields_get_required:
+ * @fields: The fields list.
+ *
* Returns a list of all required fields.
*
- * @param fields The fields list.
+ * Returns: (transfer none): The list of required fields.
+ */
+const GList *purple_request_fields_get_required(
+ const PurpleRequestFields *fields);
+
+/**
+ * purple_request_fields_get_validatable:
+ * @fields: The fields list.
+ *
+ * Returns a list of all validated fields.
*
- * @constreturn The list of required fields.
+ * Returns: (transfer none): The list of validated fields.
*/
-GList *purple_request_fields_get_required(const PurpleRequestFields *fields);
+const GList *purple_request_fields_get_validatable(
+ const PurpleRequestFields *fields);
/**
- * Returns whether or not a field with the specified ID is required.
+ * purple_request_fields_get_autosensitive:
+ * @fields: The fields list.
*
- * @param fields The fields list.
- * @param id The field ID.
+ * Returns a list of all fields with sensitivity callback added.
*
- * @return TRUE if the specified field is required, or FALSE.
+ * Returns: (transfer none): The list of fields with automatic sensitivity
+ * callback.
+ */
+const GList *
+purple_request_fields_get_autosensitive(const PurpleRequestFields *fields);
+
+/**
+ * purple_request_fields_is_field_required:
+ * @fields: The fields list.
+ * @id: The field ID.
+ *
+ * Returns whether or not a field with the specified ID is required.
+ *
+ * Returns: TRUE if the specified field is required, or FALSE.
*/
gboolean purple_request_fields_is_field_required(const PurpleRequestFields *fields,
const char *id);
/**
- * Returns whether or not all required fields have values.
+ * purple_request_fields_all_required_filled:
+ * @fields: The fields list.
*
- * @param fields The fields list.
+ * Returns whether or not all required fields have values.
*
- * @return TRUE if all required fields have values, or FALSE.
+ * Returns: TRUE if all required fields have values, or FALSE.
*/
gboolean purple_request_fields_all_required_filled(
const PurpleRequestFields *fields);
/**
- * Return the field with the specified ID.
+ * purple_request_fields_all_valid:
+ * @fields: The fields list.
*
- * @param fields The fields list.
- * @param id The ID of the field.
+ * Returns whether or not all fields are valid.
*
- * @return The field, if found.
+ * Returns: TRUE if all fields are valid, or FALSE.
+ */
+gboolean purple_request_fields_all_valid(const PurpleRequestFields *fields);
+
+/**
+ * purple_request_fields_get_field:
+ * @fields: The fields list.
+ * @id: The ID of the field.
+ *
+ * Return the field with the specified ID.
+ *
+ * Returns: The field, if found.
*/
PurpleRequestField *purple_request_fields_get_field(
const PurpleRequestFields *fields, const char *id);
/**
- * Returns the string value of a field with the specified ID.
+ * purple_request_fields_get_string:
+ * @fields: The fields list.
+ * @id: The ID of the field.
*
- * @param fields The fields list.
- * @param id The ID of the field.
+ * Returns the string value of a field with the specified ID.
*
- * @return The string value, if found, or @c NULL otherwise.
+ * Returns: The string value, if found, or %NULL otherwise.
*/
const char *purple_request_fields_get_string(const PurpleRequestFields *fields,
const char *id);
/**
- * Returns the integer value of a field with the specified ID.
+ * purple_request_fields_get_integer:
+ * @fields: The fields list.
+ * @id: The ID of the field.
*
- * @param fields The fields list.
- * @param id The ID of the field.
+ * Returns the integer value of a field with the specified ID.
*
- * @return The integer value, if found, or 0 otherwise.
+ * Returns: The integer value, if found, or 0 otherwise.
*/
int purple_request_fields_get_integer(const PurpleRequestFields *fields,
const char *id);
/**
- * Returns the boolean value of a field with the specified ID.
+ * purple_request_fields_get_bool:
+ * @fields: The fields list.
+ * @id: The ID of the field.
*
- * @param fields The fields list.
- * @param id The ID of the field.
+ * Returns the boolean value of a field with the specified ID.
*
- * @return The boolean value, if found, or @c FALSE otherwise.
+ * Returns: The boolean value, if found, or %FALSE otherwise.
*/
gboolean purple_request_fields_get_bool(const PurpleRequestFields *fields,
const char *id);
/**
- * Returns the choice index of a field with the specified ID.
+ * purple_request_fields_get_choice:
+ * @fields: The fields list.
+ * @id: The ID of the field.
*
- * @param fields The fields list.
- * @param id The ID of the field.
+ * Returns the choice index of a field with the specified ID.
*
- * @return The choice index, if found, or -1 otherwise.
+ * Returns: The choice value, if found, or NULL otherwise.
*/
-int purple_request_fields_get_choice(const PurpleRequestFields *fields,
- const char *id);
+gpointer
+purple_request_fields_get_choice(const PurpleRequestFields *fields,
+ const char *id);
/**
- * Returns the account of a field with the specified ID.
+ * purple_request_fields_get_account:
+ * @fields: The fields list.
+ * @id: The ID of the field.
*
- * @param fields The fields list.
- * @param id The ID of the field.
+ * Returns the account of a field with the specified ID.
*
- * @return The account value, if found, or NULL otherwise.
+ * Returns: The account value, if found, or NULL otherwise.
*/
PurpleAccount *purple_request_fields_get_account(const PurpleRequestFields *fields,
const char *id);
-/*@}*/
+/**
+ * purple_request_fields_get_ui_data:
+ * @fields: The fields list.
+ *
+ * Returns the UI data associated with this object.
+ *
+ * Returns: The UI data associated with this object. This is a
+ * convenience field provided to the UIs--it is not
+ * used by the libpurple core.
+ */
+gpointer purple_request_fields_get_ui_data(const PurpleRequestFields *fields);
+
+/**
+ * purple_request_fields_set_ui_data:
+ * @fields: The fields list.
+ * @ui_data: A pointer to associate with this object.
+ *
+ * Set the UI data associated with this object.
+ */
+void purple_request_fields_set_ui_data(PurpleRequestFields *fields, gpointer ui_data);
/**************************************************************************/
-/** @name Fields Group API */
+/* Fields Group API */
/**************************************************************************/
-/*@{*/
/**
- * Creates a fields group with an optional title.
+ * purple_request_field_group_new:
+ * @title: The optional title to give the group.
*
- * @param title The optional title to give the group.
+ * Creates a fields group with an optional title.
*
- * @return A new fields group
+ * Returns: A new fields group
*/
PurpleRequestFieldGroup *purple_request_field_group_new(const char *title);
/**
- * Destroys a fields group.
+ * purple_request_field_group_set_tab:
+ * @group: The group.
+ * @tab_no: The tab number.
*
- * @param group The group to destroy.
+ * Sets tab number for a group.
+ *
+ * See purple_request_fields_set_tab_names().
+ */
+void purple_request_field_group_set_tab(PurpleRequestFieldGroup *group,
+ guint tab_no);
+
+/**
+ * purple_request_field_group_get_tab:
+ * @group: The group.
+ *
+ * Returns tab number of a group.
+ *
+ * See purple_request_fields_get_tab_names().
+ *
+ * Returns: Tab number.
+ */
+guint purple_request_field_group_get_tab(const PurpleRequestFieldGroup *group);
+
+/**
+ * purple_request_field_group_destroy:
+ * @group: The group to destroy.
+ *
+ * Destroys a fields group.
*/
void purple_request_field_group_destroy(PurpleRequestFieldGroup *group);
/**
- * Adds a field to the group.
+ * purple_request_field_group_add_field:
+ * @group: The group to add the field to.
+ * @field: The field to add to the group.
*
- * @param group The group to add the field to.
- * @param field The field to add to the group.
+ * Adds a field to the group.
*/
void purple_request_field_group_add_field(PurpleRequestFieldGroup *group,
PurpleRequestField *field);
/**
- * Returns the title of a fields group.
+ * purple_request_field_group_get_title:
+ * @group: The group.
*
- * @param group The group.
+ * Returns the title of a fields group.
*
- * @return The title, if set.
+ * Returns: The title, if set.
*/
const char *purple_request_field_group_get_title(
const PurpleRequestFieldGroup *group);
/**
- * Returns a list of all fields in a group.
+ * purple_request_field_group_get_fields:
+ * @group: The group.
*
- * @param group The group.
+ * Returns a list of all fields in a group.
*
- * @constreturn The list of fields in the group.
+ * Returns: (transfer none): The list of fields in the group.
*/
GList *purple_request_field_group_get_fields(
const PurpleRequestFieldGroup *group);
-/*@}*/
+/**
+ * purple_request_field_group_get_fields_list:
+ * @group: The group.
+ *
+ * Returns a list of all fields in a group.
+ *
+ * Returns: (transfer none): The list of fields in the group.
+ */
+PurpleRequestFields *purple_request_field_group_get_fields_list(
+ const PurpleRequestFieldGroup *group);
/**************************************************************************/
-/** @name Field API */
+/* Field API */
/**************************************************************************/
-/*@{*/
/**
- * Creates a field of the specified type.
+ * purple_request_field_new:
+ * @id: The field ID.
+ * @text: The text label of the field.
+ * @type: The type of field.
*
- * @param id The field ID.
- * @param text The text label of the field.
- * @param type The type of field.
+ * Creates a field of the specified type.
*
- * @return The new field.
+ * Returns: The new field.
*/
PurpleRequestField *purple_request_field_new(const char *id, const char *text,
PurpleRequestFieldType type);
/**
- * Destroys a field.
+ * purple_request_field_destroy:
+ * @field: The field to destroy.
*
- * @param field The field to destroy.
+ * Destroys a field.
*/
void purple_request_field_destroy(PurpleRequestField *field);
/**
- * Sets the label text of a field.
+ * purple_request_field_set_label:
+ * @field: The field.
+ * @label: The text label.
*
- * @param field The field.
- * @param label The text label.
+ * Sets the label text of a field.
*/
void purple_request_field_set_label(PurpleRequestField *field, const char *label);
/**
- * Sets whether or not a field is visible.
+ * purple_request_field_set_visible:
+ * @field: The field.
+ * @visible: TRUE if visible, or FALSE if not.
*
- * @param field The field.
- * @param visible TRUE if visible, or FALSE if not.
+ * Sets whether or not a field is visible.
*/
void purple_request_field_set_visible(PurpleRequestField *field, gboolean visible);
/**
+ * purple_request_field_set_type_hint:
+ * @field: The field.
+ * @type_hint: The type hint.
+ *
* Sets the type hint for the field.
*
* This is optionally used by the UIs to provide such features as
* auto-completion for type hints like "account" and "screenname".
- *
- * @param field The field.
- * @param type_hint The type hint.
*/
void purple_request_field_set_type_hint(PurpleRequestField *field,
const char *type_hint);
/**
- * Sets whether or not a field is required.
+ * purple_request_field_set_tooltip:
+ * @field: The field.
+ * @tooltip: The tooltip text.
+ *
+ * Sets the tooltip for the field.
*
- * @param field The field.
- * @param required TRUE if required, or FALSE.
+ * This is optionally used by the UIs to provide a tooltip for
+ * the field.
+ */
+void purple_request_field_set_tooltip(PurpleRequestField *field,
+ const char *tooltip);
+
+/**
+ * purple_request_field_set_required:
+ * @field: The field.
+ * @required: TRUE if required, or FALSE.
+ *
+ * Sets whether or not a field is required.
*/
void purple_request_field_set_required(PurpleRequestField *field,
gboolean required);
/**
- * Returns the type of a field.
+ * purple_request_field_get_field_type:
+ * @field: The field.
*
- * @param field The field.
+ * Returns the type of a field.
*
- * @return The field's type.
+ * Returns: The field's type.
*/
-PurpleRequestFieldType purple_request_field_get_type(const PurpleRequestField *field);
+PurpleRequestFieldType purple_request_field_get_field_type(const PurpleRequestField *field);
/**
- * Returns the group for the field.
- *
- * @param field The field.
+ * purple_request_field_get_group:
+ * @field: The field.
*
- * @return The UI data.
+ * Returns the group for the field.
*
- * @since 2.6.0
+ * Returns: The UI data.
*/
PurpleRequestFieldGroup *purple_request_field_get_group(const PurpleRequestField *field);
/**
- * Returns the ID of a field.
+ * purple_request_field_get_id:
+ * @field: The field.
*
- * @param field The field.
+ * Returns the ID of a field.
*
- * @return The ID
+ * Returns: The ID
*/
const char *purple_request_field_get_id(const PurpleRequestField *field);
/**
- * Returns the label text of a field.
+ * purple_request_field_get_label:
+ * @field: The field.
*
- * @param field The field.
+ * Returns the label text of a field.
*
- * @return The label text.
+ * Returns: The label text.
*/
const char *purple_request_field_get_label(const PurpleRequestField *field);
/**
- * Returns whether or not a field is visible.
+ * purple_request_field_is_visible:
+ * @field: The field.
*
- * @param field The field.
+ * Returns whether or not a field is visible.
*
- * @return TRUE if the field is visible. FALSE otherwise.
+ * Returns: TRUE if the field is visible. FALSE otherwise.
*/
gboolean purple_request_field_is_visible(const PurpleRequestField *field);
/**
+ * purple_request_field_get_type_hint:
+ * @field: The field.
+ *
* Returns the field's type hint.
*
- * @param field The field.
+ * Returns: The field's type hint.
+ */
+const char *purple_request_field_get_field_type_hint(const PurpleRequestField *field);
+
+/**
+ * purple_request_field_get_tooltip:
+ * @field: The field.
+ *
+ * Returns the field's tooltip.
*
- * @return The field's type hint.
+ * Returns: The field's tooltip.
*/
-const char *purple_request_field_get_type_hint(const PurpleRequestField *field);
+const char *purple_request_field_get_tooltip(const PurpleRequestField *field);
/**
- * Returns whether or not a field is required.
+ * purple_request_field_is_required:
+ * @field: The field.
*
- * @param field The field.
+ * Returns whether or not a field is required.
*
- * @return TRUE if the field is required, or FALSE.
+ * Returns: TRUE if the field is required, or FALSE.
*/
gboolean purple_request_field_is_required(const PurpleRequestField *field);
/**
- * Returns the ui_data for a field.
+ * purple_request_field_is_filled:
+ * @field: The field.
*
- * @param field The field.
+ * Checks, if specified field has value.
*
- * @return The UI data.
+ * Returns: TRUE if the field has value, or FALSE.
+ */
+gboolean purple_request_field_is_filled(const PurpleRequestField *field);
+
+/**
+ * purple_request_field_set_validator:
+ * @field: The field.
+ * @validator: The validator callback, NULL to disable validation.
+ * @user_data: The data to pass to the callback.
*
- * @since 2.6.0
+ * Sets validator for a single field.
*/
-gpointer purple_request_field_get_ui_data(const PurpleRequestField *field);
+void purple_request_field_set_validator(PurpleRequestField *field,
+ PurpleRequestFieldValidator validator, void *user_data);
/**
- * Sets the ui_data for a field.
+ * purple_request_field_is_validatable:
+ * @field: The field.
+ *
+ * Returns whether or not field has validator set.
+ *
+ * Returns: TRUE if the field has validator, or FALSE.
+ */
+gboolean purple_request_field_is_validatable(PurpleRequestField *field);
+
+/**
+ * purple_request_field_is_valid:
+ * @field: The field.
+ * @errmsg: If non-NULL, the memory area, where the pointer to validation
+ * failure message will be set.
+ *
+ * Checks, if specified field is valid.
*
- * @param field The field.
- * @param ui_data The UI data.
+ * If detailed message about failure reason is needed, there is an option to
+ * return (via errmsg argument) pointer to newly allocated error message.
+ * It must be freed with g_free after use.
*
- * @return The UI data.
+ * Note: empty, not required fields are valid.
*
- * @since 2.6.0
+ * Returns: TRUE, if the field is valid, FALSE otherwise.
+ */
+gboolean purple_request_field_is_valid(PurpleRequestField *field, gchar **errmsg);
+
+/**
+ * purple_request_field_set_sensitive:
+ * @field: The field.
+ * @sensitive: TRUE if the field should be sensitive for user input.
+ *
+ * Sets field editable.
+ */
+void purple_request_field_set_sensitive(PurpleRequestField *field,
+ gboolean sensitive);
+
+/**
+ * purple_request_field_is_sensitive:
+ * @field: The field.
+ *
+ * Checks, if field is editable.
+ *
+ * Returns: TRUE, if the field is sensitive for user input.
+ */
+gboolean purple_request_field_is_sensitive(PurpleRequestField *field);
+
+/**
+ * purple_request_field_set_sensitivity_cb:
+ * @field: The field.
+ * @cb: The callback.
+ *
+ * Sets the callback, used to determine if the field should be editable.
+ */
+void purple_request_field_set_sensitivity_cb(PurpleRequestField *field,
+ PurpleRequestFieldSensitivityCb cb);
+
+/**
+ * purple_request_field_get_ui_data:
+ * @field: The field.
+ *
+ * Returns the ui_data for a field.
+ *
+ * Returns: The UI data.
+ */
+gpointer purple_request_field_get_ui_data(const PurpleRequestField *field);
+
+/**
+ * purple_request_field_set_ui_data:
+ * @field: The field.
+ * @ui_data: The UI data.
+ *
+ * Sets the ui_data for a field.
+ *
+ * Returns: The UI data.
*/
void purple_request_field_set_ui_data(PurpleRequestField *field,
gpointer ui_data);
-/*@}*/
-
/**************************************************************************/
-/** @name String Field API */
+/* String Field API */
/**************************************************************************/
-/*@{*/
/**
- * Creates a string request field.
+ * purple_request_field_string_new:
+ * @id: The field ID.
+ * @text: The text label of the field.
+ * @default_value: The optional default value.
+ * @multiline: Whether or not this should be a multiline string.
*
- * @param id The field ID.
- * @param text The text label of the field.
- * @param default_value The optional default value.
- * @param multiline Whether or not this should be a multiline string.
+ * Creates a string request field.
*
- * @return The new field.
+ * Returns: The new field.
*/
PurpleRequestField *purple_request_field_string_new(const char *id,
const char *text,
@@ -637,972 +1113,1198 @@ PurpleRequestField *purple_request_field_string_new(const char *id,
gboolean multiline);
/**
- * Sets the default value in a string field.
+ * purple_request_field_string_set_default_value:
+ * @field: The field.
+ * @default_value: The default value.
*
- * @param field The field.
- * @param default_value The default value.
+ * Sets the default value in a string field.
*/
void purple_request_field_string_set_default_value(PurpleRequestField *field,
const char *default_value);
/**
- * Sets the value in a string field.
+ * purple_request_field_string_set_value:
+ * @field: The field.
+ * @value: The value.
*
- * @param field The field.
- * @param value The value.
+ * Sets the value in a string field.
*/
void purple_request_field_string_set_value(PurpleRequestField *field,
const char *value);
/**
+ * purple_request_field_string_set_masked:
+ * @field: The field.
+ * @masked: The masked value.
+ *
* Sets whether or not a string field is masked
* (commonly used for password fields).
- *
- * @param field The field.
- * @param masked The masked value.
*/
void purple_request_field_string_set_masked(PurpleRequestField *field,
gboolean masked);
/**
- * Sets whether or not a string field is editable.
+ * purple_request_field_string_get_default_value:
+ * @field: The field.
*
- * @param field The field.
- * @param editable The editable value.
- */
-void purple_request_field_string_set_editable(PurpleRequestField *field,
- gboolean editable);
-
-/**
* Returns the default value in a string field.
*
- * @param field The field.
- *
- * @return The default value.
+ * Returns: The default value.
*/
const char *purple_request_field_string_get_default_value(
const PurpleRequestField *field);
/**
- * Returns the user-entered value in a string field.
+ * purple_request_field_string_get_value:
+ * @field: The field.
*
- * @param field The field.
+ * Returns the user-entered value in a string field.
*
- * @return The value.
+ * Returns: The value.
*/
const char *purple_request_field_string_get_value(const PurpleRequestField *field);
/**
- * Returns whether or not a string field is multi-line.
+ * purple_request_field_string_is_multiline:
+ * @field: The field.
*
- * @param field The field.
+ * Returns whether or not a string field is multi-line.
*
- * @return @c TRUE if the field is mulit-line, or @c FALSE otherwise.
+ * Returns: %TRUE if the field is mulit-line, or %FALSE otherwise.
*/
gboolean purple_request_field_string_is_multiline(const PurpleRequestField *field);
/**
- * Returns whether or not a string field is masked.
+ * purple_request_field_string_is_masked:
+ * @field: The field.
*
- * @param field The field.
+ * Returns whether or not a string field is masked.
*
- * @return @c TRUE if the field is masked, or @c FALSE otherwise.
+ * Returns: %TRUE if the field is masked, or %FALSE otherwise.
*/
gboolean purple_request_field_string_is_masked(const PurpleRequestField *field);
-/**
- * Returns whether or not a string field is editable.
- *
- * @param field The field.
- *
- * @return @c TRUE if the field is editable, or @c FALSE otherwise.
- */
-gboolean purple_request_field_string_is_editable(const PurpleRequestField *field);
-
-/*@}*/
-
/**************************************************************************/
-/** @name Integer Field API */
+/* Integer Field API */
/**************************************************************************/
-/*@{*/
/**
- * Creates an integer field.
+ * purple_request_field_int_new:
+ * @id: The field ID.
+ * @text: The text label of the field.
+ * @default_value: The default value.
+ * @lower_bound: The lower bound.
+ * @upper_bound: The upper bound.
*
- * @param id The field ID.
- * @param text The text label of the field.
- * @param default_value The default value.
+ * Creates an integer field.
*
- * @return The new field.
+ * Returns: The new field.
*/
PurpleRequestField *purple_request_field_int_new(const char *id,
- const char *text,
- int default_value);
+ const char *text, int default_value, int lower_bound, int upper_bound);
/**
- * Sets the default value in an integer field.
+ * purple_request_field_int_set_default_value:
+ * @field: The field.
+ * @default_value: The default value.
*
- * @param field The field.
- * @param default_value The default value.
+ * Sets the default value in an integer field.
*/
void purple_request_field_int_set_default_value(PurpleRequestField *field,
int default_value);
/**
- * Sets the value in an integer field.
+ * purple_request_field_int_set_lower_bound:
+ * @field: The field.
+ * @lower_bound: The lower bound.
+ *
+ * Sets the lower bound in an integer field.
+ */
+void purple_request_field_int_set_lower_bound(PurpleRequestField *field, int lower_bound);
+
+/**
+ * purple_request_field_int_set_upper_bound:
+ * @field: The field.
+ * @upper_bound: The upper bound.
*
- * @param field The field.
- * @param value The value.
+ * Sets the upper bound in an integer field.
+ */
+void purple_request_field_int_set_upper_bound(PurpleRequestField *field, int upper_bound);
+
+/**
+ * purple_request_field_int_set_value:
+ * @field: The field.
+ * @value: The value.
+ *
+ * Sets the value in an integer field.
*/
void purple_request_field_int_set_value(PurpleRequestField *field, int value);
/**
- * Returns the default value in an integer field.
+ * purple_request_field_int_get_default_value:
+ * @field: The field.
*
- * @param field The field.
+ * Returns the default value in an integer field.
*
- * @return The default value.
+ * Returns: The default value.
*/
int purple_request_field_int_get_default_value(const PurpleRequestField *field);
/**
- * Returns the user-entered value in an integer field.
+ * purple_request_field_int_get_lower_bound:
+ * @field: The field.
*
- * @param field The field.
+ * Returns the lower bound in an integer field.
*
- * @return The value.
+ * Returns: The lower bound.
*/
-int purple_request_field_int_get_value(const PurpleRequestField *field);
+int purple_request_field_int_get_lower_bound(const PurpleRequestField *field);
-/*@}*/
+/**
+ * purple_request_field_int_get_upper_bound:
+ * @field: The field.
+ *
+ * Returns the upper bound in an integer field.
+ *
+ * Returns: The upper bound.
+ */
+int purple_request_field_int_get_upper_bound(const PurpleRequestField *field);
+
+/**
+ * purple_request_field_int_get_value:
+ * @field: The field.
+ *
+ * Returns the user-entered value in an integer field.
+ *
+ * Returns: The value.
+ */
+int purple_request_field_int_get_value(const PurpleRequestField *field);
/**************************************************************************/
-/** @name Boolean Field API */
+/* Boolean Field API */
/**************************************************************************/
-/*@{*/
/**
+ * purple_request_field_bool_new:
+ * @id: The field ID.
+ * @text: The text label of the field.
+ * @default_value: The default value.
+ *
* Creates a boolean field.
*
* This is often represented as a checkbox.
*
- * @param id The field ID.
- * @param text The text label of the field.
- * @param default_value The default value.
- *
- * @return The new field.
+ * Returns: The new field.
*/
PurpleRequestField *purple_request_field_bool_new(const char *id,
const char *text,
gboolean default_value);
/**
- * Sets the default value in an boolean field.
+ * purple_request_field_bool_set_default_value:
+ * @field: The field.
+ * @default_value: The default value.
*
- * @param field The field.
- * @param default_value The default value.
+ * Sets the default value in an boolean field.
*/
void purple_request_field_bool_set_default_value(PurpleRequestField *field,
gboolean default_value);
/**
- * Sets the value in an boolean field.
+ * purple_request_field_bool_set_value:
+ * @field: The field.
+ * @value: The value.
*
- * @param field The field.
- * @param value The value.
+ * Sets the value in an boolean field.
*/
void purple_request_field_bool_set_value(PurpleRequestField *field,
gboolean value);
/**
- * Returns the default value in an boolean field.
+ * purple_request_field_bool_get_default_value:
+ * @field: The field.
*
- * @param field The field.
+ * Returns the default value in an boolean field.
*
- * @return The default value.
+ * Returns: The default value.
*/
gboolean purple_request_field_bool_get_default_value(
const PurpleRequestField *field);
/**
- * Returns the user-entered value in an boolean field.
+ * purple_request_field_bool_get_value:
+ * @field: The field.
*
- * @param field The field.
+ * Returns the user-entered value in an boolean field.
*
- * @return The value.
+ * Returns: The value.
*/
gboolean purple_request_field_bool_get_value(const PurpleRequestField *field);
-/*@}*/
-
/**************************************************************************/
-/** @name Choice Field API */
+/* Choice Field API */
/**************************************************************************/
-/*@{*/
/**
+ * purple_request_field_choice_new:
+ * @id: The field ID.
+ * @text: The optional label of the field.
+ * @default_value: The default choice.
+ *
* Creates a multiple choice field.
*
* This is often represented as a group of radio buttons.
*
- * @param id The field ID.
- * @param text The optional label of the field.
- * @param default_value The default choice.
- *
- * @return The new field.
+ * Returns: The new field.
*/
-PurpleRequestField *purple_request_field_choice_new(const char *id,
- const char *text,
- int default_value);
+PurpleRequestField *
+purple_request_field_choice_new(const char *id, const char *text,
+ gpointer default_value);
/**
- * Adds a choice to a multiple choice field.
+ * purple_request_field_choice_add:
+ * @field: The choice field.
+ * @label: The choice label.
+ * @data: The choice value.
*
- * @param field The choice field.
- * @param label The choice label.
+ * Adds a choice to a multiple choice field.
*/
-void purple_request_field_choice_add(PurpleRequestField *field,
- const char *label);
+void
+purple_request_field_choice_add(PurpleRequestField *field, const char *label,
+ gpointer data);
/**
- * Sets the default value in an choice field.
+ * purple_request_field_choice_set_default_value:
+ * @field: The field.
+ * @default_value: The default value.
*
- * @param field The field.
- * @param default_value The default value.
+ * Sets the default value in an choice field.
*/
-void purple_request_field_choice_set_default_value(PurpleRequestField *field,
- int default_value);
+void
+purple_request_field_choice_set_default_value(PurpleRequestField *field,
+ gpointer default_value);
/**
- * Sets the value in an choice field.
+ * purple_request_field_choice_set_value:
+ * @field: The field.
+ * @value: The value.
*
- * @param field The field.
- * @param value The value.
+ * Sets the value in an choice field.
*/
-void purple_request_field_choice_set_value(PurpleRequestField *field, int value);
+void
+purple_request_field_choice_set_value(PurpleRequestField *field,
+ gpointer value);
/**
- * Returns the default value in an choice field.
+ * purple_request_field_choice_get_default_value:
+ * @field: The field.
*
- * @param field The field.
+ * Returns the default value in an choice field.
*
- * @return The default value.
+ * Returns: The default value.
*/
-int purple_request_field_choice_get_default_value(const PurpleRequestField *field);
+gpointer
+purple_request_field_choice_get_default_value(const PurpleRequestField *field);
/**
- * Returns the user-entered value in an choice field.
+ * purple_request_field_choice_get_value:
+ * @field: The field.
*
- * @param field The field.
+ * Returns the user-entered value in an choice field.
*
- * @return The value.
+ * Returns: The value.
*/
-int purple_request_field_choice_get_value(const PurpleRequestField *field);
+gpointer
+purple_request_field_choice_get_value(const PurpleRequestField *field);
/**
- * Returns a list of labels in a choice field.
+ * purple_request_field_choice_get_elements:
+ * @field: The field.
*
- * @param field The field.
+ * Returns a list of elements in a choice field.
*
- * @constreturn The list of labels.
+ * Returns: (transfer none): The list of pairs of {label, value}.
*/
-GList *purple_request_field_choice_get_labels(const PurpleRequestField *field);
+GList *
+purple_request_field_choice_get_elements(const PurpleRequestField *field);
-/*@}*/
+/**
+ * purple_request_field_choice_set_data_destructor:
+ * @field: The field.
+ * @destroy: The destroy function.
+ *
+ * Sets the destructor for field values.
+ */
+void
+purple_request_field_choice_set_data_destructor(PurpleRequestField *field,
+ GDestroyNotify destroy);
/**************************************************************************/
-/** @name List Field API */
+/* List Field API */
/**************************************************************************/
-/*@{*/
/**
- * Creates a multiple list item field.
+ * purple_request_field_list_new:
+ * @id: The field ID.
+ * @text: The optional label of the field.
*
- * @param id The field ID.
- * @param text The optional label of the field.
+ * Creates a multiple list item field.
*
- * @return The new field.
+ * Returns: The new field.
*/
PurpleRequestField *purple_request_field_list_new(const char *id, const char *text);
/**
- * Sets whether or not a list field allows multiple selection.
- *
- * @param field The list field.
- * @param multi_select TRUE if multiple selection is enabled,
+ * purple_request_field_list_set_multi_select:
+ * @field: The list field.
+ * @multi_select: TRUE if multiple selection is enabled,
* or FALSE otherwise.
+ *
+ * Sets whether or not a list field allows multiple selection.
*/
void purple_request_field_list_set_multi_select(PurpleRequestField *field,
gboolean multi_select);
/**
- * Returns whether or not a list field allows multiple selection.
+ * purple_request_field_list_get_multi_select:
+ * @field: The list field.
*
- * @param field The list field.
+ * Returns whether or not a list field allows multiple selection.
*
- * @return TRUE if multiple selection is enabled, or FALSE otherwise.
+ * Returns: TRUE if multiple selection is enabled, or FALSE otherwise.
*/
gboolean purple_request_field_list_get_multi_select(
const PurpleRequestField *field);
/**
- * Returns the data for a particular item.
+ * purple_request_field_list_get_data:
+ * @field: The list field.
+ * @text: The item text.
*
- * @param field The list field.
- * @param text The item text.
+ * Returns the data for a particular item.
*
- * @return The data associated with the item.
+ * Returns: The data associated with the item.
*/
void *purple_request_field_list_get_data(const PurpleRequestField *field,
const char *text);
/**
- * Adds an item to a list field.
- *
- * @param field The list field.
- * @param item The list item.
- * @param data The associated data.
+ * purple_request_field_list_add_icon:
+ * @field: The list field.
+ * @item: The list item.
+ * @icon_path: The path to icon file, or %NULL for no icon.
+ * @data: The associated data.
*
- * @deprecated Use purple_request_field_list_add_icon() instead.
- */
-void purple_request_field_list_add(PurpleRequestField *field,
- const char *item, void *data);
-
-/**
* Adds an item to a list field.
- *
- * @param field The list field.
- * @param item The list item.
- * @param icon_path The path to icon file, or @c NULL for no icon.
- * @param data The associated data.
*/
void purple_request_field_list_add_icon(PurpleRequestField *field,
const char *item, const char* icon_path, void* data);
/**
- * Adds a selected item to the list field.
+ * purple_request_field_list_add_selected:
+ * @field: The field.
+ * @item: The item to add.
*
- * @param field The field.
- * @param item The item to add.
+ * Adds a selected item to the list field.
*/
void purple_request_field_list_add_selected(PurpleRequestField *field,
const char *item);
/**
- * Clears the list of selected items in a list field.
+ * purple_request_field_list_clear_selected:
+ * @field: The field.
*
- * @param field The field.
+ * Clears the list of selected items in a list field.
*/
void purple_request_field_list_clear_selected(PurpleRequestField *field);
/**
- * Sets a list of selected items in a list field.
+ * purple_request_field_list_set_selected:
+ * @field: The field.
+ * @items: The list of selected items, which is not modified or freed.
*
- * @param field The field.
- * @param items The list of selected items, which is not modified or freed.
+ * Sets a list of selected items in a list field.
*/
void purple_request_field_list_set_selected(PurpleRequestField *field,
GList *items);
/**
- * Returns whether or not a particular item is selected in a list field.
+ * purple_request_field_list_is_selected:
+ * @field: The field.
+ * @item: The item.
*
- * @param field The field.
- * @param item The item.
+ * Returns whether or not a particular item is selected in a list field.
*
- * @return TRUE if the item is selected. FALSE otherwise.
+ * Returns: TRUE if the item is selected. FALSE otherwise.
*/
gboolean purple_request_field_list_is_selected(const PurpleRequestField *field,
const char *item);
/**
+ * purple_request_field_list_get_selected:
+ * @field: The field.
+ *
* Returns a list of selected items in a list field.
*
* To retrieve the data for each item, use
* purple_request_field_list_get_data().
*
- * @param field The field.
- *
- * @constreturn The list of selected items.
+ * Returns: (transfer none): The list of selected items.
*/
GList *purple_request_field_list_get_selected(
const PurpleRequestField *field);
/**
- * Returns a list of items in a list field.
+ * purple_request_field_list_get_items:
+ * @field: The field.
*
- * @param field The field.
+ * Returns a list of items in a list field.
*
- * @constreturn The list of items.
+ * Returns: (transfer none): The list of items.
*/
GList *purple_request_field_list_get_items(const PurpleRequestField *field);
/**
+ * purple_request_field_list_get_icons:
+ * @field: The field.
+ *
* Returns a list of icons in a list field.
*
* The icons will correspond with the items, in order.
*
- * @param field The field.
- *
- * @constreturn The list of icons or @c NULL (i.e. the empty GList) if no
- * items have icons.
+ * Returns: (transfer none): The list of icons or %NULL (i.e. the empty #GList)
+ * if no items have icons.
*/
GList *purple_request_field_list_get_icons(const PurpleRequestField *field);
-/*@}*/
-
/**************************************************************************/
-/** @name Label Field API */
+/* Label Field API */
/**************************************************************************/
-/*@{*/
/**
- * Creates a label field.
+ * purple_request_field_label_new:
+ * @id: The field ID.
+ * @text: The label of the field.
*
- * @param id The field ID.
- * @param text The label of the field.
+ * Creates a label field.
*
- * @return The new field.
+ * Returns: The new field.
*/
PurpleRequestField *purple_request_field_label_new(const char *id,
const char *text);
-/*@}*/
-
/**************************************************************************/
-/** @name Image Field API */
+/* Image Field API */
/**************************************************************************/
-/*@{*/
/**
- * Creates an image field.
+ * purple_request_field_image_new:
+ * @id: The field ID.
+ * @text: The label of the field.
+ * @buf: The image data.
+ * @size: The size of the data in @buf.
*
- * @param id The field ID.
- * @param text The label of the field.
- * @param buf The image data.
- * @param size The size of the data in @a buffer.
+ * Creates an image field.
*
- * @return The new field.
+ * Returns: The new field.
*/
PurpleRequestField *purple_request_field_image_new(const char *id, const char *text,
const char *buf, gsize size);
/**
- * Sets the scale factors of an image field.
+ * purple_request_field_image_set_scale:
+ * @field: The image field.
+ * @x: The x scale factor.
+ * @y: The y scale factor.
*
- * @param field The image field.
- * @param x The x scale factor.
- * @param y The y scale factor.
+ * Sets the scale factors of an image field.
*/
void purple_request_field_image_set_scale(PurpleRequestField *field, unsigned int x, unsigned int y);
/**
- * Returns pointer to the image.
+ * purple_request_field_image_get_buffer:
+ * @field: The image field.
*
- * @param field The image field.
+ * Returns pointer to the image.
*
- * @return Pointer to the image.
+ * Returns: Pointer to the image.
*/
const char *purple_request_field_image_get_buffer(PurpleRequestField *field);
/**
- * Returns size (in bytes) of the image.
+ * purple_request_field_image_get_size:
+ * @field: The image field.
*
- * @param field The image field.
+ * Returns size (in bytes) of the image.
*
- * @return Size of the image.
+ * Returns: Size of the image.
*/
gsize purple_request_field_image_get_size(PurpleRequestField *field);
/**
- * Returns X scale coefficient of the image.
+ * purple_request_field_image_get_scale_x:
+ * @field: The image field.
*
- * @param field The image field.
+ * Returns X scale coefficient of the image.
*
- * @return X scale coefficient of the image.
+ * Returns: X scale coefficient of the image.
*/
unsigned int purple_request_field_image_get_scale_x(PurpleRequestField *field);
/**
- * Returns Y scale coefficient of the image.
+ * purple_request_field_image_get_scale_y:
+ * @field: The image field.
*
- * @param field The image field.
+ * Returns Y scale coefficient of the image.
*
- * @return Y scale coefficient of the image.
+ * Returns: Y scale coefficient of the image.
*/
unsigned int purple_request_field_image_get_scale_y(PurpleRequestField *field);
-/*@}*/
-
/**************************************************************************/
-/** @name Account Field API */
+/* Account Field API */
/**************************************************************************/
-/*@{*/
/**
+ * purple_request_field_account_new:
+ * @id: The field ID.
+ * @text: The text label of the field.
+ * @account: The optional default account.
+ *
* Creates an account field.
*
* By default, this field will not show offline accounts.
*
- * @param id The field ID.
- * @param text The text label of the field.
- * @param account The optional default account.
- *
- * @return The new field.
+ * Returns: The new field.
*/
PurpleRequestField *purple_request_field_account_new(const char *id,
const char *text,
PurpleAccount *account);
/**
- * Sets the default account on an account field.
+ * purple_request_field_account_set_default_value:
+ * @field: The account field.
+ * @default_value: The default account.
*
- * @param field The account field.
- * @param default_value The default account.
+ * Sets the default account on an account field.
*/
void purple_request_field_account_set_default_value(PurpleRequestField *field,
PurpleAccount *default_value);
/**
- * Sets the account in an account field.
+ * purple_request_field_account_set_value:
+ * @field: The account field.
+ * @value: The account.
*
- * @param field The account field.
- * @param value The account.
+ * Sets the account in an account field.
*/
void purple_request_field_account_set_value(PurpleRequestField *field,
PurpleAccount *value);
/**
+ * purple_request_field_account_set_show_all:
+ * @field: The account field.
+ * @show_all: Whether or not to show all accounts.
+ *
* Sets whether or not to show all accounts in an account field.
*
* If TRUE, all accounts, online or offline, will be shown. If FALSE,
* only online accounts will be shown.
- *
- * @param field The account field.
- * @param show_all Whether or not to show all accounts.
*/
void purple_request_field_account_set_show_all(PurpleRequestField *field,
gboolean show_all);
/**
+ * purple_request_field_account_set_filter:
+ * @field: The account field.
+ * @filter_func: The account filter function.
+ *
* Sets the account filter function in an account field.
*
* This function will determine which accounts get displayed and which
* don't.
- *
- * @param field The account field.
- * @param filter_func The account filter function.
*/
void purple_request_field_account_set_filter(PurpleRequestField *field,
PurpleFilterAccountFunc filter_func);
/**
- * Returns the default account in an account field.
+ * purple_request_field_account_get_default_value:
+ * @field: The field.
*
- * @param field The field.
+ * Returns the default account in an account field.
*
- * @return The default account.
+ * Returns: The default account.
*/
PurpleAccount *purple_request_field_account_get_default_value(
const PurpleRequestField *field);
/**
- * Returns the user-entered account in an account field.
+ * purple_request_field_account_get_value:
+ * @field: The field.
*
- * @param field The field.
+ * Returns the user-entered account in an account field.
*
- * @return The user-entered account.
+ * Returns: The user-entered account.
*/
PurpleAccount *purple_request_field_account_get_value(
const PurpleRequestField *field);
/**
+ * purple_request_field_account_get_show_all:
+ * @field: The account field.
+ *
* Returns whether or not to show all accounts in an account field.
*
* If TRUE, all accounts, online or offline, will be shown. If FALSE,
* only online accounts will be shown.
*
- * @param field The account field.
- * @return Whether or not to show all accounts.
+ * Returns: Whether or not to show all accounts.
*/
gboolean purple_request_field_account_get_show_all(
const PurpleRequestField *field);
/**
+ * purple_request_field_account_get_filter:
+ * @field: The account field.
+ *
* Returns the account filter function in an account field.
*
* This function will determine which accounts get displayed and which
* don't.
*
- * @param field The account field.
- *
- * @return The account filter function.
+ * Returns: The account filter function.
*/
PurpleFilterAccountFunc purple_request_field_account_get_filter(
const PurpleRequestField *field);
-/*@}*/
+/**************************************************************************/
+/* Certificate Field API */
+/**************************************************************************/
+
+/**
+ * purple_request_field_certificate_new:
+ * @id: The field ID.
+ * @text: The label of the field.
+ * @cert: The certificate of the field.
+ *
+ * Creates a certificate field.
+ *
+ * Returns: The new field.
+ */
+PurpleRequestField *purple_request_field_certificate_new(const char *id,
+ const char *text,
+ PurpleCertificate *cert);
+
+/**
+ * purple_request_field_certificate_get_value:
+ * @field: The field.
+ *
+ * Returns the certificate in a certificate field.
+ *
+ * Returns: The certificate.
+ */
+PurpleCertificate *purple_request_field_certificate_get_value(
+ const PurpleRequestField *field);
+
+/**************************************************************************/
+/* Datasheet Field API */
+/**************************************************************************/
+
+/**
+ * purple_request_field_datasheet_new:
+ * @id: The field ID.
+ * @text: The label of the field, may be %NULL.
+ * @sheet: The datasheet.
+ *
+ * Creates a datasheet item field.
+ *
+ * Returns: The new field.
+ */
+PurpleRequestField *purple_request_field_datasheet_new(const char *id,
+ const gchar *text, PurpleRequestDatasheet *sheet);
+
+/**
+ * purple_request_field_datasheet_get_sheet:
+ * @field: The field.
+ *
+ * Returns a datasheet for a field.
+ *
+ * Returns: (transfer none): The datasheet object.
+ */
+PurpleRequestDatasheet *purple_request_field_datasheet_get_sheet(
+ PurpleRequestField *field);
+
+/**************************************************************************/
+/* Validators for request fields. */
+/**************************************************************************/
+
+/**
+ * purple_request_field_email_validator:
+ * @field: The field.
+ * @errmsg: (Optional) destination for error message.
+ * @user_data: Ignored.
+ *
+ * Validates a field which should contain an email address.
+ *
+ * See purple_request_field_set_validator().
+ *
+ * Returns: TRUE, if field contains valid email address.
+ */
+gboolean purple_request_field_email_validator(PurpleRequestField *field,
+ gchar **errmsg, void *user_data);
+
+/**
+ * purple_request_field_alphanumeric_validator:
+ * @field: The field.
+ * @errmsg: (allow-none): destination for error message.
+ * @allowed_characters: (allow-none): allowed character list
+ * (NULL-terminated string).
+ *
+ * Validates a field which should contain alphanumeric content.
+ *
+ * See purple_request_field_set_validator().
+ *
+ * Returns: TRUE, if field contains only alphanumeric characters.
+ */
+gboolean purple_request_field_alphanumeric_validator(PurpleRequestField *field,
+ gchar **errmsg, void *allowed_characters);
/**************************************************************************/
-/** @name Request API */
+/* Request API */
/**************************************************************************/
-/*@{*/
/**
+ * purple_request_input:
+ * @handle: The plugin or connection handle. For some
+ * things this is <emphasis>extremely</emphasis> important. The
+ * handle is used to programmatically close the request
+ * dialog when it is no longer needed. For protocols this
+ * is often a pointer to the #PurpleConnection
+ * instance. For plugins this should be a similar,
+ * unique memory location. This value is important
+ * because it allows a request to be closed with
+ * purple_request_close_with_handle() when, for
+ * example, you sign offline. If the request is
+ * <emphasis>not</emphasis> closed it is
+ * <emphasis>very</emphasis> likely to cause a crash whenever
+ * the callback handler functions are triggered.
+ * @title: The title of the message, or %NULL if it should have
+ * no title.
+ * @primary: The main point of the message, or %NULL if you're
+ * feeling enigmatic.
+ * @secondary: Secondary information, or %NULL if there is none.
+ * @default_value: The default value.
+ * @multiline: %TRUE if the inputted text can span multiple lines.
+ * @masked: %TRUE if the inputted text should be masked in some
+ * way (such as by displaying characters as stars). This
+ * might be because the input is some kind of password.
+ * @hint: Optionally suggest how the input box should appear.
+ * Use "html", for example, to allow the user to enter HTML.
+ * @ok_text: The text for the <literal>OK</literal> button, which may not
+ * be %NULL.
+ * @ok_cb: The callback for the <literal>OK</literal> button, which may
+ * not be %NULL.
+ * @cancel_text: The text for the <literal>Cancel</literal> button, which may
+ * not be %NULL.
+ * @cancel_cb: The callback for the <literal>Cancel</literal> button, which
+ * may be %NULL.
+ * @cpar: The #PurpleRequestCommonParameters object, which gets
+ * unref'ed after this call.
+ * @user_data: The data to pass to the callback.
+ *
* Prompts the user for text input.
*
- * @param handle The plugin or connection handle. For some
- * things this is <em>extremely</em> important. The
- * handle is used to programmatically close the request
- * dialog when it is no longer needed. For PRPLs this
- * is often a pointer to the #PurpleConnection
- * instance. For plugins this should be a similar,
- * unique memory location. This value is important
- * because it allows a request to be closed with
- * purple_request_close_with_handle() when, for
- * example, you sign offline. If the request is
- * <em>not</em> closed it is <strong>very</strong>
- * likely to cause a crash whenever the callback
- * handler functions are triggered.
- * @param title The title of the message, or @c NULL if it should have
- * no title.
- * @param primary The main point of the message, or @c NULL if you're
- * feeling enigmatic.
- * @param secondary Secondary information, or @c NULL if there is none.
- * @param default_value The default value.
- * @param multiline @c TRUE if the inputted text can span multiple lines.
- * @param masked @c TRUE if the inputted text should be masked in some
- * way (such as by displaying characters as stars). This
- * might be because the input is some kind of password.
- * @param hint Optionally suggest how the input box should appear.
- * Use "html", for example, to allow the user to enter
- * HTML.
- * @param ok_text The text for the @c OK button, which may not be @c NULL.
- * @param ok_cb The callback for the @c OK button, which may not be @c
- * NULL.
- * @param cancel_text The text for the @c Cancel button, which may not be @c
- * NULL.
- * @param cancel_cb The callback for the @c Cancel button, which may be
- * @c NULL.
- * @param account The #PurpleAccount associated with this request, or @c
- * NULL if none is.
- * @param who The username of the buddy associated with this request,
- * or @c NULL if none is.
- * @param conv The #PurpleConversation associated with this request, or
- * @c NULL if none is.
- * @param user_data The data to pass to the callback.
- *
- * @return A UI-specific handle.
+ * Returns: A UI-specific handle.
*/
void *purple_request_input(void *handle, const char *title, const char *primary,
const char *secondary, const char *default_value, gboolean multiline,
gboolean masked, gchar *hint,
const char *ok_text, GCallback ok_cb,
const char *cancel_text, GCallback cancel_cb,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
+ PurpleRequestCommonParameters *cpar,
void *user_data);
/**
+ * purple_request_choice:
+ * @handle: The plugin or connection handle. For some things this
+ * is <emphasis>extremely</emphasis> important. See the comments on
+ * purple_request_input().
+ * @title: The title of the message, or %NULL if it should have
+ * no title.
+ * @primary: The main point of the message, or %NULL if you're
+ * feeling enigmatic.
+ * @secondary: Secondary information, or %NULL if there is none.
+ * @default_value: The default choice; this should be one of the values
+ * listed in the varargs.
+ * @ok_text: The text for the <literal>OK</literal> button, which may not
+ * be %NULL.
+ * @ok_cb: The callback for the <literal>OK</literal> button, which may
+ * not be %NULL.
+ * @cancel_text: The text for the <literal>Cancel</literal> button, which may
+ * not be %NULL.
+ * @cancel_cb: The callback for the <literal>Cancel</literal> button, or
+ * %NULL to do nothing.
+ * @cpar: The #PurpleRequestCommonParameters object, which gets
+ * unref'ed after this call.
+ * @user_data: The data to pass to the callback.
+ * @...: The choices, which should be pairs of <type>char *</type>
+ * descriptions and <type>int</type> values, terminated with a
+ * %NULL parameter.
+ *
* Prompts the user for multiple-choice input.
*
- * @param handle The plugin or connection handle. For some things this
- * is <em>extremely</em> important. See the comments on
- * purple_request_input().
- * @param title The title of the message, or @c NULL if it should have
- * no title.
- * @param primary The main point of the message, or @c NULL if you're
- * feeling enigmatic.
- * @param secondary Secondary information, or @c NULL if there is none.
- * @param default_value The default choice; this should be one of the values
- * listed in the varargs.
- * @param ok_text The text for the @c OK button, which may not be @c NULL.
- * @param ok_cb The callback for the @c OK button, which may not be @c
- * NULL.
- * @param cancel_text The text for the @c Cancel button, which may not be @c
- * NULL.
- * @param cancel_cb The callback for the @c Cancel button, or @c NULL to
- * do nothing.
- * @param account The #PurpleAccount associated with this request, or @c
- * NULL if none is.
- * @param who The username of the buddy associated with this request,
- * or @c NULL if none is.
- * @param conv The #PurpleConversation associated with this request, or
- * @c NULL if none is.
- * @param user_data The data to pass to the callback.
- * @param ... The choices, which should be pairs of <tt>char *</tt>
- * descriptions and <tt>int</tt> values, terminated with a
- * @c NULL parameter.
- *
- * @return A UI-specific handle.
+ * Returns: A UI-specific handle.
*/
void *purple_request_choice(void *handle, const char *title, const char *primary,
- const char *secondary, int default_value,
+ const char *secondary, gpointer default_value,
const char *ok_text, GCallback ok_cb,
const char *cancel_text, GCallback cancel_cb,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
+ PurpleRequestCommonParameters *cpar,
void *user_data, ...) G_GNUC_NULL_TERMINATED;
/**
- * <tt>va_list</tt> version of purple_request_choice(); see its documentation.
+ * purple_request_choice_varg:
+ *
+ * <literal>va_list</literal> version of purple_request_choice(); see its
+ * documentation.
*/
void *purple_request_choice_varg(void *handle, const char *title,
- const char *primary, const char *secondary, int default_value,
+ const char *primary, const char *secondary, gpointer default_value,
const char *ok_text, GCallback ok_cb,
const char *cancel_text, GCallback cancel_cb,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
+ PurpleRequestCommonParameters *cpar,
void *user_data, va_list choices);
/**
- * Prompts the user for an action.
- *
- * This is often represented as a dialog with a button for each action.
- *
- * @param handle The plugin or connection handle. For some things this
- * is <em>extremely</em> important. See the comments on
+ * purple_request_action:
+ * @handle: The plugin or connection handle. For some things this
+ * is <emphasis>extremely</emphasis> important. See the comments on
* purple_request_input().
- * @param title The title of the message, or @c NULL if it should have
+ * @title: The title of the message, or %NULL if it should have
* no title.
- * @param primary The main point of the message, or @c NULL if you're
+ * @primary: The main point of the message, or %NULL if you're
* feeling enigmatic.
- * @param secondary Secondary information, or @c NULL if there is none.
- * @param default_action The default action, zero-indexed; if the third action
- * supplied should be the default, supply <tt>2</tt>.
- * The should be the action that users are most likely
- * to select.
- * @param account The #PurpleAccount associated with this request, or @c
- * NULL if none is.
- * @param who The username of the buddy associated with this request,
- * or @c NULL if none is.
- * @param conv The #PurpleConversation associated with this request, or
- * @c NULL if none is.
- * @param user_data The data to pass to the callback.
- * @param action_count The number of actions.
- * @param ... A list of actions. These are pairs of
+ * @secondary: Secondary information, or %NULL if there is none.
+ * @default_action: The default action, zero-indexed; if the third action
+ * supplied should be the default, supply
+ * <literal>2</literal>. This should be the action that
+ * users are most likely to select.
+ * @cpar: The #PurpleRequestCommonParameters object, which gets
+ * unref'ed after this call.
+ * @user_data: The data to pass to the callback.
+ * @action_count: The number of actions.
+ * @...: A list of actions. These are pairs of
* arguments. The first of each pair is the
- * <tt>char *</tt> label that appears on the button. It
- * should have an underscore before the letter you want
+ * <type>char *</type> label that appears on the button.
+ * It should have an underscore before the letter you want
* to use as the accelerator key for the button. The
* second of each pair is the #PurpleRequestActionCb
* function to use when the button is clicked.
*
- * @return A UI-specific handle.
+ * Prompts the user for an action.
+ *
+ * This is often represented as a dialog with a button for each action.
+ *
+ * Returns: A UI-specific handle.
*/
-void *purple_request_action(void *handle, const char *title, const char *primary,
- const char *secondary, int default_action, PurpleAccount *account,
- const char *who, PurpleConversation *conv, void *user_data,
+void *
+purple_request_action(void *handle, const char *title, const char *primary,
+ const char *secondary, int default_action,
+ PurpleRequestCommonParameters *cpar, void *user_data,
size_t action_count, ...);
/**
- * <tt>va_list</tt> version of purple_request_action(); see its documentation.
+ * purple_request_action_varg:
+ *
+ * <literal>va_list</literal> version of purple_request_action(); see its
+ * documentation.
*/
-void *purple_request_action_varg(void *handle, const char *title,
- const char *primary, const char *secondary, int default_action,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
- void *user_data, size_t action_count, va_list actions);
+void *
+purple_request_action_varg(void *handle, const char *title, const char *primary,
+ const char *secondary, int default_action,
+ PurpleRequestCommonParameters *cpar, void *user_data,
+ size_t action_count, va_list actions);
/**
- * Version of purple_request_action() supplying an image for the UI to
- * optionally display as an icon in the dialog; see its documentation
- * @since 2.7.0
+ * purple_request_wait:
+ * @handle: The plugin or connection handle. For some things this
+ * is <emphasis>extremely</emphasis> important. See the comments on
+ * purple_request_input().
+ * @title: The title of the message, or %NULL if it should have
+ * default title.
+ * @primary: The main point of the message, or %NULL if you're
+ * feeling enigmatic.
+ * @secondary: Secondary information, or %NULL if there is none.
+ * @with_progress: %TRUE, if we want to display progress bar, %FALSE
+ * otherwise
+ * @cancel_cb: The callback for the <literal>Cancel</literal> button, which
+ * may be %NULL.
+ * @cpar: The #PurpleRequestCommonParameters object, which gets
+ * unref'ed after this call.
+ * @user_data: The data to pass to the callback.
+ *
+ * Displays a "please wait" dialog.
+ *
+ * Returns: A UI-specific handle.
*/
-void *purple_request_action_with_icon(void *handle, const char *title,
- const char *primary, const char *secondary, int default_action,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
- gconstpointer icon_data, gsize icon_size, void *user_data,
- size_t action_count, ...);
+void *
+purple_request_wait(void *handle, const char *title, const char *primary,
+ const char *secondary, gboolean with_progress,
+ PurpleRequestCancelCb cancel_cb, PurpleRequestCommonParameters *cpar,
+ void *user_data);
/**
- * <tt>va_list</tt> version of purple_request_action_with_icon();
- * see its documentation.
- * @since 2.7.0
+ * purple_request_wait_pulse:
+ * @ui_handle: The request UI handle.
+ *
+ * Notifies the "please wait" dialog that some progress has been made, but you
+ * don't know how much.
*/
-void *purple_request_action_with_icon_varg(void *handle, const char *title,
- const char *primary, const char *secondary, int default_action,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
- gconstpointer icon_data, gsize icon_size,
- void *user_data, size_t action_count, va_list actions);
+void
+purple_request_wait_pulse(void *ui_handle);
+/**
+ * purple_request_wait_progress:
+ * @ui_handle: The request UI handle.
+ * @fraction: The part of task that is done (between 0.0 and 1.0,
+ * inclusive).
+ *
+ * Notifies the "please wait" dialog about progress has been made.
+ */
+void
+purple_request_wait_progress(void *ui_handle, gfloat fraction);
/**
+ * purple_request_fields:
+ * @handle: The plugin or connection handle. For some things this
+ * is <emphasis>extremely</emphasis> important. See the comments on
+ * purple_request_input().
+ * @title: The title of the message, or %NULL if it should have
+ * no title.
+ * @primary: The main point of the message, or %NULL if you're
+ * feeling enigmatic.
+ * @secondary: Secondary information, or %NULL if there is none.
+ * @fields: The list of fields.
+ * @ok_text: The text for the <literal>OK</literal> button, which may not be
+ * %NULL.
+ * @ok_cb: The callback for the <literal>OK</literal> button, which may
+ * not be
+ * %NULL.
+ * @cancel_text: The text for the <literal>Cancel</literal> button, which may
+ * not be %NULL.
+ * @cancel_cb: The callback for the <literal>Cancel</literal> button, which
+ * may be %NULL.
+ * @cpar: The #PurpleRequestCommonParameters object, which gets
+ * unref'ed after this call.
+ * @user_data: The data to pass to the callback.
+ *
* Displays groups of fields for the user to fill in.
*
- * @param handle The plugin or connection handle. For some things this
- * is <em>extremely</em> important. See the comments on
- * purple_request_input().
- * @param title The title of the message, or @c NULL if it should have
- * no title.
- * @param primary The main point of the message, or @c NULL if you're
- * feeling enigmatic.
- * @param secondary Secondary information, or @c NULL if there is none.
- * @param fields The list of fields.
- * @param ok_text The text for the @c OK button, which may not be @c NULL.
- * @param ok_cb The callback for the @c OK button, which may not be @c
- * NULL.
- * @param cancel_text The text for the @c Cancel button, which may not be @c
- * NULL.
- * @param cancel_cb The callback for the @c Cancel button, which may be
- * @c NULL.
- * @param account The #PurpleAccount associated with this request, or @c
- * NULL if none is
- * @param who The username of the buddy associated with this request,
- * or @c NULL if none is
- * @param conv The #PurpleConversation associated with this request, or
- * @c NULL if none is
- * @param user_data The data to pass to the callback.
- *
- * @return A UI-specific handle.
- */
-void *purple_request_fields(void *handle, const char *title, const char *primary,
+ * Returns: A UI-specific handle.
+ */
+void *
+purple_request_fields(void *handle, const char *title, const char *primary,
const char *secondary, PurpleRequestFields *fields,
const char *ok_text, GCallback ok_cb,
const char *cancel_text, GCallback cancel_cb,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
+ PurpleRequestCommonParameters *cpar,
void *user_data);
/**
- * Closes a request.
+ * purple_request_is_valid_ui_handle:
+ * @ui_handle: The UI handle.
+ * @type: The pointer to variable, where request type may be stored
+ * (may be %NULL).
+ *
+ * Checks, if passed UI handle is valid.
+ *
+ * Returns: TRUE, if handle is valid, FALSE otherwise.
+ */
+gboolean
+purple_request_is_valid_ui_handle(void *ui_handle, PurpleRequestType *type);
+
+/**
+ * purple_request_add_close_notify:
+ * @ui_handle: The UI handle.
+ * @notify: The function to be called.
+ * @notify_data: The data to be passed to the callback function.
*
- * @param type The request type.
- * @param uihandle The request UI handle.
+ * Adds a function called when notification dialog is closed.
+ */
+void
+purple_request_add_close_notify(void *ui_handle, GDestroyNotify notify,
+ gpointer notify_data);
+
+/**
+ * purple_request_close:
+ * @type: The request type.
+ * @uihandle: The request UI handle.
+ *
+ * Closes a request.
*/
void purple_request_close(PurpleRequestType type, void *uihandle);
/**
- * Closes all requests registered with the specified handle.
+ * purple_request_close_with_handle:
+ * @handle: The handle, as supplied as the @handle parameter to one of the
+ * <literal>purple_request_*</literal> functions.
*
- * @param handle The handle, as supplied as the @a handle parameter to one of the
- * <tt>purple_request_*</tt> functions.
+ * Closes all requests registered with the specified handle.
*
- * @see purple_request_input().
+ * See purple_request_input().
*/
void purple_request_close_with_handle(void *handle);
/**
- * A wrapper for purple_request_action() that uses @c Yes and @c No buttons.
+ * purple_request_yes_no:
+ *
+ * A wrapper for purple_request_action() that uses <literal>Yes</literal> and
+ * <literal>No</literal> buttons.
*/
#define purple_request_yes_no(handle, title, primary, secondary, \
- default_action, account, who, conv, \
- user_data, yes_cb, no_cb) \
+ default_action, cpar, user_data, yes_cb, no_cb) \
purple_request_action((handle), (title), (primary), (secondary), \
- (default_action), account, who, conv, (user_data), 2, \
- _("_Yes"), (yes_cb), _("_No"), (no_cb))
+ (default_action), (cpar), (user_data), 2, _("_Yes"), (yes_cb), \
+ _("_No"), (no_cb))
/**
- * A wrapper for purple_request_action() that uses @c OK and @c Cancel buttons.
+ * purple_request_ok_cancel:
+ *
+ * A wrapper for purple_request_action() that uses <literal>OK</literal> and
+ * <literal>Cancel</literal> buttons.
*/
#define purple_request_ok_cancel(handle, title, primary, secondary, \
- default_action, account, who, conv, \
- user_data, ok_cb, cancel_cb) \
+ default_action, cpar, user_data, ok_cb, cancel_cb) \
purple_request_action((handle), (title), (primary), (secondary), \
- (default_action), account, who, conv, (user_data), 2, \
- _("_OK"), (ok_cb), _("_Cancel"), (cancel_cb))
+ (default_action), (cpar), (user_data), 2, _("_OK"), (ok_cb), \
+ _("_Cancel"), (cancel_cb))
/**
+ * purple_request_accept_cancel:
+ *
* A wrapper for purple_request_action() that uses Accept and Cancel buttons.
*/
#define purple_request_accept_cancel(handle, title, primary, secondary, \
- default_action, account, who, conv, \
- user_data, accept_cb, cancel_cb) \
+ default_action, cpar, user_data, accept_cb, cancel_cb) \
purple_request_action((handle), (title), (primary), (secondary), \
- (default_action), account, who, conv, (user_data), 2, \
- _("_Accept"), (accept_cb), _("_Cancel"), (cancel_cb))
+ (default_action), (cpar), (user_data), 2, _("_Accept"), \
+ (accept_cb), _("_Cancel"), (cancel_cb))
/**
- * A wrapper for purple_request_action_with_icon() that uses Accept and Cancel
- * buttons.
- */
-#define purple_request_accept_cancel_with_icon(handle, title, primary, secondary, \
- default_action, account, who, conv, \
- icon_data, icon_size, \
- user_data, accept_cb, cancel_cb) \
- purple_request_action_with_icon((handle), (title), (primary), (secondary), \
- (default_action), account, who, conv, icon_data, icon_size, \
- (user_data), 2, \
- _("_Accept"), (accept_cb), _("_Cancel"), (cancel_cb))
-
-/**
- * Displays a file selector request dialog. Returns the selected filename to
- * the callback. Can be used for either opening a file or saving a file.
- *
- * @param handle The plugin or connection handle. For some things this
- * is <em>extremely</em> important. See the comments on
+ * purple_request_file:
+ * @handle: The plugin or connection handle. For some things this
+ * is <emphasis>extremely</emphasis> important. See the comments on
* purple_request_input().
- * @param title The title of the message, or @c NULL if it should have
+ * @title: The title of the message, or %NULL if it should have
* no title.
- * @param filename The default filename (may be @c NULL)
- * @param savedialog True if this dialog is being used to save a file.
+ * @filename: The default filename (may be %NULL)
+ * @savedialog: True if this dialog is being used to save a file.
* False if it is being used to open a file.
- * @param ok_cb The callback for the @c OK button.
- * @param cancel_cb The callback for the @c Cancel button, which may be @c NULL.
- * @param account The #PurpleAccount associated with this request, or @c
- * NULL if none is
- * @param who The username of the buddy associated with this request,
- * or @c NULL if none is
- * @param conv The #PurpleConversation associated with this request, or
- * @c NULL if none is
- * @param user_data The data to pass to the callback.
- *
- * @return A UI-specific handle.
- */
-void *purple_request_file(void *handle, const char *title, const char *filename,
+ * @ok_cb: The callback for the <literal>OK</literal> button.
+ * @cancel_cb: The callback for the <literal>Cancel</literal> button, which
+ * may be %NULL.
+ * @cpar: The #PurpleRequestCommonParameters object, which gets
+ * unref'ed after this call.
+ * @user_data: The data to pass to the callback.
+ *
+ * Displays a file selector request dialog. Returns the selected filename to
+ * the callback. Can be used for either opening a file or saving a file.
+ *
+ * Returns: A UI-specific handle.
+ */
+void *
+purple_request_file(void *handle, const char *title, const char *filename,
gboolean savedialog, GCallback ok_cb, GCallback cancel_cb,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
- void *user_data);
+ PurpleRequestCommonParameters *cpar, void *user_data);
/**
+ * purple_request_folder:
+ * @handle: The plugin or connection handle. For some things this
+ * is <emphasis>extremely</emphasis> important. See the comments on
+ * purple_request_input().
+ * @title: The title of the message, or %NULL if it should have
+ * no title.
+ * @dirname: The default directory name (may be %NULL)
+ * @ok_cb: The callback for the <literal>OK</literal> button.
+ * @cancel_cb: The callback for the <literal>Cancel</literal> button, which
+ * may be %NULL.
+ * @cpar: The #PurpleRequestCommonParameters object, which gets
+ * unref'ed after this call.
+ * @user_data: The data to pass to the callback.
+ *
* Displays a folder select dialog. Returns the selected filename to
* the callback.
*
- * @param handle The plugin or connection handle. For some things this
- * is <em>extremely</em> important. See the comments on
- * purple_request_input().
- * @param title The title of the message, or @c NULL if it should have
- * no title.
- * @param dirname The default directory name (may be @c NULL)
- * @param ok_cb The callback for the @c OK button.
- * @param cancel_cb The callback for the @c Cancel button, which may be @c NULL.
- * @param account The #PurpleAccount associated with this request, or @c
- * NULL if none is
- * @param who The username of the buddy associated with this request,
- * or @c NULL if none is
- * @param conv The #PurpleConversation associated with this request, or
- * @c NULL if none is
- * @param user_data The data to pass to the callback.
- *
- * @return A UI-specific handle.
- */
-void *purple_request_folder(void *handle, const char *title, const char *dirname,
+ * Returns: A UI-specific handle.
+ */
+void *
+purple_request_folder(void *handle, const char *title, const char *dirname,
GCallback ok_cb, GCallback cancel_cb,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
+ PurpleRequestCommonParameters *cpar, void *user_data);
+
+/**
+ * purple_request_certificate:
+ * @handle: The plugin or connection handle. For some things this
+ * is <emphasis>extremely</emphasis> important. See the comments on
+ * purple_request_input().
+ * @title: The title of the message, or %NULL if it should have
+ * no title.
+ * @primary: The main point of the message, or %NULL if you're
+ * feeling enigmatic.
+ * @secondary: Secondary information, or %NULL if there is none.
+ * @cert: The #PurpleCertificate associated with this request.
+ * @ok_text: The text for the <literal>OK</literal> button, which may not
+ * be %NULL.
+ * @ok_cb: The callback for the <literal>OK</literal> button, which may
+ * not be %NULL.
+ * @cancel_text: The text for the <literal>Cancel</literal> button, which may
+ * not be %NULL.
+ * @cancel_cb: The callback for the <literal>Cancel</literal> button, which
+ * may be %NULL.
+ * @user_data: The data to pass to the callback.
+ *
+ * Prompts the user for action over a certificate.
+ *
+ * This is often represented as a dialog with a button for each action.
+ *
+ * Returns: A UI-specific handle.
+ */
+void *purple_request_certificate(void *handle, const char *title,
+ const char *primary, const char *secondary, PurpleCertificate *cert,
+ const char *ok_text, GCallback ok_cb,
+ const char *cancel_text, GCallback cancel_cb,
void *user_data);
-/*@}*/
-
/**************************************************************************/
-/** @name UI Registration Functions */
+/* UI Registration Functions */
/**************************************************************************/
-/*@{*/
/**
+ * purple_request_ui_ops_get_type:
+ *
+ * Returns: The #GType for the #PurpleRequestUiOps boxed structure.
+ */
+GType purple_request_ui_ops_get_type(void);
+
+/**
+ * purple_request_set_ui_ops:
+ * @ops: The UI operations structure.
+ *
* Sets the UI operations structure to be used when displaying a
* request.
- *
- * @param ops The UI operations structure.
*/
void purple_request_set_ui_ops(PurpleRequestUiOps *ops);
/**
+ * purple_request_get_ui_ops:
+ *
* Returns the UI operations structure to be used when displaying a
* request.
*
- * @return The UI operations structure.
+ * Returns: The UI operations structure.
*/
PurpleRequestUiOps *purple_request_get_ui_ops(void);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_REQUEST_H_ */
diff --git a/libpurple/roomlist.c b/libpurple/roomlist.c
index 8a415ebe00..de70957066 100644
--- a/libpurple/roomlist.c
+++ b/libpurple/roomlist.c
@@ -1,8 +1,3 @@
-/**
- * @file roomlist.c Room List API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -25,6 +20,7 @@
*/
#include "internal.h"
+#include "glibcompat.h"
#include "account.h"
#include "connection.h"
@@ -32,13 +28,65 @@
#include "roomlist.h"
#include "server.h"
+#define PURPLE_ROOMLIST_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_ROOMLIST, PurpleRoomlistPrivate))
+typedef struct _PurpleRoomlistPrivate PurpleRoomlistPrivate;
+
+/*
+ * Private data for a room list.
+ */
+struct _PurpleRoomlistPrivate {
+ PurpleAccount *account; /* The account this list belongs to. */
+ GList *fields; /* The fields. */
+ GList *rooms; /* The list of rooms. */
+ gboolean in_progress; /* The listing is in progress. */
+
+ /* TODO Remove this and use protocol-specific subclasses. */
+ gpointer proto_data; /* Protocol private data. */
+};
+
+/*
+ * Represents a room.
+ */
+struct _PurpleRoomlistRoom {
+ PurpleRoomlistRoomType type; /* The type of room. */
+ gchar *name; /* The name of the room. */
+ GList *fields; /* Other fields. */
+ PurpleRoomlistRoom *parent; /* The parent room, or NULL. */
+ gboolean expanded_once; /* A flag the UI uses to avoid multiple expand prpl cbs. */
+};
+
+/*
+ * A field a room might have.
+ */
+struct _PurpleRoomlistField {
+ PurpleRoomlistFieldType type; /* The type of field. */
+ gchar *label; /* The i18n user displayed name of the field. */
+ gchar *name; /* The internal name of the field. */
+ gboolean hidden; /* Hidden? */
+};
+
+/* Room list property enums */
+enum
+{
+ PROP_0,
+ PROP_ACCOUNT,
+ PROP_FIELDS,
+ PROP_IN_PROGRESS,
+ PROP_LAST
+};
+
+static GObjectClass *parent_class;
+static GParamSpec *properties[PROP_LAST];
static PurpleRoomlistUiOps *ops = NULL;
+static void purple_roomlist_field_free(PurpleRoomlistField *f);
+static void purple_roomlist_room_destroy(PurpleRoomlist *list, PurpleRoomlistRoom *r);
+
/**************************************************************************/
-/** @name Room List API */
+/* Room List API */
/**************************************************************************/
-/*@{*/
void purple_roomlist_show_with_account(PurpleAccount *account)
{
@@ -46,120 +94,60 @@ void purple_roomlist_show_with_account(PurpleAccount *account)
ops->show_with_account(account);
}
-PurpleRoomlist *purple_roomlist_new(PurpleAccount *account)
-{
- PurpleRoomlist *list;
-
- g_return_val_if_fail(account != NULL, NULL);
-
- list = g_new0(PurpleRoomlist, 1);
- list->account = account;
- list->rooms = NULL;
- list->fields = NULL;
- list->ref = 1;
-
- if (ops && ops->create)
- ops->create(list);
-
- return list;
-}
-
-void purple_roomlist_ref(PurpleRoomlist *list)
-{
- g_return_if_fail(list != NULL);
-
- list->ref++;
- purple_debug_misc("roomlist", "reffing list, ref count now %d\n", list->ref);
-}
-
-static void purple_roomlist_room_destroy(PurpleRoomlist *list, PurpleRoomlistRoom *r)
-{
- GList *l, *j;
-
- for (l = list->fields, j = r->fields; l && j; l = l->next, j = j->next) {
- PurpleRoomlistField *f = l->data;
- if (f->type == PURPLE_ROOMLIST_FIELD_STRING)
- g_free(j->data);
- }
-
- g_list_free(r->fields);
- g_free(r->name);
- g_free(r);
-}
-
-static void purple_roomlist_field_destroy(PurpleRoomlistField *f)
-{
- g_free(f->label);
- g_free(f->name);
- g_free(f);
-}
-
-static void purple_roomlist_destroy(PurpleRoomlist *list)
+PurpleAccount *purple_roomlist_get_account(PurpleRoomlist *list)
{
- GList *l;
-
- purple_debug_misc("roomlist", "destroying list %p\n", list);
-
- if (ops && ops->destroy)
- ops->destroy(list);
-
- for (l = list->rooms; l; l = l->next) {
- PurpleRoomlistRoom *r = l->data;
- purple_roomlist_room_destroy(list, r);
- }
- g_list_free(list->rooms);
+ PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
- g_list_foreach(list->fields, (GFunc)purple_roomlist_field_destroy, NULL);
- g_list_free(list->fields);
+ g_return_val_if_fail(priv != NULL, NULL);
- g_free(list);
-}
-
-void purple_roomlist_unref(PurpleRoomlist *list)
-{
- g_return_if_fail(list != NULL);
- g_return_if_fail(list->ref > 0);
-
- list->ref--;
-
- purple_debug_misc("roomlist", "unreffing list, ref count now %d\n", list->ref);
- if (list->ref == 0)
- purple_roomlist_destroy(list);
+ return priv->account;
}
void purple_roomlist_set_fields(PurpleRoomlist *list, GList *fields)
{
- g_return_if_fail(list != NULL);
+ PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
+
+ g_return_if_fail(priv != NULL);
- list->fields = fields;
+ priv->fields = fields;
if (ops && ops->set_fields)
ops->set_fields(list, fields);
+
+ g_object_notify_by_pspec(G_OBJECT(list), properties[PROP_FIELDS]);
}
void purple_roomlist_set_in_progress(PurpleRoomlist *list, gboolean in_progress)
{
- g_return_if_fail(list != NULL);
+ PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
+
+ g_return_if_fail(priv != NULL);
- list->in_progress = in_progress;
+ priv->in_progress = in_progress;
if (ops && ops->in_progress)
ops->in_progress(list, in_progress);
+
+ g_object_notify_by_pspec(G_OBJECT(list), properties[PROP_IN_PROGRESS]);
}
gboolean purple_roomlist_get_in_progress(PurpleRoomlist *list)
{
- g_return_val_if_fail(list != NULL, FALSE);
+ PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
+
+ g_return_val_if_fail(priv != NULL, FALSE);
- return list->in_progress;
+ return priv->in_progress;
}
void purple_roomlist_room_add(PurpleRoomlist *list, PurpleRoomlistRoom *room)
{
- g_return_if_fail(list != NULL);
+ PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
+
+ g_return_if_fail(priv != NULL);
g_return_if_fail(room != NULL);
- list->rooms = g_list_append(list->rooms, room);
+ priv->rooms = g_list_append(priv->rooms, room);
if (ops && ops->add_room)
ops->add_room(list, room);
@@ -170,7 +158,7 @@ PurpleRoomlist *purple_roomlist_get_list(PurpleConnection *gc)
PurplePlugin *prpl = NULL;
PurplePluginProtocolInfo *prpl_info = NULL;
- g_return_val_if_fail(gc != NULL, NULL);
+ g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
g_return_val_if_fail(PURPLE_CONNECTION_IS_CONNECTED(gc), NULL);
prpl = purple_connection_get_prpl(gc);
@@ -186,15 +174,16 @@ PurpleRoomlist *purple_roomlist_get_list(PurpleConnection *gc)
void purple_roomlist_cancel_get_list(PurpleRoomlist *list)
{
+ PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
PurplePlugin *prpl = NULL;
PurplePluginProtocolInfo *prpl_info = NULL;
PurpleConnection *gc;
- g_return_if_fail(list != NULL);
+ g_return_if_fail(priv != NULL);
- gc = purple_account_get_connection(list->account);
+ gc = purple_account_get_connection(priv->account);
- g_return_if_fail(gc != NULL);
+ g_return_if_fail(PURPLE_IS_CONNECTION(gc));
if(gc)
prpl = purple_connection_get_prpl(gc);
@@ -208,16 +197,17 @@ void purple_roomlist_cancel_get_list(PurpleRoomlist *list)
void purple_roomlist_expand_category(PurpleRoomlist *list, PurpleRoomlistRoom *category)
{
+ PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
PurplePlugin *prpl = NULL;
PurplePluginProtocolInfo *prpl_info = NULL;
PurpleConnection *gc;
- g_return_if_fail(list != NULL);
+ g_return_if_fail(priv != NULL);
g_return_if_fail(category != NULL);
g_return_if_fail(category->type & PURPLE_ROOMLIST_ROOMTYPE_CATEGORY);
- gc = purple_account_get_connection(list->account);
- g_return_if_fail(gc != NULL);
+ gc = purple_account_get_connection(priv->account);
+ g_return_if_fail(PURPLE_IS_CONNECTION(gc));
if(gc)
prpl = purple_connection_get_prpl(gc);
@@ -231,15 +221,207 @@ void purple_roomlist_expand_category(PurpleRoomlist *list, PurpleRoomlistRoom *c
GList * purple_roomlist_get_fields(PurpleRoomlist *list)
{
- return list->fields;
+ PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->fields;
+}
+
+gpointer purple_roomlist_get_protocol_data(PurpleRoomlist *list)
+{
+ PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->proto_data;
+}
+
+void purple_roomlist_set_protocol_data(PurpleRoomlist *list, gpointer proto_data)
+{
+ PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->proto_data = proto_data;
+}
+
+gpointer purple_roomlist_get_ui_data(PurpleRoomlist *list)
+{
+ g_return_val_if_fail(PURPLE_IS_ROOMLIST(list), NULL);
+
+ return list->ui_data;
+}
+
+void purple_roomlist_set_ui_data(PurpleRoomlist *list, gpointer ui_data)
+{
+ g_return_if_fail(PURPLE_IS_ROOMLIST(list));
+
+ list->ui_data = ui_data;
+}
+
+/**************************************************************************/
+/* Room List GObject code */
+/**************************************************************************/
+
+/* Set method for GObject properties */
+static void
+purple_roomlist_set_property(GObject *obj, guint param_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleRoomlist *list = PURPLE_ROOMLIST(obj);
+ PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
+
+ switch (param_id) {
+ case PROP_ACCOUNT:
+ priv->account = g_value_get_object(value);
+ break;
+ case PROP_FIELDS:
+ purple_roomlist_set_fields(list, g_value_get_pointer(value));
+ break;
+ case PROP_IN_PROGRESS:
+ purple_roomlist_set_in_progress(list, g_value_get_boolean(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* Get method for GObject properties */
+static void
+purple_roomlist_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleRoomlist *list = PURPLE_ROOMLIST(obj);
+
+ switch (param_id) {
+ case PROP_ACCOUNT:
+ g_value_set_object(value, purple_roomlist_get_account(list));
+ break;
+ case PROP_FIELDS:
+ g_value_set_pointer(value, purple_roomlist_get_fields(list));
+ break;
+ case PROP_IN_PROGRESS:
+ g_value_set_boolean(value, purple_roomlist_get_in_progress(list));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* Called when done constructing */
+static void
+purple_roomlist_constructed(GObject *object)
+{
+ PurpleRoomlist *list = PURPLE_ROOMLIST(object);
+
+ parent_class->constructed(object);
+
+ if (ops && ops->create)
+ ops->create(list);
+}
+
+/* GObject finalize function */
+static void
+purple_roomlist_finalize(GObject *object)
+{
+ PurpleRoomlist *list = PURPLE_ROOMLIST(object);
+ PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
+ GList *l;
+
+ purple_debug_misc("roomlist", "destroying list %p\n", list);
+
+ if (ops && ops->destroy)
+ ops->destroy(list);
+
+ for (l = priv->rooms; l; l = l->next) {
+ PurpleRoomlistRoom *r = l->data;
+ purple_roomlist_room_destroy(list, r);
+ }
+ g_list_free(priv->rooms);
+
+ g_list_foreach(priv->fields, (GFunc)purple_roomlist_field_free, NULL);
+ g_list_free(priv->fields);
+
+ parent_class->finalize(object);
+}
+
+/* Class initializer function */
+static void
+purple_roomlist_class_init(PurpleRoomlistClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ obj_class->finalize = purple_roomlist_finalize;
+ obj_class->constructed = purple_roomlist_constructed;
+
+ /* Setup properties */
+ obj_class->get_property = purple_roomlist_get_property;
+ obj_class->set_property = purple_roomlist_set_property;
+
+ g_type_class_add_private(klass, sizeof(PurpleRoomlistPrivate));
+
+ properties[PROP_ACCOUNT] = g_param_spec_object("account", "Account",
+ "The account for the room list.",
+ PURPLE_TYPE_ACCOUNT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_FIELDS] = g_param_spec_pointer("fields", "Fields",
+ "The list of fields for a roomlist.",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_IN_PROGRESS] = g_param_spec_boolean("in-progress",
+ "In progress",
+ "Whether the room list is being fetched.", FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, PROP_LAST, properties);
+}
+
+GType
+purple_roomlist_get_type(void)
+{
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleRoomlistClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_roomlist_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleRoomlist),
+ 0,
+ NULL,
+ NULL,
+ };
+
+ type = g_type_register_static(G_TYPE_OBJECT, "PurpleRoomlist",
+ &info, 0);
+ }
+
+ return type;
}
-/*@}*/
+PurpleRoomlist *purple_roomlist_new(PurpleAccount *account)
+{
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+
+ return g_object_new(PURPLE_TYPE_ROOMLIST,
+ "account", account,
+ NULL
+ );
+}
/**************************************************************************/
-/** @name Room API */
+/* Room API */
/**************************************************************************/
-/*@{*/
PurpleRoomlistRoom *purple_roomlist_room_new(PurpleRoomlistRoomType type, const gchar *name,
PurpleRoomlistRoom *parent)
@@ -258,11 +440,12 @@ PurpleRoomlistRoom *purple_roomlist_room_new(PurpleRoomlistRoomType type, const
void purple_roomlist_room_add_field(PurpleRoomlist *list, PurpleRoomlistRoom *room, gconstpointer field)
{
+ PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
PurpleRoomlistField *f;
- g_return_if_fail(list != NULL);
+ g_return_if_fail(priv != NULL);
g_return_if_fail(room != NULL);
- g_return_if_fail(list->fields != NULL);
+ g_return_if_fail(priv->fields != NULL);
/* If this is the first call for this room, grab the first field in
* the Roomlist's fields. Otherwise, grab the field that is one
@@ -270,9 +453,9 @@ void purple_roomlist_room_add_field(PurpleRoomlist *list, PurpleRoomlistRoom *ro
* (This works because g_list_nth_data() is zero-indexed and
* g_list_length() is one-indexed.) */
if (!room->fields)
- f = list->fields->data;
+ f = priv->fields->data;
else
- f = g_list_nth_data(list->fields, g_list_length(room->fields));
+ f = g_list_nth_data(priv->fields, g_list_length(room->fields));
g_return_if_fail(f != NULL);
@@ -285,36 +468,39 @@ void purple_roomlist_room_add_field(PurpleRoomlist *list, PurpleRoomlistRoom *ro
room->fields = g_list_append(room->fields, GINT_TO_POINTER(field));
break;
}
+
+ g_object_notify_by_pspec(G_OBJECT(list), properties[PROP_FIELDS]);
}
void purple_roomlist_room_join(PurpleRoomlist *list, PurpleRoomlistRoom *room)
{
+ PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
GHashTable *components;
GList *l, *j;
PurpleConnection *gc;
- g_return_if_fail(list != NULL);
+ g_return_if_fail(priv != NULL);
g_return_if_fail(room != NULL);
- gc = purple_account_get_connection(list->account);
+ gc = purple_account_get_connection(priv->account);
if (!gc)
return;
components = g_hash_table_new(g_str_hash, g_str_equal);
g_hash_table_replace(components, "name", room->name);
- for (l = list->fields, j = room->fields; l && j; l = l->next, j = j->next) {
+ for (l = priv->fields, j = room->fields; l && j; l = l->next, j = j->next) {
PurpleRoomlistField *f = l->data;
g_hash_table_replace(components, f->name, j->data);
}
- serv_join_chat(gc, components);
+ purple_serv_join_chat(gc, components);
g_hash_table_destroy(components);
}
-PurpleRoomlistRoomType purple_roomlist_room_get_type(PurpleRoomlistRoom *room)
+PurpleRoomlistRoomType purple_roomlist_room_get_room_type(PurpleRoomlistRoom *room)
{
return room->type;
}
@@ -329,17 +515,77 @@ PurpleRoomlistRoom * purple_roomlist_room_get_parent(PurpleRoomlistRoom *room)
return room->parent;
}
-GList * purple_roomlist_room_get_fields(PurpleRoomlistRoom *room)
+gboolean purple_roomlist_room_get_expanded_once(PurpleRoomlistRoom *room)
+{
+ g_return_val_if_fail(room != NULL, FALSE);
+
+ return room->expanded_once;
+}
+
+void purple_roomlist_room_set_expanded_once(PurpleRoomlistRoom *room, gboolean expanded_once)
+{
+ g_return_if_fail(room != NULL);
+
+ room->expanded_once = expanded_once;
+}
+
+GList *purple_roomlist_room_get_fields(PurpleRoomlistRoom *room)
{
return room->fields;
}
-/*@}*/
+static void purple_roomlist_room_destroy(PurpleRoomlist *list, PurpleRoomlistRoom *r)
+{
+ PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
+ GList *l, *j;
+
+ for (l = priv->fields, j = r->fields; l && j; l = l->next, j = j->next) {
+ PurpleRoomlistField *f = l->data;
+ if (f->type == PURPLE_ROOMLIST_FIELD_STRING)
+ g_free(j->data);
+ }
+
+ g_list_free(r->fields);
+ g_free(r->name);
+ g_free(r);
+}
+
+/**************************************************************************/
+/* Room GBoxed code */
+/**************************************************************************/
+
+static PurpleRoomlistRoom *purple_roomlist_room_copy(PurpleRoomlistRoom *r)
+{
+ g_return_val_if_fail(r != NULL, NULL);
+
+ return purple_roomlist_room_new(r->type, r->name, r->parent);
+}
+
+static void purple_roomlist_room_free(PurpleRoomlistRoom *r)
+{
+ g_return_if_fail(r != NULL);
+
+ g_list_free(r->fields);
+ g_free(r->name);
+ g_free(r);
+}
+
+GType purple_roomlist_room_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleRoomlistRoom",
+ (GBoxedCopyFunc)purple_roomlist_room_copy,
+ (GBoxedFreeFunc)purple_roomlist_room_free);
+ }
+
+ return type;
+}
/**************************************************************************/
-/** @name Room Field API */
+/* Room Field API */
/**************************************************************************/
-/*@{*/
PurpleRoomlistField *purple_roomlist_field_new(PurpleRoomlistFieldType type,
const gchar *label, const gchar *name,
@@ -360,7 +606,7 @@ PurpleRoomlistField *purple_roomlist_field_new(PurpleRoomlistFieldType type,
return f;
}
-PurpleRoomlistFieldType purple_roomlist_field_get_type(PurpleRoomlistField *field)
+PurpleRoomlistFieldType purple_roomlist_field_get_field_type(PurpleRoomlistField *field)
{
return field->type;
}
@@ -375,13 +621,42 @@ gboolean purple_roomlist_field_get_hidden(PurpleRoomlistField *field)
return field->hidden;
}
-/*@}*/
-
/**************************************************************************/
-/** @name UI Registration Functions */
+/* Room Field GBoxed code */
/**************************************************************************/
-/*@{*/
+static PurpleRoomlistField *purple_roomlist_field_copy(PurpleRoomlistField *f)
+{
+ g_return_val_if_fail(f != NULL, NULL);
+
+ return purple_roomlist_field_new(f->type, f->label, f->name, f->hidden);
+}
+
+static void purple_roomlist_field_free(PurpleRoomlistField *f)
+{
+ g_return_if_fail(f != NULL);
+
+ g_free(f->label);
+ g_free(f->name);
+ g_free(f);
+}
+
+GType purple_roomlist_field_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleRoomlistField",
+ (GBoxedCopyFunc)purple_roomlist_field_copy,
+ (GBoxedFreeFunc)purple_roomlist_field_free);
+ }
+
+ return type;
+}
+
+/**************************************************************************/
+/* UI Registration Functions */
+/**************************************************************************/
void purple_roomlist_set_ui_ops(PurpleRoomlistUiOps *ui_ops)
{
@@ -393,4 +668,33 @@ PurpleRoomlistUiOps *purple_roomlist_get_ui_ops(void)
return ops;
}
-/*@}*/
+/**************************************************************************
+ * UI Ops GBoxed code
+ **************************************************************************/
+
+static PurpleRoomlistUiOps *
+purple_roomlist_ui_ops_copy(PurpleRoomlistUiOps *ops)
+{
+ PurpleRoomlistUiOps *ops_new;
+
+ g_return_val_if_fail(ops != NULL, NULL);
+
+ ops_new = g_new(PurpleRoomlistUiOps, 1);
+ *ops_new = *ops;
+
+ return ops_new;
+}
+
+GType
+purple_roomlist_ui_ops_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleRoomlistUiOps",
+ (GBoxedCopyFunc)purple_roomlist_ui_ops_copy,
+ (GBoxedFreeFunc)g_free);
+ }
+
+ return type;
+}
diff --git a/libpurple/roomlist.h b/libpurple/roomlist.h
index de9270f002..db414b1d57 100644
--- a/libpurple/roomlist.h
+++ b/libpurple/roomlist.h
@@ -1,8 +1,3 @@
-/**
- * @file roomlist.h Room List API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -26,33 +21,64 @@
#ifndef _PURPLE_ROOMLIST_H_
#define _PURPLE_ROOMLIST_H_
+/**
+ * SECTION:roomlist
+ * @section_id: libpurple-roomlist
+ * @short_description: <filename>roomlist.h</filename>
+ * @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))
typedef struct _PurpleRoomlist PurpleRoomlist;
+typedef struct _PurpleRoomlistClass PurpleRoomlistClass;
+
+#define PURPLE_TYPE_ROOMLIST_ROOM (purple_roomlist_room_get_type())
+
typedef struct _PurpleRoomlistRoom PurpleRoomlistRoom;
+
+#define PURPLE_TYPE_ROOMLIST_FIELD (purple_roomlist_field_get_type())
+
typedef struct _PurpleRoomlistField PurpleRoomlistField;
-/** @copydoc _PurpleRoomlistUiOps */
+
+#define PURPLE_TYPE_ROOMLIST_UI_OPS (purple_roomlist_ui_ops_get_type())
+
typedef struct _PurpleRoomlistUiOps PurpleRoomlistUiOps;
/**
+ * PurpleRoomlistRoomType:
+ * @PURPLE_ROOMLIST_ROOMTYPE_CATEGORY: It's a category, but not a room you can
+ * join.
+ * @PURPLE_ROOMLIST_ROOMTYPE_ROOM: It's a room, like the kind you can join.
+ *
* The types of rooms.
*
* These are ORable flags.
*/
typedef enum
{
- PURPLE_ROOMLIST_ROOMTYPE_CATEGORY = 0x01, /**< It's a category, but not a room you can join. */
- PURPLE_ROOMLIST_ROOMTYPE_ROOM = 0x02 /**< It's a room, like the kind you can join. */
+ PURPLE_ROOMLIST_ROOMTYPE_CATEGORY = 0x01,
+ PURPLE_ROOMLIST_ROOMTYPE_ROOM = 0x02
} PurpleRoomlistRoomType;
/**
+ * PurpleRoomlistFieldType:
+ * @PURPLE_ROOMLIST_FIELD_STRING: We do a g_strdup on the passed value if it's
+ * this type.
+ *
* The types of fields.
*/
typedef enum
{
PURPLE_ROOMLIST_FIELD_BOOL,
PURPLE_ROOMLIST_FIELD_INT,
- PURPLE_ROOMLIST_FIELD_STRING /**< We do a g_strdup on the passed value if it's this type. */
+ PURPLE_ROOMLIST_FIELD_STRING
} PurpleRoomlistFieldType;
@@ -64,274 +90,364 @@ typedef enum
/**************************************************************************/
/**
- * Represents a list of rooms for a given connection on a given protocol.
+ * PurpleRoomlistUiOps:
+ * @show_with_account: Force the ui to pop up a dialog and get the list.
+ * @create: A new list was created.
+ * @set_fields: Sets the columns.
+ * @add_room: Add a room to the list.
+ * @in_progress: Are we fetching stuff still?
+ * @destroy: We're destroying list.
+ *
+ * The room list ops to be filled out by the UI.
*/
-struct _PurpleRoomlist {
- PurpleAccount *account; /**< The account this list belongs to. */
- GList *fields; /**< The fields. */
- GList *rooms; /**< The list of rooms. */
- gboolean in_progress; /**< The listing is in progress. */
- gpointer ui_data; /**< UI private data. */
- gpointer proto_data; /** Prpl private data. */
- guint ref; /**< The reference count. */
+struct _PurpleRoomlistUiOps {
+ void (*show_with_account)(PurpleAccount *account);
+ void (*create)(PurpleRoomlist *list);
+ void (*set_fields)(PurpleRoomlist *list, GList *fields);
+ void (*add_room)(PurpleRoomlist *list, PurpleRoomlistRoom *room);
+ void (*in_progress)(PurpleRoomlist *list, gboolean flag);
+ void (*destroy)(PurpleRoomlist *list);
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
};
/**
- * Represents a room.
+ * PurpleRoomlist:
+ * @ui_data: The UI data associated with this room list. This is a convenience
+ * field provided to the UIs -- it is not used by the libpurple core.
+ *
+ * Represents a list of rooms for a given connection on a given protocol.
*/
-struct _PurpleRoomlistRoom {
- PurpleRoomlistRoomType type; /**< The type of room. */
- gchar *name; /**< The name of the room. */
- GList *fields; /**< Other fields. */
- PurpleRoomlistRoom *parent; /**< The parent room, or NULL. */
- gboolean expanded_once; /**< A flag the UI uses to avoid multiple expand prpl cbs. */
-};
+struct _PurpleRoomlist {
+ GObject gparent;
-/**
- * A field a room might have.
- */
-struct _PurpleRoomlistField {
- PurpleRoomlistFieldType type; /**< The type of field. */
- gchar *label; /**< The i18n user displayed name of the field. */
- gchar *name; /**< The internal name of the field. */
- gboolean hidden; /**< Hidden? */
+ /*< public >*/
+ gpointer ui_data;
};
/**
- * The room list ops to be filled out by the UI.
+ * PurpleRoomlistClass:
+ *
+ * Base class for all #PurpleRoomlist's
*/
-struct _PurpleRoomlistUiOps {
- void (*show_with_account)(PurpleAccount *account); /**< Force the ui to pop up a dialog and get the list */
- void (*create)(PurpleRoomlist *list); /**< A new list was created. */
- void (*set_fields)(PurpleRoomlist *list, GList *fields); /**< Sets the columns. */
- void (*add_room)(PurpleRoomlist *list, PurpleRoomlistRoom *room); /**< Add a room to the list. */
- void (*in_progress)(PurpleRoomlist *list, gboolean flag); /**< Are we fetching stuff still? */
- void (*destroy)(PurpleRoomlist *list); /**< We're destroying list. */
+struct _PurpleRoomlistClass {
+ GObjectClass parent_class;
+ /*< private >*/
void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
void (*_purple_reserved4)(void);
};
-
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name Room List API */
+/* Room List API */
/**************************************************************************/
-/*@{*/
/**
+ * purple_roomlist_get_type:
+ *
+ * Returns: The #GType for the Room List object.
+ */
+GType purple_roomlist_get_type(void);
+
+/**
+ * purple_roomlist_show_with_account:
+ * @account: The account to get the list on.
+ *
* This is used to get the room list on an account, asking the UI
* to pop up a dialog with the specified account already selected,
* and pretend the user clicked the get list button.
* While we're pretending, predend I didn't say anything about dialogs
* or buttons, since this is the core.
- *
- * @param account The account to get the list on.
*/
void purple_roomlist_show_with_account(PurpleAccount *account);
/**
- * Returns a newly created room list object.
+ * purple_roomlist_new:
+ * @account: The account that's listing rooms.
*
- * It has an initial reference count of 1.
+ * Returns a newly created room list object.
*
- * @param account The account that's listing rooms.
- * @return The new room list handle.
+ * Returns: The new room list handle.
*/
PurpleRoomlist *purple_roomlist_new(PurpleAccount *account);
/**
- * Increases the reference count on the room list.
+ * purple_roomlist_get_account:
*
- * @param list The object to ref.
- */
-void purple_roomlist_ref(PurpleRoomlist *list);
-
-/**
- * Decreases the reference count on the room list.
- *
- * The room list will be destroyed when this reaches 0.
+ * Retrieve the PurpleAccount that was given when the room list was
+ * created.
*
- * @param list The room list object to unref and possibly
- * destroy.
+ * Returns: The PurpleAccount tied to this room list.
*/
-void purple_roomlist_unref(PurpleRoomlist *list);
+PurpleAccount *purple_roomlist_get_account(PurpleRoomlist *list);
/**
+ * purple_roomlist_set_fields:
+ * @list: The room list.
+ * @fields: A GList of PurpleRoomlistField's. UI's are encouraged
+ * to default to displaying them in the order given.
+ *
* Set the different field types and their names for this protocol.
*
* This must be called before purple_roomlist_room_add().
- *
- * @param list The room list.
- * @param fields A GList of PurpleRoomlistField's. UI's are encouraged
- * to default to displaying them in the order given.
*/
void purple_roomlist_set_fields(PurpleRoomlist *list, GList *fields);
/**
+ * purple_roomlist_set_in_progress:
+ * @list: The room list.
+ * @in_progress: We're downloading it, or we're not.
+ *
* Set the "in progress" state of the room list.
*
* The UI is encouraged to somehow hint to the user
* whether or not we're busy downloading a room list or not.
- *
- * @param list The room list.
- * @param in_progress We're downloading it, or we're not.
*/
void purple_roomlist_set_in_progress(PurpleRoomlist *list, gboolean in_progress);
/**
+ * purple_roomlist_get_in_progress:
+ * @list: The room list.
+ *
* Gets the "in progress" state of the room list.
*
* The UI is encouraged to somehow hint to the user
* whether or not we're busy downloading a room list or not.
*
- * @param list The room list.
- * @return True if we're downloading it, or false if we're not.
+ * Returns: True if we're downloading it, or false if we're not.
*/
gboolean purple_roomlist_get_in_progress(PurpleRoomlist *list);
/**
- * Adds a room to the list of them.
- *
- * @param list The room list.
- * @param room The room to add to the list. The GList of fields must be in the same
+ * purple_roomlist_room_add:
+ * @list: The room list.
+ * @room: The room to add to the list. The GList of fields must be in the same
order as was given in purple_roomlist_set_fields().
+ *
+ * Adds a room to the list of them.
*/
void purple_roomlist_room_add(PurpleRoomlist *list, PurpleRoomlistRoom *room);
/**
- * Returns a PurpleRoomlist structure from the prpl, and
- * instructs the prpl to start fetching the list.
+ * purple_roomlist_get_list:
+ * @gc: The PurpleConnection to have get a list.
*
- * @param gc The PurpleConnection to have get a list.
+ * Returns a PurpleRoomlist structure from the protocol, and
+ * instructs the protocol to start fetching the list.
*
- * @return A PurpleRoomlist* or @c NULL if the protocol
+ * Returns: A PurpleRoomlist* or %NULL if the protocol
* doesn't support that.
*/
PurpleRoomlist *purple_roomlist_get_list(PurpleConnection *gc);
/**
- * Tells the prpl to stop fetching the list.
- * If this is possible and done, the prpl will
- * call set_in_progress with @c FALSE and possibly
- * unref the list if it took a reference.
+ * purple_roomlist_cancel_get_list:
+ * @list: The room list to cancel a get_list on.
*
- * @param list The room list to cancel a get_list on.
+ * Tells the protocol to stop fetching the list.
+ * If this is possible and done, the protocol will
+ * call set_in_progress with %FALSE and possibly
+ * unref the list if it took a reference.
*/
void purple_roomlist_cancel_get_list(PurpleRoomlist *list);
/**
- * Tells the prpl that a category was expanded.
+ * purple_roomlist_expand_category:
+ * @list: The room list.
+ * @category: The category that was expanded. The expression
+ * (category->type & PURPLE_ROOMLIST_ROOMTYPE_CATEGORY)
+ * must be true.
+ *
+ * Tells the protocol that a category was expanded.
*
* On some protocols, the rooms in the category
* won't be fetched until this is called.
- *
- * @param list The room list.
- * @param category The category that was expanded. The expression
- * (category->type & PURPLE_ROOMLIST_ROOMTYPE_CATEGORY)
- * must be true.
*/
void purple_roomlist_expand_category(PurpleRoomlist *list, PurpleRoomlistRoom *category);
/**
+ * purple_roomlist_get_fields:
+ * @roomlist: The roomlist, which must not be %NULL.
+ *
* Get the list of fields for a roomlist.
*
- * @param roomlist The roomlist, which must not be @c NULL.
- * @constreturn A list of fields
- * @since 2.4.0
+ * Returns: (transfer none): A list of fields
*/
-GList * purple_roomlist_get_fields(PurpleRoomlist *roomlist);
+GList *purple_roomlist_get_fields(PurpleRoomlist *roomlist);
-/*@}*/
+/**
+ * purple_roomlist_get_protocol_data:
+ * @list: The roomlist, which must not be %NULL.
+ *
+ * Get the protocol data associated with this room list.
+ *
+ * Returns: The protocol data associated with this room list. This is a
+ * convenience field provided to the protocol -- it is not
+ * used the libpurple core.
+ */
+gpointer purple_roomlist_get_protocol_data(PurpleRoomlist *list);
+
+/**
+ * purple_roomlist_set_protocol_data:
+ * @list: The roomlist, which must not be %NULL.
+ * @proto_data: A pointer to associate with this room list.
+ *
+ * Set the protocol data associated with this room list.
+ */
+void purple_roomlist_set_protocol_data(PurpleRoomlist *list, gpointer proto_data);
+
+/**
+ * purple_roomlist_get_ui_data:
+ * @list: The roomlist, which must not be %NULL.
+ *
+ * Get the UI data associated with this room list.
+ *
+ * Returns: The UI data associated with this room list. This is a
+ * convenience field provided to the UIs--it is not
+ * used by the libpurple core.
+ */
+gpointer purple_roomlist_get_ui_data(PurpleRoomlist *list);
+
+/**
+ * purple_roomlist_set_ui_data:
+ * @list: The roomlist, which must not be %NULL.
+ * @ui_data: A pointer to associate with this room list.
+ *
+ * Set the UI data associated with this room list.
+ */
+void purple_roomlist_set_ui_data(PurpleRoomlist *list, gpointer ui_data);
/**************************************************************************/
-/** @name Room API */
+/* Room API */
/**************************************************************************/
-/*@{*/
/**
- * Creates a new room, to be added to the list.
+ * purple_roomlist_room_get_type:
+ *
+ * Returns: The #GType for the #PurpleRoomlistRoom boxed structure.
+ */
+GType purple_roomlist_room_get_type(void);
+
+/**
+ * purple_roomlist_room_new:
+ * @type: The type of room.
+ * @name: The name of the room.
+ * @parent: The room's parent, if any.
*
- * @param type The type of room.
- * @param name The name of the room.
- * @param parent The room's parent, if any.
+ * Creates a new room, to be added to the list.
*
- * @return A new room.
+ * Returns: A new room.
*/
PurpleRoomlistRoom *purple_roomlist_room_new(PurpleRoomlistRoomType type, const gchar *name,
PurpleRoomlistRoom *parent);
/**
- * Adds a field to a room.
+ * purple_roomlist_room_add_field:
+ * @list: The room list the room belongs to.
+ * @room: The room.
+ * @field: The field to append. Strings get g_strdup'd internally.
*
- * @param list The room list the room belongs to.
- * @param room The room.
- * @param field The field to append. Strings get g_strdup'd internally.
+ * Adds a field to a room.
*/
void purple_roomlist_room_add_field(PurpleRoomlist *list, PurpleRoomlistRoom *room, gconstpointer field);
/**
- * Join a room, given a PurpleRoomlistRoom and it's associated PurpleRoomlist.
+ * purple_roomlist_room_join:
+ * @list: The room list the room belongs to.
+ * @room: The room to join.
*
- * @param list The room list the room belongs to.
- * @param room The room to join.
+ * Join a room, given a PurpleRoomlistRoom and it's associated PurpleRoomlist.
*/
void purple_roomlist_room_join(PurpleRoomlist *list, PurpleRoomlistRoom *room);
/**
+ * purple_roomlist_room_get_room_type:
+ * @room: The room, which must not be %NULL.
+ *
* Get the type of a room.
- * @param room The room, which must not be @c NULL.
- * @return The type of the room.
- * @since 2.4.0
+ *
+ * Returns: The type of the room.
*/
-PurpleRoomlistRoomType purple_roomlist_room_get_type(PurpleRoomlistRoom *room);
+PurpleRoomlistRoomType purple_roomlist_room_get_room_type(PurpleRoomlistRoom *room);
/**
+ * purple_roomlist_room_get_name:
+ * @room: The room, which must not be %NULL.
+ *
* Get the name of a room.
- * @param room The room, which must not be @c NULL.
- * @return The name of the room.
- * @since 2.4.0
+ *
+ * Returns: The name of the room.
*/
const char * purple_roomlist_room_get_name(PurpleRoomlistRoom *room);
/**
+ * purple_roomlist_room_get_parent:
+ * @room: The room, which must not be %NULL.
+ *
* Get the parent of a room.
- * @param room The room, which must not be @c NULL.
- * @return The parent of the room, which can be @c NULL.
- * @since 2.4.0
+ *
+ * Returns: The parent of the room, which can be %NULL.
*/
PurpleRoomlistRoom * purple_roomlist_room_get_parent(PurpleRoomlistRoom *room);
/**
+ * purple_roomlist_room_get_expanded_once:
+ * @room: The room, which must not be %NULL.
+ *
+ * Get the value of the expanded_once flag.
+ *
+ * Returns: The value of the expanded_once flag.
+ */
+gboolean purple_roomlist_room_get_expanded_once(PurpleRoomlistRoom *room);
+
+/**
+ * purple_roomlist_room_set_expanded_once:
+ * @room: The room, which must not be %NULL.
+ * @expanded_once: The new value of the expanded_once flag.
+ *
+ * Set the expanded_once flag.
+ */
+void purple_roomlist_room_set_expanded_once(PurpleRoomlistRoom *room, gboolean expanded_once);
+
+/**
+ * purple_roomlist_room_get_fields:
+ * @room: The room, which must not be %NULL.
+ *
* Get the list of fields for a room.
*
- * @param room The room, which must not be @c NULL.
- * @constreturn A list of fields
- * @since 2.4.0
+ * Returns: (transfer none): A list of fields
*/
GList * purple_roomlist_room_get_fields(PurpleRoomlistRoom *room);
-/*@}*/
-
/**************************************************************************/
-/** @name Room Field API */
+/* Room Field API */
/**************************************************************************/
-/*@{*/
/**
- * Creates a new field.
+ * purple_roomlist_field_get_type:
+ *
+ * Returns: The #GType for the #PurpleRoomlistField boxed structure.
+ */
+GType purple_roomlist_field_get_type(void);
+
+/**
+ * purple_roomlist_field_new:
+ * @type: The type of the field.
+ * @label: The i18n'ed, user displayable name.
+ * @name: The internal name of the field.
+ * @hidden: Hide the field.
*
- * @param type The type of the field.
- * @param label The i18n'ed, user displayable name.
- * @param name The internal name of the field.
- * @param hidden Hide the field.
+ * Creates a new field.
*
- * @return A new PurpleRoomlistField, ready to be added to a GList and passed to
+ * Returns: A new PurpleRoomlistField, ready to be added to a GList and passed to
* purple_roomlist_set_fields().
*/
PurpleRoomlistField *purple_roomlist_field_new(PurpleRoomlistFieldType type,
@@ -339,60 +455,64 @@ PurpleRoomlistField *purple_roomlist_field_new(PurpleRoomlistFieldType type,
gboolean hidden);
/**
- * Get the type of a field.
+ * purple_roomlist_field_get_field_type:
+ * @field: A PurpleRoomlistField, which must not be %NULL.
*
- * @param field A PurpleRoomlistField, which must not be @c NULL.
+ * Get the type of a field.
*
- * @return The type of the field.
- * @since 2.4.0
+ * Returns: The type of the field.
*/
-PurpleRoomlistFieldType purple_roomlist_field_get_type(PurpleRoomlistField *field);
+PurpleRoomlistFieldType purple_roomlist_field_get_field_type(PurpleRoomlistField *field);
/**
- * Get the label of a field.
+ * purple_roomlist_field_get_label:
+ * @field: A PurpleRoomlistField, which must not be %NULL.
*
- * @param field A PurpleRoomlistField, which must not be @c NULL.
+ * Get the label of a field.
*
- * @return The label of the field.
- * @since 2.4.0
+ * Returns: The label of the field.
*/
const char * purple_roomlist_field_get_label(PurpleRoomlistField *field);
/**
+ * purple_roomlist_field_get_hidden:
+ * @field: A PurpleRoomlistField, which must not be %NULL.
+ *
* Check whether a roomlist-field is hidden.
- * @param field A PurpleRoomlistField, which must not be @c NULL.
*
- * @return @c TRUE if the field is hidden, @c FALSE otherwise.
- * @since 2.4.0
+ * Returns: %TRUE if the field is hidden, %FALSE otherwise.
*/
gboolean purple_roomlist_field_get_hidden(PurpleRoomlistField *field);
-/*@}*/
-
/**************************************************************************/
-/** @name UI Registration Functions */
+/* UI Registration Functions */
/**************************************************************************/
-/*@{*/
/**
- * Sets the UI operations structure to be used in all purple room lists.
+ * purple_roomlist_ui_ops_get_type:
+ *
+ * Returns: The #GType for the #PurpleRoomlistUiOps boxed structure.
+ */
+GType purple_roomlist_ui_ops_get_type(void);
+
+/**
+ * purple_roomlist_set_ui_ops:
+ * @ops: The UI operations structure.
*
- * @param ops The UI operations structure.
+ * Sets the UI operations structure to be used in all purple room lists.
*/
void purple_roomlist_set_ui_ops(PurpleRoomlistUiOps *ops);
/**
+ * purple_roomlist_get_ui_ops:
+ *
* Returns the purple window UI operations structure to be used in
* new windows.
*
- * @return A filled-out PurpleRoomlistUiOps structure.
+ * Returns: A filled-out PurpleRoomlistUiOps structure.
*/
PurpleRoomlistUiOps *purple_roomlist_get_ui_ops(void);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_ROOMLIST_H_ */
diff --git a/libpurple/savedstatuses.c b/libpurple/savedstatuses.c
index 70f4190b2a..e5952fae8a 100644
--- a/libpurple/savedstatuses.c
+++ b/libpurple/savedstatuses.c
@@ -1,8 +1,3 @@
-/**
- * @file savedstatuses.c Saved Status API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -35,19 +30,19 @@
#include "util.h"
#include "xmlnode.h"
-/**
+/*
* The maximum number of transient statuses to save. This
* is used during the shutdown process to clean out old
* transient statuses.
*/
#define MAX_TRANSIENTS 5
-/**
+/*
* The default message to use when the user becomes auto-away.
*/
#define DEFAULT_AUTOAWAY_MESSAGE _("I'm not here right now")
-/**
+/*
* The information stores a snap-shot of the statuses of all
* your accounts. Basically these are your saved away messages.
* There is an overall status and message that applies to
@@ -64,14 +59,14 @@ struct _PurpleSavedStatus
PurpleStatusPrimitive type;
char *message;
- /** The timestamp when this saved status was created. This must be unique. */
+ /* The timestamp when this saved status was created. This must be unique. */
time_t creation_time;
time_t lastused;
unsigned int usage_count;
- GList *substatuses; /**< A list of PurpleSavedStatusSub's. */
+ GList *substatuses; /* A list of PurpleSavedStatusSub's. */
};
/*
@@ -156,7 +151,7 @@ set_creation_time(PurpleSavedStatus *status, time_t creation_time)
status);
}
-/**
+/*
* A magic number is calculated for each status, and then the
* statuses are ordered by the magic number. The magic number
* is the date the status was last used offset by one day for
@@ -183,7 +178,7 @@ saved_statuses_sort_func(gconstpointer a, gconstpointer b)
return 0;
}
-/**
+/*
* Transient statuses are added and removed automatically by
* Purple. If they're not used for a certain length of time then
* they'll expire and be automatically removed. This function
@@ -234,42 +229,42 @@ remove_old_transient_statuses(void)
* Writing to disk *
*********************************************************************/
-static xmlnode *
+static PurpleXmlNode *
substatus_to_xmlnode(PurpleSavedStatusSub *substatus)
{
- xmlnode *node, *child;
+ PurpleXmlNode *node, *child;
- node = xmlnode_new("substatus");
+ node = purple_xmlnode_new("substatus");
- child = xmlnode_new_child(node, "account");
- xmlnode_set_attrib(child, "protocol", purple_account_get_protocol_id(substatus->account));
- xmlnode_insert_data(child,
+ child = purple_xmlnode_new_child(node, "account");
+ purple_xmlnode_set_attrib(child, "protocol", purple_account_get_protocol_id(substatus->account));
+ purple_xmlnode_insert_data(child,
purple_normalize(substatus->account,
purple_account_get_username(substatus->account)), -1);
- child = xmlnode_new_child(node, "state");
- xmlnode_insert_data(child, purple_status_type_get_id(substatus->type), -1);
+ child = purple_xmlnode_new_child(node, "state");
+ purple_xmlnode_insert_data(child, purple_status_type_get_id(substatus->type), -1);
if (substatus->message != NULL)
{
- child = xmlnode_new_child(node, "message");
- xmlnode_insert_data(child, substatus->message, -1);
+ child = purple_xmlnode_new_child(node, "message");
+ purple_xmlnode_insert_data(child, substatus->message, -1);
}
return node;
}
-static xmlnode *
+static PurpleXmlNode *
status_to_xmlnode(PurpleSavedStatus *status)
{
- xmlnode *node, *child;
+ PurpleXmlNode *node, *child;
char buf[21];
GList *cur;
- node = xmlnode_new("status");
+ node = purple_xmlnode_new("status");
if (status->title != NULL)
{
- xmlnode_set_attrib(node, "name", status->title);
+ purple_xmlnode_set_attrib(node, "name", status->title);
}
else
{
@@ -281,50 +276,50 @@ status_to_xmlnode(PurpleSavedStatus *status)
* whether the "name" attribute is set to something or if
* it does not exist at all.
*/
- xmlnode_set_attrib(node, "name", "Auto-Cached");
- xmlnode_set_attrib(node, "transient", "true");
+ purple_xmlnode_set_attrib(node, "name", "Auto-Cached");
+ purple_xmlnode_set_attrib(node, "transient", "true");
}
g_snprintf(buf, sizeof(buf), "%lu", status->creation_time);
- xmlnode_set_attrib(node, "created", buf);
+ purple_xmlnode_set_attrib(node, "created", buf);
g_snprintf(buf, sizeof(buf), "%lu", status->lastused);
- xmlnode_set_attrib(node, "lastused", buf);
+ purple_xmlnode_set_attrib(node, "lastused", buf);
g_snprintf(buf, sizeof(buf), "%u", status->usage_count);
- xmlnode_set_attrib(node, "usage_count", buf);
+ purple_xmlnode_set_attrib(node, "usage_count", buf);
- child = xmlnode_new_child(node, "state");
- xmlnode_insert_data(child, purple_primitive_get_id_from_type(status->type), -1);
+ child = purple_xmlnode_new_child(node, "state");
+ purple_xmlnode_insert_data(child, purple_primitive_get_id_from_type(status->type), -1);
if (status->message != NULL)
{
- child = xmlnode_new_child(node, "message");
- xmlnode_insert_data(child, status->message, -1);
+ child = purple_xmlnode_new_child(node, "message");
+ purple_xmlnode_insert_data(child, status->message, -1);
}
for (cur = status->substatuses; cur != NULL; cur = cur->next)
{
child = substatus_to_xmlnode(cur->data);
- xmlnode_insert_child(node, child);
+ purple_xmlnode_insert_child(node, child);
}
return node;
}
-static xmlnode *
+static PurpleXmlNode *
statuses_to_xmlnode(void)
{
- xmlnode *node, *child;
+ PurpleXmlNode *node, *child;
GList *cur;
- node = xmlnode_new("statuses");
- xmlnode_set_attrib(node, "version", "1.0");
+ node = purple_xmlnode_new("statuses");
+ purple_xmlnode_set_attrib(node, "version", "1.0");
for (cur = saved_statuses; cur != NULL; cur = cur->next)
{
child = status_to_xmlnode(cur->data);
- xmlnode_insert_child(node, child);
+ purple_xmlnode_insert_child(node, child);
}
return node;
@@ -333,7 +328,7 @@ statuses_to_xmlnode(void)
static void
sync_statuses(void)
{
- xmlnode *node;
+ PurpleXmlNode *node;
char *data;
if (!statuses_loaded)
@@ -344,10 +339,10 @@ sync_statuses(void)
}
node = statuses_to_xmlnode();
- data = xmlnode_to_formatted_str(node, NULL);
+ data = purple_xmlnode_to_formatted_str(node, NULL);
purple_util_write_data_to_file("status.xml", data, -1);
g_free(data);
- xmlnode_free(node);
+ purple_xmlnode_free(node);
}
static gboolean
@@ -371,23 +366,22 @@ schedule_save(void)
*********************************************************************/
static PurpleSavedStatusSub *
-parse_substatus(xmlnode *substatus)
+parse_substatus(PurpleXmlNode *substatus)
{
PurpleSavedStatusSub *ret;
- xmlnode *node;
+ PurpleXmlNode *node;
char *data;
ret = g_new0(PurpleSavedStatusSub, 1);
/* Read the account */
- node = xmlnode_get_child(substatus, "account");
+ node = purple_xmlnode_get_child(substatus, "account");
if (node != NULL)
{
char *acct_name;
const char *protocol;
- acct_name = xmlnode_get_data(node);
- protocol = xmlnode_get_attrib(node, "protocol");
- protocol = _purple_oscar_convert(acct_name, protocol); /* XXX: Remove */
+ acct_name = purple_xmlnode_get_data(node);
+ protocol = purple_xmlnode_get_attrib(node, "protocol");
if ((acct_name != NULL) && (protocol != NULL))
ret->account = purple_accounts_find(acct_name, protocol);
g_free(acct_name);
@@ -400,11 +394,11 @@ parse_substatus(xmlnode *substatus)
}
/* Read the state */
- node = xmlnode_get_child(substatus, "state");
- if ((node != NULL) && ((data = xmlnode_get_data(node)) != NULL))
+ node = purple_xmlnode_get_child(substatus, "state");
+ if ((node != NULL) && ((data = purple_xmlnode_get_data(node)) != NULL))
{
ret->type = purple_status_type_find_with_id(
- ret->account->status_types, data);
+ purple_account_get_status_types(ret->account), data);
g_free(data);
}
@@ -415,8 +409,8 @@ parse_substatus(xmlnode *substatus)
}
/* Read the message */
- node = xmlnode_get_child(substatus, "message");
- if ((node != NULL) && ((data = xmlnode_get_data(node)) != NULL))
+ node = purple_xmlnode_get_child(substatus, "message");
+ if ((node != NULL) && ((data = purple_xmlnode_get_data(node)) != NULL))
{
ret->message = data;
}
@@ -425,7 +419,7 @@ parse_substatus(xmlnode *substatus)
return ret;
}
-/**
+/*
* Parse a saved status and add it to the saved_statuses linked list.
*
* Here's an example of the XML for a saved status:
@@ -450,21 +444,21 @@ parse_substatus(xmlnode *substatus)
* I know. Moving, huh?
*/
static PurpleSavedStatus *
-parse_status(xmlnode *status)
+parse_status(PurpleXmlNode *status)
{
PurpleSavedStatus *ret;
- xmlnode *node;
+ PurpleXmlNode *node;
const char *attrib;
char *data;
int i;
ret = g_new0(PurpleSavedStatus, 1);
- attrib = xmlnode_get_attrib(status, "transient");
+ attrib = purple_xmlnode_get_attrib(status, "transient");
if (!purple_strequal(attrib, "true"))
{
/* Read the title */
- attrib = xmlnode_get_attrib(status, "name");
+ attrib = purple_xmlnode_get_attrib(status, "name");
ret->title = g_strdup(attrib);
}
@@ -481,35 +475,35 @@ parse_status(xmlnode *status)
}
/* Read the creation time */
- attrib = xmlnode_get_attrib(status, "created");
+ attrib = purple_xmlnode_get_attrib(status, "created");
set_creation_time(ret, (attrib != NULL ? atol(attrib) : 0));
/* Read the last used time */
- attrib = xmlnode_get_attrib(status, "lastused");
+ attrib = purple_xmlnode_get_attrib(status, "lastused");
ret->lastused = (attrib != NULL ? atol(attrib) : 0);
/* Read the usage count */
- attrib = xmlnode_get_attrib(status, "usage_count");
+ attrib = purple_xmlnode_get_attrib(status, "usage_count");
ret->usage_count = (attrib != NULL ? atol(attrib) : 0);
/* Read the primitive status type */
- node = xmlnode_get_child(status, "state");
- if ((node != NULL) && ((data = xmlnode_get_data(node)) != NULL))
+ node = purple_xmlnode_get_child(status, "state");
+ if ((node != NULL) && ((data = purple_xmlnode_get_data(node)) != NULL))
{
ret->type = purple_primitive_get_type_from_id(data);
g_free(data);
}
/* Read the message */
- node = xmlnode_get_child(status, "message");
- if ((node != NULL) && ((data = xmlnode_get_data(node)) != NULL))
+ node = purple_xmlnode_get_child(status, "message");
+ if ((node != NULL) && ((data = purple_xmlnode_get_data(node)) != NULL))
{
ret->message = data;
}
/* Read substatuses */
- for (node = xmlnode_get_child(status, "substatus"); node != NULL;
- node = xmlnode_get_next_twin(node))
+ for (node = purple_xmlnode_get_child(status, "substatus"); node != NULL;
+ node = purple_xmlnode_get_next_twin(node))
{
PurpleSavedStatusSub *new;
new = parse_substatus(node);
@@ -521,16 +515,18 @@ parse_status(xmlnode *status)
return ret;
}
-/**
+/*
+ * load_statuses:
+ *
* Read the saved statuses from a file in the Purple user dir.
*
- * @return TRUE on success, FALSE on failure (if the file can not
- * be opened, or if it contains invalid XML).
+ * Returns: TRUE on success, FALSE on failure (if the file can not
+ * be opened, or if it contains invalid XML).
*/
static void
load_statuses(void)
{
- xmlnode *statuses, *status;
+ PurpleXmlNode *statuses, *status;
statuses_loaded = TRUE;
@@ -539,8 +535,8 @@ load_statuses(void)
if (statuses == NULL)
return;
- for (status = xmlnode_get_child(statuses, "status"); status != NULL;
- status = xmlnode_get_next_twin(status))
+ for (status = purple_xmlnode_get_child(statuses, "status"); status != NULL;
+ status = purple_xmlnode_get_next_twin(status))
{
PurpleSavedStatus *new;
new = parse_status(status);
@@ -548,7 +544,7 @@ load_statuses(void)
}
saved_statuses = g_list_sort(saved_statuses, saved_statuses_sort_func);
- xmlnode_free(statuses);
+ purple_xmlnode_free(statuses);
}
@@ -598,7 +594,7 @@ purple_savedstatus_set_title(PurpleSavedStatus *status, const char *title)
}
void
-purple_savedstatus_set_type(PurpleSavedStatus *status, PurpleStatusPrimitive type)
+purple_savedstatus_set_primitive_type(PurpleSavedStatus *status, PurpleStatusPrimitive type)
{
g_return_if_fail(status != NULL);
@@ -879,7 +875,7 @@ purple_savedstatus_set_idleaway(gboolean idleaway)
if (!idleaway)
purple_idle_touch();
- if (idleaway && (purple_savedstatus_get_type(old) != PURPLE_STATUS_AVAILABLE))
+ if (idleaway && (purple_savedstatus_get_primitive_type(old) != PURPLE_STATUS_AVAILABLE))
/* Our global status is already "away," so don't change anything */
return;
@@ -1009,7 +1005,7 @@ purple_savedstatus_get_title(const PurpleSavedStatus *saved_status)
if ((message == NULL) || (*message == '\0'))
{
PurpleStatusPrimitive primitive;
- primitive = purple_savedstatus_get_type(saved_status);
+ primitive = purple_savedstatus_get_primitive_type(saved_status);
return purple_primitive_get_name_from_type(primitive);
}
else
@@ -1032,7 +1028,7 @@ purple_savedstatus_get_title(const PurpleSavedStatus *saved_status)
}
PurpleStatusPrimitive
-purple_savedstatus_get_type(const PurpleSavedStatus *saved_status)
+purple_savedstatus_get_primitive_type(const PurpleSavedStatus *saved_status)
{
g_return_val_if_fail(saved_status != NULL, PURPLE_STATUS_OFFLINE);
@@ -1084,7 +1080,7 @@ purple_savedstatus_get_substatus(const PurpleSavedStatus *saved_status,
}
const PurpleStatusType *
-purple_savedstatus_substatus_get_type(const PurpleSavedStatusSub *substatus)
+purple_savedstatus_substatus_get_status_type(const PurpleSavedStatusSub *substatus)
{
g_return_val_if_fail(substatus != NULL, NULL);
@@ -1173,6 +1169,33 @@ purple_savedstatus_activate_for_account(const PurpleSavedStatus *saved_status,
}
}
+static PurpleSavedStatus *
+purple_savedstatus_copy(PurpleSavedStatus *savedstatus)
+{
+ PurpleSavedStatus *savedstatus_copy;
+
+ g_return_val_if_fail(savedstatus != NULL, NULL);
+
+ savedstatus_copy = g_new(PurpleSavedStatus, 1);
+ *savedstatus_copy = *savedstatus;
+
+ return savedstatus_copy;
+}
+
+GType
+purple_savedstatus_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleSavedStatus",
+ (GBoxedCopyFunc)purple_savedstatus_copy,
+ (GBoxedFreeFunc)g_free);
+ }
+
+ return type;
+}
+
void *
purple_savedstatuses_get_handle(void)
{
@@ -1205,26 +1228,20 @@ purple_savedstatuses_init(void)
load_statuses();
purple_signal_register(handle, "savedstatus-changed",
- purple_marshal_VOID__POINTER_POINTER, NULL, 2,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_SAVEDSTATUS),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_SAVEDSTATUS));
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ PURPLE_TYPE_SAVEDSTATUS, PURPLE_TYPE_SAVEDSTATUS);
purple_signal_register(handle, "savedstatus-added",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_SAVEDSTATUS));
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_SAVEDSTATUS);
purple_signal_register(handle, "savedstatus-deleted",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_SAVEDSTATUS));
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_SAVEDSTATUS);
purple_signal_register(handle, "savedstatus-modified",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_SAVEDSTATUS));
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_SAVEDSTATUS);
purple_signal_connect(purple_accounts_get_handle(), "account-removed",
handle,
diff --git a/libpurple/savedstatuses.h b/libpurple/savedstatuses.h
index bd5b091343..6482312898 100644
--- a/libpurple/savedstatuses.h
+++ b/libpurple/savedstatuses.h
@@ -1,9 +1,3 @@
-/**
- * @file savedstatuses.h Saved Status API
- * @ingroup core
- * @see @ref savedstatus-signals
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -24,10 +18,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_SAVEDSTATUSES_H_
#define _PURPLE_SAVEDSTATUSES_H_
+/**
+ * SECTION:savedstatuses
+ * @section_id: libpurple-savedstatuses
+ * @short_description: <filename>savedstatuses.h</filename>
+ * @title: Saved Status API
+ * @see_also: <link linkend="chapter-signals-savedstatus">Saved Status signals</link>
+ */
+
+#define PURPLE_TYPE_SAVEDSTATUS (purple_savedstatus_get_type())
/**
+ * PurpleSavedStatus:
+ *
* Saved statuses don't really interact much with the rest of Purple. It
* could really be a plugin. It's just a list of away states. When
* a user chooses one of the saved states, their Purple accounts are set
@@ -48,78 +54,89 @@
* we'll cache it for them in case they want to use it again. If
* they don't use it again, we'll just delete it.
*/
-
/*
* TODO: Hmm. We should probably just be saving PurplePresences. That's
* something we should look into once the status box gets fleshed
* out more.
*/
-
typedef struct _PurpleSavedStatus PurpleSavedStatus;
+
typedef struct _PurpleSavedStatusSub PurpleSavedStatusSub;
#include "status.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name Saved status subsystem */
+/* Saved status subsystem */
/**************************************************************************/
-/*@{*/
/**
- * Create a new saved status. This will add the saved status to the
- * list of saved statuses and writes the revised list to status.xml.
+ * purple_savedstatus_get_type:
*
- * @param title The title of the saved status. This must be
+ * Returns: The #GType for the #PurpleSavedStatus boxed structure.
+ */
+/* TODO Boxing of PurpleSavedStatus is a temporary solution to having a GType
+ * for saved statuses. This should rather be a GObject instead of a GBoxed.
+ */
+GType purple_savedstatus_get_type(void);
+
+/**
+ * purple_savedstatus_new:
+ * @title: The title of the saved status. This must be
* unique. Or, if you want to create a transient
* saved status, then pass in NULL.
- * @param type The type of saved status.
+ * @type: The type of saved status.
+ *
+ * Create a new saved status. This will add the saved status to the
+ * list of saved statuses and writes the revised list to status.xml.
*
- * @return The newly created saved status, or NULL if the title you
+ * Returns: The newly created saved status, or NULL if the title you
* used was already taken.
*/
PurpleSavedStatus *purple_savedstatus_new(const char *title,
PurpleStatusPrimitive type);
/**
- * Set the title for the given saved status.
+ * purple_savedstatus_set_title:
+ * @status: The saved status.
+ * @title: The title of the saved status.
*
- * @param status The saved status.
- * @param title The title of the saved status.
+ * Set the title for the given saved status.
*/
void purple_savedstatus_set_title(PurpleSavedStatus *status,
const char *title);
/**
- * Set the type for the given saved status.
+ * purple_savedstatus_set_primitive_type:
+ * @status: The saved status.
+ * @type: The type of saved status.
*
- * @param status The saved status.
- * @param type The type of saved status.
+ * Set the type for the given saved status.
*/
-void purple_savedstatus_set_type(PurpleSavedStatus *status,
+void purple_savedstatus_set_primitive_type(PurpleSavedStatus *status,
PurpleStatusPrimitive type);
/**
- * Set the message for the given saved status.
- *
- * @param status The saved status.
- * @param message The message, or NULL if you want to unset the
+ * purple_savedstatus_set_message:
+ * @status: The saved status.
+ * @message: The message, or NULL if you want to unset the
* message for this status.
+ *
+ * Set the message for the given saved status.
*/
void purple_savedstatus_set_message(PurpleSavedStatus *status,
const char *message);
/**
- * Set a substatus for an account in a saved status.
- *
- * @param status The saved status.
- * @param account The account.
- * @param type The status type for the account in the staved
+ * purple_savedstatus_set_substatus:
+ * @status: The saved status.
+ * @account: The account.
+ * @type: The status type for the account in the saved
* status.
- * @param message The message for the account in the substatus.
+ * @message: The message for the account in the substatus.
+ *
+ * Set a substatus for an account in a saved status.
*/
void purple_savedstatus_set_substatus(PurpleSavedStatus *status,
const PurpleAccount *account,
@@ -127,56 +144,62 @@ void purple_savedstatus_set_substatus(PurpleSavedStatus *status,
const char *message);
/**
+ * purple_savedstatus_unset_substatus:
+ * @saved_status: The saved status.
+ * @account: The account.
+ *
* Unset a substatus for an account in a saved status. This clears
* the previosly set substatus for the PurpleSavedStatus. If this
* saved status is activated then this account will use the default
* status type and message.
- *
- * @param saved_status The saved status.
- * @param account The account.
-*/
+ */
void purple_savedstatus_unset_substatus(PurpleSavedStatus *saved_status,
const PurpleAccount *account);
/**
+ * purple_savedstatus_delete:
+ * @title: The title of the saved status.
+ *
* Delete a saved status. This removes the saved status from the list
* of saved statuses, and writes the revised list to status.xml.
*
- * @param title The title of the saved status.
- *
- * @return TRUE if the status was successfully deleted. FALSE if the
+ * Returns: TRUE if the status was successfully deleted. FALSE if the
* status could not be deleted because no saved status exists
* with the given title.
*/
gboolean purple_savedstatus_delete(const char *title);
/**
- * Delete a saved status. This removes the saved status from the list
- * of saved statuses, and writes the revised list to status.xml.
- *
- * @param saved_status the status to delete, the pointer is invalid after
+ * purple_savedstatus_delete_by_status:
+ * @saved_status: the status to delete, the pointer is invalid after
* the call
*
+ * Delete a saved status. This removes the saved status from the list
+ * of saved statuses, and writes the revised list to status.xml.
*/
void purple_savedstatus_delete_by_status(PurpleSavedStatus *saved_status);
/**
+ * purple_savedstatuses_get_all:
+ *
* Returns all saved statuses.
*
- * @constreturn A list of saved statuses.
+ * Returns: (transfer none): A list of saved statuses.
*/
GList *purple_savedstatuses_get_all(void);
/**
+ * purple_savedstatuses_get_popular:
+ * @how_many: The maximum number of saved statuses
+ * to return, or '0' to get all saved
+ * statuses sorted by popularity.
+ *
* Returns the n most popular saved statuses. "Popularity" is
* determined by when the last time a saved_status was used and
* how many times it has been used. Transient statuses without
* messages are not included in the list.
*
- * @param how_many The maximum number of saved statuses
- * to return, or '0' to get all saved
- * statuses sorted by popularity.
- * @return A linked list containing at most how_many
+ * Returns: A linked list containing at most how_many
* PurpleSavedStatuses. This list should be
* g_list_free'd by the caller (but the
* PurpleSavedStatuses must not be free'd).
@@ -184,90 +207,107 @@ GList *purple_savedstatuses_get_all(void);
GList *purple_savedstatuses_get_popular(unsigned int how_many);
/**
+ * purple_savedstatus_get_current:
+ *
* Returns the currently selected saved status. If we are idle
* then this returns purple_savedstatus_get_idleaway(). Otherwise
* it returns purple_savedstatus_get_default().
*
- * @return A pointer to the in-use PurpleSavedStatus.
+ * Returns: A pointer to the in-use PurpleSavedStatus.
* This function never returns NULL.
*/
PurpleSavedStatus *purple_savedstatus_get_current(void);
/**
+ * purple_savedstatus_get_default:
+ *
* Returns the default saved status that is used when our
* accounts are not idle-away.
*
- * @return A pointer to the in-use PurpleSavedStatus.
+ * Returns: A pointer to the in-use PurpleSavedStatus.
* This function never returns NULL.
*/
PurpleSavedStatus *purple_savedstatus_get_default(void);
/**
+ * purple_savedstatus_get_idleaway:
+ *
* Returns the saved status that is used when your
* accounts become idle-away.
*
- * @return A pointer to the idle-away PurpleSavedStatus.
+ * Returns: A pointer to the idle-away PurpleSavedStatus.
* This function never returns NULL.
*/
PurpleSavedStatus *purple_savedstatus_get_idleaway(void);
/**
+ * purple_savedstatus_is_idleaway:
+ *
* Return TRUE if we are currently idle-away. Otherwise
* returns FALSE.
*
- * @return TRUE if our accounts have been set to idle-away.
+ * Returns: TRUE if our accounts have been set to idle-away.
*/
gboolean purple_savedstatus_is_idleaway(void);
/**
- * Set whether accounts in Purple are idle-away or not.
- *
- * @param idleaway TRUE if accounts should be switched to use the
+ * purple_savedstatus_set_idleaway:
+ * @idleaway: TRUE if accounts should be switched to use the
* idle-away saved status. FALSE if they should
* be switched to use the default status.
+ *
+ * Set whether accounts in Purple are idle-away or not.
*/
void purple_savedstatus_set_idleaway(gboolean idleaway);
/**
+ * purple_savedstatus_get_startup:
+ *
* Returns the status to be used when purple is starting up
*
- * @return A pointer to the startup PurpleSavedStatus.
+ * Returns: A pointer to the startup PurpleSavedStatus.
* This function never returns NULL.
*/
PurpleSavedStatus *purple_savedstatus_get_startup(void);
/**
- * Finds a saved status with the specified title.
+ * purple_savedstatus_find:
+ * @title: The name of the saved status.
*
- * @param title The name of the saved status.
+ * Finds a saved status with the specified title.
*
- * @return The saved status if found, or NULL.
+ * Returns: The saved status if found, or NULL.
*/
PurpleSavedStatus *purple_savedstatus_find(const char *title);
/**
- * Finds a saved status with the specified creation time.
- *
- * @param creation_time The timestamp when the saved
+ * purple_savedstatus_find_by_creation_time:
+ * @creation_time: The timestamp when the saved
* status was created.
*
- * @return The saved status if found, or NULL.
+ * Finds a saved status with the specified creation time.
+ *
+ * Returns: The saved status if found, or NULL.
*/
PurpleSavedStatus *purple_savedstatus_find_by_creation_time(time_t creation_time);
/**
- * Finds a saved status with the specified primitive and message.
- *
- * @param type The PurpleStatusPrimitive for the status you're trying
+ * purple_savedstatus_find_transient_by_type_and_message:
+ * @type: The PurpleStatusPrimitive for the status you're trying
* to find.
- * @param message The message for the status you're trying
+ * @message: The message for the status you're trying
* to find.
*
- * @return The saved status if found, or NULL.
+ * Finds a saved status with the specified primitive and message.
+ *
+ * Returns: The saved status if found, or NULL.
*/
PurpleSavedStatus *purple_savedstatus_find_transient_by_type_and_message(PurpleStatusPrimitive type, const char *message);
/**
+ * purple_savedstatus_is_transient:
+ * @saved_status: The saved status.
+ *
* Determines if a given saved status is "transient."
* A transient saved status is one that was not
* explicitly added by the user. Transient statuses
@@ -280,18 +320,17 @@ PurpleSavedStatus *purple_savedstatus_find_transient_by_type_and_message(PurpleS
* we need to save this status information is so we can
* restore it when Purple restarts.
*
- * @param saved_status The saved status.
- *
- * @return TRUE if the saved status is transient.
+ * Returns: TRUE if the saved status is transient.
*/
gboolean purple_savedstatus_is_transient(const PurpleSavedStatus *saved_status);
/**
- * Return the name of a given saved status.
+ * purple_savedstatus_get_title:
+ * @saved_status: The saved status.
*
- * @param saved_status The saved status.
+ * Return the name of a given saved status.
*
- * @return The title. This value may be a static buffer which may
+ * Returns: The title. This value may be a static buffer which may
* be overwritten on subsequent calls to this function. If
* you need a reference to the title for prolonged use then
* you should make a copy of it.
@@ -299,20 +338,22 @@ gboolean purple_savedstatus_is_transient(const PurpleSavedStatus *saved_status);
const char *purple_savedstatus_get_title(const PurpleSavedStatus *saved_status);
/**
- * Return the type of a given saved status.
+ * purple_savedstatus_get_primitive_type:
+ * @saved_status: The saved status.
*
- * @param saved_status The saved status.
+ * Return the type of a given saved status.
*
- * @return The name.
+ * Returns: The primitive type.
*/
-PurpleStatusPrimitive purple_savedstatus_get_type(const PurpleSavedStatus *saved_status);
+PurpleStatusPrimitive purple_savedstatus_get_primitive_type(const PurpleSavedStatus *saved_status);
/**
- * Return the default message of a given saved status.
+ * purple_savedstatus_get_message:
+ * @saved_status: The saved status.
*
- * @param saved_status The saved status.
+ * Return the default message of a given saved status.
*
- * @return The message. This will return NULL if the saved
+ * Returns: The message. This will return NULL if the saved
* status does not have a message. This will
* contain the normal markup that is created by
* Purple's IMHTML (basically HTML markup).
@@ -320,6 +361,9 @@ PurpleStatusPrimitive purple_savedstatus_get_type(const PurpleSavedStatus *saved
const char *purple_savedstatus_get_message(const PurpleSavedStatus *saved_status);
/**
+ * purple_savedstatus_get_creation_time:
+ * @saved_status: The saved status.
+ *
* Return the time in seconds-since-the-epoch when this
* saved status was created. Note: For any status created
* by Purple 1.5.0 or older this value will be invalid and
@@ -330,31 +374,31 @@ const char *purple_savedstatus_get_message(const PurpleSavedStatus *saved_status
* However, this value is guaranteed to be a unique
* identifier for the given saved status.
*
- * @param saved_status The saved status.
- *
- * @return The timestamp when this saved status was created.
+ * Returns: The timestamp when this saved status was created.
*/
time_t purple_savedstatus_get_creation_time(const PurpleSavedStatus *saved_status);
/**
+ * purple_savedstatus_has_substatuses:
+ * @saved_status: The saved status.
+ *
* Determine if a given saved status has "substatuses,"
* or if it is a simple status (the same for all
* accounts).
*
- * @param saved_status The saved status.
- *
- * @return TRUE if the saved_status has substatuses.
+ * Returns: TRUE if the saved_status has substatuses.
* FALSE otherwise.
*/
gboolean purple_savedstatus_has_substatuses(const PurpleSavedStatus *saved_status);
/**
- * Get the substatus for an account in a saved status.
+ * purple_savedstatus_get_substatus:
+ * @saved_status: The saved status.
+ * @account: The account.
*
- * @param saved_status The saved status.
- * @param account The account.
+ * Get the substatus for an account in a saved status.
*
- * @return The PurpleSavedStatusSub for the account, or NULL if
+ * Returns: The PurpleSavedStatusSub for the account, or NULL if
* the given account does not have a substatus that
* differs from the default status of this PurpleSavedStatus.
*/
@@ -363,63 +407,71 @@ PurpleSavedStatusSub *purple_savedstatus_get_substatus(
const PurpleAccount *account);
/**
- * Get the status type of a given substatus.
+ * purple_savedstatus_substatus_get_status_type:
+ * @substatus: The substatus.
*
- * @param substatus The substatus.
+ * Get the status type of a given substatus.
*
- * @return The status type.
+ * Returns: The status type.
*/
-const PurpleStatusType *purple_savedstatus_substatus_get_type(const PurpleSavedStatusSub *substatus);
+const PurpleStatusType *purple_savedstatus_substatus_get_status_type(
+ const PurpleSavedStatusSub *substatus);
/**
- * Get the message of a given substatus.
+ * purple_savedstatus_substatus_get_message:
+ * @substatus: The substatus.
*
- * @param substatus The substatus.
+ * Get the message of a given substatus.
*
- * @return The message of the substatus, or NULL if this substatus does
+ * Returns: The message of the substatus, or NULL if this substatus does
* not have a message.
*/
const char *purple_savedstatus_substatus_get_message(const PurpleSavedStatusSub *substatus);
/**
+ * purple_savedstatus_activate:
+ * @saved_status: The status you want to set your accounts to.
+ *
* Sets the statuses for all your accounts to those specified
* by the given saved_status. This function calls
* purple_savedstatus_activate_for_account() for all your accounts.
- *
- * @param saved_status The status you want to set your accounts to.
*/
void purple_savedstatus_activate(PurpleSavedStatus *saved_status);
/**
+ * purple_savedstatus_activate_for_account:
+ * @saved_status: The status you want to set your accounts to.
+ * @account: The account whose statuses you want to change.
+ *
* Sets the statuses for a given account to those specified
* by the given saved_status.
- *
- * @param saved_status The status you want to set your accounts to.
- * @param account The account whose statuses you want to change.
*/
void purple_savedstatus_activate_for_account(const PurpleSavedStatus *saved_status, PurpleAccount *account);
/**
+ * purple_savedstatuses_get_handle:
+ *
* Get the handle for the status subsystem.
*
- * @return the handle to the status subsystem
+ * Returns: the handle to the status subsystem
*/
void *purple_savedstatuses_get_handle(void);
/**
+ * purple_savedstatuses_init:
+ *
* Initializes the status subsystem.
*/
void purple_savedstatuses_init(void);
/**
+ * purple_savedstatuses_uninit:
+ *
* Uninitializes the status subsystem.
*/
void purple_savedstatuses_uninit(void);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_SAVEDSTATUSES_H_ */
+
diff --git a/libpurple/server.c b/libpurple/server.c
index 27a796ffef..660f6c1227 100644
--- a/libpurple/server.c
+++ b/libpurple/server.c
@@ -24,13 +24,12 @@
/* This file is the fullcrap */
#include "internal.h"
-#include "blist.h"
+#include "buddylist.h"
#include "conversation.h"
#include "debug.h"
#include "log.h"
#include "notify.h"
#include "prefs.h"
-#include "privacy.h"
#include "prpl.h"
#include "request.h"
#include "signals.h"
@@ -42,7 +41,7 @@
#define SEX_BEFORE_RESENDING_AUTORESPONSE "Only after you're married"
unsigned int
-serv_send_typing(PurpleConnection *gc, const char *name, PurpleTypingState state)
+purple_serv_send_typing(PurpleConnection *gc, const char *name, PurpleIMTypingState state)
{
PurplePlugin *prpl;
PurplePluginProtocolInfo *prpl_info;
@@ -117,18 +116,19 @@ get_last_auto_response(PurpleConnection *gc, const char *name)
return lar;
}
-int serv_send_im(PurpleConnection *gc, const char *name, const char *message,
- PurpleMessageFlags flags)
+int purple_serv_send_im(PurpleConnection *gc, PurpleMessage *msg)
{
- PurpleConversation *conv = NULL;
+ PurpleIMConversation *im = NULL;
PurpleAccount *account = NULL;
PurplePresence *presence = NULL;
PurplePlugin *prpl = NULL;
PurplePluginProtocolInfo *prpl_info = NULL;
int val = -EINVAL;
const gchar *auto_reply_pref = NULL;
+ const gchar *recipient;
g_return_val_if_fail(gc != NULL, val);
+ g_return_val_if_fail(msg != NULL, val);
prpl = purple_connection_get_prpl(gc);
@@ -138,33 +138,34 @@ int serv_send_im(PurpleConnection *gc, const char *name, const char *message,
account = purple_connection_get_account(gc);
presence = purple_account_get_presence(account);
+ recipient = purple_message_get_recipient(msg);
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, account);
+ im = purple_conversations_find_im_with_account(recipient, account);
if (prpl_info->send_im)
- val = prpl_info->send_im(gc, name, message, flags);
+ val = prpl_info->send_im(gc, msg);
/*
* XXX - If "only auto-reply when away & idle" is set, then shouldn't
* this only reset lar->sent if we're away AND idle?
*/
auto_reply_pref = purple_prefs_get_string("/purple/away/auto_reply");
- if((gc->flags & PURPLE_CONNECTION_AUTO_RESP) &&
+ if((purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_AUTO_RESP) &&
!purple_presence_is_available(presence) &&
!purple_strequal(auto_reply_pref, "never")) {
struct last_auto_response *lar;
- lar = get_last_auto_response(gc, name);
+ lar = get_last_auto_response(gc, recipient);
lar->sent = time(NULL);
}
- if(conv && purple_conv_im_get_send_typed_timeout(PURPLE_CONV_IM(conv)))
- purple_conv_im_stop_send_typed_timeout(PURPLE_CONV_IM(conv));
+ if(im && purple_im_conversation_get_send_typed_timeout(im))
+ purple_im_conversation_stop_send_typed_timeout(im);
return val;
}
-void serv_get_info(PurpleConnection *gc, const char *name)
+void purple_serv_get_info(PurpleConnection *gc, const char *name)
{
PurplePlugin *prpl;
PurplePluginProtocolInfo *prpl_info;
@@ -178,7 +179,7 @@ void serv_get_info(PurpleConnection *gc, const char *name)
}
}
-void serv_set_info(PurpleConnection *gc, const char *info)
+void purple_serv_set_info(PurpleConnection *gc, const char *info)
{
PurplePlugin *prpl;
PurplePluginProtocolInfo *prpl_info;
@@ -191,9 +192,8 @@ void serv_set_info(PurpleConnection *gc, const char *info)
if (prpl_info->set_info) {
account = purple_connection_get_account(gc);
- if (purple_signal_emit_return_1(purple_accounts_get_handle(),
- "account-setting-info", account, info))
- return;
+ purple_signal_emit(purple_accounts_get_handle(),
+ "account-setting-info", account, info);
prpl_info->set_info(gc, info);
@@ -206,7 +206,7 @@ void serv_set_info(PurpleConnection *gc, const char *info)
/*
* Set buddy's alias on server roster/list
*/
-void serv_alias_buddy(PurpleBuddy *b)
+void purple_serv_alias_buddy(PurpleBuddy *b)
{
PurpleAccount *account;
PurpleConnection *gc;
@@ -226,22 +226,22 @@ void serv_alias_buddy(PurpleBuddy *b)
if (prpl_info->alias_buddy)
prpl_info->alias_buddy(gc,
purple_buddy_get_name(b),
- purple_buddy_get_local_buddy_alias(b));
+ purple_buddy_get_local_alias(b));
}
}
}
}
void
-serv_got_alias(PurpleConnection *gc, const char *who, const char *alias)
+purple_serv_got_alias(PurpleConnection *gc, const char *who, const char *alias)
{
PurpleAccount *account;
GSList *buddies;
PurpleBuddy *b;
- PurpleConversation *conv;
+ PurpleIMConversation *im;
account = purple_connection_get_account(gc);
- buddies = purple_find_buddies(account, who);
+ buddies = purple_blist_find_buddies(account, who);
while (buddies != NULL)
{
@@ -255,19 +255,19 @@ serv_got_alias(PurpleConnection *gc, const char *who, const char *alias)
if (purple_strequal(server_alias, alias))
continue;
- purple_blist_server_alias_buddy(b, alias);
+ purple_buddy_set_server_alias(b, alias);
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, purple_buddy_get_name(b), account);
- if (conv != NULL && alias != NULL && !purple_strequal(alias, who))
+ im = purple_conversations_find_im_with_account(purple_buddy_get_name(b), account);
+ if (im != NULL && alias != NULL && !purple_strequal(alias, who))
{
char *escaped = g_markup_escape_text(who, -1);
char *escaped2 = g_markup_escape_text(alias, -1);
char *tmp = g_strdup_printf(_("%s is now known as %s.\n"),
escaped, escaped2);
- purple_conversation_write(conv, NULL, tmp,
- PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY,
- time(NULL));
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(im), tmp,
+ PURPLE_MESSAGE_NO_LINKIFY);
g_free(tmp);
g_free(escaped2);
@@ -284,7 +284,7 @@ purple_serv_got_private_alias(PurpleConnection *gc, const char *who, const char
PurpleBuddy *b = NULL;
account = purple_connection_get_account(gc);
- buddies = purple_find_buddies(account, who);
+ buddies = purple_blist_find_buddies(account, who);
while(buddies != NULL) {
const char *balias;
@@ -292,11 +292,11 @@ purple_serv_got_private_alias(PurpleConnection *gc, const char *who, const char
buddies = g_slist_delete_link(buddies, buddies);
- balias = purple_buddy_get_local_buddy_alias(b);
+ balias = purple_buddy_get_local_alias(b);
if (purple_strequal(balias, alias))
continue;
- purple_blist_alias_buddy(b, alias);
+ purple_buddy_set_local_alias(b, alias);
}
}
@@ -325,18 +325,6 @@ PurpleAttentionType *purple_get_attention_type_from_code(PurpleAccount *account,
return attn;
}
-void
-serv_send_attention(PurpleConnection *gc, const char *who, guint type_code)
-{
- purple_prpl_send_attention(gc, who, type_code);
-}
-
-void
-serv_got_attention(PurpleConnection *gc, const char *who, guint type_code)
-{
- purple_prpl_got_attention(gc, who, type_code);
-}
-
/*
* Move a buddy from one group to another on server.
@@ -345,7 +333,7 @@ serv_got_attention(PurpleConnection *gc, const char *who, guint type_code)
* it should be possible. Probably needs to be done, someday. Although,
* the UI for that would be difficult, because groups are Purple-wide.
*/
-void serv_move_buddy(PurpleBuddy *b, PurpleGroup *og, PurpleGroup *ng)
+void purple_serv_move_buddy(PurpleBuddy *b, PurpleGroup *og, PurpleGroup *ng)
{
PurpleAccount *account;
PurpleConnection *gc;
@@ -370,7 +358,7 @@ void serv_move_buddy(PurpleBuddy *b, PurpleGroup *og, PurpleGroup *ng)
}
}
-void serv_add_permit(PurpleConnection *gc, const char *name)
+void purple_serv_add_permit(PurpleConnection *gc, const char *name)
{
PurplePlugin *prpl;
PurplePluginProtocolInfo *prpl_info;
@@ -384,7 +372,7 @@ void serv_add_permit(PurpleConnection *gc, const char *name)
}
}
-void serv_add_deny(PurpleConnection *gc, const char *name)
+void purple_serv_add_deny(PurpleConnection *gc, const char *name)
{
PurplePlugin *prpl;
PurplePluginProtocolInfo *prpl_info;
@@ -398,7 +386,7 @@ void serv_add_deny(PurpleConnection *gc, const char *name)
}
}
-void serv_rem_permit(PurpleConnection *gc, const char *name)
+void purple_serv_rem_permit(PurpleConnection *gc, const char *name)
{
PurplePlugin *prpl;
PurplePluginProtocolInfo *prpl_info;
@@ -412,7 +400,7 @@ void serv_rem_permit(PurpleConnection *gc, const char *name)
}
}
-void serv_rem_deny(PurpleConnection *gc, const char *name)
+void purple_serv_rem_deny(PurpleConnection *gc, const char *name)
{
PurplePlugin *prpl;
PurplePluginProtocolInfo *prpl_info;
@@ -426,7 +414,7 @@ void serv_rem_deny(PurpleConnection *gc, const char *name)
}
}
-void serv_set_permit_deny(PurpleConnection *gc)
+void purple_serv_set_permit_deny(PurpleConnection *gc)
{
PurplePlugin *prpl;
PurplePluginProtocolInfo *prpl_info;
@@ -446,7 +434,7 @@ void serv_set_permit_deny(PurpleConnection *gc)
}
}
-void serv_join_chat(PurpleConnection *gc, GHashTable *data)
+void purple_serv_join_chat(PurpleConnection *gc, GHashTable *data)
{
PurplePlugin *prpl;
PurplePluginProtocolInfo *prpl_info;
@@ -461,7 +449,7 @@ void serv_join_chat(PurpleConnection *gc, GHashTable *data)
}
-void serv_reject_chat(PurpleConnection *gc, GHashTable *data)
+void purple_serv_reject_chat(PurpleConnection *gc, GHashTable *data)
{
PurplePlugin *prpl;
PurplePluginProtocolInfo *prpl_info;
@@ -475,16 +463,16 @@ void serv_reject_chat(PurpleConnection *gc, GHashTable *data)
}
}
-void serv_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name)
+void purple_serv_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name)
{
PurplePlugin *prpl = NULL;
PurplePluginProtocolInfo *prpl_info = NULL;
- PurpleConversation *conv;
+ PurpleChatConversation *chat;
char *buffy = message && *message ? g_strdup(message) : NULL;
- conv = purple_find_chat(gc, id);
+ chat = purple_conversations_find_chat(gc, id);
- if(conv == NULL)
+ if(chat == NULL)
return;
if(gc)
@@ -494,23 +482,23 @@ void serv_chat_invite(PurpleConnection *gc, int id, const char *message, const c
prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
purple_signal_emit(purple_conversations_get_handle(), "chat-inviting-user",
- conv, name, &buffy);
+ chat, name, &buffy);
if (prpl_info && prpl_info->chat_invite)
prpl_info->chat_invite(gc, id, buffy, name);
purple_signal_emit(purple_conversations_get_handle(), "chat-invited-user",
- conv, name, buffy);
+ chat, name, buffy);
g_free(buffy);
}
-/* Ya know, nothing uses this except purple_conversation_destroy(),
+/* Ya know, nothing uses this except purple_chat_conversation_finalize(),
* I think I'll just merge it into that later...
* Then again, something might want to use this, from outside prpl-land
* to leave a chat without destroying the conversation.
*/
-void serv_chat_leave(PurpleConnection *gc, int id)
+void purple_serv_chat_leave(PurpleConnection *gc, int id)
{
PurplePlugin *prpl;
PurplePluginProtocolInfo *prpl_info;
@@ -522,21 +510,7 @@ void serv_chat_leave(PurpleConnection *gc, int id)
prpl_info->chat_leave(gc, id);
}
-void serv_chat_whisper(PurpleConnection *gc, int id, const char *who, const char *message)
-{
- PurplePlugin *prpl;
- PurplePluginProtocolInfo *prpl_info;
-
- if (gc) {
- prpl = purple_connection_get_prpl(gc);
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-
- if (prpl_info->chat_whisper)
- prpl_info->chat_whisper(gc, id, who, message);
- }
-}
-
-int serv_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags)
+int purple_serv_chat_send(PurpleConnection *gc, int id, PurpleMessage *msg)
{
PurplePlugin *prpl;
PurplePluginProtocolInfo *prpl_info;
@@ -544,8 +518,10 @@ int serv_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMess
prpl = purple_connection_get_prpl(gc);
prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+ g_return_val_if_fail(msg != NULL, -EINVAL);
+
if (prpl_info->chat_send)
- return prpl_info->chat_send(gc, id, message, flags);
+ return prpl_info->chat_send(gc, id, msg);
return -EINVAL;
}
@@ -554,14 +530,15 @@ int serv_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMess
* woo. i'm actually going to comment this function. isn't that fun. make
* sure to follow along, kids
*/
-void serv_got_im(PurpleConnection *gc, const char *who, const char *msg,
+void purple_serv_got_im(PurpleConnection *gc, const char *who, const char *msg,
PurpleMessageFlags flags, time_t mtime)
{
PurpleAccount *account;
- PurpleConversation *conv;
+ PurpleIMConversation *im;
char *message, *name;
char *angel, *buffy;
int plugin_return;
+ PurpleMessage *pmsg;
g_return_if_fail(msg != NULL);
@@ -569,7 +546,7 @@ void serv_got_im(PurpleConnection *gc, const char *who, const char *msg,
if (mtime < 0) {
purple_debug_error("server",
- "serv_got_im ignoring negative timestamp\n");
+ "purple_serv_got_im ignoring negative timestamp\n");
/* TODO: Would be more appropriate to use a value that indicates
that the timestamp is unknown, and surface that in the UI. */
mtime = time(NULL);
@@ -580,7 +557,7 @@ void serv_got_im(PurpleConnection *gc, const char *who, const char *msg,
*/
flags |= PURPLE_MESSAGE_RECV;
- if (!purple_privacy_check(account, who)) {
+ if (!purple_account_privacy_check(account, who)) {
purple_signal_emit(purple_conversations_get_handle(), "blocked-im-msg",
account, who, msg, flags, (unsigned int)mtime);
return;
@@ -590,7 +567,7 @@ void serv_got_im(PurpleConnection *gc, const char *who, const char *msg,
* We should update the conversation window buttons and menu,
* if it exists.
*/
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, gc->account);
+ im = purple_conversations_find_im_with_account(who, purple_connection_get_account(gc));
/*
* Make copies of the message and the sender in case plugins want
@@ -601,8 +578,8 @@ void serv_got_im(PurpleConnection *gc, const char *who, const char *msg,
plugin_return = GPOINTER_TO_INT(
purple_signal_emit_return_1(purple_conversations_get_handle(),
- "receiving-im-msg", gc->account,
- &angel, &buffy, conv, &flags));
+ "receiving-im-msg", purple_connection_get_account(gc),
+ &angel, &buffy, im, &flags));
if (!buffy || !angel || plugin_return) {
g_free(buffy);
@@ -613,17 +590,18 @@ void serv_got_im(PurpleConnection *gc, const char *who, const char *msg,
name = angel;
message = buffy;
- purple_signal_emit(purple_conversations_get_handle(), "received-im-msg", gc->account,
- name, message, conv, flags);
+ purple_signal_emit(purple_conversations_get_handle(), "received-im-msg", purple_connection_get_account(gc),
+ name, message, im, flags);
/* search for conversation again in case it was created by received-im-msg handler */
- if (conv == NULL)
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, gc->account);
+ if (im == NULL)
+ im = purple_conversations_find_im_with_account(name, purple_connection_get_account(gc));
- if (conv == NULL)
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, name);
+ if (im == NULL)
+ im = purple_im_conversation_new(account, name);
- purple_conv_im_write(PURPLE_CONV_IM(conv), name, message, flags, mtime);
+ pmsg = purple_message_new_incoming(name, message, flags, mtime);
+ purple_conversation_write_message(PURPLE_CONVERSATION(im), pmsg);
g_free(message);
/*
@@ -635,7 +613,7 @@ void serv_got_im(PurpleConnection *gc, const char *who, const char *msg,
* - or we're not idle and the 'only auto respond if idle' pref
* is set
*/
- if (gc->flags & PURPLE_CONNECTION_AUTO_RESP)
+ if (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_AUTO_RESP)
{
PurplePresence *presence;
PurpleStatus *status;
@@ -649,7 +627,7 @@ void serv_got_im(PurpleConnection *gc, const char *who, const char *msg,
presence = purple_account_get_presence(account);
status = purple_presence_get_active_status(presence);
- status_type = purple_status_get_type(status);
+ status_type = purple_status_get_status_type(status);
primitive = purple_status_type_get_primitive(status_type);
mobile = purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE);
if ((primitive == PURPLE_STATUS_AVAILABLE) ||
@@ -662,7 +640,7 @@ void serv_got_im(PurpleConnection *gc, const char *who, const char *msg,
return;
}
- away_msg = purple_value_get_string(
+ away_msg = g_value_get_string(
purple_status_get_attr_value(status, "message"));
if ((away_msg != NULL) && (*away_msg != '\0')) {
@@ -690,11 +668,13 @@ void serv_got_im(PurpleConnection *gc, const char *who, const char *msg,
if (!(flags & PURPLE_MESSAGE_AUTO_RESP))
{
- serv_send_im(gc, name, away_msg, PURPLE_MESSAGE_AUTO_RESP);
+ PurpleMessage *msg;
+
+ msg = purple_message_new_outgoing(name,
+ away_msg, PURPLE_MESSAGE_AUTO_RESP);
- purple_conv_im_write(PURPLE_CONV_IM(conv), NULL, away_msg,
- PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_AUTO_RESP,
- mtime);
+ purple_serv_send_im(gc, msg);
+ purple_conversation_write_message(PURPLE_CONVERSATION(im), msg);
}
}
}
@@ -703,58 +683,52 @@ void serv_got_im(PurpleConnection *gc, const char *who, const char *msg,
g_free(name);
}
-void serv_got_typing(PurpleConnection *gc, const char *name, int timeout,
- PurpleTypingState state) {
- PurpleConversation *conv;
- PurpleConvIm *im = NULL;
+void purple_serv_got_typing(PurpleConnection *gc, const char *name, int timeout,
+ PurpleIMTypingState state) {
+ PurpleIMConversation *im;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, gc->account);
- if (conv != NULL) {
- im = PURPLE_CONV_IM(conv);
-
- purple_conv_im_set_typing_state(im, state);
+ im = purple_conversations_find_im_with_account(name, purple_connection_get_account(gc));
+ if (im != NULL) {
+ purple_im_conversation_set_typing_state(im, state);
} else {
switch (state)
{
- case PURPLE_TYPING:
+ case PURPLE_IM_TYPING:
purple_signal_emit(purple_conversations_get_handle(),
- "buddy-typing", gc->account, name);
+ "buddy-typing", purple_connection_get_account(gc), name);
break;
- case PURPLE_TYPED:
+ case PURPLE_IM_TYPED:
purple_signal_emit(purple_conversations_get_handle(),
- "buddy-typed", gc->account, name);
+ "buddy-typed", purple_connection_get_account(gc), name);
break;
- case PURPLE_NOT_TYPING:
+ case PURPLE_IM_NOT_TYPING:
purple_signal_emit(purple_conversations_get_handle(),
- "buddy-typing-stopped", gc->account, name);
+ "buddy-typing-stopped", purple_connection_get_account(gc), name);
break;
}
}
- if (conv != NULL && timeout > 0)
- purple_conv_im_start_typing_timeout(im, timeout);
+ if (im != NULL && timeout > 0)
+ purple_im_conversation_start_typing_timeout(im, timeout);
}
-void serv_got_typing_stopped(PurpleConnection *gc, const char *name) {
+void purple_serv_got_typing_stopped(PurpleConnection *gc, const char *name) {
- PurpleConversation *conv;
- PurpleConvIm *im;
+ PurpleIMConversation *im;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, gc->account);
- if (conv != NULL)
+ im = purple_conversations_find_im_with_account(name, purple_connection_get_account(gc));
+ if (im != NULL)
{
- im = PURPLE_CONV_IM(conv);
-
- if (im->typing_state == PURPLE_NOT_TYPING)
+ if (purple_im_conversation_get_typing_state(im) == PURPLE_IM_NOT_TYPING)
return;
- purple_conv_im_stop_typing_timeout(im);
- purple_conv_im_set_typing_state(im, PURPLE_NOT_TYPING);
+ purple_im_conversation_stop_typing_timeout(im);
+ purple_im_conversation_set_typing_state(im, PURPLE_IM_NOT_TYPING);
}
else
{
purple_signal_emit(purple_conversations_get_handle(),
- "buddy-typing-stopped", gc->account, name);
+ "buddy-typing-stopped", purple_connection_get_account(gc), name);
}
}
@@ -773,20 +747,20 @@ static void chat_invite_data_free(struct chat_invite_data *cid)
static void chat_invite_reject(struct chat_invite_data *cid)
{
- serv_reject_chat(cid->gc, cid->components);
+ purple_serv_reject_chat(cid->gc, cid->components);
chat_invite_data_free(cid);
}
static void chat_invite_accept(struct chat_invite_data *cid)
{
- serv_join_chat(cid->gc, cid->components);
+ purple_serv_join_chat(cid->gc, cid->components);
chat_invite_data_free(cid);
}
-void serv_got_chat_invite(PurpleConnection *gc, const char *name,
+void purple_serv_got_chat_invite(PurpleConnection *gc, const char *name,
const char *who, const char *message, GHashTable *data)
{
PurpleAccount *account;
@@ -798,7 +772,7 @@ void serv_got_chat_invite(PurpleConnection *gc, const char *name,
g_return_if_fail(who != NULL);
account = purple_connection_get_account(gc);
- if (!purple_privacy_check(account, who)) {
+ if (!purple_account_privacy_check(account, who)) {
purple_signal_emit(purple_conversations_get_handle(), "chat-invite-blocked",
account, who, name, message, data);
return;
@@ -827,10 +801,12 @@ void serv_got_chat_invite(PurpleConnection *gc, const char *name,
who, purple_account_get_username(account), name);
- purple_request_accept_cancel(gc, NULL, _("Accept chat invitation?"), buf2,
- PURPLE_DEFAULT_ACTION_NONE, account, who, NULL,
- cid, G_CALLBACK(chat_invite_accept),
- G_CALLBACK(chat_invite_reject));
+ purple_request_accept_cancel(gc, NULL,
+ _("Accept chat invitation?"), buf2,
+ PURPLE_DEFAULT_ACTION_NONE,
+ purple_request_cpar_from_connection(gc), cid,
+ G_CALLBACK(chat_invite_accept),
+ G_CALLBACK(chat_invite_reject));
}
else if (plugin_return > 0)
chat_invite_accept(cid);
@@ -838,11 +814,10 @@ void serv_got_chat_invite(PurpleConnection *gc, const char *name,
chat_invite_reject(cid);
}
-PurpleConversation *serv_got_joined_chat(PurpleConnection *gc,
+PurpleChatConversation *purple_serv_got_joined_chat(PurpleConnection *gc,
int id, const char *name)
{
- PurpleConversation *conv;
- PurpleConvChat *chat;
+ PurpleChatConversation *chat;
PurpleAccount *account;
account = purple_connection_get_account(gc);
@@ -850,49 +825,43 @@ PurpleConversation *serv_got_joined_chat(PurpleConnection *gc,
g_return_val_if_fail(account != NULL, NULL);
g_return_val_if_fail(name != NULL, NULL);
- conv = purple_conversation_new(PURPLE_CONV_TYPE_CHAT, account, name);
- g_return_val_if_fail(conv != NULL, NULL);
+ chat = purple_chat_conversation_new(account, name);
+ g_return_val_if_fail(chat != NULL, NULL);
- chat = PURPLE_CONV_CHAT(conv);
+ if (!g_slist_find(purple_connection_get_active_chats(gc), chat))
+ _purple_connection_add_active_chat(gc, chat);
- if (!g_slist_find(gc->buddy_chats, conv))
- gc->buddy_chats = g_slist_append(gc->buddy_chats, conv);
+ purple_chat_conversation_set_id(chat, id);
- purple_conv_chat_set_id(chat, id);
+ purple_signal_emit(purple_conversations_get_handle(), "chat-joined", chat);
- purple_signal_emit(purple_conversations_get_handle(), "chat-joined", conv);
-
- return conv;
+ return chat;
}
-void serv_got_chat_left(PurpleConnection *g, int id)
+void purple_serv_got_chat_left(PurpleConnection *g, int id)
{
GSList *bcs;
- PurpleConversation *conv = NULL;
- PurpleConvChat *chat = NULL;
-
- for (bcs = g->buddy_chats; bcs != NULL; bcs = bcs->next) {
- conv = (PurpleConversation *)bcs->data;
+ PurpleChatConversation *chat = NULL;
- chat = PURPLE_CONV_CHAT(conv);
-
- if (purple_conv_chat_get_id(chat) == id)
+ for (bcs = purple_connection_get_active_chats(g); bcs != NULL; bcs = bcs->next) {
+ if (purple_chat_conversation_get_id(
+ PURPLE_CHAT_CONVERSATION(bcs->data)) == id) {
+ chat = (PurpleChatConversation *)bcs->data;
break;
-
- conv = NULL;
+ }
}
- if (!conv)
+ if (!chat)
return;
purple_debug(PURPLE_DEBUG_INFO, "server", "Leaving room: %s\n",
- purple_conversation_get_name(conv));
+ purple_conversation_get_name(PURPLE_CONVERSATION(chat)));
- g->buddy_chats = g_slist_remove(g->buddy_chats, conv);
+ _purple_connection_remove_active_chat(g, chat);
- purple_conv_chat_left(PURPLE_CONV_CHAT(conv));
+ purple_chat_conversation_leave(chat);
- purple_signal_emit(purple_conversations_get_handle(), "chat-left", conv);
+ purple_signal_emit(purple_conversations_get_handle(), "chat-left", chat);
}
void purple_serv_got_join_chat_failed(PurpleConnection *gc, GHashTable *data)
@@ -901,43 +870,41 @@ void purple_serv_got_join_chat_failed(PurpleConnection *gc, GHashTable *data)
gc, data);
}
-void serv_got_chat_in(PurpleConnection *g, int id, const char *who,
+void purple_serv_got_chat_in(PurpleConnection *g, int id, const char *who,
PurpleMessageFlags flags, const char *message, time_t mtime)
{
GSList *bcs;
- PurpleConversation *conv = NULL;
- PurpleConvChat *chat = NULL;
+ PurpleChatConversation *chat = NULL;
char *buffy, *angel;
int plugin_return;
+ PurpleMessage *pmsg;
g_return_if_fail(who != NULL);
g_return_if_fail(message != NULL);
if (mtime < 0) {
purple_debug_error("server",
- "serv_got_chat_in ignoring negative timestamp\n");
+ "purple_serv_got_chat_in ignoring negative timestamp\n");
/* TODO: Would be more appropriate to use a value that indicates
that the timestamp is unknown, and surface that in the UI. */
mtime = time(NULL);
}
- for (bcs = g->buddy_chats; bcs != NULL; bcs = bcs->next) {
- conv = (PurpleConversation *)bcs->data;
-
- chat = PURPLE_CONV_CHAT(conv);
-
- if (purple_conv_chat_get_id(chat) == id)
+ for (bcs = purple_connection_get_active_chats(g); bcs != NULL; bcs = bcs->next) {
+ if (purple_chat_conversation_get_id(
+ PURPLE_CHAT_CONVERSATION(bcs->data)) == id) {
+ chat = (PurpleChatConversation *)bcs->data;
break;
-
- conv = NULL;
+ }
}
- if (!conv)
+ if (!chat)
return;
/* Did I send the message? */
- if (purple_strequal(purple_conv_chat_get_nick(chat),
- purple_normalize(purple_conversation_get_account(conv), who))) {
+ if (purple_strequal(purple_chat_conversation_get_nick(chat),
+ purple_normalize(purple_conversation_get_account(
+ PURPLE_CONVERSATION(chat)), who))) {
flags |= PURPLE_MESSAGE_SEND;
flags &= ~PURPLE_MESSAGE_RECV; /* Just in case some prpl sets it! */
} else {
@@ -953,8 +920,8 @@ void serv_got_chat_in(PurpleConnection *g, int id, const char *who,
plugin_return = GPOINTER_TO_INT(
purple_signal_emit_return_1(purple_conversations_get_handle(),
- "receiving-chat-msg", g->account,
- &angel, &buffy, conv, &flags));
+ "receiving-chat-msg", purple_connection_get_account(g),
+ &angel, &buffy, chat, &flags));
if (!buffy || !angel || plugin_return) {
g_free(buffy);
@@ -965,16 +932,22 @@ void serv_got_chat_in(PurpleConnection *g, int id, const char *who,
who = angel;
message = buffy;
- purple_signal_emit(purple_conversations_get_handle(), "received-chat-msg", g->account,
- who, message, conv, flags);
+ purple_signal_emit(purple_conversations_get_handle(), "received-chat-msg", purple_connection_get_account(g),
+ who, message, chat, flags);
- purple_conv_chat_write(chat, who, message, flags, mtime);
+ if (flags & PURPLE_MESSAGE_RECV)
+ pmsg = purple_message_new_incoming(who, message, flags, mtime);
+ else {
+ pmsg = purple_message_new_outgoing(who, message, flags);
+ purple_message_set_time(pmsg, mtime);
+ }
+ purple_conversation_write_message(PURPLE_CONVERSATION(chat), pmsg);
g_free(angel);
g_free(buffy);
}
-void serv_send_file(PurpleConnection *gc, const char *who, const char *file)
+void purple_serv_send_file(PurpleConnection *gc, const char *who, const char *file)
{
PurplePlugin *prpl;
PurplePluginProtocolInfo *prpl_info;
diff --git a/libpurple/server.h b/libpurple/server.h
index 504011561b..39e54fae04 100644
--- a/libpurple/server.h
+++ b/libpurple/server.h
@@ -1,8 +1,3 @@
-/**
- * @file server.h Server API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -23,196 +18,190 @@
* 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_SERVER_H_
#define _PURPLE_SERVER_H_
+/**
+ * SECTION:server
+ * @section_id: libpurple-server
+ * @short_description: <filename>server.h</filename>
+ * @title: Server API
+ */
-#include "account.h"
-#include "conversation.h"
+#include "accounts.h"
+#include "conversations.h"
+#include "message.h"
#include "prpl.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**
- * Send a typing message to a given user over a given connection.
+ * purple_serv_send_typing:
+ * @gc: The connection over which to send the typing notification.
+ * @name: The user to send the typing notification to.
+ * @state: One of PURPLE_IM_TYPING, PURPLE_IM_TYPED, or PURPLE_IM_NOT_TYPING.
*
- * TODO: Could probably move this into the conversation API.
+ * Send a typing message to a given user over a given connection.
*
- * @param gc The connection over which to send the typing notification.
- * @param name The user to send the typing notification to.
- * @param state One of PURPLE_TYPING, PURPLE_TYPED, or PURPLE_NOT_TYPING.
- * @return A quiet-period, specified in seconds, where Purple will not
+ * Returns: A quiet-period, specified in seconds, where Purple will not
* send any additional typing notification messages. Most
* protocols should return 0, which means that no additional
- * PURPLE_TYPING messages need to be sent. If this is 5, for
+ * PURPLE_IM_TYPING messages need to be sent. If this is 5, for
* example, then Purple will wait five seconds, and if the Purple
- * user is still typing then Purple will send another PURPLE_TYPING
+ * user is still typing then Purple will send another PURPLE_IM_TYPING
* message.
*/
-unsigned int serv_send_typing(PurpleConnection *gc, const char *name, PurpleTypingState state);
+/* TODO Could probably move this into the conversation API. */
+unsigned int purple_serv_send_typing(PurpleConnection *gc, const char *name, PurpleIMTypingState state);
-void serv_move_buddy(PurpleBuddy *, PurpleGroup *, PurpleGroup *);
-int serv_send_im(PurpleConnection *, const char *, const char *, PurpleMessageFlags flags);
+void purple_serv_move_buddy(PurpleBuddy *, PurpleGroup *, PurpleGroup *);
+int purple_serv_send_im(PurpleConnection *, PurpleMessage *msg);
-/** Get information about an account's attention commands, from the prpl.
+/**
+ * purple_get_attention_type_from_code:
*
- * @return The attention command numbered 'code' from the prpl's attention_types, or NULL.
+ * Get information about an account's attention commands, from the protocol.
+ *
+ * Returns: The attention command numbered 'code' from the protocol's attention_types, or NULL.
*/
PurpleAttentionType *purple_get_attention_type_from_code(PurpleAccount *account, guint type_code);
-/** Send an attention request message.
- *
- * @deprecated Use purple_prpl_send_attention() instead.
- *
- * @param gc The connection to send the message on.
- * @param who Whose attention to request.
- * @param type_code An index into the prpl's attention_types list determining the type
- * of the attention request command to send. 0 if prpl only defines one
- * (for example, Yahoo and MSN), but some protocols define more (MySpaceIM).
- *
- * Note that you can't send arbitrary PurpleAttentionType's, because there is
- * only a fixed set of attention commands.
- */
-void serv_send_attention(PurpleConnection *gc, const char *who, guint type_code);
+void purple_serv_get_info(PurpleConnection *, const char *);
+void purple_serv_set_info(PurpleConnection *, const char *);
-/** Process an incoming attention message.
- *
- * @deprecated Use purple_prpl_got_attention() instead.
- *
- * @param gc The connection that received the attention message.
- * @param who Who requested your attention.
- * @param type_code An index into the prpl's attention_types list determining the type
- * of the attention request command to send.
- */
-void serv_got_attention(PurpleConnection *gc, const char *who, guint type_code);
-
-void serv_get_info(PurpleConnection *, const char *);
-void serv_set_info(PurpleConnection *, const char *);
-
-void serv_add_permit(PurpleConnection *, const char *);
-void serv_add_deny(PurpleConnection *, const char *);
-void serv_rem_permit(PurpleConnection *, const char *);
-void serv_rem_deny(PurpleConnection *, const char *);
-void serv_set_permit_deny(PurpleConnection *);
-void serv_chat_invite(PurpleConnection *, int, const char *, const char *);
-void serv_chat_leave(PurpleConnection *, int);
-void serv_chat_whisper(PurpleConnection *, int, const char *, const char *);
-int serv_chat_send(PurpleConnection *, int, const char *, PurpleMessageFlags flags);
-void serv_alias_buddy(PurpleBuddy *);
-void serv_got_alias(PurpleConnection *gc, const char *who, const char *alias);
+void purple_serv_add_permit(PurpleConnection *, const char *);
+void purple_serv_add_deny(PurpleConnection *, const char *);
+void purple_serv_rem_permit(PurpleConnection *, const char *);
+void purple_serv_rem_deny(PurpleConnection *, const char *);
+void purple_serv_set_permit_deny(PurpleConnection *);
+void purple_serv_chat_invite(PurpleConnection *, int, const char *, const char *);
+void purple_serv_chat_leave(PurpleConnection *, int);
+int purple_serv_chat_send(PurpleConnection *, int, PurpleMessage *);
+void purple_serv_alias_buddy(PurpleBuddy *);
+void purple_serv_got_alias(PurpleConnection *gc, const char *who, const char *alias);
/**
- * A protocol plugin should call this when it retrieves a private alias from
+ * purple_serv_got_private_alias:
+ * @gc: The connection on which the alias was received.
+ * @who: The name of the buddy whose alias was received.
+ * @alias: The alias that was received.
+ *
+ * A protocol should call this when it retrieves a private alias from
* the server. Private aliases are the aliases the user sets, while public
* aliases are the aliases or display names that buddies set for themselves.
- *
- * @param gc The connection on which the alias was received.
- * @param who The name of the buddy whose alias was received.
- * @param alias The alias that was received.
*/
void purple_serv_got_private_alias(PurpleConnection *gc, const char *who, const char *alias);
/**
- * Receive a typing message from a remote user. Either PURPLE_TYPING
- * or PURPLE_TYPED. If the user has stopped typing then use
- * serv_got_typing_stopped instead.
- *
- * TODO: Could probably move this into the conversation API.
- *
- * @param gc The connection on which the typing message was received.
- * @param name The name of the remote user.
- * @param timeout If this is a number greater than 0, then
+ * purple_serv_got_typing:
+ * @gc: The connection on which the typing message was received.
+ * @name: The name of the remote user.
+ * @timeout: If this is a number greater than 0, then
* Purple will wait this number of seconds and then
- * set this buddy to the PURPLE_NOT_TYPING state. This
+ * set this buddy to the PURPLE_IM_NOT_TYPING state. This
* is used by protocols that send repeated typing messages
* while the user is composing the message.
- * @param state The typing state received
+ * @state: The typing state received
+ *
+ * Receive a typing message from a remote user. Either PURPLE_IM_TYPING
+ * or PURPLE_IM_TYPED. If the user has stopped typing then use
+ * purple_serv_got_typing_stopped instead.
+ *
+ * @todo Could probably move this into the conversation API.
*/
-void serv_got_typing(PurpleConnection *gc, const char *name, int timeout,
- PurpleTypingState state);
+void purple_serv_got_typing(PurpleConnection *gc, const char *name, int timeout,
+ PurpleIMTypingState state);
/**
- * TODO: Could probably move this into the conversation API.
+ * purple_serv_got_typing_stopped:
+ *
+ * @todo Could probably move this into the conversation API.
*/
-void serv_got_typing_stopped(PurpleConnection *gc, const char *name);
+void purple_serv_got_typing_stopped(PurpleConnection *gc, const char *name);
-void serv_got_im(PurpleConnection *gc, const char *who, const char *msg,
+void purple_serv_got_im(PurpleConnection *gc, const char *who, const char *msg,
PurpleMessageFlags flags, time_t mtime);
/**
- * @param data The hash function should be g_str_hash() and the equal
+ * purple_serv_join_chat:
+ * @data: The hash function should be g_str_hash() and the equal
* function should be g_str_equal().
*/
-void serv_join_chat(PurpleConnection *, GHashTable *data);
+void purple_serv_join_chat(PurpleConnection *, GHashTable *data);
/**
- * @param data The hash function should be g_str_hash() and the equal
+ * purple_serv_reject_chat:
+ * @data: The hash function should be g_str_hash() and the equal
* function should be g_str_equal().
*/
-void serv_reject_chat(PurpleConnection *, GHashTable *data);
+void purple_serv_reject_chat(PurpleConnection *, GHashTable *data);
/**
- * Called by a prpl when an account is invited into a chat.
- *
- * @param gc The connection on which the invite arrived.
- * @param name The name of the chat you're being invited to.
- * @param who The username of the person inviting the account.
- * @param message The optional invite message.
- * @param data The components necessary if you want to call serv_join_chat().
+ * purple_serv_got_chat_invite:
+ * @gc: The connection on which the invite arrived.
+ * @name: The name of the chat you're being invited to.
+ * @who: The username of the person inviting the account.
+ * @message: The optional invite message.
+ * @data: The components necessary if you want to call purple_serv_join_chat().
* The hash function should be g_str_hash() and the equal
* function should be g_str_equal().
+ *
+ * Called by a protocol when an account is invited into a chat.
*/
-void serv_got_chat_invite(PurpleConnection *gc, const char *name,
+void purple_serv_got_chat_invite(PurpleConnection *gc, const char *name,
const char *who, const char *message,
GHashTable *data);
/**
- * Called by a prpl when an account has joined a chat.
+ * purple_serv_got_joined_chat:
+ * @gc: The connection on which the chat was joined.
+ * @id: The id of the chat, assigned by the protocol.
+ * @name: The name of the chat.
+ *
+ * Called by a protocol when an account has joined a chat.
*
- * @param gc The connection on which the chat was joined.
- * @param id The id of the chat, assigned by the prpl.
- * @param name The name of the chat.
- * @return The resulting conversation
+ * Returns: The resulting conversation
*/
-PurpleConversation *serv_got_joined_chat(PurpleConnection *gc,
+PurpleChatConversation *purple_serv_got_joined_chat(PurpleConnection *gc,
int id, const char *name);
/**
- * Called by a prpl when an attempt to join a chat via serv_join_chat()
- * fails.
- *
- * @param gc The connection on which chat joining failed
- * @param data The components passed to serv_join_chat() originally.
+ * purple_serv_got_join_chat_failed:
+ * @gc: The connection on which chat joining failed
+ * @data: The components passed to purple_serv_join_chat() originally.
* The hash function should be g_str_hash() and the equal
* function should be g_str_equal().
+ *
+ * Called by a protocol when an attempt to join a chat via purple_serv_join_chat()
+ * fails.
*/
void purple_serv_got_join_chat_failed(PurpleConnection *gc, GHashTable *data);
/**
- * Called by a prpl when an account has left a chat.
+ * purple_serv_got_chat_left:
+ * @g: The connection on which the chat was left.
+ * @id: The id of the chat, as assigned by the protocol.
*
- * @param g The connection on which the chat was left.
- * @param id The id of the chat, as assigned by the prpl.
+ * Called by a protocol when an account has left a chat.
*/
-void serv_got_chat_left(PurpleConnection *g, int id);
+void purple_serv_got_chat_left(PurpleConnection *g, int id);
/**
- * Called by a prpl when a message has been received in a chat.
- *
- * @param g The connection on which the message was received.
- * @param id The id of the chat, as assigned by the prpl.
- * @param who The name of the user who sent the message.
- * @param flags The flags of the message.
- * @param message The message received in the chat.
- * @param mtime The time when the message was received.
+ * purple_serv_got_chat_in:
+ * @g: The connection on which the message was received.
+ * @id: The id of the chat, as assigned by the protocol.
+ * @who: The name of the user who sent the message.
+ * @flags: The flags of the message.
+ * @message: The message received in the chat.
+ * @mtime: The time when the message was received.
+ *
+ * Called by a protocol when a message has been received in a chat.
*/
-void serv_got_chat_in(PurpleConnection *g, int id, const char *who,
+void purple_serv_got_chat_in(PurpleConnection *g, int id, const char *who,
PurpleMessageFlags flags, const char *message, time_t mtime);
-void serv_send_file(PurpleConnection *gc, const char *who, const char *file);
+void purple_serv_send_file(PurpleConnection *gc, const char *who, const char *file);
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_SERVER_H_ */
+
diff --git a/libpurple/signals.c b/libpurple/signals.c
index 60d7d2d7f1..4248a3c4b5 100644
--- a/libpurple/signals.c
+++ b/libpurple/signals.c
@@ -1,8 +1,3 @@
-/**
- * @file signals.c Signal API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -28,7 +23,6 @@
#include "dbus-maybe.h"
#include "debug.h"
#include "signals.h"
-#include "value.h"
/* must include this to use G_VA_COPY */
#include <string.h>
@@ -51,8 +45,8 @@ typedef struct
PurpleSignalMarshalFunc marshal;
int num_values;
- PurpleValue **values;
- PurpleValue *ret_value;
+ GType *value_types;
+ GType ret_type;
GList *handlers;
size_t handler_count;
@@ -87,25 +81,14 @@ destroy_signal_data(PurpleSignalData *signal_data)
g_list_foreach(signal_data->handlers, (GFunc)g_free, NULL);
g_list_free(signal_data->handlers);
- if (signal_data->values != NULL)
- {
- int i;
-
- for (i = 0; i < signal_data->num_values; i++)
- purple_value_destroy((PurpleValue *)signal_data->values[i]);
-
- g_free(signal_data->values);
- }
-
- if (signal_data->ret_value != NULL)
- purple_value_destroy(signal_data->ret_value);
+ g_free(signal_data->value_types);
g_free(signal_data);
}
gulong
purple_signal_register(void *instance, const char *signal,
PurpleSignalMarshalFunc marshal,
- PurpleValue *ret_value, int num_values, ...)
+ GType ret_type, int num_values, ...)
{
PurpleInstanceData *instance_data;
PurpleSignalData *signal_data;
@@ -136,19 +119,19 @@ purple_signal_register(void *instance, const char *signal,
signal_data->id = instance_data->next_signal_id;
signal_data->marshal = marshal;
signal_data->next_handler_id = 1;
- signal_data->ret_value = ret_value;
+ signal_data->ret_type = ret_type;
signal_data->num_values = num_values;
if (num_values > 0)
{
int i;
- signal_data->values = g_new0(PurpleValue *, num_values);
+ signal_data->value_types = g_new0(GType, num_values);
va_start(args, num_values);
for (i = 0; i < num_values; i++)
- signal_data->values[i] = va_arg(args, PurpleValue *);
+ signal_data->value_types[i] = va_arg(args, GType);
va_end(args);
}
@@ -203,17 +186,17 @@ purple_signals_unregister_by_instance(void *instance)
}
void
-purple_signal_get_values(void *instance, const char *signal,
- PurpleValue **ret_value,
- int *num_values, PurpleValue ***values)
+purple_signal_get_types(void *instance, const char *signal,
+ GType *ret_type,
+ int *num_values, GType **value_types)
{
PurpleInstanceData *instance_data;
PurpleSignalData *signal_data;
- g_return_if_fail(instance != NULL);
- g_return_if_fail(signal != NULL);
- g_return_if_fail(num_values != NULL);
- g_return_if_fail(values != NULL);
+ g_return_if_fail(instance != NULL);
+ g_return_if_fail(signal != NULL);
+ g_return_if_fail(num_values != NULL);
+ g_return_if_fail(value_types != NULL);
/* Get the instance data */
instance_data =
@@ -227,11 +210,11 @@ purple_signal_get_values(void *instance, const char *signal,
g_return_if_fail(signal_data != NULL);
- *num_values = signal_data->num_values;
- *values = signal_data->values;
+ *num_values = signal_data->num_values;
+ *value_types = signal_data->value_types;
- if (ret_value != NULL)
- *ret_value = signal_data->ret_value;
+ if (ret_type != NULL)
+ *ret_type = signal_data->ret_type;
}
static gint handler_priority(void * a, void * b) {
@@ -488,7 +471,7 @@ purple_signal_emit_vargs(void *instance, const char *signal, va_list args)
#ifdef HAVE_DBUS
purple_dbus_signal_emit_purple(signal, signal_data->num_values,
- signal_data->values, args);
+ signal_data->value_types, args);
#endif /* HAVE_DBUS */
}
@@ -540,7 +523,7 @@ purple_signal_emit_vargs_return_1(void *instance, const char *signal,
#ifdef HAVE_DBUS
G_VA_COPY(tmp, args);
purple_dbus_signal_emit_purple(signal, signal_data->num_values,
- signal_data->values, tmp);
+ signal_data->value_types, tmp);
va_end(tmp);
#endif /* HAVE_DBUS */
@@ -693,6 +676,17 @@ purple_marshal_VOID__POINTER_POINTER_UINT_UINT(PurpleCallback cb, va_list args,
}
void
+purple_marshal_VOID__POINTER_UINT_UINT(PurpleCallback cb, va_list args,
+ void *data, void **return_val)
+{
+ void *arg1 = va_arg(args, void *);
+ guint arg2 = va_arg(args, guint);
+ guint arg3 = va_arg(args, guint);
+
+ ((void (*)(void *, guint, guint, void *))cb)(arg1, arg2, arg3, data);
+}
+
+void
purple_marshal_VOID__POINTER_POINTER_POINTER(PurpleCallback cb, va_list args,
void *data, void **return_val)
{
diff --git a/libpurple/signals.h b/libpurple/signals.h
index 8499cebe5f..2e83e592e6 100644
--- a/libpurple/signals.h
+++ b/libpurple/signals.h
@@ -1,8 +1,3 @@
-/**
- * @file signals.h Signal API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -23,11 +18,19 @@
* 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_SIGNALS_H_
#define _PURPLE_SIGNALS_H_
+/**
+ * SECTION:signals
+ * @section_id: libpurple-signals
+ * @short_description: <filename>signals.h</filename>
+ * @title: Purple-signals API
+ * @see_also: <link linkend="chapter-tut-signals">Signals tutorial</link>
+ */
#include <glib.h>
-#include "value.h"
+#include <glib-object.h>
#define PURPLE_CALLBACK(func) ((PurpleCallback)func)
@@ -35,126 +38,150 @@ typedef void (*PurpleCallback)(void);
typedef void (*PurpleSignalMarshalFunc)(PurpleCallback cb, va_list args,
void *data, void **return_val);
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name Signal API */
+/* Signal API */
/**************************************************************************/
-/*@{*/
-/** The priority of a signal connected using purple_signal_connect().
+/**
+ * PURPLE_SIGNAL_PRIORITY_DEFAULT:
*
- * @see purple_signal_connect_priority()
+ * The priority of a signal connected using purple_signal_connect().
+ *
+ * See purple_signal_connect_priority()
*/
#define PURPLE_SIGNAL_PRIORITY_DEFAULT 0
-/** The largest signal priority; signals with this priority will be called
- * <em>last</em>. (This is highest as in numerical value, not as in order of
- * importance.)
+
+/**
+ * PURPLE_SIGNAL_PRIORITY_HIGHEST:
*
- * @see purple_signal_connect_priority().
+ * The largest signal priority; signals with this priority will be called
+ * <emphasis>last</emphasis>. (This is highest as in numerical value, not as in
+ * order of importance.)
+ *
+ * See purple_signal_connect_priority().
*/
#define PURPLE_SIGNAL_PRIORITY_HIGHEST 9999
-/** The smallest signal priority; signals with this priority will be called
- * <em>first</em>. (This is lowest as in numerical value, not as in order of
- * importance.)
+
+/**
+ * PURPLE_SIGNAL_PRIORITY_LOWEST:
*
- * @see purple_signal_connect_priority().
+ * The smallest signal priority; signals with this priority will be called
+ * <emphasis>first</emphasis>. (This is lowest as in numerical value, not as in
+ * order of importance.)
+ *
+ * See purple_signal_connect_priority().
*/
#define PURPLE_SIGNAL_PRIORITY_LOWEST -9999
/**
- * Registers a signal in an instance.
+ * purple_signal_register:
+ * @instance: The instance to register the signal for.
+ * @signal: The signal name.
+ * @marshal: The marshal function.
+ * @ret_type: The return type, or G_TYPE_NONE for no return type.
+ * @num_values: The number of values to be passed to the callbacks.
+ * @...: The types of the parameters for the callbacks.
*
- * @param instance The instance to register the signal for.
- * @param signal The signal name.
- * @param marshal The marshal function.
- * @param ret_value The return value type, or NULL for no return value.
- * @param num_values The number of values to be passed to the callbacks.
- * @param ... The values to pass to the callbacks.
- *
- * @return The signal ID local to that instance, or 0 if the signal
- * couldn't be registered.
+ * Registers a signal in an instance.
*
- * @see PurpleValue
+ * Returns: The signal ID local to that instance, or 0 if the signal
+ * couldn't be registered.
*/
gulong purple_signal_register(void *instance, const char *signal,
PurpleSignalMarshalFunc marshal,
- PurpleValue *ret_value, int num_values, ...);
+ GType ret_type, int num_values, ...);
/**
- * Unregisters a signal in an instance.
+ * purple_signal_unregister:
+ * @instance: The instance to unregister the signal for.
+ * @signal: The signal name.
*
- * @param instance The instance to unregister the signal for.
- * @param signal The signal name.
+ * Unregisters a signal in an instance.
*/
void purple_signal_unregister(void *instance, const char *signal);
/**
- * Unregisters all signals in an instance.
+ * purple_signals_unregister_by_instance:
+ * @instance: The instance to unregister the signal for.
*
- * @param instance The instance to unregister the signal for.
+ * Unregisters all signals in an instance.
*/
void purple_signals_unregister_by_instance(void *instance);
/**
- * Returns a list of value types used for a signal.
- *
- * @param instance The instance the signal is registered to.
- * @param signal The signal.
- * @param ret_value The return value from the last signal handler.
- * @param num_values The returned number of values.
- * @param values The returned list of values.
+ * purple_signal_get_types:
+ * @instance: The instance the signal is registered to.
+ * @signal: The signal.
+ * @ret_type: (out): The return type.
+ * @num_values: (out): The returned number of parameters.
+ * @param_types: (out): The returned list of parameter types.
+ *
+ * Outputs a list of value types used for a signal through the @ret_type,
+ * @num_values and @param_types out parameters.
*/
-void purple_signal_get_values(void *instance, const char *signal,
- PurpleValue **ret_value,
- int *num_values, PurpleValue ***values);
+void purple_signal_get_types(void *instance, const char *signal,
+ GType *ret_type, int *num_values,
+ GType **param_types);
/**
+ * purple_signal_connect_priority:
+ * @instance: The instance to connect to.
+ * @signal: The name of the signal to connect.
+ * @handle: The handle of the receiver.
+ * @func: The callback function.
+ * @data: The data to pass to the callback function.
+ * @priority: The priority with which the handler should be called. Signal
+ * handlers are called in ascending numerical order of
+ * @priority from #PURPLE_SIGNAL_PRIORITY_LOWEST to
+ * #PURPLE_SIGNAL_PRIORITY_HIGHEST.
+ *
* Connects a signal handler to a signal for a particular object.
*
* Take care not to register a handler function twice. Purple will
* not correct any mistakes for you in this area.
*
- * @param instance The instance to connect to.
- * @param signal The name of the signal to connect.
- * @param handle The handle of the receiver.
- * @param func The callback function.
- * @param data The data to pass to the callback function.
- * @param priority The priority with which the handler should be called. Signal
- * handlers are called in ascending numerical order of @a
- * priority from #PURPLE_SIGNAL_PRIORITY_LOWEST to
- * #PURPLE_SIGNAL_PRIORITY_HIGHEST.
- *
- * @return The signal handler ID.
+ * See purple_signal_disconnect()
*
- * @see purple_signal_disconnect()
+ * Returns: The signal handler ID.
*/
gulong purple_signal_connect_priority(void *instance, const char *signal,
void *handle, PurpleCallback func, void *data, int priority);
/**
+ * purple_signal_connect:
+ * @instance: The instance to connect to.
+ * @signal: The name of the signal to connect.
+ * @handle: The handle of the receiver.
+ * @func: The callback function.
+ * @data: The data to pass to the callback function.
+ *
* Connects a signal handler to a signal for a particular object.
* (Its priority defaults to 0, aka #PURPLE_SIGNAL_PRIORITY_DEFAULT.)
*
* Take care not to register a handler function twice. Purple will
* not correct any mistakes for you in this area.
*
- * @param instance The instance to connect to.
- * @param signal The name of the signal to connect.
- * @param handle The handle of the receiver.
- * @param func The callback function.
- * @param data The data to pass to the callback function.
- *
- * @return The signal handler ID.
+ * See purple_signal_disconnect()
*
- * @see purple_signal_disconnect()
+ * Returns: The signal handler ID.
*/
gulong purple_signal_connect(void *instance, const char *signal,
void *handle, PurpleCallback func, void *data);
/**
+ * purple_signal_connect_priority_vargs:
+ * @instance: The instance to connect to.
+ * @signal: The name of the signal to connect.
+ * @handle: The handle of the receiver.
+ * @func: The callback function.
+ * @data: The data to pass to the callback function.
+ * @priority: The priority with which the handler should be called. Signal
+ * handlers are called in ascending numerical order of
+ * @priority from #PURPLE_SIGNAL_PRIORITY_LOWEST to
+ * #PURPLE_SIGNAL_PRIORITY_HIGHEST.
+ *
* Connects a signal handler to a signal for a particular object.
*
* The signal handler will take a va_args of arguments, instead of
@@ -163,24 +190,21 @@ gulong purple_signal_connect(void *instance, const char *signal,
* Take care not to register a handler function twice. Purple will
* not correct any mistakes for you in this area.
*
- * @param instance The instance to connect to.
- * @param signal The name of the signal to connect.
- * @param handle The handle of the receiver.
- * @param func The callback function.
- * @param data The data to pass to the callback function.
- * @param priority The priority with which the handler should be called. Signal
- * handlers are called in ascending numerical order of @a
- * priority from #PURPLE_SIGNAL_PRIORITY_LOWEST to
- * #PURPLE_SIGNAL_PRIORITY_HIGHEST.
- *
- * @return The signal handler ID.
+ * See purple_signal_disconnect()
*
- * @see purple_signal_disconnect()
+ * Returns: The signal handler ID.
*/
gulong purple_signal_connect_priority_vargs(void *instance, const char *signal,
void *handle, PurpleCallback func, void *data, int priority);
/**
+ * purple_signal_connect_vargs:
+ * @instance: The instance to connect to.
+ * @signal: The name of the signal to connect.
+ * @handle: The handle of the receiver.
+ * @func: The callback function.
+ * @data: The data to pass to the callback function.
+ *
* Connects a signal handler to a signal for a particular object.
* (Its priority defaults to 0, aka #PURPLE_SIGNAL_PRIORITY_DEFAULT.)
*
@@ -190,106 +214,105 @@ gulong purple_signal_connect_priority_vargs(void *instance, const char *signal,
* Take care not to register a handler function twice. Purple will
* not correct any mistakes for you in this area.
*
- * @param instance The instance to connect to.
- * @param signal The name of the signal to connect.
- * @param handle The handle of the receiver.
- * @param func The callback function.
- * @param data The data to pass to the callback function.
+ * See purple_signal_disconnect()
*
- * @return The signal handler ID.
- *
- * @see purple_signal_disconnect()
+ * Returns: The signal handler ID.
*/
gulong purple_signal_connect_vargs(void *instance, const char *signal,
void *handle, PurpleCallback func, void *data);
/**
- * Disconnects a signal handler from a signal on an object.
+ * purple_signal_disconnect:
+ * @instance: The instance to disconnect from.
+ * @signal: The name of the signal to disconnect.
+ * @handle: The handle of the receiver.
+ * @func: The registered function to disconnect.
*
- * @param instance The instance to disconnect from.
- * @param signal The name of the signal to disconnect.
- * @param handle The handle of the receiver.
- * @param func The registered function to disconnect.
+ * Disconnects a signal handler from a signal on an object.
*
- * @see purple_signal_connect()
+ * See purple_signal_connect()
*/
void purple_signal_disconnect(void *instance, const char *signal,
void *handle, PurpleCallback func);
/**
- * Removes all callbacks associated with a receiver handle.
+ * purple_signals_disconnect_by_handle:
+ * @handle: The receiver handle.
*
- * @param handle The receiver handle.
+ * Removes all callbacks associated with a receiver handle.
*/
void purple_signals_disconnect_by_handle(void *handle);
/**
- * Emits a signal.
+ * purple_signal_emit:
+ * @instance: The instance emitting the signal.
+ * @signal: The signal being emitted.
*
- * @param instance The instance emitting the signal.
- * @param signal The signal being emitted.
+ * Emits a signal.
*
- * @see purple_signal_connect()
- * @see purple_signal_disconnect()
+ * See purple_signal_connect(), purple_signal_disconnect()
*/
void purple_signal_emit(void *instance, const char *signal, ...);
/**
- * Emits a signal, using a va_list of arguments.
+ * purple_signal_emit_vargs:
+ * @instance: The instance emitting the signal.
+ * @signal: The signal being emitted.
+ * @args: The arguments list.
*
- * @param instance The instance emitting the signal.
- * @param signal The signal being emitted.
- * @param args The arguments list.
+ * Emits a signal, using a va_list of arguments.
*
- * @see purple_signal_connect()
- * @see purple_signal_disconnect()
+ * See purple_signal_connect(), purple_signal_disconnect()
*/
void purple_signal_emit_vargs(void *instance, const char *signal, va_list args);
/**
+ * purple_signal_emit_return_1:
+ * @instance: The instance emitting the signal.
+ * @signal: The signal being emitted.
+ *
* Emits a signal and returns the first non-NULL return value.
*
* Further signal handlers are NOT called after a handler returns
* something other than NULL.
*
- * @param instance The instance emitting the signal.
- * @param signal The signal being emitted.
- *
- * @return The first non-NULL return value
+ * Returns: The first non-NULL return value
*/
void *purple_signal_emit_return_1(void *instance, const char *signal, ...);
/**
+ * purple_signal_emit_vargs_return_1:
+ * @instance: The instance emitting the signal.
+ * @signal: The signal being emitted.
+ * @args: The arguments list.
+ *
* Emits a signal and returns the first non-NULL return value.
*
* Further signal handlers are NOT called after a handler returns
* something other than NULL.
*
- * @param instance The instance emitting the signal.
- * @param signal The signal being emitted.
- * @param args The arguments list.
- *
- * @return The first non-NULL return value
+ * Returns: The first non-NULL return value
*/
void *purple_signal_emit_vargs_return_1(void *instance, const char *signal,
va_list args);
/**
+ * purple_signals_init:
+ *
* Initializes the signals subsystem.
*/
void purple_signals_init(void);
/**
+ * purple_signals_uninit:
+ *
* Uninitializes the signals subsystem.
*/
void purple_signals_uninit(void);
-/*@}*/
-
/**************************************************************************/
-/** @name Marshal Functions */
+/* Marshal Functions */
/**************************************************************************/
-/*@{*/
void purple_marshal_VOID(
PurpleCallback cb, va_list args, void *data, void **return_val);
@@ -311,6 +334,8 @@ void purple_marshal_VOID__POINTER_POINTER_UINT(
PurpleCallback cb, va_list args, void *data, void **return_val);
void purple_marshal_VOID__POINTER_POINTER_UINT_UINT(
PurpleCallback cb, va_list args, void *data, void **return_val);
+void purple_marshal_VOID__POINTER_UINT_UINT(
+ PurpleCallback cb, va_list args, void *data, void **return_val);
void purple_marshal_VOID__POINTER_POINTER_POINTER(
PurpleCallback cb, va_list args, void *data, void **return_val);
void purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER(
@@ -371,10 +396,8 @@ void purple_marshal_POINTER__POINTER_INT64_BOOLEAN(
PurpleCallback cb, va_list args, void *data, void **return_val);
void purple_marshal_POINTER__POINTER_POINTER(
PurpleCallback cb, va_list args, void *data, void **return_val);
-/*@}*/
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_SIGNALS_H_ */
+
diff --git a/libpurple/smiley-custom.c b/libpurple/smiley-custom.c
new file mode 100644
index 0000000000..7bcb571781
--- /dev/null
+++ b/libpurple/smiley-custom.c
@@ -0,0 +1,317 @@
+/* purpleOB
+ *
+ * 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 "smiley-custom.h"
+
+#include "debug.h"
+#include "util.h"
+
+#include <glib/gstdio.h>
+#include <errno.h>
+
+#define SMILEYS_DEFAULT_FOLDER "custom_smiley"
+#define SMILEYS_INDEX_FILE "smileys.xml"
+
+static gchar *smileys_dir;
+static gchar *smileys_index;
+
+static PurpleSmileyList *smileys_list;
+static gboolean disable_write = FALSE;
+
+/*******************************************************************************
+ * XML storage
+ ******************************************************************************/
+
+static void
+purple_smiley_custom_load(void)
+{
+ PurpleXmlNode *root_node, *profile_node, *smileyset_node, *smiley_node;
+ int got_smileys = 0;
+
+ if (!g_file_test(smileys_index, G_FILE_TEST_EXISTS))
+ return;
+
+ root_node = purple_xmlnode_from_file(g_path_get_dirname(smileys_index),
+ g_path_get_basename(smileys_index),
+ "custom smileys index", "smiley-custom");
+ g_return_if_fail(root_node);
+
+ profile_node = purple_xmlnode_get_child(root_node, "profile");
+ if (!profile_node) {
+ purple_xmlnode_free(root_node);
+ g_return_if_fail(profile_node);
+ }
+
+ smileyset_node = purple_xmlnode_get_child(profile_node, "smiley_set");
+ if (!smileyset_node) {
+ purple_xmlnode_free(root_node);
+ g_return_if_fail(smileyset_node);
+ }
+
+ smiley_node = purple_xmlnode_get_child(smileyset_node, "smiley");
+ while (smiley_node) {
+ const gchar *shortcut, *file_name;
+
+ shortcut = purple_xmlnode_get_attrib(smiley_node, "shortcut");
+ file_name = purple_xmlnode_get_attrib(smiley_node, "filename");
+
+ if (shortcut && file_name) {
+ PurpleSmiley *smiley;
+ gchar *file_path = g_build_filename(
+ smileys_dir, file_name, NULL);
+
+ smiley = purple_smiley_new(shortcut, file_path);
+ g_free(file_path);
+
+ if (purple_smiley_list_add(smileys_list, smiley)) {
+ got_smileys++;
+ } else {
+ purple_debug_warning("smiley-custom",
+ "Couldn't add '%s' smiley", shortcut);
+ }
+ g_object_unref(smiley);
+ }
+
+ smiley_node = purple_xmlnode_get_next_twin(smiley_node);
+ }
+
+ purple_xmlnode_free(root_node);
+
+ purple_debug_info("smiley-custom", "Loaded %d custom smiley(s)",
+ got_smileys);
+}
+
+static PurpleXmlNode *
+smileys_to_xmlnode(void)
+{
+ PurpleXmlNode *root_node, *profile_node, *smileyset_node;
+ GList *smileys, *it;
+
+ root_node = purple_xmlnode_new("smileys");
+ purple_xmlnode_set_attrib(root_node, "version", "1.0");
+
+ profile_node = purple_xmlnode_new("profile");
+ purple_xmlnode_insert_child(root_node, profile_node);
+ purple_xmlnode_set_attrib(profile_node, "name", "Default");
+
+ smileyset_node = purple_xmlnode_new("smiley_set");
+ purple_xmlnode_insert_child(profile_node, smileyset_node);
+
+ smileys = purple_smiley_list_get_all(smileys_list);
+
+ for (it = smileys; it; it = g_list_next(it)) {
+ PurpleSmiley *smiley = PURPLE_SMILEY(it->data);
+ PurpleXmlNode *smiley_node;
+
+ smiley_node = purple_xmlnode_new("smiley");
+ purple_xmlnode_insert_child(smileyset_node, smiley_node);
+ purple_xmlnode_set_attrib(smiley_node, "shortcut",
+ purple_smiley_get_shortcut(smiley));
+ purple_xmlnode_set_attrib(smiley_node, "filename",
+ g_path_get_basename(purple_image_get_path(
+ purple_smiley_get_image(smiley))));
+ }
+
+ return root_node;
+}
+
+static void
+purple_smiley_custom_save(void)
+{
+ PurpleXmlNode *root_node;
+ gchar *data;
+ GError *error = NULL;
+
+ if (disable_write)
+ return;
+
+ root_node = smileys_to_xmlnode();
+ g_return_if_fail(root_node != NULL);
+
+ data = purple_xmlnode_to_formatted_str(root_node, NULL);
+ g_return_if_fail(data != NULL);
+
+ g_file_set_contents(smileys_index, data, -1, &error);
+
+ g_free(data);
+ purple_xmlnode_free(root_node);
+
+ if (error) {
+ purple_debug_error("smiley-custom",
+ "Error writting custom smileys xml file");
+ }
+}
+
+static gchar *
+purple_smiley_custom_img_checksum(PurpleImage *img)
+{
+ g_return_val_if_fail(PURPLE_IS_IMAGE(img), NULL);
+
+ return g_compute_checksum_for_data(G_CHECKSUM_SHA1,
+ purple_image_get_data(img),
+ purple_image_get_size(img));
+}
+
+
+/*******************************************************************************
+ * API implementation
+ ******************************************************************************/
+
+PurpleSmiley *
+purple_smiley_custom_add(PurpleImage *img, const gchar *shortcut)
+{
+ PurpleSmiley *existing_smiley, *smiley;
+ gchar *checksum, *file_path;
+ gchar file_name[256];
+ const gchar *file_ext;
+ gboolean succ;
+
+ g_return_val_if_fail(PURPLE_IS_IMAGE(img), NULL);
+
+ existing_smiley = purple_smiley_list_get_by_shortcut(
+ smileys_list, shortcut);
+
+ g_object_ref(img);
+
+ if (existing_smiley) {
+ disable_write = TRUE;
+ purple_smiley_custom_remove(existing_smiley);
+ disable_write = FALSE;
+ }
+
+ checksum = purple_smiley_custom_img_checksum(img);
+ file_ext = purple_image_get_extension(img);
+ if (file_ext == NULL || g_strcmp0("icon", file_ext) == 0) {
+ purple_debug_warning("smiley-custom", "Invalid image type");
+ return NULL;
+ }
+
+ g_snprintf(file_name, sizeof(file_name), "%s.%s", checksum, file_ext);
+ g_free(checksum);
+
+ file_path = g_build_filename(smileys_dir, file_name, NULL);
+
+ if (!purple_image_save(img, file_path)) {
+ purple_debug_error("smiley-custom", "Failed writing smiley "
+ "file %s", file_path);
+ g_free(file_path);
+ g_object_unref(img);
+ return NULL;
+ }
+ g_object_unref(img);
+
+ smiley = purple_smiley_new(shortcut, file_path);
+ g_free(file_path);
+ succ = purple_smiley_list_add(smileys_list, smiley);
+ g_object_unref(smiley);
+
+ if (!succ)
+ purple_debug_error("smiley-custom", "Failed adding a smiley");
+
+ purple_smiley_custom_save();
+
+ return smiley;
+}
+
+void
+purple_smiley_custom_remove(PurpleSmiley *smiley)
+{
+ PurpleSmiley *existing_smiley;
+ const gchar *smiley_shortcut, *path;
+ GList *other_smileys, *it;
+ gboolean is_unique;
+
+ g_return_if_fail(PURPLE_IS_SMILEY(smiley));
+
+ smiley_shortcut = purple_smiley_get_shortcut(smiley);
+ existing_smiley = purple_smiley_list_get_by_shortcut(
+ smileys_list, smiley_shortcut);
+ if (existing_smiley == NULL)
+ return;
+ if (existing_smiley != smiley) {
+ purple_debug_warning("smiley-custom", "Smiley is not in store");
+ return;
+ }
+
+ g_object_ref(smiley);
+ purple_smiley_list_remove(smileys_list, smiley);
+
+ path = purple_image_get_path(purple_smiley_get_image(smiley));
+
+ other_smileys = purple_smiley_list_get_unique(smileys_list);
+ is_unique = TRUE;
+ for (it = other_smileys; it; it = g_list_next(it)) {
+ PurpleSmiley *other = it->data;
+ const gchar *other_path = purple_image_get_path(
+ purple_smiley_get_image(other));
+ if (g_strcmp0(other_path, path) == 0) {
+ is_unique = FALSE;
+ break;
+ }
+ }
+ g_list_free(other_smileys);
+
+ if (is_unique)
+ g_unlink(path);
+
+ g_object_unref(smiley);
+
+ purple_smiley_custom_save();
+}
+
+PurpleSmileyList *
+purple_smiley_custom_get_list(void)
+{
+ return smileys_list;
+}
+
+
+/*******************************************************************************
+ * Custom smiley subsystem
+ ******************************************************************************/
+
+void
+_purple_smiley_custom_init(void)
+{
+ gint ret;
+
+ smileys_dir = g_build_filename(purple_user_dir(),
+ SMILEYS_DEFAULT_FOLDER, NULL);
+ smileys_index = g_build_filename(purple_user_dir(),
+ SMILEYS_INDEX_FILE, NULL);
+ smileys_list = purple_smiley_list_new();
+
+ ret = g_mkdir(smileys_dir, S_IRUSR | S_IWUSR | S_IXUSR);
+ if (ret != 0 && errno != EEXIST) {
+ purple_debug_error("smiley-custom", "Failed creating custom "
+ "smileys directory: %s (%d)", g_strerror(errno), errno);
+ }
+
+ purple_smiley_custom_load();
+}
+
+void
+_purple_smiley_custom_uninit(void)
+{
+ g_free(smileys_dir);
+ g_free(smileys_index);
+ g_object_unref(smileys_list);
+}
diff --git a/libpurple/smiley-custom.h b/libpurple/smiley-custom.h
new file mode 100644
index 0000000000..7c2183ed34
--- /dev/null
+++ b/libpurple/smiley-custom.h
@@ -0,0 +1,91 @@
+/* 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 _PURPLE_SMILEY_CUSTOM_H_
+#define _PURPLE_SMILEY_CUSTOM_H_
+/**
+ * SECTION:smiley-custom
+ * @include:smiley-custom.h
+ * @section_id: libpurple-smiley-custom
+ * @short_description: a persistent storage for user-defined smileys
+ * @title: Custom smileys storage
+ *
+ * A custom smiley is a non-standard (not defined by a particular protocol)
+ * #PurpleSmiley, defined by user. Some protocols support sending such smileys
+ * for other buddies, that do not have such image on their machine. Protocol
+ * that supports this feature should set the flag
+ * @PURPLE_CONNECTION_FLAG_ALLOW_CUSTOM_SMILEY of #PurpleConnectionFlags.
+ */
+
+#include "smiley.h"
+#include "smiley-list.h"
+
+/**
+ * purple_smiley_custom_add:
+ * @image: the smiley's image.
+ * @shortcut: textual representation of a smiley.
+ *
+ * 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.
+ */
+PurpleSmiley *
+purple_smiley_custom_add(PurpleImage *image, const gchar *shortcut);
+
+/**
+ * purple_smiley_custom_remove:
+ * @smiley: the smiley to be removed.
+ *
+ * Removes a @smiley from the store. If the @smiley file is unique (not used by
+ * other smileys) it will be removed from a disk.
+ */
+void
+purple_smiley_custom_remove(PurpleSmiley *smiley);
+
+/**
+ * purple_smiley_custom_get_list:
+ *
+ * Returns the whole list of user-configured custom smileys.
+ *
+ * Returns: a #PurpleSmileyList of custom smileys.
+ */
+PurpleSmileyList *
+purple_smiley_custom_get_list(void);
+
+/**
+ * _purple_smiley_custom_init: (skip)
+ *
+ * Initializes the custom smileys storage subsystem.
+ * Stability: Private
+ */
+void
+_purple_smiley_custom_init(void);
+
+/**
+ * _purple_smiley_custom_uninit: (skip)
+ *
+ * Uninitializes the custom smileys storage subsystem.
+ */
+void
+_purple_smiley_custom_uninit(void);
+
+#endif /* _PURPLE_SMILEY_CUSTOM_H_ */
diff --git a/libpurple/smiley-list.c b/libpurple/smiley-list.c
new file mode 100644
index 0000000000..1ababec288
--- /dev/null
+++ b/libpurple/smiley-list.c
@@ -0,0 +1,433 @@
+/* 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 "smiley-list.h"
+
+#include "dbus-maybe.h"
+#include "debug.h"
+#include "smiley-parser.h"
+#include "trie.h"
+
+#define PURPLE_SMILEY_LIST_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_SMILEY_LIST, \
+ PurpleSmileyListPrivate))
+
+typedef struct {
+ GList *smileys;
+ GList *smileys_end;
+ PurpleTrie *trie;
+ GHashTable *path_map;
+ GHashTable *shortcut_map;
+
+ gboolean drop_failed_remotes;
+} PurpleSmileyListPrivate;
+
+enum
+{
+ PROP_0,
+ PROP_DROP_FAILED_REMOTES,
+ PROP_LAST
+};
+
+static GObjectClass *parent_class;
+static GParamSpec *properties[PROP_LAST];
+
+static void
+_list_append2(GList **head_p, GList **tail_p, gpointer data)
+{
+ GList *head = *head_p;
+ GList *tail = *tail_p;
+ GList *elem;
+
+ g_return_if_fail((head == NULL) == (tail == NULL));
+ g_return_if_fail((tail == NULL) || (tail->next == NULL));
+
+ elem = g_list_alloc();
+ elem->data = data;
+ elem->prev = tail;
+ elem->next = NULL;
+
+ if (head) {
+ tail->next = elem;
+ *tail_p = elem;
+ } else
+ *head_p = *tail_p = elem;
+}
+
+static void
+_list_delete_link2(GList **head_p, GList **tail_p, GList *link)
+{
+ GList *head = *head_p;
+ GList *tail = *tail_p;
+
+ g_return_if_fail(head != NULL);
+ g_return_if_fail(tail != NULL);
+
+ if (link == tail)
+ *tail_p = tail->prev;
+ *head_p = g_list_delete_link(head, link);
+}
+
+static const gchar *
+smiley_get_uniqid(PurpleSmiley *smiley)
+{
+ return purple_image_get_path(purple_smiley_get_image(smiley));
+}
+
+/*******************************************************************************
+ * API implementation
+ ******************************************************************************/
+
+PurpleSmileyList *
+purple_smiley_list_new(void)
+{
+ return g_object_new(PURPLE_TYPE_SMILEY_LIST, NULL);
+}
+
+static void
+remote_smiley_failed(PurpleImage *smiley_img, gpointer _list)
+{
+ PurpleSmileyList *list = _list;
+ PurpleSmiley *smiley;
+
+ smiley = g_object_get_data(G_OBJECT(smiley_img),
+ "purple-smiley-list-smiley");
+
+ purple_debug_info("smiley-list", "remote smiley '%s' has failed, "
+ "removing it from the list...",
+ purple_smiley_get_shortcut(smiley));
+
+ purple_smiley_list_remove(list, smiley);
+}
+
+gboolean
+purple_smiley_list_add(PurpleSmileyList *list, PurpleSmiley *smiley)
+{
+ PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(list);
+ PurpleImage *smiley_img;
+ const gchar *smiley_path;
+ gboolean succ;
+ gchar *shortcut_escaped;
+ const gchar *shortcut;
+
+ g_return_val_if_fail(priv != NULL, FALSE);
+ g_return_val_if_fail(PURPLE_IS_SMILEY(smiley), FALSE);
+
+ if (g_object_get_data(G_OBJECT(smiley), "purple-smiley-list") != NULL) {
+ purple_debug_warning("smiley-list",
+ "smiley is already associated with some list");
+ return FALSE;
+ }
+
+ shortcut = purple_smiley_get_shortcut(smiley);
+
+ if (g_hash_table_lookup(priv->shortcut_map, shortcut) != NULL)
+ return FALSE;
+
+ shortcut_escaped = g_markup_escape_text(shortcut, -1);
+ succ = purple_trie_add(priv->trie, shortcut_escaped, smiley);
+
+ /* A special-case for WebKit, which unescapes apos entity.
+ * Please, don't trust this hack - it may be removed in future releases.
+ */
+ if (succ && strstr(shortcut_escaped, "&apos;") != NULL) {
+ gchar *tmp = shortcut_escaped;
+ shortcut_escaped = purple_strreplace(shortcut_escaped,
+ "&apos;", "'");
+ g_free(tmp);
+ succ = purple_trie_add(priv->trie, shortcut_escaped, smiley);
+ }
+
+ g_free(shortcut_escaped);
+ if (!succ)
+ return FALSE;
+
+ g_object_ref(smiley);
+ _list_append2(&priv->smileys, &priv->smileys_end, smiley);
+ g_object_set_data(G_OBJECT(smiley), "purple-smiley-list", list);
+ g_object_set_data(G_OBJECT(smiley), "purple-smiley-list-elem",
+ priv->smileys_end);
+
+ g_hash_table_insert(priv->shortcut_map, g_strdup(shortcut), smiley);
+
+ smiley_img = purple_smiley_get_image(smiley);
+ if (priv->drop_failed_remotes && !purple_image_is_ready(smiley_img)) {
+ g_object_set_data(G_OBJECT(smiley_img),
+ "purple-smiley-list-smiley", smiley);
+ g_signal_connect_object(smiley_img, "failed",
+ G_CALLBACK(remote_smiley_failed), list, 0);
+ }
+
+ smiley_path = smiley_get_uniqid(smiley);
+
+ /* TODO: add to the table, when the smiley sets the path */
+ if (!smiley_path)
+ return TRUE;
+
+ if (g_hash_table_lookup(priv->path_map, smiley_path) == NULL) {
+ g_hash_table_insert(priv->path_map,
+ g_strdup(smiley_path), smiley);
+ }
+
+ return TRUE;
+}
+
+void
+purple_smiley_list_remove(PurpleSmileyList *list, PurpleSmiley *smiley)
+{
+ PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(list);
+ GList *list_elem, *it;
+ const gchar *shortcut, *path;
+ gchar *shortcut_escaped;
+
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(PURPLE_IS_SMILEY(smiley));
+
+ if (g_object_get_data(G_OBJECT(smiley), "purple-smiley-list") != list) {
+ purple_debug_warning("smiley-list", "remove: invalid list");
+ return;
+ }
+
+ list_elem = g_object_get_data(G_OBJECT(smiley),
+ "purple-smiley-list-elem");
+
+ shortcut = purple_smiley_get_shortcut(smiley);
+ path = smiley_get_uniqid(smiley);
+
+ g_hash_table_remove(priv->shortcut_map, shortcut);
+ if (path)
+ g_hash_table_remove(priv->path_map, path);
+
+ shortcut_escaped = g_markup_escape_text(shortcut, -1);
+ purple_trie_remove(priv->trie, shortcut);
+ g_free(shortcut_escaped);
+
+ _list_delete_link2(&priv->smileys, &priv->smileys_end, list_elem);
+
+ /* re-add entry to path_map if smiley was not unique */
+ for (it = priv->smileys; it && path; it = g_list_next(it)) {
+ PurpleSmiley *smiley = it->data;
+
+ if (g_strcmp0(smiley_get_uniqid(smiley), path) == 0) {
+ g_hash_table_insert(priv->path_map,
+ g_strdup(path), smiley);
+ break;
+ }
+ }
+
+ g_object_set_data(G_OBJECT(smiley), "purple-smiley-list", NULL);
+ g_object_set_data(G_OBJECT(smiley), "purple-smiley-list-elem", NULL);
+ g_object_unref(smiley);
+}
+
+gboolean
+purple_smiley_list_is_empty(PurpleSmileyList *list)
+{
+ PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(list);
+
+ g_return_val_if_fail(priv != NULL, TRUE);
+
+ return (priv->smileys == NULL);
+}
+
+PurpleSmiley *
+purple_smiley_list_get_by_shortcut(PurpleSmileyList *list,
+ const gchar *shortcut)
+{
+ PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(list);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return g_hash_table_lookup(priv->shortcut_map, shortcut);
+}
+
+PurpleTrie *
+purple_smiley_list_get_trie(PurpleSmileyList *list)
+{
+ PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(list);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->trie;
+}
+
+GList *
+purple_smiley_list_get_unique(PurpleSmileyList *list)
+{
+ GList *unique = NULL, *it;
+ GHashTable *unique_map;
+ PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(list);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ /* We could just return g_hash_table_get_values(priv->path_map) here,
+ * but it won't be in order. */
+
+ unique_map = g_hash_table_new(g_str_hash, g_str_equal);
+
+ for (it = priv->smileys; it; it = g_list_next(it)) {
+ PurpleSmiley *smiley = it->data;
+ const gchar *path = smiley_get_uniqid(smiley);
+
+ if (g_hash_table_lookup(unique_map, path))
+ continue;
+
+ unique = g_list_prepend(unique, smiley);
+ g_hash_table_insert(unique_map, (gpointer)path, smiley);
+ }
+
+ g_hash_table_destroy(unique_map);
+
+ return g_list_reverse(unique);
+}
+
+GList *
+purple_smiley_list_get_all(PurpleSmileyList *list)
+{
+ PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(list);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return g_hash_table_get_values(priv->shortcut_map);
+}
+
+
+/*******************************************************************************
+ * Object stuff
+ ******************************************************************************/
+
+static void
+purple_smiley_list_init(GTypeInstance *instance, gpointer klass)
+{
+ PurpleSmileyList *sl = PURPLE_SMILEY_LIST(instance);
+ PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(sl);
+
+ priv->trie = purple_trie_new();
+ priv->path_map = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, NULL);
+ priv->shortcut_map = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, NULL);
+
+ PURPLE_DBUS_REGISTER_POINTER(sl, PurpleSmileyList);
+}
+
+static void
+purple_smiley_list_finalize(GObject *obj)
+{
+ PurpleSmileyList *sl = PURPLE_SMILEY_LIST(obj);
+ PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(sl);
+ GList *it;
+
+ g_object_unref(priv->trie);
+ g_hash_table_destroy(priv->path_map);
+ g_hash_table_destroy(priv->shortcut_map);
+
+ for (it = priv->smileys; it; it = g_list_next(it)) {
+ PurpleSmiley *smiley = it->data;
+ g_object_set_data(G_OBJECT(smiley), "purple-smiley-list", NULL);
+ g_object_set_data(G_OBJECT(smiley), "purple-smiley-list-elem", NULL);
+ g_object_unref(smiley);
+ }
+ g_list_free(priv->smileys);
+
+ PURPLE_DBUS_UNREGISTER_POINTER(sl);
+
+ G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+static void
+purple_smiley_list_get_property(GObject *object, guint par_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleSmileyList *sl = PURPLE_SMILEY_LIST(object);
+ PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(sl);
+
+ switch (par_id) {
+ case PROP_DROP_FAILED_REMOTES:
+ g_value_set_boolean(value, priv->drop_failed_remotes);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, par_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_smiley_list_set_property(GObject *object, guint par_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleSmileyList *sl = PURPLE_SMILEY_LIST(object);
+ PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(sl);
+
+ switch (par_id) {
+ case PROP_DROP_FAILED_REMOTES:
+ priv->drop_failed_remotes = g_value_get_boolean(value);
+ /* XXX: we could scan for remote smiley's on our list
+ * and change the setting, but we don't care that much.
+ */
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, par_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_smiley_list_class_init(PurpleSmileyListClass *klass)
+{
+ GObjectClass *gobj_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ g_type_class_add_private(klass, sizeof(PurpleSmileyListPrivate));
+
+ gobj_class->get_property = purple_smiley_list_get_property;
+ gobj_class->set_property = purple_smiley_list_set_property;
+ gobj_class->finalize = purple_smiley_list_finalize;
+
+ properties[PROP_DROP_FAILED_REMOTES] = g_param_spec_boolean(
+ "drop-failed-remotes", "Drop failed PurpleRemoteSmileys",
+ "Watch added remote smileys and remove them from the list, "
+ "if they change their state to failed", FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(gobj_class, PROP_LAST, properties);
+}
+
+GType
+purple_smiley_list_get_type(void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY(type == 0)) {
+ static const GTypeInfo info = {
+ .class_size = sizeof(PurpleSmileyListClass),
+ .class_init = (GClassInitFunc)purple_smiley_list_class_init,
+ .instance_size = sizeof(PurpleSmileyList),
+ .instance_init = purple_smiley_list_init,
+ };
+
+ type = g_type_register_static(G_TYPE_OBJECT,
+ "PurpleSmileyList", &info, 0);
+ }
+
+ return type;
+}
diff --git a/libpurple/smiley-list.h b/libpurple/smiley-list.h
new file mode 100644
index 0000000000..59ebd3bec1
--- /dev/null
+++ b/libpurple/smiley-list.h
@@ -0,0 +1,193 @@
+/* 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 _PURPLE_SMILEY_LIST_H_
+#define _PURPLE_SMILEY_LIST_H_
+/**
+ * SECTION:smiley-list
+ * @include:smiley-list.h
+ * @section_id: libpurple-smiley-list
+ * @short_description: a collection of smileys
+ * @title: Smiley lists
+ *
+ * A #PurpleSmileyList is a handy storage for a set of #PurpleSmiley's.
+ * It holds structures needed for parsing, accessing them by a shortcut, or
+ * listing (either all, or by unique image).
+ */
+
+#include <glib-object.h>
+
+#include "smiley.h"
+#include "trie.h"
+
+typedef struct _PurpleSmileyList PurpleSmileyList;
+typedef struct _PurpleSmileyListClass PurpleSmileyListClass;
+
+#define PURPLE_TYPE_SMILEY_LIST (purple_smiley_list_get_type())
+#define PURPLE_SMILEY_LIST(smiley) (G_TYPE_CHECK_INSTANCE_CAST((smiley), PURPLE_TYPE_SMILEY_LIST, PurpleSmileyList))
+#define PURPLE_SMILEY_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_SMILEY_LIST, PurpleSmileyListClass))
+#define PURPLE_IS_SMILEY_LIST(smiley) (G_TYPE_CHECK_INSTANCE_TYPE((smiley), PURPLE_TYPE_SMILEY_LIST))
+#define PURPLE_IS_SMILEY_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_SMILEY_LIST))
+#define PURPLE_SMILEY_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_SMILEY_LIST, PurpleSmileyListClass))
+
+/**
+ * PurpleSmileyList:
+ *
+ * A container for #PurpleSmiley's.
+ */
+struct _PurpleSmileyList
+{
+ /*< private >*/
+ GObject parent;
+};
+
+/**
+ * PurpleSmileyListClass:
+ *
+ * Base class for #PurpleSmileyList objects.
+ */
+struct _PurpleSmileyListClass
+{
+ /*< private >*/
+ GObjectClass parent_class;
+
+ void (*purple_reserved1)(void);
+ void (*purple_reserved2)(void);
+ void (*purple_reserved3)(void);
+ void (*purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+/**
+ * purple_smiley_list_get_type:
+ *
+ * Returns: the #GType for a smiley list.
+ */
+GType
+purple_smiley_list_get_type(void);
+
+/**
+ * purple_smiley_list_new:
+ *
+ * Creates new #PurpleSmileyList. You might prefer using existing lists, like
+ * #purple_smiley_custom_get_list or #purple_conversation_get_remote_smileys
+ * (the latter is read-only, accessed with
+ * #purple_conversation_add_remote_smiley and
+ * #purple_conversation_get_remote_smiley).
+ *
+ * Returns: newly created #PurpleSmileyList.
+ */
+PurpleSmileyList *
+purple_smiley_list_new(void);
+
+/**
+ * purple_smiley_list_add:
+ * @list: the smiley list.
+ * @smiley: the smiley to be added.
+ *
+ * Adds the @smiley to the @list. A particular @smiley can only be added to
+ * a single @list. Also, a @list can not contain multiple @smiley's with
+ * the same shortcut.
+ *
+ * It increases the reference count of @smiley, if needed.
+ *
+ * Returns: %TRUE if the @smiley was successfully added to the list.
+ */
+gboolean
+purple_smiley_list_add(PurpleSmileyList *list, PurpleSmiley *smiley);
+
+/**
+ * purple_smiley_list_remove:
+ * @list: the smiley list.
+ * @smiley: the smiley to be removed.
+ *
+ * Removes a @smiley from the @list. If @smiley's reference count drops to zero,
+ * it's destroyed.
+ */
+void
+purple_smiley_list_remove(PurpleSmileyList *list, PurpleSmiley *smiley);
+
+/**
+ * purple_smiley_list_is_empty:
+ * @list: the smiley list.
+ *
+ * Checks, if the smiley @list is empty.
+ *
+ * Returns: %TRUE if the @list is empty, %FALSE otherwise.
+ */
+gboolean
+purple_smiley_list_is_empty(PurpleSmileyList *list);
+
+/**
+ * purple_smiley_list_get_by_shortcut:
+ * @list: the smiley list.
+ * @shortcut: the textual representation of smiley to lookup.
+ *
+ * Retrieves a smiley with the specified @shortcut from the @list.
+ *
+ * Returns: a #PurpleSmiley if the smiley was found, %NULL otherwise.
+ */
+PurpleSmiley *
+purple_smiley_list_get_by_shortcut(PurpleSmileyList *list,
+ const gchar *shortcut);
+
+/**
+ * purple_smiley_list_get_trie:
+ * @list: the smiley list.
+ *
+ * Returns the #PurpleTrie for searching of #PurpleSmiley's being kept
+ * in the @list. It holds markup escaped shortcuts, so if you want to search
+ * in plain message, you have to escape it first. If you don't do this, it won't
+ * find smileys containing special characters.
+ *
+ * Returns: (transfer none): a #PurpleTrie for contained smileys.
+ */
+PurpleTrie *
+purple_smiley_list_get_trie(PurpleSmileyList *list);
+
+/**
+ * purple_smiley_list_get_unique:
+ * @list_: the smiley list.
+ *
+ * Returns the list of smileys with unique image file paths.
+ *
+ * Returns: (transfer container): the #GList of unique smileys. Use #g_list_free
+ * when done using it.
+ */
+GList *
+purple_smiley_list_get_unique(PurpleSmileyList *list_);
+
+/**
+ * purple_smiley_list_get_all:
+ * @list_: the smiley list.
+ *
+ * Returns the list of all smileys added to the @list_.
+ *
+ * Returns: (transfer container): the #GList of smileys. Use #g_list_free
+ * when done using it.
+ */
+GList *
+purple_smiley_list_get_all(PurpleSmileyList *list_);
+
+G_END_DECLS
+
+#endif /* _PURPLE_SMILEY_H_ */
diff --git a/libpurple/smiley-parser.c b/libpurple/smiley-parser.c
new file mode 100644
index 0000000000..05b2e1c3d5
--- /dev/null
+++ b/libpurple/smiley-parser.c
@@ -0,0 +1,262 @@
+/* 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 "smiley-parser.h"
+
+#include "smiley-custom.h"
+#include "smiley-theme.h"
+
+#define DISPLAY_OUR_CUSTOM_SMILEYS_FOR_INCOMING_MESSAGES 1
+
+typedef struct
+{
+ union {
+ struct {
+ PurpleConversation *conv;
+ PurpleSmileyParseCb cb;
+ gpointer ui_data;
+ } replace;
+ struct {
+ GHashTable *found_smileys;
+ } find;
+ } job;
+
+ gboolean in_html_tag;
+} PurpleSmileyParseData;
+
+static PurpleTrie *html_sentry;
+
+static inline void
+purple_smiley_parse_check_html(const gchar *word,
+ PurpleSmileyParseData *parse_data)
+{
+ if (G_LIKELY(word[0] == '<'))
+ parse_data->in_html_tag = TRUE;
+ else if (G_LIKELY(word[0] == '>'))
+ parse_data->in_html_tag = FALSE;
+ else
+ g_return_if_reached();
+ g_warn_if_fail(word[1] == '\0');
+}
+
+static gboolean
+purple_smiley_parse_cb(GString *out, const gchar *word, gpointer _smiley,
+ gpointer _parse_data)
+{
+ PurpleSmileyParseData *parse_data = _parse_data;
+ PurpleSmiley *smiley = _smiley;
+
+ /* a special-case for html_sentry */
+ if (smiley == NULL) {
+ purple_smiley_parse_check_html(word, parse_data);
+ return FALSE;
+ }
+
+ if (parse_data->in_html_tag)
+ return FALSE;
+
+ return parse_data->job.replace.cb(out, smiley,
+ parse_data->job.replace.conv, parse_data->job.replace.ui_data);
+}
+
+/* XXX: this shouldn't be a conv for incoming messages - see
+ * PurpleConversationPrivate.remote_smileys.
+ * For outgoing messages, we could pass conv in ui_data (or something).
+ * Or maybe we should use two distinct functions?
+ * To be reconsidered when we had PurpleDude-like objects.
+ */
+gchar *
+purple_smiley_parser_smileify(PurpleConversation *conv, const gchar *html_message,
+ gboolean use_remote_smileys, PurpleSmileyParseCb cb, gpointer ui_data)
+{
+ PurpleSmileyTheme *theme;
+ PurpleSmileyList *theme_smileys = NULL, *remote_smileys = NULL;
+ PurpleTrie *theme_trie = NULL, *custom_trie = NULL, *remote_trie = NULL;
+ GSList *tries = NULL;
+ GSList tries_sentry, tries_theme, tries_custom, tries_remote;
+ PurpleSmileyParseData parse_data;
+
+ if (html_message == NULL || html_message[0] == '\0')
+ return g_strdup(html_message);
+
+ /* get remote smileys */
+ if (use_remote_smileys)
+ remote_smileys = purple_conversation_get_remote_smileys(conv);
+ if (remote_smileys)
+ remote_trie = purple_smiley_list_get_trie(remote_smileys);
+ if (remote_trie && purple_trie_get_size(remote_trie) == 0)
+ remote_trie = NULL;
+
+ /* get custom smileys */
+ if (purple_conversation_get_features(conv) &
+ PURPLE_CONNECTION_FLAG_ALLOW_CUSTOM_SMILEY)
+ {
+ custom_trie = purple_smiley_list_get_trie(
+ purple_smiley_custom_get_list());
+#if !DISPLAY_OUR_CUSTOM_SMILEYS_FOR_INCOMING_MESSAGES
+ if (use_remote_smileys)
+ custom_trie = NULL;
+#endif
+ }
+ if (custom_trie && purple_trie_get_size(custom_trie) == 0)
+ custom_trie = NULL;
+
+ /* get theme smileys */
+ theme = purple_smiley_theme_get_current();
+ if (theme != NULL)
+ theme_smileys = purple_smiley_theme_get_smileys(theme, ui_data);
+ if (theme_smileys != NULL)
+ theme_trie = purple_smiley_list_get_trie(theme_smileys);
+
+ /* we have absolutely no smileys */
+ if (theme_trie == NULL && custom_trie == NULL && remote_trie == NULL)
+ return g_strdup(html_message);
+
+ /* Create a tries list on the stack. */
+ tries_sentry.data = html_sentry;
+ tries_theme.data = theme_trie;
+ tries_custom.data = custom_trie;
+ tries_remote.data = remote_trie;
+ tries_sentry.next = NULL;
+ tries_theme.next = tries_custom.next = tries_remote.next = NULL;
+ tries = &tries_sentry;
+ if (remote_trie != NULL)
+ g_slist_last(tries)->next = &tries_remote;
+ if (custom_trie != NULL)
+ g_slist_last(tries)->next = &tries_custom;
+ if (theme_trie != NULL)
+ g_slist_last(tries)->next = &tries_theme;
+
+ parse_data.job.replace.conv = conv;
+ parse_data.job.replace.cb = cb;
+ parse_data.job.replace.ui_data = ui_data;
+ parse_data.in_html_tag = FALSE;
+
+ /* TODO: parse greedily (as much as possible) when PurpleTrie
+ * provides support for it. */
+ return purple_trie_multi_replace(tries, html_message,
+ purple_smiley_parse_cb, &parse_data);
+}
+
+gchar *
+purple_smiley_parser_replace(PurpleSmileyList *smileys,
+ const gchar *html_message, PurpleSmileyParseCb cb, gpointer ui_data)
+{
+ PurpleTrie *smileys_trie = NULL;
+ GSList trie_1, trie_2;
+ PurpleSmileyParseData parse_data;
+
+ if (html_message == NULL || html_message[0] == '\0')
+ return g_strdup(html_message);
+
+ if (smileys == NULL || purple_smiley_list_is_empty(smileys))
+ return g_strdup(html_message);
+
+ smileys_trie = purple_smiley_list_get_trie(smileys);
+ g_return_val_if_fail(smileys_trie != NULL, NULL);
+
+ trie_1.data = html_sentry;
+ trie_2.data = smileys_trie;
+ trie_1.next = &trie_2;
+ trie_2.next = NULL;
+
+ parse_data.job.replace.conv = NULL;
+ parse_data.job.replace.cb = cb;
+ parse_data.job.replace.ui_data = ui_data;
+ parse_data.in_html_tag = FALSE;
+
+ return purple_trie_multi_replace(&trie_1, html_message,
+ purple_smiley_parse_cb, &parse_data);
+}
+
+static gboolean
+smiley_find_cb(const gchar *word, gpointer _smiley, gpointer _parse_data)
+{
+ PurpleSmiley *smiley = _smiley;
+ PurpleSmileyParseData *parse_data = _parse_data;
+
+ /* a special-case for html_sentry */
+ if (smiley == NULL) {
+ purple_smiley_parse_check_html(word, parse_data);
+ return FALSE;
+ }
+
+ if (parse_data->in_html_tag)
+ return FALSE;
+
+ g_hash_table_insert(parse_data->job.find.found_smileys, smiley, smiley);
+
+ return TRUE;
+}
+
+GList *
+purple_smiley_parser_find(PurpleSmileyList *smileys, const gchar *message,
+ gboolean is_html)
+{
+ PurpleTrie *smileys_trie;
+ GList *found_list;
+ gchar *escaped_message = NULL;
+ PurpleSmileyParseData parse_data;
+ GSList trie_1, trie_2;
+
+ if (message == NULL || message[0] == '\0')
+ return NULL;
+
+ if (smileys == NULL || purple_smiley_list_is_empty(smileys))
+ return NULL;
+
+ smileys_trie = purple_smiley_list_get_trie(smileys);
+ g_return_val_if_fail(smileys_trie != NULL, NULL);
+
+ if (!is_html)
+ message = escaped_message = g_markup_escape_text(message, -1);
+
+ parse_data.job.find.found_smileys =
+ g_hash_table_new(g_direct_hash, g_direct_equal);
+ parse_data.in_html_tag = FALSE;
+
+ trie_1.data = html_sentry;
+ trie_2.data = smileys_trie;
+ trie_1.next = &trie_2;
+ trie_2.next = NULL;
+ purple_trie_multi_find(&trie_1, message, smiley_find_cb, &parse_data);
+
+ g_free(escaped_message);
+
+ found_list = g_hash_table_get_values(parse_data.job.find.found_smileys);
+ g_hash_table_destroy(parse_data.job.find.found_smileys);
+
+ return found_list;
+}
+
+void
+_purple_smiley_parser_init(void)
+{
+ html_sentry = purple_trie_new();
+ purple_trie_add(html_sentry, "<", NULL);
+ purple_trie_add(html_sentry, ">", NULL);
+}
+
+void
+_purple_smiley_parser_uninit(void)
+{
+ g_object_unref(html_sentry);
+}
diff --git a/libpurple/smiley-parser.h b/libpurple/smiley-parser.h
new file mode 100644
index 0000000000..e258d4222c
--- /dev/null
+++ b/libpurple/smiley-parser.h
@@ -0,0 +1,138 @@
+/* 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 _PURPLE_SMILEY_PARSER_H_
+#define _PURPLE_SMILEY_PARSER_H_
+/**
+ * SECTION:smiley-parser
+ * @include:smiley-parser.h
+ * @section_id: libpurple-smiley-parser
+ * @short_description: a efficient smiley processor
+ * @title: Smiley parser
+ *
+ * This module is a fast and easy method for searching (and optionally replacing)
+ * #PurpleSmiley's in a text. It may use all suitable smiley sets to smileyify
+ * the message in one step. The priority if always the following: remote
+ * smileys > local custom smileys > theme smileys.
+ */
+
+#include "purple.h"
+
+/**
+ * PurpleSmileyParseCb:
+ * @out: the message buffer.
+ * @smiley: found smiley.
+ * @conv: the conversation of a message (or %NULL, if not passed).
+ * @ui_data: the data being passed to #purple_smiley_parse.
+ *
+ * A replace callback for the found @smiley. It should append a HTML tag
+ * representing the @smiley to the @out string. It must not modify the
+ * @out string in other way than appending to its end.
+ *
+ * If callback decides not to replace a smiley, it must not modify
+ * @out in any way.
+ *
+ * Returns: %TRUE if the smiley was inserted.
+ */
+typedef gboolean (*PurpleSmileyParseCb)(GString *out, PurpleSmiley *smiley,
+ PurpleConversation *conv, gpointer ui_data);
+
+/**
+ * purple_smiley_parser_smileify:
+ * @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.
+ * @ui_data: the user data to be passed to @cb and
+ * #purple_smiley_theme_get_smileys.
+ *
+ * Replaces all textual smiley representations with proper smiley images
+ * configured for libpurple.
+ *
+ * The @use_remote_smileys parameter should be %TRUE for incoming messages,
+ * %FALSE for outgoing.
+ *
+ * This function is intended for replacing all smileys before displaying. For
+ * replacing custom smileys before sending to the other party,
+ * see #purple_smiley_parser_replace.
+ *
+ * Returns: (transfer full): the smileifed message. Should be #g_free'd when
+ * done using it. Returns %NULL if and only if @html_message was %NULL.
+ */
+gchar *
+purple_smiley_parser_smileify(PurpleConversation *conv, const gchar *html_message,
+ gboolean use_remote_smileys, PurpleSmileyParseCb cb, gpointer ui_data);
+
+/**
+ * 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.
+ *
+ * Replaces all textual smiley representations from @smileys list with images.
+ *
+ * This function is intended for replacing custom smileys before sending to the
+ * other party. For replacing all sets of smileys (custom, remote and theme) at
+ * once (ie. before displaying), use #purple_smiley_parser_smileify.
+ *
+ * Returns: (transfer full): the smileifed message. Should be #g_free'd when
+ * done using it. Returns %NULL if and only if @html_message was %NULL.
+ */
+gchar *
+purple_smiley_parser_replace(PurpleSmileyList *smileys,
+ const gchar *html_message, PurpleSmileyParseCb cb, gpointer ui_data);
+
+/**
+ * purple_smiley_parser_find:
+ * @smileys: the list of smileys to find.
+ * @message: the message.
+ * @is_html: %TRUE if the message is HTML, %FALSE if it's plain, unescaped.
+ *
+ * Searches for all smileys from the @smileys list present in @message.
+ * Each smiley is returned only once, regardless how many times it appeared in
+ * text. However, distinct smileys may share common image file (thus, their
+ * paths will be the same).
+ *
+ * Returns: (transfer container): the #GList of found smileys. Use #g_list_free
+ * when no longer need it.
+ */
+GList *
+purple_smiley_parser_find(PurpleSmileyList *smileys, const gchar *message,
+ gboolean is_html);
+
+/**
+ * _purple_smiley_parser_init: (skip)
+ *
+ * Initializes the smileys parser subsystem.
+ */
+void
+_purple_smiley_parser_init(void);
+
+/**
+ * _purple_smiley_parser_uninit: (skip)
+ *
+ * Uninitializes the smileys parser subsystem.
+ */
+void
+_purple_smiley_parser_uninit(void);
+
+#endif /* _PURPLE_SMILEY_PARSER_H_ */
diff --git a/libpurple/smiley-theme.c b/libpurple/smiley-theme.c
new file mode 100644
index 0000000000..7f43f43624
--- /dev/null
+++ b/libpurple/smiley-theme.c
@@ -0,0 +1,97 @@
+/* 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 "smiley-theme.h"
+
+static PurpleSmileyTheme *current = NULL;
+
+/*******************************************************************************
+ * API implementation
+ ******************************************************************************/
+
+PurpleSmileyList *
+purple_smiley_theme_get_smileys(PurpleSmileyTheme *theme, gpointer ui_data)
+{
+ PurpleSmileyThemeClass *klass;
+
+ g_return_val_if_fail(PURPLE_IS_SMILEY_THEME(theme), NULL);
+ klass = PURPLE_SMILEY_THEME_GET_CLASS(theme);
+ g_return_val_if_fail(klass != NULL, NULL);
+ g_return_val_if_fail(klass->get_smileys != NULL, NULL);
+
+ return klass->get_smileys(theme, ui_data);
+}
+
+void
+purple_smiley_theme_set_current(PurpleSmileyTheme *theme)
+{
+ PurpleSmileyThemeClass *klass;
+
+ g_return_if_fail(theme == NULL || PURPLE_IS_SMILEY_THEME(theme));
+
+ if (theme)
+ g_object_ref(theme);
+ if (current)
+ g_object_unref(current);
+ current = theme;
+
+ if (!theme)
+ return;
+ klass = PURPLE_SMILEY_THEME_GET_CLASS(theme);
+ g_return_if_fail(klass != NULL);
+ if (klass->activate)
+ klass->activate(theme);
+}
+
+PurpleSmileyTheme *
+purple_smiley_theme_get_current(void)
+{
+ return current;
+}
+
+void
+_purple_smiley_theme_uninit(void)
+{
+ purple_smiley_theme_set_current(NULL);
+}
+
+
+/*******************************************************************************
+ * Object stuff
+ ******************************************************************************/
+
+GType
+purple_smiley_theme_get_type(void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY(type == 0)) {
+ static const GTypeInfo info = {
+ .class_size = sizeof(PurpleSmileyThemeClass),
+ .instance_size = sizeof(PurpleSmileyTheme),
+ };
+
+ type = g_type_register_static(G_TYPE_OBJECT,
+ "PurpleSmileyTheme", &info, G_TYPE_FLAG_ABSTRACT);
+ }
+
+ return type;
+}
diff --git a/libpurple/smiley-theme.h b/libpurple/smiley-theme.h
new file mode 100644
index 0000000000..909be8cc07
--- /dev/null
+++ b/libpurple/smiley-theme.h
@@ -0,0 +1,144 @@
+/* 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 _PURPLE_SMILEY_THEME_H_
+#define _PURPLE_SMILEY_THEME_H_
+/**
+ * SECTION:smiley-theme
+ * @include:smiley-theme.h
+ * @section_id: libpurple-smiley-theme
+ * @short_description: a categorized set of standard smileys
+ * @title: Smiley themes
+ *
+ * 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.
+ */
+
+#include <glib-object.h>
+
+#include "smiley.h"
+#include "smiley-list.h"
+
+typedef struct _PurpleSmileyTheme PurpleSmileyTheme;
+typedef struct _PurpleSmileyThemeClass PurpleSmileyThemeClass;
+
+#define PURPLE_TYPE_SMILEY_THEME (purple_smiley_theme_get_type())
+#define PURPLE_SMILEY_THEME(smiley) (G_TYPE_CHECK_INSTANCE_CAST((smiley), PURPLE_TYPE_SMILEY_THEME, PurpleSmileyTheme))
+#define PURPLE_SMILEY_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_SMILEY_THEME, PurpleSmileyThemeClass))
+#define PURPLE_IS_SMILEY_THEME(smiley) (G_TYPE_CHECK_INSTANCE_TYPE((smiley), PURPLE_TYPE_SMILEY_THEME))
+#define PURPLE_IS_SMILEY_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_SMILEY_THEME))
+#define PURPLE_SMILEY_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_SMILEY_THEME, PurpleSmileyThemeClass))
+
+/**
+ * PurpleSmileyTheme:
+ *
+ * An abstract class for smiley theme.
+ */
+struct _PurpleSmileyTheme
+{
+ /*< private >*/
+ GObject parent;
+};
+
+/**
+ * PurpleSmileyThemeClass:
+ * @get_smileys: a callback for getting smiley list based on choosen category.
+ * The criteria for a category are being passed using the
+ * @ui_data parameter.
+ * @activate: a callback being fired after activating the @theme. It may be used
+ * for loading its contents before using @get_smileys callback.
+ *
+ * Base class for #PurpleSmileyTheme objects.
+ */
+struct _PurpleSmileyThemeClass
+{
+ /*< private >*/
+ GObjectClass parent_class;
+
+ /*< public >*/
+ PurpleSmileyList * (*get_smileys)(PurpleSmileyTheme *theme,
+ gpointer ui_data);
+ void (*activate)(PurpleSmileyTheme *theme);
+
+ /*< private >*/
+ void (*purple_reserved1)(void);
+ void (*purple_reserved2)(void);
+ void (*purple_reserved3)(void);
+ void (*purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+/**
+ * purple_smiley_theme_get_type:
+ *
+ * Returns: the #GType for a smiley list.
+ */
+GType
+purple_smiley_theme_get_type(void);
+
+/**
+ * purple_smiley_theme_get_smileys:
+ * @theme: the smiley theme.
+ * @ui_data: the UI-passed criteria to choose a smiley set.
+ *
+ * Retrieves a smiley category based on UI-provided criteria.
+ *
+ * You might want to use <link linkend="libpurple-smiley-parser">smiley
+ * parser</link> instead. It's mostly for the UI, prpls shouldn't use it.
+ *
+ * Returns: (transfer none): a #PurpleSmileyList with standard smileys to use.
+ */
+PurpleSmileyList *
+purple_smiley_theme_get_smileys(PurpleSmileyTheme *theme, gpointer ui_data);
+
+/**
+ * purple_smiley_theme_set_current:
+ * @theme: the smiley theme to be set as currently used. May be %NULL.
+ *
+ * Sets the new smiley theme to be used for displaying messages.
+ */
+void
+purple_smiley_theme_set_current(PurpleSmileyTheme *theme);
+
+/**
+ * purple_smiley_theme_get_current:
+ *
+ * Returns the currently used smiley theme.
+ *
+ * Returns: the #PurpleSmileyTheme or %NULL, if none is set.
+ */
+PurpleSmileyTheme *
+purple_smiley_theme_get_current(void);
+
+/**
+ * _purple_smiley_theme_uninit: (skip)
+ *
+ * Uninitializes the smileys theme subsystem.
+ */
+void
+_purple_smiley_theme_uninit(void);
+
+G_END_DECLS
+
+#endif /* _PURPLE_SMILEY_THEME_H_ */
diff --git a/libpurple/smiley.c b/libpurple/smiley.c
index bb7acd037a..7f5aa59a5f 100644
--- a/libpurple/smiley.c
+++ b/libpurple/smiley.c
@@ -1,8 +1,3 @@
-/**
- * @file smiley.c Simley API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -25,402 +20,179 @@
*/
#include "internal.h"
+#include "glibcompat.h"
#include "dbus-maybe.h"
#include "debug.h"
-#include "imgstore.h"
#include "smiley.h"
#include "util.h"
#include "xmlnode.h"
-/**************************************************************************/
-/* Main structures, members and constants */
-/**************************************************************************/
+#define PURPLE_SMILEY_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_SMILEY, PurpleSmileyPrivate))
-struct _PurpleSmiley
-{
- GObject parent;
- PurpleStoredImage *img; /**< The id of the stored image with the
- the smiley data. */
- char *shortcut; /**< Shortcut associated with the custom
- smiley. This field will work as a
- unique key by this API. */
- char *checksum; /**< The smiley checksum. */
-};
+typedef struct {
+ gchar *shortcut;
+ PurpleImage *image;
+} PurpleSmileyPrivate;
-struct _PurpleSmileyClass
+enum
{
- GObjectClass parent_class;
+ PROP_0,
+ PROP_SHORTCUT,
+ PROP_LAST
};
-static GHashTable *smiley_shortcut_index = NULL; /* shortcut (char *) => smiley (PurpleSmiley*) */
-static GHashTable *smiley_checksum_index = NULL; /* checksum (char *) => smiley (PurpleSmiley*) */
-
-static guint save_timer = 0;
-static gboolean smileys_loaded = FALSE;
-static char *smileys_dir = NULL;
-
-#define SMILEYS_DEFAULT_FOLDER "custom_smiley"
-#define SMILEYS_LOG_ID "smileys"
-
-#define XML_FILE_NAME "smileys.xml"
-
-#define XML_ROOT_TAG "smileys"
-#define XML_PROFILE_TAG "profile"
-#define XML_PROFILE_NAME_ATTRIB_TAG "name"
-#define XML_ACCOUNT_TAG "account"
-#define XML_ACCOUNT_USERID_ATTRIB_TAG "userid"
-#define XML_SMILEY_SET_TAG "smiley_set"
-#define XML_SMILEY_TAG "smiley"
-#define XML_SHORTCUT_ATTRIB_TAG "shortcut"
-#define XML_CHECKSUM_ATRIB_TAG "checksum"
-#define XML_FILENAME_ATRIB_TAG "filename"
-
-
-/******************************************************************************
- * XML descriptor file layout *
- ******************************************************************************
- *
- * Although we are creating the profile XML structure here, now we
- * won't handle it.
- * So, we just add one profile named "default" that has no associated
- * account elements, and have only the smiley_set that will contain
- * all existent custom smiley.
- *
- * It's our "Highlander Profile" :-)
- *
- ******************************************************************************
- *
- * <smileys>
- * <profile name="john.doe">
- * <account userid="john.doe@jabber.org">
- * <account userid="john.doe@gmail.com">
- * <smiley_set>
- * <smiley shortcut="aaa" checksum="xxxxxxxx" filename="file_name1.gif"/>
- * <smiley shortcut="bbb" checksum="yyyyyyy" filename="file_name2.gif"/>
- * </smiley_set>
- * </profile>
- * </smiley>
- *
- *****************************************************************************/
-
-
-/*********************************************************************
- * Forward declarations *
- *********************************************************************/
-
-static gboolean read_smiley_file(const char *path, guchar **data, size_t *len);
-
-static char *get_file_full_path(const char *filename);
-
-static PurpleSmiley *purple_smiley_create(const char *shortcut);
-
-static void purple_smiley_load_file(const char *shortcut, const char *checksum,
- const char *filename);
-
-static void
-purple_smiley_set_data_impl(PurpleSmiley *smiley, guchar *smiley_data,
- size_t smiley_data_len);
-
-static void
-purple_smiley_data_store(PurpleStoredImage *stored_img);
-
-static void
-purple_smiley_data_unstore(const char *filename);
+static GObjectClass *parent_class;
+static GParamSpec *properties[PROP_LAST];
-/*********************************************************************
- * Writing to disk *
- *********************************************************************/
+/*******************************************************************************
+ * API implementation
+ ******************************************************************************/
-static xmlnode *
-smiley_to_xmlnode(PurpleSmiley *smiley)
+PurpleSmiley *
+purple_smiley_new(const gchar *shortcut, const gchar *path)
{
- xmlnode *smiley_node = NULL;
-
- smiley_node = xmlnode_new(XML_SMILEY_TAG);
-
- if (!smiley_node)
- return NULL;
+ PurpleSmiley *smiley;
+ PurpleSmileyPrivate *priv;
- xmlnode_set_attrib(smiley_node, XML_SHORTCUT_ATTRIB_TAG,
- smiley->shortcut);
+ g_return_val_if_fail(shortcut != NULL, NULL);
+ g_return_val_if_fail(path != NULL, NULL);
- xmlnode_set_attrib(smiley_node, XML_CHECKSUM_ATRIB_TAG,
- smiley->checksum);
+ smiley = g_object_new(PURPLE_TYPE_SMILEY,
+ "shortcut", shortcut,
+ NULL);
+ priv = PURPLE_SMILEY_GET_PRIVATE(smiley);
- xmlnode_set_attrib(smiley_node, XML_FILENAME_ATRIB_TAG,
- purple_imgstore_get_filename(smiley->img));
+ priv->image = purple_image_new_from_file(path, FALSE);
+ if (!priv->image) {
+ purple_debug_error("smiley", "Couldn't load smiley data ");
+ g_object_unref(smiley);
+ return NULL;
+ }
- return smiley_node;
+ return smiley;
}
-static void
-add_smiley_to_main_node(gpointer key, gpointer value, gpointer user_data)
+PurpleSmiley *
+purple_smiley_new_remote(const gchar *shortcut)
{
- xmlnode *child_node;
+ PurpleSmiley *smiley;
+ PurpleSmileyPrivate *priv;
- child_node = smiley_to_xmlnode(value);
- xmlnode_insert_child((xmlnode*)user_data, child_node);
-}
+ g_return_val_if_fail(shortcut != NULL, NULL);
-static xmlnode *
-smileys_to_xmlnode(void)
-{
- xmlnode *root_node, *profile_node, *smileyset_node;
-
- root_node = xmlnode_new(XML_ROOT_TAG);
- xmlnode_set_attrib(root_node, "version", "1.0");
-
- /* See the top comments above to understand why initial tag elements
- * are not being considered by now. */
- profile_node = xmlnode_new(XML_PROFILE_TAG);
- if (profile_node) {
- xmlnode_set_attrib(profile_node, XML_PROFILE_NAME_ATTRIB_TAG, "Default");
- xmlnode_insert_child(root_node, profile_node);
-
- smileyset_node = xmlnode_new(XML_SMILEY_SET_TAG);
- if (smileyset_node) {
- xmlnode_insert_child(profile_node, smileyset_node);
- g_hash_table_foreach(smiley_shortcut_index, add_smiley_to_main_node, smileyset_node);
- }
- }
+ smiley = g_object_new(PURPLE_TYPE_SMILEY,
+ "shortcut", shortcut,
+ NULL);
+ priv = PURPLE_SMILEY_GET_PRIVATE(smiley);
+
+ priv->image = purple_image_transfer_new();
- return root_node;
+ return smiley;
}
-static void
-sync_smileys(void)
+const gchar *
+purple_smiley_get_shortcut(const PurpleSmiley *smiley)
{
- xmlnode *root_node;
- char *data;
+ PurpleSmileyPrivate *priv = PURPLE_SMILEY_GET_PRIVATE(smiley);
- if (!smileys_loaded) {
- purple_debug_error(SMILEYS_LOG_ID, "Attempted to save smileys before it "
- "was read!\n");
- return;
- }
+ g_return_val_if_fail(priv != NULL, NULL);
- root_node = smileys_to_xmlnode();
- data = xmlnode_to_formatted_str(root_node, NULL);
- purple_util_write_data_to_file(XML_FILE_NAME, data, -1);
-
- g_free(data);
- xmlnode_free(root_node);
+ return priv->shortcut;
}
-static gboolean
-save_smileys_cb(gpointer data)
+PurpleImage *
+purple_smiley_get_image(PurpleSmiley *smiley)
{
- sync_smileys();
- save_timer = 0;
- return FALSE;
-}
+ PurpleSmileyPrivate *priv = PURPLE_SMILEY_GET_PRIVATE(smiley);
-static void
-purple_smileys_save(void)
-{
- if (save_timer == 0)
- save_timer = purple_timeout_add_seconds(5, save_smileys_cb, NULL);
+ g_return_val_if_fail(priv != NULL, NULL);
+ g_return_val_if_fail(priv->image != NULL, NULL);
+
+ return priv->image;
}
-/*********************************************************************
- * Reading from disk *
- *********************************************************************/
+/*******************************************************************************
+ * Object stuff
+ ******************************************************************************/
static void
-parse_smiley(xmlnode *smiley_node)
+purple_smiley_init(GTypeInstance *instance, gpointer klass)
{
- const char *shortcut = NULL;
- const char *checksum = NULL;
- const char *filename = NULL;
-
- shortcut = xmlnode_get_attrib(smiley_node, XML_SHORTCUT_ATTRIB_TAG);
- checksum = xmlnode_get_attrib(smiley_node, XML_CHECKSUM_ATRIB_TAG);
- filename = xmlnode_get_attrib(smiley_node, XML_FILENAME_ATRIB_TAG);
-
- if ((shortcut == NULL) || (checksum == NULL) || (filename == NULL))
- return;
-
- purple_smiley_load_file(shortcut, checksum, filename);
+ PurpleSmiley *smiley = PURPLE_SMILEY(instance);
+ PURPLE_DBUS_REGISTER_POINTER(smiley, PurpleSmiley);
}
static void
-purple_smileys_load(void)
-{
- xmlnode *root_node, *profile_node;
- xmlnode *smileyset_node = NULL;
- xmlnode *smiley_node;
-
- smileys_loaded = TRUE;
-
- root_node = purple_util_read_xml_from_file(XML_FILE_NAME,
- _(SMILEYS_LOG_ID));
-
- if (root_node == NULL)
- return;
-
- /* See the top comments above to understand why initial tag elements
- * are not being considered by now. */
- profile_node = xmlnode_get_child(root_node, XML_PROFILE_TAG);
- if (profile_node)
- smileyset_node = xmlnode_get_child(profile_node, XML_SMILEY_SET_TAG);
-
- if (smileyset_node) {
- smiley_node = xmlnode_get_child(smileyset_node, XML_SMILEY_TAG);
- for (; smiley_node != NULL;
- smiley_node = xmlnode_get_next_twin(smiley_node)) {
- parse_smiley(smiley_node);
- }
- }
-
- xmlnode_free(root_node);
-}
-
-/*********************************************************************
- * GObject Stuff *
- *********************************************************************/
-enum
+purple_smiley_finalize(GObject *obj)
{
- PROP_0,
- PROP_SHORTCUT,
- PROP_IMGSTORE
-};
+ PurpleSmiley *smiley = PURPLE_SMILEY(obj);
+ PurpleSmileyPrivate *priv = PURPLE_SMILEY_GET_PRIVATE(smiley);
-#define PROP_SHORTCUT_S "shortcut"
-#define PROP_IMGSTORE_S "image"
+ g_free(priv->shortcut);
-enum
-{
- SIG_DESTROY,
- SIG_LAST
-};
+ if (priv->image)
+ g_object_unref(priv->image);
-static guint signals[SIG_LAST];
-static GObjectClass *parent_class;
+ PURPLE_DBUS_UNREGISTER_POINTER(smiley);
-static void
-purple_smiley_init(GTypeInstance *instance, gpointer klass)
-{
- PurpleSmiley *smiley = PURPLE_SMILEY(instance);
- PURPLE_DBUS_REGISTER_POINTER(smiley, PurpleSmiley);
+ G_OBJECT_CLASS(parent_class)->finalize(obj);
}
static void
-purple_smiley_get_property(GObject *object, guint param_id, GValue *value,
- GParamSpec *spec)
+purple_smiley_get_property(GObject *object, guint par_id, GValue *value,
+ GParamSpec *pspec)
{
PurpleSmiley *smiley = PURPLE_SMILEY(object);
- switch (param_id) {
+ PurpleSmileyPrivate *priv = PURPLE_SMILEY_GET_PRIVATE(smiley);
+
+ switch (par_id) {
case PROP_SHORTCUT:
- g_value_set_string(value, smiley->shortcut);
- break;
- case PROP_IMGSTORE:
- g_value_set_pointer(value, smiley->img);
+ g_value_set_string(value, priv->shortcut);
break;
default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, spec);
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, par_id, pspec);
break;
}
}
static void
-purple_smiley_set_property(GObject *object, guint param_id, const GValue *value,
- GParamSpec *spec)
+purple_smiley_set_property(GObject *object, guint par_id, const GValue *value,
+ GParamSpec *pspec)
{
PurpleSmiley *smiley = PURPLE_SMILEY(object);
- switch (param_id) {
+ PurpleSmileyPrivate *priv = PURPLE_SMILEY_GET_PRIVATE(smiley);
+
+ switch (par_id) {
case PROP_SHORTCUT:
- {
- const char *shortcut = g_value_get_string(value);
- purple_smiley_set_shortcut(smiley, shortcut);
- }
- break;
- case PROP_IMGSTORE:
- {
- PurpleStoredImage *img = g_value_get_pointer(value);
-
- purple_imgstore_unref(smiley->img);
- g_free(smiley->checksum);
-
- smiley->img = img;
- if (img) {
- smiley->checksum = purple_util_get_image_checksum(
- purple_imgstore_get_data(img),
- purple_imgstore_get_size(img));
- purple_smiley_data_store(img);
- } else {
- smiley->checksum = NULL;
- }
-
- g_object_notify(object, PROP_IMGSTORE_S);
- }
+ g_free(priv->shortcut);
+ priv->shortcut = g_strdup(g_value_get_string(value));
break;
default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, spec);
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, par_id, pspec);
break;
}
}
static void
-purple_smiley_finalize(GObject *obj)
-{
- PurpleSmiley *smiley = PURPLE_SMILEY(obj);
-
- if (g_hash_table_lookup(smiley_shortcut_index, smiley->shortcut)) {
- g_hash_table_remove(smiley_shortcut_index, smiley->shortcut);
- g_hash_table_remove(smiley_checksum_index, smiley->checksum);
- }
-
- g_free(smiley->shortcut);
- g_free(smiley->checksum);
- if (smiley->img)
- purple_smiley_data_unstore(purple_imgstore_get_filename(smiley->img));
- purple_imgstore_unref(smiley->img);
-
- PURPLE_DBUS_UNREGISTER_POINTER(smiley);
-
- purple_smileys_save();
-}
-
-static void
-purple_smiley_dispose(GObject *gobj)
-{
- g_signal_emit(gobj, signals[SIG_DESTROY], 0);
- parent_class->dispose(gobj);
-}
-
-static void
purple_smiley_class_init(PurpleSmileyClass *klass)
{
GObjectClass *gobj_class = G_OBJECT_CLASS(klass);
- GParamSpec *pspec;
parent_class = g_type_class_peek_parent(klass);
+ g_type_class_add_private(klass, sizeof(PurpleSmileyPrivate));
+
gobj_class->get_property = purple_smiley_get_property;
gobj_class->set_property = purple_smiley_set_property;
gobj_class->finalize = purple_smiley_finalize;
- gobj_class->dispose = purple_smiley_dispose;
-
- /* Shortcut */
- pspec = g_param_spec_string(PROP_SHORTCUT_S, _("Shortcut"),
- _("The text-shortcut for the smiley"),
- NULL,
- G_PARAM_READWRITE);
- g_object_class_install_property(gobj_class, PROP_SHORTCUT, pspec);
-
- /* Stored Image */
- pspec = g_param_spec_pointer(PROP_IMGSTORE_S, _("Stored Image"),
- _("Stored Image. (that'll have to do for now)"),
- G_PARAM_READWRITE);
- g_object_class_install_property(gobj_class, PROP_IMGSTORE, pspec);
-
- signals[SIG_DESTROY] = g_signal_new("destroy",
- G_OBJECT_CLASS_TYPE(klass),
- G_SIGNAL_RUN_LAST,
- 0, NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+
+ properties[PROP_SHORTCUT] = g_param_spec_string("shortcut", "Shortcut",
+ "A non-escaped textual representation of a smiley.", NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(gobj_class, PROP_LAST, properties);
}
GType
@@ -428,471 +200,17 @@ purple_smiley_get_type(void)
{
static GType type = 0;
- if(type == 0) {
+ if (G_UNLIKELY(type == 0)) {
static const GTypeInfo info = {
- sizeof(PurpleSmileyClass),
- NULL,
- NULL,
- (GClassInitFunc)purple_smiley_class_init,
- NULL,
- NULL,
- sizeof(PurpleSmiley),
- 0,
- purple_smiley_init,
- NULL,
+ .class_size = sizeof(PurpleSmileyClass),
+ .class_init = (GClassInitFunc)purple_smiley_class_init,
+ .instance_size = sizeof(PurpleSmiley),
+ .instance_init = purple_smiley_init,
};
type = g_type_register_static(G_TYPE_OBJECT,
- "PurpleSmiley",
- &info, 0);
+ "PurpleSmiley", &info, 0);
}
return type;
}
-
-/*********************************************************************
- * Other Stuff *
- *********************************************************************/
-
-static char *get_file_full_path(const char *filename)
-{
- char *path;
-
- path = g_build_filename(purple_smileys_get_storing_dir(), filename, NULL);
-
- if (!g_file_test(path, G_FILE_TEST_EXISTS)) {
- g_free(path);
- return NULL;
- }
-
- return path;
-}
-
-static void
-purple_smiley_load_file(const char *shortcut, const char *checksum, const char *filename)
-{
- PurpleSmiley *smiley = NULL;
- guchar *smiley_data;
- size_t smiley_data_len;
- char *fullpath = NULL;
-
- g_return_if_fail(shortcut != NULL);
- g_return_if_fail(checksum != NULL);
- g_return_if_fail(filename != NULL);
-
- fullpath = get_file_full_path(filename);
- if (!fullpath) {
- purple_debug_error(SMILEYS_LOG_ID, "Path for filename %s doesn't exist\n", filename);
- return;
- }
-
- smiley = purple_smiley_create(shortcut);
- if (!smiley) {
- g_free(fullpath);
- return;
- }
-
- smiley->checksum = g_strdup(checksum);
-
- if (read_smiley_file(fullpath, &smiley_data, &smiley_data_len))
- purple_smiley_set_data_impl(smiley, smiley_data,
- smiley_data_len);
- else
- purple_smiley_delete(smiley);
-
- g_free(fullpath);
-
-}
-
-static void
-purple_smiley_data_store(PurpleStoredImage *stored_img)
-{
- const char *dirname;
- char *path;
- FILE *file = NULL;
-
- g_return_if_fail(stored_img != NULL);
-
- if (!smileys_loaded)
- return;
-
- dirname = purple_smileys_get_storing_dir();
- path = g_build_filename(dirname, purple_imgstore_get_filename(stored_img), NULL);
-
- if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) {
- purple_debug_info(SMILEYS_LOG_ID, "Creating smileys directory.\n");
-
- if (g_mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR) < 0) {
- purple_debug_error(SMILEYS_LOG_ID,
- "Unable to create directory %s: %s\n",
- dirname, g_strerror(errno));
- }
- }
-
- if ((file = g_fopen(path, "wb")) != NULL) {
- if (!fwrite(purple_imgstore_get_data(stored_img),
- purple_imgstore_get_size(stored_img), 1, file)) {
- purple_debug_error(SMILEYS_LOG_ID, "Error writing %s: %s\n",
- path, g_strerror(errno));
- } else {
- purple_debug_info(SMILEYS_LOG_ID, "Wrote cache file: %s\n", path);
- }
-
- fclose(file);
- } else {
- purple_debug_error(SMILEYS_LOG_ID, "Unable to create file %s: %s\n",
- path, g_strerror(errno));
- g_free(path);
-
- return;
- }
-
- g_free(path);
-}
-
-static void
-purple_smiley_data_unstore(const char *filename)
-{
- const char *dirname;
- char *path;
-
- g_return_if_fail(filename != NULL);
-
- dirname = purple_smileys_get_storing_dir();
- path = g_build_filename(dirname, filename, NULL);
-
- if (g_file_test(path, G_FILE_TEST_EXISTS)) {
- if (g_unlink(path))
- purple_debug_error(SMILEYS_LOG_ID, "Failed to delete %s: %s\n",
- path, g_strerror(errno));
- else
- purple_debug_info(SMILEYS_LOG_ID, "Deleted cache file: %s\n", path);
- }
-
- g_free(path);
-}
-
-static gboolean
-read_smiley_file(const char *path, guchar **data, size_t *len)
-{
- GError *err = NULL;
-
- if (!g_file_get_contents(path, (gchar **)data, len, &err)) {
- purple_debug_error(SMILEYS_LOG_ID, "Error reading %s: %s\n",
- path, err->message);
- g_error_free(err);
-
- return FALSE;
- }
-
- return TRUE;
-}
-
-static PurpleStoredImage *
-purple_smiley_data_new(guchar *smiley_data, size_t smiley_data_len)
-{
- char *filename;
- PurpleStoredImage *stored_img;
-
- g_return_val_if_fail(smiley_data != NULL, NULL);
- g_return_val_if_fail(smiley_data_len > 0, NULL);
-
- filename = purple_util_get_image_filename(smiley_data, smiley_data_len);
-
- if (filename == NULL) {
- g_free(smiley_data);
- return NULL;
- }
-
- stored_img = purple_imgstore_add(smiley_data, smiley_data_len, filename);
-
- g_free(filename);
-
- return stored_img;
-}
-
-static void
-purple_smiley_set_data_impl(PurpleSmiley *smiley, guchar *smiley_data,
- size_t smiley_data_len)
-{
- PurpleStoredImage *old_img, *new_img;
- const char *old_filename = NULL;
- const char *new_filename = NULL;
-
- g_return_if_fail(smiley != NULL);
- g_return_if_fail(smiley_data != NULL);
- g_return_if_fail(smiley_data_len > 0);
-
- old_img = smiley->img;
-
- new_img = purple_smiley_data_new(smiley_data, smiley_data_len);
-
- g_object_set(G_OBJECT(smiley), PROP_IMGSTORE_S, new_img, NULL);
-
- /* If the old and new image files have different names we need
- * to unstore old image file. */
- if (!old_img)
- return;
-
- old_filename = purple_imgstore_get_filename(old_img);
- new_filename = purple_imgstore_get_filename(smiley->img);
-
- if (g_ascii_strcasecmp(old_filename, new_filename))
- purple_smiley_data_unstore(old_filename);
- purple_imgstore_unref(old_img);
-}
-
-
-/*****************************************************************************
- * Public API functions *
- *****************************************************************************/
-
-static PurpleSmiley *
-purple_smiley_create(const char *shortcut)
-{
- PurpleSmiley *smiley;
-
- smiley = PURPLE_SMILEY(g_object_new(PURPLE_TYPE_SMILEY, PROP_SHORTCUT_S, shortcut, NULL));
-
- return smiley;
-}
-
-PurpleSmiley *
-purple_smiley_new(PurpleStoredImage *img, const char *shortcut)
-{
- PurpleSmiley *smiley = NULL;
-
- g_return_val_if_fail(shortcut != NULL, NULL);
- g_return_val_if_fail(img != NULL, NULL);
-
- smiley = purple_smileys_find_by_shortcut(shortcut);
- if (smiley)
- return smiley;
-
- smiley = purple_smiley_create(shortcut);
- if (!smiley)
- return NULL;
-
- g_object_set(G_OBJECT(smiley), PROP_IMGSTORE_S, img, NULL);
-
- return smiley;
-}
-
-static PurpleSmiley *
-purple_smiley_new_from_stream(const char *shortcut, guchar *smiley_data,
- size_t smiley_data_len)
-{
- PurpleSmiley *smiley;
-
- g_return_val_if_fail(shortcut != NULL, NULL);
- g_return_val_if_fail(smiley_data != NULL, NULL);
- g_return_val_if_fail(smiley_data_len > 0, NULL);
-
- smiley = purple_smileys_find_by_shortcut(shortcut);
- if (smiley)
- return smiley;
-
- /* purple_smiley_create() sets shortcut */
- smiley = purple_smiley_create(shortcut);
- if (!smiley)
- return NULL;
-
- purple_smiley_set_data_impl(smiley, smiley_data, smiley_data_len);
-
- purple_smiley_data_store(smiley->img);
-
- return smiley;
-}
-
-PurpleSmiley *
-purple_smiley_new_from_file(const char *shortcut, const char *filepath)
-{
- PurpleSmiley *smiley = NULL;
- guchar *smiley_data;
- size_t smiley_data_len;
-
- g_return_val_if_fail(shortcut != NULL, NULL);
- g_return_val_if_fail(filepath != NULL, NULL);
-
- if (read_smiley_file(filepath, &smiley_data, &smiley_data_len)) {
- smiley = purple_smiley_new_from_stream(shortcut, smiley_data,
- smiley_data_len);
- }
-
- return smiley;
-}
-
-void
-purple_smiley_delete(PurpleSmiley *smiley)
-{
- g_return_if_fail(smiley != NULL);
-
- g_object_unref(smiley);
-}
-
-gboolean
-purple_smiley_set_shortcut(PurpleSmiley *smiley, const char *shortcut)
-{
- g_return_val_if_fail(smiley != NULL, FALSE);
- g_return_val_if_fail(shortcut != NULL, FALSE);
-
- /* Check out whether the new shortcut is already being used. */
- if (g_hash_table_lookup(smiley_shortcut_index, shortcut))
- return FALSE;
-
- /* Remove the old shortcut. */
- if (smiley->shortcut)
- g_hash_table_remove(smiley_shortcut_index, smiley->shortcut);
-
- /* Insert the new shortcut. */
- g_hash_table_insert(smiley_shortcut_index, g_strdup(shortcut), smiley);
-
- g_free(smiley->shortcut);
- smiley->shortcut = g_strdup(shortcut);
-
- g_object_notify(G_OBJECT(smiley), PROP_SHORTCUT_S);
-
- purple_smileys_save();
-
- return TRUE;
-}
-
-void
-purple_smiley_set_data(PurpleSmiley *smiley, guchar *smiley_data,
- size_t smiley_data_len)
-{
- g_return_if_fail(smiley != NULL);
- g_return_if_fail(smiley_data != NULL);
- g_return_if_fail(smiley_data_len > 0);
-
- /* Remove the previous entry */
- g_hash_table_remove(smiley_checksum_index, smiley->checksum);
-
- /* Update the file data. This also updates the checksum. */
- purple_smiley_set_data_impl(smiley, smiley_data, smiley_data_len);
-
- /* Reinsert the index item. */
- g_hash_table_insert(smiley_checksum_index, g_strdup(smiley->checksum), smiley);
-
- purple_smileys_save();
-}
-
-PurpleStoredImage *
-purple_smiley_get_stored_image(const PurpleSmiley *smiley)
-{
- return purple_imgstore_ref(smiley->img);
-}
-
-const char *purple_smiley_get_shortcut(const PurpleSmiley *smiley)
-{
- g_return_val_if_fail(smiley != NULL, NULL);
-
- return smiley->shortcut;
-}
-
-const char *
-purple_smiley_get_checksum(const PurpleSmiley *smiley)
-{
- g_return_val_if_fail(smiley != NULL, NULL);
-
- return smiley->checksum;
-}
-
-gconstpointer
-purple_smiley_get_data(const PurpleSmiley *smiley, size_t *len)
-{
- g_return_val_if_fail(smiley != NULL, NULL);
-
- if (smiley->img) {
- if (len != NULL)
- *len = purple_imgstore_get_size(smiley->img);
-
- return purple_imgstore_get_data(smiley->img);
- }
-
- return NULL;
-}
-
-const char *
-purple_smiley_get_extension(const PurpleSmiley *smiley)
-{
- if (smiley->img != NULL)
- return purple_imgstore_get_extension(smiley->img);
-
- return NULL;
-}
-
-char *purple_smiley_get_full_path(PurpleSmiley *smiley)
-{
- g_return_val_if_fail(smiley != NULL, NULL);
-
- if (smiley->img == NULL)
- return NULL;
-
- return get_file_full_path(purple_imgstore_get_filename(smiley->img));
-}
-
-static void add_smiley_to_list(gpointer key, gpointer value, gpointer user_data)
-{
- GList** returninglist = (GList**)user_data;
-
- *returninglist = g_list_append(*returninglist, value);
-}
-
-GList *
-purple_smileys_get_all(void)
-{
- GList *returninglist = NULL;
-
- g_hash_table_foreach(smiley_shortcut_index, add_smiley_to_list, &returninglist);
-
- return returninglist;
-}
-
-PurpleSmiley *
-purple_smileys_find_by_shortcut(const char *shortcut)
-{
- g_return_val_if_fail(shortcut != NULL, NULL);
-
- return g_hash_table_lookup(smiley_shortcut_index, shortcut);
-}
-
-PurpleSmiley *
-purple_smileys_find_by_checksum(const char *checksum)
-{
- g_return_val_if_fail(checksum != NULL, NULL);
-
- return g_hash_table_lookup(smiley_checksum_index, checksum);
-}
-
-const char *
-purple_smileys_get_storing_dir(void)
-{
- return smileys_dir;
-}
-
-void
-purple_smileys_init(void)
-{
- smiley_shortcut_index = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
- smiley_checksum_index = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
-
- smileys_dir = g_build_filename(purple_user_dir(), SMILEYS_DEFAULT_FOLDER, NULL);
-
- purple_smileys_load();
-}
-
-void
-purple_smileys_uninit(void)
-{
- if (save_timer != 0) {
- purple_timeout_remove(save_timer);
- save_timer = 0;
- sync_smileys();
- }
-
- g_hash_table_destroy(smiley_shortcut_index);
- g_hash_table_destroy(smiley_checksum_index);
- g_free(smileys_dir);
-}
-
diff --git a/libpurple/smiley.h b/libpurple/smiley.h
index 73202bda47..0145ca8f7a 100644
--- a/libpurple/smiley.h
+++ b/libpurple/smiley.h
@@ -1,9 +1,3 @@
-/**
- * @file smiley.h Smiley API
- * @ingroup core
- * @since 2.5.0
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -22,241 +16,131 @@
*
* 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
- *
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#ifndef _PURPLE_SMILEY_H_
#define _PURPLE_SMILEY_H_
-
-#include <glib-object.h>
-
-#include "imgstore.h"
-#include "util.h"
-
-/**
- * A custom smiley.
- * This contains everything Purple will ever need to know about a custom smiley.
- * Everything.
- *
- * PurpleSmiley is a GObject.
- */
-typedef struct _PurpleSmiley PurpleSmiley;
-typedef struct _PurpleSmileyClass PurpleSmileyClass;
-
-#define PURPLE_TYPE_SMILEY (purple_smiley_get_type ())
-#define PURPLE_SMILEY(smiley) (G_TYPE_CHECK_INSTANCE_CAST ((smiley), PURPLE_TYPE_SMILEY, PurpleSmiley))
-#define PURPLE_SMILEY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PURPLE_TYPE_SMILEY, PurpleSmileyClass))
-#define PURPLE_IS_SMILEY(smiley) (G_TYPE_CHECK_INSTANCE_TYPE ((smiley), PURPLE_TYPE_SMILEY))
-#define PURPLE_IS_SMILEY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PURPLE_TYPE_SMILEY))
-#define PURPLE_SMILEY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PURPLE_TYPE_SMILEY, PurpleSmileyClass))
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**************************************************************************/
-/** @name Custom Smiley API */
-/**************************************************************************/
-/*@{*/
-
-/**
- * GObject-fu.
- * @internal.
- */
-GType purple_smiley_get_type(void);
-
/**
- * Creates a new custom smiley from a PurpleStoredImage.
+ * SECTION:smiley
+ * @include:smiley.h
+ * @section_id: libpurple-smiley
+ * @short_description: a link between emoticon image and its textual representation
+ * @title: Smileys
*
- * If a custom smiley with the given shortcut already exists, it
- * will be automaticaly returned.
+ * A #PurpleSmiley is a base class for associating emoticon images and their
+ * textual representation. It's intended for various smiley-related tasks:
+ * parsing the text against them, displaying in the smiley selector, or handling
+ * remote data (using #PurpleRemoteSmiley).
*
- * @param img The image associated with the smiley.
- * @param shortcut The associated shortcut (e.g. "(homer)").
- *
- * @return The custom smiley.
+ * The #PurpleSmiley:shortcut is always unescaped, but <link linkend="libpurple-smiley-parser">smiley parser</link>
+ * may deal with special characters.
*/
-PurpleSmiley *
-purple_smiley_new(PurpleStoredImage *img, const char *shortcut);
-/**
- * Creates a new custom smiley, reading the image data from a file.
- *
- * If a custom smiley with the given shortcut already exists, it
- * will be automaticaly returned.
- *
- * @param shortcut The associated shortcut (e.g. "(homer)").
- * @param filepath The image file.
- *
- * @return The custom smiley.
- */
-PurpleSmiley *
-purple_smiley_new_from_file(const char *shortcut, const char *filepath);
+#include "image.h"
-/**
- * Destroys the custom smiley and releases the associated resources.
- *
- * @param smiley The custom smiley.
- */
-void
-purple_smiley_delete(PurpleSmiley *smiley);
+#include <glib-object.h>
-/**
- * Changes the custom smiley's shortcut.
- *
- * @param smiley The custom smiley.
- * @param shortcut The new shortcut. A custom smiley with this shortcut
- * cannot already be in use.
- *
- * @return TRUE if the shortcut was changed. FALSE otherwise.
- */
-gboolean
-purple_smiley_set_shortcut(PurpleSmiley *smiley, const char *shortcut);
+typedef struct _PurpleSmiley PurpleSmiley;
+typedef struct _PurpleSmileyClass PurpleSmileyClass;
-/**
- * Changes the custom smiley's image data.
- *
- * @param smiley The custom smiley.
- * @param smiley_data The custom smiley data, which the smiley code
- * takes ownership of and will free.
- * @param smiley_data_len The length of the data in @a smiley_data.
- */
-void
-purple_smiley_set_data(PurpleSmiley *smiley, guchar *smiley_data,
- size_t smiley_data_len);
+#define PURPLE_TYPE_SMILEY (purple_smiley_get_type())
+#define PURPLE_SMILEY(smiley) (G_TYPE_CHECK_INSTANCE_CAST((smiley), PURPLE_TYPE_SMILEY, PurpleSmiley))
+#define PURPLE_SMILEY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_SMILEY, PurpleSmileyClass))
+#define PURPLE_IS_SMILEY(smiley) (G_TYPE_CHECK_INSTANCE_TYPE((smiley), PURPLE_TYPE_SMILEY))
+#define PURPLE_IS_SMILEY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_SMILEY))
+#define PURPLE_SMILEY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_SMILEY, PurpleSmileyClass))
/**
- * Returns the custom smiley's associated shortcut (e.g. "(homer)").
- *
- * @param smiley The custom smiley.
+ * PurpleSmiley:
*
- * @return The shortcut.
+ * A generic smiley. It can either be a theme smiley, or a custom smiley.
*/
-const char *purple_smiley_get_shortcut(const PurpleSmiley *smiley);
+struct _PurpleSmiley
+{
+ /*< private >*/
+ GObject parent;
+};
/**
- * Returns the custom smiley data's checksum.
+ * PurpleSmileyClass:
*
- * @param smiley The custom smiley.
- *
- * @return The checksum.
+ * Base class for #PurpleSmiley objects.
*/
-const char *purple_smiley_get_checksum(const PurpleSmiley *smiley);
+struct _PurpleSmileyClass
+{
+ /*< private >*/
+ GObjectClass parent_class;
-/**
- * Returns the PurpleStoredImage with the reference counter incremented.
- *
- * The returned PurpleStoredImage reference counter must be decremented
- * when the caller is done using it.
- *
- * @param smiley The custom smiley.
- *
- * @return A PurpleStoredImage.
- */
-PurpleStoredImage *purple_smiley_get_stored_image(const PurpleSmiley *smiley);
+ /*< private >*/
+ void (*purple_reserved1)(void);
+ void (*purple_reserved2)(void);
+ void (*purple_reserved3)(void);
+ void (*purple_reserved4)(void);
+};
-/**
- * Returns the custom smiley's data.
- *
- * @param smiley The custom smiley.
- * @param len If not @c NULL, the length of the image data returned
- * will be set in the location pointed to by this.
- *
- * @return A pointer to the custom smiley data.
- */
-gconstpointer purple_smiley_get_data(const PurpleSmiley *smiley, size_t *len);
+G_BEGIN_DECLS
/**
- * Returns an extension corresponding to the custom smiley's file type.
- *
- * @param smiley The custom smiley.
+ * purple_smiley_get_type:
*
- * @return The custom smiley's extension, "icon" if unknown, or @c NULL if
- * the image data has disappeared.
+ * Returns: the #GType for a smiley.
*/
-const char *purple_smiley_get_extension(const PurpleSmiley *smiley);
+GType
+purple_smiley_get_type(void);
/**
- * Returns a full path to an custom smiley.
+ * purple_smiley_new:
+ * @shortcut: the smiley shortcut (unescaped).
+ * @path: the smiley image file path.
*
- * If the custom smiley has data and the file exists in the cache, this
- * will return a full path to the cached file.
+ * Creates new smiley, which is ready to display (its file exists
+ * and is a valid image).
*
- * In general, it is not appropriate to be poking in the file cache
- * directly. If you find yourself wanting to use this function, think
- * very long and hard about it, and then don't.
- *
- * Think some more.
- *
- * @param smiley The custom smiley.
- *
- * @return A full path to the file, or @c NULL under various conditions.
- * The caller should use g_free to free the returned string.
+ * Returns: the new #PurpleSmiley.
*/
-char *purple_smiley_get_full_path(PurpleSmiley *smiley);
-
-/*@}*/
-
-
-/**************************************************************************/
-/** @name Custom Smiley Subsystem API */
-/**************************************************************************/
-/*@{*/
-
-/**
- * Returns a list of all custom smileys. The caller is responsible for freeing
- * the list.
- *
- * @return A list of all custom smileys.
- */
-GList *
-purple_smileys_get_all(void);
+PurpleSmiley *
+purple_smiley_new(const gchar *shortcut, const gchar *path);
/**
- * Returns a custom smiley given its shortcut.
+ * purple_smiley_new_remote:
+ * @shortcut: the smiley shortcut (unescaped).
*
- * @param shortcut The custom smiley's shortcut.
+ * Creates new remote smiley. It's not bound to any conversation, so most
+ * probably you might want to use
+ * #purple_conversation_add_remote_smiley instead.
*
- * @return The custom smiley if found, or @c NULL if not found.
+ * Returns: the new remote #PurpleSmiley.
*/
PurpleSmiley *
-purple_smileys_find_by_shortcut(const char *shortcut);
+purple_smiley_new_remote(const gchar *shortcut);
/**
- * Returns a custom smiley given its checksum.
+ * purple_smiley_get_shortcut:
+ * @smiley: the smiley.
*
- * @param checksum The custom smiley's checksum.
+ * Returns the @smiley's associated shortcut (e.g. <literal>(homer)</literal> or
+ * <literal>:-)</literal>).
*
- * @return The custom smiley if found, or @c NULL if not found.
+ * Returns: the unescaped shortcut.
*/
-PurpleSmiley *
-purple_smileys_find_by_checksum(const char *checksum);
+const gchar *
+purple_smiley_get_shortcut(const PurpleSmiley *smiley);
/**
- * Returns the directory used to store custom smiley cached files.
+ * purple_smiley_get_image:
+ * @smiley: the smiley.
*
- * The default directory is PURPLEDIR/custom_smiley.
+ * Returns the image contents for a @smiley. It may not be ready for remote
+ * smileys, so check it with #purple_image_is_ready.
*
- * @return The directory in which to store custom smileys cached files.
- */
-const char *purple_smileys_get_storing_dir(void);
-
-/**
- * Initializes the custom smiley subsystem.
- */
-void purple_smileys_init(void);
-
-/**
- * Uninitializes the custom smiley subsystem.
+ * If you want to save it, increase a ref count for the returned object.
+ *
+ * Returns: (transfer none): the image contents for a @smiley.
*/
-void purple_smileys_uninit(void);
+PurpleImage *
+purple_smiley_get_image(PurpleSmiley *smiley);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_SMILEY_H_ */
-
diff --git a/libpurple/sound-theme-loader.c b/libpurple/sound-theme-loader.c
index ea85881053..630266bace 100644
--- a/libpurple/sound-theme-loader.c
+++ b/libpurple/sound-theme-loader.c
@@ -32,53 +32,57 @@
*****************************************************************************/
static PurpleTheme *
-purple_sound_loader_build(const gchar *dir)
+purple_sound_loader_build(const gchar *theme_dir)
{
- xmlnode *root_node = NULL, *sub_node;
- gchar *filename_full, *data = NULL;
+ PurpleXmlNode *root_node = NULL, *sub_node;
+ gchar *dir, *filename_full, *data = NULL;
PurpleSoundTheme *theme = NULL;
const gchar *name;
/* Find the theme file */
- g_return_val_if_fail(dir != NULL, NULL);
+ g_return_val_if_fail(theme_dir != NULL, NULL);
+ dir = g_build_filename(theme_dir, "purple", "sound", NULL);
filename_full = g_build_filename(dir, "theme.xml", NULL);
if (g_file_test(filename_full, G_FILE_TEST_IS_REGULAR))
- root_node = xmlnode_from_file(dir, "theme.xml", "sound themes", "sound-theme-loader");
+ root_node = purple_xmlnode_from_file(dir, "theme.xml", "sound themes", "sound-theme-loader");
g_free(filename_full);
- if (root_node == NULL)
+ if (root_node == NULL) {
+ g_free(dir);
return NULL;
+ }
- name = xmlnode_get_attrib(root_node, "name");
+ name = purple_xmlnode_get_attrib(root_node, "name");
- if (name && purple_strequal(xmlnode_get_attrib(root_node, "type"), "sound")) {
+ if (name && purple_strequal(purple_xmlnode_get_attrib(root_node, "type"), "sound")) {
/* Parse the tree */
- sub_node = xmlnode_get_child(root_node, "description");
- data = xmlnode_get_data(sub_node);
+ sub_node = purple_xmlnode_get_child(root_node, "description");
+ data = purple_xmlnode_get_data(sub_node);
- if (xmlnode_get_attrib(root_node, "name") != NULL) {
+ if (purple_xmlnode_get_attrib(root_node, "name") != NULL) {
theme = g_object_new(PURPLE_TYPE_SOUND_THEME,
"type", "sound",
"name", name,
- "author", xmlnode_get_attrib(root_node, "author"),
- "image", xmlnode_get_attrib(root_node, "image"),
+ "author", purple_xmlnode_get_attrib(root_node, "author"),
+ "image", purple_xmlnode_get_attrib(root_node, "image"),
"directory", dir,
"description", data, NULL);
- sub_node = xmlnode_get_child(root_node, "event");
+ sub_node = purple_xmlnode_get_child(root_node, "event");
while (sub_node) {
purple_sound_theme_set_file(theme,
- xmlnode_get_attrib(sub_node, "name"),
- xmlnode_get_attrib(sub_node, "file"));
- sub_node = xmlnode_get_next_twin(sub_node);
+ purple_xmlnode_get_attrib(sub_node, "name"),
+ purple_xmlnode_get_attrib(sub_node, "file"));
+ sub_node = purple_xmlnode_get_next_twin(sub_node);
}
}
} else purple_debug_warning("sound-theme-loader", "Missing attribute or problem with the root element\n");
- xmlnode_free(root_node);
+ purple_xmlnode_free(root_node);
g_free(data);
+ g_free(dir);
return PURPLE_THEME(theme);
}
diff --git a/libpurple/sound-theme-loader.h b/libpurple/sound-theme-loader.h
index 9eb927b156..62e9bd120d 100644
--- a/libpurple/sound-theme-loader.h
+++ b/libpurple/sound-theme-loader.h
@@ -1,7 +1,3 @@
-/**
- * @file sound-theme-loader.h Purple Sound Theme Loader Class API
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -25,17 +21,17 @@
#ifndef PURPLE_SOUND_THEME_LOADER_H
#define PURPLE_SOUND_THEME_LOADER_H
+/**
+ * SECTION:sound-theme-loader
+ * @section_id: libpurple-sound-theme-loader
+ * @short_description: <filename>sound-theme-loader.h</filename>
+ * @title: Sound Theme Loader Class
+ */
#include <glib.h>
#include <glib-object.h>
#include "theme-loader.h"
-/**
- * A purple sound theme loader. extends PurpleThemeLoader (theme-loader.h)
- * This is a class designed to build sound themes
- *
- * PurpleSoundThemeLoader is a GObject.
- */
typedef struct _PurpleSoundThemeLoader PurpleSoundThemeLoader;
typedef struct _PurpleSoundThemeLoaderClass PurpleSoundThemeLoaderClass;
@@ -46,6 +42,12 @@ typedef struct _PurpleSoundThemeLoaderClass PurpleSoundThemeLoaderClass;
#define PURPLE_IS_SOUND_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_SOUND_THEME_LOADER))
#define PURPLE_SOUND_THEME_LOADER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_SOUND_THEME_LOADER, PurpleSoundThemeLoaderClass))
+/**
+ * PurpleSoundThemeLoader:
+ *
+ * A purple sound theme loader. extends PurpleThemeLoader (theme-loader.h)
+ * This is a class designed to build sound themes
+ */
struct _PurpleSoundThemeLoader
{
PurpleThemeLoader parent;
@@ -54,16 +56,23 @@ struct _PurpleSoundThemeLoader
struct _PurpleSoundThemeLoaderClass
{
PurpleThemeLoaderClass parent_class;
+
+ /*< private >*/
+ void (*purple_reserved1)(void);
+ void (*purple_reserved2)(void);
+ void (*purple_reserved3)(void);
+ void (*purple_reserved4)(void);
};
/**************************************************************************/
-/** @name Purple Theme-Loader API */
+/* Purple Theme-Loader API */
/**************************************************************************/
G_BEGIN_DECLS
/**
- * GObject foo.
- * @internal.
+ * purple_sound_theme_loader_get_type:
+ *
+ * Returns: The #GType for sound theme loader.
*/
GType purple_sound_theme_loader_get_type(void);
diff --git a/libpurple/sound-theme.c b/libpurple/sound-theme.c
index 4918f40758..1f471a3e5c 100644
--- a/libpurple/sound-theme.c
+++ b/libpurple/sound-theme.c
@@ -24,7 +24,7 @@
#include "sound-theme.h"
#define PURPLE_SOUND_THEME_GET_PRIVATE(Gobject) \
- ((PurpleSoundThemePrivate *) ((PURPLE_SOUND_THEME(Gobject))->priv))
+ (G_TYPE_INSTANCE_GET_PRIVATE((Gobject), PURPLE_TYPE_SOUND_THEME, PurpleSoundThemePrivate))
/******************************************************************************
* Structs
@@ -55,8 +55,6 @@ purple_sound_theme_init(GTypeInstance *instance,
{
PurpleSoundThemePrivate *priv;
- (PURPLE_SOUND_THEME(instance))->priv = g_new0(PurpleSoundThemePrivate, 1);
-
priv = PURPLE_SOUND_THEME_GET_PRIVATE(instance);
priv->sound_files = g_hash_table_new_full(g_str_hash,
@@ -82,6 +80,8 @@ purple_sound_theme_class_init(PurpleSoundThemeClass *klass)
parent_class = g_type_class_peek_parent(klass);
+ g_type_class_add_private(klass, sizeof(PurpleSoundThemePrivate));
+
obj_class->finalize = purple_sound_theme_finalize;
}
diff --git a/libpurple/sound-theme.h b/libpurple/sound-theme.h
index 3814e4527f..c7f4edbb64 100644
--- a/libpurple/sound-theme.h
+++ b/libpurple/sound-theme.h
@@ -1,7 +1,3 @@
-/**
- * @file sound-theme.h Purple Sound Theme Abstact Class API
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -25,19 +21,18 @@
#ifndef PURPLE_SOUND_THEME_H
#define PURPLE_SOUND_THEME_H
+/**
+ * SECTION:sound-theme
+ * @section_id: libpurple-sound-theme
+ * @short_description: <filename>sound-theme.h</filename>
+ * @title: Sound Theme Abstact Class
+ */
#include <glib.h>
#include <glib-object.h>
#include "theme.h"
#include "sound.h"
-/**
- * extends PurpleTheme (theme.h)
- * A purple sound theme.
- * This is an object for Purple to represent a sound theme.
- *
- * PurpleSoundTheme is a PurpleTheme Object.
- */
typedef struct _PurpleSoundTheme PurpleSoundTheme;
typedef struct _PurpleSoundThemeClass PurpleSoundThemeClass;
@@ -48,61 +43,78 @@ typedef struct _PurpleSoundThemeClass PurpleSoundThemeClass;
#define PURPLE_IS_SOUND_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_SOUND_THEME))
#define PURPLE_SOUND_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_SOUND_THEME, PurpleSoundThemeClass))
+/**
+ * PurpleSoundTheme:
+ *
+ * A purple sound theme.
+ * This is an object for Purple to represent a sound theme.
+ */
struct _PurpleSoundTheme
{
+ /*< private >*/
PurpleTheme parent;
- gpointer priv;
};
struct _PurpleSoundThemeClass
{
+ /*< private >*/
PurpleThemeClass parent_class;
+
+ void (*purple_reserved1)(void);
+ void (*purple_reserved2)(void);
+ void (*purple_reserved3)(void);
+ void (*purple_reserved4)(void);
};
/**************************************************************************/
-/** @name Purple Sound Theme API */
+/* Purple Sound Theme API */
/**************************************************************************/
G_BEGIN_DECLS
/**
- * GObject foo.
- * @internal.
+ * purple_sound_theme_get_type:
+ *
+ * Returns: The #GType for a sound theme.
*/
GType purple_sound_theme_get_type(void);
/**
- * Returns a copy of the filename for the sound event.
+ * purple_sound_theme_get_file:
+ * @theme: The theme.
+ * @event: The purple sound event to look up.
*
- * @param theme The theme.
- * @param event The purple sound event to look up.
+ * Returns a copy of the filename for the sound event.
*
- * @returns The filename of the sound event.
+ * Returns: The filename of the sound event.
*/
const gchar *purple_sound_theme_get_file(PurpleSoundTheme *theme,
const gchar *event);
/**
- * Returns a copy of the directory and filename for the sound event
+ * purple_sound_theme_get_file_full:
+ * @theme: The theme.
+ * @event: The purple sound event to look up
*
- * @param theme The theme.
- * @param event The purple sound event to look up
+ * Returns a copy of the directory and filename for the sound event
*
- * @returns The directory + '/' + filename of the sound event. This is
+ * Returns: The directory + '/' + filename of the sound event. This is
* a newly allocated string that should be freed with g_free.
*/
gchar *purple_sound_theme_get_file_full(PurpleSoundTheme *theme,
const gchar *event);
/**
- * Sets the filename for a given sound event
+ * purple_sound_theme_set_file:
+ * @theme: The theme.
+ * @event: the purple sound event to look up
+ * @filename: the name of the file to be used for the event
*
- * @param theme The theme.
- * @param event the purple sound event to look up
- * @param filename the name of the file to be used for the event
+ * Sets the filename for a given sound event
*/
void purple_sound_theme_set_file(PurpleSoundTheme *theme,
const gchar *event,
const gchar *filename);
G_END_DECLS
+
#endif /* PURPLE_SOUND_THEME_H */
diff --git a/libpurple/sound.c b/libpurple/sound.c
index 4cb4ebb013..f4b84cb5f7 100644
--- a/libpurple/sound.c
+++ b/libpurple/sound.c
@@ -22,7 +22,7 @@
*/
#include "internal.h"
-#include "blist.h"
+#include "buddylist.h"
#include "prefs.h"
#include "sound.h"
#include "sound-theme-loader.h"
@@ -81,6 +81,8 @@ purple_sound_play_event(PurpleSoundEventID event, const PurpleAccount *account)
if (!purple_sound_play_required(account))
return;
+ g_return_if_fail(event < PURPLE_NUM_SOUNDS);
+
if (time(NULL) - last_played[event] < 2)
return;
last_played[event] = time(NULL);
@@ -99,6 +101,33 @@ purple_sound_play_event(PurpleSoundEventID event, const PurpleAccount *account)
}
}
+static PurpleSoundUiOps *
+purple_sound_ui_ops_copy(PurpleSoundUiOps *ops)
+{
+ PurpleSoundUiOps *ops_new;
+
+ g_return_val_if_fail(ops != NULL, NULL);
+
+ ops_new = g_new(PurpleSoundUiOps, 1);
+ *ops_new = *ops;
+
+ return ops_new;
+}
+
+GType
+purple_sound_ui_ops_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleSoundUiOps",
+ (GBoxedCopyFunc)purple_sound_ui_ops_copy,
+ (GBoxedFreeFunc)g_free);
+ }
+
+ return type;
+}
+
void
purple_sound_set_ui_ops(PurpleSoundUiOps *ops)
{
@@ -128,10 +157,7 @@ purple_sound_init()
purple_signal_register(handle, "playing-sound-event",
purple_marshal_BOOLEAN__INT_POINTER,
- purple_value_new(PURPLE_TYPE_BOOLEAN), 2,
- purple_value_new(PURPLE_TYPE_INT),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT));
+ G_TYPE_BOOLEAN, 2, G_TYPE_INT, PURPLE_TYPE_ACCOUNT);
purple_prefs_add_none("/purple/sound");
purple_prefs_add_int("/purple/sound/while_status", STATUS_AVAILABLE);
diff --git a/libpurple/sound.h b/libpurple/sound.h
index 1879725818..3ed643424a 100644
--- a/libpurple/sound.h
+++ b/libpurple/sound.h
@@ -1,9 +1,3 @@
-/**
- * @file sound.h Sound API
- * @ingroup core
- * @see @ref sound-signals
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -24,120 +18,160 @@
* 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_SOUND_H_
#define _PURPLE_SOUND_H_
+/**
+ * SECTION:sound
+ * @section_id: libpurple-sound
+ * @short_description: <filename>sound.h</filename>
+ * @title: Sound API
+ * @see_also: <link linkend="chapter-signals-sound">Sound signals</link>
+ */
#include "account.h"
+#define PURPLE_TYPE_SOUND_UI_OPS (purple_sound_ui_ops_get_type())
+
/**************************************************************************/
/** Data Structures */
/**************************************************************************/
-
+typedef struct _PurpleSoundUiOps PurpleSoundUiOps;
/**
+ * PurpleSoundEventID:
+ * @PURPLE_SOUND_BUDDY_ARRIVE: Buddy signs on.
+ * @PURPLE_SOUND_BUDDY_LEAVE: Buddy signs off.
+ * @PURPLE_SOUND_RECEIVE: Receive an IM.
+ * @PURPLE_SOUND_FIRST_RECEIVE: Receive an IM that starts a conv.
+ * @PURPLE_SOUND_SEND: Send an IM.
+ * @PURPLE_SOUND_CHAT_JOIN: Someone joins a chat.
+ * @PURPLE_SOUND_CHAT_LEAVE: Someone leaves a chat.
+ * @PURPLE_SOUND_CHAT_YOU_SAY: You say something in a chat.
+ * @PURPLE_SOUND_CHAT_SAY: Someone else says somthing in a chat.
+ * @PURPLE_SOUND_POUNCE_DEFAULT: Default sound for a buddy pounce.
+ * @PURPLE_SOUND_CHAT_NICK: Someone says your name in a chat.
+ * @PURPLE_SOUND_GOT_ATTENTION: Got an attention.
+ * @PURPLE_NUM_SOUNDS: Total number of sounds.
+ *
* A type of sound.
*/
-typedef enum _PurpleSoundEventID
+typedef enum
{
- PURPLE_SOUND_BUDDY_ARRIVE = 0, /**< Buddy signs on. */
- PURPLE_SOUND_BUDDY_LEAVE, /**< Buddy signs off. */
- PURPLE_SOUND_RECEIVE, /**< Receive an IM. */
- PURPLE_SOUND_FIRST_RECEIVE, /**< Receive an IM that starts a conv. */
- PURPLE_SOUND_SEND, /**< Send an IM. */
- PURPLE_SOUND_CHAT_JOIN, /**< Someone joins a chat. */
- PURPLE_SOUND_CHAT_LEAVE, /**< Someone leaves a chat. */
- PURPLE_SOUND_CHAT_YOU_SAY, /**< You say something in a chat. */
- PURPLE_SOUND_CHAT_SAY, /**< Someone else says somthing in a chat. */
- PURPLE_SOUND_POUNCE_DEFAULT, /**< Default sound for a buddy pounce. */
- PURPLE_SOUND_CHAT_NICK, /**< Someone says your name in a chat. */
- PURPLE_SOUND_GOT_ATTENTION, /**< Got an attention */
- PURPLE_NUM_SOUNDS /**< Total number of sounds. */
+ PURPLE_SOUND_BUDDY_ARRIVE = 0,
+ PURPLE_SOUND_BUDDY_LEAVE,
+ PURPLE_SOUND_RECEIVE,
+ PURPLE_SOUND_FIRST_RECEIVE,
+ PURPLE_SOUND_SEND,
+ PURPLE_SOUND_CHAT_JOIN,
+ PURPLE_SOUND_CHAT_LEAVE,
+ PURPLE_SOUND_CHAT_YOU_SAY,
+ PURPLE_SOUND_CHAT_SAY,
+ PURPLE_SOUND_POUNCE_DEFAULT,
+ PURPLE_SOUND_CHAT_NICK,
+ PURPLE_SOUND_GOT_ATTENTION,
+ PURPLE_NUM_SOUNDS
} PurpleSoundEventID;
-/** Operations used by the core to request that particular sound files, or the
- * sound associated with a particular event, should be played.
+/**
+ * PurpleSoundUiOps:
+ *
+ * Operations used by the core to request that particular sound files, or the
+ * sound associated with a particular event, should be played.
*/
-typedef struct _PurpleSoundUiOps
+struct _PurpleSoundUiOps
{
void (*init)(void);
void (*uninit)(void);
void (*play_file)(const char *filename);
void (*play_event)(PurpleSoundEventID event);
+ /*< private >*/
void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
void (*_purple_reserved4)(void);
-} PurpleSoundUiOps;
+};
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name Sound API */
+/* Sound API */
/**************************************************************************/
-/*@{*/
/**
- * Plays the specified sound file.
+ * purple_sound_ui_ops_get_type:
*
- * @param filename The file to play.
- * @param account The account that this sound is associated with, or
+ * Returns: The #GType for the #PurpleSoundUiOps boxed structure.
+ */
+GType purple_sound_ui_ops_get_type(void);
+
+/**
+ * purple_sound_play_file:
+ * @filename: The file to play.
+ * @account: The account that this sound is associated with, or
* NULL if the sound is not associated with any specific
* account. This is needed for the "sounds while away?"
* preference to work correctly.
+ *
+ * Plays the specified sound file.
*/
void purple_sound_play_file(const char *filename, const PurpleAccount *account);
/**
- * Plays the sound associated with the specified event.
- *
- * @param event The event.
- * @param account The account that this sound is associated with, or
+ * purple_sound_play_event:
+ * @event: The event.
+ * @account: The account that this sound is associated with, or
* NULL if the sound is not associated with any specific
* account. This is needed for the "sounds while away?"
* preference to work correctly.
+ *
+ * Plays the sound associated with the specified event.
*/
void purple_sound_play_event(PurpleSoundEventID event, const PurpleAccount *account);
/**
- * Sets the UI sound operations
+ * purple_sound_set_ui_ops:
+ * @ops: The UI sound operations structure.
*
- * @param ops The UI sound operations structure.
+ * Sets the UI sound operations
*/
void purple_sound_set_ui_ops(PurpleSoundUiOps *ops);
/**
+ * purple_sound_get_ui_ops:
+ *
* Gets the UI sound operations
*
- * @return The UI sound operations structure.
+ * Returns: The UI sound operations structure.
*/
PurpleSoundUiOps *purple_sound_get_ui_ops(void);
/**
+ * purple_sound_init:
+ *
* Initializes the sound subsystem
*/
void purple_sound_init(void);
/**
+ * purple_sound_uninit:
+ *
* Shuts down the sound subsystem
*/
void purple_sound_uninit(void);
/**
+ * purple_sounds_get_handle:
+ *
* Returns the sound subsystem handle.
*
- * @return The sound subsystem handle.
+ * Returns: The sound subsystem handle.
*/
void *purple_sounds_get_handle(void);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_SOUND_H_ */
diff --git a/libpurple/sslconn.c b/libpurple/sslconn.c
index e6365d390e..15065cf2aa 100644
--- a/libpurple/sslconn.c
+++ b/libpurple/sslconn.c
@@ -1,8 +1,3 @@
-/**
- * @file sslconn.c SSL API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -162,12 +157,23 @@ purple_ssl_input_add(PurpleSslConnection *gsc, PurpleSslInputFunction func,
g_return_if_fail(func != NULL);
g_return_if_fail(purple_ssl_is_supported());
+ purple_ssl_input_remove(gsc);
+
gsc->recv_cb_data = data;
gsc->recv_cb = func;
gsc->inpa = purple_input_add(gsc->fd, PURPLE_INPUT_READ, recv_cb, gsc);
}
+void
+purple_ssl_input_remove(PurpleSslConnection *gsc)
+{
+ if (gsc->inpa > 0) {
+ purple_input_remove(gsc->inpa);
+ gsc->inpa = 0;
+ }
+}
+
const gchar *
purple_ssl_strerror(PurpleSslErrorType error)
{
@@ -185,15 +191,6 @@ purple_ssl_strerror(PurpleSslErrorType error)
}
PurpleSslConnection *
-purple_ssl_connect_fd(PurpleAccount *account, int fd,
- PurpleSslInputFunction func,
- PurpleSslErrorFunction error_func,
- void *data)
-{
- return purple_ssl_connect_with_host_fd(account, fd, func, error_func, NULL, data);
-}
-
-PurpleSslConnection *
purple_ssl_connect_with_host_fd(PurpleAccount *account, int fd,
PurpleSslInputFunction func,
PurpleSslErrorFunction error_func,
diff --git a/libpurple/sslconn.h b/libpurple/sslconn.h
index 0bceccd57a..5646fc78b1 100644
--- a/libpurple/sslconn.h
+++ b/libpurple/sslconn.h
@@ -1,8 +1,3 @@
-/**
- * @file sslconn.h SSL API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -23,10 +18,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 _PURPLE_SSLCONN_H_
#define _PURPLE_SSLCONN_H_
+/**
+ * SECTION:sslconn
+ * @section_id: libpurple-sslconn
+ * @short_description: <filename>sslconn.h</filename>
+ * @title: SSL API
+ */
-/** Possible SSL errors. */
+/**
+ * PurpleSslErrorType:
+ *
+ * Possible SSL errors.
+ */
typedef enum
{
PURPLE_SSL_HANDSHAKE_FAILED = 1,
@@ -39,147 +45,148 @@ typedef enum
#define PURPLE_SSL_DEFAULT_PORT 443
-/** @copydoc _PurpleSslConnection */
typedef struct _PurpleSslConnection PurpleSslConnection;
+typedef struct _PurpleSslOps PurpleSslOps;
typedef void (*PurpleSslInputFunction)(gpointer, PurpleSslConnection *,
PurpleInputCondition);
typedef void (*PurpleSslErrorFunction)(PurpleSslConnection *, PurpleSslErrorType,
gpointer);
+/**
+ * PurpleSslConnection:
+ * @host: Hostname to which the SSL connection will be made
+ * @port: Port to connect to
+ * @connect_cb_data: Data to pass to @connect_cb
+ * @connect_cb: Callback triggered once the SSL handshake is complete
+ * @error_cb: Callback triggered if there is an error during connection
+ * @recv_cb_data: Data passed to @recv_cb
+ * @recv_cb: User-defined callback executed when the SSL connection
+ * receives data
+ * @fd: File descriptor used to refer to the socket
+ * @inpa: Glib event source ID; used to refer to the received data
+ * callback in the glib eventloop
+ * @connect_data: Data related to the underlying TCP connection
+ * @private_data: Internal connection data managed by the SSL backend
+ * (GnuTLS/LibNSS/whatever)
+ * @verifier: Verifier to use in authenticating the peer
+ */
struct _PurpleSslConnection
{
- /** Hostname to which the SSL connection will be made */
char *host;
- /** Port to connect to */
int port;
- /** Data to pass to PurpleSslConnection::connect_cb() */
void *connect_cb_data;
- /** Callback triggered once the SSL handshake is complete */
PurpleSslInputFunction connect_cb;
- /** Callback triggered if there is an error during connection */
PurpleSslErrorFunction error_cb;
- /** Data passed to PurpleSslConnection::recv_cb() */
void *recv_cb_data;
- /** User-defined callback executed when the SSL connection receives data */
PurpleSslInputFunction recv_cb;
- /** File descriptor used to refer to the socket */
int fd;
- /** Glib event source ID; used to refer to the received data callback
- * in the glib eventloop */
guint inpa;
- /** Data related to the underlying TCP connection */
PurpleProxyConnectData *connect_data;
- /** Internal connection data managed by the SSL backend (GnuTLS/LibNSS/whatever) */
void *private_data;
-
- /** Verifier to use in authenticating the peer */
PurpleCertificateVerifier *verifier;
};
/**
+ * PurpleSslOps:
+ * @init: Initializes the SSL system provided. See purple_ssl_init().
+ * <sbr/>Returns: %TRUE if initialization succeeded
+ * @uninit: Unloads the SSL system. Inverse of PurpleSslOps::init.
+ * See purple_ssl_uninit().
+ * @connectfunc: Sets up the SSL connection for a #PurpleSslConnection once the
+ * TCP connection has been established. See purple_ssl_connect().
+ * @close: Destroys the internal data of the SSL connection provided. Freeing
+ * @gsc itself is left to purple_ssl_close().
+ * @read: Reads data from a connection (like POSIX read()).
+ * See purple_ssl_read().
+ * <sbr/>@gsc: Connection context
+ * <sbr/>@data: Pointer to buffer to drop data into
+ * <sbr/>@len: Maximum number of bytes to read
+ * <sbr/>Returns: Number of bytes actually written into @data
+ * (which may be less than @len), or <0 on error
+ * @write: Writes data to a connection (like POSIX send()).
+ * See purple_ssl_write().
+ * <sbr/>@gsc: Connection context
+ * <sbr/>@data: Data buffer to send data from
+ * <sbr/>@len: Number of bytes to send from buffer
+ * <sbr/>Returns: The number of bytes written to @data (may be less than
+ * @len) or <0 on error
+ * @get_peer_certificates: Obtains the certificate chain provided by the peer.
+ * See #PurpleCertificate.
+ * <sbr/>@gsc: Connection context
+ * <sbr/>Returns: A newly allocated list containing the
+ * certificates the peer provided.
+ *
* SSL implementation operations structure.
*
- * Every SSL implementation must provide all of these and register it via purple_ssl_set_ops()
+ * Every SSL implementation must provide all of these and register it via
+ * purple_ssl_set_ops().
* These should not be called directly! Instead, use the purple_ssl_* functions.
*/
-typedef struct
+struct _PurpleSslOps
{
- /** Initializes the SSL system provided.
- * @return @a TRUE if initialization succeeded
- * @see purple_ssl_init
- */
gboolean (*init)(void);
- /** Unloads the SSL system. Inverse of PurpleSslOps::init.
- * @see purple_ssl_uninit
- */
void (*uninit)(void);
- /** Sets up the SSL connection for a #PurpleSslConnection once
- * the TCP connection has been established
- * @see purple_ssl_connect
- */
void (*connectfunc)(PurpleSslConnection *gsc);
- /** Destroys the internal data of the SSL connection provided.
- * Freeing gsc itself is left to purple_ssl_close()
- * @see purple_ssl_close
- */
void (*close)(PurpleSslConnection *gsc);
- /** Reads data from a connection (like POSIX read())
- * @param gsc Connection context
- * @param data Pointer to buffer to drop data into
- * @param len Maximum number of bytes to read
- * @return Number of bytes actually written into @a data (which may be
- * less than @a len), or <0 on error
- * @see purple_ssl_read
- */
size_t (*read)(PurpleSslConnection *gsc, void *data, size_t len);
- /** Writes data to a connection (like POSIX send())
- * @param gsc Connection context
- * @param data Data buffer to send data from
- * @param len Number of bytes to send from buffer
- * @return The number of bytes written to @a data (may be less than
- * @a len) or <0 on error
- * @see purple_ssl_write
- */
size_t (*write)(PurpleSslConnection *gsc, const void *data, size_t len);
- /** Obtains the certificate chain provided by the peer
- *
- * @param gsc Connection context
- * @return A newly allocated list containing the certificates
- * the peer provided.
- * @see PurpleCertificate
- * @todo Decide whether the ordering of certificates in this
- * list can be guaranteed.
- */
+
+ /* TODO Decide whether the ordering of certificates in this list can be
+ guaranteed. */
GList * (* get_peer_certificates)(PurpleSslConnection * gsc);
+ /*< private >*/
+ void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
void (*_purple_reserved4)(void);
-} PurpleSslOps;
+};
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name SSL API */
+/* SSL API */
/**************************************************************************/
-/*@{*/
/**
+ * purple_ssl_is_supported:
+ *
* Returns whether or not SSL is currently supported.
*
- * @return @a TRUE if SSL is supported, or @a FALSE otherwise.
+ * Returns: %TRUE if SSL is supported, or %FALSE otherwise.
*/
gboolean purple_ssl_is_supported(void);
/**
+ * purple_ssl_strerror:
+ * @error: Error code
+ *
* Returns a human-readable string for an SSL error.
*
- * @param error Error code
- * @return Human-readable error explanation
+ * Returns: Human-readable error explanation
*/
const gchar * purple_ssl_strerror(PurpleSslErrorType error);
/**
+ * purple_ssl_connect:
+ * @account: The account making the connection.
+ * @host: The destination host.
+ * @port: The destination port.
+ * @func: The SSL input handler function.
+ * @error_func: The SSL error handler function. This function
+ * should <emphasis>NOT</emphasis> call purple_ssl_close(). In
+ * the event of an error the #PurpleSslConnection will be
+ * destroyed for you.
+ * @data: User-defined data.
+ *
* Makes a SSL connection to the specified host and port. The caller
* should keep track of the returned value and use it to cancel the
* connection, if needed.
*
- * @param account The account making the connection.
- * @param host The destination host.
- * @param port The destination port.
- * @param func The SSL input handler function.
- * @param error_func The SSL error handler function. This function
- * should <strong>NOT</strong> call purple_ssl_close(). In
- * the event of an error the #PurpleSslConnection will be
- * destroyed for you.
- * @param data User-defined data.
- *
- * @return The SSL connection handle.
+ * Returns: The SSL connection handle.
*/
PurpleSslConnection *purple_ssl_connect(PurpleAccount *account, const char *host,
int port, PurpleSslInputFunction func,
@@ -187,23 +194,23 @@ PurpleSslConnection *purple_ssl_connect(PurpleAccount *account, const char *host
void *data);
/**
+ * purple_ssl_connect_with_ssl_cn:
+ * @account: The account making the connection.
+ * @host: The destination host.
+ * @port: The destination port.
+ * @func: The SSL input handler function.
+ * @error_func: The SSL error handler function. This function
+ * should <emphasis>NOT</emphasis> call purple_ssl_close(). In
+ * the event of an error the #PurpleSslConnection will be
+ * destroyed for you.
+ * @ssl_host: The hostname of the other peer (to verify the CN)
+ * @data: User-defined data.
+ *
* Makes a SSL connection to the specified host and port, using the separate
* name to verify with the certificate. The caller should keep track of the
* returned value and use it to cancel the connection, if needed.
*
- * @param account The account making the connection.
- * @param host The destination host.
- * @param port The destination port.
- * @param func The SSL input handler function.
- * @param error_func The SSL error handler function. This function
- * should <strong>NOT</strong> call purple_ssl_close(). In
- * the event of an error the #PurpleSslConnection will be
- * destroyed for you.
- * @param ssl_host The hostname of the other peer (to verify the CN)
- * @param data User-defined data.
- *
- * @return The SSL connection handle.
- * @since 2.6.0
+ * Returns: The SSL connection handle.
*/
PurpleSslConnection *purple_ssl_connect_with_ssl_cn(PurpleAccount *account, const char *host,
int port, PurpleSslInputFunction func,
@@ -211,39 +218,18 @@ PurpleSslConnection *purple_ssl_connect_with_ssl_cn(PurpleAccount *account, cons
const char *ssl_host,
void *data);
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_SSLCONN_C_)
/**
- * Makes a SSL connection using an already open file descriptor.
- *
- * @deprecated Use purple_ssl_connect_with_host_fd() instead.
- *
- * @param account The account making the connection.
- * @param fd The file descriptor.
- * @param func The SSL input handler function.
- * @param error_func The SSL error handler function.
- * @param data User-defined data.
+ * purple_ssl_connect_with_host_fd:
+ * @account: The account making the connection.
+ * @fd: The file descriptor.
+ * @func: The SSL input handler function.
+ * @error_func: The SSL error handler function.
+ * @host: The hostname of the other peer (to verify the CN)
+ * @data: User-defined data.
*
- * @return The SSL connection handle.
- */
-PurpleSslConnection *purple_ssl_connect_fd(PurpleAccount *account, int fd,
- PurpleSslInputFunction func,
- PurpleSslErrorFunction error_func,
- void *data);
-#endif
-
-/**
* Makes a SSL connection using an already open file descriptor.
*
- * @param account The account making the connection.
- * @param fd The file descriptor.
- * @param func The SSL input handler function.
- * @param error_func The SSL error handler function.
- * @param host The hostname of the other peer (to verify the CN)
- * @param data User-defined data.
- *
- * @return The SSL connection handle.
- *
- * @since 2.2.0
+ * Returns: The SSL connection handle.
*/
PurpleSslConnection *purple_ssl_connect_with_host_fd(PurpleAccount *account, int fd,
PurpleSslInputFunction func,
@@ -252,92 +238,106 @@ PurpleSslConnection *purple_ssl_connect_with_host_fd(PurpleAccount *account, int
void *data);
/**
+ * purple_ssl_input_add:
+ * @gsc: The SSL connection handle.
+ * @func: The callback function.
+ * @data: User-defined data.
+ *
* Adds an input watcher for the specified SSL connection.
* Once the SSL handshake is complete, use this to watch for actual data across it.
- *
- * @param gsc The SSL connection handle.
- * @param func The callback function.
- * @param data User-defined data.
*/
void purple_ssl_input_add(PurpleSslConnection *gsc, PurpleSslInputFunction func,
void *data);
/**
- * Closes a SSL connection.
+ * purple_ssl_input_remove:
+ * @gsc: The SSL connection handle.
+ *
+ * Removes an input watcher, added with purple_ssl_input_add().
*
- * @param gsc The SSL connection to close.
+ * If there is no input watcher set, does nothing.
+ */
+void
+purple_ssl_input_remove(PurpleSslConnection *gsc);
+
+/**
+ * purple_ssl_close:
+ * @gsc: The SSL connection to close.
+ *
+ * Closes a SSL connection.
*/
void purple_ssl_close(PurpleSslConnection *gsc);
/**
- * Reads data from an SSL connection.
+ * purple_ssl_read:
+ * @gsc: The SSL connection handle.
+ * @buffer: The destination buffer.
+ * @len: The maximum number of bytes to read.
*
- * @param gsc The SSL connection handle.
- * @param buffer The destination buffer.
- * @param len The maximum number of bytes to read.
+ * Reads data from an SSL connection.
*
- * @return The number of bytes read.
+ * Returns: The number of bytes read.
*/
size_t purple_ssl_read(PurpleSslConnection *gsc, void *buffer, size_t len);
/**
- * Writes data to an SSL connection.
+ * purple_ssl_write:
+ * @gsc: The SSL connection handle.
+ * @buffer: The buffer to write.
+ * @len: The length of the data to write.
*
- * @param gsc The SSL connection handle.
- * @param buffer The buffer to write.
- * @param len The length of the data to write.
+ * Writes data to an SSL connection.
*
- * @return The number of bytes written.
+ * Returns: The number of bytes written.
*/
size_t purple_ssl_write(PurpleSslConnection *gsc, const void *buffer, size_t len);
/**
- * Obtains the peer's presented certificates
+ * purple_ssl_get_peer_certificates:
+ * @gsc: The SSL connection handle
*
- * @param gsc The SSL connection handle
- *
- * @return The peer certificate chain, in the order of certificate, issuer,
- * issuer's issuer, etc. @a NULL if no certificates have been provided,
+ * Obtains the peer's presented certificates
*
- * @since 2.2.0
+ * Returns: The peer certificate chain, in the order of certificate, issuer,
+ * issuer's issuer, etc. %NULL if no certificates have been provided,
*/
GList * purple_ssl_get_peer_certificates(PurpleSslConnection *gsc);
-/*@}*/
-
/**************************************************************************/
-/** @name Subsystem API */
+/* Subsystem API */
/**************************************************************************/
-/*@{*/
/**
- * Sets the current SSL operations structure.
+ * purple_ssl_set_ops:
+ * @ops: The SSL operations structure to assign.
*
- * @param ops The SSL operations structure to assign.
+ * Sets the current SSL operations structure.
*/
void purple_ssl_set_ops(PurpleSslOps *ops);
/**
+ * purple_ssl_get_ops:
+ *
* Returns the current SSL operations structure.
*
- * @return The SSL operations structure.
+ * Returns: The SSL operations structure.
*/
PurpleSslOps *purple_ssl_get_ops(void);
/**
+ * purple_ssl_init:
+ *
* Initializes the SSL subsystem.
*/
void purple_ssl_init(void);
/**
+ * purple_ssl_uninit:
+ *
* Uninitializes the SSL subsystem.
*/
void purple_ssl_uninit(void);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_SSLCONN_H_ */
diff --git a/libpurple/status.c b/libpurple/status.c
index 9f7f80f151..5e1b964f40 100644
--- a/libpurple/status.c
+++ b/libpurple/status.c
@@ -1,8 +1,3 @@
-/**
- * @file status.c Status API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -23,11 +18,10 @@
* 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_STATUS_C_
-
#include "internal.h"
+#include "glibcompat.h"
-#include "blist.h"
+#include "buddylist.h"
#include "core.h"
#include "dbus-maybe.h"
#include "debug.h"
@@ -35,16 +29,22 @@
#include "prefs.h"
#include "status.h"
-/**
+#define PURPLE_STATUS_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_STATUS, PurpleStatusPrivate))
+
+typedef struct _PurpleStatusPrivate PurpleStatusPrivate;
+
+/*
* A type of status.
*/
struct _PurpleStatusType
{
+ int box_count;
+
PurpleStatusPrimitive primitive;
char *id;
char *name;
- char *primary_attr_id;
gboolean saveable;
gboolean user_settable;
@@ -53,60 +53,22 @@ struct _PurpleStatusType
GList *attrs;
};
-/**
+/*
* A status attribute.
*/
-struct _PurpleStatusAttr
+struct _PurpleStatusAttribute
{
char *id;
char *name;
- PurpleValue *value_type;
-};
-
-/**
- * A list of statuses.
- */
-struct _PurplePresence
-{
- PurplePresenceContext context;
-
- gboolean idle;
- time_t idle_time;
- time_t login_time;
-
- GList *statuses;
- GHashTable *status_table;
-
- PurpleStatus *active_status;
-
- union
- {
- PurpleAccount *account;
-
- struct
- {
- PurpleConversation *conv;
- char *user;
-
- } chat;
-
- struct
- {
- PurpleAccount *account;
- char *name;
- PurpleBuddy *buddy;
-
- } buddy;
-
- } u;
+ GValue *value_type;
};
-/**
- * An active status.
+/*
+ * Private data for PurpleStatus
*/
-struct _PurpleStatus
+struct _PurpleStatusPrivate
{
- PurpleStatusType *type;
+ PurpleStatusType *status_type;
PurplePresence *presence;
gboolean active;
@@ -115,17 +77,30 @@ struct _PurpleStatus
* The current values of the attributes for this status. The
* key is a string containing the name of the attribute. It is
* a borrowed reference from the list of attrs in the
- * PurpleStatusType. The value is a PurpleValue.
+ * PurpleStatusType. The value is a GValue.
*/
GHashTable *attr_values;
};
+/* GObject property enums */
+enum
+{
+ PROP_0,
+ PROP_STATUS_TYPE,
+ PROP_PRESENCE,
+ PROP_ACTIVE,
+ PROP_LAST
+};
+
typedef struct
{
PurpleAccount *account;
char *name;
} PurpleStatusBuddyKey;
+static GObjectClass *parent_class;
+static GParamSpec *properties[PROP_LAST];
+
static int primitive_scores[] =
{
0, /* unset */
@@ -170,6 +145,12 @@ static struct PurpleStatusPrimitiveMap
{ PURPLE_STATUS_MOOD, "mood", N_("Feeling") },
};
+int *
+_purple_statuses_get_primitive_scores(void)
+{
+ return primitive_scores;
+}
+
const char *
purple_primitive_get_id_from_type(PurpleStatusPrimitive type)
{
@@ -258,12 +239,48 @@ purple_status_type_new(PurpleStatusPrimitive primitive, const char *id,
user_settable, FALSE);
}
+static void
+status_type_add_attr(PurpleStatusType *status_type, const char *id,
+ const char *name, GValue *value)
+{
+ PurpleStatusAttribute *attr;
+
+ g_return_if_fail(status_type != NULL);
+ g_return_if_fail(id != NULL);
+ g_return_if_fail(name != NULL);
+ g_return_if_fail(value != NULL);
+
+ attr = purple_status_attribute_new(id, name, value);
+
+ status_type->attrs = g_list_append(status_type->attrs, attr);
+}
+
+static void
+status_type_add_attrs_vargs(PurpleStatusType *status_type, va_list args)
+{
+ const char *id, *name;
+ GValue *value;
+
+ g_return_if_fail(status_type != NULL);
+
+ while ((id = va_arg(args, const char *)) != NULL)
+ {
+ name = va_arg(args, const char *);
+ g_return_if_fail(name != NULL);
+
+ value = va_arg(args, GValue *);
+ g_return_if_fail(value != NULL);
+
+ status_type_add_attr(status_type, id, name, value);
+ }
+}
+
PurpleStatusType *
purple_status_type_new_with_attrs(PurpleStatusPrimitive primitive,
const char *id, const char *name,
gboolean saveable, gboolean user_settable,
gboolean independent, const char *attr_id,
- const char *attr_name, PurpleValue *attr_value,
+ const char *attr_name, GValue *attr_value,
...)
{
PurpleStatusType *status_type;
@@ -278,10 +295,10 @@ purple_status_type_new_with_attrs(PurpleStatusPrimitive primitive,
user_settable, independent);
/* Add the first attribute */
- purple_status_type_add_attr(status_type, attr_id, attr_name, attr_value);
+ status_type_add_attr(status_type, attr_id, attr_name, attr_value);
va_start(args, attr_value);
- purple_status_type_add_attrs_vargs(status_type, args);
+ status_type_add_attrs_vargs(status_type, args);
va_end(args);
return status_type;
@@ -294,79 +311,14 @@ purple_status_type_destroy(PurpleStatusType *status_type)
g_free(status_type->id);
g_free(status_type->name);
- g_free(status_type->primary_attr_id);
- g_list_foreach(status_type->attrs, (GFunc)purple_status_attr_destroy, NULL);
+ g_list_foreach(status_type->attrs, (GFunc)purple_status_attribute_destroy, NULL);
g_list_free(status_type->attrs);
PURPLE_DBUS_UNREGISTER_POINTER(status_type);
g_free(status_type);
}
-void
-purple_status_type_set_primary_attr(PurpleStatusType *status_type, const char *id)
-{
- g_return_if_fail(status_type != NULL);
-
- g_free(status_type->primary_attr_id);
- status_type->primary_attr_id = g_strdup(id);
-}
-
-void
-purple_status_type_add_attr(PurpleStatusType *status_type, const char *id,
- const char *name, PurpleValue *value)
-{
- PurpleStatusAttr *attr;
-
- g_return_if_fail(status_type != NULL);
- g_return_if_fail(id != NULL);
- g_return_if_fail(name != NULL);
- g_return_if_fail(value != NULL);
-
- attr = purple_status_attr_new(id, name, value);
-
- status_type->attrs = g_list_append(status_type->attrs, attr);
-}
-
-void
-purple_status_type_add_attrs_vargs(PurpleStatusType *status_type, va_list args)
-{
- const char *id, *name;
- PurpleValue *value;
-
- g_return_if_fail(status_type != NULL);
-
- while ((id = va_arg(args, const char *)) != NULL)
- {
- name = va_arg(args, const char *);
- g_return_if_fail(name != NULL);
-
- value = va_arg(args, PurpleValue *);
- g_return_if_fail(value != NULL);
-
- purple_status_type_add_attr(status_type, id, name, value);
- }
-}
-
-void
-purple_status_type_add_attrs(PurpleStatusType *status_type, const char *id,
- const char *name, PurpleValue *value, ...)
-{
- va_list args;
-
- g_return_if_fail(status_type != NULL);
- g_return_if_fail(id != NULL);
- g_return_if_fail(name != NULL);
- g_return_if_fail(value != NULL);
-
- /* Add the first attribute */
- purple_status_type_add_attr(status_type, id, name, value);
-
- va_start(args, value);
- purple_status_type_add_attrs_vargs(status_type, args);
- va_end(args);
-}
-
PurpleStatusPrimitive
purple_status_type_get_primitive(const PurpleStatusType *status_type)
{
@@ -435,15 +387,7 @@ purple_status_type_is_available(const PurpleStatusType *status_type)
return (primitive == PURPLE_STATUS_AVAILABLE);
}
-const char *
-purple_status_type_get_primary_attr(const PurpleStatusType *status_type)
-{
- g_return_val_if_fail(status_type != NULL, NULL);
-
- return status_type->primary_attr_id;
-}
-
-PurpleStatusAttr *
+PurpleStatusAttribute *
purple_status_type_get_attr(const PurpleStatusType *status_type, const char *id)
{
GList *l;
@@ -453,9 +397,9 @@ purple_status_type_get_attr(const PurpleStatusType *status_type, const char *id)
for (l = status_type->attrs; l != NULL; l = l->next)
{
- PurpleStatusAttr *attr = (PurpleStatusAttr *)l->data;
+ PurpleStatusAttribute *attr = (PurpleStatusAttribute *)l->data;
- if (purple_strequal(purple_status_attr_get_id(attr), id))
+ if (purple_strequal(purple_status_attribute_get_id(attr), id))
return attr;
}
@@ -492,19 +436,19 @@ purple_status_type_find_with_id(GList *status_types, const char *id)
/**************************************************************************
-* PurpleStatusAttr API
+* PurpleStatusAttribute API
**************************************************************************/
-PurpleStatusAttr *
-purple_status_attr_new(const char *id, const char *name, PurpleValue *value_type)
+PurpleStatusAttribute *
+purple_status_attribute_new(const char *id, const char *name, GValue *value_type)
{
- PurpleStatusAttr *attr;
+ PurpleStatusAttribute *attr;
g_return_val_if_fail(id != NULL, NULL);
g_return_val_if_fail(name != NULL, NULL);
g_return_val_if_fail(value_type != NULL, NULL);
- attr = g_new0(PurpleStatusAttr, 1);
- PURPLE_DBUS_REGISTER_POINTER(attr, PurpleStatusAttr);
+ attr = g_new0(PurpleStatusAttribute, 1);
+ PURPLE_DBUS_REGISTER_POINTER(attr, PurpleStatusAttribute);
attr->id = g_strdup(id);
attr->name = g_strdup(name);
@@ -514,21 +458,21 @@ purple_status_attr_new(const char *id, const char *name, PurpleValue *value_type
}
void
-purple_status_attr_destroy(PurpleStatusAttr *attr)
+purple_status_attribute_destroy(PurpleStatusAttribute *attr)
{
g_return_if_fail(attr != NULL);
g_free(attr->id);
g_free(attr->name);
- purple_value_destroy(attr->value_type);
+ purple_value_free(attr->value_type);
PURPLE_DBUS_UNREGISTER_POINTER(attr);
g_free(attr);
}
const char *
-purple_status_attr_get_id(const PurpleStatusAttr *attr)
+purple_status_attribute_get_id(const PurpleStatusAttribute *attr)
{
g_return_val_if_fail(attr != NULL, NULL);
@@ -536,15 +480,15 @@ purple_status_attr_get_id(const PurpleStatusAttr *attr)
}
const char *
-purple_status_attr_get_name(const PurpleStatusAttr *attr)
+purple_status_attribute_get_name(const PurpleStatusAttribute *attr)
{
g_return_val_if_fail(attr != NULL, NULL);
return attr->name;
}
-PurpleValue *
-purple_status_attr_get_value(const PurpleStatusAttr *attr)
+GValue *
+purple_status_attribute_get_value(const PurpleStatusAttribute *attr)
{
g_return_val_if_fail(attr != NULL, NULL);
@@ -555,54 +499,6 @@ purple_status_attr_get_value(const PurpleStatusAttr *attr)
/**************************************************************************
* PurpleStatus API
**************************************************************************/
-PurpleStatus *
-purple_status_new(PurpleStatusType *status_type, PurplePresence *presence)
-{
- PurpleStatus *status;
- GList *l;
-
- g_return_val_if_fail(status_type != NULL, NULL);
- g_return_val_if_fail(presence != NULL, NULL);
-
- status = g_new0(PurpleStatus, 1);
- PURPLE_DBUS_REGISTER_POINTER(status, PurpleStatus);
-
- status->type = status_type;
- status->presence = presence;
-
- status->attr_values =
- g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
- (GDestroyNotify)purple_value_destroy);
-
- for (l = purple_status_type_get_attrs(status_type); l != NULL; l = l->next)
- {
- PurpleStatusAttr *attr = (PurpleStatusAttr *)l->data;
- PurpleValue *value = purple_status_attr_get_value(attr);
- PurpleValue *new_value = purple_value_dup(value);
-
- g_hash_table_insert(status->attr_values,
- (char *)purple_status_attr_get_id(attr),
- new_value);
- }
-
- return status;
-}
-
-/*
- * TODO: If the PurpleStatus is in a PurplePresence, then
- * remove it from the PurplePresence?
- */
-void
-purple_status_destroy(PurpleStatus *status)
-{
- g_return_if_fail(status != NULL);
-
- g_hash_table_destroy(status->attr_values);
-
- PURPLE_DBUS_UNREGISTER_POINTER(status);
- g_free(status);
-}
-
static void
notify_buddy_status_update(PurpleBuddy *buddy, PurplePresence *presence,
PurpleStatus *old_status, PurpleStatus *new_status)
@@ -658,11 +554,10 @@ static void
notify_status_update(PurplePresence *presence, PurpleStatus *old_status,
PurpleStatus *new_status)
{
- PurplePresenceContext context = purple_presence_get_context(presence);
-
- if (context == PURPLE_PRESENCE_CONTEXT_ACCOUNT)
+ if (PURPLE_IS_ACCOUNT_PRESENCE(presence))
{
- PurpleAccount *account = purple_presence_get_account(presence);
+ PurpleAccount *account = purple_account_presence_get_account(
+ PURPLE_ACCOUNT_PRESENCE(presence));
PurpleAccountUiOps *ops = purple_accounts_get_ui_ops();
if (purple_account_get_enabled(account, purple_core_get_ui()))
@@ -673,10 +568,11 @@ notify_status_update(PurplePresence *presence, PurpleStatus *old_status,
ops->status_changed(account, new_status);
}
}
- else if (context == PURPLE_PRESENCE_CONTEXT_BUDDY)
+ else if (PURPLE_IS_BUDDY_PRESENCE(presence))
{
- notify_buddy_status_update(purple_presence_get_buddy(presence), presence,
- old_status, new_status);
+ notify_buddy_status_update(purple_buddy_presence_get_buddy(
+ PURPLE_BUDDY_PRESENCE(presence)), presence, old_status,
+ new_status);
}
}
@@ -696,16 +592,83 @@ status_has_changed(PurpleStatus *status)
if (purple_status_is_exclusive(status))
{
old_status = purple_presence_get_active_status(presence);
- if (old_status != NULL && (old_status != status))
- old_status->active = FALSE;
- presence->active_status = status;
+ if (old_status != NULL && (old_status != status)) {
+ PURPLE_STATUS_GET_PRIVATE(old_status)->active = FALSE;
+ g_object_notify_by_pspec(G_OBJECT(old_status),
+ properties[PROP_ACTIVE]);
+ }
}
else
old_status = NULL;
+ g_object_set(presence, "active-status", status, NULL);
+ g_object_notify_by_pspec(G_OBJECT(status), properties[PROP_ACTIVE]);
+
notify_status_update(presence, old_status, status);
}
+static void
+status_set_attr_boolean(PurpleStatus *status, const char *id,
+ gboolean value)
+{
+ GValue *attr_value;
+
+ g_return_if_fail(PURPLE_IS_STATUS(status));
+ g_return_if_fail(id != NULL);
+
+ /* Make sure this attribute exists and is the correct type. */
+ attr_value = purple_status_get_attr_value(status, id);
+ g_return_if_fail(attr_value != NULL);
+ g_return_if_fail(G_VALUE_TYPE(attr_value) == G_TYPE_BOOLEAN);
+
+ g_value_set_boolean(attr_value, value);
+}
+
+static void
+status_set_attr_int(PurpleStatus *status, const char *id, int value)
+{
+ GValue *attr_value;
+
+ g_return_if_fail(PURPLE_IS_STATUS(status));
+ g_return_if_fail(id != NULL);
+
+ /* Make sure this attribute exists and is the correct type. */
+ attr_value = purple_status_get_attr_value(status, id);
+ g_return_if_fail(attr_value != NULL);
+ g_return_if_fail(G_VALUE_TYPE(attr_value) == G_TYPE_INT);
+
+ g_value_set_int(attr_value, value);
+}
+
+static void
+status_set_attr_string(PurpleStatus *status, const char *id,
+ const char *value)
+{
+ GValue *attr_value;
+
+ g_return_if_fail(PURPLE_IS_STATUS(status));
+ g_return_if_fail(id != NULL);
+
+ /* Make sure this attribute exists and is the correct type. */
+ attr_value = purple_status_get_attr_value(status, id);
+ /* This used to be g_return_if_fail, but it's failing a LOT, so
+ * let's generate a log error for now. */
+ /* g_return_if_fail(attr_value != NULL); */
+ if (attr_value == NULL) {
+ purple_debug_error("status",
+ "Attempted to set status attribute '%s' for "
+ "status '%s', which is not legal. Fix "
+ "this!\n", id,
+ purple_status_type_get_name(purple_status_get_status_type(status)));
+ return;
+ }
+ g_return_if_fail(G_VALUE_TYPE(attr_value) == G_TYPE_STRING);
+
+ /* XXX: Check if the value has actually changed. If it has, and the status
+ * is active, should this trigger 'status_has_changed'? */
+ g_value_set_string(attr_value, value);
+}
+
void
purple_status_set_active(PurpleStatus *status, gboolean active)
{
@@ -715,7 +678,7 @@ purple_status_set_active(PurpleStatus *status, gboolean active)
/*
* This used to parse the va_list directly, but now it creates a GList
* and passes it to purple_status_set_active_with_attrs_list(). That
- * function was created because accounts.c needs to pass a GList of
+ * function was created because account.c needs to pass a GList of
* attributes to the status API.
*/
void
@@ -743,8 +706,9 @@ purple_status_set_active_with_attrs_list(PurpleStatus *status, gboolean active,
GList *l;
GList *specified_attr_ids = NULL;
PurpleStatusType *status_type;
+ PurpleStatusPrivate *priv = PURPLE_STATUS_GET_PRIVATE(status);
- g_return_if_fail(status != NULL);
+ g_return_if_fail(priv != NULL);
if (!active && purple_status_is_exclusive(status))
{
@@ -754,19 +718,19 @@ purple_status_set_active_with_attrs_list(PurpleStatus *status, gboolean active,
return;
}
- if (status->active != active)
+ if (priv->active != active)
{
changed = TRUE;
}
- status->active = active;
+ priv->active = active;
/* Set any attributes */
l = attrs;
while (l != NULL)
{
const gchar *id;
- PurpleValue *value;
+ GValue *value;
id = l->data;
l = l->next;
@@ -774,7 +738,7 @@ purple_status_set_active_with_attrs_list(PurpleStatus *status, gboolean active,
if (value == NULL)
{
purple_debug_warning("status", "The attribute \"%s\" on the status \"%s\" is "
- "not supported.\n", id, status->type->name);
+ "not supported.\n", id, priv->status_type->name);
/* Skip over the data and move on to the next attribute */
l = l->next;
continue;
@@ -782,31 +746,31 @@ purple_status_set_active_with_attrs_list(PurpleStatus *status, gboolean active,
specified_attr_ids = g_list_prepend(specified_attr_ids, (gpointer)id);
- if (value->type == PURPLE_TYPE_STRING)
+ if (G_VALUE_TYPE(value) == G_TYPE_STRING)
{
const gchar *string_data = l->data;
l = l->next;
- if (purple_strequal(string_data, value->data.string_data))
+ if (purple_strequal(string_data, g_value_get_string(value)))
continue;
- purple_status_set_attr_string(status, id, string_data);
+ status_set_attr_string(status, id, string_data);
changed = TRUE;
}
- else if (value->type == PURPLE_TYPE_INT)
+ else if (G_VALUE_TYPE(value) == G_TYPE_INT)
{
int int_data = GPOINTER_TO_INT(l->data);
l = l->next;
- if (int_data == value->data.int_data)
+ if (int_data == g_value_get_int(value))
continue;
- purple_status_set_attr_int(status, id, int_data);
+ status_set_attr_int(status, id, int_data);
changed = TRUE;
}
- else if (value->type == PURPLE_TYPE_BOOLEAN)
+ else if (G_VALUE_TYPE(value) == G_TYPE_BOOLEAN)
{
gboolean boolean_data = GPOINTER_TO_INT(l->data);
l = l->next;
- if (boolean_data == value->data.boolean_data)
+ if (boolean_data == g_value_get_boolean(value))
continue;
- purple_status_set_attr_boolean(status, id, boolean_data);
+ status_set_attr_boolean(status, id, boolean_data);
changed = TRUE;
}
else
@@ -817,41 +781,41 @@ purple_status_set_active_with_attrs_list(PurpleStatus *status, gboolean active,
}
/* Reset any unspecified attributes to their default value */
- status_type = purple_status_get_type(status);
+ status_type = purple_status_get_status_type(status);
l = purple_status_type_get_attrs(status_type);
while (l != NULL) {
- PurpleStatusAttr *attr;
+ PurpleStatusAttribute *attr;
attr = l->data;
l = l->next;
if (!g_list_find_custom(specified_attr_ids, attr->id, (GCompareFunc)strcmp)) {
- PurpleValue *default_value;
- default_value = purple_status_attr_get_value(attr);
- if (default_value->type == PURPLE_TYPE_STRING) {
+ GValue *default_value;
+ default_value = purple_status_attribute_get_value(attr);
+ if (G_VALUE_TYPE(default_value) == G_TYPE_STRING) {
const char *cur = purple_status_get_attr_string(status, attr->id);
- const char *def = purple_value_get_string(default_value);
+ const char *def = g_value_get_string(default_value);
if ((cur == NULL && def == NULL)
|| (cur != NULL && def != NULL
&& !strcmp(cur, def))) {
continue;
}
- purple_status_set_attr_string(status, attr->id, def);
- } else if (default_value->type == PURPLE_TYPE_INT) {
+ status_set_attr_string(status, attr->id, def);
+ } else if (G_VALUE_TYPE(default_value) == G_TYPE_INT) {
int cur = purple_status_get_attr_int(status, attr->id);
- int def = purple_value_get_int(default_value);
+ int def = g_value_get_int(default_value);
if (cur == def)
continue;
- purple_status_set_attr_int(status, attr->id, def);
- } else if (default_value->type == PURPLE_TYPE_BOOLEAN) {
+ status_set_attr_int(status, attr->id, def);
+ } else if (G_VALUE_TYPE(default_value) == G_TYPE_BOOLEAN) {
gboolean cur = purple_status_get_attr_boolean(status, attr->id);
- gboolean def = purple_value_get_boolean(default_value);
+ gboolean def = g_value_get_boolean(default_value);
if (cur == def)
continue;
- purple_status_set_attr_boolean(status, attr->id, def);
+ status_set_attr_boolean(status, attr->id, def);
}
changed = TRUE;
}
@@ -863,130 +827,74 @@ purple_status_set_active_with_attrs_list(PurpleStatus *status, gboolean active,
status_has_changed(status);
}
-void
-purple_status_set_attr_boolean(PurpleStatus *status, const char *id,
- gboolean value)
-{
- PurpleValue *attr_value;
-
- g_return_if_fail(status != NULL);
- g_return_if_fail(id != NULL);
-
- /* Make sure this attribute exists and is the correct type. */
- attr_value = purple_status_get_attr_value(status, id);
- g_return_if_fail(attr_value != NULL);
- g_return_if_fail(purple_value_get_type(attr_value) == PURPLE_TYPE_BOOLEAN);
-
- purple_value_set_boolean(attr_value, value);
-}
-
-void
-purple_status_set_attr_int(PurpleStatus *status, const char *id, int value)
-{
- PurpleValue *attr_value;
-
- g_return_if_fail(status != NULL);
- g_return_if_fail(id != NULL);
-
- /* Make sure this attribute exists and is the correct type. */
- attr_value = purple_status_get_attr_value(status, id);
- g_return_if_fail(attr_value != NULL);
- g_return_if_fail(purple_value_get_type(attr_value) == PURPLE_TYPE_INT);
-
- purple_value_set_int(attr_value, value);
-}
-
-void
-purple_status_set_attr_string(PurpleStatus *status, const char *id,
- const char *value)
-{
- PurpleValue *attr_value;
-
- g_return_if_fail(status != NULL);
- g_return_if_fail(id != NULL);
-
- /* Make sure this attribute exists and is the correct type. */
- attr_value = purple_status_get_attr_value(status, id);
- /* This used to be g_return_if_fail, but it's failing a LOT, so
- * let's generate a log error for now. */
- /* g_return_if_fail(attr_value != NULL); */
- if (attr_value == NULL) {
- purple_debug_error("status",
- "Attempted to set status attribute '%s' for "
- "status '%s', which is not legal. Fix "
- "this!\n", id,
- purple_status_type_get_name(purple_status_get_type(status)));
- return;
- }
- g_return_if_fail(purple_value_get_type(attr_value) == PURPLE_TYPE_STRING);
-
- /* XXX: Check if the value has actually changed. If it has, and the status
- * is active, should this trigger 'status_has_changed'? */
- purple_value_set_string(attr_value, value);
-}
-
PurpleStatusType *
-purple_status_get_type(const PurpleStatus *status)
+purple_status_get_status_type(const PurpleStatus *status)
{
- g_return_val_if_fail(status != NULL, NULL);
+ PurpleStatusPrivate *priv = PURPLE_STATUS_GET_PRIVATE(status);
+
+ g_return_val_if_fail(priv != NULL, NULL);
- return status->type;
+ return priv->status_type;
}
PurplePresence *
purple_status_get_presence(const PurpleStatus *status)
{
- g_return_val_if_fail(status != NULL, NULL);
+ PurpleStatusPrivate *priv = PURPLE_STATUS_GET_PRIVATE(status);
- return status->presence;
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->presence;
}
const char *
purple_status_get_id(const PurpleStatus *status)
{
- g_return_val_if_fail(status != NULL, NULL);
+ g_return_val_if_fail(PURPLE_IS_STATUS(status), NULL);
- return purple_status_type_get_id(purple_status_get_type(status));
+ return purple_status_type_get_id(purple_status_get_status_type(status));
}
const char *
purple_status_get_name(const PurpleStatus *status)
{
- g_return_val_if_fail(status != NULL, NULL);
+ g_return_val_if_fail(PURPLE_IS_STATUS(status), NULL);
- return purple_status_type_get_name(purple_status_get_type(status));
+ return purple_status_type_get_name(purple_status_get_status_type(status));
}
gboolean
purple_status_is_independent(const PurpleStatus *status)
{
- g_return_val_if_fail(status != NULL, FALSE);
+ g_return_val_if_fail(PURPLE_IS_STATUS(status), FALSE);
- return purple_status_type_is_independent(purple_status_get_type(status));
+ return purple_status_type_is_independent(purple_status_get_status_type(status));
}
gboolean
purple_status_is_exclusive(const PurpleStatus *status)
{
- g_return_val_if_fail(status != NULL, FALSE);
+ g_return_val_if_fail(PURPLE_IS_STATUS(status), FALSE);
- return purple_status_type_is_exclusive(purple_status_get_type(status));
+ return purple_status_type_is_exclusive(purple_status_get_status_type(status));
}
gboolean
purple_status_is_available(const PurpleStatus *status)
{
- g_return_val_if_fail(status != NULL, FALSE);
+ g_return_val_if_fail(PURPLE_IS_STATUS(status), FALSE);
- return purple_status_type_is_available(purple_status_get_type(status));
+ return purple_status_type_is_available(purple_status_get_status_type(status));
}
gboolean
purple_status_is_active(const PurpleStatus *status)
{
- g_return_val_if_fail(status != NULL, FALSE);
+ PurpleStatusPrivate *priv = PURPLE_STATUS_GET_PRIVATE(status);
+
+ g_return_val_if_fail(priv != NULL, FALSE);
- return status->active;
+ return priv->active;
}
gboolean
@@ -994,69 +902,71 @@ purple_status_is_online(const PurpleStatus *status)
{
PurpleStatusPrimitive primitive;
- g_return_val_if_fail( status != NULL, FALSE);
+ g_return_val_if_fail(PURPLE_IS_STATUS(status), FALSE);
- primitive = purple_status_type_get_primitive(purple_status_get_type(status));
+ primitive = purple_status_type_get_primitive(purple_status_get_status_type(status));
return (primitive != PURPLE_STATUS_UNSET &&
primitive != PURPLE_STATUS_OFFLINE);
}
-PurpleValue *
+GValue *
purple_status_get_attr_value(const PurpleStatus *status, const char *id)
{
- g_return_val_if_fail(status != NULL, NULL);
- g_return_val_if_fail(id != NULL, NULL);
+ PurpleStatusPrivate *priv = PURPLE_STATUS_GET_PRIVATE(status);
- return (PurpleValue *)g_hash_table_lookup(status->attr_values, id);
+ g_return_val_if_fail(priv != NULL, NULL);
+ g_return_val_if_fail(id != NULL, NULL);
+
+ return (GValue *)g_hash_table_lookup(priv->attr_values, id);
}
gboolean
purple_status_get_attr_boolean(const PurpleStatus *status, const char *id)
{
- const PurpleValue *value;
+ const GValue *value;
- g_return_val_if_fail(status != NULL, FALSE);
- g_return_val_if_fail(id != NULL, FALSE);
+ g_return_val_if_fail(PURPLE_IS_STATUS(status), FALSE);
+ g_return_val_if_fail(id != NULL, FALSE);
if ((value = purple_status_get_attr_value(status, id)) == NULL)
return FALSE;
- g_return_val_if_fail(purple_value_get_type(value) == PURPLE_TYPE_BOOLEAN, FALSE);
+ g_return_val_if_fail(G_VALUE_TYPE(value) == G_TYPE_BOOLEAN, FALSE);
- return purple_value_get_boolean(value);
+ return g_value_get_boolean(value);
}
int
purple_status_get_attr_int(const PurpleStatus *status, const char *id)
{
- const PurpleValue *value;
+ const GValue *value;
- g_return_val_if_fail(status != NULL, 0);
- g_return_val_if_fail(id != NULL, 0);
+ g_return_val_if_fail(PURPLE_IS_STATUS(status), 0);
+ g_return_val_if_fail(id != NULL, 0);
if ((value = purple_status_get_attr_value(status, id)) == NULL)
return 0;
- g_return_val_if_fail(purple_value_get_type(value) == PURPLE_TYPE_INT, 0);
+ g_return_val_if_fail(G_VALUE_TYPE(value) == G_TYPE_INT, 0);
- return purple_value_get_int(value);
+ return g_value_get_int(value);
}
const char *
purple_status_get_attr_string(const PurpleStatus *status, const char *id)
{
- const PurpleValue *value;
+ const GValue *value;
- g_return_val_if_fail(status != NULL, NULL);
- g_return_val_if_fail(id != NULL, NULL);
+ g_return_val_if_fail(PURPLE_IS_STATUS(status), NULL);
+ g_return_val_if_fail(id != NULL, NULL);
if ((value = purple_status_get_attr_value(status, id)) == NULL)
return NULL;
- g_return_val_if_fail(purple_value_get_type(value) == PURPLE_TYPE_STRING, NULL);
+ g_return_val_if_fail(G_VALUE_TYPE(value) == G_TYPE_STRING, NULL);
- return purple_value_get_string(value);
+ return g_value_get_string(value);
}
gint
@@ -1075,8 +985,8 @@ purple_status_compare(const PurpleStatus *status1, const PurpleStatus *status2)
else if (status2 == NULL)
return -1;
- type1 = purple_status_get_type(status1);
- type2 = purple_status_get_type(status2);
+ type1 = purple_status_get_status_type(status1);
+ type2 = purple_status_get_status_type(status2);
if (purple_status_is_active(status1))
score1 = primitive_scores[purple_status_type_get_primitive(type1)];
@@ -1094,549 +1004,284 @@ purple_status_compare(const PurpleStatus *status1, const PurpleStatus *status2)
/**************************************************************************
-* PurplePresence API
+* GBoxed code for PurpleStatusType
**************************************************************************/
-PurplePresence *
-purple_presence_new(PurplePresenceContext context)
+static PurpleStatusType *
+purple_status_type_ref(PurpleStatusType *status_type)
{
- PurplePresence *presence;
-
- g_return_val_if_fail(context != PURPLE_PRESENCE_CONTEXT_UNSET, NULL);
-
- presence = g_new0(PurplePresence, 1);
- PURPLE_DBUS_REGISTER_POINTER(presence, PurplePresence);
-
- presence->context = context;
-
- presence->status_table =
- g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, NULL);
-
- return presence;
-}
-
-PurplePresence *
-purple_presence_new_for_account(PurpleAccount *account)
-{
- PurplePresence *presence = NULL;
- g_return_val_if_fail(account != NULL, NULL);
-
- presence = purple_presence_new(PURPLE_PRESENCE_CONTEXT_ACCOUNT);
- presence->u.account = account;
- presence->statuses = purple_prpl_get_statuses(account, presence);
-
- return presence;
-}
-
-PurplePresence *
-purple_presence_new_for_conv(PurpleConversation *conv)
-{
- PurplePresence *presence;
-
- g_return_val_if_fail(conv != NULL, NULL);
+ g_return_val_if_fail(status_type != NULL, NULL);
- presence = purple_presence_new(PURPLE_PRESENCE_CONTEXT_CONV);
- presence->u.chat.conv = conv;
- /* presence->statuses = purple_prpl_get_statuses(conv->account, presence); ? */
+ status_type->box_count++;
- return presence;
+ return status_type;
}
-PurplePresence *
-purple_presence_new_for_buddy(PurpleBuddy *buddy)
+static void
+purple_status_type_unref(PurpleStatusType *status_type)
{
- PurplePresence *presence;
- PurpleAccount *account;
-
- g_return_val_if_fail(buddy != NULL, NULL);
- account = purple_buddy_get_account(buddy);
-
- presence = purple_presence_new(PURPLE_PRESENCE_CONTEXT_BUDDY);
-
- presence->u.buddy.name = g_strdup(purple_buddy_get_name(buddy));
- presence->u.buddy.account = account;
- presence->statuses = purple_prpl_get_statuses(account, presence);
-
- presence->u.buddy.buddy = buddy;
+ g_return_if_fail(status_type != NULL);
+ g_return_if_fail(status_type->box_count >= 0);
- return presence;
+ if (!status_type->box_count--)
+ purple_status_type_destroy(status_type);
}
-void
-purple_presence_destroy(PurplePresence *presence)
+GType
+purple_status_type_get_type(void)
{
- g_return_if_fail(presence != NULL);
+ static GType type = 0;
- if (purple_presence_get_context(presence) == PURPLE_PRESENCE_CONTEXT_BUDDY)
- {
- g_free(presence->u.buddy.name);
- }
- else if (purple_presence_get_context(presence) == PURPLE_PRESENCE_CONTEXT_CONV)
- {
- g_free(presence->u.chat.user);
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleStatusType",
+ (GBoxedCopyFunc)purple_status_type_ref,
+ (GBoxedFreeFunc)purple_status_type_unref);
}
- g_list_foreach(presence->statuses, (GFunc)purple_status_destroy, NULL);
- g_list_free(presence->statuses);
-
- g_hash_table_destroy(presence->status_table);
-
- PURPLE_DBUS_UNREGISTER_POINTER(presence);
- g_free(presence);
+ return type;
}
-void
-purple_presence_add_status(PurplePresence *presence, PurpleStatus *status)
+/**************************************************************************
+* GBoxed code for PurpleStatusAttribute
+**************************************************************************/
+static PurpleStatusAttribute *
+purple_status_attribute_copy(PurpleStatusAttribute *status_attr)
{
- g_return_if_fail(presence != NULL);
- g_return_if_fail(status != NULL);
+ g_return_val_if_fail(status_attr != NULL, NULL);
- presence->statuses = g_list_append(presence->statuses, status);
-
- g_hash_table_insert(presence->status_table,
- g_strdup(purple_status_get_id(status)), status);
+ return purple_status_attribute_new(status_attr->id,
+ status_attr->name,
+ purple_value_dup(status_attr->value_type));
}
-void
-purple_presence_add_list(PurplePresence *presence, GList *source_list)
+GType
+purple_status_attribute_get_type(void)
{
- GList *l;
+ static GType type = 0;
- g_return_if_fail(presence != NULL);
- g_return_if_fail(source_list != NULL);
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleStatusAttribute",
+ (GBoxedCopyFunc)purple_status_attribute_copy,
+ (GBoxedFreeFunc)purple_status_attribute_destroy);
+ }
- for (l = source_list; l != NULL; l = l->next)
- purple_presence_add_status(presence, (PurpleStatus *)l->data);
+ return type;
}
-void
-purple_presence_set_status_active(PurplePresence *presence, const char *status_id,
- gboolean active)
+/**************************************************************************
+* GBoxed code for PurpleMood
+**************************************************************************/
+static PurpleMood *
+purple_mood_copy(PurpleMood *mood)
{
- PurpleStatus *status;
-
- g_return_if_fail(presence != NULL);
- g_return_if_fail(status_id != NULL);
-
- status = purple_presence_get_status(presence, status_id);
+ PurpleMood *mood_copy;
- g_return_if_fail(status != NULL);
- /* TODO: Should we do the following? */
- /* g_return_if_fail(active == status->active); */
+ g_return_val_if_fail(mood != NULL, NULL);
- if (purple_status_is_exclusive(status))
- {
- if (!active)
- {
- purple_debug_warning("status",
- "Attempted to set a non-independent status "
- "(%s) inactive. Only independent statuses "
- "can be specifically marked inactive.",
- status_id);
- return;
- }
- }
+ mood_copy = g_new(PurpleMood, 1);
- purple_status_set_active(status, active);
-}
+ mood_copy->mood = g_strdup(mood->mood);
+ mood_copy->description = g_strdup(mood->description);
-void
-purple_presence_switch_status(PurplePresence *presence, const char *status_id)
-{
- purple_presence_set_status_active(presence, status_id, TRUE);
+ return mood_copy;
}
static void
-update_buddy_idle(PurpleBuddy *buddy, PurplePresence *presence,
- time_t current_time, gboolean old_idle, gboolean idle)
+purple_mood_free(PurpleMood *mood)
{
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
- PurpleAccount *account = purple_buddy_get_account(buddy);
-
- if (!old_idle && idle)
- {
- if (purple_prefs_get_bool("/purple/logging/log_system"))
- {
- PurpleLog *log = purple_account_get_log(account, FALSE);
+ g_free((gchar *)mood->mood);
+ g_free((gchar *)mood->description);
- if (log != NULL)
- {
- char *tmp, *tmp2;
- tmp = g_strdup_printf(_("%s became idle"),
- purple_buddy_get_alias(buddy));
- tmp2 = g_markup_escape_text(tmp, -1);
- g_free(tmp);
-
- purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
- purple_buddy_get_alias(buddy), current_time, tmp2);
- g_free(tmp2);
- }
- }
- }
- else if (old_idle && !idle)
- {
- if (purple_prefs_get_bool("/purple/logging/log_system"))
- {
- PurpleLog *log = purple_account_get_log(account, FALSE);
-
- if (log != NULL)
- {
- char *tmp, *tmp2;
- tmp = g_strdup_printf(_("%s became unidle"),
- purple_buddy_get_alias(buddy));
- tmp2 = g_markup_escape_text(tmp, -1);
- g_free(tmp);
-
- purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
- purple_buddy_get_alias(buddy), current_time, tmp2);
- g_free(tmp2);
- }
- }
- }
-
- if (old_idle != idle)
- purple_signal_emit(purple_blist_get_handle(), "buddy-idle-changed", buddy,
- old_idle, idle);
-
- purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy));
-
- /* Should this be done here? It'd perhaps make more sense to
- * connect to buddy-[un]idle signals and update from there
- */
-
- if (ops != NULL && ops->update != NULL)
- ops->update(purple_get_blist(), (PurpleBlistNode *)buddy);
+ g_free(mood);
}
-void
-purple_presence_set_idle(PurplePresence *presence, gboolean idle, time_t idle_time)
+GType
+purple_mood_get_type(void)
{
- gboolean old_idle;
- time_t current_time;
-
- g_return_if_fail(presence != NULL);
-
- if (presence->idle == idle && presence->idle_time == idle_time)
- return;
+ static GType type = 0;
- old_idle = presence->idle;
- presence->idle = idle;
- presence->idle_time = (idle ? idle_time : 0);
-
- current_time = time(NULL);
-
- if (purple_presence_get_context(presence) == PURPLE_PRESENCE_CONTEXT_BUDDY)
- {
- update_buddy_idle(purple_presence_get_buddy(presence), presence, current_time,
- old_idle, idle);
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleMood",
+ (GBoxedCopyFunc)purple_mood_copy,
+ (GBoxedFreeFunc)purple_mood_free);
}
- else if (purple_presence_get_context(presence) == PURPLE_PRESENCE_CONTEXT_ACCOUNT)
- {
- PurpleAccount *account;
- PurpleConnection *gc = NULL;
- PurplePlugin *prpl = NULL;
- PurplePluginProtocolInfo *prpl_info = NULL;
-
- account = purple_presence_get_account(presence);
-
- if (purple_prefs_get_bool("/purple/logging/log_system"))
- {
- PurpleLog *log = purple_account_get_log(account, FALSE);
-
- if (log != NULL)
- {
- char *msg, *tmp;
-
- if (idle)
- tmp = g_strdup_printf(_("+++ %s became idle"), purple_account_get_username(account));
- else
- tmp = g_strdup_printf(_("+++ %s became unidle"), purple_account_get_username(account));
-
- msg = g_markup_escape_text(tmp, -1);
- g_free(tmp);
- purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
- purple_account_get_username(account),
- (idle ? idle_time : current_time), msg);
- g_free(msg);
- }
- }
-
- gc = purple_account_get_connection(account);
-
- if(gc)
- prpl = purple_connection_get_prpl(gc);
- if(PURPLE_CONNECTION_IS_CONNECTED(gc) && prpl != NULL)
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-
- if (prpl_info && prpl_info->set_idle)
- prpl_info->set_idle(gc, (idle ? (current_time - idle_time) : 0));
- }
-}
-
-void
-purple_presence_set_login_time(PurplePresence *presence, time_t login_time)
-{
- g_return_if_fail(presence != NULL);
-
- if (presence->login_time == login_time)
- return;
-
- presence->login_time = login_time;
+ return type;
}
-PurplePresenceContext
-purple_presence_get_context(const PurplePresence *presence)
-{
- g_return_val_if_fail(presence != NULL, PURPLE_PRESENCE_CONTEXT_UNSET);
-
- return presence->context;
-}
-
-PurpleAccount *
-purple_presence_get_account(const PurplePresence *presence)
-{
- PurplePresenceContext context;
-
- g_return_val_if_fail(presence != NULL, NULL);
-
- context = purple_presence_get_context(presence);
- g_return_val_if_fail(context == PURPLE_PRESENCE_CONTEXT_ACCOUNT ||
- context == PURPLE_PRESENCE_CONTEXT_BUDDY, NULL);
-
- return presence->u.account;
-}
-
-PurpleConversation *
-purple_presence_get_conversation(const PurplePresence *presence)
-{
- g_return_val_if_fail(presence != NULL, NULL);
- g_return_val_if_fail(purple_presence_get_context(presence) ==
- PURPLE_PRESENCE_CONTEXT_CONV, NULL);
+/**************************************************************************
+* GObject code
+**************************************************************************/
- return presence->u.chat.conv;
+/* Set method for GObject properties */
+static void
+purple_status_set_property(GObject *obj, guint param_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleStatus *status = PURPLE_STATUS(obj);
+ PurpleStatusPrivate *priv = PURPLE_STATUS_GET_PRIVATE(status);
+
+ switch (param_id) {
+ case PROP_STATUS_TYPE:
+ priv->status_type = g_value_get_pointer(value);
+ break;
+ case PROP_PRESENCE:
+ priv->presence = g_value_get_object(value);
+ break;
+ case PROP_ACTIVE:
+ purple_status_set_active(status, g_value_get_boolean(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
}
-const char *
-purple_presence_get_chat_user(const PurplePresence *presence)
-{
- g_return_val_if_fail(presence != NULL, NULL);
- g_return_val_if_fail(purple_presence_get_context(presence) ==
- PURPLE_PRESENCE_CONTEXT_CONV, NULL);
-
- return presence->u.chat.user;
+/* Get method for GObject properties */
+static void
+purple_status_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleStatus *status = PURPLE_STATUS(obj);
+
+ switch (param_id) {
+ case PROP_STATUS_TYPE:
+ g_value_set_pointer(value, purple_status_get_status_type(status));
+ break;
+ case PROP_PRESENCE:
+ g_value_set_object(value, purple_status_get_presence(status));
+ break;
+ case PROP_ACTIVE:
+ g_value_set_boolean(value, purple_status_is_active(status));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
}
-PurpleBuddy *
-purple_presence_get_buddy(const PurplePresence *presence)
+/* GObject initialization function */
+static void
+purple_status_init(GTypeInstance *instance, gpointer klass)
{
- g_return_val_if_fail(presence != NULL, NULL);
- g_return_val_if_fail(purple_presence_get_context(presence) ==
- PURPLE_PRESENCE_CONTEXT_BUDDY, NULL);
-
- return presence->u.buddy.buddy;
-}
+ PurpleStatus *status = PURPLE_STATUS(instance);
-GList *
-purple_presence_get_statuses(const PurplePresence *presence)
-{
- g_return_val_if_fail(presence != NULL, NULL);
+ PURPLE_DBUS_REGISTER_POINTER(status, PurpleStatus);
- return presence->statuses;
+ PURPLE_STATUS_GET_PRIVATE(status)->attr_values =
+ g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
+ (GDestroyNotify)purple_value_free);
}
-PurpleStatus *
-purple_presence_get_status(const PurplePresence *presence, const char *status_id)
+/* Called when done constructing */
+static void
+purple_status_constructed(GObject *object)
{
- PurpleStatus *status;
- GList *l = NULL;
-
- g_return_val_if_fail(presence != NULL, NULL);
- g_return_val_if_fail(status_id != NULL, NULL);
-
- /* What's the purpose of this hash table? */
- status = (PurpleStatus *)g_hash_table_lookup(presence->status_table,
- status_id);
+ GList *l;
+ PurpleStatusPrivate *priv = PURPLE_STATUS_GET_PRIVATE(object);
- if (status == NULL) {
- for (l = purple_presence_get_statuses(presence);
- l != NULL && status == NULL; l = l->next)
- {
- PurpleStatus *temp_status = l->data;
+ parent_class->constructed(object);
- if (purple_strequal(status_id, purple_status_get_id(temp_status)))
- status = temp_status;
- }
+ for (l = purple_status_type_get_attrs(priv->status_type); l != NULL; l = l->next)
+ {
+ PurpleStatusAttribute *attr = (PurpleStatusAttribute *)l->data;
+ GValue *value = purple_status_attribute_get_value(attr);
+ GValue *new_value = purple_value_dup(value);
- if (status != NULL)
- g_hash_table_insert(presence->status_table,
- g_strdup(purple_status_get_id(status)), status);
+ g_hash_table_insert(priv->attr_values,
+ (char *)purple_status_attribute_get_id(attr),
+ new_value);
}
-
- return status;
}
-PurpleStatus *
-purple_presence_get_active_status(const PurplePresence *presence)
-{
- g_return_val_if_fail(presence != NULL, NULL);
-
- return presence->active_status;
-}
-
-gboolean
-purple_presence_is_available(const PurplePresence *presence)
-{
- PurpleStatus *status;
-
- g_return_val_if_fail(presence != NULL, FALSE);
-
- status = purple_presence_get_active_status(presence);
-
- return ((status != NULL && purple_status_is_available(status)) &&
- !purple_presence_is_idle(presence));
-}
-
-gboolean
-purple_presence_is_online(const PurplePresence *presence)
+/*
+ * GObject finalize function
+ * TODO: If the PurpleStatus is in a PurplePresence, then
+ * remove it from the PurplePresence?
+ */
+static void
+purple_status_finalize(GObject *object)
{
- PurpleStatus *status;
+ g_hash_table_destroy(PURPLE_STATUS_GET_PRIVATE(object)->attr_values);
- g_return_val_if_fail(presence != NULL, FALSE);
-
- if ((status = purple_presence_get_active_status(presence)) == NULL)
- return FALSE;
+ PURPLE_DBUS_UNREGISTER_POINTER(object);
- return purple_status_is_online(status);
+ parent_class->finalize(object);
}
-gboolean
-purple_presence_is_status_active(const PurplePresence *presence,
- const char *status_id)
+/* Class initializer function */
+static void
+purple_status_class_init(PurpleStatusClass *klass)
{
- PurpleStatus *status;
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
- g_return_val_if_fail(presence != NULL, FALSE);
- g_return_val_if_fail(status_id != NULL, FALSE);
+ parent_class = g_type_class_peek_parent(klass);
- status = purple_presence_get_status(presence, status_id);
+ obj_class->finalize = purple_status_finalize;
+ obj_class->constructed = purple_status_constructed;
- return (status != NULL && purple_status_is_active(status));
-}
+ /* Setup properties */
+ obj_class->get_property = purple_status_get_property;
+ obj_class->set_property = purple_status_set_property;
-gboolean
-purple_presence_is_status_primitive_active(const PurplePresence *presence,
- PurpleStatusPrimitive primitive)
-{
- GList *l;
+ g_type_class_add_private(klass, sizeof(PurpleStatusPrivate));
- g_return_val_if_fail(presence != NULL, FALSE);
- g_return_val_if_fail(primitive != PURPLE_STATUS_UNSET, FALSE);
+ properties[PROP_STATUS_TYPE] = g_param_spec_pointer("status-type",
+ "Status type",
+ "The PurpleStatusType of the status.",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
- for (l = purple_presence_get_statuses(presence);
- l != NULL; l = l->next)
- {
- PurpleStatus *temp_status = l->data;
- PurpleStatusType *type = purple_status_get_type(temp_status);
-
- if (purple_status_type_get_primitive(type) == primitive &&
- purple_status_is_active(temp_status))
- return TRUE;
- }
- return FALSE;
-}
+ properties[PROP_PRESENCE] = g_param_spec_object("presence", "Presence",
+ "The presence that the status belongs to.",
+ PURPLE_TYPE_PRESENCE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
-gboolean
-purple_presence_is_idle(const PurplePresence *presence)
-{
- g_return_val_if_fail(presence != NULL, FALSE);
-
- return purple_presence_is_online(presence) && presence->idle;
-}
-
-time_t
-purple_presence_get_idle_time(const PurplePresence *presence)
-{
- g_return_val_if_fail(presence != NULL, 0);
+ properties[PROP_ACTIVE] = g_param_spec_boolean("active", "Active",
+ "Whether the status is active or not.", FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- return presence->idle_time;
+ g_object_class_install_properties(obj_class, PROP_LAST, properties);
}
-time_t
-purple_presence_get_login_time(const PurplePresence *presence)
+GType
+purple_status_get_type(void)
{
- g_return_val_if_fail(presence != NULL, 0);
+ static GType type = 0;
- return purple_presence_is_online(presence) ? presence->login_time : 0;
-}
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleStatusClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_status_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleStatus),
+ 0,
+ (GInstanceInitFunc)purple_status_init,
+ NULL,
+ };
-static int
-purple_presence_compute_score(const PurplePresence *presence)
-{
- GList *l;
- int score = 0;
-
- for (l = purple_presence_get_statuses(presence); l != NULL; l = l->next) {
- PurpleStatus *status = (PurpleStatus *)l->data;
- PurpleStatusType *type = purple_status_get_type(status);
-
- if (purple_status_is_active(status)) {
- score += primitive_scores[purple_status_type_get_primitive(type)];
- if (!purple_status_is_online(status)) {
- PurpleBuddy *b = purple_presence_get_buddy(presence);
- if (b && purple_account_supports_offline_message(purple_buddy_get_account(b), b))
- score += primitive_scores[SCORE_OFFLINE_MESSAGE];
- }
- }
+ type = g_type_register_static(G_TYPE_OBJECT,
+ "PurpleStatus",
+ &info, 0);
}
- score += purple_account_get_int(purple_presence_get_account(presence), "score", 0);
- if (purple_presence_is_idle(presence))
- score += primitive_scores[SCORE_IDLE];
- return score;
+
+ return type;
}
-gint
-purple_presence_compare(const PurplePresence *presence1,
- const PurplePresence *presence2)
+PurpleStatus *
+purple_status_new(PurpleStatusType *status_type, PurplePresence *presence)
{
- time_t idle_time_1, idle_time_2;
- int score1 = 0, score2 = 0;
-
- if (presence1 == presence2)
- return 0;
- else if (presence1 == NULL)
- return 1;
- else if (presence2 == NULL)
- return -1;
-
- if (purple_presence_is_online(presence1) &&
- !purple_presence_is_online(presence2))
- return -1;
- else if (purple_presence_is_online(presence2) &&
- !purple_presence_is_online(presence1))
- return 1;
-
- /* Compute the score of the first set of statuses. */
- score1 = purple_presence_compute_score(presence1);
-
- /* Compute the score of the second set of statuses. */
- score2 = purple_presence_compute_score(presence2);
-
- idle_time_1 = time(NULL) - purple_presence_get_idle_time(presence1);
- idle_time_2 = time(NULL) - purple_presence_get_idle_time(presence2);
-
- if (idle_time_1 > idle_time_2)
- score1 += primitive_scores[SCORE_IDLE_TIME];
- else if (idle_time_1 < idle_time_2)
- score2 += primitive_scores[SCORE_IDLE_TIME];
-
- if (score1 < score2)
- return 1;
- else if (score1 > score2)
- return -1;
+ g_return_val_if_fail(status_type != NULL, NULL);
+ g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), NULL);
- return 0;
+ return g_object_new(PURPLE_TYPE_STATUS,
+ "status-type", status_type,
+ "presence", presence,
+ NULL);
}
@@ -1653,16 +1298,16 @@ score_pref_changed_cb(const char *name, PurplePrefType type,
}
void *
-purple_status_get_handle(void) {
+purple_statuses_get_handle(void) {
static int handle;
return &handle;
}
void
-purple_status_init(void)
+purple_statuses_init(void)
{
- void *handle = purple_status_get_handle();
+ void *handle = purple_statuses_get_handle();
purple_prefs_add_none("/purple/status");
purple_prefs_add_none("/purple/status/scores");
@@ -1679,6 +1324,8 @@ purple_status_init(void)
primitive_scores[PURPLE_STATUS_EXTENDED_AWAY]);
purple_prefs_add_int("/purple/status/scores/idle",
primitive_scores[SCORE_IDLE]);
+ purple_prefs_add_int("/purple/status/scores/idle_time",
+ primitive_scores[SCORE_IDLE_TIME]);
purple_prefs_add_int("/purple/status/scores/offline_msg",
primitive_scores[SCORE_OFFLINE_MESSAGE]);
@@ -1700,6 +1347,9 @@ purple_status_init(void)
purple_prefs_connect_callback(handle, "/purple/status/scores/idle",
score_pref_changed_cb,
GINT_TO_POINTER(SCORE_IDLE));
+ purple_prefs_connect_callback(handle, "/purple/status/scores/idle_time",
+ score_pref_changed_cb,
+ GINT_TO_POINTER(SCORE_IDLE_TIME));
purple_prefs_connect_callback(handle, "/purple/status/scores/offline_msg",
score_pref_changed_cb,
GINT_TO_POINTER(SCORE_OFFLINE_MESSAGE));
@@ -1710,11 +1360,12 @@ purple_status_init(void)
purple_prefs_trigger_callback("/purple/status/scores/away");
purple_prefs_trigger_callback("/purple/status/scores/extended_away");
purple_prefs_trigger_callback("/purple/status/scores/idle");
+ purple_prefs_trigger_callback("/purple/status/scores/idle_time");
purple_prefs_trigger_callback("/purple/status/scores/offline_msg");
}
void
-purple_status_uninit(void)
+purple_statuses_uninit(void)
{
purple_prefs_disconnect_by_handle(purple_prefs_get_handle());
}
diff --git a/libpurple/status.h b/libpurple/status.h
index f47ce900bb..638658c626 100644
--- a/libpurple/status.h
+++ b/libpurple/status.h
@@ -19,73 +19,47 @@
* 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_STATUS_H_
#define _PURPLE_STATUS_H_
-
/**
- * @file status.h Status API
- * @ingroup core
- *
- * A brief explanation of the status API:
- *
- * PurpleStatusType's are created by each PRPL. They outline the
- * available statuses of the protocol. AIM, for example, supports
- * an available state with an optional available message, an away
- * state with a mandatory message, and an invisible state (which is
- * technically "independent" of the other two, but we'll get into
- * that later). PurpleStatusTypes are very permanent. They are
- * hardcoded in each PRPL and will not change often. And because
- * they are hardcoded, they do not need to be saved to any XML file.
- *
- * A PurpleStatus can be thought of as an "instance" of a PurpleStatusType.
- * If you're familiar with object-oriented programming languages
- * then this should be immediately clear. Say, for example, that
- * one of your AIM buddies has set himself as "away." You have a
- * PurpleBuddy node for this person in your buddy list. Purple wants
- * to mark this buddy as "away," so it creates a new PurpleStatus.
- * The PurpleStatus has its PurpleStatusType set to the "away" state
- * for the oscar PRPL. The PurpleStatus also contains the buddy's
- * away message. PurpleStatuses are sometimes saved, depending on
- * the context. The current PurpleStatuses associated with each of
- * your accounts are saved so that the next time you start Purple,
- * your accounts will be set to their last known statuses. There
- * is also a list of saved statuses that are written to the
- * status.xml file. Also, each PurpleStatus has a "saveable" boolean.
- * If "saveable" is set to FALSE then the status is NEVER saved.
- * All PurpleStatuses should be inside a PurplePresence.
- *
- *
- * A PurpleStatus is either "independent" or "exclusive."
- * Independent statuses can be active or inactive and they don't
- * affect anything else. However, you can only have one exclusive
- * status per PurplePresence. If you activate one exclusive status,
- * then the previous exclusive status is automatically deactivated.
- *
- * A PurplePresence is like a collection of PurpleStatuses (plus some
- * other random info). For any buddy, or for any one of your accounts,
- * or for any person with which you're chatting, you may know various
- * amounts of information. This information is all contained in
- * one PurplePresence. If one of your buddies is away and idle,
- * then the presence contains the PurpleStatus for their awayness,
- * and it contains their current idle time. PurplePresences are
- * never saved to disk. The information they contain is only relevant
- * for the current PurpleSession.
+ * SECTION:status
+ * @section_id: libpurple-status
+ * @short_description: <filename>status.h</filename>
+ * @title: Status Object API
*/
+#define PURPLE_TYPE_STATUS (purple_status_get_type())
+#define PURPLE_STATUS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_STATUS, PurpleStatus))
+#define PURPLE_STATUS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_STATUS, PurpleStatusClass))
+#define PURPLE_IS_STATUS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_STATUS))
+#define PURPLE_IS_STATUS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_STATUS))
+#define PURPLE_STATUS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_STATUS, PurpleStatusClass))
+
+typedef struct _PurpleStatus PurpleStatus;
+typedef struct _PurpleStatusClass PurpleStatusClass;
+
+#define PURPLE_TYPE_STATUS_TYPE (purple_status_type_get_type())
+
/**
- * PurpleStatusType's are created by each PRPL. They outline the
+ * PurpleStatusType:
+ *
+ * PurpleStatusType's are created by each protocol. They outline the
* available statuses of the protocol. AIM, for example, supports
* an available state with an optional available message, an away
* state with a mandatory message, and an invisible state (which is
* technically "independent" of the other two, but we'll get into
* that later). PurpleStatusTypes are very permanent. They are
- * hardcoded in each PRPL and will not change often. And because
+ * hardcoded in each protocol and will not change often. And because
* they are hardcoded, they do not need to be saved to any XML file.
*/
-typedef struct _PurpleStatusType PurpleStatusType;
-typedef struct _PurpleStatusAttr PurpleStatusAttr;
-typedef struct _PurplePresence PurplePresence;
-typedef struct _PurpleStatus PurpleStatus;
+typedef struct _PurpleStatusType PurpleStatusType;
+
+#define PURPLE_TYPE_STATUS_ATTRIBUTE (purple_status_attribute_get_type())
+
+typedef struct _PurpleStatusAttribute PurpleStatusAttribute;
+
+#define PURPLE_TYPE_MOOD (purple_mood_get_type())
typedef struct _PurpleMood {
const char *mood;
@@ -94,20 +68,8 @@ typedef struct _PurpleMood {
} PurpleMood;
/**
- * A context for a presence.
+ * PurpleStatusPrimitive:
*
- * The context indicates to what the presence applies.
- */
-typedef enum
-{
- PURPLE_PRESENCE_CONTEXT_UNSET = 0,
- PURPLE_PRESENCE_CONTEXT_ACCOUNT,
- PURPLE_PRESENCE_CONTEXT_CONV,
- PURPLE_PRESENCE_CONTEXT_BUDDY
-
-} PurplePresenceContext;
-
-/**
* A primitive defining the basic structure of a status type.
*/
/*
@@ -129,10 +91,7 @@ typedef enum
PURPLE_STATUS_NUM_PRIMITIVES
} PurpleStatusPrimitive;
-#include "account.h"
-#include "blist.h"
-#include "conversation.h"
-#include "value.h"
+#include "presence.h"
#define PURPLE_TUNE_ARTIST "tune_artist"
#define PURPLE_TUNE_TITLE "tune_title"
@@ -148,68 +107,124 @@ typedef enum
#define PURPLE_MOOD_NAME "mood"
#define PURPLE_MOOD_COMMENT "moodtext"
-#ifdef __cplusplus
-extern "C" {
-#endif
+/**
+ * PurpleStatus:
+ *
+ * A PurpleStatus can be thought of as an "instance" of a PurpleStatusType.
+ * If you're familiar with object-oriented programming languages
+ * then this should be immediately clear. Say, for example, that
+ * one of your AIM buddies has set himself as "away." You have a
+ * PurpleBuddy node for this person in your buddy list. Purple wants
+ * to mark this buddy as "away," so it creates a new PurpleStatus.
+ * The PurpleStatus has its PurpleStatusType set to the "away" state
+ * for the oscar protocol. The PurpleStatus also contains the buddy's
+ * away message. PurpleStatuses are sometimes saved, depending on
+ * the context. The current PurpleStatuses associated with each of
+ * your accounts are saved so that the next time you start Purple,
+ * your accounts will be set to their last known statuses. There
+ * is also a list of saved statuses that are written to the
+ * status.xml file. Also, each PurpleStatus has a "saveable" boolean.
+ * If "saveable" is set to FALSE then the status is NEVER saved.
+ * All PurpleStatuses should be inside a PurplePresence.
+ *
+ * A PurpleStatus is either "independent" or "exclusive."
+ * Independent statuses can be active or inactive and they don't
+ * affect anything else. However, you can only have one exclusive
+ * status per PurplePresence. If you activate one exclusive status,
+ * then the previous exclusive status is automatically deactivated.
+ *
+ * A PurplePresence is like a collection of PurpleStatuses (plus some
+ * other random info).
+ *
+ * See <link linkend="libpurple-presence">Presence API</link>
+ */
+struct _PurpleStatus
+{
+ GObject gparent;
+};
+
+/**
+ * PurpleStatusClass:
+ *
+ * Base class for all #PurpleStatus's
+ */
+struct _PurpleStatusClass {
+ GObjectClass parent_class;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name PurpleStatusPrimitive API */
+/* PurpleStatusPrimitive API */
/**************************************************************************/
-/*@{*/
/**
+ * purple_primitive_get_id_from_type:
+ * @type: A primitive status type.
+ *
* Lookup the id of a primitive status type based on the type. This
* ID is a unique plain-text name of the status, without spaces.
*
- * @param type A primitive status type.
- *
- * @return The unique ID for this type.
+ * Returns: The unique ID for this type.
*/
const char *purple_primitive_get_id_from_type(PurpleStatusPrimitive type);
/**
+ * purple_primitive_get_name_from_type:
+ * @type: A primitive status type.
+ *
* Lookup the name of a primitive status type based on the type. This
* name is the plain-English name of the status type. It is usually one
* or two words.
*
- * @param type A primitive status type.
- *
- * @return The name of this type, suitable for users to see.
+ * Returns: The name of this type, suitable for users to see.
*/
const char *purple_primitive_get_name_from_type(PurpleStatusPrimitive type);
/**
+ * purple_primitive_get_type_from_id:
+ * @id: The unique ID of a primitive status type.
+ *
* Lookup the value of a primitive status type based on the id. The
* ID is a unique plain-text name of the status, without spaces.
*
- * @param id The unique ID of a primitive status type.
- *
- * @return The PurpleStatusPrimitive value.
+ * Returns: The PurpleStatusPrimitive value.
*/
PurpleStatusPrimitive purple_primitive_get_type_from_id(const char *id);
-/*@}*/
-
/**************************************************************************/
-/** @name PurpleStatusType API */
+/* PurpleStatusType API */
/**************************************************************************/
-/*@{*/
/**
- * Creates a new status type.
+ * purple_status_type_get_type:
*
- * @param primitive The primitive status type.
- * @param id The ID of the status type, or @c NULL to use the id of
+ * Returns: The #GType for the #PurpleStatusType boxed structure.
+ */
+GType purple_status_type_get_type(void);
+
+/**
+ * purple_status_type_new_full:
+ * @primitive: The primitive status type.
+ * @id: The ID of the status type, or %NULL to use the id of
* the primitive status type.
- * @param name The name presented to the user, or @c NULL to use the
+ * @name: The name presented to the user, or %NULL to use the
* name of the primitive status type.
- * @param saveable TRUE if the information set for this status by the
+ * @saveable: TRUE if the information set for this status by the
* user can be saved for future sessions.
- * @param user_settable TRUE if this is a status the user can manually set.
- * @param independent TRUE if this is an independent (non-exclusive)
+ * @user_settable: TRUE if this is a status the user can manually set.
+ * @independent: TRUE if this is an independent (non-exclusive)
* status type.
*
- * @return A new status type.
+ * Creates a new status type.
+ *
+ * Returns: A new status type.
*/
PurpleStatusType *purple_status_type_new_full(PurpleStatusPrimitive primitive,
const char *id, const char *name,
@@ -218,41 +233,43 @@ PurpleStatusType *purple_status_type_new_full(PurpleStatusPrimitive primitive,
gboolean independent);
/**
- * Creates a new status type with some default values (
- * saveable and not independent).
- *
- * @param primitive The primitive status type.
- * @param id The ID of the status type, or @c NULL to use the id of
+ * purple_status_type_new:
+ * @primitive: The primitive status type.
+ * @id: The ID of the status type, or %NULL to use the id of
* the primitive status type.
- * @param name The name presented to the user, or @c NULL to use the
+ * @name: The name presented to the user, or %NULL to use the
* name of the primitive status type.
- * @param user_settable TRUE if this is a status the user can manually set.
+ * @user_settable: TRUE if this is a status the user can manually set.
+ *
+ * Creates a new status type with some default values (
+ * saveable and not independent).
*
- * @return A new status type.
+ * Returns: A new status type.
*/
PurpleStatusType *purple_status_type_new(PurpleStatusPrimitive primitive,
const char *id, const char *name,
gboolean user_settable);
/**
- * Creates a new status type with attributes.
- *
- * @param primitive The primitive status type.
- * @param id The ID of the status type, or @c NULL to use the id of
+ * purple_status_type_new_with_attrs:
+ * @primitive: The primitive status type.
+ * @id: The ID of the status type, or %NULL to use the id of
* the primitive status type.
- * @param name The name presented to the user, or @c NULL to use the
+ * @name: The name presented to the user, or %NULL to use the
* name of the primitive status type.
- * @param saveable TRUE if the information set for this status by the
+ * @saveable: TRUE if the information set for this status by the
* user can be saved for future sessions.
- * @param user_settable TRUE if this is a status the user can manually set.
- * @param independent TRUE if this is an independent (non-exclusive)
+ * @user_settable: TRUE if this is a status the user can manually set.
+ * @independent: TRUE if this is an independent (non-exclusive)
* status type.
- * @param attr_id The ID of the first attribute.
- * @param attr_name The name of the first attribute.
- * @param attr_value The value type of the first attribute attribute.
- * @param ... Additional attribute information.
+ * @attr_id: The ID of the first attribute.
+ * @attr_name: The name of the first attribute.
+ * @attr_value: The value type of the first attribute.
+ * @...: Additional attribute information.
+ *
+ * Creates a new status type with attributes.
*
- * @return A new status type.
+ * Returns: A new status type.
*/
PurpleStatusType *purple_status_type_new_with_attrs(PurpleStatusPrimitive primitive,
const char *id,
@@ -262,842 +279,474 @@ PurpleStatusType *purple_status_type_new_with_attrs(PurpleStatusPrimitive primit
gboolean independent,
const char *attr_id,
const char *attr_name,
- PurpleValue *attr_value, ...) G_GNUC_NULL_TERMINATED;
+ GValue *attr_value, ...) G_GNUC_NULL_TERMINATED;
/**
- * Destroys a status type.
+ * purple_status_type_destroy:
+ * @status_type: The status type to destroy.
*
- * @param status_type The status type to destroy.
+ * Destroys a status type.
*/
void purple_status_type_destroy(PurpleStatusType *status_type);
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
-/**
- * Sets a status type's primary attribute.
- *
- * The value for the primary attribute is used as the description for
- * the particular status type. An example is an away message. The message
- * would be the primary attribute.
- *
- * @param status_type The status type.
- * @param attr_id The ID of the primary attribute.
- *
- * @deprecated This function isn't used and should be removed in 3.0.0.
- */
-void purple_status_type_set_primary_attr(PurpleStatusType *status_type,
- const char *attr_id);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
-/**
- * Adds an attribute to a status type.
- *
- * @param status_type The status type to add the attribute to.
- * @param id The ID of the attribute.
- * @param name The name presented to the user.
- * @param value The value type of this attribute.
- *
- * @deprecated This function isn't needed and should be removed in 3.0.0.
- * Status type attributes should be set when the status type
- * is created, in the call to purple_status_type_new_with_attrs.
- */
-void purple_status_type_add_attr(PurpleStatusType *status_type, const char *id,
- const char *name, PurpleValue *value);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
-/**
- * Adds multiple attributes to a status type.
- *
- * @param status_type The status type to add the attribute to.
- * @param id The ID of the first attribute.
- * @param name The description of the first attribute.
- * @param value The value type of the first attribute attribute.
- * @param ... Additional attribute information.
- *
- * @deprecated This function isn't needed and should be removed in 3.0.0.
- * Status type attributes should be set when the status type
- * is created, in the call to purple_status_type_new_with_attrs.
- */
-void purple_status_type_add_attrs(PurpleStatusType *status_type, const char *id,
- const char *name, PurpleValue *value, ...) G_GNUC_NULL_TERMINATED;
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
/**
- * Adds multiple attributes to a status type using a va_list.
+ * purple_status_type_get_primitive:
+ * @status_type: The status type.
*
- * @param status_type The status type to add the attribute to.
- * @param args The va_list of attributes.
- *
- * @deprecated This function isn't needed and should be removed in 3.0.0.
- * Status type attributes should be set when the status type
- * is created, in the call to purple_status_type_new_with_attrs.
- */
-void purple_status_type_add_attrs_vargs(PurpleStatusType *status_type,
- va_list args);
-#endif
-
-/**
* Returns the primitive type of a status type.
*
- * @param status_type The status type.
- *
- * @return The primitive type of the status type.
+ * Returns: The primitive type of the status type.
*/
PurpleStatusPrimitive purple_status_type_get_primitive(
const PurpleStatusType *status_type);
/**
- * Returns the ID of a status type.
+ * purple_status_type_get_id:
+ * @status_type: The status type.
*
- * @param status_type The status type.
+ * Returns the ID of a status type.
*
- * @return The ID of the status type.
+ * Returns: The ID of the status type.
*/
const char *purple_status_type_get_id(const PurpleStatusType *status_type);
/**
- * Returns the name of a status type.
+ * purple_status_type_get_name:
+ * @status_type: The status type.
*
- * @param status_type The status type.
+ * Returns the name of a status type.
*
- * @return The name of the status type.
+ * Returns: The name of the status type.
*/
const char *purple_status_type_get_name(const PurpleStatusType *status_type);
/**
- * Returns whether or not the status type is saveable.
+ * purple_status_type_is_saveable:
+ * @status_type: The status type.
*
- * @param status_type The status type.
+ * Returns whether or not the status type is saveable.
*
- * @return TRUE if user-defined statuses based off this type are saveable.
+ * Returns: TRUE if user-defined statuses based off this type are saveable.
* FALSE otherwise.
*/
gboolean purple_status_type_is_saveable(const PurpleStatusType *status_type);
/**
+ * purple_status_type_is_user_settable:
+ * @status_type: The status type.
+ *
* Returns whether or not the status type can be set or modified by the
* user.
*
- * @param status_type The status type.
- *
- * @return TRUE if the status type can be set or modified by the user.
+ * Returns: TRUE if the status type can be set or modified by the user.
* FALSE if it's a protocol-set setting.
*/
gboolean purple_status_type_is_user_settable(const PurpleStatusType *status_type);
/**
+ * purple_status_type_is_independent:
+ * @status_type: The status type.
+ *
* Returns whether or not the status type is independent.
*
* Independent status types are non-exclusive. If other status types on
* the same hierarchy level are set, this one will not be affected.
*
- * @param status_type The status type.
- *
- * @return TRUE if the status type is independent, or FALSE otherwise.
+ * Returns: TRUE if the status type is independent, or FALSE otherwise.
*/
gboolean purple_status_type_is_independent(const PurpleStatusType *status_type);
/**
- * Returns whether the status type is exclusive.
+ * purple_status_type_is_exclusive:
+ * @status_type: The status type.
*
- * @param status_type The status type.
+ * Returns whether the status type is exclusive.
*
- * @return TRUE if the status type is exclusive, FALSE otherwise.
+ * Returns: TRUE if the status type is exclusive, FALSE otherwise.
*/
gboolean purple_status_type_is_exclusive(const PurpleStatusType *status_type);
/**
+ * purple_status_type_is_available:
+ * @status_type: The status type.
+ *
* Returns whether or not a status type is available.
*
* Available status types are online and possibly invisible, but not away.
*
- * @param status_type The status type.
- *
- * @return TRUE if the status is available, or FALSE otherwise.
+ * Returns: TRUE if the status is available, or FALSE otherwise.
*/
gboolean purple_status_type_is_available(const PurpleStatusType *status_type);
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
/**
- * Returns a status type's primary attribute ID.
- *
- * @param type The status type.
- *
- * @return The primary attribute's ID.
+ * purple_status_type_get_attr:
+ * @status_type: The status type containing the attribute.
+ * @id: The ID of the desired attribute.
*
- * @deprecated This function isn't used and should be removed in 3.0.0.
- */
-const char *purple_status_type_get_primary_attr(const PurpleStatusType *type);
-#endif
-
-/**
* Returns the attribute with the specified ID.
*
- * @param status_type The status type containing the attribute.
- * @param id The ID of the desired attribute.
- *
- * @return The attribute, if found. NULL otherwise.
+ * Returns: The attribute, if found. NULL otherwise.
*/
-PurpleStatusAttr *purple_status_type_get_attr(const PurpleStatusType *status_type,
+PurpleStatusAttribute *purple_status_type_get_attr(const PurpleStatusType *status_type,
const char *id);
/**
- * Returns a list of all attributes in a status type.
+ * purple_status_type_get_attrs:
+ * @status_type: The status type.
*
- * @param status_type The status type.
+ * Returns a list of all attributes in a status type.
*
- * @constreturn The list of attributes.
+ * Returns: (transfer none): The list of attributes.
*/
GList *purple_status_type_get_attrs(const PurpleStatusType *status_type);
/**
- * Find the PurpleStatusType with the given id.
+ * purple_status_type_find_with_id:
+ * @status_types: A list of status types. Often account->status_types.
+ * @id: The unique ID of the status type you wish to find.
*
- * @param status_types A list of status types. Often account->status_types.
- * @param id The unique ID of the status type you wish to find.
+ * Find the PurpleStatusType with the given id.
*
- * @return The status type with the given ID, or NULL if one could
+ * Returns: The status type with the given ID, or NULL if one could
* not be found.
*/
const PurpleStatusType *purple_status_type_find_with_id(GList *status_types,
const char *id);
-/*@}*/
-
/**************************************************************************/
-/** @name PurpleStatusAttr API */
+/* PurpleStatusAttribute API */
/**************************************************************************/
-/*@{*/
/**
- * Creates a new status attribute.
+ * purple_status_attribute_get_type:
+ *
+ * Returns: The #GType for the #PurpleStatusAttribute boxed structure.
+ */
+GType purple_status_attribute_get_type(void);
+
+/**
+ * purple_status_attribute_new:
+ * @id: The ID of the attribute.
+ * @name: The name presented to the user.
+ * @value_type: The type of data contained in the attribute.
*
- * @param id The ID of the attribute.
- * @param name The name presented to the user.
- * @param value_type The type of data contained in the attribute.
+ * Creates a new status attribute.
*
- * @return A new status attribute.
+ * Returns: A new status attribute.
*/
-PurpleStatusAttr *purple_status_attr_new(const char *id, const char *name,
- PurpleValue *value_type);
+PurpleStatusAttribute *purple_status_attribute_new(const char *id, const char *name,
+ GValue *value_type);
/**
- * Destroys a status attribute.
+ * purple_status_attribute_destroy:
+ * @attr: The status attribute to destroy.
*
- * @param attr The status attribute to destroy.
+ * Destroys a status attribute.
*/
-void purple_status_attr_destroy(PurpleStatusAttr *attr);
+void purple_status_attribute_destroy(PurpleStatusAttribute *attr);
/**
- * Returns the ID of a status attribute.
+ * purple_status_attribute_get_id:
+ * @attr: The status attribute.
*
- * @param attr The status attribute.
+ * Returns the ID of a status attribute.
*
- * @return The status attribute's ID.
+ * Returns: The status attribute's ID.
*/
-const char *purple_status_attr_get_id(const PurpleStatusAttr *attr);
+const char *purple_status_attribute_get_id(const PurpleStatusAttribute *attr);
/**
- * Returns the name of a status attribute.
+ * purple_status_attribute_get_name:
+ * @attr: The status attribute.
*
- * @param attr The status attribute.
+ * Returns the name of a status attribute.
*
- * @return The status attribute's name.
+ * Returns: The status attribute's name.
*/
-const char *purple_status_attr_get_name(const PurpleStatusAttr *attr);
+const char *purple_status_attribute_get_name(const PurpleStatusAttribute *attr);
/**
- * Returns the value of a status attribute.
+ * purple_status_attribute_get_value:
+ * @attr: The status attribute.
*
- * @param attr The status attribute.
+ * Returns the value of a status attribute.
*
- * @return The status attribute's value.
+ * Returns: The status attribute's value.
*/
-PurpleValue *purple_status_attr_get_value(const PurpleStatusAttr *attr);
-
-/*@}*/
+GValue *purple_status_attribute_get_value(const PurpleStatusAttribute *attr);
/**************************************************************************/
-/** @name PurpleStatus API */
+/* PurpleMood API */
/**************************************************************************/
-/*@{*/
/**
- * Creates a new status.
- *
- * @param status_type The type of status.
- * @param presence The parent presence.
+ * purple_mood_get_type:
*
- * @return The new status.
+ * Returns: The #GType for the #PurpleMood boxed structure.
*/
-PurpleStatus *purple_status_new(PurpleStatusType *status_type,
- PurplePresence *presence);
+GType purple_mood_get_type(void);
+
+/**************************************************************************/
+/* PurpleStatus API */
+/**************************************************************************/
/**
- * Destroys a status.
+ * purple_status_get_type:
*
- * @param status The status to destroy.
+ * Returns: The #GType for the Status object.
*/
-void purple_status_destroy(PurpleStatus *status);
+GType purple_status_get_type(void);
/**
- * Sets whether or not a status is active.
+ * purple_status_new:
+ * @status_type: The type of status.
+ * @presence: The parent presence.
*
- * This should only be called by the account, conversation, and buddy APIs.
+ * Creates a new status.
*
- * @param status The status.
- * @param active The active state.
+ * Returns: The new status.
*/
-void purple_status_set_active(PurpleStatus *status, gboolean active);
+PurpleStatus *purple_status_new(PurpleStatusType *status_type,
+ PurplePresence *presence);
/**
+ * purple_status_set_active:
+ * @status: The status.
+ * @active: The active state.
+ *
* Sets whether or not a status is active.
*
* This should only be called by the account, conversation, and buddy APIs.
- *
- * @param status The status.
- * @param active The active state.
- * @param args A list of attributes to set on the status. This list is
+ */
+void purple_status_set_active(PurpleStatus *status, gboolean active);
+
+/**
+ * purple_status_set_active_with_attrs:
+ * @status: The status.
+ * @active: The active state.
+ * @args: A list of attributes to set on the status. This list is
* composed of key/value pairs, where each key is a valid
* attribute name for this PurpleStatusType. The list should
* be NULL terminated.
+ *
+ * Sets whether or not a status is active.
+ *
+ * This should only be called by the account, conversation, and buddy APIs.
*/
void purple_status_set_active_with_attrs(PurpleStatus *status, gboolean active,
va_list args);
/**
- * Sets whether or not a status is active.
- *
- * This should only be called by the account, conversation, and buddy APIs.
- *
- * @param status The status.
- * @param active The active state.
- * @param attrs A list of attributes to set on the status. This list is
+ * purple_status_set_active_with_attrs_list:
+ * @status: The status.
+ * @active: The active state.
+ * @attrs: A list of attributes to set on the status. This list is
* composed of key/value pairs, where each key is a valid
* attribute name for this PurpleStatusType. The list is
* not modified or freed by this function.
- */
-void purple_status_set_active_with_attrs_list(PurpleStatus *status, gboolean active,
- GList *attrs);
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
-/**
- * Sets the boolean value of an attribute in a status with the specified ID.
- *
- * @param status The status.
- * @param id The attribute ID.
- * @param value The boolean value.
- *
- * @deprecated This function is only used by status.c and should be made
- * static in 3.0.0.
- */
-void purple_status_set_attr_boolean(PurpleStatus *status, const char *id,
- gboolean value);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
-/**
- * Sets the integer value of an attribute in a status with the specified ID.
*
- * @param status The status.
- * @param id The attribute ID.
- * @param value The integer value.
+ * Sets whether or not a status is active.
*
- * @deprecated This function is only used by status.c and should be made
- * static in 3.0.0.
+ * This should only be called by the account, conversation, and buddy APIs.
*/
-void purple_status_set_attr_int(PurpleStatus *status, const char *id,
- int value);
-#endif
+void purple_status_set_active_with_attrs_list(PurpleStatus *status, gboolean active,
+ GList *attrs);
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
/**
- * Sets the string value of an attribute in a status with the specified ID.
- *
- * @param status The status.
- * @param id The attribute ID.
- * @param value The string value.
+ * purple_status_get_status_type:
+ * @status: The status.
*
- * @deprecated This function is only used by status.c and should be made
- * static in 3.0.0.
- */
-void purple_status_set_attr_string(PurpleStatus *status, const char *id,
- const char *value);
-#endif
-
-/**
* Returns the status's type.
*
- * @param status The status.
- *
- * @return The status's type.
+ * Returns: The status's type.
*/
-PurpleStatusType *purple_status_get_type(const PurpleStatus *status);
+PurpleStatusType *purple_status_get_status_type(const PurpleStatus *status);
/**
- * Returns the status's presence.
+ * purple_status_get_presence:
+ * @status: The status.
*
- * @param status The status.
+ * Returns the status's presence.
*
- * @return The status's presence.
+ * Returns: The status's presence.
*/
PurplePresence *purple_status_get_presence(const PurpleStatus *status);
/**
+ * purple_status_get_id:
+ * @status: The status.
+ *
* Returns the status's type ID.
*
* This is a convenience method for
- * purple_status_type_get_id(purple_status_get_type(status)).
- *
- * @param status The status.
+ * purple_status_type_get_id(purple_status_get_status_type(status)).
*
- * @return The status's ID.
+ * Returns: The status's ID.
*/
const char *purple_status_get_id(const PurpleStatus *status);
/**
+ * purple_status_get_name:
+ * @status: The status.
+ *
* Returns the status's name.
*
* This is a convenience method for
- * purple_status_type_get_name(purple_status_get_type(status)).
+ * purple_status_type_get_name(purple_status_get_status_type(status)).
*
- * @param status The status.
- *
- * @return The status's name.
+ * Returns: The status's name.
*/
const char *purple_status_get_name(const PurpleStatus *status);
/**
+ * purple_status_is_independent:
+ * @status: The status.
+ *
* Returns whether or not a status is independent.
*
* This is a convenience method for
- * purple_status_type_is_independent(purple_status_get_type(status)).
- *
- * @param status The status.
+ * purple_status_type_is_independent(purple_status_get_status_type(status)).
*
- * @return TRUE if the status is independent, or FALSE otherwise.
+ * Returns: TRUE if the status is independent, or FALSE otherwise.
*/
gboolean purple_status_is_independent(const PurpleStatus *status);
/**
+ * purple_status_is_exclusive:
+ * @status: The status.
+ *
* Returns whether or not a status is exclusive.
*
* This is a convenience method for
- * purple_status_type_is_exclusive(purple_status_get_type(status)).
- *
- * @param status The status.
+ * purple_status_type_is_exclusive(purple_status_get_status_type(status)).
*
- * @return TRUE if the status is exclusive, FALSE otherwise.
+ * Returns: TRUE if the status is exclusive, FALSE otherwise.
*/
gboolean purple_status_is_exclusive(const PurpleStatus *status);
/**
+ * purple_status_is_available:
+ * @status: The status.
+ *
* Returns whether or not a status is available.
*
* Available statuses are online and possibly invisible, but not away or idle.
*
* This is a convenience method for
- * purple_status_type_is_available(purple_status_get_type(status)).
+ * purple_status_type_is_available(purple_status_get_status_type(status)).
*
- * @param status The status.
- *
- * @return TRUE if the status is available, or FALSE otherwise.
+ * Returns: TRUE if the status is available, or FALSE otherwise.
*/
gboolean purple_status_is_available(const PurpleStatus *status);
/**
- * Returns the active state of a status.
+ * purple_status_is_active:
+ * @status: The status.
*
- * @param status The status.
+ * Returns the active state of a status.
*
- * @return The active state of the status.
+ * Returns: The active state of the status.
*/
gboolean purple_status_is_active(const PurpleStatus *status);
/**
- * Returns whether or not a status is considered 'online'
+ * purple_status_is_online:
+ * @status: The status.
*
- * @param status The status.
+ * Returns whether or not a status is considered 'online'
*
- * @return TRUE if the status is considered online, FALSE otherwise
+ * Returns: TRUE if the status is considered online, FALSE otherwise
*/
gboolean purple_status_is_online(const PurpleStatus *status);
/**
- * Returns the value of an attribute in a status with the specified ID.
+ * purple_status_get_attr_value:
+ * @status: The status.
+ * @id: The attribute ID.
*
- * @param status The status.
- * @param id The attribute ID.
+ * Returns the value of an attribute in a status with the specified ID.
*
- * @return The value of the attribute.
+ * Returns: The value of the attribute.
*/
-PurpleValue *purple_status_get_attr_value(const PurpleStatus *status,
+GValue *purple_status_get_attr_value(const PurpleStatus *status,
const char *id);
/**
- * Returns the boolean value of an attribute in a status with the specified ID.
+ * purple_status_get_attr_boolean:
+ * @status: The status.
+ * @id: The attribute ID.
*
- * @param status The status.
- * @param id The attribute ID.
+ * Returns the boolean value of an attribute in a status with the specified ID.
*
- * @return The boolean value of the attribute.
+ * Returns: The boolean value of the attribute.
*/
gboolean purple_status_get_attr_boolean(const PurpleStatus *status,
const char *id);
/**
- * Returns the integer value of an attribute in a status with the specified ID.
+ * purple_status_get_attr_int:
+ * @status: The status.
+ * @id: The attribute ID.
*
- * @param status The status.
- * @param id The attribute ID.
+ * Returns the integer value of an attribute in a status with the specified ID.
*
- * @return The integer value of the attribute.
+ * Returns: The integer value of the attribute.
*/
int purple_status_get_attr_int(const PurpleStatus *status, const char *id);
/**
- * Returns the string value of an attribute in a status with the specified ID.
+ * purple_status_get_attr_string:
+ * @status: The status.
+ * @id: The attribute ID.
*
- * @param status The status.
- * @param id The attribute ID.
+ * Returns the string value of an attribute in a status with the specified ID.
*
- * @return The string value of the attribute.
+ * Returns: The string value of the attribute.
*/
const char *purple_status_get_attr_string(const PurpleStatus *status,
const char *id);
/**
- * Compares two statuses for availability.
+ * purple_status_compare:
+ * @status1: The first status.
+ * @status2: The second status.
*
- * @param status1 The first status.
- * @param status2 The second status.
+ * Compares two statuses for availability.
*
- * @return -1 if @a status1 is more available than @a status2.
- * 0 if @a status1 is equal to @a status2.
- * 1 if @a status2 is more available than @a status1.
+ * Returns: -1 if @status1 is more available than @status2.
+ * 0 if @status1 is equal to @status2.
+ * 1 if @status2 is more available than @status1.
*/
gint purple_status_compare(const PurpleStatus *status1, const PurpleStatus *status2);
-/*@}*/
-
/**************************************************************************/
-/** @name PurplePresence API */
+/* Statuses subsystem */
/**************************************************************************/
-/*@{*/
-
-/**
- * Creates a new presence.
- *
- * @param context The presence context.
- *
- * @return A new presence.
- */
-PurplePresence *purple_presence_new(PurplePresenceContext context);
-
-/**
- * Creates a presence for an account.
- *
- * @param account The account.
- *
- * @return The new presence.
- */
-PurplePresence *purple_presence_new_for_account(PurpleAccount *account);
-
-/**
- * Creates a presence for a conversation.
- *
- * @param conv The conversation.
- *
- * @return The new presence.
- */
-PurplePresence *purple_presence_new_for_conv(PurpleConversation *conv);
-
-/**
- * Creates a presence for a buddy.
- *
- * @param buddy The buddy.
- *
- * @return The new presence.
- */
-PurplePresence *purple_presence_new_for_buddy(PurpleBuddy *buddy);
-
-/**
- * Destroys a presence.
- *
- * All statuses added to this list will be destroyed along with
- * the presence.
- *
- * @param presence The presence to destroy.
- */
-void purple_presence_destroy(PurplePresence *presence);
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
-/**
- * Adds a status to a presence.
- *
- * @param presence The presence.
- * @param status The status to add.
- *
- * @deprecated This function is only used by purple_presence_add_list,
- * and both should be removed in 3.0.0.
- */
-void purple_presence_add_status(PurplePresence *presence, PurpleStatus *status);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
-/**
- * Adds a list of statuses to the presence.
- *
- * @param presence The presence.
- * @param source_list The source list of statuses to add, which is not
- * modified or freed by this function.
- *
- * @deprecated This function isn't used and should be removed in 3.0.0.
- */
-void purple_presence_add_list(PurplePresence *presence, GList *source_list);
-#endif
-
-/**
- * Sets the active state of a status in a presence.
- *
- * Only independent statuses can be set unactive. Normal statuses can only
- * be set active, so if you wish to disable a status, set another
- * non-independent status to active, or use purple_presence_switch_status().
- *
- * @param presence The presence.
- * @param status_id The ID of the status.
- * @param active The active state.
- */
-void purple_presence_set_status_active(PurplePresence *presence,
- const char *status_id, gboolean active);
-
-/**
- * Switches the active status in a presence.
- *
- * This is similar to purple_presence_set_status_active(), except it won't
- * activate independent statuses.
- *
- * @param presence The presence.
- * @param status_id The status ID to switch to.
- */
-void purple_presence_switch_status(PurplePresence *presence,
- const char *status_id);
-
-/**
- * Sets the idle state and time on a presence.
- *
- * @param presence The presence.
- * @param idle The idle state.
- * @param idle_time The idle time, if @a idle is TRUE. This
- * is the time at which the user became idle,
- * in seconds since the epoch. If this value is
- * unknown then 0 should be used.
- */
-void purple_presence_set_idle(PurplePresence *presence, gboolean idle,
- time_t idle_time);
-
-/**
- * Sets the login time on a presence.
- *
- * @param presence The presence.
- * @param login_time The login time.
- */
-void purple_presence_set_login_time(PurplePresence *presence, time_t login_time);
-
-
-/**
- * Returns the presence's context.
- *
- * @param presence The presence.
- *
- * @return The presence's context.
- */
-PurplePresenceContext purple_presence_get_context(const PurplePresence *presence);
-
-/**
- * Returns a presence's account.
- *
- * @param presence The presence.
- *
- * @return The presence's account.
- */
-PurpleAccount *purple_presence_get_account(const PurplePresence *presence);
-
-/**
- * Returns a presence's conversation.
- *
- * @param presence The presence.
- *
- * @return The presence's conversation.
- */
-PurpleConversation *purple_presence_get_conversation(const PurplePresence *presence);
-
-/**
- * Returns a presence's chat user.
- *
- * @param presence The presence.
- *
- * @return The chat's user.
- */
-const char *purple_presence_get_chat_user(const PurplePresence *presence);
-
-/**
- * Returns the presence's buddy.
- *
- * @param presence The presence.
- *
- * @return The presence's buddy.
- */
-PurpleBuddy *purple_presence_get_buddy(const PurplePresence *presence);
-
-/**
- * Returns all the statuses in a presence.
- *
- * @param presence The presence.
- *
- * @constreturn The statuses.
- */
-GList *purple_presence_get_statuses(const PurplePresence *presence);
-
-/**
- * Returns the status with the specified ID from a presence.
- *
- * @param presence The presence.
- * @param status_id The ID of the status.
- *
- * @return The status if found, or NULL.
- */
-PurpleStatus *purple_presence_get_status(const PurplePresence *presence,
- const char *status_id);
-
-/**
- * Returns the active exclusive status from a presence.
- *
- * @param presence The presence.
- *
- * @return The active exclusive status.
- */
-PurpleStatus *purple_presence_get_active_status(const PurplePresence *presence);
-
-/**
- * Returns whether or not a presence is available.
- *
- * Available presences are online and possibly invisible, but not away or idle.
- *
- * @param presence The presence.
- *
- * @return TRUE if the presence is available, or FALSE otherwise.
- */
-gboolean purple_presence_is_available(const PurplePresence *presence);
-
-/**
- * Returns whether or not a presence is online.
- *
- * @param presence The presence.
- *
- * @return TRUE if the presence is online, or FALSE otherwise.
- */
-gboolean purple_presence_is_online(const PurplePresence *presence);
-
-/**
- * Returns whether or not a status in a presence is active.
- *
- * A status is active if itself or any of its sub-statuses are active.
- *
- * @param presence The presence.
- * @param status_id The ID of the status.
- *
- * @return TRUE if the status is active, or FALSE.
- */
-gboolean purple_presence_is_status_active(const PurplePresence *presence,
- const char *status_id);
-
-/**
- * Returns whether or not a status with the specified primitive type
- * in a presence is active.
- *
- * A status is active if itself or any of its sub-statuses are active.
- *
- * @param presence The presence.
- * @param primitive The status primitive.
- *
- * @return TRUE if the status is active, or FALSE.
- */
-gboolean purple_presence_is_status_primitive_active(
- const PurplePresence *presence, PurpleStatusPrimitive primitive);
-
-/**
- * Returns whether or not a presence is idle.
- *
- * @param presence The presence.
- *
- * @return TRUE if the presence is idle, or FALSE otherwise.
- * If the presence is offline (purple_presence_is_online()
- * returns FALSE) then FALSE is returned.
- */
-gboolean purple_presence_is_idle(const PurplePresence *presence);
-
-/**
- * Returns the presence's idle time.
- *
- * @param presence The presence.
- *
- * @return The presence's idle time.
- */
-time_t purple_presence_get_idle_time(const PurplePresence *presence);
-
-/**
- * Returns the presence's login time.
- *
- * @param presence The presence.
- *
- * @return The presence's login time.
- */
-time_t purple_presence_get_login_time(const PurplePresence *presence);
/**
- * Compares two presences for availability.
+ * purple_statuses_get_handle:
*
- * @param presence1 The first presence.
- * @param presence2 The second presence.
- *
- * @return -1 if @a presence1 is more available than @a presence2.
- * 0 if @a presence1 is equal to @a presence2.
- * 1 if @a presence1 is less available than @a presence2.
- */
-gint purple_presence_compare(const PurplePresence *presence1,
- const PurplePresence *presence2);
-
-/*@}*/
-
-/**************************************************************************/
-/** @name Status subsystem */
-/**************************************************************************/
-/*@{*/
-
-/**
* Get the handle for the status subsystem.
*
- * @return the handle to the status subsystem
+ * Returns: the handle to the status subsystem
*/
-void *purple_status_get_handle(void);
+void *purple_statuses_get_handle(void);
/**
+ * purple_statuses_init:
+ *
* Initializes the status subsystem.
*/
-void purple_status_init(void);
+void purple_statuses_init(void);
/**
+ * purple_statuses_uninit:
+ *
* Uninitializes the status subsystem.
*/
-void purple_status_uninit(void);
-
-/*@}*/
+void purple_statuses_uninit(void);
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_STATUS_H_ */
+
diff --git a/libpurple/stringref.c b/libpurple/stringref.c
index 3957edcea3..8ec1637b9f 100644
--- a/libpurple/stringref.c
+++ b/libpurple/stringref.c
@@ -1,8 +1,3 @@
-/**
- * @file stringref.c Reference-counted immutable strings
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -34,7 +29,7 @@
#include "eventloop.h"
#include "stringref.h"
-/**
+/*
* The internal representation of a stringref.
*
* @note For this structure to be useful, the string contained within
@@ -42,14 +37,14 @@
* directly!
*/
struct _PurpleStringref {
- guint32 ref; /**< The reference count of this string.
+ guint32 ref; /* The reference count of this string.
* Note that reference counts are only
* 31 bits, and the high-order bit
* indicates whether this string is up
* for GC at the next idle handler...
* But you aren't going to touch this
* anyway, right? */
- char value[1]; /**< The string contained in this ref.
+ char value[1]; /* The string contained in this ref.
* Notice that it is simply "hanging
* off the end" of the ref ... this
* is to save an allocation. */
diff --git a/libpurple/stringref.h b/libpurple/stringref.h
index cbb9eecb19..10313b39c7 100644
--- a/libpurple/stringref.h
+++ b/libpurple/stringref.h
@@ -1,10 +1,5 @@
/* TODO: Can we just replace this whole thing with a GCache */
-/**
- * @file stringref.h Reference-counted immutable strings
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -26,75 +21,88 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
*/
+
#ifndef _PURPLE_STRINGREF_H_
#define _PURPLE_STRINGREF_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
+/**
+ * SECTION:stringref
+ * @section_id: libpurple-stringref
+ * @short_description: <filename>stringref.h</filename>
+ * @title: Reference-counted Immutable Strings
+ */
typedef struct _PurpleStringref PurpleStringref;
+G_BEGIN_DECLS
+
/**
+ * purple_stringref_new:
+ * @value: This will be the value of the string; it will be
+ * duplicated.
+ *
* Creates an immutable reference-counted string object. The newly
* created object will have a reference count of 1.
*
- * @param value This will be the value of the string; it will be
- * duplicated.
- *
- * @return A newly allocated string reference object with a refcount
+ * Returns: A newly allocated string reference object with a refcount
* of 1.
*/
PurpleStringref *purple_stringref_new(const char *value);
/**
+ * purple_stringref_new_noref:
+ * @value: This will be the value of the string; it will be
+ * duplicated.
+ *
* Creates an immutable reference-counted string object. The newly
* created object will have a reference count of zero, and if it is
* not referenced before the next iteration of the mainloop it will
* be freed at that time.
*
- * @param value This will be the value of the string; it will be
- * duplicated.
- *
- * @return A newly allocated string reference object with a refcount
+ * Returns: A newly allocated string reference object with a refcount
* of zero.
*/
PurpleStringref *purple_stringref_new_noref(const char *value);
/**
+ * purple_stringref_printf:
+ * @format: A printf-style format specification.
+ *
* Creates an immutable reference-counted string object from a printf
* format specification and arguments. The created object will have a
* reference count of 1.
*
- * @param format A printf-style format specification.
- *
- * @return A newly allocated string reference object with a refcount
+ * Returns: A newly allocated string reference object with a refcount
* of 1.
*/
PurpleStringref *purple_stringref_printf(const char *format, ...);
/**
- * Increase the reference count of the given stringref.
+ * purple_stringref_ref:
+ * @stringref: String to be referenced.
*
- * @param stringref String to be referenced.
+ * Increase the reference count of the given stringref.
*
- * @return A pointer to the referenced string.
+ * Returns: A pointer to the referenced string.
*/
PurpleStringref *purple_stringref_ref(PurpleStringref *stringref);
/**
+ * purple_stringref_unref:
+ * @stringref: String to be dereferenced.
+ *
* Decrease the reference count of the given stringref. If this
* reference count reaches zero, the stringref will be freed; thus
* you MUST NOT use this string after dereferencing it.
- *
- * @param stringref String to be dereferenced.
*/
void purple_stringref_unref(PurpleStringref *stringref);
/**
+ * purple_stringref_value:
+ * @stringref: String reference from which to retrieve the value.
+ *
* Retrieve the value of a stringref.
*
- * @note This value should not be cached or stored in a local variable.
+ * Note: This value should not be cached or stored in a local variable.
* While there is nothing inherently incorrect about doing so, it
* is easy to forget that the cached value is in fact a
* reference-counted object and accidentally use it after
@@ -103,36 +111,33 @@ void purple_stringref_unref(PurpleStringref *stringref);
* be valid or invalid nondeterministically based on how many
* other references to it exist.
*
- * @param stringref String reference from which to retrieve the value.
- *
- * @return The contents of the string reference.
+ * Returns: The contents of the string reference.
*/
const char *purple_stringref_value(const PurpleStringref *stringref);
/**
+ * purple_stringref_cmp:
+ * @s1: The reference string.
+ * @s2: The string to compare against the reference.
+ *
* Compare two stringrefs for string equality. This returns the same
* value as strcmp would, where <0 indicates that s1 is "less than" s2
* in the ASCII lexicography, 0 indicates equality, etc.
*
- * @param s1 The reference string.
- *
- * @param s2 The string to compare against the reference.
- *
- * @return An ordering indication on s1 and s2.
+ * Returns: An ordering indication on s1 and s2.
*/
int purple_stringref_cmp(const PurpleStringref *s1, const PurpleStringref *s2);
/**
- * Find the length of the string inside a stringref.
+ * purple_stringref_len:
+ * @stringref: The string in whose length we are interested.
*
- * @param stringref The string in whose length we are interested.
+ * Find the length of the string inside a stringref.
*
- * @return The length of the string in stringref
+ * Returns: The length of the string in stringref
*/
size_t purple_stringref_len(const PurpleStringref *stringref);
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_STRINGREF_H_ */
diff --git a/libpurple/stun.c b/libpurple/stun.c
index c470b967b3..e6d2d5b317 100644
--- a/libpurple/stun.c
+++ b/libpurple/stun.c
@@ -1,8 +1,3 @@
-/**
- * @file stun.c STUN (RFC3489) Implementation
- * @ingroup core
- */
-
/* purple
*
* STUN implementation inspired by jstun [http://jstun.javawi.de/]
@@ -53,6 +48,10 @@
#define ATTRIB_MAPPEDADDRESS 0x0001
+#ifndef _SIZEOF_ADDR_IFREQ
+# define _SIZEOF_ADDR_IFREQ(a) sizeof(a)
+#endif
+
struct stun_header {
guint16 type;
guint16 len;
@@ -106,7 +105,7 @@ static void close_stun_conn(struct stun_conn *sc) {
static void do_callbacks(void) {
while (callbacks) {
- StunCallback cb = callbacks->data;
+ PurpleStunCallback cb = callbacks->data;
if (cb)
cb(&nattype);
callbacks = g_slist_delete_link(callbacks, callbacks);
@@ -138,7 +137,7 @@ static gboolean timeoutfunc(gpointer data) {
sc->retry++;
if (sendto(sc->fd, sc->packet, sc->packetsize, 0,
(struct sockaddr *)&(sc->addr), sizeof(struct sockaddr_in)) !=
- sc->packetsize)
+ (gssize)sc->packetsize)
{
purple_debug_warning("stun", "sendto failed\n");
return FALSE;
@@ -169,12 +168,13 @@ static void do_test2(struct stun_conn *sc) {
static void reply_cb(gpointer data, gint source, PurpleInputCondition cond) {
struct stun_conn *sc = data;
- char buffer[65536];
- char *tmp;
- int len;
+ guchar buffer[65536];
+ struct ifreq buffer_ifr[1000];
+ guchar *it, *it_end;
+ gssize len;
struct in_addr in;
- struct stun_attrib *attrib;
- struct stun_header *hdr;
+ struct stun_attrib attrib;
+ struct stun_header hdr;
struct ifconf ifc;
struct ifreq *ifr;
struct sockaddr_in *sinptr;
@@ -182,61 +182,61 @@ static void reply_cb(gpointer data, gint source, PurpleInputCondition cond) {
memset(&in, 0, sizeof(in));
len = recv(source, buffer, sizeof(buffer) - 1, 0);
- if (len < 0) {
+ if (len <= 0) {
purple_debug_warning("stun", "unable to read stun response\n");
return;
}
buffer[len] = '\0';
- if (len < sizeof(struct stun_header)) {
+ if ((gsize)len < sizeof(struct stun_header)) {
purple_debug_warning("stun", "got invalid response\n");
return;
}
- hdr = (struct stun_header*) buffer;
- if (len != (ntohs(hdr->len) + sizeof(struct stun_header))) {
+ memcpy(&hdr, buffer, sizeof(hdr));
+ if ((gsize)len != (ntohs(hdr.len) + sizeof(struct stun_header))) {
purple_debug_warning("stun", "got incomplete response\n");
return;
}
/* wrong transaction */
- if(hdr->transid[0] != sc->packet->transid[0]
- || hdr->transid[1] != sc->packet->transid[1]
- || hdr->transid[2] != sc->packet->transid[2]
- || hdr->transid[3] != sc->packet->transid[3]) {
+ if(hdr.transid[0] != sc->packet->transid[0]
+ || hdr.transid[1] != sc->packet->transid[1]
+ || hdr.transid[2] != sc->packet->transid[2]
+ || hdr.transid[3] != sc->packet->transid[3]) {
purple_debug_warning("stun", "got wrong transid\n");
return;
}
if(sc->test==1) {
- if (hdr->type != MSGTYPE_BINDINGRESPONSE) {
+ if (hdr.type != MSGTYPE_BINDINGRESPONSE) {
purple_debug_warning("stun",
"Expected Binding Response, got %d\n",
- hdr->type);
+ hdr.type);
return;
}
- tmp = buffer + sizeof(struct stun_header);
- while((buffer + len) > (tmp + sizeof(struct stun_attrib))) {
- attrib = (struct stun_attrib*) tmp;
- tmp += sizeof(struct stun_attrib);
+ it = buffer + sizeof(struct stun_header);
+ while((buffer + len) > (it + sizeof(struct stun_attrib))) {
+ memcpy(&attrib, it, sizeof(attrib));
+ it += sizeof(struct stun_attrib);
- if (!((buffer + len) > (tmp + ntohs(attrib->len))))
+ if (!((buffer + len) > (it + ntohs(attrib.len))))
break;
- if(attrib->type == htons(ATTRIB_MAPPEDADDRESS)
- && ntohs(attrib->len) == 8) {
+ if(attrib.type == htons(ATTRIB_MAPPEDADDRESS)
+ && ntohs(attrib.len) == 8) {
char *ip;
/* Skip the first unused byte,
* the family(1 byte), and the port(2 bytes);
* then read the 4 byte IPv4 address */
- memcpy(&in.s_addr, tmp + 4, 4);
+ memcpy(&in.s_addr, it + 4, 4);
ip = inet_ntoa(in);
if(ip)
g_strlcpy(nattype.publicip, ip, sizeof(nattype.publicip));
}
- tmp += ntohs(attrib->len);
+ it += ntohs(attrib.len);
}
purple_debug_info("stun", "got public ip %s\n", nattype.publicip);
nattype.status = PURPLE_STUN_STATUS_DISCOVERED;
@@ -245,19 +245,19 @@ static void reply_cb(gpointer data, gint source, PurpleInputCondition cond) {
/* is it a NAT? */
- ifc.ifc_len = sizeof(buffer);
- ifc.ifc_req = (struct ifreq *) buffer;
+ ifc.ifc_len = sizeof(buffer_ifr);
+ ifc.ifc_req = buffer_ifr;
ioctl(source, SIOCGIFCONF, &ifc);
- tmp = buffer;
- while(tmp < buffer + ifc.ifc_len) {
- ifr = (struct ifreq *) tmp;
-
- tmp += sizeof(struct ifreq);
+ it = buffer;
+ it_end = it + ifc.ifc_len;
+ while (it < it_end) {
+ ifr = (struct ifreq*)(gpointer)it;
+ it += _SIZEOF_ADDR_IFREQ(*ifr);
if(ifr->ifr_addr.sa_family == AF_INET) {
/* we only care about ipv4 interfaces */
- sinptr = (struct sockaddr_in *) &ifr->ifr_addr;
+ sinptr = (struct sockaddr_in *)(gpointer)&ifr->ifr_addr;
if(sinptr->sin_addr.s_addr == in.s_addr) {
/* no NAT */
purple_debug_info("stun", "no nat\n");
@@ -323,7 +323,7 @@ static void hbn_listen_cb(int fd, gpointer data) {
if(sendto(sc->fd, &hdr_data, sizeof(struct stun_header), 0,
(struct sockaddr *)&(sc->addr),
- sizeof(struct sockaddr_in)) < sizeof(struct stun_header)) {
+ sizeof(struct sockaddr_in)) < (gssize)sizeof(struct stun_header)) {
nattype.status = PURPLE_STUN_STATUS_UNKNOWN;
nattype.lookup_time = time(NULL);
do_callbacks();
@@ -345,7 +345,7 @@ static void hbn_cb(GSList *hosts, gpointer data, const char *error_message) {
return;
}
- if (!purple_network_listen_range(12108, 12208, SOCK_DGRAM, hbn_listen_cb, hosts)) {
+ if (!purple_network_listen_range(12108, 12208, AF_UNSPEC, SOCK_DGRAM, TRUE, hbn_listen_cb, hosts)) {
while (hosts) {
hosts = g_slist_delete_link(hosts, hosts);
g_free(hosts->data);
@@ -372,17 +372,17 @@ static void do_test1(PurpleSrvResponse *resp, int results, gpointer sdata) {
purple_debug_info("stun", "got %d SRV responses, server: %s, port: %d\n",
results, servername, port);
- purple_dnsquery_a_account(NULL, servername, port, hbn_cb, NULL);
+ purple_dnsquery_a(NULL, servername, port, hbn_cb, NULL);
g_free(resp);
}
static gboolean call_callback(gpointer data) {
- StunCallback cb = data;
+ PurpleStunCallback cb = data;
cb(&nattype);
return FALSE;
}
-PurpleStunNatDiscovery *purple_stun_discover(StunCallback cb) {
+PurpleStunNatDiscovery *purple_stun_discover(PurpleStunCallback cb) {
const char *servername = purple_prefs_get_string("/purple/network/stun_server");
purple_debug_info("stun", "using server %s\n", servername);
@@ -396,8 +396,8 @@ PurpleStunNatDiscovery *purple_stun_discover(StunCallback cb) {
if(nattype.status != PURPLE_STUN_STATUS_UNDISCOVERED) {
gboolean use_cached_result = TRUE;
- /** Deal with the server name having changed since we did the
- lookup */
+ /* Deal with the server name having changed since we did the
+ lookup */
if (servername && strlen(servername) > 1
&& !purple_strequal(servername, nattype.servername)) {
use_cached_result = FALSE;
@@ -431,7 +431,7 @@ PurpleStunNatDiscovery *purple_stun_discover(StunCallback cb) {
nattype.servername = g_strdup(servername);
callbacks = g_slist_append(callbacks, cb);
- purple_srv_resolve_account(NULL, "stun", "udp", servername, do_test1,
+ purple_srv_resolve(NULL, "stun", "udp", servername, do_test1,
(gpointer) servername);
return &nattype;
diff --git a/libpurple/stun.h b/libpurple/stun.h
index b51a61c7e5..0a45c4954c 100644
--- a/libpurple/stun.h
+++ b/libpurple/stun.h
@@ -1,8 +1,3 @@
-/**
- * @file stun.h STUN API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -23,23 +18,29 @@
* 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_STUN_H_
#define _PURPLE_STUN_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
+/**
+ * SECTION:stun
+ * @section_id: libpurple-stun
+ * @short_description: <filename>stun.h</filename>
+ * @title: STUN API
+ */
/**************************************************************************/
-/** @name STUN API */
+/* STUN API */
/**************************************************************************/
-/*@{*/
typedef struct _PurpleStunNatDiscovery PurpleStunNatDiscovery;
+/**
+ * PurpleStunStatus:
+ * @PURPLE_STUN_STATUS_UNKNOWN: no STUN server reachable
+ */
typedef enum {
PURPLE_STUN_STATUS_UNDISCOVERED = -1,
- PURPLE_STUN_STATUS_UNKNOWN, /* no STUN server reachable */
+ PURPLE_STUN_STATUS_UNKNOWN,
PURPLE_STUN_STATUS_DISCOVERING,
PURPLE_STUN_STATUS_DISCOVERED
} PurpleStunStatus;
@@ -61,28 +62,27 @@ struct _PurpleStunNatDiscovery {
time_t lookup_time;
};
-typedef void (*StunCallback) (PurpleStunNatDiscovery *);
+typedef void (*PurpleStunCallback) (PurpleStunNatDiscovery *);
+
+G_BEGIN_DECLS
/**
+ * 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.
+ *
* Starts a NAT discovery. It returns a PurpleStunNatDiscovery if the discovery
* is already done. Otherwise the callback is called when the discovery is over
* and NULL is returned.
*
- * @param 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.
- *
- * @return a PurpleStunNatDiscovery which includes the public IP and the type
+ * Returns: a PurpleStunNatDiscovery which includes the public IP and the type
* of NAT or NULL is discovery would block
*/
-PurpleStunNatDiscovery *purple_stun_discover(StunCallback cb);
+PurpleStunNatDiscovery *purple_stun_discover(PurpleStunCallback cb);
void purple_stun_init(void);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_STUN_H_ */
diff --git a/libpurple/tag.sh b/libpurple/tag.sh
new file mode 100755
index 0000000000..91504ceedc
--- /dev/null
+++ b/libpurple/tag.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+# Script to silence win32 and perl build by displaying a neat one-line notice
+# instead of full command contents when executing compilers.
+#
+# Written by Tomek Wasilczyk <tomkiewicz@cpw.pidgin.im>, licensed under GNU GPL
+
+tag=$1
+found=0
+object=""
+file_1=""
+file_2=""
+is_final=0
+for arg in "$@"
+do
+ if [ "$found" == 1 ]; then
+ object="$arg"
+ break
+ fi
+ if [ "$arg" == "-o" ]; then
+ found=1
+ fi
+ if [ "$tag" == "auto" ] && [ "$arg" == "-shared" ]; then
+ tag="CCLD"
+ fi
+ if [ "$tag" == "PERL" ] && [ "${arg%(*}" == "Mkbootstrap" ]; then
+ object="${arg%;}"
+ is_final=1
+ break
+ fi
+ if [ "$tag" == "PERL" ] && [ "${arg%(*}" == "ExtUtils::ParseXS::process_file" ]; then
+ object="${arg#*output => \"}"
+ object="${object%\", *}"
+ is_final=1
+ break
+ fi
+ ext_1=${arg#${arg%??}}
+ if [ "${ext_1}" == ".c" ]; then
+ file_1="$arg"
+ fi
+ ext_2=${arg#${arg%???}}
+ if [ "${ext_2}" == ".xs" ]; then
+ file_2="$arg"
+ fi
+ ext_3=${arg#${arg%????}}
+ if [ "${ext_3}" == ".3pm" ]; then
+ file_2="$arg"
+ fi
+done
+
+if [ "$tag" == "auto" ]; then
+ tag="CC"
+fi
+
+if [ "$tag" == "PERL" ] && [ "$is_final" == 0 ]; then
+ object=`echo "$object" | sed -n 's|.*output *=> *"\([^"]*\)".*|\1|p'`
+fi
+
+if [ "$object" == "" ] && [ "${file_1}" != "" ]; then
+ object="${file_1}"
+fi
+if [ "$object" == "" ] && [ "${file_2}" != "" ]; then
+ object="${file_2}"
+fi
+
+shift 1
+if [ "$object" == "" ]; then
+ echo "$@" >&2
+else
+ echo -e " $tag\t$object" >&2
+fi
+"$@"
diff --git a/libpurple/tests/Makefile.am b/libpurple/tests/Makefile.am
index ea91aab4d3..d010d7dce6 100644
--- a/libpurple/tests/Makefile.am
+++ b/libpurple/tests/Makefile.am
@@ -15,6 +15,7 @@ check_libpurple_SOURCES=\
test_jabber_jutil.c \
test_jabber_scram.c \
test_oscar_util.c \
+ test_trie.c \
test_yahoo_util.c \
test_util.c \
test_xmlnode.c \
@@ -26,8 +27,7 @@ check_libpurple_CFLAGS=\
$(DEBUG_CFLAGS) \
$(LIBXML_CFLAGS) \
-I.. \
- -I$(top_srcdir)/libpurple \
- -DBUILDDIR=\"$(top_builddir)\"
+ -I$(top_srcdir)/libpurple
check_libpurple_LDADD=\
$(top_builddir)/libpurple/protocols/jabber/libjabber.la \
diff --git a/libpurple/tests/check_libpurple.c b/libpurple/tests/check_libpurple.c
index 2eb80bf44a..fb4ecb4cbb 100644
--- a/libpurple/tests/check_libpurple.c
+++ b/libpurple/tests/check_libpurple.c
@@ -25,11 +25,8 @@ static PurpleEventLoopUiOps eventloop_ui_ops = {
purple_check_input_add,
g_source_remove,
NULL, /* input_get_error */
-#if GLIB_CHECK_VERSION(2,14,0)
g_timeout_add_seconds,
-#else
NULL,
-#endif
NULL,
NULL,
NULL
@@ -49,7 +46,7 @@ purple_check_init(void) {
{
gchar *home_dir;
- home_dir = g_build_path(G_DIR_SEPARATOR_S, BUILDDIR, "libpurple", "tests", "home", NULL);
+ home_dir = g_build_path(G_DIR_SEPARATOR_S, $(top_builddir), "libpurple", "tests", "home", NULL);
purple_util_set_user_dir(home_dir);
g_free(home_dir);
}
@@ -94,7 +91,8 @@ int main(void)
srunner_add_suite(sr, oscar_util_suite());
srunner_add_suite(sr, yahoo_util_suite());
srunner_add_suite(sr, util_suite());
- srunner_add_suite(sr, xmlnode_suite());
+ srunner_add_suite(sr, purple_xmlnode_suite());
+ srunner_add_suite(sr, purple_trie_suite());
/* make this a libpurple "ui" */
purple_check_init();
diff --git a/libpurple/tests/test_cipher.c b/libpurple/tests/test_cipher.c
index adb176dd0b..8f21a6935c 100644
--- a/libpurple/tests/test_cipher.c
+++ b/libpurple/tests/test_cipher.c
@@ -7,29 +7,32 @@
#include "tests.h"
-#include "../cipher.h"
+#include "../ciphers/des3cipher.h"
+#include "../ciphers/descipher.h"
+#include "../ciphers/hmaccipher.h"
+#include "../ciphers/md4hash.h"
+#include "../ciphers/md5hash.h"
+#include "../ciphers/sha1hash.h"
+#include "../ciphers/sha256hash.h"
/******************************************************************************
* MD4 Tests
*****************************************************************************/
#define MD4_TEST(data, digest) { \
- PurpleCipher *cipher = NULL; \
- PurpleCipherContext *context = NULL; \
+ PurpleHash *hash = NULL; \
gchar cdigest[33]; \
gboolean ret = FALSE; \
\
- cipher = purple_ciphers_find_cipher("md4"); \
- context = purple_cipher_context_new(cipher, NULL); \
- purple_cipher_context_append(context, (guchar *)(data), strlen((data))); \
+ hash = purple_md4_hash_new(); \
+ purple_hash_append(hash, (guchar *)(data), strlen((data))); \
\
- ret = purple_cipher_context_digest_to_str(context, sizeof(cdigest), cdigest, \
- NULL); \
+ ret = purple_hash_digest_to_str(hash, cdigest, sizeof(cdigest)); \
\
fail_unless(ret == TRUE, NULL); \
\
fail_unless(strcmp((digest), cdigest) == 0, NULL); \
\
- purple_cipher_context_destroy(context); \
+ g_object_unref(hash); \
}
START_TEST(test_md4_empty_string) {
@@ -76,23 +79,20 @@ END_TEST
* MD5 Tests
*****************************************************************************/
#define MD5_TEST(data, digest) { \
- PurpleCipher *cipher = NULL; \
- PurpleCipherContext *context = NULL; \
+ PurpleHash *hash = NULL; \
gchar cdigest[33]; \
gboolean ret = FALSE; \
\
- cipher = purple_ciphers_find_cipher("md5"); \
- context = purple_cipher_context_new(cipher, NULL); \
- purple_cipher_context_append(context, (guchar *)(data), strlen((data))); \
+ hash = purple_md5_hash_new(); \
+ purple_hash_append(hash, (guchar *)(data), strlen((data))); \
\
- ret = purple_cipher_context_digest_to_str(context, sizeof(cdigest), cdigest, \
- NULL); \
+ ret = purple_hash_digest_to_str(hash, cdigest, sizeof(cdigest)); \
\
fail_unless(ret == TRUE, NULL); \
\
fail_unless(strcmp((digest), cdigest) == 0, NULL); \
\
- purple_cipher_context_destroy(context); \
+ g_object_unref(hash); \
}
START_TEST(test_md5_empty_string) {
@@ -138,17 +138,15 @@ END_TEST
* SHA-1 Tests
*****************************************************************************/
#define SHA1_TEST(data, digest) { \
- PurpleCipher *cipher = NULL; \
- PurpleCipherContext *context = NULL; \
+ PurpleHash *hash = NULL; \
gchar cdigest[41]; \
gboolean ret = FALSE; \
gchar *input = data; \
\
- cipher = purple_ciphers_find_cipher("sha1"); \
- context = purple_cipher_context_new(cipher, NULL); \
+ hash = purple_sha1_hash_new(); \
\
if (input) { \
- purple_cipher_context_append(context, (guchar *)input, strlen(input)); \
+ purple_hash_append(hash, (guchar *)input, strlen(input)); \
} else { \
gint j; \
guchar buff[1000]; \
@@ -156,17 +154,16 @@ END_TEST
memset(buff, 'a', 1000); \
\
for(j = 0; j < 1000; j++) \
- purple_cipher_context_append(context, buff, 1000); \
+ purple_hash_append(hash, buff, 1000); \
} \
\
- ret = purple_cipher_context_digest_to_str(context, sizeof(cdigest), cdigest, \
- NULL); \
+ ret = purple_hash_digest_to_str(hash, cdigest, sizeof(cdigest)); \
\
fail_unless(ret == TRUE, NULL); \
\
fail_unless(strcmp((digest), cdigest) == 0, NULL); \
\
- purple_cipher_context_destroy(context); \
+ g_object_unref(hash); \
}
START_TEST(test_sha1_empty_string) {
@@ -199,17 +196,15 @@ END_TEST
* SHA-256 Tests
*****************************************************************************/
#define SHA256_TEST(data, digest) { \
- PurpleCipher *cipher = NULL; \
- PurpleCipherContext *context = NULL; \
+ PurpleHash *hash = NULL; \
gchar cdigest[65]; \
gboolean ret = FALSE; \
gchar *input = data; \
\
- cipher = purple_ciphers_find_cipher("sha256"); \
- context = purple_cipher_context_new(cipher, NULL); \
+ hash = purple_sha256_hash_new(); \
\
if (input) { \
- purple_cipher_context_append(context, (guchar *)input, strlen(input)); \
+ purple_hash_append(hash, (guchar *)input, strlen(input)); \
} else { \
gint j; \
guchar buff[1000]; \
@@ -217,17 +212,16 @@ END_TEST
memset(buff, 'a', 1000); \
\
for(j = 0; j < 1000; j++) \
- purple_cipher_context_append(context, buff, 1000); \
+ purple_hash_append(hash, buff, 1000); \
} \
\
- ret = purple_cipher_context_digest_to_str(context, sizeof(cdigest), cdigest, \
- NULL); \
+ ret = purple_hash_digest_to_str(hash, cdigest, sizeof(cdigest)); \
\
fail_unless(ret == TRUE, NULL); \
\
fail_unless(strcmp((digest), cdigest) == 0, NULL); \
\
- purple_cipher_context_destroy(context); \
+ g_object_unref(hash); \
}
START_TEST(test_sha256_empty_string) {
@@ -261,29 +255,24 @@ END_TEST
*****************************************************************************/
#define DES_TEST(in, keyz, out, len) { \
PurpleCipher *cipher = NULL; \
- PurpleCipherContext *context = NULL; \
guchar answer[len+1]; \
gint ret = 0; \
guchar decrypt[len+1] = in; \
guchar key[8+1] = keyz;\
guchar encrypt[len+1] = out;\
- size_t outlen; \
\
- cipher = purple_ciphers_find_cipher("des"); \
- context = purple_cipher_context_new(cipher, NULL); \
- purple_cipher_context_set_key(context, key); \
+ cipher = purple_des_cipher_new(); \
+ purple_cipher_set_key(cipher, key, 8); \
\
- ret = purple_cipher_context_encrypt(context, decrypt, len, answer, &outlen); \
- fail_unless(ret == 0, NULL); \
- fail_unless(outlen == (len), NULL); \
+ ret = purple_cipher_encrypt(cipher, decrypt, len, answer, len); \
+ fail_unless(ret == len, NULL); \
fail_unless(memcmp(encrypt, answer, len) == 0, NULL); \
\
- ret = purple_cipher_context_decrypt(context, encrypt, len, answer, &outlen); \
- fail_unless(ret == 0, NULL); \
- fail_unless(outlen == (len), NULL); \
+ ret = purple_cipher_decrypt(cipher, encrypt, len, answer, len); \
+ fail_unless(ret == len, NULL); \
fail_unless(memcmp(decrypt, answer, len) == 0, NULL); \
\
- purple_cipher_context_destroy(context); \
+ g_object_unref(cipher); \
}
START_TEST(test_des_12345678) {
@@ -310,30 +299,25 @@ END_TEST
#define DES3_TEST(in, key, iv, out, len, mode) { \
PurpleCipher *cipher = NULL; \
- PurpleCipherContext *context = NULL; \
guchar answer[len+1]; \
guchar decrypt[len+1] = in; \
guchar encrypt[len+1] = out; \
- size_t outlen; \
gint ret = 0; \
\
- cipher = purple_ciphers_find_cipher("des3"); \
- context = purple_cipher_context_new(cipher, NULL); \
- purple_cipher_context_set_key(context, (guchar *)key); \
- purple_cipher_context_set_batch_mode(context, (mode)); \
- purple_cipher_context_set_iv(context, (guchar *)iv, 8); \
+ cipher = purple_des3_cipher_new(); \
+ purple_cipher_set_key(cipher, (guchar *)key, 24); \
+ purple_cipher_set_batch_mode(cipher, (mode)); \
+ purple_cipher_set_iv(cipher, (guchar *)iv, 8); \
\
- ret = purple_cipher_context_encrypt(context, decrypt, len, answer, &outlen); \
- fail_unless(ret == 0, NULL); \
- fail_unless(outlen == (len), NULL); \
+ ret = purple_cipher_encrypt(cipher, decrypt, len, answer, len); \
+ fail_unless(ret == len, NULL); \
fail_unless(memcmp(encrypt, answer, len) == 0, NULL); \
\
- ret = purple_cipher_context_decrypt(context, encrypt, len, answer, &outlen); \
- fail_unless(ret == 0, NULL); \
- fail_unless(outlen == (len), NULL); \
+ ret = purple_cipher_decrypt(cipher, encrypt, len, answer, len); \
+ fail_unless(ret == len, NULL); \
fail_unless(memcmp(decrypt, answer, len) == 0, NULL); \
\
- purple_cipher_context_destroy(context); \
+ g_object_unref(cipher); \
}
START_TEST(test_des3_ecb_nist1) {
@@ -481,23 +465,22 @@ END_TEST
#define HMAC_TEST(data, data_len, key, key_len, type, digest) { \
PurpleCipher *cipher = NULL; \
- PurpleCipherContext *context = NULL; \
+ PurpleHash *hash = NULL; \
gchar cdigest[41]; \
gboolean ret = FALSE; \
\
- cipher = purple_ciphers_find_cipher("hmac"); \
- context = purple_cipher_context_new(cipher, NULL); \
- purple_cipher_context_set_option(context, "hash", type); \
- purple_cipher_context_set_key_with_len(context, (guchar *)key, (key_len)); \
+ hash = purple_##type##_hash_new(); \
+ cipher = purple_hmac_cipher_new(hash); \
+ purple_cipher_set_key(cipher, (guchar *)key, (key_len)); \
\
- purple_cipher_context_append(context, (guchar *)(data), (data_len)); \
- ret = purple_cipher_context_digest_to_str(context, sizeof(cdigest), cdigest, \
- NULL); \
+ purple_cipher_append(cipher, (guchar *)(data), (data_len)); \
+ ret = purple_cipher_digest_to_str(cipher, cdigest, sizeof(cdigest)); \
\
fail_unless(ret == TRUE, NULL); \
fail_unless(strcmp((digest), cdigest) == 0, NULL); \
\
- purple_cipher_context_destroy(context); \
+ g_object_unref(cipher); \
+ g_object_unref(hash); \
}
/* HMAC MD5 */
@@ -507,7 +490,7 @@ START_TEST(test_hmac_md5_Hi) {
8,
"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
16,
- "md5",
+ md5,
"9294727a3638bb1c13f48ef8158bfc9d");
}
END_TEST
@@ -517,7 +500,7 @@ START_TEST(test_hmac_md5_what) {
28,
"Jefe",
4,
- "md5",
+ md5,
"750c783e6ab0b503eaa86e310a5db738");
}
END_TEST
@@ -531,7 +514,7 @@ START_TEST(test_hmac_md5_dd) {
50,
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
16,
- "md5",
+ md5,
"56be34521d144c88dbb8c733f0e8b3f6");
}
END_TEST
@@ -547,7 +530,7 @@ START_TEST(test_hmac_md5_cd) {
"\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
"\x15\x16\x17\x18\x19",
25,
- "md5",
+ md5,
"697eaf0aca3a3aea3a75164746ffaa79");
}
END_TEST
@@ -557,7 +540,7 @@ START_TEST(test_hmac_md5_truncation) {
20,
"\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c",
16,
- "md5",
+ md5,
"56461ef2342edc00f9bab995690efd4c");
}
END_TEST
@@ -574,7 +557,7 @@ START_TEST(test_hmac_md5_large_key) {
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
80,
- "md5",
+ md5,
"6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd");
}
END_TEST
@@ -591,7 +574,7 @@ START_TEST(test_hmac_md5_large_key_and_data) {
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
80,
- "md5",
+ md5,
"6f630fad67cda0ee1fb1f562db3aa53e");
}
END_TEST
@@ -602,7 +585,7 @@ START_TEST(test_hmac_md5_null_key) {
"\x0a\x0b\x00\x0d\x0e\x0f\x1a\x2f\x0b\x0b"
"\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b",
20,
- "md5",
+ md5,
"597bfd644b797a985561eeb03a169e59");
}
END_TEST
@@ -613,7 +596,7 @@ START_TEST(test_hmac_md5_null_text) {
"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
20,
- "md5",
+ md5,
"70be8e1b7b50dfcc335d6cd7992c564f");
}
END_TEST
@@ -624,7 +607,7 @@ START_TEST(test_hmac_md5_null_key_and_text) {
"\x0c\x0d\x00\x0f\x10\x1a\x3a\x3a\xe6\x34"
"\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b",
20,
- "md5",
+ md5,
"b31bcbba35a33a067cbba9131cba4889");
}
END_TEST
@@ -637,7 +620,7 @@ START_TEST(test_hmac_sha1_Hi) {
"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
20,
- "sha1",
+ sha1,
"b617318655057264e28bc0b6fb378c8ef146be00");
}
END_TEST
@@ -647,7 +630,7 @@ START_TEST(test_hmac_sha1_what) {
28,
"Jefe",
4,
- "sha1",
+ sha1,
"effcdf6ae5eb2fa2d27416d5f184df9c259a7c79");
}
END_TEST
@@ -662,7 +645,7 @@ START_TEST(test_hmac_sha1_dd) {
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
20,
- "sha1",
+ sha1,
"125d7342b9ac11cd91a39af48aa17b4f63f175d3");
}
END_TEST
@@ -678,7 +661,7 @@ START_TEST(test_hmac_sha1_cd) {
"\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
"\x15\x16\x17\x18\x19",
25,
- "sha1",
+ sha1,
"4c9007f4026250c6bc8414f9bf50c86c2d7235da");
}
END_TEST
@@ -689,7 +672,7 @@ START_TEST(test_hmac_sha1_truncation) {
"\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c"
"\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c",
20,
- "sha1",
+ sha1,
"4c1a03424b55e07fe7f27be1d58bb9324a9a5a04");
}
END_TEST
@@ -706,7 +689,7 @@ START_TEST(test_hmac_sha1_large_key) {
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
80,
- "sha1",
+ sha1,
"aa4ae5e15272d00e95705637ce8a3b55ed402112");
}
END_TEST
@@ -723,7 +706,7 @@ START_TEST(test_hmac_sha1_large_key_and_data) {
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
80,
- "sha1",
+ sha1,
"e8e99d0f45237d786d6bbaa7965c7808bbff1a91");
}
END_TEST
@@ -734,7 +717,7 @@ START_TEST(test_hmac_sha1_null_key) {
"\x0a\x0b\x00\x0d\x0e\x0f\x1a\x2f\x0b\x0b"
"\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b",
20,
- "sha1",
+ sha1,
"eb62a2e0e33d300be669c52aab3f591bc960aac5");
}
END_TEST
@@ -745,7 +728,7 @@ START_TEST(test_hmac_sha1_null_text) {
"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
20,
- "sha1",
+ sha1,
"31ca58d849e971e418e3439de2c6f83144b6abb7");
}
END_TEST
@@ -756,7 +739,7 @@ START_TEST(test_hmac_sha1_null_key_and_text) {
"\x0c\x0d\x00\x0f\x10\x1a\x3a\x3a\xe6\x34"
"\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b",
20,
- "sha1",
+ sha1,
"e6b8e2fede87aa09dcb13e554df1435e056eae36");
}
END_TEST
diff --git a/libpurple/tests/test_jabber_caps.c b/libpurple/tests/test_jabber_caps.c
index 6af180b658..0f534787d6 100644
--- a/libpurple/tests/test_jabber_caps.c
+++ b/libpurple/tests/test_jabber_caps.c
@@ -3,36 +3,46 @@
#include "tests.h"
#include "../xmlnode.h"
#include "../protocols/jabber/caps.h"
+#include "../ciphers/md5hash.h"
+#include "../ciphers/sha1hash.h"
START_TEST(test_parse_invalid)
{
- xmlnode *query;
+ PurpleXmlNode *query;
fail_unless(NULL == jabber_caps_parse_client_info(NULL));
/* Something other than a disco#info query */
- query = xmlnode_new("foo");
+ query = purple_xmlnode_new("foo");
fail_unless(NULL == jabber_caps_parse_client_info(query));
- xmlnode_free(query);
+ purple_xmlnode_free(query);
- query = xmlnode_new("query");
+ query = purple_xmlnode_new("query");
fail_unless(NULL == jabber_caps_parse_client_info(query));
- xmlnode_set_namespace(query, "jabber:iq:last");
+ purple_xmlnode_set_namespace(query, "jabber:iq:last");
fail_unless(NULL == jabber_caps_parse_client_info(query));
- xmlnode_free(query);
+ purple_xmlnode_free(query);
}
END_TEST
#define assert_caps_calculate_match(hash_func, hash, str) { \
- xmlnode *query = xmlnode_from_str((str), -1); \
+ PurpleXmlNode *query = purple_xmlnode_from_str((str), -1); \
+ PurpleHash *hasher = NULL; \
JabberCapsClientInfo *info = jabber_caps_parse_client_info(query); \
- gchar *got_hash = jabber_caps_calculate_hash(info, (hash_func)); \
+ gchar *got_hash; \
+ if (g_str_equal(hash_func, "sha-1")) { \
+ hasher = purple_sha1_hash_new(); \
+ } else if (g_str_equal(hash_func, "md5")) { \
+ hasher = purple_md5_hash_new(); \
+ } \
+ got_hash = jabber_caps_calculate_hash(info, hasher); \
+ g_object_unref(hasher); \
assert_string_equal_free((hash), got_hash); \
}
START_TEST(test_calculate_caps)
{
- assert_caps_calculate_match("sha1", "GNjxthSckUNvAIoCCJFttjl6VL8=",
+ assert_caps_calculate_match("sha-1", "GNjxthSckUNvAIoCCJFttjl6VL8=",
"<query xmlns='http://jabber.org/protocol/disco#info' node='http://tkabber.jabber.ru/#GNjxthSckUNvAIoCCJFttjl6VL8='><identity category='client' type='pc' name='Tkabber'/><x xmlns='jabber:x:data' type='result'><field var='FORM_TYPE' type='hidden'><value>urn:xmpp:dataforms:softwareinfo</value></field><field var='software'><value>Tkabber</value></field><field var='software_version'><value> ( 8.5.5 )</value></field><field var='os'><value>ATmega640-16AU</value></field><field var='os_version'><value/></field></x><feature var='games:board'/><feature var='google:mail:notify'/><feature var='http://jabber.org/protocol/activity'/><feature var='http://jabber.org/protocol/bytestreams'/><feature var='http://jabber.org/protocol/chatstates'/><feature var='http://jabber.org/protocol/commands'/><feature var='http://jabber.org/protocol/commands'/><feature var='http://jabber.org/protocol/disco#info'/><feature var='http://jabber.org/protocol/disco#items'/><feature var='http://jabber.org/protocol/feature-neg'/><feature var='http://jabber.org/protocol/geoloc'/><feature var='http://jabber.org/protocol/ibb'/><feature var='http://jabber.org/protocol/iqibb'/><feature var='http://jabber.org/protocol/mood'/><feature var='http://jabber.org/protocol/muc'/><feature var='http://jabber.org/protocol/mute#ancestor'/><feature var='http://jabber.org/protocol/mute#editor'/><feature var='http://jabber.org/protocol/rosterx'/><feature var='http://jabber.org/protocol/si'/><feature var='http://jabber.org/protocol/si/profile/file-transfer'/><feature var='http://jabber.org/protocol/tune'/><feature var='jabber:iq:avatar'/><feature var='jabber:iq:browse'/><feature var='jabber:iq:dtcp'/><feature var='jabber:iq:filexfer'/><feature var='jabber:iq:ibb'/><feature var='jabber:iq:inband'/><feature var='jabber:iq:jidlink'/><feature var='jabber:iq:last'/><feature var='jabber:iq:oob'/><feature var='jabber:iq:privacy'/><feature var='jabber:iq:time'/><feature var='jabber:iq:version'/><feature var='jabber:x:data'/><feature var='jabber:x:event'/><feature var='jabber:x:oob'/><feature var='urn:xmpp:ping'/><feature var='urn:xmpp:receipts'/><feature var='urn:xmpp:time'/></query>");
}
END_TEST
diff --git a/libpurple/tests/test_jabber_scram.c b/libpurple/tests/test_jabber_scram.c
index 512bc05a29..bb440100a5 100644
--- a/libpurple/tests/test_jabber_scram.c
+++ b/libpurple/tests/test_jabber_scram.c
@@ -4,8 +4,9 @@
#include "../util.h"
#include "../protocols/jabber/auth_scram.h"
#include "../protocols/jabber/jutil.h"
+#include "../ciphers/sha1hash.h"
-static JabberScramHash sha1_mech = { "-SHA-1", "sha1", 20 };
+static JabberScramHash sha1_mech = { "-SHA-1", purple_sha1_hash_new, 20 };
#define assert_pbkdf2_equal(password, salt, count, expected) { \
GString *p = g_string_new(password); \
diff --git a/libpurple/tests/test_oscar_util.c b/libpurple/tests/test_oscar_util.c
index 5f6ac1404c..380c9ec4f0 100644
--- a/libpurple/tests/test_oscar_util.c
+++ b/libpurple/tests/test_oscar_util.c
@@ -5,7 +5,7 @@
START_TEST(test_oscar_util_name_compare)
{
- int i;
+ size_t i;
const char *good[] = {
"test",
"TEST",
diff --git a/libpurple/tests/test_trie.c b/libpurple/tests/test_trie.c
new file mode 100644
index 0000000000..19b1b0ea9b
--- /dev/null
+++ b/libpurple/tests/test_trie.c
@@ -0,0 +1,364 @@
+/*
+ * 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 <string.h>
+
+#include "tests.h"
+#include "../trie.h"
+
+static gint find_sum;
+
+static gboolean
+test_trie_replace_cb(GString *out, const gchar *word, gpointer word_data,
+ gpointer user_data)
+{
+ /* the "test" word for the test_trie_replace test */
+ if ((gintptr)word_data == 0x1001)
+ return FALSE;
+
+ g_string_append_printf(out, "[%d:%x]",
+ (int)(gintptr)user_data, (int)(gintptr)word_data);
+
+ return TRUE;
+}
+
+static gboolean
+test_trie_find_cb(const gchar *word, gpointer word_data,
+ gpointer user_data)
+{
+ if ((gintptr)word_data == 0x7004)
+ return FALSE;
+
+ find_sum += (gintptr)word_data;
+ find_sum -= (gintptr)user_data * 0x1000;
+
+ return TRUE;
+}
+
+START_TEST(test_trie_replace)
+{
+ PurpleTrie *trie;
+ const gchar *in;
+ gchar *out;
+
+ trie = purple_trie_new();
+ purple_trie_set_reset_on_match(trie, FALSE);
+
+ purple_trie_add(trie, "test", (gpointer)0x1001);
+ purple_trie_add(trie, "testing", (gpointer)0x1002);
+ purple_trie_add(trie, "overtested", (gpointer)0x1003);
+ purple_trie_add(trie, "trie", (gpointer)0x1004);
+ purple_trie_add(trie, "tree", (gpointer)0x1005);
+ purple_trie_add(trie, "implement", (gpointer)0x1006);
+ purple_trie_add(trie, "implementation", (gpointer)0x1007);
+
+ in = "Alice is testing her trie implementation, "
+ "but she's far away from making test tree overtested";
+
+ out = purple_trie_replace(trie, in, test_trie_replace_cb, (gpointer)1);
+
+ assert_string_equal("Alice is [1:1002] her [1:1004] [1:1006]ation,"
+ " but she's far away from making test [1:1005] [1:1003]", out);
+
+ g_object_unref(trie);
+ g_free(out);
+}
+END_TEST
+
+START_TEST(test_trie_replace_whole)
+{
+ PurpleTrie *trie;
+ const gchar *in;
+ gchar *out;
+
+ trie = purple_trie_new();
+
+ purple_trie_add(trie, "test", (gpointer)0x2002);
+
+ in = "test";
+
+ out = purple_trie_replace(trie, in, test_trie_replace_cb, (gpointer)2);
+
+ assert_string_equal("[2:2002]", out);
+
+ g_object_unref(trie);
+ g_free(out);
+}
+END_TEST
+
+START_TEST(test_trie_replace_inner)
+{
+ PurpleTrie *trie;
+ const gchar *in;
+ gchar *out;
+
+ trie = purple_trie_new();
+
+ purple_trie_add(trie, "est", (gpointer)0x3001);
+ purple_trie_add(trie, "tester", (gpointer)0x3002);
+
+ in = "the test!";
+
+ out = purple_trie_replace(trie, in, test_trie_replace_cb, (gpointer)3);
+
+ assert_string_equal("the t[3:3001]!", out);
+
+ g_object_unref(trie);
+ g_free(out);
+}
+END_TEST
+
+START_TEST(test_trie_replace_empty)
+{
+ PurpleTrie *trie;
+ const gchar *in;
+ gchar *out;
+
+ trie = purple_trie_new();
+
+ purple_trie_add(trie, "test", (gpointer)0x4001);
+
+ in = "";
+
+ out = purple_trie_replace(trie, in, test_trie_replace_cb, (gpointer)4);
+
+ assert_string_equal("", out);
+
+ g_object_unref(trie);
+ g_free(out);
+}
+END_TEST
+
+START_TEST(test_trie_multi_replace)
+{
+ PurpleTrie *trie1, *trie2, *trie3;
+ GSList *tries = NULL;
+ const gchar *in;
+ gchar *out;
+
+ trie1 = purple_trie_new();
+ trie2 = purple_trie_new();
+ trie3 = purple_trie_new();
+
+ /* appending is not efficient, but we have only 3 elements */
+ tries = g_slist_append(tries, trie1);
+ tries = g_slist_append(tries, trie2);
+ tries = g_slist_append(tries, trie3);
+
+ purple_trie_add(trie1, "test", (gpointer)0x5011);
+ purple_trie_add(trie1, "trie1", (gpointer)0x5012);
+ purple_trie_add(trie1, "Alice", (gpointer)0x5013);
+
+ purple_trie_add(trie2, "test", (gpointer)0x5021);
+ purple_trie_add(trie2, "trie2", (gpointer)0x5022);
+ purple_trie_add(trie2, "example", (gpointer)0x5023);
+ purple_trie_add(trie2, "Ali", (gpointer)0x5024);
+
+ /* "tester" without last (accepting) letter of "test" */
+ purple_trie_add(trie3, "teser", (gpointer)0x5031);
+ purple_trie_add(trie3, "trie3", (gpointer)0x5032);
+ purple_trie_add(trie3, "tester", (gpointer)0x5033);
+ purple_trie_add(trie3, "example", (gpointer)0x5034);
+ purple_trie_add(trie3, "Al", (gpointer)0x5035);
+
+ in = "test tester trie trie1 trie2 trie3 example Alice";
+
+ out = purple_trie_multi_replace(tries, in,
+ test_trie_replace_cb, (gpointer)5);
+
+ assert_string_equal("[5:5011] [5:5011]er trie [5:5012] [5:5022] "
+ "[5:5032] [5:5023] [5:5035]ice", out);
+
+ g_slist_free_full(tries, g_object_unref);
+ g_free(out);
+}
+END_TEST
+
+START_TEST(test_trie_remove)
+{
+ PurpleTrie *trie;
+ const gchar *in;
+ gchar *out;
+
+ trie = purple_trie_new();
+
+ purple_trie_add(trie, "alice", (gpointer)0x6001);
+ purple_trie_add(trie, "bob", (gpointer)0x6002);
+ purple_trie_add(trie, "cherry", (gpointer)0x6003);
+
+ purple_trie_remove(trie, "bob");
+
+ in = "alice bob cherry";
+
+ out = purple_trie_replace(trie, in, test_trie_replace_cb, (gpointer)6);
+
+ assert_string_equal("[6:6001] bob [6:6003]", out);
+
+ g_object_unref(trie);
+ g_free(out);
+}
+END_TEST
+
+START_TEST(test_trie_find)
+{
+ PurpleTrie *trie;
+ const gchar *in;
+ gint out;
+
+ trie = purple_trie_new();
+
+ purple_trie_add(trie, "alice", (gpointer)0x7001);
+ purple_trie_add(trie, "bob", (gpointer)0x7002);
+ purple_trie_add(trie, "cherry", (gpointer)0x7003);
+ purple_trie_add(trie, "al", (gpointer)0x7004); /* not accepted */
+
+ in = "test alice bob test cherry alice";
+
+ find_sum = 0;
+ out = purple_trie_find(trie, in, test_trie_find_cb, (gpointer)7);
+
+ assert_int_equal(4, out);
+ assert_int_equal(2*1 + 2 + 3, find_sum);
+
+ g_object_unref(trie);
+}
+END_TEST
+
+START_TEST(test_trie_find_reset)
+{
+ PurpleTrie *trie;
+ const gchar *in;
+ gint out;
+
+ trie = purple_trie_new();
+ purple_trie_set_reset_on_match(trie, TRUE);
+
+ purple_trie_add(trie, "alice", (gpointer)0x8001);
+ purple_trie_add(trie, "ali", (gpointer)0x8002);
+ purple_trie_add(trie, "al", (gpointer)0x8003);
+
+ in = "al ali alice";
+
+ find_sum = 0;
+ out = purple_trie_find(trie, in, test_trie_find_cb, (gpointer)8);
+
+ assert_int_equal(3, out);
+ assert_int_equal(3 * 3, find_sum);
+
+ g_object_unref(trie);
+}
+END_TEST
+
+START_TEST(test_trie_find_noreset)
+{
+ PurpleTrie *trie;
+ const gchar *in;
+ gint out;
+
+ trie = purple_trie_new();
+ purple_trie_set_reset_on_match(trie, FALSE);
+
+ purple_trie_add(trie, "alice", (gpointer)0x9001);
+ purple_trie_add(trie, "ali", (gpointer)0x9002);
+ purple_trie_add(trie, "al", (gpointer)0x9003);
+
+ in = "al ali alice";
+
+ find_sum = 0;
+ out = purple_trie_find(trie, in, test_trie_find_cb, (gpointer)9);
+
+ assert_int_equal(6, out);
+ assert_int_equal(3*3 + 2*2 + 1, find_sum);
+
+ g_object_unref(trie);
+}
+END_TEST
+
+START_TEST(test_trie_multi_find)
+{
+ PurpleTrie *trie1, *trie2, *trie3;
+ GSList *tries = NULL;
+ const gchar *in;
+ int out;
+
+ trie1 = purple_trie_new();
+ trie2 = purple_trie_new();
+ trie3 = purple_trie_new();
+ purple_trie_set_reset_on_match(trie1, FALSE);
+ purple_trie_set_reset_on_match(trie2, TRUE);
+ purple_trie_set_reset_on_match(trie3, FALSE);
+
+ /* appending is not efficient, but we have only 3 elements */
+ tries = g_slist_append(tries, trie1);
+ tries = g_slist_append(tries, trie2);
+ tries = g_slist_append(tries, trie3);
+
+ purple_trie_add(trie1, "test", (gpointer)0x10011);
+ purple_trie_add(trie1, "trie1", (gpointer)0x10012);
+ purple_trie_add(trie1, "Alice", (gpointer)0x10013);
+
+ purple_trie_add(trie2, "test", (gpointer)0x10021);
+ purple_trie_add(trie2, "trie2", (gpointer)0x10022);
+ purple_trie_add(trie2, "example", (gpointer)0x10023);
+ purple_trie_add(trie2, "Ali", (gpointer)0x10024);
+
+ /* "tester" without last (accepting) letter of "test" */
+ purple_trie_add(trie3, "teser", (gpointer)0x10031);
+ purple_trie_add(trie3, "trie3", (gpointer)0x10032);
+ purple_trie_add(trie3, "tester", (gpointer)0x10033);
+ purple_trie_add(trie3, "example", (gpointer)0x10034);
+ purple_trie_add(trie3, "Al", (gpointer)0x10035);
+
+ in = "test tester trie trie1 trie2 trie3 example Alice";
+
+ out = purple_trie_multi_find(tries, in,
+ test_trie_find_cb, (gpointer)0x10);
+
+ assert_int_equal(9, out);
+ assert_int_equal(2 * 0x11 + 0x33 + 0x12 + 0x22 +
+ 0x32 + 0x23 + 0x35 + 0x13, find_sum);
+
+ g_slist_free_full(tries, g_object_unref);
+}
+END_TEST
+
+Suite *
+purple_trie_suite(void)
+{
+ Suite *s = suite_create("PurpleTrie class");
+
+ TCase *tc = tcase_create("trie");
+ tcase_add_test(tc, test_trie_replace);
+ tcase_add_test(tc, test_trie_replace_whole);
+ tcase_add_test(tc, test_trie_replace_inner);
+ tcase_add_test(tc, test_trie_replace_empty);
+ tcase_add_test(tc, test_trie_multi_replace);
+ tcase_add_test(tc, test_trie_remove);
+ tcase_add_test(tc, test_trie_find);
+ tcase_add_test(tc, test_trie_find_reset);
+ tcase_add_test(tc, test_trie_find_noreset);
+ tcase_add_test(tc, test_trie_multi_find);
+
+ suite_add_tcase(s, tc);
+
+ return s;
+}
diff --git a/libpurple/tests/test_util.c b/libpurple/tests/test_util.c
index 303a056117..d6bd04d952 100644
--- a/libpurple/tests/test_util.c
+++ b/libpurple/tests/test_util.c
@@ -109,7 +109,7 @@ const char *invalid_emails[] = {
"missingDot@com",
"two@@signs.com",
"colonButNoPort@127.0.0.1:",
- ""
+ "",
/* "someone-else@127.0.0.1.26", */
".localStartsWithDot@domain.com",
/* "localEndsWithDot.@domain.com", */ /* I don't think this is invalid -- Stu */
@@ -176,14 +176,179 @@ START_TEST(test_markup_html_to_xhtml)
{
gchar *xhtml = NULL;
gchar *plaintext = NULL;
+
purple_markup_html_to_xhtml("<a>", &xhtml, &plaintext);
assert_string_equal_free("<a href=\"\"></a>", xhtml);
assert_string_equal_free("", plaintext);
+ purple_markup_html_to_xhtml("<A href='URL'>ABOUT</a>", &xhtml, &plaintext);
+ assert_string_equal_free("<a href=\"URL\">ABOUT</a>", xhtml);
+ assert_string_equal_free("ABOUT <URL>", plaintext);
+
+ purple_markup_html_to_xhtml("<a href='URL'>URL</a>", &xhtml, &plaintext);
+ assert_string_equal_free("URL", plaintext);
+ assert_string_equal_free("<a href=\"URL\">URL</a>", xhtml);
+
+ purple_markup_html_to_xhtml("<a href='mailto:mail'>mail</a>", &xhtml, &plaintext);
+ assert_string_equal_free("mail", plaintext);
+ assert_string_equal_free("<a href=\"mailto:mail\">mail</a>", xhtml);
+
+ purple_markup_html_to_xhtml("<A href='\"U&apos;R&L'>ABOUT</a>", &xhtml, &plaintext);
+ assert_string_equal_free("<a href=\"&quot;U&apos;R&amp;L\">ABOUT</a>", xhtml);
+ assert_string_equal_free("ABOUT <\"U'R&L>", plaintext);
+
+ purple_markup_html_to_xhtml("<img src='SRC' alt='ALT'/>", &xhtml, &plaintext);
+ assert_string_equal_free("<img src='SRC' alt='ALT' />", xhtml);
+ assert_string_equal_free("ALT", plaintext);
+
+ purple_markup_html_to_xhtml("<img src=\"'S&apos;R&C\" alt=\"'A&apos;L&T\"/>", &xhtml, &plaintext);
+ assert_string_equal_free("<img src='&apos;S&apos;R&amp;C' alt='&apos;A&apos;L&amp;T' />", xhtml);
+ assert_string_equal_free("'A'L&T", plaintext);
+
+ purple_markup_html_to_xhtml("<unknown>", &xhtml, &plaintext);
+ assert_string_equal_free("&lt;unknown>", xhtml);
+ assert_string_equal_free("<unknown>", plaintext);
+
+ purple_markup_html_to_xhtml("&eacute;&amp;", &xhtml, &plaintext);
+ assert_string_equal_free("&eacute;&amp;", xhtml);
+ assert_string_equal_free("&eacute;&", plaintext);
+
+ purple_markup_html_to_xhtml("<h1>A<h2>B</h2>C</h1>", &xhtml, &plaintext);
+ assert_string_equal_free("<h1>A<h2>B</h2>C</h1>", xhtml);
+ assert_string_equal_free("ABC", plaintext);
+
+ purple_markup_html_to_xhtml("<h1><h2><h3><h4>", &xhtml, &plaintext);
+ assert_string_equal_free("<h1><h2><h3><h4></h4></h3></h2></h1>", xhtml);
+ assert_string_equal_free("", plaintext);
+
+ purple_markup_html_to_xhtml("<italic/>", &xhtml, &plaintext);
+ assert_string_equal_free("<em/>", xhtml);
+ assert_string_equal_free("", plaintext);
+
+ purple_markup_html_to_xhtml("</", &xhtml, &plaintext);
+ assert_string_equal_free("&lt;/", xhtml);
+ assert_string_equal_free("</", plaintext);
+
+ purple_markup_html_to_xhtml("</div>", &xhtml, &plaintext);
+ assert_string_equal_free("", xhtml);
+ assert_string_equal_free("", plaintext);
+
+ purple_markup_html_to_xhtml("<hr/>", &xhtml, &plaintext);
+ assert_string_equal_free("<br/>", xhtml);
+ assert_string_equal_free("\n", plaintext);
+
+ purple_markup_html_to_xhtml("<hr>", &xhtml, &plaintext);
+ assert_string_equal_free("<br/>", xhtml);
+ assert_string_equal_free("\n", plaintext);
+
+ purple_markup_html_to_xhtml("<br />", &xhtml, &plaintext);
+ assert_string_equal_free("<br/>", xhtml);
+ assert_string_equal_free("\n", plaintext);
+
+ purple_markup_html_to_xhtml("<br>INSIDE</br>", &xhtml, &plaintext);
+ assert_string_equal_free("<br/>INSIDE", xhtml);
+ assert_string_equal_free("\nINSIDE", plaintext);
+
+ purple_markup_html_to_xhtml("<div></div>", &xhtml, &plaintext);
+ assert_string_equal_free("<div></div>", xhtml);
+ assert_string_equal_free("", plaintext);
+
+ purple_markup_html_to_xhtml("<div/>", &xhtml, &plaintext);
+ assert_string_equal_free("<div/>", xhtml);
+ assert_string_equal_free("", plaintext);
+
+ purple_markup_html_to_xhtml("<div attr='\"&<>'/>", &xhtml, &plaintext);
+ assert_string_equal_free("<div attr='&quot;&amp;&lt;&gt;'/>", xhtml);
+ assert_string_equal_free("", plaintext);
+
+ purple_markup_html_to_xhtml("<div attr=\"'\"/>", &xhtml, &plaintext);
+ assert_string_equal_free("<div attr=\"&apos;\"/>", xhtml);
+ assert_string_equal_free("", plaintext);
+
+ purple_markup_html_to_xhtml("<div/> < <div/>", &xhtml, &plaintext);
+ assert_string_equal_free("<div/> &lt; <div/>", xhtml);
+ assert_string_equal_free(" < ", plaintext);
+
+ purple_markup_html_to_xhtml("<div>x</div>", &xhtml, &plaintext);
+ assert_string_equal_free("<div>x</div>", xhtml);
+ assert_string_equal_free("x", plaintext);
+
+ purple_markup_html_to_xhtml("<b>x</b>", &xhtml, &plaintext);
+ assert_string_equal_free("<span style='font-weight: bold;'>x</span>", xhtml);
+ assert_string_equal_free("x", plaintext);
+
+ purple_markup_html_to_xhtml("<bold>x</bold>", &xhtml, &plaintext);
+ assert_string_equal_free("<span style='font-weight: bold;'>x</span>", xhtml);
+ assert_string_equal_free("x", plaintext);
+
+ purple_markup_html_to_xhtml("<strong>x</strong>", &xhtml, &plaintext);
+ assert_string_equal_free("<span style='font-weight: bold;'>x</span>", xhtml);
+ assert_string_equal_free("x", plaintext);
+
+ purple_markup_html_to_xhtml("<u>x</u>", &xhtml, &plaintext);
+ assert_string_equal_free("<span style='text-decoration: underline;'>x</span>", xhtml);
+ assert_string_equal_free("x", plaintext);
+
+ purple_markup_html_to_xhtml("<underline>x</underline>", &xhtml, &plaintext);
+ assert_string_equal_free("<span style='text-decoration: underline;'>x</span>", xhtml);
+ assert_string_equal_free("x", plaintext);
+
+ purple_markup_html_to_xhtml("<s>x</s>", &xhtml, &plaintext);
+ assert_string_equal_free("<span style='text-decoration: line-through;'>x</span>", xhtml);
+ assert_string_equal_free("x", plaintext);
+
+ purple_markup_html_to_xhtml("<strike>x</strike>", &xhtml, &plaintext);
+ assert_string_equal_free("<span style='text-decoration: line-through;'>x</span>", xhtml);
+ assert_string_equal_free("x", plaintext);
+
+ purple_markup_html_to_xhtml("<sub>x</sub>", &xhtml, &plaintext);
+ assert_string_equal_free("<span style='vertical-align:sub;'>x</span>", xhtml);
+ assert_string_equal_free("x", plaintext);
+
+ purple_markup_html_to_xhtml("<sup>x</sup>", &xhtml, &plaintext);
+ assert_string_equal_free("<span style='vertical-align:super;'>x</span>", xhtml);
+ assert_string_equal_free("x", plaintext);
purple_markup_html_to_xhtml("<FONT>x</FONT>", &xhtml, &plaintext);
assert_string_equal_free("x", xhtml);
assert_string_equal_free("x", plaintext);
+
+ purple_markup_html_to_xhtml("<font face=\"'Times&gt;New & Roman'\">x</font>", &xhtml, &plaintext);
+ assert_string_equal_free("x", plaintext);
+ assert_string_equal_free("<span style='font-family: \"Times&gt;New &amp; Roman\";'>x</span>", xhtml);
+
+ purple_markup_html_to_xhtml("<font back=\"'color&gt;blue&red'\">x</font>", &xhtml, &plaintext);
+ assert_string_equal_free("x", plaintext);
+ assert_string_equal_free("<span style='background: \"color&gt;blue&amp;red\";'>x</span>", xhtml);
+
+ purple_markup_html_to_xhtml("<font color=\"'color&gt;blue&red'\">x</font>", &xhtml, &plaintext);
+ assert_string_equal_free("x", plaintext);
+ assert_string_equal_free("<span style='color: \"color&gt;blue&amp;red\";'>x</span>", xhtml);
+
+ purple_markup_html_to_xhtml("<font size=1>x</font>", &xhtml, &plaintext);
+ assert_string_equal_free("x", plaintext);
+ assert_string_equal_free("<span style='font-size: xx-small;'>x</span>", xhtml);
+
+ purple_markup_html_to_xhtml("<font size=432>x</font>", &xhtml, &plaintext);
+ assert_string_equal_free("x", plaintext);
+ assert_string_equal_free("<span style='font-size: medium;'>x</span>", xhtml);
+
+ /* The following tests document a behaviour that looks suspicious */
+
+ /* bug report https://developer.pidgin.im/ticket/13485 */
+ purple_markup_html_to_xhtml("<!--COMMENT-->", &xhtml, &plaintext);
+ assert_string_equal_free("<!--COMMENT-->", xhtml);
+ assert_string_equal_free("COMMENT-->", plaintext);
+
+ /* no bug report */
+ purple_markup_html_to_xhtml("<br />", &xhtml, &plaintext);
+ assert_string_equal_free("&lt;br />", xhtml);
+ assert_string_equal_free("<br />", plaintext);
+
+ /* same code section as <br /> */
+ purple_markup_html_to_xhtml("<hr />", &xhtml, &plaintext);
+ assert_string_equal_free("&lt;hr />", xhtml);
+ assert_string_equal_free("<hr />", plaintext);
}
END_TEST
diff --git a/libpurple/tests/test_xmlnode.c b/libpurple/tests/test_xmlnode.c
index 59f8d44338..67d373b480 100644
--- a/libpurple/tests/test_xmlnode.c
+++ b/libpurple/tests/test_xmlnode.c
@@ -16,18 +16,116 @@ START_TEST(test_xmlnode_billion_laughs_attack)
the parser for the above XML document */
/* purple_debug_set_enabled(TRUE); */
- fail_if(xmlnode_from_str(malicious_xml_doc, -1),
- "xmlnode_from_str() returned an XML tree, but we didn't want it to");
+ fail_if(purple_xmlnode_from_str(malicious_xml_doc, -1),
+ "purple_xmlnode_from_str() returned an XML tree, but we didn't want it to");
+}
+END_TEST
+
+#define check_doc_structure(x) { \
+ PurpleXmlNode *ping, *child1, *child2; \
+ fail_if(x == NULL, "Failed to parse document"); \
+ ping = purple_xmlnode_get_child(x, "ping"); \
+ fail_if(ping == NULL, "Failed to find 'ping' child"); \
+ child1 = purple_xmlnode_get_child(ping, "child1"); \
+ fail_if(child1 == NULL, "Failed to find 'child1'"); \
+ child2 = purple_xmlnode_get_child(child1, "child2"); \
+ fail_if(child2 == NULL, "Failed to find 'child2'"); \
+ purple_xmlnode_new_child(child2, "a"); \
+ \
+ assert_string_equal("jabber:client", purple_xmlnode_get_namespace(x)); \
+ /* NOTE: purple_xmlnode_get_namespace() returns the namespace of the element, not the
+ * current default namespace. See http://www.w3.org/TR/xml-names/#defaulting and
+ * http://www.w3.org/TR/xml-names/#dt-defaultNS.
+ */ \
+ assert_string_equal("urn:xmpp:ping", purple_xmlnode_get_namespace(ping)); \
+ assert_string_equal("jabber:client", purple_xmlnode_get_namespace(child1)); \
+ assert_string_equal("urn:xmpp:ping", purple_xmlnode_get_namespace(child2)); \
+ /*
+ * This fails (well, actually crashes [the ns is NULL]) unless
+ * purple_xmlnode_new_child() actually sets the element namespace.
+ assert_string_equal("jabber:client", purple_xmlnode_get_namespace(purple_xmlnode_get_child(child2, "a")));
+ */ \
+ \
+ assert_string_equal("jabber:client", purple_xmlnode_get_default_namespace(x)); \
+ assert_string_equal("jabber:client", purple_xmlnode_get_default_namespace(ping)); \
+ assert_string_equal("jabber:client", purple_xmlnode_get_default_namespace(child1)); \
+ assert_string_equal("jabber:client", purple_xmlnode_get_default_namespace(child2)); \
+}
+
+START_TEST(test_xmlnode_prefixes)
+{
+ const char *xml_doc =
+ "<iq type='get' xmlns='jabber:client' xmlns:ping='urn:xmpp:ping'>"
+ "<ping:ping>"
+ "<child1>"
+ "<ping:child2></ping:child2>" /* xmlns='jabber:child' */
+ "</child1>"
+ "</ping:ping>"
+ "</iq>";
+ char *str;
+ PurpleXmlNode *xml, *reparsed;
+
+ xml = purple_xmlnode_from_str(xml_doc, -1);
+ check_doc_structure(xml);
+
+ /* Check that purple_xmlnode_from_str(purple_xmlnode_to_str(xml, NULL), -1) is idempotent. */
+ str = purple_xmlnode_to_str(xml, NULL);
+ fail_if(str == NULL, "Failed to serialize XMLnode");
+ reparsed = purple_xmlnode_from_str(str, -1);
+ fail_if(reparsed == NULL, "Failed to reparse xml document");
+ check_doc_structure(reparsed);
+
+ g_free(str);
+ purple_xmlnode_free(xml);
+ purple_xmlnode_free(reparsed);
+}
+END_TEST
+
+
+START_TEST(test_strip_prefixes)
+{
+ const char *xml_doc = "<message xmlns='jabber:client' from='user@gmail.com/resource' to='another_user@darkrain42.org' type='chat' id='purple'>"
+ "<cha:active xmlns:cha='http://jabber.org/protocol/chatstates'/>"
+ "<body>xvlc xvlc</body>"
+ "<im:html xmlns:im='http://jabber.org/protocol/xhtml-im'>"
+ "<xht:body xmlns:xht='http://www.w3.org/1999/xhtml'>"
+ "<xht:p>xvlc <xht:span style='font-weight: bold;'>xvlc</xht:span></xht:p>"
+ "</xht:body>"
+ "</im:html>"
+ "</message>";
+ const char *out = "<message xmlns='jabber:client' from='user@gmail.com/resource' to='another_user@darkrain42.org' type='chat' id='purple'>"
+ "<active xmlns:cha='http://jabber.org/protocol/chatstates' xmlns='http://jabber.org/protocol/chatstates'/>"
+ "<body>xvlc xvlc</body>"
+ "<html xmlns:im='http://jabber.org/protocol/xhtml-im' xmlns='http://jabber.org/protocol/xhtml-im'>"
+ "<body xmlns:xht='http://www.w3.org/1999/xhtml' xmlns='http://www.w3.org/1999/xhtml'>"
+ "<p>xvlc <span style='font-weight: bold;'>xvlc</span></p>"
+ "</body>"
+ "</html>"
+ "</message>";
+ char *str;
+ PurpleXmlNode *xml;
+
+ xml = purple_xmlnode_from_str(xml_doc, -1);
+ fail_if(xml == NULL, "Failed to parse XML");
+
+ purple_xmlnode_strip_prefixes(xml);
+ str = purple_xmlnode_to_str(xml, NULL);
+ assert_string_equal_free(out, str);
+
+ purple_xmlnode_free(xml);
}
END_TEST
Suite *
-xmlnode_suite(void)
+purple_xmlnode_suite(void)
{
Suite *s = suite_create("Utility Functions");
TCase *tc = tcase_create("xmlnode");
tcase_add_test(tc, test_xmlnode_billion_laughs_attack);
+ tcase_add_test(tc, test_xmlnode_prefixes);
+ tcase_add_test(tc, test_strip_prefixes);
+
suite_add_tcase(s, tc);
return s;
diff --git a/libpurple/tests/test_yahoo_util.c b/libpurple/tests/test_yahoo_util.c
index 57a573d93a..ff498265d4 100644
--- a/libpurple/tests/test_yahoo_util.c
+++ b/libpurple/tests/test_yahoo_util.c
@@ -55,8 +55,8 @@ START_TEST(test_codes_to_html)
yahoo_codes_to_html("\x1B[1mbold \x1B[2mbolditalic \x1B[4mbolditalicunderline\x1B[x1m italicunderline"));
/* link */
- assert_string_equal_free("http://pidgin.im/",
- yahoo_codes_to_html("\x1B[lmhttp://pidgin.im/\x1B[xlm"));
+ assert_string_equal_free("https://pidgin.im/",
+ yahoo_codes_to_html("\x1B[lmhttps://pidgin.im/\x1B[xlm"));
#ifdef USE_CSS_FORMATTING
/* font color */
@@ -150,13 +150,13 @@ START_TEST(test_html_to_codes)
yahoo_html_to_codes("<b>bold <i>bolditalic</i></b><i> <u>italicunderline</u></i>"));
/* link */
- assert_string_equal_free("http://pidgin.im/",
- yahoo_html_to_codes("<A HREF=\"http://pidgin.im/\">http://pidgin.im/</A>"));
+ assert_string_equal_free("https://pidgin.im/",
+ yahoo_html_to_codes("<A HREF=\"https://pidgin.im/\">https://pidgin.im/</A>"));
assert_string_equal_free("mark@example.com",
yahoo_html_to_codes("<A HREF=\"mailto:mark@example.com\">mark@example.com</A>"));
#if 0
- assert_string_equal_free("Pidgin (http://pidgin.im/)",
- yahoo_html_to_codes("<A HREF=\"http://pidgin.im/\">Pidgin</A>"));
+ assert_string_equal_free("Pidgin (https://pidgin.im/)",
+ yahoo_html_to_codes("<A HREF=\"https://pidgin.im/\">Pidgin</A>"));
#endif
/* font nothing */
diff --git a/libpurple/tests/tests.h b/libpurple/tests/tests.h
index 76f9c34f8f..9b7c0bd486 100644
--- a/libpurple/tests/tests.h
+++ b/libpurple/tests/tests.h
@@ -16,7 +16,8 @@ Suite * jabber_scram_suite(void);
Suite * oscar_util_suite(void);
Suite * yahoo_util_suite(void);
Suite * util_suite(void);
-Suite * xmlnode_suite(void);
+Suite * purple_xmlnode_suite(void);
+Suite * purple_trie_suite(void);
/* helper macros */
#define assert_int_equal(expected, actual) { \
diff --git a/libpurple/theme-loader.c b/libpurple/theme-loader.c
index a77533b1e0..6f9dd9b1be 100644
--- a/libpurple/theme-loader.c
+++ b/libpurple/theme-loader.c
@@ -21,10 +21,11 @@
*/
#include "internal.h"
+#include "glibcompat.h"
#include "theme-loader.h"
#define PURPLE_THEME_LOADER_GET_PRIVATE(PurpleThemeLoader) \
- ((PurpleThemeLoaderPrivate *) ((PurpleThemeLoader)->priv))
+ (G_TYPE_INSTANCE_GET_PRIVATE((PurpleThemeLoader), PURPLE_TYPE_THEME_LOADER, PurpleThemeLoaderPrivate))
void purple_theme_loader_set_type_string(PurpleThemeLoader *loader, const gchar *type);
@@ -36,21 +37,23 @@ typedef struct {
} PurpleThemeLoaderPrivate;
/******************************************************************************
- * Globals
- *****************************************************************************/
-
-static GObjectClass *parent_class = NULL;
-
-/******************************************************************************
* Enums
*****************************************************************************/
enum {
PROP_ZERO = 0,
PROP_TYPE,
+ PROP_LAST
};
/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+static GObjectClass *parent_class = NULL;
+static GParamSpec *properties[PROP_LAST];
+
+/******************************************************************************
* GObject Stuff *
*****************************************************************************/
@@ -86,12 +89,19 @@ purple_theme_loader_set_property(GObject *obj, guint param_id, const GValue *val
}
}
-static void
-purple_theme_loader_init(GTypeInstance *instance,
- gpointer klass)
+static gboolean
+purple_theme_loader_probe_directory(PurpleThemeLoader *loader, const gchar *dir)
{
- PurpleThemeLoader *loader = PURPLE_THEME_LOADER(instance);
- loader->priv = g_new0(PurpleThemeLoaderPrivate, 1);
+ const gchar *type = purple_theme_loader_get_type_string(loader);
+ char *themedir;
+ gboolean result;
+
+ /* Checks for directory as $root/purple/$type */
+ themedir = g_build_filename(dir, "purple", type, NULL);
+ result = g_file_test(themedir, G_FILE_TEST_IS_DIR);
+ g_free(themedir);
+
+ return result;
}
static void
@@ -101,7 +111,6 @@ purple_theme_loader_finalize(GObject *obj)
PurpleThemeLoaderPrivate *priv = PURPLE_THEME_LOADER_GET_PRIVATE(loader);
g_free(priv->type);
- g_free(priv);
parent_class->finalize(obj);
}
@@ -110,20 +119,23 @@ static void
purple_theme_loader_class_init(PurpleThemeLoaderClass *klass)
{
GObjectClass *obj_class = G_OBJECT_CLASS(klass);
- GParamSpec *pspec;
parent_class = g_type_class_peek_parent(klass);
+ g_type_class_add_private(klass, sizeof(PurpleThemeLoaderPrivate));
+
obj_class->get_property = purple_theme_loader_get_property;
obj_class->set_property = purple_theme_loader_set_property;
obj_class->finalize = purple_theme_loader_finalize;
/* TYPE STRING (read only) */
- pspec = g_param_spec_string("type", "Type",
+ properties[PROP_TYPE] = g_param_spec_string("type", "Type",
"The string representing the type of the theme",
NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
- g_object_class_install_property(obj_class, PROP_TYPE, pspec);
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, PROP_LAST, properties);
}
GType
@@ -140,7 +152,7 @@ purple_theme_loader_get_type(void)
NULL, /* class_data */
sizeof(PurpleThemeLoader),
0, /* n_preallocs */
- purple_theme_loader_init, /* instance_init */
+ NULL, /* instance_init */
NULL, /* value table */
};
type = g_type_register_static(G_TYPE_OBJECT,
@@ -176,6 +188,8 @@ purple_theme_loader_set_type_string(PurpleThemeLoader *loader, const gchar *type
g_free(priv->type);
priv->type = g_strdup(type);
+
+ g_object_notify_by_pspec(G_OBJECT(loader), properties[PROP_TYPE]);
}
PurpleTheme *
@@ -183,3 +197,13 @@ purple_theme_loader_build(PurpleThemeLoader *loader, const gchar *dir)
{
return PURPLE_THEME_LOADER_GET_CLASS(loader)->purple_theme_loader_build(dir);
}
+
+gboolean
+purple_theme_loader_probe(PurpleThemeLoader *loader, const gchar *dir)
+{
+ if (PURPLE_THEME_LOADER_GET_CLASS(loader)->probe_directory != NULL)
+ return PURPLE_THEME_LOADER_GET_CLASS(loader)->probe_directory(dir);
+ else
+ return purple_theme_loader_probe_directory(loader, dir);
+}
+
diff --git a/libpurple/theme-loader.h b/libpurple/theme-loader.h
index bf76b38920..eb5640fc4a 100644
--- a/libpurple/theme-loader.h
+++ b/libpurple/theme-loader.h
@@ -1,7 +1,3 @@
-/**
- * @file theme-loader.h Purple Theme Loader Abstact Class API
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -25,18 +21,17 @@
#ifndef PURPLE_THEME_LOADER_H
#define PURPLE_THEME_LOADER_H
+/**
+ * SECTION:theme-loader
+ * @section_id: libpurple-theme-loader
+ * @short_description: <filename>theme-loader.h</filename>
+ * @title: Theme Loader Abstact Class
+ */
#include <glib.h>
#include <glib-object.h>
#include "theme.h"
-/**
- * A purple theme loader.
- * This is an abstract class for Purple to use with the Purple theme manager.
- * The loader is responsible for building each type of theme
- *
- * PurpleThemeLoader is a GObject.
- */
typedef struct _PurpleThemeLoader PurpleThemeLoader;
typedef struct _PurpleThemeLoaderClass PurpleThemeLoaderClass;
@@ -47,47 +42,80 @@ typedef struct _PurpleThemeLoaderClass PurpleThemeLoaderClass;
#define PURPLE_IS_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_THEME_LOADER))
#define PURPLE_THEME_LOADER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_THEME_LOADER, PurpleThemeLoaderClass))
+/**
+ * PurpleThemeLoader:
+ *
+ * A purple theme loader.
+ * This is an abstract class for Purple to use with the Purple theme manager.
+ * The loader is responsible for building each type of theme
+ */
struct _PurpleThemeLoader
{
GObject parent;
- gpointer priv;
};
struct _PurpleThemeLoaderClass
{
GObjectClass parent_class;
- PurpleTheme *((*purple_theme_loader_build)(const gchar*));
+
+ PurpleTheme *(*purple_theme_loader_build)(const gchar*);
+ gboolean (*probe_directory)(const gchar *);
+
+ /*< private >*/
+ void (*purple_reserved1)(void);
+ void (*purple_reserved2)(void);
+ void (*purple_reserved3)(void);
+ void (*purple_reserved4)(void);
};
/**************************************************************************/
-/** @name Purple Theme-Loader API */
+/* Purple Theme-Loader API */
/**************************************************************************/
G_BEGIN_DECLS
/**
- * GObject foo.
- * @internal.
+ * purple_theme_loader_get_type:
+ *
+ * Returns: The #GType for theme loader.
*/
GType purple_theme_loader_get_type(void);
/**
- * Returns the string representing the type of the theme loader
+ * purple_theme_loader_get_type_string:
+ * @self: The theme loader
*
- * @param self The theme loader
+ * Returns the string representing the type of the theme loader
*
- * @returns The string representing this type
+ * Returns: The string representing this type
*/
const gchar *purple_theme_loader_get_type_string(PurpleThemeLoader *self);
/**
- * Creates a new PurpleTheme
+ * purple_theme_loader_build:
+ * @loader: The theme loader
+ * @dir: The directory containing the theme
*
- * @param loader The theme loader
- * @param dir The directory containing the theme
+ * Creates a new PurpleTheme
*
- * @returns A PurpleTheme containing the information from the directory
+ * Returns: A PurpleTheme containing the information from the directory
*/
PurpleTheme *purple_theme_loader_build(PurpleThemeLoader *loader, const gchar *dir);
+/**
+ * purple_theme_loader_probe:
+ * @loader: The theme loader
+ * @dir: The directory that may contain the theme
+ *
+ * Probes a directory to see if it might possibly contain a theme
+ *
+ * This function might only check for obvious files or directory structure.
+ * Loading of a theme may fail for other reasons.
+ * The default prober checks for $dir/purple/$type.
+ *
+ * Returns: TRUE if the directory appears to contain a theme, FALSE otherwise.
+ */
+gboolean purple_theme_loader_probe(PurpleThemeLoader *loader, const gchar *dir);
+
G_END_DECLS
+
#endif /* PURPLE_THEME_LOADER_H */
diff --git a/libpurple/theme-manager.c b/libpurple/theme-manager.c
index 7b8e3506f8..db4f987294 100644
--- a/libpurple/theme-manager.c
+++ b/libpurple/theme-manager.c
@@ -80,28 +80,32 @@ purple_theme_manager_is_theme_type(gchar *key,
}
static gboolean
-purple_theme_manager_is_theme(gchar *key,
- gpointer value,
- gchar *user_data)
+check_if_theme_or_loader(gchar *key, gpointer value, GSList **loaders)
{
- return PURPLE_IS_THEME(value);
+ if (PURPLE_IS_THEME(value))
+ return TRUE;
+ else if (PURPLE_IS_THEME_LOADER(value))
+ *loaders = g_slist_prepend(*loaders, value);
+
+ return FALSE;
}
static void
purple_theme_manager_function_wrapper(gchar *key,
gpointer value,
- PTFunc user_data)
+ PurpleThemeFunc user_data)
{
if (PURPLE_IS_THEME(value))
(* user_data)(value);
}
static void
-purple_theme_manager_build_dir(const gchar *root)
+purple_theme_manager_build_dir(GSList *loaders, const gchar *root)
{
- gchar *purple_dir, *theme_dir;
- const gchar *name = NULL, *type = NULL;
- GDir *rdir, *tdir;
+ gchar *theme_dir;
+ const gchar *name;
+ GDir *rdir;
+ GSList *tmp;
PurpleThemeLoader *loader;
rdir = g_dir_open(root, 0, NULL);
@@ -109,33 +113,20 @@ purple_theme_manager_build_dir(const gchar *root)
if (!rdir)
return;
- /* Parses directory by root/name/purple/type */
while ((name = g_dir_read_name(rdir))) {
- purple_dir = g_build_filename(root, name, "purple", NULL);
- tdir = g_dir_open(purple_dir, 0, NULL);
-
- if (!tdir) {
- g_free(purple_dir);
+ theme_dir = g_build_filename(root, name, NULL);
- continue;
- }
-
- while ((type = g_dir_read_name(tdir))) {
- if ((loader = g_hash_table_lookup(theme_table, type))) {
- PurpleTheme *theme = NULL;
-
- theme_dir = g_build_filename(purple_dir, type, NULL);
-
- theme = purple_theme_loader_build(loader, theme_dir);
- g_free(theme_dir);
+ for (tmp = loaders; tmp; tmp = g_slist_next(tmp)) {
+ loader = PURPLE_THEME_LOADER(tmp->data);
+ if (purple_theme_loader_probe(loader, theme_dir)) {
+ PurpleTheme *theme = purple_theme_loader_build(loader, theme_dir);
if (PURPLE_IS_THEME(theme))
purple_theme_manager_add_theme(theme);
}
}
- g_dir_close(tdir);
- g_free(purple_dir);
+ g_free(theme_dir);
}
g_dir_close(rdir);
@@ -155,16 +146,17 @@ purple_theme_manager_init(void)
void
purple_theme_manager_refresh(void)
{
- gchar *path = NULL;
- const gchar *xdg = NULL;
- gint i = 0;
+ gchar *path;
+ const gchar *xdg;
+ gint i;
+ GSList *loaders = NULL;
- g_hash_table_foreach_remove(theme_table,
- (GHRFunc) purple_theme_manager_is_theme, NULL);
+ g_hash_table_foreach_remove(theme_table, (GHRFunc)check_if_theme_or_loader,
+ &loaders);
/* Add themes from ~/.purple */
path = g_build_filename(purple_user_dir(), "themes", NULL);
- purple_theme_manager_build_dir(path);
+ purple_theme_manager_build_dir(loaders, path);
g_free(path);
/* look for XDG_DATA_HOME. If we don't have it use ~/.local, and add it */
@@ -173,7 +165,7 @@ purple_theme_manager_refresh(void)
else
path = g_build_filename(purple_home_dir(), ".local", "themes", NULL);
- purple_theme_manager_build_dir(path);
+ purple_theme_manager_build_dir(loaders, path);
g_free(path);
/* now dig through XDG_DATA_DIRS and add those too */
@@ -183,12 +175,14 @@ purple_theme_manager_refresh(void)
for (i = 0; xdg_dirs[i]; i++) {
path = g_build_filename(xdg_dirs[i], "themes", NULL);
- purple_theme_manager_build_dir(path);
+ purple_theme_manager_build_dir(loaders, path);
g_free(path);
}
g_strfreev(xdg_dirs);
}
+
+ g_slist_free(loaders);
}
void
@@ -284,7 +278,7 @@ purple_theme_manager_remove_theme(PurpleTheme *theme)
}
void
-purple_theme_manager_for_each_theme(PTFunc func)
+purple_theme_manager_for_each_theme(PurpleThemeFunc func)
{
g_return_if_fail(func);
diff --git a/libpurple/theme-manager.h b/libpurple/theme-manager.h
index f7273394c0..2b05abcc20 100644
--- a/libpurple/theme-manager.h
+++ b/libpurple/theme-manager.h
@@ -1,7 +1,3 @@
-/**
- * @file theme-manager.h Theme Manager API
- */
-
/*
* purple
*
@@ -26,13 +22,19 @@
#ifndef PURPLE_THEME_MANAGER_H
#define PURPLE_THEME_MANAGER_H
+/**
+ * SECTION:theme-manager
+ * @section_id: libpurple-theme-manager
+ * @short_description: <filename>theme-manager.h</filename>
+ * @title: Theme Manager API
+ */
#include <glib-object.h>
#include <glib.h>
#include "theme.h"
#include "theme-loader.h"
-typedef void (*PTFunc) (PurpleTheme *);
+typedef void (*PurpleThemeFunc) (PurpleTheme *);
typedef struct _PurpleThemeManager PurpleThemeManager;
typedef struct _PurpleThemeManagerClass PurpleThemeManagerClass;
@@ -50,90 +52,110 @@ struct _PurpleThemeManager {
struct _PurpleThemeManagerClass {
GObjectClass parent_class;
+
+ /*< private >*/
+ void (*purple_reserved1)(void);
+ void (*purple_reserved2)(void);
+ void (*purple_reserved3)(void);
+ void (*purple_reserved4)(void);
};
/**************************************************************************/
-/** @name Purple Theme Manager API */
+/* Purple Theme Manager API */
/**************************************************************************/
G_BEGIN_DECLS
/**
- * GObject foo.
+ * purple_theme_manager_get_type:
*
- * @internal.
+ * Returns: The #GType for theme manager.
*/
GType purple_theme_manager_get_type(void);
/**
+ * purple_theme_manager_init:
+ *
* Initalizes the theme manager.
*/
void purple_theme_manager_init(void);
/**
+ * purple_theme_manager_uninit:
+ *
* Uninitalizes the manager then frees all the themes and loaders it is
* responsible for.
*/
void purple_theme_manager_uninit(void);
/**
+ * purple_theme_manager_refresh:
+ *
* Rebuilds all the themes in the theme manager.
* (Removes all current themes but keeps the added loaders.)
*/
void purple_theme_manager_refresh(void);
/**
- * Finds the PurpleTheme object stored by the theme manager.
+ * purple_theme_manager_find_theme:
+ * @name: The name of the PurpleTheme.
+ * @type: The type of the PurpleTheme.
*
- * @param name The name of the PurpleTheme.
- * @param type The type of the PurpleTheme.
+ * Finds the PurpleTheme object stored by the theme manager.
*
- * @returns The PurpleTheme, or NULL if it wasn't found.
+ * Returns: The PurpleTheme, or NULL if it wasn't found.
*/
PurpleTheme *purple_theme_manager_find_theme(const gchar *name, const gchar *type);
/**
+ * purple_theme_manager_add_theme:
+ * @theme: The PurpleTheme to add to the manager.
+ *
* Adds a PurpleTheme to the theme manager. If the theme already exists
* then this function does nothing.
- *
- * @param theme The PurpleTheme to add to the manager.
*/
void purple_theme_manager_add_theme(PurpleTheme *theme);
/**
- * Removes a PurpleTheme from the theme manager and frees the theme.
+ * purple_theme_manager_remove_theme:
+ * @theme: The PurpleTheme to remove from the manager.
*
- * @param theme The PurpleTheme to remove from the manager.
+ * Removes a PurpleTheme from the theme manager and frees the theme.
*/
void purple_theme_manager_remove_theme(PurpleTheme *theme);
/**
- * Adds a loader to the theme manager so it knows how to build themes.
+ * purple_theme_manager_register_type:
+ * @loader: The PurpleThemeLoader to add.
*
- * @param loader The PurpleThemeLoader to add.
+ * Adds a loader to the theme manager so it knows how to build themes.
*/
void purple_theme_manager_register_type(PurpleThemeLoader *loader);
/**
- * Removes the loader and all themes of the same type from the loader.
+ * purple_theme_manager_unregister_type:
+ * @loader: The PurpleThemeLoader to be removed.
*
- * @param loader The PurpleThemeLoader to be removed.
+ * Removes the loader and all themes of the same type from the loader.
*/
void purple_theme_manager_unregister_type(PurpleThemeLoader *loader);
/**
- * Calls the given function on each purple theme.
+ * purple_theme_manager_for_each_theme:
+ * @func: The PurpleThemeFunc to be applied to each theme.
*
- * @param func The PTFunc to be applied to each theme.
+ * Calls the given function on each purple theme.
*/
-void purple_theme_manager_for_each_theme(PTFunc func);
+void purple_theme_manager_for_each_theme(PurpleThemeFunc func);
/**
- * Loads a theme of the given type without adding it to the manager
+ * purple_theme_manager_load_theme:
+ * @theme_dir: the directory of the theme to load
+ * @type: the type of theme to load
*
- * @param theme_dir the directory of the theme to load
- * @param type the type of theme to load
+ * Loads a theme of the given type without adding it to the manager
*/
PurpleTheme *purple_theme_manager_load_theme(const gchar *theme_dir, const gchar *type);
G_END_DECLS
+
#endif /* PURPLE_THEME_MANAGER_H */
diff --git a/libpurple/theme.c b/libpurple/theme.c
index c21e71a053..bcf39c4844 100644
--- a/libpurple/theme.c
+++ b/libpurple/theme.c
@@ -21,11 +21,12 @@
*/
#include "internal.h"
+#include "glibcompat.h"
#include "theme.h"
#include "util.h"
#define PURPLE_THEME_GET_PRIVATE(PurpleTheme) \
- ((PurpleThemePrivate *) ((PurpleTheme)->priv))
+ (G_TYPE_INSTANCE_GET_PRIVATE((PurpleTheme), PURPLE_TYPE_THEME, PurpleThemePrivate))
void purple_theme_set_type_string(PurpleTheme *theme, const gchar *type);
@@ -43,12 +44,6 @@ typedef struct {
} PurpleThemePrivate;
/******************************************************************************
- * Globals
- *****************************************************************************/
-
-static GObjectClass *parent_class = NULL;
-
-/******************************************************************************
* Enums
*****************************************************************************/
@@ -59,10 +54,18 @@ enum {
PROP_AUTHOR,
PROP_TYPE,
PROP_DIR,
- PROP_IMAGE
+ PROP_IMAGE,
+ PROP_LAST
};
/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+static GObjectClass *parent_class = NULL;
+static GParamSpec *properties[PROP_LAST];
+
+/******************************************************************************
* GObject Stuff
*****************************************************************************/
@@ -129,14 +132,6 @@ purple_theme_set_property(GObject *obj, guint param_id, const GValue *value,
}
static void
-purple_theme_init(GTypeInstance *instance,
- gpointer klass)
-{
- PurpleTheme *theme = PURPLE_THEME(instance);
- theme->priv = g_new0(PurpleThemePrivate, 1);
-}
-
-static void
purple_theme_finalize(GObject *obj)
{
PurpleTheme *theme = PURPLE_THEME(obj);
@@ -156,55 +151,54 @@ static void
purple_theme_class_init(PurpleThemeClass *klass)
{
GObjectClass *obj_class = G_OBJECT_CLASS(klass);
- GParamSpec *pspec;
parent_class = g_type_class_peek_parent(klass);
+ g_type_class_add_private(klass, sizeof(PurpleThemePrivate));
+
obj_class->get_property = purple_theme_get_property;
obj_class->set_property = purple_theme_set_property;
obj_class->finalize = purple_theme_finalize;
/* NAME */
- pspec = g_param_spec_string("name", "Name",
+ properties[PROP_NAME] = g_param_spec_string("name", "Name",
"The name of the theme",
NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
- g_object_class_install_property(obj_class, PROP_NAME, pspec);
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
/* DESCRIPTION */
- pspec = g_param_spec_string("description", "Description",
+ properties[PROP_DESCRIPTION] = g_param_spec_string("description",
+ "Description",
"The description of the theme",
NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
- g_object_class_install_property(obj_class, PROP_DESCRIPTION, pspec);
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
/* AUTHOR */
- pspec = g_param_spec_string("author", "Author",
+ properties[PROP_AUTHOR] = g_param_spec_string("author", "Author",
"The author of the theme",
NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
- g_object_class_install_property(obj_class, PROP_AUTHOR, pspec);
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
/* TYPE STRING (read only) */
- pspec = g_param_spec_string("type", "Type",
+ properties[PROP_TYPE] = g_param_spec_string("type", "Type",
"The string representing the type of the theme",
NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
- g_object_class_install_property(obj_class, PROP_TYPE, pspec);
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
/* DIRECTORY */
- pspec = g_param_spec_string("directory", "Directory",
+ properties[PROP_DIR] = g_param_spec_string("directory", "Directory",
"The directory that contains the theme and all its files",
NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
- g_object_class_install_property(obj_class, PROP_DIR, pspec);
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
/* PREVIEW IMAGE */
- pspec = g_param_spec_string("image", "Image",
+ properties[PROP_IMAGE] = g_param_spec_string("image", "Image",
"A preview image of the theme",
NULL,
- G_PARAM_READWRITE);
- g_object_class_install_property(obj_class, PROP_IMAGE, pspec);
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, PROP_LAST, properties);
}
@@ -222,7 +216,7 @@ purple_theme_get_type(void)
NULL, /* class_data */
sizeof(PurpleTheme),
0, /* n_preallocs */
- purple_theme_init, /* instance_init */
+ NULL, /* instance_init */
NULL, /* value table */
};
type = g_type_register_static (G_TYPE_OBJECT,
@@ -273,6 +267,8 @@ purple_theme_set_name(PurpleTheme *theme, const gchar *name)
g_free(priv->name);
priv->name = theme_clean_text(name);
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_NAME]);
}
const gchar *
@@ -297,6 +293,8 @@ purple_theme_set_description(PurpleTheme *theme, const gchar *description)
g_free(priv->description);
priv->description = theme_clean_text(description);
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_DESCRIPTION]);
}
const gchar *
@@ -321,6 +319,8 @@ purple_theme_set_author(PurpleTheme *theme, const gchar *author)
g_free(priv->author);
priv->author = theme_clean_text(author);
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_AUTHOR]);
}
const gchar *
@@ -346,6 +346,8 @@ purple_theme_set_type_string(PurpleTheme *theme, const gchar *type)
g_free(priv->type);
priv->type = g_strdup(type);
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_TYPE]);
}
const gchar *
@@ -370,6 +372,8 @@ purple_theme_set_dir(PurpleTheme *theme, const gchar *dir)
g_free(priv->dir);
priv->dir = g_strdup(dir);
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_DIR]);
}
const gchar *
@@ -406,4 +410,6 @@ purple_theme_set_image(PurpleTheme *theme, const gchar *img)
g_free(priv->img);
priv->img = g_strdup(img);
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_IMAGE]);
}
diff --git a/libpurple/theme.h b/libpurple/theme.h
index e6ffa130e5..18b8e607f9 100644
--- a/libpurple/theme.h
+++ b/libpurple/theme.h
@@ -1,7 +1,3 @@
-/**
- * @file theme.h Purple Theme Abstact Class API
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -25,17 +21,16 @@
#ifndef PURPLE_THEME_H
#define PURPLE_THEME_H
+/**
+ * SECTION:theme
+ * @section_id: libpurple-theme
+ * @short_description: <filename>theme.h</filename>
+ * @title: Theme Abstact Class
+ */
#include <glib.h>
#include <glib-object.h>
-#include "imgstore.h"
-/**
- * A purple theme.
- * This is an abstract class for Purple to use with the Purple theme manager.
- *
- * PurpleTheme is a GObject.
- */
typedef struct _PurpleTheme PurpleTheme;
typedef struct _PurpleThemeClass PurpleThemeClass;
@@ -46,130 +41,155 @@ typedef struct _PurpleThemeClass PurpleThemeClass;
#define PURPLE_IS_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PURPLE_TYPE_THEME))
#define PURPLE_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PURPLE_TYPE_THEME, PurpleThemeClass))
+/**
+ * PurpleTheme:
+ *
+ * A purple theme.
+ * This is an abstract class for Purple to use with the Purple theme manager.
+ */
struct _PurpleTheme
{
GObject parent;
- gpointer priv;
};
struct _PurpleThemeClass
{
GObjectClass parent_class;
+
+ /*< private >*/
+ void (*purple_reserved1)(void);
+ void (*purple_reserved2)(void);
+ void (*purple_reserved3)(void);
+ void (*purple_reserved4)(void);
};
/**************************************************************************/
-/** @name Purple Theme API */
+/* Purple Theme API */
/**************************************************************************/
G_BEGIN_DECLS
/**
- * GObject foo.
- * @internal.
+ * purple_theme_get_type:
+ *
+ * Returns: The #GType for a theme.
*/
GType purple_theme_get_type(void);
/**
- * Returns the name of the PurpleTheme object.
+ * purple_theme_get_name:
+ * @theme: The purple theme.
*
- * @param theme The purple theme.
+ * Returns the name of the PurpleTheme object.
*
- * @return The string representing the name of the theme.
+ * Returns: The string representing the name of the theme.
*/
const gchar *purple_theme_get_name(PurpleTheme *theme);
/**
- * Sets the name of the PurpleTheme object.
+ * purple_theme_set_name:
+ * @theme: The purple theme.
+ * @name: The name of the PurpleTheme object.
*
- * @param theme The purple theme.
- * @param name The name of the PurpleTheme object.
+ * Sets the name of the PurpleTheme object.
*/
void purple_theme_set_name(PurpleTheme *theme, const gchar *name);
/**
- * Returns the description of the PurpleTheme object.
+ * purple_theme_get_description:
+ * @theme: The purple theme.
*
- * @param theme The purple theme.
+ * Returns the description of the PurpleTheme object.
*
- * @return A short description of the theme.
+ * Returns: A short description of the theme.
*/
const gchar *purple_theme_get_description(PurpleTheme *theme);
/**
- * Sets the description of the PurpleTheme object.
+ * purple_theme_set_description:
+ * @theme: The purple theme.
+ * @description: The description of the PurpleTheme object.
*
- * @param theme The purple theme.
- * @param description The description of the PurpleTheme object.
+ * Sets the description of the PurpleTheme object.
*/
void purple_theme_set_description(PurpleTheme *theme, const gchar *description);
/**
- * Returns the author of the PurpleTheme object.
+ * purple_theme_get_author:
+ * @theme: The purple theme.
*
- * @param theme The purple theme.
+ * Returns the author of the PurpleTheme object.
*
- * @return The author of the theme.
+ * Returns: The author of the theme.
*/
const gchar *purple_theme_get_author(PurpleTheme *theme);
/**
- * Sets the author of the PurpleTheme object.
+ * purple_theme_set_author:
+ * @theme: The purple theme.
+ * @author: The author of the PurpleTheme object.
*
- * @param theme The purple theme.
- * @param author The author of the PurpleTheme object.
+ * Sets the author of the PurpleTheme object.
*/
void purple_theme_set_author(PurpleTheme *theme, const gchar *author);
/**
- * Returns the type (string) of the PurpleTheme object.
+ * purple_theme_get_type_string:
+ * @theme: The purple theme.
*
- * @param theme The purple theme.
+ * Returns the type (string) of the PurpleTheme object.
*
- * @return The string representing the type.
+ * Returns: The string representing the type.
*/
const gchar *purple_theme_get_type_string(PurpleTheme *theme);
/**
- * Returns the directory of the PurpleTheme object.
+ * purple_theme_get_dir:
+ * @theme: The purple theme.
*
- * @param theme The purple theme.
+ * Returns the directory of the PurpleTheme object.
*
- * @return The string representing the theme directory.
+ * Returns: The string representing the theme directory.
*/
const gchar *purple_theme_get_dir(PurpleTheme *theme);
/**
- * Sets the directory of the PurpleTheme object.
+ * purple_theme_set_dir:
+ * @theme: The purple theme.
+ * @dir: The directory of the PurpleTheme object.
*
- * @param theme The purple theme.
- * @param dir The directory of the PurpleTheme object.
+ * Sets the directory of the PurpleTheme object.
*/
void purple_theme_set_dir(PurpleTheme *theme, const gchar *dir);
/**
- * Returns the image preview of the PurpleTheme object.
+ * purple_theme_get_image:
+ * @theme: The purple theme.
*
- * @param theme The purple theme.
+ * Returns the image preview of the PurpleTheme object.
*
- * @return The image preview of the PurpleTheme object.
+ * Returns: The image preview of the PurpleTheme object.
*/
const gchar *purple_theme_get_image(PurpleTheme *theme);
/**
- * Returns the image preview and directory of the PurpleTheme object.
+ * purple_theme_get_image_full:
+ * @theme: The purple theme.
*
- * @param theme The purple theme.
+ * Returns the image preview and directory of the PurpleTheme object.
*
- * @return The image preview of the PurpleTheme object.
+ * Returns: The image preview of the PurpleTheme object.
*/
gchar *purple_theme_get_image_full(PurpleTheme *theme);
/**
- * Sets the directory of the PurpleTheme object.
+ * purple_theme_set_image:
+ * @theme: The purple theme.
+ * @img: The image preview of the PurpleTheme object.
*
- * @param theme The purple theme.
- * @param img The image preview of the PurpleTheme object.
+ * Sets the directory of the PurpleTheme object.
*/
void purple_theme_set_image(PurpleTheme *theme, const gchar *img);
G_END_DECLS
+
#endif /* PURPLE_THEME_H */
diff --git a/libpurple/trie.c b/libpurple/trie.c
new file mode 100644
index 0000000000..7be15afd1b
--- /dev/null
+++ b/libpurple/trie.c
@@ -0,0 +1,907 @@
+/*
+ * 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 "trie.h"
+
+#include <string.h>
+
+#include "debug.h"
+#include "memorypool.h"
+
+#define PURPLE_TRIE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_TRIE, PurpleTriePrivate))
+
+/* A single internal (that don't have any children) consists
+ * of 256 + 4 pointers. That's 1040 bytes on 32-bit machine or 2080 bytes
+ * on 64-bit.
+ *
+ * Thus, in 10500-byte pool block we can hold about 5-10 internal states.
+ * Threshold of 100 states means, we'd need 10-20 "small" blocks before
+ * switching to ~1-2 large blocks.
+ */
+#define PURPLE_TRIE_LARGE_THRESHOLD 100
+#define PURPLE_TRIE_STATES_SMALL_POOL_BLOCK_SIZE 10880
+#define PURPLE_TRIE_STATES_LARGE_POOL_BLOCK_SIZE 102400
+
+typedef struct _PurpleTrieRecord PurpleTrieRecord;
+typedef struct _PurpleTrieState PurpleTrieState;
+typedef struct _PurpleTrieRecordList PurpleTrieRecordList;
+
+typedef struct
+{
+ gboolean reset_on_match;
+
+ PurpleMemoryPool *records_str_mempool;
+ PurpleMemoryPool *records_obj_mempool;
+ PurpleTrieRecordList *records;
+ GHashTable *records_map;
+ gsize records_total_size;
+
+ PurpleMemoryPool *states_mempool;
+ PurpleTrieState *root_state;
+} PurpleTriePrivate;
+
+struct _PurpleTrieRecord
+{
+ gchar *word;
+ guint word_len;
+ gpointer data;
+};
+
+struct _PurpleTrieRecordList
+{
+ PurpleTrieRecord *rec;
+ PurpleTrieRecordList *next;
+ PurpleTrieRecordList *prev;
+
+ gpointer extra_data;
+};
+
+struct _PurpleTrieState
+{
+ PurpleTrieState *parent;
+ PurpleTrieState **children;
+
+ PurpleTrieState *longest_suffix;
+
+ PurpleTrieRecord *found_word;
+};
+
+typedef struct
+{
+ PurpleTrieState *state;
+
+ PurpleTrieState *root_state;
+ gboolean reset_on_match;
+
+ PurpleTrieReplaceCb replace_cb;
+ PurpleTrieFindCb find_cb;
+ gpointer user_data;
+} PurpleTrieMachine;
+
+/* TODO: an option to make it eager or lazy (now, it's eager) */
+enum
+{
+ PROP_ZERO,
+ PROP_RESET_ON_MATCH,
+ PROP_LAST
+};
+
+static GObjectClass *parent_class = NULL;
+static GParamSpec *properties[PROP_LAST];
+
+
+/*******************************************************************************
+ * Records list
+ ******************************************************************************/
+
+static PurpleTrieRecordList *
+purple_record_list_new(PurpleMemoryPool *mpool, PurpleTrieRecord *rec)
+{
+ PurpleTrieRecordList *node;
+
+ node = purple_memory_pool_alloc0(mpool,
+ sizeof(PurpleTrieRecordList), sizeof(gpointer));
+ g_return_val_if_fail(node != NULL, NULL);
+
+ node->rec = rec;
+
+ return node;
+}
+
+static PurpleTrieRecordList *
+purple_record_list_prepend(PurpleMemoryPool *mpool,
+ PurpleTrieRecordList *old_head, PurpleTrieRecord *rec)
+{
+ PurpleTrieRecordList *new_head;
+
+ new_head = purple_record_list_new(mpool, rec);
+ g_return_val_if_fail(new_head != NULL, NULL);
+
+ new_head->next = old_head;
+ if (old_head)
+ old_head->prev = new_head;
+
+ return new_head;
+}
+
+static PurpleTrieRecordList *
+purple_record_list_copy(PurpleMemoryPool *mpool,
+ const PurpleTrieRecordList *head)
+{
+ PurpleTrieRecordList *new_head = NULL, *new_tail = NULL;
+
+ while (head) {
+ PurpleTrieRecordList *node;
+
+ node = purple_record_list_new(mpool, head->rec);
+ g_return_val_if_fail(node != NULL, NULL); /* there is no leak */
+
+ node->prev = new_tail;
+ if (new_tail)
+ new_tail->next = node;
+ new_tail = node;
+ if (!new_head)
+ new_head = node;
+
+ head = head->next;
+ }
+
+ return new_head;
+}
+
+static PurpleTrieRecordList *
+purple_record_list_remove(PurpleTrieRecordList *head,
+ PurpleTrieRecordList *node)
+{
+ g_return_val_if_fail(head != NULL, NULL);
+ g_return_val_if_fail(node != NULL, head);
+ g_return_val_if_fail(head->prev == NULL, NULL);
+
+ if (head == node) {
+ if (head->next != NULL)
+ head->next->prev = NULL;
+ return head->next;
+ } else {
+ g_return_val_if_fail(node->prev != NULL, NULL);
+ node->prev->next = node->next;
+ if (node->next != NULL)
+ node->next->prev = node->prev;
+ return head;
+ }
+}
+
+
+/*******************************************************************************
+ * States management
+ ******************************************************************************/
+
+static void
+purple_trie_states_cleanup(PurpleTrie *trie)
+{
+ PurpleTriePrivate *priv = PURPLE_TRIE_GET_PRIVATE(trie);
+
+ g_return_if_fail(priv != NULL);
+
+ if (priv->root_state != NULL) {
+ purple_memory_pool_cleanup(priv->states_mempool);
+ priv->root_state = NULL;
+ }
+}
+
+/* Allocates a state and binds it to the parent. */
+static PurpleTrieState *
+purple_trie_state_new(PurpleTrie *trie, PurpleTrieState *parent, guchar character)
+{
+ PurpleTriePrivate *priv = PURPLE_TRIE_GET_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);
+
+ if (parent == NULL)
+ return state;
+
+ state->parent = parent;
+ if (parent->children == NULL) {
+ parent->children = purple_memory_pool_alloc0(
+ priv->states_mempool,
+ /* PurpleTrieState *children[G_MAXUCHAR + 1] */
+ 256 * sizeof(gpointer),
+ sizeof(gpointer));
+ }
+
+ if (parent->children == NULL) {
+ purple_memory_pool_free(priv->states_mempool, state);
+ g_warn_if_reached();
+ return NULL;
+ }
+
+ parent->children[character] = state;
+
+ return state;
+}
+
+#if 0
+static gchar *
+purple_trie_print(PurpleTrieState *state, int limit)
+{
+ GString *str = g_string_new(NULL);
+ int i;
+
+ if (limit < 0)
+ return g_strdup("{ LIMIT }");
+
+ if (state->found_word)
+ g_string_append(str, "*");
+ g_string_append(str, "{ ");
+ for (i = 0; i < 256; i++) {
+ gchar *chp;
+ if (!state->children)
+ continue;
+ if (!state->children[i])
+ continue;
+ if (i == 0)
+ g_string_append(str, "(null)->");
+ else
+ g_string_append_printf(str, "%c->", i);
+ if (state->children[i] == state)
+ g_string_append(str, "loop");
+ else {
+ chp = purple_trie_print(state->children[i], limit - 1);
+ g_string_append(str, chp);
+ g_string_append_c(str, ' ');
+ g_free(chp);
+ }
+ }
+ g_string_append(str, "}");
+
+ return g_string_free(str, FALSE);
+}
+#endif
+
+static gboolean
+purple_trie_states_build(PurpleTrie *trie)
+{
+ PurpleTriePrivate *priv = PURPLE_TRIE_GET_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;
+
+ if (priv->records_total_size < PURPLE_TRIE_LARGE_THRESHOLD) {
+ purple_memory_pool_set_block_size(priv->states_mempool,
+ PURPLE_TRIE_STATES_SMALL_POOL_BLOCK_SIZE);
+ } else {
+ purple_memory_pool_set_block_size(priv->states_mempool,
+ PURPLE_TRIE_STATES_LARGE_POOL_BLOCK_SIZE);
+ }
+
+ priv->root_state = root = purple_trie_state_new(trie, NULL, '\0');
+ g_return_val_if_fail(root != NULL, FALSE);
+ g_assert(root->longest_suffix == NULL);
+
+ /* reclist is a list of words not yet added to the trie. Shorter words
+ * are removed from the list, when they are fully added to the trie. */
+ reclist_mpool = purple_memory_pool_new();
+ reclist = purple_record_list_copy(reclist_mpool, priv->records);
+
+ /* extra_data on every element of reclist will be a pointer to a trie
+ * node -- the prefix of the word with len of cur_len */
+ for (it = reclist; it != NULL; it = it->next) {
+ it->extra_data = root;
+ }
+
+ /* Iterate over indexes of words -- every loop iteration checks certain
+ * index of all remaining words. Loop finishes when there are no words
+ * longer than cur_len. */
+ for (cur_len = 0; reclist != NULL; cur_len++) {
+ for (it = reclist; it; it = it->next) {
+ PurpleTrieRecord *rec = it->rec;
+ guchar character = rec->word[cur_len];
+ PurpleTrieState *prefix = it->extra_data;
+ PurpleTrieState *lon_suf_parent;
+
+ g_assert(character != '\0');
+
+ if (prefix->children && prefix->children[character]) {
+ /* Word's prefix is already in the trie, added
+ * by the other word. */
+ prefix = prefix->children[character];
+ } else {
+ /* We need to create a new branch of trie. */
+ prefix = purple_trie_state_new(trie, prefix,
+ character);
+ if (!prefix) {
+ g_warn_if_reached();
+ g_object_unref(reclist_mpool);
+ return FALSE;
+ }
+ }
+ it->extra_data = prefix;
+ /* prefix is now of length increased by one character. */
+
+ /* The whole word is now added to the trie. */
+ if (rec->word[cur_len + 1] == '\0') {
+ if (prefix->found_word == NULL)
+ prefix->found_word = rec;
+ else {
+ purple_debug_warning("trie", "found "
+ "a collision of \"%s\" words",
+ rec->word);
+ }
+
+ /* "it" is not modified here, so it->next is
+ * still valid */
+ reclist = purple_record_list_remove(reclist, it);
+ }
+
+ /* We need to fill the longest_suffix field -- a longest
+ * complete suffix of the prefix we created. We look for
+ * that suffix in any path starting in root and ending
+ * in the (cur_len - 1) level of trie. */
+ if (prefix->longest_suffix != NULL)
+ continue;
+ lon_suf_parent = prefix->parent->longest_suffix;
+ while (lon_suf_parent) {
+ if (lon_suf_parent->children &&
+ lon_suf_parent->children[character])
+ {
+ prefix->longest_suffix = lon_suf_parent->
+ children[character];
+ break;
+ }
+ lon_suf_parent = lon_suf_parent->longest_suffix;
+ }
+ if (prefix->longest_suffix == NULL)
+ prefix->longest_suffix = root;
+ if (prefix->found_word == NULL) {
+ prefix->found_word =
+ prefix->longest_suffix->found_word;
+ }
+ }
+ }
+
+ g_object_unref(reclist_mpool);
+
+ return TRUE;
+}
+
+/*******************************************************************************
+ * Searching
+ ******************************************************************************/
+
+static void
+purple_trie_advance(PurpleTrieMachine *m, const guchar character)
+{
+ /* change state after processing a character */
+ while (TRUE) {
+ /* Perfect fit - next character is the same, as the child of the
+ * prefix we reached so far. */
+ if (m->state->children && m->state->children[character]) {
+ m->state = m->state->children[character];
+ break;
+ }
+
+ /* We reached root, that's a pity. */
+ if (m->state == m->root_state)
+ break;
+
+ /* Let's try a bit shorter suffix. */
+ m->state = m->state->longest_suffix;
+ }
+}
+
+static gboolean
+purple_trie_replace_do_replacement(PurpleTrieMachine *m, GString *out)
+{
+ gboolean was_replaced = FALSE;
+ gsize str_old_len;
+
+ /* if we reached a "found" state, let's process it */
+ if (!m->state->found_word)
+ return FALSE;
+
+ /* let's get back to the beginning of the word */
+ g_assert(out->len >= m->state->found_word->word_len - 1);
+ str_old_len = out->len;
+ out->len -= m->state->found_word->word_len - 1;
+
+ was_replaced = m->replace_cb(out, m->state->found_word->word,
+ m->state->found_word->data, m->user_data);
+
+ /* output was untouched, revert to the previous position */
+ if (!was_replaced)
+ out->len = str_old_len;
+
+ /* XXX */
+ if (was_replaced || m->reset_on_match)
+ m->state = m->root_state;
+
+ return was_replaced;
+}
+
+static gboolean
+purple_trie_find_do_discovery(PurpleTrieMachine *m)
+{
+ gboolean was_accepted;
+
+ /* if we reached a "found" state, let's process it */
+ if (!m->state->found_word)
+ return FALSE;
+
+ if (m->find_cb) {
+ was_accepted = m->find_cb(m->state->found_word->word,
+ m->state->found_word->data, m->user_data);
+ } else {
+ was_accepted = TRUE;
+ }
+
+ if (was_accepted && m->reset_on_match)
+ m->state = m->root_state;
+
+ return was_accepted;
+}
+
+gchar *
+purple_trie_replace(PurpleTrie *trie, const gchar *src,
+ PurpleTrieReplaceCb replace_cb, gpointer user_data)
+{
+ PurpleTriePrivate *priv = PURPLE_TRIE_GET_PRIVATE(trie);
+ PurpleTrieMachine machine;
+ GString *out;
+ gsize i;
+
+ if (src == NULL)
+ return NULL;
+
+ g_return_val_if_fail(replace_cb != NULL, g_strdup(src));
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ purple_trie_states_build(trie);
+
+ machine.state = priv->root_state;
+ machine.root_state = priv->root_state;
+ machine.reset_on_match = priv->reset_on_match;
+ machine.replace_cb = replace_cb;
+ machine.user_data = user_data;
+
+ out = g_string_new(NULL);
+ i = 0;
+ while (src[i] != '\0') {
+ guchar character = src[i++];
+ gboolean was_replaced;
+
+ purple_trie_advance(&machine, character);
+ was_replaced = purple_trie_replace_do_replacement(&machine, out);
+
+ /* We skipped a character without finding any records,
+ * let's just copy it to the output. */
+ if (!was_replaced)
+ g_string_append_c(out, character);
+ }
+
+ return g_string_free(out, FALSE);
+}
+
+gchar *
+purple_trie_multi_replace(const GSList *tries, const gchar *src,
+ PurpleTrieReplaceCb replace_cb, gpointer user_data)
+{
+ guint tries_count, m_idx;
+ PurpleTrieMachine *machines;
+ GString *out;
+ gsize i;
+
+ if (src == NULL)
+ return NULL;
+
+ g_return_val_if_fail(replace_cb != NULL, g_strdup(src));
+
+ tries_count = g_slist_length((GSList*)tries);
+ if (tries_count == 0)
+ return g_strdup(src);
+
+ /* Initialize all machines. */
+ 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_PRIVATE(trie);
+
+ if (priv == NULL) {
+ g_warn_if_reached();
+ g_free(machines);
+ return NULL;
+ }
+
+ purple_trie_states_build(trie);
+
+ machines[i].state = priv->root_state;
+ machines[i].root_state = priv->root_state;
+ machines[i].reset_on_match = priv->reset_on_match;
+ machines[i].replace_cb = replace_cb;
+ machines[i].user_data = user_data;
+ }
+
+ out = g_string_new(NULL);
+ i = 0;
+ while (src[i] != '\0') {
+ guchar character = src[i++];
+ gboolean was_replaced = FALSE;
+
+ /* Advance every machine and possibly perform a replacement. */
+ for (m_idx = 0; m_idx < tries_count; m_idx++) {
+ purple_trie_advance(&machines[m_idx], character);
+ if (was_replaced)
+ continue;
+ was_replaced = purple_trie_replace_do_replacement(
+ &machines[m_idx], out);
+ }
+
+ /* We skipped a character without finding any records,
+ * let's just copy it to the output. */
+ if (!was_replaced)
+ g_string_append_c(out, character);
+
+ /* If we replaced a word, reset _all_ machines */
+ if (was_replaced) {
+ for (m_idx = 0; m_idx < tries_count; m_idx++) {
+ machines[m_idx].state =
+ machines[m_idx].root_state;
+ }
+ }
+ }
+
+ g_free(machines);
+ return g_string_free(out, FALSE);
+}
+
+gulong
+purple_trie_find(PurpleTrie *trie, const gchar *src,
+ PurpleTrieFindCb find_cb, gpointer user_data)
+{
+ PurpleTriePrivate *priv = PURPLE_TRIE_GET_PRIVATE(trie);
+ PurpleTrieMachine machine;
+ gulong found_count = 0;
+ gsize i;
+
+ if (src == NULL)
+ return 0;
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ purple_trie_states_build(trie);
+
+ machine.state = priv->root_state;
+ machine.root_state = priv->root_state;
+ machine.reset_on_match = priv->reset_on_match;
+ machine.find_cb = find_cb;
+ machine.user_data = user_data;
+
+ i = 0;
+ while (src[i] != '\0') {
+ guchar character = src[i++];
+ gboolean was_found;
+
+ purple_trie_advance(&machine, character);
+
+ was_found = purple_trie_find_do_discovery(&machine);
+
+ if (was_found)
+ found_count++;
+ }
+
+ return found_count;
+}
+
+gulong
+purple_trie_multi_find(const GSList *tries, const gchar *src,
+ PurpleTrieFindCb find_cb, gpointer user_data)
+{
+ guint tries_count, m_idx;
+ PurpleTrieMachine *machines;
+ gulong found_count = 0;
+ gsize i;
+
+ if (src == NULL)
+ return 0;
+
+ tries_count = g_slist_length((GSList*)tries);
+ if (tries_count == 0)
+ return 0;
+
+ /* Initialize all machines. */
+ 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_PRIVATE(trie);
+
+ if (priv == NULL) {
+ g_warn_if_reached();
+ g_free(machines);
+ return 0;
+ }
+
+ purple_trie_states_build(trie);
+
+ machines[i].state = priv->root_state;
+ machines[i].root_state = priv->root_state;
+ machines[i].reset_on_match = priv->reset_on_match;
+ machines[i].find_cb = find_cb;
+ machines[i].user_data = user_data;
+ }
+
+ i = 0;
+ while (src[i] != '\0') {
+ guchar character = src[i++];
+ gboolean was_found = FALSE;
+
+ /* Advance every machine and possibly perform a replacement. */
+ for (m_idx = 0; m_idx < tries_count; m_idx++) {
+ purple_trie_advance(&machines[m_idx], character);
+ if (was_found)
+ continue;
+ was_found =
+ purple_trie_find_do_discovery(&machines[m_idx]);
+ if (was_found)
+ found_count++;
+ }
+
+ /* If we replaced a word, reset _all_ machines */
+ if (was_found) {
+ for (m_idx = 0; m_idx < tries_count; m_idx++) {
+ if (!machines[m_idx].reset_on_match)
+ continue;
+ machines[m_idx].state =
+ machines[m_idx].root_state;
+ }
+ }
+ }
+
+ g_free(machines);
+ return found_count;
+}
+
+
+/*******************************************************************************
+ * Records
+ ******************************************************************************/
+
+gboolean
+purple_trie_add(PurpleTrie *trie, const gchar *word, gpointer data)
+{
+ PurpleTriePrivate *priv = PURPLE_TRIE_GET_PRIVATE(trie);
+ PurpleTrieRecord *rec;
+
+ g_return_val_if_fail(priv != NULL, FALSE);
+ g_return_val_if_fail(word != NULL, FALSE);
+ g_return_val_if_fail(word[0] != '\0', FALSE);
+
+ if (g_hash_table_lookup(priv->records_map, word) != NULL) {
+ purple_debug_warning("trie", "record exists: %s", word);
+ return FALSE;
+ }
+
+ /* 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);
+
+ rec = purple_memory_pool_alloc(priv->records_obj_mempool,
+ sizeof(PurpleTrieRecord), sizeof(gpointer));
+ rec->word = purple_memory_pool_strdup(priv->records_str_mempool, word);
+ rec->word_len = strlen(word);
+ g_assert(rec->word_len > 0);
+ rec->data = data;
+
+ priv->records_total_size += rec->word_len;
+ priv->records = purple_record_list_prepend(priv->records_obj_mempool,
+ priv->records, rec);
+ g_hash_table_insert(priv->records_map, rec->word, priv->records);
+
+ return TRUE;
+}
+
+void
+purple_trie_remove(PurpleTrie *trie, const gchar *word)
+{
+ PurpleTriePrivate *priv = PURPLE_TRIE_GET_PRIVATE(trie);
+ PurpleTrieRecordList *it;
+
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(word != NULL);
+ g_return_if_fail(word[0] != '\0');
+
+ it = g_hash_table_lookup(priv->records_map, word);
+ if (it == NULL)
+ return;
+
+ /* see purple_trie_add */
+ purple_trie_states_cleanup(trie);
+
+ priv->records_total_size -= it->rec->word_len;
+ priv->records = purple_record_list_remove(priv->records, it);
+ g_hash_table_remove(priv->records_map, it->rec->word);
+
+ purple_memory_pool_free(priv->records_str_mempool, it->rec->word);
+ purple_memory_pool_free(priv->records_obj_mempool, it->rec);
+ purple_memory_pool_free(priv->records_obj_mempool, it);
+}
+
+guint
+purple_trie_get_size(PurpleTrie *trie)
+{
+ PurpleTriePrivate *priv = PURPLE_TRIE_GET_PRIVATE(trie);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return g_hash_table_size(priv->records_map);
+}
+
+
+/*******************************************************************************
+ * API implementation
+ ******************************************************************************/
+
+gboolean
+purple_trie_get_reset_on_match(PurpleTrie *trie)
+{
+ PurpleTriePrivate *priv = PURPLE_TRIE_GET_PRIVATE(trie);
+
+ g_return_val_if_fail(priv, FALSE);
+
+ return priv->reset_on_match;
+}
+
+void
+purple_trie_set_reset_on_match(PurpleTrie *trie, gboolean reset)
+{
+ PurpleTriePrivate *priv = PURPLE_TRIE_GET_PRIVATE(trie);
+
+ g_return_if_fail(priv);
+
+ priv->reset_on_match = reset;
+ g_object_notify_by_pspec(G_OBJECT(trie), properties[PROP_RESET_ON_MATCH]);
+}
+
+/*******************************************************************************
+ * Object stuff
+ ******************************************************************************/
+
+PurpleTrie *
+purple_trie_new(void)
+{
+ return g_object_new(PURPLE_TYPE_TRIE, NULL);
+}
+
+static void
+purple_trie_init(GTypeInstance *instance, gpointer klass)
+{
+ PurpleTrie *trie = PURPLE_TRIE(instance);
+ PurpleTriePrivate *priv = PURPLE_TRIE_GET_PRIVATE(trie);
+
+ priv->records_obj_mempool = purple_memory_pool_new();
+ priv->records_str_mempool = purple_memory_pool_new();
+ priv->states_mempool = purple_memory_pool_new();
+ purple_memory_pool_set_block_size(priv->states_mempool,
+ PURPLE_TRIE_STATES_SMALL_POOL_BLOCK_SIZE);
+
+ priv->records_map = g_hash_table_new(g_str_hash, g_str_equal);
+}
+
+static void
+purple_trie_finalize(GObject *obj)
+{
+ PurpleTriePrivate *priv = PURPLE_TRIE_GET_PRIVATE(obj);
+
+ g_hash_table_destroy(priv->records_map);
+ g_object_unref(priv->records_obj_mempool);
+ g_object_unref(priv->records_str_mempool);
+ g_object_unref(priv->states_mempool);
+
+ G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+static void
+purple_trie_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleTrie *trie = PURPLE_TRIE(obj);
+ PurpleTriePrivate *priv = PURPLE_TRIE_GET_PRIVATE(trie);
+
+ switch (param_id) {
+ case PROP_RESET_ON_MATCH:
+ g_value_set_boolean(value, priv->reset_on_match);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ }
+}
+
+static void
+purple_trie_set_property(GObject *obj, guint param_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ PurpleTrie *trie = PURPLE_TRIE(obj);
+ PurpleTriePrivate *priv = PURPLE_TRIE_GET_PRIVATE(trie);
+
+ switch (param_id) {
+ case PROP_RESET_ON_MATCH:
+ priv->reset_on_match = g_value_get_boolean(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ }
+}
+
+static void
+purple_trie_class_init(PurpleTrieClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ g_type_class_add_private(klass, sizeof(PurpleTriePrivate));
+
+ obj_class->finalize = purple_trie_finalize;
+ obj_class->get_property = purple_trie_get_property;
+ obj_class->set_property = purple_trie_set_property;
+
+ properties[PROP_RESET_ON_MATCH] = g_param_spec_boolean("reset-on-match",
+ "Reset on match", "Determines, if the search state machine "
+ "should be reset to the initial state on every match. This "
+ "ensures, that every match is distinct from each other. "
+ "Please note, that it's not well-defined for a replace "
+ "operation, so it's better to leave this value default, unless "
+ "you perform only find operations.", TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, PROP_LAST, properties);
+}
+
+GType
+purple_trie_get_type(void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY(type == 0)) {
+ static const GTypeInfo info = {
+ .class_size = sizeof(PurpleTrieClass),
+ .class_init = (GClassInitFunc)purple_trie_class_init,
+ .instance_size = sizeof(PurpleTrie),
+ .instance_init = purple_trie_init,
+ };
+
+ type = g_type_register_static(G_TYPE_OBJECT,
+ "PurpleTrie", &info, 0);
+ }
+
+ return type;
+}
diff --git a/libpurple/trie.h b/libpurple/trie.h
new file mode 100644
index 0000000000..a302928c44
--- /dev/null
+++ b/libpurple/trie.h
@@ -0,0 +1,306 @@
+/*
+ * 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 PURPLE_TRIE_H
+#define PURPLE_TRIE_H
+/**
+ * SECTION:trie
+ * @include:trie.h
+ * @section_id: libpurple-trie
+ * @short_description: a structure for linear-time text searching
+ * @title: Tries
+ *
+ * A #PurpleTrie is a structure for quick searching of multiple phrases within
+ * a text. It's intended for repeated searches of the same set of patterns
+ * within multiple source texts (or a single, big one).
+ *
+ * It's preparation time is <literal>O(p)</literal>, where <literal>p</literal>
+ * is the total length of searched phrases. In current implementation, the
+ * internal structure is invalidated after every modification of the
+ * #PurpleTrie's contents, so it's not efficient to do alternating modifications
+ * and searches. Search time does not depend on patterns being stored within
+ * a trie and is always <literal>O(n)</literal>, where <literal>n</literal> is
+ * the size of a text.
+ *
+ * Its main drawback is a significant memory usage - every internal trie node
+ * needs about 1kB of memory on 32-bit machine and 2kB on 64-bit. Fortunately,
+ * the trie grows slower when more words (with common prefixes) are added.
+ * We could avoid invalidating the whole tree when altering it, but it would
+ * require figuring out, how to update <literal>longest_suffix</literal> fields
+ * in satisfying time.
+ */
+
+#include <glib-object.h>
+
+#define PURPLE_TYPE_TRIE (purple_trie_get_type())
+#define PURPLE_TRIE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_TRIE, PurpleTrie))
+#define PURPLE_TRIE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_TRIE, PurpleTrieClass))
+#define PURPLE_IS_TRIE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_TRIE))
+#define PURPLE_IS_TRIE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_TRIE))
+#define PURPLE_TRIE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_TRIE, PurpleTrieClass))
+
+typedef struct _PurpleTrie PurpleTrie;
+typedef struct _PurpleTrieClass PurpleTrieClass;
+
+/**
+ * PurpleTrie:
+ *
+ * The trie object instance.
+ */
+struct _PurpleTrie
+{
+ /*< private >*/
+ GObject parent_instance;
+};
+
+/**
+ * PurpleTrieClass:
+ *
+ * Base class for #PurpleTrie objects.
+ */
+struct _PurpleTrieClass
+{
+ /*< private >*/
+ GObjectClass parent_class;
+
+ void (*purple_reserved1)(void);
+ void (*purple_reserved2)(void);
+ void (*purple_reserved3)(void);
+ void (*purple_reserved4)(void);
+};
+
+/**
+ * PurpleTrieReplaceCb:
+ * @out: currently built output string, append replacement to it.
+ * @word: found word.
+ * @word_data: the user data bound with this word, when added with
+ * #purple_trie_add.
+ * @user_data: the user supplied data passed when calling #purple_trie_replace.
+ *
+ * A funtion called on every matching substring to be replaced.
+ *
+ * If you decide to replace the word, append your text to @out and return %TRUE.
+ * Otherwise, you must not touch @out. In both cases, you must not do any
+ * operations on @out other than appending text to it.
+ *
+ * Returns: %TRUE if the word was replaced, %FALSE otherwise.
+ */
+typedef gboolean (*PurpleTrieReplaceCb)(GString *out, const gchar *word,
+ gpointer word_data, gpointer user_data);
+
+/**
+ * PurpleTrieFindCb:
+ * @word: found word.
+ * @word_data: the user data bound with this word, when added with
+ * #purple_trie_add.
+ * @user_data: the user data passed when calling #purple_trie_find.
+ *
+ * A function called on every matching substring.
+ *
+ * You can decide to count the match or not (for the total number of found
+ * words, that is returned by #purple_trie_find). In both cases you can
+ * obviously do some processing outside the #PurpleTrie object.
+ *
+ * If you decide to count the word and #PurpleTrie:reset-on-match property
+ * is set, no overlapping words will be found - the processing will skip after
+ * the end of this word.
+ *
+ * Returns: %TRUE if the word should be counter, %FALSE otherwise.
+ */
+typedef gboolean (*PurpleTrieFindCb)(const gchar *word, gpointer word_data,
+ gpointer user_data);
+
+G_BEGIN_DECLS
+
+/**
+ * purple_trie_get_type:
+ *
+ * Returns: the #GType for a #PurpleTrie.
+ */
+GType
+purple_trie_get_type(void);
+
+/**
+ * purple_trie_new:
+ *
+ * Creates a new trie.
+ *
+ * Returns: the new #PurpleTrie.
+ */
+PurpleTrie *
+purple_trie_new(void);
+
+/**
+ * purple_trie_get_reset_on_match:
+ * @trie: the trie.
+ *
+ * Checks, if the trie will reset its internal state after every match.
+ *
+ * Returns: %TRUE, if trie will reset, %FALSE otherwise.
+ */
+gboolean
+purple_trie_get_reset_on_match(PurpleTrie *trie);
+
+/**
+ * purple_trie_set_reset_on_match:
+ * @trie: the trie.
+ * @reset: %TRUE, if trie should reset, %FALSE otherwise.
+ *
+ * Enables or disables a feature of resetting trie's state after every match.
+ * When enabled, it will not search for overlapping matches.
+ *
+ * It's well defined for #purple_trie_find, but not for replace operations.
+ * Thus, for the latter, it's better to stay with this option enabled, because
+ * its behavior may be changed in future.
+ */
+void
+purple_trie_set_reset_on_match(PurpleTrie *trie, gboolean reset);
+
+/**
+ * purple_trie_add:
+ * @trie: the trie.
+ * @word: the word.
+ * @data: the word-related data (may be %NULL).
+ *
+ * Adds a word to the trie. Current implementation doesn't allow for duplicates,
+ * so please avoid adding those.
+ *
+ * Please note, that altering a trie invalidates its internal structure, so by
+ * the occasion of next search, it will be rebuilt. It's done in
+ * <literal>O(n)</literal>, where n is the total length of strings
+ * in #PurpleTrie.
+ *
+ * Returns: %TRUE if succeeded, %FALSE otherwise.
+ */
+gboolean
+purple_trie_add(PurpleTrie *trie, const gchar *word, gpointer data);
+
+/**
+ * purple_trie_remove:
+ * @trie: the trie.
+ * @word: the word.
+ *
+ * Removes a word from the trie. Depending on used memory pool, this may not
+ * free allocated memory (that will be freed when destroying the whole
+ * collection), so use it wisely. See #purple_memory_pool_free.
+ *
+ * Please note, that altering a trie invalidates its internal structure.
+ * See #purple_trie_add.
+ */
+void
+purple_trie_remove(PurpleTrie *trie, const gchar *word);
+
+/**
+ * purple_trie_get_size:
+ * @trie: the trie.
+ *
+ * Returns the number of elements contained in the #PurpleTrie.
+ *
+ * Returns: the number of stored words in @trie.
+ */
+guint
+purple_trie_get_size(PurpleTrie *trie);
+
+/**
+ * purple_trie_replace:
+ * @trie: the trie.
+ * @src: the source string.
+ * @replace_cb: the replacement function.
+ * @user_data: custom data to be passed to @replace_cb.
+ *
+ * Processes @src string and replaces all occuriences of words added to @trie.
+ * It's <literal>O(strlen(src))</literal>, if @replace_cb runs in
+ * <literal>O(strlen(word))</literal> and #PurpleTrie:reset-on-match is set.
+ *
+ * Returns: resulting string. Must be #g_free'd when you are done using it.
+ */
+gchar *
+purple_trie_replace(PurpleTrie *trie, const gchar *src,
+ PurpleTrieReplaceCb replace_cb, gpointer user_data);
+
+/**
+ * purple_trie_multi_replace:
+ * @tries: the list of tries.
+ * @src: the source string.
+ * @replace_cb: the replacement function.
+ * @user_data: custom data to be passed to @replace_cb.
+ *
+ * Processes @src and replaces all occuriences of words added to tries in list
+ * @tries. Entries added to tries on the beginning of the list have higher
+ * priority, than ones added further.
+ *
+ * Different #GSList's can be combined to possess common parts, so you can create
+ * a "tree of tries".
+ *
+ * Returns: resulting string. Must be #g_free'd when you are done using it.
+ */
+gchar *
+purple_trie_multi_replace(const GSList *tries, const gchar *src,
+ PurpleTrieReplaceCb replace_cb, gpointer user_data);
+
+/**
+ * purple_trie_find:
+ * @trie: the trie.
+ * @src: the source string.
+ * @find_cb: the callback for the found entries (may be %NULL).
+ * @user_data: custom data to be passed to @find_cb.
+ *
+ * Processes @src string and finds all occuriences of words added to @trie.
+ * It's <literal>O(strlen(src))</literal>, if find_cb runs
+ * in <literal>O(1)</literal>.
+ *
+ * The word is counted as found if it's found and the callback returns %TRUE.
+ *
+ * Returns: the number of found words.
+ */
+gulong
+purple_trie_find(PurpleTrie *trie, const gchar *src,
+ PurpleTrieFindCb find_cb, gpointer user_data);
+
+/**
+ * purple_trie_multi_find:
+ * @tries: the list of tries.
+ * @src: the source string.
+ * @find_cb: the callback for the found entries (may be %NULL).
+ * @user_data: custom data to be passed to @find_cb.
+ *
+ * Processes @src and replaces all occuriences of words added to tries in
+ * list @tries. Entries added to tries on the beginning of the list have higher
+ * priority, than ones added further.
+ *
+ * Different #GSList's can be combined to possess common parts, so you can create
+ * a "tree of tries".
+ *
+ * Returns: the number of found words.
+ */
+gulong
+purple_trie_multi_find(const GSList *tries, const gchar *src,
+ PurpleTrieFindCb find_cb, gpointer user_data);
+
+G_END_DECLS
+
+#endif /* PURPLE_MEMORY_POOL_H */
diff --git a/libpurple/upnp.c b/libpurple/upnp.c
index e4dd36921b..d1e2ab397c 100644
--- a/libpurple/upnp.c
+++ b/libpurple/upnp.c
@@ -1,8 +1,3 @@
-/**
- * @file upnp.c UPnP Implementation
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -29,6 +24,7 @@
#include "debug.h"
#include "eventloop.h"
+#include "http.h"
#include "network.h"
#include "proxy.h"
#include "signals.h"
@@ -69,13 +65,6 @@
/******************************************************************
** Action Defines *
*******************************************************************/
-#define HTTP_HEADER_ACTION \
- "POST /%s HTTP/1.1\r\n" \
- "HOST: %s:%d\r\n" \
- "SOAPACTION: \"urn:schemas-upnp-org:service:%s#%s\"\r\n" \
- "CONTENT-TYPE: text/xml ; charset=\"utf-8\"\r\n" \
- "CONTENT-LENGTH: %" G_GSIZE_FORMAT "\r\n\r\n"
-
#define SOAP_ACTION \
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" \
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " \
@@ -135,7 +124,7 @@ typedef struct {
gchar *full_url;
} UPnPDiscoveryData;
-struct _UPnPMappingAddRemove
+struct _PurpleUPnPMappingAddRemove
{
unsigned short portmap;
gchar protocol[4];
@@ -144,7 +133,7 @@ struct _UPnPMappingAddRemove
gpointer cb_data;
gboolean success;
guint tima; /* purple_timeout_add handle */
- PurpleUtilFetchUrlData *gfud;
+ PurpleHttpConnection *hc;
};
static PurpleUPnPControlInfo control_info = {
@@ -160,7 +149,7 @@ static void lookup_internal_ip(void);
static gboolean
fire_ar_cb_async_and_free(gpointer data)
{
- UPnPMappingAddRemove *ar = data;
+ PurpleUPnPMappingAddRemove *ar = data;
if (ar) {
if (ar->cb)
ar->cb(ar->success, ar->cb_data);
@@ -184,9 +173,9 @@ fire_discovery_callbacks(gboolean success)
}
static gboolean
-purple_upnp_compare_device(const xmlnode* device, const gchar* deviceType)
+purple_upnp_compare_device(const PurpleXmlNode* device, const gchar* deviceType)
{
- xmlnode* deviceTypeNode = xmlnode_get_child(device, "deviceType");
+ PurpleXmlNode* deviceTypeNode = purple_xmlnode_get_child(device, "deviceType");
char *tmp;
gboolean ret;
@@ -194,7 +183,7 @@ purple_upnp_compare_device(const xmlnode* device, const gchar* deviceType)
return FALSE;
}
- tmp = xmlnode_get_data(deviceTypeNode);
+ tmp = purple_xmlnode_get_data(deviceTypeNode);
ret = !g_ascii_strcasecmp(tmp, deviceType);
g_free(tmp);
@@ -202,9 +191,9 @@ purple_upnp_compare_device(const xmlnode* device, const gchar* deviceType)
}
static gboolean
-purple_upnp_compare_service(const xmlnode* service, const gchar* serviceType)
+purple_upnp_compare_service(const PurpleXmlNode* service, const gchar* serviceType)
{
- xmlnode* serviceTypeNode;
+ PurpleXmlNode* serviceTypeNode;
char *tmp;
gboolean ret;
@@ -212,13 +201,13 @@ purple_upnp_compare_service(const xmlnode* service, const gchar* serviceType)
return FALSE;
}
- serviceTypeNode = xmlnode_get_child(service, "serviceType");
+ serviceTypeNode = purple_xmlnode_get_child(service, "serviceType");
if(serviceTypeNode == NULL) {
return FALSE;
}
- tmp = xmlnode_get_data(serviceTypeNode);
+ tmp = purple_xmlnode_get_data(serviceTypeNode);
ret = !g_ascii_strcasecmp(tmp, serviceType);
g_free(tmp);
@@ -229,27 +218,12 @@ static gchar*
purple_upnp_parse_description_response(const gchar* httpResponse, gsize len,
const gchar* httpURL, const gchar* serviceType)
{
- gchar *xmlRoot, *baseURL, *controlURL, *service;
- xmlnode *xmlRootNode, *serviceTypeNode, *controlURLNode, *baseURLNode;
+ gchar *baseURL, *controlURL, *service;
+ PurpleXmlNode *xmlRootNode, *serviceTypeNode, *controlURLNode, *baseURLNode;
char *tmp;
- /* make sure we have a valid http response */
- if(g_strstr_len(httpResponse, len, HTTP_OK) == NULL) {
- purple_debug_error("upnp",
- "parse_description_response(): Failed In HTTP_OK\n");
- return NULL;
- }
-
- /* find the root of the xml document */
- if((xmlRoot = g_strstr_len(httpResponse, len, "<root")) == NULL) {
- purple_debug_error("upnp",
- "parse_description_response(): Failed finding root\n");
- return NULL;
- }
-
/* create the xml root node */
- if((xmlRootNode = xmlnode_from_str(xmlRoot,
- len - (xmlRoot - httpResponse))) == NULL) {
+ if ((xmlRootNode = purple_xmlnode_from_str(httpResponse, len)) == NULL) {
purple_debug_error("upnp",
"parse_description_response(): Could not parse xml root node\n");
return NULL;
@@ -257,8 +231,8 @@ purple_upnp_parse_description_response(const gchar* httpResponse, gsize len,
/* get the baseURL of the device */
baseURL = NULL;
- if((baseURLNode = xmlnode_get_child(xmlRootNode, "URLBase")) != NULL) {
- baseURL = xmlnode_get_data(baseURLNode);
+ if((baseURLNode = purple_xmlnode_get_child(xmlRootNode, "URLBase")) != NULL) {
+ baseURL = purple_xmlnode_get_data(baseURLNode);
}
/* fixes upnp-descriptions with empty urlbase-element */
if(baseURL == NULL){
@@ -268,79 +242,79 @@ purple_upnp_parse_description_response(const gchar* httpResponse, gsize len,
/* get the serviceType child that has the service type as its data */
/* get urn:schemas-upnp-org:device:InternetGatewayDevice:1 and its devicelist */
- serviceTypeNode = xmlnode_get_child(xmlRootNode, "device");
+ serviceTypeNode = purple_xmlnode_get_child(xmlRootNode, "device");
while(!purple_upnp_compare_device(serviceTypeNode,
"urn:schemas-upnp-org:device:InternetGatewayDevice:1") &&
serviceTypeNode != NULL) {
- serviceTypeNode = xmlnode_get_next_twin(serviceTypeNode);
+ serviceTypeNode = purple_xmlnode_get_next_twin(serviceTypeNode);
}
if(serviceTypeNode == NULL) {
purple_debug_error("upnp",
"parse_description_response(): could not get serviceTypeNode 1\n");
g_free(baseURL);
- xmlnode_free(xmlRootNode);
+ purple_xmlnode_free(xmlRootNode);
return NULL;
}
- serviceTypeNode = xmlnode_get_child(serviceTypeNode, "deviceList");
+ serviceTypeNode = purple_xmlnode_get_child(serviceTypeNode, "deviceList");
if(serviceTypeNode == NULL) {
purple_debug_error("upnp",
"parse_description_response(): could not get serviceTypeNode 2\n");
g_free(baseURL);
- xmlnode_free(xmlRootNode);
+ purple_xmlnode_free(xmlRootNode);
return NULL;
}
/* get urn:schemas-upnp-org:device:WANDevice:1 and its devicelist */
- serviceTypeNode = xmlnode_get_child(serviceTypeNode, "device");
+ serviceTypeNode = purple_xmlnode_get_child(serviceTypeNode, "device");
while(!purple_upnp_compare_device(serviceTypeNode,
"urn:schemas-upnp-org:device:WANDevice:1") &&
serviceTypeNode != NULL) {
- serviceTypeNode = xmlnode_get_next_twin(serviceTypeNode);
+ serviceTypeNode = purple_xmlnode_get_next_twin(serviceTypeNode);
}
if(serviceTypeNode == NULL) {
purple_debug_error("upnp",
"parse_description_response(): could not get serviceTypeNode 3\n");
g_free(baseURL);
- xmlnode_free(xmlRootNode);
+ purple_xmlnode_free(xmlRootNode);
return NULL;
}
- serviceTypeNode = xmlnode_get_child(serviceTypeNode, "deviceList");
+ serviceTypeNode = purple_xmlnode_get_child(serviceTypeNode, "deviceList");
if(serviceTypeNode == NULL) {
purple_debug_error("upnp",
"parse_description_response(): could not get serviceTypeNode 4\n");
g_free(baseURL);
- xmlnode_free(xmlRootNode);
+ purple_xmlnode_free(xmlRootNode);
return NULL;
}
/* get urn:schemas-upnp-org:device:WANConnectionDevice:1 and its servicelist */
- serviceTypeNode = xmlnode_get_child(serviceTypeNode, "device");
+ serviceTypeNode = purple_xmlnode_get_child(serviceTypeNode, "device");
while(serviceTypeNode && !purple_upnp_compare_device(serviceTypeNode,
"urn:schemas-upnp-org:device:WANConnectionDevice:1")) {
- serviceTypeNode = xmlnode_get_next_twin(serviceTypeNode);
+ serviceTypeNode = purple_xmlnode_get_next_twin(serviceTypeNode);
}
if(serviceTypeNode == NULL) {
purple_debug_error("upnp",
"parse_description_response(): could not get serviceTypeNode 5\n");
g_free(baseURL);
- xmlnode_free(xmlRootNode);
+ purple_xmlnode_free(xmlRootNode);
return NULL;
}
- serviceTypeNode = xmlnode_get_child(serviceTypeNode, "serviceList");
+ serviceTypeNode = purple_xmlnode_get_child(serviceTypeNode, "serviceList");
if(serviceTypeNode == NULL) {
purple_debug_error("upnp",
"parse_description_response(): could not get serviceTypeNode 6\n");
g_free(baseURL);
- xmlnode_free(xmlRootNode);
+ purple_xmlnode_free(xmlRootNode);
return NULL;
}
/* get the serviceType variable passed to this function */
service = g_strdup_printf(SEARCH_REQUEST_DEVICE, serviceType);
- serviceTypeNode = xmlnode_get_child(serviceTypeNode, "service");
+ serviceTypeNode = purple_xmlnode_get_child(serviceTypeNode, "service");
while(!purple_upnp_compare_service(serviceTypeNode, service) &&
serviceTypeNode != NULL) {
- serviceTypeNode = xmlnode_get_next_twin(serviceTypeNode);
+ serviceTypeNode = purple_xmlnode_get_next_twin(serviceTypeNode);
}
g_free(service);
@@ -348,21 +322,21 @@ purple_upnp_parse_description_response(const gchar* httpResponse, gsize len,
purple_debug_error("upnp",
"parse_description_response(): could not get serviceTypeNode 7\n");
g_free(baseURL);
- xmlnode_free(xmlRootNode);
+ purple_xmlnode_free(xmlRootNode);
return NULL;
}
/* get the controlURL of the service */
- if((controlURLNode = xmlnode_get_child(serviceTypeNode,
+ if((controlURLNode = purple_xmlnode_get_child(serviceTypeNode,
"controlURL")) == NULL) {
purple_debug_error("upnp",
"parse_description_response(): Could not find controlURL\n");
g_free(baseURL);
- xmlnode_free(xmlRootNode);
+ purple_xmlnode_free(xmlRootNode);
return NULL;
}
- tmp = xmlnode_get_data(controlURLNode);
+ tmp = purple_xmlnode_get_data(controlURLNode);
if(baseURL && !purple_str_has_prefix(tmp, "http://") &&
!purple_str_has_prefix(tmp, "HTTP://")) {
/* Handle absolute paths in a relative URL. This probably
@@ -372,7 +346,7 @@ purple_upnp_parse_description_response(const gchar* httpResponse, gsize len,
const char *path, *start = strstr(baseURL, "://");
start = start ? start + 3 : baseURL;
path = strchr(start, '/');
- length = path ? path - baseURL : strlen(baseURL);
+ length = path ? (gsize)(path - baseURL) : strlen(baseURL);
controlURL = g_strdup_printf("%.*s%s", (int)length, baseURL, tmp);
} else {
controlURL = g_strdup_printf("%s%s", baseURL, tmp);
@@ -382,21 +356,26 @@ purple_upnp_parse_description_response(const gchar* httpResponse, gsize len,
controlURL = tmp;
}
g_free(baseURL);
- xmlnode_free(xmlRootNode);
+ purple_xmlnode_free(xmlRootNode);
return controlURL;
}
static void
-upnp_parse_description_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
- const gchar *httpResponse, gsize len, const gchar *error_message)
+upnp_parse_description_cb(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _dd)
{
- UPnPDiscoveryData *dd = user_data;
+ UPnPDiscoveryData *dd = _dd;
gchar *control_url = NULL;
- if (len > 0)
+ if (response && purple_http_response_is_successful(response)) {
+ const gchar *got_data;
+ size_t got_len;
+
+ got_data = purple_http_response_get_data(response, &got_len);
control_url = purple_upnp_parse_description_response(
- httpResponse, len, dd->full_url, dd->service_type);
+ got_data, got_len, dd->full_url, dd->service_type);
+ }
g_free(dd->full_url);
@@ -431,47 +410,31 @@ upnp_parse_description_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
static void
purple_upnp_parse_description(const gchar* descriptionURL, UPnPDiscoveryData *dd)
{
- gchar* httpRequest;
- gchar* descriptionXMLAddress;
- gchar* descriptionAddress;
- int port = 0;
-
- /* parse the 4 above variables out of the descriptionURL
- example description URL: http://192.168.1.1:5678/rootDesc.xml */
-
- /* parse the url into address, port, path variables */
- if(!purple_url_parse(descriptionURL, &descriptionAddress,
- &port, &descriptionXMLAddress, NULL, NULL)) {
- return;
- }
- if(port == 0 || port == -1) {
- port = DEFAULT_HTTP_PORT;
- }
-
- /* for example...
- GET /rootDesc.xml HTTP/1.1\r\nHost: 192.168.1.1:5678\r\n\r\n */
- httpRequest = g_strdup_printf(
- "GET /%s HTTP/1.1\r\n"
- "Connection: close\r\n"
- "Host: %s:%d\r\n\r\n",
- descriptionXMLAddress, descriptionAddress, port);
-
- g_free(descriptionXMLAddress);
-
- dd->full_url = g_strdup_printf("http://%s:%d",
- descriptionAddress, port);
- g_free(descriptionAddress);
+ PurpleHttpRequest *req;
+ PurpleHttpURL *url;
/* Remove the timeout because everything it is waiting for has
* successfully completed */
purple_timeout_remove(dd->tima);
dd->tima = 0;
- purple_util_fetch_url_request_len(descriptionURL, TRUE, NULL, TRUE, httpRequest,
- TRUE, MAX_UPNP_DOWNLOAD, upnp_parse_description_cb, dd);
-
- g_free(httpRequest);
-
+ /* Extract base url out of the descriptionURL.
+ * Example description URL: http://192.168.1.1:5678/rootDesc.xml
+ */
+ url = purple_http_url_parse(descriptionURL);
+ if (!url) {
+ upnp_parse_description_cb(NULL, NULL, dd);
+ return;
+ }
+ dd->full_url = g_strdup_printf("http://%s:%d",
+ purple_http_url_get_host(url),
+ purple_http_url_get_port(url));
+ purple_http_url_free(url);
+
+ req = purple_http_request_new(descriptionURL);
+ purple_http_request_set_max_len(req, MAX_UPNP_DOWNLOAD);
+ purple_http_request(NULL, req, upnp_parse_description_cb, dd);
+ purple_http_request_unref(req);
}
static void
@@ -611,10 +574,10 @@ purple_upnp_discover_send_broadcast(UPnPDiscoveryData *dd)
totalSize = strlen(sendMessage);
do {
- if(sendto(dd->fd, sendMessage, totalSize, 0,
- (struct sockaddr*) &(dd->server),
- sizeof(struct sockaddr_in)
- ) == totalSize) {
+ gssize sent = sendto(dd->fd, sendMessage, totalSize, 0,
+ (struct sockaddr*) &(dd->server),
+ sizeof(struct sockaddr_in));
+ if(sent >= 0 && (gsize)sent == totalSize) {
sentSuccess = TRUE;
break;
}
@@ -695,58 +658,41 @@ purple_upnp_discover(PurpleUPnPCallback cb, gpointer cb_data)
purple_upnp_discover_send_broadcast(dd);
}
-static PurpleUtilFetchUrlData*
+static PurpleHttpConnection*
purple_upnp_generate_action_message_and_send(const gchar* actionName,
- const gchar* actionParams, PurpleUtilFetchUrlCallback cb,
+ const gchar* actionParams, PurpleHttpCallback cb,
gpointer cb_data)
{
- PurpleUtilFetchUrlData* gfud;
+ PurpleHttpConnection *hc;
+ PurpleHttpRequest *req;
gchar* soapMessage;
- gchar* totalSendMessage;
- gchar* pathOfControl;
- gchar* addressOfControl;
- int port = 0;
-
- /* parse the url into address, port, path variables */
- if(!purple_url_parse(control_info.control_url, &addressOfControl,
- &port, &pathOfControl, NULL, NULL)) {
- purple_debug_error("upnp",
- "generate_action_message_and_send(): Failed In Parse URL\n");
- /* XXX: This should probably be async */
- if(cb)
- cb(NULL, cb_data, NULL, 0, NULL);
- return NULL;
- }
- if(port == 0 || port == -1) {
- port = DEFAULT_HTTP_PORT;
- }
/* set the soap message */
soapMessage = g_strdup_printf(SOAP_ACTION, actionName,
control_info.service_type, actionParams, actionName);
- /* set the HTTP Header, and append the body to it */
- totalSendMessage = g_strdup_printf(HTTP_HEADER_ACTION "%s",
- pathOfControl, addressOfControl, port,
- control_info.service_type, actionName,
- strlen(soapMessage), soapMessage);
- g_free(pathOfControl);
- g_free(soapMessage);
-
- gfud = purple_util_fetch_url_request_len(control_info.control_url, FALSE, NULL, TRUE,
- totalSendMessage, TRUE, MAX_UPNP_DOWNLOAD, cb, cb_data);
+ req = purple_http_request_new(control_info.control_url);
+ purple_http_request_set_max_len(req, MAX_UPNP_DOWNLOAD);
+ purple_http_request_set_method(req, "POST");
+ purple_http_request_header_set_printf(req, "SOAPAction",
+ "\"urn:schemas-upnp-org:service:%s#%s\"",
+ control_info.service_type, actionName);
+ purple_http_request_header_set(req, "Content-Type",
+ "text/xml; charset=utf-8");
+ purple_http_request_set_contents(req, soapMessage, -1);
+ hc = purple_http_request(NULL, req, cb, cb_data);
+ purple_http_request_unref(req);
- g_free(totalSendMessage);
- g_free(addressOfControl);
+ g_free(soapMessage);
- return gfud;
+ return hc;
}
const gchar *
purple_upnp_get_public_ip()
{
if (control_info.status == PURPLE_UPNP_STATUS_DISCOVERED
- && strlen(control_info.publicip) > 0)
+ && *control_info.publicip)
return control_info.publicip;
/* Trigger another UPnP discovery if 5 minutes have elapsed since the
@@ -759,27 +705,30 @@ purple_upnp_get_public_ip()
}
static void
-looked_up_public_ip_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
- const gchar *httpResponse, gsize len, const gchar *error_message)
+looked_up_public_ip_cb(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer user_data)
{
gchar* temp, *temp2;
+ const gchar *got_data;
+ size_t got_len;
- if ((error_message != NULL) || (httpResponse == NULL))
+ if (!purple_http_response_is_successful(response))
return;
/* extract the ip, or see if there is an error */
- if((temp = g_strstr_len(httpResponse, len,
+ got_data = purple_http_response_get_data(response, &got_len);
+ if((temp = g_strstr_len(got_data, got_len,
"<NewExternalIPAddress")) == NULL) {
purple_debug_error("upnp",
"looked_up_public_ip_cb(): Failed Finding <NewExternalIPAddress\n");
return;
}
- if(!(temp = g_strstr_len(temp, len - (temp - httpResponse), ">"))) {
+ if(!(temp = g_strstr_len(temp, got_len - (temp - got_data), ">"))) {
purple_debug_error("upnp",
"looked_up_public_ip_cb(): Failed In Finding >\n");
return;
}
- if(!(temp2 = g_strstr_len(temp, len - (temp - httpResponse), "<"))) {
+ if(!(temp2 = g_strstr_len(temp, got_len - (temp - got_data), "<"))) {
purple_debug_error("upnp",
"looked_up_public_ip_cb(): Failed In Finding <\n");
return;
@@ -804,7 +753,7 @@ static const gchar *
purple_upnp_get_internal_ip(void)
{
if (control_info.status == PURPLE_UPNP_STATUS_DISCOVERED
- && strlen(control_info.internalip) > 0)
+ && *control_info.internalip)
return control_info.internalip;
/* Trigger another UPnP discovery if 5 minutes have elapsed since the
@@ -834,44 +783,42 @@ looked_up_internal_ip_cb(gpointer data, gint source, const gchar *error_message)
static void
lookup_internal_ip()
{
- gchar* addressOfControl;
- int port = 0;
+ PurpleHttpURL *url;
- if(!purple_url_parse(control_info.control_url, &addressOfControl, &port,
- NULL, NULL, NULL)) {
+ url = purple_http_url_parse(control_info.control_url);
+ if (!url) {
purple_debug_error("upnp",
"lookup_internal_ip(): Failed In Parse URL\n");
return;
}
- if(port == 0 || port == -1) {
- port = DEFAULT_HTTP_PORT;
- }
- if(purple_proxy_connect(NULL, NULL, addressOfControl, port,
- looked_up_internal_ip_cb, NULL) == NULL)
+ if(purple_proxy_connect(NULL, NULL, purple_http_url_get_host(url),
+ purple_http_url_get_port(url), looked_up_internal_ip_cb,
+ NULL) == NULL)
{
- purple_debug_error("upnp", "Get Local IP Connect Failed: Address: %s @@@ Port %d\n",
- addressOfControl, port);
+ purple_debug_error("upnp", "Get Local IP Connect Failed: "
+ "Address: %s @@@ Port %d\n",
+ purple_http_url_get_host(url),
+ purple_http_url_get_port(url));
}
- g_free(addressOfControl);
+ purple_http_url_free(url);
}
static void
-done_port_mapping_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
- const gchar *httpResponse, gsize len, const gchar *error_message)
+done_port_mapping_cb(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer user_data)
{
- UPnPMappingAddRemove *ar = user_data;
+ PurpleUPnPMappingAddRemove *ar = user_data;
gboolean success = TRUE;
/* determine if port mapping was a success */
- if ((error_message != NULL) || (httpResponse == NULL) ||
- (g_strstr_len(httpResponse, len, HTTP_OK) == NULL))
+ if (!purple_http_response_is_successful(response))
{
purple_debug_error("upnp",
"purple_upnp_set_port_mapping(): Failed HTTP_OK\n%s\n",
- httpResponse ? httpResponse : "(null)");
+ purple_http_response_get_error(response));
success = FALSE;
} else
purple_debug_info("upnp", "Successfully completed port mapping operation\n");
@@ -883,7 +830,7 @@ done_port_mapping_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
static void
do_port_mapping_cb(gboolean has_control_mapping, gpointer data)
{
- UPnPMappingAddRemove *ar = data;
+ PurpleUPnPMappingAddRemove *ar = data;
if (has_control_mapping) {
gchar action_name[25];
@@ -911,8 +858,8 @@ do_port_mapping_cb(gboolean has_control_mapping, gpointer data)
ar->portmap, ar->protocol);
}
- ar->gfud = purple_upnp_generate_action_message_and_send(action_name,
- action_params, done_port_mapping_cb, ar);
+ ar->hc = purple_upnp_generate_action_message_and_send(
+ action_name, action_params, done_port_mapping_cb, ar);
g_free(action_params);
return;
@@ -925,14 +872,14 @@ do_port_mapping_cb(gboolean has_control_mapping, gpointer data)
static gboolean
fire_port_mapping_failure_cb(gpointer data)
{
- UPnPMappingAddRemove *ar = data;
+ PurpleUPnPMappingAddRemove *ar = data;
ar->tima = 0;
do_port_mapping_cb(FALSE, data);
return FALSE;
}
-void purple_upnp_cancel_port_mapping(UPnPMappingAddRemove *ar)
+void purple_upnp_cancel_port_mapping(PurpleUPnPMappingAddRemove *ar)
{
GSList *l;
@@ -956,19 +903,18 @@ void purple_upnp_cancel_port_mapping(UPnPMappingAddRemove *ar)
if (ar->tima > 0)
purple_timeout_remove(ar->tima);
- if (ar->gfud)
- purple_util_fetch_url_cancel(ar->gfud);
+ purple_http_conn_cancel(ar->hc);
g_free(ar);
}
-UPnPMappingAddRemove *
+PurpleUPnPMappingAddRemove *
purple_upnp_set_port_mapping(unsigned short portmap, const gchar* protocol,
PurpleUPnPCallback cb, gpointer cb_data)
{
- UPnPMappingAddRemove *ar;
+ PurpleUPnPMappingAddRemove *ar;
- ar = g_new0(UPnPMappingAddRemove, 1);
+ ar = g_new0(PurpleUPnPMappingAddRemove, 1);
ar->cb = cb;
ar->cb_data = cb_data;
ar->add = TRUE;
@@ -1009,13 +955,13 @@ purple_upnp_set_port_mapping(unsigned short portmap, const gchar* protocol,
return ar;
}
-UPnPMappingAddRemove *
+PurpleUPnPMappingAddRemove *
purple_upnp_remove_port_mapping(unsigned short portmap, const char* protocol,
PurpleUPnPCallback cb, gpointer cb_data)
{
- UPnPMappingAddRemove *ar;
+ PurpleUPnPMappingAddRemove *ar;
- ar = g_new0(UPnPMappingAddRemove, 1);
+ ar = g_new0(PurpleUPnPMappingAddRemove, 1);
ar->cb = cb;
ar->cb_data = cb_data;
ar->add = FALSE;
diff --git a/libpurple/upnp.h b/libpurple/upnp.h
index b0e4ab4e47..8a19177f27 100644
--- a/libpurple/upnp.h
+++ b/libpurple/upnp.h
@@ -1,8 +1,3 @@
-/**
- * @file upnp.h Universal Plug N Play API
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -26,17 +21,20 @@
#ifndef _PURPLE_UPNP_H_
#define _PURPLE_UPNP_H_
+/**
+ * SECTION:upnp
+ * @section_id: libpurple-upnp
+ * @short_description: <filename>upnp.h</filename>
+ * @title: Universal Plug N Play API
+ */
-typedef struct _UPnPMappingAddRemove UPnPMappingAddRemove;
+typedef struct _PurpleUPnPMappingAddRemove PurpleUPnPMappingAddRemove;
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name UPnP API */
+/* UPnP API */
/**************************************************************************/
-/*@{*/
/* typedef struct _PurpleUPnPRequestData PurpleUPnPRequestData; */
@@ -44,89 +42,97 @@ typedef void (*PurpleUPnPCallback) (gboolean success, gpointer data);
/**
+ * purple_upnp_init:
+ *
* Initialize UPnP
*/
void purple_upnp_init(void);
/**
+ * 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
+ *
* Sends a discovery request to search for a UPnP enabled IGD that
* contains the WANIPConnection service that will allow us to recieve the
* public IP address of the IGD, and control it for forwarding ports.
* The result will be cached for further use.
- *
- * @param cb an optional callback function to be notified when the UPnP
- * discovery is complete
- * @param cb_data Extra data to be passed to the callback
*/
void purple_upnp_discover(PurpleUPnPCallback cb, gpointer cb_data);
#if 0
/**
+ * purple_upnp_get_control_info:
+ *
* Retrieve the current UPnP control info, if there is any available.
* This will only be filled in if purple_upnp_discover() had been called,
* and finished discovering.
*
- * @return The control URL for the IGD we'll use to use the IGD services
+ * Returns: The control URL for the IGD we'll use to use the IGD services
*/
const PurpleUPnPControlInfo* purple_upnp_get_control_info(void);
#endif
/**
+ * purple_upnp_get_public_ip:
+ *
* Gets the IP address from a UPnP enabled IGD that sits on the local
* network, so when getting the network IP, instead of returning the
* local network IP, the public IP is retrieved. This is a cached value from
* the time of the UPnP discovery.
*
- * @return The IP address of the network, or NULL if something went wrong
+ * Returns: The IP address of the network, or NULL if something went wrong
*/
const gchar* purple_upnp_get_public_ip(void);
/**
+ * purple_upnp_cancel_port_mapping:
+ * @mapping_data: The data returned when you initiated the UPnP mapping request.
+ *
* Cancel a pending port mapping request initiated with either
* purple_upnp_set_port_mapping() or purple_upnp_remove_port_mapping().
- *
- * @param mapping_data The data returned when you initiated the UPnP mapping request.
*/
-void purple_upnp_cancel_port_mapping(UPnPMappingAddRemove *mapping_data);
+void purple_upnp_cancel_port_mapping(PurpleUPnPMappingAddRemove *mapping_data);
/**
+ * 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
+ *
* 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
* forwarding so things like file transfers can work behind NAT firewalls
*
- * @param portmap The port to map to this client
- * @param protocol The protocol to map, either "TCP" or "UDP"
- * @param cb an optional callback function to be notified when the mapping
- * addition is complete
- * @param cb_data Extra data to be passed to the callback
- *
- * @return Data which can be passed to purple_upnp_port_mapping_cancel() to cancel
+ * Returns: Data which can be passed to purple_upnp_cancel_port_mapping() to
+ * cancel
*/
-UPnPMappingAddRemove *purple_upnp_set_port_mapping(unsigned short portmap, const gchar* protocol,
+PurpleUPnPMappingAddRemove *purple_upnp_set_port_mapping(unsigned short portmap, const gchar* protocol,
PurpleUPnPCallback cb, gpointer cb_data);
/**
+ * 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
+ *
* 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
* port forwarding after they have completed a connection so another client on
* the local network can take advantage of the port forwarding
*
- * @param portmap The port to delete the mapping for
- * @param protocol The protocol to map to. Either "TCP" or "UDP"
- * @param cb an optional callback function to be notified when the mapping
- * removal is complete
- * @param cb_data Extra data to be passed to the callback
- *
- * @return Data which can be passed to purple_upnp_port_mapping_cancel() to cancel
+ * Returns: Data which can be passed to purple_upnp_cancel_port_mapping() to
+ * cancel
*/
-UPnPMappingAddRemove *purple_upnp_remove_port_mapping(unsigned short portmap,
+PurpleUPnPMappingAddRemove *purple_upnp_remove_port_mapping(unsigned short portmap,
const gchar* protocol, PurpleUPnPCallback cb, gpointer cb_data);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_UPNP_H_ */
diff --git a/libpurple/util.c b/libpurple/util.c
index c39812ef20..850fa5b087 100644
--- a/libpurple/util.c
+++ b/libpurple/util.c
@@ -1,8 +1,3 @@
-/*
- * @file util.h Utility Functions
- * @ingroup core
- */
-
/* 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.
@@ -23,65 +18,32 @@
*/
#include "internal.h"
-#include "cipher.h"
+#include "ciphers/md5hash.h"
#include "conversation.h"
#include "core.h"
#include "debug.h"
+#include "glibcompat.h"
#include "notify.h"
-#include "ntlm.h"
#include "prpl.h"
#include "prefs.h"
#include "util.h"
-/* 512KiB Default value for maximum HTTP download size (when the client hasn't
- specified a length) */
-#define DEFAULT_MAX_HTTP_DOWNLOAD (512 * 1024)
+#include <json-glib/json-glib.h>
-#define MAX_HTTP_CHUNK_SIZE (10 * 1024 * 1024)
-
-struct _PurpleUtilFetchUrlData
+struct _PurpleMenuAction
{
- PurpleUtilFetchUrlCallback callback;
- void *user_data;
-
- struct
- {
- char *user;
- char *passwd;
- char *address;
- int port;
- char *page;
-
- } website;
-
- char *url;
- int num_times_redirected;
- gboolean full;
- char *user_agent;
- gboolean http11;
- char *request;
- gsize request_written;
- gboolean include_headers;
-
- gboolean is_ssl;
- PurpleSslConnection *ssl_connection;
- PurpleProxyConnectData *connect_data;
- int fd;
- guint inpa;
-
- gboolean got_headers;
- gboolean has_explicit_data_len;
- char *webdata;
- gsize len;
- unsigned long data_len;
- gsize max_len;
- gboolean chunked;
- PurpleAccount *account;
+ char *label;
+ PurpleCallback callback;
+ gpointer data;
+ GList *children;
+ gchar *stock_icon;
};
static char *custom_user_dir = NULL;
static char *user_dir = NULL;
+static JsonNode *escape_js_node = NULL;
+static JsonGenerator *escape_js_gen = NULL;
PurpleMenuAction *
purple_menu_action_new(const char *label, PurpleCallback callback, gpointer data,
@@ -100,15 +62,88 @@ purple_menu_action_free(PurpleMenuAction *act)
{
g_return_if_fail(act != NULL);
+ g_free(act->stock_icon);
g_free(act->label);
g_free(act);
}
+char * purple_menu_action_get_label(const PurpleMenuAction *act)
+{
+ g_return_val_if_fail(act != NULL, NULL);
+
+ return act->label;
+}
+
+PurpleCallback purple_menu_action_get_callback(const PurpleMenuAction *act)
+{
+ g_return_val_if_fail(act != NULL, NULL);
+
+ return act->callback;
+}
+
+gpointer purple_menu_action_get_data(const PurpleMenuAction *act)
+{
+ g_return_val_if_fail(act != NULL, NULL);
+
+ return act->data;
+}
+
+GList* purple_menu_action_get_children(const PurpleMenuAction *act)
+{
+ g_return_val_if_fail(act != NULL, NULL);
+
+ return act->children;
+}
+
+void purple_menu_action_set_label(PurpleMenuAction *act, char *label)
+{
+ g_return_if_fail(act != NULL);
+
+ act-> label = label;
+}
+
+void purple_menu_action_set_callback(PurpleMenuAction *act, PurpleCallback callback)
+{
+ g_return_if_fail(act != NULL);
+
+ act->callback = callback;
+}
+
+void purple_menu_action_set_data(PurpleMenuAction *act, gpointer data)
+{
+ g_return_if_fail(act != NULL);
+
+ act->data = data;
+}
+
+void purple_menu_action_set_children(PurpleMenuAction *act, GList *children)
+{
+ g_return_if_fail(act != NULL);
+
+ act->children = children;
+}
+
+void purple_menu_action_set_stock_icon(PurpleMenuAction *act,
+ const gchar *stock)
+{
+ g_return_if_fail(act != NULL);
+
+ g_free(act->stock_icon);
+ act->stock_icon = g_strdup(stock);
+}
+
+const gchar *
+purple_menu_action_get_stock_icon(PurpleMenuAction *act)
+{
+ return act->stock_icon;
+}
+
void
purple_util_init(void)
{
- /* This does nothing right now. It exists for symmetry with
- * purple_util_uninit() and forwards compatibility. */
+ escape_js_node = json_node_new(JSON_NODE_VALUE);
+ escape_js_gen = json_generator_new();
+ json_node_set_boolean(escape_js_node, FALSE);
}
void
@@ -121,6 +156,12 @@ purple_util_uninit(void)
g_free(user_dir);
user_dir = NULL;
+
+ json_node_free(escape_js_node);
+ escape_js_node = NULL;
+
+ g_object_unref(escape_js_gen);
+ escape_js_gen = NULL;
}
/**************************************************************************
@@ -129,7 +170,7 @@ purple_util_uninit(void)
gchar *
purple_base16_encode(const guchar *data, gsize len)
{
- int i;
+ gsize i;
gchar *ascii = NULL;
g_return_val_if_fail(data != NULL, NULL);
@@ -138,7 +179,7 @@ purple_base16_encode(const guchar *data, gsize len)
ascii = g_malloc(len * 2 + 1);
for (i = 0; i < len; i++)
- g_snprintf(&ascii[i * 2], 3, "%02hhx", data[i]);
+ g_snprintf(&ascii[i * 2], 3, "%02x", data[i] & 0xFF);
return ascii;
}
@@ -153,7 +194,7 @@ purple_base16_decode(const char *str, gsize *ret_len)
len = strlen(str);
- g_return_val_if_fail(strlen(str) > 0, 0);
+ g_return_val_if_fail(*str, 0);
g_return_val_if_fail(len % 2 == 0, 0);
data = g_malloc(len / 2);
@@ -193,7 +234,7 @@ purple_base16_decode(const char *str, gsize *ret_len)
gchar *
purple_base16_encode_chunked(const guchar *data, gsize len)
{
- int i;
+ gsize i;
gchar *ascii = NULL;
g_return_val_if_fail(data != NULL, NULL);
@@ -206,7 +247,7 @@ purple_base16_encode_chunked(const guchar *data, gsize len)
ascii = g_malloc(len * 3 + 1);
for (i = 0; i < len; i++)
- g_snprintf(&ascii[i * 3], 4, "%02hhx:", data[i]);
+ g_snprintf(&ascii[i * 3], 4, "%02x:", data[i] & 0xFF);
/* Replace the final colon with NULL */
ascii[len * 3 - 1] = 0;
@@ -466,16 +507,16 @@ const char *purple_get_tzoff_str(const struct tm *tm, gboolean iso)
#ifdef _WIN32
if ((off = wpurple_get_tz_offset()) == -1)
return "";
-#else
-# ifdef HAVE_TM_GMTOFF
+#elif defined(HAVE_TM_GMTOFF)
off = new_tm.tm_gmtoff;
-# else
-# ifdef HAVE_TIMEZONE
+#elif defined(HAVE_TIMEZONE)
tzset();
off = -1 * timezone;
-# endif /* HAVE_TIMEZONE */
-# endif /* !HAVE_TM_GMTOFF */
-#endif /* _WIN32 */
+#else
+ purple_debug_warning("util",
+ "there is no possibility to obtain tz offset");
+ return "";
+#endif
min = (off / 60) % 60;
hrs = ((off / 60) - min) / 60;
@@ -525,10 +566,10 @@ static size_t purple_internal_strftime(char *s, size_t max, const char *format,
if (*c == 'z')
{
char *tmp = g_strdup_printf("%s%.*s%s",
- fmt ? fmt : "",
- c - start - 1,
- start,
- purple_get_tzoff_str(tm, FALSE));
+ fmt ? fmt : "",
+ (int)(c - start - 1),
+ start,
+ purple_get_tzoff_str(tm, FALSE));
g_free(fmt);
fmt = tmp;
start = c + 1;
@@ -538,10 +579,10 @@ static size_t purple_internal_strftime(char *s, size_t max, const char *format,
if (*c == 'Z')
{
char *tmp = g_strdup_printf("%s%.*s%s",
- fmt ? fmt : "",
- c - start - 1,
- start,
- wpurple_get_timezone_abbreviation(tm));
+ fmt ? fmt : "",
+ (int)(c - start - 1),
+ start,
+ wpurple_get_timezone_abbreviation(tm));
g_free(fmt);
fmt = tmp;
start = c + 1;
@@ -618,7 +659,7 @@ purple_utf8_strftime(const char *format, const struct tm *tm)
}
else
{
- purple_strlcpy(buf, utf8);
+ g_strlcpy(buf, utf8, sizeof(buf));
g_free(utf8);
}
@@ -874,6 +915,365 @@ purple_str_to_time(const char *timestamp, gboolean utc,
return retval;
}
+char *
+purple_uts35_to_str(const char *format, size_t len, struct tm *tm)
+{
+ GString *string;
+ guint i, count;
+
+ if (tm == NULL) {
+ time_t now = time(NULL);
+ tm = localtime(&now);
+ }
+
+ string = g_string_sized_new(len);
+ i = 0;
+ while (i < len) {
+ count = 1;
+ while ((i + count) < len && format[i] == format[i+count])
+ count++;
+
+ switch (format[i]) {
+ /* Era Designator */
+ case 'G':
+ if (count <= 3) {
+ /* Abbreviated */
+ } else if (count == 4) {
+ /* Full */
+ } else if (count >= 5) {
+ /* Narrow */
+ count = 5;
+ }
+ break;
+
+
+ /* Year */
+ case 'y':
+ if (count == 2) {
+ /* Two-digits only */
+ g_string_append(string, purple_utf8_strftime("%y", tm));
+ } else {
+ /* Zero-padding */
+ char *tmp = g_strdup_printf("%%0%dY", count);
+ g_string_append(string, purple_utf8_strftime(tmp, tm));
+ g_free(tmp);
+ }
+ break;
+
+ /* Year (in "Week of Year" based calendars) */
+ case 'Y':
+ if (count == 2) {
+ /* Two-digits only */
+ } else {
+ /* Zero-padding */
+ }
+ break;
+
+ /* Extended Year */
+ case 'u':
+ break;
+
+ /* Cyclic Year Name */
+ case 'U':
+ if (count <= 3) {
+ /* Abbreviated */
+ } else if (count == 4) {
+ /* Full */
+ } else if (count >= 5) {
+ /* Narrow */
+ count = 5;
+ }
+ break;
+
+
+ /* Quarter */
+ case 'Q':
+ 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) {
+ /* Numerical */
+ } else if (count == 3) {
+ /* Abbreviation */
+ } else if (count >= 4) {
+ /* Full */
+ count = 4;
+ }
+ break;
+
+ /* 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) {
+ /* Numerical */
+ g_string_append(string, purple_utf8_strftime("%m", tm));
+ } else if (count == 3) {
+ /* Abbreviation */
+ g_string_append(string, purple_utf8_strftime("%b", tm));
+ } else if (count == 4) {
+ /* Full */
+ g_string_append(string, purple_utf8_strftime("%B", tm));
+ } else if (count >= 5) {
+ g_string_append_len(string, purple_utf8_strftime("%b", tm), 1);
+ count = 5;
+ }
+ break;
+
+ /* Ignored */
+ case 'l':
+ break;
+
+
+ /* Week of Year */
+ case 'w':
+ g_string_append(string, purple_utf8_strftime("%W", tm));
+ count = MIN(count, 2);
+ break;
+
+ /* Week of Month */
+ case 'W':
+ count = 1;
+ break;
+
+
+ /* Day of Month */
+ case 'd':
+ g_string_append(string, purple_utf8_strftime("%d", tm));
+ count = MIN(count, 2);
+ break;
+
+ /* Day of Year */
+ case 'D':
+ g_string_append(string, purple_utf8_strftime("%j", tm));
+ count = MIN(count, 3);
+ break;
+
+ /* Day of Year in Month */
+ case 'F':
+ count = 1;
+ break;
+
+ /* Modified Julian Day */
+ case 'g':
+ break;
+
+
+ /* Day of Week */
+ case 'E':
+ if (count <= 3) {
+ /* Short */
+ g_string_append(string, purple_utf8_strftime("%a", tm));
+ } else if (count == 4) {
+ /* Full */
+ g_string_append(string, purple_utf8_strftime("%A", tm));
+ } else if (count >= 5) {
+ /* Narrow */
+ g_string_append_len(string, purple_utf8_strftime("%a", tm), 1);
+ count = 5;
+ }
+ break;
+
+ /* Local Day of Week */
+ case 'e':
+ if (count <= 2) {
+ /* Numeric */
+ g_string_append(string, purple_utf8_strftime("%u", tm));
+ } else if (count == 3) {
+ /* Short */
+ g_string_append(string, purple_utf8_strftime("%a", tm));
+ } else if (count == 4) {
+ /* Full */
+ g_string_append(string, purple_utf8_strftime("%A", tm));
+ } else if (count >= 5) {
+ /* Narrow */
+ g_string_append_len(string, purple_utf8_strftime("%a", tm), 1);
+ count = 5;
+ }
+ break;
+
+ /* Stand-alone Local Day of Week */
+ case 'c':
+ if (count <= 2) {
+ /* Numeric */
+ g_string_append(string, purple_utf8_strftime("%u", tm));
+ count = 1;
+ } else if (count == 3) {
+ /* Short */
+ g_string_append(string, purple_utf8_strftime("%a", tm));
+ } else if (count == 4) {
+ /* Full */
+ g_string_append(string, purple_utf8_strftime("%A", tm));
+ } else if (count >= 5) {
+ /* Narrow */
+ g_string_append_len(string, purple_utf8_strftime("%a", tm), 1);
+ count = 5;
+ }
+ break;
+
+
+ /* AM/PM */
+ case 'a':
+ g_string_append(string, purple_utf8_strftime("%p", tm));
+ break;
+
+
+ /* Hour (1-12) */
+ case 'h':
+ if (count == 1) {
+ /* No padding */
+ g_string_append(string, purple_utf8_strftime("%I", tm));
+ } else if (count >= 2) {
+ /* Zero-padded */
+ g_string_append(string, purple_utf8_strftime("%I", tm));
+ count = 2;
+ }
+ break;
+
+ /* Hour (0-23) */
+ case 'H':
+ if (count == 1) {
+ /* No padding */
+ g_string_append(string, purple_utf8_strftime("%H", tm));
+ } else if (count >= 2) {
+ /* Zero-padded */
+ g_string_append(string, purple_utf8_strftime("%H", tm));
+ count = 2;
+ }
+ break;
+
+ /* Hour (0-11) */
+ case 'K':
+ if (count == 1) {
+ /* No padding */
+ } else if (count >= 2) {
+ /* Zero-padded */
+ count = 2;
+ }
+ break;
+
+ /* Hour (1-24) */
+ case 'k':
+ if (count == 1) {
+ /* No padding */
+ } else if (count >= 2) {
+ /* Zero-padded */
+ count = 2;
+ }
+ break;
+
+ /* Hour (hHkK by locale) */
+ case 'j':
+ break;
+
+
+ /* Minute */
+ case 'm':
+ g_string_append(string, purple_utf8_strftime("%M", tm));
+ count = MIN(count, 2);
+ break;
+
+
+ /* Second */
+ case 's':
+ g_string_append(string, purple_utf8_strftime("%S", tm));
+ count = MIN(count, 2);
+ break;
+
+ /* Fractional Sub-second */
+ case 'S':
+ break;
+
+ /* Millisecond */
+ case 'A':
+ break;
+
+
+ /* Time Zone (specific non-location format) */
+ case 'z':
+ if (count <= 3) {
+ /* Short */
+ } else if (count >= 4) {
+ /* Full */
+ count = 4;
+ }
+ break;
+
+ /* Time Zone */
+ case 'Z':
+ if (count <= 3) {
+ /* RFC822 */
+ g_string_append(string, purple_utf8_strftime("%z", tm));
+ } else if (count == 4) {
+ /* Localized GMT */
+ } else if (count >= 5) {
+ /* ISO8601 */
+ g_string_append(string, purple_utf8_strftime("%z", tm));
+ count = 5;
+ }
+ break;
+
+ /* Time Zone (generic non-location format) */
+ case 'v':
+ if (count <= 3) {
+ /* Short */
+ g_string_append(string, purple_utf8_strftime("%Z", tm));
+ count = 1;
+ } else if (count >= 4) {
+ /* Long */
+ g_string_append(string, purple_utf8_strftime("%Z", tm));
+ count = 4;
+ }
+ break;
+
+ /* Time Zone */
+ case 'V':
+ if (count <= 3) {
+ /* Same as z */
+ count = 1;
+ } else if (count >= 4) {
+ /* Generic Location Format) */
+ g_string_append(string, purple_utf8_strftime("%Z", tm));
+ count = 4;
+ }
+ break;
+
+
+ default:
+ g_string_append_len(string, format + i, count);
+ break;
+ }
+
+ i += count;
+ }
+
+ return g_string_free(string, FALSE);
+}
+
/**************************************************************************
* Markup Functions
**************************************************************************/
@@ -1333,7 +1733,7 @@ purple_markup_extract_info_field(const char *str, int len, PurpleNotifyUserInfo
g_string_append_len(dest, p, q - p);
}
- purple_notify_user_info_add_pair(user_info, display_name, dest->str);
+ purple_notify_user_info_add_pair_html(user_info, display_name, dest->str);
g_string_free(dest, TRUE);
return TRUE;
@@ -1480,7 +1880,7 @@ purple_markup_html_to_xhtml(const char *html, char **xhtml_out,
if (cdata && url &&
(!g_string_equal(cdata, url) && (g_ascii_strncasecmp(url->str, "mailto:", 7) != 0 ||
g_utf8_collate(url->str + 7, cdata->str) != 0)))
- g_string_append_printf(plain, " <%s>", g_strstrip(url->str));
+ g_string_append_printf(plain, " <%s>", g_strstrip(purple_unescape_html(url->str)));
if (cdata) {
g_string_free(cdata, TRUE);
cdata = NULL;
@@ -1611,33 +2011,39 @@ purple_markup_html_to_xhtml(const char *html, char **xhtml_out,
if (!g_ascii_strncasecmp(c, "<img", 4) && (*(c+4) == '>' || *(c+4) == ' ')) {
const char *p = c + 4;
GString *src = NULL, *alt = NULL;
+#define ESCAPE(from, to) \
+ CHECK_QUOTE(from); \
+ while (VALID_CHAR(from)) { \
+ int len; \
+ if ((*from == '&') && (purple_markup_unescape_entity(from, &len) == NULL)) \
+ to = g_string_append(to, "&amp;"); \
+ else if (*from == '\'') \
+ to = g_string_append(to, "&apos;"); \
+ else \
+ to = g_string_append_c(to, *from); \
+ from++; \
+ }
+
while (*p && *p != '>') {
if (!g_ascii_strncasecmp(p, "src=", 4)) {
const char *q = p + 4;
if (src)
g_string_free(src, TRUE);
src = g_string_new("");
- CHECK_QUOTE(q);
- while (VALID_CHAR(q)) {
- src = g_string_append_c(src, *q);
- q++;
- }
+ ESCAPE(q, src);
p = q;
} else if (!g_ascii_strncasecmp(p, "alt=", 4)) {
const char *q = p + 4;
if (alt)
g_string_free(alt, TRUE);
alt = g_string_new("");
- CHECK_QUOTE(q);
- while (VALID_CHAR(q)) {
- alt = g_string_append_c(alt, *q);
- q++;
- }
+ ESCAPE(q, alt);
p = q;
} else {
p++;
}
}
+#undef ESCAPE
if ((c = strchr(p, '>')) != NULL)
c++;
else
@@ -1647,7 +2053,7 @@ purple_markup_html_to_xhtml(const char *html, char **xhtml_out,
g_string_append_printf(xhtml, "<img src='%s' alt='%s' />", g_strstrip(src->str), alt ? alt->str : "");
if(alt) {
if(plain)
- plain = g_string_append(plain, alt->str);
+ plain = g_string_append(plain, purple_unescape_html(alt->str));
if(!src && xhtml)
xhtml = g_string_append(xhtml, alt->str);
g_string_free(alt, TRUE);
@@ -1672,6 +2078,8 @@ purple_markup_html_to_xhtml(const char *html, char **xhtml_out,
int len;
if ((*q == '&') && (purple_markup_unescape_entity(q, &len) == NULL))
url = g_string_append(url, "&amp;");
+ else if (*q == '"')
+ url = g_string_append(url, "&quot;");
else
url = g_string_append_c(url, *q);
q++;
@@ -1693,6 +2101,18 @@ purple_markup_html_to_xhtml(const char *html, char **xhtml_out,
g_string_append_printf(xhtml, "<a href=\"%s\">", url ? g_strstrip(url->str) : "");
continue;
}
+#define ESCAPE(from, to) \
+ CHECK_QUOTE(from); \
+ while (VALID_CHAR(from)) { \
+ int len; \
+ if ((*from == '&') && (purple_markup_unescape_entity(from, &len) == NULL)) \
+ to = g_string_append(to, "&amp;"); \
+ else if (*from == '\'') \
+ to = g_string_append_c(to, '\"'); \
+ else \
+ to = g_string_append_c(to, *from); \
+ from++; \
+ }
if(!g_ascii_strncasecmp(c, "<font", 5) && (*(c+5) == '>' || *(c+5) == ' ')) {
const char *p = c + 5;
GString *style = g_string_new("");
@@ -1701,33 +2121,21 @@ purple_markup_html_to_xhtml(const char *html, char **xhtml_out,
if (!g_ascii_strncasecmp(p, "back=", 5)) {
const char *q = p + 5;
GString *color = g_string_new("");
- CHECK_QUOTE(q);
- while (VALID_CHAR(q)) {
- color = g_string_append_c(color, *q);
- q++;
- }
+ ESCAPE(q, color);
g_string_append_printf(style, "background: %s; ", color->str);
g_string_free(color, TRUE);
p = q;
} else if (!g_ascii_strncasecmp(p, "color=", 6)) {
const char *q = p + 6;
GString *color = g_string_new("");
- CHECK_QUOTE(q);
- while (VALID_CHAR(q)) {
- color = g_string_append_c(color, *q);
- q++;
- }
+ ESCAPE(q, color);
g_string_append_printf(style, "color: %s; ", color->str);
g_string_free(color, TRUE);
p = q;
} else if (!g_ascii_strncasecmp(p, "face=", 5)) {
const char *q = p + 5;
GString *face = g_string_new("");
- CHECK_QUOTE(q);
- while (VALID_CHAR(q)) {
- face = g_string_append_c(face, *q);
- q++;
- }
+ ESCAPE(q, face);
g_string_append_printf(style, "font-family: %s; ", g_strstrip(face->str));
g_string_free(face, TRUE);
p = q;
@@ -1782,6 +2190,7 @@ purple_markup_html_to_xhtml(const char *html, char **xhtml_out,
g_string_free(style, TRUE);
continue;
}
+#undef ESCAPE
if (!g_ascii_strncasecmp(c, "<body ", 6)) {
const char *p = c + 6;
gboolean did_something = FALSE;
@@ -1997,9 +2406,9 @@ purple_markup_strip_html(const char *str)
size_t hrlen = strlen(href);
/* Only insert the href if it's different from the CDATA. */
- if ((hrlen != j - href_st ||
+ if ((hrlen != (gsize)(j - href_st) ||
strncmp(str2 + href_st, href, hrlen)) &&
- (hrlen != j - href_st + 7 || /* 7 == strlen("http://") */
+ (hrlen != (gsize)(j - href_st + 7) || /* 7 == strlen("http://") */
strncmp(str2 + href_st, href + 7, hrlen - 7)))
{
str2[j++] = ' ';
@@ -2279,7 +2688,7 @@ purple_markup_linkify(const char *text)
url_buf = g_string_free(gurl_buf, FALSE);
/* strip off trailing periods */
- if (strlen(url_buf) > 0) {
+ if (*url_buf) {
for (d = url_buf + strlen(url_buf) - 1; *d == '.'; d--, t--)
*d = '\0';
}
@@ -2504,7 +2913,7 @@ purple_home_dir(void)
#ifndef _WIN32
return g_get_home_dir();
#else
- return wpurple_data_dir();
+ return wpurple_home_dir();
#endif
}
@@ -2549,7 +2958,7 @@ purple_util_write_data_to_file(const char *filename, const char *data, gssize si
g_return_val_if_fail(user_dir != NULL, FALSE);
- purple_debug_info("util", "Writing file %s to directory %s\n",
+ purple_debug_misc("util", "Writing file %s to directory %s",
filename, user_dir);
/* Ensure the user directory exists */
@@ -2576,13 +2985,13 @@ purple_util_write_data_to_file_absolute(const char *filename_full, const char *d
{
gchar *filename_temp;
FILE *file;
- size_t real_size, byteswritten;
- struct stat st;
+ gsize real_size, byteswritten;
+ GStatBuf st;
#ifndef HAVE_FILENO
int fd;
#endif
- purple_debug_info("util", "Writing file %s\n",
+ purple_debug_misc("util", "Writing file %s",
filename_full);
g_return_val_if_fail((size >= -1), FALSE);
@@ -2701,12 +3110,9 @@ purple_util_write_data_to_file_absolute(const char *filename_full, const char *d
* It causes TOCTOU coverity warning (against g_rename below),
* but it's not a threat for us.
*/
- if ((g_stat(filename_temp, &st) == -1) || (st.st_size != real_size))
- {
+ if ((g_stat(filename_temp, &st) == -1) || ((gsize)st.st_size != real_size)) {
purple_debug_error("util", "Error writing data to file %s: "
- "Incomplete file written; is your disk "
- "full?\n",
- filename_temp);
+ "couldn't g_stat file", filename_temp);
g_free(filename_temp);
return FALSE;
}
@@ -2725,10 +3131,10 @@ purple_util_write_data_to_file_absolute(const char *filename_full, const char *d
return TRUE;
}
-xmlnode *
+PurpleXmlNode *
purple_util_read_xml_from_file(const char *filename, const char *description)
{
- return xmlnode_from_file(purple_user_dir(), filename, description, "util");
+ return purple_xmlnode_from_file(purple_user_dir(), filename, description, "util");
}
/*
@@ -2780,72 +3186,6 @@ purple_mkstemp(char **fpath, gboolean binary)
return fp;
}
-const char *
-purple_util_get_image_extension(gconstpointer data, size_t len)
-{
- g_return_val_if_fail(data != NULL, NULL);
- g_return_val_if_fail(len > 0, NULL);
-
- if (len >= 4)
- {
- if (!strncmp((char *)data, "GIF8", 4))
- return "gif";
- else if (!strncmp((char *)data, "\xff\xd8\xff", 3)) /* 4th may be e0 through ef */
- return "jpg";
- else if (!strncmp((char *)data, "\x89PNG", 4))
- return "png";
- else if (!strncmp((char *)data, "MM", 2) ||
- !strncmp((char *)data, "II", 2))
- return "tif";
- else if (!strncmp((char *)data, "BM", 2))
- return "bmp";
- }
-
- return "icon";
-}
-
-/*
- * We thought about using non-cryptographic hashes like CRC32 here.
- * They would be faster, but we think using something more secure is
- * important, so that it is more difficult for someone to maliciously
- * replace one buddy's icon with something else.
- */
-char *
-purple_util_get_image_checksum(gconstpointer image_data, size_t image_len)
-{
- PurpleCipherContext *context;
- gchar digest[41];
-
- context = purple_cipher_context_new_by_name("sha1", NULL);
- if (context == NULL)
- {
- purple_debug_error("util", "Could not find sha1 cipher\n");
- g_return_val_if_reached(NULL);
- }
-
- /* Hash the image data */
- purple_cipher_context_append(context, image_data, image_len);
- if (!purple_cipher_context_digest_to_str(context, sizeof(digest), digest, NULL))
- {
- purple_debug_error("util", "Failed to get SHA-1 digest.\n");
- g_return_val_if_reached(NULL);
- }
- purple_cipher_context_destroy(context);
-
- return g_strdup(digest);
-}
-
-char *
-purple_util_get_image_filename(gconstpointer image_data, size_t image_len)
-{
- /* Return the filename */
- char *checksum = purple_util_get_image_checksum(image_data, image_len);
- char *filename = g_strdup_printf("%s.%s", checksum,
- purple_util_get_image_extension(image_data, image_len));
- g_free(checksum);
- return filename;
-}
-
gboolean
purple_program_is_valid(const char *program)
{
@@ -3005,7 +3345,7 @@ purple_socket_speaks_ipv4(int fd)
case AF_INET6:
{
int val = 0;
- guint len = sizeof(val);
+ socklen_t len = sizeof(val);
if (getsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, &len) != 0)
return FALSE;
@@ -3023,12 +3363,7 @@ purple_socket_speaks_ipv4(int fd)
gboolean
purple_strequal(const gchar *left, const gchar *right)
{
-#if GLIB_CHECK_VERSION(2,16,0)
return (g_strcmp0(left, right) == 0);
-#else
- return ((left == NULL && right == NULL) ||
- (left != NULL && right != NULL && strcmp(left, right) == 0));
-#endif
}
const char *
@@ -3089,6 +3424,28 @@ purple_normalize_nocase(const PurpleAccount *account, const char *str)
return buf;
}
+gboolean
+purple_validate(const PurplePlugin *prpl, const char *str)
+{
+ PurplePluginProtocolInfo *prpl_info;
+ const char *normalized;
+
+ g_return_val_if_fail(prpl != NULL, FALSE);
+ g_return_val_if_fail(str != NULL, FALSE);
+
+ if (str[0] == '\0')
+ return FALSE;
+
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+
+ if (!prpl_info->normalize)
+ return TRUE;
+
+ normalized = prpl_info->normalize(NULL, str);
+
+ return (NULL != normalized);
+}
+
gchar *
purple_strdup_withhtml(const gchar *src)
{
@@ -3130,6 +3487,15 @@ purple_str_has_prefix(const char *s, const char *p)
}
gboolean
+purple_str_has_caseprefix(const gchar *s, const gchar *p)
+{
+ g_return_val_if_fail(s, FALSE);
+ g_return_val_if_fail(p, FALSE);
+
+ return (g_ascii_strncasecmp(s, p, strlen(p)) == 0);
+}
+
+gboolean
purple_str_has_suffix(const char *s, const char *x)
{
return g_str_has_suffix(s, x);
@@ -3300,11 +3666,11 @@ purple_strcasestr(const char *haystack, const char *needle)
}
char *
-purple_str_size_to_units(size_t size)
+purple_str_size_to_units(goffset size)
{
- static const char * const size_str[] = { "bytes", "KiB", "MiB", "GiB" };
+ static const char * const size_str[] = { "bytes", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB" };
float size_mag;
- int size_index = 0;
+ gsize size_index = 0;
if (size == -1) {
return g_strdup(_("Calculating..."));
@@ -3315,15 +3681,15 @@ purple_str_size_to_units(size_t size)
else {
size_mag = (float)size;
- while ((size_index < 3) && (size_mag > 1024)) {
+ 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_GSIZE_FORMAT " %s", size, size_str[size_index]);
+ 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]);
+ return g_strdup_printf("%.2f %s", size_mag, _(size_str[size_index]));
}
}
}
@@ -3344,7 +3710,7 @@ purple_str_seconds_to_string(guint secs)
hrs = secs / (60 * 60);
secs = secs % (60 * 60);
mins = secs / 60;
- secs = secs % 60;
+ /* secs = secs % 60; */
if (days > 0)
{
@@ -3395,7 +3761,7 @@ purple_str_binary_to_ascii(const unsigned char *binary, guint len)
for (i = 0; i < len; i++)
if (binary[i] < 32 || binary[i] > 126)
- g_string_append_printf(ret, "\\x%02hhx", binary[i]);
+ g_string_append_printf(ret, "\\x%02x", binary[i] & 0xFF);
else if (binary[i] == '\\')
g_string_append(ret, "\\\\");
else
@@ -3404,6 +3770,41 @@ purple_str_binary_to_ascii(const unsigned char *binary, guint len)
return g_string_free(ret, FALSE);
}
+size_t
+purple_utf16_size(const gunichar2 *str)
+{
+ /* UTF16 cannot contain two consequent NUL bytes starting at even
+ * position - see Unicode standards Chapter 3.9 D91 or RFC2781
+ * Chapter 2.
+ */
+
+ size_t i = 0;
+
+ g_return_val_if_fail(str != NULL, 0);
+
+ while (str[i++]);
+
+ return i * sizeof(gunichar2);
+}
+
+void
+purple_str_wipe(gchar *str)
+{
+ if (str == NULL)
+ return;
+ memset(str, 0, strlen(str));
+ g_free(str);
+}
+
+void
+purple_utf16_wipe(gunichar2 *str)
+{
+ if (str == NULL)
+ return;
+ memset(str, 0, purple_utf16_size(str));
+ g_free(str);
+}
+
/**************************************************************************
* URI/URL Functions
**************************************************************************/
@@ -3415,13 +3816,13 @@ void purple_got_protocol_handler_uri(const char *uri)
const char *tmp, *param_string;
char *cmd;
GHashTable *params = NULL;
- int len;
+ gsize len;
if (!(tmp = strchr(uri, ':')) || tmp == uri) {
purple_debug_error("util", "Malformed protocol handler message - missing protocol.\n");
return;
}
- len = MIN(sizeof(proto) - 1, (tmp - uri));
+ len = MIN(sizeof(proto) - 1, (gsize)(tmp - uri));
strncpy(proto, uri, len);
proto[len] = '\0';
@@ -3448,8 +3849,12 @@ void purple_got_protocol_handler_uri(const char *uri)
while (*tmp || *pairstart) {
if (*tmp == delimiter || !(*tmp)) {
/* If there is no explicit value */
- if (keyend == NULL)
+ if (keyend == NULL) {
keyend = tmp;
+ }
+ /* without these brackets, clang won't
+ * recognize tmp as a non-NULL
+ */
if (keyend && keyend != pairstart) {
char *p;
@@ -3479,812 +3884,6 @@ void purple_got_protocol_handler_uri(const char *uri)
g_hash_table_destroy(params);
}
-/*
- * TODO: Should probably add a "gboolean *ret_ishttps" parameter that
- * is set to TRUE if this URL is https, otherwise it is set to
- * FALSE. But that change will break the API.
- *
- * This is important for Yahoo! web messenger login. They now
- * force https login, and if you access the web messenger login
- * page via http then it redirects you to the https version, but
- * purple_util_fetch_url() ignores the "https" and attempts to
- * fetch the URL via http again, which gets redirected again.
- */
-gboolean
-purple_url_parse(const char *url, char **ret_host, int *ret_port,
- char **ret_path, char **ret_user, char **ret_passwd)
-{
- gboolean is_https = FALSE;
- const char * scan_info;
- char port_str[6];
- int f;
- const char *at, *slash;
- const char *turl;
- char host[256], path[256], user[256], passwd[256];
- int port = 0;
- /* hyphen at end includes it in control set */
-
-#define ADDR_CTRL "A-Za-z0-9.-"
-#define PORT_CTRL "0-9"
-#define PAGE_CTRL "A-Za-z0-9.~_/:*!@&%%?=+^-"
-#define USER_CTRL "A-Za-z0-9.~_/*!&%%?=+^-"
-#define PASSWD_CTRL "A-Za-z0-9.~_/*!&%%?=+^-"
-
- g_return_val_if_fail(url != NULL, FALSE);
-
- if ((turl = purple_strcasestr(url, "http://")) != NULL)
- {
- turl += 7;
- url = turl;
- }
- else if ((turl = purple_strcasestr(url, "https://")) != NULL)
- {
- is_https = TRUE;
- turl += 8;
- url = turl;
- }
-
- /* parse out authentication information if supplied */
- /* Only care about @ char BEFORE the first / */
- at = strchr(url, '@');
- slash = strchr(url, '/');
- f = 0;
- if (at && (!slash || at < slash)) {
- scan_info = "%255[" USER_CTRL "]:%255[" PASSWD_CTRL "]^@";
- f = sscanf(url, scan_info, user, passwd);
-
- if (f == 1) {
- /* No passwd, possibly just username supplied */
- scan_info = "%255[" USER_CTRL "]^@";
- f = sscanf(url, scan_info, user);
- }
-
- url = at+1; /* move pointer after the @ char */
- }
-
- if (f < 1) {
- *user = '\0';
- *passwd = '\0';
- } else if (f == 1)
- *passwd = '\0';
-
- scan_info = "%255[" ADDR_CTRL "]:%5[" PORT_CTRL "]/%255[" PAGE_CTRL "]";
- f = sscanf(url, scan_info, host, port_str, path);
-
- if (f == 1)
- {
- scan_info = "%255[" ADDR_CTRL "]/%255[" PAGE_CTRL "]";
- f = sscanf(url, scan_info, host, path);
- /* Use the default port */
- if (is_https)
- g_snprintf(port_str, sizeof(port_str), "443");
- else
- g_snprintf(port_str, sizeof(port_str), "80");
- }
-
- if (f == 0)
- *host = '\0';
-
- if (f <= 1)
- *path = '\0';
-
- if (sscanf(port_str, "%d", &port) != 1)
- purple_debug_error("util", "Error parsing URL port from %s\n", url);
-
- if (ret_host != NULL) *ret_host = g_strdup(host);
- if (ret_port != NULL) *ret_port = port;
- if (ret_path != NULL) *ret_path = g_strdup(path);
- if (ret_user != NULL) *ret_user = g_strdup(user);
- if (ret_passwd != NULL) *ret_passwd = g_strdup(passwd);
-
- return ((*host != '\0') ? TRUE : FALSE);
-
-#undef ADDR_CTRL
-#undef PORT_CTRL
-#undef PAGE_CTRL
-#undef USER_CTRL
-#undef PASSWD_CTRL
-}
-
-/**
- * The arguments to this function are similar to printf.
- */
-static void
-purple_util_fetch_url_error(PurpleUtilFetchUrlData *gfud, const char *format, ...)
-{
- gchar *error_message;
- va_list args;
-
- va_start(args, format);
- error_message = g_strdup_vprintf(format, args);
- va_end(args);
-
- gfud->callback(gfud, gfud->user_data, NULL, 0, error_message);
- g_free(error_message);
- purple_util_fetch_url_cancel(gfud);
-}
-
-static void url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message);
-static void ssl_url_fetch_connect_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond);
-static void ssl_url_fetch_error_cb(PurpleSslConnection *ssl_connection, PurpleSslErrorType error, gpointer data);
-
-static gboolean
-parse_redirect(const char *data, gsize data_len,
- PurpleUtilFetchUrlData *gfud)
-{
- gchar *s;
- gchar *new_url, *temp_url, *end;
- gboolean full;
- int len;
-
- if ((s = g_strstr_len(data, data_len, "\nLocation: ")) == NULL)
- /* We're not being redirected */
- return FALSE;
-
- s += strlen("Location: ");
- end = strchr(s, '\r');
-
- /* Just in case :) */
- if (end == NULL)
- end = strchr(s, '\n');
-
- if (end == NULL)
- return FALSE;
-
- len = end - s;
-
- new_url = g_malloc(len + 1);
- strncpy(new_url, s, len);
- new_url[len] = '\0';
-
- full = gfud->full;
-
- if (*new_url == '/' || g_strstr_len(new_url, len, "://") == NULL)
- {
- temp_url = new_url;
-
- new_url = g_strdup_printf("%s:%d%s", gfud->website.address,
- gfud->website.port, temp_url);
-
- g_free(temp_url);
-
- full = FALSE;
- }
-
- purple_debug_info("util", "Redirecting to %s\n", new_url);
-
- gfud->num_times_redirected++;
- if (gfud->num_times_redirected >= 5)
- {
- purple_util_fetch_url_error(gfud,
- _("Could not open %s: Redirected too many times"),
- gfud->url);
- return TRUE;
- }
-
- /*
- * Try again, with this new location. This code is somewhat
- * ugly, but we need to reuse the gfud because whoever called
- * us is holding a reference to it.
- */
- g_free(gfud->url);
- gfud->url = new_url;
- gfud->full = full;
- g_free(gfud->request);
- gfud->request = NULL;
-
- if (gfud->is_ssl) {
- gfud->is_ssl = FALSE;
- purple_ssl_close(gfud->ssl_connection);
- gfud->ssl_connection = NULL;
- } else {
- purple_input_remove(gfud->inpa);
- gfud->inpa = 0;
- close(gfud->fd);
- gfud->fd = -1;
- }
- gfud->request_written = 0;
- gfud->len = 0;
- gfud->data_len = 0;
-
- g_free(gfud->website.user);
- g_free(gfud->website.passwd);
- g_free(gfud->website.address);
- g_free(gfud->website.page);
- purple_url_parse(new_url, &gfud->website.address, &gfud->website.port,
- &gfud->website.page, &gfud->website.user, &gfud->website.passwd);
-
- if (purple_strcasestr(new_url, "https://") != NULL) {
- gfud->is_ssl = TRUE;
- gfud->ssl_connection = purple_ssl_connect(gfud->account,
- gfud->website.address, gfud->website.port,
- ssl_url_fetch_connect_cb, ssl_url_fetch_error_cb, gfud);
- } else {
- gfud->connect_data = purple_proxy_connect(NULL, gfud->account,
- gfud->website.address, gfud->website.port,
- url_fetch_connect_cb, gfud);
- }
-
- if (gfud->ssl_connection == NULL && gfud->connect_data == NULL)
- {
- purple_util_fetch_url_error(gfud, _("Unable to connect to %s"),
- gfud->website.address);
- }
-
- return TRUE;
-}
-
-/* find the starting point of the content for the specified header and make
- * sure that the content is safe to pass to sscanf */
-static const char *
-find_header_content(const char *data, gsize data_len, const char *header)
-{
- const char *p = NULL;
-
- gsize header_len = strlen(header);
-
- if (data_len > header_len) {
- /* Check if the first header matches (data won't start with a \n") */
- if (header[0] == '\n')
- p = (g_ascii_strncasecmp(data, header + 1, header_len - 1) == 0) ? data : NULL;
- if (!p)
- p = purple_strcasestr_len(data, data_len, header, header_len);
- if (p)
- p += header_len;
- }
-
- /* If we can find the header at all, try to sscanf it.
- * Response headers should end with at least \r\n, so sscanf is safe,
- * if we make sure that there is indeed a \n in our header.
- */
- if (p && g_strstr_len(p, data_len - (p - data), "\n")) {
- return p;
- }
-
- return NULL;
-}
-
-static gsize
-parse_content_len(const char *data, gsize data_len)
-{
- gsize content_len = 0;
- const char *p = NULL;
-
- p = find_header_content(data, data_len, "\nContent-Length: ");
- if (p) {
- if (sscanf(p, "%" G_GSIZE_FORMAT, &content_len) != 1) {
- purple_debug_warning("util", "invalid number format\n");
- content_len = 0;
- }
- purple_debug_misc("util", "parsed %" G_GSIZE_FORMAT "\n", content_len);
- }
-
- return content_len;
-}
-
-static gboolean
-content_is_chunked(const char *data, gsize data_len)
-{
- const char *p = find_header_content(data, data_len, "\nTransfer-Encoding: ");
- if (p && g_ascii_strncasecmp(p, "chunked", 7) == 0)
- return TRUE;
-
- return FALSE;
-}
-
-/* Process in-place */
-static void
-process_chunked_data(char *data, gsize *len)
-{
- gsize sz;
- gsize newlen = 0;
- char *p = data;
- char *s = data;
-
- while (*s) {
- /* Read the size of this chunk */
- if (sscanf(s, "%" G_GSIZE_MODIFIER "x", &sz) != 1)
- {
- purple_debug_error("util", "Error processing chunked data: "
- "Expected data length, found: %s\n", s);
- break;
- }
- if (sz == 0) {
- /* We've reached the last chunk */
- /*
- * TODO: The spec allows "footers" to follow the last chunk.
- * If there is more data after this line then we should
- * treat it like a header.
- */
- break;
- }
-
- /* Advance to the start of the data */
- s = strstr(s, "\r\n");
- if (s == NULL)
- break;
- s += 2;
-
- if (sz > MAX_HTTP_CHUNK_SIZE || s + sz > data + *len) {
- purple_debug_error("util", "Error processing chunked data: "
- "Chunk size %" G_GSIZE_FORMAT " bytes was longer "
- "than the data remaining in the buffer (%"
- G_GSIZE_FORMAT " bytes)\n", sz, data + *len - s);
- break;
- }
-
- /* Move all data overtop of the chunk length that we read in earlier */
- g_memmove(p, s, sz);
- p += sz;
- s += sz;
- newlen += sz;
- if (*s == '\0' || (*s != '\r' && *(s + 1) != '\n')) {
- purple_debug_error("util", "Error processing chunked data: "
- "Expected \\r\\n, found: %s\n", s);
- break;
- }
- s += 2;
- }
-
- /* NULL terminate the data */
- *p = 0;
-
- *len = newlen;
-}
-
-static void
-url_fetch_recv_cb(gpointer url_data, gint source, PurpleInputCondition cond)
-{
- PurpleUtilFetchUrlData *gfud = url_data;
- int len;
- char buf[4096];
- char *data_cursor;
- gboolean got_eof = FALSE;
-
- if (!gfud->is_ssl && source < 0) {
- g_warn_if_reached();
- len = -1;
- errno = EINVAL;
- }
-
- /*
- * Read data in a loop until we can't read any more! This is a
- * little confusing because we read using a different function
- * depending on whether the socket is ssl or cleartext.
- */
- while ((gfud->is_ssl && ((len = purple_ssl_read(gfud->ssl_connection, buf, sizeof(buf))) > 0)) ||
- (!gfud->is_ssl && source >= 0 && (len = read(source, buf, sizeof(buf))) > 0))
- {
- if((gfud->len + len) > gfud->max_len) {
- purple_util_fetch_url_error(gfud, _("Error reading from %s: response too long (%d bytes limit)"),
- gfud->website.address, gfud->max_len);
- return;
- }
-
- /* If we've filled up our buffer, make it bigger */
- if((gfud->len + len) >= gfud->data_len) {
- while((gfud->len + len) >= gfud->data_len)
- gfud->data_len += sizeof(buf);
-
- gfud->webdata = g_realloc(gfud->webdata, gfud->data_len);
- }
-
- data_cursor = gfud->webdata + gfud->len;
-
- gfud->len += len;
-
- memcpy(data_cursor, buf, len);
-
- gfud->webdata[gfud->len] = '\0';
-
- if(!gfud->got_headers) {
- char *end_of_headers;
-
- /* See if we've reached the end of the headers yet */
- end_of_headers = strstr(gfud->webdata, "\r\n\r\n");
- if (end_of_headers) {
- guint header_len = (end_of_headers + 4 - gfud->webdata);
- gsize content_len;
-
- purple_debug_misc("util", "Response headers: '%.*s'\n",
- header_len, gfud->webdata);
-
- /* See if we can find a redirect. */
- if(parse_redirect(gfud->webdata, header_len, gfud))
- return;
-
- gfud->got_headers = TRUE;
-
- /* No redirect. See if we can find a content length. */
- content_len = parse_content_len(gfud->webdata, header_len);
- gfud->chunked = content_is_chunked(gfud->webdata, header_len);
-
- if (content_len == 0) {
- /* We'll stick with an initial 8192 */
- content_len = 8192;
- } else {
- gfud->has_explicit_data_len = TRUE;
- if (content_len > gfud->max_len) {
- purple_debug_error("util",
- "Overriding explicit Content-Length of %" G_GSIZE_FORMAT " with max of %" G_GSSIZE_FORMAT "\n",
- content_len, gfud->max_len);
- content_len = gfud->max_len;
- }
- }
-
-
- /* If we're returning the headers too, we don't need to clean them out */
- if (gfud->include_headers) {
- char *new_data;
- gfud->data_len = content_len + header_len;
- new_data = g_try_realloc(gfud->webdata, gfud->data_len);
- if (new_data == NULL) {
- purple_debug_error("util",
- "Failed to allocate %" G_GSIZE_FORMAT " bytes: %s\n",
- content_len, g_strerror(errno));
- purple_util_fetch_url_error(gfud,
- _("Unable to allocate enough memory to hold "
- "the contents from %s. The web server may "
- "be trying something malicious."),
- gfud->website.address);
-
- return;
- }
- gfud->webdata = new_data;
- } else {
- char *new_data;
- gsize body_len = gfud->len - header_len;
-
- content_len = MAX(content_len, body_len);
-
- new_data = g_try_malloc(content_len);
- if (new_data == NULL) {
- purple_debug_error("util",
- "Failed to allocate %" G_GSIZE_FORMAT " bytes: %s\n",
- content_len, g_strerror(errno));
- purple_util_fetch_url_error(gfud,
- _("Unable to allocate enough memory to hold "
- "the contents from %s. The web server may "
- "be trying something malicious."),
- gfud->website.address);
-
- return;
- }
-
- /* We may have read part of the body when reading the headers, don't lose it */
- if (body_len > 0) {
- memcpy(new_data, end_of_headers + 4, body_len);
- }
-
- /* Out with the old... */
- g_free(gfud->webdata);
-
- /* In with the new. */
- gfud->len = body_len;
- gfud->data_len = content_len;
- gfud->webdata = new_data;
- }
- }
- }
-
- if(gfud->has_explicit_data_len && gfud->len >= gfud->data_len) {
- got_eof = TRUE;
- break;
- }
- }
-
- if(len < 0) {
- if(errno == EAGAIN) {
- return;
- } else {
- purple_util_fetch_url_error(gfud, _("Error reading from %s: %s"),
- gfud->website.address, g_strerror(errno));
- return;
- }
- }
-
- if((len == 0) || got_eof) {
- gfud->webdata = g_realloc(gfud->webdata, gfud->len + 1);
- gfud->webdata[gfud->len] = '\0';
-
- if (!gfud->include_headers && gfud->chunked) {
- /* Process only if we don't want the headers. */
- process_chunked_data(gfud->webdata, &gfud->len);
- }
-
- gfud->callback(gfud, gfud->user_data, gfud->webdata, gfud->len, NULL);
- purple_util_fetch_url_cancel(gfud);
- }
-}
-
-static void ssl_url_fetch_recv_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond)
-{
- url_fetch_recv_cb(data, -1, cond);
-}
-
-/**
- * This function is called when the socket is available to be written
- * to.
- *
- * @param source The file descriptor that can be written to. This can
- * be an http connection or it can be the SSL connection of an
- * https request. So be careful what you use it for! If it's
- * an https request then use purple_ssl_write() instead of
- * writing to it directly.
- */
-static void
-url_fetch_send_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
- PurpleUtilFetchUrlData *gfud;
- int len, total_len;
-
- gfud = data;
-
- if (gfud->request == NULL) {
-
- PurpleProxyInfo *gpi = purple_proxy_get_setup(gfud->account);
- GString *request_str = g_string_new(NULL);
-
- g_string_append_printf(request_str, "GET %s%s HTTP/%s\r\n"
- "Connection: close\r\n",
- (gfud->full ? "" : "/"),
- (gfud->full ? (gfud->url ? gfud->url : "") : (gfud->website.page ? gfud->website.page : "")),
- (gfud->http11 ? "1.1" : "1.0"));
-
- if (gfud->user_agent)
- g_string_append_printf(request_str, "User-Agent: %s\r\n", gfud->user_agent);
-
- /* Host header is not forbidden in HTTP/1.0 requests, and HTTP/1.1
- * clients must know how to handle the "chunked" transfer encoding.
- * Purple doesn't know how to handle "chunked", so should always send
- * the Host header regardless, to get around some observed problems
- */
- g_string_append_printf(request_str, "Accept: */*\r\n"
- "Host: %s\r\n",
- (gfud->website.address ? gfud->website.address : ""));
-
- if (purple_proxy_info_get_username(gpi) != NULL
- && (purple_proxy_info_get_type(gpi) == PURPLE_PROXY_USE_ENVVAR
- || purple_proxy_info_get_type(gpi) == PURPLE_PROXY_HTTP)) {
- /* This chunk of code was copied from proxy.c http_start_connect_tunneling()
- * This is really a temporary hack - we need a more complete proxy handling solution,
- * so I didn't think it was worthwhile to refactor for reuse
- */
- char *t1, *t2, *ntlm_type1;
- char hostname[256];
- int ret;
-
- ret = gethostname(hostname, sizeof(hostname));
- hostname[sizeof(hostname) - 1] = '\0';
- if (ret < 0 || hostname[0] == '\0') {
- purple_debug_warning("util", "proxy - gethostname() failed -- is your hostname set?");
- strcpy(hostname, "localhost");
- }
-
- t1 = g_strdup_printf("%s:%s",
- purple_proxy_info_get_username(gpi),
- purple_proxy_info_get_password(gpi) ?
- purple_proxy_info_get_password(gpi) : "");
- t2 = purple_base64_encode((const guchar *)t1, strlen(t1));
- g_free(t1);
-
- ntlm_type1 = purple_ntlm_gen_type1(hostname, "");
-
- g_string_append_printf(request_str,
- "Proxy-Authorization: Basic %s\r\n"
- "Proxy-Authorization: NTLM %s\r\n"
- "Proxy-Connection: Keep-Alive\r\n",
- t2, ntlm_type1);
- g_free(ntlm_type1);
- g_free(t2);
- }
-
- g_string_append(request_str, "\r\n");
-
- gfud->request = g_string_free(request_str, FALSE);
- }
-
- if(purple_debug_is_unsafe())
- purple_debug_misc("util", "Request: '%s'\n", gfud->request);
- else
- purple_debug_misc("util", "request constructed\n");
-
- total_len = strlen(gfud->request);
-
- if (gfud->is_ssl)
- len = purple_ssl_write(gfud->ssl_connection, gfud->request + gfud->request_written,
- total_len - gfud->request_written);
- else
- len = write(gfud->fd, gfud->request + gfud->request_written,
- total_len - gfud->request_written);
-
- if (len < 0 && errno == EAGAIN)
- return;
- else if (len < 0) {
- purple_util_fetch_url_error(gfud, _("Error writing to %s: %s"),
- gfud->website.address, g_strerror(errno));
- return;
- }
- gfud->request_written += len;
-
- if (gfud->request_written < total_len)
- return;
-
- /* We're done writing our request, now start reading the response */
- if (gfud->is_ssl) {
- purple_input_remove(gfud->inpa);
- gfud->inpa = 0;
- purple_ssl_input_add(gfud->ssl_connection, ssl_url_fetch_recv_cb, gfud);
- } else {
- purple_input_remove(gfud->inpa);
- gfud->inpa = purple_input_add(gfud->fd, PURPLE_INPUT_READ, url_fetch_recv_cb,
- gfud);
- }
-}
-
-static void
-url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message)
-{
- PurpleUtilFetchUrlData *gfud;
-
- gfud = url_data;
- gfud->connect_data = NULL;
-
- if (source == -1)
- {
- purple_util_fetch_url_error(gfud, _("Unable to connect to %s: %s"),
- (gfud->website.address ? gfud->website.address : ""), error_message);
- return;
- }
-
- gfud->fd = source;
-
- gfud->inpa = purple_input_add(source, PURPLE_INPUT_WRITE,
- url_fetch_send_cb, gfud);
- url_fetch_send_cb(gfud, source, PURPLE_INPUT_WRITE);
-}
-
-static void ssl_url_fetch_connect_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond)
-{
- PurpleUtilFetchUrlData *gfud;
-
- gfud = data;
-
- gfud->inpa = purple_input_add(ssl_connection->fd, PURPLE_INPUT_WRITE,
- url_fetch_send_cb, gfud);
- url_fetch_send_cb(gfud, ssl_connection->fd, PURPLE_INPUT_WRITE);
-}
-
-static void ssl_url_fetch_error_cb(PurpleSslConnection *ssl_connection, PurpleSslErrorType error, gpointer data)
-{
- PurpleUtilFetchUrlData *gfud;
-
- gfud = data;
- gfud->ssl_connection = NULL;
-
- purple_util_fetch_url_error(gfud, _("Unable to connect to %s: %s"),
- (gfud->website.address ? gfud->website.address : ""),
- purple_ssl_strerror(error));
-}
-
-PurpleUtilFetchUrlData *
-purple_util_fetch_url_request(const char *url, gboolean full,
- const char *user_agent, gboolean http11,
- const char *request, gboolean include_headers,
- PurpleUtilFetchUrlCallback callback, void *user_data)
-{
- return purple_util_fetch_url_request_len_with_account(NULL, url, full,
- user_agent, http11,
- request, include_headers, -1,
- callback, user_data);
-}
-
-PurpleUtilFetchUrlData *
-purple_util_fetch_url_request_len(const char *url, gboolean full,
- const char *user_agent, gboolean http11,
- const char *request, gboolean include_headers, gssize max_len,
- PurpleUtilFetchUrlCallback callback, void *user_data)
-{
- return purple_util_fetch_url_request_len_with_account(NULL, url, full,
- user_agent, http11, request, include_headers, max_len, callback,
- user_data);
-}
-
-PurpleUtilFetchUrlData *
-purple_util_fetch_url_request_len_with_account(PurpleAccount *account,
- const char *url, gboolean full, const char *user_agent, gboolean http11,
- const char *request, gboolean include_headers, gssize max_len,
- PurpleUtilFetchUrlCallback callback, void *user_data)
-{
- PurpleUtilFetchUrlData *gfud;
-
- g_return_val_if_fail(url != NULL, NULL);
- g_return_val_if_fail(callback != NULL, NULL);
-
- if(purple_debug_is_unsafe())
- purple_debug_info("util",
- "requested to fetch (%s), full=%d, user_agent=(%s), http11=%d\n",
- url, full, user_agent?user_agent:"(null)", http11);
- else
- purple_debug_info("util", "requesting to fetch a URL\n");
-
- gfud = g_new0(PurpleUtilFetchUrlData, 1);
-
- gfud->callback = callback;
- gfud->user_data = user_data;
- gfud->url = g_strdup(url);
- gfud->user_agent = g_strdup(user_agent);
- gfud->http11 = http11;
- gfud->full = full;
- gfud->request = g_strdup(request);
- gfud->include_headers = include_headers;
- gfud->fd = -1;
- if (max_len <= 0) {
- max_len = DEFAULT_MAX_HTTP_DOWNLOAD;
- purple_debug_error("util", "Defaulting max download from %s to %" G_GSSIZE_FORMAT "\n", url, max_len);
- }
- gfud->max_len = (gsize) max_len;
- gfud->account = account;
-
- purple_url_parse(url, &gfud->website.address, &gfud->website.port,
- &gfud->website.page, &gfud->website.user, &gfud->website.passwd);
-
- if (purple_strcasestr(url, "https://") != NULL) {
- if (!purple_ssl_is_supported()) {
- purple_util_fetch_url_error(gfud,
- _("Unable to connect to %s: %s"),
- gfud->website.address,
- _("Server requires TLS/SSL, but no TLS/SSL support was found."));
- return NULL;
- }
-
- gfud->is_ssl = TRUE;
- gfud->ssl_connection = purple_ssl_connect(account,
- gfud->website.address, gfud->website.port,
- ssl_url_fetch_connect_cb, ssl_url_fetch_error_cb, gfud);
- } else {
- gfud->connect_data = purple_proxy_connect(NULL, account,
- gfud->website.address, gfud->website.port,
- url_fetch_connect_cb, gfud);
- }
-
- if (gfud->ssl_connection == NULL && gfud->connect_data == NULL)
- {
- purple_util_fetch_url_error(gfud, _("Unable to connect to %s"),
- gfud->website.address);
- return NULL;
- }
-
- return gfud;
-}
-
-void
-purple_util_fetch_url_cancel(PurpleUtilFetchUrlData *gfud)
-{
- if (gfud->ssl_connection != NULL)
- purple_ssl_close(gfud->ssl_connection);
-
- if (gfud->connect_data != NULL)
- purple_proxy_connect_cancel(gfud->connect_data);
-
- if (gfud->inpa > 0)
- purple_input_remove(gfud->inpa);
-
- if (gfud->fd >= 0)
- close(gfud->fd);
-
- g_free(gfud->website.user);
- g_free(gfud->website.passwd);
- g_free(gfud->website.address);
- g_free(gfud->website.page);
- g_free(gfud->url);
- g_free(gfud->user_agent);
- g_free(gfud->request);
- g_free(gfud->webdata);
-
- g_free(gfud);
-}
-
const char *
purple_url_decode(const char *str)
{
@@ -4349,7 +3948,7 @@ purple_url_encode(const char *str)
buf[j++] = c;
} else {
int bytes = g_unichar_to_utf8(c, utf_char);
- for (i = 0; i < bytes; i++) {
+ for (i = 0; (int)i < bytes; i++) {
if (j > (BUF_LEN - 4))
break;
if (i >= sizeof(utf_char)) {
@@ -4486,11 +4085,10 @@ purple_ipv6_address_is_valid(const gchar *ip)
return (double_colon && chunks < 8) || (!double_colon && chunks == 8);
}
-/* TODO 3.0.0: Add ipv6 check, too */
gboolean
purple_ip_address_is_valid(const char *ip)
{
- return purple_ipv4_address_is_valid(ip);
+ return (purple_ipv4_address_is_valid(ip) || purple_ipv6_address_is_valid(ip));
}
/* Stolen from gnome_uri_list_extract_uris */
@@ -4670,7 +4268,7 @@ purple_utf8_strip_unprintables(const gchar *str)
* This function is copied from g_strerror() but changed to use
* gai_strerror().
*/
-G_CONST_RETURN gchar *
+const gchar *
purple_gai_strerror(gint errnum)
{
#if GLIB_CHECK_VERSION(2, 32, 0)
@@ -4853,8 +4451,8 @@ purple_utf8_has_word(const char *haystack, const char *needle)
("!g_unichar_isalnum()" is not a valid way to determine word
boundaries, but it is the only reasonable thing to do here),
and isn't the '&' from a "&amp;" or some such entity*/
- (before != -2 && !g_unichar_isalnum(before) && *(p - 1) != '&'))
- && after != -2 && !g_unichar_isalnum(after)) {
+ (before != (gunichar)-2 && !g_unichar_isalnum(before) && *(p - 1) != '&'))
+ && after != (gunichar)-2 && !g_unichar_isalnum(after)) {
ret = TRUE;
break;
}
@@ -5008,7 +4606,7 @@ purple_escape_filename(const char *str)
buf[j++] = c;
} else {
int bytes = g_unichar_to_utf8(c, utf_char);
- for (i = 0; i < bytes; i++) {
+ for (i = 0; (int)i < bytes; i++) {
if (j > (BUF_LEN - 4))
break;
if (i >= sizeof(utf_char)) {
@@ -5032,16 +4630,16 @@ purple_escape_filename(const char *str)
return buf;
}
-const char *_purple_oscar_convert(const char *act, const char *protocol)
+gchar * purple_escape_js(const gchar *str)
{
- if (act && purple_strequal(protocol, "prpl-oscar")) {
- int i;
- for (i = 0; act[i] != '\0'; i++)
- if (!isdigit(act[i]))
- return "prpl-aim";
- return "prpl-icq";
- }
- return protocol;
+ gchar *escaped;
+
+ json_node_set_string(escape_js_node, str);
+ json_generator_set_root(escape_js_gen, escape_js_node);
+ escaped = json_generator_to_data(escape_js_gen, NULL);
+ json_node_set_boolean(escape_js_node, FALSE);
+
+ return escaped;
}
void purple_restore_default_signal_handlers(void)
@@ -5165,3 +4763,304 @@ purple_uuid_random(void)
b,
(tmp >> 16) & 0xFFFF, g_random_int());
}
+
+void purple_callback_set_zero(gpointer data)
+{
+ gpointer *ptr = data;
+
+ g_return_if_fail(ptr != NULL);
+
+ *ptr = NULL;
+}
+
+GValue *
+purple_value_new(GType type)
+{
+ GValue *ret;
+
+ g_return_val_if_fail(type != G_TYPE_NONE, NULL);
+
+ ret = g_new0(GValue, 1);
+ g_value_init(ret, type);
+
+ return ret;
+}
+
+GValue *
+purple_value_dup(GValue *value)
+{
+ GValue *ret;
+
+ g_return_val_if_fail(value != NULL, NULL);
+
+ ret = g_new0(GValue, 1);
+ g_value_init(ret, G_VALUE_TYPE(value));
+ g_value_copy(value, ret);
+
+ return ret;
+}
+
+void
+purple_value_free(GValue *value)
+{
+ g_return_if_fail(value != NULL);
+
+ g_value_unset(value);
+ g_free(value);
+}
+
+gchar *purple_http_digest_calculate_session_key(
+ const gchar *algorithm,
+ const gchar *username,
+ const gchar *realm,
+ const gchar *password,
+ const gchar *nonce,
+ const gchar *client_nonce)
+{
+ PurpleHash *hasher;
+ gchar hash[33]; /* We only support MD5. */
+ gboolean digest_ok;
+
+ g_return_val_if_fail(username != NULL, NULL);
+ g_return_val_if_fail(realm != NULL, NULL);
+ g_return_val_if_fail(password != NULL, NULL);
+ g_return_val_if_fail(nonce != NULL, NULL);
+
+ /* Check for a supported algorithm. */
+ g_return_val_if_fail(algorithm == NULL ||
+ *algorithm == '\0' ||
+ g_ascii_strcasecmp(algorithm, "MD5") ||
+ g_ascii_strcasecmp(algorithm, "MD5-sess"), NULL);
+
+ hasher = purple_md5_hash_new();
+ g_return_val_if_fail(hash != NULL, NULL);
+
+ purple_hash_append(hasher, (guchar *)username, strlen(username));
+ purple_hash_append(hasher, (guchar *)":", 1);
+ purple_hash_append(hasher, (guchar *)realm, strlen(realm));
+ purple_hash_append(hasher, (guchar *)":", 1);
+ purple_hash_append(hasher, (guchar *)password, strlen(password));
+
+ if (algorithm != NULL && !g_ascii_strcasecmp(algorithm, "MD5-sess"))
+ {
+ guchar digest[16];
+
+ if (client_nonce == NULL)
+ {
+ g_object_unref(hasher);
+ purple_debug_error("hash", "Required client_nonce missing for MD5-sess digest calculation.\n");
+ return NULL;
+ }
+
+ purple_hash_digest(hasher, digest, sizeof(digest));
+
+ purple_hash_reset(hasher);
+ purple_hash_append(hasher, digest, sizeof(digest));
+ purple_hash_append(hasher, (guchar *)":", 1);
+ purple_hash_append(hasher, (guchar *)nonce, strlen(nonce));
+ purple_hash_append(hasher, (guchar *)":", 1);
+ purple_hash_append(hasher, (guchar *)client_nonce, strlen(client_nonce));
+ }
+
+ digest_ok = purple_hash_digest_to_str(hasher, hash, sizeof(hash));
+ g_object_unref(hasher);
+
+ g_return_val_if_fail(digest_ok, NULL);
+
+ return g_strdup(hash);
+}
+
+gchar *purple_http_digest_calculate_response(
+ const gchar *algorithm,
+ const gchar *method,
+ const gchar *digest_uri,
+ const gchar *qop,
+ const gchar *entity,
+ const gchar *nonce,
+ const gchar *nonce_count,
+ const gchar *client_nonce,
+ const gchar *session_key)
+{
+ PurpleHash *hash;
+ static gchar hash2[33]; /* We only support MD5. */
+ gboolean digest_ok;
+
+ g_return_val_if_fail(method != NULL, NULL);
+ g_return_val_if_fail(digest_uri != NULL, NULL);
+ g_return_val_if_fail(nonce != NULL, NULL);
+ g_return_val_if_fail(session_key != NULL, NULL);
+
+ /* Check for a supported algorithm. */
+ g_return_val_if_fail(algorithm == NULL ||
+ *algorithm == '\0' ||
+ g_ascii_strcasecmp(algorithm, "MD5") ||
+ g_ascii_strcasecmp(algorithm, "MD5-sess"), NULL);
+
+ /* Check for a supported "quality of protection". */
+ g_return_val_if_fail(qop == NULL ||
+ *qop == '\0' ||
+ g_ascii_strcasecmp(qop, "auth") ||
+ g_ascii_strcasecmp(qop, "auth-int"), NULL);
+
+ hash = purple_md5_hash_new();
+ g_return_val_if_fail(hash != NULL, NULL);
+
+ purple_hash_append(hash, (guchar *)method, strlen(method));
+ purple_hash_append(hash, (guchar *)":", 1);
+ purple_hash_append(hash, (guchar *)digest_uri, strlen(digest_uri));
+
+ if (qop != NULL && !g_ascii_strcasecmp(qop, "auth-int"))
+ {
+ PurpleHash *hash2;
+ gchar entity_hash[33];
+
+ if (entity == NULL)
+ {
+ g_object_unref(hash);
+ purple_debug_error("hash", "Required entity missing for auth-int digest calculation.\n");
+ return NULL;
+ }
+
+ hash2 = purple_md5_hash_new();
+ purple_hash_append(hash2, (guchar *)entity, strlen(entity));
+ digest_ok = purple_hash_digest_to_str(hash2, entity_hash, sizeof(entity_hash));
+ g_object_unref(hash2);
+
+ if (!digest_ok) {
+ g_object_unref(hash);
+ g_return_val_if_reached(NULL);
+ }
+
+ purple_hash_append(hash, (guchar *)":", 1);
+ purple_hash_append(hash, (guchar *)entity_hash, strlen(entity_hash));
+ }
+
+ digest_ok = purple_hash_digest_to_str(hash, hash2, sizeof(hash2));
+ purple_hash_reset(hash);
+
+ if (!digest_ok) {
+ g_object_unref(hash);
+ g_return_val_if_reached(NULL);
+ }
+
+ purple_hash_append(hash, (guchar *)session_key, strlen(session_key));
+ purple_hash_append(hash, (guchar *)":", 1);
+ purple_hash_append(hash, (guchar *)nonce, strlen(nonce));
+ purple_hash_append(hash, (guchar *)":", 1);
+
+ if (qop != NULL && *qop != '\0')
+ {
+ if (nonce_count == NULL)
+ {
+ g_object_unref(hash);
+ purple_debug_error("hash", "Required nonce_count missing for digest calculation.\n");
+ return NULL;
+ }
+
+ if (client_nonce == NULL)
+ {
+ g_object_unref(hash);
+ purple_debug_error("hash", "Required client_nonce missing for digest calculation.\n");
+ return NULL;
+ }
+
+ purple_hash_append(hash, (guchar *)nonce_count, strlen(nonce_count));
+ purple_hash_append(hash, (guchar *)":", 1);
+ purple_hash_append(hash, (guchar *)client_nonce, strlen(client_nonce));
+ purple_hash_append(hash, (guchar *)":", 1);
+
+ purple_hash_append(hash, (guchar *)qop, strlen(qop));
+
+ purple_hash_append(hash, (guchar *)":", 1);
+ }
+
+ purple_hash_append(hash, (guchar *)hash2, strlen(hash2));
+ digest_ok = purple_hash_digest_to_str(hash, hash2, sizeof(hash2));
+ g_object_unref(hash);
+
+ g_return_val_if_fail(digest_ok, NULL);
+
+ return g_strdup(hash2);
+}
+
+int
+_purple_fstat(int fd, GStatBuf *st)
+{
+ int ret;
+
+ g_return_val_if_fail(st != NULL, -1);
+
+#ifdef _WIN32
+ ret = _fstat(fd, st);
+#else
+ ret = fstat(fd, st);
+#endif
+
+ return ret;
+}
+
+#if 0
+
+/* Temporarily removed - re-add this when you need ini file support. */
+
+#define PURPLE_KEY_FILE_DEFAULT_MAX_SIZE 102400
+#define PURPLE_KEY_FILE_HARD_LIMIT 10485760
+
+gboolean
+purple_key_file_load_from_ini(GKeyFile *key_file, const gchar *file,
+ gsize max_size)
+{
+ const gchar *header = "[default]\n\n";
+ int header_len = strlen(header);
+ int fd;
+ GStatBuf st;
+ gsize file_size, buff_size;
+ gchar *buff;
+ GError *error = NULL;
+
+ g_return_val_if_fail(key_file != NULL, FALSE);
+ g_return_val_if_fail(file != NULL, FALSE);
+ g_return_val_if_fail(max_size < PURPLE_KEY_FILE_HARD_LIMIT, FALSE);
+
+ if (max_size == 0)
+ max_size = PURPLE_KEY_FILE_DEFAULT_MAX_SIZE;
+
+ fd = g_open(file, O_RDONLY, S_IREAD);
+ if (fd == -1) {
+ purple_debug_error("util", "Failed to read ini file %s", file);
+ return FALSE;
+ }
+
+ if (_purple_fstat(fd, &st) != 0) {
+ purple_debug_error("util", "Failed to fstat ini file %s", file);
+ return FALSE;
+ }
+
+ file_size = (st.st_size > max_size) ? max_size : st.st_size;
+
+ buff_size = file_size + header_len;
+ buff = g_new(gchar, buff_size);
+ memcpy(buff, header, header_len);
+ if (read(fd, buff + header_len, file_size) != (gssize)file_size) {
+ purple_debug_error("util",
+ "Failed to read whole ini file %s", file);
+ g_close(fd, NULL);
+ free(buff);
+ return FALSE;
+ }
+ g_close(fd, NULL);
+
+ g_key_file_load_from_data(key_file, buff, buff_size,
+ G_KEY_FILE_NONE, &error);
+
+ free(buff);
+
+ if (error) {
+ purple_debug_error("util", "Failed parsing ini file %s: %s",
+ file, error->message);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+#endif
diff --git a/libpurple/util.h b/libpurple/util.h
index c14b23564f..d8ac7cc143 100644
--- a/libpurple/util.h
+++ b/libpurple/util.h
@@ -1,8 +1,3 @@
-/**
- * @file util.h Utility Functions
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -23,50 +18,49 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
- * @todo Rename the functions so that they live somewhere in the purple
- * namespace.
+ * TODO Rename the functions so that they live somewhere in the purple
+ * namespace.
*/
+
#ifndef _PURPLE_UTIL_H_
#define _PURPLE_UTIL_H_
+/**
+ * SECTION:util
+ * @section_id: libpurple-util
+ * @short_description: <filename>util.h</filename>
+ * @title: Utility Functions
+ */
#include <stdio.h>
/**
- * An opaque structure representing a URL request. Can be used to cancel
- * the request.
- */
-typedef struct _PurpleUtilFetchUrlData PurpleUtilFetchUrlData;
-/** @copydoc _PurpleMenuAction */
+ * PurpleMenuAction:
+ *
+ * A generic structure that contains information about an "action." One
+ * place this is is used is by protocols to tell the core the list of available
+ * right-click actions for a buddy list row.
+ */
typedef struct _PurpleMenuAction PurpleMenuAction;
-/** @copydoc _PurpleKeyValuePair */
+
+/**
+ * PurpleKeyValuePair:
+ *
+ * A key-value pair.
+ *
+ * This is used by, among other things, purple_gtk_combo* functions to pass in a
+ * list of key-value pairs so it can display a user-friendly value.
+ */
typedef struct _PurpleKeyValuePair PurpleKeyValuePair;
#include "account.h"
#include "signals.h"
#include "xmlnode.h"
#include "notify.h"
+#include "plugin.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct _PurpleMenuAction
-{
- char *label;
- PurpleCallback callback;
- gpointer data;
- GList *children;
-};
-
typedef char *(*PurpleInfoFieldFormatCallback)(const char *field, size_t len);
-/**
- * A key-value pair.
- *
- * This is used by, among other things, purple_gtk_combo* functions to pass in a
- * list of key-value pairs so it can display a user-friendly value.
- */
struct _PurpleKeyValuePair
{
gchar *key;
@@ -74,189 +68,288 @@ struct _PurpleKeyValuePair
};
+G_BEGIN_DECLS
+
/**
- * Creates a new PurpleMenuAction.
- *
- * @param label The text label to display for this action.
- * @param callback The function to be called when the action is used on
+ * purple_menu_action_new:
+ * @label: The text label to display for this action.
+ * @callback: The function to be called when the action is used on
* the selected item.
- * @param data Additional data to be passed to the callback.
- * @param children A GList of PurpleMenuActions to be added as a submenu
+ * @data: Additional data to be passed to the callback.
+ * @children: A GList of PurpleMenuActions to be added as a submenu
* of the action.
- * @return The PurpleMenuAction.
+ *
+ * Creates a new PurpleMenuAction.
+ *
+ * Returns: The PurpleMenuAction.
*/
PurpleMenuAction *purple_menu_action_new(const char *label, PurpleCallback callback,
gpointer data, GList *children);
/**
- * Frees a PurpleMenuAction
+ * purple_menu_action_free:
+ * @act: The PurpleMenuAction to free.
*
- * @param act The PurpleMenuAction to free.
+ * Frees a PurpleMenuAction
*/
void purple_menu_action_free(PurpleMenuAction *act);
/**
- * Set the appropriate presence values for the currently playing song.
+ * purple_menu_action_get_label:
+ * @act: The PurpleMenuAction.
+ *
+ * Returns the label of the PurpleMenuAction.
+ *
+ * Returns: The label string.
+ */
+char * purple_menu_action_get_label(const PurpleMenuAction *act);
+
+/**
+ * purple_menu_action_get_callback:
+ * @act: The PurpleMenuAction.
+ *
+ * Returns the callback of the PurpleMenuAction.
+ *
+ * Returns: The callback function.
+ */
+PurpleCallback purple_menu_action_get_callback(const PurpleMenuAction *act);
+
+/**
+ * purple_menu_action_get_data:
+ * @act: The PurpleMenuAction.
+ *
+ * Returns the data stored in the PurpleMenuAction.
*
- * @param title The title of the song, @c NULL to unset the value.
- * @param artist The artist of the song, can be @c NULL.
- * @param album The album of the song, can be @c NULL.
- * @since 2.4.0
+ * Returns: The data.
+ */
+gpointer purple_menu_action_get_data(const PurpleMenuAction *act);
+
+/**
+ * purple_menu_action_get_children:
+ * @act: The PurpleMenuAction.
+ *
+ * Returns the children of the PurpleMenuAction.
+ *
+ * Returns: The GList of children.
+ */
+GList* purple_menu_action_get_children(const PurpleMenuAction *act);
+
+/**
+ * purple_menu_action_set_label:
+ * @act: The menu action.
+ * @label: The label for the menu action.
+ *
+ * Set the label to the PurpleMenuAction.
+ */
+void purple_menu_action_set_label(PurpleMenuAction *act, char *label);
+
+/**
+ * purple_menu_action_set_callback:
+ * @act: The menu action.
+ * @callback: The callback.
+ *
+ * Set the callback that will be used by the PurpleMenuAction.
+ */
+void purple_menu_action_set_callback(PurpleMenuAction *act, PurpleCallback callback);
+
+/**
+ * purple_menu_action_set_data:
+ * @act: The menu action.
+ * @data: The data used by this PurpleMenuAction
+ *
+ * Set the label to the PurpleMenuAction.
+ */
+void purple_menu_action_set_data(PurpleMenuAction *act, gpointer data);
+
+/**
+ * purple_menu_action_set_children:
+ * @act: The menu action.
+ * @children: The PurpleMenuAtion children
+ *
+ * Set the children of the PurpleMenuAction.
+ */
+void purple_menu_action_set_children(PurpleMenuAction *act, GList *children);
+
+/**
+ * purple_menu_action_set_stock_icon:
+ * @act: The menu action.
+ * @stock: The stock icon identifier.
+ *
+ * Sets the icon for the PurpleMenuAction.
+ */
+void purple_menu_action_set_stock_icon(PurpleMenuAction *act,
+ const gchar *stock);
+
+/**
+ * purple_menu_action_get_stock_icon:
+ * @act: The menu action.
+ *
+ * Gets the stock icon of the PurpleMenuAction.
+ *
+ * Returns: The stock icon identifier.
+ */
+const gchar *
+purple_menu_action_get_stock_icon(PurpleMenuAction *act);
+
+/**
+ * purple_util_set_current_song:
+ * @title: The title of the song, %NULL to unset the value.
+ * @artist: The artist of the song, can be %NULL.
+ * @album: The album of the song, can be %NULL.
+ *
+ * Set the appropriate presence values for the currently playing song.
*/
void purple_util_set_current_song(const char *title, const char *artist,
const char *album);
/**
- * Format song information.
+ * purple_util_format_song_info:
+ * @title: The title of the song, %NULL to unset the value.
+ * @artist: The artist of the song, can be %NULL.
+ * @album: The album of the song, can be %NULL.
+ * @unused: Currently unused, must be %NULL.
*
- * @param title The title of the song, @c NULL to unset the value.
- * @param artist The artist of the song, can be @c NULL.
- * @param album The album of the song, can be @c NULL.
- * @param unused Currently unused, must be @c NULL.
+ * Format song information.
*
- * @return The formatted string. The caller must g_free the returned string.
- * @since 2.4.0
+ * Returns: The formatted string. The caller must g_free the returned string.
*/
char * purple_util_format_song_info(const char *title, const char *artist,
const char *album, gpointer unused);
/**************************************************************************/
-/** @name Utility Subsystem */
+/* Utility Subsystem */
/**************************************************************************/
-/*@{*/
/**
- * Initializes the utility subsystem.
+ * purple_util_init:
*
- * @since 2.3.0
+ * Initializes the utility subsystem.
*/
void purple_util_init(void);
/**
- * Uninitializes the util subsystem.
+ * purple_util_uninit:
*
- * @since 2.3.0
+ * Uninitializes the util subsystem.
*/
void purple_util_uninit(void);
-/*@}*/
-
/**************************************************************************/
-/** @name Base16 Functions */
+/* Base16 Functions */
/**************************************************************************/
-/*@{*/
/**
+ * purple_base16_encode:
+ * @data: The data to convert.
+ * @len: The length of the data.
+ *
* Converts a chunk of binary data to its base-16 equivalent.
*
- * @param data The data to convert.
- * @param len The length of the data.
+ * See purple_base16_decode()
*
- * @return The base-16 string in the ASCII encoding. Must be
+ * Returns: The base-16 string in the ASCII encoding. Must be
* g_free'd when no longer needed.
- *
- * @see purple_base16_decode()
*/
gchar *purple_base16_encode(const guchar *data, gsize len);
/**
- * Converts an ASCII string of base-16 encoded data to
- * the binary equivalent.
- *
- * @param str The base-16 string to convert to raw data.
- * @param ret_len The length of the returned data. You can
+ * purple_base16_decode:
+ * @str: The base-16 string to convert to raw data.
+ * @ret_len: The length of the returned data. You can
* pass in NULL if you're sure that you know
* the length of the decoded data, or if you
* know you'll be able to use strlen to
* determine the length, etc.
*
- * @return The raw data. Must be g_free'd when no longer needed.
+ * Converts an ASCII string of base-16 encoded data to
+ * the binary equivalent.
+ *
+ * See purple_base16_encode()
*
- * @see purple_base16_encode()
+ * Returns: The raw data. Must be g_free'd when no longer needed.
*/
guchar *purple_base16_decode(const char *str, gsize *ret_len);
/**
+ * purple_base16_encode_chunked:
+ * @data: The data to convert.
+ * @len: The length of the data.
+ *
* Converts a chunk of binary data to a chunked base-16 representation
* (handy for key fingerprints)
*
* Example output: 01:23:45:67:89:AB:CD:EF
*
- * @param data The data to convert.
- * @param len The length of the data.
- *
- * @return The base-16 string in the ASCII chunked encoding. Must be
+ * Returns: The base-16 string in the ASCII chunked encoding. Must be
* g_free'd when no longer needed.
*/
gchar *purple_base16_encode_chunked(const guchar *data, gsize len);
-/*@}*/
-
/**************************************************************************/
-/** @name Base64 Functions */
+/* Base64 Functions */
/**************************************************************************/
-/*@{*/
/**
+ * purple_base64_encode:
+ * @data: The data to convert.
+ * @len: The length of the data.
+ *
* Converts a chunk of binary data to its base-64 equivalent.
*
- * @param data The data to convert.
- * @param len The length of the data.
+ * See purple_base64_decode()
*
- * @return The base-64 string in the ASCII encoding. Must be
+ * Returns: The base-64 string in the ASCII encoding. Must be
* g_free'd when no longer needed.
- *
- * @see purple_base64_decode()
*/
gchar *purple_base64_encode(const guchar *data, gsize len);
/**
- * Converts an ASCII string of base-64 encoded data to
- * the binary equivalent.
- *
- * @param str The base-64 string to convert to raw data.
- * @param ret_len The length of the returned data. You can
+ * purple_base64_decode:
+ * @str: The base-64 string to convert to raw data.
+ * @ret_len: The length of the returned data. You can
* pass in NULL if you're sure that you know
* the length of the decoded data, or if you
* know you'll be able to use strlen to
* determine the length, etc.
*
- * @return The raw data. Must be g_free'd when no longer needed.
+ * Converts an ASCII string of base-64 encoded data to
+ * the binary equivalent.
+ *
+ * See purple_base64_encode()
*
- * @see purple_base64_encode()
+ * Returns: The raw data. Must be g_free'd when no longer needed.
*/
guchar *purple_base64_decode(const char *str, gsize *ret_len);
-/*@}*/
-
/**************************************************************************/
-/** @name Quoted Printable Functions */
+/* Quoted Printable Functions */
/**************************************************************************/
-/*@{*/
/**
+ * purple_quotedp_decode:
+ * @str: The quoted printable ASCII string to convert to raw data.
+ * @ret_len: The length of the returned data.
+ *
* Converts a quoted printable string back to its readable equivalent.
* What is a quoted printable string, you ask? It's an encoding used
* to transmit binary data as ASCII. It's intended purpose is to send
* emails containing non-ASCII characters. Wikipedia has a pretty good
* explanation. Also see RFC 2045.
*
- * @param str The quoted printable ASCII string to convert to raw data.
- * @param ret_len The length of the returned data.
- *
- * @return The readable string. Must be g_free'd when no longer needed.
+ * Returns: The readable string. Must be g_free'd when no longer needed.
*/
guchar *purple_quotedp_decode(const char *str, gsize *ret_len);
-/*@}*/
-
/**************************************************************************/
-/** @name MIME Functions */
+/* MIME Functions */
/**************************************************************************/
-/*@{*/
/**
+ * purple_mime_decode_field:
+ * @str: The ASCII string, possibly containing any number of
+ * encoded-word sections.
+ *
* Converts a MIME header field string back to its readable equivalent
* according to RFC 2047. Basically, a header is plain ASCII and can
* contain any number of sections called "encoded-words." The format
@@ -268,213 +361,237 @@ guchar *purple_quotedp_decode(const char *str, gsize *ret_len);
* question mark. The first piece is the character set, the second
* piece is the encoding, and the third piece is the encoded text.
*
- * @param str The ASCII string, possibly containing any number of
- * encoded-word sections.
- *
- * @return The string, with any encoded-word sections decoded and
+ * Returns: The string, with any encoded-word sections decoded and
* converted to UTF-8. Must be g_free'd when no longer
* needed.
*/
char *purple_mime_decode_field(const char *str);
-/*@}*/
-
/**************************************************************************/
-/** @name Date/Time Functions */
+/* Date/Time Functions */
/**************************************************************************/
-/*@{*/
/**
+ * purple_utf8_strftime:
+ * @format: The format string, in UTF-8
+ * @tm: The time to format, or %NULL to use the current local time
+ *
* Formats a time into the specified format.
*
* This is essentially strftime(), but it has a static buffer
* and handles the UTF-8 conversion for the caller.
*
- * This function also provides the GNU %z formatter if the underlying C
+ * This function also provides the GNU \%z formatter if the underlying C
* library doesn't. However, the format string parser is very naive, which
- * means that conversions specifiers to %z cannot be guaranteed. The GNU
- * strftime(3) man page describes %z as: 'The time-zone as hour offset from
+ * means that conversions specifiers to \%z cannot be guaranteed. The GNU
+ * strftime(3) man page describes \%z as: 'The time-zone as hour offset from
* GMT. Required to emit RFC822-conformant dates
- * (using "%a, %d %b %Y %H:%M:%S %z"). (GNU)'
+ * (using "\%a, \%d \%b \%Y \%H:\%M:\%S \%z"). (GNU)'
*
- * On Windows, this function also converts the results for %Z from a timezone
- * name (as returned by the system strftime() %Z format string) to a timezone
- * abbreviation (as is the case on Unix). As with %z, conversion specifiers
+ * On Windows, this function also converts the results for \%Z from a timezone
+ * name (as returned by the system strftime() \%Z format string) to a timezone
+ * abbreviation (as is the case on Unix). As with \%z, conversion specifiers
* should not be used.
*
- * @param format The format string, in UTF-8
- * @param tm The time to format, or @c NULL to use the current local time
- *
- * @return The formatted time, in UTF-8.
- *
- * @note @a format is required to be in UTF-8. This differs from strftime(),
+ * Note: @format is required to be in UTF-8. This differs from strftime(),
* where the format is provided in the locale charset.
+ *
+ * Returns: The formatted time, in UTF-8.
*/
const char *purple_utf8_strftime(const char *format, const struct tm *tm);
/**
- * Gets a string representation of the local timezone offset
- *
- * @param tm The time to get the timezone for
- * @param iso TRUE to format the offset according to ISO-8601, FALSE to
+ * purple_get_tzoff_str:
+ * @tm: The time to get the timezone for
+ * @iso: TRUE to format the offset according to ISO-8601, FALSE to
* not substitute 'Z' for 0 offset, and to not separate
* hours and minutes with a colon.
+ *
+ * Gets a string representation of the local timezone offset
*/
const char *purple_get_tzoff_str(const struct tm *tm, gboolean iso);
/**
+ * purple_date_format_short:
+ * @tm: The time to format, or %NULL to use the current local time
+ *
* Formats a time into the user's preferred short date format.
*
* The returned string is stored in a static buffer, so the result
* should be g_strdup()'d if it's going to be kept.
*
- * @param tm The time to format, or @c NULL to use the current local time
- *
- * @return The date, formatted as per the user's settings.
+ * Returns: The date, formatted as per the user's settings. In the USA this
+ * is something like "02/18/13"
*/
const char *purple_date_format_short(const struct tm *tm);
/**
+ * purple_date_format_long:
+ * @tm: The time to format, or %NULL to use the current local time
+ *
* Formats a time into the user's preferred short date plus time format.
*
* The returned string is stored in a static buffer, so the result
* should be g_strdup()'d if it's going to be kept.
*
- * @param tm The time to format, or @c NULL to use the current local time
- *
- * @return The timestamp, formatted as per the user's settings.
+ * Returns: The timestamp, formatted as per the user's settings. In the USA
+ * this is something like "02/18/13 15:26:44"
*/
const char *purple_date_format_long(const struct tm *tm);
/**
+ * purple_date_format_full:
+ * @tm: The time to format, or %NULL to use the current local time
+ *
* Formats a time into the user's preferred full date and time format.
*
* The returned string is stored in a static buffer, so the result
* should be g_strdup()'d if it's going to be kept.
*
- * @param tm The time to format, or @c NULL to use the current local time
- *
- * @return The date and time, formatted as per the user's settings.
+ * Returns: The date and time, formatted as per the user's settings. In the
+ * USA this is something like "Mon Feb 18 15:26:44 2013"
*/
const char *purple_date_format_full(const struct tm *tm);
/**
+ * purple_time_format:
+ * @tm: The time to format, or %NULL to use the current local time
+ *
* Formats a time into the user's preferred time format.
*
* The returned string is stored in a static buffer, so the result
* should be g_strdup()'d if it's going to be kept.
*
- * @param tm The time to format, or @c NULL to use the current local time
- *
- * @return The time, formatted as per the user's settings.
+ * Returns: The time, formatted as per the user's settings. In the USA this
+ * is something like "15:26:44"
*/
const char *purple_time_format(const struct tm *tm);
/**
- * Builds a time_t from the supplied information.
+ * purple_time_build:
+ * @year: The year.
+ * @month: The month.
+ * @day: The day.
+ * @hour: The hour.
+ * @min: The minute.
+ * @sec: The second.
*
- * @param year The year.
- * @param month The month.
- * @param day The day.
- * @param hour The hour.
- * @param min The minute.
- * @param sec The second.
+ * Builds a time_t from the supplied information.
*
- * @return A time_t.
+ * Returns: A time_t.
*/
time_t purple_time_build(int year, int month, int day, int hour,
int min, int sec);
-/** Used by purple_str_to_time to indicate no timezone offset was
- * specified in the timestamp string. */
+/**
+ * PURPLE_NO_TZ_OFF:
+ *
+ * Used by purple_str_to_time to indicate no timezone offset was
+ * specified in the timestamp string.
+ */
#define PURPLE_NO_TZ_OFF -500000
/**
- * Parses a timestamp in jabber, ISO8601, or MM/DD/YYYY format and returns
- * a time_t.
- *
- * @param timestamp The timestamp
- * @param utc Assume UTC if no timezone specified
- * @param tm If not @c NULL, the caller can get a copy of the
+ * purple_str_to_time:
+ * @timestamp: The timestamp
+ * @utc: Assume UTC if no timezone specified
+ * @tm: If not %NULL, the caller can get a copy of the
* struct tm used to calculate the time_t return value.
- * @param tz_off If not @c NULL, the caller can get a copy of the
+ * @tz_off: If not %NULL, the caller can get a copy of the
* timezone offset (from UTC) used to calculate the time_t
* return value. Note: Zero is a valid offset. As such,
- * the value of the macro @c PURPLE_NO_TZ_OFF indicates no
+ * the value of the macro PURPLE_NO_TZ_OFF indicates no
* offset was specified (which means that the local
* timezone was used in the calculation).
- * @param rest If not @c NULL, the caller can get a pointer to the
- * part of @a timestamp left over after parsing is
- * completed, if it's not the end of @a timestamp.
+ * @rest: If not %NULL, the caller can get a pointer to the
+ * part of @timestamp left over after parsing is
+ * completed, if it's not the end of @timestamp.
*
- * @return A time_t.
+ * Parses a timestamp in jabber, ISO8601, or MM/DD/YYYY format and returns
+ * a time_t.
+ *
+ * Returns: A time_t.
*/
time_t purple_str_to_time(const char *timestamp, gboolean utc,
struct tm *tm, long *tz_off, const char **rest);
-/*@}*/
+/**
+ * purple_uts35_to_str:
+ * @format: The formatting string, according to UTS \#35
+ * See http://unicode.org/reports/tr35/
+ * (NOTE: not all formats are supported)
+ * @len: The length of the formatting string
+ * @tm: The time to format, or %NULL to use the current local time
+ *
+ * Formats a datetime according to a UTS-35 Date Format Pattern.
+ *
+ * Returns: The time, formatted as per the user's settings.
+ */
+char *purple_uts35_to_str(const char *format, size_t len, struct tm *tm);
/**************************************************************************/
-/** @name Markup Functions */
+/* Markup Functions */
/**************************************************************************/
-/*@{*/
/**
+ * purple_markup_escape_text:
+ *
* Escapes special characters in a plain-text string so they display
- * correctly as HTML. For example, & is replaced with &amp; and < is
- * replaced with &lt;
+ * correctly as HTML. For example, &amp; is replaced with &amp;amp; and &lt; is
+ * replaced with &amp;lt;
*
* This is exactly the same as g_markup_escape_text(), except that it
- * does not change ' to &apos; because &apos; is not a valid HTML 4 entity,
+ * does not change ' to &amp;apos; because &amp;apos; is not a valid HTML 4 entity,
* and is displayed literally in IE7.
- *
- * @since 2.6.0
*/
gchar *purple_markup_escape_text(const gchar *text, gssize length);
/**
+ * purple_markup_find_tag:
+ * @needle: The name of the tag
+ * @haystack: The null-delimited string to search in
+ * @start: A pointer to the start of the tag if found
+ * @end: A pointer to the end of the tag if found
+ * @attributes: The attributes, if the tag was found. This should
+ * be freed with g_datalist_clear().
+ *
* Finds an HTML tag matching the given name.
*
* This locates an HTML tag's start and end, and stores its attributes
* in a GData hash table. The names of the attributes are lower-cased
* in the hash table, and the name of the tag is case insensitive.
*
- * @param needle The name of the tag
- * @param haystack The null-delimited string to search in
- * @param start A pointer to the start of the tag if found
- * @param end A pointer to the end of the tag if found
- * @param attributes The attributes, if the tag was found. This should
- * be freed with g_datalist_clear().
- * @return TRUE if the tag was found
+ * Returns: TRUE if the tag was found
*/
gboolean purple_markup_find_tag(const char *needle, const char *haystack,
const char **start, const char **end,
GData **attributes);
/**
+ * purple_markup_extract_info_field:
+ * @str: The string to parse.
+ * @len: The size of str.
+ * @user_info: The destination PurpleNotifyUserInfo to which the new
+ * field info should be added.
+ * @start_token: The beginning token.
+ * @skip: The number of characters to skip after the
+ * start token.
+ * @end_token: The ending token.
+ * @check_value: The value that the last character must meet.
+ * @no_value_token: The token indicating no value is given.
+ * @display_name: The short descriptive name to display for this token.
+ * @is_link: TRUE if this should be a link, or FALSE otherwise.
+ * @link_prefix: The prefix for the link.
+ * @format_cb: A callback to format the value before adding it.
+ *
* Extracts a field of data from HTML.
*
* This is a scary function. See protocols/msn/msn.c and
* protocols/yahoo/yahoo_profile.c for example usage.
*
- * @param str The string to parse.
- * @param len The size of str.
- * @param user_info The destination PurpleNotifyUserInfo to which the new
- * field info should be added.
- * @param start_token The beginning token.
- * @param skip The number of characters to skip after the
- * start token.
- * @param end_token The ending token.
- * @param check_value The value that the last character must meet.
- * @param no_value_token The token indicating no value is given.
- * @param display_name The short descriptive name to display for this token.
- * @param is_link TRUE if this should be a link, or FALSE otherwise.
- * @param link_prefix The prefix for the link.
- * @param format_cb A callback to format the value before adding it.
- *
- * @return TRUE if successful, or FALSE otherwise.
+ * Returns: TRUE if successful, or FALSE otherwise.
*/
gboolean purple_markup_extract_info_field(const char *str, int len, PurpleNotifyUserInfo *user_info,
const char *start_token, int skip,
@@ -485,70 +602,82 @@ gboolean purple_markup_extract_info_field(const char *str, int len, PurpleNotify
PurpleInfoFieldFormatCallback format_cb);
/**
- * Converts HTML markup to XHTML.
+ * purple_markup_html_to_xhtml:
+ * @html: The HTML markup.
+ * @dest_xhtml: The destination XHTML output.
+ * @dest_plain: The destination plain-text output.
*
- * @param html The HTML markup.
- * @param dest_xhtml The destination XHTML output.
- * @param dest_plain The destination plain-text output.
+ * Converts HTML markup to XHTML.
*/
void purple_markup_html_to_xhtml(const char *html, char **dest_xhtml,
char **dest_plain);
/**
- * Strips HTML tags from a string.
+ * purple_markup_strip_html:
+ * @str: The string to strip HTML from.
*
- * @param str The string to strip HTML from.
+ * Strips HTML tags from a string.
*
- * @return The new string without HTML. You must g_free this string
+ * Returns: The new string without HTML. You must g_free this string
* when finished with it.
*/
char *purple_markup_strip_html(const char *str);
/**
- * Adds the necessary HTML code to turn URIs into HTML links in a string.
+ * purple_markup_linkify:
+ * @str: The string to linkify.
*
- * @param str The string to linkify.
+ * Adds the necessary HTML code to turn URIs into HTML links in a string.
*
- * @return The new string with all URIs surrounded in standard
- * HTML <a href="whatever"></a> tags. You must g_free this
- * string when finished with it.
+ * Returns: The new string with all URIs surrounded in standard
+ * HTML &lt;a href="whatever"&gt;&lt;/a&gt; tags. You must g_free()
+ * this string when finished with it.
*/
char *purple_markup_linkify(const char *str);
/**
+ * purple_unescape_text:
+ * @text: The string in which to unescape any HTML entities
+ *
* Unescapes HTML entities to their literal characters in the text.
- * For example "&amp;" is replaced by '&' and so on. Also converts
- * numerical entities (e.g. "&#38;" is also '&').
+ * For example "&amp;amp;" is replaced by '&amp;' and so on. Also converts
+ * numerical entities (e.g. "&amp;\#38;" is also '&amp;').
*
* This function currently supports the following named entities:
- * "&amp;", "&lt;", "&gt;", "&copy;", "&quot;", "&reg;", "&apos;"
+ * "&amp;amp;", "&amp;lt;", "&amp;gt;", "&amp;copy;", "&amp;quot;",
+ * "&amp;reg;", "&amp;apos;"
*
- * purple_unescape_html() is similar, but also converts "<br>" into "\n".
+ * purple_unescape_html() is similar, but also converts "&lt;br&gt;" into "\n".
*
- * @param text The string in which to unescape any HTML entities
+ * See purple_unescape_html()
*
- * @return The text with HTML entities literalized. You must g_free
+ * Returns: The text with HTML entities literalized. You must g_free
* this string when finished with it.
- *
- * @see purple_unescape_html()
- * @since 2.7.0
*/
char *purple_unescape_text(const char *text);
/**
+ * purple_unescape_html:
+ * @html: The string in which to unescape any HTML entities
+ *
* Unescapes HTML entities to their literal characters and converts
- * "<br>" to "\n". See purple_unescape_text() for more details.
+ * "&lt;br&gt;" to "\n". See purple_unescape_text() for more details.
*
- * @param html The string in which to unescape any HTML entities
+ * See purple_unescape_text()
*
- * @return The text with HTML entities literalized. You must g_free
+ * Returns: The text with HTML entities literalized. You must g_free
* this string when finished with it.
- *
- * @see purple_unescape_text()
*/
char *purple_unescape_html(const char *html);
/**
+ * purple_markup_slice:
+ * @str: The input NUL terminated, HTML, UTF-8 (or ASCII) string.
+ * @x: The character offset into an unformatted version of str to
+ * begin at.
+ * @y: The character offset (into an unformatted vesion of str) of
+ * one past the last character to include in the slice.
+ *
* Returns a newly allocated substring of the HTML UTF-8 string "str".
* The markup is preserved such that the substring will have the same
* formatting as original string, even though some tags may have been
@@ -561,175 +690,182 @@ char *purple_unescape_html(const char *html);
* when used with other UI's. libpurple users are encouraged to report and
* work out any problems encountered.
*
- * @param str The input NUL terminated, HTML, UTF-8 (or ASCII) string.
- * @param x The character offset into an unformatted version of str to
- * begin at.
- * @param y The character offset (into an unformatted vesion of str) of
- * one past the last character to include in the slice.
- *
- * @return The HTML slice of string, with all formatting retained.
+ * Returns: The HTML slice of string, with all formatting retained.
*/
char *purple_markup_slice(const char *str, guint x, guint y);
/**
+ * purple_markup_get_tag_name:
+ * @tag: The string starting a HTML tag.
+ *
* Returns a newly allocated string containing the name of the tag
* located at "tag". Tag is expected to point to a '<', and contain
* a '>' sometime after that. If there is no '>' and the string is
* not NUL terminated, this function can be expected to segfault.
*
- * @param tag The string starting a HTML tag.
- * @return A string containing the name of the tag.
+ * Returns: A string containing the name of the tag.
*/
char *purple_markup_get_tag_name(const char *tag);
/**
+ * purple_markup_unescape_entity:
+ * @text: A string containing an HTML entity.
+ * @length: If not %NULL, the string length of the entity is stored in this location.
+ *
* Returns a constant string of the character representation of the HTML
- * entity pointed to by @a text. For example, purple_markup_unescape_entity("&amp;")
- * will return "&". The @a text variable is expected to point to an '&',
+ * entity pointed to by @text. For example, purple_markup_unescape_entity("&amp;amp;")
+ * will return "&amp;". The @text variable is expected to point to an '&amp;',
* the first character of the entity. If given an unrecognized entity, the function
- * returns @c NULL.
+ * returns %NULL.
*
* Note that this function, unlike purple_unescape_html(), does not search
* the string for the entity, does not replace the entity, and does not
* return a newly allocated string.
*
- * @param text A string containing an HTML entity.
- * @param length If not @c NULL, the string length of the entity is stored in this location.
- *
- * @return A constant string containing the character representation of the given entity.
+ * Returns: A constant string containing the character representation of the given entity.
*/
const char * purple_markup_unescape_entity(const char *text, int *length);
/**
+ * purple_markup_get_css_property:
+ * @style: A string containing the inline CSS text.
+ * @opt: The requested CSS property.
+ *
* Returns a newly allocated string containing the value of the CSS property specified
- * in opt. The @a style argument is expected to point to a HTML inline CSS.
+ * in opt. The @style argument is expected to point to a HTML inline CSS.
* The function will seek for the CSS property and return its value.
*
* For example, purple_markup_get_css_property("direction:rtl;color:#dc4d1b;",
* "color") would return "#dc4d1b".
*
* On error or if the requested property was not found, the function returns
- * @c NULL.
- *
- * @param style A string containing the inline CSS text.
- * @param opt The requested CSS property.
+ * %NULL.
*
- * @return The value of the requested CSS property.
+ * Returns: The value of the requested CSS property.
*/
char * purple_markup_get_css_property(const gchar *style, const gchar *opt);
/**
- * Check if the given HTML contains RTL text.
- *
- * @param html The HTML text.
+ * purple_markup_is_rtl:
+ * @html: The HTML text.
*
- * @return TRUE if the text contains RTL text, FALSE otherwise.
+ * Check if the given HTML contains RTL text.
*
- * @since 2.6.0
+ * Returns: TRUE if the text contains RTL text, FALSE otherwise.
*/
gboolean purple_markup_is_rtl(const char *html);
-/*@}*/
-
/**************************************************************************/
-/** @name Path/Filename Functions */
+/* Path/Filename Functions */
/**************************************************************************/
-/*@{*/
/**
+ * purple_home_dir:
+ *
* Returns the user's home directory.
*
- * @return The user's home directory.
+ * See purple_user_dir()
*
- * @see purple_user_dir()
+ * Returns: The user's home directory.
*/
const gchar *purple_home_dir(void);
/**
+ * purple_user_dir:
+ *
* Returns the purple settings directory in the user's home directory.
* This is usually ~/.purple
*
- * @return The purple settings directory.
+ * See purple_home_dir()
*
- * @see purple_home_dir()
+ * Returns: The purple settings directory.
*/
const char *purple_user_dir(void);
/**
+ * purple_util_set_user_dir:
+ * @dir: The custom settings directory
+ *
* Define a custom purple settings directory, overriding the default (user's home directory/.purple)
- * @param dir The custom settings directory
*/
void purple_util_set_user_dir(const char *dir);
/**
+ * purple_build_dir:
+ * @path: The path you wish to create. Note that it must start
+ * from the root or this function will fail.
+ * @mode: Unix-style permissions for this directory.
+ *
* Builds a complete path from the root, making any directories along
* the path which do not already exist.
*
- * @param path The path you wish to create. Note that it must start
- * from the root or this function will fail.
- * @param mode Unix-style permissions for this directory.
- *
- * @return 0 for success, nonzero on any error.
+ * Returns: 0 for success, nonzero on any error.
*/
int purple_build_dir(const char *path, int mode);
/**
+ * purple_util_write_data_to_file:
+ * @filename: The basename of the file to write in the purple_user_dir.
+ * @data: A null-terminated string of data to write.
+ * @size: The size of the data to save. If data is
+ * null-terminated you can pass in -1.
+ *
* Write a string of data to a file of the given name in the Purple
* user directory ($HOME/.purple by default). The data is typically
* a serialized version of one of Purple's config files, such as
* prefs.xml, accounts.xml, etc. And the string is typically
- * obtained using xmlnode_to_formatted_str. However, this function
+ * obtained using purple_xmlnode_to_formatted_str. However, this function
* should work fine for saving binary files as well.
*
- * @param filename The basename of the file to write in the purple_user_dir.
- * @param data A null-terminated string of data to write.
- * @param size The size of the data to save. If data is
- * null-terminated you can pass in -1.
- *
- * @return TRUE if the file was written successfully. FALSE otherwise.
+ * Returns: TRUE if the file was written successfully. FALSE otherwise.
*/
gboolean purple_util_write_data_to_file(const char *filename, const char *data,
gssize size);
/**
+ * purple_util_write_data_to_file_absolute:
+ * @filename_full: Filename to write to
+ * @data: A null-terminated string of data to write.
+ * @size: The size of the data to save. If data is
+ * null-terminated you can pass in -1.
+ *
* Write data to a file using the absolute path.
*
* This exists for Glib backwards compatibility reasons.
*
- * @param filename_full Filename to write to
- * @param data A null-terminated string of data to write.
- * @param size The size of the data to save. If data is
- * null-terminated you can pass in -1.
- *
- * @return TRUE if the file was written successfully. FALSE otherwise.
- *
- * @todo Remove this function (use g_file_set_contents instead) when 3.0.0
- * rolls around.
- * @see purple_util_write_data_to_file()
+ * See purple_util_write_data_to_file()
*
+ * Returns: TRUE if the file was written successfully. FALSE otherwise.
*/
+/* TODO: Remove this function (use g_file_set_contents instead) when 3.0.0
+ * rolls around. */
gboolean
purple_util_write_data_to_file_absolute(const char *filename_full, const char *data, gssize size);
/**
- * Read the contents of a given file and parse the results into an
- * xmlnode tree structure. This is intended to be used to read
- * Purple's configuration xml files (prefs.xml, pounces.xml, etc.)
- *
- * @param filename The basename of the file to open in the purple_user_dir.
- * @param description A very short description of the contents of this
+ * purple_util_read_xml_from_file:
+ * @filename: The basename of the file to open in the purple_user_dir.
+ * @description: A very short description of the contents of this
* file. This is used in error messages shown to the
* user when the file can not be opened. For example,
* "preferences," or "buddy pounces."
*
- * @return An xmlnode tree of the contents of the given file. Or NULL, if
+ * Read the contents of a given file and parse the results into an
+ * PurpleXmlNode tree structure. This is intended to be used to read
+ * Purple's configuration xml files (prefs.xml, pounces.xml, etc.)
+ *
+ * Returns: An PurpleXmlNode tree of the contents of the given file. Or NULL, if
* the file does not exist or there was an error reading the file.
*/
-xmlnode *purple_util_read_xml_from_file(const char *filename,
+PurpleXmlNode *purple_util_read_xml_from_file(const char *filename,
const char *description);
/**
+ * purple_mkstemp:
+ * @path: The returned path to the temp file.
+ * @binary: Text or binary, for platforms where it matters.
+ *
* Creates a temporary file and returns a file pointer to it.
*
* This is like mkstemp(), but returns a file pointer and uses a
@@ -737,244 +873,251 @@ xmlnode *purple_util_read_xml_from_file(const char *filename,
* directory to use and allocates the space for the file path.
*
* The caller is responsible for closing the file and removing it when
- * done, as well as freeing the space pointed to by @a path with
+ * done, as well as freeing the space pointed to by @path with
* g_free().
*
- * @param path The returned path to the temp file.
- * @param binary Text or binary, for platforms where it matters.
- *
- * @return A file pointer to the temporary file, or @c NULL on failure.
+ * Returns: A file pointer to the temporary file, or %NULL on failure.
*/
FILE *purple_mkstemp(char **path, gboolean binary);
-/**
- * Returns an extension corresponding to the image data's file type.
- *
- * @param data A pointer to the image data
- * @param len The length of the image data
- *
- * @return The appropriate extension, or "icon" if unknown.
- */
-const char *
-purple_util_get_image_extension(gconstpointer data, size_t len);
-
-/**
- * Returns a SHA-1 hash string of the data passed in.
- */
-char *purple_util_get_image_checksum(gconstpointer image_data, size_t image_len);
-
-/**
- * @return A hex encoded version of the SHA-1 hash of the data passed
- * in with the correct file extention appended. The file
- * extension is determined by calling
- * purple_util_get_image_extension(). This return value must
- * be g_freed by the caller.
- */
-char *purple_util_get_image_filename(gconstpointer image_data, size_t image_len);
-
-/*@}*/
-
/**************************************************************************/
-/** @name Environment Detection Functions */
+/* Environment Detection Functions */
/**************************************************************************/
-/*@{*/
/**
- * Checks if the given program name is valid and executable.
+ * purple_program_is_valid:
+ * @program: The file name of the application.
*
- * @param program The file name of the application.
+ * Checks if the given program name is valid and executable.
*
- * @return TRUE if the program is runable.
+ * Returns: TRUE if the program is runable.
*/
gboolean purple_program_is_valid(const char *program);
/**
+ * purple_running_gnome:
+ *
* Check if running GNOME.
*
- * @return TRUE if running GNOME, FALSE otherwise.
+ * Returns: TRUE if running GNOME, FALSE otherwise.
*/
gboolean purple_running_gnome(void);
/**
+ * purple_running_kde:
+ *
* Check if running KDE.
*
- * @return TRUE if running KDE, FALSE otherwise.
+ * Returns: TRUE if running KDE, FALSE otherwise.
*/
gboolean purple_running_kde(void);
/**
+ * purple_running_osx:
+ *
* Check if running OS X.
*
- * @return TRUE if running OS X, FALSE otherwise.
+ * Returns: TRUE if running OS X, FALSE otherwise.
*/
gboolean purple_running_osx(void);
/**
- * Returns the IP address from a socket file descriptor.
+ * purple_fd_get_ip:
+ * @fd: The socket file descriptor.
*
- * @param fd The socket file descriptor.
+ * Returns the IP address from a socket file descriptor.
*
- * @return The IP address, or @c NULL on error.
+ * Returns: The IP address, or %NULL on error.
*/
char *purple_fd_get_ip(int fd);
/**
- * Returns the address family of a socket.
+ * purple_socket_get_family:
+ * @fd: The socket file descriptor.
*
- * @param fd The socket file descriptor.
+ * Returns the address family of a socket.
*
- * @return The address family of the socket (AF_INET, AF_INET6, etc) or -1
+ * Returns: The address family of the socket (AF_INET, AF_INET6, etc) or -1
* on error.
- * @since 2.7.0
*/
int purple_socket_get_family(int fd);
/**
+ * purple_socket_speaks_ipv4:
+ * @fd: The socket file descriptor
+ *
* Returns TRUE if a socket is capable of speaking IPv4.
*
* This is the case for IPv4 sockets and, on some systems, IPv6 sockets
* (due to the IPv4-mapped address functionality).
*
- * @param fd The socket file descriptor
- * @return TRUE if a socket can speak IPv4.
- * @since 2.7.0
+ * Returns: TRUE if a socket can speak IPv4.
*/
gboolean purple_socket_speaks_ipv4(int fd);
-/*@}*/
-
/**************************************************************************/
-/** @name String Functions */
+/* String Functions */
/**************************************************************************/
-/*@{*/
/**
+ * purple_strequal:
+ * @left: A string
+ * @right: A string to compare with left
+ *
* Tests two strings for equality.
*
* Unlike strcmp(), this function will not crash if one or both of the
- * strings are @c NULL.
+ * strings are %NULL.
*
- * @param left A string
- * @param right A string to compare with left
- *
- * @return @c TRUE if the strings are the same, else @c FALSE.
- *
- * @since 2.6.0
+ * Returns: %TRUE if the strings are the same, else %FALSE.
*/
gboolean purple_strequal(const gchar *left, const gchar *right);
/**
+ * purple_normalize:
+ * @account: The account the string belongs to, or NULL if you do
+ * not know the account. If you use NULL, the string
+ * will still be normalized, but if the protocol uses a
+ * custom normalization function then the string may
+ * not be normalized correctly.
+ * @str: The string to normalize.
+ *
* Normalizes a string, so that it is suitable for comparison.
*
* The returned string will point to a static buffer, so if the
- * string is intended to be kept long-term, you <i>must</i>
+ * string is intended to be kept long-term, you <emphasis>must</emphasis>
* g_strdup() it. Also, calling normalize() twice in the same line
* will lead to problems.
*
- * @param account The account the string belongs to, or NULL if you do
- * not know the account. If you use NULL, the string
- * will still be normalized, but if the PRPL uses a
- * custom normalization function then the string may
- * not be normalized correctly.
- * @param str The string to normalize.
- *
- * @return A pointer to the normalized version stored in a static buffer.
+ * Returns: A pointer to the normalized version stored in a static buffer.
*/
const char *purple_normalize(const PurpleAccount *account, const char *str);
/**
+ * purple_normalize_nocase:
+ * @account: The account the string belongs to.
+ * @str: The string to normalize.
+ *
* Normalizes a string, so that it is suitable for comparison.
*
- * This is one possible implementation for the PRPL callback
+ * This is one possible implementation for the protocol callback
* function "normalize." It returns a lowercase and UTF-8
* normalized version of the string.
*
- * @param account The account the string belongs to.
- * @param str The string to normalize.
- *
- * @return A pointer to the normalized version stored in a static buffer.
+ * Returns: A pointer to the normalized version stored in a static buffer.
*/
const char *purple_normalize_nocase(const PurpleAccount *account, const char *str);
/**
+ * purple_validate:
+ * @prpl: The protocol plugin the string belongs to.
+ * @str: The string to validate.
+ *
+ * Checks, if a string is valid.
+ *
+ * Returns: TRUE, if string is valid, otherwise FALSE.
+ */
+gboolean purple_validate(const PurplePlugin *prpl, const char *str);
+
+/**
+ * purple_str_has_prefix:
+ * @s: The string to check.
+ * @p: The prefix in question.
+ *
* Compares two strings to see if the first contains the second as
* a proper prefix.
*
- * @param s The string to check.
- * @param p The prefix in question.
- *
- * @return TRUE if p is a prefix of s, otherwise FALSE.
+ * Returns: TRUE if p is a prefix of s, otherwise FALSE.
*/
gboolean purple_str_has_prefix(const char *s, const char *p);
/**
+ * purple_str_has_caseprefix:
+ * @s: The string to check.
+ * @p: The prefix in question.
+ *
+ * Compares two strings to see if the first contains the second as
+ * a proper case-insensitive prefix.
+ *
+ * Returns: %TRUE if @p is a prefix of @s, otherwise %FALSE.
+ */
+gboolean
+purple_str_has_caseprefix(const gchar *s, const gchar *p);
+
+/**
+ * purple_str_has_suffix:
+ * @s: The string to check.
+ * @x: The suffix in question.
+ *
* Compares two strings to see if the second is a proper suffix
* of the first.
*
- * @param s The string to check.
- * @param x The suffix in question.
- *
- * @return TRUE if x is a a suffix of s, otherwise FALSE.
+ * Returns: TRUE if x is a a suffix of s, otherwise FALSE.
*/
gboolean purple_str_has_suffix(const char *s, const char *x);
/**
+ * purple_strdup_withhtml:
+ * @src: The source string.
+ *
* Duplicates a string and replaces all newline characters from the
* source string with HTML linebreaks.
*
- * @param src The source string.
- *
- * @return The new string. Must be g_free'd by the caller.
+ * Returns: The new string. Must be g_free'd by the caller.
*/
gchar *purple_strdup_withhtml(const gchar *src);
/**
- * Ensures that all linefeeds have a matching carriage return.
+ * purple_str_add_cr:
+ * @str: The source string.
*
- * @param str The source string.
+ * Ensures that all linefeeds have a matching carriage return.
*
- * @return The string with carriage returns.
+ * Returns: The string with carriage returns.
*/
char *purple_str_add_cr(const char *str);
/**
+ * purple_str_strip_char:
+ * @str: The string to strip characters from.
+ * @thechar: The character to strip from the given string.
+ *
* Strips all instances of the given character from the
* given string. The string is modified in place. This
* is useful for stripping new line characters, for example.
*
* Example usage:
* purple_str_strip_char(my_dumb_string, '\n');
- *
- * @param str The string to strip characters from.
- * @param thechar The character to strip from the given string.
*/
void purple_str_strip_char(char *str, char thechar);
/**
+ * purple_util_chrreplace:
+ * @string: The string from which to replace stuff.
+ * @delimiter: The character you want replaced.
+ * @replacement: The character you want inserted in place
+ * of the delimiting character.
+ *
* Given a string, this replaces all instances of one character
* with another. This happens inline (the original string IS
* modified).
- *
- * @param string The string from which to replace stuff.
- * @param delimiter The character you want replaced.
- * @param replacement The character you want inserted in place
- * of the delimiting character.
*/
void purple_util_chrreplace(char *string, char delimiter,
char replacement);
/**
+ * purple_strreplace:
+ * @string: The string from which to replace stuff.
+ * @delimiter: The substring you want replaced.
+ * @replacement: The substring you want inserted in place
+ * of the delimiting substring.
+ *
* Given a string, this replaces one substring with another
* and returns a newly allocated string.
*
- * @param string The string from which to replace stuff.
- * @param delimiter The substring you want replaced.
- * @param replacement The substring you want inserted in place
- * of the delimiting substring.
- *
- * @return A new string, after performing the substitution.
+ * Returns: A new string, after performing the substitution.
* free this with g_free().
*/
gchar *purple_strreplace(const char *string, const char *delimiter,
@@ -982,485 +1125,383 @@ gchar *purple_strreplace(const char *string, const char *delimiter,
/**
+ * purple_utf8_ncr_encode:
+ * @in: The string which might contain utf-8 substrings
+ *
* Given a string, this replaces any utf-8 substrings in that string with
* the corresponding numerical character reference, and returns a newly
* allocated string.
*
- * @param in The string which might contain utf-8 substrings
- *
- * @return A new string, with utf-8 replaced with numerical character
+ * Returns: A new string, with utf-8 replaced with numerical character
* references, free this with g_free()
*/
char *purple_utf8_ncr_encode(const char *in);
/**
+ * purple_utf8_ncr_decode:
+ * @in: The string which might contain numerical character references.
+ *
* Given a string, this replaces any numerical character references
* in that string with the corresponding actual utf-8 substrings,
* and returns a newly allocated string.
*
- * @param in The string which might contain numerical character references.
- *
- * @return A new string, with numerical character references
+ * Returns: A new string, with numerical character references
* replaced with actual utf-8, free this with g_free().
*/
char *purple_utf8_ncr_decode(const char *in);
/**
+ * purple_strcasereplace:
+ * @string: The string from which to replace stuff.
+ * @delimiter: The substring you want replaced.
+ * @replacement: The substring you want inserted in place
+ * of the delimiting substring.
+ *
* Given a string, this replaces one substring with another
* ignoring case and returns a newly allocated string.
*
- * @param string The string from which to replace stuff.
- * @param delimiter The substring you want replaced.
- * @param replacement The substring you want inserted in place
- * of the delimiting substring.
- *
- * @return A new string, after performing the substitution.
+ * Returns: A new string, after performing the substitution.
* free this with g_free().
*/
gchar *purple_strcasereplace(const char *string, const char *delimiter,
const char *replacement);
/**
+ * purple_strcasestr:
+ * @haystack: The string to search in.
+ * @needle: The substring to find.
+ *
* This is like strstr, except that it ignores ASCII case in
* searching for the substring.
*
- * @param haystack The string to search in.
- * @param needle The substring to find.
- *
- * @return the location of the substring if found, or NULL if not
+ * Returns: the location of the substring if found, or NULL if not
*/
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.)
*
- * @param size The size
- *
- * @return The string in units form. This must be freed.
+ * Returns: The string in units form. This must be freed.
*/
-char *purple_str_size_to_units(size_t size);
+char *purple_str_size_to_units(goffset size);
/**
- * Converts seconds into a human-readable form.
+ * purple_str_seconds_to_string:
+ * @sec: The seconds.
*
- * @param sec The seconds.
+ * Converts seconds into a human-readable form.
*
- * @return A human-readable form, containing days, hours, minutes, and
+ * Returns: A human-readable form, containing days, hours, minutes, and
* seconds.
*/
char *purple_str_seconds_to_string(guint sec);
/**
+ * purple_str_binary_to_ascii:
+ * @binary: A string of random data, possibly with embedded NULs
+ * and such.
+ * @len: The length in bytes of the input string. Must not be 0.
+ *
* Converts a binary string into a NUL terminated ascii string,
* replacing nonascii characters and characters below SPACE (including
* NUL) into \\xyy, where yy are two hex digits. Also backslashes are
* changed into two backslashes (\\\\). The returned, newly allocated
* string can be outputted to the console, and must be g_free()d.
*
- * @param binary A string of random data, possibly with embedded NULs
- * and such.
- * @param len The length in bytes of the input string. Must not be 0.
- *
- * @return A newly allocated ASCIIZ string.
+ * Returns: A newly allocated ASCIIZ string.
*/
char *purple_str_binary_to_ascii(const unsigned char *binary, guint len);
-/*@}*/
-
-
-/**************************************************************************/
-/** @name URI/URL Functions */
-/**************************************************************************/
-/*@{*/
-
-void purple_got_protocol_handler_uri(const char *uri);
/**
- * Parses a URL, returning its host, port, file path, username and password.
- *
- * The returned data must be freed.
- *
- * @param url The URL to parse.
- * @param ret_host The returned host.
- * @param ret_port The returned port.
- * @param ret_path The returned path.
- * @param ret_user The returned username.
- * @param ret_passwd The returned password.
- */
-gboolean purple_url_parse(const char *url, char **ret_host, int *ret_port,
- char **ret_path, char **ret_user, char **ret_passwd);
-
-/**
- * This is the signature used for functions that act as the callback
- * to purple_util_fetch_url() or purple_util_fetch_url_request().
- *
- * @param url_data The same value that was returned when you called
- * purple_fetch_url() or purple_fetch_url_request().
- * @param user_data The user data that your code passed into either
- * purple_util_fetch_url() or purple_util_fetch_url_request().
- * @param url_text This will be NULL on error. Otherwise this
- * will contain the contents of the URL.
- * @param len 0 on error, otherwise this is the length of buf.
- * @param error_message If something went wrong then this will contain
- * a descriptive error message, and buf will be
- * NULL and len will be 0.
- */
-typedef void (*PurpleUtilFetchUrlCallback)(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message);
-
-/**
- * Fetches the data from a URL, and passes it to a callback function.
- *
- * @param url The URL.
- * @param full TRUE if this is the full URL, or FALSE if it's a
- * partial URL.
- * @param user_agent The user agent field to use, or NULL.
- * @param http11 TRUE if HTTP/1.1 should be used to download the file.
- * @param cb The callback function.
- * @param data The user data to pass to the callback function.
+ * purple_utf16_size:
+ * @str: String to check.
+ *
+ * Calculates UTF-16 string size (in bytes).
+ *
+ * Returns: Number of bytes (including NUL character) that string occupies.
*/
-#define purple_util_fetch_url(url, full, user_agent, http11, cb, data) \
- purple_util_fetch_url_request(url, full, user_agent, http11, NULL, \
- FALSE, cb, data);
+size_t purple_utf16_size(const gunichar2 *str);
/**
- * Fetches the data from a URL, and passes it to a callback function.
+ * purple_str_wipe:
+ * @str: A NUL-terminated string to free, or a NULL-pointer.
*
- * @param url The URL.
- * @param full TRUE if this is the full URL, or FALSE if it's a
- * partial URL.
- * @param user_agent The user agent field to use, or NULL.
- * @param http11 TRUE if HTTP/1.1 should be used to download the file.
- * @param max_len The maximum number of bytes to retrieve (-1 for unlimited)
- * @param cb The callback function.
- * @param data The user data to pass to the callback function.
- * @deprecated In 3.0.0, we'll rename this to "purple_util_fetch_url" and get rid of the old one
+ * Fills a NUL-terminated string with zeros and frees it.
+ *
+ * It should be used to free sensitive data, like passwords.
*/
-#define purple_util_fetch_url_len(url, full, user_agent, http11, max_len, cb, data) \
- purple_util_fetch_url_request_len(url, full, user_agent, http11, NULL, \
- FALSE, max_len, cb, data);
+void purple_str_wipe(gchar *str);
/**
- * Fetches the data from a URL, and passes it to a callback function.
+ * purple_utf16_wipe:
+ * @str: A NUL-terminated string to free, or a NULL-pointer.
+ *
+ * Fills a NUL-terminated UTF-16 string with zeros and frees it.
*
- * @param url The URL.
- * @param full TRUE if this is the full URL, or FALSE if it's a
- * partial URL.
- * @param user_agent The user agent field to use, or NULL.
- * @param http11 TRUE if HTTP/1.1 should be used to download the file.
- * @param request A HTTP request to send to the server instead of the
- * standard GET
- * @param include_headers
- * If TRUE, include the HTTP headers in the response.
- * @param callback The callback function.
- * @param data The user data to pass to the callback function.
+ * It should be used to free sensitive data, like passwords.
*/
-PurpleUtilFetchUrlData *purple_util_fetch_url_request(const gchar *url,
- gboolean full, const gchar *user_agent, gboolean http11,
- const gchar *request, gboolean include_headers,
- PurpleUtilFetchUrlCallback callback, gpointer data);
+void purple_utf16_wipe(gunichar2 *str);
-/**
- * Fetches the data from a URL, and passes it to a callback function.
- *
- * @param url The URL.
- * @param full TRUE if this is the full URL, or FALSE if it's a
- * partial URL.
- * @param user_agent The user agent field to use, or NULL.
- * @param http11 TRUE if HTTP/1.1 should be used to download the file.
- * @param request A HTTP request to send to the server instead of the
- * standard GET
- * @param include_headers
- * If TRUE, include the HTTP headers in the response.
- * @param max_len The maximum number of bytes to retrieve (-1 for unlimited)
- * @param callback The callback function.
- * @param data The user data to pass to the callback function.
- * @deprecated In 3.0.0, this will go away.
- */
-PurpleUtilFetchUrlData *purple_util_fetch_url_request_len(const gchar *url,
- gboolean full, const gchar *user_agent, gboolean http11,
- const gchar *request, gboolean include_headers, gssize max_len,
- PurpleUtilFetchUrlCallback callback, gpointer data);
-
-/**
- * Fetches the data from a URL, and passes it to a callback function.
- *
- * @param account The account for which the request is needed, or NULL.
- * @param url The URL.
- * @param full TRUE if this is the full URL, or FALSE if it's a
- * partial URL.
- * @param user_agent The user agent field to use, or NULL.
- * @param http11 TRUE if HTTP/1.1 should be used to download the file.
- * @param request A HTTP request to send to the server instead of the
- * standard GET
- * @param include_headers
- * If TRUE, include the HTTP headers in the response.
- * @param max_len The maximum number of bytes to retrieve, or a negative
- * number to use the default max of 512 KiB.
- * @param callback The callback function.
- * @param data The user data to pass to the callback function.
- * @deprecated In 3.0.0, we'll rename this to "purple_util_fetch_url_request" and get rid of the old one
- */
-PurpleUtilFetchUrlData *purple_util_fetch_url_request_len_with_account(
- PurpleAccount *account, const gchar *url,
- gboolean full, const gchar *user_agent, gboolean http11,
- const gchar *request, gboolean include_headers, gssize max_len,
- PurpleUtilFetchUrlCallback callback, gpointer data);
-
-/**
- * Cancel a pending URL request started with either
- * purple_util_fetch_url_request() or purple_util_fetch_url().
- *
- * @param url_data The data returned when you initiated the URL fetch.
- */
-void purple_util_fetch_url_cancel(PurpleUtilFetchUrlData *url_data);
+
+/**************************************************************************/
+/* URI/URL Functions */
+/**************************************************************************/
+
+void purple_got_protocol_handler_uri(const char *uri);
/**
+ * purple_url_decode:
+ * @str: The string to translate.
+ *
* Decodes a URL into a plain string.
*
* This will change hex codes and such to their ascii equivalents.
*
- * @param str The string to translate.
- *
- * @return The resulting string.
+ * Returns: The resulting string.
*/
const char *purple_url_decode(const char *str);
/**
+ * purple_url_encode:
+ * @str: The string to translate.
+ *
* Encodes a URL into an escaped string.
*
* This will change non-alphanumeric characters to hex codes.
*
- * @param str The string to translate.
- *
- * @return The resulting string.
+ * Returns: The resulting string.
*/
const char *purple_url_encode(const char *str);
/**
- * Checks if the given email address is syntactically valid.
+ * purple_email_is_valid:
+ * @address: The email address to validate.
*
- * @param address The email address to validate.
+ * Checks if the given email address is syntactically valid.
*
- * @return True if the email address is syntactically correct.
+ * Returns: True if the email address is syntactically correct.
*/
gboolean purple_email_is_valid(const char *address);
/**
- * Checks if the given IP address is a syntactically valid IPv4 address.
+ * purple_ip_address_is_valid:
+ * @ip: The IP address to validate.
*
- * @param ip The IP address to validate.
+ * Checks if the given IP address is a syntactically valid IPv4 or
+ * IPv6 address.
+ * If you specifically want to check for an IPv4 address use
+ * purple_ipv4_address_is_valid(), or for an IPv6 address use
+ * purple_ipv6_address_is_valid().
*
- * @return True if the IP address is syntactically correct.
- * @deprecated This function will be replaced with one that validates
- * as either IPv4 or IPv6 in 3.0.0. If you don't want this,
- * behavior, use one of the more specific functions.
+ * Returns: True if the IP address is syntactically correct.
*/
gboolean purple_ip_address_is_valid(const char *ip);
/**
- * Checks if the given IP address is a syntactically valid IPv4 address.
+ * purple_ipv4_address_is_valid:
+ * @ip: The IP address to validate.
*
- * @param ip The IP address to validate.
+ * Checks if the given IP address is a syntactically valid IPv4 address.
*
- * @return True if the IP address is syntactically correct.
- * @since 2.6.0
+ * Returns: True if the IP address is syntactically correct.
*/
gboolean purple_ipv4_address_is_valid(const char *ip);
/**
- * Checks if the given IP address is a syntactically valid IPv6 address.
+ * purple_ipv6_address_is_valid:
+ * @ip: The IP address to validate.
*
- * @param ip The IP address to validate.
+ * Checks if the given IP address is a syntactically valid IPv6 address.
*
- * @return True if the IP address is syntactically correct.
- * @since 2.6.0
+ * Returns: True if the IP address is syntactically correct.
*/
gboolean purple_ipv6_address_is_valid(const char *ip);
/**
+ * purple_uri_list_extract_uris:
+ * @uri_list: An uri-list in the standard format.
+ *
* This function extracts a list of URIs from the a "text/uri-list"
* string. It was "borrowed" from gnome_uri_list_extract_uris
*
- * @param uri_list An uri-list in the standard format.
- *
- * @return A GList containing strings allocated with g_malloc
+ * Returns: A GList containing strings allocated with g_malloc
* that have been splitted from uri-list.
*/
GList *purple_uri_list_extract_uris(const gchar *uri_list);
/**
+ * purple_uri_list_extract_filenames:
+ * @uri_list: A uri-list in the standard format.
+ *
* This function extracts a list of filenames from a
* "text/uri-list" string. It was "borrowed" from
* gnome_uri_list_extract_filenames
*
- * @param uri_list A uri-list in the standard format.
- *
- * @return A GList containing strings allocated with g_malloc that
+ * Returns: A GList containing strings allocated with g_malloc that
* contain the filenames in the uri-list. Note that unlike
* 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);
-/*@}*/
-
/**************************************************************************
* UTF8 String Functions
**************************************************************************/
-/*@{*/
/**
+ * purple_utf8_try_convert:
+ * @str: The source string.
+ *
* Attempts to convert a string to UTF-8 from an unknown encoding.
*
* This function checks the locale and tries sane defaults.
*
- * @param str The source string.
- *
- * @return The UTF-8 string, or @c NULL if it could not be converted.
+ * Returns: The UTF-8 string, or %NULL if it could not be converted.
*/
gchar *purple_utf8_try_convert(const char *str);
/**
+ * purple_utf8_salvage:
+ * @str: The source string.
+ *
* Salvages the valid UTF-8 characters from a string, replacing any
* invalid characters with a filler character (currently hardcoded to
* '?').
*
- * @param str The source string.
- *
- * @return A valid UTF-8 string.
+ * Returns: A valid UTF-8 string.
*/
gchar *purple_utf8_salvage(const char *str);
/**
+ * purple_utf8_strip_unprintables:
+ * @str: A valid UTF-8 string.
+ *
* Removes unprintable characters from a UTF-8 string. These characters
* (in particular low-ASCII characters) are invalid in XML 1.0 and thus
* are not allowed in XMPP and are rejected by libxml2 by default.
*
* The returned string must be freed by the caller.
*
- * @param str A valid UTF-8 string.
- *
- * @return A newly allocated UTF-8 string without the unprintable characters.
- * @since 2.6.0
+ * Returns: A newly allocated UTF-8 string without the unprintable characters.
*/
gchar *purple_utf8_strip_unprintables(const gchar *str);
/**
- * Return the UTF-8 version of gai_strerror(). It calls gai_strerror()
- * then converts the result to UTF-8. This function is analogous to
- * g_strerror().
+ * purple_gai_strerror:
+ * @errnum: The error code.
*
- * @param errnum The error code.
+ * Return the UTF-8 version of #gai_strerror. It calls #gai_strerror
+ * then converts the result to UTF-8. This function is analogous to
+ * g_strerror().
*
- * @return The UTF-8 error message.
- * @since 2.4.0
+ * Returns: The UTF-8 error message.
*/
-G_CONST_RETURN gchar *purple_gai_strerror(gint errnum);
+const gchar *purple_gai_strerror(gint errnum);
/**
+ * purple_utf8_strcasecmp:
+ * @a: The first string.
+ * @b: The second string.
+ *
* Compares two UTF-8 strings case-insensitively. This comparison is
* more expensive than a simple g_utf8_collate() comparison because
* it calls g_utf8_casefold() on each string, which allocates new
* strings.
*
- * @param a The first string.
- * @param b The second string.
- *
- * @return -1 if @a is less than @a b.
- * 0 if @a is equal to @a b.
- * 1 if @a is greater than @a b.
+ * Returns: -1 if @a is less than @b.
+ * 0 if @a is equal to @b.
+ * 1 if @a is greater than @b.
*/
int purple_utf8_strcasecmp(const char *a, const char *b);
/**
+ * purple_utf8_has_word:
+ * @haystack: The string to search in.
+ * @needle: The substring to find.
+ *
* Case insensitive search for a word in a string. The needle string
* must be contained in the haystack string and not be immediately
* preceded or immediately followed by another alpha-numeric character.
*
- * @param haystack The string to search in.
- * @param needle The substring to find.
- *
- * @return TRUE if haystack has the word, otherwise FALSE
+ * Returns: TRUE if haystack has the word, otherwise FALSE
*/
gboolean purple_utf8_has_word(const char *haystack, const char *needle);
/**
+ * purple_print_utf8_to_console:
+ * @filestream: The file stream (e.g. STDOUT or STDERR)
+ * @message: The message to print.
+ *
* Prints a UTF-8 message to the given file stream. The function
* tries to convert the UTF-8 message to user's locale. If this
* is not possible, the original UTF-8 text will be printed.
- *
- * @param filestream The file stream (e.g. STDOUT or STDERR)
- * @param message The message to print.
*/
void purple_print_utf8_to_console(FILE *filestream, char *message);
/**
- * Checks for messages starting (post-HTML) with "/me ", including the space.
+ * purple_message_meify:
+ * @message: The message to check
+ * @len: The message length, or -1
*
- * @param message The message to check
- * @param len The message length, or -1
+ * Checks for messages starting (post-HTML) with "/me ", including the space.
*
- * @return TRUE if it starts with "/me ", and it has been removed, otherwise
+ * Returns: TRUE if it starts with "/me ", and it has been removed, otherwise
* FALSE
*/
gboolean purple_message_meify(char *message, gssize len);
/**
+ * purple_text_strip_mnemonic:
+ * @in: The string to strip
+ *
* Removes the underscore characters from a string used identify the mnemonic
* character.
*
- * @param in The string to strip
- *
- * @return The stripped string
+ * Returns: The stripped string
*/
char *purple_text_strip_mnemonic(const char *in);
-/*@}*/
-
/**
- * Adds 8 to something.
- *
- * Blame SimGuy.
+ * purple_unescape_filename:
+ * @str: The string to translate.
*
- * @param x The number to add 8 to.
- *
- * @return x + 8
- */
-#define purple_add_eight(x) ((x)+8)
-
-/**
* Does the reverse of purple_escape_filename
*
* This will change hex codes and such to their ascii equivalents.
*
- * @param str The string to translate.
- *
- * @return The resulting string.
+ * Returns: The resulting string.
*/
const char *purple_unescape_filename(const char *str);
/**
- * Escapes filesystem-unfriendly characters from a filename
+ * purple_escape_filename:
+ * @str: The string to translate.
*
- * @param str The string to translate.
+ * Escapes filesystem-unfriendly characters from a filename
*
- * @return The resulting string.
+ * Returns: The resulting string.
*/
const char *purple_escape_filename(const char *str);
/**
- * This is added temporarily to assist the split of oscar into aim and icq.
- * This should not be used by plugins.
+ * purple_escape_js:
+ * @str: The string to escape.
+ *
+ * Escapes javascript-unfriendly substrings from a string.
*
- * @deprecated This function should not be used in new code and should be
- * removed in 3.0.0. The aim/icq prpl split happened a long
- * time ago, and we don't need to keep migrating old data.
+ * Returns: The javascript-safe string (must be g_free'd after use).
*/
-const char *_purple_oscar_convert(const char *act, const char *protocol);
+gchar * purple_escape_js(const gchar *str);
/**
+ * purple_restore_default_signal_handlers:
+ *
* Restore default signal handlers for signals which might reasonably have
* handlers. This should be called by a fork()'d child process, since child processes
* inherit the handlers of the parent.
@@ -1468,23 +1509,107 @@ const char *_purple_oscar_convert(const char *act, const char *protocol);
void purple_restore_default_signal_handlers(void);
/**
+ * purple_get_host_name:
+ *
* Gets the host name of the machine. If it not possible to determine the
* host name, "localhost" is returned
*
- * @constreturn The hostname
+ * Returns: The hostname
*/
const gchar *purple_get_host_name(void);
/**
+ * purple_uuid_random:
+ *
* Returns a type 4 (random) UUID
*
- * @return A UUID, caller is responsible for freeing it
- * @since 2.7.0
+ * Returns: A UUID, caller is responsible for freeing it
*/
gchar *purple_uuid_random(void);
-#ifdef __cplusplus
-}
-#endif
+/**
+ * purple_callback_set_zero:
+ * @data: A pointer to variable, which should be set to NULL.
+ *
+ * Sets given pointer to NULL.
+ *
+ * Function designed to be used as a GDestroyNotify callback.
+ */
+void purple_callback_set_zero(gpointer data);
+
+/**
+ * purple_value_new:
+ * @type: The type of data to be held by the GValue
+ *
+ * Creates a new GValue of the specified type.
+ *
+ * Returns: The created GValue
+ */
+GValue *purple_value_new(GType type);
+
+/**
+ * purple_value_dup:
+ * @value: The GValue to duplicate
+ *
+ * Duplicates a GValue.
+ *
+ * Returns: The duplicated GValue
+ */
+GValue *purple_value_dup(GValue *value);
+
+/**
+ * purple_value_free:
+ * @value: The GValue to free.
+ *
+ * Frees a GValue.
+ */
+void purple_value_free(GValue *value);
+
+/**
+ * purple_http_digest_calculate_session_key:
+ * @algorithm: The hash algorithm to use
+ * @username: The username provided by the user
+ * @realm: The authentication realm provided by the server
+ * @password: The password provided by the user
+ * @nonce: The nonce provided by the server
+ * @client_nonce: The nonce provided by the client
+ *
+ * Calculates a session key for HTTP Digest authentation
+ *
+ * See RFC 2617 for more information.
+ *
+ * Returns: The session key, or %NULL if an error occurred.
+ */
+gchar *purple_http_digest_calculate_session_key(
+ const gchar *algorithm, const gchar *username,
+ const gchar *realm, const gchar *password,
+ const gchar *nonce, const gchar *client_nonce);
+
+/**
+ * purple_http_digest_calculate_response:
+ * @algorithm: The hash algorithm to use
+ * @method: The HTTP method in use
+ * @digest_uri: The URI from the initial request
+ * @qop: The "quality of protection"
+ * @entity: The entity body
+ * @nonce: The nonce provided by the server
+ * @nonce_count: The nonce count
+ * @client_nonce: The nonce provided by the client
+ * @session_key: The session key from purple_http_digest_calculate_session_key()
+ *
+ * Calculate a response for HTTP Digest authentication
+ *
+ * See RFC 2617 for more information.
+ *
+ * Returns: The hashed response, or %NULL if an error occurred.
+ */
+gchar *purple_http_digest_calculate_response(
+ const gchar *algorithm, const gchar *method,
+ const gchar *digest_uri, const gchar *qop,
+ const gchar *entity, const gchar *nonce,
+ const gchar *nonce_count, const gchar *client_nonce,
+ const gchar *session_key);
+
+G_END_DECLS
#endif /* _PURPLE_UTIL_H_ */
diff --git a/libpurple/value.c b/libpurple/value.c
deleted file mode 100644
index c7002c60ed..0000000000
--- a/libpurple/value.c
+++ /dev/null
@@ -1,488 +0,0 @@
-/**
- * @file value.c Value wrapper API
- * @ingroup core
- */
-
-/* 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 "value.h"
-
-#define OUTGOING_FLAG 0x01
-
-PurpleValue *
-purple_value_new(PurpleType type, ...)
-{
- PurpleValue *value;
- va_list args;
-
- g_return_val_if_fail(type != PURPLE_TYPE_UNKNOWN, NULL);
-
- value = g_new0(PurpleValue, 1);
-
- value->type = type;
-
- va_start(args, type);
-
- if (type == PURPLE_TYPE_SUBTYPE)
- value->u.subtype = va_arg(args, int);
- else if (type == PURPLE_TYPE_BOXED)
- value->u.specific_type = g_strdup(va_arg(args, char *));
-
- va_end(args);
-
- return value;
-}
-
-PurpleValue *
-purple_value_new_outgoing(PurpleType type, ...)
-{
- PurpleValue *value;
- va_list args;
-
- g_return_val_if_fail(type != PURPLE_TYPE_UNKNOWN, NULL);
-
- value = g_new0(PurpleValue, 1);
-
- value->type = type;
-
- va_start(args, type);
-
- if (type == PURPLE_TYPE_SUBTYPE)
- value->u.subtype = va_arg(args, int);
- else if (type == PURPLE_TYPE_BOXED)
- value->u.specific_type = g_strdup(va_arg(args, char *));
-
- va_end(args);
-
- value->flags |= OUTGOING_FLAG;
-
- return value;
-}
-
-void
-purple_value_destroy(PurpleValue *value)
-{
- g_return_if_fail(value != NULL);
-
- if (purple_value_get_type(value) == PURPLE_TYPE_BOXED)
- {
- g_free(value->u.specific_type);
- }
- else if (purple_value_get_type(value) == PURPLE_TYPE_STRING)
- {
- g_free(value->data.string_data);
- }
-
- g_free(value);
-}
-
-PurpleValue *
-purple_value_dup(const PurpleValue *value)
-{
- PurpleValue *new_value;
- PurpleType type;
-
- g_return_val_if_fail(value != NULL, NULL);
-
- type = purple_value_get_type(value);
-
- if (type == PURPLE_TYPE_SUBTYPE)
- {
- new_value = purple_value_new(PURPLE_TYPE_SUBTYPE,
- purple_value_get_subtype(value));
- }
- else if (type == PURPLE_TYPE_BOXED)
- {
- new_value = purple_value_new(PURPLE_TYPE_BOXED,
- purple_value_get_specific_type(value));
- }
- else
- new_value = purple_value_new(type);
-
- new_value->flags = value->flags;
-
- switch (type)
- {
- case PURPLE_TYPE_CHAR:
- purple_value_set_char(new_value, purple_value_get_char(value));
- break;
-
- case PURPLE_TYPE_UCHAR:
- purple_value_set_uchar(new_value, purple_value_get_uchar(value));
- break;
-
- case PURPLE_TYPE_BOOLEAN:
- purple_value_set_boolean(new_value, purple_value_get_boolean(value));
- break;
-
- case PURPLE_TYPE_SHORT:
- purple_value_set_short(new_value, purple_value_get_short(value));
- break;
-
- case PURPLE_TYPE_USHORT:
- purple_value_set_ushort(new_value, purple_value_get_ushort(value));
- break;
-
- case PURPLE_TYPE_INT:
- purple_value_set_int(new_value, purple_value_get_int(value));
- break;
-
- case PURPLE_TYPE_UINT:
- purple_value_set_uint(new_value, purple_value_get_uint(value));
- break;
-
- case PURPLE_TYPE_LONG:
- purple_value_set_long(new_value, purple_value_get_long(value));
- break;
-
- case PURPLE_TYPE_ULONG:
- purple_value_set_ulong(new_value, purple_value_get_ulong(value));
- break;
-
- case PURPLE_TYPE_INT64:
- purple_value_set_int64(new_value, purple_value_get_int64(value));
- break;
-
- case PURPLE_TYPE_UINT64:
- purple_value_set_uint64(new_value, purple_value_get_uint64(value));
- break;
-
- case PURPLE_TYPE_STRING:
- purple_value_set_string(new_value, purple_value_get_string(value));
- break;
-
- case PURPLE_TYPE_OBJECT:
- purple_value_set_object(new_value, purple_value_get_object(value));
- break;
-
- case PURPLE_TYPE_POINTER:
- purple_value_set_pointer(new_value, purple_value_get_pointer(value));
- break;
-
- case PURPLE_TYPE_ENUM:
- purple_value_set_enum(new_value, purple_value_get_enum(value));
- break;
-
- case PURPLE_TYPE_BOXED:
- purple_value_set_boxed(new_value, purple_value_get_boxed(value));
- break;
-
- default:
- break;
- }
-
- return new_value;
-}
-
-PurpleType
-purple_value_get_type(const PurpleValue *value)
-{
- g_return_val_if_fail(value != NULL, PURPLE_TYPE_UNKNOWN);
-
- return value->type;
-}
-
-unsigned int
-purple_value_get_subtype(const PurpleValue *value)
-{
- g_return_val_if_fail(value != NULL, 0);
- g_return_val_if_fail(purple_value_get_type(value) == PURPLE_TYPE_SUBTYPE, 0);
-
- return value->u.subtype;
-}
-
-const char *
-purple_value_get_specific_type(const PurpleValue *value)
-{
- g_return_val_if_fail(value != NULL, NULL);
- g_return_val_if_fail(purple_value_get_type(value) == PURPLE_TYPE_BOXED, NULL);
-
- return value->u.specific_type;
-}
-
-gboolean
-purple_value_is_outgoing(const PurpleValue *value)
-{
- g_return_val_if_fail(value != NULL, FALSE);
-
- return (value->flags & OUTGOING_FLAG);
-}
-
-void
-purple_value_set_char(PurpleValue *value, char data)
-{
- g_return_if_fail(value != NULL);
-
- value->data.char_data = data;
-}
-
-void
-purple_value_set_uchar(PurpleValue *value, unsigned char data)
-{
- g_return_if_fail(value != NULL);
-
- value->data.uchar_data = data;
-}
-
-void
-purple_value_set_boolean(PurpleValue *value, gboolean data)
-{
- g_return_if_fail(value != NULL);
-
- value->data.boolean_data = data;
-}
-
-void
-purple_value_set_short(PurpleValue *value, short data)
-{
- g_return_if_fail(value != NULL);
-
- value->data.short_data = data;
-}
-
-void
-purple_value_set_ushort(PurpleValue *value, unsigned short data)
-{
- g_return_if_fail(value != NULL);
-
- value->data.ushort_data = data;
-}
-
-void
-purple_value_set_int(PurpleValue *value, int data)
-{
- g_return_if_fail(value != NULL);
-
- value->data.int_data = data;
-}
-
-void
-purple_value_set_uint(PurpleValue *value, unsigned int data)
-{
- g_return_if_fail(value != NULL);
-
- value->data.int_data = data;
-}
-
-void
-purple_value_set_long(PurpleValue *value, long data)
-{
- g_return_if_fail(value != NULL);
-
- value->data.long_data = data;
-}
-
-void
-purple_value_set_ulong(PurpleValue *value, unsigned long data)
-{
- g_return_if_fail(value != NULL);
-
- value->data.long_data = data;
-}
-
-void
-purple_value_set_int64(PurpleValue *value, gint64 data)
-{
- g_return_if_fail(value != NULL);
-
- value->data.int64_data = data;
-}
-
-void
-purple_value_set_uint64(PurpleValue *value, guint64 data)
-{
- g_return_if_fail(value != NULL);
-
- value->data.uint64_data = data;
-}
-
-void
-purple_value_set_string(PurpleValue *value, const char *data)
-{
- g_return_if_fail(value != NULL);
- g_return_if_fail(data == NULL || g_utf8_validate(data, -1, NULL));
-
- g_free(value->data.string_data);
- value->data.string_data = g_strdup(data);
-}
-
-void
-purple_value_set_object(PurpleValue *value, void *data)
-{
- g_return_if_fail(value != NULL);
-
- value->data.object_data = data;
-}
-
-void
-purple_value_set_pointer(PurpleValue *value, void *data)
-{
- g_return_if_fail(value != NULL);
-
- value->data.pointer_data = data;
-}
-
-void
-purple_value_set_enum(PurpleValue *value, int data)
-{
- g_return_if_fail(value != NULL);
-
- value->data.enum_data = data;
-}
-
-void
-purple_value_set_boxed(PurpleValue *value, void *data)
-{
- g_return_if_fail(value != NULL);
-
- value->data.boxed_data = data;
-}
-
-char
-purple_value_get_char(const PurpleValue *value)
-{
- g_return_val_if_fail(value != NULL, 0);
-
- return value->data.char_data;
-}
-
-unsigned char
-purple_value_get_uchar(const PurpleValue *value)
-{
- g_return_val_if_fail(value != NULL, 0);
-
- return value->data.uchar_data;
-}
-
-gboolean
-purple_value_get_boolean(const PurpleValue *value)
-{
- g_return_val_if_fail(value != NULL, FALSE);
-
- return value->data.boolean_data;
-}
-
-short
-purple_value_get_short(const PurpleValue *value)
-{
- g_return_val_if_fail(value != NULL, 0);
-
- return value->data.short_data;
-}
-
-unsigned short
-purple_value_get_ushort(const PurpleValue *value)
-{
- g_return_val_if_fail(value != NULL, 0);
-
- return value->data.ushort_data;
-}
-
-int
-purple_value_get_int(const PurpleValue *value)
-{
- g_return_val_if_fail(value != NULL, 0);
-
- return value->data.int_data;
-}
-
-unsigned int
-purple_value_get_uint(const PurpleValue *value)
-{
- g_return_val_if_fail(value != NULL, 0);
-
- return value->data.int_data;
-}
-
-long
-purple_value_get_long(const PurpleValue *value)
-{
- g_return_val_if_fail(value != NULL, 0);
-
- return value->data.long_data;
-}
-
-unsigned long
-purple_value_get_ulong(const PurpleValue *value)
-{
- g_return_val_if_fail(value != NULL, 0);
-
- return value->data.long_data;
-}
-
-gint64
-purple_value_get_int64(const PurpleValue *value)
-{
- g_return_val_if_fail(value != NULL, 0);
-
- return value->data.int64_data;
-}
-
-guint64
-purple_value_get_uint64(const PurpleValue *value)
-{
- g_return_val_if_fail(value != NULL, 0);
-
- return value->data.uint64_data;
-}
-
-const char *
-purple_value_get_string(const PurpleValue *value)
-{
- g_return_val_if_fail(value != NULL, NULL);
-
- return value->data.string_data;
-}
-
-void *
-purple_value_get_object(const PurpleValue *value)
-{
- g_return_val_if_fail(value != NULL, NULL);
-
- return value->data.object_data;
-}
-
-void *
-purple_value_get_pointer(const PurpleValue *value)
-{
- g_return_val_if_fail(value != NULL, NULL);
-
- return value->data.pointer_data;
-}
-
-int
-purple_value_get_enum(const PurpleValue *value)
-{
- g_return_val_if_fail(value != NULL, -1);
-
- return value->data.enum_data;
-}
-
-void *
-purple_value_get_boxed(const PurpleValue *value)
-{
- g_return_val_if_fail(value != NULL, NULL);
-
- return value->data.boxed_data;
-}
-
diff --git a/libpurple/value.h b/libpurple/value.h
deleted file mode 100644
index 155c0146b4..0000000000
--- a/libpurple/value.h
+++ /dev/null
@@ -1,504 +0,0 @@
-/**
- * @file value.h Value wrapper API
- * @ingroup core
- */
-
-/* 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 _PURPLE_VALUE_H_
-#define _PURPLE_VALUE_H_
-
-#include <glib.h>
-
-/**
- * Specific value types.
- */
-typedef enum
-{
- PURPLE_TYPE_UNKNOWN = 0, /**< Unknown type. */
- PURPLE_TYPE_SUBTYPE, /**< Subtype. */
- PURPLE_TYPE_CHAR, /**< Character. */
- PURPLE_TYPE_UCHAR, /**< Unsigned character. */
- PURPLE_TYPE_BOOLEAN, /**< Boolean. */
- PURPLE_TYPE_SHORT, /**< Short integer. */
- PURPLE_TYPE_USHORT, /**< Unsigned short integer. */
- PURPLE_TYPE_INT, /**< Integer. */
- PURPLE_TYPE_UINT, /**< Unsigned integer. */
- PURPLE_TYPE_LONG, /**< Long integer. */
- PURPLE_TYPE_ULONG, /**< Unsigned long integer. */
- PURPLE_TYPE_INT64, /**< 64-bit integer. */
- PURPLE_TYPE_UINT64, /**< 64-bit unsigned integer. */
- PURPLE_TYPE_STRING, /**< String. */
- PURPLE_TYPE_OBJECT, /**< Object pointer. */
- PURPLE_TYPE_POINTER, /**< Generic pointer. */
- PURPLE_TYPE_ENUM, /**< Enum. */
- PURPLE_TYPE_BOXED /**< Boxed pointer with specific type. */
-
-} PurpleType;
-
-
-/**
- * Purple-specific subtype values.
- */
-typedef enum
-{
- PURPLE_SUBTYPE_UNKNOWN = 0,
- PURPLE_SUBTYPE_ACCOUNT,
- PURPLE_SUBTYPE_BLIST,
- PURPLE_SUBTYPE_BLIST_BUDDY,
- PURPLE_SUBTYPE_BLIST_GROUP,
- PURPLE_SUBTYPE_BLIST_CHAT,
- PURPLE_SUBTYPE_BUDDY_ICON,
- PURPLE_SUBTYPE_CONNECTION,
- PURPLE_SUBTYPE_CONVERSATION,
- PURPLE_SUBTYPE_PLUGIN,
- PURPLE_SUBTYPE_BLIST_NODE,
- PURPLE_SUBTYPE_CIPHER,
- PURPLE_SUBTYPE_STATUS,
- PURPLE_SUBTYPE_LOG,
- PURPLE_SUBTYPE_XFER,
- PURPLE_SUBTYPE_SAVEDSTATUS,
- PURPLE_SUBTYPE_XMLNODE,
- PURPLE_SUBTYPE_USERINFO,
- PURPLE_SUBTYPE_STORED_IMAGE,
- PURPLE_SUBTYPE_CERTIFICATEPOOL,
- PURPLE_SUBTYPE_CHATBUDDY
-} PurpleSubType;
-
-/**
- * A wrapper for a type, subtype, and specific type of value.
- */
-typedef struct
-{
- PurpleType type;
- unsigned short flags;
-
- union
- {
- char char_data;
- unsigned char uchar_data;
- gboolean boolean_data;
- short short_data;
- unsigned short ushort_data;
- int int_data;
- unsigned int uint_data;
- long long_data;
- unsigned long ulong_data;
- gint64 int64_data;
- guint64 uint64_data;
- char *string_data;
- void *object_data;
- void *pointer_data;
- int enum_data;
- void *boxed_data;
-
- } data;
-
- union
- {
- unsigned int subtype;
- char *specific_type;
-
- } u;
-
-} PurpleValue;
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Creates a new PurpleValue.
- *
- * This function takes a type and, depending on that type, a sub-type
- * or specific type.
- *
- * If @a type is PURPLE_TYPE_BOXED, the next parameter must be a
- * string representing the specific type.
- *
- * If @a type is PURPLE_TYPE_SUBTYPE, the next parameter must be a
- * integer or enum representing the sub-type.
- *
- * If the subtype or specific type is not set when required, random
- * errors may occur. You have been warned.
- *
- * @param type The type.
- *
- * @return The new value.
- */
-PurpleValue *purple_value_new(PurpleType type, ...);
-
-/**
- * Creates a new outgoing PurpleValue. If a value is an "outgoing" value
- * it means the value can be modified by plugins and scripts.
- *
- * This function takes a type and, depending on that type, a sub-type
- * or specific type.
- *
- * If @a type is PURPLE_TYPE_BOXED, the next parameter must be a
- * string representing the specific type.
- *
- * If @a type is PURPLE_TYPE_SUBTYPE, the next parameter must be a
- * integer or enum representing the sub-type.
- *
- * If the sub-type or specific type is not set when required, random
- * errors may occur. You have been warned.
- *
- * @param type The type.
- *
- * @return The new value.
- */
-PurpleValue *purple_value_new_outgoing(PurpleType type, ...);
-
-/**
- * Destroys a PurpleValue.
- *
- * @param value The value to destroy.
- */
-void purple_value_destroy(PurpleValue *value);
-
-/**
- * Duplicated a PurpleValue.
- *
- * @param value The value to duplicate.
- *
- * @return The duplicate value.
- */
-PurpleValue *purple_value_dup(const PurpleValue *value);
-
-/**
- * Returns a value's type.
- *
- * @param value The value whose type you want.
- *
- * @return The value's type.
- */
-PurpleType purple_value_get_type(const PurpleValue *value);
-
-/**
- * Returns a value's subtype.
- *
- * If the value's type is not PURPLE_TYPE_SUBTYPE, this will return 0.
- * Subtypes should never have a subtype of 0.
- *
- * @param value The value whose subtype you want.
- *
- * @return The value's subtype, or 0 if @a type is not PURPLE_TYPE_SUBTYPE.
- */
-unsigned int purple_value_get_subtype(const PurpleValue *value);
-
-/**
- * Returns a value's specific type.
- *
- * If the value's type is not PURPLE_TYPE_BOXED, this will return @c NULL.
- *
- * @param value The value whose specific type you want.
- *
- * @return The value's specific type, or @a NULL if not PURPLE_TYPE_BOXED.
- */
-const char *purple_value_get_specific_type(const PurpleValue *value);
-
-/**
- * Returns whether or not the value is an outgoing value.
- *
- * @param value The value.
- *
- * @return TRUE if the value is outgoing, or FALSE otherwise.
- */
-gboolean purple_value_is_outgoing(const PurpleValue *value);
-
-/**
- * Sets the value's character data.
- *
- * @param value The value.
- * @param data The character data.
- */
-void purple_value_set_char(PurpleValue *value, char data);
-
-/**
- * Sets the value's unsigned character data.
- *
- * @param value The value.
- * @param data The unsigned character data.
- */
-void purple_value_set_uchar(PurpleValue *value, unsigned char data);
-
-/**
- * Sets the value's boolean data.
- *
- * @param value The value.
- * @param data The boolean data.
- */
-void purple_value_set_boolean(PurpleValue *value, gboolean data);
-
-/**
- * Sets the value's short integer data.
- *
- * @param value The value.
- * @param data The short integer data.
- */
-void purple_value_set_short(PurpleValue *value, short data);
-
-/**
- * Sets the value's unsigned short integer data.
- *
- * @param value The value.
- * @param data The unsigned short integer data.
- */
-void purple_value_set_ushort(PurpleValue *value, unsigned short data);
-
-/**
- * Sets the value's integer data.
- *
- * @param value The value.
- * @param data The integer data.
- */
-void purple_value_set_int(PurpleValue *value, int data);
-
-/**
- * Sets the value's unsigned integer data.
- *
- * @param value The value.
- * @param data The unsigned integer data.
- */
-void purple_value_set_uint(PurpleValue *value, unsigned int data);
-
-/**
- * Sets the value's long integer data.
- *
- * @param value The value.
- * @param data The long integer data.
- */
-void purple_value_set_long(PurpleValue *value, long data);
-
-/**
- * Sets the value's unsigned long integer data.
- *
- * @param value The value.
- * @param data The unsigned long integer data.
- */
-void purple_value_set_ulong(PurpleValue *value, unsigned long data);
-
-/**
- * Sets the value's 64-bit integer data.
- *
- * @param value The value.
- * @param data The 64-bit integer data.
- */
-void purple_value_set_int64(PurpleValue *value, gint64 data);
-
-/**
- * Sets the value's unsigned 64-bit integer data.
- *
- * @param value The value.
- * @param data The unsigned 64-bit integer data.
- */
-void purple_value_set_uint64(PurpleValue *value, guint64 data);
-
-/**
- * Sets the value's string data.
- *
- * @param value The value.
- * @param data The string data.
- */
-void purple_value_set_string(PurpleValue *value, const char *data);
-
-/**
- * Sets the value's object data.
- *
- * @param value The value.
- * @param data The object data.
- */
-void purple_value_set_object(PurpleValue *value, void *data);
-
-/**
- * Sets the value's pointer data.
- *
- * @param value The value.
- * @param data The pointer data.
- */
-void purple_value_set_pointer(PurpleValue *value, void *data);
-
-/**
- * Sets the value's enum data.
- *
- * @param value The value.
- * @param data The enum data.
- */
-void purple_value_set_enum(PurpleValue *value, int data);
-
-/**
- * Sets the value's boxed data.
- *
- * @param value The value.
- * @param data The boxed data.
- */
-void purple_value_set_boxed(PurpleValue *value, void *data);
-
-/**
- * Returns the value's character data.
- *
- * @param value The value.
- *
- * @return The character data.
- */
-char purple_value_get_char(const PurpleValue *value);
-
-/**
- * Returns the value's unsigned character data.
- *
- * @param value The value.
- *
- * @return The unsigned character data.
- */
-unsigned char purple_value_get_uchar(const PurpleValue *value);
-
-/**
- * Returns the value's boolean data.
- *
- * @param value The value.
- *
- * @return The boolean data.
- */
-gboolean purple_value_get_boolean(const PurpleValue *value);
-
-/**
- * Returns the value's short integer data.
- *
- * @param value The value.
- *
- * @return The short integer data.
- */
-short purple_value_get_short(const PurpleValue *value);
-
-/**
- * Returns the value's unsigned short integer data.
- *
- * @param value The value.
- *
- * @return The unsigned short integer data.
- */
-unsigned short purple_value_get_ushort(const PurpleValue *value);
-
-/**
- * Returns the value's integer data.
- *
- * @param value The value.
- *
- * @return The integer data.
- */
-int purple_value_get_int(const PurpleValue *value);
-
-/**
- * Returns the value's unsigned integer data.
- *
- * @param value The value.
- *
- * @return The unsigned integer data.
- */
-unsigned int purple_value_get_uint(const PurpleValue *value);
-
-/**
- * Returns the value's long integer data.
- *
- * @param value The value.
- *
- * @return The long integer data.
- */
-long purple_value_get_long(const PurpleValue *value);
-
-/**
- * Returns the value's unsigned long integer data.
- *
- * @param value The value.
- *
- * @return The unsigned long integer data.
- */
-unsigned long purple_value_get_ulong(const PurpleValue *value);
-
-/**
- * Returns the value's 64-bit integer data.
- *
- * @param value The value.
- *
- * @return The 64-bit integer data.
- */
-gint64 purple_value_get_int64(const PurpleValue *value);
-
-/**
- * Returns the value's unsigned 64-bit integer data.
- *
- * @param value The value.
- *
- * @return The unsigned 64-bit integer data.
- */
-guint64 purple_value_get_uint64(const PurpleValue *value);
-
-/**
- * Returns the value's string data.
- *
- * @param value The value.
- *
- * @return The string data.
- */
-const char *purple_value_get_string(const PurpleValue *value);
-
-/**
- * Returns the value's object data.
- *
- * @param value The value.
- *
- * @return The object data.
- */
-void *purple_value_get_object(const PurpleValue *value);
-
-/**
- * Returns the value's pointer data.
- *
- * @param value The value.
- *
- * @return The pointer data.
- */
-void *purple_value_get_pointer(const PurpleValue *value);
-
-/**
- * Returns the value's enum data.
- *
- * @param value The value.
- *
- * @return The enum data.
- */
-int purple_value_get_enum(const PurpleValue *value);
-
-/**
- * Returns the value's boxed data.
- *
- * @param value The value.
- *
- * @return The boxed data.
- */
-void *purple_value_get_boxed(const PurpleValue *value);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _PURPLE_VALUE_H_ */
diff --git a/libpurple/version.c b/libpurple/version.c
index 48857ee71e..af142822ac 100644
--- a/libpurple/version.c
+++ b/libpurple/version.c
@@ -1,8 +1,3 @@
-/*
- * @file version.c Version Functions
- * @ingroup core
- */
-
/* 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.
diff --git a/libpurple/version.h.in b/libpurple/version.h.in
index 6e13bb3748..602a47dc80 100644
--- a/libpurple/version.h.in
+++ b/libpurple/version.h.in
@@ -1,6 +1,4 @@
-/**
- * @file version.h Purple Versioning
- *
+/*
* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -21,14 +19,35 @@
* 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_VERSION_H_
#define _PURPLE_VERSION_H_
+/**
+ * SECTION:version
+ * @section_id: libpurple-version
+ * @short_description: <filename>version.h</filename>
+ * @title: Purple Versioning
+ */
-/** The major version of the running libpurple. */
+/**
+ * PURPLE_MAJOR_VERSION:
+ *
+ * The major version of the running libpurple.
+ */
#define PURPLE_MAJOR_VERSION (@PURPLE_MAJOR_VERSION@)
-/** The minor version of the running libpurple. */
+
+/**
+ * PURPLE_MINOR_VERSION:
+ *
+ * The minor version of the running libpurple.
+ */
#define PURPLE_MINOR_VERSION (@PURPLE_MINOR_VERSION@)
-/** The micro version of the running libpurple. */
+
+/**
+ * PURPLE_MICRO_VERSION:
+ *
+ * The micro version of the running libpurple.
+ */
#define PURPLE_MICRO_VERSION (@PURPLE_MICRO_VERSION@)
#define PURPLE_VERSION_CHECK(x,y,z) ((x) == PURPLE_MAJOR_VERSION && \
@@ -40,43 +59,43 @@ extern "C" {
#endif
/**
+ * purple_version_check:
+ * @required_major: the required major version.
+ * @required_minor: the required minor version.
+ * @required_micro: the required micro version.
+ *
* Checks that the libpurple version is compatible with the requested
* version
*
- * @param required_major: the required major version.
- * @param required_minor: the required minor version.
- * @param required_micro: the required micro version.
- *
- * @return NULL if the versions are compatible, or a string describing
- * the version mismatch if not compatible.
+ * Returns: %NULL if the versions are compatible, or a string describing
+ * the version mismatch if not compatible.
*/
const char *purple_version_check(guint required_major, guint required_minor, guint required_micro);
/**
+ * purple_major_version:
+ *
* The major version of the running libpurple. Contrast with
* #PURPLE_MAJOR_VERSION, which expands at compile time to the major version of
* libpurple being compiled against.
- *
- * @since 2.4.0
*/
extern const guint purple_major_version;
/**
+ * purple_minor_version:
+ *
* The minor version of the running libpurple. Contrast with
* #PURPLE_MINOR_VERSION, which expands at compile time to the minor version of
* libpurple being compiled against.
- *
- * @since 2.4.0
*/
extern const guint purple_minor_version;
/**
+ * purple_micro_version:
*
* The micro version of the running libpurple. Contrast with
* #PURPLE_MICRO_VERSION, which expands at compile time to the micro version of
* libpurple being compiled against.
- *
- * @since 2.4.0
*/
extern const guint purple_micro_version;
diff --git a/libpurple/whiteboard.c b/libpurple/whiteboard.c
index 3ac4c4e010..0c3467a722 100644
--- a/libpurple/whiteboard.c
+++ b/libpurple/whiteboard.c
@@ -22,22 +22,86 @@
*/
#include "internal.h"
+#include "glibcompat.h"
#include "whiteboard.h"
#include "prpl.h"
+#define PURPLE_WHITEBOARD_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_WHITEBOARD, PurpleWhiteboardPrivate))
+
+typedef struct _PurpleWhiteboardPrivate PurpleWhiteboardPrivate;
+
+/* Private data for a whiteboard */
+struct _PurpleWhiteboardPrivate
+{
+ int state; /* State of whiteboard session */
+
+ PurpleAccount *account; /* Account associated with this session */
+ char *who; /* Name of the remote user */
+
+ /* TODO Remove this and use protocol-specific subclasses. */
+ void *proto_data; /* Protocol specific data */
+
+ PurpleWhiteboardPrplOps *prpl_ops; /* Protocol-plugin operations */
+
+ GList *draw_list; /* List of drawing elements/deltas to
+ send */
+};
+
+/* GObject Property enums */
+enum
+{
+ PROP_0,
+ PROP_STATE,
+ PROP_ACCOUNT,
+ PROP_WHO,
+ PROP_DRAW_LIST,
+ PROP_LAST
+};
+
/******************************************************************************
* Globals
*****************************************************************************/
+static GObjectClass *parent_class;
+static GParamSpec *properties[PROP_LAST];
+
static PurpleWhiteboardUiOps *whiteboard_ui_ops = NULL;
/* static PurpleWhiteboardPrplOps *whiteboard_prpl_ops = NULL; */
-static GList *wbList = NULL;
+static GList *wb_list = NULL;
/*static gboolean auto_accept = TRUE; */
/******************************************************************************
* API
*****************************************************************************/
+static PurpleWhiteboardUiOps *
+purple_whiteboard_ui_ops_copy(PurpleWhiteboardUiOps *ops)
+{
+ PurpleWhiteboardUiOps *ops_new;
+
+ g_return_val_if_fail(ops != NULL, NULL);
+
+ ops_new = g_new(PurpleWhiteboardUiOps, 1);
+ *ops_new = *ops;
+
+ return ops_new;
+}
+
+GType
+purple_whiteboard_ui_ops_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleWhiteboardUiOps",
+ (GBoxedCopyFunc)purple_whiteboard_ui_ops_copy,
+ (GBoxedFreeFunc)g_free);
+ }
+
+ return type;
+}
+
void purple_whiteboard_set_ui_ops(PurpleWhiteboardUiOps *ops)
{
whiteboard_ui_ops = ops;
@@ -45,49 +109,49 @@ void purple_whiteboard_set_ui_ops(PurpleWhiteboardUiOps *ops)
void purple_whiteboard_set_prpl_ops(PurpleWhiteboard *wb, PurpleWhiteboardPrplOps *ops)
{
- wb->prpl_ops = ops;
+ PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->prpl_ops = ops;
}
-PurpleWhiteboard *purple_whiteboard_create(PurpleAccount *account, const char *who, int state)
+PurpleAccount *purple_whiteboard_get_account(const PurpleWhiteboard *wb)
{
- PurplePluginProtocolInfo *prpl_info;
- PurpleWhiteboard *wb = g_new0(PurpleWhiteboard, 1);
+ PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb);
- wb->account = account;
- wb->state = state;
- wb->who = g_strdup(who);
+ g_return_val_if_fail(priv != NULL, NULL);
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(
- purple_account_get_connection(account)));
- purple_whiteboard_set_prpl_ops(wb, prpl_info->whiteboard_prpl_ops);
+ return priv->account;
+}
- /* Start up protocol specifics */
- if(wb->prpl_ops && wb->prpl_ops->start)
- wb->prpl_ops->start(wb);
+const char *purple_whiteboard_get_who(const PurpleWhiteboard *wb)
+{
+ PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb);
- wbList = g_list_append(wbList, wb);
+ g_return_val_if_fail(priv != NULL, NULL);
- return wb;
+ return priv->who;
}
-void purple_whiteboard_destroy(PurpleWhiteboard *wb)
+void purple_whiteboard_set_state(PurpleWhiteboard *wb, int state)
{
- g_return_if_fail(wb != NULL);
+ PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb);
- if(wb->ui_data)
- {
- /* Destroy frontend */
- if(whiteboard_ui_ops && whiteboard_ui_ops->destroy)
- whiteboard_ui_ops->destroy(wb);
- }
+ g_return_if_fail(priv != NULL);
- /* Do protocol specific session ending procedures */
- if(wb->prpl_ops && wb->prpl_ops->end)
- wb->prpl_ops->end(wb);
+ priv->state = state;
- g_free(wb->who);
- wbList = g_list_remove(wbList, wb);
- g_free(wb);
+ g_object_notify_by_pspec(G_OBJECT(wb), properties[PROP_STATE]);
+}
+
+int purple_whiteboard_get_state(const PurpleWhiteboard *wb)
+{
+ PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb);
+
+ g_return_val_if_fail(priv != NULL, -1);
+
+ return priv->state;
}
void purple_whiteboard_start(PurpleWhiteboard *wb)
@@ -104,16 +168,18 @@ void purple_whiteboard_start(PurpleWhiteboard *wb)
PurpleWhiteboard *purple_whiteboard_get_session(const PurpleAccount *account, const char *who)
{
PurpleWhiteboard *wb;
+ PurpleWhiteboardPrivate *priv;
- GList *l = wbList;
+ GList *l = wb_list;
/* Look for a whiteboard session between the local user and the remote user
*/
while(l != NULL)
{
wb = l->data;
+ priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb);
- if(wb->account == account && purple_strequal(wb->who, who))
+ if(priv->account == account && purple_strequal(priv->who, who))
return wb;
l = l->next;
@@ -129,7 +195,12 @@ void purple_whiteboard_draw_list_destroy(GList *draw_list)
gboolean purple_whiteboard_get_dimensions(const PurpleWhiteboard *wb, int *width, int *height)
{
- PurpleWhiteboardPrplOps *prpl_ops = wb->prpl_ops;
+ PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb);
+ PurpleWhiteboardPrplOps *prpl_ops;
+
+ g_return_val_if_fail(priv != NULL, FALSE);
+
+ prpl_ops = priv->prpl_ops;
if (prpl_ops && prpl_ops->get_dimensions)
{
@@ -148,7 +219,12 @@ void purple_whiteboard_set_dimensions(PurpleWhiteboard *wb, int width, int heigh
void purple_whiteboard_send_draw_list(PurpleWhiteboard *wb, GList *list)
{
- PurpleWhiteboardPrplOps *prpl_ops = wb->prpl_ops;
+ PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb);
+ PurpleWhiteboardPrplOps *prpl_ops;
+
+ g_return_if_fail(priv != NULL);
+
+ prpl_ops = priv->prpl_ops;
if (prpl_ops && prpl_ops->send_draw_list)
prpl_ops->send_draw_list(wb, list);
@@ -174,7 +250,12 @@ void purple_whiteboard_clear(PurpleWhiteboard *wb)
void purple_whiteboard_send_clear(PurpleWhiteboard *wb)
{
- PurpleWhiteboardPrplOps *prpl_ops = wb->prpl_ops;
+ PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb);
+ PurpleWhiteboardPrplOps *prpl_ops;
+
+ g_return_if_fail(priv != NULL);
+
+ prpl_ops = priv->prpl_ops;
if (prpl_ops && prpl_ops->clear)
prpl_ops->clear(wb);
@@ -182,7 +263,12 @@ void purple_whiteboard_send_clear(PurpleWhiteboard *wb)
void purple_whiteboard_send_brush(PurpleWhiteboard *wb, int size, int color)
{
- PurpleWhiteboardPrplOps *prpl_ops = wb->prpl_ops;
+ PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb);
+ PurpleWhiteboardPrplOps *prpl_ops;
+
+ g_return_if_fail(priv != NULL);
+
+ prpl_ops = priv->prpl_ops;
if (prpl_ops && prpl_ops->set_brush)
prpl_ops->set_brush(wb, size, color);
@@ -190,7 +276,12 @@ void purple_whiteboard_send_brush(PurpleWhiteboard *wb, int size, int color)
gboolean purple_whiteboard_get_brush(const PurpleWhiteboard *wb, int *size, int *color)
{
- PurpleWhiteboardPrplOps *prpl_ops = wb->prpl_ops;
+ PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb);
+ PurpleWhiteboardPrplOps *prpl_ops;
+
+ g_return_val_if_fail(priv != NULL, FALSE);
+
+ prpl_ops = priv->prpl_ops;
if (prpl_ops && prpl_ops->get_brush)
{
@@ -206,3 +297,234 @@ void purple_whiteboard_set_brush(PurpleWhiteboard *wb, int size, int color)
whiteboard_ui_ops->set_brush(wb, size, color);
}
+GList *purple_whiteboard_get_draw_list(const PurpleWhiteboard *wb)
+{
+ PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->draw_list;
+}
+
+void purple_whiteboard_set_draw_list(PurpleWhiteboard *wb, GList* draw_list)
+{
+ PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->draw_list = draw_list;
+
+ g_object_notify_by_pspec(G_OBJECT(wb), properties[PROP_DRAW_LIST]);
+}
+
+void purple_whiteboard_set_protocol_data(PurpleWhiteboard *wb, gpointer proto_data)
+{
+ PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->proto_data = proto_data;
+}
+
+gpointer purple_whiteboard_get_protocol_data(const PurpleWhiteboard *wb)
+{
+ PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->proto_data;
+}
+
+void purple_whiteboard_set_ui_data(PurpleWhiteboard *wb, gpointer ui_data)
+{
+ g_return_if_fail(PURPLE_IS_WHITEBOARD(wb));
+
+ wb->ui_data = ui_data;
+}
+
+gpointer purple_whiteboard_get_ui_data(const PurpleWhiteboard *wb)
+{
+ g_return_val_if_fail(PURPLE_IS_WHITEBOARD(wb), NULL);
+
+ return wb->ui_data;
+}
+
+/******************************************************************************
+ * GObject code
+ *****************************************************************************/
+/* Set method for GObject properties */
+static void
+purple_whiteboard_set_property(GObject *obj, guint param_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleWhiteboard *wb = PURPLE_WHITEBOARD(obj);
+ PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb);
+
+ switch (param_id) {
+ case PROP_STATE:
+ purple_whiteboard_set_state(wb, g_value_get_int(value));
+ break;
+ case PROP_ACCOUNT:
+ priv->account = g_value_get_object(value);
+ break;
+ case PROP_WHO:
+ priv->who = g_strdup(g_value_get_string(value));
+ break;
+ case PROP_DRAW_LIST:
+ purple_whiteboard_set_draw_list(wb, 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_whiteboard_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleWhiteboard *wb = PURPLE_WHITEBOARD(obj);
+
+ switch (param_id) {
+ case PROP_STATE:
+ g_value_set_int(value, purple_whiteboard_get_state(wb));
+ break;
+ case PROP_ACCOUNT:
+ g_value_set_object(value, purple_whiteboard_get_account(wb));
+ break;
+ case PROP_WHO:
+ g_value_set_string(value, purple_whiteboard_get_who(wb));
+ break;
+ case PROP_DRAW_LIST:
+ g_value_set_pointer(value, purple_whiteboard_get_draw_list(wb));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* Called when done constructing */
+static void
+purple_whiteboard_constructed(GObject *object)
+{
+ PurpleWhiteboard *wb = PURPLE_WHITEBOARD(object);
+ PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb);
+ PurplePluginProtocolInfo *prpl_info;
+
+ parent_class->constructed(object);
+
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(
+ purple_account_get_connection(priv->account)));
+ purple_whiteboard_set_prpl_ops(wb, prpl_info->whiteboard_prpl_ops);
+
+ /* Start up protocol specifics */
+ if(priv->prpl_ops && priv->prpl_ops->start)
+ priv->prpl_ops->start(wb);
+
+ wb_list = g_list_append(wb_list, wb);
+}
+
+/* GObject finalize function */
+static void
+purple_whiteboard_finalize(GObject *object)
+{
+ PurpleWhiteboard *wb = PURPLE_WHITEBOARD(object);
+ PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb);
+
+ if(wb->ui_data)
+ {
+ /* Destroy frontend */
+ if(whiteboard_ui_ops && whiteboard_ui_ops->destroy)
+ whiteboard_ui_ops->destroy(wb);
+ }
+
+ /* Do protocol specific session ending procedures */
+ if(priv->prpl_ops && priv->prpl_ops->end)
+ priv->prpl_ops->end(wb);
+
+ wb_list = g_list_remove(wb_list, wb);
+
+ g_free(priv->who);
+
+ parent_class->finalize(object);
+}
+
+/* Class initializer function */
+static void
+purple_whiteboard_class_init(PurpleWhiteboardClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ obj_class->finalize = purple_whiteboard_finalize;
+ obj_class->constructed = purple_whiteboard_constructed;
+
+ /* Setup properties */
+ obj_class->get_property = purple_whiteboard_get_property;
+ obj_class->set_property = purple_whiteboard_set_property;
+
+ g_type_class_add_private(klass, sizeof(PurpleWhiteboardPrivate));
+
+ properties[PROP_STATE] = g_param_spec_int("state", "State",
+ "State of the whiteboard.",
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_ACCOUNT] = g_param_spec_object("account", "Account",
+ "The whiteboard's account.", PURPLE_TYPE_ACCOUNT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_WHO] = g_param_spec_string("who", "Who",
+ "Who you're drawing with.", NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_DRAW_LIST] = g_param_spec_pointer("draw-list", "Draw list",
+ "A list of points to draw to the buddy.",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, PROP_LAST, properties);
+}
+
+GType
+purple_whiteboard_get_type(void)
+{
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleWhiteboardClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_whiteboard_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleWhiteboard),
+ 0,
+ NULL,
+ NULL,
+ };
+
+ type = g_type_register_static(G_TYPE_OBJECT, "PurpleWhiteboard",
+ &info, 0);
+ }
+
+ return type;
+}
+
+PurpleWhiteboard *purple_whiteboard_new(PurpleAccount *account, const char *who, int state)
+{
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+ g_return_val_if_fail(who != NULL, NULL);
+
+ return g_object_new(PURPLE_TYPE_WHITEBOARD,
+ "account", account,
+ "who", who,
+ "state", state,
+ NULL
+ );
+}
diff --git a/libpurple/whiteboard.h b/libpurple/whiteboard.h
index 7d5700dee9..d9cb16eb36 100644
--- a/libpurple/whiteboard.h
+++ b/libpurple/whiteboard.h
@@ -1,7 +1,3 @@
-/**
- * @file whiteboard.h The PurpleWhiteboard core object
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -25,238 +21,402 @@
#ifndef _PURPLE_WHITEBOARD_H_
#define _PURPLE_WHITEBOARD_H_
-
/**
- * Whiteboard PRPL Operations
+ * SECTION:whiteboard
+ * @section_id: libpurple-whiteboard
+ * @short_description: <filename>whiteboard.h</filename>
+ * @title: Whiteboard Object
*/
-typedef struct _PurpleWhiteboardPrplOps PurpleWhiteboardPrplOps;
-#include "account.h"
+#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))
-/**
- * A PurpleWhiteboard
- */
-typedef struct _PurpleWhiteboard
-{
- int state; /**< State of whiteboard session */
+#define PURPLE_TYPE_WHITEBOARD_UI_OPS (purple_whiteboard_ui_ops_get_type())
- PurpleAccount *account; /**< Account associated with this session */
- char *who; /**< Name of the remote user */
+typedef struct _PurpleWhiteboard PurpleWhiteboard;
+typedef struct _PurpleWhiteboardClass PurpleWhiteboardClass;
- void *ui_data; /**< Graphical user-interface data */
- void *proto_data; /**< Protocol specific data */
- PurpleWhiteboardPrplOps *prpl_ops; /**< Protocol-plugin operations */
+typedef struct _PurpleWhiteboardUiOps PurpleWhiteboardUiOps;
+typedef struct _PurpleWhiteboardPrplOps PurpleWhiteboardPrplOps;
- GList *draw_list; /**< List of drawing elements/deltas to send */
-} PurpleWhiteboard;
+#include "account.h"
/**
+ * PurpleWhiteboardUiOps:
+ * @create: create whiteboard
+ * @destroy: destory whiteboard
+ * @set_dimensions: set whiteboard dimensions
+ * @set_brush: set the size and color of the brush
+ * @draw_point: draw a point
+ * @draw_line: draw a line
+ * @clear: clear whiteboard
+ *
* The PurpleWhiteboard UI Operations
*/
-typedef struct _PurpleWhiteboardUiOps
+struct _PurpleWhiteboardUiOps
{
- void (*create)(PurpleWhiteboard *wb); /**< create function */
- void (*destroy)(PurpleWhiteboard *wb); /**< destory function */
- void (*set_dimensions)(PurpleWhiteboard *wb, int width, int height); /**< set_dimensions function */
- void (*set_brush) (PurpleWhiteboard *wb, int size, int color); /**< set the size and color of the brush */
+ void (*create)(PurpleWhiteboard *wb);
+ void (*destroy)(PurpleWhiteboard *wb);
+ void (*set_dimensions)(PurpleWhiteboard *wb, int width, int height);
+ void (*set_brush) (PurpleWhiteboard *wb, int size, int color);
void (*draw_point)(PurpleWhiteboard *wb, int x, int y,
- int color, int size); /**< draw_point function */
+ int color, int size);
void (*draw_line)(PurpleWhiteboard *wb, int x1, int y1,
- int x2, int y2,
- int color, int size); /**< draw_line function */
- void (*clear)(PurpleWhiteboard *wb); /**< clear function */
+ int x2, int y2,
+ int color, int size);
+ void (*clear)(PurpleWhiteboard *wb);
+ /*< private >*/
void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
void (*_purple_reserved4)(void);
-} PurpleWhiteboardUiOps;
+};
/**
- * PurpleWhiteboard PRPL Operations
+ * PurpleWhiteboardPrplOps:
+ * @start: start function
+ * @end: end function
+ * @get_dimensions: get whiteboard dimensions
+ * @set_dimensions: set whiteboard dimensions
+ * @get_brush: get the brush size and color
+ * @set_brush: set the brush size and color
+ * @send_draw_list: send_draw_list function
+ * @clear: clear whiteboard
+ *
+ * Whiteboard protocol operations
*/
struct _PurpleWhiteboardPrplOps
{
- void (*start)(PurpleWhiteboard *wb); /**< start function */
- void (*end)(PurpleWhiteboard *wb); /**< end function */
- void (*get_dimensions)(const PurpleWhiteboard *wb, int *width, int *height); /**< get_dimensions function */
- void (*set_dimensions)(PurpleWhiteboard *wb, int width, int height); /**< set_dimensions function */
- void (*get_brush) (const PurpleWhiteboard *wb, int *size, int *color); /**< get the brush size and color */
- void (*set_brush) (PurpleWhiteboard *wb, int size, int color); /**< set the brush size and color */
- void (*send_draw_list)(PurpleWhiteboard *wb, GList *draw_list); /**< send_draw_list function */
- void (*clear)(PurpleWhiteboard *wb); /**< clear function */
+ void (*start)(PurpleWhiteboard *wb);
+ void (*end)(PurpleWhiteboard *wb);
+ void (*get_dimensions)(const PurpleWhiteboard *wb, int *width, int *height);
+ void (*set_dimensions)(PurpleWhiteboard *wb, int width, int height);
+ void (*get_brush) (const PurpleWhiteboard *wb, int *size, int *color);
+ void (*set_brush) (PurpleWhiteboard *wb, int size, int color);
+ void (*send_draw_list)(PurpleWhiteboard *wb, GList *draw_list);
+ void (*clear)(PurpleWhiteboard *wb);
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+/**
+ * PurpleWhiteboard:
+ * @ui_data: The UI data associated with this whiteboard. This is a convenience
+ * field provided to the UIs -- it is not used by the libpurple core.
+ *
+ * A Whiteboard
+ */
+struct _PurpleWhiteboard
+{
+ GObject gparent;
+
+ /*< public >*/
+ 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);
};
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
+G_BEGIN_DECLS
/******************************************************************************/
-/** @name PurpleWhiteboard API */
+/* PurpleWhiteboard API */
/******************************************************************************/
-/*@{*/
/**
- * Sets the UI operations
+ * purple_whiteboard_get_type:
+ *
+ * Returns: The #GType for the #PurpleWhiteboard object.
+ */
+GType purple_whiteboard_get_type(void);
+
+/**
+ * purple_whiteboard_ui_ops_get_type:
*
- * @param ops The UI operations to set
+ * Returns: The #GType for the #PurpleWhiteboardUiOps boxed structure.
+ */
+GType purple_whiteboard_ui_ops_get_type(void);
+
+/**
+ * purple_whiteboard_set_ui_ops:
+ * @ops: The UI operations to set
+ *
+ * Sets the UI operations
*/
void purple_whiteboard_set_ui_ops(PurpleWhiteboardUiOps *ops);
/**
- * Sets the prpl operations for a whiteboard
+ * purple_whiteboard_set_protocol_ops:
+ * @wb: The whiteboard for which to set the protocol operations
+ * @ops: The protocol operations to set
*
- * @param wb The whiteboard for which to set the prpl operations
- * @param ops The prpl operations to set
+ * Sets the protocol operations for a whiteboard
*/
void purple_whiteboard_set_prpl_ops(PurpleWhiteboard *wb, PurpleWhiteboardPrplOps *ops);
/**
- * Creates a whiteboard
+ * purple_whiteboard_new:
+ * @account: The account.
+ * @who: Who you're drawing with.
+ * @state: The state.
*
- * @param account The account.
- * @param who Who you're drawing with.
- * @param state The state.
+ * Creates a new whiteboard
*
- * @return The new whiteboard
+ * Returns: The new whiteboard
*/
-PurpleWhiteboard *purple_whiteboard_create(PurpleAccount *account, const char *who, int state);
+PurpleWhiteboard *purple_whiteboard_new(PurpleAccount *account, const char *who, int state);
/**
- * Destroys a whiteboard
+ * purple_whiteboard_get_account:
+ * @wb: The whiteboard.
+ *
+ * Returns the whiteboard's account.
*
- * @param wb The whiteboard.
+ * Returns: The whiteboard's account.
*/
-void purple_whiteboard_destroy(PurpleWhiteboard *wb);
+PurpleAccount *purple_whiteboard_get_account(const PurpleWhiteboard *wb);
/**
- * Starts a whiteboard
+ * purple_whiteboard_get_who:
+ * @wb: The whiteboard
+ *
+ * Return who you're drawing with.
*
- * @param wb The whiteboard.
+ * Returns: Who you're drawing with.
+ */
+const char *purple_whiteboard_get_who(const PurpleWhiteboard *wb);
+
+/**
+ * purple_whiteboard_set_state:
+ * @wb: The whiteboard.
+ * @state: The state
+ *
+ * Set the state of the whiteboard.
+ */
+void purple_whiteboard_set_state(PurpleWhiteboard *wb, int state);
+
+/**
+ * purple_whiteboard_get_state:
+ * @wb: The whiteboard.
+ *
+ * Return the state of the whiteboard.
+ *
+ * Returns: The state of the whiteboard.
+ */
+int purple_whiteboard_get_state(const PurpleWhiteboard *wb);
+
+/**
+ * purple_whiteboard_start:
+ * @wb: The whiteboard.
+ *
+ * Starts a whiteboard
*/
void purple_whiteboard_start(PurpleWhiteboard *wb);
/**
- * Finds a whiteboard from an account and user.
+ * purple_whiteboard_get_session:
+ * @account: The account.
+ * @who: The user.
*
- * @param account The account.
- * @param who The user.
+ * Finds a whiteboard from an account and user.
*
- * @return The whiteboard if found, otherwise @c NULL.
+ * Returns: The whiteboard if found, otherwise %NULL.
*/
PurpleWhiteboard *purple_whiteboard_get_session(const PurpleAccount *account, const char *who);
/**
- * Destorys a drawing list for a whiteboard
+ * purple_whiteboard_draw_list_destroy:
+ * @draw_list: The drawing list.
*
- * @param draw_list The drawing list.
+ * Destorys a drawing list for a whiteboard
*/
void purple_whiteboard_draw_list_destroy(GList *draw_list);
/**
- * Gets the dimension of a whiteboard.
+ * purple_whiteboard_get_dimensions:
+ * @wb: The whiteboard.
+ * @width: The width to be set.
+ * @height: The height to be set.
*
- * @param wb The whiteboard.
- * @param width The width to be set.
- * @param height The height to be set.
+ * Gets the dimension of a whiteboard.
*
- * @return TRUE if the values of width and height were set.
+ * Returns: TRUE if the values of width and height were set.
*/
gboolean purple_whiteboard_get_dimensions(const PurpleWhiteboard *wb, int *width, int *height);
/**
- * Sets the dimensions for a whiteboard.
+ * purple_whiteboard_set_dimensions:
+ * @wb: The whiteboard.
+ * @width: The width.
+ * @height: The height.
*
- * @param wb The whiteboard.
- * @param width The width.
- * @param height The height.
+ * Sets the dimensions for a whiteboard.
*/
void purple_whiteboard_set_dimensions(PurpleWhiteboard *wb, int width, int height);
/**
- * Draws a point on a whiteboard.
+ * purple_whiteboard_draw_point:
+ * @wb: The whiteboard.
+ * @x: The x coordinate.
+ * @y: The y coordinate.
+ * @color: The color to use.
+ * @size: The brush size.
*
- * @param wb The whiteboard.
- * @param x The x coordinate.
- * @param y The y coordinate.
- * @param color The color to use.
- * @param size The brush size.
+ * Draws a point on a whiteboard.
*/
void purple_whiteboard_draw_point(PurpleWhiteboard *wb, int x, int y, int color, int size);
/**
- * Send a list of points to draw to the buddy.
+ * purple_whiteboard_send_draw_list:
+ * @wb: The whiteboard
+ * @list: A GList of points
*
- * @param wb The whiteboard
- * @param list A GList of points
+ * Send a list of points to draw to the buddy.
*/
void purple_whiteboard_send_draw_list(PurpleWhiteboard *wb, GList *list);
/**
- * Draws a line on a whiteboard
+ * purple_whiteboard_draw_line:
+ * @wb: The whiteboard.
+ * @x1: The top-left x coordinate.
+ * @y1: The top-left y coordinate.
+ * @x2: The bottom-right x coordinate.
+ * @y2: The bottom-right y coordinate.
+ * @color: The color to use.
+ * @size: The brush size.
*
- * @param wb The whiteboard.
- * @param x1 The top-left x coordinate.
- * @param y1 The top-left y coordinate.
- * @param x2 The bottom-right x coordinate.
- * @param y2 The bottom-right y coordinate.
- * @param color The color to use.
- * @param size The brush size.
+ * Draws a line on a whiteboard
*/
void purple_whiteboard_draw_line(PurpleWhiteboard *wb, int x1, int y1, int x2, int y2, int color, int size);
/**
- * Clears a whiteboard
+ * purple_whiteboard_clear:
+ * @wb: The whiteboard.
*
- * @param wb The whiteboard.
+ * Clears a whiteboard
*/
void purple_whiteboard_clear(PurpleWhiteboard *wb);
/**
- * Sends a request to the buddy to clear the whiteboard.
+ * purple_whiteboard_send_clear:
+ * @wb: The whiteboard
*
- * @param wb The whiteboard
+ * Sends a request to the buddy to clear the whiteboard.
*/
void purple_whiteboard_send_clear(PurpleWhiteboard *wb);
/**
- * Sends a request to change the size and color of the brush.
+ * purple_whiteboard_send_brush:
+ * @wb: The whiteboard
+ * @size: The size of the brush
+ * @color: The color of the brush
*
- * @param wb The whiteboard
- * @param size The size of the brush
- * @param color The color of the brush
+ * Sends a request to change the size and color of the brush.
*/
void purple_whiteboard_send_brush(PurpleWhiteboard *wb, int size, int color);
/**
- * Gets the size and color of the brush.
+ * purple_whiteboard_get_brush:
+ * @wb: The whiteboard
+ * @size: The size of the brush
+ * @color: The color of the brush
*
- * @param wb The whiteboard
- * @param size The size of the brush
- * @param color The color of the brush
+ * Gets the size and color of the brush.
*
- * @return TRUE if the size and color were set.
+ * Returns: TRUE if the size and color were set.
*/
gboolean purple_whiteboard_get_brush(const PurpleWhiteboard *wb, int *size, int *color);
/**
- * Sets the size and color of the brush.
+ * purple_whiteboard_set_brush:
+ * @wb: The whiteboard
+ * @size: The size of the brush
+ * @color: The color of the brush
*
- * @param wb The whiteboard
- * @param size The size of the brush
- * @param color The color of the brush
+ * Sets the size and color of the brush.
*/
void purple_whiteboard_set_brush(PurpleWhiteboard *wb, int size, int color);
-/*@}*/
+/**
+ * purple_whiteboard_get_draw_list:
+ * @wb: The whiteboard.
+ *
+ * Return the drawing list.
+ *
+ * Returns: The drawing list
+ */
+GList *purple_whiteboard_get_draw_list(const PurpleWhiteboard *wb);
+
+/**
+ * purple_whiteboard_set_draw_list:
+ * @wb: The whiteboard
+ * @draw_list: The drawing list.
+ *
+ * Set the drawing list.
+ */
+void purple_whiteboard_set_draw_list(PurpleWhiteboard *wb, GList* draw_list);
+
+/**
+ * purple_whiteboard_set_protocol_data:
+ * @wb: The whiteboard.
+ * @proto_data: The protocol data to set for the whiteboard.
+ *
+ * Sets the protocol data for a whiteboard.
+ */
+void purple_whiteboard_set_protocol_data(PurpleWhiteboard *wb, gpointer proto_data);
+
+/**
+ * purple_whiteboard_get_protocol_data:
+ * @wb: The whiteboard.
+ *
+ * Gets the protocol data for a whiteboard.
+ *
+ * Returns: The protocol data for the whiteboard.
+ */
+gpointer purple_whiteboard_get_protocol_data(const PurpleWhiteboard *wb);
+
+/**
+ * purple_whiteboard_set_ui_data:
+ * @wb: The whiteboard.
+ * @ui_data: A pointer to associate with this whiteboard.
+ *
+ * Set the UI data associated with this whiteboard.
+ */
+void purple_whiteboard_set_ui_data(PurpleWhiteboard *wb, gpointer ui_data);
+
+/**
+ * purple_whiteboard_get_ui_data:
+ * @wb: The whiteboard..
+ *
+ * Get the UI data associated with this whiteboard.
+ *
+ * Returns: The UI data associated with this whiteboard. This is a
+ * convenience field provided to the UIs--it is not
+ * used by the libpurple core.
+ */
+gpointer purple_whiteboard_get_ui_data(const PurpleWhiteboard *wb);
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
+G_END_DECLS
#endif /* _PURPLE_WHITEBOARD_H_ */
diff --git a/libpurple/win32/giowin32.c b/libpurple/win32/giowin32.c
index 4b82d90fd1..fd96ceed15 100644
--- a/libpurple/win32/giowin32.c
+++ b/libpurple/win32/giowin32.c
@@ -32,7 +32,7 @@
/* Define this to get (very) verbose logging of all channels */
/* #define G_IO_WIN32_DEBUG */
-/* #include "config.h" */
+#include <config.h>
#include <glib.h>
@@ -222,7 +222,7 @@ init_reset_sockets (GIOWin32Channel *channel)
int len;
channel->reset_send = (gint) socket (AF_INET, SOCK_DGRAM, 0);
- if (channel->reset_send == INVALID_SOCKET)
+ if (channel->reset_send == (gint)INVALID_SOCKET)
{
g_warning (G_STRLOC ": Error creating reset_send socket: %s\n",
g_win32_error_message (WSAGetLastError ()));
@@ -243,7 +243,7 @@ init_reset_sockets (GIOWin32Channel *channel)
local2.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
channel->reset_recv = (gint) socket (AF_INET, SOCK_DGRAM, 0);
- if (channel->reset_recv == INVALID_SOCKET)
+ if (channel->reset_recv == (gint)INVALID_SOCKET)
{
g_warning (G_STRLOC ": Error creating reset_recv socket: %s\n",
g_win32_error_message (WSAGetLastError ()));
@@ -603,9 +603,9 @@ g_io_win32_free (GIOChannel *channel)
win32_channel->thread_id,
win32_channel->fd);
- if (win32_channel->reset_send && win32_channel->reset_send != INVALID_SOCKET)
+ if (win32_channel->reset_send && win32_channel->reset_send != (gint)INVALID_SOCKET)
closesocket (win32_channel->reset_send);
- if (win32_channel->reset_recv && win32_channel->reset_recv != INVALID_SOCKET)
+ if (win32_channel->reset_recv && win32_channel->reset_recv != (gint)INVALID_SOCKET)
closesocket (win32_channel->reset_recv);
if (win32_channel->data_avail_event)
CloseHandle (win32_channel->data_avail_event);
diff --git a/libpurple/win32/global.mak b/libpurple/win32/global.mak
index 1ba802ed9e..d9dd54bc13 100644
--- a/libpurple/win32/global.mak
+++ b/libpurple/win32/global.mak
@@ -8,22 +8,35 @@
#include optional $(PIDGIN_TREE_TOP)/local.mak to allow overriding of any definitions
-include $(PIDGIN_TREE_TOP)/local.mak
+# TODO: we should do parsing like for PURPLE_VERSION, if we won't drop
+# Makefile.mingw files before 3.0.0 release
+PURPLE_MAJOR_VERSION := 3
+PURPLE_MINOR_VERSION := 0
+PURPLE_MICRO_VERSION := 0
+PURPLE_API_VERSION := 20
+
# Locations of our various dependencies
WIN32_DEV_TOP ?= $(PIDGIN_TREE_TOP)/../win32-dev
-GTKSPELL_TOP ?= $(WIN32_DEV_TOP)/gtkspell-2.0.16
-ENCHANT_TOP ?= $(WIN32_DEV_TOP)/enchant_1.6.0_win32
-GTK_TOP ?= $(WIN32_DEV_TOP)/gtk_2_0-2.14
+ENCHANT_TOP ?= $(WIN32_DEV_TOP)/enchant-1.6
+GNUTLS_TOP ?= $(WIN32_DEV_TOP)/gnutls-3.1
+GTK_TOP ?= $(WIN32_DEV_TOP)/gtk2-2.24
GTK_BIN ?= $(GTK_TOP)/bin
-BONJOUR_TOP ?= $(WIN32_DEV_TOP)/Bonjour_SDK
-LIBXML2_TOP ?= $(WIN32_DEV_TOP)/libxml2-2.9.0
-MEANWHILE_TOP ?= $(WIN32_DEV_TOP)/meanwhile-1.0.2_daa3
-NSS_TOP ?= $(WIN32_DEV_TOP)/nss-3.16-nspr-4.10.4
-PERL_LIB_TOP ?= $(WIN32_DEV_TOP)/perl-5.10.0
-SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.1.10
-TCL_LIB_TOP ?= $(WIN32_DEV_TOP)/tcl-8.4.5
-GSTREAMER_TOP ?= $(WIN32_DEV_TOP)/gstreamer-0.10.13
+BONJOUR_TOP ?= $(WIN32_DEV_TOP)/bonjour-sdk
+JSON_GLIB_TOP ?= $(WIN32_DEV_TOP)/json-glib-0.14
+LIBXML2_TOP ?= $(WIN32_DEV_TOP)/libxml2-2.9
+MEANWHILE_TOP ?= $(WIN32_DEV_TOP)/meanwhile-1.0
+NSS_TOP ?= $(WIN32_DEV_TOP)/nss-3.14
+PERL_LIB_TOP ?= $(WIN32_DEV_TOP)/perl-5.10
+SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.1
+TCL_LIB_TOP ?= $(WIN32_DEV_TOP)/tcl-8.5
+GSTREAMER_TOP ?= $(WIN32_DEV_TOP)/gstreamer-0.10
GCC_SSP_TOP ?= $(shell dirname $(shell which $(CC)))
-CYRUS_SASL_TOP ?= $(WIN32_DEV_TOP)/cyrus-sasl-2.1.25
+CYRUS_SASL_TOP ?= $(WIN32_DEV_TOP)/cyrus-sasl-2.1
+WEBKITGTK_TOP ?= $(WIN32_DEV_TOP)/libwebkitgtk-1.10
+LIBSOUP_TOP ?= $(WIN32_DEV_TOP)/libsoup-2.42
+GETTEXT_TOP ?= $(WIN32_DEV_TOP)/gettext-0.18
+INTLTOOL_TOP ?= $(WIN32_DEV_TOP)/intltool-0.50
+LIBGADU_TOP ?= $(WIN32_DEV_TOP)/libgadu-1.12
# Where we installing this stuff to?
PIDGIN_INSTALL_DIR := $(PIDGIN_TREE_TOP)/win32-install-dir
@@ -62,6 +75,7 @@ GCCWARNINGS ?= -Waggregate-return -Wcast-align -Wdeclaration-after-statement -We
CC_HARDENING_OPTIONS ?= -Wstack-protector -fwrapv -fno-strict-overflow -Wno-missing-field-initializers -Wformat-security -fstack-protector-all --param ssp-buffer-size=1
LD_HARDENING_OPTIONS ?= -Wl,--dynamicbase -Wl,--nxcompat
+TAG := @$(PURPLE_TOP)/tag.sh
# parse the version number from the configure.ac file if it is newer
#m4_define([purple_major_version], [2])
@@ -103,18 +117,45 @@ DLL_LD_FLAGS += -Wl,--enable-auto-image-base -Wl,--enable-auto-import $(LD_HARDE
ifeq "$(origin CC)" "default"
CC := gcc.exe
endif
-GMSGFMT ?= $(WIN32_DEV_TOP)/gettext-0.17/bin/msgfmt
+# comment out the next line to make output more verbose
+CC := $(TAG) "auto" $(CC)
+
+GMSGFMT ?= $(GETTEXT_TOP)/bin/msgfmt
MAKENSIS ?= makensis.exe
PERL ?= perl
WINDRES ?= windres
STRIP ?= strip
-INTLTOOL_MERGE ?= $(WIN32_DEV_TOP)/intltool_0.40.4-1_win32/bin/intltool-merge
+INTLTOOL_MERGE ?= $(INTLTOOL_TOP)/bin/intltool-merge
MONO_SIGNCODE ?= signcode
GPG_SIGN ?= gpg
+GLIB_GENMARSHAL ?= $(GTK_BIN)/glib-genmarshal
+GLIB_MKENUMS ?= $(GTK_BIN)/glib-mkenums
PIDGIN_COMMON_RULES := $(PURPLE_TOP)/win32/rules.mak
PIDGIN_COMMON_TARGETS := $(PURPLE_TOP)/win32/targets.mak
MINGW_MAKEFILE := Makefile.mingw
+MAKE_at := @
+
+USE_VV ?= 1
+
+ifeq "$(USE_VV)" "1"
+VV_LIBS := \
+ -lgstreamer-0.10 \
+ -lgstvideo-0.10 \
+ -lgstinterfaces-0.10 \
+ -lfarstream-0.1
+VV_INCLUDE_PATHS := \
+ -I$(GSTREAMER_TOP)/include/gstreamer-0.10 \
+ -I$(GSTREAMER_TOP)/include/farstream-0.1 \
+ -I$(LIBXML2_TOP)/include/libxml2
+VV_LIB_PATHS := \
+ -L$(GSTREAMER_TOP)/lib
+DEFINES += -DUSE_GSTREAMER -DUSE_VV
+else
+VV_LIBS :=
+VV_INCLUDE_PATHS :=
+VV_LIB_PATHS :=
+endif
INSTALL_PIXMAPS ?= 1
INSTALL_SSL_CERTIFICATES ?= 1
diff --git a/libpurple/win32/libc_interface.c b/libpurple/win32/libc_interface.c
index 493dfca53f..fc4e171940 100644
--- a/libpurple/win32/libc_interface.c
+++ b/libpurple/win32/libc_interface.c
@@ -18,6 +18,9 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
*/
+
+#include <config.h>
+
#include <winsock2.h>
#include <ws2tcpip.h>
#include <io.h>
@@ -28,7 +31,6 @@
#include <sys/stat.h>
#include <time.h>
#include <glib.h>
-#include "config.h"
#include "debug.h"
#include "libc_internal.h"
#include <glib/gstdio.h>
@@ -82,7 +84,7 @@ int wpurple_socket (int namespace, int style, int protocol) {
ret = socket( namespace, style, protocol );
- if( ret == INVALID_SOCKET ) {
+ if (ret == (int)INVALID_SOCKET) {
errno = WSAGetLastError();
return -1;
}
@@ -308,7 +310,7 @@ wpurple_inet_pton(int af, const char *src, void *dst)
struct sockaddr_in6 sin6;
struct sockaddr_in sin;
} sa;
- size_t srcsize;
+ int srcsize;
switch(af)
{
@@ -325,7 +327,7 @@ wpurple_inet_pton(int af, const char *src, void *dst)
return -1;
}
- if (WSAStringToAddress(src, af, NULL, (struct sockaddr *) &sa, &srcsize) != 0)
+ if (WSAStringToAddress((LPTSTR)src, af, NULL, (struct sockaddr *) &sa, &srcsize) != 0)
{
errno = WSAGetLastError();
return -1;
@@ -511,12 +513,6 @@ int wpurple_gettimeofday(struct timeval *p, struct timezone *z) {
return res;
}
-/* stdio.h */
-
-int wpurple_rename (const char *oldname, const char *newname) {
- return g_rename(oldname, newname);
-}
-
/* time.h */
struct tm * wpurple_localtime_r (const time_t *time, struct tm *resultp) {
@@ -1091,15 +1087,3 @@ wpurple_get_timezone_abbreviation(const struct tm *tm)
purple_debug_warning("wpurple", "could not find a match for Windows timezone \"%s\"\n", tzname);
return "";
}
-
-int wpurple_g_access (const gchar *filename, int mode);
-/**
- * @deprecated - remove for 3.0.0
- */
-int
-wpurple_g_access (const gchar *filename, int mode)
-{
- return g_access(filename, mode);
-}
-
-
diff --git a/libpurple/win32/libc_interface.h b/libpurple/win32/libc_interface.h
index 1aacf56863..011d178d51 100644
--- a/libpurple/win32/libc_interface.h
+++ b/libpurple/win32/libc_interface.h
@@ -22,12 +22,16 @@
*/
#ifndef _LIBC_INTERFACE_H_
#define _LIBC_INTERFACE_H_
+
+#include <config.h>
+
#include <winsock2.h>
#include <ws2tcpip.h>
#include <io.h>
#include <errno.h>
#include "libc_internal.h"
#include <glib.h>
+#include "glibcompat.h"
#ifdef __cplusplus
extern "C" {
@@ -132,18 +136,23 @@ wpurple_gethostname( name, size )
wpurple_gettimeofday( timeval, timezone )
/* stdio.h */
-#undef snprintf
-#define snprintf _snprintf
-#undef vsnprintf
-#define vsnprintf _vsnprintf
+#if !defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 3 || \
+ !defined(IS_WIN32_CROSS_COMPILED)
+# undef snprintf
+# define snprintf _snprintf
+# undef vsnprintf
+# define vsnprintf _vsnprintf
+#endif
#define rename( oldname, newname ) \
-wpurple_rename( oldname, newname )
+g_rename( oldname, newname )
/* sys/stat.h */
#define fchmod(a,b)
/* time.h */
+/* XXX: it may be also defined by pthread.h */
+#undef localtime_r
#define localtime_r( time, resultp ) \
wpurple_localtime_r( time, resultp )
diff --git a/libpurple/win32/libc_internal.h b/libpurple/win32/libc_internal.h
index 565af6ea3e..10fa2483e8 100644
--- a/libpurple/win32/libc_internal.h
+++ b/libpurple/win32/libc_internal.h
@@ -142,9 +142,6 @@ int wpurple_close(int fd);
int wpurple_gethostname(char *name, size_t size);
-/* stdio.h */
-int wpurple_rename(const char *oldname, const char *newname);
-
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/libpurple/win32/libpurplerc.rc.in b/libpurple/win32/libpurplerc.rc.in
index 3d5e9e3e1d..26441110b7 100644
--- a/libpurple/win32/libpurplerc.rc.in
+++ b/libpurple/win32/libpurplerc.rc.in
@@ -1,9 +1,8 @@
#include <winver.h>
-#include "version.h"
VS_VERSION_INFO VERSIONINFO
- FILEVERSION PURPLE_MAJOR_VERSION,PURPLE_MINOR_VERSION,PURPLE_MICRO_VERSION,0
- PRODUCTVERSION PURPLE_MAJOR_VERSION,PURPLE_MINOR_VERSION,PURPLE_MICRO_VERSION,0
+ FILEVERSION @PURPLE_MAJOR_VERSION@,@PURPLE_MINOR_VERSION@,@PURPLE_MICRO_VERSION@,0
+ PRODUCTVERSION @PURPLE_MAJOR_VERSION@,@PURPLE_MINOR_VERSION@,@PURPLE_MICRO_VERSION@,0
FILEFLAGSMASK 0
FILEFLAGS 0
FILEOS VOS__WINDOWS32
@@ -15,12 +14,12 @@ VS_VERSION_INFO VERSIONINFO
BLOCK "040904B0"
BEGIN
VALUE "CompanyName", "The Pidgin developer community"
- VALUE "FileDescription", "LibPurple Library"
+ VALUE "FileDescription", "libpurple library"
VALUE "FileVersion", "@PURPLE_VERSION@"
VALUE "InternalName", "libpurple"
- VALUE "LegalCopyright", "Copyright (C) 1998-2010 The Pidgin developer community (See the COPYRIGHT file in the source distribution)."
- VALUE "OriginalFilename", "libpurple.dll"
- VALUE "ProductName", "LibPurple"
+ VALUE "LegalCopyright", "Copyright (C) 1998-2014 The Pidgin developer community (See the COPYRIGHT file in the source distribution)."
+ VALUE "OriginalFilename", "libpurple-@PURPLE_API_VERSION@.dll"
+ VALUE "ProductName", "libpurple"
VALUE "ProductVersion", "@PURPLE_VERSION@"
END
END
diff --git a/libpurple/win32/rules.mak b/libpurple/win32/rules.mak
index f372f2a5be..9ea9372495 100644
--- a/libpurple/win32/rules.mak
+++ b/libpurple/win32/rules.mak
@@ -4,10 +4,17 @@
$(CC) $(CFLAGS) $(DEFINES) $(INCLUDE_PATHS) -o $@ -c $<
%.c: %.xs
- $(PERL) -MExtUtils::ParseXS -e 'ExtUtils::ParseXS::process_file(filename => "$<", output => "$@", typemap => "$(PURPLE_PERL_TOP)/common/typemap");'
+ $(TAG) "PERL" $(PERL) -MExtUtils::ParseXS -e 'ExtUtils::ParseXS::process_file(filename => "$<", output => "$@", typemap => "$(PURPLE_PERL_TOP)/common/typemap");'
%.o: %.rc
- $(WINDRES) -I$(PURPLE_TOP) -i $< -o $@
+ @echo -e " GEN\t$@"
+ @$(WINDRES) -I$(PURPLE_TOP) -i $< -o $@
%.desktop: %.desktop.in $(wildcard $(PIDGIN_TREE_TOP)/po/*.po)
LC_ALL=C $(PERL) $(INTLTOOL_MERGE) -d -u -c $(PIDGIN_TREE_TOP)/po/.intltool-merge-cache $(PIDGIN_TREE_TOP)/po $< $@
+
+%.html.h: %.html
+ @echo -e " GEN\t$@"
+ @echo "static const char $*_html[] = {" > $@
+ @sed -e 's/^[ ]\+//g' -e 's/[ ]\+/ /g' $< | xxd -i | sed -e 's/\(0x[0-9a-f][0-9a-f]\)$$/\1, 0x00/' >> $@
+ @echo "};" >> $@
diff --git a/libpurple/win32/targets.mak b/libpurple/win32/targets.mak
index e4f8049459..f48f2ba836 100644
--- a/libpurple/win32/targets.mak
+++ b/libpurple/win32/targets.mak
@@ -6,26 +6,31 @@
#
$(PIDGIN_CONFIG_H): $(PIDGIN_CONFIG_H).mingw $(PIDGIN_TREE_TOP)/configure.ac
- sed -e 's/@VERSION@/$(PIDGIN_VERSION)/; s/@DISPLAY_VERSION@/$(DISPLAY_VERSION)/' $@.mingw > $@
+ @echo -e " GEN\t$@"
+ @sed -e 's/@VERSION@/$(PIDGIN_VERSION)/; s/@DISPLAY_VERSION@/$(DISPLAY_VERSION)/' $@.mingw > $@
$(PURPLE_PURPLE_H): $(PURPLE_PURPLE_H).in
- sed -e 's/@PLUGINS_DEFINE@/#define PURPLE_PLUGINS 1/' $@.in > $@
+ @echo -e " GEN\t$@"
+ @sed -e 's/@PLUGINS_DEFINE@/#define PURPLE_PLUGINS 1/' $@.in > $@
$(PURPLE_VERSION_H): $(PURPLE_VERSION_H).in $(PIDGIN_TREE_TOP)/configure.ac
- awk 'BEGIN {FS="[\\(\\)\\[\\]]"} \
+ @echo -e " GEN\t$@"
+ @awk 'BEGIN {FS="[\\(\\)\\[\\]]"} \
/^m4_define..purple_major_version/ {system("sed -e s/@PURPLE_MAJOR_VERSION@/"$$5"/ $@.in > $@");} \
/^m4_define..purple_minor_version/ {system("sed -e s/@PURPLE_MINOR_VERSION@/"$$5"/ $@ > $@.tmp && mv $@.tmp $@");} \
/^m4_define..purple_micro_version/ {system("sed -e s/@PURPLE_MICRO_VERSION@/"$$5"/ $@ > $@.tmp && mv $@.tmp $@"); exit}' $(PIDGIN_TREE_TOP)/configure.ac
$(PIDGIN_REVISION_RAW_TXT):
- (hg --cwd $(PIDGIN_TREE_TOP) id -i --debug) 2>/dev/null >$@ \
+ @echo -e " GEN\t$@"
+ @(hg --cwd $(PIDGIN_TREE_TOP) id -i --debug) 2>/dev/null >$@ \
|| rm -f $@
$(PIDGIN_REVISION_H): $(PIDGIN_REVISION_RAW_TXT)
- if [ -f $< ]; then \
+ @echo -e " GEN\t$@"
+ @if [ -f $< ]; then \
sed 's/^\(.\{1,\}\)$$/#define REVISION "\1"/' $< > $@; \
fi
- [ -f $@ ] || echo "#define REVISION \"unknown\"" > $@
+ @[ -f $@ ] || echo "#define REVISION \"unknown\"" > $@
$(PURPLE_DLL) $(PURPLE_DLL).a: $(PURPLE_VERSION_H)
$(MAKE) -C $(PURPLE_TOP) -f $(MINGW_MAKEFILE) libpurple.dll
@@ -34,7 +39,7 @@ $(PURPLE_PERL_DLL) $(PURPLE_PERL_DLL).a:
$(MAKE) -C $(PURPLE_PERL_TOP) -f $(MINGW_MAKEFILE) perl.dll
$(PIDGIN_DLL) $(PIDGIN_DLL).a:
- $(MAKE) -C $(PIDGIN_TOP) -f $(MINGW_MAKEFILE) pidgin.dll
+ $(MAKE_at) $(MAKE) -C $(PIDGIN_TOP) -f $(MINGW_MAKEFILE) pidgin.dll
$(PIDGIN_EXE):
$(MAKE) -C $(PIDGIN_TOP) -f $(MINGW_MAKEFILE) pidgin.exe
diff --git a/libpurple/win32/win32dep.c b/libpurple/win32/win32dep.c
index 2c2824f744..6ca5e737f5 100644
--- a/libpurple/win32/win32dep.c
+++ b/libpurple/win32/win32dep.c
@@ -22,18 +22,21 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
*/
-#define _WIN32_IE 0x501
#include "internal.h"
#include <winuser.h>
#include "debug.h"
+#include "glibcompat.h"
#include "notify.h"
+#define MAX_PATH_LEN 2048
+
/*
* LOCALS
*/
-static char *app_data_dir = NULL, *install_dir = NULL,
- *lib_dir = NULL, *locale_dir = NULL;
+static char *app_data_dir = NULL, *bin_dir = NULL, *data_dir = NULL,
+ *lib_dir = NULL, *locale_dir = NULL, *sysconf_dir = NULL,
+ *cert_dir = NULL;
static HINSTANCE libpurpledll_hInstance = NULL;
@@ -80,7 +83,8 @@ FARPROC wpurple_find_and_loadproc(const char *dllname, const char *procedure) {
wchar_t *wc_dllname = g_utf8_to_utf16(dllname, -1, NULL, NULL, NULL);
if(!(hmod = GetModuleHandleW(wc_dllname))) {
- purple_debug_warning("wpurple", "%s not already loaded; loading it...\n", dllname);
+ if (purple_debug_is_verbose())
+ purple_debug_info("wpurple", "%s not already loaded; loading it...\n", dllname);
if(!(hmod = LoadLibraryW(wc_dllname))) {
purple_debug_error("wpurple", "Could not load: %s (%s)\n", dllname,
g_win32_error_message(GetLastError()));
@@ -95,8 +99,10 @@ FARPROC wpurple_find_and_loadproc(const char *dllname, const char *procedure) {
wc_dllname = NULL;
if((proc = GetProcAddress(hmod, procedure))) {
- purple_debug_info("wpurple", "This version of %s contains %s\n",
- dllname, procedure);
+ if (purple_debug_is_verbose()) {
+ purple_debug_info("wpurple", "This version of %s contains %s\n",
+ dllname, procedure);
+ }
return proc;
}
else {
@@ -125,12 +131,17 @@ gchar *wpurple_get_special_folder(int folder_type) {
return retval;
}
-const char *wpurple_install_dir(void) {
+const char *wpurple_bin_dir(void) {
static gboolean initialized = FALSE;
if (!initialized) {
char *tmp = NULL;
wchar_t winstall_dir[MAXPATHLEN];
+
+ /* We might use g_win32_get_package_installation_directory_of_module
+ * here, but we won't because this routine strips bin or lib
+ * part of the path.
+ */
if (GetModuleFileNameW(libpurpledll_hInstance, winstall_dir,
MAXPATHLEN) > 0) {
tmp = g_utf16_to_utf8(winstall_dir, -1,
@@ -144,48 +155,144 @@ const char *wpurple_install_dir(void) {
g_free(tmp);
return NULL;
} else {
- install_dir = g_path_get_dirname(tmp);
+ bin_dir = g_path_get_dirname(tmp);
g_free(tmp);
initialized = TRUE;
}
}
- return install_dir;
+ return bin_dir;
+}
+
+#ifdef USE_WIN32_FHS
+static gchar *
+wpurple_install_relative_path(const gchar *abspath)
+{
+ const gchar *bindir = WIN32_FHS_BINDIR;
+ const gchar *relpath;
+ int i, last_dirsep = -1, bin_esc_cnt;
+ gchar *ret;
+ GString *bin_esc;
+
+ g_return_val_if_fail(bindir != NULL, NULL);
+ g_return_val_if_fail(bindir[0] != '\0', NULL);
+ g_return_val_if_fail(abspath != NULL, NULL);
+ g_return_val_if_fail(abspath[0] != '\0', NULL);
+
+ /* let's find the common prefix of those paths */
+ for (i = 0; bindir[i] == abspath[i]; i++) {
+ if (bindir[i] == '\0')
+ break;
+ if (bindir[i] == '\\' || bindir[i] == '/')
+ last_dirsep = i;
+ }
+ if (bindir[i] == '\0' && (abspath[i] == '\\' || abspath[i] == '/'))
+ last_dirsep = i;
+ if (abspath[i] == '\0' && (bindir[i] == '\\' || bindir[i] == '/'))
+ last_dirsep = i;
+
+ /* there is no common prefix, return absolute path */
+ if (last_dirsep == -1)
+ return g_strdup(abspath);
+
+ /* let's check, how many dirs we need to go up to the common prefix */
+ bin_esc_cnt = 0;
+ for (i = last_dirsep; bindir[i]; i++) {
+ if (bindir[i] != '\\' && bindir[i] != '/')
+ continue;
+ if (bindir[i + 1] == '\0') /* trailing dir separator */
+ break;
+ bin_esc_cnt++;
+ }
+ bin_esc = g_string_new("");
+ for (i = 0; i < bin_esc_cnt; i++)
+ g_string_append(bin_esc, ".." G_DIR_SEPARATOR_S);
+
+ /* now, we need to go back deeper into the directory tree */
+ relpath = &abspath[last_dirsep];
+ if (relpath[0] != '\0')
+ relpath++;
+
+ /* - enter bin dir
+ * - escape it to the common prefix
+ * - dive into the abspath dir
+ */
+ ret = g_build_filename(wpurple_bin_dir(), bin_esc->str, relpath, NULL);
+ g_string_free(bin_esc, TRUE);
+
+ purple_debug_misc("wpurple", "wpurple_install_relative_path(%s) = %s",
+ abspath, ret);
+
+ return ret;
}
+#endif
+
+const char *
+wpurple_data_dir(void) {
+#ifdef USE_WIN32_FHS
+ static gboolean initialized = FALSE;
+ if (initialized)
+ return data_dir;
+ data_dir = wpurple_install_relative_path(WIN32_FHS_DATADIR);
+ initialized = TRUE;
+
+ return data_dir;
+#else
+ return wpurple_bin_dir();
+#endif
+}
+
-const char *wpurple_lib_dir(void) {
+const char *wpurple_lib_dir(const char *subdir)
+{
static gboolean initialized = FALSE;
+ static gchar subpath[MAX_PATH_LEN];
if (!initialized) {
- const char *inst_dir = wpurple_install_dir();
+#ifdef USE_WIN32_FHS
+ lib_dir = wpurple_install_relative_path(WIN32_FHS_LIBDIR);
+ initialized = TRUE;
+#else
+ const char *inst_dir = wpurple_bin_dir();
if (inst_dir != NULL) {
lib_dir = g_strdup_printf("%s" G_DIR_SEPARATOR_S "plugins", inst_dir);
initialized = TRUE;
} else {
return NULL;
}
+#endif
}
- return lib_dir;
+ if (subdir == NULL)
+ return lib_dir;
+
+ g_snprintf(subpath, sizeof(subpath),
+ "%s" G_DIR_SEPARATOR_S "%s", lib_dir, subdir);
+ return subpath;
}
const char *wpurple_locale_dir(void) {
static gboolean initialized = FALSE;
if (!initialized) {
- const char *inst_dir = wpurple_install_dir();
+#ifdef USE_WIN32_FHS
+ locale_dir = wpurple_install_relative_path(WIN32_FHS_LOCALEDIR);
+ initialized = TRUE;
+#else
+ const char *inst_dir = wpurple_bin_dir();
if (inst_dir != NULL) {
locale_dir = g_strdup_printf("%s" G_DIR_SEPARATOR_S "locale", inst_dir);
initialized = TRUE;
} else {
return NULL;
}
+#endif
}
return locale_dir;
}
-const char *wpurple_data_dir(void) {
+const char *wpurple_home_dir(void) {
if (!app_data_dir) {
/* Set app data dir, used by purple_home_dir */
@@ -204,6 +311,38 @@ const char *wpurple_data_dir(void) {
return app_data_dir;
}
+const char *wpurple_sysconf_dir(void)
+{
+ static gboolean initialized = FALSE;
+
+ if (!initialized) {
+#ifdef USE_WIN32_FHS
+ sysconf_dir = wpurple_install_relative_path(WIN32_FHS_SYSCONFDIR);
+#else
+ sysconf_dir = wpurple_get_special_folder(CSIDL_COMMON_APPDATA);
+#endif
+ initialized = TRUE;
+ }
+
+ return sysconf_dir;
+}
+
+#if defined(USE_WIN32_FHS) && defined(SSL_CERTIFICATES_DIR)
+const char *
+wpurple_cert_dir(void)
+{
+ static gboolean initialized = FALSE;
+
+ if (initialized)
+ return sysconf_dir;
+
+ sysconf_dir = wpurple_install_relative_path(SSL_CERTIFICATES_DIR);
+ initialized = TRUE;
+
+ return sysconf_dir;
+}
+#endif
+
/* Miscellaneous */
gboolean wpurple_write_reg_string(HKEY rootkey, const char *subkey, const char *valname,
@@ -287,6 +426,52 @@ static gboolean _reg_read(HKEY reg_key, const char *valname, LPDWORD type, LPBYT
return (rv == ERROR_SUCCESS);
}
+gboolean wpurple_reg_val_exists(HKEY rootkey, const char *subkey, const char *valname)
+{
+ HKEY hkey;
+ LONG retv;
+ DWORD index;
+ wchar_t name_buffer[100];
+ BOOL exists = FALSE;
+ wchar_t *wc_valname = NULL;
+ wchar_t *wc_subkey;
+
+ if (subkey == NULL)
+ return FALSE;
+
+ wc_subkey = g_utf8_to_utf16(subkey, -1, NULL, NULL, NULL);
+ retv = RegOpenKeyExW(rootkey, wc_subkey, 0, KEY_ENUMERATE_SUB_KEYS, &hkey);
+ g_free(wc_subkey);
+
+ if (retv != ERROR_SUCCESS)
+ return FALSE;
+
+ if (valname[0] == '\0' || valname == NULL) {
+ RegCloseKey(hkey);
+ return TRUE;
+ }
+
+ wc_valname = g_utf8_to_utf16(valname, -1, NULL, NULL, NULL);
+ index = 0;
+ while (TRUE)
+ {
+ DWORD name_size = sizeof(name_buffer);
+ retv = RegEnumValueW(hkey, index++, name_buffer, &name_size,
+ NULL, NULL, NULL, NULL);
+ if (retv != ERROR_SUCCESS)
+ break;
+ name_size /= sizeof(wchar_t);
+ if (wcsncmp(name_buffer, wc_valname, name_size) == 0) {
+ exists = TRUE;
+ break;
+ }
+ }
+ g_free(wc_valname);
+
+ RegCloseKey(hkey);
+ return exists;
+}
+
gboolean wpurple_read_reg_dword(HKEY rootkey, const char *subkey, const char *valname, LPDWORD result) {
DWORD type;
@@ -328,17 +513,129 @@ char *wpurple_read_reg_string(HKEY rootkey, const char *subkey, const char *valn
return result;
}
+int wpurple_input_pipe(int pipefd[2])
+{
+ SOCKET sock_server, sock_client, sock_server_established;
+ struct sockaddr_in saddr_in;
+ struct sockaddr * const saddr_p = (struct sockaddr *)&saddr_in;
+ int saddr_len = sizeof(struct sockaddr_in);
+ u_long arg;
+ fd_set select_set;
+ char succ = 1;
+
+ sock_server = sock_client = sock_server_established = INVALID_SOCKET;
+
+ purple_debug_misc("wpurple", "wpurple_input_pipe(0x%x[%d,%d])\n",
+ (unsigned int)pipefd, pipefd[0], pipefd[1]);
+
+ /* create client and passive server sockets */
+ sock_server = socket(AF_INET, SOCK_STREAM, 0);
+ sock_client = socket(AF_INET, SOCK_STREAM, 0);
+ succ = (sock_server != INVALID_SOCKET || sock_client != INVALID_SOCKET);
+
+ /* set created sockets into nonblocking mode */
+ arg = 1;
+ succ = (succ &&
+ ioctlsocket(sock_server, FIONBIO, &arg) != SOCKET_ERROR);
+ arg = 1;
+ succ = (succ &&
+ ioctlsocket(sock_client, FIONBIO, &arg) != SOCKET_ERROR);
+
+ /* listen on server socket */
+ memset(&saddr_in, 0, saddr_len);
+ saddr_in.sin_family = AF_INET;
+ saddr_in.sin_port = 0;
+ saddr_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ succ = (succ &&
+ bind(sock_server, saddr_p, saddr_len) != SOCKET_ERROR &&
+ listen(sock_server, 1) != SOCKET_ERROR &&
+ getsockname(sock_server, saddr_p, &saddr_len) != SOCKET_ERROR);
+
+ /* request a connection from client to server socket */
+ succ = (succ &&
+ connect(sock_client, saddr_p, saddr_len) == SOCKET_ERROR &&
+ WSAGetLastError() == WSAEWOULDBLOCK);
+
+ /* ensure, that server socket is readable */
+ if (succ)
+ {
+ FD_ZERO(&select_set);
+ FD_SET(sock_server, &select_set);
+ }
+ succ = (succ &&
+ select(0, &select_set, NULL, NULL, NULL) != SOCKET_ERROR &&
+ FD_ISSET(sock_server, &select_set));
+
+ /* accept (establish) connection from client socket */
+ if (succ)
+ {
+ sock_server_established =
+ accept(sock_server, saddr_p, &saddr_len);
+ succ = (sock_server_established != INVALID_SOCKET);
+ }
+
+ /* ensure, that client socket is writable */
+ if (succ)
+ {
+ FD_ZERO(&select_set);
+ FD_SET(sock_client, &select_set);
+ }
+ succ = (succ &&
+ select(0, NULL, &select_set, NULL, NULL) != SOCKET_ERROR &&
+ FD_ISSET(sock_client, &select_set));
+
+ /* set sockets into blocking mode */
+ arg = 0;
+ succ = (succ &&
+ ioctlsocket(sock_client, FIONBIO, &arg) != SOCKET_ERROR);
+ arg = 0;
+ succ = (succ &&
+ ioctlsocket(sock_server_established, FIONBIO, &arg)
+ != SOCKET_ERROR);
+
+ /* we don't need (passive) server socket anymore */
+ if (sock_server != INVALID_SOCKET)
+ closesocket(sock_server);
+
+ if (succ)
+ {
+ purple_debug_misc("wpurple",
+ "wpurple_input_pipe created pipe [%d,%d]\n",
+ sock_client, sock_server_established);
+ pipefd[0] = sock_client; /* for reading */
+ pipefd[1] = sock_server_established; /* for writing */
+ return 0;
+ }
+ else
+ {
+ purple_debug_error("wpurple", "wpurple_input_pipe failed\n");
+ if (sock_client != INVALID_SOCKET)
+ closesocket(sock_client);
+ if (sock_server_established != INVALID_SOCKET)
+ closesocket(sock_server_established);
+ errno = EMFILE;
+ return -1;
+ }
+}
+
void wpurple_init(void) {
WORD wVersionRequested;
WSADATA wsaData;
+#if !GLIB_CHECK_VERSION(2, 32, 0)
+ /* GLib threading system is automaticaly initialized since 2.32.
+ * For earlier versions, it have to be initialized before calling any
+ * Glib or GTK+ functions.
+ */
if (!g_thread_supported())
g_thread_init(NULL);
+#endif
- purple_debug_info("wpurple", "wpurple_init start\n");
- purple_debug_info("wpurple", "libpurple version: " DISPLAY_VERSION "\n");
+ if (purple_debug_is_verbose())
+ purple_debug_misc("wpurple", "wpurple_init start\n");
- purple_debug_info("wpurple", "Glib:%u.%u.%u\n",
+ purple_debug_info("wpurple", "libpurple version: " DISPLAY_VERSION "\n");
+ purple_debug_info("wpurple", "Glib: %u.%u.%u\n",
glib_major_version, glib_minor_version, glib_micro_version);
/* Winsock init */
@@ -354,7 +651,8 @@ void wpurple_init(void) {
WSACleanup();
}
- purple_debug_info("wpurple", "wpurple_init end\n");
+ if (purple_debug_is_verbose())
+ purple_debug_misc("wpurple", "wpurple_init end\n");
}
/* Windows Cleanup */
@@ -366,14 +664,20 @@ void wpurple_cleanup(void) {
WSACleanup();
g_free(app_data_dir);
- g_free(install_dir);
+ g_free(bin_dir);
+ g_free(data_dir);
g_free(lib_dir);
g_free(locale_dir);
+ g_free(sysconf_dir);
+ g_free(cert_dir);
app_data_dir = NULL;
- install_dir = NULL;
+ bin_dir = NULL;
+ data_dir = NULL;
lib_dir = NULL;
locale_dir = NULL;
+ sysconf_dir = NULL;
+ cert_dir = NULL;
libpurpledll_hInstance = NULL;
}
diff --git a/libpurple/win32/win32dep.h b/libpurple/win32/win32dep.h
index 673b7d7abc..d4311ad519 100644
--- a/libpurple/win32/win32dep.h
+++ b/libpurple/win32/win32dep.h
@@ -22,6 +22,9 @@
*/
#ifndef _WIN32DEP_H_
#define _WIN32DEP_H_
+
+#include <config.h>
+
#include <winsock2.h>
#include <windows.h>
#include <shlobj.h>
@@ -54,18 +57,27 @@ typedef struct {
**/
/* Windows helper functions */
FARPROC wpurple_find_and_loadproc(const char *dllname, const char *procedure);
+gboolean wpurple_reg_val_exists(HKEY rootkey, const char *subkey, const char *valname);
gboolean wpurple_read_reg_dword(HKEY rootkey, const char *subkey, const char *valname, LPDWORD result);
char *wpurple_read_reg_string(HKEY rootkey, const char *subkey, const char *valname); /* needs to be g_free'd */
gboolean wpurple_write_reg_string(HKEY rootkey, const char *subkey, const char *valname, const char *value);
char *wpurple_escape_dirsep(const char *filename); /* needs to be g_free'd */
GIOChannel *wpurple_g_io_channel_win32_new_socket(int socket); /* Until we get the post-2.8 glib win32 giochannel implementation working, use the thread-based one */
+/* Simulate unix pipes by creating a pair of connected sockets */
+int wpurple_input_pipe(int pipefd[2]);
+
/* Determine Purple paths */
gchar *wpurple_get_special_folder(int folder_type); /* needs to be g_free'd */
-const char *wpurple_install_dir(void);
-const char *wpurple_lib_dir(void);
-const char *wpurple_locale_dir(void);
+const char *wpurple_bin_dir(void);
const char *wpurple_data_dir(void);
+const char *wpurple_lib_dir(const char *subdir);
+const char *wpurple_locale_dir(void);
+const char *wpurple_home_dir(void);
+const char *wpurple_sysconf_dir(void);
+#if defined(USE_WIN32_FHS) && defined(SSL_CERTIFICATES_DIR)
+const char *wpurple_cert_dir(void);
+#endif
/* init / cleanup */
void wpurple_init(void);
@@ -73,17 +85,6 @@ void wpurple_cleanup(void);
long wpurple_get_tz_offset(void);
-/*
- * MACROS
- */
-
-/*
- * Purple specific
- */
-#define DATADIR wpurple_install_dir()
-#define LIBDIR wpurple_lib_dir()
-#define LOCALEDIR wpurple_locale_dir()
-
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/libpurple/xfer.c b/libpurple/xfer.c
new file mode 100644
index 0000000000..4385123c1c
--- /dev/null
+++ b/libpurple/xfer.c
@@ -0,0 +1,2442 @@
+/* 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 "glibcompat.h"
+
+#include "dbus-maybe.h"
+#include "enums.h"
+#include "image-store.h"
+#include "xfer.h"
+#include "network.h"
+#include "notify.h"
+#include "prefs.h"
+#include "proxy.h"
+#include "request.h"
+#include "util.h"
+#include "debug.h"
+
+#define FT_INITIAL_BUFFER_SIZE 4096
+#define FT_MAX_BUFFER_SIZE 65535
+
+#define PURPLE_XFER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_XFER, PurpleXferPrivate))
+
+typedef struct _PurpleXferPrivate PurpleXferPrivate;
+
+static PurpleXferUiOps *xfer_ui_ops = NULL;
+static GList *xfers;
+
+/* Private data for a file transfer */
+struct _PurpleXferPrivate {
+ PurpleXferType type; /* The type of transfer. */
+
+ PurpleAccount *account; /* The account. */
+
+ char *who; /* The person on the other end of the
+ transfer. */
+
+ char *message; /* A message sent with the request */
+ char *filename; /* The name sent over the network. */
+ char *local_filename; /* The name on the local hard drive. */
+ goffset size; /* The size of the file. */
+
+ FILE *dest_fp; /* The destination file pointer. */
+
+ char *remote_ip; /* The remote IP address. */
+ guint16 local_port; /* The local port. */
+ guint16 remote_port; /* The remote port. */
+
+ int fd; /* The socket file descriptor. */
+ int watcher; /* Watcher. */
+
+ goffset bytes_sent; /* The number of bytes sent. */
+ goffset bytes_remaining; /* The number of bytes remaining. */
+ time_t start_time; /* When the transfer of data began. */
+ time_t 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 prpl 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;
+
+ PurpleXferUiOps *ui_ops; /* UI-specific operations. */
+
+ /* TODO Remove this and use protocol-specific subclasses. */
+ void *proto_data; /* prpl-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/prpl call the respective
+ * function, which is somewhat akin to a fd watch being triggered.
+ */
+ enum {
+ PURPLE_XFER_READY_NONE = 0x0,
+ PURPLE_XFER_READY_UI = 0x1,
+ PURPLE_XFER_READY_PRPL = 0x2,
+ } ready;
+
+ /* TODO: Should really use a PurpleCircBuffer for this. */
+ GByteArray *buffer;
+
+ gpointer thumbnail_data; /* thumbnail image */
+ gsize thumbnail_size;
+ gchar *thumbnail_mimetype;
+};
+
+/* GObject property enums */
+enum
+{
+ PROP_0,
+ PROP_TYPE,
+ PROP_ACCOUNT,
+ PROP_REMOTE_USER,
+ PROP_MESSAGE,
+ PROP_FILENAME,
+ PROP_LOCAL_FILENAME,
+ PROP_FILE_SIZE,
+ PROP_REMOTE_IP,
+ PROP_LOCAL_PORT,
+ PROP_REMOTE_PORT,
+ PROP_FD,
+ PROP_WATCHER,
+ PROP_BYTES_SENT,
+ PROP_START_TIME,
+ PROP_END_TIME,
+ PROP_STATUS,
+ PROP_LAST
+};
+
+static GObjectClass *parent_class;
+static GParamSpec *properties[PROP_LAST];
+
+static int purple_xfer_choose_file(PurpleXfer *xfer);
+
+static const gchar *
+purple_xfer_status_type_to_string(PurpleXferStatus type)
+{
+ static const struct {
+ PurpleXferStatus type;
+ const char *name;
+ } type_names[] = {
+ { PURPLE_XFER_STATUS_UNKNOWN, "unknown" },
+ { PURPLE_XFER_STATUS_NOT_STARTED, "not started" },
+ { PURPLE_XFER_STATUS_ACCEPTED, "accepted" },
+ { PURPLE_XFER_STATUS_STARTED, "started" },
+ { PURPLE_XFER_STATUS_DONE, "done" },
+ { PURPLE_XFER_STATUS_CANCEL_LOCAL, "cancelled locally" },
+ { PURPLE_XFER_STATUS_CANCEL_REMOTE, "cancelled remotely" }
+ };
+ gsize i;
+
+ for (i = 0; i < G_N_ELEMENTS(type_names); ++i)
+ if (type_names[i].type == type)
+ return type_names[i].name;
+
+ return "invalid state";
+}
+
+void
+purple_xfer_set_status(PurpleXfer *xfer, PurpleXferStatus status)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_if_fail(priv != NULL);
+
+ if (purple_debug_is_verbose())
+ purple_debug_info("xfer", "Changing status of xfer %p from %s to %s\n",
+ xfer, purple_xfer_status_type_to_string(priv->status),
+ purple_xfer_status_type_to_string(status));
+
+ if (priv->status == status)
+ return;
+
+ priv->status = status;
+
+ g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_STATUS]);
+
+ if(priv->type == PURPLE_XFER_TYPE_SEND) {
+ switch(status) {
+ case PURPLE_XFER_STATUS_ACCEPTED:
+ purple_signal_emit(purple_xfers_get_handle(), "file-send-accept", xfer);
+ break;
+ case PURPLE_XFER_STATUS_STARTED:
+ purple_signal_emit(purple_xfers_get_handle(), "file-send-start", xfer);
+ break;
+ case PURPLE_XFER_STATUS_DONE:
+ purple_signal_emit(purple_xfers_get_handle(), "file-send-complete", xfer);
+ break;
+ case PURPLE_XFER_STATUS_CANCEL_LOCAL:
+ case PURPLE_XFER_STATUS_CANCEL_REMOTE:
+ purple_signal_emit(purple_xfers_get_handle(), "file-send-cancel", xfer);
+ break;
+ default:
+ break;
+ }
+ } else if(priv->type == PURPLE_XFER_TYPE_RECEIVE) {
+ switch(status) {
+ case PURPLE_XFER_STATUS_ACCEPTED:
+ purple_signal_emit(purple_xfers_get_handle(), "file-recv-accept", xfer);
+ break;
+ case PURPLE_XFER_STATUS_STARTED:
+ purple_signal_emit(purple_xfers_get_handle(), "file-recv-start", xfer);
+ break;
+ case PURPLE_XFER_STATUS_DONE:
+ purple_signal_emit(purple_xfers_get_handle(), "file-recv-complete", xfer);
+ break;
+ case PURPLE_XFER_STATUS_CANCEL_LOCAL:
+ case PURPLE_XFER_STATUS_CANCEL_REMOTE:
+ purple_signal_emit(purple_xfers_get_handle(), "file-recv-cancel", xfer);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void
+purple_xfer_conversation_write_internal(PurpleXfer *xfer,
+ const char *message, gboolean is_error, gboolean print_thumbnail)
+{
+ PurpleIMConversation *im = NULL;
+ PurpleMessageFlags flags = PURPLE_MESSAGE_SYSTEM;
+ char *escaped;
+ gconstpointer thumbnail_data;
+ gsize size;
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_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,
+ purple_xfer_get_account(xfer));
+
+ if (im == NULL)
+ return;
+
+ escaped = g_markup_escape_text(message, -1);
+
+ if (is_error)
+ flags |= PURPLE_MESSAGE_ERROR;
+
+ if (print_thumbnail && thumbnail_data) {
+ gchar *message_with_img;
+ gpointer data = g_memdup(thumbnail_data, size);
+ PurpleImage *img;
+ guint id;
+
+ img = purple_image_new_from_data(data, size);
+ id = purple_image_store_add(img);
+ g_object_unref(img);
+
+ message_with_img = g_strdup_printf("<img src=\""
+ PURPLE_IMAGE_STORE_PROTOCOL "%u\"> %s", id, escaped);
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(im), message_with_img, flags);
+ g_free(message_with_img);
+ } else {
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(im), escaped, flags);
+ }
+ g_free(escaped);
+}
+
+void
+purple_xfer_conversation_write(PurpleXfer *xfer, const gchar *message,
+ gboolean is_error)
+{
+ purple_xfer_conversation_write_internal(xfer, message, is_error, FALSE);
+}
+
+/* maybe this one should be exported publically? */
+static void
+purple_xfer_conversation_write_with_thumbnail(PurpleXfer *xfer,
+ const gchar *message)
+{
+ purple_xfer_conversation_write_internal(xfer, message, FALSE, TRUE);
+}
+
+
+static void purple_xfer_show_file_error(PurpleXfer *xfer, const char *filename)
+{
+ int err = errno;
+ gchar *msg = NULL, *utf8;
+ PurpleXferType xfer_type = purple_xfer_get_xfer_type(xfer);
+ PurpleAccount *account = purple_xfer_get_account(xfer);
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
+ switch(xfer_type) {
+ case PURPLE_XFER_TYPE_SEND:
+ msg = g_strdup_printf(_("Error reading %s: \n%s.\n"),
+ utf8, g_strerror(err));
+ break;
+ case PURPLE_XFER_TYPE_RECEIVE:
+ msg = g_strdup_printf(_("Error writing %s: \n%s.\n"),
+ utf8, g_strerror(err));
+ break;
+ default:
+ msg = g_strdup_printf(_("Error accessing %s: \n%s.\n"),
+ utf8, g_strerror(err));
+ break;
+ }
+ g_free(utf8);
+
+ purple_xfer_conversation_write(xfer, msg, TRUE);
+ purple_xfer_error(xfer_type, account, priv->who, msg);
+ g_free(msg);
+}
+
+static void
+purple_xfer_choose_file_ok_cb(void *user_data, const char *filename)
+{
+ PurpleXfer *xfer;
+ PurpleXferType type;
+ GStatBuf st;
+ gchar *dir;
+
+ xfer = (PurpleXfer *)user_data;
+ type = purple_xfer_get_xfer_type(xfer);
+
+ if (g_stat(filename, &st) != 0) {
+ /* File not found. */
+ if (type == PURPLE_XFER_TYPE_RECEIVE) {
+#ifndef _WIN32
+ int mode = W_OK;
+#else
+ int mode = F_OK;
+#endif
+ dir = g_path_get_dirname(filename);
+
+ if (g_access(dir, mode) == 0) {
+ purple_xfer_request_accepted(xfer, filename);
+ } else {
+ g_object_ref(xfer);
+ purple_notify_message(
+ NULL, PURPLE_NOTIFY_MSG_ERROR, NULL,
+ _("Directory is not writable."), NULL,
+ purple_request_cpar_from_account(
+ purple_xfer_get_account(xfer)),
+ (PurpleNotifyCloseCallback)purple_xfer_choose_file, xfer);
+ }
+
+ g_free(dir);
+ }
+ else {
+ purple_xfer_show_file_error(xfer, filename);
+ purple_xfer_cancel_local(xfer);
+ }
+ }
+ else if ((type == PURPLE_XFER_TYPE_SEND) && (st.st_size == 0)) {
+
+ purple_notify_error(NULL, NULL,
+ _("Cannot send a file of 0 bytes."), NULL,
+ purple_request_cpar_from_account(
+ purple_xfer_get_account(xfer)));
+
+ purple_xfer_cancel_local(xfer);
+ }
+ else if ((type == PURPLE_XFER_TYPE_SEND) && S_ISDIR(st.st_mode)) {
+ /*
+ * XXX - Sending a directory should be valid for some protocols.
+ */
+ purple_notify_error(NULL, NULL, _("Cannot send a directory."),
+ NULL, purple_request_cpar_from_account(
+ purple_xfer_get_account(xfer)));
+
+ purple_xfer_cancel_local(xfer);
+ }
+ else if ((type == PURPLE_XFER_TYPE_RECEIVE) && S_ISDIR(st.st_mode)) {
+ char *msg, *utf8;
+ utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
+ msg = g_strdup_printf(
+ _("%s is not a regular file. Cowardly refusing to overwrite it.\n"), utf8);
+ g_free(utf8);
+ purple_notify_error(NULL, NULL, msg, NULL,
+ purple_request_cpar_from_account(
+ purple_xfer_get_account(xfer)));
+ g_free(msg);
+ purple_xfer_request_denied(xfer);
+ }
+ else if (type == PURPLE_XFER_TYPE_SEND) {
+#ifndef _WIN32
+ int mode = R_OK;
+#else
+ int mode = F_OK;
+#endif
+
+ if (g_access(filename, mode) == 0) {
+ purple_xfer_request_accepted(xfer, filename);
+ } else {
+ g_object_ref(xfer);
+ purple_notify_message(
+ NULL, PURPLE_NOTIFY_MSG_ERROR, NULL,
+ _("File is not readable."), NULL,
+ purple_request_cpar_from_account(
+ purple_xfer_get_account(xfer)),
+ (PurpleNotifyCloseCallback)purple_xfer_choose_file, xfer);
+ }
+ }
+ else {
+ purple_xfer_request_accepted(xfer, filename);
+ }
+
+ g_object_unref(xfer);
+}
+
+static void
+purple_xfer_choose_file_cancel_cb(void *user_data, const char *filename)
+{
+ PurpleXfer *xfer = (PurpleXfer *)user_data;
+
+ purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL);
+ if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND)
+ purple_xfer_cancel_local(xfer);
+ else
+ purple_xfer_request_denied(xfer);
+ g_object_unref(xfer);
+}
+
+static int
+purple_xfer_choose_file(PurpleXfer *xfer)
+{
+ purple_request_file(xfer, NULL, purple_xfer_get_filename(xfer),
+ (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE),
+ G_CALLBACK(purple_xfer_choose_file_ok_cb),
+ G_CALLBACK(purple_xfer_choose_file_cancel_cb),
+ purple_request_cpar_from_account(purple_xfer_get_account(xfer)),
+ xfer);
+
+ return 0;
+}
+
+static int
+cancel_recv_cb(PurpleXfer *xfer)
+{
+ purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL);
+ purple_xfer_request_denied(xfer);
+ g_object_unref(xfer);
+
+ return 0;
+}
+
+static void
+purple_xfer_ask_recv(PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+ char *buf, *size_buf;
+ goffset size;
+ gconstpointer thumb;
+ gsize thumb_size;
+
+ /* If we have already accepted the request, ask the destination file
+ name directly */
+ if (purple_xfer_get_status(xfer) != PURPLE_XFER_STATUS_ACCEPTED) {
+ PurpleRequestCommonParameters *cpar;
+ PurpleBuddy *buddy = purple_blist_find_buddy(priv->account, priv->who);
+
+ if (purple_xfer_get_filename(xfer) != NULL)
+ {
+ size = purple_xfer_get_size(xfer);
+ size_buf = purple_str_size_to_units(size);
+ 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);
+ g_free(size_buf);
+ }
+ else
+ {
+ buf = g_strdup_printf(_("%s wants to send you a file"),
+ buddy ? purple_buddy_get_alias(buddy) : priv->who);
+ }
+
+ if (priv->message != NULL)
+ purple_serv_got_im(purple_account_get_connection(priv->account),
+ priv->who, priv->message, 0, time(NULL));
+
+ cpar = purple_request_cpar_from_account(priv->account);
+ if ((thumb = purple_xfer_get_thumbnail(xfer, &thumb_size))) {
+ purple_request_cpar_set_custom_icon(cpar, thumb,
+ thumb_size);
+ }
+
+ purple_request_accept_cancel(xfer, NULL, buf, NULL,
+ PURPLE_DEFAULT_ACTION_NONE, cpar, xfer,
+ G_CALLBACK(purple_xfer_choose_file),
+ G_CALLBACK(cancel_recv_cb));
+
+ g_free(buf);
+ } else
+ purple_xfer_choose_file(xfer);
+}
+
+static int
+ask_accept_ok(PurpleXfer *xfer)
+{
+ purple_xfer_request_accepted(xfer, NULL);
+
+ return 0;
+}
+
+static int
+ask_accept_cancel(PurpleXfer *xfer)
+{
+ purple_xfer_request_denied(xfer);
+ g_object_unref(xfer);
+
+ return 0;
+}
+
+static void
+purple_xfer_ask_accept(PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+ char *buf, *buf2 = NULL;
+ PurpleBuddy *buddy = purple_blist_find_buddy(priv->account, priv->who);
+
+ buf = g_strdup_printf(_("Accept file transfer request from %s?"),
+ buddy ? purple_buddy_get_alias(buddy) : priv->who);
+ if (purple_xfer_get_remote_ip(xfer) &&
+ purple_xfer_get_remote_port(xfer))
+ buf2 = g_strdup_printf(_("A file is available for download from:\n"
+ "Remote host: %s\nRemote port: %d"),
+ purple_xfer_get_remote_ip(xfer),
+ purple_xfer_get_remote_port(xfer));
+ purple_request_accept_cancel(xfer, NULL, buf, buf2,
+ PURPLE_DEFAULT_ACTION_NONE,
+ purple_request_cpar_from_account(priv->account), xfer,
+ G_CALLBACK(ask_accept_ok), G_CALLBACK(ask_accept_cancel));
+ g_free(buf);
+ g_free(buf2);
+}
+
+void
+purple_xfer_request(PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_if_fail(priv != NULL);
+ g_return_if_fail(priv->ops.init != NULL);
+
+ g_object_ref(xfer);
+
+ if (purple_xfer_get_xfer_type(xfer) == 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)
+ {
+ /* 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)
+ {
+ gchar* message = NULL;
+ PurpleBuddy *buddy = purple_blist_find_buddy(priv->account, priv->who);
+
+ message = g_strdup_printf(_("%s is offering to send file %s"),
+ buddy ? purple_buddy_get_alias(buddy) : priv->who, purple_xfer_get_filename(xfer));
+ purple_xfer_conversation_write_with_thumbnail(xfer, message);
+ g_free(message);
+
+ /* Ask for a filename to save to if it's not already given by a plugin */
+ if (priv->local_filename == NULL)
+ purple_xfer_ask_recv(xfer);
+ }
+ else
+ {
+ purple_xfer_ask_accept(xfer);
+ }
+ }
+ else
+ {
+ purple_xfer_choose_file(xfer);
+ }
+}
+
+void
+purple_xfer_request_accepted(PurpleXfer *xfer, const char *filename)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+ PurpleXferType type;
+ GStatBuf st;
+ char *msg, *utf8, *base;
+ PurpleAccount *account;
+ PurpleBuddy *buddy;
+
+ if (priv == NULL)
+ return;
+
+ type = purple_xfer_get_xfer_type(xfer);
+ account = purple_xfer_get_account(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);
+ return;
+ } else {
+ g_return_if_fail(filename != NULL);
+ }
+
+ buddy = purple_blist_find_buddy(account, priv->who);
+
+ if (type == PURPLE_XFER_TYPE_SEND) {
+ /* Sending a file */
+ /* Check the filename. */
+ PurpleXferUiOps *ui_ops;
+ ui_ops = purple_xfer_get_ui_ops(xfer);
+
+#ifdef _WIN32
+ if (g_strrstr(filename, "../") || g_strrstr(filename, "..\\"))
+#else
+ if (g_strrstr(filename, "../"))
+#endif
+ {
+ 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);
+ g_free(utf8);
+ g_free(msg);
+
+ g_object_unref(xfer);
+ return;
+ }
+
+ if (ui_ops == NULL || (ui_ops->ui_read == NULL && ui_ops->ui_write == NULL)) {
+ if (g_stat(filename, &st) == -1) {
+ purple_xfer_show_file_error(xfer, filename);
+ g_object_unref(xfer);
+ return;
+ }
+
+ purple_xfer_set_local_filename(xfer, filename);
+ purple_xfer_set_size(xfer, st.st_size);
+ } else {
+ purple_xfer_set_local_filename(xfer, filename);
+ }
+
+ base = g_path_get_basename(filename);
+ utf8 = g_filename_to_utf8(base, -1, NULL, NULL, NULL);
+ g_free(base);
+ purple_xfer_set_filename(xfer, utf8);
+
+ msg = g_strdup_printf(_("Offering to send %s to %s"),
+ utf8, buddy ? purple_buddy_get_alias(buddy) : priv->who);
+ g_free(utf8);
+ purple_xfer_conversation_write(xfer, msg, FALSE);
+ g_free(msg);
+ }
+ else {
+ /* Receiving a file */
+ purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_ACCEPTED);
+ purple_xfer_set_local_filename(xfer, filename);
+
+ msg = g_strdup_printf(_("Starting transfer of %s from %s"),
+ priv->filename, buddy ? purple_buddy_get_alias(buddy) : priv->who);
+ purple_xfer_conversation_write(xfer, msg, FALSE);
+ g_free(msg);
+ }
+
+ purple_xfer_add(xfer);
+ priv->ops.init(xfer);
+
+}
+
+void
+purple_xfer_request_denied(PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_if_fail(priv != NULL);
+
+ purple_debug_misc("xfer", "xfer %p denied\n", xfer);
+
+ if (priv->ops.request_denied != NULL)
+ priv->ops.request_denied(xfer);
+
+ g_object_unref(xfer);
+}
+
+int purple_xfer_get_fd(PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return priv->fd;
+}
+
+int purple_xfer_get_watcher(PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return priv->watcher;
+}
+
+PurpleXferType
+purple_xfer_get_xfer_type(const PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_val_if_fail(priv != NULL, PURPLE_XFER_TYPE_UNKNOWN);
+
+ return priv->type;
+}
+
+PurpleAccount *
+purple_xfer_get_account(const PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->account;
+}
+
+void
+purple_xfer_set_remote_user(PurpleXfer *xfer, const char *who)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_if_fail(priv != NULL);
+
+ g_free(priv->who);
+ priv->who = g_strdup(who);
+
+ g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_REMOTE_USER]);
+}
+
+const char *
+purple_xfer_get_remote_user(const PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->who;
+}
+
+PurpleXferStatus
+purple_xfer_get_status(const PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_val_if_fail(priv != NULL, PURPLE_XFER_STATUS_UNKNOWN);
+
+ return priv->status;
+}
+
+gboolean
+purple_xfer_is_cancelled(const PurpleXfer *xfer)
+{
+ g_return_val_if_fail(PURPLE_IS_XFER(xfer), TRUE);
+
+ if ((purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) ||
+ (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_REMOTE))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+gboolean
+purple_xfer_is_completed(const PurpleXfer *xfer)
+{
+ g_return_val_if_fail(PURPLE_IS_XFER(xfer), TRUE);
+
+ return (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_DONE);
+}
+
+const char *
+purple_xfer_get_filename(const PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->filename;
+}
+
+const char *
+purple_xfer_get_local_filename(const PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->local_filename;
+}
+
+goffset
+purple_xfer_get_bytes_sent(const PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return priv->bytes_sent;
+}
+
+goffset
+purple_xfer_get_bytes_remaining(const PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return priv->bytes_remaining;
+}
+
+goffset
+purple_xfer_get_size(const PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return priv->size;
+}
+
+double
+purple_xfer_get_progress(const PurpleXfer *xfer)
+{
+ g_return_val_if_fail(PURPLE_IS_XFER(xfer), 0.0);
+
+ if (purple_xfer_get_size(xfer) == 0)
+ return 0.0;
+
+ return ((double)purple_xfer_get_bytes_sent(xfer) /
+ (double)purple_xfer_get_size(xfer));
+}
+
+guint16
+purple_xfer_get_local_port(const PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_val_if_fail(priv != NULL, -1);
+
+ return priv->local_port;
+}
+
+const char *
+purple_xfer_get_remote_ip(const PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->remote_ip;
+}
+
+guint16
+purple_xfer_get_remote_port(const PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_val_if_fail(priv != NULL, -1);
+
+ return priv->remote_port;
+}
+
+time_t
+purple_xfer_get_start_time(const PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return priv->start_time;
+}
+
+time_t
+purple_xfer_get_end_time(const PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return priv->end_time;
+}
+
+void purple_xfer_set_fd(PurpleXfer *xfer, int fd)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->fd = fd;
+
+ g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_FD]);
+}
+
+void purple_xfer_set_watcher(PurpleXfer *xfer, int watcher)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->watcher = watcher;
+
+ g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_WATCHER]);
+}
+
+void
+purple_xfer_set_completed(PurpleXfer *xfer, gboolean completed)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+ PurpleXferUiOps *ui_ops;
+
+ g_return_if_fail(priv != NULL);
+
+ if (completed == TRUE) {
+ char *msg = NULL;
+ PurpleIMConversation *im;
+
+ purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_DONE);
+
+ if (purple_xfer_get_filename(xfer) != NULL)
+ {
+ char *filename = g_markup_escape_text(purple_xfer_get_filename(xfer), -1);
+ if (purple_xfer_get_local_filename(xfer)
+ && purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE)
+ {
+ char *local = g_markup_escape_text(purple_xfer_get_local_filename(xfer), -1);
+ msg = g_strdup_printf(_("Transfer of file <A HREF=\"file://%s\">%s</A> complete"),
+ local, filename);
+ g_free(local);
+ }
+ else
+ msg = g_strdup_printf(_("Transfer of file %s complete"),
+ filename);
+ g_free(filename);
+ }
+ else
+ msg = g_strdup(_("File transfer complete"));
+
+ im = purple_conversations_find_im_with_account(priv->who,
+ purple_xfer_get_account(xfer));
+
+ if (im != NULL) {
+ purple_conversation_write_system_message(
+ PURPLE_CONVERSATION(im), msg, 0);
+ }
+ g_free(msg);
+ }
+
+ ui_ops = purple_xfer_get_ui_ops(xfer);
+
+ if (ui_ops != NULL && ui_ops->update_progress != NULL)
+ ui_ops->update_progress(xfer, purple_xfer_get_progress(xfer));
+}
+
+void
+purple_xfer_set_message(PurpleXfer *xfer, const char *message)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_if_fail(priv != NULL);
+
+ g_free(priv->message);
+ priv->message = g_strdup(message);
+
+ g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_MESSAGE]);
+}
+
+const char *
+purple_xfer_get_message(const PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->message;
+}
+
+void
+purple_xfer_set_filename(PurpleXfer *xfer, const char *filename)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_if_fail(priv != NULL);
+
+ g_free(priv->filename);
+ priv->filename = g_strdup(filename);
+
+ g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_FILENAME]);
+}
+
+void
+purple_xfer_set_local_filename(PurpleXfer *xfer, const char *filename)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_if_fail(priv != NULL);
+
+ g_free(priv->local_filename);
+ priv->local_filename = g_strdup(filename);
+
+ g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_LOCAL_FILENAME]);
+}
+
+void
+purple_xfer_set_size(PurpleXfer *xfer, goffset size)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->size = size;
+ priv->bytes_remaining = priv->size - purple_xfer_get_bytes_sent(xfer);
+
+ g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_FILE_SIZE]);
+}
+
+void
+purple_xfer_set_local_port(PurpleXfer *xfer, guint16 local_port)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->local_port = local_port;
+
+ g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_LOCAL_PORT]);
+}
+
+void
+purple_xfer_set_bytes_sent(PurpleXfer *xfer, goffset bytes_sent)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->bytes_sent = bytes_sent;
+
+ if (purple_xfer_get_size(xfer) > 0)
+ priv->bytes_remaining = purple_xfer_get_size(xfer) - bytes_sent;
+
+ g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_BYTES_SENT]);
+}
+
+PurpleXferUiOps *
+purple_xfer_get_ui_ops(const PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->ui_ops;
+}
+
+void
+purple_xfer_set_init_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *))
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_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_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_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_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_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_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_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_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_PRIVATE(xfer);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->ops.cancel_recv = fnc;
+}
+
+static void
+purple_xfer_increase_buffer_size(PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ priv->current_buffer_size = MIN(priv->current_buffer_size * 1.5,
+ FT_MAX_BUFFER_SIZE);
+}
+
+gssize
+purple_xfer_read(PurpleXfer *xfer, guchar **buffer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+ gssize s, r;
+
+ g_return_val_if_fail(priv != NULL, 0);
+ g_return_val_if_fail(buffer != NULL, 0);
+
+ 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 {
+ *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;
+ }
+
+ if (r >= 0 && (gsize)r == priv->current_buffer_size)
+ /*
+ * We managed to read the entire buffer. This means our this
+ * network is fast and our buffer is too small, so make it
+ * bigger.
+ */
+ purple_xfer_increase_buffer_size(xfer);
+
+ return r;
+}
+
+gssize
+purple_xfer_write(PurpleXfer *xfer, const guchar *buffer, gsize size)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+ gssize r, s;
+
+ g_return_val_if_fail(priv != NULL, 0);
+ g_return_val_if_fail(buffer != NULL, 0);
+ g_return_val_if_fail(size != 0, 0);
+
+ s = MIN((gssize)purple_xfer_get_bytes_remaining(xfer), (gssize)size);
+
+ if (priv->ops.write != NULL) {
+ r = (priv->ops.write)(buffer, s, xfer);
+ } else {
+ r = write(priv->fd, buffer, s);
+ if (r < 0 && errno == EAGAIN)
+ r = 0;
+ }
+ if (r >= 0 && (purple_xfer_get_bytes_sent(xfer)+r) >= purple_xfer_get_size(xfer) &&
+ !purple_xfer_is_completed(xfer))
+ purple_xfer_set_completed(xfer, TRUE);
+
+
+ return r;
+}
+
+gboolean
+purple_xfer_write_file(PurpleXfer *xfer, const guchar *buffer, gsize size)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+ PurpleXferUiOps *ui_ops;
+ gsize wc;
+ gboolean fs_known;
+
+ g_return_val_if_fail(priv != NULL, FALSE);
+ g_return_val_if_fail(buffer != NULL, FALSE);
+
+ 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)) {
+ 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);
+ }
+
+ if (ui_ops && ui_ops->ui_write)
+ wc = ui_ops->ui_write(xfer, buffer, size);
+ else {
+ if (priv->dest_fp == NULL) {
+ purple_debug_error("xfer",
+ "File is not opened for writing\n");
+ purple_xfer_cancel_local(xfer);
+ return FALSE;
+ }
+ wc = fwrite(buffer, 1, size, priv->dest_fp);
+ }
+
+ if (wc != size) {
+ purple_debug_error("xfer",
+ "Unable to write whole buffer.\n");
+ purple_xfer_cancel_local(xfer);
+ return FALSE;
+ }
+
+ purple_xfer_set_bytes_sent(xfer, purple_xfer_get_bytes_sent(xfer) +
+ size);
+
+ return TRUE;
+}
+
+gssize
+purple_xfer_read_file(PurpleXfer *xfer, guchar *buffer, gsize size)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+ PurpleXferUiOps *ui_ops;
+ gssize got_len;
+
+ g_return_val_if_fail(priv != NULL, FALSE);
+ g_return_val_if_fail(buffer != NULL, FALSE);
+
+ ui_ops = purple_xfer_get_ui_ops(xfer);
+
+ if (ui_ops && ui_ops->ui_read) {
+ guchar *buffer_got = NULL;
+
+ got_len = ui_ops->ui_read(xfer, &buffer_got, size);
+
+ if (got_len >= 0 && (gsize)got_len > size) {
+ g_free(buffer_got);
+ purple_debug_error("xfer",
+ "Got too much data from UI.\n");
+ purple_xfer_cancel_local(xfer);
+ return -1;
+ }
+
+ if (got_len > 0)
+ memcpy(buffer, buffer_got, got_len);
+ g_free(buffer_got);
+ } else {
+ if (priv->dest_fp == NULL) {
+ purple_debug_error("xfer",
+ "File is not opened for reading\n");
+ purple_xfer_cancel_local(xfer);
+ return -1;
+ }
+ got_len = fread(buffer, size, 1, priv->dest_fp);
+ if ((got_len < 0 || (gsize)got_len != size) &&
+ ferror(priv->dest_fp))
+ {
+ purple_debug_error("xfer",
+ "Unable to read file.\n");
+ purple_xfer_cancel_local(xfer);
+ return -1;
+ }
+ }
+
+ if (got_len > 0) {
+ purple_xfer_set_bytes_sent(xfer,
+ purple_xfer_get_bytes_sent(xfer) + got_len);
+ }
+
+ return got_len;
+}
+
+static void
+do_transfer(PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+ PurpleXferUiOps *ui_ops;
+ guchar *buffer = NULL;
+ gssize r = 0;
+
+ ui_ops = purple_xfer_get_ui_ops(xfer);
+
+ if (priv->type == PURPLE_XFER_TYPE_RECEIVE) {
+ r = purple_xfer_read(xfer, &buffer);
+ if (r > 0) {
+ if (!purple_xfer_write_file(xfer, buffer, r)) {
+ g_free(buffer);
+ return;
+ }
+
+ if ((purple_xfer_get_size(xfer) > 0) &&
+ ((purple_xfer_get_bytes_sent(xfer)+r) >= purple_xfer_get_size(xfer)))
+ purple_xfer_set_completed(xfer, TRUE);
+ } else if(r < 0) {
+ purple_xfer_cancel_remote(xfer);
+ g_free(buffer);
+ return;
+ }
+ } 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);
+ gboolean read = TRUE;
+
+ /* this is so the prpl can keep the connection open
+ if it needs to for some odd reason. */
+ if (s == 0) {
+ if (priv->watcher) {
+ purple_input_remove(priv->watcher);
+ purple_xfer_set_watcher(xfer, 0);
+ }
+ return;
+ }
+
+ if (priv->buffer) {
+ if (priv->buffer->len < s) {
+ s -= priv->buffer->len;
+ read = TRUE;
+ } else {
+ read = FALSE;
+ }
+ }
+
+ if (read) {
+ buffer = g_new(guchar, s);
+ result = purple_xfer_read_file(xfer, buffer, s);
+ if (result == 0) {
+ /*
+ * The UI claimed it was ready, but didn't have any data for
+ * us... It will call purple_xfer_ui_ready when ready, which
+ * sets back up this watcher.
+ */
+ if (priv->watcher != 0) {
+ purple_input_remove(priv->watcher);
+ purple_xfer_set_watcher(xfer, 0);
+ }
+
+ /* Need to indicate the prpl is still ready... */
+ priv->ready |= PURPLE_XFER_READY_PRPL;
+
+ g_return_if_reached();
+ }
+ if (result < 0)
+ return;
+ }
+
+ if (priv->buffer) {
+ g_byte_array_append(priv->buffer, buffer, result);
+ g_free(buffer);
+ buffer = priv->buffer->data;
+ result = priv->buffer->len;
+ }
+
+ r = purple_xfer_write(xfer, buffer, result);
+
+ if (r == -1) {
+ purple_xfer_cancel_remote(xfer);
+ if (!priv->buffer)
+ /* We don't free buffer if priv->buffer is set, because in
+ that case buffer doesn't belong to us. */
+ g_free(buffer);
+ return;
+ } else if (r == result) {
+ /*
+ * We managed to write 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);
+ } else {
+ if (ui_ops && ui_ops->data_not_sent)
+ ui_ops->data_not_sent(xfer, buffer + r, result - r);
+ }
+
+ if (priv->buffer) {
+ /*
+ * Remove what we wrote
+ * If we wrote the whole buffer the byte array will be empty
+ * Otherwise we'll keep what wasn't sent for next time.
+ */
+ buffer = NULL;
+ g_byte_array_remove_range(priv->buffer, 0, r);
+ }
+ }
+
+ if (r > 0) {
+ purple_xfer_set_bytes_sent(xfer, priv->bytes_sent + r);
+
+ if (priv->ops.ack != NULL)
+ priv->ops.ack(xfer, buffer, r);
+
+ g_free(buffer);
+
+ if (ui_ops != NULL && ui_ops->update_progress != NULL)
+ ui_ops->update_progress(xfer,
+ purple_xfer_get_progress(xfer));
+ }
+
+ if (purple_xfer_is_completed(xfer))
+ purple_xfer_end(xfer);
+}
+
+static void
+transfer_cb(gpointer data, gint source, PurpleInputCondition condition)
+{
+ PurpleXfer *xfer = data;
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ if (priv->dest_fp == NULL) {
+ /* The UI is moderating its side manually */
+ if (0 == (priv->ready & PURPLE_XFER_READY_UI)) {
+ priv->ready |= PURPLE_XFER_READY_PRPL;
+
+ purple_input_remove(priv->watcher);
+ purple_xfer_set_watcher(xfer, 0);
+
+ purple_debug_misc("xfer", "prpl is ready on ft %p, waiting for UI\n", xfer);
+ return;
+ }
+
+ priv->ready = PURPLE_XFER_READY_NONE;
+ }
+
+ do_transfer(xfer);
+}
+
+static void
+begin_transfer(PurpleXfer *xfer, PurpleInputCondition cond)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+ PurpleXferType type = purple_xfer_get_xfer_type(xfer);
+ PurpleXferUiOps *ui_ops = purple_xfer_get_ui_ops(xfer);
+
+ if (priv->start_time != 0) {
+ purple_debug_error("xfer", "Transfer is being started multiple times\n");
+ g_return_if_reached();
+ }
+
+ 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");
+
+ if (priv->dest_fp == NULL) {
+ purple_xfer_show_file_error(xfer, purple_xfer_get_local_filename(xfer));
+ purple_xfer_cancel_local(xfer);
+ return;
+ }
+
+ if (fseek(priv->dest_fp, priv->bytes_sent, SEEK_SET) != 0) {
+ purple_debug_error("xfer", "couldn't seek\n");
+ purple_xfer_show_file_error(xfer, purple_xfer_get_local_filename(xfer));
+ purple_xfer_cancel_local(xfer);
+ return;
+ }
+ }
+
+ if (priv->fd != -1)
+ purple_xfer_set_watcher(xfer,
+ purple_input_add(priv->fd, cond, transfer_cb, xfer));
+
+ priv->start_time = time(NULL);
+
+ g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_START_TIME]);
+
+ if (priv->ops.start != NULL)
+ priv->ops.start(xfer);
+}
+
+static void
+connect_cb(gpointer data, gint source, const gchar *error_message)
+{
+ PurpleXfer *xfer = PURPLE_XFER(data);
+
+ if (source < 0) {
+ purple_xfer_cancel_local(xfer);
+ return;
+ }
+
+ purple_xfer_set_fd(xfer, source);
+ begin_transfer(xfer, PURPLE_INPUT_READ);
+}
+
+void
+purple_xfer_ui_ready(PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+ PurpleInputCondition cond;
+ PurpleXferType type;
+
+ g_return_if_fail(priv != NULL);
+
+ priv->ready |= PURPLE_XFER_READY_UI;
+
+ if (0 == (priv->ready & PURPLE_XFER_READY_PRPL)) {
+ purple_debug_misc("xfer", "UI is ready on ft %p, waiting for prpl\n", xfer);
+ return;
+ }
+
+ purple_debug_misc("xfer", "UI (and prpl) ready on ft %p, so proceeding\n", xfer);
+
+ type = purple_xfer_get_xfer_type(xfer);
+ if (type == PURPLE_XFER_TYPE_SEND)
+ cond = PURPLE_INPUT_WRITE;
+ else /* if (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));
+
+ priv->ready = PURPLE_XFER_READY_NONE;
+
+ do_transfer(xfer);
+}
+
+void
+purple_xfer_prpl_ready(PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->ready |= PURPLE_XFER_READY_PRPL;
+
+ /* I don't think fwrite/fread are ever *not* ready */
+ if (priv->dest_fp == NULL && 0 == (priv->ready & PURPLE_XFER_READY_UI)) {
+ purple_debug_misc("xfer", "prpl is ready on ft %p, waiting for UI\n", xfer);
+ return;
+ }
+
+ purple_debug_misc("xfer", "Prpl (and UI) ready on ft %p, so proceeding\n", xfer);
+
+ priv->ready = PURPLE_XFER_READY_NONE;
+
+ do_transfer(xfer);
+}
+
+void
+purple_xfer_start(PurpleXfer *xfer, int fd, const char *ip, guint16 port)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+ 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);
+
+ purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_STARTED);
+
+ if (type == PURPLE_XFER_TYPE_RECEIVE) {
+ cond = PURPLE_INPUT_READ;
+
+ if (ip != NULL) {
+ priv->remote_ip = g_strdup(ip);
+ priv->remote_port = port;
+
+ obj = G_OBJECT(xfer);
+ g_object_freeze_notify(obj);
+ g_object_notify_by_pspec(obj, properties[PROP_REMOTE_IP]);
+ g_object_notify_by_pspec(obj, properties[PROP_REMOTE_PORT]);
+ g_object_thaw_notify(obj);
+
+ /* Establish a file descriptor. */
+ purple_proxy_connect(NULL, priv->account, priv->remote_ip,
+ priv->remote_port, connect_cb, xfer);
+
+ return;
+ }
+ else {
+ purple_xfer_set_fd(xfer, fd);
+ }
+ }
+ else {
+ cond = PURPLE_INPUT_WRITE;
+
+ purple_xfer_set_fd(xfer, fd);
+ }
+
+ begin_transfer(xfer, cond);
+}
+
+void
+purple_xfer_end(PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_if_fail(priv != NULL);
+
+ /* See if we are actually trying to cancel this. */
+ if (!purple_xfer_is_completed(xfer)) {
+ purple_xfer_cancel_local(xfer);
+ return;
+ }
+
+ priv->end_time = time(NULL);
+
+ g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_END_TIME]);
+
+ if (priv->ops.end != NULL)
+ priv->ops.end(xfer);
+
+ if (priv->watcher != 0) {
+ purple_input_remove(priv->watcher);
+ purple_xfer_set_watcher(xfer, 0);
+ }
+
+ if (priv->fd != -1)
+ close(priv->fd);
+
+ if (priv->dest_fp != NULL) {
+ fclose(priv->dest_fp);
+ priv->dest_fp = NULL;
+ }
+
+ g_object_unref(xfer);
+}
+
+void
+purple_xfer_add(PurpleXfer *xfer)
+{
+ PurpleXferUiOps *ui_ops;
+
+ g_return_if_fail(PURPLE_IS_XFER(xfer));
+
+ ui_ops = purple_xfer_get_ui_ops(xfer);
+
+ if (ui_ops != NULL && ui_ops->add_xfer != NULL)
+ ui_ops->add_xfer(xfer);
+}
+
+void
+purple_xfer_cancel_local(PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+ PurpleXferUiOps *ui_ops;
+ char *msg = NULL;
+
+ g_return_if_fail(priv != NULL);
+
+ /* TODO: We definitely want to close any open request dialogs associated
+ with this transfer. However, in some cases the request dialog might
+ own a reference on the xfer. This happens at least with the "%s wants
+ to send you %s" dialog from purple_xfer_ask_recv(). In these cases
+ the ref count will not be decremented when the request dialog is
+ closed, so the ref count will never reach 0 and the xfer will never
+ be freed. This is a memleak and should be fixed. It's not clear what
+ the correct fix is. Probably requests should have a destroy function
+ that is called when the request is destroyed. But also, ref counting
+ xfer objects makes this code REALLY complicated. An alternate fix is
+ to not ref count and instead just make sure the object still exists
+ when we try to use it. */
+ purple_request_close_with_handle(xfer);
+
+ purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL);
+ priv->end_time = time(NULL);
+
+ g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_END_TIME]);
+
+ if (purple_xfer_get_filename(xfer) != NULL)
+ {
+ msg = g_strdup_printf(_("You cancelled the transfer of %s"),
+ purple_xfer_get_filename(xfer));
+ }
+ else
+ {
+ msg = g_strdup(_("File transfer cancelled"));
+ }
+ purple_xfer_conversation_write(xfer, msg, FALSE);
+ 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->watcher != 0) {
+ purple_input_remove(priv->watcher);
+ purple_xfer_set_watcher(xfer, 0);
+ }
+
+ if (priv->fd != -1)
+ close(priv->fd);
+
+ if (priv->dest_fp != NULL) {
+ fclose(priv->dest_fp);
+ priv->dest_fp = NULL;
+ }
+
+ ui_ops = purple_xfer_get_ui_ops(xfer);
+
+ if (ui_ops != NULL && ui_ops->cancel_local != NULL)
+ ui_ops->cancel_local(xfer);
+
+ priv->bytes_remaining = 0;
+
+ g_object_unref(xfer);
+}
+
+void
+purple_xfer_cancel_remote(PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+ PurpleXferUiOps *ui_ops;
+ gchar *msg;
+ PurpleAccount *account;
+ PurpleBuddy *buddy;
+
+ g_return_if_fail(priv != NULL);
+
+ purple_request_close_with_handle(xfer);
+ purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE);
+ priv->end_time = time(NULL);
+
+ g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_END_TIME]);
+
+ account = purple_xfer_get_account(xfer);
+ buddy = purple_blist_find_buddy(account, priv->who);
+
+ if (purple_xfer_get_filename(xfer) != NULL)
+ {
+ msg = g_strdup_printf(_("%s cancelled the transfer of %s"),
+ buddy ? purple_buddy_get_alias(buddy) : priv->who, purple_xfer_get_filename(xfer));
+ }
+ else
+ {
+ msg = g_strdup_printf(_("%s cancelled the file transfer"),
+ buddy ? purple_buddy_get_alias(buddy) : priv->who);
+ }
+ purple_xfer_conversation_write(xfer, msg, TRUE);
+ 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->watcher != 0) {
+ purple_input_remove(priv->watcher);
+ purple_xfer_set_watcher(xfer, 0);
+ }
+
+ if (priv->fd != -1)
+ close(priv->fd);
+
+ if (priv->dest_fp != NULL) {
+ fclose(priv->dest_fp);
+ priv->dest_fp = NULL;
+ }
+
+ ui_ops = purple_xfer_get_ui_ops(xfer);
+
+ if (ui_ops != NULL && ui_ops->cancel_remote != NULL)
+ ui_ops->cancel_remote(xfer);
+
+ priv->bytes_remaining = 0;
+
+ g_object_unref(xfer);
+}
+
+void
+purple_xfer_error(PurpleXferType type, PurpleAccount *account, const char *who, const char *msg)
+{
+ char *title;
+
+ g_return_if_fail(msg != NULL);
+ g_return_if_fail(type != PURPLE_XFER_TYPE_UNKNOWN);
+
+ if (account) {
+ PurpleBuddy *buddy;
+ buddy = purple_blist_find_buddy(account, who);
+ if (buddy)
+ who = purple_buddy_get_alias(buddy);
+ }
+
+ if (type == PURPLE_XFER_TYPE_SEND)
+ title = g_strdup_printf(_("File transfer to %s failed."), who);
+ else
+ title = g_strdup_printf(_("File transfer from %s failed."), who);
+
+ purple_notify_error(NULL, NULL, title, msg,
+ purple_request_cpar_from_account(account));
+
+ g_free(title);
+}
+
+void
+purple_xfer_update_progress(PurpleXfer *xfer)
+{
+ PurpleXferUiOps *ui_ops;
+
+ 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)
+ ui_ops->update_progress(xfer, purple_xfer_get_progress(xfer));
+}
+
+gconstpointer
+purple_xfer_get_thumbnail(const PurpleXfer *xfer, gsize *len)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ if (len)
+ *len = priv->thumbnail_size;
+
+ return priv->thumbnail_data;
+}
+
+const gchar *
+purple_xfer_get_thumbnail_mimetype(const PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->thumbnail_mimetype;
+}
+
+void
+purple_xfer_set_thumbnail(PurpleXfer *xfer, gconstpointer thumbnail,
+ gsize size, const gchar *mimetype)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_if_fail(priv != NULL);
+
+ g_free(priv->thumbnail_data);
+ g_free(priv->thumbnail_mimetype);
+
+ if (thumbnail && size > 0) {
+ priv->thumbnail_data = g_memdup(thumbnail, size);
+ priv->thumbnail_size = size;
+ priv->thumbnail_mimetype = g_strdup(mimetype);
+ } else {
+ priv->thumbnail_data = NULL;
+ priv->thumbnail_size = 0;
+ priv->thumbnail_mimetype = NULL;
+ }
+}
+
+void
+purple_xfer_prepare_thumbnail(PurpleXfer *xfer, const gchar *formats)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_if_fail(priv != NULL);
+
+ if (priv->ui_ops->add_thumbnail) {
+ priv->ui_ops->add_thumbnail(xfer, formats);
+ }
+}
+
+void
+purple_xfer_set_protocol_data(PurpleXfer *xfer, gpointer proto_data)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ g_return_if_fail(priv != NULL);
+
+ priv->proto_data = proto_data;
+}
+
+gpointer
+purple_xfer_get_protocol_data(const PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_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)
+{
+ g_return_if_fail(PURPLE_IS_XFER(xfer));
+
+ xfer->ui_data = ui_data;
+}
+
+gpointer purple_xfer_get_ui_data(const PurpleXfer *xfer)
+{
+ g_return_val_if_fail(PURPLE_IS_XFER(xfer), NULL);
+
+ return xfer->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)
+{
+ PurpleXfer *xfer = PURPLE_XFER(obj);
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ switch (param_id) {
+ case PROP_TYPE:
+ priv->type = g_value_get_enum(value);
+ break;
+ case PROP_ACCOUNT:
+ priv->account = g_value_get_object(value);
+ break;
+ case PROP_REMOTE_USER:
+ purple_xfer_set_remote_user(xfer, g_value_get_string(value));
+ break;
+ case PROP_MESSAGE:
+ purple_xfer_set_message(xfer, g_value_get_string(value));
+ break;
+ case PROP_FILENAME:
+ purple_xfer_set_filename(xfer, g_value_get_string(value));
+ break;
+ case PROP_LOCAL_FILENAME:
+ purple_xfer_set_local_filename(xfer, g_value_get_string(value));
+ break;
+ case PROP_FILE_SIZE:
+ purple_xfer_set_size(xfer, g_value_get_int64(value));
+ break;
+ case PROP_LOCAL_PORT:
+ purple_xfer_set_local_port(xfer, g_value_get_int(value));
+ break;
+ case PROP_FD:
+ purple_xfer_set_fd(xfer, g_value_get_int(value));
+ break;
+ case PROP_WATCHER:
+ purple_xfer_set_watcher(xfer, g_value_get_int(value));
+ break;
+ case PROP_BYTES_SENT:
+ purple_xfer_set_bytes_sent(xfer, g_value_get_int64(value));
+ break;
+ case PROP_STATUS:
+ purple_xfer_set_status(xfer, g_value_get_enum(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)
+{
+ PurpleXfer *xfer = PURPLE_XFER(obj);
+
+ switch (param_id) {
+ case PROP_TYPE:
+ g_value_set_enum(value, purple_xfer_get_xfer_type(xfer));
+ break;
+ case PROP_ACCOUNT:
+ g_value_set_object(value, purple_xfer_get_account(xfer));
+ break;
+ case PROP_REMOTE_USER:
+ g_value_set_string(value, purple_xfer_get_remote_user(xfer));
+ break;
+ case PROP_MESSAGE:
+ g_value_set_string(value, purple_xfer_get_message(xfer));
+ break;
+ case PROP_FILENAME:
+ g_value_set_string(value, purple_xfer_get_filename(xfer));
+ break;
+ case PROP_LOCAL_FILENAME:
+ g_value_set_string(value, purple_xfer_get_local_filename(xfer));
+ break;
+ case PROP_FILE_SIZE:
+ g_value_set_int64(value, purple_xfer_get_size(xfer));
+ break;
+ case PROP_REMOTE_IP:
+ g_value_set_string(value, purple_xfer_get_remote_ip(xfer));
+ break;
+ case PROP_LOCAL_PORT:
+ g_value_set_int(value, purple_xfer_get_local_port(xfer));
+ break;
+ case PROP_REMOTE_PORT:
+ g_value_set_int(value, purple_xfer_get_remote_port(xfer));
+ break;
+ case PROP_FD:
+ g_value_set_int(value, purple_xfer_get_fd(xfer));
+ break;
+ case PROP_WATCHER:
+ g_value_set_int(value, purple_xfer_get_watcher(xfer));
+ break;
+ case PROP_BYTES_SENT:
+ 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;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* GObject initialization function */
+static void
+purple_xfer_init(GTypeInstance *instance, gpointer klass)
+{
+ PurpleXfer *xfer = PURPLE_XFER(instance);
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+
+ PURPLE_DBUS_REGISTER_POINTER(xfer, PurpleXfer);
+
+ priv->ui_ops = purple_xfers_get_ui_ops();
+ priv->current_buffer_size = FT_INITIAL_BUFFER_SIZE;
+ priv->fd = -1;
+ priv->ready = PURPLE_XFER_READY_NONE;
+}
+
+/* Called when done constructing */
+static void
+purple_xfer_constructed(GObject *object)
+{
+ PurpleXfer *xfer = PURPLE_XFER(object);
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+ PurpleXferUiOps *ui_ops;
+
+ parent_class->constructed(object);
+
+ ui_ops = purple_xfers_get_ui_ops();
+
+ if (ui_ops && ui_ops->data_not_sent) {
+ /* If the ui will handle unsent data no need for buffer */
+ priv->buffer = NULL;
+ } else {
+ priv->buffer = g_byte_array_sized_new(FT_INITIAL_BUFFER_SIZE);
+ }
+
+ 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)
+{
+ PurpleXfer *xfer = PURPLE_XFER(object);
+ PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
+ PurpleXferUiOps *ui_ops;
+
+ /* Close the file browser, if it's open */
+ purple_request_close_with_handle(xfer);
+
+ 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)
+ ui_ops->destroy(xfer);
+
+ xfers = g_list_remove(xfers, xfer);
+
+ g_free(priv->who);
+ g_free(priv->filename);
+ g_free(priv->remote_ip);
+ g_free(priv->local_filename);
+
+ if (priv->buffer)
+ g_byte_array_free(priv->buffer, TRUE);
+
+ g_free(priv->thumbnail_data);
+ g_free(priv->thumbnail_mimetype);
+
+ PURPLE_DBUS_UNREGISTER_POINTER(xfer);
+
+ parent_class->finalize(object);
+}
+
+/* Class initializer function */
+static void
+purple_xfer_class_init(PurpleXferClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ 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;
+
+ g_type_class_add_private(klass, sizeof(PurpleXferPrivate));
+
+ properties[PROP_TYPE] = g_param_spec_enum("type", "Transfer type",
+ "The type of file transfer.", PURPLE_TYPE_XFER_TYPE,
+ PURPLE_XFER_TYPE_UNKNOWN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_ACCOUNT] = g_param_spec_object("account", "Account",
+ "The account sending or receiving the file.",
+ PURPLE_TYPE_ACCOUNT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_REMOTE_USER] = g_param_spec_string("remote-user",
+ "Remote user",
+ "The name of the remote user.", NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_MESSAGE] = g_param_spec_string("message", "Message",
+ "The message for the file transfer.", NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_FILENAME] = g_param_spec_string("filename", "Filename",
+ "The filename for the file transfer.", NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_LOCAL_FILENAME] = g_param_spec_string("local-filename",
+ "Local filename",
+ "The local filename for the file transfer.", NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_FILE_SIZE] = g_param_spec_int64("file-size", "File size",
+ "Size of the file in a file transfer.",
+ G_MININT64, G_MAXINT64, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_REMOTE_IP] = g_param_spec_string("remote-ip", "Remote IP",
+ "The remote IP address in the file transfer.", NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_LOCAL_PORT] = g_param_spec_int("local-port", "Local port",
+ "The local port number in the file transfer.",
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_REMOTE_PORT] = g_param_spec_int("remote-port",
+ "Remote port",
+ "The remote port number in the file transfer.",
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_FD] = g_param_spec_int("fd", "Socket FD",
+ "The socket file descriptor.",
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_WATCHER] = g_param_spec_int("watcher", "Watcher",
+ "The watcher for the file transfer.",
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_BYTES_SENT] = g_param_spec_int64("bytes-sent", "Bytes sent",
+ "The number of bytes sent (or received) so far.",
+ 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_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);
+
+ g_object_class_install_properties(obj_class, PROP_LAST, properties);
+}
+
+GType
+purple_xfer_get_type(void)
+{
+ static GType type = 0;
+
+ if(type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PurpleXferClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)purple_xfer_class_init,
+ NULL,
+ NULL,
+ sizeof(PurpleXfer),
+ 0,
+ (GInstanceInitFunc)purple_xfer_init,
+ NULL,
+ };
+
+ type = g_type_register_static(G_TYPE_OBJECT, "PurpleXfer",
+ &info, 0);
+ }
+
+ return type;
+}
+
+PurpleXfer *
+purple_xfer_new(PurpleAccount *account, PurpleXferType type, const char *who)
+{
+ g_return_val_if_fail(type != PURPLE_XFER_TYPE_UNKNOWN, NULL);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+ g_return_val_if_fail(who != NULL, NULL);
+
+ return g_object_new(PURPLE_TYPE_XFER,
+ "type", type,
+ "account", account,
+ "remote-user", who,
+ NULL
+ );
+}
+
+/**************************************************************************
+ * File Transfer Subsystem API
+ **************************************************************************/
+GList *
+purple_xfers_get_all()
+{
+ return xfers;
+}
+
+void *
+purple_xfers_get_handle(void) {
+ static int handle = 0;
+
+ return &handle;
+}
+
+void
+purple_xfers_init(void) {
+ void *handle = purple_xfers_get_handle();
+
+ /* register signals */
+ purple_signal_register(handle, "file-recv-accept",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_XFER);
+ purple_signal_register(handle, "file-send-accept",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_XFER);
+ purple_signal_register(handle, "file-recv-start",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_XFER);
+ purple_signal_register(handle, "file-send-start",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_XFER);
+ purple_signal_register(handle, "file-send-cancel",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_XFER);
+ purple_signal_register(handle, "file-recv-cancel",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_XFER);
+ purple_signal_register(handle, "file-send-complete",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_XFER);
+ purple_signal_register(handle, "file-recv-complete",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_XFER);
+ purple_signal_register(handle, "file-recv-request",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_XFER);
+}
+
+void
+purple_xfers_uninit(void)
+{
+ void *handle = purple_xfers_get_handle();
+
+ purple_signals_disconnect_by_handle(handle);
+ purple_signals_unregister_by_instance(handle);
+}
+
+void
+purple_xfers_set_ui_ops(PurpleXferUiOps *ops) {
+ xfer_ui_ops = ops;
+}
+
+PurpleXferUiOps *
+purple_xfers_get_ui_ops(void) {
+ return xfer_ui_ops;
+}
+
+/**************************************************************************
+ * GBoxed code
+ **************************************************************************/
+static PurpleXferUiOps *
+purple_xfer_ui_ops_copy(PurpleXferUiOps *ops)
+{
+ PurpleXferUiOps *ops_new;
+
+ g_return_val_if_fail(ops != NULL, NULL);
+
+ ops_new = g_new(PurpleXferUiOps, 1);
+ *ops_new = *ops;
+
+ return ops_new;
+}
+
+GType
+purple_xfer_ui_ops_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleXferUiOps",
+ (GBoxedCopyFunc)purple_xfer_ui_ops_copy,
+ (GBoxedFreeFunc)g_free);
+ }
+
+ return type;
+}
diff --git a/libpurple/xfer.h b/libpurple/xfer.h
new file mode 100644
index 0000000000..64fbd8d9e5
--- /dev/null
+++ b/libpurple/xfer.h
@@ -0,0 +1,943 @@
+/* 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 _PURPLE_XFER_H_
+#define _PURPLE_XFER_H_
+/**
+ * SECTION:xfer
+ * @section_id: libpurple-xfer
+ * @short_description: <filename>xfer.h</filename>
+ * @title: File Transfer API
+ * @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())
+
+/**************************************************************************/
+/** Data Structures */
+/**************************************************************************/
+typedef struct _PurpleXfer PurpleXfer;
+typedef struct _PurpleXferClass PurpleXferClass;
+
+typedef struct _PurpleXferUiOps PurpleXferUiOps;
+
+#include <glib.h>
+#include <stdio.h>
+
+#include "account.h"
+
+/**
+ * PurpleXferType:
+ * @PURPLE_XFER_TYPE_UNKNOWN: Unknown file transfer type.
+ * @PURPLE_XFER_TYPE_SEND: File sending.
+ * @PURPLE_XFER_TYPE_RECEIVE: File receiving.
+ *
+ * Types of file transfers.
+ */
+typedef enum
+{
+ PURPLE_XFER_TYPE_UNKNOWN = 0,
+ PURPLE_XFER_TYPE_SEND,
+ PURPLE_XFER_TYPE_RECEIVE
+
+} PurpleXferType;
+
+/**
+ * PurpleXferStatus:
+ * @PURPLE_XFER_STATUS_UNKNOWN: Unknown, the xfer may be null.
+ * @PURPLE_XFER_STATUS_NOT_STARTED: It hasn't started yet.
+ * @PURPLE_XFER_STATUS_ACCEPTED: Receive accepted, but destination file
+ * not selected yet
+ * @PURPLE_XFER_STATUS_STARTED: purple_xfer_start has been called.
+ * @PURPLE_XFER_STATUS_DONE: The xfer completed successfully.
+ * @PURPLE_XFER_STATUS_CANCEL_LOCAL: The xfer was cancelled by us.
+ * @PURPLE_XFER_STATUS_CANCEL_REMOTE: The xfer was cancelled by the other end,
+ * or we couldn't connect.
+ *
+ * The different states of the xfer.
+ */
+typedef enum
+{
+ PURPLE_XFER_STATUS_UNKNOWN = 0,
+ PURPLE_XFER_STATUS_NOT_STARTED,
+ PURPLE_XFER_STATUS_ACCEPTED,
+ PURPLE_XFER_STATUS_STARTED,
+ PURPLE_XFER_STATUS_DONE,
+ PURPLE_XFER_STATUS_CANCEL_LOCAL,
+ PURPLE_XFER_STATUS_CANCEL_REMOTE
+} PurpleXferStatus;
+
+/**
+ * PurpleXferUiOps:
+ * @ui_write: UI op to write data received from the protocol. The UI must deal
+ * with the entire buffer and return size, or it is treated as an
+ * error.
+ * <sbr/>@xfer: The file transfer structure
+ * <sbr/>@buffer: The buffer to write
+ * <sbr/>@size: The size of the buffer
+ * <sbr/>Returns: size if the write was successful, or a value
+ * between 0 and size on error.
+ * @ui_read: UI op to read data to send to the protocol for a file transfer.
+ * <sbr/>@xfer: The file transfer structure
+ * <sbr/>@buffer: A pointer to a buffer. The UI must allocate this
+ * buffer. libpurple will free the data.
+ * <sbr/>@size: The maximum amount of data to put in the buffer.
+ * <sbr/>Returns: The amount of data in the buffer, 0 if nothing is
+ * available, and a negative value if an error occurred
+ * and the transfer should be cancelled (libpurple will
+ * cancel).
+ * @data_not_sent: Op to notify the UI that not all the data read in was
+ * written. The UI should re-enqueue this data and return it the
+ * next time read is called.
+ * <sbr/>This <emphasis>MUST</emphasis> be implemented if read
+ * and write are implemented.
+ * <sbr/>@xfer: The file transfer structure
+ * <sbr/>@buffer: A pointer to the beginning of the unwritten
+ * data.
+ * <sbr/>@size: The amount of unwritten data.
+ * @add_thumbnail: Op to create a thumbnail image for a file transfer
+ *
+ * File transfer UI operations.
+ *
+ * Any UI representing a file transfer must assign a filled-out
+ * PurpleXferUiOps structure to the purple_xfer.
+ */
+struct _PurpleXferUiOps
+{
+ void (*new_xfer)(PurpleXfer *xfer);
+ void (*destroy)(PurpleXfer *xfer);
+ void (*add_xfer)(PurpleXfer *xfer);
+ void (*update_progress)(PurpleXfer *xfer, double percent);
+ void (*cancel_local)(PurpleXfer *xfer);
+ void (*cancel_remote)(PurpleXfer *xfer);
+ gssize (*ui_write)(PurpleXfer *xfer, const guchar *buffer, gssize size);
+ gssize (*ui_read)(PurpleXfer *xfer, guchar **buffer, gssize size);
+ void (*data_not_sent)(PurpleXfer *xfer, const guchar *buffer, gsize size);
+ void (*add_thumbnail)(PurpleXfer *xfer, const gchar *formats);
+};
+
+/**
+ * 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:
+ *
+ * Base class for all #PurpleXfer's
+ */
+struct _PurpleXferClass
+{
+ GObjectClass parent_class;
+
+ /*< private >*/
+ void (*_purple_reserved1)(void);
+ void (*_purple_reserved2)(void);
+ void (*_purple_reserved3)(void);
+ void (*_purple_reserved4)(void);
+};
+
+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.
+ * @who: The name of the remote user.
+ *
+ * Creates a new file transfer handle.
+ * This is called by protocols.
+ *
+ * Returns: A file transfer handle.
+ */
+PurpleXfer *purple_xfer_new(PurpleAccount *account,
+ PurpleXferType type, const char *who);
+
+/**
+ * purple_xfer_request:
+ * @xfer: The file transfer to request confirmation on.
+ *
+ * Requests confirmation for a file transfer from the user. If receiving
+ * a file which is known at this point, this requests user to accept and
+ * save the file. If the filename is unknown (not set) this only requests user
+ * to accept the file transfer. In this case protocol must call this function
+ * again once the filename is available.
+ */
+void purple_xfer_request(PurpleXfer *xfer);
+
+/**
+ * purple_xfer_request_accepted:
+ * @xfer: The file transfer.
+ * @filename: The filename.
+ *
+ * Called if the user accepts the file transfer request.
+ */
+void purple_xfer_request_accepted(PurpleXfer *xfer, const char *filename);
+
+/**
+ * purple_xfer_request_denied:
+ * @xfer: The file transfer.
+ *
+ * Called if the user rejects the file transfer request.
+ */
+void purple_xfer_request_denied(PurpleXfer *xfer);
+
+/**
+ * purple_xfer_get_fd:
+ * @xfer: The file transfer.
+ *
+ * Returns the socket file descriptor.
+ *
+ * Returns: The socket file descriptor.
+ */
+int purple_xfer_get_fd(PurpleXfer *xfer);
+
+/**
+ * purple_xfer_get_watcher:
+ * @xfer: The file transfer.
+ *
+ * Returns the Watcher for the transfer.
+ *
+ * Returns: The watcher.
+ */
+int purple_xfer_get_watcher(PurpleXfer *xfer);
+
+/**
+ * purple_xfer_get_xfer_type:
+ * @xfer: The file transfer.
+ *
+ * Returns the type of file transfer.
+ *
+ * Returns: The type of the file transfer.
+ */
+PurpleXferType purple_xfer_get_xfer_type(const PurpleXfer *xfer);
+
+/**
+ * purple_xfer_get_account:
+ * @xfer: The file transfer.
+ *
+ * Returns the account the file transfer is using.
+ *
+ * Returns: The account.
+ */
+PurpleAccount *purple_xfer_get_account(const PurpleXfer *xfer);
+
+/**
+ * purple_xfer_set_remote_user:
+ * @xfer: The file transfer.
+ * @who: The name of the remote user.
+ *
+ * Sets the name of the remote user.
+ */
+void purple_xfer_set_remote_user(PurpleXfer *xfer, const char *who);
+
+/**
+ * purple_xfer_get_remote_user:
+ * @xfer: The file transfer.
+ *
+ * Returns the name of the remote user.
+ *
+ * Returns: The name of the remote user.
+ */
+const char *purple_xfer_get_remote_user(const PurpleXfer *xfer);
+
+/**
+ * purple_xfer_get_status:
+ * @xfer: The file transfer.
+ *
+ * Returns the status of the xfer.
+ *
+ * Returns: The status.
+ */
+PurpleXferStatus purple_xfer_get_status(const PurpleXfer *xfer);
+
+/**
+ * purple_xfer_is_cancelled:
+ * @xfer: The file transfer.
+ *
+ * Returns true if the file transfer was cancelled.
+ *
+ * Returns: Whether or not the transfer was cancelled.
+ */
+gboolean purple_xfer_is_cancelled(const PurpleXfer *xfer);
+
+/**
+ * purple_xfer_is_completed:
+ * @xfer: The file transfer.
+ *
+ * Returns the completed state for a file transfer.
+ *
+ * Returns: The completed state.
+ */
+gboolean purple_xfer_is_completed(const PurpleXfer *xfer);
+
+/**
+ * purple_xfer_get_filename:
+ * @xfer: The file transfer.
+ *
+ * Returns the name of the file being sent or received.
+ *
+ * Returns: The filename.
+ */
+const char *purple_xfer_get_filename(const PurpleXfer *xfer);
+
+/**
+ * purple_xfer_get_local_filename:
+ * @xfer: The file transfer.
+ *
+ * Returns the file's destination filename,
+ *
+ * Returns: The destination filename.
+ */
+const char *purple_xfer_get_local_filename(const PurpleXfer *xfer);
+
+/**
+ * purple_xfer_get_bytes_sent:
+ * @xfer: The file transfer.
+ *
+ * Returns the number of bytes sent (or received) so far.
+ *
+ * Returns: The number of bytes sent.
+ */
+goffset purple_xfer_get_bytes_sent(const PurpleXfer *xfer);
+
+/**
+ * purple_xfer_get_bytes_remaining:
+ * @xfer: The file transfer.
+ *
+ * Returns the number of bytes remaining to send or receive.
+ *
+ * Returns: The number of bytes remaining.
+ */
+goffset purple_xfer_get_bytes_remaining(const PurpleXfer *xfer);
+
+/**
+ * purple_xfer_get_size:
+ * @xfer: The file transfer.
+ *
+ * Returns the size of the file being sent or received.
+ *
+ * Returns: The total size of the file.
+ */
+goffset purple_xfer_get_size(const PurpleXfer *xfer);
+
+/**
+ * purple_xfer_get_progress:
+ * @xfer: The file transfer.
+ *
+ * Returns the current percentage of progress of the transfer.
+ *
+ * This is a number between 0 (0%) and 1 (100%).
+ *
+ * Returns: The percentage complete.
+ */
+double purple_xfer_get_progress(const PurpleXfer *xfer);
+
+/**
+ * purple_xfer_get_local_port:
+ * @xfer: The file transfer.
+ *
+ * Returns the local port number in the file transfer.
+ *
+ * Returns: The port number on this end.
+ */
+guint16 purple_xfer_get_local_port(const PurpleXfer *xfer);
+
+/**
+ * purple_xfer_get_remote_ip:
+ * @xfer: The file transfer.
+ *
+ * Returns the remote IP address in the file transfer.
+ *
+ * Returns: The IP address on the other end.
+ */
+const char *purple_xfer_get_remote_ip(const PurpleXfer *xfer);
+
+/**
+ * purple_xfer_get_remote_port:
+ * @xfer: The file transfer.
+ *
+ * Returns the remote port number in the file transfer.
+ *
+ * Returns: The port number on the other end.
+ */
+guint16 purple_xfer_get_remote_port(const PurpleXfer *xfer);
+
+/**
+ * purple_xfer_get_start_time:
+ * @xfer: The file transfer.
+ *
+ * Returns the time the transfer of a file started.
+ *
+ * Returns: The time when the transfer started.
+ */
+time_t purple_xfer_get_start_time(const PurpleXfer *xfer);
+
+/**
+ * purple_xfer_get_end_time:
+ * @xfer: The file transfer.
+ *
+ * Returns the time the transfer of a file ended.
+ *
+ * Returns: The time when the transfer ended.
+ */
+time_t purple_xfer_get_end_time(const PurpleXfer *xfer);
+
+/**
+ * purple_xfer_set_fd:
+ * @xfer: The file transfer.
+ * @fd: The file descriptor.
+ *
+ * Sets the socket file descriptor.
+ */
+void purple_xfer_set_fd(PurpleXfer *xfer, int fd);
+
+/**
+ * purple_xfer_set_watcher:
+ * @xfer: The file transfer.
+ * @watcher: The watcher.
+ *
+ * Sets the watcher for the file transfer.
+ */
+void purple_xfer_set_watcher(PurpleXfer *xfer, int watcher);
+
+/**
+ * purple_xfer_set_completed:
+ * @xfer: The file transfer.
+ * @completed: The completed state.
+ *
+ * Sets the completed state for the file transfer.
+ */
+void purple_xfer_set_completed(PurpleXfer *xfer, gboolean completed);
+
+/**
+ * purple_xfer_set_status:
+ * @xfer: The file transfer.
+ * @status: The current status.
+ *
+ * Sets the current status for the file transfer.
+ */
+void purple_xfer_set_status(PurpleXfer *xfer, PurpleXferStatus status);
+
+/**
+ * purple_xfer_set_message:
+ * @xfer: The file transfer.
+ * @message: The message.
+ *
+ * Sets the message for the file transfer.
+ */
+void purple_xfer_set_message(PurpleXfer *xfer, const char *message);
+
+/**
+ * purple_xfer_get_message:
+ * @xfer: The file transfer.
+ *
+ * Returns the message for the file transfer.
+ *
+ * Returns: The message.
+ */
+const char *purple_xfer_get_message(const PurpleXfer *xfer);
+
+/**
+ * purple_xfer_set_filename:
+ * @xfer: The file transfer.
+ * @filename: The filename.
+ *
+ * Sets the filename for the file transfer.
+ */
+void purple_xfer_set_filename(PurpleXfer *xfer, const char *filename);
+
+/**
+ * purple_xfer_set_local_filename:
+ * @xfer: The file transfer.
+ * @filename: The filename
+ *
+ * Sets the local filename for the file transfer.
+ */
+void purple_xfer_set_local_filename(PurpleXfer *xfer, const char *filename);
+
+/**
+ * purple_xfer_set_size:
+ * @xfer: The file transfer.
+ * @size: The size of the file.
+ *
+ * Sets the size of the file in a file transfer.
+ */
+void purple_xfer_set_size(PurpleXfer *xfer, goffset size);
+
+/**
+ * purple_xfer_set_local_port:
+ * @xfer: The file transfer.
+ * @local_port: The local port.
+ *
+ * Sets the local port of the file transfer.
+ */
+void purple_xfer_set_local_port(PurpleXfer *xfer, guint16 local_port);
+
+/**
+ * purple_xfer_set_bytes_sent:
+ * @xfer: The file transfer.
+ * @bytes_sent: The new current position in the file. If we're
+ * sending a file then this is the next byte that we
+ * will send. If we're receiving a file, this is the
+ * next byte that we expect to receive.
+ *
+ * Sets the current working position in the active file transfer. This
+ * can be used to jump backward in the file if the protocol detects
+ * that some bit of data needs to be resent or has been sent twice.
+ *
+ * It's used for pausing and resuming an oscar file transfer.
+ */
+void purple_xfer_set_bytes_sent(PurpleXfer *xfer, goffset bytes_sent);
+
+/**
+ * purple_xfer_get_ui_ops:
+ * @xfer: The file transfer.
+ *
+ * Returns the UI operations structure for a file transfer.
+ *
+ * Returns: The UI operations structure.
+ */
+PurpleXferUiOps *purple_xfer_get_ui_ops(const 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.
+ *
+ * Reads in data from a file transfer stream.
+ *
+ * Returns: The number of bytes read, or -1.
+ */
+gssize purple_xfer_read(PurpleXfer *xfer, guchar **buffer);
+
+/**
+ * purple_xfer_write:
+ * @xfer: The file transfer.
+ * @buffer: The buffer to read the data from.
+ * @size: The number of bytes to write.
+ *
+ * Writes data to a file transfer stream.
+ *
+ * Returns: The number of bytes written, or -1.
+ */
+gssize purple_xfer_write(PurpleXfer *xfer, const guchar *buffer, gsize size);
+
+/**
+ * purple_xfer_write_file:
+ * @xfer: The file transfer.
+ * @buffer: The buffer to read the data from.
+ * @size: The number of bytes to write.
+ *
+ * Writes chunk of received file.
+ *
+ * Returns: TRUE on success, FALSE otherwise.
+ */
+gboolean
+purple_xfer_write_file(PurpleXfer *xfer, const guchar *buffer, gsize size);
+
+/**
+ * purple_xfer_read_file:
+ * @xfer: The file transfer.
+ * @buffer: The buffer to write the data to.
+ * @size: The size of buffer.
+ *
+ * Writes chunk of file being sent.
+ *
+ * Returns: Number of bytes written (0 means, the device is busy), or -1 on
+ * failure.
+ */
+gssize
+purple_xfer_read_file(PurpleXfer *xfer, guchar *buffer, gsize size);
+
+/**
+ * purple_xfer_start:
+ * @xfer: The file transfer.
+ * @fd: The file descriptor for the socket.
+ * @ip: The IP address to connect to.
+ * @port: The port to connect to.
+ *
+ * Starts a file transfer.
+ *
+ * Either @fd must be specified <emphasis>or</emphasis> @ip and @port on a
+ * file receive transfer. On send, @fd must be specified, and
+ * @ip and @port are ignored.
+ *
+ * Passing @fd as '-1' is a special-case and indicates to the
+ * protocol plugin to facilitate the file transfer itself.
+ */
+void purple_xfer_start(PurpleXfer *xfer, int fd, const char *ip, guint16 port);
+
+/**
+ * purple_xfer_end:
+ * @xfer: The file transfer.
+ *
+ * Ends a file transfer.
+ */
+void purple_xfer_end(PurpleXfer *xfer);
+
+/**
+ * purple_xfer_add:
+ * @xfer: The file transfer.
+ *
+ * Adds a new file transfer to the list of file transfers. Call this only
+ * if you are not using purple_xfer_start.
+ */
+void purple_xfer_add(PurpleXfer *xfer);
+
+/**
+ * purple_xfer_cancel_local:
+ * @xfer: The file transfer.
+ *
+ * Cancels a file transfer on the local end.
+ */
+void purple_xfer_cancel_local(PurpleXfer *xfer);
+
+/**
+ * purple_xfer_cancel_remote:
+ * @xfer: The file transfer.
+ *
+ * Cancels a file transfer from the remote end.
+ */
+void purple_xfer_cancel_remote(PurpleXfer *xfer);
+
+/**
+ * purple_xfer_error:
+ * @type: The type of file transfer.
+ * @account: The account sending or receiving the file.
+ * @who: The user on the other end of the transfer.
+ * @msg: The message to display.
+ *
+ * Displays a file transfer-related error message.
+ *
+ * This is a wrapper around purple_notify_error(), which automatically
+ * specifies a title ("File transfer to <literal>user</literal> failed" or
+ * "File Transfer from <literal>user</literal> failed").
+ */
+void purple_xfer_error(PurpleXferType type, PurpleAccount *account, const char *who, const char *msg);
+
+/**
+ * purple_xfer_update_progress:
+ * @xfer: The file transfer.
+ *
+ * Updates file transfer progress.
+ */
+void purple_xfer_update_progress(PurpleXfer *xfer);
+
+/**
+ * purple_xfer_conversation_write:
+ * @xfer: The file transfer to which this message relates.
+ * @message: The message to display.
+ * @is_error: Is this an error message?.
+ *
+ * Displays a file transfer-related message in the conversation window
+ *
+ * This is a wrapper around purple_conversation_write_system_message
+ */
+void purple_xfer_conversation_write(PurpleXfer *xfer, const gchar *message, gboolean is_error);
+
+/**
+ * purple_xfer_ui_ready:
+ * @xfer: The file transfer which is ready.
+ *
+ * Allows the UI to signal it's ready to send/receive data (depending on
+ * the direction of the file transfer. Used when the UI is providing
+ * read/write/data_not_sent UI ops.
+ */
+void purple_xfer_ui_ready(PurpleXfer *xfer);
+
+/**
+ * purple_xfer_protocol_ready:
+ * @xfer: The file transfer which is ready.
+ *
+ * Allows the protocol to signal it's ready to send/receive data (depending on
+ * the direction of the file transfer. Used when the protocol provides read/write
+ * ops and cannot/does not provide a raw fd to the core.
+ */
+void purple_xfer_prpl_ready(PurpleXfer *xfer);
+
+/**
+ * purple_xfer_get_thumbnail:
+ * @xfer: The file transfer to get the thumbnail for
+ * @len: If not %NULL, the length of the thumbnail data returned
+ * will be set in the location pointed to by this.
+ *
+ * Gets the thumbnail data for a transfer
+ *
+ * Returns: The thumbnail data, or NULL if there is no thumbnail
+ */
+gconstpointer purple_xfer_get_thumbnail(const PurpleXfer *xfer, gsize *len);
+
+/**
+ * purple_xfer_get_thumbnail_mimetype:
+ * @xfer: The file transfer to get the mimetype for
+ *
+ * Gets the mimetype of the thumbnail preview for a transfer
+ *
+ * Returns: The mimetype of the thumbnail, or %NULL if not thumbnail is set
+ */
+const gchar *purple_xfer_get_thumbnail_mimetype(const PurpleXfer *xfer);
+
+
+/**
+ * purple_xfer_set_thumbnail:
+ * @xfer: The file transfer to set the data for
+ * @thumbnail: A pointer to the thumbnail data, this will be copied
+ * @size: The size in bytes of the passed in thumbnail data
+ * @mimetype: The mimetype of the generated thumbnail
+ *
+ * Sets the thumbnail data for a transfer
+ */
+void purple_xfer_set_thumbnail(PurpleXfer *xfer, gconstpointer thumbnail,
+ gsize size, const gchar *mimetype);
+
+/**
+ * purple_xfer_prepare_thumbnail:
+ * @xfer: The file transfer to create a thumbnail for
+ * @formats: A comma-separated list of mimetypes for image formats
+ * the protocols can use for thumbnails.
+ *
+ * Prepare a thumbnail for a transfer (if the UI supports it)
+ * will be no-op in case the UI doesn't implement thumbnail creation
+ */
+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(const PurpleXfer *xfer);
+
+/**
+ * purple_xfer_set_ui_data:
+ * @xfer: The file transfer.
+ * @ui_data: A pointer to associate with this file transfer.
+ *
+ * Set the UI data associated with this file transfer.
+ */
+void purple_xfer_set_ui_data(PurpleXfer *xfer, gpointer ui_data);
+
+/**
+ * purple_xfer_get_ui_data:
+ * @xfer: The file transfer.
+ *
+ * Get the UI data associated with this file transfer.
+ *
+ * Returns: 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.
+ */
+gpointer purple_xfer_get_ui_data(const PurpleXfer *xfer);
+
+/**************************************************************************/
+/* File Transfer Subsystem API */
+/**************************************************************************/
+
+/**
+ * purple_xfer_ui_ops_get_type:
+ *
+ * Returns: The #GType for the #PurpleXferUiOps boxed structure.
+ */
+GType purple_xfer_ui_ops_get_type(void);
+
+/**
+ * purple_xfers_get_all:
+ *
+ * Returns all xfers
+ *
+ * Returns: all current xfers with refs
+ */
+GList *purple_xfers_get_all(void);
+
+/**
+ * purple_xfers_get_handle:
+ *
+ * Returns the handle to the file transfer subsystem
+ *
+ * Returns: The handle
+ */
+void *purple_xfers_get_handle(void);
+
+/**
+ * purple_xfers_init:
+ *
+ * Initializes the file transfer subsystem
+ */
+void purple_xfers_init(void);
+
+/**
+ * purple_xfers_uninit:
+ *
+ * Uninitializes the file transfer subsystem
+ */
+void purple_xfers_uninit(void);
+
+/**
+ * purple_xfers_set_ui_ops:
+ * @ops: The UI operations structure.
+ *
+ * Sets the UI operations structure to be used in all purple file transfers.
+ */
+void purple_xfers_set_ui_ops(PurpleXferUiOps *ops);
+
+/**
+ * purple_xfers_get_ui_ops:
+ *
+ * Returns the UI operations structure to be used in all purple file transfers.
+ *
+ * Returns: The UI operations structure.
+ */
+PurpleXferUiOps *purple_xfers_get_ui_ops(void);
+
+G_END_DECLS
+
+#endif /* _PURPLE_XFER_H_ */
+
diff --git a/libpurple/xmlnode.c b/libpurple/xmlnode.c
index 00c5866638..a4a8f44139 100644
--- a/libpurple/xmlnode.c
+++ b/libpurple/xmlnode.c
@@ -1,7 +1,3 @@
-/**
- * @file xmlnode.c XML DOM functions
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -46,44 +42,57 @@
# define NEWLINE_S "\n"
#endif
-static xmlnode*
-new_node(const char *name, XMLNodeType type)
+static PurpleXmlNode*
+new_node(const char *name, PurpleXmlNodeType type)
{
- xmlnode *node = g_new0(xmlnode, 1);
+ PurpleXmlNode *node = g_new0(PurpleXmlNode, 1);
node->name = g_strdup(name);
node->type = type;
- PURPLE_DBUS_REGISTER_POINTER(node, xmlnode);
+ PURPLE_DBUS_REGISTER_POINTER(node, PurpleXmlNode);
return node;
}
-xmlnode*
-xmlnode_new(const char *name)
+PurpleXmlNode*
+purple_xmlnode_new(const char *name)
{
g_return_val_if_fail(name != NULL && *name != '\0', NULL);
- return new_node(name, XMLNODE_TYPE_TAG);
+ return new_node(name, PURPLE_XMLNODE_TYPE_TAG);
}
-xmlnode *
-xmlnode_new_child(xmlnode *parent, const char *name)
+PurpleXmlNode *
+purple_xmlnode_new_child(PurpleXmlNode *parent, const char *name)
{
- xmlnode *node;
+ PurpleXmlNode *node;
g_return_val_if_fail(parent != NULL, NULL);
g_return_val_if_fail(name != NULL && *name != '\0', NULL);
- node = new_node(name, XMLNODE_TYPE_TAG);
-
- xmlnode_insert_child(parent, node);
+ node = new_node(name, PURPLE_XMLNODE_TYPE_TAG);
+
+ purple_xmlnode_insert_child(parent, node);
+#if 0
+ /* This would give PurpleXmlNodes more appropriate namespacing
+ * when creating them. Otherwise, unless an explicit namespace
+ * is set, purple_xmlnode_get_namespace() will return NULL, when
+ * there may be a default namespace.
+ *
+ * I'm unconvinced that it's useful, and concerned it may break things.
+ *
+ * _insert_child would need the same thing, probably (assuming
+ * xmlns->node == NULL)
+ */
+ purple_xmlnode_set_namespace(node, purple_xmlnode_get_default_namespace(node))
+#endif
return node;
}
void
-xmlnode_insert_child(xmlnode *parent, xmlnode *child)
+purple_xmlnode_insert_child(PurpleXmlNode *parent, PurpleXmlNode *child)
{
g_return_if_fail(parent != NULL);
g_return_if_fail(child != NULL);
@@ -100,36 +109,36 @@ xmlnode_insert_child(xmlnode *parent, xmlnode *child)
}
void
-xmlnode_insert_data(xmlnode *node, const char *data, gssize size)
+purple_xmlnode_insert_data(PurpleXmlNode *node, const char *data, gssize size)
{
- xmlnode *child;
+ PurpleXmlNode *child;
gsize real_size;
g_return_if_fail(node != NULL);
g_return_if_fail(data != NULL);
g_return_if_fail(size != 0);
- real_size = size == -1 ? strlen(data) : size;
+ real_size = size == -1 ? strlen(data) : (gsize)size;
- child = new_node(NULL, XMLNODE_TYPE_DATA);
+ child = new_node(NULL, PURPLE_XMLNODE_TYPE_DATA);
child->data = g_memdup(data, real_size);
child->data_sz = real_size;
- xmlnode_insert_child(node, child);
+ purple_xmlnode_insert_child(node, child);
}
void
-xmlnode_remove_attrib(xmlnode *node, const char *attr)
+purple_xmlnode_remove_attrib(PurpleXmlNode *node, const char *attr)
{
- xmlnode *attr_node, *sibling = NULL;
+ PurpleXmlNode *attr_node, *sibling = NULL;
g_return_if_fail(node != NULL);
g_return_if_fail(attr != NULL);
attr_node = node->child;
while (attr_node) {
- if(attr_node->type == XMLNODE_TYPE_ATTRIB &&
+ if(attr_node->type == PURPLE_XMLNODE_TYPE_ATTRIB &&
purple_strequal(attr_node->name, attr))
{
if (node->lastchild == attr_node) {
@@ -137,12 +146,12 @@ xmlnode_remove_attrib(xmlnode *node, const char *attr)
}
if (sibling == NULL) {
node->child = attr_node->next;
- xmlnode_free(attr_node);
+ purple_xmlnode_free(attr_node);
attr_node = node->child;
} else {
sibling->next = attr_node->next;
sibling = attr_node->next;
- xmlnode_free(attr_node);
+ purple_xmlnode_free(attr_node);
attr_node = sibling;
}
}
@@ -155,16 +164,16 @@ xmlnode_remove_attrib(xmlnode *node, const char *attr)
}
void
-xmlnode_remove_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns)
+purple_xmlnode_remove_attrib_with_namespace(PurpleXmlNode *node, const char *attr, const char *xmlns)
{
- xmlnode *attr_node, *sibling = NULL;
+ PurpleXmlNode *attr_node, *sibling = NULL;
g_return_if_fail(node != NULL);
g_return_if_fail(attr != NULL);
for(attr_node = node->child; attr_node; attr_node = attr_node->next)
{
- if(attr_node->type == XMLNODE_TYPE_ATTRIB &&
+ if(attr_node->type == PURPLE_XMLNODE_TYPE_ATTRIB &&
purple_strequal(attr, attr_node->name) &&
purple_strequal(xmlns, attr_node->xmlns))
{
@@ -176,7 +185,7 @@ xmlnode_remove_attrib_with_namespace(xmlnode *node, const char *attr, const char
if (node->lastchild == attr_node) {
node->lastchild = sibling;
}
- xmlnode_free(attr_node);
+ purple_xmlnode_free(attr_node);
return;
}
sibling = attr_node;
@@ -184,54 +193,42 @@ xmlnode_remove_attrib_with_namespace(xmlnode *node, const char *attr, const char
}
void
-xmlnode_set_attrib(xmlnode *node, const char *attr, const char *value)
+purple_xmlnode_set_attrib(PurpleXmlNode *node, const char *attr, const char *value)
{
- xmlnode_remove_attrib(node, attr);
- xmlnode_set_attrib_full(node, attr, NULL, NULL, value);
+ purple_xmlnode_remove_attrib(node, attr);
+ purple_xmlnode_set_attrib_full(node, attr, NULL, NULL, value);
}
void
-xmlnode_set_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns, const char *value)
+purple_xmlnode_set_attrib_full(PurpleXmlNode *node, const char *attr, const char *xmlns, const char *prefix, const char *value)
{
- xmlnode_set_attrib_full(node, attr, xmlns, NULL, value);
-}
-
-void
-xmlnode_set_attrib_with_prefix(xmlnode *node, const char *attr, const char *prefix, const char *value)
-{
- xmlnode_set_attrib_full(node, attr, NULL, prefix, value);
-}
-
-void
-xmlnode_set_attrib_full(xmlnode *node, const char *attr, const char *xmlns, const char *prefix, const char *value)
-{
- xmlnode *attrib_node;
+ PurpleXmlNode *attrib_node;
g_return_if_fail(node != NULL);
g_return_if_fail(attr != NULL);
g_return_if_fail(value != NULL);
- xmlnode_remove_attrib_with_namespace(node, attr, xmlns);
- attrib_node = new_node(attr, XMLNODE_TYPE_ATTRIB);
+ purple_xmlnode_remove_attrib_with_namespace(node, attr, xmlns);
+ attrib_node = new_node(attr, PURPLE_XMLNODE_TYPE_ATTRIB);
attrib_node->data = g_strdup(value);
attrib_node->xmlns = g_strdup(xmlns);
attrib_node->prefix = g_strdup(prefix);
- xmlnode_insert_child(node, attrib_node);
+ purple_xmlnode_insert_child(node, attrib_node);
}
const char *
-xmlnode_get_attrib(const xmlnode *node, const char *attr)
+purple_xmlnode_get_attrib(const PurpleXmlNode *node, const char *attr)
{
- xmlnode *x;
+ PurpleXmlNode *x;
g_return_val_if_fail(node != NULL, NULL);
g_return_val_if_fail(attr != NULL, NULL);
for(x = node->child; x; x = x->next) {
- if(x->type == XMLNODE_TYPE_ATTRIB && purple_strequal(attr, x->name)) {
+ if(x->type == PURPLE_XMLNODE_TYPE_ATTRIB && purple_strequal(attr, x->name)) {
return x->data;
}
}
@@ -240,15 +237,15 @@ xmlnode_get_attrib(const xmlnode *node, const char *attr)
}
const char *
-xmlnode_get_attrib_with_namespace(const xmlnode *node, const char *attr, const char *xmlns)
+purple_xmlnode_get_attrib_with_namespace(const PurpleXmlNode *node, const char *attr, const char *xmlns)
{
- const xmlnode *x;
+ const PurpleXmlNode *x;
g_return_val_if_fail(node != NULL, NULL);
g_return_val_if_fail(attr != NULL, NULL);
for(x = node->child; x; x = x->next) {
- if(x->type == XMLNODE_TYPE_ATTRIB &&
+ if(x->type == PURPLE_XMLNODE_TYPE_ATTRIB &&
purple_strequal(attr, x->name) &&
purple_strequal(xmlns, x->xmlns)) {
return x->data;
@@ -259,22 +256,56 @@ xmlnode_get_attrib_with_namespace(const xmlnode *node, const char *attr, const c
}
-void xmlnode_set_namespace(xmlnode *node, const char *xmlns)
+void purple_xmlnode_set_namespace(PurpleXmlNode *node, const char *xmlns)
{
+ char *tmp;
g_return_if_fail(node != NULL);
- g_free(node->xmlns);
+ tmp = node->xmlns;
node->xmlns = g_strdup(xmlns);
+
+ if (node->namespace_map) {
+ g_hash_table_insert(node->namespace_map,
+ g_strdup(""), g_strdup(xmlns));
+ }
+
+ g_free(tmp);
}
-const char *xmlnode_get_namespace(xmlnode *node)
+const char *purple_xmlnode_get_namespace(const PurpleXmlNode *node)
{
g_return_val_if_fail(node != NULL, NULL);
return node->xmlns;
}
-void xmlnode_set_prefix(xmlnode *node, const char *prefix)
+const char *purple_xmlnode_get_default_namespace(const PurpleXmlNode *node)
+{
+ const PurpleXmlNode *current_node;
+ const char *ns = NULL;
+
+ g_return_val_if_fail(node != NULL, NULL);
+
+ current_node = node;
+ while (current_node) {
+ /* If this node does *not* have a prefix, node->xmlns is the default
+ * namespace. Otherwise, it's the prefix namespace.
+ */
+ if (!current_node->prefix && current_node->xmlns) {
+ return current_node->xmlns;
+ } else if (current_node->namespace_map) {
+ ns = g_hash_table_lookup(current_node->namespace_map, "");
+ if (ns && *ns)
+ return ns;
+ }
+
+ current_node = current_node->parent;
+ }
+
+ return ns;
+}
+
+void purple_xmlnode_set_prefix(PurpleXmlNode *node, const char *prefix)
{
g_return_if_fail(node != NULL);
@@ -282,22 +313,69 @@ void xmlnode_set_prefix(xmlnode *node, const char *prefix)
node->prefix = g_strdup(prefix);
}
-const char *xmlnode_get_prefix(const xmlnode *node)
+const char *purple_xmlnode_get_prefix(const PurpleXmlNode *node)
{
g_return_val_if_fail(node != NULL, NULL);
return node->prefix;
}
-xmlnode *xmlnode_get_parent(const xmlnode *child)
+const char *purple_xmlnode_get_prefix_namespace(const PurpleXmlNode *node, const char *prefix)
+{
+ const PurpleXmlNode *current_node;
+
+ g_return_val_if_fail(node != NULL, NULL);
+ g_return_val_if_fail(prefix != NULL, purple_xmlnode_get_default_namespace(node));
+
+ current_node = node;
+ while (current_node) {
+ if (current_node->prefix && g_str_equal(prefix, current_node->prefix) &&
+ current_node->xmlns) {
+ return current_node->xmlns;
+ } else if (current_node->namespace_map) {
+ const char *ns = g_hash_table_lookup(current_node->namespace_map, prefix);
+ if (ns && *ns) {
+ return ns;
+ }
+ }
+
+ current_node = current_node->parent;
+ }
+
+ return NULL;
+}
+
+void purple_xmlnode_strip_prefixes(PurpleXmlNode *node)
+{
+ PurpleXmlNode *child;
+ const char *prefix;
+
+ g_return_if_fail(node != NULL);
+
+ for (child = node->child; child; child = child->next) {
+ if (child->type == PURPLE_XMLNODE_TYPE_TAG)
+ purple_xmlnode_strip_prefixes(child);
+ }
+
+ prefix = purple_xmlnode_get_prefix(node);
+ if (prefix) {
+ const char *ns = purple_xmlnode_get_prefix_namespace(node, prefix);
+ purple_xmlnode_set_namespace(node, ns);
+ purple_xmlnode_set_prefix(node, NULL);
+ } else {
+ purple_xmlnode_set_namespace(node, purple_xmlnode_get_default_namespace(node));
+ }
+}
+
+PurpleXmlNode *purple_xmlnode_get_parent(const PurpleXmlNode *child)
{
g_return_val_if_fail(child != NULL, NULL);
return child->parent;
}
void
-xmlnode_free(xmlnode *node)
+purple_xmlnode_free(PurpleXmlNode *node)
{
- xmlnode *x, *y;
+ PurpleXmlNode *x, *y;
g_return_if_fail(node != NULL);
@@ -308,7 +386,7 @@ xmlnode_free(xmlnode *node)
if (node->parent->lastchild == node)
node->parent->lastchild = node->next;
} else {
- xmlnode *prev = node->parent->child;
+ PurpleXmlNode *prev = node->parent->child;
while(prev && prev->next != node) {
prev = prev->next;
}
@@ -324,7 +402,7 @@ xmlnode_free(xmlnode *node)
x = node->child;
while(x) {
y = x->next;
- xmlnode_free(x);
+ purple_xmlnode_free(x);
x = y;
}
@@ -341,16 +419,16 @@ xmlnode_free(xmlnode *node)
g_free(node);
}
-xmlnode*
-xmlnode_get_child(const xmlnode *parent, const char *name)
+PurpleXmlNode*
+purple_xmlnode_get_child(const PurpleXmlNode *parent, const char *name)
{
- return xmlnode_get_child_with_namespace(parent, name, NULL);
+ return purple_xmlnode_get_child_with_namespace(parent, name, NULL);
}
-xmlnode *
-xmlnode_get_child_with_namespace(const xmlnode *parent, const char *name, const char *ns)
+PurpleXmlNode *
+purple_xmlnode_get_child_with_namespace(const PurpleXmlNode *parent, const char *name, const char *ns)
{
- xmlnode *x, *ret = NULL;
+ PurpleXmlNode *x, *ret = NULL;
char **names;
char *parent_name, *child_name;
@@ -365,9 +443,9 @@ xmlnode_get_child_with_namespace(const xmlnode *parent, const char *name, const
/* XXX: Is it correct to ignore the namespace for the match if none was specified? */
const char *xmlns = NULL;
if(ns)
- xmlns = xmlnode_get_namespace(x);
+ xmlns = purple_xmlnode_get_namespace(x);
- if(x->type == XMLNODE_TYPE_TAG && purple_strequal(parent_name, x->name)
+ if(x->type == PURPLE_XMLNODE_TYPE_TAG && purple_strequal(parent_name, x->name)
&& purple_strequal(ns, xmlns)) {
ret = x;
break;
@@ -375,22 +453,22 @@ xmlnode_get_child_with_namespace(const xmlnode *parent, const char *name, const
}
if(child_name && ret)
- ret = xmlnode_get_child(ret, child_name);
+ ret = purple_xmlnode_get_child(ret, child_name);
g_strfreev(names);
return ret;
}
char *
-xmlnode_get_data(const xmlnode *node)
+purple_xmlnode_get_data(const PurpleXmlNode *node)
{
GString *str = NULL;
- xmlnode *c;
+ PurpleXmlNode *c;
g_return_val_if_fail(node != NULL, NULL);
for(c = node->child; c; c = c->next) {
- if(c->type == XMLNODE_TYPE_DATA) {
+ if(c->type == PURPLE_XMLNODE_TYPE_DATA) {
if(!str)
str = g_string_new_len(c->data, c->data_sz);
else
@@ -405,9 +483,9 @@ xmlnode_get_data(const xmlnode *node)
}
char *
-xmlnode_get_data_unescaped(const xmlnode *node)
+purple_xmlnode_get_data_unescaped(const PurpleXmlNode *node)
{
- char *escaped = xmlnode_get_data(node);
+ char *escaped = purple_xmlnode_get_data(node);
char *unescaped = escaped ? purple_unescape_html(escaped) : NULL;
@@ -417,7 +495,7 @@ xmlnode_get_data_unescaped(const xmlnode *node)
}
static void
-xmlnode_to_str_foreach_append_ns(const char *key, const char *value,
+purple_xmlnode_to_str_foreach_append_ns(const char *key, const char *value,
GString *buf)
{
if (*key) {
@@ -428,11 +506,11 @@ xmlnode_to_str_foreach_append_ns(const char *key, const char *value,
}
static char *
-xmlnode_to_str_helper(const xmlnode *node, int *len, gboolean formatting, int depth)
+purple_xmlnode_to_str_helper(const PurpleXmlNode *node, int *len, gboolean formatting, int depth)
{
GString *text = g_string_new("");
const char *prefix;
- const xmlnode *c;
+ const PurpleXmlNode *c;
char *node_name, *esc, *esc2, *tab = NULL;
gboolean need_end = FALSE, pretty = formatting;
@@ -444,7 +522,7 @@ xmlnode_to_str_helper(const xmlnode *node, int *len, gboolean formatting, int de
}
node_name = g_markup_escape_text(node->name, -1);
- prefix = xmlnode_get_prefix(node);
+ prefix = purple_xmlnode_get_prefix(node);
if (prefix) {
g_string_append_printf(text, "<%s:%s", prefix, node_name);
@@ -454,19 +532,29 @@ xmlnode_to_str_helper(const xmlnode *node, int *len, gboolean formatting, int de
if (node->namespace_map) {
g_hash_table_foreach(node->namespace_map,
- (GHFunc)xmlnode_to_str_foreach_append_ns, text);
- } else if (node->xmlns) {
- if(!node->parent || !purple_strequal(node->xmlns, node->parent->xmlns))
+ (GHFunc)purple_xmlnode_to_str_foreach_append_ns, text);
+ } else {
+ /* Figure out if this node has a different default namespace from parent */
+ const char *xmlns = NULL;
+ const char *parent_xmlns = NULL;
+ if (!prefix)
+ xmlns = node->xmlns;
+
+ if (!xmlns)
+ xmlns = purple_xmlnode_get_default_namespace(node);
+ if (node->parent)
+ parent_xmlns = purple_xmlnode_get_default_namespace(node->parent);
+ if (!purple_strequal(xmlns, parent_xmlns))
{
- char *xmlns = g_markup_escape_text(node->xmlns, -1);
- g_string_append_printf(text, " xmlns='%s'", xmlns);
- g_free(xmlns);
+ char *escaped_xmlns = g_markup_escape_text(xmlns, -1);
+ g_string_append_printf(text, " xmlns='%s'", escaped_xmlns);
+ g_free(escaped_xmlns);
}
}
for(c = node->child; c; c = c->next)
{
- if(c->type == XMLNODE_TYPE_ATTRIB) {
- const char *aprefix = xmlnode_get_prefix(c);
+ if(c->type == PURPLE_XMLNODE_TYPE_ATTRIB) {
+ const char *aprefix = purple_xmlnode_get_prefix(c);
esc = g_markup_escape_text(c->name, -1);
esc2 = g_markup_escape_text(c->data, -1);
if (aprefix) {
@@ -476,8 +564,8 @@ xmlnode_to_str_helper(const xmlnode *node, int *len, gboolean formatting, int de
}
g_free(esc);
g_free(esc2);
- } else if(c->type == XMLNODE_TYPE_TAG || c->type == XMLNODE_TYPE_DATA) {
- if(c->type == XMLNODE_TYPE_DATA)
+ } 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;
}
@@ -488,12 +576,12 @@ xmlnode_to_str_helper(const xmlnode *node, int *len, gboolean formatting, int de
for(c = node->child; c; c = c->next)
{
- if(c->type == XMLNODE_TYPE_TAG) {
+ if(c->type == PURPLE_XMLNODE_TYPE_TAG) {
int esc_len;
- esc = xmlnode_to_str_helper(c, &esc_len, pretty, depth+1);
+ esc = purple_xmlnode_to_str_helper(c, &esc_len, pretty, depth+1);
text = g_string_append_len(text, esc, esc_len);
g_free(esc);
- } else if(c->type == XMLNODE_TYPE_DATA && c->data_sz > 0) {
+ } else if(c->type == PURPLE_XMLNODE_TYPE_DATA && c->data_sz > 0) {
esc = g_markup_escape_text(c->data, c->data_sz);
text = g_string_append(text, esc);
g_free(esc);
@@ -522,19 +610,19 @@ xmlnode_to_str_helper(const xmlnode *node, int *len, gboolean formatting, int de
}
char *
-xmlnode_to_str(const xmlnode *node, int *len)
+purple_xmlnode_to_str(const PurpleXmlNode *node, int *len)
{
- return xmlnode_to_str_helper(node, len, FALSE, 0);
+ return purple_xmlnode_to_str_helper(node, len, FALSE, 0);
}
char *
-xmlnode_to_formatted_str(const xmlnode *node, int *len)
+purple_xmlnode_to_formatted_str(const PurpleXmlNode *node, int *len)
{
char *xml, *xml_with_declaration;
g_return_val_if_fail(node != NULL, NULL);
- xml = xmlnode_to_str_helper(node, len, TRUE, 0);
+ xml = purple_xmlnode_to_str_helper(node, len, TRUE, 0);
xml_with_declaration =
g_strdup_printf("<?xml version='1.0' encoding='UTF-8' ?>" NEWLINE_S NEWLINE_S "%s", xml);
g_free(xml);
@@ -546,30 +634,30 @@ xmlnode_to_formatted_str(const xmlnode *node, int *len)
}
struct _xmlnode_parser_data {
- xmlnode *current;
+ PurpleXmlNode *current;
gboolean error;
};
static void
-xmlnode_parser_element_start_libxml(void *user_data,
+purple_xmlnode_parser_element_start_libxml(void *user_data,
const xmlChar *element_name, const xmlChar *prefix, const xmlChar *xmlns,
int nb_namespaces, const xmlChar **namespaces,
int nb_attributes, int nb_defaulted, const xmlChar **attributes)
{
struct _xmlnode_parser_data *xpd = user_data;
- xmlnode *node;
+ PurpleXmlNode *node;
int i, j;
if(!element_name || xpd->error) {
return;
} else {
if(xpd->current)
- node = xmlnode_new_child(xpd->current, (const char*) element_name);
+ node = purple_xmlnode_new_child(xpd->current, (const char*) element_name);
else
- node = xmlnode_new((const char *) element_name);
+ node = purple_xmlnode_new((const char *) element_name);
- xmlnode_set_namespace(node, (const char *) xmlns);
- xmlnode_set_prefix(node, (const char *)prefix);
+ purple_xmlnode_set_namespace(node, (const char *) xmlns);
+ purple_xmlnode_set_prefix(node, (const char *)prefix);
if (nb_namespaces != 0) {
node->namespace_map = g_hash_table_new_full(
@@ -592,7 +680,7 @@ xmlnode_parser_element_start_libxml(void *user_data,
txt = attrib;
attrib = purple_unescape_text(txt);
g_free(txt);
- xmlnode_set_attrib_full(node, name, NULL, prefix, attrib);
+ purple_xmlnode_set_attrib_full(node, name, NULL, prefix, attrib);
g_free(attrib);
}
@@ -601,7 +689,7 @@ xmlnode_parser_element_start_libxml(void *user_data,
}
static void
-xmlnode_parser_element_end_libxml(void *user_data, const xmlChar *element_name,
+purple_xmlnode_parser_element_end_libxml(void *user_data, const xmlChar *element_name,
const xmlChar *prefix, const xmlChar *xmlns)
{
struct _xmlnode_parser_data *xpd = user_data;
@@ -616,7 +704,7 @@ xmlnode_parser_element_end_libxml(void *user_data, const xmlChar *element_name,
}
static void
-xmlnode_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len)
+purple_xmlnode_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len)
{
struct _xmlnode_parser_data *xpd = user_data;
@@ -626,11 +714,11 @@ xmlnode_parser_element_text_libxml(void *user_data, const xmlChar *text, int tex
if(!text || !text_len)
return;
- xmlnode_insert_data(xpd->current, (const char*) text, text_len);
+ purple_xmlnode_insert_data(xpd->current, (const char*) text, text_len);
}
static void
-xmlnode_parser_error_libxml(void *user_data, const char *msg, ...)
+purple_xmlnode_parser_error_libxml(void *user_data, const char *msg, ...)
{
struct _xmlnode_parser_data *xpd = user_data;
char errmsg[2048];
@@ -646,28 +734,28 @@ xmlnode_parser_error_libxml(void *user_data, const char *msg, ...)
}
static void
-xmlnode_parser_structural_error_libxml(void *user_data, xmlErrorPtr error)
+purple_xmlnode_parser_structural_error_libxml(void *user_data, xmlErrorPtr error)
{
struct _xmlnode_parser_data *xpd = user_data;
if (error && (error->level == XML_ERR_ERROR ||
error->level == XML_ERR_FATAL)) {
xpd->error = TRUE;
- purple_debug_error("xmlnode", "XML parser error for xmlnode %p: "
+ purple_debug_error("xmlnode", "XML parser error for PurpleXmlNode %p: "
"Domain %i, code %i, level %i: %s",
user_data, error->domain, error->code, error->level,
error->message ? error->message : "(null)\n");
} else if (error)
- purple_debug_warning("xmlnode", "XML parser error for xmlnode %p: "
+ purple_debug_warning("xmlnode", "XML parser error for PurpleXmlNode %p: "
"Domain %i, code %i, level %i: %s",
user_data, error->domain, error->code, error->level,
error->message ? error->message : "(null)\n");
else
- purple_debug_warning("xmlnode", "XML parser error for xmlnode %p\n",
+ purple_debug_warning("xmlnode", "XML parser error for PurpleXmlNode %p\n",
user_data);
}
-static xmlSAXHandler xmlnode_parser_libxml = {
+static xmlSAXHandler purple_xmlnode_parser_libxml = {
NULL, /* internalSubset */
NULL, /* isStandalone */
NULL, /* hasInternalSubset */
@@ -685,65 +773,65 @@ static xmlSAXHandler xmlnode_parser_libxml = {
NULL, /* startElement */
NULL, /* endElement */
NULL, /* reference */
- xmlnode_parser_element_text_libxml, /* characters */
+ purple_xmlnode_parser_element_text_libxml, /* characters */
NULL, /* ignorableWhitespace */
NULL, /* processingInstruction */
NULL, /* comment */
NULL, /* warning */
- xmlnode_parser_error_libxml, /* error */
+ purple_xmlnode_parser_error_libxml, /* error */
NULL, /* fatalError */
NULL, /* getParameterEntity */
NULL, /* cdataBlock */
NULL, /* externalSubset */
XML_SAX2_MAGIC, /* initialized */
NULL, /* _private */
- xmlnode_parser_element_start_libxml, /* startElementNs */
- xmlnode_parser_element_end_libxml, /* endElementNs */
- xmlnode_parser_structural_error_libxml, /* serror */
+ purple_xmlnode_parser_element_start_libxml, /* startElementNs */
+ purple_xmlnode_parser_element_end_libxml, /* endElementNs */
+ purple_xmlnode_parser_structural_error_libxml, /* serror */
};
-xmlnode *
-xmlnode_from_str(const char *str, gssize size)
+PurpleXmlNode *
+purple_xmlnode_from_str(const char *str, gssize size)
{
struct _xmlnode_parser_data *xpd;
- xmlnode *ret;
+ PurpleXmlNode *ret;
gsize real_size;
g_return_val_if_fail(str != NULL, NULL);
- real_size = size < 0 ? strlen(str) : size;
+ real_size = size < 0 ? strlen(str) : (gsize)size;
xpd = g_new0(struct _xmlnode_parser_data, 1);
- if (xmlSAXUserParseMemory(&xmlnode_parser_libxml, xpd, str, real_size) < 0) {
+ if (xmlSAXUserParseMemory(&purple_xmlnode_parser_libxml, xpd, str, real_size) < 0) {
while(xpd->current && xpd->current->parent)
xpd->current = xpd->current->parent;
if(xpd->current)
- xmlnode_free(xpd->current);
+ purple_xmlnode_free(xpd->current);
xpd->current = NULL;
}
ret = xpd->current;
if (xpd->error) {
ret = NULL;
if (xpd->current)
- xmlnode_free(xpd->current);
+ purple_xmlnode_free(xpd->current);
}
g_free(xpd);
return ret;
}
-xmlnode *
-xmlnode_from_file(const char *dir,const char *filename, const char *description, const char *process)
+PurpleXmlNode *
+purple_xmlnode_from_file(const char *dir,const char *filename, const char *description, const char *process)
{
gchar *filename_full;
GError *error = NULL;
gchar *contents = NULL;
gsize length;
- xmlnode *node = NULL;
+ PurpleXmlNode *node = NULL;
g_return_val_if_fail(dir != NULL, NULL);
- purple_debug_info(process, "Reading file %s from directory %s\n",
+ purple_debug_misc(process, "Reading file %s from directory %s\n",
filename, dir);
filename_full = g_build_filename(dir, filename, NULL);
@@ -765,7 +853,7 @@ xmlnode_from_file(const char *dir,const char *filename, const char *description,
if ((contents != NULL) && (length > 0))
{
- node = xmlnode_from_str(contents, length);
+ node = purple_xmlnode_from_str(contents, length);
/* If we were unable to parse the file then save its contents to a backup file */
if (node == NULL)
@@ -794,7 +882,7 @@ xmlnode_from_file(const char *dir,const char *filename, const char *description,
msg = g_strdup_printf(_("An error was encountered reading your "
"%s. The file has not been loaded, and the old file "
"has been renamed to %s~."), description, filename_full);
- purple_notify_error(NULL, NULL, title, msg);
+ purple_notify_error(NULL, NULL, title, msg, NULL);
g_free(title);
g_free(msg);
}
@@ -805,18 +893,18 @@ xmlnode_from_file(const char *dir,const char *filename, const char *description,
}
static void
-xmlnode_copy_foreach_ns(gpointer key, gpointer value, gpointer user_data)
+purple_xmlnode_copy_foreach_ns(gpointer key, gpointer value, gpointer user_data)
{
GHashTable *ret = (GHashTable *)user_data;
g_hash_table_insert(ret, g_strdup(key), g_strdup(value));
}
-xmlnode *
-xmlnode_copy(const xmlnode *src)
+PurpleXmlNode *
+purple_xmlnode_copy(const PurpleXmlNode *src)
{
- xmlnode *ret;
- xmlnode *child;
- xmlnode *sibling = NULL;
+ PurpleXmlNode *ret;
+ PurpleXmlNode *child;
+ PurpleXmlNode *sibling = NULL;
g_return_val_if_fail(src != NULL, NULL);
@@ -834,15 +922,15 @@ xmlnode_copy(const xmlnode *src)
if (src->namespace_map) {
ret->namespace_map = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, g_free);
- g_hash_table_foreach(src->namespace_map, xmlnode_copy_foreach_ns, ret->namespace_map);
+ g_hash_table_foreach(src->namespace_map, purple_xmlnode_copy_foreach_ns, ret->namespace_map);
}
for (child = src->child; child; child = child->next) {
if (sibling) {
- sibling->next = xmlnode_copy(child);
+ sibling->next = purple_xmlnode_copy(child);
sibling = sibling->next;
} else {
- ret->child = sibling = xmlnode_copy(child);
+ ret->child = sibling = purple_xmlnode_copy(child);
}
sibling->parent = ret;
}
@@ -852,25 +940,39 @@ xmlnode_copy(const xmlnode *src)
return ret;
}
-xmlnode *
-xmlnode_get_next_twin(xmlnode *node)
+PurpleXmlNode *
+purple_xmlnode_get_next_twin(PurpleXmlNode *node)
{
- xmlnode *sibling;
- const char *ns = xmlnode_get_namespace(node);
+ PurpleXmlNode *sibling;
+ const char *ns = purple_xmlnode_get_namespace(node);
g_return_val_if_fail(node != NULL, NULL);
- g_return_val_if_fail(node->type == XMLNODE_TYPE_TAG, NULL);
+ g_return_val_if_fail(node->type == PURPLE_XMLNODE_TYPE_TAG, NULL);
for(sibling = node->next; sibling; sibling = sibling->next) {
/* XXX: Is it correct to ignore the namespace for the match if none was specified? */
const char *xmlns = NULL;
if(ns)
- xmlns = xmlnode_get_namespace(sibling);
+ xmlns = purple_xmlnode_get_namespace(sibling);
- if(sibling->type == XMLNODE_TYPE_TAG && purple_strequal(node->name, sibling->name) &&
+ if(sibling->type == PURPLE_XMLNODE_TYPE_TAG && purple_strequal(node->name, sibling->name) &&
purple_strequal(ns, xmlns))
return sibling;
}
return NULL;
}
+
+GType
+purple_xmlnode_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PurpleXmlNode",
+ (GBoxedCopyFunc)purple_xmlnode_copy,
+ (GBoxedFreeFunc)purple_xmlnode_free);
+ }
+
+ return type;
+}
diff --git a/libpurple/xmlnode.h b/libpurple/xmlnode.h
index 95b330cde1..13431562fd 100644
--- a/libpurple/xmlnode.h
+++ b/libpurple/xmlnode.h
@@ -1,8 +1,3 @@
-/**
- * @file xmlnode.h XML DOM functions
- * @ingroup core
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -23,334 +18,411 @@
* 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_XMLNODE_H_
#define _PURPLE_XMLNODE_H_
+/**
+ * SECTION:xmlnode
+ * @section_id: libpurple-xmlnode
+ * @short_description: <filename>xmlnode.h</filename>
+ * @title: XML DOM functions
+ */
#include <glib.h>
+#include <glib-object.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
+#define PURPLE_TYPE_XMLNODE (purple_xmlnode_get_type())
/**
- * The valid types for an xmlnode
+ * PurpleXmlNodeType:
+ * @PURPLE_XMLNODE_TYPE_TAG: Just a tag
+ * @PURPLE_XMLNODE_TYPE_ATTRIB: Has attributes
+ * @PURPLE_XMLNODE_TYPE_DATA: Has data
+ *
+ * The valid types for an PurpleXmlNode
*/
-typedef enum _XMLNodeType
+typedef enum
{
- XMLNODE_TYPE_TAG, /**< Just a tag */
- XMLNODE_TYPE_ATTRIB, /**< Has attributes */
- XMLNODE_TYPE_DATA /**< Has data */
-} XMLNodeType;
+ PURPLE_XMLNODE_TYPE_TAG,
+ PURPLE_XMLNODE_TYPE_ATTRIB,
+ PURPLE_XMLNODE_TYPE_DATA
+
+} PurpleXmlNodeType;
/**
- * An xmlnode.
- */
-typedef struct _xmlnode xmlnode;
-struct _xmlnode
+ * PurpleXmlNode:
+ * @name: The name of the node.
+ * @xmlns: The namespace of the node.
+ * @type: The type of the node.
+ * @data: The data for the node.
+ * @data_sz: The size of the data.
+ * @parent: The parent node or %NULL.
+ * @child: The child node or %NULL.
+ * @lastchild: The last child node or %NULL.
+ * @next: The next node or %NULL.
+ * @prefix: The namespace prefix if any.
+ * @namespace_map: The namespace map.
+ *
+ * An PurpleXmlNode.
+ */
+typedef struct _PurpleXmlNode PurpleXmlNode;
+struct _PurpleXmlNode
{
- char *name; /**< The name of the node. */
- char *xmlns; /**< The namespace of the node */
- XMLNodeType type; /**< The type of the node. */
- char *data; /**< The data for the node. */
- size_t data_sz; /**< The size of the data. */
- xmlnode *parent; /**< The parent node or @c NULL.*/
- xmlnode *child; /**< The child node or @c NULL.*/
- xmlnode *lastchild; /**< The last child node or @c NULL.*/
- xmlnode *next; /**< The next node or @c NULL. */
- char *prefix; /**< The namespace prefix if any. */
- GHashTable *namespace_map; /**< The namespace map. */
+ char *name;
+ char *xmlns;
+ PurpleXmlNodeType type;
+ char *data;
+ size_t data_sz;
+ PurpleXmlNode *parent;
+ PurpleXmlNode *child;
+ PurpleXmlNode *lastchild;
+ PurpleXmlNode *next;
+ char *prefix;
+ GHashTable *namespace_map;
};
+G_BEGIN_DECLS
+
/**
- * Creates a new xmlnode.
+ * purple_xmlnode_get_type:
*
- * @param name The name of the node.
+ * Returns: The #GType for the #PurpleXmlNode boxed structure.
+ */
+GType purple_xmlnode_get_type(void);
+
+/**
+ * purple_xmlnode_new:
+ * @name: The name of the node.
*
- * @return The new node.
+ * Creates a new PurpleXmlNode.
+ *
+ * Returns: The new node.
*/
-xmlnode *xmlnode_new(const char *name);
+PurpleXmlNode *purple_xmlnode_new(const char *name);
/**
- * Creates a new xmlnode child.
+ * purple_xmlnode_new_child:
+ * @parent: The parent node.
+ * @name: The name of the child node.
*
- * @param parent The parent node.
- * @param name The name of the child node.
+ * Creates a new PurpleXmlNode child.
*
- * @return The new child node.
+ * Returns: The new child node.
*/
-xmlnode *xmlnode_new_child(xmlnode *parent, const char *name);
+PurpleXmlNode *purple_xmlnode_new_child(PurpleXmlNode *parent, const char *name);
/**
- * Inserts a node into a node as a child.
+ * purple_xmlnode_insert_child:
+ * @parent: The parent node to insert child into.
+ * @child: The child node to insert into parent.
*
- * @param parent The parent node to insert child into.
- * @param child The child node to insert into parent.
+ * Inserts a node into a node as a child.
*/
-void xmlnode_insert_child(xmlnode *parent, xmlnode *child);
+void purple_xmlnode_insert_child(PurpleXmlNode *parent, PurpleXmlNode *child);
/**
- * Gets a child node named name.
+ * purple_xmlnode_get_child:
+ * @parent: The parent node.
+ * @name: The child's name.
*
- * @param parent The parent node.
- * @param name The child's name.
+ * Gets a child node named name.
*
- * @return The child or NULL.
+ * Returns: The child or NULL.
*/
-xmlnode *xmlnode_get_child(const xmlnode *parent, const char *name);
+PurpleXmlNode *purple_xmlnode_get_child(const PurpleXmlNode *parent, const char *name);
/**
- * Gets a child node named name in a namespace.
+ * purple_xmlnode_get_child_with_namespace:
+ * @parent: The parent node.
+ * @name: The child's name.
+ * @xmlns: The namespace.
*
- * @param parent The parent node.
- * @param name The child's name.
- * @param xmlns The namespace.
+ * Gets a child node named name in a namespace.
*
- * @return The child or NULL.
+ * Returns: The child or NULL.
*/
-xmlnode *xmlnode_get_child_with_namespace(const xmlnode *parent, const char *name, const char *xmlns);
+PurpleXmlNode *purple_xmlnode_get_child_with_namespace(const PurpleXmlNode *parent, const char *name, const char *xmlns);
/**
- * Gets the next node with the same name as node.
+ * purple_xmlnode_get_next_twin:
+ * @node: The node of a twin to find.
*
- * @param node The node of a twin to find.
+ * Gets the next node with the same name as node.
*
- * @return The twin of node or NULL.
+ * Returns: The twin of node or NULL.
*/
-xmlnode *xmlnode_get_next_twin(xmlnode *node);
+PurpleXmlNode *purple_xmlnode_get_next_twin(PurpleXmlNode *node);
/**
- * Inserts data into a node.
- *
- * @param node The node to insert data into.
- * @param data The data to insert.
- * @param size The size of the data to insert. If data is
+ * purple_xmlnode_insert_data:
+ * @node: The node to insert data into.
+ * @data: The data to insert.
+ * @size: The size of the data to insert. If data is
* null-terminated you can pass in -1.
+ *
+ * Inserts data into a node.
*/
-void xmlnode_insert_data(xmlnode *node, const char *data, gssize size);
+void purple_xmlnode_insert_data(PurpleXmlNode *node, const char *data, gssize size);
/**
- * Gets (escaped) data from a node.
+ * purple_xmlnode_get_data:
+ * @node: The node to get data from.
*
- * @param node The node to get data from.
+ * Gets (escaped) data from a node.
*
- * @return The data from the node or NULL. This data is in raw escaped format.
+ * Returns: The data from the node or NULL. This data is in raw escaped format.
* You must g_free this string when finished using it.
*/
-char *xmlnode_get_data(const xmlnode *node);
+char *purple_xmlnode_get_data(const PurpleXmlNode *node);
/**
- * Gets unescaped data from a node.
+ * purple_xmlnode_get_data_unescaped:
+ * @node: The node to get data from.
*
- * @param node The node to get data from.
+ * Gets unescaped data from a node.
*
- * @return The data from the node, in unescaped form. You must g_free
+ * Returns: The data from the node, in unescaped form. You must g_free
* this string when finished using it.
*/
-char *xmlnode_get_data_unescaped(const xmlnode *node);
+char *purple_xmlnode_get_data_unescaped(const PurpleXmlNode *node);
/**
- * Sets an attribute for a node.
+ * purple_xmlnode_set_attrib:
+ * @node: The node to set an attribute for.
+ * @attr: The name of the attribute.
+ * @value: The value of the attribute.
*
- * @param node The node to set an attribute for.
- * @param attr The name of the attribute.
- * @param value The value of the attribute.
+ * Sets an attribute for a node.
*/
-void xmlnode_set_attrib(xmlnode *node, const char *attr, const char *value);
+void purple_xmlnode_set_attrib(PurpleXmlNode *node, const char *attr, const char *value);
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_XMLNODE_C_)
/**
- * Sets a prefixed attribute for a node
- *
- * @param node The node to set an attribute for.
- * @param attr The name of the attribute to set
- * @param prefix The prefix of the attribute to ste
- * @param value The value of the attribute
+ * purple_xmlnode_set_attrib_full:
+ * @node: The node to set an attribute for.
+ * @attr: The name of the attribute to set
+ * @xmlns: The namespace of the attribute to set
+ * @prefix: The prefix of the attribute to set
+ * @value: The value of the attribute
*
- * @deprecated Use xmlnode_set_attrib_full instead.
+ * Sets a namespaced attribute for a node
*/
-void xmlnode_set_attrib_with_prefix(xmlnode *node, const char *attr, const char *prefix, const char *value);
+void purple_xmlnode_set_attrib_full(PurpleXmlNode *node, const char *attr, const char *xmlns,
+ const char *prefix, const char *value);
/**
- * Sets a namespaced attribute for a node
+ * purple_xmlnode_get_attrib:
+ * @node: The node to get an attribute from.
+ * @attr: The attribute to get.
*
- * @param node The node to set an attribute for.
- * @param attr The name of the attribute to set
- * @param xmlns The namespace of the attribute to ste
- * @param value The value of the attribute
+ * Gets an attribute from a node.
*
- * @deprecated Use xmlnode_set_attrib_full instead.
+ * Returns: The value of the attribute.
*/
-void xmlnode_set_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns, const char *value);
-#endif /* PURPLE_DISABLE_DEPRECATED */
+const char *purple_xmlnode_get_attrib(const PurpleXmlNode *node, const char *attr);
/**
- * Sets a namespaced attribute for a node
+ * purple_xmlnode_get_attrib_with_namespace:
+ * @node: The node to get an attribute from.
+ * @attr: The attribute to get
+ * @xmlns: The namespace of the attribute to get
*
- * @param node The node to set an attribute for.
- * @param attr The name of the attribute to set
- * @param xmlns The namespace of the attribute to ste
- * @param prefix The prefix of the attribute to ste
- * @param value The value of the attribute
+ * Gets a namespaced attribute from a node
*
- * @since 2.6.0
+ * Returns: The value of the attribute/
*/
-void xmlnode_set_attrib_full(xmlnode *node, const char *attr, const char *xmlns,
- const char *prefix, const char *value);
+const char *purple_xmlnode_get_attrib_with_namespace(const PurpleXmlNode *node, const char *attr, const char *xmlns);
/**
- * Gets an attribute from a node.
- *
- * @param node The node to get an attribute from.
- * @param attr The attribute to get.
+ * purple_xmlnode_remove_attrib:
+ * @node: The node to remove an attribute from.
+ * @attr: The attribute to remove.
*
- * @return The value of the attribute.
+ * Removes an attribute from a node.
*/
-const char *xmlnode_get_attrib(const xmlnode *node, const char *attr);
+void purple_xmlnode_remove_attrib(PurpleXmlNode *node, const char *attr);
/**
- * Gets a namespaced attribute from a node
- *
- * @param node The node to get an attribute from.
- * @param attr The attribute to get
- * @param xmlns The namespace of the attribute to get
+ * purple_xmlnode_remove_attrib_with_namespace:
+ * @node: The node to remove an attribute from
+ * @attr: The attribute to remove
+ * @xmlns: The namespace of the attribute to remove
*
- * @return The value of the attribute/
+ * Removes a namespaced attribute from a node
*/
-const char *xmlnode_get_attrib_with_namespace(const xmlnode *node, const char *attr, const char *xmlns);
+void purple_xmlnode_remove_attrib_with_namespace(PurpleXmlNode *node, const char *attr, const char *xmlns);
/**
- * Removes an attribute from a node.
+ * purple_xmlnode_set_namespace:
+ * @node: The node to qualify
+ * @xmlns: The namespace of the node
*
- * @param node The node to remove an attribute from.
- * @param attr The attribute to remove.
+ * Sets the namespace of a node
*/
-void xmlnode_remove_attrib(xmlnode *node, const char *attr);
+void purple_xmlnode_set_namespace(PurpleXmlNode *node, const char *xmlns);
/**
- * Removes a namespaced attribute from a node
+ * purple_xmlnode_get_namespace:
+ * @node: The node to get the namepsace from
*
- * @param node The node to remove an attribute from
- * @param attr The attribute to remove
- * @param xmlns The namespace of the attribute to remove
+ * Returns the namespace of a node
+ *
+ * Returns: The namespace of this node
*/
-void xmlnode_remove_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns);
+const char *purple_xmlnode_get_namespace(const PurpleXmlNode *node);
/**
- * Sets the namespace of a node
+ * purple_xmlnode_get_default_namespace:
+ * @node: The node for which to return the default namespace
*
- * @param node The node to qualify
- * @param xmlns The namespace of the node
+ * Returns the current default namespace. The default
+ * namespace is the current namespace which applies to child
+ * elements which are unprefixed and which do not contain their
+ * own namespace.
+ *
+ * For example, given:
+ * <programlisting>
+ * &lt;iq type='get' xmlns='jabber:client' xmlns:ns1='http://example.org/ns1'&gt;
+ * &lt;ns1:element&gt;&lt;child1/&gt;&lt;/ns1:element&gt;
+ * &lt;/iq&gt;
+ * </programlisting>
+ *
+ * The default namespace of all nodes (including 'child1') is "jabber:client",
+ * though the namespace for 'element' is "http://example.org/ns1".
+ *
+ * Returns: The default namespace of this node
*/
-void xmlnode_set_namespace(xmlnode *node, const char *xmlns);
+const char *purple_xmlnode_get_default_namespace(const PurpleXmlNode *node);
/**
- * Returns the namespace of a node
+ * purple_xmlnode_get_prefix_namespace:
+ * @node: The node from which to start the search.
+ * @prefix: The prefix for which to return the associated namespace.
+ *
+ * Returns the defined namespace for a prefix.
*
- * @param node The node to get the namepsace from
- * @return The namespace of this node
+ * Returns: The namespace for this prefix.
*/
-const char *xmlnode_get_namespace(xmlnode *node);
+const char *purple_xmlnode_get_prefix_namespace(const PurpleXmlNode *node, const char *prefix);
/**
- * Sets the prefix of a node
+ * purple_xmlnode_set_prefix:
+ * @node: The node to qualify
+ * @prefix: The prefix of the node
*
- * @param node The node to qualify
- * @param prefix The prefix of the node
+ * Sets the prefix of a node
*/
-void xmlnode_set_prefix(xmlnode *node, const char *prefix);
+void purple_xmlnode_set_prefix(PurpleXmlNode *node, const char *prefix);
/**
+ * purple_xmlnode_get_prefix:
+ * @node: The node to get the prefix from
+ *
* Returns the prefix of a node
*
- * @param node The node to get the prefix from
- * @return The prefix of this node
+ * Returns: The prefix of this node
*/
-const char *xmlnode_get_prefix(const xmlnode *node);
+const char *purple_xmlnode_get_prefix(const PurpleXmlNode *node);
/**
- * Gets the parent node.
+ * purple_xmlnode_strip_prefixes:
+ * @node: The node from which to strip prefixes
+ *
+ * Remove all element prefixes from an PurpleXmlNode tree. The prefix's
+ * namespace is transformed into the default namespace for an element.
*
- * @param child The child node.
+ * Note that this will not necessarily remove all prefixes in use
+ * (prefixed attributes may still exist), and that this usage may
+ * break some applications (SOAP / XPath apparently often rely on
+ * the prefixes having the same name.
+ */
+void purple_xmlnode_strip_prefixes(PurpleXmlNode *node);
+
+/**
+ * purple_xmlnode_get_parent:
+ * @child: The child node.
*
- * @return The parent or NULL.
+ * Gets the parent node.
*
- * @since 2.6.0
+ * Returns: The parent or NULL.
*/
-xmlnode *xmlnode_get_parent(const xmlnode *child);
+PurpleXmlNode *purple_xmlnode_get_parent(const PurpleXmlNode *child);
/**
- * Returns the node in a string of xml.
+ * purple_xmlnode_to_str:
+ * @node: The starting node to output.
+ * @len: Address for the size of the string.
*
- * @param node The starting node to output.
- * @param len Address for the size of the string.
+ * Returns the node in a string of xml.
*
- * @return The node represented as a string. You must
+ * Returns: The node represented as a string. You must
* g_free this string when finished using it.
*/
-char *xmlnode_to_str(const xmlnode *node, int *len);
+char *purple_xmlnode_to_str(const PurpleXmlNode *node, int *len);
/**
- * Returns the node in a string of human readable xml.
+ * purple_xmlnode_to_formatted_str:
+ * @node: The starting node to output.
+ * @len: Address for the size of the string.
*
- * @param node The starting node to output.
- * @param len Address for the size of the string.
+ * Returns the node in a string of human readable xml.
*
- * @return The node as human readable string including
+ * Returns: The node as human readable string including
* tab and new line characters. You must
* g_free this string when finished using it.
*/
-char *xmlnode_to_formatted_str(const xmlnode *node, int *len);
+char *purple_xmlnode_to_formatted_str(const PurpleXmlNode *node, int *len);
/**
+ * purple_xmlnode_from_str:
+ * @str: The string of xml.
+ * @size: The size of the string, or -1 if @str is
+ * NUL-terminated.
+ *
* Creates a node from a string of XML. Calling this on the
* root node of an XML document will parse the entire document
- * into a tree of nodes, and return the xmlnode of the root.
+ * into a tree of nodes, and return the PurpleXmlNode of the root.
*
- * @param str The string of xml.
- * @param size The size of the string, or -1 if @a str is
- * NUL-terminated.
- *
- * @return The new node.
+ * Returns: The new node.
*/
-xmlnode *xmlnode_from_str(const char *str, gssize size);
+PurpleXmlNode *purple_xmlnode_from_str(const char *str, gssize size);
/**
- * Creates a new node from the source node.
+ * purple_xmlnode_copy:
+ * @src: The node to copy.
*
- * @param src The node to copy.
+ * Creates a new node from the source node.
*
- * @return A new copy of the src node.
+ * Returns: A new copy of the src node.
*/
-xmlnode *xmlnode_copy(const xmlnode *src);
+PurpleXmlNode *purple_xmlnode_copy(const PurpleXmlNode *src);
/**
- * Frees a node and all of its children.
+ * purple_xmlnode_free:
+ * @node: The node to free.
*
- * @param node The node to free.
+ * Frees a node and all of its children.
*/
-void xmlnode_free(xmlnode *node);
+void purple_xmlnode_free(PurpleXmlNode *node);
/**
+ * purple_xmlnode_from_file:
+ * @dir: The directory where the file is located
+ * @filename: The filename
+ * @description: A description of the file being parsed. Displayed to
+ * the user if the file cannot be read.
+ * @process: The subsystem that is calling purple_xmlnode_from_file. Used as
+ * the category for debugging.
+ *
* Creates a node from a XML File. Calling this on the
* root node of an XML document will parse the entire document
- * into a tree of nodes, and return the xmlnode of the root.
- *
- * @param dir The directory where the file is located
- * @param filename The filename
- * @param description A description of the file being parsed. Displayed to
- * the user if the file cannot be read.
- * @param process The subsystem that is calling xmlnode_from_file. Used as
- * the category for debugging.
+ * into a tree of nodes, and return the PurpleXmlNode of the root.
*
- * @return The new node or NULL if an error occurred.
- *
- * @since 2.6.0
+ * Returns: The new node or NULL if an error occurred.
*/
-xmlnode *xmlnode_from_file(const char *dir, const char *filename,
- const char *description, const char *process);
+PurpleXmlNode *purple_xmlnode_from_file(const char *dir, const char *filename,
+ const char *description, const char *process);
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PURPLE_XMLNODE_H_ */
+
diff --git a/m4macros/Makefile.am b/m4macros/Makefile.am
index 03a8f32840..ff5da09b79 100644
--- a/m4macros/Makefile.am
+++ b/m4macros/Makefile.am
@@ -1,6 +1,6 @@
installed_m4=purple.m4
-EXTRA_DIST=$(installed_m4)
+EXTRA_DIST=$(installed_m4) as-ac-expand.m4
m4datadir=$(datadir)/aclocal
m4data_DATA=$(installed_m4)
diff --git a/m4macros/as-ac-expand.m4 b/m4macros/as-ac-expand.m4
new file mode 100644
index 0000000000..deebd2bf81
--- /dev/null
+++ b/m4macros/as-ac-expand.m4
@@ -0,0 +1,49 @@
+dnl as-ac-expand.m4 0.2.0 -*- autoconf -*-
+dnl autostars m4 macro for expanding directories using configure's prefix
+
+dnl (C) 2003, 2004, 2005 Thomas Vander Stichele <thomas at apestaart dot org>
+
+dnl Copying and distribution of this file, with or without modification,
+dnl are permitted in any medium without royalty provided the copyright
+dnl notice and this notice are preserved.
+
+dnl AS_AC_EXPAND(VAR, CONFIGURE_VAR)
+
+dnl example:
+dnl AS_AC_EXPAND(SYSCONFDIR, $sysconfdir)
+dnl will set SYSCONFDIR to /usr/local/etc if prefix=/usr/local
+
+AC_DEFUN([AS_AC_EXPAND],
+[
+ EXP_VAR=[$1]
+ FROM_VAR=[$2]
+
+ dnl first expand prefix and exec_prefix if necessary
+ prefix_save=$prefix
+ exec_prefix_save=$exec_prefix
+
+ dnl if no prefix given, then use /usr/local, the default prefix
+ if test "x$prefix" = "xNONE"; then
+ prefix="$ac_default_prefix"
+ fi
+ dnl if no exec_prefix given, then use prefix
+ if test "x$exec_prefix" = "xNONE"; then
+ exec_prefix=$prefix
+ fi
+
+ full_var="$FROM_VAR"
+ dnl loop until it doesn't change anymore
+ while true; do
+ new_full_var="`eval echo $full_var`"
+ if test "x$new_full_var" = "x$full_var"; then break; fi
+ full_var=$new_full_var
+ done
+
+ dnl clean up
+ full_var=$new_full_var
+ AC_SUBST([$1], "$full_var")
+
+ dnl restore prefix and exec_prefix
+ prefix=$prefix_save
+ exec_prefix=$exec_prefix_save
+])
diff --git a/m4macros/purple.m4 b/m4macros/purple.m4
index c3002a7ca2..543bbfa89d 100644
--- a/m4macros/purple.m4
+++ b/m4macros/purple.m4
@@ -84,7 +84,7 @@ AC_DEFUN([AM_PATH_PURPLE],
if test x"found_version" != x"" ; then
echo "*** A new enough version of purple was not found."
echo "*** You have version $found_version"
- echo "*** See http://pidgin.im/"
+ echo "*** See https://pidgin.im/"
fi
PURPLE_CFLAGS=""
diff --git a/pidgin.apspec.in b/pidgin.apspec.in
index f6478847c7..2aed4535a3 100644
--- a/pidgin.apspec.in
+++ b/pidgin.apspec.in
@@ -5,7 +5,7 @@ RootName: @pidgin.im/pidgin:$SOFTWAREVERSION
DisplayName: Pidgin Internet Messenger
ShortName: Pidgin
Maintainer: The Pidgin Developers
-URL: http://pidgin.im/
+URL: https://pidgin.im/
Packager: Tim Ringenbach <marv_sf@users.sourceforge.net>
Summary: A GTK+ based multiprotocol instant messaging client
SoftwareVersion: @VERSION@
@@ -44,7 +44,6 @@ echo '*' | import
require @gtk.org/gtk 2.2
require @68k.org/libaudiofile 0.0
require @xiph.org/libao 2.0
-require @gtkspell.sourceforge.net/gtkspell 0.0
require @gnutls.org/gnutls 11.0 || { require @mozilla.org/nspr 4 && require @mozilla.org/nss 3; }
[Install]
diff --git a/pidgin.spec.in b/pidgin.spec.in
deleted file mode 100644
index 9e1e15f321..0000000000
--- a/pidgin.spec.in
+++ /dev/null
@@ -1,671 +0,0 @@
-# Older RPM doesn't define these by default
-%{!?perl_vendorlib: %define perl_vendorlib %(eval "`%{__perl} -V:installvendorlib`"; echo $installvendorlib)}
-%{!?perl_vendorarch: %define perl_vendorarch %(eval "`%{__perl} -V:installvendorarch`"; echo $installvendorarch)}
-%{!?perl_archlib: %define perl_archlib %(eval "`%{__perl} -V:installarchlib`"; echo $installarchlib)}
-
-# When not doing betas comment this out
-# NOTE: %defines in spec files are evaluated in comments so the correct
-# way to comment it out is to replace the % with #
-#define beta 7
-
-%if 0%{?beta}
-%define pidginver %(echo "@VERSION@"|sed -e 's/dev.*//; s/beta.*//')
-%else
-%define pidginver @VERSION@
-%endif
-
-# define the minimum API version required, so we can use it for plugin deps
-%define apiver %(echo "@VERSION@"|awk -F. '{print $1"."$2}')
-
-Summary: A GTK+ based multiprotocol instant messaging client
-Name: @PACKAGE@
-Version: %pidginver
-Release: 0%{?beta:.beta%{beta}}
-License: GPL
-Group: Applications/Internet
-URL: http://pidgin.im/
-Source: %{name}-@VERSION@.tar.bz2
-BuildRoot: %{_tmppath}/%{name}-%{version}-root
-
-# Generic build requirements
-BuildRequires: libtool, pkgconfig, intltool, gettext, libxml2-devel
-BuildRequires: gtk2-devel, libidn-devel
-
-%{!?_without_startupnotification:BuildRequires: startup-notification-devel}
-%{?_with_avahi:BuildRequires: avahi-glib-devel}
-%{!?_without_gtkspell:BuildRequires: gtkspell-devel}
-%{?_with_meanwhile:BuildRequires: meanwhile-devel}
-%{?_with_mono:BuildRequires: mono-devel}
-%{?_with_sasl:BuildRequires: cyrus-sasl-devel >= 2}
-%{!?_without_silc:BuildRequires: /usr/include/silc/silcclient.h}
-%{!?_without_tcl:BuildRequires: tcl, tk, /usr/include/tcl.h}
-%{!?_without_text:BuildRequires: ncurses-devel}
-%{!?_without_nm:BuildRequires: NetworkManager-devel}
-%{!?_without_gevolution:BuildRequires: evolution-data-server-devel}
-
-%if "%{_vendor}" == "suse"
-# For SuSE:
-BuildRequires: gnutls-devel
-%define sslopts --enable-gnutls=yes --enable-nss=no
-%{?_with_dbus:BuildRequires: dbus-1-devel >= 0.35}
-%{!?_without_gstreamer:BuildRequires: gstreamer010-devel >= 0.10}
-Requires(pre): gconf2
-Requires(post): gconf2
-Requires(preun): gconf2
-%else
-%define sslopts --enable-gnutls=no --enable-nss=yes
-%{?_with_dbus:BuildRequires: dbus-devel >= 0.35}
-%{!?_without_gstreamer:BuildRequires: gstreamer-devel >= 0.10}
-Requires(pre): GConf2
-Requires(post): GConf2
-Requires(preun): GConf2
-%endif
-
-# Mandrake 10.1 and lower || Mandrake 10.2 (and higher?)
-%if "%{_vendor}" == "MandrakeSoft" || "%{_vendor}" == "Mandrakesoft" || "%{_vendor}" == "Mandriva"
-# For Mandrake/Mandriva:
-BuildRequires: libnss3-devel, perl-devel
-Obsoletes: libgaim-remote0
-%{!?_without_modularx:BuildRequires: libsm-devel, libxscrnsaver-devel}
-%else
-# For !Mandriva
-%{!?_without_modularx:BuildRequires: libSM-devel, libXScrnSaver-devel}
-# For SuSE, Red Hat, Fedora and others:
-%if "%{_vendor}" != "suse"
-# For Red Hat, Fedora and others:
-# let's assume RH & FC1 are the only brain-dead distros missing the
-# perl-XML-Parser dependency on intltool and that other RH/FC releases
-# don't care if we specify it here
-BuildRequires: perl-XML-Parser
-BuildRequires: mozilla-nss-devel
-%endif
-%endif
-
-# For some reason perl isn't always automatically detected as a requirement :(
-Requires: perl
-
-Requires: libpurple = %{version}
-
-Obsoletes: gaim
-Provides: gaim
-Obsoletes: pidgin-perl < %{version}
-Provides: pidgin-perl = %{version}-%{release}
-
-%package devel
-Summary: Development headers, documentation, and libraries for Pidgin
-Group: Applications/Internet
-Requires: pidgin = %{version}, libpurple-devel = %{version}
-Requires: gtk2-devel
-Requires: pkgconfig
-Obsoletes: gaim-devel
-Provides: gaim-devel
-
-%package -n libpurple
-Summary: libpurple library for IM clients like Pidgin and Finch
-Group: Applications/Internet
-Obsoletes: gaim-silc
-Obsoletes: gaim-tcl
-Obsoletes: gaim-gadugadu
-Obsoletes: pidgin-tcl < 2.0.0
-Obsoletes: pidgin-silc < 2.0.0
-Obsoletes: libpurple-perl < %{version}
-Provides: libpurple-perl = %{version}-%{release}
-%{?_with_sasl:Requires: cyrus-sasl-plain, cyrus-sasl-md5}
-
-%package -n libpurple-devel
-Summary: Development headers, documentation, and libraries for libpurple
-Group: Applications/Internet
-Requires: libpurple = %{version}
-Requires: pkgconfig
-%if "%{_vendor}" == "suse"
-# For SuSE:
-%{?_with_dbus:Requires: dbus-1-devel >= 0.35}
-%else
-%{?_with_dbus:Requires: dbus-devel >= 0.35}
-%endif
-
-%if 0%{?_with_avahi:1}
-%package -n libpurple-bonjour
-Summary: Bonjour plugin for Pidgin
-Group: Applications/Internet
-Requires: libpurple >= %{apiver}
-%endif
-
-%if 0%{?_with_meanwhile:1}
-%package -n libpurple-meanwhile
-Summary: Lotus Sametime plugin for Pidgin using the Meanwhile library
-Group: Applications/Internet
-Requires: libpurple >= %{apiver}
-%endif
-
-%if 0%{?_with_mono:1}
-%package -n libpurple-mono
-Summary: Mono .NET plugin support for Pidgin
-Group: Applications/Internet
-Requires: libpurple >= %{apiver}
-%endif
-
-%if 0%{!?_without_text:1}
-%package -n finch
-Summary: A text-based user interface for Pidgin
-Group: Applications/Internet
-Requires: libpurple = %{version}
-
-%package -n finch-devel
-Summary: Headers etc. for finch stuffs
-Group: Applications/Internet
-Requires: finch = %{version}, libpurple-devel = %{version}
-Requires: ncurses-devel
-Requires: pkgconfig
-%endif
-
-%description
-Pidgin allows you to talk to anyone using a variety of messaging
-protocols including AIM, MSN, Yahoo!, XMPP, Bonjour, Gadu-Gadu,
-ICQ, IRC, Novell Groupwise, QQ, Lotus Sametime, SILC, Simple and
-Zephyr. These protocols are implemented using a modular, easy to
-use design. To use a protocol, just add an account using the
-account editor.
-
-Pidgin supports many common features of other clients, as well as many
-unique features, such as perl scripting, TCL scripting and C plugins.
-
-Pidgin is not affiliated with or endorsed by America Online, Inc.,
-Microsoft Corporation, Yahoo! Inc., or ICQ Inc.
-
-%description devel
-The pidgin-devel package contains the header files, developer
-documentation, and libraries required for development of Pidgin scripts
-and plugins.
-
-%description -n libpurple
-libpurple contains the core IM support for IM clients such as Pidgin
-and Finch.
-
-libpurple supports a variety of messaging protocols including AIM, MSN,
-Yahoo!, XMPP, Bonjour, Gadu-Gadu, ICQ, IRC, Novell Groupwise, QQ,
-Lotus Sametime, SILC, Simple and Zephyr.
-
-%description -n libpurple-devel
-The libpurple-devel package contains the header files, developer
-documentation, and libraries required for development of libpurple based
-instant messaging clients or plugins for any libpurple based client.
-
-%if 0%{?_with_avahi:1}
-%description -n libpurple-bonjour
-Bonjour plugin for Pidgin.
-%endif
-
-%if 0%{?_with_meanwhile:1}
-%description -n libpurple-meanwhile
-Lotus Sametime plugin for Pidgin using the Meanwhile library.
-%endif
-
-%if 0%{?_with_mono:1}
-%description -n libpurple-mono
-Mono plugin loader for Pidgin. This package will allow you to write or
-use Pidgin plugins written in the .NET programming language.
-%endif
-
-%if 0%{!?_without_text:1}
-%description -n finch
-A text-based user interface for using libpurple. This can be run from a
-standard text console or from a terminal within X Windows. It
-uses ncurses and our homegrown gnt library for drawing windows
-and text.
-
-%description -n finch-devel
-The finch-devel package contains the header files, developer
-documentation, and libraries required for development of Finch scripts
-and plugins.
-%endif
-
-%prep
-%setup -q -n %{name}-@VERSION@
-
-%build
-CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=%{_prefix} \
- --bindir=%{_bindir} \
- --datadir=%{_datadir} \
- --includedir=%{_includedir} \
- --libdir=%{_libdir} \
- --mandir=%{_mandir} \
- --sysconfdir=%{_sysconfdir} \
- --disable-schemas-install \
- %{sslopts} \
- %{!?_with_vv:--disable-vv} \
- %{!?_with_dbus:--disable-dbus} \
- %{!?_with_avahi:--disable-avahi} \
- %{!?_with_meanwhile:--disable-meanwhile} \
- %{?_without_gstreamer:--disable-gstreamer} \
- %{?_without_gtkspell:--disable-gtkspell} \
- %{?_without_nm:--disable-nm} \
- %{!?_without_gevolution:--enable-gevolution} \
- %{?_with_mono:--enable-mono} \
- %{?_with_sasl:--enable-cyrus-sasl} \
- %{?_without_tcl:--disable-tcl} \
- %{?_without_text:--disable-consoleui} \
- %{?_with_trayiconcompat:--enable-trayicon-compat}
-
-make %{?_smp_mflags} LIBTOOL=/usr/bin/libtool
-
-%install
-rm -rf %{buildroot}
-make DESTDIR=$RPM_BUILD_ROOT LIBTOOL=/usr/bin/libtool install
-
-# Delete files that we don't want to put in any of the RPMs
-rm -f $RPM_BUILD_ROOT%{_libdir}/finch/*.la
-rm -f $RPM_BUILD_ROOT%{_libdir}/gnt/*.la
-rm -f $RPM_BUILD_ROOT%{_libdir}/pidgin/*.la
-rm -f $RPM_BUILD_ROOT%{_libdir}/purple-2/*.la
-rm -f $RPM_BUILD_ROOT%{_libdir}/purple-2/liboscar.so
-rm -f $RPM_BUILD_ROOT%{_libdir}/purple-2/libjabber.so
-rm -f $RPM_BUILD_ROOT%{_libdir}/purple-2/libymsg.so
-rm -f $RPM_BUILD_ROOT%{_libdir}/*.la
-rm -f $RPM_BUILD_ROOT%{perl_archlib}/perllocal.pod
-find $RPM_BUILD_ROOT -type f -name '*.a' -exec rm -f {} ';'
-find $RPM_BUILD_ROOT -type f -name .packlist -exec rm -f {} ';'
-find $RPM_BUILD_ROOT -type f -name '*.bs' -empty -exec rm -f {} ';'
-
-%if 0%{!?_with_avahi:1}
-rm -f $RPM_BUILD_ROOT%{_libdir}/purple-2/libbonjour.so
-%endif
-
-%if 0%{!?_with_meanwhile:1}
-rm -f $RPM_BUILD_ROOT%{_libdir}/purple-2/libsametime.so
-%endif
-
-%if 0%{!?_with_mono:1}
-rm -f $RPM_BUILD_ROOT%{_libdir}/purple-2/mono.so
-rm -f $RPM_BUILD_ROOT%{_libdir}/purple-2/*.dll
-%endif
-
-%if 0%{?_without_silc:1}
-rm -f $RPM_BUILD_ROOT%{_libdir}/purple-2/libsilcpurple.so
-%endif
-
-%if 0%{?_without_tcl:1}
-rm -f $RPM_BUILD_ROOT%{_libdir}/purple-2/tcl.so
-%endif
-
-%if 0%{?_without_text:1}
-rm -f $RPM_BUILD_ROOT%{_mandir}/man1/finch.*
-rm -rf $RPM_BUILD_ROOT%{_bindir}/finch
-rm -f $RPM_BUILD_ROOT%{_libdir}/libgnt.so.*
-%endif
-
-%find_lang %{name}
-
-find $RPM_BUILD_ROOT%{_libdir}/purple-2 -xtype f -print | \
- sed "s@^$RPM_BUILD_ROOT@@g" | \
- grep -v /libbonjour.so | \
- grep -v /libsametime.so | \
- grep -v /mono.so | \
- grep -v ".dll$" > %{name}-%{version}-purpleplugins
-
-find $RPM_BUILD_ROOT%{_libdir}/pidgin -xtype f -print | \
- sed "s@^$RPM_BUILD_ROOT@@g" > %{name}-%{version}-pidginplugins
-
-find $RPM_BUILD_ROOT%{_libdir}/finch -xtype f -print | \
- sed "s@^$RPM_BUILD_ROOT@@g" > %{name}-%{version}-finchplugins
-
-# files -f file can only take one filename :(
-cat %{name}.lang >> %{name}-%{version}-purpleplugins
-
-%clean
-rm -rf %{buildroot}
-
-%pre
-if [ "$1" -gt 1 -a -n "`which gconftool-2 2>/dev/null`" ]; then
- export GCONF_CONFIG_SOURCE=`gconftool-2 --get-default-source`
- if [ -f %{_sysconfdir}/gconf/schemas/purple.schemas ]; then
- gconftool-2 --makefile-uninstall-rule \
- %{_sysconfdir}/gconf/schemas/purple.schemas >/dev/null || :
- killall -HUP gconfd-2 &> /dev/null || :
- fi
-fi
-
-%post
-if [ -n "`which gconftool-2 2>/dev/null`" ]; then
- export GCONF_CONFIG_SOURCE=`gconftool-2 --get-default-source`
- gconftool-2 --makefile-install-rule \
- %{_sysconfdir}/gconf/schemas/purple.schemas > /dev/null || :
- killall -HUP gconfd-2 &> /dev/null || :
-fi
-touch --no-create %{_datadir}/icons/hicolor || :
-if [ -x %{_bindir}/gtk-update-icon-cache ]; then
- %{_bindir}/gtk-update-icon-cache --quiet %{_datadir}/icons/hicolor &> /dev/null || :
-fi
-
-%post -n libpurple -p /sbin/ldconfig
-
-%post -n finch -p /sbin/ldconfig
-
-%preun
-if [ "$1" -eq 0 -a -n "`which gconftool-2 2>/dev/null`" ]; then
- export GCONF_CONFIG_SOURCE=`gconftool-2 --get-default-source`
- gconftool-2 --makefile-uninstall-rule \
- %{_sysconfdir}/gconf/schemas/purple.schemas > /dev/null || :
- killall -HUP gconfd-2 &> /dev/null || :
-fi
-
-%postun
-touch --no-create %{_datadir}/icons/hicolor || :
-if [ -x %{_bindir}/gtk-update-icon-cache ]; then
- %{_bindir}/gtk-update-icon-cache --quiet %{_datadir}/icons/hicolor &> /dev/null || :
-fi
-
-%postun -n libpurple -p /sbin/ldconfig
-
-%postun -n finch -p /sbin/ldconfig
-
-%files -f %{name}-%{version}-pidginplugins
-%defattr(-, root, root)
-
-%doc AUTHORS
-%doc COPYING
-%doc COPYRIGHT
-%doc ChangeLog
-%doc NEWS
-%doc README
-%doc README.hg
-%doc doc/the_penguin.txt
-%doc %{_mandir}/man1/pidgin.*
-%doc %{_mandir}/man3*/*
-
-%dir %{_libdir}/pidgin
-%dir %{_libdir}/pidgin/perl
-%dir %{_libdir}/pidgin/perl/auto
-%dir %{_libdir}/pidgin/perl/auto/Pidgin
-%{_bindir}/pidgin
-%{_datadir}/pixmaps/pidgin
-%{_datadir}/icons/hicolor/*/apps/pidgin.*
-%{_datadir}/applications/*
-%{_sysconfdir}/gconf/schemas/purple.schemas
-
-
-%files -f %{name}-%{version}-purpleplugins -n libpurple
-%defattr(-, root, root)
-
-%{_libdir}/libpurple.so.*
-%dir %{_libdir}/purple-2
-%dir %{_libdir}/purple-2/perl
-%dir %{_libdir}/purple-2/perl/auto
-%dir %{_libdir}/purple-2/perl/auto/Purple
-%{_datadir}/purple
-%{_datadir}/sounds/purple
-
-%if 0%{?_with_dbus:1}
-%{_bindir}/purple-client-example
-%{_bindir}/purple-remote
-%{_bindir}/purple-send
-%{_bindir}/purple-send-async
-%{_bindir}/purple-url-handler
-%{_libdir}/libpurple-client.so.*
-%doc libpurple/purple-notifications-example
-%endif
-
-%files devel
-%defattr(-, root, root)
-%dir %{_includedir}/pidgin
-%{_includedir}/pidgin/*.h
-%{_libdir}/pkgconfig/pidgin.pc
-
-%files -n libpurple-devel
-%defattr(-, root, root)
-
-%doc ChangeLog.API
-%doc HACKING
-%doc PLUGIN_HOWTO
-
-%dir %{_includedir}/libpurple
-%{_includedir}/libpurple/*
-%{_libdir}/libpurple.so
-%{_libdir}/pkgconfig/purple.pc
-%{_datadir}/aclocal/purple.m4
-%if 0%{?_with_dbus:1}
-%{_libdir}/libpurple-client.so
-%endif
-
-
-%if 0%{?_with_avahi:1}
-%files -n libpurple-bonjour
-%defattr(-, root, root)
-
-%{_libdir}/purple-2/libbonjour.*
-%endif
-
-%if 0%{?_with_meanwhile:1}
-%files -n libpurple-meanwhile
-%defattr(-, root, root)
-
-%{_libdir}/purple-2/libsametime.*
-%endif
-
-%if 0%{?_with_mono:1}
-%files -n libpurple-mono
-%defattr(-, root, root)
-
-%{_libdir}/purple-2/mono.so
-%{_libdir}/purple-2/*.dll
-%endif
-
-%if 0%{!?_without_text:1}
-%files -f %{name}-%{version}-finchplugins -n finch
-%defattr(-, root, root)
-
-%doc %{_mandir}/man1/finch.*
-%{_bindir}/finch
-%{_libdir}/libgnt.so.*
-%{_libdir}/gnt/irssi.so
-%{_libdir}/gnt/s.so
-
-%files -n finch-devel
-%defattr(-, root, root)
-%dir %{_includedir}/finch
-%{_includedir}/finch/*.h
-# libgnt
-%dir %{_includedir}/gnt
-%{_includedir}/gnt/*.h
-%{_libdir}/pkgconfig/finch.pc
-%{_libdir}/pkgconfig/gnt.pc
-%{_libdir}/libgnt.so
-
-%endif
-
-%changelog
-* Wed Sep 01 2010 Stu Tomlinson <stu@nosnilmot.com>
-- Ensure predictable use of SSL libs
-
-* Wed Jun 02 2010 Stu Tomlinson <stu@nosnilmot.com>
-- add an option to build RPMs using --enable-trayicon-compat
- (--with trayiconcompat)
-
-* Thu May 13 2010 Stu Tomlinson <stu@nosnilmot.com>
-- Include all libpurple headers in libpurple-devel
-
-* Sat Sep 05 2009 Stu Tomlinson <stu@nosnilmot.com>
-- Disable Voice & Video unless --with vv is used
-- Add BuildRequires for libidn-devel
-- Add Provides/Obsoletes to ease transition from Red Hat / Fedora RPMs
-
-* Sat Jul 11 2009 Stu Tomlinson <stu@nosnilmot.com>
-- Update to reflect changes in perl module installation directories
-
-* Mon May 19 2008 Stu Tomlinson <stu@nosnilmot.com>
-- Fix building without meanwhile support
-
-* Fri May 16 2008 Stu Tomlinson <stu@nosnilmot.com>
-- Add "--without nm" support to build without NetworkManager
-
-* Thu Feb 28 2008 Stu Tomlinson <stu@nosnilmot.com>
-- Remove --with-howl options as we no longer support using howl for bonjour
-
-* Wed Dec 5 2007 Stu Tomlinson <stu@nosnilmot.com>
-- When building with avahi, use native avahi instead of howl compatability
- headers
-- Make the split out plugins depend only on the minimum necessary API
- version of libpurple
-
-* Tue Oct 23 2007 Stu Tomlinson <stu@nosnilmot.com>
-- Add finch.pc to finch-devel
-
-* Mon Sep 17 2007 Stu Tomlinson <stu@nosnilmot.com>
-- Add version dependency on libpurple for pidgin
-- Support for OpenSuse lowercase package name for GConf2
-
-* Fri Sep 14 2007 Stu Tomlinson <stu@nosnilmot.com>
-- Fix spec file for moved sounds & new CA certificates
-
-* Thu Jul 12 2007 Stu Tomlinson <stu@nosnilmot.com>
-- Don't hardcode silc header locations, rely on pkg-config for those,
- because I think I broke non-pkg-config detection of older silc
- toolkit.
-
-* Tue Jun 5 2007 Stu Tomlinson <stu@nosnilmot.com>
-- Add missing Requires for gtk2-devel, dbus-devel & ncurses-devel to
- appropriate -devel subpackages
-
-* Sun May 27 2007 Stu Tomlinson <stu@nosnilmot.com>
-- add cyrus-sasl-plain & cyrus-sasl-md5 to Requires
-
-* Thu May 24 2007 Stu Tomlinson <stu@nosnilmot.com>
-- Silence errors from gtk-update-icon-cache
-- Change Mandriva build dependencies to reflect the correct (lower case)
- names for libSM-devel & libXScrnSaver-devel (Sunny Dubey)
-
-* Thu May 10 2007 Stu Tomlinson <stu@nosnilmot.com>
-- Add scriptlet Requires for GConf2 to fix schema installation
-- Silence harmless errors when gconfd-2 is not running at install time
-
-* Thu May 3 2007 Stu Tomlinson <stu@nosnilmot.com>
-- Add missing BuildRequires: startup-notification-devel, if you really
- need to build on a distro without it use --without startupnotification
-- Add BuildRequires: libSM-devel, libXScrnSaver-devel for distros with
- modular X. For those without, build with --without modularx
-- Change Mandriva BuildRequires to gkt2-devel (reported by Götz Waschk)
-
-* Tue May 1 2007 Stu Tomlinson <stu@nosnilmot.com>
-- Run gtk-update-icon-cache on installation/uninstallation
-- Guard against errors when upgrading from Gaim/Pidgin 1.5.x which had
- no schemas file
-
-* Sun Apr 29 2007 Stu Tomlinson <stu@nosnilmot.com>
-- Update to reflect perl Purple::GtkUI -> Pidgin change
-
-* Wed Apr 25 2007 Stu Tomlinson <stu@nosnilmot.com>
-- Update libpurple to pick up plugins in %%{_libdir}/purple
-
-* Sun Apr 22 2007 Stu Tomlinson <stu@nosnilmot.com>
-- Remove Epoch because it's gone in Fedora now
-- Add virtual provides for gaim & gaim-devel
-
-* Thu Apr 19 2007 Stu Tomlinson <stu@nosnilmot.com>
-- Fix pkgconfig Requires
-- Add ldconfig in pre/post scripts for libpurple & finch
-- Bump Epoch to 2 because Fedora unfortunately forgot to drop the Epoch
- during the rename :(
-
-* Tue Apr 17 2007 Stu Tomlinson <stu@nosnilmot.com>
-- Add some Obsolete:s to help upgrades
-- Remove explicit Packager: %%{packager} from spec, it was redundant
-
-* Sun Apr 15 2007 Stu Tomlinson <stu@nosnilmot.com>
-- Fix for building without Finch
-- Drop -tcl & -silc sub-packages, include them in the main libpurple
- package (--without tcl and --without silc can be used to build libpurple
- without support for these).
-
-* Thu Apr 12 2007 Stu Tomlinson <stu@nosnilmot.com>
-- Actually move libpurple.so.* to the libpurple RPM
-
-* Wed Apr 11 2007 Stu Tomlinson <stu@nosnilmot.com>
-- Separate out libpurple, libpurple-devel and finch-devel
-
-* Sat Mar 24 2007 Stu Tomlinson <stu@nosnilmot.com>
-- Update to reflect renaming to pidgin/libpurple
-
-* Sun Oct 1 2006 Stu Tomlinson <stu@nosnilmot.com>
-- We can build with internal gadu gadu again, so bring it back into the
- main package
-- Deal with gconf schame uninstallation on package upgrade and removal
-
-* Sun Aug 20 2006 Stu Tomlinson <stu@nosnilmot.com>
-- Make the gstreamer-devel dependency overridable with '--without-gstreamer'
- to allow building on older distributions without suitable gstreamer
-
-* Tue Aug 15 2006 Mark Doliner <thekingant@users.sourceforge.net>
-- Add a BuildRequire for gstreamer-devel
-- Remove the BuildRequires for audiofile-devel and libao-devel
-
-* Mon May 8 2006 Mark Doliner <thekingant@users.sourceforge.net>
-- Add --with avahi option to compile the gaim-bonjour package against
- Avahi's Howl compatibility layer
-
-* Wed Mar 29 2006 Stu Tomlinson <stu@nosnilmot.com>
-- Source RPM uses tar.bz2 now to save space
-- Update BuildRequires for new intltool dependencies
-- Add a --with perlmakehack option to allow builds to succeed on RH9
-- Add a --with gadugadu to build (separate) gaim-gadugadu package
-
-* Sat Dec 17 2005 Stu Tomlinson <stu@nosnilmot.com>
-- Add support for beta versions so the subsequent releases are seen as newer
- by RPM
-- Split of sametime support to gaim-meanwhile
-- Use make DESTDIR=... instead of overloading prefix etc. when installing
-- Default build to include cyrus-sasl support in Jabber
-- Add --with dbus to build with DBUS support
-
-* Sun Dec 04 2005 Christopher O'Brien <siege@preoccupied.net>
-- Added obsoletes gaim-meanwhile
-
-* Sun Oct 30 2005 Stu Tomlinson <stu@nosnilmot.com>
-- Add separate gaim-bonjour package if built with --with-howl
-- Add separate gaim-mono package if built with --with-mono
-- Exclude some unwanted perl files
-
-* Sat Aug 20 2005 Stu Tomlinson <stu@nosnilmot.com>
-- Include libgaimperl.so
-- Include gaim.m4 in gaim-devel
-
-* Thu Apr 28 2005 Stu Tomlinson <stu@nosnilmot.com>
-- Use perl_vendorlib & perl_archlib for better 64bit compat (Jeff Mahoney)
-- Clean up Requires, most should be auto-detected
-- Restore gtkspell-devel build requirement (and add --without gtkspell option)
-- Fix Tcl build requirements to work across more distros
-- Fix SILC build requirements to work across more distros
-
-* Mon Oct 11 2004 John Jolly <john.jolly@gmail.com>
-- Added if "%%{_vendor}" == "suse" to handle GnuTLS libraries for SuSE
-
-* Sat Oct 2 2004 Stu Tomlinson <stu@nosnilmot.com>
-- If --with tcl or silc are not specified, make sure the plugins don't
- exist to prevent RPM complaining about unpackaged files
-
-* Tue Jun 29 2004 Ethan Blanton <eblanton@cs.ohiou.edu>
-- Change Tcl to use --with tcl, the same as SILC, and build a gaim-tcl
- package if specified.
-
-* Thu Jun 24 2004 Mark Doliner <thekingant@users.sourceforge.net>
-- Add --with silc rebuild option for compiling a separate gaim-silc
- RPM containing the silc protocol plugin (Stu Tomlinson).
-
-* Wed Jun 23 2004 Ethan Blanton <eblanton@cs.ohiou.edu>
-- Moved gaim headers and a pkgconfig configuration file into the
- gaim-devel RPM (Stu Tomlinson).
-
-* Thu Jan 15 2004 Ethan Blanton <eblanton@cs.ohiou.edu>
-- Removed the manual strip command, as it seems to be unwarranted if
- the necessary programs are properly installed. (For me, this was
- elfutils.)
-
-* Sun Jul 20 2003 Bjoern Voigt <bjoern@cs.tu-berlin.de>
-- Added pkgconfig build dependency.
-- if "%%{_vendor}" != "MandrakeSoft" now also works with rpm 3.x.
-- Added Gaim-specific directories to list of Gaim's files.
-
-* Wed Jul 16 2003 Ethan Blanton <eblanton@cs.ohiou.edu>
-- Complete spec file rewrite to take advantage of "new" RPM features
- and make things prettier.
-- Use system-supplied %%{_prefix}, %%{_datadir}, etc. rather than
- attempt to define our own.
diff --git a/pidgin/Makefile.am b/pidgin/Makefile.am
index b6a5bd4642..86b154bff6 100644
--- a/pidgin/Makefile.am
+++ b/pidgin/Makefile.am
@@ -2,13 +2,16 @@ EXTRA_DIST = \
getopt.c \
getopt.h \
getopt1.c \
+ gtk3compat.h \
+ gtkdebug.html \
Makefile.mingw \
- pidgin.pc.in \
- pidgin-uninstalled.pc.in \
+ pidgin-3.pc.in \
+ pidgin-3-uninstalled.pc.in \
win32/MinimizeToTray.h \
win32/MinimizeToTray.c \
win32/pidgin_dll_rc.rc.in \
win32/pidgin_exe_rc.rc.in \
+ win32/prepare-workspace.sh \
win32/gtkdocklet-win32.c \
win32/gtkwin32dep.c \
win32/gtkwin32dep.h \
@@ -16,10 +19,7 @@ EXTRA_DIST = \
win32/untar.c \
win32/untar.h \
win32/winpidgin.c \
- win32/wspell.c \
- win32/wspell.h \
win32/nsis/generate_gtk_zip.sh \
- win32/nsis/rpm2zip.sh \
win32/nsis/pixmaps/pidgin-header.bmp \
win32/nsis/pixmaps/pidgin-intro.bmp \
win32/nsis/pixmaps/pidgin-install.ico \
@@ -32,13 +32,15 @@ EXTRA_DIST = \
if ENABLE_GTK
pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = pidgin.pc
+pkgconfig_DATA = pidgin-3.pc
-SUBDIRS = pixmaps plugins
+SUBDIRS = . pixmaps plugins themes
+# XXX: should this be lib_, or noinst_?
+lib_LTLIBRARIES = libpidgin.la
bin_PROGRAMS = pidgin
-pidgin_SOURCES = \
+libpidgin_la_SOURCES = \
pidginstock.c \
gtkaccount.c \
gtkblist.c \
@@ -48,20 +50,17 @@ pidgin_SOURCES = \
gtkcertmgr.c \
gtkconn.c \
gtkconv.c \
+ gtkconv-theme.c \
+ gtkconv-theme-loader.c \
gtkdebug.c \
gtkdialogs.c \
gtkdnd-hints.c \
gtkdocklet.c \
- gtkdocklet-gtk.c \
gtkeventloop.c \
- gtkft.c \
gtkicon-theme.c \
gtkicon-theme-loader.c \
gtkidle.c \
- gtkimhtml.c \
- gtkimhtmltoolbar.c \
gtklog.c \
- gtkmain.c \
gtkmedia.c \
gtkmenutray.c \
gtknotify.c \
@@ -75,20 +74,21 @@ pidgin_SOURCES = \
gtksavedstatuses.c \
gtkscrollbook.c \
gtksession.c \
- gtksmiley.c \
+ gtksmiley-manager.c \
+ gtksmiley-theme.c \
gtksound.c \
- gtksourceiter.c \
- gtksourceundomanager.c \
- gtksourceview-marshal.c \
gtkstatus-icon-theme.c \
gtkstatusbox.c \
- gtkthemes.c \
gtkutils.c \
+ gtkwebview.c \
+ gtkwebviewtoolbar.c \
gtkwhiteboard.c \
+ gtkxfer.c \
+ libpidgin.c \
minidialog.c \
pidgintooltip.c
-pidgin_headers = \
+libpidgin_la_headers = \
gtkaccount.h \
gtkblist.h \
gtkblist-theme.h \
@@ -98,18 +98,16 @@ pidgin_headers = \
gtkconn.h \
gtkconv.h \
gtkconvwin.h \
+ gtkconv-theme.h \
+ gtkconv-theme-loader.h \
gtkdebug.h \
gtkdialogs.h \
gtkdnd-hints.h \
gtkdocklet.h \
gtkeventloop.h \
- gtkft.h \
gtkicon-theme.h \
gtkicon-theme-loader.h \
gtkidle.h \
- gtkgaim-compat.h \
- gtkimhtml.h \
- gtkimhtmltoolbar.h \
gtklog.h \
gtkmedia.h \
gtkmenutray.h \
@@ -125,60 +123,115 @@ pidgin_headers = \
gtksavedstatuses.h \
gtkscrollbook.h \
gtksession.h \
- gtksmiley.h \
+ gtksmiley-manager.h \
+ gtksmiley-theme.h \
gtksound.h \
- gtksourceiter.h \
- gtksourceundomanager.h \
- gtksourceview-marshal.h \
gtkstatus-icon-theme.h \
gtkstatusbox.h \
pidginstock.h \
- gtkthemes.h \
gtkutils.h \
+ gtkwebview.h \
+ gtkwebviewtoolbar.h \
gtkwhiteboard.h \
+ gtkxfer.h \
minidialog.h \
pidgintooltip.h \
pidgin.h
-pidginincludedir=$(includedir)/pidgin
-pidgininclude_HEADERS = \
- $(pidgin_headers)
+pidgin_SOURCES = \
+ pidgin.c
+noinst_HEADERS= gtkinternal.h
-pidgin_DEPENDENCIES = @LIBOBJS@
-pidgin_LDFLAGS = -export-dynamic
-pidgin_LDADD = \
+if IS_WIN32
+libpidgin_la_SOURCES += \
+ win32/gtkwin32dep.c \
+ win32/untar.c
+
+# Files that looks like obsolete (were used in Pidgin2):
+# win32/gtkdocklet-win32.c
+# win32/MinimizeToTray.c
+# win32/MinimizeToTray.h
+
+pidgin_SOURCES += \
+ win32/winpidgin.c \
+ win32/pidgin_exe_rc.rc
+
+libpidgin_la_headers += \
+ win32/gtkwin32dep.h \
+ win32/resource.h \
+ win32/untar.h
+
+.rc.o: %.rc
+ $(AM_V_GEN)$(WINDRES) -I $(srcdir)/win32 -I $(srcdir) -i $< -o $@
+
+LIBPIDGIN_WIN32RES = win32/pidgin_dll_rc.o
+LIBPIDGIN_WIN32RES_LDFLAGS = -Wl,$(LIBPIDGIN_WIN32RES)
+endif
+
+libpidginincludedir=$(includedir)/pidgin
+libpidgininclude_HEADERS = \
+ $(libpidgin_la_headers)
+
+libpidgin_la_builtheaders = gtkdebug.html.h
+
+BUILT_SOURCES = $(libpidgin_la_builtheaders)
+
+CLEANFILES = gtkdebug.html.h
+
+%.html.h: %.html
+ $(AM_V_GEN)echo "static const char $*_html[] = {" > $@
+ $(AM_V_at)$(sedpath) -e 's/^[ ]\+//g' -e 's/[ ]\+/ /g' $< | $(xxdpath) -i | sed -e 's/\(0x[0-9a-f][0-9a-f]\)$$/\1, 0x00/' >> $@
+ $(AM_V_at)echo "};" >> $@
+
+gtkdebug.c: gtkdebug.html.h
+
+libpidgin_la_DEPENDENCIES = @LIBOBJS@ $(LIBPIDGIN_WIN32RES)
+libpidgin_la_LDFLAGS = -export-dynamic -no-undefined \
+ -version-info $(PURPLE_LT_VERSION_INFO) $(LIBPIDGIN_WIN32RES_LDFLAGS)
+libpidgin_la_LIBADD = \
@LIBOBJS@ \
+ $(ENCHANT_LIBS) \
$(GLIB_LIBS) \
+ $(GCR_LIBS) \
$(DBUS_LIBS) \
$(GSTREAMER_LIBS) \
+ $(GSTVIDEO_LIBS) \
+ $(GSTINTERFACES_LIBS) \
$(XSS_LIBS) \
$(SM_LIBS) \
$(INTLLIBS) \
- $(GTKSPELL_LIBS) \
$(LIBXML_LIBS) \
+ $(WEBKIT_LIBS) \
$(GTK_LIBS) \
+ $(X11_LIBS) \
$(top_builddir)/libpurple/libpurple.la
-if USE_INTERNAL_LIBGADU
-INTGG_CFLAGS = -DUSE_INTERNAL_LIBGADU
+pidgin_DEPENDENCIES = $(builddir)/libpidgin.la
+pidgin_LDFLAGS = -export-dynamic
+pidgin_LDADD = $(builddir)/libpidgin.la $(libpidgin_la_LIBADD)
+
+if IS_WIN32
+libpidgin_la_LIBADD += -lwinmm
+pidgin_LDFLAGS += -mwindows
endif
AM_CPPFLAGS = \
- -DDATADIR=\"$(datadir)\" \
- -DLIBDIR=\"$(libdir)/pidgin/\" \
- -DLOCALEDIR=\"$(datadir)/locale\" \
- -DSYSCONFDIR=\"$(sysconfdir)\" \
-I$(top_builddir)/libpurple \
-I$(top_srcdir)/libpurple/ \
-I$(top_builddir) \
-I$(top_srcdir) \
+ $(ENCHANT_CFLAGS) \
$(GLIB_CFLAGS) \
+ $(GCR_CFLAGS) \
$(GSTREAMER_CFLAGS) \
+ $(GSTVIDEO_CFLAGS) \
+ $(GSTINTERFACES_CFLAGS) \
$(DEBUG_CFLAGS) \
$(GTK_CFLAGS) \
+ $(X11_CFLAGS) \
$(DBUS_CFLAGS) \
- $(GTKSPELL_CFLAGS) \
$(LIBXML_CFLAGS) \
- $(INTGG_CFLAGS)
+ $(WEBKIT_CFLAGS)
endif # ENABLE_GTK
+
diff --git a/pidgin/Makefile.mingw b/pidgin/Makefile.mingw
index aa77ddabf2..0790fff518 100644
--- a/pidgin/Makefile.mingw
+++ b/pidgin/Makefile.mingw
@@ -35,16 +35,23 @@ INCLUDE_PATHS += \
$(PURPLE_INCLUDE_PATHS) \
-I$(PIDGIN_TOP) \
-I$(PIDGIN_TOP)/win32 \
+ $(VV_INCLUDE_PATHS) \
-I$(GTK_TOP)/include/gtk-2.0 \
-I$(GTK_TOP)/include/pango-1.0 \
-I$(GTK_TOP)/include/atk-1.0 \
-I$(GTK_TOP)/include/cairo \
+ -I$(GTK_TOP)/include/gdk-pixbuf-2.0 \
-I$(GTK_TOP)/lib/gtk-2.0/include \
- -I$(GTKSPELL_TOP)/include/gtkspell-2.0
+ -I$(LIBSOUP_TOP)/include/libsoup-2.4 \
+ -I$(LIBGADU_TOP)/include \
+ -I$(WEBKITGTK_TOP)/include/webkitgtk-1.0
LIB_PATHS += -L$(GTK_TOP)/lib \
+ -L$(WEBKITGTK_TOP)/lib \
+ -L$(LIBGADU_TOP)/lib \
-L$(PURPLE_TOP) \
- -L$(PIDGIN_TOP)
+ -L$(PIDGIN_TOP) \
+ $(VV_LIB_PATHS)
##
## SOURCES, OBJECTS
@@ -58,19 +65,17 @@ PIDGIN_C_SRC = \
gtkcertmgr.c \
gtkconn.c \
gtkconv.c \
+ gtkconv-theme.c \
+ gtkconv-theme-loader.c \
gtkdebug.c \
gtkdialogs.c \
gtkdnd-hints.c \
gtkdocklet.c \
gtkeventloop.c \
- gtkft.c \
gtkicon-theme-loader.c \
gtkicon-theme.c \
gtkidle.c \
- gtkimhtml.c \
- gtkimhtmltoolbar.c \
gtklog.c \
- gtkmain.c \
gtkmedia.c \
gtkmenutray.c \
gtknotify.c \
@@ -83,23 +88,23 @@ PIDGIN_C_SRC = \
gtkroomlist.c \
gtksavedstatuses.c \
gtkscrollbook.c \
- gtksmiley.c \
+ gtksmiley-manager.c \
+ gtksmiley-theme.c \
gtksound.c \
- gtksourceiter.c \
- gtksourceundomanager.c \
gtkstatus-icon-theme.c \
gtkstatusbox.c \
- gtkthemes.c \
gtkutils.c \
+ gtkwebview.c \
+ gtkwebviewtoolbar.c \
gtkwhiteboard.c \
+ gtkxfer.c \
+ libpidgin.c \
minidialog.c \
+ pidgin.c \
pidginstock.c \
pidgintooltip.c \
- win32/MinimizeToTray.c \
- win32/gtkdocklet-win32.c \
win32/gtkwin32dep.c \
- win32/untar.c \
- win32/wspell.c
+ win32/untar.c
PIDGIN_RC_SRC = win32/pidgin_dll_rc.rc
PIDGIN_OBJECTS = $(PIDGIN_C_SRC:%.c=%.o) $(PIDGIN_RC_SRC:%.rc=%.o)
@@ -126,7 +131,10 @@ PIDGIN_LIBS = \
-lgdk-win32-2.0 \
-lgdk_pixbuf-2.0 \
-lgdi32 \
- -lwinmm
+ -lgadu \
+ -lwinmm \
+ $(VV_LIBS) \
+ -lwebkitgtk-1.0
include $(PIDGIN_COMMON_RULES)
@@ -136,26 +144,42 @@ include $(PIDGIN_COMMON_RULES)
.PHONY: all install install_shallow clean
all: $(EXE_TARGET).exe $(PIDGIN_TARGET).dll
- $(MAKE) -C $(PIDGIN_PLUGINS_TOP) -f $(MINGW_MAKEFILE)
+ $(MAKE_at) $(MAKE) -C $(PIDGIN_PLUGINS_TOP) -f $(MINGW_MAKEFILE)
+
+gtkdialogs.c: $(PIDGIN_REVISION_H)
win32/pidgin_exe_rc.rc: win32/pidgin_exe_rc.rc.in $(PIDGIN_TREE_TOP)/VERSION
- sed -e 's/@PIDGIN_VERSION@/$(PIDGIN_VERSION)/g' \
- -e 's/@ORIGINAL_FILENAME@/$(EXE_NAME)/' \
+ @echo -e " GEN\t$@"
+ @sed -e 's/@PURPLE_VERSION@/$(PIDGIN_VERSION)/g' \
+ -e 's/@PURPLE_MAJOR_VERSION@/$(PURPLE_MAJOR_VERSION)/g' \
+ -e 's/@PURPLE_MINOR_VERSION@/$(PURPLE_MINOR_VERSION)/g' \
+ -e 's/@PURPLE_MICRO_VERSION@/$(PURPLE_MICRO_VERSION)/g' \
+ -e 's/@PURPLE_API_VERSION@/$(PURPLE_API_VERSION)/g' \
$@.in > $@
install_shallow: $(PIDGIN_INSTALL_DIR) $(EXE_TARGET).exe $(PIDGIN_TARGET).dll
cp $(EXE_TARGET).exe $(PIDGIN_TARGET).dll $(PIDGIN_INSTALL_DIR)
+ mkdir -p "$(PIDGIN_INSTALL_DIR)/theme"
+ cp -r themes/* "$(PIDGIN_INSTALL_DIR)/theme/"
+ chmod -R 777 "$(PIDGIN_INSTALL_DIR)/theme/"
install: install_shallow all
- $(MAKE) -C $(PIDGIN_PLUGINS_TOP) -f $(MINGW_MAKEFILE) install
- $(MAKE) -C $(PIDGIN_PIXMAPS_TOP) -f $(MINGW_MAKEFILE) install
+ $(MAKE_at) $(MAKE) -C $(PIDGIN_PLUGINS_TOP) -f $(MINGW_MAKEFILE) install
+ $(MAKE_at) $(MAKE) -C $(PIDGIN_PIXMAPS_TOP) -f $(MINGW_MAKEFILE) install
win32/pidgin_dll_rc.rc: win32/pidgin_dll_rc.rc.in $(PIDGIN_TREE_TOP)/VERSION
- sed -e 's/@PIDGIN_VERSION@/$(PIDGIN_VERSION)/g' \
+ @echo -e " GEN\t$@"
+ @sed -e 's/@PURPLE_VERSION@/$(PIDGIN_VERSION)/g' \
+ -e 's/@PURPLE_MAJOR_VERSION@/$(PURPLE_MAJOR_VERSION)/g' \
+ -e 's/@PURPLE_MINOR_VERSION@/$(PURPLE_MINOR_VERSION)/g' \
+ -e 's/@PURPLE_MICRO_VERSION@/$(PURPLE_MICRO_VERSION)/g' \
+ -e 's/@PURPLE_API_VERSION@/$(PURPLE_API_VERSION)/g' \
$@.in > $@
$(EXE_OBJECTS) $(PIDGIN_OBJECTS): $(PIDGIN_CONFIG_H)
+gtkdebug.c: gtkdebug.html.h
+
$(PIDGIN_TARGET).dll $(PIDGIN_TARGET).dll.a: $(PURPLE_DLL).a $(PIDGIN_OBJECTS)
$(CC) -shared $(PIDGIN_OBJECTS) $(LIB_PATHS) $(PIDGIN_LIBS) $(DLL_LD_FLAGS) -Wl,--output-def,$(PIDGIN_TARGET).def,--out-implib,$(PIDGIN_TARGET).dll.a -o $(PIDGIN_TARGET).dll
@@ -166,8 +190,8 @@ $(EXE_TARGET).exe: $(PIDGIN_CONFIG_H) $(PIDGIN_DLL).a $(EXE_OBJECTS) $(PIDGIN_TA
## CLEAN RULES
##
clean:
- $(MAKE) -C $(PIDGIN_PLUGINS_TOP) -f $(MINGW_MAKEFILE) clean
- $(MAKE) -C $(PIDGIN_PIXMAPS_TOP) -f $(MINGW_MAKEFILE) clean
+ $(MAKE_at) $(MAKE) -C $(PIDGIN_PLUGINS_TOP) -f $(MINGW_MAKEFILE) clean
+ $(MAKE_at) $(MAKE) -C $(PIDGIN_PIXMAPS_TOP) -f $(MINGW_MAKEFILE) clean
rm -f $(PIDGIN_OBJECTS) $(PIDGIN_RC_SRC) $(EXE_OBJECTS) $(EXE_RC_SRC)
rm -f $(PIDGIN_TARGET).dll $(PIDGIN_TARGET).dll.a $(PIDGIN_TARGET).def
rm -f $(EXE_TARGET).exe
diff --git a/pidgin/gtk3compat.h b/pidgin/gtk3compat.h
new file mode 100644
index 0000000000..87b502c19f
--- /dev/null
+++ b/pidgin/gtk3compat.h
@@ -0,0 +1,289 @@
+/* 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 _PIDGINGTK3COMPAT_H_
+#define _PIDGINGTK3COMPAT_H_
+/*
+ * SECTION:gtk3compat
+ * @section_id: pidgin-gtk3compat
+ * @short_description: <filename>gtk3compat.h</filename>
+ * @title: GTK3 version-dependent definitions
+ *
+ * This file is internal to Pidgin. Do not use!
+ * Also, any public API should not depend on this file.
+ */
+
+#include <gtk/gtk.h>
+#include <math.h>
+
+#if !GTK_CHECK_VERSION(3,4,0)
+
+#define gtk_color_chooser_dialog_new(title, parent) \
+ gtk_color_selection_dialog_new(title)
+#define GTK_COLOR_CHOOSER(widget) (GTK_WIDGET(widget))
+
+static inline void
+gtk_color_chooser_set_use_alpha(GtkWidget *widget, gboolean use_alpha)
+{
+ if (GTK_IS_COLOR_BUTTON(widget)) {
+ gtk_color_button_set_use_alpha(GTK_COLOR_BUTTON(widget),
+ use_alpha);
+ }
+}
+
+static inline void
+pidgin_color_chooser_set_rgb(GtkWidget *widget, const GdkColor *color)
+{
+ if (GTK_IS_COLOR_SELECTION_DIALOG(widget)) {
+ GtkWidget *colorsel;
+
+ colorsel = gtk_color_selection_dialog_get_color_selection(
+ GTK_COLOR_SELECTION_DIALOG(widget));
+ gtk_color_selection_set_current_color(
+ GTK_COLOR_SELECTION(colorsel), color);
+ } else
+ gtk_color_button_set_color(GTK_COLOR_BUTTON(widget), color);
+}
+
+static inline void
+pidgin_color_chooser_get_rgb(GtkWidget *widget, GdkColor *color)
+{
+ if (GTK_IS_COLOR_SELECTION_DIALOG(widget)) {
+ GtkWidget *colorsel;
+
+ colorsel = gtk_color_selection_dialog_get_color_selection(
+ GTK_COLOR_SELECTION_DIALOG(widget));
+ gtk_color_selection_get_current_color(
+ GTK_COLOR_SELECTION(colorsel), color);
+ } else
+ gtk_color_button_get_color(GTK_COLOR_BUTTON(widget), color);
+}
+
+#else
+
+static inline void
+pidgin_color_chooser_set_rgb(GtkColorChooser *chooser, const GdkColor *rgb)
+{
+ GdkRGBA rgba;
+
+ rgba.red = rgb->red / 65535.0;
+ rgba.green = rgb->green / 65535.0;
+ rgba.blue = rgb->blue / 65535.0;
+ rgba.alpha = 1.0;
+
+ gtk_color_chooser_set_rgba(chooser, &rgba);
+}
+
+static inline void
+pidgin_color_chooser_get_rgb(GtkColorChooser *chooser, GdkColor *rgb)
+{
+ GdkRGBA rgba;
+
+ gtk_color_chooser_get_rgba(chooser, &rgba);
+ rgb->red = (int)round(rgba.red * 65535.0);
+ rgb->green = (int)round(rgba.green * 65535.0);
+ rgb->blue = (int)round(rgba.blue * 65535.0);
+}
+
+#endif /* 3.4.0 and gtk_color_chooser_ */
+
+
+#if !GTK_CHECK_VERSION(3,2,0)
+
+#define GTK_FONT_CHOOSER GTK_FONT_SELECTION_DIALOG
+#define gtk_font_chooser_get_font gtk_font_selection_dialog_get_font_name
+#define gtk_font_chooser_set_font gtk_font_selection_dialog_set_font_name
+
+static inline GtkWidget * gtk_font_chooser_dialog_new(const gchar *title,
+ GtkWindow *parent)
+{
+ return gtk_font_selection_dialog_new(title);
+}
+
+#if !GTK_CHECK_VERSION(3,0,0)
+
+#define gdk_x11_window_get_xid GDK_WINDOW_XWINDOW
+#define gtk_widget_get_preferred_size(x,y,z) gtk_widget_size_request(x,z)
+
+#ifdef GDK_WINDOWING_X11
+#define GDK_IS_X11_WINDOW(window) TRUE
+#endif
+#ifdef GDK_WINDOWING_WIN32
+#define GDK_IS_WIN32_WINDOW(window) TRUE
+#endif
+#ifdef GDK_WINDOWING_QUARTZ
+#define GDK_IS_QUARTZ_WINDOW(window) TRUE
+#endif
+
+static inline GdkPixbuf *
+gdk_pixbuf_get_from_surface(cairo_surface_t *surface, gint src_x, gint src_y,
+ gint width, gint height)
+{
+ GdkPixmap *pixmap;
+ GdkPixbuf *pixbuf;
+ cairo_t *cr;
+
+ pixmap = gdk_pixmap_new(NULL, width, height, 24);
+
+ cr = gdk_cairo_create(pixmap);
+ cairo_set_source_surface(cr, surface, -src_x, -src_y);
+ cairo_paint(cr);
+ cairo_destroy(cr);
+
+ pixbuf = gdk_pixbuf_get_from_drawable(NULL, pixmap,
+ gdk_drawable_get_colormap(pixmap), 0, 0, 0, 0, width, height);
+
+ g_object_unref(pixmap);
+
+ return pixbuf;
+}
+
+static inline GdkPixbuf *
+gdk_pixbuf_get_from_window(GdkWindow *window, gint src_x, gint src_y,
+ gint width, gint height)
+{
+ return gdk_pixbuf_get_from_drawable(NULL, window, NULL,
+ src_x, src_y, 0, 0, width, height);
+}
+
+static inline GtkWidget *
+gtk_box_new(GtkOrientation orientation, gint spacing)
+{
+ g_return_val_if_fail(orientation == GTK_ORIENTATION_HORIZONTAL ||
+ orientation == GTK_ORIENTATION_VERTICAL, NULL);
+
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ return gtk_hbox_new(FALSE, spacing);
+ else /* GTK_ORIENTATION_VERTICAL */
+ return gtk_vbox_new(FALSE, spacing);
+}
+
+static inline GtkWidget *
+gtk_separator_new(GtkOrientation orientation)
+{
+ g_return_val_if_fail(orientation == GTK_ORIENTATION_HORIZONTAL ||
+ orientation == GTK_ORIENTATION_VERTICAL, NULL);
+
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ return gtk_hseparator_new();
+ else /* GTK_ORIENTATION_VERTICAL */
+ return gtk_vseparator_new();
+}
+
+static inline GtkWidget *
+gtk_button_box_new(GtkOrientation orientation)
+{
+ g_return_val_if_fail(orientation == GTK_ORIENTATION_HORIZONTAL ||
+ orientation == GTK_ORIENTATION_VERTICAL, NULL);
+
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ return gtk_hbutton_box_new();
+ else /* GTK_ORIENTATION_VERTICAL */
+ return gtk_vbutton_box_new();
+}
+
+static inline GtkWidget *
+gtk_paned_new(GtkOrientation orientation)
+{
+ g_return_val_if_fail(orientation == GTK_ORIENTATION_HORIZONTAL ||
+ orientation == GTK_ORIENTATION_VERTICAL, NULL);
+
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ return gtk_hpaned_new();
+ else /* GTK_ORIENTATION_VERTICAL */
+ return gtk_vpaned_new();
+}
+
+static inline GtkWidget *
+gtk_scale_new_with_range(GtkOrientation orientation, gdouble min, gdouble max,
+ gdouble step)
+{
+ g_return_val_if_fail(orientation == GTK_ORIENTATION_HORIZONTAL ||
+ orientation == GTK_ORIENTATION_VERTICAL, NULL);
+
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ return gtk_hscale_new_with_range(min, max, step);
+ else /* GTK_ORIENTATION_VERTICAL */
+ return gtk_vscale_new_with_range(min, max, step);
+}
+
+#if !GTK_CHECK_VERSION(2,24,0)
+
+#define gdk_x11_set_sm_client_id gdk_set_sm_client_id
+#define gdk_window_get_display gdk_drawable_get_display
+#define GtkComboBoxText GtkComboBox
+#define GTK_COMBO_BOX_TEXT GTK_COMBO_BOX
+#define gtk_combo_box_text_new gtk_combo_box_new_text
+#define gtk_combo_box_text_new_with_entry gtk_combo_box_entry_new_text
+#define gtk_combo_box_text_append_text gtk_combo_box_append_text
+#define gtk_combo_box_text_get_active_text gtk_combo_box_get_active_text
+#define gtk_combo_box_text_remove gtk_combo_box_remove_text
+
+static inline gint gdk_window_get_width(GdkWindow *x)
+{
+ gint w;
+ gdk_drawable_get_size(GDK_DRAWABLE(x), &w, NULL);
+ return w;
+}
+
+static inline gint gdk_window_get_height(GdkWindow *x)
+{
+ gint h;
+ gdk_drawable_get_size(GDK_DRAWABLE(x), NULL, &h);
+ return h;
+}
+
+#if !GTK_CHECK_VERSION(2,22,0)
+
+#define gdk_drag_context_get_actions(x) (x)->action
+#define gdk_drag_context_get_suggested_action(x) (x)->suggested_action
+#define gtk_text_view_get_vadjustment(x) (x)->vadjustment
+#define gtk_font_selection_dialog_get_font_selection(x) (x)->fontsel
+
+#if !GTK_CHECK_VERSION(2,20,0)
+
+#define gtk_widget_get_mapped GTK_WIDGET_MAPPED
+#define gtk_widget_set_mapped(x,y) do { \
+ if (y) \
+ GTK_WIDGET_SET_FLAGS(x, GTK_MAPPED); \
+ else \
+ GTK_WIDGET_UNSET_FLAGS(x, GTK_MAPPED); \
+} while(0)
+#define gtk_widget_get_realized GTK_WIDGET_REALIZED
+#define gtk_widget_set_realized(x,y) do { \
+ if (y) \
+ GTK_WIDGET_SET_FLAGS(x, GTK_REALIZED); \
+ else \
+ GTK_WIDGET_UNSET_FLAGS(x, GTK_REALIZED); \
+} while(0)
+
+#endif /* 2.20.0 */
+
+#endif /* 2.22.0 */
+
+#endif /* 2.24.0 */
+
+#endif /* 3.0.0 */
+
+#endif /* 3.2.0 */
+
+#endif /* _PIDGINGTK3COMPAT_H_ */
+
diff --git a/pidgin/gtkaccount.c b/pidgin/gtkaccount.c
index 78ed55c753..faf4c1fa2b 100644
--- a/pidgin/gtkaccount.c
+++ b/pidgin/gtkaccount.c
@@ -1,8 +1,3 @@
-/**
- * @file gtkaccount.c GTK+ Account Editor UI
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -48,6 +43,8 @@
#include "pidginstock.h"
#include "minidialog.h"
+#include "gtk3compat.h"
+
enum
{
COLUMN_ICON,
@@ -116,7 +113,13 @@ typedef struct
GtkWidget *login_frame;
GtkWidget *protocol_menu;
GtkWidget *password_box;
+ gchar *password;
GtkWidget *username_entry;
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkRGBA username_entry_hint_color;
+#else
+ GdkColor *username_entry_hint_color;
+#endif
GtkWidget *password_entry;
GtkWidget *alias_entry;
GtkWidget *remember_pass_check;
@@ -130,7 +133,7 @@ typedef struct
GtkWidget *icon_filesel;
GtkWidget *icon_preview;
GtkWidget *icon_text;
- PurpleStoredImage *icon_img;
+ PurpleImage *icon_img;
/* Protocol Options */
GtkWidget *protocol_frame;
@@ -178,17 +181,24 @@ set_dialog_icon(AccountPrefsDialog *dialog, gpointer data, size_t len, gchar *ne
{
GdkPixbuf *pixbuf = NULL;
- dialog->icon_img = purple_imgstore_unref(dialog->icon_img);
- if (data != NULL)
- {
+ if (dialog->icon_img) {
+ g_object_unref(dialog->icon_img);
+ dialog->icon_img = NULL;
+ }
+
+ if (new_icon_path != NULL) {
+ dialog->icon_img = purple_image_new_from_file(new_icon_path, TRUE);
+ purple_debug_warning("gtkaccount", "data was not necessary");
+ g_free(data);
+ } else if (data != NULL) {
if (len > 0)
- dialog->icon_img = purple_imgstore_add(data, len, new_icon_path);
+ dialog->icon_img = purple_image_new_from_data(data, len);
else
g_free(data);
}
if (dialog->icon_img != NULL) {
- pixbuf = pidgin_pixbuf_from_imgstore(dialog->icon_img);
+ pixbuf = pidgin_pixbuf_from_image(dialog->icon_img);
}
if (pixbuf && dialog->prpl_info &&
@@ -220,7 +230,7 @@ set_dialog_icon(AccountPrefsDialog *dialog, gpointer data, size_t len, gchar *ne
}
static void
-set_account_protocol_cb(GtkWidget *item, const char *id,
+set_account_protocol_cb(GtkWidget *widget, const char *id,
AccountPrefsDialog *dialog)
{
PurplePlugin *new_plugin;
@@ -254,32 +264,72 @@ set_account_protocol_cb(GtkWidget *item, const char *id,
gtk_widget_grab_focus(dialog->protocol_menu);
- if (!dialog->prpl_info || !dialog->prpl_info->register_user ||
- g_object_get_data(G_OBJECT(item), "fake")) {
+ if (!dialog->prpl_info || !dialog->prpl_info->register_user) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
+ dialog->register_button), FALSE);
gtk_widget_hide(dialog->register_button);
} else {
if (dialog->prpl_info != NULL &&
(dialog->prpl_info->options & OPT_PROTO_REGISTER_NOSCREENNAME)) {
gtk_widget_set_sensitive(dialog->register_button, TRUE);
} else {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
+ dialog->register_button), FALSE);
gtk_widget_set_sensitive(dialog->register_button, FALSE);
}
gtk_widget_show(dialog->register_button);
}
}
+static void
+username_changed_cb(GtkEntry *entry, AccountPrefsDialog *dialog)
+{
+ gboolean opt_noscreenname = (dialog->prpl_info != NULL &&
+ (dialog->prpl_info->options & OPT_PROTO_REGISTER_NOSCREENNAME));
+ gboolean username_valid = purple_validate(dialog->plugin,
+ gtk_entry_get_text(entry));
+
+ if (dialog->ok_button) {
+ if (opt_noscreenname && dialog->register_button &&
+ gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(dialog->register_button)))
+ gtk_widget_set_sensitive(dialog->ok_button, TRUE);
+ else
+ gtk_widget_set_sensitive(dialog->ok_button,
+ username_valid);
+ }
+
+ if (dialog->register_button) {
+ if (opt_noscreenname)
+ gtk_widget_set_sensitive(dialog->register_button, TRUE);
+ else
+ gtk_widget_set_sensitive(dialog->register_button,
+ username_valid);
+ }
+}
+
+#if !GTK_CHECK_VERSION(3,2,0)
static gboolean
username_focus_cb(GtkWidget *widget, GdkEventFocus *event, AccountPrefsDialog *dialog)
{
GHashTable *table;
const char *label;
+ if (!dialog->prpl_info || ! PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(
+ dialog->prpl_info, get_account_text_table)) {
+ return FALSE;
+ }
+
table = dialog->prpl_info->get_account_text_table(NULL);
label = g_hash_table_lookup(table, "login_label");
if(!strcmp(gtk_entry_get_text(GTK_ENTRY(widget)), label)) {
gtk_entry_set_text(GTK_ENTRY(widget), "");
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_color(widget, GTK_STATE_NORMAL, NULL);
+#else
gtk_widget_modify_text(widget, GTK_STATE_NORMAL,NULL);
+#endif
}
g_hash_table_destroy(table);
@@ -287,29 +337,13 @@ username_focus_cb(GtkWidget *widget, GdkEventFocus *event, AccountPrefsDialog *d
return FALSE;
}
-static void
-username_changed_cb(GtkEntry *entry, AccountPrefsDialog *dialog)
-{
- if (dialog->ok_button)
- gtk_widget_set_sensitive(dialog->ok_button,
- *gtk_entry_get_text(entry) != '\0');
- if (dialog->register_button) {
- if (dialog->prpl_info != NULL && (dialog->prpl_info->options & OPT_PROTO_REGISTER_NOSCREENNAME))
- gtk_widget_set_sensitive(dialog->register_button, TRUE);
- else
- gtk_widget_set_sensitive(dialog->register_button,
- *gtk_entry_get_text(entry) != '\0');
- }
-}
-
static gboolean
username_nofocus_cb(GtkWidget *widget, GdkEventFocus *event, AccountPrefsDialog *dialog)
{
- GdkColor color = {0, 34952, 35466, 34181};
GHashTable *table = NULL;
const char *label = NULL;
- if(PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(dialog->prpl_info, get_account_text_table)) {
+ if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(dialog->prpl_info, get_account_text_table)) {
table = dialog->prpl_info->get_account_text_table(NULL);
label = g_hash_table_lookup(table, "login_label");
@@ -321,7 +355,11 @@ username_nofocus_cb(GtkWidget *widget, GdkEventFocus *event, AccountPrefsDialog
gtk_entry_set_text(GTK_ENTRY(widget), label);
/* Make sure we can hit it again */
g_signal_handlers_unblock_by_func(widget, G_CALLBACK(username_changed_cb), dialog);
- gtk_widget_modify_text(widget, GTK_STATE_NORMAL, &color);
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_color(widget, GTK_STATE_NORMAL, &dialog->username_entry_hint_color);
+#else
+ gtk_widget_modify_text(widget, GTK_STATE_NORMAL, dialog->username_entry_hint_color);
+#endif
}
g_hash_table_destroy(table);
@@ -330,6 +368,119 @@ username_nofocus_cb(GtkWidget *widget, GdkEventFocus *event, AccountPrefsDialog
return FALSE;
}
+static gboolean
+username_themechange_cb(GObject *widget, GdkEventFocus *event, AccountPrefsDialog *dialog)
+{
+ GHashTable *table;
+ const char *label, *text;
+ char *temp_text = NULL;
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context;
+ GtkBorder border;
+#else
+ GtkStyle *style;
+ const GtkBorder *border = NULL;
+#endif
+ gint xsize;
+
+ table = dialog->prpl_info->get_account_text_table(NULL);
+ label = g_hash_table_lookup(table, "login_label");
+ text = gtk_entry_get_text(GTK_ENTRY(widget));
+
+ g_signal_handlers_block_by_func(widget, G_CALLBACK(username_themechange_cb), dialog);
+ g_signal_handlers_block_by_func(widget, G_CALLBACK(username_changed_cb), dialog);
+ if (strcmp(text, label)) {
+ temp_text = g_strdup(text);
+ gtk_entry_set_text(GTK_ENTRY(widget), label);
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_color(GTK_WIDGET(widget), GTK_STATE_NORMAL, NULL);
+#else
+ gtk_widget_modify_text(GTK_WIDGET(widget), GTK_STATE_NORMAL, NULL);
+#endif
+ }
+
+#if GTK_CHECK_VERSION(3,0,0)
+ context = gtk_widget_get_style_context(dialog->username_entry);
+ gtk_style_context_get_color(context, GTK_STATE_FLAG_INSENSITIVE,
+ &dialog->username_entry_hint_color);
+#else
+ style = gtk_rc_get_style(dialog->username_entry);
+ dialog->username_entry_hint_color = &(style->fg[GTK_STATE_INSENSITIVE]);
+#endif
+
+ pango_layout_get_pixel_size(gtk_entry_get_layout(GTK_ENTRY(widget)), &xsize, NULL);
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_style_context_get_margin(context, GTK_STATE_FLAG_NORMAL, &border);
+ xsize += border.left + border.right;
+ gtk_style_context_get_padding(context, GTK_STATE_FLAG_NORMAL, &border);
+ xsize += border.left + border.right;
+#else
+ xsize += 2 * style->xthickness;
+ gtk_style_get(style, GTK_TYPE_ENTRY, "inner-border", &border, NULL);
+ if (border)
+ xsize += border->left + border->right;
+ else
+ xsize += 4; /* 2 * default inner-border */
+#endif
+ gtk_widget_set_size_request(GTK_WIDGET(widget), xsize, -1);
+ if (temp_text) {
+ gtk_entry_set_text(GTK_ENTRY(widget), temp_text);
+ g_free(temp_text);
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_color(GTK_WIDGET(widget), GTK_STATE_NORMAL, NULL);
+#else
+ gtk_widget_modify_text(GTK_WIDGET(widget), GTK_STATE_NORMAL, NULL);
+#endif
+ } else
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_color(GTK_WIDGET(widget), GTK_STATE_NORMAL, &dialog->username_entry_hint_color);
+#else
+ gtk_widget_modify_text(GTK_WIDGET(widget), GTK_STATE_NORMAL, dialog->username_entry_hint_color);
+#endif
+
+ g_signal_handlers_unblock_by_func(widget, G_CALLBACK(username_themechange_cb), dialog);
+ g_signal_handlers_unblock_by_func(widget, G_CALLBACK(username_changed_cb), dialog);
+ g_hash_table_destroy(table);
+
+ return FALSE;
+}
+#endif
+
+static void
+register_button_cb(GtkWidget *checkbox, AccountPrefsDialog *dialog)
+{
+ int register_checked = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(dialog->register_button));
+ int opt_noscreenname = (dialog->prpl_info != NULL &&
+ (dialog->prpl_info->options & OPT_PROTO_REGISTER_NOSCREENNAME));
+ int register_noscreenname = (opt_noscreenname && register_checked);
+
+#if !GTK_CHECK_VERSION(3,2,0)
+ /* get rid of login_label in username field */
+ username_focus_cb(dialog->username_entry, NULL, dialog);
+#endif
+
+ if (register_noscreenname) {
+ gtk_entry_set_text(GTK_ENTRY(dialog->username_entry), "");
+ gtk_entry_set_text(GTK_ENTRY(dialog->password_entry), "");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->remember_pass_check), FALSE);
+ }
+ gtk_widget_set_sensitive(dialog->username_entry, !register_noscreenname);
+ gtk_widget_set_sensitive(dialog->password_entry, !register_noscreenname);
+ gtk_widget_set_sensitive(dialog->remember_pass_check, !register_noscreenname);
+
+ if (dialog->ok_button) {
+ gtk_widget_set_sensitive(dialog->ok_button,
+ (opt_noscreenname && register_checked) ||
+ *gtk_entry_get_text(GTK_ENTRY(dialog->username_entry))
+ != '\0');
+ }
+
+#if !GTK_CHECK_VERSION(3,2,0)
+ username_nofocus_cb(dialog->username_entry, NULL, dialog);
+#endif
+}
+
static void
icon_filesel_choose_cb(const char *filename, gpointer data)
{
@@ -337,7 +488,7 @@ icon_filesel_choose_cb(const char *filename, gpointer data)
if (filename != NULL)
{
- size_t len;
+ size_t len = 0;
gpointer data = pidgin_convert_buddy_icon(dialog->plugin, filename, &len);
set_dialog_icon(dialog, data, len, g_strdup(filename));
}
@@ -362,16 +513,18 @@ static void
account_dnd_recv(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
GtkSelectionData *sd, guint info, guint t, AccountPrefsDialog *dialog)
{
- gchar *name = (gchar *)sd->data;
+ const gchar *name = (gchar *)gtk_selection_data_get_data(sd);
+ gint length = gtk_selection_data_get_length(sd);
+ gint format = gtk_selection_data_get_format(sd);
- if ((sd->length >= 0) && (sd->format == 8)) {
+ if ((length >= 0) && (format == 8)) {
/* Well, it looks like the drag event was cool.
* Let's do something with it */
if (!g_ascii_strncasecmp(name, "file://", 7)) {
GError *converr = NULL;
gchar *tmp, *rtmp;
gpointer data;
- size_t len;
+ size_t len = 0;
/* It looks like we're dealing with a local file. */
if(!(tmp = g_filename_from_uri(name, NULL, &converr))) {
@@ -395,7 +548,12 @@ account_dnd_recv(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
static void
update_editable(PurpleConnection *gc, AccountPrefsDialog *dialog)
{
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *style;
+ GdkRGBA color;
+#else
GtkStyle *style;
+#endif
gboolean set;
GList *l;
@@ -408,16 +566,38 @@ update_editable(PurpleConnection *gc, AccountPrefsDialog *dialog)
set = !(purple_account_is_connected(dialog->account) || purple_account_is_connecting(dialog->account));
gtk_widget_set_sensitive(dialog->protocol_menu, set);
gtk_editable_set_editable(GTK_EDITABLE(dialog->username_entry), set);
+#if GTK_CHECK_VERSION(3,0,0)
+ style = set ? NULL : gtk_widget_get_style_context(dialog->username_entry);
+ if (style) {
+ gtk_style_context_get_background_color(style, GTK_STATE_FLAG_INSENSITIVE, &color);
+ gtk_widget_override_background_color(dialog->username_entry, GTK_STATE_FLAG_NORMAL, &color);
+ } else {
+ gtk_widget_override_background_color(dialog->username_entry, GTK_STATE_FLAG_NORMAL, NULL);
+ }
+#else
style = set ? NULL : gtk_widget_get_style(dialog->username_entry);
gtk_widget_modify_base(dialog->username_entry, GTK_STATE_NORMAL,
style ? &style->base[GTK_STATE_INSENSITIVE] : NULL);
+#endif
for (l = dialog->user_split_entries ; l != NULL ; l = l->next) {
+ if (l->data == NULL)
+ continue;
if (GTK_IS_EDITABLE(l->data)) {
gtk_editable_set_editable(GTK_EDITABLE(l->data), set);
+#if GTK_CHECK_VERSION(3,0,0)
+ style = set ? NULL : gtk_widget_get_style_context(GTK_WIDGET(l->data));
+ if (style) {
+ gtk_style_context_get_background_color(style, GTK_STATE_FLAG_INSENSITIVE, &color);
+ gtk_widget_override_background_color(GTK_WIDGET(l->data), GTK_STATE_FLAG_NORMAL, &color);
+ } else {
+ gtk_widget_override_background_color(GTK_WIDGET(l->data), GTK_STATE_FLAG_NORMAL, NULL);
+ }
+#else
style = set ? NULL : gtk_widget_get_style(GTK_WIDGET(l->data));
gtk_widget_modify_base(GTK_WIDGET(l->data), GTK_STATE_NORMAL,
style ? &style->base[GTK_STATE_INSENSITIVE] : NULL);
+#endif
} else {
gtk_widget_set_sensitive(GTK_WIDGET(l->data), set);
}
@@ -431,19 +611,13 @@ add_login_options(AccountPrefsDialog *dialog, GtkWidget *parent)
GtkWidget *hbox;
GtkWidget *vbox;
GtkWidget *entry;
- GtkWidget *menu;
- GtkWidget *item;
GList *user_splits;
GList *l, *l2;
char *username = NULL;
if (dialog->protocol_menu != NULL)
{
-#if GTK_CHECK_VERSION(2,12,0)
g_object_ref(G_OBJECT(dialog->protocol_menu));
-#else
- gtk_widget_ref(dialog->protocol_menu);
-#endif
hbox = g_object_get_data(G_OBJECT(dialog->protocol_menu), "container");
gtk_container_remove(GTK_CONTAINER(hbox), dialog->protocol_menu);
}
@@ -461,7 +635,7 @@ add_login_options(AccountPrefsDialog *dialog, GtkWidget *parent)
gtk_widget_show(dialog->login_frame);
/* Main vbox */
- vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
gtk_container_add(GTK_CONTAINER(frame), vbox);
gtk_widget_show(vbox);
@@ -470,21 +644,13 @@ add_login_options(AccountPrefsDialog *dialog, GtkWidget *parent)
{
dialog->protocol_menu = pidgin_protocol_option_menu_new(
dialog->protocol_id, G_CALLBACK(set_account_protocol_cb), dialog);
-#if GTK_CHECK_VERSION(2,12,0)
g_object_ref(G_OBJECT(dialog->protocol_menu));
-#else
- gtk_widget_ref(dialog->protocol_menu);
-#endif
}
hbox = add_pref_box(dialog, vbox, _("Pro_tocol:"), dialog->protocol_menu);
g_object_set_data(G_OBJECT(dialog->protocol_menu), "container", hbox);
-#if GTK_CHECK_VERSION(2,12,0)
g_object_unref(G_OBJECT(dialog->protocol_menu));
-#else
- gtk_widget_unref(dialog->protocol_menu);
-#endif
/* Username */
dialog->username_entry = gtk_entry_new();
@@ -497,18 +663,24 @@ add_login_options(AccountPrefsDialog *dialog, GtkWidget *parent)
if (!username && dialog->prpl_info
&& PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(dialog->prpl_info, get_account_text_table)) {
- GdkColor color = {0, 34952, 35466, 34181};
GHashTable *table;
const char *label;
table = dialog->prpl_info->get_account_text_table(NULL);
label = g_hash_table_lookup(table, "login_label");
+#if GTK_CHECK_VERSION(3,2,0)
+ gtk_entry_set_placeholder_text(GTK_ENTRY(dialog->username_entry), label);
+#else
gtk_entry_set_text(GTK_ENTRY(dialog->username_entry), label);
+ username_themechange_cb(G_OBJECT(dialog->username_entry), NULL, dialog);
+ g_signal_connect(G_OBJECT(dialog->username_entry), "style-set",
+ G_CALLBACK(username_themechange_cb), dialog);
g_signal_connect(G_OBJECT(dialog->username_entry), "focus-in-event",
G_CALLBACK(username_focus_cb), dialog);
g_signal_connect(G_OBJECT(dialog->username_entry), "focus-out-event",
G_CALLBACK(username_nofocus_cb), dialog);
- gtk_widget_modify_text(dialog->username_entry, GTK_STATE_NORMAL, &color);
+#endif
+
g_hash_table_destroy(table);
}
@@ -530,13 +702,14 @@ add_login_options(AccountPrefsDialog *dialog, GtkWidget *parent)
PurpleAccountUserSplit *split = l->data;
char *buf;
- buf = g_strdup_printf("_%s:", purple_account_user_split_get_text(split));
-
- entry = gtk_entry_new();
-
- add_pref_box(dialog, vbox, buf, entry);
-
- g_free(buf);
+ if (purple_account_user_split_is_constant(split))
+ entry = NULL;
+ else {
+ buf = g_strdup_printf("_%s:", purple_account_user_split_get_text(split));
+ entry = gtk_entry_new();
+ add_pref_box(dialog, vbox, buf, entry);
+ g_free(buf);
+ }
dialog->user_split_entries =
g_list_append(dialog->user_split_entries, entry);
@@ -552,7 +725,7 @@ add_login_options(AccountPrefsDialog *dialog, GtkWidget *parent)
const char *value = NULL;
char *c;
- if (dialog->account != NULL) {
+ if (dialog->account != NULL && username != NULL) {
if(purple_account_user_split_get_reverse(split))
c = strrchr(username,
purple_account_user_split_get_separator(split));
@@ -570,18 +743,7 @@ add_login_options(AccountPrefsDialog *dialog, GtkWidget *parent)
if (value == NULL)
value = purple_account_user_split_get_default_value(split);
- /* Google Talk default domain hackery! */
- menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(dialog->protocol_menu));
- item = gtk_menu_get_active(GTK_MENU(menu));
- if (value == NULL && g_object_get_data(G_OBJECT(item), "fakegoogle") &&
- !strcmp(purple_account_user_split_get_text(split), _("Domain")))
- value = "gmail.com";
-
- if (value == NULL && g_object_get_data(G_OBJECT(item), "fakefacebook") &&
- !strcmp(purple_account_user_split_get_text(split), _("Domain")))
- value = "chat.facebook.com";
-
- if (value != NULL)
+ if (value != NULL && entry != NULL)
gtk_entry_set_text(GTK_ENTRY(entry), value);
}
@@ -594,10 +756,6 @@ add_login_options(AccountPrefsDialog *dialog, GtkWidget *parent)
/* Password */
dialog->password_entry = gtk_entry_new();
gtk_entry_set_visibility(GTK_ENTRY(dialog->password_entry), FALSE);
-#if !GTK_CHECK_VERSION(2,16,0)
- if (gtk_entry_get_invisible_char(GTK_ENTRY(dialog->password_entry)) == '*')
- gtk_entry_set_invisible_char(GTK_ENTRY(dialog->password_entry), PIDGIN_INVISIBLE_CHAR);
-#endif /* Less than GTK+ 2.16 */
dialog->password_box = add_pref_box(dialog, vbox, _("_Password:"),
dialog->password_entry);
@@ -612,10 +770,11 @@ add_login_options(AccountPrefsDialog *dialog, GtkWidget *parent)
/* Set the fields. */
if (dialog->account != NULL) {
- if (purple_account_get_password(dialog->account) &&
- purple_account_get_remember_password(dialog->account))
+ if (dialog->password && purple_account_get_remember_password(
+ dialog->account)) {
gtk_entry_set_text(GTK_ENTRY(dialog->password_entry),
- purple_account_get_password(dialog->account));
+ dialog->password);
+ }
gtk_toggle_button_set_active(
GTK_TOGGLE_BUTTON(dialog->remember_pass_check),
@@ -665,7 +824,7 @@ add_user_options(AccountPrefsDialog *dialog, GtkWidget *parent)
gtk_widget_show(dialog->user_frame);
/* Main vbox */
- vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
gtk_container_add(GTK_CONTAINER(frame), vbox);
gtk_widget_show(vbox);
@@ -685,7 +844,7 @@ add_user_options(AccountPrefsDialog *dialog, GtkWidget *parent)
gtk_widget_show(dialog->icon_check);
gtk_box_pack_start(GTK_BOX(vbox), dialog->icon_check, FALSE, FALSE, 0);
- dialog->icon_hbox = hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ dialog->icon_hbox = hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtk_widget_set_sensitive(hbox, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->icon_check)));
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show(hbox);
@@ -704,15 +863,17 @@ add_user_options(AccountPrefsDialog *dialog, GtkWidget *parent)
gtk_container_add(GTK_CONTAINER(button), dialog->icon_entry);
gtk_widget_show(dialog->icon_entry);
/* TODO: Uh, isn't this next line pretty useless? */
- pidgin_set_accessible_label (dialog->icon_entry, label);
- purple_imgstore_unref(dialog->icon_img);
- dialog->icon_img = NULL;
+ pidgin_set_accessible_label(dialog->icon_entry, GTK_LABEL(label));
+ if (dialog->icon_img) {
+ g_object_unref(dialog->icon_img);
+ dialog->icon_img = NULL;
+ }
- vbox2 = gtk_vbox_new(FALSE, 0);
+ vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0);
gtk_widget_show(vbox2);
- hbox2 = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ hbox2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_start(GTK_BOX(vbox2), hbox2, FALSE, FALSE, PIDGIN_HIG_BORDER);
gtk_widget_show(hbox2);
@@ -733,13 +894,13 @@ add_user_options(AccountPrefsDialog *dialog, GtkWidget *parent)
}
if (dialog->account != NULL) {
- PurpleStoredImage *img;
+ PurpleImage *img;
gpointer data = NULL;
size_t len = 0;
- if (purple_account_get_alias(dialog->account))
+ if (purple_account_get_private_alias(dialog->account))
gtk_entry_set_text(GTK_ENTRY(dialog->alias_entry),
- purple_account_get_alias(dialog->account));
+ purple_account_get_private_alias(dialog->account));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->new_mail_check),
purple_account_get_check_mail(dialog->account));
@@ -751,8 +912,8 @@ add_user_options(AccountPrefsDialog *dialog, GtkWidget *parent)
img = purple_buddy_icons_find_account_icon(dialog->account);
if (img)
{
- len = purple_imgstore_get_size(img);
- data = g_memdup(purple_imgstore_get_data(img), len);
+ len = purple_image_get_size(img);
+ data = g_memdup(purple_image_get_data(img), len);
}
set_dialog_icon(dialog, data, len,
g_strdup(purple_account_get_buddy_icon_path(dialog->account)));
@@ -776,7 +937,7 @@ add_protocol_options(AccountPrefsDialog *dialog)
{
PurpleAccountOption *option;
PurpleAccount *account;
- GtkWidget *vbox, *check, *entry, *combo, *menu, *item;
+ GtkWidget *vbox, *check, *entry, *combo;
GList *list, *node;
gint i, idx, int_value;
GtkListStore *model;
@@ -789,6 +950,7 @@ add_protocol_options(AccountPrefsDialog *dialog)
const char *str_value;
gboolean bool_value;
ProtocolOptEntry *opt_entry;
+ const GSList *str_hints;
if (dialog->protocol_frame != NULL) {
gtk_notebook_remove_page (GTK_NOTEBOOK(dialog->notebook), 1);
@@ -809,21 +971,18 @@ add_protocol_options(AccountPrefsDialog *dialog)
account = dialog->account;
/* Main vbox */
- dialog->protocol_frame = vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ dialog->protocol_frame = vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
gtk_container_set_border_width(GTK_CONTAINER(vbox), PIDGIN_HIG_BORDER);
gtk_notebook_insert_page(GTK_NOTEBOOK(dialog->notebook), vbox,
gtk_label_new_with_mnemonic(_("Ad_vanced")), 1);
gtk_widget_show(vbox);
- menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(dialog->protocol_menu));
- item = gtk_menu_get_active(GTK_MENU(menu));
-
for (l = dialog->prpl_info->protocol_options; l != NULL; l = l->next)
{
option = (PurpleAccountOption *)l->data;
opt_entry = g_new0(ProtocolOptEntry, 1);
- opt_entry->type = purple_account_option_get_type(option);
+ opt_entry->type = purple_account_option_get_pref_type(option);
opt_entry->setting = g_strdup(purple_account_option_get_setting(option));
switch (opt_entry->type)
@@ -892,18 +1051,35 @@ add_protocol_options(AccountPrefsDialog *dialog)
purple_account_option_get_default_string(option));
}
- opt_entry->widget = entry = gtk_entry_new();
- if (purple_account_option_get_masked(option))
+ str_hints = purple_account_option_string_get_hints(option);
+ if (str_hints)
+ {
+ const GSList *hint_it = str_hints;
+ entry = gtk_combo_box_text_new_with_entry();
+ while (hint_it)
+ {
+ const gchar *hint = hint_it->data;
+ hint_it = g_slist_next(hint_it);
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(entry),
+ hint);
+ }
+ }
+ else
+ entry = gtk_entry_new();
+
+ opt_entry->widget = entry;
+ if (purple_account_option_string_get_masked(option) && str_hints)
+ g_warn_if_reached();
+ else if (purple_account_option_string_get_masked(option))
{
gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
-#if !GTK_CHECK_VERSION(2,16,0)
- if (gtk_entry_get_invisible_char(GTK_ENTRY(entry)) == '*')
- gtk_entry_set_invisible_char(GTK_ENTRY(entry), PIDGIN_INVISIBLE_CHAR);
-#endif /* Less than GTK+ 2.16 */
}
- if (str_value != NULL)
- gtk_entry_set_text(GTK_ENTRY(entry), str_value);
+ if (str_value != NULL && str_hints)
+ gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(entry))),
+ str_value);
+ else
+ gtk_entry_set_text(GTK_ENTRY(entry), str_value ? str_value : "");
title = g_strdup_printf("_%s:",
purple_account_option_get_text(option));
@@ -932,10 +1108,6 @@ add_protocol_options(AccountPrefsDialog *dialog)
model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
opt_entry->widget = combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
- if (g_object_get_data(G_OBJECT(item), "fakefacebook") &&
- !strcmp(opt_entry->setting, "connection_security"))
- str_value = "opportunistic_tls";
-
/* Loop through list of PurpleKeyValuePair items */
for (node = list; node != NULL; node = node->next) {
if (node->data != NULL) {
@@ -1062,7 +1234,7 @@ proxy_type_changed_cb(GtkWidget *menu, AccountPrefsDialog *dialog)
dialog->new_proxy_type == PURPLE_PROXY_NONE ||
dialog->new_proxy_type == PURPLE_PROXY_USE_ENVVAR) {
- gtk_widget_hide_all(dialog->proxy_vbox);
+ gtk_widget_hide(dialog->proxy_vbox);
}
else
gtk_widget_show_all(dialog->proxy_vbox);
@@ -1105,7 +1277,7 @@ add_proxy_options(AccountPrefsDialog *dialog, GtkWidget *parent)
gtk_widget_destroy(dialog->proxy_frame);
/* Main vbox */
- dialog->proxy_frame = vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ dialog->proxy_frame = vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
gtk_container_add(GTK_CONTAINER(parent), vbox);
gtk_widget_show(vbox);
@@ -1115,7 +1287,7 @@ add_proxy_options(AccountPrefsDialog *dialog, GtkWidget *parent)
add_pref_box(dialog, vbox, _("Proxy _type:"), dialog->proxy_dropdown);
/* Setup the second vbox, which may be hidden at times. */
- dialog->proxy_vbox = vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ dialog->proxy_vbox = vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_start(GTK_BOX(vbox), vbox2, FALSE, FALSE, PIDGIN_HIG_BORDER);
gtk_widget_show(vbox2);
@@ -1138,10 +1310,6 @@ add_proxy_options(AccountPrefsDialog *dialog, GtkWidget *parent)
/* Password */
dialog->proxy_pass_entry = gtk_entry_new();
gtk_entry_set_visibility(GTK_ENTRY(dialog->proxy_pass_entry), FALSE);
-#if !GTK_CHECK_VERSION(2,16,0)
- if (gtk_entry_get_invisible_char(GTK_ENTRY(dialog->proxy_pass_entry)) == '*')
- gtk_entry_set_invisible_char(GTK_ENTRY(dialog->proxy_pass_entry), PIDGIN_INVISIBLE_CHAR);
-#endif /* Less than GTK+ 2.16 */
add_pref_box(dialog, vbox2, _("Pa_ssword:"), dialog->proxy_pass_entry);
if (dialog->account != NULL &&
@@ -1149,7 +1317,7 @@ add_proxy_options(AccountPrefsDialog *dialog, GtkWidget *parent)
const char *value;
int int_val;
- dialog->new_proxy_type = purple_proxy_info_get_type(proxy_info);
+ dialog->new_proxy_type = purple_proxy_info_get_proxy_type(proxy_info);
if ((value = purple_proxy_info_get_host(proxy_info)) != NULL)
gtk_entry_set_text(GTK_ENTRY(dialog->proxy_host_entry), value);
@@ -1206,7 +1374,7 @@ add_voice_options(AccountPrefsDialog *dialog)
}
if (!dialog->voice_frame) {
- dialog->voice_frame = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
+ dialog->voice_frame = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BORDER);
gtk_container_set_border_width(GTK_CONTAINER(dialog->voice_frame),
PIDGIN_HIG_BORDER);
@@ -1220,8 +1388,12 @@ add_voice_options(AccountPrefsDialog *dialog)
gtk_widget_show_all(dialog->voice_frame);
}
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->suppression_check),
- purple_account_get_silence_suppression(dialog->account));
+ if (dialog->account) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->suppression_check),
+ purple_account_get_silence_suppression(dialog->account));
+ } else {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->suppression_check), FALSE);
+ }
#endif
}
@@ -1243,13 +1415,16 @@ account_win_destroy_cb(GtkWidget *w, GdkEvent *event,
g_free(dialog->protocol_id);
g_object_unref(dialog->sg);
- purple_imgstore_unref(dialog->icon_img);
+ if (dialog->icon_img)
+ g_object_unref(dialog->icon_img);
if (dialog->icon_filesel)
gtk_widget_destroy(dialog->icon_filesel);
purple_signals_disconnect_by_handle(dialog);
+ purple_str_wipe(dialog->password);
+
g_free(dialog);
return FALSE;
}
@@ -1261,6 +1436,23 @@ cancel_account_prefs_cb(GtkWidget *w, AccountPrefsDialog *dialog)
}
static void
+account_register_cb(PurpleAccount *account, gboolean succeeded, void *user_data)
+{
+ if (succeeded)
+ {
+ const PurpleSavedStatus *saved_status = purple_savedstatus_get_current();
+ purple_signal_emit(pidgin_accounts_get_handle(), "account-modified", account);
+
+ if (saved_status != NULL && purple_account_get_remember_password(account)) {
+ purple_savedstatus_activate_for_account(saved_status, account);
+ purple_account_set_enabled(account, PIDGIN_UI, TRUE);
+ }
+ }
+ else
+ purple_accounts_delete(account);
+}
+
+static void
ok_account_prefs_cb(GtkWidget *w, AccountPrefsDialog *dialog)
{
PurpleProxyInfo *proxy_info = NULL;
@@ -1270,6 +1462,7 @@ ok_account_prefs_cb(GtkWidget *w, AccountPrefsDialog *dialog)
char *tmp;
gboolean new_acct = FALSE, icon_change = FALSE;
PurpleAccount *account;
+ gboolean remember;
/* Build the username string. */
username = g_strdup(gtk_entry_get_text(GTK_ENTRY(dialog->username_entry)));
@@ -1285,7 +1478,9 @@ ok_account_prefs_cb(GtkWidget *w, AccountPrefsDialog *dialog)
GtkEntry *entry = l2->data;
char sep[2] = " ";
- value = gtk_entry_get_text(entry);
+ value = entry ? gtk_entry_get_text(entry) : "";
+ if (!value)
+ value = "";
*sep = purple_account_user_split_get_separator(split);
@@ -1306,7 +1501,7 @@ ok_account_prefs_cb(GtkWidget *w, AccountPrefsDialog *dialog)
dialog->protocol_id, username);
purple_notify_error(NULL, NULL, _("Unable to save new account"),
- _("An account already exists with the specified criteria."));
+ _("An account already exists with the specified criteria."), NULL);
g_free(username);
return;
@@ -1332,9 +1527,9 @@ ok_account_prefs_cb(GtkWidget *w, AccountPrefsDialog *dialog)
value = gtk_entry_get_text(GTK_ENTRY(dialog->alias_entry));
if (*value != '\0')
- purple_account_set_alias(account, value);
+ purple_account_set_private_alias(account, value);
else
- purple_account_set_alias(account, NULL);
+ purple_account_set_private_alias(account, NULL);
/* Buddy Icon */
if (dialog->prpl_info != NULL && dialog->prpl_info->icon_spec.format != NULL)
@@ -1352,11 +1547,11 @@ ok_account_prefs_cb(GtkWidget *w, AccountPrefsDialog *dialog)
{
if (dialog->icon_img)
{
- size_t len = purple_imgstore_get_size(dialog->icon_img);
+ size_t len = purple_image_get_size(dialog->icon_img);
purple_buddy_icons_set_account_icon(account,
- g_memdup(purple_imgstore_get_data(dialog->icon_img), len),
- len);
- purple_account_set_buddy_icon_path(account, purple_imgstore_get_filename(dialog->icon_img));
+ g_memdup(purple_image_get_data(dialog->icon_img), len), len);
+ purple_account_set_buddy_icon_path(account,
+ purple_image_get_path(dialog->icon_img));
}
else
{
@@ -1366,7 +1561,7 @@ ok_account_prefs_cb(GtkWidget *w, AccountPrefsDialog *dialog)
}
else if ((filename = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon")) && icon_change)
{
- size_t len;
+ size_t len = 0;
gpointer data = pidgin_convert_buddy_icon(dialog->plugin, filename, &len);
purple_account_set_buddy_icon_path(account, filename);
purple_buddy_icons_set_account_icon(account, data, len);
@@ -1375,9 +1570,12 @@ ok_account_prefs_cb(GtkWidget *w, AccountPrefsDialog *dialog)
/* Remember Password */
- purple_account_set_remember_password(account,
- gtk_toggle_button_get_active(
- GTK_TOGGLE_BUTTON(dialog->remember_pass_check)));
+ remember = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(dialog->remember_pass_check));
+ if(!remember)
+ purple_keyring_set_password(account, NULL, NULL, NULL);
+
+ purple_account_set_remember_password(account, remember);
/* Check Mail */
if (dialog->prpl_info && dialog->prpl_info->options & OPT_PROTO_MAIL_CHECK)
@@ -1395,9 +1593,9 @@ ok_account_prefs_cb(GtkWidget *w, AccountPrefsDialog *dialog)
* don't want to prompt them.
*/
if ((purple_account_get_remember_password(account) || new_acct) && (*value != '\0'))
- purple_account_set_password(account, value);
+ purple_account_set_password(account, value, NULL, NULL);
else
- purple_account_set_password(account, NULL);
+ purple_account_set_password(account, NULL, NULL, NULL);
purple_account_set_username(account, username);
g_free(username);
@@ -1416,7 +1614,10 @@ ok_account_prefs_cb(GtkWidget *w, AccountPrefsDialog *dialog)
switch (opt_entry->type) {
case PURPLE_PREF_STRING:
- value = gtk_entry_get_text(GTK_ENTRY(opt_entry->widget));
+ if (GTK_IS_COMBO_BOX(opt_entry->widget))
+ value = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(opt_entry->widget));
+ else
+ value = gtk_entry_get_text(GTK_ENTRY(opt_entry->widget));
purple_account_set_string(account, opt_entry->setting, value);
break;
@@ -1454,7 +1655,7 @@ ok_account_prefs_cb(GtkWidget *w, AccountPrefsDialog *dialog)
}
/* Set the proxy info type. */
- purple_proxy_info_set_type(proxy_info, dialog->new_proxy_type);
+ purple_proxy_info_set_proxy_type(proxy_info, dialog->new_proxy_type);
/* Host */
value = gtk_entry_get_text(GTK_ENTRY(dialog->proxy_host_entry));
@@ -1489,7 +1690,7 @@ ok_account_prefs_cb(GtkWidget *w, AccountPrefsDialog *dialog)
purple_proxy_info_set_password(proxy_info, NULL);
/* If there are no values set then proxy_info NULL */
- if ((purple_proxy_info_get_type(proxy_info) == PURPLE_PROXY_USE_GLOBAL) &&
+ if ((purple_proxy_info_get_proxy_type(proxy_info) == PURPLE_PROXY_USE_GLOBAL) &&
(purple_proxy_info_get_host(proxy_info) == NULL) &&
(purple_proxy_info_get_port(proxy_info) == 0) &&
(purple_proxy_info_get_username(proxy_info) == NULL) &&
@@ -1509,10 +1710,11 @@ ok_account_prefs_cb(GtkWidget *w, AccountPrefsDialog *dialog)
if (new_acct)
purple_accounts_add(account);
else
- purple_signal_emit(pidgin_account_get_handle(), "account-modified", account);
+ purple_signal_emit(pidgin_accounts_get_handle(), "account-modified", account);
/* If this is a new account, then sign on! */
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->register_button))) {
+ purple_account_set_register_callback(account, account_register_cb, NULL);
purple_account_register(account);
} else if (new_acct) {
const PurpleSavedStatus *saved_status;
@@ -1535,10 +1737,11 @@ static const GtkTargetEntry dnd_targets[] = {
{"STRING", 0, 2}
};
-void
-pidgin_account_dialog_show(PidginAccountDialogType type,
- PurpleAccount *account)
+static void
+pidgin_account_dialog_show_continue(PurpleAccount *account,
+ const gchar *password, GError *error, gpointer _type)
{
+ PidginAccountDialogType type = GPOINTER_TO_INT(_type);
AccountPrefsDialog *dialog;
GtkWidget *win;
GtkWidget *main_vbox;
@@ -1562,8 +1765,9 @@ pidgin_account_dialog_show(PidginAccountDialogType type,
}
dialog->account = account;
- dialog->type = type;
- dialog->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
+ dialog->password = g_strdup(password);
+ dialog->type = type;
+ dialog->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
if (dialog->account == NULL) {
/* Select the first prpl in the list*/
@@ -1594,7 +1798,7 @@ pidgin_account_dialog_show(PidginAccountDialogType type,
gtk_widget_show(GTK_WIDGET(notebook));
/* Setup the inner vbox */
- dialog->top_vbox = vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
+ dialog->top_vbox = vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BORDER);
gtk_container_set_border_width(GTK_CONTAINER(vbox), PIDGIN_HIG_BORDER);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox,
gtk_label_new_with_mnemonic(_("_Basic")));
@@ -1609,6 +1813,7 @@ pidgin_account_dialog_show(PidginAccountDialogType type,
gtk_box_pack_start(GTK_BOX(main_vbox), button, FALSE, FALSE, 0);
gtk_widget_show(button);
dialog->register_button = button;
+ g_signal_connect(G_OBJECT(dialog->register_button), "toggled", G_CALLBACK(register_button_cb), dialog);
if (dialog->account == NULL)
gtk_widget_set_sensitive(button, FALSE);
@@ -1619,7 +1824,7 @@ pidgin_account_dialog_show(PidginAccountDialogType type,
add_protocol_options(dialog);
/* Setup the page with 'Proxy'. */
- dbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
+ dbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BORDER);
gtk_container_set_border_width(GTK_CONTAINER(dbox), PIDGIN_HIG_BORDER);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), dbox,
gtk_label_new_with_mnemonic(_("P_roxy")));
@@ -1657,6 +1862,14 @@ pidgin_account_dialog_show(PidginAccountDialogType type,
gtk_widget_grab_focus(dialog->protocol_menu);
}
+void
+pidgin_account_dialog_show(PidginAccountDialogType type, PurpleAccount *account)
+{
+ /* this is to make sure the password will be cached */
+ purple_account_get_password(account,
+ pidgin_account_dialog_show_continue, GINT_TO_POINTER(type));
+}
+
/**************************************************************************
* Accounts Dialog
**************************************************************************/
@@ -1765,7 +1978,9 @@ drag_data_get_cb(GtkWidget *widget, GdkDragContext *ctx,
GtkSelectionData *data, guint info, guint time,
AccountsWindow *dialog)
{
- if (data->target == gdk_atom_intern("PURPLE_ACCOUNT", FALSE)) {
+ GdkAtom target = gtk_selection_data_get_target(data);
+
+ if (target == gdk_atom_intern("PURPLE_ACCOUNT", FALSE)) {
GtkTreeRowReference *ref;
GtkTreePath *source_row;
GtkTreeIter iter;
@@ -1836,13 +2051,16 @@ drag_data_received_cb(GtkWidget *widget, GdkDragContext *ctx,
guint x, guint y, GtkSelectionData *sd,
guint info, guint t, AccountsWindow *dialog)
{
- if (sd->target == gdk_atom_intern("PURPLE_ACCOUNT", FALSE) && sd->data) {
+ GdkAtom target = gtk_selection_data_get_target(sd);
+ const guchar *data = gtk_selection_data_get_data(sd);
+
+ if (target == gdk_atom_intern("PURPLE_ACCOUNT", FALSE) && data) {
gint dest_index;
PurpleAccount *a = NULL;
GtkTreePath *path = NULL;
GtkTreeViewDropPosition position;
- memcpy(&a, sd->data, sizeof(a));
+ memcpy(&a, data, sizeof(a));
if (gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(widget), x, y,
&path, &position)) {
@@ -1880,7 +2098,8 @@ drag_data_received_cb(GtkWidget *widget, GdkDragContext *ctx,
return;
}
- purple_accounts_reorder(a, dest_index);
+ if (dest_index >= 0)
+ purple_accounts_reorder(a, dest_index);
}
}
}
@@ -1945,11 +2164,9 @@ ask_delete_account_sel(GtkTreeModel *model, GtkTreePath *path,
purple_request_close_with_handle(account);
purple_request_action(account, NULL, buf, NULL,
- PURPLE_DEFAULT_ACTION_NONE,
- account, NULL, NULL,
- account, 2,
- _("Delete"), delete_account_cb,
- _("Cancel"), NULL);
+ PURPLE_DEFAULT_ACTION_NONE,
+ purple_request_cpar_from_account(account), account, 2,
+ _("Delete"), delete_account_cb, _("Cancel"), NULL);
g_free(buf);
}
}
@@ -2062,7 +2279,7 @@ static void
set_account(GtkListStore *store, GtkTreeIter *iter, PurpleAccount *account, GdkPixbuf *global_buddyicon)
{
GdkPixbuf *pixbuf, *buddyicon = NULL;
- PurpleStoredImage *img = NULL;
+ PurpleImage *img = NULL;
PurplePlugin *prpl;
PurplePluginProtocolInfo *prpl_info = NULL;
@@ -2082,7 +2299,7 @@ set_account(GtkListStore *store, GtkTreeIter *iter, PurpleAccount *account, GdkP
const char *path;
path = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon");
if ((path != NULL) && (*path != '\0')) {
- img = purple_imgstore_new_from_file(path);
+ img = purple_image_new_from_file(path, TRUE);
}
}
} else {
@@ -2092,8 +2309,8 @@ set_account(GtkListStore *store, GtkTreeIter *iter, PurpleAccount *account, GdkP
if (img != NULL) {
GdkPixbuf *buddyicon_pixbuf;
- buddyicon_pixbuf = pidgin_pixbuf_from_imgstore(img);
- purple_imgstore_unref(img);
+ buddyicon_pixbuf = pidgin_pixbuf_from_image(img);
+ g_object_unref(img);
if (buddyicon_pixbuf != NULL) {
buddyicon = gdk_pixbuf_scale_simple(buddyicon_pixbuf, 22, 22, GDK_INTERP_HYPER);
@@ -2345,7 +2562,11 @@ pidgin_accounts_window_show(void)
width = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/accounts/dialog/width");
height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/accounts/dialog/height");
+#if GTK_CHECK_VERSION(3,0,0)
+ dialog->window = win = pidgin_create_dialog(_("Accounts"), 0, "accounts", TRUE);
+#else
dialog->window = win = pidgin_create_dialog(_("Accounts"), PIDGIN_HIG_BORDER, "accounts", TRUE);
+#endif
gtk_window_set_default_size(GTK_WINDOW(win), width, height);
g_signal_connect(G_OBJECT(win), "delete_event",
@@ -2375,7 +2596,7 @@ pidgin_accounts_window_show(void)
/* Close button */
pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE, G_CALLBACK(close_accounts_cb), dialog);
- purple_signal_connect(pidgin_account_get_handle(), "account-modified",
+ purple_signal_connect(pidgin_accounts_get_handle(), "account-modified",
accounts_window,
PURPLE_CALLBACK(account_modified_cb), accounts_window);
purple_prefs_connect_callback(accounts_window,
@@ -2510,18 +2731,93 @@ free_auth_request(struct auth_request *ar)
}
static void
-authorize_and_add_cb(struct auth_request *ar)
+authorize_and_add_cb(struct auth_request *ar, const char *message)
{
- ar->auth_cb(ar->data);
+ ar->auth_cb(message, ar->data);
if (ar->add_buddy_after_auth) {
purple_blist_request_add_buddy(ar->account, ar->username, NULL, ar->alias);
}
}
static void
-deny_no_add_cb(struct auth_request *ar)
+authorize_noreason_cb(struct auth_request *ar)
+{
+ authorize_and_add_cb(ar, NULL);
+}
+
+static void
+authorize_reason_cb(struct auth_request *ar)
+{
+ const char *protocol_id;
+ PurplePlugin *plugin;
+ PurplePluginProtocolInfo *prpl_info = NULL;
+
+ protocol_id = purple_account_get_protocol_id(ar->account);
+ if ((plugin = purple_find_prpl(protocol_id)) != NULL)
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
+
+ if (prpl_info && (prpl_info->options & OPT_PROTO_AUTHORIZATION_GRANTED_MESSAGE)) {
+ /* Duplicate information because ar is freed by closing minidialog */
+ struct auth_request *aa = g_new0(struct auth_request, 1);
+ aa->auth_cb = ar->auth_cb;
+ aa->deny_cb = ar->deny_cb;
+ aa->data = ar->data;
+ aa->account = ar->account;
+ aa->username = g_strdup(ar->username);
+ aa->alias = g_strdup(ar->alias);
+ aa->add_buddy_after_auth = ar->add_buddy_after_auth;
+ purple_request_input(ar->account, NULL, _("Authorization acceptance message:"),
+ NULL, _("No reason given."), TRUE, FALSE, NULL,
+ _("OK"), G_CALLBACK(authorize_and_add_cb),
+ _("Cancel"), G_CALLBACK(authorize_noreason_cb),
+ purple_request_cpar_from_account(ar->account),
+ aa);
+ /* FIXME: aa is going to leak now. */
+ } else {
+ authorize_noreason_cb(ar);
+ }
+}
+
+static void
+deny_no_add_cb(struct auth_request *ar, const char *message)
{
- ar->deny_cb(ar->data);
+ ar->deny_cb(message, ar->data);
+}
+
+static void
+deny_noreason_cb(struct auth_request *ar)
+{
+ ar->deny_cb(NULL, ar->data);
+}
+
+static void
+deny_reason_cb(struct auth_request *ar)
+{
+ const char *protocol_id;
+ PurplePlugin *plugin;
+ PurplePluginProtocolInfo *prpl_info = NULL;
+
+ protocol_id = purple_account_get_protocol_id(ar->account);
+ if ((plugin = purple_find_prpl(protocol_id)) != NULL)
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
+
+ if (prpl_info && (prpl_info->options & OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE)) {
+ /* Duplicate information because ar is freed by closing minidialog */
+ struct auth_request *aa = g_new0(struct auth_request, 1);
+ aa->auth_cb = ar->auth_cb;
+ aa->deny_cb = ar->deny_cb;
+ aa->data = ar->data;
+ aa->add_buddy_after_auth = ar->add_buddy_after_auth;
+ purple_request_input(ar->account, NULL, _("Authorization denied message:"),
+ NULL, _("No reason given."), TRUE, FALSE, NULL,
+ _("OK"), G_CALLBACK(deny_no_add_cb),
+ _("Cancel"), G_CALLBACK(deny_noreason_cb),
+ purple_request_cpar_from_account(ar->account),
+ aa);
+ /* FIXME: aa is going to leak now. */
+ } else {
+ deny_noreason_cb(ar);
+ }
}
static gboolean
@@ -2564,43 +2860,41 @@ pidgin_accounts_request_authorization(PurpleAccount *account,
GdkPixbuf *prpl_icon;
struct auth_request *aa;
const char *our_name;
- gboolean have_valid_alias = alias && *alias;
+ gboolean have_valid_alias;
+ char *escaped_remote_user;
+ char *escaped_alias;
+ char *escaped_our_name;
+ char *escaped_message;
gc = purple_account_get_connection(account);
- if (message != NULL && *message == '\0')
- message = NULL;
+ if (message != NULL && *message != '\0')
+ escaped_message = g_markup_escape_text(message, -1);
+ else
+ escaped_message = g_strdup("");
our_name = (id != NULL) ? id :
(purple_connection_get_display_name(gc) != NULL) ? purple_connection_get_display_name(gc) :
purple_account_get_username(account);
+ escaped_our_name = g_markup_escape_text(our_name, -1);
- if (pidgin_mini_dialog_links_supported()) {
- char *escaped_remote_user = g_markup_escape_text(remote_user, -1);
- char *escaped_alias = alias != NULL ? g_markup_escape_text(alias, -1) : g_strdup("");
- char *escaped_our_name = g_markup_escape_text(our_name, -1);
- char *escaped_message = message != NULL ? g_markup_escape_text(message, -1) : g_strdup("");
- buffer = g_strdup_printf(_("<a href=\"viewinfo\">%s</a>%s%s%s wants to add you (%s) to his or her buddy list%s%s"),
- escaped_remote_user,
- (have_valid_alias ? " (" : ""),
- escaped_alias,
- (have_valid_alias ? ")" : ""),
- escaped_our_name,
- (have_valid_alias ? ": " : "."),
- escaped_message);
- g_free(escaped_remote_user);
- g_free(escaped_alias);
- g_free(escaped_our_name);
- g_free(escaped_message);
- } else {
- buffer = g_strdup_printf(_("%s%s%s%s wants to add you (%s) to his or her buddy list%s%s"),
- remote_user,
- (have_valid_alias ? " (" : ""),
- (have_valid_alias ? alias : ""),
- (have_valid_alias ? ")" : ""),
- our_name,
- (message != NULL ? ": " : "."),
- (message != NULL ? message : ""));
- }
+ escaped_remote_user = g_markup_escape_text(remote_user, -1);
+
+ have_valid_alias = alias && *alias;
+ escaped_alias = have_valid_alias ? g_markup_escape_text(alias, -1) : g_strdup("");
+
+ buffer = g_strdup_printf(_("<a href=\"viewinfo\">%s</a>%s%s%s wants to add you (%s) to his or her buddy list%s%s"),
+ escaped_remote_user,
+ (have_valid_alias ? " (" : ""),
+ escaped_alias,
+ (have_valid_alias ? ")" : ""),
+ escaped_our_name,
+ (have_valid_alias ? ": " : "."),
+ escaped_message);
+
+ g_free(escaped_remote_user);
+ g_free(escaped_alias);
+ g_free(escaped_our_name);
+ g_free(escaped_message);
prpl_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
@@ -2616,15 +2910,13 @@ pidgin_accounts_request_authorization(PurpleAccount *account,
alert = pidgin_make_mini_dialog_with_custom_icon(
gc, prpl_icon,
_("Authorize buddy?"), NULL, aa,
- _("Authorize"), authorize_and_add_cb,
- _("Deny"), deny_no_add_cb,
+ _("Authorize"), authorize_reason_cb,
+ _("Deny"), deny_reason_cb,
NULL);
dialog = PIDGIN_MINI_DIALOG(alert);
- if (pidgin_mini_dialog_links_supported()) {
- pidgin_mini_dialog_enable_description_markup(dialog);
- pidgin_mini_dialog_set_link_callback(dialog, G_CALLBACK(get_user_info_cb), aa);
- }
+ pidgin_mini_dialog_enable_description_markup(dialog);
+ pidgin_mini_dialog_set_link_callback(dialog, G_CALLBACK(get_user_info_cb), aa);
pidgin_mini_dialog_set_description(dialog, buffer);
pidgin_mini_dialog_add_non_closing_button(dialog, _("Send Instant Message"), send_im_cb, aa);
@@ -2653,7 +2945,8 @@ static PurpleAccountUiOps ui_ops =
NULL,
NULL,
NULL,
- NULL
+ NULL,
+ NULL, NULL, NULL, NULL
};
PurpleAccountUiOps *
@@ -2663,14 +2956,14 @@ pidgin_accounts_get_ui_ops(void)
}
void *
-pidgin_account_get_handle(void) {
+pidgin_accounts_get_handle(void) {
static int handle;
return &handle;
}
void
-pidgin_account_init(void)
+pidgin_accounts_init(void)
{
char *default_avatar = NULL;
purple_prefs_add_none(PIDGIN_PREFS_ROOT "/accounts");
@@ -2690,29 +2983,28 @@ pidgin_account_init(void)
purple_prefs_add_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon", default_avatar);
g_free(default_avatar);
- purple_signal_register(pidgin_account_get_handle(), "account-modified",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT));
+ purple_signal_register(pidgin_accounts_get_handle(), "account-modified",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_ACCOUNT);
/* Setup some purple signal handlers. */
purple_signal_connect(purple_connections_get_handle(), "signed-on",
- pidgin_account_get_handle(),
+ pidgin_accounts_get_handle(),
PURPLE_CALLBACK(signed_on_off_cb), NULL);
purple_signal_connect(purple_connections_get_handle(), "signed-off",
- pidgin_account_get_handle(),
+ pidgin_accounts_get_handle(),
PURPLE_CALLBACK(signed_on_off_cb), NULL);
purple_signal_connect(purple_accounts_get_handle(), "account-added",
- pidgin_account_get_handle(),
+ pidgin_accounts_get_handle(),
PURPLE_CALLBACK(add_account_to_liststore), NULL);
purple_signal_connect(purple_accounts_get_handle(), "account-removed",
- pidgin_account_get_handle(),
+ pidgin_accounts_get_handle(),
PURPLE_CALLBACK(account_removed_cb), NULL);
purple_signal_connect(purple_accounts_get_handle(), "account-disabled",
- pidgin_account_get_handle(),
+ pidgin_accounts_get_handle(),
PURPLE_CALLBACK(account_abled_cb), GINT_TO_POINTER(FALSE));
purple_signal_connect(purple_accounts_get_handle(), "account-enabled",
- pidgin_account_get_handle(),
+ pidgin_accounts_get_handle(),
PURPLE_CALLBACK(account_abled_cb), GINT_TO_POINTER(TRUE));
account_pref_wins =
@@ -2720,7 +3012,7 @@ pidgin_account_init(void)
}
void
-pidgin_account_uninit(void)
+pidgin_accounts_uninit(void)
{
/*
* TODO: Need to free all the dialogs in here. Could probably create
@@ -2729,8 +3021,7 @@ pidgin_account_uninit(void)
*/
g_hash_table_destroy(account_pref_wins);
- purple_signals_disconnect_by_handle(pidgin_account_get_handle());
- purple_signals_unregister_by_instance(pidgin_account_get_handle());
+ purple_signals_disconnect_by_handle(pidgin_accounts_get_handle());
+ purple_signals_unregister_by_instance(pidgin_accounts_get_handle());
}
-
diff --git a/pidgin/gtkaccount.h b/pidgin/gtkaccount.h
index e9a5a5319b..e9750389c0 100644
--- a/pidgin/gtkaccount.h
+++ b/pidgin/gtkaccount.h
@@ -1,9 +1,3 @@
-/**
- * @file gtkaccount.h GTK+ Account Editor UI
- * @ingroup pidgin
- * @see @ref gtkaccount-signals
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -24,10 +18,18 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _PIDGINACCOUNT_H_
#define _PIDGINACCOUNT_H_
+/**
+ * SECTION:gtkaccount
+ * @section_id: pidgin-gtkaccount
+ * @short_description: <filename>gtkaccount.h</filename>
+ * @title: Account Editor UI
+ * @see_also: <link linkend="chapter-signals-gtkaccount">Account signals</link>
+ */
-#include "account.h"
+#include "accounts.h"
typedef enum
{
@@ -37,47 +39,64 @@ typedef enum
} PidginAccountDialogType;
+G_BEGIN_DECLS
+
/**
+ * pidgin_accounts_window_show:
+ *
* Shows the accounts window.
*/
void pidgin_accounts_window_show(void);
/**
+ * pidgin_accounts_window_hide:
+ *
* Hides the accounts window.
*/
void pidgin_accounts_window_hide(void);
/**
- * Shows an add/modify account dialog.
+ * pidgin_account_dialog_show:
+ * @type: The type of dialog.
+ * @account: The associated account, or %NULL for an Add dialog.
*
- * @param type The type of dialog.
- * @param account The associated account, or @c NULL for an Add dialog.
+ * Shows an add/modify account dialog.
*/
void pidgin_account_dialog_show(PidginAccountDialogType type,
PurpleAccount *account);
/**
+ * pidgin_accounts_get_ui_ops:
+ *
* Returns the GTK+ account UI ops
*
- * @return The UI operations structure.
+ * Returns: The UI operations structure.
*/
PurpleAccountUiOps *pidgin_accounts_get_ui_ops(void);
/**
+ * pidgin_accounts_get_handle:
+ *
* Returns the gtkaccounts handle
*
- * @return The handle to the GTK+ account system
+ * Returns: The handle to the GTK+ account system
*/
-void *pidgin_account_get_handle(void);
+void *pidgin_accounts_get_handle(void);
/**
+ * pidgin_accounts_init:
+ *
* Initializes the GTK+ account system
*/
-void pidgin_account_init(void);
+void pidgin_accounts_init(void);
/**
+ * pidgin_accounts_uninit:
+ *
* Uninitializes the GTK+ account system
*/
-void pidgin_account_uninit(void);
+void pidgin_accounts_uninit(void);
+
+G_END_DECLS
#endif /* _PIDGINACCOUNT_H_ */
diff --git a/pidgin/gtkblist-theme-loader.c b/pidgin/gtkblist-theme-loader.c
index b5936f3916..becbdf229e 100644
--- a/pidgin/gtkblist-theme-loader.c
+++ b/pidgin/gtkblist-theme-loader.c
@@ -43,15 +43,15 @@
*****************************************************************************/
static PidginThemeFont *
-pidgin_theme_font_parse(xmlnode *node)
+pidgin_theme_font_parse(PurpleXmlNode *node)
{
const char *font;
const char *colordesc;
GdkColor color;
- font = xmlnode_get_attrib(node, "font");
+ font = purple_xmlnode_get_attrib(node, "font");
- if ((colordesc = xmlnode_get_attrib(node, "color")) == NULL ||
+ if ((colordesc = purple_xmlnode_get_attrib(node, "color")) == NULL ||
!gdk_color_parse(colordesc, &color))
gdk_color_parse(DEFAULT_TEXT_COLOR, &color);
@@ -59,13 +59,15 @@ pidgin_theme_font_parse(xmlnode *node)
}
static GdkColor *
-parse_color(xmlnode *node, const char *tag)
+parse_color(PurpleXmlNode *node, const char *tag)
{
- const char *temp = xmlnode_get_attrib(node, tag);
+ const char *temp = purple_xmlnode_get_attrib(node, tag);
GdkColor color;
if (temp && gdk_color_parse(temp, &color)) {
+#if !GTK_CHECK_VERSION(3,0,0)
gdk_colormap_alloc_color(gdk_colormap_get_system(), &color, FALSE, TRUE);
+#endif
return gdk_color_copy(&color);
} else {
return NULL;
@@ -73,10 +75,10 @@ parse_color(xmlnode *node, const char *tag)
}
static PurpleTheme *
-pidgin_blist_loader_build(const gchar *dir)
+pidgin_blist_loader_build(const gchar *theme_dir)
{
- xmlnode *root_node = NULL, *sub_node, *sub_sub_node;
- gchar *filename_full, *data = NULL;
+ PurpleXmlNode *root_node = NULL, *sub_node, *sub_sub_node;
+ gchar *dir, *filename_full, *data = NULL;
const gchar *temp, *name;
gboolean success = TRUE;
GdkColor *bgcolor, *expanded_bgcolor, *collapsed_bgcolor, *contact_color;
@@ -112,29 +114,32 @@ pidgin_blist_loader_build(const gchar *dir)
status = NULL;
/* Find the theme file */
- g_return_val_if_fail(dir != NULL, NULL);
+ g_return_val_if_fail(theme_dir != NULL, NULL);
+ dir = g_build_filename(theme_dir, "purple", "blist", NULL);
filename_full = g_build_filename(dir, "theme.xml", NULL);
if (g_file_test(filename_full, G_FILE_TEST_IS_REGULAR))
- root_node = xmlnode_from_file(dir, "theme.xml", "buddy list themes", "blist-loader");
+ root_node = purple_xmlnode_from_file(dir, "theme.xml", "buddy list themes", "blist-loader");
g_free(filename_full);
- if (root_node == NULL)
+ if (root_node == NULL) {
+ g_free(dir);
return NULL;
+ }
- sub_node = xmlnode_get_child(root_node, "description");
- data = xmlnode_get_data(sub_node);
+ sub_node = purple_xmlnode_get_child(root_node, "description");
+ data = purple_xmlnode_get_data(sub_node);
- name = xmlnode_get_attrib(root_node, "name");
+ name = purple_xmlnode_get_attrib(root_node, "name");
/* <blist> */
- success = name && purple_strequal(xmlnode_get_attrib(root_node, "type"), "pidgin buddy list");
+ success = name && purple_strequal(purple_xmlnode_get_attrib(root_node, "type"), "pidgin buddy list");
if (!success)
purple_debug_warning("gtkblist-theme-loader", "Missing attribute or problem with the root element\n");
if (success) {
- if ((success = (sub_node = xmlnode_get_child(root_node, "blist")) != NULL))
+ if ((success = (sub_node = purple_xmlnode_get_child(root_node, "blist")) != NULL))
bgcolor = parse_color(sub_node, "color");
else
purple_debug_warning("gtkblist-theme-loader", "Missing or problem with tags: <blist>.\n");
@@ -142,8 +147,8 @@ pidgin_blist_loader_build(const gchar *dir)
/* <groups> */
if (success) {
- if ((success = (sub_node = xmlnode_get_child(root_node, "groups")) != NULL
- && (sub_sub_node = xmlnode_get_child(sub_node, "expanded")) != NULL)) {
+ if ((success = (sub_node = purple_xmlnode_get_child(root_node, "groups")) != NULL
+ && (sub_sub_node = purple_xmlnode_get_child(sub_node, "expanded")) != NULL)) {
expanded = pidgin_theme_font_parse(sub_sub_node);
expanded_bgcolor = parse_color(sub_sub_node, "background");
} else
@@ -151,7 +156,7 @@ pidgin_blist_loader_build(const gchar *dir)
}
if (success) {
- if ((success = sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "collapsed")) != NULL)) {
+ if ((success = sub_node != NULL && (sub_sub_node = purple_xmlnode_get_child(sub_node, "collapsed")) != NULL)) {
collapsed = pidgin_theme_font_parse(sub_sub_node);
collapsed_bgcolor = parse_color(sub_sub_node, "background");
} else
@@ -160,21 +165,21 @@ pidgin_blist_loader_build(const gchar *dir)
/* <buddys> */
if (success) {
- if ((success = (sub_node = xmlnode_get_child(root_node, "buddys")) != NULL &&
- (sub_sub_node = xmlnode_get_child(sub_node, "placement")) != NULL)) {
+ if ((success = (sub_node = purple_xmlnode_get_child(root_node, "buddys")) != NULL &&
+ (sub_sub_node = purple_xmlnode_get_child(sub_node, "placement")) != NULL)) {
- layout.status_icon = (temp = xmlnode_get_attrib(sub_sub_node, "status_icon")) != NULL ? atoi(temp) : 0;
- layout.text = (temp = xmlnode_get_attrib(sub_sub_node, "name")) != NULL ? atoi(temp) : 1;
- layout.emblem = (temp = xmlnode_get_attrib(sub_sub_node, "emblem")) != NULL ? atoi(temp) : 2;
- layout.protocol_icon = (temp = xmlnode_get_attrib(sub_sub_node, "protocol_icon")) != NULL ? atoi(temp) : 3;
- layout.buddy_icon = (temp = xmlnode_get_attrib(sub_sub_node, "buddy_icon")) != NULL ? atoi(temp) : 4;
- layout.show_status = (temp = xmlnode_get_attrib(sub_sub_node, "status_icon")) != NULL ? atoi(temp) != 0 : 1;
+ layout.status_icon = (temp = purple_xmlnode_get_attrib(sub_sub_node, "status_icon")) != NULL ? atoi(temp) : 0;
+ layout.text = (temp = purple_xmlnode_get_attrib(sub_sub_node, "name")) != NULL ? atoi(temp) : 1;
+ layout.emblem = (temp = purple_xmlnode_get_attrib(sub_sub_node, "emblem")) != NULL ? atoi(temp) : 2;
+ layout.protocol_icon = (temp = purple_xmlnode_get_attrib(sub_sub_node, "protocol_icon")) != NULL ? atoi(temp) : 3;
+ layout.buddy_icon = (temp = purple_xmlnode_get_attrib(sub_sub_node, "buddy_icon")) != NULL ? atoi(temp) : 4;
+ layout.show_status = (temp = purple_xmlnode_get_attrib(sub_sub_node, "status_icon")) != NULL ? atoi(temp) != 0 : 1;
} else purple_debug_warning("gtkblist-theme-loader", "Missing or problem with tags: <buddys> <placement>.\n");
}
if (success) {
- if ((success = (sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "background")) != NULL)))
+ if ((success = (sub_node != NULL && (sub_sub_node = purple_xmlnode_get_child(sub_node, "background")) != NULL)))
contact_color = parse_color(sub_sub_node, "color");
else
purple_debug_warning("gtkblist-theme-loader", "Missing or problem with tags: <buddys> <background>.\n");
@@ -182,7 +187,7 @@ pidgin_blist_loader_build(const gchar *dir)
for (i = 0; success && lookups[i].tag; i++) {
if ((success = (sub_node != NULL &&
- (sub_sub_node = xmlnode_get_child(sub_node, lookups[i].tag)) != NULL))) {
+ (sub_sub_node = purple_xmlnode_get_child(sub_node, lookups[i].tag)) != NULL))) {
*(lookups[i].font) = pidgin_theme_font_parse(sub_sub_node);
} else {
*(lookups[i].font) = NULL;
@@ -190,14 +195,14 @@ pidgin_blist_loader_build(const gchar *dir)
}
/* name is required for theme manager */
- success = (success && xmlnode_get_attrib(root_node, "name") != NULL);
+ success = (success && purple_xmlnode_get_attrib(root_node, "name") != NULL);
/* the new theme */
theme = g_object_new(PIDGIN_TYPE_BLIST_THEME,
"type", "blist",
"name", name,
- "author", xmlnode_get_attrib(root_node, "author"),
- "image", xmlnode_get_attrib(root_node, "image"),
+ "author", purple_xmlnode_get_attrib(root_node, "author"),
+ "image", purple_xmlnode_get_attrib(root_node, "image"),
"directory", dir,
"description", data,
"background-color", bgcolor,
@@ -213,7 +218,7 @@ pidgin_blist_loader_build(const gchar *dir)
"offline", offline,
"idle", idle,
"message", message,
- "message_nick_said", message_nick_said,
+ "message-nick-said", message_nick_said,
"status", status, NULL);
for (i = 0; lookups[i].tag; i++) {
@@ -225,8 +230,9 @@ pidgin_blist_loader_build(const gchar *dir)
pidgin_theme_font_free(expanded);
pidgin_theme_font_free(collapsed);
- xmlnode_free(root_node);
+ purple_xmlnode_free(root_node);
g_free(data);
+ g_free(dir);
/* malformed xml file - also frees all partial data*/
if (!success) {
diff --git a/pidgin/gtkblist-theme-loader.h b/pidgin/gtkblist-theme-loader.h
index 049c1c6ceb..6c1ed5091f 100644
--- a/pidgin/gtkblist-theme-loader.h
+++ b/pidgin/gtkblist-theme-loader.h
@@ -1,7 +1,3 @@
-/**
- * @file gtkblist-theme-loader.h Pidgin Buddy List Theme Loader Class API
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -25,17 +21,17 @@
#ifndef PIDGIN_BLIST_THEME_LOADER_H
#define PIDGIN_BLIST_THEME_LOADER_H
+/**
+ * SECTION:gtkblist-theme-loader
+ * @section_id: pidgin-gtkblist-theme-loader
+ * @short_description: <filename>gtkblist-theme-loader.h</filename>
+ * @title: Buddy List Theme Loader Class
+ */
#include <glib.h>
#include <glib-object.h>
#include "theme-loader.h"
-/**
- * A pidgin buddy list theme loader. extends PurpleThemeLoader (theme-loader.h)
- * This is a class designed to build sound themes
- *
- * PidginBlistThemeLoader is a GObject.
- */
typedef struct _PidginBlistThemeLoader PidginBlistThemeLoader;
typedef struct _PidginBlistThemeLoaderClass PidginBlistThemeLoaderClass;
@@ -46,6 +42,12 @@ typedef struct _PidginBlistThemeLoaderClass PidginBlistThemeLoaderClass;
#define PIDGIN_IS_BLIST_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_BLIST_THEME_LOADER))
#define PIDGIN_BLIST_THEME_LOADER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_BLIST_THEME_LOADER, PidginBlistThemeLoaderClass))
+/**
+ * PidginBlistThemeLoader:
+ *
+ * A pidgin buddy list theme loader. extends PurpleThemeLoader (theme-loader.h)
+ * This is a class designed to build sound themes
+ */
struct _PidginBlistThemeLoader
{
PurpleThemeLoader parent;
@@ -57,15 +59,17 @@ struct _PidginBlistThemeLoaderClass
};
/**************************************************************************/
-/** @name Buddy List Theme-Loader API */
+/* Buddy List Theme-Loader API */
/**************************************************************************/
G_BEGIN_DECLS
/**
- * GObject foo.
- * @internal.
+ * pidgin_blist_theme_loader_get_type:
+ *
+ * Returns: The #GType for a blist theme loader.
*/
GType pidgin_blist_theme_loader_get_type(void);
G_END_DECLS
+
#endif /* PIDGIN_BLIST_THEME_LOADER_H */
diff --git a/pidgin/gtkblist-theme.c b/pidgin/gtkblist-theme.c
index 83585aa0bd..f6de377662 100644
--- a/pidgin/gtkblist-theme.c
+++ b/pidgin/gtkblist-theme.c
@@ -21,10 +21,12 @@
*/
#include "internal.h"
+#include "glibcompat.h"
+
#include "gtkblist-theme.h"
#define PIDGIN_BLIST_THEME_GET_PRIVATE(Gobject) \
- ((PidginBlistThemePrivate *) ((PIDGIN_BLIST_THEME(Gobject))->priv))
+ (G_TYPE_INSTANCE_GET_PRIVATE((Gobject), PIDGIN_TYPE_BLIST_THEME, PidginBlistThemePrivate))
/******************************************************************************
* Structs
@@ -67,12 +69,6 @@ struct _PidginThemeFont
};
/******************************************************************************
- * Globals
- *****************************************************************************/
-
-static GObjectClass *parent_class = NULL;
-
-/******************************************************************************
* Enums
*****************************************************************************/
@@ -94,9 +90,17 @@ enum {
PROP_MESSAGE,
PROP_MESSAGE_NICK_SAID,
PROP_STATUS,
+ PROP_LAST
};
/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+static GObjectClass *parent_class = NULL;
+static GParamSpec *properties[PROP_LAST];
+
+/******************************************************************************
* Helpers
*****************************************************************************/
@@ -189,13 +193,6 @@ pidgin_theme_font_get_color_describe(PidginThemeFont *font)
*****************************************************************************/
static void
-pidgin_blist_theme_init(GTypeInstance *instance,
- gpointer klass)
-{
- (PIDGIN_BLIST_THEME(instance))->priv = g_new0(PidginBlistThemePrivate, 1);
-}
-
-static void
pidgin_blist_theme_get_property(GObject *obj, guint param_id, GValue *value,
GParamSpec *psec)
{
@@ -349,125 +346,110 @@ pidgin_blist_theme_finalize(GObject *obj)
pidgin_theme_font_free(priv->message_nick_said);
pidgin_theme_font_free(priv->status);
- g_free(priv);
-
parent_class->finalize (obj);
}
static void
+pidgin_blist_theme_init(PidginBlistTheme *theme)
+{
+ PIDGIN_BLIST_THEME_GET_PRIVATE(theme)->opacity = 1.0;
+}
+
+static void
pidgin_blist_theme_class_init(PidginBlistThemeClass *klass)
{
GObjectClass *obj_class = G_OBJECT_CLASS(klass);
- GParamSpec *pspec;
parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private(klass, sizeof(PidginBlistThemePrivate));
+
obj_class->get_property = pidgin_blist_theme_get_property;
obj_class->set_property = pidgin_blist_theme_set_property;
obj_class->finalize = pidgin_blist_theme_finalize;
/* Buddy List */
- pspec = g_param_spec_boxed("background-color", _("Background Color"),
- _("The background color for the buddy list"),
- GDK_TYPE_COLOR, G_PARAM_READWRITE);
- g_object_class_install_property(obj_class, PROP_BACKGROUND_COLOR, pspec);
+ properties[PROP_BACKGROUND_COLOR] = g_param_spec_boxed("background-color",
+ "Background Color",
+ "The background color for the buddy list",
+ GDK_TYPE_COLOR, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- pspec = g_param_spec_pointer("layout", _("Layout"),
- _("The layout of icons, name, and status of the buddy list"),
- G_PARAM_READWRITE);
+ properties[PROP_OPACITY] = g_param_spec_double("opacity", "Opacity",
+ "The opacity of the buddy list",
+ 0.0, 1.0, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property(obj_class, PROP_LAYOUT, pspec);
+ properties[PROP_LAYOUT] = g_param_spec_pointer("layout", "Layout",
+ "The layout of icons, name, and status of the buddy list",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/* Group */
- /* Note to translators: These two strings refer to the background color
- of a buddy list group when in its expanded state */
- pspec = g_param_spec_boxed("expanded-color", _("Expanded Background Color"),
- _("The background color of an expanded group"),
- GDK_TYPE_COLOR, G_PARAM_READWRITE);
- g_object_class_install_property(obj_class, PROP_EXPANDED_COLOR, pspec);
-
- /* Note to translators: These two strings refer to the font and color
- of a buddy list group when in its expanded state */
- pspec = g_param_spec_pointer("expanded-text", _("Expanded Text"),
- _("The text information for when a group is expanded"),
- G_PARAM_READWRITE);
- g_object_class_install_property(obj_class, PROP_EXPANDED_TEXT, pspec);
-
- /* Note to translators: These two strings refer to the background color
- of a buddy list group when in its collapsed state */
- pspec = g_param_spec_boxed("collapsed-color", _("Collapsed Background Color"),
- _("The background color of a collapsed group"),
- GDK_TYPE_COLOR, G_PARAM_READWRITE);
- g_object_class_install_property(obj_class, PROP_COLLAPSED_COLOR, pspec);
-
- /* Note to translators: These two strings refer to the font and color
- of a buddy list group when in its collapsed state */
- pspec = g_param_spec_pointer("collapsed-text", _("Collapsed Text"),
- _("The text information for when a group is collapsed"),
- G_PARAM_READWRITE);
- g_object_class_install_property(obj_class, PROP_COLLAPSED_TEXT, pspec);
+ properties[PROP_EXPANDED_COLOR] = g_param_spec_boxed("expanded-color",
+ "Expanded Background Color",
+ "The background color of an expanded group",
+ GDK_TYPE_COLOR, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_EXPANDED_TEXT] = g_param_spec_pointer("expanded-text",
+ "Expanded Text",
+ "The text information for when a group is expanded",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_COLLAPSED_COLOR] = g_param_spec_boxed("collapsed-color",
+ "Collapsed Background Color",
+ "The background color of a collapsed group",
+ GDK_TYPE_COLOR, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_COLLAPSED_TEXT] = g_param_spec_pointer("collapsed-text",
+ "Collapsed Text",
+ "The text information for when a group is collapsed",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/* Buddy */
- /* Note to translators: These two strings refer to the background color
- of a buddy list contact or chat room */
- pspec = g_param_spec_boxed("contact-color", _("Contact/Chat Background Color"),
- _("The background color of a contact or chat"),
- GDK_TYPE_COLOR, G_PARAM_READWRITE);
- g_object_class_install_property(obj_class, PROP_CONTACT_COLOR, pspec);
-
- /* Note to translators: These two strings refer to the font and color
- of a buddy list contact when in its expanded state */
- pspec = g_param_spec_pointer("contact", _("Contact Text"),
- _("The text information for when a contact is expanded"),
- G_PARAM_READWRITE);
- g_object_class_install_property(obj_class, PROP_CONTACT, pspec);
-
- /* Note to translators: These two strings refer to the font and color
- of a buddy list buddy when it is online */
- pspec = g_param_spec_pointer("online", _("Online Text"),
- _("The text information for when a buddy is online"),
- G_PARAM_READWRITE);
- g_object_class_install_property(obj_class, PROP_ONLINE, pspec);
-
- /* Note to translators: These two strings refer to the font and color
- of a buddy list buddy when it is away */
- pspec = g_param_spec_pointer("away", _("Away Text"),
- _("The text information for when a buddy is away"),
- G_PARAM_READWRITE);
- g_object_class_install_property(obj_class, PROP_AWAY, pspec);
-
- /* Note to translators: These two strings refer to the font and color
- of a buddy list buddy when it is offline */
- pspec = g_param_spec_pointer("offline", _("Offline Text"),
- _("The text information for when a buddy is offline"),
- G_PARAM_READWRITE);
- g_object_class_install_property(obj_class, PROP_OFFLINE, pspec);
-
- /* Note to translators: These two strings refer to the font and color
- of a buddy list buddy when it is idle */
- pspec = g_param_spec_pointer("idle", _("Idle Text"),
- _("The text information for when a buddy is idle"),
- G_PARAM_READWRITE);
- g_object_class_install_property(obj_class, PROP_IDLE, pspec);
-
- /* Note to translators: These two strings refer to the font and color
- of a buddy list buddy when they have sent you a new message */
- pspec = g_param_spec_pointer("message", _("Message Text"),
- _("The text information for when a buddy has an unread message"),
- G_PARAM_READWRITE);
- g_object_class_install_property(obj_class, PROP_MESSAGE, pspec);
-
- /* Note to translators: These two strings refer to the font and color
- of a buddy list buddy when they have sent you a new message */
- pspec = g_param_spec_pointer("message_nick_said", _("Message (Nick Said) Text"),
- _("The text information for when a chat has an unread message that mentions your nickname"),
- G_PARAM_READWRITE);
- g_object_class_install_property(obj_class, PROP_MESSAGE_NICK_SAID, pspec);
-
- pspec = g_param_spec_pointer("status", _("Status Text"),
- _("The text information for a buddy's status"),
- G_PARAM_READWRITE);
- g_object_class_install_property(obj_class, PROP_STATUS, pspec);
+ properties[PROP_CONTACT_COLOR] = g_param_spec_boxed("contact-color",
+ "Contact/Chat Background Color",
+ "The background color of a contact or chat",
+ GDK_TYPE_COLOR, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_CONTACT] = g_param_spec_pointer("contact",
+ "Contact Text",
+ "The text information for when a contact is expanded",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_ONLINE] = g_param_spec_pointer("online",
+ "Online Text",
+ "The text information for when a buddy is online",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_AWAY] = g_param_spec_pointer("away",
+ "Away Text",
+ "The text information for when a buddy is away",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_OFFLINE] = g_param_spec_pointer("offline",
+ "Offline Text",
+ "The text information for when a buddy is offline",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_IDLE] = g_param_spec_pointer("idle",
+ "Idle Text",
+ "The text information for when a buddy is idle",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_MESSAGE] = g_param_spec_pointer("message",
+ "Message Text",
+ "The text information for when a buddy has an unread message",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_MESSAGE_NICK_SAID] = g_param_spec_pointer("message-nick-said",
+ "Message (Nick Said) Text",
+ "The text information for when a chat has an unread message that mentions your nickname",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_STATUS] = g_param_spec_pointer("status",
+ "Status Text",
+ "The text information for a buddy's status",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, PROP_LAST, properties);
}
GType
@@ -484,7 +466,7 @@ pidgin_blist_theme_get_type (void)
NULL, /* class_data */
sizeof(PidginBlistTheme),
0, /* n_preallocs */
- pidgin_blist_theme_init, /* instance_init */
+ (GInstanceInitFunc)pidgin_blist_theme_init, /* instance_init */
NULL, /* value table */
};
type = g_type_register_static (PURPLE_TYPE_THEME,
@@ -493,6 +475,54 @@ pidgin_blist_theme_get_type (void)
return type;
}
+/**************************************************************************
+ * GBoxed Stuff
+ **************************************************************************/
+
+static PidginThemeFont *
+pidgin_theme_font_copy(PidginThemeFont *font)
+{
+ g_return_val_if_fail(font != NULL, NULL);
+
+ return pidgin_theme_font_new(font->font, font->gdkcolor);
+}
+
+GType
+pidgin_theme_font_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PidginThemeFont",
+ (GBoxedCopyFunc)pidgin_theme_font_copy,
+ (GBoxedFreeFunc)pidgin_theme_font_free);
+ }
+
+ return type;
+}
+
+static PidginBlistLayout *
+pidgin_blist_layout_copy(const PidginBlistLayout *layout)
+{
+ g_return_val_if_fail(layout != NULL, NULL);
+
+ return g_memdup(layout, sizeof(PidginBlistLayout));
+}
+
+GType
+pidgin_blist_layout_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PidginBlistLayout",
+ (GBoxedCopyFunc)pidgin_blist_layout_copy,
+ (GBoxedFreeFunc)g_free);
+ }
+
+ return type;
+}
+
/*****************************************************************************
* Public API functions
@@ -507,7 +537,7 @@ pidgin_blist_theme_get_background_color(PidginBlistTheme *theme)
g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
return priv->bgcolor;
}
@@ -519,7 +549,7 @@ pidgin_blist_theme_get_opacity(PidginBlistTheme *theme)
g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), 1.0);
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
return priv->opacity;
}
@@ -531,7 +561,7 @@ pidgin_blist_theme_get_layout(PidginBlistTheme *theme)
g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
return priv->layout;
}
@@ -543,7 +573,7 @@ pidgin_blist_theme_get_expanded_background_color(PidginBlistTheme *theme)
g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
return priv->expanded_color;
}
@@ -555,7 +585,7 @@ pidgin_blist_theme_get_expanded_text_info(PidginBlistTheme *theme)
g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
return priv->expanded;
}
@@ -567,7 +597,7 @@ pidgin_blist_theme_get_collapsed_background_color(PidginBlistTheme *theme)
g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
return priv->collapsed_color;
}
@@ -579,7 +609,7 @@ pidgin_blist_theme_get_collapsed_text_info(PidginBlistTheme *theme)
g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
return priv->collapsed;
}
@@ -591,7 +621,7 @@ pidgin_blist_theme_get_contact_color(PidginBlistTheme *theme)
g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
return priv->contact_color;
}
@@ -603,7 +633,7 @@ pidgin_blist_theme_get_contact_text_info(PidginBlistTheme *theme)
g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
return priv->contact;
}
@@ -615,7 +645,7 @@ pidgin_blist_theme_get_online_text_info(PidginBlistTheme *theme)
g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
return priv->online;
}
@@ -627,7 +657,7 @@ pidgin_blist_theme_get_away_text_info(PidginBlistTheme *theme)
g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
return priv->away;
}
@@ -639,7 +669,7 @@ pidgin_blist_theme_get_offline_text_info(PidginBlistTheme *theme)
g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
return priv->offline;
}
@@ -651,7 +681,7 @@ pidgin_blist_theme_get_idle_text_info(PidginBlistTheme *theme)
g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
return priv->idle;
}
@@ -663,7 +693,7 @@ pidgin_blist_theme_get_unread_message_text_info(PidginBlistTheme *theme)
g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
return priv->message;
}
@@ -675,7 +705,7 @@ pidgin_blist_theme_get_unread_message_nick_said_text_info(PidginBlistTheme *them
g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
return priv->message_nick_said;
}
@@ -687,7 +717,7 @@ pidgin_blist_theme_get_status_text_info(PidginBlistTheme *theme)
g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
return priv->status;
}
@@ -700,11 +730,13 @@ pidgin_blist_theme_set_background_color(PidginBlistTheme *theme, const GdkColor
g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
if (priv->bgcolor)
gdk_color_free(priv->bgcolor);
priv->bgcolor = color ? gdk_color_copy(color) : NULL;
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_BACKGROUND_COLOR]);
}
void
@@ -714,9 +746,11 @@ pidgin_blist_theme_set_opacity(PidginBlistTheme *theme, gdouble opacity)
g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme) || opacity < 0.0 || opacity > 1.0);
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
priv->opacity = opacity;
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_OPACITY]);
}
void
@@ -726,10 +760,12 @@ pidgin_blist_theme_set_layout(PidginBlistTheme *theme, const PidginBlistLayout *
g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
g_free(priv->layout);
- priv->layout = g_memdup(layout, sizeof(PidginBlistLayout));
+ priv->layout = pidgin_blist_layout_copy(layout);
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_LAYOUT]);
}
void
@@ -739,11 +775,13 @@ pidgin_blist_theme_set_expanded_background_color(PidginBlistTheme *theme, const
g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
if (priv->expanded_color)
gdk_color_free(priv->expanded_color);
priv->expanded_color = color ? gdk_color_copy(color) : NULL;
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_EXPANDED_COLOR]);
}
void
@@ -753,10 +791,12 @@ pidgin_blist_theme_set_expanded_text_info(PidginBlistTheme *theme, const PidginT
g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
pidgin_theme_font_free(priv->expanded);
priv->expanded = copy_font_and_color(pair);
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_EXPANDED_TEXT]);
}
void
@@ -766,11 +806,13 @@ pidgin_blist_theme_set_collapsed_background_color(PidginBlistTheme *theme, const
g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
if (priv->collapsed_color)
gdk_color_free(priv->collapsed_color);
priv->collapsed_color = color ? gdk_color_copy(color) : NULL;
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_COLLAPSED_COLOR]);
}
void
@@ -780,10 +822,12 @@ pidgin_blist_theme_set_collapsed_text_info(PidginBlistTheme *theme, const Pidgin
g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
pidgin_theme_font_free(priv->collapsed);
priv->collapsed = copy_font_and_color(pair);
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_COLLAPSED_TEXT]);
}
void
@@ -793,11 +837,13 @@ pidgin_blist_theme_set_contact_color(PidginBlistTheme *theme, const GdkColor *co
g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
if (priv->contact_color)
gdk_color_free(priv->contact_color);
priv->contact_color = color ? gdk_color_copy(color) : NULL;
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_CONTACT_COLOR]);
}
void
@@ -807,10 +853,12 @@ pidgin_blist_theme_set_contact_text_info(PidginBlistTheme *theme, const PidginTh
g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
pidgin_theme_font_free(priv->contact);
priv->contact = copy_font_and_color(pair);
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_CONTACT]);
}
void
@@ -820,10 +868,12 @@ pidgin_blist_theme_set_online_text_info(PidginBlistTheme *theme, const PidginThe
g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
pidgin_theme_font_free(priv->online);
priv->online = copy_font_and_color(pair);
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_ONLINE]);
}
void
@@ -833,10 +883,12 @@ pidgin_blist_theme_set_away_text_info(PidginBlistTheme *theme, const PidginTheme
g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
pidgin_theme_font_free(priv->away);
priv->away = copy_font_and_color(pair);
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_AWAY]);
}
void
@@ -846,10 +898,12 @@ pidgin_blist_theme_set_offline_text_info(PidginBlistTheme *theme, const PidginTh
g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
pidgin_theme_font_free(priv->offline);
priv->offline = copy_font_and_color(pair);
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_OFFLINE]);
}
void
@@ -859,10 +913,12 @@ pidgin_blist_theme_set_idle_text_info(PidginBlistTheme *theme, const PidginTheme
g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
pidgin_theme_font_free(priv->idle);
priv->idle = copy_font_and_color(pair);
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_IDLE]);
}
void
@@ -872,10 +928,12 @@ pidgin_blist_theme_set_unread_message_text_info(PidginBlistTheme *theme, const P
g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
pidgin_theme_font_free(priv->message);
priv->message = copy_font_and_color(pair);
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_MESSAGE]);
}
void
@@ -885,10 +943,12 @@ pidgin_blist_theme_set_unread_message_nick_said_text_info(PidginBlistTheme *them
g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
pidgin_theme_font_free(priv->message_nick_said);
priv->message_nick_said = copy_font_and_color(pair);
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_MESSAGE_NICK_SAID]);
}
void
@@ -898,8 +958,10 @@ pidgin_blist_theme_set_status_text_info(PidginBlistTheme *theme, const PidginThe
g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
- priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
pidgin_theme_font_free(priv->status);
priv->status = copy_font_and_color(pair);
+
+ g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_STATUS]);
}
diff --git a/pidgin/gtkblist-theme.h b/pidgin/gtkblist-theme.h
index 2c4fb07fb0..8385dfecf4 100644
--- a/pidgin/gtkblist-theme.h
+++ b/pidgin/gtkblist-theme.h
@@ -1,7 +1,3 @@
-/**
- * @file gtkblist-theme.h GTK+ Buddy List Theme API
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -25,6 +21,12 @@
#ifndef PIDGIN_BLIST_THEME_H
#define PIDGIN_BLIST_THEME_H
+/**
+ * SECTION:gtkblist-theme
+ * @section_id: pidgin-gtkblist-theme
+ * @short_description: <filename>gtkblist-theme.h</filename>
+ * @title: Buddy List Theme API
+ */
#include <glib.h>
#include <glib-object.h>
@@ -32,12 +34,6 @@
#include "theme.h"
-/**
- * A pidgin buddy list theme.
- * This is an object for Purple to represent a buddy list theme.
- *
- * PidginBlistTheme is a PurpleTheme Object.
- */
typedef struct _PidginBlistTheme PidginBlistTheme;
typedef struct _PidginBlistThemeClass PidginBlistThemeClass;
@@ -48,10 +44,21 @@ typedef struct _PidginBlistThemeClass PidginBlistThemeClass;
#define PIDGIN_IS_BLIST_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_BLIST_THEME))
#define PIDGIN_BLIST_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_BLIST_THEME, PidginBlistThemeClass))
+#define PIDGIN_TYPE_THEME_FONT (pidgin_theme_font_get_type())
+
+#define PIDGIN_TYPE_BLIST_LAYOUT (pidgin_blist_layout_get_type())
+
+/**
+ * PidginBlistTheme:
+ *
+ * A pidgin buddy list theme.
+ * This is an object for Purple to represent a buddy list theme.
+ *
+ * PidginBlistTheme is a PurpleTheme Object.
+ */
struct _PidginBlistTheme
{
PurpleTheme parent;
- gpointer priv;
};
struct _PidginBlistThemeClass
@@ -59,17 +66,11 @@ struct _PidginBlistThemeClass
PurpleThemeClass parent_class;
};
-#if 0
-typedef struct
-{
- const gchar *font;
- const gchar *color;
-
-} PidginThemeFont;
-#endif
typedef struct _PidginThemeFont PidginThemeFont;
-typedef struct
+typedef struct _PidginBlistLayout PidginBlistLayout;
+
+struct _PidginBlistLayout
{
gint status_icon;
gint text;
@@ -78,361 +79,417 @@ typedef struct
gint buddy_icon;
gboolean show_status;
-} PidginBlistLayout;
+};
+
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name PidginThemeFont API */
+/* PidginThemeFont API */
/**************************************************************************/
/**
- * Create a new PidginThemeFont.
+ * pidgin_theme_font_get_type:
+ *
+ * Returns: The #GType for the #PidginThemeFont boxed structure.
+ */
+GType pidgin_theme_font_get_type(void);
+
+/**
+ * pidgin_theme_font_new:
+ * @face: The font face
+ * @color: The color of the font
*
- * @param face The font face
- * @param color The color of the font
+ * Create a new PidginThemeFont.
*
- * @return A newly created PidginThemeFont
+ * Returns: A newly created PidginThemeFont
*/
PidginThemeFont * pidgin_theme_font_new(const gchar *face, GdkColor *color);
/**
- * Frees a font and color pair
+ * pidgin_theme_font_free:
+ * @font: The theme font
*
- * @param font The theme font
+ * Frees a font and color pair
*/
void pidgin_theme_font_free(PidginThemeFont *font);
/**
- * Set the font-face of a PidginThemeFont.
+ * pidgin_theme_font_set_font_face:
+ * @font: The PidginThemeFont
+ * @face: The font-face
*
- * @param font The PidginThemeFont
- * @param face The font-face
+ * Set the font-face of a PidginThemeFont.
*/
void pidgin_theme_font_set_font_face(PidginThemeFont *font, const gchar *face);
/**
- * Set the color of a PidginThemeFont.
+ * pidgin_theme_font_set_color:
+ * @font: The PidginThemeFont
+ * @color: The color
*
- * @param font The PidginThemeFont
- * @param color The color
+ * Set the color of a PidginThemeFont.
*/
void pidgin_theme_font_set_color(PidginThemeFont *font, const GdkColor *color);
/**
- * Get the font-face of a PidginThemeFont.
+ * pidgin_theme_font_get_font_face:
+ * @font: The PidginThemeFont
*
- * @param font The PidginThemeFont
+ * Get the font-face of a PidginThemeFont.
*
- * @return The font-face, or NULL if none is set.
+ * Returns: The font-face, or NULL if none is set.
*/
const gchar * pidgin_theme_font_get_font_face(PidginThemeFont *font);
/**
- * Get the color of a PidginThemeFont as a GdkColor object.
+ * pidgin_theme_font_get_color:
+ * @font: The PidginThemeFont
*
- * @param font The PidginThemeFont
+ * Get the color of a PidginThemeFont as a GdkColor object.
*
- * @return The color, or NULL if none is set.
+ * Returns: The color, or NULL if none is set.
*/
const GdkColor * pidgin_theme_font_get_color(PidginThemeFont *font);
/**
- * Get the color of a PidginThemeFont.
+ * pidgin_theme_font_get_color_describe:
+ * @font: The PidginThemeFont
*
- * @param font The PidginThemeFont
+ * Get the color of a PidginThemeFont.
*
- * @return The color, or NULL if none is set.
+ * Returns: The color, or NULL if none is set.
*/
const gchar * pidgin_theme_font_get_color_describe(PidginThemeFont *font);
/**************************************************************************/
-/** @name Purple Buddy List Theme API */
+/* Purple Buddy List Theme API */
/**************************************************************************/
-G_BEGIN_DECLS
/**
- * GObject foo.
- * @internal.
+ * pidgin_blist_theme_get_type:
+ *
+ * Returns: The #GType for a blist theme.
*/
GType pidgin_blist_theme_get_type(void);
+/**
+ * pidgin_blist_layout_get_type:
+ *
+ * Returns: The #GType for the #PidginBlistLayout boxed structure.
+ */
+GType pidgin_blist_layout_get_type(void);
+
/* get methods */
/**
- * Returns the background color of the buddy list.
+ * pidgin_blist_theme_get_background_color:
+ * @theme: The PidginBlist theme.
*
- * @param theme The PidginBlist theme.
+ * Returns the background color of the buddy list.
*
- * @returns A gdk color.
+ * Returns: A gdk color.
*/
GdkColor *pidgin_blist_theme_get_background_color(PidginBlistTheme *theme);
/**
+ * pidgin_blist_theme_get_opacity:
+ * @theme: The PidginBlist theme.
+ *
* Returns the opacity of the buddy list window
* (0.0 or clear to 1.0 fully opaque).
*
- * @param theme The PidginBlist theme.
- *
- * @returns The opacity
+ * Returns: The opacity
*/
gdouble pidgin_blist_theme_get_opacity(PidginBlistTheme *theme);
/**
- * Returns the layout to be used with the buddy list.
+ * pidgin_blist_theme_get_layout:
+ * @theme: The PidginBlist theme.
*
- * @param theme The PidginBlist theme.
+ * Returns the layout to be used with the buddy list.
*
- * @returns The buddy list layout.
+ * Returns: The buddy list layout.
*/
- PidginBlistLayout *pidgin_blist_theme_get_layout(PidginBlistTheme *theme);
+PidginBlistLayout *pidgin_blist_theme_get_layout(PidginBlistTheme *theme);
/**
- * Returns the background color to be used with expanded groups.
+ * pidgin_blist_theme_get_expanded_background_color:
+ * @theme: The PidginBlist theme.
*
- * @param theme The PidginBlist theme.
+ * Returns the background color to be used with expanded groups.
*
- * @returns A gdk color.
+ * Returns: A gdk color.
*/
- GdkColor *pidgin_blist_theme_get_expanded_background_color(PidginBlistTheme *theme);
+GdkColor *pidgin_blist_theme_get_expanded_background_color(PidginBlistTheme *theme);
/**
- * Returns the text font and color to be used with expanded groups.
+ * pidgin_blist_theme_get_expanded_text_info:
+ * @theme: The PidginBlist theme.
*
- * @param theme The PidginBlist theme.
+ * Returns the text font and color to be used with expanded groups.
*
- * @returns A font and color pair.
+ * Returns: A font and color pair.
*/
- PidginThemeFont *pidgin_blist_theme_get_expanded_text_info(PidginBlistTheme *theme);
+PidginThemeFont *pidgin_blist_theme_get_expanded_text_info(PidginBlistTheme *theme);
/**
- * Returns the background color to be used with collapsed groups.
+ * pidgin_blist_theme_get_collapsed_background_color:
+ * @theme: The PidginBlist theme.
*
- * @param theme The PidginBlist theme.
+ * Returns the background color to be used with collapsed groups.
*
- * @returns A gdk color.
+ * Returns: A gdk color.
*/
- GdkColor *pidgin_blist_theme_get_collapsed_background_color(PidginBlistTheme *theme);
+GdkColor *pidgin_blist_theme_get_collapsed_background_color(PidginBlistTheme *theme);
/**
- * Returns the text font and color to be used with collapsed groups.
+ * pidgin_blist_theme_get_collapsed_text_info:
+ * @theme: The PidginBlist theme.
*
- * @param theme The PidginBlist theme.
+ * Returns the text font and color to be used with collapsed groups.
*
- * @returns A font and color pair.
+ * Returns: A font and color pair.
*/
- PidginThemeFont *pidgin_blist_theme_get_collapsed_text_info(PidginBlistTheme *theme);
+PidginThemeFont *pidgin_blist_theme_get_collapsed_text_info(PidginBlistTheme *theme);
/**
- * Returns the colors to be used for contacts and chats.
+ * pidgin_blist_theme_get_contact_color:
+ * @theme: The PidginBlist theme.
*
- * @param theme The PidginBlist theme.
+ * Returns the colors to be used for contacts and chats.
*
- * @returns A gdkcolor for contacts and chats.
+ * Returns: A gdkcolor for contacts and chats.
*/
- GdkColor *pidgin_blist_theme_get_contact_color(PidginBlistTheme *theme);
+GdkColor *pidgin_blist_theme_get_contact_color(PidginBlistTheme *theme);
/**
- * Returns the text font and color to be used for expanded contacts.
+ * pidgin_blist_theme_get_contact_text_info:
+ * @theme: The PidginBlist theme.
*
- * @param theme The PidginBlist theme.
+ * Returns the text font and color to be used for expanded contacts.
*
- * @returns A font and color pair.
+ * Returns: A font and color pair.
*/
- PidginThemeFont *pidgin_blist_theme_get_contact_text_info(PidginBlistTheme *theme);
+PidginThemeFont *pidgin_blist_theme_get_contact_text_info(PidginBlistTheme *theme);
/**
- * Returns the text font and color to be used for online buddies.
+ * pidgin_blist_theme_get_online_text_info:
+ * @theme: The PidginBlist theme.
*
- * @param theme The PidginBlist theme.
+ * Returns the text font and color to be used for online buddies.
*
- * @returns A font and color pair.
+ * Returns: A font and color pair.
*/
- PidginThemeFont *pidgin_blist_theme_get_online_text_info(PidginBlistTheme *theme);
+PidginThemeFont *pidgin_blist_theme_get_online_text_info(PidginBlistTheme *theme);
/**
- * Returns the text font and color to be used for away and idle buddies.
+ * pidgin_blist_theme_get_away_text_info:
+ * @theme: The PidginBlist theme.
*
- * @param theme The PidginBlist theme.
+ * Returns the text font and color to be used for away and idle buddies.
*
- * @returns A font and color pair.
+ * Returns: A font and color pair.
*/
- PidginThemeFont *pidgin_blist_theme_get_away_text_info(PidginBlistTheme *theme);
+PidginThemeFont *pidgin_blist_theme_get_away_text_info(PidginBlistTheme *theme);
/**
- * Returns the text font and color to be used for offline buddies.
+ * pidgin_blist_theme_get_offline_text_info:
+ * @theme: The PidginBlist theme.
*
- * @param theme The PidginBlist theme.
+ * Returns the text font and color to be used for offline buddies.
*
- * @returns A font and color pair.
+ * Returns: A font and color pair.
*/
- PidginThemeFont *pidgin_blist_theme_get_offline_text_info(PidginBlistTheme *theme);
+PidginThemeFont *pidgin_blist_theme_get_offline_text_info(PidginBlistTheme *theme);
/**
- * Returns the text font and color to be used for idle buddies.
+ * pidgin_blist_theme_get_idle_text_info:
+ * @theme: The PidginBlist theme.
*
- * @param theme The PidginBlist theme.
+ * Returns the text font and color to be used for idle buddies.
*
- * @returns A font and color pair.
+ * Returns: A font and color pair.
*/
- PidginThemeFont *pidgin_blist_theme_get_idle_text_info(PidginBlistTheme *theme);
+PidginThemeFont *pidgin_blist_theme_get_idle_text_info(PidginBlistTheme *theme);
/**
- * Returns the text font and color to be used for buddies with unread messages.
+ * pidgin_blist_theme_get_unread_message_text_info:
+ * @theme: The PidginBlist theme.
*
- * @param theme The PidginBlist theme.
+ * Returns the text font and color to be used for buddies with unread messages.
*
- * @returns A font and color pair.
+ * Returns: A font and color pair.
*/
- PidginThemeFont *pidgin_blist_theme_get_unread_message_text_info(PidginBlistTheme *theme);
+PidginThemeFont *pidgin_blist_theme_get_unread_message_text_info(PidginBlistTheme *theme);
/**
+ * pidgin_blist_theme_get_unread_message_nick_said_text_info:
+ * @theme: The PidginBlist theme.
+ *
* Returns the text font and color to be used for chats with unread messages
* that mention your nick.
*
- * @param theme The PidginBlist theme.
- *
- * @returns A font and color pair.
+ * Returns: A font and color pair.
*/
- PidginThemeFont *pidgin_blist_theme_get_unread_message_nick_said_text_info(PidginBlistTheme *theme);
+PidginThemeFont *pidgin_blist_theme_get_unread_message_nick_said_text_info(PidginBlistTheme *theme);
/**
- * Returns the text font and color to be used for a buddy's status message.
+ * pidgin_blist_theme_get_status_text_info:
+ * @theme: The PidginBlist theme.
*
- * @param theme The PidginBlist theme.
+ * Returns the text font and color to be used for a buddy's status message.
*
- * @returns A font and color pair.
+ * Returns: A font and color pair.
*/
- PidginThemeFont *pidgin_blist_theme_get_status_text_info(PidginBlistTheme *theme);
+PidginThemeFont *pidgin_blist_theme_get_status_text_info(PidginBlistTheme *theme);
/* Set Methods */
/**
- * Sets the background color to be used for this buddy list theme.
+ * pidgin_blist_theme_set_background_color:
+ * @theme: The PidginBlist theme.
+ * @color: The new background color.
*
- * @param theme The PidginBlist theme.
- * @param color The new background color.
+ * Sets the background color to be used for this buddy list theme.
*/
void pidgin_blist_theme_set_background_color(PidginBlistTheme *theme, const GdkColor *color);
/**
- * Sets the opacity to be used for this buddy list theme.
+ * pidgin_blist_theme_set_opacity:
+ * @theme: The PidginBlist theme.
+ * @opacity: The new opacity setting.
*
- * @param theme The PidginBlist theme.
- * @param opacity The new opacity setting.
+ * Sets the opacity to be used for this buddy list theme.
*/
void pidgin_blist_theme_set_opacity(PidginBlistTheme *theme, gdouble opacity);
/**
- * Sets the buddy list layout to be used for this buddy list theme.
+ * pidgin_blist_theme_set_layout:
+ * @theme: The PidginBlist theme.
+ * @layout: The new layout.
*
- * @param theme The PidginBlist theme.
- * @param layout The new layout.
+ * Sets the buddy list layout to be used for this buddy list theme.
*/
void pidgin_blist_theme_set_layout(PidginBlistTheme *theme, const PidginBlistLayout *layout);
/**
- * Sets the background color to be used for expanded groups.
+ * pidgin_blist_theme_set_expanded_background_color:
+ * @theme: The PidginBlist theme.
+ * @color: The new background color.
*
- * @param theme The PidginBlist theme.
- * @param color The new background color.
+ * Sets the background color to be used for expanded groups.
*/
void pidgin_blist_theme_set_expanded_background_color(PidginBlistTheme *theme, const GdkColor *color);
/**
- * Sets the text color and font to be used for expanded groups.
+ * pidgin_blist_theme_set_expanded_text_info:
+ * @theme: The PidginBlist theme.
+ * @pair: The new text font and color pair.
*
- * @param theme The PidginBlist theme.
- * @param pair The new text font and color pair.
+ * Sets the text color and font to be used for expanded groups.
*/
void pidgin_blist_theme_set_expanded_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair);
/**
- * Sets the background color to be used for collapsed groups.
+ * pidgin_blist_theme_set_collapsed_background_color:
+ * @theme: The PidginBlist theme.
+ * @color: The new background color.
*
- * @param theme The PidginBlist theme.
- * @param color The new background color.
+ * Sets the background color to be used for collapsed groups.
*/
void pidgin_blist_theme_set_collapsed_background_color(PidginBlistTheme *theme, const GdkColor *color);
/**
- * Sets the text color and font to be used for expanded groups.
+ * pidgin_blist_theme_set_collapsed_text_info:
+ * @theme: The PidginBlist theme.
+ * @pair: The new text font and color pair.
*
- * @param theme The PidginBlist theme.
- * @param pair The new text font and color pair.
+ * Sets the text color and font to be used for expanded groups.
*/
void pidgin_blist_theme_set_collapsed_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair);
/**
- * Sets the background color to be used for contacts and chats.
+ * pidgin_blist_theme_set_contact_color:
+ * @theme: The PidginBlist theme.
+ * @color: The color to use for contacts and chats.
*
- * @param theme The PidginBlist theme.
- * @param color The color to use for contacts and chats.
+ * Sets the background color to be used for contacts and chats.
*/
void pidgin_blist_theme_set_contact_color(PidginBlistTheme *theme, const GdkColor *color);
/**
- * Sets the text color and font to be used for expanded contacts.
+ * pidgin_blist_theme_set_contact_text_info:
+ * @theme: The PidginBlist theme.
+ * @pair: The new text font and color pair.
*
- * @param theme The PidginBlist theme.
- * @param pair The new text font and color pair.
+ * Sets the text color and font to be used for expanded contacts.
*/
void pidgin_blist_theme_set_contact_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair);
/**
- * Sets the text color and font to be used for online buddies.
+ * pidgin_blist_theme_set_online_text_info:
+ * @theme: The PidginBlist theme.
+ * @pair: The new text font and color pair.
*
- * @param theme The PidginBlist theme.
- * @param pair The new text font and color pair.
+ * Sets the text color and font to be used for online buddies.
*/
void pidgin_blist_theme_set_online_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair);
/**
- * Sets the text color and font to be used for away and idle buddies.
+ * pidgin_blist_theme_set_away_text_info:
+ * @theme: The PidginBlist theme.
+ * @pair: The new text font and color pair.
*
- * @param theme The PidginBlist theme.
- * @param pair The new text font and color pair.
+ * Sets the text color and font to be used for away and idle buddies.
*/
void pidgin_blist_theme_set_away_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair);
/**
- * Sets the text color and font to be used for offline buddies.
+ * pidgin_blist_theme_set_offline_text_info:
+ * @theme: The PidginBlist theme.
+ * @pair: The new text font and color pair.
*
- * @param theme The PidginBlist theme.
- * @param pair The new text font and color pair.
+ * Sets the text color and font to be used for offline buddies.
*/
void pidgin_blist_theme_set_offline_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair);
/**
- * Sets the text color and font to be used for idle buddies.
+ * pidgin_blist_theme_set_idle_text_info:
+ * @theme: The PidginBlist theme.
+ * @pair: The new text font and color pair.
*
- * @param theme The PidginBlist theme.
- * @param pair The new text font and color pair.
+ * Sets the text color and font to be used for idle buddies.
*/
void pidgin_blist_theme_set_idle_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair);
/**
- * Sets the text color and font to be used for buddies with unread messages.
+ * pidgin_blist_theme_set_unread_message_text_info:
+ * @theme: The PidginBlist theme.
+ * @pair: The new text font and color pair.
*
- * @param theme The PidginBlist theme.
- * @param pair The new text font and color pair.
+ * Sets the text color and font to be used for buddies with unread messages.
*/
void pidgin_blist_theme_set_unread_message_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair);
/**
+ * pidgin_blist_theme_set_unread_message_nick_said_text_info:
+ * @theme: The PidginBlist theme.
+ * @pair: The new text font and color pair.
+ *
* Sets the text color and font to be used for a chat with unread messages
* that mention your nick.
- *
- * @param theme The PidginBlist theme.
- * @param pair The new text font and color pair.
*/
void pidgin_blist_theme_set_unread_message_nick_said_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair);
/**
- * Sets the text color and font to be used for buddy status messages.
+ * pidgin_blist_theme_set_status_text_info:
+ * @theme: The PidginBlist theme.
+ * @pair: The new text font and color pair.
*
- * @param theme The PidginBlist theme.
- * @param pair The new text font and color pair.
+ * Sets the text color and font to be used for buddy status messages.
*/
void pidgin_blist_theme_set_status_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair);
G_END_DECLS
+
#endif /* PIDGIN_BLIST_THEME_H */
diff --git a/pidgin/gtkblist.c b/pidgin/gtkblist.c
index 57a842778a..84521885a8 100644
--- a/pidgin/gtkblist.c
+++ b/pidgin/gtkblist.c
@@ -1,8 +1,3 @@
-/*
- * @file gtkblist.c GTK+ BuddyList API
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -49,7 +44,7 @@
#include "gtkconv.h"
#include "gtkdebug.h"
#include "gtkdialogs.h"
-#include "gtkft.h"
+#include "gtkxfer.h"
#include "gtklog.h"
#include "gtkmenutray.h"
#include "gtkpounce.h"
@@ -59,7 +54,7 @@
#include "gtkroomlist.h"
#include "gtkstatusbox.h"
#include "gtkscrollbook.h"
-#include "gtksmiley.h"
+#include "gtksmiley-manager.h"
#include "gtkblist-theme.h"
#include "gtkblist-theme-loader.h"
#include "gtkutils.h"
@@ -70,6 +65,8 @@
#include <gtk/gtk.h>
#include <gdk/gdk.h>
+#include "gtk3compat.h"
+
typedef struct
{
PurpleAccount *account;
@@ -108,22 +105,42 @@ typedef struct
typedef struct
{
- /** Used to hold error minidialogs. Gets packed
- * inside PidginBuddyList.error_buttons
+ /* GBoxed reference count */
+ int box_count;
+
+ /* Used to hold error minidialogs. Gets packed
+ * inside PidginBuddyList.error_buttons
*/
PidginScrollBook *error_scrollbook;
- /** Pointer to the mini-dialog about having signed on elsewhere, if one
- * is showing; @c NULL otherwise.
+ /* Pointer to the mini-dialog about having signed on elsewhere, if one
+ * is showing; %NULL otherwise.
*/
PidginMiniDialog *signed_on_elsewhere;
PidginBlistTheme *current_theme;
+
+ guint select_notebook_page_timeout;
+
+#if !GTK_CHECK_VERSION(3,0,0)
+ GdkCursor *hand_cursor; /* Hand cursor */
+ GdkCursor *arrow_cursor; /* Arrow cursor */
+ gboolean changing_style; /* True when changing GTK+ theme style */
+#endif
+
} PidginBuddyListPrivate;
#define PIDGIN_BUDDY_LIST_GET_PRIVATE(list) \
((PidginBuddyListPrivate *)((list)->priv))
+#define PIDGIN_WINDOW_ICONIFIED(x) \
+ (gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(x))) & \
+ GDK_WINDOW_STATE_ICONIFIED)
+
+#define PIDGIN_WINDOW_MAXIMIZED(x) \
+ (gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(x))) & \
+ GDK_WINDOW_STATE_MAXIMIZED)
+
static GtkWidget *accountmenu = NULL;
static guint visibility_manager_count = 0;
@@ -132,12 +149,15 @@ static gboolean gtk_blist_focused = FALSE;
static gboolean editing_blist = FALSE;
static GList *pidgin_blist_sort_methods = NULL;
-static struct pidgin_blist_sort_method *current_sort_method = NULL;
+static struct _PidginBlistSortMethod *current_sort_method = NULL;
static void sort_method_none(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter);
static void sort_method_alphabetical(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter);
static void sort_method_status(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter);
static void sort_method_log_activity(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter);
+static guint sort_merge_id;
+static GtkActionGroup *sort_action_group = NULL;
+
static PidginBuddyList *gtkblist = NULL;
static GList *groups_tree(void);
@@ -148,7 +168,6 @@ static void pidgin_blist_update(PurpleBuddyList *list, PurpleBlistNode *node);
static void pidgin_blist_update_group(PurpleBuddyList *list, PurpleBlistNode *node);
static void pidgin_blist_update_contact(PurpleBuddyList *list, PurpleBlistNode *node);
static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full);
-static const char *item_factory_translate_func (const char *path, gpointer func_data);
static gboolean get_iter_from_node(PurpleBlistNode *node, GtkTreeIter *iter);
static gboolean buddy_is_displayable(PurpleBuddy *buddy);
static void redo_buddy_list(PurpleBuddyList *list, gboolean remove, gboolean rerender);
@@ -186,7 +205,7 @@ static gboolean gtk_blist_visibility_cb(GtkWidget *w, GdkEventVisibility *event,
old_state != GDK_VISIBILITY_FULLY_OBSCURED) {
/* no longer fully obscured */
- pidgin_blist_refresh_timer(purple_get_blist());
+ pidgin_blist_refresh_timer(purple_blist_get_buddy_list());
}
/* continue to handle event normally */
@@ -200,7 +219,7 @@ static gboolean gtk_blist_window_state_cb(GtkWidget *w, GdkEventWindowState *eve
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_get_blist());
+ pidgin_blist_refresh_timer(purple_blist_get_buddy_list());
}
}
@@ -214,7 +233,7 @@ static gboolean gtk_blist_window_state_cb(GtkWidget *w, GdkEventWindowState *eve
/* 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_get_blist());
+ pidgin_blist_refresh_timer(purple_blist_get_buddy_list());
}
return FALSE;
@@ -242,7 +261,7 @@ static gboolean gtk_blist_configure_cb(GtkWidget *w, GdkEventConfigure *event, g
/* check for visibility because when we aren't visible, this will *
* give us bogus (0,0) coordinates. - xOr */
- if (GTK_WIDGET_VISIBLE(w))
+ if (gtk_widget_get_visible(w))
gtk_window_get_position(GTK_WINDOW(w), &x, &y);
else
return FALSE; /* carry on normally */
@@ -250,10 +269,8 @@ static gboolean gtk_blist_configure_cb(GtkWidget *w, GdkEventConfigure *event, g
#ifdef _WIN32
/* Workaround for GTK+ bug # 169811 - "configure_event" is fired
* when the window is being maximized */
- if (gdk_window_get_state(w->window)
- & GDK_WINDOW_STATE_MAXIMIZED) {
+ if (PIDGIN_WINDOW_MAXIMIZED(w))
return FALSE;
- }
#endif
/* don't save if nothing changed */
@@ -330,7 +347,7 @@ static void gtk_blist_menu_send_file_cb(GtkWidget *w, PurpleBuddy *b)
{
PurpleAccount *account = purple_buddy_get_account(b);
- serv_send_file(purple_account_get_connection(account),
+ purple_serv_send_file(purple_account_get_connection(account),
purple_buddy_get_name(b), NULL);
}
@@ -343,13 +360,13 @@ static void gtk_blist_menu_move_to_cb(GtkWidget *w, PurpleBlistNode *node)
static void gtk_blist_menu_autojoin_cb(GtkWidget *w, PurpleChat *chat)
{
- purple_blist_node_set_bool((PurpleBlistNode*)chat, "gtk-autojoin",
+ purple_blist_node_set_bool(PURPLE_BLIST_NODE(chat), "gtk-autojoin",
gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)));
}
static void gtk_blist_menu_persistent_cb(GtkWidget *w, PurpleChat *chat)
{
- purple_blist_node_set_bool((PurpleBlistNode*)chat, "gtk-persistent",
+ purple_blist_node_set_bool(PURPLE_BLIST_NODE(chat), "gtk-persistent",
gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)));
}
@@ -359,9 +376,8 @@ find_conversation_with_buddy(PurpleBuddy *buddy)
PidginBlistNode *ui = purple_blist_node_get_ui_data(PURPLE_BLIST_NODE(buddy));
if (ui)
return ui->conv.conv;
- return purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
- purple_buddy_get_name(buddy),
- purple_buddy_get_account(buddy));
+ return PURPLE_CONVERSATION(purple_conversations_find_im_with_account(
+ purple_buddy_get_name(buddy), purple_buddy_get_account(buddy)));
}
static void gtk_blist_join_chat(PurpleChat *chat)
@@ -388,15 +404,15 @@ static void gtk_blist_join_chat(PurpleChat *chat)
else
name = purple_chat_get_name(chat);
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, name,
- account);
+ conv = PURPLE_CONVERSATION(purple_conversations_find_chat_with_account(name,
+ account));
if (conv != NULL) {
pidgin_conv_attach_to_conversation(conv);
purple_conversation_present(conv);
}
- serv_join_chat(purple_account_get_connection(account), components);
+ purple_serv_join_chat(purple_account_get_connection(account), components);
g_free(chat_name);
}
@@ -427,22 +443,16 @@ static void gtk_blist_renderer_editing_started_cb(GtkCellRenderer *renderer,
gtk_tree_path_free (path);
gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
- switch (purple_blist_node_get_type(node)) {
- case PURPLE_BLIST_CONTACT_NODE:
+ if (PURPLE_IS_CONTACT(node))
text = purple_contact_get_alias(PURPLE_CONTACT(node));
- break;
- case PURPLE_BLIST_BUDDY_NODE:
+ else if (PURPLE_IS_BUDDY(node))
text = purple_buddy_get_alias(PURPLE_BUDDY(node));
- break;
- case PURPLE_BLIST_GROUP_NODE:
+ else if (PURPLE_IS_GROUP(node))
text = purple_group_get_name(PURPLE_GROUP(node));
- break;
- case PURPLE_BLIST_CHAT_NODE:
+ else if (PURPLE_IS_CHAT(node))
text = purple_chat_get_name(PURPLE_CHAT(node));
- break;
- default:
+ else
g_return_if_reached();
- }
if (GTK_IS_ENTRY (editable)) {
GtkEntry *entry = GTK_ENTRY (editable);
@@ -459,23 +469,18 @@ gtk_blist_do_personize(GList *merges)
GList *tmp;
/* First, we find the contact to merge the rest of the buddies into.
- * This will be the contact with the most buddies in it; ties are broken
- * by which contact is higher in the list
- */
+ * This will be the contact with the most buddies in it; ties are broken
+ * by which contact is higher in the list
+ */
for (tmp = merges; tmp; tmp = tmp->next) {
PurpleBlistNode *node = tmp->data;
PurpleBlistNode *b;
- PurpleBlistNodeType type;
int i = 0;
- type = purple_blist_node_get_type(node);
-
- if (type == PURPLE_BLIST_BUDDY_NODE) {
+ if (PURPLE_IS_BUDDY(node))
node = purple_blist_node_get_parent(node);
- type = purple_blist_node_get_type(node);
- }
- if (type != PURPLE_BLIST_CONTACT_NODE)
+ if (!PURPLE_IS_CONTACT(node))
continue;
for (b = purple_blist_node_get_first_child(node);
@@ -497,13 +502,13 @@ gtk_blist_do_personize(GList *merges)
/* Merge all those buddies into this contact */
for (tmp = merges; tmp; tmp = tmp->next) {
PurpleBlistNode *node = tmp->data;
- if (purple_blist_node_get_type(node) == PURPLE_BLIST_BUDDY_NODE)
+ if (PURPLE_IS_BUDDY(node))
node = purple_blist_node_get_parent(node);
if (node == contact)
continue;
- purple_blist_merge_contact((PurpleContact *)node, contact);
+ purple_contact_merge((PurpleContact *)node, contact);
}
/* And show the expanded contact, so the people know what's going on */
@@ -524,7 +529,7 @@ gtk_blist_auto_personize(PurpleBlistNode *group, const char *alias)
contact != NULL;
contact = purple_blist_node_get_sibling_next(contact)) {
char *node_alias;
- if (purple_blist_node_get_type(contact) != PURPLE_BLIST_CONTACT_NODE)
+ if (!PURPLE_IS_CONTACT(contact))
continue;
node_alias = g_utf8_casefold(purple_contact_get_alias((PurpleContact *)contact), -1);
@@ -540,7 +545,7 @@ gtk_blist_auto_personize(PurpleBlistNode *group, const char *alias)
buddy;
buddy = purple_blist_node_get_sibling_next(buddy))
{
- if (purple_blist_node_get_type(buddy) != PURPLE_BLIST_BUDDY_NODE)
+ if (!PURPLE_IS_BUDDY(buddy))
continue;
node_alias = g_utf8_casefold(purple_buddy_get_alias(PURPLE_BUDDY(buddy)), -1);
@@ -559,7 +564,7 @@ gtk_blist_auto_personize(PurpleBlistNode *group, const char *alias)
{
char *msg = g_strdup_printf(ngettext("You have %d contact named %s. Would you like to merge them?", "You currently have %d contacts named %s. Would you like to merge them?", i), i, alias);
purple_request_action(NULL, NULL, msg, _("Merging these contacts will cause them to share a single entry on the buddy list and use a single conversation window. "
- "You can separate them again by choosing 'Expand' from the contact's context menu"), 0, NULL, NULL, NULL,
+ "You can separate them again by choosing 'Expand' from the contact's context menu"), 0, NULL,
merges, 2, _("_Yes"), PURPLE_CALLBACK(gtk_blist_do_personize), _("_No"), PURPLE_CALLBACK(g_list_free));
g_free(msg);
} else
@@ -573,6 +578,7 @@ static void gtk_blist_renderer_edited_cb(GtkCellRendererText *text_rend, char *a
GtkTreePath *path;
PurpleBlistNode *node;
PurpleGroup *dest;
+ gchar *alias = NULL;
editing_blist = FALSE;
path = gtk_tree_path_new_from_string (arg1);
@@ -582,56 +588,46 @@ static void gtk_blist_renderer_edited_cb(GtkCellRendererText *text_rend, char *a
gtk_tree_view_set_enable_search (GTK_TREE_VIEW(gtkblist->treeview), TRUE);
g_object_set(G_OBJECT(gtkblist->text_rend), "editable", FALSE, NULL);
- switch (purple_blist_node_get_type(node))
- {
- case PURPLE_BLIST_CONTACT_NODE:
- {
- PurpleContact *contact = PURPLE_CONTACT(node);
- struct _pidgin_blist_node *gtknode =
- (struct _pidgin_blist_node *)purple_blist_node_get_ui_data(node);
-
- /*
- * XXX Using purple_contact_get_alias here breaks because we
- * specifically want to check the contact alias only (i.e. not
- * the priority buddy, which purple_contact_get_alias does).
- * Adding yet another get_alias is evil, so figure this out
- * later :-P
- */
- if (contact->alias || gtknode->contact_expanded) {
- purple_blist_alias_contact(contact, arg2);
- gtk_blist_auto_personize(purple_blist_node_get_parent(node), arg2);
- } else {
- PurpleBuddy *buddy = purple_contact_get_priority_buddy(contact);
- purple_blist_alias_buddy(buddy, arg2);
- serv_alias_buddy(buddy);
- gtk_blist_auto_personize(purple_blist_node_get_parent(node), arg2);
- }
- }
- break;
+ if (PURPLE_IS_CONTACT(node)) {
+ PurpleContact *contact = PURPLE_CONTACT(node);
+ struct _pidgin_blist_node *gtknode =
+ (struct _pidgin_blist_node *)purple_blist_node_get_ui_data(node);
- case PURPLE_BLIST_BUDDY_NODE:
- {
- PurpleGroup *group = purple_buddy_get_group(PURPLE_BUDDY(node));
+ /*
+ * Using purple_contact_get_alias here breaks because we
+ * specifically want to check the contact alias only (i.e. not
+ * the priority buddy, which purple_contact_get_alias does).
+ * The "alias" GObject property gives us just the alias.
+ */
+ g_object_get(contact, "alias", &alias, NULL);
- purple_blist_alias_buddy(PURPLE_BUDDY(node), arg2);
- serv_alias_buddy(PURPLE_BUDDY(node));
- gtk_blist_auto_personize(PURPLE_BLIST_NODE(group), arg2);
- }
- break;
- case PURPLE_BLIST_GROUP_NODE:
- dest = purple_find_group(arg2);
- if (dest != NULL && purple_utf8_strcasecmp(arg2, purple_group_get_name(PURPLE_GROUP(node)))) {
- pidgin_dialogs_merge_groups(PURPLE_GROUP(node), arg2);
- } else {
- purple_blist_rename_group(PURPLE_GROUP(node), arg2);
- }
- break;
- case PURPLE_BLIST_CHAT_NODE:
- purple_blist_alias_chat(PURPLE_CHAT(node), arg2);
- break;
- default:
- break;
+ if (alias || gtknode->contact_expanded) {
+ purple_contact_set_alias(contact, arg2);
+ gtk_blist_auto_personize(purple_blist_node_get_parent(node), arg2);
+ } else {
+ PurpleBuddy *buddy = purple_contact_get_priority_buddy(contact);
+ purple_buddy_set_local_alias(buddy, arg2);
+ purple_serv_alias_buddy(buddy);
+ gtk_blist_auto_personize(purple_blist_node_get_parent(node), arg2);
+ }
+ } else if (PURPLE_IS_BUDDY(node)) {
+ PurpleGroup *group = purple_buddy_get_group(PURPLE_BUDDY(node));
+
+ purple_buddy_set_local_alias(PURPLE_BUDDY(node), arg2);
+ purple_serv_alias_buddy(PURPLE_BUDDY(node));
+ gtk_blist_auto_personize(PURPLE_BLIST_NODE(group), arg2);
+ } else if (PURPLE_IS_GROUP(node)) {
+ dest = purple_blist_find_group(arg2);
+ if (dest != NULL && purple_utf8_strcasecmp(arg2, purple_group_get_name(PURPLE_GROUP(node)))) {
+ pidgin_dialogs_merge_groups(PURPLE_GROUP(node), arg2);
+ } else {
+ purple_group_set_name(PURPLE_GROUP(node), arg2);
+ }
+ } else if (PURPLE_IS_CHAT(node)) {
+ purple_chat_set_alias(PURPLE_CHAT(node), arg2);
}
+
+ g_free(alias);
pidgin_blist_refresh(list);
}
@@ -648,7 +644,7 @@ chat_components_edit_ok(PurpleChat *chat, PurpleRequestFields *allfields)
char *val;
id = purple_request_field_get_id(field);
- if (purple_request_field_get_type(field) == PURPLE_REQUEST_FIELD_INTEGER)
+ if (purple_request_field_get_field_type(field) == PURPLE_REQUEST_FIELD_INTEGER)
val = g_strdup_printf("%d", purple_request_field_int_get_value(field));
else
val = g_strdup(purple_request_field_string_get_value(field));
@@ -684,7 +680,7 @@ static void chat_components_edit(GtkWidget *w, PurpleBlistNode *node)
const char *str = g_hash_table_lookup(purple_chat_get_components(chat), pce->identifier);
if (!str || sscanf(str, "%d", &val) != 1)
val = pce->min;
- field = purple_request_field_int_new(pce->identifier, pce->label, val);
+ field = purple_request_field_int_new(pce->identifier, pce->label, val, INT_MIN, INT_MAX);
} else {
field = purple_request_field_string_new(pce->identifier, pce->label,
g_hash_table_lookup(purple_chat_get_components(chat), pce->identifier), FALSE);
@@ -703,8 +699,7 @@ static void chat_components_edit(GtkWidget *w, PurpleBlistNode *node)
purple_request_fields(NULL, _("Edit Chat"), NULL, _("Please update the necessary fields."),
fields, _("Save"), G_CALLBACK(chat_components_edit_ok), _("Cancel"), NULL,
- NULL, NULL, NULL,
- chat);
+ NULL, chat);
}
static void gtk_blist_menu_alias_cb(GtkWidget *w, PurpleBlistNode *node)
@@ -745,12 +740,12 @@ static void gtk_blist_menu_showlog_cb(GtkWidget *w, PurpleBlistNode *node)
pidgin_set_cursor(gtkblist->window, GDK_WATCH);
- if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ if (PURPLE_IS_BUDDY(node)) {
PurpleBuddy *b = (PurpleBuddy*) node;
type = PURPLE_LOG_IM;
name = g_strdup(purple_buddy_get_name(b));
account = purple_buddy_get_account(b);
- } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if (PURPLE_IS_CHAT(node)) {
PurpleChat *c = PURPLE_CHAT(node);
PurplePluginProtocolInfo *prpl_info = NULL;
type = PURPLE_LOG_CHAT;
@@ -759,7 +754,7 @@ static void gtk_blist_menu_showlog_cb(GtkWidget *w, PurpleBlistNode *node)
if (prpl_info && prpl_info->get_chat_name) {
name = prpl_info->get_chat_name(purple_chat_get_components(c));
}
- } else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ } else if (PURPLE_IS_CONTACT(node)) {
pidgin_log_show_contact(PURPLE_CONTACT(node));
pidgin_clear_cursor(gtkblist->window);
return;
@@ -781,13 +776,13 @@ static void gtk_blist_menu_showlog_cb(GtkWidget *w, PurpleBlistNode *node)
static void gtk_blist_menu_showoffline_cb(GtkWidget *w, PurpleBlistNode *node)
{
- if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if (PURPLE_IS_BUDDY(node))
{
purple_blist_node_set_bool(node, "show_offline",
!purple_blist_node_get_bool(node, "show_offline"));
- pidgin_blist_update(purple_get_blist(), node);
+ pidgin_blist_update(purple_blist_get_buddy_list(), node);
}
- else if (PURPLE_BLIST_NODE_IS_CONTACT(node))
+ else if (PURPLE_IS_CONTACT(node))
{
PurpleBlistNode *bnode;
gboolean setting = !purple_blist_node_get_bool(node, "show_offline");
@@ -798,9 +793,9 @@ static void gtk_blist_menu_showoffline_cb(GtkWidget *w, PurpleBlistNode *node)
bnode = purple_blist_node_get_sibling_next(bnode))
{
purple_blist_node_set_bool(bnode, "show_offline", setting);
- pidgin_blist_update(purple_get_blist(), bnode);
+ pidgin_blist_update(purple_blist_get_buddy_list(), bnode);
}
- } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ } else if (PURPLE_IS_GROUP(node)) {
PurpleBlistNode *cnode, *bnode;
gboolean setting = !purple_blist_node_get_bool(node, "show_offline");
@@ -815,7 +810,7 @@ static void gtk_blist_menu_showoffline_cb(GtkWidget *w, PurpleBlistNode *node)
bnode = purple_blist_node_get_sibling_next(bnode))
{
purple_blist_node_set_bool(bnode, "show_offline", setting);
- pidgin_blist_update(purple_get_blist(), bnode);
+ pidgin_blist_update(purple_blist_get_buddy_list(), bnode);
}
}
}
@@ -890,7 +885,7 @@ do_joinchat(GtkWidget *dialog, int id, PidginChatData *info)
* strings are empty then don't allow the user to click on "OK."
*/
static void
-set_sensitive_if_input_cb(GtkWidget *entry, gpointer user_data)
+set_sensitive_if_input_chat_cb(GtkWidget *entry, gpointer user_data)
{
PurplePluginProtocolInfo *prpl_info;
PurpleConnection *gc;
@@ -916,19 +911,34 @@ set_sensitive_if_input_cb(GtkWidget *entry, gpointer user_data)
gtk_dialog_set_response_sensitive(GTK_DIALOG(data->rq_data.window), GTK_RESPONSE_OK, sensitive);
gc = purple_account_get_connection(data->rq_data.account);
- prpl_info = (gc != NULL) ? PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl) : NULL;
+ prpl_info = (gc != NULL) ? PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)) : NULL;
sensitive = (prpl_info != NULL && prpl_info->roomlist_get_list != NULL);
gtk_dialog_set_response_sensitive(GTK_DIALOG(data->rq_data.window), 1, sensitive);
}
static void
+set_sensitive_if_input_buddy_cb(GtkWidget *entry, gpointer user_data)
+{
+ PurplePlugin *prpl;
+ PidginAddBuddyData *data = user_data;
+ const char *text;
+
+ prpl = purple_find_prpl(purple_account_get_protocol_id(
+ data->rq_data.account));
+ text = gtk_entry_get_text(GTK_ENTRY(entry));
+
+ gtk_dialog_set_response_sensitive(GTK_DIALOG(data->rq_data.window),
+ GTK_RESPONSE_OK, purple_validate(prpl, text));
+}
+
+static void
pidgin_blist_update_privacy_cb(PurpleBuddy *buddy)
{
struct _pidgin_blist_node *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_get_blist(), PURPLE_BLIST_NODE(buddy), TRUE);
+ pidgin_blist_update_buddy(purple_blist_get_buddy_list(), PURPLE_BLIST_NODE(buddy), TRUE);
}
static gboolean
@@ -940,7 +950,7 @@ chat_account_filter_func(PurpleAccount *account)
if (gc == NULL)
return FALSE;
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
return (prpl_info->chat_info != NULL);
}
@@ -979,27 +989,28 @@ make_blist_request_dialog(PidginBlistRequestData *data, PurpleAccount *account,
img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION,
gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
- gtkblist = PIDGIN_BLIST(purple_get_blist());
+ gtkblist = PIDGIN_BLIST(purple_blist_get_buddy_list());
blist_window = gtkblist ? GTK_WINDOW(gtkblist->window) : NULL;
- data->window = gtk_dialog_new_with_buttons(title,
- blist_window, GTK_DIALOG_NO_SEPARATOR,
- NULL);
-
+ data->window = gtk_dialog_new();
+ gtk_window_set_title(GTK_WINDOW(data->window), title);
gtk_window_set_transient_for(GTK_WINDOW(data->window), blist_window);
gtk_dialog_set_default_response(GTK_DIALOG(data->window), GTK_RESPONSE_OK);
gtk_container_set_border_width(GTK_CONTAINER(data->window), PIDGIN_HIG_BOX_SPACE);
gtk_window_set_resizable(GTK_WINDOW(data->window), FALSE);
- gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(data->window)->vbox), PIDGIN_HIG_BORDER);
- gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), PIDGIN_HIG_BOX_SPACE);
+ gtk_box_set_spacing(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(data->window))),
+ PIDGIN_HIG_BORDER);
+ gtk_container_set_border_width(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(data->window))),
+ PIDGIN_HIG_BOX_SPACE);
gtk_window_set_role(GTK_WINDOW(data->window), window_role);
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
- gtk_container_add(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), hbox);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BORDER);
+ gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(data->window))),
+ hbox);
gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
- vbox = gtk_vbox_new(FALSE, 5);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_container_add(GTK_CONTAINER(hbox), vbox);
label = gtk_label_new(label_text);
@@ -1015,7 +1026,7 @@ make_blist_request_dialog(PidginBlistRequestData *data, PurpleAccount *account,
callback_func, filter_func, data);
pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("A_ccount"), data->sg, data->account_menu, TRUE, NULL);
- data->vbox = GTK_BOX(gtk_vbox_new(FALSE, 5));
+ data->vbox = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 5));
gtk_container_set_border_width(GTK_CONTAINER(data->vbox), 0);
gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(data->vbox), FALSE, FALSE, 0);
@@ -1030,6 +1041,7 @@ static void
rebuild_chat_entries(PidginChatData *data, const char *default_chat_name)
{
PurpleConnection *gc;
+ PurplePluginProtocolInfo *prpl_info;
GList *list = NULL, *tmp;
GHashTable *defaults = NULL;
struct proto_chat_entry *pce;
@@ -1038,17 +1050,18 @@ rebuild_chat_entries(PidginChatData *data, const char *default_chat_name)
g_return_if_fail(data->rq_data.account != NULL);
gc = purple_account_get_connection(data->rq_data.account);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
gtk_container_foreach(GTK_CONTAINER(data->rq_data.vbox), (GtkCallback)gtk_widget_destroy, NULL);
g_list_free(data->entries);
data->entries = NULL;
- if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info != NULL)
- list = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info(gc);
+ if (prpl_info->chat_info != NULL)
+ list = prpl_info->chat_info(gc);
- if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults != NULL)
- defaults = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults(gc, default_chat_name);
+ if (prpl_info->chat_info_defaults != NULL)
+ defaults = prpl_info->chat_info_defaults(gc, default_chat_name);
for (tmp = list; tmp; tmp = tmp->next)
{
@@ -1058,10 +1071,11 @@ rebuild_chat_entries(PidginChatData *data, const char *default_chat_name)
if (pce->is_int)
{
- GtkObject *adjust;
- adjust = gtk_adjustment_new(pce->min, pce->min, pce->max,
- 1, 10, 10);
- input = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0);
+ GtkAdjustment *adjust;
+ adjust = GTK_ADJUSTMENT(gtk_adjustment_new(pce->min,
+ pce->min, pce->max,
+ 1, 10, 10));
+ input = gtk_spin_button_new(adjust, 1, 0);
gtk_widget_set_size_request(input, 50, -1);
pidgin_add_widget_to_vbox(GTK_BOX(data->rq_data.vbox), pce->label, data->rq_data.sg, input, FALSE, NULL);
}
@@ -1076,14 +1090,10 @@ rebuild_chat_entries(PidginChatData *data, const char *default_chat_name)
if (pce->secret)
{
gtk_entry_set_visibility(GTK_ENTRY(input), FALSE);
-#if !GTK_CHECK_VERSION(2,16,0)
- if (gtk_entry_get_invisible_char(GTK_ENTRY(input)) == '*')
- gtk_entry_set_invisible_char(GTK_ENTRY(input), PIDGIN_INVISIBLE_CHAR);
-#endif /* Less than GTK+ 2.16 */
}
pidgin_add_widget_to_vbox(data->rq_data.vbox, pce->label, data->rq_data.sg, input, TRUE, NULL);
g_signal_connect(G_OBJECT(input), "changed",
- G_CALLBACK(set_sensitive_if_input_cb), data);
+ G_CALLBACK(set_sensitive_if_input_chat_cb), data);
}
/* Do the following for any type of input widget */
@@ -1104,7 +1114,7 @@ rebuild_chat_entries(PidginChatData *data, const char *default_chat_name)
g_hash_table_destroy(defaults);
/* Set whether the "OK" button should be clickable initially */
- set_sensitive_if_input_cb(NULL, data);
+ set_sensitive_if_input_chat_cb(NULL, data);
gtk_widget_show_all(GTK_WIDGET(data->rq_data.vbox));
}
@@ -1113,6 +1123,10 @@ static void
chat_select_account_cb(GObject *w, PurpleAccount *account,
PidginChatData *data)
{
+ g_return_if_fail(w != NULL);
+ g_return_if_fail(data != NULL);
+ g_return_if_fail(account != NULL);
+
if (strcmp(purple_account_get_protocol_id(data->rq_data.account),
purple_account_get_protocol_id(account)) == 0)
{
@@ -1158,7 +1172,7 @@ static void gtk_blist_row_expanded_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTre
gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &node, -1);
- if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ if (PURPLE_IS_GROUP(node)) {
char *title;
title = pidgin_get_group_title(node, TRUE);
@@ -1180,7 +1194,7 @@ static void gtk_blist_row_collapsed_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTr
gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &node, -1);
- if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ if (PURPLE_IS_GROUP(node)) {
char *title;
struct _pidgin_blist_node *gtknode;
PurpleBlistNode *cnode;
@@ -1196,7 +1210,7 @@ static void gtk_blist_row_collapsed_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTr
purple_blist_node_set_bool(node, "collapsed", TRUE);
for(cnode = purple_blist_node_get_first_child(node); cnode; cnode = purple_blist_node_get_sibling_next(cnode)) {
- if (PURPLE_BLIST_NODE_IS_CONTACT(cnode)) {
+ if (PURPLE_IS_CONTACT(cnode)) {
gtknode = purple_blist_node_get_ui_data(cnode);
if (!gtknode->contact_expanded)
continue;
@@ -1205,7 +1219,7 @@ static void gtk_blist_row_collapsed_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTr
}
}
pidgin_blist_tooltip_destroy();
- } else if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ } else if(PURPLE_IS_CONTACT(node)) {
pidgin_blist_collapse_contact_cb(NULL, node);
}
}
@@ -1217,18 +1231,18 @@ static void gtk_blist_row_activated_cb(GtkTreeView *tv, GtkTreePath *path, GtkTr
gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
- if(PURPLE_BLIST_NODE_IS_CONTACT(node) || PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ if(PURPLE_IS_CONTACT(node) || PURPLE_IS_BUDDY(node)) {
PurpleBuddy *buddy;
- if(PURPLE_BLIST_NODE_IS_CONTACT(node))
+ if(PURPLE_IS_CONTACT(node))
buddy = purple_contact_get_priority_buddy((PurpleContact*)node);
else
buddy = (PurpleBuddy*)node;
pidgin_dialogs_im_with_user(purple_buddy_get_account(buddy), purple_buddy_get_name(buddy));
- } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if (PURPLE_IS_CHAT(node)) {
gtk_blist_join_chat((PurpleChat *)node);
- } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ } else if (PURPLE_IS_GROUP(node)) {
/* if (gtk_tree_view_row_expanded(tv, path))
gtk_tree_view_collapse_row(tv, path);
else
@@ -1244,11 +1258,11 @@ static void pidgin_blist_add_chat_cb(void)
if(gtk_tree_selection_get_selected(sel, NULL, &iter)){
gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
- if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if (PURPLE_IS_BUDDY(node))
purple_blist_request_add_chat(NULL, purple_buddy_get_group(PURPLE_BUDDY(node)), NULL, NULL);
- if (PURPLE_BLIST_NODE_IS_CONTACT(node) || PURPLE_BLIST_NODE_IS_CHAT(node))
+ if (PURPLE_IS_CONTACT(node) || PURPLE_IS_CHAT(node))
purple_blist_request_add_chat(NULL, purple_contact_get_group(PURPLE_CONTACT(node)), NULL, NULL);
- else if (PURPLE_BLIST_NODE_IS_GROUP(node))
+ else if (PURPLE_IS_GROUP(node))
purple_blist_request_add_chat(NULL, (PurpleGroup*)node, NULL, NULL);
}
else {
@@ -1264,13 +1278,13 @@ static void pidgin_blist_add_buddy_cb(void)
if(gtk_tree_selection_get_selected(sel, NULL, &iter)){
gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
- if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ if (PURPLE_IS_BUDDY(node)) {
PurpleGroup *group = purple_buddy_get_group(PURPLE_BUDDY(node));
purple_blist_request_add_buddy(NULL, NULL, purple_group_get_name(group), NULL);
- } else if (PURPLE_BLIST_NODE_IS_CONTACT(node) || PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if (PURPLE_IS_CONTACT(node) || PURPLE_IS_CHAT(node)) {
PurpleGroup *group = purple_contact_get_group(PURPLE_CONTACT(node));
purple_blist_request_add_buddy(NULL, NULL, purple_group_get_name(group), NULL);
- } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ } else if (PURPLE_IS_GROUP(node)) {
purple_blist_request_add_buddy(NULL, NULL, purple_group_get_name(PURPLE_GROUP(node)), NULL);
}
}
@@ -1282,13 +1296,13 @@ static void pidgin_blist_add_buddy_cb(void)
static void
pidgin_blist_remove_cb (GtkWidget *w, PurpleBlistNode *node)
{
- if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ if (PURPLE_IS_BUDDY(node)) {
pidgin_dialogs_remove_buddy((PurpleBuddy*)node);
- } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if (PURPLE_IS_CHAT(node)) {
pidgin_dialogs_remove_chat((PurpleChat*)node);
- } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ } else if (PURPLE_IS_GROUP(node)) {
pidgin_dialogs_remove_group((PurpleGroup*)node);
- } else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ } else if (PURPLE_IS_CONTACT(node)) {
pidgin_dialogs_remove_contact((PurpleContact*)node);
}
}
@@ -1320,7 +1334,7 @@ pidgin_blist_expand_contact_cb(GtkWidget *w, PurpleBlistNode *node)
PurpleBlistNode *bnode;
GtkTreePath *path;
- if(!PURPLE_BLIST_NODE_IS_CONTACT(node))
+ if(!PURPLE_IS_CONTACT(node))
return;
gtknode = purple_blist_node_get_ui_data(node);
@@ -1353,7 +1367,7 @@ pidgin_blist_collapse_contact_cb(GtkWidget *w, PurpleBlistNode *node)
PurpleBlistNode *bnode;
struct _pidgin_blist_node *gtknode;
- if(!PURPLE_BLIST_NODE_IS_CONTACT(node))
+ if(!PURPLE_IS_CONTACT(node))
return;
gtknode = purple_blist_node_get_ui_data(node);
@@ -1373,23 +1387,23 @@ toggle_privacy(GtkWidget *widget, PurpleBlistNode *node)
gboolean permitted;
const char *name;
- if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if (!PURPLE_IS_BUDDY(node))
return;
buddy = (PurpleBuddy *)node;
account = purple_buddy_get_account(buddy);
name = purple_buddy_get_name(buddy);
- permitted = purple_privacy_check(account, name);
+ permitted = purple_account_privacy_check(account, name);
/* XXX: Perhaps ask whether to restore the previous lists where appropirate? */
if (permitted)
- purple_privacy_deny(account, name, FALSE, FALSE);
+ purple_account_privacy_deny(account, name);
else
- purple_privacy_allow(account, name, FALSE, FALSE);
+ purple_account_privacy_allow(account, name);
- pidgin_blist_update(purple_get_blist(), node);
+ pidgin_blist_update(purple_blist_get_buddy_list(), node);
}
void pidgin_append_blist_node_privacy_menu(GtkWidget *menu, PurpleBlistNode *node)
@@ -1399,7 +1413,7 @@ void pidgin_append_blist_node_privacy_menu(GtkWidget *menu, PurpleBlistNode *nod
gboolean permitted;
account = purple_buddy_get_account(buddy);
- permitted = purple_privacy_check(account, purple_buddy_get_name(buddy));
+ permitted = purple_account_privacy_check(account, purple_buddy_get_name(buddy));
pidgin_new_item_from_stock(menu, permitted ? _("_Block") : _("Un_block"),
permitted ? PIDGIN_STOCK_TOOLBAR_BLOCK : PIDGIN_STOCK_TOOLBAR_UNBLOCK, G_CALLBACK(toggle_privacy),
@@ -1411,7 +1425,7 @@ pidgin_append_blist_node_proto_menu(GtkWidget *menu, PurpleConnection *gc,
PurpleBlistNode *node)
{
GList *l, *ll;
- PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
if(!prpl_info || !prpl_info->blist_node_menu)
return;
@@ -1452,7 +1466,7 @@ pidgin_append_blist_node_move_to_menu(GtkWidget *menu, PurpleBlistNode *node)
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)) {
- if (!PURPLE_BLIST_NODE_IS_GROUP(group))
+ if (!PURPLE_IS_GROUP(group))
continue;
if (group == purple_blist_node_get_parent(node))
continue;
@@ -1519,7 +1533,7 @@ pidgin_blist_make_buddy_menu(GtkWidget *menu, PurpleBuddy *buddy, gboolean sub)
if (prpl_info && prpl_info->send_file) {
if (!prpl_info->can_receive_file ||
- prpl_info->can_receive_file(buddy->account->gc, buddy->name))
+ prpl_info->can_receive_file(purple_account_get_connection(purple_buddy_get_account(buddy)), purple_buddy_get_name(buddy)))
{
pidgin_new_item_from_stock(menu, _("_Send File..."),
PIDGIN_STOCK_TOOLBAR_SEND_FILE,
@@ -1541,20 +1555,20 @@ pidgin_blist_make_buddy_menu(GtkWidget *menu, PurpleBuddy *buddy, gboolean sub)
G_CALLBACK(gtk_blist_menu_showlog_cb), buddy, 0, 0, NULL);
}
- if (!PURPLE_BLIST_NODE_HAS_FLAG(node, PURPLE_BLIST_NODE_FLAG_NO_SAVE)) {
+ if (!purple_blist_node_is_transient(node)) {
gboolean show_offline = purple_blist_node_get_bool(node, "show_offline");
pidgin_new_item_from_stock(menu, show_offline ? _("Hide When Offline") : _("Show When Offline"),
NULL, G_CALLBACK(gtk_blist_menu_showoffline_cb), node, 0, 0, NULL);
}
- pidgin_append_blist_node_proto_menu(menu, buddy->account->gc, node);
+ pidgin_append_blist_node_proto_menu(menu, purple_account_get_connection(purple_buddy_get_account(buddy)), node);
pidgin_append_blist_node_extended_menu(menu, node);
if (!contact_expanded && contact != NULL)
- pidgin_append_blist_node_move_to_menu(menu, (PurpleBlistNode *)contact);
+ pidgin_append_blist_node_move_to_menu(menu, PURPLE_BLIST_NODE(contact));
if (node->parent && node->parent->child->next &&
- !sub && !contact_expanded) {
+ !sub && !contact_expanded) {
pidgin_separator(menu);
pidgin_append_blist_node_privacy_menu(menu, node);
pidgin_new_item_from_stock(menu, _("_Alias..."), PIDGIN_STOCK_ALIAS,
@@ -1592,22 +1606,22 @@ gtk_blist_key_press_cb(GtkWidget *tv, GdkEventKey *event, gpointer data)
(event->keyval == 'o' || event->keyval == 'O')) {
PurpleBuddy *buddy;
- if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ if(PURPLE_IS_CONTACT(node)) {
buddy = purple_contact_get_priority_buddy((PurpleContact*)node);
- } else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ } else if(PURPLE_IS_BUDDY(node)) {
buddy = (PurpleBuddy*)node;
} else {
return FALSE;
}
if(buddy)
- pidgin_retrieve_user_info(buddy->account->gc, buddy->name);
+ pidgin_retrieve_user_info(purple_account_get_connection(purple_buddy_get_account(buddy)), purple_buddy_get_name(buddy));
} else {
switch (event->keyval) {
- case GDK_F2:
+ case GDK_KEY_F2:
gtk_blist_menu_alias_cb(tv, node);
break;
- case GDK_Left:
+ case GDK_KEY_Left:
path = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter);
if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(tv), path)) {
/* Collapse the Group */
@@ -1629,15 +1643,15 @@ gtk_blist_key_press_cb(GtkWidget *tv, GdkEventKey *event, gpointer data)
gtk_tree_path_free(path);
break;
- case GDK_Right:
+ case GDK_KEY_Right:
path = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter);
if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(tv), path)) {
/* Expand the Group */
- if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ if (PURPLE_IS_CONTACT(node)) {
pidgin_blist_expand_contact_cb(NULL, node);
gtk_tree_path_free(path);
return TRUE;
- } else if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ } else if (!PURPLE_IS_BUDDY(node)) {
gtk_tree_view_expand_row(GTK_TREE_VIEW(tv), path, FALSE);
gtk_tree_path_free(path);
return TRUE;
@@ -1721,7 +1735,7 @@ create_group_menu (PurpleBlistNode *node, PurpleGroup *g)
G_CALLBACK(pidgin_blist_remove_cb), node, 0, 0, NULL);
pidgin_new_item_from_stock(menu, _("_Rename"), NULL,
G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL);
- if (!(purple_blist_node_get_flags(node) & PURPLE_BLIST_NODE_FLAG_NO_SAVE)) {
+ if (!purple_blist_node_is_transient(node)) {
gboolean show_offline = purple_blist_node_get_bool(node, "show_offline");
pidgin_new_item_from_stock(menu, show_offline ? _("Hide When Offline") : _("Show When Offline"),
NULL, G_CALLBACK(gtk_blist_menu_showoffline_cb), node, 0, 0, NULL);
@@ -1753,7 +1767,7 @@ create_chat_menu(PurpleBlistNode *node, PurpleChat *c)
pidgin_new_item_from_stock(menu, _("View _Log"), NULL,
G_CALLBACK(gtk_blist_menu_showlog_cb), node, 0, 0, NULL);
- pidgin_append_blist_node_proto_menu(menu, c->account->gc, node);
+ pidgin_append_blist_node_proto_menu(menu, purple_account_get_connection(purple_chat_get_account(c)), node);
pidgin_append_blist_node_extended_menu(menu, node);
pidgin_separator(menu);
@@ -1803,7 +1817,7 @@ create_contact_menu (PurpleBlistNode *node)
static GtkWidget *
create_buddy_menu(PurpleBlistNode *node, PurpleBuddy *b)
{
- struct _pidgin_blist_node *gtknode = (struct _pidgin_blist_node *)node->ui_data;
+ struct _pidgin_blist_node *gtknode = purple_blist_node_get_ui_data(node);
GtkWidget *menu;
GtkWidget *menuitem;
gboolean show_offline = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_offline_buddies");
@@ -1811,7 +1825,7 @@ create_buddy_menu(PurpleBlistNode *node, PurpleBuddy *b)
menu = gtk_menu_new();
pidgin_blist_make_buddy_menu(menu, b, FALSE);
- if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ if(PURPLE_IS_CONTACT(node)) {
pidgin_separator(menu);
add_buddy_icon_menu_items(menu, node);
@@ -1838,13 +1852,13 @@ create_buddy_menu(PurpleBlistNode *node, PurpleBuddy *b)
if(buddy == b)
continue;
- if(!buddy->account->gc)
+ if(!purple_account_get_connection(purple_buddy_get_account(buddy)))
continue;
if(!show_offline && !PURPLE_BUDDY_IS_ONLINE(buddy))
continue;
- menuitem = gtk_image_menu_item_new_with_label(buddy->name);
- buf = pidgin_create_prpl_icon(buddy->account,PIDGIN_PRPL_ICON_SMALL);
+ menuitem = gtk_image_menu_item_new_with_label(purple_buddy_get_name(buddy));
+ buf = pidgin_create_prpl_icon(purple_buddy_get_account(buddy), PIDGIN_PRPL_ICON_SMALL);
image = gtk_image_new_from_pixbuf(buf);
g_object_unref(G_OBJECT(buf));
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem),
@@ -1871,27 +1885,25 @@ pidgin_blist_show_context_menu(PurpleBlistNode *node,
guint button,
guint32 time)
{
- struct _pidgin_blist_node *gtknode;
+ struct _pidgin_blist_node *gtknode = purple_blist_node_get_ui_data(node);
GtkWidget *menu = NULL;
gboolean handled = FALSE;
- gtknode = (struct _pidgin_blist_node *)node->ui_data;
-
/* Create a menu based on the thing we right-clicked on */
- if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ if (PURPLE_IS_GROUP(node)) {
PurpleGroup *g = (PurpleGroup *)node;
menu = create_group_menu(node, g);
- } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if (PURPLE_IS_CHAT(node)) {
PurpleChat *c = (PurpleChat *)node;
menu = create_chat_menu(node, c);
- } else if ((PURPLE_BLIST_NODE_IS_CONTACT(node)) && (gtknode->contact_expanded)) {
+ } else if ((PURPLE_IS_CONTACT(node)) && (gtknode->contact_expanded)) {
menu = create_contact_menu(node);
- } else if (PURPLE_BLIST_NODE_IS_CONTACT(node) || PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ } else if (PURPLE_IS_CONTACT(node) || PURPLE_IS_BUDDY(node)) {
PurpleBuddy *b;
- if (PURPLE_BLIST_NODE_IS_CONTACT(node))
+ if (PURPLE_IS_CONTACT(node))
b = purple_contact_get_priority_buddy((PurpleContact*)node);
else
b = (PurpleBuddy *)node;
@@ -1938,7 +1950,7 @@ gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer user_da
return FALSE;
gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
- gtknode = (struct _pidgin_blist_node *)node->ui_data;
+ gtknode = purple_blist_node_get_ui_data(node);
/* Right click draws a context menu */
if ((event->button == 3) && (event->type == GDK_BUTTON_PRESS)) {
@@ -1946,7 +1958,7 @@ gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer user_da
/* CTRL+middle click expands or collapse a contact */
} else if ((event->button == 2) && (event->type == GDK_BUTTON_PRESS) &&
- (event->state & GDK_CONTROL_MASK) && (PURPLE_BLIST_NODE_IS_CONTACT(node))) {
+ (event->state & GDK_CONTROL_MASK) && (PURPLE_IS_CONTACT(node))) {
if (gtknode->contact_expanded)
pidgin_blist_collapse_contact_cb(NULL, node);
else
@@ -1955,19 +1967,19 @@ gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer user_da
/* Double middle click gets info */
} else if ((event->button == 2) && (event->type == GDK_2BUTTON_PRESS) &&
- ((PURPLE_BLIST_NODE_IS_CONTACT(node)) || (PURPLE_BLIST_NODE_IS_BUDDY(node)))) {
+ ((PURPLE_IS_CONTACT(node)) || (PURPLE_IS_BUDDY(node)))) {
PurpleBuddy *b;
- if(PURPLE_BLIST_NODE_IS_CONTACT(node))
+ if(PURPLE_IS_CONTACT(node))
b = purple_contact_get_priority_buddy((PurpleContact*)node);
else
b = (PurpleBuddy *)node;
- prpl = purple_find_prpl(purple_account_get_protocol_id(b->account));
+ prpl = purple_find_prpl(purple_account_get_protocol_id(purple_buddy_get_account(b)));
if (prpl != NULL)
prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
if (prpl && prpl_info->get_info)
- pidgin_retrieve_user_info(b->account->gc, b->name);
+ pidgin_retrieve_user_info(purple_account_get_connection(purple_buddy_get_account(b)), purple_buddy_get_name(b));
handled = TRUE;
}
@@ -2012,64 +2024,70 @@ pidgin_blist_popup_menu_cb(GtkWidget *tv, void *user_data)
return handled;
}
-static void pidgin_blist_buddy_details_cb(gpointer data, guint action, GtkWidget *item)
+static void gtk_blist_show_xfer_dialog_cb(GtkAction *item, gpointer data)
+{
+ pidgin_xfer_dialog_show(NULL);
+}
+
+static void pidgin_blist_buddy_details_cb(GtkToggleAction *item, gpointer data)
{
pidgin_set_cursor(gtkblist->window, GDK_WATCH);
purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons",
- gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item)));
+ gtk_toggle_action_get_active(item));
pidgin_clear_cursor(gtkblist->window);
}
-static void pidgin_blist_show_idle_time_cb(gpointer data, guint action, GtkWidget *item)
+static void pidgin_blist_show_idle_time_cb(GtkToggleAction *item, gpointer data)
{
pidgin_set_cursor(gtkblist->window, GDK_WATCH);
purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time",
- gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item)));
+ gtk_toggle_action_get_active(item));
pidgin_clear_cursor(gtkblist->window);
}
-static void pidgin_blist_show_protocol_icons_cb(gpointer data, guint action, GtkWidget *item)
+static void pidgin_blist_show_protocol_icons_cb(GtkToggleAction *item, gpointer data)
{
purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons",
- gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item)));
+ gtk_toggle_action_get_active(item));
}
-static void pidgin_blist_show_empty_groups_cb(gpointer data, guint action, GtkWidget *item)
+static void pidgin_blist_show_empty_groups_cb(GtkToggleAction *item, gpointer data)
{
pidgin_set_cursor(gtkblist->window, GDK_WATCH);
purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/show_empty_groups",
- gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item)));
+ gtk_toggle_action_get_active(item));
pidgin_clear_cursor(gtkblist->window);
}
-static void pidgin_blist_edit_mode_cb(gpointer callback_data, guint callback_action,
- GtkWidget *checkitem)
+static void pidgin_blist_edit_mode_cb(GtkToggleAction *checkitem, gpointer data)
{
pidgin_set_cursor(gtkblist->window, GDK_WATCH);
purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/show_offline_buddies",
- gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(checkitem)));
+ gtk_toggle_action_get_active(checkitem));
pidgin_clear_cursor(gtkblist->window);
}
-static void pidgin_blist_mute_sounds_cb(gpointer data, guint action, GtkWidget *item)
+static void pidgin_blist_mute_sounds_cb(GtkToggleAction *item, gpointer data)
{
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/sound/mute", GTK_CHECK_MENU_ITEM(item)->active);
+ purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/sound/mute",
+ gtk_toggle_action_get_active(item));
}
+
static void
pidgin_blist_mute_pref_cb(const char *name, PurplePrefType type,
gconstpointer value, gpointer data)
{
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item(gtkblist->ift,
- N_("/Tools/Mute Sounds"))), (gboolean)GPOINTER_TO_INT(value));
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(gtk_ui_manager_get_action(gtkblist->ui,
+ "/BList/ToolsMenu/MuteSounds")), (gboolean)GPOINTER_TO_INT(value));
}
static void
@@ -2081,7 +2099,7 @@ pidgin_blist_sound_method_pref_cb(const char *name, PurplePrefType type,
if(!strcmp(value, "none"))
sensitive = FALSE;
- gtk_widget_set_sensitive(gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Mute Sounds")), sensitive);
+ gtk_action_set_sensitive(gtk_ui_manager_get_action(gtkblist->ui, "/BList/ToolsMenu/MuteSounds"), sensitive);
}
static void
@@ -2111,7 +2129,7 @@ add_buddies_from_vcard(const char *prpl_id, PurpleGroup *group, GList *list,
for (l = list; l != NULL; l = l->next)
{
purple_blist_request_add_buddy(account, l->data,
- (group ? group->name : NULL),
+ (group ? purple_group_get_name(group) : NULL),
alias);
}
}
@@ -2238,9 +2256,9 @@ static void pidgin_blist_drag_data_get_cb(GtkWidget *widget,
guint time,
gpointer null)
{
+ GdkAtom target = gtk_selection_data_get_target(data);
- if (data->target == gdk_atom_intern("PURPLE_BLIST_NODE", FALSE))
- {
+ if (target == gdk_atom_intern("PURPLE_BLIST_NODE", FALSE)) {
GtkTreeRowReference *ref = g_object_get_data(G_OBJECT(dc), "gtk-tree-view-source-row");
GtkTreePath *sourcerow = gtk_tree_row_reference_get_path(ref);
GtkTreeIter iter;
@@ -2256,9 +2274,7 @@ static void pidgin_blist_drag_data_get_cb(GtkWidget *widget,
sizeof (node));
gtk_tree_path_free(sourcerow);
- }
- else if (data->target == gdk_atom_intern("application/x-im-contact", FALSE))
- {
+ } else if (target == gdk_atom_intern("application/x-im-contact", FALSE)) {
GtkTreeRowReference *ref;
GtkTreePath *sourcerow;
GtkTreeIter iter;
@@ -2278,11 +2294,11 @@ static void pidgin_blist_drag_data_get_cb(GtkWidget *widget,
sourcerow);
gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
- if (PURPLE_BLIST_NODE_IS_CONTACT(node))
+ if (PURPLE_IS_CONTACT(node))
{
buddy = purple_contact_get_priority_buddy((PurpleContact *)node);
}
- else if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
+ else if (!PURPLE_IS_BUDDY(node))
{
gtk_tree_path_free(sourcerow);
return;
@@ -2292,7 +2308,7 @@ static void pidgin_blist_drag_data_get_cb(GtkWidget *widget,
buddy = (PurpleBuddy *)node;
}
- gc = purple_account_get_connection(buddy->account);
+ gc = purple_account_get_connection(purple_buddy_get_account(buddy));
if (gc == NULL)
{
@@ -2301,7 +2317,7 @@ static void pidgin_blist_drag_data_get_cb(GtkWidget *widget,
}
protocol =
- PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->list_icon(buddy->account,
+ PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc))->list_icon(purple_buddy_get_account(buddy),
buddy);
str = g_string_new(NULL);
@@ -2311,13 +2327,13 @@ static void pidgin_blist_drag_data_get_cb(GtkWidget *widget,
"X-IM-Protocol: %s\r\n"
"X-IM-Username: %s\r\n",
protocol,
- buddy->name);
+ purple_buddy_get_name(buddy));
- if (buddy->alias != NULL)
+ if (purple_buddy_get_local_alias(buddy) != NULL)
{
g_string_append_printf(str,
"X-IM-Alias: %s\r\n",
- buddy->alias);
+ purple_buddy_get_local_alias(buddy));
}
g_string_append(str, "\r\n");
@@ -2336,16 +2352,19 @@ static void pidgin_blist_drag_data_get_cb(GtkWidget *widget,
static void pidgin_blist_drag_data_rcv_cb(GtkWidget *widget, GdkDragContext *dc, guint x, guint y,
GtkSelectionData *sd, guint info, guint t)
{
+ GdkAtom target = gtk_selection_data_get_target(sd);
+ const guchar *data = gtk_selection_data_get_data(sd);
+
if (gtkblist->drag_timeout) {
g_source_remove(gtkblist->drag_timeout);
gtkblist->drag_timeout = 0;
}
- if (sd->target == gdk_atom_intern("PURPLE_BLIST_NODE", FALSE) && sd->data) {
+ if (target == gdk_atom_intern("PURPLE_BLIST_NODE", FALSE) && data) {
PurpleBlistNode *n = NULL;
GtkTreePath *path = NULL;
GtkTreeViewDropPosition position;
- memcpy(&n, sd->data, sizeof(n));
+ memcpy(&n, data, sizeof(n));
if(gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(widget), x, y, &path, &position)) {
/* if we're here, I think it means the drop is ok */
GtkTreeIter iter;
@@ -2356,14 +2375,14 @@ static void pidgin_blist_drag_data_rcv_cb(GtkWidget *widget, GdkDragContext *dc,
&iter, path);
gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel),
&iter, NODE_COLUMN, &node, -1);
- gtknode = node->ui_data;
+ gtknode = purple_blist_node_get_ui_data(node);
- if (PURPLE_BLIST_NODE_IS_CONTACT(n)) {
+ if (PURPLE_IS_CONTACT(n)) {
PurpleContact *c = (PurpleContact*)n;
- if (PURPLE_BLIST_NODE_IS_CONTACT(node) && gtknode->contact_expanded) {
- purple_blist_merge_contact(c, node);
- } else if (PURPLE_BLIST_NODE_IS_CONTACT(node) ||
- PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ if (PURPLE_IS_CONTACT(node) && gtknode->contact_expanded) {
+ purple_contact_merge(c, node);
+ } else if (PURPLE_IS_CONTACT(node) ||
+ PURPLE_IS_CHAT(node)) {
switch(position) {
case GTK_TREE_VIEW_DROP_AFTER:
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
@@ -2376,14 +2395,14 @@ static void pidgin_blist_drag_data_rcv_cb(GtkWidget *widget, GdkDragContext *dc,
node->prev);
break;
}
- } else if(PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ } else if(PURPLE_IS_GROUP(node)) {
purple_blist_add_contact(c, (PurpleGroup*)node, NULL);
- } else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
- purple_blist_merge_contact(c, node);
+ } else if(PURPLE_IS_BUDDY(node)) {
+ purple_contact_merge(c, node);
}
- } else if (PURPLE_BLIST_NODE_IS_BUDDY(n)) {
+ } else if (PURPLE_IS_BUDDY(n)) {
PurpleBuddy *b = (PurpleBuddy*)n;
- if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ if (PURPLE_IS_BUDDY(node)) {
switch(position) {
case GTK_TREE_VIEW_DROP_AFTER:
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
@@ -2397,12 +2416,12 @@ static void pidgin_blist_drag_data_rcv_cb(GtkWidget *widget, GdkDragContext *dc,
node->prev);
break;
}
- } else if(PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if(PURPLE_IS_CHAT(node)) {
purple_blist_add_buddy(b, NULL, (PurpleGroup*)node->parent,
NULL);
- } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ } else if (PURPLE_IS_GROUP(node)) {
purple_blist_add_buddy(b, NULL, (PurpleGroup*)node, NULL);
- } else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ } else if (PURPLE_IS_CONTACT(node)) {
if(gtknode->contact_expanded) {
switch(position) {
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
@@ -2431,9 +2450,9 @@ static void pidgin_blist_drag_data_rcv_cb(GtkWidget *widget, GdkDragContext *dc,
}
}
}
- } else if (PURPLE_BLIST_NODE_IS_CHAT(n)) {
+ } else if (PURPLE_IS_CHAT(n)) {
PurpleChat *chat = (PurpleChat *)n;
- if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ if (PURPLE_IS_BUDDY(node)) {
switch(position) {
case GTK_TREE_VIEW_DROP_AFTER:
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
@@ -2444,8 +2463,8 @@ static void pidgin_blist_drag_data_rcv_cb(GtkWidget *widget, GdkDragContext *dc,
node->parent);
break;
}
- } else if(PURPLE_BLIST_NODE_IS_CONTACT(node) ||
- PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if(PURPLE_IS_CONTACT(node) ||
+ PURPLE_IS_CHAT(node)) {
switch(position) {
case GTK_TREE_VIEW_DROP_AFTER:
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
@@ -2456,12 +2475,12 @@ static void pidgin_blist_drag_data_rcv_cb(GtkWidget *widget, GdkDragContext *dc,
purple_blist_add_chat(chat, (PurpleGroup*)node->parent, node->prev);
break;
}
- } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ } else if (PURPLE_IS_GROUP(node)) {
purple_blist_add_chat(chat, (PurpleGroup*)node, NULL);
}
- } else if (PURPLE_BLIST_NODE_IS_GROUP(n)) {
+ } else if (PURPLE_IS_GROUP(n)) {
PurpleGroup *g = (PurpleGroup*)n;
- if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ if (PURPLE_IS_GROUP(node)) {
switch (position) {
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
case GTK_TREE_VIEW_DROP_AFTER:
@@ -2472,21 +2491,19 @@ static void pidgin_blist_drag_data_rcv_cb(GtkWidget *widget, GdkDragContext *dc,
purple_blist_add_group(g, node->prev);
break;
}
- } else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ } else if(PURPLE_IS_BUDDY(node)) {
purple_blist_add_group(g, node->parent->parent);
- } else if(PURPLE_BLIST_NODE_IS_CONTACT(node) ||
- PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if(PURPLE_IS_CONTACT(node) ||
+ PURPLE_IS_CHAT(node)) {
purple_blist_add_group(g, node->parent);
}
}
gtk_tree_path_free(path);
- gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t);
+ gtk_drag_finish(dc, TRUE, (gdk_drag_context_get_actions(dc) == GDK_ACTION_MOVE), t);
}
- }
- else if (sd->target == gdk_atom_intern("application/x-im-contact",
- FALSE) && sd->data)
- {
+ } else if (target == gdk_atom_intern("application/x-im-contact",
+ FALSE) && data) {
PurpleGroup *group = NULL;
GtkTreePath *path = NULL;
GtkTreeViewDropPosition position;
@@ -2506,34 +2523,34 @@ static void pidgin_blist_drag_data_rcv_cb(GtkWidget *widget, GdkDragContext *dc,
gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel),
&iter, NODE_COLUMN, &node, -1);
- if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if (PURPLE_IS_BUDDY(node))
{
group = (PurpleGroup *)node->parent->parent;
}
- else if (PURPLE_BLIST_NODE_IS_CHAT(node) ||
- PURPLE_BLIST_NODE_IS_CONTACT(node))
+ else if (PURPLE_IS_CHAT(node) ||
+ PURPLE_IS_CONTACT(node))
{
group = (PurpleGroup *)node->parent;
}
- else if (PURPLE_BLIST_NODE_IS_GROUP(node))
+ else if (PURPLE_IS_GROUP(node))
{
group = (PurpleGroup *)node;
}
}
- if (pidgin_parse_x_im_contact((const char *)sd->data, FALSE, &account,
+ if (pidgin_parse_x_im_contact((const char *)data, FALSE, &account,
&protocol, &username, &alias))
{
if (account == NULL)
{
purple_notify_error(NULL, NULL,
_("You are not currently signed on with an account that "
- "can add that buddy."), NULL);
+ "can add that buddy."), NULL, NULL);
}
else
{
purple_blist_request_add_buddy(account, username,
- (group ? group->name : NULL),
+ (group ? purple_group_get_name(group) : NULL),
alias);
}
}
@@ -2545,9 +2562,10 @@ static void pidgin_blist_drag_data_rcv_cb(GtkWidget *widget, GdkDragContext *dc,
if (path != NULL)
gtk_tree_path_free(path);
- gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t);
+ gtk_drag_finish(dc, TRUE,
+ gdk_drag_context_get_actions(dc) == GDK_ACTION_MOVE, t);
}
- else if (sd->target == gdk_atom_intern("text/x-vcard", FALSE) && sd->data)
+ else if (target == gdk_atom_intern("text/x-vcard", FALSE) && data)
{
gboolean result;
PurpleGroup *group = NULL;
@@ -2565,25 +2583,26 @@ static void pidgin_blist_drag_data_rcv_cb(GtkWidget *widget, GdkDragContext *dc,
gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel),
&iter, NODE_COLUMN, &node, -1);
- if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if (PURPLE_IS_BUDDY(node))
{
group = (PurpleGroup *)node->parent->parent;
}
- else if (PURPLE_BLIST_NODE_IS_CHAT(node) ||
- PURPLE_BLIST_NODE_IS_CONTACT(node))
+ else if (PURPLE_IS_CHAT(node) ||
+ PURPLE_IS_CONTACT(node))
{
group = (PurpleGroup *)node->parent;
}
- else if (PURPLE_BLIST_NODE_IS_GROUP(node))
+ else if (PURPLE_IS_GROUP(node))
{
group = (PurpleGroup *)node;
}
}
- result = parse_vcard((const gchar *)sd->data, group);
+ result = parse_vcard((const gchar *)data, group);
- gtk_drag_finish(dc, result, (dc->action == GDK_ACTION_MOVE), t);
- } else if (sd->target == gdk_atom_intern("text/uri-list", FALSE) && sd->data) {
+ gtk_drag_finish(dc, result,
+ gdk_drag_context_get_actions(dc) == GDK_ACTION_MOVE, t);
+ } else if (target == gdk_atom_intern("text/uri-list", FALSE) && data) {
GtkTreePath *path = NULL;
GtkTreeViewDropPosition position;
@@ -2598,10 +2617,11 @@ static void pidgin_blist_drag_data_rcv_cb(GtkWidget *widget, GdkDragContext *dc,
gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel),
&iter, NODE_COLUMN, &node, -1);
- if (PURPLE_BLIST_NODE_IS_BUDDY(node) || PURPLE_BLIST_NODE_IS_CONTACT(node)) {
- PurpleBuddy *b = PURPLE_BLIST_NODE_IS_BUDDY(node) ? PURPLE_BUDDY(node) : purple_contact_get_priority_buddy(PURPLE_CONTACT(node));
- pidgin_dnd_file_manage(sd, b->account, b->name);
- gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t);
+ if (PURPLE_IS_BUDDY(node) || PURPLE_IS_CONTACT(node)) {
+ PurpleBuddy *b = PURPLE_IS_BUDDY(node) ? PURPLE_BUDDY(node) : purple_contact_get_priority_buddy(PURPLE_CONTACT(node));
+ pidgin_dnd_file_manage(sd, purple_buddy_get_account(b), purple_buddy_get_name(b));
+ gtk_drag_finish(dc, TRUE,
+ gdk_drag_context_get_actions(dc) == GDK_ACTION_MOVE, t);
} else {
gtk_drag_finish(dc, FALSE, FALSE, t);
}
@@ -2650,19 +2670,19 @@ static GdkPixbuf *pidgin_blist_get_buddy_icon(PurpleBlistNode *node,
PurpleBuddyIcon *icon = NULL;
PurpleAccount *account = NULL;
PurpleContact *contact = NULL;
- PurpleStoredImage *custom_img;
+ PurpleImage *custom_img;
PurplePluginProtocolInfo *prpl_info = NULL;
gint orig_width, orig_height, scale_width, scale_height;
- if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ if (PURPLE_IS_CONTACT(node)) {
buddy = purple_contact_get_priority_buddy((PurpleContact*)node);
contact = (PurpleContact*)node;
- } else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ } else if (PURPLE_IS_BUDDY(node)) {
buddy = (PurpleBuddy*)node;
contact = purple_buddy_get_contact(buddy);
- } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ } else if (PURPLE_IS_GROUP(node)) {
group = (PurpleGroup*)node;
- } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if (PURPLE_IS_CHAT(node)) {
/* We don't need to do anything here. We just need to not fall
* into the else block and return. */
} else {
@@ -2673,8 +2693,8 @@ static GdkPixbuf *pidgin_blist_get_buddy_icon(PurpleBlistNode *node,
account = purple_buddy_get_account(buddy);
}
- if(account && account->gc) {
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(account->gc->prpl);
+ if(account && purple_account_get_connection(account)) {
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(purple_account_get_connection(account)));
}
#if 0
@@ -2687,20 +2707,20 @@ static GdkPixbuf *pidgin_blist_get_buddy_icon(PurpleBlistNode *node,
* a contact then this is a group or some other type of node and we
* want to use that directly. */
if (contact) {
- custom_img = purple_buddy_icons_node_find_custom_icon((PurpleBlistNode*)contact);
+ custom_img = purple_buddy_icons_node_find_custom_icon(PURPLE_BLIST_NODE(contact));
} else {
custom_img = purple_buddy_icons_node_find_custom_icon(node);
}
if (custom_img) {
- data = purple_imgstore_get_data(custom_img);
- len = purple_imgstore_get_size(custom_img);
+ data = purple_image_get_data(custom_img);
+ len = purple_image_get_size(custom_img);
}
if (data == NULL) {
if (buddy) {
/* Not sure I like this...*/
- if (!(icon = purple_buddy_icons_find(buddy->account, buddy->name)))
+ if (!(icon = purple_buddy_icons_find(purple_buddy_get_account(buddy), purple_buddy_get_name(buddy))))
return NULL;
data = purple_buddy_icon_get_data(icon, &len);
}
@@ -2712,17 +2732,18 @@ static GdkPixbuf *pidgin_blist_get_buddy_icon(PurpleBlistNode *node,
buf = pidgin_pixbuf_from_data(data, len);
purple_buddy_icon_unref(icon);
if (!buf) {
- purple_debug_warning("gtkblist", "Couldn't load buddy icon "
- "on account %s (%s) buddyname=%s "
- "custom_img_data=%p\n",
- account ? purple_account_get_username(account) : "(no account)",
- account ? purple_account_get_protocol_id(account) : "(no account)",
- buddy ? purple_buddy_get_name(buddy) : "(no buddy)",
- custom_img ? purple_imgstore_get_data(custom_img) : NULL);
- purple_imgstore_unref(custom_img);
+ purple_debug_warning("gtkblist", "Couldn't load buddy icon on "
+ "account %s (%s); buddyname=%s; custom_img_size=%" G_GSIZE_FORMAT,
+ account ? purple_account_get_username(account) : "(no account)",
+ account ? purple_account_get_protocol_id(account) : "(no account)",
+ buddy ? purple_buddy_get_name(buddy) : "(no buddy)",
+ custom_img ? purple_image_get_size(custom_img) : 0);
+ if (custom_img)
+ g_object_unref(custom_img);
return NULL;
}
- purple_imgstore_unref(custom_img);
+ if (custom_img)
+ g_object_unref(custom_img);
if (greyed) {
gboolean offline = FALSE, idle = FALSE;
@@ -2734,7 +2755,7 @@ static GdkPixbuf *pidgin_blist_get_buddy_icon(PurpleBlistNode *node,
if (purple_presence_is_idle(presence))
idle = TRUE;
} else if (group) {
- if (purple_blist_get_group_online_count(group) == 0)
+ if (purple_counting_node_get_online_count(PURPLE_COUNTING_NODE(group)) == 0)
offline = TRUE;
}
@@ -2869,10 +2890,10 @@ static struct tooltip_data * create_tip_for_node(PurpleBlistNode *node, gboolean
PurpleAccount *account = NULL;
char *tmp = NULL, *node_name = NULL, *tooltip_text = NULL;
- if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
- account = ((PurpleBuddy*)(node))->account;
- } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
- account = ((PurpleChat*)(node))->account;
+ if (PURPLE_IS_BUDDY(node)) {
+ account = purple_buddy_get_account((PurpleBuddy*)(node));
+ } else if (PURPLE_IS_CHAT(node)) {
+ account = purple_chat_get_account((PurpleChat*)(node));
}
td->padding = TOOLTIP_BORDER;
@@ -2886,11 +2907,11 @@ static struct tooltip_data * create_tip_for_node(PurpleBlistNode *node, gboolean
td->layout = create_pango_layout(tooltip_text, &td->width, &td->height);
}
- if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ if (PURPLE_IS_BUDDY(node)) {
tmp = g_markup_escape_text(purple_buddy_get_name((PurpleBuddy*)node), -1);
- } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if (PURPLE_IS_CHAT(node)) {
tmp = g_markup_escape_text(purple_chat_get_name((PurpleChat*)node), -1);
- } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ } else if (PURPLE_IS_GROUP(node)) {
tmp = g_markup_escape_text(purple_group_get_name((PurpleGroup*)node), -1);
} else {
/* I don't believe this can happen currently, I think
@@ -2923,9 +2944,13 @@ static struct tooltip_data * create_tip_for_node(PurpleBlistNode *node, gboolean
}
static gboolean
-pidgin_blist_paint_tip(GtkWidget *widget, gpointer null)
+pidgin_blist_paint_tip(GtkWidget *widget, cairo_t *cr, gpointer null)
{
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context;
+#else
GtkStyle *style;
+#endif
int current_height, max_width;
int max_text_width;
int max_avatar_width;
@@ -2937,7 +2962,12 @@ pidgin_blist_paint_tip(GtkWidget *widget, gpointer null)
if(gtkblist->tooltipdata == NULL)
return FALSE;
- style = gtkblist->tipwindow->style;
+#if GTK_CHECK_VERSION(3,0,0)
+ context = gtk_widget_get_style_context(gtkblist->tipwindow);
+ gtk_style_context_add_class(context, GTK_STYLE_CLASS_TOOLTIP);
+#else
+ style = gtk_widget_get_style(gtkblist->tipwindow);
+#endif
max_text_width = 0;
max_avatar_width = 0;
@@ -2966,72 +2996,128 @@ pidgin_blist_paint_tip(GtkWidget *widget, gpointer null)
if (td->avatar && pidgin_gdk_pixbuf_is_opaque(td->avatar))
{
- if (dir == GTK_TEXT_DIR_RTL)
- gtk_paint_flat_box(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
- NULL, gtkblist->tipwindow, "tooltip",
- TOOLTIP_BORDER -1, current_height -1, td->avatar_width +2, td->avatar_height + 2);
- else
- gtk_paint_flat_box(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
- NULL, gtkblist->tipwindow, "tooltip",
- max_width - (td->avatar_width+ TOOLTIP_BORDER)-1,
- current_height-1,td->avatar_width+2, td->avatar_height+2);
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_style_context_save(context);
+ gtk_style_context_add_class(context, GTK_STYLE_CLASS_FRAME);
+ if (dir == GTK_TEXT_DIR_RTL) {
+ gtk_render_frame(context, cr,
+ TOOLTIP_BORDER - 1, current_height - 1,
+ td->avatar_width + 2, td->avatar_height + 2);
+ } else {
+ gtk_render_frame(context, cr,
+ max_width - (td->avatar_width + TOOLTIP_BORDER) - 1,
+ current_height - 1,
+ td->avatar_width + 2, td->avatar_height + 2);
+ }
+ gtk_style_context_restore(context);
+#else
+ if (dir == GTK_TEXT_DIR_RTL) {
+ gtk_paint_flat_box(style, gtkblist->tipwindow->window,
+ GTK_STATE_NORMAL, GTK_SHADOW_OUT,
+ NULL, gtkblist->tipwindow, "tooltip",
+ TOOLTIP_BORDER - 1, current_height - 1,
+ td->avatar_width + 2, td->avatar_height + 2);
+ } else {
+ gtk_paint_flat_box(style, gtkblist->tipwindow->window,
+ GTK_STATE_NORMAL, GTK_SHADOW_OUT,
+ NULL, gtkblist->tipwindow, "tooltip",
+ max_width - (td->avatar_width + TOOLTIP_BORDER) - 1,
+ current_height - 1, td->avatar_width + 2,
+ td->avatar_height + 2);
+ }
+#endif
}
if (td->status_icon) {
- if (dir == GTK_TEXT_DIR_RTL)
- gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, td->status_icon,
- 0, 0, max_width - TOOLTIP_BORDER - status_size, current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
- else
- gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, td->status_icon,
- 0, 0, TOOLTIP_BORDER, current_height, -1 , -1, GDK_RGB_DITHER_NONE, 0, 0);
+ if (dir == GTK_TEXT_DIR_RTL) {
+ gdk_cairo_set_source_pixbuf(cr, td->status_icon,
+ max_width - TOOLTIP_BORDER - status_size,
+ current_height);
+ cairo_paint(cr);
+ } else {
+ gdk_cairo_set_source_pixbuf(cr, td->status_icon,
+ TOOLTIP_BORDER, current_height);
+ cairo_paint(cr);
+ }
}
- if(td->avatar) {
- if (dir == GTK_TEXT_DIR_RTL)
- gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL,
- td->avatar, 0, 0, TOOLTIP_BORDER, current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
- else
- gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL,
- td->avatar, 0, 0, max_width - (td->avatar_width + TOOLTIP_BORDER),
- current_height, -1 , -1, GDK_RGB_DITHER_NONE, 0, 0);
+ if (td->avatar) {
+ if (dir == GTK_TEXT_DIR_RTL) {
+ gdk_cairo_set_source_pixbuf(cr, td->avatar,
+ TOOLTIP_BORDER, current_height);
+ cairo_paint(cr);
+ } else {
+ gdk_cairo_set_source_pixbuf(cr, td->avatar,
+ max_width - (td->avatar_width + TOOLTIP_BORDER),
+ current_height);
+ cairo_paint(cr);
+ }
}
- if (!td->avatar_is_prpl_icon && td->prpl_icon)
- gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, td->prpl_icon,
- 0, 0,
- prpl_col,
- current_height + ((td->name_height / 2) - (PRPL_SIZE / 2)),
- -1 , -1, GDK_RGB_DITHER_NONE, 0, 0);
+ if (!td->avatar_is_prpl_icon && td->prpl_icon) {
+ gdk_cairo_set_source_pixbuf(cr, td->prpl_icon, prpl_col,
+ current_height +
+ (td->name_height - PRPL_SIZE) / 2);
+ cairo_paint(cr);
+ }
if (td->name_layout) {
+#if GTK_CHECK_VERSION(3,0,0)
+ if (dir == GTK_TEXT_DIR_RTL) {
+ gtk_render_layout(context, cr,
+ max_width - (TOOLTIP_BORDER + status_size + SMALL_SPACE) - PANGO_PIXELS(300000),
+ current_height, td->name_layout);
+ } else {
+ gtk_render_layout(context, cr,
+ TOOLTIP_BORDER + status_size + SMALL_SPACE,
+ current_height, td->name_layout);
+ }
+#else
if (dir == GTK_TEXT_DIR_RTL) {
gtk_paint_layout(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
- NULL, gtkblist->tipwindow, "tooltip",
- max_width -(TOOLTIP_BORDER + status_size + SMALL_SPACE) - PANGO_PIXELS(300000),
- current_height, td->name_layout);
+ NULL, gtkblist->tipwindow, "tooltip",
+ max_width -(TOOLTIP_BORDER + status_size + SMALL_SPACE) - PANGO_PIXELS(300000),
+ current_height, td->name_layout);
} else {
- gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
- NULL, gtkblist->tipwindow, "tooltip",
- TOOLTIP_BORDER + status_size + SMALL_SPACE, current_height, td->name_layout);
+ gtk_paint_layout(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
+ NULL, gtkblist->tipwindow, "tooltip",
+ TOOLTIP_BORDER + status_size + SMALL_SPACE, current_height, td->name_layout);
}
+#endif
}
if (td->layout) {
+#if GTK_CHECK_VERSION(3,0,0)
+ if (dir != GTK_TEXT_DIR_RTL) {
+ gtk_render_layout(context, cr,
+ TOOLTIP_BORDER + status_size + SMALL_SPACE,
+ current_height + td->name_height,
+ td->layout);
+ } else {
+ gtk_render_layout(context, cr,
+ max_width - (TOOLTIP_BORDER + status_size + SMALL_SPACE) - PANGO_PIXELS(300000),
+ current_height + td->name_height,
+ td->layout);
+ }
+#else
if (dir != GTK_TEXT_DIR_RTL) {
- gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
- NULL, gtkblist->tipwindow, "tooltip",
- TOOLTIP_BORDER + status_size + SMALL_SPACE, current_height + td->name_height, td->layout);
+ gtk_paint_layout(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
+ NULL, gtkblist->tipwindow, "tooltip",
+ TOOLTIP_BORDER + status_size + SMALL_SPACE,
+ current_height + td->name_height, td->layout);
} else {
gtk_paint_layout(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
- NULL, gtkblist->tipwindow, "tooltip",
- max_width - (TOOLTIP_BORDER + status_size + SMALL_SPACE) - PANGO_PIXELS(300000),
- current_height + td->name_height,
- td->layout);
+ NULL, gtkblist->tipwindow, "tooltip",
+ max_width - (TOOLTIP_BORDER + status_size + SMALL_SPACE) - PANGO_PIXELS(300000),
+ current_height + td->name_height,
+ td->layout);
}
+#endif
}
current_height += MAX(td->name_height + td->height, td->avatar_height) + td->padding;
}
+
return FALSE;
}
@@ -3094,12 +3180,12 @@ pidgin_blist_create_tooltip_for_node(GtkWidget *widget, gpointer data, int *w, i
}
gtkblist->tipwindow = widget;
- if (PURPLE_BLIST_NODE_IS_CHAT(node) ||
- PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ if (PURPLE_IS_CHAT(node) ||
+ PURPLE_IS_BUDDY(node)) {
struct tooltip_data *td = create_tip_for_node(node, TRUE);
pidgin_blist_align_tooltip(td, gtkblist->tipwindow);
gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td);
- } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ } else if (PURPLE_IS_GROUP(node)) {
PurpleGroup *group = (PurpleGroup*)node;
GSList *accounts;
struct tooltip_data *td = create_tip_for_node(node, TRUE);
@@ -3114,13 +3200,13 @@ pidgin_blist_create_tooltip_for_node(GtkWidget *widget, gpointer data, int *w, i
td = create_tip_for_account(account);
gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td);
}
- } else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ } else if (PURPLE_IS_CONTACT(node)) {
PurpleBlistNode *child;
PurpleBuddy *b = purple_contact_get_priority_buddy((PurpleContact *)node);
for(child = node->child; child; child = child->next)
{
- if(PURPLE_BLIST_NODE_IS_BUDDY(child) && buddy_is_displayable((PurpleBuddy*)child)) {
+ if(PURPLE_IS_BUDDY(child) && buddy_is_displayable((PurpleBuddy*)child)) {
struct tooltip_data *td = create_tip_for_node(child, (b == (PurpleBuddy*)child));
pidgin_blist_align_tooltip(td, gtkblist->tipwindow);
if (b == (PurpleBuddy *)child) {
@@ -3167,12 +3253,12 @@ static gboolean pidgin_blist_expand_timeout(GtkWidget *tv)
gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
- if(!PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ if(!PURPLE_IS_CONTACT(node)) {
gtk_tree_path_free(path);
return FALSE;
}
- gtknode = node->ui_data;
+ gtknode = purple_blist_node_get_ui_data(node);
if (!gtknode->contact_expanded) {
GtkTreeIter i;
@@ -3180,7 +3266,8 @@ static gboolean pidgin_blist_expand_timeout(GtkWidget *tv)
pidgin_blist_expand_contact_cb(NULL, node);
gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &gtkblist->contact_rect);
- gdk_drawable_get_size(GDK_DRAWABLE(tv->window), &(gtkblist->contact_rect.width), NULL);
+ gtkblist->contact_rect.width =
+ gdk_window_get_width(gtk_widget_get_window(tv));
gtkblist->mouseover_contact = node;
gtk_tree_path_down (path);
while (gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &i, path)) {
@@ -3201,13 +3288,13 @@ static gboolean buddy_is_displayable(PurpleBuddy *buddy)
if(!buddy)
return FALSE;
- gtknode = ((PurpleBlistNode*)buddy)->ui_data;
+ gtknode = purple_blist_node_get_ui_data(PURPLE_BLIST_NODE(buddy));
- return (purple_account_is_connected(buddy->account) &&
- (purple_presence_is_online(buddy->presence) ||
+ return (purple_account_is_connected(purple_buddy_get_account(buddy)) &&
+ (purple_presence_is_online(purple_buddy_get_presence(buddy)) ||
(gtknode && gtknode->recent_signonoff) ||
purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_offline_buddies") ||
- purple_blist_node_get_bool((PurpleBlistNode*)buddy, "show_offline")));
+ purple_blist_node_get_bool(PURPLE_BLIST_NODE(buddy), "show_offline")));
}
void pidgin_blist_draw_tooltip(PurpleBlistNode *node, GtkWidget *widget)
@@ -3332,15 +3419,15 @@ static char *get_mood_icon_path(const char *mood)
char *path;
if (!strcmp(mood, "busy")) {
- path = g_build_filename(DATADIR, "pixmaps", "pidgin",
- "status", "16", "busy.png", NULL);
+ path = g_build_filename(PURPLE_DATADIR, "pixmaps", "pidgin",
+ "status", "16", "busy.png", NULL);
} else if (!strcmp(mood, "hiptop")) {
- path = g_build_filename(DATADIR, "pixmaps", "pidgin",
- "emblems", "16", "hiptop.png", NULL);
+ path = g_build_filename(PURPLE_DATADIR, "pixmaps", "pidgin",
+ "emblems", "16", "hiptop.png", NULL);
} else {
char *filename = g_strdup_printf("%s.png", mood);
- path = g_build_filename(DATADIR, "pixmaps", "pidgin",
- "emotes", "small", filename, NULL);
+ path = g_build_filename(PURPLE_DATADIR, "pixmaps", "pidgin",
+ "emotes", "small", filename, NULL);
g_free(filename);
}
return path;
@@ -3382,7 +3469,7 @@ edit_mood_cb(PurpleConnection *gc, PurpleRequestFields *fields)
const char *text;
PurpleAccount *account = purple_connection_get_account(gc);
- if (gc->flags & PURPLE_CONNECTION_SUPPORT_MOOD_MESSAGES) {
+ if (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_SUPPORT_MOOD_MESSAGES) {
PurpleRequestField *text_field;
text_field = purple_request_fields_get_field(fields, "text");
text = purple_request_field_string_get_value(text_field);
@@ -3398,7 +3485,7 @@ edit_mood_cb(PurpleConnection *gc, PurpleRequestFields *fields)
PurpleAccount *account = (PurpleAccount *) accounts->data;
PurpleConnection *gc = purple_account_get_connection(account);
- if (gc && gc->flags & PURPLE_CONNECTION_SUPPORT_MOODS) {
+ if (gc && (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_SUPPORT_MOODS)) {
update_status_with_mood(account, mood, NULL);
}
}
@@ -3433,12 +3520,12 @@ get_global_moods(void)
if (purple_account_is_connected(account)) {
PurpleConnection *gc = purple_account_get_connection(account);
- if (gc->flags & PURPLE_CONNECTION_SUPPORT_MOODS) {
+ if (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_SUPPORT_MOODS) {
PurplePluginProtocolInfo *prpl_info =
- PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
PurpleMood *mood = NULL;
- /* PURPLE_CONNECTION_SUPPORT_MOODS would not be set if the prpl doesn't
+ /* PURPLE_CONNECTION_FLAG_SUPPORT_MOODS would not be set if the prpl doesn't
* have get_moods, so using PURPLE_PROTOCOL_PLUGIN_HAS_FUNC isn't necessary
* here */
for (mood = prpl_info->get_moods(account) ;
@@ -3493,8 +3580,8 @@ get_global_mood_status(void)
PurpleAccount *account = (PurpleAccount *) accounts->data;
if (purple_account_is_connected(account) &&
- (purple_account_get_connection(account)->flags &
- PURPLE_CONNECTION_SUPPORT_MOODS)) {
+ (purple_connection_get_flags(purple_account_get_connection(account)) &
+ PURPLE_CONNECTION_FLAG_SUPPORT_MOODS)) {
PurplePresence *presence = purple_account_get_presence(account);
PurpleStatus *status = purple_presence_get_status(presence, "mood");
const gchar *curr_mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
@@ -3528,8 +3615,8 @@ set_mood_cb(GtkWidget *widget, PurpleAccount *account)
PurplePresence *presence = purple_account_get_presence(account);
PurpleStatus *status = purple_presence_get_status(presence, "mood");
gc = purple_account_get_connection(account);
- g_return_if_fail(gc->prpl != NULL);
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ g_return_if_fail(purple_connection_get_prpl(gc) != NULL);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
current_mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
} else {
current_mood = get_global_mood_status();
@@ -3539,7 +3626,7 @@ set_mood_cb(GtkWidget *widget, PurpleAccount *account)
g = purple_request_field_group_new(NULL);
f = purple_request_field_list_new("mood", _("Please select your mood from the list"));
- purple_request_field_list_add(f, _("None"), "");
+ purple_request_field_list_add_icon(f, _("None"), NULL, "");
if (current_mood == NULL)
purple_request_field_list_add_selected(f, _("None"));
@@ -3568,7 +3655,7 @@ set_mood_cb(GtkWidget *widget, PurpleAccount *account)
purple_request_fields_add_group(fields, g);
/* if the connection allows setting a mood message */
- if (gc && (gc->flags & PURPLE_CONNECTION_SUPPORT_MOOD_MESSAGES)) {
+ if (gc && (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_SUPPORT_MOOD_MESSAGES)) {
g = purple_request_field_group_new(NULL);
f = purple_request_field_string_new("text",
_("Message (optional)"), NULL, FALSE);
@@ -3577,11 +3664,10 @@ set_mood_cb(GtkWidget *widget, PurpleAccount *account)
}
purple_request_fields(gc, _("Edit User Mood"), _("Edit User Mood"),
- NULL, fields,
- _("OK"), G_CALLBACK(edit_mood_cb),
- _("Cancel"), NULL,
- gc ? purple_connection_get_account(gc) : NULL,
- NULL, NULL, gc);
+ NULL, fields,
+ _("OK"), G_CALLBACK(edit_mood_cb),
+ _("Cancel"), NULL,
+ purple_request_cpar_from_connection(gc), gc);
g_free(global_moods);
}
@@ -3595,64 +3681,121 @@ set_mood_show(void)
/***************************************************
* Crap *
***************************************************/
-static GtkItemFactoryEntry blist_menu[] =
-{
+/* TODO: fill out tooltips... */
+static const GtkActionEntry blist_menu_entries[] = {
/* NOTE: Do not set any accelerator to Control+O. It is mapped by
gtk_blist_key_press_cb to "Get User Info" on the selected buddy. */
-
/* Buddies menu */
- { N_("/_Buddies"), NULL, NULL, 0, "<Branch>", NULL },
- { N_("/Buddies/New Instant _Message..."), "<CTL>M", pidgin_dialogs_im, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW },
- { N_("/Buddies/Join a _Chat..."), "<CTL>C", pidgin_blist_joinchat_show, 0, "<StockItem>", PIDGIN_STOCK_CHAT },
- { N_("/Buddies/Get User _Info..."), "<CTL>I", pidgin_dialogs_info, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_USER_INFO },
- { N_("/Buddies/View User _Log..."), "<CTL>L", pidgin_dialogs_log, 0, "<Item>", NULL },
- { "/Buddies/sep1", NULL, NULL, 0, "<Separator>", NULL },
- { N_("/Buddies/Sh_ow"), NULL, NULL, 0, "<Branch>", NULL},
- { N_("/Buddies/Show/_Offline Buddies"), NULL, pidgin_blist_edit_mode_cb, 1, "<CheckItem>", NULL },
- { N_("/Buddies/Show/_Empty Groups"), NULL, pidgin_blist_show_empty_groups_cb, 1, "<CheckItem>", NULL },
- { N_("/Buddies/Show/Buddy _Details"), NULL, pidgin_blist_buddy_details_cb, 1, "<CheckItem>", NULL },
- { N_("/Buddies/Show/Idle _Times"), NULL, pidgin_blist_show_idle_time_cb, 1, "<CheckItem>", NULL },
- { N_("/Buddies/Show/_Protocol Icons"), NULL, pidgin_blist_show_protocol_icons_cb, 1, "<CheckItem>", NULL },
- { N_("/Buddies/_Sort Buddies"), NULL, NULL, 0, "<Branch>", NULL },
- { "/Buddies/sep2", NULL, NULL, 0, "<Separator>", NULL },
- { N_("/Buddies/_Add Buddy..."), "<CTL>B", pidgin_blist_add_buddy_cb, 0, "<StockItem>", GTK_STOCK_ADD },
- { N_("/Buddies/Add C_hat..."), NULL, pidgin_blist_add_chat_cb, 0, "<StockItem>", GTK_STOCK_ADD },
- { N_("/Buddies/Add _Group..."), NULL, purple_blist_request_add_group, 0, "<StockItem>", GTK_STOCK_ADD },
- { "/Buddies/sep3", NULL, NULL, 0, "<Separator>", NULL },
- { N_("/Buddies/_Quit"), "<CTL>Q", purple_core_quit, 0, "<StockItem>", GTK_STOCK_QUIT },
+ { "BuddiesMenu", NULL, N_("_Buddies"), NULL, NULL, NULL },
+ { "NewInstantMessage", PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW, N_("New Instant _Message..."), "<control>M", NULL, pidgin_dialogs_im },
+ { "JoinAChat", PIDGIN_STOCK_CHAT, N_("Join a _Chat..."), "<control>C", NULL, pidgin_blist_joinchat_show },
+ { "GetUserInfo", PIDGIN_STOCK_TOOLBAR_USER_INFO, N_("Get User _Info..."), "<control>I", NULL, pidgin_dialogs_info },
+ { "ViewUserLog", NULL, N_("View User _Log..."), "<control>L", NULL, pidgin_dialogs_log },
+ { "ShowMenu", NULL, N_("Sh_ow"), NULL, NULL, NULL },
+ { "SortMenu", NULL, N_("_Sort Buddies"), NULL, NULL, NULL },
+ { "AddBuddy", GTK_STOCK_ADD, N_("_Add Buddy..."), "<control>B", NULL, pidgin_blist_add_buddy_cb },
+ { "AddChat", GTK_STOCK_ADD, N_("Add C_hat..."), NULL, NULL, pidgin_blist_add_chat_cb },
+ { "AddGroup", GTK_STOCK_ADD, N_("Add _Group..."), NULL, NULL, purple_blist_request_add_group },
+ { "Quit", GTK_STOCK_QUIT, N_("_Quit"), "<control>Q", NULL, purple_core_quit },
/* Accounts menu */
- { N_("/_Accounts"), NULL, NULL, 0, "<Branch>", NULL },
- { N_("/Accounts/Manage Accounts"), "<CTL>A", pidgin_accounts_window_show, 0, "<Item>", NULL },
+ { "AccountsMenu", NULL, N_("_Accounts"), NULL, NULL, NULL },
+ { "ManageAccounts", NULL, N_("Manage Accounts"), "<control>A", NULL, pidgin_accounts_window_show },
/* Tools */
- { N_("/_Tools"), NULL, NULL, 0, "<Branch>", NULL },
- { N_("/Tools/Buddy _Pounces"), NULL, pidgin_pounces_manager_show, 1, "<Item>", NULL },
- { N_("/Tools/_Certificates"), NULL, pidgin_certmgr_show, 0, "<Item>", NULL },
- { N_("/Tools/Custom Smile_ys"), "<CTL>Y", pidgin_smiley_manager_show, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_SMILEY },
- { N_("/Tools/Plu_gins"), "<CTL>U", pidgin_plugin_dialog_show, 2, "<StockItem>", PIDGIN_STOCK_TOOLBAR_PLUGINS },
- { N_("/Tools/Pr_eferences"), "<CTL>P", pidgin_prefs_show, 0, "<StockItem>", GTK_STOCK_PREFERENCES },
- { N_("/Tools/Pr_ivacy"), NULL, pidgin_privacy_dialog_show, 0, "<Item>", NULL },
- { N_("/Tools/Set _Mood"), "<CTL>D", set_mood_show, 0, "<Item>", NULL },
- { "/Tools/sep2", NULL, NULL, 0, "<Separator>", NULL },
- { N_("/Tools/_File Transfers"), "<CTL>T", pidgin_xfer_dialog_show, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_TRANSFER },
- { N_("/Tools/R_oom List"), NULL, pidgin_roomlist_dialog_show, 0, "<Item>", NULL },
- { N_("/Tools/System _Log"), NULL, gtk_blist_show_systemlog_cb, 3, "<Item>", NULL },
- { "/Tools/sep3", NULL, NULL, 0, "<Separator>", NULL },
- { N_("/Tools/Mute _Sounds"), NULL, pidgin_blist_mute_sounds_cb, 0, "<CheckItem>", NULL },
+ { "ToolsMenu", NULL, N_("_Tools"), NULL, NULL, NULL },
+ { "BuddyPounces", NULL, N_("Buddy _Pounces"), NULL, NULL, pidgin_pounces_manager_show },
+ { "Certificates", NULL, N_("_Certificates"), NULL, NULL, pidgin_certmgr_show },
+ { "CustomSmileys", PIDGIN_STOCK_TOOLBAR_SMILEY, N_("Custom Smile_ys"), "<control>Y", NULL, pidgin_smiley_manager_show },
+ { "Plugins", PIDGIN_STOCK_TOOLBAR_PLUGINS, N_("Plu_gins"), "<control>U", NULL, pidgin_plugin_dialog_show },
+ { "Preferences", GTK_STOCK_PREFERENCES, N_("Pr_eferences"), "<control>P", NULL, pidgin_prefs_show },
+ { "Privacy", NULL, N_("Pr_ivacy"), NULL, NULL, pidgin_privacy_dialog_show },
+ { "SetMood", NULL, N_("Set _Mood"), "<control>D", NULL, set_mood_show },
+ { "FileTransfers", PIDGIN_STOCK_TOOLBAR_TRANSFER, N_("_File Transfers"), "<control>T", NULL, G_CALLBACK(gtk_blist_show_xfer_dialog_cb) },
+ { "RoomList", NULL, N_("R_oom List"), NULL, NULL, pidgin_roomlist_dialog_show },
+ { "SystemLog", NULL, N_("System _Log"), NULL, NULL, gtk_blist_show_systemlog_cb },
+
/* Help */
- { N_("/_Help"), NULL, NULL, 0, "<Branch>", NULL },
- { N_("/Help/Online _Help"), "F1", gtk_blist_show_onlinehelp_cb, 0, "<StockItem>", GTK_STOCK_HELP },
- { "/Help/sep1", NULL, NULL, 0, "<Separator>", NULL },
- { N_("/Help/_Build Information"), NULL, pidgin_dialogs_buildinfo, 0, "<Item>", NULL },
- { N_("/Help/_Debug Window"), NULL, toggle_debug, 0, "<Item>", NULL },
- { N_("/Help/De_veloper Information"), NULL, pidgin_dialogs_developers, 0, "<Item>", NULL },
- { N_("/Help/_Plugin Information"), NULL, pidgin_dialogs_plugins_info, 0, "<Item>", NULL },
- { N_("/Help/_Translator Information"), NULL, pidgin_dialogs_translators, 0, "<Item>", NULL },
- { "/Help/sep2", NULL, NULL, 0, "<Separator>", NULL },
- { N_("/Help/_About"), NULL, pidgin_dialogs_about, 4, "<StockItem>", GTK_STOCK_ABOUT },
+ { "HelpMenu", NULL, N_("_Help"), NULL, NULL, NULL },
+ { "OnlineHelp", GTK_STOCK_HELP, N_("Online _Help"), "F1", NULL, gtk_blist_show_onlinehelp_cb },
+ { "BuildInformation", NULL, N_("_Build Information"), NULL, NULL, pidgin_dialogs_buildinfo },
+ { "DebugWindow", NULL, N_("_Debug Window"), NULL, NULL, toggle_debug },
+ { "DeveloperInformation", NULL, N_("De_veloper Information"), NULL, NULL, pidgin_dialogs_developers },
+ { "PluginInformation", NULL, N_("_Plugin Information"), NULL, NULL, pidgin_dialogs_plugins_info },
+ { "TranslatorInformation", NULL, N_("_Translator Information"), NULL, NULL, pidgin_dialogs_translators },
+ { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, pidgin_dialogs_about },
+};
+
+/* Toggle items */
+static const GtkToggleActionEntry blist_menu_toggle_entries[] = {
+ /* Buddies->Show menu */
+ { "ShowOffline", NULL, N_("_Offline Buddies"), NULL, NULL, G_CALLBACK(pidgin_blist_edit_mode_cb), FALSE },
+ { "ShowEmptyGroups", NULL, N_("_Empty Groups"), NULL, NULL, G_CALLBACK(pidgin_blist_show_empty_groups_cb), FALSE },
+ { "ShowBuddyDetails", NULL, N_("Buddy _Details"), NULL, NULL, G_CALLBACK(pidgin_blist_buddy_details_cb), FALSE },
+ { "ShowIdleTimes", NULL, N_("Idle _Times"), NULL, NULL, G_CALLBACK(pidgin_blist_show_idle_time_cb), FALSE },
+ { "ShowProtocolIcons", NULL, N_("_Protocol Icons"), NULL, NULL, G_CALLBACK(pidgin_blist_show_protocol_icons_cb), FALSE },
+
+ /* Tools menu */
+ { "MuteSounds", NULL, N_("Mute _Sounds"), NULL, NULL, G_CALLBACK(pidgin_blist_mute_sounds_cb), FALSE },
};
+static const char *blist_menu =
+"<ui>"
+ "<menubar name='BList'>"
+ "<menu action='BuddiesMenu'>"
+ "<menuitem action='NewInstantMessage'/>"
+ "<menuitem action='JoinAChat'/>"
+ "<menuitem action='GetUserInfo'/>"
+ "<menuitem action='ViewUserLog'/>"
+ "<separator/>"
+ "<menu action='ShowMenu'>"
+ "<menuitem action='ShowOffline'/>"
+ "<menuitem action='ShowEmptyGroups'/>"
+ "<menuitem action='ShowBuddyDetails'/>"
+ "<menuitem action='ShowIdleTimes'/>"
+ "<menuitem action='ShowProtocolIcons'/>"
+ "</menu>"
+ "<menu action='SortMenu'/>"
+ "<separator/>"
+ "<menuitem action='AddBuddy'/>"
+ "<menuitem action='AddChat'/>"
+ "<menuitem action='AddGroup'/>"
+ "<separator/>"
+ "<menuitem action='Quit'/>"
+ "</menu>"
+ "<menu action='AccountsMenu'>"
+ "<menuitem action='ManageAccounts'/>"
+ "</menu>"
+ "<menu action='ToolsMenu'>"
+ "<menuitem action='BuddyPounces'/>"
+ "<menuitem action='Certificates'/>"
+ "<menuitem action='CustomSmileys'/>"
+ "<menuitem action='Plugins'/>"
+ "<menuitem action='Preferences'/>"
+ "<menuitem action='Privacy'/>"
+ "<menuitem action='SetMood'/>"
+ "<separator/>"
+ "<menuitem action='FileTransfers'/>"
+ "<menuitem action='RoomList'/>"
+ "<menuitem action='SystemLog'/>"
+ "<separator/>"
+ "<menuitem action='MuteSounds'/>"
+ "<placeholder name='PluginActions'/>"
+ "</menu>"
+ "<menu action='HelpMenu'>"
+ "<menuitem action='OnlineHelp'/>"
+ "<separator/>"
+ "<menuitem action='BuildInformation'/>"
+ "<menuitem action='DebugWindow'/>"
+ "<menuitem action='DeveloperInformation'/>"
+ "<menuitem action='PluginInformation'/>"
+ "<menuitem action='TranslatorInformation'/>"
+ "<separator/>"
+ "<menuitem action='About'/>"
+ "</menu>"
+ "</menubar>"
+"</ui>";
+
/*********************************************************
* Private Utility functions *
*********************************************************/
@@ -3664,48 +3807,48 @@ static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full)
PurplePluginProtocolInfo *prpl_info = NULL;
char *tmp;
- if (PURPLE_BLIST_NODE_IS_CHAT(node))
+ if (PURPLE_IS_CHAT(node))
{
PurpleChat *chat;
GList *connections;
GList *cur;
struct proto_chat_entry *pce;
char *name, *value;
- PurpleConversation *conv;
- PidginBlistNode *bnode = node->ui_data;
+ PurpleChatConversation *conv;
+ PidginBlistNode *bnode = purple_blist_node_get_ui_data(node);
chat = (PurpleChat *)node;
- prpl = purple_find_prpl(purple_account_get_protocol_id(chat->account));
+ prpl = purple_find_prpl(purple_account_get_protocol_id(purple_chat_get_account(chat)));
prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
connections = purple_connections_get_all();
if (connections && connections->next)
{
- tmp = g_markup_escape_text(chat->account->username, -1);
+ tmp = g_markup_escape_text(purple_account_get_username(purple_chat_get_account(chat)), -1);
g_string_append_printf(str, _("<b>Account:</b> %s"), tmp);
g_free(tmp);
}
if (bnode && bnode->conv.conv) {
- conv = bnode->conv.conv;
+ conv = PURPLE_CHAT_CONVERSATION(bnode->conv.conv);
} else {
char *chat_name;
if (prpl_info && prpl_info->get_chat_name)
- chat_name = prpl_info->get_chat_name(chat->components);
+ chat_name = prpl_info->get_chat_name(purple_chat_get_components(chat));
else
chat_name = g_strdup(purple_chat_get_name(chat));
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, chat_name,
- chat->account);
+ conv = purple_conversations_find_chat_with_account(chat_name,
+ purple_chat_get_account(chat));
g_free(chat_name);
}
- if (conv && !purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv))) {
+ if (conv && !purple_chat_conversation_has_left(conv)) {
g_string_append_printf(str, _("\n<b>Occupants:</b> %d"),
- g_list_length(purple_conv_chat_get_users(PURPLE_CONV_CHAT(conv))));
+ purple_chat_conversation_get_users_count(conv));
if (prpl_info && (prpl_info->options & OPT_PROTO_CHAT_TOPIC)) {
- const char *chattopic = purple_conv_chat_get_topic(PURPLE_CONV_CHAT(conv));
+ const char *chattopic = purple_chat_conversation_get_topic(conv);
char *topic = chattopic ? g_markup_escape_text(chattopic, -1) : NULL;
g_string_append_printf(str, _("\n<b>Topic:</b> %s"), topic ? topic : _("(no topic set)"));
g_free(topic);
@@ -3713,7 +3856,7 @@ static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full)
}
if (prpl_info && prpl_info->chat_info != NULL)
- cur = prpl_info->chat_info(chat->account->gc);
+ cur = prpl_info->chat_info(purple_account_get_connection(purple_chat_get_account(chat)));
else
cur = NULL;
@@ -3721,14 +3864,13 @@ static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full)
{
pce = cur->data;
- if (!pce->secret && (!pce->required &&
- g_hash_table_lookup(chat->components, pce->identifier) == NULL))
+ if (!pce->secret)
{
tmp = purple_text_strip_mnemonic(pce->label);
name = g_markup_escape_text(tmp, -1);
g_free(tmp);
value = g_markup_escape_text(g_hash_table_lookup(
- chat->components, pce->identifier), -1);
+ purple_chat_get_components(chat), pce->identifier), -1);
g_string_append_printf(str, "\n<b>%s</b> %s",
name ? name : "",
value ? value : "");
@@ -3740,7 +3882,7 @@ static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full)
cur = g_list_delete_link(cur, cur);
}
}
- else if (PURPLE_BLIST_NODE_IS_CONTACT(node) || PURPLE_BLIST_NODE_IS_BUDDY(node))
+ else if (PURPLE_IS_CONTACT(node) || PURPLE_IS_BUDDY(node))
{
/* NOTE: THIS FUNCTION IS NO LONGER CALLED FOR CONTACTS.
* It is only called by create_tip_for_node(), and create_tip_for_node() is never called for a contact.
@@ -3751,9 +3893,10 @@ static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full)
PurpleNotifyUserInfo *user_info;
GList *connections;
char *tmp;
+ gchar *alias;
time_t idle_secs, signon;
- if (PURPLE_BLIST_NODE_IS_CONTACT(node))
+ if (PURPLE_IS_CONTACT(node))
{
c = (PurpleContact *)node;
b = purple_contact_get_priority_buddy(c);
@@ -3764,7 +3907,7 @@ static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full)
c = purple_buddy_get_contact(b);
}
- prpl = purple_find_prpl(purple_account_get_protocol_id(b->account));
+ prpl = purple_find_prpl(purple_account_get_protocol_id(purple_buddy_get_account(b)));
prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
presence = purple_buddy_get_presence(b);
@@ -3774,22 +3917,20 @@ static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full)
connections = purple_connections_get_all();
if (full && connections && connections->next)
{
- tmp = g_markup_escape_text(purple_account_get_username(
- purple_buddy_get_account(b)), -1);
- purple_notify_user_info_add_pair(user_info, _("Account"), tmp);
- g_free(tmp);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Account"),
+ purple_account_get_username(purple_buddy_get_account(b)));
}
/* Alias */
/* If there's not a contact alias, the node is being displayed with
* this alias, so there's no point in showing it in the tooltip. */
- if (full && c && b->alias != NULL && b->alias[0] != '\0' &&
- (c->alias != NULL && c->alias[0] != '\0') &&
- strcmp(c->alias, b->alias) != 0)
+ g_object_get(c, "alias", &alias, NULL);
+ if (full && c && purple_buddy_get_local_alias(b) != NULL && purple_buddy_get_local_alias(b)[0] != '\0' &&
+ (alias != NULL && alias[0] != '\0') &&
+ strcmp(alias, purple_buddy_get_local_alias(b)) != 0)
{
- tmp = g_markup_escape_text(b->alias, -1);
- purple_notify_user_info_add_pair(user_info, _("Buddy Alias"), tmp);
- g_free(tmp);
+ purple_notify_user_info_add_pair_plaintext(user_info,
+ _("Buddy Alias"), purple_buddy_get_local_alias(b));
}
/* Nickname/Server Alias */
@@ -3797,11 +3938,10 @@ static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full)
* alias, but many people on MSN set long nicknames, which
* get ellipsized, so the only way to see the whole thing is
* to look at the tooltip. */
- if (full && b->server_alias != NULL && b->server_alias[0] != '\0')
+ if (full && purple_buddy_get_server_alias(b))
{
- tmp = g_markup_escape_text(b->server_alias, -1);
- purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp);
- g_free(tmp);
+ purple_notify_user_info_add_pair_plaintext(user_info,
+ _("Nickname"), purple_buddy_get_server_alias(b));
}
/* Logged In */
@@ -3817,7 +3957,7 @@ static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full)
tmp = g_strdup(purple_date_format_long(localtime(&signon)));
} else
tmp = purple_str_seconds_to_string(time(NULL) - signon);
- purple_notify_user_info_add_pair(user_info, _("Logged In"), tmp);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Logged In"), tmp);
g_free(tmp);
}
@@ -3828,7 +3968,7 @@ static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full)
if (idle_secs > 0)
{
tmp = purple_str_seconds_to_string(time(NULL) - idle_secs);
- purple_notify_user_info_add_pair(user_info, _("Idle"), tmp);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Idle"), tmp);
g_free(tmp);
}
}
@@ -3836,11 +3976,11 @@ static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full)
/* Last Seen */
if (full && c && !PURPLE_BUDDY_IS_ONLINE(b))
{
- struct _pidgin_blist_node *gtknode = ((PurpleBlistNode *)c)->ui_data;
+ struct _pidgin_blist_node *gtknode = purple_blist_node_get_ui_data(PURPLE_BLIST_NODE(c));
PurpleBlistNode *bnode;
int lastseen = 0;
- if (gtknode && (!gtknode->contact_expanded || PURPLE_BLIST_NODE_IS_CONTACT(node)))
+ if (gtknode && (!gtknode->contact_expanded || PURPLE_IS_CONTACT(node)))
{
/* We're either looking at a buddy for a collapsed contact or
* an expanded contact itself so we show the most recent
@@ -3863,7 +4003,7 @@ static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full)
if (lastseen > 0)
{
tmp = purple_str_seconds_to_string(time(NULL) - lastseen);
- purple_notify_user_info_add_pair(user_info, _("Last Seen"), tmp);
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Last Seen"), tmp);
g_free(tmp);
}
}
@@ -3873,10 +4013,10 @@ static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full)
/* FIXME: Why is this status special-cased by the core? --rlaager
* FIXME: Alternatively, why not have the core do all of them? --rlaager */
if (!PURPLE_BUDDY_IS_ONLINE(b)) {
- purple_notify_user_info_add_pair(user_info, _("Status"), _("Offline"));
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), _("Offline"));
}
- if (purple_account_is_connected(b->account) &&
+ if (purple_account_is_connected(purple_buddy_get_account(b)) &&
prpl_info && prpl_info->tooltip_text)
{
/* Additional text from the PRPL */
@@ -3884,44 +4024,42 @@ static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full)
}
/* These are Easter Eggs. Patches to remove them will be rejected. */
- if (!g_ascii_strcasecmp(b->name, "robflynn"))
- purple_notify_user_info_add_pair(user_info, _("Description"), _("Spooky"));
- if (!g_ascii_strcasecmp(b->name, "seanegn"))
- purple_notify_user_info_add_pair(user_info, _("Status"), _("Awesome"));
- if (!g_ascii_strcasecmp(b->name, "chipx86"))
- purple_notify_user_info_add_pair(user_info, _("Status"), _("Rockin'"));
+ if (!g_ascii_strcasecmp(purple_buddy_get_name(b), "robflynn"))
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Description"), _("Spooky"));
+ if (!g_ascii_strcasecmp(purple_buddy_get_name(b), "seanegn"))
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), _("Awesome"));
+ if (!g_ascii_strcasecmp(purple_buddy_get_name(b), "chipx86"))
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), _("Rockin'"));
tmp = purple_notify_user_info_get_text_with_newline(user_info, "\n");
g_string_append(str, tmp);
g_free(tmp);
+ g_free(alias);
purple_notify_user_info_destroy(user_info);
- } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+ } else if (PURPLE_IS_GROUP(node)) {
gint count;
PurpleGroup *group = (PurpleGroup*)node;
PurpleNotifyUserInfo *user_info;
user_info = purple_notify_user_info_new();
- count = purple_blist_get_group_online_count(group);
-
+ count = purple_counting_node_get_online_count(PURPLE_COUNTING_NODE(group));
if (count != 0) {
/* Online buddies in group */
- tmp = g_strdup_printf("%d", count);
- purple_notify_user_info_add_pair(user_info,
- _("Online Buddies"),
- tmp);
- g_free(tmp);
+ char tmp2[12];
+ sprintf(tmp2, "%d", count);
+ purple_notify_user_info_add_pair_plaintext(user_info,
+ _("Online Buddies"), tmp2);
}
- count = purple_blist_get_group_size(group, FALSE);
+ count = purple_counting_node_get_current_size(PURPLE_COUNTING_NODE(group));
if (count != 0) {
/* Total buddies (from online accounts) in group */
- tmp = g_strdup_printf("%d", count);
- purple_notify_user_info_add_pair(user_info,
- _("Total Buddies"),
- tmp);
- g_free(tmp);
+ char tmp2[12];
+ sprintf(tmp2, "%d", count);
+ purple_notify_user_info_add_pair_html(user_info,
+ _("Total Buddies"), tmp2);
}
tmp = purple_notify_user_info_get_text_with_newline(user_info, "\n");
@@ -3968,7 +4106,7 @@ GdkPixbuf *
pidgin_blist_get_emblem(PurpleBlistNode *node)
{
PurpleBuddy *buddy = NULL;
- struct _pidgin_blist_node *gtknode = node->ui_data;
+ struct _pidgin_blist_node *gtknode = purple_blist_node_get_ui_data(node);
PurplePlugin *prpl;
PurplePluginProtocolInfo *prpl_info;
const char *name = NULL;
@@ -3976,25 +4114,25 @@ pidgin_blist_get_emblem(PurpleBlistNode *node)
PurplePresence *p = NULL;
PurpleStatus *tune;
- if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ if(PURPLE_IS_CONTACT(node)) {
if(!gtknode->contact_expanded) {
buddy = purple_contact_get_priority_buddy((PurpleContact*)node);
}
- } else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ } else if(PURPLE_IS_BUDDY(node)) {
buddy = (PurpleBuddy*)node;
p = purple_buddy_get_presence(buddy);
if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) {
/* This emblem comes from the small emoticon set now,
* to reduce duplication. */
- path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emotes",
- "small", "mobile.png", NULL);
+ path = g_build_filename(PURPLE_DATADIR, "pixmaps",
+ "pidgin", "emotes", "small", "mobile.png", NULL);
return _pidgin_blist_get_cached_emblem(path);
}
- if (((struct _pidgin_blist_node*)(node->parent->ui_data))->contact_expanded) {
+ if (((struct _pidgin_blist_node*)purple_blist_node_get_ui_data(node->parent))->contact_expanded) {
if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"))
return NULL;
- return pidgin_create_prpl_icon(((PurpleBuddy*)node)->account, PIDGIN_PRPL_ICON_SMALL);
+ return pidgin_create_prpl_icon(purple_buddy_get_account((PurpleBuddy*)node), PIDGIN_PRPL_ICON_SMALL);
}
} else {
return NULL;
@@ -4002,8 +4140,9 @@ pidgin_blist_get_emblem(PurpleBlistNode *node)
g_return_val_if_fail(buddy != NULL, NULL);
- if (!purple_privacy_check(buddy->account, purple_buddy_get_name(buddy))) {
- path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "blocked.png", NULL);
+ if (!purple_account_privacy_check(purple_buddy_get_account(buddy), purple_buddy_get_name(buddy))) {
+ path = g_build_filename(PURPLE_DATADIR, "pixmaps", "pidgin",
+ "emblems", "16", "blocked.png", NULL);
return _pidgin_blist_get_cached_emblem(path);
}
@@ -4014,7 +4153,8 @@ pidgin_blist_get_emblem(PurpleBlistNode *node)
if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) {
/* This emblem comes from the small emoticon set now, to reduce duplication. */
- path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emotes", "small", "mobile.png", NULL);
+ path = g_build_filename(PURPLE_DATADIR, "pixmaps", "pidgin",
+ "emotes", "small", "mobile.png", NULL);
return _pidgin_blist_get_cached_emblem(path);
}
@@ -4023,22 +4163,25 @@ pidgin_blist_get_emblem(PurpleBlistNode *node)
/* Only in MSN.
* TODO: Replace "Tune" with generalized "Media" in 3.0. */
if (purple_status_get_attr_string(tune, "game") != NULL) {
- path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "game.png", NULL);
+ path = g_build_filename(PURPLE_DATADIR, "pixmaps",
+ "pidgin", "emblems", "16", "game.png", NULL);
return _pidgin_blist_get_cached_emblem(path);
}
/* Only in MSN.
* TODO: Replace "Tune" with generalized "Media" in 3.0. */
if (purple_status_get_attr_string(tune, "office") != NULL) {
- path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "office.png", NULL);
+ path = g_build_filename(PURPLE_DATADIR, "pixmaps",
+ "pidgin", "emblems", "16", "office.png", NULL);
return _pidgin_blist_get_cached_emblem(path);
}
/* Regular old "tune" is the only one in all protocols. */
/* This emblem comes from the small emoticon set now, to reduce duplication. */
- path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emotes", "small", "music.png", NULL);
+ path = g_build_filename(PURPLE_DATADIR, "pixmaps", "pidgin",
+ "emotes", "small", "music.png", NULL);
return _pidgin_blist_get_cached_emblem(path);
}
- prpl = purple_find_prpl(purple_account_get_protocol_id(buddy->account));
+ prpl = purple_find_prpl(purple_account_get_protocol_id(purple_buddy_get_account(buddy)));
if (!prpl)
return NULL;
@@ -4061,7 +4204,8 @@ pidgin_blist_get_emblem(PurpleBlistNode *node)
path = get_mood_icon_path(name);
} else {
filename = g_strdup_printf("%s.png", name);
- path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", filename, NULL);
+ path = g_build_filename(PURPLE_DATADIR, "pixmaps", "pidgin",
+ "emblems", "16", filename, NULL);
g_free(filename);
}
@@ -4075,23 +4219,23 @@ pidgin_blist_get_status_icon(PurpleBlistNode *node, PidginStatusIconSize size)
{
GdkPixbuf *ret;
const char *icon = NULL;
- struct _pidgin_blist_node *gtknode = node->ui_data;
+ struct _pidgin_blist_node *gtknode = purple_blist_node_get_ui_data(node);
struct _pidgin_blist_node *gtkbuddynode = NULL;
PurpleBuddy *buddy = NULL;
PurpleChat *chat = NULL;
GtkIconSize icon_size = gtk_icon_size_from_name((size == PIDGIN_STATUS_ICON_LARGE) ? PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL :
PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC);
- if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ if(PURPLE_IS_CONTACT(node)) {
if(!gtknode->contact_expanded) {
buddy = purple_contact_get_priority_buddy((PurpleContact*)node);
if (buddy != NULL)
- gtkbuddynode = ((PurpleBlistNode*)buddy)->ui_data;
+ gtkbuddynode = purple_blist_node_get_ui_data(PURPLE_BLIST_NODE(buddy));
}
- } else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ } else if(PURPLE_IS_BUDDY(node)) {
buddy = (PurpleBuddy*)node;
- gtkbuddynode = node->ui_data;
- } else if(PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ gtkbuddynode = purple_blist_node_get_ui_data(node);
+ } else if(PURPLE_IS_CHAT(node)) {
chat = (PurpleChat*)node;
} else {
return NULL;
@@ -4102,9 +4246,9 @@ pidgin_blist_get_status_icon(PurpleBlistNode *node, PidginStatusIconSize size)
PurplePlugin *prpl;
if(buddy)
- account = buddy->account;
+ account = purple_buddy_get_account(buddy);
else
- account = chat->account;
+ account = purple_chat_get_account(chat);
prpl = purple_find_prpl(purple_account_get_protocol_id(account));
if(!prpl)
@@ -4119,7 +4263,7 @@ pidgin_blist_get_status_icon(PurpleBlistNode *node, PidginStatusIconSize size)
if(conv != NULL) {
PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
if (gtkconv == NULL && size == PIDGIN_STATUS_ICON_SMALL) {
- PidginBlistNode *ui = buddy->node.ui_data;
+ PidginBlistNode *ui = purple_blist_node_get_ui_data(&(buddy->node));
if (ui == NULL || (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE))
return gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview),
PIDGIN_STOCK_STATUS_MESSAGE, icon_size, "GtkTreeView");
@@ -4201,9 +4345,10 @@ pidgin_blist_get_name_markup(PurpleBuddy *b, gboolean selected, gboolean aliased
gboolean biglist = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons");
PidginThemeFont *statusfont = NULL, *namefont = NULL;
PidginBlistTheme *theme;
+ gchar *contact_alias;
if (conv != NULL) {
- PidginBlistNode *ui = b->node.ui_data;
+ PidginBlistNode *ui = purple_blist_node_get_ui_data(&(b->node));
if (ui) {
if (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE)
hidden_conv = TRUE;
@@ -4218,9 +4363,11 @@ pidgin_blist_get_name_markup(PurpleBuddy *b, gboolean selected, gboolean aliased
if(contact)
gtkcontactnode = purple_blist_node_get_ui_data(PURPLE_BLIST_NODE(contact));
+ g_object_get(contact, "alias", &contact_alias, NULL);
+
/* Name */
- if (gtkcontactnode && !gtkcontactnode->contact_expanded && contact->alias)
- name = contact->alias;
+ if (gtkcontactnode && !gtkcontactnode->contact_expanded && contact_alias)
+ name = contact_alias;
else
name = purple_buddy_get_alias(b);
@@ -4237,12 +4384,12 @@ pidgin_blist_get_name_markup(PurpleBuddy *b, gboolean selected, gboolean aliased
if (!aliased || biglist) {
/* Status Info */
- prpl = purple_find_prpl(purple_account_get_protocol_id(b->account));
+ prpl = purple_find_prpl(purple_account_get_protocol_id(purple_buddy_get_account(b)));
if (prpl != NULL)
prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
- if (prpl_info && prpl_info->status_text && b->account->gc) {
+ if (prpl_info && prpl_info->status_text && purple_account_get_connection(purple_buddy_get_account(b))) {
char *tmp = prpl_info->status_text(b);
const char *end;
@@ -4374,6 +4521,7 @@ pidgin_blist_get_name_markup(PurpleBuddy *b, gboolean selected, gboolean aliased
g_free(nametext);
g_free(statustext);
g_free(idletime);
+ g_free(contact_alias);
if (hidden_conv) {
char *tmp = text;
@@ -4393,7 +4541,7 @@ static void pidgin_blist_restore_position(void)
/* if the window exists, is hidden, we're saving positions, and the
* position is sane... */
if (gtkblist && gtkblist->window &&
- !GTK_WIDGET_VISIBLE(gtkblist->window) && blist_width != 0) {
+ !gtk_widget_get_visible(gtkblist->window) && blist_width != 0) {
blist_x = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/x");
blist_y = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/y");
@@ -4423,21 +4571,21 @@ static gboolean pidgin_blist_refresh_timer(PurpleBuddyList *list)
PurpleBlistNode *gnode, *cnode;
if (gtk_blist_visibility == GDK_VISIBILITY_FULLY_OBSCURED
- || !GTK_WIDGET_VISIBLE(gtkblist->window))
+ || !gtk_widget_get_visible(gtkblist->window))
return TRUE;
for(gnode = list->root; gnode; gnode = gnode->next) {
- if(!PURPLE_BLIST_NODE_IS_GROUP(gnode))
+ if(!PURPLE_IS_GROUP(gnode))
continue;
for(cnode = gnode->child; cnode; cnode = cnode->next) {
- if(PURPLE_BLIST_NODE_IS_CONTACT(cnode)) {
+ if(PURPLE_IS_CONTACT(cnode)) {
PurpleBuddy *buddy;
buddy = purple_contact_get_priority_buddy((PurpleContact*)cnode);
if (buddy &&
purple_presence_is_idle(purple_buddy_get_presence(buddy)))
- pidgin_blist_update_contact(list, (PurpleBlistNode*)buddy);
+ pidgin_blist_update_contact(list, PURPLE_BLIST_NODE(buddy));
}
}
}
@@ -4448,7 +4596,7 @@ static gboolean pidgin_blist_refresh_timer(PurpleBuddyList *list)
static void pidgin_blist_hide_node(PurpleBuddyList *list, PurpleBlistNode *node, gboolean update)
{
- struct _pidgin_blist_node *gtknode = (struct _pidgin_blist_node *)node->ui_data;
+ struct _pidgin_blist_node *gtknode = purple_blist_node_get_ui_data(node);
GtkTreeIter iter;
if (!gtknode || !gtknode->row || !gtkblist)
@@ -4458,8 +4606,8 @@ static void pidgin_blist_hide_node(PurpleBuddyList *list, PurpleBlistNode *node,
gtkblist->selected_node = NULL;
if (get_iter_from_node(node, &iter)) {
gtk_tree_store_remove(gtkblist->treemodel, &iter);
- if(update && (PURPLE_BLIST_NODE_IS_CONTACT(node) ||
- PURPLE_BLIST_NODE_IS_BUDDY(node) || PURPLE_BLIST_NODE_IS_CHAT(node))) {
+ if(update && (PURPLE_IS_CONTACT(node) ||
+ PURPLE_IS_BUDDY(node) || PURPLE_IS_CHAT(node))) {
pidgin_blist_update(list, node->parent);
}
}
@@ -4469,25 +4617,26 @@ static void pidgin_blist_hide_node(PurpleBuddyList *list, PurpleBlistNode *node,
static const char *require_connection[] =
{
- N_("/Buddies/New Instant Message..."),
- N_("/Buddies/Join a Chat..."),
- N_("/Buddies/Get User Info..."),
- N_("/Buddies/Add Buddy..."),
- N_("/Buddies/Add Chat..."),
- N_("/Buddies/Add Group..."),
+ "/BList/BuddiesMenu/NewInstantMessage",
+ "/BList/BuddiesMenu/JoinAChat",
+ "/BList/BuddiesMenu/GetUserInfo",
+ "/BList/BuddiesMenu/AddBuddy",
+ "/BList/BuddiesMenu/AddChat",
+ "/BList/BuddiesMenu/AddGroup",
+ "/BList/ToolsMenu/Privacy",
};
static const int require_connection_size = sizeof(require_connection)
/ sizeof(*require_connection);
-/**
+/*
* Rebuild dynamic menus and make menu items sensitive/insensitive
* where appropriate.
*/
static void
update_menu_bar(PidginBuddyList *gtkblist)
{
- GtkWidget *widget;
+ GtkAction *action;
gboolean sensitive;
int i;
@@ -4499,21 +4648,18 @@ update_menu_bar(PidginBuddyList *gtkblist)
for (i = 0; i < require_connection_size; i++)
{
- widget = gtk_item_factory_get_widget(gtkblist->ift, require_connection[i]);
- gtk_widget_set_sensitive(widget, sensitive);
+ action = gtk_ui_manager_get_action(gtkblist->ui, require_connection[i]);
+ gtk_action_set_sensitive(action, sensitive);
}
- widget = gtk_item_factory_get_widget(gtkblist->ift, N_("/Buddies/Join a Chat..."));
- gtk_widget_set_sensitive(widget, pidgin_blist_joinchat_is_showable());
-
- widget = gtk_item_factory_get_widget(gtkblist->ift, N_("/Buddies/Add Chat..."));
- gtk_widget_set_sensitive(widget, pidgin_blist_joinchat_is_showable());
+ action = gtk_ui_manager_get_action(gtkblist->ui, "/BList/BuddiesMenu/JoinAChat");
+ gtk_action_set_sensitive(action, pidgin_blist_joinchat_is_showable());
- widget = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Privacy"));
- gtk_widget_set_sensitive(widget, sensitive);
+ action = gtk_ui_manager_get_action(gtkblist->ui, "/BList/BuddiesMenu/AddChat");
+ gtk_action_set_sensitive(action, pidgin_blist_joinchat_is_showable());
- widget = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Room List"));
- gtk_widget_set_sensitive(widget, pidgin_roomlist_is_showable());
+ action = gtk_ui_manager_get_action(gtkblist->ui, "/BList/ToolsMenu/RoomList");
+ gtk_action_set_sensitive(action, pidgin_roomlist_is_showable());
}
static void
@@ -4542,11 +4688,9 @@ unseen_conv_menu(void)
menu = NULL;
}
- ims = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_IM,
- PIDGIN_UNSEEN_TEXT, FALSE, 0);
+ ims = pidgin_conversations_get_unseen_ims(PIDGIN_UNSEEN_TEXT, FALSE, 0);
- chats = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_CHAT,
- PIDGIN_UNSEEN_NICK, FALSE, 0);
+ chats = pidgin_conversations_get_unseen_chats(PIDGIN_UNSEEN_NICK, FALSE, 0);
if(ims && chats)
convs = g_list_concat(ims, chats);
@@ -4575,12 +4719,10 @@ menutray_press_cb(GtkWidget *widget, GdkEventButton *event)
switch (event->button) {
case 1:
- convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_IM,
- PIDGIN_UNSEEN_TEXT, FALSE, 1);
+ convs = pidgin_conversations_get_unseen_ims(PIDGIN_UNSEEN_TEXT, FALSE, 1);
if(!convs)
- convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_CHAT,
- PIDGIN_UNSEEN_NICK, FALSE, 1);
+ convs = pidgin_conversations_get_unseen_chats(PIDGIN_UNSEEN_NICK, FALSE, 1);
if (convs) {
pidgin_conv_present_conversation((PurpleConversation*)convs->data);
g_list_free(convs);
@@ -4594,20 +4736,21 @@ menutray_press_cb(GtkWidget *widget, GdkEventButton *event)
}
static void
-conversation_updated_cb(PurpleConversation *conv, PurpleConvUpdateType type,
+conversation_updated_cb(PurpleConversation *conv, PurpleConversationUpdateType type,
PidginBuddyList *gtkblist)
{
+ PurpleAccount *account = purple_conversation_get_account(conv);
GList *convs = NULL;
GList *ims, *chats;
GList *l = NULL;
- if (type != PURPLE_CONV_UPDATE_UNSEEN)
+ if (type != PURPLE_CONVERSATION_UPDATE_UNSEEN)
return;
- if(conv->account != NULL && conv->name != NULL) {
- PurpleBuddy *buddy = purple_find_buddy(conv->account, conv->name);
+ if(account != NULL && purple_conversation_get_name(conv) != NULL) {
+ PurpleBuddy *buddy = purple_blist_find_buddy(account, purple_conversation_get_name(conv));
if(buddy != NULL)
- pidgin_blist_update_buddy(NULL, (PurpleBlistNode *)buddy, TRUE);
+ pidgin_blist_update_buddy(NULL, PURPLE_BLIST_NODE(buddy), TRUE);
}
if (gtkblist->menutrayicon) {
@@ -4615,11 +4758,9 @@ conversation_updated_cb(PurpleConversation *conv, PurpleConvUpdateType type,
gtkblist->menutrayicon = NULL;
}
- ims = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_IM,
- PIDGIN_UNSEEN_TEXT, FALSE, 0);
+ ims = pidgin_conversations_get_unseen_ims(PIDGIN_UNSEEN_TEXT, FALSE, 0);
- chats = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_CHAT,
- PIDGIN_UNSEEN_NICK, FALSE, 0);
+ chats = pidgin_conversations_get_unseen_chats(PIDGIN_UNSEEN_NICK, FALSE, 0);
if(ims && chats)
convs = g_list_concat(ims, chats);
@@ -4640,8 +4781,8 @@ conversation_updated_cb(PurpleConversation *conv, PurpleConvUpdateType type,
if(gtkconv)
count = gtkconv->unseen_count;
- else if(purple_conversation_get_data(l->data, "unseen-count"))
- count = GPOINTER_TO_INT(purple_conversation_get_data(l->data, "unseen-count"));
+ else if(g_object_get_data(G_OBJECT(l->data), "unseen-count"))
+ count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(l->data), "unseen-count"));
g_string_append_printf(tooltip_text,
ngettext("%d unread message from %s\n", "%d unread messages from %s\n", count),
@@ -4670,7 +4811,7 @@ conversation_updated_cb(PurpleConversation *conv, PurpleConvUpdateType type,
static void
conversation_deleting_cb(PurpleConversation *conv, PidginBuddyList *gtkblist)
{
- conversation_updated_cb(conv, PURPLE_CONV_UPDATE_UNSEEN, gtkblist);
+ conversation_updated_cb(conv, PURPLE_CONVERSATION_UPDATE_UNSEEN, gtkblist);
}
static void
@@ -4684,81 +4825,124 @@ conversation_deleted_update_ui_cb(PurpleConversation *conv, struct _pidgin_blist
}
static void
-written_msg_update_ui_cb(PurpleAccount *account, const char *who, const char *message,
- PurpleConversation *conv, PurpleMessageFlags flag, PurpleBlistNode *node)
+written_msg_update_ui_cb(PurpleConversation *conv, PurpleMessage *msg, PurpleBlistNode *node)
{
- PidginBlistNode *ui = node->ui_data;
- if (ui->conv.conv != conv || !pidgin_conv_is_hidden(PIDGIN_CONVERSATION(conv)) ||
- !(flag & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV)))
+ PidginBlistNode *ui = purple_blist_node_get_ui_data(node);
+
+ if (ui->conv.conv != conv)
return;
+
+ if (!pidgin_conv_is_hidden(PIDGIN_CONVERSATION(conv)))
+ return;
+
+ if (!(purple_message_get_flags(msg) & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV)))
+ return;
+
ui->conv.flags |= PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE;
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT
- && (flag & PURPLE_MESSAGE_NICK))
+ 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;
ui->conv.last_message = time(NULL); /* XXX: for lack of better data */
- pidgin_blist_update(purple_get_blist(), node);
+ pidgin_blist_update(purple_blist_get_buddy_list(), node);
}
static void
displayed_msg_update_ui_cb(PidginConversation *gtkconv, PurpleBlistNode *node)
{
- PidginBlistNode *ui = node->ui_data;
+ PidginBlistNode *ui = purple_blist_node_get_ui_data(node);
if (ui->conv.conv != gtkconv->active_conv)
return;
ui->conv.flags &= ~(PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE |
PIDGIN_BLIST_CHAT_HAS_PENDING_MESSAGE_WITH_NICK);
- pidgin_blist_update(purple_get_blist(), node);
+ pidgin_blist_update(purple_blist_get_buddy_list(), node);
}
static void
conversation_created_cb(PurpleConversation *conv, PidginBuddyList *gtkblist)
{
- switch (conv->type) {
- case PURPLE_CONV_TYPE_IM:
- {
- GSList *buddies = purple_find_buddies(conv->account, conv->name);
- while (buddies) {
- PurpleBlistNode *buddy = buddies->data;
- struct _pidgin_blist_node *ui = buddy->ui_data;
- buddies = g_slist_delete_link(buddies, buddies);
- if (!ui)
- continue;
- ui->conv.conv = conv;
- ui->conv.flags = 0;
- ui->conv.last_message = 0;
- purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation",
- ui, PURPLE_CALLBACK(conversation_deleted_update_ui_cb), ui);
- purple_signal_connect(purple_conversations_get_handle(), "wrote-im-msg",
- ui, PURPLE_CALLBACK(written_msg_update_ui_cb), buddy);
- purple_signal_connect(pidgin_conversations_get_handle(), "conversation-displayed",
- ui, PURPLE_CALLBACK(displayed_msg_update_ui_cb), buddy);
- }
- }
- break;
- case PURPLE_CONV_TYPE_CHAT:
- {
- PurpleChat *chat = purple_blist_find_chat(conv->account, conv->name);
- struct _pidgin_blist_node *ui;
- if (!chat)
- break;
- ui = chat->node.ui_data;
- if (!ui)
- break;
- ui->conv.conv = conv;
- ui->conv.flags = 0;
- ui->conv.last_message = 0;
- purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation",
- ui, PURPLE_CALLBACK(conversation_deleted_update_ui_cb), ui);
- purple_signal_connect(purple_conversations_get_handle(), "wrote-chat-msg",
- ui, PURPLE_CALLBACK(written_msg_update_ui_cb), chat);
- purple_signal_connect(pidgin_conversations_get_handle(), "conversation-displayed",
- ui, PURPLE_CALLBACK(displayed_msg_update_ui_cb), chat);
- }
- break;
- default:
- break;
+ PurpleAccount *account = purple_conversation_get_account(conv);
+
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
+ GSList *buddies = purple_blist_find_buddies(account, purple_conversation_get_name(conv));
+ while (buddies) {
+ PurpleBlistNode *buddy = buddies->data;
+ struct _pidgin_blist_node *ui = purple_blist_node_get_ui_data(buddy);
+ buddies = g_slist_delete_link(buddies, buddies);
+ if (!ui)
+ continue;
+ ui->conv.conv = conv;
+ ui->conv.flags = 0;
+ ui->conv.last_message = 0;
+ purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation",
+ ui, PURPLE_CALLBACK(conversation_deleted_update_ui_cb), ui);
+ purple_signal_connect(purple_conversations_get_handle(), "wrote-im-msg",
+ ui, PURPLE_CALLBACK(written_msg_update_ui_cb), buddy);
+ purple_signal_connect(pidgin_conversations_get_handle(), "conversation-displayed",
+ ui, PURPLE_CALLBACK(displayed_msg_update_ui_cb), buddy);
+ }
+ } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
+ PurpleChat *chat = purple_blist_find_chat(account, purple_conversation_get_name(conv));
+ struct _pidgin_blist_node *ui;
+ if (!chat)
+ return;
+ ui = purple_blist_node_get_ui_data(&(chat->node));
+ if (!ui)
+ return;
+ ui->conv.conv = conv;
+ ui->conv.flags = 0;
+ ui->conv.last_message = 0;
+ purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation",
+ ui, PURPLE_CALLBACK(conversation_deleted_update_ui_cb), ui);
+ purple_signal_connect(purple_conversations_get_handle(), "wrote-chat-msg",
+ ui, PURPLE_CALLBACK(written_msg_update_ui_cb), chat);
+ purple_signal_connect(pidgin_conversations_get_handle(), "conversation-displayed",
+ ui, PURPLE_CALLBACK(displayed_msg_update_ui_cb), chat);
+ }
+}
+
+/**************************************************************************
+ * 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;
}
/**********************************************************************************
@@ -4770,8 +4954,6 @@ static void pidgin_blist_new_list(PurpleBuddyList *blist)
PidginBuddyList *gtkblist;
gtkblist = g_new0(PidginBuddyList, 1);
- gtkblist->connection_errors = g_hash_table_new_full(g_direct_hash,
- g_direct_equal, NULL, g_free);
gtkblist->priv = g_new0(PidginBuddyListPrivate, 1);
blist->ui_data = gtkblist;
@@ -4779,20 +4961,20 @@ static void pidgin_blist_new_list(PurpleBuddyList *blist)
static void pidgin_blist_new_node(PurpleBlistNode *node)
{
- node->ui_data = g_new0(struct _pidgin_blist_node, 1);
+ purple_blist_node_set_ui_data(node, g_new0(struct _pidgin_blist_node, 1));
}
gboolean pidgin_blist_node_is_contact_expanded(PurpleBlistNode *node)
{
- if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ if (PURPLE_IS_BUDDY(node)) {
node = node->parent;
if (node == NULL)
return FALSE;
}
- g_return_val_if_fail(PURPLE_BLIST_NODE_IS_CONTACT(node), FALSE);
+ g_return_val_if_fail(PURPLE_IS_CONTACT(node), FALSE);
- return ((struct _pidgin_blist_node *)node->ui_data)->contact_expanded;
+ return ((struct _pidgin_blist_node *)purple_blist_node_get_ui_data(node))->contact_expanded;
}
enum {
@@ -4804,12 +4986,6 @@ enum {
NUM_TARGETS
};
-static const char *
-item_factory_translate_func (const char *path, gpointer func_data)
-{
- return _((char *)path);
-}
-
void pidgin_blist_setup_sort_methods()
{
const char *id;
@@ -4840,7 +5016,7 @@ static void _prefs_change_redo_list(const char *name, PurplePrefType type,
gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
}
- redo_buddy_list(purple_get_blist(), FALSE, FALSE);
+ redo_buddy_list(purple_blist_get_buddy_list(), FALSE, FALSE);
gtk_tree_view_columns_autosize(GTK_TREE_VIEW(gtkblist->treeview));
if (node)
@@ -4848,7 +5024,7 @@ static void _prefs_change_redo_list(const char *name, PurplePrefType type,
struct _pidgin_blist_node *gtknode;
GtkTreePath *path;
- gtknode = node->ui_data;
+ gtknode = purple_blist_node_get_ui_data(node);
if (gtknode && gtknode->row)
{
path = gtk_tree_row_reference_get_path(gtknode->row);
@@ -4885,12 +5061,15 @@ static gboolean pidgin_blist_select_notebook_page_cb(gpointer user_data)
} else
gtk_notebook_set_current_page(GTK_NOTEBOOK(gtkblist->notebook), 0);
+ priv->select_notebook_page_timeout = 0;
return FALSE;
}
static void pidgin_blist_select_notebook_page(PidginBuddyList *gtkblist)
{
- purple_timeout_add(0, pidgin_blist_select_notebook_page_cb, gtkblist);
+ PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
+ priv->select_notebook_page_timeout = purple_timeout_add(0,
+ pidgin_blist_select_notebook_page_cb, gtkblist);
}
static void account_modified(PurpleAccount *account, PidginBuddyList *gtkblist)
@@ -4921,36 +5100,29 @@ account_status_changed(PurpleAccount *account, PurpleStatus *old,
static gboolean
gtk_blist_window_key_press_cb(GtkWidget *w, GdkEventKey *event, PidginBuddyList *gtkblist)
{
- GtkWidget *widget;
-
- if (!gtkblist)
- return FALSE;
-
/* clear any tooltips */
pidgin_blist_tooltip_destroy();
- widget = gtk_window_get_focus(GTK_WINDOW(gtkblist->window));
-
- if (GTK_IS_IMHTML(widget) || GTK_IS_ENTRY(widget)) {
- if (gtk_bindings_activate(GTK_OBJECT(widget), event->keyval, event->state))
- return TRUE;
- }
return FALSE;
}
+#if !GTK_CHECK_VERSION(3,0,0)
static gboolean
-headline_box_enter_cb(GtkWidget *widget, GdkEventCrossing *event, PidginBuddyList *gtkblist)
+headline_box_enter_cb(GtkWidget *widget, GdkEventCrossing *event,
+ PidginBuddyListPrivate *priv)
{
- gdk_window_set_cursor(widget->window, gtkblist->hand_cursor);
+ gdk_window_set_cursor(widget->window, priv->hand_cursor);
return FALSE;
}
static gboolean
-headline_box_leave_cb(GtkWidget *widget, GdkEventCrossing *event, PidginBuddyList *gtkblist)
+headline_box_leave_cb(GtkWidget *widget, GdkEventCrossing *event,
+ PidginBuddyListPrivate *priv)
{
- gdk_window_set_cursor(widget->window, gtkblist->arrow_cursor);
+ gdk_window_set_cursor(widget->window, priv->arrow_cursor);
return FALSE;
}
+#endif
static void
reset_headline(PidginBuddyList *gtkblist)
@@ -4970,17 +5142,58 @@ headline_click_callback(gpointer unused)
return FALSE;
}
+#if GTK_CHECK_VERSION(3,0,0)
+
+static gboolean
+headline_response_cb(GtkInfoBar *infobar, int resp, PidginBuddyList *gtkblist)
+{
+ gtk_widget_hide(gtkblist->headline);
+
+ if (resp == GTK_RESPONSE_OK) {
+ if (gtkblist->headline_callback)
+ g_idle_add(headline_click_callback, NULL);
+ else {
+ if (gtkblist->headline_destroy)
+ gtkblist->headline_destroy(gtkblist->headline_data);
+ reset_headline(gtkblist);
+ }
+ } else {
+ if (gtkblist->headline_destroy)
+ gtkblist->headline_destroy(gtkblist->headline_data);
+ reset_headline(gtkblist);
+ }
+
+ return FALSE;
+}
+
+static void
+headline_realize_cb(GtkWidget *widget, gpointer data)
+{
+ GdkCursor *hand_cursor = gdk_cursor_new(GDK_HAND2);
+ gdk_window_set_cursor(gtk_widget_get_window(widget), hand_cursor);
+ g_object_unref(hand_cursor);
+}
+
+static gboolean
+headline_press_cb(GtkWidget *widget, GdkEventButton *event, GtkInfoBar *infobar)
+{
+ gtk_info_bar_response(infobar, GTK_RESPONSE_OK);
+ return TRUE;
+}
+
+#else
+
static gboolean
headline_close_press_cb(GtkButton *button, PidginBuddyList *gtkblist)
{
- gtk_widget_hide(gtkblist->headline_hbox);
+ gtk_widget_hide(gtkblist->headline);
return FALSE;
}
static gboolean
headline_box_press_cb(GtkWidget *widget, GdkEventButton *event, PidginBuddyList *gtkblist)
{
- gtk_widget_hide(gtkblist->headline_hbox);
+ gtk_widget_hide(gtkblist->headline);
if (gtkblist->headline_callback)
g_idle_add(headline_click_callback, NULL);
else {
@@ -4991,6 +5204,8 @@ headline_box_press_cb(GtkWidget *widget, GdkEventButton *event, PidginBuddyList
return TRUE;
}
+#endif
+
/***********************************/
/* Connection error handling stuff */
/***********************************/
@@ -5082,10 +5297,9 @@ generic_error_enable_cb(PurpleAccount *account)
}
static void
-generic_error_destroy_cb(GtkObject *dialog,
+generic_error_destroy_cb(GtkWidget *dialog,
PurpleAccount *account)
{
- g_hash_table_remove(gtkblist->connection_errors, account);
/* If the error dialog is being destroyed in response to the
* account-error-changed signal, we don't want to clear the current
* error.
@@ -5094,7 +5308,7 @@ generic_error_destroy_cb(GtkObject *dialog,
purple_account_clear_current_error(account);
}
-#define SSL_FAQ_URI "http://d.pidgin.im/wiki/FAQssl"
+#define SSL_FAQ_URI "https://developer.pidgin.im/wiki/FAQssl"
static void
ssl_faq_clicked_cb(PidginMiniDialog *mini_dialog,
@@ -5271,8 +5485,9 @@ create_account_label(PurpleAccount *account)
GtkWidget *hbox, *label;
const char *username = purple_account_get_username(account);
char *markup;
+ char *description;
- hbox = gtk_hbox_new(FALSE, 6);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
g_object_set_data(G_OBJECT(hbox), OBJECT_DATA_KEY_ACCOUNT, account);
pack_prpl_icon_start(hbox, account);
@@ -5283,14 +5498,9 @@ create_account_label(PurpleAccount *account)
g_free(markup);
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
g_object_set(G_OBJECT(label), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-#if GTK_CHECK_VERSION(2,12,0)
- { /* avoid unused variable warnings on pre-2.12 Gtk */
- char *description =
- purple_account_get_current_error(account)->description;
- if (description != NULL && *description != '\0')
- gtk_widget_set_tooltip_text(label, description);
- }
-#endif
+ description = purple_account_get_current_error(account)->description;
+ if (description != NULL && *description != '\0')
+ gtk_widget_set_tooltip_text(label, description);
gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
return hbox;
@@ -5334,12 +5544,10 @@ static void
update_signed_on_elsewhere_tooltip(PurpleAccount *account,
const char *description)
{
-#if GTK_CHECK_VERSION(2,12,0)
PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_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);
-#endif
}
@@ -5356,12 +5564,6 @@ update_account_error_state(PurpleAccount *account,
if (old == NULL && new == NULL)
return;
- /* For backwards compatibility: */
- if (new)
- pidgin_blist_update_account_error_state(account, new->description);
- else
- pidgin_blist_update_account_error_state(account, NULL);
-
if (new != NULL)
pidgin_blist_select_notebook_page(gtkblist);
@@ -5427,18 +5629,7 @@ show_initial_account_errors(PidginBuddyList *gtkblist)
}
}
-void
-pidgin_blist_update_account_error_state(PurpleAccount *account, const char *text)
-{
- /* connection_errors isn't actually used anywhere; it's just kept in
- * sync with reality in case a plugin uses it.
- */
- if (text == NULL)
- g_hash_table_remove(gtkblist->connection_errors, account);
- else
- g_hash_table_insert(gtkblist->connection_errors, account, g_strdup(text));
-}
-
+#if !GTK_CHECK_VERSION(3,0,0)
static gboolean
paint_headline_hbox (GtkWidget *widget,
GdkEventExpose *event,
@@ -5457,6 +5648,7 @@ paint_headline_hbox (GtkWidget *widget,
widget->allocation.height - 2);
return FALSE;
}
+#endif
/* This assumes there are not things like groupless buddies or multi-leveled groups.
* I'm sure other things in this code assumes that also.
@@ -5474,32 +5666,33 @@ treeview_style_set (GtkWidget *widget,
}
}
+#if !GTK_CHECK_VERSION(3,0,0)
static void
headline_style_set (GtkWidget *widget,
GtkStyle *prev_style)
{
- GtkTooltips *tooltips;
+ PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
GtkStyle *style;
+ GtkWidget *window;
- if (gtkblist->changing_style)
+ if (priv->changing_style)
return;
- tooltips = gtk_tooltips_new ();
- g_object_ref_sink (tooltips);
+ /* This is a hack needed to use the tooltip background colour */
+ window = gtk_window_new(GTK_WINDOW_POPUP);
+ gtk_widget_set_name(window, "gtk-tooltip");
+ gtk_widget_ensure_style(window);
+ style = gtk_widget_get_style(window);
- gtk_tooltips_force_window (tooltips);
-#if GTK_CHECK_VERSION(2, 12, 0)
- gtk_widget_set_name (tooltips->tip_window, "gtk-tooltips");
-#endif
- gtk_widget_ensure_style (tooltips->tip_window);
- style = gtk_widget_get_style (tooltips->tip_window);
+ priv->changing_style = TRUE;
+ gtk_widget_set_style(gtkblist->headline, style);
+ priv->changing_style = FALSE;
- gtkblist->changing_style = TRUE;
- gtk_widget_set_style (gtkblist->headline_hbox, style);
- gtkblist->changing_style = FALSE;
+ gtk_widget_destroy(window);
- g_object_unref (tooltips);
+ gtk_widget_queue_draw(gtkblist->headline);
}
+#endif
/******************************************/
/* End of connection error handling stuff */
@@ -5521,7 +5714,7 @@ blist_focus_cb(GtkWidget *widget, GdkEventFocus *event, PidginBuddyList *gtkblis
static GtkWidget *
kiosk_page()
{
- GtkWidget *ret = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ GtkWidget *ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
GtkWidget *label;
GtkWidget *entry;
GtkWidget *bbox;
@@ -5548,7 +5741,7 @@ kiosk_page()
label = gtk_label_new(" ");
gtk_box_pack_start(GTK_BOX(ret), label, FALSE, FALSE, 0);
- bbox = gtk_hbutton_box_new();
+ bbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
button = gtk_button_new_with_mnemonic(_("_Login"));
gtk_box_pack_start(GTK_BOX(ret), bbox, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(bbox), button);
@@ -5700,12 +5893,12 @@ pidgin_blist_search_equal_func(GtkTreeModel *model, gint column,
return TRUE;
compare = NULL;
- if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ if (PURPLE_IS_CONTACT(node)) {
PurpleBuddy *b = purple_contact_get_priority_buddy(PURPLE_CONTACT(node));
- if (!purple_buddy_get_local_buddy_alias(b))
+ if (!purple_buddy_get_local_alias(b))
compare = purple_buddy_get_name(b);
- } else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
- if (!purple_buddy_get_local_buddy_alias(PURPLE_BUDDY(node)))
+ } else if (PURPLE_IS_BUDDY(node)) {
+ if (!purple_buddy_get_local_alias(PURPLE_BUDDY(node)))
compare = purple_buddy_get_name(PURPLE_BUDDY(node));
}
@@ -5729,14 +5922,24 @@ static void pidgin_blist_show(PurpleBuddyList *list)
void *handle;
GtkTreeViewColumn *column;
GtkWidget *menu;
- GtkWidget *ebox;
GtkWidget *sep;
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkWidget *infobar;
+ GtkWidget *content_area;
+#else
+ GtkWidget *ebox;
+#endif
GtkWidget *label;
GtkWidget *close;
char *pretty, *tmp;
const char *theme_name;
+ GtkActionGroup *action_group;
+ GError *error;
GtkAccelGroup *accel_group;
GtkTreeSelection *selection;
+#if ! GTK_CHECK_VERSION(3,0,0)
+ int blist_width;
+#endif
GtkTargetEntry dte[] = {{"PURPLE_BLIST_NODE", GTK_TARGET_SAME_APP, DRAG_ROW},
{"application/x-im-contact", 0, DRAG_BUDDY},
{"text/x-vcard", 0, DRAG_VCARD },
@@ -5770,9 +5973,13 @@ static void pidgin_blist_show(PurpleBuddyList *list)
G_CALLBACK(blist_focus_cb), gtkblist);
g_signal_connect(G_OBJECT(gtkblist->window), "focus-out-event",
G_CALLBACK(blist_focus_cb), gtkblist);
+
+ /* TODO: how is this done in gtk+ 3.0? */
+#if !GTK_CHECK_VERSION(3,0,0)
GTK_WINDOW(gtkblist->window)->allow_shrink = TRUE;
+#endif
- gtkblist->main_vbox = gtk_vbox_new(FALSE, 0);
+ gtkblist->main_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_show(gtkblist->main_vbox);
gtk_container_add(GTK_CONTAINER(gtkblist->window), gtkblist->main_vbox);
@@ -5784,27 +5991,44 @@ static void pidgin_blist_show(PurpleBuddyList *list)
gtk_widget_add_events(gtkblist->window, GDK_VISIBILITY_NOTIFY_MASK);
/******************************* Menu bar *************************************/
- accel_group = gtk_accel_group_new();
- gtk_window_add_accel_group(GTK_WINDOW (gtkblist->window), accel_group);
- g_object_unref(accel_group);
- gtkblist->ift = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<PurpleMain>", accel_group);
- gtk_item_factory_set_translate_func(gtkblist->ift,
- (GtkTranslateFunc)item_factory_translate_func,
- NULL, NULL);
- gtk_item_factory_create_items(gtkblist->ift, sizeof(blist_menu) / sizeof(*blist_menu),
- blist_menu, NULL);
+ action_group = gtk_action_group_new("BListActions");
+#ifdef ENABLE_NLS
+ gtk_action_group_set_translation_domain(action_group, PACKAGE);
+#endif
+ gtk_action_group_add_actions(action_group,
+ blist_menu_entries,
+ G_N_ELEMENTS(blist_menu_entries),
+ GTK_WINDOW(gtkblist->window));
+ gtk_action_group_add_toggle_actions(action_group,
+ blist_menu_toggle_entries,
+ G_N_ELEMENTS(blist_menu_toggle_entries),
+ GTK_WINDOW(gtkblist->window));
+
+ gtkblist->ui = gtk_ui_manager_new();
+ gtk_ui_manager_insert_action_group(gtkblist->ui, action_group, 0);
+
+ accel_group = gtk_ui_manager_get_accel_group(gtkblist->ui);
+ gtk_window_add_accel_group(GTK_WINDOW(gtkblist->window), accel_group);
pidgin_load_accels();
g_signal_connect(G_OBJECT(accel_group), "accel-changed", G_CALLBACK(pidgin_save_accels_cb), NULL);
- menu = gtk_item_factory_get_widget(gtkblist->ift, "<PurpleMain>");
+ error = NULL;
+ if (!gtk_ui_manager_add_ui_from_string(gtkblist->ui, blist_menu, -1, &error))
+ {
+ g_message("building menus failed: %s", error->message);
+ g_error_free(error);
+ exit(EXIT_FAILURE);
+ }
+
+ menu = gtk_ui_manager_get_widget(gtkblist->ui, "/BList");
gtkblist->menutray = pidgin_menu_tray_new();
gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtkblist->menutray);
gtk_widget_show(gtkblist->menutray);
gtk_widget_show(menu);
gtk_box_pack_start(GTK_BOX(gtkblist->main_vbox), menu, FALSE, FALSE, 0);
- accountmenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Accounts"));
-
+ menu = gtk_ui_manager_get_widget(gtkblist->ui, "/BList/AccountsMenu");
+ accountmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu));
/****************************** Notebook *************************************/
gtkblist->notebook = gtk_notebook_new();
@@ -5826,59 +6050,96 @@ static void pidgin_blist_show(PurpleBuddyList *list)
pretty = pidgin_make_pretty_arrows(tmp);
g_free(tmp);
label = gtk_label_new(NULL);
- gtk_widget_set_size_request(label, purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/width") - 12, -1);
gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
gtk_misc_set_alignment(GTK_MISC(label), 0.5, 0.2);
gtk_label_set_markup(GTK_LABEL(label), pretty);
g_free(pretty);
gtk_notebook_append_page(GTK_NOTEBOOK(gtkblist->notebook),label, NULL);
- gtkblist->vbox = gtk_vbox_new(FALSE, 0);
+ gtkblist->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_notebook_append_page(GTK_NOTEBOOK(gtkblist->notebook), gtkblist->vbox, NULL);
gtk_widget_show_all(gtkblist->notebook);
pidgin_blist_select_notebook_page(gtkblist);
+ /****************************** Headline **********************************/
+#if GTK_CHECK_VERSION(3,0,0)
+
+ gtkblist->headline = gtk_event_box_new();
+ gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->headline,
+ FALSE, FALSE, 0);
+ infobar = gtk_info_bar_new();
+ gtk_container_add(GTK_CONTAINER(gtkblist->headline), infobar);
+ gtk_info_bar_set_default_response(GTK_INFO_BAR(infobar), GTK_RESPONSE_OK);
+ gtk_info_bar_set_message_type(GTK_INFO_BAR(infobar), GTK_MESSAGE_INFO);
+
+ content_area = gtk_info_bar_get_content_area(GTK_INFO_BAR(infobar));
+ gtkblist->headline_image = gtk_image_new_from_pixbuf(NULL);
+ gtk_misc_set_alignment(GTK_MISC(gtkblist->headline_image), 0.5, 0.5);
+ gtkblist->headline_label = gtk_label_new(NULL);
+ gtk_label_set_line_wrap(GTK_LABEL(gtkblist->headline_label), TRUE);
+ gtk_box_pack_start(GTK_BOX(content_area), gtkblist->headline_image,
+ FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(content_area), gtkblist->headline_label,
+ TRUE, TRUE, 0);
+
+ close = gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
+ close = pidgin_create_small_button(close);
+ gtk_widget_set_tooltip_text(close, _("Close"));
+ gtk_info_bar_add_action_widget(GTK_INFO_BAR(infobar), close,
+ GTK_RESPONSE_CLOSE);
+
+ g_signal_connect(infobar, "response", G_CALLBACK(headline_response_cb),
+ gtkblist);
+ g_signal_connect(infobar, "close", G_CALLBACK(gtk_info_bar_response),
+ GINT_TO_POINTER(GTK_RESPONSE_CLOSE));
+ g_signal_connect(gtkblist->headline, "realize",
+ G_CALLBACK(headline_realize_cb), NULL);
+ g_signal_connect(gtkblist->headline, "button-press-event",
+ G_CALLBACK(headline_press_cb), infobar);
+
+#else
+
ebox = gtk_event_box_new();
gtk_box_pack_start(GTK_BOX(gtkblist->vbox), ebox, FALSE, FALSE, 0);
- gtkblist->headline_hbox = gtk_hbox_new(FALSE, 3);
- gtk_container_set_border_width(GTK_CONTAINER(gtkblist->headline_hbox), 6);
- gtk_container_add(GTK_CONTAINER(ebox), gtkblist->headline_hbox);
+ gtkblist->headline = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(gtkblist->headline), 6);
+ gtk_container_add(GTK_CONTAINER(ebox), gtkblist->headline);
gtkblist->headline_image = gtk_image_new_from_pixbuf(NULL);
gtk_misc_set_alignment(GTK_MISC(gtkblist->headline_image), 0.0, 0);
gtkblist->headline_label = gtk_label_new(NULL);
- gtk_widget_set_size_request(gtkblist->headline_label,
- purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/width")-25,-1);
+ blist_width = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/width") - 25;
+ if (blist_width < -1)
+ blist_width = -1;
+ gtk_widget_set_size_request(gtkblist->headline_label, blist_width, -1);
gtk_label_set_line_wrap(GTK_LABEL(gtkblist->headline_label), TRUE);
- gtk_box_pack_start(GTK_BOX(gtkblist->headline_hbox), gtkblist->headline_image, FALSE, FALSE, 0);
- gtk_box_pack_start(GTK_BOX(gtkblist->headline_hbox), gtkblist->headline_label, TRUE, TRUE, 0);
- g_signal_connect(gtkblist->headline_label, /* connecting on headline_hbox doesn't work, because
+ gtk_box_pack_start(GTK_BOX(gtkblist->headline), gtkblist->headline_image, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(gtkblist->headline), gtkblist->headline_label, TRUE, TRUE, 0);
+ g_signal_connect(gtkblist->headline_label, /* connecting on headline doesn't work, because
the signal is not emitted when theme is changed */
"style-set",
G_CALLBACK(headline_style_set),
NULL);
- g_signal_connect (gtkblist->headline_hbox,
+ g_signal_connect (gtkblist->headline,
"expose_event",
G_CALLBACK (paint_headline_hbox),
NULL);
- gtk_widget_set_name(gtkblist->headline_hbox, "gtk-tooltips");
+ gtk_widget_set_name(gtkblist->headline, "gtk-tooltips");
- gtkblist->headline_close = gtk_widget_render_icon(ebox, GTK_STOCK_CLOSE,
- gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC), NULL);
- gtkblist->hand_cursor = gdk_cursor_new (GDK_HAND2);
- gtkblist->arrow_cursor = gdk_cursor_new (GDK_LEFT_PTR);
+ priv->hand_cursor = gdk_cursor_new (GDK_HAND2);
+ priv->arrow_cursor = gdk_cursor_new (GDK_LEFT_PTR);
/* Close button. */
close = gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
close = pidgin_create_small_button(close);
- gtk_box_pack_start(GTK_BOX(gtkblist->headline_hbox), close, FALSE, FALSE, 0);
-#if GTK_CHECK_VERSION(2,12,0)
+ gtk_box_pack_start(GTK_BOX(gtkblist->headline), close, FALSE, FALSE, 0);
gtk_widget_set_tooltip_text(close, _("Close"));
-#endif
g_signal_connect(close, "clicked", G_CALLBACK(headline_close_press_cb), gtkblist);
- g_signal_connect(G_OBJECT(ebox), "enter-notify-event", G_CALLBACK(headline_box_enter_cb), gtkblist);
- g_signal_connect(G_OBJECT(ebox), "leave-notify-event", G_CALLBACK(headline_box_leave_cb), gtkblist);
+ g_signal_connect(G_OBJECT(ebox), "enter-notify-event", G_CALLBACK(headline_box_enter_cb), priv);
+ g_signal_connect(G_OBJECT(ebox), "leave-notify-event", G_CALLBACK(headline_box_leave_cb), priv);
g_signal_connect(G_OBJECT(ebox), "button-press-event", G_CALLBACK(headline_box_press_cb), gtkblist);
+#endif
+
/****************************** GtkTreeView **********************************/
gtkblist->treemodel = gtk_tree_store_new(BLIST_COLUMNS,
GDK_TYPE_PIXBUF, /* Status icon */
@@ -5963,55 +6224,48 @@ static void pidgin_blist_show(PurpleBuddyList *list)
pidgin_make_scrollable(gtkblist->treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_NONE, -1, -1),
TRUE, TRUE, 0);
- sep = gtk_hseparator_new();
+ sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
gtk_box_pack_start(GTK_BOX(gtkblist->vbox), sep, FALSE, FALSE, 0);
gtkblist->scrollbook = pidgin_scroll_book_new();
gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->scrollbook, FALSE, FALSE, 0);
- /* Create an vbox which holds the scrollbook which is actually used to
- * display connection errors. The vbox needs to still exist for
- * backwards compatibility.
- */
- gtkblist->error_buttons = gtk_vbox_new(FALSE, 0);
- gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->error_buttons, FALSE, FALSE, 0);
- gtk_container_set_border_width(GTK_CONTAINER(gtkblist->error_buttons), 0);
-
priv->error_scrollbook = PIDGIN_SCROLL_BOOK(pidgin_scroll_book_new());
- gtk_box_pack_start(GTK_BOX(gtkblist->error_buttons),
+ gtk_box_pack_start(GTK_BOX(gtkblist->vbox),
GTK_WIDGET(priv->error_scrollbook), FALSE, FALSE, 0);
-
/* Add the statusbox */
gtkblist->statusbox = pidgin_status_box_new();
gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->statusbox, FALSE, TRUE, 0);
gtk_widget_set_name(gtkblist->statusbox, "pidgin_blist_statusbox");
+#if !GTK_CHECK_VERSION(3,0,0)
gtk_container_set_border_width(GTK_CONTAINER(gtkblist->statusbox), 3);
+#endif
gtk_widget_show(gtkblist->statusbox);
/* set the Show Offline Buddies option. must be done
* after the treeview or faceprint gets mad. -Robot101
*/
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show/Offline Buddies"))),
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(gtk_ui_manager_get_action(gtkblist->ui, "/BList/BuddiesMenu/ShowMenu/ShowOffline")),
purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_offline_buddies"));
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show/Empty Groups"))),
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(gtk_ui_manager_get_action(gtkblist->ui, "/BList/BuddiesMenu/ShowMenu/ShowEmptyGroups")),
purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_empty_groups"));
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Tools/Mute Sounds"))),
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(gtk_ui_manager_get_action(gtkblist->ui, "/BList/ToolsMenu/MuteSounds")),
purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/sound/mute"));
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show/Buddy Details"))),
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(gtk_ui_manager_get_action(gtkblist->ui, "/BList/BuddiesMenu/ShowMenu/ShowBuddyDetails")),
purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"));
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show/Idle Times"))),
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(gtk_ui_manager_get_action(gtkblist->ui, "/BList/BuddiesMenu/ShowMenu/ShowIdleTimes")),
purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time"));
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show/Protocol Icons"))),
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(gtk_ui_manager_get_action(gtkblist->ui, "/BList/BuddiesMenu/ShowMenu/ShowProtocolIcons")),
purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"));
if(!strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method"), "none"))
- gtk_widget_set_sensitive(gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Mute Sounds")), FALSE);
+ gtk_action_set_sensitive(gtk_ui_manager_get_action(gtkblist->ui, "/BList/ToolsMenu/MuteSounds"), FALSE);
/* Update some dynamic things */
update_menu_bar(gtkblist);
@@ -6070,7 +6324,7 @@ static void pidgin_blist_show(PurpleBuddyList *list)
purple_signal_connect(handle, "account-actions-changed", gtkblist,
PURPLE_CALLBACK(account_actions_changed), NULL);
- handle = pidgin_account_get_handle();
+ handle = pidgin_accounts_get_handle();
purple_signal_connect(handle, "account-modified", gtkblist,
PURPLE_CALLBACK(account_modified), gtkblist);
@@ -6097,7 +6351,7 @@ static void pidgin_blist_show(PurpleBuddyList *list)
PURPLE_CALLBACK(conversation_created_cb),
gtkblist);
- gtk_widget_hide(gtkblist->headline_hbox);
+ gtk_widget_hide(gtkblist->headline);
show_initial_account_errors(gtkblist);
@@ -6121,14 +6375,14 @@ static void redo_buddy_list(PurpleBuddyList *list, gboolean remove, gboolean rer
/* This is only needed when we're reverting to a non-GTK+ sorted
* status. We shouldn't need to remove otherwise.
*/
- if (remove && !PURPLE_BLIST_NODE_IS_GROUP(node))
+ if (remove && !PURPLE_IS_GROUP(node))
pidgin_blist_hide_node(list, node, FALSE);
- if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if (PURPLE_IS_BUDDY(node))
pidgin_blist_update_buddy(list, node, rerender);
- else if (PURPLE_BLIST_NODE_IS_CHAT(node))
+ else if (PURPLE_IS_CHAT(node))
pidgin_blist_update(list, node);
- else if (PURPLE_BLIST_NODE_IS_GROUP(node))
+ else if (PURPLE_IS_GROUP(node))
pidgin_blist_update(list, node);
node = purple_blist_node_next(node, FALSE);
}
@@ -6146,14 +6400,14 @@ pidgin_blist_update_refresh_timeout()
PurpleBuddyList *blist;
PidginBuddyList *gtkblist;
- blist = purple_get_blist();
- gtkblist = PIDGIN_BLIST(purple_get_blist());
+ blist = purple_blist_get_buddy_list();
+ gtkblist = PIDGIN_BLIST(purple_blist_get_buddy_list());
gtkblist->refresh_timer = purple_timeout_add_seconds(30,(GSourceFunc)pidgin_blist_refresh_timer, blist);
}
static gboolean get_iter_from_node(PurpleBlistNode *node, GtkTreeIter *iter) {
- struct _pidgin_blist_node *gtknode = (struct _pidgin_blist_node *)node->ui_data;
+ struct _pidgin_blist_node *gtknode = purple_blist_node_get_ui_data(node);
GtkTreePath *path;
if (!gtknode) {
@@ -6182,7 +6436,7 @@ static gboolean get_iter_from_node(PurpleBlistNode *node, GtkTreeIter *iter) {
static void pidgin_blist_remove(PurpleBuddyList *list, PurpleBlistNode *node)
{
- struct _pidgin_blist_node *gtknode = node->ui_data;
+ struct _pidgin_blist_node *gtknode = purple_blist_node_get_ui_data(node);
purple_request_close_with_handle(node);
@@ -6204,9 +6458,9 @@ static void pidgin_blist_remove(PurpleBuddyList *list, PurpleBlistNode *node)
if(gtknode->recent_signonoff_timer > 0)
purple_timeout_remove(gtknode->recent_signonoff_timer);
- purple_signals_disconnect_by_handle(node->ui_data);
- g_free(node->ui_data);
- node->ui_data = NULL;
+ purple_signals_disconnect_by_handle(gtknode);
+ g_free(gtknode);
+ purple_blist_node_set_ui_data(node, NULL);
}
}
@@ -6241,7 +6495,7 @@ static void pidgin_blist_selection_changed(GtkTreeSelection *selection, gpointer
* but we don't do it for groups, because it causes total bizarness -
* the previously selected buddy node might rendered at half height.
*/
- if ((new_selection != NULL) && PURPLE_BLIST_NODE_IS_GROUP(new_selection)) {
+ if ((new_selection != NULL) && PURPLE_IS_GROUP(new_selection)) {
do_selection_changed(new_selection);
} else {
g_timeout_add(0, (GSourceFunc)do_selection_changed, new_selection);
@@ -6250,8 +6504,8 @@ static void pidgin_blist_selection_changed(GtkTreeSelection *selection, gpointer
static gboolean insert_node(PurpleBuddyList *list, PurpleBlistNode *node, GtkTreeIter *iter)
{
- GtkTreeIter parent_iter, cur, *curptr = NULL;
- struct _pidgin_blist_node *gtknode = node->ui_data;
+ GtkTreeIter parent_iter = {0, NULL, NULL, NULL}, cur, *curptr = NULL;
+ struct _pidgin_blist_node *gtknode = purple_blist_node_get_ui_data(node);
GtkTreePath *newpath;
if(!iter)
@@ -6266,7 +6520,7 @@ static gboolean insert_node(PurpleBuddyList *list, PurpleBlistNode *node, GtkTre
if(get_iter_from_node(node, &cur))
curptr = &cur;
- if(PURPLE_BLIST_NODE_IS_CONTACT(node) || PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ if(PURPLE_IS_CONTACT(node) || PURPLE_IS_CHAT(node)) {
current_sort_method->func(node, list, parent_iter, curptr, iter);
} else {
sort_method_none(node, list, parent_iter, curptr, iter);
@@ -6276,7 +6530,7 @@ static gboolean insert_node(PurpleBuddyList *list, PurpleBlistNode *node, GtkTre
gtk_tree_row_reference_free(gtknode->row);
} else {
pidgin_blist_new_node(node);
- gtknode = (struct _pidgin_blist_node *)node->ui_data;
+ gtknode = purple_blist_node_get_ui_data(node);
}
newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel),
@@ -6294,12 +6548,12 @@ static gboolean insert_node(PurpleBuddyList *list, PurpleBlistNode *node, GtkTre
if(node->parent) {
GtkTreePath *expand = NULL;
- struct _pidgin_blist_node *gtkparentnode = node->parent->ui_data;
+ struct _pidgin_blist_node *gtkparentnode = purple_blist_node_get_ui_data(node->parent);
- if(PURPLE_BLIST_NODE_IS_GROUP(node->parent)) {
+ if(PURPLE_IS_GROUP(node->parent)) {
if(!purple_blist_node_get_bool(node->parent, "collapsed"))
expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &parent_iter);
- } else if(PURPLE_BLIST_NODE_IS_CONTACT(node->parent) &&
+ } else if(PURPLE_IS_CONTACT(node->parent) &&
gtkparentnode->contact_expanded) {
expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &parent_iter);
}
@@ -6316,12 +6570,12 @@ static gboolean pidgin_blist_group_has_show_offline_buddy(PurpleGroup *group)
{
PurpleBlistNode *gnode, *cnode, *bnode;
- gnode = (PurpleBlistNode *)group;
+ gnode = PURPLE_BLIST_NODE(group);
for(cnode = gnode->child; cnode; cnode = cnode->next) {
- if(PURPLE_BLIST_NODE_IS_CONTACT(cnode)) {
+ if(PURPLE_IS_CONTACT(cnode)) {
for(bnode = cnode->child; bnode; bnode = bnode->next) {
PurpleBuddy *buddy = (PurpleBuddy *)bnode;
- if (purple_account_is_connected(buddy->account) &&
+ if (purple_account_is_connected(purple_buddy_get_account(buddy)) &&
purple_blist_node_get_bool(bnode, "show_offline"))
return TRUE;
}
@@ -6346,11 +6600,11 @@ static void pidgin_blist_update_group(PurpleBuddyList *list,
if (editing_blist)
return;
- if (PURPLE_BLIST_NODE_IS_GROUP(node))
+ if (PURPLE_IS_GROUP(node))
gnode = node;
- else if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+ else if (PURPLE_IS_BUDDY(node))
gnode = node->parent->parent;
- else if (PURPLE_BLIST_NODE_IS_CONTACT(node) || PURPLE_BLIST_NODE_IS_CHAT(node))
+ else if (PURPLE_IS_CONTACT(node) || PURPLE_IS_CHAT(node))
gnode = node->parent;
else
return;
@@ -6360,13 +6614,13 @@ static void pidgin_blist_update_group(PurpleBuddyList *list,
show_offline = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_offline_buddies");
if(show_offline)
- count = purple_blist_get_group_size(group, FALSE);
+ count = purple_counting_node_get_current_size(PURPLE_COUNTING_NODE(group));
else
- count = purple_blist_get_group_online_count(group);
+ count = purple_counting_node_get_online_count(PURPLE_COUNTING_NODE(group));
if (count > 0 || purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_empty_groups"))
show = TRUE;
- else if (PURPLE_BLIST_NODE_IS_BUDDY(node) && buddy_is_displayable((PurpleBuddy*)node)) { /* Or chat? */
+ else if (PURPLE_IS_BUDDY(node) && buddy_is_displayable((PurpleBuddy*)node)) { /* Or chat? */
show = TRUE;
} else if (!show_offline) {
show = pidgin_blist_group_has_show_offline_buddy(group);
@@ -6445,8 +6699,8 @@ static char *pidgin_get_group_title(PurpleBlistNode *gnode, gboolean expanded)
if (!expanded) {
g_snprintf(group_count, sizeof(group_count), "%d/%d",
- purple_blist_get_group_online_count(group),
- purple_blist_get_group_size(group, FALSE));
+ purple_counting_node_get_online_count(PURPLE_COUNTING_NODE(group)),
+ purple_counting_node_get_current_size(PURPLE_COUNTING_NODE(group)));
}
theme = pidgin_blist_get_theme();
@@ -6461,7 +6715,7 @@ static char *pidgin_get_group_title(PurpleBlistNode *gnode, gboolean expanded)
text_color = selected ? NULL : theme_font_get_color_default(pair, NULL);
text_font = theme_font_get_face_default(pair, "");
- esc = g_markup_escape_text(group->name, -1);
+ esc = g_markup_escape_text(purple_group_get_name(group), -1);
if (text_color) {
mark = g_strdup_printf("<span foreground='%s' font_desc='%s'><b>%s</b>%s%s%s</span>",
text_color, text_font,
@@ -6488,7 +6742,7 @@ static void buddy_node(PurpleBuddy *buddy, GtkTreeIter *iter, PurpleBlistNode *n
GdkColor *color = NULL;
char *mark;
char *idle = NULL;
- gboolean expanded = ((struct _pidgin_blist_node *)(node->parent->ui_data))->contact_expanded;
+ gboolean expanded = ((struct _pidgin_blist_node *)purple_blist_node_get_ui_data(node->parent))->contact_expanded;
gboolean selected = (gtkblist->selected_node == node);
gboolean biglist = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons");
PidginBlistTheme *theme;
@@ -6496,12 +6750,12 @@ static void buddy_node(PurpleBuddy *buddy, GtkTreeIter *iter, PurpleBlistNode *n
if (editing_blist)
return;
- status = pidgin_blist_get_status_icon((PurpleBlistNode*)buddy,
+ status = pidgin_blist_get_status_icon(PURPLE_BLIST_NODE(buddy),
biglist ? PIDGIN_STATUS_ICON_LARGE : PIDGIN_STATUS_ICON_SMALL);
/* Speed it up if we don't want buddy icons. */
if(biglist)
- avatar = pidgin_blist_get_buddy_icon((PurpleBlistNode *)buddy, TRUE, TRUE);
+ avatar = pidgin_blist_get_buddy_icon(PURPLE_BLIST_NODE(buddy), TRUE, TRUE);
else
avatar = NULL;
@@ -6512,7 +6766,7 @@ static void buddy_node(PurpleBuddy *buddy, GtkTreeIter *iter, PurpleBlistNode *n
do_alphashift(avatar, 77);
}
- emblem = pidgin_blist_get_emblem((PurpleBlistNode*) buddy);
+ emblem = pidgin_blist_get_emblem(PURPLE_BLIST_NODE(buddy));
mark = pidgin_blist_get_name_markup(buddy, selected, TRUE);
theme = pidgin_blist_get_theme();
@@ -6553,7 +6807,7 @@ static void buddy_node(PurpleBuddy *buddy, GtkTreeIter *iter, PurpleBlistNode *n
}
}
- prpl_icon = pidgin_create_prpl_icon(buddy->account, PIDGIN_PRPL_ICON_SMALL);
+ prpl_icon = pidgin_create_prpl_icon(purple_buddy_get_account(buddy), PIDGIN_PRPL_ICON_SMALL);
if (theme != NULL)
color = pidgin_blist_theme_get_contact_color(theme);
@@ -6601,15 +6855,15 @@ static void pidgin_blist_update_contact(PurpleBuddyList *list, PurpleBlistNode *
if (editing_blist)
return;
- if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if (PURPLE_IS_BUDDY(node))
cnode = node->parent;
else
cnode = node;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CONTACT(cnode));
+ g_return_if_fail(PURPLE_IS_CONTACT(cnode));
/* First things first, update the group */
- if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if (PURPLE_IS_BUDDY(node))
pidgin_blist_update_group(list, node);
else
pidgin_blist_update_group(list, cnode->parent);
@@ -6624,7 +6878,7 @@ static void pidgin_blist_update_contact(PurpleBuddyList *list, PurpleBlistNode *
if(!insert_node(list, cnode, &iter))
return;
- gtknode = (struct _pidgin_blist_node *)cnode->ui_data;
+ gtknode = purple_blist_node_get_ui_data(cnode);
if(gtknode->contact_expanded) {
GdkPixbuf *status;
@@ -6691,7 +6945,7 @@ static void pidgin_blist_update_buddy(PurpleBuddyList *list, PurpleBlistNode *no
PurpleBuddy *buddy;
struct _pidgin_blist_node *gtkparentnode;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(PURPLE_IS_BUDDY(node));
if (node->parent == NULL)
return;
@@ -6701,7 +6955,7 @@ static void pidgin_blist_update_buddy(PurpleBuddyList *list, PurpleBlistNode *no
/* First things first, update the contact */
pidgin_blist_update_contact(list, node);
- gtkparentnode = (struct _pidgin_blist_node *)node->parent->ui_data;
+ gtkparentnode = purple_blist_node_get_ui_data(node->parent);
if (gtkparentnode->contact_expanded && buddy_is_displayable(buddy))
{
@@ -6722,7 +6976,7 @@ static void pidgin_blist_update_chat(PurpleBuddyList *list, PurpleBlistNode *nod
{
PurpleChat *chat;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+ g_return_if_fail(PURPLE_IS_CHAT(node));
if (editing_blist)
return;
@@ -6732,7 +6986,7 @@ static void pidgin_blist_update_chat(PurpleBuddyList *list, PurpleBlistNode *nod
chat = (PurpleChat*)node;
- if(purple_account_is_connected(chat->account)) {
+ if(purple_account_is_connected(purple_chat_get_account(chat))) {
GtkTreeIter iter;
GdkPixbuf *status, *avatar, *emblem, *prpl_icon;
const gchar *color, *font;
@@ -6751,7 +7005,7 @@ static void pidgin_blist_update_chat(PurpleBuddyList *list, PurpleBlistNode *nod
if (!insert_node(list, node, &iter))
return;
- ui = node->ui_data;
+ ui = purple_blist_node_get_ui_data(node);
conv = ui->conv.conv;
if (conv && pidgin_conv_is_hidden(PIDGIN_CONVERSATION(conv))) {
hidden = (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE);
@@ -6796,7 +7050,7 @@ static void pidgin_blist_update_chat(PurpleBuddyList *list, PurpleBlistNode *nod
g_free(mark);
mark = tmp;
- prpl_icon = pidgin_create_prpl_icon(chat->account, PIDGIN_PRPL_ICON_SMALL);
+ prpl_icon = pidgin_create_prpl_icon(purple_chat_get_account(chat), PIDGIN_PRPL_ICON_SMALL);
if (theme != NULL)
bgcolor = pidgin_blist_theme_get_contact_color(theme);
@@ -6837,26 +7091,17 @@ static void pidgin_blist_update(PurpleBuddyList *list, PurpleBlistNode *node)
if(!gtkblist || !gtkblist->treeview || !node)
return;
- if (node->ui_data == NULL)
+ if (purple_blist_node_get_ui_data(node) == NULL)
pidgin_blist_new_node(node);
- switch(node->type) {
- case PURPLE_BLIST_GROUP_NODE:
- pidgin_blist_update_group(list, node);
- break;
- case PURPLE_BLIST_CONTACT_NODE:
- pidgin_blist_update_contact(list, node);
- break;
- case PURPLE_BLIST_BUDDY_NODE:
- pidgin_blist_update_buddy(list, node, TRUE);
- break;
- case PURPLE_BLIST_CHAT_NODE:
- pidgin_blist_update_chat(list, node);
- break;
- case PURPLE_BLIST_OTHER_NODE:
- return;
- }
-
+ if (PURPLE_IS_GROUP(node))
+ pidgin_blist_update_group(list, node);
+ else if (PURPLE_IS_CONTACT(node))
+ pidgin_blist_update_contact(list, node);
+ else if (PURPLE_IS_BUDDY(node))
+ pidgin_blist_update_buddy(list, node, TRUE);
+ else if (PURPLE_IS_CHAT(node))
+ pidgin_blist_update_chat(list, node);
}
static void pidgin_blist_destroy(PurpleBuddyList *list)
@@ -6870,9 +7115,6 @@ static void pidgin_blist_destroy(PurpleBuddyList *list)
purple_signals_disconnect_by_handle(gtkblist);
- if (gtkblist->headline_close)
- g_object_unref(gtkblist->headline_close);
-
gtk_widget_destroy(gtkblist->window);
pidgin_blist_tooltip_destroy();
@@ -6884,24 +7126,28 @@ static void pidgin_blist_destroy(PurpleBuddyList *list)
if (gtkblist->drag_timeout)
g_source_remove(gtkblist->drag_timeout);
- g_hash_table_destroy(gtkblist->connection_errors);
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->ift));
+ g_object_unref(G_OBJECT(gtkblist->ui));
g_object_unref(G_OBJECT(gtkblist->empty_avatar));
- gdk_cursor_unref(gtkblist->hand_cursor);
- gdk_cursor_unref(gtkblist->arrow_cursor);
- gtkblist->hand_cursor = NULL;
- gtkblist->arrow_cursor = NULL;
-
priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
+
+#if !GTK_CHECK_VERSION(3,0,0)
+ gdk_cursor_unref(priv->hand_cursor);
+ gdk_cursor_unref(priv->arrow_cursor);
+ priv->hand_cursor = NULL;
+ priv->arrow_cursor = NULL;
+#endif
+
if (priv->current_theme)
g_object_unref(priv->current_theme);
+ if (priv->select_notebook_page_timeout)
+ purple_timeout_remove(priv->select_notebook_page_timeout);
g_free(priv);
g_free(gtkblist);
@@ -6916,7 +7162,8 @@ static void pidgin_blist_set_visible(PurpleBuddyList *list, gboolean show)
return;
if (show) {
- if(!PIDGIN_WINDOW_ICONIFIED(gtkblist->window) && !GTK_WIDGET_VISIBLE(gtkblist->window))
+ if(!PIDGIN_WINDOW_ICONIFIED(gtkblist->window) &&
+ !gtk_widget_get_visible(gtkblist->window))
purple_signal_emit(pidgin_blist_get_handle(), "gtkblist-unhiding", gtkblist);
pidgin_blist_restore_position();
gtk_window_present(GTK_WINDOW(gtkblist->window));
@@ -6925,7 +7172,7 @@ static void pidgin_blist_set_visible(PurpleBuddyList *list, gboolean show)
purple_signal_emit(pidgin_blist_get_handle(), "gtkblist-hiding", gtkblist);
gtk_widget_hide(gtkblist->window);
} else {
- if (!GTK_WIDGET_VISIBLE(gtkblist->window))
+ if (!gtk_widget_get_visible(gtkblist->window))
gtk_widget_show(gtkblist->window);
gtk_window_iconify(GTK_WINDOW(gtkblist->window));
}
@@ -6936,28 +7183,27 @@ static GList *
groups_tree(void)
{
static GList *list = NULL;
- char *tmp2;
PurpleGroup *g;
PurpleBlistNode *gnode;
g_list_free(list);
list = NULL;
- if (purple_get_blist()->root == NULL)
+ if (purple_blist_get_buddy_list()->root == NULL)
{
- list = g_list_append(list, (gpointer)_("Buddies"));
+ list = g_list_append(list,
+ (gpointer)PURPLE_BLIST_DEFAULT_GROUP_NAME);
}
else
{
- for (gnode = purple_get_blist()->root;
+ for (gnode = purple_blist_get_buddy_list()->root;
gnode != NULL;
gnode = gnode->next)
{
- if (PURPLE_BLIST_NODE_IS_GROUP(gnode))
+ if (PURPLE_IS_GROUP(gnode))
{
g = (PurpleGroup *)gnode;
- tmp2 = g->name;
- list = g_list_append(list, tmp2);
+ list = g_list_append(list, (char *) purple_group_get_name(g));
}
}
}
@@ -6987,6 +7233,7 @@ add_buddy_select_account_cb(GObject *w, PurpleAccount *account,
invite_enabled = FALSE;
gtk_widget_set_sensitive(data->entry_for_invite, invite_enabled);
+ set_sensitive_if_input_buddy_cb(data->entry, data);
}
static void
@@ -7002,7 +7249,7 @@ add_buddy_cb(GtkWidget *w, int resp, PidginAddBuddyData *data)
PurpleAccount *account;
PurpleGroup *g;
PurpleBuddy *b;
- PurpleConversation *c;
+ PurpleIMConversation *im;
PurpleBuddyIcon *icon;
if (resp == GTK_RESPONSE_OK)
@@ -7021,15 +7268,15 @@ add_buddy_cb(GtkWidget *w, int resp, PidginAddBuddyData *data)
g = NULL;
if ((grp != NULL) && (*grp != '\0'))
{
- if ((g = purple_find_group(grp)) == NULL)
+ if ((g = purple_blist_find_group(grp)) == NULL)
{
g = purple_group_new(grp);
purple_blist_add_group(g, NULL);
}
- b = purple_find_buddy_in_group(account, who, g);
+ b = purple_blist_find_buddy_in_group(account, who, g);
}
- else if ((b = purple_find_buddy(account, who)) != NULL)
+ else if ((b = purple_blist_find_buddy(account, who)) != NULL)
{
g = purple_buddy_get_group(b);
}
@@ -7040,11 +7287,11 @@ add_buddy_cb(GtkWidget *w, int resp, PidginAddBuddyData *data)
purple_blist_add_buddy(b, NULL, g, NULL);
}
- purple_account_add_buddy_with_invite(account, b, invite);
+ purple_account_add_buddy(account, b, invite);
/* Offer to merge people with the same alias. */
if (whoalias != NULL && g != NULL)
- gtk_blist_auto_personize((PurpleBlistNode *)g, whoalias);
+ gtk_blist_auto_personize(PURPLE_BLIST_NODE(g), whoalias);
/*
* XXX
@@ -7061,9 +7308,9 @@ add_buddy_cb(GtkWidget *w, int resp, PidginAddBuddyData *data)
* Or something. --Mark
*/
- c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, data->rq_data.account);
- if (c != NULL) {
- icon = purple_conv_im_get_icon(PURPLE_CONV_IM(c));
+ im = purple_conversations_find_im_with_account(who, data->rq_data.account);
+ if (im != NULL) {
+ icon = purple_im_conversation_get_icon(im);
if (icon != NULL)
purple_buddy_icon_update(icon);
}
@@ -7112,8 +7359,8 @@ pidgin_blist_request_add_buddy(PurpleAccount *account, const char *username,
gtk_entry_set_activates_default (GTK_ENTRY(data->entry), TRUE);
g_signal_connect(G_OBJECT(data->entry), "changed",
- G_CALLBACK(pidgin_set_sensitive_if_input),
- data->rq_data.window);
+ G_CALLBACK(set_sensitive_if_input_buddy_cb),
+ data);
data->entry_for_alias = gtk_entry_new();
pidgin_add_widget_to_vbox(data->rq_data.vbox, _("(Optional) A_lias:"),
@@ -7183,7 +7430,7 @@ add_chat_cb(GtkWidget *w, PidginAddChatData *data)
group = NULL;
if ((group_name != NULL) && (*group_name != '\0') &&
- ((group = purple_find_group(group_name)) == NULL))
+ ((group = purple_blist_find_group(group_name)) == NULL))
{
group = purple_group_new(group_name);
purple_blist_add_group(group, NULL);
@@ -7192,10 +7439,10 @@ add_chat_cb(GtkWidget *w, PidginAddChatData *data)
purple_blist_add_chat(chat, group, NULL);
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->autojoin)))
- purple_blist_node_set_bool((PurpleBlistNode*)chat, "gtk-autojoin", TRUE);
+ purple_blist_node_set_bool(PURPLE_BLIST_NODE(chat), "gtk-autojoin", TRUE);
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->persistent)))
- purple_blist_node_set_bool((PurpleBlistNode*)chat, "gtk-persistent", TRUE);
+ purple_blist_node_set_bool(PURPLE_BLIST_NODE(chat), "gtk-persistent", TRUE);
}
gtk_widget_destroy(data->chat_data.rq_data.window);
@@ -7232,20 +7479,25 @@ pidgin_blist_request_add_chat(PurpleAccount *account, PurpleGroup *group,
GList *l;
PurpleConnection *gc;
GtkBox *vbox;
+ PurplePluginProtocolInfo *prpl_info = NULL;
if (account != NULL) {
gc = purple_account_get_connection(account);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
- if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->join_chat == NULL) {
- purple_notify_error(gc, NULL, _("This protocol does not support chat rooms."), NULL);
+ if (prpl_info->join_chat == NULL) {
+ purple_notify_error(gc, NULL, _("This protocol does not"
+ " support chat rooms."), NULL,
+ purple_request_cpar_from_account(account));
return;
}
} else {
/* Find an account with chat capabilities */
for (l = purple_connections_get_all(); l != NULL; l = l->next) {
gc = (PurpleConnection *)l->data;
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
- if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->join_chat != NULL) {
+ if (prpl_info->join_chat != NULL) {
account = purple_connection_get_account(gc);
break;
}
@@ -7254,7 +7506,7 @@ pidgin_blist_request_add_chat(PurpleAccount *account, PurpleGroup *group,
if (account == NULL) {
purple_notify_error(NULL, NULL,
_("You are not currently signed on with any "
- "protocols that have the ability to chat."), NULL);
+ "protocols that have the ability to chat."), NULL, NULL);
return;
}
}
@@ -7289,7 +7541,7 @@ pidgin_blist_request_add_chat(PurpleAccount *account, PurpleGroup *group,
if (name != NULL)
gtk_widget_grab_focus(data->alias_entry);
- data->group_combo = pidgin_text_combo_box_entry_new(group ? group->name : NULL, groups_tree());
+ data->group_combo = pidgin_text_combo_box_entry_new(group ? purple_group_get_name(group) : NULL, groups_tree());
pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Group:"),
data->chat_data.rq_data.sg, data->group_combo,
TRUE, NULL);
@@ -7322,15 +7574,14 @@ pidgin_blist_request_add_group(void)
NULL, FALSE, FALSE, NULL,
_("Add"), G_CALLBACK(add_group_cb),
_("Cancel"), NULL,
- NULL, NULL, NULL,
- NULL);
+ NULL, NULL);
}
void
pidgin_blist_toggle_visibility()
{
if (gtkblist && gtkblist->window) {
- if (GTK_WIDGET_VISIBLE(gtkblist->window)) {
+ if (gtk_widget_get_visible(gtkblist->window)) {
/* make the buddy list visible if it is iconified or if it is
* obscured and not currently focused (the focus part ensures
* that we do something reasonable if the buddy list is obscured
@@ -7385,9 +7636,9 @@ pidgin_blist_set_headline(const char *text, GdkPixbuf *pixbuf, GCallback callbac
gtkblist->headline_destroy = destroy;
if (text != NULL || pixbuf != NULL) {
set_urgent();
- gtk_widget_show_all(gtkblist->headline_hbox);
+ gtk_widget_show_all(gtkblist->headline);
} else {
- gtk_widget_hide(gtkblist->headline_hbox);
+ gtk_widget_hide(gtkblist->headline);
}
}
@@ -7395,7 +7646,7 @@ pidgin_blist_set_headline(const char *text, GdkPixbuf *pixbuf, GCallback callbac
static void
set_urgent(void)
{
- if (gtkblist->window && !GTK_WIDGET_HAS_FOCUS(gtkblist->window))
+ if (gtkblist->window && !gtk_widget_has_focus(gtkblist->window))
pidgin_set_urgent(GTK_WINDOW(gtkblist->window), TRUE);
}
@@ -7414,7 +7665,7 @@ static PurpleBlistUiOps blist_ui_ops =
NULL,
NULL,
NULL,
- NULL
+ NULL, NULL, NULL, NULL
};
@@ -7433,24 +7684,24 @@ static gboolean autojoin_cb(PurpleConnection *gc, gpointer data)
{
PurpleAccount *account = purple_connection_get_account(gc);
PurpleBlistNode *gnode, *cnode;
- for(gnode = purple_get_blist()->root; gnode; gnode = gnode->next)
+ for(gnode = purple_blist_get_buddy_list()->root; gnode; gnode = gnode->next)
{
- if(!PURPLE_BLIST_NODE_IS_GROUP(gnode))
+ if(!PURPLE_IS_GROUP(gnode))
continue;
for(cnode = gnode->child; cnode; cnode = cnode->next)
{
PurpleChat *chat;
- if(!PURPLE_BLIST_NODE_IS_CHAT(cnode))
+ if(!PURPLE_IS_CHAT(cnode))
continue;
chat = (PurpleChat *)cnode;
- if(chat->account != account)
+ if(purple_chat_get_account(chat) != account)
continue;
- if (purple_blist_node_get_bool((PurpleBlistNode*)chat, "gtk-autojoin"))
- serv_join_chat(gc, chat->components);
+ if (purple_blist_node_get_bool(PURPLE_BLIST_NODE(chat), "gtk-autojoin"))
+ purple_serv_join_chat(gc, purple_chat_get_components(chat));
}
}
@@ -7467,30 +7718,33 @@ pidgin_blist_get_handle() {
static gboolean buddy_signonoff_timeout_cb(PurpleBuddy *buddy)
{
- struct _pidgin_blist_node *gtknode = ((PurpleBlistNode*)buddy)->ui_data;
+ struct _pidgin_blist_node *gtknode = purple_blist_node_get_ui_data(PURPLE_BLIST_NODE(buddy));
gtknode->recent_signonoff = FALSE;
gtknode->recent_signonoff_timer = 0;
- pidgin_blist_update(NULL, (PurpleBlistNode*)buddy);
+ pidgin_blist_update(NULL, PURPLE_BLIST_NODE(buddy));
+ g_object_unref(buddy);
return FALSE;
}
static void buddy_signonoff_cb(PurpleBuddy *buddy)
{
- struct _pidgin_blist_node *gtknode;
+ struct _pidgin_blist_node *gtknode = purple_blist_node_get_ui_data(PURPLE_BLIST_NODE(buddy));
- if(!((PurpleBlistNode*)buddy)->ui_data) {
- pidgin_blist_new_node((PurpleBlistNode*)buddy);
+ if(!gtknode) {
+ pidgin_blist_new_node(PURPLE_BLIST_NODE(buddy));
}
- gtknode = ((PurpleBlistNode*)buddy)->ui_data;
+ gtknode = purple_blist_node_get_ui_data(PURPLE_BLIST_NODE(buddy));
gtknode->recent_signonoff = TRUE;
if(gtknode->recent_signonoff_timer > 0)
purple_timeout_remove(gtknode->recent_signonoff_timer);
+
+ g_object_ref(buddy);
gtknode->recent_signonoff_timer = purple_timeout_add_seconds(10,
(GSourceFunc)buddy_signonoff_timeout_cb, buddy);
}
@@ -7499,7 +7753,7 @@ void
pidgin_blist_set_theme(PidginBlistTheme *theme)
{
PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
- PurpleBuddyList *list = purple_get_blist();
+ PurpleBuddyList *list = purple_blist_get_buddy_list();
if (theme != NULL)
purple_prefs_set_string(PIDGIN_PREFS_ROOT "/blist/theme",
@@ -7546,42 +7800,32 @@ void pidgin_blist_init(void)
purple_prefs_add_int(PIDGIN_PREFS_ROOT "/blist/y", 0);
purple_prefs_add_int(PIDGIN_PREFS_ROOT "/blist/width", 250); /* Golden ratio, baby */
purple_prefs_add_int(PIDGIN_PREFS_ROOT "/blist/height", 405); /* Golden ratio, baby */
-#if !GTK_CHECK_VERSION(2,14,0)
- /* This pref is used in pidgintooltip.c. */
- purple_prefs_add_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay", 500);
-#endif
purple_prefs_add_string(PIDGIN_PREFS_ROOT "/blist/theme", "");
purple_theme_manager_register_type(g_object_new(PIDGIN_TYPE_BLIST_THEME_LOADER, "type", "blist", NULL));
/* Register our signals */
purple_signal_register(gtk_blist_handle, "gtkblist-hiding",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_BLIST));
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_BUDDY_LIST);
purple_signal_register(gtk_blist_handle, "gtkblist-unhiding",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_BLIST));
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_BUDDY_LIST);
purple_signal_register(gtk_blist_handle, "gtkblist-created",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_BLIST));
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_BUDDY_LIST);
purple_signal_register(gtk_blist_handle, "drawing-tooltip",
- purple_marshal_VOID__POINTER_POINTER_UINT, NULL, 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_BLIST_NODE),
- purple_value_new_outgoing(PURPLE_TYPE_BOXED, "GString *"),
- purple_value_new(PURPLE_TYPE_BOOLEAN));
+ purple_marshal_VOID__POINTER_POINTER_UINT, G_TYPE_NONE,
+ 3, PURPLE_TYPE_BLIST_NODE,
+ G_TYPE_POINTER, /* pointer to a (GString *) */
+ G_TYPE_BOOLEAN);
purple_signal_register(gtk_blist_handle, "drawing-buddy",
purple_marshal_POINTER__POINTER,
- purple_value_new(PURPLE_TYPE_STRING), 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_BLIST_BUDDY));
+ G_TYPE_STRING, 1, PURPLE_TYPE_BUDDY);
purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on",
gtk_blist_handle, PURPLE_CALLBACK(buddy_signonoff_cb), NULL);
@@ -7614,13 +7858,13 @@ GList *pidgin_blist_get_sort_methods()
void pidgin_blist_sort_method_reg(const char *id, const char *name, pidgin_blist_sort_function func)
{
- struct pidgin_blist_sort_method *method;
+ struct _PidginBlistSortMethod *method;
g_return_if_fail(id != NULL);
g_return_if_fail(name != NULL);
g_return_if_fail(func != NULL);
- method = g_new0(struct pidgin_blist_sort_method, 1);
+ method = g_new0(struct _PidginBlistSortMethod, 1);
method->id = g_strdup(id);
method->name = g_strdup(name);
method->func = func;
@@ -7635,7 +7879,7 @@ void pidgin_blist_sort_method_unreg(const char *id)
g_return_if_fail(id != NULL);
while(l) {
- struct pidgin_blist_sort_method *method = l->data;
+ struct _PidginBlistSortMethod *method = l->data;
if(!strcmp(method->id, id)) {
pidgin_blist_sort_methods = g_list_delete_link(pidgin_blist_sort_methods, l);
g_free(method->id);
@@ -7654,7 +7898,7 @@ void pidgin_blist_sort_method_set(const char *id){
if(!id)
id = "none";
- while (l && strcmp(((struct pidgin_blist_sort_method*)l->data)->id, id))
+ while (l && strcmp(((struct _PidginBlistSortMethod*)l->data)->id, id))
l = l->next;
if (l) {
@@ -7664,9 +7908,9 @@ void pidgin_blist_sort_method_set(const char *id){
return;
}
if (!strcmp(id, "none")) {
- redo_buddy_list(purple_get_blist(), TRUE, FALSE);
+ redo_buddy_list(purple_blist_get_buddy_list(), TRUE, FALSE);
} else {
- redo_buddy_list(purple_get_blist(), FALSE, FALSE);
+ redo_buddy_list(purple_blist_get_buddy_list(), FALSE, FALSE);
}
}
@@ -7699,9 +7943,9 @@ static void sort_method_alphabetical(PurpleBlistNode *node, PurpleBuddyList *bli
const char *my_name;
- if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ if(PURPLE_IS_CONTACT(node)) {
my_name = purple_contact_get_alias((PurpleContact*)node);
- } else if(PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if(PURPLE_IS_CHAT(node)) {
my_name = purple_chat_get_name((PurpleChat*)node);
} else {
sort_method_none(node, blist, groupiter, cur, iter);
@@ -7720,9 +7964,9 @@ static void sort_method_alphabetical(PurpleBlistNode *node, PurpleBuddyList *bli
gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &n, -1);
- if(PURPLE_BLIST_NODE_IS_CONTACT(n)) {
+ if(PURPLE_IS_CONTACT(n)) {
this_name = purple_contact_get_alias((PurpleContact*)n);
- } else if(PURPLE_BLIST_NODE_IS_CHAT(n)) {
+ } else if(PURPLE_IS_CHAT(n)) {
this_name = purple_chat_get_name((PurpleChat*)n);
} else {
this_name = NULL;
@@ -7759,9 +8003,9 @@ static void sort_method_status(PurpleBlistNode *node, PurpleBuddyList *blist, Gt
PurpleBuddy *my_buddy, *this_buddy;
- if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ if(PURPLE_IS_CONTACT(node)) {
my_buddy = purple_contact_get_priority_buddy((PurpleContact*)node);
- } else if(PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if(PURPLE_IS_CHAT(node)) {
if (cur != NULL) {
*iter = *cur;
return;
@@ -7787,7 +8031,7 @@ static void sort_method_status(PurpleBlistNode *node, PurpleBuddyList *blist, Gt
gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &n, -1);
- if(PURPLE_BLIST_NODE_IS_CONTACT(n)) {
+ if(PURPLE_IS_CONTACT(n)) {
this_buddy = purple_contact_get_priority_buddy((PurpleContact*)n);
} else {
this_buddy = NULL;
@@ -7799,9 +8043,9 @@ static void sort_method_status(PurpleBlistNode *node, PurpleBuddyList *blist, Gt
? purple_contact_get_alias(purple_buddy_get_contact(this_buddy))
: NULL));
- presence_cmp = purple_presence_compare(
- purple_buddy_get_presence(my_buddy),
- this_buddy ? purple_buddy_get_presence(this_buddy) : NULL);
+ presence_cmp = purple_buddy_presence_compare(
+ PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(my_buddy)),
+ this_buddy ? PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(this_buddy)) : NULL);
if (this_buddy == NULL ||
(presence_cmp < 0 ||
@@ -7847,15 +8091,15 @@ static void sort_method_log_activity(PurpleBlistNode *node, PurpleBuddyList *bli
return;
}
- if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+ if(PURPLE_IS_CONTACT(node)) {
PurpleBlistNode *n;
PurpleBuddy *buddy;
for (n = node->child; n; n = n->next) {
buddy = (PurpleBuddy*)n;
- activity_score += purple_log_get_activity_score(PURPLE_LOG_IM, buddy->name, buddy->account);
+ activity_score += purple_log_get_activity_score(PURPLE_LOG_IM, purple_buddy_get_name(buddy), purple_buddy_get_account(buddy));
}
buddy_name = purple_contact_get_alias((PurpleContact*)node);
- } else if(PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ } else if(PURPLE_IS_CHAT(node)) {
/* we don't have a reliable way of getting the log filename
* from the chat info in the blist, yet */
if (cur != NULL) {
@@ -7885,10 +8129,10 @@ static void sort_method_log_activity(PurpleBlistNode *node, PurpleBuddyList *bli
gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &n, -1);
this_log_activity_score = 0;
- if(PURPLE_BLIST_NODE_IS_CONTACT(n)) {
+ if(PURPLE_IS_CONTACT(n)) {
for (n2 = n->child; n2; n2 = n2->next) {
- buddy = (PurpleBuddy*)n2;
- this_log_activity_score += purple_log_get_activity_score(PURPLE_LOG_IM, buddy->name, buddy->account);
+ buddy = (PurpleBuddy*)n2;
+ this_log_activity_score += purple_log_get_activity_score(PURPLE_LOG_IM, purple_buddy_get_name(buddy), purple_buddy_get_account(buddy));
}
this_buddy_name = purple_contact_get_alias((PurpleContact*)n);
} else {
@@ -7897,7 +8141,7 @@ static void sort_method_log_activity(PurpleBlistNode *node, PurpleBuddyList *bli
cmp = purple_utf8_strcasecmp(buddy_name, this_buddy_name);
- if (!PURPLE_BLIST_NODE_IS_CONTACT(n) || activity_score > this_log_activity_score ||
+ if (!PURPLE_IS_CONTACT(n) || activity_score > this_log_activity_score ||
((activity_score == this_log_activity_score) &&
(cmp < 0 || (cmp == 0 && node < n)))) {
if (cur != NULL) {
@@ -7923,47 +8167,50 @@ static void sort_method_log_activity(PurpleBlistNode *node, PurpleBuddyList *bli
}
static void
-plugin_act(GtkObject *obj, PurplePluginAction *pam)
+plugin_act(GtkWidget *obj, PurplePluginAction *pam)
{
if (pam && pam->callback)
pam->callback(pam);
}
static void
-build_plugin_actions(GtkWidget *menu, PurplePlugin *plugin,
- gpointer context)
+build_plugin_actions(GtkActionGroup *action_group, GString *ui, char *parent,
+ PurplePlugin *plugin, gpointer context)
{
- GtkWidget *menuitem;
+ GtkAction *menuaction;
PurplePluginAction *action = NULL;
GList *actions, *l;
+ char *name;
+ int count = 0;
actions = PURPLE_PLUGIN_ACTIONS(plugin, context);
- for (l = actions; l != NULL; l = l->next)
- {
- if (l->data)
- {
- action = (PurplePluginAction *) l->data;
+ for (l = actions; l != NULL; l = l->next) {
+ if (l->data) {
+ action = (PurplePluginAction *)l->data;
action->plugin = plugin;
action->context = context;
- menuitem = gtk_menu_item_new_with_label(action->label);
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+ name = g_strdup_printf("%s-action-%d", parent, count++);
+ menuaction = gtk_action_new(name, action->label, NULL, NULL);
+ gtk_action_group_add_action(action_group, menuaction);
+ g_string_append_printf(ui, "<menuitem action='%s'/>", name);
- g_signal_connect(G_OBJECT(menuitem), "activate",
+ g_signal_connect(G_OBJECT(menuaction), "activate",
G_CALLBACK(plugin_act), action);
- g_object_set_data_full(G_OBJECT(menuitem), "plugin_action",
+ g_object_set_data_full(G_OBJECT(menuaction), "plugin_action",
action,
(GDestroyNotify)purple_plugin_action_free);
- gtk_widget_show(menuitem);
+ g_free(name);
}
else
- pidgin_separator(menu);
+ g_string_append(ui, "<separator/>");
}
g_list_free(actions);
}
+
static void
modify_account_cb(GtkWidget *widget, gpointer data)
{
@@ -7990,14 +8237,12 @@ disable_account_cb(GtkCheckMenuItem *widget, gpointer data)
purple_account_set_enabled(account, PIDGIN_UI, FALSE);
}
-
-
void
pidgin_blist_update_accounts_menu(void)
{
- GtkWidget *menuitem = NULL, *submenu = NULL;
- GtkAccelGroup *accel_group = NULL;
- GList *l = NULL, *accounts = NULL;
+ GtkWidget *menuitem, *submenu;
+ GtkAccelGroup *accel_group;
+ GList *l, *accounts;
gboolean disabled_accounts = FALSE;
gboolean enabled_accounts = FALSE;
@@ -8008,10 +8253,12 @@ pidgin_blist_update_accounts_menu(void)
for (l = gtk_container_get_children(GTK_CONTAINER(accountmenu)); l; l = g_list_delete_link(l, l)) {
menuitem = l->data;
- if (menuitem != gtk_item_factory_get_widget(gtkblist->ift, N_("/Accounts/Manage Accounts")))
+ if (menuitem != gtk_ui_manager_get_widget(gtkblist->ui, "/BList/AccountsMenu/ManageAccounts"))
gtk_widget_destroy(menuitem);
}
+ accel_group = gtk_menu_get_accel_group(GTK_MENU(accountmenu));
+
for (accounts = purple_accounts_get_all(); accounts; accounts = accounts->next) {
char *buf = NULL;
GtkWidget *image = NULL;
@@ -8020,14 +8267,14 @@ pidgin_blist_update_accounts_menu(void)
account = accounts->data;
- if(!purple_account_get_enabled(account, PIDGIN_UI)) {
+ if (!purple_account_get_enabled(account, PIDGIN_UI)) {
if (!disabled_accounts) {
menuitem = gtk_menu_item_new_with_label(_("Enable Account"));
gtk_menu_shell_append(GTK_MENU_SHELL(accountmenu), menuitem);
submenu = gtk_menu_new();
gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group);
- gtk_menu_set_accel_path(GTK_MENU(submenu), N_("<PurpleMain>/Accounts/Enable Account"));
+ gtk_menu_set_accel_path(GTK_MENU(submenu), "<Actions>/BListActions/EnableAccount");
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
disabled_accounts = TRUE;
@@ -8037,9 +8284,9 @@ pidgin_blist_update_accounts_menu(void)
purple_account_get_protocol_name(account), ")", NULL);
menuitem = gtk_image_menu_item_new_with_label(buf);
g_free(buf);
+
pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
- if (pixbuf != NULL)
- {
+ if (pixbuf != NULL) {
if (!purple_account_is_connected(account))
gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE);
image = gtk_image_new_from_pixbuf(pixbuf);
@@ -8047,9 +8294,11 @@ pidgin_blist_update_accounts_menu(void)
gtk_widget_show(image);
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
}
+
g_signal_connect(G_OBJECT(menuitem), "activate",
G_CALLBACK(enable_account_cb), account);
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
+
} else {
enabled_accounts = TRUE;
}
@@ -8061,7 +8310,6 @@ pidgin_blist_update_accounts_menu(void)
}
pidgin_separator(accountmenu);
- accel_group = gtk_menu_get_accel_group(GTK_MENU(accountmenu));
for (accounts = purple_accounts_get_all(); accounts; accounts = accounts->next) {
char *buf = NULL;
@@ -8081,8 +8329,9 @@ pidgin_blist_update_accounts_menu(void)
buf = g_strconcat(purple_account_get_username(account), " (",
purple_account_get_protocol_name(account), ")", NULL);
menuitem = gtk_image_menu_item_new_with_label(buf);
- accel_path_buf = g_strconcat(N_("<PurpleMain>/Accounts/"), buf, NULL);
+ accel_path_buf = g_strconcat("<Actions>/AccountActions/", buf, NULL);
g_free(buf);
+
pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
if (pixbuf != NULL) {
if (!purple_account_is_connected(account))
@@ -8093,6 +8342,7 @@ pidgin_blist_update_accounts_menu(void)
gtk_widget_show(image);
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
}
+
gtk_menu_shell_append(GTK_MENU_SHELL(accountmenu), menuitem);
submenu = gtk_menu_new();
@@ -8101,7 +8351,6 @@ pidgin_blist_update_accounts_menu(void)
g_free(accel_path_buf);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
-
menuitem = gtk_menu_item_new_with_mnemonic(_("_Edit Account"));
g_signal_connect(G_OBJECT(menuitem), "activate",
G_CALLBACK(modify_account_cb), account);
@@ -8110,14 +8359,14 @@ pidgin_blist_update_accounts_menu(void)
pidgin_separator(submenu);
gc = purple_account_get_connection(account);
- plugin = gc && PURPLE_CONNECTION_IS_CONNECTED(gc) ? gc->prpl : NULL;
+ plugin = gc && PURPLE_CONNECTION_IS_CONNECTED(gc) ? purple_connection_get_prpl(gc) : NULL;
prpl_info = plugin ? PURPLE_PLUGIN_PROTOCOL_INFO(plugin) : NULL;
if (prpl_info &&
(PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_moods) ||
PURPLE_PLUGIN_HAS_ACTIONS(plugin))) {
if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_moods) &&
- gc->flags & PURPLE_CONNECTION_SUPPORT_MOODS) {
+ (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_SUPPORT_MOODS)) {
if (purple_account_get_status(account, "mood")) {
menuitem = gtk_menu_item_new_with_mnemonic(_("Set _Mood..."));
@@ -8126,8 +8375,37 @@ pidgin_blist_update_accounts_menu(void)
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
}
}
+
if (PURPLE_PLUGIN_HAS_ACTIONS(plugin)) {
- build_plugin_actions(submenu, plugin, gc);
+ GtkWidget *menuitem;
+ PurplePluginAction *action = NULL;
+ GList *actions, *l;
+
+ actions = PURPLE_PLUGIN_ACTIONS(plugin, gc);
+
+ for (l = actions; l != NULL; l = l->next)
+ {
+ if (l->data)
+ {
+ action = (PurplePluginAction *) l->data;
+ action->plugin = plugin;
+ action->context = gc;
+
+ menuitem = gtk_menu_item_new_with_label(action->label);
+ gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
+
+ g_signal_connect(G_OBJECT(menuitem), "activate",
+ G_CALLBACK(plugin_act), action);
+ g_object_set_data_full(G_OBJECT(menuitem), "plugin_action",
+ action,
+ (GDestroyNotify)purple_plugin_action_free);
+ gtk_widget_show(menuitem);
+ }
+ else
+ pidgin_separator(submenu);
+ }
+
+ g_list_free(actions);
}
} else {
menuitem = gtk_menu_item_new_with_label(_("No actions available"));
@@ -8142,36 +8420,45 @@ pidgin_blist_update_accounts_menu(void)
G_CALLBACK(disable_account_cb), account);
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
}
+
gtk_widget_show_all(accountmenu);
}
-static GList *plugin_submenus = NULL;
+static guint plugins_merge_id;
+static GtkActionGroup *plugins_action_group = NULL;
void
pidgin_blist_update_plugin_actions(void)
{
- GtkWidget *menuitem, *submenu;
PurplePlugin *plugin = NULL;
GList *l;
- GtkAccelGroup *accel_group;
- GtkWidget *pluginmenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools"));
+ GtkAction *action;
+ GString *plugins_ui;
+ gchar *ui_string;
+ int count = 0;
- g_return_if_fail(pluginmenu != NULL);
+ if ((gtkblist == NULL) || (gtkblist->ui == NULL))
+ return;
- /* Remove old plugin action submenus from the Tools menu */
- for (l = plugin_submenus; l; l = l->next)
- gtk_widget_destroy(GTK_WIDGET(l->data));
- g_list_free(plugin_submenus);
- plugin_submenus = NULL;
+ /* Clear the old menu */
+ if (plugins_action_group) {
+ gtk_ui_manager_remove_ui(gtkblist->ui, plugins_merge_id);
+ gtk_ui_manager_remove_action_group(gtkblist->ui, plugins_action_group);
+ g_object_unref(G_OBJECT(plugins_action_group));
+ }
- accel_group = gtk_menu_get_accel_group(GTK_MENU(pluginmenu));
+ plugins_action_group = gtk_action_group_new("PluginActions");
+#ifdef ENABLE_NLS
+ gtk_action_group_set_translation_domain(plugins_action_group, PACKAGE);
+#endif
+ plugins_ui = g_string_new(NULL);
/* Add a submenu for each plugin with custom actions */
for (l = purple_plugins_get_loaded(); l; l = l->next) {
- char *path;
+ char *name;
- plugin = (PurplePlugin *) l->data;
+ plugin = (PurplePlugin *)l->data;
if (PURPLE_IS_PROTOCOL_PLUGIN(plugin))
continue;
@@ -8179,28 +8466,34 @@ pidgin_blist_update_plugin_actions(void)
if (!PURPLE_PLUGIN_HAS_ACTIONS(plugin))
continue;
- menuitem = gtk_image_menu_item_new_with_label(_(plugin->info->name));
- gtk_menu_shell_append(GTK_MENU_SHELL(pluginmenu), menuitem);
+ name = g_strdup_printf("plugin%d", count);
+ action = gtk_action_new(name, _(plugin->info->name), NULL, NULL);
+ gtk_action_group_add_action(plugins_action_group, action);
+ g_string_append_printf(plugins_ui, "<menu action='%s'>", name);
- plugin_submenus = g_list_append(plugin_submenus, menuitem);
-
- submenu = gtk_menu_new();
- gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
+ build_plugin_actions(plugins_action_group, plugins_ui, name, plugin, NULL);
- gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group);
- path = g_strdup_printf("%s/Tools/%s", gtkblist->ift->path, plugin->info->name);
- gtk_menu_set_accel_path(GTK_MENU(submenu), path);
- g_free(path);
+ g_string_append(plugins_ui, "</menu>");
+ count++;
- build_plugin_actions(submenu, plugin, NULL);
+ g_free(name);
}
- gtk_widget_show_all(pluginmenu);
+
+ ui_string = g_strconcat("<ui><menubar action='BList'><menu action='ToolsMenu'><placeholder name='PluginActions'>",
+ plugins_ui->str,
+ "</placeholder></menu></menubar></ui>",
+ NULL);
+ gtk_ui_manager_insert_action_group(gtkblist->ui, plugins_action_group, 1);
+ plugins_merge_id = gtk_ui_manager_add_ui_from_string(gtkblist->ui, ui_string, -1, NULL);
+
+ g_string_free(plugins_ui, TRUE);
+ g_free(ui_string);
}
static void
-sortmethod_act(GtkCheckMenuItem *checkmenuitem, char *id)
+sortmethod_act(GtkRadioAction *action, GtkRadioAction *current, char *id)
{
- if (gtk_check_menu_item_get_active(checkmenuitem))
+ if (action == current)
{
pidgin_set_cursor(gtkblist->window, GDK_WATCH);
/* This is redundant. I think. */
@@ -8214,40 +8507,58 @@ sortmethod_act(GtkCheckMenuItem *checkmenuitem, char *id)
void
pidgin_blist_update_sort_methods(void)
{
- GtkWidget *menuitem = NULL, *activeitem = NULL;
PidginBlistSortMethod *method = NULL;
GList *l;
GSList *sl = NULL;
- GtkWidget *sortmenu;
const char *m = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/sort_type");
- if ((gtkblist == NULL) || (gtkblist->ift == NULL))
- return;
-
- g_return_if_fail(m != NULL);
-
- sortmenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Buddies/Sort Buddies"));
+ GtkRadioAction *action;
+ GString *ui_string;
- if (sortmenu == NULL)
+ if ((gtkblist == NULL) || (gtkblist->ui == NULL))
return;
/* Clear the old menu */
- for (l = gtk_container_get_children(GTK_CONTAINER(sortmenu)); l; l = g_list_delete_link(l, l)) {
- menuitem = l->data;
- gtk_widget_destroy(GTK_WIDGET(menuitem));
+ if (sort_action_group) {
+ gtk_ui_manager_remove_ui(gtkblist->ui, sort_merge_id);
+ gtk_ui_manager_remove_action_group(gtkblist->ui, sort_action_group);
+ g_object_unref(G_OBJECT(sort_action_group));
}
+ sort_action_group = gtk_action_group_new("SortMethods");
+#ifdef ENABLE_NLS
+ gtk_action_group_set_translation_domain(sort_action_group, PACKAGE);
+#endif
+ ui_string = g_string_new("<ui><menubar name='BList'>"
+ "<menu action='BuddiesMenu'><menu action='SortMenu'>");
+
for (l = pidgin_blist_sort_methods; l; l = l->next) {
- method = (PidginBlistSortMethod *) l->data;
- menuitem = gtk_radio_menu_item_new_with_label(sl, _(method->name));
- if (g_str_equal(m, method->id))
- activeitem = menuitem;
- sl = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(menuitem));
- gtk_menu_shell_append(GTK_MENU_SHELL(sortmenu), menuitem);
- g_signal_connect(G_OBJECT(menuitem), "toggled",
- G_CALLBACK(sortmethod_act), method->id);
- gtk_widget_show(menuitem);
- }
- if (activeitem)
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(activeitem), TRUE);
+ method = (PidginBlistSortMethod *)l->data;
+
+ g_string_append_printf(ui_string, "<menuitem action='%s'/>", method->id);
+ action = gtk_radio_action_new(method->id,
+ method->name,
+ NULL,
+ NULL,
+ 0);
+ gtk_action_group_add_action_with_accel(sort_action_group, GTK_ACTION(action), NULL);
+
+ gtk_radio_action_set_group(action, sl);
+ sl = gtk_radio_action_get_group(action);
+
+ if (!strcmp(m, method->id))
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE);
+ else
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), FALSE);
+
+ g_signal_connect(G_OBJECT(action), "changed",
+ G_CALLBACK(sortmethod_act), method->id);
+ }
+
+ g_string_append(ui_string, "</menu></menu></menubar></ui>");
+ gtk_ui_manager_insert_action_group(gtkblist->ui, sort_action_group, 1);
+ sort_merge_id = gtk_ui_manager_add_ui_from_string(gtkblist->ui, ui_string->str, -1, NULL);
+
+ g_string_free(ui_string, TRUE);
}
+
diff --git a/pidgin/gtkblist.h b/pidgin/gtkblist.h
index a3d5f26c4f..0dba4ad259 100644
--- a/pidgin/gtkblist.h
+++ b/pidgin/gtkblist.h
@@ -1,9 +1,3 @@
-/**
- * @file gtkblist.h GTK+ Buddy List API
- * @ingroup pidgin
- * @see @ref gtkblist-signals
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -24,10 +18,19 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _PIDGINBLIST_H_
#define _PIDGINBLIST_H_
+/**
+ * SECTION:gtkblist
+ * @section_id: pidgin-gtkblist
+ * @short_description: <filename>gtkblist.h</filename>
+ * @title: Buddy List API
+ * @see_also: <link linkend="chapter-signals-gtkblist">Buddy List signals</link>
+ */
+
+#define PIDGIN_TYPE_BUDDY_LIST (pidgin_buddy_list_get_type())
-/** @copydoc _PidginBuddyList */
typedef struct _PidginBuddyList PidginBuddyList;
enum {
@@ -59,136 +62,170 @@ typedef enum {
} PidginStatusIconSize;
#include "pidgin.h"
-#include "blist.h"
+#include "buddylist.h"
#include "gtkblist-theme.h"
/**************************************************************************
- * @name Structures
+ * Structures
**************************************************************************/
/**
+ * PidginBuddyList:
+ * @notebook: The notebook that switches between the real buddy list
+ * and the helpful instructions page
+ * @main_vbox: This vbox contains the menu and notebook
+ * @vbox: This is the vbox that everything important gets packed
+ * into. Your plugin might want to pack something in it
+ * itself. Go, plugins!
+ * @treeview: It's a treeview... d'uh.
+ * @treemodel: This is the treemodel.
+ * @text_column: Column
+ * @menutray: The menu tray widget.
+ * @menutrayicon: The menu tray icon.
+ * @refresh_timer: The timer for refreshing every 30 seconds
+ * @timeout: The timeout for the tooltip.
+ * @drag_timeout: The timeout for expanding contacts on drags
+ * @tip_rect: This is the bounding rectangle of the cell we're
+ * currently hovering over. This is used for tooltips.
+ * @contact_rect: This is the bounding rectangle of the contact node and
+ * its children. This is used for auto-expand on mouseover.
+ * @mouseover_contact: This is the contact currently mouse-over expanded
+ * @tipwindow: The window used by the tooltip
+ * @tooltipdata: The data for each "chunk" of the tooltip
+ * @selected_node: The currently selected node
+ * @scrollbook: Scrollbook for alerts
+ * @headline: Widget for headline notifications
+ * @headline_label: Label for headline notifications
+ * @headline_image: Image for headline notifications
+ * @headline_callback: Callback for headline notifications
+ * @headline_data: User data for headline notifications
+ * @headline_destroy: Callback to use for destroying the headline-data
+ * @statusbox: The status selector dropdown
+ * @empty_avatar: A 32x32 transparent pixbuf
+ * @priv: Pointer to opaque private data
+ *
* Like, everything you need to know about the gtk buddy list
*/
struct _PidginBuddyList {
GtkWidget *window;
- GtkWidget *notebook; /**< The notebook that switches between the real buddy list and the helpful
- instructions page */
- GtkWidget *main_vbox; /**< This vbox contains the menu and notebook */
- GtkWidget *vbox; /**< This is the vbox that everything important gets packed into.
- Your plugin might want to pack something in it itself. Go, plugins! */
+ GtkWidget *notebook;
+ GtkWidget *main_vbox;
+ GtkWidget *vbox;
- GtkWidget *treeview; /**< It's a treeview... d'uh. */
- GtkTreeStore *treemodel; /**< This is the treemodel. */
- GtkTreeViewColumn *text_column; /**< Column */
+ GtkWidget *treeview;
+ GtkTreeStore *treemodel;
+ GtkTreeViewColumn *text_column;
GtkCellRenderer *text_rend;
- GtkItemFactory *ift;
- GtkWidget *menutray; /**< The menu tray widget. */
- GtkWidget *menutrayicon; /**< The menu tray icon. */
-
- /** Caches connection error messages; keys are #PurpleAccount and
- * values are non-@c NULL <tt>const char *</tt>s containing localised
- * error messages. (If an account does not have an error, it will not
- * appear in the table.)
- * @deprecated in favour of purple_account_get_current_error(), which also
- * gives you the #PurpleConnectionError value.
- */
- GHashTable *connection_errors;
-
- guint refresh_timer; /**< The timer for refreshing every 30 seconds */
-
- guint timeout; /**< The timeout for the tooltip. */
- guint drag_timeout; /**< The timeout for expanding contacts on drags */
- GdkRectangle tip_rect; /**< This is the bounding rectangle of the
- cell we're currently hovering over. This is
- used for tooltips. */
- GdkRectangle contact_rect; /**< This is the bounding rectangle of the contact node
- and its children. This is used for auto-expand on
- mouseover. */
- PurpleBlistNode *mouseover_contact; /**< This is the contact currently mouse-over expanded */
-
- GtkWidget *tipwindow; /**< The window used by the tooltip */
- GList *tooltipdata; /**< The data for each "chunk" of the tooltip */
-
- PurpleBlistNode *selected_node; /**< The currently selected node */
-
- GdkCursor *hand_cursor; /**< Hand cursor */
- GdkCursor *arrow_cursor; /**< Arrow cursor */
-
- GtkWidget *scrollbook; /**< Scrollbook for alerts */
- GtkWidget *headline_hbox; /**< Hbox for headline notification */
- GtkWidget *headline_label; /**< Label for headline notifications */
- GtkWidget *headline_image; /**< Image for headline notifications */
- GdkPixbuf *headline_close; /**< @deprecated: Close image for closing the headline without triggering the callback */
- GCallback headline_callback; /**< Callback for headline notifications */
- gpointer headline_data; /**< User data for headline notifications */
- GDestroyNotify headline_destroy; /**< Callback to use for destroying the headline-data */
- gboolean changing_style; /**< True when changing GTK+ theme style */
-
- GtkWidget *error_buttons; /**< Box containing the connection error buttons */
- GtkWidget *statusbox; /**< The status selector dropdown */
- GdkPixbuf *empty_avatar; /**< A 32x32 transparent pixbuf */
-
- gpointer priv; /**< Pointer to opaque private data */
+ GtkUIManager *ui;
+ GtkWidget *menutray;
+ GtkWidget *menutrayicon;
+
+ guint refresh_timer;
+
+ guint timeout;
+ guint drag_timeout;
+ GdkRectangle tip_rect;
+ GdkRectangle contact_rect;
+ PurpleBlistNode *mouseover_contact;
+
+ GtkWidget *tipwindow;
+ GList *tooltipdata;
+
+ PurpleBlistNode *selected_node;
+
+ GtkWidget *scrollbook;
+ GtkWidget *headline;
+ GtkWidget *headline_label;
+ GtkWidget *headline_image;
+ GCallback headline_callback;
+ gpointer headline_data;
+ GDestroyNotify headline_destroy;
+
+ 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
+
/**************************************************************************
- * @name GTK+ Buddy List API
+ * GTK+ Buddy List API
**************************************************************************/
/**
+ * pidgin_buddy_list_get_type:
+ *
+ * Returns: The #GType for the #PidginBuddyList boxed structure.
+ */
+GType pidgin_buddy_list_get_type(void);
+
+/**
+ * pidgin_blist_get_handle:
+ *
* Get the handle for the GTK+ blist system.
*
- * @return the handle to the blist system
+ * Returns: the handle to the blist system
*/
void *pidgin_blist_get_handle(void);
/**
+ * pidgin_blist_init:
+ *
* Initializes the GTK+ blist system.
*/
void pidgin_blist_init(void);
/**
+ * pidgin_blist_uninit:
+ *
* Uninitializes the GTK+ blist system.
*/
void pidgin_blist_uninit(void);
/**
+ * pidgin_blist_get_ui_ops:
+ *
* Returns the UI operations structure for the buddy list.
*
- * @return The GTK+ list operations structure.
+ * 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
*
* There's normally only one buddy list window, but that isn't a necessity. This function
* returns the PidginBuddyList we're most likely wanting to work with. This is slightly
* cleaner than an externed global.
*
- * @return The default GTK+ buddy list
+ * Returns: The default GTK+ buddy list
*/
PidginBuddyList *pidgin_blist_get_default_gtk_blist(void);
/**
- * Populates a menu with the items shown on the buddy list for a buddy.
+ * pidgin_blist_make_buddy_menu:
+ * @menu: The menu to populate
+ * @buddy: The buddy whose menu to get
+ * @sub: %TRUE if this is a sub-menu, %FALSE otherwise
*
- * @param menu The menu to populate
- * @param buddy The buddy whose menu to get
- * @param sub TRUE if this is a sub-menu, FALSE otherwise
+ * Populates a menu with the items shown on the buddy list for a buddy.
*/
void pidgin_blist_make_buddy_menu(GtkWidget *menu, PurpleBuddy *buddy, gboolean sub);
/**
+ * pidgin_blist_refresh:
+ * @list: This is the core list that gets updated from
+ *
* Refreshes all the nodes of the buddy list.
* This should only be called when something changes to affect most of the nodes (such as a ui preference changing)
- *
- * @param list This is the core list that gets updated from
*/
void pidgin_blist_refresh(PurpleBuddyList *list);
@@ -196,36 +233,43 @@ void pidgin_blist_update_columns(void);
void pidgin_blist_update_refresh_timeout(void);
/**
+ * pidgin_blist_get_emblem:
+ * @node: The node to return an emblem for
+ *
* Returns the blist emblem.
*
* This may be an existing pixbuf that has been given an additional ref,
* so it shouldn't be modified.
*
- * @param node The node to return an emblem for
- *
- * @return A GdkPixbuf for the emblem to show, or NULL
+ * Returns: A GdkPixbuf for the emblem to show, or NULL
*/
GdkPixbuf *
pidgin_blist_get_emblem(PurpleBlistNode *node);
/**
+ * pidgin_blist_get_status_icon:
+ *
* Useful for the buddy ticker
*/
GdkPixbuf *pidgin_blist_get_status_icon(PurpleBlistNode *node,
PidginStatusIconSize size);
/**
- * Returns a boolean indicating if @a node is part of an expanded contact.
+ * pidgin_blist_node_is_contact_expanded:
+ * @node: The node in question.
*
- * This only makes sense for contact and buddy nodes. @c FALSE is returned
+ * Returns a boolean indicating if @node is part of an expanded contact.
+ *
+ * This only makes sense for contact and buddy nodes. %FALSE is returned
* for other types of nodes.
*
- * @param node The node in question.
- * @return A boolean indicating if @a node is part of an expanded contact.
+ * Returns: A boolean indicating if @node is part of an expanded contact.
*/
gboolean pidgin_blist_node_is_contact_expanded(PurpleBlistNode *node);
/**
+ * pidgin_blist_toggle_visibility:
+ *
* Intelligently toggles the visibility of the buddy list. If the buddy
* list is obscured, it is brought to the front. If it is not obscured,
* it is hidden. If it is hidden it is shown.
@@ -233,6 +277,8 @@ gboolean pidgin_blist_node_is_contact_expanded(PurpleBlistNode *node);
void pidgin_blist_toggle_visibility(void);
/**
+ * pidgin_blist_visibility_manager_add:
+ *
* Increases the reference count of visibility managers. Callers should
* call the complementary remove function when no longer managing
* visibility.
@@ -243,192 +289,203 @@ void pidgin_blist_toggle_visibility(void);
void pidgin_blist_visibility_manager_add(void);
/**
+ * pidgin_blist_visibility_manager_remove:
+ *
* Decreases the reference count of visibility managers. If the count
* drops below zero, the buddy list is shown.
*/
void pidgin_blist_visibility_manager_remove(void);
/**
- * Adds a mini-alert to the blist scrollbook
+ * pidgin_blist_add_alert:
+ * @widget: The widget to add
*
- * @param widget The widget to add
+ * Adds a mini-alert to the blist scrollbook
*/
void pidgin_blist_add_alert(GtkWidget *widget);
/**
- * Sets the current theme for Pidgin to use
+ * pidgin_blist_set_theme:
+ * @theme: the new theme to use
*
- * @param theme the new theme to use
- *
- * @since 2.6.0
+ * Sets the current theme for Pidgin to use
*/
void pidgin_blist_set_theme(PidginBlistTheme *theme);
/**
- * Gets Pidgin's current buddy list theme
+ * pidgin_blist_get_theme:
*
- * @returns the current theme
+ * Gets Pidgin's current buddy list theme
*
- * @since 2.6.0
+ * Returns: the current theme
*/
PidginBlistTheme *pidgin_blist_get_theme(void);
/**************************************************************************
- * @name GTK+ Buddy List sorting functions
+ * GTK+ Buddy List sorting functions
**************************************************************************/
typedef void (*pidgin_blist_sort_function)(PurpleBlistNode *new, PurpleBuddyList *blist, GtkTreeIter group, GtkTreeIter *cur, GtkTreeIter *iter);
/**
+ * pidgin_blist_get_sort_methods:
+ *
* Gets the current list of sort methods.
*
- * @return A GSlist of sort methods
+ * Returns: A GSlist of sort methods
*/
GList *pidgin_blist_get_sort_methods(void);
-struct pidgin_blist_sort_method {
+struct _PidginBlistSortMethod {
char *id;
char *name;
pidgin_blist_sort_function func;
};
-typedef struct pidgin_blist_sort_method PidginBlistSortMethod;
+typedef struct _PidginBlistSortMethod PidginBlistSortMethod;
/**
- * Registers a buddy list sorting method.
- *
- * @param id The unique ID of the sorting method
- * @param name The method's name.
- * @param func A pointer to the function.
+ * pidgin_blist_sort_method_reg:
+ * @id: The unique ID of the sorting method
+ * @name: The method's name.
+ * @func: A pointer to the function.
*
+ * Registers a buddy list sorting method.
*/
void pidgin_blist_sort_method_reg(const char *id, const char *name, pidgin_blist_sort_function func);
/**
- * Unregisters a buddy list sorting method.
+ * pidgin_blist_sort_method_unreg:
+ * @id: The method's id
*
- * @param id The method's id
+ * Unregisters a buddy list sorting method.
*/
void pidgin_blist_sort_method_unreg(const char *id);
/**
- * Sets a buddy list sorting method.
+ * pidgin_blist_sort_method_set:
+ * @id: The method's id.
*
- * @param id The method's id.
+ * Sets a buddy list sorting method.
*/
void pidgin_blist_sort_method_set(const char *id);
/**
+ * pidgin_blist_setup_sort_methods:
+ *
* Sets up the programs default sort methods
*/
void pidgin_blist_setup_sort_methods(void);
/**
+ * pidgin_blist_update_accounts_menu:
+ *
* Updates the accounts menu on the GTK+ buddy list window.
*/
void pidgin_blist_update_accounts_menu(void);
/**
+ * pidgin_blist_update_plugin_actions:
+ *
* Updates the plugin actions menu on the GTK+ buddy list window.
*/
void pidgin_blist_update_plugin_actions(void);
/**
+ * pidgin_blist_update_sort_methods:
+ *
* Updates the Sorting menu on the GTK+ buddy list window.
*/
void pidgin_blist_update_sort_methods(void);
/**
+ * pidgin_blist_joinchat_is_showable:
+ *
* Determines if showing the join chat dialog is a valid action.
*
- * @return Returns TRUE if there are accounts online capable of
+ * Returns: Returns TRUE if there are accounts online capable of
* joining chat rooms. Otherwise returns FALSE.
*/
gboolean pidgin_blist_joinchat_is_showable(void);
/**
+ * pidgin_blist_joinchat_show:
+ *
* Shows the join chat dialog.
*/
void pidgin_blist_joinchat_show(void);
/**
+ * pidgin_append_blist_node_privacy_menu:
+ *
* Appends the privacy menu items for a PurpleBlistNode
- * TODO: Rename these.
*/
+/* TODO Rename these. */
void pidgin_append_blist_node_privacy_menu(GtkWidget *menu, PurpleBlistNode *node);
/**
+ * pidgin_append_blist_node_proto_menu:
+ *
* Appends the protocol specific menu items for a PurpleBlistNode
- * TODO: Rename these.
*/
+/* TODO Rename these. */
void pidgin_append_blist_node_proto_menu (GtkWidget *menu, PurpleConnection *gc, PurpleBlistNode *node);
/**
+ * pidgin_append_blist_node_extended_menu:
+ *
* Appends the extended menu items for a PurpleBlistNode
- * TODO: Rename these.
*/
+/* TODO Rename these. */
void pidgin_append_blist_node_extended_menu(GtkWidget *menu, PurpleBlistNode *node);
/**
- * Was used by the connection API to tell the blist if an account has a
- * connection error or no longer has a connection error, but the blist now does
- * this itself with the @ref account-error-changed signal.
+ * pidgin_blist_set_headline:
+ * @text: Pango Markup for the label text
+ * @pixbuf: The GdkPixbuf for the icon
+ * @callback: The callback to call when headline is clicked
+ * @user_data: The userdata to include in the callback
+ * @destroy: The callback to call when headline is closed or replaced by another headline.
*
- * @param account The account that either has a connection error
- * or no longer has a connection error.
- * @param message The connection error message, or NULL if this
- * account is no longer in an error state.
- * @deprecated There was no good reason for code other than gtkconn to call
- * this.
- */
-void pidgin_blist_update_account_error_state(PurpleAccount *account, const char *message);
-
-/**
* Sets a headline notification
*
* This is currently used for mail notification, but could theoretically be used for anything.
* Only the most recent headline will be shown.
- *
- * @param text Pango Markup for the label text
- * @param pixbuf The GdkPixbuf for the icon
- * @param callback The callback to call when headline is clicked
- * @param user_data The userdata to include in the callback
- * @param destroy The callback to call when headline is closed or replaced by another headline.
*/
void pidgin_blist_set_headline(const char *text, GdkPixbuf *pixbuf, GCallback callback, gpointer user_data,
GDestroyNotify destroy);
/**
- * Returns a buddy's Pango markup appropriate for setting in a GtkCellRenderer.
+ * pidgin_blist_get_name_markup:
+ * @buddy: The buddy to return markup from
+ * @selected: Whether this buddy is selected. If TRUE, the markup will not change the color.
+ * @aliased: TRUE to return the appropriate alias of this buddy, FALSE to return its username and status information
*
- * @param buddy The buddy to return markup from
- * @param selected Whether this buddy is selected. If TRUE, the markup will not change the color.
- * @param aliased TRUE to return the appropriate alias of this buddy, FALSE to return its username and status information
- * @return The markup for this buddy
+ * Returns a buddy's Pango markup appropriate for setting in a GtkCellRenderer.
*
- * @since 2.1.0
+ * Returns: The markup for this buddy
*/
gchar *pidgin_blist_get_name_markup(PurpleBuddy *buddy, gboolean selected, gboolean aliased);
/**
+ * pidgin_blist_draw_tooltip:
+ * @node: The buddy list node to show a tooltip for
+ * @widget: The widget to draw the tooltip on
+ *
* Creates the Buddy List tooltip at the current pointer location for the given buddy list node.
*
* This tooltip will be destroyed the next time this function is called, or when XXXX
* is called
- *
- * @param node The buddy list node to show a tooltip for
- * @param widget The widget to draw the tooltip on
- *
- * @since 2.1.0
*/
void pidgin_blist_draw_tooltip(PurpleBlistNode *node, GtkWidget *widget);
/**
- * Destroys the current (if any) Buddy List tooltip
+ * pidgin_blist_tooltip_destroy:
*
- * @since 2.1.0
+ * Destroys the current (if any) Buddy List tooltip
*/
void pidgin_blist_tooltip_destroy(void);
+G_END_DECLS
#endif /* _PIDGINBLIST_H_ */
diff --git a/pidgin/gtkcellrendererexpander.c b/pidgin/gtkcellrendererexpander.c
index a5bb6c8a98..0d5d4c7767 100644
--- a/pidgin/gtkcellrendererexpander.c
+++ b/pidgin/gtkcellrendererexpander.c
@@ -1,8 +1,3 @@
-/*
- * @file gtkcellrendererexpander.c GTK+ Cell Renderer Expander
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -29,9 +24,16 @@
* Jonathon Blandford <jrb@redhat.com> for RedHat, Inc.
*/
-#include <gtk/gtk.h>
#include "gtkcellrendererexpander.h"
+#include "gtk3compat.h"
+
+#if GTK_CHECK_VERSION(3,0,0)
+#define GTK3_CONST const
+#else
+#define GTK3_CONST
+#endif
+
static void pidgin_cell_renderer_expander_get_property (GObject *object,
guint param_id,
GValue *value,
@@ -44,24 +46,30 @@ static void pidgin_cell_renderer_expander_init (PidginCellRendererExpander
static void pidgin_cell_renderer_expander_class_init (PidginCellRendererExpanderClass *class);
static void pidgin_cell_renderer_expander_get_size (GtkCellRenderer *cell,
GtkWidget *widget,
- GdkRectangle *cell_area,
+ GTK3_CONST GdkRectangle *cell_area,
gint *x_offset,
gint *y_offset,
gint *width,
gint *height);
static void pidgin_cell_renderer_expander_render (GtkCellRenderer *cell,
+#if GTK_CHECK_VERSION(3,0,0)
+ cairo_t *cr,
+#else
GdkWindow *window,
+#endif
GtkWidget *widget,
- GdkRectangle *background_area,
- GdkRectangle *cell_area,
- GdkRectangle *expose_area,
- guint flags);
+ GTK3_CONST GdkRectangle *background_area,
+ GTK3_CONST GdkRectangle *cell_area,
+#if !GTK_CHECK_VERSION(3,0,0)
+ GdkRectangle *export_area,
+#endif
+ GtkCellRendererState flags);
static gboolean pidgin_cell_renderer_expander_activate (GtkCellRenderer *r,
GdkEvent *event,
GtkWidget *widget,
const gchar *p,
- GdkRectangle *bg,
- GdkRectangle *cell,
+ GTK3_CONST GdkRectangle *bg,
+ GTK3_CONST GdkRectangle *cell,
GtkCellRendererState flags);
static void pidgin_cell_renderer_expander_finalize (GObject *gobject);
@@ -108,9 +116,9 @@ GType pidgin_cell_renderer_expander_get_type (void)
static void pidgin_cell_renderer_expander_init (PidginCellRendererExpander *cellexpander)
{
- GTK_CELL_RENDERER(cellexpander)->mode = GTK_CELL_RENDERER_MODE_ACTIVATABLE;
- GTK_CELL_RENDERER(cellexpander)->xpad = 0;
- GTK_CELL_RENDERER(cellexpander)->ypad = 2;
+ g_object_set(G_OBJECT(cellexpander), "mode",
+ GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
+ gtk_cell_renderer_set_padding(GTK_CELL_RENDERER(cellexpander), 0, 2);
}
static void pidgin_cell_renderer_expander_class_init (PidginCellRendererExpanderClass *class)
@@ -134,7 +142,7 @@ static void pidgin_cell_renderer_expander_class_init (PidginCellRendererExpander
"Is Expander",
"True if the renderer should draw an expander",
FALSE,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
static void pidgin_cell_renderer_expander_finalize (GObject *object)
@@ -185,12 +193,13 @@ static void pidgin_cell_renderer_expander_set_property (GObject *object,
GtkCellRenderer *pidgin_cell_renderer_expander_new(void)
{
- return g_object_new(PIDGIN_TYPE_GTK_CELL_RENDERER_EXPANDER, NULL);
+ return g_object_new(PIDGIN_TYPE_CELL_RENDERER_EXPANDER, NULL);
}
-static void pidgin_cell_renderer_expander_get_size (GtkCellRenderer *cell,
+static void
+pidgin_cell_renderer_expander_get_size (GtkCellRenderer *cell,
GtkWidget *widget,
- GdkRectangle *cell_area,
+ GTK3_CONST GdkRectangle *cell_area,
gint *x_offset,
gint *y_offset,
gint *width,
@@ -199,11 +208,17 @@ static void pidgin_cell_renderer_expander_get_size (GtkCellRenderer *cell,
gint calc_width;
gint calc_height;
gint expander_size;
+ gint xpad;
+ gint ypad;
+ gfloat xalign;
+ gfloat yalign;
gtk_widget_style_get(widget, "expander-size", &expander_size, NULL);
- calc_width = (gint) cell->xpad * 2 + expander_size;
- calc_height = (gint) cell->ypad * 2 + expander_size;
+ gtk_cell_renderer_get_padding(cell, &xpad, &ypad);
+ gtk_cell_renderer_get_alignment(cell, &xalign, &yalign);
+ calc_width = (gint) xpad * 2 + expander_size;
+ calc_height = (gint) ypad * 2 + expander_size;
if (width)
*width = calc_width;
@@ -215,69 +230,110 @@ static void pidgin_cell_renderer_expander_get_size (GtkCellRenderer *cell,
{
if (x_offset)
{
- *x_offset = cell->xalign * (cell_area->width - calc_width);
+ *x_offset = xalign * (cell_area->width - calc_width);
*x_offset = MAX (*x_offset, 0);
}
if (y_offset)
{
- *y_offset = cell->yalign * (cell_area->height - calc_height);
+ *y_offset = yalign * (cell_area->height - calc_height);
*y_offset = MAX (*y_offset, 0);
}
}
}
-static void pidgin_cell_renderer_expander_render(GtkCellRenderer *cell,
- GdkWindow *window,
- GtkWidget *widget,
- GdkRectangle *background_area,
- GdkRectangle *cell_area,
- GdkRectangle *expose_area,
- guint flags)
+static void
+pidgin_cell_renderer_expander_render(GtkCellRenderer *cell,
+#if GTK_CHECK_VERSION(3,0,0)
+ cairo_t *cr,
+#else
+ GdkWindow *window,
+#endif
+ GtkWidget *widget,
+ GTK3_CONST GdkRectangle *background_area,
+ GTK3_CONST GdkRectangle *cell_area,
+#if !GTK_CHECK_VERSION(3,0,0)
+ GdkRectangle *expose_area,
+#endif
+ GtkCellRendererState flags)
{
PidginCellRendererExpander *cellexpander = (PidginCellRendererExpander *) cell;
gboolean set;
gint width, height;
GtkStateType state;
+ gint xpad;
+ gint ypad;
+ gboolean is_expanded;
+ GtkAllocation allocation;
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context;
+#endif
if (!cellexpander->is_expander)
return;
+ gtk_cell_renderer_get_padding(cell, &xpad, &ypad);
+ g_object_get(G_OBJECT(cell), "is-expanded", &is_expanded, NULL);
+
width = cell_area->width;
height = cell_area->height;
- if (!cell->sensitive)
+ if (!gtk_widget_get_sensitive(widget))
state = GTK_STATE_INSENSITIVE;
else if (flags & GTK_CELL_RENDERER_PRELIT)
state = GTK_STATE_PRELIGHT;
- else if (GTK_WIDGET_HAS_FOCUS (widget) && flags & GTK_CELL_RENDERER_SELECTED)
+ else if (gtk_widget_has_focus(widget) && flags & GTK_CELL_RENDERER_SELECTED)
state = GTK_STATE_ACTIVE;
else
state = GTK_STATE_NORMAL;
- width -= cell->xpad*2;
- height -= cell->ypad*2;
+ width -= xpad*2;
+ height -= ypad*2;
- gtk_paint_expander (widget->style,
- window, state,
- NULL, widget, "treeview",
- cell_area->x + cell->xpad + (width / 2),
- cell_area->y + cell->ypad + (height / 2),
- cell->is_expanded ? GTK_EXPANDER_EXPANDED : GTK_EXPANDER_COLLAPSED);
+#if GTK_CHECK_VERSION(3,0,0)
+ if (is_expanded)
+ state |= GTK_STATE_ACTIVE;
+ else
+ state &= ~GTK_STATE_ACTIVE;
+
+ context = gtk_widget_get_style_context(widget);
+ gtk_style_context_add_class(context, GTK_STYLE_CLASS_VIEW);
+ gtk_style_context_add_class(context, GTK_STYLE_CLASS_EXPANDER);
+ gtk_style_context_set_state(context, (GtkStateFlags)state);
+ gtk_render_expander(context, cr,
+ cell_area->x + xpad, cell_area->y + ypad,
+ width, height);
+#else
+ gtk_paint_expander(gtk_widget_get_style(widget),
+ window, state,
+ NULL, widget, "treeview",
+ cell_area->x + cell->xpad + (width / 2),
+ cell_area->y + cell->ypad + (height / 2),
+ is_expanded ? GTK_EXPANDER_EXPANDED : GTK_EXPANDER_COLLAPSED);
+#endif
/* only draw the line if the color isn't set - this prevents a bug where the hline appears only under the expander */
g_object_get(cellexpander, "cell-background-set", &set, NULL);
- if (cell->is_expanded && !set)
- gtk_paint_hline (widget->style, window, state, NULL, widget, NULL, 0,
- widget->allocation.width, cell_area->y + cell_area->height);
+ gtk_widget_get_allocation(widget, &allocation);
+
+#if GTK_CHECK_VERSION(3,0,0)
+ if (is_expanded && !set)
+ gtk_render_line(context, cr, 0, cell_area->y + cell_area->height,
+ allocation.width, cell_area->y + cell_area->height);
+#else
+ if (is_expanded && !set)
+ gtk_paint_hline(gtk_widget_get_style(widget), window, state, NULL, widget, NULL, 0,
+ allocation.width, cell_area->y + cell_area->height);
+#endif
}
-static gboolean pidgin_cell_renderer_expander_activate(GtkCellRenderer *r,
+static gboolean
+pidgin_cell_renderer_expander_activate(GtkCellRenderer *r,
GdkEvent *event,
GtkWidget *widget,
const gchar *p,
- GdkRectangle *bg,
- GdkRectangle *cell,
+ GTK3_CONST GdkRectangle *bg,
+ GTK3_CONST GdkRectangle *cell,
GtkCellRendererState flags)
{
GtkTreePath *path = gtk_tree_path_new_from_string(p);
diff --git a/pidgin/gtkcellrendererexpander.h b/pidgin/gtkcellrendererexpander.h
index 983a9bd84a..2ca4cbde85 100644
--- a/pidgin/gtkcellrendererexpander.h
+++ b/pidgin/gtkcellrendererexpander.h
@@ -1,4 +1,4 @@
-/* gtkxcellrendererexpander.h
+/*
* 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.
@@ -18,22 +18,24 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
*/
+
#ifndef _PIDGINCELLRENDEREREXPANDER_H_
#define _PIDGINCELLRENDEREREXPANDER_H_
+/**
+ * SECTION:gtkcellrendererexpander
+ * @section_id: pidgin-gtkcellrendererexpander
+ * @short_description: <filename>gtkcellrendererexpander.h</filename>
+ * @title: Cell Renderer Expander
+ */
#include <gtk/gtk.h>
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-
-#define PIDGIN_TYPE_GTK_CELL_RENDERER_EXPANDER (pidgin_cell_renderer_expander_get_type())
-#define PIDGIN_CELL_RENDERER_EXPANDER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PIDGIN_TYPE_GTK_CELL_RENDERER_EXPANDER, PidginCellRendererExpander))
+#define PIDGIN_TYPE_CELL_RENDERER_EXPANDER (pidgin_cell_renderer_expander_get_type())
+#define PIDGIN_CELL_RENDERER_EXPANDER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PIDGIN_TYPE_CELL_RENDERER_EXPANDER, PidginCellRendererExpander))
#define PIDGIN_CELL_RENDERER_EXPANDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PURPLE_TYPE_GTK_CELL_RENDERER_EXPANDER, PidginCellRendererExpanderClass))
-#define PIDGIN_IS_GTK_CELL_RENDERER_EXPANDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_GTK_CELL_RENDERER_EXPANDER))
-#define PIDGIN_IS_GTK_CELL_RENDERER_EXPANDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_GTK_CELL_RENDERER_EXPANDER))
-#define PIDGIN_CELL_RENDERER_EXPANDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_GTK_CELL_RENDERER_EXPANDER, PidginCellRendererExpanderClass))
+#define PIDGIN_IS_CELL_RENDERER_EXPANDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_CELL_RENDERER_EXPANDER))
+#define PIDGIN_IS_CELL_RENDERER_EXPANDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_CELL_RENDERER_EXPANDER))
+#define PIDGIN_CELL_RENDERER_EXPANDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_CELL_RENDERER_EXPANDER, PidginCellRendererExpanderClass))
typedef struct _PidginCellRendererExpander PidginCellRendererExpander;
typedef struct _PidginCellRendererExpanderClass PidginCellRendererExpanderClass;
@@ -48,12 +50,11 @@ struct _PidginCellRendererExpanderClass {
GtkCellRendererClass parent_class;
};
+G_BEGIN_DECLS
+
GType pidgin_cell_renderer_expander_get_type (void);
GtkCellRenderer *pidgin_cell_renderer_expander_new (void);
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
+G_END_DECLS
#endif /* _PIDGINCELLRENDEREREXPANDER_H_ */
diff --git a/pidgin/gtkcertmgr.c b/pidgin/gtkcertmgr.c
index 381ff2c72b..a492cf8877 100644
--- a/pidgin/gtkcertmgr.c
+++ b/pidgin/gtkcertmgr.c
@@ -1,8 +1,3 @@
-/*
- * @file gtkcertmgr.c GTK+ Certificate Manager API
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -35,6 +30,7 @@
#include "notify.h"
#include "request.h"
+#include "gtk3compat.h"
#include "gtkblist.h"
#include "gtkutils.h"
@@ -194,7 +190,7 @@ tls_peers_mgmt_import_ok_cb(gpointer data, const char *filename)
G_CALLBACK(tls_peers_mgmt_import_ok2_cb),
_("Cancel"),
G_CALLBACK(tls_peers_mgmt_import_cancel2_cb),
- NULL, NULL, NULL, /* No account/who/conv*/
+ NULL, /* No additional parameters */
crt /* Pass cert instance to callback*/
);
@@ -209,7 +205,7 @@ tls_peers_mgmt_import_ok_cb(gpointer data, const char *filename)
purple_notify_error(NULL,
_("Certificate Import Error"),
_("X.509 certificate import failed"),
- secondary);
+ secondary, NULL);
g_free(secondary);
}
}
@@ -224,7 +220,7 @@ tls_peers_mgmt_import_cb(GtkWidget *button, gpointer data)
FALSE, /* Not a save dialog */
G_CALLBACK(tls_peers_mgmt_import_ok_cb),
NULL, /* Do nothing if cancelled */
- NULL, NULL, NULL, NULL );/* No account,conv,etc. */
+ NULL, NULL); /* No extra parameters */
}
static void
@@ -244,7 +240,7 @@ tls_peers_mgmt_export_ok_cb(gpointer data, const char *filename)
purple_notify_error(NULL,
_("Certificate Export Error"),
_("X.509 certificate export failed"),
- secondary);
+ secondary, NULL);
g_free(secondary);
}
@@ -298,7 +294,7 @@ tls_peers_mgmt_export_cb(GtkWidget *button, gpointer data)
TRUE, /* Is a save dialog */
G_CALLBACK(tls_peers_mgmt_export_ok_cb),
G_CALLBACK(tls_peers_mgmt_export_cancel_cb),
- NULL, NULL, NULL, /* No account,conv,etc. */
+ NULL, /* No extra parameters */
crt); /* Pass the certificate on to the callback */
}
@@ -310,6 +306,7 @@ tls_peers_mgmt_info_cb(GtkWidget *button, gpointer data)
GtkTreeModel *model;
gchar *id;
PurpleCertificate *crt;
+ char *title;
/* See if things are selected */
if (!gtk_tree_selection_get_selected(select, &model, &iter)) {
@@ -326,10 +323,20 @@ tls_peers_mgmt_info_cb(GtkWidget *button, gpointer data)
g_return_if_fail(crt);
/* Fire the notification */
- purple_certificate_display_x509(crt);
+ title = g_strdup_printf(_("Certificate Information for %s"), id);
+ purple_request_certificate(tpm_dat, title, NULL, NULL, crt,
+ _("OK"), G_CALLBACK(purple_certificate_destroy),
+ _("Cancel"), G_CALLBACK(purple_certificate_destroy),
+ crt);
g_free(id);
- purple_certificate_destroy(crt);
+ g_free(title);
+}
+
+static void
+tls_peers_mgmt_activated_cb(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, gpointer data)
+{
+ tls_peers_mgmt_info_cb(NULL, NULL);
}
static void
@@ -371,7 +378,7 @@ tls_peers_mgmt_delete_cb(GtkWidget *button, gpointer data)
purple_request_yes_no(tpm_dat, _("Confirm certificate delete"),
primary, NULL, /* Can this be NULL? */
0, /* "yes" is the default action */
- NULL, NULL, NULL,
+ NULL,
id, /* id ownership passed to callback */
tls_peers_mgmt_delete_confirm_cb,
tls_peers_mgmt_delete_confirm_cb );
@@ -404,9 +411,8 @@ tls_peers_mgmt_build(void)
/* Create a struct to store context information about this window */
tpm_dat = g_new0(tls_peers_mgmt_data, 1);
- tpm_dat->mgmt_widget = mgmt_widget =
- gtk_hbox_new(FALSE, /* Non-homogeneous */
- PIDGIN_HIG_BOX_SPACE);
+ tpm_dat->mgmt_widget = mgmt_widget = gtk_box_new(
+ GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtk_container_set_border_width(GTK_CONTAINER(mgmt_widget),
PIDGIN_HIG_BOX_SPACE);
gtk_widget_show(mgmt_widget);
@@ -452,6 +458,9 @@ tls_peers_mgmt_build(void)
g_signal_connect(G_OBJECT(select), "changed",
G_CALLBACK(tls_peers_mgmt_select_chg_cb), NULL);
+ g_signal_connect(G_OBJECT(listview), "row-activated",
+ G_CALLBACK(tls_peers_mgmt_activated_cb), NULL);
+
gtk_box_pack_start(GTK_BOX(mgmt_widget),
pidgin_make_scrollable(GTK_WIDGET(listview), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, -1),
TRUE, TRUE, /* Take up lots of space */
@@ -462,7 +471,7 @@ tls_peers_mgmt_build(void)
tls_peers_mgmt_repopulate_list();
/* Right-hand side controls box */
- bbox = gtk_vbutton_box_new();
+ bbox = gtk_button_box_new(GTK_ORIENTATION_VERTICAL);
gtk_box_pack_end(GTK_BOX(mgmt_widget), bbox,
FALSE, FALSE, /* Do not take up space */
0);
@@ -471,9 +480,8 @@ tls_peers_mgmt_build(void)
gtk_widget_show(bbox);
/* Import button */
- /* TODO: This is the wrong stock button */
tpm_dat->importbutton = importbutton =
- gtk_button_new_from_stock(GTK_STOCK_ADD);
+ gtk_button_new_from_stock(GTK_STOCK_OPEN);
gtk_box_pack_start(GTK_BOX(bbox), importbutton, FALSE, FALSE, 0);
gtk_widget_show(importbutton);
g_signal_connect(G_OBJECT(importbutton), "clicked",
@@ -481,9 +489,8 @@ tls_peers_mgmt_build(void)
/* Export button */
- /* TODO: This is the wrong stock button */
tpm_dat->exportbutton = exportbutton =
- gtk_button_new_from_stock(GTK_STOCK_SAVE);
+ gtk_button_new_from_stock(GTK_STOCK_SAVE_AS);
gtk_box_pack_start(GTK_BOX(bbox), exportbutton, FALSE, FALSE, 0);
gtk_widget_show(exportbutton);
g_signal_connect(G_OBJECT(exportbutton), "clicked",
@@ -601,7 +608,11 @@ pidgin_certmgr_show(void)
win = dlg->window =
pidgin_create_dialog(_("Certificate Manager"),/* Title */
+#if GTK_CHECK_VERSION(3,0,0)
+ 0, /*Window border*/
+#else
PIDGIN_HIG_BORDER, /*Window border*/
+#endif
"certmgr", /* Role */
TRUE); /* Allow resizing */
g_signal_connect(G_OBJECT(win), "delete_event",
diff --git a/pidgin/gtkcertmgr.h b/pidgin/gtkcertmgr.h
index 8971be41e1..41f8f42061 100644
--- a/pidgin/gtkcertmgr.h
+++ b/pidgin/gtkcertmgr.h
@@ -1,7 +1,3 @@
-/**
- * @file gtkcertmgr.h GTK+ Certificate Manager API
- * @ingroup pidgin
- */
/*
* pidgin
*
@@ -27,13 +23,21 @@
#ifndef _PIDGINCERTMGR_H_
#define _PIDGINCERTMGR_H_
+/**
+ * SECTION:gtkcertmgr
+ * @section_id: pidgin-gtkcertmgr
+ * @short_description: <filename>gtkcertmgr.h</filename>
+ * @title: Certificate Manager API
+ */
/**************************************************************************
- * @name Structures *
+ * Structures *
**************************************************************************/
typedef struct _PidginCertificateManager PidginCertificateManager;
/**
+ * PidginCertificateManager:
+ *
* GTK+ Certificate Manager subwidget
*/
struct _PidginCertificateManager {
@@ -43,20 +47,26 @@ struct _PidginCertificateManager {
gchar *label;
};
+G_BEGIN_DECLS
+
/**************************************************************************/
-/** @name Certificate Manager API */
+/* Certificate Manager API */
/**************************************************************************/
-/*@{*/
+
/**
+ * pidgin_certmgr_show:
+ *
* Show the certificate manager window
*/
void pidgin_certmgr_show(void);
/**
+ * pidgin_certmgr_hide:
+ *
* Hide the certificate manager window
*/
void pidgin_certmgr_hide(void);
-/*@}*/
+G_END_DECLS
#endif /* _PIDGINCERTMGR_H_ */
diff --git a/pidgin/gtkconn.c b/pidgin/gtkconn.c
index 6fa8e3b085..ecb6db53a8 100644
--- a/pidgin/gtkconn.c
+++ b/pidgin/gtkconn.c
@@ -1,8 +1,3 @@
-/*
- * @file gtkconn.c GTK+ Connection API
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -49,7 +44,7 @@ typedef struct {
guint timeout;
} PidginAutoRecon;
-/**
+/*
* Contains accounts that are auto-reconnecting.
* The key is a pointer to the PurpleAccount and the
* value is a pointer to a PidginAutoRecon.
@@ -136,9 +131,9 @@ do_signon(gpointer data)
}
static void
-pidgin_connection_report_disconnect_reason (PurpleConnection *gc,
- PurpleConnectionError reason,
- const char *text)
+pidgin_connection_report_disconnect(PurpleConnection *gc,
+ PurpleConnectionError reason,
+ const char *text)
{
PurpleAccount *account = NULL;
PidginAutoRecon *info;
@@ -196,10 +191,7 @@ static void pidgin_connection_network_disconnected (void)
while (l) {
PurpleAccount *a = (PurpleAccount*)l->data;
if (!purple_account_is_disconnected(a)) {
- char *password = g_strdup(purple_account_get_password(a));
purple_account_disconnect(a);
- purple_account_set_password(a, password);
- g_free(password);
}
l = l->next;
}
@@ -215,10 +207,10 @@ static PurpleConnectionUiOps conn_ui_ops =
pidgin_connection_connected,
pidgin_connection_disconnected,
pidgin_connection_notice,
- NULL, /* report_disconnect */
pidgin_connection_network_connected,
pidgin_connection_network_disconnected,
- pidgin_connection_report_disconnect_reason,
+ pidgin_connection_report_disconnect,
+ NULL,
NULL,
NULL,
NULL
diff --git a/pidgin/gtkconn.h b/pidgin/gtkconn.h
index 9fda617259..02e446e2c3 100644
--- a/pidgin/gtkconn.h
+++ b/pidgin/gtkconn.h
@@ -1,7 +1,3 @@
-/**
- * @file gtkconn.h GTK+ Connection API
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -22,38 +18,54 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _PIDGINCONN_H_
#define _PIDGINCONN_H_
+/**
+ * SECTION:gtkconn
+ * @section_id: pidgin-gtkconn
+ * @short_description: <filename>gtkconn.h</filename>
+ * @title: Connection API
+ */
+
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name GTK+ Connection API */
+/* GTK+ Connection API */
/**************************************************************************/
-/*@{*/
/**
+ * pidgin_connections_get_ui_ops:
+ *
* Gets GTK+ Connection UI ops
*
- * @return UI operations struct
+ * Returns: UI operations struct
*/
PurpleConnectionUiOps *pidgin_connections_get_ui_ops(void);
-/*@}*/
-
/**
+ * pidgin_connection_get_handle:
+ *
* Returns the GTK+ connection handle.
*
- * @return The handle to the GTK+ connection system.
+ * Returns: The handle to the GTK+ connection system.
*/
void *pidgin_connection_get_handle(void);
/**
+ * pidgin_connection_init:
+ *
* Initializes the GTK+ connection system.
*/
void pidgin_connection_init(void);
/**
+ * pidgin_connection_uninit:
+ *
* Uninitializes the GTK+ connection system.
*/
void pidgin_connection_uninit(void);
+G_END_DECLS
+
#endif /* _PIDGINCONN_H_ */
diff --git a/pidgin/gtkconv-theme-loader.c b/pidgin/gtkconv-theme-loader.c
new file mode 100644
index 0000000000..b82dcd0373
--- /dev/null
+++ b/pidgin/gtkconv-theme-loader.c
@@ -0,0 +1,294 @@
+/*
+ * 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"
+
+/*****************************************************************************
+ * 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;
+}
+
diff --git a/pidgin/gtkconv-theme-loader.h b/pidgin/gtkconv-theme-loader.h
new file mode 100644
index 0000000000..d065f02d75
--- /dev/null
+++ b/pidgin/gtkconv-theme-loader.h
@@ -0,0 +1,76 @@
+/* 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"
+
+typedef struct _PidginConvThemeLoader PidginConvThemeLoader;
+typedef struct _PidginConvThemeLoaderClass PidginConvThemeLoaderClass;
+
+#define PIDGIN_TYPE_CONV_THEME_LOADER (pidgin_conversation_theme_loader_get_type ())
+#define PIDGIN_CONV_THEME_LOADER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_CONV_THEME_LOADER, PidginConvThemeLoader))
+#define PIDGIN_CONV_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_CONV_THEME_LOADER, PidginConvThemeLoaderClass))
+#define PIDGIN_IS_CONV_THEME_LOADER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_CONV_THEME_LOADER))
+#define PIDGIN_IS_CONV_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_CONV_THEME_LOADER))
+#define PIDGIN_CONV_THEME_LOADER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_CONV_THEME_LOADER, PidginConvThemeLoaderClass))
+
+/**
+ * PidginConvThemeLoader:
+ *
+ * A pidgin conversation theme loader. Extends PurpleThemeLoader (theme-loader.h)
+ * This is a class designed to build conversation themes
+ */
+struct _PidginConvThemeLoader
+{
+ PurpleThemeLoader parent;
+};
+
+struct _PidginConvThemeLoaderClass
+{
+ PurpleThemeLoaderClass parent_class;
+};
+
+/**************************************************************************/
+/* Pidgin Conversation Theme-Loader API */
+/**************************************************************************/
+G_BEGIN_DECLS
+
+/**
+ * pidgin_conversation_theme_loader_get_type:
+ *
+ * Returns: The #GType for a conversation theme loader.
+ */
+GType pidgin_conversation_theme_loader_get_type(void);
+
+G_END_DECLS
+
+#endif /* PIDGIN_CONV_THEME_LOADER_H */
+
diff --git a/pidgin/gtkconv-theme.c b/pidgin/gtkconv-theme.c
new file mode 100644
index 0000000000..48a1c75526
--- /dev/null
+++ b/pidgin/gtkconv-theme.c
@@ -0,0 +1,791 @@
+/*
+ * 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 "gtkwebview.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#define PIDGIN_CONV_THEME_GET_PRIVATE(Gobject) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((Gobject), PIDGIN_TYPE_CONV_THEME, PidginConvThemePrivate))
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+
+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 GObjectClass *parent_class = NULL;
+static GParamSpec *properties[PROP_LAST];
+
+/******************************************************************************
+ * 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);
+#if defined(_WIN32) && !defined(USE_WIN32_FHS)
+ file = g_build_filename(PURPLE_DATADIR,
+ "theme", "Template.html", NULL);
+#else
+ file = g_build_filename(PURPLE_DATADIR,
+ "pidgin", "theme", "Template.html", NULL);
+#endif
+ }
+
+ 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));
+ }
+
+ 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_conv_theme_init(GTypeInstance *instance,
+ gpointer klass)
+{
+#if 0
+ PidginConvThemePrivate *priv;
+
+ priv = PIDGIN_CONV_THEME_GET_PRIVATE(instance);
+#endif
+}
+
+static void
+pidgin_conv_theme_finalize(GObject *obj)
+{
+ PidginConvThemePrivate *priv;
+ GList *list;
+
+ priv = PIDGIN_CONV_THEME_GET_PRIVATE(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);
+
+ parent_class->finalize(obj);
+}
+
+static void
+pidgin_conv_theme_class_init(PidginConvThemeClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ g_type_class_add_private(klass, sizeof(PidginConvThemePrivate));
+
+ 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);
+}
+
+GType
+pidgin_conversation_theme_get_type(void)
+{
+ static GType type = 0;
+ if (type == 0) {
+ static const GTypeInfo info = {
+ sizeof(PidginConvThemeClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc)pidgin_conv_theme_class_init, /* class_init */
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof(PidginConvTheme),
+ 0, /* n_preallocs */
+ pidgin_conv_theme_init, /* instance_init */
+ NULL, /* value table */
+ };
+ type = g_type_register_static(PURPLE_TYPE_THEME,
+ "PidginConvTheme", &info, 0);
+ }
+ return type;
+}
+
+/*****************************************************************************
+ * Public API functions
+ *****************************************************************************/
+
+const GHashTable *
+pidgin_conversation_theme_get_info(const PidginConvTheme *theme)
+{
+ PidginConvThemePrivate *priv;
+
+ g_return_val_if_fail(theme != NULL, NULL);
+
+ priv = PIDGIN_CONV_THEME_GET_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_CONV_THEME_GET_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_CONV_THEME_GET_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_CONV_THEME_GET_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_CONV_THEME_GET_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_CONV_THEME_GET_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_CONV_THEME_GET_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_CONV_THEME_GET_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_CONV_THEME_GET_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_CONV_THEME_GET_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(GdkColor));
+ 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++)
+ {
+ GdkColor color;
+ if(gdk_color_parse(color_strings[i], &color))
+ {
+ 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;
+}
+
diff --git a/pidgin/gtkconv-theme.h b/pidgin/gtkconv-theme.h
new file mode 100644
index 0000000000..c06912a432
--- /dev/null
+++ b/pidgin/gtkconv-theme.h
@@ -0,0 +1,220 @@
+/* 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"
+
+typedef struct _PidginConvTheme PidginConvTheme;
+typedef struct _PidginConvThemeClass PidginConvThemeClass;
+
+#define PIDGIN_TYPE_CONV_THEME (pidgin_conversation_theme_get_type ())
+#define PIDGIN_CONV_THEME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_CONV_THEME, PidginConvTheme))
+#define PIDGIN_CONV_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_CONV_THEME, PidginConvThemeClass))
+#define PIDGIN_IS_CONV_THEME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_CONV_THEME))
+#define PIDGIN_IS_CONV_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_CONV_THEME))
+#define PIDGIN_CONV_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_CONV_THEME, PidginConvThemeClass))
+
+/**
+ * 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;
+};
+
+struct _PidginConvThemeClass
+{
+ PurpleThemeClass parent_class;
+};
+
+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.
+ */
+GType pidgin_conversation_theme_get_type(void);
+
+/**
+ * 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(const 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: 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: 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 */
+
diff --git a/pidgin/gtkconv.c b/pidgin/gtkconv.c
index 61280ed645..13e58da855 100644
--- a/pidgin/gtkconv.c
+++ b/pidgin/gtkconv.c
@@ -1,8 +1,3 @@
-/**
- * @file gtkconv.c GTK+ Conversation API
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -29,11 +24,8 @@
#include "internal.h"
#include "pidgin.h"
-#ifdef USE_GTKSPELL
-# include <gtkspell/gtkspell.h>
-# ifdef _WIN32
-# include "wspell.h"
-# endif
+#ifndef _WIN32
+# include <X11/Xlib.h>
#endif
#include <gdk/gdkkeysyms.h>
@@ -42,34 +34,77 @@
#include "cmds.h"
#include "core.h"
#include "debug.h"
+#include "glibcompat.h"
#include "idle.h"
-#include "imgstore.h"
+#include "image-store.h"
#include "log.h"
#include "notify.h"
#include "prpl.h"
#include "request.h"
+#include "smiley-parser.h"
+#include "theme-loader.h"
+#include "theme-manager.h"
#include "util.h"
#include "version.h"
+#include "gtkinternal.h"
#include "gtkdnd-hints.h"
#include "gtkblist.h"
#include "gtkconv.h"
#include "gtkconvwin.h"
+#include "gtkconv-theme.h"
+#include "gtkconv-theme-loader.h"
#include "gtkdialogs.h"
-#include "gtkimhtml.h"
-#include "gtkimhtmltoolbar.h"
#include "gtklog.h"
#include "gtkmenutray.h"
#include "gtkpounce.h"
#include "gtkprefs.h"
#include "gtkprivacy.h"
-#include "gtkthemes.h"
#include "gtkutils.h"
+#include "gtkwebview.h"
#include "pidginstock.h"
#include "pidgintooltip.h"
#include "gtknickcolors.h"
+#define GTK_TOOLTIPS_VAR gtkconv->tooltips
+#include "gtk3compat.h"
+
+#define ADD_MESSAGE_HISTORY_AT_ONCE 100
+
+/*
+ * A GTK+ Instant Message pane.
+ */
+struct _PidginImPane
+{
+ GtkWidget *block;
+ GtkWidget *send_file;
+ GtkWidget *sep1;
+ GtkWidget *sep2;
+ GtkWidget *check;
+ GtkWidget *progress;
+ guint32 typing_timer;
+
+ /* Buddy icon stuff */
+ GtkWidget *icon_container;
+ GtkWidget *icon;
+ gboolean show_icon;
+ gboolean animate;
+ GdkPixbufAnimation *anim;
+ GdkPixbufAnimationIter *iter;
+ guint32 icon_timer;
+};
+
+/*
+ * GTK+ Chat panes.
+ */
+struct _PidginChatPane
+{
+ GtkWidget *count;
+ GtkWidget *list;
+ GtkWidget *topic_text;
+};
+
#define CLOSE_CONV_TIMEOUT_SECS (10 * 60)
#define AUTO_RESPONSE "&lt;AUTO-REPLY&gt; : "
@@ -82,7 +117,8 @@ typedef enum
PIDGIN_CONV_TAB_ICON = 1 << 3,
PIDGIN_CONV_TOPIC = 1 << 4,
PIDGIN_CONV_SMILEY_THEME = 1 << 5,
- PIDGIN_CONV_COLORIZE_TITLE = 1 << 6
+ PIDGIN_CONV_COLORIZE_TITLE = 1 << 6,
+ PIDGIN_CONV_E2EE = 1 << 7
}PidginConvFields;
enum {
@@ -103,18 +139,29 @@ enum {
#define BUDDYICON_SIZE_MIN 32
#define BUDDYICON_SIZE_MAX 96
-/* Undef this to turn off "custom-smiley" debug messages */
-#define DEBUG_CUSTOM_SMILEY
-
#define LUMINANCE(c) (float)((0.3*(c.red))+(0.59*(c.green))+(0.11*(c.blue)))
-/* From http://www.w3.org/TR/AERT#color-contrast */
-#define MIN_BRIGHTNESS_CONTRAST 75
-#define MIN_COLOR_CONTRAST 200
+/* From http://www.w3.org/TR/AERT#color-contrast
+ * Range for color difference is 500
+ * Range for brightness is 125
+ */
+#define MIN_BRIGHTNESS_CONTRAST 85
+#define MIN_COLOR_CONTRAST 250
+
+#define NICK_COLOR_GENERATE_COUNT 220
+static GArray *generated_nick_colors = NULL;
+
+/* These probably won't conflict with any WebKit values. */
+#define PIDGIN_DRAG_BLIST_NODE (1337)
+#define PIDGIN_DRAG_IM_CONTACT (31337)
-#define NUM_NICK_COLORS 220
-static GdkColor *nick_colors = NULL;
-static guint nbr_nick_colors;
+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;
typedef struct {
GtkWidget *window;
@@ -122,14 +169,14 @@ typedef struct {
GtkWidget *entry;
GtkWidget *message;
- PurpleConversation *conv;
+ PurpleChatConversation *chat;
} InviteBuddyInfo;
static GtkWidget *invite_dialog = NULL;
static GtkWidget *warn_close_dialog = NULL;
-static PidginWindow *hidden_convwin = NULL;
+static PidginConvWindow *hidden_convwin = NULL;
static GList *window_list = NULL;
/* Lists of status icons at all available sizes for use as window icons */
@@ -139,72 +186,70 @@ static GList *busy_list = NULL;
static GList *xa_list = NULL;
static GList *offline_list = NULL;
static GHashTable *prpl_lists = NULL;
+static GHashTable *e2ee_stock = NULL;
-static gboolean update_send_to_selection(PidginWindow *win);
-static void generate_send_to_items(PidginWindow *win);
+static PurpleTheme *default_conv_theme = NULL;
+
+static GRegex *image_store_tag_re = 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_buddy_common(PurpleConversation *conv, PurpleConvChatBuddy *cb, const char *old_name);
+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, PurpleConvUpdateType type);
+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);
static void update_typing_icon(PidginConversation *gtkconv);
static void update_typing_message(PidginConversation *gtkconv, const char *message);
-static const char *item_factory_translate_func (const char *path, gpointer func_data);
gboolean pidgin_conv_has_focus(PurpleConversation *conv);
-static GdkColor* generate_nick_colors(guint *numcolors, GdkColor background);
-static gboolean color_is_visible(GdkColor foreground, GdkColor background, int color_contrast, int brightness_contrast);
-static GtkTextTag *get_buddy_tag(PurpleConversation *conv, const char *who, PurpleMessageFlags flag, gboolean create);
+static GArray* generate_nick_colors(guint numcolors, GdkColor background);
+static gboolean color_is_visible(GdkColor foreground, GdkColor background, guint color_contrast, guint brightness_contrast);
+static GtkTextTag *get_buddy_tag(PurpleChatConversation *chat, const char *who, PurpleMessageFlags flag, gboolean create);
static void pidgin_conv_update_fields(PurpleConversation *conv, PidginConvFields fields);
-static void focus_out_from_menubar(GtkWidget *wid, PidginWindow *win);
-static void pidgin_conv_tab_pack(PidginWindow *win, PidginConversation *gtkconv);
+static void focus_out_from_menubar(GtkWidget *wid, PidginConvWindow *win);
+static void pidgin_conv_tab_pack(PidginConvWindow *win, PidginConversation *gtkconv);
static gboolean infopane_press_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *conv);
static void hide_conv(PidginConversation *gtkconv, gboolean closetimer);
-static void pidgin_conv_set_position_size(PidginWindow *win, int x, int y,
+static void pidgin_conv_set_position_size(PidginConvWindow *win, int x, int y,
int width, int height);
-static gboolean pidgin_conv_xy_to_right_infopane(PidginWindow *win, int x, int y);
+static gboolean pidgin_conv_xy_to_right_infopane(PidginConvWindow *win, int x, int y);
-static const GdkColor *get_nick_color(PidginConversation *gtkconv, const char *name)
+static const GdkColor *
+get_nick_color(PidginConversation *gtkconv, const gchar *name)
{
static GdkColor col;
- GtkStyle *style = gtk_widget_get_style(gtkconv->imhtml);
- float scale;
- col = nick_colors[g_str_hash(name) % nbr_nick_colors];
- scale = ((1-(LUMINANCE(style->base[GTK_STATE_NORMAL]) / LUMINANCE(style->white))) *
- (LUMINANCE(style->white)/MAX(MAX(col.red, col.blue), col.green)));
-
- /* The colors are chosen to look fine on white; we should never have to darken */
- if (scale > 1) {
- col.red *= scale;
- col.green *= scale;
- col.blue *= scale;
+ if (name == NULL) {
+ col.red = col.green = col.blue = 0;
+ return &col;
}
+ col = g_array_index(gtkconv->nick_colors, GdkColor,
+ g_str_hash(name) % gtkconv->nick_colors->len);
+
return &col;
}
static PurpleBlistNode *
get_conversation_blist_node(PurpleConversation *conv)
{
+ PurpleAccount *account = purple_conversation_get_account(conv);
PurpleBlistNode *node = NULL;
- switch (purple_conversation_get_type(conv)) {
- case PURPLE_CONV_TYPE_IM:
- node = PURPLE_BLIST_NODE(purple_find_buddy(conv->account, conv->name));
- node = node ? node->parent : NULL;
- break;
- case PURPLE_CONV_TYPE_CHAT:
- node = PURPLE_BLIST_NODE(purple_blist_find_chat(conv->account, conv->name));
- break;
- default:
- break;
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
+ node = PURPLE_BLIST_NODE(purple_blist_find_buddy(account, purple_conversation_get_name(conv)));
+ node = node ? node->parent : NULL;
+ } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
+ node = PURPLE_BLIST_NODE(purple_blist_find_chat(account, purple_conversation_get_name(conv)));
}
+
return node;
}
@@ -217,7 +262,7 @@ close_this_sucker(gpointer data)
{
PidginConversation *gtkconv = data;
GList *list = g_list_copy(gtkconv->convs);
- g_list_foreach(list, (GFunc)purple_conversation_destroy, NULL);
+ g_list_foreach(list, (GFunc)g_object_unref, NULL);
g_list_free(list);
return FALSE;
}
@@ -234,27 +279,18 @@ close_conv_cb(GtkButton *button, PidginConversation *gtkconv)
PurpleAccount *account = purple_conversation_get_account(conv);
const char *name = purple_conversation_get_name(conv);
- switch (purple_conversation_get_type(conv)) {
- case PURPLE_CONV_TYPE_IM:
- {
- if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/close_immediately"))
- close_this_sucker(gtkconv);
- else
- hide_conv(gtkconv, TRUE);
- break;
- }
- case PURPLE_CONV_TYPE_CHAT:
- {
- PurpleChat *chat = purple_blist_find_chat(account, name);
- if (!chat ||
- !purple_blist_node_get_bool(&chat->node, "gtk-persistent"))
- close_this_sucker(gtkconv);
- else
- hide_conv(gtkconv, FALSE);
- break;
- }
- default:
- ;
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
+ if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/close_immediately"))
+ close_this_sucker(gtkconv);
+ else
+ hide_conv(gtkconv, TRUE);
+ } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
+ PurpleChat *chat = purple_blist_find_chat(account, name);
+ if (!chat ||
+ !purple_blist_node_get_bool(&chat->node, "gtk-persistent"))
+ close_this_sucker(gtkconv);
+ else
+ hide_conv(gtkconv, FALSE);
}
return TRUE;
@@ -272,20 +308,28 @@ static void
default_formatize(PidginConversation *c)
{
PurpleConversation *conv = c->active_conv;
- gtk_imhtml_setup_entry(GTK_IMHTML(c->entry), conv->features);
+ pidgin_webview_setup_entry(PIDGIN_WEBVIEW(c->entry), purple_conversation_get_features(conv));
}
static void
conversation_entry_clear(PidginConversation *gtkconv)
{
- GtkIMHtml *imhtml = GTK_IMHTML(gtkconv->entry);
- gtk_source_undo_manager_begin_not_undoable_action(imhtml->undo_manager);
- gtk_imhtml_clear(imhtml);
- gtk_source_undo_manager_end_not_undoable_action(imhtml->undo_manager);
+ 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(GtkIMHtml *imhtml, PidginConversation *gtkconv)
+clear_formatting_cb(PidginWebView *webview, PidginConversation *gtkconv)
{
default_formatize(gtkconv);
}
@@ -300,10 +344,7 @@ static PurpleCmdRet
say_command_cb(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
- purple_conv_im_send(PURPLE_CONV_IM(conv), args[0]);
- else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
- purple_conv_chat_send(PURPLE_CONV_CHAT(conv), args[0]);
+ purple_conversation_send(conv, args[0]);
return PURPLE_CMD_RET_OK;
}
@@ -315,11 +356,7 @@ me_command_cb(PurpleConversation *conv,
char *tmp;
tmp = g_strdup_printf("/me %s", args[0]);
-
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
- purple_conv_im_send(PURPLE_CONV_IM(conv), tmp);
- else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
- purple_conv_chat_send(PURPLE_CONV_CHAT(conv), tmp);
+ purple_conversation_send(conv, tmp);
g_free(tmp);
return PURPLE_CMD_RET_OK;
@@ -352,17 +389,43 @@ debug_command_cb(PurpleConversation *conv,
}
tmp = g_string_free(str, FALSE);
+ } else if (!g_ascii_strcasecmp(args[0], "unsafe")) {
+ if (purple_debug_is_unsafe()) {
+ purple_debug_set_unsafe(FALSE);
+ purple_conversation_write_system_message(conv,
+ _("Unsafe debugging is now disabled."),
+ PURPLE_MESSAGE_NO_LOG);
+ } else {
+ purple_debug_set_unsafe(TRUE);
+ purple_conversation_write_system_message(conv,
+ _("Unsafe debugging is now enabled."),
+ PURPLE_MESSAGE_NO_LOG);
+ }
+
+ return PURPLE_CMD_RET_OK;
+ } else if (!g_ascii_strcasecmp(args[0], "verbose")) {
+ if (purple_debug_is_verbose()) {
+ purple_debug_set_verbose(FALSE);
+ purple_conversation_write_system_message(conv,
+ _("Verbose debugging is now disabled."),
+ PURPLE_MESSAGE_NO_LOG);
+ } else {
+ purple_debug_set_verbose(TRUE);
+ purple_conversation_write_system_message(conv,
+ _("Verbose debugging is now enabled."),
+ PURPLE_MESSAGE_NO_LOG);
+ }
+
+ return PURPLE_CMD_RET_OK;
} else {
- purple_conversation_write(conv, NULL, _("Supported debug options are: plugins version"),
- PURPLE_MESSAGE_NO_LOG|PURPLE_MESSAGE_ERROR, time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("Supported debug options are: plugins version unsafe verbose"),
+ PURPLE_MESSAGE_NO_LOG);
return PURPLE_CMD_RET_OK;
}
markup = g_markup_escape_text(tmp, -1);
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
- purple_conv_im_send(PURPLE_CONV_IM(conv), markup);
- else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
- purple_conv_chat_send(PURPLE_CONV_CHAT(conv), markup);
+ purple_conversation_send(conv, markup);
g_free(tmp);
g_free(markup);
@@ -372,11 +435,12 @@ debug_command_cb(PurpleConversation *conv,
static void clear_conversation_scrollback_cb(PurpleConversation *conv,
void *data)
{
- PidginConversation *gtkconv = NULL;
+ PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
- gtkconv = PIDGIN_CONVERSATION(conv);
- if (gtkconv)
- gtk_imhtml_clear(GTK_IMHTML(gtkconv->imhtml));
+ if (PIDGIN_CONVERSATION(conv)) {
+ load_conv_theme(gtkconv);
+ gtkconv->last_flags = 0;
+ }
}
static PurpleCmdRet
@@ -391,7 +455,10 @@ static PurpleCmdRet
clearall_command_cb(PurpleConversation *conv,
const char *cmd, char **args, char **error, void *data)
{
- purple_conversation_foreach(purple_conversation_clear_message_history);
+ GList *l;
+ for (l = purple_conversations_get_all(); l != NULL; l = l->next)
+ purple_conversation_clear_message_history(PURPLE_CONVERSATION(l->data));
+
return PURPLE_CMD_RET_OK;
}
@@ -416,8 +483,8 @@ help_command_cb(PurpleConversation *conv,
g_string_append(s, _("No such command (in this context)."));
}
} else {
- s = g_string_new(_("Use \"/help &lt;command&gt;\" for help on a specific command.\n"
- "The following commands are available in this context:\n"));
+ s = g_string_new(_("Use \"/help &lt;command&gt;\" for help on a specific command.<br/>"
+ "The following commands are available in this context:<br/>"));
text = purple_cmd_list(conv);
for (l = text; l; l = l->next)
@@ -428,7 +495,7 @@ help_command_cb(PurpleConversation *conv,
g_list_free(text);
}
- purple_conversation_write(conv, NULL, s->str, PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(conv, s->str, PURPLE_MESSAGE_NO_LOG);
g_string_free(s, TRUE);
return PURPLE_CMD_RET_OK;
@@ -451,37 +518,32 @@ check_for_and_do_command(PurpleConversation *conv)
PidginConversation *gtkconv;
char *cmd;
const char *prefix;
- GtkTextIter start;
gboolean retval = FALSE;
gtkconv = PIDGIN_CONVERSATION(conv);
prefix = pidgin_get_cmd_prefix();
- cmd = gtk_imhtml_get_text(GTK_IMHTML(gtkconv->entry), NULL, NULL);
- gtk_text_buffer_get_start_iter(GTK_IMHTML(gtkconv->entry)->text_buffer, &start);
+ cmd = pidgin_webview_get_body_text(PIDGIN_WEBVIEW(gtkconv->entry));
- if (cmd && (strncmp(cmd, prefix, strlen(prefix)) == 0)
- && !gtk_text_iter_get_child_anchor(&start)) {
+ if (cmd && purple_str_has_prefix(cmd, prefix)) {
PurpleCmdStatus status;
char *error, *cmdline, *markup, *send_history;
- GtkTextIter end;
- send_history = gtk_imhtml_get_markup(GTK_IMHTML(gtkconv->entry));
+ send_history = pidgin_webview_get_body_html(PIDGIN_WEBVIEW(gtkconv->entry));
send_history_add(gtkconv, send_history);
g_free(send_history);
cmdline = cmd + strlen(prefix);
if (strcmp(cmdline, "xyzzy") == 0) {
- purple_conversation_write(conv, "", "Nothing happens",
- PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(conv,
+ "Nothing happens", PURPLE_MESSAGE_NO_LOG);
g_free(cmd);
return TRUE;
}
- gtk_text_iter_forward_chars(&start, g_utf8_strlen(prefix, -1));
- gtk_text_buffer_get_end_iter(GTK_IMHTML(gtkconv->entry)->text_buffer, &end);
- markup = gtk_imhtml_get_markup_range(GTK_IMHTML(gtkconv->entry), &start, &end);
+ /* TODO WebKit: Cut out prefix for markup... */
+ markup = pidgin_webview_get_body_html(PIDGIN_WEBVIEW(gtkconv->entry));
status = purple_cmd_do_command(conv, cmdline, markup, &error);
g_free(markup);
@@ -494,8 +556,8 @@ check_for_and_do_command(PurpleConversation *conv)
PurplePluginProtocolInfo *prpl_info = NULL;
PurpleConnection *gc;
- if ((gc = purple_conversation_get_gc(conv)))
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ if ((gc = purple_conversation_get_connection(conv)))
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
if ((prpl_info != NULL) && (prpl_info->options & OPT_PROTO_SLASH_COMMANDS_NATIVE)) {
char *spaceslash;
@@ -507,42 +569,49 @@ check_for_and_do_command(PurpleConversation *conv)
spaceslash++;
if (*spaceslash != '/') {
- purple_conversation_write(conv, "", _("Unknown command."), PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("Unknown command."), PURPLE_MESSAGE_NO_LOG);
retval = TRUE;
}
}
break;
}
case PURPLE_CMD_STATUS_WRONG_ARGS:
- purple_conversation_write(conv, "", _("Syntax Error: You typed the wrong number of arguments "
- "to that command."),
- PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("Syntax Error: You typed the wrong "
+ "number of arguments to that command."),
+ PURPLE_MESSAGE_NO_LOG);
retval = TRUE;
break;
case PURPLE_CMD_STATUS_FAILED:
- purple_conversation_write(conv, "", error ? error : _("Your command failed for an unknown reason."),
- PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(conv,
+ error ? error : _("Your command failed for an unknown reason."),
+ PURPLE_MESSAGE_NO_LOG);
g_free(error);
retval = TRUE;
break;
case PURPLE_CMD_STATUS_WRONG_TYPE:
- if(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
- purple_conversation_write(conv, "", _("That command only works in chats, not IMs."),
- PURPLE_MESSAGE_NO_LOG, time(NULL));
+ if(PURPLE_IS_IM_CONVERSATION(conv))
+ purple_conversation_write_system_message(conv,
+ _("That command only works in chats, not IMs."),
+ PURPLE_MESSAGE_NO_LOG);
else
- purple_conversation_write(conv, "", _("That command only works in IMs, not chats."),
- PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("That command only works in IMs, not chats."),
+ PURPLE_MESSAGE_NO_LOG);
retval = TRUE;
break;
case PURPLE_CMD_STATUS_WRONG_PRPL:
- purple_conversation_write(conv, "", _("That command doesn't work on this protocol."),
- PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("That command doesn't work on this protocol."),
+ PURPLE_MESSAGE_NO_LOG);
retval = TRUE;
break;
}
}
g_free(cmd);
+
return retval;
}
@@ -551,9 +620,11 @@ send_cb(GtkWidget *widget, PidginConversation *gtkconv)
{
PurpleConversation *conv = gtkconv->active_conv;
PurpleAccount *account;
+#if 0
PurpleConnection *gc;
+#endif
PurpleMessageFlags flags = 0;
- char *buf, *clean;
+ char *buf;
account = purple_conversation_get_account(conv);
@@ -562,59 +633,53 @@ send_cb(GtkWidget *widget, PidginConversation *gtkconv)
return;
}
- if ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) &&
- purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)))
+ if (PURPLE_IS_CHAT_CONVERSATION(conv) &&
+ purple_chat_conversation_has_left(PURPLE_CHAT_CONVERSATION(conv)))
return;
if (!purple_account_is_connected(account))
return;
- buf = gtk_imhtml_get_markup(GTK_IMHTML(gtkconv->entry));
- clean = gtk_imhtml_get_text(GTK_IMHTML(gtkconv->entry), NULL, NULL);
+ if (pidgin_webview_is_empty(PIDGIN_WEBVIEW(gtkconv->entry)))
+ return;
+
+ buf = pidgin_webview_get_body_html(PIDGIN_WEBVIEW(gtkconv->entry));
+ g_return_if_fail(buf != NULL);
gtk_widget_grab_focus(gtkconv->entry);
- if (strlen(clean) == 0) {
- g_free(buf);
- g_free(clean);
- return;
- }
-
purple_idle_touch();
/* XXX: is there a better way to tell if the message has images? */
- if (GTK_IMHTML(gtkconv->entry)->im_images != NULL)
+ if (strstr(buf, "<img ") != NULL)
flags |= PURPLE_MESSAGE_IMAGES;
+#if 0
gc = purple_account_get_connection(account);
- if (gc && (conv->features & PURPLE_CONNECTION_NO_NEWLINES)) {
+ if (gc && (purple_conversation_get_features(conv) & PURPLE_CONNECTION_FLAG_NO_NEWLINES)) {
+ /* TODO WebKit */
char **bufs;
int i;
- bufs = gtk_imhtml_get_markup_lines(GTK_IMHTML(gtkconv->entry));
+ bufs = pidgin_webview_get_markup_lines(PIDGIN_WEBVIEW(gtkconv->entry));
for (i = 0; bufs[i]; i++) {
send_history_add(gtkconv, bufs[i]);
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
- purple_conv_im_send_with_flags(PURPLE_CONV_IM(conv), bufs[i], flags);
- else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
- purple_conv_chat_send_with_flags(PURPLE_CONV_CHAT(conv), bufs[i], flags);
+ purple_conversation_send_with_flags(conv, bufs[i], flags);
}
g_strfreev(bufs);
-
- } else {
+ } else
+#endif
+ {
send_history_add(gtkconv, buf);
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
- purple_conv_im_send_with_flags(PURPLE_CONV_IM(conv), buf, flags);
- else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
- purple_conv_chat_send_with_flags(PURPLE_CONV_CHAT(conv), buf, flags);
+ purple_conversation_send_with_flags(conv, buf, flags);
}
- g_free(clean);
g_free(buf);
conversation_entry_clear(gtkconv);
gtkconv_set_unseen(gtkconv, PIDGIN_UNSEEN_NONE);
+ gtk_widget_grab_focus(gtkconv->entry); // XXX: doesn't work
}
static void
@@ -627,15 +692,15 @@ add_remove_cb(GtkWidget *widget, PidginConversation *gtkconv)
account = purple_conversation_get_account(conv);
name = purple_conversation_get_name(conv);
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
PurpleBuddy *b;
- b = purple_find_buddy(account, name);
+ b = purple_blist_find_buddy(account, name);
if (b != NULL)
pidgin_dialogs_remove_buddy(b);
else if (account != NULL && purple_account_is_connected(account))
purple_blist_request_add_buddy(account, (char *)name, NULL, NULL);
- } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
+ } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
PurpleChat *c;
c = purple_blist_find_chat(account, name);
@@ -650,11 +715,11 @@ add_remove_cb(GtkWidget *widget, PidginConversation *gtkconv)
static void chat_do_info(PidginConversation *gtkconv, const char *who)
{
- PurpleConversation *conv = gtkconv->active_conv;
+ PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(gtkconv->active_conv);
PurpleConnection *gc;
- if ((gc = purple_conversation_get_gc(conv))) {
- pidgin_retrieve_user_info_in_chat(gc, who, purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)));
+ if ((gc = purple_conversation_get_connection(gtkconv->active_conv))) {
+ pidgin_retrieve_user_info_in_chat(gc, who, purple_chat_conversation_get_id(chat));
}
}
@@ -664,11 +729,11 @@ info_cb(GtkWidget *widget, PidginConversation *gtkconv)
{
PurpleConversation *conv = gtkconv->active_conv;
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
- pidgin_retrieve_user_info(purple_conversation_get_gc(conv),
+ 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_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
+ } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
/* Get info of the person currently selected in the GtkTreeView */
PidginChatPane *gtkchat;
GtkTreeIter iter;
@@ -742,9 +807,9 @@ static void
do_invite(GtkWidget *w, int resp, InviteBuddyInfo *info)
{
const char *buddy, *message;
- PurpleConversation *conv;
+ PurpleChatConversation *chat;
- conv = info->conv;
+ chat = info->chat;
if (resp == GTK_RESPONSE_OK) {
buddy = gtk_entry_get_text(GTK_ENTRY(info->entry));
@@ -753,8 +818,8 @@ do_invite(GtkWidget *w, int resp, InviteBuddyInfo *info)
if (!g_ascii_strcasecmp(buddy, ""))
return;
- serv_chat_invite(purple_conversation_get_gc(conv),
- purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)),
+ purple_serv_chat_invite(purple_conversation_get_connection(PURPLE_CONVERSATION(chat)),
+ purple_chat_conversation_get_id(chat),
message, buddy);
}
@@ -766,60 +831,67 @@ do_invite(GtkWidget *w, int resp, InviteBuddyInfo *info)
static void
invite_dnd_recv(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
- GtkSelectionData *sd, guint inf, guint t, gpointer data)
+ GtkSelectionData *sd, guint dnd_info, guint t, gpointer data)
{
InviteBuddyInfo *info = (InviteBuddyInfo *)data;
const char *convprotocol;
gboolean success = TRUE;
- convprotocol = purple_account_get_protocol_id(purple_conversation_get_account(info->conv));
+ convprotocol = purple_account_get_protocol_id(
+ purple_conversation_get_account(PURPLE_CONVERSATION(info->chat)));
- if (sd->target == gdk_atom_intern("PURPLE_BLIST_NODE", FALSE))
+ if (dnd_info == PIDGIN_DRAG_BLIST_NODE)
{
PurpleBlistNode *node = NULL;
PurpleBuddy *buddy;
+ const guchar *data = gtk_selection_data_get_data(sd);
- memcpy(&node, sd->data, sizeof(node));
+ memcpy(&node, data, sizeof(node));
- if (PURPLE_BLIST_NODE_IS_CONTACT(node))
+ if (PURPLE_IS_CONTACT(node))
buddy = purple_contact_get_priority_buddy((PurpleContact *)node);
- else if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+ else if (PURPLE_IS_BUDDY(node))
buddy = (PurpleBuddy *)node;
else
return;
- if (strcmp(convprotocol, purple_account_get_protocol_id(buddy->account)))
+ if (strcmp(convprotocol, purple_account_get_protocol_id(purple_buddy_get_account(buddy))))
{
- purple_notify_error(PIDGIN_CONVERSATION(info->conv), NULL,
- _("That buddy is not on the same protocol as this "
- "chat."), NULL);
+ purple_notify_error(PIDGIN_CONVERSATION(PURPLE_CONVERSATION(info->chat)),
+ NULL, _("That buddy is not on the same protocol"
+ " as this chat."), NULL,
+ purple_request_cpar_from_conversation(PURPLE_CONVERSATION(info->chat)));
success = FALSE;
}
else
gtk_entry_set_text(GTK_ENTRY(info->entry), purple_buddy_get_name(buddy));
- gtk_drag_finish(dc, success, (dc->action == GDK_ACTION_MOVE), t);
+ gtk_drag_finish(dc, success,
+ gdk_drag_context_get_actions(dc) == GDK_ACTION_MOVE, t);
}
- else if (sd->target == gdk_atom_intern("application/x-im-contact", FALSE))
+ else if (dnd_info == PIDGIN_DRAG_IM_CONTACT)
{
char *protocol = NULL;
char *username = NULL;
PurpleAccount *account;
- if (pidgin_parse_x_im_contact((const char *)sd->data, FALSE, &account,
+ if (pidgin_parse_x_im_contact((const char *) data, FALSE, &account,
&protocol, &username, NULL))
{
if (account == NULL)
{
- purple_notify_error(PIDGIN_CONVERSATION(info->conv), NULL,
+ purple_notify_error(PIDGIN_CONVERSATION(PURPLE_CONVERSATION(info->chat)), NULL,
_("You are not currently signed on with an account that "
- "can invite that buddy."), NULL);
+ "can invite that buddy."), NULL, NULL);
}
else if (strcmp(convprotocol, purple_account_get_protocol_id(account)))
{
- purple_notify_error(PIDGIN_CONVERSATION(info->conv), NULL,
- _("That buddy is not on the same protocol as this "
- "chat."), NULL);
+ purple_notify_error(
+ PIDGIN_CONVERSATION(PURPLE_CONVERSATION(info->chat)), NULL,
+ _("That buddy is not on the same "
+ "protocol as this chat."), NULL,
+ purple_request_cpar_from_account(
+ account));
success = FALSE;
}
else
@@ -831,24 +903,19 @@ invite_dnd_recv(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
g_free(username);
g_free(protocol);
- gtk_drag_finish(dc, success, (dc->action == GDK_ACTION_MOVE), t);
+ gtk_drag_finish(dc, success,
+ gdk_drag_context_get_actions(dc) == GDK_ACTION_MOVE, t);
}
}
-static const GtkTargetEntry dnd_targets[] =
-{
- {"PURPLE_BLIST_NODE", GTK_TARGET_SAME_APP, 0},
- {"application/x-im-contact", 0, 1}
-};
-
static void
invite_cb(GtkWidget *widget, PidginConversation *gtkconv)
{
- PurpleConversation *conv = gtkconv->active_conv;
+ PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(gtkconv->active_conv);
InviteBuddyInfo *info = NULL;
if (invite_dialog == NULL) {
- PidginWindow *gtkwin;
+ PidginConvWindow *gtkwin;
GtkWidget *label;
GtkWidget *vbox, *hbox;
GtkWidget *table;
@@ -858,7 +925,7 @@ invite_cb(GtkWidget *widget, PidginConversation *gtkconv)
gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
info = g_new0(InviteBuddyInfo, 1);
- info->conv = conv;
+ info->chat = chat;
gtkwin = pidgin_conv_get_window(gtkconv);
@@ -873,24 +940,23 @@ invite_cb(GtkWidget *widget, PidginConversation *gtkconv)
GTK_RESPONSE_OK);
gtk_container_set_border_width(GTK_CONTAINER(invite_dialog), PIDGIN_HIG_BOX_SPACE);
gtk_window_set_resizable(GTK_WINDOW(invite_dialog), FALSE);
- gtk_dialog_set_has_separator(GTK_DIALOG(invite_dialog), FALSE);
info->window = GTK_WIDGET(invite_dialog);
/* Setup the outside spacing. */
- vbox = GTK_DIALOG(invite_dialog)->vbox;
+ vbox = gtk_dialog_get_content_area(GTK_DIALOG(invite_dialog));
gtk_box_set_spacing(GTK_BOX(vbox), PIDGIN_HIG_BORDER);
gtk_container_set_border_width(GTK_CONTAINER(vbox), PIDGIN_HIG_BOX_SPACE);
/* Setup the inner hbox and put the dialog's icon in it. */
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BORDER);
gtk_container_add(GTK_CONTAINER(vbox), hbox);
gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
/* Setup the right vbox. */
- vbox = gtk_vbox_new(FALSE, 0);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add(GTK_CONTAINER(hbox), vbox);
/* Put our happy label in it. */
@@ -903,7 +969,7 @@ invite_cb(GtkWidget *widget, PidginConversation *gtkconv)
gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
/* hbox for the table, and to give it some spacing on the left. */
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtk_container_add(GTK_CONTAINER(vbox), hbox);
/* Setup the table we're going to use to lay stuff out. */
@@ -921,8 +987,8 @@ invite_cb(GtkWidget *widget, PidginConversation *gtkconv)
/* Now the Buddy drop-down entry field. */
info->entry = gtk_entry_new();
- pidgin_setup_screenname_autocomplete_with_filter(info->entry, NULL, chat_invite_filter,
- purple_conversation_get_account(conv));
+ pidgin_setup_screenname_autocomplete(info->entry, NULL, chat_invite_filter,
+ purple_conversation_get_account(PURPLE_CONVERSATION(chat)));
gtk_table_attach_defaults(GTK_TABLE(table), info->entry, 1, 2, 0, 1);
gtk_label_set_mnemonic_widget(GTK_LABEL(label), info->entry);
@@ -970,13 +1036,13 @@ invite_cb(GtkWidget *widget, PidginConversation *gtkconv)
}
static void
-menu_new_conv_cb(gpointer data, guint action, GtkWidget *widget)
+menu_new_conv_cb(GtkAction *action, gpointer data)
{
pidgin_dialogs_im();
}
static void
-menu_join_chat_cb(gpointer data, guint action, GtkWidget *widget)
+menu_join_chat_cb(GtkAction *action, gpointer data)
{
pidgin_blist_joinchat_show();
}
@@ -985,30 +1051,38 @@ static void
savelog_writefile_cb(void *user_data, const char *filename)
{
PurpleConversation *conv = (PurpleConversation *)user_data;
+ PidginWebView *webview;
FILE *fp;
const char *name;
- char **lines;
gchar *text;
if ((fp = g_fopen(filename, "w+")) == NULL) {
- purple_notify_error(PIDGIN_CONVERSATION(conv), NULL, _("Unable to open file."), NULL);
+ purple_notify_error(PIDGIN_CONVERSATION(conv), NULL,
+ _("Unable to open file."), NULL,
+ purple_request_cpar_from_conversation(conv));
return;
}
+ webview = PIDGIN_WEBVIEW(PIDGIN_CONVERSATION(conv)->webview);
name = purple_conversation_get_name(conv);
- fprintf(fp, "<html>\n<head>\n");
+ 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</head>\n<body>\n", name);
- fprintf(fp, _("<h1>Conversation with %s</h1>\n"), name);
+ 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");
- lines = gtk_imhtml_get_markup_lines(
- GTK_IMHTML(PIDGIN_CONVERSATION(conv)->imhtml));
- text = g_strjoinv("<br>\n", lines);
+ fprintf(fp, "<body>\n");
+ fprintf(fp, _("<h1>Conversation with %s</h1>\n"), name);
+ text = pidgin_webview_get_body_html(webview);
fprintf(fp, "%s", text);
g_free(text);
- g_strfreev(lines);
+ fprintf(fp, "\n</body>\n");
- fprintf(fp, "\n</body>\n</html>\n");
+ fprintf(fp, "</html>\n");
fclose(fp);
}
@@ -1017,11 +1091,12 @@ savelog_writefile_cb(void *user_data, const char *filename)
* plaintext v. HTML file.
*/
static void
-menu_save_as_cb(gpointer data, guint action, GtkWidget *widget)
+menu_save_as_cb(GtkAction *action, gpointer data)
{
- PidginWindow *win = data;
+ PidginConvWindow *win = data;
PurpleConversation *conv = pidgin_conv_window_get_active_conversation(win);
- PurpleBuddy *buddy = purple_find_buddy(conv->account, conv->name);
+ PurpleAccount *account = purple_conversation_get_account(conv);
+ PurpleBuddy *buddy = purple_blist_find_buddy(account, purple_conversation_get_name(conv));
const char *name;
gchar *buf;
gchar *c;
@@ -1029,7 +1104,7 @@ menu_save_as_cb(gpointer data, guint action, GtkWidget *widget)
if (buddy != NULL)
name = purple_buddy_get_contact_alias(buddy);
else
- name = purple_normalize(conv->account, conv->name);
+ name = purple_normalize(account, purple_conversation_get_name(conv));
buf = g_strdup_printf("%s.html", name);
for (c = buf ; *c ; c++)
@@ -1038,22 +1113,19 @@ menu_save_as_cb(gpointer data, guint action, GtkWidget *widget)
*c = ' ';
}
purple_request_file(PIDGIN_CONVERSATION(conv), _("Save Conversation"),
- buf,
- TRUE, G_CALLBACK(savelog_writefile_cb), NULL,
- NULL, NULL, conv,
- conv);
+ buf, TRUE, G_CALLBACK(savelog_writefile_cb), NULL,
+ purple_request_cpar_from_conversation(conv), conv);
g_free(buf);
}
static void
-menu_view_log_cb(gpointer data, guint action, GtkWidget *widget)
+menu_view_log_cb(GtkAction *action, gpointer data)
{
- PidginWindow *win = data;
+ PidginConvWindow *win = data;
PurpleConversation *conv;
PurpleLogType type;
PidginBuddyList *gtkblist;
- GdkCursor *cursor;
const char *name;
PurpleAccount *account;
GSList *buddies;
@@ -1061,25 +1133,22 @@ menu_view_log_cb(gpointer data, guint action, GtkWidget *widget)
conv = pidgin_conv_window_get_active_conversation(win);
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
+ if (PURPLE_IS_IM_CONVERSATION(conv))
type = PURPLE_LOG_IM;
- else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
+ else if (PURPLE_IS_CHAT_CONVERSATION(conv))
type = PURPLE_LOG_CHAT;
else
return;
gtkblist = pidgin_blist_get_default_gtk_blist();
- cursor = gdk_cursor_new(GDK_WATCH);
- gdk_window_set_cursor(gtkblist->window->window, cursor);
- gdk_window_set_cursor(win->window->window, cursor);
- gdk_cursor_unref(cursor);
- gdk_display_flush(gdk_drawable_get_display(GDK_DRAWABLE(widget->window)));
+ pidgin_set_cursor(gtkblist->window, GDK_WATCH);
+ pidgin_set_cursor(win->window, GDK_WATCH);
name = purple_conversation_get_name(conv);
account = purple_conversation_get_account(conv);
- buddies = purple_find_buddies(account, name);
+ buddies = purple_blist_find_buddies(account, name);
for (cur = buddies; cur != NULL; cur = cur->next)
{
PurpleBlistNode *node = cur->data;
@@ -1087,8 +1156,8 @@ menu_view_log_cb(gpointer data, guint action, GtkWidget *widget)
{
pidgin_log_show_contact((PurpleContact *)node->parent);
g_slist_free(buddies);
- gdk_window_set_cursor(gtkblist->window->window, NULL);
- gdk_window_set_cursor(win->window->window, NULL);
+ pidgin_clear_cursor(gtkblist->window);
+ pidgin_clear_cursor(win->window);
return;
}
}
@@ -1096,14 +1165,14 @@ menu_view_log_cb(gpointer data, guint action, GtkWidget *widget)
pidgin_log_show(type, name, account);
- gdk_window_set_cursor(gtkblist->window->window, NULL);
- gdk_window_set_cursor(win->window->window, NULL);
+ pidgin_clear_cursor(gtkblist->window);
+ pidgin_clear_cursor(win->window);
}
static void
-menu_clear_cb(gpointer data, guint action, GtkWidget *widget)
+menu_clear_cb(GtkAction *action, gpointer data)
{
- PidginWindow *win = data;
+ PidginConvWindow *win = data;
PurpleConversation *conv;
conv = pidgin_conv_window_get_active_conversation(win);
@@ -1111,59 +1180,64 @@ menu_clear_cb(gpointer data, guint action, GtkWidget *widget)
}
static void
-menu_find_cb(gpointer data, guint action, GtkWidget *widget)
+menu_find_cb(GtkAction *action, gpointer data)
{
- PidginWindow *gtkwin = data;
+ PidginConvWindow *gtkwin = data;
PidginConversation *gtkconv = pidgin_conv_window_get_active_gtkconv(gtkwin);
- gtk_widget_show_all(gtkconv->quickfind.container);
- gtk_widget_grab_focus(gtkconv->quickfind.entry);
+ gtk_widget_show_all(gtkconv->quickfind_container);
+ gtk_widget_grab_focus(gtkconv->quickfind_entry);
}
#ifdef USE_VV
static void
-menu_initiate_media_call_cb(gpointer data, guint action, GtkWidget *widget)
+menu_initiate_media_call_cb(GtkAction *action, gpointer data)
{
- PidginWindow *win = (PidginWindow *)data;
+ PidginConvWindow *win = (PidginConvWindow *)data;
PurpleConversation *conv = pidgin_conv_window_get_active_conversation(win);
PurpleAccount *account = purple_conversation_get_account(conv);
purple_prpl_initiate_media(account,
purple_conversation_get_name(conv),
- action == 0 ? PURPLE_MEDIA_AUDIO :
- action == 1 ? PURPLE_MEDIA_VIDEO :
- action == 2 ? PURPLE_MEDIA_AUDIO |
+ action == win->menu->audio_call ? PURPLE_MEDIA_AUDIO :
+ action == win->menu->video_call ? PURPLE_MEDIA_VIDEO :
+ action == win->menu->audio_video_call ? PURPLE_MEDIA_AUDIO |
PURPLE_MEDIA_VIDEO : PURPLE_MEDIA_NONE);
}
#endif
static void
-menu_send_file_cb(gpointer data, guint action, GtkWidget *widget)
+menu_send_file_cb(GtkAction *action, gpointer data)
{
- PidginWindow *win = data;
+ PidginConvWindow *win = data;
PurpleConversation *conv = pidgin_conv_window_get_active_conversation(win);
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
- serv_send_file(purple_conversation_get_gc(conv), purple_conversation_get_name(conv), NULL);
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
+ purple_serv_send_file(purple_conversation_get_connection(conv), purple_conversation_get_name(conv), NULL);
}
}
static void
-menu_get_attention_cb(gpointer data, guint action, GtkWidget *widget)
+menu_get_attention_cb(GObject *obj, gpointer data)
{
- PidginWindow *win = data;
+ PidginConvWindow *win = data;
PurpleConversation *conv = pidgin_conv_window_get_active_conversation(win);
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
- purple_prpl_send_attention(purple_conversation_get_gc(conv),
- purple_conversation_get_name(conv), 0);
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
+ int index;
+ if ((GtkAction *)obj == win->menu->get_attention)
+ index = 0;
+ else
+ index = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(obj), "index"));
+ purple_prpl_send_attention(purple_conversation_get_connection(conv),
+ purple_conversation_get_name(conv), index);
}
}
static void
-menu_add_pounce_cb(gpointer data, guint action, GtkWidget *widget)
+menu_add_pounce_cb(GtkAction *action, gpointer data)
{
- PidginWindow *win = data;
+ PidginConvWindow *win = data;
PurpleConversation *conv;
conv = pidgin_conv_window_get_active_gtkconv(win)->active_conv;
@@ -1173,38 +1247,35 @@ menu_add_pounce_cb(gpointer data, guint action, GtkWidget *widget)
}
static void
-menu_insert_link_cb(gpointer data, guint action, GtkWidget *widget)
+menu_insert_link_cb(GtkAction *action, gpointer data)
{
- PidginWindow *win = data;
+ PidginConvWindow *win = data;
PidginConversation *gtkconv;
- GtkIMHtmlToolbar *toolbar;
+ PidginWebView *entry;
gtkconv = pidgin_conv_window_get_active_gtkconv(win);
- toolbar = GTK_IMHTMLTOOLBAR(gtkconv->toolbar);
+ entry = PIDGIN_WEBVIEW(gtkconv->entry);
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->link),
- !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->link)));
+ pidgin_webview_activate_toolbar(entry, PIDGIN_WEBVIEW_ACTION_LINK);
}
static void
-menu_insert_image_cb(gpointer data, guint action, GtkWidget *widget)
+menu_insert_image_cb(GtkAction *action, gpointer data)
{
- PidginWindow *win = data;
+ PidginConvWindow *win = data;
PidginConversation *gtkconv;
- GtkIMHtmlToolbar *toolbar;
+ PidginWebView *entry;
gtkconv = pidgin_conv_window_get_active_gtkconv(win);
- toolbar = GTK_IMHTMLTOOLBAR(gtkconv->toolbar);
+ entry = PIDGIN_WEBVIEW(gtkconv->entry);
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image),
- !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->image)));
+ pidgin_webview_activate_toolbar(entry, PIDGIN_WEBVIEW_ACTION_IMAGE);
}
-
static void
-menu_alias_cb(gpointer data, guint action, GtkWidget *widget)
+menu_alias_cb(GtkAction *action, gpointer data)
{
- PidginWindow *win = data;
+ PidginConvWindow *win = data;
PurpleConversation *conv;
PurpleAccount *account;
const char *name;
@@ -1213,13 +1284,13 @@ menu_alias_cb(gpointer data, guint action, GtkWidget *widget)
account = purple_conversation_get_account(conv);
name = purple_conversation_get_name(conv);
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
PurpleBuddy *b;
- b = purple_find_buddy(account, name);
+ b = purple_blist_find_buddy(account, name);
if (b != NULL)
pidgin_dialogs_alias_buddy(b);
- } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
+ } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
PurpleChat *c;
c = purple_blist_find_chat(account, name);
@@ -1229,9 +1300,9 @@ menu_alias_cb(gpointer data, guint action, GtkWidget *widget)
}
static void
-menu_get_info_cb(gpointer data, guint action, GtkWidget *widget)
+menu_get_info_cb(GtkAction *action, gpointer data)
{
- PidginWindow *win = data;
+ PidginConvWindow *win = data;
PurpleConversation *conv;
conv = pidgin_conv_window_get_active_conversation(win);
@@ -1240,9 +1311,9 @@ menu_get_info_cb(gpointer data, guint action, GtkWidget *widget)
}
static void
-menu_invite_cb(gpointer data, guint action, GtkWidget *widget)
+menu_invite_cb(GtkAction *action, gpointer data)
{
- PidginWindow *win = data;
+ PidginConvWindow *win = data;
PurpleConversation *conv;
conv = pidgin_conv_window_get_active_conversation(win);
@@ -1251,9 +1322,9 @@ menu_invite_cb(gpointer data, guint action, GtkWidget *widget)
}
static void
-menu_block_cb(gpointer data, guint action, GtkWidget *widget)
+menu_block_cb(GtkAction *action, gpointer data)
{
- PidginWindow *win = data;
+ PidginConvWindow *win = data;
PurpleConversation *conv;
conv = pidgin_conv_window_get_active_conversation(win);
@@ -1262,9 +1333,9 @@ menu_block_cb(gpointer data, guint action, GtkWidget *widget)
}
static void
-menu_unblock_cb(gpointer data, guint action, GtkWidget *widget)
+menu_unblock_cb(GtkAction *action, gpointer data)
{
- PidginWindow *win = data;
+ PidginConvWindow *win = data;
PurpleConversation *conv;
conv = pidgin_conv_window_get_active_conversation(win);
@@ -1273,9 +1344,9 @@ menu_unblock_cb(gpointer data, guint action, GtkWidget *widget)
}
static void
-menu_add_remove_cb(gpointer data, guint action, GtkWidget *widget)
+menu_add_remove_cb(GtkAction *action, gpointer data)
{
- PidginWindow *win = data;
+ PidginConvWindow *win = data;
PurpleConversation *conv;
conv = pidgin_conv_window_get_active_conversation(win);
@@ -1286,7 +1357,7 @@ menu_add_remove_cb(gpointer data, guint action, GtkWidget *widget)
static gboolean
close_already(gpointer data)
{
- purple_conversation_destroy(data);
+ g_object_unref(data);
return FALSE;
}
@@ -1301,11 +1372,11 @@ hide_conv(PidginConversation *gtkconv, gboolean closetimer)
for (list = g_list_copy(gtkconv->convs); list; list = g_list_delete_link(list, list)) {
PurpleConversation *conv = list->data;
if (closetimer) {
- guint timer = GPOINTER_TO_INT(purple_conversation_get_data(conv, "close-timer"));
+ guint timer = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "close-timer"));
if (timer)
purple_timeout_remove(timer);
timer = purple_timeout_add_seconds(CLOSE_CONV_TIMEOUT_SECS, close_already, conv);
- purple_conversation_set_data(conv, "close-timer", GINT_TO_POINTER(timer));
+ g_object_set_data(G_OBJECT(conv), "close-timer", GINT_TO_POINTER(timer));
}
#if 0
/* I will miss you */
@@ -1318,17 +1389,17 @@ hide_conv(PidginConversation *gtkconv, gboolean closetimer)
}
static void
-menu_close_conv_cb(gpointer data, guint action, GtkWidget *widget)
+menu_close_conv_cb(GtkAction *action, gpointer data)
{
- PidginWindow *win = data;
+ PidginConvWindow *win = data;
close_conv_cb(NULL, PIDGIN_CONVERSATION(pidgin_conv_window_get_active_conversation(win)));
}
static void
-menu_logging_cb(gpointer data, guint action, GtkWidget *widget)
+menu_logging_cb(GtkAction *action, gpointer data)
{
- PidginWindow *win = data;
+ PidginConvWindow *win = data;
PurpleConversation *conv;
gboolean logging;
PurpleBlistNode *node;
@@ -1338,7 +1409,7 @@ menu_logging_cb(gpointer data, guint action, GtkWidget *widget)
if (conv == NULL)
return;
- logging = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
+ logging = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
if (logging == purple_conversation_is_logging(conv))
return;
@@ -1350,57 +1421,43 @@ menu_logging_cb(gpointer data, guint action, GtkWidget *widget)
/* Enable logging first so the message below can be logged. */
purple_conversation_set_logging(conv, TRUE);
- purple_conversation_write(conv, NULL,
- _("Logging started. Future messages in this conversation will be logged."),
- conv->logs ? (PURPLE_MESSAGE_SYSTEM) :
- (PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG),
- time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("Logging started. Future messages in this conversation will be logged."), 0);
}
else
{
- purple_conversation_write(conv, NULL,
- _("Logging stopped. Future messages in this conversation will not be logged."),
- conv->logs ? (PURPLE_MESSAGE_SYSTEM) :
- (PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG),
- time(NULL));
+ purple_conversation_write_system_message(conv,
+ _("Logging stopped. Future messages in this conversation will not be logged."), 0);
/* Disable the logging second, so that the above message can be logged. */
purple_conversation_set_logging(conv, FALSE);
}
/* Save the setting IFF it's different than the pref. */
- switch (conv->type)
- {
- case PURPLE_CONV_TYPE_IM:
- if (logging == purple_prefs_get_bool("/purple/logging/log_ims"))
- purple_blist_node_remove_setting(node, "enable-logging");
- else
- purple_blist_node_set_bool(node, "enable-logging", logging);
- break;
-
- case PURPLE_CONV_TYPE_CHAT:
- if (logging == purple_prefs_get_bool("/purple/logging/log_chats"))
- purple_blist_node_remove_setting(node, "enable-logging");
- else
- purple_blist_node_set_bool(node, "enable-logging", logging);
- break;
-
- default:
- break;
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
+ if (logging == purple_prefs_get_bool("/purple/logging/log_ims"))
+ purple_blist_node_remove_setting(node, "enable-logging");
+ else
+ purple_blist_node_set_bool(node, "enable-logging", logging);
+ } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
+ if (logging == purple_prefs_get_bool("/purple/logging/log_chats"))
+ purple_blist_node_remove_setting(node, "enable-logging");
+ else
+ purple_blist_node_set_bool(node, "enable-logging", logging);
}
}
static void
-menu_toolbar_cb(gpointer data, guint action, GtkWidget *widget)
+menu_toolbar_cb(GtkAction *action, gpointer data)
{
purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/show_formatting_toolbar",
- gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)));
+ gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)));
}
static void
-menu_sounds_cb(gpointer data, guint action, GtkWidget *widget)
+menu_sounds_cb(GtkAction *action, gpointer data)
{
- PidginWindow *win = data;
+ PidginConvWindow *win = data;
PurpleConversation *conv;
PidginConversation *gtkconv;
PurpleBlistNode *node;
@@ -1413,20 +1470,13 @@ menu_sounds_cb(gpointer data, guint action, GtkWidget *widget)
gtkconv = PIDGIN_CONVERSATION(conv);
gtkconv->make_sound =
- gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
+ gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
node = get_conversation_blist_node(conv);
if (node)
purple_blist_node_set_bool(node, "gtk-mute-sound", !gtkconv->make_sound);
}
static void
-menu_timestamps_cb(gpointer data, guint action, GtkWidget *widget)
-{
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/show_timestamps",
- gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)));
-}
-
-static void
chat_do_im(PidginConversation *gtkconv, const char *who)
{
PurpleConversation *conv = gtkconv->active_conv;
@@ -1441,11 +1491,11 @@ chat_do_im(PidginConversation *gtkconv, const char *who)
gc = purple_account_get_connection(account);
g_return_if_fail(gc != NULL);
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
if (prpl_info && prpl_info->get_cb_real_name)
real_who = prpl_info->get_cb_real_name(gc,
- purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), who);
+ purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv)), who);
if(!who && !real_who)
return;
@@ -1455,27 +1505,25 @@ chat_do_im(PidginConversation *gtkconv, const char *who)
g_free(real_who);
}
-static void pidgin_conv_chat_update_user(PurpleConversation *conv, const char *user);
+static void pidgin_conv_chat_update_user(PurpleChatUser *chatuser);
static void
ignore_cb(GtkWidget *w, PidginConversation *gtkconv)
{
- PurpleConversation *conv = gtkconv->active_conv;
- PurpleConvChat *chat;
+ PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(gtkconv->active_conv);
const char *name;
- chat = PURPLE_CONV_CHAT(conv);
name = g_object_get_data(G_OBJECT(w), "user_data");
if (name == NULL)
return;
- if (purple_conv_chat_is_user_ignored(chat, name))
- purple_conv_chat_unignore(chat, name);
+ if (purple_chat_conversation_is_ignored_user(chat, name))
+ purple_chat_conversation_unignore(chat, name);
else
- purple_conv_chat_ignore(chat, name);
+ purple_chat_conversation_ignore(chat, name);
- pidgin_conv_chat_update_user(conv, name);
+ pidgin_conv_chat_update_user(purple_chat_conversation_find_user(chat, name));
}
static void
@@ -1492,18 +1540,18 @@ menu_chat_send_file_cb(GtkWidget *w, PidginConversation *gtkconv)
PurplePluginProtocolInfo *prpl_info;
PurpleConversation *conv = gtkconv->active_conv;
const char *who = g_object_get_data(G_OBJECT(w), "user_data");
- PurpleConnection *gc = purple_conversation_get_gc(conv);
+ PurpleConnection *gc = purple_conversation_get_connection(conv);
gchar *real_who = NULL;
g_return_if_fail(gc != NULL);
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
if (prpl_info && prpl_info->get_cb_real_name)
real_who = prpl_info->get_cb_real_name(gc,
- purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), who);
+ purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv)), who);
- serv_send_file(gc, real_who ? real_who : who, NULL);
+ purple_serv_send_file(gc, real_who ? real_who : who, NULL);
g_free(real_who);
}
@@ -1518,32 +1566,6 @@ menu_chat_info_cb(GtkWidget *w, PidginConversation *gtkconv)
}
static void
-menu_chat_get_away_cb(GtkWidget *w, PidginConversation *gtkconv)
-{
- PurpleConversation *conv = gtkconv->active_conv;
- PurplePluginProtocolInfo *prpl_info = NULL;
- PurpleConnection *gc;
- char *who;
-
- gc = purple_conversation_get_gc(conv);
- who = g_object_get_data(G_OBJECT(w), "user_data");
-
- if (gc != NULL) {
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
-
- /*
- * May want to expand this to work similarly to menu_info_cb?
- */
-
- if (prpl_info->get_cb_away != NULL)
- {
- prpl_info->get_cb_away(gc,
- purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), who);
- }
- }
-}
-
-static void
menu_chat_add_remove_cb(GtkWidget *w, PidginConversation *gtkconv)
{
PurpleConversation *conv = gtkconv->active_conv;
@@ -1553,7 +1575,7 @@ menu_chat_add_remove_cb(GtkWidget *w, PidginConversation *gtkconv)
account = purple_conversation_get_account(conv);
name = g_object_get_data(G_OBJECT(w), "user_data");
- b = purple_find_buddy(account, name);
+ b = purple_blist_find_buddy(account, name);
if (b != NULL)
pidgin_dialogs_remove_buddy(b);
@@ -1563,44 +1585,66 @@ menu_chat_add_remove_cb(GtkWidget *w, PidginConversation *gtkconv)
gtk_widget_grab_focus(PIDGIN_CONVERSATION(conv)->entry);
}
-static GtkTextMark *
+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)
{
- GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml));
- char *tmp = g_strconcat("user:", who, NULL);
- GtkTextMark *mark = gtk_text_buffer_get_mark(buf, tmp);
+ 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);
- return mark;
+
+ 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)
{
- GtkTextMark *mark;
+ WebKitDOMNode *node;
const char *who;
who = g_object_get_data(G_OBJECT(w), "user_data");
- mark = get_mark_for_user(gtkconv, who);
+ node = get_mark_for_user(gtkconv, who);
- if (mark != NULL)
- gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(gtkconv->imhtml), mark, 0.1, FALSE, 0, 0);
+ if (node != NULL)
+ webkit_dom_element_scroll_into_view(WEBKIT_DOM_ELEMENT(node), TRUE);
else
g_return_if_reached();
}
static GtkWidget *
-create_chat_menu(PurpleConversation *conv, const char *who, PurpleConnection *gc)
+create_chat_menu(PurpleChatConversation *chat, const char *who, PurpleConnection *gc)
{
static GtkWidget *menu = NULL;
PurplePluginProtocolInfo *prpl_info = NULL;
- PurpleConvChat *chat = PURPLE_CONV_CHAT(conv);
+ PurpleConversation *conv = PURPLE_CONVERSATION(chat);
+ PurpleAccount *account = purple_conversation_get_account(conv);
gboolean is_me = FALSE;
GtkWidget *button;
PurpleBuddy *buddy = NULL;
if (gc != NULL)
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
/*
* If a menu already exists, destroy it before creating a new one,
@@ -1609,7 +1653,7 @@ create_chat_menu(PurpleConversation *conv, const char *who, PurpleConnection *gc
if (menu)
gtk_widget_destroy(menu);
- if (!strcmp(chat->nick, purple_normalize(conv->account, who)))
+ if (!strcmp(purple_chat_conversation_get_nick(chat), purple_normalize(account, who)))
is_me = TRUE;
menu = gtk_menu_new();
@@ -1638,7 +1682,7 @@ create_chat_menu(PurpleConversation *conv, const char *who, PurpleConnection *gc
gchar *real_who = NULL;
if (prpl_info->get_cb_real_name)
real_who = prpl_info->get_cb_real_name(gc,
- purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), who);
+ purple_chat_conversation_get_id(chat), who);
if (!(!prpl_info->can_receive_file || prpl_info->can_receive_file(gc, real_who ? real_who : who)))
can_receive_file = FALSE;
g_free(real_who);
@@ -1651,7 +1695,7 @@ create_chat_menu(PurpleConversation *conv, const char *who, PurpleConnection *gc
}
- if (purple_conv_chat_is_user_ignored(PURPLE_CONV_CHAT(conv), who))
+ if (purple_chat_conversation_is_ignored_user(chat, who))
button = pidgin_new_item_from_stock(menu, _("Un-Ignore"), PIDGIN_STOCK_IGNORE,
G_CALLBACK(ignore_cb), PIDGIN_CONVERSATION(conv), 0, 0, NULL);
else
@@ -1674,18 +1718,8 @@ create_chat_menu(PurpleConversation *conv, const char *who, PurpleConnection *gc
g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
}
- if (prpl_info && prpl_info->get_cb_away) {
- button = pidgin_new_item_from_stock(menu, _("Get Away Message"), PIDGIN_STOCK_AWAY,
- G_CALLBACK(menu_chat_get_away_cb), PIDGIN_CONVERSATION(conv), 0, 0, NULL);
-
- if (gc == NULL)
- gtk_widget_set_sensitive(button, FALSE);
- else
- g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
- }
-
if (!is_me && prpl_info && !(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
- if ((buddy = purple_find_buddy(conv->account, who)) != NULL)
+ if ((buddy = purple_blist_find_buddy(account, who)) != NULL)
button = pidgin_new_item_from_stock(menu, _("Remove"), GTK_STOCK_REMOVE,
G_CALLBACK(menu_chat_add_remove_cb), PIDGIN_CONVERSATION(conv), 0, 0, NULL);
else
@@ -1706,8 +1740,8 @@ create_chat_menu(PurpleConversation *conv, const char *who, PurpleConnection *gc
if (buddy != NULL)
{
- if (purple_account_is_connected(conv->account))
- pidgin_append_blist_node_proto_menu(menu, conv->account->gc,
+ if (purple_account_is_connected(account))
+ pidgin_append_blist_node_proto_menu(menu, purple_account_get_connection(account),
(PurpleBlistNode *)buddy);
pidgin_append_blist_node_extended_menu(menu, (PurpleBlistNode *)buddy);
gtk_widget_show_all(menu);
@@ -1733,7 +1767,7 @@ gtkconv_chat_popup_menu_cb(GtkWidget *widget, PidginConversation *gtkconv)
gtkconv = PIDGIN_CONVERSATION(conv);
gtkchat = gtkconv->u.chat;
account = purple_conversation_get_account(conv);
- gc = account->gc;
+ gc = purple_account_get_connection(account);
model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list));
@@ -1742,7 +1776,7 @@ gtkconv_chat_popup_menu_cb(GtkWidget *widget, PidginConversation *gtkconv)
return FALSE;
gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &who, -1);
- menu = create_chat_menu (conv, who, gc);
+ menu = create_chat_menu (PURPLE_CHAT_CONVERSATION(conv), who, gc);
gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
pidgin_treeview_popup_menu_position_func, widget,
0, GDK_CURRENT_TIME);
@@ -1769,7 +1803,7 @@ right_click_chat_cb(GtkWidget *widget, GdkEventButton *event,
gtkchat = gtkconv->u.chat;
account = purple_conversation_get_account(conv);
- gc = account->gc;
+ gc = purple_account_get_connection(account);
model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list));
@@ -1801,12 +1835,13 @@ right_click_chat_cb(GtkWidget *widget, GdkEventButton *event,
chat_do_im(gtkconv, who);
} else if (event->button == 2 && event->type == GDK_BUTTON_PRESS) {
/* Move to user's anchor */
- GtkTextMark *mark = get_mark_for_user(gtkconv, who);
+ WebKitDOMNode *node = get_mark_for_user(gtkconv, who);
+
+ if (node != NULL)
+ webkit_dom_element_scroll_into_view(WEBKIT_DOM_ELEMENT(node), TRUE);
- if(mark != NULL)
- gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(gtkconv->imhtml), mark, 0.1, FALSE, 0, 0);
} else if (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
- GtkWidget *menu = create_chat_menu (conv, who, gc);
+ GtkWidget *menu = create_chat_menu (PURPLE_CHAT_CONVERSATION(conv), who, gc);
gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
event->button, event->time);
}
@@ -1839,7 +1874,7 @@ move_to_next_unread_tab(PidginConversation *gtkconv, gboolean forward)
{
PidginConversation *next_gtkconv = NULL, *most_active = NULL;
PidginUnseenState unseen_state = PIDGIN_UNSEEN_NONE;
- PidginWindow *win;
+ PidginConvWindow *win;
int initial, i, total, diff;
win = gtkconv->win;
@@ -1875,14 +1910,14 @@ static gboolean
gtkconv_cycle_focus(PidginConversation *gtkconv, GtkDirectionType dir)
{
PurpleConversation *conv = gtkconv->active_conv;
- gboolean chat = purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT;
+ gboolean chat = PURPLE_IS_CHAT_CONVERSATION(conv);
GtkWidget *next = NULL;
struct {
GtkWidget *from;
GtkWidget *to;
} transitions[] = {
- {gtkconv->entry, gtkconv->imhtml},
- {gtkconv->imhtml, chat ? gtkconv->u.chat->list : gtkconv->entry},
+ {gtkconv->entry, gtkconv->webview},
+ {gtkconv->webview, chat ? gtkconv->u.chat->list : gtkconv->entry},
{chat ? gtkconv->u.chat->list : NULL, gtkconv->entry},
{NULL, NULL}
}, *ptr;
@@ -1905,13 +1940,60 @@ gtkconv_cycle_focus(PidginConversation *gtkconv, GtkDirectionType dir)
return !!next;
}
+static void
+update_typing_inserting(PidginConversation *gtkconv)
+{
+ gboolean is_empty;
+
+ g_return_if_fail(gtkconv != NULL);
+
+ is_empty = pidgin_webview_is_empty(PIDGIN_WEBVIEW(gtkconv->entry));
+
+ got_typing_keypress(gtkconv, is_empty);
+}
+
+static gboolean
+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) {
+ /* 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 {
+ /* We're deleting, but not all of it, so it counts as typing. */
+ got_typing_keypress(gtkconv, FALSE);
+ }
+
+ return FALSE;
+}
+
+static void
+update_typing_deleting(PidginConversation *gtkconv)
+{
+ gboolean is_empty;
+
+ g_return_if_fail(gtkconv != NULL);
+
+ is_empty = pidgin_webview_is_empty(PIDGIN_WEBVIEW(gtkconv->entry));
+
+ if (!is_empty)
+ purple_timeout_add(0, (GSourceFunc)update_typing_deleting_cb, gtkconv);
+}
+
static gboolean
conv_keypress_common(PidginConversation *gtkconv, GdkEventKey *event)
{
- PidginWindow *win;
+ PidginConvWindow *win;
int curconv;
- win = gtkconv->win;
+ win = gtkconv->win;
curconv = gtk_notebook_get_current_page(GTK_NOTEBOOK(win->notebook));
/* clear any tooltips */
@@ -1920,8 +2002,8 @@ conv_keypress_common(PidginConversation *gtkconv, GdkEventKey *event)
/* If CTRL was held down... */
if (event->state & GDK_CONTROL_MASK) {
switch (event->keyval) {
- case GDK_Page_Down:
- case GDK_KP_Page_Down:
+ case GDK_KEY_Page_Down:
+ case GDK_KEY_KP_Page_Down:
case ']':
if (!pidgin_conv_window_get_gtkconv_at_index(win, curconv + 1))
gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), 0);
@@ -1930,8 +2012,8 @@ conv_keypress_common(PidginConversation *gtkconv, GdkEventKey *event)
return TRUE;
break;
- case GDK_Page_Up:
- case GDK_KP_Page_Up:
+ case GDK_KEY_Page_Up:
+ case GDK_KEY_KP_Page_Up:
case '[':
if (!pidgin_conv_window_get_gtkconv_at_index(win, curconv - 1))
gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), -1);
@@ -1940,9 +2022,9 @@ conv_keypress_common(PidginConversation *gtkconv, GdkEventKey *event)
return TRUE;
break;
- case GDK_Tab:
- case GDK_KP_Tab:
- case GDK_ISO_Left_Tab:
+ case GDK_KEY_Tab:
+ case GDK_KEY_KP_Tab:
+ case GDK_KEY_ISO_Left_Tab:
if (event->state & GDK_SHIFT_MASK) {
move_to_next_unread_tab(gtkconv, FALSE);
} else {
@@ -1952,20 +2034,20 @@ conv_keypress_common(PidginConversation *gtkconv, GdkEventKey *event)
return TRUE;
break;
- case GDK_comma:
+ case GDK_KEY_comma:
gtk_notebook_reorder_child(GTK_NOTEBOOK(win->notebook),
gtk_notebook_get_nth_page(GTK_NOTEBOOK(win->notebook), curconv),
curconv - 1);
return TRUE;
break;
- case GDK_period:
+ case GDK_KEY_period:
gtk_notebook_reorder_child(GTK_NOTEBOOK(win->notebook),
gtk_notebook_get_nth_page(GTK_NOTEBOOK(win->notebook), curconv),
(curconv + 1) % gtk_notebook_get_n_pages(GTK_NOTEBOOK(win->notebook)));
return TRUE;
break;
- case GDK_F6:
+ case GDK_KEY_F6:
if (gtkconv_cycle_focus(gtkconv, event->state & GDK_SHIFT_MASK ? GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD))
return TRUE;
break;
@@ -1989,13 +2071,13 @@ conv_keypress_common(PidginConversation *gtkconv, GdkEventKey *event)
else
{
switch (event->keyval) {
- case GDK_F2:
+ case GDK_KEY_F2:
if (gtk_widget_is_focus(GTK_WIDGET(win->notebook))) {
infopane_entry_activate(gtkconv);
return TRUE;
}
break;
- case GDK_F6:
+ case GDK_KEY_F6:
if (gtkconv_cycle_focus(gtkconv, event->state & GDK_SHIFT_MASK ? GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD))
return TRUE;
break;
@@ -2019,7 +2101,7 @@ entry_key_press_cb(GtkWidget *entry, GdkEventKey *event, gpointer data)
/* If CTRL was held down... */
if (event->state & GDK_CONTROL_MASK) {
switch (event->keyval) {
- case GDK_Up:
+ case GDK_KEY_Up:
if (!gtkconv->send_history)
break;
@@ -2027,50 +2109,48 @@ entry_key_press_cb(GtkWidget *entry, GdkEventKey *event, gpointer data)
break;
if (!gtkconv->send_history->prev) {
- GtkTextIter start, end;
-
g_free(gtkconv->send_history->data);
- gtk_text_buffer_get_start_iter(gtkconv->entry_buffer,
- &start);
- gtk_text_buffer_get_end_iter(gtkconv->entry_buffer, &end);
-
gtkconv->send_history->data =
- gtk_imhtml_get_markup(GTK_IMHTML(gtkconv->entry));
+ 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);
+ g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, gtkconv);
/* Clear the formatting. */
- gtk_imhtml_clear_formatting(GTK_IMHTML(gtkconv->entry));
+ 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_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, gtkconv);
g_object_unref(object);
- gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry));
- gtk_imhtml_append_text_with_images(
- GTK_IMHTML(gtkconv->entry), gtkconv->send_history->data,
- 0, NULL);
+ 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_Down:
+ case GDK_KEY_Down:
if (!gtkconv->send_history)
break;
@@ -2079,31 +2159,35 @@ entry_key_press_cb(GtkWidget *entry, GdkEventKey *event, gpointer data)
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);
+ g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, gtkconv);
/* Clear the formatting. */
- gtk_imhtml_clear_formatting(GTK_IMHTML(gtkconv->entry));
+ 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_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, gtkconv);
g_object_unref(object);
- gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry));
- gtk_imhtml_append_text_with_images(
- GTK_IMHTML(gtkconv->entry), gtkconv->send_history->data,
- 0, NULL);
+ 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);
@@ -2123,9 +2207,9 @@ entry_key_press_cb(GtkWidget *entry, GdkEventKey *event, gpointer data)
/* If neither CTRL nor ALT were held down... */
else {
switch (event->keyval) {
- case GDK_Tab:
- case GDK_KP_Tab:
- case GDK_ISO_Left_Tab:
+ case GDK_KEY_Tab:
+ case GDK_KEY_KP_Tab:
+ case GDK_KEY_ISO_Left_Tab:
if (gtkconv->entry != entry)
break;
{
@@ -2137,20 +2221,41 @@ entry_key_press_cb(GtkWidget *entry, GdkEventKey *event, gpointer data)
}
break;
- case GDK_Page_Up:
- case GDK_KP_Page_Up:
- gtk_imhtml_page_up(GTK_IMHTML(gtkconv->imhtml));
+ case GDK_KEY_Page_Up:
+ case GDK_KEY_KP_Page_Up:
+ pidgin_webview_page_up(PIDGIN_WEBVIEW(gtkconv->webview));
return TRUE;
break;
- case GDK_Page_Down:
- case GDK_KP_Page_Down:
- gtk_imhtml_page_down(GTK_IMHTML(gtkconv->imhtml));
+ case GDK_KEY_Page_Down:
+ case GDK_KEY_KP_Page_Down:
+ pidgin_webview_page_down(PIDGIN_WEBVIEW(gtkconv->webview));
+ return TRUE;
+ break;
+
+ case GDK_KEY_KP_Enter:
+ case GDK_KEY_Return:
+ send_cb(entry, gtkconv);
return TRUE;
break;
}
}
+
+ if (PURPLE_IS_IM_CONVERSATION(conv) &&
+ purple_prefs_get_bool("/purple/conversations/im/send_typing")) {
+
+ switch (event->keyval) {
+ case GDK_KEY_BackSpace:
+ case GDK_KEY_Delete:
+ case GDK_KEY_KP_Delete:
+ update_typing_deleting(gtkconv);
+ break;
+ default:
+ update_typing_inserting(gtkconv);
+ }
+ }
+
return FALSE;
}
@@ -2188,50 +2293,49 @@ refocus_entry_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
/* If we have a valid key for the conversation display, then exit */
if ((event->state & GDK_CONTROL_MASK) ||
- (event->keyval == GDK_F6) ||
- (event->keyval == GDK_F10) ||
- (event->keyval == GDK_Shift_L) ||
- (event->keyval == GDK_Shift_R) ||
- (event->keyval == GDK_Control_L) ||
- (event->keyval == GDK_Control_R) ||
- (event->keyval == GDK_Escape) ||
- (event->keyval == GDK_Up) ||
- (event->keyval == GDK_Down) ||
- (event->keyval == GDK_Left) ||
- (event->keyval == GDK_Right) ||
- (event->keyval == GDK_Page_Up) ||
- (event->keyval == GDK_KP_Page_Up) ||
- (event->keyval == GDK_Page_Down) ||
- (event->keyval == GDK_KP_Page_Down) ||
- (event->keyval == GDK_Home) ||
- (event->keyval == GDK_End) ||
- (event->keyval == GDK_Tab) ||
- (event->keyval == GDK_KP_Tab) ||
- (event->keyval == GDK_ISO_Left_Tab))
+ (event->keyval == GDK_KEY_F6) ||
+ (event->keyval == GDK_KEY_F10) ||
+ (event->keyval == GDK_KEY_Menu) ||
+ (event->keyval == GDK_KEY_Shift_L) ||
+ (event->keyval == GDK_KEY_Shift_R) ||
+ (event->keyval == GDK_KEY_Control_L) ||
+ (event->keyval == GDK_KEY_Control_R) ||
+ (event->keyval == GDK_KEY_Escape) ||
+ (event->keyval == GDK_KEY_Up) ||
+ (event->keyval == GDK_KEY_Down) ||
+ (event->keyval == GDK_KEY_Left) ||
+ (event->keyval == GDK_KEY_Right) ||
+ (event->keyval == GDK_KEY_Page_Up) ||
+ (event->keyval == GDK_KEY_KP_Page_Up) ||
+ (event->keyval == GDK_KEY_Page_Down) ||
+ (event->keyval == GDK_KEY_KP_Page_Down) ||
+ (event->keyval == GDK_KEY_Home) ||
+ (event->keyval == GDK_KEY_End) ||
+ (event->keyval == GDK_KEY_Tab) ||
+ (event->keyval == GDK_KEY_KP_Tab) ||
+ (event->keyval == GDK_KEY_ISO_Left_Tab))
{
if (event->type == GDK_KEY_PRESS)
return conv_keypress_common(gtkconv, event);
return FALSE;
}
- if (event->type == GDK_KEY_RELEASE)
- gtk_widget_grab_focus(gtkconv->entry);
-
+ gtk_widget_grab_focus(gtkconv->entry);
gtk_widget_event(gtkconv->entry, (GdkEvent *)event);
return TRUE;
}
static void
-regenerate_options_items(PidginWindow *win);
+regenerate_options_items(PidginConvWindow *win);
void
pidgin_conv_switch_active_conversation(PurpleConversation *conv)
{
PidginConversation *gtkconv;
PurpleConversation *old_conv;
- GtkIMHtml *entry;
- const char *protocol_name;
+ PidginWebView *entry;
+ PurpleConnectionFlags features;
g_return_if_fail(conv != NULL);
@@ -2240,8 +2344,6 @@ pidgin_conv_switch_active_conversation(PurpleConversation *conv)
purple_debug_info("gtkconv", "setting active conversation on toolbar %p\n",
conv);
- gtk_imhtmltoolbar_switch_active_conversation(GTK_IMHTMLTOOLBAR(gtkconv->toolbar),
- conv);
if (old_conv == conv)
return;
@@ -2249,18 +2351,20 @@ pidgin_conv_switch_active_conversation(PurpleConversation *conv)
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_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(gtkconv->win->menu.logging)));
+ gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(gtkconv->win->menu->logging)));
- entry = GTK_IMHTML(gtkconv->entry);
- protocol_name = purple_account_get_protocol_name(conv->account);
- gtk_imhtml_set_protocol_name(entry, protocol_name);
- gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml), protocol_name);
+ entry = PIDGIN_WEBVIEW(gtkconv->entry);
- if (!(conv->features & PURPLE_CONNECTION_HTML))
- gtk_imhtml_clear_formatting(GTK_IMHTML(gtkconv->entry));
- else if (conv->features & PURPLE_CONNECTION_FORMATTING_WBFO &&
- !(old_conv->features & PURPLE_CONNECTION_FORMATTING_WBFO))
+ 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
@@ -2271,59 +2375,71 @@ pidgin_conv_switch_active_conversation(PurpleConversation *conv)
gboolean bold;
gboolean italic;
gboolean underline;
- char *fontface = gtk_imhtml_get_current_fontface(entry);
- char *forecolor = gtk_imhtml_get_current_forecolor(entry);
- char *backcolor = gtk_imhtml_get_current_backcolor(entry);
- char *background = gtk_imhtml_get_current_background(entry);
- gint fontsize = gtk_imhtml_get_current_fontsize(entry);
+ 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;
- gtk_imhtml_get_current_format(entry, &bold, &italic, &underline);
+ pidgin_webview_get_current_format(entry, &bold, &italic, &underline, &strike);
/* Clear existing formatting */
- gtk_imhtml_clear_formatting(entry);
+ pidgin_webview_clear_formatting(entry);
/* Apply saved formatting to the whole buffer. */
- gtk_imhtml_get_current_format(entry, &bold2, &italic2, &underline2);
+ pidgin_webview_get_current_format(entry, &bold2, &italic2, &underline2, &strike2);
if (bold != bold2)
- gtk_imhtml_toggle_bold(entry);
+ pidgin_webview_toggle_bold(entry);
if (italic != italic2)
- gtk_imhtml_toggle_italic(entry);
+ pidgin_webview_toggle_italic(entry);
if (underline != underline2)
- gtk_imhtml_toggle_underline(entry);
+ pidgin_webview_toggle_underline(entry);
+
+ if (strike != strike2)
+ pidgin_webview_toggle_strike(entry);
- gtk_imhtml_toggle_fontface(entry, fontface);
+ pidgin_webview_toggle_fontface(entry, fontface);
- if (!(conv->features & PURPLE_CONNECTION_NO_FONTSIZE))
- gtk_imhtml_font_set_size(entry, fontsize);
+ if (!(features & PURPLE_CONNECTION_FLAG_NO_FONTSIZE))
+ pidgin_webview_font_set_size(entry, fontsize);
- gtk_imhtml_toggle_forecolor(entry, forecolor);
+ pidgin_webview_toggle_forecolor(entry, forecolor);
- if (!(conv->features & PURPLE_CONNECTION_NO_BGCOLOR))
+ if (!(features & PURPLE_CONNECTION_FLAG_NO_BGCOLOR))
{
- gtk_imhtml_toggle_backcolor(entry, backcolor);
- gtk_imhtml_toggle_background(entry, background);
+ 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 gtk_imhtml_clear_formatting() (because we want to
+ * 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. */
- gtk_imhtml_set_whole_buffer_formatting_only(entry,
- (conv->features & PURPLE_CONNECTION_FORMATTING_WBFO));
+ pidgin_webview_set_whole_buffer_formatting_only(entry,
+ (features & PURPLE_CONNECTION_FLAG_FORMATTING_WBFO));
}
purple_signal_emit(pidgin_conversations_get_handle(), "conversation-switched", conv);
@@ -2342,60 +2458,13 @@ menu_conv_sel_send_cb(GObject *m, gpointer data)
{
PurpleAccount *account = g_object_get_data(m, "purple_account");
gchar *name = g_object_get_data(m, "purple_buddy_name");
- PurpleConversation *conv;
+ PurpleIMConversation *im;
if (gtk_check_menu_item_get_active((GtkCheckMenuItem*) m) == FALSE)
return;
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, name);
- pidgin_conv_switch_active_conversation(conv);
-}
-
-static void
-insert_text_cb(GtkTextBuffer *textbuffer, GtkTextIter *position,
- gchar *new_text, gint new_text_length, gpointer user_data)
-{
- PidginConversation *gtkconv = (PidginConversation *)user_data;
-
- g_return_if_fail(gtkconv != NULL);
-
- if (!purple_prefs_get_bool("/purple/conversations/im/send_typing"))
- return;
-
- got_typing_keypress(gtkconv, (gtk_text_iter_is_start(position) &&
- gtk_text_iter_is_end(position)));
-}
-
-static void
-delete_text_cb(GtkTextBuffer *textbuffer, GtkTextIter *start_pos,
- GtkTextIter *end_pos, gpointer user_data)
-{
- PidginConversation *gtkconv = (PidginConversation *)user_data;
- PurpleConversation *conv;
- PurpleConvIm *im;
-
- g_return_if_fail(gtkconv != NULL);
-
- conv = gtkconv->active_conv;
-
- if (!purple_prefs_get_bool("/purple/conversations/im/send_typing"))
- return;
-
- im = PURPLE_CONV_IM(conv);
-
- if (gtk_text_iter_is_start(start_pos) && gtk_text_iter_is_end(end_pos)) {
-
- /* We deleted all the text, so turn off typing. */
- purple_conv_im_stop_send_typed_timeout(im);
-
- serv_send_typing(purple_conversation_get_gc(conv),
- purple_conversation_get_name(conv),
- PURPLE_NOT_TYPING);
- }
- else {
- /* We're deleting, but not all of it, so it counts as typing. */
- got_typing_keypress(gtkconv, FALSE);
- }
+ im = purple_im_conversation_new(account, name);
+ pidgin_conv_switch_active_conversation(PURPLE_CONVERSATION(im));
}
/**************************************************************************
@@ -2435,8 +2504,8 @@ pidgin_conv_get_tab_icons(PurpleConversation *conv)
g_return_val_if_fail(name != NULL, NULL);
/* Use the buddy icon, if possible */
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
- PurpleBuddy *b = purple_find_buddy(account, name);
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
+ PurpleBuddy *b = purple_blist_find_buddy(account, name);
if (b != NULL) {
PurplePresence *p;
p = purple_buddy_get_presence(b);
@@ -2468,15 +2537,15 @@ pidgin_conv_get_icon_stock(PurpleConversation *conv)
g_return_val_if_fail(account != NULL, NULL);
/* Use the buddy icon, if possible */
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
const char *name = NULL;
PurpleBuddy *b;
name = purple_conversation_get_name(conv);
- b = purple_find_buddy(account, name);
+ b = purple_blist_find_buddy(account, name);
if (b != NULL) {
PurplePresence *p = purple_buddy_get_presence(b);
PurpleStatus *active = purple_presence_get_active_status(p);
- PurpleStatusType *type = purple_status_get_type(active);
+ PurpleStatusType *type = purple_status_get_status_type(active);
PurpleStatusPrimitive prim = purple_status_type_get_primitive(type);
stock = pidgin_stock_id_from_status_primitive(prim);
} else {
@@ -2508,8 +2577,8 @@ pidgin_conv_get_icon(PurpleConversation *conv, GtkWidget *parent, const char *ic
g_return_val_if_fail(name != NULL, NULL);
/* Use the buddy icon, if possible */
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
- PurpleBuddy *b = purple_find_buddy(account, name);
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
+ PurpleBuddy *b = purple_blist_find_buddy(account, name);
if (b != NULL) {
/* I hate this hack. It fixes a bug where the pending message icon
* displays in the conv tab even though it shouldn't.
@@ -2537,7 +2606,7 @@ static void
update_tab_icon(PurpleConversation *conv)
{
PidginConversation *gtkconv;
- PidginWindow *win;
+ PidginConvWindow *win;
GList *l;
GdkPixbuf *emblem = NULL;
const char *status = NULL;
@@ -2552,8 +2621,8 @@ update_tab_icon(PurpleConversation *conv)
status = infopane_status = pidgin_conv_get_icon_stock(conv);
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
- PurpleBuddy *b = purple_find_buddy(conv->account, conv->name);
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
+ PurpleBuddy *b = purple_blist_find_buddy(purple_conversation_get_account(conv), purple_conversation_get_name(conv));
if (b)
emblem = pidgin_blist_get_emblem((PurpleBlistNode*)b);
}
@@ -2574,7 +2643,7 @@ update_tab_icon(PurpleConversation *conv)
g_object_unref(emblem);
if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons")) {
- emblem = pidgin_create_prpl_icon(gtkconv->active_conv->account, PIDGIN_PRPL_ICON_SMALL);
+ emblem = pidgin_create_prpl_icon(purple_conversation_get_account(gtkconv->active_conv), PIDGIN_PRPL_ICON_SMALL);
} else {
emblem = NULL;
}
@@ -2590,8 +2659,7 @@ update_tab_icon(PurpleConversation *conv)
gtk_widget_queue_draw(gtkconv->infopane);
if (pidgin_conv_window_is_active_conversation(conv) &&
- (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_IM ||
- gtkconv->u.im->anim == NULL))
+ (!PURPLE_IS_IM_CONVERSATION(conv) || gtkconv->u.im->anim == NULL))
{
l = pidgin_conv_get_tab_icons(conv);
@@ -2630,7 +2698,7 @@ redraw_icon(gpointer data)
gtkconv = PIDGIN_CONVERSATION(conv);
account = purple_conversation_get_account(conv);
- if (!(account && account->gc)) {
+ if (!(account && purple_account_get_connection(account))) {
gtkconv->u.im->icon_timer = 0;
return FALSE;
}
@@ -2675,7 +2743,7 @@ redraw_icon(gpointer data)
}
static void
-start_anim(GtkObject *obj, PidginConversation *gtkconv)
+start_anim(GtkWidget *widget, PidginConversation *gtkconv)
{
int delay;
@@ -2735,16 +2803,16 @@ static void
saveicon_writefile_cb(void *user_data, const char *filename)
{
PidginConversation *gtkconv = (PidginConversation *)user_data;
- PurpleConversation *conv = gtkconv->active_conv;
+ PurpleIMConversation *im = PURPLE_IM_CONVERSATION(gtkconv->active_conv);
PurpleBuddyIcon *icon;
const void *data;
size_t len;
- icon = purple_conv_im_get_icon(PURPLE_CONV_IM(conv));
+ icon = purple_im_conversation_get_icon(im);
data = purple_buddy_icon_get_data(icon, &len);
if ((len <= 0) || (data == NULL) || !purple_util_write_data_to_file_absolute(filename, data, len)) {
- purple_notify_error(gtkconv, NULL, _("Unable to save icon file to disk."), NULL);
+ purple_notify_error(gtkconv, NULL, _("Unable to save icon file to disk."), NULL, NULL);
}
}
@@ -2760,7 +2828,7 @@ custom_icon_sel_cb(const char *filename, gpointer data)
PurpleAccount *account = purple_conversation_get_account(conv);
name = purple_conversation_get_name(conv);
- buddy = purple_find_buddy(account, name);
+ 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;
@@ -2795,9 +2863,9 @@ change_size_cb(GtkWidget *widget, PidginConversation *gtkconv)
}
gtk_widget_set_size_request(gtkconv->u.im->icon_container, -1, size);
- pidgin_conv_update_buddy_icon(conv);
+ pidgin_conv_update_buddy_icon(PURPLE_IM_CONVERSATION(conv));
- buddies = purple_find_buddies(purple_conversation_get_account(conv),
+ buddies = purple_blist_find_buddies(purple_conversation_get_account(conv),
purple_conversation_get_name(conv));
for (; buddies; buddies = g_slist_delete_link(buddies, buddies)) {
PurpleBuddy *buddy = buddies->data;
@@ -2817,7 +2885,7 @@ remove_custom_icon_cb(GtkWidget *widget, PidginConversation *gtkconv)
account = purple_conversation_get_account(conv);
name = purple_conversation_get_name(conv);
- buddy = purple_find_buddy(account, name);
+ buddy = purple_blist_find_buddy(account, name);
if (!buddy) {
return;
}
@@ -2835,20 +2903,19 @@ icon_menu_save_cb(GtkWidget *widget, PidginConversation *gtkconv)
g_return_if_fail(conv != NULL);
- ext = purple_buddy_icon_get_extension(purple_conv_im_get_icon(PURPLE_CONV_IM(conv)));
+ ext = purple_buddy_icon_get_extension(purple_im_conversation_get_icon(PURPLE_IM_CONVERSATION(conv)));
- buf = g_strdup_printf("%s.%s", purple_normalize(conv->account, conv->name), ext);
+ buf = g_strdup_printf("%s.%s", purple_normalize(purple_conversation_get_account(conv), purple_conversation_get_name(conv)), ext);
purple_request_file(gtkconv, _("Save Icon"), buf, TRUE,
- G_CALLBACK(saveicon_writefile_cb), NULL,
- conv->account, NULL, conv,
- gtkconv);
+ G_CALLBACK(saveicon_writefile_cb), NULL,
+ purple_request_cpar_from_conversation(conv), gtkconv);
g_free(buf);
}
static void
-stop_anim(GtkObject *obj, PidginConversation *gtkconv)
+stop_anim(GtkWidget *widget, PidginConversation *gtkconv)
{
if (gtkconv->u.im->icon_timer != 0)
g_source_remove(gtkconv->u.im->icon_timer);
@@ -2870,7 +2937,7 @@ toggle_icon_animate_cb(GtkWidget *w, PidginConversation *gtkconv)
}
static gboolean
-icon_menu(GtkObject *obj, GdkEventButton *e, PidginConversation *gtkconv)
+icon_menu(GtkWidget *widget, GdkEventButton *e, PidginConversation *gtkconv)
{
static GtkWidget *menu = NULL;
PurpleConversation *conv;
@@ -2919,7 +2986,7 @@ icon_menu(GtkObject *obj, GdkEventButton *e, PidginConversation *gtkconv)
/* Is there a custom icon for this person? */
conv = gtkconv->active_conv;
- buddy = purple_find_buddy(purple_conversation_get_account(conv),
+ buddy = purple_blist_find_buddy(purple_conversation_get_account(conv),
purple_conversation_get_name(conv));
if (buddy)
{
@@ -2957,24 +3024,15 @@ pidgin_conv_present_conversation(PurpleConversation *conv)
gtk_window_present(GTK_WINDOW(gtkconv->win->window));
}
-GList *
-pidgin_conversations_find_unseen_list(PurpleConversationType type,
- PidginUnseenState min_state,
- gboolean hidden_only,
- guint max_count)
+static GList *
+pidgin_conversations_get_unseen(GList *l,
+ PidginUnseenState min_state,
+ gboolean hidden_only,
+ guint max_count)
{
- GList *l;
GList *r = NULL;
guint c = 0;
- if (type == PURPLE_CONV_TYPE_IM) {
- l = purple_get_ims();
- } else if (type == PURPLE_CONV_TYPE_CHAT) {
- l = purple_get_chats();
- } else {
- l = purple_get_conversations();
- }
-
for (; l != NULL && (max_count == 0 || c < max_count); l = l->next) {
PurpleConversation *conv = (PurpleConversation*)l->data;
PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
@@ -2994,6 +3052,33 @@ pidgin_conversations_find_unseen_list(PurpleConversationType type,
return r;
}
+GList *
+pidgin_conversations_get_unseen_all(PidginUnseenState min_state,
+ gboolean hidden_only,
+ guint max_count)
+{
+ return pidgin_conversations_get_unseen(purple_conversations_get_all(),
+ min_state, hidden_only, max_count);
+}
+
+GList *
+pidgin_conversations_get_unseen_ims(PidginUnseenState min_state,
+ gboolean hidden_only,
+ guint max_count)
+{
+ return pidgin_conversations_get_unseen(purple_conversations_get_ims(),
+ min_state, hidden_only, max_count);
+}
+
+GList *
+pidgin_conversations_get_unseen_chats(PidginUnseenState min_state,
+ gboolean hidden_only,
+ guint max_count)
+{
+ return pidgin_conversations_get_unseen(purple_conversations_get_chats(),
+ min_state, hidden_only, max_count);
+}
+
static void
unseen_conv_menu_cb(GtkMenuItem *item, PurpleConversation *conv)
{
@@ -3057,121 +3142,125 @@ pidgin_conversations_fill_menu(GtkWidget *menu, GList *convs)
return ret;
}
-PidginWindow *
+PidginConvWindow *
pidgin_conv_get_window(PidginConversation *gtkconv)
{
g_return_val_if_fail(gtkconv != NULL, NULL);
return gtkconv->win;
}
-static GtkItemFactoryEntry menu_items[] =
+static GtkActionEntry menu_entries[] =
+/* TODO: fill out tooltips... */
{
/* Conversation menu */
- { N_("/_Conversation"), NULL, NULL, 0, "<Branch>", NULL },
-
- { N_("/Conversation/New Instant _Message..."), "<CTL>M", menu_new_conv_cb,
- 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW },
- { N_("/Conversation/Join a _Chat..."), NULL, menu_join_chat_cb,
- 0, "<StockItem>", PIDGIN_STOCK_CHAT },
-
- { "/Conversation/sep0", NULL, NULL, 0, "<Separator>", NULL },
-
- { N_("/Conversation/_Find..."), NULL, menu_find_cb, 0,
- "<StockItem>", GTK_STOCK_FIND },
- { N_("/Conversation/View _Log"), NULL, menu_view_log_cb, 0, "<Item>", NULL },
- { N_("/Conversation/_Save As..."), NULL, menu_save_as_cb, 0,
- "<StockItem>", GTK_STOCK_SAVE_AS },
- { N_("/Conversation/Clea_r Scrollback"), "<CTL>L", menu_clear_cb, 0, "<StockItem>", GTK_STOCK_CLEAR },
-
- { "/Conversation/sep1", NULL, NULL, 0, "<Separator>", NULL },
+ { "ConversationMenu", NULL, N_("_Conversation"), NULL, NULL, NULL },
+ { "NewInstantMessage", PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW, N_("New Instant _Message..."), "<control>M", NULL, G_CALLBACK(menu_new_conv_cb) },
+ { "JoinAChat", PIDGIN_STOCK_CHAT, N_("Join a _Chat..."), NULL, NULL, G_CALLBACK(menu_join_chat_cb) },
+ { "Find", GTK_STOCK_FIND, N_("_Find..."), NULL, NULL, G_CALLBACK(menu_find_cb) },
+ { "ViewLog", NULL, N_("View _Log"), NULL, NULL, G_CALLBACK(menu_view_log_cb) },
+ { "SaveAs", GTK_STOCK_SAVE_AS, N_("_Save As..."), NULL, NULL, G_CALLBACK(menu_save_as_cb) },
+ { "ClearScrollback", GTK_STOCK_CLEAR, N_("Clea_r Scrollback"), "<control>L", NULL, G_CALLBACK(menu_clear_cb) },
#ifdef USE_VV
- { N_("/Conversation/M_edia"), NULL, NULL, 0, "<Branch>", NULL },
-
- { N_("/Conversation/Media/_Audio Call"), NULL, menu_initiate_media_call_cb, 0,
- "<StockItem>", PIDGIN_STOCK_TOOLBAR_AUDIO_CALL },
- { N_("/Conversation/Media/_Video Call"), NULL, menu_initiate_media_call_cb, 1,
- "<StockItem>", PIDGIN_STOCK_TOOLBAR_VIDEO_CALL },
- { N_("/Conversation/Media/Audio\\/Video _Call"), NULL, menu_initiate_media_call_cb, 2,
- "<StockItem>", PIDGIN_STOCK_TOOLBAR_VIDEO_CALL },
+ { "MediaMenu", NULL, N_("M_edia"), NULL, NULL, NULL },
+ { "AudioCall", PIDGIN_STOCK_TOOLBAR_AUDIO_CALL, N_("_Audio Call"), NULL, NULL, G_CALLBACK(menu_initiate_media_call_cb) },
+ { "VideoCall", PIDGIN_STOCK_TOOLBAR_VIDEO_CALL, N_("_Video Call"), NULL, NULL, G_CALLBACK(menu_initiate_media_call_cb) },
+ { "AudioVideoCall", PIDGIN_STOCK_TOOLBAR_VIDEO_CALL, N_("Audio/Video _Call"), NULL, NULL, G_CALLBACK(menu_initiate_media_call_cb) },
#endif
- { N_("/Conversation/Se_nd File..."), NULL, menu_send_file_cb, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_SEND_FILE },
- { N_("/Conversation/Get _Attention"), NULL, menu_get_attention_cb, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_SEND_ATTENTION },
- { N_("/Conversation/Add Buddy _Pounce..."), NULL, menu_add_pounce_cb,
- 0, "<Item>", NULL },
- { N_("/Conversation/_Get Info"), "<CTL>O", menu_get_info_cb, 0,
- "<StockItem>", PIDGIN_STOCK_TOOLBAR_USER_INFO },
- { N_("/Conversation/In_vite..."), NULL, menu_invite_cb, 0,
- "<Item>", NULL },
- { N_("/Conversation/M_ore"), NULL, NULL, 0, "<Branch>", NULL },
-
- { "/Conversation/sep2", NULL, NULL, 0, "<Separator>", NULL },
-
- { N_("/Conversation/Al_ias..."), NULL, menu_alias_cb, 0,
- "<Item>", NULL },
- { N_("/Conversation/_Block..."), NULL, menu_block_cb, 0,
- "<StockItem>", PIDGIN_STOCK_TOOLBAR_BLOCK },
- { N_("/Conversation/_Unblock..."), NULL, menu_unblock_cb, 0,
- "<StockItem>", PIDGIN_STOCK_TOOLBAR_UNBLOCK },
- { N_("/Conversation/_Add..."), NULL, menu_add_remove_cb, 0,
- "<StockItem>", GTK_STOCK_ADD },
- { N_("/Conversation/_Remove..."), NULL, menu_add_remove_cb, 0,
- "<StockItem>", GTK_STOCK_REMOVE },
-
- { "/Conversation/sep3", NULL, NULL, 0, "<Separator>", NULL },
-
- { N_("/Conversation/Insert Lin_k..."), NULL, menu_insert_link_cb, 0,
- "<StockItem>", PIDGIN_STOCK_TOOLBAR_INSERT_LINK },
- { N_("/Conversation/Insert Imag_e..."), NULL, menu_insert_image_cb, 0,
- "<StockItem>", PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE },
-
- { "/Conversation/sep4", NULL, NULL, 0, "<Separator>", NULL },
-
-
- { N_("/Conversation/_Close"), NULL, menu_close_conv_cb, 0,
- "<StockItem>", GTK_STOCK_CLOSE },
+ { "SendFile", PIDGIN_STOCK_TOOLBAR_SEND_FILE, N_("Se_nd File..."), NULL, NULL, G_CALLBACK(menu_send_file_cb) },
+ { "GetAttention", PIDGIN_STOCK_TOOLBAR_SEND_ATTENTION, N_("Get _Attention"), NULL, NULL, G_CALLBACK(menu_get_attention_cb) },
+ { "AddBuddyPounce", NULL, N_("Add Buddy _Pounce..."), NULL, NULL, G_CALLBACK(menu_add_pounce_cb) },
+ { "GetInfo", PIDGIN_STOCK_TOOLBAR_USER_INFO, N_("_Get Info"), "<control>O", NULL, G_CALLBACK(menu_get_info_cb) },
+ { "Invite", NULL, N_("In_vite..."), NULL, NULL, G_CALLBACK(menu_invite_cb) },
+ { "MoreMenu", NULL, N_("M_ore"), NULL, NULL, NULL },
+ { "Alias", NULL, N_("Al_ias..."), NULL, NULL, G_CALLBACK(menu_alias_cb) },
+ { "Block", PIDGIN_STOCK_TOOLBAR_BLOCK, N_("_Block..."), NULL, NULL, G_CALLBACK(menu_block_cb) },
+ { "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) },
+ { "Close", GTK_STOCK_CLOSE, N_("_Close"), NULL, NULL, G_CALLBACK(menu_close_conv_cb) },
/* Options */
- { N_("/_Options"), NULL, NULL, 0, "<Branch>", NULL },
- { N_("/Options/Enable _Logging"), NULL, menu_logging_cb, 0, "<CheckItem>", NULL },
- { N_("/Options/Enable _Sounds"), NULL, menu_sounds_cb, 0, "<CheckItem>", NULL },
- { "/Options/sep0", NULL, NULL, 0, "<Separator>", NULL },
- { N_("/Options/Show Formatting _Toolbars"), NULL, menu_toolbar_cb, 0, "<CheckItem>", NULL },
- { N_("/Options/Show Ti_mestamps"), NULL, menu_timestamps_cb, 0, "<CheckItem>", NULL },
+ { "OptionsMenu", NULL, N_("_Options"), NULL, NULL, NULL },
};
-static const int menu_item_count =
-sizeof(menu_items) / sizeof(*menu_items);
+/* Toggle items */
+static const GtkToggleActionEntry menu_toggle_entries[] = {
+ { "EnableLogging", NULL, N_("Enable _Logging"), NULL, NULL, G_CALLBACK(menu_logging_cb), FALSE },
+ { "EnableSounds", NULL, N_("Enable _Sounds"), NULL, NULL, G_CALLBACK(menu_sounds_cb), FALSE },
+ { "ShowFormattingToolbars", NULL, N_("Show Formatting _Toolbars"), NULL, NULL, G_CALLBACK(menu_toolbar_cb), FALSE },
+};
-static const char *
-item_factory_translate_func (const char *path, gpointer func_data)
-{
- return _(path);
-}
+static const char *conversation_menu =
+"<ui>"
+ "<menubar name='Conversation'>"
+ "<menu action='ConversationMenu'>"
+ "<menuitem action='NewInstantMessage'/>"
+ "<menuitem action='JoinAChat'/>"
+ "<separator/>"
+ "<menuitem action='Find'/>"
+ "<menuitem action='ViewLog'/>"
+ "<menuitem action='SaveAs'/>"
+ "<menuitem action='ClearScrollback'/>"
+ "<separator/>"
+#ifdef USE_VV
+ "<menu action='MediaMenu'>"
+ "<menuitem action='AudioCall'/>"
+ "<menuitem action='VideoCall'/>"
+ "<menuitem action='AudioVideoCall'/>"
+ "</menu>"
+#endif
+ "<menuitem action='SendFile'/>"
+ "<menuitem action='GetAttention'/>"
+ "<menuitem action='AddBuddyPounce'/>"
+ "<menuitem action='GetInfo'/>"
+ "<menuitem action='Invite'/>"
+ "<menu action='MoreMenu'/>"
+ "<separator/>"
+ "<menuitem action='Alias'/>"
+ "<menuitem action='Block'/>"
+ "<menuitem action='Unblock'/>"
+ "<menuitem action='Add'/>"
+ "<menuitem action='Remove'/>"
+ "<separator/>"
+ "<menuitem action='InsertLink'/>"
+ "<menuitem action='InsertImage'/>"
+ "<separator/>"
+ "<menuitem action='Close'/>"
+ "</menu>"
+ "<menu action='OptionsMenu'>"
+ "<menuitem action='EnableLogging'/>"
+ "<menuitem action='EnableSounds'/>"
+ "<separator/>"
+ "<menuitem action='ShowFormattingToolbars'/>"
+ "</menu>"
+ "</menubar>"
+"</ui>";
static void
sound_method_pref_changed_cb(const char *name, PurplePrefType type,
gconstpointer value, gpointer data)
{
- PidginWindow *win = data;
+ PidginConvWindow *win = data;
const char *method = value;
if (!strcmp(method, "none"))
{
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(win->menu.sounds),
- FALSE);
- gtk_widget_set_sensitive(win->menu.sounds, FALSE);
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(win->menu->sounds),
+ FALSE);
+ gtk_action_set_sensitive(win->menu->sounds, FALSE);
}
else
{
PidginConversation *gtkconv = pidgin_conv_window_get_active_gtkconv(win);
if (gtkconv != NULL)
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(win->menu.sounds),
- gtkconv->make_sound);
- gtk_widget_set_sensitive(win->menu.sounds, TRUE);
-
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(win->menu->sounds),
+ gtkconv->make_sound);
+ gtk_action_set_sensitive(win->menu->sounds, TRUE);
}
}
@@ -3181,21 +3270,23 @@ populate_menu_with_options(GtkWidget *menu, PidginConversation *gtkconv, gboolea
{
GList *list;
PurpleConversation *conv;
+ PurpleAccount *account;
PurpleBlistNode *node = NULL;
PurpleChat *chat = NULL;
PurpleBuddy *buddy = NULL;
gboolean ret;
conv = gtkconv->active_conv;
+ account = purple_conversation_get_account(conv);
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
- chat = purple_blist_find_chat(conv->account, conv->name);
+ if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
+ chat = purple_blist_find_chat(account, purple_conversation_get_name(conv));
- if ((chat == NULL) && (gtkconv->imhtml != NULL)) {
- chat = g_object_get_data(G_OBJECT(gtkconv->imhtml), "transient_chat");
+ if ((chat == NULL) && (gtkconv->webview != NULL)) {
+ chat = g_object_get_data(G_OBJECT(gtkconv->webview), "transient_chat");
}
- if ((chat == NULL) && (gtkconv->imhtml != NULL)) {
+ if ((chat == NULL) && (gtkconv->webview != NULL)) {
GHashTable *components;
PurpleAccount *account = purple_conversation_get_account(conv);
PurplePlugin *prpl = purple_find_prpl(purple_account_get_protocol_id(account));
@@ -3210,31 +3301,24 @@ populate_menu_with_options(GtkWidget *menu, PidginConversation *gtkconv, gboolea
g_hash_table_replace(components, g_strdup("channel"),
g_strdup(purple_conversation_get_name(conv)));
}
- chat = purple_chat_new(conv->account, NULL, components);
- purple_blist_node_set_flags((PurpleBlistNode *)chat,
- PURPLE_BLIST_NODE_FLAG_NO_SAVE);
- g_object_set_data_full(G_OBJECT(gtkconv->imhtml), "transient_chat",
+ 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",
chat, (GDestroyNotify)purple_blist_remove_chat);
}
} else {
- if (!purple_account_is_connected(conv->account))
+ if (!purple_account_is_connected(account))
return FALSE;
- buddy = purple_find_buddy(conv->account, conv->name);
-
- /* gotta remain bug-compatible :( libpurple < 2.0.2 didn't handle
- * removing "isolated" buddy nodes well */
- if (purple_version_check(2, 0, 2) == NULL) {
- if ((buddy == NULL) && (gtkconv->imhtml != NULL)) {
- buddy = g_object_get_data(G_OBJECT(gtkconv->imhtml), "transient_buddy");
- }
+ 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 == NULL) && (gtkconv->imhtml != NULL)) {
- buddy = purple_buddy_new(conv->account, conv->name, NULL);
- purple_blist_node_set_flags((PurpleBlistNode *)buddy,
- PURPLE_BLIST_NODE_FLAG_NO_SAVE);
- g_object_set_data_full(G_OBJECT(gtkconv->imhtml), "transient_buddy",
- buddy, (GDestroyNotify)purple_buddy_destroy);
+ 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",
+ buddy, (GDestroyNotify)g_object_unref);
}
}
}
@@ -3252,8 +3336,8 @@ populate_menu_with_options(GtkWidget *menu, PidginConversation *gtkconv, gboolea
/* XXX: */
}
} else if (node) {
- if (purple_account_is_connected(conv->account))
- pidgin_append_blist_node_proto_menu(menu, conv->account->gc, node);
+ if (purple_account_is_connected(account))
+ pidgin_append_blist_node_proto_menu(menu, purple_account_get_connection(account), node);
pidgin_append_blist_node_extended_menu(menu, node);
}
@@ -3267,7 +3351,7 @@ populate_menu_with_options(GtkWidget *menu, PidginConversation *gtkconv, gboolea
}
static void
-regenerate_media_items(PidginWindow *win)
+regenerate_media_items(PidginConvWindow *win)
{
#ifdef USE_VV
PurpleAccount *account;
@@ -3293,44 +3377,104 @@ regenerate_media_items(PidginWindow *win)
* Check if account support voice and/or calls, and
* if the current buddy supports it.
*/
- if (account != NULL && purple_conversation_get_type(conv)
- == PURPLE_CONV_TYPE_IM) {
+ if (account != NULL && PURPLE_IS_IM_CONVERSATION(conv)) {
PurpleMediaCaps caps =
purple_prpl_get_media_caps(account,
purple_conversation_get_name(conv));
- gtk_widget_set_sensitive(win->audio_call,
+ gtk_action_set_sensitive(win->menu->audio_call,
caps & PURPLE_MEDIA_CAPS_AUDIO
? TRUE : FALSE);
- gtk_widget_set_sensitive(win->video_call,
+ gtk_action_set_sensitive(win->menu->video_call,
caps & PURPLE_MEDIA_CAPS_VIDEO
? TRUE : FALSE);
- gtk_widget_set_sensitive(win->audio_video_call,
+ gtk_action_set_sensitive(win->menu->audio_video_call,
caps & PURPLE_MEDIA_CAPS_AUDIO_VIDEO
? TRUE : FALSE);
- } else if (purple_conversation_get_type(conv)
- == PURPLE_CONV_TYPE_CHAT) {
+ } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
/* for now, don't care about chats... */
- gtk_widget_set_sensitive(win->audio_call, FALSE);
- gtk_widget_set_sensitive(win->video_call, FALSE);
- gtk_widget_set_sensitive(win->audio_video_call, FALSE);
+ gtk_action_set_sensitive(win->menu->audio_call, FALSE);
+ gtk_action_set_sensitive(win->menu->video_call, FALSE);
+ gtk_action_set_sensitive(win->menu->audio_video_call, FALSE);
} else {
- gtk_widget_set_sensitive(win->audio_call, FALSE);
- gtk_widget_set_sensitive(win->video_call, FALSE);
- gtk_widget_set_sensitive(win->audio_video_call, FALSE);
+ gtk_action_set_sensitive(win->menu->audio_call, FALSE);
+ gtk_action_set_sensitive(win->menu->video_call, FALSE);
+ gtk_action_set_sensitive(win->menu->audio_video_call, FALSE);
}
#endif
}
static void
-regenerate_options_items(PidginWindow *win)
+regenerate_attention_items(PidginConvWindow *win)
+{
+ GtkWidget *attention;
+ GtkWidget *menu;
+ PurpleConversation *conv;
+ PurpleConnection *pc;
+ PurplePlugin *prpl = NULL;
+ PurplePluginProtocolInfo *prpl_info = NULL;
+ GList *list;
+
+ conv = pidgin_conv_window_get_active_conversation(win);
+ if (!conv)
+ return;
+
+ attention = gtk_ui_manager_get_widget(win->menu->ui,
+ "/Conversation/ConversationMenu/GetAttention");
+
+ /* Remove the previous entries */
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(attention), NULL);
+
+ pc = purple_conversation_get_connection(conv);
+ if (pc != NULL)
+ prpl = purple_connection_get_prpl(pc);
+ if (prpl != NULL)
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+
+ if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_attention_types)) {
+ list = prpl_info->get_attention_types(purple_connection_get_account(pc));
+
+ /* Multiple attention types */
+ if (list && list->next) {
+ int index = 0;
+
+ menu = gtk_menu_new();
+ while (list) {
+ PurpleAttentionType *type;
+ GtkWidget *menuitem;
+
+ type = list->data;
+
+ menuitem = gtk_menu_item_new_with_label(purple_attention_type_get_name(type));
+ g_object_set_data(G_OBJECT(menuitem), "index", GINT_TO_POINTER(index));
+ g_signal_connect(G_OBJECT(menuitem), "activate",
+ G_CALLBACK(menu_get_attention_cb),
+ win);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+
+ index++;
+ list = g_list_delete_link(list, list);
+ }
+
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(attention), menu);
+ gtk_widget_show_all(menu);
+ }
+ }
+}
+
+static void
+regenerate_options_items(PidginConvWindow *win)
{
GtkWidget *menu;
PidginConversation *gtkconv;
GList *list;
+ GtkWidget *more_menu;
gtkconv = pidgin_conv_window_get_active_gtkconv(win);
- menu = gtk_item_factory_get_widget(win->menu.item_factory, N_("/Conversation/More"));
+ more_menu = gtk_ui_manager_get_widget(win->menu->ui,
+ "/Conversation/ConversationMenu/MoreMenu");
+ gtk_widget_show(more_menu);
+ menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(more_menu));
/* Remove the previous entries */
for (list = gtk_container_get_children(GTK_CONTAINER(menu)); list; )
@@ -3351,7 +3495,7 @@ regenerate_options_items(PidginWindow *win)
}
static void
-remove_from_list(GtkWidget *widget, PidginWindow *win)
+remove_from_list(GtkWidget *widget, PidginConvWindow *win)
{
GList *list = g_object_get_data(G_OBJECT(win->window), "plugin-actions");
list = g_list_remove(list, widget);
@@ -3359,7 +3503,7 @@ remove_from_list(GtkWidget *widget, PidginWindow *win)
}
static void
-regenerate_plugins_items(PidginWindow *win)
+regenerate_plugins_items(PidginConvWindow *win)
{
GList *action_items;
GtkWidget *menu;
@@ -3386,7 +3530,8 @@ regenerate_plugins_items(PidginWindow *win)
action_items = g_list_delete_link(action_items, action_items);
}
- menu = gtk_item_factory_get_widget(win->menu.item_factory, N_("/Options"));
+ item = gtk_ui_manager_get_widget(win->menu->ui, "/Conversation/OptionsMenu");
+ menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(item));
list = purple_conversation_get_extended_menu(conv);
if (list) {
@@ -3406,165 +3551,178 @@ regenerate_plugins_items(PidginWindow *win)
static void menubar_activated(GtkWidget *item, gpointer data)
{
- PidginWindow *win = data;
+ PidginConvWindow *win = data;
regenerate_media_items(win);
regenerate_options_items(win);
regenerate_plugins_items(win);
+ regenerate_attention_items(win);
/* The following are to make sure the 'More' submenu is not regenerated every time
* the focus shifts from 'Conversations' to some other menu and back. */
g_signal_handlers_block_by_func(G_OBJECT(item), G_CALLBACK(menubar_activated), data);
- g_signal_connect(G_OBJECT(win->menu.menubar), "deactivate", G_CALLBACK(focus_out_from_menubar), data);
+ g_signal_connect(G_OBJECT(win->menu->menubar), "deactivate", G_CALLBACK(focus_out_from_menubar), data);
}
static void
-focus_out_from_menubar(GtkWidget *wid, PidginWindow *win)
+focus_out_from_menubar(GtkWidget *wid, PidginConvWindow *win)
{
/* The menubar has been deactivated. Make sure the 'More' submenu is regenerated next time
* the 'Conversation' menu pops up. */
- GtkWidget *menuitem = gtk_item_factory_get_item(win->menu.item_factory, N_("/Conversation"));
+ GtkWidget *menuitem = gtk_ui_manager_get_widget(win->menu->ui, "/Conversation/ConversationMenu");
g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), G_CALLBACK(menubar_activated), win);
- g_signal_handlers_disconnect_by_func(G_OBJECT(win->menu.menubar),
+ g_signal_handlers_disconnect_by_func(G_OBJECT(win->menu->menubar),
G_CALLBACK(focus_out_from_menubar), win);
}
static GtkWidget *
-setup_menubar(PidginWindow *win)
+setup_menubar(PidginConvWindow *win)
{
GtkAccelGroup *accel_group;
const char *method;
+ GtkActionGroup *action_group;
+ GError *error;
GtkWidget *menuitem;
- accel_group = gtk_accel_group_new ();
+ action_group = gtk_action_group_new("ConversationActions");
+#ifdef ENABLE_NLS
+ gtk_action_group_set_translation_domain(action_group, PACKAGE);
+#endif
+ gtk_action_group_add_actions(action_group,
+ menu_entries,
+ G_N_ELEMENTS(menu_entries),
+ win);
+ gtk_action_group_add_toggle_actions(action_group,
+ menu_toggle_entries,
+ G_N_ELEMENTS(menu_toggle_entries),
+ win);
+
+ win->menu->ui = gtk_ui_manager_new();
+ gtk_ui_manager_insert_action_group(win->menu->ui, action_group, 0);
+
+ accel_group = gtk_ui_manager_get_accel_group(win->menu->ui);
gtk_window_add_accel_group(GTK_WINDOW(win->window), accel_group);
- g_object_unref(accel_group);
-
- win->menu.item_factory =
- gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<main>", accel_group);
-
- gtk_item_factory_set_translate_func(win->menu.item_factory,
- (GtkTranslateFunc)item_factory_translate_func,
- NULL, NULL);
-
- gtk_item_factory_create_items(win->menu.item_factory, menu_item_count,
- menu_items, win);
g_signal_connect(G_OBJECT(accel_group), "accel-changed",
G_CALLBACK(pidgin_save_accels_cb), NULL);
+ error = NULL;
+ if (!gtk_ui_manager_add_ui_from_string(win->menu->ui, conversation_menu, -1, &error))
+ {
+ g_message("building menus failed: %s", error->message);
+ g_error_free(error);
+ exit(EXIT_FAILURE);
+ }
+
+ win->menu->menubar =
+ gtk_ui_manager_get_widget(win->menu->ui, "/Conversation");
+
/* Make sure the 'Conversation -> More' menuitems are regenerated whenever
* the 'Conversation' menu pops up because the entries can change after the
* conversation is created. */
- menuitem = gtk_item_factory_get_item(win->menu.item_factory, N_("/Conversation"));
+ menuitem = gtk_ui_manager_get_widget(win->menu->ui, "/Conversation/ConversationMenu");
g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(menubar_activated), win);
- win->menu.menubar =
- gtk_item_factory_get_widget(win->menu.item_factory, "<main>");
-
- win->menu.view_log =
- gtk_item_factory_get_widget(win->menu.item_factory,
- N_("/Conversation/View Log"));
+ win->menu->view_log =
+ gtk_ui_manager_get_action(win->menu->ui,
+ "/Conversation/ConversationMenu/ViewLog");
#ifdef USE_VV
- win->audio_call =
- gtk_item_factory_get_widget(win->menu.item_factory,
- N_("/Conversation/Media/Audio Call"));
- win->video_call =
- gtk_item_factory_get_widget(win->menu.item_factory,
- N_("/Conversation/Media/Video Call"));
- win->audio_video_call =
- gtk_item_factory_get_widget(win->menu.item_factory,
- N_("/Conversation/Media/Audio\\/Video Call"));
+ win->menu->audio_call =
+ gtk_ui_manager_get_action(win->menu->ui,
+ "/Conversation/ConversationMenu/MediaMenu/AudioCall");
+ win->menu->video_call =
+ gtk_ui_manager_get_action(win->menu->ui,
+ "/Conversation/ConversationMenu/MediaMenu/VideoCall");
+ win->menu->audio_video_call =
+ gtk_ui_manager_get_action(win->menu->ui,
+ "/Conversation/ConversationMenu/MediaMenu/AudioVideoCall");
#endif
/* --- */
- win->menu.send_file =
- gtk_item_factory_get_widget(win->menu.item_factory,
- N_("/Conversation/Send File..."));
+ win->menu->send_file =
+ gtk_ui_manager_get_action(win->menu->ui,
+ "/Conversation/ConversationMenu/SendFile");
+
+ win->menu->get_attention =
+ gtk_ui_manager_get_action(win->menu->ui,
+ "/Conversation/ConversationMenu/GetAttention");
- g_object_set_data(G_OBJECT(win->window), "get_attention",
- gtk_item_factory_get_widget(win->menu.item_factory,
- N_("/Conversation/Get Attention")));
- win->menu.add_pounce =
- gtk_item_factory_get_widget(win->menu.item_factory,
- N_("/Conversation/Add Buddy Pounce..."));
+ win->menu->add_pounce =
+ gtk_ui_manager_get_action(win->menu->ui,
+ "/Conversation/ConversationMenu/AddBuddyPounce");
/* --- */
- win->menu.get_info =
- gtk_item_factory_get_widget(win->menu.item_factory,
- N_("/Conversation/Get Info"));
+ win->menu->get_info =
+ gtk_ui_manager_get_action(win->menu->ui,
+ "/Conversation/ConversationMenu/GetInfo");
- win->menu.invite =
- gtk_item_factory_get_widget(win->menu.item_factory,
- N_("/Conversation/Invite..."));
+ win->menu->invite =
+ gtk_ui_manager_get_action(win->menu->ui,
+ "/Conversation/ConversationMenu/Invite");
/* --- */
- win->menu.alias =
- gtk_item_factory_get_widget(win->menu.item_factory,
- N_("/Conversation/Alias..."));
+ win->menu->alias =
+ gtk_ui_manager_get_action(win->menu->ui,
+ "/Conversation/ConversationMenu/Alias");
- win->menu.block =
- gtk_item_factory_get_widget(win->menu.item_factory,
- N_("/Conversation/Block..."));
+ win->menu->block =
+ gtk_ui_manager_get_action(win->menu->ui,
+ "/Conversation/ConversationMenu/Block");
- win->menu.unblock =
- gtk_item_factory_get_widget(win->menu.item_factory,
- N_("/Conversation/Unblock..."));
+ win->menu->unblock =
+ gtk_ui_manager_get_action(win->menu->ui,
+ "/Conversation/ConversationMenu/Unblock");
- win->menu.add =
- gtk_item_factory_get_widget(win->menu.item_factory,
- N_("/Conversation/Add..."));
+ win->menu->add =
+ gtk_ui_manager_get_action(win->menu->ui,
+ "/Conversation/ConversationMenu/Add");
- win->menu.remove =
- gtk_item_factory_get_widget(win->menu.item_factory,
- N_("/Conversation/Remove..."));
+ win->menu->remove =
+ gtk_ui_manager_get_action(win->menu->ui,
+ "/Conversation/ConversationMenu/Remove");
/* --- */
- win->menu.insert_link =
- gtk_item_factory_get_widget(win->menu.item_factory,
- N_("/Conversation/Insert Link..."));
+ win->menu->insert_link =
+ gtk_ui_manager_get_action(win->menu->ui,
+ "/Conversation/ConversationMenu/InsertLink");
- win->menu.insert_image =
- gtk_item_factory_get_widget(win->menu.item_factory,
- N_("/Conversation/Insert Image..."));
+ win->menu->insert_image =
+ gtk_ui_manager_get_action(win->menu->ui,
+ "/Conversation/ConversationMenu/InsertImage");
/* --- */
- win->menu.logging =
- gtk_item_factory_get_widget(win->menu.item_factory,
- N_("/Options/Enable Logging"));
- win->menu.sounds =
- gtk_item_factory_get_widget(win->menu.item_factory,
- N_("/Options/Enable Sounds"));
+ win->menu->logging =
+ gtk_ui_manager_get_action(win->menu->ui,
+ "/Conversation/OptionsMenu/EnableLogging");
+ win->menu->sounds =
+ gtk_ui_manager_get_action(win->menu->ui,
+ "/Conversation/OptionsMenu/EnableSounds");
method = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method");
if (method != NULL && !strcmp(method, "none"))
{
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(win->menu.sounds),
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(win->menu->sounds),
FALSE);
- gtk_widget_set_sensitive(win->menu.sounds, FALSE);
+ gtk_action_set_sensitive(win->menu->sounds, FALSE);
}
purple_prefs_connect_callback(win, PIDGIN_PREFS_ROOT "/sound/method",
sound_method_pref_changed_cb, win);
- win->menu.show_formatting_toolbar =
- gtk_item_factory_get_widget(win->menu.item_factory,
- N_("/Options/Show Formatting Toolbars"));
- win->menu.show_timestamps =
- gtk_item_factory_get_widget(win->menu.item_factory,
- N_("/Options/Show Timestamps"));
- win->menu.show_icon = NULL;
+ win->menu->show_formatting_toolbar =
+ gtk_ui_manager_get_action(win->menu->ui,
+ "/Conversation/OptionsMenu/ShowFormattingToolbars");
- win->menu.tray = pidgin_menu_tray_new();
- gtk_menu_shell_append(GTK_MENU_SHELL(win->menu.menubar),
- win->menu.tray);
- gtk_widget_show(win->menu.tray);
+ win->menu->tray = pidgin_menu_tray_new();
+ gtk_menu_shell_append(GTK_MENU_SHELL(win->menu->menubar),
+ win->menu->tray);
+ gtk_widget_show(win->menu->tray);
- gtk_widget_show(win->menu.menubar);
+ gtk_widget_show(win->menu->menubar);
- return win->menu.menubar;
+ return win->menu->menubar;
}
@@ -3576,27 +3734,27 @@ static void
got_typing_keypress(PidginConversation *gtkconv, gboolean first)
{
PurpleConversation *conv = gtkconv->active_conv;
- PurpleConvIm *im;
+ PurpleIMConversation *im;
/*
* We know we got something, so we at least have to make sure we don't
- * send PURPLE_TYPED any time soon.
+ * send PURPLE_IM_TYPED any time soon.
*/
- im = PURPLE_CONV_IM(conv);
+ im = PURPLE_IM_CONVERSATION(conv);
- purple_conv_im_stop_send_typed_timeout(im);
- purple_conv_im_start_send_typed_timeout(im);
+ purple_im_conversation_stop_send_typed_timeout(im);
+ purple_im_conversation_start_send_typed_timeout(im);
- /* Check if we need to send another PURPLE_TYPING message */
- if (first || (purple_conv_im_get_type_again(im) != 0 &&
- time(NULL) > purple_conv_im_get_type_again(im)))
+ /* Check if we need to send another PURPLE_IM_TYPING message */
+ if (first || (purple_im_conversation_get_type_again(im) != 0 &&
+ time(NULL) > purple_im_conversation_get_type_again(im)))
{
unsigned int timeout;
- timeout = serv_send_typing(purple_conversation_get_gc(conv),
+ timeout = purple_serv_send_typing(purple_conversation_get_connection(conv),
purple_conversation_get_name(conv),
- PURPLE_TYPING);
- purple_conv_im_set_type_again(im, timeout);
+ PURPLE_IM_TYPING);
+ purple_im_conversation_set_type_again(im, timeout);
}
}
@@ -3604,7 +3762,7 @@ got_typing_keypress(PidginConversation *gtkconv, gboolean first)
static gboolean
typing_animation(gpointer data) {
PidginConversation *gtkconv = data;
- PidginWindow *gtkwin = gtkconv->win;
+ PidginConvWindow *gtkwin = gtkconv->win;
const char *stock_id = NULL;
if(gtkconv != pidgin_conv_window_get_active_gtkconv(gtkwin)) {
@@ -3628,15 +3786,15 @@ typing_animation(gpointer data) {
stock_id = PIDGIN_STOCK_ANIMATION_TYPING4;
break;
}
- if (gtkwin->menu.typing_icon == NULL) {
- gtkwin->menu.typing_icon = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_MENU);
- pidgin_menu_tray_append(PIDGIN_MENU_TRAY(gtkwin->menu.tray),
- gtkwin->menu.typing_icon,
- _("User is typing..."));
+ if (gtkwin->menu->typing_icon == NULL) {
+ gtkwin->menu->typing_icon = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_MENU);
+ pidgin_menu_tray_append(PIDGIN_MENU_TRAY(gtkwin->menu->tray),
+ gtkwin->menu->typing_icon,
+ _("User is typing..."));
} else {
- gtk_image_set_from_stock(GTK_IMAGE(gtkwin->menu.typing_icon), stock_id, GTK_ICON_SIZE_MENU);
+ gtk_image_set_from_stock(GTK_IMAGE(gtkwin->menu->typing_icon), stock_id, GTK_ICON_SIZE_MENU);
}
- gtk_widget_show(gtkwin->menu.typing_icon);
+ gtk_widget_show(gtkwin->menu->typing_icon);
return TRUE;
}
#endif
@@ -3644,6 +3802,8 @@ typing_animation(gpointer data) {
static void
update_typing_message(PidginConversation *gtkconv, const char *message)
{
+ /* TODO WEBKIT: this is not handled at all */
+#if 0
GtkTextBuffer *buffer;
GtkTextMark *stmark, *enmark;
@@ -3676,22 +3836,21 @@ update_typing_message(PidginConversation *gtkconv, const char *message)
gtk_text_buffer_get_end_iter(buffer, &iter);
gtk_text_buffer_create_mark(buffer, "typing-notification-end", &iter, TRUE);
}
+#endif /* if 0 */
}
static void
update_typing_icon(PidginConversation *gtkconv)
{
- PurpleConvIm *im = NULL;
- PurpleConversation *conv = gtkconv->active_conv;
+ PurpleIMConversation *im;
char *message = NULL;
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
- im = PURPLE_CONV_IM(conv);
-
- if (im == NULL)
+ if (!PURPLE_IS_IM_CONVERSATION(gtkconv->active_conv))
return;
- if (purple_conv_im_get_typing_state(im) == PURPLE_NOT_TYPING) {
+ im = PURPLE_IM_CONVERSATION(gtkconv->active_conv);
+
+ if (purple_im_conversation_get_typing_state(im) == PURPLE_IM_NOT_TYPING) {
#ifdef RESERVE_LINE
update_typing_message(gtkconv, NULL);
#else
@@ -3700,10 +3859,10 @@ update_typing_icon(PidginConversation *gtkconv)
return;
}
- if (purple_conv_im_get_typing_state(im) == PURPLE_TYPING) {
- message = g_strdup_printf(_("\n%s is typing..."), purple_conversation_get_title(conv));
+ if (purple_im_conversation_get_typing_state(im) == PURPLE_IM_TYPING) {
+ message = g_strdup_printf(_("\n%s is typing..."), purple_conversation_get_title(PURPLE_CONVERSATION(im)));
} else {
- message = g_strdup_printf(_("\n%s has stopped typing"), purple_conversation_get_title(conv));
+ message = g_strdup_printf(_("\n%s has stopped typing"), purple_conversation_get_title(PURPLE_CONVERSATION(im)));
}
update_typing_message(gtkconv, message);
@@ -3711,7 +3870,7 @@ update_typing_icon(PidginConversation *gtkconv)
}
static gboolean
-update_send_to_selection(PidginWindow *win)
+update_send_to_selection(PidginConvWindow *win)
{
PurpleAccount *account;
PurpleConversation *conv;
@@ -3729,16 +3888,15 @@ update_send_to_selection(PidginWindow *win)
if (account == NULL)
return FALSE;
- if (win->menu.send_to == NULL)
+ if (win->menu->send_to == NULL)
return FALSE;
- if (!(b = purple_find_buddy(account, conv->name)))
+ if (!(b = purple_blist_find_buddy(account, purple_conversation_get_name(conv))))
return FALSE;
+ gtk_widget_show(win->menu->send_to);
- gtk_widget_show(win->menu.send_to);
-
- menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(win->menu.send_to));
+ menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(win->menu->send_to));
for (child = gtk_container_get_children(GTK_CONTAINER(menu));
child != NULL;
@@ -3749,7 +3907,7 @@ update_send_to_selection(PidginWindow *win)
PurpleAccount *item_account = g_object_get_data(G_OBJECT(item), "purple_account");
gchar *buddy_name = g_object_get_data(G_OBJECT(item),
"purple_buddy_name");
- item_buddy = purple_find_buddy(item_account, buddy_name);
+ item_buddy = purple_blist_find_buddy(item_account, buddy_name);
if (b == item_buddy) {
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
@@ -3775,12 +3933,28 @@ send_to_item_leave_notify_cb(GtkWidget *menuitem, GdkEventCrossing *event, GtkWi
return FALSE;
}
+static GtkWidget *
+e2ee_state_to_gtkimage(PurpleE2eeState *state)
+{
+ PurpleImage *img;
+
+ img = _pidgin_e2ee_stock_icon_get(
+ purple_e2ee_state_get_stock_icon(state));
+ if (!img)
+ return NULL;
+
+ return gtk_image_new_from_pixbuf(pidgin_pixbuf_from_image(img));
+}
+
static void
-create_sendto_item(GtkWidget *menu, GtkSizeGroup *sg, GSList **group, PurpleBuddy *buddy, PurpleAccount *account, const char *name)
+create_sendto_item(GtkWidget *menu, GtkSizeGroup *sg, GSList **group,
+ PurpleBuddy *buddy, PurpleAccount *account, const char *name,
+ gboolean e2ee_enabled)
{
GtkWidget *box;
GtkWidget *label;
GtkWidget *image;
+ GtkWidget *e2ee_image = NULL;
GtkWidget *menuitem;
GdkPixbuf *pixbuf;
gchar *text;
@@ -3797,6 +3971,20 @@ create_sendto_item(GtkWidget *menu, GtkSizeGroup *sg, GSList **group, PurpleBudd
g_object_unref(G_OBJECT(pixbuf));
}
+ if (e2ee_enabled) {
+ PurpleIMConversation *im;
+ PurpleE2eeState *state = NULL;
+
+ im = purple_conversations_find_im_with_account(
+ purple_buddy_get_name(buddy), purple_buddy_get_account(buddy));
+ if (im)
+ state = purple_conversation_get_e2ee_state(PURPLE_CONVERSATION(im));
+ if (state)
+ e2ee_image = e2ee_state_to_gtkimage(state);
+ else
+ e2ee_image = gtk_image_new();
+ }
+
gtk_size_group_add_widget(sg, image);
/* Make our menu item */
@@ -3806,14 +3994,17 @@ create_sendto_item(GtkWidget *menu, GtkSizeGroup *sg, GSList **group, PurpleBudd
*group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(menuitem));
/* Do some evil, see some evil, speak some evil. */
- box = gtk_hbox_new(FALSE, 0);
+ box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
label = gtk_bin_get_child(GTK_BIN(menuitem));
g_object_ref(label);
gtk_container_remove(GTK_CONTAINER(menuitem), label);
gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 0);
+
gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 4);
+ if (e2ee_image)
+ gtk_box_pack_start(GTK_BOX(box), e2ee_image, FALSE, FALSE, 0);
if (buddy != NULL &&
!purple_presence_is_online(purple_buddy_get_presence(buddy)))
@@ -3836,6 +4027,8 @@ create_sendto_item(GtkWidget *menu, GtkSizeGroup *sg, GSList **group, PurpleBudd
gtk_widget_show(label);
gtk_widget_show(image);
+ if (e2ee_image)
+ gtk_widget_show(e2ee_image);
gtk_widget_show(box);
/* Set our data and callbacks. */
@@ -3855,8 +4048,8 @@ compare_buddy_presence(PurplePresence *p1, PurplePresence *p2)
/* This is necessary because multiple PurpleBuddy's don't share the same
* PurplePresence anymore.
*/
- PurpleBuddy *b1 = purple_presence_get_buddy(p1);
- PurpleBuddy *b2 = purple_presence_get_buddy(p2);
+ PurpleBuddy *b1 = purple_buddy_presence_get_buddy(PURPLE_BUDDY_PRESENCE(p1));
+ PurpleBuddy *b2 = purple_buddy_presence_get_buddy(PURPLE_BUDDY_PRESENCE(p2));
if (purple_buddy_get_account(b1) == purple_buddy_get_account(b2) &&
strcmp(purple_buddy_get_name(b1), purple_buddy_get_name(b2)) == 0)
return FALSE;
@@ -3864,7 +4057,7 @@ compare_buddy_presence(PurplePresence *p1, PurplePresence *p2)
}
static void
-generate_send_to_items(PidginWindow *win)
+generate_send_to_items(PidginConvWindow *win)
{
GtkWidget *menu;
GSList *group = NULL;
@@ -3878,22 +4071,22 @@ generate_send_to_items(PidginWindow *win)
g_return_if_fail(gtkconv != NULL);
- if (win->menu.send_to != NULL)
- gtk_widget_destroy(win->menu.send_to);
+ if (win->menu->send_to != NULL)
+ gtk_widget_destroy(win->menu->send_to);
/* Build the Send To menu */
- win->menu.send_to = gtk_menu_item_new_with_mnemonic(_("S_end To"));
- gtk_widget_show(win->menu.send_to);
+ win->menu->send_to = gtk_menu_item_new_with_mnemonic(_("S_end To"));
+ gtk_widget_show(win->menu->send_to);
menu = gtk_menu_new();
- gtk_menu_shell_insert(GTK_MENU_SHELL(win->menu.menubar),
- win->menu.send_to, 2);
- gtk_menu_item_set_submenu(GTK_MENU_ITEM(win->menu.send_to), menu);
+ gtk_menu_shell_insert(GTK_MENU_SHELL(win->menu->menubar),
+ win->menu->send_to, 2);
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(win->menu->send_to), menu);
gtk_widget_show(menu);
- if (gtkconv->active_conv->type == PURPLE_CONV_TYPE_IM) {
- buds = purple_find_buddies(gtkconv->active_conv->account, gtkconv->active_conv->name);
+ if (PURPLE_IS_IM_CONVERSATION(gtkconv->active_conv)) {
+ buds = purple_blist_find_buddies(purple_conversation_get_account(gtkconv->active_conv), purple_conversation_get_name(gtkconv->active_conv));
if (buds == NULL)
{
@@ -3901,6 +4094,7 @@ generate_send_to_items(PidginWindow *win)
}
else
{
+ gboolean e2ee_enabled = FALSE;
GList *list = NULL, *iter;
for (l = buds; l != NULL; l = l->next)
{
@@ -3912,12 +4106,18 @@ generate_send_to_items(PidginWindow *win)
{
PurpleBuddy *buddy = (PurpleBuddy *)node;
PurpleAccount *account;
+ PurpleIMConversation *im;
- if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if (!PURPLE_IS_BUDDY(node))
continue;
+ im = purple_conversations_find_im_with_account(purple_buddy_get_name(buddy), purple_buddy_get_account(buddy));
+ if (im && purple_conversation_get_e2ee_state(PURPLE_CONVERSATION(im)) != NULL)
+ e2ee_enabled = TRUE;
+
account = purple_buddy_get_account(buddy);
- if (purple_account_is_connected(account) || account == gtkconv->active_conv->account)
+ /* TODO WEBKIT: (I'm not actually sure if this is webkit-related --Mark Doliner) */
+ if (purple_account_is_connected(account) /*|| account == purple_conversation_get_account(gtkconv->active_conv)*/)
{
/* Use the PurplePresence to get unique buddies. */
PurplePresence *presence = purple_buddy_get_presence(buddy);
@@ -3933,9 +4133,9 @@ generate_send_to_items(PidginWindow *win)
* since we did a g_list_prepend() earlier. */
for (iter = g_list_last(list); iter != NULL; iter = iter->prev) {
PurplePresence *pre = iter->data;
- PurpleBuddy *buddy = purple_presence_get_buddy(pre);
+ PurpleBuddy *buddy = purple_buddy_presence_get_buddy(PURPLE_BUDDY_PRESENCE(pre));
create_sendto_item(menu, sg, &group, buddy,
- purple_buddy_get_account(buddy), purple_buddy_get_name(buddy));
+ purple_buddy_get_account(buddy), purple_buddy_get_name(buddy), e2ee_enabled);
}
}
g_list_free(list);
@@ -3945,27 +4145,109 @@ generate_send_to_items(PidginWindow *win)
g_object_unref(sg);
- gtk_widget_show(win->menu.send_to);
+ gtk_widget_show(win->menu->send_to);
/* TODO: This should never be insensitive. Possibly hidden or not. */
if (!group)
- gtk_widget_set_sensitive(win->menu.send_to, FALSE);
+ gtk_widget_set_sensitive(win->menu->send_to, FALSE);
update_send_to_selection(win);
}
+PurpleImage *
+_pidgin_e2ee_stock_icon_get(const gchar *stock_name)
+{
+ gchar filename[100], *path;
+ PurpleImage *image;
+
+ /* core is quitting */
+ if (e2ee_stock == NULL)
+ return NULL;
+
+ if (g_hash_table_lookup_extended(e2ee_stock, stock_name, NULL, (gpointer*)&image))
+ return image;
+
+ g_snprintf(filename, sizeof(filename), "%s.png", stock_name);
+ path = g_build_filename(PURPLE_DATADIR, "pixmaps", "pidgin",
+ "e2ee", "16", filename, NULL);
+ image = purple_image_new_from_file(path, FALSE);
+ g_free(path);
+
+ g_hash_table_insert(e2ee_stock, g_strdup(stock_name), image);
+ return image;
+}
+
+static void
+generate_e2ee_controls(PidginConvWindow *win)
+{
+ PidginConversation *gtkconv;
+ PurpleConversation *conv;
+ PurpleE2eeState *state;
+ PurpleE2eeProvider *provider;
+ GtkWidget *menu;
+ GList *menu_actions, *it;
+ GtkWidget *e2ee_image;
+
+ gtkconv = pidgin_conv_window_get_active_gtkconv(win);
+ g_return_if_fail(gtkconv != NULL);
+
+ conv = gtkconv->active_conv;
+ g_return_if_fail(conv != NULL);
+
+ if (win->menu->e2ee != NULL) {
+ gtk_widget_destroy(win->menu->e2ee);
+ win->menu->e2ee = NULL;
+ }
+
+ provider = purple_e2ee_provider_get_main();
+ state = purple_conversation_get_e2ee_state(conv);
+ if (state == NULL || provider == NULL)
+ return;
+ if (purple_e2ee_state_get_provider(state) != provider)
+ return;
+
+ win->menu->e2ee = gtk_image_menu_item_new_with_label(
+ purple_e2ee_provider_get_name(provider));
+
+ menu = gtk_menu_new();
+ gtk_menu_shell_insert(GTK_MENU_SHELL(win->menu->menubar),
+ win->menu->e2ee, 3);
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(win->menu->e2ee), menu);
+
+ e2ee_image = e2ee_state_to_gtkimage(state);
+ if (e2ee_image) {
+ gtk_image_menu_item_set_image(
+ GTK_IMAGE_MENU_ITEM(win->menu->e2ee), e2ee_image);
+ }
+
+ gtk_widget_set_tooltip_text(win->menu->e2ee,
+ purple_e2ee_state_get_name(state));
+
+ menu_actions = purple_e2ee_provider_get_conv_menu_actions(provider, conv);
+ for (it = menu_actions; it; it = g_list_next(it)) {
+ PurpleMenuAction *action = it->data;
+
+ gtk_widget_show_all(
+ pidgin_append_menu_action(menu, action, conv));
+ }
+ g_list_free(menu_actions);
+
+ gtk_widget_show(win->menu->e2ee);
+ gtk_widget_show(menu);
+}
+
static const char *
-get_chat_buddy_status_icon(PurpleConvChat *chat, const char *name, PurpleConvChatBuddyFlags flags)
+get_chat_user_status_icon(PurpleChatConversation *chat, const char *name, PurpleChatUserFlags flags)
{
const char *image = NULL;
- if (flags & PURPLE_CBFLAGS_FOUNDER) {
+ if (flags & PURPLE_CHAT_USER_FOUNDER) {
image = PIDGIN_STOCK_STATUS_FOUNDER;
- } else if (flags & PURPLE_CBFLAGS_OP) {
+ } else if (flags & PURPLE_CHAT_USER_OP) {
image = PIDGIN_STOCK_STATUS_OPERATOR;
- } else if (flags & PURPLE_CBFLAGS_HALFOP) {
+ } else if (flags & PURPLE_CHAT_USER_HALFOP) {
image = PIDGIN_STOCK_STATUS_HALFOP;
- } else if (flags & PURPLE_CBFLAGS_VOICE) {
+ } else if (flags & PURPLE_CHAT_USER_VOICE) {
image = PIDGIN_STOCK_STATUS_VOICE;
- } else if ((!flags) && purple_conv_chat_is_user_ignored(chat, name)) {
+ } else if ((!flags) && purple_chat_conversation_is_ignored_user(chat, name)) {
image = PIDGIN_STOCK_STATUS_IGNORED;
} else {
return NULL;
@@ -3974,21 +4256,22 @@ get_chat_buddy_status_icon(PurpleConvChat *chat, const char *name, PurpleConvCha
}
static void
-deleting_chat_buddy_cb(PurpleConvChatBuddy *cb)
+deleting_chat_user_cb(PurpleChatUser *cb)
{
- if (cb->ui_data) {
- GtkTreeRowReference *ref = cb->ui_data;
+ GtkTreeRowReference *ref = purple_chat_user_get_ui_data(cb);
+
+ if (ref) {
gtk_tree_row_reference_free(ref);
- cb->ui_data = NULL;
+ purple_chat_user_set_ui_data(cb, NULL);
}
}
static void
-add_chat_buddy_common(PurpleConversation *conv, PurpleConvChatBuddy *cb, const char *old_name)
+add_chat_user_common(PurpleChatConversation *chat, PurpleChatUser *cb, const char *old_name)
{
PidginConversation *gtkconv;
+ PurpleConversation *conv;
PidginChatPane *gtkchat;
- PurpleConvChat *chat;
PurpleConnection *gc;
PurplePluginProtocolInfo *prpl_info;
GtkTreeModel *tm;
@@ -3998,46 +4281,50 @@ add_chat_buddy_common(PurpleConversation *conv, PurpleConvChatBuddy *cb, const c
GtkTreeIter iter;
gboolean is_me = FALSE;
gboolean is_buddy;
- gchar *tmp, *alias_key, *name, *alias;
- PurpleConvChatBuddyFlags flags;
+ const gchar *name, *alias;
+ gchar *tmp, *alias_key;
+ PurpleChatUserFlags flags;
GdkColor *color = NULL;
- alias = cb->alias;
- name = cb->name;
- flags = cb->flags;
+ alias = purple_chat_user_get_alias(cb);
+ name = purple_chat_user_get_name(cb);
+ flags = purple_chat_user_get_flags(cb);
- chat = PURPLE_CONV_CHAT(conv);
+ conv = PURPLE_CONVERSATION(chat);
gtkconv = PIDGIN_CONVERSATION(conv);
gtkchat = gtkconv->u.chat;
- gc = purple_conversation_get_gc(conv);
+ gc = purple_conversation_get_connection(conv);
- if (!gc || !(prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)))
+ if (!gc || !(prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc))))
return;
tm = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list));
ls = GTK_LIST_STORE(tm);
- stock = get_chat_buddy_status_icon(chat, name, flags);
+ stock = get_chat_user_status_icon(chat, name, flags);
- if (!strcmp(chat->nick, purple_normalize(conv->account, old_name != NULL ? old_name : name)))
+ if (!strcmp(purple_chat_conversation_get_nick(chat), purple_normalize(purple_conversation_get_account(conv), old_name != NULL ? old_name : name)))
is_me = TRUE;
- is_buddy = cb->buddy;
+ is_buddy = purple_chat_user_is_buddy(cb);
tmp = g_utf8_casefold(alias, -1);
alias_key = g_utf8_collate_key(tmp, -1);
g_free(tmp);
if (is_me) {
+#if 0
+ /* TODO WEBKIT: No tags in webkit stuff, yet. */
GtkTextTag *tag = gtk_text_tag_table_lookup(
- gtk_text_buffer_get_tag_table(GTK_IMHTML(gtkconv->imhtml)->text_buffer),
+ gtk_text_buffer_get_tag_table(GTK_IMHTML(gtkconv->webview)->text_buffer),
"send-name");
g_object_get(tag, "foreground-gdk", &color, NULL);
+#endif /* if 0 */
} else {
GtkTextTag *tag;
- if ((tag = get_buddy_tag(conv, name, 0, FALSE)))
+ if ((tag = get_buddy_tag(chat, name, 0, FALSE)))
g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_NORMAL, NULL);
- if ((tag = get_buddy_tag(conv, name, PURPLE_MESSAGE_NICK, FALSE)))
+ if ((tag = get_buddy_tag(chat, name, PURPLE_MESSAGE_NICK, FALSE)))
g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_NORMAL, NULL);
color = (GdkColor*)get_nick_color(gtkconv, name);
}
@@ -4060,48 +4347,58 @@ add_chat_buddy_common(PurpleConversation *conv, PurpleConvChatBuddy *cb, const c
CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
-1);
- if (cb->ui_data) {
- GtkTreeRowReference *ref = cb->ui_data;
+ if (purple_chat_user_get_ui_data(cb)) {
+ GtkTreeRowReference *ref = purple_chat_user_get_ui_data(cb);
gtk_tree_row_reference_free(ref);
}
newpath = gtk_tree_model_get_path(tm, &iter);
- cb->ui_data = gtk_tree_row_reference_new(tm, newpath);
+ purple_chat_user_set_ui_data(cb, gtk_tree_row_reference_new(tm, newpath));
gtk_tree_path_free(newpath);
+#if 0
if (is_me && color)
gdk_color_free(color);
+#endif
g_free(alias_key);
}
-/**
- * @param most_matched Used internally by this function.
- * @param entered The partial string that the user types before hitting the
+/*
+ * tab_complete_process_item:
+ * @most_matched: Used internally by this function.
+ * @entered: The partial string that the user types before hitting the
* tab key.
- * @param entered_bytes The length of entered.
- * @param partial This is a return variable. This will be set to a string
+ * @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"
- * @param nick_partial Used internally by this function. Shoudl be a
- * temporary buffer that is entered_bytes+1 bytes long.
- * @param matches This is a return variable. If the given name is a potential
+ * @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.
- * @param name The buddy name or alias or slash command name that we're
+ * @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_bytes, char **partial, char *nick_partial,
- GList **matches, char *name)
+tab_complete_process_item(int *most_matched, const char *entered, gsize entered_chars, char **partial,
+ GList **matches, const char *name)
{
- memcpy(nick_partial, name, entered_bytes);
- if (purple_utf8_strcasecmp(nick_partial, entered))
+ 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) {
@@ -4109,16 +4406,16 @@ tab_complete_process_item(int *most_matched, const char *entered, gsize entered_
* this will only get called once, since from now
* on *most_matched is >= 0
*/
- *most_matched = strlen(name);
+ *most_matched = name_len;
*partial = g_strdup(name);
}
else if (*most_matched) {
char *tmp = g_strdup(name);
while (purple_utf8_strcasecmp(tmp, *partial)) {
- (*partial)[*most_matched] = '\0';
- if (*most_matched < strlen(tmp))
- tmp[*most_matched] = '\0';
+ *(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)++;
@@ -4131,96 +4428,122 @@ tab_complete_process_item(int *most_matched, const char *entered, gsize entered_
}
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 (!strcmp(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;
- GtkTextIter cursor, word_start, start_buffer;
- int start;
- int most_matched = -1;
+ WebKitDOMNode *container;
+ glong caret, word_start, content_len;
+ int most_matched = -1, colon = 0;
+ char *ch, *ch2 = NULL;
char *entered, *partial = NULL;
- char *text;
- char *nick_partial;
+ char *content, *sub1, *sub2, *modified;
const char *prefix;
GList *matches = NULL;
gboolean command = FALSE;
- gsize entered_bytes = 0;
+ gsize entered_chars = 0;
gtkconv = PIDGIN_CONVERSATION(conv);
-
- gtk_text_buffer_get_start_iter(gtkconv->entry_buffer, &start_buffer);
- gtk_text_buffer_get_iter_at_mark(gtkconv->entry_buffer, &cursor,
- gtk_text_buffer_get_insert(gtkconv->entry_buffer));
-
- word_start = cursor;
+ pidgin_webview_get_caret(PIDGIN_WEBVIEW(gtkconv->entry), &container, &caret);
/* if there's nothing there just return */
- if (!gtk_text_iter_compare(&cursor, &start_buffer))
- return (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) ? TRUE : FALSE;
+ if (caret <= 0)
+ return PURPLE_IS_CHAT_CONVERSATION(conv);
- text = gtk_text_buffer_get_text(gtkconv->entry_buffer, &start_buffer,
- &cursor, FALSE);
+ content = webkit_dom_node_get_node_value(container);
+ content_len = g_utf8_strlen(content, -1);
- /* if we're at the end of ": " we need to move back 2 spaces */
- start = strlen(text) - 1;
-
- if (start >= 1 && !strncmp(&text[start-1], ": ", 2)) {
- gtk_text_iter_backward_chars(&word_start, 2);
+ /* 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);
}
- /* find the start of the word that we're tabbing.
- * Using gtk_text_iter_backward_word_start won't work, because a nick can contain
- * characters (e.g. '.', '/' etc.) that Pango may think are word separators. */
- while (gtk_text_iter_backward_char(&word_start)) {
- if (gtk_text_iter_get_char(&word_start) == ' ') {
- /* Reached the whitespace before the start of the word. Move forward once */
- gtk_text_iter_forward_char(&word_start);
+ 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 (gtk_text_iter_get_offset(&word_start) == 0 &&
- (strlen(text) >= strlen(prefix)) && !strncmp(text, prefix, strlen(prefix))) {
+ if (word_start == 0 &&
+ ((gsize)caret >= strlen(prefix)) && !strncmp(content, prefix, strlen(prefix))) {
command = TRUE;
- gtk_text_iter_forward_chars(&word_start, strlen(prefix));
+ word_start += strlen(prefix);
}
- g_free(text);
-
- entered = gtk_text_buffer_get_text(gtkconv->entry_buffer, &word_start,
- &cursor, FALSE);
- entered_bytes = strlen(entered);
+ entered = g_utf8_substring(content, word_start, caret);
+ entered_chars = g_utf8_strlen(entered, -1);
- if (!g_utf8_strlen(entered, -1)) {
+ if (!entered_chars) {
+ g_free(content);
g_free(entered);
- return (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) ? TRUE : FALSE;
+ return PURPLE_IS_CHAT_CONVERSATION(conv);
}
- nick_partial = g_malloc0(entered_bytes + 1);
-
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_bytes, &partial, nick_partial,
+ tab_complete_process_item(&most_matched, entered, entered_chars, &partial,
&matches, l->data);
}
g_list_free(list);
- } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
- PurpleConvChat *chat = PURPLE_CONV_CHAT(conv);
- GList *l = purple_conv_chat_get_users(chat);
+ } 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 */
- for (; l != NULL; l = l->next) {
- tab_complete_process_item(&most_matched, entered, entered_bytes, &partial, nick_partial,
- &matches, ((PurpleConvChatBuddy *)l->data)->name);
+ 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))
@@ -4235,7 +4558,7 @@ tab_complete(PurpleConversation *conv)
-1);
if (name && alias && strcmp(name, alias))
- tab_complete_process_item(&most_matched, entered, entered_bytes, &partial, nick_partial,
+ tab_complete_process_item(&most_matched, entered, entered_chars, &partial,
&matches, alias);
g_free(name);
g_free(alias);
@@ -4244,38 +4567,49 @@ tab_complete(PurpleConversation *conv)
} while (f != 0);
}
} else {
- g_free(nick_partial);
+ g_free(content);
g_free(entered);
return FALSE;
}
- g_free(nick_partial);
-
- /* we're only here if we're doing new style */
-
/* 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_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) ? TRUE : FALSE;
+ return PURPLE_IS_CHAT_CONVERSATION(conv);
}
- gtk_text_buffer_delete(gtkconv->entry_buffer, &word_start, &cursor);
+ 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. */
- gtk_text_buffer_get_start_iter(gtkconv->entry_buffer, &start_buffer);
- gtk_text_buffer_get_iter_at_mark(gtkconv->entry_buffer, &cursor,
- gtk_text_buffer_get_insert(gtkconv->entry_buffer));
- if (!gtk_text_iter_compare(&cursor, &start_buffer)) {
- char *tmp = g_strdup_printf("%s: ", (char *)matches->data);
- gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer, tmp, -1);
+ 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);
}
- else
- gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer,
- matches->data, -1);
g_free(matches->data);
g_list_free(matches);
@@ -4295,14 +4629,21 @@ tab_complete(PurpleConversation *conv)
matches = g_list_remove(matches, matches->data);
}
- purple_conversation_write(conv, NULL, addthis, PURPLE_MESSAGE_NO_LOG,
- time(NULL));
- gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer, partial, -1);
+ 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;
}
@@ -4316,9 +4657,9 @@ static void topic_callback(GtkWidget *w, PidginConversation *gtkconv)
char *new_topic;
const char *current_topic;
- gc = purple_conversation_get_gc(conv);
+ gc = purple_conversation_get_connection(conv);
- if(!gc || !(prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)))
+ if(!gc || !(prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc))))
return;
if(prpl_info->set_chat_topic == NULL)
@@ -4327,7 +4668,7 @@ static void topic_callback(GtkWidget *w, PidginConversation *gtkconv)
gtkconv = PIDGIN_CONVERSATION(conv);
gtkchat = gtkconv->u.chat;
new_topic = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtkchat->topic_text)));
- current_topic = purple_conv_chat_get_topic(PURPLE_CONV_CHAT(conv));
+ current_topic = purple_chat_conversation_get_topic(PURPLE_CHAT_CONVERSATION(conv));
if(current_topic && !g_utf8_collate(new_topic, current_topic)){
g_free(new_topic);
@@ -4339,7 +4680,7 @@ static void topic_callback(GtkWidget *w, PidginConversation *gtkconv)
else
gtk_entry_set_text(GTK_ENTRY(gtkchat->topic_text), "");
- prpl_info->set_chat_topic(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)),
+ prpl_info->set_chat_topic(gc, purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv)),
new_topic);
g_free(new_topic);
@@ -4348,7 +4689,7 @@ static void topic_callback(GtkWidget *w, PidginConversation *gtkconv)
static gint
sort_chat_users(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
- PurpleConvChatBuddyFlags f1 = 0, f2 = 0;
+ PurpleChatUserFlags f1 = 0, f2 = 0;
char *user1 = NULL, *user2 = NULL;
gboolean buddy1 = FALSE, buddy2 = FALSE;
gint ret = 0;
@@ -4365,10 +4706,10 @@ sort_chat_users(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer us
-1);
/* Only sort by membership levels */
- f1 &= PURPLE_CBFLAGS_VOICE | PURPLE_CBFLAGS_HALFOP | PURPLE_CBFLAGS_OP |
- PURPLE_CBFLAGS_FOUNDER;
- f2 &= PURPLE_CBFLAGS_VOICE | PURPLE_CBFLAGS_HALFOP | PURPLE_CBFLAGS_OP |
- PURPLE_CBFLAGS_FOUNDER;
+ f1 &= PURPLE_CHAT_USER_VOICE | PURPLE_CHAT_USER_HALFOP | PURPLE_CHAT_USER_OP |
+ PURPLE_CHAT_USER_FOUNDER;
+ f2 &= PURPLE_CHAT_USER_VOICE | PURPLE_CHAT_USER_HALFOP | PURPLE_CHAT_USER_OP |
+ PURPLE_CHAT_USER_FOUNDER;
if (user1 == NULL || user2 == NULL) {
if (!(user1 == NULL && user2 == NULL))
@@ -4389,17 +4730,17 @@ sort_chat_users(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer us
}
static void
-update_chat_alias(PurpleBuddy *buddy, PurpleConversation *conv, PurpleConnection *gc, PurplePluginProtocolInfo *prpl_info)
+update_chat_alias(PurpleBuddy *buddy, PurpleChatConversation *chat, PurpleConnection *gc, PurplePluginProtocolInfo *prpl_info)
{
- PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
- PurpleConvChat *chat = PURPLE_CONV_CHAT(conv);
+ PidginConversation *gtkconv = PIDGIN_CONVERSATION(PURPLE_CONVERSATION(chat));
+ PurpleAccount *account = purple_conversation_get_account(PURPLE_CONVERSATION(chat));
GtkTreeModel *model;
char *normalized_name;
GtkTreeIter iter;
int f;
g_return_if_fail(buddy != NULL);
- g_return_if_fail(conv != NULL);
+ g_return_if_fail(chat != NULL);
/* This is safe because this callback is only used in chats, not IMs. */
model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkconv->u.chat->list));
@@ -4407,23 +4748,23 @@ update_chat_alias(PurpleBuddy *buddy, PurpleConversation *conv, PurpleConnection
if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter))
return;
- normalized_name = g_strdup(purple_normalize(conv->account, buddy->name));
+ normalized_name = g_strdup(purple_normalize(account, purple_buddy_get_name(buddy)));
do {
char *name;
gtk_tree_model_get(model, &iter, CHAT_USERS_NAME_COLUMN, &name, -1);
- if (!strcmp(normalized_name, purple_normalize(conv->account, name))) {
+ if (!strcmp(normalized_name, purple_normalize(account, name))) {
const char *alias = name;
char *tmp;
char *alias_key = NULL;
PurpleBuddy *buddy2;
- if (strcmp(chat->nick, purple_normalize(conv->account, name))) {
+ if (strcmp(purple_chat_conversation_get_nick(chat), purple_normalize(account, name))) {
/* This user is not me, so look into updating the alias. */
- if ((buddy2 = purple_find_buddy(conv->account, name)) != NULL) {
+ if ((buddy2 = purple_blist_find_buddy(account, name)) != NULL) {
alias = purple_buddy_get_contact_alias(buddy2);
}
@@ -4450,38 +4791,39 @@ update_chat_alias(PurpleBuddy *buddy, PurpleConversation *conv, PurpleConnection
}
static void
-blist_node_aliased_cb(PurpleBlistNode *node, const char *old_alias, PurpleConversation *conv)
+blist_node_aliased_cb(PurpleBlistNode *node, const char *old_alias, PurpleChatConversation *chat)
{
PurpleConnection *gc;
PurplePluginProtocolInfo *prpl_info;
+ PurpleConversation *conv = PURPLE_CONVERSATION(chat);
g_return_if_fail(node != NULL);
g_return_if_fail(conv != NULL);
- gc = purple_conversation_get_gc(conv);
+ gc = purple_conversation_get_connection(conv);
g_return_if_fail(gc != NULL);
- g_return_if_fail(gc->prpl != NULL);
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ g_return_if_fail(purple_connection_get_prpl(gc) != NULL);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
if (prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)
return;
- if (PURPLE_BLIST_NODE_IS_CONTACT(node))
+ if (PURPLE_IS_CONTACT(node))
{
PurpleBlistNode *bnode;
for(bnode = node->child; bnode; bnode = bnode->next) {
- if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
+ if(!PURPLE_IS_BUDDY(bnode))
continue;
- update_chat_alias((PurpleBuddy *)bnode, conv, gc, prpl_info);
+ update_chat_alias((PurpleBuddy *)bnode, chat, gc, prpl_info);
}
}
- else if (PURPLE_BLIST_NODE_IS_BUDDY(node))
- update_chat_alias((PurpleBuddy *)node, conv, gc, prpl_info);
- else if (PURPLE_BLIST_NODE_IS_CHAT(node) &&
- purple_conversation_get_account(conv) == ((PurpleChat*)node)->account)
+ else if (PURPLE_IS_BUDDY(node))
+ update_chat_alias((PurpleBuddy *)node, chat, gc, prpl_info);
+ else if (PURPLE_IS_CHAT(node) &&
+ purple_conversation_get_account(conv) == purple_chat_get_account((PurpleChat*)node))
{
if (old_alias == NULL || g_utf8_collate(old_alias, purple_conversation_get_title(conv)) == 0)
pidgin_conv_update_fields(conv, PIDGIN_CONV_SET_TITLE);
@@ -4489,12 +4831,13 @@ blist_node_aliased_cb(PurpleBlistNode *node, const char *old_alias, PurpleConver
}
static void
-buddy_cb_common(PurpleBuddy *buddy, PurpleConversation *conv, gboolean is_buddy)
+buddy_cb_common(PurpleBuddy *buddy, PurpleChatConversation *chat, gboolean is_buddy)
{
GtkTreeModel *model;
char *normalized_name;
GtkTreeIter iter;
GtkTextTag *texttag;
+ PurpleConversation *conv = PURPLE_CONVERSATION(chat);
int f;
g_return_if_fail(buddy != NULL);
@@ -4510,14 +4853,14 @@ buddy_cb_common(PurpleBuddy *buddy, PurpleConversation *conv, gboolean is_buddy)
if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter))
return;
- normalized_name = g_strdup(purple_normalize(conv->account, buddy->name));
+ normalized_name = g_strdup(purple_normalize(purple_conversation_get_account(conv), purple_buddy_get_name(buddy)));
do {
char *name;
gtk_tree_model_get(model, &iter, CHAT_USERS_NAME_COLUMN, &name, -1);
- if (!strcmp(normalized_name, purple_normalize(conv->account, name))) {
+ if (!strcmp(normalized_name, purple_normalize(purple_conversation_get_account(conv), name))) {
gtk_list_store_set(GTK_LIST_STORE(model), &iter,
CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, -1);
g_free(name);
@@ -4531,55 +4874,52 @@ buddy_cb_common(PurpleBuddy *buddy, PurpleConversation *conv, gboolean is_buddy)
g_free(normalized_name);
- blist_node_aliased_cb((PurpleBlistNode *)buddy, NULL, conv);
+ blist_node_aliased_cb((PurpleBlistNode *)buddy, NULL, chat);
- texttag = get_buddy_tag(conv, purple_buddy_get_name(buddy), 0, FALSE); /* XXX: do we want the normalized name? */
+ texttag = get_buddy_tag(chat, purple_buddy_get_name(buddy), 0, FALSE); /* XXX: do we want the normalized name? */
if (texttag) {
g_object_set(texttag, "weight", is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, NULL);
}
}
static void
-buddy_added_cb(PurpleBlistNode *node, PurpleConversation *conv)
+buddy_added_cb(PurpleBlistNode *node, PurpleChatConversation *chat)
{
- if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if (!PURPLE_IS_BUDDY(node))
return;
- buddy_cb_common(PURPLE_BUDDY(node), conv, TRUE);
+ buddy_cb_common(PURPLE_BUDDY(node), chat, TRUE);
}
static void
-buddy_removed_cb(PurpleBlistNode *node, PurpleConversation *conv)
+buddy_removed_cb(PurpleBlistNode *node, PurpleChatConversation *chat)
{
- if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if (!PURPLE_IS_BUDDY(node))
return;
/* If there's another buddy for the same "dude" on the list, do nothing. */
- if (purple_find_buddy(purple_buddy_get_account(PURPLE_BUDDY(node)),
+ if (purple_blist_find_buddy(purple_buddy_get_account(PURPLE_BUDDY(node)),
purple_buddy_get_name(PURPLE_BUDDY(node))) != NULL)
return;
- buddy_cb_common(PURPLE_BUDDY(node), conv, FALSE);
-}
-
-static void send_menu_cb(GtkWidget *widget, PidginConversation *gtkconv)
-{
- g_signal_emit_by_name(gtkconv->entry, "message_send");
+ buddy_cb_common(PURPLE_BUDDY(node), chat, FALSE);
}
static void
-entry_popup_menu_cb(GtkIMHtml *imhtml, GtkMenu *menu, gpointer data)
+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_item_from_stock(NULL, _("_Send"), NULL,
- G_CALLBACK(send_menu_cb), gtkconv,
- 0, 0, NULL);
- if (gtk_text_buffer_get_char_count(imhtml->text_buffer) == 0)
+ G_CALLBACK(send_cb), gtkconv,
+ 0, 0, NULL);
+ 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);
@@ -4588,20 +4928,32 @@ entry_popup_menu_cb(GtkIMHtml *imhtml, GtkMenu *menu, gpointer data)
gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, 1);
}
-static gboolean resize_imhtml_cb(PidginConversation *gtkconv)
+static gboolean
+resize_webview_cb(PidginConversation *gtkconv)
{
+#if 0
+ /* TODO WebKit: entry sizing */
GtkTextBuffer *buffer;
GtkTextIter iter;
int lines;
GdkRectangle oneline;
int height, diff;
int pad_top, pad_inside, pad_bottom;
- int total_height = (gtkconv->imhtml->allocation.height + gtkconv->entry->allocation.height);
- int max_height = total_height / 2;
+ int total_height;
+ int max_height;
int min_lines = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/minimum_entry_lines");
int min_height;
gboolean interior_focus;
int focus_width;
+ GtkAllocation webview_allocation;
+ GtkAllocation entry_allocation;
+ GtkAllocation lower_hbox_allocation;
+
+ gtk_widget_get_allocation(gtkconv->webview, &webview_allocation);
+ gtk_widget_get_allocation(gtkconv->entry, &entry_allocation);
+ gtk_widget_get_allocation(gtkconv->lower_hbox, &lower_hbox_allocation);
+ total_height = webview_allocation.height + entry_allocation.height;
+ max_height = total_height / 2;
pad_top = gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(gtkconv->entry));
pad_bottom = gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(gtkconv->entry));
@@ -4635,12 +4987,17 @@ static gboolean resize_imhtml_cb(PidginConversation *gtkconv)
if (!interior_focus)
height += 2 * focus_width;
- diff = height - gtkconv->entry->allocation.height;
+ diff = height - entry_allocation.height;
if (ABS(diff) < oneline.height / 2)
return FALSE;
+ purple_debug_info("pidgin", "resizing to %d, %d lines, diff %d\n",
+ diff + lower_hbox_allocation.height, min_lines, diff);
+
gtk_widget_set_size_request(gtkconv->lower_hbox, -1,
- diff + gtkconv->lower_hbox->allocation.height);
+ diff + lower_hbox_allocation.height);
+#endif
+ gtk_widget_set_size_request(gtkconv->lower_hbox, -1, -1);
return FALSE;
}
@@ -4651,14 +5008,14 @@ minimum_entry_lines_pref_cb(const char *name,
gconstpointer value,
gpointer data)
{
- GList *l = purple_get_conversations();
+ GList *l = purple_conversations_get_all();
PurpleConversation *conv;
while (l != NULL)
{
conv = (PurpleConversation *)l->data;
if (PIDGIN_IS_PIDGIN_CONVERSATION(conv))
- resize_imhtml_cb(PIDGIN_CONVERSATION(conv));
+ resize_webview_cb(PIDGIN_CONVERSATION(conv));
l = l->next;
}
@@ -4668,14 +5025,14 @@ static void
setup_chat_topic(PidginConversation *gtkconv, GtkWidget *vbox)
{
PurpleConversation *conv = gtkconv->active_conv;
- PurpleConnection *gc = purple_conversation_get_gc(conv);
- PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ PurpleConnection *gc = purple_conversation_get_connection(conv);
+ PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
if (prpl_info->options & OPT_PROTO_CHAT_TOPIC)
{
GtkWidget *hbox, *label;
PidginChatPane *gtkchat = gtkconv->u.chat;
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
label = gtk_label_new(_("Topic:"));
@@ -4687,7 +5044,7 @@ setup_chat_topic(PidginConversation *gtkconv, GtkWidget *vbox)
if(prpl_info->set_chat_topic == NULL) {
gtk_editable_set_editable(GTK_EDITABLE(gtkchat->topic_text), FALSE);
} else {
- g_signal_connect(GTK_OBJECT(gtkchat->topic_text), "activate",
+ g_signal_connect(G_OBJECT(gtkchat->topic_text), "activate",
G_CALLBACK(topic_callback), gtkconv);
}
@@ -4710,7 +5067,7 @@ pidgin_conv_userlist_create_tooltip(GtkWidget *tipwindow, GtkTreePath *path,
PurpleAccount *account = purple_conversation_get_account(conv);
char *who = NULL;
- if (account->gc == NULL)
+ if (purple_account_get_connection(account) == NULL)
return FALSE;
if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path))
@@ -4718,8 +5075,8 @@ pidgin_conv_userlist_create_tooltip(GtkWidget *tipwindow, GtkTreePath *path,
gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &who, -1);
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(account->gc->prpl);
- node = (PurpleBlistNode*)(purple_find_buddy(conv->account, who));
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(purple_account_get_connection(account)));
+ node = (PurpleBlistNode*)(purple_blist_find_buddy(purple_conversation_get_account(conv), who));
if (node && prpl_info && (prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME))
pidgin_blist_draw_tooltip(node, gtkconv->infopane);
@@ -4740,7 +5097,7 @@ setup_chat_userlist(PidginConversation *gtkconv, GtkWidget *hpaned)
PurpleConversation *conv = gtkconv->active_conv;
/* Build the right pane. */
- lbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ lbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
gtk_paned_pack2(GTK_PANED(hpaned), lbox, FALSE, TRUE);
gtk_widget_show(lbox);
@@ -4820,7 +5177,7 @@ setup_chat_userlist(PidginConversation *gtkconv, GtkWidget *hpaned)
gtkchat->list = list;
- gtk_box_pack_start(GTK_BOX(lbox),
+ gtk_box_pack_start(GTK_BOX(lbox),
pidgin_make_scrollable(list, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, -1),
TRUE, TRUE, 0);
}
@@ -4833,16 +5190,16 @@ pidgin_conv_create_tooltip(GtkWidget *tipwindow, gpointer userdata, int *w, int
PidginConversation *gtkconv = userdata;
conv = gtkconv->active_conv;
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
- node = (PurpleBlistNode*)(purple_blist_find_chat(conv->account, conv->name));
+ 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->imhtml), "transient_chat");
+ node = g_object_get_data(G_OBJECT(gtkconv->webview), "transient_chat");
} else {
- node = (PurpleBlistNode*)(purple_find_buddy(conv->account, conv->name));
+ node = (PurpleBlistNode*)(purple_blist_find_buddy(purple_conversation_get_account(conv), purple_conversation_get_name(conv)));
#if 0
/* Using the transient blist nodes to show the tooltip doesn't quite work yet. */
if (!node)
- node = g_object_get_data(G_OBJECT(gtkconv->imhtml), "transient_buddy");
+ node = g_object_get_data(G_OBJECT(gtkconv->webview), "transient_buddy");
#endif
}
@@ -4855,10 +5212,15 @@ pidgin_conv_create_tooltip(GtkWidget *tipwindow, gpointer userdata, int *w, int
static gboolean
pidgin_conv_end_quickfind(PidginConversation *gtkconv)
{
- gtk_widget_modify_base(gtkconv->quickfind.entry, GTK_STATE_NORMAL, NULL);
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context = gtk_widget_get_style_context(gtkconv->quickfind_entry);
+ gtk_style_context_remove_class(context, "not-found");
+#else
+ gtk_widget_modify_base(gtkconv->quickfind_entry, GTK_STATE_NORMAL, NULL);
+#endif
- gtk_imhtml_search_clear(GTK_IMHTML(gtkconv->imhtml));
- gtk_widget_hide_all(gtkconv->quickfind.container);
+ 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;
@@ -4868,19 +5230,29 @@ static gboolean
quickfind_process_input(GtkWidget *entry, GdkEventKey *event, PidginConversation *gtkconv)
{
switch (event->keyval) {
- case GDK_Return:
- case GDK_KP_Enter:
- if (gtk_imhtml_search_find(GTK_IMHTML(gtkconv->imhtml), gtk_entry_get_text(GTK_ENTRY(entry)))) {
- gtk_widget_modify_base(gtkconv->quickfind.entry, GTK_STATE_NORMAL, NULL);
+ 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)) {
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context = gtk_widget_get_style_context(gtkconv->quickfind_entry);
+ gtk_style_context_remove_class(context, "not-found");
+#else
+ gtk_widget_modify_base(gtkconv->quickfind_entry, GTK_STATE_NORMAL, NULL);
+#endif
} else {
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context = gtk_widget_get_style_context(gtkconv->quickfind_entry);
+ gtk_style_context_add_class(context, "not-found");
+#else
GdkColor col;
col.red = 0xffff;
col.green = 0xafff;
col.blue = 0xafff;
- gtk_widget_modify_base(gtkconv->quickfind.entry, GTK_STATE_NORMAL, &col);
+ gtk_widget_modify_base(gtkconv->quickfind_entry, GTK_STATE_NORMAL, &col);
+#endif
}
break;
- case GDK_Escape:
+ case GDK_KEY_Escape:
pidgin_conv_end_quickfind(gtkconv);
break;
default:
@@ -4892,27 +5264,45 @@ quickfind_process_input(GtkWidget *entry, GdkEventKey *event, PidginConversation
static void
pidgin_conv_setup_quickfind(PidginConversation *gtkconv, GtkWidget *container)
{
- GtkWidget *widget = gtk_hbox_new(FALSE, 0);
+ GtkWidget *widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
GtkWidget *label, *entry, *close;
+#if GTK_CHECK_VERSION(3,0,0)
+ 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;"
+ "}";
+#endif
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_tooltips_set_tip(gtkconv->tooltips, close,
- _("Close Find bar"), NULL);
+ 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);
+#if GTK_CHECK_VERSION(3,0,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);
+#endif
- gtkconv->quickfind.entry = entry;
- gtkconv->quickfind.container = widget;
+ gtkconv->quickfind_entry = entry;
+ gtkconv->quickfind_container = widget;
/* Hook to signals and stuff */
- g_signal_connect(G_OBJECT(entry), "key_press_event",
+ 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);
@@ -4920,26 +5310,260 @@ pidgin_conv_setup_quickfind(PidginConversation *gtkconv, GtkWidget *container)
/* }}} */
+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, *imhtml_sw, *event_box;
+ GtkWidget *vbox, *frame, *webview_sw, *event_box;
GtkCellRenderer *rend;
GtkTreePath *path;
PurpleConversation *conv = gtkconv->active_conv;
PurpleBuddy *buddy;
- gboolean chat = (conv->type == PURPLE_CONV_TYPE_CHAT);
+ gboolean chat = PURPLE_IS_CHAT_CONVERSATION(conv);
int buddyicon_size = 0;
/* Setup the top part of the pane */
- vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
gtk_widget_show(vbox);
/* Setup the info pane */
event_box = gtk_event_box_new();
gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box), FALSE);
gtk_widget_show(event_box);
- gtkconv->infopane_hbox = gtk_hbox_new(FALSE, 0);
+ gtkconv->infopane_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_pack_start(GTK_BOX(vbox), event_box, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(event_box), gtkconv->infopane_hbox);
gtk_widget_show(gtkconv->infopane_hbox);
@@ -4969,15 +5593,15 @@ setup_common_pane(PidginConversation *gtkconv)
for both the buddy list and the chat window, but PidginConversation
is pretty much stuck until 3.0. */
GtkWidget *sizing_vbox;
- sizing_vbox = gtk_vbox_new(FALSE, 0);
+ sizing_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_size_request(sizing_vbox, -1, BUDDYICON_SIZE_MIN);
gtk_box_pack_start(GTK_BOX(gtkconv->infopane_hbox), sizing_vbox, FALSE, FALSE, 0);
gtk_widget_show(sizing_vbox);
}
else {
- gtkconv->u.im->icon_container = gtk_vbox_new(FALSE, 0);
+ gtkconv->u.im->icon_container = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
- if ((buddy = purple_find_buddy(purple_conversation_get_account(conv),
+ if ((buddy = purple_blist_find_buddy(purple_conversation_get_account(conv),
purple_conversation_get_name(conv))) != NULL) {
PurpleContact *contact = purple_buddy_get_contact(buddy);
if (contact) {
@@ -5019,17 +5643,21 @@ setup_common_pane(PidginConversation *gtkconv)
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 gtkimhtml widget */
- frame = pidgin_create_imhtml(FALSE, &gtkconv->imhtml, NULL, &imhtml_sw);
- gtk_widget_set_size_request(gtkconv->imhtml, -1, 0);
+ /* 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);
+
if (chat) {
GtkWidget *hpaned;
/* Add the topic */
setup_chat_topic(gtkconv, vbox);
- /* Add the gtkimhtml frame */
- hpaned = gtk_hpaned_new();
+ /* Add the gtkwebview frame */
+ 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);
@@ -5039,67 +5667,59 @@ setup_common_pane(PidginConversation *gtkconv)
} else {
gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
}
- gtk_widget_show(frame);
-
- gtk_widget_set_name(gtkconv->imhtml, "pidgin_conv_imhtml");
- gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),TRUE);
- g_object_set_data(G_OBJECT(gtkconv->imhtml), "gtkconv", gtkconv);
+ gtk_widget_show_all(frame);
- g_object_set(G_OBJECT(imhtml_sw), "vscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
+ 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->imhtml), "button_press_event",
+ g_signal_connect_after(G_OBJECT(gtkconv->webview), "button_press_event",
G_CALLBACK(entry_stop_rclick_cb), NULL);
- g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_press_event",
+ g_signal_connect(G_OBJECT(gtkconv->webview), "key_press_event",
G_CALLBACK(refocus_entry_cb), gtkconv);
- g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_release_event",
+ g_signal_connect(G_OBJECT(gtkconv->webview), "key_release_event",
G_CALLBACK(refocus_entry_cb), gtkconv);
pidgin_conv_setup_quickfind(gtkconv, vbox);
- gtkconv->lower_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ 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 toolbar, entry widget and all signals */
- frame = pidgin_create_imhtml(TRUE, &gtkconv->entry, &gtkconv->toolbar, NULL);
+ /* 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");
- gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->entry),
- purple_account_get_protocol_name(conv->account));
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_signal_connect(G_OBJECT(gtkconv->entry), "key-press-event",
G_CALLBACK(entry_key_press_cb), gtkconv);
- g_signal_connect_after(G_OBJECT(gtkconv->entry), "message_send",
- G_CALLBACK(send_cb), gtkconv);
- g_signal_connect_after(G_OBJECT(gtkconv->entry), "button_press_event",
+#if 0
+ /* TODO WebKit: Why? */
+ g_signal_connect_after(G_OBJECT(gtkconv->entry), "button-press-event",
G_CALLBACK(entry_stop_rclick_cb), NULL);
-
- gtkconv->entry_buffer =
- gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry));
- g_object_set_data(G_OBJECT(gtkconv->entry_buffer), "user_data", gtkconv);
+#endif
if (!chat) {
/* For sending typing notifications for IMs */
- g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "insert_text",
- G_CALLBACK(insert_text_cb), gtkconv);
- g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "delete_range",
- G_CALLBACK(delete_text_cb), gtkconv);
gtkconv->u.im->typing_timer = 0;
gtkconv->u.im->animate = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/animate_buddy_icons");
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_imhtml_cb), gtkconv);
+ G_CALLBACK(resize_webview_cb), gtkconv);
g_signal_connect_swapped(G_OBJECT(gtkconv->entry), "size-allocate",
- G_CALLBACK(resize_imhtml_cb), gtkconv);
+ G_CALLBACK(resize_webview_cb), gtkconv);
+#endif
default_formatize(gtkconv);
- g_signal_connect_after(G_OBJECT(gtkconv->entry), "format_function_clear",
+ g_signal_connect_after(G_OBJECT(gtkconv->entry), "format-cleared",
G_CALLBACK(clear_formatting_cb), gtkconv);
return vbox;
}
@@ -5110,25 +5730,27 @@ conv_dnd_recv(GtkWidget *widget, GdkDragContext *dc, guint x, guint y,
PidginConversation *gtkconv)
{
PurpleConversation *conv = gtkconv->active_conv;
- PidginWindow *win = gtkconv->win;
- PurpleConversation *c;
+ PidginConvWindow *win = gtkconv->win;
+ PurpleIMConversation *im;
PurpleAccount *convaccount = purple_conversation_get_account(conv);
PurpleConnection *gc = purple_account_get_connection(convaccount);
- PurplePluginProtocolInfo *prpl_info = gc ? PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl) : NULL;
+ PurplePluginProtocolInfo *prpl_info = gc ? PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)) : NULL;
+ const guchar *data = gtk_selection_data_get_data(sd);
- if (sd->target == gdk_atom_intern("PURPLE_BLIST_NODE", FALSE))
+ 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 = *(PurpleBlistNode **)sd->data;
+ n = *data_val;
- if (PURPLE_BLIST_NODE_IS_CONTACT(n))
+ if (PURPLE_IS_CONTACT(n))
b = purple_contact_get_priority_buddy((PurpleContact*)n);
- else if (PURPLE_BLIST_NODE_IS_BUDDY(n))
+ else if (PURPLE_IS_BUDDY(n))
b = (PurpleBuddy*)n;
else
return;
@@ -5139,29 +5761,29 @@ conv_dnd_recv(GtkWidget *widget, GdkDragContext *dc, guint x, guint y,
* If a buddy is dragged to a chat window of the same protocol,
* invite him to the chat.
*/
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT &&
+ if (PURPLE_IS_CHAT_CONVERSATION(conv) &&
prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, chat_invite) &&
strcmp(purple_account_get_protocol_id(convaccount),
purple_account_get_protocol_id(buddyaccount)) == 0) {
- purple_conv_chat_invite_user(PURPLE_CONV_CHAT(conv), buddyname, NULL, TRUE);
+ 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.
*/
- c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddyname, buddyaccount);
- if (c != NULL) {
- PidginWindow *oldwin;
- gtkconv = PIDGIN_CONVERSATION(c);
+ 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 {
- c = purple_conversation_new(PURPLE_CONV_TYPE_IM, buddyaccount, buddyname);
- gtkconv = PIDGIN_CONVERSATION(c);
+ 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);
@@ -5172,35 +5794,36 @@ conv_dnd_recv(GtkWidget *widget, GdkDragContext *dc, guint x, guint y,
pidgin_conv_window_switch_gtkconv(win, gtkconv);
}
- gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t);
+ gtk_drag_finish(dc, TRUE,
+ gdk_drag_context_get_actions(dc) == GDK_ACTION_MOVE, t);
}
- else if (sd->target == gdk_atom_intern("application/x-im-contact", FALSE))
+ else if (info == PIDGIN_DRAG_IM_CONTACT)
{
char *protocol = NULL;
char *username = NULL;
PurpleAccount *account;
PidginConversation *gtkconv;
- if (pidgin_parse_x_im_contact((const char *)sd->data, FALSE, &account,
+ if (pidgin_parse_x_im_contact((const char *) data, FALSE, &account,
&protocol, &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);
+ "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_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT &&
+ if (PURPLE_IS_CHAT_CONVERSATION(conv) &&
prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, chat_invite) &&
strcmp(purple_account_get_protocol_id(convaccount), protocol) == 0) {
- purple_conv_chat_invite_user(PURPLE_CONV_CHAT(conv), username, NULL, TRUE);
+ purple_chat_conversation_invite_user(PURPLE_CHAT_CONVERSATION(conv), username, NULL, TRUE);
} else {
- c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, username);
- gtkconv = PIDGIN_CONVERSATION(c);
+ 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);
@@ -5212,29 +5835,24 @@ conv_dnd_recv(GtkWidget *widget, GdkDragContext *dc, guint x, guint y,
g_free(username);
g_free(protocol);
- gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t);
+ gtk_drag_finish(dc, TRUE,
+ gdk_drag_context_get_actions(dc) == GDK_ACTION_MOVE, t);
}
- else if (sd->target == gdk_atom_intern("text/uri-list", FALSE)) {
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
+ 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, (dc->action == GDK_ACTION_MOVE), t);
+ gtk_drag_finish(dc, TRUE,
+ gdk_drag_context_get_actions(dc) == GDK_ACTION_MOVE, t);
}
else
gtk_drag_finish(dc, FALSE, FALSE, t);
}
-static const GtkTargetEntry te[] =
-{
- GTK_IMHTML_DND_TARGETS,
- {"PURPLE_BLIST_NODE", GTK_TARGET_SAME_APP, GTK_IMHTML_DRAG_NUM},
- {"application/x-im-contact", 0, GTK_IMHTML_DRAG_NUM + 1}
-};
-
static PidginConversation *
pidgin_conv_find_gtkconv(PurpleConversation * conv)
{
- PurpleBuddy *bud = purple_find_buddy(conv->account, conv->name);
+ PurpleBuddy *bud = purple_blist_find_buddy(purple_conversation_get_account(conv), purple_conversation_get_name(conv));
PurpleContact *c;
PurpleBlistNode *cn, *bn;
@@ -5247,10 +5865,10 @@ pidgin_conv_find_gtkconv(PurpleConversation * conv)
cn = PURPLE_BLIST_NODE(c);
for (bn = purple_blist_node_get_first_child(cn); bn; bn = purple_blist_node_get_sibling_next(bn)) {
PurpleBuddy *b = PURPLE_BUDDY(bn);
- PurpleConversation *conv;
- if ((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, b->name, b->account))) {
- if (conv->ui_data)
- return conv->ui_data;
+ PurpleIMConversation *im;
+ if ((im = purple_conversations_find_im_with_account(purple_buddy_get_name(b), purple_buddy_get_account(b)))) {
+ if (PIDGIN_CONVERSATION(PURPLE_CONVERSATION(im)))
+ return PIDGIN_CONVERSATION(PURPLE_CONVERSATION(im));
}
}
@@ -5263,15 +5881,15 @@ buddy_update_cb(PurpleBlistNode *bnode, gpointer null)
GList *list;
g_return_if_fail(bnode);
- if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
+ if (!PURPLE_IS_BUDDY(bnode))
return;
for (list = pidgin_conv_windows_get_list(); list; list = list->next)
{
- PidginWindow *win = list->data;
+ PidginConvWindow *win = list->data;
PurpleConversation *conv = pidgin_conv_window_get_active_conversation(win);
- if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_IM)
+ if (!PURPLE_IS_IM_CONVERSATION(conv))
continue;
pidgin_conv_update_fields(conv, PIDGIN_CONV_MENU);
@@ -5292,6 +5910,8 @@ ignore_middle_click(GtkWidget *widget, GdkEventButton *e, gpointer null)
static void set_typing_font(GtkWidget *widget, GtkStyle *style, PidginConversation *gtkconv)
{
+/* TODO WEBKIT */
+#if 0
static PangoFontDescription *font_desc = NULL;
static GdkColor *color = NULL;
static gboolean enable = TRUE;
@@ -5322,6 +5942,7 @@ static void set_typing_font(GtkWidget *widget, GtkStyle *style, PidginConversati
}
g_signal_handlers_disconnect_by_func(G_OBJECT(widget), set_typing_font, gtkconv);
+#endif /* if 0 */
}
/**************************************************************************
@@ -5331,14 +5952,15 @@ static void
private_gtkconv_new(PurpleConversation *conv, gboolean hidden)
{
PidginConversation *gtkconv;
- PurpleConversationType conv_type = purple_conversation_get_type(conv);
+ const char *theme_name;
+ PurpleTheme *theme = NULL;
GtkWidget *pane = NULL;
GtkWidget *tab_cont;
PurpleBlistNode *convnode;
- PurpleValue *value;
+ GtkTargetList *targets;
- if (conv_type == PURPLE_CONV_TYPE_IM && (gtkconv = pidgin_conv_find_gtkconv(conv))) {
- conv->ui_data = gtkconv;
+ if (PURPLE_IS_IM_CONVERSATION(conv) && (gtkconv = pidgin_conv_find_gtkconv(conv))) {
+ purple_conversation_set_ui_data(conv, gtkconv);
if (!g_list_find(gtkconv->convs, conv))
gtkconv->convs = g_list_prepend(gtkconv->convs, conv);
pidgin_conv_switch_active_conversation(conv);
@@ -5346,69 +5968,85 @@ private_gtkconv_new(PurpleConversation *conv, gboolean hidden)
}
gtkconv = g_new0(PidginConversation, 1);
- conv->ui_data = gtkconv;
+ purple_conversation_set_ui_data(conv, gtkconv);
gtkconv->active_conv = conv;
gtkconv->convs = g_list_prepend(gtkconv->convs, conv);
gtkconv->send_history = g_list_append(NULL, NULL);
/* Setup some initial variables. */
- gtkconv->tooltips = gtk_tooltips_new();
gtkconv->unseen_state = PIDGIN_UNSEEN_NONE;
gtkconv->unseen_count = 0;
-
- if (conv_type == PURPLE_CONV_TYPE_IM) {
+ 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)) {
gtkconv->u.im = g_malloc0(sizeof(PidginImPane));
- } else if (conv_type == PURPLE_CONV_TYPE_CHAT) {
+ } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
gtkconv->u.chat = g_malloc0(sizeof(PidginChatPane));
}
pane = setup_common_pane(gtkconv);
- gtk_imhtml_set_format_functions(GTK_IMHTML(gtkconv->imhtml),
- gtk_imhtml_get_format_functions(GTK_IMHTML(gtkconv->imhtml)) | GTK_IMHTML_IMAGE);
-
if (pane == NULL) {
- if (conv_type == PURPLE_CONV_TYPE_CHAT)
+ if (PURPLE_IS_CHAT_CONVERSATION(conv))
g_free(gtkconv->u.chat);
- else if (conv_type == PURPLE_CONV_TYPE_IM)
+ else if (PURPLE_IS_IM_CONVERSATION(conv))
g_free(gtkconv->u.im);
g_free(gtkconv);
- conv->ui_data = NULL;
+ purple_conversation_set_ui_data(conv, NULL);
return;
}
/* Setup drag-and-drop */
- gtk_drag_dest_set(pane,
- GTK_DEST_DEFAULT_MOTION |
- GTK_DEST_DEFAULT_DROP,
- te, sizeof(te) / sizeof(GtkTargetEntry),
- GDK_ACTION_COPY);
- gtk_drag_dest_set(pane,
- GTK_DEST_DEFAULT_MOTION |
- GTK_DEST_DEFAULT_DROP,
- te, sizeof(te) / sizeof(GtkTargetEntry),
- GDK_ACTION_COPY);
- gtk_drag_dest_set(gtkconv->imhtml, 0,
- te, sizeof(te) / sizeof(GtkTargetEntry),
- GDK_ACTION_COPY);
-
- gtk_drag_dest_set(gtkconv->entry, 0,
- te, sizeof(te) / sizeof(GtkTargetEntry),
- GDK_ACTION_COPY);
+ 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_signal_connect(G_OBJECT(pane), "drag-data-received",
G_CALLBACK(conv_dnd_recv), gtkconv);
- g_signal_connect(G_OBJECT(gtkconv->imhtml), "drag_data_received",
+#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_signal_connect(G_OBJECT(gtkconv->entry), "drag-data-received",
G_CALLBACK(conv_dnd_recv), gtkconv);
+#endif
- g_signal_connect(gtkconv->imhtml, "style-set", G_CALLBACK(set_typing_font), gtkconv);
+ g_signal_connect(gtkconv->webview, "style-set", G_CALLBACK(set_typing_font), gtkconv);
/* Setup the container for the tab. */
- gtkconv->tab_cont = tab_cont = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ 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);
@@ -5418,27 +6056,25 @@ private_gtkconv_new(PurpleConversation *conv, gboolean hidden)
if (convnode == NULL || !purple_blist_node_get_bool(convnode, "gtk-mute-sound"))
gtkconv->make_sound = TRUE;
- if (convnode != NULL &&
- (value = g_hash_table_lookup(convnode->settings, "enable-logging")) &&
- purple_value_get_type(value) == PURPLE_TYPE_BOOLEAN)
- {
- purple_conversation_set_logging(conv, purple_value_get_boolean(value));
+ if (convnode != NULL && purple_blist_node_has_setting(convnode, "enable-logging")) {
+ gboolean logging = purple_blist_node_get_bool(convnode, "enable-logging");
+ purple_conversation_set_logging(conv, logging);
}
if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/show_formatting_toolbar"))
- gtk_widget_show(gtkconv->toolbar);
+ pidgin_webview_show_toolbar(PIDGIN_WEBVIEW(gtkconv->entry));
else
- gtk_widget_hide(gtkconv->toolbar);
+ 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);
if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons"))
gtk_widget_show(gtkconv->infopane_hbox);
else
gtk_widget_hide(gtkconv->infopane_hbox);
- gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),
- purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/show_timestamps"));
- gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml),
- purple_account_get_protocol_name(conv->account));
g_signal_connect_swapped(G_OBJECT(pane), "focus",
G_CALLBACK(gtk_widget_grab_focus),
@@ -5449,13 +6085,14 @@ private_gtkconv_new(PurpleConversation *conv, gboolean hidden)
else
pidgin_conv_placement_place(gtkconv);
- if (nick_colors == NULL) {
- nbr_nick_colors = NUM_NICK_COLORS;
- nick_colors = generate_nick_colors(&nbr_nick_colors, gtk_widget_get_style(gtkconv->imhtml)->base[GTK_STATE_NORMAL]);
+ if (generated_nick_colors == NULL) {
+ generated_nick_colors = generate_nick_colors(NICK_COLOR_GENERATE_COUNT, gtk_widget_get_style(gtkconv->webview)->base[GTK_STATE_NORMAL]);
}
- if (conv->features & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY)
- pidgin_themes_smiley_themeize_custom(gtkconv->entry);
+ if(NULL == (gtkconv->nick_colors = pidgin_conversation_theme_get_nick_colors(gtkconv->theme)))
+ {
+ gtkconv->nick_colors = g_array_ref(generated_nick_colors);
+ }
}
static void
@@ -5500,16 +6137,16 @@ received_im_msg_cb(PurpleAccount *account, char *sender, char *message,
if (hide) {
ui_ops->create_conversation = pidgin_conv_new_hidden;
- purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender);
+ purple_im_conversation_new(account, sender);
ui_ops->create_conversation = pidgin_conv_new;
}
/* Somebody wants to keep this conversation around, so don't time it out */
if (conv) {
- timer = GPOINTER_TO_INT(purple_conversation_get_data(conv, "close-timer"));
+ timer = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "close-timer"));
if (timer) {
purple_timeout_remove(timer);
- purple_conversation_set_data(conv, "close-timer", GINT_TO_POINTER(0));
+ g_object_set_data(G_OBJECT(conv), "close-timer", GINT_TO_POINTER(0));
}
}
}
@@ -5525,7 +6162,7 @@ pidgin_conv_destroy(PurpleConversation *conv)
/* Make sure the destroyed conversation is not the active one */
if (gtkconv->active_conv == conv) {
gtkconv->active_conv = gtkconv->convs->data;
- purple_conversation_update(gtkconv->active_conv, PURPLE_CONV_UPDATE_FEATURES);
+ purple_conversation_update(gtkconv->active_conv, PURPLE_CONVERSATION_UPDATE_FEATURES);
}
return;
}
@@ -5539,7 +6176,7 @@ pidgin_conv_destroy(PurpleConversation *conv)
gtk_widget_destroy(gtkconv->tab_cont);
g_object_unref(gtkconv->tab_cont);
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
if (gtkconv->u.im->icon_timer != 0)
g_source_remove(gtkconv->u.im->icon_timer);
@@ -5550,48 +6187,29 @@ pidgin_conv_destroy(PurpleConversation *conv)
g_source_remove(gtkconv->u.im->typing_timer);
g_free(gtkconv->u.im);
- } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
+ } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
purple_signals_disconnect_by_handle(gtkconv->u.chat);
g_free(gtkconv->u.chat);
}
- gtk_object_sink(GTK_OBJECT(gtkconv->tooltips));
-
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);
- if (gtkconv->attach.timer) {
- g_source_remove(gtkconv->attach.timer);
+ if (gtkconv->attach_timer) {
+ g_source_remove(gtkconv->attach_timer);
}
- g_free(gtkconv);
-}
-
+ g_array_unref(gtkconv->nick_colors);
-static void
-pidgin_conv_write_im(PurpleConversation *conv, const char *who,
- const char *message, PurpleMessageFlags flags,
- time_t mtime)
-{
- PidginConversation *gtkconv;
-
- gtkconv = PIDGIN_CONVERSATION(conv);
-
- if (conv != gtkconv->active_conv &&
- flags & PURPLE_MESSAGE_ACTIVE_ONLY)
- {
- /* Plugins that want these messages suppressed should be
- * calling purple_conv_im_write(), so they get suppressed here,
- * before being written to the log. */
- purple_debug_info("gtkconv",
- "Suppressing message for an inactive conversation in pidgin_conv_write_im()\n");
- return;
- }
+ g_object_disconnect(G_OBJECT(gtkconv->theme), "any_signal::notify",
+ conv_variant_changed_cb, gtkconv, NULL);
+ g_object_unref(gtkconv->theme);
- purple_conversation_write(conv, who, message, flags, mtime);
+ g_free(gtkconv);
}
+#if 0
static const char *
get_text_tag_color(GtkTextTag *tag)
{
@@ -5619,33 +6237,37 @@ static gboolean buddytag_event(GtkTextTag *tag, GObject *imhtml,
GdkEventButton *btn_event = (GdkEventButton*) event;
PurpleConversation *conv = data;
char *buddyname;
+ gchar *name;
+
+ g_object_get(G_OBJECT(tag), "name", &name, NULL);
/* strlen("BUDDY " or "HILIT ") == 6 */
- g_return_val_if_fail((tag->name != NULL)
- && (strlen(tag->name) > 6), FALSE);
+ g_return_val_if_fail((name != NULL) && (strlen(name) > 6), FALSE);
- buddyname = (tag->name) + 6;
+ buddyname = name + 6;
/* emit chat-nick-clicked signal */
if (event->type == GDK_BUTTON_PRESS) {
gint plugin_return = GPOINTER_TO_INT(purple_signal_emit_return_1(
pidgin_conversations_get_handle(), "chat-nick-clicked",
data, buddyname, btn_event->button));
- if (plugin_return)
+ if (plugin_return) {
+ g_free(name);
return TRUE;
+ }
}
- if (btn_event->button == 1 &&
- event->type == GDK_2BUTTON_PRESS) {
+ if (btn_event->button == 1 && event->type == GDK_2BUTTON_PRESS) {
chat_do_im(PIDGIN_CONVERSATION(conv), buddyname);
+ g_free(name);
+
return TRUE;
- } else if (btn_event->button == 2
- && event->type == GDK_2BUTTON_PRESS) {
+ } else if (btn_event->button == 2 && event->type == GDK_2BUTTON_PRESS) {
chat_do_info(PIDGIN_CONVERSATION(conv), buddyname);
+ g_free(name);
return TRUE;
- } else if (btn_event->button == 3
- && event->type == GDK_BUTTON_PRESS) {
+ } else if (btn_event->button == 3 && event->type == GDK_BUTTON_PRESS) {
GtkTextIter start, end;
/* we shouldn't display the popup
@@ -5655,7 +6277,7 @@ static gboolean buddytag_event(GtkTextTag *tag, GObject *imhtml,
&start, &end)) {
GtkWidget *menu = NULL;
PurpleConnection *gc =
- purple_conversation_get_gc(conv);
+ purple_conversation_get_connection(conv);
menu = create_chat_menu(conv, buddyname, gc);
@@ -5664,18 +6286,25 @@ static gboolean buddytag_event(GtkTextTag *tag, GObject *imhtml,
btn_event->button,
btn_event->time);
+ g_free(name);
+
/* Don't propagate the event any further */
return TRUE;
}
}
+
+ g_free(name);
}
return FALSE;
}
+#endif
-static GtkTextTag *get_buddy_tag(PurpleConversation *conv, const char *who, PurpleMessageFlags flag,
+static GtkTextTag *get_buddy_tag(PurpleChatConversation *chat, const char *who, PurpleMessageFlags flag,
gboolean create)
{
+/* TODO WEBKIT */
+#if 0
PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
GtkTextTag *buddytag;
gchar *str;
@@ -5698,7 +6327,7 @@ static GtkTextTag *get_buddy_tag(PurpleConversation *conv, const char *who, Purp
buddytag = gtk_text_buffer_create_tag(
buffer, str,
"foreground-gdk", get_nick_color(gtkconv, who),
- "weight", purple_find_buddy(purple_conversation_get_account(conv), who) ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
+ "weight", purple_blist_find_buddy(purple_conversation_get_account(conv), who) ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
NULL);
g_object_set_data(G_OBJECT(buddytag), "cursor", "");
@@ -5709,8 +6338,11 @@ static GtkTextTag *get_buddy_tag(PurpleConversation *conv, const char *who, Purp
g_free(str);
return buddytag;
+#endif /* if 0 */
+ return NULL;
}
+#if 0
static void pidgin_conv_calculate_newday(PidginConversation *gtkconv, time_t mtime)
{
struct tm *tm = localtime(&mtime);
@@ -5756,19 +6388,306 @@ str_embed_direction_chars(char **str)
*str = ret;
#endif
}
+#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 GdkColor *color = get_nick_color(PIDGIN_CONVERSATION(conv), name);
+ replace = freeval = g_strdup_printf("#%02x%02x%02x", (color->red >> 8), (color->green >> 8), (color->blue >> 8));
+
+ } 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)
+{
+ PurpleImage *image;
+ gchar *escaped_shortcut;
+ gchar *uri;
+
+ escaped_shortcut = g_markup_escape_text(
+ purple_smiley_get_shortcut(smiley), -1);
+ image = purple_smiley_get_image(smiley);
+ uri = purple_image_store_get_uri(image);
+
+ 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 void
+remote_image_got(PurpleImage *image, gpointer _conv)
+{
+ PurpleConversation *conv = _conv;
+ PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
+ guint image_id;
+ gchar *js;
+
+ if (!gtkconv)
+ return;
+
+ image_id = purple_image_store_add_temporary(image);
+
+ purple_debug_info("gtkconv", "Remote image %u is ready for display",
+ image_id);
+
+ js = g_strdup_printf("remoteImageIsReady(%u)", image_id);
+ pidgin_webview_safe_execute_script(
+ PIDGIN_WEBVIEW(gtkconv->webview), js);
+ g_free(js);
+}
+
+static gboolean
+box_remote_image_cb(const GMatchInfo *info, GString *result, gpointer _conv)
+{
+ PurpleConversation *conv = _conv;
+ gchar *uri, *before, *after, *full, *alt;
+ PurpleImage *image;
+ guint img_id;
+
+ uri = g_match_info_fetch(info, 2);
+ image = purple_image_store_get_from_uri(uri);
+ g_free(uri);
+
+ full = g_match_info_fetch(info, 0);
+
+ if (purple_image_is_ready(image)) {
+ g_string_append(result, full);
+ g_free(full);
+ return FALSE;
+ }
+
+ /* search for alt */
+ alt = strstr(full, "alt=\"");
+ if (alt) {
+ gchar *end;
+ alt += strlen("alt=\"");
+ end = strstr(alt, "\"");
+ if (end)
+ end[0] = '\0';
+ else
+ alt = NULL;
+ if (alt && alt[0] == '\0')
+ alt = NULL;
+ }
+
+ /* add for ever - we don't know, when transfer finishes */
+ img_id = purple_image_store_add(image);
+
+ before = g_match_info_fetch(info, 1);
+ after = g_match_info_fetch(info, 3);
+
+ g_string_append_printf(result, "<span class=\"pending-image "
+ "pending-image-id-%u\">", img_id);
+
+ if (alt)
+ g_string_append(result, alt);
+ else
+ g_string_append(result, "&lt;img&gt;");
+
+ g_string_append(result, before);
+ g_string_append(result, "about:blank");
+ g_string_append(result, after);
+
+ g_string_append(result, "</span>");
+
+ g_free(before);
+ g_free(after);
+ g_free(full);
+
+ g_signal_connect_object(image, "ready",
+ G_CALLBACK(remote_image_got), conv, 0);
+
+ return FALSE;
+}
+
+static gchar *
+box_remote_images(PurpleConversation *conv, const gchar *msg)
+{
+ return g_regex_replace_eval(image_store_tag_re, msg, -1, 0, 0,
+ box_remote_image_cb, conv, NULL);
+}
+
+static gboolean
+writing_msg(PurpleConversation *conv, PurpleMessage *msg, gpointer _unused)
+{
+ PidginConversation *gtkconv;
+
+ g_return_val_if_fail(msg != NULL, FALSE);
+
+ if (!(purple_message_get_flags(msg) & PURPLE_MESSAGE_ACTIVE_ONLY))
+ return FALSE;
+
+ g_return_val_if_fail(conv != NULL, FALSE);
+ gtkconv = PIDGIN_CONVERSATION(conv);
+ g_return_val_if_fail(gtkconv != NULL, FALSE);
+
+ if (conv == gtkconv->active_conv)
+ return FALSE;
+
+ purple_debug_info("gtkconv",
+ "Suppressing message for an inactive conversation");
+
+ return TRUE;
+}
static void
-pidgin_conv_write_conv(PurpleConversation *conv, const char *name, const char *alias,
- const char *message, PurpleMessageFlags flags,
- time_t mtime)
+pidgin_conv_write_conv(PurpleConversation *conv, PurpleMessage *pmsg)
{
PidginConversation *gtkconv;
PurpleConnection *gc;
PurpleAccount *account;
+#if 0
int gtk_font_options = 0;
int gtk_font_options_all = 0;
- int max_scrollback_lines;
- int line_count;
char buf2[BUF_LONG];
gboolean show_date;
char *mdate;
@@ -5776,18 +6695,28 @@ pidgin_conv_write_conv(PurpleConversation *conv, const char *name, const char *a
char *with_font_tag;
char *sml_attrib = NULL;
size_t length;
- PurpleConversationType type;
+#endif
char *displaying;
gboolean plugin_return;
- char *bracket;
- int tag_count = 0;
+#if 0
gboolean is_rtl_message = FALSE;
+#endif
+
+ const char *message_html;
+ char *msg_tokenized;
+ char *escape;
+ char *script;
+ char *smileyed;
+ gchar *imgized;
+ 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 (gtkconv->attach.timer) {
+ 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.
* Instead, this message will be added to the message-list, which in turn will
@@ -5798,24 +6727,12 @@ pidgin_conv_write_conv(PurpleConversation *conv, const char *name, const char *a
if (conv != gtkconv->active_conv)
{
- if (flags & PURPLE_MESSAGE_ACTIVE_ONLY)
- {
- /* Unless this had PURPLE_MESSAGE_NO_LOG, this message
- * was logged. Plugin writers: if this isn't what
- * you wanted, call purple_conv_im_write() instead of
- * purple_conversation_write(). */
- purple_debug_info("gtkconv",
- "Suppressing message for an inactive conversation in pidgin_conv_write_conv()\n");
- return;
- }
-
/* Set the active conversation to the one that just messaged us. */
/* TODO: consider not doing this if the account is offline or something */
if (flags & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV))
pidgin_conv_switch_active_conversation(conv);
}
- type = purple_conversation_get_type(conv);
account = purple_conversation_get_account(conv);
g_return_if_fail(account != NULL);
gc = purple_account_get_connection(account);
@@ -5823,71 +6740,86 @@ pidgin_conv_write_conv(PurpleConversation *conv, const char *name, const char *a
/* Make sure URLs are clickable */
if(flags & PURPLE_MESSAGE_NO_LINKIFY)
- displaying = g_strdup(message);
+ displaying = g_strdup(purple_message_get_contents(pmsg));
else
- displaying = purple_markup_linkify(message);
+ displaying = purple_markup_linkify(purple_message_get_contents(pmsg));
plugin_return = GPOINTER_TO_INT(purple_signal_emit_return_1(
- pidgin_conversations_get_handle(), (type == PURPLE_CONV_TYPE_IM ?
- "displaying-im-msg" : "displaying-chat-msg"),
- account, name, &displaying, conv, flags));
+ 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
- /* Awful hack to work around GtkIMHtml's inefficient rendering of messages with lots of formatting changes.
- * If a message has over 100 '<' characters, strip formatting before appending it. Hopefully nobody actually
- * needs that much formatting, anyway.
- */
- for (bracket = strchr(displaying, '<'); bracket && *(bracket + 1); bracket = strchr(bracket + 1, '<'))
- tag_count++;
-
- if (tag_count > 100) {
- char *tmp = displaying;
- displaying = purple_markup_strip_html(tmp);
- g_free(tmp);
- }
-
- line_count = gtk_text_buffer_get_line_count(
- gtk_text_view_get_buffer(GTK_TEXT_VIEW(
- gtkconv->imhtml)));
+ 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";
- max_scrollback_lines = purple_prefs_get_int(
- PIDGIN_PREFS_ROOT "/conversations/scrollback_lines");
- /* If we're sitting at more than 100 lines more than the
- max scrollback, trim down to max scrollback */
- if (max_scrollback_lines > 0
- && line_count > (max_scrollback_lines + 100)) {
- GtkTextBuffer *text_buffer = gtk_text_view_get_buffer(
- GTK_TEXT_VIEW(gtkconv->imhtml));
- GtkTextIter start, end;
+ } else if (flags & PURPLE_MESSAGE_SEND) {
+ message_html = pidgin_conversation_theme_get_template(gtkconv->theme,
+ PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_CONTENT);
- gtk_text_buffer_get_start_iter(text_buffer, &start);
- gtk_text_buffer_get_iter_at_line(text_buffer, &end,
- (line_count - max_scrollback_lines));
- gtk_imhtml_delete(GTK_IMHTML(gtkconv->imhtml), &start, &end);
- }
+ } 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;
- if (type == PURPLE_CONV_TYPE_CHAT)
- {
- /* Create anchor for user */
- GtkTextIter iter;
- char *tmp = g_strconcat("user:", name, NULL);
+ g_assert(history != NULL);
+ g_assert(last_msg != NULL);
- gtk_text_buffer_get_end_iter(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml)), &iter);
- gtk_text_buffer_create_mark(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml)),
- tmp, &iter, TRUE);
- g_free(tmp);
- }
+ /* 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);
- if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/use_smooth_scrolling"))
- gtk_font_options_all |= GTK_IMHTML_USE_SMOOTHSCROLLING;
+ } else {
+ message_html = pidgin_conversation_theme_get_template(gtkconv->theme,
+ PIDGIN_CONVERSATION_THEME_TEMPLATE_STATUS);
+ }
+ gtkconv->last_flags = flags;
+ gtkconv->last_conversed = conv;
+
+ smileyed = purple_smiley_parser_smileify(conv, displaying,
+ (flags & PURPLE_MESSAGE_RECV), pidgin_conv_write_smiley,
+ (gpointer)purple_account_get_protocol_name(account));
+ imgized = box_remote_images(conv, smileyed);
+ msg_tokenized = replace_message_tokens(message_html, conv,
+ purple_message_get_author(pmsg),
+ purple_message_get_author_alias(pmsg),
+ imgized,
+ 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(imgized);
+ g_free(msg_tokenized);
+ g_free(escape);
- if (gtk_text_buffer_get_char_count(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml))))
- gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR>", gtk_font_options_all | GTK_IMHTML_NO_SCROLL);
+#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)
@@ -5930,40 +6862,34 @@ pidgin_conv_write_conv(PurpleConversation *conv, const char *name, const char *a
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_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(conv->account)))->options &
+ if (PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(purple_conversation_get_account(conv))))->options &
OPT_PROTO_USE_POINTSIZE) {
gtk_font_options |= GTK_IMHTML_USE_POINTSIZE;
}
- if (!(flags & PURPLE_MESSAGE_RECV) && (conv->features & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY))
- {
- /* We want to see our own smileys. Need to revert it after send*/
- pidgin_themes_smiley_themeize_custom(gtkconv->imhtml);
- }
-
/* TODO: These colors should not be hardcoded so log.c can use them */
if (flags & PURPLE_MESSAGE_RAW) {
- gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), message, gtk_font_options_all);
+ 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\"><!--%s --></FONT><B>%s</B></FONT>",
+ "<font %s><font size=\"2\"><span class='timestamp'>%s</span></font><b>%s</b></font>",
sml_attrib ? sml_attrib : "", mdate, displaying);
- gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all);
+ 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\"><!--%s --></FONT><B>%s</B></FONT></FONT>",
+ "<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);
- gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all);
+ 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>",
+ "<b><font %s color=\"#777777\">%s</font></b>",
sml_attrib ? sml_attrib : "", displaying);
- gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all);
+ 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(""));
@@ -5972,32 +6898,12 @@ pidgin_conv_write_conv(PurpleConversation *conv, const char *name, const char *a
int tag_start_offset = 0;
const char *tagname = NULL;
- GtkTextIter start, end;
- GtkTextMark *mark;
- GtkTextTag *tag;
- GtkTextBuffer *buffer = GTK_IMHTML(gtkconv->imhtml)->text_buffer;
-
/* Enforce direction on alias */
if (is_rtl_message)
str_embed_direction_chars(&alias_escaped);
str = g_malloc(1024);
- if (flags & PURPLE_MESSAGE_WHISPER) {
- /* If we're whispering, it's not an autoresponse. */
- if (purple_message_meify(new_message, -1 )) {
- g_snprintf(str, 1024, "***%s", alias_escaped);
- tag_start_offset += 3;
- tagname = "whisper-action-name";
- }
- else {
- g_snprintf(str, 1024, "*%s*:", alias_escaped);
- tag_start_offset += 1;
-#if 0
- tag_end_offset = 2;
-#endif
- tagname = "whisper-name";
- }
- } else {
+ 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);
@@ -6041,55 +6947,41 @@ pidgin_conv_write_conv(PurpleConversation *conv, const char *name, const char *a
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);
+ /* 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);
- gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all | GTK_IMHTML_NO_SCROLL);
+ pidgin_webview_append_html (PIDGIN_WEBVIEW(gtkconv->webview), buf2);
}
-
- gtk_text_buffer_get_end_iter(buffer, &end);
- mark = gtk_text_buffer_create_mark(buffer, NULL, &end, TRUE);
-
- g_snprintf(buf2, BUF_LONG, "<FONT %s>%s</FONT> ", sml_attrib ? sml_attrib : "", str);
- gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all | GTK_IMHTML_NO_SCROLL);
-
- gtk_text_buffer_get_end_iter(buffer, &end);
- gtk_text_buffer_get_iter_at_mark(buffer, &start, mark);
- gtk_text_buffer_apply_tag(buffer, tag, &start, &end);
- gtk_text_buffer_delete_mark(buffer, mark);
+#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){
+ if (gc) {
char *pre = g_strdup_printf("<font %s>", sml_attrib ? sml_attrib : "");
char *post = "</font>";
- int pre_len = strlen(pre);
- int post_len = strlen(post);
-
- with_font_tag = g_malloc(length + pre_len + post_len + 1);
-
- strcpy(with_font_tag, pre);
- memcpy(with_font_tag + pre_len, new_message, length);
- strcpy(with_font_tag + pre_len + length, post);
-
- length += pre_len + post_len;
+ 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);
- gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml),
- with_font_tag, gtk_font_options | gtk_font_options_all);
+ pidgin_webview_append_html(PIDGIN_WEBVIEW(gtkconv->webview),
+ with_font_tag);
g_free(with_font_tag);
g_free(new_message);
@@ -6098,6 +6990,8 @@ pidgin_conv_write_conv(PurpleConversation *conv, const char *name, const char *a
g_free(mdate);
g_free(sml_attrib);
+#endif
+
/* Tab highlighting stuff */
if (!(flags & PURPLE_MESSAGE_SEND) && !pidgin_conv_has_focus(conv))
{
@@ -6116,20 +7010,54 @@ pidgin_conv_write_conv(PurpleConversation *conv, const char *name, const char *a
gtkconv_set_unseen(gtkconv, unseen);
}
- if (!(flags & PURPLE_MESSAGE_RECV) && (conv->features & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY))
- {
- /* Restore the smiley-data */
- pidgin_themes_smiley_themeize(gtkconv->imhtml);
+ /* on rejoin only request message history from after this message */
+ if (flags & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV) &&
+ PURPLE_IS_CHAT_CONVERSATION(conv)) {
+ PurpleChat *chat = purple_blist_find_chat(
+ purple_conversation_get_account(conv),
+ purple_conversation_get_name(conv));
+ if (chat) {
+ GHashTable *comps = purple_chat_get_components(chat);
+ time_t now, history_since, prev_history_since = 0;
+ struct tm *history_since_tm;
+ const char *history_since_s, *prev_history_since_s;
+
+ history_since = purple_message_get_time(pmsg) + 1;
+
+ prev_history_since_s = g_hash_table_lookup(comps,
+ "history_since");
+ if (prev_history_since_s != NULL)
+ prev_history_since = purple_str_to_time(
+ prev_history_since_s, TRUE, NULL, NULL,
+ NULL);
+
+ now = time(NULL);
+ /* in case of incorrectly stored timestamps */
+ if (prev_history_since > now)
+ prev_history_since = now;
+ /* in case of delayed messages */
+ if (history_since < prev_history_since)
+ history_since = prev_history_since;
+
+ history_since_tm = gmtime(&history_since);
+ history_since_s = purple_utf8_strftime(
+ "%Y-%m-%dT%H:%M:%SZ", history_since_tm);
+ if (g_strcmp0(prev_history_since_s,
+ history_since_s) != 0)
+ g_hash_table_replace(comps,
+ g_strdup("history_since"),
+ g_strdup(history_since_s));
+ }
}
purple_signal_emit(pidgin_conversations_get_handle(),
- (type == PURPLE_CONV_TYPE_IM ? "displayed-im-msg" : "displayed-chat-msg"),
- account, name, displaying, conv, flags);
+ (PURPLE_IS_IM_CONVERSATION(conv) ? "displayed-im-msg" : "displayed-chat-msg"),
+ conv, pmsg);
g_free(displaying);
update_typing_message(gtkconv, NULL);
}
-static gboolean get_iter_from_chatbuddy(PurpleConvChatBuddy *cb, GtkTreeIter *iter)
+static gboolean get_iter_from_chatuser(PurpleChatUser *cb, GtkTreeIter *iter)
{
GtkTreeRowReference *ref;
GtkTreePath *path;
@@ -6137,7 +7065,7 @@ static gboolean get_iter_from_chatbuddy(PurpleConvChatBuddy *cb, GtkTreeIter *it
g_return_val_if_fail(cb != NULL, FALSE);
- ref = cb->ui_data;
+ ref = purple_chat_user_get_ui_data(cb);
if (!ref)
return FALSE;
@@ -6155,9 +7083,8 @@ static gboolean get_iter_from_chatbuddy(PurpleConvChatBuddy *cb, GtkTreeIter *it
}
static void
-pidgin_conv_chat_add_users(PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals)
+pidgin_conv_chat_add_users(PurpleChatConversation *chat, GList *cbuddies, gboolean new_arrivals)
{
- PurpleConvChat *chat;
PidginConversation *gtkconv;
PidginChatPane *gtkchat;
GtkListStore *ls;
@@ -6166,11 +7093,10 @@ pidgin_conv_chat_add_users(PurpleConversation *conv, GList *cbuddies, gboolean n
char tmp[BUF_LONG];
int num_users;
- chat = PURPLE_CONV_CHAT(conv);
- gtkconv = PIDGIN_CONVERSATION(conv);
+ gtkconv = PIDGIN_CONVERSATION(PURPLE_CONVERSATION(chat));
gtkchat = gtkconv->u.chat;
- num_users = g_list_length(purple_conv_chat_get_users(chat));
+ num_users = purple_chat_conversation_get_users_count(chat);
g_snprintf(tmp, sizeof(tmp),
ngettext("%d person in room", "%d people in room",
@@ -6186,7 +7112,7 @@ pidgin_conv_chat_add_users(PurpleConversation *conv, GList *cbuddies, gboolean n
l = cbuddies;
while (l != NULL) {
- add_chat_buddy_common(conv, (PurpleConvChatBuddy *)l->data, NULL);
+ add_chat_user_common(chat, (PurpleChatUser *)l->data, NULL);
l = l->next;
}
@@ -6198,19 +7124,17 @@ pidgin_conv_chat_add_users(PurpleConversation *conv, GList *cbuddies, gboolean n
}
static void
-pidgin_conv_chat_rename_user(PurpleConversation *conv, const char *old_name,
+pidgin_conv_chat_rename_user(PurpleChatConversation *chat, const char *old_name,
const char *new_name, const char *new_alias)
{
- PurpleConvChat *chat;
PidginConversation *gtkconv;
PidginChatPane *gtkchat;
- PurpleConvChatBuddy *old_cbuddy, *new_cbuddy;
+ PurpleChatUser *old_chatuser, *new_chatuser;
GtkTreeIter iter;
GtkTreeModel *model;
GtkTextTag *tag;
- chat = PURPLE_CONV_CHAT(conv);
- gtkconv = PIDGIN_CONVERSATION(conv);
+ gtkconv = PIDGIN_CONVERSATION(PURPLE_CONVERSATION(chat));
gtkchat = gtkconv->u.chat;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list));
@@ -6218,34 +7142,33 @@ pidgin_conv_chat_rename_user(PurpleConversation *conv, const char *old_name,
if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter))
return;
- if ((tag = get_buddy_tag(conv, old_name, 0, FALSE)))
+ if ((tag = get_buddy_tag(chat, old_name, 0, FALSE)))
g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_ITALIC, NULL);
- if ((tag = get_buddy_tag(conv, old_name, PURPLE_MESSAGE_NICK, FALSE)))
+ if ((tag = get_buddy_tag(chat, old_name, PURPLE_MESSAGE_NICK, FALSE)))
g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_ITALIC, NULL);
- old_cbuddy = purple_conv_chat_cb_find(chat, old_name);
- if (!old_cbuddy)
+ old_chatuser = purple_chat_conversation_find_user(chat, old_name);
+ if (!old_chatuser)
return;
- if (get_iter_from_chatbuddy(old_cbuddy, &iter)) {
- GtkTreeRowReference *ref = old_cbuddy->ui_data;
+ if (get_iter_from_chatuser(old_chatuser, &iter)) {
+ GtkTreeRowReference *ref = purple_chat_user_get_ui_data(old_chatuser);
gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
gtk_tree_row_reference_free(ref);
- old_cbuddy->ui_data = NULL;
+ purple_chat_user_set_ui_data(old_chatuser, NULL);
}
g_return_if_fail(new_alias != NULL);
- new_cbuddy = purple_conv_chat_cb_find(chat, new_name);
+ new_chatuser = purple_chat_conversation_find_user(chat, new_name);
- add_chat_buddy_common(conv, new_cbuddy, old_name);
+ add_chat_user_common(chat, new_chatuser, old_name);
}
static void
-pidgin_conv_chat_remove_users(PurpleConversation *conv, GList *users)
+pidgin_conv_chat_remove_users(PurpleChatConversation *chat, GList *users)
{
- PurpleConvChat *chat;
PidginConversation *gtkconv;
PidginChatPane *gtkchat;
GtkTreeIter iter;
@@ -6256,11 +7179,10 @@ pidgin_conv_chat_remove_users(PurpleConversation *conv, GList *users)
gboolean f;
GtkTextTag *tag;
- chat = PURPLE_CONV_CHAT(conv);
- gtkconv = PIDGIN_CONVERSATION(conv);
+ gtkconv = PIDGIN_CONVERSATION(PURPLE_CONVERSATION(chat));
gtkchat = gtkconv->u.chat;
- num_users = g_list_length(purple_conv_chat_get_users(chat));
+ num_users = purple_chat_conversation_get_users_count(chat);
for (l = users; l != NULL; l = l->next) {
model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list));
@@ -6284,9 +7206,9 @@ pidgin_conv_chat_remove_users(PurpleConversation *conv, GList *users)
g_free(val);
} while (f);
- if ((tag = get_buddy_tag(conv, l->data, 0, FALSE)))
+ if ((tag = get_buddy_tag(chat, l->data, 0, FALSE)))
g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_ITALIC, NULL);
- if ((tag = get_buddy_tag(conv, l->data, PURPLE_MESSAGE_NICK, FALSE)))
+ if ((tag = get_buddy_tag(chat, l->data, PURPLE_MESSAGE_NICK, FALSE)))
g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_ITALIC, NULL);
}
@@ -6298,17 +7220,19 @@ pidgin_conv_chat_remove_users(PurpleConversation *conv, GList *users)
}
static void
-pidgin_conv_chat_update_user(PurpleConversation *conv, const char *user)
+pidgin_conv_chat_update_user(PurpleChatUser *chatuser)
{
- PurpleConvChat *chat;
- PurpleConvChatBuddy *cbuddy;
+ PurpleChatConversation *chat;
PidginConversation *gtkconv;
PidginChatPane *gtkchat;
GtkTreeIter iter;
GtkTreeModel *model;
- chat = PURPLE_CONV_CHAT(conv);
- gtkconv = PIDGIN_CONVERSATION(conv);
+ if (!chatuser)
+ return;
+
+ chat = purple_chat_user_get_chat(chatuser);
+ gtkconv = PIDGIN_CONVERSATION(PURPLE_CONVERSATION(chat));
gtkchat = gtkconv->u.chat;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list));
@@ -6316,26 +7240,22 @@ pidgin_conv_chat_update_user(PurpleConversation *conv, const char *user)
if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter))
return;
- cbuddy = purple_conv_chat_cb_find(chat, user);
- if (!cbuddy)
- return;
-
- if (get_iter_from_chatbuddy(cbuddy, &iter)) {
- GtkTreeRowReference *ref = cbuddy->ui_data;
+ if (get_iter_from_chatuser(chatuser, &iter)) {
+ GtkTreeRowReference *ref = purple_chat_user_get_ui_data(chatuser);
gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
gtk_tree_row_reference_free(ref);
- cbuddy->ui_data = NULL;
+ purple_chat_user_set_ui_data(chatuser, NULL);
}
- if (cbuddy)
- add_chat_buddy_common(conv, cbuddy, NULL);
+ if (chatuser)
+ add_chat_user_common(chat, chatuser, NULL);
}
gboolean
pidgin_conv_has_focus(PurpleConversation *conv)
{
PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
- PidginWindow *win;
+ PidginConvWindow *win;
gboolean has_focus;
win = gtkconv->win;
@@ -6348,152 +7268,12 @@ pidgin_conv_has_focus(PurpleConversation *conv)
return FALSE;
}
-static gboolean
-add_custom_smiley_for_imhtml(GtkIMHtml *imhtml, const char *sml, const char *smile)
-{
- GtkIMHtmlSmiley *smiley;
-
- smiley = gtk_imhtml_smiley_get(imhtml, sml, smile);
-
- if (smiley) {
- if (!(smiley->flags & GTK_IMHTML_SMILEY_CUSTOM)) {
- return FALSE;
- }
- gtk_imhtml_smiley_reload(smiley);
- return TRUE;
- }
-
- smiley = gtk_imhtml_smiley_create(NULL, smile, FALSE, GTK_IMHTML_SMILEY_CUSTOM);
- gtk_imhtml_associate_smiley(imhtml, sml, smiley);
- g_signal_connect_swapped(imhtml, "destroy", G_CALLBACK(gtk_imhtml_smiley_destroy), smiley);
-
- return TRUE;
-}
-
-static gboolean
-pidgin_conv_custom_smiley_add(PurpleConversation *conv, const char *smile, gboolean remote)
-{
- PidginConversation *gtkconv;
- struct smiley_list *list;
- const char *sml = NULL, *conv_sml;
-
- if (!conv || !smile || !*smile) {
- return FALSE;
- }
-
- /* If smileys are off, return false */
- if (pidgin_themes_smileys_disabled())
- return FALSE;
-
- /* If possible add this smiley to the current theme.
- * The addition is only temporary: custom smilies aren't saved to disk. */
- conv_sml = purple_account_get_protocol_name(conv->account);
- gtkconv = PIDGIN_CONVERSATION(conv);
-
- for (list = (struct smiley_list *)current_smiley_theme->list; list; list = list->next) {
- if (!strcmp(list->sml, conv_sml)) {
- sml = list->sml;
- break;
- }
- }
-
- if (!add_custom_smiley_for_imhtml(GTK_IMHTML(gtkconv->imhtml), sml, smile))
- return FALSE;
-
- if (!remote) /* If it's a local custom smiley, then add it for the entry */
- if (!add_custom_smiley_for_imhtml(GTK_IMHTML(gtkconv->entry), sml, smile))
- return FALSE;
-
- return TRUE;
-}
-
-static void
-pidgin_conv_custom_smiley_write(PurpleConversation *conv, const char *smile,
- const guchar *data, gsize size)
-{
- PidginConversation *gtkconv;
- GtkIMHtmlSmiley *smiley;
- const char *sml;
- GError *error = NULL;
-
- sml = purple_account_get_protocol_name(conv->account);
- gtkconv = PIDGIN_CONVERSATION(conv);
- smiley = gtk_imhtml_smiley_get(GTK_IMHTML(gtkconv->imhtml), sml, smile);
-
- if (!smiley)
- return;
-
- smiley->data = g_realloc(smiley->data, smiley->datasize + size);
- g_memmove((guchar *)smiley->data + smiley->datasize, data, size);
- smiley->datasize += size;
-
- if (!smiley->loader)
- return;
-
- if (!gdk_pixbuf_loader_write(smiley->loader, data, size, &error) || error) {
- purple_debug_warning("gtkconv", "gdk_pixbuf_loader_write() "
- "failed with size=%zu: %s\n", size,
- error ? error->message : "(no error message)");
- if (error)
- g_error_free(error);
- /* We must stop using the GdkPixbufLoader because trying to load
- certain invalid GIFs with at least gdk-pixbuf 2.23.3 can return
- a GdkPixbuf that will cause some operations (like
- gdk_pixbuf_scale_simple()) to consume memory in an infinite loop.
- But we also don't want to set smiley->loader to NULL because our
- code might expect it to be set. So create a new loader. */
- g_object_unref(G_OBJECT(smiley->loader));
- smiley->loader = gdk_pixbuf_loader_new();
- }
-}
-
-static void
-pidgin_conv_custom_smiley_close(PurpleConversation *conv, const char *smile)
-{
- PidginConversation *gtkconv;
- GtkIMHtmlSmiley *smiley;
- const char *sml;
- GError *error = NULL;
-
- g_return_if_fail(conv != NULL);
- g_return_if_fail(smile != NULL);
-
- sml = purple_account_get_protocol_name(conv->account);
- gtkconv = PIDGIN_CONVERSATION(conv);
- smiley = gtk_imhtml_smiley_get(GTK_IMHTML(gtkconv->imhtml), sml, smile);
-
- if (!smiley)
- return;
-
- if (!smiley->loader)
- return;
-
- purple_debug_info("gtkconv", "About to close the smiley pixbuf\n");
-
- if (!gdk_pixbuf_loader_close(smiley->loader, &error) || error) {
- purple_debug_warning("gtkconv", "gdk_pixbuf_loader_close() "
- "failed: %s\n",
- error ? error->message : "(no error message)");
- if (error)
- g_error_free(error);
- /* We must stop using the GdkPixbufLoader because if we tried to
- load certain invalid GIFs with all current versions of GDK (as
- of 2011-06-15) then it's possible the loader will contain data
- that could cause some operations (like gdk_pixbuf_scale_simple())
- to consume memory in an infinite loop. But we also don't want
- to set smiley->loader to NULL because our code might expect it
- to be set. So create a new loader. */
- g_object_unref(G_OBJECT(smiley->loader));
- smiley->loader = gdk_pixbuf_loader_new();
- }
-}
-
static void
pidgin_conv_send_confirm(PurpleConversation *conv, const char *message)
{
PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
- gtk_imhtml_append_text(GTK_IMHTML(gtkconv->entry), message, 0);
+ pidgin_webview_append_html(PIDGIN_WEBVIEW(gtkconv->webview), message);
}
/*
@@ -6504,22 +7284,22 @@ pidgin_conv_send_confirm(PurpleConversation *conv, const char *message)
static void
gray_stuff_out(PidginConversation *gtkconv)
{
- PidginWindow *win;
+ PidginConvWindow *win;
PurpleConversation *conv = gtkconv->active_conv;
PurpleConnection *gc;
PurplePluginProtocolInfo *prpl_info = NULL;
GdkPixbuf *window_icon = NULL;
- GtkIMHtmlButtons buttons;
+ PidginWebViewButtons buttons;
PurpleAccount *account;
win = pidgin_conv_get_window(gtkconv);
- gc = purple_conversation_get_gc(conv);
+ gc = purple_conversation_get_connection(conv);
account = purple_conversation_get_account(conv);
if (gc != NULL)
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
- if (win->menu.send_to != NULL)
+ if (win->menu->send_to != NULL)
update_send_to_selection(win);
/*
@@ -6530,61 +7310,61 @@ gray_stuff_out(PidginConversation *gtkconv)
* supports it or not--that only affects if the button or menu item
* is sensitive or not.
*/
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
/* Show stuff that applies to IMs, hide stuff that applies to chats */
/* Deal with menu items */
- gtk_widget_show(win->menu.view_log);
- gtk_widget_show(win->menu.send_file);
- gtk_widget_show(g_object_get_data(G_OBJECT(win->window), "get_attention"));
- gtk_widget_show(win->menu.add_pounce);
- gtk_widget_show(win->menu.get_info);
- gtk_widget_hide(win->menu.invite);
- gtk_widget_show(win->menu.alias);
- if (purple_privacy_check(account, purple_conversation_get_name(conv))) {
- gtk_widget_hide(win->menu.unblock);
- gtk_widget_show(win->menu.block);
+ gtk_action_set_visible(win->menu->view_log, TRUE);
+ gtk_action_set_visible(win->menu->send_file, TRUE);
+ gtk_action_set_visible(win->menu->get_attention, TRUE);
+ gtk_action_set_visible(win->menu->add_pounce, TRUE);
+ gtk_action_set_visible(win->menu->get_info, TRUE);
+ gtk_action_set_visible(win->menu->invite, FALSE);
+ gtk_action_set_visible(win->menu->alias, TRUE);
+ if (purple_account_privacy_check(account, purple_conversation_get_name(conv))) {
+ gtk_action_set_visible(win->menu->unblock, FALSE);
+ gtk_action_set_visible(win->menu->block, TRUE);
} else {
- gtk_widget_hide(win->menu.block);
- gtk_widget_show(win->menu.unblock);
+ gtk_action_set_visible(win->menu->block, FALSE);
+ gtk_action_set_visible(win->menu->unblock, TRUE);
}
- if (purple_find_buddy(account, purple_conversation_get_name(conv)) == NULL) {
- gtk_widget_show(win->menu.add);
- gtk_widget_hide(win->menu.remove);
+ if (purple_blist_find_buddy(account, purple_conversation_get_name(conv)) == NULL) {
+ gtk_action_set_visible(win->menu->add, TRUE);
+ gtk_action_set_visible(win->menu->remove, FALSE);
} else {
- gtk_widget_show(win->menu.remove);
- gtk_widget_hide(win->menu.add);
+ gtk_action_set_visible(win->menu->remove, TRUE);
+ gtk_action_set_visible(win->menu->add, FALSE);
}
- gtk_widget_show(win->menu.insert_link);
- gtk_widget_show(win->menu.insert_image);
- } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
+ gtk_action_set_visible(win->menu->insert_link, TRUE);
+ gtk_action_set_visible(win->menu->insert_image, TRUE);
+ } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
/* Show stuff that applies to Chats, hide stuff that applies to IMs */
/* Deal with menu items */
- gtk_widget_show(win->menu.view_log);
- gtk_widget_hide(win->menu.send_file);
- gtk_widget_hide(g_object_get_data(G_OBJECT(win->window), "get_attention"));
- gtk_widget_hide(win->menu.add_pounce);
- gtk_widget_hide(win->menu.get_info);
- gtk_widget_show(win->menu.invite);
- gtk_widget_show(win->menu.alias);
- gtk_widget_hide(win->menu.block);
- gtk_widget_hide(win->menu.unblock);
+ gtk_action_set_visible(win->menu->view_log, TRUE);
+ gtk_action_set_visible(win->menu->send_file, FALSE);
+ gtk_action_set_visible(win->menu->get_attention, FALSE);
+ gtk_action_set_visible(win->menu->add_pounce, FALSE);
+ gtk_action_set_visible(win->menu->get_info, FALSE);
+ gtk_action_set_visible(win->menu->invite, TRUE);
+ gtk_action_set_visible(win->menu->alias, TRUE);
+ gtk_action_set_visible(win->menu->block, FALSE);
+ gtk_action_set_visible(win->menu->unblock, FALSE);
if ((account == NULL) || purple_blist_find_chat(account, purple_conversation_get_name(conv)) == NULL) {
/* If the chat is NOT in the buddy list */
- gtk_widget_show(win->menu.add);
- gtk_widget_hide(win->menu.remove);
+ gtk_action_set_visible(win->menu->add, TRUE);
+ gtk_action_set_visible(win->menu->remove, FALSE);
} else {
/* If the chat IS in the buddy list */
- gtk_widget_hide(win->menu.add);
- gtk_widget_show(win->menu.remove);
+ gtk_action_set_visible(win->menu->add, FALSE);
+ gtk_action_set_visible(win->menu->remove, TRUE);
}
- gtk_widget_show(win->menu.insert_link);
- gtk_widget_show(win->menu.insert_image);
+ gtk_action_set_visible(win->menu->insert_link, TRUE);
+ gtk_action_set_visible(win->menu->insert_image, TRUE);
}
/*
@@ -6592,67 +7372,63 @@ gray_stuff_out(PidginConversation *gtkconv)
* and what features that account supports.
*/
if ((gc != NULL) &&
- ((purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_CHAT) ||
- !purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)) ))
+ (!PURPLE_IS_CHAT_CONVERSATION(conv) ||
+ !purple_chat_conversation_has_left(PURPLE_CHAT_CONVERSATION(conv)) ))
{
+ PurpleConnectionFlags features = purple_conversation_get_features(conv);
/* Account is online */
/* Deal with the toolbar */
- if (conv->features & PURPLE_CONNECTION_HTML)
+ if (features & PURPLE_CONNECTION_FLAG_HTML)
{
- buttons = GTK_IMHTML_ALL; /* Everything on */
- if (conv->features & PURPLE_CONNECTION_NO_BGCOLOR)
- buttons &= ~GTK_IMHTML_BACKCOLOR;
- if (conv->features & PURPLE_CONNECTION_NO_FONTSIZE)
+ buttons = PIDGIN_WEBVIEW_ALL; /* Everything on */
+ if (features & PURPLE_CONNECTION_FLAG_NO_BGCOLOR)
+ buttons &= ~PIDGIN_WEBVIEW_BACKCOLOR;
+ if (features & PURPLE_CONNECTION_FLAG_NO_FONTSIZE)
{
- buttons &= ~GTK_IMHTML_GROW;
- buttons &= ~GTK_IMHTML_SHRINK;
+ buttons &= ~PIDGIN_WEBVIEW_GROW;
+ buttons &= ~PIDGIN_WEBVIEW_SHRINK;
}
- if (conv->features & PURPLE_CONNECTION_NO_URLDESC)
- buttons &= ~GTK_IMHTML_LINKDESC;
+ if (features & PURPLE_CONNECTION_FLAG_NO_URLDESC)
+ buttons &= ~PIDGIN_WEBVIEW_LINKDESC;
} else {
- buttons = GTK_IMHTML_SMILEY | GTK_IMHTML_IMAGE;
+ buttons = PIDGIN_WEBVIEW_SMILEY | PIDGIN_WEBVIEW_IMAGE;
}
- if (!(prpl_info->options & OPT_PROTO_IM_IMAGE))
- conv->features |= PURPLE_CONNECTION_NO_IMAGES;
-
- if(conv->features & PURPLE_CONNECTION_NO_IMAGES)
- buttons &= ~GTK_IMHTML_IMAGE;
+ if (features & PURPLE_CONNECTION_FLAG_NO_IMAGES)
+ buttons &= ~PIDGIN_WEBVIEW_IMAGE;
- if (conv->features & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY)
- buttons |= GTK_IMHTML_CUSTOM_SMILEY;
+ if (features & PURPLE_CONNECTION_FLAG_ALLOW_CUSTOM_SMILEY)
+ buttons |= PIDGIN_WEBVIEW_CUSTOM_SMILEY;
else
- buttons &= ~GTK_IMHTML_CUSTOM_SMILEY;
+ buttons &= ~PIDGIN_WEBVIEW_CUSTOM_SMILEY;
- gtk_imhtml_set_format_functions(GTK_IMHTML(gtkconv->entry), buttons);
- if (account != NULL)
- gtk_imhtmltoolbar_associate_smileys(GTK_IMHTMLTOOLBAR(gtkconv->toolbar), purple_account_get_protocol_id(account));
+ pidgin_webview_set_format_functions(PIDGIN_WEBVIEW(gtkconv->entry), buttons);
/* Deal with menu items */
- gtk_widget_set_sensitive(win->menu.view_log, TRUE);
- gtk_widget_set_sensitive(win->menu.add_pounce, TRUE);
- gtk_widget_set_sensitive(win->menu.get_info, (prpl_info->get_info != NULL));
- gtk_widget_set_sensitive(win->menu.invite, (prpl_info->chat_invite != NULL));
- gtk_widget_set_sensitive(win->menu.insert_link, (conv->features & PURPLE_CONNECTION_HTML));
- gtk_widget_set_sensitive(win->menu.insert_image, !(conv->features & PURPLE_CONNECTION_NO_IMAGES));
-
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
+ 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, (prpl_info->get_info != NULL));
+ gtk_action_set_sensitive(win->menu->invite, (prpl_info->chat_invite != NULL));
+ 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));
+
+ if (PURPLE_IS_IM_CONVERSATION(conv))
{
- gtk_widget_set_sensitive(win->menu.add, (prpl_info->add_buddy != NULL) || (prpl_info->add_buddy_with_invite != NULL));
- gtk_widget_set_sensitive(win->menu.remove, (prpl_info->remove_buddy != NULL));
- gtk_widget_set_sensitive(win->menu.send_file,
+ gtk_action_set_sensitive(win->menu->add, (prpl_info->add_buddy != NULL));
+ gtk_action_set_sensitive(win->menu->remove, (prpl_info->remove_buddy != NULL));
+ gtk_action_set_sensitive(win->menu->send_file,
(prpl_info->send_file != NULL && (!prpl_info->can_receive_file ||
prpl_info->can_receive_file(gc, purple_conversation_get_name(conv)))));
- gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(win->window), "get_attention"), (prpl_info->send_attention != NULL));
- gtk_widget_set_sensitive(win->menu.alias,
+ gtk_action_set_sensitive(win->menu->get_attention, (prpl_info->send_attention != NULL));
+ gtk_action_set_sensitive(win->menu->alias,
(account != NULL) &&
- (purple_find_buddy(account, purple_conversation_get_name(conv)) != NULL));
+ (purple_blist_find_buddy(account, purple_conversation_get_name(conv)) != NULL));
}
- else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
+ else if (PURPLE_IS_CHAT_CONVERSATION(conv))
{
- gtk_widget_set_sensitive(win->menu.add, (prpl_info->join_chat != NULL));
- gtk_widget_set_sensitive(win->menu.remove, (prpl_info->join_chat != NULL));
- gtk_widget_set_sensitive(win->menu.alias,
+ gtk_action_set_sensitive(win->menu->add, (prpl_info->join_chat != NULL));
+ gtk_action_set_sensitive(win->menu->remove, (prpl_info->join_chat != NULL));
+ gtk_action_set_sensitive(win->menu->alias,
(account != NULL) &&
(purple_blist_find_chat(account, purple_conversation_get_name(conv)) != NULL));
}
@@ -6662,18 +7438,17 @@ gray_stuff_out(PidginConversation *gtkconv)
/* Or it's a chat that we've left. */
/* Then deal with menu items */
- gtk_widget_set_sensitive(win->menu.view_log, TRUE);
- gtk_widget_set_sensitive(win->menu.send_file, FALSE);
- gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(win->window),
- "get_attention"), FALSE);
- gtk_widget_set_sensitive(win->menu.add_pounce, TRUE);
- gtk_widget_set_sensitive(win->menu.get_info, FALSE);
- gtk_widget_set_sensitive(win->menu.invite, FALSE);
- gtk_widget_set_sensitive(win->menu.alias, FALSE);
- gtk_widget_set_sensitive(win->menu.add, FALSE);
- gtk_widget_set_sensitive(win->menu.remove, FALSE);
- gtk_widget_set_sensitive(win->menu.insert_link, TRUE);
- gtk_widget_set_sensitive(win->menu.insert_image, FALSE);
+ gtk_action_set_sensitive(win->menu->view_log, TRUE);
+ gtk_action_set_sensitive(win->menu->send_file, FALSE);
+ gtk_action_set_sensitive(win->menu->get_attention, FALSE);
+ gtk_action_set_sensitive(win->menu->add_pounce, TRUE);
+ gtk_action_set_sensitive(win->menu->get_info, FALSE);
+ gtk_action_set_sensitive(win->menu->invite, FALSE);
+ gtk_action_set_sensitive(win->menu->alias, FALSE);
+ gtk_action_set_sensitive(win->menu->add, FALSE);
+ gtk_action_set_sensitive(win->menu->remove, FALSE);
+ gtk_action_set_sensitive(win->menu->insert_link, TRUE);
+ gtk_action_set_sensitive(win->menu->insert_image, FALSE);
}
/*
@@ -6682,10 +7457,10 @@ gray_stuff_out(PidginConversation *gtkconv)
if (pidgin_conv_window_is_active_conversation(conv))
{
GList *l = NULL;
- if ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) &&
+ if (PURPLE_IS_IM_CONVERSATION(conv) &&
(gtkconv->u.im->anim))
{
- PurpleBuddy *buddy = purple_find_buddy(conv->account, conv->name);
+ PurpleBuddy *buddy = purple_blist_find_buddy(purple_conversation_get_account(conv), purple_conversation_get_name(conv));
window_icon =
gdk_pixbuf_animation_get_static_image(gtkconv->u.im->anim);
@@ -6709,7 +7484,7 @@ static void
pidgin_conv_update_fields(PurpleConversation *conv, PidginConvFields fields)
{
PidginConversation *gtkconv;
- PidginWindow *win;
+ PidginConvWindow *win;
gtkconv = PIDGIN_CONVERSATION(conv);
if (!gtkconv)
@@ -6725,16 +7500,20 @@ pidgin_conv_update_fields(PurpleConversation *conv, PidginConvFields fields)
if (fields & PIDGIN_CONV_BUDDY_ICON)
{
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
- pidgin_conv_update_buddy_icon(conv);
+ if (PURPLE_IS_IM_CONVERSATION(conv))
+ pidgin_conv_update_buddy_icon(PURPLE_IM_CONVERSATION(conv));
}
if (fields & PIDGIN_CONV_MENU)
{
gray_stuff_out(PIDGIN_CONVERSATION(conv));
generate_send_to_items(win);
+ regenerate_plugins_items(win);
}
+ if (fields & PIDGIN_CONV_E2EE)
+ generate_e2ee_controls(win);
+
if (fields & PIDGIN_CONV_TAB_ICON)
{
update_tab_icon(conv);
@@ -6742,31 +7521,27 @@ pidgin_conv_update_fields(PurpleConversation *conv, PidginConvFields fields)
}
if ((fields & PIDGIN_CONV_TOPIC) &&
- purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
+ PURPLE_IS_CHAT_CONVERSATION(conv))
{
const char *topic;
- PurpleConvChat *chat = PURPLE_CONV_CHAT(conv);
PidginChatPane *gtkchat = gtkconv->u.chat;
if (gtkchat->topic_text != NULL)
{
- topic = purple_conv_chat_get_topic(chat);
+ topic = purple_chat_conversation_get_topic(PURPLE_CHAT_CONVERSATION(conv));
gtk_entry_set_text(GTK_ENTRY(gtkchat->topic_text), topic ? topic : "");
- gtk_tooltips_set_tip(gtkconv->tooltips, gtkchat->topic_text,
- topic ? topic : "", NULL);
+ gtk_widget_set_tooltip_text(gtkchat->topic_text,
+ topic ? topic : "");
}
}
- if (fields & PIDGIN_CONV_SMILEY_THEME)
- pidgin_themes_smiley_themeize(PIDGIN_CONVERSATION(conv)->imhtml);
-
if ((fields & PIDGIN_CONV_COLORIZE_TITLE) ||
(fields & PIDGIN_CONV_SET_TITLE) ||
- (fields & PIDGIN_CONV_TOPIC))
+ (fields & PIDGIN_CONV_TOPIC))
{
char *title;
- PurpleConvIm *im = NULL;
+ PurpleIMConversation *im = NULL;
PurpleAccount *account = purple_conversation_get_account(conv);
PurpleBuddy *buddy = NULL;
char *markup = NULL;
@@ -6774,37 +7549,50 @@ pidgin_conv_update_fields(PurpleConversation *conv, PidginConvFields fields)
/* I think this is a little longer than it needs to be but I'm lazy. */
char *style;
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
- im = PURPLE_CONV_IM(conv);
+ if (PURPLE_IS_IM_CONVERSATION(conv))
+ im = PURPLE_IM_CONVERSATION(conv);
if ((account == NULL) ||
!purple_account_is_connected(account) ||
- ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
- && purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv))))
+ (PURPLE_IS_CHAT_CONVERSATION(conv)
+ && purple_chat_conversation_has_left(PURPLE_CHAT_CONVERSATION(conv))))
title = g_strdup_printf("(%s)", purple_conversation_get_title(conv));
else
title = g_strdup(purple_conversation_get_title(conv));
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
- buddy = purple_find_buddy(account, conv->name);
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
+ buddy = purple_blist_find_buddy(account, purple_conversation_get_name(conv));
if (buddy) {
markup = pidgin_blist_get_name_markup(buddy, FALSE, FALSE);
} else {
markup = title;
}
- } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
+ } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
const char *topic = gtkconv->u.chat->topic_text
? gtk_entry_get_text(GTK_ENTRY(gtkconv->u.chat->topic_text))
: NULL;
- char *esc = NULL, *tmp;
- esc = topic ? g_markup_escape_text(topic, -1) : NULL;
- tmp = g_markup_escape_text(purple_conversation_get_title(conv), -1);
- markup = g_strdup_printf("%s%s<span color='%s' size='smaller'>%s</span>",
- tmp, esc && *esc ? "\n" : "",
+ const char *title = purple_conversation_get_title(conv);
+ const char *name = purple_conversation_get_name(conv);
+
+ char *topic_esc, *unaliased, *unaliased_esc, *title_esc;
+
+ topic_esc = topic ? g_markup_escape_text(topic, -1) : NULL;
+ unaliased = g_utf8_collate(title, name) ? g_strdup_printf("(%s)", name) : NULL;
+ unaliased_esc = unaliased ? g_markup_escape_text(unaliased, -1) : NULL;
+ title_esc = g_markup_escape_text(title, -1);
+
+ markup = g_strdup_printf("%s%s<span size='smaller'>%s</span>%s<span color='%s' size='smaller'>%s</span>",
+ title_esc,
+ unaliased_esc ? " " : "",
+ unaliased_esc ? unaliased_esc : "",
+ topic_esc && *topic_esc ? "\n" : "",
pidgin_get_dim_grey_string(gtkconv->infopane),
- esc ? esc : "");
- g_free(tmp);
- g_free(esc);
+ topic_esc ? topic_esc : "");
+
+ g_free(title_esc);
+ g_free(topic_esc);
+ g_free(unaliased);
+ g_free(unaliased_esc);
}
gtk_list_store_set(gtkconv->infopane_model, &(gtkconv->infopane_iter),
CONV_TEXT_COLUMN, markup, -1);
@@ -6814,16 +7602,16 @@ pidgin_conv_update_fields(PurpleConversation *conv, PidginConvFields fields)
if (title != markup)
g_free(markup);
- if (!GTK_WIDGET_REALIZED(gtkconv->tab_label))
+ if (!gtk_widget_get_realized(gtkconv->tab_label))
gtk_widget_realize(gtkconv->tab_label);
accessibility_obj = gtk_widget_get_accessible(gtkconv->tab_cont);
if (im != NULL &&
- purple_conv_im_get_typing_state(im) == PURPLE_TYPING) {
+ purple_im_conversation_get_typing_state(im) == PURPLE_IM_TYPING) {
atk_object_set_description(accessibility_obj, _("Typing"));
style = "tab-label-typing";
} else if (im != NULL &&
- purple_conv_im_get_typing_state(im) == PURPLE_TYPED) {
+ purple_im_conversation_get_typing_state(im) == PURPLE_IM_TYPED) {
atk_object_set_description(accessibility_obj, _("Stopped Typing"));
style = "tab-label-typed";
} else if (gtkconv->unseen_state == PIDGIN_UNSEEN_NICK) {
@@ -6831,7 +7619,7 @@ pidgin_conv_update_fields(PurpleConversation *conv, PidginConvFields fields)
style = "tab-label-attention";
} else if (gtkconv->unseen_state == PIDGIN_UNSEEN_TEXT) {
atk_object_set_description(accessibility_obj, _("Unread Messages"));
- if (gtkconv->active_conv->type == PURPLE_CONV_TYPE_CHAT)
+ if (PURPLE_IS_CHAT_CONVERSATION(gtkconv->active_conv))
style = "tab-label-unreadchat";
else
style = "tab-label-attention";
@@ -6865,7 +7653,7 @@ pidgin_conv_update_fields(PurpleConversation *conv, PidginConvFields fields)
gtk_label_set_text(GTK_LABEL(gtkconv->menu_label), title);
if (pidgin_conv_window_is_active_conversation(conv)) {
const char* current_title = gtk_window_get_title(GTK_WINDOW(win->window));
- if (current_title == NULL || strcmp(current_title, title) != 0)
+ if (current_title == NULL || g_strcmp0(current_title, title) != 0)
gtk_window_set_title(GTK_WINDOW(win->window), title);
}
@@ -6874,60 +7662,66 @@ pidgin_conv_update_fields(PurpleConversation *conv, PidginConvFields fields)
}
static void
-pidgin_conv_updated(PurpleConversation *conv, PurpleConvUpdateType type)
+pidgin_conv_updated(PurpleConversation *conv, PurpleConversationUpdateType type)
{
PidginConvFields flags = 0;
g_return_if_fail(conv != NULL);
- if (type == PURPLE_CONV_UPDATE_ACCOUNT)
+ if (type == PURPLE_CONVERSATION_UPDATE_ACCOUNT)
{
flags = PIDGIN_CONV_ALL;
}
- else if (type == PURPLE_CONV_UPDATE_TYPING ||
- type == PURPLE_CONV_UPDATE_UNSEEN ||
- type == PURPLE_CONV_UPDATE_TITLE)
+ else if (type == PURPLE_CONVERSATION_UPDATE_TYPING ||
+ type == PURPLE_CONVERSATION_UPDATE_UNSEEN ||
+ type == PURPLE_CONVERSATION_UPDATE_TITLE)
{
flags = PIDGIN_CONV_COLORIZE_TITLE;
}
- else if (type == PURPLE_CONV_UPDATE_TOPIC)
+ else if (type == PURPLE_CONVERSATION_UPDATE_TOPIC)
{
flags = PIDGIN_CONV_TOPIC;
}
- else if (type == PURPLE_CONV_ACCOUNT_ONLINE ||
- type == PURPLE_CONV_ACCOUNT_OFFLINE)
+ else if (type == PURPLE_CONVERSATION_ACCOUNT_ONLINE ||
+ type == PURPLE_CONVERSATION_ACCOUNT_OFFLINE)
{
flags = PIDGIN_CONV_MENU | PIDGIN_CONV_TAB_ICON | PIDGIN_CONV_SET_TITLE;
}
- else if (type == PURPLE_CONV_UPDATE_AWAY)
+ else if (type == PURPLE_CONVERSATION_UPDATE_AWAY)
{
flags = PIDGIN_CONV_TAB_ICON;
}
- else if (type == PURPLE_CONV_UPDATE_ADD ||
- type == PURPLE_CONV_UPDATE_REMOVE ||
- type == PURPLE_CONV_UPDATE_CHATLEFT)
+ else if (type == PURPLE_CONVERSATION_UPDATE_ADD ||
+ type == PURPLE_CONVERSATION_UPDATE_REMOVE ||
+ type == PURPLE_CONVERSATION_UPDATE_CHATLEFT)
{
flags = PIDGIN_CONV_SET_TITLE | PIDGIN_CONV_MENU;
}
- else if (type == PURPLE_CONV_UPDATE_ICON)
+ else if (type == PURPLE_CONVERSATION_UPDATE_ICON)
{
flags = PIDGIN_CONV_BUDDY_ICON;
}
- else if (type == PURPLE_CONV_UPDATE_FEATURES)
+ else if (type == PURPLE_CONVERSATION_UPDATE_FEATURES)
{
flags = PIDGIN_CONV_MENU;
}
+ else if (type == PURPLE_CONVERSATION_UPDATE_E2EE)
+ {
+ flags = PIDGIN_CONV_E2EE | PIDGIN_CONV_MENU;
+ }
pidgin_conv_update_fields(conv, flags);
}
static void
-wrote_msg_update_unseen_cb(PurpleAccount *account, const char *who, const char *message,
- PurpleConversation *conv, PurpleMessageFlags flags, gpointer null)
+wrote_msg_update_unseen_cb(PurpleConversation *conv, PurpleMessage *msg,
+ gpointer _unused)
{
PidginConversation *gtkconv = conv ? PIDGIN_CONVERSATION(conv) : NULL;
+ PurpleMessageFlags flags;
if (conv == NULL || (gtkconv && gtkconv->win != hidden_convwin))
return;
+ flags = purple_message_get_flags(msg);
if (flags & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV)) {
PidginUnseenState unseen = PIDGIN_UNSEEN_NONE;
@@ -6950,7 +7744,7 @@ static PurpleConversationUiOps conversation_ui_ops =
pidgin_conv_new,
pidgin_conv_destroy, /* destroy_conversation */
NULL, /* write_chat */
- pidgin_conv_write_im, /* write_im */
+ NULL, /* write_im */
pidgin_conv_write_conv, /* write_conv */
pidgin_conv_chat_add_users, /* chat_add_users */
pidgin_conv_chat_rename_user, /* chat_rename_user */
@@ -6958,9 +7752,6 @@ static PurpleConversationUiOps conversation_ui_ops =
pidgin_conv_chat_update_user, /* chat_update_user */
pidgin_conv_present_conversation, /* present */
pidgin_conv_has_focus, /* has_focus */
- pidgin_conv_custom_smiley_add, /* custom_smiley_add */
- pidgin_conv_custom_smiley_write, /* custom_smiley_write */
- pidgin_conv_custom_smiley_close, /* custom_smiley_close */
pidgin_conv_send_confirm, /* send_confirm */
NULL,
NULL,
@@ -6978,14 +7769,15 @@ pidgin_conversations_get_conv_ui_ops(void)
* Public conversation utility functions
**************************************************************************/
void
-pidgin_conv_update_buddy_icon(PurpleConversation *conv)
+pidgin_conv_update_buddy_icon(PurpleIMConversation *im)
{
PidginConversation *gtkconv;
- PidginWindow *win;
+ PurpleConversation *conv;
+ PidginConvWindow *win;
PurpleBuddy *buddy;
- PurpleStoredImage *custom_img = NULL;
+ PurpleImage *custom_img = NULL;
gconstpointer data = NULL;
size_t len;
@@ -7001,9 +7793,10 @@ pidgin_conv_update_buddy_icon(PurpleConversation *conv)
PurpleBuddyIcon *icon;
+ conv = PURPLE_CONVERSATION(im);
+
g_return_if_fail(conv != NULL);
g_return_if_fail(PIDGIN_IS_PIDGIN_CONVERSATION(conv));
- g_return_if_fail(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM);
gtkconv = PIDGIN_CONVERSATION(conv);
win = gtkconv->win;
@@ -7043,10 +7836,10 @@ pidgin_conv_update_buddy_icon(PurpleConversation *conv)
if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons"))
return;
- if (purple_conversation_get_gc(conv) == NULL)
+ if (purple_conversation_get_connection(conv) == NULL)
return;
- buddy = purple_find_buddy(account, purple_conversation_get_name(conv));
+ buddy = purple_blist_find_buddy(account, purple_conversation_get_name(conv));
if (buddy)
{
PurpleContact *contact = purple_buddy_get_contact(buddy);
@@ -7054,14 +7847,14 @@ pidgin_conv_update_buddy_icon(PurpleConversation *conv)
custom_img = purple_buddy_icons_node_find_custom_icon((PurpleBlistNode*)contact);
if (custom_img) {
/* There is a custom icon for this user */
- data = purple_imgstore_get_data(custom_img);
- len = purple_imgstore_get_size(custom_img);
+ data = purple_image_get_data(custom_img);
+ len = purple_image_get_size(custom_img);
}
}
}
if (data == NULL) {
- icon = purple_conv_im_get_icon(PURPLE_CONV_IM(conv));
+ icon = purple_im_conversation_get_icon(im);
if (icon == NULL)
{
gtk_widget_set_size_request(gtkconv->u.im->icon_container,
@@ -7079,7 +7872,8 @@ pidgin_conv_update_buddy_icon(PurpleConversation *conv)
}
gtkconv->u.im->anim = pidgin_pixbuf_anim_from_data(data, len);
- purple_imgstore_unref(custom_img);
+ if (custom_img)
+ g_object_unref(custom_img);
if (!gtkconv->u.im->anim) {
purple_debug_error("gtkconv", "Couldn't load icon for conv %s\n",
@@ -7129,7 +7923,7 @@ pidgin_conv_update_buddy_icon(PurpleConversation *conv)
gtk_container_add(GTK_CONTAINER(gtkconv->u.im->icon_container), event);
gtk_event_box_set_visible_window(GTK_EVENT_BOX(event), FALSE);
gtk_widget_add_events(event,
- GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
+ GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
g_signal_connect(G_OBJECT(event), "button-press-event",
G_CALLBACK(icon_menu), gtkconv);
@@ -7155,7 +7949,7 @@ pidgin_conv_update_buddy_icon(PurpleConversation *conv)
void
pidgin_conv_update_buttons_by_protocol(PurpleConversation *conv)
{
- PidginWindow *win;
+ PidginConvWindow *win;
if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv))
return;
@@ -7167,19 +7961,22 @@ pidgin_conv_update_buttons_by_protocol(PurpleConversation *conv)
}
static gboolean
-pidgin_conv_xy_to_right_infopane(PidginWindow *win, int x, int y)
+pidgin_conv_xy_to_right_infopane(PidginConvWindow *win, int x, int y)
{
gint pane_x, pane_y, x_rel;
PidginConversation *gtkconv;
+ GtkAllocation allocation;
- gdk_window_get_origin(win->notebook->window, &pane_x, &pane_y);
+ gdk_window_get_origin(gtk_widget_get_window(win->notebook),
+ &pane_x, &pane_y);
x_rel = x - pane_x;
gtkconv = pidgin_conv_window_get_active_gtkconv(win);
- return (x_rel > gtkconv->infopane->allocation.x + gtkconv->infopane->allocation.width / 2);
+ gtk_widget_get_allocation(gtkconv->infopane, &allocation);
+ return (x_rel > allocation.x + allocation.width / 2);
}
int
-pidgin_conv_get_tab_at_xy(PidginWindow *win, int x, int y, gboolean *to_right)
+pidgin_conv_get_tab_at_xy(PidginConvWindow *win, int x, int y, gboolean *to_right)
{
gint nb_x, nb_y, x_rel, y_rel;
GtkNotebook *notebook;
@@ -7193,7 +7990,7 @@ pidgin_conv_get_tab_at_xy(PidginWindow *win, int x, int y, gboolean *to_right)
notebook = GTK_NOTEBOOK(win->notebook);
- gdk_window_get_origin(win->notebook->window, &nb_x, &nb_y);
+ gdk_window_get_origin(gtk_widget_get_window(win->notebook), &nb_x, &nb_y);
x_rel = x - nb_x;
y_rel = y - nb_y;
@@ -7203,30 +8000,32 @@ pidgin_conv_get_tab_at_xy(PidginWindow *win, int x, int y, gboolean *to_right)
count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook));
for (i = 0; i < count; i++) {
+ GtkAllocation allocation;
page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), i);
tab = gtk_notebook_get_tab_label(GTK_NOTEBOOK(notebook), page);
+ gtk_widget_get_allocation(tab, &allocation);
/* Make sure the tab is not hidden beyond an arrow */
- if (!GTK_WIDGET_DRAWABLE(tab) && gtk_notebook_get_show_tabs(notebook))
+ if (!gtk_widget_is_drawable(tab) && gtk_notebook_get_show_tabs(notebook))
continue;
if (horiz) {
- if (x_rel >= tab->allocation.x - PIDGIN_HIG_BOX_SPACE &&
- x_rel <= tab->allocation.x + tab->allocation.width + PIDGIN_HIG_BOX_SPACE) {
+ if (x_rel >= allocation.x - PIDGIN_HIG_BOX_SPACE &&
+ x_rel <= allocation.x + allocation.width + PIDGIN_HIG_BOX_SPACE) {
page_num = i;
- if (to_right && x_rel >= tab->allocation.x + tab->allocation.width/2)
+ if (to_right && x_rel >= allocation.x + allocation.width/2)
*to_right = TRUE;
break;
}
} else {
- if (y_rel >= tab->allocation.y - PIDGIN_HIG_BOX_SPACE &&
- y_rel <= tab->allocation.y + tab->allocation.height + PIDGIN_HIG_BOX_SPACE) {
+ if (y_rel >= allocation.y - PIDGIN_HIG_BOX_SPACE &&
+ y_rel <= allocation.y + allocation.height + PIDGIN_HIG_BOX_SPACE) {
page_num = i;
- if (to_right && y_rel >= tab->allocation.y + tab->allocation.height/2)
+ if (to_right && y_rel >= allocation.y + allocation.height/2)
*to_right = TRUE;
break;
@@ -7250,7 +8049,7 @@ close_on_tabs_pref_cb(const char *name, PurplePrefType type,
PurpleConversation *conv;
PidginConversation *gtkconv;
- for (l = purple_get_conversations(); l != NULL; l = l->next) {
+ for (l = purple_conversations_get_all(); l != NULL; l = l->next) {
conv = (PurpleConversation *)l->data;
if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv))
@@ -7269,13 +8068,11 @@ static void
spellcheck_pref_cb(const char *name, PurplePrefType type,
gconstpointer value, gpointer data)
{
-#ifdef USE_GTKSPELL
GList *cl;
PurpleConversation *conv;
PidginConversation *gtkconv;
- GtkSpell *spell;
- for (cl = purple_get_conversations(); cl != NULL; cl = cl->next) {
+ for (cl = purple_conversations_get_all(); cl != NULL; cl = cl->next) {
conv = (PurpleConversation *)cl->data;
@@ -7284,15 +8081,9 @@ spellcheck_pref_cb(const char *name, PurplePrefType type,
gtkconv = PIDGIN_CONVERSATION(conv);
- if (value)
- pidgin_setup_gtkspell(GTK_TEXT_VIEW(gtkconv->entry));
- else {
- spell = gtkspell_get_from_text_view(GTK_TEXT_VIEW(gtkconv->entry));
- if (spell)
- gtkspell_detach(spell);
- }
+ pidgin_webview_set_spellcheck(PIDGIN_WEBVIEW(gtkconv->entry),
+ (gboolean)GPOINTER_TO_INT(value));
}
-#endif
}
static void
@@ -7301,7 +8092,7 @@ tab_side_pref_cb(const char *name, PurplePrefType type,
{
GList *gtkwins, *gtkconvs;
GtkPositionType pos;
- PidginWindow *gtkwin;
+ PidginConvWindow *gtkwin;
pos = GPOINTER_TO_INT(value);
@@ -7315,43 +8106,15 @@ tab_side_pref_cb(const char *name, PurplePrefType type,
}
static void
-show_timestamps_pref_cb(const char *name, PurplePrefType type,
- gconstpointer value, gpointer data)
-{
- GList *l;
- PurpleConversation *conv;
- PidginConversation *gtkconv;
- PidginWindow *win;
-
- for (l = purple_get_conversations(); l != NULL; l = l->next)
- {
- conv = (PurpleConversation *)l->data;
-
- if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv))
- continue;
-
- gtkconv = PIDGIN_CONVERSATION(conv);
- win = gtkconv->win;
-
- gtk_check_menu_item_set_active(
- GTK_CHECK_MENU_ITEM(win->menu.show_timestamps),
- (gboolean)GPOINTER_TO_INT(value));
-
- gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),
- (gboolean)GPOINTER_TO_INT(value));
- }
-}
-
-static void
show_formatting_toolbar_pref_cb(const char *name, PurplePrefType type,
gconstpointer value, gpointer data)
{
GList *l;
PurpleConversation *conv;
PidginConversation *gtkconv;
- PidginWindow *win;
+ PidginConvWindow *win;
- for (l = purple_get_conversations(); l != NULL; l = l->next)
+ for (l = purple_conversations_get_all(); l != NULL; l = l->next)
{
conv = (PurpleConversation *)l->data;
@@ -7361,16 +8124,16 @@ show_formatting_toolbar_pref_cb(const char *name, PurplePrefType type,
gtkconv = PIDGIN_CONVERSATION(conv);
win = gtkconv->win;
- gtk_check_menu_item_set_active(
- GTK_CHECK_MENU_ITEM(win->menu.show_formatting_toolbar),
+ gtk_toggle_action_set_active(
+ GTK_TOGGLE_ACTION(win->menu->show_formatting_toolbar),
(gboolean)GPOINTER_TO_INT(value));
if ((gboolean)GPOINTER_TO_INT(value))
- gtk_widget_show(gtkconv->toolbar);
+ pidgin_webview_show_toolbar(PIDGIN_WEBVIEW(gtkconv->entry));
else
- gtk_widget_hide(gtkconv->toolbar);
+ pidgin_webview_hide_toolbar(PIDGIN_WEBVIEW(gtkconv->entry));
- g_idle_add((GSourceFunc)resize_imhtml_cb,gtkconv);
+ g_idle_add((GSourceFunc)resize_webview_cb, gtkconv);
}
}
@@ -7381,13 +8144,13 @@ animate_buddy_icons_pref_cb(const char *name, PurplePrefType type,
GList *l;
PurpleConversation *conv;
PidginConversation *gtkconv;
- PidginWindow *win;
+ PidginConvWindow *win;
if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons"))
return;
/* Set the "animate" flag for each icon based on the new preference */
- for (l = purple_get_ims(); l != NULL; l = l->next) {
+ for (l = purple_conversations_get_ims(); l != NULL; l = l->next) {
conv = (PurpleConversation *)l->data;
gtkconv = PIDGIN_CONVERSATION(conv);
if (gtkconv)
@@ -7398,7 +8161,7 @@ animate_buddy_icons_pref_cb(const char *name, PurplePrefType type,
for (l = pidgin_conv_windows_get_list(); l != NULL; l = l->next) {
win = l->data;
conv = pidgin_conv_window_get_active_conversation(win);
- pidgin_conv_update_buddy_icon(conv);
+ pidgin_conv_update_buddy_icon(PURPLE_IM_CONVERSATION(conv));
}
}
@@ -7408,7 +8171,7 @@ show_buddy_icons_pref_cb(const char *name, PurplePrefType type,
{
GList *l;
- for (l = purple_get_conversations(); l != NULL; l = l->next) {
+ for (l = purple_conversations_get_all(); l != NULL; l = l->next) {
PurpleConversation *conv = l->data;
if (!PIDGIN_CONVERSATION(conv))
continue;
@@ -7417,14 +8180,14 @@ show_buddy_icons_pref_cb(const char *name, PurplePrefType type,
else
gtk_widget_hide(PIDGIN_CONVERSATION(conv)->infopane_hbox);
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
- pidgin_conv_update_buddy_icon(conv);
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
+ pidgin_conv_update_buddy_icon(PURPLE_IM_CONVERSATION(conv));
}
}
/* Make the tabs show/hide correctly */
for (l = pidgin_conv_windows_get_list(); l != NULL; l = l->next) {
- PidginWindow *win = l->data;
+ PidginConvWindow *win = l->data;
if (pidgin_conv_window_get_gtkconv_count(win) == 1)
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook),
GPOINTER_TO_INT(value) == 0);
@@ -7436,7 +8199,7 @@ show_protocol_icons_pref_cb(const char *name, PurplePrefType type,
gconstpointer value, gpointer data)
{
GList *l;
- for (l = purple_get_conversations(); l != NULL; l = l->next) {
+ for (l = purple_conversations_get_all(); l != NULL; l = l->next) {
PurpleConversation *conv = l->data;
if (PIDGIN_CONVERSATION(conv))
update_tab_icon(conv);
@@ -7469,7 +8232,7 @@ account_status_changed_cb(PurpleAccount *account, PurpleStatus *oldstatus,
l = l->next;
conv = gtkconv->active_conv;
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT ||
+ if (PURPLE_IS_CHAT_CONVERSATION(conv) ||
account != purple_conversation_get_account(conv))
continue;
@@ -7477,7 +8240,7 @@ account_status_changed_cb(PurpleAccount *account, PurpleStatus *oldstatus,
/* TODO: do we need to do anything for any other conversations that are in the same gtkconv here?
* I'm a little concerned that not doing so will cause the "pending" indicator in the gtkblist not to be cleared. -DAA*/
- purple_conversation_update(conv, PURPLE_CONV_UPDATE_UNSEEN);
+ purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_UNSEEN);
}
}
@@ -7506,7 +8269,7 @@ hide_new_pref_cb(const char *name, PurplePrefType type,
conv = gtkconv->active_conv;
- if (conv->type == PURPLE_CONV_TYPE_CHAT ||
+ if (PURPLE_IS_CHAT_CONVERSATION(conv) ||
gtkconv->unseen_count == 0 ||
(when_away && !purple_status_is_available(
purple_account_get_active_status(
@@ -7545,10 +8308,10 @@ get_gtkconv_with_contact(PurpleContact *contact)
for (; node; node = node->next)
{
PurpleBuddy *buddy = (PurpleBuddy*)node;
- PurpleConversation *conv;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy->name, buddy->account);
- if (conv)
- return PIDGIN_CONVERSATION(conv);
+ PurpleIMConversation *im;
+ im = purple_conversations_find_im_with_account(purple_buddy_get_name(buddy), purple_buddy_get_account(buddy));
+ if (im)
+ return PIDGIN_CONVERSATION(PURPLE_CONVERSATION(im));
}
return NULL;
}
@@ -7558,7 +8321,7 @@ account_signed_off_cb(PurpleConnection *gc, gpointer event)
{
GList *iter;
- for (iter = purple_get_conversations(); iter; iter = iter->next)
+ for (iter = purple_conversations_get_all(); iter; iter = iter->next)
{
PurpleConversation *conv = iter->data;
@@ -7571,18 +8334,20 @@ account_signed_off_cb(PurpleConnection *gc, gpointer event)
PIDGIN_CONV_MENU | PIDGIN_CONV_COLORIZE_TITLE);
if (PURPLE_CONNECTION_IS_CONNECTED(gc) &&
- conv->type == PURPLE_CONV_TYPE_CHAT &&
- conv->account == gc->account &&
- purple_conversation_get_data(conv, "want-to-rejoin")) {
+ PURPLE_IS_CHAT_CONVERSATION(conv) &&
+ purple_conversation_get_account(conv) == purple_connection_get_account(gc) &&
+ g_object_get_data(G_OBJECT(conv), "want-to-rejoin")) {
GHashTable *comps = NULL;
- PurpleChat *chat = purple_blist_find_chat(conv->account, conv->name);
+ PurpleChat *chat = purple_blist_find_chat(purple_conversation_get_account(conv), purple_conversation_get_name(conv));
if (chat == NULL) {
- if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults != NULL)
- comps = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults(gc, conv->name);
+ PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
+
+ if (prpl_info->chat_info_defaults != NULL)
+ comps = prpl_info->chat_info_defaults(gc, purple_conversation_get_name(conv));
} else {
- comps = chat->components;
+ comps = purple_chat_get_components(chat);
}
- serv_join_chat(gc, comps);
+ purple_serv_join_chat(gc, comps);
if (chat == NULL && comps != NULL)
g_hash_table_destroy(comps);
}
@@ -7592,20 +8357,21 @@ account_signed_off_cb(PurpleConnection *gc, gpointer event)
static void
account_signing_off(PurpleConnection *gc)
{
- GList *list = purple_get_chats();
+ GList *list = purple_conversations_get_chats();
PurpleAccount *account = purple_connection_get_account(gc);
/* We are about to sign off. See which chats we are currently in, and mark
* them for rejoin on reconnect. */
while (list) {
PurpleConversation *conv = list->data;
- if (!purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)) &&
+ if (!purple_chat_conversation_has_left(PURPLE_CHAT_CONVERSATION(conv)) &&
purple_conversation_get_account(conv) == account) {
- purple_conversation_set_data(conv, "want-to-rejoin", GINT_TO_POINTER(TRUE));
- purple_conversation_write(conv, NULL, _("The account has disconnected and you are no "
- "longer in this chat. You will automatically rejoin the chat when "
- "the account reconnects."),
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ g_object_set_data(G_OBJECT(conv), "want-to-rejoin", GINT_TO_POINTER(TRUE));
+ purple_conversation_write_system_message(conv,
+ _("The account has disconnected and you are no "
+ "longer in this chat. You will automatically "
+ "rejoin the chat when the account reconnects."),
+ PURPLE_MESSAGE_NO_LOG);
}
list = list->next;
}
@@ -7645,21 +8411,21 @@ update_buddy_privacy_changed(PurpleBuddy *buddy)
static void
update_buddy_idle_changed(PurpleBuddy *buddy, gboolean old, gboolean newidle)
{
- PurpleConversation *conv;
+ PurpleIMConversation *im;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy->name, buddy->account);
- if (conv)
- pidgin_conv_update_fields(conv, PIDGIN_CONV_TAB_ICON);
+ im = purple_conversations_find_im_with_account(purple_buddy_get_name(buddy), purple_buddy_get_account(buddy));
+ if (im)
+ pidgin_conv_update_fields(PURPLE_CONVERSATION(im), PIDGIN_CONV_TAB_ICON);
}
static void
update_buddy_icon(PurpleBuddy *buddy)
{
- PurpleConversation *conv;
+ PurpleIMConversation *im;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy->name, buddy->account);
- if (conv)
- pidgin_conv_update_fields(conv, PIDGIN_CONV_BUDDY_ICON);
+ im = purple_conversations_find_im_with_account(purple_buddy_get_name(buddy), purple_buddy_get_account(buddy));
+ if (im)
+ pidgin_conv_update_fields(PURPLE_CONVERSATION(im), PIDGIN_CONV_BUDDY_ICON);
}
static void
@@ -7683,8 +8449,9 @@ update_buddy_sign(PurpleBuddy *buddy, const char *which)
static void
update_conversation_switched(PurpleConversation *conv)
{
- pidgin_conv_update_fields(conv, PIDGIN_CONV_TAB_ICON | PIDGIN_CONV_SET_TITLE |
- PIDGIN_CONV_MENU | PIDGIN_CONV_BUDDY_ICON);
+ pidgin_conv_update_fields(conv, PIDGIN_CONV_TAB_ICON |
+ PIDGIN_CONV_SET_TITLE | PIDGIN_CONV_MENU |
+ PIDGIN_CONV_BUDDY_ICON | PIDGIN_CONV_E2EE );
}
static void
@@ -7693,7 +8460,7 @@ update_buddy_typing(PurpleAccount *account, const char *who)
PurpleConversation *conv;
PidginConversation *gtkconv;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, account);
+ conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(who, account));
if (!conv)
return;
@@ -7703,26 +8470,26 @@ update_buddy_typing(PurpleAccount *account, const char *who)
}
static void
-update_chat(PurpleConversation *conv)
+update_chat(PurpleChatConversation *chat)
{
- pidgin_conv_update_fields(conv, PIDGIN_CONV_TOPIC |
+ pidgin_conv_update_fields(PURPLE_CONVERSATION(chat), PIDGIN_CONV_TOPIC |
PIDGIN_CONV_MENU | PIDGIN_CONV_SET_TITLE);
}
static void
-update_chat_topic(PurpleConversation *conv, const char *old, const char *new)
+update_chat_topic(PurpleChatConversation *chat, const char *old, const char *new)
{
- pidgin_conv_update_fields(conv, PIDGIN_CONV_TOPIC);
+ pidgin_conv_update_fields(PURPLE_CONVERSATION(chat), PIDGIN_CONV_TOPIC);
}
/* Message history stuff */
-/* Compare two PurpleConvMessage's, according to time in ascending order. */
+/* Compare two PurpleMessages, according to time in ascending order. */
static int
message_compare(gconstpointer p1, gconstpointer p2)
{
- const PurpleConvMessage *m1 = p1, *m2 = p2;
- return (m1->when > m2->when);
+ const PurpleMessage *m1 = p1, *m2 = p2;
+ return (purple_message_get_time(m1) > purple_message_get_time(m2));
}
/* Adds some message history to the gtkconv. This happens in a idle-callback. */
@@ -7730,32 +8497,35 @@ static gboolean
add_message_history_to_gtkconv(gpointer data)
{
PidginConversation *gtkconv = data;
+ PidginWebView *webview = PIDGIN_WEBVIEW(gtkconv->webview);
int count = 0;
- int timer = gtkconv->attach.timer;
+ int timer = gtkconv->attach_timer;
time_t when = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(gtkconv->entry), "attach-start-time"));
- gboolean im = (gtkconv->active_conv->type == PURPLE_CONV_TYPE_IM);
-
- gtkconv->attach.timer = 0;
- while (gtkconv->attach.current && count < 100) { /* XXX: 100 is a random value here */
- PurpleConvMessage *msg = gtkconv->attach.current->data;
- if (!im && when && when < msg->when) {
- gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR><HR>", 0);
+ 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);
}
- pidgin_conv_write_conv(msg->conv, msg->who, msg->alias, msg->what, msg->flags, msg->when);
+ /* XXX: should it be gtkconv->active_conv? */
+ pidgin_conv_write_conv(gtkconv->active_conv, msg);
if (im) {
- gtkconv->attach.current = g_list_delete_link(gtkconv->attach.current, gtkconv->attach.current);
+ gtkconv->attach_current = g_list_delete_link(gtkconv->attach_current, gtkconv->attach_current);
} else {
- gtkconv->attach.current = gtkconv->attach.current->prev;
+ gtkconv->attach_current = gtkconv->attach_current->prev;
}
count++;
}
- gtkconv->attach.timer = timer;
- if (gtkconv->attach.current)
+ gtkconv->attach_timer = timer;
+ if (gtkconv->attach_current)
return TRUE;
- g_source_remove(gtkconv->attach.timer);
- gtkconv->attach.timer = 0;
+ g_source_remove(gtkconv->attach_timer);
+ gtkconv->attach_timer = 0;
if (im) {
/* Print any message that was sent while the old history was being added back. */
GList *msgs = NULL;
@@ -7764,17 +8534,19 @@ add_message_history_to_gtkconv(gpointer data)
PurpleConversation *conv = iter->data;
GList *history = purple_conversation_get_message_history(conv);
for (; history; history = history->next) {
- PurpleConvMessage *msg = history->data;
- if (msg->when > when)
+ PurpleMessage *msg = history->data;
+ if (purple_message_get_time(msg) > (guint64)when)
msgs = g_list_prepend(msgs, msg);
}
}
msgs = g_list_sort(msgs, message_compare);
for (; msgs; msgs = g_list_delete_link(msgs, msgs)) {
- PurpleConvMessage *msg = msgs->data;
- pidgin_conv_write_conv(msg->conv, msg->who, msg->alias, msg->what, msg->flags, msg->when);
+ PurpleMessage *msg = msgs->data;
+ /* XXX: see above - should it be active_conv? */
+ pidgin_conv_write_conv(gtkconv->active_conv, msg);
}
- gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR><HR>", 0);
+ 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);
}
@@ -7788,15 +8560,15 @@ static void
pidgin_conv_attach(PurpleConversation *conv)
{
int timer;
- purple_conversation_set_data(conv, "unseen-count", NULL);
- purple_conversation_set_data(conv, "unseen-state", NULL);
+ g_object_set_data(G_OBJECT(conv), "unseen-count", NULL);
+ g_object_set_data(G_OBJECT(conv), "unseen-state", NULL);
purple_conversation_set_ui_ops(conv, pidgin_conversations_get_conv_ui_ops());
if (!PIDGIN_CONVERSATION(conv))
private_gtkconv_new(conv, FALSE);
- timer = GPOINTER_TO_INT(purple_conversation_get_data(conv, "close-timer"));
+ timer = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "close-timer"));
if (timer) {
purple_timeout_remove(timer);
- purple_conversation_set_data(conv, "close-timer", NULL);
+ g_object_set_data(G_OBJECT(conv), "close-timer", NULL);
}
}
@@ -7827,44 +8599,48 @@ gboolean pidgin_conv_attach_to_conversation(PurpleConversation *conv)
list = purple_conversation_get_message_history(conv);
if (list) {
- switch (purple_conversation_get_type(conv)) {
- case PURPLE_CONV_TYPE_IM:
- {
- GList *convs;
- list = g_list_copy(list);
- for (convs = purple_get_ims(); convs; convs = convs->next)
- if (convs->data != conv &&
- pidgin_conv_find_gtkconv(convs->data) == gtkconv) {
- 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);
- gtkconv->attach.current = list;
- list = g_list_last(list);
- break;
- }
- case PURPLE_CONV_TYPE_CHAT:
- gtkconv->attach.current = g_list_last(list);
- break;
- default:
- g_return_val_if_reached(TRUE);
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
+ GList *convs;
+ list = g_list_copy(list);
+ for (convs = purple_conversations_get_ims(); convs; convs = convs->next)
+ if (convs->data != conv &&
+ pidgin_conv_find_gtkconv(convs->data) == gtkconv) {
+ 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);
+ 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",
- GINT_TO_POINTER(((PurpleConvMessage*)(list->data))->when));
- gtkconv->attach.timer = g_idle_add(add_message_history_to_gtkconv, gtkconv);
+ GINT_TO_POINTER(purple_message_get_time(list->data)));
+ gtkconv->attach_timer = g_idle_add(add_message_history_to_gtkconv, gtkconv);
} else {
purple_signal_emit(pidgin_conversations_get_handle(),
"conversation-displayed", gtkconv);
}
- if (conv->type == PURPLE_CONV_TYPE_CHAT) {
+ if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
+ GList *users;
+ PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(conv);
pidgin_conv_update_fields(conv, PIDGIN_CONV_TOPIC);
- pidgin_conv_chat_add_users(conv, PURPLE_CONV_CHAT(conv)->in_room, TRUE);
+ users = purple_chat_conversation_get_users(chat);
+ pidgin_conv_chat_add_users(chat, users, TRUE);
+ g_list_free(users);
}
return TRUE;
}
+PurpleTheme *
+pidgin_conversations_get_default_theme(void)
+{
+ return default_conv_theme;
+}
+
void *
pidgin_conversations_get_handle(void)
{
@@ -7873,26 +8649,39 @@ pidgin_conversations_get_handle(void)
return &handle;
}
+static void
+pidgin_conversations_pre_uninit(void);
+
void
pidgin_conversations_init(void)
{
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);
+
+ image_store_tag_re = g_regex_new("(<img [^>]*src=\")("
+ PURPLE_IMAGE_STORE_PROTOCOL "[0-9]+)(\"[^>]*>)",
+ G_REGEX_OPTIMIZE | G_REGEX_DOTALL, 0, NULL);
/* 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);
purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic", FALSE);
purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline", FALSE);
+ purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/send_strike", FALSE);
purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/spellcheck", TRUE);
purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting", TRUE);
+ /* TODO: it's about *remote* smileys, not local ones */
purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/resize_custom_smileys", TRUE);
purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/custom_smileys_size", 96);
purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/minimum_entry_lines", 2);
- purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/show_timestamps", TRUE);
purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/show_formatting_toolbar", TRUE);
purple_prefs_add_string(PIDGIN_PREFS_ROOT "/conversations/placement", "last");
@@ -7941,8 +8730,6 @@ pidgin_conversations_init(void)
/* Connect callbacks. */
purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/conversations/close_on_tabs",
close_on_tabs_pref_cb, NULL);
- purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/conversations/show_timestamps",
- show_timestamps_pref_cb, NULL);
purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/conversations/show_formatting_toolbar",
show_formatting_toolbar_pref_cb, NULL);
purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/conversations/spellcheck",
@@ -7968,19 +8755,15 @@ pidgin_conversations_init(void)
purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/blist/show_protocol_icons",
show_protocol_icons_pref_cb, NULL);
purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/conversations/im/hide_new",
- hide_new_pref_cb, NULL);
-
-
+ hide_new_pref_cb, NULL);
/**********************************************************************
* Register signals
**********************************************************************/
purple_signal_register(handle, "conversation-dragging",
- purple_marshal_VOID__POINTER_POINTER, NULL, 2,
- purple_value_new(PURPLE_TYPE_BOXED,
- "PidginWindow *"),
- purple_value_new(PURPLE_TYPE_BOXED,
- "PidginWindow *"));
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ G_TYPE_POINTER, /* pointer to a (PidginConvWindow *) */
+ G_TYPE_POINTER); /* pointer to a (PidginConvWindow *) */
purple_signal_register(handle, "conversation-timestamp",
#if SIZEOF_TIME_T == 4
@@ -7990,90 +8773,56 @@ pidgin_conversations_init(void)
#else
#error Unkown size of time_t
#endif
- purple_value_new(PURPLE_TYPE_STRING), 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
+ G_TYPE_STRING, 3, PURPLE_TYPE_CONVERSATION,
#if SIZEOF_TIME_T == 4
- purple_value_new(PURPLE_TYPE_INT),
+ G_TYPE_INT,
#elif SIZEOF_TIME_T == 8
- purple_value_new(PURPLE_TYPE_INT64),
+ G_TYPE_INT64,
#else
# error Unknown size of time_t
#endif
- purple_value_new(PURPLE_TYPE_BOOLEAN));
+ G_TYPE_BOOLEAN);
purple_signal_register(handle, "displaying-im-msg",
- purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
- purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new_outgoing(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_INT));
+ purple_marshal_BOOLEAN__POINTER_POINTER,
+ G_TYPE_BOOLEAN, 2, PURPLE_TYPE_CONVERSATION, PURPLE_TYPE_MESSAGE);
purple_signal_register(handle, "displayed-im-msg",
- purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT,
- NULL, 5,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_INT));
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ PURPLE_TYPE_CONVERSATION, PURPLE_TYPE_MESSAGE);
purple_signal_register(handle, "displaying-chat-msg",
- purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
- purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new_outgoing(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_INT));
+ purple_marshal_BOOLEAN__POINTER_POINTER,
+ G_TYPE_BOOLEAN, 2, PURPLE_TYPE_CONVERSATION, PURPLE_TYPE_MESSAGE);
purple_signal_register(handle, "displayed-chat-msg",
- purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT,
- NULL, 5,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_ACCOUNT),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_INT));
+ purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2,
+ PURPLE_TYPE_CONVERSATION, PURPLE_TYPE_MESSAGE);
purple_signal_register(handle, "conversation-switched",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION));
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ PURPLE_TYPE_CONVERSATION);
purple_signal_register(handle, "conversation-hiding",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_BOXED,
- "PidginConversation *"));
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ G_TYPE_POINTER); /* (PidginConversation *) */
purple_signal_register(handle, "conversation-displayed",
- purple_marshal_VOID__POINTER, NULL, 1,
- purple_value_new(PURPLE_TYPE_BOXED,
- "PidginConversation *"));
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ G_TYPE_POINTER); /* (PidginConversation *) */
purple_signal_register(handle, "chat-nick-autocomplete",
purple_marshal_BOOLEAN__POINTER_BOOLEAN,
- purple_value_new(PURPLE_TYPE_BOOLEAN), 1,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION));
+ G_TYPE_BOOLEAN, 1, PURPLE_TYPE_CONVERSATION);
purple_signal_register(handle, "chat-nick-clicked",
purple_marshal_BOOLEAN__POINTER_POINTER_UINT,
- purple_value_new(PURPLE_TYPE_BOOLEAN), 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_CONVERSATION),
- purple_value_new(PURPLE_TYPE_STRING),
- purple_value_new(PURPLE_TYPE_UINT));
+ G_TYPE_BOOLEAN, 3, PURPLE_TYPE_CONVERSATION,
+ G_TYPE_STRING, G_TYPE_UINT);
+
+ purple_signal_register(handle, "conversation-window-created",
+ purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+ G_TYPE_POINTER); /* (PidginConvWindow *) */
/**********************************************************************
@@ -8104,20 +8853,24 @@ pidgin_conversations_init(void)
purple_signal_connect(purple_connections_get_handle(), "signed-on", handle,
G_CALLBACK(account_signed_off_cb),
- GINT_TO_POINTER(PURPLE_CONV_ACCOUNT_ONLINE));
+ GINT_TO_POINTER(PURPLE_CONVERSATION_ACCOUNT_ONLINE));
purple_signal_connect(purple_connections_get_handle(), "signed-off", handle,
G_CALLBACK(account_signed_off_cb),
- GINT_TO_POINTER(PURPLE_CONV_ACCOUNT_OFFLINE));
+ GINT_TO_POINTER(PURPLE_CONVERSATION_ACCOUNT_OFFLINE));
purple_signal_connect(purple_connections_get_handle(), "signing-off", handle,
G_CALLBACK(account_signing_off), NULL);
+ purple_signal_connect(purple_conversations_get_handle(), "writing-im-msg",
+ handle, G_CALLBACK(writing_msg), NULL);
+ purple_signal_connect(purple_conversations_get_handle(), "writing-chat-msg",
+ handle, G_CALLBACK(writing_msg), NULL);
purple_signal_connect(purple_conversations_get_handle(), "received-im-msg",
handle, G_CALLBACK(received_im_msg_cb), NULL);
purple_signal_connect(purple_conversations_get_handle(), "cleared-message-history",
handle, G_CALLBACK(clear_conversation_scrollback_cb), NULL);
- purple_signal_connect(purple_conversations_get_handle(), "deleting-chat-buddy",
- handle, G_CALLBACK(deleting_chat_buddy_cb), NULL);
+ purple_signal_connect(purple_conversations_get_handle(), "deleting-chat-user",
+ handle, G_CALLBACK(deleting_chat_user_cb), NULL);
purple_conversations_set_ui_ops(&conversation_ui_ops);
@@ -8125,7 +8878,10 @@ pidgin_conversations_init(void)
window_list = g_list_remove(window_list, hidden_convwin);
purple_signal_connect(purple_accounts_get_handle(), "account-status-changed",
- handle, PURPLE_CALLBACK(account_status_changed_cb), NULL);
+ handle, PURPLE_CALLBACK(account_status_changed_cb), NULL);
+
+ purple_signal_connect_priority(purple_get_core(), "quitting", handle,
+ PURPLE_CALLBACK(pidgin_conversations_pre_uninit), NULL, PURPLE_SIGNAL_PRIORITY_HIGHEST);
/* Callbacks to update a conversation */
purple_signal_connect(blist_handle, "blist-node-added", handle,
@@ -8164,6 +8920,16 @@ pidgin_conversations_init(void)
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));
+#if defined(_WIN32) && !defined(USE_WIN32_FHS)
+ theme_dir = g_build_filename(PURPLE_DATADIR, "theme", NULL);
+#else
+ theme_dir = g_build_filename(PURPLE_DATADIR, "pidgin", "theme", NULL);
+#endif
+ default_conv_theme = purple_theme_manager_load_theme(theme_dir, "conversation");
+ g_free(theme_dir);
+
+#if !GTK_CHECK_VERSION(3,0,0)
{
/* Set default tab colors */
GString *str = g_string_new(NULL);
@@ -8199,6 +8965,14 @@ pidgin_conversations_init(void)
g_string_free(str, TRUE);
gtk_rc_reset_styles(settings);
}
+#endif
+}
+
+static void
+pidgin_conversations_pre_uninit(void)
+{
+ g_hash_table_destroy(e2ee_stock);
+ e2ee_stock = NULL;
}
void
@@ -8207,6 +8981,46 @@ pidgin_conversations_uninit(void)
purple_prefs_disconnect_by_handle(pidgin_conversations_get_handle());
purple_signals_disconnect_by_handle(pidgin_conversations_get_handle());
purple_signals_unregister_by_instance(pidgin_conversations_get_handle());
+
+ g_regex_unref(image_store_tag_re);
+ image_store_tag_re = NULL;
+}
+
+/**************************************************************************
+ * PidginConversation GBoxed code
+ **************************************************************************/
+static PidginConversation *
+pidgin_conversation_ref(PidginConversation *gtkconv)
+{
+ g_return_val_if_fail(gtkconv != NULL, NULL);
+
+ gtkconv->box_count++;
+
+ return gtkconv;
+}
+
+static void
+pidgin_conversation_unref(PidginConversation *gtkconv)
+{
+ g_return_if_fail(gtkconv != NULL);
+ g_return_if_fail(gtkconv->box_count >= 0);
+
+ if (!gtkconv->box_count--)
+ pidgin_conv_destroy(gtkconv->active_conv);
+}
+
+GType
+pidgin_conversation_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PidginConversation",
+ (GBoxedCopyFunc)pidgin_conversation_ref,
+ (GBoxedFreeFunc)pidgin_conversation_unref);
+ }
+
+ return type;
}
@@ -8227,11 +9041,7 @@ pidgin_conversations_uninit(void)
/* down here is where gtkconvwin.c ought to start. except they share like every freaking function,
* and touch each others' private members all day long */
-/**
- * @file gtkconvwin.c GTK+ Conversation Window API
- * @ingroup pidgin
- *
- * pidgin
+/* 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
@@ -8261,7 +9071,6 @@ pidgin_conversations_uninit(void)
#include "account.h"
#include "cmds.h"
#include "debug.h"
-#include "imgstore.h"
#include "log.h"
#include "notify.h"
#include "prpl.h"
@@ -8278,11 +9087,9 @@ pidgin_conversations_uninit(void)
#include "gtkprivacy.h"
#include "gtkutils.h"
#include "pidginstock.h"
-#include "gtkimhtml.h"
-#include "gtkimhtmltoolbar.h"
static void
-do_close(GtkWidget *w, int resp, PidginWindow *win)
+do_close(GtkWidget *w, int resp, PidginConvWindow *win)
{
gtk_widget_destroy(warn_close_dialog);
warn_close_dialog = NULL;
@@ -8292,7 +9099,7 @@ do_close(GtkWidget *w, int resp, PidginWindow *win)
}
static void
-build_warn_close_dialog(PidginWindow *gtkwin)
+build_warn_close_dialog(PidginConvWindow *gtkwin)
{
GtkWidget *label, *vbox, *hbox, *img;
@@ -8309,11 +9116,9 @@ build_warn_close_dialog(PidginWindow *gtkwin)
gtk_container_set_border_width(GTK_CONTAINER(warn_close_dialog),
6);
gtk_window_set_resizable(GTK_WINDOW(warn_close_dialog), FALSE);
- gtk_dialog_set_has_separator(GTK_DIALOG(warn_close_dialog),
- FALSE);
/* Setup the outside spacing. */
- vbox = GTK_DIALOG(warn_close_dialog)->vbox;
+ vbox = gtk_dialog_get_content_area(GTK_DIALOG(warn_close_dialog));
gtk_box_set_spacing(GTK_BOX(vbox), 12);
gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
@@ -8321,13 +9126,13 @@ build_warn_close_dialog(PidginWindow *gtkwin)
img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_WARNING,
gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
/* Setup the inner hbox and put the dialog's icon in it. */
- hbox = gtk_hbox_new(FALSE, 12);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
gtk_container_add(GTK_CONTAINER(vbox), hbox);
gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
/* Setup the right vbox. */
- vbox = gtk_vbox_new(FALSE, 12);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
gtk_container_add(GTK_CONTAINER(hbox), vbox);
label = gtk_label_new(_("You have unread messages. Are you sure you want to close the window?"));
@@ -8349,7 +9154,7 @@ build_warn_close_dialog(PidginWindow *gtkwin)
static gboolean
close_win_cb(GtkWidget *w, GdkEventAny *e, gpointer d)
{
- PidginWindow *win = d;
+ PidginConvWindow *win = d;
GList *l;
/* If there are unread messages then show a warning dialog */
@@ -8357,7 +9162,7 @@ close_win_cb(GtkWidget *w, GdkEventAny *e, gpointer d)
l != NULL; l = l->next)
{
PidginConversation *gtkconv = l->data;
- if (purple_conversation_get_type(gtkconv->active_conv) == PURPLE_CONV_TYPE_IM &&
+ if (PURPLE_IS_IM_CONVERSATION(gtkconv->active_conv) &&
gtkconv->unseen_state >= PIDGIN_UNSEEN_TEXT)
{
build_warn_close_dialog(win);
@@ -8378,11 +9183,11 @@ conv_set_unseen(PurpleConversation *conv, PidginUnseenState state)
int unseen_count = 0;
PidginUnseenState unseen_state = PIDGIN_UNSEEN_NONE;
- if(purple_conversation_get_data(conv, "unseen-count"))
- unseen_count = GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count"));
+ if(g_object_get_data(G_OBJECT(conv), "unseen-count"))
+ unseen_count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "unseen-count"));
- if(purple_conversation_get_data(conv, "unseen-state"))
- unseen_state = GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-state"));
+ if(g_object_get_data(G_OBJECT(conv), "unseen-state"))
+ unseen_state = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "unseen-state"));
if (state == PIDGIN_UNSEEN_NONE)
{
@@ -8398,10 +9203,10 @@ conv_set_unseen(PurpleConversation *conv, PidginUnseenState state)
unseen_state = state;
}
- purple_conversation_set_data(conv, "unseen-count", GINT_TO_POINTER(unseen_count));
- purple_conversation_set_data(conv, "unseen-state", GINT_TO_POINTER(unseen_state));
+ g_object_set_data(G_OBJECT(conv), "unseen-count", GINT_TO_POINTER(unseen_count));
+ g_object_set_data(G_OBJECT(conv), "unseen-state", GINT_TO_POINTER(unseen_state));
- purple_conversation_update(conv, PURPLE_CONV_UPDATE_UNSEEN);
+ purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_UNSEEN);
}
static void
@@ -8421,10 +9226,10 @@ gtkconv_set_unseen(PidginConversation *gtkconv, PidginUnseenState state)
gtkconv->unseen_state = state;
}
- purple_conversation_set_data(gtkconv->active_conv, "unseen-count", GINT_TO_POINTER(gtkconv->unseen_count));
- purple_conversation_set_data(gtkconv->active_conv, "unseen-state", GINT_TO_POINTER(gtkconv->unseen_state));
+ g_object_set_data(G_OBJECT(gtkconv->active_conv), "unseen-count", GINT_TO_POINTER(gtkconv->unseen_count));
+ g_object_set_data(G_OBJECT(gtkconv->active_conv), "unseen-state", GINT_TO_POINTER(gtkconv->unseen_state));
- purple_conversation_update(gtkconv->active_conv, PURPLE_CONV_UPDATE_UNSEEN);
+ purple_conversation_update(gtkconv->active_conv, PURPLE_CONVERSATION_UPDATE_UNSEEN);
}
/*
@@ -8435,7 +9240,7 @@ gtkconv_set_unseen(PidginConversation *gtkconv, PidginUnseenState state)
static gboolean
focus_win_cb(GtkWidget *w, GdkEventFocus *e, gpointer d)
{
- PidginWindow *win = d;
+ PidginConvWindow *win = d;
PidginConversation *gtkconv = pidgin_conv_window_get_active_gtkconv(win);
if (gtkconv)
@@ -8445,9 +9250,12 @@ focus_win_cb(GtkWidget *w, GdkEventFocus *e, gpointer d)
}
static void
-notebook_init_grab(PidginWindow *gtkwin, GtkWidget *widget)
+notebook_init_grab(PidginConvWindow *gtkwin, GtkWidget *widget, GdkEvent *event)
{
static GdkCursor *cursor = NULL;
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkDevice *device;
+#endif
gtkwin->in_drag = TRUE;
@@ -8462,18 +9270,27 @@ notebook_init_grab(PidginWindow *gtkwin, GtkWidget *widget)
/* Grab the pointer */
gtk_grab_add(gtkwin->notebook);
+#if GTK_CHECK_VERSION(3,0,0)
+ 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));
+#else
#ifndef _WIN32
/* Currently for win32 GTK+ (as of 2.2.1), gdk_pointer_is_grabbed will
always be true after a button press. */
if (!gdk_pointer_is_grabbed())
#endif
- gdk_pointer_grab(gtkwin->notebook->window, FALSE,
+ gdk_pointer_grab(gtk_widget_get_window(gtkwin->notebook), FALSE,
GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
- NULL, cursor, GDK_CURRENT_TIME);
+ NULL, cursor, gdk_event_get_time(event));
+#endif
}
static gboolean
-notebook_motion_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win)
+notebook_motion_cb(GtkWidget *widget, GdkEventButton *e, PidginConvWindow *win)
{
/*
@@ -8487,11 +9304,11 @@ notebook_motion_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win)
e->y_root >= win->drag_max_y) {
win->in_predrag = FALSE;
- notebook_init_grab(win, widget);
+ notebook_init_grab(win, widget, (GdkEvent *)e);
}
}
else { /* Otherwise, draw the arrows. */
- PidginWindow *dest_win;
+ PidginConvWindow *dest_win;
GtkNotebook *dest_notebook;
GtkWidget *tab;
gint page_num;
@@ -8499,10 +9316,10 @@ notebook_motion_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win)
gboolean to_right = FALSE;
/* Get the window that the cursor is over. */
- dest_win = pidgin_conv_window_get_at_xy(e->x_root, e->y_root);
+ dest_win = pidgin_conv_window_get_at_event((GdkEvent *)e);
if (dest_win == NULL) {
- dnd_hints_hide_all();
+ pidgin_dnd_hints_hide_all();
return TRUE;
}
@@ -8528,23 +9345,23 @@ notebook_motion_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win)
if (gtk_notebook_get_show_tabs(dest_notebook) == FALSE && win == dest_win)
{
/* dragging a tab from a single-tabbed window over its own window */
- dnd_hints_hide_all();
+ pidgin_dnd_hints_hide_all();
return TRUE;
} else if (horiz_tabs) {
if (((gpointer)win == (gpointer)dest_win && win->drag_tab < page_num) || to_right) {
- dnd_hints_show_relative(HINT_ARROW_DOWN, tab, HINT_POSITION_RIGHT, HINT_POSITION_TOP);
- dnd_hints_show_relative(HINT_ARROW_UP, tab, HINT_POSITION_RIGHT, HINT_POSITION_BOTTOM);
+ pidgin_dnd_hints_show_relative(HINT_ARROW_DOWN, tab, HINT_POSITION_RIGHT, HINT_POSITION_TOP);
+ pidgin_dnd_hints_show_relative(HINT_ARROW_UP, tab, HINT_POSITION_RIGHT, HINT_POSITION_BOTTOM);
} else {
- dnd_hints_show_relative(HINT_ARROW_DOWN, tab, HINT_POSITION_LEFT, HINT_POSITION_TOP);
- dnd_hints_show_relative(HINT_ARROW_UP, tab, HINT_POSITION_LEFT, HINT_POSITION_BOTTOM);
+ pidgin_dnd_hints_show_relative(HINT_ARROW_DOWN, tab, HINT_POSITION_LEFT, HINT_POSITION_TOP);
+ pidgin_dnd_hints_show_relative(HINT_ARROW_UP, tab, HINT_POSITION_LEFT, HINT_POSITION_BOTTOM);
}
} else {
if (((gpointer)win == (gpointer)dest_win && win->drag_tab < page_num) || to_right) {
- dnd_hints_show_relative(HINT_ARROW_RIGHT, tab, HINT_POSITION_LEFT, HINT_POSITION_BOTTOM);
- dnd_hints_show_relative(HINT_ARROW_LEFT, tab, HINT_POSITION_RIGHT, HINT_POSITION_BOTTOM);
+ pidgin_dnd_hints_show_relative(HINT_ARROW_RIGHT, tab, HINT_POSITION_LEFT, HINT_POSITION_BOTTOM);
+ pidgin_dnd_hints_show_relative(HINT_ARROW_LEFT, tab, HINT_POSITION_RIGHT, HINT_POSITION_BOTTOM);
} else {
- dnd_hints_show_relative(HINT_ARROW_RIGHT, tab, HINT_POSITION_LEFT, HINT_POSITION_TOP);
- dnd_hints_show_relative(HINT_ARROW_LEFT, tab, HINT_POSITION_RIGHT, HINT_POSITION_TOP);
+ pidgin_dnd_hints_show_relative(HINT_ARROW_RIGHT, tab, HINT_POSITION_LEFT, HINT_POSITION_TOP);
+ pidgin_dnd_hints_show_relative(HINT_ARROW_LEFT, tab, HINT_POSITION_RIGHT, HINT_POSITION_TOP);
}
}
}
@@ -8553,7 +9370,7 @@ notebook_motion_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win)
}
static gboolean
-notebook_leave_cb(GtkWidget *widget, GdkEventCrossing *e, PidginWindow *win)
+notebook_leave_cb(GtkWidget *widget, GdkEventCrossing *e, PidginConvWindow *win)
{
if (win->in_drag)
return FALSE;
@@ -8563,9 +9380,9 @@ notebook_leave_cb(GtkWidget *widget, GdkEventCrossing *e, PidginWindow *win)
e->y_root < win->drag_min_y ||
e->y_root >= win->drag_max_y) {
- win->in_predrag = FALSE;
- notebook_init_grab(win, widget);
- }
+ win->in_predrag = FALSE;
+ notebook_init_grab(win, widget, (GdkEvent *)e);
+ }
return TRUE;
}
@@ -8587,6 +9404,9 @@ infopane_press_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *gtkc
if (e->button == 1) {
int nb_x, nb_y;
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation(gtkconv->infopane_hbox, &allocation);
if (gtkconv->win->in_drag)
return TRUE;
@@ -8594,12 +9414,12 @@ infopane_press_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *gtkc
gtkconv->win->in_predrag = TRUE;
gtkconv->win->drag_tab = gtk_notebook_page_num(GTK_NOTEBOOK(gtkconv->win->notebook), gtkconv->tab_cont);
- gdk_window_get_origin(gtkconv->infopane_hbox->window, &nb_x, &nb_y);
+ gdk_window_get_origin(gtk_widget_get_window(gtkconv->infopane_hbox), &nb_x, &nb_y);
- gtkconv->win->drag_min_x = gtkconv->infopane_hbox->allocation.x + nb_x;
- gtkconv->win->drag_min_y = gtkconv->infopane_hbox->allocation.y + nb_y;
- gtkconv->win->drag_max_x = gtkconv->infopane_hbox->allocation.width + gtkconv->win->drag_min_x;
- gtkconv->win->drag_max_y = gtkconv->infopane_hbox->allocation.height + gtkconv->win->drag_min_y;
+ gtkconv->win->drag_min_x = allocation.x + nb_x;
+ gtkconv->win->drag_min_y = allocation.y + nb_y;
+ gtkconv->win->drag_max_x = allocation.width + gtkconv->win->drag_min_x;
+ gtkconv->win->drag_max_y = allocation.height + gtkconv->win->drag_min_y;
gtkconv->win->drag_motion_signal = g_signal_connect(G_OBJECT(gtkconv->win->notebook), "motion_notify_event",
G_CALLBACK(notebook_motion_cb), gtkconv->win);
@@ -8612,9 +9432,9 @@ infopane_press_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *gtkc
/* Right click was pressed. Popup the context menu. */
GtkWidget *menu = gtk_menu_new(), *sub;
gboolean populated = populate_menu_with_options(menu, gtkconv, TRUE);
- sub = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtkconv->win->menu.send_to));
- if (sub && GTK_WIDGET_IS_SENSITIVE(gtkconv->win->menu.send_to)) {
+ sub = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtkconv->win->menu->send_to));
+ if (sub && gtk_widget_is_sensitive(gtkconv->win->menu->send_to)) {
GtkWidget *item = gtk_menu_item_new_with_mnemonic(_("S_end To"));
if (populated)
pidgin_separator(menu);
@@ -8635,12 +9455,13 @@ infopane_press_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *gtkc
}
static gboolean
-notebook_press_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win)
+notebook_press_cb(GtkWidget *widget, GdkEventButton *e, PidginConvWindow *win)
{
gint nb_x, nb_y;
int tab_clicked;
GtkWidget *page;
GtkWidget *tab;
+ GtkAllocation allocation;
if (e->button == 2 && e->type == GDK_BUTTON_PRESS) {
PidginConversation *gtkconv;
@@ -8678,7 +9499,7 @@ notebook_press_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win)
* Get the relative position of the press event, with regards to
* the position of the notebook.
*/
- gdk_window_get_origin(win->notebook->window, &nb_x, &nb_y);
+ gdk_window_get_origin(gtk_widget_get_window(win->notebook), &nb_x, &nb_y);
/* Reset the min/max x/y */
win->drag_min_x = 0;
@@ -8690,10 +9511,12 @@ notebook_press_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win)
page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(win->notebook), tab_clicked);
tab = gtk_notebook_get_tab_label(GTK_NOTEBOOK(win->notebook), page);
- win->drag_min_x = tab->allocation.x + nb_x;
- win->drag_min_y = tab->allocation.y + nb_y;
- win->drag_max_x = tab->allocation.width + win->drag_min_x;
- win->drag_max_y = tab->allocation.height + win->drag_min_y;
+ gtk_widget_get_allocation(tab, &allocation);
+
+ win->drag_min_x = allocation.x + nb_x;
+ win->drag_min_y = allocation.y + nb_y;
+ win->drag_max_x = allocation.width + win->drag_min_x;
+ win->drag_max_y = allocation.height + win->drag_min_y;
/* Make sure the click occurred in the tab. */
if (e->x_root < win->drag_min_x ||
@@ -8701,8 +9524,8 @@ notebook_press_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win)
e->y_root < win->drag_min_y ||
e->y_root >= win->drag_max_y) {
- return FALSE;
- }
+ return FALSE;
+ }
win->in_predrag = TRUE;
win->drag_tab = tab_clicked;
@@ -8720,15 +9543,18 @@ notebook_press_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win)
}
static gboolean
-notebook_release_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win)
+notebook_release_cb(GtkWidget *widget, GdkEventButton *e, PidginConvWindow *win)
{
- PidginWindow *dest_win;
+ PidginConvWindow *dest_win;
GtkNotebook *dest_notebook;
PidginConversation *active_gtkconv;
PidginConversation *gtkconv;
gint dest_page_num = 0;
gboolean new_window = FALSE;
gboolean to_right = FALSE;
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkDevice *device;
+#endif
/*
* Don't check to make sure that the event's window matches the
@@ -8738,10 +9564,18 @@ notebook_release_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win)
if (e->button != 1 && e->type != GDK_BUTTON_RELEASE)
return FALSE;
+#if GTK_CHECK_VERSION(3,0,0)
+ 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));
+ gtk_grab_remove(widget);
+ }
+#else
if (gdk_pointer_is_grabbed()) {
- gdk_pointer_ungrab(GDK_CURRENT_TIME);
+ gdk_pointer_ungrab(gdk_event_get_time((GdkEvent *)e));
gtk_grab_remove(widget);
}
+#endif
if (!win->in_predrag && !win->in_drag)
return FALSE;
@@ -8776,9 +9610,9 @@ notebook_release_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win)
win->in_drag = FALSE;
- dnd_hints_hide_all();
+ pidgin_dnd_hints_hide_all();
- dest_win = pidgin_conv_window_get_at_xy(e->x_root, e->y_root);
+ dest_win = pidgin_conv_window_get_at_event((GdkEvent *)e);
active_gtkconv = pidgin_conv_window_get_active_gtkconv(win);
@@ -8844,7 +9678,7 @@ static void
before_switch_conv_cb(GtkNotebook *notebook, GtkWidget *page, gint page_num,
gpointer user_data)
{
- PidginWindow *win;
+ PidginConvWindow *win;
PurpleConversation *conv;
PidginConversation *gtkconv;
@@ -8853,7 +9687,7 @@ before_switch_conv_cb(GtkNotebook *notebook, GtkWidget *page, gint page_num,
g_return_if_fail(conv != NULL);
- if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_IM)
+ if (!PURPLE_IS_IM_CONVERSATION(conv))
return;
gtkconv = PIDGIN_CONVERSATION(conv);
@@ -8865,24 +9699,24 @@ before_switch_conv_cb(GtkNotebook *notebook, GtkWidget *page, gint page_num,
stop_anim(NULL, gtkconv);
}
+
static void
-close_window(GtkWidget *w, PidginWindow *win)
+close_window(GtkWidget *w, PidginConvWindow *win)
{
close_win_cb(w, NULL, win);
}
static void
-detach_tab_cb(GtkWidget *w, GObject *menu)
+detach_tab_cb(GtkWidget *w, PidginConvWindow *win)
{
- PidginWindow *win, *new_window;
+ PidginConvWindow *new_window;
PidginConversation *gtkconv;
- gtkconv = g_object_get_data(menu, "clicked_tab");
+ gtkconv = win->clicked_tab;
if (!gtkconv)
return;
- win = pidgin_conv_get_window(gtkconv);
/* Nothing to do if there's only one tab in the window */
if (pidgin_conv_window_get_gtkconv_count(win) == 1)
return;
@@ -8895,19 +9729,16 @@ detach_tab_cb(GtkWidget *w, GObject *menu)
}
static void
-close_others_cb(GtkWidget *w, GObject *menu)
+close_others_cb(GtkWidget *w, PidginConvWindow *win)
{
GList *iter;
PidginConversation *gtkconv;
- PidginWindow *win;
- gtkconv = g_object_get_data(menu, "clicked_tab");
+ gtkconv = win->clicked_tab;
if (!gtkconv)
return;
- win = pidgin_conv_get_window(gtkconv);
-
for (iter = pidgin_conv_window_get_gtkconvs(win); iter; )
{
PidginConversation *gconv = iter->data;
@@ -8920,66 +9751,118 @@ close_others_cb(GtkWidget *w, GObject *menu)
}
}
-static void close_tab_cb(GtkWidget *w, GObject *menu)
+static void
+close_tab_cb(GtkWidget *w, PidginConvWindow *win)
{
PidginConversation *gtkconv;
- gtkconv = g_object_get_data(menu, "clicked_tab");
+ gtkconv = win->clicked_tab;
if (gtkconv)
close_conv_cb(NULL, gtkconv);
}
-static gboolean
-right_click_menu_cb(GtkNotebook *notebook, GdkEventButton *event, PidginWindow *win)
+static void
+notebook_menu_switch_cb(GtkWidget *item, GtkWidget *child)
{
- GtkWidget *item, *menu;
- PidginConversation *gtkconv;
+ GtkNotebook *notebook;
+ int index;
- if (event->type != GDK_BUTTON_PRESS || event->button != 3)
- return FALSE;
+ notebook = GTK_NOTEBOOK(gtk_widget_get_parent(child));
+ index = gtk_notebook_page_num(notebook, child);
+ gtk_notebook_set_current_page(notebook, index);
+}
- gtkconv = pidgin_conv_window_get_gtkconv_at_index(win,
- pidgin_conv_get_tab_at_xy(win, event->x_root, event->y_root, NULL));
+static void
+notebook_menu_update_label_cb(GtkWidget *child, GParamSpec *pspec,
+ GtkNotebook *notebook)
+{
+ GtkWidget *item;
+ GtkWidget *label;
- if (g_object_get_data(G_OBJECT(notebook->menu), "clicked_tab"))
- {
- g_object_set_data(G_OBJECT(notebook->menu), "clicked_tab", gtkconv);
- return FALSE;
+ item = g_object_get_data(G_OBJECT(child), "popup-menu-item");
+ label = gtk_bin_get_child(GTK_BIN(item));
+ if (label)
+ gtk_container_remove(GTK_CONTAINER(item), label);
+
+ label = gtk_notebook_get_menu_label(notebook, child);
+ if (label) {
+ gtk_widget_show(label);
+ gtk_container_add(GTK_CONTAINER(item), label);
+ gtk_widget_show(item);
+ } else {
+ gtk_widget_hide(item);
+ }
+}
+
+static void
+notebook_add_tab_to_menu_cb(GtkNotebook *notebook, GtkWidget *child,
+ guint page_num, PidginConvWindow *win)
+{
+ GtkWidget *item;
+ GtkWidget *label;
+
+ item = gtk_menu_item_new();
+ label = gtk_notebook_get_menu_label(notebook, child);
+ if (label) {
+ gtk_widget_show(label);
+ gtk_container_add(GTK_CONTAINER(item), label);
+ gtk_widget_show(item);
}
- g_object_set_data(G_OBJECT(notebook->menu), "clicked_tab", gtkconv);
+ g_signal_connect(child, "child-notify::menu-label",
+ G_CALLBACK(notebook_menu_update_label_cb), notebook);
+ g_signal_connect(item, "activate",
+ G_CALLBACK(notebook_menu_switch_cb), child);
+ g_object_set_data(G_OBJECT(child), "popup-menu-item", item);
- menu = notebook->menu;
- pidgin_separator(GTK_WIDGET(menu));
+ gtk_menu_shell_insert(GTK_MENU_SHELL(win->notebook_menu), item, page_num);
+}
- item = gtk_menu_item_new_with_label(_("Close other tabs"));
- gtk_widget_show(item);
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
- g_signal_connect(G_OBJECT(item), "activate",
- G_CALLBACK(close_others_cb), menu);
+static void
+notebook_remove_tab_from_menu_cb(GtkNotebook *notebook, GtkWidget *child,
+ guint page_num, PidginConvWindow *win)
+{
+ GtkWidget *item;
- item = gtk_menu_item_new_with_label(_("Close all tabs"));
- gtk_widget_show(item);
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
- g_signal_connect(G_OBJECT(item), "activate",
- G_CALLBACK(close_window), win);
+ /* Disconnecting the "child-notify::menu-label" signal. */
+ g_signal_handlers_disconnect_by_data(child, notebook);
- pidgin_separator(menu);
+ item = g_object_get_data(G_OBJECT(child), "popup-menu-item");
+ gtk_container_remove(GTK_CONTAINER(win->notebook_menu), item);
+}
- item = gtk_menu_item_new_with_label(_("Detach this tab"));
- gtk_widget_show(item);
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
- g_signal_connect(G_OBJECT(item), "activate",
- G_CALLBACK(detach_tab_cb), menu);
- item = gtk_menu_item_new_with_label(_("Close this tab"));
- gtk_widget_show(item);
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
- g_signal_connect(G_OBJECT(item), "activate",
- G_CALLBACK(close_tab_cb), menu);
+static void
+notebook_reorder_tab_in_menu_cb(GtkNotebook *notebook, GtkWidget *child,
+ guint page_num, PidginConvWindow *win)
+{
+ GtkWidget *item;
- return FALSE;
+ item = g_object_get_data(G_OBJECT(child), "popup-menu-item");
+ gtk_menu_reorder_child(GTK_MENU(win->notebook_menu), item, page_num);
+}
+
+static gboolean
+notebook_right_click_menu_cb(GtkNotebook *notebook, GdkEventButton *event,
+ PidginConvWindow *win)
+{
+ GtkWidget *menu;
+ PidginConversation *gtkconv;
+
+ if (event->type != GDK_BUTTON_PRESS || event->button != 3)
+ return FALSE;
+
+ gtkconv = pidgin_conv_window_get_gtkconv_at_index(win,
+ pidgin_conv_get_tab_at_xy(win, event->x_root, event->y_root, NULL));
+
+ win->clicked_tab = gtkconv;
+
+ menu = win->notebook_menu;
+
+ gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
+
+ return TRUE;
}
static void
@@ -9002,7 +9885,7 @@ alias_focus_cb(GtkWidget *widget, GdkEventFocus *event, gpointer user_data)
static gboolean
alias_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
{
- if (event->keyval == GDK_Escape) {
+ if (event->keyval == GDK_KEY_Escape) {
remove_edit_entry(user_data, widget);
return TRUE;
}
@@ -9025,15 +9908,14 @@ alias_cb(GtkEntry *entry, gpointer user_data)
account = purple_conversation_get_account(conv);
name = purple_conversation_get_name(conv);
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
PurpleBuddy *buddy;
- buddy = purple_find_buddy(account, name);
+ buddy = purple_blist_find_buddy(account, name);
if (buddy != NULL) {
- purple_blist_alias_buddy(buddy,
- gtk_entry_get_text(entry));
+ purple_buddy_set_local_alias(buddy, gtk_entry_get_text(entry));
}
- serv_alias_buddy(buddy);
- } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
+ purple_serv_alias_buddy(buddy);
+ } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
gtk_entry_set_text(GTK_ENTRY(gtkconv->u.chat->topic_text), gtk_entry_get_text(entry));
topic_callback(NULL, gtkconv);
}
@@ -9047,35 +9929,35 @@ infopane_entry_activate(PidginConversation *gtkconv)
PurpleConversation *conv = gtkconv->active_conv;
const char *text = NULL;
- if (!GTK_WIDGET_VISIBLE(gtkconv->infopane)) {
+ if (!gtk_widget_get_visible(gtkconv->infopane)) {
/* There's already an entry for alias. Let's not create another one. */
return FALSE;
}
- if (!purple_account_is_connected(gtkconv->active_conv->account)) {
+ if (!purple_account_is_connected(purple_conversation_get_account(gtkconv->active_conv))) {
/* Do not allow aliasing someone on a disconnected account. */
return FALSE;
}
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
- PurpleBuddy *buddy = purple_find_buddy(gtkconv->active_conv->account, gtkconv->active_conv->name);
+ if (PURPLE_IS_IM_CONVERSATION(conv)) {
+ PurpleBuddy *buddy = purple_blist_find_buddy(purple_conversation_get_account(gtkconv->active_conv), purple_conversation_get_name(gtkconv->active_conv));
if (!buddy)
/* This buddy isn't in your buddy list, so we can't alias him */
return FALSE;
text = purple_buddy_get_contact_alias(buddy);
- } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
+ } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
PurpleConnection *gc;
PurplePluginProtocolInfo *prpl_info = NULL;
- gc = purple_conversation_get_gc(conv);
+ gc = purple_conversation_get_connection(conv);
if (gc != NULL)
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
if (prpl_info && prpl_info->set_chat_topic == NULL)
/* This protocol doesn't support setting the chat room topic */
return FALSE;
- text = purple_conv_chat_get_topic(PURPLE_CONV_CHAT(conv));
+ text = purple_chat_conversation_get_topic(PURPLE_CHAT_CONVERSATION(conv));
}
/* alias label */
@@ -9102,7 +9984,7 @@ infopane_entry_activate(PidginConversation *gtkconv)
}
static gboolean
-window_keypress_cb(GtkWidget *widget, GdkEventKey *event, PidginWindow *win)
+window_keypress_cb(GtkWidget *widget, GdkEventKey *event, PidginConvWindow *win)
{
PidginConversation *gtkconv = pidgin_conv_window_get_active_gtkconv(win);
@@ -9113,7 +9995,7 @@ static void
switch_conv_cb(GtkNotebook *notebook, GtkWidget *page, gint page_num,
gpointer user_data)
{
- PidginWindow *win;
+ PidginConvWindow *win;
PurpleConversation *conv;
PidginConversation *gtkconv;
const char *sound_method;
@@ -9131,10 +10013,11 @@ switch_conv_cb(GtkNotebook *notebook, GtkWidget *page, gint page_num,
/* Update the menubar */
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtkconv->win->menu.logging),
- purple_conversation_is_logging(conv));
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(gtkconv->win->menu->logging),
+ purple_conversation_is_logging(conv));
generate_send_to_items(win);
+ generate_e2ee_controls(win);
regenerate_options_items(win);
regenerate_plugins_items(win);
@@ -9142,20 +10025,17 @@ switch_conv_cb(GtkNotebook *notebook, GtkWidget *page, gint page_num,
sound_method = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method");
if (strcmp(sound_method, "none") != 0)
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(win->menu.sounds),
- gtkconv->make_sound);
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(win->menu->sounds),
+ gtkconv->make_sound);
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(win->menu.show_formatting_toolbar),
- purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/show_formatting_toolbar"));
-
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(win->menu.show_timestamps),
- purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/show_timestamps"));
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(win->menu->show_formatting_toolbar),
+ purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/show_formatting_toolbar"));
/*
* We pause icons when they are not visible. If this icon should
* be animated then start it back up again.
*/
- if ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) &&
+ if (PURPLE_IS_IM_CONVERSATION(conv) &&
(gtkconv->u.im->animate))
start_anim(NULL, gtkconv);
@@ -9176,14 +10056,18 @@ static GList*
make_status_icon_list(const char *stock, GtkWidget *w)
{
GList *l = NULL;
- l = g_list_append(l, gtk_widget_render_icon (w, stock,
- gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL), "GtkWindow"));
- l = g_list_append(l, gtk_widget_render_icon (w, stock,
- gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_SMALL), "GtkWindow"));
- l = g_list_append(l, gtk_widget_render_icon (w, stock,
- gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_MEDIUM), "GtkWindow"));
- l = g_list_append(l, gtk_widget_render_icon (w, stock,
- gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_LARGE), "GtkWindow"));
+ l = g_list_append(l,
+ gtk_widget_render_icon(w, stock,
+ gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL), "GtkWindow"));
+ l = g_list_append(l,
+ gtk_widget_render_icon(w, stock,
+ gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_SMALL), "GtkWindow"));
+ l = g_list_append(l,
+ gtk_widget_render_icon(w, stock,
+ gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_MEDIUM), "GtkWindow"));
+ l = g_list_append(l,
+ gtk_widget_render_icon(w, stock,
+ gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_LARGE), "GtkWindow"));
return l;
}
@@ -9207,14 +10091,14 @@ plugin_changed_cb(PurplePlugin *p, gpointer data)
static gboolean gtk_conv_configure_cb(GtkWidget *w, GdkEventConfigure *event, gpointer data) {
int x, y;
- if (GTK_WIDGET_VISIBLE(w))
+ if (gtk_widget_get_visible(w))
gtk_window_get_position(GTK_WINDOW(w), &x, &y);
else
return FALSE; /* carry on normally */
/* Workaround for GTK+ bug # 169811 - "configure_event" is fired
* when the window is being maximized */
- if (gdk_window_get_state(w->window) & GDK_WINDOW_STATE_MAXIMIZED)
+ if (gdk_window_get_state(gtk_widget_get_window(w)) & GDK_WINDOW_STATE_MAXIMIZED)
return FALSE;
/* don't save off-screen positioning */
@@ -9236,13 +10120,13 @@ static gboolean gtk_conv_configure_cb(GtkWidget *w, GdkEventConfigure *event, gp
}
static void
-pidgin_conv_set_position_size(PidginWindow *win, int conv_x, int conv_y,
+pidgin_conv_set_position_size(PidginConvWindow *win, int conv_x, int conv_y,
int conv_width, int conv_height)
{
- /* if the window exists, is hidden, we're saving positions, and the
- * position is sane... */
+ /* if the window exists, is hidden, we're saving positions, and the
+ * position is sane... */
if (win && win->window &&
- !GTK_WIDGET_VISIBLE(win->window) && conv_width != 0) {
+ !gtk_widget_get_visible(win->window) && conv_width != 0) {
#ifdef _WIN32 /* only override window manager placement on Windows */
/* ...check position is on screen... */
@@ -9264,7 +10148,7 @@ pidgin_conv_set_position_size(PidginWindow *win, int conv_x, int conv_y,
}
static void
-pidgin_conv_restore_position(PidginWindow *win) {
+pidgin_conv_restore_position(PidginConvWindow *win) {
pidgin_conv_set_position_size(win,
purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/x"),
purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/y"),
@@ -9272,21 +10156,25 @@ pidgin_conv_restore_position(PidginWindow *win) {
purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/height"));
}
-PidginWindow *
+PidginConvWindow *
pidgin_conv_window_new()
{
- PidginWindow *win;
+ PidginConvWindow *win;
GtkPositionType pos;
GtkWidget *testidea;
GtkWidget *menubar;
+ GtkWidget *menu;
+ GtkWidget *item;
GdkModifierType state;
- win = g_malloc0(sizeof(PidginWindow));
+ win = g_malloc0(sizeof(PidginConvWindow));
+ win->menu = g_malloc0(sizeof(PidginConvWindowMenu));
window_list = g_list_append(window_list, win);
/* Create the window. */
win->window = pidgin_create_window(NULL, 0, "conversation", TRUE);
+ /*_pidgin_widget_set_accessible_name(win->window, "Conversations");*/
if (!gtk_get_current_event_state(&state))
gtk_window_set_focus_on_map(GTK_WINDOW(win->window), FALSE);
@@ -9323,12 +10211,48 @@ pidgin_conv_window_new()
#endif
gtk_notebook_set_tab_pos(GTK_NOTEBOOK(win->notebook), pos);
gtk_notebook_set_scrollable(GTK_NOTEBOOK(win->notebook), TRUE);
- gtk_notebook_popup_enable(GTK_NOTEBOOK(win->notebook));
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), FALSE);
gtk_notebook_set_show_border(GTK_NOTEBOOK(win->notebook), TRUE);
+ menu = win->notebook_menu = gtk_menu_new();
+
+ pidgin_separator(GTK_WIDGET(menu));
+
+ item = gtk_menu_item_new_with_label(_("Close other tabs"));
+ gtk_widget_show(item);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+ g_signal_connect(G_OBJECT(item), "activate",
+ G_CALLBACK(close_others_cb), win);
+
+ item = gtk_menu_item_new_with_label(_("Close all tabs"));
+ gtk_widget_show(item);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+ g_signal_connect(G_OBJECT(item), "activate",
+ G_CALLBACK(close_window), win);
+
+ pidgin_separator(menu);
+
+ item = gtk_menu_item_new_with_label(_("Detach this tab"));
+ gtk_widget_show(item);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+ g_signal_connect(G_OBJECT(item), "activate",
+ G_CALLBACK(detach_tab_cb), win);
+
+ item = gtk_menu_item_new_with_label(_("Close this tab"));
+ gtk_widget_show(item);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+ g_signal_connect(G_OBJECT(item), "activate",
+ G_CALLBACK(close_tab_cb), win);
+
+ g_signal_connect(G_OBJECT(win->notebook), "page-added",
+ G_CALLBACK(notebook_add_tab_to_menu_cb), win);
+ g_signal_connect(G_OBJECT(win->notebook), "page-removed",
+ G_CALLBACK(notebook_remove_tab_from_menu_cb), win);
+ g_signal_connect(G_OBJECT(win->notebook), "page-reordered",
+ G_CALLBACK(notebook_reorder_tab_in_menu_cb), win);
+
g_signal_connect(G_OBJECT(win->notebook), "button-press-event",
- G_CALLBACK(right_click_menu_cb), win);
+ G_CALLBACK(notebook_right_click_menu_cb), win);
gtk_widget_show(win->notebook);
@@ -9345,7 +10269,7 @@ pidgin_conv_window_new()
g_signal_connect(G_OBJECT(win->notebook), "button_release_event",
G_CALLBACK(notebook_release_cb), win);
- testidea = gtk_vbox_new(FALSE, 0);
+ testidea = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
/* Setup the menubar. */
menubar = setup_menubar(win);
@@ -9373,11 +10297,14 @@ pidgin_conv_window_new()
gtk_window_iconify(GTK_WINDOW(win->window));
#endif
+ purple_signal_emit(pidgin_conversations_get_handle(),
+ "conversation-window-created", win);
+
return win;
}
void
-pidgin_conv_window_destroy(PidginWindow *win)
+pidgin_conv_window_destroy(PidginConvWindow *win)
{
if (win->gtkconvs) {
GList *iter = win->gtkconvs;
@@ -9393,40 +10320,38 @@ pidgin_conv_window_destroy(PidginWindow *win)
purple_prefs_disconnect_by_handle(win);
window_list = g_list_remove(window_list, win);
- /* Close the "Find" dialog if it's open */
- if (win->dialogs.search)
- gtk_widget_destroy(win->dialogs.search);
-
+ gtk_widget_destroy(win->notebook_menu);
gtk_widget_destroy(win->window);
- g_object_unref(G_OBJECT(win->menu.item_factory));
+ g_object_unref(G_OBJECT(win->menu->ui));
purple_notify_close_with_handle(win);
purple_signals_disconnect_by_handle(win);
+ g_free(win->menu);
g_free(win);
}
void
-pidgin_conv_window_show(PidginWindow *win)
+pidgin_conv_window_show(PidginConvWindow *win)
{
gtk_widget_show(win->window);
}
void
-pidgin_conv_window_hide(PidginWindow *win)
+pidgin_conv_window_hide(PidginConvWindow *win)
{
gtk_widget_hide(win->window);
}
void
-pidgin_conv_window_raise(PidginWindow *win)
+pidgin_conv_window_raise(PidginConvWindow *win)
{
- gdk_window_raise(GDK_WINDOW(win->window->window));
+ gdk_window_raise(GDK_WINDOW(gtk_widget_get_window(win->window)));
}
void
-pidgin_conv_window_switch_gtkconv(PidginWindow *win, PidginConversation *gtkconv)
+pidgin_conv_window_switch_gtkconv(PidginConvWindow *win, PidginConversation *gtkconv)
{
gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook),
gtk_notebook_page_num(GTK_NOTEBOOK(win->notebook),
@@ -9436,9 +10361,6 @@ pidgin_conv_window_switch_gtkconv(PidginWindow *win, PidginConversation *gtkconv
static gboolean
gtkconv_tab_set_tip(GtkWidget *widget, GdkEventCrossing *event, PidginConversation *gtkconv)
{
-#if GTK_CHECK_VERSION(2, 12, 0)
-#define gtk_tooltips_set_tip(tips, w, l, p) gtk_widget_set_tooltip_text(w, l)
-#endif
/* PANGO_VERSION_CHECK macro was introduced in 1.15. So we need this double check. */
#ifndef PANGO_VERSION_CHECK
#define pango_layout_is_ellipsized(l) TRUE
@@ -9448,26 +10370,68 @@ gtkconv_tab_set_tip(GtkWidget *widget, GdkEventCrossing *event, PidginConversati
PangoLayout *layout;
layout = gtk_label_get_layout(GTK_LABEL(gtkconv->tab_label));
- gtk_tooltips_set_tip(gtkconv->tooltips, widget,
- pango_layout_is_ellipsized(layout) ? gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)) : NULL,
- NULL);
+ if (pango_layout_is_ellipsized(layout))
+ gtk_widget_set_tooltip_text(widget, gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)));
+ else
+ gtk_widget_set_tooltip_text(widget, NULL);
+
return FALSE;
-#if GTK_CHECK_VERSION(2, 12, 0)
-#undef gtk_tooltips_set_tip
-#endif
}
+#if GTK_CHECK_VERSION(3,0,0)
+static void
+set_default_tab_colors(GtkWidget *widget)
+{
+ GString *str;
+ GtkCssProvider *provider;
+ GError *error = NULL;
+ int iter;
+
+ struct {
+ const char *labelname;
+ const char *color;
+ } styles[] = {
+ {"tab-label-typing", "#4e9a06"},
+ {"tab-label-typed", "#c4a000"},
+ {"tab-label-attention", "#006aff"},
+ {"tab-label-unreadchat", "#cc0000"},
+ {"tab-label-event", "#888a85"},
+ {NULL, NULL}
+ };
+
+ str = g_string_new(NULL);
+
+ for (iter = 0; styles[iter].labelname; iter++) {
+ g_string_append_printf(str,
+ "#%s {\n"
+ " color: %s;\n"
+ "}\n",
+ styles[iter].labelname,
+ styles[iter].color);
+ }
+
+ provider = gtk_css_provider_new();
+
+ gtk_css_provider_load_from_data(provider, str->str, str->len, &error);
+
+ gtk_style_context_add_provider(gtk_widget_get_style_context(widget),
+ GTK_STYLE_PROVIDER(provider),
+ GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+ if (error)
+ g_error_free(error);
+ g_string_free(str, TRUE);
+}
+#endif
+
void
-pidgin_conv_window_add_gtkconv(PidginWindow *win, PidginConversation *gtkconv)
+pidgin_conv_window_add_gtkconv(PidginConvWindow *win, PidginConversation *gtkconv)
{
PurpleConversation *conv = gtkconv->active_conv;
PidginConversation *focus_gtkconv;
GtkWidget *tab_cont = gtkconv->tab_cont;
- PurpleConversationType conv_type;
const gchar *tmp_lab;
- conv_type = purple_conversation_get_type(conv);
-
win->gtkconvs = g_list_append(win->gtkconvs, gtkconv);
gtkconv->win = win;
@@ -9477,8 +10441,7 @@ pidgin_conv_window_add_gtkconv(PidginWindow *win, PidginConversation *gtkconv)
/* Close button. */
gtkconv->close = pidgin_create_small_button(gtk_label_new("×"));
- gtk_tooltips_set_tip(gtkconv->tooltips, gtkconv->close,
- _("Close conversation"), NULL);
+ gtk_widget_set_tooltip_text(gtkconv->close, _("Close conversation"));
g_signal_connect(gtkconv->close, "clicked", G_CALLBACK (close_conv_cb), gtkconv);
@@ -9496,9 +10459,12 @@ pidgin_conv_window_add_gtkconv(PidginWindow *win, PidginConversation *gtkconv)
/* Tab label. */
gtkconv->tab_label = gtk_label_new(tmp_lab = purple_conversation_get_title(conv));
+#if GTK_CHECK_VERSION(3,0,0)
+ set_default_tab_colors(gtkconv->tab_label);
+#endif
gtk_widget_set_name(gtkconv->tab_label, "tab-label");
- gtkconv->menu_tabby = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ gtkconv->menu_tabby = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtkconv->menu_label = gtk_label_new(tmp_lab);
gtk_box_pack_start(GTK_BOX(gtkconv->menu_tabby), gtkconv->menu_icon, FALSE, FALSE, 0);
@@ -9510,8 +10476,8 @@ pidgin_conv_window_add_gtkconv(PidginWindow *win, PidginConversation *gtkconv)
gtk_widget_show(gtkconv->menu_tabby);
- if (conv_type == PURPLE_CONV_TYPE_IM)
- pidgin_conv_update_buddy_icon(conv);
+ if (PURPLE_IS_IM_CONVERSATION(conv))
+ pidgin_conv_update_buddy_icon(PURPLE_IM_CONVERSATION(conv));
/* Build and set conversations tab */
pidgin_conv_tab_pack(win, gtkconv);
@@ -9536,7 +10502,7 @@ pidgin_conv_window_add_gtkconv(PidginWindow *win, PidginConversation *gtkconv)
}
static void
-pidgin_conv_tab_pack(PidginWindow *win, PidginConversation *gtkconv)
+pidgin_conv_tab_pack(PidginConvWindow *win, PidginConversation *gtkconv)
{
gboolean tabs_side = FALSE;
gint angle = 0;
@@ -9573,9 +10539,9 @@ pidgin_conv_tab_pack(PidginWindow *win, PidginConversation *gtkconv)
#endif
if (angle)
- gtkconv->tabby = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ gtkconv->tabby = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
else
- gtkconv->tabby = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ gtkconv->tabby = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtk_widget_set_name(gtkconv->tabby, "tab-container");
/* select the correct ordering for verticle tabs */
@@ -9593,7 +10559,7 @@ pidgin_conv_tab_pack(PidginWindow *win, PidginConversation *gtkconv)
g_signal_connect(G_OBJECT(ebox), "enter-notify-event",
G_CALLBACK(gtkconv_tab_set_tip), gtkconv);
- if (gtkconv->tab_label->parent == NULL) {
+ if (gtk_widget_get_parent(gtkconv->tab_label) == NULL) {
/* Pack if it's a new widget */
gtk_box_pack_start(GTK_BOX(gtkconv->tabby), first, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(gtkconv->tabby), gtkconv->tab_label, TRUE, TRUE, 0);
@@ -9614,9 +10580,9 @@ pidgin_conv_tab_pack(PidginWindow *win, PidginConversation *gtkconv)
gtk_notebook_set_tab_label(GTK_NOTEBOOK(win->notebook), gtkconv->tab_cont, ebox);
}
- gtk_notebook_set_tab_label_packing(GTK_NOTEBOOK(win->notebook), gtkconv->tab_cont,
- !tabs_side && !angle,
- TRUE, GTK_PACK_START);
+ gtk_container_child_set(GTK_CONTAINER(win->notebook), gtkconv->tab_cont,
+ "tab-expand", !tabs_side && !angle,
+ "tab-fill", TRUE, NULL);
if (pidgin_conv_window_get_gtkconv_count(win) == 1)
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook),
@@ -9634,14 +10600,13 @@ pidgin_conv_tab_pack(PidginWindow *win, PidginConversation *gtkconv)
}
void
-pidgin_conv_window_remove_gtkconv(PidginWindow *win, PidginConversation *gtkconv)
+pidgin_conv_window_remove_gtkconv(PidginConvWindow *win, PidginConversation *gtkconv)
{
unsigned int index;
index = gtk_notebook_page_num(GTK_NOTEBOOK(win->notebook), gtkconv->tab_cont);
- g_object_ref(gtkconv->tab_cont);
- gtk_object_sink(GTK_OBJECT(gtkconv->tab_cont));
+ g_object_ref_sink(G_OBJECT(gtkconv->tab_cont));
gtk_notebook_remove_page(GTK_NOTEBOOK(win->notebook), index);
@@ -9658,7 +10623,7 @@ pidgin_conv_window_remove_gtkconv(PidginWindow *win, PidginConversation *gtkconv
}
PidginConversation *
-pidgin_conv_window_get_gtkconv_at_index(const PidginWindow *win, int index)
+pidgin_conv_window_get_gtkconv_at_index(const PidginConvWindow *win, int index)
{
GtkWidget *tab_cont;
@@ -9669,7 +10634,7 @@ pidgin_conv_window_get_gtkconv_at_index(const PidginWindow *win, int index)
}
PidginConversation *
-pidgin_conv_window_get_active_gtkconv(const PidginWindow *win)
+pidgin_conv_window_get_active_gtkconv(const PidginConvWindow *win)
{
int index;
GtkWidget *tab_cont;
@@ -9685,7 +10650,7 @@ pidgin_conv_window_get_active_gtkconv(const PidginWindow *win)
PurpleConversation *
-pidgin_conv_window_get_active_conversation(const PidginWindow *win)
+pidgin_conv_window_get_active_conversation(const PidginConvWindow *win)
{
PidginConversation *gtkconv;
@@ -9700,7 +10665,7 @@ pidgin_conv_window_is_active_conversation(const PurpleConversation *conv)
}
gboolean
-pidgin_conv_window_has_focus(PidginWindow *win)
+pidgin_conv_window_has_focus(PidginConvWindow *win)
{
gboolean has_focus = FALSE;
@@ -9709,14 +10674,20 @@ pidgin_conv_window_has_focus(PidginWindow *win)
return has_focus;
}
-PidginWindow *
-pidgin_conv_window_get_at_xy(int x, int y)
+PidginConvWindow *
+pidgin_conv_window_get_at_event(GdkEvent *event)
{
- PidginWindow *win;
+ PidginConvWindow *win;
GdkWindow *gdkwin;
GList *l;
+ int x, y;
+#if GTK_CHECK_VERSION(3,0,0)
+ gdkwin = gdk_device_get_window_at_position(gdk_event_get_device(event),
+ &x, &y);
+#else
gdkwin = gdk_window_at_pointer(&x, &y);
+#endif
if (gdkwin)
gdkwin = gdk_window_get_toplevel(gdkwin);
@@ -9724,7 +10695,7 @@ pidgin_conv_window_get_at_xy(int x, int y)
for (l = pidgin_conv_windows_get_list(); l != NULL; l = l->next) {
win = l->data;
- if (gdkwin == win->window->window)
+ if (gdkwin == gtk_widget_get_window(win->window))
return win;
}
@@ -9732,27 +10703,24 @@ pidgin_conv_window_get_at_xy(int x, int y)
}
GList *
-pidgin_conv_window_get_gtkconvs(PidginWindow *win)
+pidgin_conv_window_get_gtkconvs(PidginConvWindow *win)
{
return win->gtkconvs;
}
guint
-pidgin_conv_window_get_gtkconv_count(PidginWindow *win)
+pidgin_conv_window_get_gtkconv_count(PidginConvWindow *win)
{
return g_list_length(win->gtkconvs);
}
-PidginWindow *
-pidgin_conv_window_first_with_type(PurpleConversationType type)
+PidginConvWindow *
+pidgin_conv_window_first_im(void)
{
GList *wins, *convs;
- PidginWindow *win;
+ PidginConvWindow *win;
PidginConversation *conv;
- if (type == PURPLE_CONV_TYPE_UNKNOWN)
- return NULL;
-
for (wins = pidgin_conv_windows_get_list(); wins != NULL; wins = wins->next) {
win = wins->data;
@@ -9762,7 +10730,7 @@ pidgin_conv_window_first_with_type(PurpleConversationType type)
conv = convs->data;
- if (purple_conversation_get_type(conv->active_conv) == type)
+ if (PURPLE_IS_IM_CONVERSATION(conv->active_conv))
return win;
}
}
@@ -9770,15 +10738,63 @@ pidgin_conv_window_first_with_type(PurpleConversationType type)
return NULL;
}
-PidginWindow *
-pidgin_conv_window_last_with_type(PurpleConversationType type)
+PidginConvWindow *
+pidgin_conv_window_last_im(void)
{
GList *wins, *convs;
- PidginWindow *win;
+ PidginConvWindow *win;
PidginConversation *conv;
- if (type == PURPLE_CONV_TYPE_UNKNOWN)
- return NULL;
+ for (wins = g_list_last(pidgin_conv_windows_get_list());
+ wins != NULL;
+ wins = wins->prev) {
+
+ win = wins->data;
+
+ for (convs = win->gtkconvs;
+ convs != NULL;
+ convs = convs->next) {
+
+ conv = convs->data;
+
+ if (PURPLE_IS_IM_CONVERSATION(conv->active_conv))
+ return win;
+ }
+ }
+
+ return NULL;
+}
+
+PidginConvWindow *
+pidgin_conv_window_first_chat(void)
+{
+ GList *wins, *convs;
+ PidginConvWindow *win;
+ PidginConversation *conv;
+
+ for (wins = pidgin_conv_windows_get_list(); wins != NULL; wins = wins->next) {
+ win = wins->data;
+
+ for (convs = win->gtkconvs;
+ convs != NULL;
+ convs = convs->next) {
+
+ conv = convs->data;
+
+ if (PURPLE_IS_CHAT_CONVERSATION(conv->active_conv))
+ return win;
+ }
+ }
+
+ return NULL;
+}
+
+PidginConvWindow *
+pidgin_conv_window_last_chat(void)
+{
+ GList *wins, *convs;
+ PidginConvWindow *win;
+ PidginConversation *conv;
for (wins = g_list_last(pidgin_conv_windows_get_list());
wins != NULL;
@@ -9792,7 +10808,7 @@ pidgin_conv_window_last_with_type(PurpleConversationType type)
conv = convs->data;
- if (purple_conversation_get_type(conv->active_conv) == type)
+ if (PURPLE_IS_CHAT_CONVERSATION(conv->active_conv))
return win;
}
}
@@ -9819,7 +10835,7 @@ static PidginConvPlacementFunc place_conv = NULL;
static void
conv_placement_last_created_win(PidginConversation *conv)
{
- PidginWindow *win;
+ PidginConvWindow *win;
GList *l = g_list_last(pidgin_conv_windows_get_list());
win = l ? l->data : NULL;;
@@ -9843,17 +10859,16 @@ conv_placement_last_created_win_type_configured_cb(GtkWidget *w,
GdkEventConfigure *event, PidginConversation *conv)
{
int x, y;
- PurpleConversationType type = purple_conversation_get_type(conv->active_conv);
GList *all;
- if (GTK_WIDGET_VISIBLE(w))
+ if (gtk_widget_get_visible(w))
gtk_window_get_position(GTK_WINDOW(w), &x, &y);
else
return FALSE; /* carry on normally */
/* Workaround for GTK+ bug # 169811 - "configure_event" is fired
* when the window is being maximized */
- if (gdk_window_get_state(w->window) & GDK_WINDOW_STATE_MAXIMIZED)
+ if (gdk_window_get_state(gtk_widget_get_window(w)) & GDK_WINDOW_STATE_MAXIMIZED)
return FALSE;
/* don't save off-screen positioning */
@@ -9864,18 +10879,18 @@ conv_placement_last_created_win_type_configured_cb(GtkWidget *w,
return FALSE; /* carry on normally */
for (all = conv->convs; all != NULL; all = all->next) {
- if (type != purple_conversation_get_type(all->data)) {
+ if (PURPLE_IS_IM_CONVERSATION(conv->active_conv) != PURPLE_IS_IM_CONVERSATION(all->data)) {
/* this window has different types of conversation, don't save */
return FALSE;
}
}
- if (type == PURPLE_CONV_TYPE_IM) {
+ if (PURPLE_IS_IM_CONVERSATION(conv->active_conv)) {
purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/x", x);
purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/y", y);
purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/width", event->width);
purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/height", event->height);
- } else if (type == PURPLE_CONV_TYPE_CHAT) {
+ } else if (PURPLE_IS_CHAT_CONVERSATION(conv->active_conv)) {
purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/chat/x", x);
purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/chat/y", y);
purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/chat/width", event->width);
@@ -9888,21 +10903,24 @@ conv_placement_last_created_win_type_configured_cb(GtkWidget *w,
static void
conv_placement_last_created_win_type(PidginConversation *conv)
{
- PidginWindow *win;
+ PidginConvWindow *win;
- win = pidgin_conv_window_last_with_type(purple_conversation_get_type(conv->active_conv));
+ if (PURPLE_IS_IM_CONVERSATION(conv->active_conv))
+ win = pidgin_conv_window_last_im();
+ else
+ win = pidgin_conv_window_last_chat();
if (win == NULL) {
win = pidgin_conv_window_new();
- if (PURPLE_CONV_TYPE_IM == purple_conversation_get_type(conv->active_conv) ||
+ if (PURPLE_IS_IM_CONVERSATION(conv->active_conv) ||
purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/width") == 0) {
pidgin_conv_set_position_size(win,
purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/x"),
purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/y"),
purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/width"),
purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/height"));
- } else if (PURPLE_CONV_TYPE_CHAT == purple_conversation_get_type(conv->active_conv)) {
+ } else if (PURPLE_IS_CHAT_CONVERSATION(conv->active_conv)) {
pidgin_conv_set_position_size(win,
purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/x"),
purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/y"),
@@ -9923,7 +10941,7 @@ conv_placement_last_created_win_type(PidginConversation *conv)
static void
conv_placement_new_window(PidginConversation *conv)
{
- PidginWindow *win;
+ PidginConvWindow *win;
win = pidgin_conv_window_new();
@@ -9940,16 +10958,16 @@ conv_get_group(PidginConversation *conv)
{
PurpleGroup *group = NULL;
- if (purple_conversation_get_type(conv->active_conv) == PURPLE_CONV_TYPE_IM) {
+ if (PURPLE_IS_IM_CONVERSATION(conv->active_conv)) {
PurpleBuddy *buddy;
- buddy = purple_find_buddy(purple_conversation_get_account(conv->active_conv),
+ buddy = purple_blist_find_buddy(purple_conversation_get_account(conv->active_conv),
purple_conversation_get_name(conv->active_conv));
if (buddy != NULL)
group = purple_buddy_get_group(buddy);
- } else if (purple_conversation_get_type(conv->active_conv) == PURPLE_CONV_TYPE_CHAT) {
+ } else if (PURPLE_IS_CHAT_CONVERSATION(conv->active_conv)) {
PurpleChat *chat;
chat = purple_blist_find_chat(purple_conversation_get_account(conv->active_conv),
@@ -9977,7 +10995,7 @@ conv_placement_by_group(PidginConversation *conv)
/* Go through the list of IMs and find one with this group. */
for (wl = pidgin_conv_windows_get_list(); wl != NULL; wl = wl->next) {
- PidginWindow *win2;
+ PidginConvWindow *win2;
PidginConversation *conv2;
PurpleGroup *group2 = NULL;
@@ -10013,7 +11031,7 @@ conv_placement_by_account(PidginConversation *conv)
/* Go through the list of IMs and find one with this group. */
for (wins = pidgin_conv_windows_get_list(); wins != NULL; wins = wins->next) {
- PidginWindow *win2;
+ PidginConvWindow *win2;
PidginConversation *conv2;
win2 = wins->data;
@@ -10195,7 +11213,7 @@ pidgin_conv_is_hidden(PidginConversation *gtkconv)
/* Algorithm from http://www.w3.org/TR/AERT#color-contrast */
static gboolean
-color_is_visible(GdkColor foreground, GdkColor background, int color_contrast, int brightness_contrast)
+color_is_visible(GdkColor foreground, GdkColor background, guint color_contrast, guint brightness_contrast)
{
gulong fg_brightness;
gulong bg_brightness;
@@ -10227,12 +11245,11 @@ color_is_visible(GdkColor foreground, GdkColor background, int color_contrast, i
}
-static GdkColor*
-generate_nick_colors(guint *color_count, GdkColor background)
+static GArray*
+generate_nick_colors(guint numcolors, GdkColor background)
{
- guint numcolors = *color_count;
guint i = 0, j = 0;
- GdkColor *colors = g_new(GdkColor, numcolors);
+ GArray *colors = g_array_new(FALSE, FALSE, sizeof(GdkColor));
GdkColor nick_highlight;
GdkColor send_color;
time_t breakout_time;
@@ -10248,7 +11265,7 @@ generate_nick_colors(guint *color_count, GdkColor background)
* list. only some of them will differ from the background color though. lets see if we can find
* numcolors of them that do
*/
- while (i < numcolors && j < NUM_NICK_SEED_COLORS && time(NULL) < breakout_time)
+ while (i < numcolors && j < PIDGIN_NUM_NICK_SEED_COLORS && time(NULL) < breakout_time)
{
GdkColor color = nick_seed_colors[j];
@@ -10256,7 +11273,7 @@ generate_nick_colors(guint *color_count, GdkColor background)
color_is_visible(color, nick_highlight, MIN_COLOR_CONTRAST / 2, 0) &&
color_is_visible(color, send_color, MIN_COLOR_CONTRAST / 4, 0))
{
- colors[i] = color;
+ g_array_append_val(colors, color);
i++;
}
j++;
@@ -10275,18 +11292,51 @@ generate_nick_colors(guint *color_count, GdkColor background)
color_is_visible(color, nick_highlight, MIN_COLOR_CONTRAST / 2, 0) &&
color_is_visible(color, send_color, MIN_COLOR_CONTRAST / 4, 0))
{
- colors[i] = color;
+ g_array_append_val(colors, color);
i++;
}
}
if (i < numcolors) {
- GdkColor *c = colors;
purple_debug_warning("gtkconv", "Unable to generate enough random colors before timeout. %u colors found.\n", i);
- colors = g_memdup(c, i * sizeof(GdkColor));
- g_free(c);
- *color_count = i;
}
return colors;
}
+
+/**************************************************************************
+ * PidginConvWindow GBoxed code
+ **************************************************************************/
+static PidginConvWindow *
+pidgin_conv_window_ref(PidginConvWindow *win)
+{
+ g_return_val_if_fail(win != NULL, NULL);
+
+ win->box_count++;
+
+ return win;
+}
+
+static void
+pidgin_conv_window_unref(PidginConvWindow *win)
+{
+ g_return_if_fail(win != NULL);
+ g_return_if_fail(win->box_count >= 0);
+
+ if (!win->box_count--)
+ pidgin_conv_window_destroy(win);
+}
+
+GType
+pidgin_conv_window_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ type = g_boxed_type_register_static("PidginConvWindow",
+ (GBoxedCopyFunc)pidgin_conv_window_ref,
+ (GBoxedFreeFunc)pidgin_conv_window_unref);
+ }
+
+ return type;
+}
diff --git a/pidgin/gtkconv.h b/pidgin/gtkconv.h
index a952e73b81..76df96fb6c 100644
--- a/pidgin/gtkconv.h
+++ b/pidgin/gtkconv.h
@@ -1,9 +1,3 @@
-/**
- * @file gtkconv.h GTK+ Conversation API
- * @ingroup pidgin
- * @see @ref gtkconv-signals
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -24,23 +18,40 @@
* 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_CONVERSATION_H_
#define _PIDGIN_CONVERSATION_H_
+/**
+ * SECTION:gtkconv
+ * @section_id: pidgin-gtkconv
+ * @short_description: <filename>gtkconv.h</filename>
+ * @title: Conversation API
+ * @see_also: <link linkend="chapter-signals-gtkconv">Conversation signals</link>
+ */
+
+#define PIDGIN_TYPE_CONVERSATION (pidgin_conversation_get_type())
typedef struct _PidginImPane PidginImPane;
typedef struct _PidginChatPane PidginChatPane;
typedef struct _PidginConversation PidginConversation;
/**
+ * PidginUnseenState:
+ * @PIDGIN_UNSEEN_NONE: No unseen text in the conversation.
+ * @PIDGIN_UNSEEN_EVENT: Unseen events in the conversation.
+ * @PIDGIN_UNSEEN_NO_LOG: Unseen text with NO_LOG flag.
+ * @PIDGIN_UNSEEN_TEXT: Unseen text in the conversation.
+ * @PIDGIN_UNSEEN_NICK: Unseen text and the nick was said.
+ *
* Unseen text states.
*/
typedef enum
{
- PIDGIN_UNSEEN_NONE, /**< No unseen text in the conversation. */
- PIDGIN_UNSEEN_EVENT, /**< Unseen events in the conversation. */
- PIDGIN_UNSEEN_NO_LOG, /**< Unseen text with NO_LOG flag. */
- PIDGIN_UNSEEN_TEXT, /**< Unseen text in the conversation. */
- PIDGIN_UNSEEN_NICK /**< Unseen text and the nick was said. */
+ PIDGIN_UNSEEN_NONE,
+ PIDGIN_UNSEEN_EVENT,
+ PIDGIN_UNSEEN_NO_LOG,
+ PIDGIN_UNSEEN_TEXT,
+ PIDGIN_UNSEEN_NICK
} PidginUnseenState;
enum {
@@ -51,12 +62,12 @@ enum {
CHAT_USERS_FLAGS_COLUMN,
CHAT_USERS_COLOR_COLUMN,
CHAT_USERS_WEIGHT_COLUMN,
- CHAT_USERS_ICON_STOCK_COLUMN, /** @since 2.6.0 */
+ CHAT_USERS_ICON_STOCK_COLUMN,
CHAT_USERS_COLUMNS
};
#define PIDGIN_CONVERSATION(conv) \
- ((PidginConversation *)(conv)->ui_data)
+ ((PidginConversation *)purple_conversation_get_ui_data(conv))
#define PIDGIN_IS_PIDGIN_CONVERSATION(conv) \
(purple_conversation_get_ui_ops(conv) == \
@@ -65,75 +76,42 @@ enum {
#include "pidgin.h"
#include "conversation.h"
#include "gtkconvwin.h"
+#include "gtkconv-theme.h"
/**************************************************************************
- * @name Structures
+ * Structures
**************************************************************************/
-/*@{*/
-
-/**
- * A GTK+ representation of a graphical window containing one or more
- * conversations.
- */
-
-/**
- * A GTK+ Instant Message pane.
- */
-struct _PidginImPane
-{
- GtkWidget *block;
- GtkWidget *send_file;
- GtkWidget *sep1;
- GtkWidget *sep2;
- GtkWidget *check;
- GtkWidget *progress;
- guint32 typing_timer;
-
- /* Buddy icon stuff */
- GtkWidget *icon_container;
- GtkWidget *icon;
- gboolean show_icon;
- gboolean animate;
- GdkPixbufAnimation *anim;
- GdkPixbufAnimationIter *iter;
- guint32 icon_timer;
-};
-
-/**
- * GTK+ Chat panes.
- */
-struct _PidginChatPane
-{
- GtkWidget *count;
- GtkWidget *list;
- GtkWidget *topic_text;
-};
/**
+ * PidginConversation:
+ *
* A GTK+ conversation pane.
*/
struct _PidginConversation
{
+ gint box_count;
+
PurpleConversation *active_conv;
+ PurpleConversation *last_conversed;
GList *convs;
GList *send_history;
- PidginWindow *win;
+ PidginConvWindow *win;
gboolean make_sound;
- GtkTooltips *tooltips;
-
GtkWidget *tab_cont;
GtkWidget *tabby;
GtkWidget *menu_tabby;
- GtkWidget *imhtml;
- GtkTextBuffer *entry_buffer;
+ PidginConvTheme *theme;
+ GArray *nick_colors;
+ PurpleMessageFlags last_flags;
+ GtkWidget *webview;
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
+ * 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
@@ -144,17 +122,9 @@ struct _PidginConversation
GtkWidget *tab_label;
GtkWidget *menu_icon;
GtkWidget *menu_label;
-#if !(defined PIDGIN_DISABLE_DEPRECATED) || (defined _PIDGIN_GTKCONV_C_)
- /** @deprecated */
- GtkSizeGroup *sg;
-#else
- gpointer depr1;
-#endif
GtkWidget *lower_hbox;
- GtkWidget *toolbar;
-
PidginUnseenState unseen_state;
guint unseen_count;
@@ -173,136 +143,238 @@ struct _PidginConversation
/* Used when attaching a PidginConversation to a PurpleConversation
* with message history */
- struct {
- int timer;
- GList *current;
- } attach;
+ int attach_timer;
+ GList *attach_current;
- /**
+ /*
* Quick Find.
- *
- * @since 2.7.0
*/
- struct {
- GtkWidget *entry;
- GtkWidget *container;
- } quickfind;
+ GtkWidget *quickfind_entry;
+ GtkWidget *quickfind_container;
};
-/*@}*/
+G_BEGIN_DECLS
/**************************************************************************
- * @name GTK+ Conversation API
+ * GTK+ Conversation API
**************************************************************************/
-/*@{*/
/**
+ * pidgin_conversation_get_type:
+ *
+ * Returns: The #GType for the #PidginConversation boxed structure.
+ */
+GType pidgin_conversation_get_type(void);
+
+/**
+ * pidgin_conversations_get_conv_ui_ops:
+ *
* Returns the UI operations structure for GTK+ conversations.
*
- * @return The GTK+ conversation operations structure.
+ * Returns: The GTK+ conversation operations structure.
*/
PurpleConversationUiOps *pidgin_conversations_get_conv_ui_ops(void);
/**
- * Updates the buddy icon on a conversation.
+ * pidgin_conversations_get_default_theme:
+ *
+ * Returns the default theme for GTK+ conversations.
*
- * @param conv The conversation.
+ * Returns: The default GTK+ conversation theme.
*/
-void pidgin_conv_update_buddy_icon(PurpleConversation *conv);
+PurpleTheme *pidgin_conversations_get_default_theme(void);
/**
- * Sets the active conversation within a GTK-conversation.
+ * pidgin_conv_update_buddy_icon:
+ * @im: The IM conversation.
*
- * @param conv The conversation
+ * Updates the buddy icon on a conversation.
+ */
+void pidgin_conv_update_buddy_icon(PurpleIMConversation *im);
+
+/**
+ * pidgin_conv_switch_active_conversation:
+ * @conv: The conversation
+ *
+ * Sets the active conversation within a GTK-conversation.
*/
void pidgin_conv_switch_active_conversation(PurpleConversation *conv);
/**
- * Updates conversation buttons by protocol.
+ * pidgin_conv_update_buttons_by_protocol:
+ * @conv: The conversation.
*
- * @param conv The conversation.
+ * Updates conversation buttons by protocol.
*/
void pidgin_conv_update_buttons_by_protocol(PurpleConversation *conv);
/**
- * Returns a list of conversations of the given type which have an unseen
+ * pidgin_conversations_get_unseen_all:
+ * @min_state: The minimum unseen state.
+ * @hidden_only: If %TRUE, only consider hidden conversations.
+ * @max_count: Maximum number of conversations to return, or 0 for
+ * no maximum.
+ *
+ * Returns a list of conversations of any type which have an unseen
* state greater than or equal to the specified minimum state. Using the
* hidden_only parameter, this search can be limited to hidden
* conversations. The max_count parameter will limit the total number of
* converations returned if greater than zero. The returned list should
* be freed by the caller.
*
- * @param type The type of conversation.
- * @param min_state The minimum unseen state.
- * @param hidden_only If TRUE, only consider hidden conversations.
- * @param max_count Maximum number of conversations to return, or 0 for
- * no maximum.
- * @return List of PurpleConversation matching criteria, or NULL.
+ * Returns: List of PurpleConversation matching criteria, or %NULL.
*/
GList *
-pidgin_conversations_find_unseen_list(PurpleConversationType type,
- PidginUnseenState min_state,
+pidgin_conversations_get_unseen_all(PidginUnseenState min_state,
gboolean hidden_only,
guint max_count);
/**
+ * pidgin_conversations_get_unseen_ims:
+ * @min_state: The minimum unseen state.
+ * @hidden_only: If %TRUE, only consider hidden conversations.
+ * @max_count: Maximum number of conversations to return, or 0 for
+ * no maximum.
+ *
+ * Returns a list of IM conversations which have an unseen state greater
+ * than or equal to the specified minimum state. Using the hidden_only
+ * parameter, this search can be limited to hidden IM conversations. The
+ * max_count parameter will limit the total number of IM converations
+ * returned if greater than zero. The returned list should be freed by the
+ * caller.
+ *
+ * Returns: List of PurpleIMConversation matching criteria, or %NULL.
+ */
+GList *
+pidgin_conversations_get_unseen_ims(PidginUnseenState min_state,
+ gboolean hidden_only,
+ guint max_count);
+
+/**
+ * pidgin_conversations_get_unseen_chats:
+ * @min_state: The minimum unseen state.
+ * @hidden_only: If %TRUE, only consider hidden conversations.
+ * @max_count: Maximum number of conversations to return, or 0 for
+ * no maximum.
+ *
+ * Returns a list of chat conversations which have an unseen state greater
+ * than or equal to the specified minimum state. Using the hidden_only
+ * parameter, this search can be limited to hidden chat conversations. The
+ * max_count parameter will limit the total number of chat converations
+ * returned if greater than zero. The returned list should be freed by the
+ * caller.
+ *
+ * Returns: List of PurpleChatConversation matching criteria, or %NULL.
+ */
+GList *
+pidgin_conversations_get_unseen_chats(PidginUnseenState min_state,
+ gboolean hidden_only,
+ guint max_count);
+
+/**
+ * pidgin_conversations_fill_menu:
+ * @menu: Menu widget to add items to.
+ * @convs: List of PurpleConversation to add to menu.
+ *
* Fill a menu with a list of conversations. Clicking the conversation
* menu item will present that conversation to the user.
*
- * @param menu Menu widget to add items to.
- * @param convs List of PurpleConversation to add to menu.
- * @return Number of conversations added to menu.
+ * Returns: Number of conversations added to menu.
*/
guint
pidgin_conversations_fill_menu(GtkWidget *menu, GList *convs);
/**
- * Presents a purple conversation to the user.
+ * pidgin_conv_present_conversation:
+ * @conv: The conversation.
*
- * @param conv The conversation.
+ * Presents a purple conversation to the user.
*/
void pidgin_conv_present_conversation(PurpleConversation *conv);
/**
- * Reattach Pidgin UI to a conversation.
- *
- * @param conv The conversation.
+ * pidgin_conv_attach_to_conversation:
+ * @conv: The conversation.
*
- * @return Wheter Pidgin UI was successfully attached.
+ * Reattach Pidgin UI to a conversation.
*
- * @since 2.2.0
+ * Returns: Wheter Pidgin UI was successfully attached.
*/
gboolean pidgin_conv_attach_to_conversation(PurpleConversation *conv);
-PidginWindow *pidgin_conv_get_window(PidginConversation *gtkconv);
+/**
+ * pidgin_conv_get_window:
+ * @gtkconv: The GTK+ conversation.
+ *
+ * Returns: The window the conversation belongs to.
+ */
+PidginConvWindow *pidgin_conv_get_window(PidginConversation *gtkconv);
+
+/**
+ * pidgin_conv_get_tab_icon:
+ * @conv: The conversation.
+ * @small_icon: Whether to get the small icon.
+ *
+ * Returns: The tab icon.
+ */
GdkPixbuf *pidgin_conv_get_tab_icon(PurpleConversation *conv, gboolean small_icon);
+
+/**
+ * pidgin_conv_new:
+ * @conv: The conversation.
+ *
+ * Creates a new GTK+ conversation for a given #PurpleConversation.
+ */
void pidgin_conv_new(PurpleConversation *conv);
-int pidgin_conv_get_tab_at_xy(PidginWindow *win, int x, int y, gboolean *to_right);
+
+/**
+ * pidgin_conv_get_tab_at_xy:
+ * @win: The conversation window.
+ * @x: X-coordinate to look up.
+ * @y: Y-coordinate to look up.
+ * @to_right: (out): Return address for a boolean which will be %TRUE if the
+ * coordinates are on the right side of the tab (or bottom in case of
+ * a vertical notebook), %FALSE otherwise.
+ *
+ * Returns: The tab index of a conversation in @win at (@x, @y).
+ */
+int pidgin_conv_get_tab_at_xy(PidginConvWindow *win, int x, int y, gboolean *to_right);
+
+/**
+ * pidgin_conv_is_hidden:
+ * @gtkconv: The GTK+ conversation.
+ *
+ * Returns: %TRUE if the conversation is hidden, %FALSE otherwise.
+ */
gboolean pidgin_conv_is_hidden(PidginConversation *gtkconv);
-/*@}*/
/**************************************************************************/
-/** @name GTK+ Conversations Subsystem */
+/* GTK+ Conversations Subsystem */
/**************************************************************************/
-/*@{*/
/**
+ * pidgin_conversations_get_handle:
+ *
* Returns the gtk conversations subsystem handle.
*
- * @return The conversations subsystem handle.
+ * Returns: The conversations subsystem handle.
*/
void *pidgin_conversations_get_handle(void);
/**
+ * pidgin_conversations_init:
+ *
* Initializes the GTK+ conversations subsystem.
*/
void pidgin_conversations_init(void);
/**
+ * pidgin_conversations_uninit:
+ *
* Uninitialized the GTK+ conversation subsystem.
*/
void pidgin_conversations_uninit(void);
-/*@}*/
+G_END_DECLS
#endif /* _PIDGIN_CONVERSATION_H_ */
diff --git a/pidgin/gtkconvwin.h b/pidgin/gtkconvwin.h
index 986b1ca28f..5e59b49124 100644
--- a/pidgin/gtkconvwin.h
+++ b/pidgin/gtkconvwin.h
@@ -1,8 +1,3 @@
-/**
- * @file gtkconvwin.h GTK+ Conversation Window API
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -23,68 +18,87 @@
* 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_CONVERSATION_WINDOW_H_
#define _PIDGIN_CONVERSATION_WINDOW_H_
+/**
+ * SECTION:gtkconvwin
+ * @section_id: pidgin-gtkconvwin
+ * @short_description: <filename>gtkconvwin.h</filename>
+ * @title: Conversation Window API
+ */
-typedef struct _PidginWindow PidginWindow;
+#define PIDGIN_TYPE_CONV_WINDOW (pidgin_conv_window_get_type())
+
+typedef struct _PidginConvWindowMenu PidginConvWindowMenu;
+typedef struct _PidginConvWindow PidginConvWindow;
/**************************************************************************
- * @name Structures
+ * Structures
**************************************************************************/
-/*@{*/
-/**
- * A GTK+ representation of a graphical window containing one or more
- * conversations.
- */
-struct _PidginWindow
+struct _PidginConvWindowMenu
{
- GtkWidget *window; /**< The window. */
- GtkWidget *notebook; /**< The notebook of conversations. */
- GList *gtkconvs;
+ GtkUIManager *ui;
+ GtkWidget *menubar;
- struct
- {
- GtkWidget *menubar;
+ GtkAction *view_log;
- GtkWidget *view_log;
+ GtkAction *audio_call;
+ GtkAction *video_call;
+ GtkAction *audio_video_call;
- GtkWidget *send_file;
- GtkWidget *add_pounce;
- GtkWidget *get_info;
- GtkWidget *invite;
+ GtkAction *send_file;
+ GtkAction *get_attention;
+ GtkAction *add_pounce;
+ GtkAction *get_info;
+ GtkAction *invite;
- GtkWidget *alias;
- GtkWidget *block;
- GtkWidget *unblock;
- GtkWidget *add;
- GtkWidget *remove;
+ GtkAction *alias;
+ GtkAction *block;
+ GtkAction *unblock;
+ GtkAction *add;
+ GtkAction *remove;
- GtkWidget *insert_link;
- GtkWidget *insert_image;
+ GtkAction *insert_link;
+ GtkAction *insert_image;
- GtkWidget *logging;
- GtkWidget *sounds;
- GtkWidget *show_formatting_toolbar;
- GtkWidget *show_timestamps;
- GtkWidget *show_icon;
+ GtkAction *logging;
+ GtkAction *sounds;
+ GtkAction *show_formatting_toolbar;
- GtkWidget *send_to;
+ GtkWidget *send_to;
+ GtkWidget *e2ee;
- GtkWidget *tray;
+ GtkWidget *tray;
- GtkWidget *typing_icon;
-
- GtkItemFactory *item_factory;
-
- } menu;
+ GtkWidget *typing_icon;
+};
- struct
- {
- GtkWidget *search;
+/**
+ * PidginConvWindow:
+ * @window: The window.
+ * @notebook: The notebook of conversations.
+ * @notebook_menu: The menu on the notebook.
+ * @clicked_tab: The menu currently clicked.
+ *
+ * A GTK+ representation of a graphical window containing one or more
+ * conversations.
+ */
+struct _PidginConvWindow
+{
+ /*< private >*/
+ gint box_count;
+
+ /*< public >*/
+ GtkWidget *window;
+ GtkWidget *notebook;
+ GtkWidget *notebook_menu;
+ PidginConversation *clicked_tab;
+ GList *gtkconvs;
- } dialogs;
+ PidginConvWindowMenu *menu;
/* Tab dragging stuff. */
gboolean in_drag;
@@ -96,59 +110,210 @@ struct _PidginWindow
gint drag_motion_signal;
gint drag_leave_signal;
-
- /* Media menu options. */
- GtkWidget *audio_call;
- GtkWidget *video_call;
- GtkWidget *audio_video_call;
};
-/*@}*/
+G_BEGIN_DECLS
/**************************************************************************
- * @name GTK+ Conversation Window API
+ * GTK+ Conversation Window API
**************************************************************************/
-/*@{*/
-PidginWindow * pidgin_conv_window_new(void);
-void pidgin_conv_window_destroy(PidginWindow *win);
+/**
+ * pidgin_conv_window_get_type:
+ *
+ * Returns: The #GType for the #PidginConvWindow boxed structure.
+ */
+GType pidgin_conv_window_get_type(void);
+
+/**
+ * pidgin_conv_window_new:
+ *
+ * Returns: A new #PidginConvWindow.
+ */
+PidginConvWindow * pidgin_conv_window_new(void);
+
+/**
+ * pidgin_conv_window_destroy:
+ * @win: The conversation window to destroy
+ */
+void pidgin_conv_window_destroy(PidginConvWindow *win);
+
+/**
+ * pidgin_conv_windows_get_list:
+ *
+ * Returns: (transfer none) (element-type Pidgin.ConvWindow): The list of
+ * windows.
+ */
GList *pidgin_conv_windows_get_list(void);
-void pidgin_conv_window_show(PidginWindow *win);
-void pidgin_conv_window_hide(PidginWindow *win);
-void pidgin_conv_window_raise(PidginWindow *win);
-void pidgin_conv_window_switch_gtkconv(PidginWindow *win, PidginConversation *gtkconv);
-void pidgin_conv_window_add_gtkconv(PidginWindow *win, PidginConversation *gtkconv);
-void pidgin_conv_window_remove_gtkconv(PidginWindow *win, PidginConversation *gtkconv);
-PidginConversation *pidgin_conv_window_get_gtkconv_at_index(const PidginWindow *win, int index);
-PidginConversation *pidgin_conv_window_get_active_gtkconv(const PidginWindow *win);
-PurpleConversation *pidgin_conv_window_get_active_conversation(const PidginWindow *win);
+
+/**
+ * pidgin_conv_window_show:
+ * @win: The conversation window to show
+ */
+void pidgin_conv_window_show(PidginConvWindow *win);
+
+/**
+ * pidgin_conv_window_hide:
+ * @win: The conversation window to hide
+ */
+void pidgin_conv_window_hide(PidginConvWindow *win);
+
+/**
+ * pidgin_conv_window_raise:
+ * @win: The conversation window to raise
+ */
+void pidgin_conv_window_raise(PidginConvWindow *win);
+
+/**
+ * pidgin_conv_window_switch_gtkconv:
+ * @win: The conversation window
+ * @gtkconv: The pidgin conversation to switch to
+ */
+void pidgin_conv_window_switch_gtkconv(PidginConvWindow *win, PidginConversation *gtkconv);
+
+/**
+ * pidgin_conv_window_add_gtkconv:
+ * @win: The conversation window
+ * @gtkconv: The pidgin conversation to add
+ */
+void pidgin_conv_window_add_gtkconv(PidginConvWindow *win, PidginConversation *gtkconv);
+
+/**
+ * pidgin_conv_window_remove_gtkconv:
+ * @win: The conversation window
+ * @gtkconv: The pidgin conversation to remove
+ */
+void pidgin_conv_window_remove_gtkconv(PidginConvWindow *win, PidginConversation *gtkconv);
+
+/**
+ * pidgin_conv_window_get_gtkconv_at_index:
+ * @win: The conversation window
+ * @index: The index in the window to get the conversation from
+ *
+ * Returns: The conversation in @win at @index
+ */
+PidginConversation *pidgin_conv_window_get_gtkconv_at_index(const PidginConvWindow *win, int index);
+
+/**
+ * pidgin_conv_window_get_active_gtkconv:
+ * @win: The conversation window
+ *
+ * Returns: The active #PidginConversation in @win.
+ */
+PidginConversation *pidgin_conv_window_get_active_gtkconv(const PidginConvWindow *win);
+
+/**
+ * pidgin_conv_window_get_active_conversation:
+ * @win: The conversation window
+ *
+ * Returns: The active #PurpleConversation in @win.
+ */
+PurpleConversation *pidgin_conv_window_get_active_conversation(const PidginConvWindow *win);
+
+/**
+ * pidgin_conv_window_is_active_conversation:
+ * @conv: The conversation
+ *
+ * Returns: %TRUE if @conv is the active conversation, %FALSE otherwise.
+ */
gboolean pidgin_conv_window_is_active_conversation(const PurpleConversation *conv);
-gboolean pidgin_conv_window_has_focus(PidginWindow *win);
-PidginWindow *pidgin_conv_window_get_at_xy(int x, int y);
-GList *pidgin_conv_window_get_gtkconvs(PidginWindow *win);
-guint pidgin_conv_window_get_gtkconv_count(PidginWindow *win);
-PidginWindow *pidgin_conv_window_first_with_type(PurpleConversationType type);
-PidginWindow *pidgin_conv_window_last_with_type(PurpleConversationType type);
+/**
+ * pidgin_conv_window_has_focus:
+ * @win: The conversation window
+ *
+ * Returns: %TRUE if @win has focus, %FALSE otherwise.
+ */
+gboolean pidgin_conv_window_has_focus(PidginConvWindow *win);
+
+/**
+ * pidgin_conv_window_get_at_event:
+ * @event: The event
+ *
+ * Returns: The #PidginConvWindow on which @event occured.
+ */
+PidginConvWindow *pidgin_conv_window_get_at_event(GdkEvent *event);
+
+/**
+ * pidgin_conv_window_get_gtkconvs:
+ * @win: The conversation window
+ *
+ * Returns: (transfer none) (element-type Pidgin.Conversation): A list of
+ * #PidginConversation's in @win.
+ */
+GList *pidgin_conv_window_get_gtkconvs(PidginConvWindow *win);
+
+/**
+ * pidgin_conv_window_get_gtkconv_count:
+ * @win: The conversation window
+ *
+ * Returns: The number of conversations in @win
+ */
+guint pidgin_conv_window_get_gtkconv_count(PidginConvWindow *win);
+
+/**
+ * pidgin_conv_window_first_im:
+ *
+ * Returns: The window which has the first IM, %NULL if no IM is found.
+ */
+PidginConvWindow *pidgin_conv_window_first_im(void);
+
+/**
+ * pidgin_conv_window_last_im:
+ *
+ * Returns: The window which has the last IM, %NULL if no IM is found.
+ */
+PidginConvWindow *pidgin_conv_window_last_im(void);
-/*@}*/
+/**
+ * pidgin_conv_window_first_chat:
+ *
+ * Returns: The window which has the first chat, %NULL if no chat is found.
+ */
+PidginConvWindow *pidgin_conv_window_first_chat(void);
+
+/**
+ * pidgin_conv_window_last_chat:
+ *
+ * Returns: The window which has the last chat, %NULL if no chat is found.
+ */
+PidginConvWindow *pidgin_conv_window_last_chat(void);
/**************************************************************************
- * @name GTK+ Conversation Placement API
+ * GTK+ Conversation Placement API
**************************************************************************/
-/*@{*/
typedef void (*PidginConvPlacementFunc)(PidginConversation *);
GList *pidgin_conv_placement_get_options(void);
+const char *pidgin_conv_placement_get_name(const char *id);
+void pidgin_conv_placement_place(PidginConversation *gtkconv);
+
+/**
+ * pidgin_conv_placement_add_fnc: (skip)
+ */
void pidgin_conv_placement_add_fnc(const char *id, const char *name, PidginConvPlacementFunc fnc);
+
+/**
+ * pidgin_conv_placement_remove_fnc: (skip)
+ */
void pidgin_conv_placement_remove_fnc(const char *id);
-const char *pidgin_conv_placement_get_name(const char *id);
+
+/**
+ * pidgin_conv_placement_get_fnc: (skip)
+ */
PidginConvPlacementFunc pidgin_conv_placement_get_fnc(const char *id);
+
+/**
+ * pidgin_conv_placement_set_current_func: (skip)
+ */
void pidgin_conv_placement_set_current_func(PidginConvPlacementFunc func);
+
+/**
+ * pidgin_conv_placement_get_current_func: (skip)
+ */
PidginConvPlacementFunc pidgin_conv_placement_get_current_func(void);
-void pidgin_conv_placement_place(PidginConversation *gtkconv);
-/*@}*/
+G_END_DECLS
#endif /* _PIDGIN_CONVERSATION_WINDOW_H_ */
diff --git a/pidgin/gtkdebug.c b/pidgin/gtkdebug.c
index 895ba5b32d..926a4a95c5 100644
--- a/pidgin/gtkdebug.c
+++ b/pidgin/gtkdebug.c
@@ -1,8 +1,3 @@
-/**
- * @file gtkdebug.c GTK+ Debug API
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -33,72 +28,44 @@
#include "gtkdebug.h"
#include "gtkdialogs.h"
-#include "gtkimhtml.h"
#include "gtkutils.h"
+#include "gtkwebview.h"
#include "pidginstock.h"
-#ifdef HAVE_REGEX_H
-# include <regex.h>
-# define USE_REGEX 1
-#else
-#if GLIB_CHECK_VERSION(2,14,0)
-# define USE_REGEX 1
+#ifdef ENABLE_GLIBTRACE
+#include <execinfo.h>
#endif
-#endif /* HAVE_REGEX_H */
#include <gdk/gdkkeysyms.h>
+#include "gtk3compat.h"
+
+#include "gtkdebug.html.h"
+
typedef struct
{
GtkWidget *window;
GtkWidget *text;
-
- GtkListStore *store;
-
- gboolean paused;
-
-#ifdef USE_REGEX
GtkWidget *filter;
GtkWidget *expression;
+ GtkWidget *filterlevel;
+
+ gboolean paused;
gboolean invert;
gboolean highlight;
-
guint timer;
-# ifdef HAVE_REGEX_H
- regex_t regex;
-# else
GRegex *regex;
-# endif /* HAVE_REGEX_H */
-#else
- GtkWidget *find;
-#endif /* USE_REGEX */
- GtkWidget *filterlevel;
} DebugWindow;
-static const char debug_fg_colors[][8] = {
- "#000000", /**< All debug levels. */
- "#666666", /**< Misc. */
- "#000000", /**< Information. */
- "#660000", /**< Warnings. */
- "#FF0000", /**< Errors. */
- "#FF0000", /**< Fatal errors. */
-};
-
static DebugWindow *debug_win = NULL;
static guint debug_enabled_timer = 0;
-#ifdef USE_REGEX
-static void regex_filter_all(DebugWindow *win);
-static void regex_show_all(DebugWindow *win);
-#endif /* USE_REGEX */
-
static gint
debug_window_destroy(GtkWidget *w, GdkEvent *event, void *unused)
{
purple_prefs_disconnect_by_handle(pidgin_debug_get_handle());
-#ifdef USE_REGEX
if(debug_win->timer != 0) {
const gchar *text;
@@ -107,12 +74,8 @@ debug_window_destroy(GtkWidget *w, GdkEvent *event, void *unused)
text = gtk_entry_get_text(GTK_ENTRY(debug_win->expression));
purple_prefs_set_string(PIDGIN_PREFS_ROOT "/debug/regex", text);
}
-#ifdef HAVE_REGEX_H
- regfree(&debug_win->regex);
-#else
- g_regex_unref(debug_win->regex);
-#endif /* HAVE_REGEX_H */
-#endif /* USE_REGEX */
+ if (debug_win->regex != NULL)
+ g_regex_unref(debug_win->regex);
/* If the "Save Log" dialog is open then close it */
purple_request_close_with_handle(debug_win);
@@ -128,7 +91,7 @@ debug_window_destroy(GtkWidget *w, GdkEvent *event, void *unused)
static gboolean
configure_cb(GtkWidget *w, GdkEventConfigure *event, DebugWindow *win)
{
- if (GTK_WIDGET_VISIBLE(w)) {
+ if (gtk_widget_get_visible(w)) {
purple_prefs_set_int(PIDGIN_PREFS_ROOT "/debug/width", event->width);
purple_prefs_set_int(PIDGIN_PREFS_ROOT "/debug/height", event->height);
}
@@ -136,89 +99,6 @@ configure_cb(GtkWidget *w, GdkEventConfigure *event, DebugWindow *win)
return FALSE;
}
-#ifndef USE_REGEX
-struct _find {
- DebugWindow *window;
- GtkWidget *entry;
-};
-
-static void
-do_find_cb(GtkWidget *widget, gint response, struct _find *f)
-{
- switch (response) {
- case GTK_RESPONSE_OK:
- gtk_imhtml_search_find(GTK_IMHTML(f->window->text),
- gtk_entry_get_text(GTK_ENTRY(f->entry)));
- break;
-
- case GTK_RESPONSE_DELETE_EVENT:
- case GTK_RESPONSE_CLOSE:
- gtk_imhtml_search_clear(GTK_IMHTML(f->window->text));
- gtk_widget_destroy(f->window->find);
- f->window->find = NULL;
- g_free(f);
- break;
- }
-}
-
-static void
-find_cb(GtkWidget *w, DebugWindow *win)
-{
- GtkWidget *hbox, *img, *label;
- struct _find *f;
-
- if(win->find)
- {
- gtk_window_present(GTK_WINDOW(win->find));
- return;
- }
-
- f = g_malloc(sizeof(struct _find));
- f->window = win;
- win->find = gtk_dialog_new_with_buttons(_("Find"),
- GTK_WINDOW(win->window), GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
- GTK_STOCK_FIND, GTK_RESPONSE_OK, NULL);
- gtk_dialog_set_default_response(GTK_DIALOG(win->find),
- GTK_RESPONSE_OK);
- g_signal_connect(G_OBJECT(win->find), "response",
- G_CALLBACK(do_find_cb), f);
-
- gtk_container_set_border_width(GTK_CONTAINER(win->find), PIDGIN_HIG_BOX_SPACE);
- gtk_window_set_resizable(GTK_WINDOW(win->find), FALSE);
- gtk_dialog_set_has_separator(GTK_DIALOG(win->find), FALSE);
- gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(win->find)->vbox), PIDGIN_HIG_BORDER);
- gtk_container_set_border_width(
- GTK_CONTAINER(GTK_DIALOG(win->find)->vbox), PIDGIN_HIG_BOX_SPACE);
-
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
- gtk_container_add(GTK_CONTAINER(GTK_DIALOG(win->find)->vbox),
- hbox);
- img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION,
- gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
- gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
-
- gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
- gtk_dialog_set_response_sensitive(GTK_DIALOG(win->find),
- GTK_RESPONSE_OK, FALSE);
-
- label = gtk_label_new(NULL);
- gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("_Search for:"));
- gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-
- f->entry = gtk_entry_new();
- gtk_entry_set_activates_default(GTK_ENTRY(f->entry), TRUE);
- gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_WIDGET(f->entry));
- g_signal_connect(G_OBJECT(f->entry), "changed",
- G_CALLBACK(pidgin_set_sensitive_if_input),
- win->find);
- gtk_box_pack_start(GTK_BOX(hbox), f->entry, FALSE, FALSE, 0);
-
- gtk_widget_show_all(win->find);
- gtk_widget_grab_focus(f->entry);
-}
-#endif /* USE_REGEX */
-
static void
save_writefile_cb(void *user_data, const char *filename)
{
@@ -227,11 +107,11 @@ save_writefile_cb(void *user_data, const char *filename)
char *tmp;
if ((fp = g_fopen(filename, "w+")) == NULL) {
- purple_notify_error(win, NULL, _("Unable to open file."), NULL);
+ purple_notify_error(win, NULL, _("Unable to open file."), NULL, NULL);
return;
}
- tmp = gtk_imhtml_get_text(GTK_IMHTML(win->text), NULL, NULL);
+ tmp = pidgin_webview_get_body_text(PIDGIN_WEBVIEW(win->text));
fprintf(fp, "Pidgin Debug Log : %s\n", purple_date_format_full(NULL));
fprintf(fp, "%s", tmp);
g_free(tmp);
@@ -243,19 +123,13 @@ static void
save_cb(GtkWidget *w, DebugWindow *win)
{
purple_request_file(win, _("Save Debug Log"), "purple-debug.log", TRUE,
- G_CALLBACK(save_writefile_cb), NULL,
- NULL, NULL, NULL,
- win);
+ G_CALLBACK(save_writefile_cb), NULL, NULL, win);
}
static void
clear_cb(GtkWidget *w, DebugWindow *win)
{
- gtk_imhtml_clear(GTK_IMHTML(win->text));
-
-#ifdef USE_REGEX
- gtk_list_store_clear(win->store);
-#endif /* USE_REGEX */
+ pidgin_webview_safe_execute_script(PIDGIN_WEBVIEW(win->text), "clear();");
}
static void
@@ -263,253 +137,75 @@ pause_cb(GtkWidget *w, DebugWindow *win)
{
win->paused = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(w));
-#ifdef USE_REGEX
- if(!win->paused) {
- if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter)))
- regex_filter_all(win);
- else
- regex_show_all(win);
- }
-#endif /* USE_REGEX */
+ if (win->paused)
+ pidgin_webview_safe_execute_script(PIDGIN_WEBVIEW(win->text), "pauseOutput();");
+ else
+ pidgin_webview_safe_execute_script(PIDGIN_WEBVIEW(win->text), "resumeOutput();");
}
/******************************************************************************
* regex stuff
*****************************************************************************/
-#ifdef USE_REGEX
static void
regex_clear_color(GtkWidget *w) {
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context = gtk_widget_get_style_context(w);
+ gtk_style_context_remove_class(context, "good-filter");
+ gtk_style_context_remove_class(context, "bad-filter");
+#else
gtk_widget_modify_base(w, GTK_STATE_NORMAL, NULL);
+#endif
}
static void
-regex_change_color(GtkWidget *w, guint16 r, guint16 g, guint16 b) {
- GdkColor color;
-
- color.red = r;
- color.green = g;
- color.blue = b;
-
- gtk_widget_modify_base(w, GTK_STATE_NORMAL, &color);
-}
-
-static void
-regex_highlight_clear(DebugWindow *win) {
- GtkIMHtml *imhtml = GTK_IMHTML(win->text);
- GtkTextIter s, e;
-
- gtk_text_buffer_get_start_iter(imhtml->text_buffer, &s);
- gtk_text_buffer_get_end_iter(imhtml->text_buffer, &e);
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "regex", &s, &e);
-}
-
-static void
-regex_match(DebugWindow *win, const gchar *text) {
- GtkIMHtml *imhtml = GTK_IMHTML(win->text);
-#ifdef HAVE_REGEX_H
- regmatch_t matches[4]; /* adjust if necessary */
- size_t n_matches = sizeof(matches) / sizeof(matches[0]);
- gint inverted;
-#else
- GMatchInfo *match_info;
-#endif /* HAVE_REGEX_H */
- gchar *plaintext;
+regex_change_color(GtkWidget *w, gboolean success) {
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context = gtk_widget_get_style_context(w);
- if(!text)
- return;
-
- /* I don't like having to do this, but we need it for highlighting. Plus
- * it makes the ^ and $ operators work :)
- */
- plaintext = purple_markup_strip_html(text);
-
- /* we do a first pass to see if it matches at all. If it does we append
- * it, and work out the offsets to highlight.
- */
-#ifdef HAVE_REGEX_H
- inverted = (win->invert) ? REG_NOMATCH : 0;
- if(regexec(&win->regex, plaintext, n_matches, matches, 0) == inverted) {
+ if (success) {
+ gtk_style_context_add_class(context, "good-filter");
+ gtk_style_context_remove_class(context, "bad-filter");
+ } else {
+ gtk_style_context_add_class(context, "bad-filter");
+ gtk_style_context_remove_class(context, "good-filter");
+ }
#else
- if(g_regex_match(win->regex, plaintext, 0, &match_info) != win->invert) {
-#endif /* HAVE_REGEX_H */
- gchar *p = plaintext;
- GtkTextIter ins;
- gint i, offset = 0;
-
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &ins,
- gtk_text_buffer_get_insert(imhtml->text_buffer));
- i = gtk_text_iter_get_offset(&ins);
-
- gtk_imhtml_append_text(imhtml, text, 0);
-
- /* If we're not highlighting or the expression is inverted, we're
- * done and move on.
- */
- if(!win->highlight || win->invert) {
- g_free(plaintext);
-#ifndef HAVE_REGEX_H
- g_match_info_free(match_info);
-#endif
- return;
- }
-
- /* we use a do-while to highlight the first match, and then continue
- * if necessary...
- */
-#ifdef HAVE_REGEX_H
- do {
- size_t m;
-
- for(m = 0; m < n_matches; m++) {
- GtkTextIter ms, me;
-
- if(matches[m].rm_eo == -1)
- break;
-
- i += offset;
-
- gtk_text_buffer_get_iter_at_offset(imhtml->text_buffer, &ms,
- i + matches[m].rm_so);
- gtk_text_buffer_get_iter_at_offset(imhtml->text_buffer, &me,
- i + matches[m].rm_eo);
- gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "regex",
- &ms, &me);
- offset = matches[m].rm_eo;
- }
+ GdkColor color;
- p += offset;
- } while(regexec(&win->regex, p, n_matches, matches, REG_NOTBOL) == inverted);
-#else
- do
- {
- gint m;
- gint start_pos, end_pos;
- GtkTextIter ms, me;
-
- if (!g_match_info_matches(match_info))
- break;
-
- for (m = 0; m < g_match_info_get_match_count(match_info); m++)
- {
- if (m == 1)
- continue;
-
- g_match_info_fetch_pos(match_info, m, &start_pos, &end_pos);
-
- if (end_pos == -1)
- break;
-
- gtk_text_buffer_get_iter_at_offset(imhtml->text_buffer, &ms,
- i + start_pos);
- gtk_text_buffer_get_iter_at_offset(imhtml->text_buffer, &me,
- i + end_pos);
- gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "regex",
- &ms, &me);
- offset = end_pos;
- }
-
- g_match_info_free(match_info);
- p += offset;
- i += offset;
- } while (g_regex_match(win->regex, p, G_REGEX_MATCH_NOTBOL, &match_info) != win->invert);
- g_match_info_free(match_info);
-#endif /* HAVE_REGEX_H */
+ if (success) {
+ color.red = 0xAFFF;
+ color.green = 0xFFFF;
+ color.blue = 0xAFFF;
+ } else {
+ color.red = 0xFFFF;
+ color.green = 0xAFFF;
+ color.blue = 0xAFFF;
}
- g_free(plaintext);
-}
-
-static gboolean
-regex_filter_all_cb(GtkTreeModel *m, GtkTreePath *p, GtkTreeIter *iter,
- gpointer data)
-{
- DebugWindow *win = (DebugWindow *)data;
- gchar *text;
- PurpleDebugLevel level;
-
- gtk_tree_model_get(m, iter, 0, &text, 1, &level, -1);
-
- if (level >= purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/filterlevel"))
- regex_match(win, text);
-
- g_free(text);
-
- return FALSE;
-}
-
-static void
-regex_filter_all(DebugWindow *win) {
- gtk_imhtml_clear(GTK_IMHTML(win->text));
-
- if(win->highlight)
- regex_highlight_clear(win);
-
- gtk_tree_model_foreach(GTK_TREE_MODEL(win->store), regex_filter_all_cb,
- win);
-}
-
-static gboolean
-regex_show_all_cb(GtkTreeModel *m, GtkTreePath *p, GtkTreeIter *iter,
- gpointer data)
-{
- DebugWindow *win = (DebugWindow *)data;
- gchar *text;
- PurpleDebugLevel level;
-
- gtk_tree_model_get(m, iter, 0, &text, 1, &level, -1);
- if (level >= purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/filterlevel"))
- gtk_imhtml_append_text(GTK_IMHTML(win->text), text, 0);
- g_free(text);
-
- return FALSE;
-}
-
-static void
-regex_show_all(DebugWindow *win) {
- gtk_imhtml_clear(GTK_IMHTML(win->text));
-
- if(win->highlight)
- regex_highlight_clear(win);
-
- gtk_tree_model_foreach(GTK_TREE_MODEL(win->store), regex_show_all_cb,
- win);
+ gtk_widget_modify_base(w, GTK_STATE_NORMAL, &color);
+#endif
}
static void
-regex_compile(DebugWindow *win) {
- const gchar *text;
-
- text = gtk_entry_get_text(GTK_ENTRY(win->expression));
-
- if(text == NULL || *text == '\0') {
- regex_clear_color(win->expression);
- gtk_widget_set_sensitive(win->filter, FALSE);
- return;
- }
-
-#ifdef HAVE_REGEX_H
- regfree(&win->regex);
- if(regcomp(&win->regex, text, REG_EXTENDED | REG_ICASE) != 0) {
-#else
- if (win->regex)
- g_regex_unref(win->regex);
- win->regex = g_regex_new(text, G_REGEX_EXTENDED | G_REGEX_CASELESS, 0, NULL);
- if(win->regex == NULL) {
-#endif
- /* failed to compile */
- regex_change_color(win->expression, 0xFFFF, 0xAFFF, 0xAFFF);
- gtk_widget_set_sensitive(win->filter, FALSE);
- } else {
- /* compiled successfully */
- regex_change_color(win->expression, 0xAFFF, 0xFFFF, 0xAFFF);
- gtk_widget_set_sensitive(win->filter, TRUE);
+regex_toggle_filter(DebugWindow *win, gboolean filter)
+{
+ pidgin_webview_safe_execute_script(PIDGIN_WEBVIEW(win->text), "regex.clear();");
+
+ if (filter) {
+ const char *text;
+ char *regex;
+ char *script;
+
+ text = gtk_entry_get_text(GTK_ENTRY(win->expression));
+ regex = pidgin_webview_quote_js_string(text);
+ script = g_strdup_printf("regex.filterAll(%s, %s, %s);",
+ regex,
+ win->invert ? "true" : "false",
+ win->highlight ? "true" : "false");
+ pidgin_webview_safe_execute_script(PIDGIN_WEBVIEW(win->text), script);
+ g_free(script);
+ g_free(regex);
}
-
- /* we check if the filter is on in case it was only of the options that
- * got changed, and not the expression.
- */
- if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter)))
- regex_filter_all(win);
}
static void
@@ -519,11 +215,11 @@ regex_pref_filter_cb(const gchar *name, PurplePrefType type,
DebugWindow *win = (DebugWindow *)data;
gboolean active = GPOINTER_TO_INT(val), current;
- if(!win || !win->window)
+ if (!win || !win->window)
return;
current = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter));
- if(active != current)
+ if (active != current)
gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win->filter), active);
}
@@ -546,8 +242,8 @@ regex_pref_invert_cb(const gchar *name, PurplePrefType type,
win->invert = active;
- if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter)))
- regex_filter_all(win);
+ if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter)))
+ regex_toggle_filter(win, TRUE);
}
static void
@@ -559,40 +255,8 @@ regex_pref_highlight_cb(const gchar *name, PurplePrefType type,
win->highlight = active;
- if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter)))
- regex_filter_all(win);
-}
-
-static void
-regex_row_changed_cb(GtkTreeModel *model, GtkTreePath *path,
- GtkTreeIter *iter, DebugWindow *win)
-{
- gchar *text;
- PurpleDebugLevel level;
-
- if(!win || !win->window)
- return;
-
- /* If the debug window is paused, we just return since it's in the store.
- * We don't call regex_match because it doesn't make sense to check the
- * string if it's paused. When we unpause we clear the imhtml and
- * reiterate over the store to handle matches that were outputted when
- * we were paused.
- */
- if(win->paused)
- return;
-
- gtk_tree_model_get(model, iter, 0, &text, 1, &level, -1);
-
- if (level >= purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/filterlevel")) {
- if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) {
- regex_match(win, text);
- } else {
- gtk_imhtml_append_text(GTK_IMHTML(win->text), text, 0);
- }
- }
-
- g_free(text);
+ if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter)))
+ regex_toggle_filter(win, TRUE);
}
static gboolean
@@ -609,21 +273,46 @@ regex_timer_cb(DebugWindow *win) {
static void
regex_changed_cb(GtkWidget *w, DebugWindow *win) {
- if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) {
+ const gchar *text;
+
+ if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) {
gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win->filter),
FALSE);
}
- if(win->timer == 0)
+ if (win->timer == 0)
win->timer = purple_timeout_add_seconds(5, (GSourceFunc)regex_timer_cb, win);
- regex_compile(win);
+ text = gtk_entry_get_text(GTK_ENTRY(win->expression));
+
+ if (text == NULL || *text == '\0') {
+ regex_clear_color(win->expression);
+ gtk_widget_set_sensitive(win->filter, FALSE);
+ return;
+ }
+
+ if (win->regex)
+ g_regex_unref(win->regex);
+#if GLIB_CHECK_VERSION(2,34,0)
+ win->regex = g_regex_new(text, G_REGEX_CASELESS|G_REGEX_JAVASCRIPT_COMPAT, 0, NULL);
+#else
+ win->regex = g_regex_new(text, G_REGEX_CASELESS, 0, NULL);
+#endif
+ if (win->regex == NULL) {
+ /* failed to compile */
+ regex_change_color(win->expression, FALSE);
+ gtk_widget_set_sensitive(win->filter, FALSE);
+ } else {
+ /* compiled successfully */
+ regex_change_color(win->expression, TRUE);
+ gtk_widget_set_sensitive(win->filter, TRUE);
+ }
}
static void
regex_key_release_cb(GtkWidget *w, GdkEventKey *e, DebugWindow *win) {
- if(e->keyval == GDK_Return &&
- GTK_WIDGET_IS_SENSITIVE(win->filter) &&
+ if(e->keyval == GDK_KEY_Return &&
+ gtk_widget_is_sensitive(win->filter) &&
!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter)))
{
gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win->filter), TRUE);
@@ -651,35 +340,34 @@ regex_popup_cb(GtkEntry *entry, GtkWidget *menu, DebugWindow *win) {
}
static void
-regex_filter_toggled_cb(GtkToggleToolButton *button, DebugWindow *win) {
+regex_filter_toggled_cb(GtkToggleToolButton *button, DebugWindow *win)
+{
gboolean active;
active = gtk_toggle_tool_button_get_active(button);
purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/debug/filter", active);
- if(!GTK_IS_IMHTML(win->text))
+ if (!PIDGIN_IS_WEBVIEW(win->text))
return;
- if(active)
- regex_filter_all(win);
- else
- regex_show_all(win);
+ regex_toggle_filter(win, active);
}
static void
filter_level_pref_changed(const char *name, PurplePrefType type, gconstpointer value, gpointer data)
{
DebugWindow *win = data;
+ int level = GPOINTER_TO_INT(value);
+ char *tmp;
- if (GPOINTER_TO_INT(value) != gtk_combo_box_get_active(GTK_COMBO_BOX(win->filterlevel)))
- gtk_combo_box_set_active(GTK_COMBO_BOX(win->filterlevel), GPOINTER_TO_INT(value));
- if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter)))
- regex_filter_all(win);
- else
- regex_show_all(win);
+ if (level != gtk_combo_box_get_active(GTK_COMBO_BOX(win->filterlevel)))
+ gtk_combo_box_set_active(GTK_COMBO_BOX(win->filterlevel), level);
+
+ tmp = g_strdup_printf("setFilterLevel('%d');", level);
+ pidgin_webview_safe_execute_script(PIDGIN_WEBVIEW(win->text), tmp);
+ g_free(tmp);
}
-#endif /* USE_REGEX */
static void
filter_level_changed_cb(GtkWidget *combo, gpointer null)
@@ -722,14 +410,14 @@ toolbar_context(GtkWidget *toolbar, GdkEventButton *event, gpointer null)
item = gtk_check_menu_item_new_with_mnemonic(text[i]);
g_object_set_data(G_OBJECT(item), "user_data", GINT_TO_POINTER(value[i]));
g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(toolbar_icon_pref_changed), toolbar);
- if (value[i] == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/style"))
+ if (value[i] == (GtkToolbarStyle)purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/style"))
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
}
gtk_widget_show_all(menu);
- gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
+ gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
return FALSE;
}
@@ -743,8 +431,22 @@ debug_window_new(void)
gint width, height;
void *handle;
GtkToolItem *item;
-#if !GTK_CHECK_VERSION(2,12,0)
- GtkTooltips *tooltips;
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context;
+ GtkCssProvider *filter_css;
+ const gchar filter_style[] =
+ ".bad-filter {"
+ "color: @error_fg_color;"
+ "text-shadow: 0 1px @error_text_shadow;"
+ "background-image: none;"
+ "background-color: @error_bg_color;"
+ "}"
+ ".good-filter {"
+ "color: @question_fg_color;"
+ "text-shadow: 0 1px @question_text_shadow;"
+ "background-image: none;"
+ "background-color: @success_color;"
+ "}";
#endif
win = g_new0(DebugWindow, 1);
@@ -765,33 +467,13 @@ debug_window_new(void)
handle = pidgin_debug_get_handle();
-#ifdef USE_REGEX
- /* the list store for all the messages */
- win->store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
-
- /* row-changed gets called when we do gtk_list_store_set, and row-inserted
- * gets called with gtk_list_store_append, which is a
- * completely empty row. So we just ignore row-inserted, and deal with row
- * changed. -Gary
- */
- g_signal_connect(G_OBJECT(win->store), "row-changed",
- G_CALLBACK(regex_row_changed_cb), win);
-
-#endif /* USE_REGEX */
-
/* Setup the vbox */
- vbox = gtk_vbox_new(FALSE, 0);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add(GTK_CONTAINER(win->window), vbox);
if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/toolbar")) {
/* Setup our top button bar thingie. */
toolbar = gtk_toolbar_new();
-#if !GTK_CHECK_VERSION(2,12,0)
- tooltips = gtk_tooltips_new();
-#endif
-#if !GTK_CHECK_VERSION(2,14,0)
- gtk_toolbar_set_tooltips(GTK_TOOLBAR(toolbar), TRUE);
-#endif
gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolbar), TRUE);
g_signal_connect(G_OBJECT(toolbar), "button-press-event", G_CALLBACK(toolbar_context), win);
@@ -804,38 +486,17 @@ debug_window_new(void)
gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
-#ifndef USE_REGEX
- /* Find button */
- item = gtk_tool_button_new_from_stock(GTK_STOCK_FIND);
- gtk_tool_item_set_is_important(item, TRUE);
-#if GTK_CHECK_VERSION(2,12,0)
- gtk_tool_item_set_tooltip_text(item, _("Find"));
-#else
- gtk_tool_item_set_tooltip(item, tooltips, _("Find"), NULL);
-#endif
- g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(find_cb), win);
- gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
-#endif /* USE_REGEX */
-
/* Save */
item = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE);
gtk_tool_item_set_is_important(item, TRUE);
-#if GTK_CHECK_VERSION(2,12,0)
gtk_tool_item_set_tooltip_text(item, _("Save"));
-#else
- gtk_tool_item_set_tooltip(item, tooltips, _("Save"), NULL);
-#endif
g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(save_cb), win);
gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
/* Clear button */
item = gtk_tool_button_new_from_stock(GTK_STOCK_CLEAR);
gtk_tool_item_set_is_important(item, TRUE);
-#if GTK_CHECK_VERSION(2,12,0)
gtk_tool_item_set_tooltip_text(item, _("Clear"));
-#else
- gtk_tool_item_set_tooltip(item, tooltips, _("Clear"), NULL);
-#endif
g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(clear_cb), win);
gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
@@ -845,15 +506,10 @@ debug_window_new(void)
/* Pause */
item = gtk_toggle_tool_button_new_from_stock(PIDGIN_STOCK_PAUSE);
gtk_tool_item_set_is_important(item, TRUE);
-#if GTK_CHECK_VERSION(2,12,0)
gtk_tool_item_set_tooltip_text(item, _("Pause"));
-#else
- gtk_tool_item_set_tooltip(item, tooltips, _("Pause"), NULL);
-#endif
g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(pause_cb), win);
gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
-#ifdef USE_REGEX
/* regex stuff */
item = gtk_separator_tool_item_new();
gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
@@ -863,11 +519,7 @@ debug_window_new(void)
gtk_tool_item_set_is_important(item, TRUE);
win->filter = GTK_WIDGET(item);
gtk_tool_button_set_label(GTK_TOOL_BUTTON(win->filter), _("Filter"));
-#if GTK_CHECK_VERSION(2,12,0)
gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(win->filter), _("Filter"));
-#else
- gtk_tooltips_set_tip(tooltips, win->filter, _("Filter"), NULL);
-#endif
g_signal_connect(G_OBJECT(win->filter), "clicked", G_CALLBACK(regex_filter_toggled_cb), win);
gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(win->filter));
@@ -885,14 +537,19 @@ debug_window_new(void)
/* regex entry */
win->expression = gtk_entry_new();
item = gtk_tool_item_new();
-#if GTK_CHECK_VERSION(2,12,0)
gtk_widget_set_tooltip_text(win->expression, _("Right click for more options."));
-#else
- gtk_tooltips_set_tip(tooltips, win->expression, _("Right click for more options."), NULL);
-#endif
gtk_container_add(GTK_CONTAINER(item), GTK_WIDGET(win->expression));
gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
+#if GTK_CHECK_VERSION(3,0,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(win->expression);
+ gtk_style_context_add_provider(context,
+ GTK_STYLE_PROVIDER(filter_css),
+ GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+#endif
+
/* this needs to be before the text is set from the pref if we want it
* to colorize a stored expression.
*/
@@ -916,8 +573,6 @@ debug_window_new(void)
purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/debug/highlight",
regex_pref_highlight_cb, win);
-#endif /* USE_REGEX */
-
item = gtk_separator_tool_item_new();
gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
@@ -925,46 +580,36 @@ debug_window_new(void)
gtk_container_add(GTK_CONTAINER(item), gtk_label_new(_("Level ")));
gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
- win->filterlevel = gtk_combo_box_new_text();
+ win->filterlevel = gtk_combo_box_text_new();
item = gtk_tool_item_new();
-#if GTK_CHECK_VERSION(2,12,0)
gtk_widget_set_tooltip_text(win->filterlevel, _("Select the debug filter level."));
-#else
- gtk_tooltips_set_tip(tooltips, win->filterlevel, _("Select the debug filter level."), NULL);
-#endif
gtk_container_add(GTK_CONTAINER(item), win->filterlevel);
gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
- gtk_combo_box_append_text(GTK_COMBO_BOX(win->filterlevel), _("All"));
- gtk_combo_box_append_text(GTK_COMBO_BOX(win->filterlevel), _("Misc"));
- gtk_combo_box_append_text(GTK_COMBO_BOX(win->filterlevel), _("Info"));
- gtk_combo_box_append_text(GTK_COMBO_BOX(win->filterlevel), _("Warning"));
- gtk_combo_box_append_text(GTK_COMBO_BOX(win->filterlevel), _("Error "));
- gtk_combo_box_append_text(GTK_COMBO_BOX(win->filterlevel), _("Fatal Error"));
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(win->filterlevel), _("All"));
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(win->filterlevel), _("Misc"));
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(win->filterlevel), _("Info"));
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(win->filterlevel), _("Warning"));
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(win->filterlevel), _("Error "));
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(win->filterlevel), _("Fatal Error"));
gtk_combo_box_set_active(GTK_COMBO_BOX(win->filterlevel),
purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/filterlevel"));
-#ifdef USE_REGEX
+
purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/debug/filterlevel",
filter_level_pref_changed, win);
-#endif
g_signal_connect(G_OBJECT(win->filterlevel), "changed",
G_CALLBACK(filter_level_changed_cb), NULL);
}
- /* Add the gtkimhtml */
- frame = pidgin_create_imhtml(FALSE, &win->text, NULL, NULL);
- gtk_imhtml_set_format_functions(GTK_IMHTML(win->text),
- GTK_IMHTML_ALL ^ GTK_IMHTML_SMILEY ^ GTK_IMHTML_IMAGE);
+ /* Add the gtkwebview */
+ frame = pidgin_create_webview(FALSE, &win->text, NULL);
+ pidgin_webview_set_format_functions(PIDGIN_WEBVIEW(win->text),
+ PIDGIN_WEBVIEW_ALL ^ PIDGIN_WEBVIEW_SMILEY ^ PIDGIN_WEBVIEW_IMAGE);
+ pidgin_webview_load_html_string(PIDGIN_WEBVIEW(win->text), gtkdebug_html);
gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
gtk_widget_show(frame);
-#ifdef USE_REGEX
- /* add the tag for regex highlighting */
- gtk_text_buffer_create_tag(GTK_IMHTML(win->text)->text_buffer, "regex",
- "background", "#FFAFAF",
- "weight", "bold",
- NULL);
-#endif /* USE_REGEX */
+ clear_cb(NULL, win);
gtk_widget_show_all(win->window);
@@ -1027,6 +672,17 @@ pidgin_glib_log_handler(const gchar *domain, GLogLevelFlags flags,
if (new_msg != NULL)
{
+#ifdef ENABLE_GLIBTRACE
+ void *bt_buff[20];
+ size_t bt_size;
+
+ bt_size = backtrace(bt_buff, 20);
+ fprintf(stderr, "\nBacktrace for \"%s\" (%s):\n", new_msg,
+ new_domain != NULL ? new_domain : "g_log");
+ backtrace_symbols_fd(bt_buff, bt_size, STDERR_FILENO);
+ fprintf(stderr, "\n");
+#endif
+
purple_debug(level, (new_domain != NULL ? new_domain : "g_log"),
"%s\n", new_msg);
@@ -1065,13 +721,11 @@ pidgin_debug_init(void)
purple_prefs_add_int(PIDGIN_PREFS_ROOT "/debug/width", 450);
purple_prefs_add_int(PIDGIN_PREFS_ROOT "/debug/height", 250);
-#ifdef USE_REGEX
purple_prefs_add_string(PIDGIN_PREFS_ROOT "/debug/regex", "");
purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/debug/filter", FALSE);
purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/debug/invert", FALSE);
purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/debug/case_insensitive", FALSE);
purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/debug/highlight", FALSE);
-#endif /* USE_REGEX */
purple_prefs_connect_callback(NULL, PIDGIN_PREFS_ROOT "/debug/enabled",
debug_enabled_cb, NULL);
@@ -1090,6 +744,7 @@ pidgin_debug_init(void)
REGISTER_G_LOG_HANDLER("GModule");
REGISTER_G_LOG_HANDLER("GLib-GObject");
REGISTER_G_LOG_HANDLER("GThread");
+ REGISTER_G_LOG_HANDLER("Json");
#ifdef USE_GSTREAMER
REGISTER_G_LOG_HANDLER("GStreamer");
#endif
@@ -1131,59 +786,29 @@ pidgin_debug_window_hide(void)
static void
pidgin_debug_print(PurpleDebugLevel level, const char *category,
- const char *arg_s)
+ const char *arg_s)
{
-#ifdef USE_REGEX
- GtkTreeIter iter;
-#endif /* USE_REGEX */
- gchar *ts_s;
- gchar *esc_s, *cat_s, *tmp, *s;
+ gchar *esc_s;
const char *mdate;
time_t mtime;
+ gchar *js;
- if (debug_win == NULL ||
- !purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled"))
- {
+ if (debug_win == NULL)
+ return;
+ if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled"))
return;
- }
mtime = time(NULL);
mdate = purple_utf8_strftime("%H:%M:%S", localtime(&mtime));
- ts_s = g_strdup_printf("(%s) ", mdate);
- if (category == NULL)
- cat_s = g_strdup("");
- else
- cat_s = g_strdup_printf("<b>%s:</b> ", category);
- esc_s = g_markup_escape_text(arg_s, -1);
+ esc_s = purple_escape_js(arg_s);
- s = g_strdup_printf("<font color=\"%s\">%s%s%s</font>",
- debug_fg_colors[level], ts_s, cat_s, esc_s);
-
- g_free(ts_s);
- g_free(cat_s);
+ js = g_strdup_printf("append(%d, '%s', '%s', %s);",
+ level, mdate, category ? category : "", esc_s);
g_free(esc_s);
- tmp = purple_utf8_try_convert(s);
- g_free(s);
- s = tmp;
-
- if (level == PURPLE_DEBUG_FATAL) {
- tmp = g_strdup_printf("<b>%s</b>", s);
- g_free(s);
- s = tmp;
- }
-
-#ifdef USE_REGEX
- /* add the text to the list store */
- gtk_list_store_append(debug_win->store, &iter);
- gtk_list_store_set(debug_win->store, &iter, 0, s, 1, level, -1);
-#else /* USE_REGEX */
- if(!debug_win->paused && level >= purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/filterlevel"))
- gtk_imhtml_append_text(GTK_IMHTML(debug_win->text), s, 0);
-#endif /* !USE_REGEX */
-
- g_free(s);
+ pidgin_webview_safe_execute_script(PIDGIN_WEBVIEW(debug_win->text), js);
+ g_free(js);
}
static gboolean
@@ -1215,3 +840,4 @@ pidgin_debug_get_handle() {
return &handle;
}
+
diff --git a/pidgin/gtkdebug.h b/pidgin/gtkdebug.h
index 75f0a8535e..74c009b3ce 100644
--- a/pidgin/gtkdebug.h
+++ b/pidgin/gtkdebug.h
@@ -1,8 +1,3 @@
-/**
- * @file gtkdebug.h GTK+ Debug API
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -23,43 +18,66 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _PIDGINDEBUG_H_
#define _PIDGINDEBUG_H_
+/**
+ * SECTION:gtkdebug
+ * @section_id: pidgin-gtkdebug
+ * @short_description: <filename>gtkdebug.h</filename>
+ * @title: Debug API
+ */
#include "debug.h"
+G_BEGIN_DECLS
+
/**
+ * pidgin_debug_init:
+ *
* Initializes the GTK+ debug system.
*/
void pidgin_debug_init(void);
/**
+ * pidgin_debug_uninit:
+ *
* Uninitialized the GTK+ debug system.
*/
void pidgin_debug_uninit(void);
/**
+ * pidgin_debug_get_handle:
+ *
* Get the handle for the GTK+ debug system.
*
- * @return the handle to the debug system
+ * Returns: the handle to the debug system
*/
void *pidgin_debug_get_handle(void);
/**
+ * pidgin_debug_window_show:
+ *
* Shows the debug window.
*/
void pidgin_debug_window_show(void);
/**
+ * pidgin_debug_window_hide:
+ *
* Hides the debug window.
*/
void pidgin_debug_window_hide(void);
/**
+ * pidgin_debug_get_ui_ops:
+ *
* Returns the UI operations structure for GTK+ debug output.
*
- * @return The GTK+ UI debug operations structure.
+ * Returns: The GTK+ UI debug operations structure.
*/
PurpleDebugUiOps *pidgin_debug_get_ui_ops(void);
+G_END_DECLS
+
#endif /* _PIDGINDEBUG_H_ */
diff --git a/pidgin/gtkdebug.html b/pidgin/gtkdebug.html
new file mode 100644
index 0000000000..9d63d5fc32
--- /dev/null
+++ b/pidgin/gtkdebug.html
@@ -0,0 +1,251 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <style>
+ body{white-space:pre-wrap;}
+ div.l0{color:#000000;} /* All debug levels. */
+ div.l1{color:#666666;} /* Misc. */
+ div.l2{color:#000000;} /* Information. */
+ div.l3{color:#660000;} /* Warnings. */
+ div.l4{color:#FF0000;} /* Errors. */
+ div.l5{color:#FF0000;font-weight:bold;} /* Fatal errors. */
+ /* Filter levels */
+ div#pause~div{display:none;}
+ body.l1 div.l0{display:none;}
+ body.l2 div.l0,body.l2 div.l1{display:none;}
+ body.l3 div.l0,body.l3 div.l1,body.l3 div.l2{display:none;}
+ body.l4 div.l0,body.l4 div.l1,body.l4 div.l2,body.l4 div.l3{display:none;}
+ body.l5 div.l0,body.l5 div.l1,body.l5 div.l2,body.l5 div.l3,body.l5 div.l4{display:none;}
+ /* Regex */
+ div.hide{display:none;}
+ span.regex{background-color:#ffafaf;font-weight:bold;}
+ </style>
+ <script>
+ function nearBottom() {
+ return ( document.body.scrollTop >= ( document.body.offsetHeight - ( window.innerHeight * 1.5 ) ) );
+ }
+
+ function scrollToBottom() {
+ document.body.scrollTop = document.body.offsetHeight;
+ }
+
+ regex = {
+ clear: function () {
+ var list, i;
+ var scroll = nearBottom();
+
+ /* Remove highlighting SPANs */
+ list = document.getElementsByClassName('regex');
+ i = list.length;
+ while (i--) {
+ var span = list[i];
+ var parent = span.parentNode;
+ var content = span.textContent;
+ var text = document.createTextNode(content);
+ parent.replaceChild(text, span);
+ }
+
+ /* Remove hidden DIVs */
+ list = document.getElementsByClassName('hide');
+ i = list.length;
+ while (i--) {
+ list[i].classList.remove('hide');
+ }
+
+ if (scroll)
+ scrollToBottom();
+ this.enabled = false;
+ },
+
+ highlightTextNodes: function (div, start_pos, end_pos) {
+ var data = [], node, range, span, contents;
+ var ind, end_ind
+ var this_start, this_end;
+
+ ind = 0;
+ div.normalize();
+ node = div;
+
+ /* First, find the container nodes and offsets to apply highlighting. */
+ do {
+ if (node.nodeType === Node.TEXT_NODE) {
+ end_ind = ind + node.length;
+
+ if (start_pos <= ind)
+ this_start = 0;
+ else if (start_pos < end_ind)
+ this_start = start_pos - ind;
+ else
+ this_start = -1;
+
+ if (end_pos < end_ind)
+ this_end = end_pos - ind;
+ else
+ this_end = end_ind - ind;
+
+ if (this_start != -1 && this_start < this_end) {
+ data.push(this_end, this_start, node);
+ }
+
+ ind = end_ind;
+ }
+
+ if (node.hasChildNodes()) {
+ node = node.firstChild;
+ } else {
+ while (node != div) {
+ var next = node.nextSibling;
+ if (next) {
+ node = next;
+ break;
+ } else {
+ node = node.parentNode;
+ }
+ }
+ }
+ } while (node != div);
+
+ /* Second, apply highlighting to saved sections. Changing the DOM is
+ automatically reflected in all WebKit API, so we have to do this after
+ finding the offsets, or things could get complicated. */
+ while (data.length) {
+ node = data.pop();
+ this_start = data.pop();
+ this_end = data.pop();
+
+ range = document.createRange();
+ range.setStart(node, this_start);
+ range.setEnd(node, this_end);
+
+ span = document.createElement('span');
+ span.className = 'regex';
+
+ contents = range.extractContents();
+ range.insertNode(span);
+ span.appendChild(contents);
+ }
+ },
+
+ match: function (div) {
+ var text, match_info;
+ var m, count, start_pos, end_pos;
+
+ text = div.textContent;
+ if (!text)
+ return;
+
+ /* We do a first pass to see if it matches at all. If it does we work out
+ * the offsets to highlight.
+ */
+ this.regex.lastIndex = 0;
+ var match_info = this.regex.exec(text);
+ if ((match_info != null) != this.invert) {
+ /* If we're not highlighting or the expression is inverted, we're
+ * done and move on.
+ */
+ if (!this.highlight || this.invert)
+ return;
+
+ do {
+ if (match_info === null)
+ break;
+
+ count = match_info.length;
+ if (count === 1)
+ m = 0;
+ else
+ m = 1;
+
+ /* We do this because JS doesn't provide a sufficient means to
+ determine the indices of matched groups. So we're just going
+ to highlight the entire match instead. */
+ m = 0; count = 1;
+
+ for (; m < count; m++) {
+ start_pos = match_info.index;
+ end_pos = this.regex.lastIndex;
+ if (end_pos == -1)
+ break;
+
+ this.highlightTextNodes(div, start_pos, end_pos);
+ }
+
+ /* Workaround broken API for empty matches */
+ if (match_info.index == this.regex.lastIndex)
+ this.regex.lastIndex++;
+ } while (match_info = this.regex.exec(text));
+ } else {
+ div.classList.add('hide');
+ }
+ },
+
+ filterAll: function (str, inv, high) {
+ this.regex = new RegExp(str, 'gi');
+ this.invert = inv;
+ this.highlight = high;
+ this.enabled = true;
+ var list = document.getElementsByTagName('div');
+ for (var i = 0; i < list.length; i++)
+ this.match(list[i]);
+ },
+
+ highlight: false,
+ invert: false,
+ enabled: false,
+ regex: undefined
+ }
+
+ function append(level, time, cat, msg) {
+ var div = document.createElement('div');
+ div.className = 'l' + level;
+
+ div.appendChild(document.createTextNode('(' + time + ') '));
+
+ if (cat) {
+ var cat_n = document.createElement('b');
+ cat_n.appendChild(document.createTextNode(cat + ':'));
+ div.appendChild(cat_n);
+ div.appendChild(document.createTextNode(' '));
+ }
+
+ div.appendChild(document.createTextNode(msg));
+
+ if (regex.enabled)
+ regex.match(div);
+
+ var scroll = nearBottom();
+ document.body.appendChild(div);
+ if (scroll)
+ scrollToBottom();
+ }
+
+ function clear() {
+ document.body.innerHTML = '';
+ }
+
+ function pauseOutput() {
+ document.body.insertAdjacentHTML('beforeEnd', '<div id=pause></div>');
+ }
+
+ function resumeOutput() {
+ var pause = document.getElementById('pause');
+ if (pause) {
+ var parent = pause.parentNode;
+ parent.removeChild(pause);
+ scrollToBottom();
+ }
+ }
+
+ function setFilterLevel(l) {
+ var scroll = nearBottom();
+
+ document.body.className = 'l'+l;
+
+ if (scroll)
+ scrollToBottom();
+ }
+ </script>
+ </head>
+ <body class=l0></body>
+</html>
+
diff --git a/pidgin/gtkdialogs.c b/pidgin/gtkdialogs.c
index 71dd8ad520..4168498741 100644
--- a/pidgin/gtkdialogs.c
+++ b/pidgin/gtkdialogs.c
@@ -1,8 +1,3 @@
-/*
- * @file gtkdialogs.c GTK+ Dialogs
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -39,12 +34,17 @@
#include "gtkblist.h"
#include "gtkdialogs.h"
-#include "gtkimhtml.h"
-#include "gtkimhtmltoolbar.h"
#include "gtklog.h"
#include "gtkutils.h"
+#include "gtkwebview.h"
#include "pidginstock.h"
+#ifdef USE_GSTREAMER
+#include <gst/gst.h>
+#endif
+
+#include "gtk3compat.h"
+
static GList *dialogwindows = NULL;
struct _PidginGroupMergeObject {
@@ -101,6 +101,7 @@ static const struct developer patch_writers[] = {
{"Jakub 'haakon' Adam", NULL, NULL},
{"Krzysztof Klinikowski", NULL, NULL},
{"Eion Robb", NULL, NULL},
+ {"Ankit Vani", NULL, NULL},
{NULL, NULL, NULL}
};
@@ -145,33 +146,34 @@ static const struct developer retired_patch_writers[] = {
{NULL, NULL, NULL}
};
-/* Order: Code, then Alphabetical by Last Name */
+/* Order: Code, then Alphabetical by Last Name
+ Use NULL language and code for secondary translators. */
static const struct translator translators[] = {
{N_("Afrikaans"), "af", "Samuel Murray", "afrikaans@gmail.com"},
- {N_("Afrikaans"), "af", "Friedel Wolff", "friedel@translate.org.za"},
+ {NULL, NULL, "Friedel Wolff", "friedel@translate.org.za"},
{N_("Arabic"), "ar", "Khaled Hosny", "khaledhosny@eglug.org"},
{N_("Assamese"), "as", "Amitakhya Phukan", "aphukan@fedoraproject.org"},
{N_("Asturian"), "ast", "Llumex03", "l.lumex03.tornes@gmail.com"},
{N_("Belarusian Latin"), "be@latin", "Ihar Hrachyshka", "ihar.hrachyshka@gmail.com"},
{N_("Bulgarian"), "bg", "Vladimira Girginova", "missing@here.is"},
- {N_("Bulgarian"), "bg", "Vladimir (Kaladan) Petkov", "kaladan@gmail.com"},
+ {NULL, NULL, "Vladimir (Kaladan) Petkov", "kaladan@gmail.com"},
{N_("Bengali"), "bn", "Jamil Ahmed", "jamil@bengalinux.org"},
- {N_("Bengali"), "bn", "Israt Jahan", "israt@ankur.org.bd"},
- {N_("Bengali"), "bn", "Samia Nimatullah", "mailsamia2001@yahoo.com"},
+ {NULL, NULL, "Israt Jahan", "israt@ankur.org.bd"},
+ {NULL, NULL, "Samia Nimatullah", "mailsamia2001@yahoo.com"},
{N_("Bengali-India"), "bn_IN", "Runa Bhattacharjee", "runab@fedoraproject.org"},
{N_("Bodo"), "brx", "Chandrakant Dhutadmal", "cpdhutadmal@yahoo.com"},
{N_("Bosnian"), "bs", "Lejla Hadzialic", "lejlah@gmail.com"},
{N_("Catalan"), "ca", "Josep Puigdemont", "josep.puigdemont@gmail.com"},
{N_("Valencian-Catalan"), "ca@valencia", "Toni Hermoso", "toniher@softcatala.org"},
- {N_("Valencian-Catalan"), "ca@valencia", "Josep Puigdemont", "tradgnome@softcatala.org"},
+ {NULL, NULL, "Josep Puigdemont", "tradgnome@softcatala.org"},
{N_("Czech"), "cs", "David Vachulka", "david@konstrukce-cad.com"},
{N_("Danish"), "da", "Nicky Thomassen", "nicky@aptget.dk"},
{N_("German"), "de", "Björn Voigt", "bjoernv@arcor.de"},
{N_("Dzongkha"), "dz", "Norbu", "nor_den@hotmail.com"},
- {N_("Dzongkha"), "dz", "Jurmey Rabgay", "jur_gay@yahoo.com"},
- {N_("Dzongkha"), "dz", "Wangmo Sherpa", "rinwanshe@yahoo.com"},
+ {NULL, NULL, "Jurmey Rabgay", "jur_gay@yahoo.com"},
+ {NULL, NULL, "Wangmo Sherpa", "rinwanshe@yahoo.com"},
{N_("Greek"), "el", "Katsaloulis Panayotis", "panayotis@panayotis.com"},
- {N_("Greek"), "el", "Panos Bouklis", "panos@echidna-band.com"},
+ {NULL, NULL, "Panos Bouklis", "panos@echidna-band.com"},
{N_("Australian English"), "en_AU", "Michael Findlay", "keltoiboy@gmail.com"},
{N_("British English"), "en_GB", "Phil Hannent", "phil@hannent.co.uk"},
{N_("Canadian English"), "en_CA", "Adam Weinberger", "adamw@gnome.org"},
@@ -181,19 +183,19 @@ static const struct translator translators[] = {
{N_("Estonian"), "et", "Ivar Smolin", "okul@linux.ee"},
{N_("Basque"), "eu", "Mikel Pascual Aldabaldetreku", "mikel.paskual@gmail.com"},
{N_("Persian"), "fa", "Elnaz Sarbar", "elnaz@farsiweb.info"},
- {N_("Persian"), "fa", "Roozbeh Pournader", "roozbeh@farsiweb.info"},
- {N_("Persian"), "fa", "Meelad Zakaria", "meelad@farsiweb.info"},
+ {NULL, NULL, "Roozbeh Pournader", "roozbeh@farsiweb.info"},
+ {NULL, NULL, "Meelad Zakaria", "meelad@farsiweb.info"},
{N_("Finnish"), "fi", "Timo Jyrinki", "timo.jyrinki@iki.fi"},
{N_("French"), "fr", "Éric Boumaour", "zongo_fr@users.sourceforge.net"},
{N_("Irish"), "ga", "Aaron Kearns", "ajkearns6@gmail.com"},
{N_("Irish"), "ga", "Kevin Scannell", NULL},
{N_("Galician"), "gl", "Mar Castro", "mariamarcp@gmail.com"},
- {N_("Galician"), "gl", "Frco. Javier Rial", "fjrial@cesga.es"},
+ {NULL, NULL, "Frco. Javier Rial", "fjrial@cesga.es"},
{N_("Gujarati"), "gu", "Ankit Patel", "ankit_patel@users.sf.net"},
- {N_("Gujarati"), "gu", N_("Gujarati Language Team"), "indianoss-gujarati@lists.sourceforge.net"},
+ {NULL, NULL, N_("Gujarati Language Team"), "indianoss-gujarati@lists.sourceforge.net"},
{N_("Hebrew"), "he", "Shalom Craimer", "scraimer@gmail.com"},
{N_("Hindi"), "hi", "Sangeeta Kumari", "sangeeta_0975@yahoo.com"},
- {N_("Hindi"), "hi", "Rajesh Ranjan", "rajeshkajha@yahoo.com"},
+ {NULL, NULL, "Rajesh Ranjan", "rajeshkajha@yahoo.com"},
{N_("Croatian"), "hr", "Sabina Drempetić", "bina91991@googlemail.com"},
{N_("Hungarian"), "hu", "Kelemen Gábor", "kelemeng@gnome.hu"},
{N_("Indonesian"), "id", "Rai S. Regawa", "raireg@yahoo.com"},
@@ -206,16 +208,16 @@ static const struct translator translators[] = {
{N_("Korean"), "ko", "Sushizang", "sushizang@empal.com"},
{N_("Kashmiri"), "kas", "Chandrakant Dhutadmal", "cpdhutadmal@yahoo.com"},
{N_("Kurdish"), "ku", "Amed Ç. Jiyan", "amedcj@hotmail.com"},
- {N_("Kurdish"), "ku", "Erdal Ronahi", "erdal.ronahi@gmail.com"},
- {N_("Kurdish"), "ku", "Rizoyê Xerzî", "rizoxerzi@hotmail.com"},
+ {NULL, NULL, "Erdal Ronahi", "erdal.ronahi@gmail.com"},
+ {NULL, NULL, "Rizoyê Xerzî", "rizoxerzi@hotmail.com"},
{N_("Lithuanian"), "lt", "Algimantas Margevičius", "margevicius.algimantas@gmail.com"},
{N_("Latvian"), "lv", "Rudolfs Mazurs", "rudolfs.mazurs@gmail.com"},
{N_("Maithili"), "mai", "Sangeeta Kumari", "sangeeta_0975@yahoo.com"},
- {N_("Maithili"), "mai", "Rajesh Ranjan", "rajeshkajha@yahoo.com"},
+ {NULL, NULL, "Rajesh Ranjan", "rajeshkajha@yahoo.com"},
{N_("Meadow Mari"), "mhr", "David Preece", "davidpreece1@gmail.com"},
{N_("Macedonian"), "mk", "Arangel Angov ", "arangel@linux.net.mk"},
- {N_("Macedonian"), "mk", "Ivana Kirkovska", "ivana.kirkovska@gmail.com"},
- {N_("Macedonian"), "mk", "Jovan Naumovski", "jovan@lugola.net"},
+ {NULL, NULL, "Ivana Kirkovska", "ivana.kirkovska@gmail.com"},
+ {NULL, NULL, "Jovan Naumovski", "jovan@lugola.net"},
{N_("Malayalam"), "ml", "Ani Peter", "apeter@redhat.com"},
{N_("Mongolian"), "mn", "gooyo", NULL},
{N_("Marathi"), "mr", "Sandeep Shedmake", "sandeep.shedmake@gmail.com"},
@@ -232,21 +234,21 @@ static const struct translator translators[] = {
{N_("Portuguese-Brazil"), "pt_BR", "Renato Silva", "br.renatosilva@gmail.com"},
{N_("Pashto"), "ps", "Kashif Masood", "masudmails@yahoo.com"},
{N_("Romanian"), "ro", "Mișu Moldovan", "dumol@gnome.org"},
- {N_("Romanian"), "ro", "Andrei Popescu", "andreimpopescu@gmail.com"},
+ {NULL, NULL, "Andrei Popescu", "andreimpopescu@gmail.com"},
{N_("Russian"), "ru", "Антон Самохвалов", "samant.ua@mail.ru"},
{N_("Sindhi"), "sd", "Chandrakant Dhutadmal", "cpdhutadmal@yahoo.com"},
{N_("Slovak"), "sk", "Jozef Káčer", "quickparser@gmail.com"},
- {N_("Slovak"), "sk", "loptosko", "loptosko@gmail.com"},
+ {NULL, NULL, "loptosko", "loptosko@gmail.com"},
{N_("Slovenian"), "sl", "Martin Srebotnjak", "miles@filmsi.net"},
{N_("Albanian"), "sq", "Besnik Bleta", "besnik@programeshqip.org"},
{N_("Serbian"), "sr", "Miloš Popović", "gpopac@gmail.com"},
{N_("Serbian Latin"), "sr@latin", "Miloš Popović", "gpopac@gmail.com"},
{N_("Sinhala"), "si", "Yajith Ajantha Dayarathna", "yajith@gmail.com"},
- {N_("Sinhala"), "si", "Danishka Navin", "snavin@redhat.com"},
+ {NULL, NULL, "Danishka Navin", "snavin@redhat.com"},
{N_("Swedish"), "sv", "Peter Hjalmarsson", "xake@telia.com"},
{N_("Swahili"), "sw", "Paul Msegeya", "msegeya@gmail.com"},
{N_("Tamil"), "ta", "I. Felix", "ifelix25@gmail.com"},
- {N_("Tamil"), "ta", "Viveka Nathan K", "vivekanathan@users.sourceforge.net"},
+ {NULL, NULL, "Viveka Nathan K", "vivekanathan@users.sourceforge.net"},
{N_("Telugu"), "te", "Krishnababu Krottapalli", "krottapalli@ymail.com"},
{N_("Thai"), "th", "Isriya Paireepairit", "markpeak@gmail.com"},
{N_("Tatar"), "tt", "ILDAR Valeev", "v_ildar@bk.ru"},
@@ -256,42 +258,43 @@ static const struct translator translators[] = {
{N_("Vietnamese"), "vi", "Nguyễn Vũ Hưng", "vuhung16plus@gmail.com"},
{N_("Simplified Chinese"), "zh_CN", "Aron Xu", "happyaron.xu@gmail.com"},
{N_("Hong Kong Chinese"), "zh_HK", "Abel Cheung", "abelindsay@gmail.com"},
- {N_("Hong Kong Chinese"), "zh_HK", "Ambrose C. Li", "acli@ada.dhs.org"},
- {N_("Hong Kong Chinese"), "zh_HK", "Paladin R. Liu", "paladin@ms1.hinet.net"},
+ {NULL, NULL, "Ambrose C. Li", "acli@ada.dhs.org"},
+ {NULL, NULL, "Paladin R. Liu", "paladin@ms1.hinet.net"},
{N_("Traditional Chinese"), "zh_TW", "Ambrose C. Li", "acli@ada.dhs.org"},
- {N_("Traditional Chinese"), "zh_TW", "Paladin R. Liu", "paladin@ms1.hinet.net"},
+ {NULL, NULL, "Paladin R. Liu", "paladin@ms1.hinet.net"},
{NULL, NULL, NULL, NULL}
};
static const struct translator past_translators[] = {
{N_("Amharic"), "am", "Daniel Yacob", NULL},
- {N_("Arabic"), "ar", "Mohamed Magdy", "alnokta@yahoo.com"},
+ {N_("Arabic"), "ar", "Mohamed Magdy", NULL},
{N_("Bulgarian"), "bg", "Hristo Todorov", NULL},
- {N_("Bengali"), "bn", "Indranil Das Gupta", "indradg@l2c2.org"},
- {N_("Bengali"), "bn", "Tisa Nafisa", "tisa_nafisa@yahoo.com"},
+ {N_("Bengali"), "bn", "Indranil Das Gupta", NULL},
+ {NULL, NULL, "Tisa Nafisa", NULL},
{N_("Catalan"), "ca", "JM Pérez Cáncer", NULL},
- {N_("Catalan"), "ca", "Robert Millan", NULL},
+ {NULL, NULL, "Robert Millan", NULL},
{N_("Czech"), "cs", "Honza Král", NULL},
- {N_("Czech"), "cs", "Miloslav Trmac", "mitr@volny.cz"},
- {N_("Danish"), "da", "Peter Bach", "bach.peter@gmail.com"},
- {N_("Danish"), "da", "Morten Brix Pedersen", "morten@wtf.dk"},
- {N_("German"), "de", "Daniel Seifert, Karsten Weiss", NULL},
- {N_("German"), "de", "Jochen Kemnade", "jochenkemnade@web.de"},
- {N_("Australian English"), "en_AU", "Peter Lawler", "trans@six-by-nine.com.au"},
- {N_("British English"), "en_GB", "Luke Ross", "luke@lukeross.name"},
+ {NULL, NULL, "Miloslav Trmac", NULL},
+ {N_("Danish"), "da", "Peter Bach", NULL},
+ {NULL, NULL, "Morten Brix Pedersen", NULL},
+ {N_("German"), "de", "Daniel Seifert", NULL},
+ {NULL, NULL, "Karsten Weiss", NULL},
+ {NULL, NULL, "Jochen Kemnade", NULL},
+ {N_("Australian English"), "en_AU", "Peter Lawler", NULL},
+ {N_("British English"), "en_GB", "Luke Ross", NULL},
{N_("Spanish"), "es", "JM Pérez Cáncer", NULL},
- {N_("Spanish"), "es", "Nicolás Lichtmaier", NULL},
- {N_("Spanish"), "es", "Amaya Rodrigo", NULL},
- {N_("Spanish"), "es", "Alejandro G Villar", NULL},
- {N_("Basque"), "eu", "Iñaki Larrañaga Murgoitio", "dooteo@zundan.com"},
- {N_("Basque"), "eu", "Hizkuntza Politikarako Sailburuordetza", "hizkpol@ej-gv.es"},
+ {NULL, NULL, "Nicolás Lichtmaier", NULL},
+ {NULL, NULL, "Amaya Rodrigo", NULL},
+ {NULL, NULL, "Alejandro G Villar", NULL},
+ {N_("Basque"), "eu", "Iñaki Larrañaga Murgoitio", NULL},
+ {NULL, NULL, "Hizkuntza Politikarako Sailburuordetza", NULL},
{N_("Finnish"), "fi", "Arto Alakulju", NULL},
- {N_("Finnish"), "fi", "Tero Kuusela", NULL},
+ {NULL, NULL, "Tero Kuusela", NULL},
{N_("French"), "fr", "Sébastien François", NULL},
- {N_("French"), "fr", "Loïc Jeannin", NULL},
- {N_("French"), "fr", "Stéphane Pontier", NULL},
- {N_("French"), "fr", "Stéphane Wirtel", NULL},
+ {NULL, NULL, "Loïc Jeannin", NULL},
+ {NULL, NULL, "Stéphane Pontier", NULL},
+ {NULL, NULL, "Stéphane Wirtel", NULL},
{N_("Galician"), "gl", "Ignacio Casal Quinteiro", NULL},
{N_("Hebrew"), "he", "Pavel Bibergal", NULL},
{N_("Hindi"), "hi", "Ravishankar Shrivastava", NULL},
@@ -299,50 +302,53 @@ static const struct translator past_translators[] = {
{N_("Armenian"), "hy", "David Avsharyan", NULL},
{N_("Italian"), "it", "Salvatore di Maggio", NULL},
{N_("Japanese"), "ja", "Takashi Aihana", NULL},
- {N_("Japanese"), "ja", "Ryosuke Kutsuna", NULL},
- {N_("Japanese"), "ja", "Junichi Uekawa", NULL},
- {N_("Japanese"), "ja", "Taku Yasui", NULL},
+ {NULL, NULL, "Ryosuke Kutsuna", NULL},
+ {NULL, NULL, "Junichi Uekawa", NULL},
+ {NULL, NULL, "Taku Yasui", NULL},
{N_("Georgian"), "ka", "Temuri Doghonadze", NULL},
- {N_("Korean"), "ko", "Sang-hyun S, A Ho-seok Lee", NULL},
- {N_("Korean"), "ko", "Kyeong-uk Son", NULL},
+ {N_("Korean"), "ko", "Sang-hyun S", NULL},
+ {NULL, NULL, "A Ho-seok Lee", NULL},
+ {NULL, NULL, "Kyeong-uk Son", NULL},
{N_("Lao"), "lo", "Anousak Souphavah", NULL},
- {N_("Lithuanian"), "lt", "Laurynas Biveinis", "laurynas.biveinis@gmail.com"},
- {N_("Lithuanian"), "lt", "Gediminas Čičinskas", NULL},
- {N_("Lithuanian"), "lt", "Andrius Štikonas", NULL},
+ {N_("Lithuanian"), "lt", "Laurynas Biveinis", NULL},
+ {NULL, NULL, "Gediminas Čičinskas", NULL},
+ {NULL, NULL, "Andrius Štikonas", NULL},
{N_("Macedonian"), "mk", "Tomislav Markovski", NULL},
{N_("Malay"), "ms_MY", "Muhammad Najmi bin Ahmad Zabidi", NULL},
- {N_("Bokmål Norwegian"), "nb", "Hans Fredrik Nordhaug", "hans@nordhaug.priv.no"},
- {N_("Bokmål Norwegian"), "nb", "Hallvard Glad", "hallvard.glad@gmail.com"},
- {N_("Bokmål Norwegian"), "nb", "Petter Johan Olsen", NULL},
- {N_("Bokmål Norwegian"), "nb", "Espen Stefansen", "espenas@gmail.com"},
- {N_("Dutch, Flemish"), "nl", "Vincent van Adrighem", "V.vanAdrighem@dirck.mine.nu"},
- {N_("Occitan"), "oc", "Yannig Marchegay", "yannig@marchegay.org"},
- {N_("Polish"), "pl", "Krzysztof Foltman", "krzysztof@foltman.com"},
- {N_("Polish"), "pl", "Paweł Godlewski", "pawel@bajk.pl"},
- {N_("Polish"), "pl", "Piotr Makowski", NULL},
- {N_("Polish"), "pl", "Emil Nowak", "emil5@go2.pl"},
- {N_("Polish"), "pl", "Przemysław Sułek", NULL},
+ {N_("Bokmål Norwegian"), "nb", "Hans Fredrik Nordhaug", NULL},
+ {NULL, NULL, "Hallvard Glad", NULL},
+ {NULL, NULL, "Petter Johan Olsen", NULL},
+ {NULL, NULL, "Espen Stefansen", NULL},
+ {N_("Dutch, Flemish"), "nl", "Vincent van Adrighem", NULL},
+ {N_("Occitan"), "oc", "Yannig Marchegay", NULL},
+ {N_("Polish"), "pl", "Krzysztof Foltman", NULL},
+ {NULL, NULL, "Paweł Godlewski", NULL},
+ {NULL, NULL, "Piotr Makowski", NULL},
+ {NULL, NULL, "Emil Nowak", NULL},
+ {NULL, NULL, "Przemysław Sułek", NULL},
{N_("Portuguese"), "pt", "Duarte Henriques", NULL},
- {N_("Portuguese-Brazil"), "pt_BR", "Maurício de Lemos Rodrigues Collares Neto", "mauricioc@gmail.com"},
- {N_("Portuguese-Brazil"), "pt_BR", "Rodrigo Luiz Marques Flores", "rodrigomarquesflores@gmail.com"},
- {N_("Russian"), "ru", "Dmitry Beloglazov", "dmaa@users.sf.net"},
- {N_("Russian"), "ru", "Alexandre Prokoudine", NULL},
- {N_("Russian"), "ru", "Sergey Volozhanin", NULL},
+ {N_("Portuguese-Brazil"), "pt_BR", "Maurício de Lemos Rodrigues Collares Neto", NULL},
+ {N_("Portuguese-Brazil"), "pt_BR", "Rodrigo Luiz Marques Flores", NULL},
+ {N_("Russian"), "ru", "Dmitry Beloglazov", NULL},
+ {NULL, NULL, "Alexandre Prokoudine", NULL},
+ {NULL, NULL, "Sergey Volozhanin", NULL},
{N_("Slovak"), "sk", "Daniel Režný", NULL},
- {N_("Slovak"), "sk", "Richard Golier", NULL},
- {N_("Slovak"), "sk", "helix84", NULL},
+ {NULL, NULL, "Richard Golier", NULL},
+ {NULL, NULL, "helix84", NULL},
{N_("Slovenian"), "sl", "Matjaz Horvat", NULL},
- {N_("Serbian"), "sr", "Danilo Šegan", "dsegan@gmx.net"},
- {N_("Serbian"), "sr", "Aleksandar Urosevic", "urke@users.sourceforge.net"},
+ {N_("Serbian"), "sr", "Danilo Šegan", NULL},
+ {NULL, NULL, "Aleksandar Urosevic", NULL},
{N_("Swedish"), "sv", "Tore Lundqvist", NULL},
- {N_("Swedish"), "sv", "Christian Rose", NULL},
- {N_("Telugu"), "te", "Mr. Subbaramaih", "info.gist@cdac.in"},
- {N_("Turkish"), "tr", "Serdar Soytetir", "tulliana@gmail.com"},
- {N_("Turkish"), "tr", "Ahmet Alp Balkan", NULL},
- {N_("Vietnamese"), "vi", N_("T.M.Thanh and the Gnome-Vi Team"), "gnomevi-list@lists.sf.net"},
- {N_("Simplified Chinese"), "zh_CN", "Hashao, Rocky S. Lee", NULL},
- {N_("Simplified Chinese"), "zh_CN", "Funda Wang", "fundawang@linux.net.cn"},
- {N_("Traditional Chinese"), "zh_TW", "Hashao, Rocky S. Lee", NULL},
+ {NULL, NULL, "Christian Rose", NULL},
+ {N_("Telugu"), "te", "Mr. Subbaramaih", NULL},
+ {N_("Turkish"), "tr", "Serdar Soytetir", NULL},
+ {NULL, "tr", "Ahmet Alp Balkan", NULL},
+ {N_("Vietnamese"), "vi", N_("T.M.Thanh and the Gnome-Vi Team"), NULL},
+ {N_("Simplified Chinese"), "zh_CN", "Hashao", NULL},
+ {NULL, NULL, "Rocky S. Lee", NULL},
+ {NULL, NULL, "Funda Wang", NULL},
+ {N_("Traditional Chinese"), "zh_TW", "Hashao", NULL},
+ {NULL, NULL, "Rocky S. Lee", NULL},
{NULL, NULL, NULL, NULL}
};
@@ -354,14 +360,15 @@ add_developers(GString *str, const struct developer *list)
const gchar *proto = "mailto:";
if (strchr(list->email, ':') != NULL)
proto = "";
- g_string_append_printf(str, " <a href=\"%s%s\">%s</a>%s%s%s<br/>",
+ g_string_append_printf(str,
+ "<li><a href=\"%s%s\" title=\"%s\">%s</a>%s%s%s</li>",
proto,
- list->email, _(list->name),
+ list->email, list->email, _(list->name),
list->role ? " (" : "",
list->role ? _(list->role) : "",
list->role ? ")" : "");
} else {
- g_string_append_printf(str, " %s%s%s%s<br/>",
+ g_string_append_printf(str, "<li>%s%s%s%s</li>",
_(list->name),
list->role ? " (" : "",
list->role ? _(list->role) : "",
@@ -373,18 +380,18 @@ add_developers(GString *str, const struct developer *list)
static void
add_translators(GString *str, const struct translator *list)
{
- for (; list->language != NULL; list++) {
+ for (; list->name != NULL; list++) {
+ if (list->language && list->abbr) {
+ g_string_append_printf(str, "<dt>%s (%s)</dt>",
+ _(list->language), list->abbr);
+ }
if (list->email != NULL) {
- g_string_append_printf(str, " <b>%s (%s)</b> - <a href=\"mailto:%s\">%s</a><br/>",
- _(list->language),
- list->abbr,
- list->email,
+ g_string_append_printf(str,
+ "<dd><a href=\"mailto:%s\" title=\"%s\">%s</a></dd>",
+ list->email, list->email,
_(list->name));
} else {
- g_string_append_printf(str, " <b>%s (%s)</b> - %s<br/>",
- _(list->language),
- list->abbr,
- _(list->name));
+ g_string_append_printf(str, "<dd>%s</dd>", _(list->name));
}
}
}
@@ -447,18 +454,23 @@ pidgin_logo_versionize(GdkPixbuf **original, GtkWidget *widget) {
static GtkWidget *
pidgin_build_help_dialog(const char *title, const char *role, GString *string)
{
- GtkWidget *win, *vbox, *frame, *logo, *imhtml, *button;
+ GtkWidget *win, *vbox, *frame, *logo, *webview, *button;
GdkPixbuf *pixbuf;
- GtkTextIter iter;
AtkObject *obj;
char *filename, *tmp;
+#if GTK_CHECK_VERSION(3,0,0)
+ win = pidgin_create_dialog(title, 0, role, TRUE);
+ vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, 0);
+#else
win = pidgin_create_dialog(title, PIDGIN_HIG_BORDER, role, TRUE);
vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
- gtk_window_set_default_size(GTK_WINDOW(win), 450, 450);
+#endif
+ gtk_window_set_default_size(GTK_WINDOW(win), 475, 450);
/* Generate a logo with a version number */
- filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "logo.png", NULL);
+ filename = g_build_filename(PURPLE_DATADIR,
+ "pixmaps", "pidgin", "logo.png", NULL);
pixbuf = pidgin_pixbuf_new_from_file(filename);
g_free(filename);
@@ -476,18 +488,16 @@ pidgin_build_help_dialog(const char *title, const char *role, GString *string)
g_free(tmp);
gtk_box_pack_start(GTK_BOX(vbox), logo, FALSE, FALSE, 0);
- frame = pidgin_create_imhtml(FALSE, &imhtml, NULL, NULL);
- gtk_imhtml_set_format_functions(GTK_IMHTML(imhtml), GTK_IMHTML_ALL ^ GTK_IMHTML_SMILEY);
+ frame = pidgin_create_webview(FALSE, &webview, NULL);
+ pidgin_webview_set_format_functions(PIDGIN_WEBVIEW(webview), PIDGIN_WEBVIEW_ALL ^ PIDGIN_WEBVIEW_SMILEY);
gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
- gtk_imhtml_append_text(GTK_IMHTML(imhtml), string->str, GTK_IMHTML_NO_SCROLL);
- gtk_text_buffer_get_start_iter(gtk_text_view_get_buffer(GTK_TEXT_VIEW(imhtml)), &iter);
- gtk_text_buffer_place_cursor(gtk_text_view_get_buffer(GTK_TEXT_VIEW(imhtml)), &iter);
+ pidgin_webview_append_html(PIDGIN_WEBVIEW(webview), string->str);
button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE,
G_CALLBACK(destroy_win), win);
- GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+ gtk_widget_set_can_default(button, TRUE);
gtk_widget_grab_default(button);
gtk_widget_show_all(win);
@@ -512,44 +522,52 @@ void pidgin_dialogs_about(void)
str = g_string_sized_new(4096);
g_string_append_printf(str,
- "<CENTER><FONT SIZE=\"4\"><B>%s %s</B></FONT></CENTER> (libpurple %s)"
- "<BR>%s<BR><BR>", PIDGIN_NAME, DISPLAY_VERSION,
+ "<h2>%s %s</h2>"
+ "<strong>(libpurple %s)<br/>%s</strong>",
+ PIDGIN_NAME, DISPLAY_VERSION,
purple_core_get_version(), REVISION);
g_string_append_printf(str,
- _("%s is a messaging client based on libpurple which is capable of "
+ _("<p>%s is a messaging client based on libpurple which is capable of "
"connecting to multiple messaging services at once. %s is written "
"in C using GTK+. %s is released, and may be modified and "
"redistributed, under the terms of the GPL version 2 (or later). "
"A copy of the GPL is distributed with %s. %s is copyrighted by "
"its contributors, a list of whom is also distributed with %s. "
- "There is no warranty for %s.<BR><BR>"), PIDGIN_NAME, PIDGIN_NAME,
+ "There is no warranty for %s.</p>"), PIDGIN_NAME, PIDGIN_NAME,
PIDGIN_NAME, PIDGIN_NAME, PIDGIN_NAME, PIDGIN_NAME, PIDGIN_NAME);
g_string_append_printf(str,
- _("<FONT SIZE=\"4\"><B>Helpful Resources</B></FONT><BR>\t<A "
- "HREF=\"%s\">Website</A><BR>\t<A HREF=\"%s\">Frequently Asked "
- "Questions</A><BR>\tIRC Channel: #pidgin on irc.freenode.net<BR>"
- "\tXMPP MUC: devel@conference.pidgin.im<BR><BR>"), PURPLE_WEBSITE,
- "http://developer.pidgin.im/wiki/FAQ");
+ _("<h3>Helpful Resources</h3>"
+ "<ul>"
+ "<li><a href=\"%s\" title=\"%s\">Website</a></li>"
+ "<li><a href=\"%s\" title=\"%s\">Frequently Asked Questions</a></li>"
+ "<li>IRC Channel: #pidgin on irc.freenode.net</li>"
+ "<li>XMPP MUC: devel@conference.pidgin.im</li>"
+ "</ul>"),
+ PURPLE_WEBSITE, PURPLE_WEBSITE,
+ "https://developer.pidgin.im/wiki/FAQ",
+ "https://developer.pidgin.im/wiki/FAQ");
g_string_append(str,
- "<font size=\"4\"><b>Help for Oracle Employees</b></font> is "
+ "<p><strong>Help for Oracle Employees</strong> is "
"available from your normal internal helpdesk or IT department. "
"The Pidgin developer and user communities cannot assist you in "
"the configuration or use of Pidgin within Oracle, as we know "
- "nothing of Oracle's infrastructure.<br/><br/>");
+ "nothing of Oracle's infrastructure.</p>");
- g_string_append(str,
- _("<font size=\"4\"><b>Help from other Pidgin users</b></font> is "
- "available by e-mailing <a "
- "href=\"mailto:support@pidgin.im\">support@pidgin.im</a><br/>"
- "This is a <b>public</b> mailing list! "
- "(<a href=\"http://pidgin.im/pipermail/support/\">archive</a>)<br/>"
+ g_string_append_printf(str,
+ _("<p><strong>Help from other Pidgin users</strong> is available "
+ "by e-mailing <a href=\"mailto:%s\">%s</a>.<br/>"
+ "This is a <strong>public</strong> mailing list! "
+ "(<a href=\"%s\" title=\"%s\">archive</a>)<br/>"
"We can't help with third-party protocols or plugins!<br/>"
- "This list's primary language is <b>English</b>. You are "
- "welcome to post in another language, but the responses may "
- "be less helpful.<br/>"));
+ "This list's primary language is English. You "
+ "are welcome to post in another language, but the responses may "
+ "be less helpful.</p>"),
+ "support@pidgin.im", "support@pidgin.im",
+ "https://pidgin.im/pipermail/support/",
+ "https://pidgin.im/pipermail/support/");
tmp = g_strdup_printf(_("About %s"), PIDGIN_NAME);
about = pidgin_build_help_dialog(tmp, "about", str);
@@ -571,171 +589,165 @@ void pidgin_dialogs_buildinfo(void)
str = g_string_sized_new(4096);
g_string_append_printf(str,
- "<FONT SIZE=\"4\"><B>%s %s</B></FONT> (libpurple %s)<BR>%s<BR><BR>", PIDGIN_NAME, DISPLAY_VERSION, purple_core_get_version(), REVISION);
+ "<h2>%s %s</h2>"
+ "<strong>(libpurple %s)<br/>%s</strong>",
+ PIDGIN_NAME, DISPLAY_VERSION, purple_core_get_version(), REVISION);
- g_string_append_printf(str, "<FONT SIZE=\"4\"><B>%s</B></FONT><br/>", _("Build Information"));
+ g_string_append_printf(str, "<h3>%s</h3><dl>", _("Build Information"));
- /* The following primarly intented for user/developer interaction and thus
- ought not be translated */
+ /* The following is primarily intended for user/developer interaction and
+ thus ought not be translated */
#ifdef CONFIG_ARGS /* win32 build doesn't use configure */
- g_string_append(str, " <b>Arguments to <i>./configure</i>:</b> " CONFIG_ARGS "<br/>");
+ g_string_append(str, "<dt>Arguments to <em>./configure</em>:</dt><dd>" CONFIG_ARGS "</dd>");
#endif
#ifndef _WIN32
#ifdef DEBUG
- g_string_append(str, " <b>Print debugging messages:</b> Yes<br/>");
+ g_string_append(str, "<dt>Print debugging messages:</dt><dd>Yes</dd>");
#else
- g_string_append(str, " <b>Print debugging messages:</b> No<br/>");
+ g_string_append(str, "<dt>Print debugging messages:</dt><dd>No</dd>");
#endif
#endif
#ifdef PURPLE_PLUGINS
- g_string_append(str, " <b>Plugins:</b> Enabled<br/>");
+ g_string_append(str, "<dt>Plugins:</dt><dd>Enabled</dd>");
#else
- g_string_append(str, " <b>Plugins:</b> Disabled<br/>");
+ g_string_append(str, "<dt>Plugins:</dt><dd>Disabled</dd>");
#endif
#ifdef HAVE_SSL
- g_string_append(str, " <b>SSL:</b> SSL support is present.<br/>");
+ g_string_append(str, "<dt>SSL:</dt><dd>SSL support is present.</dd>");
#else
- g_string_append(str, " <b>SSL:</b> SSL support was <b><i>NOT</i></b> compiled!<br/>");
+ g_string_append(str, "<dt>SSL:</dt><dd>SSL support was <strong><em>NOT</em></strong> compiled!</dd>");
#endif
-/* This might be useful elsewhere too, but it is particularly useful for
- * debugging stuff known to be GTK+/Glib bugs on Windows */
-#ifdef _WIN32
- g_string_append_printf(str, " <b>GTK+ Runtime:</b> %u.%u.%u<br/>"
- " <b>Glib Runtime:</b> %u.%u.%u<br/>",
+ g_string_append_printf(str, "<dt>GTK+ Runtime:</dt><dd>%u.%u.%u</dd>"
+ "<dt>GLib Runtime:</dt><dd>%u.%u.%u</dd>",
gtk_major_version, gtk_minor_version, gtk_micro_version,
glib_major_version, glib_minor_version, glib_micro_version);
-#endif
-g_string_append(str, "<br/> <b>Library Support</b><br/>");
+ g_string_append(str, "</dl><h3>Library Support</h3><dl>");
#ifdef HAVE_CYRUS_SASL
- g_string_append_printf(str, " <b>Cyrus SASL:</b> Enabled<br/>");
+ g_string_append_printf(str, "<dt>Cyrus SASL:</dt><dd>Enabled</dd>");
#else
- g_string_append_printf(str, " <b>Cyrus SASL:</b> Disabled<br/>");
+ g_string_append_printf(str, "<dt>Cyrus SASL:</dt><dd>Disabled</dd>");
#endif
#ifndef _WIN32
#ifdef HAVE_DBUS
- g_string_append_printf(str, " <b>D-Bus:</b> Enabled<br/>");
+ g_string_append_printf(str, "<dt>D-Bus:</dt><dd>Enabled</dd>");
#else
- g_string_append_printf(str, " <b>D-Bus:</b> Disabled<br/>");
+ g_string_append_printf(str, "<dt>D-Bus:</dt><dd>Disabled</dd>");
#endif
#ifdef HAVE_EVOLUTION_ADDRESSBOOK
- g_string_append_printf(str, " <b>Evolution Addressbook:</b> Enabled<br/>");
+ g_string_append_printf(str, "<dt>Evolution Addressbook:</dt><dd>Enabled</dd>");
#else
- g_string_append_printf(str, " <b>Evolution Addressbook:</b> Disabled<br/>");
+ g_string_append_printf(str, "<dt>Evolution Addressbook:</dt><dd>Disabled</dd>");
#endif
#endif
#ifdef HAVE_LIBGADU
- g_string_append(str, " <b>Gadu-Gadu library (libgadu):</b> External<br/>");
-#else
- g_string_append(str, " <b>Gadu-Gadu library (libgadu):</b> Internal<br/>");
-#endif
-
-#ifdef USE_GTKSPELL
- g_string_append(str, " <b>GtkSpell:</b> Enabled<br/>");
+ g_string_append(str, "<dt>Gadu-Gadu library (libgadu):</dt><dd>External</dd>");
#else
- g_string_append(str, " <b>GtkSpell:</b> Disabled<br/>");
+ g_string_append(str, "<dt>Gadu-Gadu library (libgadu):</dt><dd>Internal</dd>");
#endif
#ifdef HAVE_GNUTLS
- g_string_append(str, " <b>GnuTLS:</b> Enabled<br/>");
+ g_string_append(str, "<dt>GnuTLS:</dt><dd>Enabled</dd>");
#else
- g_string_append(str, " <b>GnuTLS:</b> Disabled<br/>");
+ g_string_append(str, "<dt>GnuTLS:</dt><dd>Disabled</dd>");
#endif
-#ifndef _WIN32
#ifdef USE_GSTREAMER
- g_string_append(str, " <b>GStreamer:</b> Enabled<br/>");
+ tmp = gst_version_string();
+ g_string_append_printf(str, "<dt>GStreamer:</dt><dd>%s</dd>", tmp);
+ g_free(tmp);
#else
- g_string_append(str, " <b>GStreamer:</b> Disabled<br/>");
-#endif
+ g_string_append(str, "<dt>GStreamer:</dt><dd>Disabled</dd>");
#endif
#ifndef _WIN32
#ifdef ENABLE_MONO
- g_string_append(str, " <b>Mono:</b> Enabled<br/>");
+ g_string_append(str, "<dt>Mono:</dt><dd>Enabled</dd>");
#else
- g_string_append(str, " <b>Mono:</b> Disabled<br/>");
+ g_string_append(str, "<dt>Mono:</dt><dd>Disabled</dd>");
#endif
#endif
#ifndef _WIN32
#ifdef HAVE_NETWORKMANAGER
- g_string_append(str, " <b>NetworkManager:</b> Enabled<br/>");
+ g_string_append(str, "<dt>NetworkManager:</dt><dd>Enabled</dd>");
#else
- g_string_append(str, " <b>NetworkManager:</b> Disabled<br/>");
+ g_string_append(str, "<dt>NetworkManager:</dt><dd>Disabled</dd>");
#endif
#endif
#ifdef HAVE_NSS
- g_string_append(str, " <b>Network Security Services (NSS):</b> Enabled<br/>");
+ g_string_append(str, "<dt>Network Security Services (NSS):</dt><dd>Enabled</dd>");
#else
- g_string_append(str, " <b>Network Security Services (NSS):</b> Disabled<br/>");
+ g_string_append(str, "<dt>Network Security Services (NSS):</dt><dd>Disabled</dd>");
#endif
-if (purple_plugins_find_with_id("core-perl") != NULL)
- g_string_append(str, " <b>Perl:</b> Enabled<br/>");
-else
- g_string_append(str, " <b>Perl:</b> Disabled<br/>");
+ if (purple_plugins_find_with_id("core-perl") != NULL)
+ g_string_append(str, "<dt>Perl:</dt><dd>Enabled</dd>");
+ else
+ g_string_append(str, "<dt>Perl:</dt><dd>Disabled</dd>");
-if (purple_plugins_find_with_id("core-tcl") != NULL) {
- g_string_append(str, " <b>Tcl:</b> Enabled<br/>");
+ if (purple_plugins_find_with_id("core-tcl") != NULL) {
+ g_string_append(str, "<dt>Tcl:</dt><dd>Enabled</dd>");
#ifdef HAVE_TK
- g_string_append(str, " <b>Tk:</b> Enabled<br/>");
+ g_string_append(str, "<dt>Tk:</dt><dd>Enabled</dd>");
#else
- g_string_append(str, " <b>Tk:</b> Disabled<br/>");
+ g_string_append(str, "<dt>Tk:</dt><dd>Disabled</dd>");
#endif
-} else {
- g_string_append(str, " <b>Tcl:</b> Disabled<br/>");
- g_string_append(str, " <b>Tk:</b> Disabled<br/>");
-}
+ } else {
+ g_string_append(str, "<dt>Tcl:</dt><dd>Disabled</dd>");
+ g_string_append(str, "<dt>Tk:</dt><dd>Disabled</dd>");
+ }
#ifdef USE_IDN
- g_string_append(str, " <b>UTF-8 DNS (IDN):</b> Enabled<br/>");
+ g_string_append(str, "<dt>UTF-8 DNS (IDN):</dt><dd>Enabled</dd>");
#else
- g_string_append(str, " <b>UTF-8 DNS (IDN):</b> Disabled<br/>");
+ g_string_append(str, "<dt>UTF-8 DNS (IDN):</dt><dd>Disabled</dd>");
#endif
#ifdef USE_VV
- g_string_append(str, " <b>Voice and Video:</b> Enabled<br/>");
+ g_string_append(str, "<dt>Voice and Video:</dt><dd>Enabled</dd>");
#else
- g_string_append(str, " <b>Voice and Video:</b> Disabled<br/>");
+ g_string_append(str, "<dt>Voice and Video:</dt><dd>Disabled</dd>");
#endif
#ifndef _WIN32
#ifdef USE_SM
- g_string_append(str, " <b>X Session Management:</b> Enabled<br/>");
+ g_string_append(str, "<dt>X Session Management:</dt><dd>Enabled</dd>");
#else
- g_string_append(str, " <b>X Session Management:</b> Disabled<br/>");
+ g_string_append(str, "<dt>X Session Management:</dt><dd>Disabled</dd>");
#endif
#ifdef USE_SCREENSAVER
- g_string_append(str, " <b>XScreenSaver:</b> Enabled<br/>");
+ g_string_append(str, "<dt>XScreenSaver:</dt><dd>Enabled</dd>");
#else
- g_string_append(str, " <b>XScreenSaver:</b> Disabled<br/>");
+ g_string_append(str, "<dt>XScreenSaver:</dt><dd>Disabled</dd>");
#endif
#ifdef LIBZEPHYR_EXT
- g_string_append(str, " <b>Zephyr library (libzephyr):</b> External<br/>");
+ g_string_append(str, "<dt>Zephyr library (libzephyr):</dt><dd>External</dd>");
#else
- g_string_append(str, " <b>Zephyr library (libzephyr):</b> Internal<br/>");
+ g_string_append(str, "<dt>Zephyr library (libzephyr):</dt><dd>Internal</dd>");
#endif
#ifdef ZEPHYR_USES_KERBEROS
- g_string_append(str, " <b>Zephyr uses Kerberos:</b> Yes<br/>");
+ g_string_append(str, "<dt>Zephyr uses Kerberos:</dt><dd>Yes</dd>");
#else
- g_string_append(str, " <b>Zephyr uses Kerberos:</b> No<br/>");
+ g_string_append(str, "<dt>Zephyr uses Kerberos:</dt><dd>No</dd>");
#endif
#endif
+ g_string_append(str, "</dl>");
+
/* End of not to be translated section */
tmp = g_strdup_printf(_("%s Build Information"), PIDGIN_NAME);
@@ -758,27 +770,28 @@ void pidgin_dialogs_developers(void)
str = g_string_sized_new(4096);
/* Current Developers */
- g_string_append_printf(str, "<FONT SIZE=\"4\"><B>%s:</B></FONT><BR/>",
+ g_string_append_printf(str, "<h3>%s</h3><ul>",
_("Current Developers"));
add_developers(str, developers);
- g_string_append(str, "<BR/>");
+ g_string_append(str, "</ul>");
/* Crazy Patch Writers */
- g_string_append_printf(str, "<FONT SIZE=\"4\"><B>%s:</B></FONT><BR/>",
+ g_string_append_printf(str, "<h3>%s</h3><ul>",
_("Crazy Patch Writers"));
add_developers(str, patch_writers);
- g_string_append(str, "<BR/>");
+ g_string_append(str, "</ul>");
/* Retired Developers */
- g_string_append_printf(str, "<FONT SIZE=\"4\"><B>%s:</B></FONT><BR/>",
+ g_string_append_printf(str, "<h3>%s</h3><ul>",
_("Retired Developers"));
add_developers(str, retired_developers);
- g_string_append(str, "<BR/>");
+ g_string_append(str, "</ul>");
/* Retired Crazy Patch Writers */
- g_string_append_printf(str, "<FONT SIZE=\"4\"><B>%s:</B></FONT><BR/>",
+ g_string_append_printf(str, "<h3>%s</h3><ul>",
_("Retired Crazy Patch Writers"));
add_developers(str, retired_patch_writers);
+ g_string_append(str, "</ul>");
tmp = g_strdup_printf(_("%s Developer Information"), PIDGIN_NAME);
developer_info = pidgin_build_help_dialog(tmp, "developer_info", str);
@@ -800,15 +813,16 @@ void pidgin_dialogs_translators(void)
str = g_string_sized_new(4096);
/* Current Translators */
- g_string_append_printf(str, "<FONT SIZE=\"4\">%s:</FONT><BR/>",
+ g_string_append_printf(str, "<h3>%s</h3><dl>",
_("Current Translators"));
add_translators(str, translators);
- g_string_append(str, "<BR/>");
+ g_string_append(str, "</dl>");
/* Past Translators */
- g_string_append_printf(str, "<FONT SIZE=\"4\">%s:</FONT><BR/>",
+ g_string_append_printf(str, "<h3>%s</h3><dl>",
_("Past Translators"));
add_translators(str, past_translators);
+ g_string_append(str, "</dl>");
tmp = g_strdup_printf(_("%s Translator Information"), PIDGIN_NAME);
translator_info = pidgin_build_help_dialog(tmp, "translator_info", str);
@@ -829,37 +843,44 @@ void pidgin_dialogs_plugins_info(void)
str = g_string_sized_new(4096);
- g_string_append_printf(str, "<FONT SIZE=\"4\">%s</FONT><BR/>",
- _("Plugin Information"));
+ g_string_append_printf(str, "<h2>%s</h2><dl>", _("Plugin Information"));
for(l = purple_plugins_get_all(); l; l = l->next) {
plugin = (PurplePlugin *)l->data;
pname = g_markup_escape_text(purple_plugin_get_name(plugin), -1);
- pauthor = g_markup_escape_text(purple_plugin_get_author(plugin), -1);
+ if ((pauthor = (char *)purple_plugin_get_author(plugin)) != NULL)
+ pauthor = g_markup_escape_text(pauthor, -1);
pver = purple_plugin_get_version(plugin);
pwebsite = purple_plugin_get_homepage(plugin);
pid = purple_plugin_get_id(plugin);
punloadable = purple_plugin_is_unloadable(plugin);
ploaded = purple_plugin_is_loaded(plugin);
+ g_string_append_printf(str, "<dt>%s</dt><dd>", pname);
+ if (pauthor)
+ g_string_append_printf(str, "<b>Author:</b> %s<br/>", pauthor);
g_string_append_printf(str,
- "<FONT SIZE=\"3\"><B>%s</B></FONT><BR/><FONT SIZE=\"2\">"
- "\t<B>Author:</B> %s<BR/>\t<B>Version:</B> %s<BR/>"
- "\t<B>Website:</B> %s<BR/>\t<B>ID String:</B> %s<BR/>"
- "\t<B>Loadable:</B> %s<BR/>\t<B>Loaded:</B> %s<BR/>"
- "<BR/></FONT>", pname, pauthor ? pauthor : "(null)",
+ "<b>Version:</b> %s<br/>"
+ "<b>Website:</b> %s<br/>"
+ "<b>ID String:</b> %s<br/>"
+ "<b>Loadable:</b> %s<br/>"
+ "<b>Loaded:</b> %s"
+ "</dd><br/>",
pver, pwebsite, pid,
- punloadable ? "<FONT COLOR=\"#FF0000\"><B>No</B></FONT>" : "Yes",
+ punloadable ? "<span style=\"color: #FF0000;\"><b>No</b></span>" : "Yes",
ploaded ? "Yes" : "No");
+
+ g_free(pname);
+ g_free(pauthor);
}
+ g_string_append(str, "</dl>");
+
plugins_info = pidgin_build_help_dialog(title, "plugins_info", str);
g_signal_connect(G_OBJECT(plugins_info), "destroy",
G_CALLBACK(gtk_widget_destroyed), &plugins_info);
g_free(title);
- g_free(pname);
- g_free(pauthor);
}
static void
@@ -874,6 +895,28 @@ pidgin_dialogs_im_cb(gpointer data, PurpleRequestFields *fields)
pidgin_dialogs_im_with_user(account, username);
}
+static gboolean
+pidgin_dialogs_im_name_validator(PurpleRequestField *field, gchar **errmsg,
+ void *_fields)
+{
+ PurpleRequestFields *fields = _fields;
+ PurpleAccount *account;
+ PurplePlugin *prpl;
+ const char *username;
+ gboolean valid;
+
+ account = purple_request_fields_get_account(fields, "account");
+ prpl = purple_find_prpl(purple_account_get_protocol_id(account));
+ username = purple_request_fields_get_string(fields, "screenname");
+
+ valid = purple_validate(prpl, username);
+
+ if (errmsg && !valid)
+ *errmsg = g_strdup(_("Invalid username"));
+
+ return valid;
+}
+
void
pidgin_dialogs_im(void)
{
@@ -889,6 +932,7 @@ pidgin_dialogs_im(void)
field = purple_request_field_string_new("screenname", _("_Name"), NULL, FALSE);
purple_request_field_set_type_hint(field, "screenname");
purple_request_field_set_required(field, TRUE);
+ purple_request_field_set_validator(field, pidgin_dialogs_im_name_validator, fields);
purple_request_field_group_add_field(group, field);
field = purple_request_field_account_new("account", _("_Account"), NULL);
@@ -899,32 +943,31 @@ pidgin_dialogs_im(void)
purple_request_field_set_required(field, TRUE);
purple_request_field_group_add_field(group, field);
- purple_request_fields(purple_get_blist(), _("New Instant Message"),
+ 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, NULL,
- NULL);
+ NULL, NULL);
}
void
pidgin_dialogs_im_with_user(PurpleAccount *account, const char *username)
{
- PurpleConversation *conv;
+ PurpleIMConversation *im;
g_return_if_fail(account != NULL);
g_return_if_fail(username != NULL);
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, username, account);
+ im = purple_conversations_find_im_with_account(username, account);
- if (conv == NULL)
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, username);
+ if (im == NULL)
+ im = purple_im_conversation_new(account, username);
- pidgin_conv_attach_to_conversation(conv);
- purple_conversation_present(conv);
+ pidgin_conv_attach_to_conversation(PURPLE_CONVERSATION(im));
+ purple_conversation_present(PURPLE_CONVERSATION(im));
}
static gboolean
@@ -975,12 +1018,13 @@ pidgin_dialogs_ee(const char *ee)
gtk_container_set_border_width (GTK_CONTAINER(window), PIDGIN_HIG_BOX_SPACE);
gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
- gtk_dialog_set_has_separator(GTK_DIALOG(window), FALSE);
- gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(window)->vbox), PIDGIN_HIG_BORDER);
- gtk_container_set_border_width (GTK_CONTAINER(GTK_DIALOG(window)->vbox), PIDGIN_HIG_BOX_SPACE);
+ gtk_box_set_spacing(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(window))),
+ PIDGIN_HIG_BORDER);
+ gtk_container_set_border_width(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(window))),
+ PIDGIN_HIG_BOX_SPACE);
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
- gtk_container_add(GTK_CONTAINER(GTK_DIALOG(window)->vbox), hbox);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BORDER);
+ gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(window))), hbox);
img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_COOL, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
@@ -1038,15 +1082,14 @@ pidgin_dialogs_info(void)
purple_request_field_set_required(field, TRUE);
purple_request_field_group_add_field(group, field);
- purple_request_fields(purple_get_blist(), _("Get User Info"),
+ 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, NULL,
- NULL);
+ NULL, NULL);
}
static void
@@ -1068,7 +1111,7 @@ pidgin_dialogs_log_cb(gpointer data, PurpleRequestFields *fields)
pidgin_set_cursor(gtkblist->window, GDK_WATCH);
- buddies = purple_find_buddies(account, username);
+ buddies = purple_blist_find_buddies(account, username);
for (cur = buddies; cur != NULL; cur = cur->next)
{
PurpleBlistNode *node = cur->data;
@@ -1130,42 +1173,21 @@ pidgin_dialogs_log(void)
purple_request_field_set_required(field, TRUE);
purple_request_field_group_add_field(group, field);
- purple_request_fields(purple_get_blist(), _("View User Log"),
+ 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, NULL,
- NULL);
-}
-
-static void
-pidgin_dialogs_alias_contact_cb(PurpleContact *contact, const char *new_alias)
-{
- purple_blist_alias_contact(contact, new_alias);
-}
-
-void
-pidgin_dialogs_alias_contact(PurpleContact *contact)
-{
- g_return_if_fail(contact != NULL);
-
- purple_request_input(NULL, _("Alias Contact"), NULL,
- _("Enter an alias for this contact."),
- contact->alias, FALSE, FALSE, NULL,
- _("Alias"), G_CALLBACK(pidgin_dialogs_alias_contact_cb),
- _("Cancel"), NULL,
- NULL, purple_contact_get_alias(contact), NULL,
- contact);
+ NULL, NULL);
}
static void
pidgin_dialogs_alias_buddy_cb(PurpleBuddy *buddy, const char *new_alias)
{
- purple_blist_alias_buddy(buddy, new_alias);
- serv_alias_buddy(buddy);
+ purple_buddy_set_local_alias(buddy, new_alias);
+ purple_serv_alias_buddy(buddy);
}
void
@@ -1175,13 +1197,13 @@ pidgin_dialogs_alias_buddy(PurpleBuddy *buddy)
g_return_if_fail(buddy != NULL);
- secondary = g_strdup_printf(_("Enter an alias for %s."), buddy->name);
+ secondary = g_strdup_printf(_("Enter an alias for %s."), purple_buddy_get_name(buddy));
purple_request_input(NULL, _("Alias Buddy"), NULL,
- secondary, buddy->alias, FALSE, FALSE, NULL,
+ secondary, purple_buddy_get_local_alias(buddy), FALSE, FALSE, NULL,
_("Alias"), G_CALLBACK(pidgin_dialogs_alias_buddy_cb),
_("Cancel"), NULL,
- purple_buddy_get_account(buddy), purple_buddy_get_name(buddy), NULL,
+ purple_request_cpar_from_account(purple_buddy_get_account(buddy)),
buddy);
g_free(secondary);
@@ -1190,21 +1212,27 @@ pidgin_dialogs_alias_buddy(PurpleBuddy *buddy)
static void
pidgin_dialogs_alias_chat_cb(PurpleChat *chat, const char *new_alias)
{
- purple_blist_alias_chat(chat, new_alias);
+ purple_chat_set_alias(chat, new_alias);
}
void
pidgin_dialogs_alias_chat(PurpleChat *chat)
{
+ gchar *alias;
+
g_return_if_fail(chat != NULL);
+ g_object_get(chat, "alias", &alias, NULL);
+
purple_request_input(NULL, _("Alias Chat"), NULL,
_("Enter an alias for this chat."),
- chat->alias, FALSE, FALSE, NULL,
+ alias, FALSE, FALSE, NULL,
_("Alias"), G_CALLBACK(pidgin_dialogs_alias_chat_cb),
_("Cancel"), NULL,
- chat->account, NULL, NULL,
+ purple_request_cpar_from_account(purple_chat_get_account(chat)),
chat);
+
+ g_free(alias);
}
static void
@@ -1217,8 +1245,8 @@ pidgin_dialogs_remove_contact_cb(PurpleContact *contact)
group = (PurpleGroup*)cnode->parent;
for (bnode = cnode->child; bnode; bnode = bnode->next) {
PurpleBuddy *buddy = (PurpleBuddy*)bnode;
- if (purple_account_is_connected(buddy->account))
- purple_account_remove_buddy(buddy->account, buddy, group);
+ if (purple_account_is_connected(purple_buddy_get_account(buddy)))
+ purple_account_remove_buddy(purple_buddy_get_account(buddy), buddy, group);
}
purple_blist_remove_contact(contact);
}
@@ -1236,6 +1264,7 @@ pidgin_dialogs_remove_contact(PurpleContact *contact)
pidgin_dialogs_remove_buddy(buddy);
} else {
gchar *text;
+ int contact_size = purple_counting_node_get_total_size(PURPLE_COUNTING_NODE(contact));
text = g_strdup_printf(
ngettext(
"You are about to remove the contact containing %s "
@@ -1243,11 +1272,11 @@ pidgin_dialogs_remove_contact(PurpleContact *contact)
"want to continue?",
"You are about to remove the contact containing %s "
"and %d other buddies from your buddy list. Do you "
- "want to continue?", contact->totalsize - 1),
- buddy->name, contact->totalsize - 1);
+ "want to continue?", contact_size - 1),
+ purple_buddy_get_name(buddy), contact_size - 1);
purple_request_action(contact, NULL, _("Remove Contact"), text, 0,
- NULL, purple_contact_get_alias(contact), NULL,
+ NULL,
contact, 2,
_("_Remove Contact"), G_CALLBACK(pidgin_dialogs_remove_contact_cb),
_("Cancel"),
@@ -1266,7 +1295,7 @@ static void free_ggmo(struct _PidginGroupMergeObject *ggp)
static void
pidgin_dialogs_merge_groups_cb(struct _PidginGroupMergeObject *GGP)
{
- purple_blist_rename_group(GGP->parent, GGP->new_name);
+ purple_group_set_name(GGP->parent, GGP->new_name);
free_ggmo(GGP);
}
@@ -1281,14 +1310,14 @@ pidgin_dialogs_merge_groups(PurpleGroup *source, const char *new_name)
text = g_strdup_printf(
_("You are about to merge the group called %s into the group "
- "called %s. Do you want to continue?"), source->name, new_name);
+ "called %s. Do you want to continue?"), purple_group_get_name(source), new_name);
ggp = g_new(struct _PidginGroupMergeObject, 1);
ggp->parent = source;
ggp->new_name = g_strdup(new_name);
purple_request_action(source, NULL, _("Merge Groups"), text, 0,
- NULL, NULL, NULL,
+ NULL,
ggp, 2,
_("_Merge Groups"), G_CALLBACK(pidgin_dialogs_merge_groups_cb),
_("Cancel"), G_CALLBACK(free_ggmo));
@@ -1304,26 +1333,26 @@ pidgin_dialogs_remove_group_cb(PurpleGroup *group)
cnode = ((PurpleBlistNode*)group)->child;
while (cnode) {
- if (PURPLE_BLIST_NODE_IS_CONTACT(cnode)) {
+ if (PURPLE_IS_CONTACT(cnode)) {
bnode = cnode->child;
cnode = cnode->next;
while (bnode) {
PurpleBuddy *buddy;
- if (PURPLE_BLIST_NODE_IS_BUDDY(bnode)) {
+ if (PURPLE_IS_BUDDY(bnode)) {
buddy = (PurpleBuddy*)bnode;
bnode = bnode->next;
- if (purple_account_is_connected(buddy->account)) {
- purple_account_remove_buddy(buddy->account, buddy, group);
+ if (purple_account_is_connected(purple_buddy_get_account(buddy))) {
+ purple_account_remove_buddy(purple_buddy_get_account(buddy), buddy, group);
purple_blist_remove_buddy(buddy);
}
} else {
bnode = bnode->next;
}
}
- } else if (PURPLE_BLIST_NODE_IS_CHAT(cnode)) {
+ } else if (PURPLE_IS_CHAT(cnode)) {
PurpleChat *chat = (PurpleChat *)cnode;
cnode = cnode->next;
- if (purple_account_is_connected(chat->account))
+ if (purple_account_is_connected(purple_chat_get_account(chat)))
purple_blist_remove_chat(chat);
} else {
cnode = cnode->next;
@@ -1341,10 +1370,10 @@ pidgin_dialogs_remove_group(PurpleGroup *group)
g_return_if_fail(group != NULL);
text = g_strdup_printf(_("You are about to remove the group %s and all its members from your buddy list. Do you want to continue?"),
- group->name);
+ purple_group_get_name(group));
purple_request_action(group, NULL, _("Remove Group"), text, 0,
- NULL, NULL, NULL,
+ NULL,
group, 2,
_("_Remove Group"), G_CALLBACK(pidgin_dialogs_remove_group_cb),
_("Cancel"), NULL);
@@ -1361,10 +1390,10 @@ pidgin_dialogs_remove_buddy_cb(PurpleBuddy *buddy)
PurpleAccount *account;
group = purple_buddy_get_group(buddy);
- name = g_strdup(buddy->name); /* b->name is a crasher after remove_buddy */
- account = buddy->account;
+ name = g_strdup(purple_buddy_get_name(buddy)); /* purple_buddy_get_name() is a crasher after remove_buddy */
+ account = purple_buddy_get_account(buddy);
- purple_debug_info("blist", "Removing '%s' from buddy list.\n", buddy->name);
+ purple_debug_info("blist", "Removing '%s' from buddy list.\n", purple_buddy_get_name(buddy));
/* TODO - Should remove from blist first... then call purple_account_remove_buddy()? */
purple_account_remove_buddy(account, buddy, group);
purple_blist_remove_buddy(buddy);
@@ -1380,13 +1409,13 @@ pidgin_dialogs_remove_buddy(PurpleBuddy *buddy)
g_return_if_fail(buddy != NULL);
text = g_strdup_printf(_("You are about to remove %s from your buddy list. Do you want to continue?"),
- buddy->name);
+ purple_buddy_get_name(buddy));
purple_request_action(buddy, NULL, _("Remove Buddy"), text, 0,
- purple_buddy_get_account(buddy), purple_buddy_get_name(buddy), NULL,
- buddy, 2,
- _("_Remove Buddy"), G_CALLBACK(pidgin_dialogs_remove_buddy_cb),
- _("Cancel"), NULL);
+ purple_request_cpar_from_account(
+ purple_buddy_get_account(buddy)),
+ buddy, 2, _("_Remove Buddy"),
+ G_CALLBACK(pidgin_dialogs_remove_buddy_cb), _("Cancel"), NULL);
g_free(text);
}
@@ -1410,10 +1439,9 @@ pidgin_dialogs_remove_chat(PurpleChat *chat)
name ? name : "");
purple_request_action(chat, NULL, _("Remove Chat"), text, 0,
- chat->account, NULL, NULL,
- chat, 2,
- _("_Remove Chat"), G_CALLBACK(pidgin_dialogs_remove_chat_cb),
- _("Cancel"), NULL);
+ purple_request_cpar_from_account(purple_chat_get_account(chat)),
+ chat, 2, _("_Remove Chat"),
+ G_CALLBACK(pidgin_dialogs_remove_chat_cb), _("Cancel"), NULL);
g_free(text);
}
diff --git a/pidgin/gtkdialogs.h b/pidgin/gtkdialogs.h
index 821ccb6a5a..a0ff31fb00 100644
--- a/pidgin/gtkdialogs.h
+++ b/pidgin/gtkdialogs.h
@@ -1,7 +1,3 @@
-/**
- * @defgroup pidgin Pidgin (GTK+ User Interface)
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -22,14 +18,23 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _PIDGINDIALOGS_H_
#define _PIDGINDIALOGS_H_
+/**
+ * SECTION:gtkdialogs
+ * @section_id: pidgin-gtkdialogs
+ * @short_description: <filename>gtkdialogs.h</filename>
+ * @title: Pidgin Dialogs
+ */
#include "pidgin.h"
#include "account.h"
#include "conversation.h"
+G_BEGIN_DECLS
+
/* Functions in gtkdialogs.c (these should actually stay in this file) */
void pidgin_dialogs_destroy_all(void);
void pidgin_dialogs_about(void);
@@ -42,14 +47,6 @@ void pidgin_dialogs_im_with_user(PurpleAccount *, const char *);
void pidgin_dialogs_info(void);
void pidgin_dialogs_log(void);
-#if !(defined PIDGIN_DISABLE_DEPRECATED) || (defined _PIDGIN_GTKDIALOGS_C_)
-/**
- * @deprecated This function is no longer used and will be removed in
- * Pidgin 3.0.0 unless there is sufficient demand to keep it.
- */
-void pidgin_dialogs_alias_contact(PurpleContact *);
-#endif
-
void pidgin_dialogs_alias_buddy(PurpleBuddy *);
void pidgin_dialogs_alias_chat(PurpleChat *);
void pidgin_dialogs_remove_buddy(PurpleBuddy *);
@@ -58,15 +55,6 @@ void pidgin_dialogs_remove_chat(PurpleChat *);
void pidgin_dialogs_remove_contact(PurpleContact *);
void pidgin_dialogs_merge_groups(PurpleGroup *, const char *);
-/* Everything after this should probably be moved elsewhere */
-
-#ifndef PIDGIN_DISABLE_DEPRECATED
-/* This PIDGIN_DISABLE_DEPRECATED doesn't need to be deactivated by
- * _PIDGIN_GTKDIALOGS_C_, because it shouldn't be using this macro. */
-#define PIDGIN_DIALOG(x) x = gtk_window_new(GTK_WINDOW_TOPLEVEL); \
- gtk_window_set_type_hint(GTK_WINDOW(x), GDK_WINDOW_TYPE_HINT_DIALOG)
-#endif
-
-#define PIDGIN_WINDOW_ICONIFIED(x) (gdk_window_get_state(GTK_WIDGET(x)->window) & GDK_WINDOW_STATE_ICONIFIED)
+G_END_DECLS
#endif /* _PIDGINDIALOGS_H_ */
diff --git a/pidgin/gtkdnd-hints.c b/pidgin/gtkdnd-hints.c
index faed31c511..11db9c4f1b 100644
--- a/pidgin/gtkdnd-hints.c
+++ b/pidgin/gtkdnd-hints.c
@@ -1,8 +1,3 @@
-/*
- * @file gtkdnd-hints.c GTK+ Drag-and-Drop arrow hints
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -26,13 +21,12 @@
#include "gtkdnd-hints.h"
-#include <gtk/gtk.h>
+#include "internal.h"
+
#include <gdk/gdk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
-#ifdef _WIN32
-#include "win32dep.h"
-#endif
+#include "gtk3compat.h"
typedef struct
{
@@ -43,8 +37,8 @@ typedef struct
} HintWindowInfo;
-/**
- * Info about each hint widget. See DndHintWindowId enum.
+/*
+ * Info about each hint widget. See PidginDndHintWindowId enum.
*/
static HintWindowInfo hint_windows[] = {
{ NULL, "arrow-up.xpm", -13/2, 0 },
@@ -54,6 +48,59 @@ static HintWindowInfo hint_windows[] = {
{ NULL, NULL, 0, 0 }
};
+#if GTK_CHECK_VERSION(3,0,0)
+
+static void
+dnd_hints_realized_cb(GtkWidget *window, GtkWidget *pix)
+{
+ GdkPixbuf *pixbuf;
+ cairo_surface_t *surface;
+ cairo_region_t *region;
+ cairo_t *cr;
+
+ pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(pix));
+
+ surface = cairo_image_surface_create(CAIRO_FORMAT_A1,
+ gdk_pixbuf_get_width(pixbuf),
+ gdk_pixbuf_get_height(pixbuf));
+
+ cr = cairo_create(surface);
+ gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
+ cairo_paint(cr);
+ cairo_destroy(cr);
+
+ region = gdk_cairo_region_create_from_surface(surface);
+ gtk_widget_shape_combine_region(window, region);
+ cairo_region_destroy(region);
+
+ cairo_surface_destroy(surface);
+}
+
+static GtkWidget *
+dnd_hints_init_window(const gchar *fname)
+{
+ GdkPixbuf *pixbuf;
+ GtkWidget *pix;
+ GtkWidget *win;
+
+ pixbuf = gdk_pixbuf_new_from_file(fname, NULL);
+ g_return_val_if_fail(pixbuf, NULL);
+
+ win = gtk_window_new(GTK_WINDOW_POPUP);
+ pix = gtk_image_new_from_pixbuf(pixbuf);
+ gtk_container_add(GTK_CONTAINER(win), pix);
+ gtk_widget_show_all(pix);
+
+ g_object_unref(G_OBJECT(pixbuf));
+
+ g_signal_connect(G_OBJECT(win), "realize",
+ G_CALLBACK(dnd_hints_realized_cb), pix);
+
+ return win;
+}
+
+#else
+
static GtkWidget *
dnd_hints_init_window(const gchar *fname)
{
@@ -62,19 +109,20 @@ dnd_hints_init_window(const gchar *fname)
GdkBitmap *bitmap;
GtkWidget *pix;
GtkWidget *win;
+ GdkColormap *colormap;
pixbuf = gdk_pixbuf_new_from_file(fname, NULL);
g_return_val_if_fail(pixbuf, NULL);
- gdk_pixbuf_render_pixmap_and_mask(pixbuf, &pixmap, &bitmap, 128);
+ win = gtk_window_new(GTK_WINDOW_POPUP);
+ colormap = gtk_widget_get_colormap(win);
+ gdk_pixbuf_render_pixmap_and_mask_for_colormap(pixbuf, colormap,
+ &pixmap, &bitmap, 128);
g_object_unref(G_OBJECT(pixbuf));
- gtk_widget_push_colormap(gdk_rgb_get_colormap());
- win = gtk_window_new(GTK_WINDOW_POPUP);
pix = gtk_image_new_from_pixmap(pixmap, bitmap);
gtk_container_add(GTK_CONTAINER(win), pix);
gtk_widget_shape_combine_mask(win, bitmap, 0, 0);
- gtk_widget_pop_colormap();
g_object_unref(G_OBJECT(pixmap));
g_object_unref(G_OBJECT(bitmap));
@@ -84,21 +132,29 @@ dnd_hints_init_window(const gchar *fname)
return win;
}
+#endif
+
static void
get_widget_coords(GtkWidget *w, gint *x1, gint *y1, gint *x2, gint *y2)
{
gint ox, oy, width, height;
+ GtkWidget *parent = gtk_widget_get_parent(w);
- if (w->parent && w->parent->window == w->window)
+ if (parent && gtk_widget_get_window(parent) == gtk_widget_get_window(w))
{
- get_widget_coords(w->parent, &ox, &oy, NULL, NULL);
- height = w->allocation.height;
- width = w->allocation.width;
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation(w, &allocation);
+ get_widget_coords(parent, &ox, &oy, NULL, NULL);
+ height = allocation.height;
+ width = allocation.width;
}
else
{
- gdk_window_get_origin(w->window, &ox, &oy);
- gdk_drawable_get_size(w->window, &width, &height);
+ GdkWindow *win = gtk_widget_get_window(w);
+ gdk_window_get_origin(win, &ox, &oy);
+ width = gdk_window_get_width(win);
+ height = gdk_window_get_height(win);
}
if (x1) *x1 = ox;
@@ -121,8 +177,8 @@ dnd_hints_init(void)
for (i = 0; hint_windows[i].filename != NULL; i++) {
gchar *fname;
- fname = g_build_filename(DATADIR, "pixmaps", "pidgin",
- hint_windows[i].filename, NULL);
+ fname = g_build_filename(PURPLE_DATADIR, "pixmaps", "pidgin",
+ hint_windows[i].filename, NULL);
hint_windows[i].widget = dnd_hints_init_window(fname);
@@ -131,16 +187,16 @@ dnd_hints_init(void)
}
void
-dnd_hints_hide_all(void)
+pidgin_dnd_hints_hide_all(void)
{
gint i;
for (i = 0; hint_windows[i].filename != NULL; i++)
- dnd_hints_hide(i);
+ pidgin_dnd_hints_hide(i);
}
void
-dnd_hints_hide(DndHintWindowId i)
+pidgin_dnd_hints_hide(PidginDndHintWindowId i)
{
GtkWidget *w = hint_windows[i].widget;
@@ -149,7 +205,7 @@ dnd_hints_hide(DndHintWindowId i)
}
void
-dnd_hints_show(DndHintWindowId id, gint x, gint y)
+pidgin_dnd_hints_show(PidginDndHintWindowId id, gint x, gint y)
{
GtkWidget *w;
@@ -166,15 +222,18 @@ dnd_hints_show(DndHintWindowId id, gint x, gint y)
}
void
-dnd_hints_show_relative(DndHintWindowId id, GtkWidget *widget,
- DndHintPosition horiz, DndHintPosition vert)
+pidgin_dnd_hints_show_relative(PidginDndHintWindowId id, GtkWidget *widget,
+ PidginDndHintPosition horiz, PidginDndHintPosition vert)
{
gint x1, x2, y1, y2;
gint x = 0, y = 0;
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation(widget, &allocation);
get_widget_coords(widget, &x1, &y1, &x2, &y2);
- x1 += widget->allocation.x; x2 += widget->allocation.x;
- y1 += widget->allocation.y; y2 += widget->allocation.y;
+ x1 += allocation.x; x2 += allocation.x;
+ y1 += allocation.y; y2 += allocation.y;
switch (horiz)
{
@@ -183,7 +242,7 @@ dnd_hints_show_relative(DndHintWindowId id, GtkWidget *widget,
case HINT_POSITION_CENTER: x = (x1 + x2) / 2; break;
default:
/* should not happen */
- g_warning("Invalid parameter to dnd_hints_show_relative");
+ g_warning("Invalid parameter to pidgin_dnd_hints_show_relative");
break;
}
@@ -194,10 +253,10 @@ dnd_hints_show_relative(DndHintWindowId id, GtkWidget *widget,
case HINT_POSITION_CENTER: y = (y1 + y2) / 2; break;
default:
/* should not happen */
- g_warning("Invalid parameter to dnd_hints_show_relative");
+ g_warning("Invalid parameter to pidgin_dnd_hints_show_relative");
break;
}
- dnd_hints_show(id, x, y);
+ pidgin_dnd_hints_show(id, x, y);
}
diff --git a/pidgin/gtkdnd-hints.h b/pidgin/gtkdnd-hints.h
index a53b9b5835..78856c0ad1 100644
--- a/pidgin/gtkdnd-hints.h
+++ b/pidgin/gtkdnd-hints.h
@@ -1,8 +1,3 @@
-/**
- * @file gtkdnd-hints.h GTK+ Drag-and-Drop arrow hints
- * @ingroup 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.
@@ -21,67 +16,96 @@
* 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_DND_HINTS_H_
#define _PIDGIN_DND_HINTS_H_
+/**
+ * SECTION:gtkdnd-hints
+ * @section_id: pidgin-gtkdnd-hints
+ * @short_description: <filename>gtkdnd-hints.h</filename>
+ * @title: Drag-and-Drop Arrow Hints
+ */
#include <glib.h>
#include <gtk/gtk.h>
/**
+ * PidginDndHintWindowId:
+ * @HINT_ARROW_UP: Up arrow.
+ * @HINT_ARROW_DOWN: Down arrow.
+ * @HINT_ARROW_LEFT: Left arrow.
+ * @HINT_ARROW_RIGHT: Right arrow.
+ *
* Conversation drag-and-drop arrow types.
*/
typedef enum
{
- HINT_ARROW_UP, /**< Up arrow. */
- HINT_ARROW_DOWN, /**< Down arrow. */
- HINT_ARROW_LEFT, /**< Left arrow. */
- HINT_ARROW_RIGHT /**< Right arrow. */
+ HINT_ARROW_UP,
+ HINT_ARROW_DOWN,
+ HINT_ARROW_LEFT,
+ HINT_ARROW_RIGHT
-} DndHintWindowId;
+} PidginDndHintWindowId;
/**
+ * PidginDndHintPosition:
+ * @HINT_POSITION_RIGHT: Position to the right of a tab.
+ * @HINT_POSITION_LEFT: Position to the left of a tab.
+ * @HINT_POSITION_TOP: Position above a tab.
+ * @HINT_POSITION_BOTTOM: Position below a tab.
+ * @HINT_POSITION_CENTER: Position in the center of a tab.
+ *
* Conversation drag-and-drop arrow positions.
*/
typedef enum {
- HINT_POSITION_RIGHT, /**< Position to the right of a tab. */
- HINT_POSITION_LEFT, /**< Position to the left of a tab. */
- HINT_POSITION_TOP, /**< Position above a tab. */
- HINT_POSITION_BOTTOM, /**< Position below a tab. */
- HINT_POSITION_CENTER /**< Position in the center of a tab. */
+ HINT_POSITION_RIGHT,
+ HINT_POSITION_LEFT,
+ HINT_POSITION_TOP,
+ HINT_POSITION_BOTTOM,
+ HINT_POSITION_CENTER
+
+} PidginDndHintPosition;
-} DndHintPosition;
+G_BEGIN_DECLS
/**
- * Shows a drag-and-drop hint at the specified location.
+ * pidgin_dnd_hints_show:
+ * @id: The ID of the hint to show.
+ * @x: The X location to show it at.
+ * @y: The Y location to show it at.
*
- * @param id The ID of the hint to show.
- * @param x The X location to show it at.
- * @param y The Y location to show it at.
+ * Shows a drag-and-drop hint at the specified location.
*/
-void dnd_hints_show(DndHintWindowId id, gint x, gint y);
+void pidgin_dnd_hints_show(PidginDndHintWindowId id, gint x, gint y);
/**
- * Hides the specified drag-and-drop hint.
+ * pidgin_dnd_hints_hide:
+ * @id: The ID of the hint to hide.
*
- * @param id The ID of the hint to hide.
+ * Hides the specified drag-and-drop hint.
*/
-void dnd_hints_hide(DndHintWindowId id);
+void pidgin_dnd_hints_hide(PidginDndHintWindowId id);
/**
+ * pidgin_dnd_hints_hide_all:
+ *
* Hides all drag-and-drop hints.
*/
-void dnd_hints_hide_all(void);
+void pidgin_dnd_hints_hide_all(void);
/**
- * Shows a drag-and-drop hint relative to a widget.
+ * pidgin_dnd_hints_show_relative:
+ * @id: The ID of the hint.
+ * @widget: The widget that the hint is relative to.
+ * @horiz: The horizontal relative position.
+ * @vert: The vertical relative position.
*
- * @param id The ID of the hint.
- * @param widget The widget that the hint is relative to.
- * @param horiz The horizontal relative position.
- * @param vert The vertical relative position.
+ * Shows a drag-and-drop hint relative to a widget.
*/
-void dnd_hints_show_relative(DndHintWindowId id, GtkWidget *widget,
- DndHintPosition horiz, DndHintPosition vert);
+void pidgin_dnd_hints_show_relative(PidginDndHintWindowId id, GtkWidget *widget,
+ PidginDndHintPosition horiz, PidginDndHintPosition vert);
+
+G_END_DECLS
#endif /* _PIDGIN_DND_HINTS_H_ */
diff --git a/pidgin/gtkdocklet-gtk.c b/pidgin/gtkdocklet-gtk.c
deleted file mode 100644
index f70916bb57..0000000000
--- a/pidgin/gtkdocklet-gtk.c
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- * System tray icon (aka docklet) plugin for Purple
- *
- * Copyright (C) 2007 Anders Hasselqvist
- *
- * 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., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
- */
-
-#include "internal.h"
-#include "pidgin.h"
-#include "debug.h"
-#include "prefs.h"
-#include "pidginstock.h"
-#include "gtkdocklet.h"
-
-#define SHORT_EMBED_TIMEOUT 5
-#define LONG_EMBED_TIMEOUT 15
-
-/* globals */
-static GtkStatusIcon *docklet = NULL;
-static guint embed_timeout = 0;
-
-/* protos */
-static void docklet_gtk_status_create(gboolean);
-
-static gboolean
-docklet_gtk_recreate_cb(gpointer data)
-{
- docklet_gtk_status_create(TRUE);
-
- return FALSE;
-}
-
-static gboolean
-docklet_gtk_embed_timeout_cb(gpointer data)
-{
-#if !GTK_CHECK_VERSION(2,12,0)
- if (gtk_status_icon_is_embedded(docklet)) {
- /* Older GTK+ (<2.12) don't implement the embedded signal, but the
- information is still accessable through the above function. */
- purple_debug_info("docklet", "embedded\n");
-
- pidgin_docklet_embedded();
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", TRUE);
- }
- else
-#endif
- {
- /* The docklet was not embedded within the timeout.
- * Remove it as a visibility manager, but leave the plugin
- * loaded so that it can embed automatically if/when a notification
- * area becomes available.
- */
- purple_debug_info("docklet", "failed to embed within timeout\n");
- pidgin_docklet_remove();
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE);
- }
-
-#if GTK_CHECK_VERSION(2,12,0)
- embed_timeout = 0;
- return FALSE;
-#else
- return TRUE;
-#endif
-}
-
-#if GTK_CHECK_VERSION(2,12,0)
-static gboolean
-docklet_gtk_embedded_cb(GtkWidget *widget, gpointer data)
-{
- if (embed_timeout) {
- purple_timeout_remove(embed_timeout);
- embed_timeout = 0;
- }
-
- if (gtk_status_icon_is_embedded(docklet)) {
- purple_debug_info("docklet", "embedded\n");
-
- pidgin_docklet_embedded();
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", TRUE);
- } else {
- purple_debug_info("docklet", "detached\n");
-
- pidgin_docklet_remove();
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE);
- }
-
- return TRUE;
-}
-#endif
-
-static void
-docklet_gtk_destroyed_cb(GtkWidget *widget, gpointer data)
-{
- purple_debug_info("docklet", "destroyed\n");
-
- pidgin_docklet_remove();
-
- g_object_unref(G_OBJECT(docklet));
- docklet = NULL;
-
- g_idle_add(docklet_gtk_recreate_cb, NULL);
-}
-
-static void
-docklet_gtk_status_activated_cb(GtkStatusIcon *status_icon, gpointer user_data)
-{
- pidgin_docklet_clicked(1);
-}
-
-static void
-docklet_gtk_status_clicked_cb(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data)
-{
- purple_debug_info("docklet", "The button is %u\n", button);
-#ifdef GDK_WINDOWING_QUARTZ
- /* You can only click left mouse button on MacOSX native GTK. Let that be the menu */
- pidgin_docklet_clicked(3);
-#else
- pidgin_docklet_clicked(button);
-#endif
-}
-
-static void
-docklet_gtk_status_update_icon(PurpleStatusPrimitive status, gboolean connecting, gboolean pending)
-{
- const gchar *icon_name = NULL;
-
- switch (status) {
- case PURPLE_STATUS_OFFLINE:
- icon_name = PIDGIN_STOCK_TRAY_OFFLINE;
- break;
- case PURPLE_STATUS_AWAY:
- icon_name = PIDGIN_STOCK_TRAY_AWAY;
- break;
- case PURPLE_STATUS_UNAVAILABLE:
- icon_name = PIDGIN_STOCK_TRAY_BUSY;
- break;
- case PURPLE_STATUS_EXTENDED_AWAY:
- icon_name = PIDGIN_STOCK_TRAY_XA;
- break;
- case PURPLE_STATUS_INVISIBLE:
- icon_name = PIDGIN_STOCK_TRAY_INVISIBLE;
- break;
- default:
- icon_name = PIDGIN_STOCK_TRAY_AVAILABLE;
- break;
- }
-
- if (pending)
- icon_name = PIDGIN_STOCK_TRAY_PENDING;
- if (connecting)
- icon_name = PIDGIN_STOCK_TRAY_CONNECT;
-
- if (icon_name) {
- gtk_status_icon_set_from_icon_name(docklet, icon_name);
- }
-
- if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/blink")) {
- gtk_status_icon_set_blinking(docklet, (pending && !connecting));
- } else if (gtk_status_icon_get_blinking(docklet)) {
- gtk_status_icon_set_blinking(docklet, FALSE);
- }
-}
-
-static void
-docklet_gtk_status_set_tooltip(gchar *tooltip)
-{
- gtk_status_icon_set_tooltip(docklet, tooltip);
-}
-
-static void
-docklet_gtk_status_position_menu(GtkMenu *menu,
- int *x, int *y, gboolean *push_in,
- gpointer user_data)
-{
- gtk_status_icon_position_menu(menu, x, y, push_in, docklet);
-}
-
-static void
-docklet_gtk_status_destroy(void)
-{
- g_return_if_fail(docklet != NULL);
-
- pidgin_docklet_remove();
-
- if (embed_timeout) {
- purple_timeout_remove(embed_timeout);
- embed_timeout = 0;
- }
-
- gtk_status_icon_set_visible(docklet, FALSE);
- g_signal_handlers_disconnect_by_func(G_OBJECT(docklet), G_CALLBACK(docklet_gtk_destroyed_cb), NULL);
- g_object_unref(G_OBJECT(docklet));
- docklet = NULL;
-
- purple_debug_info("docklet", "GTK+ destroyed\n");
-}
-
-static void
-docklet_gtk_status_create(gboolean recreate)
-{
- if (docklet) {
- /* if this is being called when a tray icon exists, it's because
- something messed up. try destroying it before we proceed,
- although docklet_refcount may be all hosed. hopefully won't happen. */
- purple_debug_warning("docklet", "trying to create icon but it already exists?\n");
- docklet_gtk_status_destroy();
- }
-
- docklet = gtk_status_icon_new();
- g_return_if_fail(docklet != NULL);
-
- g_signal_connect(G_OBJECT(docklet), "activate", G_CALLBACK(docklet_gtk_status_activated_cb), NULL);
- g_signal_connect(G_OBJECT(docklet), "popup-menu", G_CALLBACK(docklet_gtk_status_clicked_cb), NULL);
-#if GTK_CHECK_VERSION(2,12,0)
- g_signal_connect(G_OBJECT(docklet), "notify::embedded", G_CALLBACK(docklet_gtk_embedded_cb), NULL);
-#endif
- g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_gtk_destroyed_cb), NULL);
-
- gtk_status_icon_set_visible(docklet, TRUE);
-
- /* This is a hack to avoid a race condition between the docklet getting
- * embedded in the notification area and the gtkblist restoring its
- * previous visibility state. If the docklet does not get embedded within
- * the timeout, it will be removed as a visibility manager until it does
- * get embedded. Ideally, we would only call docklet_embedded() when the
- * icon was actually embedded. This only happens when the docklet is first
- * created, not when being recreated.
- *
- * The gtk docklet tracks whether it successfully embedded in a pref and
- * allows for a longer timeout period if it successfully embedded the last
- * time it was run. This should hopefully solve problems with the buddy
- * list not properly starting hidden when Pidgin is started on login.
- */
- if (!recreate) {
- pidgin_docklet_embedded();
-#if GTK_CHECK_VERSION(2,12,0)
- if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded")) {
- embed_timeout = purple_timeout_add_seconds(LONG_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL);
- } else {
- embed_timeout = purple_timeout_add_seconds(SHORT_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL);
- }
-#else
- embed_timeout = purple_timeout_add_seconds(SHORT_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL);
-#endif
- }
-
- purple_debug_info("docklet", "GTK+ created\n");
-}
-
-static void
-docklet_gtk_status_create_ui_op(void)
-{
- docklet_gtk_status_create(FALSE);
-}
-
-static struct docklet_ui_ops ui_ops =
-{
- docklet_gtk_status_create_ui_op,
- docklet_gtk_status_destroy,
- docklet_gtk_status_update_icon,
- NULL,
- docklet_gtk_status_set_tooltip,
- docklet_gtk_status_position_menu
-};
-
-void
-docklet_ui_init(void)
-{
- pidgin_docklet_set_ui_ops(&ui_ops);
-
- purple_prefs_add_none(PIDGIN_PREFS_ROOT "/docklet/gtk");
- if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/x11/embedded")) {
- purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", TRUE);
- purple_prefs_remove(PIDGIN_PREFS_ROOT "/docklet/x11/embedded");
- } else {
- purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE);
- }
-
- gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(),
- DATADIR G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S "pidgin" G_DIR_SEPARATOR_S "tray");
-}
-
diff --git a/pidgin/gtkdocklet.c b/pidgin/gtkdocklet.c
index bbf997bc63..e59e265410 100644
--- a/pidgin/gtkdocklet.c
+++ b/pidgin/gtkdocklet.c
@@ -30,6 +30,7 @@
#include "prefs.h"
#include "signals.h"
#include "sound.h"
+#include "status.h"
#include "gtkaccount.h"
#include "gtkblist.h"
@@ -43,46 +44,87 @@
#include "pidginstock.h"
#include "gtkdocklet.h"
#include "gtkdialogs.h"
+#include "gtknotify.h"
+
+#include "gtk3compat.h"
#ifndef DOCKLET_TOOLTIP_LINE_LIMIT
#define DOCKLET_TOOLTIP_LINE_LIMIT 5
#endif
+#define SHORT_EMBED_TIMEOUT 5
+#define LONG_EMBED_TIMEOUT 15
+
/* globals */
-static struct docklet_ui_ops *ui_ops = NULL;
+static GtkStatusIcon *docklet = NULL;
+static guint embed_timeout = 0;
static PurpleStatusPrimitive status = PURPLE_STATUS_OFFLINE;
-static gboolean pending = FALSE;
-static gboolean connecting = FALSE;
+static PidginDockletFlag flags = 0;
static gboolean enable_join_chat = FALSE;
-static guint docklet_blinking_timer = 0;
static gboolean visible = FALSE;
static gboolean visibility_manager = FALSE;
+/* protos */
+static void docklet_gtk_status_create(gboolean);
+static void docklet_gtk_status_destroy(void);
+
/**************************************************************************
* docklet status and utility functions
**************************************************************************/
-static gboolean
-docklet_blink_icon(gpointer data)
+static inline gboolean
+docklet_is_blinking()
{
- static gboolean blinked = FALSE;
- gboolean ret = FALSE; /* by default, don't keep blinking */
+ return flags && !(flags & PIDGIN_DOCKLET_CONNECTING);
+}
- blinked = !blinked;
+static void
+docklet_gtk_status_update_icon(PurpleStatusPrimitive status, PidginDockletFlag newflag)
+{
+ const gchar *icon_name = NULL;
- if(pending && !connecting) {
- if (blinked) {
- if (ui_ops && ui_ops->blank_icon)
- ui_ops->blank_icon();
- } else {
- pidgin_docklet_update_icon();
- }
- ret = TRUE; /* keep blinking */
- } else {
- docklet_blinking_timer = 0;
- blinked = FALSE;
+ switch (status) {
+ case PURPLE_STATUS_OFFLINE:
+ icon_name = PIDGIN_STOCK_TRAY_OFFLINE;
+ break;
+ case PURPLE_STATUS_AWAY:
+ icon_name = PIDGIN_STOCK_TRAY_AWAY;
+ break;
+ case PURPLE_STATUS_UNAVAILABLE:
+ icon_name = PIDGIN_STOCK_TRAY_BUSY;
+ break;
+ case PURPLE_STATUS_EXTENDED_AWAY:
+ icon_name = PIDGIN_STOCK_TRAY_XA;
+ break;
+ case PURPLE_STATUS_INVISIBLE:
+ icon_name = PIDGIN_STOCK_TRAY_INVISIBLE;
+ break;
+ default:
+ icon_name = PIDGIN_STOCK_TRAY_AVAILABLE;
+ break;
}
- return ret;
+ if (newflag & PIDGIN_DOCKLET_EMAIL_PENDING)
+ icon_name = PIDGIN_STOCK_TRAY_EMAIL;
+ if (newflag & PIDGIN_DOCKLET_CONV_PENDING)
+ icon_name = PIDGIN_STOCK_TRAY_PENDING;
+ if (newflag & PIDGIN_DOCKLET_CONNECTING)
+ icon_name = PIDGIN_STOCK_TRAY_CONNECT;
+
+ if (icon_name) {
+ gtk_status_icon_set_from_icon_name(docklet, icon_name);
+ }
+
+#if !GTK_CHECK_VERSION(3,0,0)
+ if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/blink")) {
+ gboolean pending = FALSE;
+ pending |= (newflag & PIDGIN_DOCKLET_EMAIL_PENDING);
+ pending |= (newflag & PIDGIN_DOCKLET_CONV_PENDING);
+ gtk_status_icon_set_blinking(docklet, pending &&
+ !(newflag & PIDGIN_DOCKLET_CONNECTING));
+ } else if (gtk_status_icon_get_blinking(docklet)) {
+ gtk_status_icon_set_blinking(docklet, FALSE);
+ }
+#endif
}
static GList *
@@ -90,16 +132,14 @@ get_pending_list(guint max)
{
GList *l_im, *l_chat;
- l_im = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_IM,
- PIDGIN_UNSEEN_TEXT,
- FALSE, max);
+ l_im = pidgin_conversations_get_unseen_ims(PIDGIN_UNSEEN_TEXT, FALSE, max);
/* Short circuit if we have our information already */
if (max == 1 && l_im != NULL)
return l_im;
- l_chat = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_CHAT,
- PIDGIN_UNSEEN_NICK,
+ l_chat = pidgin_conversations_get_unseen_chats(
+ purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/notification_chat"),
FALSE, max);
if (l_im != NULL && l_chat != NULL)
@@ -117,7 +157,7 @@ docklet_update_status(void)
int count;
PurpleSavedStatus *saved_status;
PurpleStatusPrimitive newstatus = PURPLE_STATUS_OFFLINE;
- gboolean newpending = FALSE, newconnecting = FALSE;
+ PidginDockletFlag newflags = 0;
/* get the current savedstatus */
saved_status = purple_savedstatus_get_current();
@@ -126,12 +166,12 @@ docklet_update_status(void)
convs = get_pending_list(DOCKLET_TOOLTIP_LINE_LIMIT);
if (!strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/docklet/show"), "pending")) {
- if (convs && ui_ops->create && !visible) {
+ if (convs && !visible) {
g_list_free(convs);
- ui_ops->create();
+ docklet_gtk_status_create(FALSE);
return FALSE;
- } else if (!convs && ui_ops->destroy && visible) {
- ui_ops->destroy();
+ } else if (!convs && visible) {
+ docklet_gtk_status_destroy();
return FALSE;
}
}
@@ -142,46 +182,43 @@ docklet_update_status(void)
}
if (convs != NULL) {
- newpending = TRUE;
-
/* set tooltip if messages are pending */
- if (ui_ops->set_tooltip) {
- GString *tooltip_text = g_string_new("");
- for (l = convs, count = 0 ; l != NULL ; l = l->next, count++) {
- PurpleConversation *conv = (PurpleConversation *)l->data;
- PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
-
- if (count == DOCKLET_TOOLTIP_LINE_LIMIT - 1) {
- g_string_append(tooltip_text, _("Right-click for more unread messages...\n"));
- } else if(gtkconv) {
- g_string_append_printf(tooltip_text,
- ngettext("%d unread message from %s\n", "%d unread messages from %s\n", gtkconv->unseen_count),
- gtkconv->unseen_count,
- purple_conversation_get_title(conv));
- } else {
- g_string_append_printf(tooltip_text,
- ngettext("%d unread message from %s\n", "%d unread messages from %s\n",
- GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count"))),
- GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count")),
- purple_conversation_get_title(conv));
- }
+ GString *tooltip_text = g_string_new("");
+ newflags |= PIDGIN_DOCKLET_CONV_PENDING;
+
+ for (l = convs, count = 0 ; l != NULL ; l = l->next, count++) {
+ PurpleConversation *conv = (PurpleConversation *)l->data;
+ PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
+
+ if (count == DOCKLET_TOOLTIP_LINE_LIMIT - 1) {
+ g_string_append(tooltip_text, _("Right-click for more unread messages...\n"));
+ } else if(gtkconv) {
+ g_string_append_printf(tooltip_text,
+ ngettext("%d unread message from %s\n", "%d unread messages from %s\n", gtkconv->unseen_count),
+ gtkconv->unseen_count,
+ purple_conversation_get_title(conv));
+ } else {
+ g_string_append_printf(tooltip_text,
+ ngettext("%d unread message from %s\n", "%d unread messages from %s\n",
+ GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "unseen-count"))),
+ GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "unseen-count")),
+ purple_conversation_get_title(conv));
}
+ }
- /* get rid of the last newline */
- if (tooltip_text->len > 0)
- tooltip_text = g_string_truncate(tooltip_text, tooltip_text->len - 1);
-
- ui_ops->set_tooltip(tooltip_text->str);
+ /* get rid of the last newline */
+ if (tooltip_text->len > 0)
+ tooltip_text = g_string_truncate(tooltip_text, tooltip_text->len - 1);
- g_string_free(tooltip_text, TRUE);
- }
+ gtk_status_icon_set_tooltip_text(docklet, tooltip_text->str);
+ g_string_free(tooltip_text, TRUE);
g_list_free(convs);
- } else if (ui_ops->set_tooltip) {
+ } else {
char *tooltip_text = g_strconcat(PIDGIN_NAME, " - ",
purple_savedstatus_get_title(saved_status), NULL);
- ui_ops->set_tooltip(tooltip_text);
+ gtk_status_icon_set_tooltip_text(docklet, tooltip_text);
g_free(tooltip_text);
}
@@ -196,24 +233,20 @@ docklet_update_status(void)
continue;
if (purple_account_is_connecting(account))
- newconnecting = TRUE;
+ newflags |= PIDGIN_DOCKLET_CONNECTING;
+
+ if (pidgin_notify_emails_pending())
+ newflags |= PIDGIN_DOCKLET_EMAIL_PENDING;
}
- newstatus = purple_savedstatus_get_type(saved_status);
+ newstatus = purple_savedstatus_get_primitive_type(saved_status);
/* update the icon if we changed status */
- if (status != newstatus || pending!=newpending || connecting!=newconnecting) {
+ if (status != newstatus || flags != newflags) {
status = newstatus;
- pending = newpending;
- connecting = newconnecting;
+ flags = newflags;
- pidgin_docklet_update_icon();
-
- /* and schedule the blinker function if messages are pending */
- if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/blink")
- && pending && !connecting && docklet_blinking_timer == 0) {
- docklet_blinking_timer = g_timeout_add(500, docklet_blink_icon, NULL);
- }
+ docklet_gtk_status_update_icon(status, flags);
}
return FALSE; /* for when we're called by the glib idle handler */
@@ -227,7 +260,7 @@ online_account_supports_chat(void)
while(c != NULL) {
PurpleConnection *gc = c->data;
- PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
if (prpl_info != NULL && prpl_info->chat_info != NULL)
return TRUE;
c = c->next;
@@ -254,9 +287,9 @@ docklet_update_status_cb(void *data)
}
static void
-docklet_conv_updated_cb(PurpleConversation *conv, PurpleConvUpdateType type)
+docklet_conv_updated_cb(PurpleConversation *conv, PurpleConversationUpdateType type)
{
- if (type == PURPLE_CONV_UPDATE_UNSEEN)
+ if (type == PURPLE_CONVERSATION_UPDATE_UNSEEN)
docklet_update_status();
}
@@ -264,7 +297,7 @@ static void
docklet_signed_on_cb(PurpleConnection *gc)
{
if (!enable_join_chat) {
- if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info != NULL)
+ if (PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc))->chat_info != NULL)
enable_join_chat = TRUE;
}
docklet_update_status();
@@ -274,7 +307,7 @@ static void
docklet_signed_off_cb(PurpleConnection *gc)
{
if (enable_join_chat) {
- if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info != NULL)
+ if (PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc))->chat_info != NULL)
enable_join_chat = online_account_supports_chat();
}
docklet_update_status();
@@ -286,17 +319,15 @@ docklet_show_pref_changed_cb(const char *name, PurplePrefType type,
{
const char *val = value;
if (!strcmp(val, "always")) {
- if (ui_ops->create) {
- if (!visible)
- ui_ops->create();
- else if (!visibility_manager) {
- pidgin_blist_visibility_manager_add();
- visibility_manager = TRUE;
- }
+ if (!visible)
+ docklet_gtk_status_create(FALSE);
+ else if (!visibility_manager) {
+ pidgin_blist_visibility_manager_add();
+ visibility_manager = TRUE;
}
} else if (!strcmp(val, "never")) {
- if (visible && ui_ops->destroy)
- ui_ops->destroy();
+ if (visible)
+ docklet_gtk_status_destroy();
} else {
if (visibility_manager) {
pidgin_blist_visibility_manager_remove();
@@ -304,7 +335,6 @@ docklet_show_pref_changed_cb(const char *name, PurplePrefType type,
}
docklet_update_status();
}
-
}
/**************************************************************************
@@ -313,19 +343,23 @@ docklet_show_pref_changed_cb(const char *name, PurplePrefType type,
static void
docklet_toggle_mute(GtkWidget *toggle, void *data)
{
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/sound/mute", GTK_CHECK_MENU_ITEM(toggle)->active);
+ purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/sound/mute",
+ gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(toggle)));
}
+#if !GTK_CHECK_VERSION(3,0,0)
static void
docklet_toggle_blink(GtkWidget *toggle, void *data)
{
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/blink", GTK_CHECK_MENU_ITEM(toggle)->active);
+ purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/blink",
+ gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(toggle)));
}
+#endif
static void
docklet_toggle_blist(GtkWidget *toggle, void *data)
{
- purple_blist_set_visible(GTK_CHECK_MENU_ITEM(toggle)->active);
+ purple_blist_set_visible(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(toggle)));
}
#ifdef _WIN32
@@ -377,7 +411,7 @@ show_custom_status_editor_cb(GtkMenuItem *menuitem, gpointer user_data)
PurpleSavedStatus *saved_status;
saved_status = purple_savedstatus_get_current();
- if (purple_savedstatus_get_type(saved_status) == PURPLE_STATUS_AVAILABLE)
+ if (purple_savedstatus_get_primitive_type(saved_status) == PURPLE_STATUS_AVAILABLE)
saved_status = purple_savedstatus_new(NULL, PURPLE_STATUS_AWAY);
pidgin_status_editor_show(FALSE,
@@ -415,7 +449,7 @@ activate_status_account_cb(GtkMenuItem *menuitem, gpointer user_data)
for (; iter != NULL; iter = iter->next) {
PurpleSavedStatus *ss = iter->data;
- if ((purple_savedstatus_get_type(ss) == primitive) && purple_savedstatus_is_transient(ss) &&
+ if ((purple_savedstatus_get_primitive_type(ss) == primitive) && purple_savedstatus_is_transient(ss) &&
purple_savedstatus_has_substatuses(ss))
{
gboolean found = FALSE;
@@ -424,7 +458,7 @@ activate_status_account_cb(GtkMenuItem *menuitem, gpointer user_data)
PurpleAccount *acct = tmp->data;
PurpleSavedStatusSub *sub = purple_savedstatus_get_substatus(ss, acct);
if (sub) {
- const PurpleStatusType *sub_type = purple_savedstatus_substatus_get_type(sub);
+ const PurpleStatusType *sub_type = purple_savedstatus_substatus_get_status_type(sub);
const char *subtype_status_id = purple_status_type_get_id(sub_type);
if (subtype_status_id && !strcmp(subtype_status_id,
purple_status_type_get_id(status_type)))
@@ -521,7 +555,7 @@ add_account_statuses(GtkWidget *menu, PurpleAccount *account)
new_menu_item_with_status_icon(menu,
purple_status_type_get_name(status_type),
prim, G_CALLBACK(activate_status_account_cb),
- status_type, 0, 0, NULL);
+ GINT_TO_POINTER(status_type), 0, 0, NULL);
}
}
@@ -575,7 +609,7 @@ docklet_status_submenu(void)
time_t creation_time = purple_savedstatus_get_creation_time(saved_status);
new_menu_item_with_status_icon(submenu,
purple_savedstatus_get_title(saved_status),
- purple_savedstatus_get_type(saved_status), G_CALLBACK(activate_saved_status_cb),
+ purple_savedstatus_get_primitive_type(saved_status), G_CALLBACK(activate_saved_status_cb),
GINT_TO_POINTER(creation_time), 0, 0, NULL);
}
g_list_free(popular_statuses);
@@ -591,7 +625,7 @@ docklet_status_submenu(void)
static void
-plugin_act(GtkObject *obj, PurplePluginAction *pam)
+plugin_act(GtkWidget *widget, PurplePluginAction *pam)
{
if (pam && pam->callback)
pam->callback(pam);
@@ -672,6 +706,7 @@ docklet_menu(void)
{
static GtkWidget *menu = NULL;
GtkWidget *menuitem;
+ GtkMenuPositionFunc pos_func = gtk_status_icon_position_menu;
if (menu) {
gtk_widget_destroy(menu);
@@ -686,7 +721,7 @@ docklet_menu(void)
menuitem = gtk_menu_item_new_with_mnemonic(_("_Unread Messages"));
- if (pending) {
+ if (flags & PIDGIN_DOCKLET_CONV_PENDING) {
GtkWidget *submenu = gtk_menu_new();
GList *l = get_pending_list(0);
if (l == NULL) {
@@ -732,10 +767,12 @@ docklet_menu(void)
g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(docklet_toggle_mute), NULL);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+#if !GTK_CHECK_VERSION(3,0,0)
menuitem = gtk_check_menu_item_new_with_mnemonic(_("_Blink on New Message"));
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/blink"));
g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(docklet_toggle_blink), NULL);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+#endif
pidgin_separator(menu);
@@ -747,29 +784,22 @@ docklet_menu(void)
#ifdef _WIN32
g_signal_connect(menu, "leave-notify-event", G_CALLBACK(docklet_menu_leave_enter), NULL);
g_signal_connect(menu, "enter-notify-event", G_CALLBACK(docklet_menu_leave_enter), NULL);
+ pos_func = NULL;
#endif
gtk_widget_show_all(menu);
gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
- ui_ops->position_menu,
- NULL, 0, gtk_get_current_event_time());
-}
-
-/**************************************************************************
- * public api for ui_ops
- **************************************************************************/
-void
-pidgin_docklet_update_icon()
-{
- if (ui_ops && ui_ops->update_icon)
- ui_ops->update_icon(status, connecting, pending);
+ pos_func,
+ docklet, 0, gtk_get_current_event_time());
}
-void
+static void
pidgin_docklet_clicked(int button_type)
{
switch (button_type) {
case 1:
- if (pending) {
+ if (flags & PIDGIN_DOCKLET_EMAIL_PENDING) {
+ pidgin_notify_emails_present(NULL);
+ } else if (flags & PIDGIN_DOCKLET_CONV_PENDING) {
GList *l = get_pending_list(1);
if (l != NULL) {
pidgin_conv_present_conversation((PurpleConversation *)l->data);
@@ -785,8 +815,8 @@ pidgin_docklet_clicked(int button_type)
}
}
-void
-pidgin_docklet_embedded()
+static void
+pidgin_docklet_embedded(void)
{
if (!visibility_manager
&& strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/docklet/show"), "pending")) {
@@ -795,32 +825,151 @@ pidgin_docklet_embedded()
}
visible = TRUE;
docklet_update_status();
- pidgin_docklet_update_icon();
+ docklet_gtk_status_update_icon(status, flags);
}
-void
-pidgin_docklet_remove()
+static void
+pidgin_docklet_remove(void)
{
if (visible) {
if (visibility_manager) {
pidgin_blist_visibility_manager_remove();
visibility_manager = FALSE;
}
- if (docklet_blinking_timer) {
- g_source_remove(docklet_blinking_timer);
- docklet_blinking_timer = 0;
- }
visible = FALSE;
status = PURPLE_STATUS_OFFLINE;
}
}
-void
-pidgin_docklet_set_ui_ops(struct docklet_ui_ops *ops)
+#ifndef _WIN32
+static gboolean
+docklet_gtk_embed_timeout_cb(gpointer data)
{
- ui_ops = ops;
+ /* The docklet was not embedded within the timeout.
+ * Remove it as a visibility manager, but leave the plugin
+ * loaded so that it can embed automatically if/when a notification
+ * area becomes available.
+ */
+ purple_debug_info("docklet", "failed to embed within timeout\n");
+ pidgin_docklet_remove();
+ purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE);
+
+ embed_timeout = 0;
+ return FALSE;
}
+#endif
+static gboolean
+docklet_gtk_embedded_cb(GtkWidget *widget, gpointer data)
+{
+ if (embed_timeout) {
+ purple_timeout_remove(embed_timeout);
+ embed_timeout = 0;
+ }
+
+ if (gtk_status_icon_is_embedded(docklet)) {
+ purple_debug_info("docklet", "embedded\n");
+
+ pidgin_docklet_embedded();
+ purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", TRUE);
+ } else {
+ purple_debug_info("docklet", "detached\n");
+
+ pidgin_docklet_remove();
+ purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE);
+ }
+
+ return TRUE;
+}
+
+static void
+docklet_gtk_status_activated_cb(GtkStatusIcon *status_icon, gpointer user_data)
+{
+ pidgin_docklet_clicked(1);
+}
+
+static void
+docklet_gtk_status_clicked_cb(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data)
+{
+ purple_debug_info("docklet", "The button is %u\n", button);
+#ifdef GDK_WINDOWING_QUARTZ
+ /* You can only click left mouse button on MacOSX native GTK. Let that be the menu */
+ pidgin_docklet_clicked(3);
+#else
+ pidgin_docklet_clicked(button);
+#endif
+}
+
+static void
+docklet_gtk_status_destroy(void)
+{
+ g_return_if_fail(docklet != NULL);
+
+ pidgin_docklet_remove();
+
+ if (embed_timeout) {
+ purple_timeout_remove(embed_timeout);
+ embed_timeout = 0;
+ }
+
+ gtk_status_icon_set_visible(docklet, FALSE);
+ g_object_unref(G_OBJECT(docklet));
+ docklet = NULL;
+
+ purple_debug_info("docklet", "GTK+ destroyed\n");
+}
+
+static void
+docklet_gtk_status_create(gboolean recreate)
+{
+ if (docklet) {
+ /* if this is being called when a tray icon exists, it's because
+ something messed up. try destroying it before we proceed,
+ although docklet_refcount may be all hosed. hopefully won't happen. */
+ purple_debug_warning("docklet", "trying to create icon but it already exists?\n");
+ docklet_gtk_status_destroy();
+ }
+
+ docklet = gtk_status_icon_new();
+ g_return_if_fail(docklet != NULL);
+
+ g_signal_connect(G_OBJECT(docklet), "activate", G_CALLBACK(docklet_gtk_status_activated_cb), NULL);
+ g_signal_connect(G_OBJECT(docklet), "popup-menu", G_CALLBACK(docklet_gtk_status_clicked_cb), NULL);
+ g_signal_connect(G_OBJECT(docklet), "notify::embedded", G_CALLBACK(docklet_gtk_embedded_cb), NULL);
+
+ gtk_status_icon_set_visible(docklet, TRUE);
+
+ /* This is a hack to avoid a race condition between the docklet getting
+ * embedded in the notification area and the gtkblist restoring its
+ * previous visibility state. If the docklet does not get embedded within
+ * the timeout, it will be removed as a visibility manager until it does
+ * get embedded. Ideally, we would only call docklet_embedded() when the
+ * icon was actually embedded. This only happens when the docklet is first
+ * created, not when being recreated.
+ *
+ * The gtk docklet tracks whether it successfully embedded in a pref and
+ * allows for a longer timeout period if it successfully embedded the last
+ * time it was run. This should hopefully solve problems with the buddy
+ * list not properly starting hidden when Pidgin is started on login.
+ */
+ if (!recreate) {
+ pidgin_docklet_embedded();
+#ifndef _WIN32
+ if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded")) {
+ embed_timeout = purple_timeout_add_seconds(LONG_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL);
+ } else {
+ embed_timeout = purple_timeout_add_seconds(SHORT_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL);
+ }
+#endif
+ }
+
+ purple_debug_info("docklet", "GTK+ created\n");
+}
+
+/**************************************************************************
+ * public api
+ **************************************************************************/
+
void*
pidgin_docklet_get_handle()
{
@@ -828,6 +977,12 @@ pidgin_docklet_get_handle()
return &i;
}
+GtkStatusIcon *
+pidgin_docklet_get_status_icon(void)
+{
+ return docklet;
+}
+
void
pidgin_docklet_init()
{
@@ -836,16 +991,31 @@ pidgin_docklet_init()
void *accounts_handle = purple_accounts_get_handle();
void *status_handle = purple_savedstatuses_get_handle();
void *docklet_handle = pidgin_docklet_get_handle();
+ void *notify_handle = purple_notify_get_handle();
+ gchar *tmp;
purple_prefs_add_none(PIDGIN_PREFS_ROOT "/docklet");
purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/docklet/blink", FALSE);
purple_prefs_add_string(PIDGIN_PREFS_ROOT "/docklet/show", "always");
purple_prefs_connect_callback(docklet_handle, PIDGIN_PREFS_ROOT "/docklet/show",
docklet_show_pref_changed_cb, NULL);
+ purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/notification_chat", PIDGIN_UNSEEN_TEXT);
- docklet_ui_init();
- if (!strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/docklet/show"), "always") && ui_ops && ui_ops->create)
- ui_ops->create();
+ purple_prefs_add_none(PIDGIN_PREFS_ROOT "/docklet/gtk");
+ if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/x11/embedded")) {
+ purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", TRUE);
+ purple_prefs_remove(PIDGIN_PREFS_ROOT "/docklet/x11/embedded");
+ } else {
+ purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE);
+ }
+
+ tmp = g_build_path(G_DIR_SEPARATOR_S, PURPLE_DATADIR,
+ "pixmaps", "pidgin", "tray", NULL);
+ gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), tmp);
+ g_free(tmp);
+
+ if (!strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/docklet/show"), "always"))
+ docklet_gtk_status_create(FALSE);
purple_signal_connect(conn_handle, "signed-on",
docklet_handle, PURPLE_CALLBACK(docklet_signed_on_cb), NULL);
@@ -863,6 +1033,12 @@ pidgin_docklet_init()
docklet_handle, PURPLE_CALLBACK(docklet_conv_updated_cb), NULL);
purple_signal_connect(status_handle, "savedstatus-changed",
docklet_handle, PURPLE_CALLBACK(docklet_update_status_cb), NULL);
+ purple_signal_connect(notify_handle, "displaying-email-notification",
+ docklet_handle, PURPLE_CALLBACK(docklet_update_status_cb), NULL);
+ purple_signal_connect(notify_handle, "displaying-emails-notification",
+ docklet_handle, PURPLE_CALLBACK(docklet_update_status_cb), NULL);
+ purple_signal_connect(notify_handle, "displaying-emails-clear",
+ docklet_handle, PURPLE_CALLBACK(docklet_update_status_cb), NULL);
#if 0
purple_signal_connect(purple_get_core(), "quitting",
docklet_handle, PURPLE_CALLBACK(purple_quit_cb), NULL);
@@ -874,6 +1050,7 @@ pidgin_docklet_init()
void
pidgin_docklet_uninit()
{
- if (visible && ui_ops && ui_ops->destroy)
- ui_ops->destroy();
+ if (visible)
+ docklet_gtk_status_destroy();
}
+
diff --git a/pidgin/gtkdocklet.h b/pidgin/gtkdocklet.h
index 34f88a14f5..4cf1acd926 100644
--- a/pidgin/gtkdocklet.h
+++ b/pidgin/gtkdocklet.h
@@ -24,32 +24,36 @@
#ifndef _GTKDOCKLET_H_
#define _GTKDOCKLET_H_
+/**
+ * SECTION:gtkdocklet
+ * @section_id: pidgin-gtkdocklet
+ * @short_description: <filename>gtkdocklet.h</filename>
+ * @title: Docklet
+ *
+ * This file provides the System tray icon (aka docklet) implementation.
+ */
-#include "status.h"
+G_BEGIN_DECLS
-struct docklet_ui_ops
+typedef enum
{
- void (*create)(void);
- void (*destroy)(void);
- void (*update_icon)(PurpleStatusPrimitive, gboolean, gboolean);
- void (*blank_icon)(void);
- void (*set_tooltip)(gchar *);
- GtkMenuPositionFunc position_menu;
-};
-
-
-/* functions in gtkdocklet.c */
-void pidgin_docklet_update_icon(void);
-void pidgin_docklet_clicked(int);
-void pidgin_docklet_embedded(void);
-void pidgin_docklet_remove(void);
-void pidgin_docklet_set_ui_ops(struct docklet_ui_ops *);
-void pidgin_docklet_unload(void);
+ PIDGIN_DOCKLET_CONNECTING = 0x1,
+ PIDGIN_DOCKLET_CONV_PENDING = 0x2,
+ PIDGIN_DOCKLET_EMAIL_PENDING = 0x4
+} PidginDockletFlag;
+
+/**
+ * pidgin_docklet_get_status_icon:
+ *
+ * Returns: The #GtkStatusIcon used for the docklet.
+ */
+GtkStatusIcon *pidgin_docklet_get_status_icon(void);
+
void pidgin_docklet_init(void);
void pidgin_docklet_uninit(void);
void*pidgin_docklet_get_handle(void);
-/* function in gtkdocklet-{gtk,x11,win32}.c */
-void docklet_ui_init(void);
+G_END_DECLS
#endif /* _GTKDOCKLET_H_ */
+
diff --git a/pidgin/gtkeventloop.c b/pidgin/gtkeventloop.c
index 4192a539ca..6239a8b4da 100644
--- a/pidgin/gtkeventloop.c
+++ b/pidgin/gtkeventloop.c
@@ -1,8 +1,3 @@
-/**
- * @file gtk_eventloop.c Purple Event Loop API (gtk implementation)
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -27,9 +22,7 @@
#include <glib.h>
#include "gtkeventloop.h"
#include "eventloop.h"
-#ifdef _WIN32
-#include "win32dep.h"
-#endif
+#include "internal.h"
#define PIDGIN_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR)
#define PIDGIN_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL)
@@ -59,7 +52,7 @@ static gboolean pidgin_io_invoke(GIOChannel *source, GIOCondition condition, gpo
#ifdef _WIN32
if(! purple_cond) {
-#ifdef DEBUG
+#if 0
purple_debug_misc("gtk_eventloop",
"CLOSURE received GIOCondition of 0x%x, which does not"
" match 0x%x (READ) or 0x%x (WRITE)\n",
@@ -124,11 +117,8 @@ static PurpleEventLoopUiOps eventloop_ops =
pidgin_input_add,
g_source_remove,
NULL, /* input_get_error */
-#if GLIB_CHECK_VERSION(2,14,0)
g_timeout_add_seconds,
-#else
NULL,
-#endif
NULL,
NULL,
NULL
diff --git a/pidgin/gtkeventloop.h b/pidgin/gtkeventloop.h
index 0593a0f9e2..a782477a86 100644
--- a/pidgin/gtkeventloop.h
+++ b/pidgin/gtkeventloop.h
@@ -1,8 +1,3 @@
-/**
- * @file gtkeventloop.h Pidgin GTK+ Event Loop Implementation
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -23,16 +18,29 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _PIDGINEVENTLOOP_H_
#define _PIDGINEVENTLOOP_H_
+/**
+ * SECTION:gtkeventloop
+ * @section_id: pidgin-gtkeventloop
+ * @short_description: <filename>gtkeventloop.h</filename>
+ * @title: Event Loop Implementation
+ */
#include "eventloop.h"
+G_BEGIN_DECLS
+
/**
+ * pidgin_eventloop_get_ui_ops:
+ *
* Returns the GTK+ event loop UI operations structure.
*
- * @return The GTK+ event loop UI operations structure.
+ * Returns: The GTK+ event loop UI operations structure.
*/
PurpleEventLoopUiOps *pidgin_eventloop_get_ui_ops(void);
+G_END_DECLS
+
#endif /* _PIDGINEVENTLOOP_H_ */
diff --git a/pidgin/gtkgaim-compat.h b/pidgin/gtkgaim-compat.h
deleted file mode 100644
index 778ad303bf..0000000000
--- a/pidgin/gtkgaim-compat.h
+++ /dev/null
@@ -1,412 +0,0 @@
-/**
- * @file gtkgaim-compat.h Gtk Gaim Compat macros
- */
-
-/* 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 _GTKGAIM_COMPAT_H_
-#define _GTKGAIM_COMPAT_H_
-
-#include <pidginstock.h>
-
-#define GAIM_ALERT_TITLE PIDGIN_ALERT_TITLE
-#define GAIM_BROWSER_CURRENT PIDGIN_BROWSER_CURRENT
-#define GAIM_BROWSER_DEFAULT PIDGIN_BROWSER_DEFAULT
-#define GAIM_BROWSER_NEW_TAB PIDGIN_BROWSER_NEW_TAB
-#define GAIM_BROWSER_NEW_WINDOW PIDGIN_BROWSER_NEW_WINDOW
-#define GaimBrowserPlace PidginBrowserPlace
-#define GAIM_BUTTON_HORIZONTAL PIDGIN_BUTTON_HORIZONTAL
-#define GAIM_BUTTON_IMAGE PIDGIN_BUTTON_IMAGE
-#define GAIM_BUTTON_NONE PIDGIN_BUTTON_NONE
-#define GaimButtonOrientation PidginButtonOrientation
-#define GaimButtonStyle PidginButtonStyle
-#define GAIM_BUTTON_TEXT_IMAGE PIDGIN_BUTTON_TEXT_IMAGE
-#define GAIM_BUTTON_TEXT PIDGIN_BUTTON_TEXT
-#define GAIM_BUTTON_VERTICAL PIDGIN_BUTTON_VERTICAL
-#define GaimConvPlacementFunc PidginConvPlacementFunc
-#define GAIM_DIALOG PIDGIN_DIALOG
-#define gaim_dnd_file_manage pidgin_dnd_file_manage
-#define gaim_get_gtkxfer_dialog pidgin_get_xfer_dialog
-#define gaim_gtk_account_dialog_show pidgin_account_dialog_show
-#define GaimGtkAccountDialogType PidginAccountDialogType
-#define gaim_gtk_account_get_handle pidgin_account_get_handle
-#define gaim_gtk_account_init pidgin_account_init
-#define gaim_gtk_account_option_menu_get_selected pidgin_account_option_menu_get_selected
-#define gaim_gtk_account_option_menu_new pidgin_account_option_menu_new
-#define gaim_gtk_account_option_menu_set_selected pidgin_account_option_menu_set_selected
-#define gaim_gtk_accounts_get_ui_ops pidgin_accounts_get_ui_ops
-#define gaim_gtk_accounts_window_hide pidgin_accounts_window_hide
-#define gaim_gtk_accounts_window_show pidgin_accounts_window_show
-#define gaim_gtk_account_uninit pidgin_account_uninit
-#define GAIM_GTK_ADD_ACCOUNT_DIALOG PIDGIN_ADD_ACCOUNT_DIALOG
-#define gaim_gtk_append_blist_node_extended_menu pidgin_append_blist_node_extended_menu
-#define gaim_gtk_append_blist_node_privacy_menu pidgin_append_blist_node_privacy_menu
-#define gaim_gtk_append_blist_node_proto_menu pidgin_append_blist_node_proto_menu
-#define gaim_gtk_append_menu_action pidgin_append_menu_action
-#define gaim_gtk_blist_add_alert pidgin_blist_add_alert
-#define gaim_gtk_blist_get_default_gtk_blist pidgin_blist_get_default_gtk_blist
-#define gaim_gtk_blist_get_handle pidgin_blist_get_handle
-#define gaim_gtk_blist_get_sort_methods pidgin_blist_get_sort_methods
-#define gaim_gtk_blist_get_status_icon pidgin_blist_get_status_icon
-#define gaim_gtk_blist_get_ui_ops pidgin_blist_get_ui_ops
-#define gaim_gtk_blist_init pidgin_blist_init
-#define gaim_gtk_blist_joinchat_is_showable pidgin_blist_joinchat_is_showable
-#define gaim_gtk_blist_joinchat_show pidgin_blist_joinchat_show
-#define gaim_gtk_blist_make_buddy_menu pidgin_blist_make_buddy_menu
-#define gaim_gtk_blist_node_is_contact_expanded pidgin_blist_node_is_contact_expanded
-#define GAIM_GTK_BLIST PIDGIN_BLIST
-#define gaim_gtk_blist_refresh pidgin_blist_refresh
-#define gaim_gtk_blist_set_headline pidgin_blist_set_headline
-#define gaim_gtk_blist_setup_sort_methods pidgin_blist_setup_sort_methods
-#define gaim_gtk_blist_sort_function pidgin_blist_sort_function
-#define gaim_gtk_blist_sort_method pidgin_blist_sort_method
-#define GaimGtkBlistSortMethod PidginBlistSortMethod
-#define gaim_gtk_blist_sort_method_reg pidgin_blist_sort_method_reg
-#define gaim_gtk_blist_sort_method_set pidgin_blist_sort_method_set
-#define gaim_gtk_blist_sort_method_unreg pidgin_blist_sort_method_unreg
-#define gaim_gtk_blist_toggle_visibility pidgin_blist_toggle_visibility
-#define gaim_gtk_blist_uninit pidgin_blist_uninit
-#define gaim_gtk_blist_update_account_error_state pidgin_blist_update_account_error_state
-#define gaim_gtk_blist_update_accounts_menu pidgin_blist_update_accounts_menu
-#define gaim_gtk_blist_update_columns pidgin_blist_update_columns
-#define gaim_gtk_blist_update_plugin_actions pidgin_blist_update_plugin_actions
-#define gaim_gtk_blist_update_refresh_timeout pidgin_blist_update_refresh_timeout
-#define gaim_gtk_blist_update_sort_methods pidgin_blist_update_sort_methods
-#define gaim_gtk_blist_visibility_manager_add pidgin_blist_visibility_manager_add
-#define gaim_gtk_blist_visibility_manager_remove pidgin_blist_visibility_manager_remove
-#define gaim_gtk_buddy_icon_chooser_new pidgin_buddy_icon_chooser_new
-#define gaim_gtk_buddy_icon_get_scale_size pidgin_buddy_icon_get_scale_size
-#define GaimGtkBuddyList PidginBuddyList
-#define GaimGtkCellRendererExpanderClass PidginCellRendererExpanderClass
-#define gaim_gtk_cell_renderer_expander_get_type pidgin_cell_renderer_expander_get_type
-#define gaim_gtk_cell_renderer_expander_new pidgin_cell_renderer_expander_new
-#define GaimGtkCellRendererExpander PidginCellRendererExpander
-#define GaimGtkCellRendererProgressClass PidginCellRendererProgressClass
-#define gaim_gtk_cell_renderer_progress_get_type pidgin_cell_renderer_progress_get_type
-#define gaim_gtk_cell_renderer_progress_new pidgin_cell_renderer_progress_new
-#define GaimGtkCellRendererProgress PidginCellRendererProgress
-#define GaimGtkChatPane PidginChatPane
-#define gaim_gtk_check_if_dir pidgin_check_if_dir
-#define gaim_gtk_clear_cursor pidgin_clear_cursor
-#define gaim_gtk_connection_get_handle pidgin_connection_get_handle
-#define gaim_gtk_connection_init pidgin_connection_init
-#define gaim_gtk_connections_get_ui_ops pidgin_connections_get_ui_ops
-#define gaim_gtk_connection_uninit pidgin_connection_uninit
-#define GaimGtkConversation PidginConversation
-#define GAIM_GTK_CONVERSATION PIDGIN_CONVERSATION
-#define gaim_gtk_conversations_fill_menu pidgin_conversations_fill_menu
-#define gaim_gtk_conversations_find_unseen_list pidgin_conversations_find_unseen_list
-#define gaim_gtk_conversations_get_conv_ui_ops pidgin_conversations_get_conv_ui_ops
-#define gaim_gtk_conversations_get_handle pidgin_conversations_get_handle
-#define gaim_gtk_conversations_init pidgin_conversations_init
-#define gaim_gtk_conversations_uninit pidgin_conversations_uninit
-#define gaim_gtk_convert_buddy_icon pidgin_convert_buddy_icon
-#define gaim_gtkconv_get_tab_at_xy pidgin_conv_get_tab_at_xy
-#define gaim_gtkconv_get_tab_icon pidgin_conv_get_tab_icon
-#define gaim_gtkconv_get_window pidgin_conv_get_window
-#define gaim_gtkconv_is_hidden pidgin_conv_is_hidden
-#define gaim_gtkconv_new pidgin_conv_new
-#define gaim_gtkconv_placement_add_fnc pidgin_conv_placement_add_fnc
-#define gaim_gtkconv_placement_get_current_func pidgin_conv_placement_get_current_func
-#define gaim_gtkconv_placement_get_fnc pidgin_conv_placement_get_fnc
-#define gaim_gtkconv_placement_get_name pidgin_conv_placement_get_name
-#define gaim_gtkconv_placement_get_options pidgin_conv_placement_get_options
-#define gaim_gtkconv_placement_place pidgin_conv_placement_place
-#define gaim_gtkconv_placement_remove_fnc pidgin_conv_placement_remove_fnc
-#define gaim_gtkconv_placement_set_current_func pidgin_conv_placement_set_current_func
-#define gaim_gtkconv_present_conversation pidgin_conv_present_conversation
-#define gaim_gtkconv_switch_active_conversation pidgin_conv_switch_active_conversation
-#define gaim_gtkconv_update_buddy_icon pidgin_conv_update_buddy_icon
-#define gaim_gtkconv_update_buttons_by_protocol pidgin_conv_update_buttons_by_protocol
-#define gaim_gtk_conv_window_add_gtkconv pidgin_conv_window_add_gtkconv
-#define gaim_gtk_conv_window_destroy pidgin_conv_window_destroy
-#define gaim_gtk_conv_window_first_with_type pidgin_conv_window_first_with_type
-#define gaim_gtk_conv_window_get_active_conversation pidgin_conv_window_get_active_conversation
-#define gaim_gtk_conv_window_get_active_gtkconv pidgin_conv_window_get_active_gtkconv
-#define gaim_gtk_conv_window_get_at_xy pidgin_conv_window_get_at_xy
-#define gaim_gtk_conv_window_get_gtkconv_at_index pidgin_conv_window_get_gtkconv_at_index
-#define gaim_gtk_conv_window_get_gtkconv_count pidgin_conv_window_get_gtkconv_count
-#define gaim_gtk_conv_window_get_gtkconvs pidgin_conv_window_get_gtkconvs
-#define gaim_gtk_conv_window_has_focus pidgin_conv_window_has_focus
-#define gaim_gtk_conv_window_hide pidgin_conv_window_hide
-#define gaim_gtk_conv_window_is_active_conversation pidgin_conv_window_is_active_conversation
-#define gaim_gtk_conv_window_last_with_type pidgin_conv_window_last_with_type
-#define gaim_gtk_conv_window_new pidgin_conv_window_new
-#define gaim_gtk_conv_window_raise pidgin_conv_window_raise
-#define gaim_gtk_conv_window_remove_gtkconv pidgin_conv_window_remove_gtkconv
-#define gaim_gtk_conv_windows_get_list pidgin_conv_windows_get_list
-#define gaim_gtk_conv_window_show pidgin_conv_window_show
-#define gaim_gtk_conv_window_switch_gtkconv pidgin_conv_window_switch_gtkconv
-#define gaim_gtk_create_imhtml pidgin_create_imhtml
-#define gaim_gtk_debug_get_handle pidgin_debug_get_handle
-#define gaim_gtk_debug_get_ui_ops pidgin_debug_get_ui_ops
-#define gaim_gtk_debug_init pidgin_debug_init
-#define gaim_gtk_debug_uninit pidgin_debug_uninit
-#define gaim_gtk_debug_window_hide pidgin_debug_window_hide
-#define gaim_gtk_debug_window_show pidgin_debug_window_show
-#define gaim_gtkdialogs_about pidgin_dialogs_about
-#define gaim_gtkdialogs_alias_buddy pidgin_dialogs_alias_buddy
-#define gaim_gtkdialogs_alias_chat pidgin_dialogs_alias_chat
-#define gaim_gtkdialogs_alias_contact pidgin_dialogs_alias_contact
-#define gaim_gtkdialogs_destroy_all pidgin_dialogs_destroy_all
-#define gaim_gtkdialogs_im pidgin_dialogs_im
-#define gaim_gtkdialogs_im_with_user pidgin_dialogs_im_with_user
-#define gaim_gtkdialogs_info pidgin_dialogs_info
-#define gaim_gtkdialogs_log pidgin_dialogs_log
-#define gaim_gtkdialogs_merge_groups pidgin_dialogs_merge_groups
-#define gaim_gtkdialogs_remove_buddy pidgin_dialogs_remove_buddy
-#define gaim_gtkdialogs_remove_chat pidgin_dialogs_remove_chat
-#define gaim_gtkdialogs_remove_contact pidgin_dialogs_remove_contact
-#define gaim_gtkdialogs_remove_group pidgin_dialogs_remove_group
-#define gaim_gtk_docklet_clicked pidgin_docklet_clicked
-#define gaim_gtk_docklet_embedded pidgin_docklet_embedded
-#define gaim_gtk_docklet_get_handle pidgin_docklet_get_handle
-#define gaim_gtk_docklet_init pidgin_docklet_init
-#define gaim_gtk_docklet_remove pidgin_docklet_remove
-#define gaim_gtk_docklet_set_ui_ops pidgin_docklet_set_ui_ops
-#define gaim_gtk_docklet_uninit pidgin_docklet_uninit
-#define gaim_gtk_docklet_unload pidgin_docklet_unload
-#define gaim_gtk_eventloop_get_ui_ops pidgin_eventloop_get_ui_ops
-#define gaim_gtk_idle_get_ui_ops pidgin_idle_get_ui_ops
-#define GaimGtkImPane PidginImPane
-#define gaim_gtk_load_accels pidgin_load_accels
-#define gaim_gtk_log_get_handle pidgin_log_get_handle
-#define gaim_gtk_log_init pidgin_log_init
-#define gaim_gtk_log_show_contact pidgin_log_show_contact
-#define gaim_gtk_log_show pidgin_log_show
-#define gaim_gtk_log_uninit pidgin_log_uninit
-#define GaimGtkLogViewer PidginLogViewer
-#define gaim_gtk_make_frame pidgin_make_frame
-#define gaim_gtk_make_mini_dialog pidgin_make_mini_dialog
-#define gaim_gtk_make_pretty_arrows pidgin_make_pretty_arrows
-#define gaim_gtk_menu_tray_append pidgin_menu_tray_append
-#define GaimGtkMenuTrayClass PidginMenuTrayClass
-#define gaim_gtk_menu_tray_get_box pidgin_menu_tray_get_box
-#define gaim_gtk_menu_tray_get_gtype pidgin_menu_tray_get_gtype
-#define gaim_gtk_menu_tray_new pidgin_menu_tray_new
-#define GaimGtkMenuTray PidginMenuTray
-#define GAIM_GTK_MENU_TRAY PIDGIN_MENU_TRAY
-#define gaim_gtk_menu_tray_prepend pidgin_menu_tray_prepend
-#define gaim_gtk_menu_tray_set_tooltip pidgin_menu_tray_set_tooltip
-#define GAIM_GTK_MODIFY_ACCOUNT_DIALOG PIDGIN_MODIFY_ACCOUNT_DIALOG
-#define gaim_gtk_notify_get_ui_ops pidgin_notify_get_ui_ops
-#define gaim_gtk_parse_x_im_contact pidgin_parse_x_im_contact
-#define gaim_gtk_plugin_dialog_show pidgin_plugin_dialog_show
-#define gaim_gtk_plugin_get_config_frame pidgin_plugin_get_config_frame
-#define GAIM_GTK_PLUGIN PIDGIN_PLUGIN
-#define gaim_gtk_plugin_pref_create_frame pidgin_plugin_pref_create_frame
-#define gaim_gtk_plugins_save pidgin_plugins_save
-#define GAIM_GTK_PLUGIN_TYPE PIDGIN_PLUGIN_TYPE
-#define GaimGtkPluginUiInfo PidginPluginUiInfo
-#define GAIM_GTK_PLUGIN_UI_INFO PIDGIN_PLUGIN_UI_INFO
-#define gaim_gtk_pounce_editor_show pidgin_pounce_editor_show
-#define gaim_gtk_pounces_get_handle pidgin_pounces_get_handle
-#define gaim_gtk_pounces_init pidgin_pounces_init
-#define gaim_gtk_pounces_manager_hide pidgin_pounces_manager_hide
-#define gaim_gtk_pounces_manager_show pidgin_pounces_manager_show
-#define gaim_gtk_prefs_checkbox pidgin_prefs_checkbox
-#define gaim_gtk_prefs_dropdown_from_list pidgin_prefs_dropdown_from_list
-#define gaim_gtk_prefs_dropdown pidgin_prefs_dropdown
-#define gaim_gtk_prefs_init pidgin_prefs_init
-#define gaim_gtk_prefs_labeled_entry pidgin_prefs_labeled_entry
-#define gaim_gtk_prefs_labeled_spin_button pidgin_prefs_labeled_spin_button
-#define gaim_gtk_prefs_show pidgin_prefs_show
-#define gaim_gtk_prefs_update_old pidgin_prefs_update_old
-#define gaim_gtk_privacy_dialog_hide pidgin_privacy_dialog_hide
-#define gaim_gtk_privacy_dialog_show pidgin_privacy_dialog_show
-#define gaim_gtk_privacy_get_ui_ops pidgin_privacy_get_ui_ops
-#define gaim_gtk_privacy_init pidgin_privacy_init
-#define gaim_gtk_protocol_option_menu_new pidgin_protocol_option_menu_new
-#define gaim_gtk_request_add_block pidgin_request_add_block
-#define gaim_gtk_request_add_permit pidgin_request_add_permit
-#define gaim_gtk_request_get_ui_ops pidgin_request_get_ui_ops
-#define gaim_gtk_roomlist_dialog_show pidgin_roomlist_dialog_show
-#define gaim_gtk_roomlist_dialog_show_with_account pidgin_roomlist_dialog_show_with_account
-#define gaim_gtk_roomlist_init pidgin_roomlist_init
-#define gaim_gtk_roomlist_is_showable pidgin_roomlist_is_showable
-#define gaim_gtk_save_accels_cb pidgin_save_accels_cb
-#define gaim_gtk_save_accels pidgin_save_accels
-#define gaim_gtk_session_end pidgin_session_end
-#define gaim_gtk_session_init pidgin_session_init
-#define gaim_gtk_set_cursor pidgin_set_cursor
-#define gaim_gtk_set_custom_buddy_icon pidgin_set_custom_buddy_icon
-#define gaim_gtk_set_sensitive_if_input pidgin_set_sensitive_if_input
-#define gaim_gtk_setup_gtkspell pidgin_setup_gtkspell
-#define gaim_gtk_setup_screenname_autocomplete pidgin_setup_screenname_autocomplete
-#define gaim_gtk_set_urgent pidgin_set_urgent
-#define gaim_gtk_sound_get_event_label pidgin_sound_get_event_label
-#define gaim_gtk_sound_get_event_option pidgin_sound_get_event_option
-#define gaim_gtk_sound_get_handle pidgin_sound_get_handle
-#define gaim_gtk_sound_get_ui_ops pidgin_sound_get_ui_ops
-#define gaim_gtk_status_editor_show pidgin_status_editor_show
-#define gaim_gtk_status_get_handle pidgin_status_get_handle
-#define gaim_gtk_status_init pidgin_status_init
-#define gaim_gtk_status_menu pidgin_status_menu
-#define gaim_gtk_status_uninit pidgin_status_uninit
-#define gaim_gtk_status_window_hide pidgin_status_window_hide
-#define gaim_gtk_status_window_show pidgin_status_window_show
-#define gaim_gtk_stock_init pidgin_stock_init
-#define gaim_gtk_syslog_show pidgin_syslog_show
-#define gaim_gtkthemes_get_proto_smileys pidgin_themes_get_proto_smileys
-#define gaim_gtkthemes_init pidgin_themes_init
-#define gaim_gtkthemes_load_smiley_theme pidgin_themes_load_smiley_theme
-#define gaim_gtkthemes_smileys_disabled pidgin_themes_smileys_disabled
-#define gaim_gtkthemes_smiley_themeize pidgin_themes_smiley_themeize
-#define gaim_gtkthemes_smiley_theme_probe pidgin_themes_smiley_theme_probe
-#define gaim_gtk_toggle_sensitive_array pidgin_toggle_sensitive_array
-#define gaim_gtk_toggle_sensitive pidgin_toggle_sensitive
-#define gaim_gtk_toggle_showhide pidgin_toggle_showhide
-#define gaim_gtk_treeview_popup_menu_position_func pidgin_treeview_popup_menu_position_func
-#define gaim_gtk_tree_view_search_equal_func pidgin_tree_view_search_equal_func
-#define GAIM_GTK_TYPE_MENU_TRAY PIDGIN_TYPE_MENU_TRAY
-#define GAIM_GTK_UI PIDGIN_UI
-#define gaim_gtk_whiteboard_get_ui_ops pidgin_whiteboard_get_ui_ops
-#define GaimGtkWhiteboard PidginWhiteboard
-#define GaimGtkWindow PidginWindow
-#define gaim_gtkxfer_dialog_add_xfer pidgin_xfer_dialog_add_xfer
-#define gaim_gtkxfer_dialog_cancel_xfer pidgin_xfer_dialog_cancel_xfer
-#define gaim_gtkxfer_dialog_destroy pidgin_xfer_dialog_destroy
-#define gaim_gtkxfer_dialog_hide pidgin_xfer_dialog_hide
-#define gaim_gtkxfer_dialog_new pidgin_xfer_dialog_new
-#define GaimGtkXferDialog PidginXferDialog
-#define gaim_gtkxfer_dialog_remove_xfer pidgin_xfer_dialog_remove_xfer
-#define gaim_gtkxfer_dialog_show pidgin_xfer_dialog_show
-#define gaim_gtkxfer_dialog_update_xfer pidgin_xfer_dialog_update_xfer
-#define gaim_gtk_xfers_get_ui_ops pidgin_xfers_get_ui_ops
-#define gaim_gtk_xfers_init pidgin_xfers_init
-#define gaim_gtk_xfers_uninit pidgin_xfers_uninit
-#define GAIM_HIG_BORDER PIDGIN_HIG_BORDER
-#define GAIM_HIG_BOX_SPACE PIDGIN_HIG_BOX_SPACE
-#define GAIM_HIG_CAT_SPACE PIDGIN_HIG_CAT_SPACE
-#if !GTK_CHECK_VERSION(2,16,0)
-#define GAIM_INVISIBLE_CHAR PIDGIN_INVISIBLE_CHAR
-#endif /* Less than GTK+ 2.16 */
-#define GAIM_IS_GTK_CONVERSATION PIDGIN_IS_PIDGIN_CONVERSATION
-#define GAIM_IS_GTK_PLUGIN PIDGIN_IS_PIDGIN_PLUGIN
-#define gaim_new_check_item pidgin_new_check_item
-#define gaim_new_item_from_stock pidgin_new_item_from_stock
-#define gaim_new_item pidgin_new_item
-#define gaim_pixbuf_button_from_stock pidgin_pixbuf_button_from_stock
-#define gaim_pixbuf_toolbar_button_from_stock pidgin_pixbuf_toolbar_button_from_stock
-#define GaimScrollBookClass PidginScrollBookClass
-#define gaim_scroll_book_get_type pidgin_scroll_book_get_type
-#define gaim_scroll_book_new pidgin_scroll_book_new
-#define GaimScrollBook PidginScrollBook
-#define gaim_separator pidgin_separator
-#define gaim_set_accessible_label pidgin_set_accessible_label
-#define gaim_set_gtkxfer_dialog pidgin_set_xfer_dialog
-#define gaim_setup_imhtml pidgin_setup_imhtml
-#define gaim_status_box_add pidgin_status_box_add
-#define gaim_status_box_add_separator pidgin_status_box_add_separator
-#define GaimStatusBoxClass PidginStatusBoxClass
-#define gaim_status_box_get_buddy_icon pidgin_status_box_get_buddy_icon
-#define gaim_status_box_get_message pidgin_status_box_get_message
-#define gaim_status_box_get_type pidgin_status_box_get_type
-#define GaimStatusBoxItemType PidginStatusBoxItemType
-#define gaim_status_box_new pidgin_status_box_new
-#define gaim_status_box_new_with_account pidgin_status_box_new_with_account
-#define GaimStatusBox PidginStatusBox
-#define gaim_status_box_pulse_connecting pidgin_status_box_pulse_connecting
-#define gaim_status_box_set_buddy_icon pidgin_status_box_set_buddy_icon
-#define gaim_status_box_set_connecting pidgin_status_box_set_connecting
-#define gaim_status_box_set_network_available pidgin_status_box_set_network_available
-#define GAIM_STATUS_ICON_LARGE PIDGIN_STATUS_ICON_LARGE
-#define GaimStatusIconSize PidginStatusIconSize
-#define GAIM_STATUS_ICON_SMALL PIDGIN_STATUS_ICON_SMALL
-#define GAIM_STOCK_ABOUT PIDGIN_STOCK_ABOUT
-#define GAIM_STOCK_ACTION PIDGIN_STOCK_ACTION
-#define GAIM_STOCK_ALIAS PIDGIN_STOCK_ALIAS
-#define GAIM_STOCK_AWAY PIDGIN_STOCK_AWAY
-#define GAIM_STOCK_CHAT PIDGIN_STOCK_CHAT
-#define GAIM_STOCK_CLEAR PIDGIN_STOCK_CLEAR
-#define GAIM_STOCK_CLOSE_TABS PIDGIN_STOCK_CLOSE_TABS
-#define GAIM_STOCK_DEBUG PIDGIN_STOCK_DEBUG
-#define GAIM_STOCK_DIALOG_AUTH PIDGIN_STOCK_DIALOG_AUTH
-#define GAIM_STOCK_DIALOG_COOL PIDGIN_STOCK_DIALOG_COOL
-#define GAIM_STOCK_DIALOG_ERROR PIDGIN_STOCK_DIALOG_ERROR
-#define GAIM_STOCK_DIALOG_INFO PIDGIN_STOCK_DIALOG_INFO
-#define GAIM_STOCK_DIALOG_QUESTION PIDGIN_STOCK_DIALOG_QUESTION
-#define GAIM_STOCK_DIALOG_WARNING PIDGIN_STOCK_DIALOG_WARNING
-#define GAIM_STOCK_DISCONNECT PIDGIN_STOCK_DISCONNECT
-#define GAIM_STOCK_DOWNLOAD PIDGIN_STOCK_DOWNLOAD
-#define GAIM_STOCK_EDIT PIDGIN_STOCK_EDIT
-#define GAIM_STOCK_FGCOLOR PIDGIN_STOCK_FGCOLOR
-#define GAIM_STOCK_FILE_CANCELED PIDGIN_STOCK_FILE_CANCELED
-#define GAIM_STOCK_FILE_DONE PIDGIN_STOCK_FILE_DONE
-#define GAIM_STOCK_FILE_TRANSFER PIDGIN_STOCK_FILE_TRANSFER
-#define GAIM_STOCK_IGNORE PIDGIN_STOCK_IGNORE
-#define GAIM_STOCK_IM "gaim-im" /* foo... */
-#define GAIM_STOCK_INVITE PIDGIN_STOCK_INVITE
-#define GAIM_STOCK_MODIFY PIDGIN_STOCK_MODIFY
-#define GAIM_STOCK_OPEN_MAIL PIDGIN_STOCK_OPEN_MAIL
-#define GAIM_STOCK_PAUSE PIDGIN_STOCK_PAUSE
-#define GAIM_STOCK_POUNCE PIDGIN_STOCK_POUNCE
-#define GAIM_STOCK_SIGN_OFF PIDGIN_STOCK_SIGN_OFF
-#define GAIM_STOCK_SIGN_ON PIDGIN_STOCK_SIGN_ON
-#define GAIM_STOCK_STATUS_OFFLINE PIDGIN_STOCK_STATUS_OFFLINE
-#define GAIM_STOCK_TEXT_NORMAL PIDGIN_STOCK_TEXT_NORMAL
-#define GAIM_STOCK_TYPED PIDGIN_STOCK_TYPED
-#define GAIM_STOCK_UPLOAD PIDGIN_STOCK_UPLOAD
-#define GAIM_TYPE_GTK_CELL_RENDERER_EXPANDER PIDGIN_TYPE_GTK_CELL_RENDERER_EXPANDER
-#define GAIM_TYPE_GTK_CELL_RENDERER_PROGRESS PIDGIN_TYPE_GTK_CELL_RENDERER_PROGRESS
-#define GAIM_UNSEEN_EVENT PIDGIN_UNSEEN_EVENT
-#define GAIM_UNSEEN_NICK PIDGIN_UNSEEN_NICK
-#define GAIM_UNSEEN_NO_LOG PIDGIN_UNSEEN_NO_LOG
-#define GAIM_UNSEEN_NONE PIDGIN_UNSEEN_NONE
-#define GaimUnseenState PidginUnseenState
-#define GAIM_UNSEEN_TEXT PIDGIN_UNSEEN_TEXT
-#define GAIM_WINDOW_ICONIFIED PIDGIN_WINDOW_ICONIFIED
-#define GTK_GAIM_IS_SCROLL_BOOK_CLASS PIDGIN_IS_SCROLL_BOOK_CLASS
-#define GTK_GAIM_IS_SCROLL_BOOK PIDGIN_IS_SCROLL_BOOK
-#define GTK_GAIM_IS_STATUS_BOX_CLASS PIDGIN_IS_STATUS_BOX_CLASS
-#define GTK_GAIM_IS_STATUS_BOX PIDGIN_IS_STATUS_BOX
-#define GTK_GAIM_SCROLL_BOOK_CLASS PIDGIN_SCROLL_BOOK_CLASS
-#define GTK_GAIM_SCROLL_BOOK_GET_CLASS PIDGIN_SCROLL_BOOK_GET_CLASS
-#define gtk_gaim_scroll_book_get_type pidgin_scroll_book_get_type
-#define gtk_gaim_scroll_book_new pidgin_scroll_book_new
-#define GTK_GAIM_SCROLL_BOOK PIDGIN_SCROLL_BOOK
-#define gtk_gaim_status_box_add pidgin_status_box_add
-#define gtk_gaim_status_box_add_separator pidgin_status_box_add_separator
-#define GTK_GAIM_STATUS_BOX_CLASS PIDGIN_STATUS_BOX_CLASS
-#define gtk_gaim_status_box_get_buddy_icon pidgin_status_box_get_buddy_icon
-#define GTK_GAIM_STATUS_BOX_GET_CLASS PIDGIN_STATUS_BOX_GET_CLASS
-#define gtk_gaim_status_box_get_message pidgin_status_box_get_message
-#define gtk_gaim_status_box_get_type pidgin_status_box_get_type
-#define GtkGaimStatusBoxItemType PidginStatusBoxItemType
-#define gtk_gaim_status_box_new pidgin_status_box_new
-#define gtk_gaim_status_box_new_with_account pidgin_status_box_new_with_account
-#define GTK_GAIM_STATUS_BOX_NUM_TYPES PIDGIN_STATUS_BOX_NUM_TYPES
-#define GtkGaimStatusBox PidginStatusBox
-#define GTK_GAIM_STATUS_BOX PIDGIN_STATUS_BOX
-#define gtk_gaim_status_box_pulse_connecting pidgin_status_box_pulse_connecting
-#define gtk_gaim_status_box_set_buddy_icon pidgin_status_box_set_buddy_icon
-#define gtk_gaim_status_box_set_connecting pidgin_status_box_set_connecting
-#define gtk_gaim_status_box_set_network_available pidgin_status_box_set_network_available
-#define GTK_GAIM_STATUS_BOX_TYPE_CUSTOM PIDGIN_STATUS_BOX_TYPE_CUSTOM
-#define GTK_GAIM_STATUS_BOX_TYPE_POPULAR PIDGIN_STATUS_BOX_TYPE_POPULAR
-#define GTK_GAIM_STATUS_BOX_TYPE_PRIMITIVE PIDGIN_STATUS_BOX_TYPE_PRIMITIVE
-#define GTK_GAIM_STATUS_BOX_TYPE_SAVED PIDGIN_STATUS_BOX_TYPE_SAVED
-#define GTK_GAIM_STATUS_BOX_TYPE_SEPARATOR PIDGIN_STATUS_BOX_TYPE_SEPARATOR
-#define GTK_GAIM_TYPE_SCROLL_BOOK PIDGIN_TYPE_SCROLL_BOOK
-#define GTK_GAIM_TYPE_STATUS_BOX PIDGIN_TYPE_STATUS_BOX
-
-#endif /* _GTKGAIM_COMPAT_H */
diff --git a/pidgin/gtkicon-theme-loader.c b/pidgin/gtkicon-theme-loader.c
index e5818da667..a7c64104be 100644
--- a/pidgin/gtkicon-theme-loader.c
+++ b/pidgin/gtkicon-theme-loader.c
@@ -31,52 +31,55 @@
*****************************************************************************/
static PurpleTheme *
-pidgin_icon_loader_build(const gchar *dir)
+pidgin_icon_loader_build(const gchar *theme_dir)
{
- xmlnode *root_node = NULL, *sub_node;
- gchar *filename_full, *data = NULL;
+ PurpleXmlNode *root_node = NULL, *sub_node;
+ gchar *dir, *filename_full, *data = NULL;
PidginIconTheme *theme = NULL;
const gchar *name;
/* Find the theme file */
- g_return_val_if_fail(dir != NULL, NULL);
+ g_return_val_if_fail(theme_dir != NULL, NULL);
+ dir = g_build_filename(theme_dir, "purple", "status-icon", NULL);
filename_full = g_build_filename(dir, "theme.xml", NULL);
if (g_file_test(filename_full, G_FILE_TEST_IS_REGULAR))
- root_node = xmlnode_from_file(dir, "theme.xml", "icon themes", "icon-theme-loader");
+ root_node = purple_xmlnode_from_file(dir, "theme.xml", "icon themes", "icon-theme-loader");
g_free(filename_full);
- if (root_node == NULL)
+ if (root_node == NULL) {
+ g_free(dir);
return NULL;
+ }
- name = xmlnode_get_attrib(root_node, "name");
+ name = purple_xmlnode_get_attrib(root_node, "name");
if (name) {
/* Parse the tree */
- sub_node = xmlnode_get_child(root_node, "description");
- data = xmlnode_get_data(sub_node);
+ sub_node = purple_xmlnode_get_child(root_node, "description");
+ data = purple_xmlnode_get_data(sub_node);
- if (xmlnode_get_attrib(root_node, "name") != NULL) {
+ if (purple_xmlnode_get_attrib(root_node, "name") != NULL) {
theme = g_object_new(PIDGIN_TYPE_STATUS_ICON_THEME,
"type", "status-icon",
"name", name,
- "author", xmlnode_get_attrib(root_node, "author"),
- "image", xmlnode_get_attrib(root_node, "image"),
+ "author", purple_xmlnode_get_attrib(root_node, "author"),
+ "image", purple_xmlnode_get_attrib(root_node, "image"),
"directory", dir,
"description", data, NULL);
- sub_node = xmlnode_get_child(root_node, "icon");
+ sub_node = purple_xmlnode_get_child(root_node, "icon");
while (sub_node) {
pidgin_icon_theme_set_icon(theme,
- xmlnode_get_attrib(sub_node, "id"),
- xmlnode_get_attrib(sub_node, "file"));
- sub_node = xmlnode_get_next_twin(sub_node);
+ purple_xmlnode_get_attrib(sub_node, "id"),
+ purple_xmlnode_get_attrib(sub_node, "file"));
+ sub_node = purple_xmlnode_get_next_twin(sub_node);
}
}
}
- xmlnode_free(root_node);
+ purple_xmlnode_free(root_node);
g_free(data);
return PURPLE_THEME(theme);
}
diff --git a/pidgin/gtkicon-theme-loader.h b/pidgin/gtkicon-theme-loader.h
index eccb85dc32..37c0be464a 100644
--- a/pidgin/gtkicon-theme-loader.h
+++ b/pidgin/gtkicon-theme-loader.h
@@ -1,7 +1,3 @@
-/**
- * @file gtkicon-theme-loader.h Pidgin Icon Theme Loader Class API
- */
-
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -25,17 +21,17 @@
#ifndef PIDGIN_ICON_THEME_LOADER_H
#define PIDGIN_ICON_THEME_LOADER_H
+/**
+ * SECTION:gtkicon-theme-loader
+ * @section_id: pidgin-gtkicon-theme-loader
+ * @short_description: <filename>gtkicon-theme-loader.h</filename>
+ * @title: Pidgin Icon Theme Loader Class
+ */
#include <glib.h>
#include <glib-object.h>
#include "theme-loader.h"
-/**
- * A pidgin icon theme loader. Extends PurpleThemeLoader (theme-loader.h)
- * This is a class designed to build icon themes
- *
- * PidginIconThemeLoader is a GObject.
- */
typedef struct _PidginIconThemeLoader PidginIconThemeLoader;
typedef struct _PidginIconThemeLoaderClass PidginIconThemeLoaderClass;
@@ -46,6 +42,12 @@ typedef struct _PidginIconThemeLoaderClass PidginIconThemeLoaderClass;
#define PIDGIN_IS_ICON_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_ICON_THEME_LOADER))
#define PIDGIN_ICON_THEME_LOADER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_ICON_THEME_LOADER, PidginIconThemeLoaderClass))
+/**
+ * PidginIconThemeLoader:
+ *
+ * A pidgin icon theme loader. Extends PurpleThemeLoader (theme-loader.h)
+ * This is a class designed to build icon themes
+ */
struct _PidginIconThemeLoader
{
PurpleThemeLoader parent;
@@ -57,15 +59,17 @@ struct _PidginIconThemeLoaderClass
};
/**************************************************************************/
-/** @name Pidgin Icon Theme-Loader API */
+/* Pidgin Icon Theme-Loader API */
/**************************************************************************/
G_BEGIN_DECLS
/**
- * GObject foo.
- * @internal.
+ * pidgin_icon_theme_loader_get_type:
+ *
+ * Returns: The #GType for an icon theme loader.
*/
GType pidgin_icon_theme_loader_get_type(void);
G_END_DECLS
+
#endif /* PIDGIN_ICON_THEME_LOADER_H */
diff --git a/pidgin/gtkicon-theme.c b/pidgin/gtkicon-theme.c
index feca1dd7ef..b669d93522 100644
--- a/pidgin/gtkicon-theme.c
+++ b/pidgin/gtkicon-theme.c
@@ -26,7 +26,7 @@
#include <gtk/gtk.h>
#define PIDGIN_ICON_THEME_GET_PRIVATE(Gobject) \
- ((PidginIconThemePrivate *) ((PIDGIN_ICON_THEME(Gobject))->priv))
+ (G_TYPE_INSTANCE_GET_PRIVATE((Gobject), PIDGIN_TYPE_ICON_THEME, PidginIconThemePrivate))
/******************************************************************************
* Structs
@@ -53,8 +53,6 @@ pidgin_icon_theme_init(GTypeInstance *instance,
{
PidginIconThemePrivate *priv;
- (PIDGIN_ICON_THEME(instance))->priv = g_new0(PidginIconThemePrivate, 1);
-
priv = PIDGIN_ICON_THEME_GET_PRIVATE(instance);
priv->icon_files = g_hash_table_new_full(g_str_hash,
@@ -69,7 +67,6 @@ pidgin_icon_theme_finalize(GObject *obj)
priv = PIDGIN_ICON_THEME_GET_PRIVATE(obj);
g_hash_table_destroy(priv->icon_files);
- g_free(priv);
parent_class->finalize(obj);
}
@@ -82,6 +79,8 @@ pidgin_icon_theme_class_init(PidginIconThemeClass *klass)
parent_class = g_type_class_peek_parent(klass);
obj_class->finalize = pidgin_icon_theme_finalize;
+
+ g_type_class_add_private(klass, sizeof(PidginIconThemePrivate));
}
GType
diff --git a/pidgin/gtkicon-theme.h b/pidgin/gtkicon-theme.h
index ce67048ba8..fa8614a838 100644
--- a/pidgin/gtkicon-theme.h
+++ b/pidgin/gtkicon-theme.h
@@ -1,7 +1,3 @@
-/**
- * @file gtkicon-theme.h Pidgin Icon Theme Class API
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -25,18 +21,17 @@
#ifndef PIDGIN_ICON_THEME_H
#define PIDGIN_ICON_THEME_H
+/**
+ * SECTION:gtkicon-theme
+ * @section_id: pidgin-gtkicon-theme
+ * @short_description: <filename>gtkicon-theme.h</filename>
+ * @title: Icon Theme Class
+ */
#include <glib.h>
#include <glib-object.h>
#include "theme.h"
-/**
- * extends PurpleTheme (theme.h)
- * A pidgin icon theme.
- * This object represents a Pidgin icon theme.
- *
- * PidginIconTheme is a PurpleTheme Object.
- */
typedef struct _PidginIconTheme PidginIconTheme;
typedef struct _PidginIconThemeClass PidginIconThemeClass;
@@ -47,10 +42,18 @@ typedef struct _PidginIconThemeClass PidginIconThemeClass;
#define PIDGIN_IS_ICON_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_ICON_THEME))
#define PIDGIN_ICON_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_ICON_THEME, PidginIconThemeClass))
+/**
+ * PidginIconTheme:
+ *
+ * extends PurpleTheme (theme.h)
+ * A pidgin icon theme.
+ * This object represents a Pidgin icon theme.
+ *
+ * PidginIconTheme is a PurpleTheme Object.
+ */
struct _PidginIconTheme
{
PurpleTheme parent;
- gpointer priv;
};
struct _PidginIconThemeClass
@@ -59,37 +62,41 @@ struct _PidginIconThemeClass
};
/**************************************************************************/
-/** @name Pidgin Icon Theme API */
+/* Pidgin Icon Theme API */
/**************************************************************************/
G_BEGIN_DECLS
/**
- * GObject foo.
- * @internal.
+ * pidgin_icon_theme_get_type:
+ *
+ * Returns: The #GType for an icon theme.
*/
GType pidgin_icon_theme_get_type(void);
/**
- * Returns a copy of the filename for the icon event or NULL if it is not set
+ * pidgin_icon_theme_get_icon:
+ * @theme: the theme
+ * @event: the pidgin icon event to look up
*
- * @param theme the theme
- * @param event the pidgin icon event to look up
+ * Returns a copy of the filename for the icon event or NULL if it is not set
*
- * @returns the filename of the icon event
+ * Returns: the filename of the icon event
*/
const gchar *pidgin_icon_theme_get_icon(PidginIconTheme *theme,
const gchar *event);
/**
- * Sets the filename for a given icon id, setting the icon to NULL will remove the icon from the theme
+ * pidgin_icon_theme_set_icon:
+ * @theme: the theme
+ * @icon_id: a string representing what the icon is to be used for
+ * @filename: the name of the file to be used for the given id
*
- * @param theme the theme
- * @param icon_id a string representing what the icon is to be used for
- * @param filename the name of the file to be used for the given id
+ * Sets the filename for a given icon id, setting the icon to NULL will remove the icon from the theme
*/
void pidgin_icon_theme_set_icon(PidginIconTheme *theme,
const gchar *icon_id,
const gchar *filename);
G_END_DECLS
+
#endif /* PIDGIN_ICON_THEME_H */
diff --git a/pidgin/gtkidle.c b/pidgin/gtkidle.c
index f5cf624b97..3fd0054833 100644
--- a/pidgin/gtkidle.c
+++ b/pidgin/gtkidle.c
@@ -42,7 +42,9 @@
#include "idle.h"
-/**
+/*
+ * pidgin_get_time_idle:
+ *
* Get the number of seconds the user has been idle. In Unix-world
* this is based on the X Windows usage. In MS Windows this is
* based on keyboard/mouse usage information obtained from the OS.
@@ -65,7 +67,7 @@
*
* See watch() in xscreensaver/driver/xscreensaver-command.c.
*
- * @return The number of seconds the user has been idle.
+ * Returns: The number of seconds the user has been idle.
*/
#if defined(USE_SCREENSAVER) || defined(HAVE_IOKIT)
static time_t
@@ -107,14 +109,16 @@ pidgin_get_time_idle(void)
int event_base, error_base;
if (has_extension == -1)
- has_extension = XScreenSaverQueryExtension(GDK_DISPLAY(), &event_base, &error_base);
+ has_extension = XScreenSaverQueryExtension(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
+ &event_base, &error_base);
if (has_extension)
{
if (mit_info == NULL)
mit_info = XScreenSaverAllocInfo();
- XScreenSaverQueryInfo(GDK_DISPLAY(), GDK_ROOT_WINDOW(), mit_info);
+ XScreenSaverQueryInfo(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
+ GDK_ROOT_WINDOW(), mit_info);
return (mit_info->idle) / 1000;
}
else
diff --git a/pidgin/gtkidle.h b/pidgin/gtkidle.h
index d869584d28..c0cca10570 100644
--- a/pidgin/gtkidle.h
+++ b/pidgin/gtkidle.h
@@ -1,8 +1,3 @@
-/**
- * @file gtkidle.h GTK+ Idle API
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -23,23 +18,33 @@
* 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_IDLE_H_
#define _PIDGIN_IDLE_H_
+/**
+ * SECTION:gtkidle
+ * @section_id: pidgin-gtkidle
+ * @short_description: <filename>gtkidle.h</filename>
+ * @title: Idle API
+ */
#include "idle.h"
+G_BEGIN_DECLS
+
/**************************************************************************/
-/** @name GTK+ Idle API */
+/* GTK+ Idle API */
/**************************************************************************/
-/*@{*/
/**
+ * pidgin_idle_get_ui_ops:
+ *
* Returns the GTK+ idle UI ops.
*
- * @return The UI operations structure.
+ * Returns: The UI operations structure.
*/
PurpleIdleUiOps *pidgin_idle_get_ui_ops(void);
-/*@}*/
+G_END_DECLS
#endif /* _PIDGIN_IDLE_H_ */
diff --git a/pidgin/gtkimhtml.c b/pidgin/gtkimhtml.c
deleted file mode 100644
index 8b8cd320e6..0000000000
--- a/pidgin/gtkimhtml.c
+++ /dev/null
@@ -1,5910 +0,0 @@
-/*
- * @file gtkimhtml.c GTK+ IMHtml
- * @ingroup pidgin
- */
-
-/* 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
- *
- */
-#define _PIDGIN_GTKIMHTML_C_
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "internal.h"
-#include "pidgin.h"
-#include "pidginstock.h"
-#include "gtkutils.h"
-#include "smiley.h"
-#include "imgstore.h"
-
-#include "debug.h"
-#include "util.h"
-#include "gtkimhtml.h"
-#include "gtksmiley.h"
-#include "gtksourceiter.h"
-#include "gtksourceundomanager.h"
-#include "gtksourceview-marshal.h"
-#include <gtk/gtk.h>
-#include <glib.h>
-#include <gdk/gdkkeysyms.h>
-#include <string.h>
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-#ifdef HAVE_LANGINFO_CODESET
-#include <langinfo.h>
-#include <locale.h>
-#endif
-#ifdef _WIN32
-#include <gdk/gdkwin32.h>
-#include <windows.h>
-#endif
-
-#include <pango/pango-font.h>
-
-#define TOOLTIP_TIMEOUT 500
-
-static GtkTextViewClass *parent_class = NULL;
-
-struct scalable_data {
- GtkIMHtmlScalable *scalable;
- GtkTextMark *mark;
-};
-
-typedef struct {
- GtkIMHtmlScalable *image;
- gpointer data;
- gsize datasize;
-} GtkIMHtmlImageSave;
-
-struct im_image_data {
- int id;
- GtkTextMark *mark;
-};
-
-struct _GtkIMHtmlLink
-{
- GtkIMHtml *imhtml;
- gchar *url;
- GtkTextTag *tag;
-};
-
-typedef struct _GtkIMHtmlProtocol
-{
- char *name;
- int length;
-
- gboolean (*activate)(GtkIMHtml *imhtml, GtkIMHtmlLink *link);
- gboolean (*context_menu)(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu);
-} GtkIMHtmlProtocol;
-
-typedef struct _GtkIMHtmlFontDetail {
- gushort size;
- gchar *face;
- gchar *fore;
- gchar *back;
- gchar *bg;
- gchar *sml;
- gboolean underline;
- gboolean strike;
- gshort bold;
-} GtkIMHtmlFontDetail;
-
-static gboolean
-gtk_text_view_drag_motion (GtkWidget *widget,
- GdkDragContext *context,
- gint x,
- gint y,
- guint time);
-
-static void preinsert_cb(GtkTextBuffer *buffer, GtkTextIter *iter, gchar *text, gint len, GtkIMHtml *imhtml);
-static void insert_cb(GtkTextBuffer *buffer, GtkTextIter *iter, gchar *text, gint len, GtkIMHtml *imhtml);
-static void delete_cb(GtkTextBuffer *buffer, GtkTextIter *iter, GtkTextIter *end, GtkIMHtml *imhtml);
-static void insert_ca_cb(GtkTextBuffer *buffer, GtkTextIter *arg1, GtkTextChildAnchor *arg2, gpointer user_data);
-static void gtk_imhtml_apply_tags_on_insert(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *end);
-void gtk_imhtml_close_tags(GtkIMHtml *imhtml, GtkTextIter *iter);
-static void gtk_imhtml_link_drop_cb(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, gpointer user_data);
-static void gtk_imhtml_link_drag_rcv_cb(GtkWidget *widget, GdkDragContext *dc, guint x, guint y, GtkSelectionData *sd, guint info, guint t, GtkIMHtml *imhtml);
-static void mark_set_cb(GtkTextBuffer *buffer, GtkTextIter *arg1, GtkTextMark *mark, GtkIMHtml *imhtml);
-static void hijack_menu_cb(GtkIMHtml *imhtml, GtkMenu *menu, gpointer data);
-static void paste_received_cb (GtkClipboard *clipboard, GtkSelectionData *selection_data, gpointer data);
-static void paste_plaintext_received_cb (GtkClipboard *clipboard, const gchar *text, gpointer data);
-static void imhtml_paste_insert(GtkIMHtml *imhtml, const char *text, gboolean plaintext);
-static void imhtml_toggle_bold(GtkIMHtml *imhtml);
-static void imhtml_toggle_italic(GtkIMHtml *imhtml);
-static void imhtml_toggle_strike(GtkIMHtml *imhtml);
-static void imhtml_toggle_underline(GtkIMHtml *imhtml);
-static void imhtml_font_grow(GtkIMHtml *imhtml);
-static void imhtml_font_shrink(GtkIMHtml *imhtml);
-static void imhtml_clear_formatting(GtkIMHtml *imhtml);
-static int gtk_imhtml_is_protocol(const char *text);
-static void gtk_imhtml_activate_tag(GtkIMHtml *imhtml, GtkTextTag *tag);
-static void gtk_imhtml_link_destroy(GtkIMHtmlLink *link);
-
-/* POINT_SIZE converts from AIM font sizes to a point size scale factor. */
-#define MAX_FONT_SIZE 7
-#define POINT_SIZE(x) (_point_sizes [MIN ((x > 0 ? x : 1), MAX_FONT_SIZE) - 1])
-static const gdouble _point_sizes [] = { .85, .95, 1, 1.2, 1.44, 1.728, 2.0736};
-
-enum {
- TARGET_HTML,
- TARGET_UTF8_STRING,
- TARGET_COMPOUND_TEXT,
- TARGET_STRING,
- TARGET_TEXT
-};
-
-enum {
- URL_CLICKED,
- BUTTONS_UPDATE,
- TOGGLE_FORMAT,
- CLEAR_FORMAT,
- UPDATE_FORMAT,
- MESSAGE_SEND,
- UNDO,
- REDO,
- PASTE,
- LAST_SIGNAL
-};
-static guint signals [LAST_SIGNAL] = { 0 };
-
-static char *html_clipboard = NULL;
-static char *text_clipboard = NULL;
-static GtkClipboard *clipboard_selection = NULL;
-
-static const GtkTargetEntry selection_targets[] = {
-#ifndef _WIN32
- { "text/html", 0, TARGET_HTML },
-#else
- { "HTML Format", 0, TARGET_HTML },
-#endif
- { "UTF8_STRING", 0, TARGET_UTF8_STRING },
- { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
- { "STRING", 0, TARGET_STRING },
- { "TEXT", 0, TARGET_TEXT}};
-
-static const GtkTargetEntry link_drag_drop_targets[] = {
- GTK_IMHTML_DND_TARGETS
-};
-
-#ifdef _WIN32
-static gchar *
-clipboard_win32_to_html(char *clipboard) {
- const char *header;
- const char *begin, *end;
- gint start = 0;
- gint finish = 0;
- gchar *html;
- gchar **split;
- int clipboard_length = 0;
-
-#if 0 /* Debugging for Windows clipboard */
- FILE *fd;
-
- purple_debug_info("imhtml clipboard", "from clipboard: %s\n", clipboard);
-
- fd = g_fopen("c:\\purplecb.txt", "wb");
- fprintf(fd, "%s", clipboard);
- fclose(fd);
-#endif
-
- clipboard_length = strlen(clipboard);
-
- if (!(header = strstr(clipboard, "StartFragment:")) || (header - clipboard) >= clipboard_length)
- return NULL;
- sscanf(header, "StartFragment:%d", &start);
-
- if (!(header = strstr(clipboard, "EndFragment:")) || (header - clipboard) >= clipboard_length)
- return NULL;
- sscanf(header, "EndFragment:%d", &finish);
-
- if (finish > clipboard_length)
- finish = clipboard_length;
-
- if (start > finish)
- start = finish;
-
- begin = clipboard + start;
-
- end = clipboard + finish;
-
- html = g_strndup(begin, end - begin);
-
- /* any newlines in the string will now be \r\n, so we need to strip out the \r */
- split = g_strsplit(html, "\r\n", 0);
- g_free(html);
- html = g_strjoinv("\n", split);
- g_strfreev(split);
-
-#if 0 /* Debugging for Windows clipboard */
- purple_debug_info("imhtml clipboard", "HTML fragment: '%s'\n", html);
-#endif
-
- return html;
-}
-
-static gchar *
-clipboard_html_to_win32(char *html) {
- int length;
- GString *clipboard;
-
- if (html == NULL)
- return NULL;
-
- length = strlen(html);
- clipboard = g_string_new ("Version:1.0\r\n");
- g_string_append(clipboard, "StartHTML:0000000105\r\n");
- g_string_append_printf(clipboard, "EndHTML:%010d\r\n", 147 + length);
- g_string_append(clipboard, "StartFragment:0000000127\r\n");
- g_string_append_printf(clipboard, "EndFragment:%010d\r\n", 127 + length);
- g_string_append(clipboard, "<!--StartFragment-->\r\n");
- g_string_append(clipboard, html);
- g_string_append(clipboard, "\r\n<!--EndFragment-->");
-
- return g_string_free(clipboard, FALSE);
-}
-
-static gboolean clipboard_paste_html_win32(GtkIMHtml *imhtml) {
- gboolean pasted = FALSE;
-
- /* Win32 clipboard format value, and functions to convert back and
- * forth between HTML and the clipboard format.
- */
- static UINT win_html_fmt = 0;
-
- /* Register HTML Format as desired clipboard format */
- if (!win_html_fmt)
- win_html_fmt = RegisterClipboardFormat("HTML Format");
-
- if (gtk_text_view_get_editable(GTK_TEXT_VIEW(imhtml))
- && IsClipboardFormatAvailable(win_html_fmt)) {
- gboolean error_reading_clipboard = FALSE;
- HWND hwnd = GDK_WINDOW_HWND(GTK_WIDGET(imhtml)->window);
-
- if (OpenClipboard(hwnd)) {
- HGLOBAL hdata = GetClipboardData(win_html_fmt);
- if (hdata == NULL) {
- if (GetLastError() != ERROR_SUCCESS)
- error_reading_clipboard = TRUE;
- } else {
- char *buffer = GlobalLock(hdata);
- if (buffer == NULL) {
- error_reading_clipboard = TRUE;
- } else {
- char *text = clipboard_win32_to_html(
- buffer);
- imhtml_paste_insert(imhtml, text,
- FALSE);
- g_free(text);
- pasted = TRUE;
- }
- GlobalUnlock(hdata);
- }
-
- CloseClipboard();
- } else {
- error_reading_clipboard = TRUE;
- }
-
- if (error_reading_clipboard) {
- gchar *err_msg = g_win32_error_message(GetLastError());
- purple_debug_info("html clipboard",
- "Unable to read clipboard data: %s\n",
- err_msg ? err_msg : "Unknown Error");
- g_free(err_msg);
- }
- }
-
- return pasted;
-}
-#endif
-
-static GtkSmileyTree*
-gtk_smiley_tree_new (void)
-{
- return g_new0 (GtkSmileyTree, 1);
-}
-
-static void
-gtk_smiley_tree_insert (GtkSmileyTree *tree,
- GtkIMHtmlSmiley *smiley)
-{
- GtkSmileyTree *t = tree;
- const gchar *x = smiley->smile;
-
- if (!(*x))
- return;
-
- do {
- gchar *pos;
- gint index;
-
- if (!t->values)
- t->values = g_string_new ("");
-
- pos = strchr (t->values->str, *x);
- if (!pos) {
- t->values = g_string_append_c (t->values, *x);
- index = t->values->len - 1;
- t->children = g_realloc (t->children, t->values->len * sizeof (GtkSmileyTree *));
- t->children [index] = g_new0 (GtkSmileyTree, 1);
- } else
- index = GPOINTER_TO_INT(pos) - GPOINTER_TO_INT(t->values->str);
-
- t = t->children [index];
-
- x++;
- } while (*x);
-
- t->image = smiley;
-}
-
-
-static void
-gtk_smiley_tree_destroy (GtkSmileyTree *tree)
-{
- GSList *list = g_slist_prepend (NULL, tree);
-
- while (list) {
- GtkSmileyTree *t = list->data;
- gsize i;
- list = g_slist_remove(list, t);
- if (t && t->values) {
- for (i = 0; i < t->values->len; i++)
- list = g_slist_prepend (list, t->children [i]);
- g_string_free (t->values, TRUE);
- g_free (t->children);
- }
-
- g_free (t);
- }
-}
-
-static void (*parent_size_allocate)(GtkWidget *widget, GtkAllocation *alloc);
-
-static void gtk_imhtml_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
-{
- GtkIMHtml *imhtml = GTK_IMHTML(widget);
- GdkRectangle rect;
- int xminus;
- int height = 0, y = 0;
- GtkTextIter iter;
- gboolean scroll = TRUE;
-
- gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter);
-
- gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(widget), &rect);
- gtk_text_view_get_line_yrange(GTK_TEXT_VIEW(imhtml), &iter, &y, &height);
-
- if (((y + height) - (rect.y + rect.height)) > height &&
- gtk_text_buffer_get_char_count(imhtml->text_buffer)) {
- scroll = FALSE;
- }
-
- if(imhtml->old_rect.width != rect.width || imhtml->old_rect.height != rect.height) {
- GList *iter = GTK_IMHTML(widget)->scalables;
-
- xminus = gtk_text_view_get_left_margin(GTK_TEXT_VIEW(widget)) +
- gtk_text_view_get_right_margin(GTK_TEXT_VIEW(widget));
-
- while(iter){
- struct scalable_data *sd = iter->data;
- GtkIMHtmlScalable *scale = GTK_IMHTML_SCALABLE(sd->scalable);
- scale->scale(scale, rect.width - xminus, rect.height);
-
- iter = iter->next;
- }
- }
-
- imhtml->old_rect = rect;
- parent_size_allocate(widget, alloc);
-
- /* Don't scroll here if we're in the middle of a smooth scroll */
- if (scroll && imhtml->scroll_time == NULL &&
- GTK_WIDGET_REALIZED(imhtml))
- gtk_imhtml_scroll_to_end(imhtml, FALSE);
-}
-
-#define DEFAULT_SEND_COLOR "#204a87"
-#define DEFAULT_RECV_COLOR "#cc0000"
-#define DEFAULT_HIGHLIGHT_COLOR "#AF7F00"
-#define DEFAULT_ACTION_COLOR "#062585"
-#define DEFAULT_WHISPER_ACTION_COLOR "#6C2585"
-#define DEFAULT_WHISPER_COLOR "#00FF00"
-
-static void (*parent_style_set)(GtkWidget *widget, GtkStyle *prev_style);
-
-static void
-gtk_imhtml_style_set(GtkWidget *widget, GtkStyle *prev_style)
-{
- int i;
- struct {
- const char *tag;
- const char *color;
- const char *def;
- } styles[] = {
- {"send-name", "send-name-color", DEFAULT_SEND_COLOR},
- {"receive-name", "receive-name-color", DEFAULT_RECV_COLOR},
- {"highlight-name", "highlight-name-color", DEFAULT_HIGHLIGHT_COLOR},
- {"action-name", "action-name-color", DEFAULT_ACTION_COLOR},
- {"whisper-action-name", "whisper-action-name-color", DEFAULT_WHISPER_ACTION_COLOR},
- {"whisper-name", "whisper-name-color", DEFAULT_WHISPER_COLOR},
- {NULL, NULL, NULL}
- };
- GtkIMHtml *imhtml = GTK_IMHTML(widget);
- GtkTextTagTable *table = gtk_text_buffer_get_tag_table(imhtml->text_buffer);
-
- for (i = 0; styles[i].tag; i++) {
- GdkColor *color = NULL;
- GtkTextTag *tag = gtk_text_tag_table_lookup(table, styles[i].tag);
- if (!tag) {
- purple_debug_warning("gtkimhtml", "Cannot find tag '%s'. This should never happen. Please file a bug.\n", styles[i].tag);
- continue;
- }
- gtk_widget_style_get(widget, styles[i].color, &color, NULL);
- if (color) {
- g_object_set(tag, "foreground-gdk", color, NULL);
- gdk_color_free(color);
- } else {
- GdkColor defcolor;
- gdk_color_parse(styles[i].def, &defcolor);
- g_object_set(tag, "foreground-gdk", &defcolor, NULL);
- }
- }
- parent_style_set(widget, prev_style);
-}
-
-static gboolean
-imhtml_get_iter_bounds(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *end)
-{
- if (imhtml->wbfo) {
- gtk_text_buffer_get_bounds(imhtml->text_buffer, start, end);
- return TRUE;
- } else if (imhtml->editable) {
- if (!gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, start, end)) {
- GtkTextMark *mark = gtk_text_buffer_get_insert(imhtml->text_buffer);
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, start, mark);
- *end = *start;
- }
- return TRUE;
- }
-
- return FALSE;
-}
-
-static void
-gtk_imhtml_set_link_color(GtkIMHtml *imhtml, GtkTextTag *tag)
-{
- GdkColor *color = NULL;
- gboolean visited = !!g_object_get_data(G_OBJECT(tag), "visited");
- gtk_widget_style_get(GTK_WIDGET(imhtml), visited ? "hyperlink-visited-color" : "hyperlink-color", &color, NULL);
- if (color) {
- g_object_set(G_OBJECT(tag), "foreground-gdk", color, NULL);
- gdk_color_free(color);
- } else {
- g_object_set(G_OBJECT(tag), "foreground", visited ? "#800000" : "blue", NULL);
- }
-}
-
-static gint
-gtk_imhtml_tip_paint (GtkIMHtml *imhtml)
-{
- PangoLayout *layout;
-
- g_return_val_if_fail(GTK_IS_IMHTML(imhtml), FALSE);
-
- /* We set the text in a separate function call so we can specify a
- max length. This is important so the tooltip isn't too wide for
- the screen, and also because some X library function exits the
- process when it can't allocate enough memory for a super wide
- tooltip. */
- layout = gtk_widget_create_pango_layout(imhtml->tip_window, NULL);
- pango_layout_set_text(layout, imhtml->tip, 200);
-
- gtk_paint_flat_box (imhtml->tip_window->style, imhtml->tip_window->window,
- GTK_STATE_NORMAL, GTK_SHADOW_OUT, NULL, imhtml->tip_window,
- "tooltip", 0, 0, -1, -1);
-
- gtk_paint_layout (imhtml->tip_window->style, imhtml->tip_window->window, GTK_STATE_NORMAL,
- FALSE, NULL, imhtml->tip_window, NULL, 4, 4, layout);
-
- g_object_unref(layout);
- return FALSE;
-}
-
-static gint
-gtk_imhtml_tip (gpointer data)
-{
- GtkIMHtml *imhtml = data;
- PangoFontMetrics *font_metrics;
- PangoLayout *layout;
- PangoFont *font;
-
- gint gap, x, y, h, w, scr_w, baseline_skip;
-
- g_return_val_if_fail(GTK_IS_IMHTML(imhtml), FALSE);
-
- if (!imhtml->tip || !GTK_WIDGET_DRAWABLE (GTK_WIDGET(imhtml))) {
- imhtml->tip_timer = 0;
- return FALSE;
- }
-
- if (imhtml->tip_window){
- gtk_widget_destroy (imhtml->tip_window);
- imhtml->tip_window = NULL;
- }
-
- imhtml->tip_timer = 0;
- imhtml->tip_window = gtk_window_new (GTK_WINDOW_POPUP);
- gtk_widget_set_app_paintable (imhtml->tip_window, TRUE);
- gtk_window_set_title(GTK_WINDOW(imhtml->tip_window), "GtkIMHtml");
- gtk_window_set_resizable (GTK_WINDOW (imhtml->tip_window), FALSE);
- gtk_widget_set_name (imhtml->tip_window, "gtk-tooltips");
- gtk_window_set_type_hint (GTK_WINDOW (imhtml->tip_window),
- GDK_WINDOW_TYPE_HINT_TOOLTIP);
- g_signal_connect_swapped (G_OBJECT (imhtml->tip_window), "expose_event",
- G_CALLBACK (gtk_imhtml_tip_paint), imhtml);
-
- gtk_widget_ensure_style (imhtml->tip_window);
-
- /* We set the text in a separate function call so we can specify a
- max length. This is important so the tooltip isn't too wide for
- the screen, and also because some X library function exits the
- process when it can't allocate enough memory for a super wide
- tooltip. */
- layout = gtk_widget_create_pango_layout(imhtml->tip_window, NULL);
- pango_layout_set_text(layout, imhtml->tip, 200);
-
- font = pango_context_load_font(pango_layout_get_context(layout),
- imhtml->tip_window->style->font_desc);
-
- if (font == NULL) {
- char *tmp = pango_font_description_to_string(
- imhtml->tip_window->style->font_desc);
-
- purple_debug(PURPLE_DEBUG_ERROR, "gtk_imhtml_tip",
- "pango_context_load_font() couldn't load font: '%s'\n",
- tmp);
- g_free(tmp);
-
- g_object_unref(layout);
- return FALSE;
- }
-
- font_metrics = pango_font_get_metrics(font, NULL);
-
- pango_layout_get_pixel_size(layout, &scr_w, NULL);
- gap = PANGO_PIXELS((pango_font_metrics_get_ascent(font_metrics) +
- pango_font_metrics_get_descent(font_metrics))/ 4);
-
- if (gap < 2)
- gap = 2;
- baseline_skip = PANGO_PIXELS(pango_font_metrics_get_ascent(font_metrics) +
- pango_font_metrics_get_descent(font_metrics));
- w = 8 + scr_w;
- h = 8 + baseline_skip;
-
- gdk_window_get_pointer (NULL, &x, &y, NULL);
- if (GTK_WIDGET_NO_WINDOW (GTK_WIDGET(imhtml)))
- y += GTK_WIDGET(imhtml)->allocation.y;
-
- scr_w = gdk_screen_width();
-
- x -= ((w >> 1) + 4);
-
- if ((x + w) > scr_w)
- x -= (x + w) - scr_w;
- else if (x < 0)
- x = 0;
-
- y = y + PANGO_PIXELS(pango_font_metrics_get_ascent(font_metrics) +
- pango_font_metrics_get_descent(font_metrics));
-
- gtk_widget_set_size_request (imhtml->tip_window, w, h);
- gtk_window_move (GTK_WINDOW(imhtml->tip_window), x, y);
- gtk_widget_show (imhtml->tip_window);
-
- pango_font_metrics_unref(font_metrics);
- g_object_unref(font);
- g_object_unref(layout);
-
- return FALSE;
-}
-
-static gboolean
-gtk_motion_event_notify(GtkWidget *imhtml, GdkEventMotion *event, gpointer data)
-{
- GtkTextIter iter;
- GdkWindow *win = event->window;
- int x, y;
- char *tip = NULL;
- GSList *tags = NULL, *templist = NULL;
- GtkTextTag *tag = NULL, *oldprelit_tag;
- GtkTextChildAnchor* anchor;
- gboolean hand = TRUE;
- GdkCursor *cursor = NULL;
-
- oldprelit_tag = GTK_IMHTML(imhtml)->prelit_tag;
-
- gdk_window_get_pointer(GTK_WIDGET(imhtml)->window, NULL, NULL, NULL);
- gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(imhtml), GTK_TEXT_WINDOW_WIDGET,
- event->x, event->y, &x, &y);
- gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(imhtml), &iter, x, y);
- tags = gtk_text_iter_get_tags(&iter);
-
- templist = tags;
- while (templist) {
- tag = templist->data;
- tip = g_object_get_data(G_OBJECT(tag), "link_url");
- if (tip)
- break;
- templist = templist->next;
- }
-
- if (tip && (!tag || !g_object_get_data(G_OBJECT(tag), "visited"))) {
- GTK_IMHTML(imhtml)->prelit_tag = tag;
- if (tag != oldprelit_tag) {
- GdkColor *pre = NULL;
- gtk_widget_style_get(GTK_WIDGET(imhtml), "hyperlink-prelight-color", &pre, NULL);
- if (pre) {
- g_object_set(G_OBJECT(tag), "foreground-gdk", pre, NULL);
- gdk_color_free(pre);
- } else
- g_object_set(G_OBJECT(tag), "foreground", "#70a0ff", NULL);
- }
- } else {
- GTK_IMHTML(imhtml)->prelit_tag = NULL;
- }
-
- if ((oldprelit_tag != NULL) && (GTK_IMHTML(imhtml)->prelit_tag != oldprelit_tag)) {
- gtk_imhtml_set_link_color(GTK_IMHTML(imhtml), oldprelit_tag);
- }
-
- if (GTK_IMHTML(imhtml)->tip) {
- if (tip == GTK_IMHTML(imhtml)->tip) {
- g_slist_free(tags);
- return FALSE;
- }
- /* We've left the cell. Remove the timeout and create a new one below */
- if (GTK_IMHTML(imhtml)->tip_window) {
- gtk_widget_destroy(GTK_IMHTML(imhtml)->tip_window);
- GTK_IMHTML(imhtml)->tip_window = NULL;
- }
- if (GTK_IMHTML(imhtml)->editable)
- cursor = GTK_IMHTML(imhtml)->text_cursor;
- else
- cursor = GTK_IMHTML(imhtml)->arrow_cursor;
- if (GTK_IMHTML(imhtml)->tip_timer)
- g_source_remove(GTK_IMHTML(imhtml)->tip_timer);
- GTK_IMHTML(imhtml)->tip_timer = 0;
- }
-
- /* If we don't have a tip from a URL, let's see if we have a tip from a smiley */
- anchor = gtk_text_iter_get_child_anchor(&iter);
- if (anchor) {
- tip = g_object_get_data(G_OBJECT(anchor), "gtkimhtml_tiptext");
- hand = FALSE;
- }
-
- if (tip && *tip) {
- GTK_IMHTML(imhtml)->tip_timer = g_timeout_add (TOOLTIP_TIMEOUT,
- gtk_imhtml_tip, imhtml);
- } else if (!tip) {
- hand = FALSE;
- for (templist = tags; templist; templist = templist->next) {
- tag = templist->data;
- if ((tip = g_object_get_data(G_OBJECT(tag), "cursor"))) {
- hand = TRUE;
- break;
- }
- }
- }
-
- if (hand && !(GTK_IMHTML(imhtml)->editable))
- cursor = GTK_IMHTML(imhtml)->hand_cursor;
-
- if (cursor)
- gdk_window_set_cursor(win, cursor);
-
- GTK_IMHTML(imhtml)->tip = tip;
- g_slist_free(tags);
- return FALSE;
-}
-
-static gboolean
-gtk_enter_event_notify(GtkWidget *imhtml, GdkEventCrossing *event, gpointer data)
-{
- if (GTK_IMHTML(imhtml)->editable)
- gdk_window_set_cursor(
- gtk_text_view_get_window(GTK_TEXT_VIEW(imhtml),
- GTK_TEXT_WINDOW_TEXT),
- GTK_IMHTML(imhtml)->text_cursor);
- else
- gdk_window_set_cursor(
- gtk_text_view_get_window(GTK_TEXT_VIEW(imhtml),
- GTK_TEXT_WINDOW_TEXT),
- GTK_IMHTML(imhtml)->arrow_cursor);
-
- /* propagate the event normally */
- return FALSE;
-}
-
-static gboolean
-gtk_leave_event_notify(GtkWidget *imhtml, GdkEventCrossing *event, gpointer data)
-{
- /* when leaving the widget, clear any current & pending tooltips and restore the cursor */
- if (GTK_IMHTML(imhtml)->prelit_tag) {
- gtk_imhtml_set_link_color(GTK_IMHTML(imhtml), GTK_IMHTML(imhtml)->prelit_tag);
- GTK_IMHTML(imhtml)->prelit_tag = NULL;
- }
-
- if (GTK_IMHTML(imhtml)->tip_window) {
- gtk_widget_destroy(GTK_IMHTML(imhtml)->tip_window);
- GTK_IMHTML(imhtml)->tip_window = NULL;
- }
- if (GTK_IMHTML(imhtml)->tip_timer) {
- g_source_remove(GTK_IMHTML(imhtml)->tip_timer);
- GTK_IMHTML(imhtml)->tip_timer = 0;
- }
- gdk_window_set_cursor(
- gtk_text_view_get_window(GTK_TEXT_VIEW(imhtml),
- GTK_TEXT_WINDOW_TEXT), NULL);
-
- /* propagate the event normally */
- return FALSE;
-}
-
-static gint
-gtk_imhtml_expose_event (GtkWidget *widget,
- GdkEventExpose *event)
-{
- GtkTextIter start, end, cur;
- int buf_x, buf_y;
- GdkRectangle visible_rect;
- cairo_t *cr = gdk_cairo_create(GDK_DRAWABLE(event->window));
- GdkColor gcolor;
-
- gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(widget), &visible_rect);
- gtk_text_view_buffer_to_window_coords(GTK_TEXT_VIEW(widget),
- GTK_TEXT_WINDOW_TEXT,
- visible_rect.x,
- visible_rect.y,
- &visible_rect.x,
- &visible_rect.y);
-
- gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_TEXT,
- event->area.x, event->area.y, &buf_x, &buf_y);
-
- if (GTK_IMHTML(widget)->editable || GTK_IMHTML(widget)->wbfo) {
-
- if (GTK_IMHTML(widget)->edit.background) {
- gdk_color_parse(GTK_IMHTML(widget)->edit.background, &gcolor);
- gdk_cairo_set_source_color(cr, &gcolor);
- } else {
- gdk_cairo_set_source_color(cr, &(widget->style->base[GTK_WIDGET_STATE(widget)]));
- }
-
- cairo_rectangle(cr,
- visible_rect.x, visible_rect.y,
- visible_rect.width, visible_rect.height);
- cairo_fill(cr);
- cairo_destroy(cr);
-
- if (GTK_WIDGET_CLASS (parent_class)->expose_event)
- return (* GTK_WIDGET_CLASS (parent_class)->expose_event)
- (widget, event);
- return FALSE;
-
- }
-
- gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &start, buf_x, buf_y);
- gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &end,
- buf_x + event->area.width, buf_y + event->area.height);
-
- gtk_text_iter_order(&start, &end);
-
- cur = start;
-
- while (gtk_text_iter_in_range(&cur, &start, &end)) {
- GSList *tags = gtk_text_iter_get_tags(&cur);
- GSList *l;
-
- for (l = tags; l; l = l->next) {
- GtkTextTag *tag = l->data;
- GdkRectangle rect;
- GdkRectangle tag_area;
- const char *color;
-
- if (strncmp(tag->name, "BACKGROUND ", 11))
- continue;
-
- if (gtk_text_iter_ends_tag(&cur, tag))
- continue;
-
- gtk_text_view_get_iter_location(GTK_TEXT_VIEW(widget), &cur, &tag_area);
- gtk_text_view_buffer_to_window_coords(GTK_TEXT_VIEW(widget),
- GTK_TEXT_WINDOW_TEXT,
- tag_area.x,
- tag_area.y,
- &tag_area.x,
- &tag_area.y);
- rect.x = visible_rect.x;
- rect.y = tag_area.y;
- rect.width = visible_rect.width;
-
- do
- gtk_text_iter_forward_to_tag_toggle(&cur, tag);
- while (!gtk_text_iter_is_end(&cur) && gtk_text_iter_begins_tag(&cur, tag));
-
- gtk_text_view_get_iter_location(GTK_TEXT_VIEW(widget), &cur, &tag_area);
- gtk_text_view_buffer_to_window_coords(GTK_TEXT_VIEW(widget),
- GTK_TEXT_WINDOW_TEXT,
- tag_area.x,
- tag_area.y,
- &tag_area.x,
- &tag_area.y);
-
-
- rect.height = tag_area.y + tag_area.height - rect.y
- + gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(widget))
- + gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(widget));
-
- color = tag->name + 11;
-
- if (!gdk_color_parse(color, &gcolor)) {
- gchar tmp[8];
- tmp[0] = '#';
- strncpy(&tmp[1], color, 7);
- tmp[7] = '\0';
- if (!gdk_color_parse(tmp, &gcolor))
- gdk_color_parse("white", &gcolor);
- }
- gdk_cairo_set_source_color(cr, &gcolor);
-
- cairo_rectangle(cr,
- rect.x, rect.y,
- rect.width, rect.height);
- cairo_fill(cr);
- gtk_text_iter_backward_char(&cur); /* go back one, in case the end is the begining is the end
- * note that above, we always moved cur ahead by at least
- * one character */
- break;
- }
-
- g_slist_free(tags);
-
- /* loop until another tag begins, or no tag begins */
- while (gtk_text_iter_forward_to_tag_toggle(&cur, NULL) &&
- !gtk_text_iter_is_end(&cur) &&
- !gtk_text_iter_begins_tag(&cur, NULL));
- }
-
- cairo_destroy(cr);
-
- if (GTK_WIDGET_CLASS (parent_class)->expose_event)
- return (* GTK_WIDGET_CLASS (parent_class)->expose_event)
- (widget, event);
-
- return FALSE;
-}
-
-
-static void paste_unformatted_cb(GtkMenuItem *menu, GtkIMHtml *imhtml)
-{
- GtkClipboard *clipboard = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD);
-
- gtk_clipboard_request_text(clipboard, paste_plaintext_received_cb, imhtml);
-
-}
-
-static void clear_formatting_cb(GtkMenuItem *menu, GtkIMHtml *imhtml)
-{
- gtk_imhtml_clear_formatting(imhtml);
-}
-
-static void disable_smiley_selected(GtkMenuItem *item, GtkIMHtml *imhtml)
-{
- GtkTextIter start, end;
- GtkTextMark *mark;
- char *text;
-
- if (!gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, &start, &end))
- return;
-
- text = gtk_imhtml_get_markup_range(imhtml, &start, &end);
-
- mark = gtk_text_buffer_get_selection_bound(imhtml->text_buffer);
- gtk_text_buffer_delete_selection(imhtml->text_buffer, FALSE, FALSE);
-
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &start, mark);
- gtk_imhtml_insert_html_at_iter(imhtml, text, GTK_IMHTML_NO_NEWLINE | GTK_IMHTML_NO_SMILEY, &start);
-
- g_free(text);
-}
-
-static void hijack_menu_cb(GtkIMHtml *imhtml, GtkMenu *menu, gpointer data)
-{
- GtkWidget *menuitem;
- GtkTextIter start, end;
-
- menuitem = gtk_menu_item_new_with_mnemonic(_("Paste as Plain _Text"));
- gtk_widget_show(menuitem);
- /*
- * TODO: gtk_clipboard_wait_is_text_available() iterates the glib
- * mainloop, which tends to be a source of bugs. It would
- * be good to audit this or change it to not wait.
- */
- gtk_widget_set_sensitive(menuitem,
- (imhtml->editable &&
- gtk_clipboard_wait_is_text_available(
- gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD))));
- /* put it after "Paste" */
- gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, 3);
-
- g_signal_connect(G_OBJECT(menuitem), "activate",
- G_CALLBACK(paste_unformatted_cb), imhtml);
-
- menuitem = gtk_menu_item_new_with_mnemonic(_("_Reset formatting"));
- gtk_widget_show(menuitem);
- gtk_widget_set_sensitive(menuitem, imhtml->editable);
- /* put it after Delete */
- gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, 5);
-
- g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(clear_formatting_cb), imhtml);
-
- menuitem = gtk_menu_item_new_with_mnemonic(_("Disable _smileys in selected text"));
- gtk_widget_show(menuitem);
- if (gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, &start, &end)) {
- g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(disable_smiley_selected), imhtml);
- } else {
- gtk_widget_set_sensitive(menuitem, FALSE);
- }
- gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, 6);
-}
-
-static char *
-ucs2_order(gboolean swap)
-{
- gboolean be;
-
- be = G_BYTE_ORDER == G_BIG_ENDIAN;
- be = swap ? be : !be;
-
- if (be)
- return "UTF-16BE";
- else
- return "UTF-16LE";
-
-}
-
-/* Convert from UTF-16LE to UTF-8, stripping the BOM if one is present.*/
-static gchar *
-utf16_to_utf8_with_bom_check(gchar *data, guint len) {
- char *fromcode = NULL;
- GError *error = NULL;
- guint16 c;
- gchar *utf8_ret;
-
- /*
- * Unicode Techinical Report 20
- * ( http://www.unicode.org/unicode/reports/tr20/ ) says to treat an
- * initial 0xfeff (ZWNBSP) as a byte order indicator so that is
- * what we do. If there is no indicator assume it is in the default
- * order
- */
-
- memcpy(&c, data, 2);
- switch (c) {
- case 0xfeff:
- case 0xfffe:
- fromcode = ucs2_order(c == 0xfeff);
- data += 2;
- len -= 2;
- break;
- default:
- fromcode = "UTF-16";
- break;
- }
-
- utf8_ret = g_convert(data, len, "UTF-8", fromcode, NULL, NULL, &error);
-
- if (error) {
- purple_debug_warning("gtkimhtml", "g_convert error: %s\n", error->message);
- g_error_free(error);
- }
- return utf8_ret;
-}
-
-
-static void gtk_imhtml_clipboard_get(GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info, GtkIMHtml *imhtml) {
- char *text = NULL;
- gboolean primary = (clipboard != clipboard_selection);
- GtkTextIter start, end;
-
- if (primary) {
- GtkTextMark *sel = NULL, *ins = NULL;
-
- g_return_if_fail(imhtml != NULL);
-
- ins = gtk_text_buffer_get_insert(imhtml->text_buffer);
- sel = gtk_text_buffer_get_selection_bound(imhtml->text_buffer);
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &start, sel);
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &end, ins);
- }
-
- if (info == TARGET_HTML) {
- char *selection;
-#ifndef _WIN32
- gsize len;
- if (primary) {
- text = gtk_imhtml_get_markup_range(imhtml, &start, &end);
- } else
- text = html_clipboard;
-
- /* Mozilla asks that we start our text/html with the Unicode byte order mark */
- selection = g_convert(text, -1, "UTF-16", "UTF-8", NULL, &len, NULL);
- gtk_selection_data_set(selection_data, gdk_atom_intern("text/html", FALSE), 16, (const guchar *)selection, len);
-#else
- selection = clipboard_html_to_win32(html_clipboard);
- gtk_selection_data_set(selection_data, gdk_atom_intern("HTML Format", FALSE), 8, (const guchar *)selection, strlen(selection));
-#endif
- g_free(selection);
- } else {
- if (primary) {
- text = gtk_imhtml_get_text(imhtml, &start, &end);
- } else
- text = text_clipboard;
- gtk_selection_data_set_text(selection_data, text, strlen(text));
- }
- if (primary) /* This was allocated here */
- g_free(text);
- }
-
-static void gtk_imhtml_primary_clipboard_clear(GtkClipboard *clipboard, GtkIMHtml *imhtml)
-{
- GtkTextIter insert;
- GtkTextIter selection_bound;
-
- gtk_text_buffer_get_iter_at_mark (imhtml->text_buffer, &insert,
- gtk_text_buffer_get_mark (imhtml->text_buffer, "insert"));
- gtk_text_buffer_get_iter_at_mark (imhtml->text_buffer, &selection_bound,
- gtk_text_buffer_get_mark (imhtml->text_buffer, "selection_bound"));
-
- if (!gtk_text_iter_equal (&insert, &selection_bound))
- gtk_text_buffer_move_mark (imhtml->text_buffer,
- gtk_text_buffer_get_mark (imhtml->text_buffer, "selection_bound"),
- &insert);
-}
-
-static void gtk_imhtml_clipboard_clear (GtkClipboard *clipboard, GtkSelectionData *sel_data,
- guint info, gpointer user_data_or_owner)
-{
-}
-
-static void copy_clipboard_cb(GtkIMHtml *imhtml, gpointer unused)
-{
- GtkTextIter start, end;
- if (gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, &start, &end)) {
- if (!clipboard_selection)
- clipboard_selection = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD);
- gtk_clipboard_set_with_data(clipboard_selection,
- selection_targets, sizeof(selection_targets) / sizeof(GtkTargetEntry),
- (GtkClipboardGetFunc)gtk_imhtml_clipboard_get,
- (GtkClipboardClearFunc)gtk_imhtml_clipboard_clear, NULL);
-
- g_free(html_clipboard);
- g_free(text_clipboard);
-
- html_clipboard = gtk_imhtml_get_markup_range(imhtml, &start, &end);
- text_clipboard = gtk_imhtml_get_text(imhtml, &start, &end);
- }
-
- g_signal_stop_emission_by_name(imhtml, "copy-clipboard");
-}
-
-static void cut_clipboard_cb(GtkIMHtml *imhtml, gpointer unused)
-{
- GtkTextIter start, end;
- if (gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, &start, &end)) {
- if (!clipboard_selection)
- clipboard_selection = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD);
- gtk_clipboard_set_with_data(clipboard_selection,
- selection_targets, sizeof(selection_targets) / sizeof(GtkTargetEntry),
- (GtkClipboardGetFunc)gtk_imhtml_clipboard_get,
- (GtkClipboardClearFunc)gtk_imhtml_clipboard_clear, NULL);
-
- g_free(html_clipboard);
- g_free(text_clipboard);
-
- html_clipboard = gtk_imhtml_get_markup_range(imhtml, &start, &end);
- text_clipboard = gtk_imhtml_get_text(imhtml, &start, &end);
-
- if (imhtml->editable)
- gtk_text_buffer_delete_selection(imhtml->text_buffer, FALSE, FALSE);
- }
-
- g_signal_stop_emission_by_name(imhtml, "cut-clipboard");
-}
-
-static void imhtml_paste_insert(GtkIMHtml *imhtml, const char *text, gboolean plaintext)
-{
- GtkTextIter iter;
- GtkIMHtmlOptions flags = plaintext ? GTK_IMHTML_NO_SMILEY : (GTK_IMHTML_NO_NEWLINE | GTK_IMHTML_NO_COMMENTS);
-
- /* Delete any currently selected text */
- gtk_text_buffer_delete_selection(imhtml->text_buffer, TRUE, TRUE);
-
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, gtk_text_buffer_get_insert(imhtml->text_buffer));
- if (!imhtml->wbfo && !plaintext)
- gtk_imhtml_close_tags(imhtml, &iter);
-
- gtk_imhtml_insert_html_at_iter(imhtml, text, flags, &iter);
- gtk_text_buffer_move_mark_by_name(imhtml->text_buffer, "insert", &iter);
- gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(imhtml), gtk_text_buffer_get_insert(imhtml->text_buffer),
- 0, FALSE, 0.0, 0.0);
- if (!imhtml->wbfo && !plaintext)
- gtk_imhtml_close_tags(imhtml, &iter);
-
-}
-
-static void paste_plaintext_received_cb (GtkClipboard *clipboard, const gchar *text, gpointer data)
-{
- char *tmp;
-
- if (text == NULL || !(*text))
- return;
-
- tmp = g_markup_escape_text(text, -1);
- imhtml_paste_insert(data, tmp, TRUE);
- g_free(tmp);
-}
-
-static void paste_received_cb (GtkClipboard *clipboard, GtkSelectionData *selection_data, gpointer data)
-{
- char *text;
- GtkIMHtml *imhtml = data;
-
- if (!gtk_text_view_get_editable(GTK_TEXT_VIEW(imhtml)))
- return;
-
- if (imhtml->wbfo || selection_data->length <= 0) {
- gtk_clipboard_request_text(clipboard, paste_plaintext_received_cb, imhtml);
- return;
- } else {
-#if 0
- /* Here's some debug code, for figuring out what sent to us over the clipboard. */
- {
- int i;
-
- purple_debug_misc("gtkimhtml", "In paste_received_cb():\n\tformat = %d, length = %d\n\t",
- selection_data->format, selection_data->length);
-
- for (i = 0; i < (/*(selection_data->format / 8) **/ selection_data->length); i++) {
- if ((i % 70) == 0)
- printf("\n\t");
- if (selection_data->data[i] == '\0')
- printf(".");
- else
- printf("%c", selection_data->data[i]);
- }
- printf("\n");
- }
-#endif
-
- text = g_malloc(selection_data->length + 1);
- memcpy(text, selection_data->data, selection_data->length);
- /* Make sure the paste data is null-terminated. Given that
- * we're passed length (but assume later that it is
- * null-terminated), this seems sensible to me.
- */
- text[selection_data->length] = '\0';
- }
-
-#ifdef _WIN32
- if (gtk_selection_data_get_data_type(selection_data) == gdk_atom_intern("HTML Format", FALSE)) {
- char *tmp = clipboard_win32_to_html(text);
- g_free(text);
- text = tmp;
- }
-#endif
-
- if (selection_data->length >= 2 &&
- (*(guint16 *)text == 0xfeff || *(guint16 *)text == 0xfffe)) {
- /* This is UTF-16 */
- char *utf8 = utf16_to_utf8_with_bom_check(text, selection_data->length);
- g_free(text);
- text = utf8;
- if (!text) {
- purple_debug_warning("gtkimhtml", "g_convert from UTF-16 failed in paste_received_cb\n");
- return;
- }
- }
-
- if (!(*text) || !g_utf8_validate(text, -1, NULL)) {
- purple_debug_warning("gtkimhtml", "empty string or invalid UTF-8 in paste_received_cb\n");
- g_free(text);
- return;
- }
-
- imhtml_paste_insert(imhtml, text, FALSE);
- g_free(text);
-}
-
-
-static void smart_backspace_cb(GtkIMHtml *imhtml, gpointer blah)
-{
- GtkTextIter iter;
- GtkTextChildAnchor* anchor;
- char * text;
- gint offset;
-
- if (!imhtml->editable)
- return;
-
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, gtk_text_buffer_get_insert(imhtml->text_buffer));
-
- /* Get the character before the insertion point */
- offset = gtk_text_iter_get_offset(&iter);
- if (offset <= 0)
- return;
-
- gtk_text_iter_backward_char(&iter);
- anchor = gtk_text_iter_get_child_anchor(&iter);
-
- if (!anchor)
- return; /* No smiley here */
-
- text = g_object_get_data(G_OBJECT(anchor), "gtkimhtml_plaintext");
- if (!text)
- return;
-
- /* ok, then we need to insert the image buffer text before the anchor */
- gtk_text_buffer_insert(imhtml->text_buffer, &iter, text, -1);
-}
-
-static void paste_clipboard_cb(GtkIMHtml *imhtml, gpointer blah)
-{
-#ifdef _WIN32
- /* If we're on windows, let's see if we can get data from the HTML Format
- clipboard before we try to paste from the GTK buffer */
- if (!clipboard_paste_html_win32(imhtml) && gtk_text_view_get_editable(GTK_TEXT_VIEW(imhtml))) {
- GtkClipboard *clipboard = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD);
- gtk_clipboard_request_text(clipboard, paste_plaintext_received_cb, imhtml);
-
- }
-#else
- GtkClipboard *clipboard = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD);
- gtk_clipboard_request_contents(clipboard, gdk_atom_intern("text/html", FALSE),
- paste_received_cb, imhtml);
-#endif
- g_signal_stop_emission_by_name(imhtml, "paste-clipboard");
-}
-
-static void imhtml_realized_remove_primary(GtkIMHtml *imhtml, gpointer unused)
-{
- gtk_text_buffer_remove_selection_clipboard(GTK_IMHTML(imhtml)->text_buffer,
- gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_PRIMARY));
-
-}
-
-static void imhtml_destroy_add_primary(GtkIMHtml *imhtml, gpointer unused)
-{
- gtk_text_buffer_add_selection_clipboard(GTK_IMHTML(imhtml)->text_buffer,
- gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_PRIMARY));
-}
-
-static void mark_set_so_update_selection_cb(GtkTextBuffer *buffer, GtkTextIter *arg1, GtkTextMark *mark, GtkIMHtml *imhtml)
-{
- if (gtk_text_buffer_get_selection_bounds(buffer, NULL, NULL)) {
- gtk_clipboard_set_with_owner(gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_PRIMARY),
- selection_targets, sizeof(selection_targets) / sizeof(GtkTargetEntry),
- (GtkClipboardGetFunc)gtk_imhtml_clipboard_get,
- (GtkClipboardClearFunc)gtk_imhtml_primary_clipboard_clear, G_OBJECT(imhtml));
- }
-}
-
-static gboolean gtk_imhtml_button_press_event(GtkIMHtml *imhtml, GdkEventButton *event, gpointer unused)
-{
- if (event->button == 2) {
- int x, y;
- GtkTextIter iter;
- GtkClipboard *clipboard = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_PRIMARY);
-
- if (!imhtml->editable)
- return FALSE;
-
- gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(imhtml),
- GTK_TEXT_WINDOW_TEXT,
- event->x,
- event->y,
- &x,
- &y);
- gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(imhtml), &iter, x, y);
- gtk_text_buffer_place_cursor(imhtml->text_buffer, &iter);
-
- gtk_clipboard_request_contents(clipboard, gdk_atom_intern("text/html", FALSE),
- paste_received_cb, imhtml);
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-static void
-gtk_imhtml_undo(GtkIMHtml *imhtml)
-{
- g_return_if_fail(GTK_IS_IMHTML(imhtml));
- if (imhtml->editable &&
- gtk_source_undo_manager_can_undo(imhtml->undo_manager))
- gtk_source_undo_manager_undo(imhtml->undo_manager);
-}
-
-static void
-gtk_imhtml_redo(GtkIMHtml *imhtml)
-{
- g_return_if_fail(GTK_IS_IMHTML(imhtml));
- if (imhtml->editable &&
- gtk_source_undo_manager_can_redo(imhtml->undo_manager))
- gtk_source_undo_manager_redo(imhtml->undo_manager);
-
-}
-
-static gboolean imhtml_message_send(GtkIMHtml *imhtml)
-{
- return FALSE;
-}
-
-static void
-imhtml_paste_cb(GtkIMHtml *imhtml, const char *str)
-{
- if (!gtk_text_view_get_editable(GTK_TEXT_VIEW(imhtml)))
- return;
-
- if (!str || !*str || !strcmp(str, "html"))
- g_signal_emit_by_name(imhtml, "paste_clipboard");
- else if (!strcmp(str, "text"))
- paste_unformatted_cb(NULL, imhtml);
-}
-
-static void imhtml_toggle_format(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons)
-{
- /* since this function is the handler for the formatting keystrokes,
- we need to check here that the formatting attempted is permitted */
- buttons &= imhtml->format_functions;
-
- switch (buttons) {
- case GTK_IMHTML_BOLD:
- imhtml_toggle_bold(imhtml);
- break;
- case GTK_IMHTML_ITALIC:
- imhtml_toggle_italic(imhtml);
- break;
- case GTK_IMHTML_UNDERLINE:
- imhtml_toggle_underline(imhtml);
- break;
- case GTK_IMHTML_STRIKE:
- imhtml_toggle_strike(imhtml);
- break;
- case GTK_IMHTML_SHRINK:
- imhtml_font_shrink(imhtml);
- break;
- case GTK_IMHTML_GROW:
- imhtml_font_grow(imhtml);
- break;
- default:
- break;
- }
-}
-
-static void
-gtk_imhtml_finalize (GObject *object)
-{
- GtkIMHtml *imhtml = GTK_IMHTML(object);
- GList *scalables;
- GSList *l;
-
- if (imhtml->scroll_src)
- g_source_remove(imhtml->scroll_src);
- if (imhtml->scroll_time)
- g_timer_destroy(imhtml->scroll_time);
-
- g_hash_table_destroy(imhtml->smiley_data);
- gtk_smiley_tree_destroy(imhtml->default_smilies);
- gdk_cursor_unref(imhtml->hand_cursor);
- gdk_cursor_unref(imhtml->arrow_cursor);
- gdk_cursor_unref(imhtml->text_cursor);
-
- if(imhtml->tip_window){
- gtk_widget_destroy(imhtml->tip_window);
- }
- if(imhtml->tip_timer)
- g_source_remove(imhtml->tip_timer);
-
- for(scalables = imhtml->scalables; scalables; scalables = scalables->next) {
- struct scalable_data *sd = scalables->data;
- GtkIMHtmlScalable *scale = GTK_IMHTML_SCALABLE(sd->scalable);
- scale->free(scale);
- g_free(sd);
- }
-
- for (l = imhtml->im_images; l; l = l->next) {
- struct im_image_data *img_data = l->data;
- if (imhtml->funcs->image_unref)
- imhtml->funcs->image_unref(img_data->id);
- g_free(img_data);
- }
-
- g_list_free(imhtml->scalables);
- g_slist_free(imhtml->im_images);
- g_queue_free(imhtml->animations);
- g_free(imhtml->protocol_name);
- g_free(imhtml->search_string);
- g_object_unref(imhtml->undo_manager);
- G_OBJECT_CLASS(parent_class)->finalize (object);
-
-}
-
-static GtkIMHtmlProtocol *
-imhtml_find_protocol(const char *url, gboolean reverse)
-{
- GtkIMHtmlClass *klass;
- GList *iter;
- GtkIMHtmlProtocol *proto = NULL;
- int length = reverse ? strlen(url) : -1;
-
- klass = g_type_class_ref(GTK_TYPE_IMHTML);
- 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) {
- return proto;
- }
- }
- return NULL;
-}
-
-static void
-imhtml_url_clicked(GtkIMHtml *imhtml, const char *url)
-{
- GtkIMHtmlProtocol *proto = imhtml_find_protocol(url, FALSE);
- GtkIMHtmlLink *link;
- if (!proto)
- return;
- link = g_new0(GtkIMHtmlLink, 1);
- link->imhtml = g_object_ref(imhtml);
- link->url = g_strdup(url);
- proto->activate(imhtml, link); /* XXX: Do something with the return value? */
- gtk_imhtml_link_destroy(link);
-}
-
-/* Boring GTK+ stuff */
-static void gtk_imhtml_class_init (GtkIMHtmlClass *klass)
-{
- GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
- GtkBindingSet *binding_set;
- GObjectClass *gobject_class;
- gobject_class = (GObjectClass*) klass;
- parent_class = g_type_class_ref(GTK_TYPE_TEXT_VIEW);
- signals[URL_CLICKED] = g_signal_new("url_clicked",
- G_TYPE_FROM_CLASS(gobject_class),
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET(GtkIMHtmlClass, url_clicked),
- NULL,
- 0,
- g_cclosure_marshal_VOID__POINTER,
- G_TYPE_NONE, 1,
- G_TYPE_POINTER);
- signals[BUTTONS_UPDATE] = g_signal_new("format_buttons_update",
- G_TYPE_FROM_CLASS(gobject_class),
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET(GtkIMHtmlClass, buttons_update),
- NULL,
- 0,
- g_cclosure_marshal_VOID__INT,
- G_TYPE_NONE, 1,
- G_TYPE_INT);
- signals[TOGGLE_FORMAT] = g_signal_new("format_function_toggle",
- G_TYPE_FROM_CLASS(gobject_class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET(GtkIMHtmlClass, toggle_format),
- NULL,
- 0,
- g_cclosure_marshal_VOID__INT,
- G_TYPE_NONE, 1,
- G_TYPE_INT);
- signals[CLEAR_FORMAT] = g_signal_new("format_function_clear",
- G_TYPE_FROM_CLASS(gobject_class),
- G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET(GtkIMHtmlClass, clear_format),
- NULL,
- 0,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
- signals[UPDATE_FORMAT] = g_signal_new("format_function_update",
- G_TYPE_FROM_CLASS(gobject_class),
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET(GtkIMHtmlClass, update_format),
- NULL,
- 0,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
- signals[MESSAGE_SEND] = g_signal_new("message_send",
- G_TYPE_FROM_CLASS(gobject_class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET(GtkIMHtmlClass, message_send),
- NULL,
- 0, g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
- signals[PASTE] = g_signal_new("paste",
- G_TYPE_FROM_CLASS(gobject_class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- 0,
- NULL,
- 0, g_cclosure_marshal_VOID__STRING,
- G_TYPE_NONE, 1, G_TYPE_STRING);
- signals [UNDO] = g_signal_new ("undo",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (GtkIMHtmlClass, undo),
- NULL,
- NULL,
- gtksourceview_marshal_VOID__VOID,
- G_TYPE_NONE,
- 0);
- signals [REDO] = g_signal_new ("redo",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (GtkIMHtmlClass, redo),
- NULL,
- NULL,
- gtksourceview_marshal_VOID__VOID,
- G_TYPE_NONE,
- 0);
-
-
-
- klass->toggle_format = imhtml_toggle_format;
- klass->message_send = imhtml_message_send;
- klass->clear_format = imhtml_clear_formatting;
- klass->url_clicked = imhtml_url_clicked;
- klass->undo = gtk_imhtml_undo;
- klass->redo = gtk_imhtml_redo;
-
- gobject_class->finalize = gtk_imhtml_finalize;
- widget_class->drag_motion = gtk_text_view_drag_motion;
- widget_class->expose_event = gtk_imhtml_expose_event;
- parent_size_allocate = widget_class->size_allocate;
- widget_class->size_allocate = gtk_imhtml_size_allocate;
- parent_style_set = widget_class->style_set;
- widget_class->style_set = gtk_imhtml_style_set;
-
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("hyperlink-color",
- _("Hyperlink color"),
- _("Color to draw hyperlinks."),
- GDK_TYPE_COLOR, G_PARAM_READABLE));
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("hyperlink-visited-color",
- _("Hyperlink visited color"),
- _("Color to draw hyperlink after it has been visited (or activated)."),
- GDK_TYPE_COLOR, G_PARAM_READABLE));
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("hyperlink-prelight-color",
- _("Hyperlink prelight color"),
- _("Color to draw hyperlinks when mouse is over them."),
- GDK_TYPE_COLOR, G_PARAM_READABLE));
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("send-name-color",
- _("Sent Message Name Color"),
- _("Color to draw the name of a message you sent."),
- GDK_TYPE_COLOR, G_PARAM_READABLE));
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("receive-name-color",
- _("Received Message Name Color"),
- _("Color to draw the name of a message you received."),
- GDK_TYPE_COLOR, G_PARAM_READABLE));
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("highlight-name-color",
- _("\"Attention\" Name Color"),
- _("Color to draw the name of a message you received containing your name."),
- GDK_TYPE_COLOR, G_PARAM_READABLE));
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("action-name-color",
- _("Action Message Name Color"),
- _("Color to draw the name of an action message."),
- GDK_TYPE_COLOR, G_PARAM_READABLE));
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("whisper-action-name-color",
- _("Action Message Name Color for Whispered Message"),
- _("Color to draw the name of a whispered action message."),
- GDK_TYPE_COLOR, G_PARAM_READABLE));
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("whisper-name-color",
- _("Whisper Message Name Color"),
- _("Color to draw the name of a whispered message."),
- GDK_TYPE_COLOR, G_PARAM_READABLE));
-
- /* Customizable typing notification ... sort of. Example:
- * GtkIMHtml::typing-notification-font = "monospace italic light 8.0"
- * GtkIMHtml::typing-notification-color = "#ff0000"
- * GtkIMHtml::typing-notification-enable = 1
- */
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("typing-notification-color",
- _("Typing notification color"),
- _("The color to use for the typing notification"),
- GDK_TYPE_COLOR, G_PARAM_READABLE));
- gtk_widget_class_install_style_property(widget_class, g_param_spec_string("typing-notification-font",
- _("Typing notification font"),
- _("The font to use for the typing notification"),
- "light 8.0", G_PARAM_READABLE));
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boolean("typing-notification-enable",
- _("Enable typing notification"),
- _("Enable typing notification"),
- TRUE, G_PARAM_READABLE));
-
- binding_set = gtk_binding_set_by_class (parent_class);
- gtk_binding_entry_add_signal (binding_set, GDK_b, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_BOLD);
- gtk_binding_entry_add_signal (binding_set, GDK_i, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_ITALIC);
- gtk_binding_entry_add_signal (binding_set, GDK_u, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_UNDERLINE);
- gtk_binding_entry_add_signal (binding_set, GDK_plus, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_GROW);
- gtk_binding_entry_add_signal (binding_set, GDK_equal, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_GROW);
- gtk_binding_entry_add_signal (binding_set, GDK_minus, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_SHRINK);
- binding_set = gtk_binding_set_by_class(klass);
- gtk_binding_entry_add_signal (binding_set, GDK_r, GDK_CONTROL_MASK, "format_function_clear", 0);
- gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0, "message_send", 0);
- gtk_binding_entry_add_signal (binding_set, GDK_Return, 0, "message_send", 0);
- gtk_binding_entry_add_signal (binding_set, GDK_z, GDK_CONTROL_MASK, "undo", 0);
- gtk_binding_entry_add_signal (binding_set, GDK_z, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "redo", 0);
- gtk_binding_entry_add_signal (binding_set, GDK_F14, 0, "undo", 0);
- gtk_binding_entry_add_signal(binding_set, GDK_v, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "paste", 1, G_TYPE_STRING, "text");
-}
-
-static void gtk_imhtml_init (GtkIMHtml *imhtml)
-{
- imhtml->text_buffer = gtk_text_buffer_new(NULL);
- imhtml->undo_manager = gtk_source_undo_manager_new(imhtml->text_buffer);
- gtk_text_view_set_buffer(GTK_TEXT_VIEW(imhtml), imhtml->text_buffer);
- gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(imhtml), GTK_WRAP_WORD_CHAR);
- gtk_text_view_set_pixels_above_lines(GTK_TEXT_VIEW(imhtml), 2);
- gtk_text_view_set_pixels_below_lines(GTK_TEXT_VIEW(imhtml), 3);
- gtk_text_view_set_left_margin(GTK_TEXT_VIEW(imhtml), 2);
- gtk_text_view_set_right_margin(GTK_TEXT_VIEW(imhtml), 2);
- /*gtk_text_view_set_indent(GTK_TEXT_VIEW(imhtml), -15);*/
- /*gtk_text_view_set_justification(GTK_TEXT_VIEW(imhtml), GTK_JUSTIFY_FILL);*/
-
- /* These tags will be used often and can be reused--we create them on init and then apply them by name
- * other tags (color, size, face, etc.) will have to be created and applied dynamically
- * Note that even though we created SUB, SUP, and PRE tags here, we don't really
- * apply them anywhere yet. */
- gtk_text_buffer_create_tag(imhtml->text_buffer, "BOLD", "weight", PANGO_WEIGHT_BOLD, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "ITALICS", "style", PANGO_STYLE_ITALIC, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "UNDERLINE", "underline", PANGO_UNDERLINE_SINGLE, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "STRIKE", "strikethrough", TRUE, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "SUB", "rise", -5000, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "SUP", "rise", 5000, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "PRE", "family", "Monospace", NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "search", "background", "#22ff00", "weight", "bold", NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "comment", "weight", PANGO_WEIGHT_NORMAL,
-#if FALSE && GTK_CHECK_VERSION(2,10,10)
- "invisible", FALSE,
-#endif
- NULL);
-
- gtk_text_buffer_create_tag(imhtml->text_buffer, "send-name", "weight", PANGO_WEIGHT_BOLD, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "receive-name", "weight", PANGO_WEIGHT_BOLD, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "highlight-name", "weight", PANGO_WEIGHT_BOLD, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "action-name", "weight", PANGO_WEIGHT_BOLD, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "whisper-action-name", "weight", PANGO_WEIGHT_BOLD, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "whisper-name", "weight", PANGO_WEIGHT_BOLD, NULL);
-
- /* When hovering over a link, we show the hand cursor--elsewhere we show the plain ol' pointer cursor */
- imhtml->hand_cursor = gdk_cursor_new (GDK_HAND2);
- imhtml->arrow_cursor = gdk_cursor_new (GDK_LEFT_PTR);
- imhtml->text_cursor = gdk_cursor_new (GDK_XTERM);
-
- imhtml->show_comments = TRUE;
-
- imhtml->smiley_data = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, (GDestroyNotify)gtk_smiley_tree_destroy);
- imhtml->default_smilies = gtk_smiley_tree_new();
-
- g_signal_connect(G_OBJECT(imhtml), "motion-notify-event", G_CALLBACK(gtk_motion_event_notify), NULL);
- g_signal_connect(G_OBJECT(imhtml), "leave-notify-event", G_CALLBACK(gtk_leave_event_notify), NULL);
- g_signal_connect(G_OBJECT(imhtml), "enter-notify-event", G_CALLBACK(gtk_enter_event_notify), NULL);
- g_signal_connect(G_OBJECT(imhtml), "button_press_event", G_CALLBACK(gtk_imhtml_button_press_event), NULL);
- g_signal_connect(G_OBJECT(imhtml->text_buffer), "insert-text", G_CALLBACK(preinsert_cb), imhtml);
- g_signal_connect(G_OBJECT(imhtml->text_buffer), "delete_range", G_CALLBACK(delete_cb), imhtml);
- g_signal_connect_after(G_OBJECT(imhtml->text_buffer), "insert-text", G_CALLBACK(insert_cb), imhtml);
- g_signal_connect_after(G_OBJECT(imhtml->text_buffer), "insert-child-anchor", G_CALLBACK(insert_ca_cb), imhtml);
- gtk_drag_dest_set(GTK_WIDGET(imhtml), 0,
- link_drag_drop_targets, sizeof(link_drag_drop_targets) / sizeof(GtkTargetEntry),
- GDK_ACTION_COPY);
- g_signal_connect(G_OBJECT(imhtml), "drag_data_received", G_CALLBACK(gtk_imhtml_link_drag_rcv_cb), imhtml);
- g_signal_connect(G_OBJECT(imhtml), "drag_drop", G_CALLBACK(gtk_imhtml_link_drop_cb), imhtml);
-
- g_signal_connect(G_OBJECT(imhtml), "copy-clipboard", G_CALLBACK(copy_clipboard_cb), NULL);
- g_signal_connect(G_OBJECT(imhtml), "cut-clipboard", G_CALLBACK(cut_clipboard_cb), NULL);
- g_signal_connect(G_OBJECT(imhtml), "paste-clipboard", G_CALLBACK(paste_clipboard_cb), NULL);
- g_signal_connect_after(G_OBJECT(imhtml), "realize", G_CALLBACK(imhtml_realized_remove_primary), NULL);
- g_signal_connect(G_OBJECT(imhtml), "unrealize", G_CALLBACK(imhtml_destroy_add_primary), NULL);
- g_signal_connect(G_OBJECT(imhtml), "paste", G_CALLBACK(imhtml_paste_cb), NULL);
-
-#ifndef _WIN32
- g_signal_connect_after(G_OBJECT(GTK_IMHTML(imhtml)->text_buffer), "mark-set",
- G_CALLBACK(mark_set_so_update_selection_cb), imhtml);
-#endif
-
- gtk_widget_add_events(GTK_WIDGET(imhtml),
- GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK);
-
- imhtml->tip = NULL;
- imhtml->tip_timer = 0;
- imhtml->tip_window = NULL;
-
- imhtml->edit.bold = FALSE;
- imhtml->edit.italic = FALSE;
- imhtml->edit.underline = FALSE;
- imhtml->edit.forecolor = NULL;
- imhtml->edit.backcolor = NULL;
- imhtml->edit.fontface = NULL;
- imhtml->edit.fontsize = 0;
- imhtml->edit.link = NULL;
-
-
- imhtml->scalables = NULL;
- imhtml->animations = g_queue_new();
- gtk_imhtml_set_editable(imhtml, FALSE);
- g_signal_connect(G_OBJECT(imhtml), "populate-popup",
- G_CALLBACK(hijack_menu_cb), NULL);
-}
-
-GtkWidget *gtk_imhtml_new(void *a, void *b)
-{
- return GTK_WIDGET(g_object_new(gtk_imhtml_get_type(), NULL));
-}
-
-GType gtk_imhtml_get_type()
-{
- static GType imhtml_type = 0;
-
- if (!imhtml_type) {
- static const GTypeInfo imhtml_info = {
- sizeof(GtkIMHtmlClass),
- NULL,
- NULL,
- (GClassInitFunc) gtk_imhtml_class_init,
- NULL,
- NULL,
- sizeof (GtkIMHtml),
- 0,
- (GInstanceInitFunc) gtk_imhtml_init,
- NULL
- };
-
- imhtml_type = g_type_register_static(gtk_text_view_get_type(),
- "GtkIMHtml", &imhtml_info, 0);
- }
-
- return imhtml_type;
-}
-
-static void gtk_imhtml_link_destroy(GtkIMHtmlLink *link)
-{
- if (link->imhtml)
- g_object_unref(link->imhtml);
- if (link->tag)
- g_object_unref(link->tag);
- g_free(link->url);
- g_free(link);
-}
-
-/* The callback for an event on a link tag. */
-static gboolean tag_event(GtkTextTag *tag, GObject *imhtml, GdkEvent *event, GtkTextIter *arg2, gpointer unused)
-{
- GdkEventButton *event_button = (GdkEventButton *) event;
- if (GTK_IMHTML(imhtml)->editable)
- return FALSE;
- if (event->type == GDK_BUTTON_RELEASE) {
- if ((event_button->button == 1) || (event_button->button == 2)) {
- GtkTextIter start, end;
- /* we shouldn't open a URL if the user has selected something: */
- if (gtk_text_buffer_get_selection_bounds(
- gtk_text_iter_get_buffer(arg2), &start, &end))
- return FALSE;
- gtk_imhtml_activate_tag(GTK_IMHTML(imhtml), tag);
- return FALSE;
- } else if(event_button->button == 3) {
- GList *children;
- GtkWidget *menu;
- GtkIMHtmlProtocol *proto;
- GtkIMHtmlLink *link = g_new(GtkIMHtmlLink, 1);
- link->imhtml = g_object_ref(imhtml);
- link->url = g_strdup(g_object_get_data(G_OBJECT(tag), "link_url"));
- link->tag = g_object_ref(tag);
-
- /* Don't want the tooltip around if user right-clicked on link */
- if (GTK_IMHTML(imhtml)->tip_window) {
- gtk_widget_destroy(GTK_IMHTML(imhtml)->tip_window);
- GTK_IMHTML(imhtml)->tip_window = NULL;
- }
- if (GTK_IMHTML(imhtml)->tip_timer) {
- g_source_remove(GTK_IMHTML(imhtml)->tip_timer);
- GTK_IMHTML(imhtml)->tip_timer = 0;
- }
- if (GTK_IMHTML(imhtml)->editable)
- gdk_window_set_cursor(event_button->window, GTK_IMHTML(imhtml)->text_cursor);
- else
- gdk_window_set_cursor(event_button->window, GTK_IMHTML(imhtml)->arrow_cursor);
- menu = gtk_menu_new();
- g_object_set_data_full(G_OBJECT(menu), "x-imhtml-url-data", link,
- (GDestroyNotify)gtk_imhtml_link_destroy);
-
- proto = imhtml_find_protocol(link->url, FALSE);
-
- if (proto && proto->context_menu) {
- proto->context_menu(GTK_IMHTML(link->imhtml), link, 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);
- gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
- event_button->button, event_button->time);
-
- return TRUE;
- }
- }
- if(event->type == GDK_BUTTON_PRESS && event_button->button == 3)
- return TRUE; /* Clicking the right mouse button on a link shouldn't
- be caught by the regular GtkTextView menu */
- else
- return FALSE; /* Let clicks go through if we didn't catch anything */
-}
-
-static gboolean
-gtk_text_view_drag_motion (GtkWidget *widget,
- GdkDragContext *context,
- gint x,
- gint y,
- guint time)
-{
- GdkDragAction suggested_action = 0;
-
- if (gtk_drag_dest_find_target (widget, context, NULL) == GDK_NONE) {
- /* can't accept any of the offered targets */
- } else {
- GtkWidget *source_widget;
- suggested_action = context->suggested_action;
- source_widget = gtk_drag_get_source_widget (context);
- if (source_widget == widget) {
- /* Default to MOVE, unless the user has
- * pressed ctrl or alt to affect available actions
- */
- if ((context->actions & GDK_ACTION_MOVE) != 0)
- suggested_action = GDK_ACTION_MOVE;
- }
- }
-
- gdk_drag_status (context, suggested_action, time);
-
- /* TRUE return means don't propagate the drag motion to parent
- * widgets that may also be drop sites.
- */
- return TRUE;
-}
-
-static void
-gtk_imhtml_link_drop_cb(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, gpointer user_data)
-{
- GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
-
- if (target != GDK_NONE)
- gtk_drag_get_data (widget, context, target, time);
- else
- gtk_drag_finish (context, FALSE, FALSE, time);
-
- return;
-}
-
-static void
-gtk_imhtml_link_drag_rcv_cb(GtkWidget *widget, GdkDragContext *dc, guint x, guint y,
- GtkSelectionData *sd, guint info, guint t, GtkIMHtml *imhtml)
-{
- gchar **links;
- gchar *link;
- char *text = (char *)sd->data;
- GtkTextMark *mark = gtk_text_buffer_get_insert(imhtml->text_buffer);
- GtkTextIter iter;
- gint i = 0;
-
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, mark);
-
- if(gtk_imhtml_get_editable(imhtml) && sd->data){
- switch (info) {
- case GTK_IMHTML_DRAG_URL:
- /* TODO: Is it really ok to change sd->data...? */
- purple_str_strip_char((char *)sd->data, '\r');
-
- links = g_strsplit((char *)sd->data, "\n", 0);
- while((link = links[i]) != NULL){
- if (gtk_imhtml_is_protocol(link)) {
- gchar *label;
-
- if(links[i + 1])
- i++;
-
- label = links[i];
-
- gtk_imhtml_insert_link(imhtml, mark, link, label);
- } else if (*link == '\0') {
- /* Ignore blank lines */
- } else {
- /* Special reasons, aka images being put in via other tag, etc. */
- /* ... don't pretend we handled it if we didn't */
- gtk_drag_finish(dc, FALSE, FALSE, t);
- g_strfreev(links);
- return;
- }
-
- i++;
- }
- g_strfreev(links);
- break;
- case GTK_IMHTML_DRAG_HTML:
- {
- char *utf8 = NULL;
- /* Ewww. This is all because mozilla thinks that text/html is 'for internal use only.'
- * as explained by this comment in gtkhtml:
- *
- * FIXME This hack decides the charset of the selection. It seems that
- * mozilla/netscape alway use ucs2 for text/html
- * and openoffice.org seems to always use utf8 so we try to validate
- * the string as utf8 and if that fails we assume it is ucs2
- *
- * See also the comment on text/html here:
- * http://mail.gnome.org/archives/gtk-devel-list/2001-September/msg00114.html
- */
- if (sd->length >= 2 && !g_utf8_validate(text, sd->length - 1, NULL)) {
- utf8 = utf16_to_utf8_with_bom_check(text, sd->length);
-
- if (!utf8) {
- purple_debug_warning("gtkimhtml", "g_convert from UTF-16 failed in drag_rcv_cb\n");
- return;
- }
- } else if (!(*text) || !g_utf8_validate(text, -1, NULL)) {
- purple_debug_warning("gtkimhtml", "empty string or invalid UTF-8 in drag_rcv_cb\n");
- return;
- }
-
- gtk_imhtml_insert_html_at_iter(imhtml, utf8 ? utf8 : text, 0, &iter);
- g_free(utf8);
- break;
- }
- case GTK_IMHTML_DRAG_TEXT:
- if (!(*text) || !g_utf8_validate(text, -1, NULL)) {
- purple_debug_warning("gtkimhtml", "empty string or invalid UTF-8 in drag_rcv_cb\n");
- return;
- } else {
- char *tmp = g_markup_escape_text(text, -1);
- gtk_imhtml_insert_html_at_iter(imhtml, tmp, 0, &iter);
- g_free(tmp);
- }
- break;
- default:
- gtk_drag_finish(dc, FALSE, FALSE, t);
- return;
- }
- gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t);
- } else {
- gtk_drag_finish(dc, FALSE, FALSE, t);
- }
-}
-
-static void gtk_smiley_tree_remove (GtkSmileyTree *tree,
- GtkIMHtmlSmiley *smiley)
-{
- GtkSmileyTree *t = tree;
- const gchar *x = smiley->smile;
- gint len = 0;
-
- while (*x) {
- gchar *pos;
-
- if (!t->values)
- return;
-
- pos = strchr (t->values->str, *x);
- if (pos)
- t = t->children [pos - t->values->str];
- else
- return;
-
- x++; len++;
- }
-
- if (t->image) {
- t->image = NULL;
- }
-}
-
-static gint
-gtk_smiley_tree_lookup (GtkSmileyTree *tree,
- const gchar *text)
-{
- GtkSmileyTree *t = tree;
- const gchar *x = text;
- gint len = 0;
- const gchar *amp;
- gint alen;
-
- while (*x) {
- gchar *pos;
-
- if (!t->values)
- break;
-
- if(*x == '&' && (amp = purple_markup_unescape_entity(x, &alen))) {
- gboolean matched = TRUE;
- /* Make sure all chars of the unescaped value match */
- while (*(amp + 1)) {
- pos = strchr (t->values->str, *amp);
- if (pos)
- t = t->children [GPOINTER_TO_INT(pos) - GPOINTER_TO_INT(t->values->str)];
- else {
- matched = FALSE;
- break;
- }
- amp++;
- }
- if (!matched)
- break;
-
- pos = strchr (t->values->str, *amp);
- }
- else if (*x == '<') /* Because we're all WYSIWYG now, a '<'
- * char should only appear as the start of a tag. Perhaps a safer (but costlier)
- * check would be to call gtk_imhtml_is_tag on it */
- break;
- else {
- alen = 1;
- pos = strchr (t->values->str, *x);
- }
-
- if (pos)
- t = t->children [GPOINTER_TO_INT(pos) - GPOINTER_TO_INT(t->values->str)];
- else
- break;
-
- x += alen;
- len += alen;
- }
-
- if (t->image)
- return len;
-
- return 0;
-}
-
-static void
-gtk_imhtml_disassociate_smiley_foreach(gpointer key, gpointer value,
- gpointer user_data)
-{
- GtkSmileyTree *tree = (GtkSmileyTree *) value;
- GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) user_data;
- gtk_smiley_tree_remove(tree, smiley);
-}
-
-static void
-gtk_imhtml_disconnect_smiley(GtkIMHtml *imhtml, GtkIMHtmlSmiley *smiley)
-{
- smiley->imhtml = NULL;
- g_signal_handlers_disconnect_matched(imhtml, G_SIGNAL_MATCH_DATA, 0, 0,
- NULL, NULL, smiley);
-}
-
-static void
-gtk_imhtml_disassociate_smiley(GtkIMHtmlSmiley *smiley)
-{
- if (smiley->imhtml) {
- gtk_smiley_tree_remove(smiley->imhtml->default_smilies, smiley);
- g_hash_table_foreach(smiley->imhtml->smiley_data,
- gtk_imhtml_disassociate_smiley_foreach, smiley);
- g_signal_handlers_disconnect_matched(smiley->imhtml, G_SIGNAL_MATCH_DATA,
- 0, 0, NULL, NULL, smiley);
- smiley->imhtml = NULL;
- }
-}
-
-void
-gtk_imhtml_associate_smiley (GtkIMHtml *imhtml,
- const gchar *sml,
- GtkIMHtmlSmiley *smiley)
-{
- GtkSmileyTree *tree;
- g_return_if_fail (imhtml != NULL);
- g_return_if_fail (GTK_IS_IMHTML (imhtml));
-
- if (sml == NULL)
- tree = imhtml->default_smilies;
- else if (!(tree = g_hash_table_lookup(imhtml->smiley_data, sml))) {
- tree = gtk_smiley_tree_new();
- g_hash_table_insert(imhtml->smiley_data, g_strdup(sml), tree);
- }
-
- /* need to disconnect old imhtml, if there is one */
- if (smiley->imhtml) {
- g_signal_handlers_disconnect_matched(smiley->imhtml, G_SIGNAL_MATCH_DATA,
- 0, 0, NULL, NULL, smiley);
- }
-
- smiley->imhtml = imhtml;
-
- gtk_smiley_tree_insert (tree, smiley);
-
- /* connect destroy signal for the imhtml */
- g_signal_connect(imhtml, "destroy", G_CALLBACK(gtk_imhtml_disconnect_smiley),
- smiley);
-}
-
-static gboolean
-gtk_imhtml_is_smiley (GtkIMHtml *imhtml,
- GSList *fonts,
- const gchar *text,
- gint *len)
-{
- GtkSmileyTree *tree;
- GtkIMHtmlFontDetail *font;
- char *sml = NULL;
-
- if (fonts) {
- font = fonts->data;
- sml = font->sml;
- }
-
- if (!sml)
- sml = imhtml->protocol_name;
-
- if (!sml || !(tree = g_hash_table_lookup(imhtml->smiley_data, sml)))
- tree = imhtml->default_smilies;
-
- if (tree == NULL)
- return FALSE;
-
- *len = gtk_smiley_tree_lookup (tree, text);
- return (*len > 0);
-}
-
-static GtkIMHtmlSmiley *gtk_imhtml_smiley_get_from_tree(GtkSmileyTree *t, const gchar *text)
-{
- const gchar *x = text;
- gchar *pos;
-
- if (t == NULL)
- return NULL;
-
- while (*x) {
- if (!t->values)
- return NULL;
-
- pos = strchr(t->values->str, *x);
- if (!pos)
- return NULL;
-
- t = t->children[GPOINTER_TO_INT(pos) - GPOINTER_TO_INT(t->values->str)];
- x++;
- }
-
- return t->image;
-}
-
-GtkIMHtmlSmiley *
-gtk_imhtml_smiley_get(GtkIMHtml *imhtml, const gchar *sml, const gchar *text)
-{
- GtkIMHtmlSmiley *ret;
-
- /* Look for custom smileys first */
- if (sml != NULL) {
- ret = gtk_imhtml_smiley_get_from_tree(g_hash_table_lookup(imhtml->smiley_data, sml), text);
- if (ret != NULL)
- return ret;
- }
-
- /* Fall back to check for default smileys */
- return gtk_imhtml_smiley_get_from_tree(imhtml->default_smilies, text);
-}
-
-static GdkPixbufAnimation *
-gtk_smiley_get_image(GtkIMHtmlSmiley *smiley)
-{
- if (!smiley->icon) {
- if (smiley->file) {
- smiley->icon = gdk_pixbuf_animation_new_from_file(smiley->file, NULL);
- } else if (smiley->loader) {
- smiley->icon = gdk_pixbuf_loader_get_animation(smiley->loader);
- if (smiley->icon)
- g_object_ref(G_OBJECT(smiley->icon));
- }
- }
-
- return smiley->icon;
-}
-
-#define VALID_TAG(x) do { \
- if (!g_ascii_strncasecmp (string, x ">", strlen (x ">"))) { \
- if (tag) *tag = g_strndup (string, strlen (x)); \
- if (len) *len = strlen (x) + 1; \
- return TRUE; \
- } \
- if (type) (*type)++; \
- } while (0)
-
-#define VALID_OPT_TAG(x) do { \
- if (!g_ascii_strncasecmp (string, x " ", strlen (x " "))) { \
- const gchar *c = string + strlen (x " "); \
- gchar e = '"'; \
- gboolean quote = FALSE; \
- while (*c) { \
- if (*c == '"' || *c == '\'') { \
- if (quote && (*c == e)) \
- quote = !quote; \
- else if (!quote) { \
- quote = !quote; \
- e = *c; \
- } \
- } else if (!quote && (*c == '>')) \
- break; \
- c++; \
- } \
- if (*c) { \
- if (tag) *tag = g_strndup (string, c - string); \
- if (len) *len = c - string + 1; \
- return TRUE; \
- } \
- } \
- if (type) (*type)++; \
- } while (0)
-
-
-static gboolean
-gtk_imhtml_is_tag (const gchar *string,
- gchar **tag,
- gint *len,
- gint *type)
-{
- char *close;
- if (type)
- *type = 1;
-
- if (!(close = strchr (string, '>')))
- return FALSE;
-
- VALID_TAG ("B");
- VALID_TAG ("BOLD");
- VALID_TAG ("/B");
- VALID_TAG ("/BOLD");
- VALID_TAG ("I");
- VALID_TAG ("ITALIC");
- VALID_TAG ("/I");
- VALID_TAG ("/ITALIC");
- VALID_TAG ("U");
- VALID_TAG ("UNDERLINE");
- VALID_TAG ("/U");
- VALID_TAG ("/UNDERLINE");
- VALID_TAG ("S");
- VALID_TAG ("STRIKE");
- VALID_TAG ("/S");
- VALID_TAG ("/STRIKE");
- VALID_TAG ("SUB");
- VALID_TAG ("/SUB");
- VALID_TAG ("SUP");
- VALID_TAG ("/SUP");
- VALID_TAG ("PRE");
- VALID_TAG ("/PRE");
- VALID_TAG ("TITLE");
- VALID_TAG ("/TITLE");
- VALID_TAG ("BR");
- VALID_TAG ("HR");
- VALID_TAG ("/FONT");
- VALID_TAG ("/A");
- VALID_TAG ("P");
- VALID_TAG ("/P");
- VALID_TAG ("H3");
- VALID_TAG ("/H3");
- VALID_TAG ("HTML");
- VALID_TAG ("/HTML");
- VALID_TAG ("BODY");
- VALID_TAG ("/BODY");
- VALID_TAG ("FONT");
- VALID_TAG ("HEAD");
- VALID_TAG ("/HEAD");
- VALID_TAG ("BINARY");
- VALID_TAG ("/BINARY");
-
- VALID_OPT_TAG ("HR");
- VALID_OPT_TAG ("FONT");
- VALID_OPT_TAG ("BODY");
- VALID_OPT_TAG ("A");
- VALID_OPT_TAG ("IMG");
- VALID_OPT_TAG ("P");
- VALID_OPT_TAG ("H3");
- VALID_OPT_TAG ("HTML");
-
- VALID_TAG ("CITE");
- VALID_TAG ("/CITE");
- VALID_TAG ("EM");
- VALID_TAG ("/EM");
- VALID_TAG ("STRONG");
- VALID_TAG ("/STRONG");
-
- VALID_OPT_TAG ("SPAN");
- VALID_TAG ("/SPAN");
- VALID_TAG ("BR/"); /* hack until gtkimhtml handles things better */
- VALID_TAG ("IMG");
- VALID_TAG("SPAN");
- VALID_OPT_TAG("BR");
-
- if (!g_ascii_strncasecmp(string, "!--", strlen ("!--"))) {
- gchar *e = strstr (string + strlen("!--"), "-->");
- if (e) {
- if (len) {
- *len = e - string + strlen ("-->");
- if (tag)
- *tag = g_strndup (string + strlen ("!--"), *len - strlen ("!---->"));
- }
- return TRUE;
- }
- }
-
- if (type)
- *type = -1;
- if (len)
- *len = close - string + 1;
- if (tag)
- *tag = g_strndup(string, close - string);
- return TRUE;
-}
-
-static gchar*
-gtk_imhtml_get_html_opt (gchar *tag,
- const gchar *opt)
-{
- gchar *t = tag;
- gchar *e, *a;
- gchar *val;
- gint len;
- const gchar *c;
- GString *ret;
-
- while (g_ascii_strncasecmp (t, opt, strlen (opt))) {
- gboolean quote = FALSE;
- if (*t == '\0') break;
- while (*t && !((*t == ' ') && !quote)) {
- if (*t == '\"')
- quote = ! quote;
- t++;
- }
- while (*t && (*t == ' ')) t++;
- }
-
- if (!g_ascii_strncasecmp (t, opt, strlen (opt))) {
- t += strlen (opt);
- } else {
- return NULL;
- }
-
- if ((*t == '\"') || (*t == '\'')) {
- e = a = ++t;
- while (*e && (*e != *(t - 1))) e++;
- if (*e == '\0') {
- return NULL;
- } else
- val = g_strndup(a, e - a);
- } else {
- e = a = t;
- while (*e && !isspace ((gint) *e)) e++;
- val = g_strndup(a, e - a);
- }
-
- ret = g_string_new("");
- e = val;
- while(*e) {
- if((c = purple_markup_unescape_entity(e, &len))) {
- ret = g_string_append(ret, c);
- e += len;
- } else {
- gunichar uni = g_utf8_get_char(e);
- ret = g_string_append_unichar(ret, uni);
- e = g_utf8_next_char(e);
- }
- }
-
- g_free(val);
-
- return g_string_free(ret, FALSE);
-}
-
-/* returns if the beginning of the text is a protocol. If it is the protocol, returns the length so
- the caller knows how long the protocol string is. */
-static int gtk_imhtml_is_protocol(const char *text)
-{
- GtkIMHtmlProtocol *proto = imhtml_find_protocol(text, FALSE);
- return proto ? proto->length : 0;
-}
-
-static gboolean smooth_scroll_cb(gpointer data);
-
-/*
- <KingAnt> marv: The two IM image functions in oscar are purple_odc_send_im and purple_odc_incoming
-
-
-[19:58] <Robot101> marv: images go into the imgstore, a refcounted... well.. hash. :)
-[19:59] <KingAnt> marv: I think the image tag used by the core is something like <img id="#"/>
-[19:59] Ro0tSiEgE robert42 RobFlynn Robot101 ross22 roz
-[20:00] <KingAnt> marv: Where the ID is the what is returned when you add the image to the imgstore using purple_imgstore_add
-[20:00] <marv> Robot101: so how does the image get passed to serv_got_im() and serv_send_im()? just as the <img id="#" and then the prpl looks it up from the store?
-[20:00] <KingAnt> marv: Right
-[20:00] <marv> alright
-
-Here's my plan with IMImages. make gtk_imhtml_[append|insert]_text_with_images instead just
-gtkimhtml_[append|insert]_text (hrm maybe it should be called html instead of text), add a
-function for purple to register for look up images, i.e. gtk_imhtml_set_get_img_fnc, so that
-images can be looked up like that, instead of passing a GSList of them.
- */
-
-void gtk_imhtml_append_text_with_images (GtkIMHtml *imhtml,
- const gchar *text,
- GtkIMHtmlOptions options,
- GSList *unused)
-{
- GtkTextIter iter, ins, sel;
- int ins_offset = 0, sel_offset = 0;
- gboolean fixins = FALSE, fixsel = FALSE;
-
- g_return_if_fail (imhtml != NULL);
- g_return_if_fail (GTK_IS_IMHTML (imhtml));
- g_return_if_fail (text != NULL);
-
-
- gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter);
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &ins, gtk_text_buffer_get_insert(imhtml->text_buffer));
- if (gtk_text_iter_equal(&iter, &ins) && gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, NULL, NULL)) {
- fixins = TRUE;
- ins_offset = gtk_text_iter_get_offset(&ins);
- }
-
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &sel, gtk_text_buffer_get_selection_bound(imhtml->text_buffer));
- if (gtk_text_iter_equal(&iter, &sel) && gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, NULL, NULL)) {
- fixsel = TRUE;
- sel_offset = gtk_text_iter_get_offset(&sel);
- }
-
- if (!(options & GTK_IMHTML_NO_SCROLL)) {
- GdkRectangle rect;
- int y, height;
-
- gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect);
- gtk_text_view_get_line_yrange(GTK_TEXT_VIEW(imhtml), &iter, &y, &height);
-
- if (((y + height) - (rect.y + rect.height)) > height &&
- gtk_text_buffer_get_char_count(imhtml->text_buffer)) {
- /* If we are in the middle of smooth-scrolling, then take a scroll step.
- * If we are not in the middle of smooth-scrolling, that means we were
- * not looking at the end of the buffer before the new text was added,
- * so do not scroll. */
- if (imhtml->scroll_time)
- smooth_scroll_cb(imhtml);
- else
- options |= GTK_IMHTML_NO_SCROLL;
- }
- }
-
- gtk_imhtml_insert_html_at_iter(imhtml, text, options, &iter);
-
- if (fixins) {
- gtk_text_buffer_get_iter_at_offset(imhtml->text_buffer, &ins, ins_offset);
- gtk_text_buffer_move_mark(imhtml->text_buffer, gtk_text_buffer_get_insert(imhtml->text_buffer), &ins);
- }
-
- if (fixsel) {
- gtk_text_buffer_get_iter_at_offset(imhtml->text_buffer, &sel, sel_offset);
- gtk_text_buffer_move_mark(imhtml->text_buffer, gtk_text_buffer_get_selection_bound(imhtml->text_buffer), &sel);
- }
-
- if (!(options & GTK_IMHTML_NO_SCROLL)) {
- gtk_imhtml_scroll_to_end(imhtml, (options & GTK_IMHTML_USE_SMOOTHSCROLLING));
- }
-}
-
-#define MAX_SCROLL_TIME 0.4 /* seconds */
-#define SCROLL_DELAY 33 /* milliseconds */
-
-/*
- * Smoothly scroll a GtkIMHtml.
- *
- * @return TRUE if the window needs to be scrolled further, FALSE if we're at the bottom.
- */
-static gboolean smooth_scroll_cb(gpointer data)
-{
- GtkIMHtml *imhtml = data;
- GtkAdjustment *adj = GTK_TEXT_VIEW(imhtml)->vadjustment;
- gdouble max_val = adj->upper - adj->page_size;
- gdouble scroll_val = gtk_adjustment_get_value(adj) + ((max_val - gtk_adjustment_get_value(adj)) / 3);
-
- g_return_val_if_fail(imhtml->scroll_time != NULL, FALSE);
-
- if (g_timer_elapsed(imhtml->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(imhtml->scroll_time);
- imhtml->scroll_time = NULL;
- g_source_remove(imhtml->scroll_src);
- imhtml->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)
-{
- GtkIMHtml *imhtml = data;
- GtkAdjustment *adj = GTK_TEXT_VIEW(imhtml)->vadjustment;
- if(adj) {
- gtk_adjustment_set_value(adj, adj->upper - adj->page_size);
- }
- imhtml->scroll_src = 0;
- return FALSE;
-}
-
-void gtk_imhtml_scroll_to_end(GtkIMHtml *imhtml, gboolean smooth)
-{
- if (imhtml->scroll_time)
- g_timer_destroy(imhtml->scroll_time);
- if (imhtml->scroll_src)
- g_source_remove(imhtml->scroll_src);
- if(smooth) {
- imhtml->scroll_time = g_timer_new();
- imhtml->scroll_src = g_timeout_add_full(G_PRIORITY_LOW, SCROLL_DELAY, smooth_scroll_cb, imhtml, NULL);
- } else {
- imhtml->scroll_time = NULL;
- imhtml->scroll_src = g_idle_add_full(G_PRIORITY_LOW, scroll_idle_cb, imhtml, NULL);
- }
-}
-
-/* CSS colors are either rgb (x,y,z) or #hex
- * we need to convert to hex if it is RGB */
-static gchar*
-parse_css_color(gchar *in_color)
-{
- char *tmp = in_color;
-
- if (*tmp == 'r' && *(++tmp) == 'g' && *(++tmp) == 'b' && *(++tmp)) {
- int rgbval[] = {0, 0, 0};
- int count = 0;
- const char *v_start;
-
- while (*tmp && g_ascii_isspace(*tmp))
- tmp++;
- if (*tmp != '(') {
- /* We don't support rgba() */
- purple_debug_warning("gtkimhtml", "Invalid rgb CSS color in '%s'!\n", in_color);
- return in_color;
- }
- tmp++;
-
- while (count < 3) {
- /* Skip any leading spaces */
- while (*tmp && g_ascii_isspace(*tmp))
- tmp++;
-
- /* Find the subsequent contiguous digits */
- v_start = tmp;
- if (*v_start == '-')
- tmp++;
- while (*tmp && g_ascii_isdigit(*tmp))
- tmp++;
-
- if (tmp != v_start) {
- char prev = *tmp;
- *tmp = '\0';
- rgbval[count] = atoi(v_start);
- *tmp = prev;
-
- /* deal with % */
- while (*tmp && g_ascii_isspace(*tmp))
- tmp++;
- if (*tmp == '%') {
- rgbval[count] = (rgbval[count] / 100.0) * 255;
- tmp++;
- }
- } else {
- purple_debug_warning("gtkimhtml", "Invalid rgb CSS color in '%s'!\n", in_color);
- return in_color;
- }
-
- if (rgbval[count] > 255) {
- rgbval[count] = 255;
- } else if (rgbval[count] < 0) {
- rgbval[count] = 0;
- }
-
- while (*tmp && g_ascii_isspace(*tmp))
- tmp++;
- if (*tmp == ',')
- tmp++;
-
- count++;
- }
-
- g_free(in_color);
- return g_strdup_printf("#%02X%02X%02X", rgbval[0], rgbval[1], rgbval[2]);
- }
-
- return in_color;
-}
-
-void gtk_imhtml_insert_html_at_iter(GtkIMHtml *imhtml,
- const gchar *text,
- GtkIMHtmlOptions options,
- GtkTextIter *iter)
-{
- GdkRectangle rect;
- gint pos = 0;
- gchar *ws;
- gchar *tag;
- gchar *bg = NULL;
- gint len;
- gint tlen, smilelen, wpos=0;
- gint type;
- const gchar *c;
- const gchar *amp;
- gint len_protocol;
-
- guint bold = 0,
- italics = 0,
- underline = 0,
- strike = 0,
- sub = 0,
- sup = 0,
- title = 0,
- pre = 0;
-
- gboolean br = FALSE;
- gboolean align_right = FALSE;
- gboolean rtl_direction = FALSE;
- gint align_line = 0;
-
- GSList *fonts = NULL;
- GObject *object;
- GtkIMHtmlScalable *scalable = NULL;
-
- g_return_if_fail (imhtml != NULL);
- g_return_if_fail (GTK_IS_IMHTML (imhtml));
- g_return_if_fail (text != NULL);
- c = text;
- len = strlen(text);
- ws = g_malloc(len + 1);
- ws[0] = '\0';
-
- g_object_set_data(G_OBJECT(imhtml), "gtkimhtml_numsmileys_thismsg", GINT_TO_POINTER(0));
-
- gtk_text_buffer_begin_user_action(imhtml->text_buffer);
- while (pos < len) {
- if (*c == '<' && gtk_imhtml_is_tag (c + 1, &tag, &tlen, &type)) {
- c++;
- pos++;
- ws[wpos] = '\0';
- br = FALSE;
- switch (type)
- {
- case 1: /* B */
- case 2: /* BOLD */
- case 54: /* STRONG */
- if (!(options & GTK_IMHTML_NO_FORMATTING)) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
-
- if ((bold == 0) && (imhtml->format_functions & GTK_IMHTML_BOLD))
- gtk_imhtml_toggle_bold(imhtml);
- bold++;
- ws[0] = '\0'; wpos = 0;
- }
- break;
- case 3: /* /B */
- case 4: /* /BOLD */
- case 55: /* /STRONG */
- if (!(options & GTK_IMHTML_NO_FORMATTING)) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
-
- if (bold) {
- bold--;
- if ((bold == 0) && (imhtml->format_functions & GTK_IMHTML_BOLD) && !imhtml->wbfo)
- gtk_imhtml_toggle_bold(imhtml);
- }
- }
- break;
- case 5: /* I */
- case 6: /* ITALIC */
- case 52: /* EM */
- if (!(options & GTK_IMHTML_NO_FORMATTING)) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- if ((italics == 0) && (imhtml->format_functions & GTK_IMHTML_ITALIC))
- gtk_imhtml_toggle_italic(imhtml);
- italics++;
- }
- break;
- case 7: /* /I */
- case 8: /* /ITALIC */
- case 53: /* /EM */
- if (!(options & GTK_IMHTML_NO_FORMATTING)) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- if (italics) {
- italics--;
- if ((italics == 0) && (imhtml->format_functions & GTK_IMHTML_ITALIC) && !imhtml->wbfo)
- gtk_imhtml_toggle_italic(imhtml);
- }
- }
- break;
- case 9: /* U */
- case 10: /* UNDERLINE */
- if (!(options & GTK_IMHTML_NO_FORMATTING)) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- if ((underline == 0) && (imhtml->format_functions & GTK_IMHTML_UNDERLINE))
- gtk_imhtml_toggle_underline(imhtml);
- underline++;
- }
- break;
- case 11: /* /U */
- case 12: /* /UNDERLINE */
- if (!(options & GTK_IMHTML_NO_FORMATTING)) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- if (underline) {
- underline--;
- if ((underline == 0) && (imhtml->format_functions & GTK_IMHTML_UNDERLINE) && !imhtml->wbfo)
- gtk_imhtml_toggle_underline(imhtml);
- }
- }
- break;
- case 13: /* S */
- case 14: /* STRIKE */
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- if ((strike == 0) && (imhtml->format_functions & GTK_IMHTML_STRIKE))
- gtk_imhtml_toggle_strike(imhtml);
- strike++;
- break;
- case 15: /* /S */
- case 16: /* /STRIKE */
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- if (strike)
- strike--;
- if ((strike == 0) && (imhtml->format_functions & GTK_IMHTML_STRIKE) && !imhtml->wbfo)
- gtk_imhtml_toggle_strike(imhtml);
- break;
- case 17: /* SUB */
- /* FIXME: reimpliment this */
- sub++;
- break;
- case 18: /* /SUB */
- /* FIXME: reimpliment this */
- if (sub)
- sub--;
- break;
- case 19: /* SUP */
- /* FIXME: reimplement this */
- sup++;
- break;
- case 20: /* /SUP */
- /* FIXME: reimplement this */
- if (sup)
- sup--;
- break;
- case 21: /* PRE */
- /* FIXME: reimplement this */
- pre++;
- break;
- case 22: /* /PRE */
- /* FIXME: reimplement this */
- if (pre)
- pre--;
- break;
- case 23: /* TITLE */
- /* FIXME: what was this supposed to do anyway? */
- title++;
- break;
- case 24: /* /TITLE */
- /* FIXME: make this undo whatever 23 was supposed to do */
- if (title) {
- if (options & GTK_IMHTML_NO_TITLE) {
- wpos = 0;
- ws [wpos] = '\0';
- }
- title--;
- }
- break;
- case 25: /* BR */
- case 58: /* BR/ */
- case 61: /* BR (opt) */
- ws[wpos] = '\n';
- wpos++;
- br = TRUE;
- break;
- case 26: /* HR */
- case 42: /* HR (opt) */
- {
- int minus;
- struct scalable_data *sd = g_new(struct scalable_data, 1);
-
- ws[wpos++] = '\n';
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
-
- sd->scalable = scalable = gtk_imhtml_hr_new();
- sd->mark = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, iter, TRUE);
- gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect);
- scalable->add_to(scalable, imhtml, iter);
- minus = gtk_text_view_get_left_margin(GTK_TEXT_VIEW(imhtml)) +
- gtk_text_view_get_right_margin(GTK_TEXT_VIEW(imhtml));
- scalable->scale(scalable, rect.width - minus, rect.height);
- imhtml->scalables = g_list_append(imhtml->scalables, sd);
- ws[0] = '\0'; wpos = 0;
- ws[wpos++] = '\n';
-
- break;
- }
- case 27: /* /FONT */
- if (fonts && !imhtml->wbfo) {
- GtkIMHtmlFontDetail *font = fonts->data;
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- /* NEW_BIT (NEW_TEXT_BIT); */
-
- if (font->face && (imhtml->format_functions & GTK_IMHTML_FACE)) {
- gtk_imhtml_toggle_fontface(imhtml, NULL);
- }
- g_free (font->face);
- if (font->fore && (imhtml->format_functions & GTK_IMHTML_FORECOLOR)) {
- gtk_imhtml_toggle_forecolor(imhtml, NULL);
- }
- g_free (font->fore);
- if (font->back && (imhtml->format_functions & GTK_IMHTML_BACKCOLOR)) {
- gtk_imhtml_toggle_backcolor(imhtml, NULL);
- }
- g_free (font->back);
- g_free (font->sml);
-
- if ((font->size != 3) && (imhtml->format_functions & (GTK_IMHTML_GROW|GTK_IMHTML_SHRINK)))
- gtk_imhtml_font_set_size(imhtml, 3);
-
- fonts = g_slist_remove (fonts, font);
- g_free(font);
-
- if (fonts) {
- GtkIMHtmlFontDetail *font = fonts->data;
-
- if (font->face && (imhtml->format_functions & GTK_IMHTML_FACE))
- gtk_imhtml_toggle_fontface(imhtml, font->face);
- if (font->fore && (imhtml->format_functions & GTK_IMHTML_FORECOLOR))
- gtk_imhtml_toggle_forecolor(imhtml, font->fore);
- if (font->back && (imhtml->format_functions & GTK_IMHTML_BACKCOLOR))
- gtk_imhtml_toggle_backcolor(imhtml, font->back);
- if ((font->size != 3) && (imhtml->format_functions & (GTK_IMHTML_GROW|GTK_IMHTML_SHRINK)))
- gtk_imhtml_font_set_size(imhtml, font->size);
- }
- }
- break;
- case 28: /* /A */
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- gtk_imhtml_toggle_link(imhtml, NULL);
- ws[0] = '\0'; wpos = 0;
- break;
-
- case 29: /* P */
- case 30: /* /P */
- case 31: /* H3 */
- case 32: /* /H3 */
- case 33: /* HTML */
- case 34: /* /HTML */
- case 35: /* BODY */
- break;
- case 36: /* /BODY */
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- gtk_imhtml_toggle_background(imhtml, NULL);
- break;
- case 37: /* FONT */
- case 38: /* HEAD */
- case 39: /* /HEAD */
- case 40: /* BINARY */
- case 41: /* /BINARY */
- break;
- case 43: /* FONT (opt) */
- {
- gchar *color, *back, *face, *size, *sml;
- GtkIMHtmlFontDetail *font, *oldfont = NULL;
- color = gtk_imhtml_get_html_opt (tag, "COLOR=");
- back = gtk_imhtml_get_html_opt (tag, "BACK=");
- face = gtk_imhtml_get_html_opt (tag, "FACE=");
- size = gtk_imhtml_get_html_opt (tag, "SIZE=");
- sml = gtk_imhtml_get_html_opt (tag, "SML=");
- if (!(color || back || face || size || sml))
- break;
-
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
-
- font = g_new0 (GtkIMHtmlFontDetail, 1);
- if (fonts)
- oldfont = fonts->data;
-
- if (color && !(options & GTK_IMHTML_NO_COLOURS) && (imhtml->format_functions & GTK_IMHTML_FORECOLOR)) {
- font->fore = color;
- gtk_imhtml_toggle_forecolor(imhtml, font->fore);
- } else
- g_free(color);
-
- if (back && !(options & GTK_IMHTML_NO_COLOURS) && (imhtml->format_functions & GTK_IMHTML_BACKCOLOR)) {
- font->back = back;
- gtk_imhtml_toggle_backcolor(imhtml, font->back);
- } else
- g_free(back);
-
- if (face && !(options & GTK_IMHTML_NO_FONTS) && (imhtml->format_functions & GTK_IMHTML_FACE)) {
- font->face = face;
- gtk_imhtml_toggle_fontface(imhtml, font->face);
- } else
- g_free(face);
-
- if (sml)
- font->sml = sml;
- else {
- g_free(sml);
- if (oldfont && oldfont->sml)
- font->sml = g_strdup(oldfont->sml);
- }
-
- if (size && !(options & GTK_IMHTML_NO_SIZES) && (imhtml->format_functions & (GTK_IMHTML_GROW|GTK_IMHTML_SHRINK))) {
- if (*size == '+') {
- sscanf (size + 1, "%hd", &font->size);
- font->size += 3;
- } else if (*size == '-') {
- sscanf (size + 1, "%hd", &font->size);
- font->size = MAX (0, 3 - font->size);
- } else if (isdigit (*size)) {
- sscanf (size, "%hd", &font->size);
- }
- if (font->size > 100)
- font->size = 100;
- } else if (oldfont)
- font->size = oldfont->size;
- else
- font->size = 3;
- if ((imhtml->format_functions & (GTK_IMHTML_GROW|GTK_IMHTML_SHRINK)) && (font->size != 3 || (oldfont && oldfont->size == 3)))
- gtk_imhtml_font_set_size(imhtml, font->size);
- g_free(size);
- fonts = g_slist_prepend (fonts, font);
- }
- break;
- case 44: /* BODY (opt) */
- if (!(options & GTK_IMHTML_NO_COLOURS)) {
- char *bgcolor = gtk_imhtml_get_html_opt (tag, "BGCOLOR=");
- if (bgcolor && (imhtml->format_functions & GTK_IMHTML_BACKCOLOR)) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- /* NEW_BIT(NEW_TEXT_BIT); */
- g_free(bg);
- bg = bgcolor;
- gtk_imhtml_toggle_background(imhtml, bg);
- } else
- g_free(bgcolor);
- }
- break;
- case 45: /* A (opt) */
- {
- gchar *href = gtk_imhtml_get_html_opt (tag, "HREF=");
- if (href && (imhtml->format_functions & GTK_IMHTML_LINK)) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- gtk_imhtml_toggle_link(imhtml, href);
- }
- g_free(href);
- }
- break;
- case 46: /* IMG (opt) */
- case 59: /* IMG */
- {
- char *id;
-
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
-
- if (!(imhtml->format_functions & GTK_IMHTML_IMAGE))
- break;
-
- id = gtk_imhtml_get_html_opt(tag, "ID=");
- if (id) {
- gtk_imhtml_insert_image_at_iter(imhtml, atoi(id), iter);
- g_free(id);
- } else {
- char *src, *alt;
- src = gtk_imhtml_get_html_opt(tag, "SRC=");
- alt = gtk_imhtml_get_html_opt(tag, "ALT=");
- if (src) {
- gtk_imhtml_toggle_link(imhtml, src);
- gtk_text_buffer_insert(imhtml->text_buffer, iter, alt ? alt : src, -1);
- gtk_imhtml_toggle_link(imhtml, NULL);
- }
- g_free (src);
- g_free (alt);
- }
- break;
- }
- case 47: /* P (opt) */
- case 48: /* H3 (opt) */
- case 49: /* HTML (opt) */
- case 50: /* CITE */
- case 51: /* /CITE */
- case 56: /* SPAN (opt) */
- /* Inline CSS Support - Douglas Thrift
- *
- * color
- * background
- * font-family
- * font-size
- * text-decoration: underline
- * font-weight: bold
- * direction: rtl
- * text-align: right
- *
- * TODO:
- * background-color
- * font-style
- */
- {
- gchar *style, *color, *background, *family, *size, *direction, *alignment;
- gchar *textdec, *weight;
- GtkIMHtmlFontDetail *font, *oldfont = NULL;
- style = gtk_imhtml_get_html_opt (tag, "style=");
-
- if (!style) break;
-
- color = purple_markup_get_css_property (style, "color");
- background = purple_markup_get_css_property (style, "background");
- family = purple_markup_get_css_property (style, "font-family");
- size = purple_markup_get_css_property (style, "font-size");
- textdec = purple_markup_get_css_property (style, "text-decoration");
- weight = purple_markup_get_css_property (style, "font-weight");
- direction = purple_markup_get_css_property (style, "direction");
- alignment = purple_markup_get_css_property (style, "text-align");
-
-
- if (!(color || family || size || background || textdec || weight || direction || alignment)) {
- g_free(style);
- break;
- }
-
-
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- /* NEW_BIT (NEW_TEXT_BIT); */
-
- /* Bi-Directional text support */
- if (direction && (!g_ascii_strncasecmp(direction, "RTL", 3))) {
- rtl_direction = TRUE;
- /* insert RLE character to set direction */
- ws[wpos++] = 0xE2;
- ws[wpos++] = 0x80;
- ws[wpos++] = 0xAB;
- ws[wpos] = '\0';
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- }
- g_free(direction);
-
- if (alignment && (!g_ascii_strncasecmp(alignment, "RIGHT", 5))) {
- align_right = TRUE;
- align_line = gtk_text_iter_get_line(iter);
- }
- g_free(alignment);
-
- font = g_new0 (GtkIMHtmlFontDetail, 1);
- if (fonts)
- oldfont = fonts->data;
-
- if (color && !(options & GTK_IMHTML_NO_COLOURS) && (imhtml->format_functions & GTK_IMHTML_FORECOLOR)) {
- font->fore = parse_css_color(color);
- gtk_imhtml_toggle_forecolor(imhtml, font->fore);
- } else {
- if (oldfont && oldfont->fore)
- font->fore = g_strdup(oldfont->fore);
- g_free(color);
- }
-
- if (background && !(options & GTK_IMHTML_NO_COLOURS) && (imhtml->format_functions & GTK_IMHTML_BACKCOLOR)) {
- font->back = parse_css_color(background);
- gtk_imhtml_toggle_backcolor(imhtml, font->back);
- } else {
- if (oldfont && oldfont->back)
- font->back = g_strdup(oldfont->back);
- g_free(background);
- }
-
- if (family && !(options & GTK_IMHTML_NO_FONTS) && (imhtml->format_functions & GTK_IMHTML_FACE)) {
- font->face = family;
- gtk_imhtml_toggle_fontface(imhtml, font->face);
- } else {
- if (oldfont && oldfont->face)
- font->face = g_strdup(oldfont->face);
- g_free(family);
- }
- if (font->face && (atoi(font->face) > 100)) {
- /* WTF is this? */
- /* Maybe it sets a max size on the font face? I seem to
- * remember bad things happening if the font size was
- * 2 billion */
- g_free(font->face);
- font->face = g_strdup("100");
- }
-
- if (oldfont && oldfont->sml)
- font->sml = g_strdup(oldfont->sml);
-
- if (size && !(options & GTK_IMHTML_NO_SIZES) && (imhtml->format_functions & (GTK_IMHTML_SHRINK|GTK_IMHTML_GROW))) {
- if (g_ascii_strcasecmp(size, "xx-small") == 0)
- font->size = 1;
- else if (g_ascii_strcasecmp(size, "smaller") == 0
- || g_ascii_strcasecmp(size, "x-small") == 0)
- font->size = 2;
- else if (g_ascii_strcasecmp(size, "medium") == 0)
- font->size = 3;
- else if (g_ascii_strcasecmp(size, "large") == 0
- || g_ascii_strcasecmp(size, "larger") == 0)
- font->size = 4;
- else if (g_ascii_strcasecmp(size, "x-large") == 0)
- font->size = 5;
- else if (g_ascii_strcasecmp(size, "xx-large") == 0)
- font->size = 6;
-
- /*
- * TODO: Handle other values, like percentages, or
- * lengths specified as em, ex, px, in, cm, mm, pt
- * or pc. Or even better, use an actual HTML
- * renderer like webkit.
- */
- if (font->size > 0)
- gtk_imhtml_font_set_size(imhtml, font->size);
- }
- else if (oldfont)
- {
- font->size = oldfont->size;
- }
-
- if (oldfont)
- {
- font->underline = oldfont->underline;
- }
- if (textdec && font->underline != 1
- && g_ascii_strcasecmp(textdec, "underline") == 0
- && (imhtml->format_functions & GTK_IMHTML_UNDERLINE)
- && !(options & GTK_IMHTML_NO_FORMATTING))
- {
- gtk_imhtml_toggle_underline(imhtml);
- font->underline = 1;
- }
-
- if (oldfont)
- {
- font->strike = oldfont->strike;
- }
- if (textdec && font->strike != 1
- && g_ascii_strcasecmp(textdec, "line-through") == 0
- && (imhtml->format_functions & GTK_IMHTML_STRIKE)
- && !(options & GTK_IMHTML_NO_FORMATTING))
- {
- gtk_imhtml_toggle_strike(imhtml);
- font->strike = 1;
- }
- g_free(textdec);
-
- if (oldfont)
- {
- font->bold = oldfont->bold;
- }
- if (weight)
- {
- if(!g_ascii_strcasecmp(weight, "normal")) {
- font->bold = 0;
- } else if(!g_ascii_strcasecmp(weight, "bold")) {
- font->bold = 1;
- } else if(!g_ascii_strcasecmp(weight, "bolder")) {
- font->bold++;
- } else if(!g_ascii_strcasecmp(weight, "lighter")) {
- if(font->bold > 0)
- font->bold--;
- } else {
- int num = atoi(weight);
- if(num >= 700)
- font->bold = 1;
- else
- font->bold = 0;
- }
- if (((font->bold && oldfont && !oldfont->bold) || (oldfont && oldfont->bold && !font->bold) || (font->bold && !oldfont)) && !(options & GTK_IMHTML_NO_FORMATTING))
- {
- gtk_imhtml_toggle_bold(imhtml);
- }
- g_free(weight);
- }
-
- g_free(style);
- g_free(size);
- fonts = g_slist_prepend (fonts, font);
- }
- break;
- case 57: /* /SPAN */
- /* Inline CSS Support - Douglas Thrift */
- if (fonts && !imhtml->wbfo) {
- GtkIMHtmlFontDetail *oldfont = NULL;
- GtkIMHtmlFontDetail *font = fonts->data;
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- /* NEW_BIT (NEW_TEXT_BIT); */
- fonts = g_slist_remove (fonts, font);
- if (fonts)
- oldfont = fonts->data;
-
- if (!oldfont) {
- gtk_imhtml_font_set_size(imhtml, 3);
- if (font->underline && !(options & GTK_IMHTML_NO_FORMATTING))
- gtk_imhtml_toggle_underline(imhtml);
- if (font->strike && !(options & GTK_IMHTML_NO_FORMATTING))
- gtk_imhtml_toggle_strike(imhtml);
- if (font->bold && !(options & GTK_IMHTML_NO_FORMATTING))
- gtk_imhtml_toggle_bold(imhtml);
- if (!(options & GTK_IMHTML_NO_FONTS))
- gtk_imhtml_toggle_fontface(imhtml, NULL);
- if (!(options & GTK_IMHTML_NO_COLOURS))
- gtk_imhtml_toggle_forecolor(imhtml, NULL);
- if (!(options & GTK_IMHTML_NO_COLOURS))
- gtk_imhtml_toggle_backcolor(imhtml, NULL);
- }
- else
- {
-
- if ((font->size != oldfont->size) && !(options & GTK_IMHTML_NO_SIZES))
- gtk_imhtml_font_set_size(imhtml, oldfont->size);
-
- if ((font->underline != oldfont->underline) && !(options & GTK_IMHTML_NO_FORMATTING))
- gtk_imhtml_toggle_underline(imhtml);
-
- if ((font->strike != oldfont->strike) && !(options & GTK_IMHTML_NO_FORMATTING))
- gtk_imhtml_toggle_strike(imhtml);
-
- if (((font->bold && !oldfont->bold) || (oldfont->bold && !font->bold)) && !(options & GTK_IMHTML_NO_FORMATTING))
- gtk_imhtml_toggle_bold(imhtml);
-
- if (font->face && (!oldfont->face || strcmp(font->face, oldfont->face) != 0) && !(options & GTK_IMHTML_NO_FONTS))
- gtk_imhtml_toggle_fontface(imhtml, oldfont->face);
-
- if (font->fore && (!oldfont->fore || strcmp(font->fore, oldfont->fore) != 0) && !(options & GTK_IMHTML_NO_COLOURS))
- gtk_imhtml_toggle_forecolor(imhtml, oldfont->fore);
-
- if (font->back && (!oldfont->back || strcmp(font->back, oldfont->back) != 0) && !(options & GTK_IMHTML_NO_COLOURS))
- gtk_imhtml_toggle_backcolor(imhtml, oldfont->back);
- }
-
- g_free (font->face);
- g_free (font->fore);
- g_free (font->back);
- g_free (font->sml);
-
- g_free (font);
- }
- break;
- case 60: /* SPAN */
- break;
- case 62: /* comment */
- /* NEW_BIT (NEW_TEXT_BIT); */
- ws[wpos] = '\0';
-
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
-
-#if FALSE && GTK_CHECK_VERSION(2,10,10)
- wpos = g_snprintf (ws, len, "%s", tag);
- gtk_text_buffer_insert_with_tags_by_name(imhtml->text_buffer, iter, ws, wpos, "comment", NULL);
-#else
- if (imhtml->show_comments && !(options & GTK_IMHTML_NO_COMMENTS)) {
- wpos = g_snprintf (ws, len, "%s", tag);
- gtk_text_buffer_insert_with_tags_by_name(imhtml->text_buffer, iter, ws, wpos, "comment", NULL);
- }
-#endif
- ws[0] = '\0'; wpos = 0;
-
- /* NEW_BIT (NEW_COMMENT_BIT); */
- break;
- default:
- break;
- }
- c += tlen;
- pos += tlen;
- g_free(tag); /* This was allocated back in VALID_TAG() */
- } else if (imhtml->edit.link == NULL &&
- !(options & GTK_IMHTML_NO_SMILEY) &&
- gtk_imhtml_is_smiley(imhtml, fonts, c, &smilelen)) {
- GtkIMHtmlFontDetail *fd;
- gchar *sml = NULL;
-
- br = FALSE;
-
- if (fonts) {
- fd = fonts->data;
- sml = fd->sml;
- }
- if (!sml)
- sml = imhtml->protocol_name;
-
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- wpos = g_snprintf (ws, smilelen + 1, "%s", c);
-
- gtk_imhtml_insert_smiley_at_iter(imhtml, sml, ws, iter);
-
- c += smilelen;
- pos += smilelen;
- wpos = 0;
- ws[0] = 0;
- } else if (*c == '&' && (amp = purple_markup_unescape_entity(c, &tlen))) {
- br = FALSE;
- while(*amp) {
- ws [wpos++] = *amp++;
- }
- c += tlen;
- pos += tlen;
- } else if (*c == '\n') {
- if (!(options & GTK_IMHTML_NO_NEWLINE)) {
- ws[wpos] = '\n';
- wpos++;
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- /* NEW_BIT (NEW_TEXT_BIT); */
- } else if (!br) { /* Don't insert a space immediately after an HTML break */
- /* A newline is defined by HTML as whitespace, which means we have to replace it with a word boundary.
- * word breaks vary depending on the language used, so the correct thing to do is to use Pango to determine
- * what language this is, determine the proper word boundary to use, and insert that. I'm just going to insert
- * a space instead. What are the non-English speakers going to do? Complain in a language I'll understand?
- * Bu-wahaha! */
- ws[wpos] = ' ';
- wpos++;
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- }
- c++;
- pos++;
- } else if ((pos == 0 || wpos == 0 || isspace(*(c - 1))) &&
- (len_protocol = gtk_imhtml_is_protocol(c)) > 0 &&
- c[len_protocol] && !isspace(c[len_protocol]) &&
- (c[len_protocol] != '<' || !gtk_imhtml_is_tag(c + 1, NULL, NULL, NULL))) {
- br = FALSE;
- if (wpos > 0) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- }
- while (len_protocol--) {
- /* Skip the next len_protocol characters, but
- * make sure they're copied into the ws array.
- */
- ws [wpos++] = *c++;
- pos++;
- }
- if (!imhtml->edit.link && (imhtml->format_functions & GTK_IMHTML_LINK)) {
- while (*c && !isspace((int)*c) &&
- (*c != '<' || !gtk_imhtml_is_tag(c + 1, NULL, NULL, NULL))) {
- if (*c == '&' && (amp = purple_markup_unescape_entity(c, &tlen))) {
- while (*amp)
- ws[wpos++] = *amp++;
- c += tlen;
- pos += tlen;
- } else {
- ws [wpos++] = *c++;
- pos++;
- }
- }
- ws[wpos] = '\0';
- gtk_imhtml_toggle_link(imhtml, ws);
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- gtk_imhtml_toggle_link(imhtml, NULL);
- }
- } else if (*c) {
- br = FALSE;
- ws [wpos++] = *c++;
- pos++;
- } else {
- break;
- }
- }
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
-
- /* NEW_BIT(NEW_TEXT_BIT); */
-
- if(align_right) {
- /* insert RLM+LRM at beginning of the line to set alignment */
- GtkTextIter line_iter;
- line_iter = *iter;
- gtk_text_iter_set_line(&line_iter, align_line);
- /* insert RLM character to set alignment */
- ws[wpos++] = 0xE2;
- ws[wpos++] = 0x80;
- ws[wpos++] = 0x8F;
-
- if (!rtl_direction)
- {
- /* insert LRM character to set direction */
- /* (alignment=right and direction=LTR) */
- ws[wpos++] = 0xE2;
- ws[wpos++] = 0x80;
- ws[wpos++] = 0x8E;
- }
-
- ws[wpos] = '\0';
- gtk_text_buffer_insert(imhtml->text_buffer, &line_iter, ws, wpos);
- gtk_text_buffer_get_end_iter(gtk_text_iter_get_buffer(&line_iter), iter);
- ws[0] = '\0'; wpos = 0;
- }
-
- while (fonts) {
- GtkIMHtmlFontDetail *font = fonts->data;
- fonts = g_slist_remove (fonts, font);
- g_free (font->face);
- g_free (font->fore);
- g_free (font->back);
- g_free (font->sml);
- g_free (font);
- }
-
- g_free(ws);
- g_free(bg);
-
- if (!imhtml->wbfo)
- gtk_imhtml_close_tags(imhtml, iter);
-
- object = g_object_ref(G_OBJECT(imhtml));
- g_signal_emit(object, signals[UPDATE_FORMAT], 0);
- g_object_unref(object);
-
- gtk_text_buffer_end_user_action(imhtml->text_buffer);
-}
-
-void gtk_imhtml_remove_smileys(GtkIMHtml *imhtml)
-{
- g_hash_table_destroy(imhtml->smiley_data);
- gtk_smiley_tree_destroy(imhtml->default_smilies);
- imhtml->smiley_data = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, (GDestroyNotify)gtk_smiley_tree_destroy);
- imhtml->default_smilies = gtk_smiley_tree_new();
-}
-
-void gtk_imhtml_show_comments (GtkIMHtml *imhtml,
- gboolean show)
-{
-#if FALSE && GTK_CHECK_VERSION(2,10,10)
- GtkTextTag *tag;
- tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(imhtml->text_buffer), "comment");
- if (tag)
- g_object_set(G_OBJECT(tag), "invisible", !show, NULL);
-#endif
- imhtml->show_comments = show;
-}
-
-const char *
-gtk_imhtml_get_protocol_name(GtkIMHtml *imhtml) {
- return imhtml->protocol_name;
-}
-
-void
-gtk_imhtml_set_protocol_name(GtkIMHtml *imhtml, const gchar *protocol_name) {
- g_free(imhtml->protocol_name);
- imhtml->protocol_name = g_strdup(protocol_name);
-}
-
-void
-gtk_imhtml_delete(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *end) {
- GList *l;
- GSList *sl;
- GtkTextIter i, i_s, i_e;
- GObject *object = g_object_ref(G_OBJECT(imhtml));
-
- if (start == NULL) {
- gtk_text_buffer_get_start_iter(imhtml->text_buffer, &i_s);
- start = &i_s;
- }
-
- if (end == NULL) {
- gtk_text_buffer_get_end_iter(imhtml->text_buffer, &i_e);
- end = &i_e;
- }
-
- l = imhtml->scalables;
- while (l) {
- GList *next = l->next;
- struct scalable_data *sd = l->data;
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer,
- &i, sd->mark);
- if (gtk_text_iter_in_range(&i, start, end)) {
- GtkIMHtmlScalable *scale = GTK_IMHTML_SCALABLE(sd->scalable);
- scale->free(scale);
- g_free(sd);
- imhtml->scalables = g_list_delete_link(imhtml->scalables, l);
- }
- l = next;
- }
-
- sl = imhtml->im_images;
- while (sl) {
- GSList *next = sl->next;
- struct im_image_data *img_data = sl->data;
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer,
- &i, img_data->mark);
- if (gtk_text_iter_in_range(&i, start, end)) {
- if (imhtml->funcs->image_unref)
- imhtml->funcs->image_unref(img_data->id);
- imhtml->im_images = g_slist_delete_link(imhtml->im_images, sl);
- g_free(img_data);
- }
- sl = next;
- }
- gtk_text_buffer_delete(imhtml->text_buffer, start, end);
-
- g_object_set_data(G_OBJECT(imhtml), "gtkimhtml_numsmileys_total", GINT_TO_POINTER(0));
-
- g_object_unref(object);
-}
-
-void gtk_imhtml_page_up (GtkIMHtml *imhtml)
-{
- GdkRectangle rect;
- GtkTextIter iter;
-
- gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect);
- gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(imhtml), &iter, rect.x,
- rect.y - rect.height);
- gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(imhtml), &iter, 0, TRUE, 0, 0);
-
-}
-void gtk_imhtml_page_down (GtkIMHtml *imhtml)
-{
- GdkRectangle rect;
- GtkTextIter iter;
-
- gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect);
- gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(imhtml), &iter, rect.x,
- rect.y + rect.height);
- gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(imhtml), &iter, 0, TRUE, 0, 0);
-}
-
-/* GtkIMHtmlScalable, gtk_imhtml_image, gtk_imhtml_hr */
-GtkIMHtmlScalable *gtk_imhtml_image_new(GdkPixbuf *img, const gchar *filename, int id)
-{
- GtkIMHtmlImage *im_image = g_malloc(sizeof(GtkIMHtmlImage));
-
- GTK_IMHTML_SCALABLE(im_image)->scale = gtk_imhtml_image_scale;
- GTK_IMHTML_SCALABLE(im_image)->add_to = gtk_imhtml_image_add_to;
- GTK_IMHTML_SCALABLE(im_image)->free = gtk_imhtml_image_free;
-
- im_image->pixbuf = img;
- im_image->image = GTK_IMAGE(gtk_image_new_from_pixbuf(im_image->pixbuf));
- im_image->width = gdk_pixbuf_get_width(img);
- im_image->height = gdk_pixbuf_get_height(img);
- im_image->mark = NULL;
- im_image->filename = g_strdup(filename);
- im_image->id = id;
- im_image->filesel = NULL;
-
- g_object_ref(img);
- return GTK_IMHTML_SCALABLE(im_image);
-}
-
-static gboolean
-animate_image_cb(gpointer data)
-{
- GtkIMHtmlImage *im_image;
- int width, height;
- int delay;
-
- im_image = data;
-
- /* Update the pointer to this GdkPixbuf frame of the animation */
- if (gdk_pixbuf_animation_iter_advance(GTK_IMHTML_ANIMATION(im_image)->iter, NULL)) {
- GdkPixbuf *pb = gdk_pixbuf_animation_iter_get_pixbuf(GTK_IMHTML_ANIMATION(im_image)->iter);
- g_object_unref(G_OBJECT(im_image->pixbuf));
- im_image->pixbuf = gdk_pixbuf_copy(pb);
-
- /* Update the displayed GtkImage */
- width = gdk_pixbuf_get_width(gtk_image_get_pixbuf(im_image->image));
- height = gdk_pixbuf_get_height(gtk_image_get_pixbuf(im_image->image));
- if (width > 0 && height > 0)
- {
- /* Need to scale the new frame to the same size as the old frame */
- GdkPixbuf *tmp;
- tmp = gdk_pixbuf_scale_simple(im_image->pixbuf, width, height, GDK_INTERP_BILINEAR);
- gtk_image_set_from_pixbuf(im_image->image, tmp);
- g_object_unref(G_OBJECT(tmp));
- } else {
- /* Display at full-size */
- gtk_image_set_from_pixbuf(im_image->image, im_image->pixbuf);
- }
- }
-
- delay = MIN(gdk_pixbuf_animation_iter_get_delay_time(GTK_IMHTML_ANIMATION(im_image)->iter), 100);
- GTK_IMHTML_ANIMATION(im_image)->timer = g_timeout_add(delay, animate_image_cb, im_image);
-
- return FALSE;
-}
-
-GtkIMHtmlScalable *gtk_imhtml_animation_new(GdkPixbufAnimation *anim, const gchar *filename, int id)
-{
- GtkIMHtmlImage *im_image = (GtkIMHtmlImage *) g_new0(GtkIMHtmlAnimation, 1);
-
- GTK_IMHTML_SCALABLE(im_image)->scale = gtk_imhtml_image_scale;
- GTK_IMHTML_SCALABLE(im_image)->add_to = gtk_imhtml_image_add_to;
- GTK_IMHTML_SCALABLE(im_image)->free = gtk_imhtml_animation_free;
-
- GTK_IMHTML_ANIMATION(im_image)->anim = anim;
- if (gdk_pixbuf_animation_is_static_image(anim)) {
- im_image->pixbuf = gdk_pixbuf_animation_get_static_image(anim);
- g_object_ref(im_image->pixbuf);
- } else {
- int delay;
- GdkPixbuf *pb;
- GTK_IMHTML_ANIMATION(im_image)->iter = gdk_pixbuf_animation_get_iter(anim, NULL);
- pb = gdk_pixbuf_animation_iter_get_pixbuf(GTK_IMHTML_ANIMATION(im_image)->iter);
- im_image->pixbuf = gdk_pixbuf_copy(pb);
- delay = MIN(gdk_pixbuf_animation_iter_get_delay_time(GTK_IMHTML_ANIMATION(im_image)->iter), 100);
- GTK_IMHTML_ANIMATION(im_image)->timer = g_timeout_add(delay, animate_image_cb, im_image);
- }
- im_image->image = GTK_IMAGE(gtk_image_new_from_pixbuf(im_image->pixbuf));
- im_image->width = gdk_pixbuf_animation_get_width(anim);
- im_image->height = gdk_pixbuf_animation_get_height(anim);
- im_image->filename = g_strdup(filename);
- im_image->id = id;
-
- g_object_ref(anim);
-
- return GTK_IMHTML_SCALABLE(im_image);
-}
-
-void gtk_imhtml_image_scale(GtkIMHtmlScalable *scale, int width, int height)
-{
- GtkIMHtmlImage *im_image = (GtkIMHtmlImage *)scale;
-
- if (im_image->width > width || im_image->height > height) {
- double ratio_w, ratio_h, ratio;
- int new_h, new_w;
- GdkPixbuf *new_image = NULL;
-
- ratio_w = ((double)width - 2) / im_image->width;
- ratio_h = ((double)height - 2) / im_image->height;
-
- ratio = (ratio_w < ratio_h) ? ratio_w : ratio_h;
-
- new_w = (int)(im_image->width * ratio);
- new_h = (int)(im_image->height * ratio);
-
- new_image = gdk_pixbuf_scale_simple(im_image->pixbuf, new_w, new_h, GDK_INTERP_BILINEAR);
- gtk_image_set_from_pixbuf(im_image->image, new_image);
- g_object_unref(G_OBJECT(new_image));
- } else if (gdk_pixbuf_get_width(gtk_image_get_pixbuf(im_image->image)) != im_image->width) {
- /* Enough space to show the full-size of the image. */
- GdkPixbuf *new_image;
-
- new_image = gdk_pixbuf_scale_simple(im_image->pixbuf, im_image->width, im_image->height, GDK_INTERP_BILINEAR);
- gtk_image_set_from_pixbuf(im_image->image, new_image);
- g_object_unref(G_OBJECT(new_image));
- }
-}
-
-static void
-image_save_yes_cb(GtkIMHtmlImageSave *save, const char *filename)
-{
- GError *error = NULL;
- GtkIMHtmlImage *image = (GtkIMHtmlImage *)save->image;
-
- gtk_widget_destroy(image->filesel);
- image->filesel = NULL;
-
- if (save->data && save->datasize) {
- g_file_set_contents(filename, save->data, save->datasize, &error);
- } else {
- gchar *type = NULL;
- GSList *formats = gdk_pixbuf_get_formats();
- char *newfilename;
-
- while (formats) {
- GdkPixbufFormat *format = formats->data;
- gchar **extensions = gdk_pixbuf_format_get_extensions(format);
- gpointer p = extensions;
-
- while(gdk_pixbuf_format_is_writable(format) && extensions && extensions[0]){
- gchar *fmt_ext = extensions[0];
- const gchar* file_ext = filename + strlen(filename) - strlen(fmt_ext);
-
- if(!g_ascii_strcasecmp(fmt_ext, file_ext)){
- type = gdk_pixbuf_format_get_name(format);
- break;
- }
-
- extensions++;
- }
-
- g_strfreev(p);
-
- if (type)
- break;
-
- formats = formats->next;
- }
-
- g_slist_free(formats);
-
- /* If I can't find a valid type, I will just tell the user about it and then assume
- it's a png */
- if (!type){
- char *basename, *tmp;
- char *dirname;
- GtkWidget *dialog = gtk_message_dialog_new_with_markup(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
- _("<span size='larger' weight='bold'>Unrecognized file type</span>\n\nDefaulting to PNG."));
-
- g_signal_connect_swapped(dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
- gtk_widget_show(dialog);
-
- type = g_strdup("png");
- dirname = g_path_get_dirname(filename);
- basename = g_path_get_basename(filename);
- tmp = strrchr(basename, '.');
- if (tmp != NULL)
- tmp[0] = '\0';
- newfilename = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s.png", dirname, basename);
- g_free(dirname);
- g_free(basename);
- } else {
- /*
- * We're able to save the file in it's original format, so we
- * can use the original file name.
- */
- newfilename = g_strdup(filename);
- }
-
- gdk_pixbuf_save(image->pixbuf, newfilename, type, &error, NULL);
-
- g_free(newfilename);
- g_free(type);
- }
-
- if (error){
- GtkWidget *dialog = gtk_message_dialog_new_with_markup(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
- _("<span size='larger' weight='bold'>Error saving image</span>\n\n%s"), error->message);
- g_signal_connect_swapped(dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
- gtk_widget_show(dialog);
- g_error_free(error);
- }
-}
-
-static void
-image_save_check_if_exists_cb(GtkWidget *widget, gint response, GtkIMHtmlImageSave *save)
-{
- gchar *filename;
- GtkIMHtmlImage *image = (GtkIMHtmlImage *)save->image;
-
- if (response != GTK_RESPONSE_ACCEPT) {
- gtk_widget_destroy(widget);
- image->filesel = NULL;
- return;
- }
-
- filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
-
- /*
- * XXX - We should probably prompt the user to determine if they really
- * want to overwrite the file or not. However, I don't feel like doing
- * that, so we're just always going to overwrite if the file exists.
- */
- /*
- if (g_file_test(filename, G_FILE_TEST_EXISTS)) {
- } else
- image_save_yes_cb(image, filename);
- */
-
- image_save_yes_cb(save, filename);
-
- g_free(filename);
-}
-
-static void
-gtk_imhtml_image_save(GtkWidget *w, GtkIMHtmlImageSave *save)
-{
- GtkIMHtmlImage *image = (GtkIMHtmlImage *)save->image;
-
- if (image->filesel != NULL) {
- gtk_window_present(GTK_WINDOW(image->filesel));
- return;
- }
-
- image->filesel = gtk_file_chooser_dialog_new(_("Save Image"),
- NULL,
- GTK_FILE_CHOOSER_ACTION_SAVE,
- GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
- GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
- NULL);
- gtk_dialog_set_default_response(GTK_DIALOG(image->filesel), GTK_RESPONSE_ACCEPT);
- if (image->filename != NULL)
- gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(image->filesel), image->filename);
- g_signal_connect(G_OBJECT(GTK_FILE_CHOOSER(image->filesel)), "response",
- G_CALLBACK(image_save_check_if_exists_cb), save);
-
- gtk_widget_show(image->filesel);
-}
-
-static void
-gtk_imhtml_custom_smiley_save(GtkWidget *w, GtkIMHtmlImageSave *save)
-{
- GtkIMHtmlImage *image = (GtkIMHtmlImage *)save->image;
-
- /* Create an add dialog */
- PidginSmiley *editor = pidgin_smiley_edit(NULL, NULL);
- pidgin_smiley_editor_set_shortcut(editor, image->filename);
- pidgin_smiley_editor_set_image(editor, image->pixbuf);
- pidgin_smiley_editor_set_data(editor, save->data, save->datasize);
-}
-
-/*
- * So, um, AIM Direct IM lets you send any file, not just images. You can
- * just insert a sound or a file or whatever in a conversation. It's
- * basically like file transfer, except there is an icon to open the file
- * embedded in the conversation. Someone should make the Purple core handle
- * all of that.
- */
-static gboolean gtk_imhtml_image_clicked(GtkWidget *w, GdkEvent *event, GtkIMHtmlImageSave *save)
-{
- GdkEventButton *event_button = (GdkEventButton *) event;
- GtkIMHtmlImage *image = (GtkIMHtmlImage *)save->image;
-
- if (event->type == GDK_BUTTON_RELEASE) {
- if(event_button->button == 3) {
- GtkWidget *img, *item, *menu;
- menu = gtk_menu_new();
-
- /* buttons and such */
- img = gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_MENU);
- item = gtk_image_menu_item_new_with_mnemonic(_("_Save Image..."));
- gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
- g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(gtk_imhtml_image_save), save);
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
-
- /* Add menu item for adding custom smiley to local smileys */
- /* we only add the menu if the image is of "custom smiley size"
- <= 96x96 pixels */
- if (image->width <= 96 && image->height <= 96) {
- img = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU);
- item = gtk_image_menu_item_new_with_mnemonic(_("_Add Custom Smiley..."));
- gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
- g_signal_connect(G_OBJECT(item), "activate",
- G_CALLBACK(gtk_imhtml_custom_smiley_save), save);
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
- }
-
- gtk_widget_show_all(menu);
- gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
- event_button->button, event_button->time);
-
- return TRUE;
- }
- }
- if(event->type == GDK_BUTTON_PRESS && event_button->button == 3)
- return TRUE; /* Clicking the right mouse button on a link shouldn't
- be caught by the regular GtkTextView menu */
- else
- return FALSE; /* Let clicks go through if we didn't catch anything */
-
-}
-
-static gboolean gtk_imhtml_smiley_clicked(GtkWidget *w, GdkEvent *event, GtkIMHtmlSmiley *smiley)
-{
- GdkPixbufAnimation *anim = NULL;
- GtkIMHtmlImageSave *save = NULL;
- gboolean ret;
-
- if (event->type != GDK_BUTTON_RELEASE || ((GdkEventButton*)event)->button != 3)
- return FALSE;
-
- anim = gtk_smiley_get_image(smiley);
- if (!anim)
- return FALSE;
-
- save = g_new0(GtkIMHtmlImageSave, 1);
- save->image = (GtkIMHtmlScalable *)gtk_imhtml_animation_new(anim, smiley->smile, 0);
- save->data = smiley->data; /* Do not need to memdup here, since the smiley is not
- destroyed before this GtkIMHtmlImageSave */
- save->datasize = smiley->datasize;
- ret = gtk_imhtml_image_clicked(w, event, save);
- g_object_set_data_full(G_OBJECT(w), "image-data", save->image, (GDestroyNotify)gtk_imhtml_animation_free);
- g_object_set_data_full(G_OBJECT(w), "image-save-data", save, (GDestroyNotify)g_free);
- return ret;
-}
-
-void gtk_imhtml_image_free(GtkIMHtmlScalable *scale)
-{
- GtkIMHtmlImage *image = (GtkIMHtmlImage *)scale;
-
- g_object_unref(image->pixbuf);
- g_free(image->filename);
- if (image->filesel)
- gtk_widget_destroy(image->filesel);
- g_free(scale);
-}
-
-void gtk_imhtml_animation_free(GtkIMHtmlScalable *scale)
-{
- GtkIMHtmlAnimation *animation = (GtkIMHtmlAnimation *)scale;
-
- if (animation->timer > 0)
- g_source_remove(animation->timer);
- if (animation->iter != NULL)
- g_object_unref(animation->iter);
- g_object_unref(animation->anim);
-
- gtk_imhtml_image_free(scale);
-}
-
-void gtk_imhtml_image_add_to(GtkIMHtmlScalable *scale, GtkIMHtml *imhtml, GtkTextIter *iter)
-{
- GtkIMHtmlImage *image = (GtkIMHtmlImage *)scale;
- GtkWidget *box = gtk_event_box_new();
- char *tag;
- GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter);
- GtkIMHtmlImageSave *save;
-
- gtk_container_add(GTK_CONTAINER(box), GTK_WIDGET(image->image));
-
- if(!gtk_check_version(2, 4, 0))
- g_object_set(G_OBJECT(box), "visible-window", FALSE, NULL);
-
- gtk_widget_show(GTK_WIDGET(image->image));
- gtk_widget_show(box);
-
- tag = g_strdup_printf("<IMG ID=\"%d\">", image->id);
- g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_htmltext", tag, g_free);
- g_object_set_data(G_OBJECT(anchor), "gtkimhtml_plaintext", "[Image]");
-
- gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), box, anchor);
-
- save = g_new0(GtkIMHtmlImageSave, 1);
- save->image = scale;
- g_signal_connect(G_OBJECT(box), "event", G_CALLBACK(gtk_imhtml_image_clicked), save);
- g_object_set_data_full(G_OBJECT(box), "image-save-data", save, (GDestroyNotify)g_free);
-}
-
-GtkIMHtmlScalable *gtk_imhtml_hr_new()
-{
- GtkIMHtmlHr *hr = g_malloc(sizeof(GtkIMHtmlHr));
-
- GTK_IMHTML_SCALABLE(hr)->scale = gtk_imhtml_hr_scale;
- GTK_IMHTML_SCALABLE(hr)->add_to = gtk_imhtml_hr_add_to;
- GTK_IMHTML_SCALABLE(hr)->free = gtk_imhtml_hr_free;
-
- hr->sep = gtk_hseparator_new();
- gtk_widget_set_size_request(hr->sep, 5000, 2);
- gtk_widget_show(hr->sep);
-
- return GTK_IMHTML_SCALABLE(hr);
-}
-
-void gtk_imhtml_hr_scale(GtkIMHtmlScalable *scale, int width, int height)
-{
- gtk_widget_set_size_request(((GtkIMHtmlHr *)scale)->sep, width - 2, 2);
-}
-
-void gtk_imhtml_hr_add_to(GtkIMHtmlScalable *scale, GtkIMHtml *imhtml, GtkTextIter *iter)
-{
- GtkIMHtmlHr *hr = (GtkIMHtmlHr *)scale;
- GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter);
- g_object_set_data(G_OBJECT(anchor), "gtkimhtml_htmltext", "<hr>");
- g_object_set_data(G_OBJECT(anchor), "gtkimhtml_plaintext", "\n---\n");
- gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), hr->sep, anchor);
-}
-
-void gtk_imhtml_hr_free(GtkIMHtmlScalable *scale)
-{
- g_free(scale);
-}
-
-gboolean gtk_imhtml_search_find(GtkIMHtml *imhtml, const gchar *text)
-{
- GtkTextIter iter, start, end;
- gboolean new_search = TRUE;
- GtkTextMark *start_mark;
-
- g_return_val_if_fail(imhtml != NULL, FALSE);
- g_return_val_if_fail(text != NULL, FALSE);
-
- start_mark = gtk_text_buffer_get_mark(imhtml->text_buffer, "search");
-
- if (start_mark && imhtml->search_string && !strcmp(text, imhtml->search_string))
- new_search = FALSE;
-
- if (new_search) {
- gtk_imhtml_search_clear(imhtml);
- g_free(imhtml->search_string);
- imhtml->search_string = g_strdup(text);
- gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter);
- } else {
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter,
- start_mark);
- }
-
- if (gtk_source_iter_backward_search(&iter, imhtml->search_string,
- GTK_SOURCE_SEARCH_VISIBLE_ONLY | GTK_SOURCE_SEARCH_CASE_INSENSITIVE,
- &start, &end, NULL))
- {
- gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(imhtml), &start, 0, TRUE, 0, 0);
- gtk_text_buffer_create_mark(imhtml->text_buffer, "search", &start, FALSE);
- if (new_search)
- {
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "search", &iter, &end);
- do
- gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "search", &start, &end);
- while (gtk_source_iter_backward_search(&start, imhtml->search_string,
- GTK_SOURCE_SEARCH_VISIBLE_ONLY |
- GTK_SOURCE_SEARCH_CASE_INSENSITIVE,
- &start, &end, NULL));
- }
- return TRUE;
- }
- else if (!new_search)
- {
- /* We hit the end, so start at the beginning again. */
- gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter);
-
- if (gtk_source_iter_backward_search(&iter, imhtml->search_string,
- GTK_SOURCE_SEARCH_VISIBLE_ONLY | GTK_SOURCE_SEARCH_CASE_INSENSITIVE,
- &start, &end, NULL))
- {
- gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(imhtml), &start, 0, TRUE, 0, 0);
- gtk_text_buffer_create_mark(imhtml->text_buffer, "search", &start, FALSE);
-
- return TRUE;
- }
-
- }
-
- return FALSE;
-}
-
-void gtk_imhtml_search_clear(GtkIMHtml *imhtml)
-{
- GtkTextIter start, end;
-
- g_return_if_fail(imhtml != NULL);
-
- gtk_text_buffer_get_start_iter(imhtml->text_buffer, &start);
- gtk_text_buffer_get_end_iter(imhtml->text_buffer, &end);
-
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "search", &start, &end);
- g_free(imhtml->search_string);
- imhtml->search_string = NULL;
-}
-
-static GtkTextTag *find_font_forecolor_tag(GtkIMHtml *imhtml, gchar *color)
-{
- gchar str[18];
- GtkTextTag *tag;
-
- g_snprintf(str, sizeof(str), "FORECOLOR %s", color);
-
- tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(imhtml->text_buffer), str);
- if (!tag) {
- GdkColor gcolor;
- if (!gdk_color_parse(color, &gcolor)) {
- gchar tmp[8];
- tmp[0] = '#';
- strncpy(&tmp[1], color, 7);
- tmp[7] = '\0';
- if (!gdk_color_parse(tmp, &gcolor))
- gdk_color_parse("black", &gcolor);
- }
- tag = gtk_text_buffer_create_tag(imhtml->text_buffer, str, "foreground-gdk", &gcolor, NULL);
- }
-
- return tag;
-}
-
-static GtkTextTag *find_font_backcolor_tag(GtkIMHtml *imhtml, gchar *color)
-{
- gchar str[18];
- GtkTextTag *tag;
-
- g_snprintf(str, sizeof(str), "BACKCOLOR %s", color);
-
- tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(imhtml->text_buffer), str);
- if (!tag) {
- GdkColor gcolor;
- if (!gdk_color_parse(color, &gcolor)) {
- gchar tmp[8];
- tmp[0] = '#';
- strncpy(&tmp[1], color, 7);
- tmp[7] = '\0';
- if (!gdk_color_parse(tmp, &gcolor))
- gdk_color_parse("white", &gcolor);
- }
- tag = gtk_text_buffer_create_tag(imhtml->text_buffer, str, "background-gdk", &gcolor, NULL);
- }
-
- return tag;
-}
-
-static GtkTextTag *find_font_background_tag(GtkIMHtml *imhtml, gchar *color)
-{
- gchar str[19];
- GtkTextTag *tag;
-
- g_snprintf(str, sizeof(str), "BACKGROUND %s", color);
-
- tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(imhtml->text_buffer), str);
- if (!tag)
- tag = gtk_text_buffer_create_tag(imhtml->text_buffer, str, NULL);
-
- return tag;
-}
-
-static GtkTextTag *find_font_face_tag(GtkIMHtml *imhtml, gchar *face)
-{
- gchar str[256];
- GtkTextTag *tag;
-
- g_snprintf(str, sizeof(str), "FONT FACE %s", face);
- str[255] = '\0';
-
- tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(imhtml->text_buffer), str);
- if (!tag)
- tag = gtk_text_buffer_create_tag(imhtml->text_buffer, str, "family", face, NULL);
-
- return tag;
-}
-
-static GtkTextTag *find_font_size_tag(GtkIMHtml *imhtml, int size)
-{
- gchar str[24];
- GtkTextTag *tag;
-
- g_snprintf(str, sizeof(str), "FONT SIZE %d", size);
- str[23] = '\0';
-
- tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(imhtml->text_buffer), str);
- if (!tag) {
- /* For reasons I don't understand, setting "scale" here scaled
- * based on some default size other than my theme's default
- * size. Our size 4 was actually smaller than our size 3 for
- * me. So this works around that oddity.
- */
- GtkTextAttributes *attr = gtk_text_view_get_default_attributes(GTK_TEXT_VIEW(imhtml));
- tag = gtk_text_buffer_create_tag(imhtml->text_buffer, str, "size",
- (gint) (pango_font_description_get_size(attr->font) *
- (double) POINT_SIZE(size)), NULL);
- gtk_text_attributes_unref(attr);
- }
-
- return tag;
-}
-
-static void remove_tag_by_prefix(GtkIMHtml *imhtml, const GtkTextIter *i, const GtkTextIter *e,
- const char *prefix, guint len, gboolean homo)
-{
- GSList *tags, *l;
- GtkTextIter iter;
-
- tags = gtk_text_iter_get_tags(i);
-
- for (l = tags; l; l = l->next) {
- GtkTextTag *tag = l->data;
-
- if (tag->name && !strncmp(tag->name, prefix, len))
- gtk_text_buffer_remove_tag(imhtml->text_buffer, tag, i, e);
- }
-
- g_slist_free(tags);
-
- if (homo)
- return;
-
- iter = *i;
-
- while (gtk_text_iter_forward_char(&iter) && !gtk_text_iter_equal(&iter, e)) {
- if (gtk_text_iter_begins_tag(&iter, NULL)) {
- tags = gtk_text_iter_get_toggled_tags(&iter, TRUE);
-
- for (l = tags; l; l = l->next) {
- GtkTextTag *tag = l->data;
-
- if (tag->name && !strncmp(tag->name, prefix, len))
- gtk_text_buffer_remove_tag(imhtml->text_buffer, tag, &iter, e);
- }
-
- g_slist_free(tags);
- }
- }
-}
-
-static void remove_font_size(GtkIMHtml *imhtml, GtkTextIter *i, GtkTextIter *e, gboolean homo)
-{
- remove_tag_by_prefix(imhtml, i, e, "FONT SIZE ", 10, homo);
-}
-
-static void remove_font_face(GtkIMHtml *imhtml, GtkTextIter *i, GtkTextIter *e, gboolean homo)
-{
- remove_tag_by_prefix(imhtml, i, e, "FONT FACE ", 10, homo);
-}
-
-static void remove_font_forecolor(GtkIMHtml *imhtml, GtkTextIter *i, GtkTextIter *e, gboolean homo)
-{
- remove_tag_by_prefix(imhtml, i, e, "FORECOLOR ", 10, homo);
-}
-
-static void remove_font_backcolor(GtkIMHtml *imhtml, GtkTextIter *i, GtkTextIter *e, gboolean homo)
-{
- remove_tag_by_prefix(imhtml, i, e, "BACKCOLOR ", 10, homo);
-}
-
-static void remove_font_background(GtkIMHtml *imhtml, GtkTextIter *i, GtkTextIter *e, gboolean homo)
-{
- remove_tag_by_prefix(imhtml, i, e, "BACKGROUND ", 10, homo);
-}
-
-static void remove_font_link(GtkIMHtml *imhtml, GtkTextIter *i, GtkTextIter *e, gboolean homo)
-{
- remove_tag_by_prefix(imhtml, i, e, "LINK ", 5, homo);
-}
-
-static void
-imhtml_clear_formatting(GtkIMHtml *imhtml)
-{
- GtkTextIter start, end;
-
- if (!imhtml->editable)
- return;
-
- if (!imhtml_get_iter_bounds(imhtml, &start, &end))
- return;
-
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "BOLD", &start, &end);
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "ITALICS", &start, &end);
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "UNDERLINE", &start, &end);
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "STRIKE", &start, &end);
- remove_font_size(imhtml, &start, &end, FALSE);
- remove_font_face(imhtml, &start, &end, FALSE);
- remove_font_forecolor(imhtml, &start, &end, FALSE);
- remove_font_backcolor(imhtml, &start, &end, FALSE);
- remove_font_background(imhtml, &start, &end, FALSE);
- remove_font_link(imhtml, &start, &end, FALSE);
-
- imhtml->edit.bold = 0;
- imhtml->edit.italic = 0;
- imhtml->edit.underline = 0;
- imhtml->edit.strike = 0;
- imhtml->edit.fontsize = 0;
-
- g_free(imhtml->edit.fontface);
- imhtml->edit.fontface = NULL;
-
- g_free(imhtml->edit.forecolor);
- imhtml->edit.forecolor = NULL;
-
- g_free(imhtml->edit.backcolor);
- imhtml->edit.backcolor = NULL;
-
- g_free(imhtml->edit.background);
- imhtml->edit.background = NULL;
-}
-
-/* Editable stuff */
-static void preinsert_cb(GtkTextBuffer *buffer, GtkTextIter *iter, gchar *text, gint len, GtkIMHtml *imhtml)
-{
- imhtml->insert_offset = gtk_text_iter_get_offset(iter);
-}
-
-static void insert_ca_cb(GtkTextBuffer *buffer, GtkTextIter *arg1, GtkTextChildAnchor *arg2, gpointer user_data)
-{
- GtkTextIter start;
-
- start = *arg1;
- gtk_text_iter_backward_char(&start);
-
- gtk_imhtml_apply_tags_on_insert(user_data, &start, arg1);
-}
-
-static void insert_cb(GtkTextBuffer *buffer, GtkTextIter *end, gchar *text, gint len, GtkIMHtml *imhtml)
-{
- GtkTextIter start;
-
- if (!len)
- return;
-
- start = *end;
- gtk_text_iter_set_offset(&start, imhtml->insert_offset);
-
- gtk_imhtml_apply_tags_on_insert(imhtml, &start, end);
-}
-
-static void delete_cb(GtkTextBuffer *buffer, GtkTextIter *start, GtkTextIter *end, GtkIMHtml *imhtml)
-{
- GSList *tags, *l;
-
- tags = gtk_text_iter_get_tags(start);
- for (l = tags; l != NULL; l = l->next) {
- GtkTextTag *tag = GTK_TEXT_TAG(l->data);
-
- if (tag && /* Remove the formatting only if */
- gtk_text_iter_starts_word(start) && /* beginning of a word */
- gtk_text_iter_begins_tag(start, tag) && /* the tag starts with the selection */
- (!gtk_text_iter_has_tag(end, tag) || /* the tag ends within the selection */
- gtk_text_iter_ends_tag(end, tag))) {
- gtk_text_buffer_remove_tag(imhtml->text_buffer, tag, start, end);
- if (tag->name &&
- strncmp(tag->name, "LINK ", 5) == 0 && imhtml->edit.link) {
- gtk_imhtml_toggle_link(imhtml, NULL);
- }
- }
- }
- g_slist_free(tags);
-}
-
-static void gtk_imhtml_apply_tags_on_insert(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *end)
-{
- if (imhtml->edit.bold)
- gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "BOLD", start, end);
- else
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "BOLD", start, end);
-
- if (imhtml->edit.italic)
- gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "ITALICS", start, end);
- else
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "ITALICS", start, end);
-
- if (imhtml->edit.underline)
- gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "UNDERLINE", start, end);
- else
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "UNDERLINE", start, end);
-
- if (imhtml->edit.strike)
- gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "STRIKE", start, end);
- else
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "STRIKE", start, end);
-
- if (imhtml->edit.forecolor) {
- remove_font_forecolor(imhtml, start, end, TRUE);
- gtk_text_buffer_apply_tag(imhtml->text_buffer,
- find_font_forecolor_tag(imhtml, imhtml->edit.forecolor),
- start, end);
- }
-
- if (imhtml->edit.backcolor) {
- remove_font_backcolor(imhtml, start, end, TRUE);
- gtk_text_buffer_apply_tag(imhtml->text_buffer,
- find_font_backcolor_tag(imhtml, imhtml->edit.backcolor),
- start, end);
- }
-
- if (imhtml->edit.background) {
- remove_font_background(imhtml, start, end, TRUE);
- gtk_text_buffer_apply_tag(imhtml->text_buffer,
- find_font_background_tag(imhtml, imhtml->edit.background),
- start, end);
- }
- if (imhtml->edit.fontface) {
- remove_font_face(imhtml, start, end, TRUE);
- gtk_text_buffer_apply_tag(imhtml->text_buffer,
- find_font_face_tag(imhtml, imhtml->edit.fontface),
- start, end);
- }
-
- if (imhtml->edit.fontsize) {
- remove_font_size(imhtml, start, end, TRUE);
- gtk_text_buffer_apply_tag(imhtml->text_buffer,
- find_font_size_tag(imhtml, imhtml->edit.fontsize),
- start, end);
- }
-
- if (imhtml->edit.link) {
- remove_font_link(imhtml, start, end, TRUE);
- gtk_text_buffer_apply_tag(imhtml->text_buffer,
- imhtml->edit.link,
- start, end);
- }
-}
-
-void gtk_imhtml_set_editable(GtkIMHtml *imhtml, gboolean editable)
-{
- gtk_text_view_set_editable(GTK_TEXT_VIEW(imhtml), editable);
- /*
- * We need a visible caret for accessibility, so mouseless
- * people can highlight stuff.
- */
- /* gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(imhtml), editable); */
- if (editable && !imhtml->editable) {
- g_signal_connect_after(G_OBJECT(GTK_IMHTML(imhtml)->text_buffer), "mark-set",
- G_CALLBACK(mark_set_cb), imhtml);
- g_signal_connect(G_OBJECT(imhtml), "backspace", G_CALLBACK(smart_backspace_cb), NULL);
- } else if (!editable && imhtml->editable) {
- g_signal_handlers_disconnect_by_func(G_OBJECT(GTK_IMHTML(imhtml)->text_buffer),
- mark_set_cb, imhtml);
- g_signal_handlers_disconnect_by_func(G_OBJECT(imhtml),
- smart_backspace_cb, NULL);
- }
-
- imhtml->editable = editable;
- imhtml->format_functions = GTK_IMHTML_ALL;
-}
-
-void gtk_imhtml_set_whole_buffer_formatting_only(GtkIMHtml *imhtml, gboolean wbfo)
-{
- g_return_if_fail(imhtml != NULL);
-
- imhtml->wbfo = wbfo;
-}
-
-void gtk_imhtml_set_format_functions(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons)
-{
- GObject *object = g_object_ref(G_OBJECT(imhtml));
- imhtml->format_functions = buttons;
- g_signal_emit(object, signals[BUTTONS_UPDATE], 0, buttons);
- g_object_unref(object);
-}
-
-GtkIMHtmlButtons gtk_imhtml_get_format_functions(GtkIMHtml *imhtml)
-{
- return imhtml->format_functions;
-}
-
-void gtk_imhtml_get_current_format(GtkIMHtml *imhtml, gboolean *bold,
- gboolean *italic, gboolean *underline)
-{
- if (bold != NULL)
- (*bold) = imhtml->edit.bold;
- if (italic != NULL)
- (*italic) = imhtml->edit.italic;
- if (underline != NULL)
- (*underline) = imhtml->edit.underline;
-}
-
-char *
-gtk_imhtml_get_current_fontface(GtkIMHtml *imhtml)
-{
- return g_strdup(imhtml->edit.fontface);
-}
-
-char *
-gtk_imhtml_get_current_forecolor(GtkIMHtml *imhtml)
-{
- return g_strdup(imhtml->edit.forecolor);
-}
-
-char *
-gtk_imhtml_get_current_backcolor(GtkIMHtml *imhtml)
-{
- return g_strdup(imhtml->edit.backcolor);
-}
-
-char *
-gtk_imhtml_get_current_background(GtkIMHtml *imhtml)
-{
- return g_strdup(imhtml->edit.background);
-}
-
-gint
-gtk_imhtml_get_current_fontsize(GtkIMHtml *imhtml)
-{
- return imhtml->edit.fontsize;
-}
-
-gboolean gtk_imhtml_get_editable(GtkIMHtml *imhtml)
-{
- return imhtml->editable;
-}
-
-void
-gtk_imhtml_clear_formatting(GtkIMHtml *imhtml)
-{
- GObject *object;
-
- object = g_object_ref(G_OBJECT(imhtml));
- g_signal_emit(object, signals[CLEAR_FORMAT], 0);
-
- gtk_widget_grab_focus(GTK_WIDGET(imhtml));
-
- g_object_unref(object);
-}
-
-/*
- * I had this crazy idea about changing the text cursor color to reflex the foreground color
- * of the text about to be entered. This is the place you'd do it, along with the place where
- * we actually set a new foreground color.
- * I may not do this, because people will bitch about Purple overriding their gtk theme's cursor
- * colors.
- *
- * Just in case I do do this, I asked about what to set the secondary text cursor to.
- *
- * (12:45:27) ?? ???: secondary_cursor_color = (rgb(background) + rgb(primary_cursor_color) ) / 2
- * (12:45:55) ?? ???: understand?
- * (12:46:14) Tim: yeah. i didn't know there was an exact formula
- * (12:46:56) ?? ???: u might need to extract separate each color from RGB
- */
-
-static void mark_set_cb(GtkTextBuffer *buffer, GtkTextIter *arg1, GtkTextMark *mark,
- GtkIMHtml *imhtml)
-{
- GSList *tags, *l;
- GtkTextIter iter;
-
- if (mark != gtk_text_buffer_get_insert(buffer))
- return;
-
- if (!gtk_text_buffer_get_char_count(buffer))
- return;
-
- imhtml->edit.bold = imhtml->edit.italic = imhtml->edit.underline = imhtml->edit.strike = FALSE;
- g_free(imhtml->edit.forecolor);
- imhtml->edit.forecolor = NULL;
-
- g_free(imhtml->edit.backcolor);
- imhtml->edit.backcolor = NULL;
-
- g_free(imhtml->edit.fontface);
- imhtml->edit.fontface = NULL;
-
- imhtml->edit.fontsize = 0;
- imhtml->edit.link = NULL;
-
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, mark);
-
-
- if (gtk_text_iter_is_end(&iter))
- tags = gtk_text_iter_get_toggled_tags(&iter, FALSE);
- else
- tags = gtk_text_iter_get_tags(&iter);
-
- for (l = tags; l != NULL; l = l->next) {
- GtkTextTag *tag = GTK_TEXT_TAG(l->data);
-
- if (tag->name) {
- if (strcmp(tag->name, "BOLD") == 0)
- imhtml->edit.bold = TRUE;
- else if (strcmp(tag->name, "ITALICS") == 0)
- imhtml->edit.italic = TRUE;
- else if (strcmp(tag->name, "UNDERLINE") == 0)
- imhtml->edit.underline = TRUE;
- else if (strcmp(tag->name, "STRIKE") == 0)
- imhtml->edit.strike = TRUE;
- else if (strncmp(tag->name, "FORECOLOR ", 10) == 0)
- imhtml->edit.forecolor = g_strdup(&(tag->name)[10]);
- else if (strncmp(tag->name, "BACKCOLOR ", 10) == 0)
- imhtml->edit.backcolor = g_strdup(&(tag->name)[10]);
- else if (strncmp(tag->name, "FONT FACE ", 10) == 0)
- imhtml->edit.fontface = g_strdup(&(tag->name)[10]);
- else if (strncmp(tag->name, "FONT SIZE ", 10) == 0)
- imhtml->edit.fontsize = strtol(&(tag->name)[10], NULL, 10);
- else if ((strncmp(tag->name, "LINK ", 5) == 0) && !gtk_text_iter_is_end(&iter))
- imhtml->edit.link = tag;
- }
- }
-
- g_slist_free(tags);
-}
-
-static void imhtml_emit_signal_for_format(GtkIMHtml *imhtml, GtkIMHtmlButtons button)
-{
- GObject *object;
-
- g_return_if_fail(imhtml != NULL);
-
- object = g_object_ref(G_OBJECT(imhtml));
- g_signal_emit(object, signals[TOGGLE_FORMAT], 0, button);
- g_object_unref(object);
-}
-
-static void imhtml_toggle_bold(GtkIMHtml *imhtml)
-{
- GtkTextIter start, end;
-
- imhtml->edit.bold = !imhtml->edit.bold;
-
- if (!imhtml_get_iter_bounds(imhtml, &start, &end))
- return;
-
- if (imhtml->edit.bold)
- gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "BOLD", &start, &end);
- else
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "BOLD", &start, &end);
-}
-
-void gtk_imhtml_toggle_bold(GtkIMHtml *imhtml)
-{
- imhtml_emit_signal_for_format(imhtml, GTK_IMHTML_BOLD);
-}
-
-static void imhtml_toggle_italic(GtkIMHtml *imhtml)
-{
- GtkTextIter start, end;
-
- imhtml->edit.italic = !imhtml->edit.italic;
-
- if (!imhtml_get_iter_bounds(imhtml, &start, &end))
- return;
-
- if (imhtml->edit.italic)
- gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "ITALICS", &start, &end);
- else
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "ITALICS", &start, &end);
-}
-
-void gtk_imhtml_toggle_italic(GtkIMHtml *imhtml)
-{
- imhtml_emit_signal_for_format(imhtml, GTK_IMHTML_ITALIC);
-}
-
-static void imhtml_toggle_underline(GtkIMHtml *imhtml)
-{
- GtkTextIter start, end;
-
- imhtml->edit.underline = !imhtml->edit.underline;
-
- if (!imhtml_get_iter_bounds(imhtml, &start, &end))
- return;
-
- if (imhtml->edit.underline)
- gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "UNDERLINE", &start, &end);
- else
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "UNDERLINE", &start, &end);
-}
-
-void gtk_imhtml_toggle_underline(GtkIMHtml *imhtml)
-{
- imhtml_emit_signal_for_format(imhtml, GTK_IMHTML_UNDERLINE);
-}
-
-static void imhtml_toggle_strike(GtkIMHtml *imhtml)
-{
- GtkTextIter start, end;
-
- imhtml->edit.strike = !imhtml->edit.strike;
-
- if (!imhtml_get_iter_bounds(imhtml, &start, &end))
- return;
-
- if (imhtml->edit.strike)
- gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "STRIKE", &start, &end);
- else
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "STRIKE", &start, &end);
-}
-
-void gtk_imhtml_toggle_strike(GtkIMHtml *imhtml)
-{
- imhtml_emit_signal_for_format(imhtml, GTK_IMHTML_STRIKE);
-}
-
-void gtk_imhtml_font_set_size(GtkIMHtml *imhtml, gint size)
-{
- GObject *object;
- GtkTextIter start, end;
-
- imhtml->edit.fontsize = size;
-
- if (!imhtml_get_iter_bounds(imhtml, &start, &end))
- return;
-
- remove_font_size(imhtml, &start, &end, imhtml->wbfo);
- gtk_text_buffer_apply_tag(imhtml->text_buffer,
- find_font_size_tag(imhtml, imhtml->edit.fontsize), &start, &end);
-
- object = g_object_ref(G_OBJECT(imhtml));
- g_signal_emit(object, signals[TOGGLE_FORMAT], 0, GTK_IMHTML_SHRINK | GTK_IMHTML_GROW);
- g_object_unref(object);
-}
-
-static void imhtml_font_shrink(GtkIMHtml *imhtml)
-{
- GtkTextIter start, end;
-
- if (imhtml->edit.fontsize == 1)
- return;
-
- if (!imhtml->edit.fontsize)
- imhtml->edit.fontsize = 2;
- else
- imhtml->edit.fontsize--;
-
- if (!imhtml_get_iter_bounds(imhtml, &start, &end))
- return;
- remove_font_size(imhtml, &start, &end, imhtml->wbfo);
- gtk_text_buffer_apply_tag(imhtml->text_buffer,
- find_font_size_tag(imhtml, imhtml->edit.fontsize), &start, &end);
-}
-
-void gtk_imhtml_font_shrink(GtkIMHtml *imhtml)
-{
- imhtml_emit_signal_for_format(imhtml, GTK_IMHTML_SHRINK);
-}
-
-static void imhtml_font_grow(GtkIMHtml *imhtml)
-{
- GtkTextIter start, end;
-
- if (imhtml->edit.fontsize == MAX_FONT_SIZE)
- return;
-
- if (!imhtml->edit.fontsize)
- imhtml->edit.fontsize = 4;
- else
- imhtml->edit.fontsize++;
-
- if (!imhtml_get_iter_bounds(imhtml, &start, &end))
- return;
- remove_font_size(imhtml, &start, &end, imhtml->wbfo);
- gtk_text_buffer_apply_tag(imhtml->text_buffer,
- find_font_size_tag(imhtml, imhtml->edit.fontsize), &start, &end);
-}
-
-void gtk_imhtml_font_grow(GtkIMHtml *imhtml)
-{
- imhtml_emit_signal_for_format(imhtml, GTK_IMHTML_GROW);
-}
-
-static gboolean gtk_imhtml_toggle_str_tag(GtkIMHtml *imhtml, const char *value, char **edit_field,
- void (*remove_func)(GtkIMHtml *imhtml, GtkTextIter *i, GtkTextIter *e, gboolean homo),
- GtkTextTag *(find_func)(GtkIMHtml *imhtml, gchar *color), GtkIMHtmlButtons button)
-{
- GObject *object;
- GtkTextIter start;
- GtkTextIter end;
-
- g_free(*edit_field);
- *edit_field = NULL;
-
- if (value && strcmp(value, "") != 0)
- {
- *edit_field = g_strdup(value);
-
- if (imhtml_get_iter_bounds(imhtml, &start, &end)) {
- remove_func(imhtml, &start, &end, imhtml->wbfo);
- gtk_text_buffer_apply_tag(imhtml->text_buffer,
- find_func(imhtml, *edit_field), &start, &end);
- }
- }
- else
- {
- if (imhtml_get_iter_bounds(imhtml, &start, &end))
- remove_func(imhtml, &start, &end, TRUE); /* 'TRUE' or 'imhtml->wbfo'? */
- }
-
- object = g_object_ref(G_OBJECT(imhtml));
- g_signal_emit(object, signals[TOGGLE_FORMAT], 0, button);
- g_object_unref(object);
-
- return *edit_field != NULL;
-}
-
-gboolean gtk_imhtml_toggle_forecolor(GtkIMHtml *imhtml, const char *color)
-{
- return gtk_imhtml_toggle_str_tag(imhtml, color, &imhtml->edit.forecolor,
- remove_font_forecolor, find_font_forecolor_tag,
- GTK_IMHTML_FORECOLOR);
-}
-
-gboolean gtk_imhtml_toggle_backcolor(GtkIMHtml *imhtml, const char *color)
-{
- return gtk_imhtml_toggle_str_tag(imhtml, color, &imhtml->edit.backcolor,
- remove_font_backcolor, find_font_backcolor_tag,
- GTK_IMHTML_BACKCOLOR);
-}
-
-gboolean gtk_imhtml_toggle_background(GtkIMHtml *imhtml, const char *color)
-{
- return gtk_imhtml_toggle_str_tag(imhtml, color, &imhtml->edit.background,
- remove_font_background, find_font_background_tag,
- GTK_IMHTML_BACKGROUND);
-}
-
-gboolean gtk_imhtml_toggle_fontface(GtkIMHtml *imhtml, const char *face)
-{
- return gtk_imhtml_toggle_str_tag(imhtml, face, &imhtml->edit.fontface,
- remove_font_face, find_font_face_tag,
- GTK_IMHTML_FACE);
-}
-
-void gtk_imhtml_toggle_link(GtkIMHtml *imhtml, const char *url)
-{
- GObject *object;
- GtkTextIter start, end;
- GtkTextTag *linktag;
- static guint linkno = 0;
- gchar str[48];
- GdkColor *color = NULL;
-
- imhtml->edit.link = NULL;
-
- if (url) {
- g_snprintf(str, sizeof(str), "LINK %d", linkno++);
- str[47] = '\0';
-
- gtk_widget_style_get(GTK_WIDGET(imhtml), "hyperlink-color", &color, NULL);
- if (color) {
- imhtml->edit.link = linktag = gtk_text_buffer_create_tag(imhtml->text_buffer, str, "foreground-gdk", color, "underline", PANGO_UNDERLINE_SINGLE, NULL);
- gdk_color_free(color);
- } else {
- imhtml->edit.link = linktag = gtk_text_buffer_create_tag(imhtml->text_buffer, str, "foreground", "blue", "underline", PANGO_UNDERLINE_SINGLE, NULL);
- }
- g_object_set_data_full(G_OBJECT(linktag), "link_url", g_strdup(url), g_free);
- g_signal_connect(G_OBJECT(linktag), "event", G_CALLBACK(tag_event), NULL);
-
- if (imhtml->editable && gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, &start, &end)) {
- remove_font_link(imhtml, &start, &end, FALSE);
- gtk_text_buffer_apply_tag(imhtml->text_buffer, linktag, &start, &end);
- }
- }
-
- object = g_object_ref(G_OBJECT(imhtml));
- g_signal_emit(object, signals[TOGGLE_FORMAT], 0, GTK_IMHTML_LINK);
- g_object_unref(object);
-}
-
-void gtk_imhtml_insert_link(GtkIMHtml *imhtml, GtkTextMark *mark, const char *url, const char *text)
-{
- GtkTextIter iter;
-
- /* Delete any currently selected text */
- gtk_text_buffer_delete_selection(imhtml->text_buffer, TRUE, TRUE);
-
- gtk_imhtml_toggle_link(imhtml, url);
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, mark);
- gtk_text_buffer_insert(imhtml->text_buffer, &iter, text, -1);
- gtk_imhtml_toggle_link(imhtml, NULL);
-}
-
-void gtk_imhtml_insert_smiley(GtkIMHtml *imhtml, const char *sml, char *smiley)
-{
- GtkTextMark *mark;
- GtkTextIter iter;
-
- /* Delete any currently selected text */
- gtk_text_buffer_delete_selection(imhtml->text_buffer, TRUE, TRUE);
-
- mark = gtk_text_buffer_get_insert(imhtml->text_buffer);
-
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, mark);
- gtk_text_buffer_begin_user_action(imhtml->text_buffer);
- gtk_imhtml_insert_smiley_at_iter(imhtml, sml, smiley, &iter);
- gtk_text_buffer_end_user_action(imhtml->text_buffer);
-}
-
-static gboolean
-image_expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
-{
- GTK_WIDGET_CLASS(GTK_WIDGET_GET_CLASS(widget))->expose_event(widget, event);
-
- return TRUE;
-}
-
-/* In case the smiley gets removed from the imhtml before it gets removed from the queue */
-static void animated_smiley_destroy_cb(GtkObject *widget, GtkIMHtml *imhtml)
-{
- GList *l = imhtml->animations->head;
- while (l) {
- GList *next = l->next;
- if (l->data == widget) {
- if (l == imhtml->animations->tail)
- imhtml->animations->tail = imhtml->animations->tail->prev;
- imhtml->animations->head = g_list_delete_link(imhtml->animations->head, l);
- imhtml->num_animations--;
- }
- l = next;
- }
-}
-
-void gtk_imhtml_insert_smiley_at_iter(GtkIMHtml *imhtml, const char *sml, char *smiley, GtkTextIter *iter)
-{
- GdkPixbuf *pixbuf = NULL;
- GdkPixbufAnimation *annipixbuf = NULL;
- GtkWidget *icon = NULL;
- GtkTextChildAnchor *anchor = NULL;
- char *unescaped;
- GtkIMHtmlSmiley *imhtml_smiley;
- GtkWidget *ebox = NULL;
- int numsmileys_thismsg, numsmileys_total;
-
- /*
- * This GtkIMHtml has the maximum number of smileys allowed, so don't
- * add any more. We do this for performance reasons, because smileys
- * are apparently pretty inefficient. Hopefully we can remove this
- * restriction when we're using a better HTML widget.
- */
- unescaped = purple_unescape_html(smiley);
- numsmileys_thismsg = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(imhtml), "gtkimhtml_numsmileys_thismsg"));
- if (numsmileys_thismsg >= 30) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, unescaped, -1);
- g_free(unescaped);
- return;
- }
- numsmileys_total = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(imhtml), "gtkimhtml_numsmileys_total"));
- if (numsmileys_total >= 300) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, unescaped, -1);
- g_free(unescaped);
- return;
- }
-
- imhtml_smiley = gtk_imhtml_smiley_get(imhtml, sml, unescaped);
-
- if (imhtml->format_functions & GTK_IMHTML_SMILEY) {
- annipixbuf = imhtml_smiley ? gtk_smiley_get_image(imhtml_smiley) : NULL;
- if (annipixbuf) {
- if (gdk_pixbuf_animation_is_static_image(annipixbuf)) {
- pixbuf = gdk_pixbuf_animation_get_static_image(annipixbuf);
- if (pixbuf)
- icon = gtk_image_new_from_pixbuf(pixbuf);
- } else {
- icon = gtk_image_new_from_animation(annipixbuf);
- if (imhtml->num_animations == 20) {
- GtkImage *image = GTK_IMAGE(g_queue_pop_head(imhtml->animations));
- GdkPixbufAnimation *anim = gtk_image_get_animation(image);
- g_signal_handlers_disconnect_matched(G_OBJECT(image), G_SIGNAL_MATCH_FUNC,
- 0, 0, NULL, G_CALLBACK(animated_smiley_destroy_cb), NULL);
- if (anim) {
- GdkPixbuf *pb = gdk_pixbuf_animation_get_static_image(anim);
- if (pb != NULL) {
- GdkPixbuf *copy = gdk_pixbuf_copy(pb);
- gtk_image_set_from_pixbuf(image, copy);
- g_object_unref(G_OBJECT(copy));
- }
- }
- } else {
- imhtml->num_animations++;
- }
- g_signal_connect(G_OBJECT(icon), "destroy", G_CALLBACK(animated_smiley_destroy_cb), imhtml);
- g_queue_push_tail(imhtml->animations, icon);
- }
- }
- }
-
- if (imhtml_smiley && imhtml_smiley->flags & GTK_IMHTML_SMILEY_CUSTOM) {
- ebox = gtk_event_box_new();
- gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE);
- gtk_widget_show(ebox);
- }
-
- if (icon) {
- anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter);
- g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_plaintext", g_strdup(unescaped), g_free);
- g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_tiptext", g_strdup(unescaped), g_free);
- g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_htmltext", g_strdup(smiley), g_free);
-
- /* This catches the expose events generated by animated
- * images, and ensures that they are handled by the image
- * itself, without propagating to the textview and causing
- * a complete refresh */
- g_signal_connect(G_OBJECT(icon), "expose-event", G_CALLBACK(image_expose), NULL);
-
- gtk_widget_show(icon);
- if (ebox)
- gtk_container_add(GTK_CONTAINER(ebox), icon);
- gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), ebox ? ebox : icon, anchor);
-
- g_object_set_data(G_OBJECT(imhtml), "gtkimhtml_numsmileys_thismsg", GINT_TO_POINTER(numsmileys_thismsg + 1));
- g_object_set_data(G_OBJECT(imhtml), "gtkimhtml_numsmileys_total", GINT_TO_POINTER(numsmileys_total + 1));
- } else if (imhtml_smiley != NULL && (imhtml->format_functions & GTK_IMHTML_SMILEY)) {
- anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter);
- imhtml_smiley->anchors = g_slist_append(imhtml_smiley->anchors, g_object_ref(anchor));
- if (ebox) {
- GtkWidget *img = gtk_image_new_from_stock(GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_MENU);
- gtk_container_add(GTK_CONTAINER(ebox), img);
- gtk_widget_show(img);
- g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_plaintext", g_strdup(unescaped), g_free);
- g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_tiptext", g_strdup(unescaped), g_free);
- g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_htmltext", g_strdup(smiley), g_free);
- gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), ebox, anchor);
- }
-
- g_object_set_data(G_OBJECT(imhtml), "gtkimhtml_numsmileys_thismsg", GINT_TO_POINTER(numsmileys_thismsg + 1));
- g_object_set_data(G_OBJECT(imhtml), "gtkimhtml_numsmileys_total", GINT_TO_POINTER(numsmileys_total + 1));
- } else {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, unescaped, -1);
- }
-
- if (ebox) {
- g_signal_connect(G_OBJECT(ebox), "event", G_CALLBACK(gtk_imhtml_smiley_clicked), imhtml_smiley);
- }
-
- g_free(unescaped);
-}
-
-void gtk_imhtml_insert_image_at_iter(GtkIMHtml *imhtml, int id, GtkTextIter *iter)
-{
- GdkPixbufAnimation *anim = NULL;
- const char *filename = NULL;
- gpointer image;
- GdkRectangle rect;
- GtkIMHtmlScalable *scalable = NULL;
- struct scalable_data *sd;
- int minus;
-
- if (!imhtml->funcs || !imhtml->funcs->image_get ||
- !imhtml->funcs->image_get_size || !imhtml->funcs->image_get_data ||
- !imhtml->funcs->image_get_filename || !imhtml->funcs->image_ref ||
- !imhtml->funcs->image_unref)
- return;
-
- image = imhtml->funcs->image_get(id);
-
- if (image) {
- gpointer data;
- size_t len;
-
- data = imhtml->funcs->image_get_data(image);
- len = imhtml->funcs->image_get_size(image);
- if (data && len)
- anim = pidgin_pixbuf_anim_from_data(data, len);
-
- }
-
- if (anim) {
- struct im_image_data *t = g_new(struct im_image_data, 1);
- filename = imhtml->funcs->image_get_filename(image);
- imhtml->funcs->image_ref(id);
- t->id = id;
- t->mark = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, iter, TRUE);
- imhtml->im_images = g_slist_prepend(imhtml->im_images, t);
- scalable = gtk_imhtml_animation_new(anim, filename, id);
- g_object_unref(G_OBJECT(anim));
- } else {
- GdkPixbuf *pixbuf;
- pixbuf = gtk_widget_render_icon(GTK_WIDGET(imhtml), GTK_STOCK_MISSING_IMAGE,
- GTK_ICON_SIZE_BUTTON, "gtkimhtml-missing-image");
- scalable = gtk_imhtml_image_new(pixbuf, filename, id);
- g_object_unref(G_OBJECT(pixbuf));
- }
-
- sd = g_new(struct scalable_data, 1);
- sd->scalable = scalable;
- sd->mark = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, iter, TRUE);
- gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect);
- scalable->add_to(scalable, imhtml, iter);
- minus = gtk_text_view_get_left_margin(GTK_TEXT_VIEW(imhtml)) +
- gtk_text_view_get_right_margin(GTK_TEXT_VIEW(imhtml));
- scalable->scale(scalable, rect.width - minus, rect.height);
- imhtml->scalables = g_list_append(imhtml->scalables, sd);
-}
-
-static const gchar *tag_to_html_start(GtkTextTag *tag)
-{
- const gchar *name;
- static gchar buf[16384];
-
- name = tag->name;
- g_return_val_if_fail(name != NULL, "");
-
- if (strcmp(name, "BOLD") == 0) {
- return "<b>";
- } else if (strcmp(name, "ITALICS") == 0) {
- return "<i>";
- } else if (strcmp(name, "UNDERLINE") == 0) {
- return "<u>";
- } else if (strcmp(name, "STRIKE") == 0) {
- return "<s>";
- } else if (strncmp(name, "LINK ", 5) == 0) {
- char *tmp = g_object_get_data(G_OBJECT(tag), "link_url");
- if (tmp) {
- gchar *escaped = purple_markup_escape_text(tmp, -1);
- g_snprintf(buf, sizeof(buf), "<a href=\"%s\">", escaped);
- buf[sizeof(buf)-1] = '\0';
- g_free(escaped);
- return buf;
- } else {
- return "";
- }
- } else if (strncmp(name, "FORECOLOR ", 10) == 0) {
- g_snprintf(buf, sizeof(buf), "<font color=\"%s\">", &name[10]);
- return buf;
- } else if (strncmp(name, "BACKCOLOR ", 10) == 0) {
- g_snprintf(buf, sizeof(buf), "<font back=\"%s\">", &name[10]);
- return buf;
- } else if (strncmp(name, "BACKGROUND ", 10) == 0) {
- g_snprintf(buf, sizeof(buf), "<body bgcolor=\"%s\">", &name[11]);
- return buf;
- } else if (strncmp(name, "FONT FACE ", 10) == 0) {
- g_snprintf(buf, sizeof(buf), "<font face=\"%s\">", &name[10]);
- return buf;
- } else if (strncmp(name, "FONT SIZE ", 10) == 0) {
- g_snprintf(buf, sizeof(buf), "<font size=\"%s\">", &name[10]);
- return buf;
- } else {
- char *str = buf;
- gboolean isset;
- int ivalue = 0;
- GdkColor *color = NULL;
- GObject *obj = G_OBJECT(tag);
- gboolean empty = TRUE;
-
- str += g_snprintf(str, sizeof(buf) - (str - buf), "<span style='");
-
- /* Weight */
- g_object_get(obj, "weight-set", &isset, "weight", &ivalue, NULL);
- if (isset) {
- const char *weight = "";
- if (ivalue >= PANGO_WEIGHT_ULTRABOLD)
- weight = "bolder";
- else if (ivalue >= PANGO_WEIGHT_BOLD)
- weight = "bold";
- else if (ivalue >= PANGO_WEIGHT_NORMAL)
- weight = "normal";
- else
- weight = "lighter";
-
- str += g_snprintf(str, sizeof(buf) - (str - buf), "font-weight: %s;", weight);
- empty = FALSE;
- }
-
- /* Foreground color */
- g_object_get(obj, "foreground-set", &isset, "foreground-gdk", &color, NULL);
- if (isset && color) {
- str += g_snprintf(str, sizeof(buf) - (str - buf),
- "color: #%02x%02x%02x;",
- color->red >> 8, color->green >> 8, color->blue >> 8);
- empty = FALSE;
- }
- gdk_color_free(color);
-
- /* Background color */
- g_object_get(obj, "background-set", &isset, "background-gdk", &color, NULL);
- if (isset && color) {
- str += g_snprintf(str, sizeof(buf) - (str - buf),
- "background: #%02x%02x%02x;",
- color->red >> 8, color->green >> 8, color->blue >> 8);
- empty = FALSE;
- }
- gdk_color_free(color);
-
- /* Underline */
- g_object_get(obj, "underline-set", &isset, "underline", &ivalue, NULL);
- if (isset) {
- switch (ivalue) {
- case PANGO_UNDERLINE_NONE:
- case PANGO_UNDERLINE_ERROR:
- break;
- default:
- str += g_snprintf(str, sizeof(buf) - (str - buf), "text-decoration: underline;");
- empty = FALSE;
- }
- }
-
- g_snprintf(str, sizeof(buf) - (str - buf), "'>");
-
- return (empty ? "" : buf);
- }
-}
-
-static const gchar *tag_to_html_end(GtkTextTag *tag)
-{
- const gchar *name;
-
- name = tag->name;
- g_return_val_if_fail(name != NULL, "");
-
- if (strcmp(name, "BOLD") == 0) {
- return "</b>";
- } else if (strcmp(name, "ITALICS") == 0) {
- return "</i>";
- } else if (strcmp(name, "UNDERLINE") == 0) {
- return "</u>";
- } else if (strcmp(name, "STRIKE") == 0) {
- return "</s>";
- } else if (strncmp(name, "LINK ", 5) == 0) {
- return "</a>";
- } else if (strncmp(name, "FORECOLOR ", 10) == 0) {
- return "</font>";
- } else if (strncmp(name, "BACKCOLOR ", 10) == 0) {
- return "</font>";
- } else if (strncmp(name, "BACKGROUND ", 10) == 0) {
- return "</body>";
- } else if (strncmp(name, "FONT FACE ", 10) == 0) {
- return "</font>";
- } else if (strncmp(name, "FONT SIZE ", 10) == 0) {
- return "</font>";
- } else {
- const char *props[] = {"weight-set", "foreground-set", "background-set",
- "size-set", "underline-set", NULL};
- int i;
- for (i = 0; props[i]; i++) {
- gboolean set = FALSE;
- g_object_get(G_OBJECT(tag), props[i], &set, NULL);
- if (set)
- return "</span>";
- }
-
- return "";
- }
-}
-
-typedef struct {
- GtkTextTag *tag;
- char *end;
- char *start;
-} PidginTextTagData;
-
-static PidginTextTagData *text_tag_data_new(GtkTextTag *tag)
-{
- const char *start, *end;
- PidginTextTagData *ret = NULL;
-
- start = tag_to_html_start(tag);
- if (!start || !*start)
- return NULL;
- end = tag_to_html_end(tag);
- if (!end || !*end)
- return NULL;
-
- ret = g_new0(PidginTextTagData, 1);
- ret->start = g_strdup(start);
- ret->end = g_strdup(end);
- ret->tag = tag;
- return ret;
-}
-
-static void text_tag_data_destroy(PidginTextTagData *data)
-{
- g_free(data->start);
- g_free(data->end);
- g_free(data);
-}
-
-static gboolean tag_ends_here(GtkTextTag *tag, GtkTextIter *iter, GtkTextIter *niter)
-{
- return ((gtk_text_iter_has_tag(iter, GTK_TEXT_TAG(tag)) &&
- !gtk_text_iter_has_tag(niter, GTK_TEXT_TAG(tag))) ||
- gtk_text_iter_is_end(niter));
-}
-
-/* Basic notion here: traverse through the text buffer one-by-one, non-character elements, such
- * as smileys and IM images are represented by the Unicode "unknown" character. Handle them. Else
- * check for tags that are toggled on, insert their html form, and push them on the queue. Then insert
- * the actual text. Then check for tags that are toggled off and insert them, after checking the queue.
- * Finally, replace <, >, &, and " with their HTML equivalent.
- */
-char *gtk_imhtml_get_markup_range(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *end)
-{
- gunichar c;
- GtkTextIter iter, next_iter, non_neutral_iter;
- gboolean is_rtl_message = FALSE;
- GString *str = g_string_new("");
- GSList *tags, *sl;
- GQueue *q;
- GtkTextTag *tag;
- PidginTextTagData *tagdata;
-
- q = g_queue_new();
-
- gtk_text_iter_order(start, end);
- non_neutral_iter = next_iter = iter = *start;
- gtk_text_iter_forward_char(&next_iter);
-
- /* Bi-directional text support */
- /* Get to the first non-neutral character */
-#ifdef HAVE_PANGO14
- while ((PANGO_DIRECTION_NEUTRAL == pango_unichar_direction(gtk_text_iter_get_char(&non_neutral_iter)))
- && gtk_text_iter_forward_char(&non_neutral_iter));
- if (PANGO_DIRECTION_RTL == pango_unichar_direction(gtk_text_iter_get_char(&non_neutral_iter))) {
- is_rtl_message = TRUE;
- g_string_append(str, "<SPAN style=\"direction:rtl;text-align:right;\">");
- }
-#endif
-
- /* First add the tags that are already in progress (we don't care about non-printing tags)*/
- tags = gtk_text_iter_get_tags(start);
-
- for (sl = tags; sl; sl = sl->next) {
- tag = sl->data;
- if (!gtk_text_iter_toggles_tag(start, GTK_TEXT_TAG(tag))) {
- PidginTextTagData *data = text_tag_data_new(tag);
- if (data) {
- g_string_append(str, data->start);
- g_queue_push_tail(q, data);
- }
- }
- }
- g_slist_free(tags);
-
- while ((c = gtk_text_iter_get_char(&iter)) != 0 && !gtk_text_iter_equal(&iter, end)) {
-
- tags = gtk_text_iter_get_tags(&iter);
-
- for (sl = tags; sl; sl = sl->next) {
- tag = sl->data;
- if (gtk_text_iter_begins_tag(&iter, GTK_TEXT_TAG(tag))) {
- PidginTextTagData *data = text_tag_data_new(tag);
- if (data) {
- g_string_append(str, data->start);
- g_queue_push_tail(q, data);
- }
- }
- }
-
- if (c == 0xFFFC) {
- GtkTextChildAnchor* anchor = gtk_text_iter_get_child_anchor(&iter);
- if (anchor) {
- char *text = g_object_get_data(G_OBJECT(anchor), "gtkimhtml_htmltext");
- if (text)
- str = g_string_append(str, text);
- }
- } else if (c == '<') {
- str = g_string_append(str, "&lt;");
- } else if (c == '>') {
- str = g_string_append(str, "&gt;");
- } else if (c == '&') {
- str = g_string_append(str, "&amp;");
- } else if (c == '"') {
- str = g_string_append(str, "&quot;");
- } else if (c == '\n') {
- str = g_string_append(str, "<br>");
- } else {
- str = g_string_append_unichar(str, c);
- }
-
- tags = g_slist_reverse(tags);
- for (sl = tags; sl; sl = sl->next) {
- tag = sl->data;
- /** don't worry about non-printing tags ending */
- if (tag_ends_here(tag, &iter, &next_iter) &&
- strlen(tag_to_html_end(tag)) > 0 &&
- strlen(tag_to_html_start(tag)) > 0) {
-
- PidginTextTagData *tmp;
- GQueue *r = g_queue_new();
-
- while ((tmp = g_queue_pop_tail(q)) && tmp->tag != tag) {
- g_string_append(str, tmp->end);
- if (!tag_ends_here(tmp->tag, &iter, &next_iter))
- g_queue_push_tail(r, tmp);
- else
- text_tag_data_destroy(tmp);
- }
-
- if (tmp != NULL) {
- g_string_append(str, tmp->end);
- text_tag_data_destroy(tmp);
- }
-#if 0 /* This can't be allowed to happen because it causes the iters to be invalidated in the debug window imhtml during text copying */
- else
- purple_debug_warning("gtkimhtml", "empty queue, more closing tags than open tags!\n");
-#endif
-
- while ((tmp = g_queue_pop_head(r))) {
- g_string_append(str, tmp->start);
- g_queue_push_tail(q, tmp);
- }
- g_queue_free(r);
- }
- }
-
- g_slist_free(tags);
- gtk_text_iter_forward_char(&iter);
- gtk_text_iter_forward_char(&next_iter);
- }
-
- while ((tagdata = g_queue_pop_tail(q))) {
- g_string_append(str, tagdata->end);
- text_tag_data_destroy(tagdata);
- }
-
- /* Bi-directional text support - close tags */
- if (is_rtl_message)
- g_string_append(str, "</SPAN>");
-
- g_queue_free(q);
- return g_string_free(str, FALSE);
-}
-
-void gtk_imhtml_close_tags(GtkIMHtml *imhtml, GtkTextIter *iter)
-{
- if (imhtml->edit.bold)
- gtk_imhtml_toggle_bold(imhtml);
-
- if (imhtml->edit.italic)
- gtk_imhtml_toggle_italic(imhtml);
-
- if (imhtml->edit.underline)
- gtk_imhtml_toggle_underline(imhtml);
-
- if (imhtml->edit.strike)
- gtk_imhtml_toggle_strike(imhtml);
-
- if (imhtml->edit.forecolor)
- gtk_imhtml_toggle_forecolor(imhtml, NULL);
-
- if (imhtml->edit.backcolor)
- gtk_imhtml_toggle_backcolor(imhtml, NULL);
-
- if (imhtml->edit.fontface)
- gtk_imhtml_toggle_fontface(imhtml, NULL);
-
- imhtml->edit.fontsize = 0;
-
- if (imhtml->edit.link)
- gtk_imhtml_toggle_link(imhtml, NULL);
-}
-
-char *gtk_imhtml_get_markup(GtkIMHtml *imhtml)
-{
- GtkTextIter start, end;
-
- gtk_text_buffer_get_start_iter(imhtml->text_buffer, &start);
- gtk_text_buffer_get_end_iter(imhtml->text_buffer, &end);
- return gtk_imhtml_get_markup_range(imhtml, &start, &end);
-}
-
-char **gtk_imhtml_get_markup_lines(GtkIMHtml *imhtml)
-{
- int i, j, lines;
- GtkTextIter start, end;
- char **ret;
-
- lines = gtk_text_buffer_get_line_count(imhtml->text_buffer);
- ret = g_new0(char *, lines + 1);
- gtk_text_buffer_get_start_iter(imhtml->text_buffer, &start);
- end = start;
- gtk_text_iter_forward_to_line_end(&end);
-
- for (i = 0, j = 0; i < lines; i++) {
- if (gtk_text_iter_get_char(&start) != '\n') {
- ret[j] = gtk_imhtml_get_markup_range(imhtml, &start, &end);
- if (ret[j] != NULL)
- j++;
- }
-
- gtk_text_iter_forward_line(&start);
- end = start;
- gtk_text_iter_forward_to_line_end(&end);
- }
-
- return ret;
-}
-
-char *gtk_imhtml_get_text(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *stop)
-{
- GString *str = g_string_new("");
- GtkTextIter iter, end;
- gunichar c;
-
- if (start == NULL)
- gtk_text_buffer_get_start_iter(imhtml->text_buffer, &iter);
- else
- iter = *start;
-
- if (stop == NULL)
- gtk_text_buffer_get_end_iter(imhtml->text_buffer, &end);
- else
- end = *stop;
-
- gtk_text_iter_order(&iter, &end);
-
- while ((c = gtk_text_iter_get_char(&iter)) != 0 && !gtk_text_iter_equal(&iter, &end)) {
- if (c == 0xFFFC) {
- GtkTextChildAnchor* anchor;
- char *text = NULL;
-
- anchor = gtk_text_iter_get_child_anchor(&iter);
- if (anchor)
- text = g_object_get_data(G_OBJECT(anchor), "gtkimhtml_plaintext");
- if (text)
- str = g_string_append(str, text);
- } else {
- g_string_append_unichar(str, c);
- }
- gtk_text_iter_forward_char(&iter);
- }
-
- return g_string_free(str, FALSE);
-}
-
-void gtk_imhtml_set_funcs(GtkIMHtml *imhtml, GtkIMHtmlFuncs *f)
-{
- g_return_if_fail(imhtml != NULL);
- imhtml->funcs = f;
-}
-
-void gtk_imhtml_setup_entry(GtkIMHtml *imhtml, PurpleConnectionFlags flags)
-{
- GtkIMHtmlButtons buttons;
-
- if (flags & PURPLE_CONNECTION_HTML) {
- char color[8];
- GdkColor fg_color, bg_color;
-
- buttons = GTK_IMHTML_ALL;
-
- if (flags & PURPLE_CONNECTION_NO_BGCOLOR)
- buttons &= ~GTK_IMHTML_BACKCOLOR;
- if (flags & PURPLE_CONNECTION_NO_FONTSIZE)
- {
- buttons &= ~GTK_IMHTML_GROW;
- buttons &= ~GTK_IMHTML_SHRINK;
- }
- if (flags & PURPLE_CONNECTION_NO_URLDESC)
- buttons &= ~GTK_IMHTML_LINKDESC;
-
- gtk_imhtml_set_format_functions(imhtml, GTK_IMHTML_ALL);
- if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_bold") != imhtml->edit.bold)
- gtk_imhtml_toggle_bold(imhtml);
-
- if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic") != imhtml->edit.italic)
- gtk_imhtml_toggle_italic(imhtml);
-
- if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline") != imhtml->edit.underline)
- gtk_imhtml_toggle_underline(imhtml);
-
- gtk_imhtml_toggle_fontface(imhtml,
- purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/font_face"));
-
- if (!(flags & PURPLE_CONNECTION_NO_FONTSIZE))
- {
- int size = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/font_size");
-
- /* 3 is the default. */
- if (size != 3)
- gtk_imhtml_font_set_size(imhtml, size);
- }
-
- if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"), "") != 0)
- {
- gdk_color_parse(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"),
- &fg_color);
- g_snprintf(color, sizeof(color), "#%02x%02x%02x",
- fg_color.red / 256,
- fg_color.green / 256,
- fg_color.blue / 256);
- } else
- strcpy(color, "");
-
- gtk_imhtml_toggle_forecolor(imhtml, color);
-
- if(!(flags & PURPLE_CONNECTION_NO_BGCOLOR) &&
- strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"), "") != 0)
- {
- gdk_color_parse(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"),
- &bg_color);
- g_snprintf(color, sizeof(color), "#%02x%02x%02x",
- bg_color.red / 256,
- bg_color.green / 256,
- bg_color.blue / 256);
- } else
- strcpy(color, "");
-
- gtk_imhtml_toggle_background(imhtml, color);
-
- if (flags & PURPLE_CONNECTION_FORMATTING_WBFO)
- gtk_imhtml_set_whole_buffer_formatting_only(imhtml, TRUE);
- else
- gtk_imhtml_set_whole_buffer_formatting_only(imhtml, FALSE);
- } else {
- buttons = GTK_IMHTML_SMILEY | GTK_IMHTML_IMAGE;
- imhtml_clear_formatting(imhtml);
- }
-
- if (flags & PURPLE_CONNECTION_NO_IMAGES)
- buttons &= ~GTK_IMHTML_IMAGE;
-
- if (flags & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY)
- buttons |= GTK_IMHTML_CUSTOM_SMILEY;
- else
- buttons &= ~GTK_IMHTML_CUSTOM_SMILEY;
-
- gtk_imhtml_set_format_functions(imhtml, buttons);
-}
-
-/*******
- * GtkIMHtmlSmiley functions
- *******/
-static void gtk_custom_smiley_allocated(GdkPixbufLoader *loader, gpointer user_data)
-{
- GtkIMHtmlSmiley *smiley;
-
- smiley = (GtkIMHtmlSmiley *)user_data;
- smiley->icon = gdk_pixbuf_loader_get_animation(loader);
-
- if (smiley->icon)
- g_object_ref(G_OBJECT(smiley->icon));
-#ifdef DEBUG_CUSTOM_SMILEY
- purple_debug_info("custom-smiley", "gtk_custom_smiley_allocated(): got GdkPixbufAnimation %p for smiley '%s'\n", smiley->icon, smiley->smile);
-#endif
-}
-
-static void gtk_custom_smiley_closed(GdkPixbufLoader *loader, gpointer user_data)
-{
- GtkIMHtmlSmiley *smiley;
- GtkWidget *icon = NULL;
- GtkTextChildAnchor *anchor = NULL;
- GSList *current = NULL;
-
- smiley = (GtkIMHtmlSmiley *)user_data;
- if (!smiley->imhtml) {
-#ifdef DEBUG_CUSTOM_SMILEY
- purple_debug_error("custom-smiley", "gtk_custom_smiley_closed(): orphan smiley found: %p\n", smiley);
-#endif
- g_object_unref(G_OBJECT(loader));
- smiley->loader = NULL;
- return;
- }
-
- for (current = smiley->anchors; current; current = g_slist_next(current)) {
- anchor = GTK_TEXT_CHILD_ANCHOR(current->data);
- if (gtk_text_child_anchor_get_deleted(anchor))
- icon = NULL;
- else
- icon = gtk_image_new_from_animation(smiley->icon);
-
-#ifdef DEBUG_CUSTOM_SMILEY
- purple_debug_info("custom-smiley", "gtk_custom_smiley_closed(): got GtkImage %p from GtkPixbufAnimation %p for smiley '%s'\n",
- icon, smiley->icon, smiley->smile);
-#endif
- if (icon) {
- GList *wids;
- gtk_widget_show(icon);
-
- wids = gtk_text_child_anchor_get_widgets(anchor);
-
- g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_plaintext", purple_unescape_html(smiley->smile), g_free);
- g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_htmltext", g_strdup(smiley->smile), g_free);
-
- if (smiley->imhtml) {
- if (wids) {
- GList *children = gtk_container_get_children(GTK_CONTAINER(wids->data));
- g_list_foreach(children, (GFunc)gtk_widget_destroy, NULL);
- g_list_free(children);
- gtk_container_add(GTK_CONTAINER(wids->data), icon);
- } else
- gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(smiley->imhtml), icon, anchor);
- }
- g_list_free(wids);
- }
- g_object_unref(anchor);
- }
-
- g_slist_free(smiley->anchors);
- smiley->anchors = NULL;
-
- g_object_unref(G_OBJECT(loader));
- smiley->loader = NULL;
-}
-
-static void
-gtk_custom_smiley_size_prepared(GdkPixbufLoader *loader, gint width, gint height, gpointer data)
-{
- if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/resize_custom_smileys")) {
- int custom_smileys_size = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/custom_smileys_size");
- if (width <= custom_smileys_size && height <= custom_smileys_size)
- return;
-
- if (width >= height) {
- height = height * custom_smileys_size / width;
- width = custom_smileys_size;
- } else {
- width = width * custom_smileys_size / height;
- height = custom_smileys_size;
- }
- }
- gdk_pixbuf_loader_set_size(loader, width, height);
-}
-
-void
-gtk_imhtml_smiley_reload(GtkIMHtmlSmiley *smiley)
-{
- if (smiley->icon)
- g_object_unref(smiley->icon);
- if (smiley->loader)
- g_object_unref(smiley->loader); /* XXX: does this crash? */
-
- smiley->icon = NULL;
- smiley->loader = NULL;
-
- if (smiley->file) {
- /* We do not use the pixbuf loader for a smiley that can be loaded
- * from a file. (e.g., local custom smileys)
- */
- return;
- }
-
- smiley->loader = gdk_pixbuf_loader_new();
-
- g_signal_connect(smiley->loader, "area_prepared", G_CALLBACK(gtk_custom_smiley_allocated), smiley);
- g_signal_connect(smiley->loader, "closed", G_CALLBACK(gtk_custom_smiley_closed), smiley);
- g_signal_connect(smiley->loader, "size_prepared", G_CALLBACK(gtk_custom_smiley_size_prepared), smiley);
-}
-
-GtkIMHtmlSmiley *gtk_imhtml_smiley_create(const char *file, const char *shortcut, gboolean hide,
- GtkIMHtmlSmileyFlags flags)
-{
- GtkIMHtmlSmiley *smiley = g_new0(GtkIMHtmlSmiley, 1);
- smiley->file = g_strdup(file);
- smiley->smile = g_strdup(shortcut);
- smiley->hidden = hide;
- smiley->flags = flags;
- smiley->imhtml = NULL;
- gtk_imhtml_smiley_reload(smiley);
- return smiley;
-}
-
-void gtk_imhtml_smiley_destroy(GtkIMHtmlSmiley *smiley)
-{
- gtk_imhtml_disassociate_smiley(smiley);
- g_free(smiley->smile);
- g_free(smiley->file);
- if (smiley->icon)
- g_object_unref(smiley->icon);
- if (smiley->loader)
- g_object_unref(smiley->loader);
- g_free(smiley->data);
- g_free(smiley);
-}
-
-gboolean gtk_imhtml_class_register_protocol(const char *name,
- gboolean (*activate)(GtkIMHtml *imhtml, GtkIMHtmlLink *link),
- gboolean (*context_menu)(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu))
-{
- GtkIMHtmlClass *klass;
- GtkIMHtmlProtocol *proto;
-
- g_return_val_if_fail(name, FALSE);
-
- klass = g_type_class_ref(GTK_TYPE_IMHTML);
- g_return_val_if_fail(klass, FALSE);
-
- if ((proto = imhtml_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(GtkIMHtmlProtocol, 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;
-}
-
-static void
-gtk_imhtml_activate_tag(GtkIMHtml *imhtml, GtkTextTag *tag)
-{
- /* A link was clicked--we emit the "url_clicked" signal
- * with the URL as the argument */
- g_object_ref(G_OBJECT(tag));
- g_signal_emit(imhtml, signals[URL_CLICKED], 0, g_object_get_data(G_OBJECT(tag), "link_url"));
- g_object_unref(G_OBJECT(tag));
- g_object_set_data(G_OBJECT(tag), "visited", GINT_TO_POINTER(TRUE));
- gtk_imhtml_set_link_color(GTK_IMHTML(imhtml), tag);
-}
-
-gboolean gtk_imhtml_link_activate(GtkIMHtmlLink *link)
-{
- g_return_val_if_fail(link, FALSE);
-
- if (link->tag) {
- gtk_imhtml_activate_tag(link->imhtml, link->tag);
- } else if (link->url) {
- g_signal_emit(link->imhtml, signals[URL_CLICKED], 0, link->url);
- } else
- return FALSE;
- return TRUE;
-}
-
-const char *gtk_imhtml_link_get_url(GtkIMHtmlLink *link)
-{
- return link->url;
-}
-
-const GtkTextTag * gtk_imhtml_link_get_text_tag(GtkIMHtmlLink *link)
-{
- return link->tag;
-}
-
-static gboolean return_add_newline_cb(GtkWidget *widget, gpointer data)
-{
- GtkTextBuffer *buffer;
- GtkTextMark *mark;
- GtkTextIter iter;
-
- buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
-
- /* Delete any currently selected text */
- gtk_text_buffer_delete_selection(buffer, TRUE, TRUE);
-
- /* Insert a newline at the current cursor position */
- mark = gtk_text_buffer_get_insert(buffer);
- gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
- gtk_imhtml_insert_html_at_iter(GTK_IMHTML(widget), "\n", 0, &iter);
-
- /*
- * If we just newlined ourselves past the end of the visible area
- * then scroll down so the cursor is in view.
- */
- gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(widget),
- gtk_text_buffer_get_insert(buffer),
- 0, FALSE, 0.0, 0.0);
-
- return TRUE;
-}
-
-/*
- * It's kind of a pain that we need this function and the above just
- * to reinstate the default GtkTextView behavior. It might be better
- * if GtkIMHtml didn't intercept the enter key and just required the
- * application to deal with it--it's really not much more work than it
- * is to connect to the current "message_send" signal.
- */
-void gtk_imhtml_set_return_inserts_newline(GtkIMHtml *imhtml)
-{
- g_signal_connect(G_OBJECT(imhtml), "message_send",
- G_CALLBACK(return_add_newline_cb), NULL);
-}
-
-void gtk_imhtml_set_populate_primary_clipboard(GtkIMHtml *imhtml, gboolean populate)
-{
- gulong signal_id;
- signal_id = g_signal_handler_find(imhtml->text_buffer,
- G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_UNBLOCKED, 0, 0, NULL,
- mark_set_so_update_selection_cb, NULL);
- if (populate) {
- if (!signal_id) {
- /* We didn't find an unblocked signal handler, which means there
- is a blocked handler. Now unblock it.
- This is necessary to avoid a mutex-lock when the debug message
- saying 'no handler is blocked' is printed in the debug window.
- -- sad
- */
- g_signal_handlers_unblock_matched(imhtml->text_buffer,
- G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
- mark_set_so_update_selection_cb, NULL);
- }
- } else {
- /* Block only if we found an unblocked handler */
- if (signal_id)
- g_signal_handler_block(imhtml->text_buffer, signal_id);
- }
-}
diff --git a/pidgin/gtkimhtml.h b/pidgin/gtkimhtml.h
deleted file mode 100644
index 16c9709235..0000000000
--- a/pidgin/gtkimhtml.h
+++ /dev/null
@@ -1,983 +0,0 @@
-/**
- * @file gtkimhtml.h GTK+ IM/HTML rendering component
- * @ingroup pidgin
- * @see @ref gtkimhtml-signals
- */
-
-/* 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 _PIDGINIMHTML_H_
-#define _PIDGINIMHTML_H_
-
-#include <gdk/gdk.h>
-#include <gtk/gtk.h>
-#include "gtksourceundomanager.h"
-
-#include "connection.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**************************************************************************
- * @name Structures
- **************************************************************************/
-/*@{*/
-
-#define GTK_TYPE_IMHTML (gtk_imhtml_get_type())
-#define GTK_IMHTML(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_IMHTML, GtkIMHtml))
-#define GTK_IMHTML_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_IMHTML, GtkIMHtmlClass))
-#define GTK_IS_IMHTML(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_IMHTML))
-#define GTK_IS_IMHTML_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_IMHTML))
-#define GTK_IMHTML_SCALABLE(obj) ((GtkIMHtmlScalable *)obj)
-#define GTK_IMHTML_ANIMATION(obj) ((GtkIMHtmlAnimation *)obj)
-
-typedef struct _GtkIMHtml GtkIMHtml;
-typedef struct _GtkIMHtmlClass GtkIMHtmlClass;
-#if !(defined PIDGIN_DISABLE_DEPRECATED) && !(defined _PIDGIN_GTKIMHTML_C_)
-typedef struct _GtkIMHtmlFontDetail GtkIMHtmlFontDetail; /* The five elements contained in a FONT tag */
-#endif
-typedef struct _GtkSmileyTree GtkSmileyTree;
-typedef struct _GtkIMHtmlSmiley GtkIMHtmlSmiley;
-typedef struct _GtkIMHtmlScalable GtkIMHtmlScalable;
-typedef struct _GtkIMHtmlImage GtkIMHtmlImage;
-typedef struct _GtkIMHtmlAnimation GtkIMHtmlAnimation;
-typedef struct _GtkIMHtmlHr GtkIMHtmlHr;
-typedef struct _GtkIMHtmlFuncs GtkIMHtmlFuncs;
-
-/**
- * @since 2.6.0
- */
-typedef struct _GtkIMHtmlLink GtkIMHtmlLink;
-
-typedef enum {
- GTK_IMHTML_BOLD = 1 << 0,
- GTK_IMHTML_ITALIC = 1 << 1,
- GTK_IMHTML_UNDERLINE = 1 << 2,
- GTK_IMHTML_GROW = 1 << 3,
- GTK_IMHTML_SHRINK = 1 << 4,
- GTK_IMHTML_FACE = 1 << 5,
- GTK_IMHTML_FORECOLOR = 1 << 6,
- GTK_IMHTML_BACKCOLOR = 1 << 7,
- GTK_IMHTML_BACKGROUND = 1 << 8,
- GTK_IMHTML_LINK = 1 << 9,
- GTK_IMHTML_IMAGE = 1 << 10,
- GTK_IMHTML_SMILEY = 1 << 11,
- GTK_IMHTML_LINKDESC = 1 << 12,
- GTK_IMHTML_STRIKE = 1 << 13,
- /** Show custom smileys when appropriate. @since 2.5.0 */
- GTK_IMHTML_CUSTOM_SMILEY = 1 << 14,
- GTK_IMHTML_ALL = -1
-} GtkIMHtmlButtons;
-
-typedef enum {
- GTK_IMHTML_SMILEY_CUSTOM = 1 << 0
-} GtkIMHtmlSmileyFlags;
-
-struct _GtkIMHtml {
- GtkTextView text_view;
- GtkTextBuffer *text_buffer;
- GdkCursor *hand_cursor;
- GdkCursor *arrow_cursor;
- GdkCursor *text_cursor;
- GHashTable *smiley_data;
- GtkSmileyTree *default_smilies;
- char *protocol_name;
- guint scroll_src;
- GTimer *scroll_time;
- GQueue *animations;
- int num_animations;
-
- gboolean show_comments;
-
- GtkWidget *tip_window;
- char *tip;
- guint tip_timer;
- GtkTextTag *prelit_tag;
-
- GList *scalables;
- GdkRectangle old_rect;
-
- gchar *search_string;
-
- gboolean editable;
- GtkIMHtmlButtons format_functions;
- gboolean wbfo; /* Whole buffer formatting only. */
-
- gint insert_offset;
-
- struct {
- gboolean bold:1;
- gboolean italic:1;
- gboolean underline:1;
- gboolean strike:1;
- gchar *forecolor;
- gchar *backcolor;
- gchar *background;
- gchar *fontface;
- int fontsize;
- GtkTextTag *link;
- } edit;
-
-#if !(defined PIDGIN_DISABLE_DEPRECATED) || (defined _PIDGIN_GTKIMHTML_C_)
- /** @deprecated */
- char *clipboard_text_string;
- /** @deprecated */
- char *clipboard_html_string;
-#else
- char *depr1;
- char *depr2;
-#endif
-
- GSList *im_images;
- GtkIMHtmlFuncs *funcs;
- GtkSourceUndoManager *undo_manager;
-};
-
-struct _GtkIMHtmlClass {
- GtkTextViewClass parent_class;
-
- void (*url_clicked)(GtkIMHtml *, const gchar *);
- void (*buttons_update)(GtkIMHtml *, GtkIMHtmlButtons);
- void (*toggle_format)(GtkIMHtml *, GtkIMHtmlButtons);
- void (*clear_format)(GtkIMHtml *);
- void (*update_format)(GtkIMHtml *);
- gboolean (*message_send)(GtkIMHtml *);
- void (*undo)(GtkIMHtml *);
- void (*redo)(GtkIMHtml *);
- GList *protocols; /* List of GtkIMHtmlProtocol's */
-};
-
-#if !(defined PIDGIN_DISABLE_DEPRECATED) && !(defined _PIDGIN_GTKIMHTML_C_)
-/** @deprecated as of 2.7.10 */
-struct _GtkIMHtmlFontDetail {
- gushort size;
- gchar *face;
- gchar *fore;
- gchar *back;
- gchar *bg;
- gchar *sml;
- gboolean underline;
- gshort bold;
-};
-#endif
-
-struct _GtkSmileyTree {
- GString *values;
- GtkSmileyTree **children;
- GtkIMHtmlSmiley *image;
-};
-
-struct _GtkIMHtmlSmiley {
- gchar *smile;
- gchar *file;
- GdkPixbufAnimation *icon;
- gboolean hidden;
- GdkPixbufLoader *loader;
- GSList *anchors;
- GtkIMHtmlSmileyFlags flags;
- GtkIMHtml *imhtml;
- gpointer data; /** @since 2.6.0 */
- gsize datasize; /** @since 2.6.0 */
-};
-
-struct _GtkIMHtmlScalable {
- void (*scale)(struct _GtkIMHtmlScalable *, int, int);
- void (*add_to)(struct _GtkIMHtmlScalable *, GtkIMHtml *, GtkTextIter *);
- void (*free)(struct _GtkIMHtmlScalable *);
-};
-
-struct _GtkIMHtmlImage {
- GtkIMHtmlScalable scalable;
- GtkImage *image; /**< Contains the scaled version of this pixbuf. */
- GdkPixbuf *pixbuf; /**< The original pixbuf, before any scaling. */
- GtkTextMark *mark;
- gchar *filename;
- int width;
- int height;
- int id;
- GtkWidget *filesel;
-};
-
-struct _GtkIMHtmlAnimation {
- GtkIMHtmlImage imhtmlimage;
- GdkPixbufAnimation *anim; /**< The original animation, before any scaling. */
- GdkPixbufAnimationIter *iter;
- guint timer;
-};
-
-struct _GtkIMHtmlHr {
- GtkIMHtmlScalable scalable;
- GtkWidget *sep;
-};
-
-typedef enum {
- GTK_IMHTML_NO_COLOURS = 1 << 0,
- GTK_IMHTML_NO_FONTS = 1 << 1,
- GTK_IMHTML_NO_COMMENTS = 1 << 2, /* Remove */
- GTK_IMHTML_NO_TITLE = 1 << 3,
- GTK_IMHTML_NO_NEWLINE = 1 << 4,
- GTK_IMHTML_NO_SIZES = 1 << 5,
- GTK_IMHTML_NO_SCROLL = 1 << 6,
- GTK_IMHTML_RETURN_LOG = 1 << 7,
- GTK_IMHTML_USE_POINTSIZE = 1 << 8,
- GTK_IMHTML_NO_FORMATTING = 1 << 9,
- GTK_IMHTML_USE_SMOOTHSCROLLING = 1 << 10,
- GTK_IMHTML_NO_SMILEY = 1 << 11
-} GtkIMHtmlOptions;
-
-enum {
- GTK_IMHTML_DRAG_URL = 0,
- GTK_IMHTML_DRAG_HTML,
- GTK_IMHTML_DRAG_UTF8_STRING,
- GTK_IMHTML_DRAG_COMPOUND_TEXT,
- GTK_IMHTML_DRAG_STRING,
- GTK_IMHTML_DRAG_TEXT,
- GTK_IMHTML_DRAG_NUM
-};
-
-#define GTK_IMHTML_DND_TARGETS \
- { "text/uri-list", 0, GTK_IMHTML_DRAG_URL }, \
- { "_NETSCAPE_URL", 0, GTK_IMHTML_DRAG_URL }, \
- { "text/html", 0, GTK_IMHTML_DRAG_HTML }, \
- { "x-url/ftp", 0, GTK_IMHTML_DRAG_URL }, \
- { "x-url/http", 0, GTK_IMHTML_DRAG_URL }, \
- { "UTF8_STRING", 0, GTK_IMHTML_DRAG_UTF8_STRING }, \
- { "COMPOUND_TEXT", 0, GTK_IMHTML_DRAG_COMPOUND_TEXT }, \
- { "STRING", 0, GTK_IMHTML_DRAG_STRING }, \
- { "text/plain", 0, GTK_IMHTML_DRAG_TEXT }, \
- { "TEXT", 0, GTK_IMHTML_DRAG_TEXT }
-
-typedef gpointer (*GtkIMHtmlGetImageFunc) (int id);
-typedef gpointer (*GtkIMHtmlGetImageDataFunc) (gpointer i);
-typedef size_t (*GtkIMHtmlGetImageSizeFunc) (gpointer i);
-typedef const char *(*GtkIMHtmlGetImageFilenameFunc)(gpointer i);
-typedef void (*GtkIMHtmlImageRefFunc) (int id);
-typedef void (*GtkIMHtmlImageUnrefFunc) (int id);
-
-struct _GtkIMHtmlFuncs {
- GtkIMHtmlGetImageFunc image_get;
- GtkIMHtmlGetImageDataFunc image_get_data;
- GtkIMHtmlGetImageSizeFunc image_get_size;
- GtkIMHtmlGetImageFilenameFunc image_get_filename;
- GtkIMHtmlImageRefFunc image_ref;
- GtkIMHtmlImageUnrefFunc image_unref;
-};
-
-/*@}*/
-
-/**************************************************************************
- * @name GTK+ IM/HTML rendering component API
- **************************************************************************/
-/*@{*/
-
-/**
- * Returns the GType object for an IM/HTML widget.
- *
- * @return The GType for an IM/HTML widget.
- */
-GType gtk_imhtml_get_type(void);
-
-/**
- * Creates and returns a new GTK+ IM/HTML widget.
- *
- * @return The GTK+ IM/HTML widget created.
- */
-GtkWidget *gtk_imhtml_new(void *, void *);
-
-/**
- * Returns the smiley object associated with the text.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param sml The name of the smiley category.
- * @param text The text associated with the smiley.
- */
-
-GtkIMHtmlSmiley *gtk_imhtml_smiley_get(GtkIMHtml * imhtml,
- const gchar * sml, const gchar * text);
-
-
-/**
- * Associates a smiley with a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param sml The name of the smiley category.
- * @param smiley The GtkIMSmiley to associate.
- */
-void gtk_imhtml_associate_smiley(GtkIMHtml *imhtml, const gchar *sml, GtkIMHtmlSmiley *smiley);
-
-/**
- * Removes all smileys associated with a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_remove_smileys(GtkIMHtml *imhtml);
-
-/**
- * Sets the function callbacks to use with a GTK+ IM/HTML instance.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param f The GtkIMHTMLFuncs struct containing the functions to use.
- */
-void gtk_imhtml_set_funcs(GtkIMHtml *imhtml, GtkIMHtmlFuncs *f);
-
-/**
- * Enables or disables showing the contents of HTML comments in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param show @c TRUE if comments should be shown, or @c FALSE otherwise.
- */
-void gtk_imhtml_show_comments(GtkIMHtml *imhtml, gboolean show);
-
-/**
- * Gets the protocol name associated with this GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML
- */
-const char *gtk_imhtml_get_protocol_name(GtkIMHtml *imhtml);
-
-/**
- * Associates a protocol name with a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param protocol_name The protocol name to associate with the IM/HTML.
- */
-void gtk_imhtml_set_protocol_name(GtkIMHtml *imhtml, const gchar *protocol_name);
-
-/**
- * Appends HTML formatted text to a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param text The formatted text to append.
- * @param options A GtkIMHtmlOptions object indicating insert behavior.
- */
-#define gtk_imhtml_append_text(imhtml, text, options) \
- gtk_imhtml_append_text_with_images(imhtml, text, options, NULL)
-
-/**
- * Appends HTML formatted text to a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param text The formatted text to append.
- * @param options A GtkIMHtmlOptions object indicating insert behavior.
- * @param unused Use @c NULL value.
- */
-void gtk_imhtml_append_text_with_images(GtkIMHtml *imhtml,
- const gchar *text,
- GtkIMHtmlOptions options,
- GSList *unused);
-
-/**
- * Inserts HTML formatted text to a GTK+ IM/HTML at a given iter.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param text The formatted text to append.
- * @param options A GtkIMHtmlOptions object indicating insert behavior.
- * @param iter A GtkTextIter in the GTK+ IM/HTML at which to insert text.
- */
-void gtk_imhtml_insert_html_at_iter(GtkIMHtml *imhtml,
- const gchar *text,
- GtkIMHtmlOptions options,
- GtkTextIter *iter);
-
-/**
- * Scrolls a GTK+ IM/HTML to the end of its contents.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param smooth A boolean indicating if smooth scrolling should be used.
- */
-void gtk_imhtml_scroll_to_end(GtkIMHtml *imhtml, gboolean smooth);
-
-/**
- * Delete the contents of a GTK+ IM/HTML between start and end.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param start a postition in the imhtml's buffer
- * @param end another postition in the imhtml's buffer
- */
-void gtk_imhtml_delete(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *end);
-
-/**
- * Purges the contents from a GTK+ IM/HTML and resets formatting.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-#define gtk_imhtml_clear(imhtml) \
- gtk_imhtml_delete(imhtml, NULL, NULL)
-
-/**
- * Scrolls a GTK+ IM/HTML up by one page.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_page_up(GtkIMHtml *imhtml);
-
-/**
- * Scrolls a GTK+ IM/HTML down by one page.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_page_down(GtkIMHtml *imhtml);
-
-/**
- * Creates and returns an new GTK+ IM/HTML scalable object.
- *
- * @return A new IM/HTML Scalable object.
- */
-GtkIMHtmlScalable *gtk_imhtml_scalable_new(void);
-
-/**
- * Creates and returns a new GTK+ IM/HTML scalable object with an image.
- *
- * @param img A GdkPixbuf of the image to add.
- * @param filename The filename to associate with the image.
- * @param id The id to associate with the image.
- *
- * @return A new IM/HTML Scalable object with an image.
- */
-GtkIMHtmlScalable *gtk_imhtml_image_new(GdkPixbuf *img, const gchar *filename, int id);
-
-/**
- * Creates and returns a new GTK+ IM/HTML scalable object with an
- * animated image.
- *
- * @param img A GdkPixbufAnimation of the image to add.
- * @param filename The filename to associate with the image.
- * @param id The id to associate with the image.
- *
- * @return A new IM/HTML Scalable object with an image.
- *
- * @since 2.1.0
- */
-/*
- * TODO: All this animation code could be combined much better with
- * the image code. It couldn't be done when it was written
- * because it requires breaking backward compatibility. It
- * would be good to do it for 3.0.0.
- */
-GtkIMHtmlScalable *gtk_imhtml_animation_new(GdkPixbufAnimation *img, const gchar *filename, int id);
-
-/**
- * Destroys and frees a GTK+ IM/HTML scalable image.
- *
- * @param scale The GTK+ IM/HTML scalable.
- */
-/* TODO: Is there any reason this isn't private? */
-void gtk_imhtml_image_free(GtkIMHtmlScalable *scale);
-
-/**
- * Destroys and frees a GTK+ IM/HTML scalable animation.
- *
- * @param scale The GTK+ IM/HTML scalable.
- */
-/* TODO: Is there any reason this isn't private? */
-void gtk_imhtml_animation_free(GtkIMHtmlScalable *scale);
-
-/**
- * Rescales a GTK+ IM/HTML scalable image to a given size.
- *
- * @param scale The GTK+ IM/HTML scalable.
- * @param width The new width.
- * @param height The new height.
- */
-/* TODO: Is there any reason this isn't private? */
-void gtk_imhtml_image_scale(GtkIMHtmlScalable *scale, int width, int height);
-
-/**
- * Adds a GTK+ IM/HTML scalable image to a given GTK+ IM/HTML at a given iter.
- *
- * @param scale The GTK+ IM/HTML scalable.
- * @param imhtml The GTK+ IM/HTML.
- * @param iter The GtkTextIter at which to add the scalable.
- */
-/* TODO: Is there any reason this isn't private? */
-void gtk_imhtml_image_add_to(GtkIMHtmlScalable *scale, GtkIMHtml *imhtml, GtkTextIter *iter);
-
-/**
- * Creates and returns an new GTK+ IM/HTML scalable with a horizontal rule.
- *
- * @return A new IM/HTML Scalable object with an image.
- */
-GtkIMHtmlScalable *gtk_imhtml_hr_new(void);
-
-/**
- * Destroys and frees a GTK+ IM/HTML scalable horizontal rule.
- *
- * @param scale The GTK+ IM/HTML scalable.
- */
-void gtk_imhtml_hr_free(GtkIMHtmlScalable *scale);
-
-/**
- * Rescales a GTK+ IM/HTML scalable horizontal rule to a given size.
- *
- * @param scale The GTK+ IM/HTML scalable.
- * @param width The new width.
- * @param height The new height.
- */
-void gtk_imhtml_hr_scale(GtkIMHtmlScalable *scale, int width, int height);
-
-/**
- * Adds a GTK+ IM/HTML scalable horizontal rule to a given GTK+ IM/HTML at
- * a given iter.
- *
- * @param scale The GTK+ IM/HTML scalable.
- * @param imhtml The GTK+ IM/HTML.
- * @param iter The GtkTextIter at which to add the scalable.
- */
-void gtk_imhtml_hr_add_to(GtkIMHtmlScalable *scale, GtkIMHtml *imhtml, GtkTextIter *iter);
-
-/**
- * Finds and highlights a given string in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param text The string to search for.
- *
- * @return @c TRUE if a search was performed, or @c FALSE if not.
- */
-gboolean gtk_imhtml_search_find(GtkIMHtml *imhtml, const gchar *text);
-
-/**
- * Clears the highlighting from a prior search in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_search_clear(GtkIMHtml *imhtml);
-
-/**
- * Enables or disables editing in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param editable @c TRUE to make the widget editable, or @c FALSE otherwise.
- */
-void gtk_imhtml_set_editable(GtkIMHtml *imhtml, gboolean editable);
-
-/**
- * Enables or disables whole buffer formatting only (wbfo) in a GTK+ IM/HTML.
- * In this mode formatting options to the buffer take effect for the entire
- * buffer instead of specific text.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param wbfo @c TRUE to enable the mode, or @c FALSE otherwise.
- */
-void gtk_imhtml_set_whole_buffer_formatting_only(GtkIMHtml *imhtml, gboolean wbfo);
-
-/**
- * Indicates which formatting functions to enable and disable in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param buttons A GtkIMHtmlButtons bitmask indicating which functions to use.
- */
-void gtk_imhtml_set_format_functions(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons);
-
-/**
- * Returns which formatting functions are enabled in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- *
- * @return A GtkIMHtmlButtons bitmask indicating which functions to are enabled.
- */
-GtkIMHtmlButtons gtk_imhtml_get_format_functions(GtkIMHtml *imhtml);
-
-/**
- * Sets each boolean to @c TRUE or @c FALSE to indicate if that formatting option
- * is enabled at the current position in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param bold The boolean to set for bold or @c NULL.
- * @param italic The boolean to set for italic or @c NULL.
- * @param underline The boolean to set for underline or @c NULL.
- */
-void gtk_imhtml_get_current_format(GtkIMHtml *imhtml, gboolean *bold, gboolean *italic, gboolean *underline);
-
-/**
- * Returns a string containing the selected font face at the current position
- * in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- *
- * @return A string containg the font face or @c NULL if none is set.
- */
-char *gtk_imhtml_get_current_fontface(GtkIMHtml *imhtml);
-
-/**
- * Returns a string containing the selected foreground color at the current
- * position in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- *
- * @return A string containg the foreground color or @c NULL if none is set.
- */
-char *gtk_imhtml_get_current_forecolor(GtkIMHtml *imhtml);
-
-/**
- * Returns a string containing the selected font background color at the current
- * position in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- *
- * @return A string containg the font background color or @c NULL if none is set.
- */
-char *gtk_imhtml_get_current_backcolor(GtkIMHtml *imhtml);
-
-/**
- * Returns a string containing the selected background color at the current
- * position in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- *
- * @return A string containg the background color or @c NULL if none is set.
- */
-char *gtk_imhtml_get_current_background(GtkIMHtml *imhtml);
-
-/**
- * Returns a integer containing the selected HTML font size at the current
- * position in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- *
- * @return The HTML font size.
- */
-gint gtk_imhtml_get_current_fontsize(GtkIMHtml *imhtml);
-
-/**
- * Checks whether a GTK+ IM/HTML is marked as editable.
- *
- * @param imhtml The GTK+ IM/HTML.
- *
- * @return @c TRUE if the IM/HTML is editable, or @c FALSE otherwise.
- */
-gboolean gtk_imhtml_get_editable(GtkIMHtml *imhtml);
-
-/**
- * Clear all the formatting on a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_clear_formatting(GtkIMHtml *imhtml);
-
-/**
- * Toggles bold at the cursor location or selection in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_toggle_bold(GtkIMHtml *imhtml);
-
-/**
- * Toggles italic at the cursor location or selection in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_toggle_italic(GtkIMHtml *imhtml);
-
-/**
- * Toggles underline at the cursor location or selection in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_toggle_underline(GtkIMHtml *imhtml);
-
-/**
- * Toggles strikethrough at the cursor location or selection in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_toggle_strike(GtkIMHtml *imhtml);
-
-/**
- * Toggles a foreground color at the current location or selection in a GTK
- * IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param color The HTML-style color, or @c NULL or "" to clear the color.
- *
- * @return @c TRUE if a color was set, or @c FALSE if it was cleared.
- */
-gboolean gtk_imhtml_toggle_forecolor(GtkIMHtml *imhtml, const char *color);
-
-/**
- * Toggles a background color at the current location or selection in a GTK
- * IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param color The HTML-style color, or @c NULL or "" to clear the color.
- *
- * @return @c TRUE if a color was set, or @c FALSE if it was cleared.
- */
-gboolean gtk_imhtml_toggle_backcolor(GtkIMHtml *imhtml, const char *color);
-
-/**
- * Toggles a background color at the current location or selection in a GTK
- * IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param color The HTML-style color, or @c NULL or "" to clear the color.
- *
- * @return @c TRUE if a color was set, or @c FALSE if it was cleared.
- */
-gboolean gtk_imhtml_toggle_background(GtkIMHtml *imhtml, const char *color);
-
-/**
- * Toggles a font face at the current location or selection in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param face The font face name, or @c NULL or "" to clear the font.
- *
- * @return @c TRUE if a font name was set, or @c FALSE if it was cleared.
- */
-gboolean gtk_imhtml_toggle_fontface(GtkIMHtml *imhtml, const char *face);
-
-/**
- * Toggles a link tag with the given URL at the current location or selection
- * in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param url The URL for the link or @c NULL to terminate the link.
- */
-void gtk_imhtml_toggle_link(GtkIMHtml *imhtml, const char *url);
-
-/**
- * Inserts a link to the given url at the given GtkTextMark in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param mark The GtkTextMark to insert the link at.
- * @param url The URL for the link.
- * @param text The string to use for the link description.
- */
-void gtk_imhtml_insert_link(GtkIMHtml *imhtml, GtkTextMark *mark, const char *url, const char *text);
-
-/**
- * Inserts a smiley at the current location or selection in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param sml The category of the smiley.
- * @param smiley The text of the smiley to insert.
- */
-void gtk_imhtml_insert_smiley(GtkIMHtml *imhtml, const char *sml, char *smiley);
-/**
- * Inserts a smiley at the given iter in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param sml The category of the smiley.
- * @param smiley The text of the smiley to insert.
- * @param iter The GtkTextIter in the IM/HTML to insert the smiley at.
- */
-void gtk_imhtml_insert_smiley_at_iter(GtkIMHtml *imhtml, const char *sml, char *smiley, GtkTextIter *iter);
-
-/**
- * Inserts the IM/HTML scalable image with the given id at the given iter in a
- * GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param id The id of the IM/HTML scalable.
- * @param iter The GtkTextIter in the IM/HTML to insert the image at.
- */
-void gtk_imhtml_insert_image_at_iter(GtkIMHtml *imhtml, int id, GtkTextIter *iter);
-
-/**
- * Sets the font size at the current location or selection in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param size The HTML font size to use.
- */
-void gtk_imhtml_font_set_size(GtkIMHtml *imhtml, gint size);
-
-/**
- * Decreases the font size by 1 at the current location or selection in a GTK
- * IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_font_shrink(GtkIMHtml *imhtml);
-
-/**
- * Increases the font size by 1 at the current location or selection in a GTK
- * IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_font_grow(GtkIMHtml *imhtml);
-
-/**
- * Returns the HTML formatted contents between two iters in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param start The GtkTextIter indicating the start point in the IM/HTML.
- * @param end The GtkTextIter indicating the end point in the IM/HTML.
- *
- * @return A string containing the HTML formatted text.
- */
-char *gtk_imhtml_get_markup_range(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *end);
-
-/**
- * Returns the entire HTML formatted contents of a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- *
- * @return A string containing the HTML formatted text.
- */
-char *gtk_imhtml_get_markup(GtkIMHtml *imhtml);
-
-/**
- * Returns a null terminated array of pointers to null terminated strings, each
- * string for each line. g_strfreev() should be called to free it when done.
- *
- * @param imhtml The GTK+ IM/HTML.
- *
- * @return A null terminated array of null terminated HTML formatted strings.
- */
-char **gtk_imhtml_get_markup_lines(GtkIMHtml *imhtml);
-
-/**
- * Returns the entire unformatted (plain text) contents of a GTK+ IM/HTML
- * between two iters in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param start The GtkTextIter indicating the start point in the IM/HTML.
- * @param stop The GtkTextIter indicating the end point in the IM/HTML.
- *
- * @return A string containing the unformatted text.
- */
-char *gtk_imhtml_get_text(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *stop);
-
-/**
- * Setup formatting for an imhtml depending on the flags specified.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param flags The connection flag which describes the allowed types of formatting.
- *
- * @since 2.1.0
- */
-void gtk_imhtml_setup_entry(GtkIMHtml *imhtml, PurpleConnectionFlags flags);
-
-/**
- * Create a new GtkIMHtmlSmiley.
- *
- * @param file The image file for the smiley
- * @param shortcut The key shortcut for the smiley
- * @param hide @c TRUE if the smiley should be hidden in the smiley dialog, @c FALSE otherwise
- * @param flags The smiley flags
- *
- * @return The newly created smiley
- * @since 2.5.0
- */
-GtkIMHtmlSmiley *gtk_imhtml_smiley_create(const char *file, const char *shortcut, gboolean hide,
- GtkIMHtmlSmileyFlags flags);
-
-/**
- * Reload the image data for the smiley.
- *
- * @param smiley The smiley to reload
- *
- * @since 2.5.0
- */
-void gtk_imhtml_smiley_reload(GtkIMHtmlSmiley *smiley);
-
-/**
- * Destroy a GtkIMHtmlSmiley.
- *
- * @param smiley The smiley to destroy
- *
- * @since 2.5.0
- */
-void gtk_imhtml_smiley_destroy(GtkIMHtmlSmiley *smiley);
-
-/**
- * Register a protocol with the GtkIMHtml widget. Registering a protocol would
- * allow certain text to be clickable.
- *
- * @param name The name of the protocol (e.g. http://)
- * @param activate The callback to trigger when the protocol text is clicked.
- * Removes any current protocol definition if @c NULL. The
- * callback should return @c TRUE if the link was activated
- * properly, @c FALSE otherwise.
- * @param context_menu The callback to trigger when the context menu is popped
- * up on the protocol text. The callback should return
- * @c TRUE if the request for context menu was processed
- * successfully, @c FALSE otherwise.
- *
- * @return @c TRUE if the protocol was successfully registered (or unregistered, when \a activate is @c NULL)
- *
- * @since 2.6.0
- */
-gboolean gtk_imhtml_class_register_protocol(const char *name,
- gboolean (*activate)(GtkIMHtml *imhtml, GtkIMHtmlLink *link),
- gboolean (*context_menu)(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu));
-
-/**
- * Get the URL associated with a link. This should be used by the IMHtml protocol-callbacks.
- *
- * @param link The GtkIMHtmlLink object sent to the callback functions
- *
- * @return The URL
- *
- * @since 2.6.0
- */
-const char *gtk_imhtml_link_get_url(GtkIMHtmlLink *link);
-
-/**
- * Get the GtkTextTag object (if any) associated with a particular link.
- *
- * @param link The GtkIMHtmlLink object sent to the callback functions
- *
- * @return The GtkTextTag object, or @c NULL
- *
- * @since 2.6.0
- */
-const GtkTextTag *gtk_imhtml_link_get_text_tag(GtkIMHtmlLink *link);
-
-/**
- * Activates a GtkIMHtmlLink object. This triggers the 'url-clicked' signal, marks the
- * link as visited (when possible).
- *
- * @param link The GtkIMHtmlLink object sent to the callback functions
- *
- * @return @c TRUE if 'url-clicked' signal was emitted, @c FALSE otherwise.
- *
- * @since 2.6.0
- */
-gboolean gtk_imhtml_link_activate(GtkIMHtmlLink *link);
-
-/**
- * By default this widget intercepts presses of the "return" key and
- * emits the "message_send" signal instead. If you don't want this
- * behavior, and you want the standard GtkTextView behavior of
- * inserting a newline into the buffer, then call this function.
- *
- * @param imhtml The GtkIMHtml where you want the "return" key to add
- * newline and not emit the "message_send" signal.
- */
-void gtk_imhtml_set_return_inserts_newline(GtkIMHtml *imhtml);
-
-/**
- * By default this widget populates the PRIMARY clipboard with any selected
- * text (as you would expect). For scenarios (e.g. select-on-focus) where this
- * would be problematic, this function can disable the PRIMARY population.
- *
- * @param imhtml The GtkIMHtml to enable/disable populating PRIMARY
- * @param populate enable/disable PRIMARY population
- */
-void gtk_imhtml_set_populate_primary_clipboard(GtkIMHtml *imhtml, gboolean populate);
-
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _PIDGINIMHTML_H_ */
diff --git a/pidgin/gtkimhtmltoolbar.c b/pidgin/gtkimhtmltoolbar.c
deleted file mode 100644
index e6d6f7652b..0000000000
--- a/pidgin/gtkimhtmltoolbar.c
+++ /dev/null
@@ -1,1597 +0,0 @@
-/*
- * @file gtkimhtmltoolbar.c GTK+ IMHtml Toolbar
- * @ingroup pidgin
- */
-
-/* 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 "imgstore.h"
-#include "notify.h"
-#include "prefs.h"
-#include "request.h"
-#include "pidginstock.h"
-#include "util.h"
-#include "debug.h"
-
-#include "gtkdialogs.h"
-#include "gtkimhtmltoolbar.h"
-#include "gtksmiley.h"
-#include "gtkthemes.h"
-#include "gtkutils.h"
-
-#include <gdk/gdkkeysyms.h>
-
-static GtkHBoxClass *parent_class = NULL;
-
-static void toggle_button_set_active_block(GtkToggleButton *button,
- gboolean is_active,
- GtkIMHtmlToolbar *toolbar);
-
-static gboolean
-gtk_imhtmltoolbar_popup_menu(GtkWidget *widget,
- GdkEventButton *event, GtkIMHtmlToolbar *toolbar);
-
-static void do_bold(GtkWidget *bold, GtkIMHtmlToolbar *toolbar)
-{
- g_return_if_fail(toolbar != NULL);
- gtk_imhtml_toggle_bold(GTK_IMHTML(toolbar->imhtml));
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static void
-do_italic(GtkWidget *italic, GtkIMHtmlToolbar *toolbar)
-{
- g_return_if_fail(toolbar != NULL);
- gtk_imhtml_toggle_italic(GTK_IMHTML(toolbar->imhtml));
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static void
-do_underline(GtkWidget *underline, GtkIMHtmlToolbar *toolbar)
-{
- g_return_if_fail(toolbar != NULL);
- gtk_imhtml_toggle_underline(GTK_IMHTML(toolbar->imhtml));
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static void
-do_strikethrough(GtkWidget *strikethrough, GtkIMHtmlToolbar *toolbar)
-{
- g_return_if_fail(toolbar != NULL);
- gtk_imhtml_toggle_strike(GTK_IMHTML(toolbar->imhtml));
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static void
-do_small(GtkWidget *smalltb, GtkIMHtmlToolbar *toolbar)
-{
- g_return_if_fail(toolbar != NULL);
- /* Only shrink the font on activation, not deactivation as well */
- if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(smalltb)))
- gtk_imhtml_font_shrink(GTK_IMHTML(toolbar->imhtml));
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static void
-do_big(GtkWidget *large, GtkIMHtmlToolbar *toolbar)
-{
- g_return_if_fail(toolbar);
- /* Only grow the font on activation, not deactivation as well */
- if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(large)))
- gtk_imhtml_font_grow(GTK_IMHTML(toolbar->imhtml));
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static gboolean
-destroy_toolbar_font(GtkWidget *widget, GdkEvent *event,
- GtkIMHtmlToolbar *toolbar)
-{
- if (widget != NULL)
- gtk_imhtml_toggle_fontface(GTK_IMHTML(toolbar->imhtml), "");
-
- if (toolbar->font_dialog != NULL)
- {
- gtk_widget_destroy(toolbar->font_dialog);
- toolbar->font_dialog = NULL;
- }
- return FALSE;
-}
-
-static void
-realize_toolbar_font(GtkWidget *widget, GtkIMHtmlToolbar *toolbar)
-{
- GtkFontSelection *sel;
-
- sel = GTK_FONT_SELECTION(GTK_FONT_SELECTION_DIALOG(toolbar->font_dialog)->fontsel);
- gtk_widget_hide_all(gtk_widget_get_parent(sel->size_entry));
- gtk_widget_show_all(sel->family_list);
- gtk_widget_show(gtk_widget_get_parent(sel->family_list));
- gtk_widget_show(gtk_widget_get_parent(gtk_widget_get_parent(sel->family_list)));
-}
-
-static void
-cancel_toolbar_font(GtkWidget *widget, GtkIMHtmlToolbar *toolbar)
-{
- destroy_toolbar_font(widget, NULL, toolbar);
-}
-
-static void
-apply_font(GtkWidget *widget, GtkFontSelectionDialog *fontsel)
-{
- /* this could be expanded to include font size, weight, etc.
- but for now only works with font face */
- gchar *fontname = gtk_font_selection_dialog_get_font_name(fontsel);
- GtkIMHtmlToolbar *toolbar = g_object_get_data(G_OBJECT(fontsel),
- "purple_toolbar");
-
- if (fontname) {
- const gchar *family_name = NULL;
- PangoFontDescription *desc = NULL;
-
- desc = pango_font_description_from_string(fontname);
- family_name = pango_font_description_get_family(desc);
-
- if (family_name) {
- gtk_imhtml_toggle_fontface(GTK_IMHTML(toolbar->imhtml),
- family_name);
- }
-
- pango_font_description_free(desc);
- g_free(fontname);
- }
-
- cancel_toolbar_font(NULL, toolbar);
-}
-
-static void
-toggle_font(GtkWidget *font, GtkIMHtmlToolbar *toolbar)
-{
- g_return_if_fail(toolbar);
-
- if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(font))) {
- char *fontname = gtk_imhtml_get_current_fontface(GTK_IMHTML(toolbar->imhtml));
-
- if (!toolbar->font_dialog) {
- toolbar->font_dialog = gtk_font_selection_dialog_new(_("Select Font"));
-
- g_object_set_data(G_OBJECT(toolbar->font_dialog), "purple_toolbar", toolbar);
-
- if(fontname) {
- char *fonttif = g_strdup_printf("%s 12", fontname);
- g_free(fontname);
- gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(toolbar->font_dialog),
- fonttif);
- g_free(fonttif);
- } else {
- gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(toolbar->font_dialog),
- DEFAULT_FONT_FACE);
- }
-
- g_signal_connect(G_OBJECT(toolbar->font_dialog), "delete_event",
- G_CALLBACK(destroy_toolbar_font), toolbar);
- g_signal_connect(G_OBJECT(GTK_FONT_SELECTION_DIALOG(toolbar->font_dialog)->ok_button), "clicked",
- G_CALLBACK(apply_font), toolbar->font_dialog);
- g_signal_connect(G_OBJECT(GTK_FONT_SELECTION_DIALOG(toolbar->font_dialog)->cancel_button), "clicked",
- G_CALLBACK(cancel_toolbar_font), toolbar);
- g_signal_connect_after(G_OBJECT(toolbar->font_dialog), "realize",
- G_CALLBACK(realize_toolbar_font), toolbar);
- }
- gtk_window_present(GTK_WINDOW(toolbar->font_dialog));
- } else {
- cancel_toolbar_font(font, toolbar);
- }
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static gboolean
-destroy_toolbar_fgcolor(GtkWidget *widget, GdkEvent *event,
- GtkIMHtmlToolbar *toolbar)
-{
- if (widget != NULL)
- gtk_imhtml_toggle_forecolor(GTK_IMHTML(toolbar->imhtml), "");
-
- if (toolbar->fgcolor_dialog != NULL)
- {
- gtk_widget_destroy(toolbar->fgcolor_dialog);
- toolbar->fgcolor_dialog = NULL;
- }
- return FALSE;
-}
-
-static void cancel_toolbar_fgcolor(GtkWidget *widget,
- GtkIMHtmlToolbar *toolbar)
-{
- destroy_toolbar_fgcolor(widget, NULL, toolbar);
-}
-
-static void do_fgcolor(GtkWidget *widget, GtkColorSelection *colorsel)
-{
- GdkColor text_color;
- GtkIMHtmlToolbar *toolbar = g_object_get_data(G_OBJECT(colorsel), "purple_toolbar");
- char *open_tag;
-
- open_tag = g_malloc(30);
- gtk_color_selection_get_current_color(colorsel, &text_color);
- g_snprintf(open_tag, 23, "#%02X%02X%02X",
- text_color.red / 256,
- text_color.green / 256,
- text_color.blue / 256);
- gtk_imhtml_toggle_forecolor(GTK_IMHTML(toolbar->imhtml), open_tag);
- g_free(open_tag);
-
- cancel_toolbar_fgcolor(NULL, toolbar);
-}
-
-static void
-toggle_fg_color(GtkWidget *color, GtkIMHtmlToolbar *toolbar)
-{
- if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(color))) {
- GtkWidget *colorsel;
- GdkColor fgcolor;
- char *color = gtk_imhtml_get_current_forecolor(GTK_IMHTML(toolbar->imhtml));
-
- if (!toolbar->fgcolor_dialog) {
-
- toolbar->fgcolor_dialog = gtk_color_selection_dialog_new(_("Select Text Color"));
- colorsel = GTK_COLOR_SELECTION_DIALOG(toolbar->fgcolor_dialog)->colorsel;
- if (color) {
- gdk_color_parse(color, &fgcolor);
- gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(colorsel), &fgcolor);
- g_free(color);
- }
-
- g_object_set_data(G_OBJECT(colorsel), "purple_toolbar", toolbar);
-
- g_signal_connect(G_OBJECT(toolbar->fgcolor_dialog), "delete_event",
- G_CALLBACK(destroy_toolbar_fgcolor), toolbar);
- g_signal_connect(G_OBJECT(GTK_COLOR_SELECTION_DIALOG(toolbar->fgcolor_dialog)->ok_button), "clicked",
- G_CALLBACK(do_fgcolor), colorsel);
- g_signal_connect(G_OBJECT (GTK_COLOR_SELECTION_DIALOG(toolbar->fgcolor_dialog)->cancel_button), "clicked",
- G_CALLBACK(cancel_toolbar_fgcolor), toolbar);
- }
- gtk_window_present(GTK_WINDOW(toolbar->fgcolor_dialog));
- } else {
- cancel_toolbar_fgcolor(color, toolbar);
- }
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static gboolean
-destroy_toolbar_bgcolor(GtkWidget *widget, GdkEvent *event,
- GtkIMHtmlToolbar *toolbar)
-{
- if (widget != NULL) {
- if (gtk_text_buffer_get_selection_bounds(GTK_IMHTML(toolbar->imhtml)->text_buffer, NULL, NULL))
- gtk_imhtml_toggle_backcolor(GTK_IMHTML(toolbar->imhtml), "");
- else
- gtk_imhtml_toggle_background(GTK_IMHTML(toolbar->imhtml), "");
- }
-
- if (toolbar->bgcolor_dialog != NULL)
- {
- gtk_widget_destroy(toolbar->bgcolor_dialog);
- toolbar->bgcolor_dialog = NULL;
- }
- return FALSE;
-}
-
-static void
-cancel_toolbar_bgcolor(GtkWidget *widget, GtkIMHtmlToolbar *toolbar)
-{
- destroy_toolbar_bgcolor(widget, NULL, toolbar);
-}
-
-static void do_bgcolor(GtkWidget *widget, GtkColorSelection *colorsel)
-{
- GdkColor text_color;
- GtkIMHtmlToolbar *toolbar = g_object_get_data(G_OBJECT(colorsel), "purple_toolbar");
- char *open_tag;
-
- open_tag = g_malloc(30);
- gtk_color_selection_get_current_color(colorsel, &text_color);
- g_snprintf(open_tag, 23, "#%02X%02X%02X",
- text_color.red / 256,
- text_color.green / 256,
- text_color.blue / 256);
- if (gtk_text_buffer_get_selection_bounds(GTK_IMHTML(toolbar->imhtml)->text_buffer, NULL, NULL))
- gtk_imhtml_toggle_backcolor(GTK_IMHTML(toolbar->imhtml), open_tag);
- else
- gtk_imhtml_toggle_background(GTK_IMHTML(toolbar->imhtml), open_tag);
- g_free(open_tag);
-
- cancel_toolbar_bgcolor(NULL, toolbar);
-}
-
-static void
-toggle_bg_color(GtkWidget *color, GtkIMHtmlToolbar *toolbar)
-{
- if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(color))) {
- GtkWidget *colorsel;
- GdkColor bgcolor;
- char *color = gtk_imhtml_get_current_backcolor(GTK_IMHTML(toolbar->imhtml));
-
- if (!toolbar->bgcolor_dialog) {
-
- toolbar->bgcolor_dialog = gtk_color_selection_dialog_new(_("Select Background Color"));
- colorsel = GTK_COLOR_SELECTION_DIALOG(toolbar->bgcolor_dialog)->colorsel;
- if (color) {
- gdk_color_parse(color, &bgcolor);
- gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(colorsel), &bgcolor);
- g_free(color);
- }
-
- g_object_set_data(G_OBJECT(colorsel), "purple_toolbar", toolbar);
-
- g_signal_connect(G_OBJECT(toolbar->bgcolor_dialog), "delete_event",
- G_CALLBACK(destroy_toolbar_bgcolor), toolbar);
- g_signal_connect(G_OBJECT(GTK_COLOR_SELECTION_DIALOG(toolbar->bgcolor_dialog)->ok_button), "clicked",
- G_CALLBACK(do_bgcolor), colorsel);
- g_signal_connect(G_OBJECT(GTK_COLOR_SELECTION_DIALOG(toolbar->bgcolor_dialog)->cancel_button), "clicked",
- G_CALLBACK(cancel_toolbar_bgcolor), toolbar);
-
- }
- gtk_window_present(GTK_WINDOW(toolbar->bgcolor_dialog));
- } else {
- cancel_toolbar_bgcolor(color, toolbar);
- }
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static void
-clear_formatting_cb(GtkWidget *clear, GtkIMHtmlToolbar *toolbar)
-{
- toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->clear), FALSE, toolbar);
- gtk_imhtml_clear_formatting(GTK_IMHTML(toolbar->imhtml));
-}
-
-static void
-cancel_link_cb(GtkIMHtmlToolbar *toolbar, PurpleRequestFields *fields)
-{
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->link), FALSE);
-
- toolbar->link_dialog = NULL;
-}
-
-static void
-close_link_dialog(GtkIMHtmlToolbar *toolbar)
-{
- if (toolbar->link_dialog != NULL)
- {
- purple_request_close(PURPLE_REQUEST_FIELDS, toolbar->link_dialog);
- toolbar->link_dialog = NULL;
- }
-}
-
-static void
-do_insert_link_cb(GtkIMHtmlToolbar *toolbar, PurpleRequestFields *fields)
-{
- const char *url, *description;
-
- url = purple_request_fields_get_string(fields, "url");
- if (GTK_IMHTML(toolbar->imhtml)->format_functions & GTK_IMHTML_LINKDESC)
- description = purple_request_fields_get_string(fields, "description");
- else
- description = NULL;
-
- if (description == NULL)
- description = url;
-
- gtk_imhtml_insert_link(GTK_IMHTML(toolbar->imhtml),
- gtk_text_buffer_get_insert(GTK_IMHTML(toolbar->imhtml)->text_buffer),
- url, description);
-
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->link), FALSE);
-
- toolbar->link_dialog = NULL;
-}
-
-static void
-insert_link_cb(GtkWidget *w, GtkIMHtmlToolbar *toolbar)
-{
- if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->link))) {
- PurpleRequestFields *fields;
- PurpleRequestFieldGroup *group;
- PurpleRequestField *field;
- GtkTextIter start, end;
- 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(GTK_IMHTML(toolbar->imhtml)->format_functions & GTK_IMHTML_LINKDESC) {
- if (gtk_text_buffer_get_selection_bounds(GTK_IMHTML(toolbar->imhtml)->text_buffer, &start, &end)) {
- desc = gtk_imhtml_get_text(GTK_IMHTML(toolbar->imhtml), &start, &end);
- }
- 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."));
- }
-
- toolbar->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, NULL, NULL,
- toolbar);
- g_free(msg);
- g_free(desc);
- } else {
- close_link_dialog(toolbar);
- }
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static void insert_hr_cb(GtkWidget *widget, GtkIMHtmlToolbar *toolbar)
-{
- GtkTextIter iter;
- GtkTextMark *ins;
- GtkIMHtmlScalable *hr;
-
- ins = gtk_text_buffer_get_insert(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml)));
- gtk_text_buffer_get_iter_at_mark(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml)), &iter, ins);
- hr = gtk_imhtml_hr_new();
- gtk_imhtml_hr_add_to(hr, GTK_IMHTML(toolbar->imhtml), &iter);
-}
-
-static void
-do_insert_image_cb(GtkWidget *widget, int response, GtkIMHtmlToolbar *toolbar)
-{
- gchar *filename, *name, *buf;
- char *filedata;
- size_t size;
- GError *error = NULL;
- int id;
- GtkTextIter iter;
- GtkTextMark *ins;
-
- if (response != GTK_RESPONSE_ACCEPT)
- {
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image), FALSE);
- return;
- }
-
- filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
-
- if (filename == NULL) {
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image), FALSE);
- return;
- }
-
- /* The following triggers a callback that closes the widget */
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image), FALSE);
-
- if (!g_file_get_contents(filename, &filedata, &size, &error)) {
- purple_notify_error(NULL, NULL, error->message, NULL);
-
- g_error_free(error);
- g_free(filename);
-
- return;
- }
-
- name = strrchr(filename, G_DIR_SEPARATOR) + 1;
-
- id = purple_imgstore_add_with_id(filedata, size, name);
-
- if (id == 0) {
- buf = g_strdup_printf(_("Failed to store image: %s\n"), filename);
- purple_notify_error(NULL, NULL, buf, NULL);
-
- g_free(buf);
- g_free(filename);
-
- return;
- }
-
- g_free(filename);
-
- ins = gtk_text_buffer_get_insert(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml)));
- gtk_text_buffer_get_iter_at_mark(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml)),
- &iter, ins);
- gtk_imhtml_insert_image_at_iter(GTK_IMHTML(toolbar->imhtml), id, &iter);
- purple_imgstore_unref_by_id(id);
-}
-
-
-static void
-insert_image_cb(GtkWidget *save, GtkIMHtmlToolbar *toolbar)
-{
- GtkWidget *window;
-
- if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->image))) {
- 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(GTK_FILE_CHOOSER(window)),
- "response", G_CALLBACK(do_insert_image_cb), toolbar);
-
- gtk_widget_show(window);
- toolbar->image_dialog = window;
- } else {
- gtk_widget_destroy(toolbar->image_dialog);
- toolbar->image_dialog = NULL;
- }
-
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-
-static void
-destroy_smiley_dialog(GtkIMHtmlToolbar *toolbar)
-{
- if (toolbar->smiley_dialog != NULL)
- {
- gtk_widget_destroy(toolbar->smiley_dialog);
- toolbar->smiley_dialog = NULL;
- }
-}
-
-static gboolean
-close_smiley_dialog(GtkIMHtmlToolbar *toolbar)
-{
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->smiley), FALSE);
- return FALSE;
-}
-
-
-static void
-insert_smiley_text(GtkWidget *widget, GtkIMHtmlToolbar *toolbar)
-{
- char *smiley_text, *escaped_smiley;
-
- smiley_text = g_object_get_data(G_OBJECT(widget), "smiley_text");
- escaped_smiley = g_markup_escape_text(smiley_text, -1);
-
- gtk_imhtml_insert_smiley(GTK_IMHTML(toolbar->imhtml),
- GTK_IMHTML(toolbar->imhtml)->protocol_name,
- escaped_smiley);
-
- g_free(escaped_smiley);
-
- close_smiley_dialog(toolbar);
-}
-
-/* smiley buttons list */
-struct smiley_button_list {
- int width, height;
- GtkWidget *button;
- const GtkIMHtmlSmiley *smiley;
- struct smiley_button_list *next;
-};
-
-static struct smiley_button_list *
-sort_smileys(struct smiley_button_list *ls, GtkIMHtmlToolbar *toolbar,
- int *width, const GtkIMHtmlSmiley *smiley)
-{
- GtkWidget *image;
- GtkWidget *button;
- GtkRequisition size;
- struct smiley_button_list *cur;
- struct smiley_button_list *it, *it_last;
- const gchar *filename = smiley->file;
- gchar *face = smiley->smile;
- PurpleSmiley *psmiley = NULL;
- gboolean supports_custom = (gtk_imhtml_get_format_functions(GTK_IMHTML(toolbar->imhtml)) & GTK_IMHTML_CUSTOM_SMILEY);
-
- cur = g_new0(struct smiley_button_list, 1);
- it = ls;
- it_last = ls; /* list iterators*/
- image = gtk_image_new_from_file(filename);
-
- gtk_widget_size_request(image, &size);
-
- if (size.width > 24 &&
- smiley->flags & GTK_IMHTML_SMILEY_CUSTOM) { /* This is a custom smiley, let's scale it */
- GdkPixbuf *pixbuf = NULL;
- GtkImageType type;
-
- type = gtk_image_get_storage_type(GTK_IMAGE(image));
-
- if (type == GTK_IMAGE_PIXBUF) {
- pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(image));
- } else if (type == GTK_IMAGE_ANIMATION) {
- GdkPixbufAnimation *animation;
-
- animation = gtk_image_get_animation(GTK_IMAGE(image));
-
- pixbuf = gdk_pixbuf_animation_get_static_image(animation);
- }
-
- if (pixbuf != NULL) {
- GdkPixbuf *resized;
- resized = gdk_pixbuf_scale_simple(pixbuf, 24, 24,
- GDK_INTERP_HYPER);
-
- gtk_image_set_from_pixbuf(GTK_IMAGE(image), resized); /* This unrefs pixbuf */
- gtk_widget_size_request(image, &size);
- g_object_unref(G_OBJECT(resized));
- }
- }
-
- (*width) += size.width;
-
- button = gtk_button_new();
- gtk_container_add(GTK_CONTAINER(button), image);
-
- g_object_set_data(G_OBJECT(button), "smiley_text", face);
- g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(insert_smiley_text), toolbar);
-
- gtk_tooltips_set_tip(toolbar->tooltips, button, face, NULL);
-
- /* these look really weird with borders */
- gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
-
- psmiley = purple_smileys_find_by_shortcut(smiley->smile);
- /* If this is a "non-custom" smiley, check to see if its shortcut is
- "shadowed" by any custom smiley. This can only happen if the connection
- is custom smiley-enabled */
- if (supports_custom && psmiley && !(smiley->flags & GTK_IMHTML_SMILEY_CUSTOM)) {
- gchar tip[128];
- g_snprintf(tip, sizeof(tip),
- _("This smiley is disabled because a custom smiley exists for this shortcut:\n %s"),
- face);
- gtk_tooltips_set_tip(toolbar->tooltips, button, tip, NULL);
- gtk_widget_set_sensitive(button, FALSE);
- } else if (psmiley) {
- /* Remove the button if the smiley is destroyed */
- g_signal_connect_object(G_OBJECT(psmiley), "destroy", G_CALLBACK(gtk_widget_destroy),
- button, G_CONNECT_SWAPPED);
- }
-
- /* set current element to add */
- cur->height = size.height;
- cur->width = size.width;
- cur->button = button;
- cur->smiley = smiley;
- cur->next = ls;
-
- /* check where to insert by height */
- if (ls == NULL)
- return cur;
- while (it != NULL) {
- it_last = it;
- it = it->next;
- }
- cur->next = it;
- it_last->next = cur;
- return ls;
-}
-
-static gboolean
-smiley_is_unique(GSList *list, GtkIMHtmlSmiley *smiley)
-{
- while (list) {
- GtkIMHtmlSmiley *cur = (GtkIMHtmlSmiley *) list->data;
- if (!strcmp(cur->file, smiley->file))
- return FALSE;
- list = list->next;
- }
- return TRUE;
-}
-
-static gboolean
-smiley_dialog_input_cb(GtkWidget *dialog, GdkEvent *event, GtkIMHtmlToolbar *toolbar)
-{
- if ((event->type == GDK_KEY_PRESS && event->key.keyval == GDK_Escape) ||
- (event->type == GDK_BUTTON_PRESS && event->button.button == 1))
- {
- close_smiley_dialog(toolbar);
- return TRUE;
- }
-
- return FALSE;
-}
-
-static void
-add_smiley_list(GtkWidget *container, struct smiley_button_list *list,
- int max_width, gboolean custom)
-{
- GtkWidget *line;
- int line_width = 0;
-
- if (!list)
- return;
-
- line = gtk_hbox_new(FALSE, 0);
- gtk_box_pack_start(GTK_BOX(container), line, FALSE, FALSE, 0);
- for (; list; list = list->next) {
- if (custom != !!(list->smiley->flags & GTK_IMHTML_SMILEY_CUSTOM))
- continue;
- gtk_box_pack_start(GTK_BOX(line), list->button, FALSE, FALSE, 0);
- gtk_widget_show(list->button);
- line_width += list->width;
- if (line_width >= max_width) {
- if (list->next) {
- line = gtk_hbox_new(FALSE, 0);
- gtk_box_pack_start(GTK_BOX(container), line, FALSE, FALSE, 0);
- }
- line_width = 0;
- }
- }
-}
-
-static void
-insert_smiley_cb(GtkWidget *smiley, GtkIMHtmlToolbar *toolbar)
-{
- GtkWidget *dialog, *vbox;
- GtkWidget *smiley_table = NULL;
- GSList *smileys, *unique_smileys = NULL;
- const GSList *custom_smileys = NULL;
- gboolean supports_custom = FALSE;
- GtkRequisition req;
- GtkWidget *scrolled, *viewport;
-
- if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(smiley))) {
- destroy_smiley_dialog(toolbar);
- gtk_widget_grab_focus(toolbar->imhtml);
- return;
- }
-
- if (toolbar->sml)
- smileys = pidgin_themes_get_proto_smileys(toolbar->sml);
- else
- smileys = pidgin_themes_get_proto_smileys(NULL);
-
- /* Note: prepend smileys to list to avoid O(n^2) overhead when there is
- a large number of smileys... need to revers the list after for the dialog
- work... */
- while(smileys) {
- GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) smileys->data;
- if(!smiley->hidden) {
- if(smiley_is_unique(unique_smileys, smiley)) {
- unique_smileys = g_slist_prepend(unique_smileys, smiley);
- }
- }
- smileys = smileys->next;
- }
- supports_custom = (gtk_imhtml_get_format_functions(GTK_IMHTML(toolbar->imhtml)) & GTK_IMHTML_CUSTOM_SMILEY);
- if (toolbar->imhtml && supports_custom) {
- const GSList *iterator = NULL;
- custom_smileys = pidgin_smileys_get_all();
-
- for (iterator = custom_smileys ; iterator ;
- iterator = g_slist_next(iterator)) {
- GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) iterator->data;
- unique_smileys = g_slist_prepend(unique_smileys, smiley);
- }
- }
-
- /* we need to reverse the list to get the smileys in the correct order */
- unique_smileys = g_slist_reverse(unique_smileys);
-
- 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 (unique_smileys != NULL) {
- struct smiley_button_list *ls;
- int max_line_width, num_lines, button_width = 0;
-
- /* We use hboxes packed in a vbox */
- ls = NULL;
- max_line_width = 0;
- num_lines = floor(sqrt(g_slist_length(unique_smileys)));
- smiley_table = gtk_vbox_new(FALSE, 0);
-
- if (supports_custom) {
- GtkWidget *manage = gtk_button_new_with_mnemonic(_("_Manage custom smileys"));
- GtkRequisition req;
- g_signal_connect(G_OBJECT(manage), "clicked",
- G_CALLBACK(pidgin_smiley_manager_show), NULL);
- g_signal_connect_swapped(G_OBJECT(manage), "clicked",
- G_CALLBACK(gtk_widget_destroy), dialog);
- gtk_box_pack_end(GTK_BOX(vbox), manage, FALSE, TRUE, 0);
- gtk_widget_size_request(manage, &req);
- button_width = req.width;
- }
-
- /* create list of smileys sorted by height */
- while (unique_smileys) {
- GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) unique_smileys->data;
- if (!smiley->hidden) {
- ls = sort_smileys(ls, toolbar, &max_line_width, smiley);
- }
- unique_smileys = g_slist_delete_link(unique_smileys, unique_smileys);
- }
- /* The window will be at least as wide as the 'Manage ..' button */
- max_line_width = MAX(button_width, max_line_width / num_lines);
-
- /* pack buttons of the list */
- add_smiley_list(smiley_table, ls, max_line_width, FALSE);
- if (supports_custom) {
- gtk_box_pack_start(GTK_BOX(smiley_table), gtk_hseparator_new(), TRUE, FALSE, 0);
- add_smiley_list(smiley_table, ls, max_line_width, TRUE);
- }
- while (ls) {
- struct smiley_button_list *tmp = ls->next;
- g_free(ls);
- ls = tmp;
- }
-
- 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);
- }
-
-
- 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_size_request(viewport, &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
-
- toolbar->smiley_dialog = dialog;
-
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static void send_attention_cb(GtkWidget *attention, GtkIMHtmlToolbar *toolbar)
-{
- PurpleConversation *conv =
- g_object_get_data(G_OBJECT(toolbar), "active_conv");
- const gchar *who = purple_conversation_get_name(conv);
- PurpleConnection *gc = purple_conversation_get_gc(conv);
-
- toggle_button_set_active_block(GTK_TOGGLE_BUTTON(attention), FALSE, toolbar);
- purple_prpl_send_attention(gc, who, 0);
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static void update_buttons_cb(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons, GtkIMHtmlToolbar *toolbar)
-{
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->bold), buttons & GTK_IMHTML_BOLD);
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->italic), buttons & GTK_IMHTML_ITALIC);
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->underline), buttons & GTK_IMHTML_UNDERLINE);
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->strikethrough), buttons & GTK_IMHTML_STRIKE);
-
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->larger_size), buttons & GTK_IMHTML_GROW);
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->smaller_size), buttons & GTK_IMHTML_SHRINK);
-
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->font), buttons & GTK_IMHTML_FACE);
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->fgcolor), buttons & GTK_IMHTML_FORECOLOR);
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->bgcolor), buttons & GTK_IMHTML_BACKCOLOR);
-
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->clear),
- (buttons & GTK_IMHTML_BOLD ||
- buttons & GTK_IMHTML_ITALIC ||
- buttons & GTK_IMHTML_UNDERLINE ||
- buttons & GTK_IMHTML_STRIKE ||
- buttons & GTK_IMHTML_GROW ||
- buttons & GTK_IMHTML_SHRINK ||
- buttons & GTK_IMHTML_FACE ||
- buttons & GTK_IMHTML_FORECOLOR ||
- buttons & GTK_IMHTML_BACKCOLOR));
-
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->image), buttons & GTK_IMHTML_IMAGE);
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->link), buttons & GTK_IMHTML_LINK);
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->smiley), buttons & GTK_IMHTML_SMILEY);
-}
-
-/* we call this when we want to _set_active the toggle button, it'll
- * block the callback thats connected to the button so we don't have to
- * do the double toggling hack
- */
-static void toggle_button_set_active_block(GtkToggleButton *button,
- gboolean is_active,
- GtkIMHtmlToolbar *toolbar)
-{
- GObject *object;
- g_return_if_fail(toolbar);
-
- object = g_object_ref(button);
- g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA,
- 0, 0, NULL, NULL, toolbar);
- gtk_toggle_button_set_active(button, 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(GtkIMHtmlToolbar *toolbar)
-{
- gboolean bold, italic, underline, strike;
- char *tmp;
- char *tmp2;
- GtkLabel *label = g_object_get_data(G_OBJECT(toolbar), "font_label");
-
- gtk_label_set_label(label, _("_Font"));
-
- gtk_imhtml_get_current_format(GTK_IMHTML(toolbar->imhtml),
- &bold, &italic, &underline);
- strike = GTK_IMHTML(toolbar->imhtml)->edit.strike;
-
- if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->bold)) != bold)
- toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->bold), bold,
- toolbar);
- if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->italic)) != italic)
- toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->italic), italic,
- toolbar);
- if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->underline)) != underline)
- toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->underline),
- underline, toolbar);
- if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->strikethrough)) != strike)
- toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->strikethrough),
- strike, toolbar);
-
- /* These buttons aren't ever "active". */
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->smaller_size), FALSE);
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->larger_size), FALSE);
-
- if (bold) {
- gchar *markup = g_strdup_printf("<b>%s</b>",
- gtk_label_get_label(label));
- gtk_label_set_markup_with_mnemonic(label, markup);
- g_free(markup);
- }
- if (italic) {
- gchar *markup = g_strdup_printf("<i>%s</i>",
- gtk_label_get_label(label));
- gtk_label_set_markup_with_mnemonic(label, markup);
- g_free(markup);
- }
- if (underline) {
- gchar *markup = g_strdup_printf("<u>%s</u>",
- gtk_label_get_label(label));
- gtk_label_set_markup_with_mnemonic(label, markup);
- g_free(markup);
- }
- if (strike) {
- gchar *markup = g_strdup_printf("<s>%s</s>",
- gtk_label_get_label(label));
- gtk_label_set_markup_with_mnemonic(label, markup);
- g_free(markup);
- }
-
- tmp = gtk_imhtml_get_current_fontface(GTK_IMHTML(toolbar->imhtml));
- toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->font),
- (tmp != NULL), toolbar);
- if (tmp != NULL) {
- gchar *markup = g_strdup_printf("<span font_desc=\"%s\">%s</span>",
- tmp, gtk_label_get_label(label));
- gtk_label_set_markup_with_mnemonic(label, markup);
- g_free(markup);
- }
- g_free(tmp);
-
- tmp = gtk_imhtml_get_current_forecolor(GTK_IMHTML(toolbar->imhtml));
- toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->fgcolor),
- (tmp != NULL), toolbar);
- if (tmp != NULL) {
- gchar *markup = g_strdup_printf("<span foreground=\"%s\">%s</span>",
- tmp, gtk_label_get_label(label));
- gtk_label_set_markup_with_mnemonic(label, markup);
- g_free(markup);
- }
- g_free(tmp);
-
- tmp = gtk_imhtml_get_current_backcolor(GTK_IMHTML(toolbar->imhtml));
- tmp2 = gtk_imhtml_get_current_background(GTK_IMHTML(toolbar->imhtml));
- toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->bgcolor),
- (tmp != NULL || tmp2 != NULL), toolbar);
- if (tmp != NULL) {
- gchar *markup = g_strdup_printf("<span background=\"%s\">%s</span>",
- tmp, gtk_label_get_label(label));
- gtk_label_set_markup_with_mnemonic(label, markup);
- g_free(markup);
- }
- g_free(tmp);
- g_free(tmp2);
-}
-
-static void toggle_button_cb(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons, GtkIMHtmlToolbar *toolbar)
-{
- update_buttons(toolbar);
-}
-
-static void update_format_cb(GtkIMHtml *imhtml, GtkIMHtmlToolbar *toolbar) {
- update_buttons(toolbar);
-}
-
-static void mark_set_cb(GtkTextBuffer *buffer, GtkTextIter *location,
- GtkTextMark *mark, GtkIMHtmlToolbar *toolbar)
-{
- if(mark != gtk_text_buffer_get_insert(buffer))
- return;
-
- update_buttons(toolbar);
-}
-
-
-/* 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);
- GtkRequisition menu_req;
- gint ythickness = widget->style->ythickness;
- int savy;
-
- gtk_widget_size_request(GTK_WIDGET (menu), &menu_req);
- gdk_window_get_origin(widget->window, x, y);
- *x += widget->allocation.x;
- *y += widget->allocation.y + widget->allocation.height;
- savy = *y;
-
- pidgin_menu_position_func_helper(menu, x, y, push_in, data);
-
- if (savy > *y + ythickness + 1)
- *y -= widget->allocation.height;
-}
-
-static gboolean
-button_activate_on_click(GtkWidget *button, GdkEventButton *event, GtkIMHtmlToolbar *toolbar)
-{
- if (event->button == 1 && GTK_IS_TOGGLE_BUTTON(button))
- gtk_widget_activate(button);
- else if (event->button == 3)
- return gtk_imhtmltoolbar_popup_menu(button, event, toolbar);
- return FALSE;
-}
-
-static void pidgin_menu_clicked(GtkWidget *button, GtkMenu *menu)
-{
- gtk_widget_show_all(GTK_WIDGET(menu));
- gtk_menu_popup(menu, NULL, NULL, menu_position_func, button, 0, gtk_get_current_event_time());
-}
-
-static void pidgin_menu_deactivate(GtkWidget *menu, GtkToggleButton *button)
-{
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
-}
-
-enum {
- LAST_SIGNAL
-};
-/* static guint signals [LAST_SIGNAL] = { 0 }; */
-
-static void
-gtk_imhtmltoolbar_finalize (GObject *object)
-{
- GtkIMHtmlToolbar *toolbar = GTK_IMHTMLTOOLBAR(object);
- GtkWidget *menu;
-
- if (toolbar->image_dialog != NULL)
- {
- gtk_widget_destroy(toolbar->image_dialog);
- toolbar->image_dialog = NULL;
- }
-
- destroy_toolbar_font(NULL, NULL, toolbar);
- if (toolbar->smiley_dialog != NULL) {
- g_signal_handlers_disconnect_by_func(G_OBJECT(toolbar->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->imhtml) {
- g_signal_handlers_disconnect_matched(toolbar->imhtml,
- G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
- toolbar);
- g_signal_handlers_disconnect_matched(GTK_IMHTML(toolbar->imhtml)->text_buffer,
- G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
- toolbar);
- }
-
- g_free(toolbar->sml);
- gtk_object_sink(GTK_OBJECT(toolbar->tooltips));
-
- menu = g_object_get_data(object, "font_menu");
- if (menu)
- gtk_widget_destroy(menu);
- menu = g_object_get_data(object, "insert_menu");
- if (menu)
- gtk_widget_destroy(menu);
-
- purple_prefs_disconnect_by_handle(object);
-
- G_OBJECT_CLASS(parent_class)->finalize (object);
-}
-
-static void
-switch_toolbar_view(GtkWidget *item, GtkIMHtmlToolbar *toolbar)
-{
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/toolbar/wide",
- !purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/toolbar/wide"));
-}
-
-static gboolean
-gtk_imhtmltoolbar_popup_menu(GtkWidget *widget, GdkEventButton *event, GtkIMHtmlToolbar *toolbar)
-{
- GtkWidget *menu;
- GtkWidget *item;
- gboolean wide;
-
- if (event->button != 3)
- return FALSE;
-
- wide = GTK_WIDGET_VISIBLE(toolbar->bold);
-
- 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(GTK_MENU(menu), NULL, NULL, pidgin_menu_position_func_helper,
- widget, event->button, event->time);
- return TRUE;
-}
-
-/* Boring GTK+ stuff */
-static void gtk_imhtmltoolbar_class_init (GtkIMHtmlToolbarClass *class)
-{
- GObjectClass *gobject_class;
- gobject_class = (GObjectClass*) class;
- parent_class = g_type_class_ref(GTK_TYPE_HBOX);
- gobject_class->finalize = gtk_imhtmltoolbar_finalize;
-
- purple_prefs_add_none(PIDGIN_PREFS_ROOT "/conversations/toolbar");
- purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/toolbar/wide", FALSE);
-}
-
-static void gtk_imhtmltoolbar_create_old_buttons(GtkIMHtmlToolbar *toolbar)
-{
- GtkWidget *hbox;
- GtkWidget *button;
- struct {
- char *stock;
- gpointer callback;
- GtkWidget **button;
- const char *tooltip;
- } buttons[] = {
- {GTK_STOCK_BOLD, G_CALLBACK(do_bold), &toolbar->bold, _("Bold")},
- {GTK_STOCK_ITALIC, do_italic, &toolbar->italic, _("Italic")},
- {GTK_STOCK_UNDERLINE, do_underline, &toolbar->underline, _("Underline")},
- {GTK_STOCK_STRIKETHROUGH, do_strikethrough, &toolbar->strikethrough, _("Strikethrough")},
- {"", NULL, NULL, NULL},
- {PIDGIN_STOCK_TOOLBAR_TEXT_LARGER, do_big, &toolbar->larger_size, _("Increase Font Size")},
- {PIDGIN_STOCK_TOOLBAR_TEXT_SMALLER, do_small, &toolbar->smaller_size, _("Decrease Font Size")},
- {"", NULL, NULL, NULL},
- {PIDGIN_STOCK_TOOLBAR_FONT_FACE, toggle_font, &toolbar->font, _("Font Face")},
- {PIDGIN_STOCK_TOOLBAR_FGCOLOR, toggle_fg_color, &toolbar->fgcolor, _("Foreground Color")},
- {PIDGIN_STOCK_TOOLBAR_BGCOLOR, toggle_bg_color, &toolbar->bgcolor, _("Background Color")},
- {"", NULL, NULL, NULL},
- {PIDGIN_STOCK_CLEAR, clear_formatting_cb, &toolbar->clear, _("Reset Formatting")},
- {"", NULL, NULL, NULL},
- {PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE, insert_image_cb, &toolbar->image, _("Insert IM Image")},
- {PIDGIN_STOCK_TOOLBAR_INSERT_LINK, insert_link_cb, &toolbar->link, _("Insert Link")},
- {"", NULL, NULL, NULL},
- {PIDGIN_STOCK_TOOLBAR_SMILEY, insert_smiley_cb, &toolbar->smiley, _("Insert Smiley")},
- {NULL, NULL, NULL, NULL}
- };
- int iter;
-
- hbox = gtk_hbox_new(FALSE, 0);
-
- for (iter = 0; buttons[iter].stock; iter++) {
- if (buttons[iter].stock[0]) {
- button = pidgin_pixbuf_toolbar_button_from_stock(buttons[iter].stock);
- g_signal_connect(G_OBJECT(button), "button-press-event", G_CALLBACK(gtk_imhtmltoolbar_popup_menu), toolbar);
- g_signal_connect(G_OBJECT(button), "clicked",
- G_CALLBACK(buttons[iter].callback), toolbar);
- *(buttons[iter].button) = button;
- gtk_tooltips_set_tip(toolbar->tooltips, button, buttons[iter].tooltip, NULL);
- } else
- button = gtk_vseparator_new();
- gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
- }
- /* create the attention button (this is a bit hacky to not break ABI) */
- button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_SEND_ATTENTION);
- g_signal_connect(G_OBJECT(button), "button-press-event", G_CALLBACK(gtk_imhtmltoolbar_popup_menu), toolbar);
- g_signal_connect(G_OBJECT(button), "clicked",
- G_CALLBACK(send_attention_cb), toolbar);
- g_object_set_data(G_OBJECT(toolbar), "attention", button);
- gtk_tooltips_set_tip(toolbar->tooltips, button, _("Send Attention"), NULL);
- gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
-
- gtk_box_pack_start(GTK_BOX(toolbar), hbox, FALSE, FALSE, 0);
- g_object_set_data(G_OBJECT(toolbar), "wide-view", hbox);
-}
-
-static void
-button_visibility_changed(GtkWidget *button, gpointer dontcare, GtkWidget *item)
-{
- if (GTK_WIDGET_VISIBLE(button))
- gtk_widget_hide(item);
- else
- gtk_widget_show(item);
-}
-
-static void
-button_sensitiveness_changed(GtkWidget *button, gpointer dontcare, GtkWidget *item)
-{
- gtk_widget_set_sensitive(item, GTK_WIDGET_IS_SENSITIVE(button));
-}
-
-static void
-update_menuitem(GtkToggleButton *button, GtkCheckMenuItem *item)
-{
- g_signal_handlers_block_by_func(G_OBJECT(item), G_CALLBACK(gtk_button_clicked), button);
- gtk_check_menu_item_set_active(item, gtk_toggle_button_get_active(button));
- g_signal_handlers_unblock_by_func(G_OBJECT(item), G_CALLBACK(gtk_button_clicked), button);
-}
-
-static void
-enable_markup(GtkWidget *widget, gpointer null)
-{
- if (GTK_IS_LABEL(widget))
- g_object_set(G_OBJECT(widget), "use-markup", TRUE, NULL);
-}
-
-static void
-imhtmltoolbar_view_pref_changed(const char *name, PurplePrefType type,
- gconstpointer value, gpointer toolbar)
-{
- if (value) {
- gtk_widget_hide_all(g_object_get_data(G_OBJECT(toolbar), "lean-view"));
- gtk_widget_show_all(g_object_get_data(G_OBJECT(toolbar), "wide-view"));
- } else {
- gtk_widget_hide_all(g_object_get_data(G_OBJECT(toolbar), "wide-view"));
- gtk_widget_show_all(g_object_get_data(G_OBJECT(toolbar), "lean-view"));
- }
-}
-
-static void gtk_imhtmltoolbar_init (GtkIMHtmlToolbar *toolbar)
-{
- GtkWidget *hbox = GTK_WIDGET(toolbar), *event = gtk_event_box_new();
- GtkWidget *bbox, *box = gtk_hbox_new(FALSE, 0);
- GtkWidget *image;
- GtkWidget *label;
- GtkWidget *insert_button;
- GtkWidget *font_button;
- GtkWidget *smiley_button;
- GtkWidget *attention_button;
- GtkWidget *font_menu;
- GtkWidget *insert_menu;
- GtkWidget *menuitem;
- GtkWidget *sep;
- GObject *wide_attention_button;
- int i;
- struct {
- const char *label;
- GtkWidget **button;
- gboolean check;
- } buttons[] = {
- {_("<b>_Bold</b>"), &toolbar->bold, TRUE},
- {_("<i>_Italic</i>"), &toolbar->italic, TRUE},
- {_("<u>_Underline</u>"), &toolbar->underline, TRUE},
- {_("<span strikethrough='true'>Strikethrough</span>"), &toolbar->strikethrough, TRUE},
- {_("<span size='larger'>_Larger</span>"), &toolbar->larger_size, TRUE},
-#if 0
- {_("_Normal"), &toolbar->normal_size, TRUE},
-#endif
- {_("<span size='smaller'>_Smaller</span>"), &toolbar->smaller_size, TRUE},
- /* If we want to show the formatting for the following items, we would
- * need to update them when formatting changes. The above items don't need
- * no updating nor nothin' */
- {_("_Font face"), &toolbar->font, TRUE},
- {_("Foreground _color"), &toolbar->fgcolor, TRUE},
- {_("Bac_kground color"), &toolbar->bgcolor, TRUE},
- {_("_Reset formatting"), &toolbar->clear, FALSE},
- {NULL, NULL, FALSE}
- };
-
- toolbar->imhtml = NULL;
- toolbar->font_dialog = NULL;
- toolbar->fgcolor_dialog = NULL;
- toolbar->bgcolor_dialog = NULL;
- toolbar->link_dialog = NULL;
- toolbar->smiley_dialog = NULL;
- toolbar->image_dialog = NULL;
-
- toolbar->tooltips = gtk_tooltips_new();
-
- gtk_box_set_spacing(GTK_BOX(toolbar), 3);
-
- gtk_imhtmltoolbar_create_old_buttons(toolbar);
-
- /* Fonts */
- font_button = gtk_toggle_button_new();
- gtk_button_set_relief(GTK_BUTTON(font_button), GTK_RELIEF_NONE);
- bbox = gtk_hbox_new(FALSE, 3);
- gtk_container_add(GTK_CONTAINER(font_button), bbox);
- image = gtk_image_new_from_stock(GTK_STOCK_BOLD, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
- gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0);
- label = gtk_label_new_with_mnemonic(_("_Font"));
- gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
- g_object_set_data(G_OBJECT(hbox), "font_label", label);
- gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0);
- gtk_box_pack_start(GTK_BOX(box), font_button, FALSE, FALSE, 0);
- gtk_widget_show_all(font_button);
-
- font_menu = gtk_menu_new();
- g_object_set_data(G_OBJECT(toolbar), "font_menu", font_menu);
-
- for (i = 0; buttons[i].label; i++) {
- GtkWidget *old = *buttons[i].button;
- if (buttons[i].check) {
- menuitem = gtk_check_menu_item_new_with_mnemonic(buttons[i].label);
- g_signal_connect_after(G_OBJECT(old), "toggled",
- G_CALLBACK(update_menuitem), menuitem);
- } else {
- menuitem = gtk_menu_item_new_with_mnemonic(buttons[i].label);
- }
- g_signal_connect_swapped(G_OBJECT(menuitem), "activate",
- G_CALLBACK(gtk_button_clicked), old);
- gtk_menu_shell_append(GTK_MENU_SHELL(font_menu), menuitem);
- g_signal_connect(G_OBJECT(old), "notify::sensitive",
- G_CALLBACK(button_sensitiveness_changed), menuitem);
- g_signal_connect(G_OBJECT(old), "notify::visible",
- G_CALLBACK(button_visibility_changed), menuitem);
- gtk_container_foreach(GTK_CONTAINER(menuitem), (GtkCallback)enable_markup, NULL);
- }
-
- g_signal_connect(G_OBJECT(font_button), "button-press-event", G_CALLBACK(button_activate_on_click), toolbar);
- g_signal_connect(G_OBJECT(font_button), "activate", G_CALLBACK(pidgin_menu_clicked), font_menu);
- g_signal_connect(G_OBJECT(font_menu), "deactivate", G_CALLBACK(pidgin_menu_deactivate), font_button);
-
- /* Sep */
- sep = gtk_vseparator_new();
- gtk_box_pack_start(GTK_BOX(box), sep, FALSE, FALSE, 0);
- gtk_widget_show_all(sep);
-
- /* Insert */
- insert_button = gtk_toggle_button_new();
- gtk_button_set_relief(GTK_BUTTON(insert_button), GTK_RELIEF_NONE);
- bbox = gtk_hbox_new(FALSE, 3);
- gtk_container_add(GTK_CONTAINER(insert_button), bbox);
- image = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_INSERT, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
- gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0);
- label = gtk_label_new_with_mnemonic(_("_Insert"));
- gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0);
- gtk_box_pack_start(GTK_BOX(box), insert_button, FALSE, FALSE, 0);
- gtk_widget_show_all(insert_button);
-
- insert_menu = gtk_menu_new();
- g_object_set_data(G_OBJECT(toolbar), "insert_menu", insert_menu);
-
- menuitem = gtk_menu_item_new_with_mnemonic(_("_Image"));
- g_signal_connect_swapped(G_OBJECT(menuitem), "activate", G_CALLBACK(gtk_button_clicked), toolbar->image);
- gtk_menu_shell_append(GTK_MENU_SHELL(insert_menu), menuitem);
- g_signal_connect(G_OBJECT(toolbar->image), "notify::sensitive",
- G_CALLBACK(button_sensitiveness_changed), menuitem);
- g_signal_connect(G_OBJECT(toolbar->image), "notify::visible",
- G_CALLBACK(button_visibility_changed), menuitem);
-
- menuitem = gtk_menu_item_new_with_mnemonic(_("_Link"));
- g_signal_connect_swapped(G_OBJECT(menuitem), "activate", G_CALLBACK(gtk_button_clicked), toolbar->link);
- gtk_menu_shell_append(GTK_MENU_SHELL(insert_menu), menuitem);
- g_signal_connect(G_OBJECT(toolbar->link), "notify::sensitive",
- G_CALLBACK(button_sensitiveness_changed), menuitem);
- g_signal_connect(G_OBJECT(toolbar->link), "notify::visible",
- G_CALLBACK(button_visibility_changed), menuitem);
-
- menuitem = gtk_menu_item_new_with_mnemonic(_("_Horizontal rule"));
- g_signal_connect(G_OBJECT(menuitem), "activate" , G_CALLBACK(insert_hr_cb), toolbar);
- gtk_menu_shell_append(GTK_MENU_SHELL(insert_menu), menuitem);
- toolbar->insert_hr = menuitem;
-
- g_signal_connect(G_OBJECT(insert_button), "button-press-event", G_CALLBACK(button_activate_on_click), toolbar);
- g_signal_connect(G_OBJECT(insert_button), "activate", G_CALLBACK(pidgin_menu_clicked), insert_menu);
- g_signal_connect(G_OBJECT(insert_menu), "deactivate", G_CALLBACK(pidgin_menu_deactivate), insert_button);
- toolbar->sml = NULL;
-
- /* Sep */
- sep = gtk_vseparator_new();
- gtk_box_pack_start(GTK_BOX(box), sep, FALSE, FALSE, 0);
- gtk_widget_show_all(sep);
-
- /* Smiley */
- smiley_button = gtk_button_new();
- gtk_button_set_relief(GTK_BUTTON(smiley_button), GTK_RELIEF_NONE);
- bbox = gtk_hbox_new(FALSE, 3);
- gtk_container_add(GTK_CONTAINER(smiley_button), bbox);
- image = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_SMILEY, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
- gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0);
- label = gtk_label_new_with_mnemonic(_("_Smile!"));
- gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0);
- gtk_box_pack_start(GTK_BOX(box), smiley_button, FALSE, FALSE, 0);
- g_signal_connect(G_OBJECT(smiley_button), "button-press-event", G_CALLBACK(gtk_imhtmltoolbar_popup_menu), toolbar);
- g_signal_connect_swapped(G_OBJECT(smiley_button), "clicked", G_CALLBACK(gtk_button_clicked), toolbar->smiley);
- gtk_widget_show_all(smiley_button);
-
- /* Sep */
- sep = gtk_vseparator_new();
- gtk_box_pack_start(GTK_BOX(box), sep, FALSE, FALSE, 0);
- gtk_widget_show_all(sep);
-
- /* Attention */
- wide_attention_button = g_object_get_data(G_OBJECT(toolbar), "attention");
-
- attention_button = gtk_button_new();
- gtk_button_set_relief(GTK_BUTTON(attention_button), GTK_RELIEF_NONE);
- bbox = gtk_hbox_new(FALSE, 3);
- gtk_container_add(GTK_CONTAINER(attention_button), bbox);
- image = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_SEND_ATTENTION,
- gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
- gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0);
- label = gtk_label_new_with_mnemonic(_("_Attention!"));
- gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0);
- gtk_box_pack_start(GTK_BOX(box), attention_button, FALSE, FALSE, 0);
- g_signal_connect_swapped(G_OBJECT(attention_button), "clicked",
- G_CALLBACK(gtk_button_clicked), wide_attention_button);
- gtk_widget_show_all(attention_button);
-
- g_signal_connect(wide_attention_button, "notify::sensitive",
- G_CALLBACK(button_sensitiveness_changed), attention_button);
- g_signal_connect(wide_attention_button, "notify::visible",
- G_CALLBACK(button_visibility_changed), attention_button);
-
- /* set attention button to be greyed out until we get a conversation */
- gtk_widget_set_sensitive(GTK_WIDGET(wide_attention_button), FALSE);
-
- gtk_box_pack_start(GTK_BOX(hbox), box, FALSE, FALSE, 0);
- g_object_set_data(G_OBJECT(hbox), "lean-view", box);
- gtk_widget_show(box);
-
- purple_prefs_connect_callback(toolbar, PIDGIN_PREFS_ROOT "/conversations/toolbar/wide",
- imhtmltoolbar_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);
-
- gtk_event_box_set_visible_window(GTK_EVENT_BOX(event), FALSE);
-
- gtk_widget_add_events(event, GDK_BUTTON_PRESS_MASK);
- gtk_box_pack_start(GTK_BOX(hbox), event, TRUE, TRUE, 0);
- g_signal_connect(G_OBJECT(event), "button-press-event", G_CALLBACK(gtk_imhtmltoolbar_popup_menu), toolbar);
- gtk_widget_show(event);
-}
-
-GtkWidget *gtk_imhtmltoolbar_new()
-{
- return GTK_WIDGET(g_object_new(gtk_imhtmltoolbar_get_type(), NULL));
-}
-
-GType gtk_imhtmltoolbar_get_type()
-{
- static GType imhtmltoolbar_type = 0;
-
- if (!imhtmltoolbar_type) {
- static const GTypeInfo imhtmltoolbar_info = {
- sizeof(GtkIMHtmlToolbarClass),
- NULL,
- NULL,
- (GClassInitFunc) gtk_imhtmltoolbar_class_init,
- NULL,
- NULL,
- sizeof (GtkIMHtmlToolbar),
- 0,
- (GInstanceInitFunc) gtk_imhtmltoolbar_init,
- NULL
- };
-
- imhtmltoolbar_type = g_type_register_static(GTK_TYPE_HBOX,
- "GtkIMHtmlToolbar", &imhtmltoolbar_info, 0);
- }
-
- return imhtmltoolbar_type;
-}
-
-
-void gtk_imhtmltoolbar_attach(GtkIMHtmlToolbar *toolbar, GtkWidget *imhtml)
-{
- GtkIMHtmlButtons buttons;
-
- g_return_if_fail(toolbar != NULL);
- g_return_if_fail(GTK_IS_IMHTMLTOOLBAR(toolbar));
- g_return_if_fail(imhtml != NULL);
- g_return_if_fail(GTK_IS_IMHTML(imhtml));
-
- toolbar->imhtml = imhtml;
- g_signal_connect(G_OBJECT(imhtml), "format_buttons_update", G_CALLBACK(update_buttons_cb), toolbar);
- g_signal_connect_after(G_OBJECT(imhtml), "format_function_toggle", G_CALLBACK(toggle_button_cb), toolbar);
- g_signal_connect_after(G_OBJECT(imhtml), "format_function_clear", G_CALLBACK(update_format_cb), toolbar);
- g_signal_connect(G_OBJECT(imhtml), "format_function_update", G_CALLBACK(update_format_cb), toolbar);
- g_signal_connect_after(G_OBJECT(GTK_IMHTML(imhtml)->text_buffer), "mark-set", G_CALLBACK(mark_set_cb), toolbar);
-
- buttons = gtk_imhtml_get_format_functions(GTK_IMHTML(imhtml));
- update_buttons_cb(GTK_IMHTML(imhtml), buttons, toolbar);
- update_buttons(toolbar);
-}
-
-void gtk_imhtmltoolbar_associate_smileys(GtkIMHtmlToolbar *toolbar, const char *proto_id)
-{
- g_free(toolbar->sml);
- toolbar->sml = g_strdup(proto_id);
-}
-
-void gtk_imhtmltoolbar_switch_active_conversation(GtkIMHtmlToolbar *toolbar,
- PurpleConversation *conv)
-{
- PurpleConnection *gc = purple_conversation_get_gc(conv);
- PurplePlugin *prpl = purple_connection_get_prpl(gc);
- GtkWidget *attention =
- g_object_get_data(G_OBJECT(toolbar), "attention");
-
- g_object_set_data(G_OBJECT(toolbar), "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_widget_set_sensitive(attention,
- conv && prpl && purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM &&
- PURPLE_PLUGIN_PROTOCOL_INFO(prpl)->send_attention != NULL);
-}
-
diff --git a/pidgin/gtkimhtmltoolbar.h b/pidgin/gtkimhtmltoolbar.h
deleted file mode 100644
index d31f46c22a..0000000000
--- a/pidgin/gtkimhtmltoolbar.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * GtkIMHtmlToolbar
- *
- * 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 _PIDGINIMHTMLTOOLBAR_H_
-#define _PIDGINIMHTMLTOOLBAR_H_
-
-#include <gtk/gtk.h>
-#include "gtkimhtml.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define DEFAULT_FONT_FACE "Helvetica 12"
-
-#define GTK_TYPE_IMHTMLTOOLBAR (gtk_imhtmltoolbar_get_type())
-#define GTK_IMHTMLTOOLBAR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_IMHTMLTOOLBAR, GtkIMHtmlToolbar))
-#define GTK_IMHTMLTOOLBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_IMHTMLTOOLBAR, GtkIMHtmlToolbarClass))
-#define GTK_IS_IMHTMLTOOLBAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_IMHTMLTOOLBAR))
-#define GTK_IS_IMHTMLTOOLBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_IMHTMLTOOLBAR))
-
-typedef struct _GtkIMHtmlToolbar GtkIMHtmlToolbar;
-typedef struct _GtkIMHtmlToolbarClass GtkIMHtmlToolbarClass;
-
-struct _GtkIMHtmlToolbar {
- GtkHBox box;
-
- GtkWidget *imhtml;
-
- GtkTooltips *tooltips;
-
- GtkWidget *bold;
- GtkWidget *italic;
- GtkWidget *underline;
-
- GtkWidget *larger_size;
- GtkWidget *normal_size;
- GtkWidget *smaller_size;
-
- GtkWidget *font;
- GtkWidget *fgcolor;
- GtkWidget *bgcolor;
-
- GtkWidget *clear;
-
- GtkWidget *image;
- GtkWidget *link;
- GtkWidget *smiley;
-
- GtkWidget *font_dialog;
- GtkWidget *fgcolor_dialog;
- GtkWidget *bgcolor_dialog;
- GtkWidget *link_dialog;
- GtkWidget *smiley_dialog;
- GtkWidget *image_dialog;
-
- char *sml;
- GtkWidget *strikethrough;
- GtkWidget *insert_hr;
- GtkWidget *call;
-};
-
-struct _GtkIMHtmlToolbarClass {
- GtkHBoxClass parent_class;
-
-};
-
-GType gtk_imhtmltoolbar_get_type (void);
-GtkWidget* gtk_imhtmltoolbar_new (void);
-
-void gtk_imhtmltoolbar_attach (GtkIMHtmlToolbar *toolbar, GtkWidget *imhtml);
-void gtk_imhtmltoolbar_associate_smileys (GtkIMHtmlToolbar *toolbar, const char *proto_id);
-
-/**
- * @since 2.7.0
- */
-void gtk_imhtmltoolbar_switch_active_conversation(GtkIMHtmlToolbar *toolbar,
- PurpleConversation *conv);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _PIDGINIMHTMLTOOLBAR_H_ */
diff --git a/pidgin/gtkinternal.h b/pidgin/gtkinternal.h
new file mode 100644
index 0000000000..75035f28bd
--- /dev/null
+++ b/pidgin/gtkinternal.h
@@ -0,0 +1,47 @@
+/* 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_INTERNAL_H_
+#define _PIDGIN_INTERNAL_H_
+/*
+ * SECTION:gtkinternal
+ * @section_id: pidgin-gtkinternal
+ * @short_description: <filename>gtkinternal.h</filename>
+ * @title: Internal Definitions and Includes
+ */
+
+G_BEGIN_DECLS
+
+static inline void
+_pidgin_widget_set_accessible_name(GtkWidget *widget, const gchar *name);
+
+PurpleImage *
+_pidgin_e2ee_stock_icon_get(const gchar *stock_name);
+
+G_END_DECLS
+
+static inline void
+_pidgin_widget_set_accessible_name(GtkWidget *widget, const gchar *name)
+{
+ atk_object_set_name(gtk_widget_get_accessible(widget), name);
+}
+
+#endif /* _PIDGIN_INTERNAL_H_ */
diff --git a/pidgin/gtklog.c b/pidgin/gtklog.c
index 03eafea121..bca1523583 100644
--- a/pidgin/gtklog.c
+++ b/pidgin/gtklog.c
@@ -1,8 +1,3 @@
-/**
- * @file gtklog.c GTK+ Log viewer
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -35,9 +30,11 @@
#include "pidginstock.h"
#include "gtkblist.h"
-#include "gtkimhtml.h"
#include "gtklog.h"
#include "gtkutils.h"
+#include "gtkwebview.h"
+
+#include "gtk3compat.h"
static GHashTable *log_viewers = NULL;
static void populate_log_tree(PidginLogViewer *lv);
@@ -130,7 +127,7 @@ static void search_cb(GtkWidget *button, PidginLogViewer *lv)
populate_log_tree(lv);
g_free(lv->search);
lv->search = NULL;
- gtk_imhtml_search_clear(GTK_IMHTML(lv->imhtml));
+ webkit_web_view_unmark_text_matches(WEBKIT_WEB_VIEW(lv->web_view));
select_first_log(lv);
return;
}
@@ -138,7 +135,7 @@ static void search_cb(GtkWidget *button, PidginLogViewer *lv)
if (lv->search != NULL && !strcmp(lv->search, search_term))
{
/* Searching for the same term acts as "Find Next" */
- gtk_imhtml_search_find(GTK_IMHTML(lv->imhtml), lv->search);
+ webkit_web_view_search_text(WEBKIT_WEB_VIEW(lv->web_view), lv->search, FALSE, TRUE, TRUE);
return;
}
@@ -148,7 +145,7 @@ static void search_cb(GtkWidget *button, PidginLogViewer *lv)
lv->search = g_strdup(search_term);
gtk_tree_store_clear(lv->treestore);
- gtk_imhtml_clear(GTK_IMHTML(lv->imhtml));
+ webkit_web_view_open(WEBKIT_WEB_VIEW(lv->web_view), "about:blank"); /* clear the view */
for (logs = lv->logs; logs != NULL; logs = logs->next) {
char *read = purple_log_read((PurpleLog*)logs->data, NULL);
@@ -243,7 +240,7 @@ static void delete_log_cb(gpointer *data)
if (!purple_log_delete((PurpleLog *)data[2]))
{
purple_notify_error(NULL, NULL, _("Log Deletion Failed"),
- _("Check permissions and try again."));
+ _("Check permissions and try again."), NULL);
}
else
{
@@ -280,7 +277,7 @@ static void log_delete_log_cb(GtkWidget *menuitem, gpointer *data)
if (log->type == PURPLE_LOG_IM)
{
- PurpleBuddy *buddy = purple_find_buddy(log->account, log->name);
+ PurpleBuddy *buddy = purple_blist_find_buddy(log->account, log->name);
if (buddy != NULL)
name = purple_buddy_get_contact_alias(buddy);
else
@@ -318,7 +315,7 @@ static void log_delete_log_cb(GtkWidget *menuitem, gpointer *data)
data2[1] = data[3]; /* iter */
data2[2] = log;
purple_request_action(lv, NULL, _("Delete Log?"), tmp, 0,
- NULL, NULL, NULL,
+ NULL,
data2, 2,
_("Delete"), delete_log_cb,
_("Cancel"), delete_log_cleanup_cb);
@@ -419,8 +416,9 @@ static gboolean log_popup_menu_cb(GtkWidget *treeview, PidginLogViewer *lv)
static gboolean search_find_cb(gpointer data)
{
PidginLogViewer *viewer = data;
- gtk_imhtml_search_find(GTK_IMHTML(viewer->imhtml), viewer->search);
- g_object_steal_data(G_OBJECT(viewer->entry), "search-find-cb");
+ webkit_web_view_mark_text_matches(WEBKIT_WEB_VIEW(viewer->web_view), viewer->search, FALSE, 0);
+ webkit_web_view_set_highlight_text_matches(WEBKIT_WEB_VIEW(viewer->web_view), TRUE);
+ webkit_web_view_search_text(WEBKIT_WEB_VIEW(viewer->web_view), viewer->search, FALSE, TRUE, TRUE);
return FALSE;
}
@@ -454,30 +452,31 @@ static void log_select_cb(GtkTreeSelection *sel, PidginLogViewer *viewer) {
title = g_strdup_printf(_("<span size='larger' weight='bold'>Conversation with %s on %s</span>"),
log->name, log_get_date(log));
- gtk_label_set_markup(GTK_LABEL(viewer->label), title);
+ gtk_label_set_markup(viewer->label, title);
g_free(title);
}
read = purple_log_read(log, &flags);
viewer->flags = flags;
- gtk_imhtml_clear(GTK_IMHTML(viewer->imhtml));
- gtk_imhtml_set_protocol_name(GTK_IMHTML(viewer->imhtml),
- purple_account_get_protocol_name(log->account));
+ webkit_web_view_open(WEBKIT_WEB_VIEW(viewer->web_view), "about:blank");
purple_signal_emit(pidgin_log_get_handle(), "log-displaying", viewer, log);
+
+ /* plaintext log (html one starts with <html> tag) */
+ if (read[0] != '<')
+ {
+ char *newRead = purple_strreplace(read, "\n", "<br>");
+ g_free(read);
+ read = newRead;
+ }
- gtk_imhtml_append_text(GTK_IMHTML(viewer->imhtml), read,
- GTK_IMHTML_NO_COMMENTS | GTK_IMHTML_NO_TITLE | GTK_IMHTML_NO_SCROLL |
- ((flags & PURPLE_LOG_READ_NO_NEWLINE) ? GTK_IMHTML_NO_NEWLINE : 0));
+ webkit_web_view_load_html_string(WEBKIT_WEB_VIEW(viewer->web_view), read, "");
g_free(read);
if (viewer->search != NULL) {
- guint source;
- gtk_imhtml_search_clear(GTK_IMHTML(viewer->imhtml));
- source = g_idle_add(search_find_cb, viewer);
- g_object_set_data_full(G_OBJECT(viewer->entry), "search-find-cb",
- GINT_TO_POINTER(source), (GDestroyNotify)g_source_remove);
+ webkit_web_view_unmark_text_matches(WEBKIT_WEB_VIEW(viewer->web_view));
+ g_idle_add(search_find_cb, viewer);
}
pidgin_clear_cursor(viewer->window);
@@ -563,7 +562,7 @@ static PidginLogViewer *display_log_viewer(struct log_viewer_hash_t *ht, GList *
if(icon != NULL)
gtk_widget_destroy(icon);
- purple_notify_info(NULL, title, _("No logs were found"), log_preferences);
+ purple_notify_info(NULL, title, _("No logs were found"), log_preferences, NULL);
return NULL;
}
@@ -581,36 +580,37 @@ static PidginLogViewer *display_log_viewer(struct log_viewer_hash_t *ht, GList *
gtk_dialog_add_button(GTK_DIALOG(lv->window), _("_Browse logs folder"), GTK_RESPONSE_HELP);
#endif
gtk_container_set_border_width (GTK_CONTAINER(lv->window), PIDGIN_HIG_BOX_SPACE);
- gtk_dialog_set_has_separator(GTK_DIALOG(lv->window), FALSE);
- gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(lv->window)->vbox), 0);
+ gtk_box_set_spacing(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(lv->window))), 0);
g_signal_connect(G_OBJECT(lv->window), "response",
G_CALLBACK(destroy_cb), ht);
gtk_window_set_role(GTK_WINDOW(lv->window), "log_viewer");
/* Icon *************/
if (icon != NULL) {
- title_box = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ title_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtk_container_set_border_width(GTK_CONTAINER(title_box), PIDGIN_HIG_BOX_SPACE);
- gtk_box_pack_start(GTK_BOX(GTK_DIALOG(lv->window)->vbox), title_box, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(lv->window))),
+ title_box, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(title_box), icon, FALSE, FALSE, 0);
} else
- title_box = GTK_DIALOG(lv->window)->vbox;
+ title_box = gtk_dialog_get_content_area(GTK_DIALOG(lv->window));
/* Label ************/
- lv->label = gtk_label_new(NULL);
+ lv->label = GTK_LABEL(gtk_label_new(NULL));
text = g_strdup_printf("<span size='larger' weight='bold'>%s</span>", title);
- gtk_label_set_markup(GTK_LABEL(lv->label), text);
+ gtk_label_set_markup(lv->label, text);
gtk_misc_set_alignment(GTK_MISC(lv->label), 0, 0);
- gtk_box_pack_start(GTK_BOX(title_box), lv->label, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(title_box), GTK_WIDGET(lv->label), FALSE, FALSE, 0);
g_free(text);
/* Pane *************/
- pane = gtk_hpaned_new();
+ pane = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
gtk_container_set_border_width(GTK_CONTAINER(pane), PIDGIN_HIG_BOX_SPACE);
- gtk_box_pack_start(GTK_BOX(GTK_DIALOG(lv->window)->vbox), pane, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(lv->window))),
+ pane, TRUE, TRUE, 0);
/* List *************/
lv->treestore = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
@@ -620,7 +620,7 @@ static PidginLogViewer *display_log_viewer(struct log_viewer_hash_t *ht, GList *
col = gtk_tree_view_column_new_with_attributes ("time", rend, "markup", 0, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(lv->treeview), col);
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (lv->treeview), FALSE);
- gtk_paned_add1(GTK_PANED(pane),
+ gtk_paned_add1(GTK_PANED(pane),
pidgin_make_scrollable(lv->treeview, GTK_POLICY_NEVER, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, -1));
populate_log_tree(lv);
@@ -645,24 +645,25 @@ static PidginLogViewer *display_log_viewer(struct log_viewer_hash_t *ht, GList *
gtk_label_set_markup(GTK_LABEL(size_label), text);
/* gtk_paned_add1(GTK_PANED(pane), size_label); */
gtk_misc_set_alignment(GTK_MISC(size_label), 0, 0);
- gtk_box_pack_end(GTK_BOX(GTK_DIALOG(lv->window)->vbox), size_label, FALSE, FALSE, 0);
+ gtk_box_pack_end(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(lv->window))),
+ size_label, FALSE, FALSE, 0);
g_free(sz_txt);
g_free(text);
}
/* A fancy little box ************/
- vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
gtk_paned_add2(GTK_PANED(pane), vbox);
/* Viewer ************/
- frame = pidgin_create_imhtml(FALSE, &lv->imhtml, NULL, NULL);
- gtk_widget_set_name(lv->imhtml, "pidgin_log_imhtml");
- gtk_widget_set_size_request(lv->imhtml, 320, 200);
+ frame = pidgin_create_webview(FALSE, &lv->web_view, NULL);
+ gtk_widget_set_name(lv->web_view, "pidgin_log_web_view");
+ gtk_widget_set_size_request(lv->web_view, 320, 200);
gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
gtk_widget_show(frame);
/* Search box **********/
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
lv->entry = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(hbox), lv->entry, TRUE, TRUE, 0);
@@ -714,7 +715,7 @@ void pidgin_log_show(PurpleLogType type, const char *buddyname, PurpleAccount *a
} else {
PurpleBuddy *buddy;
- buddy = purple_find_buddy(account, buddyname);
+ buddy = purple_blist_find_buddy(account, buddyname);
if (buddy != NULL)
name = purple_buddy_get_contact_alias(buddy);
@@ -763,7 +764,7 @@ void pidgin_log_show_contact(PurpleContact *contact) {
const char *buddy_name;
PurpleAccount *account;
- if (!PURPLE_BLIST_NODE_IS_BUDDY(child))
+ if (!PURPLE_IS_BUDDY(child))
continue;
buddy_name = purple_buddy_get_name((PurpleBuddy *)child);
@@ -784,17 +785,16 @@ void pidgin_log_show_contact(PurpleContact *contact) {
image = NULL;
}
- if (contact->alias != NULL)
- name = contact->alias;
- else if (contact->priority != NULL)
- name = purple_buddy_get_contact_alias(contact->priority);
+ name = purple_contact_get_alias(contact);
/* This will happen if the contact doesn't have an alias,
* and none of the contact's buddies are online.
* There is probably a better way to deal with this. */
if (name == NULL) {
- if (contact->node.child != NULL && PURPLE_BLIST_NODE_IS_BUDDY(contact->node.child))
- name = purple_buddy_get_contact_alias((PurpleBuddy *) contact->node.child);
+ if (PURPLE_BLIST_NODE(contact)->child != NULL &&
+ PURPLE_IS_BUDDY(PURPLE_BLIST_NODE(contact)->child))
+ name = purple_buddy_get_contact_alias(PURPLE_BUDDY(
+ PURPLE_BLIST_NODE(contact)->child));
if (name == NULL)
name = "";
}
@@ -845,11 +845,9 @@ void pidgin_log_init(void)
purple_signal_register(handle, "log-displaying",
purple_marshal_VOID__POINTER_POINTER,
- NULL, 2,
- purple_value_new(PURPLE_TYPE_BOXED,
- "PidginLogViewer *"),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_LOG));
+ G_TYPE_NONE, 2,
+ G_TYPE_POINTER, /* (PidginLogViewer *) */
+ PURPLE_TYPE_LOG);
}
void
diff --git a/pidgin/gtklog.h b/pidgin/gtklog.h
index 77917a8a15..37f3c2f602 100644
--- a/pidgin/gtklog.h
+++ b/pidgin/gtklog.h
@@ -1,9 +1,3 @@
-/**
- * @file gtklog.h GTK+ Log viewer
- * @ingroup pidgin
- * @see @ref gtklog-signals
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -24,8 +18,16 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _PIDGINLOG_H_
#define _PIDGINLOG_H_
+/**
+ * SECTION:gtklog
+ * @section_id: pidgin-gtklog
+ * @short_description: <filename>gtklog.h</filename>
+ * @title: Log Viewer
+ * @see_also: <link linkend="chapter-signals-gtklog">Log signals</link>
+ */
#include "pidgin.h"
#include "log.h"
@@ -35,23 +37,35 @@
typedef struct _PidginLogViewer PidginLogViewer;
/**
+ * PidginLogViewer:
+ * @logs: The list of logs viewed in this viewer
+ * @window: The viewer's window
+ * @treestore: The treestore containing said logs
+ * @treeview: The treeview representing said treestore
+ * @web_view: The webkit web view to display said logs
+ * @entry: The search entry, in which search terms are entered
+ * @flags: The most recently used log flags
+ * @search: The string currently being searched for
+ * @label: The label at the top of the log viewer
+ *
* A GTK+ Log Viewer. You can look at logs with it.
*/
struct _PidginLogViewer {
- GList *logs; /**< The list of logs viewed in this viewer */
-
- GtkWidget *window; /**< The viewer's window */
- GtkTreeStore *treestore; /**< The treestore containing said logs */
- GtkWidget *treeview; /**< The treeview representing said treestore */
- GtkWidget *imhtml; /**< The imhtml to display said logs */
- GtkWidget *entry; /**< The search entry, in which search terms
- * are entered */
- PurpleLogReadFlags flags; /**< The most recently used log flags */
- char *search; /**< The string currently being searched for */
- GtkWidget *label; /**< The label at the top of the log viewer */
+ GList *logs;
+
+ GtkWidget *window;
+ GtkTreeStore *treestore;
+ GtkWidget *treeview;
+ GtkWidget *web_view;
+ GtkWidget *entry;
+
+ PurpleLogReadFlags flags;
+ char *search;
+ GtkLabel *label;
};
+G_BEGIN_DECLS
void pidgin_log_show(PurpleLogType type, const char *buddyname, PurpleAccount *account);
void pidgin_log_show_contact(PurpleContact *contact);
@@ -59,27 +73,32 @@ void pidgin_log_show_contact(PurpleContact *contact);
void pidgin_syslog_show(void);
/**************************************************************************/
-/** @name GTK+ Log Subsystem */
+/* GTK+ Log Subsystem */
/**************************************************************************/
-/*@{*/
/**
+ * pidgin_log_init:
+ *
* Initializes the GTK+ log subsystem.
*/
void pidgin_log_init(void);
/**
+ * pidgin_log_get_handle:
+ *
* Returns the GTK+ log subsystem handle.
*
- * @return The GTK+ log subsystem handle.
+ * Returns: The GTK+ log subsystem handle.
*/
void *pidgin_log_get_handle(void);
/**
+ * pidgin_log_uninit:
+ *
* Uninitializes the GTK+ log subsystem.
*/
void pidgin_log_uninit(void);
-/*@}*/
+G_END_DECLS
#endif
diff --git a/pidgin/gtkmedia.c b/pidgin/gtkmedia.c
index d91a951061..9cac706fea 100644
--- a/pidgin/gtkmedia.c
+++ b/pidgin/gtkmedia.c
@@ -1,8 +1,4 @@
-/**
- * @file media.c Account API
- * @ingroup core
- *
- * Pidgin
+/* 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
@@ -38,11 +34,17 @@
#ifdef USE_VV
#include "media-gst.h"
-#ifdef _WIN32
+#ifdef GDK_WINDOWING_WIN32
#include <gdk/gdkwin32.h>
#endif
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+#ifdef GDK_WINDOWING_QUARTZ
+#include <gdk/gdkquartz.h>
+#endif
-#include <gst/interfaces/xoverlay.h>
+#include "gtk3compat.h"
#define PIDGIN_TYPE_MEDIA (pidgin_media_get_type())
#define PIDGIN_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PIDGIN_TYPE_MEDIA, PidginMedia))
@@ -175,13 +177,13 @@ pidgin_media_class_init (PidginMediaClass *klass)
"PurpleMedia",
"The PurpleMedia associated with this media.",
PURPLE_TYPE_MEDIA,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class, PROP_SCREENNAME,
g_param_spec_string("screenname",
"Screenname",
"The screenname of the user this session is with.",
NULL,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_type_class_add_private(klass, sizeof(PidginMediaPrivate));
}
@@ -329,7 +331,7 @@ pidgin_media_init (PidginMedia *media)
XSetErrorHandler(pidgin_x_error_handler);
#endif
- vbox = gtk_vbox_new(FALSE, 0);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add(GTK_CONTAINER(media), vbox);
media->priv->statusbar = gtk_statusbar_new();
@@ -343,7 +345,7 @@ pidgin_media_init (PidginMedia *media)
gtk_box_pack_start(GTK_BOX(vbox), media->priv->menubar,
FALSE, TRUE, 0);
- media->priv->display = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ media->priv->display = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtk_container_set_border_width(GTK_CONTAINER(media->priv->display),
PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_start(GTK_BOX(vbox), media->priv->display,
@@ -439,7 +441,7 @@ pidgin_media_remove_widget(PidginMedia *gtkmedia,
gtk_widget_destroy(widget);
- gtk_widget_size_request(GTK_WIDGET(gtkmedia), &req);
+ gtk_widget_get_preferred_size(GTK_WIDGET(gtkmedia), NULL, &req);
gtk_window_resize(GTK_WINDOW(gtkmedia), req.width, req.height);
}
}
@@ -455,7 +457,7 @@ level_message_cb(PurpleMedia *media, gchar *session_id, gchar *participant,
progress = pidgin_media_get_widget(gtkmedia, session_id, participant);
if (progress)
- gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), level * 5);
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), level);
}
@@ -516,12 +518,11 @@ pidgin_media_finalize(GObject *media)
static void
pidgin_media_emit_message(PidginMedia *gtkmedia, const char *msg)
{
- PurpleConversation *conv = purple_find_conversation_with_account(
- PURPLE_CONV_TYPE_ANY, gtkmedia->priv->screenname,
+ PurpleConversation *conv = purple_conversations_find_with_account(
+ gtkmedia->priv->screenname,
purple_media_get_account(gtkmedia->priv->media));
if (conv != NULL)
- purple_conversation_write(conv, NULL, msg,
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(conv, msg, 0);
}
typedef struct
@@ -538,30 +539,36 @@ realize_cb_cb(PidginMediaRealizeData *data)
GdkWindow *window = NULL;
if (data->participant == NULL)
-#if GTK_CHECK_VERSION(2, 14, 0)
window = gtk_widget_get_window(priv->local_video);
-#else
- window = (priv->local_video)->window;
-#endif
else {
GtkWidget *widget = pidgin_media_get_widget(data->gtkmedia,
data->session_id, data->participant);
if (widget)
-#if GTK_CHECK_VERSION(2, 14, 0)
window = gtk_widget_get_window(widget);
-#else
- window = widget->window;
-#endif
}
if (window) {
- gulong window_id;
-#ifdef _WIN32
- window_id = GDK_WINDOW_HWND(window);
-#elif defined(HAVE_X11)
- window_id = GDK_WINDOW_XWINDOW(window);
-#else
-# error "Unsupported windowing system"
+ gulong window_id = 0;
+#ifdef GDK_WINDOWING_WIN32
+ if (GDK_IS_WIN32_WINDOW(window))
+ window_id = GPOINTER_TO_UINT(GDK_WINDOW_HWND(window));
+ else
+#endif
+#ifdef GDK_WINDOWING_X11
+ if (GDK_IS_X11_WINDOW(window))
+ window_id = gdk_x11_window_get_xid(window);
+ else
+#endif
+#ifdef GDK_WINDOWING_QUARTZ
+ if (GDK_IS_QUARTZ_WINDOW(window))
+ window_id = (gulong)gdk_quartz_window_get_nsview(window);
+ else
+#endif
+ g_warning("Unsupported GDK backend");
+#if !(defined(GDK_WINDOWING_WIN32) \
+ || defined(GDK_WINDOWING_X11) \
+ || defined(GDK_WINDOWING_QUARTZ))
+# error "Unsupported GDK windowing system"
#endif
purple_media_set_output_window(priv->media, data->session_id,
@@ -583,12 +590,17 @@ realize_cb(GtkWidget *widget, PidginMediaRealizeData *data)
static void
pidgin_media_error_cb(PidginMedia *media, const char *error, PidginMedia *gtkmedia)
{
- PurpleConversation *conv = purple_find_conversation_with_account(
- PURPLE_CONV_TYPE_ANY, gtkmedia->priv->screenname,
+ PurpleConversation *conv = purple_conversations_find_with_account(
+ gtkmedia->priv->screenname,
purple_media_get_account(gtkmedia->priv->media));
- if (conv != NULL)
- purple_conversation_write(conv, NULL, error,
- PURPLE_MESSAGE_ERROR, time(NULL));
+ if (conv != NULL) {
+ purple_conversation_write_system_message(
+ conv, error, PURPLE_MESSAGE_ERROR);
+ } else {
+ purple_notify_error(NULL, NULL, _("Media error"), error,
+ purple_request_cpar_from_conversation(conv));
+ }
+
gtk_statusbar_push(GTK_STATUSBAR(gtkmedia->priv->statusbar),
0, error);
}
@@ -622,7 +634,7 @@ pidgin_request_timeout_cb(PidginMedia *gtkmedia)
gchar *message = NULL;
account = purple_media_get_account(gtkmedia->priv->media);
- buddy = purple_find_buddy(account, gtkmedia->priv->screenname);
+ buddy = purple_blist_find_buddy(account, gtkmedia->priv->screenname);
alias = buddy ? purple_buddy_get_contact_alias(buddy) :
gtkmedia->priv->screenname;
type = gtkmedia->priv->request_type;
@@ -642,11 +654,10 @@ pidgin_request_timeout_cb(PidginMedia *gtkmedia)
gtkmedia->priv->request_type = PURPLE_MEDIA_NONE;
if (!purple_media_accepted(gtkmedia->priv->media, NULL, NULL)) {
purple_request_accept_cancel(gtkmedia, _("Incoming Call"),
- message, NULL, PURPLE_DEFAULT_ACTION_NONE,
- (void*)account, gtkmedia->priv->screenname,
- NULL, gtkmedia->priv->media,
- pidgin_media_accept_cb,
- pidgin_media_reject_cb);
+ message, NULL, PURPLE_DEFAULT_ACTION_NONE,
+ purple_request_cpar_from_account(account),
+ gtkmedia->priv->media, pidgin_media_accept_cb,
+ pidgin_media_reject_cb);
}
pidgin_media_emit_message(gtkmedia, message);
g_free(message);
@@ -654,30 +665,18 @@ pidgin_request_timeout_cb(PidginMedia *gtkmedia)
}
static void
-#if GTK_CHECK_VERSION(2,12,0)
pidgin_media_input_volume_changed(GtkScaleButton *range, double value,
PurpleMedia *media)
{
double val = (double)value * 100.0;
-#else
-pidgin_media_input_volume_changed(GtkRange *range, PurpleMedia *media)
-{
- double val = (double)gtk_range_get_value(GTK_RANGE(range));
-#endif
purple_media_set_input_volume(media, NULL, val);
}
static void
-#if GTK_CHECK_VERSION(2,12,0)
pidgin_media_output_volume_changed(GtkScaleButton *range, double value,
PurpleMedia *media)
{
double val = (double)value * 100.0;
-#else
-pidgin_media_output_volume_changed(GtkRange *range, PurpleMedia *media)
-{
- double val = (double)gtk_range_get_value(GTK_RANGE(range));
-#endif
purple_media_set_output_volume(media, NULL, NULL, val);
}
@@ -705,10 +704,9 @@ pidgin_media_add_audio_widget(PidginMedia *gtkmedia,
} else
g_return_val_if_reached(NULL);
-#if GTK_CHECK_VERSION(2,12,0)
/* Setup widget structure */
- volume_widget = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
- progress_parent = gtk_vbox_new(FALSE, 0);
+ volume_widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
+ progress_parent = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_box_pack_start(GTK_BOX(volume_widget),
progress_parent, TRUE, TRUE, 0);
@@ -717,19 +715,6 @@ pidgin_media_add_audio_widget(PidginMedia *gtkmedia,
gtk_scale_button_set_value(GTK_SCALE_BUTTON(volume), value/100.0);
gtk_box_pack_end(GTK_BOX(volume_widget),
volume, FALSE, FALSE, 0);
-#else
- /* Setup widget structure */
- volume_widget = gtk_vbox_new(FALSE, 0);
- progress_parent = volume_widget;
-
- /* Volume slider */
- volume = gtk_hscale_new_with_range(0.0, 100.0, 5.0);
- gtk_range_set_increments(GTK_RANGE(volume), 5.0, 25.0);
- gtk_range_set_value(GTK_RANGE(volume), value);
- gtk_scale_set_draw_value(GTK_SCALE(volume), FALSE);
- gtk_box_pack_end(GTK_BOX(volume_widget),
- volume, TRUE, FALSE, 0);
-#endif
/* Volume level indicator */
progress = gtk_progress_bar_new();
@@ -769,7 +754,7 @@ pidgin_media_ready_cb(PurpleMedia *media, PidginMedia *gtkmedia, const gchar *si
if (gtkmedia->priv->recv_widget == NULL
&& type & (PURPLE_MEDIA_RECV_VIDEO |
PURPLE_MEDIA_RECV_AUDIO)) {
- recv_widget = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ recv_widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_start(GTK_BOX(gtkmedia->priv->display),
recv_widget, TRUE, TRUE, 0);
gtk_widget_show(recv_widget);
@@ -779,10 +764,10 @@ pidgin_media_ready_cb(PurpleMedia *media, PidginMedia *gtkmedia, const gchar *si
if (gtkmedia->priv->send_widget == NULL
&& type & (PURPLE_MEDIA_SEND_VIDEO |
PURPLE_MEDIA_SEND_AUDIO)) {
- send_widget = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ send_widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_start(GTK_BOX(gtkmedia->priv->display),
send_widget, FALSE, TRUE, 0);
- button_widget = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ button_widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_end(GTK_BOX(recv_widget), button_widget,
FALSE, TRUE, 0);
gtk_widget_show(send_widget);
@@ -805,7 +790,11 @@ pidgin_media_ready_cb(PurpleMedia *media, PidginMedia *gtkmedia, const gchar *si
PidginMediaRealizeData *data;
GtkWidget *aspect;
GtkWidget *remote_video;
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkRGBA color = {0.0, 0.0, 0.0, 1.0};
+#else
GdkColor color = {0, 0, 0, 0};
+#endif
aspect = gtk_aspect_frame_new(NULL, 0, 0, 4.0/3.0, FALSE);
gtk_frame_set_shadow_type(GTK_FRAME(aspect), GTK_SHADOW_IN);
@@ -817,7 +806,11 @@ pidgin_media_ready_cb(PurpleMedia *media, PidginMedia *gtkmedia, const gchar *si
data->participant = g_strdup(gtkmedia->priv->screenname);
remote_video = gtk_drawing_area_new();
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_background_color(remote_video, GTK_STATE_FLAG_NORMAL, &color);
+#else
gtk_widget_modify_bg(remote_video, GTK_STATE_NORMAL, &color);
+#endif
g_signal_connect(G_OBJECT(remote_video), "realize",
G_CALLBACK(realize_cb), data);
gtk_container_add(GTK_CONTAINER(aspect), remote_video);
@@ -836,7 +829,11 @@ pidgin_media_ready_cb(PurpleMedia *media, PidginMedia *gtkmedia, const gchar *si
PidginMediaRealizeData *data;
GtkWidget *aspect;
GtkWidget *local_video;
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkRGBA color = {0.0, 0.0, 0.0, 1.0};
+#else
GdkColor color = {0, 0, 0, 0};
+#endif
aspect = gtk_aspect_frame_new(NULL, 0, 0, 4.0/3.0, TRUE);
gtk_frame_set_shadow_type(GTK_FRAME(aspect), GTK_SHADOW_IN);
@@ -848,7 +845,11 @@ pidgin_media_ready_cb(PurpleMedia *media, PidginMedia *gtkmedia, const gchar *si
data->participant = NULL;
local_video = gtk_drawing_area_new();
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_background_color(local_video, GTK_STATE_FLAG_NORMAL, &color);
+#else
gtk_widget_modify_bg(local_video, GTK_STATE_NORMAL, &color);
+#endif
g_signal_connect(G_OBJECT(local_video), "realize",
G_CALLBACK(realize_cb), data);
gtk_container_add(GTK_CONTAINER(aspect), local_video);
@@ -969,7 +970,7 @@ pidgin_media_stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type,
pidgin_media_set_state(gtkmedia, PIDGIN_MEDIA_ACCEPTED);
pidgin_media_emit_message(gtkmedia, _("Call in progress."));
gtk_statusbar_push(GTK_STATUSBAR(gtkmedia->priv->statusbar),
- 0, _("Call in progress."));
+ 0, _("Call in progress"));
gtk_widget_show(GTK_WIDGET(gtkmedia));
}
}
@@ -1056,7 +1057,7 @@ pidgin_media_new_cb(PurpleMediaManager *manager, PurpleMedia *media,
{
PidginMedia *gtkmedia = PIDGIN_MEDIA(
pidgin_media_new(media, screenname));
- PurpleBuddy *buddy = purple_find_buddy(account, screenname);
+ PurpleBuddy *buddy = purple_blist_find_buddy(account, screenname);
const gchar *alias = buddy ?
purple_buddy_get_contact_alias(buddy) : screenname;
gtk_window_set_title(GTK_WINDOW(gtkmedia), alias);
@@ -1068,6 +1069,96 @@ pidgin_media_new_cb(PurpleMediaManager *manager, PurpleMedia *media,
}
static GstElement *
+create_vv_element(const gchar *plugin, const gchar *device)
+{
+ GstElement *element, *source;
+
+ g_return_val_if_fail(plugin != NULL, NULL);
+ g_return_val_if_fail(plugin[0] != '\0', NULL);
+
+ if (device != NULL && device[0] == '\0')
+ device = NULL;
+
+ if (g_strcmp0(plugin, "videodisabledsrc") == 0) {
+ element = gst_element_factory_make("videotestsrc", NULL);
+ g_object_set(G_OBJECT(element), "is-live", TRUE, NULL);
+ if (g_strcmp0(device, "snow") == 0) {
+ /* GST_VIDEO_TEST_SRC_SNOW */
+ g_object_set(G_OBJECT(element), "pattern", 1, NULL);
+ } else {
+ /* GST_VIDEO_TEST_SRC_BLACK */
+ g_object_set(G_OBJECT(element), "pattern", 2, NULL);
+ }
+ return element;
+ }
+
+ if (g_strcmp0(plugin, "ksvideosrc") == 0) {
+ GstElement *ksv_bin, *ksv_src, *ksv_filter;
+ GstPad *ksv_pad, *ksv_ghost;
+
+ ksv_bin = gst_bin_new("ksvideofilteredsrc");
+ ksv_src = gst_element_factory_make("ksvideosrc", NULL);
+ ksv_filter = gst_element_factory_make("ffmpegcolorspace", NULL);
+
+ gst_bin_add_many(GST_BIN(ksv_bin), ksv_src, ksv_filter, NULL);
+
+ gst_element_link(ksv_src, ksv_filter);
+
+ ksv_pad = gst_element_get_static_pad(ksv_filter, "src");
+ ksv_ghost = gst_ghost_pad_new("src", ksv_pad);
+ gst_object_unref(ksv_pad);
+ gst_element_add_pad(ksv_bin, ksv_ghost);
+
+ element = ksv_bin;
+ source = ksv_src;
+ }
+ else
+ element = source = gst_element_factory_make(plugin, NULL);
+
+ if (element == NULL)
+ return NULL;
+
+ if (device != NULL) {
+ GObjectClass *klass = G_OBJECT_GET_CLASS(source);
+ if (g_object_class_find_property(klass, "device"))
+ g_object_set(G_OBJECT(source), "device", device, NULL);
+ else if (g_object_class_find_property(klass, "device-index"))
+ g_object_set(G_OBJECT(source), "device-index",
+ g_ascii_strtoull(device, NULL, 10), NULL);
+ else
+ purple_debug_warning("gtkmedia", "No possibility to "
+ "set device\n");
+ }
+
+ if (g_strcmp0(plugin, "videotestsrc") == 0)
+ g_object_set(G_OBJECT(element), "is-live", TRUE, NULL);
+
+ return element;
+}
+
+static GstElement *
+create_configured_vv_element(const gchar *type, const gchar *dir)
+{
+ gchar *tmp;
+ const gchar *plugin, *device;
+
+ tmp = g_strdup_printf(PIDGIN_PREFS_ROOT "/vvconfig/%s/%s/plugin",
+ type, dir);
+ plugin = purple_prefs_get_string(tmp);
+ g_free(tmp);
+
+ tmp = g_strdup_printf(PIDGIN_PREFS_ROOT "/vvconfig/%s/%s/device",
+ type, dir);
+ device = purple_prefs_get_string(tmp);
+ g_free(tmp);
+
+ if (plugin == NULL || plugin[0] == '\0')
+ return NULL;
+
+ return create_vv_element(plugin, device);
+}
+
+static GstElement *
create_default_video_src(PurpleMedia *media,
const gchar *session_id, const gchar *participant)
{
@@ -1075,22 +1166,32 @@ create_default_video_src(PurpleMedia *media,
GstPad *pad;
GstPad *ghost;
+ src = create_configured_vv_element("video", "src");
+
#ifdef _WIN32
- /* autovideosrc doesn't pick ksvideosrc for some reason */
- src = gst_element_factory_make("ksvideosrc", NULL);
if (src == NULL)
- src = gst_element_factory_make("dshowvideosrc", NULL);
+ src = create_vv_element("ksvideosrc", NULL);
+ if (src == NULL)
+ src = create_vv_element("dshowvideosrc", NULL);
if (src == NULL)
- src = gst_element_factory_make("autovideosrc", NULL);
+ src = create_vv_element("autovideosrc", NULL);
+#elif defined(__APPLE__)
+ if (src == NULL)
+ src = create_vv_element("osxvideosrc", NULL);
+ if (src == NULL)
+ src = create_vv_element("autovideosrc", NULL);
#else
- src = gst_element_factory_make("gconfvideosrc", NULL);
if (src == NULL)
- src = gst_element_factory_make("autovideosrc", NULL);
+ src = create_vv_element("gconfvideosrc", NULL);
+ if (src == NULL)
+ src = create_vv_element("autovideosrc", NULL);
if (src == NULL)
- src = gst_element_factory_make("v4l2src", NULL);
+ src = create_vv_element("v4l2src", NULL);
if (src == NULL)
- src = gst_element_factory_make("v4lsrc", NULL);
+ src = create_vv_element("v4lsrc", NULL);
#endif
+ if (src == NULL)
+ src = create_vv_element("videotestsrc", NULL);
if (src == NULL) {
purple_debug_error("gtkmedia", "Unable to find a suitable "
"element for the default video source.\n");
@@ -1113,9 +1214,16 @@ static GstElement *
create_default_video_sink(PurpleMedia *media,
const gchar *session_id, const gchar *participant)
{
- GstElement *sink = gst_element_factory_make("gconfvideosink", NULL);
+ GstElement *sink;
+
+ sink = create_configured_vv_element("video", "sink");
+
+ if (sink == NULL)
+ sink = create_vv_element("directdrawsink", NULL);
if (sink == NULL)
- sink = gst_element_factory_make("autovideosink", NULL);
+ sink = create_vv_element("gconfvideosink", NULL);
+ if (sink == NULL)
+ sink = create_vv_element("autovideosink", NULL);
if (sink == NULL)
purple_debug_error("gtkmedia", "Unable to find a suitable "
"element for the default video sink.\n");
@@ -1127,15 +1235,23 @@ create_default_audio_src(PurpleMedia *media,
const gchar *session_id, const gchar *participant)
{
GstElement *src;
- src = gst_element_factory_make("gconfaudiosrc", NULL);
+
+ src = create_configured_vv_element("audio", "src");
+
+ if (src == NULL)
+ src = create_vv_element("directsoundsrc", NULL);
+ if (src == NULL)
+ src = create_vv_element("gconfaudiosrc", NULL);
if (src == NULL)
- src = gst_element_factory_make("autoaudiosrc", NULL);
+ src = create_vv_element("autoaudiosrc", NULL);
if (src == NULL)
- src = gst_element_factory_make("alsasrc", NULL);
+ src = create_vv_element("alsasrc", NULL);
if (src == NULL)
- src = gst_element_factory_make("osssrc", NULL);
+ src = create_vv_element("osssrc", NULL);
if (src == NULL)
- src = gst_element_factory_make("dshowaudiosrc", NULL);
+ src = create_vv_element("dshowaudiosrc", NULL);
+ if (src == NULL)
+ src = create_vv_element("osxaudiosrc", NULL);
if (src == NULL) {
purple_debug_error("gtkmedia", "Unable to find a suitable "
"element for the default audio source.\n");
@@ -1150,9 +1266,15 @@ create_default_audio_sink(PurpleMedia *media,
const gchar *session_id, const gchar *participant)
{
GstElement *sink;
- sink = gst_element_factory_make("gconfaudiosink", NULL);
+
+ sink = create_configured_vv_element("audio", "sink");
+
+ if (sink == NULL)
+ sink = create_vv_element("directsoundsink", NULL);
+ if (sink == NULL)
+ sink = create_vv_element("gconfaudiosink", NULL);
if (sink == NULL)
- sink = gst_element_factory_make("autoaudiosink",NULL);
+ sink = create_vv_element("autoaudiosink",NULL);
if (sink == NULL) {
purple_debug_error("gtkmedia", "Unable to find a suitable "
"element for the default audio sink.\n");
diff --git a/pidgin/gtkmedia.h b/pidgin/gtkmedia.h
index a8e0152407..312d027bf6 100644
--- a/pidgin/gtkmedia.h
+++ b/pidgin/gtkmedia.h
@@ -1,8 +1,3 @@
-/**
- * @file gtkmedia.h Pidgin Media API
- * @ingroup pidgin
- */
-
/* Pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -26,6 +21,12 @@
#ifndef __GTKMEDIA_H_
#define __GTKMEDIA_H_
+/**
+ * SECTION:gtkmedia
+ * @section_id: pidgin-gtkmedia
+ * @short_description: <filename>gtkmedia.h</filename>
+ * @title: Media API
+ */
G_BEGIN_DECLS
diff --git a/pidgin/gtkmenutray.c b/pidgin/gtkmenutray.c
index 31a9f6f4b9..0e3120421f 100644
--- a/pidgin/gtkmenutray.c
+++ b/pidgin/gtkmenutray.c
@@ -17,11 +17,14 @@
* 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 "gtkmenutray.h"
-#include <gtk/gtk.h>
+#include "glibcompat.h"
+#include "gtk3compat.h"
/******************************************************************************
* Enums
@@ -35,7 +38,6 @@ enum {
* Globals
*****************************************************************************/
static GObjectClass *parent_class = NULL;
-
/******************************************************************************
* Internal Stuff
*****************************************************************************/
@@ -43,8 +45,9 @@ static GObjectClass *parent_class = NULL;
/******************************************************************************
* Item Stuff
*****************************************************************************/
+#if GTK_CHECK_VERSION(3,0,0)
static void
-pidgin_menu_tray_select(GtkItem *item) {
+pidgin_menu_tray_select(GtkMenuItem *widget) {
/* this may look like nothing, but it's really overriding the
* GtkMenuItem's select function so that it doesn't get highlighted like
* a normal menu item would.
@@ -52,15 +55,26 @@ pidgin_menu_tray_select(GtkItem *item) {
}
static void
-pidgin_menu_tray_deselect(GtkItem *item) {
+pidgin_menu_tray_deselect(GtkMenuItem *widget) {
/* Probably not necessary, but I'd rather be safe than sorry. We're
* overridding the select, so it makes sense to override deselect as well.
*/
}
-
-/******************************************************************************
- * Widget Stuff
- *****************************************************************************/
+#else
+static void
+pidgin_menu_tray_select(GtkItem *widget) {
+ /* this may look like nothing, but it's really overriding the
+ * GtkMenuItem's select function so that it doesn't get highlighted like
+ * a normal menu item would.
+ */
+}
+static void
+pidgin_menu_tray_deselect(GtkItem *widget) {
+ /* Probably not necessary, but I'd rather be safe than sorry. We're
+ * overridding the select, so it makes sense to override deselect as well.
+ */
+}
+#endif
/******************************************************************************
* Object Stuff
@@ -92,8 +106,9 @@ pidgin_menu_tray_map(GtkWidget *widget)
static void
pidgin_menu_tray_finalize(GObject *obj)
{
- PidginMenuTray *tray = PIDGIN_MENU_TRAY(obj);
#if 0
+ PidginMenuTray *tray = PIDGIN_MENU_TRAY(obj);
+
/* This _might_ be leaking, but I have a sneaking suspicion that the widget is
* getting destroyed in GtkContainer's finalize function. But if were are
* leaking here, be sure to figure out why this causes a crash.
@@ -104,17 +119,17 @@ pidgin_menu_tray_finalize(GObject *obj)
gtk_widget_destroy(GTK_WIDGET(tray->tray));
#endif
- if (tray->tooltips) {
- gtk_object_sink(GTK_OBJECT(tray->tooltips));
- }
-
G_OBJECT_CLASS(parent_class)->finalize(obj);
}
static void
pidgin_menu_tray_class_init(PidginMenuTrayClass *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
- GtkItemClass *item_class = GTK_ITEM_CLASS(klass);
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkMenuItemClass *menu_item_class = GTK_MENU_ITEM_CLASS(klass);
+#else
+ GtkItemClass *menu_item_class = GTK_ITEM_CLASS(klass);
+#endif
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
GParamSpec *pspec;
@@ -123,15 +138,15 @@ pidgin_menu_tray_class_init(PidginMenuTrayClass *klass) {
object_class->finalize = pidgin_menu_tray_finalize;
object_class->get_property = pidgin_menu_tray_get_property;
- item_class->select = pidgin_menu_tray_select;
- item_class->deselect = pidgin_menu_tray_deselect;
+ menu_item_class->select = pidgin_menu_tray_select;
+ menu_item_class->deselect = pidgin_menu_tray_deselect;
widget_class->map = pidgin_menu_tray_map;
pspec = g_param_spec_object("box", "The box",
"The box",
GTK_TYPE_BOX,
- G_PARAM_READABLE);
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property(object_class, PROP_BOX, pspec);
}
@@ -141,10 +156,14 @@ pidgin_menu_tray_init(PidginMenuTray *menu_tray) {
GtkSettings *settings;
gint height = -1;
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ /* Gtk3 docs says, it should be replaced with gtk_widget_set_hexpand and
+ * gtk_widget_set_halign. But it doesn't seems to work. */
gtk_menu_item_set_right_justified(GTK_MENU_ITEM(menu_tray), TRUE);
+G_GNUC_END_IGNORE_DEPRECATIONS
if(!GTK_IS_WIDGET(menu_tray->tray))
- menu_tray->tray = gtk_hbox_new(FALSE, 0);
+ menu_tray->tray = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
settings =
gtk_settings_get_for_screen(gtk_widget_get_screen(widget));
@@ -162,7 +181,7 @@ pidgin_menu_tray_init(PidginMenuTray *menu_tray) {
* API
*****************************************************************************/
GType
-pidgin_menu_tray_get_gtype(void) {
+pidgin_menu_tray_get_type(void) {
static GType type = 0;
if(type == 0) {
@@ -205,7 +224,7 @@ pidgin_menu_tray_add(PidginMenuTray *menu_tray, GtkWidget *widget,
g_return_if_fail(PIDGIN_IS_MENU_TRAY(menu_tray));
g_return_if_fail(GTK_IS_WIDGET(widget));
- if (GTK_WIDGET_NO_WINDOW(widget))
+ if (!gtk_widget_get_has_window(widget))
{
GtkWidget *event;
@@ -238,21 +257,17 @@ pidgin_menu_tray_prepend(PidginMenuTray *menu_tray, GtkWidget *widget, const cha
void
pidgin_menu_tray_set_tooltip(PidginMenuTray *menu_tray, GtkWidget *widget, const char *tooltip)
{
- if (!menu_tray->tooltips)
- menu_tray->tooltips = gtk_tooltips_new();
-
/* Should we check whether widget is a child of menu_tray? */
/*
- * If the widget does not have it's own window, then it
+ * If the widget does not have its own window, then it
* must have automatically been added to an event box
* when it was added to the menu tray. If this is the
* case, we want to set the tooltip on the widget's parent,
* not on the widget itself.
*/
- if (GTK_WIDGET_NO_WINDOW(widget))
- widget = widget->parent;
+ if (!gtk_widget_get_has_window(widget))
+ widget = gtk_widget_get_parent(widget);
- gtk_tooltips_set_tip(menu_tray->tooltips, widget, tooltip, NULL);
+ gtk_widget_set_tooltip_text(widget, tooltip);
}
-
diff --git a/pidgin/gtkmenutray.h b/pidgin/gtkmenutray.h
index 0661ef8425..f680ea79f8 100644
--- a/pidgin/gtkmenutray.h
+++ b/pidgin/gtkmenutray.h
@@ -1,8 +1,3 @@
-/**
- * @file gtkmenutray.h GTK+ Tray menu item
- * @ingroup 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.
@@ -21,12 +16,19 @@
* 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_MENU_TRAY_H
#define PIDGIN_MENU_TRAY_H
+/**
+ * SECTION:gtkmenutray
+ * @section_id: pidgin-gtkmenutray
+ * @short_description: <filename>gtkmenutray.h</filename>
+ * @title: Tray Menu Item
+ */
#include <gtk/gtk.h>
-#define PIDGIN_TYPE_MENU_TRAY (pidgin_menu_tray_get_gtype())
+#define PIDGIN_TYPE_MENU_TRAY (pidgin_menu_tray_get_type())
#define PIDGIN_MENU_TRAY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PIDGIN_TYPE_MENU_TRAY, PidginMenuTray))
#define PIDGIN_MENU_TRAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PIDGIN_TYPE_MENU_TRAY, PidginMenuTrayClass))
#define PIDGIN_IS_MENU_TRAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PIDGIN_TYPE_MENU_TRAY))
@@ -36,68 +38,81 @@
typedef struct _PidginMenuTray PidginMenuTray;
typedef struct _PidginMenuTrayClass PidginMenuTrayClass;
-/** A PidginMenuTray */
+/**
+ * PidginMenuTray:
+ * @tray: The tray
+ *
+ * A PidginMenuTray
+ */
struct _PidginMenuTray {
- GtkMenuItem gparent; /**< The parent instance */
- GtkWidget *tray; /**< The tray */
- GtkTooltips *tooltips; /**< Tooltips */
+ GtkMenuItem gparent;
+
+ /*< public >*/
+ GtkWidget *tray;
};
-/** A PidginMenuTrayClass */
struct _PidginMenuTrayClass {
- GtkMenuItemClass gparent; /**< The parent class */
+ GtkMenuItemClass gparent;
};
G_BEGIN_DECLS
/**
+ * pidgin_menu_tray_get_type:
+ *
* Registers the PidginMenuTray class if necessary and returns the
* type ID assigned to it.
*
- * @return The PidginMenuTray type ID
+ * Returns: The PidginMenuTray type ID
*/
-GType pidgin_menu_tray_get_gtype(void);
+GType pidgin_menu_tray_get_type(void);
/**
+ * pidgin_menu_tray_new:
+ *
* Creates a new PidginMenuTray
*
- * @return A new PidginMenuTray
+ * Returns: A new PidginMenuTray
*/
GtkWidget *pidgin_menu_tray_new(void);
/**
- * Gets the box for the PidginMenuTray
+ * pidgin_menu_tray_get_box:
+ * @menu_tray: The PidginMenuTray
*
- * @param menu_tray The PidginMenuTray
+ * Gets the box for the PidginMenuTray
*
- * @return The box that this menu tray is using
+ * Returns: The box that this menu tray is using
*/
GtkWidget *pidgin_menu_tray_get_box(PidginMenuTray *menu_tray);
/**
- * Appends a widget into the tray
+ * pidgin_menu_tray_append:
+ * @menu_tray: The tray
+ * @widget: The widget
+ * @tooltip: The tooltip for this widget (widget requires its own X-window)
*
- * @param menu_tray The tray
- * @param widget The widget
- * @param tooltip The tooltip for this widget (widget requires its own X-window)
+ * Appends a widget into the tray
*/
void pidgin_menu_tray_append(PidginMenuTray *menu_tray, GtkWidget *widget, const char *tooltip);
/**
- * Prepends a widget into the tray
+ * pidgin_menu_tray_prepend:
+ * @menu_tray: The tray
+ * @widget: The widget
+ * @tooltip: The tooltip for this widget (widget requires its own X-window)
*
- * @param menu_tray The tray
- * @param widget The widget
- * @param tooltip The tooltip for this widget (widget requires its own X-window)
+ * Prepends a widget into the tray
*/
void pidgin_menu_tray_prepend(PidginMenuTray *menu_tray, GtkWidget *widget, const char *tooltip);
/**
- * Set the tooltip for a widget
+ * pidgin_menu_tray_set_tooltip:
+ * @menu_tray: The tray
+ * @widget: The widget
+ * @tooltip: The tooltip to set for the widget (widget requires its own X-window)
*
- * @param menu_tray The tray
- * @param widget The widget
- * @param tooltip The tooltip to set for the widget (widget requires its own X-window)
+ * Set the tooltip for a widget
*/
void pidgin_menu_tray_set_tooltip(PidginMenuTray *menu_tray, GtkWidget *widget, const char *tooltip);
diff --git a/pidgin/gtknickcolors.h b/pidgin/gtknickcolors.h
index 27d306532d..26991c2d22 100644
--- a/pidgin/gtknickcolors.h
+++ b/pidgin/gtknickcolors.h
@@ -1,8 +1,3 @@
-/**
- * @file gtknickcolors.h GTK+ Conversation API
- * @ingroup pidgin
- */
-
/* 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
@@ -22,8 +17,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _PIDGINNICKCOLORS_H_
#define _PIDGINNICKCOLORS_H_
+/**
+ * SECTION:gtknickcolors
+ * @section_id: pidgin-gtknickcolors
+ * @short_description: <filename>gtknickcolors.h</filename>
+ * @title: Conversation Nick Colors
+ */
static const GdkColor nick_seed_colors[] = {
{0, 64764, 59881, 20303}, /* Butter #1 */
@@ -52,6 +54,6 @@ static const GdkColor nick_seed_colors[] = {
{0, 11822, 13364, 13878} /* Aluminium #6*/
};
-#define NUM_NICK_SEED_COLORS (sizeof(nick_seed_colors) / sizeof(nick_seed_colors[0]))
+#define PIDGIN_NUM_NICK_SEED_COLORS (sizeof(nick_seed_colors) / sizeof(nick_seed_colors[0]))
#endif
diff --git a/pidgin/gtknotify.c b/pidgin/gtknotify.c
index 2bf9b907dd..0fb426b2f7 100644
--- a/pidgin/gtknotify.c
+++ b/pidgin/gtknotify.c
@@ -1,8 +1,3 @@
-/**
- * @file gtknotify.c GTK+ Notification API
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -35,11 +30,12 @@
#include "pidginstock.h"
#include "util.h"
+#include "gtk3compat.h"
#include "gtkblist.h"
-#include "gtkimhtml.h"
#include "gtknotify.h"
#include "gtkpounce.h"
#include "gtkutils.h"
+#include "gtkwebview.h"
typedef struct
{
@@ -192,11 +188,10 @@ open_im_foreach(GtkTreeModel *model, GtkTreePath *path,
-1);
if (pounce_data != NULL) {
- PurpleConversation *conv;
+ PurpleIMConversation *im;
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM,
- pounce_data->account, pounce_data->pouncee);
- purple_conversation_present(conv);
+ im = purple_im_conversation_new(pounce_data->account, pounce_data->pouncee);
+ purple_conversation_present(PURPLE_CONVERSATION(im));
}
}
@@ -375,11 +370,32 @@ pounce_row_selected_cb(GtkTreeView *tv, GtkTreePath *path,
static void
reset_mail_dialog(GtkDialog *unused)
{
+ g_return_if_fail(mail_dialog != NULL);
+
if (mail_dialog->in_use)
return;
gtk_widget_destroy(mail_dialog->dialog);
g_free(mail_dialog);
mail_dialog = NULL;
+ purple_signal_emit(purple_notify_get_handle(), "displaying-emails-clear");
+}
+
+gboolean
+pidgin_notify_emails_pending()
+{
+ return mail_dialog != NULL
+ && !gtk_widget_get_visible(mail_dialog->dialog);
+}
+
+void pidgin_notify_emails_present(void *data)
+{
+ if (pidgin_notify_emails_pending()) {
+ gtk_widget_show_all(mail_dialog->dialog);
+ mail_dialog->in_use = TRUE;
+ pidgin_blist_set_headline(NULL, NULL, NULL, NULL, NULL);
+ mail_dialog->in_use = FALSE;
+ }
+ purple_signal_emit(purple_notify_get_handle(), "displaying-emails-clear");
}
static void
@@ -486,9 +502,39 @@ searchresults_callback_wrapper_cb(GtkWidget *widget, PidginNotifySearchResultsBu
g_list_free(row);
}
+/* copy-paste from gtkrequest.c */
+static void
+pidgin_widget_decorate_account(GtkWidget *cont, PurpleAccount *account)
+{
+ GtkWidget *image;
+ GdkPixbuf *pixbuf;
+
+ if (!account)
+ return;
+
+ pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
+ image = gtk_image_new_from_pixbuf(pixbuf);
+ g_object_unref(G_OBJECT(pixbuf));
+
+ gtk_widget_set_tooltip_text(image,
+ purple_account_get_username(account));
+
+ if (GTK_IS_DIALOG(cont)) {
+ gtk_box_pack_start(GTK_BOX(gtk_dialog_get_action_area(
+ GTK_DIALOG(cont))), image, FALSE, TRUE, 0);
+ gtk_box_reorder_child(GTK_BOX(gtk_dialog_get_action_area(
+ GTK_DIALOG(cont))), image, 0);
+ } else if (GTK_IS_HBOX(cont)) {
+ gtk_misc_set_alignment(GTK_MISC(image), 0, 0);
+ gtk_box_pack_end(GTK_BOX(cont), image, FALSE, TRUE, 0);
+ }
+ gtk_widget_show(image);
+}
+
static void *
pidgin_notify_message(PurpleNotifyMsgType type, const char *title,
- const char *primary, const char *secondary)
+ const char *primary, const char *secondary,
+ PurpleRequestCommonParameters *cpar)
{
GtkWidget *dialog;
GtkWidget *hbox;
@@ -534,16 +580,21 @@ pidgin_notify_message(PurpleNotifyMsgType type, const char *title,
gtk_container_set_border_width(GTK_CONTAINER(dialog), PIDGIN_HIG_BORDER);
gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
- gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
- gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER);
- gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BOX_SPACE);
+ gtk_box_set_spacing(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
+ PIDGIN_HIG_BORDER);
+ gtk_container_set_border_width(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
+ PIDGIN_HIG_BOX_SPACE);
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
- gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BORDER);
+ gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
+ hbox);
if (img != NULL)
gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
+ pidgin_widget_decorate_account(hbox,
+ purple_request_cpar_get_account(cpar));
+
primary_esc = g_markup_escape_text(primary, -1);
secondary_esc = (secondary != NULL) ? g_markup_escape_text(secondary, -1) : NULL;
g_snprintf(label_text, sizeof(label_text),
@@ -561,6 +612,8 @@ pidgin_notify_message(PurpleNotifyMsgType type, const char *title,
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+ g_object_set_data(G_OBJECT(dialog), "pidgin-parent-from",
+ purple_request_cpar_get_parent_from(cpar));
pidgin_auto_parent_window(dialog);
gtk_widget_show_all(dialog);
@@ -689,6 +742,8 @@ pidgin_notify_emails(PurpleConnection *gc, size_t count, gboolean detailed,
PurpleAccount *account;
PidginNotifyMailData *data = NULL, *data2;
gboolean new_data = FALSE;
+ GtkTreeSelection *sel;
+ GtkTreeIter iter;
/* Don't bother updating if there aren't new emails and we don't have any displayed currently */
if (count == 0 && mail_dialog == NULL)
@@ -734,6 +789,7 @@ pidgin_notify_emails(PurpleConnection *gc, size_t count, gboolean detailed,
g_free(to_text);
g_free(from_text);
g_free(subject_text);
+ (void)first;
/* If we don't keep track of this, will leak "data" for each of the notifications except the last */
data2 = pidgin_notify_add_mail(mail_dialog->treemodel, account, notification, urls ? *urls : NULL, 0, FALSE, &new_data);
@@ -775,7 +831,14 @@ pidgin_notify_emails(PurpleConnection *gc, size_t count, gboolean detailed,
}
}
- if (!GTK_WIDGET_VISIBLE(mail_dialog->dialog)) {
+ /* Select first item if nothing selected */
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(mail_dialog->treeview));
+ if ((gtk_tree_selection_count_selected_rows(sel) < 1)
+ && gtk_tree_model_get_iter_first(GTK_TREE_MODEL(mail_dialog->treemodel), &iter)) {
+ gtk_tree_selection_select_iter(sel, &iter);
+ }
+
+ if (!gtk_widget_get_visible(mail_dialog->dialog)) {
GdkPixbuf *pixbuf = gtk_widget_render_icon(mail_dialog->dialog, PIDGIN_STOCK_DIALOG_MAIL,
gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL), NULL);
char *label_text = g_strdup_printf(ngettext("<b>%d new email.</b>",
@@ -785,13 +848,13 @@ pidgin_notify_emails(PurpleConnection *gc, size_t count, gboolean detailed,
remove the notifications when replacing an
old notification. */
pidgin_blist_set_headline(label_text,
- pixbuf, G_CALLBACK(gtk_widget_show_all), mail_dialog->dialog,
+ pixbuf, G_CALLBACK(pidgin_notify_emails_present), mail_dialog->dialog,
(GDestroyNotify)reset_mail_dialog);
mail_dialog->in_use = FALSE;
g_free(label_text);
if (pixbuf)
g_object_unref(pixbuf);
- } else if (!GTK_WIDGET_HAS_FOCUS(mail_dialog->dialog))
+ } else if (!gtk_widget_has_focus(mail_dialog->dialog))
pidgin_set_urgent(GTK_WINDOW(mail_dialog->dialog), TRUE);
return data;
@@ -800,7 +863,7 @@ pidgin_notify_emails(PurpleConnection *gc, size_t count, gboolean detailed,
static gboolean
formatted_input_cb(GtkWidget *win, GdkEventKey *event, gpointer data)
{
- if (event->keyval == GDK_Escape)
+ if (event->keyval == GDK_KEY_Escape)
{
purple_notify_close(PURPLE_NOTIFY_FORMATTED, win);
@@ -810,21 +873,6 @@ formatted_input_cb(GtkWidget *win, GdkEventKey *event, gpointer data)
return FALSE;
}
-static GtkIMHtmlOptions
-notify_imhtml_options(void)
-{
- GtkIMHtmlOptions options = 0;
-
- if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting"))
- options |= GTK_IMHTML_NO_COLOURS | GTK_IMHTML_NO_FONTS | GTK_IMHTML_NO_SIZES;
-
- options |= GTK_IMHTML_NO_COMMENTS;
- options |= GTK_IMHTML_NO_TITLE;
- options |= GTK_IMHTML_NO_NEWLINE;
- options |= GTK_IMHTML_NO_SCROLL;
- return options;
-}
-
static void *
pidgin_notify_formatted(const char *title, const char *primary,
const char *secondary, const char *text)
@@ -833,21 +881,23 @@ pidgin_notify_formatted(const char *title, const char *primary,
GtkWidget *vbox;
GtkWidget *label;
GtkWidget *button;
- GtkWidget *imhtml;
+ GtkWidget *web_view;
GtkWidget *frame;
char label_text[2048];
char *linked_text, *primary_esc, *secondary_esc;
window = gtk_dialog_new();
gtk_window_set_title(GTK_WINDOW(window), title);
+#if !GTK_CHECK_VERSION(3,0,0)
gtk_container_set_border_width(GTK_CONTAINER(window), PIDGIN_HIG_BORDER);
+#endif
gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
g_signal_connect(G_OBJECT(window), "delete_event",
G_CALLBACK(formatted_close_cb), NULL);
/* Setup the main vbox */
- vbox = GTK_DIALOG(window)->vbox;
+ vbox = gtk_dialog_get_content_area(GTK_DIALOG(window));
/* Setup the descriptive label */
primary_esc = g_markup_escape_text(primary, -1);
@@ -869,12 +919,10 @@ pidgin_notify_formatted(const char *title, const char *primary,
gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
gtk_widget_show(label);
- /* Add the imhtml */
- frame = pidgin_create_imhtml(FALSE, &imhtml, NULL, NULL);
- gtk_widget_set_name(imhtml, "pidgin_notify_imhtml");
- gtk_imhtml_set_format_functions(GTK_IMHTML(imhtml),
- gtk_imhtml_get_format_functions(GTK_IMHTML(imhtml)) | GTK_IMHTML_IMAGE);
- gtk_widget_set_size_request(imhtml, 300, 250);
+ /* Add the webview */
+ frame = pidgin_create_webview(FALSE, &web_view, NULL);
+ gtk_widget_set_name(web_view, "pidgin_notify_webview");
+ gtk_widget_set_size_request(web_view, 300, 250);
gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
gtk_widget_show(frame);
@@ -889,10 +937,10 @@ pidgin_notify_formatted(const char *title, const char *primary,
/* Make sure URLs are clickable */
linked_text = purple_markup_linkify(text);
- gtk_imhtml_append_text(GTK_IMHTML(imhtml), linked_text, notify_imhtml_options());
+ pidgin_webview_load_html_string(PIDGIN_WEBVIEW(web_view), linked_text);
g_free(linked_text);
- g_object_set_data(G_OBJECT(window), "info-widget", imhtml);
+ g_object_set_data(G_OBJECT(window), "webview-widget", web_view);
/* Show the window */
pidgin_auto_parent_window(window);
@@ -977,7 +1025,7 @@ pidgin_notify_searchresults(PurpleConnection *gc, const char *title,
G_CALLBACK(searchresults_close_cb), data);
/* Setup the main vbox */
- vbox = GTK_DIALOG(window)->vbox;
+ vbox = gtk_dialog_get_content_area(GTK_DIALOG(window));
/* Setup the descriptive label */
primary_esc = (primary != NULL) ? g_markup_escape_text(primary, -1) : NULL;
@@ -1034,7 +1082,11 @@ pidgin_notify_searchresults(PurpleConnection *gc, const char *title,
renderer = gtk_cell_renderer_text_new();
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), -1,
- column->title, renderer, "text", i, NULL);
+ purple_notify_searchresult_column_get_title(column), renderer, "text", i, NULL);
+
+ if (!purple_notify_searchresult_column_is_visible(column))
+ gtk_tree_view_column_set_visible(gtk_tree_view_get_column(GTK_TREE_VIEW(treeview), i), FALSE);
+
i++;
}
@@ -1044,7 +1096,7 @@ pidgin_notify_searchresults(PurpleConnection *gc, const char *title,
switch (b->type) {
case PURPLE_NOTIFY_BUTTON_LABELED:
if(b->label) {
- button = gtk_button_new_with_label(b->label);
+ button = gtk_dialog_add_button(GTK_DIALOG(window), b->label, GTK_RESPONSE_NONE);
} else {
purple_debug_warning("gtknotify", "Missing button label\n");
}
@@ -1089,7 +1141,7 @@ pidgin_notify_searchresults(PurpleConnection *gc, const char *title,
g_signal_connect_swapped(G_OBJECT(close_button), "clicked",
G_CALLBACK(searchresults_close_cb), data);
- data->account = gc->account;
+ data->account = purple_connection_get_account(gc);
data->model = model;
data->treeview = treeview;
data->window = window;
@@ -1143,10 +1195,9 @@ pidgin_notify_userinfo(PurpleConnection *gc, const char *who,
info = purple_notify_user_info_get_text_with_newline(user_info, "<br />");
pinfo = g_hash_table_lookup(userinfo, key);
if (pinfo != NULL) {
- GtkIMHtml *imhtml = g_object_get_data(G_OBJECT(pinfo->window), "info-widget");
+ GtkWidget *webview = g_object_get_data(G_OBJECT(pinfo->window), "webview-widget");
char *linked_text = purple_markup_linkify(info);
- gtk_imhtml_clear(imhtml);
- gtk_imhtml_append_text(imhtml, linked_text, notify_imhtml_options());
+ pidgin_webview_load_html_string(PIDGIN_WEBVIEW(webview), linked_text);
g_free(linked_text);
g_free(key);
ui_handle = pinfo->window;
@@ -1214,7 +1265,7 @@ uri_command(GSList *arg_list, gboolean sync)
tmp = g_strdup_printf(
_("The browser command \"%s\" is invalid."),
program ? program : "(null)");
- purple_notify_error(NULL, NULL, _("Unable to open URL"), tmp);
+ purple_notify_error(NULL, NULL, _("Unable to open URL"), tmp, NULL);
g_free(tmp);
return FALSE;
@@ -1250,7 +1301,7 @@ uri_command(GSList *arg_list, gboolean sync)
tmp = g_strdup_printf(_("Error launching \"%s\": %s"), program,
error ? error->message : "(null)");
g_error_free(error);
- purple_notify_error(NULL, NULL, _("Unable to open URL"), tmp);
+ purple_notify_error(NULL, NULL, _("Unable to open URL"), tmp, NULL);
g_free(tmp);
g_free(argv);
@@ -1348,9 +1399,6 @@ pidgin_notify_uri(const char *uri)
} else if (place == PIDGIN_BROWSER_NEW_TAB) {
uri_custom = g_strdup_printf("openURL(%s,new-tab)",
uri_escaped);
- } else if (place == PIDGIN_BROWSER_CURRENT) {
- uri_custom = g_strdup_printf("openURL(%s)",
- uri_escaped);
}
if (uri_custom != NULL) {
@@ -1374,23 +1422,6 @@ pidgin_notify_uri(const char *uri)
argv_remote = g_slist_append(argv_remote, "-remote");
argv_remote = g_slist_append(argv_remote, uri_custom);
}
- } else if (!strcmp(web_browser, "netscape")) {
- argv = g_slist_append(argv, "netscape");
- argv = g_slist_append(argv, uri_escaped);
-
- if (place == PIDGIN_BROWSER_NEW_WINDOW) {
- uri_custom = g_strdup_printf("openURL(%s,new-window)",
- uri_escaped);
- } else if (place == PIDGIN_BROWSER_CURRENT) {
- uri_custom = g_strdup_printf("openURL(%s)",
- uri_escaped);
- }
-
- if (uri_custom) {
- argv_remote = g_slist_append(argv_remote, "netscape");
- argv_remote = g_slist_append(argv_remote, "-remote");
- argv_remote = g_slist_append(argv_remote, uri_custom);
- }
} else if (!strcmp(web_browser, "opera")) {
argv = g_slist_append(argv, "opera");
@@ -1398,9 +1429,7 @@ pidgin_notify_uri(const char *uri)
argv = g_slist_append(argv, "-newwindow");
else if (place == PIDGIN_BROWSER_NEW_TAB)
argv = g_slist_append(argv, "-newpage");
- else if (place == PIDGIN_BROWSER_CURRENT)
- argv = g_slist_append(argv, "-activetab");
- /* `opera -remote "openURL(%s)"` command causes Pidgin's hang,
+ /* `opera -remote "openURL(%s)"` command causes Pidgin to hang,
* if there is no Opera instance running.
*/
@@ -1438,7 +1467,7 @@ pidgin_notify_uri(const char *uri)
if (usercmd_command == NULL || *usercmd_command == '\0') {
purple_notify_error(NULL, NULL, _("Unable to open URL"),
_("The 'Manual' browser command has been "
- "chosen, but no command has been set."));
+ "chosen, but no command has been set."), NULL);
g_free(uri_escaped);
return NULL;
}
@@ -1448,7 +1477,7 @@ pidgin_notify_uri(const char *uri)
{
purple_notify_error(NULL, NULL, _("Unable to open URL: "
"the 'Manual' browser command seems invalid."),
- error ? error->message : NULL);
+ error ? error->message : NULL, NULL);
g_error_free(error);
g_free(uri_escaped);
return NULL;
@@ -1571,14 +1600,13 @@ pidgin_create_notification_dialog(PidginNotifyType type)
dialog = gtk_dialog_new();
+ /* Vertical box */
+ vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+
/* Setup the dialog */
gtk_container_set_border_width(GTK_CONTAINER(dialog), PIDGIN_HIG_BOX_SPACE);
- gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BOX_SPACE);
- gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
- gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER);
-
- /* Vertical box */
- vbox = GTK_DIALOG(dialog)->vbox;
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), PIDGIN_HIG_BOX_SPACE);
+ gtk_box_set_spacing(GTK_BOX(vbox), PIDGIN_HIG_BORDER);
/* Golden ratio it up! */
gtk_widget_set_size_request(dialog, 550, 400);
@@ -1610,11 +1638,13 @@ pidgin_create_notification_dialog(PidginNotifyType type)
gtk_tree_view_set_search_column(GTK_TREE_VIEW(spec_dialog->treeview), PIDGIN_MAIL_TEXT);
gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(spec_dialog->treeview),
pidgin_tree_view_search_equal_func, NULL, NULL);
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(spec_dialog->treeview));
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE);
g_signal_connect(G_OBJECT(dialog), "response",
G_CALLBACK(email_response_cb), spec_dialog);
- g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(spec_dialog->treeview))),
- "changed", G_CALLBACK(selection_changed_cb), spec_dialog);
+ g_signal_connect(G_OBJECT(sel), "changed",
+ G_CALLBACK(selection_changed_cb), spec_dialog);
g_signal_connect(G_OBJECT(spec_dialog->treeview), "row-activated", G_CALLBACK(email_row_activated_cb), NULL);
column = gtk_tree_view_column_new();
@@ -1712,7 +1742,7 @@ pidgin_create_notification_dialog(PidginNotifyType type)
gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
- gtk_box_pack_start(GTK_BOX(vbox),
+ gtk_box_pack_start(GTK_BOX(vbox),
pidgin_make_scrollable(spec_dialog->treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, -1),
TRUE, TRUE, 2);
diff --git a/pidgin/gtknotify.h b/pidgin/gtknotify.h
index b5973a4995..a623dbdb46 100644
--- a/pidgin/gtknotify.h
+++ b/pidgin/gtknotify.h
@@ -1,8 +1,3 @@
-/**
- * @file gtknotify.h GTK+ Notification API
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -23,40 +18,76 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _PIDGINNOTIFY_H_
#define _PIDGINNOTIFY_H_
+/**
+ * SECTION:gtknotify
+ * @section_id: pidgin-gtknotify
+ * @short_description: <filename>gtknotify.h</filename>
+ * @title: Notification API
+ */
#include "notify.h"
#include "pounce.h"
+G_BEGIN_DECLS
+
/**
- * Adds a buddy pounce to the buddy pounce dialog
+ * pidgin_notify_pounce_add:
+ * @account: The account
+ * @pounce: The pounce
+ * @alias: The buddy alias
+ * @event: Event description
+ * @message: Pounce message
+ * @date: Pounce date
*
- * @param account The account
- * @param pounce The pounce
- * @param alias The buddy alias
- * @param event Event description
- * @param message Pounce message
- * @param date Pounce date
+ * Adds a buddy pounce to the buddy pounce dialog
*/
void pidgin_notify_pounce_add(PurpleAccount *account, PurplePounce *pounce,
const char *alias, const char *event, const char *message, const char *date);
/**
+ * pidgin_notify_get_ui_ops:
+ *
* Returns the UI operations structure for GTK+ notification functions.
*
- * @return The GTK+ UI notify operations structure.
+ * Returns: The GTK+ UI notify operations structure.
*/
PurpleNotifyUiOps *pidgin_notify_get_ui_ops(void);
/**
+ * pidgin_notify_init:
+ *
* Initializes the GTK+ notifications subsystem.
*/
void pidgin_notify_init(void);
/**
+ * pidgin_notify_uninit:
+ *
* Uninitialized the GTK+ notifications subsystem.
*/
void pidgin_notify_uninit(void);
+/**
+ * pidgin_notify_emails_pending:
+ *
+ * Returns TRUE if there are unseen emails, FALSE otherwise.
+ *
+ * Returns: TRUE if there are unseen emails, FALSE otherwise.
+ */
+gboolean pidgin_notify_emails_pending(void);
+
+/**
+ * pidgin_notify_emails_present:
+ *
+ * Presents mail dialog to the user.
+ *
+ * Returns: void.
+ */
+void pidgin_notify_emails_present(void *data);
+
+G_END_DECLS
+
#endif /* _PIDGINNOTIFY_H_ */
diff --git a/pidgin/gtkplugin.c b/pidgin/gtkplugin.c
index afa21e0a6a..4e0c8a41f2 100644
--- a/pidgin/gtkplugin.c
+++ b/pidgin/gtkplugin.c
@@ -1,8 +1,3 @@
-/**
- * @file gtkplugin.c GTK+ Plugins support
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -35,8 +30,30 @@
#include <string.h>
+#include "gtk3compat.h"
+
#define PIDGIN_RESPONSE_CONFIGURE 98121
+typedef struct
+{
+ enum
+ {
+ PIDGIN_PLUGIN_UI_DATA_TYPE_FRAME,
+ PIDGIN_PLUGIN_UI_DATA_TYPE_REQUEST
+ } type;
+
+ union
+ {
+ struct
+ {
+ GtkWidget *dialog;
+ PurplePluginPrefFrame *pref_frame;
+ } frame;
+
+ gpointer request_handle;
+ } u;
+} PidginPluginUiData;
+
static void plugin_toggled_stage_two(PurplePlugin *plug, GtkTreeModel *model,
GtkTreeIter *iter, gboolean unload);
@@ -52,10 +69,37 @@ static gchar *plugin_website_uri = NULL;
static GtkLabel *plugin_filename = NULL;
static GtkWidget *pref_button = NULL;
-static GHashTable *plugin_pref_dialogs = NULL;
-GtkWidget *
-pidgin_plugin_get_config_frame(PurplePlugin *plugin)
+static gboolean
+pidgin_plugin_has_prefs(PurplePlugin *plugin)
+{
+ PurplePluginUiInfo *pinfo;
+
+ g_return_val_if_fail(plugin != NULL, FALSE);
+
+ if (!purple_plugin_is_loaded(plugin))
+ return FALSE;
+
+ if (PIDGIN_IS_PIDGIN_PLUGIN(plugin) && plugin->info->ui_info &&
+ PIDGIN_PLUGIN_UI_INFO(plugin)->get_config_frame)
+ {
+ return TRUE;
+ }
+
+ if (!plugin->info)
+ return FALSE;
+
+ pinfo = plugin->info->prefs_info;
+
+ if (!pinfo)
+ return FALSE;
+
+ return (pinfo->get_plugin_pref_frame || pinfo->get_plugin_pref_request);
+}
+
+static GtkWidget *
+pidgin_plugin_get_config_frame(PurplePlugin *plugin,
+ PurplePluginPrefFrame **purple_pref_frame)
{
GtkWidget *config = NULL;
@@ -69,15 +113,6 @@ pidgin_plugin_get_config_frame(PurplePlugin *plugin)
ui_info = PIDGIN_PLUGIN_UI_INFO(plugin);
config = ui_info->get_config_frame(plugin);
-
- if (plugin->info->prefs_info
- && plugin->info->prefs_info->get_plugin_pref_frame)
- {
- purple_debug_warning("gtkplugin",
- "Plugin %s contains both, ui_info and "
- "prefs_info preferences; prefs_info will be "
- "ignored.", plugin->info->name);
- }
}
if (config == NULL && plugin->info->prefs_info
@@ -89,12 +124,139 @@ pidgin_plugin_get_config_frame(PurplePlugin *plugin)
config = pidgin_plugin_pref_create_frame(frame);
- plugin->info->prefs_info->frame = frame;
+ *purple_pref_frame = frame;
}
return config;
}
+static void
+pref_dialog_close(PurplePlugin *plugin)
+{
+ PidginPluginUiData *ui_data;
+
+ g_return_if_fail(plugin != NULL);
+
+ ui_data = plugin->ui_data;
+ if (ui_data == NULL)
+ return;
+
+ if (ui_data->type == PIDGIN_PLUGIN_UI_DATA_TYPE_REQUEST) {
+ purple_request_close(PURPLE_REQUEST_FIELDS,
+ ui_data->u.request_handle);
+ return;
+ }
+
+ g_return_if_fail(ui_data->type == PIDGIN_PLUGIN_UI_DATA_TYPE_FRAME);
+
+ gtk_widget_destroy(ui_data->u.frame.dialog);
+
+ if (ui_data->u.frame.pref_frame)
+ purple_plugin_pref_frame_destroy(ui_data->u.frame.pref_frame);
+
+ g_free(ui_data);
+ plugin->ui_data = NULL;
+}
+
+
+static void
+pref_dialog_response_cb(GtkWidget *dialog, int response, PurplePlugin *plugin)
+{
+ if (response == GTK_RESPONSE_CLOSE ||
+ response == GTK_RESPONSE_DELETE_EVENT)
+ {
+ pref_dialog_close(plugin);
+ }
+}
+
+static void
+pidgin_plugin_open_config(PurplePlugin *plugin, GtkWindow *parent)
+{
+ PurplePluginUiInfo *pinfo;
+ PidginPluginUiData *ui_data;
+ gboolean has_purple_frame, has_purple_request, has_pidgin_frame;
+ gint prefs_count;
+
+ g_return_if_fail(plugin != NULL);
+
+ if (!pidgin_plugin_has_prefs(plugin)) {
+ purple_debug_warning("gtkplugin", "Plugin has no prefs");
+ return;
+ }
+
+ if (plugin->ui_data != NULL)
+ return;
+
+ pinfo = plugin->info->prefs_info;
+ if (pinfo == NULL)
+ has_purple_frame = has_purple_request = FALSE;
+ else {
+ has_purple_frame = (pinfo->get_plugin_pref_frame != NULL);
+ has_purple_request = (pinfo->get_plugin_pref_request != NULL);
+ }
+ has_pidgin_frame = (PIDGIN_IS_PIDGIN_PLUGIN(plugin) &&
+ plugin->info->ui_info &&
+ PIDGIN_PLUGIN_UI_INFO(plugin)->get_config_frame);
+
+ prefs_count = 0;
+ if (has_purple_frame)
+ prefs_count++;
+ if (has_purple_request)
+ prefs_count++;
+ if (has_pidgin_frame)
+ prefs_count++;
+
+ if (prefs_count > 1) {
+ purple_debug_warning("gtkplugin", "Plugin %s contains more than"
+ " one prefs callback, some will be ignored.",
+ plugin->info->name);
+ }
+ g_return_if_fail(prefs_count > 0);
+
+ plugin->ui_data = ui_data = g_new0(PidginPluginUiData, 1);
+
+ /* Priority: pidgin frame > purple request > purple frame
+ * Purple frame could be replaced with purple request some day.
+ */
+ if (has_purple_request && !has_pidgin_frame) {
+ ui_data->type = PIDGIN_PLUGIN_UI_DATA_TYPE_REQUEST;
+ ui_data->u.request_handle =
+ pinfo->get_plugin_pref_request(plugin);
+ purple_request_add_close_notify(ui_data->u.request_handle,
+ purple_callback_set_zero, &plugin->ui_data);
+ purple_request_add_close_notify(ui_data->u.request_handle,
+ g_free, ui_data);
+ } else {
+ GtkWidget *box, *dialog;
+
+ ui_data->type = PIDGIN_PLUGIN_UI_DATA_TYPE_FRAME;
+
+ box = pidgin_plugin_get_config_frame(plugin,
+ &ui_data->u.frame.pref_frame);
+ if (box == NULL) {
+ purple_debug_error("gtkplugin",
+ "Failed to display prefs frame");
+ g_free(ui_data);
+ plugin->ui_data = NULL;
+ return;
+ }
+
+ ui_data->u.frame.dialog = dialog = gtk_dialog_new_with_buttons(
+ PIDGIN_ALERT_TITLE, parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE, NULL);
+
+ g_signal_connect(G_OBJECT(dialog), "response",
+ G_CALLBACK(pref_dialog_response_cb), plugin);
+ gtk_container_add(GTK_CONTAINER(
+ gtk_dialog_get_content_area(GTK_DIALOG(dialog))), box);
+ gtk_window_set_role(GTK_WINDOW(dialog), "plugin_config");
+ gtk_window_set_title(GTK_WINDOW(dialog),
+ _(purple_plugin_get_name(plugin)));
+ gtk_widget_show_all(dialog);
+ }
+}
+
void
pidgin_plugins_save(void)
{
@@ -188,11 +350,7 @@ static void plugin_loading_common(PurplePlugin *plugin, GtkTreeView *view, gbool
if (plug == plugin)
{
gtk_widget_set_sensitive(pref_button,
- loaded
- && ((PIDGIN_IS_PIDGIN_PLUGIN(plug) && plug->info->ui_info
- && PIDGIN_PLUGIN_UI_INFO(plug)->get_config_frame)
- || (plug->info->prefs_info
- && plug->info->prefs_info->get_plugin_pref_frame)));
+ pidgin_plugin_has_prefs(plug));
}
}
@@ -213,27 +371,6 @@ static void plugin_unload_cb(PurplePlugin *plugin, gpointer data)
plugin_loading_common(plugin, view, FALSE);
}
-static void pref_dialog_response_cb(GtkWidget *d, int response, PurplePlugin *plug)
-{
- switch (response) {
- case GTK_RESPONSE_CLOSE:
- case GTK_RESPONSE_DELETE_EVENT:
- g_hash_table_remove(plugin_pref_dialogs, plug);
- if (g_hash_table_size(plugin_pref_dialogs) == 0) {
- g_hash_table_destroy(plugin_pref_dialogs);
- plugin_pref_dialogs = NULL;
- }
- gtk_widget_destroy(d);
-
- if (plug->info->prefs_info && plug->info->prefs_info->frame) {
- purple_plugin_pref_frame_destroy(plug->info->prefs_info->frame);
- plug->info->prefs_info->frame = NULL;
- }
-
- break;
- }
-}
-
static void plugin_unload_confirm_cb(gpointer *data)
{
PurplePlugin *plugin = (PurplePlugin *)data[0];
@@ -251,7 +388,6 @@ static void plugin_toggled(GtkCellRendererToggle *cell, gchar *pth, gpointer dat
GtkTreeIter *iter = g_new(GtkTreeIter, 1);
GtkTreePath *path = gtk_tree_path_new_from_string(pth);
PurplePlugin *plug;
- GtkWidget *dialog = NULL;
gtk_tree_model_get_iter(model, iter, path);
gtk_tree_path_free(path);
@@ -275,9 +411,7 @@ static void plugin_toggled(GtkCellRendererToggle *cell, gchar *pth, gpointer dat
}
else
{
- if (plugin_pref_dialogs != NULL &&
- (dialog = g_hash_table_lookup(plugin_pref_dialogs, plug)))
- pref_dialog_response_cb(dialog, GTK_RESPONSE_DELETE_EVENT, plug);
+ pref_dialog_close(plug);
if (plug->dependent_plugins != NULL)
{
@@ -300,12 +434,11 @@ static void plugin_toggled(GtkCellRendererToggle *cell, gchar *pth, gpointer dat
cb_data[2] = iter;
purple_request_action(plugin_dialog, NULL,
- _("Multiple plugins will be unloaded."),
- tmp->str, 0,
- NULL, NULL, NULL,
- cb_data, 2,
- _("Unload Plugins"), G_CALLBACK(plugin_unload_confirm_cb),
- _("Cancel"), g_free);
+ _("Multiple plugins will be unloaded."),
+ tmp->str, 0, NULL, cb_data, 2,
+ _("Unload Plugins"),
+ G_CALLBACK(plugin_unload_confirm_cb),
+ _("Cancel"), g_free);
g_string_free(tmp, TRUE);
}
else
@@ -326,12 +459,12 @@ static void plugin_toggled_stage_two(PurplePlugin *plug, GtkTreeModel *model, Gt
if (!plug->error)
{
- purple_notify_warning(NULL, NULL, primary, reload);
+ purple_notify_warning(NULL, NULL, primary, reload, NULL);
}
else
{
char *tmp = g_strdup_printf("%s\n\n%s", reload, plug->error);
- purple_notify_warning(NULL, NULL, primary, tmp);
+ purple_notify_warning(NULL, NULL, primary, tmp, NULL);
g_free(tmp);
}
@@ -341,12 +474,7 @@ static void plugin_toggled_stage_two(PurplePlugin *plug, GtkTreeModel *model, Gt
pidgin_clear_cursor(plugin_dialog);
}
- gtk_widget_set_sensitive(pref_button,
- purple_plugin_is_loaded(plug)
- && ((PIDGIN_IS_PIDGIN_PLUGIN(plug) && plug->info->ui_info
- && PIDGIN_PLUGIN_UI_INFO(plug)->get_config_frame)
- || (plug->info->prefs_info
- && plug->info->prefs_info->get_plugin_pref_frame)));
+ gtk_widget_set_sensitive(pref_button, pidgin_plugin_has_prefs(plug));
if (plug->error != NULL)
{
@@ -469,12 +597,7 @@ static void prefs_plugin_sel (GtkTreeSelection *sel, GtkTreeModel *model)
g_free(tmp);
}
- gtk_widget_set_sensitive(pref_button,
- purple_plugin_is_loaded(plug)
- && ((PIDGIN_IS_PIDGIN_PLUGIN(plug) && plug->info->ui_info
- && PIDGIN_PLUGIN_UI_INFO(plug)->get_config_frame)
- || (plug->info->prefs_info
- && plug->info->prefs_info->get_plugin_pref_frame)));
+ gtk_widget_set_sensitive(pref_button, pidgin_plugin_has_prefs(plug));
/* Make sure the selected plugin is still visible */
g_idle_add(ensure_plugin_visible, sel);
@@ -484,22 +607,20 @@ static void prefs_plugin_sel (GtkTreeSelection *sel, GtkTreeModel *model)
static void plugin_dialog_response_cb(GtkWidget *d, int response, GtkTreeSelection *sel)
{
- PurplePlugin *plug;
- GtkWidget *dialog, *box;
+ PurplePlugin *plugin;
GtkTreeModel *model;
GValue val;
GtkTreeIter iter;
+ GList *it;
switch (response) {
case GTK_RESPONSE_CLOSE:
case GTK_RESPONSE_DELETE_EVENT:
purple_request_close_with_handle(plugin_dialog);
purple_signals_disconnect_by_handle(plugin_dialog);
+ for (it = purple_plugins_get_all(); it; it = g_list_next(it))
+ pref_dialog_close(it->data);
gtk_widget_destroy(d);
- if (plugin_pref_dialogs != NULL) {
- g_hash_table_destroy(plugin_pref_dialogs);
- plugin_pref_dialogs = NULL;
- }
plugin_dialog = NULL;
break;
case PIDGIN_RESPONSE_CONFIGURE:
@@ -507,31 +628,13 @@ static void plugin_dialog_response_cb(GtkWidget *d, int response, GtkTreeSelecti
return;
val.g_type = 0;
gtk_tree_model_get_value(model, &iter, 2, &val);
- plug = g_value_get_pointer(&val);
- if (plug == NULL)
- break;
- if (plugin_pref_dialogs != NULL &&
- g_hash_table_lookup(plugin_pref_dialogs, plug))
- break;
- box = pidgin_plugin_get_config_frame(plug);
- if (box == NULL)
+ plugin = g_value_get_pointer(&val);
+ g_value_unset(&val);
+ if (plugin == NULL)
break;
- dialog = gtk_dialog_new_with_buttons(PIDGIN_ALERT_TITLE, GTK_WINDOW(d),
- GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
- NULL);
- if (plugin_pref_dialogs == NULL)
- plugin_pref_dialogs = g_hash_table_new(NULL, NULL);
-
- g_hash_table_insert(plugin_pref_dialogs, plug, dialog);
+ pidgin_plugin_open_config(plugin, GTK_WINDOW(d));
- g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(pref_dialog_response_cb), plug);
- gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), box);
- gtk_window_set_role(GTK_WINDOW(dialog), "plugin_config");
- gtk_window_set_title(GTK_WINDOW(dialog), _(purple_plugin_get_name(plug)));
- gtk_widget_show_all(dialog);
- g_value_unset(&val);
break;
}
}
@@ -559,12 +662,18 @@ show_plugin_prefs_cb(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *co
}
static gboolean
-pidgin_plugins_paint_tooltip(GtkWidget *tipwindow, gpointer data)
+pidgin_plugins_paint_tooltip(GtkWidget *tipwindow, cairo_t *cr, gpointer data)
{
PangoLayout *layout = g_object_get_data(G_OBJECT(tipwindow), "tooltip-plugin");
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context = gtk_widget_get_style_context(tipwindow);
+ gtk_style_context_add_class(context, GTK_STYLE_CLASS_TOOLTIP);
+ gtk_render_layout(context, cr, 6, 6, layout);
+#else
gtk_paint_layout(tipwindow->style, tipwindow->window, GTK_STATE_NORMAL, FALSE,
- NULL, tipwindow, "tooltip",
- 6, 6, layout);
+ NULL, tipwindow, "tooltip",
+ 6, 6, layout);
+#endif
return TRUE;
}
@@ -635,7 +744,7 @@ website_button_clicked_cb(GtkButton *button, GdkEventButton *event,
static GtkWidget *
create_details()
{
- GtkBox *vbox = GTK_BOX(gtk_vbox_new(FALSE, 3));
+ GtkBox *vbox = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 3));
GtkSizeGroup *sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
GtkWidget *label, *view, *website_button;
@@ -717,10 +826,8 @@ void pidgin_plugin_dialog_show()
return;
}
- plugin_dialog = gtk_dialog_new_with_buttons(_("Plugins"),
- NULL,
- GTK_DIALOG_NO_SEPARATOR,
- NULL);
+ plugin_dialog = gtk_dialog_new();
+ gtk_window_set_title(GTK_WINDOW(plugin_dialog), _("Plugins"));
pref_button = gtk_dialog_add_button(GTK_DIALOG(plugin_dialog),
_("Configure Pl_ugin"), PIDGIN_RESPONSE_CONFIGURE);
gtk_dialog_add_button(GTK_DIALOG(plugin_dialog),
@@ -772,7 +879,7 @@ void pidgin_plugin_dialog_show()
gtk_tree_view_append_column (GTK_TREE_VIEW(event_view), col);
gtk_tree_view_column_set_sort_column_id(col, 1);
g_object_unref(G_OBJECT(ls));
- gtk_box_pack_start(GTK_BOX(GTK_DIALOG(plugin_dialog)->vbox),
+ gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(plugin_dialog))),
pidgin_make_scrollable(event_view, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, -1),
TRUE, TRUE, 0);
gtk_tree_view_set_search_column(GTK_TREE_VIEW(event_view), 1);
@@ -788,8 +895,8 @@ void pidgin_plugin_dialog_show()
gtk_expander_set_use_markup(GTK_EXPANDER(expander), TRUE);
gtk_widget_set_sensitive(expander, FALSE);
gtk_container_add(GTK_CONTAINER(expander), create_details());
- gtk_box_pack_start(GTK_BOX(GTK_DIALOG(plugin_dialog)->vbox), expander,
- FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(plugin_dialog))),
+ expander, FALSE, FALSE, 0);
g_signal_connect (G_OBJECT (sel), "changed", G_CALLBACK (prefs_plugin_sel), NULL);
diff --git a/pidgin/gtkplugin.h b/pidgin/gtkplugin.h
index 8b07be4f12..edf54cfcb7 100644
--- a/pidgin/gtkplugin.h
+++ b/pidgin/gtkplugin.h
@@ -1,8 +1,3 @@
-/**
- * @file gtkplugin.h GTK+ Plugin API
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -23,8 +18,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _PIDGINPLUGIN_H_
#define _PIDGINPLUGIN_H_
+/**
+ * SECTION:gtkplugin
+ * @section_id: pidgin-gtkplugin
+ * @short_description: <filename>gtkplugin.h</filename>
+ * @title: Plugin API
+ */
#include "pidgin.h"
#include "plugin.h"
@@ -32,15 +34,15 @@
typedef struct _PidginPluginUiInfo PidginPluginUiInfo;
/**
+ * PidginPluginUiInfo:
+ *
* A GTK+ UI structure for plugins.
*/
struct _PidginPluginUiInfo
{
GtkWidget *(*get_config_frame)(PurplePlugin *plugin);
- int page_num; /**< Reserved */
-
- /* padding */
+ /*< private >*/
void (*_pidgin_reserved1)(void);
void (*_pidgin_reserved2)(void);
void (*_pidgin_reserved3)(void);
@@ -56,25 +58,22 @@ struct _PidginPluginUiInfo
#define PIDGIN_PLUGIN_UI_INFO(plugin) \
((PidginPluginUiInfo *)(plugin)->info->ui_info)
-/**
- * Returns the configuration frame widget for a GTK+ plugin, if one
- * exists.
- *
- * @param plugin The plugin.
- *
- * @return The frame, if the plugin is a GTK+ plugin and provides a
- * configuration frame.
- */
-GtkWidget *pidgin_plugin_get_config_frame(PurplePlugin *plugin);
+G_BEGIN_DECLS
/**
+ * pidgin_plugins_save:
+ *
* Saves all loaded plugins.
*/
void pidgin_plugins_save(void);
/**
+ * pidgin_plugin_dialog_show:
+ *
* Shows the Plugins dialog
*/
void pidgin_plugin_dialog_show(void);
+G_END_DECLS
+
#endif /* _PIDGINPLUGIN_H_ */
diff --git a/pidgin/gtkpluginpref.c b/pidgin/gtkpluginpref.c
index 024575a034..d005d21543 100644
--- a/pidgin/gtkpluginpref.c
+++ b/pidgin/gtkpluginpref.c
@@ -1,8 +1,3 @@
-/**
- * @file gtkpluginpref.c GTK+ Plugin preferences
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -29,13 +24,15 @@
#include "debug.h"
#include "internal.h"
+#include "pidgin.h"
#include "pluginpref.h"
#include "prefs.h"
-#include "gtkimhtml.h"
+#include "gtk3compat.h"
#include "gtkpluginpref.h"
#include "gtkprefs.h"
#include "gtkutils.h"
+#include "gtkwebview.h"
static gboolean
entry_cb(GtkWidget *entry, gpointer data) {
@@ -48,27 +45,20 @@ entry_cb(GtkWidget *entry, gpointer data) {
static void
-imhtml_cb(GtkTextBuffer *buffer, gpointer data)
+webview_cb(PidginWebView *webview, gpointer data)
{
char *pref;
char *text;
- GtkIMHtml *imhtml = data;
- pref = g_object_get_data(G_OBJECT(imhtml), "pref-key");
+ pref = g_object_get_data(G_OBJECT(webview), "pref-key");
g_return_if_fail(pref);
- text = gtk_imhtml_get_markup(imhtml);
+ text = pidgin_webview_get_body_html(webview);
purple_prefs_set_string(pref, text);
g_free(text);
}
static void
-imhtml_format_cb(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons, gpointer data)
-{
- imhtml_cb(gtk_text_view_get_buffer(GTK_TEXT_VIEW(imhtml)), data);
-}
-
-static void
make_string_pref(GtkWidget *parent, PurplePluginPref *pref, GtkSizeGroup *sg) {
GtkWidget *box, *gtk_label, *entry;
const gchar *pref_name;
@@ -79,7 +69,7 @@ make_string_pref(GtkWidget *parent, PurplePluginPref *pref, GtkSizeGroup *sg) {
pref_label = purple_plugin_pref_get_label(pref);
format = purple_plugin_pref_get_format_type(pref);
- switch(purple_plugin_pref_get_type(pref)) {
+ switch(purple_plugin_pref_get_pref_type(pref)) {
case PURPLE_PLUGIN_PREF_CHOICE:
gtk_label = pidgin_prefs_dropdown_from_list(parent, pref_label,
PURPLE_PREF_STRING, pref_name,
@@ -101,10 +91,6 @@ make_string_pref(GtkWidget *parent, PurplePluginPref *pref, GtkSizeGroup *sg) {
if (purple_plugin_pref_get_masked(pref))
{
gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
-#if !GTK_CHECK_VERSION(2,16,0)
- if (gtk_entry_get_invisible_char(GTK_ENTRY(entry)) == '*')
- gtk_entry_set_invisible_char(GTK_ENTRY(entry), PIDGIN_INVISIBLE_CHAR);
-#endif /* Less than GTK+ 2.16 */
}
g_signal_connect(G_OBJECT(entry), "changed",
G_CALLBACK(entry_cb),
@@ -115,11 +101,10 @@ make_string_pref(GtkWidget *parent, PurplePluginPref *pref, GtkSizeGroup *sg) {
{
GtkWidget *hbox;
GtkWidget *spacer;
- GtkWidget *imhtml;
- GtkWidget *toolbar;
+ GtkWidget *webview;
GtkWidget *frame;
- box = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ box = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
gtk_widget_show(box);
gtk_box_pack_start(GTK_BOX(parent), box, FALSE, FALSE, 0);
@@ -132,7 +117,7 @@ make_string_pref(GtkWidget *parent, PurplePluginPref *pref, GtkSizeGroup *sg) {
if(sg)
gtk_size_group_add_widget(sg, gtk_label);
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
gtk_widget_show(hbox);
@@ -140,19 +125,26 @@ make_string_pref(GtkWidget *parent, PurplePluginPref *pref, GtkSizeGroup *sg) {
gtk_box_pack_start(GTK_BOX(hbox), spacer, FALSE, FALSE, 0);
gtk_widget_show(spacer);
- frame = pidgin_create_imhtml(TRUE, &imhtml, &toolbar, NULL);
- if (!(format & PURPLE_STRING_FORMAT_TYPE_HTML))
- gtk_widget_destroy(toolbar);
+ if ((format & PURPLE_STRING_FORMAT_TYPE_HTML) != 0) {
+ frame = pidgin_create_webview(TRUE, &webview, NULL);
+ } else {
+ frame = pidgin_create_webview(FALSE, &webview, NULL);
+ pidgin_webview_set_format_functions(PIDGIN_WEBVIEW(webview), 0);
+ }
- gtk_imhtml_append_text(GTK_IMHTML(imhtml), purple_prefs_get_string(pref_name),
- (format & PURPLE_STRING_FORMAT_TYPE_MULTILINE) ? 0 : GTK_IMHTML_NO_NEWLINE);
- gtk_label_set_mnemonic_widget(GTK_LABEL(gtk_label), imhtml);
+ if (format & PURPLE_STRING_FORMAT_TYPE_MULTILINE) {
+ gchar *tmp = purple_strreplace(purple_prefs_get_string(pref_name), "\n", "<br>");
+ pidgin_webview_append_html(PIDGIN_WEBVIEW(webview), tmp);
+ g_free(tmp);
+ } else
+ pidgin_webview_append_html(PIDGIN_WEBVIEW(webview), purple_prefs_get_string(pref_name));
+ gtk_label_set_mnemonic_widget(GTK_LABEL(gtk_label), webview);
gtk_widget_show_all(frame);
- g_object_set_data(G_OBJECT(imhtml), "pref-key", (gpointer)pref_name);
- g_signal_connect(G_OBJECT(gtk_text_view_get_buffer(GTK_TEXT_VIEW(imhtml))),
- "changed", G_CALLBACK(imhtml_cb), imhtml);
- g_signal_connect(G_OBJECT(imhtml),
- "format_function_toggle", G_CALLBACK(imhtml_format_cb), imhtml);
+ g_object_set_data(G_OBJECT(webview), "pref-key", (gpointer)pref_name);
+ g_signal_connect(G_OBJECT(webview), "changed",
+ G_CALLBACK(webview_cb), NULL);
+ g_signal_connect(G_OBJECT(webview), "format-toggled",
+ G_CALLBACK(webview_cb), NULL);
gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0);
}
@@ -170,7 +162,7 @@ make_int_pref(GtkWidget *parent, PurplePluginPref *pref, GtkSizeGroup *sg) {
pref_name = purple_plugin_pref_get_name(pref);
pref_label = purple_plugin_pref_get_label(pref);
- switch(purple_plugin_pref_get_type(pref)) {
+ switch(purple_plugin_pref_get_pref_type(pref)) {
case PURPLE_PLUGIN_PREF_CHOICE:
gtk_label = pidgin_prefs_dropdown_from_list(parent, pref_label,
PURPLE_PREF_INT, pref_name, purple_plugin_pref_get_choices(pref));
@@ -210,8 +202,10 @@ pidgin_plugin_pref_create_frame(PurplePluginPrefFrame *frame) {
sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
- parent = ret = gtk_vbox_new(FALSE, 16);
+ parent = ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, 16);
+#if !GTK_CHECK_VERSION(3,0,0)
gtk_container_set_border_width(GTK_CONTAINER(ret), PIDGIN_HIG_BORDER);
+#endif
gtk_widget_show(ret);
for(pp = purple_plugin_pref_frame_get_prefs(frame);
@@ -227,7 +221,7 @@ pidgin_plugin_pref_create_frame(PurplePluginPrefFrame *frame) {
if(label == NULL)
continue;
- if(purple_plugin_pref_get_type(pref) == PURPLE_PLUGIN_PREF_INFO) {
+ if(purple_plugin_pref_get_pref_type(pref) == PURPLE_PLUGIN_PREF_INFO) {
make_info_pref(parent, pref);
} else {
parent = pidgin_make_frame(ret, label);
@@ -237,7 +231,7 @@ pidgin_plugin_pref_create_frame(PurplePluginPrefFrame *frame) {
continue;
}
- switch(purple_prefs_get_type(name)) {
+ switch(purple_prefs_get_pref_type(name)) {
case PURPLE_PREF_BOOLEAN:
pidgin_prefs_checkbox(label, name, parent);
break;
diff --git a/pidgin/gtkpluginpref.h b/pidgin/gtkpluginpref.h
index d0f9add1c4..fe4b4af062 100644
--- a/pidgin/gtkpluginpref.h
+++ b/pidgin/gtkpluginpref.h
@@ -1,8 +1,3 @@
-/**
- * @file gtkpluginpref.h GTK+ Plugin Preferences
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -24,27 +19,32 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
*/
+
#ifndef _PIDGINPLUGINPREF_H_
#define _PIDGINPLUGINPREF_H_
+/**
+ * SECTION:gtkpluginpref
+ * @section_id: pidgin-gtkpluginpref
+ * @short_description: <filename>gtkpluginpref.h</filename>
+ * @title: Plugin Preferences
+ */
#include "pluginpref.h"
#include "pidgin.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/**
+ * pidgin_plugin_pref_create_frame:
+ * @frame: PurplePluginPrefFrame
+ *
* Creates a Gtk Preference frame for a PurplePluginPrefFrame
*
- * @param frame PurplePluginPrefFrame
- * @return The gtk preference frame
+ * Returns: The gtk preference frame
*/
GtkWidget *pidgin_plugin_pref_create_frame(PurplePluginPrefFrame *frame);
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
#endif /* _PIDGINPLUGINPREF_H_ */
diff --git a/pidgin/gtkpounce.c b/pidgin/gtkpounce.c
index 99a1b5e623..e333851b0c 100644
--- a/pidgin/gtkpounce.c
+++ b/pidgin/gtkpounce.c
@@ -1,8 +1,3 @@
-/**
- * @file gtkpounce.c GTK+ Buddy Pounce API
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -36,15 +31,18 @@
#include "sound.h"
#include "util.h"
+#include "gtk3compat.h"
#include "gtkblist.h"
#include "gtkdialogs.h"
-#include "gtkimhtml.h"
#include "gtkpounce.h"
#include "gtknotify.h"
#include "pidginstock.h"
#include "gtkutils.h"
+#include "gtkwebview.h"
+
+#include <gdk/gdkkeysyms.h>
-/**
+/*
* These are used for the GtkTreeView when you're scrolling through
* all your saved pounces.
*/
@@ -156,9 +154,7 @@ filesel(GtkWidget *widget, gpointer data)
name = gtk_entry_get_text(GTK_ENTRY(entry));
purple_request_file(entry, _("Select a file"), name, FALSE,
- G_CALLBACK(pounce_update_entry_fields), NULL,
- NULL, NULL, NULL,
- entry);
+ G_CALLBACK(pounce_update_entry_fields), NULL, NULL, entry);
g_signal_connect_swapped(G_OBJECT(entry), "destroy",
G_CALLBACK(purple_request_close_with_handle), entry);
}
@@ -263,8 +259,8 @@ save_pounce_cb(GtkWidget *w, PidginPounceDialog *dialog)
if (*name == '\0')
{
- purple_notify_error(NULL, NULL,
- _("Please enter a buddy to pounce."), NULL);
+ purple_notify_error(NULL, NULL, _("Please enter a buddy to "
+ "pounce."), NULL, NULL);
return;
}
@@ -304,7 +300,7 @@ save_pounce_cb(GtkWidget *w, PidginPounceDialog *dialog)
events |= PURPLE_POUNCE_MESSAGE_RECEIVED;
/* Data fields */
- message = gtk_imhtml_get_markup(GTK_IMHTML(dialog->send_msg_entry));
+ message = pidgin_webview_get_body_html(PIDGIN_WEBVIEW(dialog->send_msg_entry));
command = gtk_entry_get_text(GTK_ENTRY(dialog->exec_cmd_entry));
sound = gtk_entry_get_text(GTK_ENTRY(dialog->play_sound_entry));
reason = gtk_entry_get_text(GTK_ENTRY(dialog->popup_entry));
@@ -378,6 +374,19 @@ save_pounce_cb(GtkWidget *w, PidginPounceDialog *dialog)
delete_win_cb(NULL, NULL, dialog);
}
+static gboolean
+entry_key_press_cb(GtkWidget *widget, GdkEventKey *event,
+ PidginPounceDialog *dialog)
+{
+ if ((event->keyval == GDK_KEY_Return)
+ || (event->keyval == GDK_KEY_KP_Enter)) {
+ save_pounce_cb(widget, dialog);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static void
pounce_choose_cb(GtkWidget *item, PurpleAccount *account,
PidginPounceDialog *dialog)
@@ -410,43 +419,45 @@ pounce_dnd_recv(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
GtkSelectionData *sd, guint info, guint t, gpointer data)
{
PidginPounceDialog *dialog;
+ GdkAtom target = gtk_selection_data_get_target(sd);
+ const guchar *sd_data = gtk_selection_data_get_data(sd);
- if (sd->target == gdk_atom_intern("PURPLE_BLIST_NODE", FALSE))
+ if (target == gdk_atom_intern("PURPLE_BLIST_NODE", FALSE))
{
PurpleBlistNode *node = NULL;
PurpleBuddy *buddy;
- memcpy(&node, sd->data, sizeof(node));
+ memcpy(&node, sd_data, sizeof(node));
- if (PURPLE_BLIST_NODE_IS_CONTACT(node))
+ if (PURPLE_IS_CONTACT(node))
buddy = purple_contact_get_priority_buddy((PurpleContact *)node);
- else if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+ else if (PURPLE_IS_BUDDY(node))
buddy = (PurpleBuddy *)node;
else
return;
dialog = (PidginPounceDialog *)data;
- gtk_entry_set_text(GTK_ENTRY(dialog->buddy_entry), buddy->name);
- dialog->account = buddy->account;
- pidgin_account_option_menu_set_selected(dialog->account_menu, buddy->account);
+ 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));
- gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t);
+ gtk_drag_finish(dc, TRUE, (gdk_drag_context_get_actions(dc) == GDK_ACTION_MOVE), t);
}
- else if (sd->target == gdk_atom_intern("application/x-im-contact", FALSE))
+ else if (target == gdk_atom_intern("application/x-im-contact", FALSE))
{
char *protocol = NULL;
char *username = NULL;
PurpleAccount *account;
- if (pidgin_parse_x_im_contact((const char *)sd->data, FALSE, &account,
+ if (pidgin_parse_x_im_contact((const char *)sd_data, FALSE, &account,
&protocol, &username, NULL))
{
if (account == NULL)
{
purple_notify_error(NULL, NULL,
_("You are not currently signed on with an account that "
- "can add that buddy."), NULL);
+ "can add that buddy."), NULL, NULL);
}
else
{
@@ -461,7 +472,7 @@ pounce_dnd_recv(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
g_free(username);
g_free(protocol);
- gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t);
+ gtk_drag_finish(dc, TRUE, (gdk_drag_context_get_actions(dc) == GDK_ACTION_MOVE), t);
}
}
@@ -475,8 +486,8 @@ static void
reset_send_msg_entry(PidginPounceDialog *dialog, GtkWidget *dontcare)
{
PurpleAccount *account = pidgin_account_option_menu_get_selected(dialog->account_menu);
- gtk_imhtml_setup_entry(GTK_IMHTML(dialog->send_msg_entry),
- (account && account->gc) ? account->gc->flags : PURPLE_CONNECTION_HTML);
+ pidgin_webview_setup_entry(PIDGIN_WEBVIEW(dialog->send_msg_entry),
+ (account && purple_account_get_connection(account)) ? purple_connection_get_flags(purple_account_get_connection(account)) : PURPLE_CONNECTION_FLAG_HTML);
}
void
@@ -494,7 +505,7 @@ pidgin_pounce_editor_show(PurpleAccount *account, const char *name,
GtkSizeGroup *sg;
GPtrArray *sound_widgets;
GPtrArray *exec_widgets;
- GtkWidget *send_msg_imhtml;
+ GtkWidget *send_msg_webview;
g_return_if_fail((cur_pounce != NULL) ||
(account != NULL) ||
@@ -534,23 +545,25 @@ pidgin_pounce_editor_show(PurpleAccount *account, const char *name,
dialog->window = window = gtk_dialog_new();
gtk_window_set_title(GTK_WINDOW(window), (cur_pounce == NULL ? _("Add Buddy Pounce") : _("Modify Buddy Pounce")));
gtk_window_set_role(GTK_WINDOW(window), "buddy_pounce");
+#if !GTK_CHECK_VERSION(3,0,0)
gtk_container_set_border_width(GTK_CONTAINER(dialog->window), PIDGIN_HIG_BORDER);
+#endif
g_signal_connect(G_OBJECT(window), "delete_event",
G_CALLBACK(delete_win_cb), dialog);
- /* Create the parent vbox for everything. */
- vbox1 = GTK_DIALOG(window)->vbox;
+ /* Get the parent vbox for everything. */
+ vbox1 = gtk_dialog_get_content_area(GTK_DIALOG(window));
/* Create the vbox that will contain all the prefs stuff. */
- vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_start(GTK_BOX(vbox1), vbox2, TRUE, TRUE, 0);
/* Create the "Pounce on Whom" frame. */
frame = pidgin_make_frame(vbox2, _("Pounce on Whom"));
/* Account: */
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_start(GTK_BOX(frame), hbox, FALSE, FALSE, 0);
gtk_widget_show(hbox);
@@ -567,10 +580,10 @@ pidgin_pounce_editor_show(PurpleAccount *account, const char *name,
gtk_box_pack_start(GTK_BOX(hbox), dialog->account_menu, FALSE, FALSE, 0);
gtk_widget_show(dialog->account_menu);
- pidgin_set_accessible_label (dialog->account_menu, label);
+ pidgin_set_accessible_label(dialog->account_menu, GTK_LABEL(label));
/* Buddy: */
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_start(GTK_BOX(frame), hbox, FALSE, FALSE, 0);
gtk_widget_show(hbox);
@@ -582,14 +595,14 @@ pidgin_pounce_editor_show(PurpleAccount *account, const char *name,
dialog->buddy_entry = gtk_entry_new();
- pidgin_setup_screenname_autocomplete_with_filter(dialog->buddy_entry, dialog->account_menu, pidgin_screenname_autocomplete_default_filter, GINT_TO_POINTER(FALSE));
+ pidgin_setup_screenname_autocomplete(dialog->buddy_entry, dialog->account_menu, pidgin_screenname_autocomplete_default_filter, GINT_TO_POINTER(FALSE));
gtk_box_pack_start(GTK_BOX(hbox), dialog->buddy_entry, TRUE, TRUE, 0);
gtk_widget_show(dialog->buddy_entry);
g_signal_connect(G_OBJECT(dialog->buddy_entry), "changed",
G_CALLBACK(buddy_changed_cb), dialog);
- pidgin_set_accessible_label (dialog->buddy_entry, label);
+ pidgin_set_accessible_label(dialog->buddy_entry, GTK_LABEL(label));
if (cur_pounce != NULL) {
gtk_entry_set_text(GTK_ENTRY(dialog->buddy_entry),
@@ -679,7 +692,7 @@ pidgin_pounce_editor_show(PurpleAccount *account, const char *name,
dialog->play_sound
= gtk_check_button_new_with_mnemonic(_("P_lay a sound"));
- send_msg_imhtml = pidgin_create_imhtml(TRUE, &dialog->send_msg_entry, NULL, NULL);
+ send_msg_webview = pidgin_create_webview(TRUE, &dialog->send_msg_entry, NULL);
reset_send_msg_entry(dialog, NULL);
dialog->exec_cmd_entry = gtk_entry_new();
dialog->popup_entry = gtk_entry_new();
@@ -691,7 +704,7 @@ pidgin_pounce_editor_show(PurpleAccount *account, const char *name,
dialog->play_sound_test = gtk_button_new_with_mnemonic(_("Pre_view"));
dialog->play_sound_reset = gtk_button_new_with_mnemonic(_("Reset"));
- gtk_widget_set_sensitive(send_msg_imhtml, FALSE);
+ gtk_widget_set_sensitive(send_msg_webview, FALSE);
gtk_widget_set_sensitive(dialog->exec_cmd_entry, FALSE);
gtk_widget_set_sensitive(dialog->popup_entry, FALSE);
gtk_widget_set_sensitive(dialog->exec_cmd_browse, FALSE);
@@ -726,7 +739,7 @@ pidgin_pounce_editor_show(PurpleAccount *account, const char *name,
GTK_FILL, 0, 0, 0);
gtk_table_attach(GTK_TABLE(table), dialog->send_msg, 0, 5, 2, 3,
GTK_FILL, 0, 0, 0);
- gtk_table_attach(GTK_TABLE(table), send_msg_imhtml, 0, 5, 3, 4,
+ gtk_table_attach(GTK_TABLE(table), send_msg_webview, 0, 5, 3, 4,
GTK_FILL, 0, 0, 0);
gtk_table_attach(GTK_TABLE(table), dialog->exec_cmd, 0, 1, 4, 5,
GTK_FILL, 0, 0, 0);
@@ -751,7 +764,7 @@ pidgin_pounce_editor_show(PurpleAccount *account, const char *name,
gtk_widget_show(dialog->popup);
gtk_widget_show(dialog->popup_entry);
gtk_widget_show(dialog->send_msg);
- gtk_widget_show(send_msg_imhtml);
+ gtk_widget_show(send_msg_webview);
gtk_widget_show(dialog->exec_cmd);
gtk_widget_show(dialog->exec_cmd_entry);
gtk_widget_show(dialog->exec_cmd_browse);
@@ -767,7 +780,7 @@ pidgin_pounce_editor_show(PurpleAccount *account, const char *name,
g_signal_connect(G_OBJECT(dialog->send_msg), "clicked",
G_CALLBACK(pidgin_toggle_sensitive),
- send_msg_imhtml);
+ send_msg_webview);
g_signal_connect(G_OBJECT(dialog->popup), "clicked",
G_CALLBACK(pidgin_toggle_sensitive),
@@ -807,13 +820,13 @@ pidgin_pounce_editor_show(PurpleAccount *account, const char *name,
g_object_set_data_full(G_OBJECT(dialog->window), "sound-widgets",
sound_widgets, (GDestroyNotify)g_ptr_array_free);
- g_signal_connect_swapped(G_OBJECT(dialog->send_msg_entry), "format_function_clear",
+ g_signal_connect_swapped(G_OBJECT(dialog->send_msg_entry), "format-cleared",
G_CALLBACK(reset_send_msg_entry), dialog);
g_signal_connect_swapped(G_OBJECT(dialog->account_menu), "changed",
G_CALLBACK(reset_send_msg_entry), dialog);
- g_signal_connect(G_OBJECT(dialog->send_msg_entry), "message_send",
- G_CALLBACK(save_pounce_cb), dialog);
+ g_signal_connect(G_OBJECT(dialog->send_msg_entry), "key-press-event",
+ G_CALLBACK(entry_key_press_cb), dialog);
g_signal_connect(G_OBJECT(dialog->popup_entry), "activate",
G_CALLBACK(save_pounce_cb), dialog);
g_signal_connect(G_OBJECT(dialog->exec_cmd_entry), "activate",
@@ -926,7 +939,7 @@ pidgin_pounce_editor_show(PurpleAccount *account, const char *name,
"send-message",
"message")) != NULL)
{
- gtk_imhtml_append_text(GTK_IMHTML(dialog->send_msg_entry), value, 0);
+ pidgin_webview_append_html(PIDGIN_WEBVIEW(dialog->send_msg_entry), value);
}
if ((value = purple_pounce_action_get_attribute(cur_pounce,
@@ -955,7 +968,7 @@ pidgin_pounce_editor_show(PurpleAccount *account, const char *name,
PurpleBuddy *buddy = NULL;
if (name != NULL)
- buddy = purple_find_buddy(account, name);
+ buddy = purple_blist_find_buddy(account, name);
/* Set some defaults */
if (buddy == NULL)
@@ -1018,7 +1031,7 @@ pidgin_pounce_editor_show(PurpleAccount *account, const char *name,
static gboolean
pounces_manager_configure_cb(GtkWidget *widget, GdkEventConfigure *event, PouncesManager *dialog)
{
- if (GTK_WIDGET_VISIBLE(widget)) {
+ if (gtk_widget_get_visible(widget)) {
purple_prefs_set_int(PIDGIN_PREFS_ROOT "/pounces/dialog/width", event->width);
purple_prefs_set_int(PIDGIN_PREFS_ROOT "/pounces/dialog/height", event->height);
}
@@ -1121,10 +1134,9 @@ pounces_manager_delete_foreach(GtkTreeModel *model, GtkTreePath *path,
buf = g_strdup_printf(_("Are you sure you want to delete the pounce on %s for %s?"), pouncee, pouncer);
purple_request_action(pounce, NULL, buf, NULL, 0,
- account, pouncee, NULL,
- pounce, 2,
- _("Delete"), pounces_manager_delete_confirm_cb,
- _("Cancel"), NULL);
+ purple_request_cpar_from_account(account),
+ pounce, 2, _("Delete"), pounces_manager_delete_confirm_cb,
+ _("Cancel"), NULL);
g_free(buf);
}
@@ -1331,7 +1343,11 @@ pidgin_pounces_manager_show(void)
width = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/pounces/dialog/width");
height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/pounces/dialog/height");
+#if GTK_CHECK_VERSION(3,0,0)
+ dialog->window = win = pidgin_create_dialog(_("Buddy Pounces"), 0, "pounces", TRUE);
+#else
dialog->window = win = pidgin_create_dialog(_("Buddy Pounces"), PIDGIN_HIG_BORDER, "pounces", TRUE);
+#endif
gtk_window_set_default_size(GTK_WINDOW(win), width, height);
g_signal_connect(G_OBJECT(win), "delete_event",
@@ -1389,7 +1405,7 @@ pidgin_pounces_manager_hide(void)
static void
pounce_cb(PurplePounce *pounce, PurplePounceEvent events, void *data)
{
- PurpleConversation *conv;
+ PurpleIMConversation *im;
PurpleAccount *account;
PurpleBuddy *buddy;
const char *pouncee;
@@ -1398,7 +1414,7 @@ pounce_cb(PurplePounce *pounce, PurplePounceEvent events, void *data)
pouncee = purple_pounce_get_pouncee(pounce);
account = purple_pounce_get_pouncer(pounce);
- buddy = purple_find_buddy(account, pouncee);
+ buddy = purple_blist_find_buddy(account, pouncee);
if (buddy != NULL)
{
alias = purple_buddy_get_alias(buddy);
@@ -1410,8 +1426,8 @@ pounce_cb(PurplePounce *pounce, PurplePounceEvent events, void *data)
if (purple_pounce_action_is_enabled(pounce, "open-window"))
{
- if (!purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, pouncee, account))
- purple_conversation_new(PURPLE_CONV_TYPE_IM, account, pouncee);
+ if (!purple_conversations_find_im_with_account(pouncee, account))
+ purple_im_conversation_new(account, pouncee);
}
if (purple_pounce_action_is_enabled(pounce, "popup-notify"))
@@ -1464,15 +1480,16 @@ pounce_cb(PurplePounce *pounce, PurplePounceEvent events, void *data)
if (message != NULL)
{
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, pouncee, account);
+ PurpleMessage *pmsg;
- if (conv == NULL)
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, pouncee);
+ im = purple_conversations_find_im_with_account(pouncee, account);
- purple_conversation_write(conv, NULL, message,
- PURPLE_MESSAGE_SEND, time(NULL));
+ if (im == NULL)
+ im = purple_im_conversation_new(account, pouncee);
- serv_send_im(account->gc, (char *)pouncee, (char *)message, 0);
+ pmsg = purple_message_new_outgoing(pouncee, message, 0);
+ purple_serv_send_im(purple_account_get_connection(account), pmsg);
+ purple_conversation_write_message(PURPLE_CONVERSATION(im), pmsg);
}
}
@@ -1485,31 +1502,13 @@ pounce_cb(PurplePounce *pounce, PurplePounceEvent events, void *data)
if (command != NULL)
{
-#ifndef _WIN32
- char *localecmd = g_locale_from_utf8(command, -1, NULL,
- NULL, NULL);
-
- if (localecmd != NULL)
- {
- int pid = fork();
-
- if (pid == 0) {
- char *args[4];
-
- args[0] = "sh";
- args[1] = "-c";
- args[2] = (char *)localecmd;
- args[3] = NULL;
-
- execvp(args[0], args);
-
- _exit(0);
- }
- g_free(localecmd);
+ GError *error = NULL;
+ if (!g_spawn_command_line_async(command, &error)) {
+ purple_debug_error("gtkpounce",
+ "pounce command could not be launched: %s\n",
+ error->message);
+ g_error_free(error);
}
-#else /* !_WIN32 */
- winpidgin_shell_execute(command, "open", NULL);
-#endif /* !_WIN32 */
}
}
diff --git a/pidgin/gtkpounce.h b/pidgin/gtkpounce.h
index ce5bbc6505..2061e8507d 100644
--- a/pidgin/gtkpounce.h
+++ b/pidgin/gtkpounce.h
@@ -1,8 +1,3 @@
-/**
- * @file gtkpounce.h GTK+ Buddy Pounce API
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -23,41 +18,61 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _PIDGINPOUNCE_H_
#define _PIDGINPOUNCE_H_
+/**
+ * SECTION:gtkpounce
+ * @section_id: pidgin-gtkpounce
+ * @short_description: <filename>gtkpounce.h</filename>
+ * @title: Buddy Pounce API
+ */
#include "pounce.h"
+G_BEGIN_DECLS
+
/**
- * Displays a New Buddy Pounce or Edit Buddy Pounce dialog.
+ * pidgin_pounce_editor_show:
+ * @account: The optional account to use.
+ * @name: The optional name to pounce on.
+ * @cur_pounce: The current buddy pounce, if editing an existing one.
*
- * @param account The optional account to use.
- * @param name The optional name to pounce on.
- * @param cur_pounce The current buddy pounce, if editing an existing one.
+ * Displays a New Buddy Pounce or Edit Buddy Pounce dialog.
*/
void pidgin_pounce_editor_show(PurpleAccount *account, const char *name,
PurplePounce *cur_pounce);
/**
+ * pidgin_pounces_manager_show:
+ *
* Shows the pounces manager window.
*/
void pidgin_pounces_manager_show(void);
/**
+ * pidgin_pounces_manager_hide:
+ *
* Hides the pounces manager window.
*/
void pidgin_pounces_manager_hide(void);
/**
+ * pidgin_pounces_get_handle:
+ *
* Returns the gtkpounces handle
*
- * @return The handle to the GTK+ pounces system
+ * Returns: The handle to the GTK+ pounces system
*/
void *pidgin_pounces_get_handle(void);
/**
+ * pidgin_pounces_init:
+ *
* Initializes the GTK+ pounces subsystem.
*/
void pidgin_pounces_init(void);
+G_END_DECLS
+
#endif /* _PIDGINPOUNCE_H_ */
diff --git a/pidgin/gtkprefs.c b/pidgin/gtkprefs.c
index a42cf93219..1edfb6fc38 100644
--- a/pidgin/gtkprefs.c
+++ b/pidgin/gtkprefs.c
@@ -1,8 +1,3 @@
-/**
- * @file gtkprefs.c GTK+ Preferences
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -25,9 +20,11 @@
*
*/
#include "internal.h"
+#include "glibcompat.h"
#include "pidgin.h"
#include "debug.h"
+#include "http.h"
#include "nat-pmp.h"
#include "notify.h"
#include "prefs.h"
@@ -42,20 +39,41 @@
#include "upnp.h"
#include "util.h"
#include "network.h"
+#include "keyring.h"
#include "gtkblist.h"
#include "gtkconv.h"
+#include "gtkconv-theme.h"
#include "gtkdebug.h"
#include "gtkdialogs.h"
-#include "gtkimhtml.h"
-#include "gtkimhtmltoolbar.h"
#include "gtkprefs.h"
#include "gtksavedstatuses.h"
+#include "gtksmiley-theme.h"
#include "gtksound.h"
#include "gtkstatus-icon-theme.h"
-#include "gtkthemes.h"
#include "gtkutils.h"
+#include "gtkwebview.h"
#include "pidginstock.h"
+#ifdef USE_VV
+#include "media-gst.h"
+#if GST_CHECK_VERSION(1,0,0)
+#include <gst/video/videooverlay.h>
+#else
+#include <gst/interfaces/xoverlay.h>
+#include <gst/interfaces/propertyprobe.h>
+#endif
+#ifdef GDK_WINDOWING_WIN32
+#include <gdk/gdkwin32.h>
+#endif
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+#ifdef GDK_WINDOWING_QUARTZ
+#include <gdk/gdkquartz.h>
+#endif
+#endif
+
+#include "gtk3compat.h"
#define PROXYHOST 0
#define PROXYPORT 1
@@ -64,6 +82,9 @@
#define PREFS_OPTIMAL_ICON_SIZE 32
+/* 25MB */
+#define PREFS_MAX_DOWNLOADED_THEME_SIZE 26214400
+
struct theme_info {
gchar *type;
gchar *extension;
@@ -78,13 +99,24 @@ static GtkWidget *prefsnotebook = NULL;
static int notebook_page = 0;
/* Conversations page */
-static GtkWidget *sample_imhtml = NULL;
+static GtkWidget *sample_webview = NULL;
/* 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;
/* Sound theme specific */
static GtkWidget *sound_entry = NULL;
@@ -94,9 +126,69 @@ 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 const gchar *AUDIO_SRC_PLUGINS[] = {
+ "alsasrc", "ALSA",
+ "directsoundsrc", "DirectSound",
+ /* "esdmon", "ESD", ? */
+ "osssrc", "OSS",
+ "pulsesrc", "PulseAudio",
+ "sndiosrc", "sndio",
+ /* "audiotestsrc wave=silence", "Silence", */
+ "audiotestsrc", N_("Test Sound"),
+ NULL
+};
+
+static const gchar *AUDIO_SINK_PLUGINS[] = {
+ "alsasink", "ALSA",
+ "directsoundsink", "DirectSound",
+ /* "gconfaudiosink", "GConf", */
+ "artsdsink", "aRts",
+ "esdsink", "ESD",
+ "osssink", "OSS",
+ "pulsesink", "PulseAudio",
+ "sndiosink", "sndio",
+ NULL
+};
+
+static const gchar *VIDEO_SRC_PLUGINS[] = {
+ "videodisabledsrc", N_("Disabled"),
+ "videotestsrc", N_("Test Input"),
+ "dshowvideosrc","DirectDraw",
+ "ksvideosrc", "KS Video",
+ "qcamsrc", "Quickcam",
+ "v4lsrc", "Video4Linux",
+ "v4l2src", "Video4Linux2",
+ "v4lmjpegsrc", "Video4Linux MJPEG",
+ NULL
+};
+
+static const gchar *VIDEO_SINK_PLUGINS[] = {
+ /* "aasink", "AALib", Didn't work for me */
+ "directdrawsink", "DirectDraw",
+ /* "gconfvideosink", "GConf", */
+ "glimagesink", "OpenGL",
+ "ximagesink", "X Window System",
+ "xvimagesink", "X Window System (Xv)",
+ NULL
+};
+
+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
*/
@@ -118,13 +210,13 @@ pidgin_prefs_labeled_spin_button(GtkWidget *box, const gchar *title,
const char *key, int min, int max, GtkSizeGroup *sg)
{
GtkWidget *spin;
- GtkObject *adjust;
+ GtkAdjustment *adjust;
int val;
val = purple_prefs_get_int(key);
- adjust = gtk_adjustment_new(val, min, max, 1, 1, 0);
- spin = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0);
+ adjust = GTK_ADJUSTMENT(gtk_adjustment_new(val, min, max, 1, 1, 0));
+ spin = gtk_spin_button_new(adjust, 1, 0);
g_object_set_data(G_OBJECT(spin), "val", (char *)key);
if (max < 10000)
gtk_widget_set_size_request(spin, 50, -1);
@@ -182,105 +274,218 @@ pidgin_prefs_labeled_password(GtkWidget *page, const gchar *title,
return pidgin_add_widget_to_vbox(GTK_BOX(page), title, sg, entry, TRUE, NULL);
}
+/* TODO: Maybe move this up somewheres... */
+enum {
+ PREF_DROPDOWN_TEXT,
+ PREF_DROPDOWN_VALUE,
+ PREF_DROPDOWN_COUNT
+};
-static void
-dropdown_set(GObject *w, const char *key)
+typedef struct
{
- const char *str_value;
- int int_value;
PurplePrefType type;
+ union {
+ const char *string;
+ int integer;
+ gboolean boolean;
+ } value;
+} PidginPrefValue;
- type = GPOINTER_TO_INT(g_object_get_data(w, "type"));
+typedef void (*PidginPrefsDropdownCallback)(GtkComboBox *combo_box,
+ PidginPrefValue value);
- if (type == PURPLE_PREF_INT) {
- int_value = GPOINTER_TO_INT(g_object_get_data(w, "value"));
+static void
+dropdown_set(GtkComboBox *combo_box, gpointer _cb)
+{
+ PidginPrefsDropdownCallback cb = _cb;
+ GtkTreeIter iter;
+ GtkTreeModel *tree_model;
+ PidginPrefValue active;
- purple_prefs_set_int(key, int_value);
- }
- else if (type == PURPLE_PREF_STRING) {
- str_value = (const char *)g_object_get_data(w, "value");
+ tree_model = gtk_combo_box_get_model(combo_box);
+ if (!gtk_combo_box_get_active_iter(combo_box, &iter))
+ return;
+ active.type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(combo_box),
+ "type"));
+
+ g_object_set_data(G_OBJECT(combo_box), "previously_active",
+ g_object_get_data(G_OBJECT(combo_box), "current_active"));
+ g_object_set_data(G_OBJECT(combo_box), "current_active",
+ GINT_TO_POINTER(gtk_combo_box_get_active(combo_box)));
- purple_prefs_set_string(key, str_value);
+ if (active.type == PURPLE_PREF_INT) {
+ gtk_tree_model_get(tree_model, &iter, PREF_DROPDOWN_VALUE,
+ &active.value.integer, -1);
}
- else if (type == PURPLE_PREF_BOOLEAN) {
- purple_prefs_set_bool(key,
- GPOINTER_TO_INT(g_object_get_data(w, "value")));
+ else if (active.type == PURPLE_PREF_STRING) {
+ gtk_tree_model_get(tree_model, &iter, PREF_DROPDOWN_VALUE,
+ &active.value.string, -1);
}
+ else if (active.type == PURPLE_PREF_BOOLEAN) {
+ gtk_tree_model_get(tree_model, &iter, PREF_DROPDOWN_VALUE,
+ &active.value.boolean, -1);
+ }
+
+ cb(combo_box, active);
}
-GtkWidget *
-pidgin_prefs_dropdown_from_list(GtkWidget *box, const gchar *title,
- PurplePrefType type, const char *key, GList *menuitems)
+static void pidgin_prefs_dropdown_revert_active(GtkComboBox *combo_box)
+{
+ 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);
+}
+
+static GtkWidget *
+pidgin_prefs_dropdown_from_list_with_cb(GtkWidget *box, const gchar *title,
+ GtkComboBox **dropdown_out, GList *menuitems,
+ PidginPrefValue initial, PidginPrefsDropdownCallback cb)
{
- GtkWidget *dropdown, *opt, *menu;
- GtkWidget *label = NULL;
- gchar *text;
- const char *stored_str = NULL;
- int stored_int = 0;
- int int_value = 0;
- const char *str_value = NULL;
- int o = 0;
+ GtkWidget *dropdown;
+ GtkWidget *label = NULL;
+ gchar *text;
+ GtkListStore *store = NULL;
+ GtkTreeIter iter;
+ GtkTreeIter active;
+ GtkCellRenderer *renderer;
+ gpointer current_active;
g_return_val_if_fail(menuitems != NULL, NULL);
- dropdown = gtk_option_menu_new();
- menu = gtk_menu_new();
+ if (initial.type == PURPLE_PREF_INT) {
+ store = gtk_list_store_new(PREF_DROPDOWN_COUNT, G_TYPE_STRING, G_TYPE_INT);
+ } else if (initial.type == PURPLE_PREF_STRING) {
+ store = gtk_list_store_new(PREF_DROPDOWN_COUNT, G_TYPE_STRING, G_TYPE_STRING);
+ } else if (initial.type == PURPLE_PREF_BOOLEAN) {
+ store = gtk_list_store_new(PREF_DROPDOWN_COUNT, G_TYPE_STRING, G_TYPE_BOOLEAN);
+ } else {
+ g_warn_if_reached();
+ return NULL;
+ }
+
+ dropdown = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
+ if (dropdown_out != NULL)
+ *dropdown_out = GTK_COMBO_BOX(dropdown);
+ g_object_set_data(G_OBJECT(dropdown), "type", GINT_TO_POINTER(initial.type));
- if (type == PURPLE_PREF_INT)
- stored_int = purple_prefs_get_int(key);
- else if (type == PURPLE_PREF_STRING)
- stored_str = purple_prefs_get_string(key);
+ while (menuitems != NULL && (text = (char *)menuitems->data) != NULL) {
+ int int_value = 0;
+ const char *str_value = NULL;
+ gboolean bool_value = FALSE;
- while (menuitems != NULL && (text = (char *) menuitems->data) != NULL) {
menuitems = g_list_next(menuitems);
g_return_val_if_fail(menuitems != NULL, NULL);
- opt = gtk_menu_item_new_with_label(text);
-
- g_object_set_data(G_OBJECT(opt), "type", GINT_TO_POINTER(type));
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ PREF_DROPDOWN_TEXT, text,
+ -1);
- if (type == PURPLE_PREF_INT) {
+ if (initial.type == PURPLE_PREF_INT) {
int_value = GPOINTER_TO_INT(menuitems->data);
- g_object_set_data(G_OBJECT(opt), "value",
- GINT_TO_POINTER(int_value));
+ gtk_list_store_set(store, &iter,
+ PREF_DROPDOWN_VALUE, int_value,
+ -1);
}
- else if (type == PURPLE_PREF_STRING) {
+ else if (initial.type == PURPLE_PREF_STRING) {
str_value = (const char *)menuitems->data;
-
- g_object_set_data(G_OBJECT(opt), "value", (char *)str_value);
+ gtk_list_store_set(store, &iter,
+ PREF_DROPDOWN_VALUE, str_value,
+ -1);
}
- else if (type == PURPLE_PREF_BOOLEAN) {
- g_object_set_data(G_OBJECT(opt), "value",
- menuitems->data);
+ else if (initial.type == PURPLE_PREF_BOOLEAN) {
+ bool_value = (gboolean)GPOINTER_TO_INT(menuitems->data);
+ gtk_list_store_set(store, &iter,
+ PREF_DROPDOWN_VALUE, bool_value,
+ -1);
}
- g_signal_connect(G_OBJECT(opt), "activate",
- G_CALLBACK(dropdown_set), (char *)key);
-
- gtk_widget_show(opt);
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), opt);
+ if ((initial.type == PURPLE_PREF_INT &&
+ initial.value.integer == int_value) ||
+ (initial.type == PURPLE_PREF_STRING &&
+ !g_strcmp0(initial.value.string, str_value)) ||
+ (initial.type == PURPLE_PREF_BOOLEAN &&
+ (initial.value.boolean == bool_value))) {
- if ((type == PURPLE_PREF_INT && stored_int == int_value) ||
- (type == PURPLE_PREF_STRING && stored_str != NULL &&
- !strcmp(stored_str, str_value)) ||
- (type == PURPLE_PREF_BOOLEAN &&
- (purple_prefs_get_bool(key) == GPOINTER_TO_INT(menuitems->data)))) {
-
- gtk_menu_set_active(GTK_MENU(menu), o);
+ active = iter;
}
menuitems = g_list_next(menuitems);
-
- o++;
}
- gtk_option_menu_set_menu(GTK_OPTION_MENU(dropdown), menu);
+ renderer = gtk_cell_renderer_text_new();
+ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(dropdown), renderer, TRUE);
+ gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(dropdown), renderer,
+ "text", 0,
+ NULL);
+
+ gtk_combo_box_set_active_iter(GTK_COMBO_BOX(dropdown), &active);
+ current_active = GINT_TO_POINTER(gtk_combo_box_get_active(GTK_COMBO_BOX(
+ dropdown)));
+ g_object_set_data(G_OBJECT(dropdown), "current_active", current_active);
+ g_object_set_data(G_OBJECT(dropdown), "previously_active", current_active);
+
+ g_signal_connect(G_OBJECT(dropdown), "changed",
+ G_CALLBACK(dropdown_set), cb);
pidgin_add_widget_to_vbox(GTK_BOX(box), title, NULL, dropdown, FALSE, &label);
return label;
}
+static void
+pidgin_prefs_dropdown_from_list_cb(GtkComboBox *combo_box,
+ PidginPrefValue value)
+{
+ const char *key;
+
+ key = g_object_get_data(G_OBJECT(combo_box), "key");
+
+ if (value.type == PURPLE_PREF_INT) {
+ purple_prefs_set_int(key, value.value.integer);
+ } else if (value.type == PURPLE_PREF_STRING) {
+ purple_prefs_set_string(key, value.value.string);
+ } else if (value.type == PURPLE_PREF_BOOLEAN) {
+ purple_prefs_set_bool(key, value.value.boolean);
+ } else {
+ g_return_if_reached();
+ }
+}
+
+GtkWidget *
+pidgin_prefs_dropdown_from_list(GtkWidget *box, const gchar *title,
+ PurplePrefType type, const char *key, GList *menuitems)
+{
+ PidginPrefValue initial;
+ GtkComboBox *dropdown = NULL;
+ GtkWidget *label;
+
+ initial.type = type;
+ if (type == PURPLE_PREF_INT) {
+ initial.value.integer = purple_prefs_get_int(key);
+ } else if (type == PURPLE_PREF_STRING) {
+ initial.value.string = purple_prefs_get_string(key);
+ } else if (type == PURPLE_PREF_BOOLEAN) {
+ initial.value.boolean = purple_prefs_get_bool(key);
+ } else {
+ g_return_val_if_reached(NULL);
+ }
+
+ label = pidgin_prefs_dropdown_from_list_with_cb(box, title, &dropdown,
+ menuitems, initial, pidgin_prefs_dropdown_from_list_cb);
+
+ g_object_set_data(G_OBJECT(dropdown), "key", (gpointer)key);
+
+ return label;
+}
+
GtkWidget *
pidgin_prefs_dropdown(GtkWidget *box, const gchar *title, PurplePrefType type,
const char *key, ...)
@@ -323,12 +528,20 @@ pidgin_prefs_dropdown(GtkWidget *box, const gchar *title, PurplePrefType type,
return dropdown;
}
+static void keyring_page_cleanup(void);
+
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);
+ purple_notify_close_with_handle(prefs);
+
/* Unregister callbacks. */
purple_prefs_disconnect_by_handle(prefs);
@@ -339,10 +552,21 @@ delete_prefs(GtkWidget *asdf, void *gdsa)
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;
- sample_imhtml = NULL;
+ keyring_page_cleanup();
+
+ sample_webview = NULL;
+
+#ifdef USE_VV
+ voice_level = NULL;
+ voice_threshold = NULL;
+ voice_volume = NULL;
+ video_drawing_area = NULL;
+#endif
notebook_page = 0;
prefsnotebook = NULL;
@@ -363,38 +587,33 @@ get_theme_markup(const char *name, gboolean custom, const char *author,
static void
smileys_refresh_theme_list(void)
{
- GdkPixbuf *pixbuf;
- GSList *themes;
+ GList *it;
GtkTreeIter iter;
+ gchar *description;
- pidgin_themes_smiley_theme_probe();
-
- if (!(themes = smiley_themes))
- return;
+ description = get_theme_markup(_("none"), FALSE, _("Penguin Pimps"),
+ _("Selecting this disables graphical emoticons."));
+ gtk_list_store_append(prefs_smiley_themes, &iter);
+ gtk_list_store_set(prefs_smiley_themes, &iter,
+ 0, NULL, 1, description, 2, "none", -1);
+ g_free(description);
- while (themes) {
- struct smiley_theme *theme = themes->data;
- char *description = get_theme_markup(_(theme->name), FALSE,
- _(theme->author), _(theme->desc));
- gtk_list_store_append(prefs_smiley_themes, &iter);
+ for (it = pidgin_smiley_theme_get_all(); it; it = g_list_next(it)) {
+ PidginSmileyTheme *theme = it->data;
- /*
- * LEAK - Gentoo memprof thinks pixbuf is leaking here... but it
- * looks like it should be ok to me. Anyone know what's up? --Mark
- */
- pixbuf = (theme->icon ? pidgin_pixbuf_new_from_file(theme->icon) : NULL);
+ description = get_theme_markup(
+ _(pidgin_smiley_theme_get_name(theme)), FALSE,
+ _(pidgin_smiley_theme_get_author(theme)),
+ _(pidgin_smiley_theme_get_description(theme)));
+ gtk_list_store_append(prefs_smiley_themes, &iter);
gtk_list_store_set(prefs_smiley_themes, &iter,
- 0, pixbuf,
- 1, description,
- 2, theme->name,
- -1);
-
- if (pixbuf != NULL)
- g_object_unref(G_OBJECT(pixbuf));
+ 0, pidgin_smiley_theme_get_icon(theme),
+ 1, description,
+ 2, pidgin_smiley_theme_get_name(theme),
+ -1);
g_free(description);
- themes = themes->next;
}
}
@@ -490,6 +709,18 @@ prefs_themes_sort(PurpleTheme *theme)
g_free(markup);
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);
}
}
@@ -528,7 +759,8 @@ prefs_themes_refresh(void)
/* refresh the list of themes in the manager */
purple_theme_manager_refresh();
- tmp = g_build_filename(DATADIR, "icons", "hicolor", "32x32", "apps", "pidgin.png", NULL);
+ tmp = g_build_filename(PURPLE_DATADIR, "icons", "hicolor", "32x32",
+ "apps", "pidgin.png", NULL);
pixbuf = pidgin_pixbuf_new_from_file_at_scale(tmp, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE);
g_free(tmp);
@@ -545,6 +777,17 @@ prefs_themes_refresh(void)
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);
@@ -565,6 +808,7 @@ prefs_themes_refresh(void)
/* 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;
@@ -578,11 +822,29 @@ prefs_themes_init(void)
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);
}
+/*
+ * prefs_theme_find_theme:
+ * @path: A directory containing a theme. The theme could be at the
+ * top level of this directory or in any subdirectory thereof.
+ * @type: The type of theme to load. The loader for this theme type
+ * will be used and this loader will determine what constitutes a
+ * "theme."
+ *
+ * Attempt to load the given directory as a theme. If we are unable to
+ * open the path as a theme then we recurse into path and attempt to
+ * load each subdirectory that we encounter.
+ *
+ * Returns: A new reference to a #PurpleTheme.
+ */
static PurpleTheme *
prefs_theme_find_theme(const gchar *path, const gchar *type)
{
@@ -688,7 +950,7 @@ theme_install_theme(char *path, struct theme_info *info)
/* Fire! */
if (system(command)) {
- purple_notify_error(NULL, NULL, _("Theme failed to unpack."), NULL);
+ purple_notify_error(NULL, NULL, _("Theme failed to unpack."), NULL, NULL);
g_free(command);
g_free(destdir);
free_theme_info(info);
@@ -696,7 +958,7 @@ theme_install_theme(char *path, struct theme_info *info)
}
#else
if (!winpidgin_gz_untar(path, destdir)) {
- purple_notify_error(NULL, NULL, _("Theme failed to unpack."), NULL);
+ purple_notify_error(NULL, NULL, _("Theme failed to unpack."), NULL, NULL);
g_free(destdir);
free_theme_info(info);
return;
@@ -726,7 +988,11 @@ theme_install_theme(char *path, struct theme_info *info)
"purple", info->type, NULL);
/* move the entire directory to new location */
- g_rename(purple_theme_get_dir(theme), theme_dest);
+ if (g_rename(purple_theme_get_dir(theme), theme_dest)) {
+ purple_debug_error("gtkprefs", "Error renaming %s to %s: "
+ "%s\n", purple_theme_get_dir(theme), theme_dest,
+ g_strerror(errno));
+ }
g_free(theme_dest);
if (g_remove(destdir) != 0) {
@@ -740,7 +1006,7 @@ theme_install_theme(char *path, struct theme_info *info)
} else {
/* something was wrong with the theme archive */
g_unlink(destdir);
- purple_notify_error(NULL, NULL, _("Theme failed to load."), NULL);
+ purple_notify_error(NULL, NULL, _("Theme failed to load."), NULL, NULL);
}
} else { /* just a single file so copy it to a new temp directory and attempt to load it*/
@@ -773,7 +1039,11 @@ theme_install_theme(char *path, struct theme_info *info)
if(!g_file_test(theme_dest, G_FILE_TEST_IS_DIR))
purple_build_dir(theme_dest, S_IRUSR | S_IWUSR | S_IXUSR);
- g_rename(purple_theme_get_dir(theme), theme_dest);
+ if (g_rename(purple_theme_get_dir(theme), theme_dest)) {
+ purple_debug_error("gtkprefs", "Error renaming %s to %s: "
+ "%s\n", purple_theme_get_dir(theme), theme_dest,
+ g_strerror(errno));
+ }
g_free(theme_dest);
g_object_unref(theme);
@@ -782,12 +1052,12 @@ theme_install_theme(char *path, struct theme_info *info)
} else {
if (g_remove(temp_path) != 0) {
purple_debug_error("gtkprefs",
- "couldn't remove temp path\n");
+ "couldn't remove temp path");
}
- purple_notify_error(NULL, NULL, _("Theme failed to load."), NULL);
+ purple_notify_error(NULL, NULL, _("Theme failed to load."), NULL, NULL);
}
} else {
- purple_notify_error(NULL, NULL, _("Theme failed to copy."), NULL);
+ purple_notify_error(NULL, NULL, _("Theme failed to copy."), NULL, NULL);
}
g_free(temp_file);
@@ -799,18 +1069,26 @@ theme_install_theme(char *path, struct theme_info *info)
}
static void
-theme_got_url(PurpleUtilFetchUrlData *url_data, gpointer user_data,
- const gchar *themedata, size_t len, const gchar *error_message)
+theme_got_url(PurpleHttpConnection *http_conn, PurpleHttpResponse *response,
+ gpointer _info)
{
+ struct theme_info *info = _info;
+ const gchar *themedata;
+ size_t len;
FILE *f;
gchar *path;
size_t wc;
- if ((error_message != NULL) || (len == 0)) {
- free_theme_info(user_data);
+ g_assert(http_conn == prefs_conv_themes_running_request);
+ prefs_conv_themes_running_request = NULL;
+
+ if (!purple_http_response_is_successful(response)) {
+ free_theme_info(info);
return;
}
+ themedata = purple_http_response_get_data(response, &len);
+
f = purple_mkstemp(&path, TRUE);
wc = fwrite(themedata, len, 1, f);
if (wc != 1) {
@@ -818,12 +1096,12 @@ theme_got_url(PurpleUtilFetchUrlData *url_data, gpointer user_data,
fclose(f);
g_unlink(path);
g_free(path);
- free_theme_info(user_data);
+ free_theme_info(info);
return;
}
fclose(f);
- theme_install_theme(path, user_data);
+ theme_install_theme(path, info);
g_unlink(path);
g_free(path);
@@ -833,9 +1111,10 @@ static void
theme_dnd_recv(GtkWidget *widget, GdkDragContext *dc, guint x, guint y,
GtkSelectionData *sd, guint info, guint t, gpointer user_data)
{
- gchar *name = g_strchomp((gchar *)sd->data);
+ gchar *name = g_strchomp((gchar *)gtk_selection_data_get_data(sd));
- if ((sd->length >= 0) && (sd->format == 8)) {
+ if ((gtk_selection_data_get_length(sd) >= 0)
+ && (gtk_selection_data_get_format(sd) == 8)) {
/* Well, it looks like the drag event was cool.
* Let's do something with it */
gchar *temp;
@@ -859,22 +1138,19 @@ theme_dnd_recv(GtkWidget *widget, GdkDragContext *dc, guint x, guint y,
}
theme_install_theme(tmp, info);
g_free(tmp);
- } else if (!g_ascii_strncasecmp(name, "http://", 7)) {
+ } else if (!g_ascii_strncasecmp(name, "http://", 7) ||
+ !g_ascii_strncasecmp(name, "https://", 8)) {
/* Oo, a web drag and drop. This is where things
* will start to get interesting */
- purple_util_fetch_url(name, TRUE, NULL, FALSE, theme_got_url, info);
- } else if (!g_ascii_strncasecmp(name, "https://", 8)) {
- /* purple_util_fetch_url() doesn't support HTTPS, but we want users
- * to be able to drag and drop links from the SF trackers, so
- * we'll try it as an HTTP URL. */
- char *tmp = g_strdup(name + 1);
- tmp[0] = 'h';
- tmp[1] = 't';
- tmp[2] = 't';
- tmp[3] = 'p';
-
- purple_util_fetch_url(tmp, TRUE, NULL, FALSE, theme_got_url, info);
- g_free(tmp);
+ PurpleHttpRequest *hr;
+ purple_http_conn_cancel(prefs_conv_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(
+ NULL, hr, theme_got_url, info);
+ purple_http_request_unref(hr);
} else
free_theme_info(info);
@@ -962,7 +1238,11 @@ prefs_set_smiley_theme_cb(GtkComboBox *combo_box, gpointer user_data)
gtk_tree_model_get(GTK_TREE_MODEL(prefs_smiley_themes), &new_iter, 2, &new_theme, -1);
purple_prefs_set_string(PIDGIN_PREFS_ROOT "/smileys/theme", new_theme);
- pidgin_themes_smiley_themeize(sample_imhtml);
+
+#if 0
+ /* TODO: update smileys in sample_webview input box. */
+ update_smileys_in_webview_input_box(sample_webview);
+#endif
g_free(new_theme);
}
@@ -1025,6 +1305,80 @@ prefs_set_blist_theme_cb(GtkComboBox *combo_box, gpointer user_data)
}
}
+/* 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)
@@ -1043,7 +1397,7 @@ prefs_set_status_icon_theme_cb(GtkComboBox *combo_box, gpointer user_data)
g_free(name);
pidgin_stock_load_status_icon_theme(theme);
- pidgin_blist_refresh(purple_get_blist());
+ pidgin_blist_refresh(purple_blist_get_buddy_list());
}
}
@@ -1057,7 +1411,7 @@ add_theme_prefs_combo(GtkWidget *vbox,
{
GtkWidget *label;
GtkWidget *combo_box = NULL;
- GtkWidget *themesel_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ GtkWidget *themesel_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
label = gtk_label_new(label_str);
gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
@@ -1078,6 +1432,40 @@ add_theme_prefs_combo(GtkWidget *vbox,
}
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)
+{
+ 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_misc_set_alignment(GTK_MISC(label), 1, 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;
@@ -1085,7 +1473,7 @@ theme_page(void)
GtkSizeGroup *label_sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
GtkSizeGroup *combo_sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
- ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
+ 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"));
@@ -1107,6 +1495,20 @@ theme_page(void)
(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);
+
/* Status Icon Themes */
prefs_status_themes_combo_box = add_theme_prefs_combo(
vbox, combo_sg, label_sg, prefs_status_icon_themes,
@@ -1137,82 +1539,69 @@ theme_page(void)
}
static void
-formatting_toggle_cb(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons, void *toolbar)
+formatting_toggle_cb(PidginWebView *webview, PidginWebViewButtons buttons, void *data)
{
- gboolean bold, italic, uline;
-
- gtk_imhtml_get_current_format(GTK_IMHTML(imhtml),
- &bold, &italic, &uline);
-
- if (buttons & GTK_IMHTML_BOLD)
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_bold", bold);
- if (buttons & GTK_IMHTML_ITALIC)
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic", italic);
- if (buttons & GTK_IMHTML_UNDERLINE)
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline", uline);
-
- if (buttons & GTK_IMHTML_GROW || buttons & GTK_IMHTML_SHRINK)
+ gboolean bold, italic, uline, strike;
+
+ pidgin_webview_get_current_format(webview, &bold, &italic, &uline, &strike);
+
+ if (buttons & PIDGIN_WEBVIEW_BOLD)
+ purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_bold",
+ bold);
+ if (buttons & PIDGIN_WEBVIEW_ITALIC)
+ purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic",
+ italic);
+ if (buttons & PIDGIN_WEBVIEW_UNDERLINE)
+ purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline",
+ uline);
+ if (buttons & PIDGIN_WEBVIEW_STRIKE)
+ purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_strike",
+ strike);
+
+ if (buttons & PIDGIN_WEBVIEW_GROW || buttons & PIDGIN_WEBVIEW_SHRINK)
purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/font_size",
- gtk_imhtml_get_current_fontsize(GTK_IMHTML(imhtml)));
- if (buttons & GTK_IMHTML_FACE) {
- char *face = gtk_imhtml_get_current_fontface(GTK_IMHTML(imhtml));
- if (!face)
- face = g_strdup("");
+ pidgin_webview_get_current_fontsize(webview));
+ if (buttons & PIDGIN_WEBVIEW_FACE) {
+ char *face = pidgin_webview_get_current_fontface(webview);
- purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/font_face", face);
- g_free(face);
- }
-
- if (buttons & GTK_IMHTML_FORECOLOR) {
- char *color = gtk_imhtml_get_current_forecolor(GTK_IMHTML(imhtml));
- if (!color)
- color = g_strdup("");
+ if (face)
+ purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/font_face", face);
+ else
+ purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/font_face", "");
- purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor", color);
- g_free(color);
+ g_free(face);
}
- if (buttons & GTK_IMHTML_BACKCOLOR) {
- char *color;
- GObject *object;
-
- color = gtk_imhtml_get_current_backcolor(GTK_IMHTML(imhtml));
- if (!color)
- color = g_strdup("");
-
- /* Block the signal to prevent a loop. */
- object = g_object_ref(G_OBJECT(imhtml));
- g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL,
- NULL, toolbar);
- /* Clear the backcolor. */
- gtk_imhtml_toggle_backcolor(GTK_IMHTML(imhtml), "");
- /* Unblock the signal. */
- g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL,
- NULL, toolbar);
- g_object_unref(object);
+ if (buttons & PIDGIN_WEBVIEW_FORECOLOR) {
+ char *color = pidgin_webview_get_current_forecolor(webview);
- /* This will fire a toggle signal and get saved below. */
- gtk_imhtml_toggle_background(GTK_IMHTML(imhtml), color);
+ if (color)
+ purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor", color);
+ else
+ purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor", "");
g_free(color);
}
- if (buttons & GTK_IMHTML_BACKGROUND) {
- char *color = gtk_imhtml_get_current_background(GTK_IMHTML(imhtml));
- if (!color)
- color = g_strdup("");
+ if (buttons & PIDGIN_WEBVIEW_BACKCOLOR) {
+ char *color = pidgin_webview_get_current_backcolor(webview);
+
+ if (color)
+ purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor", color);
+ else
+ purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor", "");
- purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor", color);
g_free(color);
}
}
static void
-formatting_clear_cb(GtkIMHtml *imhtml, void *data)
+formatting_clear_cb(PidginWebView *webview, void *data)
{
purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_bold", FALSE);
purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic", FALSE);
purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline", FALSE);
+ purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_strike", FALSE);
purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/font_size", 3);
@@ -1234,7 +1623,7 @@ conversation_usetabs_cb(const char *name, PurplePrefType type,
}
-#define CONVERSATION_CLOSE_ACCEL_PATH "<main>/Conversation/Close"
+#define CONVERSATION_CLOSE_ACCEL_PATH "<Actions>/ConversationActions/Close"
/* Filled in in keyboard_shortcuts(). */
static GtkAccelKey ctrl_w = { 0, 0, 0 };
@@ -1340,7 +1729,7 @@ interface_page(void)
GtkSizeGroup *sg;
GList *names = NULL;
- ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
+ 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);
@@ -1380,7 +1769,7 @@ interface_page(void)
* Connect a signal to the above preference. When conversations are not
* shown in a tabbed window then all tabbing options should be disabled.
*/
- vbox2 = gtk_vbox_new(FALSE, 9);
+ vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 9);
gtk_box_pack_start(GTK_BOX(vbox), vbox2, FALSE, FALSE, 0);
purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/conversations/tabs",
conversation_usetabs_cb, vbox2);
@@ -1428,7 +1817,7 @@ apply_custom_font(void)
desc = pango_font_description_from_string(font);
}
- gtk_widget_modify_font(sample_imhtml, desc);
+ gtk_widget_modify_font(sample_webview, desc);
if (desc)
pango_font_description_free(desc);
@@ -1449,20 +1838,28 @@ conv_page(void)
{
GtkWidget *ret;
GtkWidget *vbox;
- GtkWidget *toolbar;
GtkWidget *iconpref1;
GtkWidget *iconpref2;
- GtkWidget *imhtml;
+ GtkWidget *webview;
GtkWidget *frame;
+#if 0
GtkWidget *hbox;
GtkWidget *checkbox;
GtkWidget *spin_button;
+#endif
- ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
+ 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, _("Conversations"));
+ pidgin_prefs_dropdown(vbox, _("Chat notification:"),
+ PURPLE_PREF_INT, PIDGIN_PREFS_ROOT "/conversations/notification_chat",
+ _("On unseen events"), PIDGIN_UNSEEN_EVENT,
+ _("On unseen text"), PIDGIN_UNSEEN_TEXT,
+ _("On unseen text and the nick was said"), PIDGIN_UNSEEN_NICK,
+ NULL);
+
pidgin_prefs_checkbox(_("Show _formatting on incoming messages"),
PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting", vbox);
pidgin_prefs_checkbox(_("Close IMs immediately when the tab is closed"),
@@ -1479,17 +1876,18 @@ conv_page(void)
pidgin_prefs_checkbox(_("_Notify buddies that you are typing to them"),
"/purple/conversations/im/send_typing", vbox);
-#ifdef USE_GTKSPELL
pidgin_prefs_checkbox(_("Highlight _misspelled words"),
PIDGIN_PREFS_ROOT "/conversations/spellcheck", vbox);
-#endif
pidgin_prefs_checkbox(_("Use smooth-scrolling"), PIDGIN_PREFS_ROOT "/conversations/use_smooth_scrolling", vbox);
#ifdef _WIN32
pidgin_prefs_checkbox(_("F_lash window when IMs are received"), PIDGIN_PREFS_ROOT "/win32/blink_im", vbox);
#endif
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+
+#if 0
+ /* TODO: it's not implemented */
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
checkbox = pidgin_prefs_checkbox(_("Resize incoming custom smileys"),
PIDGIN_PREFS_ROOT "/conversations/resize_custom_smileys", hbox);
@@ -1507,6 +1905,7 @@ conv_page(void)
G_CALLBACK(pidgin_toggle_sensitive), spin_button);
pidgin_add_widget_to_vbox(GTK_BOX(vbox), NULL, NULL, hbox, TRUE, NULL);
+#endif
pidgin_prefs_labeled_spin_button(vbox,
_("Minimum input area height in lines:"),
@@ -1542,33 +1941,38 @@ conv_page(void)
vbox = pidgin_make_frame(ret, _("Default Formatting"));
- frame = pidgin_create_imhtml(TRUE, &imhtml, &toolbar, NULL);
+ frame = pidgin_create_webview(TRUE, &webview, NULL);
gtk_widget_show(frame);
- gtk_widget_set_name(imhtml, "pidgin_prefs_font_imhtml");
+ gtk_widget_set_name(webview, "pidgin_prefs_font_webview");
gtk_widget_set_size_request(frame, 450, -1);
- gtk_imhtml_set_whole_buffer_formatting_only(GTK_IMHTML(imhtml), TRUE);
- gtk_imhtml_set_format_functions(GTK_IMHTML(imhtml),
- GTK_IMHTML_BOLD |
- GTK_IMHTML_ITALIC |
- GTK_IMHTML_UNDERLINE |
- GTK_IMHTML_GROW |
- GTK_IMHTML_SHRINK |
- GTK_IMHTML_FACE |
- GTK_IMHTML_FORECOLOR |
- GTK_IMHTML_BACKCOLOR |
- GTK_IMHTML_BACKGROUND);
-
- gtk_imhtml_append_text(GTK_IMHTML(imhtml), _("This is how your outgoing message text will appear when you use protocols that support formatting."), 0);
+ pidgin_webview_set_whole_buffer_formatting_only(PIDGIN_WEBVIEW(webview), TRUE);
+ pidgin_webview_set_format_functions(PIDGIN_WEBVIEW(webview),
+ PIDGIN_WEBVIEW_BOLD |
+ PIDGIN_WEBVIEW_ITALIC |
+ PIDGIN_WEBVIEW_UNDERLINE |
+ PIDGIN_WEBVIEW_STRIKE |
+ PIDGIN_WEBVIEW_GROW |
+ PIDGIN_WEBVIEW_SHRINK |
+ PIDGIN_WEBVIEW_FACE |
+ PIDGIN_WEBVIEW_FORECOLOR |
+ PIDGIN_WEBVIEW_BACKCOLOR);
+
+ pidgin_webview_append_html(PIDGIN_WEBVIEW(webview),
+ _("This is how your outgoing message text will "
+ "appear when you use protocols that support "
+ "formatting."));
gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
- gtk_imhtml_setup_entry(GTK_IMHTML(imhtml), PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_FORMATTING_WBFO);
+ pidgin_webview_setup_entry(PIDGIN_WEBVIEW(webview),
+ PURPLE_CONNECTION_FLAG_HTML |
+ PURPLE_CONNECTION_FLAG_FORMATTING_WBFO);
- g_signal_connect_after(G_OBJECT(imhtml), "format_function_toggle",
- G_CALLBACK(formatting_toggle_cb), toolbar);
- g_signal_connect_after(G_OBJECT(imhtml), "format_function_clear",
- G_CALLBACK(formatting_clear_cb), NULL);
- sample_imhtml = imhtml;
+ g_signal_connect_after(G_OBJECT(webview), "format-toggled",
+ G_CALLBACK(formatting_toggle_cb), NULL);
+ g_signal_connect_after(G_OBJECT(webview), "format-cleared",
+ G_CALLBACK(formatting_clear_cb), NULL);
+ sample_webview = webview;
gtk_widget_show(ret);
@@ -1578,6 +1982,26 @@ conv_page(void)
static void
network_ip_changed(GtkEntry *entry, gpointer data)
{
+#if GTK_CHECK_VERSION(3,0,0)
+ const gchar *text = gtk_entry_get_text(entry);
+ GtkStyleContext *context = gtk_widget_get_style_context(GTK_WIDGET(entry));
+
+ if (text && *text) {
+ if (purple_ip_address_is_valid(text)) {
+ purple_network_set_public_ip(text);
+ gtk_style_context_add_class(context, "good-ip");
+ gtk_style_context_remove_class(context, "bad-ip");
+ } else {
+ gtk_style_context_add_class(context, "bad-ip");
+ gtk_style_context_remove_class(context, "good-ip");
+ }
+
+ } else {
+ purple_network_set_public_ip("");
+ gtk_style_context_remove_class(context, "bad-ip");
+ gtk_style_context_remove_class(context, "good-ip");
+ }
+#else
const gchar *text = gtk_entry_get_text(entry);
GdkColor color;
@@ -1600,6 +2024,7 @@ network_ip_changed(GtkEntry *entry, gpointer data)
purple_network_set_public_ip("");
gtk_widget_modify_base(GTK_WIDGET(entry), GTK_STATE_NORMAL, NULL);
}
+#endif
}
static gboolean
@@ -1660,7 +2085,7 @@ proxy_button_clicked_cb(GtkWidget *button, gchar *program)
if (g_spawn_command_line_async(program, &err))
return;
- purple_notify_error(NULL, NULL, _("Cannot start proxy configuration program."), err->message);
+ purple_notify_error(NULL, NULL, _("Cannot start proxy configuration program."), err->message, NULL);
g_error_free(err);
}
@@ -1673,7 +2098,7 @@ browser_button_clicked_cb(GtkWidget *button, gchar *path)
if (g_spawn_command_line_async(path, &err))
return;
- purple_notify_error(NULL, NULL, _("Cannot start browser configuration program."), err->message);
+ purple_notify_error(NULL, NULL, _("Cannot start browser configuration program."), err->message, NULL);
g_error_free(err);
}
#endif
@@ -1722,8 +2147,25 @@ network_page(void)
GtkWidget *vbox, *hbox, *entry;
GtkWidget *label, *auto_ip_checkbox, *ports_checkbox, *spin_button;
GtkSizeGroup *sg;
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context;
+ GtkCssProvider *ip_css;
+ const gchar ip_style[] =
+ ".bad-ip {"
+ "color: @error_fg_color;"
+ "text-shadow: 0 1px @error_text_shadow;"
+ "background-image: none;"
+ "background-color: @error_bg_color;"
+ "}"
+ ".good-ip {"
+ "color: @question_fg_color;"
+ "text-shadow: 0 1px @question_text_shadow;"
+ "background-image: none;"
+ "background-color: @success_color;"
+ "}";
+#endif
- ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
+ 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, _("IP Address"));
@@ -1739,7 +2181,7 @@ network_page(void)
pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("ST_UN server:"),
sg, entry, TRUE, NULL);
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtk_container_add(GTK_CONTAINER(vbox), hbox);
label = gtk_label_new(NULL);
@@ -1763,6 +2205,15 @@ network_page(void)
g_signal_connect(G_OBJECT(entry), "changed",
G_CALLBACK(network_ip_changed), NULL);
+#if GTK_CHECK_VERSION(3,0,0)
+ ip_css = gtk_css_provider_new();
+ gtk_css_provider_load_from_data(ip_css, ip_style, -1, NULL);
+ context = gtk_widget_get_style_context(entry);
+ gtk_style_context_add_provider(context,
+ GTK_STYLE_PROVIDER(ip_css),
+ GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+#endif
+
hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Public _IP:"),
sg, entry, TRUE, NULL);
@@ -1781,7 +2232,7 @@ network_page(void)
pidgin_prefs_checkbox(_("_Enable automatic router port forwarding"),
"/purple/network/map_ports", vbox);
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
ports_checkbox = pidgin_prefs_checkbox(_("_Manually specify range of ports to listen on:"),
"/purple/network/ports_range_use", hbox);
@@ -1859,7 +2310,6 @@ get_available_browsers(void)
static const struct browser possible_browsers[] = {
{N_("Seamonkey"), "seamonkey"},
{N_("Opera"), "opera"},
- {N_("Netscape"), "netscape"},
{N_("Mozilla"), "mozilla"},
{N_("Konqueror"), "kfmclient"},
{N_("Google Chrome"), "google-chrome"},
@@ -1936,7 +2386,7 @@ browser_page(void)
GtkSizeGroup *sg;
GList *browsers = NULL;
- ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
+ 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, _("Browser Selection"));
@@ -1944,12 +2394,12 @@ browser_page(void)
if (purple_running_gnome()) {
gchar *path;
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
label = gtk_label_new(_("Browser preferences are configured in GNOME preferences"));
gtk_container_add(GTK_CONTAINER(vbox), hbox);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtk_container_add(GTK_CONTAINER(vbox), hbox);
path = g_find_program_in_path("gnome-control-center");
@@ -1987,11 +2437,10 @@ browser_page(void)
gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
gtk_size_group_add_widget(sg, label);
- hbox = gtk_hbox_new(FALSE, 0);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
label = pidgin_prefs_dropdown(hbox, _("_Open link in:"), PURPLE_PREF_INT,
PIDGIN_PREFS_ROOT "/browsers/place",
_("Browser default"), PIDGIN_BROWSER_DEFAULT,
- _("Existing window"), PIDGIN_BROWSER_CURRENT,
_("New window"), PIDGIN_BROWSER_NEW_WINDOW,
_("New tab"), PIDGIN_BROWSER_NEW_TAB,
NULL);
@@ -2028,24 +2477,26 @@ static GtkWidget *
proxy_page(void)
{
GtkWidget *ret = NULL, *vbox = NULL, *hbox = NULL;
- GtkWidget *table = NULL, *entry = NULL, *label = NULL, *proxy_button = NULL;
+ GtkWidget *table = NULL, *entry = NULL, *proxy_button = NULL;
+ GtkLabel *label = NULL;
GtkWidget *prefs_proxy_frame = NULL;
PurpleProxyInfo *proxy_info;
- ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
+ 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, _("Proxy Server"));
- prefs_proxy_frame = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ prefs_proxy_frame = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
if(purple_running_gnome()) {
gchar *path = NULL;
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
- label = gtk_label_new(_("Proxy preferences are configured in GNOME preferences"));
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
+ label = GTK_LABEL(gtk_label_new(_("Proxy preferences "
+ "are configured in GNOME preferences")));
gtk_container_add(GTK_CONTAINER(vbox), hbox);
- gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), FALSE, FALSE, 0);
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtk_container_add(GTK_CONTAINER(vbox), hbox);
path = g_find_program_in_path("gnome-network-properties");
@@ -2061,10 +2512,10 @@ proxy_page(void)
}
if (path == NULL) {
- label = gtk_label_new(NULL);
- gtk_label_set_markup(GTK_LABEL(label),
- _("<b>Proxy configuration program was not found.</b>"));
- gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+ label = GTK_LABEL(gtk_label_new(NULL));
+ gtk_label_set_markup(label, _("<b>Proxy configuration "
+ "program was not found.</b>"));
+ gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), FALSE, FALSE, 0);
} else {
proxy_button = gtk_button_new_with_mnemonic(_("Configure _Proxy"));
g_signal_connect(G_OBJECT(proxy_button), "clicked",
@@ -2077,7 +2528,7 @@ proxy_page(void)
which is never */
gtk_widget_show_all(ret);
} else {
- GtkWidget *prefs_proxy_subframe = gtk_vbox_new(FALSE, 0);
+ GtkWidget *prefs_proxy_subframe = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
/* This is a global option that affects SOCKS4 usage even with
* account-specific proxy settings */
@@ -2108,12 +2559,13 @@ proxy_page(void)
gtk_table_set_row_spacings(GTK_TABLE(table), 10);
gtk_container_add(GTK_CONTAINER(prefs_proxy_subframe), table);
- label = gtk_label_new_with_mnemonic(_("_Host:"));
+ label = GTK_LABEL(gtk_label_new_with_mnemonic(_("_Host:")));
gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
- gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
+ gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(label),
+ 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
entry = gtk_entry_new();
- gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
+ gtk_label_set_mnemonic_widget(label, entry);
gtk_table_attach(GTK_TABLE(table), entry, 1, 2, 0, 1, GTK_FILL, 0, 0, 0);
g_signal_connect(G_OBJECT(entry), "changed",
G_CALLBACK(proxy_print_option), (void *)PROXYHOST);
@@ -2122,16 +2574,18 @@ proxy_page(void)
gtk_entry_set_text(GTK_ENTRY(entry),
purple_proxy_info_get_host(proxy_info));
- hbox = gtk_hbox_new(TRUE, 5);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
+ gtk_box_set_homogeneous(GTK_BOX(hbox), TRUE);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
- pidgin_set_accessible_label (entry, label);
+ pidgin_set_accessible_label(entry, label);
- label = gtk_label_new_with_mnemonic(_("P_ort:"));
+ label = GTK_LABEL(gtk_label_new_with_mnemonic(_("P_ort:")));
gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
- gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1, GTK_FILL, 0, 0, 0);
+ gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(label),
+ 2, 3, 0, 1, GTK_FILL, 0, 0, 0);
entry = gtk_spin_button_new_with_range(0, 65535, 1);
- gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
+ gtk_label_set_mnemonic_widget(label, entry);
gtk_table_attach(GTK_TABLE(table), entry, 3, 4, 0, 1, GTK_FILL, 0, 0, 0);
g_signal_connect(G_OBJECT(entry), "changed",
G_CALLBACK(proxy_print_option), (void *)PROXYPORT);
@@ -2140,14 +2594,15 @@ proxy_page(void)
gtk_spin_button_set_value(GTK_SPIN_BUTTON(entry),
purple_proxy_info_get_port(proxy_info));
}
- pidgin_set_accessible_label (entry, label);
+ pidgin_set_accessible_label(entry, label);
- label = gtk_label_new_with_mnemonic(_("User_name:"));
+ label = GTK_LABEL(gtk_label_new_with_mnemonic(_("User_name:")));
gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
- gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, 0, 0, 0);
+ gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(label),
+ 0, 1, 1, 2, GTK_FILL, 0, 0, 0);
entry = gtk_entry_new();
- gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
+ gtk_label_set_mnemonic_widget(label, entry);
gtk_table_attach(GTK_TABLE(table), entry, 1, 2, 1, 2, GTK_FILL, 0, 0, 0);
g_signal_connect(G_OBJECT(entry), "changed",
G_CALLBACK(proxy_print_option), (void *)PROXYUSER);
@@ -2156,29 +2611,27 @@ proxy_page(void)
gtk_entry_set_text(GTK_ENTRY(entry),
purple_proxy_info_get_username(proxy_info));
- hbox = gtk_hbox_new(TRUE, 5);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
+ gtk_box_set_homogeneous(GTK_BOX(hbox), TRUE);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
- pidgin_set_accessible_label (entry, label);
+ pidgin_set_accessible_label(entry, label);
- label = gtk_label_new_with_mnemonic(_("Pa_ssword:"));
+ label = GTK_LABEL(gtk_label_new_with_mnemonic(_("Pa_ssword:")));
gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
- gtk_table_attach(GTK_TABLE(table), label, 2, 3, 1, 2, GTK_FILL, 0, 0, 0);
+ gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(label),
+ 2, 3, 1, 2, GTK_FILL, 0, 0, 0);
entry = gtk_entry_new();
- gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
+ gtk_label_set_mnemonic_widget(label, entry);
gtk_table_attach(GTK_TABLE(table), entry, 3, 4, 1, 2, GTK_FILL , 0, 0, 0);
gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
-#if !GTK_CHECK_VERSION(2,16,0)
- if (gtk_entry_get_invisible_char(GTK_ENTRY(entry)) == '*')
- gtk_entry_set_invisible_char(GTK_ENTRY(entry), PIDGIN_INVISIBLE_CHAR);
-#endif /* Less than GTK+ 2.16 */
g_signal_connect(G_OBJECT(entry), "changed",
G_CALLBACK(proxy_print_option), (void *)PROXYPASS);
if (proxy_info != NULL && purple_proxy_info_get_password(proxy_info) != NULL)
gtk_entry_set_text(GTK_ENTRY(entry),
purple_proxy_info_get_password(proxy_info));
- pidgin_set_accessible_label (entry, label);
+ pidgin_set_accessible_label(entry, label);
proxy_changed_cb("/purple/proxy/type", PURPLE_PREF_STRING,
purple_prefs_get_string("/purple/proxy/type"),
@@ -2196,7 +2649,7 @@ logging_page(void)
GtkWidget *vbox;
GList *names;
- ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
+ ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_CAT_SPACE);
gtk_container_set_border_width (GTK_CONTAINER (ret), PIDGIN_HIG_BORDER);
@@ -2220,7 +2673,262 @@ logging_page(void)
return ret;
}
-#ifndef _WIN32
+/*** keyring page *******************************************************/
+
+static void
+keyring_page_settings_changed(GtkWidget *widget, gpointer _setting)
+{
+ PurpleRequestField *setting = _setting;
+ PurpleRequestFieldType field_type;
+
+ gtk_widget_set_sensitive(keyring_apply, TRUE);
+
+ field_type = purple_request_field_get_field_type(setting);
+
+ if (field_type == PURPLE_REQUEST_FIELD_BOOLEAN) {
+ purple_request_field_bool_set_value(setting,
+ gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(widget)));
+ } else if (field_type == PURPLE_REQUEST_FIELD_STRING) {
+ purple_request_field_string_set_value(setting,
+ gtk_entry_get_text(GTK_ENTRY(widget)));
+ } else if (field_type == PURPLE_REQUEST_FIELD_INTEGER) {
+ purple_request_field_int_set_value(setting,
+ gtk_spin_button_get_value_as_int(
+ GTK_SPIN_BUTTON(widget)));
+ } else
+ g_return_if_reached();
+}
+
+static GtkWidget *
+keyring_page_add_settings_field(GtkBox *vbox, PurpleRequestField *setting,
+ GtkSizeGroup *sg)
+{
+ GtkWidget *widget, *hbox;
+ PurpleRequestFieldType field_type;
+ const gchar *label;
+
+ label = purple_request_field_get_label(setting);
+
+ field_type = purple_request_field_get_field_type(setting);
+ if (field_type == PURPLE_REQUEST_FIELD_BOOLEAN) {
+ widget = gtk_check_button_new_with_label(label);
+ label = NULL;
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
+ purple_request_field_bool_get_value(setting));
+ g_signal_connect(G_OBJECT(widget), "toggled",
+ G_CALLBACK(keyring_page_settings_changed), setting);
+ } else if (field_type == PURPLE_REQUEST_FIELD_STRING) {
+ widget = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(widget),
+ purple_request_field_string_get_value(setting));
+ if (purple_request_field_string_is_masked(setting))
+ gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE);
+ g_signal_connect(G_OBJECT(widget), "changed",
+ G_CALLBACK(keyring_page_settings_changed), setting);
+ } else if (field_type == PURPLE_REQUEST_FIELD_INTEGER) {
+ widget = gtk_spin_button_new_with_range(
+ purple_request_field_int_get_lower_bound(setting),
+ purple_request_field_int_get_upper_bound(setting), 1);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget),
+ purple_request_field_int_get_value(setting));
+ g_signal_connect(G_OBJECT(widget), "value-changed",
+ G_CALLBACK(keyring_page_settings_changed), setting);
+ } else {
+ purple_debug_error("gtkprefs", "Unsupported field type\n");
+ return NULL;
+ }
+
+ hbox = pidgin_add_widget_to_vbox(vbox, label, sg, widget,
+ FALSE, NULL);
+ return ((void*)hbox == (void*)vbox) ? widget : hbox;
+}
+
+/* XXX: it could be available for all plugins, not keyrings only */
+static GList *
+keyring_page_add_settings(PurpleRequestFields *settings)
+{
+ GList *it, *groups, *added_fields;
+ GtkSizeGroup *sg;
+
+ sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
+
+ added_fields = NULL;
+ groups = purple_request_fields_get_groups(settings);
+ for (it = g_list_first(groups); it != NULL; it = g_list_next(it)) {
+ GList *it2, *fields;
+ GtkBox *vbox;
+ PurpleRequestFieldGroup *group;
+ const gchar *group_title;
+
+ 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;
+
+ 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);
+ }
+ }
+
+ g_object_unref(sg);
+
+ return added_fields;
+}
+
+static void
+keyring_page_settings_apply(GtkButton *button, gpointer _unused)
+{
+ if (!purple_keyring_apply_settings(prefs, keyring_settings))
+ return;
+
+ gtk_widget_set_sensitive(keyring_apply, FALSE);
+}
+
+static void
+keyring_page_update_settings()
+{
+ if (keyring_settings != NULL)
+ purple_request_fields_destroy(keyring_settings);
+ keyring_settings = purple_keyring_read_settings();
+ if (!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);
+}
+
+static void
+keyring_page_pref_set_inuse(GError *error, gpointer _keyring_page_instance)
+{
+ PurpleKeyring *in_use = purple_keyring_get_inuse();
+
+ if (_keyring_page_instance != keyring_page_instance) {
+ purple_debug_info("gtkprefs", "pref window already closed\n");
+ return;
+ }
+
+ gtk_widget_set_sensitive(GTK_WIDGET(keyring_combo), TRUE);
+
+ if (error != NULL) {
+ pidgin_prefs_dropdown_revert_active(keyring_combo);
+ purple_notify_error(NULL, _("Keyring"),
+ _("Failed to set new keyring"), error->message, NULL);
+ return;
+ }
+
+ g_return_if_fail(in_use != NULL);
+ purple_prefs_set_string("/purple/keyring/active",
+ purple_keyring_get_id(in_use));
+
+ keyring_page_update_settings();
+}
+
+static void
+keyring_page_pref_changed(GtkComboBox *combo_box, PidginPrefValue value)
+{
+ 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 = purple_keyring_find_keyring_by_id(keyring_id);
+ if (keyring == NULL) {
+ pidgin_prefs_dropdown_revert_active(keyring_combo);
+ purple_notify_error(NULL, _("Keyring"),
+ _("Selected keyring is disabled"), NULL, NULL);
+ return;
+ }
+
+ 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;
+
+ purple_keyring_set_inuse(keyring, FALSE, keyring_page_pref_set_inuse,
+ keyring_page_instance);
+}
+
+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)
+{
+ 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);
+ g_list_free(names);
+
+ keyring_page_update_settings();
+
+ gtk_widget_show_all(keyring_page_instance);
+
+ return keyring_page_instance;
+}
+
+/*** keyring page - end *************************************************/
+
static gint
sound_cmd_yeah(GtkEntry *entry, gpointer d)
{
@@ -2248,7 +2956,6 @@ sound_changed2_cb(const char *name, PurplePrefType type,
gtk_widget_set_sensitive(vbox, strcmp(method, "none"));
}
-#endif /* !_WIN32 */
#ifdef USE_GSTREAMER
static void
@@ -2261,7 +2968,9 @@ sound_changed3_cb(const char *name, PurplePrefType type,
gtk_widget_set_sensitive(hbox,
!strcmp(method, "automatic") ||
!strcmp(method, "alsa") ||
- !strcmp(method, "esd"));
+ !strcmp(method, "esd") ||
+ !strcmp(method, "waveform") ||
+ !strcmp(method, "directsound"));
}
#endif /* USE_GSTREAMER */
@@ -2370,9 +3079,8 @@ select_sound(GtkWidget *button, gpointer being_NULL_is_fun)
filename = NULL;
purple_request_file(prefs, _("Sound Selection"), filename, FALSE,
- G_CALLBACK(sound_chosen_cb), NULL,
- NULL, NULL, NULL,
- GINT_TO_POINTER(sound_row_sel));
+ G_CALLBACK(sound_chosen_cb), NULL, NULL,
+ GINT_TO_POINTER(sound_row_sel));
}
#ifdef USE_GSTREAMER
@@ -2453,7 +3161,10 @@ static GtkWidget *
sound_page(void)
{
GtkWidget *ret;
- GtkWidget *vbox, *vbox2, *sw, *button;
+ GtkWidget *vbox, *vbox2, *button, *parent, *parent_parent, *parent_parent_parent;
+#ifdef USE_GSTREAMER
+ GtkWidget *sw;
+#endif
GtkSizeGroup *sg;
GtkTreeIter iter;
GtkWidget *event_view;
@@ -2466,32 +3177,38 @@ sound_page(void)
int j;
const char *file;
char *pref;
-#ifndef _WIN32
GtkWidget *dd;
GtkWidget *entry;
const char *cmd;
-#endif
- ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
+ 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_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_start(GTK_BOX(vbox2), vbox, FALSE, FALSE, 0);
-#ifndef _WIN32
dd = pidgin_prefs_dropdown(vbox2, _("_Method:"), PURPLE_PREF_STRING,
PIDGIN_PREFS_ROOT "/sound/method",
- _("Console beep"), "beep",
-#ifdef USE_GSTREAMER
_("Automatic"), "automatic",
+#ifdef USE_GSTREAMER
+#ifdef _WIN32
+/* "WaveForm", "waveform", */
+ "DirectSound", "directsound",
+#else
"ESD", "esd",
"ALSA", "alsa",
-#endif
+#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);
@@ -2511,7 +3228,6 @@ sound_page(void)
gtk_widget_set_sensitive(hbox,
!strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method"),
"custom"));
-#endif /* _WIN32 */
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);
@@ -2526,7 +3242,8 @@ sound_page(void)
NULL);
#ifdef USE_GSTREAMER
- sw = gtk_hscale_new_with_range(0.0, 100.0, 5.0);
+ sw = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,
+ 0.0, 100.0, 5.0);
gtk_range_set_increments(GTK_RANGE(sw), 5.0, 25.0);
gtk_range_set_value(GTK_RANGE(sw), purple_prefs_get_int(PIDGIN_PREFS_ROOT "/sound/volume"));
g_signal_connect (G_OBJECT (sw), "format-value",
@@ -2543,22 +3260,23 @@ sound_page(void)
purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method"), hbox);
#endif
-#ifndef _WIN32
gtk_widget_set_sensitive(vbox,
strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method"), "none"));
purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/sound/method",
sound_changed2_cb, vbox);
-#endif
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 */
- gtk_box_set_child_packing(GTK_BOX(vbox->parent), vbox, TRUE, TRUE, 0,
+ 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(vbox->parent->parent), vbox->parent, TRUE,
- TRUE, 0, GTK_PACK_START);
- gtk_box_set_child_packing(GTK_BOX(vbox->parent->parent->parent),
- vbox->parent->parent, 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);
/* SOUND SELECTION */
event_store = gtk_list_store_new (4, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT);
@@ -2613,7 +3331,7 @@ sound_page(void)
pidgin_make_scrollable(event_view, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, 100),
TRUE, TRUE, 0);
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ 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",
@@ -2667,7 +3385,7 @@ away_page(void)
GtkWidget *menu;
GtkSizeGroup *sg;
- ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
+ 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);
@@ -2690,7 +3408,7 @@ away_page(void)
_("_Minutes before becoming idle:"), "/purple/away/mins_before_away",
1, 24 * 60, sg);
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
button = pidgin_prefs_checkbox(_("Change to this status when _idle:"),
@@ -2747,6 +3465,686 @@ away_page(void)
return ret;
}
+#ifdef USE_VV
+static GList *
+get_vv_element_devices(const gchar *element_name)
+{
+ GList *ret = NULL;
+ GstElement *element;
+ GObjectClass *klass;
+#if !GST_CHECK_VERSION(1,0,0)
+ GstPropertyProbe *probe;
+ const GParamSpec *pspec;
+ guint i;
+ GValueArray *array;
+ enum {
+ PROBE_NONE,
+ PROBE_DEVICE,
+ PROBE_NAME
+ } probe_attr;
+#endif
+
+ ret = g_list_prepend(ret, g_strdup(_("Default")));
+ ret = g_list_prepend(ret, g_strdup(""));
+
+ if (!strcmp(element_name, "<custom>") || (*element_name == '\0')) {
+ return g_list_reverse(ret);
+ }
+
+ if (g_strcmp0(element_name, "videodisabledsrc") == 0) {
+ ret = g_list_prepend(ret, g_strdup(_("Random noise")));
+ ret = g_list_prepend(ret, g_strdup("snow"));
+
+ return g_list_reverse(ret);
+ }
+
+ element = gst_element_factory_make(element_name, "test");
+ if (!element) {
+ purple_debug_info("vvconfig", "'%s' - unable to find element\n",
+ element_name);
+ return g_list_reverse(ret);
+ }
+
+ klass = G_OBJECT_GET_CLASS (element);
+ if (!klass) {
+ purple_debug_info("vvconfig", "'%s' - unable to find GObject "
+ "Class\n", element_name);
+ return g_list_reverse(ret);
+ }
+
+#if GST_CHECK_VERSION(1,0,0)
+ purple_debug_info("vvconfig", "'%s' - gstreamer-1.0 doesn't suport "
+ "property probing\n", element_name);
+#else
+ if (g_object_class_find_property(klass, "device"))
+ probe_attr = PROBE_DEVICE;
+ else if (g_object_class_find_property(klass, "device-index") &&
+ g_object_class_find_property(klass, "device-name"))
+ probe_attr = PROBE_NAME;
+ else
+ probe_attr = PROBE_NONE;
+
+ if (!GST_IS_PROPERTY_PROBE(element))
+ probe_attr = PROBE_NONE;
+
+ if (probe_attr == PROBE_NONE)
+ {
+ purple_debug_info("vvconfig", "'%s' - no possibility to probe "
+ "for devices\n", element_name);
+ gst_object_unref(element);
+ return g_list_reverse(ret);
+ }
+
+ probe = GST_PROPERTY_PROBE(element);
+
+ if (probe_attr == PROBE_DEVICE)
+ pspec = gst_property_probe_get_property(probe, "device");
+ else /* probe_attr == PROBE_NAME */
+ pspec = gst_property_probe_get_property(probe, "device-name");
+
+ if (!pspec) {
+ purple_debug_info("vvconfig", "'%s' - creating probe failed\n",
+ element_name);
+ gst_object_unref(element);
+ return g_list_reverse(ret);
+ }
+
+ /* Set autoprobe[-fps] to FALSE to avoid delays when probing. */
+ if (g_object_class_find_property(klass, "autoprobe"))
+ g_object_set(G_OBJECT(element), "autoprobe", FALSE, NULL);
+ if (g_object_class_find_property(klass, "autoprobe-fps"))
+ g_object_set(G_OBJECT(element), "autoprobe-fps", FALSE, NULL);
+
+ array = gst_property_probe_probe_and_get_values(probe, pspec);
+ if (array == NULL) {
+ purple_debug_info("vvconfig", "'%s' has no devices\n",
+ element_name);
+ gst_object_unref(element);
+ return g_list_reverse(ret);
+ }
+
+ for (i = 0; i < array->n_values; i++) {
+ GValue *device;
+ const gchar *name;
+ const gchar *device_name;
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ /* GValueArray is in gstreamer-0.10 API */
+ device = g_value_array_get_nth(array, i);
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+ if (probe_attr == PROBE_DEVICE) {
+ g_object_set_property(G_OBJECT(element), "device",
+ device);
+ if (gst_element_set_state(element, GST_STATE_READY)
+ != GST_STATE_CHANGE_SUCCESS)
+ {
+ purple_debug_warning("vvconfig", "Error "
+ "changing state of %s\n", element_name);
+ continue;
+ }
+
+ g_object_get(G_OBJECT(element), "device-name", &name,
+ NULL);
+ device_name = g_strdup(g_value_get_string(device));
+ } else /* probe_attr == PROBE_NAME */ {
+ name = g_strdup(g_value_get_string(device));
+ device_name = g_strdup_printf("%d", i);
+ }
+
+ if (name == NULL)
+ name = _("Unknown");
+ purple_debug_info("vvconfig", "Found device %s: %s for %s\n",
+ device_name, name, element_name);
+ ret = g_list_prepend(ret, (gpointer)name);
+ ret = g_list_prepend(ret, (gpointer)device_name);
+ gst_element_set_state(element, GST_STATE_NULL);
+ }
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ /* GValueArray is in gstreamer-0.10 API */
+ g_value_array_free(array);
+G_GNUC_END_IGNORE_DEPRECATIONS
+#endif
+
+ gst_object_unref(element);
+ return g_list_reverse(ret);
+}
+
+static GList *
+get_vv_element_plugins(const gchar **plugins)
+{
+ GList *ret = NULL;
+
+ ret = g_list_prepend(ret, (gpointer)_("Default"));
+ ret = g_list_prepend(ret, "");
+ for (; plugins[0] && plugins[1]; plugins += 2) {
+#if GST_CHECK_VERSION(1,0,0)
+ if (gst_registry_check_feature_version(gst_registry_get(),
+ plugins[0], 0, 0, 0)
+#else
+ if (gst_default_registry_check_feature_version(plugins[0], 0, 0, 0)
+#endif
+ || g_strcmp0(plugins[0], "videodisabledsrc") == 0)
+ {
+ ret = g_list_prepend(ret, (gpointer)_(plugins[1]));
+ ret = g_list_prepend(ret, (gpointer)plugins[0]);
+ }
+ }
+
+ return g_list_reverse(ret);
+}
+
+static GstElement *
+create_test_element(PurpleMediaElementType type)
+{
+ PurpleMediaElementInfo *element_info;
+
+ element_info = purple_media_manager_get_active_element(purple_media_manager_get(), type);
+
+ g_return_val_if_fail(element_info, NULL);
+
+ return purple_media_element_info_call_create(element_info,
+ NULL, NULL, NULL);
+}
+
+static void
+vv_test_switch_page_cb(GtkNotebook *notebook, GtkWidget *page, guint num, gpointer data)
+{
+ GtkWidget *test = data;
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(test), FALSE);
+}
+
+static GstElement *
+create_voice_pipeline(void)
+{
+ GstElement *pipeline;
+ GstElement *src, *sink;
+ GstElement *volume;
+ GstElement *level;
+ GstElement *valve;
+
+ pipeline = gst_pipeline_new("voicetest");
+
+ src = create_test_element(PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SRC);
+ sink = create_test_element(PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SINK);
+ volume = gst_element_factory_make("volume", "volume");
+ level = gst_element_factory_make("level", "level");
+ valve = gst_element_factory_make("valve", "valve");
+
+ gst_bin_add_many(GST_BIN(pipeline), src, volume, level, valve, sink, NULL);
+ gst_element_link_many(src, volume, level, valve, sink, NULL);
+
+ purple_debug_info("gtkprefs", "create_voice_pipeline: setting pipeline "
+ "state to GST_STATE_PLAYING - it may hang here on win32\n");
+ gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING);
+ purple_debug_info("gtkprefs", "create_voice_pipeline: state is set\n");
+
+ return pipeline;
+}
+
+static void
+on_volume_change_cb(GtkWidget *w, gdouble value, gpointer data)
+{
+ GstElement *volume;
+
+ if (!voice_pipeline)
+ return;
+
+ volume = gst_bin_get_by_name(GST_BIN(voice_pipeline), "volume");
+ g_object_set(volume, "volume",
+ gtk_scale_button_get_value(GTK_SCALE_BUTTON(w)) * 10.0, NULL);
+}
+
+static gdouble
+gst_msg_db_to_percent(GstMessage *msg, gchar *value_name)
+{
+ const GValue *list;
+ const GValue *value;
+ gdouble value_db;
+ 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;
+}
+
+static gboolean
+gst_bus_cb(GstBus *bus, GstMessage *msg, gpointer data)
+{
+ if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ELEMENT &&
+ gst_structure_has_name(gst_message_get_structure(msg), "level")) {
+
+ GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg));
+ gchar *name = gst_element_get_name(src);
+
+ if (!strcmp(name, "level")) {
+ gdouble percent;
+ gdouble threshold;
+ GstElement *valve;
+
+ percent = gst_msg_db_to_percent(msg, "rms");
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(voice_level), percent);
+
+ percent = gst_msg_db_to_percent(msg, "decay");
+ threshold = gtk_range_get_value(GTK_RANGE(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_free(name);
+ }
+
+ return TRUE;
+}
+
+static void
+voice_test_destroy_cb(GtkWidget *w, gpointer data)
+{
+ if (!voice_pipeline)
+ return;
+
+ gst_element_set_state(voice_pipeline, GST_STATE_NULL);
+ gst_object_unref(voice_pipeline);
+ voice_pipeline = NULL;
+}
+
+static void
+enable_voice_test(void)
+{
+ GstBus *bus;
+
+ voice_pipeline = create_voice_pipeline();
+ bus = gst_pipeline_get_bus(GST_PIPELINE(voice_pipeline));
+ gst_bus_add_signal_watch(bus);
+ g_signal_connect(bus, "message", G_CALLBACK(gst_bus_cb), NULL);
+ gst_object_unref(bus);
+}
+
+static void
+toggle_voice_test_cb(GtkToggleButton *test, gpointer 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);
+ g_signal_connect(test, "destroy",
+ G_CALLBACK(voice_test_destroy_cb), NULL);
+ g_signal_connect(prefsnotebook, "switch-page",
+ G_CALLBACK(vv_test_switch_page_cb), test);
+ } 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);
+ g_object_disconnect(test, "any-signal::destroy",
+ G_CALLBACK(voice_test_destroy_cb), NULL,
+ NULL);
+ g_object_disconnect(prefsnotebook, "any-signal::switch-page",
+ G_CALLBACK(vv_test_switch_page_cb), test,
+ NULL);
+ voice_test_destroy_cb(NULL, NULL);
+ }
+}
+
+static void
+volume_changed_cb(GtkScaleButton *button, gdouble value, gpointer data)
+{
+ purple_prefs_set_int("/purple/media/audio/volume/input", value * 100);
+}
+
+static void
+threshold_value_changed_cb(GtkScale *scale, GtkWidget *label)
+{
+ int value;
+ char *tmp;
+
+ value = (int)gtk_range_get_value(GTK_RANGE(scale));
+ tmp = g_strdup_printf(_("Silence threshold: %d%%"), value);
+ gtk_label_set_label(GTK_LABEL(label), tmp);
+ g_free(tmp);
+
+ purple_prefs_set_int("/purple/media/audio/silence_threshold", value);
+}
+
+static void
+make_voice_test(GtkWidget *vbox)
+{
+ GtkWidget *test;
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *level;
+ GtkWidget *volume;
+ GtkWidget *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);
+ 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);
+
+ 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_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+ 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);
+ 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;
+ g_signal_connect(test, "toggled",
+ G_CALLBACK(toggle_voice_test_cb), NULL);
+}
+
+static GstElement *
+create_video_pipeline(void)
+{
+ GstElement *pipeline;
+ GstElement *src, *sink;
+
+ pipeline = gst_pipeline_new("videotest");
+ src = create_test_element(PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SRC);
+ sink = create_test_element(PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SINK);
+
+ g_object_set_data(G_OBJECT(pipeline), "sink", sink);
+
+ gst_bin_add_many(GST_BIN(pipeline), src, sink, NULL);
+ gst_element_link_many(src, sink, NULL);
+
+ return pipeline;
+}
+
+static void
+video_test_destroy_cb(GtkWidget *w, gpointer data)
+{
+ if (!video_pipeline)
+ return;
+
+ gst_element_set_state(video_pipeline, GST_STATE_NULL);
+ gst_object_unref(video_pipeline);
+ video_pipeline = NULL;
+}
+
+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
+ 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)
+{
+ GstBus *bus;
+ GdkWindow *window = gtk_widget_get_window(video_drawing_area);
+ gulong window_id = 0;
+
+#ifdef GDK_WINDOWING_WIN32
+ if (GDK_IS_WIN32_WINDOW(window))
+ window_id = GPOINTER_TO_UINT(GDK_WINDOW_HWND(window));
+ else
+#endif
+#ifdef GDK_WINDOWING_X11
+ if (GDK_IS_X11_WINDOW(window))
+ window_id = gdk_x11_window_get_xid(window);
+ else
+#endif
+#ifdef GDK_WINDOWING_QUARTZ
+ if (GDK_IS_QUARTZ_WINDOW(window))
+ window_id = (gulong)gdk_quartz_window_get_nsview(window);
+ else
+#endif
+ g_warning("Unsupported GDK backend");
+#if !(defined(GDK_WINDOWING_WIN32) \
+ || defined(GDK_WINDOWING_X11) \
+ || defined(GDK_WINDOWING_QUARTZ))
+# 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)
+ 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);
+}
+
+static void
+toggle_video_test_cb(GtkToggleButton *test, gpointer data)
+{
+ if (gtk_toggle_button_get_active(test)) {
+ enable_video_test();
+ g_signal_connect(test, "destroy",
+ G_CALLBACK(video_test_destroy_cb), NULL);
+ g_signal_connect(prefsnotebook, "switch-page",
+ G_CALLBACK(vv_test_switch_page_cb), test);
+ } else {
+ g_object_disconnect(test, "any-signal::destroy",
+ G_CALLBACK(video_test_destroy_cb), NULL,
+ NULL);
+ g_object_disconnect(prefsnotebook, "any-signal::switch-page",
+ G_CALLBACK(vv_test_switch_page_cb), test,
+ NULL);
+ video_test_destroy_cb(NULL, NULL);
+ }
+}
+
+static void
+make_video_test(GtkWidget *vbox)
+{
+ GtkWidget *test;
+ GtkWidget *video;
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkRGBA color = {0.0, 0.0, 0.0, 1.0};
+#else
+ GdkColor color = {0, 0, 0, 0};
+#endif
+
+ video_drawing_area = video = gtk_drawing_area_new();
+ gtk_box_pack_start(GTK_BOX(vbox), video, TRUE, TRUE, 0);
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_background_color(video, GTK_STATE_FLAG_NORMAL, &color);
+#else
+ gtk_widget_modify_bg(video, GTK_STATE_NORMAL, &color);
+#endif
+ 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);
+
+ g_signal_connect(test, "toggled",
+ G_CALLBACK(toggle_video_test_cb), NULL);
+}
+
+static void
+vv_plugin_changed_cb(const gchar *name, PurplePrefType type,
+ gconstpointer value, gpointer data)
+{
+ GtkWidget *vbox = data;
+ GtkSizeGroup *sg;
+ GtkWidget *widget;
+ gchar *pref;
+ GList *devices;
+
+ sg = g_object_get_data(G_OBJECT(vbox), "size-group");
+ widget = g_object_get_data(G_OBJECT(vbox), "device-hbox");
+ gtk_widget_destroy(widget);
+
+ pref = g_strdup(name);
+ strcpy(pref + strlen(pref) - strlen("plugin"), "device");
+ devices = get_vv_element_devices(value);
+ if (g_list_find_custom(devices, purple_prefs_get_string(pref),
+ (GCompareFunc)strcmp) == NULL)
+ {
+ GList *next = g_list_next(devices);
+ if (next)
+ purple_prefs_set_string(pref, next->data);
+ }
+ widget = pidgin_prefs_dropdown_from_list(vbox, _("_Device"),
+ PURPLE_PREF_STRING, pref, devices);
+ g_list_free_full(devices, g_free);
+ gtk_size_group_add_widget(sg, widget);
+ gtk_misc_set_alignment(GTK_MISC(widget), 0, 0.5);
+
+ g_object_set_data(G_OBJECT(vbox), "device-hbox",
+ gtk_widget_get_parent(widget));
+ g_signal_connect_swapped(widget, "destroy", G_CALLBACK(g_free), pref);
+
+ /* 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();
+ }
+}
+
+static void
+make_vv_frame(GtkWidget *parent, GtkSizeGroup *sg,
+ const gchar *name, const gchar **plugin_strs,
+ const gchar *plugin_pref, const gchar *device_pref)
+{
+ GtkWidget *vbox, *widget;
+ GList *plugins, *devices;
+
+ vbox = pidgin_make_frame(parent, name);
+
+ /* Setup plugin preference */
+ plugins = get_vv_element_plugins(plugin_strs);
+ widget = pidgin_prefs_dropdown_from_list(vbox, _("_Plugin"),
+ PURPLE_PREF_STRING, plugin_pref,
+ plugins);
+ g_list_free(plugins);
+ gtk_size_group_add_widget(sg, widget);
+ gtk_misc_set_alignment(GTK_MISC(widget), 0, 0.5);
+
+ /* Setup device preference */
+ devices = get_vv_element_devices(purple_prefs_get_string(plugin_pref));
+ if (g_list_find_custom(devices, purple_prefs_get_string(device_pref),
+ (GCompareFunc)strcmp) == NULL)
+ {
+ GList *next = g_list_next(devices);
+ if (next)
+ purple_prefs_set_string(device_pref, next->data);
+ }
+ widget = pidgin_prefs_dropdown_from_list(vbox, _("_Device"),
+ PURPLE_PREF_STRING, device_pref,
+ devices);
+ g_list_free_full(devices, g_free);
+ gtk_size_group_add_widget(sg, widget);
+ gtk_misc_set_alignment(GTK_MISC(widget), 0, 0.5);
+
+ widget = gtk_widget_get_parent(widget);
+ g_object_set_data(G_OBJECT(vbox), "size-group", sg);
+ g_object_set_data(G_OBJECT(vbox), "device-hbox", widget);
+ purple_prefs_connect_callback(vbox, plugin_pref, vv_plugin_changed_cb,
+ vbox);
+ g_signal_connect_swapped(vbox, "destroy",
+ G_CALLBACK(purple_prefs_disconnect_by_handle), vbox);
+}
+
+static GtkWidget *
+vv_page(void)
+{
+ GtkWidget *ret;
+ GtkWidget *vbox;
+ GtkSizeGroup *sg;
+
+ 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);
+
+ vbox = pidgin_make_frame(ret, _("Audio"));
+ make_vv_frame(vbox, sg, _("Input"), AUDIO_SRC_PLUGINS,
+ PIDGIN_PREFS_ROOT "/vvconfig/audio/src/plugin",
+ PIDGIN_PREFS_ROOT "/vvconfig/audio/src/device");
+ make_vv_frame(vbox, sg, _("Output"), AUDIO_SINK_PLUGINS,
+ PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/plugin",
+ PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/device");
+ make_voice_test(vbox);
+
+ vbox = pidgin_make_frame(ret, _("Video"));
+ make_vv_frame(vbox, sg, _("Input"), VIDEO_SRC_PLUGINS,
+ PIDGIN_PREFS_ROOT "/vvconfig/video/src/plugin",
+ PIDGIN_PREFS_ROOT "/vvconfig/video/src/device");
+ make_vv_frame(vbox, sg, _("Output"), VIDEO_SINK_PLUGINS,
+ PIDGIN_PREFS_ROOT "/vvconfig/video/sink/plugin",
+ PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device");
+ make_video_test(vbox);
+
+ gtk_widget_show_all(ret);
+
+ return ret;
+}
+#endif
+
static int
prefs_notebook_add_page(const char *text, GtkWidget *page, int ind)
{
@@ -2769,10 +4167,14 @@ prefs_notebook_init(void)
prefs_notebook_add_page(_("Logging"), logging_page(), notebook_page++);
prefs_notebook_add_page(_("Network"), network_page(), notebook_page++);
prefs_notebook_add_page(_("Proxy"), proxy_page(), notebook_page++);
+ prefs_notebook_add_page(_("Password Storage"), keyring_page(), notebook_page++);
prefs_notebook_add_page(_("Sounds"), sound_page(), notebook_page++);
prefs_notebook_add_page(_("Status / Idle"), away_page(), notebook_page++);
prefs_notebook_add_page(_("Themes"), theme_page(), notebook_page++);
+#ifdef USE_VV
+ prefs_notebook_add_page(_("Voice/Video"), vv_page(), notebook_page++);
+#endif
}
void
@@ -2794,7 +4196,11 @@ pidgin_prefs_show(void)
/* Back to instant-apply! I win! BU-HAHAHA! */
/* Create the window */
+#if GTK_CHECK_VERSION(3,0,0)
+ prefs = pidgin_create_dialog(_("Preferences"), 0, "preferences", FALSE);
+#else
prefs = pidgin_create_dialog(_("Preferences"), PIDGIN_HIG_BORDER, "preferences", FALSE);
+#endif
g_signal_connect(G_OBJECT(prefs), "destroy",
G_CALLBACK(delete_prefs), NULL);
@@ -2849,15 +4255,24 @@ static void
smiley_theme_pref_cb(const char *name, PurplePrefType type,
gconstpointer value, gpointer data)
{
- const char *themename = value;
- GSList *themes;
+ const gchar *theme_name = value;
+ GList *themes, *it;
- for (themes = smiley_themes; themes; themes = themes->next) {
- struct smiley_theme *smile = themes->data;
- if (smile->name && strcmp(themename, smile->name) == 0) {
- pidgin_themes_load_smiley_theme(smile->path, TRUE);
- break;
- }
+ if (g_strcmp0(theme_name, "none") == 0) {
+ purple_smiley_theme_set_current(NULL);
+ return;
+ }
+
+ /* XXX: could be cached when initializing prefs view */
+ themes = pidgin_smiley_theme_get_all();
+
+ for (it = themes; it; it = g_list_next(it)) {
+ PidginSmileyTheme *theme = it->data;
+
+ if (g_strcmp0(pidgin_smiley_theme_get_name(theme), theme_name))
+ continue;
+
+ purple_smiley_theme_set_current(PURPLE_SMILEY_THEME(theme));
}
}
@@ -2888,6 +4303,10 @@ pidgin_prefs_init(void)
/* 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");
@@ -2896,45 +4315,60 @@ pidgin_prefs_init(void)
purple_prefs_connect_callback(&prefs, PIDGIN_PREFS_ROOT "/smileys/theme",
smiley_theme_pref_cb, NULL);
+#ifdef USE_VV
+ /* Voice/Video */
+ purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig");
+ purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/audio");
+ purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/audio/src");
+ purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/src/plugin", "");
+ purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/src/device", "");
+ purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink");
+ purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/plugin", "");
+ purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/device", "");
+ purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/video");
+ purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/video/src");
+ purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/video/src/plugin", "");
+ purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/video/src/device", "");
+ purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/video");
+ purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/video/sink");
+ purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/plugin", "");
+ purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device", "");
+#endif
+
pidgin_prefs_update_old();
}
void
pidgin_prefs_update_old(void)
{
- const char *str = NULL;
-
- purple_prefs_rename("/gaim/gtk", PIDGIN_PREFS_ROOT);
-
/* Rename some old prefs */
purple_prefs_rename(PIDGIN_PREFS_ROOT "/logging/log_ims", "/purple/logging/log_ims");
purple_prefs_rename(PIDGIN_PREFS_ROOT "/logging/log_chats", "/purple/logging/log_chats");
purple_prefs_rename("/purple/conversations/placement",
PIDGIN_PREFS_ROOT "/conversations/placement");
- purple_prefs_rename(PIDGIN_PREFS_ROOT "/debug/timestamps", "/purple/debug/timestamps");
purple_prefs_rename(PIDGIN_PREFS_ROOT "/conversations/im/raise_on_events", "/plugins/gtk/X11/notify/method_raise");
purple_prefs_rename_boolean_toggle(PIDGIN_PREFS_ROOT "/conversations/ignore_colors",
PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting");
/*
- * this path pref changed to a string, so migrate. I know this will break
- * things for and confuse users that use multiple versions with the same
- * config directory, but I'm not inclined to want to deal with that at the
- * moment. -- rekkanoryo
+ * This path pref changed to a string, so migrate. I know this will
+ * break things for and confuse users that use multiple versions with
+ * the same config directory, but I'm not inclined to want to deal with
+ * that at the moment. -- rekkanoryo
*/
- if((str = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/browsers/command")) != NULL) {
- purple_prefs_set_string(PIDGIN_PREFS_ROOT "/browsers/manual_command", str);
+ if (purple_prefs_exists(PIDGIN_PREFS_ROOT "/browsers/command") &&
+ purple_prefs_get_pref_type(PIDGIN_PREFS_ROOT "/browsers/command") ==
+ PURPLE_PREF_PATH)
+ {
+ const char *str = purple_prefs_get_path(
+ PIDGIN_PREFS_ROOT "/browsers/command");
+ purple_prefs_set_string(
+ PIDGIN_PREFS_ROOT "/browsers/manual_command", str);
purple_prefs_remove(PIDGIN_PREFS_ROOT "/browsers/command");
}
- /* this string pref moved into the core, try to be friendly */
- purple_prefs_rename(PIDGIN_PREFS_ROOT "/idle/reporting_method", "/purple/away/idle_reporting");
- if ((str = purple_prefs_get_string("/purple/away/idle_reporting")) &&
- strcmp(str, "gaim") == 0)
- purple_prefs_set_string("/purple/away/idle_reporting", "purple");
-
/* Remove some no-longer-used prefs */
purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/auto_expand_contacts");
purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/button_style");
@@ -2942,6 +4376,7 @@ pidgin_prefs_update_old(void)
purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/raise_on_events");
purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_group_count");
purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_warning_level");
+ purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/tooltip_delay");
purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/button_type");
purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/ctrl_enter_sends");
purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/enter_sends");
@@ -2964,6 +4399,7 @@ pidgin_prefs_update_old(void)
purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/ignore_fonts");
purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/ignore_font_sizes");
purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/passthrough_unknown_commands");
+ purple_prefs_remove(PIDGIN_PREFS_ROOT "/debug/timestamps");
purple_prefs_remove(PIDGIN_PREFS_ROOT "/idle");
purple_prefs_remove(PIDGIN_PREFS_ROOT "/logging/individual_logs");
purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/signon");
@@ -2992,4 +4428,59 @@ pidgin_prefs_update_old(void)
PIDGIN_PREFS_ROOT "/conversations/im/x");
purple_prefs_rename(PIDGIN_PREFS_ROOT "/conversations/y",
PIDGIN_PREFS_ROOT "/conversations/im/y");
+
+ /* Fixup vvconfig plugin prefs */
+ if (purple_prefs_exists("/plugins/core/vvconfig/audio/src/plugin")) {
+ purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/src/plugin",
+ purple_prefs_get_string("/plugins/core/vvconfig/audio/src/plugin"));
+ }
+ if (purple_prefs_exists("/plugins/core/vvconfig/audio/src/device")) {
+ purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/src/device",
+ purple_prefs_get_string("/plugins/core/vvconfig/audio/src/device"));
+ }
+ if (purple_prefs_exists("/plugins/core/vvconfig/audio/sink/plugin")) {
+ purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/plugin",
+ purple_prefs_get_string("/plugins/core/vvconfig/audio/sink/plugin"));
+ }
+ if (purple_prefs_exists("/plugins/core/vvconfig/audio/sink/device")) {
+ purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/device",
+ purple_prefs_get_string("/plugins/core/vvconfig/audio/sink/device"));
+ }
+ if (purple_prefs_exists("/plugins/core/vvconfig/video/src/plugin")) {
+ purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/video/src/plugin",
+ purple_prefs_get_string("/plugins/core/vvconfig/video/src/plugin"));
+ }
+ if (purple_prefs_exists("/plugins/core/vvconfig/video/src/device")) {
+ purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/video/src/device",
+ purple_prefs_get_string("/plugins/core/vvconfig/video/src/device"));
+ }
+ if (purple_prefs_exists("/plugins/gtk/vvconfig/video/sink/plugin")) {
+ purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/plugin",
+ purple_prefs_get_string("/plugins/gtk/vvconfig/video/sink/plugin"));
+ }
+ if (purple_prefs_exists("/plugins/gtk/vvconfig/video/sink/device")) {
+ purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device",
+ purple_prefs_get_string("/plugins/gtk/vvconfig/video/sink/device"));
+ }
+
+ purple_prefs_remove("/plugins/core/vvconfig");
+ purple_prefs_remove("/plugins/gtk/vvconfig");
+
+#ifndef _WIN32
+ /* Added in 3.0.0. */
+ if (purple_prefs_get_int(PIDGIN_PREFS_ROOT "/browsers/place") == 1) {
+ /* If the "open link in" pref is set to the old value for "existing
+ window" then change it to "default." */
+ purple_prefs_set_int(PIDGIN_PREFS_ROOT "/browsers/place",
+ PIDGIN_BROWSER_DEFAULT);
+ }
+
+ /* Added in 3.0.0. */
+ if (g_str_equal(
+ purple_prefs_get_string(PIDGIN_PREFS_ROOT "/browsers/browser"),
+ "netscape")) {
+ purple_prefs_set_string(PIDGIN_PREFS_ROOT "/browsers/browser", "xdg-open");
+ }
+#endif /* !_WIN32 */
}
+
diff --git a/pidgin/gtkprefs.h b/pidgin/gtkprefs.h
index d07c968b8a..9c13781359 100644
--- a/pidgin/gtkprefs.h
+++ b/pidgin/gtkprefs.h
@@ -1,8 +1,3 @@
-/**
- * @file gtkprefs.h GTK+ Preferences
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -24,41 +19,57 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
*/
+
#ifndef _PIDGINPREFS_H_
#define _PIDGINPREFS_H_
+/**
+ * SECTION:gtkprefs
+ * @section_id: pidgin-gtkprefs
+ * @short_description: <filename>gtkprefs.h</filename>
+ * @title: Preferences
+ */
#include "prefs.h"
+G_BEGIN_DECLS
+
/**
+ * pidgin_prefs_init:
+ *
* Initializes all UI-specific preferences.
*/
void pidgin_prefs_init(void);
/**
+ * pidgin_prefs_show:
+ *
* Shows the preferences dialog.
*/
void pidgin_prefs_show(void);
/**
- * Add a new checkbox for a boolean preference
+ * pidgin_prefs_checkbox:
+ * @title: The text to be displayed as the checkbox label
+ * @key: The key of the purple bool pref that will be represented by the checkbox
+ * @page: The page to which the new checkbox will be added
*
- * @param title The text to be displayed as the checkbox label
- * @param key The key of the purple bool pref that will be represented by the checkbox
- * @param page The page to which the new checkbox will be added
+ * Add a new checkbox for a boolean preference
*/
GtkWidget *pidgin_prefs_checkbox(const char *title, const char *key,
GtkWidget *page);
/**
+ * pidgin_prefs_labeled_spin_button:
+ * @page: The page to which the spin button will be added
+ * @title: The text to be displayed as the spin button label
+ * @key: The key of the int pref that will be represented by the spin button
+ * @min: The minimum value of the spin button
+ * @max: The maximum value of the spin button
+ * @sg: If not NULL, the size group to which the spin button will be added
+ *
* Add a new spin button representing an int preference
*
- * @param page The page to which the spin button will be added
- * @param title The text to be displayed as the spin button label
- * @param key The key of the int pref that will be represented by the spin button
- * @param min The minimum value of the spin button
- * @param max The maximum value of the spin button
- * @param sg If not NULL, the size group to which the spin button will be added
- * @return An hbox containing both the label and the spinner. Can be
+ * Returns: An hbox containing both the label and the spinner. Can be
* used to set the widgets to sensitive or insensitive based on the
* value of a checkbox.
*/
@@ -66,14 +77,15 @@ GtkWidget *pidgin_prefs_labeled_spin_button(GtkWidget *page,
const gchar *title, const char *key, int min, int max, GtkSizeGroup *sg);
/**
- * Add a new entry representing a string preference
+ * pidgin_prefs_labeled_entry:
+ * @page: The page to which the entry will be added
+ * @title: The text to be displayed as the entry label
+ * @key: The key of the string pref that will be represented by the entry
+ * @sg: If not NULL, the size group to which the entry will be added
*
- * @param page The page to which the entry will be added
- * @param title The text to be displayed as the entry label
- * @param key The key of the string pref that will be represented by the entry
- * @param sg If not NULL, the size group to which the entry will be added
+ * Add a new entry representing a string preference
*
- * @return An hbox containing both the label and the entry. Can be used to set
+ * Returns: An hbox containing both the label and the entry. Can be used to set
* the widgets to sensitive or insensitive based on the value of a
* checkbox.
*/
@@ -81,53 +93,58 @@ GtkWidget *pidgin_prefs_labeled_entry(GtkWidget *page, const gchar *title,
const char *key, GtkSizeGroup *sg);
/**
+ * pidgin_prefs_labeled_password:
+ * @page: The page to which the entry will be added
+ * @title: The text to be displayed as the entry label
+ * @key: The key of the string pref that will be represented by the entry
+ * @sg: If not NULL, the size group to which the entry will be added
+ *
* Add a new entry representing a password (string) preference
* The entry will use a password-style text entry (the text is substituded)
*
- * @param page The page to which the entry will be added
- * @param title The text to be displayed as the entry label
- * @param key The key of the string pref that will be represented by the entry
- * @param sg If not NULL, the size group to which the entry will be added
- *
- * @return An hbox containing both the label and the entry. Can be used to set
+ * Returns: An hbox containing both the label and the entry. Can be used to set
* the widgets to sensitive or insensitive based on the value of a
* checkbox.
- *
- * @since 2.6.0
*/
GtkWidget *pidgin_prefs_labeled_password(GtkWidget *page, const gchar *title,
const char *key, GtkSizeGroup *sg);
/**
- * Add a new dropdown representing a preference of the specified type
- *
- * @param page The page to which the dropdown will be added
- * @param title The text to be displayed as the dropdown label
- * @param type The type of preference to be stored in the generated dropdown
- * @param key The key of the pref that will be represented by the dropdown
- * @param ... The choices to be added to the dropdown, choices should be
+ * pidgin_prefs_dropdown:
+ * @page: The page to which the dropdown will be added
+ * @title: The text to be displayed as the dropdown label
+ * @type: The type of preference to be stored in the generated dropdown
+ * @key: The key of the pref that will be represented by the dropdown
+ * @...: The choices to be added to the dropdown, choices should be
* paired as label/value
+ *
+ * Add a new dropdown representing a preference of the specified type
*/
GtkWidget *pidgin_prefs_dropdown(GtkWidget *page, const gchar *title,
PurplePrefType type, const char *key, ...);
/**
- * Add a new dropdown representing a preference of the specified type
- *
- * @param page The page to which the dropdown will be added
- * @param title The text to be displayed as the dropdown label
- * @param type The type of preference to be stored in the dropdown
- * @param key The key of the pref that will be represented by the dropdown
- * @param menuitems The choices to be added to the dropdown, choices should
+ * pidgin_prefs_dropdown_from_list:
+ * @page: The page to which the dropdown will be added
+ * @title: The text to be displayed as the dropdown label
+ * @type: The type of preference to be stored in the dropdown
+ * @key: The key of the pref that will be represented by the dropdown
+ * @menuitems: The choices to be added to the dropdown, choices should
* be paired as label/value
+ *
+ * Add a new dropdown representing a preference of the specified type
*/
GtkWidget *pidgin_prefs_dropdown_from_list(GtkWidget *page,
const gchar * title, PurplePrefType type, const char *key,
GList *menuitems);
/**
+ * pidgin_prefs_update_old:
+ *
* Rename legacy prefs and delete some that no longer exist.
*/
void pidgin_prefs_update_old(void);
+G_END_DECLS
+
#endif /* _PIDGINPREFS_H_ */
diff --git a/pidgin/gtkprivacy.c b/pidgin/gtkprivacy.c
index 78f7278f13..6bf0b3e7ab 100644
--- a/pidgin/gtkprivacy.c
+++ b/pidgin/gtkprivacy.c
@@ -1,8 +1,3 @@
-/**
- * @file gtkprivacy.c GTK+ Privacy UI
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -28,14 +23,16 @@
#include "connection.h"
#include "debug.h"
-#include "privacy.h"
#include "request.h"
#include "util.h"
+#include "gtkaccount.h"
#include "gtkblist.h"
#include "gtkprivacy.h"
#include "gtkutils.h"
+#include "gtk3compat.h"
+
typedef struct
{
GtkWidget *win;
@@ -74,15 +71,15 @@ typedef struct
static struct
{
const char *text;
- int num;
+ PurpleAccountPrivacyType type;
} const menu_entries[] =
{
- { N_("Allow all users to contact me"), PURPLE_PRIVACY_ALLOW_ALL },
- { N_("Allow only the users on my buddy list"), PURPLE_PRIVACY_ALLOW_BUDDYLIST },
- { N_("Allow only the users below"), PURPLE_PRIVACY_ALLOW_USERS },
- { N_("Block all users"), PURPLE_PRIVACY_DENY_ALL },
- { N_("Block only the users below"), PURPLE_PRIVACY_DENY_USERS }
+ { N_("Allow all users to contact me"), PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL },
+ { N_("Allow only the users on my buddy list"), PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST },
+ { N_("Allow only the users below"), PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS },
+ { N_("Block all users"), PURPLE_ACCOUNT_PRIVACY_DENY_ALL },
+ { N_("Block only the users below"), PURPLE_ACCOUNT_PRIVACY_DENY_USERS }
};
static const size_t menu_entry_count = sizeof(menu_entries) / sizeof(*menu_entries);
@@ -97,7 +94,7 @@ rebuild_allow_list(PidginPrivacyDialog *dialog)
gtk_list_store_clear(dialog->allow_store);
- for (l = dialog->account->permit; l != NULL; l = l->next) {
+ for (l = purple_account_privacy_get_permitted(dialog->account); l != NULL; l = l->next) {
gtk_list_store_append(dialog->allow_store, &iter);
gtk_list_store_set(dialog->allow_store, &iter, 0, l->data, -1);
}
@@ -111,7 +108,7 @@ rebuild_block_list(PidginPrivacyDialog *dialog)
gtk_list_store_clear(dialog->block_store);
- for (l = dialog->account->deny; l != NULL; l = l->next) {
+ for (l = purple_account_privacy_get_denied(dialog->account); l != NULL; l = l->next) {
gtk_list_store_append(dialog->block_store, &iter);
gtk_list_store_set(dialog->block_store, &iter, 0, l->data, -1);
}
@@ -206,12 +203,12 @@ static void
select_account_cb(GtkWidget *dropdown, PurpleAccount *account,
PidginPrivacyDialog *dialog)
{
- int i;
+ gsize i;
dialog->account = account;
for (i = 0; i < menu_entry_count; i++) {
- if (menu_entries[i].num == account->perm_deny) {
+ if (menu_entries[i].type == purple_account_get_privacy_type(account)) {
gtk_combo_box_set_active(GTK_COMBO_BOX(dialog->type_menu), i);
break;
}
@@ -228,21 +225,22 @@ select_account_cb(GtkWidget *dropdown, PurpleAccount *account,
static void
type_changed_cb(GtkComboBox *combo, PidginPrivacyDialog *dialog)
{
- int new_type = menu_entries[gtk_combo_box_get_active(combo)].num;
+ PurpleAccountPrivacyType new_type =
+ menu_entries[gtk_combo_box_get_active(combo)].type;
- dialog->account->perm_deny = new_type;
- serv_set_permit_deny(purple_account_get_connection(dialog->account));
+ purple_account_set_privacy_type(dialog->account, new_type);
+ purple_serv_set_permit_deny(purple_account_get_connection(dialog->account));
gtk_widget_hide(dialog->allow_widget);
gtk_widget_hide(dialog->block_widget);
- gtk_widget_hide_all(dialog->button_box);
+ gtk_widget_hide(dialog->button_box);
- if (new_type == PURPLE_PRIVACY_ALLOW_USERS) {
+ if (new_type == PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS) {
gtk_widget_show(dialog->allow_widget);
gtk_widget_show_all(dialog->button_box);
dialog->in_allow_list = TRUE;
}
- else if (new_type == PURPLE_PRIVACY_DENY_USERS) {
+ else if (new_type == PURPLE_ACCOUNT_PRIVACY_DENY_USERS) {
gtk_widget_show(dialog->block_widget);
gtk_widget_show_all(dialog->button_box);
dialog->in_allow_list = FALSE;
@@ -252,7 +250,7 @@ type_changed_cb(GtkComboBox *combo, PidginPrivacyDialog *dialog)
gtk_widget_show(dialog->button_box);
purple_blist_schedule_save();
- pidgin_blist_refresh(purple_get_blist());
+ pidgin_blist_refresh(purple_blist_get_buddy_list());
}
static void
@@ -293,9 +291,9 @@ remove_cb(GtkWidget *button, PidginPrivacyDialog *dialog)
return;
if (dialog->in_allow_list)
- purple_privacy_permit_remove(dialog->account, name, FALSE);
+ purple_account_privacy_permit_remove(dialog->account, name, FALSE);
else
- purple_privacy_deny_remove(dialog->account, name, FALSE);
+ purple_account_privacy_deny_remove(dialog->account, name, FALSE);
g_free(name);
}
@@ -305,17 +303,17 @@ removeall_cb(GtkWidget *button, PidginPrivacyDialog *dialog)
{
GSList *l;
if (dialog->in_allow_list)
- l = dialog->account->permit;
+ l = purple_account_privacy_get_permitted(dialog->account);
else
- l = dialog->account->deny;
+ l = purple_account_privacy_get_denied(dialog->account);
while (l) {
char *user;
user = l->data;
l = l->next;
if (dialog->in_allow_list)
- purple_privacy_permit_remove(dialog->account, user, FALSE);
+ purple_account_privacy_permit_remove(dialog->account, user, FALSE);
else
- purple_privacy_deny_remove(dialog->account, user, FALSE);
+ purple_account_privacy_deny_remove(dialog->account, user, FALSE);
}
}
@@ -335,8 +333,8 @@ privacy_dialog_new(void)
GtkWidget *button;
GtkWidget *dropdown;
GtkWidget *label;
- int selected = -1;
- int i;
+ gssize selected = -1;
+ gsize i;
dialog = g_new0(PidginPrivacyDialog, 1);
@@ -363,15 +361,15 @@ privacy_dialog_new(void)
dialog->account = pidgin_account_option_menu_get_selected(dropdown);
/* Add the drop-down list with the allow/block types. */
- dialog->type_menu = gtk_combo_box_new_text();
+ dialog->type_menu = gtk_combo_box_text_new();
gtk_box_pack_start(GTK_BOX(vbox), dialog->type_menu, FALSE, FALSE, 0);
gtk_widget_show(dialog->type_menu);
for (i = 0; i < menu_entry_count; i++) {
- gtk_combo_box_append_text(GTK_COMBO_BOX(dialog->type_menu),
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(dialog->type_menu),
_(menu_entries[i].text));
- if (menu_entries[i].num == dialog->account->perm_deny)
+ if (menu_entries[i].type == purple_account_get_privacy_type(dialog->account))
selected = i;
}
@@ -411,12 +409,12 @@ privacy_dialog_new(void)
type_changed_cb(GTK_COMBO_BOX(dialog->type_menu), dialog);
#if 0
- if (dialog->account->perm_deny == PURPLE_PRIVACY_ALLOW_USERS) {
+ if (purple_account_get_privacy_type(dialog->account) == PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS) {
gtk_widget_show(dialog->allow_widget);
gtk_widget_show(dialog->button_box);
dialog->in_allow_list = TRUE;
}
- else if (dialog->account->perm_deny == PURPLE_PRIVACY_DENY_USERS) {
+ else if (purple_account_get_privacy_type(dialog->account) == PURPLE_ACCOUNT_PRIVACY_DENY_USERS) {
gtk_widget_show(dialog->block_widget);
gtk_widget_show(dialog->button_box);
dialog->in_allow_list = FALSE;
@@ -434,7 +432,7 @@ pidgin_privacy_dialog_show(void)
privacy_dialog = privacy_dialog_new();
gtk_widget_show(privacy_dialog->win);
- gdk_window_raise(privacy_dialog->win->window);
+ gdk_window_raise(gtk_widget_get_window(privacy_dialog->win));
}
void
@@ -460,9 +458,9 @@ static void
confirm_permit_block_cb(PidginPrivacyRequestData *data, int option)
{
if (data->block)
- purple_privacy_deny(data->account, data->name, FALSE, FALSE);
+ purple_account_privacy_deny(data->account, data->name);
else
- purple_privacy_allow(data->account, data->name, FALSE, FALSE);
+ purple_account_privacy_allow(data->account, data->name);
destroy_request_data(data);
}
@@ -495,7 +493,7 @@ pidgin_request_add_permit(PurpleAccount *account, const char *name)
NULL, FALSE, FALSE, NULL,
_("_Permit"), G_CALLBACK(add_permit_block_cb),
_("Cancel"), G_CALLBACK(destroy_request_data),
- account, name, NULL,
+ purple_request_cpar_from_account(account),
data);
}
else {
@@ -505,12 +503,11 @@ pidgin_request_add_permit(PurpleAccount *account, const char *name)
"%s to contact you?"), name);
- purple_request_action(account, _("Permit User"), primary, secondary,
- 0,
- account, name, NULL,
- data, 2,
- _("_Permit"), G_CALLBACK(confirm_permit_block_cb),
- _("Cancel"), G_CALLBACK(destroy_request_data));
+ purple_request_action(account, _("Permit User"), primary,
+ secondary, 0, purple_request_cpar_from_account(account),
+ data, 2,
+ _("_Permit"), G_CALLBACK(confirm_permit_block_cb),
+ _("Cancel"), G_CALLBACK(destroy_request_data));
g_free(primary);
g_free(secondary);
@@ -536,7 +533,7 @@ pidgin_request_add_block(PurpleAccount *account, const char *name)
NULL, FALSE, FALSE, NULL,
_("_Block"), G_CALLBACK(add_permit_block_cb),
_("Cancel"), G_CALLBACK(destroy_request_data),
- account, name, NULL,
+ purple_request_cpar_from_account(account),
data);
}
else {
@@ -544,12 +541,11 @@ pidgin_request_add_block(PurpleAccount *account, const char *name)
char *secondary =
g_strdup_printf(_("Are you sure you want to block %s?"), name);
- purple_request_action(account, _("Block User"), primary, secondary,
- 0,
- account, name, NULL,
- data, 2,
- _("_Block"), G_CALLBACK(confirm_permit_block_cb),
- _("Cancel"), G_CALLBACK(destroy_request_data));
+ purple_request_action(account, _("Block User"), primary,
+ secondary, 0, purple_request_cpar_from_account(account),
+ data, 2,
+ _("_Block"), G_CALLBACK(confirm_permit_block_cb),
+ _("Cancel"), G_CALLBACK(destroy_request_data));
g_free(primary);
g_free(secondary);
@@ -570,25 +566,11 @@ pidgin_deny_added_removed(PurpleAccount *account, const char *name)
rebuild_block_list(privacy_dialog);
}
-static PurplePrivacyUiOps privacy_ops =
-{
- pidgin_permit_added_removed,
- pidgin_permit_added_removed,
- pidgin_deny_added_removed,
- pidgin_deny_added_removed,
- NULL,
- NULL,
- NULL,
- NULL
-};
-
-PurplePrivacyUiOps *
-pidgin_privacy_get_ui_ops(void)
-{
- return &privacy_ops;
-}
-
void
pidgin_privacy_init(void)
{
+ PurpleAccountUiOps *ops = pidgin_accounts_get_ui_ops();
+
+ ops->permit_added = ops->permit_removed = pidgin_permit_added_removed;
+ ops->deny_added = ops->deny_removed = pidgin_deny_added_removed;
}
diff --git a/pidgin/gtkprivacy.h b/pidgin/gtkprivacy.h
index 0be6a05483..9317f80fdd 100644
--- a/pidgin/gtkprivacy.h
+++ b/pidgin/gtkprivacy.h
@@ -1,8 +1,3 @@
-/**
- * @file gtkprivacy.h GTK+ Privacy UI
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -23,53 +18,65 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _PIDGINPRIVACY_H_
#define _PIDGINPRIVACY_H_
+/**
+ * SECTION:gtkprivacy
+ * @section_id: pidgin-gtkprivacy
+ * @short_description: <filename>gtkprivacy.h</filename>
+ * @title: Privacy UI
+ */
+
+#include "account.h"
-#include "privacy.h"
+G_BEGIN_DECLS
/**
+ * pidgin_privacy_init:
+ *
* Initializes the GTK+ privacy subsystem.
*/
void pidgin_privacy_init(void);
/**
+ * pidgin_privacy_dialog_show:
+ *
* Shows the privacy dialog.
*/
void pidgin_privacy_dialog_show(void);
/**
+ * pidgin_privacy_dialog_hide:
+ *
* Hides the privacy dialog.
*/
void pidgin_privacy_dialog_hide(void);
/**
+ * pidgin_request_add_permit:
+ * @account: The account.
+ * @name: The name of the user to add.
+ *
* Requests confirmation to add a user to the allow list for an account,
* and then adds it.
*
- * If @a name is not specified, an input dialog will be presented.
- *
- * @param account The account.
- * @param name The name of the user to add.
+ * If @name is not specified, an input dialog will be presented.
*/
void pidgin_request_add_permit(PurpleAccount *account, const char *name);
/**
+ * pidgin_request_add_block:
+ * @account: The account.
+ * @name: The name of the user to add.
+ *
* Requests confirmation to add a user to the block list for an account,
* and then adds it.
*
- * If @a name is not specified, an input dialog will be presented.
- *
- * @param account The account.
- * @param name The name of the user to add.
+ * If @name is not specified, an input dialog will be presented.
*/
void pidgin_request_add_block(PurpleAccount *account, const char *name);
-/**
- * Returns the UI operations structure for the GTK+ privacy subsystem.
- *
- * @return The GTK+ UI privacy operations structure.
- */
-PurplePrivacyUiOps *pidgin_privacy_get_ui_ops(void);
+G_END_DECLS
#endif /* _PIDGINPRIVACY_H_ */
diff --git a/pidgin/gtkrequest.c b/pidgin/gtkrequest.c
index b6d56dccb0..b278ab1bc1 100644
--- a/pidgin/gtkrequest.c
+++ b/pidgin/gtkrequest.c
@@ -1,8 +1,3 @@
-/**
- * @file gtkrequest.c GTK+ Request API
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -30,16 +25,24 @@
#include "prefs.h"
#include "util.h"
-#include "gtkimhtml.h"
-#include "gtkimhtmltoolbar.h"
+#include "gtkwebview.h"
#include "gtkrequest.h"
#include "gtkutils.h"
#include "pidginstock.h"
#include "gtkblist.h"
+#include "gtkinternal.h"
#include <gdk/gdkkeysyms.h>
-static GtkWidget * create_account_field(PurpleRequestField *field);
+#ifdef ENABLE_GCR
+#define GCR_API_SUBJECT_TO_CHANGE
+#include <gcr/gcr.h>
+#if !GTK_CHECK_VERSION(3,0,0)
+#include <gcr/gcr-simple-certificate.h>
+#endif
+#endif
+
+#include "gtk3compat.h"
typedef struct
{
@@ -57,6 +60,11 @@ typedef struct
{
struct
{
+ GtkProgressBar *progress_bar;
+ } wait;
+
+ struct
+ {
GtkWidget *entry;
gboolean multiline;
@@ -81,12 +89,15 @@ typedef struct
} PidginRequestData;
+static GHashTable *datasheet_stock = NULL;
+
+static GtkWidget * create_account_field(PurpleRequestField *field);
+
static void
pidgin_widget_decorate_account(GtkWidget *cont, PurpleAccount *account)
{
GtkWidget *image;
GdkPixbuf *pixbuf;
- GtkTooltips *tips;
if (!account)
return;
@@ -95,12 +106,13 @@ pidgin_widget_decorate_account(GtkWidget *cont, PurpleAccount *account)
image = gtk_image_new_from_pixbuf(pixbuf);
g_object_unref(G_OBJECT(pixbuf));
- tips = gtk_tooltips_new();
- gtk_tooltips_set_tip(tips, image, purple_account_get_username(account), NULL);
+ gtk_widget_set_tooltip_text(image, purple_account_get_username(account));
if (GTK_IS_DIALOG(cont)) {
- gtk_box_pack_start(GTK_BOX(GTK_DIALOG(cont)->action_area), image, FALSE, TRUE, 0);
- gtk_box_reorder_child(GTK_BOX(GTK_DIALOG(cont)->action_area), image, 0);
+ gtk_box_pack_start(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(cont))),
+ image, FALSE, TRUE, 0);
+ gtk_box_reorder_child(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(cont))),
+ image, 0);
} else if (GTK_IS_HBOX(cont)) {
gtk_misc_set_alignment(GTK_MISC(image), 0, 0);
gtk_box_pack_end(GTK_BOX(cont), image, FALSE, TRUE, 0);
@@ -115,6 +127,10 @@ generic_response_start(PidginRequestData *data)
/* Tell the user we're doing something. */
pidgin_set_cursor(GTK_WIDGET(data->dialog), GDK_WATCH);
+
+ g_object_set_data(G_OBJECT(data->dialog),
+ "pidgin-window-is-closing", GINT_TO_POINTER(TRUE));
+ gtk_widget_set_visible(GTK_WIDGET(data->dialog), FALSE);
}
static void
@@ -134,7 +150,7 @@ input_response_cb(GtkDialog *dialog, gint id, PidginRequestData *data)
gtk_text_buffer_get_end_iter(buffer, &end_iter);
if ((data->u.input.hint != NULL) && (!strcmp(data->u.input.hint, "html")))
- multiline_value = gtk_imhtml_get_markup(GTK_IMHTML(data->u.input.entry));
+ multiline_value = pidgin_webview_get_body_html(PIDGIN_WEBVIEW(data->u.input.entry));
else
multiline_value = gtk_text_buffer_get_text(buffer, &start_iter, &end_iter,
FALSE);
@@ -144,7 +160,7 @@ input_response_cb(GtkDialog *dialog, gint id, PidginRequestData *data)
else
value = gtk_entry_get_text(GTK_ENTRY(data->u.input.entry));
- if (id < data->cb_count && data->cbs[id] != NULL)
+ if (id >= 0 && (gsize)id < data->cb_count && data->cbs[id] != NULL)
((PurpleRequestInputCb)data->cbs[id])(data->user_data, value);
else if (data->cbs[1] != NULL)
((PurpleRequestInputCb)data->cbs[1])(data->user_data, value);
@@ -160,7 +176,7 @@ action_response_cb(GtkDialog *dialog, gint id, PidginRequestData *data)
{
generic_response_start(data);
- if (id < data->cb_count && data->cbs[id] != NULL)
+ if (id >= 0 && (gsize)id < data->cb_count && data->cbs[id] != NULL)
((PurpleRequestActionCb)data->cbs[id])(data->user_data, id);
purple_request_close(PURPLE_REQUEST_INPUT, data);
@@ -175,10 +191,10 @@ choice_response_cb(GtkDialog *dialog, gint id, PidginRequestData *data)
generic_response_start(data);
- if (id < data->cb_count && data->cbs[id] != NULL)
+ if (id >= 0 && (gsize)id < data->cb_count && data->cbs[id] != NULL)
while (group) {
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(group->data))) {
- ((PurpleRequestChoiceCb)data->cbs[id])(data->user_data, GPOINTER_TO_INT(g_object_get_data(G_OBJECT(group->data), "choice_id")));
+ ((PurpleRequestChoiceCb)data->cbs[id])(data->user_data, g_object_get_data(G_OBJECT(group->data), "choice_value"));
break;
}
group = group->next;
@@ -213,16 +229,6 @@ field_string_focus_out_cb(GtkWidget *entry, GdkEventFocus *event,
return FALSE;
}
-static gboolean
-field_int_focus_out_cb(GtkEntry *entry, GdkEventFocus *event,
- PurpleRequestField *field)
-{
- purple_request_field_int_set_value(field,
- atoi(gtk_entry_get_text(entry)));
-
- return FALSE;
-}
-
static void
field_bool_cb(GtkToggleButton *button, PurpleRequestField *field)
{
@@ -233,17 +239,32 @@ field_bool_cb(GtkToggleButton *button, PurpleRequestField *field)
static void
field_choice_menu_cb(GtkComboBox *menu, PurpleRequestField *field)
{
- purple_request_field_choice_set_value(field,
- gtk_combo_box_get_active(menu));
+ int active = gtk_combo_box_get_active(menu);
+ gpointer *values = g_object_get_data(G_OBJECT(menu), "values");
+
+ g_return_if_fail(values != NULL);
+ g_return_if_fail(active >= 0);
+
+ purple_request_field_choice_set_value(field, values[active]);
}
static void
field_choice_option_cb(GtkRadioButton *button, PurpleRequestField *field)
{
- if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
- purple_request_field_choice_set_value(field,
- (g_slist_length(gtk_radio_button_get_group(button)) -
- g_slist_index(gtk_radio_button_get_group(button), button)) - 1);
+ int active;
+ gpointer *values = g_object_get_data(G_OBJECT(g_object_get_data(
+ G_OBJECT(button), "box")), "values");
+
+ if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
+ return;
+
+ active = (g_slist_length(gtk_radio_button_get_group(button)) -
+ g_slist_index(gtk_radio_button_get_group(button), button)) - 1;
+
+ g_return_if_fail(values != NULL);
+ g_return_if_fail(active >= 0);
+
+ purple_request_field_choice_set_value(field, values[active]);
}
static void
@@ -257,7 +278,7 @@ multifield_ok_cb(GtkWidget *button, PidginRequestData *data)
{
generic_response_start(data);
- if (!GTK_WIDGET_HAS_FOCUS(button))
+ if (!gtk_widget_has_focus(button))
gtk_widget_grab_focus(button);
if (data->cbs[0] != NULL)
@@ -279,6 +300,21 @@ multifield_cancel_cb(GtkWidget *button, PidginRequestData *data)
purple_request_close(PURPLE_REQUEST_FIELDS, data);
}
+static void
+multifield_extra_cb(GtkWidget *button, PidginRequestData *data)
+{
+ PurpleRequestFieldsCb cb;
+
+ generic_response_start(data);
+
+ cb = g_object_get_data(G_OBJECT(button), "extra-cb");
+
+ if (cb != NULL)
+ cb(data->user_data, data->u.multifield.fields);
+
+ purple_request_close(PURPLE_REQUEST_FIELDS, data);
+}
+
static gboolean
destroy_multifield_cb(GtkWidget *dialog, GdkEvent *event,
PidginRequestData *data)
@@ -289,44 +325,208 @@ destroy_multifield_cb(GtkWidget *dialog, GdkEvent *event,
#define STOCK_ITEMIZE(r, l) \
- if (!strcmp((r), text)) \
+ if (!strcmp((r), text) || !strcmp(_(r), text)) \
return (l);
static const char *
text_to_stock(const char *text)
{
- STOCK_ITEMIZE(_("Yes"), GTK_STOCK_YES);
- STOCK_ITEMIZE(_("No"), GTK_STOCK_NO);
- STOCK_ITEMIZE(_("OK"), GTK_STOCK_OK);
- STOCK_ITEMIZE(_("Cancel"), GTK_STOCK_CANCEL);
- STOCK_ITEMIZE(_("Apply"), GTK_STOCK_APPLY);
- STOCK_ITEMIZE(_("Close"), GTK_STOCK_CLOSE);
- STOCK_ITEMIZE(_("Delete"), GTK_STOCK_DELETE);
- STOCK_ITEMIZE(_("Add"), GTK_STOCK_ADD);
- STOCK_ITEMIZE(_("Remove"), GTK_STOCK_REMOVE);
- STOCK_ITEMIZE(_("Save"), GTK_STOCK_SAVE);
- STOCK_ITEMIZE(_("Alias"), PIDGIN_STOCK_ALIAS);
+ STOCK_ITEMIZE(N_("Yes"), GTK_STOCK_YES);
+ STOCK_ITEMIZE(N_("_Yes"), GTK_STOCK_YES);
+ STOCK_ITEMIZE(N_("No"), GTK_STOCK_NO);
+ STOCK_ITEMIZE(N_("_No"), GTK_STOCK_NO);
+ STOCK_ITEMIZE(N_("OK"), GTK_STOCK_OK);
+ STOCK_ITEMIZE(N_("_OK"), GTK_STOCK_OK);
+ STOCK_ITEMIZE(N_("Cancel"), GTK_STOCK_CANCEL);
+ STOCK_ITEMIZE(N_("_Cancel"), GTK_STOCK_CANCEL);
+ STOCK_ITEMIZE(N_("Apply"), GTK_STOCK_APPLY);
+ STOCK_ITEMIZE(N_("Close"), GTK_STOCK_CLOSE);
+ STOCK_ITEMIZE(N_("Delete"), GTK_STOCK_DELETE);
+ STOCK_ITEMIZE(N_("Add"), GTK_STOCK_ADD);
+ STOCK_ITEMIZE(N_("Remove"), GTK_STOCK_REMOVE);
+ STOCK_ITEMIZE(N_("Save"), GTK_STOCK_SAVE);
+ STOCK_ITEMIZE(N_("Next"), PIDGIN_STOCK_NEXT);
+ STOCK_ITEMIZE(N_("_Next"), PIDGIN_STOCK_NEXT);
+ STOCK_ITEMIZE(N_("Back"), GTK_STOCK_GO_BACK);
+ STOCK_ITEMIZE(N_("_Back"), GTK_STOCK_GO_BACK);
+ STOCK_ITEMIZE(N_("Alias"), PIDGIN_STOCK_ALIAS);
return text;
}
+#undef STOCK_ITEMIZE
+
+static gchar *
+pidgin_request_escape(PurpleRequestCommonParameters *cpar, const gchar *text)
+{
+ if (text == NULL)
+ return NULL;
+
+ if (purple_request_cpar_is_html(cpar)) {
+ gboolean valid;
+
+ valid = pango_parse_markup(text, -1, 0, NULL, NULL, NULL, NULL);
+
+ if (valid)
+ return g_strdup(text);
+ else {
+ purple_debug_error("pidgin", "Passed label text is not "
+ "a valid markup. Falling back to plain text.");
+ }
+ }
+
+ return g_markup_escape_text(text, -1);
+}
+
+static GtkWidget *
+pidgin_request_dialog_icon(PurpleRequestType dialog_type,
+ PurpleRequestCommonParameters *cpar)
+{
+ GtkWidget *img = NULL;
+ PurpleRequestIconType icon_type;
+ gconstpointer icon_data;
+ gsize icon_size;
+ const gchar *icon_stock = PIDGIN_STOCK_DIALOG_QUESTION;
+
+ /* Dialog icon. */
+ icon_data = purple_request_cpar_get_custom_icon(cpar, &icon_size);
+ if (icon_data) {
+ GdkPixbuf *pixbuf;
+
+ pixbuf = pidgin_pixbuf_from_data(icon_data, icon_size);
+ if (pixbuf) {
+ /* scale the image if it is too large */
+ int width = gdk_pixbuf_get_width(pixbuf);
+ int height = gdk_pixbuf_get_height(pixbuf);
+ if (width > 128 || height > 128) {
+ int scaled_width = width > height ?
+ 128 : (128 * width) / height;
+ int scaled_height = height > width ?
+ 128 : (128 * height) / width;
+ GdkPixbuf *scaled;
+
+ purple_debug_info("pidgin", "dialog icon was "
+ "too large, scaling it down");
+
+ scaled = gdk_pixbuf_scale_simple(pixbuf,
+ scaled_width, scaled_height,
+ GDK_INTERP_BILINEAR);
+ if (scaled) {
+ g_object_unref(pixbuf);
+ pixbuf = scaled;
+ }
+ }
+ img = gtk_image_new_from_pixbuf(pixbuf);
+ g_object_unref(pixbuf);
+ } else {
+ purple_debug_info("pidgin",
+ "failed to parse dialog icon");
+ }
+ }
+
+ if (img)
+ return img;
+
+ icon_type = purple_request_cpar_get_icon(cpar);
+ switch (icon_type)
+ {
+ case PURPLE_REQUEST_ICON_DEFAULT:
+ icon_stock = NULL;
+ break;
+ case PURPLE_REQUEST_ICON_REQUEST:
+ icon_stock = PIDGIN_STOCK_DIALOG_QUESTION;
+ break;
+ case PURPLE_REQUEST_ICON_DIALOG:
+ case PURPLE_REQUEST_ICON_INFO:
+ case PURPLE_REQUEST_ICON_WAIT: /* TODO: we need another icon */
+ icon_stock = PIDGIN_STOCK_DIALOG_INFO;
+ break;
+ case PURPLE_REQUEST_ICON_WARNING:
+ icon_stock = PIDGIN_STOCK_DIALOG_WARNING;
+ break;
+ case PURPLE_REQUEST_ICON_ERROR:
+ icon_stock = PIDGIN_STOCK_DIALOG_ERROR;
+ break;
+ /* intentionally no default value */
+ }
+
+ if (icon_stock == NULL) {
+ switch (dialog_type) {
+ case PURPLE_REQUEST_INPUT:
+ case PURPLE_REQUEST_CHOICE:
+ case PURPLE_REQUEST_ACTION:
+ case PURPLE_REQUEST_FIELDS:
+ case PURPLE_REQUEST_FILE:
+ case PURPLE_REQUEST_FOLDER:
+ icon_stock = PIDGIN_STOCK_DIALOG_QUESTION;
+ break;
+ case PURPLE_REQUEST_WAIT:
+ icon_stock = PIDGIN_STOCK_DIALOG_INFO;
+ break;
+ /* intentionally no default value */
+ }
+ }
+
+ img = gtk_image_new_from_stock(icon_stock,
+ gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
+
+ if (img || icon_type == PURPLE_REQUEST_ICON_REQUEST)
+ return img;
+
+ return gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION,
+ gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
+}
+
+static void
+pidgin_request_help_clicked(GtkButton *button, gpointer _unused)
+{
+ PurpleRequestHelpCb cb;
+ gpointer data;
+
+ cb = g_object_get_data(G_OBJECT(button), "pidgin-help-cb");
+ data = g_object_get_data(G_OBJECT(button), "pidgin-help-data");
+
+ g_return_if_fail(cb != NULL);
+ cb(data);
+}
+
+static void
+pidgin_request_add_help(GtkDialog *dialog, PurpleRequestCommonParameters *cpar)
+{
+ GtkWidget *button;
+ PurpleRequestHelpCb help_cb;
+ gpointer help_data;
+
+ help_cb = purple_request_cpar_get_help_cb(cpar, &help_data);
+ if (help_cb == NULL)
+ return;
+
+ button = gtk_dialog_add_button(dialog, GTK_STOCK_HELP,
+ GTK_RESPONSE_HELP);
+
+ g_object_set_data(G_OBJECT(button), "pidgin-help-cb", help_cb);
+ g_object_set_data(G_OBJECT(button), "pidgin-help-data", help_data);
+
+ g_signal_connect(G_OBJECT(button), "clicked",
+ G_CALLBACK(pidgin_request_help_clicked), NULL);
+}
+
static void *
pidgin_request_input(const char *title, const char *primary,
const char *secondary, const char *default_value,
gboolean multiline, gboolean masked, gchar *hint,
const char *ok_text, GCallback ok_cb,
const char *cancel_text, GCallback cancel_cb,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
+ PurpleRequestCommonParameters *cpar,
void *user_data)
{
PidginRequestData *data;
GtkWidget *dialog;
GtkWidget *vbox;
GtkWidget *hbox;
- GtkWidget *label;
+ GtkLabel *label;
GtkWidget *entry;
GtkWidget *img;
- GtkWidget *toolbar;
char *label_text;
char *primary_esc, *secondary_esc;
@@ -353,33 +553,36 @@ pidgin_request_input(const char *title, const char *primary,
/* Setup the dialog */
gtk_container_set_border_width(GTK_CONTAINER(dialog), PIDGIN_HIG_BORDER/2);
- gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER/2);
+ gtk_container_set_border_width(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
+ PIDGIN_HIG_BORDER / 2);
if (!multiline)
gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
- gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
gtk_dialog_set_default_response(GTK_DIALOG(dialog), 0);
- gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER);
+ gtk_box_set_spacing(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
+ PIDGIN_HIG_BORDER);
/* Setup the main horizontal box */
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
- gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BORDER);
+ gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
+ hbox);
/* Dialog icon. */
- img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION,
- gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
+ img = pidgin_request_dialog_icon(PURPLE_REQUEST_INPUT, cpar);
gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
+ pidgin_request_add_help(GTK_DIALOG(dialog), cpar);
+
/* Vertical box */
- vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BORDER);
gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
- pidgin_widget_decorate_account(hbox, account);
+ pidgin_widget_decorate_account(hbox, purple_request_cpar_get_account(cpar));
/* Descriptive label */
- primary_esc = (primary != NULL) ? g_markup_escape_text(primary, -1) : NULL;
- secondary_esc = (secondary != NULL) ? g_markup_escape_text(secondary, -1) : NULL;
+ primary_esc = pidgin_request_escape(cpar, primary);
+ secondary_esc = pidgin_request_escape(cpar, secondary);
label_text = g_strdup_printf((primary ? "<span weight=\"bold\" size=\"larger\">"
"%s</span>%s%s" : "%s%s%s"),
(primary ? primary_esc : ""),
@@ -388,12 +591,12 @@ pidgin_request_input(const char *title, const char *primary,
g_free(primary_esc);
g_free(secondary_esc);
- label = gtk_label_new(NULL);
+ label = GTK_LABEL(gtk_label_new(NULL));
- gtk_label_set_markup(GTK_LABEL(label), label_text);
- gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+ gtk_label_set_markup(label, label_text);
+ gtk_label_set_line_wrap(label, TRUE);
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
- gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(label), FALSE, FALSE, 0);
g_free(label_text);
@@ -406,16 +609,14 @@ pidgin_request_input(const char *title, const char *primary,
if ((data->u.input.hint != NULL) && (!strcmp(data->u.input.hint, "html"))) {
GtkWidget *frame;
- /* imhtml */
- frame = pidgin_create_imhtml(TRUE, &entry, &toolbar, NULL);
+ /* webview */
+ frame = pidgin_create_webview(TRUE, &entry, NULL);
gtk_widget_set_size_request(entry, 320, 130);
- gtk_widget_set_name(entry, "pidgin_request_imhtml");
+ gtk_widget_set_name(entry, "pidgin_request_webview");
if (default_value != NULL)
- gtk_imhtml_append_text(GTK_IMHTML(entry), default_value, GTK_IMHTML_NO_SCROLL);
+ pidgin_webview_append_html(PIDGIN_WEBVIEW(entry), default_value);
gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
gtk_widget_show(frame);
-
- gtk_imhtml_set_return_inserts_newline(GTK_IMHTML(entry));
}
else {
if (multiline) {
@@ -432,9 +633,6 @@ pidgin_request_input(const char *title, const char *primary,
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(entry), GTK_WRAP_WORD_CHAR);
- if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/spellcheck"))
- pidgin_setup_gtkspell(GTK_TEXT_VIEW(entry));
-
gtk_box_pack_start(GTK_BOX(vbox),
pidgin_make_scrollable(entry, GTK_POLICY_NEVER, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, 320, 130),
TRUE, TRUE, 0);
@@ -452,16 +650,12 @@ pidgin_request_input(const char *title, const char *primary,
if (masked)
{
gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
-#if !GTK_CHECK_VERSION(2,16,0)
- if (gtk_entry_get_invisible_char(GTK_ENTRY(entry)) == '*')
- gtk_entry_set_invisible_char(GTK_ENTRY(entry), PIDGIN_INVISIBLE_CHAR);
-#endif /* Less than GTK+ 2.16 */
}
}
gtk_widget_show_all(vbox);
}
- pidgin_set_accessible_label (entry, label);
+ pidgin_set_accessible_label(entry, label);
data->u.input.entry = entry;
pidgin_auto_parent_window(dialog);
@@ -474,11 +668,9 @@ pidgin_request_input(const char *title, const char *primary,
static void *
pidgin_request_choice(const char *title, const char *primary,
- const char *secondary, int default_value,
- const char *ok_text, GCallback ok_cb,
- const char *cancel_text, GCallback cancel_cb,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
- void *user_data, va_list args)
+ const char *secondary, gpointer default_value, const char *ok_text,
+ GCallback ok_cb, const char *cancel_text, GCallback cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data, va_list args)
{
PidginRequestData *data;
GtkWidget *dialog;
@@ -520,30 +712,33 @@ pidgin_request_choice(const char *title, const char *primary,
/* Setup the dialog */
gtk_container_set_border_width(GTK_CONTAINER(dialog), PIDGIN_HIG_BORDER/2);
- gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER/2);
+ gtk_container_set_border_width(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
+ PIDGIN_HIG_BORDER / 2);
gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
- gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
- gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER);
+ gtk_box_set_spacing(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
+ PIDGIN_HIG_BORDER);
/* Setup the main horizontal box */
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
- gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BORDER);
+ gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
+ hbox);
/* Dialog icon. */
- img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION,
- gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
+ img = pidgin_request_dialog_icon(PURPLE_REQUEST_CHOICE, cpar);
gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
- pidgin_widget_decorate_account(hbox, account);
+ pidgin_widget_decorate_account(hbox, purple_request_cpar_get_account(cpar));
+
+ pidgin_request_add_help(GTK_DIALOG(dialog), cpar);
/* Vertical box */
- vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BORDER);
gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
/* Descriptive label */
- primary_esc = (primary != NULL) ? g_markup_escape_text(primary, -1) : NULL;
- secondary_esc = (secondary != NULL) ? g_markup_escape_text(secondary, -1) : NULL;
+ primary_esc = pidgin_request_escape(cpar, primary);
+ secondary_esc = pidgin_request_escape(cpar, secondary);
label_text = g_strdup_printf((primary ? "<span weight=\"bold\" size=\"larger\">"
"%s</span>%s%s" : "%s%s%s"),
(primary ? primary_esc : ""),
@@ -561,13 +756,13 @@ pidgin_request_choice(const char *title, const char *primary,
g_free(label_text);
- vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_start(GTK_BOX(vbox), vbox2, FALSE, FALSE, 0);
while ((radio_text = va_arg(args, char*))) {
- int resp = va_arg(args, int);
+ gpointer resp = va_arg(args, gpointer);
radio = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(radio), radio_text);
gtk_box_pack_start(GTK_BOX(vbox2), radio, FALSE, FALSE, 0);
- g_object_set_data(G_OBJECT(radio), "choice_id", GINT_TO_POINTER(resp));
+ g_object_set_data(G_OBJECT(radio), "choice_value", resp);
if (resp == default_value)
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE);
}
@@ -583,12 +778,10 @@ pidgin_request_choice(const char *title, const char *primary,
}
static void *
-pidgin_request_action_with_icon(const char *title, const char *primary,
- const char *secondary, int default_action,
- PurpleAccount *account, const char *who,
- PurpleConversation *conv, gconstpointer icon_data,
- gsize icon_size,
- void *user_data, size_t action_count, va_list actions)
+pidgin_request_action(const char *title, const char *primary,
+ const char *secondary, int default_action,
+ PurpleRequestCommonParameters *cpar, void *user_data,
+ size_t action_count, va_list actions)
{
PidginRequestData *data;
GtkWidget *dialog;
@@ -599,7 +792,7 @@ pidgin_request_action_with_icon(const char *title, const char *primary,
void **buttons;
char *label_text;
char *primary_esc, *secondary_esc;
- int i;
+ gsize i;
data = g_new0(PidginRequestData, 1);
data->type = PURPLE_REQUEST_ACTION;
@@ -642,59 +835,33 @@ pidgin_request_action_with_icon(const char *title, const char *primary,
/* Setup the dialog */
gtk_container_set_border_width(GTK_CONTAINER(dialog), PIDGIN_HIG_BORDER/2);
- gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER/2);
+ gtk_container_set_border_width(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
+ PIDGIN_HIG_BORDER / 2);
gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
- gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
- gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER);
+ gtk_box_set_spacing(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
+ PIDGIN_HIG_BORDER);
/* Setup the main horizontal box */
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
- gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
-
- /* Dialog icon. */
- if (icon_data) {
- GdkPixbuf *pixbuf = pidgin_pixbuf_from_data(icon_data, icon_size);
- if (pixbuf) {
- /* scale the image if it is too large */
- int width = gdk_pixbuf_get_width(pixbuf);
- int height = gdk_pixbuf_get_height(pixbuf);
- if (width > 128 || height > 128) {
- int scaled_width = width > height ? 128 : (128 * width) / height;
- int scaled_height = height > width ? 128 : (128 * height) / width;
- GdkPixbuf *scaled =
- gdk_pixbuf_scale_simple(pixbuf, scaled_width, scaled_height,
- GDK_INTERP_BILINEAR);
-
- purple_debug_info("pidgin",
- "dialog icon was too large, scaled it down\n");
- if (scaled) {
- g_object_unref(pixbuf);
- pixbuf = scaled;
- }
- }
- img = gtk_image_new_from_pixbuf(pixbuf);
- g_object_unref(pixbuf);
- } else {
- purple_debug_info("pidgin", "failed to parse dialog icon\n");
- }
- }
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BORDER);
+ gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
+ hbox);
- if (!img) {
- img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION,
- gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
- }
+ img = pidgin_request_dialog_icon(PURPLE_REQUEST_ACTION, cpar);
gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
/* Vertical box */
- vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BORDER);
gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
- pidgin_widget_decorate_account(hbox, account);
+ pidgin_widget_decorate_account(hbox,
+ purple_request_cpar_get_account(cpar));
+
+ pidgin_request_add_help(GTK_DIALOG(dialog), cpar);
/* Descriptive label */
- primary_esc = (primary != NULL) ? g_markup_escape_text(primary, -1) : NULL;
- secondary_esc = (secondary != NULL) ? g_markup_escape_text(secondary, -1) : NULL;
+ primary_esc = pidgin_request_escape(cpar, primary);
+ secondary_esc = pidgin_request_escape(cpar, secondary);
label_text = g_strdup_printf((primary ? "<span weight=\"bold\" size=\"larger\">"
"%s</span>%s%s" : "%s%s%s"),
(primary ? primary_esc : ""),
@@ -715,8 +882,8 @@ pidgin_request_action_with_icon(const char *title, const char *primary,
if (default_action == PURPLE_DEFAULT_ACTION_NONE) {
- GTK_WIDGET_SET_FLAGS(img, GTK_CAN_DEFAULT);
- GTK_WIDGET_SET_FLAGS(img, GTK_CAN_FOCUS);
+ gtk_widget_set_can_default(img, TRUE);
+ gtk_widget_set_can_focus(img, TRUE);
gtk_widget_grab_focus(img);
gtk_widget_grab_default(img);
} else
@@ -734,22 +901,146 @@ pidgin_request_action_with_icon(const char *title, const char *primary,
return data;
}
+static void
+wait_cancel_cb(GtkWidget *button, PidginRequestData *data)
+{
+ generic_response_start(data);
+
+ if (data->cbs[0] != NULL)
+ ((PurpleRequestCancelCb)data->cbs[0])(data->user_data);
+
+ purple_request_close(PURPLE_REQUEST_FIELDS, data);
+}
+
static void *
-pidgin_request_action(const char *title, const char *primary,
- const char *secondary, int default_action,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
- void *user_data, size_t action_count, va_list actions)
+pidgin_request_wait(const char *title, const char *primary,
+ const char *secondary, gboolean with_progress,
+ PurpleRequestCancelCb cancel_cb, PurpleRequestCommonParameters *cpar,
+ void *user_data)
+{
+ PidginRequestData *data;
+ GtkWidget *dialog;
+ GtkWidget *hbox, *vbox, *img, *label, *button;
+ gchar *primary_esc, *secondary_esc, *label_text;
+
+ data = g_new0(PidginRequestData, 1);
+ data->type = PURPLE_REQUEST_WAIT;
+ data->user_data = user_data;
+
+ data->cb_count = 1;
+ data->cbs = g_new0(GCallback, 1);
+ data->cbs[0] = (GCallback)cancel_cb;
+
+ data->dialog = dialog = gtk_dialog_new();
+
+ gtk_window_set_deletable(GTK_WINDOW(data->dialog), cancel_cb != NULL);
+
+ if (title != NULL)
+ gtk_window_set_title(GTK_WINDOW(dialog), title);
+ else
+ gtk_window_set_title(GTK_WINDOW(dialog), _("Please wait"));
+
+ /* Setup the dialog */
+ gtk_container_set_border_width(GTK_CONTAINER(dialog),
+ PIDGIN_HIG_BORDER / 2);
+ gtk_container_set_border_width(GTK_CONTAINER(
+ gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
+ PIDGIN_HIG_BORDER / 2);
+ gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
+ gtk_box_set_spacing(GTK_BOX(gtk_dialog_get_content_area(
+ GTK_DIALOG(dialog))), PIDGIN_HIG_BORDER);
+
+ /* Setup the main horizontal box */
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BORDER);
+ gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(
+ GTK_DIALOG(dialog))), hbox);
+
+ img = pidgin_request_dialog_icon(PURPLE_REQUEST_WAIT, cpar);
+ gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
+
+ /* Cancel button */
+ button = pidgin_dialog_add_button(GTK_DIALOG(dialog),
+ text_to_stock(_("Cancel")), G_CALLBACK(wait_cancel_cb), data);
+ gtk_widget_set_can_default(button, FALSE);
+
+ /* Vertical box */
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BORDER);
+ gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
+
+ pidgin_widget_decorate_account(hbox,
+ purple_request_cpar_get_account(cpar));
+
+ pidgin_request_add_help(GTK_DIALOG(dialog), cpar);
+
+ /* Descriptive label */
+ primary_esc = pidgin_request_escape(cpar, primary);
+ secondary_esc = pidgin_request_escape(cpar, secondary);
+ label_text = g_strdup_printf((primary ? "<span weight=\"bold\" "
+ "size=\"larger\">%s</span>%s%s" : "%s%s%s"),
+ (primary ? primary_esc : ""),
+ ((primary && secondary) ? "\n\n" : ""),
+ (secondary ? secondary_esc : ""));
+ g_free(primary_esc);
+ g_free(secondary_esc);
+
+ label = gtk_label_new(NULL);
+
+ gtk_label_set_markup(GTK_LABEL(label), label_text);
+ gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+ gtk_label_set_selectable(GTK_LABEL(label), FALSE);
+ gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
+
+ g_free(label_text);
+
+ if (with_progress) {
+ GtkProgressBar *bar;
+
+ bar = data->u.wait.progress_bar =
+ GTK_PROGRESS_BAR(gtk_progress_bar_new());
+ gtk_progress_bar_set_fraction(bar, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(bar),
+ FALSE, FALSE, 0);
+ }
+
+ /* Move focus out of cancel button. */
+ gtk_widget_set_can_default(img, TRUE);
+ gtk_widget_set_can_focus(img, TRUE);
+ gtk_widget_grab_focus(img);
+ gtk_widget_grab_default(img);
+
+ /* Show everything. */
+ pidgin_auto_parent_window(dialog);
+
+ gtk_widget_show_all(dialog);
+
+ return data;
+}
+
+static void
+pidgin_request_wait_update(void *ui_handle, gboolean pulse, gfloat fraction)
{
- return pidgin_request_action_with_icon(title, primary, secondary,
- default_action, account, who, conv, NULL, 0, user_data, action_count,
- actions);
+ GtkProgressBar *bar;
+ PidginRequestData *data = ui_handle;
+
+ g_return_if_fail(data->type == PURPLE_REQUEST_WAIT);
+
+ bar = data->u.wait.progress_bar;
+ if (pulse)
+ gtk_progress_bar_pulse(bar);
+ else
+ gtk_progress_bar_set_fraction(bar, fraction);
}
static void
req_entry_field_changed_cb(GtkWidget *entry, PurpleRequestField *field)
{
- PurpleRequestFieldGroup *group;
- PidginRequestData *req_data;
+ if (purple_request_field_get_field_type(field) == PURPLE_REQUEST_FIELD_INTEGER) {
+ int value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(entry));
+ purple_request_field_int_set_value(field, value);
+ return;
+ }
if (purple_request_field_string_is_multiline(field))
{
@@ -769,12 +1060,37 @@ req_entry_field_changed_cb(GtkWidget *entry, PurpleRequestField *field)
text = gtk_entry_get_text(GTK_ENTRY(entry));
purple_request_field_string_set_value(field, (*text == '\0') ? NULL : text);
}
+}
+
+static void
+req_field_changed_cb(GtkWidget *widget, PurpleRequestField *field)
+{
+ PurpleRequestFieldGroup *group;
+ PurpleRequestFields *fields;
+ PidginRequestData *req_data;
+ const GList *it;
group = purple_request_field_get_group(field);
- req_data = (PidginRequestData *)group->fields_list->ui_data;
+ fields = purple_request_field_group_get_fields_list(group);
+ req_data = purple_request_fields_get_ui_data(fields);
gtk_widget_set_sensitive(req_data->ok_button,
- purple_request_fields_all_required_filled(group->fields_list));
+ purple_request_fields_all_required_filled(fields) &&
+ purple_request_fields_all_valid(fields));
+
+ it = purple_request_fields_get_autosensitive(fields);
+ for (; it != NULL; it = g_list_next(it)) {
+ PurpleRequestField *field = it->data;
+ GtkWidget *widget = purple_request_field_get_ui_data(field);
+ gboolean sensitive;
+
+ sensitive = purple_request_field_is_sensitive(field);
+ gtk_widget_set_sensitive(widget, sensitive);
+
+ /* XXX: and what about multiline? */
+ if (GTK_IS_EDITABLE(widget))
+ gtk_editable_set_editable(GTK_EDITABLE(widget), sensitive);
+ }
}
static void
@@ -784,19 +1100,18 @@ setup_entry_field(GtkWidget *entry, PurpleRequestField *field)
gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
- if (purple_request_field_is_required(field))
- {
- g_signal_connect(G_OBJECT(entry), "changed",
- G_CALLBACK(req_entry_field_changed_cb), field);
- }
+ g_signal_connect(G_OBJECT(entry), "changed",
+ G_CALLBACK(req_entry_field_changed_cb), field);
+ g_signal_connect(G_OBJECT(entry), "changed",
+ G_CALLBACK(req_field_changed_cb), field);
- if ((type_hint = purple_request_field_get_type_hint(field)) != NULL)
+ if ((type_hint = purple_request_field_get_field_type_hint(field)) != NULL)
{
if (purple_str_has_prefix(type_hint, "screenname"))
{
GtkWidget *optmenu = NULL;
PurpleRequestFieldGroup *group = purple_request_field_get_group(field);
- GList *fields = group->fields;
+ GList *fields = purple_request_field_group_get_fields(group);
/* Ensure the account option menu is created (if the widget hasn't
* been initialized already) for username auto-completion. */
@@ -805,10 +1120,10 @@ setup_entry_field(GtkWidget *entry, PurpleRequestField *field)
PurpleRequestField *fld = fields->data;
fields = fields->next;
- if (purple_request_field_get_type(fld) == PURPLE_REQUEST_FIELD_ACCOUNT &&
+ if (purple_request_field_get_field_type(fld) == PURPLE_REQUEST_FIELD_ACCOUNT &&
purple_request_field_is_visible(fld))
{
- const char *type_hint = purple_request_field_get_type_hint(fld);
+ const char *type_hint = purple_request_field_get_field_type_hint(fld);
if (type_hint != NULL && strcmp(type_hint, "account") == 0)
{
optmenu = GTK_WIDGET(purple_request_field_get_ui_data(fld));
@@ -820,7 +1135,7 @@ setup_entry_field(GtkWidget *entry, PurpleRequestField *field)
}
}
}
- pidgin_setup_screenname_autocomplete_with_filter(entry, optmenu, pidgin_screenname_autocomplete_default_filter, GINT_TO_POINTER(!strcmp(type_hint, "screenname-all")));
+ pidgin_setup_screenname_autocomplete(entry, optmenu, pidgin_screenname_autocomplete_default_filter, GINT_TO_POINTER(!strcmp(type_hint, "screenname-all")));
}
}
}
@@ -830,8 +1145,10 @@ create_string_field(PurpleRequestField *field)
{
const char *value;
GtkWidget *widget;
+ gboolean is_editable;
value = purple_request_field_string_get_default_value(field);
+ is_editable = purple_request_field_is_sensitive(field);
if (purple_request_field_string_is_multiline(field))
{
@@ -843,9 +1160,6 @@ create_string_field(PurpleRequestField *field)
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview),
GTK_WRAP_WORD_CHAR);
- if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/spellcheck"))
- pidgin_setup_gtkspell(GTK_TEXT_VIEW(textview));
-
gtk_widget_show(textview);
if (value != NULL)
@@ -857,8 +1171,9 @@ create_string_field(PurpleRequestField *field)
gtk_text_buffer_set_text(buffer, value, -1);
}
- gtk_text_view_set_editable(GTK_TEXT_VIEW(textview),
- purple_request_field_string_is_editable(field));
+ gtk_widget_set_tooltip_text(textview, purple_request_field_get_tooltip(field));
+
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), is_editable);
g_signal_connect(G_OBJECT(textview), "focus-out-event",
G_CALLBACK(field_string_focus_out_cb), field);
@@ -881,17 +1196,14 @@ create_string_field(PurpleRequestField *field)
if (value != NULL)
gtk_entry_set_text(GTK_ENTRY(widget), value);
+ gtk_widget_set_tooltip_text(widget, purple_request_field_get_tooltip(field));
+
if (purple_request_field_string_is_masked(field))
{
gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE);
-#if !GTK_CHECK_VERSION(2,16,0)
- if (gtk_entry_get_invisible_char(GTK_ENTRY(widget)) == '*')
- gtk_entry_set_invisible_char(GTK_ENTRY(widget), PIDGIN_INVISIBLE_CHAR);
-#endif /* Less than GTK+ 2.16 */
}
- gtk_editable_set_editable(GTK_EDITABLE(widget),
- purple_request_field_string_is_editable(field));
+ gtk_editable_set_editable(GTK_EDITABLE(widget), is_editable);
g_signal_connect(G_OBJECT(widget), "focus-out-event",
G_CALLBACK(field_string_focus_out_cb), field);
@@ -906,64 +1218,87 @@ create_int_field(PurpleRequestField *field)
int value;
GtkWidget *widget;
- widget = gtk_entry_new();
+ widget = gtk_spin_button_new_with_range(
+ purple_request_field_int_get_lower_bound(field),
+ purple_request_field_int_get_upper_bound(field), 1);
setup_entry_field(widget, field);
value = purple_request_field_int_get_default_value(field);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), value);
- if (value != 0)
- {
- char buf[32];
-
- g_snprintf(buf, sizeof(buf), "%d", value);
-
- gtk_entry_set_text(GTK_ENTRY(widget), buf);
- }
-
- g_signal_connect(G_OBJECT(widget), "focus-out-event",
- G_CALLBACK(field_int_focus_out_cb), field);
+ gtk_widget_set_tooltip_text(widget, purple_request_field_get_tooltip(field));
return widget;
}
static GtkWidget *
-create_bool_field(PurpleRequestField *field)
+create_bool_field(PurpleRequestField *field,
+ PurpleRequestCommonParameters *cpar)
{
GtkWidget *widget;
+ gchar *label;
- widget = gtk_check_button_new_with_label(
+ label = pidgin_request_escape(cpar,
purple_request_field_get_label(field));
+ widget = gtk_check_button_new_with_label(label);
+ g_free(label);
+
+ gtk_widget_set_tooltip_text(widget, purple_request_field_get_tooltip(field));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
purple_request_field_bool_get_default_value(field));
g_signal_connect(G_OBJECT(widget), "toggled",
G_CALLBACK(field_bool_cb), field);
+ g_signal_connect(widget, "toggled",
+ G_CALLBACK(req_field_changed_cb), field);
return widget;
}
static GtkWidget *
-create_choice_field(PurpleRequestField *field)
+create_choice_field(PurpleRequestField *field,
+ PurpleRequestCommonParameters *cpar)
{
GtkWidget *widget;
- GList *labels = purple_request_field_choice_get_labels(field);
- int num_labels = g_list_length(labels);
+ GList *elements = purple_request_field_choice_get_elements(field);
+ int num_labels = g_list_length(elements) / 2;
GList *l;
+ gpointer *values = g_new(gpointer, num_labels);
+ gpointer default_value;
+ gboolean default_found = FALSE;
+ int i;
- if (num_labels > 5)
+ default_value = purple_request_field_choice_get_value(field);
+ if (num_labels > 5 || purple_request_cpar_is_compact(cpar))
{
- widget = gtk_combo_box_new_text();
+ int default_index = 0;
+ widget = gtk_combo_box_text_new();
- for (l = labels; l != NULL; l = l->next)
+ i = 0;
+ l = elements;
+ while (l != NULL)
{
- const char *text = l->data;
- gtk_combo_box_append_text(GTK_COMBO_BOX(widget), text);
+ const char *text;
+ gpointer *value;
+
+ text = l->data;
+ l = g_list_next(l);
+ value = l->data;
+ l = g_list_next(l);
+
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(widget), text);
+ if (value == default_value) {
+ default_index = i;
+ default_found = TRUE;
+ }
+ values[i++] = value;
}
- gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
- purple_request_field_choice_get_default_value(field));
+ gtk_combo_box_set_active(GTK_COMBO_BOX(widget), default_index);
+
+ gtk_widget_set_tooltip_text(widget, purple_request_field_get_tooltip(field));
g_signal_connect(G_OBJECT(widget), "changed",
G_CALLBACK(field_choice_menu_cb), field);
@@ -973,27 +1308,40 @@ create_choice_field(PurpleRequestField *field)
GtkWidget *box;
GtkWidget *first_radio = NULL;
GtkWidget *radio;
- gint i;
if (num_labels == 2)
- box = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
else
- box = gtk_vbox_new(FALSE, 0);
+ box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
widget = box;
- for (l = labels, i = 0; l != NULL; l = l->next, i++)
+ gtk_widget_set_tooltip_text(widget, purple_request_field_get_tooltip(field));
+
+ i = 0;
+ l = elements;
+ while (l != NULL)
{
- const char *text = l->data;
+ const char *text;
+ gpointer *value;
+
+ text = l->data;
+ l = g_list_next(l);
+ value = l->data;
+ l = g_list_next(l);
radio = gtk_radio_button_new_with_label_from_widget(
GTK_RADIO_BUTTON(first_radio), text);
+ g_object_set_data(G_OBJECT(radio), "box", box);
if (first_radio == NULL)
first_radio = radio;
- if (i == purple_request_field_choice_get_default_value(field))
+ if (value == default_value) {
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE);
+ default_found = TRUE;
+ }
+ values[i++] = value;
gtk_box_pack_start(GTK_BOX(box), radio, TRUE, TRUE, 0);
gtk_widget_show(radio);
@@ -1003,6 +1351,11 @@ create_choice_field(PurpleRequestField *field)
}
}
+ if (!default_found && i > 0)
+ purple_request_field_choice_set_value(field, values[0]);
+
+ g_object_set_data_full(G_OBJECT(widget), "values", values, g_free);
+
return widget;
}
@@ -1024,6 +1377,8 @@ create_image_field(PurpleRequestField *field)
g_object_unref(G_OBJECT(buf));
g_object_unref(G_OBJECT(scale));
+ gtk_widget_set_tooltip_text(widget, purple_request_field_get_tooltip(field));
+
return widget;
}
@@ -1039,6 +1394,10 @@ create_account_field(PurpleRequestField *field)
purple_request_field_account_get_filter(field),
field);
+ gtk_widget_set_tooltip_text(widget, purple_request_field_get_tooltip(field));
+ g_signal_connect(widget, "changed",
+ G_CALLBACK(req_field_changed_cb), field);
+
return widget;
}
@@ -1157,38 +1516,481 @@ create_list_field(PurpleRequestField *field)
return pidgin_make_scrollable(treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, -1);
}
+static GtkWidget *
+create_certificate_field(PurpleRequestField *field)
+{
+ PurpleCertificate *cert;
+#ifdef ENABLE_GCR
+ GcrCertificateBasicsWidget *cert_widget;
+ GByteArray *der;
+ GcrCertificate *gcrt;
+#else
+ GtkWidget *cert_label;
+ char *str;
+ char *escaped;
+#endif
+
+ cert = purple_request_field_certificate_get_value(field);
+
+#ifdef ENABLE_GCR
+ der = purple_certificate_get_der_data(cert);
+ g_return_val_if_fail(der, NULL);
+
+ gcrt = gcr_simple_certificate_new(der->data, der->len);
+ g_return_val_if_fail(gcrt, NULL);
+
+ cert_widget = gcr_certificate_basics_widget_new(gcrt);
+
+ g_byte_array_free(der, TRUE);
+ g_object_unref(G_OBJECT(gcrt));
+
+ return GTK_WIDGET(cert_widget);
+#else
+ str = purple_certificate_get_display_string(cert);
+ escaped = g_markup_escape_text(str, -1);
+
+ cert_label = gtk_label_new(NULL);
+ gtk_label_set_markup(GTK_LABEL(cert_label), escaped);
+ gtk_label_set_line_wrap(GTK_LABEL(cert_label), TRUE);
+ gtk_misc_set_alignment(GTK_MISC(cert_label), 0, 0);
+
+ g_free(str);
+ g_free(escaped);
+
+ return cert_label;
+#endif
+}
+
+static GdkPixbuf*
+_pidgin_datasheet_stock_icon_get(const gchar *stock_name)
+{
+ GdkPixbuf *image = NULL;
+ gchar *domain, *id;
+
+ if (stock_name == NULL)
+ return NULL;
+
+ /* core is quitting */
+ if (datasheet_stock == NULL)
+ return NULL;
+
+ if (g_hash_table_lookup_extended(datasheet_stock, stock_name,
+ NULL, (gpointer*)&image))
+ {
+ return image;
+ }
+
+ domain = g_strdup(stock_name);
+ id = strchr(domain, '/');
+ if (!id) {
+ g_free(domain);
+ return NULL;
+ }
+ id[0] = '\0';
+ id++;
+
+ if (g_strcmp0(domain, "prpl") == 0) {
+ PurpleAccount *account;
+ gchar *prpl, *accountname;
+
+ prpl = id;
+ accountname = strchr(id, ':');
+
+ if (!accountname) {
+ g_free(domain);
+ return NULL;
+ }
+
+ accountname[0] = '\0';
+ accountname++;
+
+ account = purple_accounts_find(accountname, prpl);
+ if (account) {
+ image = pidgin_create_prpl_icon(account,
+ PIDGIN_PRPL_ICON_SMALL);
+ }
+ } else if (g_strcmp0(domain, "e2ee") == 0) {
+ image = pidgin_pixbuf_from_image(
+ _pidgin_e2ee_stock_icon_get(id));
+ } else {
+ purple_debug_error("gtkrequest", "Unknown domain: %s", domain);
+ g_free(domain);
+ return NULL;
+ }
+
+ g_hash_table_insert(datasheet_stock, g_strdup(stock_name), image);
+ return image;
+}
+
+static PurpleRequestDatasheetRecord*
+datasheet_get_selected_row(GtkWidget *sheet_widget)
+{
+ PurpleRequestDatasheet *sheet;
+ GtkTreeView *view;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GList *sel_list;
+ gpointer key;
+
+ g_return_val_if_fail(sheet_widget != NULL, NULL);
+
+ view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(sheet_widget), "view"));
+ sheet = g_object_get_data(G_OBJECT(sheet_widget), "sheet");
+
+ g_return_val_if_fail(view != NULL, NULL);
+ g_return_val_if_fail(sheet != NULL, NULL);
+
+ selection = gtk_tree_view_get_selection(view);
+ if (gtk_tree_selection_count_selected_rows(selection) != 1)
+ return NULL;
+
+ 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);
+
+ gtk_tree_model_get(model, &iter, 0, &key, -1);
+
+ return purple_request_datasheet_record_find(sheet, key);
+}
+
+static void
+datasheet_button_check_sens(GtkWidget *button, gpointer _sheet_widget)
+{
+ PurpleRequestDatasheetAction *act;
+ GtkWidget *sheet_widget = GTK_WIDGET(_sheet_widget);
+
+ g_return_if_fail(sheet_widget != NULL);
+
+ act = g_object_get_data(G_OBJECT(button), "action");
+
+ g_return_if_fail(act != NULL);
+
+ gtk_widget_set_sensitive(button,
+ purple_request_datasheet_action_is_sensitive(act,
+ datasheet_get_selected_row(sheet_widget)));
+}
+
+static void
+datasheet_selection_changed(GtkWidget *sheet_widget)
+{
+ GtkVBox *buttons_box;
+
+ g_return_if_fail(sheet_widget != NULL);
+
+ buttons_box = GTK_VBOX(g_object_get_data(G_OBJECT(sheet_widget),
+ "buttons"));
+ gtk_container_foreach(GTK_CONTAINER(buttons_box),
+ datasheet_button_check_sens, sheet_widget);
+}
+
+static void
+datasheet_update_rec(PurpleRequestDatasheetRecord *rec, GtkListStore *model,
+ GtkTreeIter *iter)
+{
+ guint i, col_count;
+ PurpleRequestDatasheet *sheet;
+
+ g_return_if_fail(rec != NULL);
+ g_return_if_fail(model != NULL);
+ g_return_if_fail(iter != NULL);
+
+ sheet = purple_request_datasheet_record_get_datasheet(rec);
+
+ g_return_if_fail(sheet != NULL);
+
+ col_count = purple_request_datasheet_get_column_count(sheet);
+
+ for (i = 0; i < col_count; i++) {
+ PurpleRequestDatasheetColumnType type;
+
+ type = purple_request_datasheet_get_column_type(
+ sheet, i);
+ if (type == PURPLE_REQUEST_DATASHEET_COLUMN_STRING) {
+ GValue val;
+
+ val.g_type = 0;
+ g_value_init(&val, G_TYPE_STRING);
+ g_value_set_string(&val,
+ purple_request_datasheet_record_get_string_data(
+ rec, i));
+ gtk_list_store_set_value(model, iter,
+ i + 1, &val);
+ } else if (type ==
+ PURPLE_REQUEST_DATASHEET_COLUMN_IMAGE)
+ {
+ GdkPixbuf *pixbuf;
+
+ pixbuf = _pidgin_datasheet_stock_icon_get(
+ purple_request_datasheet_record_get_image_data(
+ rec, i));
+ gtk_list_store_set(model, iter, i + 1,
+ pixbuf, -1);
+ } else
+ g_warn_if_reached();
+ }
+}
+
+static void
+datasheet_fill(PurpleRequestDatasheet *sheet, GtkListStore *model)
+{
+ const GList *it;
+
+ gtk_list_store_clear(model);
+
+ it = purple_request_datasheet_get_records(sheet);
+ for (; it != NULL; it = g_list_next(it)) {
+ PurpleRequestDatasheetRecord *rec = it->data;
+ GtkTreeIter iter;
+
+ gtk_list_store_append(model, &iter);
+ gtk_list_store_set(model, &iter, 0,
+ purple_request_datasheet_record_get_key(rec), -1);
+
+ datasheet_update_rec(rec, model, &iter);
+ }
+
+ datasheet_selection_changed(GTK_WIDGET(g_object_get_data(
+ G_OBJECT(model), "sheet-widget")));
+}
+
+static void
+datasheet_update(PurpleRequestDatasheet *sheet, gpointer key,
+ GtkListStore *model)
+{
+ PurpleRequestDatasheetRecord *rec;
+ GtkTreeIter iter;
+ GtkTreeModel *tmodel = GTK_TREE_MODEL(model);
+ gboolean found = FALSE;
+
+ g_return_if_fail(tmodel != NULL);
+
+ if (key == NULL) {
+ datasheet_fill(sheet, model);
+ return;
+ }
+
+ rec = purple_request_datasheet_record_find(sheet, key);
+
+ if (gtk_tree_model_get_iter_first(tmodel, &iter)) {
+ do {
+ gpointer ikey;
+
+ gtk_tree_model_get(tmodel, &iter, 0, &ikey, -1);
+
+ if (key == ikey) {
+ found = TRUE;
+ break;
+ }
+ } while (gtk_tree_model_iter_next(tmodel, &iter));
+ }
+
+ if (rec == NULL && !found)
+ return;
+
+ if (rec == NULL) {
+ gtk_list_store_remove(model, &iter);
+ return;
+ }
+
+ if (!found) {
+ gtk_list_store_append(model, &iter);
+ gtk_list_store_set(model, &iter, 0, key, -1);
+ }
+
+ datasheet_update_rec(rec, model, &iter);
+
+ datasheet_selection_changed(GTK_WIDGET(g_object_get_data(
+ G_OBJECT(model), "sheet-widget")));
+}
+
+
+static void
+datasheet_selection_changed_cb(GtkTreeSelection *sel, gpointer sheet_widget)
+{
+ datasheet_selection_changed(GTK_WIDGET(sheet_widget));
+}
+
+static void
+datasheet_action_clicked(GtkButton *btn, PurpleRequestDatasheetAction *act)
+{
+ GtkWidget *sheet_widget;
+
+ sheet_widget = g_object_get_data(G_OBJECT(btn), "sheet-widget");
+
+ g_return_if_fail(sheet_widget != NULL);
+
+ purple_request_datasheet_action_call(act, datasheet_get_selected_row(
+ sheet_widget));
+}
+
+static GtkWidget *
+create_datasheet_field(PurpleRequestField *field, GtkSizeGroup *buttons_sg)
+{
+ PurpleRequestDatasheet *sheet;
+ guint i, col_count;
+ GType *col_types;
+ GtkListStore *model;
+ GtkTreeView *view;
+ GtkTreeSelection *sel;
+ GtkWidget *scrollable;
+ GtkCellRenderer *renderer_image = NULL, *renderer_text = NULL;
+ GtkTreeViewColumn *id_column;
+ GtkHBox *main_box;
+ GtkVBox *buttons_box;
+ const GList *it;
+
+ sheet = purple_request_field_datasheet_get_sheet(field);
+ main_box = GTK_HBOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
+
+ col_count = purple_request_datasheet_get_column_count(sheet);
+
+ col_types = g_new0(GType, col_count + 1);
+ col_types[0] = G_TYPE_POINTER;
+ for (i = 0; i < col_count; i++) {
+ PurpleRequestDatasheetColumnType type;
+ type = purple_request_datasheet_get_column_type(sheet, i);
+ if (type == PURPLE_REQUEST_DATASHEET_COLUMN_STRING)
+ col_types[i + 1] = G_TYPE_STRING;
+ else if (type == PURPLE_REQUEST_DATASHEET_COLUMN_IMAGE)
+ col_types[i + 1] = GDK_TYPE_PIXBUF;
+ else
+ g_warn_if_reached();
+ }
+ model = gtk_list_store_newv(col_count + 1, col_types);
+ g_free(col_types);
+
+ view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(
+ GTK_TREE_MODEL(model)));
+ g_object_set_data(G_OBJECT(model), "sheet-widget", main_box);
+ g_object_unref(G_OBJECT(model));
+
+ id_column = gtk_tree_view_column_new();
+ gtk_tree_view_column_set_visible(id_column, FALSE);
+ gtk_tree_view_append_column(view, id_column);
+
+ for (i = 0; i < col_count; i++) {
+ PurpleRequestDatasheetColumnType type;
+ const gchar *title;
+ GtkCellRenderer *renderer = NULL;
+ const gchar *type_str = "";
+
+ type = purple_request_datasheet_get_column_type(sheet, i);
+ title = purple_request_datasheet_get_column_title(sheet, i);
+
+ if (type == PURPLE_REQUEST_DATASHEET_COLUMN_STRING) {
+ type_str = "text";
+ if (!renderer_text)
+ renderer_text = gtk_cell_renderer_text_new();
+ renderer = renderer_text;
+ }
+ else if (type == PURPLE_REQUEST_DATASHEET_COLUMN_IMAGE) {
+ type_str = "pixbuf";
+ if (!renderer_image)
+ renderer_image = gtk_cell_renderer_pixbuf_new();
+ renderer = renderer_image;
+ } else
+ g_warn_if_reached();
+
+ if (title == NULL)
+ title = "";
+ gtk_tree_view_insert_column_with_attributes(
+ view, -1, title, renderer, type_str,
+ i + 1, NULL);
+ }
+
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);
+
+ gtk_widget_set_size_request(GTK_WIDGET(view), 400, 250);
+
+ scrollable = pidgin_make_scrollable(GTK_WIDGET(view),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, -1);
+ gtk_widget_show(GTK_WIDGET(view));
+
+ buttons_box = GTK_VBOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BORDER));
+ gtk_size_group_add_widget(buttons_sg, GTK_WIDGET(buttons_box));
+
+ gtk_box_pack_start(GTK_BOX(main_box), scrollable, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(main_box), GTK_WIDGET(buttons_box),
+ FALSE, FALSE, 0);
+ gtk_widget_show(scrollable);
+ gtk_widget_show(GTK_WIDGET(buttons_box));
+
+ it = purple_request_datasheet_get_actions(sheet);
+ for (; it != NULL; it = g_list_next(it)) {
+ PurpleRequestDatasheetAction *act = it->data;
+ GtkButton *btn;
+ const gchar *label;
+
+ label = purple_request_datasheet_action_get_label(act);
+
+ btn = GTK_BUTTON(gtk_button_new_with_label(label ? label : ""));
+
+ g_object_set_data(G_OBJECT(btn), "action", act);
+ g_object_set_data(G_OBJECT(btn), "sheet-widget", main_box);
+ g_signal_connect(G_OBJECT(btn), "clicked",
+ G_CALLBACK(datasheet_action_clicked), act);
+
+ gtk_box_pack_start(GTK_BOX(buttons_box), GTK_WIDGET(btn),
+ FALSE, FALSE, 0);
+ gtk_widget_show(GTK_WIDGET(btn));
+ }
+
+ g_object_set_data(G_OBJECT(main_box), "view", view);
+ g_object_set_data(G_OBJECT(main_box), "buttons", buttons_box);
+ g_object_set_data(G_OBJECT(main_box), "sheet", sheet);
+
+ datasheet_fill(sheet, model);
+ purple_signal_connect(sheet, "record-changed",
+ pidgin_request_get_handle(),
+ PURPLE_CALLBACK(datasheet_update), model);
+
+ sel = gtk_tree_view_get_selection(view);
+ g_signal_connect(G_OBJECT(sel), "changed",
+ G_CALLBACK(datasheet_selection_changed_cb), main_box);
+
+ return GTK_WIDGET(main_box);
+}
+
static void *
pidgin_request_fields(const char *title, const char *primary,
- const char *secondary, PurpleRequestFields *fields,
- const char *ok_text, GCallback ok_cb,
- const char *cancel_text, GCallback cancel_cb,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
- void *user_data)
+ const char *secondary, PurpleRequestFields *fields, const char *ok_text,
+ GCallback ok_cb, const char *cancel_text, GCallback cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data)
{
PidginRequestData *data;
GtkWidget *win;
- GtkWidget *vbox;
- GtkWidget *vbox2;
- GtkWidget *hbox;
+ GtkNotebook *notebook;
+ GtkWidget **pages;
+ GtkWidget *hbox, *vbox;
GtkWidget *frame;
GtkWidget *label;
GtkWidget *table;
GtkWidget *button;
GtkWidget *img;
- GtkSizeGroup *sg;
+ GtkSizeGroup *sg, *datasheet_buttons_sg;
GList *gl, *fl;
PurpleRequestFieldGroup *group;
PurpleRequestField *field;
char *label_text;
char *primary_esc, *secondary_esc;
- int total_fields = 0;
+ const gboolean compact = purple_request_cpar_is_compact(cpar);
+ GSList *extra_actions, *it;
+ size_t extra_actions_count, i;
+ const gchar **tab_names;
+ guint tab_count;
+ gboolean ok_btn = (ok_text != NULL);
data = g_new0(PidginRequestData, 1);
data->type = PURPLE_REQUEST_FIELDS;
data->user_data = user_data;
data->u.multifield.fields = fields;
- fields->ui_data = data;
+ purple_request_fields_set_ui_data(fields, data);
+
+ extra_actions = purple_request_cpar_get_extra_actions(cpar);
+ extra_actions_count = g_slist_length(extra_actions) / 2;
data->cb_count = 2;
data->cbs = g_new0(GCallback, 2);
@@ -1196,7 +1998,6 @@ pidgin_request_fields(const char *title, const char *primary,
data->cbs[0] = ok_cb;
data->cbs[1] = cancel_cb;
-
#ifdef _WIN32
data->dialog = win = pidgin_create_dialog(PIDGIN_ALERT_TITLE, PIDGIN_HIG_BORDER, "multifield", TRUE) ;
#else /* !_WIN32 */
@@ -1207,38 +2008,56 @@ pidgin_request_fields(const char *title, const char *primary,
G_CALLBACK(destroy_multifield_cb), data);
/* Setup the main horizontal box */
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BORDER);
gtk_container_add(GTK_CONTAINER(pidgin_dialog_get_vbox(GTK_DIALOG(win))), hbox);
gtk_widget_show(hbox);
/* Dialog icon. */
- img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION,
- gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
+ img = pidgin_request_dialog_icon(PURPLE_REQUEST_FIELDS, cpar);
gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
gtk_widget_show(img);
+ pidgin_request_add_help(GTK_DIALOG(win), cpar);
+
+ it = extra_actions;
+ for (i = 0; i < extra_actions_count; i++, it = it->next->next) {
+ const gchar *label = it->data;
+ PurpleRequestFieldsCb *cb = it->next->data;
+
+ button = pidgin_dialog_add_button(GTK_DIALOG(win),
+ text_to_stock(label), G_CALLBACK(multifield_extra_cb),
+ data);
+ g_object_set_data(G_OBJECT(button), "extra-cb", cb);
+ }
+
/* Cancel button */
button = pidgin_dialog_add_button(GTK_DIALOG(win), text_to_stock(cancel_text), G_CALLBACK(multifield_cancel_cb), data);
- GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+ gtk_widget_set_can_default(button, TRUE);
/* OK button */
- button = pidgin_dialog_add_button(GTK_DIALOG(win), text_to_stock(ok_text), G_CALLBACK(multifield_ok_cb), data);
- data->ok_button = button;
- GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
- gtk_window_set_default(GTK_WINDOW(win), button);
+ if (!ok_btn) {
+ gtk_window_set_default(GTK_WINDOW(win), button);
+ } else {
+ button = pidgin_dialog_add_button(GTK_DIALOG(win), text_to_stock(ok_text), G_CALLBACK(multifield_ok_cb), data);
+ data->ok_button = button;
+ gtk_widget_set_can_default(button, TRUE);
+ gtk_window_set_default(GTK_WINDOW(win), button);
+ }
- pidgin_widget_decorate_account(hbox, account);
+ pidgin_widget_decorate_account(hbox,
+ purple_request_cpar_get_account(cpar));
/* Setup the vbox */
- vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BORDER);
gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
gtk_widget_show(vbox);
sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
+ datasheet_buttons_sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
if(primary) {
- primary_esc = g_markup_escape_text(primary, -1);
+ primary_esc = pidgin_request_escape(cpar, primary);
label_text = g_strdup_printf(
"<span weight=\"bold\" size=\"larger\">%s</span>", primary_esc);
g_free(primary_esc);
@@ -1252,44 +2071,92 @@ pidgin_request_fields(const char *title, const char *primary,
g_free(label_text);
}
- for (gl = purple_request_fields_get_groups(fields); gl != NULL;
- gl = gl->next)
- total_fields += g_list_length(purple_request_field_group_get_fields(gl->data));
+ /* Setup tabs */
+ tab_names = purple_request_fields_get_tab_names(fields);
+ if (tab_names == NULL) {
+ notebook = NULL;
+ tab_count = 1;
- if(total_fields > 9) {
- GtkWidget *hbox_for_spacing, *vbox_for_spacing;
+ pages = g_new0(GtkWidget*, 1);
+ pages[0] = vbox;
+ } else {
+ tab_count = g_strv_length((gchar **)tab_names);
+ notebook = GTK_NOTEBOOK(gtk_notebook_new());
- hbox_for_spacing = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
- gtk_box_pack_start(GTK_BOX(vbox),
- pidgin_make_scrollable(hbox_for_spacing, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, GTK_SHADOW_NONE, -1, 200),
- TRUE, TRUE, 0);
- gtk_widget_show(hbox_for_spacing);
+ pages = g_new0(GtkWidget*, tab_count);
- vbox_for_spacing = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
- gtk_box_pack_start(GTK_BOX(hbox_for_spacing),
- vbox_for_spacing, TRUE, TRUE, PIDGIN_HIG_BOX_SPACE);
- gtk_widget_show(vbox_for_spacing);
+ for (i = 0; i < tab_count; i++) {
+ pages[i] = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BORDER);
+ gtk_container_set_border_width(GTK_CONTAINER(pages[i]), PIDGIN_HIG_BORDER);
+ gtk_notebook_append_page(notebook, pages[i], NULL);
+ gtk_notebook_set_tab_label_text(notebook, pages[i], tab_names[i]);
+ gtk_widget_show(pages[i]);
+ }
+ }
- vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
- gtk_box_pack_start(GTK_BOX(vbox_for_spacing),
- vbox2, TRUE, TRUE, PIDGIN_HIG_BOX_SPACE);
- gtk_widget_show(vbox2);
- } else {
- vbox2 = vbox;
+ for (i = 0; i < tab_count; i++) {
+ guint total_fields = 0;
+ GList *it;
+
+ it = purple_request_fields_get_groups(fields);
+ for (; it != NULL; it = g_list_next(it)) {
+ group = it->data;
+ if (purple_request_field_group_get_tab(group) != i)
+ continue;
+ total_fields += g_list_length(
+ purple_request_field_group_get_fields(group));
+ }
+
+ if(total_fields > 9) {
+ GtkWidget *hbox_for_spacing, *vbox_for_spacing;
+
+ gtk_container_set_border_width(
+ GTK_CONTAINER(pages[i]), 0);
+
+ hbox_for_spacing =
+ gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BORDER);
+ gtk_box_pack_start(GTK_BOX(pages[i]),
+ pidgin_make_scrollable(hbox_for_spacing,
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC,
+ GTK_SHADOW_NONE, -1, 200),
+ TRUE, TRUE, 0);
+ gtk_widget_show(hbox_for_spacing);
+
+ vbox_for_spacing =
+ gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BORDER);
+ gtk_box_pack_start(GTK_BOX(hbox_for_spacing),
+ vbox_for_spacing, TRUE, TRUE,
+ PIDGIN_HIG_BOX_SPACE);
+ gtk_widget_show(vbox_for_spacing);
+
+ pages[i] = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BORDER);
+ gtk_box_pack_start(GTK_BOX(vbox_for_spacing),
+ pages[i], TRUE, TRUE, PIDGIN_HIG_BOX_SPACE);
+ gtk_widget_show(pages[i]);
+ }
+
+ if (notebook == NULL)
+ vbox = pages[0];
}
if (secondary) {
- secondary_esc = g_markup_escape_text(secondary, -1);
+ secondary_esc = pidgin_request_escape(cpar, secondary);
label = gtk_label_new(NULL);
gtk_label_set_markup(GTK_LABEL(label), secondary_esc);
g_free(secondary_esc);
gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
- gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), label, (notebook == NULL),
+ (notebook == NULL), 0);
gtk_widget_show(label);
}
+ if (notebook != NULL) {
+ gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(notebook), TRUE, TRUE, 0);
+ gtk_widget_show(GTK_WIDGET(notebook));
+ }
+
for (gl = purple_request_fields_get_groups(fields);
gl != NULL;
gl = gl->next)
@@ -1302,17 +2169,25 @@ pidgin_request_fields(const char *title, const char *primary,
size_t col_num;
#endif
size_t row_num = 0;
+ guint tab_no;
+ gboolean contains_resizable = FALSE, frame_fill;
group = gl->data;
field_list = purple_request_field_group_get_fields(group);
+ tab_no = purple_request_field_group_get_tab(group);
+ if (tab_no >= tab_count) {
+ purple_debug_warning("gtkrequest",
+ "Invalid tab number: %d", tab_no);
+ tab_no = 0;
+ }
if (purple_request_field_group_get_title(group) != NULL)
{
- frame = pidgin_make_frame(vbox2,
+ frame = pidgin_make_frame(pages[tab_no],
purple_request_field_group_get_title(group));
}
else
- frame = vbox2;
+ frame = pages[tab_no];
field_count = g_list_length(field_list);
#if 0
@@ -1335,7 +2210,10 @@ pidgin_request_fields(const char *title, const char *primary,
field = (PurpleRequestField *)fl->data;
- type = purple_request_field_get_type(field);
+ type = purple_request_field_get_field_type(field);
+
+ if (type == PURPLE_REQUEST_FIELD_DATASHEET)
+ contains_resizable = TRUE;
if (type == PURPLE_REQUEST_FIELD_LABEL)
{
@@ -1356,7 +2234,8 @@ pidgin_request_fields(const char *title, const char *primary,
#endif
rows += 2;
- }
+ } else if (compact && type != PURPLE_REQUEST_FIELD_BOOLEAN)
+ rows++;
#if 0
col_num++;
@@ -1366,11 +2245,15 @@ pidgin_request_fields(const char *title, const char *primary,
#endif
}
- table = gtk_table_new(rows, 2 * cols, FALSE);
+ if (compact)
+ table = gtk_table_new(rows, cols, FALSE);
+ else
+ table = gtk_table_new(rows, 2 * cols, FALSE);
gtk_table_set_row_spacings(GTK_TABLE(table), PIDGIN_HIG_BOX_SPACE);
gtk_table_set_col_spacings(GTK_TABLE(table), PIDGIN_HIG_BOX_SPACE);
- gtk_container_add(GTK_CONTAINER(frame), table);
+ frame_fill = (notebook == NULL || contains_resizable);
+ gtk_box_pack_start(GTK_BOX(frame), table, frame_fill, frame_fill, 0);
gtk_widget_show(table);
for (row_num = 0, fl = field_list;
@@ -1394,7 +2277,7 @@ pidgin_request_fields(const char *title, const char *primary,
#endif
PurpleRequestFieldType type;
GtkWidget *widget = NULL;
- const char *field_label;
+ gchar *field_label;
label = NULL;
field = fl->data;
@@ -1406,15 +2289,20 @@ pidgin_request_fields(const char *title, const char *primary,
continue;
}
- type = purple_request_field_get_type(field);
- field_label = purple_request_field_get_label(field);
+ type = purple_request_field_get_field_type(field);
+ field_label = pidgin_request_escape(cpar,
+ purple_request_field_get_label(field));
if (type != PURPLE_REQUEST_FIELD_BOOLEAN && field_label)
{
char *text = NULL;
- if (field_label[strlen(field_label) - 1] != ':')
+ if (field_label[strlen(field_label) - 1] != ':' &&
+ field_label[strlen(field_label) - 1] != '?' &&
+ type != PURPLE_REQUEST_FIELD_LABEL)
+ {
text = g_strdup_printf("%s:", field_label);
+ }
label = gtk_label_new(NULL);
gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), text ? text : field_label);
@@ -1451,6 +2339,7 @@ pidgin_request_fields(const char *title, const char *primary,
}
gtk_widget_show(label);
+ g_free(field_label);
}
widget = GTK_WIDGET(purple_request_field_get_ui_data(field));
@@ -1461,19 +2350,26 @@ pidgin_request_fields(const char *title, const char *primary,
else if (type == PURPLE_REQUEST_FIELD_INTEGER)
widget = create_int_field(field);
else if (type == PURPLE_REQUEST_FIELD_BOOLEAN)
- widget = create_bool_field(field);
+ widget = create_bool_field(field, cpar);
else if (type == PURPLE_REQUEST_FIELD_CHOICE)
- widget = create_choice_field(field);
+ widget = create_choice_field(field, cpar);
else if (type == PURPLE_REQUEST_FIELD_LIST)
widget = create_list_field(field);
else if (type == PURPLE_REQUEST_FIELD_IMAGE)
widget = create_image_field(field);
else if (type == PURPLE_REQUEST_FIELD_ACCOUNT)
widget = create_account_field(field);
+ else if (type == PURPLE_REQUEST_FIELD_CERTIFICATE)
+ widget = create_certificate_field(field);
+ else if (type == PURPLE_REQUEST_FIELD_DATASHEET)
+ widget = create_datasheet_field(field, datasheet_buttons_sg);
else
continue;
}
+ gtk_widget_set_sensitive(widget,
+ purple_request_field_is_sensitive(field));
+
if (label)
gtk_label_set_mnemonic_widget(GTK_LABEL(label), widget);
@@ -1505,8 +2401,15 @@ pidgin_request_fields(const char *title, const char *primary,
GTK_FILL | GTK_EXPAND,
5, 0);
}
- else
- {
+ else if (compact) {
+ row_num++;
+ gtk_table_attach(GTK_TABLE(table), widget,
+ 0, 2 * cols,
+ row_num, row_num + 1,
+ GTK_FILL | GTK_EXPAND,
+ GTK_FILL | GTK_EXPAND,
+ 5, 0);
+ } else {
gtk_table_attach(GTK_TABLE(table), widget,
1, 2 * cols,
row_num, row_num + 1,
@@ -1523,10 +2426,16 @@ pidgin_request_fields(const char *title, const char *primary,
}
g_object_unref(sg);
+ g_object_unref(datasheet_buttons_sg);
if (!purple_request_fields_all_required_filled(fields))
gtk_widget_set_sensitive(data->ok_button, FALSE);
+ if (!purple_request_fields_all_valid(fields))
+ gtk_widget_set_sensitive(data->ok_button, FALSE);
+
+ g_free(pages);
+
pidgin_auto_parent_window(win);
gtk_widget_show(win);
@@ -1577,7 +2486,7 @@ file_ok_check_if_exists_cb(GtkWidget *widget, gint response, PidginRequestData *
(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, NULL, NULL,
+ NULL,
data, 2,
_("Overwrite"), G_CALLBACK(file_yes_no_cb),
_("Choose New Name"), G_CALLBACK(file_yes_no_cb));
@@ -1587,15 +2496,15 @@ file_ok_check_if_exists_cb(GtkWidget *widget, gint response, PidginRequestData *
static void *
pidgin_request_file(const char *title, const char *filename,
- gboolean savedialog,
- GCallback ok_cb, GCallback cancel_cb,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
- void *user_data)
+ gboolean savedialog, GCallback ok_cb, GCallback cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data)
{
PidginRequestData *data;
GtkWidget *filesel;
+#ifdef _WIN32
const gchar *current_folder;
gboolean folder_set = FALSE;
+#endif
data = g_new0(PidginRequestData, 1);
data->type = PURPLE_REQUEST_FILE;
@@ -1619,11 +2528,7 @@ pidgin_request_file(const char *title, const char *filename,
NULL);
gtk_dialog_set_default_response(GTK_DIALOG(filesel), GTK_RESPONSE_ACCEPT);
- if (savedialog) {
- current_folder = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/filelocations/last_save_folder");
- } else {
- current_folder = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/filelocations/last_open_folder");
- }
+ pidgin_request_add_help(GTK_DIALOG(filesel), cpar);
if ((filename != NULL) && (*filename != '\0')) {
if (savedialog)
@@ -1631,12 +2536,19 @@ pidgin_request_file(const char *title, const char *filename,
else if (g_file_test(filename, G_FILE_TEST_EXISTS))
gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(filesel), filename);
}
+
+#ifdef _WIN32
+ if (savedialog) {
+ current_folder = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/filelocations/last_save_folder");
+ } else {
+ current_folder = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/filelocations/last_open_folder");
+ }
+
if ((filename == NULL || *filename == '\0' || !g_file_test(filename, G_FILE_TEST_EXISTS)) &&
(current_folder != NULL) && (*current_folder != '\0')) {
folder_set = gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(filesel), current_folder);
}
-#ifdef _WIN32
if (!folder_set && (filename == NULL || *filename == '\0' || !g_file_test(filename, G_FILE_TEST_EXISTS))) {
char *my_documents = wpurple_get_special_folder(CSIDL_PERSONAL);
@@ -1647,8 +2559,6 @@ pidgin_request_file(const char *title, const char *filename,
g_free(my_documents);
}
}
-#else
- (void)folder_set;
#endif
g_signal_connect(G_OBJECT(GTK_FILE_CHOOSER(filesel)), "response",
@@ -1663,10 +2573,9 @@ pidgin_request_file(const char *title, const char *filename,
}
static void *
-pidgin_request_folder(const char *title, const char *dirname,
- GCallback ok_cb, GCallback cancel_cb,
- PurpleAccount *account, const char *who, PurpleConversation *conv,
- void *user_data)
+pidgin_request_folder(const char *title, const char *dirname, GCallback ok_cb,
+ GCallback cancel_cb, PurpleRequestCommonParameters *cpar,
+ void *user_data)
{
PidginRequestData *data;
GtkWidget *dirsel;
@@ -1689,6 +2598,8 @@ pidgin_request_folder(const char *title, const char *dirname,
NULL);
gtk_dialog_set_default_response(GTK_DIALOG(dirsel), GTK_RESPONSE_ACCEPT);
+ pidgin_request_add_help(GTK_DIALOG(dirsel), cpar);
+
if ((dirname != NULL) && (*dirname != '\0'))
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dirsel), dirname);
@@ -1703,6 +2614,34 @@ pidgin_request_folder(const char *title, const char *dirname,
return (void *)data;
}
+/* if request callback issues another request, it should be attached to the
+ * primary request parent */
+static void
+pidgin_window_detach_children(GtkWindow* win)
+{
+ GList *it;
+ GtkWindow *par;
+
+ g_return_if_fail(win != NULL);
+
+ par = gtk_window_get_transient_for(win);
+ it = gtk_window_list_toplevels();
+ for (it = g_list_first(it); it != NULL; it = g_list_next(it)) {
+ GtkWindow *child = GTK_WINDOW(it->data);
+ if (gtk_window_get_transient_for(child) != win)
+ continue;
+ if (gtk_window_get_destroy_with_parent(child)) {
+#ifdef _WIN32
+ /* XXX test/verify it: Win32 gtk ignores
+ * gtk_window_set_destroy_with_parent(..., FALSE). */
+ gtk_window_set_transient_for(child, NULL);
+#endif
+ continue;
+ }
+ gtk_window_set_transient_for(child, par);
+ }
+}
+
static void
pidgin_close_request(PurpleRequestType type, void *ui_handle)
{
@@ -1710,6 +2649,8 @@ pidgin_close_request(PurpleRequestType type, void *ui_handle)
g_free(data->cbs);
+ pidgin_window_detach_children(GTK_WINDOW(data->dialog));
+
gtk_widget_destroy(data->dialog);
if (type == PURPLE_REQUEST_FIELDS)
@@ -1720,16 +2661,30 @@ pidgin_close_request(PurpleRequestType type, void *ui_handle)
g_free(data);
}
+GtkWindow *
+pidgin_request_get_dialog_window(void *ui_handle)
+{
+ PidginRequestData *data = ui_handle;
+
+ g_return_val_if_fail(
+ purple_request_is_valid_ui_handle(data, NULL), NULL);
+
+ return GTK_WINDOW(data->dialog);
+}
+
static PurpleRequestUiOps ops =
{
+ PURPLE_REQUEST_FEATURE_HTML,
pidgin_request_input,
pidgin_request_choice,
pidgin_request_action,
+ pidgin_request_wait,
+ pidgin_request_wait_update,
pidgin_request_fields,
pidgin_request_file,
- pidgin_close_request,
pidgin_request_folder,
- pidgin_request_action_with_icon,
+ pidgin_close_request,
+ NULL,
NULL,
NULL,
NULL
@@ -1740,3 +2695,34 @@ pidgin_request_get_ui_ops(void)
{
return &ops;
}
+
+void *
+pidgin_request_get_handle(void)
+{
+ static int handle;
+
+ return &handle;
+}
+
+static void
+pidgin_request_datasheet_stock_remove(gpointer obj)
+{
+ if (obj == NULL)
+ return;
+ g_object_unref(obj);
+}
+
+void
+pidgin_request_init(void)
+{
+ datasheet_stock = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, pidgin_request_datasheet_stock_remove);
+}
+
+void
+pidgin_request_uninit(void)
+{
+ purple_signals_disconnect_by_handle(pidgin_request_get_handle());
+ g_hash_table_destroy(datasheet_stock);
+ datasheet_stock = NULL;
+}
diff --git a/pidgin/gtkrequest.h b/pidgin/gtkrequest.h
index 83e466f6aa..fb475b7322 100644
--- a/pidgin/gtkrequest.h
+++ b/pidgin/gtkrequest.h
@@ -1,8 +1,3 @@
-/**
- * @file gtkrequest.h GTK+ Request API
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -23,16 +18,67 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _PIDGINREQUEST_H_
#define _PIDGINREQUEST_H_
+/**
+ * SECTION:gtkrequest
+ * @section_id: pidgin-gtkrequest
+ * @short_description: <filename>gtkrequest.h</filename>
+ * @title: Request API
+ */
#include "request.h"
+G_BEGIN_DECLS
+
/**
+ * pidgin_request_get_ui_ops:
+ *
* Returns the UI operations structure for GTK+ request functions.
*
- * @return The GTK+ UI request operations structure.
+ * Returns: The GTK+ UI request operations structure.
*/
PurpleRequestUiOps *pidgin_request_get_ui_ops(void);
+/**
+ * pidgin_request_get_dialog_window:
+ * @ui_handle: The UI handle.
+ *
+ * Gets dialog window for specified libpurple request.
+ *
+ * Returns: The dialog window.
+ */
+GtkWindow *
+pidgin_request_get_dialog_window(void *ui_handle);
+
+/**************************************************************************/
+/* GTK+ Requests Subsystem */
+/**************************************************************************/
+
+/**
+ * pidgin_request_get_handle:
+ *
+ * Returns the gtk requests subsystem handle.
+ *
+ * Returns: The requests subsystem handle.
+ */
+void *pidgin_request_get_handle(void);
+
+/**
+ * pidgin_request_init:
+ *
+ * Initializes the GTK+ requests subsystem.
+ */
+void pidgin_request_init(void);
+
+/**
+ * pidgin_request_uninit:
+ *
+ * Uninitializes the GTK+ requests subsystem.
+ */
+void pidgin_request_uninit(void);
+
+G_END_DECLS
+
#endif /* _PIDGINREQUEST_H_ */
diff --git a/pidgin/gtkroomlist.c b/pidgin/gtkroomlist.c
index 63630b1766..44173dccf3 100644
--- a/pidgin/gtkroomlist.c
+++ b/pidgin/gtkroomlist.c
@@ -1,8 +1,3 @@
-/**
- * @file gtkroomlist.c GTK+ Room List UI
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -35,6 +30,7 @@
#include "connection.h"
#include "notify.h"
+#include "gtk3compat.h"
#include "gtkroomlist.h"
typedef struct _PidginRoomlistDialog {
@@ -60,7 +56,7 @@ typedef struct _PidginRoomlist {
PidginRoomlistDialog *dialog;
GtkTreeStore *model;
GtkWidget *tree;
- GHashTable *cats; /**< Meow. */
+ GHashTable *cats; /* Meow. */
gint num_rooms, total_rooms;
GtkWidget *tipwindow;
GdkRectangle tip_rect;
@@ -91,15 +87,15 @@ static gint delete_win_cb(GtkWidget *w, GdkEventAny *e, gpointer d)
purple_timeout_remove(dialog->pg_update_to);
if (dialog->roomlist) {
- PidginRoomlist *rl = dialog->roomlist->ui_data;
+ PidginRoomlist *rl = purple_roomlist_get_ui_data(dialog->roomlist);
if (dialog->pg_update_to > 0)
/* yes, that's right, unref it twice. */
- purple_roomlist_unref(dialog->roomlist);
+ g_object_unref(dialog->roomlist);
if (rl)
rl->dialog = NULL;
- purple_roomlist_unref(dialog->roomlist);
+ g_object_unref(dialog->roomlist);
}
dialog->progress = NULL;
@@ -115,12 +111,12 @@ static void dialog_select_account_cb(GObject *w, PurpleAccount *account,
dialog->account = account;
if (change && dialog->roomlist) {
- PidginRoomlist *rl = dialog->roomlist->ui_data;
+ PidginRoomlist *rl = purple_roomlist_get_ui_data(dialog->roomlist);
if (rl->tree) {
gtk_widget_destroy(rl->tree);
rl->tree = NULL;
}
- purple_roomlist_unref(dialog->roomlist);
+ g_object_unref(dialog->roomlist);
dialog->roomlist = NULL;
}
}
@@ -135,16 +131,16 @@ static void list_button_cb(GtkButton *button, PidginRoomlistDialog *dialog)
return;
if (dialog->roomlist != NULL) {
- rl = dialog->roomlist->ui_data;
+ rl = purple_roomlist_get_ui_data(dialog->roomlist);
gtk_widget_destroy(rl->tree);
- purple_roomlist_unref(dialog->roomlist);
+ g_object_unref(dialog->roomlist);
}
dialog->roomlist = purple_roomlist_get_list(gc);
if (!dialog->roomlist)
return;
- purple_roomlist_ref(dialog->roomlist);
- rl = dialog->roomlist->ui_data;
+ g_object_ref(dialog->roomlist);
+ rl = purple_roomlist_get_ui_data(dialog->roomlist);
rl->dialog = dialog;
if (dialog->account_widget)
@@ -203,7 +199,7 @@ selection_changed_cb(GtkTreeSelection *selection, PidginRoomlist *grl) {
val.g_type = 0;
gtk_tree_model_get_value(GTK_TREE_MODEL(grl->model), &iter, ROOM_COLUMN, &val);
room = g_value_get_pointer(&val);
- if (!room || !(room->type & PURPLE_ROOMLIST_ROOMTYPE_ROOM)) {
+ if (!room || !(purple_roomlist_room_get_room_type(room) & PURPLE_ROOMLIST_ROOMTYPE_ROOM)) {
gtk_widget_set_sensitive(dialog->join_button, FALSE);
gtk_widget_set_sensitive(dialog->add_button, FALSE);
return;
@@ -228,18 +224,19 @@ selection_changed_cb(GtkTreeSelection *selection, PidginRoomlist *grl) {
static void do_add_room_cb(GtkWidget *w, struct _menu_cb_info *info)
{
char *name;
- PurpleConnection *gc = purple_account_get_connection(info->list->account);
+ PurpleAccount *account = purple_roomlist_get_account(info->list);
+ PurpleConnection *gc = purple_account_get_connection(account);
PurplePluginProtocolInfo *prpl_info = NULL;
if(gc != NULL)
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
if(prpl_info != NULL && prpl_info->roomlist_room_serialize)
name = prpl_info->roomlist_room_serialize(info->room);
else
- name = g_strdup(info->room->name);
+ name = g_strdup(purple_roomlist_room_get_name(info->room));
- purple_blist_request_add_chat(info->list->account, NULL, NULL, name);
+ purple_blist_request_add_chat(account, NULL, NULL, name);
g_free(name);
}
@@ -247,7 +244,7 @@ static void do_add_room_cb(GtkWidget *w, struct _menu_cb_info *info)
static void add_room_to_blist_cb(GtkButton *button, PidginRoomlistDialog *dialog)
{
PurpleRoomlist *rl = dialog->roomlist;
- PidginRoomlist *grl = rl->ui_data;
+ PidginRoomlist *grl = purple_roomlist_get_ui_data(rl);
struct _menu_cb_info *info = g_object_get_data(G_OBJECT(button), "room-info");
if(info != NULL)
@@ -262,7 +259,7 @@ static void do_join_cb(GtkWidget *w, struct _menu_cb_info *info)
static void join_button_cb(GtkButton *button, PidginRoomlistDialog *dialog)
{
PurpleRoomlist *rl = dialog->roomlist;
- PidginRoomlist *grl = rl->ui_data;
+ PidginRoomlist *grl = purple_roomlist_get_ui_data(rl);
struct _menu_cb_info *info = g_object_get_data(G_OBJECT(button), "room-info");
if(info != NULL)
@@ -272,7 +269,7 @@ static void join_button_cb(GtkButton *button, PidginRoomlistDialog *dialog)
static void row_activated_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *arg2,
PurpleRoomlist *list)
{
- PidginRoomlist *grl = list->ui_data;
+ PidginRoomlist *grl = purple_roomlist_get_ui_data(list);
GtkTreeIter iter;
PurpleRoomlistRoom *room;
GValue val;
@@ -282,7 +279,7 @@ static void row_activated_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColu
val.g_type = 0;
gtk_tree_model_get_value(GTK_TREE_MODEL(grl->model), &iter, ROOM_COLUMN, &val);
room = g_value_get_pointer(&val);
- if (!room || !(room->type & PURPLE_ROOMLIST_ROOMTYPE_ROOM))
+ if (!room || !(purple_roomlist_room_get_room_type(room) & PURPLE_ROOMLIST_ROOMTYPE_ROOM))
return;
info.list = list;
@@ -294,7 +291,7 @@ static void row_activated_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColu
static gboolean room_click_cb(GtkWidget *tv, GdkEventButton *event, PurpleRoomlist *list)
{
GtkTreePath *path;
- PidginRoomlist *grl = list->ui_data;
+ PidginRoomlist *grl = purple_roomlist_get_ui_data(list);
GValue val;
PurpleRoomlistRoom *room;
GtkTreeIter iter;
@@ -313,7 +310,7 @@ static gboolean room_click_cb(GtkWidget *tv, GdkEventButton *event, PurpleRoomli
gtk_tree_model_get_value (GTK_TREE_MODEL(grl->model), &iter, ROOM_COLUMN, &val);
room = g_value_get_pointer(&val);
- if (!room || !(room->type & PURPLE_ROOMLIST_ROOMTYPE_ROOM))
+ if (!room || !(purple_roomlist_room_get_room_type(room) & PURPLE_ROOMLIST_ROOMTYPE_ROOM))
return FALSE;
info.list = list;
@@ -341,9 +338,9 @@ static void row_expanded_cb(GtkTreeView *treeview, GtkTreeIter *arg1, GtkTreePat
gtk_tree_model_get_value(gtk_tree_view_get_model(treeview), arg1, ROOM_COLUMN, &val);
category = g_value_get_pointer(&val);
- if (!category->expanded_once) {
+ if (!purple_roomlist_room_get_expanded_once(category)) {
purple_roomlist_expand_category(list, category);
- category->expanded_once = TRUE;
+ purple_roomlist_room_set_expanded_once(category, TRUE);
}
}
@@ -351,49 +348,87 @@ static void row_expanded_cb(GtkTreeView *treeview, GtkTreeIter *arg1, GtkTreePat
#define TOOLTIP_BORDER 12
static gboolean
-pidgin_roomlist_paint_tooltip(GtkWidget *widget, gpointer user_data)
+pidgin_roomlist_paint_tooltip(GtkWidget *widget, cairo_t *cr, gpointer user_data)
{
PurpleRoomlist *list = user_data;
- PidginRoomlist *grl = list->ui_data;
- GtkStyle *style;
+ PidginRoomlist *grl = purple_roomlist_get_ui_data(list);
int current_height, max_width;
int max_text_width;
GtkTextDirection dir = gtk_widget_get_direction(GTK_WIDGET(grl->tree));
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context;
- style = grl->tipwindow->style;
+ context = gtk_widget_get_style_context(grl->tipwindow);
+ gtk_style_context_add_class(context, GTK_STYLE_CLASS_TOOLTIP);
+#else
+ GtkStyle *style;
+
+ style = gtk_widget_get_style(grl->tipwindow);
+#endif
max_text_width = MAX(grl->tip_width, grl->tip_name_width);
max_width = TOOLTIP_BORDER + SMALL_SPACE + max_text_width + TOOLTIP_BORDER;
current_height = 12;
+#if GTK_CHECK_VERSION(3,0,0)
+ if (dir == GTK_TEXT_DIR_RTL) {
+ gtk_render_layout(context, cr,
+ max_width - (TOOLTIP_BORDER + SMALL_SPACE) - PANGO_PIXELS(600000),
+ current_height,
+ grl->tip_name_layout);
+ } else {
+ gtk_render_layout(context, cr,
+ TOOLTIP_BORDER + SMALL_SPACE,
+ current_height,
+ grl->tip_name_layout);
+ }
+ if (dir != GTK_TEXT_DIR_RTL) {
+ gtk_render_layout(context, cr,
+ TOOLTIP_BORDER + SMALL_SPACE,
+ current_height + grl->tip_name_height,
+ grl->tip_layout);
+ } else {
+ gtk_render_layout(context, cr,
+ max_width - (TOOLTIP_BORDER + SMALL_SPACE) - PANGO_PIXELS(600000),
+ current_height + grl->tip_name_height,
+ grl->tip_layout);
+ }
+#else
if (dir == GTK_TEXT_DIR_RTL) {
gtk_paint_layout(style, grl->tipwindow->window, GTK_STATE_NORMAL, FALSE,
- NULL, grl->tipwindow, "tooltip",
- max_width - (TOOLTIP_BORDER + SMALL_SPACE) - PANGO_PIXELS(600000),
- current_height, grl->tip_name_layout);
+ NULL, grl->tipwindow, "tooltip",
+ max_width - (TOOLTIP_BORDER + SMALL_SPACE) - PANGO_PIXELS(600000),
+ current_height,
+ grl->tip_name_layout);
} else {
- gtk_paint_layout (style, grl->tipwindow->window, GTK_STATE_NORMAL, FALSE,
- NULL, grl->tipwindow, "tooltip",
- TOOLTIP_BORDER + SMALL_SPACE, current_height, grl->tip_name_layout);
+ gtk_paint_layout(style, grl->tipwindow->window, GTK_STATE_NORMAL, FALSE,
+ NULL, grl->tipwindow, "tooltip",
+ TOOLTIP_BORDER + SMALL_SPACE,
+ current_height,
+ grl->tip_name_layout);
}
if (dir != GTK_TEXT_DIR_RTL) {
- gtk_paint_layout (style, grl->tipwindow->window, GTK_STATE_NORMAL, FALSE,
- NULL, grl->tipwindow, "tooltip",
- TOOLTIP_BORDER + SMALL_SPACE, current_height + grl->tip_name_height, grl->tip_layout);
+ gtk_paint_layout(style, grl->tipwindow->window, GTK_STATE_NORMAL, FALSE,
+ NULL, grl->tipwindow, "tooltip",
+ TOOLTIP_BORDER + SMALL_SPACE,
+ current_height + grl->tip_name_height,
+ grl->tip_layout);
} else {
gtk_paint_layout(style, grl->tipwindow->window, GTK_STATE_NORMAL, FALSE,
- NULL, grl->tipwindow, "tooltip",
- max_width - (TOOLTIP_BORDER + SMALL_SPACE) - PANGO_PIXELS(600000),
- current_height + grl->tip_name_height,
- grl->tip_layout);
+ NULL, grl->tipwindow, "tooltip",
+ max_width - (TOOLTIP_BORDER + SMALL_SPACE) - PANGO_PIXELS(600000),
+ current_height + grl->tip_name_height,
+ grl->tip_layout);
}
+#endif
+
return FALSE;
}
static gboolean pidgin_roomlist_create_tip(PurpleRoomlist *list, GtkTreePath *path)
{
- PidginRoomlist *grl = list->ui_data;
+ PidginRoomlist *grl = purple_roomlist_get_ui_data(list);
PurpleRoomlistRoom *room;
GtkTreeIter iter;
GValue val;
@@ -414,19 +449,23 @@ static gboolean pidgin_roomlist_create_tip(PurpleRoomlist *list, GtkTreePath *pa
gtk_tree_model_get_value(GTK_TREE_MODEL(grl->model), &iter, ROOM_COLUMN, &val);
room = g_value_get_pointer(&val);
- if (!room || !(room->type & PURPLE_ROOMLIST_ROOMTYPE_ROOM))
+ if (!room || !(purple_roomlist_room_get_room_type(room) & PURPLE_ROOMLIST_ROOMTYPE_ROOM))
return FALSE;
tooltip_text = g_string_new("");
gtk_tree_model_get(GTK_TREE_MODEL(grl->model), &iter, NAME_COLUMN, &name, -1);
- for (j = NUM_OF_COLUMNS, l = room->fields, k = list->fields; l && k; j++, l = l->next, k = k->next) {
+ for (j = NUM_OF_COLUMNS,
+ l = purple_roomlist_room_get_fields(room),
+ k = purple_roomlist_get_fields(list);
+ l && k; j++, l = l->next, k = k->next)
+ {
PurpleRoomlistField *f = k->data;
gchar *label;
- if (f->hidden)
+ if (purple_roomlist_field_get_hidden(f))
continue;
- label = g_markup_escape_text(f->label, -1);
- switch (f->type) {
+ label = g_markup_escape_text(purple_roomlist_field_get_label(f), -1);
+ switch (purple_roomlist_field_get_field_type(f)) {
case PURPLE_ROOMLIST_FIELD_BOOL:
g_string_append_printf(tooltip_text, "%s<b>%s:</b> %s", first ? "" : "\n", label, l->data ? "True" : "False");
break;
@@ -478,7 +517,7 @@ pidgin_roomlist_create_tooltip(GtkWidget *widget, GtkTreePath *path,
gpointer data, int *w, int *h)
{
PurpleRoomlist *list = data;
- PidginRoomlist *grl = list->ui_data;
+ PidginRoomlist *grl = purple_roomlist_get_ui_data(list);
grl->tipwindow = widget;
if (!pidgin_roomlist_create_tip(data, path))
return FALSE;
@@ -497,7 +536,7 @@ static gboolean account_filter_func(PurpleAccount *account)
PurplePluginProtocolInfo *prpl_info = NULL;
if (conn && PURPLE_CONNECTION_IS_CONNECTED(conn))
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(conn->prpl);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(conn));
return (prpl_info && prpl_info->roomlist_get_list != NULL);
}
@@ -528,7 +567,11 @@ pidgin_roomlist_dialog_new_with_account(PurpleAccount *account)
dialog->account = account;
/* Create the window. */
+#if GTK_CHECK_VERSION(3,0,0)
+ dialog->window = window = pidgin_create_dialog(_("Room List"), 0, "room list", TRUE);
+#else
dialog->window = window = pidgin_create_dialog(_("Room List"), PIDGIN_HIG_BORDER, "room list", TRUE);
+#endif
g_signal_connect(G_OBJECT(window), "delete_event",
G_CALLBACK(delete_win_cb), dialog);
@@ -536,7 +579,7 @@ pidgin_roomlist_dialog_new_with_account(PurpleAccount *account)
/* Create the parent vbox for everything. */
vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(window), FALSE, PIDGIN_HIG_BORDER);
- vbox2 = gtk_vbox_new(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);
@@ -622,7 +665,7 @@ static void pidgin_roomlist_new(PurpleRoomlist *list)
{
PidginRoomlist *rl = g_new0(PidginRoomlist, 1);
- list->ui_data = rl;
+ purple_roomlist_set_ui_data(list, rl);
rl->cats = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)gtk_tree_row_reference_free);
@@ -685,7 +728,7 @@ _search_func(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *it
static void pidgin_roomlist_set_fields(PurpleRoomlist *list, GList *fields)
{
- PidginRoomlist *grl = list->ui_data;
+ PidginRoomlist *grl = purple_roomlist_get_ui_data(list);
gint columns = NUM_OF_COLUMNS;
int j;
GtkTreeStore *model;
@@ -707,7 +750,7 @@ static void pidgin_roomlist_set_fields(PurpleRoomlist *list, GList *fields)
for (j = NUM_OF_COLUMNS, l = fields; l; l = l->next, j++) {
PurpleRoomlistField *f = l->data;
- switch (f->type) {
+ switch (purple_roomlist_field_get_field_type(f)) {
case PURPLE_ROOMLIST_FIELD_BOOL:
types[j] = G_TYPE_BOOLEAN;
break;
@@ -749,18 +792,19 @@ static void pidgin_roomlist_set_fields(PurpleRoomlist *list, GList *fields)
for (j = NUM_OF_COLUMNS, l = fields; l; l = l->next, j++) {
PurpleRoomlistField *f = l->data;
- if (f->hidden)
+ if (purple_roomlist_field_get_hidden(f))
continue;
renderer = gtk_cell_renderer_text_new();
- column = gtk_tree_view_column_new_with_attributes(f->label, renderer,
- "text", j, NULL);
+ column = gtk_tree_view_column_new_with_attributes(
+ purple_roomlist_field_get_label(f), renderer,
+ "text", j, 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), j);
gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column), TRUE);
- if (f->type == PURPLE_ROOMLIST_FIELD_INT) {
+ if (purple_roomlist_field_get_field_type(f) == PURPLE_ROOMLIST_FIELD_INT) {
gtk_tree_view_column_set_cell_data_func(column, renderer, int_cell_data_func,
GINT_TO_POINTER(j), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(model), j, int_sort_func,
@@ -789,12 +833,12 @@ static void pidgin_roomlist_set_fields(PurpleRoomlist *list, GList *fields)
static gboolean pidgin_progress_bar_pulse(gpointer data)
{
PurpleRoomlist *list = data;
- PidginRoomlist *rl = list->ui_data;
+ PidginRoomlist *rl = purple_roomlist_get_ui_data(list);
if (!rl || !rl->dialog || !rl->dialog->pg_needs_pulse) {
if (rl && rl->dialog)
rl->dialog->pg_update_to = 0;
- purple_roomlist_unref(list);
+ g_object_unref(list);
return FALSE;
}
@@ -805,7 +849,7 @@ static gboolean pidgin_progress_bar_pulse(gpointer data)
static void pidgin_roomlist_add_room(PurpleRoomlist *list, PurpleRoomlistRoom *room)
{
- PidginRoomlist *rl = list->ui_data;
+ PidginRoomlist *rl = purple_roomlist_get_ui_data(list);
GtkTreeRowReference *rr, *parentrr = NULL;
GtkTreePath *path;
GtkTreeIter iter, parent, child;
@@ -814,20 +858,20 @@ static void pidgin_roomlist_add_room(PurpleRoomlist *list, PurpleRoomlistRoom *r
gboolean append = TRUE;
rl->total_rooms++;
- if (room->type == PURPLE_ROOMLIST_ROOMTYPE_ROOM)
+ if (purple_roomlist_room_get_room_type(room) == PURPLE_ROOMLIST_ROOMTYPE_ROOM)
rl->num_rooms++;
if (rl->dialog) {
if (rl->dialog->pg_update_to == 0) {
- purple_roomlist_ref(list);
+ g_object_ref(list);
rl->dialog->pg_update_to = g_timeout_add(100, pidgin_progress_bar_pulse, list);
gtk_progress_bar_pulse(GTK_PROGRESS_BAR(rl->dialog->progress));
} else
rl->dialog->pg_needs_pulse = TRUE;
}
- if (room->parent) {
- parentrr = g_hash_table_lookup(rl->cats, room->parent);
+ if (purple_roomlist_room_get_parent(room)) {
+ parentrr = g_hash_table_lookup(rl->cats, purple_roomlist_room_get_parent(room));
path = gtk_tree_row_reference_get_path(parentrr);
if (path) {
PurpleRoomlistRoom *tmproom = NULL;
@@ -848,24 +892,28 @@ static void pidgin_roomlist_add_room(PurpleRoomlist *list, PurpleRoomlistRoom *r
else
iter = child;
- if (room->type & PURPLE_ROOMLIST_ROOMTYPE_CATEGORY)
+ if (purple_roomlist_room_get_room_type(room) & PURPLE_ROOMLIST_ROOMTYPE_CATEGORY)
gtk_tree_store_append(rl->model, &child, &iter);
path = gtk_tree_model_get_path(GTK_TREE_MODEL(rl->model), &iter);
- if (room->type & PURPLE_ROOMLIST_ROOMTYPE_CATEGORY) {
+ if (purple_roomlist_room_get_room_type(room) & PURPLE_ROOMLIST_ROOMTYPE_CATEGORY) {
rr = gtk_tree_row_reference_new(GTK_TREE_MODEL(rl->model), path);
g_hash_table_insert(rl->cats, room, rr);
}
gtk_tree_path_free(path);
- gtk_tree_store_set(rl->model, &iter, NAME_COLUMN, room->name, -1);
+ gtk_tree_store_set(rl->model, &iter, NAME_COLUMN, purple_roomlist_room_get_name(room), -1);
gtk_tree_store_set(rl->model, &iter, ROOM_COLUMN, room, -1);
- for (j = NUM_OF_COLUMNS, l = room->fields, k = list->fields; l && k; j++, l = l->next, k = k->next) {
+ for (j = NUM_OF_COLUMNS,
+ l = purple_roomlist_room_get_fields(room),
+ k = purple_roomlist_get_fields(list);
+ l && k; j++, l = l->next, k = k->next)
+ {
PurpleRoomlistField *f = k->data;
- if (f->hidden)
+ if (purple_roomlist_field_get_hidden(f))
continue;
gtk_tree_store_set(rl->model, &iter, j, l->data, -1);
}
@@ -873,7 +921,7 @@ static void pidgin_roomlist_add_room(PurpleRoomlist *list, PurpleRoomlistRoom *r
static void pidgin_roomlist_in_progress(PurpleRoomlist *list, gboolean in_progress)
{
- PidginRoomlist *rl = list->ui_data;
+ PidginRoomlist *rl = purple_roomlist_get_ui_data(list);
if (!rl || !rl->dialog)
return;
@@ -895,7 +943,7 @@ static void pidgin_roomlist_in_progress(PurpleRoomlist *list, gboolean in_progre
static void pidgin_roomlist_destroy(PurpleRoomlist *list)
{
- PidginRoomlist *rl = list->ui_data;
+ PidginRoomlist *rl = purple_roomlist_get_ui_data(list);
roomlists = g_list_remove(roomlists, list);
@@ -903,7 +951,7 @@ static void pidgin_roomlist_destroy(PurpleRoomlist *list)
g_hash_table_destroy(rl->cats);
g_free(rl);
- list->ui_data = NULL;
+ purple_roomlist_set_ui_data(list, NULL);
}
static PurpleRoomlistUiOps ops = {
diff --git a/pidgin/gtkroomlist.h b/pidgin/gtkroomlist.h
index ed905e5c6b..af8ae75858 100644
--- a/pidgin/gtkroomlist.h
+++ b/pidgin/gtkroomlist.h
@@ -1,8 +1,3 @@
-/**
- * @file gtkroomlist.h GTK+ Room List UI
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -23,34 +18,52 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _PIDGINROOMLIST_H_
#define _PIDGINROOMLIST_H_
+/**
+ * SECTION:gtkroomlist
+ * @section_id: pidgin-gtkroomlist
+ * @short_description: <filename>gtkroomlist.h</filename>
+ * @title: Room List UI
+ */
#include "roomlist.h"
+G_BEGIN_DECLS
+
/**
+ * pidgin_roomlist_init:
+ *
* Initializes the room list subsystem.
*/
void pidgin_roomlist_init(void);
/**
+ * pidgin_roomlist_is_showable:
+ *
* Determines if showing the room list dialog is a valid action.
*
- * @return TRUE if there are accounts online that support listing
+ * Returns: TRUE if there are accounts online that support listing
* chat rooms. Otherwise return FALSE.
*/
gboolean pidgin_roomlist_is_showable(void);
/**
+ * pidgin_roomlist_dialog_show:
+ *
* Shows a new roomlist dialog.
*/
void pidgin_roomlist_dialog_show(void);
/**
- * Shows a new room list dialog and fetches the list for the specified account.
+ * pidgin_roomlist_dialog_show_with_account:
+ * @account: The account to use.
*
- * @param account The account to use.
+ * Shows a new room list dialog and fetches the list for the specified account.
*/
void pidgin_roomlist_dialog_show_with_account(PurpleAccount *account);
+G_END_DECLS
+
#endif /* _PIDGINROOMLIST_H_ */
diff --git a/pidgin/gtksavedstatuses.c b/pidgin/gtksavedstatuses.c
index c82c8317f3..e304498b05 100644
--- a/pidgin/gtksavedstatuses.c
+++ b/pidgin/gtksavedstatuses.c
@@ -1,8 +1,3 @@
-/**
- * @file gtksavedstatus.c GTK+ Saved Status Editor UI
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -35,11 +30,12 @@
#include "gtkblist.h"
#include "pidgin.h"
-#include "gtkimhtml.h"
-#include "gtkimhtmltoolbar.h"
#include "gtksavedstatuses.h"
#include "pidginstock.h"
#include "gtkutils.h"
+#include "gtkwebview.h"
+
+#include "gtk3compat.h"
/*
* TODO: Should attach to the account-deleted and account-added signals
@@ -47,7 +43,7 @@
* may be open.
*/
-/**
+/*
* These are used for the GtkTreeView when you're scrolling through
* all your saved statuses.
*/
@@ -62,21 +58,21 @@ enum
STATUS_WINDOW_NUM_COLUMNS
};
-/**
+/*
* These are used for the GtkTreeView containing the list of accounts
* at the bottom of the window when you're editing a particular
* saved status.
*/
enum
{
- /** A hidden column containing a pointer to the PurpleAccount. */
+ /* A hidden column containing a pointer to the PurpleAccount. */
STATUS_EDITOR_COLUMN_ACCOUNT,
- /** A hidden column containing a pointer to the editor for this substatus. */
+ /* A hidden column containing a pointer to the editor for this substatus. */
STATUS_EDITOR_COLUMN_WINDOW,
STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS,
STATUS_EDITOR_COLUMN_ICON,
STATUS_EDITOR_COLUMN_USERNAME,
- /** A hidden column containing the ID of this PurpleStatusType. */
+ /* A hidden column containing the ID of this PurpleStatusType. */
STATUS_EDITOR_COLUMN_STATUS_ID,
STATUS_EDITOR_COLUMN_STATUS_NAME,
STATUS_EDITOR_COLUMN_STATUS_MESSAGE,
@@ -84,7 +80,7 @@ enum
STATUS_EDITOR_NUM_COLUMNS
};
-/**
+/*
* These are used in the GtkComboBox to select the specific PurpleStatusType
* when setting a (sub)status for a particular saved status.
*/
@@ -118,7 +114,7 @@ typedef struct
gchar *original_title;
GtkEntry *title;
GtkComboBox *type;
- GtkIMHtml *message;
+ PidginWebView *message;
} StatusEditor;
typedef struct
@@ -129,8 +125,7 @@ typedef struct
GtkWidget *window;
GtkListStore *model;
GtkComboBox *box;
- GtkIMHtml *message;
- GtkIMHtmlToolbar *toolbar;
+ PidginWebView *message;
} SubStatusEditor;
static StatusWindow *status_window = NULL;
@@ -311,7 +306,7 @@ status_window_delete_cb(GtkButton *button, gpointer user_data)
}
purple_request_action(handle, NULL, title, NULL, 0,
- NULL, NULL, NULL,
+ NULL,
sel_titles, 2,
_("Delete"), status_window_delete_confirm_cb,
_("Cancel"), status_window_delete_cancel_cb);
@@ -379,9 +374,9 @@ add_status_to_saved_status_list(GtkListStore *model, PurpleSavedStatus *saved_st
return;
title = purple_savedstatus_get_title(saved_status);
- type = purple_primitive_get_name_from_type(purple_savedstatus_get_type(saved_status));
+ type = purple_primitive_get_name_from_type(purple_savedstatus_get_primitive_type(saved_status));
message = purple_markup_strip_html(purple_savedstatus_get_message(saved_status));
- icon = get_stock_icon_from_primitive(purple_savedstatus_get_type(saved_status));
+ icon = get_stock_icon_from_primitive(purple_savedstatus_get_primitive_type(saved_status));
gtk_list_store_append(model, &iter);
gtk_list_store_set(model, &iter,
@@ -524,7 +519,7 @@ create_saved_status_list(StatusWindow *dialog)
static gboolean
configure_cb(GtkWidget *widget, GdkEventConfigure *event, StatusWindow *dialog)
{
- if (GTK_WIDGET_VISIBLE(widget))
+ if (gtk_widget_get_visible(widget))
{
purple_prefs_set_int(PIDGIN_PREFS_ROOT "/status/dialog/width", event->width);
purple_prefs_set_int(PIDGIN_PREFS_ROOT "/status/dialog/height", event->height);
@@ -726,12 +721,12 @@ status_editor_ok_cb(GtkButton *button, gpointer user_data)
((dialog->original_title == NULL) || (strcmp(title, dialog->original_title))))
{
purple_notify_error(status_window, NULL, _("Title already in use. You must "
- "choose a unique title."), NULL);
+ "choose a unique title."), NULL, NULL);
return;
}
type = gtk_combo_box_get_active(dialog->type) + (PURPLE_STATUS_UNSET + 1);
- message = gtk_imhtml_get_markup(dialog->message);
+ message = pidgin_webview_get_body_html(dialog->message);
unformatted = purple_markup_strip_html(message);
/*
@@ -763,7 +758,7 @@ status_editor_ok_cb(GtkButton *button, gpointer user_data)
/* Modify the old status */
if (strcmp(title, dialog->original_title))
purple_savedstatus_set_title(saved_status, title);
- purple_savedstatus_set_type(saved_status, type);
+ purple_savedstatus_set_primitive_type(saved_status, type);
}
if (*unformatted == '\0')
@@ -1012,7 +1007,7 @@ status_editor_set_account(GtkListStore *store, PurpleAccount *account,
{
const PurpleStatusType *type;
- type = purple_savedstatus_substatus_get_type(substatus);
+ type = purple_savedstatus_substatus_get_status_type(substatus);
id = purple_status_type_get_id(type);
name = purple_status_type_get_name(type);
prim = purple_status_type_get_primitive(type);
@@ -1082,7 +1077,6 @@ pidgin_status_editor_show(gboolean edit, PurpleSavedStatus *saved_status)
GtkWidget *frame;
GtkWidget *hbox;
GtkWidget *text;
- GtkWidget *toolbar;
GtkWidget *vbox;
GtkWidget *win;
GList *focus_chain = NULL;
@@ -1140,33 +1134,31 @@ pidgin_status_editor_show(gboolean edit, PurpleSavedStatus *saved_status)
/* Status type */
if (saved_status != NULL)
- dropdown = create_status_type_menu(purple_savedstatus_get_type(saved_status));
+ dropdown = create_status_type_menu(purple_savedstatus_get_primitive_type(saved_status));
else
dropdown = create_status_type_menu(PURPLE_STATUS_AWAY);
dialog->type = GTK_COMBO_BOX(dropdown);
pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Status:"), sg, dropdown, TRUE, NULL);
/* Status message */
- frame = pidgin_create_imhtml(TRUE, &text, &toolbar, NULL);
- dialog->message = GTK_IMHTML(text);
+ frame = pidgin_create_webview(TRUE, &text, NULL);
+ dialog->message = PIDGIN_WEBVIEW(text);
hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Message:"), sg, frame, TRUE, NULL);
gtk_container_child_set(GTK_CONTAINER(vbox), hbox, "expand", TRUE, "fill", TRUE, NULL);
focus_chain = g_list_prepend(focus_chain, dialog->message);
gtk_container_set_focus_chain(GTK_CONTAINER(hbox), focus_chain);
g_list_free(focus_chain);
- gtk_imhtml_set_return_inserts_newline(dialog->message);
-
if ((saved_status != NULL) && (purple_savedstatus_get_message(saved_status) != NULL))
- gtk_imhtml_append_text(GTK_IMHTML(text),
- purple_savedstatus_get_message(saved_status), 0);
+ pidgin_webview_append_html(PIDGIN_WEBVIEW(text),
+ purple_savedstatus_get_message(saved_status));
/* Different status message expander */
expander = gtk_expander_new_with_mnemonic(_("Use a _different status for some accounts"));
gtk_box_pack_start(GTK_BOX(vbox), expander, FALSE, FALSE, 0);
/* Setup the box that the expander will cover */
- dbox = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
+ dbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_CAT_SPACE);
gtk_container_add(GTK_CONTAINER(expander), dbox);
/* Create the list model */
@@ -1260,12 +1252,10 @@ substatus_selection_changed_cb(GtkComboBox *box, gpointer user_data)
if (purple_status_type_get_attr(type, "message") == NULL)
{
gtk_widget_set_sensitive(GTK_WIDGET(select->message), FALSE);
- gtk_widget_set_sensitive(GTK_WIDGET(select->toolbar), FALSE);
}
else
{
gtk_widget_set_sensitive(GTK_WIDGET(select->message), TRUE);
- gtk_widget_set_sensitive(GTK_WIDGET(select->toolbar), TRUE);
}
}
@@ -1347,7 +1337,7 @@ substatus_editor_ok_cb(GtkButton *button, gpointer user_data)
-1);
type = purple_account_get_status_type(dialog->account, id);
if (purple_status_type_get_attr(type, "message") != NULL)
- message = gtk_imhtml_get_markup(GTK_IMHTML(dialog->message));
+ message = pidgin_webview_get_body_html(PIDGIN_WEBVIEW(dialog->message));
name = purple_status_type_get_name(type);
stock = get_stock_icon_from_primitive(purple_status_type_get_primitive(type));
@@ -1381,7 +1371,6 @@ edit_substatus(StatusEditor *status_editor, PurpleAccount *account)
GtkWidget *frame;
GtkWidget *label;
GtkWidget *text;
- GtkWidget *toolbar;
GtkWidget *vbox;
GtkWidget *win;
GtkTreeIter iter;
@@ -1425,7 +1414,7 @@ edit_substatus(StatusEditor *status_editor, PurpleAccount *account)
sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
/* Status type */
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
label = gtk_label_new_with_mnemonic(_("_Status:"));
@@ -1459,7 +1448,7 @@ edit_substatus(StatusEditor *status_editor, PurpleAccount *account)
gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0);
/* Status mesage */
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
label = gtk_label_new_with_mnemonic(_("_Message:"));
@@ -1467,9 +1456,8 @@ edit_substatus(StatusEditor *status_editor, PurpleAccount *account)
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
gtk_size_group_add_widget(sg, label);
- frame = pidgin_create_imhtml(TRUE, &text, &toolbar, NULL);
- dialog->message = GTK_IMHTML(text);
- dialog->toolbar = GTK_IMHTMLTOOLBAR(toolbar);
+ frame = pidgin_create_webview(TRUE, &text, NULL);
+ dialog->message = PIDGIN_WEBVIEW(text);
gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0);
/* Cancel button */
@@ -1496,14 +1484,15 @@ edit_substatus(StatusEditor *status_editor, PurpleAccount *account)
if ((saved_status = purple_savedstatus_find(status_editor->original_title)) != NULL) {
if ((substatus = purple_savedstatus_get_substatus(saved_status, account)) != NULL) {
message = (char *)purple_savedstatus_substatus_get_message(substatus);
- status_id = (char *)purple_status_type_get_id(purple_savedstatus_substatus_get_type(substatus));
+ status_id = (char *)purple_status_type_get_id(
+ purple_savedstatus_substatus_get_status_type(substatus));
}
}
}
/* TODO: Else get the generic status type from our parent */
if (message)
- gtk_imhtml_append_text(dialog->message, message, 0);
+ pidgin_webview_append_html(dialog->message, message);
for (list = purple_account_get_status_types(account); list; list = list->next)
{
@@ -1562,31 +1551,31 @@ enum {
};
enum {
- /** _SSMenuEntryType */
+ /* _SSMenuEntryType */
SS_MENU_TYPE_COLUMN,
- /**
+ /*
* This is a GdkPixbuf (the other columns are strings).
* This column is visible.
*/
SS_MENU_ICON_COLUMN,
- /** The text displayed on the status box. This column is visible. */
+ /* The text displayed on the status box. This column is visible. */
SS_MENU_TEXT_COLUMN,
- /**
+ /*
* This value depends on SS_MENU_TYPE_COLUMN. For _SAVEDSTATUS types,
* this is the creation time. For _PRIMITIVE types,
* this is the PurpleStatusPrimitive.
*/
SS_MENU_DATA_COLUMN,
- /**
+ /*
* This is the emblem to use for this status
*/
SS_MENU_EMBLEM_COLUMN,
- /**
+ /*
* And whether or not that emblem is visible
*/
SS_MENU_EMBLEM_VISIBLE_COLUMN,
@@ -1649,7 +1638,7 @@ static gboolean pidgin_status_menu_add_primitive(GtkListStore *model, GtkWidget
if (purple_savedstatus_is_transient(current_status)
&& !purple_savedstatus_has_substatuses(current_status)
- && purple_savedstatus_get_type(current_status) == primitive)
+ && purple_savedstatus_get_primitive_type(current_status) == primitive)
currently_selected = TRUE;
return currently_selected;
@@ -1664,7 +1653,7 @@ pidgin_status_menu_update_iter(GtkWidget *combobox, GtkListStore *store, GtkTree
if (store == NULL)
store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox)));
- primitive = purple_savedstatus_get_type(status);
+ primitive = purple_savedstatus_get_primitive_type(status);
gtk_list_store_set(store, iter,
SS_MENU_TYPE_COLUMN, SS_MENU_ENTRY_TYPE_SAVEDSTATUS,
SS_MENU_ICON_COLUMN, pidgin_stock_id_from_status_primitive(primitive),
diff --git a/pidgin/gtksavedstatuses.h b/pidgin/gtksavedstatuses.h
index 144fd052c7..566994748d 100644
--- a/pidgin/gtksavedstatuses.h
+++ b/pidgin/gtksavedstatuses.h
@@ -1,8 +1,3 @@
-/**
- * @file gtksavedstatuses.h GTK+ Saved Status Editor UI
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -23,66 +18,90 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _PIDGINSAVEDSTATUSES_H_
#define _PIDGINSAVEDSTATUSES_H_
+/**
+ * SECTION:gtksavedstatuses
+ * @section_id: pidgin-gtksavedstatuses
+ * @short_description: <filename>gtksavedstatuses.h</filename>
+ * @title: Saved Status Editor UI
+ */
#include "savedstatuses.h"
#include "status.h"
+G_BEGIN_DECLS
+
/**
+ * pidgin_status_window_show:
+ *
* Shows the status window.
*/
void pidgin_status_window_show(void);
/**
+ * pidgin_status_window_hide:
+ *
* Hides the status window.
*/
void pidgin_status_window_hide(void);
/**
- * Shows a status editor (used for adding a new saved status or
- * editing an already existing saved status).
- *
- * @param edit TRUE if we want to edit an existing saved
- * status or FALSE to create a new one. You
+ * pidgin_status_editor_show:
+ * @edit: %TRUE if we want to edit an existing saved
+ * status or %FALSE to create a new one. You
* can not edit transient statuses--they don't
* have titles. If you want to edit a transient
- * status, set this to FALSE and seed the dialog
+ * status, set this to %FALSE and seed the dialog
* with the transient status using the status
* parameter to this function.
- * @param status If edit is TRUE then this should be a
+ * @status: If edit is %TRUE then this should be a
* pointer to the PurpleSavedStatus to edit.
- * If edit is FALSE then this can be NULL,
+ * If edit is %FALSE then this can be NULL,
* or you can pass in a saved status to
* seed the initial values of the new status.
+ *
+ * Shows a status editor (used for adding a new saved status or
+ * editing an already existing saved status).
*/
void pidgin_status_editor_show(gboolean edit, PurpleSavedStatus *status);
/**
+ * pidgin_status_menu:
+ * @status: The default saved_status to show as 'selected'
+ * @callback: The callback to call when the selection changes
+ *
* Creates a dropdown menu of saved statuses and calls a callback
* when one is selected
*
- * @param status The default saved_status to show as 'selected'
- * @param callback The callback to call when the selection changes
- * @return The menu widget
+ * Returns: The menu widget
*/
GtkWidget *pidgin_status_menu(PurpleSavedStatus *status, GCallback callback);
/**
+ * pidgin_status_get_handle:
+ *
* Returns the GTK+ status handle.
*
- * @return The handle to the GTK+ status system.
+ * Returns: The handle to the GTK+ status system.
*/
void *pidgin_status_get_handle(void);
/**
+ * pidgin_status_init:
+ *
* Initializes the GTK+ status system.
*/
void pidgin_status_init(void);
/**
+ * pidgin_status_uninit:
+ *
* Uninitializes the GTK+ status system.
*/
void pidgin_status_uninit(void);
+G_END_DECLS
+
#endif /* _PIDGINSAVEDSTATUSES_H_ */
diff --git a/pidgin/gtkscrollbook.c b/pidgin/gtkscrollbook.c
index aa499932ed..cc36d2e1ea 100644
--- a/pidgin/gtkscrollbook.c
+++ b/pidgin/gtkscrollbook.c
@@ -1,8 +1,3 @@
-/*
- * @file gtkscrollbook.c GTK+ Scrolling notebook widget
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -26,6 +21,7 @@
#include "gtkscrollbook.h"
+#include "gtk3compat.h"
static void pidgin_scroll_book_init (PidginScrollBook *scroll_book);
static void pidgin_scroll_book_class_init (PidginScrollBookClass *klass);
@@ -102,7 +98,7 @@ refresh_scroll_box(PidginScrollBook *scroll_book, int index, int count)
gtk_widget_show_all(GTK_WIDGET(scroll_book));
if (count < 1)
- gtk_widget_hide_all(scroll_book->hbox);
+ gtk_widget_hide(scroll_book->hbox);
else {
gtk_widget_show_all(scroll_book->hbox);
if (count == 1) {
@@ -147,7 +143,7 @@ scroll_close_cb(PidginScrollBook *scroll_book, GdkEventButton *event)
}
static void
-switch_page_cb(GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, PidginScrollBook *scroll_book)
+switch_page_cb(GtkNotebook *notebook, GtkWidget *page, guint page_num, PidginScrollBook *scroll_book)
{
int count;
count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(scroll_book->notebook));
@@ -160,7 +156,7 @@ pidgin_scroll_book_add(GtkContainer *container, GtkWidget *widget)
PidginScrollBook *scroll_book;
g_return_if_fail(GTK_IS_WIDGET (widget));
- g_return_if_fail (widget->parent == NULL);
+ g_return_if_fail(gtk_widget_get_parent(widget) == NULL);
scroll_book = PIDGIN_SCROLL_BOOK(container);
scroll_book->children = g_list_append(scroll_book->children, widget);
@@ -260,7 +256,7 @@ pidgin_scroll_book_init (PidginScrollBook *scroll_book)
GtkWidget *eb;
GtkWidget *close_button;
- scroll_book->hbox = gtk_hbox_new(FALSE, 0);
+ scroll_book->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
/* Close */
eb = gtk_event_box_new();
diff --git a/pidgin/gtkscrollbook.h b/pidgin/gtkscrollbook.h
index 4dec336e87..1568de8813 100644
--- a/pidgin/gtkscrollbook.h
+++ b/pidgin/gtkscrollbook.h
@@ -1,8 +1,3 @@
-/*
- * @file gtkscrollbook GTK+ Scrolling notebook Widget
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -26,6 +21,12 @@
#ifndef __PIDGIN_SCROLL_BOOK_H__
#define __PIDGIN_SCROLL_BOOK_H__
+/**
+ * SECTION:gtkscrollbook
+ * @section_id: pidgin-gtkscrollbook
+ * @short_description: <filename>gtkscrollbook.h</filename>
+ * @title: Scrolling Notebook Widget
+ */
#include <gtk/gtk.h>
@@ -52,7 +53,7 @@ struct _PidginScrollBook
GtkWidget *right_arrow;
GList *children;
- /* Padding for future expansion */
+ /*< private >*/
void (*_gtk_reserved1) (void);
void (*_gtk_reserved2) (void);
void (*_gtk_reserved3) (void);
@@ -64,7 +65,7 @@ struct _PidginScrollBookClass
{
GtkContainerClass parent_class;
- /* Padding for future expansion */
+ /*< private >*/
void (*_gtk_reserved0) (void);
void (*_gtk_reserved1) (void);
void (*_gtk_reserved2) (void);
diff --git a/pidgin/gtksession.c b/pidgin/gtksession.c
index d65608ca95..e3c6ceda0a 100644
--- a/pidgin/gtksession.c
+++ b/pidgin/gtksession.c
@@ -1,8 +1,3 @@
-/*
- * @file gtksession.c X Windows session management API
- * @ingroup 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.
@@ -39,6 +34,8 @@
#include <gdk/gdk.h>
#include <gtk/gtk.h>
+#include "gtk3compat.h"
+
#define ERROR_LENGTH 512
static IceIOErrorHandler ice_installed_io_error_handler;
@@ -70,7 +67,10 @@ static void ice_process_messages(gpointer data, gint fd,
IceSetShutdownNegotiation(conninfo->connection, False);
IceCloseConnection(conninfo->connection);
- purple_debug(PURPLE_DEBUG_INFO, NULL, "done.\n");
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("Session Management",
+ "Connection closed.");
+ }
/* cancel the handler */
purple_input_remove(conninfo->input_id);
@@ -82,8 +82,8 @@ static void ice_connection_watch(IceConn connection, IcePointer client_data,
struct ice_connection_info *conninfo = NULL;
if (opening) {
- purple_debug(PURPLE_DEBUG_INFO, "Session Management",
- "Handling new ICE connection... \n");
+ purple_debug_misc("Session Management",
+ "Handling new ICE connection...");
/* ensure ICE connection is not passed to child processes */
if (fcntl(IceConnectionNumber(connection), F_SETFD, FD_CLOEXEC) != 0)
@@ -106,7 +106,10 @@ static void ice_connection_watch(IceConn connection, IcePointer client_data,
g_free(conninfo);
}
- purple_debug(PURPLE_DEBUG_INFO, NULL, "done.\n");
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("Session Management",
+ "ICE connection handled.");
+ }
}
/* We call any handler installed before (or after) ice_init but
@@ -123,7 +126,10 @@ static void ice_io_error_handler(IceConn connection) {
if (ice_installed_io_error_handler)
(*ice_installed_io_error_handler)(connection);
- purple_debug(PURPLE_DEBUG_INFO, NULL, "done.\n");
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("Session Management",
+ "ICE IO error handled.");
+ }
}
static void ice_init(void) {
@@ -137,8 +143,9 @@ static void ice_init(void) {
IceAddConnectionWatch(ice_connection_watch, NULL);
- purple_debug(PURPLE_DEBUG_INFO, "Session Management",
- "ICE initialized.\n");
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("Session Management", "ICE initialized.");
+ }
}
/* my magic utility function */
@@ -350,7 +357,7 @@ pidgin_session_init(gchar *argv0, gchar *previous_id, gchar *config_dir)
free(tmp);
session_managed = TRUE;
- gdk_set_sm_client_id(client_id);
+ gdk_x11_set_sm_client_id(client_id);
tmp = g_get_current_dir();
session_set_string(session, SmCurrentDirectory, tmp);
diff --git a/pidgin/gtksession.h b/pidgin/gtksession.h
index fb7711329e..feaddd6de2 100644
--- a/pidgin/gtksession.h
+++ b/pidgin/gtksession.h
@@ -1,8 +1,3 @@
-/**
- * @file gtksession.h X Windows session management API
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -23,32 +18,43 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _PIDGINSESSION_H_
#define _PIDGINSESSION_H_
+/**
+ * SECTION:gtksession
+ * @section_id: pidgin-gtksession
+ * @short_description: <filename>gtksession.h</filename>
+ * @title: X Windows Session Management
+ */
+
+G_BEGIN_DECLS
/**************************************************************************/
-/** @name X Windows session subsystem */
+/* X Windows session subsystem */
/**************************************************************************/
-/*@{*/
/**
- * Register this instance of Pidgin with the user's current session
- * manager.
- *
- * @param argv0 The first argument passed into the program. This
+ * pidgin_session_init:
+ * @argv0: The first argument passed into the program. This
* will be the name of the executable, e.g. 'purple'
- * @param previous_id An optional session ID to use. This can be NULL.
- * @param config_dir The path to the configuration directory used by
+ * @previous_id: An optional session ID to use. This can be NULL.
+ * @config_dir: The path to the configuration directory used by
* this instance of this program, e.g. '/home/user/.purple'
+ *
+ * Register this instance of Pidgin with the user's current session
+ * manager.
*/
void pidgin_session_init(gchar *argv0, gchar *previous_id, gchar *config_dir);
/**
+ * pidgin_session_end:
+ *
* Unregister this instance of Pidgin with the user's current session
* manager.
*/
void pidgin_session_end(void);
-/*@}*/
+G_END_DECLS
#endif /* _PIDGINSESSION_H_ */
diff --git a/pidgin/gtksmiley-manager.c b/pidgin/gtksmiley-manager.c
new file mode 100644
index 0000000000..9e2fd3dc22
--- /dev/null
+++ b/pidgin/gtksmiley-manager.c
@@ -0,0 +1,831 @@
+/*
+ * 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 "http.h"
+#include "notify.h"
+#include "smiley.h"
+#include "smiley-custom.h"
+#include "smiley-list.h"
+
+#include "gtksmiley-manager.h"
+#include "gtkutils.h"
+#include "pidginstock.h"
+
+#include "gtk3compat.h"
+
+typedef struct
+{
+ PurpleSmiley *smiley;
+
+ gchar *filename;
+ PurpleImage *new_image;
+
+ GtkDialog *window;
+ GtkImage *thumbnail;
+ GtkEntry *shortcut;
+} SmileyEditDialog;
+
+typedef struct
+{
+ GtkDialog *window;
+ GtkListStore *model;
+ GtkTreeView *tree;
+
+ PurpleHttpConnection *running_request;
+} SmileyManager;
+
+enum
+{
+ SMILEY_LIST_MODEL_ICON,
+ SMILEY_LIST_MODEL_SHORTCUT,
+ SMILEY_LIST_MODEL_PURPLESMILEY,
+ SMILEY_LIST_MODEL_N_COL
+};
+
+enum
+{
+ PIDGIN_RESPONSE_MODIFY
+};
+
+static SmileyManager *smiley_manager = NULL;
+
+static void
+edit_dialog_update_buttons(SmileyEditDialog *edit_dialog);
+
+static void
+manager_list_fill(SmileyManager *manager);
+
+
+/*******************************************************************************
+ * Custom smiley edit dialog image.
+ ******************************************************************************/
+
+static void
+edit_dialog_image_update_thumb(SmileyEditDialog *edit_dialog)
+{
+ GdkPixbuf *pixbuf = NULL;
+
+ if (edit_dialog->new_image) {
+ pixbuf = pidgin_pixbuf_from_image(edit_dialog->new_image);
+ } else if (edit_dialog->filename) {
+ pixbuf = pidgin_pixbuf_new_from_file(edit_dialog->filename);
+ if (!pixbuf) {
+ g_free(edit_dialog->filename);
+ edit_dialog->filename = NULL;
+ }
+ }
+
+ if (pixbuf) {
+ pixbuf = pidgin_pixbuf_scale_down(pixbuf, 64, 64,
+ GDK_INTERP_HYPER, TRUE);
+ }
+
+ if (!pixbuf) {
+ GtkIconSize icon_size =
+ gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_SMALL);
+ pixbuf = gtk_widget_render_icon(GTK_WIDGET(edit_dialog->window),
+ PIDGIN_STOCK_TOOLBAR_SELECT_AVATAR, icon_size,
+ "PidginSmileyManager");
+ }
+ g_return_if_fail(pixbuf != NULL);
+
+ gtk_image_set_from_pixbuf(GTK_IMAGE(edit_dialog->thumbnail), pixbuf);
+
+ g_object_unref(G_OBJECT(pixbuf));
+}
+
+static gboolean
+edit_dialog_set_image(SmileyEditDialog *edit_dialog, PurpleImage *image)
+{
+ GdkPixbuf *tmp = NULL;
+
+ if (edit_dialog->new_image)
+ g_object_unref(edit_dialog->new_image);
+
+ if (edit_dialog->smiley) {
+ g_object_set_data(G_OBJECT(edit_dialog->smiley),
+ "pidgin-smiley-manager-list-thumb", NULL);
+ }
+
+ /* check, if image is valid */
+ if (image)
+ tmp = pidgin_pixbuf_from_image(image);
+ if (tmp)
+ g_object_unref(tmp);
+ else {
+ g_object_unref(image);
+ image = NULL;
+ }
+
+ edit_dialog->new_image = image;
+
+ edit_dialog_image_update_thumb(edit_dialog);
+ edit_dialog_update_buttons(edit_dialog);
+
+ return (image != NULL);
+}
+
+static void
+edit_dialog_set_shortcut(SmileyEditDialog *edit_dialog,
+ const gchar *shortcut)
+{
+ gtk_entry_set_text(edit_dialog->shortcut, shortcut ? shortcut : "");
+}
+
+static void
+edit_dialog_image_choosen(const char *filename, gpointer _edit_dialog)
+{
+ PurpleImage *image;
+ SmileyEditDialog *edit_dialog = _edit_dialog;
+
+ if (!filename)
+ return;
+
+ image = purple_image_new_from_file(filename, TRUE);
+ if (!image)
+ return;
+
+ g_free(edit_dialog->filename);
+ edit_dialog->filename = NULL;
+
+ if (!edit_dialog_set_image(edit_dialog, image))
+ return;
+ edit_dialog->filename = g_strdup(filename);
+
+ gtk_widget_grab_focus(GTK_WIDGET(edit_dialog->shortcut));
+}
+
+static void
+edit_dialog_image_choose(GtkWidget *widget, gpointer _edit_dialog)
+{
+ GtkWidget *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);
+}
+
+
+/*******************************************************************************
+ * Custom smiley edit dialog.
+ ******************************************************************************/
+
+static void
+edit_dialog_destroy(GtkWidget *window, gpointer _edit_dialog)
+{
+ SmileyEditDialog *edit_dialog = _edit_dialog;
+
+ if (edit_dialog->smiley) {
+ g_object_set_data(G_OBJECT(edit_dialog->smiley),
+ "pidgin-smiley-manager-edit-dialog", NULL);
+ g_object_unref(edit_dialog->smiley);
+ }
+
+ if (edit_dialog->new_image)
+ g_object_unref(edit_dialog->new_image);
+
+ g_free(edit_dialog->filename);
+ g_free(edit_dialog);
+}
+
+static void
+edit_dialog_save(SmileyEditDialog *edit_dialog)
+{
+ const gchar *shortcut;
+ PurpleSmiley *existing_smiley;
+ gboolean shortcut_changed, image_changed;
+
+ shortcut = gtk_entry_get_text(edit_dialog->shortcut);
+
+ existing_smiley = purple_smiley_list_get_by_shortcut(
+ purple_smiley_custom_get_list(), shortcut);
+
+ if (existing_smiley && existing_smiley != edit_dialog->smiley) {
+ gchar *msg = g_strdup_printf(
+ _("A custom smiley for '%s' already exists. "
+ "Please use a different shortcut."), shortcut);
+ purple_notify_error(edit_dialog, _("Custom Smiley"),
+ _("Duplicate Shortcut"), msg, NULL);
+ g_free(msg);
+ return;
+ }
+
+ if (edit_dialog->smiley == NULL)
+ shortcut_changed = image_changed = TRUE;
+ else {
+ shortcut_changed = (g_strcmp0(purple_smiley_get_shortcut(
+ edit_dialog->smiley), shortcut) != 0);
+ image_changed = (edit_dialog->new_image != NULL);
+ }
+
+ if (!shortcut_changed && !image_changed) {
+ gtk_widget_destroy(GTK_WIDGET(edit_dialog->window));
+ return;
+ }
+
+ if (edit_dialog->new_image == NULL) {
+ edit_dialog->new_image =
+ purple_smiley_get_image(edit_dialog->smiley);
+ g_return_if_fail(edit_dialog->new_image);
+ }
+
+ if (edit_dialog->smiley)
+ purple_smiley_custom_remove(edit_dialog->smiley);
+ purple_smiley_custom_add(edit_dialog->new_image, shortcut);
+
+ if (smiley_manager)
+ manager_list_fill(smiley_manager);
+
+ gtk_widget_destroy(GTK_WIDGET(edit_dialog->window));
+}
+
+static void
+edit_dialog_update_buttons(SmileyEditDialog *edit_dialog)
+{
+ gboolean shortcut_ok, image_ok;
+
+ shortcut_ok = (gtk_entry_get_text_length(edit_dialog->shortcut) > 0);
+ image_ok = (edit_dialog->filename || edit_dialog->new_image);
+
+ gtk_dialog_set_response_sensitive(edit_dialog->window,
+ GTK_RESPONSE_ACCEPT, shortcut_ok && image_ok);
+}
+
+static void
+edit_dialog_shortcut_changed(GtkEditable *shortcut, gpointer _edit_dialog)
+{
+ SmileyEditDialog *edit_dialog = _edit_dialog;
+
+ edit_dialog_update_buttons(edit_dialog);
+}
+
+static void
+edit_dialog_response(GtkDialog *window, gint response_id,
+ gpointer _edit_dialog)
+{
+ SmileyEditDialog *edit_dialog = _edit_dialog;
+
+ switch (response_id) {
+ case GTK_RESPONSE_ACCEPT:
+ edit_dialog_save(edit_dialog);
+ break;
+ case GTK_RESPONSE_DELETE_EVENT:
+ case GTK_RESPONSE_CANCEL:
+ gtk_widget_destroy(GTK_WIDGET(edit_dialog->window));
+ break;
+ default:
+ g_warn_if_reached();
+ }
+}
+
+static SmileyEditDialog *
+edit_dialog_show(SmileyManager *manager, PurpleSmiley *smiley)
+{
+ SmileyEditDialog *edit_dialog;
+ GtkWidget *vbox, *hbox;
+ GtkLabel *label;
+ GtkButton *filech;
+
+ if (smiley) {
+ edit_dialog = g_object_get_data(G_OBJECT(smiley),
+ "pidgin-smiley-manager-edit-dialog");
+ if (edit_dialog) {
+ gtk_window_present(GTK_WINDOW(edit_dialog->window));
+ return edit_dialog;
+ }
+ }
+
+ edit_dialog = g_new0(SmileyEditDialog, 1);
+
+ edit_dialog->window = GTK_DIALOG(gtk_dialog_new_with_buttons(
+ smiley ? _("Edit Smiley") : _("Add Smiley"),
+ manager ? GTK_WINDOW(manager->window) : NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ smiley ? GTK_STOCK_SAVE : GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT,
+ NULL));
+ gtk_dialog_set_default_response(
+ edit_dialog->window, GTK_RESPONSE_ACCEPT);
+
+ if (smiley) {
+ edit_dialog->smiley = smiley;
+ g_object_set_data(G_OBJECT(smiley),
+ "pidgin-smiley-manager-edit-dialog", edit_dialog);
+ g_object_ref(smiley);
+ }
+
+#if !GTK_CHECK_VERSION(3,0,0)
+ gtk_container_set_border_width(
+ GTK_CONTAINER(edit_dialog->window), PIDGIN_HIG_BORDER);
+#endif
+
+ /* The vbox */
+#if GTK_CHECK_VERSION(3,0,0)
+ vbox = gtk_grid_new();
+ gtk_grid_set_row_spacing(GTK_GRID(vbox), PIDGIN_HIG_BORDER);
+#else
+ vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
+#endif
+ gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(
+ edit_dialog->window)), vbox);
+ gtk_widget_show(vbox);
+
+ /* The hbox */
+#if GTK_CHECK_VERSION(3,0,0)
+ hbox = gtk_grid_new();
+ gtk_grid_set_column_spacing(GTK_GRID(hbox), PIDGIN_HIG_BORDER);
+ gtk_grid_attach(GTK_GRID(vbox), hbox, 0, 0, 1, 1);
+#else
+ hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
+ gtk_container_add(GTK_CONTAINER(GTK_VBOX(vbox)), hbox);
+#endif
+
+ label = GTK_LABEL(gtk_label_new_with_mnemonic(_("_Image:")));
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_grid_attach(GTK_GRID(hbox), GTK_WIDGET(label), 0, 0, 1, 1);
+#else
+ gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), FALSE, FALSE, 0);
+#endif
+ gtk_widget_show(GTK_WIDGET(label));
+
+ filech = GTK_BUTTON(gtk_button_new());
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_grid_attach_next_to(GTK_GRID(hbox), GTK_WIDGET(filech), NULL,
+ GTK_POS_RIGHT, 1, 1);
+#else
+ gtk_box_pack_end(GTK_BOX(hbox), GTK_WIDGET(filech), FALSE, FALSE, 0);
+#endif
+ pidgin_set_accessible_label(GTK_WIDGET(filech), label);
+
+ edit_dialog->thumbnail = GTK_IMAGE(gtk_image_new());
+ gtk_container_add(GTK_CONTAINER(filech),
+ GTK_WIDGET(edit_dialog->thumbnail));
+
+ gtk_widget_show_all(hbox);
+
+ /* info */
+#if GTK_CHECK_VERSION(3,0,0)
+ hbox = gtk_grid_new();
+ gtk_grid_set_column_spacing(GTK_GRID(hbox), PIDGIN_HIG_BORDER);
+
+ gtk_grid_attach_next_to(GTK_GRID(vbox), hbox, NULL,
+ GTK_POS_BOTTOM, 1, 1);
+#else
+ hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
+ gtk_container_add(GTK_CONTAINER(GTK_VBOX(vbox)), hbox);
+#endif
+
+ /* Shortcut text */
+ label = GTK_LABEL(gtk_label_new_with_mnemonic(_("S_hortcut text:")));
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_grid_attach(GTK_GRID(hbox), GTK_WIDGET(label), 0, 0, 1, 1);
+#else
+ gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), FALSE, FALSE, 0);
+#endif
+ gtk_widget_show(GTK_WIDGET(label));
+
+ edit_dialog->shortcut = GTK_ENTRY(gtk_entry_new());
+ gtk_entry_set_activates_default(edit_dialog->shortcut, TRUE);
+ pidgin_set_accessible_label(GTK_WIDGET(edit_dialog->shortcut), label);
+
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_grid_attach_next_to(GTK_GRID(hbox),
+ GTK_WIDGET(edit_dialog->shortcut), NULL, GTK_POS_RIGHT, 1, 1);
+#else
+ gtk_box_pack_end(GTK_BOX(hbox), GTK_WIDGET(edit_dialog->shortcut),
+ FALSE, FALSE, 0);
+#endif
+
+ gtk_widget_show(GTK_WIDGET(edit_dialog->shortcut));
+ gtk_widget_show(hbox);
+ gtk_widget_show(GTK_WIDGET(edit_dialog->window));
+
+ if (smiley) {
+ edit_dialog->filename = g_strdup(purple_image_get_path(
+ purple_smiley_get_image(smiley)));
+ gtk_entry_set_text(edit_dialog->shortcut,
+ purple_smiley_get_shortcut(smiley));
+ }
+
+ edit_dialog_image_update_thumb(edit_dialog);
+ edit_dialog_update_buttons(edit_dialog);
+
+ g_signal_connect(edit_dialog->window, "response",
+ G_CALLBACK(edit_dialog_response), edit_dialog);
+ g_signal_connect(filech, "clicked",
+ G_CALLBACK(edit_dialog_image_choose), edit_dialog);
+ g_signal_connect(edit_dialog->shortcut, "changed",
+ G_CALLBACK(edit_dialog_shortcut_changed), edit_dialog);
+
+ g_signal_connect(edit_dialog->window, "destroy",
+ G_CALLBACK(edit_dialog_destroy), edit_dialog);
+ g_signal_connect(edit_dialog->window, "destroy",
+ G_CALLBACK(purple_notify_close_with_handle), edit_dialog);
+
+ return edit_dialog;
+}
+
+void
+pidgin_smiley_manager_add(PurpleImage *image, const gchar *shortcut)
+{
+ SmileyEditDialog *edit_dialog;
+
+ g_return_if_fail(image != NULL);
+
+ g_object_ref(image);
+
+ edit_dialog = edit_dialog_show(NULL, NULL);
+ edit_dialog_set_shortcut(edit_dialog, shortcut);
+ if (!edit_dialog_set_image(edit_dialog, image))
+ gtk_widget_destroy(GTK_WIDGET(edit_dialog->window));
+}
+
+
+/*******************************************************************************
+ * Custom smiley list Drag-and-drop support.
+ ******************************************************************************/
+
+static void
+smiley_list_dnd_url_got(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _manager)
+{
+ SmileyManager *manager = _manager;
+ SmileyEditDialog *edit_dialog;
+ PurpleImage *image;
+ const gchar *image_data;
+ size_t image_size;
+
+ g_return_if_fail(manager == smiley_manager);
+ g_return_if_fail(manager->running_request == http_conn);
+ manager->running_request = NULL;
+
+ if (!purple_http_response_is_successful(response))
+ return;
+
+ image_data = purple_http_response_get_data(response, &image_size);
+ image = purple_image_new_from_data(g_memdup(image_data, image_size),
+ image_size);
+ if (!image)
+ return;
+
+ edit_dialog = edit_dialog_show(manager, NULL);
+ if (!edit_dialog_set_image(edit_dialog, image))
+ gtk_widget_destroy(GTK_WIDGET(edit_dialog->window));
+}
+
+static void
+smiley_list_dnd_recv(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
+ GtkSelectionData *sd, guint info, guint time, gpointer _manager)
+{
+ SmileyManager *manager = _manager;
+ gchar content[1024];
+
+ /* We don't need anything, that is not 8-bit per element (char). */
+ if (gtk_selection_data_get_format(sd) != 8) {
+ gtk_drag_finish(dc, FALSE, FALSE, time);
+ return;
+ }
+
+ if (gtk_selection_data_get_length(sd) <= 0) {
+ gtk_drag_finish(dc, FALSE, FALSE, time);
+ return;
+ }
+
+ memset(&content, 0, sizeof(content));
+ memcpy(&content, gtk_selection_data_get_data(sd),
+ MIN((guint)gtk_selection_data_get_length(sd), sizeof(content)));
+ g_strstrip(content);
+ if (content[0] == '\0') {
+ gtk_drag_finish(dc, FALSE, FALSE, time);
+ return;
+ }
+
+ /* Well, it looks like the drag event was cool.
+ * Let's do something with it */
+
+ if (purple_str_has_caseprefix(content, "file://")) {
+ SmileyEditDialog *edit_dialog;
+ PurpleImage *image;
+ gchar *filename;
+
+ filename = g_filename_from_uri(content, NULL, NULL);
+ if (!filename || !g_file_test(filename, G_FILE_TEST_EXISTS)) {
+ purple_debug_warning("gtksmiley-manager",
+ "dropped file does not exists");
+ gtk_drag_finish(dc, FALSE, FALSE, time);
+ return;
+ }
+
+ image = purple_image_new_from_file(filename, TRUE);
+ if (!image) {
+ purple_debug_warning("gtksmiley-manager",
+ "dropped file is not a valid image");
+ gtk_drag_finish(dc, FALSE, FALSE, time);
+ return;
+ }
+ edit_dialog = edit_dialog_show(manager, NULL);
+ if (!edit_dialog_set_image(edit_dialog, image)) {
+ gtk_widget_destroy(GTK_WIDGET(edit_dialog->window));
+ gtk_drag_finish(dc, FALSE, FALSE, time);
+ return;
+ }
+
+ gtk_drag_finish(dc, TRUE, FALSE, time);
+ return;
+ }
+
+ if (purple_str_has_caseprefix(content, "http://") ||
+ purple_str_has_caseprefix(content, "https://"))
+ {
+ purple_http_conn_cancel(smiley_manager->
+ running_request);
+ smiley_manager->running_request = purple_http_get(NULL,
+ smiley_list_dnd_url_got, manager, content);
+
+ gtk_drag_finish(dc, TRUE, FALSE, time);
+ return;
+ }
+
+ gtk_drag_finish(dc, FALSE, FALSE, time);
+}
+
+/*******************************************************************************
+ * Custom smiley list.
+ ******************************************************************************/
+
+static void
+smiley_list_selected(GtkTreeSelection *sel, gpointer _manager)
+{
+ SmileyManager *manager = _manager;
+ gboolean sens;
+
+ sens = (gtk_tree_selection_count_selected_rows(sel) > 0);
+
+ gtk_dialog_set_response_sensitive(manager->window,
+ GTK_RESPONSE_NO, sens);
+ gtk_dialog_set_response_sensitive(manager->window,
+ PIDGIN_RESPONSE_MODIFY, sens);
+}
+
+static void
+smiley_list_activated(GtkTreeView *tree, GtkTreePath *path,
+ GtkTreeViewColumn *col, gpointer _manager)
+{
+ SmileyManager *manager = _manager;
+ GtkTreeIter iter;
+ PurpleSmiley *smiley = NULL;
+
+ if (!gtk_tree_model_get_iter(
+ GTK_TREE_MODEL(manager->model), &iter, path))
+ {
+ return;
+ }
+
+ gtk_tree_model_get(GTK_TREE_MODEL(manager->model), &iter,
+ SMILEY_LIST_MODEL_PURPLESMILEY, &smiley, -1);
+ g_return_if_fail(PURPLE_IS_SMILEY(smiley));
+
+ edit_dialog_show(manager, smiley);
+}
+
+static void
+manager_list_add(SmileyManager *manager, PurpleSmiley *smiley)
+{
+ GdkPixbuf *smiley_image;
+ GtkTreeIter iter;
+
+ smiley_image = g_object_get_data(G_OBJECT(smiley),
+ "pidgin-smiley-manager-list-thumb");
+ if (smiley_image == NULL) {
+ smiley_image = pidgin_pixbuf_from_image(
+ purple_smiley_get_image(smiley));
+ smiley_image = pidgin_pixbuf_scale_down(smiley_image,
+ 22, 22, GDK_INTERP_BILINEAR, TRUE);
+ g_object_set_data_full(G_OBJECT(smiley),
+ "pidgin-smiley-manager-list-thumb",
+ smiley_image, g_object_unref);
+ }
+
+ gtk_list_store_append(manager->model, &iter);
+ gtk_list_store_set(manager->model, &iter,
+ SMILEY_LIST_MODEL_ICON, smiley_image,
+ SMILEY_LIST_MODEL_SHORTCUT, purple_smiley_get_shortcut(smiley),
+ SMILEY_LIST_MODEL_PURPLESMILEY, smiley,
+ -1);
+}
+
+static void
+manager_list_fill(SmileyManager *manager)
+{
+ GList *custom_smileys, *it;
+ gtk_list_store_clear(manager->model);
+
+ custom_smileys = purple_smiley_list_get_all(
+ purple_smiley_custom_get_list());
+
+ for (it = custom_smileys; it; it = g_list_next(it)) {
+ PurpleSmiley *smiley = it->data;
+
+ manager_list_add(manager, smiley);
+ }
+ g_list_free(custom_smileys);
+}
+
+static GtkWidget *
+manager_list_create(SmileyManager *manager)
+{
+ GtkTreeView *tree;
+ GtkTreeSelection *sel;
+ GtkCellRenderer *cellrend;
+ GtkTreeViewColumn *column;
+ GtkTargetEntry targets[3] = {
+ {"text/plain", 0, 0},
+ {"text/uri-list", 0, 1},
+ {"STRING", 0, 2}
+ };
+
+ manager->model = gtk_list_store_new(SMILEY_LIST_MODEL_N_COL,
+ GDK_TYPE_PIXBUF, /* icon */
+ G_TYPE_STRING, /* shortcut */
+ G_TYPE_OBJECT /* PurpleSmiley */
+ );
+
+ manager->tree = tree = GTK_TREE_VIEW(gtk_tree_view_new_with_model(
+ GTK_TREE_MODEL(manager->model)));
+
+ gtk_tree_view_set_rules_hint(tree, TRUE);
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(manager->model),
+ SMILEY_LIST_MODEL_SHORTCUT, GTK_SORT_ASCENDING);
+
+ g_object_unref(manager->model);
+
+ sel = gtk_tree_view_get_selection(tree);
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE);
+
+ g_signal_connect(sel, "changed",
+ G_CALLBACK(smiley_list_selected), manager);
+ g_signal_connect(tree, "row-activated",
+ G_CALLBACK(smiley_list_activated), manager);
+
+ gtk_drag_dest_set(GTK_WIDGET(tree), GTK_DEST_DEFAULT_MOTION |
+ GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP,
+ targets, G_N_ELEMENTS(targets),
+ GDK_ACTION_COPY | GDK_ACTION_MOVE);
+ g_signal_connect(tree, "drag-data-received",
+ G_CALLBACK(smiley_list_dnd_recv), manager);
+
+ gtk_widget_show(GTK_WIDGET(tree));
+
+ /* setting up columns */
+
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_column_set_title(column, _("Smiley"));
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_append_column(tree, column);
+ cellrend = gtk_cell_renderer_pixbuf_new();
+ gtk_tree_view_column_pack_start(column, cellrend, FALSE);
+ gtk_tree_view_column_add_attribute(column, cellrend,
+ "pixbuf", SMILEY_LIST_MODEL_ICON);
+
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_column_set_title(column, _("Shortcut Text"));
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_append_column(tree, column);
+ cellrend = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start(column, cellrend, TRUE);
+ gtk_tree_view_column_add_attribute(column, cellrend,
+ "text", SMILEY_LIST_MODEL_SHORTCUT);
+
+ manager_list_fill(manager);
+
+ return pidgin_make_scrollable(GTK_WIDGET(tree), GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, -1);
+}
+
+/*******************************************************************************
+ * Custom smiley manager window.
+ ******************************************************************************/
+
+static void
+manager_select_cb(GtkWidget *widget, gint resp, SmileyManager *manager)
+{
+ GtkTreeSelection *selection = NULL;
+ GList *selected_rows, *selected_smileys = NULL, *it;
+ GtkTreeModel *model = GTK_TREE_MODEL(manager->model);
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(manager->tree));
+ selected_rows = gtk_tree_selection_get_selected_rows(selection, NULL);
+ for (it = selected_rows; it; it = g_list_next(it)) {
+ GtkTreePath *path = it->data;
+ GtkTreeIter iter;
+ PurpleSmiley *smiley = NULL;
+
+ if (!gtk_tree_model_get_iter(model, &iter, path))
+ continue;
+
+ gtk_tree_model_get(model, &iter,
+ SMILEY_LIST_MODEL_PURPLESMILEY, &smiley, -1);
+ if (!smiley)
+ continue;
+
+ selected_smileys = g_list_prepend(selected_smileys, smiley);
+ }
+ g_list_free_full(selected_rows, (GDestroyNotify)gtk_tree_path_free);
+
+ switch (resp) {
+ case GTK_RESPONSE_YES:
+ edit_dialog_show(manager, NULL);
+ break;
+ case GTK_RESPONSE_NO:
+ for (it = selected_smileys; it; it = g_list_next(it))
+ purple_smiley_custom_remove(it->data);
+ manager_list_fill(manager);
+ break;
+ case GTK_RESPONSE_DELETE_EVENT:
+ case GTK_RESPONSE_CLOSE:
+ gtk_widget_destroy(GTK_WIDGET(manager->window));
+ purple_http_conn_cancel(manager->running_request);
+ g_free(manager);
+ smiley_manager = NULL;
+ break;
+ case PIDGIN_RESPONSE_MODIFY:
+ for (it = selected_smileys; it; it = g_list_next(it))
+ edit_dialog_show(manager, it->data);
+ break;
+ default:
+ g_warn_if_reached();
+ }
+
+ g_list_free(selected_smileys);
+}
+
+void
+pidgin_smiley_manager_show(void)
+{
+ SmileyManager *manager;
+ GtkDialog *win;
+ GtkWidget *sw, *vbox;
+
+ if (smiley_manager) {
+ gtk_window_present(GTK_WINDOW(smiley_manager->window));
+ return;
+ }
+
+ manager = g_new0(SmileyManager, 1);
+ smiley_manager = manager;
+
+ manager->window = win = GTK_DIALOG(gtk_dialog_new_with_buttons(
+ _("Custom Smiley Manager"), NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ PIDGIN_STOCK_ADD, GTK_RESPONSE_YES,
+ PIDGIN_STOCK_MODIFY, PIDGIN_RESPONSE_MODIFY,
+ GTK_STOCK_DELETE, GTK_RESPONSE_NO,
+ GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+ NULL));
+
+ gtk_window_set_default_size(GTK_WINDOW(win), 50, 400);
+ gtk_window_set_role(GTK_WINDOW(win), "custom_smiley_manager");
+#if !GTK_CHECK_VERSION(3,0,0)
+ gtk_container_set_border_width(GTK_CONTAINER(win), PIDGIN_HIG_BORDER);
+#endif
+ gtk_dialog_set_response_sensitive(win, GTK_RESPONSE_NO, FALSE);
+ gtk_dialog_set_response_sensitive(win, PIDGIN_RESPONSE_MODIFY, FALSE);
+
+ g_signal_connect(win, "response",
+ G_CALLBACK(manager_select_cb), manager);
+
+ /* The vbox */
+ vbox = gtk_dialog_get_content_area(win);
+
+ /* get the scrolled window with all stuff */
+ sw = manager_list_create(manager);
+ gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
+ gtk_widget_show(sw);
+
+ gtk_widget_show(GTK_WIDGET(win));
+}
diff --git a/pidgin/gtksmiley-manager.h b/pidgin/gtksmiley-manager.h
new file mode 100644
index 0000000000..1e0507b6fc
--- /dev/null
+++ b/pidgin/gtksmiley-manager.h
@@ -0,0 +1,60 @@
+/* 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_SMILEY_MANAGER_H_
+#define _PIDGIN_SMILEY_MANAGER_H_
+/**
+ * SECTION:gtksmiley-manager
+ * @include:gtksmiley-manager.h
+ * @section_id: pidgin-smiley-manager
+ * @short_description: a UI for user-defined smileys management
+ * @title: Custom smileys manager
+ *
+ * This module provides a GTK+ UI that allows the user adding and removing
+ * custom smileys. See libpurple-smiley-custom section (TODO: how to link this
+ * to libpurple's docs?).
+ */
+
+G_BEGIN_DECLS
+
+/**
+ * pidgin_smiley_manager_show:
+ *
+ * Creates and shows the smiley manager window, or requests focus for it,
+ * if it's already opened.
+ */
+void
+pidgin_smiley_manager_show(void);
+
+/**
+ * pidgin_smiley_manager_add:
+ * @image: the image for a new smiley.
+ * @shortcut: the textual representation, may be %NULL.
+ *
+ * Creates and shows the new dialog for adding a new custom smiley with
+ * provided image.
+ */
+void
+pidgin_smiley_manager_add(PurpleImage *image, const gchar *shortcut);
+
+G_END_DECLS
+
+#endif /* _PIDGIN_SMILEY_MANAGER_H_ */
diff --git a/pidgin/gtksmiley-theme.c b/pidgin/gtksmiley-theme.c
new file mode 100644
index 0000000000..cc6bfd1a4e
--- /dev/null
+++ b/pidgin/gtksmiley-theme.c
@@ -0,0 +1,625 @@
+/* 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 "gtksmiley-theme.h"
+
+#include "internal.h"
+#include "glibcompat.h"
+
+#include "debug.h"
+
+#include "gtkutils.h"
+
+#include <glib/gstdio.h>
+
+#define PIDGIN_SMILEY_THEME_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PIDGIN_TYPE_SMILEY_THEME, \
+ PidginSmileyThemePrivate))
+
+#define PIDGIN_SMILEY_THEME_MAX_LINES 1024
+#define PIDGIN_SMILEY_THEME_MAX_TOKENS 1024
+
+typedef struct
+{
+ gchar *path;
+
+ gchar *name;
+ gchar *desc;
+ gchar *icon;
+ gchar *author;
+
+ GdkPixbuf *icon_pixbuf;
+
+ GHashTable *smiley_lists_map;
+} PidginSmileyThemePrivate;
+
+static GObjectClass *parent_class;
+
+static gchar **probe_dirs;
+static GList *smiley_themes = NULL;
+
+typedef struct
+{
+ gchar *name;
+ gchar *desc;
+ gchar *icon;
+ gchar *author;
+
+ GList *protocols;
+} PidginSmileyThemeIndex;
+
+typedef struct
+{
+ gchar *name;
+ GList *smileys;
+} PidginSmileyThemeIndexProtocol;
+
+typedef struct
+{
+ gchar *file;
+ gboolean hidden;
+ GList *shortcuts;
+} PidginSmileyThemeIndexSmiley;
+
+/*******************************************************************************
+ * Theme index parsing
+ ******************************************************************************/
+
+static void
+pidgin_smiley_theme_index_free(PidginSmileyThemeIndex *index)
+{
+ GList *it, *it2;
+
+ g_return_if_fail(index != NULL);
+
+ g_free(index->name);
+ g_free(index->desc);
+ g_free(index->icon);
+ g_free(index->author);
+
+ for (it = index->protocols; it; it = g_list_next(it)) {
+ PidginSmileyThemeIndexProtocol *proto = it->data;
+
+ g_free(proto->name);
+ for (it2 = proto->smileys; it2; it2 = g_list_next(it2)) {
+ PidginSmileyThemeIndexSmiley *smiley = it2->data;
+
+ g_free(smiley->file);
+ g_list_free_full(smiley->shortcuts, g_free);
+ g_free(smiley);
+ }
+ g_list_free(proto->smileys);
+ g_free(proto);
+ }
+ g_list_free(index->protocols);
+}
+
+static PidginSmileyThemeIndex *
+pidgin_smiley_theme_index_parse(const gchar *theme_path, gboolean load_contents)
+{
+ PidginSmileyThemeIndex *index;
+ PidginSmileyThemeIndexProtocol *proto = NULL;
+ gchar *index_path;
+ FILE *file;
+ int line_no = 0;
+ gboolean inv_frm = FALSE;
+
+ index_path = g_build_filename(theme_path, "theme", NULL);
+ file = g_fopen(index_path, "r");
+ if (!file) {
+ purple_debug_error("gtksmiley-theme",
+ "Failed to open index file %s", index_path);
+ g_free(index_path);
+ return NULL;
+ }
+
+ index = g_new0(PidginSmileyThemeIndex, 1);
+
+ while (!feof(file)) {
+ PidginSmileyThemeIndexSmiley *smiley;
+ gchar buff[1024];
+ gchar *line, *eqchr;
+ gchar **split;
+ int i;
+
+ if (++line_no > PIDGIN_SMILEY_THEME_MAX_LINES) {
+ purple_debug_warning("gtksmiley-theme", "file too big");
+ break;
+ }
+
+ if (!fgets(buff, sizeof(buff), file))
+ break;
+
+ /* strip comments */
+ if (buff[0] == '#')
+ continue;
+
+ g_strstrip(buff);
+ if (buff[0] == '\0')
+ continue;
+
+ if (!g_utf8_validate(buff, -1, NULL)) {
+ purple_debug_error("gtksmiley-theme",
+ "%s:%d is invalid UTF-8",
+ index_path, line_no);
+ continue;
+ }
+
+ line = buff;
+
+ if (line[0] == '[') {
+ gchar *end;
+
+ if (!load_contents)
+ break;
+ line++;
+ end = strchr(line, ']');
+ if (!end) {
+ inv_frm = TRUE;
+ break;
+ }
+
+ if (proto)
+ proto->smileys = g_list_reverse(proto->smileys);
+
+ proto = g_new0(PidginSmileyThemeIndexProtocol, 1);
+ proto->name = g_strndup(line, end - line);
+
+ index->protocols =
+ g_list_prepend(index->protocols, proto);
+
+ continue;
+ }
+
+ if ((eqchr = strchr(line, '='))) {
+ *eqchr = '\0';
+ if (g_ascii_strcasecmp(line, "name") == 0) {
+ g_free(index->name);
+ index->name = g_strdup(eqchr + 1);
+ g_strchug(index->name);
+ continue;
+ } else if (g_ascii_strcasecmp(line, "description") == 0) {
+ g_free(index->desc);
+ index->desc = g_strdup(eqchr + 1);
+ g_strchug(index->desc);
+ continue;
+ } else if (g_ascii_strcasecmp(line, "icon") == 0) {
+ g_free(index->icon);
+ index->icon = g_strdup(eqchr + 1);
+ g_strchug(index->icon);
+ continue;
+ } else if (g_ascii_strcasecmp(line, "author") == 0) {
+ g_free(index->author);
+ index->author = g_strdup(eqchr + 1);
+ g_strchug(index->author);
+ continue;
+ }
+ *eqchr = '=';
+ }
+
+ /* parsing section content */
+
+ if (proto == NULL) {
+ inv_frm = FALSE;
+ break;
+ }
+
+ smiley = g_new0(PidginSmileyThemeIndexSmiley, 1);
+ proto->smileys = g_list_prepend(proto->smileys, smiley);
+
+ smiley->hidden = FALSE;
+ if (line[0] == '!') {
+ smiley->hidden = TRUE;
+ line++;
+ }
+
+ split = g_strsplit_set(line, " \t",
+ PIDGIN_SMILEY_THEME_MAX_TOKENS);
+ for (i = 0; split[i]; i++) {
+ gchar *token = split[i];
+
+ if (token[0] == '\0')
+ continue;
+ if (i == PIDGIN_SMILEY_THEME_MAX_TOKENS - 1)
+ break;
+
+ if (!smiley->file) {
+ smiley->file = g_strdup(token);
+ continue;
+ }
+
+ smiley->shortcuts = g_list_prepend(smiley->shortcuts,
+ g_strdup(token));
+ }
+ g_strfreev(split);
+ smiley->shortcuts = g_list_reverse(smiley->shortcuts);
+ }
+
+ if (proto)
+ proto->smileys = g_list_reverse(proto->smileys);
+
+ fclose(file);
+
+ if (inv_frm) {
+ purple_debug_error("gtksmiley-theme", "%s:%d"
+ " invalid format", index_path, line_no);
+ pidgin_smiley_theme_index_free(index);
+ index = NULL;
+ }
+
+ g_free(index_path);
+ return index;
+}
+
+/*******************************************************************************
+ * Theme loading
+ ******************************************************************************/
+
+static void
+pidgin_smiley_theme_load(const gchar *theme_path)
+{
+ PidginSmileyTheme *theme;
+ PidginSmileyThemePrivate *priv;
+ PidginSmileyThemeIndex *index;
+ GList *it;
+
+ /* it's not super-efficient, but we don't expect huge amount of
+ * installed themes */
+ for (it = smiley_themes; it; it = g_list_next(it)) {
+ PidginSmileyThemePrivate *priv =
+ PIDGIN_SMILEY_THEME_GET_PRIVATE(it->data);
+
+ /* theme is already loaded */
+ if (g_strcmp0(priv->path, theme_path) == 0)
+ return;
+ }
+
+ theme = g_object_new(PIDGIN_TYPE_SMILEY_THEME, NULL);
+ priv = PIDGIN_SMILEY_THEME_GET_PRIVATE(theme);
+
+ priv->path = g_strdup(theme_path);
+
+ index = pidgin_smiley_theme_index_parse(theme_path, FALSE);
+
+ if (!index->name || index->name[0] == '\0') {
+ purple_debug_warning("gtksmiley-theme",
+ "incomplete theme %s", theme_path);
+ pidgin_smiley_theme_index_free(index);
+ g_object_unref(theme);
+ return;
+ }
+
+ priv->name = g_strdup(index->name);
+ if (index->desc && index->desc[0])
+ priv->desc = g_strdup(index->desc);
+ if (index->icon && index->icon[0])
+ priv->icon = g_strdup(index->icon);
+ if (index->author && index->author[0])
+ priv->author = g_strdup(index->author);
+
+ pidgin_smiley_theme_index_free(index);
+
+ smiley_themes = g_list_append(smiley_themes, theme);
+}
+
+static void
+pidgin_smiley_theme_probe(void)
+{
+ GList *it, *next;
+ int i;
+
+ /* remove non-existing themes */
+ for (it = smiley_themes; it; it = next) {
+ PidginSmileyTheme *theme = it->data;
+ PidginSmileyThemePrivate *priv =
+ PIDGIN_SMILEY_THEME_GET_PRIVATE(theme);
+
+ next = g_list_next(it);
+
+ if (g_file_test(priv->path, G_FILE_TEST_EXISTS))
+ continue;
+ smiley_themes = g_list_delete_link(smiley_themes, it);
+ g_object_unref(theme);
+ }
+
+ /* scan for themes */
+ for (i = 0; probe_dirs[i]; i++) {
+ GDir *dir = g_dir_open(probe_dirs[i], 0, NULL);
+ const gchar *theme_dir_name;
+
+ if (!dir)
+ continue;
+
+ while ((theme_dir_name = g_dir_read_name(dir))) {
+ gchar *theme_path;
+
+ /* Ignore Pidgin 2.x.y "none" theme. */
+ if (g_strcmp0(theme_dir_name, "none") == 0)
+ continue;
+
+ theme_path = g_build_filename(
+ probe_dirs[i], theme_dir_name, NULL);
+
+ if (g_file_test(theme_path, G_FILE_TEST_IS_DIR))
+ pidgin_smiley_theme_load(theme_path);
+
+ g_free(theme_path);
+ }
+
+ g_dir_close(dir);
+ }
+}
+
+
+/*******************************************************************************
+ * API implementation
+ ******************************************************************************/
+
+const gchar *
+pidgin_smiley_theme_get_name(PidginSmileyTheme *theme)
+{
+ PidginSmileyThemePrivate *priv = PIDGIN_SMILEY_THEME_GET_PRIVATE(theme);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->name;
+}
+
+const gchar *
+pidgin_smiley_theme_get_description(PidginSmileyTheme *theme)
+{
+ PidginSmileyThemePrivate *priv = PIDGIN_SMILEY_THEME_GET_PRIVATE(theme);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->desc;
+}
+
+GdkPixbuf *
+pidgin_smiley_theme_get_icon(PidginSmileyTheme *theme)
+{
+ PidginSmileyThemePrivate *priv = PIDGIN_SMILEY_THEME_GET_PRIVATE(theme);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ if (priv->icon == NULL)
+ return NULL;
+
+ if (!priv->icon_pixbuf) {
+ gchar *icon_path = g_build_filename(
+ priv->path, priv->icon, NULL);
+ priv->icon_pixbuf = pidgin_pixbuf_new_from_file(icon_path);
+ g_free(icon_path);
+ }
+
+ return priv->icon_pixbuf;
+}
+
+const gchar *
+pidgin_smiley_theme_get_author(PidginSmileyTheme *theme)
+{
+ PidginSmileyThemePrivate *priv = PIDGIN_SMILEY_THEME_GET_PRIVATE(theme);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->author;
+}
+
+PurpleSmileyList *
+pidgin_smiley_theme_for_conv(PurpleConversation *conv)
+{
+ PurpleAccount *acc = NULL;
+ PurpleSmileyTheme *theme;
+ const gchar *proto_name = NULL;
+
+ theme = purple_smiley_theme_get_current();
+ if (theme == NULL)
+ return NULL;
+
+ if (conv)
+ acc = purple_conversation_get_account(conv);
+ if (acc)
+ proto_name = purple_account_get_protocol_name(acc);
+
+ return purple_smiley_theme_get_smileys(theme, (gpointer)proto_name);
+}
+
+static void
+pidgin_smiley_theme_activate_impl(PurpleSmileyTheme *theme)
+{
+ PidginSmileyThemePrivate *priv = PIDGIN_SMILEY_THEME_GET_PRIVATE(theme);
+ PidginSmileyThemeIndex *index;
+ GHashTable *smap;
+ GList *it, *it2, *it3;
+
+ g_return_if_fail(priv != NULL);
+
+ if (priv->smiley_lists_map)
+ return;
+
+ priv->smiley_lists_map = smap = g_hash_table_new_full(
+ g_str_hash, g_str_equal, g_free, g_object_unref);
+
+ index = pidgin_smiley_theme_index_parse(priv->path, TRUE);
+
+ for (it = index->protocols; it; it = g_list_next(it)) {
+ PidginSmileyThemeIndexProtocol *proto_idx = it->data;
+ PurpleSmileyList *proto_smileys;
+
+ proto_smileys = g_hash_table_lookup(smap, proto_idx->name);
+ if (!proto_smileys) {
+ proto_smileys = purple_smiley_list_new();
+ g_hash_table_insert(smap,
+ g_strdup(proto_idx->name), proto_smileys);
+ }
+
+ for (it2 = proto_idx->smileys; it2; it2 = g_list_next(it2)) {
+ PidginSmileyThemeIndexSmiley *smiley_idx = it2->data;
+ gchar *smiley_path;
+
+ smiley_path = g_build_filename(
+ priv->path, smiley_idx->file, NULL);
+ if (!g_file_test(smiley_path, G_FILE_TEST_EXISTS)) {
+ purple_debug_warning("gtksmiley-theme",
+ "Smiley %s is missing", smiley_path);
+ continue;
+ }
+
+ for (it3 = smiley_idx->shortcuts; it3;
+ it3 = g_list_next(it3))
+ {
+ PurpleSmiley *smiley;
+ gchar *shortcut = it3->data;
+
+ smiley = purple_smiley_new(
+ shortcut, smiley_path);
+ g_object_set_data(G_OBJECT(smiley),
+ "pidgin-smiley-hidden",
+ GINT_TO_POINTER(smiley_idx->hidden));
+ purple_smiley_list_add(proto_smileys, smiley);
+ g_object_unref(smiley);
+ }
+ }
+ }
+
+ pidgin_smiley_theme_index_free(index);
+}
+
+static PurpleSmileyList *
+pidgin_smiley_theme_get_smileys_impl(PurpleSmileyTheme *theme, gpointer ui_data)
+{
+ PidginSmileyThemePrivate *priv = PIDGIN_SMILEY_THEME_GET_PRIVATE(theme);
+ PurpleSmileyList *smileys = NULL;
+
+ pidgin_smiley_theme_activate_impl(theme);
+
+ if (ui_data)
+ smileys = g_hash_table_lookup(priv->smiley_lists_map, ui_data);
+ if (smileys != NULL)
+ return smileys;
+
+ return g_hash_table_lookup(priv->smiley_lists_map, "default");
+}
+
+GList *
+pidgin_smiley_theme_get_all(void)
+{
+ pidgin_smiley_theme_probe();
+
+ return smiley_themes;
+}
+
+void
+_pidgin_smiley_theme_init(void)
+{
+ GList *it;
+ const gchar *user_smileys_dir;
+ const gchar *theme_name;
+
+ 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);
+
+ 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) {
+ purple_debug_error("gtksmiley-theme",
+ "Failed to create user smileys dir");
+ }
+ }
+
+ /* setting theme by name (copy-paste from gtkprefs) */
+ pidgin_smiley_theme_probe();
+ theme_name = purple_prefs_get_string(
+ PIDGIN_PREFS_ROOT "/smileys/theme");
+ for (it = smiley_themes; it; it = g_list_next(it)) {
+ PidginSmileyTheme *theme = it->data;
+
+ if (g_strcmp0(pidgin_smiley_theme_get_name(theme), theme_name))
+ continue;
+
+ purple_smiley_theme_set_current(PURPLE_SMILEY_THEME(theme));
+ }
+}
+
+void
+_pidgin_smiley_theme_uninit(void)
+{
+ g_strfreev(probe_dirs);
+}
+
+/*******************************************************************************
+ * Object stuff
+ ******************************************************************************/
+
+static void
+pidgin_smiley_theme_finalize(GObject *obj)
+{
+ PidginSmileyThemePrivate *priv = PIDGIN_SMILEY_THEME_GET_PRIVATE(obj);
+
+ g_free(priv->path);
+ g_free(priv->name);
+ g_free(priv->desc);
+ g_free(priv->icon);
+ g_free(priv->author);
+ if (priv->icon_pixbuf)
+ g_object_unref(priv->icon_pixbuf);
+ if (priv->smiley_lists_map)
+ g_hash_table_destroy(priv->smiley_lists_map);
+
+ G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+static void
+pidgin_smiley_theme_class_init(PidginSmileyThemeClass *klass)
+{
+ GObjectClass *gobj_class = G_OBJECT_CLASS(klass);
+ PurpleSmileyThemeClass *pst_class = PURPLE_SMILEY_THEME_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ g_type_class_add_private(klass, sizeof(PidginSmileyThemePrivate));
+
+ gobj_class->finalize = pidgin_smiley_theme_finalize;
+
+ pst_class->get_smileys = pidgin_smiley_theme_get_smileys_impl;
+ pst_class->activate = pidgin_smiley_theme_activate_impl;
+}
+
+GType
+pidgin_smiley_theme_get_type(void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY(type == 0)) {
+ static const GTypeInfo info = {
+ .class_size = sizeof(PidginSmileyThemeClass),
+ .class_init = (GClassInitFunc)pidgin_smiley_theme_class_init,
+ .instance_size = sizeof(PidginSmileyTheme),
+ };
+
+ type = g_type_register_static(PURPLE_TYPE_SMILEY_THEME,
+ "PidginSmileyTheme", &info, 0);
+ }
+
+ return type;
+}
diff --git a/pidgin/gtksmiley-theme.h b/pidgin/gtksmiley-theme.h
new file mode 100644
index 0000000000..209210bed0
--- /dev/null
+++ b/pidgin/gtksmiley-theme.h
@@ -0,0 +1,174 @@
+/* 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_SMILEY_THEME_H_
+#define _PIDGIN_SMILEY_THEME_H_
+/**
+ * SECTION:gtksmiley-theme
+ * @include:gtksmiley-theme.h
+ * @section_id: pidgin-smiley-theme
+ * @short_description: a per-protocol categorized sets of standard smileys
+ * @title: Pidgin's smiley themes
+ *
+ * This class implements a per-protocol based #PurpleSmileyTheme.
+ */
+
+#include <glib-object.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "conversation.h"
+#include "smiley-theme.h"
+
+typedef struct _PidginSmileyTheme PidginSmileyTheme;
+typedef struct _PidginSmileyThemeClass PidginSmileyThemeClass;
+
+#define PIDGIN_TYPE_SMILEY_THEME (pidgin_smiley_theme_get_type())
+#define PIDGIN_SMILEY_THEME(smiley) (G_TYPE_CHECK_INSTANCE_CAST((smiley), PIDGIN_TYPE_SMILEY_THEME, PidginSmileyTheme))
+#define PIDGIN_SMILEY_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PIDGIN_TYPE_SMILEY_THEME, PidginSmileyThemeClass))
+#define PIDGIN_IS_SMILEY_THEME(smiley) (G_TYPE_CHECK_INSTANCE_TYPE((smiley), PIDGIN_TYPE_SMILEY_THEME))
+#define PIDGIN_IS_SMILEY_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PIDGIN_TYPE_SMILEY_THEME))
+#define PIDGIN_SMILEY_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PIDGIN_TYPE_SMILEY_THEME, PidginSmileyThemeClass))
+
+/**
+ * PidginSmileyTheme:
+ *
+ * An implementation of a smiley theme.
+ */
+struct _PidginSmileyTheme
+{
+ /*< private >*/
+ PurpleSmileyTheme parent;
+};
+
+/**
+ * PidginSmileyThemeClass:
+ *
+ * Base class for #PidginSmileyTheme objects.
+ */
+struct _PidginSmileyThemeClass
+{
+ /*< private >*/
+ PurpleSmileyThemeClass parent_class;
+
+ void (*pidgin_reserved1)(void);
+ void (*pidgin_reserved2)(void);
+ void (*pidgin_reserved3)(void);
+ void (*pidgin_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+/**
+ * pidgin_smiley_theme_get_type:
+ *
+ * Returns: the #GType for a smiley list.
+ */
+GType
+pidgin_smiley_theme_get_type(void);
+
+/**
+ * pidgin_smiley_theme_get_name:
+ * @theme: the smiley theme.
+ *
+ * Returns the name for a @theme. Valid themes always have the name set.
+ *
+ * Returns: (transfer none): the name string, or %NULL if error occured.
+ */
+const gchar *
+pidgin_smiley_theme_get_name(PidginSmileyTheme *theme);
+
+/**
+ * pidgin_smiley_theme_get_description:
+ * @theme: the smiley theme.
+ *
+ * Returns the description for a @theme.
+ *
+ * Returns: (transfer none): the description string, or %NULL if it's not
+ * set or error occured.
+ */
+const gchar *
+pidgin_smiley_theme_get_description(PidginSmileyTheme *theme);
+
+/**
+ * pidgin_smiley_theme_get_icon:
+ * @theme: the smiley theme.
+ *
+ * Returns the @theme's icon image, possibly loading it from the disk (and
+ * adding it to the cache).
+ *
+ * Returns: (transfer none): the @theme's icon image.
+ */
+GdkPixbuf *
+pidgin_smiley_theme_get_icon(PidginSmileyTheme *theme);
+
+/**
+ * pidgin_smiley_theme_get_author:
+ * @theme: the smiley theme.
+ *
+ * Returns the autor of @theme.
+ *
+ * Returns: (transfer none): the author string, or %NULL if it's not
+ * set or error occured.
+ */
+const gchar *
+pidgin_smiley_theme_get_author(PidginSmileyTheme *theme);
+
+/**
+ * pidgin_smiley_theme_for_conv:
+ * @conv: the conversation.
+ *
+ * Gets the smiley list for a @conv based on current theme.
+ *
+ * Returns: (transfer none): the smiley list, or %NULL if there
+ * is no smiley theme set.
+ */
+PurpleSmileyList *
+pidgin_smiley_theme_for_conv(PurpleConversation *conv);
+
+/**
+ * pidgin_smiley_theme_get_all:
+ *
+ * Returns the list of currently available smiley themes.
+ *
+ * Returns: (transfer none): the #GList of #PidginSmileyTheme's.
+ */
+GList *
+pidgin_smiley_theme_get_all(void);
+
+/**
+ * _pidgin_smiley_theme_init: (skip)
+ *
+ * Initializes the Pidgin's smiley theme subsystem.
+ */
+void
+_pidgin_smiley_theme_init(void);
+
+/**
+ * _pidgin_smiley_theme_uninit: (skip)
+ *
+ * Unitializes the Pidgin's smiley theme subsystem.
+ */
+void
+_pidgin_smiley_theme_uninit(void);
+
+G_END_DECLS
+
+#endif /* _PIDGIN_SMILEY_THEME_H_ */
diff --git a/pidgin/gtksmiley.c b/pidgin/gtksmiley.c
deleted file mode 100644
index 2ea41edc14..0000000000
--- a/pidgin/gtksmiley.c
+++ /dev/null
@@ -1,894 +0,0 @@
-/**
- * @file gtksmiley.c GTK+ Smiley Manager API
- * @ingroup pidgin
- */
-
-/*
- * 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 "debug.h"
-#include "notify.h"
-#include "smiley.h"
-
-#include "gtkimhtml.h"
-#include "gtksmiley.h"
-#include "gtkutils.h"
-#include "pidginstock.h"
-
-#define PIDGIN_RESPONSE_MODIFY 1000
-
-struct _PidginSmiley
-{
- PurpleSmiley *smiley;
- GtkWidget *parent;
- GtkWidget *smile;
- GtkWidget *smiley_image;
- gchar *filename;
- GdkPixbuf *custom_pixbuf;
- gpointer data; /** @since 2.6.0 */
- gsize datasize; /** @since 2.6.0 */
- gint entry_len; /** @since 2.6.0 */
-};
-
-typedef struct
-{
- GtkWidget *window;
-
- GtkWidget *treeview;
- GtkListStore *model;
-} SmileyManager;
-
-enum
-{
- ICON,
- SHORTCUT,
- SMILEY,
- N_COL
-};
-
-static SmileyManager *smiley_manager = NULL;
-static GSList *gtk_smileys = NULL;
-
-static void
-pidgin_smiley_destroy(PidginSmiley *smiley)
-{
- if (smiley->smiley)
- g_object_set_data(G_OBJECT(smiley->smiley), "edit-dialog", NULL);
- gtk_widget_destroy(smiley->parent);
- g_free(smiley->filename);
- if (smiley->custom_pixbuf)
- g_object_unref(G_OBJECT(smiley->custom_pixbuf));
- g_free(smiley);
-}
-
-/******************************************************************************
- * GtkIMHtmlSmileys stuff
- *****************************************************************************/
-/* Perhaps these should be in gtkimhtml.c instead. -- sadrul */
-static void add_gtkimhtml_to_list(GtkIMHtmlSmiley *gtksmiley)
-{
- gtk_smileys = g_slist_prepend(gtk_smileys, gtksmiley);
-
- purple_debug_info("gtksmiley", "adding %s to gtk_smileys\n", gtksmiley->smile);
-}
-
-static void
-shortcut_changed_cb(PurpleSmiley *smiley, gpointer dontcare, GtkIMHtmlSmiley *gtksmiley)
-{
- g_free(gtksmiley->smile);
- gtksmiley->smile = g_strdup(purple_smiley_get_shortcut(smiley));
-}
-
-static void
-image_changed_cb(PurpleSmiley *smiley, gpointer dontcare, GtkIMHtmlSmiley *gtksmiley)
-{
- const char *file;
-
- g_free(gtksmiley->file);
-
- file = purple_imgstore_get_filename(purple_smiley_get_stored_image(smiley));
- gtksmiley->file = g_build_filename(purple_smileys_get_storing_dir(), file, NULL);
- gtk_imhtml_smiley_reload(gtksmiley);
-}
-
-static GtkIMHtmlSmiley *smiley_purple_to_gtkimhtml(PurpleSmiley *smiley)
-{
- GtkIMHtmlSmiley *gtksmiley;
- gchar *filename;
- const gchar *file;
-
- file = purple_imgstore_get_filename(purple_smiley_get_stored_image(smiley));
-
- filename = g_build_filename(purple_smileys_get_storing_dir(), file, NULL);
-
- gtksmiley = gtk_imhtml_smiley_create(filename, purple_smiley_get_shortcut(smiley),
- FALSE, GTK_IMHTML_SMILEY_CUSTOM);
- g_free(filename);
-
- /* Make sure the shortcut for the GtkIMHtmlSmiley is updated with the PurpleSmiley */
- g_signal_connect(G_OBJECT(smiley), "notify::shortcut",
- G_CALLBACK(shortcut_changed_cb), gtksmiley);
-
- /* And update the pixbuf too when the image is changed */
- g_signal_connect(G_OBJECT(smiley), "notify::image",
- G_CALLBACK(image_changed_cb), gtksmiley);
-
- return gtksmiley;
-}
-
-void pidgin_smiley_del_from_list(PurpleSmiley *smiley)
-{
- GSList *list = NULL;
- GtkIMHtmlSmiley *gtksmiley;
-
- if (gtk_smileys == NULL)
- return;
-
- list = gtk_smileys;
-
- for (; list; list = list->next) {
- gtksmiley = (GtkIMHtmlSmiley*)list->data;
-
- if (strcmp(gtksmiley->smile, purple_smiley_get_shortcut(smiley)))
- continue;
-
- gtk_imhtml_smiley_destroy(gtksmiley);
- g_signal_handlers_disconnect_matched(G_OBJECT(smiley), G_SIGNAL_MATCH_DATA,
- 0, 0, NULL, NULL, gtksmiley);
- break;
- }
-
- if (list)
- gtk_smileys = g_slist_delete_link(gtk_smileys, list);
-}
-
-void pidgin_smiley_add_to_list(PurpleSmiley *smiley)
-{
- GtkIMHtmlSmiley *gtksmiley;
-
- gtksmiley = smiley_purple_to_gtkimhtml(smiley);
- add_gtkimhtml_to_list(gtksmiley);
- g_signal_connect(G_OBJECT(smiley), "destroy", G_CALLBACK(pidgin_smiley_del_from_list), NULL);
-}
-
-void pidgin_smileys_init(void)
-{
- GList *smileys;
- PurpleSmiley *smiley;
-
- if (gtk_smileys != NULL)
- return;
-
- smileys = purple_smileys_get_all();
-
- for (; smileys; smileys = g_list_delete_link(smileys, smileys)) {
- smiley = (PurpleSmiley*)smileys->data;
-
- pidgin_smiley_add_to_list(smiley);
- }
-}
-
-void pidgin_smileys_uninit(void)
-{
- GSList *list;
- GtkIMHtmlSmiley *gtksmiley;
-
- list = gtk_smileys;
-
- if (list == NULL)
- return;
-
- for (; list; list = g_slist_delete_link(list, list)) {
- gtksmiley = (GtkIMHtmlSmiley*)list->data;
- gtk_imhtml_smiley_destroy(gtksmiley);
- }
-
- gtk_smileys = NULL;
-}
-
-GSList *pidgin_smileys_get_all(void)
-{
- return gtk_smileys;
-}
-
-/******************************************************************************
- * Manager stuff
- *****************************************************************************/
-
-static void refresh_list(void);
-
-/******************************************************************************
- * The Add dialog
- ******************************************************************************/
-
-static void do_add(GtkWidget *widget, PidginSmiley *s)
-{
- const gchar *entry;
- PurpleSmiley *emoticon;
-
- entry = gtk_entry_get_text(GTK_ENTRY(s->smile));
-
- emoticon = purple_smileys_find_by_shortcut(entry);
- if (emoticon && emoticon != s->smiley) {
- gchar *msg;
- msg = g_strdup_printf(_("A custom smiley for '%s' already exists. "
- "Please use a different shortcut."), entry);
- purple_notify_error(s->parent, _("Custom Smiley"),
- _("Duplicate Shortcut"), msg);
- g_free(msg);
- return;
- }
-
- if (s->smiley) {
- if (s->filename) {
- gchar *data = NULL;
- size_t len;
- GError *err = NULL;
-
- if (!g_file_get_contents(s->filename, &data, &len, &err)) {
- purple_debug_error("gtksmiley", "Error reading %s: %s\n",
- s->filename, err->message);
- g_error_free(err);
-
- return;
- }
- purple_smiley_set_data(s->smiley, (guchar*)data, len);
- }
- purple_smiley_set_shortcut(s->smiley, entry);
- } else {
- purple_debug_info("gtksmiley", "adding a new smiley\n");
-
- if (s->filename == NULL) {
- gchar *buffer = NULL;
- gsize size = 0;
- gchar *filename;
- const gchar *dirname = purple_smileys_get_storing_dir();
-
- /* since this may be called before purple_smiley_new_* has ever been
- called, we create the storing dir, if it doesn't exist yet, to be
- able to save the pixbuf before adding the smiley */
- if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) {
- purple_debug_info("gtksmiley", "Creating smileys directory.\n");
-
- if (g_mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR) < 0) {
- purple_debug_error("gtksmiley",
- "Unable to create directory %s: %s\n",
- dirname, g_strerror(errno));
- }
- }
-
- if (s->data && s->datasize) {
- /* Cached data & size in memory */
- buffer = s->data;
- size = s->datasize;
- }
- else {
- /* Get the smiley from the custom pixbuf */
- gdk_pixbuf_save_to_buffer(s->custom_pixbuf, &buffer, &size,
- "png", NULL, "compression", "9", NULL, NULL);
- }
- filename = purple_util_get_image_filename(buffer, size);
- s->filename = g_build_filename(dirname, filename, NULL);
- purple_util_write_data_to_file_absolute(s->filename, buffer, size);
- g_free(filename);
- g_free(buffer);
- }
- emoticon = purple_smiley_new_from_file(entry, s->filename);
- if (emoticon)
- pidgin_smiley_add_to_list(emoticon);
- }
-
- if (smiley_manager != NULL)
- refresh_list();
-
- gtk_widget_destroy(s->parent);
-}
-
-static void do_add_select_cb(GtkWidget *widget, gint resp, PidginSmiley *s)
-{
- switch (resp) {
- case GTK_RESPONSE_ACCEPT:
- do_add(widget, s);
- break;
- case GTK_RESPONSE_DELETE_EVENT:
- case GTK_RESPONSE_CANCEL:
- gtk_widget_destroy(s->parent);
- break;
- default:
- purple_debug_error("gtksmiley", "no valid response\n");
- break;
- }
-}
-
-static void do_add_file_cb(const char *filename, gpointer data)
-{
- PidginSmiley *s = data;
- GdkPixbuf *pixbuf;
-
- if (!filename)
- return;
-
- g_free(s->filename);
- s->filename = g_strdup(filename);
- pixbuf = pidgin_pixbuf_new_from_file_at_scale(filename, 64, 64, FALSE);
- gtk_image_set_from_pixbuf(GTK_IMAGE(s->smiley_image), pixbuf);
- if (pixbuf)
- g_object_unref(G_OBJECT(pixbuf));
- gtk_widget_grab_focus(s->smile);
-
- if (s->entry_len > 0)
- gtk_dialog_set_response_sensitive(GTK_DIALOG(s->parent), GTK_RESPONSE_ACCEPT, TRUE);
-}
-
-static void
-open_image_selector(GtkWidget *widget, PidginSmiley *psmiley)
-{
- GtkWidget *file_chooser;
- file_chooser = pidgin_buddy_icon_chooser_new(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
- do_add_file_cb, psmiley);
- 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);
-}
-
-static void
-smiley_name_insert_cb(GtkEditable *editable,
- gchar *new_text,
- gint new_text_length,
- gint *position,
- gpointer user_data)
-{
- PidginSmiley *s = user_data;
- if (new_text_length != -1)
- s->entry_len += new_text_length;
- else
- s->entry_len += strlen(new_text);
-
- if (s->filename != NULL || s->custom_pixbuf != NULL || s->smiley != NULL)
- gtk_dialog_set_response_sensitive(GTK_DIALOG(s->parent), GTK_RESPONSE_ACCEPT, TRUE);
-}
-
-static void
-smiley_name_delete_cb(GtkEditable *editable,
- gint start_pos,
- gint end_pos,
- gpointer user_data)
-{
- PidginSmiley *s = user_data;
- s->entry_len -= end_pos - start_pos;
-
- if (s->entry_len <= 0)
- gtk_dialog_set_response_sensitive(GTK_DIALOG(s->parent), GTK_RESPONSE_ACCEPT, FALSE);
-}
-
-PidginSmiley *
-pidgin_smiley_edit(GtkWidget *widget, PurpleSmiley *smiley)
-{
- GtkWidget *vbox;
- GtkWidget *hbox;
- GtkWidget *label;
- GtkWidget *filech;
- GtkWidget *window;
- GdkPixbuf *pixbuf = NULL;
- PurpleStoredImage *stored_img;
-
- PidginSmiley *s = g_new0(PidginSmiley, 1);
- s->smiley = smiley;
-
- window = gtk_dialog_new_with_buttons(smiley ? _("Edit Smiley") : _("Add Smiley"),
- widget ? GTK_WINDOW(widget) : NULL,
- GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
- GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
- smiley ? GTK_STOCK_SAVE : GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT,
- NULL);
- s->parent = window;
- if (smiley)
- g_object_set_data(G_OBJECT(smiley), "edit-dialog", window);
-
- gtk_container_set_border_width(GTK_CONTAINER(window), PIDGIN_HIG_BORDER);
-
- gtk_dialog_set_default_response(GTK_DIALOG(window), GTK_RESPONSE_ACCEPT);
- g_signal_connect(window, "response", G_CALLBACK(do_add_select_cb), s);
-
- /* The vbox */
- vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
- gtk_container_add(GTK_CONTAINER(GTK_DIALOG(window)->vbox), vbox);
- gtk_widget_show(vbox);
-
- /* The hbox */
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
- gtk_container_add(GTK_CONTAINER(GTK_VBOX(vbox)), hbox);
-
- label = gtk_label_new_with_mnemonic(_("_Image:"));
- gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
- gtk_widget_show(label);
-
- filech = gtk_button_new();
- gtk_box_pack_end(GTK_BOX(hbox), filech, FALSE, FALSE, 0);
- pidgin_set_accessible_label(filech, label);
-
- s->smiley_image = gtk_image_new();
- gtk_container_add(GTK_CONTAINER(filech), s->smiley_image);
- if (smiley && (stored_img = purple_smiley_get_stored_image(smiley))) {
- pixbuf = pidgin_pixbuf_from_imgstore(stored_img);
- purple_imgstore_unref(stored_img);
- } else {
- GtkIconSize icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_SMALL);
- pixbuf = gtk_widget_render_icon(window, PIDGIN_STOCK_TOOLBAR_SELECT_AVATAR,
- icon_size, "PidginSmiley");
- }
-
- gtk_image_set_from_pixbuf(GTK_IMAGE(s->smiley_image), pixbuf);
- if (pixbuf != NULL)
- g_object_unref(G_OBJECT(pixbuf));
- g_signal_connect(G_OBJECT(filech), "clicked", G_CALLBACK(open_image_selector), s);
-
- gtk_widget_show_all(hbox);
-
- /* info */
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
- gtk_container_add(GTK_CONTAINER(GTK_VBOX(vbox)),hbox);
-
- /* Shortcut text */
- label = gtk_label_new_with_mnemonic(_("S_hortcut text:"));
- gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
- gtk_widget_show(label);
-
- s->smile = gtk_entry_new();
- gtk_entry_set_activates_default(GTK_ENTRY(s->smile), TRUE);
- pidgin_set_accessible_label(s->smile, label);
- if (smiley) {
- const char *shortcut = purple_smiley_get_shortcut(smiley);
- gtk_entry_set_text(GTK_ENTRY(s->smile), shortcut);
- s->entry_len = strlen(shortcut);
- }
- else
- gtk_dialog_set_response_sensitive(GTK_DIALOG(window), GTK_RESPONSE_ACCEPT, FALSE);
-
- /* gtk_entry_get_text_length is 2.14+, so we'll just keep track ourselves */
- g_signal_connect(G_OBJECT(s->smile), "insert-text", G_CALLBACK(smiley_name_insert_cb), s);
- g_signal_connect(G_OBJECT(s->smile), "delete-text", G_CALLBACK(smiley_name_delete_cb), s);
-
- gtk_box_pack_end(GTK_BOX(hbox), s->smile, FALSE, FALSE, 0);
- gtk_widget_show(s->smile);
-
- gtk_widget_show(hbox);
-
- gtk_widget_show(GTK_WIDGET(window));
- g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(pidgin_smiley_destroy), s);
- g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(purple_notify_close_with_handle), s);
-
- return s;
-}
-
-void
-pidgin_smiley_editor_set_shortcut(PidginSmiley *editor, const gchar *shortcut)
-{
- gtk_entry_set_text(GTK_ENTRY(editor->smile), shortcut ? shortcut : "");
-}
-
-void
-pidgin_smiley_editor_set_image(PidginSmiley *editor, GdkPixbuf *image)
-{
- if (editor->custom_pixbuf)
- g_object_unref(G_OBJECT(editor->custom_pixbuf));
- editor->custom_pixbuf = image ? g_object_ref(G_OBJECT(image)) : NULL;
- if (image) {
- gtk_image_set_from_pixbuf(GTK_IMAGE(editor->smiley_image), image);
- if (editor->entry_len > 0)
- gtk_dialog_set_response_sensitive(GTK_DIALOG(editor->parent),
- GTK_RESPONSE_ACCEPT, TRUE);
- }
- else
- gtk_dialog_set_response_sensitive(GTK_DIALOG(editor->parent),
- GTK_RESPONSE_ACCEPT, FALSE);
-}
-
-void
-pidgin_smiley_editor_set_data(PidginSmiley *editor, gpointer data, gsize datasize)
-{
- editor->data = data;
- editor->datasize = datasize;
-}
-
-/******************************************************************************
- * Delete smiley
- *****************************************************************************/
-static void delete_foreach(GtkTreeModel *model, GtkTreePath *path,
- GtkTreeIter *iter, gpointer data)
-{
- PurpleSmiley *smiley = NULL;
-
- gtk_tree_model_get(model, iter,
- SMILEY, &smiley,
- -1);
-
- if(smiley != NULL) {
- g_object_unref(G_OBJECT(smiley));
- pidgin_smiley_del_from_list(smiley);
- purple_smiley_delete(smiley);
- }
-}
-
-static void append_to_list(GtkTreeModel *model, GtkTreePath *path,
- GtkTreeIter *iter, gpointer data)
-{
- GList **list = data;
- *list = g_list_prepend(*list, gtk_tree_path_copy(path));
-}
-
-static void smiley_delete(SmileyManager *dialog)
-{
- GtkTreeSelection *selection;
- GList *list = NULL;
-
- selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview));
- gtk_tree_selection_selected_foreach(selection, delete_foreach, dialog);
- gtk_tree_selection_selected_foreach(selection, append_to_list, &list);
-
- while (list) {
- GtkTreeIter iter;
- if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, list->data))
- gtk_list_store_remove(GTK_LIST_STORE(dialog->model), &iter);
- gtk_tree_path_free(list->data);
- list = g_list_delete_link(list, list);
- }
-}
-/******************************************************************************
- * The Smiley Manager
- *****************************************************************************/
-static void add_columns(GtkWidget *treeview, SmileyManager *dialog)
-{
- GtkCellRenderer *rend;
- GtkTreeViewColumn *column;
-
- /* Icon */
- column = gtk_tree_view_column_new();
- gtk_tree_view_column_set_title(column, _("Smiley"));
- gtk_tree_view_column_set_resizable(column, TRUE);
- gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
-
- rend = gtk_cell_renderer_pixbuf_new();
- gtk_tree_view_column_pack_start(column, rend, FALSE);
- gtk_tree_view_column_add_attribute(column, rend, "pixbuf", ICON);
-
- /* Shortcut Text */
- column = gtk_tree_view_column_new();
- gtk_tree_view_column_set_title(column, _("Shortcut Text"));
- gtk_tree_view_column_set_resizable(column, TRUE);
- gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
-
- rend = gtk_cell_renderer_text_new();
- gtk_tree_view_column_pack_start(column, rend, TRUE);
- gtk_tree_view_column_add_attribute(column, rend, "text", SHORTCUT);
-}
-
-static void store_smiley_add(PurpleSmiley *smiley)
-{
- GtkTreeIter iter;
- PurpleStoredImage *img;
- GdkPixbuf *sized_smiley = NULL;
-
- if (smiley_manager == NULL)
- return;
-
- img = purple_smiley_get_stored_image(smiley);
-
- if (img != NULL) {
- GdkPixbuf *smiley_image = pidgin_pixbuf_from_imgstore(img);
- purple_imgstore_unref(img);
-
- if (smiley_image != NULL) {
- if (gdk_pixbuf_get_width(smiley_image) > 22 ||
- gdk_pixbuf_get_height(smiley_image) > 22) {
- sized_smiley = gdk_pixbuf_scale_simple(smiley_image,
- 22, 22, GDK_INTERP_HYPER);
- g_object_unref(G_OBJECT(smiley_image));
- } else {
- /* don't scale up smaller smileys, avoid blurryness */
- sized_smiley = smiley_image;
- }
- }
- }
-
-
- gtk_list_store_append(smiley_manager->model, &iter);
-
- gtk_list_store_set(smiley_manager->model, &iter,
- ICON, sized_smiley,
- SHORTCUT, purple_smiley_get_shortcut(smiley),
- SMILEY, smiley,
- -1);
-
- if (sized_smiley != NULL)
- g_object_unref(G_OBJECT(sized_smiley));
-}
-
-static void populate_smiley_list(SmileyManager *dialog)
-{
- GList *list;
- PurpleSmiley *emoticon;
-
- gtk_list_store_clear(dialog->model);
-
- for(list = purple_smileys_get_all(); list != NULL;
- list = g_list_delete_link(list, list)) {
- emoticon = (PurpleSmiley*)list->data;
-
- store_smiley_add(emoticon);
- }
-}
-
-static void smile_selected_cb(GtkTreeSelection *sel, SmileyManager *dialog)
-{
- gint selected;
-
- selected = gtk_tree_selection_count_selected_rows(sel);
-
- gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog->window),
- GTK_RESPONSE_NO, selected > 0);
-
- gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog->window),
- PIDGIN_RESPONSE_MODIFY, selected > 0);
-}
-
-static void
-smiley_edit_iter(SmileyManager *dialog, GtkTreeIter *iter)
-{
- PurpleSmiley *smiley = NULL;
- GtkWidget *window = NULL;
- gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), iter, SMILEY, &smiley, -1);
- if ((window = g_object_get_data(G_OBJECT(smiley), "edit-dialog")) != NULL)
- gtk_window_present(GTK_WINDOW(window));
- else
- pidgin_smiley_edit(gtk_widget_get_toplevel(GTK_WIDGET(dialog->treeview)), smiley);
- g_object_unref(G_OBJECT(smiley));
-}
-
-static void smiley_edit_cb(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data)
-{
- GtkTreeIter iter;
- SmileyManager *dialog = data;
-
- gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path);
- smiley_edit_iter(dialog, &iter);
-}
-
-static void
-edit_selected_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
-{
- smiley_edit_iter(data, iter);
-}
-
-static void
-smiley_got_url(PurpleUtilFetchUrlData *url_data, gpointer user_data,
- const gchar *smileydata, size_t len, const gchar *error_message)
-{
- SmileyManager *dialog = user_data;
- FILE *f;
- gchar *path;
- size_t wc;
- PidginSmiley *ps;
- GdkPixbuf *image;
-
- if ((error_message != NULL) || (len == 0)) {
- return;
- }
-
- f = purple_mkstemp(&path, TRUE);
- wc = fwrite(smileydata, len, 1, f);
- if (wc != 1) {
- purple_debug_warning("smiley_got_url", "Unable to write smiley data.\n");
- fclose(f);
- g_unlink(path);
- g_free(path);
- return;
- }
- fclose(f);
-
- image = pidgin_pixbuf_new_from_file(path);
- g_unlink(path);
- g_free(path);
- if (!image)
- return;
-
- ps = pidgin_smiley_edit(dialog->window, NULL);
- pidgin_smiley_editor_set_image(ps, image);
- pidgin_smiley_editor_set_data(ps, g_memdup(smileydata, len), len);
-}
-
-static void
-smiley_dnd_recv(GtkWidget *widget, GdkDragContext *dc, guint x, guint y,
- GtkSelectionData *sd, guint info, guint t, gpointer user_data)
-{
- SmileyManager *dialog = user_data;
- gchar *name = g_strchomp((gchar *)sd->data);
-
- if ((sd->length >= 0) && (sd->format == 8)) {
- /* Well, it looks like the drag event was cool.
- * Let's do something with it */
-
- if (!g_ascii_strncasecmp(name, "file://", 7)) {
- GError *converr = NULL;
- gchar *tmp;
- PidginSmiley *ps;
- /* It looks like we're dealing with a local file. Let's
- * just try and read it */
- if(!(tmp = g_filename_from_uri(name, NULL, &converr))) {
- purple_debug_error("smiley dnd", "%s\n",
- (converr ? converr->message :
- "g_filename_from_uri error"));
- return;
- }
- ps = pidgin_smiley_edit(dialog->window, NULL);
- do_add_file_cb(tmp, ps);
- if (gtk_image_get_pixbuf(GTK_IMAGE(ps->smiley_image)) == NULL)
- gtk_dialog_response(GTK_DIALOG(ps->parent), GTK_RESPONSE_CANCEL);
- g_free(tmp);
- } else if (!g_ascii_strncasecmp(name, "http://", 7)) {
- /* Oo, a web drag and drop. This is where things
- * will start to get interesting */
- purple_util_fetch_url(name, TRUE, NULL, FALSE, smiley_got_url, dialog);
- } else if (!g_ascii_strncasecmp(name, "https://", 8)) {
- /* purple_util_fetch_url() doesn't support HTTPS */
- char *tmp = g_strdup(name + 1);
- tmp[0] = 'h';
- tmp[1] = 't';
- tmp[2] = 't';
- tmp[3] = 'p';
-
- purple_util_fetch_url(tmp, TRUE, NULL, FALSE, smiley_got_url, dialog);
- g_free(tmp);
- }
-
- gtk_drag_finish(dc, TRUE, FALSE, t);
- }
-
- gtk_drag_finish(dc, FALSE, FALSE, t);
-}
-
-static GtkWidget *smiley_list_create(SmileyManager *dialog)
-{
- GtkWidget *treeview;
- GtkTreeSelection *sel;
- GtkTargetEntry te[3] = {
- {"text/plain", 0, 0},
- {"text/uri-list", 0, 1},
- {"STRING", 0, 2}
- };
-
- /* Create the list model */
- dialog->model = gtk_list_store_new(N_COL,
- GDK_TYPE_PIXBUF, /* ICON */
- G_TYPE_STRING, /* SHORTCUT */
- G_TYPE_OBJECT /* SMILEY */
- );
-
- /* the actual treeview */
- treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model));
- dialog->treeview = treeview;
- gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
- gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(dialog->model), SHORTCUT, GTK_SORT_ASCENDING);
- g_object_unref(G_OBJECT(dialog->model));
-
- sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
- gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE);
-
- g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(smile_selected_cb), dialog);
- g_signal_connect(G_OBJECT(treeview), "row_activated", G_CALLBACK(smiley_edit_cb), dialog);
-
- gtk_drag_dest_set(treeview,
- GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP,
- te, G_N_ELEMENTS(te), GDK_ACTION_COPY | GDK_ACTION_MOVE);
- g_signal_connect(G_OBJECT(treeview), "drag_data_received", G_CALLBACK(smiley_dnd_recv), dialog);
-
- gtk_widget_show(treeview);
-
- add_columns(treeview, dialog);
- populate_smiley_list(dialog);
-
- return pidgin_make_scrollable(treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, -1);
-}
-
-static void refresh_list()
-{
- populate_smiley_list(smiley_manager);
-}
-
-static void smiley_manager_select_cb(GtkWidget *widget, gint resp, SmileyManager *dialog)
-{
- GtkTreeSelection *selection = NULL;
-
- switch (resp) {
- case GTK_RESPONSE_YES:
- pidgin_smiley_edit(dialog->window, NULL);
- break;
- case GTK_RESPONSE_NO:
- smiley_delete(dialog);
- break;
- case GTK_RESPONSE_DELETE_EVENT:
- case GTK_RESPONSE_CLOSE:
- gtk_widget_destroy(dialog->window);
- g_free(smiley_manager);
- smiley_manager = NULL;
- break;
- case PIDGIN_RESPONSE_MODIFY:
- /* Find smiley of selection... */
- selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview));
- gtk_tree_selection_selected_foreach(selection, edit_selected_cb, dialog);
- break;
- default:
- purple_debug_info("gtksmiley", "No valid selection\n");
- break;
- }
-}
-
-void pidgin_smiley_manager_show(void)
-{
- SmileyManager *dialog;
- GtkWidget *win;
- GtkWidget *sw;
- GtkWidget *vbox;
-
- if (smiley_manager) {
- gtk_window_present(GTK_WINDOW(smiley_manager->window));
- return;
- }
-
- dialog = g_new0(SmileyManager, 1);
- smiley_manager = dialog;
-
- dialog->window = win = gtk_dialog_new_with_buttons(
- _("Custom Smiley Manager"),
- NULL,
- GTK_DIALOG_DESTROY_WITH_PARENT,
- PIDGIN_STOCK_ADD, GTK_RESPONSE_YES,
- PIDGIN_STOCK_MODIFY, PIDGIN_RESPONSE_MODIFY,
- GTK_STOCK_DELETE, GTK_RESPONSE_NO,
- GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
- NULL);
-
- gtk_window_set_default_size(GTK_WINDOW(win), 50, 400);
- gtk_window_set_role(GTK_WINDOW(win), "custom_smiley_manager");
- gtk_container_set_border_width(GTK_CONTAINER(win),PIDGIN_HIG_BORDER);
- gtk_dialog_set_response_sensitive(GTK_DIALOG(win), GTK_RESPONSE_NO, FALSE);
- gtk_dialog_set_response_sensitive(GTK_DIALOG(win),
- PIDGIN_RESPONSE_MODIFY, FALSE);
-
- g_signal_connect(win, "response", G_CALLBACK(smiley_manager_select_cb),
- dialog);
-
- /* The vbox */
- vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
- gtk_container_add(GTK_CONTAINER(GTK_DIALOG(win)->vbox), vbox);
- gtk_widget_show(vbox);
-
- /* get the scrolled window with all stuff */
- sw = smiley_list_create(dialog);
- gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
- gtk_widget_show(sw);
-
- gtk_widget_show(win);
-}
diff --git a/pidgin/gtksmiley.h b/pidgin/gtksmiley.h
deleted file mode 100644
index 8cf62a8add..0000000000
--- a/pidgin/gtksmiley.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/**
- * @file gtksmiley.h GTK+ Custom Smiley API
- * @ingroup pidgin
- * @since 2.5.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_GTKSMILEY_H
-#define PIDGIN_GTKSMILEY_H
-
-#include "smiley.h"
-
-typedef struct _PidginSmiley PidginSmiley;
-
-/**
- * Add a PurpleSmiley to the GtkIMHtmlSmiley's list to be able to use it
- * in pidgin
- *
- * @param smiley The smiley to be added.
- */
-void pidgin_smiley_add_to_list(PurpleSmiley *smiley);
-
-/**
- * Delete a PurpleSmiley from the GtkIMHtmlSmiley's list
- *
- * @param smiley The smiley to be deleted.
- */
-void pidgin_smiley_del_from_list(PurpleSmiley *smiley);
-
-/**
- * Load the GtkIMHtml list
- */
-void pidgin_smileys_init(void);
-
-/**
- * Uninit the GtkIMHtml list
- */
-void pidgin_smileys_uninit(void);
-
-/**
- * Returns a GSList with the GtkIMHtmlSmiley of each custom smiley
- *
- * @constreturn A GtkIMHmlSmiley list
- */
-GSList *pidgin_smileys_get_all(void);
-
-/******************************************************************************
- * Smiley Manager
- *****************************************************************************/
-/**
- * Displays the Smiley Manager Window
- */
-void pidgin_smiley_manager_show(void);
-
-/**
- * Shows an editor for a smiley.
- *
- * @param widget The parent widget to be linked or @c NULL
- * @param smiley The PurpleSmiley to be edited, or @c NULL for a new smiley
- * @return The smiley add dialog
- *
- * @see pidgin_smiley_editor_set_shortcut
- * @see pidgin_smiley_editor_set_image
- */
-PidginSmiley *pidgin_smiley_edit(GtkWidget *widget, PurpleSmiley *smiley);
-
-/**
- * Set the shortcut in a smiley add dialog
- *
- * @param editor A smiley editor dialog (created by pidgin_smiley_edit)
- * @param shortcut The shortcut to set
- */
-void pidgin_smiley_editor_set_shortcut(PidginSmiley *editor, const gchar *shortcut);
-
-/**
- * Set the image in a smiley add dialog
- *
- * @param editor A smiley editor dialog
- * @param image A GdkPixbuf image
- */
-void pidgin_smiley_editor_set_image(PidginSmiley *editor, GdkPixbuf *image);
-
-/**
- * Sets the image data in a smiley add dialog
- *
- * @param editor A smiley editor dialog
- * @param data A pointer to smiley's data
- * @param datasize The size of smiley's data
- *
- * @since 2.6.0
- */
-void pidgin_smiley_editor_set_data(PidginSmiley *editor, gpointer data, gsize datasize);
-
-#endif /* PIDGIN_GTKSMILEY_H */
diff --git a/pidgin/gtksound.c b/pidgin/gtksound.c
index e8e96d5de9..43c0897e3e 100644
--- a/pidgin/gtksound.c
+++ b/pidgin/gtksound.c
@@ -1,8 +1,3 @@
-/*
- * @file gtksound.c GTK+ Sound
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -85,19 +80,19 @@ unmute_login_sounds_cb(gpointer data)
}
static gboolean
-chat_nick_matches_name(PurpleConversation *conv, const char *aname)
+chat_nick_matches_name(PurpleChatConversation *chat, const char *aname)
{
- PurpleConvChat *chat = NULL;
char *nick = NULL;
char *name = NULL;
gboolean ret = FALSE;
- chat = purple_conversation_get_chat_data(conv);
if (chat==NULL)
return ret;
- nick = g_strdup(purple_normalize(conv->account, chat->nick));
- name = g_strdup(purple_normalize(conv->account, aname));
+ nick = g_strdup(purple_normalize(purple_conversation_get_account(
+ PURPLE_CONVERSATION(chat)), purple_chat_conversation_get_nick(chat)));
+ name = g_strdup(purple_normalize(purple_conversation_get_account(
+ PURPLE_CONVERSATION(chat)), aname));
if (g_utf8_collate(nick, name) == 0)
ret = TRUE;
@@ -115,6 +110,8 @@ chat_nick_matches_name(PurpleConversation *conv, const char *aname)
static void
play_conv_event(PurpleConversation *conv, PurpleSoundEventID event)
{
+ g_return_if_fail(event < PURPLE_NUM_SOUNDS);
+
/* If we should not play the sound for some reason, then exit early */
if (conv != NULL && PIDGIN_IS_PIDGIN_CONVERSATION(conv))
{
@@ -155,64 +152,63 @@ im_msg_received_cb(PurpleAccount *account, char *sender,
}
static void
-im_msg_sent_cb(PurpleAccount *account, const char *receiver,
- const char *message, PurpleSoundEventID event)
+im_msg_sent_cb(PurpleAccount *account, PurpleMessage *msg,
+ PurpleSoundEventID event)
{
- PurpleConversation *conv = purple_find_conversation_with_account(
- PURPLE_CONV_TYPE_IM, receiver, account);
+ PurpleConversation *conv = PURPLE_CONVERSATION(
+ purple_conversations_find_im_with_account(
+ purple_message_get_recipient(msg), account));
play_conv_event(conv, event);
}
static void
-chat_buddy_join_cb(PurpleConversation *conv, const char *name,
- PurpleConvChatBuddyFlags flags, gboolean new_arrival,
+chat_user_join_cb(PurpleChatConversation *chat, const char *name,
+ PurpleChatUserFlags flags, gboolean new_arrival,
PurpleSoundEventID event)
{
- if (new_arrival && !chat_nick_matches_name(conv, name))
- play_conv_event(conv, event);
+ if (new_arrival && !chat_nick_matches_name(chat, name))
+ play_conv_event(PURPLE_CONVERSATION(chat), event);
}
static void
-chat_buddy_left_cb(PurpleConversation *conv, const char *name,
+chat_user_left_cb(PurpleChatConversation *chat, const char *name,
const char *reason, PurpleSoundEventID event)
{
- if (!chat_nick_matches_name(conv, name))
- play_conv_event(conv, event);
+ if (!chat_nick_matches_name(chat, name))
+ play_conv_event(PURPLE_CONVERSATION(chat), event);
}
static void
-chat_msg_sent_cb(PurpleAccount *account, const char *message,
- int id, PurpleSoundEventID event)
+chat_msg_sent_cb(PurpleAccount *account, PurpleMessage *msg, int id,
+ PurpleSoundEventID event)
{
PurpleConnection *conn = purple_account_get_connection(account);
PurpleConversation *conv = NULL;
if (conn!=NULL)
- conv = purple_find_chat(conn,id);
+ conv = PURPLE_CONVERSATION(purple_conversations_find_chat(conn,id));
play_conv_event(conv, event);
}
static void
chat_msg_received_cb(PurpleAccount *account, char *sender,
- char *message, PurpleConversation *conv,
+ char *message, PurpleChatConversation *chat,
PurpleMessageFlags flags, PurpleSoundEventID event)
{
- PurpleConvChat *chat;
-
+ PurpleConversation *conv = PURPLE_CONVERSATION(chat);
if (flags & PURPLE_MESSAGE_DELAYED || flags & PURPLE_MESSAGE_NOTIFY)
return;
- chat = purple_conversation_get_chat_data(conv);
- g_return_if_fail(chat != NULL);
+ g_return_if_fail(conv != NULL);
- if (purple_conv_chat_is_user_ignored(chat, sender))
+ if (purple_chat_conversation_is_ignored_user(chat, sender))
return;
- if (chat_nick_matches_name(conv, sender))
+ if (chat_nick_matches_name(chat, sender))
return;
- if (flags & PURPLE_MESSAGE_NICK || purple_utf8_has_word(message, chat->nick))
+ if (flags & PURPLE_MESSAGE_NICK || purple_utf8_has_word(message, purple_chat_conversation_get_nick(chat)))
/* This isn't quite right; if you have the PURPLE_SOUND_CHAT_NICK event disabled
* and the PURPLE_SOUND_CHAT_SAY event enabled, you won't get a sound at all */
play_conv_event(conv, PURPLE_SOUND_CHAT_NICK);
@@ -319,13 +315,11 @@ pidgin_sound_init(void)
#ifdef USE_GSTREAMER
purple_debug_info("sound", "Initializing sound output drivers.\n");
-#ifdef GST_CAN_DISABLE_FORKING
- gst_registry_fork_set_enabled (FALSE);
-#endif
+ gst_registry_fork_set_enabled(FALSE);
if ((gst_init_failed = !gst_init_check(NULL, NULL, &error))) {
purple_notify_error(NULL, _("GStreamer Failure"),
_("GStreamer failed to initialize."),
- error ? error->message : "");
+ error ? error->message : "", NULL);
if (error) {
g_error_free(error);
error = NULL;
@@ -345,11 +339,11 @@ pidgin_sound_init(void)
purple_signal_connect(conv_handle, "sent-im-msg",
gtk_sound_handle, PURPLE_CALLBACK(im_msg_sent_cb),
GINT_TO_POINTER(PURPLE_SOUND_SEND));
- purple_signal_connect(conv_handle, "chat-buddy-joined",
- gtk_sound_handle, PURPLE_CALLBACK(chat_buddy_join_cb),
+ purple_signal_connect(conv_handle, "chat-user-joined",
+ gtk_sound_handle, PURPLE_CALLBACK(chat_user_join_cb),
GINT_TO_POINTER(PURPLE_SOUND_CHAT_JOIN));
- purple_signal_connect(conv_handle, "chat-buddy-left",
- gtk_sound_handle, PURPLE_CALLBACK(chat_buddy_left_cb),
+ purple_signal_connect(conv_handle, "chat-user-left",
+ gtk_sound_handle, PURPLE_CALLBACK(chat_user_left_cb),
GINT_TO_POINTER(PURPLE_SOUND_CHAT_LEAVE));
purple_signal_connect(conv_handle, "sent-chat-msg",
gtk_sound_handle, PURPLE_CALLBACK(chat_msg_sent_cb),
@@ -427,6 +421,18 @@ expire_old_child(gpointer data)
}
#endif
+#ifdef _WIN32
+static void
+pidgin_sound_play_file_win32(const char *filename)
+{
+ wchar_t *wc_filename = g_utf8_to_utf16(filename,
+ -1, NULL, NULL, NULL);
+ if (!PlaySoundW(wc_filename, NULL, SND_ASYNC | SND_FILENAME))
+ purple_debug(PURPLE_DEBUG_ERROR, "sound", "Error playing sound.\n");
+ g_free(wc_filename);
+}
+#endif /* _WIN32 */
+
static void
pidgin_sound_play_file(const char *filename)
{
@@ -450,6 +456,12 @@ pidgin_sound_play_file(const char *filename)
gdk_beep();
return;
}
+#ifdef _WIN32
+ else if (!strcmp(method, "playsoundw")) {
+ pidgin_sound_play_file_win32(filename);
+ return;
+ }
+#endif /* _WIN32 */
if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
purple_debug_error("gtksound", "sound file (%s) does not exist.\n", filename);
@@ -510,11 +522,22 @@ pidgin_sound_play_file(const char *filename)
if (gst_init_failed) /* Perhaps do gdk_beep instead? */
return;
volume = (float)(CLAMP(purple_prefs_get_int(PIDGIN_PREFS_ROOT "/sound/volume"),0,100)) / 50;
+#ifdef _WIN32
if (!strcmp(method, "automatic")) {
- sink = gst_element_factory_make("gconfaudiosink", "sink");
+ sink = gst_element_factory_make("directsoundsink", "sink");
+ if (sink == NULL)
+ sink = gst_element_factory_make("waveformsink", "sink");
+ if (sink == NULL)
+ sink = gst_element_factory_make("gconfaudiosink", "sink");
+ } else if (!strcmp(method, "directsound")) {
+ sink = gst_element_factory_make("directsoundsink", "sink");
+ } else if (!strcmp(method, "waveform")) {
+ sink = gst_element_factory_make("waveformsink", "sink");
}
-#ifndef _WIN32
- else if (!strcmp(method, "esd")) {
+#else
+ if (!strcmp(method, "automatic")) {
+ sink = gst_element_factory_make("gconfaudiosink", "sink");
+ } else if (!strcmp(method, "esd")) {
sink = gst_element_factory_make("esdsink", "sink");
} else if (!strcmp(method, "alsa")) {
sink = gst_element_factory_make("alsasink", "sink");
@@ -530,13 +553,22 @@ pidgin_sound_play_file(const char *filename)
return;
}
+#if GST_CHECK_VERSION(1,0,0)
play = gst_element_factory_make("playbin", "play");
+#else
+ play = gst_element_factory_make("playbin2", "play");
+#endif
if (play == NULL) {
return;
}
+#ifdef _WIN32
+ uri = g_strdup_printf("file:///%s", filename);
+ g_strdelimit(uri, "\\", '/');
+#else
uri = g_strdup_printf("file://%s", filename);
+#endif
g_object_set(G_OBJECT(play), "uri", uri,
"volume", volume,
@@ -555,15 +587,7 @@ pidgin_sound_play_file(const char *filename)
#ifndef _WIN32
gdk_beep();
#else /* _WIN32 */
- purple_debug_info("sound", "Playing %s\n", filename);
-
- {
- wchar_t *wc_filename = g_utf8_to_utf16(filename,
- -1, NULL, NULL, NULL);
- if (!PlaySoundW(wc_filename, NULL, SND_ASYNC | SND_FILENAME))
- purple_debug(PURPLE_DEBUG_ERROR, "sound", "Error playing sound.\n");
- g_free(wc_filename);
- }
+ pidgin_sound_play_file_win32(filename);
#endif /* _WIN32 */
#endif /* USE_GSTREAMER */
@@ -609,11 +633,11 @@ pidgin_sound_play_event(PurpleSoundEventID event)
}
}
- if (!filename || !strlen(filename)) { /* Use Default sounds */
+ if (!filename || !strlen(filename)) { /* Use Default sounds */
g_free(filename);
- /* XXX Consider creating a constant for "sounds/purple" to be shared with Finch */
- filename = g_build_filename(DATADIR, "sounds", "purple", sounds[event].def, NULL);
+ filename = g_build_filename(PURPLE_DATADIR,
+ "sounds", "purple", sounds[event].def, NULL);
}
purple_sound_play_file(filename, NULL);
diff --git a/pidgin/gtksound.h b/pidgin/gtksound.h
index f55713907e..8eb9dbafa4 100644
--- a/pidgin/gtksound.h
+++ b/pidgin/gtksound.h
@@ -1,8 +1,3 @@
-/**
- * @file gtksound.h GTK+ Sound API
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -23,55 +18,71 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
#ifndef _PIDGINSOUND_H_
#define _PIDGINSOUND_H_
+/**
+ * SECTION:gtksound
+ * @section_id: pidgin-gtksound
+ * @short_description: <filename>gtksound.h</filename>
+ * @title: Sound API
+ */
#include "sound.h"
+G_BEGIN_DECLS
+
/**************************************************************************/
-/** @name GTK+ Sound API */
+/* GTK+ Sound API */
/**************************************************************************/
-/*@{*/
/**
+ * pidgin_sound_get_event_option:
+ * @event: The event.
+ *
* Get the prefs option for an event.
*
- * @param event The event.
- * @return The option.
+ * Returns: The option.
*/
const char *pidgin_sound_get_event_option(PurpleSoundEventID event);
/**
+ * pidgin_sound_get_event_label:
+ * @event: The event.
+ *
* Get the label for an event.
*
- * @param event The event.
- * @return The label.
+ * Returns: The label.
*/
const char *pidgin_sound_get_event_label(PurpleSoundEventID event);
/**
+ * pidgin_sound_get_ui_ops:
+ *
* Gets GTK+ sound UI ops.
*
- * @return The UI operations structure.
+ * Returns: The UI operations structure.
*/
PurpleSoundUiOps *pidgin_sound_get_ui_ops(void);
/**
+ * pidgin_sound_get_handle:
+ *
* Get the handle for the GTK+ sound system.
*
- * @return The handle to the sound system
+ * Returns: The handle to the sound system
*/
void *pidgin_sound_get_handle(void);
/**
- * Returns true Pidgin is using customized sounds
+ * pidgin_sound_is_customized:
*
- * @return TRUE if non default sounds are used.
+ * Returns true Pidgin is using customized sounds
*
- * @since 2.6.0
+ * Returns: TRUE if non default sounds are used.
*/
gboolean pidgin_sound_is_customized(void);
-/*@}*/
+G_END_DECLS
#endif /* _PIDGINSOUND_H_ */
diff --git a/pidgin/gtksourceiter.c b/pidgin/gtksourceiter.c
deleted file mode 100644
index 050f683c43..0000000000
--- a/pidgin/gtksourceiter.c
+++ /dev/null
@@ -1,777 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
- * gtksourceiter.c
- *
- * 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.
- *
- * The following copyright notice applies to this file:
- *
- * Copyright (C) 2000 - 2005 Paolo Maggi
- * Copyright (C) 2002, 2003 Jeroen Zwartepoorte
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Library 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 Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library 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.
- */
-
-/*
- * Parts of this file are copied from the gedit and glimmer project.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include "gtksourceiter.h"
-
-#define GTK_TEXT_UNKNOWN_CHAR 0xFFFC
-
-/* this function acts like g_utf8_offset_to_pointer() except that if it finds a
- * decomposable character it consumes the decomposition length from the given
- * offset. So it's useful when the offset was calculated for the normalized
- * version of str, but we need a pointer to str itself. */
-static const gchar *
-pointer_from_offset_skipping_decomp (const gchar *str, gint offset)
-{
- gchar *casefold, *normal;
- const gchar *p, *q;
-
- p = str;
- while (offset > 0)
- {
- q = g_utf8_next_char (p);
- casefold = g_utf8_casefold (p, q - p);
- normal = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
- offset -= g_utf8_strlen (normal, -1);
- g_free (casefold);
- g_free (normal);
- p = q;
- }
- return p;
-}
-
-static const gchar *
-g_utf8_strcasestr (const gchar *haystack, const gchar *needle)
-{
- gsize needle_len;
- gsize haystack_len;
- const gchar *ret = NULL;
- gchar *p;
- gchar *casefold;
- gchar *caseless_haystack;
- gint i;
-
- g_return_val_if_fail (haystack != NULL, NULL);
- g_return_val_if_fail (needle != NULL, NULL);
-
- casefold = g_utf8_casefold (haystack, -1);
- caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
- g_free (casefold);
-
- needle_len = g_utf8_strlen (needle, -1);
- haystack_len = g_utf8_strlen (caseless_haystack, -1);
-
- if (needle_len == 0)
- {
- ret = (gchar *)haystack;
- goto finally_1;
- }
-
- if (haystack_len < needle_len)
- {
- ret = NULL;
- goto finally_1;
- }
-
- p = (gchar*)caseless_haystack;
- needle_len = strlen (needle);
- i = 0;
-
- while (*p)
- {
- if ((strncmp (p, needle, needle_len) == 0))
- {
- ret = pointer_from_offset_skipping_decomp (haystack, i);
- goto finally_1;
- }
-
- p = g_utf8_next_char (p);
- i++;
- }
-
-finally_1:
- g_free (caseless_haystack);
-
- return ret;
-}
-
-static const gchar *
-g_utf8_strrcasestr (const gchar *haystack, const gchar *needle)
-{
- gsize needle_len;
- gsize haystack_len;
- const gchar *ret = NULL;
- gchar *p;
- gchar *casefold;
- gchar *caseless_haystack;
- gint i;
-
- g_return_val_if_fail (haystack != NULL, NULL);
- g_return_val_if_fail (needle != NULL, NULL);
-
- casefold = g_utf8_casefold (haystack, -1);
- caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
- g_free (casefold);
-
- needle_len = g_utf8_strlen (needle, -1);
- haystack_len = g_utf8_strlen (caseless_haystack, -1);
-
- if (needle_len == 0)
- {
- ret = (gchar *)haystack;
- goto finally_1;
- }
-
- if (haystack_len < needle_len)
- {
- ret = NULL;
- goto finally_1;
- }
-
- i = haystack_len - needle_len;
- p = g_utf8_offset_to_pointer (caseless_haystack, i);
- needle_len = strlen (needle);
-
- while (1)
- {
- if (strncmp (p, needle, needle_len) == 0)
- {
- ret = pointer_from_offset_skipping_decomp (haystack, i);
- goto finally_1;
- }
-
- if (p > caseless_haystack)
- p = g_utf8_prev_char (p);
- else
- goto finally_1;
-
- i--;
- }
-
-finally_1:
- g_free (caseless_haystack);
-
- return ret;
-}
-
-static gboolean
-g_utf8_caselessnmatch (const char *s1, const char *s2,
- gssize n1, gssize n2)
-{
- gchar *casefold;
- gchar *normalized_s1;
- gchar *normalized_s2;
- gint len_s1;
- gint len_s2;
- gboolean ret = FALSE;
-
- g_return_val_if_fail (s1 != NULL, FALSE);
- g_return_val_if_fail (s2 != NULL, FALSE);
- g_return_val_if_fail (n1 > 0, FALSE);
- g_return_val_if_fail (n2 > 0, FALSE);
-
- casefold = g_utf8_casefold (s1, n1);
- normalized_s1 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
- g_free (casefold);
-
- casefold = g_utf8_casefold (s2, n2);
- normalized_s2 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
- g_free (casefold);
-
- len_s1 = strlen (normalized_s1);
- len_s2 = strlen (normalized_s2);
-
- if (len_s1 < len_s2)
- goto finally_2;
-
- ret = (strncmp (normalized_s1, normalized_s2, len_s2) == 0);
-
-finally_2:
- g_free (normalized_s1);
- g_free (normalized_s2);
-
- return ret;
-}
-
-static void
-forward_chars_with_skipping (GtkTextIter *iter,
- gint count,
- gboolean skip_invisible,
- gboolean skip_nontext,
- gboolean skip_decomp)
-{
- gint i;
-
- g_return_if_fail (count >= 0);
-
- i = count;
-
- while (i > 0)
- {
- gboolean ignored = FALSE;
-
- /* minimal workaround to avoid the infinite loop of bug #168247.
- * It doesn't fix the problemjust the symptom...
- */
- if (gtk_text_iter_is_end (iter))
- return;
-
- if (skip_nontext && gtk_text_iter_get_char (iter) == GTK_TEXT_UNKNOWN_CHAR)
- ignored = TRUE;
-
-#if 0
- if (!ignored && skip_invisible &&
- /* _gtk_text_btree_char_is_invisible (iter)*/ FALSE)
- ignored = TRUE;
-#endif
-
- if (!ignored && skip_decomp)
- {
- /* being UTF8 correct sucks; this accounts for extra
- offsets coming from canonical decompositions of
- UTF8 characters (e.g. accented characters) which
- g_utf8_normalize() performs */
- gchar *normal;
- gchar buffer[6];
- gint buffer_len;
-
- buffer_len = g_unichar_to_utf8 (gtk_text_iter_get_char (iter), buffer);
- normal = g_utf8_normalize (buffer, buffer_len, G_NORMALIZE_NFD);
- i -= (g_utf8_strlen (normal, -1) - 1);
- g_free (normal);
- }
-
- gtk_text_iter_forward_char (iter);
-
- if (!ignored)
- --i;
- }
-}
-
-static gboolean
-lines_match (const GtkTextIter *start,
- const gchar **lines,
- gboolean visible_only,
- gboolean slice,
- GtkTextIter *match_start,
- GtkTextIter *match_end)
-{
- GtkTextIter next;
- gchar *line_text;
- const gchar *found;
- gint offset;
-
- if (*lines == NULL || **lines == '\0')
- {
- if (match_start)
- *match_start = *start;
- if (match_end)
- *match_end = *start;
- return TRUE;
- }
-
- next = *start;
- gtk_text_iter_forward_line (&next);
-
- /* No more text in buffer, but *lines is nonempty */
- if (gtk_text_iter_equal (start, &next))
- return FALSE;
-
- if (slice)
- {
- if (visible_only)
- line_text = gtk_text_iter_get_visible_slice (start, &next);
- else
- line_text = gtk_text_iter_get_slice (start, &next);
- }
- else
- {
- if (visible_only)
- line_text = gtk_text_iter_get_visible_text (start, &next);
- else
- line_text = gtk_text_iter_get_text (start, &next);
- }
-
- if (match_start) /* if this is the first line we're matching */
- {
- found = g_utf8_strcasestr (line_text, *lines);
- }
- else
- {
- /* If it's not the first line, we have to match from the
- * start of the line.
- */
- if (g_utf8_caselessnmatch (line_text, *lines, strlen (line_text),
- strlen (*lines)))
- found = line_text;
- else
- found = NULL;
- }
-
- if (found == NULL)
- {
- g_free (line_text);
- return FALSE;
- }
-
- /* Get offset to start of search string */
- offset = g_utf8_strlen (line_text, found - line_text);
-
- next = *start;
-
- /* If match start needs to be returned, set it to the
- * start of the search string.
- */
- forward_chars_with_skipping (&next, offset, visible_only, !slice, FALSE);
- if (match_start)
- {
- *match_start = next;
- }
-
- /* Go to end of search string */
- forward_chars_with_skipping (&next, g_utf8_strlen (*lines, -1), visible_only, !slice, TRUE);
-
- g_free (line_text);
-
- ++lines;
-
- if (match_end)
- *match_end = next;
-
- /* pass NULL for match_start, since we don't need to find the
- * start again.
- */
- return lines_match (&next, lines, visible_only, slice, NULL, match_end);
-}
-
-static gboolean
-backward_lines_match (const GtkTextIter *start,
- const gchar **lines,
- gboolean visible_only,
- gboolean slice,
- GtkTextIter *match_start,
- GtkTextIter *match_end)
-{
- GtkTextIter line, next;
- gchar *line_text;
- const gchar *found;
- gint offset;
-
- if (*lines == NULL || **lines == '\0')
- {
- if (match_start)
- *match_start = *start;
- if (match_end)
- *match_end = *start;
- return TRUE;
- }
-
- line = next = *start;
- if (gtk_text_iter_get_line_offset (&next) == 0)
- {
- if (!gtk_text_iter_backward_line (&next))
- return FALSE;
- }
- else
- gtk_text_iter_set_line_offset (&next, 0);
-
- if (slice)
- {
- if (visible_only)
- line_text = gtk_text_iter_get_visible_slice (&next, &line);
- else
- line_text = gtk_text_iter_get_slice (&next, &line);
- }
- else
- {
- if (visible_only)
- line_text = gtk_text_iter_get_visible_text (&next, &line);
- else
- line_text = gtk_text_iter_get_text (&next, &line);
- }
-
- if (match_start) /* if this is the first line we're matching */
- {
- found = g_utf8_strrcasestr (line_text, *lines);
- }
- else
- {
- /* If it's not the first line, we have to match from the
- * start of the line.
- */
- if (g_utf8_caselessnmatch (line_text, *lines, strlen (line_text),
- strlen (*lines)))
- found = line_text;
- else
- found = NULL;
- }
-
- if (found == NULL)
- {
- g_free (line_text);
- return FALSE;
- }
-
- /* Get offset to start of search string */
- offset = g_utf8_strlen (line_text, found - line_text);
-
- forward_chars_with_skipping (&next, offset, visible_only, !slice, FALSE);
-
- /* If match start needs to be returned, set it to the
- * start of the search string.
- */
- if (match_start)
- {
- *match_start = next;
- }
-
- /* Go to end of search string */
- forward_chars_with_skipping (&next, g_utf8_strlen (*lines, -1), visible_only, !slice, TRUE);
-
- g_free (line_text);
-
- ++lines;
-
- if (match_end)
- *match_end = next;
-
- /* try to match the rest of the lines forward, passing NULL
- * for match_start so lines_match will try to match the entire
- * line */
- return lines_match (&next, lines, visible_only,
- slice, NULL, match_end);
-}
-
-/* strsplit () that retains the delimiter as part of the string. */
-static gchar **
-strbreakup (const char *string,
- const char *delimiter,
- gint max_tokens)
-{
- GSList *string_list = NULL, *slist;
- gchar **str_array, *s, *casefold, *new_string;
- guint i, n = 1;
-
- g_return_val_if_fail (string != NULL, NULL);
- g_return_val_if_fail (delimiter != NULL, NULL);
-
- if (max_tokens < 1)
- max_tokens = G_MAXINT;
-
- s = strstr (string, delimiter);
- if (s)
- {
- guint delimiter_len = strlen (delimiter);
-
- do
- {
- guint len;
-
- len = s - string + delimiter_len;
- new_string = g_new (gchar, len + 1);
- strncpy (new_string, string, len);
- new_string[len] = 0;
- casefold = g_utf8_casefold (new_string, -1);
- g_free (new_string);
- new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
- g_free (casefold);
- string_list = g_slist_prepend (string_list, new_string);
- n++;
- string = s + delimiter_len;
- s = strstr (string, delimiter);
- } while (--max_tokens && s);
- }
-
- if (*string)
- {
- n++;
- casefold = g_utf8_casefold (string, -1);
- new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
- g_free (casefold);
- string_list = g_slist_prepend (string_list, new_string);
- }
-
- str_array = g_new (gchar*, n);
-
- i = n - 1;
-
- str_array[i--] = NULL;
- for (slist = string_list; slist; slist = slist->next)
- str_array[i--] = slist->data;
-
- g_slist_free (string_list);
-
- return str_array;
-}
-
-static GtkTextSearchFlags
-_source_flags_to_text_flags(GtkSourceSearchFlags flags)
-{
- GtkTextSearchFlags text_flags = 0;
-
- if (flags & GTK_SOURCE_SEARCH_VISIBLE_ONLY)
- text_flags |= GTK_TEXT_SEARCH_VISIBLE_ONLY;
- if (flags & GTK_SOURCE_SEARCH_TEXT_ONLY)
- text_flags |= GTK_TEXT_SEARCH_TEXT_ONLY;
-
- return text_flags;
-}
-
-/**
- * gtk_source_iter_forward_search:
- * @iter: start of search.
- * @str: a search string.
- * @flags: flags affecting how the search is done.
- * @match_start: return location for start of match, or %%NULL.
- * @match_end: return location for end of match, or %%NULL.
- * @limit: bound for the search, or %%NULL for the end of the buffer.
- *
- * Searches forward for @str. Any match is returned by setting
- * @match_start to the first character of the match and @match_end to the
- * first character after the match. The search will not continue past
- * @limit. Note that a search is a linear or O(n) operation, so you
- * may wish to use @limit to avoid locking up your UI on large
- * buffers.
- *
- * If the #GTK_SOURCE_SEARCH_VISIBLE_ONLY flag is present, the match may
- * have invisible text interspersed in @str. i.e. @str will be a
- * possibly-noncontiguous subsequence of the matched range. similarly,
- * if you specify #GTK_SOURCE_SEARCH_TEXT_ONLY, the match may have
- * pixbufs or child widgets mixed inside the matched range. If these
- * flags are not given, the match must be exact; the special 0xFFFC
- * character in @str will match embedded pixbufs or child widgets.
- * If you specify the #GTK_SOURCE_SEARCH_CASE_INSENSITIVE flag, the text will
- * be matched regardless of what case it is in.
- *
- * Same as gtk_text_iter_forward_search(), but supports case insensitive
- * searching.
- *
- * Return value: whether a match was found.
- **/
-gboolean
-gtk_source_iter_forward_search (const GtkTextIter *iter,
- const gchar *str,
- GtkSourceSearchFlags flags,
- GtkTextIter *match_start,
- GtkTextIter *match_end,
- const GtkTextIter *limit)
-{
- gchar **lines = NULL;
- GtkTextIter match;
- gboolean retval = FALSE;
- GtkTextIter search;
- gboolean visible_only;
- gboolean slice;
-
- g_return_val_if_fail (iter != NULL, FALSE);
- g_return_val_if_fail (str != NULL, FALSE);
-
- if ((flags & GTK_SOURCE_SEARCH_CASE_INSENSITIVE) == 0)
- return gtk_text_iter_forward_search (iter, str, _source_flags_to_text_flags(flags),
- match_start, match_end,
- limit);
-
- if (limit && gtk_text_iter_compare (iter, limit) >= 0)
- return FALSE;
-
- if (*str == '\0')
- {
- /* If we can move one char, return the empty string there */
- match = *iter;
-
- if (gtk_text_iter_forward_char (&match))
- {
- if (limit && gtk_text_iter_equal (&match, limit))
- return FALSE;
-
- if (match_start)
- *match_start = match;
- if (match_end)
- *match_end = match;
- return TRUE;
- }
- else
- {
- return FALSE;
- }
- }
-
- visible_only = (flags & GTK_SOURCE_SEARCH_VISIBLE_ONLY) != 0;
- slice = (flags & GTK_SOURCE_SEARCH_TEXT_ONLY) == 0;
-
- /* locate all lines */
- lines = strbreakup (str, "\n", -1);
-
- search = *iter;
-
- do
- {
- /* This loop has an inefficient worst-case, where
- * gtk_text_iter_get_text () is called repeatedly on
- * a single line.
- */
- GtkTextIter end;
-
- if (limit && gtk_text_iter_compare (&search, limit) >= 0)
- break;
-
- if (lines_match (&search, (const gchar**)lines,
- visible_only, slice, &match, &end))
- {
- if (limit == NULL ||
- (limit && gtk_text_iter_compare (&end, limit) <= 0))
- {
- retval = TRUE;
-
- if (match_start)
- *match_start = match;
- if (match_end)
- *match_end = end;
- }
- break;
- }
- } while (gtk_text_iter_forward_line (&search));
-
- g_strfreev ((gchar**)lines);
-
- return retval;
-}
-
-/**
- * gtk_source_iter_backward_search:
- * @iter: a #GtkTextIter where the search begins.
- * @str: search string.
- * @flags: bitmask of flags affecting the search.
- * @match_start: return location for start of match, or %%NULL.
- * @match_end: return location for end of match, or %%NULL.
- * @limit: location of last possible @match_start, or %%NULL for start of buffer.
- *
- * Same as gtk_text_iter_backward_search(), but supports case insensitive
- * searching.
- *
- * Return value: whether a match was found.
- **/
-gboolean
-gtk_source_iter_backward_search (const GtkTextIter *iter,
- const gchar *str,
- GtkSourceSearchFlags flags,
- GtkTextIter *match_start,
- GtkTextIter *match_end,
- const GtkTextIter *limit)
-{
- gchar **lines = NULL;
- GtkTextIter match;
- gboolean retval = FALSE;
- GtkTextIter search;
- gboolean visible_only;
- gboolean slice;
-
- g_return_val_if_fail (iter != NULL, FALSE);
- g_return_val_if_fail (str != NULL, FALSE);
-
- if ((flags & GTK_SOURCE_SEARCH_CASE_INSENSITIVE) == 0)
- return gtk_text_iter_backward_search (iter, str, _source_flags_to_text_flags(flags),
- match_start, match_end,
- limit);
-
- if (limit && gtk_text_iter_compare (iter, limit) <= 0)
- return FALSE;
-
- if (*str == '\0')
- {
- /* If we can move one char, return the empty string there */
- match = *iter;
-
- if (gtk_text_iter_backward_char (&match))
- {
- if (limit && gtk_text_iter_equal (&match, limit))
- return FALSE;
-
- if (match_start)
- *match_start = match;
- if (match_end)
- *match_end = match;
- return TRUE;
- }
- else
- {
- return FALSE;
- }
- }
-
- visible_only = (flags & GTK_SOURCE_SEARCH_VISIBLE_ONLY) != 0;
- slice = (flags & GTK_SOURCE_SEARCH_TEXT_ONLY) == 0;
-
- /* locate all lines */
- lines = strbreakup (str, "\n", -1);
-
- search = *iter;
-
- while (TRUE)
- {
- /* This loop has an inefficient worst-case, where
- * gtk_text_iter_get_text () is called repeatedly on
- * a single line.
- */
- GtkTextIter end;
-
- if (limit && gtk_text_iter_compare (&search, limit) <= 0)
- break;
-
- if (backward_lines_match (&search, (const gchar**)lines,
- visible_only, slice, &match, &end))
- {
- if (limit == NULL || (limit &&
- gtk_text_iter_compare (&end, limit) > 0))
- {
- retval = TRUE;
-
- if (match_start)
- *match_start = match;
- if (match_end)
- *match_end = end;
- }
- break;
- }
-
- if (gtk_text_iter_get_line_offset (&search) == 0)
- {
- if (!gtk_text_iter_backward_line (&search))
- break;
- }
- else
- {
- gtk_text_iter_set_line_offset (&search, 0);
- }
- }
-
- g_strfreev ((gchar**)lines);
-
- return retval;
-}
-
-/*
- * gtk_source_iter_find_matching_bracket is implemented in gtksourcebuffer.c
- */
diff --git a/pidgin/gtksourceiter.h b/pidgin/gtksourceiter.h
deleted file mode 100644
index 2be2737c9b..0000000000
--- a/pidgin/gtksourceiter.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
- * gtksourceiter.h
- *
- * 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.
- *
- * The following copyright notice applies to this file:
- *
- * Copyright (C) 2000 - 2005 Paolo Maggi
- * Copyright (C) 2002, 2003 Jeroen Zwartepoorte
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Library 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 Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library 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 _PIDGINSOURCEITER_H_
-#define _PIDGINSOURCEITER_H_
-
-#include <gtk/gtk.h>
-
-G_BEGIN_DECLS
-
-typedef enum
-{
- GTK_SOURCE_SEARCH_VISIBLE_ONLY = 1 << 0,
- GTK_SOURCE_SEARCH_TEXT_ONLY = 1 << 1,
- GTK_SOURCE_SEARCH_CASE_INSENSITIVE = 1 << 2
- /* Possible future plans: SEARCH_REGEXP */
-} GtkSourceSearchFlags;
-
-gboolean gtk_source_iter_forward_search (const GtkTextIter *iter,
- const gchar *str,
- GtkSourceSearchFlags flags,
- GtkTextIter *match_start,
- GtkTextIter *match_end,
- const GtkTextIter *limit);
-
-gboolean gtk_source_iter_backward_search (const GtkTextIter *iter,
- const gchar *str,
- GtkSourceSearchFlags flags,
- GtkTextIter *match_start,
- GtkTextIter *match_end,
- const GtkTextIter *limit);
-
-gboolean gtk_source_iter_find_matching_bracket (GtkTextIter *iter);
-
-G_END_DECLS
-
-#endif /* _PIDGINSOURCEITER_H_ */
diff --git a/pidgin/gtksourceundomanager.c b/pidgin/gtksourceundomanager.c
deleted file mode 100644
index 2c2a63e7a6..0000000000
--- a/pidgin/gtksourceundomanager.c
+++ /dev/null
@@ -1,1199 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * gtksourceundomanager.c
- * This file is part of GtkSourceView
- *
- * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence
- * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi
- * Copyright (C) 2002-2005 Paolo Maggi
- *
- * 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 <glib.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "gtksourceundomanager.h"
-#include "gtksourceview-marshal.h"
-
-
-#define DEFAULT_MAX_UNDO_LEVELS 25
-
-
-typedef struct _GtkSourceUndoAction GtkSourceUndoAction;
-typedef struct _GtkSourceUndoInsertAction GtkSourceUndoInsertAction;
-typedef struct _GtkSourceUndoDeleteAction GtkSourceUndoDeleteAction;
-typedef struct _GtkSourceUndoInsertAnchorAction GtkSourceUndoInsertAnchorAction;
-
-typedef enum {
- GTK_SOURCE_UNDO_ACTION_INSERT,
- GTK_SOURCE_UNDO_ACTION_DELETE,
- GTK_SOURCE_UNDO_ACTION_INSERT_ANCHOR,
-} GtkSourceUndoActionType;
-
-/*
- * We use offsets instead of GtkTextIters because the last ones
- * require to much memory in this context without giving us any advantage.
- */
-
-struct _GtkSourceUndoInsertAction
-{
- gint pos;
- gchar *text;
- gint length;
- gint chars;
-};
-
-struct _GtkSourceUndoDeleteAction
-{
- gint start;
- gint end;
- gchar *text;
- gboolean forward;
-};
-
-struct _GtkSourceUndoInsertAnchorAction
-{
- gint pos;
- GtkTextChildAnchor *anchor;
-};
-
-struct _GtkSourceUndoAction
-{
- GtkSourceUndoActionType action_type;
-
- union {
- GtkSourceUndoInsertAction insert;
- GtkSourceUndoDeleteAction delete;
- GtkSourceUndoInsertAnchorAction insert_anchor;
- } action;
-
- gint order_in_group;
-
- /* It is TRUE whether the action can be merged with the following action. */
- guint mergeable : 1;
-
- /* It is TRUE whether the action is marked as "modified".
- * An action is marked as "modified" if it changed the
- * state of the buffer from "not modified" to "modified". Only the first
- * action of a group can be marked as modified.
- * There can be a single action marked as "modified" in the actions list.
- */
- guint modified : 1;
-};
-
-struct _GtkSourceUndoManagerPrivate
-{
- GtkTextBuffer *document;
-
- GList* actions;
- gint next_redo;
-
- gint actions_in_current_group;
-
- gint running_not_undoable_actions;
-
- gint num_of_groups;
-
- gint max_undo_levels;
-
- guint can_undo : 1;
- guint can_redo : 1;
-
- /* It is TRUE whether, while undoing an action of the current group (with order_in_group > 1),
- * the state of the buffer changed from "not modified" to "modified".
- */
- guint modified_undoing_group : 1;
-
- /* Pointer to the action (in the action list) marked as "modified".
- * It is NULL when no action is marked as "modified". */
- GtkSourceUndoAction *modified_action;
-};
-
-enum {
- CAN_UNDO,
- CAN_REDO,
- LAST_SIGNAL
-};
-
-static void gtk_source_undo_manager_class_init (GtkSourceUndoManagerClass *klass);
-static void gtk_source_undo_manager_init (GtkSourceUndoManager *um);
-static void gtk_source_undo_manager_finalize (GObject *object);
-
-static void gtk_source_undo_manager_insert_text_handler (GtkTextBuffer *buffer,
- GtkTextIter *pos,
- const gchar *text,
- gint length,
- GtkSourceUndoManager *um);
-static void gtk_source_undo_manager_insert_anchor_handler (GtkTextBuffer *buffer,
- GtkTextIter *pos,
- GtkTextChildAnchor *anchor,
- GtkSourceUndoManager *um);
-static void gtk_source_undo_manager_delete_range_handler (GtkTextBuffer *buffer,
- GtkTextIter *start,
- GtkTextIter *end,
- GtkSourceUndoManager *um);
-static void gtk_source_undo_manager_begin_user_action_handler (GtkTextBuffer *buffer,
- GtkSourceUndoManager *um);
-static void gtk_source_undo_manager_modified_changed_handler (GtkTextBuffer *buffer,
- GtkSourceUndoManager *um);
-
-static void gtk_source_undo_manager_free_action_list (GtkSourceUndoManager *um);
-
-static void gtk_source_undo_manager_add_action (GtkSourceUndoManager *um,
- const GtkSourceUndoAction *undo_action);
-static void gtk_source_undo_manager_free_first_n_actions (GtkSourceUndoManager *um,
- gint n);
-static void gtk_source_undo_manager_check_list_size (GtkSourceUndoManager *um);
-
-static gboolean gtk_source_undo_manager_merge_action (GtkSourceUndoManager *um,
- const GtkSourceUndoAction *undo_action);
-
-static GObjectClass *parent_class = NULL;
-static guint undo_manager_signals [LAST_SIGNAL] = { 0 };
-
-GType
-gtk_source_undo_manager_get_type (void)
-{
- static GType undo_manager_type = 0;
-
- if (undo_manager_type == 0)
- {
- static const GTypeInfo our_info =
- {
- sizeof (GtkSourceUndoManagerClass),
- NULL, /* base_init */
- NULL, /* base_finalize */
- (GClassInitFunc) gtk_source_undo_manager_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (GtkSourceUndoManager),
- 0, /* n_preallocs */
- (GInstanceInitFunc) gtk_source_undo_manager_init,
- NULL /* value_table */
- };
-
- undo_manager_type = g_type_register_static (G_TYPE_OBJECT,
- "GtkSourceUndoManager",
- &our_info,
- 0);
- }
-
- return undo_manager_type;
-}
-
-static void
-gtk_source_undo_manager_class_init (GtkSourceUndoManagerClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- parent_class = g_type_class_peek_parent (klass);
-
- object_class->finalize = gtk_source_undo_manager_finalize;
-
- klass->can_undo = NULL;
- klass->can_redo = NULL;
-
- undo_manager_signals[CAN_UNDO] =
- g_signal_new ("can_undo",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GtkSourceUndoManagerClass, can_undo),
- NULL, NULL,
- gtksourceview_marshal_VOID__BOOLEAN,
- G_TYPE_NONE,
- 1,
- G_TYPE_BOOLEAN);
-
- undo_manager_signals[CAN_REDO] =
- g_signal_new ("can_redo",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GtkSourceUndoManagerClass, can_redo),
- NULL, NULL,
- gtksourceview_marshal_VOID__BOOLEAN,
- G_TYPE_NONE,
- 1,
- G_TYPE_BOOLEAN);
-}
-
-static void
-gtk_source_undo_manager_init (GtkSourceUndoManager *um)
-{
- um->priv = g_new0 (GtkSourceUndoManagerPrivate, 1);
-
- um->priv->actions = NULL;
- um->priv->next_redo = 0;
-
- um->priv->can_undo = FALSE;
- um->priv->can_redo = FALSE;
-
- um->priv->running_not_undoable_actions = 0;
-
- um->priv->num_of_groups = 0;
-
- um->priv->max_undo_levels = DEFAULT_MAX_UNDO_LEVELS;
-
- um->priv->modified_action = NULL;
-
- um->priv->modified_undoing_group = FALSE;
-}
-
-static void
-gtk_source_undo_manager_finalize (GObject *object)
-{
- GtkSourceUndoManager *um;
-
- g_return_if_fail (object != NULL);
- g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (object));
-
- um = GTK_SOURCE_UNDO_MANAGER (object);
-
- g_return_if_fail (um->priv != NULL);
-
- if (um->priv->actions != NULL)
- {
- gtk_source_undo_manager_free_action_list (um);
- }
-
- g_signal_handlers_disconnect_by_func (G_OBJECT (um->priv->document),
- G_CALLBACK (gtk_source_undo_manager_delete_range_handler),
- um);
-
- g_signal_handlers_disconnect_by_func (G_OBJECT (um->priv->document),
- G_CALLBACK (gtk_source_undo_manager_insert_text_handler),
- um);
-
- g_signal_handlers_disconnect_by_func (G_OBJECT (um->priv->document),
- G_CALLBACK (gtk_source_undo_manager_insert_anchor_handler),
- um);
-
- g_signal_handlers_disconnect_by_func (G_OBJECT (um->priv->document),
- G_CALLBACK (gtk_source_undo_manager_begin_user_action_handler),
- um);
-
- g_free (um->priv);
-
- G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-GtkSourceUndoManager*
-gtk_source_undo_manager_new (GtkTextBuffer* buffer)
-{
- GtkSourceUndoManager *um;
-
- um = GTK_SOURCE_UNDO_MANAGER (g_object_new (GTK_SOURCE_TYPE_UNDO_MANAGER, NULL));
-
- g_return_val_if_fail (um->priv != NULL, NULL);
- um->priv->document = buffer;
-
- g_signal_connect (G_OBJECT (buffer), "insert_text",
- G_CALLBACK (gtk_source_undo_manager_insert_text_handler),
- um);
-
- g_signal_connect (G_OBJECT (buffer), "insert_child_anchor",
- G_CALLBACK (gtk_source_undo_manager_insert_anchor_handler),
- um);
-
- g_signal_connect (G_OBJECT (buffer), "delete_range",
- G_CALLBACK (gtk_source_undo_manager_delete_range_handler),
- um);
-
- g_signal_connect (G_OBJECT (buffer), "begin_user_action",
- G_CALLBACK (gtk_source_undo_manager_begin_user_action_handler),
- um);
-
- g_signal_connect (G_OBJECT (buffer), "modified_changed",
- G_CALLBACK (gtk_source_undo_manager_modified_changed_handler),
- um);
- return um;
-}
-
-void
-gtk_source_undo_manager_begin_not_undoable_action (GtkSourceUndoManager *um)
-{
- g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
- g_return_if_fail (um->priv != NULL);
-
- ++um->priv->running_not_undoable_actions;
-}
-
-static void
-gtk_source_undo_manager_end_not_undoable_action_internal (GtkSourceUndoManager *um)
-{
- g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
- g_return_if_fail (um->priv != NULL);
-
- g_return_if_fail (um->priv->running_not_undoable_actions > 0);
-
- --um->priv->running_not_undoable_actions;
-}
-
-void
-gtk_source_undo_manager_end_not_undoable_action (GtkSourceUndoManager *um)
-{
- g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
- g_return_if_fail (um->priv != NULL);
-
- gtk_source_undo_manager_end_not_undoable_action_internal (um);
-
- if (um->priv->running_not_undoable_actions == 0)
- {
- gtk_source_undo_manager_free_action_list (um);
-
- um->priv->next_redo = -1;
-
- if (um->priv->can_undo)
- {
- um->priv->can_undo = FALSE;
- g_signal_emit (G_OBJECT (um),
- undo_manager_signals [CAN_UNDO],
- 0,
- FALSE);
- }
-
- if (um->priv->can_redo)
- {
- um->priv->can_redo = FALSE;
- g_signal_emit (G_OBJECT (um),
- undo_manager_signals [CAN_REDO],
- 0,
- FALSE);
- }
- }
-}
-
-gboolean
-gtk_source_undo_manager_can_undo (const GtkSourceUndoManager *um)
-{
- g_return_val_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um), FALSE);
- g_return_val_if_fail (um->priv != NULL, FALSE);
-
- return um->priv->can_undo;
-}
-
-gboolean
-gtk_source_undo_manager_can_redo (const GtkSourceUndoManager *um)
-{
- g_return_val_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um), FALSE);
- g_return_val_if_fail (um->priv != NULL, FALSE);
-
- return um->priv->can_redo;
-}
-
-static void
-set_cursor (GtkTextBuffer *buffer, gint cursor)
-{
- GtkTextIter iter;
-
- /* Place the cursor at the requested position */
- gtk_text_buffer_get_iter_at_offset (buffer, &iter, cursor);
- gtk_text_buffer_place_cursor (buffer, &iter);
-}
-
-static void
-insert_text (GtkTextBuffer *buffer, gint pos, const gchar *text, gint len)
-{
- GtkTextIter iter;
-
- gtk_text_buffer_get_iter_at_offset (buffer, &iter, pos);
- gtk_text_buffer_insert (buffer, &iter, text, len);
-}
-
-static void
-insert_anchor (GtkTextBuffer *buffer, gint pos, GtkTextChildAnchor *anchor)
-{
- GtkTextIter iter;
-
- gtk_text_buffer_get_iter_at_offset (buffer, &iter, pos);
- gtk_text_buffer_insert_child_anchor (buffer, &iter, anchor);
-}
-
-static void
-delete_text (GtkTextBuffer *buffer, gint start, gint end)
-{
- GtkTextIter start_iter;
- GtkTextIter end_iter;
-
- gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start);
-
- if (end < 0)
- gtk_text_buffer_get_end_iter (buffer, &end_iter);
- else
- gtk_text_buffer_get_iter_at_offset (buffer, &end_iter, end);
-
- gtk_text_buffer_delete (buffer, &start_iter, &end_iter);
-}
-
-static gchar*
-get_chars (GtkTextBuffer *buffer, gint start, gint end)
-{
- GtkTextIter start_iter;
- GtkTextIter end_iter;
-
- gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start);
-
- if (end < 0)
- gtk_text_buffer_get_end_iter (buffer, &end_iter);
- else
- gtk_text_buffer_get_iter_at_offset (buffer, &end_iter, end);
-
- return gtk_text_buffer_get_slice (buffer, &start_iter, &end_iter, TRUE);
-}
-
-void
-gtk_source_undo_manager_undo (GtkSourceUndoManager *um)
-{
- GtkSourceUndoAction *undo_action;
- gboolean modified = FALSE;
-
- g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
- g_return_if_fail (um->priv != NULL);
- g_return_if_fail (um->priv->can_undo);
-
- um->priv->modified_undoing_group = FALSE;
-
- gtk_source_undo_manager_begin_not_undoable_action (um);
-
- do
- {
- undo_action = g_list_nth_data (um->priv->actions, um->priv->next_redo + 1);
- g_return_if_fail (undo_action != NULL);
-
- /* undo_action->modified can be TRUE only if undo_action->order_in_group <= 1 */
- g_return_if_fail ((undo_action->order_in_group <= 1) ||
- ((undo_action->order_in_group > 1) && !undo_action->modified));
-
- if (undo_action->order_in_group <= 1)
- {
- /* Set modified to TRUE only if the buffer did not change its state from
- * "not modified" to "modified" undoing an action (with order_in_group > 1)
- * in current group. */
- modified = (undo_action->modified && !um->priv->modified_undoing_group);
- }
-
- switch (undo_action->action_type)
- {
- case GTK_SOURCE_UNDO_ACTION_DELETE:
- insert_text (
- um->priv->document,
- undo_action->action.delete.start,
- undo_action->action.delete.text,
- strlen (undo_action->action.delete.text));
-
- if (undo_action->action.delete.forward)
- set_cursor (
- um->priv->document,
- undo_action->action.delete.start);
- else
- set_cursor (
- um->priv->document,
- undo_action->action.delete.end);
-
- break;
-
- case GTK_SOURCE_UNDO_ACTION_INSERT:
- delete_text (
- um->priv->document,
- undo_action->action.insert.pos,
- undo_action->action.insert.pos +
- undo_action->action.insert.chars);
-
- set_cursor (
- um->priv->document,
- undo_action->action.insert.pos);
- break;
-
- case GTK_SOURCE_UNDO_ACTION_INSERT_ANCHOR:
- delete_text (
- um->priv->document,
- undo_action->action.insert_anchor.pos,
- undo_action->action.insert_anchor.pos + 1);
- undo_action->action.insert_anchor.anchor->segment = NULL; /* XXX: This may be a bug in GTK+ */
- break;
- default:
- /* Unknown action type. */
- g_return_if_reached ();
- }
-
- ++um->priv->next_redo;
-
- } while (undo_action->order_in_group > 1);
-
- if (modified)
- {
- --um->priv->next_redo;
- gtk_text_buffer_set_modified (um->priv->document, FALSE);
- ++um->priv->next_redo;
- }
-
- gtk_source_undo_manager_end_not_undoable_action_internal (um);
-
- um->priv->modified_undoing_group = FALSE;
-
- if (!um->priv->can_redo)
- {
- um->priv->can_redo = TRUE;
- g_signal_emit (G_OBJECT (um),
- undo_manager_signals [CAN_REDO],
- 0,
- TRUE);
- }
-
- if (um->priv->next_redo >= (gint)(g_list_length (um->priv->actions) - 1))
- {
- um->priv->can_undo = FALSE;
- g_signal_emit (G_OBJECT (um),
- undo_manager_signals [CAN_UNDO],
- 0,
- FALSE);
- }
-}
-
-void
-gtk_source_undo_manager_redo (GtkSourceUndoManager *um)
-{
- GtkSourceUndoAction *undo_action;
- gboolean modified = FALSE;
-
- g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
- g_return_if_fail (um->priv != NULL);
- g_return_if_fail (um->priv->can_redo);
-
- undo_action = g_list_nth_data (um->priv->actions, um->priv->next_redo);
- g_return_if_fail (undo_action != NULL);
-
- gtk_source_undo_manager_begin_not_undoable_action (um);
-
- do
- {
- if (undo_action->modified)
- {
- g_return_if_fail (undo_action->order_in_group <= 1);
- modified = TRUE;
- }
-
- --um->priv->next_redo;
-
- switch (undo_action->action_type)
- {
- case GTK_SOURCE_UNDO_ACTION_DELETE:
- delete_text (
- um->priv->document,
- undo_action->action.delete.start,
- undo_action->action.delete.end);
-
- set_cursor (
- um->priv->document,
- undo_action->action.delete.start);
-
- break;
-
- case GTK_SOURCE_UNDO_ACTION_INSERT:
- set_cursor (
- um->priv->document,
- undo_action->action.insert.pos);
-
- insert_text (
- um->priv->document,
- undo_action->action.insert.pos,
- undo_action->action.insert.text,
- undo_action->action.insert.length);
-
- break;
-
- case GTK_SOURCE_UNDO_ACTION_INSERT_ANCHOR:
- set_cursor (
- um->priv->document,
- undo_action->action.insert_anchor.pos);
-
- insert_anchor (
- um->priv->document,
- undo_action->action.insert_anchor.pos,
- undo_action->action.insert_anchor.anchor);
- break;
-
- default:
- /* Unknown action type */
- ++um->priv->next_redo;
- g_return_if_reached ();
- }
-
- if (um->priv->next_redo < 0)
- undo_action = NULL;
- else
- undo_action = g_list_nth_data (um->priv->actions, um->priv->next_redo);
-
- } while ((undo_action != NULL) && (undo_action->order_in_group > 1));
-
- if (modified)
- {
- ++um->priv->next_redo;
- gtk_text_buffer_set_modified (um->priv->document, FALSE);
- --um->priv->next_redo;
- }
-
- gtk_source_undo_manager_end_not_undoable_action_internal (um);
-
- if (um->priv->next_redo < 0)
- {
- um->priv->can_redo = FALSE;
- g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_REDO], 0, FALSE);
- }
-
- if (!um->priv->can_undo)
- {
- um->priv->can_undo = TRUE;
- g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_UNDO], 0, TRUE);
- }
-}
-
-static void
-gtk_source_undo_action_free (GtkSourceUndoAction *action)
-{
- if (action == NULL)
- return;
-
- if (action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
- g_free (action->action.insert.text);
- else if (action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
- g_free (action->action.delete.text);
- else if (action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT_ANCHOR)
- g_object_unref(action->action.insert_anchor.anchor);
- else {
- g_free (action);
- g_return_if_reached ();
- }
-
- g_free (action);
-}
-
-static void
-gtk_source_undo_manager_free_action_list (GtkSourceUndoManager *um)
-{
- GList *l;
-
- l = um->priv->actions;
-
- while (l != NULL)
- {
- GtkSourceUndoAction *action = l->data;
-
- if (action->order_in_group == 1)
- --um->priv->num_of_groups;
-
- if (action->modified)
- um->priv->modified_action = NULL;
-
- gtk_source_undo_action_free (action);
-
- l = g_list_next (l);
- }
-
- g_list_free (um->priv->actions);
- um->priv->actions = NULL;
-}
-
-static void
-gtk_source_undo_manager_insert_text_handler (GtkTextBuffer *buffer,
- GtkTextIter *pos,
- const gchar *text,
- gint length,
- GtkSourceUndoManager *um)
-{
- GtkSourceUndoAction undo_action;
-
- if (um->priv->running_not_undoable_actions > 0)
- return;
-
- undo_action.action_type = GTK_SOURCE_UNDO_ACTION_INSERT;
-
- undo_action.action.insert.pos = gtk_text_iter_get_offset (pos);
- undo_action.action.insert.text = (gchar*) text;
- undo_action.action.insert.length = length;
- undo_action.action.insert.chars = g_utf8_strlen (text, length);
-
- if ((undo_action.action.insert.chars > 1) || (g_utf8_get_char (text) == '\n'))
-
- undo_action.mergeable = FALSE;
- else
- undo_action.mergeable = TRUE;
-
- undo_action.modified = FALSE;
-
- gtk_source_undo_manager_add_action (um, &undo_action);
-}
-
-static void gtk_source_undo_manager_insert_anchor_handler (GtkTextBuffer *buffer,
- GtkTextIter *pos,
- GtkTextChildAnchor *anchor,
- GtkSourceUndoManager *um)
-{
- GtkSourceUndoAction undo_action;
-
- if (um->priv->running_not_undoable_actions > 0)
- return;
-
- undo_action.action_type = GTK_SOURCE_UNDO_ACTION_INSERT_ANCHOR;
-
- undo_action.action.insert_anchor.pos = gtk_text_iter_get_offset (pos);
- undo_action.action.insert_anchor.anchor = g_object_ref (anchor);
-
- undo_action.mergeable = FALSE;
- undo_action.modified = FALSE;
-
- gtk_source_undo_manager_add_action (um, &undo_action);
-}
-
-static void
-gtk_source_undo_manager_delete_range_handler (GtkTextBuffer *buffer,
- GtkTextIter *start,
- GtkTextIter *end,
- GtkSourceUndoManager *um)
-{
- GtkSourceUndoAction undo_action;
- GtkTextIter insert_iter;
-
- if (um->priv->running_not_undoable_actions > 0)
- return;
-
- undo_action.action_type = GTK_SOURCE_UNDO_ACTION_DELETE;
-
- gtk_text_iter_order (start, end);
-
- undo_action.action.delete.start = gtk_text_iter_get_offset (start);
- undo_action.action.delete.end = gtk_text_iter_get_offset (end);
-
- undo_action.action.delete.text = get_chars (
- buffer,
- undo_action.action.delete.start,
- undo_action.action.delete.end);
-
- /* figure out if the user used the Delete or the Backspace key */
- gtk_text_buffer_get_iter_at_mark (buffer, &insert_iter,
- gtk_text_buffer_get_insert (buffer));
- if (gtk_text_iter_get_offset (&insert_iter) <= undo_action.action.delete.start)
- undo_action.action.delete.forward = TRUE;
- else
- undo_action.action.delete.forward = FALSE;
-
- if (((undo_action.action.delete.end - undo_action.action.delete.start) > 1) ||
- (g_utf8_get_char (undo_action.action.delete.text ) == '\n'))
- undo_action.mergeable = FALSE;
- else
- undo_action.mergeable = TRUE;
-
- undo_action.modified = FALSE;
-
- gtk_source_undo_manager_add_action (um, &undo_action);
-
- g_free (undo_action.action.delete.text);
-
-}
-
-static void
-gtk_source_undo_manager_begin_user_action_handler (GtkTextBuffer *buffer, GtkSourceUndoManager *um)
-{
- g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
- g_return_if_fail (um->priv != NULL);
-
- if (um->priv->running_not_undoable_actions > 0)
- return;
-
- um->priv->actions_in_current_group = 0;
-}
-
-static void
-gtk_source_undo_manager_add_action (GtkSourceUndoManager *um,
- const GtkSourceUndoAction *undo_action)
-{
- GtkSourceUndoAction* action;
-
- if (um->priv->next_redo >= 0)
- {
- gtk_source_undo_manager_free_first_n_actions (um, um->priv->next_redo + 1);
- }
-
- um->priv->next_redo = -1;
-
- if (!gtk_source_undo_manager_merge_action (um, undo_action))
- {
- action = g_new (GtkSourceUndoAction, 1);
- *action = *undo_action;
-
- if (action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
- action->action.insert.text = g_strndup (undo_action->action.insert.text, undo_action->action.insert.length);
- else if (action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
- action->action.delete.text = g_strdup (undo_action->action.delete.text);
- else if (action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT_ANCHOR)
- {
- /* Nothing needs to be done */
- }
- else
- {
- g_free (action);
- g_return_if_reached ();
- }
-
- ++um->priv->actions_in_current_group;
- action->order_in_group = um->priv->actions_in_current_group;
-
- if (action->order_in_group == 1)
- ++um->priv->num_of_groups;
-
- um->priv->actions = g_list_prepend (um->priv->actions, action);
- }
-
- gtk_source_undo_manager_check_list_size (um);
-
- if (!um->priv->can_undo)
- {
- um->priv->can_undo = TRUE;
- g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_UNDO], 0, TRUE);
- }
-
- if (um->priv->can_redo)
- {
- um->priv->can_redo = FALSE;
- g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_REDO], 0, FALSE);
- }
-}
-
-static void
-gtk_source_undo_manager_free_first_n_actions (GtkSourceUndoManager *um,
- gint n)
-{
- gint i;
-
- if (um->priv->actions == NULL)
- return;
-
- for (i = 0; i < n; i++)
- {
- GtkSourceUndoAction *action = g_list_first (um->priv->actions)->data;
-
- if (action->order_in_group == 1)
- --um->priv->num_of_groups;
-
- if (action->modified)
- um->priv->modified_action = NULL;
-
- gtk_source_undo_action_free (action);
-
- um->priv->actions = g_list_delete_link (um->priv->actions,
- um->priv->actions);
-
- if (um->priv->actions == NULL)
- return;
- }
-}
-
-static void
-gtk_source_undo_manager_check_list_size (GtkSourceUndoManager *um)
-{
- gint undo_levels;
-
- g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
- g_return_if_fail (um->priv != NULL);
-
- undo_levels = gtk_source_undo_manager_get_max_undo_levels (um);
-
- if (undo_levels < 1)
- return;
-
- if (um->priv->num_of_groups > undo_levels)
- {
- GtkSourceUndoAction *undo_action;
- GList *last;
-
- last = g_list_last (um->priv->actions);
- undo_action = (GtkSourceUndoAction*) last->data;
-
- do
- {
- GList *tmp;
-
- if (undo_action->order_in_group == 1)
- --um->priv->num_of_groups;
-
- if (undo_action->modified)
- um->priv->modified_action = NULL;
-
- gtk_source_undo_action_free (undo_action);
-
- tmp = g_list_previous (last);
- um->priv->actions = g_list_delete_link (um->priv->actions, last);
- last = tmp;
- g_return_if_fail (last != NULL);
-
- undo_action = (GtkSourceUndoAction*) last->data;
-
- } while ((undo_action->order_in_group > 1) ||
- (um->priv->num_of_groups > undo_levels));
- }
-}
-
-/**
- * gtk_source_undo_manager_merge_action:
- * @um: a #GtkSourceUndoManager.
- * @undo_action: a #GtkSourceUndoAction.
- *
- * This function tries to merge the undo action at the top of
- * the stack with a new undo action. So when we undo for example
- * typing, we can undo the whole word and not each letter by itself.
- *
- * Return Value: %TRUE is merge was successful, %FALSE otherwise.
- **/
-static gboolean
-gtk_source_undo_manager_merge_action (GtkSourceUndoManager *um,
- const GtkSourceUndoAction *undo_action)
-{
- GtkSourceUndoAction *last_action;
-
- g_return_val_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um), FALSE);
- g_return_val_if_fail (um->priv != NULL, FALSE);
-
- if (um->priv->actions == NULL)
- return FALSE;
-
- last_action = (GtkSourceUndoAction*) g_list_nth_data (um->priv->actions, 0);
-
- if (!last_action->mergeable)
- return FALSE;
-
- if ((!undo_action->mergeable) ||
- (undo_action->action_type != last_action->action_type))
- {
- last_action->mergeable = FALSE;
- return FALSE;
- }
-
- if (undo_action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
- {
- const GtkSourceUndoDeleteAction *last_del, *undo_del;
-
- last_del = &last_action->action.delete;
- undo_del = &undo_action->action.delete;
-
- if (last_del->forward != undo_del->forward ||
- (last_del->start != undo_del->start && last_del->start != undo_del->end))
- {
- last_action->mergeable = FALSE;
- return FALSE;
- }
-
- if (last_action->action.delete.start == undo_action->action.delete.start)
- {
- gchar *str;
-
-#define L (last_action->action.delete.end - last_action->action.delete.start - 1)
-#define g_utf8_get_char_at(p,i) g_utf8_get_char(g_utf8_offset_to_pointer((p),(i)))
-
- /* Deleted with the delete key */
- if ((g_utf8_get_char (undo_action->action.delete.text) != ' ') &&
- (g_utf8_get_char (undo_action->action.delete.text) != '\t') &&
- ((g_utf8_get_char_at (last_action->action.delete.text, L) == ' ') ||
- (g_utf8_get_char_at (last_action->action.delete.text, L) == '\t')))
- {
- last_action->mergeable = FALSE;
- return FALSE;
- }
-
- str = g_strdup_printf ("%s%s", last_action->action.delete.text,
- undo_action->action.delete.text);
-
- g_free (last_action->action.delete.text);
- last_action->action.delete.end += (undo_action->action.delete.end -
- undo_action->action.delete.start);
- last_action->action.delete.text = str;
- }
- else
- {
- gchar *str;
-
- /* Deleted with the backspace key */
- if ((g_utf8_get_char (undo_action->action.delete.text) != ' ') &&
- (g_utf8_get_char (undo_action->action.delete.text) != '\t') &&
- ((g_utf8_get_char (last_action->action.delete.text) == ' ') ||
- (g_utf8_get_char (last_action->action.delete.text) == '\t')))
- {
- last_action->mergeable = FALSE;
- return FALSE;
- }
-
- str = g_strdup_printf ("%s%s", undo_action->action.delete.text,
- last_action->action.delete.text);
-
- g_free (last_action->action.delete.text);
- last_action->action.delete.start = undo_action->action.delete.start;
- last_action->action.delete.text = str;
- }
- }
- else if (undo_action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
- {
- gchar* str;
-
-#define I (last_action->action.insert.chars - 1)
-
- if ((undo_action->action.insert.pos !=
- (last_action->action.insert.pos + last_action->action.insert.chars)) ||
- ((g_utf8_get_char (undo_action->action.insert.text) != ' ') &&
- (g_utf8_get_char (undo_action->action.insert.text) != '\t') &&
- ((g_utf8_get_char_at (last_action->action.insert.text, I) == ' ') ||
- (g_utf8_get_char_at (last_action->action.insert.text, I) == '\t')))
- )
- {
- last_action->mergeable = FALSE;
- return FALSE;
- }
-
- str = g_strdup_printf ("%s%s", last_action->action.insert.text,
- undo_action->action.insert.text);
-
- g_free (last_action->action.insert.text);
- last_action->action.insert.length += undo_action->action.insert.length;
- last_action->action.insert.text = str;
- last_action->action.insert.chars += undo_action->action.insert.chars;
-
- }
- else if (undo_action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT_ANCHOR)
- {
- /* Nothing needs to be done */
- }
- else
- /* Unknown action inside undo merge encountered */
- g_return_val_if_reached (TRUE);
-
- return TRUE;
-}
-
-gint
-gtk_source_undo_manager_get_max_undo_levels (GtkSourceUndoManager *um)
-{
- g_return_val_if_fail (um != NULL, 0);
- g_return_val_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um), 0);
-
- return um->priv->max_undo_levels;
-}
-
-void
-gtk_source_undo_manager_set_max_undo_levels (GtkSourceUndoManager *um,
- gint max_undo_levels)
-{
- gint old_levels;
-
- g_return_if_fail (um != NULL);
- g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
-
- old_levels = um->priv->max_undo_levels;
- um->priv->max_undo_levels = max_undo_levels;
-
- if (max_undo_levels < 1)
- return;
-
- if (old_levels > max_undo_levels)
- {
- /* strip redo actions first */
- while (um->priv->next_redo >= 0 && (um->priv->num_of_groups > max_undo_levels))
- {
- gtk_source_undo_manager_free_first_n_actions (um, 1);
- um->priv->next_redo--;
- }
-
- /* now remove undo actions if necessary */
- gtk_source_undo_manager_check_list_size (um);
-
- /* emit "can_undo" and/or "can_redo" if appropiate */
- if (um->priv->next_redo < 0 && um->priv->can_redo)
- {
- um->priv->can_redo = FALSE;
- g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_REDO], 0, FALSE);
- }
-
- if (um->priv->can_undo &&
- um->priv->next_redo >= (gint)(g_list_length (um->priv->actions) - 1))
- {
- um->priv->can_undo = FALSE;
- g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_UNDO], 0, FALSE);
- }
- }
-}
-
-static void
-gtk_source_undo_manager_modified_changed_handler (GtkTextBuffer *buffer,
- GtkSourceUndoManager *um)
-{
- GtkSourceUndoAction *action;
- GList *list;
-
- g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
- g_return_if_fail (um->priv != NULL);
-
- if (um->priv->actions == NULL)
- return;
-
- list = g_list_nth (um->priv->actions, um->priv->next_redo + 1);
-
- if (list != NULL)
- action = (GtkSourceUndoAction*) list->data;
- else
- action = NULL;
-
- if (gtk_text_buffer_get_modified (buffer) == FALSE)
- {
- if (action != NULL)
- action->mergeable = FALSE;
-
- if (um->priv->modified_action != NULL)
- {
- um->priv->modified_action->modified = FALSE;
- um->priv->modified_action = NULL;
- }
-
- return;
- }
-
- if (action == NULL)
- {
- g_return_if_fail (um->priv->running_not_undoable_actions > 0);
-
- return;
- }
-
- /* gtk_text_buffer_get_modified (buffer) == TRUE */
-
- g_return_if_fail (um->priv->modified_action == NULL);
-
- if (action->order_in_group > 1)
- um->priv->modified_undoing_group = TRUE;
-
- while (action->order_in_group > 1)
- {
- list = g_list_next (list);
- g_return_if_fail (list != NULL);
-
- action = (GtkSourceUndoAction*) list->data;
- g_return_if_fail (action != NULL);
- }
-
- action->modified = TRUE;
- um->priv->modified_action = action;
-}
diff --git a/pidgin/gtksourceundomanager.h b/pidgin/gtksourceundomanager.h
deleted file mode 100644
index 0148455476..0000000000
--- a/pidgin/gtksourceundomanager.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * gtksourceundomanager.h
- * This file is part of GtkSourceView
- *
- * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence
- * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi
- * Copyright (C) 2002, 2003 Paolo Maggi
- *
- * 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 __GTK_SOURCE_UNDO_MANAGER_H__
-#define __GTK_SOURCE_UNDO_MANAGER_H__
-
-#include <gtk/gtk.h>
-
-#define GTK_SOURCE_TYPE_UNDO_MANAGER (gtk_source_undo_manager_get_type())
-#define GTK_SOURCE_UNDO_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_SOURCE_TYPE_UNDO_MANAGER, GtkSourceUndoManager))
-#define GTK_SOURCE_UNDO_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GTK_SOURCE_TYPE_UNDO_MANAGER, GtkSourceUndoManagerClass))
-#define GTK_SOURCE_IS_UNDO_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_SOURCE_TYPE_UNDO_MANAGER))
-#define GTK_SOURCE_IS_UNDO_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_SOURCE_TYPE_UNDO_MANAGER))
-#define GTK_SOURCE_UNDO_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_SOURCE_TYPE_UNDO_MANAGER, GtkSourceUndoManagerClass))
-
-
-typedef struct _GtkSourceUndoManager GtkSourceUndoManager;
-typedef struct _GtkSourceUndoManagerClass GtkSourceUndoManagerClass;
-
-typedef struct _GtkSourceUndoManagerPrivate GtkSourceUndoManagerPrivate;
-
-struct _GtkSourceUndoManager
-{
- GObject base;
-
- GtkSourceUndoManagerPrivate *priv;
-};
-
-struct _GtkSourceUndoManagerClass
-{
- GObjectClass parent_class;
-
- /* Signals */
- void (*can_undo) (GtkSourceUndoManager *um, gboolean can_undo);
- void (*can_redo) (GtkSourceUndoManager *um, gboolean can_redo);
-};
-
-GType gtk_source_undo_manager_get_type (void) G_GNUC_CONST;
-
-GtkSourceUndoManager* gtk_source_undo_manager_new (GtkTextBuffer *buffer);
-
-gboolean gtk_source_undo_manager_can_undo (const GtkSourceUndoManager *um);
-gboolean gtk_source_undo_manager_can_redo (const GtkSourceUndoManager *um);
-
-void gtk_source_undo_manager_undo (GtkSourceUndoManager *um);
-void gtk_source_undo_manager_redo (GtkSourceUndoManager *um);
-
-void gtk_source_undo_manager_begin_not_undoable_action
- (GtkSourceUndoManager *um);
-void gtk_source_undo_manager_end_not_undoable_action
- (GtkSourceUndoManager *um);
-
-gint gtk_source_undo_manager_get_max_undo_levels
- (GtkSourceUndoManager *um);
-void gtk_source_undo_manager_set_max_undo_levels
- (GtkSourceUndoManager *um,
- gint undo_levels);
-
-#endif /* __GTK_SOURCE_UNDO_MANAGER_H__ */
-
-
diff --git a/pidgin/gtksourceview-marshal.c b/pidgin/gtksourceview-marshal.c
deleted file mode 100644
index a006eb35a7..0000000000
--- a/pidgin/gtksourceview-marshal.c
+++ /dev/null
@@ -1,95 +0,0 @@
-#include "gtksourceview-marshal.h"
-
-#include <glib-object.h>
-
-
-#ifdef G_ENABLE_DEBUG
-#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
-#define g_marshal_value_peek_char(v) g_value_get_char (v)
-#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
-#define g_marshal_value_peek_int(v) g_value_get_int (v)
-#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
-#define g_marshal_value_peek_long(v) g_value_get_long (v)
-#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
-#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
-#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
-#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
-#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
-#define g_marshal_value_peek_float(v) g_value_get_float (v)
-#define g_marshal_value_peek_double(v) g_value_get_double (v)
-#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
-#define g_marshal_value_peek_param(v) g_value_get_param (v)
-#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
-#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
-#define g_marshal_value_peek_object(v) g_value_get_object (v)
-#else /* !G_ENABLE_DEBUG */
-/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
- * Do not access GValues directly in your code. Instead, use the
- * g_value_get_*() functions
- */
-#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
-#define g_marshal_value_peek_char(v) (v)->data[0].v_int
-#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
-#define g_marshal_value_peek_int(v) (v)->data[0].v_int
-#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
-#define g_marshal_value_peek_long(v) (v)->data[0].v_long
-#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
-#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
-#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
-#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
-#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
-#define g_marshal_value_peek_float(v) (v)->data[0].v_float
-#define g_marshal_value_peek_double(v) (v)->data[0].v_double
-#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
-#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
-#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
-#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
-#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
-#endif /* !G_ENABLE_DEBUG */
-
-
-/* VOID:VOID (gtksourceview-marshal.list:1) */
-
-/* VOID:BOOLEAN (gtksourceview-marshal.list:2) */
-
-/* VOID:BOXED (gtksourceview-marshal.list:3) */
-
-/* VOID:BOXED,BOXED (gtksourceview-marshal.list:4) */
-void
-gtksourceview_marshal_VOID__BOXED_BOXED (GClosure *closure,
- GValue *return_value,
- guint n_param_values,
- const GValue *param_values,
- gpointer invocation_hint,
- gpointer marshal_data)
-{
- typedef void (*GMarshalFunc_VOID__BOXED_BOXED) (gpointer data1,
- gpointer arg_1,
- gpointer arg_2,
- gpointer data2);
- register GMarshalFunc_VOID__BOXED_BOXED callback;
- register GCClosure *cc = (GCClosure*) closure;
- register gpointer data1, data2;
-
- g_return_if_fail (n_param_values == 3);
-
- if (G_CCLOSURE_SWAP_DATA (closure))
- {
- data1 = closure->data;
- data2 = g_value_peek_pointer (param_values + 0);
- }
- else
- {
- data1 = g_value_peek_pointer (param_values + 0);
- data2 = closure->data;
- }
- callback = (GMarshalFunc_VOID__BOXED_BOXED) (marshal_data ? marshal_data : cc->callback);
-
- callback (data1,
- g_marshal_value_peek_boxed (param_values + 1),
- g_marshal_value_peek_boxed (param_values + 2),
- data2);
-}
-
-/* VOID:STRING (gtksourceview-marshal.list:5) */
-
diff --git a/pidgin/gtksourceview-marshal.h b/pidgin/gtksourceview-marshal.h
deleted file mode 100644
index 8da30a9e96..0000000000
--- a/pidgin/gtksourceview-marshal.h
+++ /dev/null
@@ -1,32 +0,0 @@
-
-#ifndef __gtksourceview_marshal_MARSHAL_H__
-#define __gtksourceview_marshal_MARSHAL_H__
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-/* VOID:VOID (gtksourceview-marshal.list:1) */
-#define gtksourceview_marshal_VOID__VOID g_cclosure_marshal_VOID__VOID
-
-/* VOID:BOOLEAN (gtksourceview-marshal.list:2) */
-#define gtksourceview_marshal_VOID__BOOLEAN g_cclosure_marshal_VOID__BOOLEAN
-
-/* VOID:BOXED (gtksourceview-marshal.list:3) */
-#define gtksourceview_marshal_VOID__BOXED g_cclosure_marshal_VOID__BOXED
-
-/* VOID:BOXED,BOXED (gtksourceview-marshal.list:4) */
-extern void gtksourceview_marshal_VOID__BOXED_BOXED (GClosure *closure,
- GValue *return_value,
- guint n_param_values,
- const GValue *param_values,
- gpointer invocation_hint,
- gpointer marshal_data);
-
-/* VOID:STRING (gtksourceview-marshal.list:5) */
-#define gtksourceview_marshal_VOID__STRING g_cclosure_marshal_VOID__STRING
-
-G_END_DECLS
-
-#endif /* __gtksourceview_marshal_MARSHAL_H__ */
-
diff --git a/pidgin/gtkstatus-icon-theme.h b/pidgin/gtkstatus-icon-theme.h
index ba8269a778..d244620e4b 100644
--- a/pidgin/gtkstatus-icon-theme.h
+++ b/pidgin/gtkstatus-icon-theme.h
@@ -1,7 +1,3 @@
-/**
- * @file gtkstatus-icon-theme.h Pidgin Icon Theme Class API
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -25,17 +21,16 @@
#ifndef PIDGIN_STATUS_ICON_THEME_H
#define PIDGIN_STATUS_ICON_THEME_H
+/**
+ * SECTION:gtkstatus-icon-theme
+ * @section_id: pidgin-gtkstatus-icon-theme
+ * @short_description: <filename>gtkstatus-icon-theme.h</filename>
+ * @title: Pidgin Icon Theme Class
+ */
#include <glib-object.h>
#include "gtkicon-theme.h"
-/**
- * extends PidginIconTheme (gtkicon-theme.h)
- * A pidgin status icon theme.
- * This object represents a Pidgin status icon theme.
- *
- * PidginStatusIconTheme is a PidginIconTheme Object.
- */
typedef struct _PidginStatusIconTheme PidginStatusIconTheme;
typedef struct _PidginStatusIconThemeClass PidginStatusIconThemeClass;
@@ -46,6 +41,15 @@ typedef struct _PidginStatusIconThemeClass PidginStatusIconThemeClass;
#define PIDGIN_IS_STATUS_ICON_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_STATUS_ICON_THEME))
#define PIDGIN_STATUS_ICON_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_STATUS_ICON_THEME, PidginStatusIconThemeClass))
+/**
+ * PidginStatusIconTheme:
+ *
+ * extends PidginIconTheme (gtkicon-theme.h)
+ * A pidgin status icon theme.
+ * This object represents a Pidgin status icon theme.
+ *
+ * PidginStatusIconTheme is a PidginIconTheme Object.
+ */
struct _PidginStatusIconTheme
{
PidginIconTheme parent;
@@ -57,15 +61,17 @@ struct _PidginStatusIconThemeClass
};
/**************************************************************************/
-/** @name Pidgin Status Icon Theme API */
+/* Pidgin Status Icon Theme API */
/**************************************************************************/
G_BEGIN_DECLS
/**
- * GObject foo.
- * @internal.
+ * pidgin_status_icon_theme_get_type:
+ *
+ * Returns: The #GType for a status icon theme.
*/
GType pidgin_status_icon_theme_get_type(void);
G_END_DECLS
+
#endif /* PIDGIN_STATUS_ICON_THEME_H */
diff --git a/pidgin/gtkstatusbox.c b/pidgin/gtkstatusbox.c
index acc19eea4f..20033ef523 100644
--- a/pidgin/gtkstatusbox.c
+++ b/pidgin/gtkstatusbox.c
@@ -1,8 +1,3 @@
-/*
- * @file gtkstatusbox.c GTK+ Status Selection Widget
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -47,7 +42,6 @@
#include "account.h"
#include "buddyicon.h"
#include "core.h"
-#include "imgstore.h"
#include "network.h"
#include "request.h"
#include "savedstatuses.h"
@@ -60,18 +54,13 @@
#include "gtkstatusbox.h"
#include "gtkutils.h"
-#ifdef USE_GTKSPELL
-# include <gtkspell/gtkspell.h>
-# ifdef _WIN32
-# include "wspell.h"
-# endif
-#endif
+#include "gtk3compat.h"
/* Timeout for typing notifications in seconds */
#define TYPING_TIMEOUT 4
-static void imhtml_changed_cb(GtkTextBuffer *buffer, void *data);
-static void imhtml_format_changed_cb(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons, void *data);
+static void webview_changed_cb(PidginWebView *webview, void *data);
+static void webview_format_changed_cb(PidginWebView *webview, PidginWebViewButtons buttons, void *data);
static void remove_typing_cb(PidginStatusBox *box);
static void update_size (PidginStatusBox *box);
static gint get_statusbox_index(PidginStatusBox *box, PurpleSavedStatus *saved_status);
@@ -82,13 +71,19 @@ static void pidgin_status_box_refresh(PidginStatusBox *status_box);
static void status_menu_refresh_iter(PidginStatusBox *status_box, gboolean status_changed);
static void pidgin_status_box_regenerate(PidginStatusBox *status_box, gboolean status_changed);
static void pidgin_status_box_changed(PidginStatusBox *box);
+#if GTK_CHECK_VERSION(3,0,0)
+static void pidgin_status_box_get_preferred_height (GtkWidget *widget,
+ gint *minimum_height, gint *natural_height);
+static gboolean pidgin_status_box_draw (GtkWidget *widget, cairo_t *cr);
+#else
static void pidgin_status_box_size_request (GtkWidget *widget, GtkRequisition *requisition);
-static void pidgin_status_box_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
static gboolean pidgin_status_box_expose_event (GtkWidget *widget, GdkEventExpose *event);
+#endif
+static void pidgin_status_box_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
static void pidgin_status_box_redisplay_buddy_icon(PidginStatusBox *status_box);
static void pidgin_status_box_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data);
-static void pidgin_status_box_popup(PidginStatusBox *box);
-static void pidgin_status_box_popdown(PidginStatusBox *box);
+static void pidgin_status_box_popup(PidginStatusBox *box, GdkEvent *event);
+static void pidgin_status_box_popdown(PidginStatusBox *box, GdkEvent *event);
static void do_colorshift (GdkPixbuf *dest, GdkPixbuf *src, int shift);
static void icon_choose_cb(const char *filename, gpointer data);
@@ -96,35 +91,35 @@ static void remove_buddy_icon_cb(GtkWidget *w, PidginStatusBox *box);
static void choose_buddy_icon_cb(GtkWidget *w, PidginStatusBox *box);
enum {
- /** A PidginStatusBoxItemType */
+ /* A PidginStatusBoxItemType */
TYPE_COLUMN,
- /** This is the stock-id for the icon. */
+ /* This is the stock-id for the icon. */
ICON_STOCK_COLUMN,
- /**
+ /*
* This is a GdkPixbuf (the other columns are strings).
* This column is visible.
*/
ICON_COLUMN,
- /** The text displayed on the status box. This column is visible. */
+ /* The text displayed on the status box. This column is visible. */
TEXT_COLUMN,
- /** The plain-English title of this item */
+ /* The plain-English title of this item */
TITLE_COLUMN,
- /** A plain-English description of this item */
+ /* A plain-English description of this item */
DESC_COLUMN,
- /**
+ /*
* This value depends on TYPE_COLUMN. For POPULAR types,
* this is the creation time. For PRIMITIVE types,
* this is the PurpleStatusPrimitive.
*/
DATA_COLUMN,
- /**
+ /*
* This column stores the GdkPixbuf for the status emblem. Currently only 'saved' is stored.
* In the GtkTreeModel for the dropdown, this is the stock-id (gchararray), and for the
* GtkTreeModel for the cell_view (for the account-specific statusbox), this is the prpl-icon
@@ -132,7 +127,7 @@ enum {
*/
EMBLEM_COLUMN,
- /**
+ /*
* This column stores whether to show the emblem.
*/
EMBLEM_VISIBLE_COLUMN,
@@ -253,7 +248,7 @@ update_to_reflect_account_status(PidginStatusBox *status_box, PurpleAccount *acc
const char *message;
statustype = purple_status_type_find_with_id((GList *)purple_account_get_status_types(account),
- (char *)purple_status_type_get_id(purple_status_get_type(newstatus)));
+ (char *)purple_status_type_get_id(purple_status_get_status_type(newstatus)));
for (l = purple_account_get_status_types(account); l != NULL; l = l->next) {
PurpleStatusType *status_type = (PurpleStatusType *)l->data;
@@ -266,8 +261,11 @@ update_to_reflect_account_status(PidginStatusBox *status_box, PurpleAccount *acc
break;
}
- gtk_imhtml_set_populate_primary_clipboard(
- GTK_IMHTML(status_box->imhtml), TRUE);
+#if 0
+ /* TODO WebKit: Doesn't do this? */
+ pidgin_webview_set_populate_primary_clipboard(
+ PIDGIN_WEBVIEW(status_box->webview), TRUE);
+#endif
if (status_no != -1) {
GtkTreePath *path;
@@ -282,16 +280,14 @@ update_to_reflect_account_status(PidginStatusBox *status_box, PurpleAccount *acc
if (!message || !*message)
{
- gtk_widget_hide_all(status_box->vbox);
- status_box->imhtml_visible = FALSE;
+ gtk_widget_hide(status_box->vbox);
+ status_box->webview_visible = FALSE;
}
else
{
gtk_widget_show_all(status_box->vbox);
- status_box->imhtml_visible = TRUE;
- gtk_imhtml_clear(GTK_IMHTML(status_box->imhtml));
- gtk_imhtml_clear_formatting(GTK_IMHTML(status_box->imhtml));
- gtk_imhtml_append_text(GTK_IMHTML(status_box->imhtml), message, 0);
+ status_box->webview_visible = TRUE;
+ pidgin_webview_load_html_string(PIDGIN_WEBVIEW(status_box->webview), message);
}
gtk_widget_set_sensitive(GTK_WIDGET(status_box), TRUE);
pidgin_status_box_refresh(status_box);
@@ -343,9 +339,10 @@ static void
icon_box_dnd_cb(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
GtkSelectionData *sd, guint info, guint t, PidginStatusBox *box)
{
- gchar *name = (gchar *)sd->data;
+ gchar *name = (gchar *) gtk_selection_data_get_data(sd);
- if ((sd->length >= 0) && (sd->format == 8)) {
+ if ((gtk_selection_data_get_length(sd) >= 0)
+ && (gtk_selection_data_get_format(sd) == 8)) {
/* Well, it looks like the drag event was cool.
* Let's do something with it */
if (!g_ascii_strncasecmp(name, "file://", 7)) {
@@ -368,58 +365,10 @@ icon_box_dnd_cb(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
gtk_drag_finish(dc, FALSE, FALSE, t);
}
-static void
-statusbox_got_url(PurpleUtilFetchUrlData *url_data, gpointer user_data,
- const gchar *themedata, size_t len, const gchar *error_message)
-{
- FILE *f;
- gchar *path;
- size_t wc;
-
- if ((error_message != NULL) || (len == 0))
- return;
-
- f = purple_mkstemp(&path, TRUE);
- wc = fwrite(themedata, len, 1, f);
- if (wc != 1) {
- purple_debug_warning("theme_got_url", "Unable to write theme data.\n");
- fclose(f);
- g_unlink(path);
- g_free(path);
- return;
- }
- fclose(f);
-
- icon_choose_cb(path, user_data);
-
- g_unlink(path);
- g_free(path);
-}
-
-
-static gboolean
-statusbox_uri_handler(const char *proto, const char *cmd, GHashTable *params, void *data)
-{
- const char *src;
-
- if (g_ascii_strcasecmp(proto, "aim"))
- return FALSE;
-
- if (g_ascii_strcasecmp(cmd, "buddyicon"))
- return FALSE;
-
- src = g_hash_table_lookup(params, "account");
- if (src == NULL)
- return FALSE;
-
- purple_util_fetch_url(src, TRUE, NULL, FALSE, statusbox_got_url, data);
- return TRUE;
-}
-
static gboolean
icon_box_enter_cb(GtkWidget *widget, GdkEventCrossing *event, PidginStatusBox *box)
{
- gdk_window_set_cursor(widget->window, box->hand_cursor);
+ gdk_window_set_cursor(gtk_widget_get_window(widget), box->hand_cursor);
gtk_image_set_from_pixbuf(GTK_IMAGE(box->icon), box->buddy_icon_hover);
return FALSE;
}
@@ -427,7 +376,7 @@ icon_box_enter_cb(GtkWidget *widget, GdkEventCrossing *event, PidginStatusBox *b
static gboolean
icon_box_leave_cb(GtkWidget *widget, GdkEventCrossing *event, PidginStatusBox *box)
{
- gdk_window_set_cursor(widget->window, box->arrow_cursor);
+ gdk_window_set_cursor(gtk_widget_get_window(widget), box->arrow_cursor);
gtk_image_set_from_pixbuf(GTK_IMAGE(box->icon), box->buddy_icon) ;
return FALSE;
}
@@ -450,34 +399,28 @@ setup_icon_box(PidginStatusBox *status_box)
gtk_widget_set_parent(status_box->icon_box, GTK_WIDGET(status_box));
gtk_widget_show(status_box->icon_box);
-#if GTK_CHECK_VERSION(2,12,0)
gtk_widget_set_tooltip_text(status_box->icon_box,
status_box->account ? _("Click to change your buddyicon for this account.") :
_("Click to change your buddyicon for all accounts."));
-#endif
if (status_box->account &&
!purple_account_get_bool(status_box->account, "use-global-buddyicon", TRUE))
{
- PurpleStoredImage *img = purple_buddy_icons_find_account_icon(status_box->account);
+ PurpleImage *img = purple_buddy_icons_find_account_icon(status_box->account);
pidgin_status_box_set_buddy_icon(status_box, img);
- purple_imgstore_unref(img);
+ g_object_unref(img);
}
else
{
const char *filename = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon");
- PurpleStoredImage *img = NULL;
+ PurpleImage *img = NULL;
if (filename && *filename)
- img = purple_imgstore_new_from_file(filename);
+ img = purple_image_new_from_file(filename, TRUE);
pidgin_status_box_set_buddy_icon(status_box, img);
if (img)
- /*
- * purple_imgstore_new gives us a reference and
- * pidgin_status_box_set_buddy_icon also takes one.
- */
- purple_imgstore_unref(img);
+ g_object_unref(img);
}
status_box->hand_cursor = gdk_cursor_new (GDK_HAND2);
@@ -507,10 +450,17 @@ destroy_icon_box(PidginStatusBox *statusbox)
return;
gtk_widget_destroy(statusbox->icon_box);
+
+#if GTK_CHECK_VERSION(3,0,0)
+ g_object_unref(statusbox->hand_cursor);
+ g_object_unref(statusbox->arrow_cursor);
+#else
gdk_cursor_unref(statusbox->hand_cursor);
gdk_cursor_unref(statusbox->arrow_cursor);
+#endif
- purple_imgstore_unref(statusbox->buddy_icon_img);
+ if (statusbox->buddy_icon_img)
+ g_object_unref(statusbox->buddy_icon_img);
g_object_unref(G_OBJECT(statusbox->buddy_icon));
g_object_unref(G_OBJECT(statusbox->buddy_icon_hover));
@@ -533,7 +483,7 @@ destroy_icon_box(PidginStatusBox *statusbox)
static void
pidgin_status_box_set_property(GObject *object, guint param_id,
- const GValue *value, GParamSpec *pspec)
+ const GValue *value, GParamSpec *pspec)
{
PidginStatusBox *statusbox = PIDGIN_STATUS_BOX(object);
@@ -574,7 +524,7 @@ static void
pidgin_status_box_finalize(GObject *obj)
{
PidginStatusBox *statusbox = PIDGIN_STATUS_BOX(obj);
- int i;
+ gsize i;
purple_signals_disconnect_by_handle(statusbox);
purple_prefs_disconnect_by_handle(statusbox);
@@ -602,7 +552,7 @@ pidgin_status_box_finalize(GObject *obj)
static GType
pidgin_status_box_child_type (GtkContainer *container)
{
- return GTK_TYPE_WIDGET;
+ return GTK_TYPE_WIDGET;
}
static void
@@ -615,9 +565,14 @@ pidgin_status_box_class_init (PidginStatusBoxClass *klass)
parent_class = g_type_class_peek_parent(klass);
widget_class = (GtkWidgetClass*)klass;
+#if GTK_CHECK_VERSION(3,0,0)
+ widget_class->get_preferred_height = pidgin_status_box_get_preferred_height;
+ widget_class->draw = pidgin_status_box_draw;
+#else
widget_class->size_request = pidgin_status_box_size_request;
- widget_class->size_allocate = pidgin_status_box_size_allocate;
widget_class->expose_event = pidgin_status_box_expose_event;
+#endif
+ widget_class->size_allocate = pidgin_status_box_size_allocate;
container_class->child_type = pidgin_status_box_child_type;
container_class->forall = pidgin_status_box_forall;
@@ -635,7 +590,7 @@ pidgin_status_box_class_init (PidginStatusBoxClass *klass)
g_param_spec_pointer("account",
"Account",
"The account, or NULL for all accounts",
- G_PARAM_READWRITE
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
)
);
g_object_class_install_property(object_class,
@@ -644,12 +599,12 @@ pidgin_status_box_class_init (PidginStatusBoxClass *klass)
"Icon Selector",
"Whether the icon selector should be displayed or not.",
FALSE,
- G_PARAM_READWRITE
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
)
);
}
-/**
+/*
* This updates the text displayed on the status box so that it shows
* the current status. This is the only function in this file that
* should modify status_box->store
@@ -705,7 +660,7 @@ pidgin_status_box_refresh(PidginStatusBox *status_box)
else if (account_status)
primary = g_strdup(purple_status_get_name(purple_account_get_active_status(acct)));
else if (purple_savedstatus_is_transient(saved_status))
- primary = g_strdup(purple_primitive_get_name_from_type(purple_savedstatus_get_type(saved_status)));
+ primary = g_strdup(purple_primitive_get_name_from_type(purple_savedstatus_get_primitive_type(saved_status)));
else
primary = g_markup_escape_text(purple_savedstatus_get_title(saved_status), -1);
@@ -744,10 +699,10 @@ pidgin_status_box_refresh(PidginStatusBox *status_box)
PurpleStatusType *status_type;
PurpleStatusPrimitive prim;
if (account_status) {
- status_type = purple_status_get_type(purple_account_get_active_status(acct));
+ status_type = purple_status_get_status_type(purple_account_get_active_status(acct));
prim = purple_status_type_get_primitive(status_type);
} else {
- prim = purple_savedstatus_get_type(saved_status);
+ prim = purple_savedstatus_get_primitive_type(saved_status);
}
stock = pidgin_stock_id_from_status_primitive(prim);
@@ -809,7 +764,7 @@ find_status_type_by_index(const PurpleAccount *account, gint active)
return NULL;
}
-/**
+/*
* This updates the GtkTreeView so that it correctly shows the state
* we are currently using. It is used when the current state is
* updated from somewhere other than the GtkStatusBox (from a plugin,
@@ -844,7 +799,7 @@ status_menu_refresh_iter(PidginStatusBox *status_box, gboolean status_changed)
* If there is a token-account, then select the primitive from the
* dropdown using a loop. Otherwise select from the default list.
*/
- primitive = purple_savedstatus_get_type(saved_status);
+ primitive = purple_savedstatus_get_primitive_type(saved_status);
if (!status_box->token_status_account && purple_savedstatus_is_transient(saved_status) &&
((primitive == PURPLE_STATUS_AVAILABLE) || (primitive == PURPLE_STATUS_AWAY) ||
(primitive == PURPLE_STATUS_INVISIBLE) || (primitive == PURPLE_STATUS_OFFLINE) ||
@@ -873,7 +828,7 @@ status_menu_refresh_iter(PidginStatusBox *status_box, gboolean status_changed)
/* This is a special case because Primitives for the token_status_account are actually
* saved statuses with substatuses for the enabled accounts */
if (status_box->token_status_account && purple_savedstatus_is_transient(saved_status)
- && type == PIDGIN_STATUS_BOX_TYPE_PRIMITIVE && primitive == GPOINTER_TO_INT(data))
+ && type == PIDGIN_STATUS_BOX_TYPE_PRIMITIVE && primitive == (PurpleStatusPrimitive)GPOINTER_TO_INT(data))
{
char *name;
const char *acct_status_name = purple_status_get_name(
@@ -915,7 +870,7 @@ status_menu_refresh_iter(PidginStatusBox *status_box, gboolean status_changed)
message = purple_savedstatus_get_message(saved_status);
/*
- * If we are going to hide the imhtml, don't retain the
+ * If we are going to hide the webview, don't retain the
* message because showing the old message later is
* confusing. If we are going to set the message to a pre-set,
* then we need to do this anyway
@@ -923,25 +878,25 @@ status_menu_refresh_iter(PidginStatusBox *status_box, gboolean status_changed)
* Suppress the "changed" signal because the status
* was changed programmatically.
*/
- gtk_widget_set_sensitive(GTK_WIDGET(status_box->imhtml), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(status_box->webview), FALSE);
- gtk_imhtml_clear(GTK_IMHTML(status_box->imhtml));
- gtk_imhtml_clear_formatting(GTK_IMHTML(status_box->imhtml));
+ pidgin_webview_load_html_string(PIDGIN_WEBVIEW(status_box->webview), "");
+ pidgin_webview_clear_formatting(PIDGIN_WEBVIEW(status_box->webview));
if (!purple_savedstatus_is_transient(saved_status) || !message || !*message)
{
- status_box->imhtml_visible = FALSE;
- gtk_widget_hide_all(status_box->vbox);
+ status_box->webview_visible = FALSE;
+ gtk_widget_hide(status_box->vbox);
}
else
{
- status_box->imhtml_visible = TRUE;
+ status_box->webview_visible = TRUE;
gtk_widget_show_all(status_box->vbox);
- gtk_imhtml_append_text(GTK_IMHTML(status_box->imhtml), message, 0);
+ pidgin_webview_load_html_string(PIDGIN_WEBVIEW(status_box->webview), message);
}
- gtk_widget_set_sensitive(GTK_WIDGET(status_box->imhtml), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(status_box->webview), TRUE);
update_size(status_box);
}
@@ -1118,28 +1073,35 @@ pidgin_status_box_regenerate(PidginStatusBox *status_box, gboolean status_change
gtk_tree_view_set_search_column(GTK_TREE_VIEW(status_box->tree_view), TEXT_COLUMN);
}
-static gboolean combo_box_scroll_event_cb(GtkWidget *w, GdkEventScroll *event, GtkIMHtml *imhtml)
+static gboolean
+combo_box_scroll_event_cb(GtkWidget *w, GdkEventScroll *event, PidginWebView *webview)
{
- pidgin_status_box_popup(PIDGIN_STATUS_BOX(w));
+ pidgin_status_box_popup(PIDGIN_STATUS_BOX(w), (GdkEvent *)event);
return TRUE;
}
-static gboolean imhtml_scroll_event_cb(GtkWidget *w, GdkEventScroll *event, GtkIMHtml *imhtml)
+static gboolean
+webview_scroll_event_cb(GtkWidget *w, GdkEventScroll *event, PidginWebView *webview)
{
if (event->direction == GDK_SCROLL_UP)
- gtk_imhtml_page_up(imhtml);
+ pidgin_webview_page_up(webview);
else if (event->direction == GDK_SCROLL_DOWN)
- gtk_imhtml_page_down(imhtml);
+ pidgin_webview_page_down(webview);
return TRUE;
}
-static gboolean imhtml_remove_focus(GtkWidget *w, GdkEventKey *event, PidginStatusBox *status_box)
+static gboolean
+webview_remove_focus(GtkWidget *w, GdkEventKey *event, PidginStatusBox *status_box)
{
- if (event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab || event->keyval == GDK_ISO_Left_Tab)
+ if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter) {
+ remove_typing_cb(status_box);
+ return TRUE;
+ }
+ else if (event->keyval == GDK_KEY_Tab || event->keyval == GDK_KEY_KP_Tab || event->keyval == GDK_KEY_ISO_Left_Tab)
{
/* If last inserted character is a tab, then remove the focus from here */
GtkWidget *top = gtk_widget_get_toplevel(w);
- g_signal_emit_by_name(G_OBJECT(top), "move_focus",
+ g_signal_emit_by_name(G_OBJECT(top), "move-focus",
(event->state & GDK_SHIFT_MASK) ?
GTK_DIR_TAB_BACKWARD: GTK_DIR_TAB_FORWARD);
return TRUE;
@@ -1148,12 +1110,15 @@ static gboolean imhtml_remove_focus(GtkWidget *w, GdkEventKey *event, PidginStat
return FALSE;
/* Reset the status if Escape was pressed */
- if (event->keyval == GDK_Escape)
+ if (event->keyval == GDK_KEY_Escape)
{
purple_timeout_remove(status_box->typing);
status_box->typing = 0;
- gtk_imhtml_set_populate_primary_clipboard(
- GTK_IMHTML(status_box->imhtml), TRUE);
+#if 0
+ /* TODO WebKit: Doesn't do this? */
+ pidgin_webview_set_populate_primary_clipboard(
+ PIDGIN_WEBVIEW(status_box->webview), TRUE);
+#endif
if (status_box->account != NULL)
update_to_reflect_account_status(status_box, status_box->account,
purple_account_get_active_status(status_box->account));
@@ -1189,7 +1154,7 @@ static void
cache_pixbufs(PidginStatusBox *status_box)
{
GtkIconSize icon_size;
- int i;
+ gsize i;
g_object_set(G_OBJECT(status_box->icon_rend), "xpad", 3, NULL);
icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL);
@@ -1252,18 +1217,10 @@ static void
spellcheck_prefs_cb(const char *name, PurplePrefType type,
gconstpointer value, gpointer data)
{
-#ifdef USE_GTKSPELL
PidginStatusBox *status_box = (PidginStatusBox *)data;
- if (value)
- pidgin_setup_gtkspell(GTK_TEXT_VIEW(status_box->imhtml));
- else
- {
- GtkSpell *spell;
- spell = gtkspell_get_from_text_view(GTK_TEXT_VIEW(status_box->imhtml));
- gtkspell_detach(spell);
- }
-#endif
+ pidgin_webview_set_spellcheck(PIDGIN_WEBVIEW(status_box->webview),
+ (gboolean)GPOINTER_TO_INT(value));
}
#if 0
@@ -1273,7 +1230,7 @@ static gboolean button_released_cb(GtkWidget *widget, GdkEventButton *event, Pid
if (event->button != 1)
return FALSE;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(box->toggle_button), FALSE);
- if (!box->imhtml_visible)
+ if (!box->webview_visible)
g_signal_emit_by_name(G_OBJECT(box), "changed", NULL, NULL);
return TRUE;
}
@@ -1294,98 +1251,122 @@ static gboolean button_pressed_cb(GtkWidget *widget, GdkEventButton *event, Pidg
static void
pidgin_status_box_list_position (PidginStatusBox *status_box, int *x, int *y, int *width, int *height)
{
- GdkScreen *screen;
- gint monitor_num;
- GdkRectangle monitor;
- GtkRequisition popup_req;
- GtkPolicyType hpolicy, vpolicy;
+ GdkScreen *screen;
+ gint monitor_num;
+ GdkRectangle monitor;
+ GtkRequisition popup_req;
+ GtkPolicyType hpolicy, vpolicy;
+ GtkAllocation allocation;
- gdk_window_get_origin (GTK_WIDGET(status_box)->window, x, y);
+ gtk_widget_get_allocation(GTK_WIDGET(status_box), &allocation);
+ gdk_window_get_origin(gtk_widget_get_window(GTK_WIDGET(status_box)), x, y);
- *x += GTK_WIDGET(status_box)->allocation.x;
- *y += GTK_WIDGET(status_box)->allocation.y;
+ *x += allocation.x;
+ *y += allocation.y;
- *width = GTK_WIDGET(status_box)->allocation.width;
+ *width = allocation.width;
- hpolicy = vpolicy = GTK_POLICY_NEVER;
+ hpolicy = vpolicy = GTK_POLICY_NEVER;
g_object_set(G_OBJECT(status_box->scrolled_window),
- "hscrollbar-policy", hpolicy,
- "vscrollbar-policy", vpolicy,
- NULL);
- gtk_widget_size_request (status_box->popup_frame, &popup_req);
-
- if (popup_req.width > *width)
- {
- hpolicy = GTK_POLICY_ALWAYS;
- g_object_set(G_OBJECT(status_box->scrolled_window),
- "hscrollbar-policy", hpolicy,
- "vscrollbar-policy", vpolicy,
- NULL);
- gtk_widget_size_request (status_box->popup_frame, &popup_req);
- }
-
- *height = popup_req.height;
-
- screen = gtk_widget_get_screen (GTK_WIDGET (status_box));
- monitor_num = gdk_screen_get_monitor_at_window (screen,
- GTK_WIDGET (status_box)->window);
- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
-
- if (*x < monitor.x)
- *x = monitor.x;
- else if (*x + *width > monitor.x + monitor.width)
- *x = monitor.x + monitor.width - *width;
-
- if (*y + GTK_WIDGET(status_box)->allocation.height + *height <= monitor.y + monitor.height)
- *y += GTK_WIDGET(status_box)->allocation.height;
- else if (*y - *height >= monitor.y)
- *y -= *height;
- else if (monitor.y + monitor.height - (*y + GTK_WIDGET(status_box)->allocation.height) > *y - monitor.y)
- {
- *y += GTK_WIDGET(status_box)->allocation.height;
- *height = monitor.y + monitor.height - *y;
- }
- else
- {
- *height = *y - monitor.y;
- *y = monitor.y;
- }
-
- if (popup_req.height > *height)
- {
- vpolicy = GTK_POLICY_ALWAYS;
-
- g_object_set(G_OBJECT(status_box->scrolled_window),
- "hscrollbar-policy", hpolicy,
- "vscrollbar-policy", vpolicy,
- NULL);
- }
+ "hscrollbar-policy", hpolicy,
+ "vscrollbar-policy", vpolicy,
+ NULL);
+ gtk_widget_get_preferred_size(status_box->popup_frame, NULL, &popup_req);
+
+ if (popup_req.width > *width) {
+ hpolicy = GTK_POLICY_ALWAYS;
+ g_object_set(G_OBJECT(status_box->scrolled_window),
+ "hscrollbar-policy", hpolicy,
+ "vscrollbar-policy", vpolicy,
+ NULL);
+ gtk_widget_get_preferred_size(status_box->popup_frame, NULL, &popup_req);
+ }
+
+ *height = popup_req.height;
+
+ screen = gtk_widget_get_screen(GTK_WIDGET(status_box));
+ monitor_num = gdk_screen_get_monitor_at_window(screen,
+ gtk_widget_get_window(GTK_WIDGET(status_box)));
+ gdk_screen_get_monitor_geometry(screen, monitor_num, &monitor);
+
+ if (*x < monitor.x)
+ *x = monitor.x;
+ else if (*x + *width > monitor.x + monitor.width)
+ *x = monitor.x + monitor.width - *width;
+
+ if (*y + allocation.height + *height <= monitor.y + monitor.height)
+ *y += allocation.height;
+ else if (*y - *height >= monitor.y)
+ *y -= *height;
+ else if (monitor.y + monitor.height - (*y + allocation.height) > *y - monitor.y)
+ {
+ *y += allocation.height;
+ *height = monitor.y + monitor.height - *y;
+ }
+ else
+ {
+ *height = *y - monitor.y;
+ *y = monitor.y;
+ }
+
+ if (popup_req.height > *height)
+ {
+ vpolicy = GTK_POLICY_ALWAYS;
+
+ g_object_set(G_OBJECT(status_box->scrolled_window),
+ "hscrollbar-policy", hpolicy,
+ "vscrollbar-policy", vpolicy,
+ NULL);
+ }
}
static gboolean
-popup_grab_on_window (GdkWindow *window,
- guint32 activate_time,
- gboolean grab_keyboard)
-{
- if ((gdk_pointer_grab (window, TRUE,
- GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
- GDK_POINTER_MOTION_MASK,
- NULL, NULL, activate_time) == 0))
+popup_grab_on_window(GdkWindow *window, GdkEvent *event)
+{
+ guint32 activate_time = gdk_event_get_time(event);
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkDevice *device = gdk_event_get_device(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);
+ 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 FALSE;
+#else
+ if ((gdk_pointer_grab(window, TRUE,
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK,
+ NULL, NULL, activate_time) == 0))
{
- if (!grab_keyboard || gdk_keyboard_grab (window, TRUE, activate_time) == 0)
+ if (gdk_keyboard_grab(window, TRUE, activate_time) == 0)
return TRUE;
else {
- gdk_display_pointer_ungrab (gdk_drawable_get_display (window), activate_time);
+ gdk_display_pointer_ungrab(gdk_window_get_display(window),
+ activate_time);
return FALSE;
}
}
return FALSE;
+#endif
}
static void
-pidgin_status_box_popup(PidginStatusBox *box)
+pidgin_status_box_popup(PidginStatusBox *box, GdkEvent *event)
{
int width, height, x, y;
pidgin_status_box_list_position (box, &x, &y, &width, &height);
@@ -1394,8 +1375,7 @@ pidgin_status_box_popup(PidginStatusBox *box)
gtk_window_move (GTK_WINDOW (box->popup_window), x, y);
gtk_widget_show(box->popup_window);
gtk_widget_grab_focus (box->tree_view);
- if (!popup_grab_on_window (box->popup_window->window,
- GDK_CURRENT_TIME, TRUE)) {
+ if (!popup_grab_on_window(gtk_widget_get_window(box->popup_window), event)) {
gtk_widget_hide (box->popup_window);
return;
}
@@ -1412,28 +1392,40 @@ pidgin_status_box_popup(PidginStatusBox *box)
}
static void
-pidgin_status_box_popdown(PidginStatusBox *box)
+pidgin_status_box_popdown(PidginStatusBox *box, GdkEvent *event)
{
+ guint32 time;
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkDevice *device;
+#endif
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);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(box->toggle_button), FALSE);
+ gtk_grab_remove(box->popup_window);
+ time = gdk_event_get_time(event);
+#if GTK_CHECK_VERSION(3,0,0)
+ device = gdk_event_get_device(event);
+ gdk_device_ungrab(device, time);
+ gdk_device_ungrab(gdk_device_get_associated_device(device), time);
+#else
+ gdk_pointer_ungrab(time);
+ gdk_keyboard_ungrab(time);
+#endif
}
static gboolean
toggle_key_press_cb(GtkWidget *widget, GdkEventKey *event, PidginStatusBox *box)
{
switch (event->keyval) {
- case GDK_Return:
- case GDK_KP_Enter:
- case GDK_KP_Space:
- case GDK_space:
+ case GDK_KEY_Return:
+ case GDK_KEY_KP_Enter:
+ case GDK_KEY_KP_Space:
+ case GDK_KEY_space:
if (!box->popup_in_progress) {
- pidgin_status_box_popup (box);
+ pidgin_status_box_popup(box, (GdkEvent *)event);
box->popup_in_progress = TRUE;
} else {
- pidgin_status_box_popdown(box);
+ pidgin_status_box_popdown(box, (GdkEvent *)event);
}
return TRUE;
default:
@@ -1445,16 +1437,16 @@ static gboolean
toggled_cb(GtkWidget *widget, GdkEventButton *event, PidginStatusBox *box)
{
if (!box->popup_in_progress)
- pidgin_status_box_popup (box);
+ pidgin_status_box_popup(box, (GdkEvent *)event);
else
- pidgin_status_box_popdown(box);
+ pidgin_status_box_popdown(box, (GdkEvent *)event);
return TRUE;
}
static void
buddy_icon_set_cb(const char *filename, PidginStatusBox *box)
{
- PurpleStoredImage *img = NULL;
+ PurpleImage *img = NULL;
if (box->account) {
PurplePlugin *plug = purple_find_prpl(purple_account_get_protocol_id(box->account));
@@ -1466,12 +1458,13 @@ buddy_icon_set_cb(const char *filename, PidginStatusBox *box)
if (filename)
data = pidgin_convert_buddy_icon(plug, filename, &len);
img = purple_buddy_icons_set_account_icon(box->account, data, len);
- if (img)
+ if (img) {
/*
* set_account_icon doesn't give us a reference, but we
* unref one below (for the other code path)
*/
- purple_imgstore_ref(img);
+ g_object_ref(img);
+ }
purple_account_set_buddy_icon_path(box->account, filename);
@@ -1500,12 +1493,12 @@ buddy_icon_set_cb(const char *filename, PidginStatusBox *box)
/* Even if no accounts were processed, load the icon that was set. */
if (filename != NULL)
- img = purple_imgstore_new_from_file(filename);
+ img = purple_image_new_from_file(filename, TRUE);
}
pidgin_status_box_set_buddy_icon(box, img);
if (img)
- purple_imgstore_unref(img);
+ g_object_unref(img);
}
static void
@@ -1555,13 +1548,13 @@ update_buddyicon_cb(const char *name, PurplePrefType type,
}
static void
-treeview_activate_current_selection(PidginStatusBox *status_box, GtkTreePath *path)
+treeview_activate_current_selection(PidginStatusBox *status_box, GtkTreePath *path, GdkEvent *event)
{
if (status_box->active_row)
gtk_tree_row_reference_free(status_box->active_row);
status_box->active_row = gtk_tree_row_reference_new(GTK_TREE_MODEL(status_box->dropdown_store), path);
- pidgin_status_box_popdown (status_box);
+ pidgin_status_box_popdown(status_box, event);
pidgin_status_box_changed(status_box);
}
@@ -1577,7 +1570,7 @@ static void tree_view_delete_current_selection_cb(gpointer data)
}
static void
-tree_view_delete_current_selection(PidginStatusBox *status_box, GtkTreePath *path)
+tree_view_delete_current_selection(PidginStatusBox *status_box, GtkTreePath *path, GdkEvent *event)
{
GtkTreeIter iter;
gpointer data;
@@ -1605,14 +1598,14 @@ tree_view_delete_current_selection(PidginStatusBox *status_box, GtkTreePath *pat
msg = g_strdup_printf(_("Are you sure you want to delete %s?"), purple_savedstatus_get_title(saved));
purple_request_action(saved, NULL, msg, NULL, 0,
- NULL, NULL, NULL,
+ NULL,
data, 2,
_("Delete"), tree_view_delete_current_selection_cb,
_("Cancel"), NULL);
g_free(msg);
- pidgin_status_box_popdown(status_box);
+ pidgin_status_box_popdown(status_box, event);
}
static gboolean
@@ -1626,7 +1619,7 @@ treeview_button_release_cb(GtkWidget *widget, GdkEventButton *event, PidginStatu
if (ewidget == status_box->toggle_button &&
status_box->popup_in_progress &&
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (status_box->toggle_button))) {
- pidgin_status_box_popdown (status_box);
+ pidgin_status_box_popdown(status_box, (GdkEvent *)event);
return TRUE;
} else if (ewidget == status_box->toggle_button) {
status_box->popup_in_progress = TRUE;
@@ -1634,7 +1627,7 @@ treeview_button_release_cb(GtkWidget *widget, GdkEventButton *event, PidginStatu
/* released outside treeview */
if (ewidget != status_box->toggle_button) {
- pidgin_status_box_popdown (status_box);
+ pidgin_status_box_popdown(status_box, (GdkEvent *)event);
return TRUE;
}
@@ -1649,7 +1642,7 @@ treeview_button_release_cb(GtkWidget *widget, GdkEventButton *event, PidginStatu
if (!ret)
return TRUE; /* clicked outside window? */
- treeview_activate_current_selection(status_box, path);
+ treeview_activate_current_selection(status_box, path, (GdkEvent *)event);
gtk_tree_path_free (path);
return TRUE;
@@ -1660,8 +1653,8 @@ treeview_key_press_event(GtkWidget *widget,
GdkEventKey *event, PidginStatusBox *box)
{
if (box->popup_in_progress) {
- if (event->keyval == GDK_Escape) {
- pidgin_status_box_popdown(box);
+ if (event->keyval == GDK_KEY_Escape) {
+ pidgin_status_box_popdown(box, (GdkEvent *)event);
return TRUE;
} else {
GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(box->tree_view));
@@ -1671,10 +1664,10 @@ treeview_key_press_event(GtkWidget *widget,
if (gtk_tree_selection_get_selected(sel, NULL, &iter)) {
gboolean ret = TRUE;
path = gtk_tree_model_get_path(GTK_TREE_MODEL(box->dropdown_store), &iter);
- if (event->keyval == GDK_Return) {
- treeview_activate_current_selection(box, path);
- } else if (event->keyval == GDK_Delete) {
- tree_view_delete_current_selection(box, path);
+ if (event->keyval == GDK_KEY_Return) {
+ treeview_activate_current_selection(box, path, (GdkEvent *)event);
+ } else if (event->keyval == GDK_KEY_Delete) {
+ tree_view_delete_current_selection(box, path, (GdkEvent *)event);
} else
ret = FALSE;
@@ -1687,14 +1680,13 @@ treeview_key_press_event(GtkWidget *widget,
}
static void
-imhtml_cursor_moved_cb(gpointer data, GtkMovementStep step, gint count, gboolean extend,
- GtkWidget *widget)
+webview_cursor_moved_cb(gpointer data, PidginWebView *webview)
{
/* Restart the typing timeout if arrow keys are pressed while editing the message */
PidginStatusBox *status_box = data;
if (status_box->typing == 0)
return;
- imhtml_changed_cb(NULL, status_box);
+ webview_changed_cb(NULL, status_box);
}
static void
@@ -1745,19 +1737,18 @@ pidgin_status_box_init (PidginStatusBox *status_box)
GtkCellRenderer *text_rend;
GtkCellRenderer *icon_rend;
GtkCellRenderer *emblem_rend;
- GtkTextBuffer *buffer;
GtkWidget *toplevel;
GtkTreeSelection *sel;
- GTK_WIDGET_SET_FLAGS (status_box, GTK_NO_WINDOW);
- status_box->imhtml_visible = FALSE;
+ gtk_widget_set_has_window(GTK_WIDGET(status_box), FALSE);
+ status_box->webview_visible = FALSE;
status_box->network_available = purple_network_is_available();
status_box->connecting = FALSE;
status_box->typing = 0;
status_box->toggle_button = gtk_toggle_button_new();
- status_box->hbox = gtk_hbox_new(FALSE, 6);
+ status_box->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
status_box->cell_view = gtk_cell_view_new();
- status_box->vsep = gtk_vseparator_new();
+ status_box->vsep = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
status_box->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
status_box->store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING,
@@ -1842,11 +1833,10 @@ pidgin_status_box_init (PidginStatusBox *status_box)
gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(status_box->cell_view), emblem_rend, "pixbuf", EMBLEM_COLUMN, "visible", EMBLEM_VISIBLE_COLUMN, NULL);
g_object_set(status_box->text_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
- status_box->vbox = gtk_vbox_new(0, FALSE);
- status_box->sw = pidgin_create_imhtml(FALSE, &status_box->imhtml, NULL, NULL);
- gtk_imhtml_set_editable(GTK_IMHTML(status_box->imhtml), TRUE);
+ status_box->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, FALSE);
+ status_box->sw = pidgin_create_webview(TRUE, &status_box->webview, NULL);
+ pidgin_webview_hide_toolbar(PIDGIN_WEBVIEW(status_box->webview));
- buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(status_box->imhtml));
#if 0
g_signal_connect(G_OBJECT(status_box->toggle_button), "button-press-event",
G_CALLBACK(button_pressed_cb), status_box);
@@ -1857,19 +1847,17 @@ pidgin_status_box_init (PidginStatusBox *status_box)
G_CALLBACK(toggle_key_press_cb), status_box);
g_signal_connect(G_OBJECT(status_box->toggle_button), "button-press-event",
G_CALLBACK(toggled_cb), status_box);
- g_signal_connect(G_OBJECT(buffer), "changed", G_CALLBACK(imhtml_changed_cb), status_box);
- g_signal_connect(G_OBJECT(status_box->imhtml), "format_function_toggle",
- G_CALLBACK(imhtml_format_changed_cb), status_box);
- g_signal_connect_swapped(G_OBJECT(status_box->imhtml), "move_cursor",
- G_CALLBACK(imhtml_cursor_moved_cb), status_box);
- g_signal_connect(G_OBJECT(status_box->imhtml), "key_press_event",
- G_CALLBACK(imhtml_remove_focus), status_box);
- g_signal_connect_swapped(G_OBJECT(status_box->imhtml), "message_send", G_CALLBACK(remove_typing_cb), status_box);
-
-#ifdef USE_GTKSPELL
+ g_signal_connect(G_OBJECT(status_box->webview), "changed",
+ G_CALLBACK(webview_changed_cb), status_box);
+ g_signal_connect(G_OBJECT(status_box->webview), "format-toggled",
+ G_CALLBACK(webview_format_changed_cb), status_box);
+ g_signal_connect_swapped(G_OBJECT(status_box->webview), "selection-changed",
+ G_CALLBACK(webview_cursor_moved_cb), status_box);
+ g_signal_connect(G_OBJECT(status_box->webview), "key-press-event",
+ G_CALLBACK(webview_remove_focus), status_box);
+
if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/spellcheck"))
- pidgin_setup_gtkspell(GTK_TEXT_VIEW(status_box->imhtml));
-#endif
+ pidgin_webview_set_spellcheck(PIDGIN_WEBVIEW(status_box->webview), TRUE);
gtk_widget_set_parent(status_box->vbox, GTK_WIDGET(status_box));
gtk_widget_show_all(status_box->vbox);
@@ -1877,9 +1865,9 @@ pidgin_status_box_init (PidginStatusBox *status_box)
gtk_box_pack_start(GTK_BOX(status_box->vbox), status_box->sw, TRUE, TRUE, 0);
- g_signal_connect(G_OBJECT(status_box), "scroll_event", G_CALLBACK(combo_box_scroll_event_cb), NULL);
- g_signal_connect(G_OBJECT(status_box->imhtml), "scroll_event",
- G_CALLBACK(imhtml_scroll_event_cb), status_box->imhtml);
+ g_signal_connect(G_OBJECT(status_box), "scroll-event", G_CALLBACK(combo_box_scroll_event_cb), NULL);
+ g_signal_connect(G_OBJECT(status_box->webview), "scroll-event",
+ G_CALLBACK(webview_scroll_event_cb), status_box->webview);
g_signal_connect(G_OBJECT(status_box->popup_window), "button_release_event", G_CALLBACK(treeview_button_release_cb), status_box);
g_signal_connect(G_OBJECT(status_box->popup_window), "key_press_event", G_CALLBACK(treeview_key_press_event), status_box);
g_signal_connect(G_OBJECT(status_box->tree_view), "cursor-changed",
@@ -1919,11 +1907,36 @@ pidgin_status_box_init (PidginStatusBox *status_box)
spellcheck_prefs_cb, status_box);
purple_prefs_connect_callback(status_box, PIDGIN_PREFS_ROOT "/accounts/buddyicon",
update_buddyicon_cb, status_box);
- purple_signal_connect(purple_get_core(), "uri-handler", status_box,
- PURPLE_CALLBACK(statusbox_uri_handler), status_box);
}
+#if GTK_CHECK_VERSION(3,0,0)
+static void
+pidgin_status_box_get_preferred_height(GtkWidget *widget, gint *minimum_height,
+ gint *natural_height)
+{
+ gint box_min_height, box_nat_height;
+ gint border_width = gtk_container_get_border_width(GTK_CONTAINER (widget));
+
+ gtk_widget_get_preferred_height(PIDGIN_STATUS_BOX(widget)->toggle_button,
+ minimum_height, natural_height);
+
+ *minimum_height = MAX(*minimum_height, 34) + border_width * 2;
+ *natural_height = MAX(*natural_height, 34) + border_width * 2;
+
+ /* If the gtkwebview is visible, then add some additional padding */
+ if (PIDGIN_STATUS_BOX(widget)->webview_visible) {
+ gtk_widget_get_preferred_height(PIDGIN_STATUS_BOX(widget)->vbox,
+ &box_min_height, &box_nat_height);
+
+ if (box_min_height > 1)
+ *minimum_height += box_min_height + border_width * 2;
+
+ if (box_nat_height > 1)
+ *natural_height += box_nat_height + border_width * 2;
+ }
+}
+#else
static void
pidgin_status_box_size_request(GtkWidget *widget,
GtkRequisition *requisition)
@@ -1937,13 +1950,16 @@ pidgin_status_box_size_request(GtkWidget *widget,
requisition->height = MAX(requisition->height, 34);
requisition->height += border_width * 2;
- /* If the gtkimhtml is visible, then add some additional padding */
- gtk_widget_size_request(PIDGIN_STATUS_BOX(widget)->vbox, &box_req);
- if (box_req.height > 1)
- requisition->height += box_req.height + border_width * 2;
+ /* If the gtkwebview is visible, then add some additional padding */
+ if (PIDGIN_STATUS_BOX(widget)->webview_visible) {
+ gtk_widget_size_request(PIDGIN_STATUS_BOX(widget)->vbox, &box_req);
+ if (box_req.height > 1)
+ requisition->height += box_req.height + border_width * 2;
+ }
requisition->width = 1;
}
+#endif
/* From gnome-panel */
static void
@@ -1992,9 +2008,9 @@ pidgin_status_box_size_allocate(GtkWidget *widget,
PidginStatusBox *status_box = PIDGIN_STATUS_BOX(widget);
GtkRequisition req = {0,0};
GtkAllocation parent_alc, box_alc, icon_alc;
- gint border_width = GTK_CONTAINER (widget)->border_width;
+ gint border_width = gtk_container_get_border_width(GTK_CONTAINER (widget));
- gtk_widget_size_request(status_box->toggle_button, &req);
+ gtk_widget_get_preferred_size(status_box->toggle_button, NULL, &req);
/* Make this icon the same size as other buddy icons in the list; unless it already wants to be bigger */
req.height = MAX(req.height, 34);
@@ -2031,9 +2047,28 @@ pidgin_status_box_size_allocate(GtkWidget *widget,
gtk_widget_size_allocate(status_box->icon_box, &icon_alc);
}
gtk_widget_size_allocate(status_box->toggle_button, &parent_alc);
- widget->allocation = *allocation;
+ gtk_widget_set_allocation(GTK_WIDGET(status_box), allocation);
}
+#if GTK_CHECK_VERSION(3,0,0)
+static gboolean
+pidgin_status_box_draw(GtkWidget *widget, cairo_t *cr)
+{
+ PidginStatusBox *status_box = PIDGIN_STATUS_BOX(widget);
+ gtk_widget_draw(status_box->toggle_button, cr);
+
+ if (status_box->icon_box && status_box->icon_opaque) {
+ GtkAllocation allocation;
+ GtkStyleContext *context;
+
+ gtk_widget_get_allocation(status_box->icon_box, &allocation);
+ context = gtk_widget_get_style_context(widget);
+ gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON);
+ gtk_render_frame(context, cr, allocation.x-1, allocation.y-1, 34, 34);
+ }
+ return FALSE;
+}
+#else
static gboolean
pidgin_status_box_expose_event(GtkWidget *widget,
GdkEventExpose *event)
@@ -2048,6 +2083,7 @@ pidgin_status_box_expose_event(GtkWidget *widget,
}
return FALSE;
}
+#endif
static void
pidgin_status_box_forall(GtkContainer *container,
@@ -2081,26 +2117,27 @@ pidgin_status_box_new_with_account(PurpleAccount *account)
"iconsel", TRUE, NULL);
}
-/**
- * Add a row to the dropdown menu.
- *
- * @param status_box The status box itself.
- * @param type A PidginStatusBoxItemType.
- * @param pixbuf The icon to associate with this row in the menu. The
+/*
+ * pidgin_status_box_add:
+ * @status_box: The status box itself.
+ * @type: A PidginStatusBoxItemType.
+ * @pixbuf: The icon to associate with this row in the menu. The
* function will try to decide a pixbuf if none is given.
- * @param title The title of this item. For the primitive entries,
+ * @title: The title of this item. For the primitive entries,
* this is something like "Available" or "Away." For
* the saved statuses, this is something like
* "My favorite away message!" This should be
* plaintext (non-markedup) (this function escapes it).
- * @param desc The secondary text for this item. This will be
+ * @desc: The secondary text for this item. This will be
* placed on the row below the title, in a dimmer
* font (generally gray). This text should be plaintext
* (non-markedup) (this function escapes it).
- * @param data Data to be associated with this row in the dropdown
+ * @data: Data to be associated with this row in the dropdown
* menu. For primitives this is the value of the
* PurpleStatusPrimitive. For saved statuses this is the
* creation timestamp.
+ *
+ * Add a row to the dropdown menu.
*/
void
pidgin_status_box_add(PidginStatusBox *status_box, PidginStatusBoxItemType type, GdkPixbuf *pixbuf,
@@ -2143,7 +2180,7 @@ pidgin_status_box_add(PidginStatusBox *status_box, PidginStatusBoxItemType type,
type == PIDGIN_STATUS_BOX_TYPE_POPULAR) {
PurpleSavedStatus *saved = purple_savedstatus_find_by_creation_time(GPOINTER_TO_INT(data));
if (saved) {
- prim = purple_savedstatus_get_type(saved);
+ prim = purple_savedstatus_get_primitive_type(saved);
}
}
@@ -2232,21 +2269,23 @@ pidgin_status_box_redisplay_buddy_icon(PidginStatusBox *status_box)
g_signal_connect(G_OBJECT(loader), "size-prepared", G_CALLBACK(pixbuf_size_prepared_cb), NULL);
if (!gdk_pixbuf_loader_write(loader,
- purple_imgstore_get_data(status_box->buddy_icon_img),
- purple_imgstore_get_size(status_box->buddy_icon_img),
+ purple_image_get_data(status_box->buddy_icon_img),
+ purple_image_get_size(status_box->buddy_icon_img),
&error) || error)
{
- purple_debug_warning("gtkstatusbox", "gdk_pixbuf_loader_write() "
- "failed with size=%zu: %s\n",
- purple_imgstore_get_size(status_box->buddy_icon_img),
- error ? error->message : "(no error message)");
+ purple_debug_warning("gtkstatusbox",
+ "gdk_pixbuf_loader_write() failed with size=%"
+ G_GSIZE_FORMAT ": %s", purple_image_get_size(
+ status_box->buddy_icon_img),
+ error ? error->message : "(no error message)");
if (error)
g_error_free(error);
} else if (!gdk_pixbuf_loader_close(loader, &error) || error) {
- purple_debug_warning("gtkstatusbox", "gdk_pixbuf_loader_close() "
- "failed for image of size %zu: %s\n",
- purple_imgstore_get_size(status_box->buddy_icon_img),
- error ? error->message : "(no error message)");
+ purple_debug_warning("gtkstatusbox",
+ "gdk_pixbuf_loader_close() failed for image of "
+ "size %" G_GSIZE_FORMAT ": %s",
+ purple_image_get_size(status_box->buddy_icon_img),
+ error ? error->message : "(no error message)");
if (error)
g_error_free(error);
} else {
@@ -2286,12 +2325,13 @@ pidgin_status_box_redisplay_buddy_icon(PidginStatusBox *status_box)
}
void
-pidgin_status_box_set_buddy_icon(PidginStatusBox *status_box, PurpleStoredImage *img)
+pidgin_status_box_set_buddy_icon(PidginStatusBox *status_box, PurpleImage *img)
{
- purple_imgstore_unref(status_box->buddy_icon_img);
+ if (status_box->buddy_icon_img)
+ g_object_unref(status_box->buddy_icon_img);
status_box->buddy_icon_img = img;
if (status_box->buddy_icon_img != NULL)
- purple_imgstore_ref(status_box->buddy_icon_img);
+ g_object_ref(status_box->buddy_icon_img);
pidgin_status_box_redisplay_buddy_icon(status_box);
}
@@ -2353,8 +2393,8 @@ activate_currently_selected_status(PidginStatusBox *status_box)
message = pidgin_status_box_get_message(status_box);
if (!message || !*message)
{
- gtk_widget_hide_all(status_box->vbox);
- status_box->imhtml_visible = FALSE;
+ gtk_widget_hide(status_box->vbox);
+ status_box->webview_visible = FALSE;
if (message != NULL)
{
g_free(message);
@@ -2391,14 +2431,14 @@ activate_currently_selected_status(PidginStatusBox *status_box)
/* Make sure that statusbox displays the correct thing.
* It can get messed up if the previous selection was a
* saved status that wasn't supported by this account */
- if ((purple_savedstatus_get_type(ss) == primitive)
+ if ((purple_savedstatus_get_primitive_type(ss) == primitive)
&& purple_savedstatus_is_transient(ss)
&& purple_savedstatus_has_substatuses(ss))
changed = FALSE;
}
} else {
saved_status = purple_savedstatus_get_current();
- if (purple_savedstatus_get_type(saved_status) == primitive &&
+ if (purple_savedstatus_get_primitive_type(saved_status) == primitive &&
!purple_savedstatus_has_substatuses(saved_status) &&
purple_strequal(purple_savedstatus_get_message(saved_status), message))
{
@@ -2418,7 +2458,7 @@ activate_currently_selected_status(PidginStatusBox *status_box)
const char *ss_msg = purple_savedstatus_get_message(ss);
/* find a known transient status that is the same as the
* new selected one */
- if ((purple_savedstatus_get_type(ss) == primitive) && purple_savedstatus_is_transient(ss) &&
+ if ((purple_savedstatus_get_primitive_type(ss) == primitive) && purple_savedstatus_is_transient(ss) &&
purple_savedstatus_has_substatuses(ss) && /* Must have substatuses */
purple_strequal(ss_msg, message))
{
@@ -2428,7 +2468,8 @@ activate_currently_selected_status(PidginStatusBox *status_box)
PurpleAccount *acct = tmp->data;
PurpleSavedStatusSub *sub = purple_savedstatus_get_substatus(ss, acct);
if (sub) {
- const PurpleStatusType *sub_type = purple_savedstatus_substatus_get_type(sub);
+ const PurpleStatusType *sub_type =
+ purple_savedstatus_substatus_get_status_type(sub);
const char *subtype_status_id = purple_status_type_get_id(sub_type);
if (purple_strequal(subtype_status_id, id)) {
found = TRUE;
@@ -2512,6 +2553,8 @@ activate_currently_selected_status(PidginStatusBox *status_box)
static void update_size(PidginStatusBox *status_box)
{
+#if 0
+ /* TODO WebKit Sizing */
GtkTextBuffer *buffer;
GtkTextIter iter;
int display_lines;
@@ -2521,25 +2564,28 @@ static void update_size(PidginStatusBox *status_box)
int pad_top, pad_inside, pad_bottom;
gboolean interior_focus;
int focus_width;
+#endif
- if (!status_box->imhtml_visible)
+ if (!status_box->webview_visible)
{
if (status_box->vbox != NULL)
gtk_widget_set_size_request(status_box->vbox, -1, -1);
return;
}
- buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(status_box->imhtml));
+#if 0
+ /* TODO WebKit: Entry sizing */
+ buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(status_box->webview));
height = 0;
display_lines = 1;
gtk_text_buffer_get_start_iter(buffer, &iter);
do {
- gtk_text_view_get_iter_location(GTK_TEXT_VIEW(status_box->imhtml), &iter, &oneline);
+ gtk_text_view_get_iter_location(GTK_TEXT_VIEW(status_box->webview), &iter, &oneline);
height += oneline.height;
display_lines++;
} while (display_lines <= 4 &&
- gtk_text_view_forward_display_line(GTK_TEXT_VIEW(status_box->imhtml), &iter));
+ gtk_text_view_forward_display_line(GTK_TEXT_VIEW(status_box->webview), &iter));
/*
* This check fixes the case where the last character entered is a
@@ -2553,7 +2599,7 @@ static void update_size(PidginStatusBox *status_box)
&& gtk_text_iter_backward_char(&iter)
&& gtk_text_iter_get_char(&iter) == '\n')
{
- gtk_text_view_get_iter_location(GTK_TEXT_VIEW(status_box->imhtml), &iter, &oneline);
+ gtk_text_view_get_iter_location(GTK_TEXT_VIEW(status_box->webview), &iter, &oneline);
height += oneline.height;
display_lines++;
}
@@ -2564,14 +2610,14 @@ static void update_size(PidginStatusBox *status_box)
lines = MIN(lines, 4);
display_lines = MIN(display_lines, 4);
- pad_top = gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(status_box->imhtml));
- pad_bottom = gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(status_box->imhtml));
- pad_inside = gtk_text_view_get_pixels_inside_wrap(GTK_TEXT_VIEW(status_box->imhtml));
+ pad_top = gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(status_box->webview));
+ pad_bottom = gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(status_box->webview));
+ pad_inside = gtk_text_view_get_pixels_inside_wrap(GTK_TEXT_VIEW(status_box->webview));
height += (pad_top + pad_bottom) * lines;
height += (pad_inside) * (display_lines - lines);
- gtk_widget_style_get(status_box->imhtml,
+ gtk_widget_style_get(status_box->webview,
"interior-focus", &interior_focus,
"focus-line-width", &focus_width,
NULL);
@@ -2579,6 +2625,8 @@ static void update_size(PidginStatusBox *status_box)
height += 2 * focus_width;
gtk_widget_set_size_request(status_box->vbox, -1, height + PIDGIN_HIG_BOX_SPACE);
+#endif
+ gtk_widget_set_size_request(status_box->vbox, -1, -1);
}
static void remove_typing_cb(PidginStatusBox *status_box)
@@ -2590,8 +2638,11 @@ static void remove_typing_cb(PidginStatusBox *status_box)
return;
}
- gtk_imhtml_set_populate_primary_clipboard(
- GTK_IMHTML(status_box->imhtml), TRUE);
+#if 0
+ /* TODO WebKit: Doesn't do this? */
+ pidgin_webview_set_populate_primary_clipboard(
+ PIDGIN_WEBVIEW(status_box->webview), TRUE);
+#endif
purple_timeout_remove(status_box->typing);
status_box->typing = 0;
@@ -2625,7 +2676,7 @@ static void pidgin_status_box_changed(PidginStatusBox *status_box)
purple_timeout_remove(status_box->typing);
status_box->typing = 0;
- if (GTK_WIDGET_IS_SENSITIVE(GTK_WIDGET(status_box)))
+ if (gtk_widget_get_sensitive(GTK_WIDGET(status_box)))
{
if (type == PIDGIN_STATUS_BOX_TYPE_POPULAR || type == PIDGIN_STATUS_BOX_TYPE_SAVED_POPULAR)
{
@@ -2640,7 +2691,7 @@ static void pidgin_status_box_changed(PidginStatusBox *status_box)
{
PurpleSavedStatus *saved_status;
saved_status = purple_savedstatus_get_current();
- if (purple_savedstatus_get_type(saved_status) == PURPLE_STATUS_AVAILABLE)
+ if (purple_savedstatus_get_primitive_type(saved_status) == PURPLE_STATUS_AVAILABLE)
saved_status = purple_savedstatus_new(NULL, PURPLE_STATUS_AWAY);
pidgin_status_editor_show(FALSE,
purple_savedstatus_is_transient(saved_status)
@@ -2670,7 +2721,7 @@ static void pidgin_status_box_changed(PidginStatusBox *status_box)
accounts = g_list_prepend(accounts, status_box->account);
else
accounts = purple_accounts_get_all_active();
- status_box->imhtml_visible = FALSE;
+ status_box->webview_visible = FALSE;
for (node = accounts; node != NULL; node = node->next)
{
PurpleAccount *account;
@@ -2681,33 +2732,30 @@ static void pidgin_status_box_changed(PidginStatusBox *status_box)
if ((status_type != NULL) &&
(purple_status_type_get_attr(status_type, "message") != NULL))
{
- status_box->imhtml_visible = TRUE;
+ status_box->webview_visible = TRUE;
break;
}
}
g_list_free(accounts);
- if (GTK_WIDGET_IS_SENSITIVE(GTK_WIDGET(status_box)))
+ if (gtk_widget_get_sensitive(GTK_WIDGET(status_box)))
{
- if (status_box->imhtml_visible)
+ if (status_box->webview_visible)
{
- GtkTextIter start, end;
- GtkTextBuffer *buffer;
gtk_widget_show_all(status_box->vbox);
status_box->typing = purple_timeout_add_seconds(TYPING_TIMEOUT, (GSourceFunc)remove_typing_cb, status_box);
- gtk_widget_grab_focus(status_box->imhtml);
- buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(status_box->imhtml));
-
- gtk_imhtml_set_populate_primary_clipboard(
- GTK_IMHTML(status_box->imhtml), FALSE);
+ gtk_widget_grab_focus(status_box->webview);
+#if 0
+ /* TODO WebKit: Doesn't do this? */
+ pidgin_webview_set_populate_primary_clipboard(
+ PIDGIN_WEBVIEW(status_box->webview), FALSE);
+#endif
- gtk_text_buffer_get_bounds(buffer, &start, &end);
- gtk_text_buffer_move_mark(buffer, gtk_text_buffer_get_mark(buffer, "insert"), &end);
- gtk_text_buffer_move_mark(buffer, gtk_text_buffer_get_mark(buffer, "selection_bound"), &start);
+ webkit_web_view_select_all(WEBKIT_WEB_VIEW(status_box->webview));
}
else
{
- gtk_widget_hide_all(status_box->vbox);
+ gtk_widget_hide(status_box->vbox);
activate_currently_selected_status(status_box); /* This is where we actually set the status */
}
}
@@ -2719,7 +2767,7 @@ get_statusbox_index(PidginStatusBox *box, PurpleSavedStatus *saved_status)
{
gint index = -1;
- switch (purple_savedstatus_get_type(saved_status))
+ switch (purple_savedstatus_get_primitive_type(saved_status))
{
/* In reverse order */
case PURPLE_STATUS_OFFLINE:
@@ -2744,10 +2792,11 @@ get_statusbox_index(PidginStatusBox *box, PurpleSavedStatus *saved_status)
return index;
}
-static void imhtml_changed_cb(GtkTextBuffer *buffer, void *data)
+static void
+webview_changed_cb(PidginWebView *webview, void *data)
{
PidginStatusBox *status_box = (PidginStatusBox*)data;
- if (GTK_WIDGET_IS_SENSITIVE(GTK_WIDGET(status_box)))
+ if (gtk_widget_get_sensitive(GTK_WIDGET(status_box)))
{
if (status_box->typing != 0) {
pidgin_status_box_pulse_typing(status_box);
@@ -2758,15 +2807,16 @@ static void imhtml_changed_cb(GtkTextBuffer *buffer, void *data)
pidgin_status_box_refresh(status_box);
}
-static void imhtml_format_changed_cb(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons, void *data)
+static void
+webview_format_changed_cb(PidginWebView *webview, PidginWebViewButtons buttons, void *data)
{
- imhtml_changed_cb(NULL, data);
+ webview_changed_cb(NULL, data);
}
char *pidgin_status_box_get_message(PidginStatusBox *status_box)
{
- if (status_box->imhtml_visible)
- return gtk_imhtml_get_markup(GTK_IMHTML(status_box->imhtml));
+ if (status_box->webview_visible)
+ return g_strstrip(pidgin_webview_get_body_text(PIDGIN_WEBVIEW(status_box->webview)));
else
return NULL;
}
diff --git a/pidgin/gtkstatusbox.h b/pidgin/gtkstatusbox.h
index 37b536788f..62f7909d60 100644
--- a/pidgin/gtkstatusbox.h
+++ b/pidgin/gtkstatusbox.h
@@ -1,8 +1,3 @@
-/*
- * @file gtkstatusbox.c GTK+ Status Selection Widget
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -24,14 +19,18 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-
#ifndef __PIDGIN_STATUS_BOX_H__
#define __PIDGIN_STATUS_BOX_H__
+/**
+ * SECTION:gtkstatusbox
+ * @section_id: pidgin-gtkstatusbox
+ * @short_description: <filename>gtkstatusbox.h</filename>
+ * @title: Status Selection Widget
+ */
#include <gtk/gtk.h>
-#include "gtkimhtml.h"
+#include "gtkwebview.h"
#include "account.h"
-#include "imgstore.h"
#include "savedstatuses.h"
#include "status.h"
@@ -45,6 +44,8 @@ G_BEGIN_DECLS
#define PIDGIN_STATUS_BOX_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), PIDGIN_TYPE_STATUS_BOX, PidginStatusBoxClass))
/**
+ * PidginStatusBoxItemType:
+ *
* This is a hidden field in the GtkStatusBox that identifies the
* item in the list store. The item could be a normal
* PurpleStatusPrimitive, or it could be something special like the
@@ -64,32 +65,32 @@ typedef enum
typedef struct _PidginStatusBox PidginStatusBox;
typedef struct _PidginStatusBoxClass PidginStatusBoxClass;
+/**
+ * PidginStatusBox:
+ * @store: This GtkListStore contains only one row--the currently
+ * selected status.
+ * @dropdown_store: This is the dropdown GtkListStore that contains the
+ * available statuses, plus some recently used statuses, plus
+ * the "Custom..." and "Saved..." options.
+ * @token_status_account: This will be non-NULL and contain a sample account
+ * when all enabled accounts use the same statuses
+ */
struct _PidginStatusBox
{
GtkContainer parent_instance;
- /**
- * This GtkListStore contains only one row--the currently selected status.
- */
+ /*< public >*/
GtkListStore *store;
-
- /**
- * This is the dropdown GtkListStore that contains the available statuses,
- * plus some recently used statuses, plus the "Custom..." and "Saved..."
- * options.
- */
GtkListStore *dropdown_store;
PurpleAccount *account;
- /* This will be non-NULL and contain a sample account
- * when all enabled accounts use the same statuses */
PurpleAccount *token_status_account;
GtkWidget *vbox, *sw;
- GtkWidget *imhtml;
+ GtkWidget *webview;
- PurpleStoredImage *buddy_icon_img;
+ PurpleImage *buddy_icon_img;
GdkPixbuf *buddy_icon;
GdkPixbuf *buddy_icon_hover;
GtkWidget *buddy_icon_sel;
@@ -98,10 +99,10 @@ struct _PidginStatusBox
GtkWidget *icon_box_menu;
GdkCursor *hand_cursor;
GdkCursor *arrow_cursor;
- int icon_size;
- gboolean icon_opaque;
+ int icon_size;
+ gboolean icon_opaque;
- gboolean imhtml_visible;
+ gboolean webview_visible;
GtkWidget *cell_view;
GtkCellRenderer *icon_rend;
@@ -148,7 +149,7 @@ struct _PidginStatusBoxClass
/* signals */
void (* changed) (GtkComboBox *combo_box);
- /* Padding for future expansion */
+ /*< private >*/
void (*_gtk_reserved0) (void);
void (*_gtk_reserved1) (void);
void (*_gtk_reserved2) (void);
@@ -176,7 +177,7 @@ void
pidgin_status_box_pulse_connecting(PidginStatusBox *status_box);
void
-pidgin_status_box_set_buddy_icon(PidginStatusBox *status_box, PurpleStoredImage *img);
+pidgin_status_box_set_buddy_icon(PidginStatusBox *status_box, PurpleImage *img);
char *pidgin_status_box_get_message(PidginStatusBox *status_box);
diff --git a/pidgin/gtkthemes.c b/pidgin/gtkthemes.c
deleted file mode 100644
index f1102e22d9..0000000000
--- a/pidgin/gtkthemes.c
+++ /dev/null
@@ -1,475 +0,0 @@
-/*
- * 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 "pidgin.h"
-
-#include "conversation.h"
-#include "debug.h"
-#include "prpl.h"
-#include "util.h"
-
-#include "gtkconv.h"
-#include "gtkdialogs.h"
-#include "gtkimhtml.h"
-#include "gtksmiley.h"
-#include "gtkthemes.h"
-
-GSList *smiley_themes = NULL;
-struct smiley_theme *current_smiley_theme;
-
-static void pidgin_themes_destroy_smiley_theme_smileys(struct smiley_theme *theme);
-
-gboolean pidgin_themes_smileys_disabled()
-{
- if (!current_smiley_theme)
- return 1;
-
- return strcmp(current_smiley_theme->name, "none") == 0;
-}
-
-static void
-pidgin_themes_destroy_smiley_theme(struct smiley_theme *theme)
-{
- pidgin_themes_destroy_smiley_theme_smileys(theme);
-
- g_free(theme->name);
- g_free(theme->desc);
- g_free(theme->author);
- g_free(theme->icon);
- g_free(theme->path);
- g_free(theme);
-}
-
-static void pidgin_themes_remove_theme_dir(const char *theme_dir_name)
-{
- GString *str = NULL;
- const char *file_name = NULL;
- GDir *theme_dir = NULL;
-
- if ((theme_dir = g_dir_open(theme_dir_name, 0, NULL)) != NULL) {
- if ((str = g_string_new(theme_dir_name)) != NULL) {
- while ((file_name = g_dir_read_name(theme_dir)) != NULL) {
- g_string_printf(str, "%s%s%s", theme_dir_name, G_DIR_SEPARATOR_S, file_name);
- g_unlink(str->str);
- }
- g_string_free(str, TRUE);
- }
- g_dir_close(theme_dir);
- g_rmdir(theme_dir_name);
- }
-}
-
-void pidgin_themes_remove_smiley_theme(const char *file)
-{
- char *theme_dir = NULL, *last_slash = NULL;
- g_return_if_fail(NULL != file);
-
- if (!g_file_test(file, G_FILE_TEST_EXISTS)) return;
- if ((theme_dir = g_strdup(file)) == NULL) return ;
-
- if ((last_slash = g_strrstr(theme_dir, G_DIR_SEPARATOR_S)) != NULL) {
- GSList *iter = NULL;
- struct smiley_theme *theme = NULL, *new_theme = NULL;
-
- *last_slash = 0;
-
- /* Delete files on disk */
- pidgin_themes_remove_theme_dir(theme_dir);
-
- /* Find theme in themes list and remove it */
- for (iter = smiley_themes ; iter ; iter = iter->next) {
- theme = ((struct smiley_theme *)(iter->data));
- if (!strcmp(theme->path, file))
- break ;
- }
- if (iter) {
- if (theme == current_smiley_theme) {
- new_theme = ((struct smiley_theme *)(NULL == iter->next ? (smiley_themes == iter ? NULL : smiley_themes->data) : iter->next->data));
- if (new_theme)
- purple_prefs_set_string(PIDGIN_PREFS_ROOT "/smileys/theme", new_theme->name);
- else
- current_smiley_theme = NULL;
- }
- smiley_themes = g_slist_delete_link(smiley_themes, iter);
-
- /* Destroy theme structure */
- pidgin_themes_destroy_smiley_theme(theme);
- }
- }
-
- g_free(theme_dir);
-}
-
-static void _pidgin_themes_smiley_themeize(GtkWidget *imhtml, gboolean custom)
-{
- struct smiley_list *list;
- if (!current_smiley_theme)
- return;
-
- gtk_imhtml_remove_smileys(GTK_IMHTML(imhtml));
- list = current_smiley_theme->list;
- while (list) {
- char *sml = !strcmp(list->sml, "default") ? NULL : list->sml;
- GSList *icons = list->smileys;
- while (icons) {
- gtk_imhtml_associate_smiley(GTK_IMHTML(imhtml), sml, icons->data);
- icons = icons->next;
- }
-
- if (custom == TRUE) {
- icons = pidgin_smileys_get_all();
-
- while (icons) {
- gtk_imhtml_associate_smiley(GTK_IMHTML(imhtml), sml, icons->data);
- icons = icons->next;
- }
- }
-
- list = list->next;
- }
-}
-
-void pidgin_themes_smiley_themeize(GtkWidget *imhtml)
-{
- _pidgin_themes_smiley_themeize(imhtml, FALSE);
-}
-
-void pidgin_themes_smiley_themeize_custom(GtkWidget *imhtml)
-{
- _pidgin_themes_smiley_themeize(imhtml, TRUE);
-}
-
-static void
-pidgin_themes_destroy_smiley_theme_smileys(struct smiley_theme *theme)
-{
- GHashTable *already_freed;
- struct smiley_list *wer;
-
- already_freed = g_hash_table_new(g_direct_hash, g_direct_equal);
- for (wer = theme->list; wer != NULL; wer = theme->list) {
- while (wer->smileys) {
- GtkIMHtmlSmiley *uio = wer->smileys->data;
-
- if (uio->imhtml) {
- g_signal_handlers_disconnect_matched(uio->imhtml, G_SIGNAL_MATCH_DATA,
- 0, 0, NULL, NULL, uio);
- }
-
- if (uio->icon)
- g_object_unref(uio->icon);
- if (g_hash_table_lookup(already_freed, uio->file) == NULL) {
- g_free(uio->file);
- g_hash_table_insert(already_freed, uio->file, GINT_TO_POINTER(1));
- }
- g_free(uio->smile);
- g_free(uio);
- wer->smileys = g_slist_delete_link(wer->smileys, wer->smileys);
- }
- theme->list = wer->next;
- g_free(wer->sml);
- g_free(wer);
- }
- theme->list = NULL;
-
- g_hash_table_destroy(already_freed);
-}
-
-static void
-pidgin_smiley_themes_remove_non_existing(void)
-{
- static struct smiley_theme *theme = NULL;
- GSList *iter = NULL;
-
- if (!smiley_themes) return ;
-
- for (iter = smiley_themes ; iter ; iter = iter->next) {
- theme = ((struct smiley_theme *)(iter->data));
- if (!g_file_test(theme->path, G_FILE_TEST_EXISTS)) {
- if (theme == current_smiley_theme)
- current_smiley_theme = ((struct smiley_theme *)(NULL == iter->next ? NULL : iter->next->data));
- pidgin_themes_destroy_smiley_theme(theme);
- iter->data = NULL;
- }
- }
- /* Remove all elements whose data is NULL */
- smiley_themes = g_slist_remove_all(smiley_themes, NULL);
-
- if (!current_smiley_theme && smiley_themes) {
- struct smiley_theme *smile = g_slist_last(smiley_themes)->data;
- pidgin_themes_load_smiley_theme(smile->path, TRUE);
- }
-}
-
-void pidgin_themes_load_smiley_theme(const char *file, gboolean load)
-{
- FILE *f = g_fopen(file, "rb");
- char buf[256];
- char *i;
- gsize line_nbr = 0;
- struct smiley_theme *theme=NULL;
- struct smiley_list *list = NULL;
- GSList *lst = smiley_themes;
- char *dirname;
-
- if (!f)
- return;
-
- while (lst) {
- struct smiley_theme *thm = lst->data;
- if (!strcmp(thm->path, file)) {
- theme = thm;
- break;
- }
- lst = lst->next;
- }
-
- if (theme != NULL && theme == current_smiley_theme) {
- /* Don't reload the theme if it is already loaded */
- fclose(f);
- return;
- }
-
- if (theme == NULL) {
- theme = g_new0(struct smiley_theme, 1);
- theme->path = g_strdup(file);
- smiley_themes = g_slist_prepend(smiley_themes, theme);
- }
-
- dirname = g_path_get_dirname(file);
-
- while (!feof(f)) {
- if (!fgets(buf, sizeof(buf), f)) {
- break;
- }
- line_nbr++;
-
- if (buf[0] == '#' || buf[0] == '\0')
- continue;
- else {
- int len = strlen(buf);
- while (len && (buf[len - 1] == '\r' || buf[len - 1] == '\n'))
- buf[--len] = '\0';
- if (len == 0)
- continue;
- }
-
- if (! g_utf8_validate(buf, -1, NULL)) {
- purple_debug_error("gtkthemes", "%s:%" G_GSIZE_FORMAT " is invalid UTF-8\n", file, line_nbr);
- continue;
- }
-
- i = buf;
- while (isspace(*i))
- i++;
-
- if (*i == '[' && strchr(i, ']') && load) {
- struct smiley_list *child = g_new0(struct smiley_list, 1);
- child->sml = g_strndup(i+1, strchr(i, ']') - i - 1);
- if (list)
- list->next = child;
- else
- theme->list = child;
- /* Reverse the Smiley list since it was built in reverse order for efficiency reasons */
- if (list != NULL)
- list->smileys = g_slist_reverse(list->smileys);
- list = child;
- } else if (!g_ascii_strncasecmp(i, "Name=", strlen("Name="))) {
- g_free(theme->name);
- theme->name = g_strdup(i + strlen("Name="));
- } else if (!g_ascii_strncasecmp(i, "Description=", strlen("Description="))) {
- g_free(theme->desc);
- theme->desc = g_strdup(i + strlen("Description="));
- } else if (!g_ascii_strncasecmp(i, "Icon=", strlen("Icon="))) {
- g_free(theme->icon);
- theme->icon = g_build_filename(dirname, i + strlen("Icon="), NULL);
- } else if (!g_ascii_strncasecmp(i, "Author=", strlen("Author="))) {
- g_free(theme->author);
- theme->author = g_strdup(i + strlen("Author="));
- } else if (load && list) {
- gboolean hidden = FALSE;
- char *sfile = NULL;
-
- if (*i == '!' && *(i + 1) == ' ') {
- hidden = TRUE;
- i = i + 2;
- }
- while (*i) {
- char l[64];
- int li = 0;
- char *next;
- while (*i && !isspace(*i) && li < sizeof(l) - 1) {
- if (*i == '\\' && *(i+1) != '\0')
- i++;
- /* coverity[tainted_data] */
- next = g_utf8_next_char(i);
- if ((next - i) > (sizeof(l) - li -1)) {
- break;
- }
- while (i != next)
- l[li++] = *(i++);
- }
- l[li] = 0;
- if (!sfile) {
- sfile = g_build_filename(dirname, l, NULL);
- } else {
- GtkIMHtmlSmiley *smiley = gtk_imhtml_smiley_create(sfile, l, hidden, 0);
- list->smileys = g_slist_prepend(list->smileys, smiley);
- }
- while (isspace(*i))
- i++;
-
- }
-
-
- g_free(sfile);
- }
- }
-
- /* Reverse the Smiley list since it was built in reverse order for efficiency reasons */
- if (list != NULL)
- list->smileys = g_slist_reverse(list->smileys);
-
- g_free(dirname);
- fclose(f);
-
- if (!theme->name || !theme->desc || !theme->author) {
- purple_debug_error("gtkthemes", "Invalid file format, not loading smiley theme from '%s'\n", file);
-
- smiley_themes = g_slist_remove(smiley_themes, theme);
- pidgin_themes_destroy_smiley_theme(theme);
- return;
- }
-
- if (load) {
- GList *cnv;
-
- if (current_smiley_theme)
- pidgin_themes_destroy_smiley_theme_smileys(current_smiley_theme);
- current_smiley_theme = theme;
-
- for (cnv = purple_get_conversations(); cnv != NULL; cnv = cnv->next) {
- PurpleConversation *conv = cnv->data;
-
- if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) {
- /* We want to see our custom smileys on our entry if we write the shortcut */
- pidgin_themes_smiley_themeize(PIDGIN_CONVERSATION(conv)->imhtml);
- pidgin_themes_smiley_themeize_custom(PIDGIN_CONVERSATION(conv)->entry);
- }
- }
- }
-}
-
-void pidgin_themes_smiley_theme_probe()
-{
- GDir *dir;
- const gchar *file;
- gchar *path, *test_path;
- int l;
- char* probedirs[3];
-
- pidgin_smiley_themes_remove_non_existing();
-
- probedirs[0] = g_build_filename(DATADIR, "pixmaps", "pidgin", "emotes", NULL);
- probedirs[1] = g_build_filename(purple_user_dir(), "smileys", NULL);
- probedirs[2] = 0;
- for (l=0; probedirs[l]; l++) {
- dir = g_dir_open(probedirs[l], 0, NULL);
- if (dir) {
- while ((file = g_dir_read_name(dir))) {
- test_path = g_build_filename(probedirs[l], file, NULL);
- if (g_file_test(test_path, G_FILE_TEST_IS_DIR)) {
- path = g_build_filename(probedirs[l], file, "theme", NULL);
-
- /* Here we check to see that the theme has proper syntax.
- * We set the second argument to FALSE so that it doesn't load
- * the theme yet.
- */
- pidgin_themes_load_smiley_theme(path, FALSE);
- g_free(path);
- }
- g_free(test_path);
- }
- g_dir_close(dir);
- } else if (l == 1) {
- if (g_mkdir(probedirs[l], S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
- purple_debug_error("gtkthemes",
- "couldn't create smileys dir\n");
- }
- }
- g_free(probedirs[l]);
- }
-
- if (!current_smiley_theme && smiley_themes) {
- struct smiley_theme *smile = smiley_themes->data;
- pidgin_themes_load_smiley_theme(smile->path, TRUE);
- }
-}
-
-GSList *pidgin_themes_get_proto_smileys(const char *id) {
- PurplePlugin *proto;
- struct smiley_list *list, *def;
-
- if ((current_smiley_theme == NULL) || (current_smiley_theme->list == NULL))
- return NULL;
-
- def = list = current_smiley_theme->list;
-
- if (id == NULL)
- return def->smileys;
-
- proto = purple_find_prpl(id);
-
- while (list) {
- if (!strcmp(list->sml, "default"))
- def = list;
- else if (proto && !strcmp(proto->info->name, list->sml))
- break;
-
- list = list->next;
- }
-
- return list ? list->smileys : def->smileys;
-}
-
-void pidgin_themes_init()
-{
- GSList *l;
- const char *current_theme =
- purple_prefs_get_string(PIDGIN_PREFS_ROOT "/smileys/theme");
-
- pidgin_themes_smiley_theme_probe();
-
- for (l = smiley_themes; l; l = l->next) {
- struct smiley_theme *smile = l->data;
- if (smile->name && strcmp(current_theme, smile->name) == 0) {
- pidgin_themes_load_smiley_theme(smile->path, TRUE);
- break;
- }
- }
-
- /* If we still don't have a smiley theme, choose the first one */
- if (!current_smiley_theme && smiley_themes) {
- struct smiley_theme *smile = smiley_themes->data;
- pidgin_themes_load_smiley_theme(smile->path, TRUE);
- }
-}
diff --git a/pidgin/gtkthemes.h b/pidgin/gtkthemes.h
deleted file mode 100644
index 138edbbeb0..0000000000
--- a/pidgin/gtkthemes.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * @file gtkthemes.h GTK+ Smiley Theme API
- * @ingroup pidgin
- */
-
-/* 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 _PIDGINTHEMES_H_
-#define _PIDGINTHEMES_H_
-
-struct smiley_list {
- char *sml;
- GSList *smileys;
- struct smiley_list *next;
-};
-
-struct smiley_theme {
- char *path;
- char *name;
- char *desc;
- char *icon;
- char *author;
-
- struct smiley_list *list;
-};
-
-extern struct smiley_theme *current_smiley_theme;
-extern GSList *smiley_themes;
-
-void pidgin_themes_init(void);
-
-gboolean pidgin_themes_smileys_disabled(void);
-
-void pidgin_themes_smiley_themeize(GtkWidget *);
-
-/**
- * @since 2.5.0
- */
-void pidgin_themes_smiley_themeize_custom(GtkWidget *);
-
-void pidgin_themes_smiley_theme_probe(void);
-
-void pidgin_themes_load_smiley_theme(const char *file, gboolean load);
-
-/**
- * @since 2.1.0
- */
-void pidgin_themes_remove_smiley_theme(const char *file);
-
-GSList *pidgin_themes_get_proto_smileys(const char *id);
-
-#endif /* _PIDGINTHEMES_H_ */
diff --git a/pidgin/gtkutils.c b/pidgin/gtkutils.c
index 4d4552f50a..004d338bb7 100644
--- a/pidgin/gtkutils.c
+++ b/pidgin/gtkutils.c
@@ -1,8 +1,3 @@
-/**
- * @file gtkutils.c GTK+ utility functions
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -26,27 +21,19 @@
#define _PIDGIN_GTKUTILS_C_
#include "internal.h"
+#include "glibcompat.h"
#include "pidgin.h"
#ifdef _WIN32
-# ifdef small
# undef small
-# endif
+# include <shellapi.h>
#endif /*_WIN32*/
-#ifdef USE_GTKSPELL
-# include <gtkspell/gtkspell.h>
-# ifdef _WIN32
-# include "wspell.h"
-# endif
-#endif
-
#include <gdk/gdkkeysyms.h>
#include "conversation.h"
#include "debug.h"
#include "desktopitem.h"
-#include "imgstore.h"
#include "notify.h"
#include "prefs.h"
#include "prpl.h"
@@ -60,20 +47,94 @@
#include "gtkconv.h"
#include "gtkdialogs.h"
-#include "gtkimhtml.h"
-#include "gtkimhtmltoolbar.h"
#include "pidginstock.h"
-#include "gtkthemes.h"
+#include "gtkrequest.h"
#include "gtkutils.h"
+#include "gtkwebview.h"
+#include "gtkwebviewtoolbar.h"
#include "pidgin/minidialog.h"
+#include "gtk3compat.h"
+
+
+/******************************************************************************
+ * Enums
+ *****************************************************************************/
+
+enum {
+ AOP_ICON_COLUMN,
+ AOP_NAME_COLUMN,
+ AOP_DATA_COLUMN,
+ AOP_COLUMN_COUNT
+};
+
+enum {
+ DND_FILE_TRANSFER,
+ DND_IM_IMAGE,
+ DND_BUDDY_ICON
+};
+
+enum {
+ COMPLETION_DISPLAYED_COLUMN, /* displayed completion value */
+ COMPLETION_BUDDY_COLUMN, /* buddy name */
+ COMPLETION_NORMALIZED_COLUMN, /* UTF-8 normalized & casefolded buddy name */
+ COMPLETION_COMPARISON_COLUMN, /* UTF-8 normalized & casefolded value for comparison */
+ COMPLETION_ACCOUNT_COLUMN, /* account */
+ COMPLETION_COLUMN_COUNT
+};
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+
typedef struct {
- GtkWidget *menu;
+ GtkTreeModel *model;
gint default_item;
} AopMenu;
+typedef struct {
+ char *filename;
+ PurpleAccount *account;
+ char *who;
+} _DndData;
+
+typedef struct
+{
+ GtkWidget *entry;
+ GtkWidget *accountopt;
+
+ PidginFilterBuddyCompletionEntryFunc filter_func;
+ gpointer filter_func_user_data;
+
+ GtkListStore *store;
+} PidginCompletionData;
+
+struct _icon_chooser {
+ GtkWidget *icon_filesel;
+ GtkWidget *icon_preview;
+ GtkWidget *icon_text;
+
+ void (*callback)(const char*,gpointer);
+ gpointer data;
+};
+
+struct _old_button_clicked_cb_data
+{
+ PidginUtilMiniDialogCallback cb;
+ gpointer data;
+};
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
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)
@@ -84,44 +145,32 @@ url_clicked_idle_cb(gpointer data)
}
static gboolean
-url_clicked_cb(GtkIMHtml *unused, GtkIMHtmlLink *link)
+url_clicked_cb(PidginWebView *unused, const char *uri)
{
- const char *uri = gtk_imhtml_link_get_url(link);
g_idle_add(url_clicked_idle_cb, g_strdup(uri));
return TRUE;
}
-static GtkIMHtmlFuncs gtkimhtml_cbs = {
- (GtkIMHtmlGetImageFunc)purple_imgstore_find_by_id,
- (GtkIMHtmlGetImageDataFunc)purple_imgstore_get_data,
- (GtkIMHtmlGetImageSizeFunc)purple_imgstore_get_size,
- (GtkIMHtmlGetImageFilenameFunc)purple_imgstore_get_filename,
- purple_imgstore_ref_by_id,
- purple_imgstore_unref_by_id,
-};
-
void
-pidgin_setup_imhtml(GtkWidget *imhtml)
+pidgin_setup_webview(GtkWidget *webview)
{
- g_return_if_fail(imhtml != NULL);
- g_return_if_fail(GTK_IS_IMHTML(imhtml));
-
- pidgin_themes_smiley_themeize(imhtml);
-
- gtk_imhtml_set_funcs(GTK_IMHTML(imhtml), &gtkimhtml_cbs);
+ 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")) {
- PangoFontDescription *desc;
- const char *font = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/custom_font");
- desc = pango_font_description_from_string(font);
- if (desc) {
- gtk_widget_modify_font(imhtml, desc);
- pango_font_description_free(desc);
- }
+ 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
@@ -162,7 +211,9 @@ pidgin_create_small_button(GtkWidget *image)
gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE);
/* set style to make it as small as possible */
+#if !GTK_CHECK_VERSION(3,0,0)
gtk_widget_set_name(button, "pidgin-small-close-button");
+#endif
gtk_widget_show(image);
@@ -178,7 +229,6 @@ pidgin_create_dialog(const char *title, guint border_width, const char *role, gb
wnd = GTK_WINDOW(gtk_dialog_new());
pidgin_window_init(wnd, title, border_width, role, resizable);
- g_object_set(G_OBJECT(wnd), "has-separator", FALSE, NULL);
return GTK_WIDGET(wnd);
}
@@ -186,7 +236,7 @@ pidgin_create_dialog(const char *title, guint border_width, const char *role, gb
GtkWidget *
pidgin_dialog_get_vbox_with_properties(GtkDialog *dialog, gboolean homogeneous, gint spacing)
{
- GtkBox *vbox = GTK_BOX(GTK_DIALOG(dialog)->vbox);
+ GtkBox *vbox = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
gtk_box_set_homogeneous(vbox, homogeneous);
gtk_box_set_spacing(vbox, spacing);
return GTK_WIDGET(vbox);
@@ -194,12 +244,12 @@ pidgin_dialog_get_vbox_with_properties(GtkDialog *dialog, gboolean homogeneous,
GtkWidget *pidgin_dialog_get_vbox(GtkDialog *dialog)
{
- return GTK_DIALOG(dialog)->vbox;
+ return gtk_dialog_get_content_area(GTK_DIALOG(dialog));
}
GtkWidget *pidgin_dialog_get_action_area(GtkDialog *dialog)
{
- return GTK_DIALOG(dialog)->action_area;
+ return gtk_dialog_get_action_area(GTK_DIALOG(dialog));
}
GtkWidget *pidgin_dialog_add_button(GtkDialog *dialog, const char *label,
@@ -215,10 +265,10 @@ GtkWidget *pidgin_dialog_add_button(GtkDialog *dialog, const char *label,
}
GtkWidget *
-pidgin_create_imhtml(gboolean editable, GtkWidget **imhtml_ret, GtkWidget **toolbar_ret, GtkWidget **sw_ret)
+pidgin_create_webview(gboolean editable, GtkWidget **webview_ret, GtkWidget **sw_ret)
{
GtkWidget *frame;
- GtkWidget *imhtml;
+ GtkWidget *webview;
GtkWidget *sep;
GtkWidget *sw;
GtkWidget *toolbar = NULL;
@@ -227,46 +277,41 @@ pidgin_create_imhtml(gboolean editable, GtkWidget **imhtml_ret, GtkWidget **tool
frame = gtk_frame_new(NULL);
gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
- vbox = gtk_vbox_new(FALSE, 0);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add(GTK_CONTAINER(frame), vbox);
gtk_widget_show(vbox);
if (editable) {
- toolbar = gtk_imhtmltoolbar_new();
+ toolbar = pidgin_webviewtoolbar_new();
gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
gtk_widget_show(toolbar);
- sep = gtk_hseparator_new();
+ 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);
}
- imhtml = gtk_imhtml_new(NULL, NULL);
- gtk_imhtml_set_editable(GTK_IMHTML(imhtml), editable);
- gtk_imhtml_set_format_functions(GTK_IMHTML(imhtml), GTK_IMHTML_ALL ^ GTK_IMHTML_IMAGE);
- gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(imhtml), GTK_WRAP_WORD_CHAR);
-#ifdef USE_GTKSPELL
+ webview = pidgin_webview_new(editable);
if (editable && purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/spellcheck"))
- pidgin_setup_gtkspell(GTK_TEXT_VIEW(imhtml));
-#endif
- gtk_widget_show(imhtml);
+ pidgin_webview_set_spellcheck(PIDGIN_WEBVIEW(webview), TRUE);
+ gtk_widget_show(webview);
if (editable) {
- gtk_imhtmltoolbar_attach(GTK_IMHTMLTOOLBAR(toolbar), imhtml);
- gtk_imhtmltoolbar_associate_smileys(GTK_IMHTMLTOOLBAR(toolbar), "default");
+ pidgin_webviewtoolbar_attach(PIDGIN_WEBVIEWTOOLBAR(toolbar), webview);
+ pidgin_webview_set_toolbar(PIDGIN_WEBVIEW(webview), toolbar);
}
- pidgin_setup_imhtml(imhtml);
+ pidgin_setup_webview(webview);
- sw = pidgin_make_scrollable(imhtml, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_NONE, -1, -1);
+ 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);
- if (imhtml_ret != NULL)
- *imhtml_ret = imhtml;
+ pidgin_webview_set_vadjustment(PIDGIN_WEBVIEW(webview),
+ gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(sw)));
- if (editable && (toolbar_ret != NULL))
- *toolbar_ret = toolbar;
+ if (webview_ret != NULL)
+ *webview_ret = webview;
if (sw_ret != NULL)
*sw_ret = sw;
@@ -290,7 +335,7 @@ pidgin_toggle_sensitive(GtkWidget *widget, GtkWidget *to_toggle)
if (to_toggle == NULL)
return;
- sensitivity = GTK_WIDGET_IS_SENSITIVE(to_toggle);
+ sensitivity = gtk_widget_get_sensitive(to_toggle);
gtk_widget_set_sensitive(to_toggle, !sensitivity);
}
@@ -300,14 +345,14 @@ pidgin_toggle_sensitive_array(GtkWidget *w, GPtrArray *data)
{
gboolean sensitivity;
gpointer element;
- int i;
+ guint i;
for (i=0; i < data->len; i++) {
element = g_ptr_array_index(data,i);
if (element == NULL)
continue;
- sensitivity = GTK_WIDGET_IS_SENSITIVE(element);
+ sensitivity = gtk_widget_get_sensitive(element);
gtk_widget_set_sensitive(element, !sensitivity);
}
@@ -319,7 +364,7 @@ pidgin_toggle_showhide(GtkWidget *widget, GtkWidget *to_toggle)
if (to_toggle == NULL)
return;
- if (GTK_WIDGET_VISIBLE(to_toggle))
+ if (gtk_widget_get_visible(to_toggle))
gtk_widget_hide(to_toggle);
else
gtk_widget_show(to_toggle);
@@ -354,7 +399,7 @@ GtkWidget *pidgin_new_item(GtkWidget *menu, const char *str)
gtk_widget_add_accelerator(menuitem, "activate", accel, str[0],
GDK_MOD1_MASK, GTK_ACCEL_LOCKED);
*/
- pidgin_set_accessible_label (menuitem, label);
+ pidgin_set_accessible_label(menuitem, GTK_LABEL(label));
return menuitem;
}
@@ -385,7 +430,7 @@ pidgin_pixbuf_toolbar_button_from_stock(const char *icon)
button = gtk_toggle_button_new();
gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
- bbox = gtk_vbox_new(FALSE, 0);
+ bbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add (GTK_CONTAINER(button), bbox);
@@ -401,20 +446,20 @@ GtkWidget *
pidgin_pixbuf_button_from_stock(const char *text, const char *icon,
PidginButtonOrientation style)
{
- GtkWidget *button, *image, *label, *bbox, *ibox, *lbox = NULL;
+ GtkWidget *button, *image, *bbox, *ibox, *lbox = NULL;
button = gtk_button_new();
if (style == PIDGIN_BUTTON_HORIZONTAL) {
- bbox = gtk_hbox_new(FALSE, 0);
- ibox = gtk_hbox_new(FALSE, 0);
+ bbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ ibox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
if (text)
- lbox = gtk_hbox_new(FALSE, 0);
+ lbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
} else {
- bbox = gtk_vbox_new(FALSE, 0);
- ibox = gtk_vbox_new(FALSE, 0);
+ bbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+ ibox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
if (text)
- lbox = gtk_vbox_new(FALSE, 0);
+ lbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
}
gtk_container_add(GTK_CONTAINER(button), bbox);
@@ -426,12 +471,15 @@ pidgin_pixbuf_button_from_stock(const char *text, const char *icon,
}
if (text) {
+ GtkLabel *label;
+
gtk_box_pack_start(GTK_BOX(bbox), lbox, TRUE, TRUE, 0);
- label = gtk_label_new(NULL);
- gtk_label_set_text_with_mnemonic(GTK_LABEL(label), text);
- gtk_label_set_mnemonic_widget(GTK_LABEL(label), button);
- gtk_box_pack_start(GTK_BOX(lbox), label, FALSE, TRUE, 0);
- pidgin_set_accessible_label (button, label);
+ label = GTK_LABEL(gtk_label_new(NULL));
+ gtk_label_set_text_with_mnemonic(label, text);
+ gtk_label_set_mnemonic_widget(label, button);
+ gtk_box_pack_start(GTK_BOX(lbox), GTK_WIDGET(label),
+ FALSE, TRUE, 0);
+ pidgin_set_accessible_label(button, label);
}
gtk_widget_show_all(bbox);
@@ -486,102 +534,73 @@ GtkWidget *pidgin_new_item_from_stock(GtkWidget *menu, const char *str, const ch
GtkWidget *
pidgin_make_frame(GtkWidget *parent, const char *title)
{
- GtkWidget *vbox, *label, *hbox;
+ GtkWidget *vbox, *vbox2, *hbox;
+ GtkLabel *label;
char *labeltitle;
- vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_start(GTK_BOX(parent), vbox, FALSE, FALSE, 0);
gtk_widget_show(vbox);
- label = gtk_label_new(NULL);
+ label = GTK_LABEL(gtk_label_new(NULL));
labeltitle = g_strdup_printf("<span weight=\"bold\">%s</span>", title);
- gtk_label_set_markup(GTK_LABEL(label), labeltitle);
+ gtk_label_set_markup(label, labeltitle);
g_free(labeltitle);
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
- gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
- gtk_widget_show(label);
- pidgin_set_accessible_label (vbox, label);
+ gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(label), FALSE, FALSE, 0);
+ gtk_widget_show(GTK_WIDGET(label));
+ pidgin_set_accessible_label(vbox, label);
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show(hbox);
- label = gtk_label_new(" ");
- gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
- gtk_widget_show(label);
+ label = GTK_LABEL(gtk_label_new(" "));
+ gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), FALSE, FALSE, 0);
+ gtk_widget_show(GTK_WIDGET(label));
- vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
- gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
- gtk_widget_show(vbox);
+ vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
+ gtk_box_pack_start(GTK_BOX(hbox), vbox2, FALSE, FALSE, 0);
+ gtk_widget_show(vbox2);
- return vbox;
+ g_object_set_data(G_OBJECT(vbox2), "main-vbox", vbox);
+
+ return vbox2;
}
static gpointer
-aop_option_menu_get_selected(GtkWidget *optmenu, GtkWidget **p_item)
+aop_option_menu_get_selected(GtkWidget *optmenu)
{
- GtkWidget *menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(optmenu));
- GtkWidget *item = gtk_menu_get_active(GTK_MENU(menu));
- if (p_item)
- (*p_item) = item;
- return item ? g_object_get_data(G_OBJECT(item), "aop_per_item_data") : NULL;
+ gpointer data = NULL;
+ GtkTreeIter iter;
+
+ g_return_val_if_fail(optmenu != NULL, NULL);
+
+ if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(optmenu), &iter))
+ gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)),
+ &iter, AOP_DATA_COLUMN, &data, -1);
+
+ return data;
}
static void
aop_menu_cb(GtkWidget *optmenu, GCallback cb)
{
- GtkWidget *item;
- gpointer per_item_data;
-
- per_item_data = aop_option_menu_get_selected(optmenu, &item);
-
if (cb != NULL) {
- ((void (*)(GtkWidget *, gpointer, gpointer))cb)(item, per_item_data, g_object_get_data(G_OBJECT(optmenu), "user_data"));
+ ((void (*)(GtkWidget *, gpointer, gpointer))cb)(optmenu,
+ aop_option_menu_get_selected(optmenu),
+ g_object_get_data(G_OBJECT(optmenu), "user_data"));
}
}
-static GtkWidget *
-aop_menu_item_new(GtkSizeGroup *sg, GdkPixbuf *pixbuf, const char *lbl, gpointer per_item_data, const char *data)
+static void
+aop_option_menu_replace_menu(GtkWidget *optmenu, AopMenu *new_aop_menu)
{
- GtkWidget *item;
- GtkWidget *hbox;
- GtkWidget *image;
- GtkWidget *label;
-
- item = gtk_menu_item_new();
- gtk_widget_show(item);
-
- hbox = gtk_hbox_new(FALSE, 4);
- gtk_widget_show(hbox);
-
- /* Create the image */
- if (pixbuf == NULL)
- image = gtk_image_new();
- else
- image = gtk_image_new_from_pixbuf(pixbuf);
- gtk_widget_show(image);
-
- if (sg)
- gtk_size_group_add_widget(sg, image);
-
- /* Create the label */
- label = gtk_label_new (lbl);
- gtk_widget_show (label);
- gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
- gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
-
- gtk_container_add(GTK_CONTAINER(item), hbox);
- gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
- gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
-
- g_object_set_data(G_OBJECT (item), data, per_item_data);
- g_object_set_data(G_OBJECT (item), "aop_per_item_data", per_item_data);
-
- pidgin_set_accessible_label(item, label);
-
- return item;
+ gtk_combo_box_set_model(GTK_COMBO_BOX(optmenu), new_aop_menu->model);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), new_aop_menu->default_item);
+ g_free(new_aop_menu);
}
static GdkPixbuf *
@@ -607,10 +626,11 @@ pidgin_create_prpl_icon_from_prpl(PurplePlugin *prpl, PidginPrplIconSize size, P
*/
tmp = g_strconcat(protoname, ".png", NULL);
- filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols",
- size == PIDGIN_PRPL_ICON_SMALL ? "16" :
- size == PIDGIN_PRPL_ICON_MEDIUM ? "22" : "48",
- tmp, NULL);
+ filename = g_build_filename(PURPLE_DATADIR,
+ "pixmaps", "pidgin", "protocols",
+ (size == PIDGIN_PRPL_ICON_SMALL) ? "16" :
+ ((size == PIDGIN_PRPL_ICON_MEDIUM) ? "22" : "48"),
+ tmp, NULL);
g_free(tmp);
pixbuf = pidgin_pixbuf_new_from_file(filename);
@@ -622,16 +642,17 @@ pidgin_create_prpl_icon_from_prpl(PurplePlugin *prpl, PidginPrplIconSize size, P
static GtkWidget *
aop_option_menu_new(AopMenu *aop_menu, GCallback cb, gpointer user_data)
{
- GtkWidget *optmenu;
+ GtkWidget *optmenu = NULL;
+ GtkCellRenderer *cr = NULL;
- optmenu = gtk_option_menu_new();
+ optmenu = gtk_combo_box_new();
gtk_widget_show(optmenu);
- gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), aop_menu->menu);
-
- if (aop_menu->default_item != -1)
- gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), aop_menu->default_item);
+ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(optmenu), cr = gtk_cell_renderer_pixbuf_new(), FALSE);
+ gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(optmenu), cr, "pixbuf", AOP_ICON_COLUMN);
+ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(optmenu), cr = gtk_cell_renderer_text_new(), TRUE);
+ gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(optmenu), cr, "text", AOP_NAME_COLUMN);
- g_object_set_data_full(G_OBJECT(optmenu), "aop_menu", aop_menu, (GDestroyNotify)g_free);
+ aop_option_menu_replace_menu(optmenu, aop_menu);
g_object_set_data(G_OBJECT(optmenu), "user_data", user_data);
g_signal_connect(G_OBJECT(optmenu), "changed", G_CALLBACK(aop_menu_cb), cb);
@@ -640,32 +661,20 @@ aop_option_menu_new(AopMenu *aop_menu, GCallback cb, gpointer user_data)
}
static void
-aop_option_menu_replace_menu(GtkWidget *optmenu, AopMenu *new_aop_menu)
-{
- if (gtk_option_menu_get_menu(GTK_OPTION_MENU(optmenu)))
- gtk_option_menu_remove_menu(GTK_OPTION_MENU(optmenu));
-
- gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), new_aop_menu->menu);
-
- if (new_aop_menu->default_item != -1)
- gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), new_aop_menu->default_item);
-
- g_object_set_data_full(G_OBJECT(optmenu), "aop_menu", new_aop_menu, (GDestroyNotify)g_free);
-}
-
-static void
aop_option_menu_select_by_data(GtkWidget *optmenu, gpointer data)
{
- guint idx;
- GList *llItr = NULL;
-
- for (idx = 0, llItr = GTK_MENU_SHELL(gtk_option_menu_get_menu(GTK_OPTION_MENU(optmenu)))->children;
- llItr != NULL;
- llItr = llItr->next, idx++) {
- if (data == g_object_get_data(G_OBJECT(llItr->data), "aop_per_item_data")) {
- gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), idx);
- break;
- }
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gpointer iter_data;
+ model = gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu));
+ 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(optmenu), &iter);
+ return;
+ }
+ } while (gtk_tree_model_iter_next(model, &iter));
}
}
@@ -675,21 +684,16 @@ create_protocols_menu(const char *default_proto_id)
AopMenu *aop_menu = NULL;
PurplePlugin *plugin;
GdkPixbuf *pixbuf = NULL;
- GtkSizeGroup *sg;
+ GtkTreeIter iter;
+ GtkListStore *ls;
GList *p;
- const char *gtalk_name = NULL, *facebook_name = NULL;
int i;
- aop_menu = g_malloc0(sizeof(AopMenu));
- aop_menu->default_item = -1;
- aop_menu->menu = gtk_menu_new();
- gtk_widget_show(aop_menu->menu);
- sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
+ ls = gtk_list_store_new(AOP_COLUMN_COUNT, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER);
- if (purple_find_prpl("prpl-jabber")) {
- gtalk_name = _("Google Talk");
- facebook_name = _("Facebook (XMPP)");
- }
+ aop_menu = g_malloc0(sizeof(AopMenu));
+ aop_menu->default_item = 0;
+ aop_menu->model = GTK_TREE_MODEL(ls);
for (p = purple_plugins_get_protocols(), i = 0;
p != NULL;
@@ -697,56 +701,14 @@ create_protocols_menu(const char *default_proto_id)
plugin = (PurplePlugin *)p->data;
- if (gtalk_name && strcmp(gtalk_name, plugin->info->name) < 0) {
- char *filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols",
- "16", "google-talk.png", NULL);
- GtkWidget *item;
-
- pixbuf = pidgin_pixbuf_new_from_file(filename);
- g_free(filename);
-
- gtk_menu_shell_append(GTK_MENU_SHELL(aop_menu->menu),
- item = aop_menu_item_new(sg, pixbuf, gtalk_name, "prpl-jabber", "protocol"));
- g_object_set_data(G_OBJECT(item), "fakegoogle", GINT_TO_POINTER(1));
-
- if (pixbuf)
- g_object_unref(pixbuf);
-
- /* libpurple3 compatibility */
- if (g_strcmp0(default_proto_id, "prpl-gtalk") == 0)
- aop_menu->default_item = i;
-
- gtalk_name = NULL;
- i++;
- }
-
- if (facebook_name && strcmp(facebook_name, plugin->info->name) < 0) {
- char *filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols",
- "16", "facebook.png", NULL);
- GtkWidget *item;
-
- pixbuf = pidgin_pixbuf_new_from_file(filename);
- g_free(filename);
-
- gtk_menu_shell_append(GTK_MENU_SHELL(aop_menu->menu),
- item = aop_menu_item_new(sg, pixbuf, facebook_name, "prpl-jabber", "protocol"));
- g_object_set_data(G_OBJECT(item), "fakefacebook", GINT_TO_POINTER(1));
-
- if (pixbuf)
- g_object_unref(pixbuf);
-
- /* libpurple3 compatibility */
- if (g_strcmp0(default_proto_id, "prpl-facebook-xmpp") == 0)
- aop_menu->default_item = i;
-
- facebook_name = NULL;
- i++;
- }
-
pixbuf = pidgin_create_prpl_icon_from_prpl(plugin, PIDGIN_PRPL_ICON_SMALL, NULL);
- gtk_menu_shell_append(GTK_MENU_SHELL(aop_menu->menu),
- aop_menu_item_new(sg, pixbuf, plugin->info->name, plugin->info->id, "protocol"));
+ gtk_list_store_append(ls, &iter);
+ gtk_list_store_set(ls, &iter,
+ AOP_ICON_COLUMN, pixbuf,
+ AOP_NAME_COLUMN, plugin->info->name,
+ AOP_DATA_COLUMN, plugin->info->id,
+ -1);
if (pixbuf)
g_object_unref(pixbuf);
@@ -755,8 +717,6 @@ create_protocols_menu(const char *default_proto_id)
aop_menu->default_item = i;
}
- g_object_unref(sg);
-
return aop_menu;
}
@@ -770,13 +730,13 @@ pidgin_protocol_option_menu_new(const char *id, GCallback cb,
const char *
pidgin_protocol_option_menu_get_selected(GtkWidget *optmenu)
{
- return (const char *)aop_option_menu_get_selected(optmenu, NULL);
+ 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, NULL);
+ return (PurpleAccount *)aop_option_menu_get_selected(optmenu);
}
static AopMenu *
@@ -788,7 +748,8 @@ create_account_menu(PurpleAccount *default_account,
GdkPixbuf *pixbuf = NULL;
GList *list;
GList *p;
- GtkSizeGroup *sg;
+ GtkListStore *ls;
+ GtkTreeIter iter;
int i;
char buf[256];
@@ -797,11 +758,11 @@ create_account_menu(PurpleAccount *default_account,
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 = -1;
- aop_menu->menu = gtk_menu_new();
- gtk_widget_show(aop_menu->menu);
- sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
+ 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)
@@ -825,10 +786,10 @@ create_account_menu(PurpleAccount *default_account,
gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE);
}
- if (purple_account_get_alias(account)) {
+ if (purple_account_get_private_alias(account)) {
g_snprintf(buf, sizeof(buf), "%s (%s) (%s)",
purple_account_get_username(account),
- purple_account_get_alias(account),
+ purple_account_get_private_alias(account),
purple_account_get_protocol_name(account));
} else {
g_snprintf(buf, sizeof(buf), "%s (%s)",
@@ -836,8 +797,12 @@ create_account_menu(PurpleAccount *default_account,
purple_account_get_protocol_name(account));
}
- gtk_menu_shell_append(GTK_MENU_SHELL(aop_menu->menu),
- aop_menu_item_new(sg, pixbuf, buf, account, "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);
@@ -846,8 +811,6 @@ create_account_menu(PurpleAccount *default_account,
aop_menu->default_item = i;
}
- g_object_unref(sg);
-
return aop_menu;
}
@@ -858,7 +821,7 @@ regenerate_account_menu(GtkWidget *optmenu)
PurpleAccount *account;
PurpleFilterAccountFunc filter_func;
- account = (PurpleAccount *)aop_option_menu_get_selected(optmenu, NULL);
+ 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");
@@ -928,43 +891,6 @@ pidgin_account_option_menu_new(PurpleAccount *default_account,
return optmenu;
}
-gboolean
-pidgin_check_if_dir(const char *path, GtkFileSelection *filesel)
-{
- char *dirname = NULL;
-
- if (g_file_test(path, G_FILE_TEST_IS_DIR)) {
- /* append a / if needed */
- if (path[strlen(path) - 1] != G_DIR_SEPARATOR) {
- dirname = g_strconcat(path, G_DIR_SEPARATOR_S, NULL);
- }
- gtk_file_selection_set_filename(filesel, (dirname != NULL) ? dirname : path);
- g_free(dirname);
- return TRUE;
- }
-
- return FALSE;
-}
-
-void
-pidgin_setup_gtkspell(GtkTextView *textview)
-{
-#ifdef USE_GTKSPELL
- GError *error = NULL;
- char *locale = NULL;
-
- g_return_if_fail(textview != NULL);
- g_return_if_fail(GTK_IS_TEXT_VIEW(textview));
-
- if (gtkspell_new_attach(textview, locale, &error) == NULL && error)
- {
- purple_debug_warning("gtkspell", "Failed to setup GtkSpell: %s\n",
- error->message);
- g_error_free(error);
- }
-#endif /* USE_GTKSPELL */
-}
-
void
pidgin_save_accels_cb(GtkAccelGroup *accel_group, guint arg1,
GdkModifierType arg2, GClosure *arg3,
@@ -1008,7 +934,7 @@ static void
show_retrieveing_info(PurpleConnection *conn, const char *name)
{
PurpleNotifyUserInfo *info = purple_notify_user_info_new();
- purple_notify_user_info_add_pair(info, _("Information"), _("Retrieving..."));
+ purple_notify_user_info_add_pair_plaintext(info, _("Information"), _("Retrieving..."));
purple_notify_userinfo(conn, name, info, NULL, NULL);
purple_notify_user_info_destroy(info);
}
@@ -1016,7 +942,7 @@ show_retrieveing_info(PurpleConnection *conn, const char *name)
void pidgin_retrieve_user_info(PurpleConnection *conn, const char *name)
{
show_retrieveing_info(conn, name);
- serv_get_info(conn, name);
+ purple_serv_get_info(conn, name);
}
void pidgin_retrieve_user_info_in_chat(PurpleConnection *conn, const char *name, int chat)
@@ -1029,7 +955,7 @@ void pidgin_retrieve_user_info_in_chat(PurpleConnection *conn, const char *name,
return;
}
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(conn->prpl);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(conn));
if (prpl_info != NULL && prpl_info->get_cb_real_name)
who = prpl_info->get_cb_real_name(conn, chat, name);
if (prpl_info == NULL || prpl_info->get_cb_info == NULL) {
@@ -1155,7 +1081,7 @@ pidgin_parse_x_im_contact(const char *msg, gboolean all_accounts,
gc = (PurpleConnection *)l->data;
account = purple_connection_get_account(gc);
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
}
protoname = prpl_info->list_icon(account, NULL);
@@ -1197,7 +1123,7 @@ pidgin_parse_x_im_contact(const char *msg, gboolean all_accounts,
gc = (PurpleConnection *)l->data;
account = purple_connection_get_account(gc);
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
}
protoname = prpl_info->list_icon(account, NULL);
@@ -1227,7 +1153,7 @@ pidgin_parse_x_im_contact(const char *msg, gboolean all_accounts,
}
void
-pidgin_set_accessible_label (GtkWidget *w, GtkWidget *l)
+pidgin_set_accessible_label(GtkWidget *w, GtkLabel *l)
{
AtkObject *acc;
const gchar *label_text;
@@ -1238,7 +1164,7 @@ pidgin_set_accessible_label (GtkWidget *w, GtkWidget *l)
/* If this object has no name, set it's name with the label text */
existing_name = atk_object_get_name (acc);
if (!existing_name) {
- label_text = gtk_label_get_text (GTK_LABEL(l));
+ label_text = gtk_label_get_text(l);
if (label_text)
atk_object_set_name (acc, label_text);
}
@@ -1247,7 +1173,7 @@ pidgin_set_accessible_label (GtkWidget *w, GtkWidget *l)
}
void
-pidgin_set_accessible_relations (GtkWidget *w, GtkWidget *l)
+pidgin_set_accessible_relations (GtkWidget *w, GtkLabel *l)
{
AtkObject *acc, *label;
AtkObject *rel_obj[1];
@@ -1255,10 +1181,10 @@ pidgin_set_accessible_relations (GtkWidget *w, GtkWidget *l)
AtkRelation *relation;
acc = gtk_widget_get_accessible (w);
- label = gtk_widget_get_accessible (l);
+ label = gtk_widget_get_accessible(GTK_WIDGET(l));
/* Make sure mnemonics work */
- gtk_label_set_mnemonic_widget(GTK_LABEL(l), w);
+ gtk_label_set_mnemonic_widget(l, w);
/* Create the labeled-by relation */
set = atk_object_ref_relation_set (acc);
@@ -1300,8 +1226,8 @@ pidgin_menu_position_func_helper(GtkMenu *menu,
widget = GTK_WIDGET(menu);
screen = gtk_widget_get_screen(widget);
- xthickness = widget->style->xthickness;
- ythickness = widget->style->ythickness;
+ xthickness = gtk_widget_get_style(widget)->xthickness;
+ ythickness = gtk_widget_get_style(widget)->ythickness;
rtl = (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL);
/*
@@ -1310,7 +1236,7 @@ pidgin_menu_position_func_helper(GtkMenu *menu,
* if a size_request was queued while we weren't popped up,
* the requisition won't have been recomputed yet.
*/
- gtk_widget_size_request (widget, &requisition);
+ gtk_widget_get_preferred_size(widget, NULL, &requisition);
monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
@@ -1439,9 +1365,9 @@ pidgin_treeview_popup_menu_position_func(GtkMenu *menu,
GtkTreePath *path;
GtkTreeViewColumn *col;
GdkRectangle rect;
- gint ythickness = GTK_WIDGET(menu)->style->ythickness;
+ gint ythickness = gtk_widget_get_style(GTK_WIDGET(menu))->ythickness;
- gdk_window_get_origin (widget->window, x, y);
+ gdk_window_get_origin (gtk_widget_get_window(widget), x, y);
gtk_tree_view_get_cursor (tv, &path, &col);
gtk_tree_view_get_cell_area (tv, path, col, &rect);
@@ -1450,31 +1376,19 @@ pidgin_treeview_popup_menu_position_func(GtkMenu *menu,
pidgin_menu_position_func_helper(menu, x, y, push_in, data);
}
-enum {
- DND_FILE_TRANSFER,
- DND_IM_IMAGE,
- DND_BUDDY_ICON
-};
-
-typedef struct {
- char *filename;
- PurpleAccount *account;
- char *who;
-} _DndData;
-
static void dnd_image_ok_callback(_DndData *data, int choice)
{
const gchar *shortname;
gchar *filedata;
size_t size;
- struct stat st;
+ GStatBuf st;
GError *err = NULL;
PurpleConversation *conv;
PidginConversation *gtkconv;
- GtkTextIter iter;
- int id;
PurpleBuddy *buddy;
PurpleContact *contact;
+ PurpleImage *img;
+
switch (choice) {
case DND_BUDDY_ICON:
if (g_stat(data->filename, &st)) {
@@ -1483,14 +1397,13 @@ static void dnd_image_ok_callback(_DndData *data, int choice)
str = g_strdup_printf(_("The following error has occurred loading %s: %s"),
data->filename, g_strerror(errno));
purple_notify_error(NULL, NULL,
- _("Failed to load image"),
- str);
+ _("Failed to load image"), str, NULL);
g_free(str);
break;
}
- buddy = purple_find_buddy(data->account, data->who);
+ buddy = purple_blist_find_buddy(data->account, data->who);
if (!buddy) {
purple_debug_info("custom-icon", "You can only set custom icons for people on your buddylist.\n");
break;
@@ -1499,10 +1412,10 @@ static void dnd_image_ok_callback(_DndData *data, int choice)
purple_buddy_icons_node_set_custom_icon_from_file((PurpleBlistNode*)contact, data->filename);
break;
case DND_FILE_TRANSFER:
- serv_send_file(purple_account_get_connection(data->account), data->who, data->filename);
+ purple_serv_send_file(purple_account_get_connection(data->account), data->who, data->filename);
break;
case DND_IM_IMAGE:
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, data->account, data->who);
+ conv = PURPLE_CONVERSATION(purple_im_conversation_new(data->account, data->who));
gtkconv = PIDGIN_CONVERSATION(conv);
if (!g_file_get_contents(data->filename, &filedata, &size,
@@ -1511,8 +1424,7 @@ static void dnd_image_ok_callback(_DndData *data, int choice)
str = g_strdup_printf(_("The following error has occurred loading %s: %s"), data->filename, err->message);
purple_notify_error(NULL, NULL,
- _("Failed to load image"),
- str);
+ _("Failed to load image"), str, NULL);
g_error_free(err);
g_free(str);
@@ -1521,12 +1433,11 @@ static void dnd_image_ok_callback(_DndData *data, int choice)
}
shortname = strrchr(data->filename, G_DIR_SEPARATOR);
shortname = shortname ? shortname + 1 : data->filename;
- id = purple_imgstore_add_with_id(filedata, size, shortname);
+ img = purple_image_new_from_data(filedata, size);
+ purple_image_set_friendly_filename(img, shortname);
- gtk_text_buffer_get_iter_at_mark(GTK_IMHTML(gtkconv->entry)->text_buffer, &iter,
- gtk_text_buffer_get_insert(GTK_IMHTML(gtkconv->entry)->text_buffer));
- gtk_imhtml_insert_image_at_iter(GTK_IMHTML(gtkconv->entry), id, &iter);
- purple_imgstore_unref_by_id(id);
+ pidgin_webview_insert_image(PIDGIN_WEBVIEW(gtkconv->entry), img);
+ g_object_unref(img);
break;
}
@@ -1558,7 +1469,7 @@ void
pidgin_dnd_file_manage(GtkSelectionData *sd, PurpleAccount *account, const char *who)
{
GdkPixbuf *pb;
- GList *files = purple_uri_list_extract_filenames((const gchar *)sd->data);
+ GList *files = purple_uri_list_extract_filenames((const gchar *) gtk_selection_data_get_data(sd));
PurpleConnection *gc = purple_account_get_connection(account);
PurplePluginProtocolInfo *prpl_info = NULL;
#ifndef _WIN32
@@ -1590,8 +1501,8 @@ pidgin_dnd_file_manage(GtkSelectionData *sd, PurpleAccount *account, const char
str = g_strdup_printf(_("Cannot send folder %s."), basename);
str2 = g_strdup_printf(_("%s cannot transfer a folder. You will need to send the files within individually."), PIDGIN_NAME);
- purple_notify_error(NULL, NULL,
- str, str2);
+ purple_notify_error(NULL, NULL, str, str2,
+ purple_request_cpar_from_connection(gc));
g_free(str);
g_free(str2);
@@ -1609,9 +1520,9 @@ pidgin_dnd_file_manage(GtkSelectionData *sd, PurpleAccount *account, const char
data->account = account;
if (gc)
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
- if (prpl_info && prpl_info->options & OPT_PROTO_IM_IMAGE)
+ if (!(purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_NO_IMAGES))
im = TRUE;
if (prpl_info && prpl_info->can_receive_file)
@@ -1626,7 +1537,7 @@ pidgin_dnd_file_manage(GtkSelectionData *sd, PurpleAccount *account, const char
"embed it into this message, or use it as the buddy icon for this user."),
DND_FILE_TRANSFER, _("OK"), (GCallback)dnd_image_ok_callback,
_("Cancel"), (GCallback)dnd_image_cancel_callback,
- account, who, NULL,
+ purple_request_cpar_from_account(account),
data,
_("Set as buddy icon"), DND_BUDDY_ICON,
_("Send image file"), DND_FILE_TRANSFER,
@@ -1636,17 +1547,17 @@ pidgin_dnd_file_manage(GtkSelectionData *sd, PurpleAccount *account, const char
purple_request_yes_no(NULL, NULL, _("You have dragged an image"),
_("Would you like to set it as the buddy icon for this user?"),
PURPLE_DEFAULT_ACTION_NONE,
- account, who, NULL,
+ purple_request_cpar_from_account(account),
data, (GCallback)dnd_set_icon_ok_cb, (GCallback)dnd_set_icon_cancel_cb);
else
purple_request_choice(NULL, NULL,
_("You have dragged an image"),
(ft ? _("You can send this image as a file transfer, or use it as the buddy icon for this user.") :
_("You can insert this image into this message, or use it as the buddy icon for this user")),
- (ft ? DND_FILE_TRANSFER : DND_IM_IMAGE),
+ GINT_TO_POINTER(ft ? DND_FILE_TRANSFER : DND_IM_IMAGE),
_("OK"), (GCallback)dnd_image_ok_callback,
_("Cancel"), (GCallback)dnd_image_cancel_callback,
- account, who, NULL,
+ purple_request_cpar_from_account(account),
data,
_("Set as buddy icon"), DND_BUDDY_ICON,
(ft ? _("Send image file") : _("Insert in message")), (ft ? DND_FILE_TRANSFER : DND_IM_IMAGE),
@@ -1684,11 +1595,11 @@ pidgin_dnd_file_manage(GtkSelectionData *sd, PurpleAccount *account, const char
PidginConversation *gtkconv;
case PURPLE_DESKTOP_ITEM_TYPE_LINK:
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, who);
+ conv = PURPLE_CONVERSATION(purple_im_conversation_new(account, who));
gtkconv = PIDGIN_CONVERSATION(conv);
- gtk_imhtml_insert_link(GTK_IMHTML(gtkconv->entry),
- gtk_text_buffer_get_insert(GTK_IMHTML(gtkconv->entry)->text_buffer),
- purple_desktop_item_get_string(item, "URL"), itemname);
+ pidgin_webview_insert_link(PIDGIN_WEBVIEW(gtkconv->entry),
+ purple_desktop_item_get_string(item, "URL"),
+ itemname);
break;
default:
/* I don't know if we really want to do anything here. Most of
@@ -1703,7 +1614,7 @@ pidgin_dnd_file_manage(GtkSelectionData *sd, PurpleAccount *account, const char
_("You dragged a desktop launcher. Most "
"likely you wanted to send the target "
"of this launcher instead of this "
- "launcher itself."));
+ "launcher itself."), NULL);
break;
}
purple_desktop_item_unref(item);
@@ -1717,7 +1628,7 @@ pidgin_dnd_file_manage(GtkSelectionData *sd, PurpleAccount *account, const char
#endif /* _WIN32 */
/* Everything is fine, let's send */
- serv_send_file(gc, who, filename);
+ purple_serv_send_file(gc, who, filename);
}
g_free(filename);
@@ -1799,7 +1710,7 @@ pidgin_stock_id_from_presence(PurplePresence *presence)
g_return_val_if_fail(presence, NULL);
status = purple_presence_get_active_status(presence);
- type = purple_status_get_type(status);
+ type = purple_status_get_status_type(status);
prim = purple_status_type_get_primitive(type);
idle = purple_presence_is_idle(presence);
@@ -1838,21 +1749,45 @@ pidgin_append_menu_action(GtkWidget *menu, PurpleMenuAction *act,
gpointer object)
{
GtkWidget *menuitem;
+ GList *list;
+ const gchar *stock_id;
+ GtkWidget *icon_image = NULL;
if (act == NULL) {
return pidgin_separator(menu);
}
- if (act->children == NULL) {
- menuitem = gtk_menu_item_new_with_mnemonic(act->label);
+ stock_id = purple_menu_action_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_menu_action_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_menu_action_get_label(act));
+ }
+
+ list = purple_menu_action_get_children(act);
+
+ if (list == NULL) {
+ PurpleCallback callback;
- if (act->callback != NULL) {
+ callback = purple_menu_action_get_callback(act);
+
+ if (callback != NULL) {
g_object_set_data(G_OBJECT(menuitem),
"purplecallback",
- act->callback);
+ callback);
g_object_set_data(G_OBJECT(menuitem),
"purplecallbackdata",
- act->data);
+ purple_menu_action_get_data(act));
g_signal_connect(G_OBJECT(menuitem), "activate",
G_CALLBACK(menu_action_cb),
object);
@@ -1866,7 +1801,6 @@ pidgin_append_menu_action(GtkWidget *menu, PurpleMenuAction *act,
GtkWidget *submenu = NULL;
GtkAccelGroup *group;
- menuitem = gtk_menu_item_new_with_mnemonic(act->label);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
submenu = gtk_menu_new();
@@ -1874,35 +1808,26 @@ pidgin_append_menu_action(GtkWidget *menu, PurpleMenuAction *act,
group = gtk_menu_get_accel_group(GTK_MENU(menu));
if (group) {
- char *path = g_strdup_printf("%s/%s", GTK_MENU_ITEM(menuitem)->accel_path, act->label);
+ char *path = g_strdup_printf("%s/%s",
+ gtk_menu_item_get_accel_path(GTK_MENU_ITEM(menuitem)),
+ purple_menu_action_get_label(act));
gtk_menu_set_accel_path(GTK_MENU(submenu), path);
g_free(path);
gtk_menu_set_accel_group(GTK_MENU(submenu), group);
}
- for (l = act->children; l; l = l->next) {
+ for (l = list; l; l = l->next) {
PurpleMenuAction *act = (PurpleMenuAction *)l->data;
pidgin_append_menu_action(submenu, act, object);
}
- g_list_free(act->children);
- act->children = NULL;
+ g_list_free(list);
+ purple_menu_action_set_children(act, NULL);
}
purple_menu_action_free(act);
return menuitem;
}
-typedef struct
-{
- GtkWidget *entry;
- GtkWidget *accountopt;
-
- PidginFilterBuddyCompletionEntryFunc filter_func;
- gpointer filter_func_user_data;
-
- GtkListStore *store;
-} PidginCompletionData;
-
static gboolean buddyname_completion_match_func(GtkEntryCompletion *completion,
const gchar *key, GtkTreeIter *iter, gpointer user_data)
{
@@ -1911,10 +1836,10 @@ static gboolean buddyname_completion_match_func(GtkEntryCompletion *completion,
GValue val2;
const char *tmp;
- model = gtk_entry_completion_get_model (completion);
+ model = gtk_entry_completion_get_model(completion);
val1.g_type = 0;
- gtk_tree_model_get_value(model, iter, 2, &val1);
+ gtk_tree_model_get_value(model, iter, COMPLETION_NORMALIZED_COLUMN, &val1);
tmp = g_value_get_string(&val1);
if (tmp != NULL && purple_str_has_prefix(tmp, key))
{
@@ -1924,7 +1849,7 @@ static gboolean buddyname_completion_match_func(GtkEntryCompletion *completion,
g_value_unset(&val1);
val2.g_type = 0;
- gtk_tree_model_get_value(model, iter, 3, &val2);
+ gtk_tree_model_get_value(model, iter, COMPLETION_COMPARISON_COLUMN, &val2);
tmp = g_value_get_string(&val2);
if (tmp != NULL && purple_str_has_prefix(tmp, key))
{
@@ -1944,11 +1869,11 @@ static gboolean buddyname_completion_match_selected_cb(GtkEntryCompletion *compl
PurpleAccount *account;
val.g_type = 0;
- gtk_tree_model_get_value(model, iter, 1, &val);
+ gtk_tree_model_get_value(model, iter, COMPLETION_BUDDY_COLUMN, &val);
gtk_entry_set_text(GTK_ENTRY(data->entry), g_value_get_string(&val));
g_value_unset(&val);
- gtk_tree_model_get_value(model, iter, 4, &val);
+ gtk_tree_model_get_value(model, iter, COMPLETION_ACCOUNT_COLUMN, &val);
account = g_value_get_pointer(&val);
g_value_unset(&val);
@@ -1985,11 +1910,11 @@ add_buddyname_autocomplete_entry(GtkListStore *store, const char *buddy_alias, c
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter,
- 0, completion_entry,
- 1, buddyname,
- 2, normalized_buddyname,
- 3, tmp,
- 4, account,
+ COMPLETION_DISPLAYED_COLUMN, completion_entry,
+ COMPLETION_BUDDY_COLUMN, buddyname,
+ COMPLETION_NORMALIZED_COLUMN, normalized_buddyname,
+ COMPLETION_COMPARISON_COLUMN, tmp,
+ COMPLETION_ACCOUNT_COLUMN, account,
-1);
g_free(completion_entry);
g_free(tmp);
@@ -2010,11 +1935,11 @@ add_buddyname_autocomplete_entry(GtkListStore *store, const char *buddy_alias, c
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter,
- 0, completion_entry,
- 1, buddyname,
- 2, normalized_buddyname,
- 3, tmp,
- 4, account,
+ COMPLETION_DISPLAYED_COLUMN, completion_entry,
+ COMPLETION_BUDDY_COLUMN, buddyname,
+ COMPLETION_NORMALIZED_COLUMN, normalized_buddyname,
+ COMPLETION_COMPARISON_COLUMN, tmp,
+ COMPLETION_ACCOUNT_COLUMN, account,
-1);
g_free(completion_entry);
g_free(tmp);
@@ -2026,11 +1951,11 @@ add_buddyname_autocomplete_entry(GtkListStore *store, const char *buddy_alias, c
/* Add the buddy's name. */
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter,
- 0, buddyname,
- 1, buddyname,
- 2, normalized_buddyname,
- 3, NULL,
- 4, account,
+ COMPLETION_DISPLAYED_COLUMN, buddyname,
+ COMPLETION_BUDDY_COLUMN, buddyname,
+ COMPLETION_NORMALIZED_COLUMN, normalized_buddyname,
+ COMPLETION_COMPARISON_COLUMN, NULL,
+ COMPLETION_ACCOUNT_COLUMN, account,
-1);
}
@@ -2063,19 +1988,22 @@ add_completion_list(PidginCompletionData *data)
PidginFilterBuddyCompletionEntryFunc filter_func = data->filter_func;
gpointer user_data = data->filter_func_user_data;
GHashTable *sets;
+ gchar *alias;
gtk_list_store_clear(data->store);
- for (gnode = purple_get_blist()->root; gnode != NULL; gnode = gnode->next)
+ for (gnode = purple_blist_get_buddy_list()->root; gnode != NULL; gnode = gnode->next)
{
- if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
+ if (!PURPLE_IS_GROUP(gnode))
continue;
for (cnode = gnode->child; cnode != NULL; cnode = cnode->next)
{
- if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
+ if (!PURPLE_IS_CONTACT(cnode))
continue;
+ g_object_get(cnode, "alias", &alias, NULL);
+
for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
{
PidginBuddyCompletionEntry entry;
@@ -2084,13 +2012,15 @@ add_completion_list(PidginCompletionData *data)
if (filter_func(&entry, user_data)) {
add_buddyname_autocomplete_entry(data->store,
- ((PurpleContact *)cnode)->alias,
+ alias,
purple_buddy_get_contact_alias(entry.entry.buddy),
- entry.entry.buddy->account,
- entry.entry.buddy->name
+ purple_buddy_get_account(entry.entry.buddy),
+ purple_buddy_get_name(entry.entry.buddy)
);
}
}
+
+ g_free(alias);
}
}
@@ -2114,7 +2044,7 @@ repopulate_autocomplete(gpointer something, gpointer data)
}
void
-pidgin_setup_screenname_autocomplete_with_filter(GtkWidget *entry, GtkWidget *accountopt, PidginFilterBuddyCompletionEntryFunc filter_func, gpointer user_data)
+pidgin_setup_screenname_autocomplete(GtkWidget *entry, GtkWidget *accountopt, PidginFilterBuddyCompletionEntryFunc filter_func, gpointer user_data)
{
PidginCompletionData *data;
@@ -2128,7 +2058,9 @@ pidgin_setup_screenname_autocomplete_with_filter(GtkWidget *entry, GtkWidget *ac
GtkEntryCompletion *completion;
data = g_new0(PidginCompletionData, 1);
- store = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
+ store = gtk_list_store_new(COMPLETION_COLUMN_COUNT, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_POINTER);
data->entry = entry;
data->accountopt = accountopt;
@@ -2145,7 +2077,8 @@ pidgin_setup_screenname_autocomplete_with_filter(GtkWidget *entry, GtkWidget *ac
/* Sort the completion list by buddy name */
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store),
- 1, GTK_SORT_ASCENDING);
+ COMPLETION_BUDDY_COLUMN,
+ GTK_SORT_ASCENDING);
completion = gtk_entry_completion_new();
gtk_entry_completion_set_match_func(completion, buddyname_completion_match_func, NULL, NULL);
@@ -2159,7 +2092,7 @@ pidgin_setup_screenname_autocomplete_with_filter(GtkWidget *entry, GtkWidget *ac
gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(store));
g_object_unref(store);
- gtk_entry_completion_set_text_column(completion, 0);
+ gtk_entry_completion_set_text_column(completion, COMPLETION_DISPLAYED_COLUMN);
purple_signal_connect(purple_connections_get_handle(), "signed-on", entry,
PURPLE_CALLBACK(repopulate_autocomplete), data);
@@ -2179,52 +2112,41 @@ pidgin_screenname_autocomplete_default_filter(const PidginBuddyCompletionEntry *
gboolean all = GPOINTER_TO_INT(all_accounts);
if (completion_entry->is_buddy) {
- return all || purple_account_is_connected(completion_entry->entry.buddy->account);
+ return all || purple_account_is_connected(purple_buddy_get_account(completion_entry->entry.buddy));
} else {
return all || (completion_entry->entry.logged_buddy->account != NULL && purple_account_is_connected(completion_entry->entry.logged_buddy->account));
}
}
-void
-pidgin_setup_screenname_autocomplete(GtkWidget *entry, GtkWidget *accountopt, gboolean all) {
- pidgin_setup_screenname_autocomplete_with_filter(entry, accountopt, pidgin_screenname_autocomplete_default_filter, GINT_TO_POINTER(all));
-}
-
-
-
void pidgin_set_cursor(GtkWidget *widget, GdkCursorType cursor_type)
{
GdkCursor *cursor;
g_return_if_fail(widget != NULL);
- if (widget->window == NULL)
+ if (gtk_widget_get_window(widget) == NULL)
return;
cursor = gdk_cursor_new(cursor_type);
- gdk_window_set_cursor(widget->window, cursor);
+ gdk_window_set_cursor(gtk_widget_get_window(widget), cursor);
+
+#if GTK_CHECK_VERSION(3,0,0)
+ g_object_unref(cursor);
+#else
gdk_cursor_unref(cursor);
+#endif
- gdk_display_flush(gdk_drawable_get_display(GDK_DRAWABLE(widget->window)));
+ gdk_display_flush(gdk_window_get_display(gtk_widget_get_window(widget)));
}
void pidgin_clear_cursor(GtkWidget *widget)
{
g_return_if_fail(widget != NULL);
- if (widget->window == NULL)
+ if (gtk_widget_get_window(widget) == NULL)
return;
- gdk_window_set_cursor(widget->window, NULL);
+ gdk_window_set_cursor(gtk_widget_get_window(widget), NULL);
}
-struct _icon_chooser {
- GtkWidget *icon_filesel;
- GtkWidget *icon_preview;
- GtkWidget *icon_text;
-
- void (*callback)(const char*,gpointer);
- gpointer data;
-};
-
static void
icon_filesel_choose_cb(GtkWidget *widget, gint response, struct _icon_chooser *dialog)
{
@@ -2254,7 +2176,7 @@ icon_filesel_choose_cb(GtkWidget *widget, gint response, struct _icon_chooser *d
gtk_widget_destroy(dialog->icon_filesel);
g_free(filename);
g_free(dialog);
- }
+}
static void
@@ -2263,7 +2185,7 @@ icon_preview_change_cb(GtkFileChooser *widget, struct _icon_chooser *dialog)
GdkPixbuf *pixbuf;
int height, width;
char *basename, *markup, *size;
- struct stat st;
+ GStatBuf st;
char *filename;
filename = gtk_file_chooser_get_preview_filename(
@@ -2321,7 +2243,7 @@ GtkWidget *pidgin_buddy_icon_chooser_new(GtkWindow *parent, void(*callback)(cons
dialog->icon_preview = gtk_image_new();
dialog->icon_text = gtk_label_new(NULL);
- vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
gtk_widget_set_size_request(GTK_WIDGET(vbox), -1, 50);
gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(dialog->icon_preview), TRUE, FALSE, 0);
gtk_box_pack_end(GTK_BOX(vbox), GTK_WIDGET(dialog->icon_text), FALSE, FALSE, 0);
@@ -2345,8 +2267,10 @@ GtkWidget *pidgin_buddy_icon_chooser_new(GtkWindow *parent, void(*callback)(cons
return dialog->icon_filesel;
}
-/**
- * @return True if any string from array a exists in array b.
+/*
+ * str_array_match:
+ *
+ * Returns: %TRUE if any string from array @a exists in array @b.
*/
static gboolean
str_array_match(char **a, char **b)
@@ -2488,9 +2412,9 @@ pidgin_convert_buddy_icon(PurplePlugin *plugin, const char *path, size_t *len)
the image. */
purple_debug_info("buddyicon", "Converted image from "
"%dx%d to %dx%d, format=%s, quality=%u, "
- "filesize=%zu\n", orig_width, orig_height,
- new_width, new_height, prpl_formats[i], quality,
- length);
+ "filesize=%" G_GSIZE_FORMAT "\n",
+ orig_width, orig_height, new_width, new_height,
+ prpl_formats[i], quality, length);
if (len)
*len = length;
g_strfreev(prpl_formats);
@@ -2527,27 +2451,12 @@ pidgin_convert_buddy_icon(PurplePlugin *plugin, const char *path, size_t *len)
tmp = g_strdup_printf(_("The file '%s' is too large for %s. Please try a smaller image.\n"),
path, plugin->info->name);
- purple_notify_error(NULL, _("Icon Error"), _("Could not set icon"), tmp);
+ purple_notify_error(NULL, _("Icon Error"), _("Could not set icon"), tmp, NULL);
g_free(tmp);
return NULL;
}
-void pidgin_set_custom_buddy_icon(PurpleAccount *account, const char *who, const char *filename)
-{
- PurpleBuddy *buddy;
- PurpleContact *contact;
-
- buddy = purple_find_buddy(account, who);
- if (!buddy) {
- purple_debug_info("custom-icon", "You can only set custom icon for someone in your buddylist.\n");
- return;
- }
-
- contact = purple_buddy_get_contact(buddy);
- purple_buddy_icons_node_set_custom_icon_from_file((PurpleBlistNode*)contact, filename);
-}
-
char *pidgin_make_pretty_arrows(const char *str)
{
char *ret;
@@ -2572,8 +2481,6 @@ void pidgin_set_urgent(GtkWindow *window, gboolean urgent)
#endif
}
-static GSList *minidialogs = NULL;
-
static void *
pidgin_utils_get_handle(void)
{
@@ -2598,12 +2505,6 @@ static void alert_killed_cb(GtkWidget *widget)
minidialogs = g_slist_remove(minidialogs, widget);
}
-struct _old_button_clicked_cb_data
-{
- PidginUtilMiniDialogCallback cb;
- gpointer data;
-};
-
static void
old_mini_dialog_button_clicked_cb(PidginMiniDialog *mini_dialog,
GtkButton *button,
@@ -2725,8 +2626,8 @@ gboolean pidgin_tree_view_search_equal_func(GtkTreeModel *model, gint column,
if (g_ascii_strcasecmp(key, "Global Thermonuclear War") == 0)
{
- purple_notify_info(NULL, "WOPR",
- "Wouldn't you prefer a nice game of chess?", NULL);
+ purple_notify_info(NULL, "WOPR", "Wouldn't you prefer a nice "
+ "game of chess?", NULL, NULL);
return FALSE;
}
@@ -2884,7 +2785,7 @@ const char *pidgin_get_dim_grey_string(GtkWidget *widget) {
if (!widget)
return "dim grey";
- style = gtk_widget_get_style(widget);
+ style = gtk_widget_get_style(widget);
if (!style)
return "dim grey";
@@ -2896,18 +2797,18 @@ const char *pidgin_get_dim_grey_string(GtkWidget *widget) {
}
static void
-combo_box_changed_cb(GtkComboBox *combo_box, GtkEntry *entry)
+combo_box_changed_cb(GtkComboBoxText *combo_box, GtkEntry *entry)
{
- char *text = gtk_combo_box_get_active_text(combo_box);
+ char *text = gtk_combo_box_text_get_active_text(combo_box);
gtk_entry_set_text(entry, text ? text : "");
g_free(text);
}
static gboolean
-entry_key_pressed_cb(GtkWidget *entry, GdkEventKey *key, GtkComboBox *combo)
+entry_key_pressed_cb(GtkWidget *entry, GdkEventKey *key, GtkComboBoxText *combo)
{
- if (key->keyval == GDK_Down || key->keyval == GDK_Up) {
- gtk_combo_box_popup(combo);
+ if (key->keyval == GDK_KEY_Down || key->keyval == GDK_KEY_Up) {
+ gtk_combo_box_popup(GTK_COMBO_BOX(combo));
return TRUE;
}
return FALSE;
@@ -2916,12 +2817,17 @@ entry_key_pressed_cb(GtkWidget *entry, GdkEventKey *key, GtkComboBox *combo)
GtkWidget *
pidgin_text_combo_box_entry_new(const char *default_item, GList *items)
{
- GtkComboBox *ret = NULL;
+ GtkComboBoxText *ret = NULL;
GtkWidget *the_entry = NULL;
+#if GTK_CHECK_VERSION(2,24,0)
+ ret = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new_with_entry());
+ the_entry = gtk_bin_get_child(GTK_BIN(ret));
+#else
ret = GTK_COMBO_BOX(gtk_combo_box_entry_new_text());
the_entry = gtk_entry_new();
gtk_container_add(GTK_CONTAINER(ret), the_entry);
+#endif
if (default_item)
gtk_entry_set_text(GTK_ENTRY(the_entry), default_item);
@@ -2929,7 +2835,7 @@ pidgin_text_combo_box_entry_new(const char *default_item, GList *items)
for (; items != NULL ; items = items->next) {
char *text = items->data;
if (text && *text)
- gtk_combo_box_append_text(ret, text);
+ gtk_combo_box_text_append_text(ret, text);
}
g_signal_connect(G_OBJECT(ret), "changed", (GCallback)combo_box_changed_cb, the_entry);
@@ -2940,12 +2846,12 @@ pidgin_text_combo_box_entry_new(const char *default_item, GList *items)
const char *pidgin_text_combo_box_entry_get_text(GtkWidget *widget)
{
- return gtk_entry_get_text(GTK_ENTRY(GTK_BIN((widget))->child));
+ return gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((widget)))));
}
void pidgin_text_combo_box_entry_set_text(GtkWidget *widget, const char *text)
{
- gtk_entry_set_text(GTK_ENTRY(GTK_BIN((widget))->child), (text));
+ gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((widget)))), (text));
}
GtkWidget *
@@ -2955,7 +2861,7 @@ pidgin_add_widget_to_vbox(GtkBox *vbox, const char *widget_label, GtkSizeGroup *
GtkWidget *label = NULL;
if (widget_label) {
- hbox = gtk_hbox_new(FALSE, 5);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
gtk_widget_show(hbox);
gtk_box_pack_start(vbox, hbox, FALSE, FALSE, 0);
@@ -2974,7 +2880,7 @@ pidgin_add_widget_to_vbox(GtkBox *vbox, const char *widget_label, GtkSizeGroup *
gtk_box_pack_start(GTK_BOX(hbox), widget, expand, TRUE, 0);
if (label) {
gtk_label_set_mnemonic_widget(GTK_LABEL(label), widget);
- pidgin_set_accessible_label (widget, label);
+ pidgin_set_accessible_label(widget, GTK_LABEL(label));
}
if (p_label)
@@ -3012,7 +2918,7 @@ gboolean pidgin_auto_parent_window(GtkWidget *widget)
windows = g_list_delete_link(windows, windows);
if (window == widget ||
- !GTK_WIDGET_VISIBLE(window))
+ !gtk_widget_get_visible(window))
continue;
if (!gdk_property_get(window->window, _WindowTime, _Cardinal, 0, sizeof(time_t), FALSE,
@@ -3042,9 +2948,27 @@ gboolean pidgin_auto_parent_window(GtkWidget *widget)
#else
/* This finds the currently active window and makes that the parent window. */
GList *windows = NULL;
- GtkWidget *parent = NULL;
+ GtkWindow *parent = NULL;
GdkEvent *event = gtk_get_current_event();
GdkWindow *menu = NULL;
+ gpointer parent_from;
+ PurpleNotifyType notify_type;
+
+ parent_from = g_object_get_data(G_OBJECT(widget), "pidgin-parent-from");
+ if (purple_request_is_valid_ui_handle(parent_from, NULL)) {
+
+ gtk_window_set_transient_for(GTK_WINDOW(widget),
+ gtk_window_get_transient_for(
+ pidgin_request_get_dialog_window(parent_from)));
+ return TRUE;
+ }
+ if (purple_notify_is_valid_ui_handle(parent_from, &notify_type) &&
+ notify_type == PURPLE_NOTIFY_MESSAGE)
+ {
+ gtk_window_set_transient_for(GTK_WINDOW(widget),
+ gtk_window_get_transient_for(GTK_WINDOW(parent_from)));
+ return TRUE;
+ }
if (event == NULL)
/* The window was not triggered by a user action. */
@@ -3062,16 +2986,23 @@ gboolean pidgin_auto_parent_window(GtkWidget *widget)
windows = gtk_window_list_toplevels();
while (windows) {
- GtkWidget *window = windows->data;
+ GtkWindow *window = GTK_WINDOW(windows->data);
windows = g_list_delete_link(windows, windows);
- if (window == widget ||
- !GTK_WIDGET_VISIBLE(window)) {
+ if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(window),
+ "pidgin-window-is-closing")))
+ {
+ parent = gtk_window_get_transient_for(window);
+ break;
+ }
+
+ if (GTK_WIDGET(window) == widget ||
+ !gtk_widget_get_visible(GTK_WIDGET(window))) {
continue;
}
- if (gtk_window_has_toplevel_focus(GTK_WINDOW(window)) ||
- (menu && menu == window->window)) {
+ if (gtk_window_has_toplevel_focus(window) ||
+ (menu && menu == gtk_widget_get_window(GTK_WIDGET(window)))) {
parent = window;
break;
}
@@ -3079,7 +3010,7 @@ gboolean pidgin_auto_parent_window(GtkWidget *widget)
if (windows)
g_list_free(windows);
if (parent) {
- gtk_window_set_transient_for(GTK_WINDOW(widget), GTK_WINDOW(parent));
+ gtk_window_set_transient_for(GTK_WINDOW(widget), parent);
return TRUE;
}
return FALSE;
@@ -3096,7 +3027,7 @@ static GObject *pidgin_pixbuf_from_data_helper(const guchar *buf, gsize count, g
if (!gdk_pixbuf_loader_write(loader, buf, count, &error) || error) {
purple_debug_warning("gtkutils", "gdk_pixbuf_loader_write() "
- "failed with size=%zu: %s\n", count,
+ "failed with size=%" G_GSIZE_FORMAT ": %s\n", count,
error ? error->message : "(no error message)");
if (error)
g_error_free(error);
@@ -3106,7 +3037,7 @@ static GObject *pidgin_pixbuf_from_data_helper(const guchar *buf, gsize count, g
if (!gdk_pixbuf_loader_close(loader, &error) || error) {
purple_debug_warning("gtkutils", "gdk_pixbuf_loader_close() "
- "failed for image of size %zu: %s\n", count,
+ "failed for image of size %" G_GSIZE_FORMAT ": %s\n", count,
error ? error->message : "(no error message)");
if (error)
g_error_free(error);
@@ -3120,7 +3051,7 @@ static GObject *pidgin_pixbuf_from_data_helper(const guchar *buf, gsize count, g
pixbuf = G_OBJECT(gdk_pixbuf_loader_get_pixbuf(loader));
if (!pixbuf) {
purple_debug_warning("gtkutils", "%s() returned NULL for image "
- "of size %zu\n",
+ "of size %" G_GSIZE_FORMAT "\n",
animated ? "gdk_pixbuf_loader_get_animation"
: "gdk_pixbuf_loader_get_pixbuf", count);
g_object_unref(G_OBJECT(loader));
@@ -3143,10 +3074,11 @@ GdkPixbufAnimation *pidgin_pixbuf_anim_from_data(const guchar *buf, gsize count)
return GDK_PIXBUF_ANIMATION(pidgin_pixbuf_from_data_helper(buf, count, TRUE));
}
-GdkPixbuf *pidgin_pixbuf_from_imgstore(PurpleStoredImage *image)
+GdkPixbuf *
+pidgin_pixbuf_from_image(PurpleImage *image)
{
- return pidgin_pixbuf_from_data(purple_imgstore_get_data(image),
- purple_imgstore_get_size(image));
+ return pidgin_pixbuf_from_data(purple_image_get_data(image),
+ purple_image_get_size(image));
}
GdkPixbuf *pidgin_pixbuf_new_from_file(const gchar *filename)
@@ -3154,6 +3086,9 @@ GdkPixbuf *pidgin_pixbuf_new_from_file(const gchar *filename)
GdkPixbuf *pixbuf;
GError *error = NULL;
+ g_return_val_if_fail(filename != NULL, NULL);
+ g_return_val_if_fail(filename[0] != '\0', NULL);
+
pixbuf = gdk_pixbuf_new_from_file(filename, &error);
if (!pixbuf || error) {
purple_debug_warning("gtkutils", "gdk_pixbuf_new_from_file() "
@@ -3176,6 +3111,9 @@ GdkPixbuf *pidgin_pixbuf_new_from_file_at_size(const char *filename, int width,
GdkPixbuf *pixbuf;
GError *error = NULL;
+ g_return_val_if_fail(filename != NULL, NULL);
+ g_return_val_if_fail(filename[0] != '\0', NULL);
+
pixbuf = gdk_pixbuf_new_from_file_at_size(filename,
width, height, &error);
if (!pixbuf || error) {
@@ -3199,6 +3137,9 @@ GdkPixbuf *pidgin_pixbuf_new_from_file_at_scale(const char *filename, int width,
GdkPixbuf *pixbuf;
GError *error = NULL;
+ g_return_val_if_fail(filename != NULL, NULL);
+ g_return_val_if_fail(filename[0] != '\0', NULL);
+
pixbuf = gdk_pixbuf_new_from_file_at_scale(filename,
width, height, preserve_aspect_ratio, &error);
if (!pixbuf || error) {
@@ -3217,7 +3158,55 @@ GdkPixbuf *pidgin_pixbuf_new_from_file_at_scale(const char *filename, int width,
return pixbuf;
}
-static void url_copy(GtkWidget *w, gchar *url)
+GdkPixbuf *
+pidgin_pixbuf_scale_down(GdkPixbuf *src, guint max_width, guint max_height,
+ GdkInterpType interp_type, gboolean preserve_ratio)
+{
+ guint cur_w, cur_h;
+ GdkPixbuf *dst;
+
+ g_return_val_if_fail(src != NULL, NULL);
+
+ if (max_width == 0 || max_height == 0) {
+ g_object_unref(src);
+ g_return_val_if_reached(NULL);
+ }
+
+ cur_w = gdk_pixbuf_get_width(src);
+ cur_h = gdk_pixbuf_get_height(src);
+
+ if (cur_w <= max_width && cur_h <= max_height)
+ return src;
+
+ /* cur_ratio = cur_w / cur_h
+ * max_ratio = max_w / max_h
+ */
+
+ if (!preserve_ratio) {
+ cur_w = MIN(cur_w, max_width);
+ cur_h = MIN(cur_h, max_height);
+ } else if ((guint64)cur_w * max_height > (guint64)max_width * cur_h) {
+ /* cur_w / cur_h > max_width / max_height */
+ cur_h = (guint64)max_width * cur_h / cur_w;
+ cur_w = max_width;
+ } else {
+ cur_w = (guint64)max_height * cur_w / cur_h;
+ cur_h = max_height;
+ }
+
+ if (cur_w <= 0)
+ cur_w = 1;
+ if (cur_h <= 0)
+ cur_h = 1;
+
+ dst = gdk_pixbuf_scale_simple(src, cur_w, cur_h, interp_type);
+ g_object_unref(src);
+
+ return dst;
+}
+
+static void
+url_copy(GtkWidget *w, gchar *url)
{
GtkClipboard *clipboard;
@@ -3229,58 +3218,68 @@ static void url_copy(GtkWidget *w, gchar *url)
}
static gboolean
-link_context_menu(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu)
+link_context_menu(PidginWebView *webview, WebKitDOMHTMLAnchorElement *link, GtkWidget *menu)
{
GtkWidget *img, *item;
- const char *url;
+ char *url;
- url = gtk_imhtml_link_get_url(link);
+ 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(gtk_imhtml_link_activate), link);
+ 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);
- g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(url_copy), (gpointer)url);
+ /* 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(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu)
+copy_email_address(PidginWebView *webview, WebKitDOMHTMLAnchorElement *link, GtkWidget *menu)
{
GtkWidget *img, *item;
- const char *text;
+ char *text;
char *address;
#define MAILTOSIZE (sizeof("mailto:") - 1)
- text = gtk_imhtml_link_get_url(link);
- g_return_val_if_fail(text && strlen(text) > MAILTOSIZE, FALSE);
- address = (char*)text + MAILTOSIZE;
+ 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(G_OBJECT(item), "activate", G_CALLBACK(url_copy), address);
+ 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;
}
-/**
- * @param filename The path to a file. Specifically this is the link target
+/*
+ * 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(GtkIMHtml *imhtml, const char *filename)
+open_file(PidginWebView *webview, const char *filename)
{
/* Copied from gtkft.c:open_button_cb */
#ifdef _WIN32
@@ -3300,13 +3299,14 @@ open_file(GtkIMHtml *imhtml, const char *filename)
if (code == SE_ERR_ASSOCINCOMPLETE || code == SE_ERR_NOASSOC)
{
- purple_notify_error(imhtml, NULL,
- _("There is no application configured to open this type of file."), NULL);
+ 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(imhtml, NULL,
- _("An error occurred while opening the file."), NULL);
+ 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);
}
@@ -3344,7 +3344,7 @@ open_file(GtkIMHtml *imhtml, const char *filename)
{
tmp = g_strdup_printf(_("Error launching %s: %s"),
filename, error->message);
- purple_notify_error(imhtml, NULL, _("Unable to open file."), tmp);
+ purple_notify_error(webview, NULL, _("Unable to open file."), tmp, NULL);
g_free(tmp);
g_error_free(error);
}
@@ -3353,7 +3353,7 @@ open_file(GtkIMHtml *imhtml, const char *filename)
char *primary = g_strdup_printf(_("Error running %s"), command);
char *secondary = g_strdup_printf(_("Process returned error code %d"),
exit_status);
- purple_notify_error(imhtml, NULL, primary, secondary);
+ purple_notify_error(webview, NULL, primary, secondary, NULL);
g_free(tmp);
}
}
@@ -3362,44 +3362,46 @@ open_file(GtkIMHtml *imhtml, const char *filename)
#define FILELINKSIZE (sizeof("file://") - 1)
static gboolean
-file_clicked_cb(GtkIMHtml *imhtml, GtkIMHtmlLink *link)
+file_clicked_cb(PidginWebView *webview, const char *uri)
{
/* Strip "file://" from the URI. */
- const char *filename = gtk_imhtml_link_get_url(link) + FILELINKSIZE;
- open_file(imhtml, filename);
+ open_file(webview, uri + FILELINKSIZE);
return TRUE;
}
static gboolean
-open_containing_cb(GtkIMHtml *imhtml, const char *url)
+open_containing_cb(PidginWebView *webview, const char *uri)
{
- char *dir = g_path_get_dirname(url + FILELINKSIZE);
- open_file(imhtml, dir);
+ char *dir = g_path_get_dirname(uri + FILELINKSIZE);
+ open_file(webview, dir);
g_free(dir);
return TRUE;
}
static gboolean
-file_context_menu(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu)
+file_context_menu(PidginWebView *webview, WebKitDOMHTMLAnchorElement *link, GtkWidget *menu)
{
GtkWidget *img, *item;
- const char *url;
+ char *url;
- url = gtk_imhtml_link_get_url(link);
+ 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(gtk_imhtml_link_activate), link);
+ 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);
-
- g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(open_containing_cb), (gpointer)url);
+ /* 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;
@@ -3407,14 +3409,12 @@ file_context_menu(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu)
#define AUDIOLINKSIZE (sizeof("audio://") - 1)
static gboolean
-audio_clicked_cb(GtkIMHtml *imhtml, GtkIMHtmlLink *link)
+audio_clicked_cb(PidginWebView *webview, const char *uri)
{
- const char *uri;
- PidginConversation *conv = g_object_get_data(G_OBJECT(imhtml), "gtkconv");
+ PidginConversation *conv = g_object_get_data(G_OBJECT(webview), "gtkconv");
if (!conv) /* no playback in debug window */
return TRUE;
- uri = gtk_imhtml_link_get_url(link) + AUDIOLINKSIZE;
- purple_sound_play_file(uri, NULL);
+ purple_sound_play_file(uri + AUDIOLINKSIZE, NULL);
return TRUE;
}
@@ -3430,8 +3430,10 @@ savefile_write_cb(gpointer user_data, char *file)
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",
@@ -3442,56 +3444,61 @@ savefile_write_cb(gpointer user_data, char *file)
static gboolean
save_file_cb(GtkWidget *item, const char *url)
{
- PidginConversation *conv = g_object_get_data(G_OBJECT(item), "gtkconv");
- if (!conv)
+ PidginConversation *gtkconv = g_object_get_data(G_OBJECT(item), "gtkconv");
+ PurpleConversation *conv;
+ if (!gtkconv)
return TRUE;
- purple_request_file(conv->active_conv, _("Save File"), NULL, TRUE,
- G_CALLBACK(savefile_write_cb), NULL,
- conv->active_conv->account, NULL, conv->active_conv,
- (void *)url);
+ 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(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu)
+audio_context_menu(PidginWebView *webview, WebKitDOMHTMLAnchorElement *link, GtkWidget *menu)
{
GtkWidget *img, *item;
- const char *url;
- PidginConversation *conv = g_object_get_data(G_OBJECT(imhtml), "gtkconv");
+ char *url;
+ PidginConversation *conv = g_object_get_data(G_OBJECT(webview), "gtkconv");
if (!conv) /* No menu in debug window */
return TRUE;
- url = gtk_imhtml_link_get_url(link);
+ 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(gtk_imhtml_link_activate), link);
+ 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(G_OBJECT(item), "activate", G_CALLBACK(save_file_cb), (gpointer)(url+AUDIOLINKSIZE));
+ 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(GtkIMHtml *imhtml, GtkIMHtmlLink *link)
+open_dialog(PidginWebView *webview, const char *url)
{
- const char *url;
const char *str;
- url = gtk_imhtml_link_get_url(link);
- if (!url || strlen(url) < sizeof("open://"))
+ if (!url || strlen(url) < sizeof("open://")) {
return FALSE;
+ }
str = url + sizeof("open://") - 1;
@@ -3505,7 +3512,7 @@ open_dialog(GtkIMHtml *imhtml, GtkIMHtmlLink *link)
}
static gboolean
-dummy(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu)
+dummy(PidginWebView *webview, WebKitDOMHTMLAnchorElement *link, GtkWidget *menu)
{
return TRUE;
}
@@ -3570,7 +3577,7 @@ register_gnome_url_handlers(void)
protocol = g_strdup_printf("%s:", start);
registered_url_handlers = g_slist_prepend(registered_url_handlers, protocol);
- gtk_imhtml_class_register_protocol(protocol, url_clicked_cb, link_context_menu);
+ pidgin_webview_class_register_protocol(protocol, url_clicked_cb, link_context_menu);
}
start = c + 1;
}
@@ -3603,7 +3610,7 @@ winpidgin_register_win32_url_handlers(void)
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 */
- gtk_imhtml_class_register_protocol(protocol, url_clicked_cb, link_context_menu);
+ pidgin_webview_class_register_protocol(protocol, url_clicked_cb, link_context_menu);
}
RegCloseKey(reg_key);
}
@@ -3629,10 +3636,18 @@ pidgin_make_scrollable(GtkWidget *child, GtkPolicyType hscrollbar_policy, GtkPol
if (width != -1 || height != -1)
gtk_widget_set_size_request(sw, width, height);
if (child) {
+#if GTK_CHECK_VERSION(3,8,0)
+ gtk_container_add(GTK_CONTAINER(sw), child);
+#else
+#if GTK_CHECK_VERSION(3,0,0)
+ if (GTK_IS_SCROLLABLE(child))
+#else
if (GTK_WIDGET_GET_CLASS(child)->set_scroll_adjustments_signal)
+#endif /* GTK_CHECK_VERSION(3,0,0) */
gtk_container_add(GTK_CONTAINER(sw), child);
else
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw), child);
+#endif /* GTK_CHECK_VERSION(3,8,0) */
}
return sw;
}
@@ -3642,23 +3657,24 @@ pidgin_make_scrollable(GtkWidget *child, GtkPolicyType hscrollbar_policy, GtkPol
void pidgin_utils_init(void)
{
- gtk_imhtml_class_register_protocol("http://", url_clicked_cb, link_context_menu);
- gtk_imhtml_class_register_protocol("https://", url_clicked_cb, link_context_menu);
- gtk_imhtml_class_register_protocol("ftp://", url_clicked_cb, link_context_menu);
- gtk_imhtml_class_register_protocol("gopher://", url_clicked_cb, link_context_menu);
- gtk_imhtml_class_register_protocol("mailto:", url_clicked_cb, copy_email_address);
+ 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);
- gtk_imhtml_class_register_protocol("file://", file_clicked_cb, file_context_menu);
- gtk_imhtml_class_register_protocol("audio://", audio_clicked_cb, audio_context_menu);
+ 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. */
- gtk_imhtml_class_register_protocol("open://", open_dialog, dummy);
+ pidgin_webview_class_register_protocol("open://", open_dialog, dummy);
/* If we're under GNOME, try registering the system URL handlers. */
if (purple_running_gnome())
register_gnome_url_handlers();
/* Used to make small buttons */
+#if !GTK_CHECK_VERSION(3,0,0)
gtk_rc_parse_string("style \"pidgin-small-close-button\"\n"
"{\n"
"GtkWidget::focus-padding = 0\n"
@@ -3670,6 +3686,7 @@ void pidgin_utils_init(void)
"GtkButton::default-border = {0, 0, 0, 0}\n"
"}\n"
"widget \"*.pidgin-small-close-button\" style \"pidgin-small-close-button\"");
+#endif
#ifdef _WIN32
winpidgin_register_win32_url_handlers();
@@ -3679,7 +3696,7 @@ void pidgin_utils_init(void)
void pidgin_utils_uninit(void)
{
- gtk_imhtml_class_register_protocol("open://", NULL, NULL);
+ pidgin_webview_class_register_protocol("open://", NULL, NULL);
/* If we have GNOME handlers registered, unregister them. */
if (registered_url_handlers)
@@ -3687,7 +3704,7 @@ void pidgin_utils_uninit(void)
GSList *l;
for (l = registered_url_handlers; l; l = l->next)
{
- gtk_imhtml_class_register_protocol((char *)l->data, NULL, NULL);
+ pidgin_webview_class_register_protocol((char *)l->data, NULL, NULL);
g_free(l->data);
}
g_slist_free(registered_url_handlers);
@@ -3695,13 +3712,12 @@ void pidgin_utils_uninit(void)
return;
}
- gtk_imhtml_class_register_protocol("audio://", NULL, NULL);
- gtk_imhtml_class_register_protocol("file://", NULL, NULL);
+ pidgin_webview_class_register_protocol("audio://", NULL, NULL);
+ pidgin_webview_class_register_protocol("file://", NULL, NULL);
- gtk_imhtml_class_register_protocol("http://", NULL, NULL);
- gtk_imhtml_class_register_protocol("https://", NULL, NULL);
- gtk_imhtml_class_register_protocol("ftp://", NULL, NULL);
- gtk_imhtml_class_register_protocol("mailto:", NULL, NULL);
- gtk_imhtml_class_register_protocol("gopher://", 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);
}
-
diff --git a/pidgin/gtkutils.h b/pidgin/gtkutils.h
index 716ec6de81..5ea13bd19a 100644
--- a/pidgin/gtkutils.h
+++ b/pidgin/gtkutils.h
@@ -1,8 +1,3 @@
-/**
- * @file gtkutils.h GTK+ utility functions
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -23,18 +18,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 _PIDGINUTILS_H_
#define _PIDGINUTILS_H_
+/**
+ * SECTION:gtkutils
+ * @section_id: pidgin-gtkutils
+ * @short_description: <filename>gtkutils.h</filename>
+ * @title: Utility functions
+ */
#include "gtkconv.h"
#include "pidgin.h"
#include "prpl.h"
#include "util.h"
-
-
-
-
typedef enum
{
PIDGIN_BUTTON_HORIZONTAL,
@@ -62,8 +60,8 @@ typedef enum
typedef enum
{
PIDGIN_BROWSER_DEFAULT = 0,
- PIDGIN_BROWSER_CURRENT,
- PIDGIN_BROWSER_NEW_WINDOW,
+ /* value '1' was used by PIDGIN_BROWSER_CURRENT, which no longer exists */
+ PIDGIN_BROWSER_NEW_WINDOW = 2,
PIDGIN_BROWSER_NEW_TAB
} PidginBrowserPlace;
@@ -80,192 +78,197 @@ typedef struct {
typedef gboolean (*PidginFilterBuddyCompletionEntryFunc) (const PidginBuddyCompletionEntry *completion_entry, gpointer user_data);
+G_BEGIN_DECLS
+
/**
- * Sets up a gtkimhtml widget, loads it with smileys, and sets the
- * default signal handlers.
+ * pidgin_setup_webview:
+ * @webview: The gtkwebview widget to setup.
*
- * @param imhtml The gtkimhtml widget to setup.
+ * Sets up a gtkwebview widget and sets the default signal handlers.
*/
-void pidgin_setup_imhtml(GtkWidget *imhtml);
+void pidgin_setup_webview(GtkWidget *webview);
/**
- * Create an GtkIMHtml widget and associated GtkIMHtmlToolbar widget. This
- * functions puts both widgets in a nice GtkFrame. They're separate by an
+ * 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.
*
- * @param editable @c TRUE if this imhtml should be editable. If this is @c FALSE,
- * then the toolbar will NOT be created. If this imthml should be
- * read-only at first, but may become editable later, then pass in
- * @c TRUE here and then manually call gtk_imhtml_set_editable() later.
- * @param imhtml_ret A pointer to a pointer to a GtkWidget. This pointer
- * will be set to the imhtml when this function exits.
- * @param toolbar_ret A pointer to a pointer to a GtkWidget. If editable is
- * TRUE then this will be set to the toolbar when this function exits.
- * Otherwise this will be set to @c NULL.
- * @param sw_ret This will be filled with a pointer to the scrolled window
- * widget which contains the imhtml.
- * @return The GtkFrame containing the toolbar and imhtml.
+ * Returns: The GtkFrame containing the toolbar and webview.
*/
-GtkWidget *pidgin_create_imhtml(gboolean editable, GtkWidget **imhtml_ret, GtkWidget **toolbar_ret, GtkWidget **sw_ret);
+GtkWidget *pidgin_create_webview(gboolean editable, GtkWidget **webview_ret, GtkWidget **sw_ret);
/**
- * Creates a small button
+ * pidgin_create_small_button:
+ * @image: A button image.
*
- * @param image A button image.
+ * Creates a small button
*
- * @return A GtkButton created from the image.
- * @since 2.7.0
+ * Returns: A GtkButton created from the image.
*/
GtkWidget *pidgin_create_small_button(GtkWidget *image);
/**
- * Creates a new window
+ * pidgin_create_window:
+ * @title: The window title, or %NULL
+ * @border_width: The window's desired border width
+ * @role: A string indicating what the window is responsible for doing, or %NULL
+ * @resizable: Whether the window should be resizable (%TRUE) or not (%FALSE)
*
- * @param title The window title, or @c NULL
- * @param border_width The window's desired border width
- * @param role A string indicating what the window is responsible for doing, or @c NULL
- * @param resizable Whether the window should be resizable (@c TRUE) or not (@c FALSE)
- *
- * @since 2.1.0
+ * Creates a new window
*/
GtkWidget *pidgin_create_window(const char *title, guint border_width, const char *role, gboolean resizable);
/**
- * Creates a new dialog window
- *
- * @param title The window title, or @c NULL
- * @param border_width The window's desired border width
- * @param role A string indicating what the window is responsible for doing, or @c NULL
- * @param resizable Whether the window should be resizable (@c TRUE) or not (@c FALSE)
+ * pidgin_create_dialog:
+ * @title: The window title, or %NULL
+ * @border_width: The window's desired border width
+ * @role: A string indicating what the window is responsible for doing, or %NULL
+ * @resizable: Whether the window should be resizable (%TRUE) or not (%FALSE)
*
- * @since 2.4.0
+ * Creates a new dialog window
*/
GtkWidget *pidgin_create_dialog(const char *title, guint border_width, const char *role, gboolean resizable);
/**
- * Retrieves the main content box (vbox) from a pidgin dialog window
- *
- * @param dialog The dialog window
- * @param homogeneous TRUE if all children are to be given equal space allotments.
- * @param spacing the number of pixels to place by default between children
+ * pidgin_dialog_get_vbox_with_properties:
+ * @dialog: The dialog window
+ * @homogeneous: TRUE if all children are to be given equal space allotments.
+ * @spacing: the number of pixels to place by default between children
*
- * @since 2.4.0
+ * Retrieves the main content box (vbox) from a pidgin dialog window
*/
GtkWidget *pidgin_dialog_get_vbox_with_properties(GtkDialog *dialog, gboolean homogeneous, gint spacing);
/**
- * Retrieves the main content box (vbox) from a pidgin dialog window
- *
- * @param dialog The dialog window
+ * pidgin_dialog_get_vbox:
+ * @dialog: The dialog window
*
- * @since 2.4.0
+ * Retrieves the main content box (vbox) from a pidgin dialog window
*/
GtkWidget *pidgin_dialog_get_vbox(GtkDialog *dialog);
/**
- * Add a button to a dialog created by #pidgin_create_dialog.
+ * pidgin_dialog_add_button:
+ * @dialog: The dialog window
+ * @label: The stock-id or the label for the button
+ * @callback: The callback function for the button
+ * @callbackdata: The user data for the callback function
*
- * @param dialog The dialog window
- * @param label The stock-id or the label for the button
- * @param callback The callback function for the button
- * @param callbackdata The user data for the callback function
+ * Add a button to a dialog created by #pidgin_create_dialog.
*
- * @return The created button.
- * @since 2.4.0
+ * Returns: The created button.
*/
GtkWidget *pidgin_dialog_add_button(GtkDialog *dialog, const char *label,
GCallback callback, gpointer callbackdata);
/**
- * Retrieves the action area (button box) from a pidgin dialog window
- *
- * @param dialog The dialog window
+ * pidgin_dialog_get_action_area:
+ * @dialog: The dialog window
*
- * @since 2.4.0
+ * Retrieves the action area (button box) from a pidgin dialog window
*/
GtkWidget *pidgin_dialog_get_action_area(GtkDialog *dialog);
/**
- * Toggles the sensitivity of a widget.
+ * pidgin_toggle_sensitive:
+ * @widget: %NULL. Used for signal handlers.
+ * @to_toggle: The widget to toggle.
*
- * @param widget @c NULL. Used for signal handlers.
- * @param to_toggle The widget to toggle.
+ * Toggles the sensitivity of a widget.
*/
void pidgin_toggle_sensitive(GtkWidget *widget, GtkWidget *to_toggle);
/**
+ * pidgin_set_sensitive_if_input:
+ * @entry: The text entry widget.
+ * @dialog: The dialog containing the text entry widget.
+ *
* Checks if text has been entered into a GtkTextEntry widget. If
* so, the GTK_RESPONSE_OK on the given dialog is set to TRUE.
* Otherwise GTK_RESPONSE_OK is set to FALSE.
- *
- * @param entry The text entry widget.
- * @param dialog The dialog containing the text entry widget.
*/
void pidgin_set_sensitive_if_input(GtkWidget *entry, GtkWidget *dialog);
/**
- * Toggles the sensitivity of all widgets in a pointer array.
+ * pidgin_toggle_sensitive_array:
+ * @w: %NULL. Used for signal handlers.
+ * @data: The array containing the widgets to toggle.
*
- * @param w @c NULL. Used for signal handlers.
- * @param data The array containing the widgets to toggle.
+ * Toggles the sensitivity of all widgets in a pointer array.
*/
void pidgin_toggle_sensitive_array(GtkWidget *w, GPtrArray *data);
/**
- * Toggles the visibility of a widget.
+ * pidgin_toggle_showhide:
+ * @widget: %NULL. Used for signal handlers.
+ * @to_toggle: The widget to toggle.
*
- * @param widget @c NULL. Used for signal handlers.
- * @param to_toggle The widget to toggle.
+ * Toggles the visibility of a widget.
*/
void pidgin_toggle_showhide(GtkWidget *widget, GtkWidget *to_toggle);
/**
- * Adds a separator to a menu.
+ * pidgin_separator:
+ * @menu: The menu to add a separator to.
*
- * @param menu The menu to add a separator to.
+ * Adds a separator to a menu.
*
- * @return The separator.
+ * Returns: The separator.
*/
GtkWidget *pidgin_separator(GtkWidget *menu);
/**
- * Creates a menu item.
+ * pidgin_new_item:
+ * @menu: The menu to which to append the menu item.
+ * @str: The title to use for the newly created menu item.
*
- * @param menu The menu to which to append the menu item.
- * @param str The title to use for the newly created menu item.
+ * Creates a menu item.
*
- * @return The newly created menu item.
+ * Returns: The newly created menu item.
*/
GtkWidget *pidgin_new_item(GtkWidget *menu, const char *str);
/**
- * Creates a check menu item.
+ * pidgin_new_check_item:
+ * @menu: The menu to which to append the check menu item.
+ * @str: The title to use for the newly created menu item.
+ * @cb: A function to call when the menu item is activated.
+ * @data: Data to pass to the signal function.
+ * @checked: The initial state of the check item
*
- * @param menu The menu to which to append the check menu item.
- * @param str The title to use for the newly created menu item.
- * @param cb A function to call when the menu item is activated.
- * @param data Data to pass to the signal function.
- * @param checked The initial state of the check item
+ * Creates a check menu item.
*
- * @return The newly created menu item.
+ * Returns: The newly created menu item.
*/
GtkWidget *pidgin_new_check_item(GtkWidget *menu, const char *str,
GCallback cb, gpointer data, gboolean checked);
/**
- * Creates a menu item.
+ * pidgin_new_item_from_stock:
+ * @menu: The menu to which to append the menu item.
+ * @str: The title for the menu item.
+ * @icon: An icon to place to the left of the menu item,
+ * or %NULL for no icon.
+ * @cb: A function to call when the menu item is activated.
+ * @data: Data to pass to the signal function.
+ * @accel_key: Something.
+ * @accel_mods: Something.
+ * @mod: Something.
*
- * @param menu The menu to which to append the menu item.
- * @param str The title for the menu item.
- * @param icon An icon to place to the left of the menu item,
- * or @c NULL for no icon.
- * @param cb A function to call when the menu item is activated.
- * @param data Data to pass to the signal function.
- * @param accel_key Something.
- * @param accel_mods Something.
- * @param mod Something.
+ * Creates a menu item.
*
- * @return The newly created menu item.
+ * Returns: The newly created menu item.
*/
GtkWidget *pidgin_new_item_from_stock(GtkWidget *menu, const char *str,
const char *icon, GCallback cb,
@@ -273,160 +276,132 @@ GtkWidget *pidgin_new_item_from_stock(GtkWidget *menu, const char *str,
guint accel_mods, char *mod);
/**
- * Creates a button with the specified text and stock icon.
+ * pidgin_pixbuf_button_from_stock:
+ * @text: The text for the button.
+ * @icon: The stock icon name.
+ * @style: The orientation of the button.
*
- * @param text The text for the button.
- * @param icon The stock icon name.
- * @param style The orientation of the button.
+ * Creates a button with the specified text and stock icon.
*
- * @return The button.
+ * Returns: The button.
*/
GtkWidget *pidgin_pixbuf_button_from_stock(const char *text, const char *icon,
PidginButtonOrientation style);
/**
- * Creates a toolbar button with the stock icon.
+ * pidgin_pixbuf_toolbar_button_from_stock:
+ * @stock: The stock icon name.
*
- * @param stock The stock icon name.
+ * Creates a toolbar button with the stock icon.
*
- * @return The button.
+ * Returns: The button.
*/
GtkWidget *pidgin_pixbuf_toolbar_button_from_stock(const char *stock);
/**
- * Creates a HIG preferences frame.
+ * pidgin_make_frame:
+ * @parent: The widget to put the frame into.
+ * @title: The title for the frame.
*
- * @param parent The widget to put the frame into.
- * @param title The title for the frame.
+ * Creates a HIG preferences frame.
*
- * @return The vbox to put things into.
+ * Returns: The vbox to put things into.
*/
GtkWidget *pidgin_make_frame(GtkWidget *parent, const char *title);
/**
- * Creates a drop-down option menu filled with protocols.
+ * pidgin_protocol_option_menu_new:
+ * @id: The protocol to select by default.
+ * @cb: The callback to call when a protocol is selected.
+ * @user_data: Data to pass to the callback function.
*
- * @param id The protocol to select by default.
- * @param cb The callback to call when a protocol is selected.
- * @param user_data Data to pass to the callback function.
+ * Creates a drop-down option menu filled with protocols.
*
- * @return The drop-down option menu.
+ * Returns: The drop-down option menu.
*/
GtkWidget *pidgin_protocol_option_menu_new(const char *id,
GCallback cb,
gpointer user_data);
/**
+ * pidgin_protocol_option_menu_get_selected:
+ * @optmenu: The drop-down option menu created by
+ * pidgin_account_option_menu_new.
+ *
* Gets the currently selected protocol from a protocol drop down box.
*
- * @param optmenu The drop-down option menu created by
- * pidgin_account_option_menu_new.
- * @return Returns the protocol ID that is currently selected.
+ * Returns: Returns the protocol ID that is currently selected.
*/
const char *pidgin_protocol_option_menu_get_selected(GtkWidget *optmenu);
/**
- * Creates a drop-down option menu filled with accounts.
- *
- * @param default_account The account to select by default.
- * @param show_all Whether or not to show all accounts, or just
+ * 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.
- * @param cb The callback to call when an account is selected.
- * @param filter_func A function for checking if an account should
+ * @cb: The callback to call when an account is selected.
+ * @filter_func: A function for checking if an account should
* be shown. This can be NULL.
- * @param user_data Data to pass to the callback function.
+ * @user_data: Data to pass to the callback function.
+ *
+ * Creates a drop-down option menu filled with accounts.
*
- * @return The drop-down option menu.
+ * Returns: 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.
*
- * @param optmenu The drop-down option menu created by
- * pidgin_account_option_menu_new.
- * @return Returns the PurpleAccount that is currently selected.
+ * Returns: Returns the PurpleAccount that is currently selected.
*/
PurpleAccount *pidgin_account_option_menu_get_selected(GtkWidget *optmenu);
/**
- * Sets the currently selected account for an account drop down box.
- *
- * @param optmenu The GtkOptionMenu created by
+ * pidgin_account_option_menu_set_selected:
+ * @optmenu: The GtkOptionMenu created by
* pidgin_account_option_menu_new.
- * @param account The PurpleAccount to select.
+ * @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);
/**
- * Add autocompletion of screenames to an entry, supporting a filtering function.
- *
- * @param entry The GtkEntry on which to setup autocomplete.
- * @param optmenu A menu for accounts, returned by pidgin_account_option_menu_new().
- * If @a optmenu is not @c NULL, it'll be updated when a username is chosen
+ * 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.
- * @param filter_func A function for checking if an autocomplete entry
- * should be shown. This can be @c NULL.
- * @param user_data The data to be passed to the filter_func function.
- */
-void pidgin_setup_screenname_autocomplete_with_filter(GtkWidget *entry, GtkWidget *optmenu, PidginFilterBuddyCompletionEntryFunc filter_func, gpointer user_data);
-
-/**
- * The default filter function for username autocomplete.
+ * @filter_func: 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.
*
- * @param completion_entry The completion entry to filter.
- * @param all_accounts If this is @c FALSE, only the autocompletion entries
- * which belong to an online account will be filtered.
- * @return Returns @c TRUE if the autocompletion entry is filtered.
- */
-gboolean pidgin_screenname_autocomplete_default_filter(const PidginBuddyCompletionEntry *completion_entry, gpointer all_accounts);
-
-/**
- * Add autocompletion of screenames to an entry.
- *
- * @deprecated
- * For new code, use the equivalent:
- * #pidgin_setup_screenname_autocomplete_with_filter(@a entry, @a optmenu,
- * #pidgin_screenname_autocomplete_default_filter, <tt>GINT_TO_POINTER(@a
- * all)</tt>)
- *
- * @param entry The GtkEntry on which to setup autocomplete.
- * @param optmenu A menu for accounts, returned by
- * pidgin_account_option_menu_new(). If @a optmenu is not @c
- * NULL, it'll be updated when a username is chosen from the
- * autocomplete list.
- * @param all Whether to include usernames from disconnected accounts.
+ * Add autocompletion of screenames to an entry, supporting a filtering function.
*/
-void pidgin_setup_screenname_autocomplete(GtkWidget *entry, GtkWidget *optmenu, gboolean all);
+void pidgin_setup_screenname_autocomplete(GtkWidget *entry, GtkWidget *optmenu, PidginFilterBuddyCompletionEntryFunc filter_func, gpointer user_data);
/**
- * Check if the given path is a directory or not. If it is, then modify
- * the given GtkFileSelection dialog so that it displays the given path.
- * If the given path is not a directory, then do nothing.
+ * pidgin_screenname_autocomplete_default_filter:
+ * @completion_entry: The completion entry to filter.
+ * @all_accounts: If this is %FALSE, only the autocompletion entries
+ * which belong to an online account will be filtered.
*
- * @param path The path entered in the file selection window by the user.
- * @param filesel The file selection window.
+ * The default filter function for username autocomplete.
*
- * @return TRUE if given path is a directory, FALSE otherwise.
- * @deprecated Pidgin no longer uses GtkFileSelection internally. It has also
- * been deprecated by GTK+. Use GtkFileChooser instead and ignore
- * this function.
+ * Returns: Returns %TRUE if the autocompletion entry is filtered.
*/
-gboolean pidgin_check_if_dir(const char *path, GtkFileSelection *filesel);
+gboolean pidgin_screenname_autocomplete_default_filter(const PidginBuddyCompletionEntry *completion_entry, gpointer all_accounts);
/**
- * Sets up GtkSpell for the given GtkTextView, reporting errors
- * if encountered.
- *
- * This does nothing if Pidgin is not compiled with GtkSpell support.
+ * pidgin_save_accels_cb:
*
- * @param textview The textview widget to setup spellchecking for.
- */
-void pidgin_setup_gtkspell(GtkTextView *textview);
-
-/**
* Save menu accelerators callback
*/
void pidgin_save_accels_cb(GtkAccelGroup *accel_group, guint arg1,
@@ -434,50 +409,53 @@ void pidgin_save_accels_cb(GtkAccelGroup *accel_group, guint arg1,
gpointer data);
/**
+ * pidgin_save_accels:
+ *
* Save menu accelerators
*/
gboolean pidgin_save_accels(gpointer data);
/**
+ * pidgin_load_accels:
+ *
* Load menu accelerators
*/
void pidgin_load_accels(void);
/**
- * Get information about a user. Show immediate feedback.
+ * pidgin_retrieve_user_info:
+ * @conn: The connection to get information from.
+ * @name: The user to get information about.
*
- * @param conn The connection to get information from.
- * @param name The user to get information about.
- *
- * @since 2.1.0
+ * Get information about a user. Show immediate feedback.
*/
void pidgin_retrieve_user_info(PurpleConnection *conn, const char *name);
/**
- * Get information about a user in a chat. Show immediate feedback.
+ * pidgin_retrieve_user_info_in_chat:
+ * @conn: The connection to get information from.
+ * @name: The user to get information about.
+ * @chatid: The chat id.
*
- * @param conn The connection to get information from.
- * @param name The user to get information about.
- * @param chatid The chat id.
- *
- * @since 2.1.0
+ * Get information about a user in a chat. Show immediate feedback.
*/
void pidgin_retrieve_user_info_in_chat(PurpleConnection *conn, const char *name, int chatid);
/**
- * Parses an application/x-im-contact MIME message and returns the
- * data inside.
- *
- * @param msg The MIME message.
- * @param all_accounts If TRUE, check all compatible accounts, online or
+ * pidgin_parse_x_im_contact:
+ * @msg: The MIME message.
+ * @all_accounts: If TRUE, check all compatible accounts, online or
* offline. If FALSE, check only online accounts.
- * @param ret_account The best guess at a compatible protocol,
+ * @ret_account: The best guess at a compatible protocol,
* based on ret_protocol. If NULL, no account was found.
- * @param ret_protocol The returned protocol type.
- * @param ret_username The returned username.
- * @param ret_alias The returned alias.
+ * @ret_protocol: The returned protocol type.
+ * @ret_username: The returned username.
+ * @ret_alias: The returned alias.
*
- * @return TRUE if the message was parsed for the minimum necessary data.
+ * Parses an application/x-im-contact MIME message and returns the
+ * data inside.
+ *
+ * Returns: TRUE if the message was parsed for the minimum necessary data.
* FALSE otherwise.
*/
gboolean pidgin_parse_x_im_contact(const char *msg, gboolean all_accounts,
@@ -486,55 +464,55 @@ gboolean pidgin_parse_x_im_contact(const char *msg, gboolean all_accounts,
char **ret_alias);
/**
+ * pidgin_set_accessible_label:
+ * @w: The widget that we want to name.
+ * @l: A GtkLabel that we want to use as the ATK name for the widget.
+ *
* Sets an ATK name for a given widget. Also sets the labelled-by
* and label-for ATK relationships.
- *
- * @param w The widget that we want to name.
- * @param l A GtkLabel that we want to use as the ATK name for the widget.
*/
-void pidgin_set_accessible_label(GtkWidget *w, GtkWidget *l);
+void pidgin_set_accessible_label(GtkWidget *w, GtkLabel *l);
/**
- * Sets the labelled-by and label-for ATK relationships.
- *
- * @param w The widget that we want to label.
- * @param l A GtkLabel that we want to use as the label for the widget.
+ * pidgin_set_accessible_relations:
+ * @w: The widget that we want to label.
+ * @l: A GtkLabel that we want to use as the label for the widget.
*
- * @since 2.2.0
+ * Sets the labelled-by and label-for ATK relationships.
*/
-void pidgin_set_accessible_relations(GtkWidget *w, GtkWidget *l);
+void pidgin_set_accessible_relations(GtkWidget *w, GtkLabel *l);
/**
- * A helper function for GtkMenuPositionFuncs. This ensures the menu will
- * be kept on screen if possible.
- *
- * @param menu The menu we are positioning.
- * @param x Address of the gint representing the horizontal position
+ * pidgin_menu_position_func_helper:
+ * @menu: The menu we are positioning.
+ * @x: Address of the gint representing the horizontal position
* where the menu shall be drawn. This is an output parameter.
- * @param y Address of the gint representing the vertical position
+ * @y: Address of the gint representing the vertical position
* where the menu shall be drawn. This is an output parameter.
- * @param push_in This is an output parameter?
- * @param data Not used by this particular position function.
+ * @push_in: This is an output parameter?
+ * @data: Not used by this particular position function.
*
- * @since 2.1.0
+ * A helper function for GtkMenuPositionFuncs. This ensures the menu will
+ * be kept on screen if possible.
*/
void pidgin_menu_position_func_helper(GtkMenu *menu, gint *x, gint *y,
gboolean *push_in, gpointer data);
/**
+ * pidgin_treeview_popup_menu_position_func:
+ * @menu: The menu we are positioning.
+ * @x: Address of the gint representing the horizontal position
+ * where the menu shall be drawn. This is an output parameter.
+ * @y: Address of the gint representing the vertical position
+ * where the menu shall be drawn. This is an output parameter.
+ * @push_in: This is an output parameter?
+ * @user_data: Not used by this particular position function.
+ *
* A valid GtkMenuPositionFunc. This is used to determine where
* to draw context menus when the menu is activated with the
* keyboard (shift+F10). If the menu is activated with the mouse,
* then you should just use GTK's built-in position function,
* because it does a better job of positioning the menu.
- *
- * @param menu The menu we are positioning.
- * @param x Address of the gint representing the horizontal position
- * where the menu shall be drawn. This is an output parameter.
- * @param y Address of the gint representing the vertical position
- * where the menu shall be drawn. This is an output parameter.
- * @param push_in This is an output parameter?
- * @param user_data Not used by this particular position function.
*/
void pidgin_treeview_popup_menu_position_func(GtkMenu *menu,
gint *x,
@@ -543,27 +521,31 @@ void pidgin_treeview_popup_menu_position_func(GtkMenu *menu,
gpointer user_data);
/**
- * Manages drag'n'drop of files.
+ * pidgin_dnd_file_manage:
+ * @sd: GtkSelectionData for managing drag'n'drop
+ * @account: Account to be used (may be NULL if conv is not NULL)
+ * @who: Buddy name (may be NULL if conv is not NULL)
*
- * @param sd GtkSelectionData for managing drag'n'drop
- * @param account Account to be used (may be NULL if conv is not NULL)
- * @param who Buddy name (may be NULL if conv is not NULL)
+ * Manages drag'n'drop of files.
*/
void pidgin_dnd_file_manage(GtkSelectionData *sd, PurpleAccount *account, const char *who);
/**
+ * pidgin_buddy_icon_get_scale_size:
+ *
* Convenience wrapper for purple_buddy_icon_get_scale_size
*/
void pidgin_buddy_icon_get_scale_size(GdkPixbuf *buf, PurpleBuddyIconSpec *spec, PurpleIconScaleRules rules, int *width, int *height);
/**
+ * pidgin_create_protocol_icon:
+ * @account: The account.
+ * @size: The size of the icon to return.
+ *
* Returns the base image to represent the account, based on
* the currently selected theme.
*
- * @param account The account.
- * @param size The size of the icon to return.
- *
- * @return A newly-created pixbuf with a reference count of 1,
+ * Returns: A newly-created pixbuf with a reference count of 1,
* or NULL if any of several error conditions occurred:
* the file could not be opened, there was no loader
* for the file's format, there was not enough memory
@@ -573,151 +555,154 @@ void pidgin_buddy_icon_get_scale_size(GdkPixbuf *buf, PurpleBuddyIconSpec *spec,
GdkPixbuf *pidgin_create_prpl_icon(PurpleAccount *account, PidginPrplIconSize size);
/**
+ * pidgin_create_status_icon:
+ * @primitive: The status primitive
+ * @w: The widget to render this
+ * @size: The icon size to render at
+ *
* Creates a status icon for a given primitve
*
- * @param primitive The status primitive
- * @param w The widget to render this
- * @param size The icon size to render at
- * @return A GdkPixbuf, created from stock
+ * Returns: A GdkPixbuf, created from stock
*/
GdkPixbuf * pidgin_create_status_icon(PurpleStatusPrimitive primitive, GtkWidget *w, const char *size);
/**
- * Returns an appropriate stock-id for a status primitive.
- *
- * @param prim The status primitive
+ * pidgin_stock_id_from_status_primitive:
+ * @prim: The status primitive
*
- * @return The stock-id
+ * Returns an appropriate stock-id for a status primitive.
*
- * @since 2.6.0
+ * Returns: The stock-id
*/
const char *pidgin_stock_id_from_status_primitive(PurpleStatusPrimitive prim);
/**
- * Returns an appropriate stock-id for a PurplePresence.
+ * pidgin_stock_id_from_presence:
+ * @presence: The presence.
*
- * @param presence The presence.
- *
- * @return The stock-id
+ * Returns an appropriate stock-id for a PurplePresence.
*
- * @since 2.6.0
+ * Returns: The stock-id
*/
const char *pidgin_stock_id_from_presence(PurplePresence *presence);
/**
- * Append a PurpleMenuAction to a menu.
+ * pidgin_append_menu_action:
+ * @menu: The menu to append to.
+ * @act: The PurpleMenuAction to append.
+ * @gobject: The object to be passed to the action callback.
*
- * @param menu The menu to append to.
- * @param act The PurpleMenuAction to append.
- * @param gobject The object to be passed to the action callback.
+ * Append a PurpleMenuAction to a menu.
*
- * @return The menuitem added.
+ * Returns: The menuitem added.
*/
GtkWidget *pidgin_append_menu_action(GtkWidget *menu, PurpleMenuAction *act,
gpointer gobject);
/**
+ * pidgin_set_cursor:
+ * @widget: The widget for which to set the mouse pointer
+ * @cursor_type: The type of cursor to set
+ *
* Sets the mouse pointer for a GtkWidget.
*
* After setting the cursor, the display is flushed, so the change will
* take effect immediately.
*
- * If the window for @a widget is @c NULL, this function simply returns.
- *
- * @param widget The widget for which to set the mouse pointer
- * @param cursor_type The type of cursor to set
+ * If the window for @widget is %NULL, this function simply returns.
*/
void pidgin_set_cursor(GtkWidget *widget, GdkCursorType cursor_type);
/**
+ * pidgin_clear_cursor:
+ *
* Sets the mouse point for a GtkWidget back to that of its parent window.
*
- * If @a widget is @c NULL, this function simply returns.
+ * If @widget is %NULL, this function simply returns.
*
- * If the window for @a widget is @c NULL, this function simply returns.
+ * If the window for @widget is %NULL, this function simply returns.
*
- * @note The display is not flushed from this function.
+ * Note: The display is not flushed from this function.
*/
void pidgin_clear_cursor(GtkWidget *widget);
/**
+ * pidgin_buddy_icon_chooser_new:
+ * @parent: The parent window
+ * @callback: The callback to call when the window is closed. If the user chose an icon, the char* argument will point to its path
+ * @data: Data to pass to @callback
+ *
* Creates a File Selection widget for choosing a buddy icon
*
- * @param parent The parent window
- * @param callback The callback to call when the window is closed. If the user chose an icon, the char* argument will point to its path
- * @param data Data to pass to @a callback
- * @return The file dialog
+ * Returns: The file dialog
*/
GtkWidget *pidgin_buddy_icon_chooser_new(GtkWindow *parent, void(*callback)(const char*,gpointer), gpointer data);
/**
- * Converts a buddy icon to the required size and format
+ * pidgin_convert_buddy_icon:
+ * @plugin: The prpl to convert the icon
+ * @path: The path of a file to convert
+ * @len: If not %NULL, the length of the returned data will be set here.
*
- * @param plugin The prpl to convert the icon
- * @param path The path of a file to convert
- * @param len If not @c NULL, the length of the returned data will be set here.
+ * Converts a buddy icon to the required size and format
*
- * @return The converted image data, or @c NULL if an error occurred.
+ * Returns: The converted image data, or %NULL if an error occurred.
*/
gpointer pidgin_convert_buddy_icon(PurplePlugin *plugin, const char *path, size_t *len);
-#if !(defined PIDGIN_DISABLE_DEPRECATED) || (defined _PIDGIN_GTKUTILS_C_)
/**
- * Set or unset a custom buddyicon for a user.
+ * pidgin_make_pretty_arrows:
+ * @str: The text to convert
*
- * @param account The account the user belongs to.
- * @param who The name of the user.
- * @param filename The path of the custom icon. If this is @c NULL, then any
- * previously set custom buddy icon for the user is removed.
- * @deprecated See purple_buddy_icons_node_set_custom_icon_from_file()
- */
-void pidgin_set_custom_buddy_icon(PurpleAccount *account, const char *who, const char *filename);
-#endif
-
-/**
* Converts "->" and "<-" in strings to Unicode arrow characters, for use in referencing
* menu items.
*
- * @param str The text to convert
- * @return A newly allocated string with unicode arrow characters
+ * Returns: A newly allocated string with unicode arrow characters
*/
char *pidgin_make_pretty_arrows(const char *str);
/**
+ * PidginUtilMiniDialogCallback:
+ *
* The type of callbacks passed to pidgin_make_mini_dialog().
*/
typedef void (*PidginUtilMiniDialogCallback)(gpointer user_data, GtkButton *);
/**
- * Creates a #PidginMiniDialog, tied to a #PurpleConnection, suitable for
- * embedding in the buddy list scrollbook with pidgin_blist_add_alert().
- *
- * @param handle The #PurpleConnection to which this mini-dialog
- * refers, or @c NULL if it does not refer to a
- * connection. If @a handle is supplied, the mini-dialog
+ * pidgin_make_mini_dialog:
+ * @handle: The #PurpleConnection to which this mini-dialog
+ * refers, or %NULL if it does not refer to a
+ * connection. If @handle is supplied, the mini-dialog
* will be automatically removed and destroyed when the
* connection signs off.
- * @param stock_id The ID of a stock image to use in the mini dialog.
- * @param primary The primary text
- * @param secondary The secondary text, or @c NULL for no description.
- * @param user_data Data to pass to the callbacks
- * @param ... a <tt>NULL</tt>-terminated list of button labels
- * (<tt>char *</tt>) and callbacks
- * (#PidginUtilMiniDialogCallback). @a user_data will be
+ * @stock_id: The ID of a stock image to use in the mini dialog.
+ * @primary: The primary text
+ * @secondary: The secondary text, or %NULL for no description.
+ * @user_data: Data to pass to the callbacks
+ * @...: a %NULL-terminated list of button labels
+ * (<type>char *</type>) and callbacks
+ * (#PidginUtilMiniDialogCallback). @user_data will be
* passed as the first argument. (Callbacks may lack a
- * second argument, or be @c NULL to take no action when
+ * second argument, or be %NULL to take no action when
* the corresponding button is pressed.) When a button is
* pressed, the callback (if any) will be called; when
* the callback returns the dialog will be destroyed.
- * @return A #PidginMiniDialog, suitable for passing to
+ *
+ * Creates a #PidginMiniDialog, tied to a #PurpleConnection, suitable for
+ * embedding in the buddy list scrollbook with pidgin_blist_add_alert().
+ *
+ * See <link linkend="pidgin-pidginstock">Stock Resources</link>.
+ *
+ * Returns: A #PidginMiniDialog, suitable for passing to
* pidgin_blist_add_alert().
- * @see pidginstock.h
*/
GtkWidget *pidgin_make_mini_dialog(PurpleConnection *handle,
const char* stock_id, const char *primary, const char *secondary,
void *user_data, ...) G_GNUC_NULL_TERMINATED;
/**
+ * pidgin_make_mini_dialog_with_custom_icon:
+ *
* Does exactly what pidgin_make_mini_dialog() does, except you can specify
* a custom icon for the dialog.
*/
@@ -729,6 +714,8 @@ GtkWidget *pidgin_make_mini_dialog_with_custom_icon(PurpleConnection *gc,
...) G_GNUC_NULL_TERMINATED;
/**
+ * pidgin_tree_view_search_equal_func:
+ *
* This is a callback function to be used for Ctrl+F searching in treeviews.
* Sample Use:
* gtk_tree_view_set_search_equal_func(treeview,
@@ -740,137 +727,140 @@ gboolean pidgin_tree_view_search_equal_func(GtkTreeModel *model, gint column,
const gchar *key, GtkTreeIter *iter, gpointer data);
/**
+ * pidgin_set_urgent:
+ * @window: The window to draw attention to
+ * @urgent: Whether to set the urgent hint or not
+ *
* Sets or resets a window to 'urgent,' by setting the URGENT hint in X
* or blinking in the win32 taskbar
- *
- * @param window The window to draw attention to
- * @param urgent Whether to set the urgent hint or not
*/
void pidgin_set_urgent(GtkWindow *window, gboolean urgent);
/**
+ * pidgin_gdk_pixbuf_is_opaque:
+ * @pixbuf: The pixbug
+ *
* Returns TRUE if the GdkPixbuf is opaque, as determined by no
* alpha at any of the edge pixels.
*
- * @param pixbuf The pixbug
- * @return TRUE if the pixbuf is opaque around the edges, FALSE otherwise
+ * Returns: TRUE if the pixbuf is opaque around the edges, FALSE otherwise
*/
gboolean pidgin_gdk_pixbuf_is_opaque(GdkPixbuf *pixbuf);
/**
- * Rounds the corners of a 32x32 GdkPixbuf in place
+ * pidgin_gdk_pixbuf_make_round:
+ * @pixbuf: The buddy icon to transform
*
- * @param pixbuf The buddy icon to transform
+ * Rounds the corners of a 32x32 GdkPixbuf in place
*/
void pidgin_gdk_pixbuf_make_round(GdkPixbuf *pixbuf);
/**
+ * pidgin_get_dim_grey_string:
+ * @widget: The widget to return dim grey for
+ *
* Returns an HTML-style color string for use as a dim grey
* string
*
- * @param widget The widget to return dim grey for
- * @return The dim grey string
+ * Returns: The dim grey string
*/
const char *pidgin_get_dim_grey_string(GtkWidget *widget);
/**
- * Create a simple text GtkComboBoxEntry equivalent
+ * pidgin_text_combo_box_entry_new:
+ * @default_item: Initial contents of GtkEntry
+ * @items: GList containing strings to add to GtkComboBox
*
- * @param default_item Initial contents of GtkEntry
- * @param items GList containing strings to add to GtkComboBox
+ * Create a simple text GtkComboBoxEntry equivalent
*
- * @return A newly created text GtkComboBox containing a GtkEntry
+ * Returns: A newly created text GtkComboBox containing a GtkEntry
* child.
- *
- * @since 2.2.0
*/
GtkWidget *pidgin_text_combo_box_entry_new(const char *default_item, GList *items);
/**
- * Retrieve the text from the entry of the simple text GtkComboBoxEntry equivalent
- *
- * @param widget The simple text GtkComboBoxEntry equivalent widget
+ * pidgin_text_combo_box_entry_get_text:
+ * @widget: The simple text GtkComboBoxEntry equivalent widget
*
- * @return The text in the widget's entry. It must not be freed
+ * Retrieve the text from the entry of the simple text GtkComboBoxEntry equivalent
*
- * @since 2.2.0
+ * Returns: The text in the widget's entry. It must not be freed
*/
const char *pidgin_text_combo_box_entry_get_text(GtkWidget *widget);
/**
- * Set the text in the entry of the simple text GtkComboBoxEntry equivalent
- *
- * @param widget The simple text GtkComboBoxEntry equivalent widget
- * @param text The text to set
+ * pidgin_text_combo_box_entry_set_text:
+ * @widget: The simple text GtkComboBoxEntry equivalent widget
+ * @text: The text to set
*
- * @since 2.2.0
+ * Set the text in the entry of the simple text GtkComboBoxEntry equivalent
*/
void pidgin_text_combo_box_entry_set_text(GtkWidget *widget, const char *text);
/**
- * Automatically make a window transient to a suitable parent window.
- *
- * @param window The window to make transient.
+ * pidgin_auto_parent_window:
+ * @window: The window to make transient.
*
- * @return Whether the window was made transient or not.
+ * Automatically make a window transient to a suitable parent window.
*
- * @since 2.4.0
+ * Returns: Whether the window was made transient or not.
*/
gboolean pidgin_auto_parent_window(GtkWidget *window);
/**
- * Add a labelled widget to a GtkVBox
+ * pidgin_add_widget_to_vbox:
+ * @vbox: The GtkVBox to add the widget to.
+ * @widget_label: The label to give the widget, can be %NULL.
+ * @sg: The GtkSizeGroup to add the label to, can be %NULL.
+ * @widget: The GtkWidget to add.
+ * @expand: Whether to expand the widget horizontally.
+ * @p_label: Place to store a pointer to the GtkLabel, or %NULL if you don't care.
*
- * @param vbox The GtkVBox to add the widget to.
- * @param widget_label The label to give the widget, can be @c NULL.
- * @param sg The GtkSizeGroup to add the label to, can be @c NULL.
- * @param widget The GtkWidget to add.
- * @param expand Whether to expand the widget horizontally.
- * @param p_label Place to store a pointer to the GtkLabel, or @c NULL if you don't care.
+ * Add a labelled widget to a GtkVBox
*
- * @return A GtkHBox already added to the GtkVBox containing the GtkLabel and the GtkWidget.
- * @since 2.4.0
+ * Returns: A GtkHBox already added to the GtkVBox containing the GtkLabel and the GtkWidget.
*/
GtkWidget *pidgin_add_widget_to_vbox(GtkBox *vbox, const char *widget_label, GtkSizeGroup *sg, GtkWidget *widget, gboolean expand, GtkWidget **p_label);
/**
- * Create a GdkPixbuf from a chunk of image data.
+ * pidgin_pixbuf_from_data:
+ * @buf: The raw binary image data.
+ * @count: The length of buf in bytes.
*
- * @param buf The raw binary image data.
- * @param count The length of buf in bytes.
+ * Create a GdkPixbuf from a chunk of image data.
*
- * @return A GdkPixbuf created from the image data, or NULL if
+ * Returns: A GdkPixbuf created from the image data, or NULL if
* there was an error parsing the data.
- *
- * @since 2.9.0
*/
GdkPixbuf *pidgin_pixbuf_from_data(const guchar *buf, gsize count);
/**
- * Create a GdkPixbufAnimation from a chunk of image data.
+ * pidgin_pixbuf_anim_from_data:
+ * @buf: The raw binary image data.
+ * @count: The length of buf in bytes.
*
- * @param buf The raw binary image data.
- * @param count The length of buf in bytes.
+ * Create a GdkPixbufAnimation from a chunk of image data.
*
- * @return A GdkPixbufAnimation created from the image data, or NULL if
+ * Returns: A GdkPixbufAnimation created from the image data, or NULL if
* there was an error parsing the data.
- *
- * @since 2.9.0
*/
GdkPixbufAnimation *pidgin_pixbuf_anim_from_data(const guchar *buf, gsize count);
/**
- * Create a GdkPixbuf from a PurpleStoredImage.
+ * pidgin_pixbuf_from_image:
+ * @image: a PurpleImage.
*
- * @param image A PurpleStoredImage.
+ * Create a GdkPixbuf from a PurpleImage.
*
- * @return A GdkPixbuf created from the stored image.
- *
- * @since 2.5.0
+ * Returns: a GdkPixbuf created from the @image.
*/
-GdkPixbuf *pidgin_pixbuf_from_imgstore(PurpleStoredImage *image);
+GdkPixbuf *
+pidgin_pixbuf_from_image(PurpleImage *image);
/**
+ * pidgin_pixbuf_new_from_file:
+ * @filename: Name of file to load, in the GLib file name encoding
+ *
* Helper function that calls gdk_pixbuf_new_from_file() and checks both
* the return code and the GError and returns NULL if either one failed.
*
@@ -886,16 +876,17 @@ GdkPixbuf *pidgin_pixbuf_from_imgstore(PurpleStoredImage *image);
* gdk-pixbuf where the aforementioned bug is fixed. However, it might be
* nice to keep this function around for the debug message that it logs.
*
- * @param filename Name of file to load, in the GLib file name encoding
- *
- * @return The GdkPixbuf if successful. Otherwise NULL is returned and
+ * Returns: The GdkPixbuf if successful. Otherwise NULL is returned and
* a warning is logged.
- *
- * @since 2.9.0
*/
GdkPixbuf *pidgin_pixbuf_new_from_file(const char *filename);
/**
+ * pidgin_pixbuf_new_from_file_at_size:
+ * @filename: Name of file to load, in the GLib file name encoding
+ * @width: The width the image should have or -1 to not constrain the width
+ * @height: The height the image should have or -1 to not constrain the height
+ *
* Helper function that calls gdk_pixbuf_new_from_file_at_size() and checks
* both the return code and the GError and returns NULL if either one failed.
*
@@ -911,18 +902,18 @@ GdkPixbuf *pidgin_pixbuf_new_from_file(const char *filename);
* gdk-pixbuf where the aforementioned bug is fixed. However, it might be
* nice to keep this function around for the debug message that it logs.
*
- * @param filename Name of file to load, in the GLib file name encoding
- * @param width The width the image should have or -1 to not constrain the width
- * @param height The height the image should have or -1 to not constrain the height
- *
- * @return The GdkPixbuf if successful. Otherwise NULL is returned and
+ * Returns: The GdkPixbuf if successful. Otherwise NULL is returned and
* a warning is logged.
- *
- * @since 2.9.0
*/
GdkPixbuf *pidgin_pixbuf_new_from_file_at_size(const char *filename, int width, int height);
/**
+ * pidgin_pixbuf_new_from_file_at_scale:
+ * @filename: Name of file to load, in the GLib file name encoding
+ * @width: The width the image should have or -1 to not constrain the width
+ * @height: The height the image should have or -1 to not constrain the height
+ * @preserve_aspect_ratio: TRUE to preserve the image's aspect ratio
+ *
* Helper function that calls gdk_pixbuf_new_from_file_at_scale() and checks
* both the return code and the GError and returns NULL if either one failed.
*
@@ -938,44 +929,59 @@ GdkPixbuf *pidgin_pixbuf_new_from_file_at_size(const char *filename, int width,
* gdk-pixbuf where the aforementioned bug is fixed. However, it might be
* nice to keep this function around for the debug message that it logs.
*
- * @param filename Name of file to load, in the GLib file name encoding
- * @param width The width the image should have or -1 to not constrain the width
- * @param height The height the image should have or -1 to not constrain the height
- * @param preserve_aspect_ratio TRUE to preserve the image's aspect ratio
- *
- * @return The GdkPixbuf if successful. Otherwise NULL is returned and
+ * Returns: The GdkPixbuf if successful. Otherwise NULL is returned and
* a warning is logged.
- *
- * @since 2.9.0
*/
GdkPixbuf *pidgin_pixbuf_new_from_file_at_scale(const char *filename, int width, int height, gboolean preserve_aspect_ratio);
/**
- * Add scrollbars to a widget
- * @param widget The child widget
- * @hscrollbar_policy Horizontal scrolling policy
- * @vscrollbar_policy Vertical scrolling policy
- * @shadow Shadow type
- * @width Desired widget width, or -1 for default
- * @height Desired widget height, or -1 for default
+ * pidgin_pixbuf_scale_down:
+ * @src: The source image.
+ * @max_width: Maximum width in px.
+ * @max_height: Maximum height in px.
+ * @interp_type: Interpolation method.
+ * @preserve_ratio: %TRUE to preserve image's aspect ratio.
*
- * @since 2.8.0
+ * Scales the image to the desired dimensions. If image is smaller, it will be
+ * returned without modifications.
+ *
+ * If new image is created, @src reference cound will be decreased and new image
+ * with a ref count of 1 will be returned.
+ *
+ * Returns: The image with proper sizing. %NULL in case of error.
+ */
+GdkPixbuf *
+pidgin_pixbuf_scale_down(GdkPixbuf *src, guint max_width, guint max_height,
+ GdkInterpType interp_type, gboolean preserve_ratio);
+
+/**
+ * pidgin_make_scrollable:
+ * @child: The child widget
+ * @hscrollbar_policy: Horizontal scrolling policy
+ * @vscrollbar_policy: Vertical scrolling policy
+ * @shadow_type: Shadow type
+ * @width: Desired widget width, or -1 for default
+ * @height: Desired widget height, or -1 for default
+ *
+ * Add scrollbars to a widget
*/
GtkWidget *pidgin_make_scrollable(GtkWidget *child, GtkPolicyType hscrollbar_policy, GtkPolicyType vscrollbar_policy, GtkShadowType shadow_type, int width, int height);
/**
- * Initialize some utility functions.
+ * pidgin_utils_init:
*
- * @since 2.6.0
+ * Initialize some utility functions.
*/
void pidgin_utils_init(void);
/**
- * Uninitialize some utility functions.
+ * pidgin_utils_uninit:
*
- * @since 2.6.0
+ * Uninitialize some utility functions.
*/
void pidgin_utils_uninit(void);
+G_END_DECLS
+
#endif /* _PIDGINUTILS_H_ */
diff --git a/pidgin/gtkwebview.c b/pidgin/gtkwebview.c
new file mode 100644
index 0000000000..be9ab9302c
--- /dev/null
+++ b/pidgin/gtkwebview.c
@@ -0,0 +1,2334 @@
+/* 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 "marshallers.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 */
+
+#define PIDGIN_WEBVIEW_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PIDGIN_TYPE_WEBVIEW, PidginWebViewPriv))
+
+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 _PidginWebViewPriv {
+ /* 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;
+} PidginWebViewPriv;
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+static WebKitWebViewClass *parent_class = NULL;
+
+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);
+ 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)
+ return;
+ } else {
+ purple_debug_warning("webview", "Invalid purple stock "
+ "image domain: %s", domain);
+ return;
+ }
+ } else
+ return;
+
+ if (img != NULL) {
+ path = purple_image_get_path(img);
+ if (path) {
+ gchar *uri = g_filename_to_uri(path, NULL, NULL);
+ webkit_network_request_set_uri(request, uri);
+ g_free(uri);
+ } else {
+ gchar *b64, *src;
+ const gchar *type;
+
+ b64 = purple_base64_encode(
+ purple_image_get_data(img),
+ purple_image_get_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)
+{
+ PidginWebViewPriv *priv = PIDGIN_WEBVIEW_GET_PRIVATE(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)
+{
+ PidginWebViewPriv *priv = PIDGIN_WEBVIEW_GET_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)
+{
+ PidginWebViewPriv *priv = PIDGIN_WEBVIEW_GET_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)
+{
+ PidginWebViewPriv *priv = PIDGIN_WEBVIEW_GET_PRIVATE(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)
+{
+ PidginWebViewPriv *priv = PIDGIN_WEBVIEW_GET_PRIVATE(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, PidginWebViewPriv *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)
+{
+ PidginWebViewPriv *priv = PIDGIN_WEBVIEW_GET_PRIVATE(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)
+{
+ PidginWebViewPriv *priv = PIDGIN_WEBVIEW_GET_PRIVATE(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;
+ PidginWebViewPriv *priv;
+ WebKitDOMDocument *dom;
+ char buf[6];
+
+ priv = PIDGIN_WEBVIEW_GET_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)
+{
+ PidginWebViewPriv *priv = PIDGIN_WEBVIEW_GET_PRIVATE(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, int button, int time, 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(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_item_from_stock(menu, _("Cu_t"), GTK_STOCK_CUT,
+ NULL, NULL, 0, 0, NULL);
+ g_signal_connect_swapped(G_OBJECT(cut), "activate",
+ G_CALLBACK(webkit_web_view_cut_clipboard),
+ webview);
+
+ copy = pidgin_new_item_from_stock(menu, _("_Copy"), GTK_STOCK_COPY,
+ NULL, NULL, 0, 0, NULL);
+ g_signal_connect_swapped(G_OBJECT(copy), "activate",
+ G_CALLBACK(webkit_web_view_copy_clipboard),
+ webview);
+
+ paste = pidgin_new_item_from_stock(menu, _("_Paste"), GTK_STOCK_PASTE,
+ NULL, NULL, 0, 0, NULL);
+ g_signal_connect_swapped(G_OBJECT(paste), "activate",
+ G_CALLBACK(webkit_web_view_paste_clipboard),
+ webview);
+
+ delete = pidgin_new_item_from_stock(menu, _("_Delete"), GTK_STOCK_DELETE,
+ NULL, NULL, 0, 0, NULL);
+ g_signal_connect_swapped(G_OBJECT(delete), "activate",
+ G_CALLBACK(webkit_web_view_delete_selection),
+ webview);
+
+ pidgin_separator(menu);
+
+ select = pidgin_new_item_from_stock(menu, _("Select _All"),
+ GTK_STOCK_SELECT_ALL,
+ NULL, NULL, 0, 0, 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_item_from_stock(menu, _("Inspect _Element"),
+ PIDGIN_STOCK_DEBUG, NULL, NULL, 0, 0, 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(GTK_MENU(menu), NULL, NULL, NULL, NULL, button, time);
+}
+
+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, 0, gtk_get_current_event_time(),
+ context, node, uri);
+
+ g_free(uri);
+
+ return TRUE;
+}
+
+static gboolean
+webview_button_pressed(WebKitWebView *webview, GdkEventButton *event)
+{
+ if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
+ 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, event->button, event->time, 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)
+{
+ PidginWebViewPriv *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)
+{
+ PidginWebViewPriv *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)
+{
+ PidginWebViewPriv *priv = PIDGIN_WEBVIEW_GET_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)
+{
+ PidginWebViewPriv *priv = PIDGIN_WEBVIEW_GET_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)
+{
+ PidginWebViewPriv *priv = PIDGIN_WEBVIEW_GET_PRIVATE(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(parent_class)->finalize(G_OBJECT(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, gpointer userdata)
+{
+ GObjectClass *gobject_class;
+ GtkBindingSet *binding_set;
+
+ parent_class = g_type_class_ref(webkit_web_view_get_type());
+ gobject_class = G_OBJECT_CLASS(klass);
+
+ g_type_class_add_private(klass, sizeof(PidginWebViewPriv));
+
+ /* 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, g_cclosure_marshal_VOID__INT,
+ 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, g_cclosure_marshal_VOID__INT,
+ 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, g_cclosure_marshal_VOID__VOID,
+ 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, g_cclosure_marshal_VOID__VOID,
+ 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, g_cclosure_marshal_VOID__VOID,
+ 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,
+ g_cclosure_marshal_VOID__OBJECT,
+ 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,
+ purple_smarshal_BOOLEAN__OBJECT, 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(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, gpointer userdata)
+{
+ PidginWebViewPriv *priv = PIDGIN_WEBVIEW_GET_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);
+ }
+}
+
+GType
+pidgin_webview_get_type(void)
+{
+ static GType mview_type = 0;
+ if (G_UNLIKELY(mview_type == 0)) {
+ static const GTypeInfo mview_info = {
+ sizeof(PidginWebViewClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)pidgin_webview_class_init,
+ NULL,
+ NULL,
+ sizeof(PidginWebView),
+ 0,
+ (GInstanceInitFunc)pidgin_webview_init,
+ NULL
+ };
+ mview_type = g_type_register_static(webkit_web_view_get_type(),
+ "PidginWebView", &mview_info, 0);
+ }
+ return mview_type;
+}
+
+/*****************************************************************************
+ * 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)
+{
+ PidginWebViewPriv *priv;
+
+ g_return_if_fail(webview != NULL);
+
+ priv = PIDGIN_WEBVIEW_GET_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)
+{
+ PidginWebViewPriv *priv;
+
+ g_return_if_fail(webview != NULL);
+
+ priv = PIDGIN_WEBVIEW_GET_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)
+{
+ PidginWebViewPriv *priv;
+
+ g_return_if_fail(webview != NULL);
+
+ priv = PIDGIN_WEBVIEW_GET_PRIVATE(webview);
+ priv->vadj = vadj;
+}
+
+void
+pidgin_webview_scroll_to_end(PidginWebView *webview, gboolean smooth)
+{
+ PidginWebViewPriv *priv;
+
+ g_return_if_fail(webview != NULL);
+
+ priv = PIDGIN_WEBVIEW_GET_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)
+{
+ PidginWebViewPriv *priv;
+
+ g_return_if_fail(webview != NULL);
+
+ priv = PIDGIN_WEBVIEW_GET_PRIVATE(webview);
+ priv->autoscroll = scroll;
+}
+
+gboolean
+pidgin_webview_get_autoscroll(PidginWebView *webview)
+{
+ PidginWebViewPriv *priv;
+
+ g_return_val_if_fail(webview != NULL, FALSE);
+
+ priv = PIDGIN_WEBVIEW_GET_PRIVATE(webview);
+ return priv->autoscroll;
+}
+
+void
+pidgin_webview_page_up(PidginWebView *webview)
+{
+ PidginWebViewPriv *priv;
+ GtkAdjustment *vadj;
+ gdouble scroll_val;
+
+ g_return_if_fail(webview != NULL);
+
+ priv = PIDGIN_WEBVIEW_GET_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)
+{
+ PidginWebViewPriv *priv;
+ GtkAdjustment *vadj;
+ gdouble scroll_val;
+ gdouble page_size;
+
+ g_return_if_fail(webview != NULL);
+
+ priv = PIDGIN_WEBVIEW_GET_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)
+{
+ PidginWebViewPriv *priv;
+
+ g_return_if_fail(webview != NULL);
+
+ priv = PIDGIN_WEBVIEW_GET_PRIVATE(webview);
+ priv->edit.wbfo = wbfo;
+}
+
+void
+pidgin_webview_set_format_functions(PidginWebView *webview, PidginWebViewButtons buttons)
+{
+ PidginWebViewPriv *priv;
+ GObject *object;
+
+ g_return_if_fail(webview != NULL);
+
+ priv = PIDGIN_WEBVIEW_GET_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)
+{
+ PidginWebViewPriv *priv;
+
+ g_return_val_if_fail(webview != NULL, 0);
+
+ priv = PIDGIN_WEBVIEW_GET_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)
+{
+ PidginWebViewPriv *priv;
+ WebKitDOMDocument *dom;
+
+ g_return_if_fail(webview != NULL);
+
+ priv = PIDGIN_WEBVIEW_GET_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)
+{
+ PidginWebViewPriv *priv;
+ WebKitDOMDocument *dom;
+ char *link;
+
+ g_return_if_fail(webview != NULL);
+
+ priv = PIDGIN_WEBVIEW_GET_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)
+{
+ PidginWebViewPriv *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_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);
+}
+
+void
+pidgin_webview_set_toolbar(PidginWebView *webview, GtkWidget *toolbar)
+{
+ PidginWebViewPriv *priv;
+
+ g_return_if_fail(webview != NULL);
+
+ priv = PIDGIN_WEBVIEW_GET_PRIVATE(webview);
+ priv->toolbar = PIDGIN_WEBVIEWTOOLBAR(toolbar);
+}
+
+GtkWidget *
+pidgin_webview_get_toolbar(PidginWebView *webview)
+{
+ PidginWebViewPriv *priv;
+
+ g_return_val_if_fail(webview != NULL, NULL);
+
+ priv = PIDGIN_WEBVIEW_GET_PRIVATE(webview);
+ return GTK_WIDGET(priv->toolbar);
+}
+
+void
+pidgin_webview_show_toolbar(PidginWebView *webview)
+{
+ PidginWebViewPriv *priv;
+
+ g_return_if_fail(webview != NULL);
+
+ priv = PIDGIN_WEBVIEW_GET_PRIVATE(webview);
+ g_return_if_fail(priv->toolbar != NULL);
+
+ gtk_widget_show(GTK_WIDGET(priv->toolbar));
+}
+
+void
+pidgin_webview_hide_toolbar(PidginWebView *webview)
+{
+ PidginWebViewPriv *priv;
+
+ g_return_if_fail(webview != NULL);
+
+ priv = PIDGIN_WEBVIEW_GET_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)
+{
+ PidginWebViewPriv *priv;
+
+ g_return_if_fail(webview != NULL);
+
+ priv = PIDGIN_WEBVIEW_GET_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)
+{
+ PidginWebViewPriv *priv = PIDGIN_WEBVIEW_GET_PRIVATE(webview);
+
+ g_return_if_fail(priv != NULL);
+ if (priv->toolbar == NULL)
+ return;
+
+ pidgin_webviewtoolbar_switch_active_conversation(priv->toolbar, conv);
+}
diff --git a/pidgin/gtkwebview.h b/pidgin/gtkwebview.h
new file mode 100644
index 0000000000..4f8b0fbff0
--- /dev/null
+++ b/pidgin/gtkwebview.h
@@ -0,0 +1,649 @@
+/* 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))
+
+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 *, PidginWebViewButtons);
+ void (*toggle_format)(PidginWebView *, PidginWebViewButtons);
+ void (*clear_format)(PidginWebView *);
+ void (*update_format)(PidginWebView *);
+ void (*changed)(PidginWebView *);
+ void (*html_appended)(PidginWebView *, WebKitDOMRange *);
+ gboolean (*insert_image)(PidginWebView *, PurpleImage *);
+};
+
+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 %TRUES, 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_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: the toolbar.
+ */
+GtkWidget *
+pidgin_webview_get_toolbar(PidginWebView *webview);
+
+G_END_DECLS
+
+#endif /* _PIDGIN_WEBVIEW_H_ */
diff --git a/pidgin/gtkwebviewtoolbar.c b/pidgin/gtkwebviewtoolbar.c
new file mode 100644
index 0000000000..550aeac3cb
--- /dev/null
+++ b/pidgin/gtkwebviewtoolbar.c
@@ -0,0 +1,1849 @@
+/* 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_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PIDGIN_TYPE_WEBVIEWTOOLBAR, PidginWebViewToolbarPriv))
+
+#define PIDGIN_WEBVIEWTOOLBAR_DEFAULT_FONT "sans-serif"
+#define PIDGIN_WEBVIEWTOOLBAR_DEFAULT_BGCOLOR "inherit"
+#define PIDGIN_WEBVIEWTOOLBAR_DEFAULT_FGCOLOR "#000000"
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+
+typedef struct _PidginWebViewToolbarPriv {
+ 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;
+} PidginWebViewToolbarPriv;
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+static GtkHBoxClass *parent_class = NULL;
+
+/* XXX: I would bet, there is a better way to do this */
+static guint resources_ref_cnt = 0;
+static GRegex *color_parse_rgb = NULL;
+
+/******************************************************************************
+ * Prototypes
+ *****************************************************************************/
+
+static void
+toggle_action_set_active_block(GtkToggleAction *action, gboolean is_active,
+ PidginWebViewToolbar *toolbar);
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+
+static gboolean
+pidgin_color_parse(const gchar *str, GdkColor *color)
+{
+ GdkColor dummy_color;
+ gboolean succ;
+
+ 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;
+ } else if (strncmp(str, "rgb", 3) == 0) {
+ GMatchInfo *match;
+
+ g_regex_match(color_parse_rgb, str, 0, &match);
+ succ = g_match_info_matches(match);
+ if (succ) {
+ int m_start, val;
+
+ g_match_info_fetch_pos(match, 1, &m_start, NULL);
+ val = strtoul(str + m_start, NULL, 10);
+ if (val >= 0 && val <= 255)
+ color->red = val * 256;
+ else
+ succ = FALSE;
+
+ g_match_info_fetch_pos(match, 2, &m_start, NULL);
+ val = strtoul(str + m_start, NULL, 10);
+ if (val >= 0 && val <= 255)
+ color->green = val * 256;
+ else
+ succ = FALSE;
+
+ g_match_info_fetch_pos(match, 3, &m_start, NULL);
+ val = strtoul(str + m_start, NULL, 10);
+ if (val >= 0 && val <= 255)
+ color->blue = val * 256;
+ else
+ succ = FALSE;
+ }
+
+ g_match_info_free(match);
+ return succ;
+ }
+
+ purple_debug_warning("gtkwebviewtoolbar",
+ "Invalid color format: \"%s\"", str);
+ return FALSE;
+}
+
+static gchar*
+pidgin_color_to_str(GdkColor *color)
+{
+ return g_strdup_printf("#%02X%02X%02X", color->red / 256,
+ color->green / 256, color->blue / 256);
+}
+
+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)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar);
+
+ if (priv->font_dialog != NULL)
+ {
+ gtk_widget_destroy(priv->font_dialog);
+ priv->font_dialog = NULL;
+ }
+}
+
+static void
+realize_toolbar_font(GtkWidget *widget, PidginWebViewToolbar *toolbar)
+{
+#if !GTK_CHECK_VERSION(3,2,0)
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar);
+ GtkFontSelection *sel;
+
+ sel = GTK_FONT_SELECTION(
+ gtk_font_selection_dialog_get_font_selection(GTK_FONT_SELECTION_DIALOG(priv->font_dialog)));
+ gtk_widget_hide(gtk_widget_get_parent(
+ gtk_font_selection_get_size_entry(sel)));
+ gtk_widget_show_all(gtk_font_selection_get_family_list(sel));
+ gtk_widget_show(gtk_widget_get_parent(
+ gtk_font_selection_get_family_list(sel)));
+ gtk_widget_show(gtk_widget_get_parent(gtk_widget_get_parent(
+ gtk_font_selection_get_family_list(sel))));
+#endif
+}
+
+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)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_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);
+ g_signal_connect_after(G_OBJECT(priv->font_dialog), "realize",
+ G_CALLBACK(realize_toolbar_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)
+{
+ PidginWebViewToolbarPriv *priv =
+ PIDGIN_WEBVIEWTOOLBAR_GET_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;
+ GdkColor text_color;
+ gchar *open_tag;
+
+ if (response != GTK_RESPONSE_OK) {
+ destroy_toolbar_fgcolor(GTK_WIDGET(toolbar), NULL, toolbar);
+ return;
+ }
+
+ pidgin_color_chooser_get_rgb(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)
+{
+ PidginWebViewToolbarPriv *priv =
+ PIDGIN_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar);
+
+ if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(color))) {
+ GdkColor 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)) {
+ pidgin_color_chooser_set_rgb(
+ 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)
+{
+ PidginWebViewToolbarPriv *priv =
+ PIDGIN_WEBVIEWTOOLBAR_GET_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;
+ GdkColor text_color;
+ gchar *open_tag;
+
+ if (response != GTK_RESPONSE_OK) {
+ destroy_toolbar_bgcolor(GTK_WIDGET(toolbar), NULL, toolbar);
+ return;
+ }
+
+ pidgin_color_chooser_get_rgb(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)
+{
+ PidginWebViewToolbarPriv *priv =
+ PIDGIN_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar);
+
+ if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(color))) {
+ GdkColor 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)) {
+ pidgin_color_chooser_set_rgb(
+ 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)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar);
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(priv->link), FALSE);
+
+ priv->link_dialog = NULL;
+}
+
+static void
+close_link_dialog(PidginWebViewToolbar *toolbar)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_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)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_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)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_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)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_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, TRUE);
+
+ 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 prpl, not before
+ * g_object_unref(img);
+ */
+}
+
+static void
+insert_image_cb(GtkAction *action, PidginWebViewToolbar *toolbar)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_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)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar);
+ if (priv->smiley_dialog != NULL)
+ {
+ gtk_widget_destroy(priv->smiley_dialog);
+ priv->smiley_dialog = NULL;
+ }
+}
+
+static gboolean
+close_smiley_dialog(PidginWebViewToolbar *toolbar)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_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;
+ PurpleImage *image;
+ 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 = purple_smiley_get_image(smiley);
+ image_id = purple_image_store_add(image);
+
+ 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 == 1))
+ {
+ 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_smiley_get_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)
+{
+ PidginWebViewToolbarPriv *priv =
+ PIDGIN_WEBVIEWTOOLBAR_GET_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)
+{
+ PidginWebViewToolbarPriv *priv =
+ PIDGIN_WEBVIEWTOOLBAR_GET_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)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar);
+ PurpleConversation *conv = priv->active_conv;
+ const gchar *who = purple_conversation_get_name(conv);
+ PurpleConnection *gc = purple_conversation_get_connection(conv);
+
+ purple_prpl_send_attention(gc, who, 0);
+ gtk_widget_grab_focus(toolbar->webview);
+}
+
+static void
+update_buttons_cb(PidginWebView *webview, PidginWebViewButtons buttons,
+ PidginWebViewToolbar *toolbar)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_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(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)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar);
+ gboolean bold, italic, underline, strike;
+ char *tmp, *color_str;
+ char *label;
+ GdkColor 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);
+}
+
+/* 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);
+ GtkRequisition menu_req;
+ GtkAllocation allocation;
+ gint ythickness = gtk_widget_get_style(widget)->ythickness;
+ int savy;
+
+ gtk_widget_get_allocation(widget, &allocation);
+ gtk_widget_get_preferred_size(GTK_WIDGET(menu), NULL, &menu_req);
+ 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 + ythickness + 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());
+ }
+}
+
+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)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar);
+ GtkWidget *menu;
+ GtkWidget *item;
+ gboolean wide;
+
+ if (event->button != 3)
+ 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(GTK_MENU(menu), NULL, NULL, pidgin_menu_position_func_helper,
+ widget, event->button, event->time);
+
+ 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)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_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);
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_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);
+
+ if (--resources_ref_cnt == 0) {
+ g_regex_unref(color_parse_rgb);
+ color_parse_rgb = NULL;
+ }
+
+ G_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+static void
+pidgin_webviewtoolbar_class_init(gpointer _class, gpointer class_data)
+{
+ PidginWebViewToolbarClass *class = _class;
+ GObjectClass *gobject_class = _class;
+
+ parent_class = g_type_class_ref(GTK_TYPE_HBOX);
+ gobject_class->finalize = pidgin_webviewtoolbar_finalize;
+
+ g_type_class_add_private(class, sizeof(PidginWebViewToolbarPriv));
+
+ 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)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_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");
+#ifdef ENABLE_NLS
+ gtk_action_group_set_translation_domain(action_group, PACKAGE);
+#endif
+
+ 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)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_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)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_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)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar);
+ GtkWidget *hbox = GTK_WIDGET(toolbar);
+
+ if (resources_ref_cnt++ == 0) {
+ color_parse_rgb = g_regex_new("^rgb\\s*\\(\\s*"
+ "([0-9]+),\\s*([0-9]+),\\s*([0-9]+)\\s*\\)",
+ G_REGEX_OPTIMIZE, 0, NULL);
+ }
+
+ 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));
+}
+
+GType
+pidgin_webviewtoolbar_get_type(void)
+{
+ static GType webviewtoolbar_type = 0;
+
+ if (!webviewtoolbar_type) {
+ static const GTypeInfo webviewtoolbar_info = {
+ sizeof(PidginWebViewToolbarClass),
+ NULL,
+ NULL,
+ pidgin_webviewtoolbar_class_init,
+ NULL,
+ NULL,
+ sizeof(PidginWebViewToolbar),
+ 0,
+ (GInstanceInitFunc)pidgin_webviewtoolbar_init,
+ NULL
+ };
+
+ webviewtoolbar_type = g_type_register_static(GTK_TYPE_HBOX,
+ "PidginWebViewToolbar", &webviewtoolbar_info, 0);
+ }
+
+ return webviewtoolbar_type;
+}
+
+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)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar);
+ PurpleConnection *gc = purple_conversation_get_connection(conv);
+ PurplePlugin *prpl = purple_connection_get_prpl(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 && prpl && PURPLE_IS_IM_CONVERSATION(conv) &&
+ PURPLE_PLUGIN_PROTOCOL_INFO(prpl)->send_attention != NULL);
+
+ update_smiley_button(toolbar);
+}
+
+void
+pidgin_webviewtoolbar_activate(PidginWebViewToolbar *toolbar,
+ PidginWebViewAction action)
+{
+ PidginWebViewToolbarPriv *priv;
+ GtkAction *act;
+
+ g_return_if_fail(toolbar != NULL);
+
+ priv = PIDGIN_WEBVIEWTOOLBAR_GET_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)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar);
+
+ g_return_val_if_fail(toolbar != NULL, NULL);
+
+ return priv->wide_view;
+}
+
+GtkWidget *
+pidgin_webviewtoolbar_get_lean_view(PidginWebViewToolbar *toolbar)
+{
+ PidginWebViewToolbarPriv *priv = PIDGIN_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar);
+
+ g_return_val_if_fail(toolbar != NULL, NULL);
+
+ return priv->lean_view;
+}
diff --git a/pidgin/gtkwebviewtoolbar.h b/pidgin/gtkwebviewtoolbar.h
new file mode 100644
index 0000000000..7c743352d6
--- /dev/null
+++ b/pidgin/gtkwebviewtoolbar.h
@@ -0,0 +1,131 @@
+/*
+ * 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 {
+ GtkHBox box;
+
+ GtkWidget *webview;
+};
+
+struct _PidginWebViewToolbarClass {
+ GtkHBoxClass 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: 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: the lean toolbar variant.
+ */
+GtkWidget *
+pidgin_webviewtoolbar_get_lean_view(PidginWebViewToolbar *toolbar);
+
+G_END_DECLS
+
+#endif /* _PIDGINWEBVIEWTOOLBAR_H_ */
diff --git a/pidgin/gtkwhiteboard.c b/pidgin/gtkwhiteboard.c
index 8471d9e304..c012e36588 100644
--- a/pidgin/gtkwhiteboard.c
+++ b/pidgin/gtkwhiteboard.c
@@ -22,12 +22,57 @@
*/
#include "internal.h"
-#include "blist.h"
+#include "buddylist.h"
#include "debug.h"
+#include "pidgin.h"
+#include "whiteboard.h"
+#include "gtk3compat.h"
#include "gtkwhiteboard.h"
#include "gtkutils.h"
+typedef enum {
+ PIDGIN_WHITEBOARD_BRUSH_UP,
+ PIDGIN_WHITEBOARD_BRUSH_DOWN,
+ PIDGIN_WHITEBOARD_BRUSH_MOTION
+} PidginWhiteboardBrushState;
+
+typedef struct _PidginWhiteboard PidginWhiteboard;
+typedef struct _PidginWhiteboardPrivate PidginWhiteboardPrivate;
+
+struct _PidginWhiteboardPrivate {
+ cairo_t *cr;
+ cairo_surface_t *surface;
+};
+
+/**
+ * PidginWhiteboard:
+ * @priv: Internal data
+ * @wb: Backend data for this whiteboard
+ * @window: Window for the Doodle session
+ * @drawing_area: Drawing area
+ * @width: Canvas width
+ * @height: Canvas height
+ * @brush_color: Foreground color
+ * @brush_size: Brush size
+ *
+ * A PidginWhiteboard
+ */
+struct _PidginWhiteboard
+{
+ PidginWhiteboardPrivate *priv;
+
+ PurpleWhiteboard *wb;
+
+ GtkWidget *window;
+ GtkWidget *drawing_area;
+
+ int width;
+ int height;
+ int brush_color;
+ int brush_size;
+};
+
/******************************************************************************
* Prototypes
*****************************************************************************/
@@ -39,7 +84,14 @@ static gboolean whiteboard_close_cb(GtkWidget *widget, GdkEvent *event, PidginWh
/*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_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data);
+static gboolean
+pidgin_whiteboard_draw_event(GtkWidget *widget, cairo_t *cr,
+ gpointer _gtkwb);
+#if !GTK_CHECK_VERSION(3,0,0)
+static gboolean
+pidgin_whiteboard_expose_event(GtkWidget *widget, GdkEventExpose *event,
+ gpointer _gtkwb);
+#endif
static gboolean pidgin_whiteboard_brush_down(GtkWidget *widget, GdkEventButton *event, gpointer data);
static gboolean pidgin_whiteboard_brush_motion(GtkWidget *widget, GdkEventMotion *event, gpointer data);
@@ -61,20 +113,16 @@ static void pidgin_whiteboard_set_canvas_as_icon(PidginWhiteboard *gtkwb);
static void pidgin_whiteboard_rgb24_to_rgb48(int color_rgb, GdkColor *color);
-static void color_select_dialog(GtkWidget *widget, PidginWhiteboard *gtkwb);
+static void color_selected(GtkColorButton *button, PidginWhiteboard *gtkwb);
/******************************************************************************
* Globals
*****************************************************************************/
-/*
-GList *buttonList = NULL;
-GdkColor DefaultColor[PALETTE_NUM_COLORS];
-*/
static int LastX; /* Tracks last position of the mouse when drawing */
static int LastY;
static int MotionCount; /* Tracks how many brush motions made */
-static int BrushState = BRUSH_STATE_UP;
+static PidginWhiteboardBrushState brush_state = PIDGIN_WHITEBOARD_BRUSH_UP;
static PurpleWhiteboardUiOps ui_ops =
{
@@ -121,11 +169,13 @@ static void pidgin_whiteboard_create(PurpleWhiteboard *wb)
GtkWidget *clear_button;
GtkWidget *save_button;
GtkWidget *color_button;
+ GdkColor color;
PidginWhiteboard *gtkwb = g_new0(PidginWhiteboard, 1);
+ gtkwb->priv = g_new0(PidginWhiteboardPrivate, 1);
gtkwb->wb = wb;
- wb->ui_data = gtkwb;
+ purple_whiteboard_set_ui_data(wb, gtkwb);
/* Get dimensions (default?) for the whiteboard canvas */
if (!purple_whiteboard_get_dimensions(wb, &gtkwb->width, &gtkwb->height))
@@ -145,50 +195,16 @@ static void pidgin_whiteboard_create(PurpleWhiteboard *wb)
/* Try and set window title as the name of the buddy, else just use their
* username
*/
- buddy = purple_find_buddy(wb->account, wb->who);
+ 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) : wb->who, 0, NULL, FALSE);
+ 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, wb->who);
+ gtk_widget_set_name(window, purple_whiteboard_get_who(wb));
g_signal_connect(G_OBJECT(window), "delete_event",
G_CALLBACK(whiteboard_close_cb), gtkwb);
-#if 0
- int i;
-
- GtkWidget *hbox_palette;
- GtkWidget *vbox_palette_above_canvas_and_controls;
- GtkWidget *palette_color_box[PALETTE_NUM_COLORS];
-
- /* Create vertical box to place palette above the canvas and controls */
- vbox_palette_above_canvas_and_controls = gtk_vbox_new(FALSE, 0);
- gtk_container_add(GTK_CONTAINER(window), vbox_palette_above_canvas_and_controls);
- gtk_widget_show(vbox_palette_above_canvas_and_controls);
-
- /* Create horizontal box for the palette and all its entries */
- hbox_palette = gtk_hbox_new(FALSE, 0);
- gtk_box_pack_start(GTK_BOX(vbox_palette_above_canvas_and_controls),
- hbox_palette, FALSE, FALSE, PIDGIN_HIG_BORDER);
- gtk_widget_show(hbox_palette);
-
- /* Create horizontal box to seperate the canvas from the controls */
- hbox_canvas_and_controls = gtk_hbox_new(FALSE, 0);
- gtk_box_pack_start(GTK_BOX(vbox_palette_above_canvas_and_controls),
- hbox_canvas_and_controls, FALSE, FALSE, PIDGIN_HIG_BORDER);
- gtk_widget_show(hbox_canvas_and_controls);
-
- for(i = 0; i < PALETTE_NUM_COLORS; i++)
- {
- palette_color_box[i] = gtk_image_new_from_pixbuf(NULL);
- gtk_widget_set_size_request(palette_color_box[i], gtkwb->width / PALETTE_NUM_COLORS ,32);
- gtk_container_add(GTK_CONTAINER(hbox_palette), palette_color_box[i]);
-
- gtk_widget_show(palette_color_box[i]);
- }
-#endif
-
- hbox_canvas_and_controls = gtk_hbox_new(FALSE, 0);
+ 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);
@@ -203,11 +219,19 @@ static void pidgin_whiteboard_create(PurpleWhiteboard *wb)
gtk_widget_show(drawing_area);
/* Signals used to handle backing pixmap */
+#if GTK_CHECK_VERSION(3,0,0)
+ 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);
+#else
g_signal_connect(G_OBJECT(drawing_area), "expose_event",
- G_CALLBACK(pidgin_whiteboard_expose_event), gtkwb);
+ G_CALLBACK(pidgin_whiteboard_expose_event), gtkwb);
g_signal_connect(G_OBJECT(drawing_area), "configure_event",
- G_CALLBACK(pidgin_whiteboard_configure_event), gtkwb);
+ G_CALLBACK(pidgin_whiteboard_configure_event), gtkwb);
+#endif
/* Event signals */
g_signal_connect(G_OBJECT(drawing_area), "button_press_event",
@@ -228,7 +252,7 @@ static void pidgin_whiteboard_create(PurpleWhiteboard *wb)
GDK_POINTER_MOTION_HINT_MASK);
/* Create vertical box to contain the controls */
- vbox_controls = gtk_vbox_new(FALSE, 0);
+ 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);
@@ -249,11 +273,16 @@ static void pidgin_whiteboard_create(PurpleWhiteboard *wb)
G_CALLBACK(pidgin_whiteboard_button_save_press), gtkwb);
/* Add a color selector */
- color_button = gtk_button_new_from_stock(GTK_STOCK_SELECT_COLOR);
+ 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);
- g_signal_connect(G_OBJECT(color_button), "clicked",
- G_CALLBACK(color_select_dialog), gtkwb);
+
+ gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(color_button), FALSE);
+ pidgin_whiteboard_rgb24_to_rgb48(gtkwb->brush_color, &color);
+ pidgin_color_chooser_set_rgb(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);
@@ -274,19 +303,19 @@ static void pidgin_whiteboard_destroy(PurpleWhiteboard *wb)
GtkWidget *colour_dialog;
g_return_if_fail(wb != NULL);
- gtkwb = wb->ui_data;
+ 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->pixmap)
- {
- cairo_t *cr = g_object_get_data(G_OBJECT(gtkwb->pixmap), "cairo-context");
- if (cr)
- cairo_destroy(cr);
- g_object_unref(gtkwb->pixmap);
- gtkwb->pixmap = NULL;
+ 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");
@@ -300,8 +329,10 @@ static void pidgin_whiteboard_destroy(PurpleWhiteboard *wb)
gtk_widget_destroy(gtkwb->window);
gtkwb->window = NULL;
}
+
+ g_free(gtkwb->priv);
g_free(gtkwb);
- wb->ui_data = NULL;
+ purple_whiteboard_set_ui_data(wb, NULL);
}
static gboolean whiteboard_close_cb(GtkWidget *widget, GdkEvent *event, PidginWhiteboard *gtkwb)
@@ -312,7 +343,7 @@ static gboolean whiteboard_close_cb(GtkWidget *widget, GdkEvent *event, PidginWh
wb = gtkwb->wb;
g_return_val_if_fail(wb != NULL, FALSE);
- purple_whiteboard_destroy(wb);
+ g_object_unref(wb);
return FALSE;
}
@@ -340,7 +371,7 @@ static void pidginwhiteboard_button_start_press(GtkButton *button, gpointer data
/* 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_conv_im_write(PURPLE_CONV_IM(conv), "", XXXX_("Sent Doodle request."),
+ 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);
@@ -349,76 +380,86 @@ static void pidginwhiteboard_button_start_press(GtkButton *button, gpointer data
/* Insert this 'session' in the list. At this point, it's only a requested
* session.
*/
- wb = purple_whiteboard_create(account, to, DOODLE_STATE_REQUESTING);
+ 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;
- GdkPixmap *pixmap = gtkwb->pixmap;
cairo_t *cr;
+ GtkAllocation allocation;
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkRGBA white = {1.0, 1.0, 1.0, 1.0};
+#endif
- if (pixmap) {
- cr = g_object_get_data(G_OBJECT(pixmap), "cairo-context");
- if (cr)
- cairo_destroy(cr);
- g_object_unref(pixmap);
- }
+ if (gtkwb->priv->cr)
+ cairo_destroy(gtkwb->priv->cr);
+ if (gtkwb->priv->surface)
+ cairo_surface_destroy(gtkwb->priv->surface);
+
+ gtk_widget_get_allocation(widget, &allocation);
- pixmap = gdk_pixmap_new(widget->window,
- widget->allocation.width,
- widget->allocation.height,
- -1);
- gtkwb->pixmap = pixmap;
-
- cr = gdk_cairo_create(GDK_DRAWABLE(pixmap));
- g_object_set_data(G_OBJECT(pixmap), "cairo-context", cr);
- gdk_cairo_set_source_color(cr, &widget->style->white);
- cairo_rectangle(cr,
- 0, 0,
- widget->allocation.width, widget->allocation.height);
+ gtkwb->priv->surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
+ allocation.width, allocation.height);
+ gtkwb->priv->cr = cr = cairo_create(gtkwb->priv->surface);
+#if GTK_CHECK_VERSION(3,0,0)
+ gdk_cairo_set_source_rgba(cr, &white);
+#else
+ gdk_cairo_set_source_color(cr, &gtk_widget_get_style(widget)->white);
+#endif
+ cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
cairo_fill(cr);
return TRUE;
}
-static gboolean pidgin_whiteboard_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
+static gboolean
+pidgin_whiteboard_draw_event(GtkWidget *widget, cairo_t *cr,
+ gpointer _gtkwb)
+{
+ PidginWhiteboard *gtkwb = _gtkwb;
+
+ cairo_set_source_surface(cr, gtkwb->priv->surface, 0, 0);
+ cairo_paint(cr);
+
+ return FALSE;
+}
+
+#if !GTK_CHECK_VERSION(3,0,0)
+static gboolean
+pidgin_whiteboard_expose_event(GtkWidget *widget, GdkEventExpose *event,
+ gpointer _gtkwb)
{
- PidginWhiteboard *gtkwb = (PidginWhiteboard*)(data);
- GdkPixmap *pixmap = gtkwb->pixmap;
cairo_t *cr;
cr = gdk_cairo_create(GDK_DRAWABLE(widget->window));
- gdk_cairo_set_source_pixmap(cr, pixmap, 0, 0);
- cairo_rectangle(cr,
- event->area.x, event->area.y,
- event->area.width, event->area.height);
- cairo_fill(cr);
+
+ pidgin_whiteboard_draw_event(widget, cr, _gtkwb);
+
cairo_destroy(cr);
return FALSE;
}
+#endif
static gboolean pidgin_whiteboard_brush_down(GtkWidget *widget, GdkEventButton *event, gpointer data)
{
PidginWhiteboard *gtkwb = (PidginWhiteboard*)data;
- GdkPixmap *pixmap = gtkwb->pixmap;
PurpleWhiteboard *wb = gtkwb->wb;
- GList *draw_list = wb->draw_list;
+ GList *draw_list = purple_whiteboard_get_draw_list(wb);
- if(BrushState != BRUSH_STATE_UP)
- {
+ if (brush_state != PIDGIN_WHITEBOARD_BRUSH_UP) {
/* Potential double-click DOWN to DOWN? */
- BrushState = BRUSH_STATE_DOWN;
+ brush_state = PIDGIN_WHITEBOARD_BRUSH_DOWN;
/* return FALSE; */
}
- BrushState = BRUSH_STATE_DOWN;
+ brush_state = PIDGIN_WHITEBOARD_BRUSH_DOWN;
- if(event->button == 1 && pixmap != NULL)
+ if(event->button == 1 && gtkwb->priv->cr != NULL)
{
/* Check if draw_list has contents; if so, clear it */
if(draw_list)
@@ -441,7 +482,7 @@ static gboolean pidgin_whiteboard_brush_down(GtkWidget *widget, GdkEventButton *
gtkwb->brush_color, gtkwb->brush_size);
}
- wb->draw_list = draw_list;
+ purple_whiteboard_set_draw_list(wb, draw_list);
return TRUE;
}
@@ -456,13 +497,17 @@ static gboolean pidgin_whiteboard_brush_motion(GtkWidget *widget, GdkEventMotion
GdkModifierType state;
PidginWhiteboard *gtkwb = (PidginWhiteboard*)data;
- GdkPixmap *pixmap = gtkwb->pixmap;
PurpleWhiteboard *wb = gtkwb->wb;
- GList *draw_list = wb->draw_list;
+ GList *draw_list = purple_whiteboard_get_draw_list(wb);
if(event->is_hint)
+#if GTK_CHECK_VERSION(3,0,0)
+ gdk_window_get_device_position(event->window, event->device, &x, &y,
+ &state);
+#else
gdk_window_get_pointer(event->window, &x, &y, &state);
+#endif
else
{
x = event->x;
@@ -470,17 +515,20 @@ static gboolean pidgin_whiteboard_brush_motion(GtkWidget *widget, GdkEventMotion
state = event->state;
}
- if(state & GDK_BUTTON1_MASK && pixmap != NULL)
+ if(state & GDK_BUTTON1_MASK && gtkwb->priv->cr != NULL)
{
- if((BrushState != BRUSH_STATE_DOWN) && (BrushState != BRUSH_STATE_MOTION))
+ 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", BrushState);
+ purple_debug_error("gtkwhiteboard",
+ "***Bad brush state transition %d to MOTION\n",
+ brush_state);
- BrushState = BRUSH_STATE_MOTION;
+ brush_state = PIDGIN_WHITEBOARD_BRUSH_MOTION;
return FALSE;
}
- BrushState = BRUSH_STATE_MOTION;
+ brush_state = PIDGIN_WHITEBOARD_BRUSH_MOTION;
dx = x - LastX;
dy = y - LastY;
@@ -528,7 +576,7 @@ static gboolean pidgin_whiteboard_brush_motion(GtkWidget *widget, GdkEventMotion
LastY = y;
}
- wb->draw_list = draw_list;
+ purple_whiteboard_set_draw_list(wb, draw_list);
return TRUE;
}
@@ -536,22 +584,24 @@ static gboolean pidgin_whiteboard_brush_motion(GtkWidget *widget, GdkEventMotion
static gboolean pidgin_whiteboard_brush_up(GtkWidget *widget, GdkEventButton *event, gpointer data)
{
PidginWhiteboard *gtkwb = (PidginWhiteboard*)data;
- GdkPixmap *pixmap = gtkwb->pixmap;
PurpleWhiteboard *wb = gtkwb->wb;
- GList *draw_list = wb->draw_list;
+ GList *draw_list = purple_whiteboard_get_draw_list(wb);
- if((BrushState != BRUSH_STATE_DOWN) && (BrushState != BRUSH_STATE_MOTION))
+ if ((brush_state != PIDGIN_WHITEBOARD_BRUSH_DOWN) &&
+ (brush_state != PIDGIN_WHITEBOARD_BRUSH_MOTION))
{
- purple_debug_error("gtkwhiteboard", "***Bad brush state transition %d to UP\n", BrushState);
+ purple_debug_error("gtkwhiteboard",
+ "***Bad brush state transition %d to UP\n",
+ brush_state);
- BrushState = BRUSH_STATE_UP;
+ brush_state = PIDGIN_WHITEBOARD_BRUSH_UP;
return FALSE;
}
- BrushState = BRUSH_STATE_UP;
+ brush_state = PIDGIN_WHITEBOARD_BRUSH_UP;
- if(event->button == 1 && pixmap != NULL)
+ if(event->button == 1 && gtkwb->priv->cr != NULL)
{
/* If the brush was never moved, express two sets of two deltas That's a
* 'point,' but not for Yahoo!
@@ -583,7 +633,7 @@ static gboolean pidgin_whiteboard_brush_up(GtkWidget *widget, GdkEventButton *ev
if(draw_list)
purple_whiteboard_draw_list_destroy(draw_list);
- wb->draw_list = NULL;
+ purple_whiteboard_set_draw_list(wb, NULL);
}
return TRUE;
@@ -591,17 +641,26 @@ static gboolean pidgin_whiteboard_brush_up(GtkWidget *widget, GdkEventButton *ev
static void pidgin_whiteboard_draw_brush_point(PurpleWhiteboard *wb, int x, int y, int color, int size)
{
- PidginWhiteboard *gtkwb = wb->ui_data;
+ PidginWhiteboard *gtkwb = purple_whiteboard_get_ui_data(wb);
GtkWidget *widget = gtkwb->drawing_area;
- GdkPixmap *pixmap = gtkwb->pixmap;
-
- cairo_t *gfx_con = g_object_get_data(G_OBJECT(pixmap), "cairo-context");
+ cairo_t *gfx_con = gtkwb->priv->cr;
GdkColor col;
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkRGBA rgba;
+#endif
/* Interpret and convert color */
pidgin_whiteboard_rgb24_to_rgb48(color, &col);
+#if GTK_CHECK_VERSION(3,0,0)
+ rgba.red = col.red / 0xffff;
+ rgba.green = col.green / 0xffff;
+ rgba.blue = col.blue / 0xffff;
+ rgba.alpha = 1.0;
+ gdk_cairo_set_source_rgba(gfx_con, &rgba);
+#else
gdk_cairo_set_source_color(gfx_con, &col);
+#endif
/* Draw a circle */
cairo_arc(gfx_con,
@@ -684,7 +743,7 @@ static void pidgin_whiteboard_draw_brush_line(PurpleWhiteboard *wb, int x0, int
static void pidgin_whiteboard_set_dimensions(PurpleWhiteboard *wb, int width, int height)
{
- PidginWhiteboard *gtkwb = wb->ui_data;
+ PidginWhiteboard *gtkwb = purple_whiteboard_get_ui_data(wb);
gtkwb->width = width;
gtkwb->height = height;
@@ -692,7 +751,7 @@ static void pidgin_whiteboard_set_dimensions(PurpleWhiteboard *wb, int width, in
static void pidgin_whiteboard_set_brush(PurpleWhiteboard *wb, int size, int color)
{
- PidginWhiteboard *gtkwb = wb->ui_data;
+ PidginWhiteboard *gtkwb = purple_whiteboard_get_ui_data(wb);
gtkwb->brush_size = size;
gtkwb->brush_color = color;
@@ -700,22 +759,27 @@ static void pidgin_whiteboard_set_brush(PurpleWhiteboard *wb, int size, int colo
static void pidgin_whiteboard_clear(PurpleWhiteboard *wb)
{
- PidginWhiteboard *gtkwb = wb->ui_data;
- GdkPixmap *pixmap = gtkwb->pixmap;
+ PidginWhiteboard *gtkwb = purple_whiteboard_get_ui_data(wb);
GtkWidget *drawing_area = gtkwb->drawing_area;
- cairo_t *cr = g_object_get_data(G_OBJECT(pixmap), "cairo-context");
+ cairo_t *cr = gtkwb->priv->cr;
+ GtkAllocation allocation;
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkRGBA white = {1.0, 1.0, 1.0, 1.0};
+#endif
+
+ gtk_widget_get_allocation(drawing_area, &allocation);
- gdk_cairo_set_source_color(cr, &drawing_area->style->white);
- cairo_rectangle(cr,
- 0, 0,
- drawing_area->allocation.width,
- drawing_area->allocation.height);
+#if GTK_CHECK_VERSION(3,0,0)
+ gdk_cairo_set_source_rgba(cr, &white);
+#else
+ gdk_cairo_set_source_color(cr,
+ &gtk_widget_get_style(drawing_area)->white);
+#endif
+ cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
cairo_fill(cr);
- gtk_widget_queue_draw_area(drawing_area,
- 0, 0,
- drawing_area->allocation.width,
- drawing_area->allocation.height);
+ gtk_widget_queue_draw_area(drawing_area, 0, 0,
+ allocation.width, allocation.height);
}
static void pidgin_whiteboard_button_clear_press(GtkWidget *widget, gpointer data)
@@ -742,64 +806,52 @@ static void pidgin_whiteboard_button_clear_press(GtkWidget *widget, gpointer dat
}
}
-static void pidgin_whiteboard_button_save_press(GtkWidget *widget, gpointer data)
+static void
+pidgin_whiteboard_button_save_press(GtkWidget *widget, gpointer _gtkwb)
{
- PidginWhiteboard *gtkwb = (PidginWhiteboard*)(data);
+ PidginWhiteboard *gtkwb = _gtkwb;
GdkPixbuf *pixbuf;
-
GtkWidget *dialog;
-
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);
+ 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);
- /* gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), (gboolean)(TRUE)); */
+ gtk_file_chooser_set_do_overwrite_confirmation(
+ GTK_FILE_CHOOSER(dialog), TRUE);
- /* if(user_edited_a_new_document) */
- {
- /* gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), default_folder_for_saving); */
- gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "whiteboard.jpg");
- }
- /*
- else
- gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog), filename_for_existing_document);
- */
+ gtk_file_chooser_set_current_name(
+ GTK_FILE_CHOOSER(dialog), "whiteboard.png");
result = gtk_dialog_run(GTK_DIALOG(dialog));
- if(result == GTK_RESPONSE_ACCEPT)
- {
- char *filename;
-
- filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+ if (result == GTK_RESPONSE_ACCEPT) {
+ gboolean success;
+ gchar *filename =
+ gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
gtk_widget_destroy(dialog);
- /* Makes an icon from the whiteboard's canvas 'image' */
- pixbuf = gdk_pixbuf_get_from_drawable(NULL,
- (GdkDrawable*)(gtkwb->pixmap),
- gdk_drawable_get_colormap(gtkwb->pixmap),
- 0, 0,
- 0, 0,
- gtkwb->width, gtkwb->height);
-
- if(gdk_pixbuf_save(pixbuf, filename, "jpeg", NULL, "quality", "100", NULL))
- purple_debug_info("gtkwhiteboard", "File Saved...\n");
- else
- purple_debug_info("gtkwhiteboard", "File not Saved... Error\n");
+ pixbuf = gdk_pixbuf_get_from_surface(gtkwb->priv->surface, 0, 0,
+ gtkwb->width, gtkwb->height);
+
+ success = gdk_pixbuf_save(pixbuf, filename, "png", NULL,
+ "compression", "9", NULL);
+ g_object_unref(pixbuf);
+ if (success) {
+ purple_debug_info("gtkwhiteboard",
+ "whiteboard saved to \"%s\"", filename);
+ } else {
+ purple_notify_error(NULL, _("Whiteboard"),
+ _("Unable to save the file"), NULL, NULL);
+ purple_debug_error("gtkwhiteboard", "whiteboard "
+ "couldn't be saved to \"%s\"", filename);
+ }
g_free(filename);
- }
- else if(result == GTK_RESPONSE_CANCEL)
- {
+ } else if (result == GTK_RESPONSE_CANCEL)
gtk_widget_destroy(dialog);
-
- purple_debug_info("gtkwhiteboard", "File not Saved... Cancelled\n");
- }
}
static void pidgin_whiteboard_set_canvas_as_icon(PidginWhiteboard *gtkwb)
@@ -807,14 +859,10 @@ 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_drawable(NULL,
- (GdkDrawable*)(gtkwb->pixmap),
- gdk_drawable_get_colormap(gtkwb->pixmap),
- 0, 0,
- 0, 0,
- gtkwb->width, gtkwb->height);
-
- gtk_window_set_icon((GtkWindow*)(gtkwb->window), pixbuf);
+ 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_rgb48(int color_rgb, GdkColor *color)
@@ -825,15 +873,15 @@ static void pidgin_whiteboard_rgb24_to_rgb48(int color_rgb, GdkColor *color)
}
static void
-change_color_cb(GtkColorSelection *selection, PidginWhiteboard *gtkwb)
+color_selected(GtkColorButton *button, PidginWhiteboard *gtkwb)
{
GdkColor color;
- int old_size = 5;
- int old_color = 0;
- int new_color;
PurpleWhiteboard *wb = gtkwb->wb;
+ int old_size, old_color;
+ int new_color;
+
+ pidgin_color_chooser_get_rgb(GTK_COLOR_CHOOSER(button), &color);
- gtk_color_selection_get_current_color(selection, &color);
new_color = (color.red & 0xFF00) << 8;
new_color |= (color.green & 0xFF00);
new_color |= (color.blue & 0xFF00) >> 8;
@@ -842,35 +890,3 @@ change_color_cb(GtkColorSelection *selection, PidginWhiteboard *gtkwb)
purple_whiteboard_send_brush(wb, old_size, new_color);
}
-static void color_selection_dialog_destroy(GtkWidget *w, PidginWhiteboard *gtkwb)
-{
- GtkWidget *dialog = g_object_get_data(G_OBJECT(gtkwb->window), "colour-dialog");
- gtk_widget_destroy(dialog);
- g_object_set_data(G_OBJECT(gtkwb->window), "colour-dialog", NULL);
-}
-
-static void color_select_dialog(GtkWidget *widget, PidginWhiteboard *gtkwb)
-{
- GdkColor color;
- GtkColorSelectionDialog *dialog;
-
- dialog = (GtkColorSelectionDialog *)gtk_color_selection_dialog_new(_("Select color"));
- g_object_set_data(G_OBJECT(gtkwb->window), "colour-dialog", dialog);
-
- g_signal_connect(G_OBJECT(dialog->colorsel), "color-changed",
- G_CALLBACK(change_color_cb), gtkwb);
-
- gtk_widget_destroy(dialog->cancel_button);
- gtk_widget_destroy(dialog->help_button);
-
- g_signal_connect(G_OBJECT(dialog->ok_button), "clicked",
- G_CALLBACK(color_selection_dialog_destroy), gtkwb);
-
- gtk_color_selection_set_has_palette(GTK_COLOR_SELECTION(dialog->colorsel), TRUE);
-
- pidgin_whiteboard_rgb24_to_rgb48(gtkwb->brush_color, &color);
- gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(dialog->colorsel), &color);
-
- gtk_widget_show_all(GTK_WIDGET(dialog));
-}
-
diff --git a/pidgin/gtkwhiteboard.h b/pidgin/gtkwhiteboard.h
index d5bad34a36..7606f9de10 100644
--- a/pidgin/gtkwhiteboard.h
+++ b/pidgin/gtkwhiteboard.h
@@ -1,7 +1,3 @@
-/**
- * @file gtkwhiteboard.h The PidginWhiteboard frontend object
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -25,60 +21,30 @@
#ifndef _PIDGINWHITEBOARD_H_
#define _PIDGINWHITEBOARD_H_
-
-#include "pidgin.h"
-
-#include "whiteboard.h"
-
-#define FULL_CIRCLE_DEGREES 23040
-
-/* TODO: Make into an enum. */
-#define BRUSH_STATE_UP 0
-#define BRUSH_STATE_DOWN 1
-#define BRUSH_STATE_MOTION 2
-
-/* XXX: This seems duplicated with the Yahoo! Doodle prpl code.
- * XXX: How should they work together? */
-#define PALETTE_NUM_COLORS 7
-
/**
- * A PidginWhiteboard
+ * SECTION:gtkwhiteboard
+ * @section_id: pidgin-gtkwhiteboard
+ * @short_description: <filename>gtkwhiteboard.h</filename>
+ * @title: Whiteboard Frontend
*/
-typedef struct _PidginWhiteboard
-{
- PurpleWhiteboard *wb; /**< backend data for this whiteboard */
-
- GtkWidget *window; /**< Window for the Doodle session */
- GtkWidget *drawing_area; /**< Drawing area */
-
- GdkPixmap *pixmap; /**< Memory for drawing area */
- int width; /**< Canvas width */
- int height; /**< Canvas height */
- int brush_color; /**< Foreground color */
- int brush_size; /**< Brush size */
-} PidginWhiteboard;
+#include "pidgin.h"
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
+G_BEGIN_DECLS
/*****************************************************************************/
-/** @name PidginWhiteboard API */
+/* PidginWhiteboard API */
/*****************************************************************************/
-/*@{*/
/**
+ * pidgin_whiteboard_get_ui_ops:
+ *
* Gets the GtkWhiteboard UI Operations.
*
- * @return The GtkWhiteboard UI Operations.
+ * Returns: The GtkWhiteboard UI Operations.
*/
-PurpleWhiteboardUiOps *pidgin_whiteboard_get_ui_ops( void );
-
-/*@}*/
+PurpleWhiteboardUiOps *pidgin_whiteboard_get_ui_ops(void);
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
+G_END_DECLS
#endif /* _PIDGINWHITEBOARD_H_ */
diff --git a/pidgin/gtkft.c b/pidgin/gtkxfer.c
index 4c1bc658ea..8e6ee9a540 100644
--- a/pidgin/gtkft.c
+++ b/pidgin/gtkxfer.c
@@ -1,8 +1,3 @@
-/**
- * @file gtkft.c GTK+ File Transfer UI
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -28,23 +23,26 @@
#include "debug.h"
#include "notify.h"
-#include "ft.h"
+#include "xfer.h"
#include "prpl.h"
#include "util.h"
-#include "gtkft.h"
+#include "gtkxfer.h"
#include "prefs.h"
#include "pidginstock.h"
#include "gtkutils.h"
-#define PIDGINXFER(xfer) \
- (PidginXferUiData *)(xfer)->ui_data
+#ifdef _WIN32
+# include <shellapi.h>
+#endif
/* the maximum size of files we will try to make a thumbnail for */
#define PIDGIN_XFER_MAX_SIZE_IMAGE_THUMBNAIL 10 * 1024 * 1024
struct _PidginXferDialog
{
+ gint box_count;
+
gboolean keep_open;
gboolean auto_clear;
@@ -116,14 +114,17 @@ get_xfer_info_strings(PurpleXfer *xfer, char **kbsec, char **time_elapsed,
double kbps = 0.0;
time_t elapsed, now;
- if (xfer->end_time != 0)
- now = xfer->end_time;
- else
+ now = purple_xfer_get_end_time(xfer);
+ if (now == 0)
now = time(NULL);
kb_sent = purple_xfer_get_bytes_sent(xfer) / 1024.0;
kb_rem = purple_xfer_get_bytes_remaining(xfer) / 1024.0;
- elapsed = (xfer->start_time > 0 ? now - xfer->start_time : 0);
+ elapsed = purple_xfer_get_start_time(xfer);
+ if (elapsed > 0)
+ elapsed = now - elapsed;
+ else
+ elapsed = 0;
kbps = (elapsed > 0 ? (kb_sent / elapsed) : 0);
if (kbsec != NULL) {
@@ -135,9 +136,9 @@ get_xfer_info_strings(PurpleXfer *xfer, char **kbsec, char **time_elapsed,
int h, m, s;
int secs_elapsed;
- if (xfer->start_time > 0)
+ if (purple_xfer_get_start_time(xfer) > 0)
{
- secs_elapsed = now - xfer->start_time;
+ secs_elapsed = now - purple_xfer_get_start_time(xfer);
h = secs_elapsed / 3600;
m = (secs_elapsed % 3600) / 60;
@@ -155,10 +156,10 @@ get_xfer_info_strings(PurpleXfer *xfer, char **kbsec, char **time_elapsed,
if (purple_xfer_is_completed(xfer)) {
*time_remaining = g_strdup(_("Finished"));
}
- else if (purple_xfer_is_canceled(xfer)) {
+ else if (purple_xfer_is_cancelled(xfer)) {
*time_remaining = g_strdup(_("Cancelled"));
}
- else if (purple_xfer_get_size(xfer) == 0 || (kb_sent > 0 && kbps == 0)) {
+ else if (purple_xfer_get_size(xfer) == 0 || (kb_sent > 0 && kbps < 0.001)) {
*time_remaining = g_strdup(_("Unknown"));
}
else if (kb_sent <= 0) {
@@ -243,11 +244,11 @@ update_detailed_info(PidginXferDialog *dialog, PurpleXfer *xfer)
if (dialog == NULL || xfer == NULL)
return;
- data = PIDGINXFER(xfer);
+ data = purple_xfer_get_ui_data(xfer);
get_xfer_info_strings(xfer, &kbsec, &time_elapsed, &time_remaining);
- status = g_strdup_printf("%d%% (%" G_GSIZE_FORMAT " of %" G_GSIZE_FORMAT " bytes)",
+ status = g_strdup_printf("%d%% (%" G_GOFFSET_FORMAT " of %" G_GOFFSET_FORMAT " bytes)",
(int)(purple_xfer_get_progress(xfer)*100),
purple_xfer_get_bytes_sent(xfer),
purple_xfer_get_size(xfer));
@@ -267,7 +268,7 @@ update_detailed_info(PidginXferDialog *dialog, PurpleXfer *xfer)
g_object_unref(pixbuf);
}
- if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) {
+ if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE) {
gtk_label_set_markup(GTK_LABEL(dialog->local_user_desc_label),
_("<b>Receiving As:</b>"));
gtk_label_set_markup(GTK_LABEL(dialog->remote_user_desc_label),
@@ -281,12 +282,12 @@ update_detailed_info(PidginXferDialog *dialog, PurpleXfer *xfer)
}
gtk_label_set_text(GTK_LABEL(dialog->local_user_label),
- purple_account_get_username(xfer->account));
- gtk_label_set_text(GTK_LABEL(dialog->remote_user_label), xfer->who);
+ purple_account_get_username(purple_xfer_get_account(xfer)));
+ gtk_label_set_text(GTK_LABEL(dialog->remote_user_label), purple_xfer_get_remote_user(xfer));
gtk_label_set_text(GTK_LABEL(dialog->protocol_label),
- purple_account_get_protocol_name(xfer->account));
+ purple_account_get_protocol_name(purple_xfer_get_account(xfer)));
- if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) {
+ if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE) {
gtk_label_set_text(GTK_LABEL(dialog->filename_label),
purple_xfer_get_filename(xfer));
} else {
@@ -343,13 +344,13 @@ update_buttons(PidginXferDialog *dialog, PurpleXfer *xfer)
#ifdef _WIN32
/* If using Win32... */
- if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) {
+ if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE) {
gtk_widget_set_sensitive(dialog->open_button, TRUE);
} else {
gtk_widget_set_sensitive(dialog->open_button, FALSE);
}
#else
- if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) {
+ if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE) {
gtk_widget_set_sensitive(dialog->open_button, TRUE);
} else {
gtk_widget_set_sensitive (dialog->open_button, FALSE);
@@ -357,7 +358,7 @@ update_buttons(PidginXferDialog *dialog, PurpleXfer *xfer)
#endif
gtk_widget_set_sensitive(dialog->remove_button, TRUE);
- } else if (purple_xfer_is_canceled(xfer)) {
+ } else if (purple_xfer_is_cancelled(xfer)) {
gtk_widget_hide(dialog->stop_button);
gtk_widget_show(dialog->remove_button);
@@ -471,13 +472,14 @@ open_button_cb(GtkButton *button, PidginXferDialog *dialog)
if (code == SE_ERR_ASSOCINCOMPLETE || code == SE_ERR_NOASSOC)
{
purple_notify_error(dialog, NULL,
- _("There is no application configured to open this type of file."), NULL);
+ _("There is no application configured to open this type of file."),
+ NULL, NULL);
}
else if (code < 32)
{
purple_notify_error(dialog, NULL,
- _("An error occurred while opening the file."), NULL);
- purple_debug_warning("ft", "filename: %s; code: %d\n",
+ _("An error occurred while opening the file."), NULL, NULL);
+ purple_debug_warning("xfer", "filename: %s; code: %d\n",
purple_xfer_get_local_filename(dialog->selected_xfer), code);
}
#else
@@ -516,7 +518,7 @@ open_button_cb(GtkButton *button, PidginXferDialog *dialog)
tmp = g_strdup_printf(_("Error launching %s: %s"),
purple_xfer_get_local_filename(dialog->selected_xfer),
error->message);
- purple_notify_error(dialog, NULL, _("Unable to open file."), tmp);
+ purple_notify_error(dialog, NULL, _("Unable to open file."), tmp, NULL);
g_free(tmp);
g_error_free(error);
}
@@ -525,7 +527,7 @@ open_button_cb(GtkButton *button, PidginXferDialog *dialog)
char *primary = g_strdup_printf(_("Error running %s"), command);
char *secondary = g_strdup_printf(_("Process returned error code %d"),
exit_status);
- purple_notify_error(dialog, NULL, primary, secondary);
+ purple_notify_error(dialog, NULL, primary, secondary, NULL);
g_free(tmp);
}
}
@@ -636,7 +638,7 @@ make_info_table(PidginXferDialog *dialog)
{
GtkWidget *table;
GtkWidget *label;
- int i;
+ gsize i;
struct
{
@@ -701,7 +703,7 @@ pidgin_xfer_dialog_new(void)
{
PidginXferDialog *dialog;
GtkWidget *window;
- GtkWidget *vbox1, *vbox2;
+ GtkWidget *vbox;
GtkWidget *expander;
GtkWidget *alignment;
GtkWidget *table;
@@ -715,25 +717,22 @@ pidgin_xfer_dialog_new(void)
purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/filetransfer/clear_finished");
/* Create the window. */
- dialog->window = window = pidgin_create_window(_("File Transfers"), PIDGIN_HIG_BORDER, "file transfer", TRUE);
+#if GTK_CHECK_VERSION(3,0,0)
+ dialog->window = window = pidgin_create_dialog(_("File Transfers"), 0, "file transfer", TRUE);
+#else
+ dialog->window = window = pidgin_create_dialog(_("File Transfers"), PIDGIN_HIG_BORDER, "file transfer", TRUE);
+#endif
gtk_window_set_default_size(GTK_WINDOW(window), 450, 250);
g_signal_connect(G_OBJECT(window), "delete_event",
G_CALLBACK(delete_win_cb), dialog);
- /* Create the parent vbox for everything. */
- vbox1 = gtk_vbox_new(FALSE, 0);
- gtk_widget_show(vbox1);
- gtk_container_add(GTK_CONTAINER(window), vbox1);
-
/* Create the main vbox for top half of the window. */
- vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
- gtk_box_pack_start(GTK_BOX(vbox1), vbox2, TRUE, TRUE, 0);
- gtk_widget_show(vbox2);
+ vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(window), FALSE, PIDGIN_HIG_BORDER);
/* Setup the listbox */
- gtk_box_pack_start(GTK_BOX(vbox2),
- pidgin_make_scrollable(setup_tree(dialog), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, 140),
+ 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);
/* "Close this window when all transfers finish" */
@@ -743,7 +742,7 @@ pidgin_xfer_dialog_new(void)
!dialog->keep_open);
g_signal_connect(G_OBJECT(checkbox), "toggled",
G_CALLBACK(toggle_keep_open_cb), dialog);
- gtk_box_pack_start(GTK_BOX(vbox2), checkbox, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
gtk_widget_show(checkbox);
/* "Clear finished transfers" */
@@ -753,13 +752,13 @@ pidgin_xfer_dialog_new(void)
dialog->auto_clear);
g_signal_connect(G_OBJECT(checkbox), "toggled",
G_CALLBACK(toggle_clear_finished_cb), dialog);
- gtk_box_pack_start(GTK_BOX(vbox2), checkbox, FALSE, FALSE, 0);
+ 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(vbox2), expander, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), expander, FALSE, FALSE, 0);
gtk_widget_show(expander);
gtk_widget_set_sensitive(expander, FALSE);
@@ -775,11 +774,7 @@ pidgin_xfer_dialog_new(void)
gtk_container_add(GTK_CONTAINER(alignment), table);
gtk_widget_show(table);
- bbox = gtk_hbutton_box_new();
- gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
- gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
- gtk_box_pack_end(GTK_BOX(vbox1), bbox, FALSE, TRUE, 0);
- gtk_widget_show(bbox);
+ 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); \
@@ -867,22 +862,22 @@ pidgin_xfer_dialog_add_xfer(PidginXferDialog *dialog, PurpleXfer *xfer)
g_return_if_fail(dialog != NULL);
g_return_if_fail(xfer != NULL);
- purple_xfer_ref(xfer);
+ g_object_ref(xfer);
- data = PIDGINXFER(xfer);
+ data = purple_xfer_get_ui_data(xfer);
data->in_list = TRUE;
pidgin_xfer_dialog_show(dialog);
data->last_updated_time = 0;
- type = purple_xfer_get_type(xfer);
+ 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));
pixbuf = gtk_widget_render_icon(dialog->window,
- (type == PURPLE_XFER_RECEIVE
+ (type == PURPLE_XFER_TYPE_RECEIVE
? PIDGIN_STOCK_DOWNLOAD
: PIDGIN_STOCK_UPLOAD),
GTK_ICON_SIZE_MENU, NULL);
@@ -895,7 +890,7 @@ pidgin_xfer_dialog_add_xfer(PidginXferDialog *dialog, PurpleXfer *xfer)
gtk_list_store_set(dialog->model, &data->iter,
COLUMN_STATUS, pixbuf,
COLUMN_PROGRESS, 0,
- COLUMN_FILENAME, (type == PURPLE_XFER_RECEIVE)
+ COLUMN_FILENAME, (type == PURPLE_XFER_TYPE_RECEIVE)
? purple_xfer_get_filename(xfer)
: lfilename,
COLUMN_SIZE, size_str,
@@ -926,7 +921,7 @@ pidgin_xfer_dialog_remove_xfer(PidginXferDialog *dialog,
g_return_if_fail(dialog != NULL);
g_return_if_fail(xfer != NULL);
- data = PIDGINXFER(xfer);
+ data = purple_xfer_get_ui_data(xfer);
if (data == NULL)
return;
@@ -943,7 +938,7 @@ pidgin_xfer_dialog_remove_xfer(PidginXferDialog *dialog,
ensure_row_selected(dialog);
update_title_progress(dialog);
- purple_xfer_unref(xfer);
+ g_object_unref(xfer);
}
void
@@ -957,7 +952,7 @@ pidgin_xfer_dialog_cancel_xfer(PidginXferDialog *dialog,
g_return_if_fail(dialog != NULL);
g_return_if_fail(xfer != NULL);
- data = PIDGINXFER(xfer);
+ data = purple_xfer_get_ui_data(xfer);
if (data == NULL)
return;
@@ -970,16 +965,16 @@ pidgin_xfer_dialog_cancel_xfer(PidginXferDialog *dialog,
return;
}
- data = PIDGINXFER(xfer);
+ data = purple_xfer_get_ui_data(xfer);
update_detailed_info(dialog, xfer);
update_title_progress(dialog);
pixbuf = gtk_widget_render_icon(dialog->window,
- PIDGIN_STOCK_FILE_CANCELED,
+ PIDGIN_STOCK_FILE_CANCELLED,
GTK_ICON_SIZE_MENU, NULL);
- if (purple_xfer_is_canceled(xfer))
+ if (purple_xfer_is_cancelled(xfer))
status = _("Cancelled");
else
status = _("Failed");
@@ -1007,7 +1002,7 @@ pidgin_xfer_dialog_update_xfer(PidginXferDialog *dialog,
g_return_if_fail(dialog != NULL);
g_return_if_fail(xfer != NULL);
- if ((data = PIDGINXFER(xfer)) == NULL)
+ if ((data = purple_xfer_get_ui_data(xfer)) == NULL)
return;
if (data->in_list == FALSE)
@@ -1088,6 +1083,43 @@ pidgin_xfer_dialog_update_xfer(PidginXferDialog *dialog,
}
/**************************************************************************
+ * 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
@@ -1095,9 +1127,9 @@ pidgin_xfer_new_xfer(PurpleXfer *xfer)
{
PidginXferUiData *data;
- /* This is where we're setting xfer->ui_data for the first time. */
+ /* This is where we're setting xfer's "ui_data" for the first time. */
data = g_new0(PidginXferUiData, 1);
- xfer->ui_data = data;
+ purple_xfer_set_ui_data(xfer, data);
}
static void
@@ -1105,11 +1137,11 @@ pidgin_xfer_destroy(PurpleXfer *xfer)
{
PidginXferUiData *data;
- data = PIDGINXFER(xfer);
+ data = purple_xfer_get_ui_data(xfer);
if (data) {
g_free(data->name);
g_free(data);
- xfer->ui_data = NULL;
+ purple_xfer_set_ui_data(xfer, NULL);
}
}
@@ -1145,7 +1177,7 @@ pidgin_xfer_cancel_remote(PurpleXfer *xfer)
static void
pidgin_xfer_add_thumbnail(PurpleXfer *xfer, const gchar *formats)
{
- purple_debug_info("ft", "creating thumbnail for transfer\n");
+ purple_debug_info("xfer", "creating thumbnail for transfer\n");
if (purple_xfer_get_size(xfer) <= PIDGIN_XFER_MAX_SIZE_IMAGE_THUMBNAIL) {
GdkPixbuf *thumbnail =
@@ -1163,13 +1195,13 @@ pidgin_xfer_add_thumbnail(PurpleXfer *xfer, const gchar *formats)
for (i = 0; formats_split[i]; i++) {
if (purple_strequal(formats_split[i], "jpeg")) {
- purple_debug_info("ft", "creating JPEG thumbnail\n");
+ purple_debug_info("xfer", "creating JPEG thumbnail\n");
option_keys[0] = "quality";
option_values[0] = "90";
format = "jpeg";
break;
} else if (purple_strequal(formats_split[i], "png")) {
- purple_debug_info("ft", "creating PNG thumbnail\n");
+ purple_debug_info("xfer", "creating PNG thumbnail\n");
option_keys[0] = "compression";
option_values[0] = "9";
format = "png";
@@ -1179,7 +1211,7 @@ pidgin_xfer_add_thumbnail(PurpleXfer *xfer, const gchar *formats)
/* Try the first format given by the PRPL without options */
if (format == NULL) {
- purple_debug_info("ft",
+ purple_debug_info("xfer",
"creating thumbnail of format %s as demanded by PRPL\n",
formats_split[0]);
format = formats_split[0];
@@ -1190,7 +1222,7 @@ pidgin_xfer_add_thumbnail(PurpleXfer *xfer, const gchar *formats)
if (buffer) {
gchar *mimetype = g_strdup_printf("image/%s", format);
- purple_debug_info("ft",
+ purple_debug_info("xfer",
"created thumbnail of %" G_GSIZE_FORMAT " bytes\n",
size);
purple_xfer_set_thumbnail(xfer, buffer, size, mimetype);
diff --git a/pidgin/gtkft.h b/pidgin/gtkxfer.h
index 12f8b380ff..ba1898d8af 100644
--- a/pidgin/gtkft.h
+++ b/pidgin/gtkxfer.h
@@ -1,8 +1,3 @@
-/**
- * @file gtkft.h GTK+ File Transfer UI
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -23,12 +18,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 _PIDGINFT_H_
-#define _PIDGINFT_H_
-#include "ft.h"
+#ifndef _PIDGINXFER_H_
+#define _PIDGINXFER_H_
+/**
+ * SECTION:gtkxfer
+ * @section_id: pidgin-gtkxfer
+ * @short_description: <filename>gtkxfer.h</filename>
+ * @title: File Transfer UI
+ */
+
+#include "xfer.h"
/**
+ * PidginXferDialog:
+ *
* A file transfer dialog.
*
* The structure is opaque, as nobody should be touching anything inside of
@@ -36,113 +40,138 @@
*/
typedef struct _PidginXferDialog PidginXferDialog;
+#define PIDGIN_TYPE_XFER_DIALOG (pidgin_xfer_dialog_get_type())
+
+G_BEGIN_DECLS
+
/**************************************************************************/
-/** @name GTK+ File Transfer Dialog API */
+/* 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.
*
- * @return The new dialog.
+ * Returns: The new dialog.
*/
PidginXferDialog *pidgin_xfer_dialog_new(void);
/**
- * Destroys a file transfer dialog.
+ * pidgin_xfer_dialog_destroy:
+ * @dialog: The file transfer dialog.
*
- * @param dialog The file transfer dialog.
+ * Destroys a file transfer dialog.
*/
void pidgin_xfer_dialog_destroy(PidginXferDialog *dialog);
/**
- * Displays the file transfer dialog given.
- * If dialog is @c NULL, displays the default dialog, creating one if necessary
+ * pidgin_xfer_dialog_show:
+ * @dialog: The file transfer dialog to show.
*
- * @param dialog The file transfer dialog to show.
+ * Displays the file transfer dialog given.
+ * If dialog is %NULL, displays the default dialog, creating one if necessary
*/
void pidgin_xfer_dialog_show(PidginXferDialog *dialog);
/**
- * Hides the file transfer dialog.
+ * pidgin_xfer_dialog_hide:
+ * @dialog: The file transfer dialog to hide.
*
- * @param dialog The file transfer dialog to hide.
+ * Hides the file transfer dialog.
*/
void pidgin_xfer_dialog_hide(PidginXferDialog *dialog);
/**
- * Adds a file transfer to the dialog.
+ * pidgin_xfer_dialog_add_xfer:
+ * @dialog: The file transfer dialog.
+ * @xfer: The file transfer.
*
- * @param dialog The file transfer dialog.
- * @param xfer The file transfer.
+ * Adds a file transfer to the dialog.
*/
void pidgin_xfer_dialog_add_xfer(PidginXferDialog *dialog, PurpleXfer *xfer);
/**
- * Removes a file transfer from the dialog.
+ * pidgin_xfer_dialog_remove_xfer:
+ * @dialog: The file transfer dialog.
+ * @xfer: The file transfer.
*
- * @param dialog The file transfer dialog.
- * @param xfer The file transfer.
+ * Removes a file transfer from the dialog.
*/
void pidgin_xfer_dialog_remove_xfer(PidginXferDialog *dialog,
PurpleXfer *xfer);
/**
- * Indicate in a file transfer dialog that a transfer was cancelled.
+ * pidgin_xfer_dialog_cancel_xfer:
+ * @dialog: The file transfer dialog.
+ * @xfer: The file transfer that was cancelled.
*
- * @param dialog The file transfer dialog.
- * @param xfer The file transfer that was cancelled.
+ * Indicate in a file transfer dialog that a transfer was cancelled.
*/
void pidgin_xfer_dialog_cancel_xfer(PidginXferDialog *dialog,
PurpleXfer *xfer);
/**
- * Updates the information for a transfer in the dialog.
+ * pidgin_xfer_dialog_update_xfer:
+ * @dialog: The file transfer dialog.
+ * @xfer: The file transfer.
*
- * @param dialog The file transfer dialog.
- * @param xfer The file transfer.
+ * Updates the information for a transfer in the dialog.
*/
void pidgin_xfer_dialog_update_xfer(PidginXferDialog *dialog,
PurpleXfer *xfer);
-/*@}*/
-
/**************************************************************************/
-/** @name GTK+ File Transfer API */
+/* GTK+ File Transfer API */
/**************************************************************************/
-/*@{*/
/**
+ * pidgin_xfers_init:
+ *
* Initializes the GTK+ file transfer system.
*/
void pidgin_xfers_init(void);
/**
+ * pidgin_xfers_uninit:
+ *
* Uninitializes the GTK+ file transfer system.
*/
void pidgin_xfers_uninit(void);
/**
- * Sets pidgin's main file transfer dialog.
+ * pidgin_set_xfer_dialog:
+ * @dialog: The main dialog.
*
- * @param dialog The main dialog.
+ * Sets pidgin's main file transfer dialog.
*/
void pidgin_set_xfer_dialog(PidginXferDialog *dialog);
/**
+ * pidgin_get_xfer_dialog:
+ *
* Returns pirgin's main file transfer dialog.
*
- * @return The main dialog.
+ * Returns: The main dialog.
*/
PidginXferDialog *pidgin_get_xfer_dialog(void);
/**
+ * pidgin_xfers_get_ui_ops:
+ *
* Returns the UI operations structure for the GTK+ file transfer UI.
*
- * @return The GTK+ file transfer UI operations structure.
+ * Returns: The GTK+ file transfer UI operations structure.
*/
PurpleXferUiOps *pidgin_xfers_get_ui_ops(void);
-/*@}*/
+G_END_DECLS
-#endif /* _PIDGINFT_H_ */
+#endif /* _PIDGINXFER_H_ */
diff --git a/pidgin/gtkmain.c b/pidgin/libpidgin.c
index 88166a2128..d28c1dcee1 100644
--- a/pidgin/gtkmain.c
+++ b/pidgin/libpidgin.c
@@ -30,7 +30,7 @@
#include "dbus-maybe.h"
#include "debug.h"
#include "eventloop.h"
-#include "ft.h"
+#include "glibcompat.h"
#include "log.h"
#include "network.h"
#include "notify.h"
@@ -41,6 +41,7 @@
#include "status.h"
#include "util.h"
#include "whiteboard.h"
+#include "xfer.h"
#include "gtkaccount.h"
#include "gtkblist.h"
@@ -50,7 +51,7 @@
#include "gtkdialogs.h"
#include "gtkdocklet.h"
#include "gtkeventloop.h"
-#include "gtkft.h"
+#include "gtkxfer.h"
#include "gtkidle.h"
#include "gtklog.h"
#include "gtkmedia.h"
@@ -63,9 +64,8 @@
#include "gtkroomlist.h"
#include "gtksavedstatuses.h"
#include "gtksession.h"
-#include "gtksmiley.h"
+#include "gtksmiley-theme.h"
#include "gtksound.h"
-#include "gtkthemes.h"
#include "gtkutils.h"
#include "pidginstock.h"
#include "gtkwhiteboard.h"
@@ -89,9 +89,6 @@ static const int catch_sig_list[] = {
SIGTERM,
SIGQUIT,
SIGCHLD,
-#if defined(USE_GSTREAMER) && !defined(GST_CAN_DISABLE_FORKING)
- SIGALRM,
-#endif
-1
};
@@ -136,29 +133,6 @@ static int signal_sockets[2];
static void sighandler(int sig);
-/*
- * This child process reaping stuff is currently only used for processes that
- * were forked to play sounds. It's not needed for forked DNS child, which
- * have their own waitpid() call. It might be wise to move this code into
- * gtksound.c.
- */
-static void
-clean_pid(void)
-{
- int status;
- pid_t pid;
-
- do {
- pid = waitpid(-1, &status, WNOHANG);
- } while (pid != 0 && pid != (pid_t)-1);
-
- if ((pid == (pid_t) - 1) && (errno != ECHILD)) {
- char errmsg[BUFSIZ];
- snprintf(errmsg, sizeof(errmsg), "Warning: waitpid() returned %d", pid);
- perror(errmsg);
- }
-}
-
static void sighandler(int sig)
{
ssize_t written;
@@ -210,31 +184,13 @@ mainloop_sighandler(GIOChannel *source, GIOCondition cond, gpointer data)
}
switch (sig) {
-#if defined(USE_GSTREAMER) && !defined(GST_CAN_DISABLE_FORKING)
-/* By default, gstreamer forks when you initialize it, and waitpids for the
- * child. But if libpurple reaps the child rather than leaving it to
- * gstreamer, gstreamer's initialization fails. So, we wait a second before
- * reaping child processes, to give gst a chance to reap it if it wants to.
- *
- * This is not needed in later gstreamers, which let us disable the forking.
- * And, it breaks the world on some Real Unices.
- */
- case SIGCHLD:
- /* Restore signal catching */
- signal(SIGCHLD, sighandler);
- alarm(1);
- break;
- case SIGALRM:
-#else
- case SIGCHLD:
-#endif
- clean_pid();
- /* Restore signal catching */
- signal(SIGCHLD, sighandler);
- break;
- default:
- purple_debug_warning("sighandler", "Caught signal %d\n", sig);
- purple_core_quit();
+ case SIGCHLD:
+ /* Restore signal catching */
+ signal(SIGCHLD, sighandler);
+ break;
+ default:
+ purple_debug_warning("sighandler", "Caught signal %d\n", sig);
+ purple_core_quit();
}
return TRUE;
@@ -248,7 +204,7 @@ ui_main(void)
GList *icons = NULL;
GdkPixbuf *icon = NULL;
char *icon_path;
- int i;
+ gsize i;
struct {
const char *dir;
const char *filename;
@@ -262,14 +218,13 @@ ui_main(void)
#endif
- pidgin_themes_init();
-
pidgin_blist_setup_sort_methods();
#ifndef _WIN32
/* use the nice PNG icon for all the windows */
for(i=0; i<G_N_ELEMENTS(icon_sizes); i++) {
- icon_path = g_build_filename(DATADIR, "icons", "hicolor", icon_sizes[i].dir, "apps", icon_sizes[i].filename, NULL);
+ icon_path = g_build_filename(PURPLE_DATADIR, "icons", "hicolor",
+ icon_sizes[i].dir, "apps", icon_sizes[i].filename, NULL);
icon = pidgin_pixbuf_new_from_file(icon_path);
g_free(icon_path);
if (icon) {
@@ -309,7 +264,6 @@ pidgin_ui_init(void)
purple_xfers_set_ui_ops(pidgin_xfers_get_ui_ops());
purple_blist_set_ui_ops(pidgin_blist_get_ui_ops());
purple_notify_set_ui_ops(pidgin_notify_get_ui_ops());
- purple_privacy_set_ui_ops(pidgin_privacy_get_ui_ops());
purple_request_set_ui_ops(pidgin_request_get_ui_ops());
purple_sound_set_ui_ops(pidgin_sound_get_ui_ops());
purple_connections_set_ui_ops(pidgin_connections_get_ui_ops());
@@ -318,8 +272,9 @@ pidgin_ui_init(void)
purple_idle_set_ui_ops(pidgin_idle_get_ui_ops());
#endif
- pidgin_account_init();
+ pidgin_accounts_init();
pidgin_connection_init();
+ pidgin_request_init();
pidgin_blist_init();
pidgin_status_init();
pidgin_conversations_init();
@@ -329,7 +284,7 @@ pidgin_ui_init(void)
pidgin_roomlist_init();
pidgin_log_init();
pidgin_docklet_init();
- pidgin_smileys_init();
+ _pidgin_smiley_theme_init();
pidgin_utils_init();
pidgin_medias_init();
pidgin_notify_init();
@@ -348,13 +303,14 @@ pidgin_quit(void)
/* Uninit */
pidgin_utils_uninit();
pidgin_notify_uninit();
- pidgin_smileys_uninit();
+ _pidgin_smiley_theme_uninit();
pidgin_conversations_uninit();
pidgin_status_uninit();
pidgin_docklet_uninit();
pidgin_blist_uninit();
+ pidgin_request_uninit();
pidgin_connection_uninit();
- pidgin_account_uninit();
+ pidgin_accounts_uninit();
pidgin_xfers_uninit();
pidgin_debug_uninit();
@@ -372,8 +328,8 @@ static GHashTable *pidgin_ui_get_info(void)
g_hash_table_insert(ui_info, "name", (char*)PIDGIN_NAME);
g_hash_table_insert(ui_info, "version", VERSION);
- g_hash_table_insert(ui_info, "website", "http://pidgin.im");
- g_hash_table_insert(ui_info, "dev_website", "http://developer.pidgin.im");
+ g_hash_table_insert(ui_info, "website", "https://pidgin.im");
+ g_hash_table_insert(ui_info, "dev_website", "https://developer.pidgin.im");
g_hash_table_insert(ui_info, "client_type", "pc");
/*
@@ -410,6 +366,7 @@ static PurpleCoreUiOps core_ops =
pidgin_ui_get_info,
NULL,
NULL,
+ NULL,
NULL
};
@@ -432,7 +389,7 @@ show_usage(const char *name, gboolean terse)
g_string_append_printf(str, _("Usage: %s [OPTION]...\n\n"), name);
g_string_append_printf(str, " -c, --config=%s %s\n",
_("DIR"), _("use DIR for config files"));
- g_string_append_printf(str, " -d, --debug %s\n",
+ g_string_append_printf(str, " -d, --debug[=colored] %s\n",
_("print debugging messages to stdout"));
g_string_append_printf(str, " -f, --force-online %s\n",
_("force online, regardless of network status"));
@@ -462,14 +419,7 @@ show_usage(const char *name, gboolean terse)
g_free(text);
}
-/* FUCKING GET ME A TOWEL! */
-#ifdef _WIN32
-/* suppress gcc "no previous prototype" warning */
-int __cdecl pidgin_main(HINSTANCE hint, int argc, char *argv[]);
-int __cdecl pidgin_main(HINSTANCE hint, int argc, char *argv[])
-#else
-int main(int argc, char *argv[])
-#endif
+int pidgin_start(int argc, char *argv[])
{
gboolean opt_force_online = FALSE;
gboolean opt_help = FALSE;
@@ -481,6 +431,10 @@ int main(int argc, char *argv[])
char *opt_login_arg = NULL;
char *opt_session_arg = NULL;
char *search_path;
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkCssProvider *provider;
+ GdkScreen *screen;
+#endif
GList *accounts;
#ifdef HAVE_SIGNAL_H
int sig_indx; /* for setting up signal catching */
@@ -492,17 +446,18 @@ int main(int argc, char *argv[])
#ifndef DEBUG
char *segfault_message_tmp;
#endif
+#endif
+#if defined(HAVE_SIGNAL_H) || GTK_CHECK_VERSION(3,0,0)
GError *error;
#endif
int opt;
gboolean gui_check;
- gboolean debug_enabled;
- gboolean migration_failed = FALSE;
+ gboolean debug_enabled, debug_colored;
GList *active_accounts;
struct option long_options[] = {
{"config", required_argument, NULL, 'c'},
- {"debug", no_argument, NULL, 'd'},
+ {"debug", optional_argument, NULL, 'd'},
{"force-online", no_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'},
{"login", optional_argument, NULL, 'l'},
@@ -515,24 +470,15 @@ int main(int argc, char *argv[])
{0, 0, 0, 0}
};
+ debug_colored = FALSE;
#ifdef DEBUG
debug_enabled = TRUE;
#else
debug_enabled = FALSE;
#endif
-#if !GLIB_CHECK_VERSION(2, 32, 0)
- /* GLib threading system is automaticaly initialized since 2.32.
- * For earlier versions, it have to be initialized before calling any
- * Glib or GTK+ functions.
- */
- g_thread_init(NULL);
-#endif
-
- g_set_prgname("Pidgin");
-
#ifdef ENABLE_NLS
- bindtextdomain(PACKAGE, LOCALEDIR);
+ bindtextdomain(PACKAGE, PURPLE_LOCALEDIR);
bind_textdomain_codeset(PACKAGE, "UTF-8");
textdomain(PACKAGE);
#endif
@@ -662,6 +608,8 @@ int main(int argc, char *argv[])
break;
case 'd': /* debug */
debug_enabled = TRUE;
+ if (g_strcmp0(optarg, "colored") == 0)
+ debug_colored = TRUE;
break;
case 'f': /* force-online */
opt_force_online = TRUE;
@@ -723,7 +671,16 @@ int main(int argc, char *argv[])
/* set a user-specified config directory */
if (opt_config_dir_arg != NULL) {
- purple_util_set_user_dir(opt_config_dir_arg);
+ if (g_path_is_absolute(opt_config_dir_arg)) {
+ purple_util_set_user_dir(opt_config_dir_arg);
+ } else {
+ /* Make an absolute (if not canonical) path */
+ char *cwd = g_get_current_dir();
+ char *path = g_build_path(G_DIR_SEPARATOR_S, cwd, opt_config_dir_arg, NULL);
+ purple_util_set_user_dir(path);
+ g_free(path);
+ g_free(cwd);
+ }
}
/*
@@ -731,21 +688,16 @@ int main(int argc, char *argv[])
* Fire up this baby.
*/
+ if (g_getenv("PIDGIN_DEBUG_COLORED") != NULL)
+ debug_colored = TRUE;
purple_debug_set_enabled(debug_enabled);
+ purple_debug_set_colored(debug_colored);
- /* If we're using a custom configuration directory, we
- * do NOT want to migrate, or weird things will happen. */
- if (opt_config_dir_arg == NULL)
- {
- if (!purple_core_migrate())
- {
- migration_failed = TRUE;
- }
- }
-
+#if !GTK_CHECK_VERSION(3,0,0)
search_path = g_build_filename(purple_user_dir(), "gtkrc-2.0", NULL);
gtk_rc_add_default_file(search_path);
g_free(search_path);
+#endif
gui_check = gtk_init_check(&argc, &argv);
if (!gui_check) {
@@ -762,42 +714,29 @@ int main(int argc, char *argv[])
return 1;
}
- g_set_application_name(PIDGIN_NAME);
-
-#ifdef _WIN32
- winpidgin_init(hint);
-#endif
-
- if (migration_failed)
- {
- char *old = g_strconcat(purple_home_dir(),
- G_DIR_SEPARATOR_S ".gaim", NULL);
- const char *text = _(
- "%s encountered errors migrating your settings "
- "from %s to %s. Please investigate and complete the "
- "migration by hand. Please report this error at http://developer.pidgin.im");
- GtkWidget *dialog;
-
- dialog = gtk_message_dialog_new(NULL,
- 0,
- GTK_MESSAGE_ERROR,
- GTK_BUTTONS_CLOSE,
- text, PIDGIN_NAME,
- old, purple_user_dir());
- g_free(old);
-
- g_signal_connect_swapped(dialog, "response",
- G_CALLBACK(gtk_main_quit), NULL);
+#if GTK_CHECK_VERSION(3,0,0)
+ search_path = g_build_filename(purple_user_dir(), "gtk-3.0.css", NULL);
- gtk_widget_show_all(dialog);
+ error = NULL;
+ provider = gtk_css_provider_new();
+ gui_check = gtk_css_provider_load_from_path(provider, search_path, &error);
+
+ if (gui_check && !error) {
+ screen = gdk_screen_get_default();
+ gtk_style_context_add_provider_for_screen(screen,
+ GTK_STYLE_PROVIDER(provider),
+ GTK_STYLE_PROVIDER_PRIORITY_USER);
+ } else {
+ purple_debug_error("gtk", "Unable to load custom gtk-3.0.css: %s\n",
+ error ? error->message : "(unknown error)");
+ }
- gtk_main();
+ g_free(search_path);
+#endif
-#ifdef HAVE_SIGNAL_H
- g_free(segfault_message);
+#ifdef _WIN32
+ winpidgin_init();
#endif
- return 0;
- }
purple_core_set_ui_ops(pidgin_core_get_ui_ops());
purple_eventloop_set_ui_ops(pidgin_eventloop_get_ui_ops());
@@ -811,7 +750,7 @@ int main(int argc, char *argv[])
fprintf(stderr, "Couldn't create plugins dir\n");
purple_plugins_add_search_path(search_path);
g_free(search_path);
- purple_plugins_add_search_path(LIBDIR);
+ purple_plugins_add_search_path(PIDGIN_LIBDIR);
if (!purple_core_init(PIDGIN_UI)) {
fprintf(stderr,
@@ -826,8 +765,8 @@ int main(int argc, char *argv[])
if (opt_si && !purple_core_ensure_single_instance()) {
#ifdef HAVE_DBUS
DBusConnection *conn = purple_dbus_get_connection();
- DBusMessage *message = dbus_message_new_method_call(DBUS_SERVICE_PURPLE, DBUS_PATH_PURPLE,
- DBUS_INTERFACE_PURPLE, "PurpleBlistSetVisible");
+ DBusMessage *message = dbus_message_new_method_call(PURPLE_DBUS_SERVICE, PURPLE_DBUS_PATH,
+ PURPLE_DBUS_INTERFACE, "PurpleBlistSetVisible");
gboolean tr = TRUE;
dbus_message_append_args(message, DBUS_TYPE_INT32, &tr, DBUS_TYPE_INVALID);
dbus_connection_send_with_reply_and_block(conn, message, -1, NULL);
@@ -842,16 +781,9 @@ int main(int argc, char *argv[])
return 0;
}
- /* TODO: Move blist loading into purple_blist_init() */
- purple_set_blist(purple_blist_new());
- purple_blist_load();
-
/* load plugins we had when we quit */
purple_plugins_load_saved(PIDGIN_PREFS_ROOT "/plugins/loaded");
- /* TODO: Move pounces loading into purple_pounces_init() */
- purple_pounces_load();
-
ui_main();
#ifdef USE_SM
diff --git a/pidgin/minidialog.c b/pidgin/minidialog.c
index 10a4286620..fa7b953e26 100644
--- a/pidgin/minidialog.c
+++ b/pidgin/minidialog.c
@@ -1,8 +1,3 @@
-/**
- * @file minidialog.c Implementation of the #PidginMiniDialog Gtk widget.
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -25,9 +20,12 @@
*/
#include "internal.h"
+#include "glibcompat.h"
#include <gtk/gtk.h>
+#include "gtk3compat.h"
+
#include "libpurple/prefs.h"
#include "pidgin/minidialog.h"
@@ -81,6 +79,8 @@ enum
LAST_PROPERTY
} HazeConnectionProperties;
+static GParamSpec *properties[LAST_PROPERTY];
+
typedef struct _PidginMiniDialogPrivate
{
GtkImage *icon;
@@ -145,16 +145,6 @@ pidgin_mini_dialog_enable_description_markup(PidginMiniDialog *mini_dialog)
g_object_set(G_OBJECT(mini_dialog), "enable-description-markup", TRUE, NULL);
}
-gboolean
-pidgin_mini_dialog_links_supported()
-{
-#if GTK_CHECK_VERSION(2,18,0)
- return TRUE;
-#else
- return FALSE;
-#endif
-}
-
void pidgin_mini_dialog_set_link_callback(PidginMiniDialog *mini_dialog, GCallback cb, gpointer user_data)
{
g_signal_connect(PIDGIN_MINI_DIALOG_GET_PRIVATE(mini_dialog)->desc, "activate-link", cb, user_data);
@@ -184,7 +174,12 @@ struct _mini_dialog_button_clicked_cb_data
guint
pidgin_mini_dialog_get_num_children(PidginMiniDialog *mini_dialog)
{
- return g_list_length(mini_dialog->contents->children);
+ GList *tmp;
+ guint len;
+ tmp = gtk_container_get_children(GTK_CONTAINER(mini_dialog->contents));
+ len = g_list_length(tmp);
+ g_list_free(tmp);
+ return len;
}
static gboolean
@@ -327,6 +322,8 @@ mini_dialog_set_title(PidginMiniDialog *self,
g_free(title_esc);
g_free(title_markup);
+
+ g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_TITLE]);
}
static void
@@ -357,6 +354,8 @@ mini_dialog_set_description(PidginMiniDialog *self,
*/
g_object_set(G_OBJECT(priv->desc), "no-show-all", TRUE, NULL);
}
+
+ g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_DESCRIPTION]);
}
static void
@@ -411,45 +410,43 @@ static void
pidgin_mini_dialog_class_init(PidginMiniDialogClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
- GParamSpec *param_spec;
object_class->get_property = pidgin_mini_dialog_get_property;
object_class->set_property = pidgin_mini_dialog_set_property;
object_class->finalize = pidgin_mini_dialog_finalize;
- param_spec = g_param_spec_string("title", "title",
+ properties[PROP_TITLE] = g_param_spec_string("title",
+ "title",
"String specifying the mini-dialog's title", NULL,
- G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB |
- G_PARAM_READWRITE);
- g_object_class_install_property (object_class, PROP_TITLE, param_spec);
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
- param_spec = g_param_spec_string("description", "description",
+ properties[PROP_DESCRIPTION] = g_param_spec_string("description",
+ "description",
"Description text for the mini-dialog, if desired", NULL,
- G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB |
- G_PARAM_READWRITE);
- g_object_class_install_property (object_class, PROP_DESCRIPTION, param_spec);
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
- param_spec = g_param_spec_string("icon-name", "icon-name",
+ properties[PROP_ICON_NAME] = g_param_spec_string("icon-name",
+ "icon-name",
"String specifying the Gtk stock name of the dialog's icon",
NULL,
- G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB |
- G_PARAM_READWRITE);
- g_object_class_install_property (object_class, PROP_ICON_NAME, param_spec);
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
- param_spec = g_param_spec_object("custom-icon", "custom-icon",
+ properties[PROP_CUSTOM_ICON] = g_param_spec_object("custom-icon",
+ "custom-icon",
"Pixbuf to use as the dialog's icon",
GDK_TYPE_PIXBUF,
- G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB |
- G_PARAM_READWRITE);
- g_object_class_install_property (object_class, PROP_CUSTOM_ICON, param_spec);
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
- param_spec = g_param_spec_boolean("enable-description-markup", "enable-description-markup",
+ properties[PROP_ENABLE_DESCRIPTION_MARKUP] =
+ g_param_spec_boolean("enable-description-markup",
+ "enable-description-markup",
"Use GMarkup in the description text", FALSE,
- G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB |
- G_PARAM_READWRITE);
- g_object_class_install_property (object_class, PROP_ENABLE_DESCRIPTION_MARKUP, param_spec);
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
+
+ g_object_class_install_properties(object_class, LAST_PROPERTY, properties);
}
+#if !GTK_CHECK_VERSION(3,0,0)
/* 16 is the width of the icon, due to PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL */
#define BLIST_WIDTH_OTHER_THAN_LABEL \
((PIDGIN_HIG_BOX_SPACE * 3) + 16)
@@ -471,26 +468,31 @@ blist_width_changed_cb(const char *name,
gtk_widget_set_size_request(GTK_WIDGET(priv->title), label_width, -1);
gtk_widget_set_size_request(GTK_WIDGET(priv->desc), label_width, -1);
}
+#endif
static void
pidgin_mini_dialog_init(PidginMiniDialog *self)
{
GtkBox *self_box = GTK_BOX(self);
+#if !GTK_CHECK_VERSION(3,0,0)
guint blist_width = purple_prefs_get_int(BLIST_WIDTH_PREF);
guint label_width = blist_width - BLIST_WIDTH_OTHER_THAN_LABEL;
+#endif
PidginMiniDialogPrivate *priv = g_new0(PidginMiniDialogPrivate, 1);
self->priv = priv;
gtk_container_set_border_width(GTK_CONTAINER(self), PIDGIN_HIG_BOX_SPACE);
- priv->title_box = GTK_BOX(gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE));
+ priv->title_box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE));
priv->icon = GTK_IMAGE(gtk_image_new());
gtk_misc_set_alignment(GTK_MISC(priv->icon), 0, 0);
priv->title = GTK_LABEL(gtk_label_new(NULL));
+#if !GTK_CHECK_VERSION(3,0,0)
gtk_widget_set_size_request(GTK_WIDGET(priv->title), label_width, -1);
+#endif
gtk_label_set_line_wrap(priv->title, TRUE);
gtk_label_set_selectable(priv->title, TRUE);
gtk_misc_set_alignment(GTK_MISC(priv->title), 0, 0);
@@ -499,7 +501,9 @@ pidgin_mini_dialog_init(PidginMiniDialog *self)
gtk_box_pack_start(priv->title_box, GTK_WIDGET(priv->title), TRUE, TRUE, 0);
priv->desc = GTK_LABEL(gtk_label_new(NULL));
+#if !GTK_CHECK_VERSION(3,0,0)
gtk_widget_set_size_request(GTK_WIDGET(priv->desc), label_width, -1);
+#endif
gtk_label_set_line_wrap(priv->desc, TRUE);
gtk_misc_set_alignment(GTK_MISC(priv->desc), 0, 0);
gtk_label_set_selectable(priv->desc, TRUE);
@@ -508,12 +512,14 @@ pidgin_mini_dialog_init(PidginMiniDialog *self)
*/
g_object_set(G_OBJECT(priv->desc), "no-show-all", TRUE, NULL);
+#if !GTK_CHECK_VERSION(3,0,0)
purple_prefs_connect_callback(self, BLIST_WIDTH_PREF,
blist_width_changed_cb, self);
+#endif
- self->contents = GTK_BOX(gtk_vbox_new(FALSE, 0));
+ self->contents = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
- priv->buttons = GTK_BOX(gtk_hbox_new(FALSE, 0));
+ priv->buttons = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
gtk_box_pack_start(self_box, GTK_WIDGET(priv->title_box), FALSE, FALSE, 0);
gtk_box_pack_start(self_box, GTK_WIDGET(priv->desc), FALSE, FALSE, 0);
diff --git a/pidgin/minidialog.h b/pidgin/minidialog.h
index 1bdd54b3ec..3017c4354a 100644
--- a/pidgin/minidialog.h
+++ b/pidgin/minidialog.h
@@ -1,8 +1,3 @@
-/**
- * @file minidialog.h API for the #PidginMiniDialog Gtk widget.
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -26,6 +21,12 @@
#ifndef __PIDGIN_MINI_DIALOG_H__
#define __PIDGIN_MINI_DIALOG_H__
+/**
+ * SECTION:minidialog
+ * @section_id: pidgin-minidialog
+ * @short_description: <filename>minidialog.h</filename>
+ * @title: Mini Dialog Widget
+ */
#include <glib-object.h>
#include <gtk/gtk.h>
@@ -55,144 +56,197 @@ G_BEGIN_DECLS
PIDGIN_TYPE_MINI_DIALOG, PidginMiniDialogClass))
/**
+ * PidginMiniDialog:
+ * @contents: A GtkVBox into which extra widgets for the dialog should be packed.
+ *
* A widget resembling a diminutive dialog box, designed to be embedded in the
* #PidginBuddyList. Mini-dialogs have titles, optional descriptions, and a row
- * of buttons at the bottom; above the buttons is a <tt>GtkHBox</tt> into which
+ * of buttons at the bottom; above the buttons is a #GtkHBox into which
* you can pack any random widgets you want to add to the dialog. When any of
* the dialog's buttons is clicked, the dialog will be destroyed.
*
* Dialogs have the following GObject properties:
- * <dl>
- * <dt><tt>"title"</tt> (<tt>char *</tt>)</dt>
- * <dd>A string to be displayed as the dialog's title.</dd>
- * <dt><tt>"description"</tt> (<tt>char *</tt>)</dt>
- * <dd>A string to be displayed as the dialog's description. If this is @c
- * NULL, the description widget will be hidden.
- * </dd>
- * <dt><tt>"icon-name"</tt> (<tt>char *</tt>)</dt>
- * <dd>The Gtk stock id of an icon for the dialog, or @c NULL for no icon.
- * @see pidginstock.h
- * </dd>
- * <dt><tt>"custom-icon"</tt> (<tt>GdkPixbuf *</tt>)</dt>
- * <dd>The custom icon to use instead of a stock one (overrides the "icon-name" property).</dd>
- * </dl>
+ * <informaltable frame='none'>
+ * <tgroup cols='2'><tbody>
+ * <row><entry><literal>"title"</literal></entry>
+ * <entry>(<type>char *</type>) A string to be displayed as the dialog's
+ * title.</entry>
+ * </row>
+ * <row><entry><literal>"description"</literal></entry>
+ * <entry>(<type>char *</type>) A string to be displayed as the dialog's
+ * description. If this is %NULL, the description widget will be
+ * hidden.</entry>
+ * </row>
+ * <row><entry><literal>"icon-name"</literal></entry>
+ * <entry>(<type>char *</type>)The Gtk stock id of an icon for the dialog,
+ * or %NULL for no icon. See
+ * <link linkend="pidgin-pidginstock">Stock Resources</link></entry>
+ * </row>
+ * <row><entry><literal>"custom-icon"</literal></entry>
+ * <entry>(<type>GdkPixbuf *</type>) The custom icon to use instead of a
+ * stock one (overrides the <literal>"icon-name"</literal>
+ * property).</entry>
+ * </row>
+ * </tbody></tgroup>
+ * </informaltable>
*/
typedef struct {
GtkVBox parent;
+ gpointer priv;
- /** A GtkVBox into which extra widgets for the dialog should be packed.
- */
+ /*< public >*/
GtkBox *contents;
-
- gpointer priv;
} PidginMiniDialog;
-/** The class of #PidginMiniDialog objects. */
+/**
+ * PidginMiniDialogClass:
+ *
+ * The class of #PidginMiniDialog objects.
+ */
typedef struct {
GtkBoxClass parent_class;
+ /*< private >*/
void (*_purple_reserved1) (void);
void (*_purple_reserved2) (void);
void (*_purple_reserved3) (void);
void (*_purple_reserved4) (void);
} PidginMiniDialogClass;
-/** The type of a callback triggered by a button in a mini-dialog being pressed.
- * @param mini_dialog a dialog, one of whose buttons has been pressed.
- * @param button the button which was pressed.
- * @param user_data arbitrary data, supplied to
+/**
+ * PidginMiniDialogCallback:
+ * @mini_dialog: a dialog, one of whose buttons has been pressed.
+ * @button: the button which was pressed.
+ * @user_data: arbitrary data, supplied to
* pidgin_mini_dialog_add_button() when the button was
* created.
+ *
+ * The type of a callback triggered by a button in a mini-dialog being pressed.
*/
typedef void (*PidginMiniDialogCallback)(PidginMiniDialog *mini_dialog,
GtkButton *button, gpointer user_data);
-/** Get the GType of #PidginMiniDialog. */
+/**
+ * pidgin_mini_dialog_get_type:
+ *
+ * Get the GType of #PidginMiniDialog.
+ */
GType pidgin_mini_dialog_get_type (void);
-/** Creates a new #PidginMiniDialog with a stock icon. This is a shortcut for creating the dialog
- * with @c g_object_new() then setting each property yourself.
- * @return a new #PidginMiniDialog.
+/**
+ * pidgin_mini_dialog_new:
+ *
+ * Creates a new #PidginMiniDialog with a stock icon. This is a shortcut for creating the dialog
+ * with g_object_new() then setting each property yourself.
+ *
+ * Returns: a new #PidginMiniDialog.
*/
PidginMiniDialog *pidgin_mini_dialog_new(const gchar *title,
const gchar *description, const gchar *icon_name);
-/** Creates a new #PidginMiniDialog with a custom icon. This is a shortcut for creating the dialog
- * with @c g_object_new() then setting each property yourself.
- * @return a new #PidginMiniDialog.
+/**
+ * pidgin_mini_dialog_new_with_custom_icon:
+ *
+ * Creates a new #PidginMiniDialog with a custom icon. This is a shortcut for creating the dialog
+ * with g_object_new() then setting each property yourself.
+ *
+ * Returns: a new #PidginMiniDialog.
*/
PidginMiniDialog *pidgin_mini_dialog_new_with_custom_icon(const gchar *title,
const gchar *description, GdkPixbuf *custom_icon);
-/** Shortcut for setting a mini-dialog's title via GObject properties.
- * @param mini_dialog a mini-dialog
- * @param title the new title for @a mini_dialog
+/**
+ * pidgin_mini_dialog_set_title:
+ * @mini_dialog: a mini-dialog
+ * @title: the new title for @mini_dialog
+ *
+ * Shortcut for setting a mini-dialog's title via GObject properties.
*/
void pidgin_mini_dialog_set_title(PidginMiniDialog *mini_dialog,
const char *title);
-/** Shortcut for setting a mini-dialog's description via GObject properties.
- * @param mini_dialog a mini-dialog
- * @param description the new description for @a mini_dialog, or @c NULL to
- * hide the description widget.
+/**
+ * pidgin_mini_dialog_set_description:
+ * @mini_dialog: a mini-dialog
+ * @description: the new description for @mini_dialog, or %NULL to
+ * hide the description widget.
+ *
+ * Shortcut for setting a mini-dialog's description via GObject properties.
*/
void pidgin_mini_dialog_set_description(PidginMiniDialog *mini_dialog,
const char *description);
-/** Enable GMarkup elements in the mini-dialog's description.
- * @param mini_dialog a mini-dialog
+/**
+ * pidgin_mini_dialog_enable_description_markup:
+ * @mini_dialog: a mini-dialog
+ *
+ * Enable GMarkup elements in the mini-dialog's description.
*/
void pidgin_mini_dialog_enable_description_markup(PidginMiniDialog *mini_dialog);
-/** Mini-dialogs support hyperlinks in their description
- * (you should first call pidgin_mini_dialog_enable_description_markup() on a given
- * dialog to enable them). */
-gboolean pidgin_mini_dialog_links_supported(void);
-
-/** Sets a callback which gets invoked when a hyperlink in the dialog's description is clicked on.
- * @param mini_dialog a mini-dialog
- * @param cb the callback to invoke
- * @param user_data the user data to pass to the callback
+/**
+ * pidgin_mini_dialog_set_link_callback:
+ * @mini_dialog: a mini-dialog
+ * @cb: the callback to invoke
+ * @user_data: the user data to pass to the callback
+ *
+ * Sets a callback which gets invoked when a hyperlink in the dialog's description is clicked on.
*/
void pidgin_mini_dialog_set_link_callback(PidginMiniDialog *mini_dialog, GCallback cb, gpointer user_data);
-/** Shortcut for setting a mini-dialog's icon via GObject properties.
- * @param mini_dialog a mini-dialog
- * @param icon_name the Gtk stock ID of an icon, or @c NULL for no icon.
+/**
+ * pidgin_mini_dialog_set_icon_name:
+ * @mini_dialog: a mini-dialog
+ * @icon_name: the Gtk stock ID of an icon, or %NULL for no icon.
+ *
+ * Shortcut for setting a mini-dialog's icon via GObject properties.
*/
void pidgin_mini_dialog_set_icon_name(PidginMiniDialog *mini_dialog,
const char *icon_name);
-/** Shortcut for setting a mini-dialog's custom icon via GObject properties.
- * @param mini_dialog a mini-dialog
- * @param custom_icon the pixbuf to use as a custom icon
+/**
+ * pidgin_mini_dialog_set_custom_icon:
+ * @mini_dialog: a mini-dialog
+ * @custom_icon: the pixbuf to use as a custom icon
+ *
+ * Shortcut for setting a mini-dialog's custom icon via GObject properties.
*/
void pidgin_mini_dialog_set_custom_icon(PidginMiniDialog *mini_dialog,
GdkPixbuf *custom_icon);
-/** Adds a new button to a mini-dialog, and attaches the supplied callback to
- * its <tt>clicked</tt> signal. After a button is clicked, the dialog is
- * destroyed.
- * @param mini_dialog a mini-dialog
- * @param text the text to display on the new button
- * @param clicked_cb the function to call when the button is clicked
- * @param user_data arbitrary data to pass to @a clicked_cb when it is
- * called.
+/**
+ * pidgin_mini_dialog_add_button:
+ * @mini_dialog: a mini-dialog
+ * @text: the text to display on the new button
+ * @clicked_cb: the function to call when the button is clicked
+ * @user_data: arbitrary data to pass to @clicked_cb when it is
+ * called.
+ *
+ * Adds a new button to a mini-dialog, and attaches the supplied callback to
+ * its <literal>clicked</literal> signal. After a button is clicked, the dialog
+ * is destroyed.
*/
void pidgin_mini_dialog_add_button(PidginMiniDialog *mini_dialog,
const char *text, PidginMiniDialogCallback clicked_cb,
gpointer user_data);
-/** Equivalent to pidgin_mini_dialog_add_button(), the only difference
- * is that the mini-dialog won't be closed after the button is clicked.
+/**
+ * pidgin_mini_dialog_add_non_closing_button:
+ *
+ * Equivalent to pidgin_mini_dialog_add_button(), the only difference
+ * is that the mini-dialog won't be closed after the button is clicked.
*/
void pidgin_mini_dialog_add_non_closing_button(PidginMiniDialog *mini_dialog,
const char *text, PidginMiniDialogCallback clicked_cb,
gpointer user_data);
-/** Gets the number of widgets packed into PidginMiniDialog.contents.
- * @param mini_dialog a mini-dialog
- * @return the number of widgets in @a mini_dialog->contents.
+/**
+ * pidgin_mini_dialog_get_num_children:
+ * @mini_dialog: a mini-dialog
+ *
+ * Gets the number of widgets packed into PidginMiniDialog.contents.
+ *
+ * Returns: the number of widgets in @mini_dialog->contents.
*/
guint pidgin_mini_dialog_get_num_children(PidginMiniDialog *mini_dialog);
diff --git a/pidgin/pidgin-2-uninstalled.pc.in b/pidgin/pidgin-3-uninstalled.pc.in
index a2e4fd9da3..558ad54703 100644
--- a/pidgin/pidgin-2-uninstalled.pc.in
+++ b/pidgin/pidgin-3-uninstalled.pc.in
@@ -17,5 +17,5 @@ plugindir=${libdir}/pidgin
Name: Pidgin
Description: Pidgin is a GTK2-based instant messenger application.
Version: @VERSION@
-Requires: gtk+-2.0 purple
+Requires: gtk+-2.0 purple-3
Cflags: -I${abs_top_srcdir}
diff --git a/pidgin/pidgin-2.pc.in b/pidgin/pidgin-3.pc.in
index 30c930ae22..3ce9fd67c1 100644
--- a/pidgin/pidgin-2.pc.in
+++ b/pidgin/pidgin-3.pc.in
@@ -1,7 +1,7 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
-includedir=@includedir@
+includedir=@includedir@/pidgin
datarootdir=@datarootdir@
datadir=@datadir@
sysconfdir=@sysconfdir@
@@ -11,5 +11,6 @@ plugindir=${libdir}/pidgin
Name: Pidgin
Description: Pidgin is a GTK2-based instant messenger application.
Version: @VERSION@
-Requires: gtk+-2.0 purple
+Requires: @GTK_PC_MODULE@ @WEBKIT_PC_MODULE@ purple-3
Cflags: -I${includedir}
+Libs: -L${libdir} -lpidgin
diff --git a/pidgin/pidgin-uninstalled.pc.in b/pidgin/pidgin-uninstalled.pc.in
deleted file mode 100644
index ba2205ad3f..0000000000
--- a/pidgin/pidgin-uninstalled.pc.in
+++ /dev/null
@@ -1,18 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-datarootdir=@datarootdir@
-datadir=@datadir@
-sysconfdir=@sysconfdir@
-
-abs_srcdir=@abs_srcdir@
-abs_builddir=@abs_builddir@
-
-plugindir=${libdir}/pidgin
-
-Name: Pidgin
-Description: Pidgin is a GTK2-based instant messenger application.
-Version: @VERSION@
-Requires: gtk+-2.0 purple
-Cflags: -I${abs_srcdir}
diff --git a/pidgin/pidgin.c b/pidgin/pidgin.c
new file mode 100644
index 0000000000..755fbe3646
--- /dev/null
+++ b/pidgin/pidgin.c
@@ -0,0 +1,59 @@
+/*
+ * 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"
+
+#ifdef _WIN32
+/* suppress gcc "no previous prototype" warning */
+int __cdecl pidgin_main(HINSTANCE hint, int argc, char *argv[]);
+int __cdecl pidgin_main(HINSTANCE hint, int argc, char *argv[])
+#else
+int main(int argc, char *argv[])
+#endif
+{
+ const gchar *test_prgname;
+
+#ifdef _WIN32
+ SetConsoleOutputCP(65001); /* UTF-8 */
+#endif
+
+#if !GLIB_CHECK_VERSION(2, 32, 0)
+ /* GLib threading system is automaticaly initialized since 2.32.
+ * For earlier versions, it have to be initialized before calling any
+ * Glib or GTK+ functions.
+ */
+ g_thread_init(NULL);
+#endif
+
+ /* This is for UI testing purposes only, don't use it! */
+ test_prgname = g_getenv("PIDGIN_TEST_PRGNAME");
+ g_set_prgname(test_prgname ? test_prgname : "Pidgin");
+ g_set_application_name(PIDGIN_NAME);
+
+#ifdef _WIN32
+ winpidgin_set_exe_hinstance(hint);
+#endif
+
+ return pidgin_start(argc, argv);
+}
diff --git a/pidgin/pidgin.h b/pidgin/pidgin.h
index 3319f1e9ae..a3ac83f31d 100644
--- a/pidgin/pidgin.h
+++ b/pidgin/pidgin.h
@@ -1,8 +1,3 @@
-/**
- * @file pidgin.h UI definitions and includes
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -24,20 +19,25 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
/* #warning ***pidgin*** */
+
#ifndef _PIDGIN_H_
#define _PIDGIN_H_
+/**
+ * SECTION:pidgin
+ * @section_id: pidgin-pidgin
+ * @short_description: <filename>pidgin.h</filename>
+ * @title: UI Definitions and Includes
+ */
#include <gtk/gtk.h>
-#ifdef GDK_WINDOWING_X11
-# include <gdk/gdkx.h>
-#endif
-
#ifdef _WIN32
-# include "gtkwin32dep.h"
+# include "win32/gtkwin32dep.h"
#endif
/**
+ * PIDGIN_UI:
+ *
* Our UI's identifier.
*/
/* leave this as gtk-gaim until we have a decent way to migrate UI-prefs */
@@ -64,22 +64,12 @@
#define PIDGIN_HIG_BORDER 12
#define PIDGIN_HIG_BOX_SPACE 6
-#if !GTK_CHECK_VERSION(2,16,0) || !defined(PIDGIN_DISABLE_DEPRECATED)
-/*
- * Older versions of GNOME defaulted to using an asterisk as the invisible
- * character. But this is ugly and we want to use something nicer.
- *
- * The default invisible character was changed in GNOME revision 21446
- * (GTK+ 2.16) from an asterisk to the first available character out of
- * 0x25cf, 0x2022, 0x2731, 0x273a. See GNOME bugs 83935 and 307304 for
- * discussion leading up to the change.
- *
- * Here's the change:
- * http://svn.gnome.org/viewvc/gtk%2B?view=revision&revision=21446
+/**
+ * pidgin_start:
*
+ * Start pidgin with the given command line arguments.
*/
-#define PIDGIN_INVISIBLE_CHAR (gunichar)0x25cf
-#endif /* Less than GTK+ 2.16 */
+int pidgin_start(int argc, char *argv[]);
#endif /* _PIDGIN_H_ */
diff --git a/pidgin/pidgin.pc.in b/pidgin/pidgin.pc.in
deleted file mode 100644
index 53e1b88702..0000000000
--- a/pidgin/pidgin.pc.in
+++ /dev/null
@@ -1,16 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-datarootdir=@datarootdir@
-datadir=@datadir@
-sysconfdir=@sysconfdir@
-
-plugindir=${libdir}/pidgin
-
-Name: Pidgin
-Description: Pidgin is a GTK2-based instant messenger application.
-Version: @VERSION@
-Requires: gtk+-2.0 purple
-Cflags: -I${includedir}/pidgin
-
diff --git a/pidgin/pidginstock.c b/pidgin/pidginstock.c
index 79de271094..c8247b44a3 100644
--- a/pidgin/pidginstock.c
+++ b/pidgin/pidginstock.c
@@ -1,8 +1,3 @@
-/**
- * @file pidginstock.c GTK+ Stock resources
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -62,7 +57,7 @@ static struct StockIcon
{ PIDGIN_STOCK_DISCONNECT, NULL, GTK_STOCK_DISCONNECT },
{ PIDGIN_STOCK_FGCOLOR, "buttons", "change-fgcolor-small.png" },
{ PIDGIN_STOCK_EDIT, NULL, GTK_STOCK_EDIT },
- { PIDGIN_STOCK_FILE_CANCELED, NULL, GTK_STOCK_CANCEL },
+ { PIDGIN_STOCK_FILE_CANCELLED, NULL, GTK_STOCK_CANCEL },
{ PIDGIN_STOCK_FILE_DONE, NULL, GTK_STOCK_APPLY },
{ PIDGIN_STOCK_IGNORE, NULL, GTK_STOCK_DIALOG_ERROR },
{ PIDGIN_STOCK_INVITE, NULL, GTK_STOCK_JUMP_TO },
@@ -76,6 +71,7 @@ static struct StockIcon
{ PIDGIN_STOCK_TYPED, "pidgin", "typed.png" },
{ PIDGIN_STOCK_UPLOAD, NULL, GTK_STOCK_GO_UP },
{ PIDGIN_STOCK_INFO, NULL, GTK_STOCK_INFO },
+ { PIDGIN_STOCK_NEXT, NULL, GTK_STOCK_GO_FORWARD },
};
static const GtkStockItem stock_items[] =
@@ -90,12 +86,13 @@ static const GtkStockItem stock_items[] =
{ PIDGIN_STOCK_ADD, N_("_Add..."), 0, 0, PACKAGE },
{ PIDGIN_STOCK_OPEN_MAIL, N_("_Open Mail"), 0, 0, PACKAGE },
{ PIDGIN_STOCK_PAUSE, N_("_Pause"), 0, 0, PACKAGE },
- { PIDGIN_STOCK_EDIT, N_("_Edit"), 0, 0, PACKAGE }
+ { PIDGIN_STOCK_EDIT, N_("_Edit"), 0, 0, PACKAGE },
+ { PIDGIN_STOCK_NEXT, N_("_Next"), 0, 0, PACKAGE },
};
typedef struct {
const char *name;
- const char *dir;
+ const char *dir;
const char *filename;
gboolean microscopic;
gboolean extra_small;
@@ -171,6 +168,7 @@ const SizedStockIcon sized_stock_icons [] = {
{ PIDGIN_STOCK_TOOLBAR_TEXT_LARGER, "toolbar", "font-size-up.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
{ PIDGIN_STOCK_TOOLBAR_INSERT, "toolbar", "insert.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
{ PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE, "toolbar", "insert-image.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+ { PIDGIN_STOCK_TOOLBAR_INSERT_SCREENSHOT, "toolbar", "insert-screenshot.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
{ PIDGIN_STOCK_TOOLBAR_INSERT_LINK, "toolbar", "insert-link.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
{ PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW, "toolbar", "message-new.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
{ PIDGIN_STOCK_TOOLBAR_PENDING, "toolbar", "message-new.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
@@ -241,7 +239,7 @@ find_file_common(const char *name)
return filename;
g_free(filename);
}
- filename = g_build_filename(DATADIR, name, NULL);
+ filename = g_build_filename(PURPLE_DATADIR, name, NULL);
if (g_file_test(filename, G_FILE_TEST_EXISTS))
return filename;
g_free(filename);
@@ -390,9 +388,13 @@ add_sized_icon(GtkIconSet *iconset, GtkIconSize sizeid, PidginIconTheme *theme,
static void
reload_settings(void)
{
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_style_context_reset_widgets(gdk_screen_get_default());
+#else
GtkSettings *setting = NULL;
setting = gtk_settings_get_default();
gtk_rc_reset_styles(setting);
+#endif
}
/*****************************************************************************
@@ -403,7 +405,7 @@ void
pidgin_stock_load_status_icon_theme(PidginStatusIconTheme *theme)
{
GtkIconFactory *icon_factory;
- gint i;
+ gsize i;
GtkIconSet *normal;
GtkIconSet *translucent = NULL;
GtkWidget *win;
@@ -491,7 +493,7 @@ void
pidgin_stock_load_stock_icon_theme(PidginStockIconTheme *theme)
{
GtkIconFactory *icon_factory;
- gint i;
+ gsize i;
GtkWidget *win;
if (theme != NULL) {
@@ -520,8 +522,13 @@ pidgin_stock_load_stock_icon_theme(PidginStockIconTheme *theme)
if (stock_icons[i].dir == NULL) {
/* GTK+ Stock icon */
+#if GTK_CHECK_VERSION(3,0,0)
+ iconset = gtk_style_context_lookup_icon_set(gtk_widget_get_style_context(win),
+ stock_icons[i].filename);
+#else
iconset = gtk_style_lookup_icon_set(gtk_widget_get_style(win),
- stock_icons[i].filename);
+ stock_icons[i].filename);
+#endif
} else {
filename = find_file(stock_icons[i].dir, stock_icons[i].filename);
diff --git a/pidgin/pidginstock.h b/pidgin/pidginstock.h
index 83185aee83..d4ec616ec7 100644
--- a/pidgin/pidginstock.h
+++ b/pidgin/pidginstock.h
@@ -1,8 +1,3 @@
-/**
- * @file pidginstock.h GTK+ Stock resources
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -23,16 +18,23 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#include <gtk/gtk.h>
-#include "gtkstatus-icon-theme.h"
#ifndef _PIDGIN_STOCK_H_
#define _PIDGIN_STOCK_H_
+/**
+ * SECTION:pidginstock
+ * @section_id: pidgin-pidginstock
+ * @short_description: <filename>pidginstock.h</filename>
+ * @title: Stock Resources
+ */
+
+#include <gtk/gtk.h>
+#include "gtkstatus-icon-theme.h"
/**************************************************************************/
-/** @name Stock images */
+/* Stock images */
/**************************************************************************/
-/*@{*/
+
#define PIDGIN_STOCK_ACTION "pidgin-action"
#define PIDGIN_STOCK_ALIAS "pidgin-alias"
#define PIDGIN_STOCK_AWAY "pidgin-away"
@@ -44,7 +46,7 @@
#define PIDGIN_STOCK_DOWNLOAD "pidgin-download"
#define PIDGIN_STOCK_EDIT "pidgin-edit"
#define PIDGIN_STOCK_FGCOLOR "pidgin-fgcolor"
-#define PIDGIN_STOCK_FILE_CANCELED "pidgin-file-cancelled"
+#define PIDGIN_STOCK_FILE_CANCELLED "pidgin-file-cancelled"
#define PIDGIN_STOCK_FILE_DONE "pidgin-file-done"
#define PIDGIN_STOCK_IGNORE "pidgin-ignore"
#define PIDGIN_STOCK_INFO "pidgin-info"
@@ -59,6 +61,7 @@
#define PIDGIN_STOCK_TEXT_NORMAL "pidgin-text-normal"
#define PIDGIN_STOCK_TYPED "pidgin-typed"
#define PIDGIN_STOCK_UPLOAD "pidgin-upload"
+#define PIDGIN_STOCK_NEXT "pidgin-next"
/* Status icons */
#define PIDGIN_STOCK_STATUS_AVAILABLE "pidgin-status-available"
@@ -144,6 +147,7 @@
#define PIDGIN_STOCK_TOOLBAR_TEXT_LARGER "pidgin-text-larger"
#define PIDGIN_STOCK_TOOLBAR_INSERT "pidgin-insert"
#define PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE "pidgin-insert-image"
+#define PIDGIN_STOCK_TOOLBAR_INSERT_SCREENSHOT "pidgin-insert-screenshot"
#define PIDGIN_STOCK_TOOLBAR_INSERT_LINK "pidgin-insert-link"
#define PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW "pidgin-message-new"
#define PIDGIN_STOCK_TOOLBAR_PENDING "pidgin-pending"
@@ -173,9 +177,7 @@
#define PIDGIN_STOCK_TRAY_EMAIL "pidgin-tray-email"
-/*@}*/
-
-/**
+/*
* For using icons that aren't one of the default GTK_ICON_SIZEs
*/
#define PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC "pidgin-icon-size-tango-microscopic"
@@ -185,13 +187,6 @@
#define PIDGIN_ICON_SIZE_TANGO_LARGE "pidgin-icon-size-tango-large"
#define PIDGIN_ICON_SIZE_TANGO_HUGE "pidgin-icon-size-tango-huge"
-/**
- * extends PidginIconTheme (gtkicon-theme.h)
- * A pidgin stock icon theme.
- * This object represents a Pidgin stock icon theme.
- *
- * PidginStockIconTheme is a PidginIconTheme Object.
- */
typedef struct _PidginStockIconTheme PidginStockIconTheme;
typedef struct _PidginStockIconThemeClass PidginStockIconThemeClass;
@@ -202,6 +197,15 @@ typedef struct _PidginStockIconThemeClass PidginStockIconThemeClass;
#define PIDGIN_IS_STOCK_ICON_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_STOCK_ICON_THEME))
#define PIDGIN_STOCK_ICON_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_STOCK_ICON_THEME, PidginStockIconThemeClass))
+/**
+ * PidginStockIconTheme:
+ *
+ * extends PidginIconTheme (gtkicon-theme.h)
+ * A pidgin stock icon theme.
+ * This object represents a Pidgin stock icon theme.
+ *
+ * PidginStockIconTheme is a PidginIconTheme Object.
+ */
struct _PidginStockIconTheme
{
PidginIconTheme parent;
@@ -215,15 +219,17 @@ struct _PidginStockIconThemeClass
G_BEGIN_DECLS
/**
- * GObject foo.
- * @internal.
+ * pidgin_stock_icon_theme_get_type:
+ *
+ * Returns: The #GType for a stock icon theme.
*/
GType pidgin_stock_icon_theme_get_type(void);
/**
- * Loades all of the icons from the status icon theme into Pidgin stock
+ * pidgin_stock_load_status_icon_theme:
+ * @theme: the theme to load, or null to load all the default icons
*
- * @param theme the theme to load, or null to load all the default icons
+ * Loades all of the icons from the status icon theme into Pidgin stock
*/
void pidgin_stock_load_status_icon_theme(PidginStatusIconTheme *theme);
@@ -231,9 +237,12 @@ void pidgin_stock_load_status_icon_theme(PidginStatusIconTheme *theme);
void pidgin_stock_load_stock_icon_theme(PidginStockIconTheme *theme);
/**
+ * pidgin_stock_init:
+ *
* Sets up the purple stock repository.
*/
void pidgin_stock_init(void);
G_END_DECLS
+
#endif /* _PIDGIN_STOCK_H_ */
diff --git a/pidgin/pidgintooltip.c b/pidgin/pidgintooltip.c
index ca66b1c46c..d46be2e4a8 100644
--- a/pidgin/pidgintooltip.c
+++ b/pidgin/pidgintooltip.c
@@ -1,8 +1,3 @@
-/**
- * @file pidgintooltip.c Pidgin Tooltip API
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -61,22 +56,15 @@ typedef struct
static void
initialize_tooltip_delay()
{
-#if GTK_CHECK_VERSION(2,14,0)
GtkSettings *settings;
-#endif
if (tooltip_delay != -1)
return;
-#if GTK_CHECK_VERSION(2,14,0)
settings = gtk_settings_get_default();
g_object_get(settings, "gtk-enable-tooltips", &enable_tooltips, NULL);
g_object_get(settings, "gtk-tooltip-timeout", &tooltip_delay, NULL);
-#else
- tooltip_delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay");
- enable_tooltips = (tooltip_delay != 0);
-#endif
}
static void
@@ -100,16 +88,37 @@ void pidgin_tooltip_destroy()
}
}
+#if GTK_CHECK_VERSION(3,0,0)
+static gboolean
+pidgin_tooltip_draw_cb(GtkWidget *widget, cairo_t *cr, gpointer data)
+{
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation(widget, &allocation);
+
+ if (pidgin_tooltip.paint_tooltip) {
+ GtkStyleContext *context = gtk_widget_get_style_context(widget);
+ gtk_style_context_add_class(context, GTK_STYLE_CLASS_TOOLTIP);
+ gtk_render_background(context, cr,
+ 0, 0, allocation.width, allocation.height);
+ pidgin_tooltip.paint_tooltip(widget, cr, data);
+ }
+ return FALSE;
+}
+#else
static gboolean
pidgin_tooltip_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
if (pidgin_tooltip.paint_tooltip) {
+ cairo_t *cr = gdk_cairo_create(GDK_DRAWABLE(gtk_widget_get_window(widget)));
gtk_paint_flat_box(widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
- NULL, widget, "tooltip", 0, 0, -1, -1);
- pidgin_tooltip.paint_tooltip(widget, data);
+ NULL, widget, "tooltip", 0, 0, -1, -1);
+ pidgin_tooltip.paint_tooltip(widget, cr, data);
+ cairo_destroy(cr);
}
return FALSE;
}
+#endif
static GtkWidget*
setup_tooltip_window(void)
@@ -140,7 +149,18 @@ setup_tooltip_window_position(gpointer data, int w, int h)
GdkRectangle mon_size;
GtkWidget *tipwindow = pidgin_tooltip.tipwindow;
- gdk_display_get_pointer(gdk_display_get_default(), &screen, &x, &y, NULL);
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkDeviceManager *devmgr;
+ GdkDevice *dev;
+
+ devmgr = gdk_display_get_device_manager(gdk_display_get_default());
+ dev = gdk_device_manager_get_client_pointer(devmgr);
+ gdk_device_get_position(dev, &screen, &x, &y);
+#else
+ gdk_display_get_pointer(gdk_display_get_default(),
+ &screen, &x, &y, NULL);
+#endif
+
mon_num = gdk_screen_get_monitor_at_point(screen, x, y);
gdk_screen_get_monitor_geometry(screen, mon_num, &mon_size);
@@ -189,8 +209,13 @@ setup_tooltip_window_position(gpointer data, int w, int h)
gtk_window_move(GTK_WINDOW(tipwindow), x, y);
gtk_widget_show(tipwindow);
+#if GTK_CHECK_VERSION(3,0,0)
+ g_signal_connect(G_OBJECT(tipwindow), "draw",
+ G_CALLBACK(pidgin_tooltip_draw_cb), data);
+#else
g_signal_connect(G_OBJECT(tipwindow), "expose_event",
G_CALLBACK(pidgin_tooltip_expose_event), data);
+#endif
/* Hide the tooltip when the widget is destroyed */
sig = g_signal_connect(G_OBJECT(pidgin_tooltip.widget), "destroy", G_CALLBACK(pidgin_tooltip_destroy), NULL);
diff --git a/pidgin/pidgintooltip.h b/pidgin/pidgintooltip.h
index 889232341e..916b55280f 100644
--- a/pidgin/pidgintooltip.h
+++ b/pidgin/pidgintooltip.h
@@ -1,8 +1,3 @@
-/**
- * @file pidgintooltip.h Pidgin Tooltip API
- * @ingroup pidgin
- */
-
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
@@ -23,90 +18,103 @@
* 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_TOOLTIP_H_
#define _PIDGIN_TOOLTIP_H_
+/**
+ * SECTION:pidgintooltip
+ * @section_id: pidgin-pidgintooltip
+ * @short_description: <filename>pidgintooltip.h</filename>
+ * @title: Pidgin Tooltip API
+ */
#include <gtk/gtk.h>
/**
- * @param tipwindow The window for the tooltip.
- * @param path The GtkTreePath representing the row under the cursor.
- * @param userdata The userdata set during pidgin_tooltip_setup_for_treeview.
- * @param w The value of this should be set to the desired width of the tooltip window.
- * @param h The value of this should be set to the desired height of the tooltip window.
+ * PidginTooltipCreateForTree:
+ * @tipwindow: The window for the tooltip.
+ * @path: The GtkTreePath representing the row under the cursor.
+ * @userdata: The userdata set during pidgin_tooltip_setup_for_treeview.
+ * @w: The value of this should be set to the desired width of the tooltip window.
+ * @h: The value of this should be set to the desired height of the tooltip window.
*
- * @return @c TRUE if the tooltip was created correctly, @c FALSE otherwise.
- * @since 2.4.0
+ * Returns: %TRUE if the tooltip was created correctly, %FALSE otherwise.
*/
typedef gboolean (*PidginTooltipCreateForTree)(GtkWidget *tipwindow,
GtkTreePath *path, gpointer userdata, int *w, int *h);
/**
- * @param tipwindow The window for the tooltip.
- * @param userdata The userdata set during pidgin_tooltip_show.
- * @param w The value of this should be set to the desired width of the tooltip window.
- * @param h The value of this should be set to the desired height of the tooltip window.
+ * PidginTooltipCreate:
+ * @tipwindow: The window for the tooltip.
+ * @userdata: The userdata set during pidgin_tooltip_show.
+ * @w: The value of this should be set to the desired width of the tooltip window.
+ * @h: The value of this should be set to the desired height of the tooltip window.
*
- * @return @c TRUE if the tooltip was created correctly, @c FALSE otherwise.
- * @since 2.4.0
+ * Returns: %TRUE if the tooltip was created correctly, %FALSE otherwise.
*/
typedef gboolean (*PidginTooltipCreate)(GtkWidget *tipwindow,
gpointer userdata, int *w, int *h);
/**
- * @param tipwindow The window for the tooltip.
- * @param userdata The userdata set during pidgin_tooltip_setup_for_treeview or pidgin_tooltip_show.
+ * PidginTooltipPaint:
+ * @tipwindow: The window for the tooltip.
+ * @cr: The cairo context for drawing.
+ * @userdata: The userdata set during pidgin_tooltip_setup_for_treeview or pidgin_tooltip_show.
*
- * @return @c TRUE if the tooltip was painted correctly, @c FALSE otherwise.
- * @since 2.4.0
+ * Returns: %TRUE if the tooltip was painted correctly, %FALSE otherwise.
*/
-typedef gboolean (*PidginTooltipPaint)(GtkWidget *tipwindow, gpointer userdata);
+typedef gboolean (*PidginTooltipPaint)(GtkWidget *tipwindow, cairo_t *cr,
+ gpointer userdata);
+
+G_BEGIN_DECLS
/**
- * Setup tooltip drawing functions for a treeview.
+ * pidgin_tooltip_setup_for_treeview:
+ * @tree: The treeview
+ * @userdata: The userdata to send to the callback functions
+ * @create_cb: Callback function to create the tooltip for a GtkTreePath
+ * @paint_cb: Callback function to paint the tooltip
*
- * @param tree The treeview
- * @param userdata The userdata to send to the callback functions
- * @param create_cb Callback function to create the tooltip for a GtkTreePath
- * @param paint_cb Callback function to paint the tooltip
+ * Setup tooltip drawing functions for a treeview.
*
- * @return @c TRUE if the tooltip callbacks were setup correctly.
- * @since 2.4.0
+ * Returns: %TRUE if the tooltip callbacks were setup correctly.
*/
gboolean pidgin_tooltip_setup_for_treeview(GtkWidget *tree, gpointer userdata,
PidginTooltipCreateForTree create_cb, PidginTooltipPaint paint_cb);
/**
- * Setup tooltip drawing functions for any widget.
+ * pidgin_tooltip_setup_for_widget:
+ * @widget: The widget
+ * @userdata: The userdata to send to the callback functions
+ * @create_cb: Callback function to create the tooltip for the widget
+ * @paint_cb: Callback function to paint the tooltip
*
- * @param widget The widget
- * @param userdata The userdata to send to the callback functions
- * @param create_cb Callback function to create the tooltip for the widget
- * @param paint_cb Callback function to paint the tooltip
+ * Setup tooltip drawing functions for any widget.
*
- * @return @c TRUE if the tooltip callbacks were setup correctly.
- * @since 2.4.0
+ * Returns: %TRUE if the tooltip callbacks were setup correctly.
*/
gboolean pidgin_tooltip_setup_for_widget(GtkWidget *widget, gpointer userdata,
PidginTooltipCreate create_cb, PidginTooltipPaint paint_cb);
/**
+ * pidgin_tooltip_destroy:
+ *
* Destroy the tooltip.
- * @since 2.4.0
*/
void pidgin_tooltip_destroy(void);
/**
- * Create and show a tooltip.
- *
- * @param widget The widget the tooltip is for
- * @param userdata The userdata to send to the callback functions
- * @param create_cb Callback function to create the tooltip from the GtkTreePath
- * @param paint_cb Callback function to paint the tooltip
+ * pidgin_tooltip_show:
+ * @widget: The widget the tooltip is for
+ * @userdata: The userdata to send to the callback functions
+ * @create_cb: Callback function to create the tooltip from the GtkTreePath
+ * @paint_cb: Callback function to paint the tooltip
*
- * @since 2.4.0
+ * Create and show a tooltip.
*/
void pidgin_tooltip_show(GtkWidget *widget, gpointer userdata,
PidginTooltipCreate create_cb, PidginTooltipPaint paint_cb);
+G_END_DECLS
+
#endif
diff --git a/pidgin/pixmaps/Makefile.am b/pidgin/pixmaps/Makefile.am
index 44354b36ae..efcb6d7a6c 100644
--- a/pidgin/pixmaps/Makefile.am
+++ b/pidgin/pixmaps/Makefile.am
@@ -1,12 +1,11 @@
pidginpixmapdir = $(datadir)/pixmaps/pidgin
pidginiconsdir = $(datadir)
-SUBDIRS = emotes/default/24 emotes/none emotes/small/16
+SUBDIRS = emotes/default/24 emotes/small/16
if INSTALL_PIXMAPS
MAKEFILE_MINGW = \
emotes/default/24/Makefile.mingw \
- emotes/none/Makefile.mingw \
emotes/small/16/Makefile.mingw
ANIMATIONS_16 = \
@@ -88,6 +87,12 @@ DIALOGS_SCALABLE = \
dialogs/scalable/question.svg \
dialogs/scalable/warning.svg
+E2EE_STATES_16 = \
+ e2ee/16/finished.png \
+ e2ee/16/not-private.png \
+ e2ee/16/private.png \
+ e2ee/16/unverified.png
+
EMBLEMS_16 = \
emblems/16/aol-client.png \
emblems/16/birthday.png \
@@ -231,7 +236,6 @@ PROTOCOLS_16 = \
protocols/16/meanwhile.png \
protocols/16/msn.png \
protocols/16/mxit.png \
- protocols/16/myspace.png \
protocols/16/silc.png \
protocols/16/simple.png \
protocols/16/yahoo.png \
@@ -286,7 +290,6 @@ PROTOCOLS_22 = \
protocols/22/meanwhile.png \
protocols/22/msn.png \
protocols/22/mxit.png \
- protocols/22/myspace.png \
protocols/22/silc.png \
protocols/22/simple.png \
protocols/22/yahoo.png \
@@ -297,6 +300,7 @@ PROTOCOLS_48 = \
protocols/48/bonjour.png \
protocols/48/facebook.png \
protocols/48/gadu-gadu.png \
+ protocols/48/google-talk.png \
protocols/48/novell.png \
protocols/48/icq.png \
protocols/48/irc.png \
@@ -304,7 +308,6 @@ PROTOCOLS_48 = \
protocols/48/meanwhile.png \
protocols/48/msn.png \
protocols/48/mxit.png \
- protocols/48/myspace.png \
protocols/48/silc.png \
protocols/48/simple.png \
protocols/48/yahoo.png \
@@ -433,6 +436,7 @@ TOOLBAR_16 = \
toolbar/16/insert.png \
toolbar/16/insert-image.png \
toolbar/16/insert-link.png \
+ toolbar/16/insert-screenshot.png \
toolbar/16/message-new.png \
toolbar/16/plugins.png \
toolbar/16/send-file.png \
@@ -560,6 +564,7 @@ nobase_dist_pidginpixmap_DATA = \
$(DIALOGS_16) \
$(DIALOGS_64) \
$(DIALOGS_SCALABLE) \
+ $(E2EE_STATES_16) \
$(EMBLEMS_16) \
$(EMBLEMS_SCALABLE) \
$(PROTOCOLS_16) \
diff --git a/pidgin/pixmaps/Makefile.mingw b/pidgin/pixmaps/Makefile.mingw
index 24ca3e0b82..ad9ab37dfb 100644
--- a/pidgin/pixmaps/Makefile.mingw
+++ b/pidgin/pixmaps/Makefile.mingw
@@ -13,7 +13,7 @@ datadir := $(PIDGIN_INSTALL_DIR)
.PHONY: install clean
install: ./Makefile.am.mingw
- if test '$(SUBDIRS)'; then \
+ $(MAKE_at) if test '$(SUBDIRS)'; then \
list='$(SUBDIRS)'; for subdir in $$list; do \
$(MAKE) -C $$subdir -f $(MINGW_MAKEFILE) install || exit 1 ;\
done; \
@@ -35,7 +35,7 @@ install: ./Makefile.am.mingw
fi;
clean: ./Makefile.am.mingw
- if test '$(SUBDIRS)'; then \
+ $(MAKE_at) if test '$(SUBDIRS)'; then \
list='$(SUBDIRS)'; for subdir in $$list; do \
$(MAKE) -C $$subdir -f $(MINGW_MAKEFILE) clean;\
done; \
diff --git a/pidgin/pixmaps/e2ee/16/finished.png b/pidgin/pixmaps/e2ee/16/finished.png
new file mode 100644
index 0000000000..84d219a33a
--- /dev/null
+++ b/pidgin/pixmaps/e2ee/16/finished.png
Binary files differ
diff --git a/pidgin/pixmaps/e2ee/16/not-private.png b/pidgin/pixmaps/e2ee/16/not-private.png
new file mode 100644
index 0000000000..68ff7c5322
--- /dev/null
+++ b/pidgin/pixmaps/e2ee/16/not-private.png
Binary files differ
diff --git a/pidgin/pixmaps/e2ee/16/private.png b/pidgin/pixmaps/e2ee/16/private.png
new file mode 100644
index 0000000000..577de999c5
--- /dev/null
+++ b/pidgin/pixmaps/e2ee/16/private.png
Binary files differ
diff --git a/pidgin/pixmaps/e2ee/16/unverified.png b/pidgin/pixmaps/e2ee/16/unverified.png
new file mode 100644
index 0000000000..e748fa5024
--- /dev/null
+++ b/pidgin/pixmaps/e2ee/16/unverified.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/Makefile.am b/pidgin/pixmaps/emotes/default/24/Makefile.am
index ffd6edb05f..5e6d9eb610 100644
--- a/pidgin/pixmaps/emotes/default/24/Makefile.am
+++ b/pidgin/pixmaps/emotes/default/24/Makefile.am
@@ -18,7 +18,6 @@ SMILEYS = \
bowl.png \
boy.png \
brb.png \
- bulgy-eyes.png \
bunny.png \
bye.png \
cake.png \
@@ -70,7 +69,6 @@ SMILEYS = \
fingers-crossed.png \
flag.png \
foot-in-mouth.png \
- freaked-out.png \
ghost.png \
giggle.png \
girl.png \
@@ -106,9 +104,7 @@ SMILEYS = \
male-fighter2.png \
mean.png \
meeting.png \
- messed.png \
mobile.png \
- mohawk.png \
moneymouth.png \
monkey.png \
moon.png \
@@ -152,15 +148,12 @@ SMILEYS = \
shout.png \
shut-mouth.png \
sick.png \
- sidefrown.png \
silly.png \
- sinister.png \
skeleton.png \
skywalker.png \
sleeping.png \
sleepy.png \
happy.png \
- smirk.png \
snail.png \
snicker.png \
snowman.png \
@@ -205,7 +198,7 @@ pidginsmileypix_DATA = \
theme
theme: default.theme.in
- sed -e 's/^_Name=/Name=/' \
+ $(AM_V_GEN) sed -e 's/^_Name=/Name=/' \
-e 's/^_Description=/Description=/' \
-e 's/^_Author=/Author=/' \
$< > $@
diff --git a/pidgin/pixmaps/emotes/default/24/bulgy-eyes.png b/pidgin/pixmaps/emotes/default/24/bulgy-eyes.png
deleted file mode 100644
index 8cc7c3cd6e..0000000000
--- a/pidgin/pixmaps/emotes/default/24/bulgy-eyes.png
+++ /dev/null
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/default.theme.in b/pidgin/pixmaps/emotes/default/24/default.theme.in
index e7c3714783..a5a03fde71 100644
--- a/pidgin/pixmaps/emotes/default/24/default.theme.in
+++ b/pidgin/pixmaps/emotes/default/24/default.theme.in
@@ -189,106 +189,6 @@ console.png (xx) (XX)
fingers-crossed.png (yn) (YN)
-# Following QQ 2006
-[QQ]
-shocked.png /:O /jy /surprised
-curl-lip.png /:~ /pz /curl_lip
-desire.png /:* /se /desire
-dazed.png /:| /dazed
-party.png /8-) /dy /revel
-crying.png /:< /ll /cry
-bashful.png /:$ /hx /bashful
-shut-mouth.png /:X /bz /shut_mouth
-sleeping.png /:Z /shui /sleep
-weep.png /:'( /dk /weep
-embarrassed.png /:-| /gg /embarassed
-pissed-off.png /:@ /fn /pissed_off
-act-up.png /:P /tp /act_up
-excited.png /:D /cy /toothy_smile
-happy.png /:) /wx /small_smile
-sad.png /:( /ng /sad
-glasses-cool.png /:+ /kuk /cool
-doctor.png /:# /feid /SARS
-silly.png /:Q /zk /crazy
-sick.png /:T /tu /vomit
-snicker.png /;p /tx /titter
-cute.png /;-D /ka /cute
-disdain.png /;d /by /disdain
-arrogant.png /;o /am /arrogant
-starving.png /:g /jie /starving
-sleepy.png /|-) /kun /sleepy
-terror.png /:! /jk /terror
-hot.png /:L /sweat
-smirk.png /:> /hanx /smirk
-soldier.png /:; /db /soldier
-struggle.png /;f /fendou /struggle
-curse.png /:-S /zhm /curse
-question.png /? /yiw /question
-quiet.png /;x /xu /shh
-hypnotized.png /;@ /yun /dizzy
-excruciating.png /:8 /zhem /excrutiating
-freaked-out.png /;! /shuai /freaked_out
-skeleton.png /!!! /kl /skeleton
-hammer.png /xx /qiao /hammer
-bye.png /bye /zj /bye
-go-away.png /go /shan /go
-afraid.png /shake /fad /shake
-amorous.png /love /aiq /love
-jump.png /jump /tiao /jump
-search.png /find /zhao /search
-lashes.png /& /mm /beautiful_eyebrows
-pig.png /pig /zt /pig
-cat.png /cat /mm /cat
-dog.png /dog /xg /dog
-hug-left.png /hug /yb /hug
-coins.png /$ /qianc /money
-lamp.png /! /dp /lightbulb
-bowl.png /cup /bei /cup
-cake.png /cake /dg /cake
-thunder.png /li /shd /lightning
-bomb.png /bome /zhd /bomb
-knife.png /kn /dao /knife
-soccerball.png /footb /zq /soccer
-musical-note.png /music /yy /music
-poop.png /shit /bb /shit
-coffee.png /coffee /kf /coffee
-hungry.png /eat /fan /eat
-pill.png /pill /yw /pill
-rose.png /rose /mg /rose
-wilt.png /fade /dx /wilt
-kiss.png /kiss /wen /kiss
-in_love.png /heart /xin /heart
-love-over.png /break /xs /broken_heart
-meeting.png /meeting /hy /meeting
-present.png /gift /lw /gift
-phone.png /phone /dh /phone
-clock.png /time /sj /time
-mail.png /email /yj /email
-tv.png /TV /ds /TV
-sun.png /sun /ty /sun
-moon.png /moon /yl /moon
-good.png /strong /qiang /thumbs_up
-bad.png /weak /ruo /thumbs_down
-handshake.png /share /ws /handshake
-victory.png /v /shl /victory
-beauty.png /<J> /mn /beauty
-qq.png /<QQ> /qz /qq
-blowkiss.png /<L> /fw /blow_kiss
-angry.png /<O> /oh /angry
-liquor.png /<B> /bj /baijiu
-can.png /<U> /qsh /soda
-watermelon.png /<W> /xigua /watermelon
-rain.png /<!!> /xy /rain
-cloudy.png /<~> /duoy /cloudy
-snowman.png /<Z> /xr /snowman
-star.png /<*> /xixing /star
-girl.png /<00> /nv /woman
-boy.png /<11> /nan /man
-! skywalker.png C:-) c:-) C:) c:)
-! monkey.png :-(|) :(|) 8-|)
-! cyclops.png O-) o-)
-
-
# Following ICQ 6.0
[ICQ]
happy.png :-) :)
@@ -512,38 +412,6 @@ female-fighter.png o-+ O-+
yin-yang.png (%)
-# Following MySpaceIM Beta 1.0.697.0
-[MySpaceIM]
-excited.png :D :-D
-devil.png }:)
-confused.png :Z
-glasses-nerdy.png B)
-bulgy-eyes.png %)
-freaked-out.png :E
-happy.png :) :-)
-amorous.png :X
-laugh.png :))
-mohawk.png -:
-mad-tongue.png X(
-messed.png X)
-glasses-nerdy.png Q)
-doh.png :G
-pirate.png P)
-shocked.png :O
-sidefrown.png :{
-sinister.png :B
-smirk.png :,
-neutral.png :|
-tongue.png :P :p
-pissed-off.png B|
-wink.png ;-) ;)
-sad.png :[
-kiss.png :x
-! skywalker.png C:-) c:-) C:) c:)
-! monkey.png :-(|) :(|) 8-|)
-! cyclops.png O-) o-)
-
-
# MXit standard emoticons
[MXit]
happy.png :-) :)
diff --git a/pidgin/pixmaps/emotes/default/24/freaked-out.png b/pidgin/pixmaps/emotes/default/24/freaked-out.png
deleted file mode 100644
index 85ca5717cb..0000000000
--- a/pidgin/pixmaps/emotes/default/24/freaked-out.png
+++ /dev/null
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/messed.png b/pidgin/pixmaps/emotes/default/24/messed.png
deleted file mode 100644
index f76307f631..0000000000
--- a/pidgin/pixmaps/emotes/default/24/messed.png
+++ /dev/null
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/mohawk.png b/pidgin/pixmaps/emotes/default/24/mohawk.png
deleted file mode 100644
index 60a9f0ffb3..0000000000
--- a/pidgin/pixmaps/emotes/default/24/mohawk.png
+++ /dev/null
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/sidefrown.png b/pidgin/pixmaps/emotes/default/24/sidefrown.png
deleted file mode 100644
index 2d4e422061..0000000000
--- a/pidgin/pixmaps/emotes/default/24/sidefrown.png
+++ /dev/null
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/sinister.png b/pidgin/pixmaps/emotes/default/24/sinister.png
deleted file mode 100644
index f26a3a6171..0000000000
--- a/pidgin/pixmaps/emotes/default/24/sinister.png
+++ /dev/null
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/smirk.png b/pidgin/pixmaps/emotes/default/24/smirk.png
deleted file mode 100644
index c458e1a60a..0000000000
--- a/pidgin/pixmaps/emotes/default/24/smirk.png
+++ /dev/null
Binary files differ
diff --git a/pidgin/pixmaps/emotes/none/Makefile.am b/pidgin/pixmaps/emotes/none/Makefile.am
deleted file mode 100644
index 40ddc6d03f..0000000000
--- a/pidgin/pixmaps/emotes/none/Makefile.am
+++ /dev/null
@@ -1,17 +0,0 @@
-pidginsmileypix_in_files = none.theme.in
-
-if INSTALL_PIXMAPS
-pidginsmileypixdir = $(datadir)/pixmaps/pidgin/emotes/none
-pidginsmileypix_DATA = theme
-
-theme: none.theme.in
- sed -e 's/^_Name=/Name=/' \
- -e 's/^_Description=/Description=/' \
- -e 's/^_Author=/Author=/' \
- $< > $@
-endif
-
-EXTRA_DIST = \
- $(pidginsmileypix_in_files) \
- Makefile.mingw \
- theme
diff --git a/pidgin/pixmaps/emotes/none/Makefile.mingw b/pidgin/pixmaps/emotes/none/Makefile.mingw
deleted file mode 100644
index bcc6d5dfe4..0000000000
--- a/pidgin/pixmaps/emotes/none/Makefile.mingw
+++ /dev/null
@@ -1,27 +0,0 @@
-#
-# Makefile.mingw
-#
-# Description: Makefile for win32 (mingw) version of Pidgin pixmaps
-#
-
-PIDGIN_TREE_TOP := ../../../..
-include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
-
-datadir = $(PIDGIN_INSTALL_DIR)
--include ./Makefile.am.mingw
-
-.PHONY: install clean
-
-install: ./Makefile.am.mingw theme
- if test '$(pidginsmileypix_DATA)'; then \
- mkdir -p $(pidginsmileypixdir); \
- cp $(pidginsmileypix_DATA) $(pidginsmileypixdir); \
- fi;
-
-clean:
- rm -f theme ./Makefile.am.mingw
-
-./Makefile.am.mingw: ./Makefile.am
- sed -e 's/^if\ INSTALL_PIXMAPS/ifeq (\$$(INSTALL_PIXMAPS), 1)/' ./Makefile.am > $@
- include $@
-
diff --git a/pidgin/pixmaps/emotes/none/none.theme.in b/pidgin/pixmaps/emotes/none/none.theme.in
deleted file mode 100644
index ce39d93084..0000000000
--- a/pidgin/pixmaps/emotes/none/none.theme.in
+++ /dev/null
@@ -1,3 +0,0 @@
-_Name=none
-_Description=Selecting this disables graphical emoticons.
-_Author=Penguin Pimps
diff --git a/pidgin/pixmaps/emotes/small/16/Makefile.am b/pidgin/pixmaps/emotes/small/16/Makefile.am
index af06ecce1b..4f3ddf2686 100644
--- a/pidgin/pixmaps/emotes/small/16/Makefile.am
+++ b/pidgin/pixmaps/emotes/small/16/Makefile.am
@@ -43,6 +43,7 @@ SMILEYS = \
excruciating.png \
eyeroll.png \
girl.png \
+ glasses-cool.png \
happy.png \
hug-left.png \
hug-right.png \
@@ -65,6 +66,7 @@ SMILEYS = \
question.png \
rose.png \
sad.png \
+ sarcastic.png \
shame.png \
shocked.png \
shut-mouth.png \
@@ -92,7 +94,7 @@ pidginsmileypix_DATA = \
theme
theme: small.theme.in
- sed -e 's/^_Name=/Name=/' \
+ $(AM_V_GEN) sed -e 's/^_Name=/Name=/' \
-e 's/^_Description=/Description=/' \
-e 's/^_Author=/Author=/' \
$< > $@
diff --git a/pidgin/pixmaps/emotes/small/16/small.theme.in b/pidgin/pixmaps/emotes/small/16/small.theme.in
index cba32b4378..998b1c1b4f 100644
--- a/pidgin/pixmaps/emotes/small/16/small.theme.in
+++ b/pidgin/pixmaps/emotes/small/16/small.theme.in
@@ -118,44 +118,6 @@ cigarette.png (ci) (CI)
console.png (xx) (XX)
-# Following QQ 2006
-[QQ]
-shocked.png /:O /jy /surprised
-party.png /8-) /dy /revel
-crying.png /:< /ll /cry
-shut-mouth.png /:X /bz /shut_mouth
-sleeping.png /:Z /shui /sleep
-embarrassed.png /:-| /gg /embarassed
-pissed-off.png /:@ /fn /pissed_off
-excited.png /:D /cy /toothy_smile
-happy.png /:) /wx /small_smile
-sad.png /:( /ng /sad
-glasses-cool.png /:+ /kuk /cool
-sick.png /:T /tu /vomit
-sleepy.png /|-) /kun /sleepy
-hot.png /:L /sweat
-question.png /? /yiw /question
-excruciating.png /:8 /zhem /excrutiating
-afraid.png /shake /fad /shake
-amorous.png /love /aiq /love
-search.png /find /zhao /search
-hug-left.png /hug /yb /hug
-lamp.png /! /dp /lightbulb
-thunder.png /li /shd /lightning
-musical-note.png /music /yy /music
-coffee.png /coffee /kf /coffee
-hungry.png /eat /fan /eat
-rose.png /rose /mg /rose
-kiss.png /kiss /wen /kiss
-in_love.png /heart /xin /heart
-meeting.png /meeting /hy /meeting
-phone.png /phone /dh /phone
-tv.png /TV /ds /TV
-angry.png /<O> /oh /angry
-girl.png /<00> /nv /woman
-boy.png /<11> /nan /man
-
-
# Following ICQ 6.0
[ICQ]
happy.png :-) :)
@@ -260,22 +222,6 @@ musical-note.png :-"
star.png (*)
-# Following MySpaceIM Beta 1.0.697.0
-[MySpaceIM]
-excited.png :D :-D
-devil.png }:)
-confused.png :Z
-happy.png :) :-)
-amorous.png :X
-pirate.png P)
-shocked.png :O
-neutral.png :|
-tongue.png :P :p
-pissed-off.png B|
-wink.png ;-) ;)
-sad.png :[
-kiss.png :x
-
# MXit standard emoticons
[MXit]
happy.png :-) :)
diff --git a/pidgin/pixmaps/protocols/16/myspace.png b/pidgin/pixmaps/protocols/16/myspace.png
deleted file mode 100644
index e7c3034cc9..0000000000
--- a/pidgin/pixmaps/protocols/16/myspace.png
+++ /dev/null
Binary files differ
diff --git a/pidgin/pixmaps/protocols/22/myspace.png b/pidgin/pixmaps/protocols/22/myspace.png
deleted file mode 100644
index 2b6020b58c..0000000000
--- a/pidgin/pixmaps/protocols/22/myspace.png
+++ /dev/null
Binary files differ
diff --git a/pidgin/pixmaps/protocols/22/scalable/myspace.svg b/pidgin/pixmaps/protocols/22/scalable/myspace.svg
deleted file mode 100644
index daba8a66ec..0000000000
--- a/pidgin/pixmaps/protocols/22/scalable/myspace.svg
+++ /dev/null
@@ -1,93 +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="24"
- height="24"
- id="svg2160"
- sodipodi:version="0.32"
- inkscape:version="0.46"
- sodipodi:docname="myspace.svg"
- sodipodi:docbase="/home/hbons/Desktop"
- inkscape:output_extension="org.inkscape.output.svg.inkscape"
- sodipodi:modified="true"
- version="1.0">
- <defs
- id="defs2162">
- <linearGradient
- inkscape:collect="always"
- id="linearGradient3147">
- <stop
- style="stop-color:#eeeeec;stop-opacity:1;"
- offset="0"
- id="stop3149" />
- <stop
- style="stop-color:#eeeeec;stop-opacity:0;"
- offset="1"
- id="stop3151" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient3147"
- id="linearGradient3153"
- x1="6.0203052"
- y1="1.0398448"
- x2="26.101271"
- y2="25.83909"
- gradientUnits="userSpaceOnUse" />
- </defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="9.8994949"
- inkscape:cx="31.349135"
- inkscape:cy="11.3794"
- inkscape:current-layer="layer1"
- showgrid="true"
- inkscape:grid-bbox="true"
- inkscape:document-units="px"
- inkscape:window-width="1440"
- inkscape:window-height="847"
- inkscape:window-x="3"
- inkscape:window-y="25"
- width="24px"
- height="24px" />
- <metadata
- id="metadata2165">
- <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="opacity:1;fill:#babdb6;fill-opacity:1;stroke:#888a85;stroke-width:1.00000012;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="M 17 1.5 C 15.068 1.5 13.5 3.0680001 13.5 5 C 13.5 6.932 15.068 8.5 17 8.5 C 18.932 8.5 20.499999 6.932 20.5 5 C 20.5 3.068 18.931999 1.4999999 17 1.5 z M 10.5 2.5 C 8.8439999 2.5 7.5000001 3.8439999 7.5 5.5 C 7.5 7.1559999 8.8439997 8.4999996 10.5 8.5 C 9.395701 8.5527352 8.4448563 9.5454035 7.875 10.4375 C 7.830859 8.7148111 7.161553 7.5791344 5.6875 7.5 C 6.7035586 7.4052513 7.5 6.5406823 7.5 5.5 C 7.5 4.396 6.604 3.5 5.5 3.5 C 4.396 3.5 3.4999999 4.396 3.5 5.5 C 3.5 6.5828941 4.3626534 7.4666427 5.4375 7.5 C 4.352857 7.6401732 3.5000005 8.6243123 3.5 9.875 L 3.5 14.53125 L 7.5625 14.53125 L 7.5625 18.53125 L 12.5 18.53125 L 12.5 22.5 L 21.5 22.5 C 21.499867 19.408873 21.5 16.303843 21.5 13.21875 C 21.5 10.634939 19.434134 8.53125 16.875 8.53125 C 15.587036 8.5312503 14.40291 9.424387 13.5625 10.28125 C 13.046577 9.1148819 11.916283 8.5 10.59375 8.5 C 12.205095 8.4489386 13.5 7.1236737 13.5 5.5 C 13.5 3.8440001 12.156 2.5 10.5 2.5 z "
- id="path3149" />
- <path
- sodipodi:type="inkscape:offset"
- inkscape:radius="-1.00878"
- inkscape:original="M 17 1.5 C 15.068 1.5 13.5 3.0680001 13.5 5 C 13.5 6.932 15.068 8.5 17 8.5 C 18.932 8.5 20.499999 6.932 20.5 5 C 20.5 3.068 18.931999 1.4999999 17 1.5 z M 10.5 2.5 C 8.8439999 2.5 7.5000001 3.8439999 7.5 5.5 C 7.5 7.1559999 8.8439997 8.4999996 10.5 8.5 C 9.395701 8.5527352 8.4448563 9.5454035 7.875 10.4375 C 7.830859 8.7148111 7.161553 7.5791344 5.6875 7.5 C 6.7035586 7.4052513 7.5 6.5406823 7.5 5.5 C 7.5 4.396 6.604 3.5 5.5 3.5 C 4.396 3.5 3.4999999 4.396 3.5 5.5 C 3.5 6.5828941 4.3626534 7.4666427 5.4375 7.5 C 4.352857 7.6401732 3.5000005 8.6243123 3.5 9.875 L 3.5 14.53125 L 7.5625 14.53125 L 7.5625 18.53125 L 12.5 18.53125 L 12.5 22.5 L 21.5 22.5 C 21.499867 19.408873 21.5 16.303843 21.5 13.21875 C 21.5 10.634939 19.434134 8.53125 16.875 8.53125 C 15.587036 8.5312503 14.40291 9.424387 13.5625 10.28125 C 13.046577 9.1148819 11.916283 8.5 10.59375 8.5 C 12.205095 8.4489386 13.5 7.1236737 13.5 5.5 C 13.5 3.8440001 12.156 2.5 10.5 2.5 z "
- style="opacity:1;fill:url(#linearGradient3153);fill-opacity:1.0;stroke:#ffffff;stroke-width:1.00000012;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- id="path2174"
- d="M 17,2.5 C 15.613813,2.5 14.5,3.6138135 14.5,5 C 14.5,6.3861867 15.613813,7.5 17,7.5 C 18.386187,7.5 19.499999,6.3861873 19.5,5 C 19.5,3.6138137 18.386185,2.4999999 17,2.5 z M 10.5,3.5 C 9.3898132,3.5 8.5000001,4.3898131 8.5,5.5 C 8.5,6.5997176 9.3733703,7.4835976 10.46875,7.5 C 10.499983,7.4985473 10.531267,7.4985473 10.5625,7.5 C 10.572807,7.4996734 10.583484,7.5004795 10.59375,7.5 C 11.655314,7.4504166 12.5,6.5769144 12.5,5.5 C 12.5,4.3898135 11.610187,3.5 10.5,3.5 z M 5.5,4.5 C 4.9418133,4.5 4.4999999,4.9418134 4.5,5.5 C 4.5,6.0471523 4.9293308,6.4832594 5.46875,6.5 C 5.4999831,6.4985473 5.5312669,6.4985473 5.5625,6.5 C 5.572916,6.4998387 5.583334,6.4998387 5.59375,6.5 C 6.0934659,6.4534009 6.5,6.0259981 6.5,5.5 C 6.5,4.9418133 6.0581867,4.5 5.5,4.5 z M 5.53125,8.5 C 4.98621,8.5848564 4.5000003,9.0959604 4.5,9.875 L 4.5,13.53125 L 7.5625,13.53125 C 8.1127746,13.536073 8.557677,13.980975 8.5625,14.53125 L 8.5625,17.53125 L 12.5,17.53125 C 13.050275,17.536073 13.495177,17.980975 13.5,18.53125 L 13.5,21.5 L 20.5,21.5 C 20.499928,18.739558 20.5,15.971417 20.5,13.21875 C 20.5,11.172647 18.881301,9.53125 16.875,9.53125 C 16.053394,9.5312502 15.035346,10.231141 14.28125,11 C 14.045303,11.244024 13.70246,11.353041 13.368907,11.290106 C 13.035354,11.227172 12.755806,11.000722 12.625,10.6875 C 12.280526,9.9087333 11.615906,9.5089368 10.625,9.5 C 10.614584,9.5001613 10.604166,9.5001613 10.59375,9.5 C 10.583334,9.5001613 10.572916,9.5001613 10.5625,9.5 C 10.033117,9.5252804 9.1973144,10.219569 8.71875,10.96875 C 8.4774641,11.327111 8.0347855,11.49207 7.6178253,11.378996 C 7.2008651,11.265922 6.9021772,10.899915 6.875,10.46875 C 6.8561284,9.7322475 6.6929787,9.2126452 6.5,8.9375 C 6.312535,8.6702161 6.1126513,8.5308317 5.65625,8.5 C 5.645834,8.5001613 5.635416,8.5001613 5.625,8.5 C 5.5937669,8.5014527 5.5624831,8.5014527 5.53125,8.5 L 5.53125,8.5 z " />
- </g>
-</svg>
diff --git a/pidgin/pixmaps/protocols/48/google-talk.png b/pidgin/pixmaps/protocols/48/google-talk.png
new file mode 100644
index 0000000000..c42c97a41a
--- /dev/null
+++ b/pidgin/pixmaps/protocols/48/google-talk.png
Binary files differ
diff --git a/pidgin/pixmaps/protocols/48/myspace.png b/pidgin/pixmaps/protocols/48/myspace.png
deleted file mode 100644
index d9740ef5ff..0000000000
--- a/pidgin/pixmaps/protocols/48/myspace.png
+++ /dev/null
Binary files differ
diff --git a/pidgin/pixmaps/toolbar/16/insert-screenshot.png b/pidgin/pixmaps/toolbar/16/insert-screenshot.png
new file mode 100644
index 0000000000..f84bbb9a53
--- /dev/null
+++ b/pidgin/pixmaps/toolbar/16/insert-screenshot.png
Binary files differ
diff --git a/pidgin/plugins/Makefile.am b/pidgin/plugins/Makefile.am
index 3bb8c22402..e3f6b87f87 100644
--- a/pidgin/plugins/Makefile.am
+++ b/pidgin/plugins/Makefile.am
@@ -1,4 +1,4 @@
-DIST_SUBDIRS = cap disco gestures gevolution musicmessaging perl ticker
+DIST_SUBDIRS = cap disco gestures gevolution musicmessaging perl ticker win32/winprefs win32/transparency
if BUILD_GEVOLUTION
GEVOLUTION_DIR = gevolution
@@ -29,51 +29,50 @@ SUBDIRS = \
disco \
ticker
-plugindir = $(libdir)/pidgin
-
-convcolors_la_LDFLAGS = -module -avoid-version
-contact_priority_la_LDFLAGS = -module -avoid-version
-extplacement_la_LDFLAGS = -module -avoid-version
-gtk_signals_test_la_LDFLAGS = -module -avoid-version
-gtkbuddynote_la_LDFLAGS = -module -avoid-version
-history_la_LDFLAGS = -module -avoid-version
-iconaway_la_LDFLAGS = -module -avoid-version
-markerline_la_LDFLAGS = -module -avoid-version
-notify_la_LDFLAGS = -module -avoid-version
-pidginrc_la_LDFLAGS = -module -avoid-version
-relnot_la_LDFLAGS = -module -avoid-version
-sendbutton_la_LDFLAGS = -module -avoid-version
-spellchk_la_LDFLAGS = -module -avoid-version
-themeedit_la_LDFLAGS = -module -avoid-version
-timestamp_la_LDFLAGS = -module -avoid-version
-timestamp_format_la_LDFLAGS = -module -avoid-version
-unity_la_LDFLAGS = -module -avoid-version
-vvconfig_la_LDFLAGS = -module -avoid-version
-xmppconsole_la_LDFLAGS = -module -avoid-version
+if IS_WIN32
+SUBDIRS += \
+ win32/winprefs \
+ win32/transparency
+endif
+
+plugindir = @PIDGIN_PLUGINDIR@
+
+contact_priority_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+extplacement_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+gtk_signals_test_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+gtkbuddynote_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+history_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+iconaway_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+imgupload_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+markerline_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+notify_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+relnot_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+screencap_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+sendbutton_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+spellchk_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+themeedit_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+unity_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+webkit_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+xmppconsole_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
if PLUGINS
plugin_LTLIBRARIES = \
- convcolors.la \
extplacement.la \
gtkbuddynote.la \
history.la \
iconaway.la \
+ imgupload.la \
markerline.la \
notify.la \
- pidginrc.la \
relnot.la \
+ screencap.la \
sendbutton.la \
spellchk.la \
- themeedit.la \
- timestamp.la \
- timestamp_format.la \
+ themeedit.la \
+ webkit.la \
xmppconsole.la
-if USE_VV
-plugin_LTLIBRARIES += vvconfig.la
-endif
-
if ENABLE_UNITY
plugin_LTLIBRARIES += unity.la
endif
@@ -82,45 +81,43 @@ noinst_LTLIBRARIES = \
contact_priority.la \
gtk_signals_test.la
-convcolors_la_SOURCES = convcolors.c
contact_priority_la_SOURCES = contact_priority.c
extplacement_la_SOURCES = extplacement.c
gtk_signals_test_la_SOURCES = gtk-signals-test.c
gtkbuddynote_la_SOURCES = gtkbuddynote.c
history_la_SOURCES = history.c
iconaway_la_SOURCES = iconaway.c
+imgupload_la_SOURCES = imgupload.c
markerline_la_SOURCES = markerline.c
notify_la_SOURCES = notify.c
-pidginrc_la_SOURCES = pidginrc.c
relnot_la_SOURCES = relnot.c
+screencap_la_SOURCES = screencap.c
sendbutton_la_SOURCES = sendbutton.c
spellchk_la_SOURCES = spellchk.c
themeedit_la_SOURCES = themeedit.c themeedit-icon.c themeedit-icon.h
-timestamp_la_SOURCES = timestamp.c
-timestamp_format_la_SOURCES = timestamp_format.c
unity_la_SOURCES = unity.c
-vvconfig_la_SOURCES = vvconfig.c
+webkit_la_SOURCES = webkit.c
xmppconsole_la_SOURCES = xmppconsole.c
-convcolors_la_LIBADD = $(GTK_LIBS)
-contact_priority_la_LIBADD = $(GTK_LIBS)
-extplacement_la_LIBADD = $(GTK_LIBS)
-gtk_signals_test_la_LIBADD = $(GTK_LIBS)
-gtkbuddynote_la_LIBADD = $(GTK_LIBS)
-history_la_LIBADD = $(GTK_LIBS)
-iconaway_la_LIBADD = $(GTK_LIBS)
-markerline_la_LIBADD = $(GTK_LIBS)
-notify_la_LIBADD = $(GTK_LIBS)
-pidginrc_la_LIBADD = $(GTK_LIBS)
-relnot_la_LIBADD = $(GLIB_LIBS)
-sendbutton_la_LIBADD = $(GTK_LIBS)
-spellchk_la_LIBADD = $(GTK_LIBS)
-themeedit_la_LIBADD = $(GTK_LIBS)
-timestamp_la_LIBADD = $(GTK_LIBS)
-timestamp_format_la_LIBADD = $(GTK_LIBS)
-unity_la_LIBADD = $(GTK_LIBS) $(UNITY_LIBS)
-vvconfig_la_LIBADD = $(GTK_LIBS) $(GSTREAMER_LIBS)
-xmppconsole_la_LIBADD = $(GTK_LIBS)
+contact_priority_la_LIBADD = @PIDGIN_LIBS@
+extplacement_la_LIBADD = @PIDGIN_LIBS@
+gtk_signals_test_la_LIBADD = @PIDGIN_LIBS@
+gtkbuddynote_la_LIBADD = @PIDGIN_LIBS@
+history_la_LIBADD = @PIDGIN_LIBS@
+iconaway_la_LIBADD = @PIDGIN_LIBS@
+imgupload_la_LIBADD = @PIDGIN_LIBS@ $(JSON_LIBS)
+markerline_la_LIBADD = @PIDGIN_LIBS@ $(WEBKIT_LIBS)
+notify_la_LIBADD = @PIDGIN_LIBS@
+relnot_la_LIBADD = @PIDGIN_LIBS@
+screencap_la_LIBADD = @PIDGIN_LIBS@
+sendbutton_la_LIBADD = @PIDGIN_LIBS@
+spellchk_la_LIBADD = @PIDGIN_LIBS@
+themeedit_la_LIBADD = @PIDGIN_LIBS@
+unity_la_LIBADD = @PIDGIN_LIBS@ $(UNITY_LIBS)
+webkit_la_LIBADD = @PIDGIN_LIBS@ $(WEBKIT_LIBS)
+xmppconsole_la_LIBADD = @PIDGIN_LIBS@
+
+imgupload_la_CFLAGS = $(JSON_CFLAGS)
endif # PLUGINS
@@ -137,13 +134,13 @@ EXTRA_DIST = \
win32/winprefs/winprefs.c
AM_CPPFLAGS = \
- -DDATADIR=\"$(datadir)\" \
-I$(top_builddir)/libpurple \
-I$(top_srcdir)/libpurple \
-I$(top_srcdir)/pidgin \
$(DEBUG_CFLAGS) \
$(GTK_CFLAGS) \
$(UNITY_CFLAGS) \
+ $(WEBKIT_CFLAGS) \
$(GSTREAMER_CFLAGS) \
$(PLUGIN_CFLAGS)
@@ -154,7 +151,7 @@ AM_CPPFLAGS = \
SUFFIXES = .c .so
.c.so:
$(LIBTOOL) --mode=compile $(CC) -DHAVE_CONFIG_H -I$(top_builddir) $(AM_CPPFLAGS) $(CFLAGS) -c $< -o tmp$@.lo $(PLUGIN_CFLAGS)
- $(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o libtmp$@.la -rpath $(plugindir) tmp$@.lo $(LIBS) $(LDFLAGS) -module -avoid-version $(PLUGIN_LIBS)
+ $(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o libtmp$@.la -rpath $(plugindir) tmp$@.lo $(LIBS) $(LDFLAGS) -module @PLUGIN_LDFLAGS@ $(PLUGIN_LIBS)
@rm -f tmp$@.lo tmp$@.o libtmp$@.la
@cp .libs/libtmp$@*.so $@
@rm -rf .libs/libtmp$@.*
diff --git a/pidgin/plugins/Makefile.mingw b/pidgin/plugins/Makefile.mingw
index f16f9f506d..22da6e3327 100644
--- a/pidgin/plugins/Makefile.mingw
+++ b/pidgin/plugins/Makefile.mingw
@@ -26,8 +26,11 @@ INCLUDE_PATHS += \
-I$(GTK_TOP)/include/pango-1.0 \
-I$(GTK_TOP)/include/atk-1.0 \
-I$(GTK_TOP)/include/cairo \
+ -I$(GTK_TOP)/include/gdk-pixbuf-2.0 \
-I$(GTK_TOP)/lib/glib-2.0/include \
-I$(GTK_TOP)/lib/gtk-2.0/include \
+ -I$(LIBSOUP_TOP)/include/libsoup-2.4 \
+ -I$(WEBKITGTK_TOP)/include/webkitgtk-1.0 \
-I$(PIDGIN_TREE_TOP) \
-I$(PURPLE_TOP) \
-I$(PURPLE_TOP)/win32 \
@@ -35,6 +38,7 @@ INCLUDE_PATHS += \
-I$(PIDGIN_TOP)/win32
LIB_PATHS += -L$(GTK_TOP)/lib \
+ -L$(WEBKITGTK_TOP)/lib \
-L$(PURPLE_TOP) \
-L$(PIDGIN_TOP)
@@ -51,6 +55,7 @@ LIBS = -lgtk-win32-2.0 \
-lcairo \
-lintl \
-lws2_32 \
+ -lwebkitgtk-1.0 \
-lpurple \
-lpidgin
@@ -60,18 +65,18 @@ LIBS = -lgtk-win32-2.0 \
.PHONY: all clean plugins install
all: plugins
- $(MAKE) -C $(DISCO_PLUGIN) -f $(MINGW_MAKEFILE)
- $(MAKE) -C $(GTKPERL_PLUGIN) -f $(MINGW_MAKEFILE)
- $(MAKE) -C $(TICKER_PLUGIN) -f $(MINGW_MAKEFILE)
- $(MAKE) -C $(TRANSPARENCY_PLUGIN) -f $(MINGW_MAKEFILE)
- $(MAKE) -C $(WINPREFS_PLUGIN) -f $(MINGW_MAKEFILE)
+ $(MAKE_at) $(MAKE) -C $(DISCO_PLUGIN) -f $(MINGW_MAKEFILE)
+ $(MAKE_at) $(MAKE) -C $(GTKPERL_PLUGIN) -f $(MINGW_MAKEFILE)
+ $(MAKE_at) $(MAKE) -C $(TICKER_PLUGIN) -f $(MINGW_MAKEFILE)
+ $(MAKE_at) $(MAKE) -C $(TRANSPARENCY_PLUGIN) -f $(MINGW_MAKEFILE)
+ $(MAKE_at) $(MAKE) -C $(WINPREFS_PLUGIN) -f $(MINGW_MAKEFILE)
install: all $(PIDGIN_INSTALL_PLUGINS_DIR)
- $(MAKE) -C $(DISCO_PLUGIN) -f $(MINGW_MAKEFILE) install
- $(MAKE) -C $(GTKPERL_PLUGIN) -f $(MINGW_MAKEFILE) install
- $(MAKE) -C $(TICKER_PLUGIN) -f $(MINGW_MAKEFILE) install
- $(MAKE) -C $(TRANSPARENCY_PLUGIN) -f $(MINGW_MAKEFILE) install
- $(MAKE) -C $(WINPREFS_PLUGIN) -f $(MINGW_MAKEFILE) install
+ $(MAKE_at) $(MAKE) -C $(DISCO_PLUGIN) -f $(MINGW_MAKEFILE) install
+ $(MAKE_at) $(MAKE) -C $(GTKPERL_PLUGIN) -f $(MINGW_MAKEFILE) install
+ $(MAKE_at) $(MAKE) -C $(TICKER_PLUGIN) -f $(MINGW_MAKEFILE) install
+ $(MAKE_at) $(MAKE) -C $(TRANSPARENCY_PLUGIN) -f $(MINGW_MAKEFILE) install
+ $(MAKE_at) $(MAKE) -C $(WINPREFS_PLUGIN) -f $(MINGW_MAKEFILE) install
cp *.dll $(PIDGIN_INSTALL_PLUGINS_DIR)
THEMEEDIT_SRC = themeedit.c themeedit-icon.c
@@ -88,20 +93,18 @@ themeedit.dll: $(THEMEEDIT_OBJECTS)
include $(PIDGIN_COMMON_RULES)
plugins: \
- convcolors.dll \
+ contact_priority.dll \
extplacement.dll \
gtkbuddynote.dll \
history.dll \
iconaway.dll \
markerline.dll \
notify.dll \
- pidginrc.dll \
relnot.dll \
sendbutton.dll \
spellchk.dll \
themeedit.dll \
- timestamp_format.dll \
- timestamp.dll \
+ webkit.dll \
xmppconsole.dll
##
@@ -109,10 +112,10 @@ plugins: \
##
clean:
rm -f *.o *.dll
- $(MAKE) -C $(DISCO_PLUGIN) -f $(MINGW_MAKEFILE) clean
- $(MAKE) -C $(GTKPERL_PLUGIN) -f $(MINGW_MAKEFILE) clean
- $(MAKE) -C $(TICKER_PLUGIN) -f $(MINGW_MAKEFILE) clean
- $(MAKE) -C $(TRANSPARENCY_PLUGIN) -f $(MINGW_MAKEFILE) clean
- $(MAKE) -C $(WINPREFS_PLUGIN) -f $(MINGW_MAKEFILE) clean
+ $(MAKE_at) $(MAKE) -C $(DISCO_PLUGIN) -f $(MINGW_MAKEFILE) clean
+ $(MAKE_at) $(MAKE) -C $(GTKPERL_PLUGIN) -f $(MINGW_MAKEFILE) clean
+ $(MAKE_at) $(MAKE) -C $(TICKER_PLUGIN) -f $(MINGW_MAKEFILE) clean
+ $(MAKE_at) $(MAKE) -C $(TRANSPARENCY_PLUGIN) -f $(MINGW_MAKEFILE) clean
+ $(MAKE_at) $(MAKE) -C $(WINPREFS_PLUGIN) -f $(MINGW_MAKEFILE) clean
include $(PIDGIN_COMMON_TARGETS)
diff --git a/pidgin/plugins/cap/Makefile.am b/pidgin/plugins/cap/Makefile.am
index 67aaefb8b7..2aac838ed9 100644
--- a/pidgin/plugins/cap/Makefile.am
+++ b/pidgin/plugins/cap/Makefile.am
@@ -1,6 +1,6 @@
-capdir = $(libdir)/pidgin
+capdir = @PIDGIN_PLUGINDIR@
-cap_la_LDFLAGS = -module -avoid-version
+cap_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
if PLUGINS
@@ -15,10 +15,9 @@ cap_la_SOURCES = \
endif
-cap_la_LIBADD = $(GTK_LIBS) $(SQLITE3_LIBS)
+cap_la_LIBADD = @PIDGIN_LIBS@ $(SQLITE3_LIBS)
AM_CPPFLAGS = \
- -DDATADIR=\"$(datadir)\" \
-I$(top_srcdir)/libpurple \
-I$(top_builddir)/libpurple \
-I$(top_srcdir)/pidgin \
diff --git a/pidgin/plugins/cap/cap.c b/pidgin/plugins/cap/cap.c
index d5faffa23b..6335c1be30 100644
--- a/pidgin/plugins/cap/cap.c
+++ b/pidgin/plugins/cap/cap.c
@@ -33,9 +33,10 @@ static void generate_prediction(CapStatistics *statistics) {
static double generate_prediction_for(PurpleBuddy *buddy) {
double prediction = 1.0f;
gboolean generated = FALSE;
- gchar *buddy_name = buddy->name;
- const gchar *protocol_id = purple_account_get_protocol_id(buddy->account);
- const gchar *account_id = purple_account_get_username(buddy->account);
+ PurpleAccount *account = purple_buddy_get_account(buddy);
+ const gchar *buddy_name = purple_buddy_get_name(buddy);
+ const gchar *protocol_id = purple_account_get_protocol_id(account);
+ const gchar *account_id = purple_account_get_username(account);
const gchar *status_id = purple_status_get_id(get_status_for(buddy));
time_t t = time(NULL);
struct tm *current_time = localtime(&t);
@@ -43,7 +44,7 @@ static double generate_prediction_for(PurpleBuddy *buddy) {
int threshold = purple_prefs_get_int("/plugins/gtk/cap/threshold");
int min_minute = (current_minute - threshold) % 1440;
int max_minute = (current_minute + threshold) % 1440;
- char *sql, *sta_id = NULL;
+ gchar *sql;
sqlite3_stmt *stmt = NULL;
const char *tail = NULL;
int rc;
@@ -93,15 +94,6 @@ static double generate_prediction_for(PurpleBuddy *buddy) {
}
sqlite3_free(sql);
-
- sta_id = purple_status_get_id(get_status_for(buddy));
-
- if(sta_id && !strcmp(sta_id, "offline")) {
- /* This is kind of stupid, change it. */
- if(prediction == 1.0f)
- prediction = 0.0f;
- }
-
if(generated)
return prediction;
else
@@ -113,7 +105,7 @@ static CapStatistics * get_stats_for(PurpleBuddy *buddy) {
g_return_val_if_fail(buddy != NULL, NULL);
- stats = g_hash_table_lookup(_buddy_stats, buddy->name);
+ stats = g_hash_table_lookup(_buddy_stats, purple_buddy_get_name(buddy));
if(!stats) {
stats = g_malloc0(sizeof(CapStatistics));
stats->last_message = -1;
@@ -121,7 +113,8 @@ static CapStatistics * get_stats_for(PurpleBuddy *buddy) {
stats->last_seen = -1;
stats->last_status_id = "";
- g_hash_table_insert(_buddy_stats, g_strdup(buddy->name), stats);
+ g_hash_table_insert(_buddy_stats,
+ g_strdup(purple_buddy_get_name(buddy)), stats);
} else {
/* This may actually be a different PurpleBuddy than what is in stats.
* We replace stats->buddy to make sure we're looking at a valid pointer. */
@@ -290,9 +283,10 @@ insert_cap_status_count_failed(const char *buddy_name, const char *account, cons
}
static void insert_cap_success(CapStatistics *stats) {
- gchar *buddy_name = stats->buddy->name;
- const gchar *protocol_id = purple_account_get_protocol_id(stats->buddy->account);
- const gchar *account_id = purple_account_get_username(stats->buddy->account);
+ PurpleAccount *account = purple_buddy_get_account(stats->buddy);
+ const gchar *buddy_name = purple_buddy_get_name(stats->buddy);
+ const gchar *protocol_id = purple_account_get_protocol_id(account);
+ const gchar *account_id = purple_account_get_username(account);
const gchar *status_id = (stats->last_message_status_id) ?
stats->last_message_status_id :
purple_status_get_id(get_status_for(stats->buddy));
@@ -316,9 +310,10 @@ static void insert_cap_success(CapStatistics *stats) {
}
static void insert_cap_failure(CapStatistics *stats) {
- gchar *buddy_name = stats->buddy->name;
- const gchar *protocol_id = purple_account_get_protocol_id(stats->buddy->account);
- const gchar *account_id = purple_account_get_username(stats->buddy->account);
+ PurpleAccount *account = purple_buddy_get_account(stats->buddy);
+ const gchar *buddy_name = purple_buddy_get_name(stats->buddy);
+ const gchar *protocol_id = purple_account_get_protocol_id(account);
+ const gchar *account_id = purple_account_get_username(account);
const gchar *status_id = (stats->last_message_status_id) ?
stats->last_message_status_id :
purple_status_get_id(get_status_for(stats->buddy));
@@ -344,22 +339,23 @@ static gboolean max_message_difference_cb(gpointer data) {
/* Purple Signal Handlers */
/* sent-im-msg */
-static void sent_im_msg(PurpleAccount *account, const char *receiver, const char *message) {
+static void sent_im_msg(PurpleAccount *account, PurpleMessage *msg, gpointer _unused)
+{
PurpleBuddy *buddy;
guint interval, words;
CapStatistics *stats = NULL;
- buddy = purple_find_buddy(account, receiver);
+ buddy = purple_blist_find_buddy(account, purple_message_get_who(msg));
if (buddy == NULL)
return;
interval = purple_prefs_get_int("/plugins/gtk/cap/max_msg_difference") * 60;
- words = word_count(message);
+ words = word_count(purple_message_get_contents(msg));
stats = get_stats_for(buddy);
- insert_word_count(purple_account_get_username(account), receiver, words);
+ insert_word_count(purple_account_get_username(account), purple_message_get_who(msg), words);
stats->last_message = time(NULL);
stats->last_message_status_id = purple_status_get_id(get_status_for(buddy));
if(stats->timeout_source_id != 0)
@@ -378,7 +374,7 @@ received_im_msg(PurpleAccount *account, char *sender, char *message, PurpleConve
if (flags & PURPLE_MESSAGE_AUTO_RESP)
return;
- buddy = purple_find_buddy(account, sender);
+ buddy = purple_blist_find_buddy(account, sender);
if (buddy == NULL)
return;
@@ -438,8 +434,8 @@ static void buddy_signed_off(PurpleBuddy *buddy) {
/* drawing-tooltip */
static void drawing_tooltip(PurpleBlistNode *node, GString *text, gboolean full) {
- if(node->type == PURPLE_BLIST_BUDDY_NODE) {
- PurpleBuddy *buddy = (PurpleBuddy *)node;
+ if (PURPLE_IS_BUDDY(node)) {
+ PurpleBuddy *buddy = PURPLE_BUDDY(node);
CapStatistics *stats = get_stats_for(buddy);
/* get the probability that this buddy will respond and add to the tooltip */
if(stats->prediction->probability >= 0.0) {
@@ -509,8 +505,7 @@ static PurpleStatus * get_status_for(PurpleBuddy *buddy) {
}
static void create_tables() {
- int rc;
- rc = sqlite3_exec(_db,
+ sqlite3_exec(_db,
"CREATE TABLE IF NOT EXISTS cap_status ("
" buddy varchar(60) not null,"
" account varchar(60) not null,"
@@ -521,7 +516,7 @@ static void create_tables() {
");",
NULL, NULL, NULL);
- rc = sqlite3_exec(_db,
+ sqlite3_exec(_db,
"create table if not exists cap_message ("
" sender varchar(60) not null,"
" receiver varchar(60) not null,"
@@ -533,7 +528,7 @@ static void create_tables() {
");",
NULL, NULL, NULL);
- rc = sqlite3_exec(_db,
+ sqlite3_exec(_db,
"create table if not exists cap_msg_count ("
" buddy varchar(60) not null,"
" account varchar(60) not null,"
@@ -545,7 +540,7 @@ static void create_tables() {
");",
NULL, NULL, NULL);
- rc = sqlite3_exec(_db,
+ sqlite3_exec(_db,
"create table if not exists cap_status_count ("
" buddy varchar(60) not null,"
" account varchar(60) not null,"
@@ -557,7 +552,7 @@ static void create_tables() {
");",
NULL, NULL, NULL);
- rc = sqlite3_exec(_db,
+ sqlite3_exec(_db,
"create table if not exists cap_my_usage ("
" account varchar(60) not null,"
" protocol varchar(60) not null,"
@@ -611,8 +606,8 @@ static void insert_status_change(CapStatistics *statistics) {
}
static void insert_status_change_from_purple_status(CapStatistics *statistics, PurpleStatus *status) {
+ PurpleAccount *account = purple_buddy_get_account(statistics->buddy);
char *sql;
- int rc;
const gchar *status_id;
const gchar *buddy_name;
const gchar *protocol_id;
@@ -625,16 +620,16 @@ static void insert_status_change_from_purple_status(CapStatistics *statistics, P
return;
status_id = purple_status_get_id(status);
- buddy_name = statistics->buddy->name;
- protocol_id = purple_account_get_protocol_id(statistics->buddy->account);
- account_id = purple_account_get_username(statistics->buddy->account);
+ buddy_name = purple_buddy_get_name(statistics->buddy);
+ protocol_id = purple_account_get_protocol_id(account);
+ account_id = purple_account_get_username(account);
statistics->last_status_id = purple_status_get_id(status);
purple_debug_info("cap", "Executing: insert into cap_status (buddy, account, protocol, status, event_time) values(%s, %s, %s, %s, now());\n", buddy_name, account_id, protocol_id, status_id);
sql = sqlite3_mprintf("insert into cap_status values (%Q, %Q, %Q, %Q, now());", buddy_name, account_id, protocol_id, status_id);
- rc = sqlite3_exec(_db, sql, NULL, NULL, NULL);
+ sqlite3_exec(_db, sql, NULL, NULL, NULL);
sqlite3_free(sql);
}
@@ -868,7 +863,7 @@ static CapPrefsUI * create_cap_prefs_ui() {
return ui;
}
-static void cap_prefs_ui_destroy_cb(GtkObject *object, gpointer user_data) {
+static void cap_prefs_ui_destroy_cb(GObject *object, gpointer user_data) {
CapPrefsUI *ui = user_data;
if(_db) {
add_plugin_functionality(_plugin_pointer);
@@ -882,8 +877,7 @@ static void numeric_spinner_prefs_cb(GtkSpinButton *spinbutton, gpointer user_da
static PidginPluginUiInfo ui_info = {
get_config_frame,
- 0 /* page_num (reserved) */,
- NULL,NULL,NULL,NULL
+ NULL, NULL, NULL, NULL
};
static PurplePluginInfo info = {
diff --git a/pidgin/plugins/cap/cap.h b/pidgin/plugins/cap/cap.h
index 3832b3f901..2d5e9fdca5 100644
--- a/pidgin/plugins/cap/cap.h
+++ b/pidgin/plugins/cap/cap.h
@@ -32,7 +32,7 @@
#include "gtkplugin.h"
#include "gtkutils.h"
-#include "blist.h"
+#include "buddylist.h"
#include "notify.h"
#include "version.h"
#include "debug.h"
@@ -89,7 +89,7 @@ static void insert_cap_failure(CapStatistics *stats);
static gboolean max_message_difference_cb(gpointer data);
/* Pidgin Signal Handlers */
/* sent-im-msg */
-static void sent_im_msg(PurpleAccount *account, const char *receiver, const char *message);
+static void sent_im_msg(PurpleAccount *account, PurpleMessage *msg, gpointer _unused);
/* received-im-msg */
static void received_im_msg(PurpleAccount *account, char *sender, char *message, PurpleConversation *conv, PurpleMessageFlags flags);
/* buddy-status-changed */
@@ -120,7 +120,7 @@ static void remove_plugin_functionality(PurplePlugin *plugin);
static void write_stats_on_unload(gpointer key, gpointer value, gpointer user_data);
static gboolean plugin_unload(PurplePlugin *plugin);
static CapPrefsUI * create_cap_prefs_ui(void);
-static void cap_prefs_ui_destroy_cb(GtkObject *object, gpointer user_data);
+static void cap_prefs_ui_destroy_cb(GObject *object, gpointer user_data);
static void numeric_spinner_prefs_cb(GtkSpinButton *spinbutton, gpointer user_data);
static GtkWidget * get_config_frame(PurplePlugin *plugin);
static void init_plugin(PurplePlugin *plugin);
diff --git a/pidgin/plugins/cap/cap_statistics.h b/pidgin/plugins/cap/cap_statistics.h
index f52495efdc..85a11da3d5 100644
--- a/pidgin/plugins/cap/cap_statistics.h
+++ b/pidgin/plugins/cap/cap_statistics.h
@@ -22,7 +22,7 @@
#ifndef _CAP_STATISTICS_H_
#define _CAP_STATISTICS_H_
-#include "blist.h"
+#include "buddylist.h"
#include <gdk/gdk.h>
#include <glib.h>
#include <time.h>
diff --git a/pidgin/plugins/contact_priority.c b/pidgin/plugins/contact_priority.c
index e59566b481..4840e2e808 100644
--- a/pidgin/plugins/contact_priority.c
+++ b/pidgin/plugins/contact_priority.c
@@ -20,6 +20,7 @@
#include "internal.h"
#include "pidgin.h"
+#include "gtk3compat.h"
#include "gtkplugin.h"
#include "gtkutils.h"
#include "prefs.h"
@@ -35,20 +36,20 @@ select_account(GtkWidget *widget, PurpleAccount *account, gpointer data)
}
static void
-account_update(GtkWidget *widget, GtkOptionMenu *optmenu)
+account_update(GtkWidget *widget, GtkWidget *optmenu)
{
PurpleAccount *account = NULL;
- account = g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(optmenu)))), "account");
+ account = pidgin_account_option_menu_get_selected(optmenu);
purple_account_set_int(account, "score", gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)));
}
static void
pref_update(GtkWidget *widget, char *pref)
{
- if (purple_prefs_get_type(pref) == PURPLE_PREF_INT)
+ if (purple_prefs_get_pref_type(pref) == PURPLE_PREF_INT)
purple_prefs_set_int(pref, gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)));
- if (purple_prefs_get_type(pref) == PURPLE_PREF_BOOLEAN)
+ if (purple_prefs_get_pref_type(pref) == PURPLE_PREF_BOOLEAN)
purple_prefs_set_bool(pref, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
}
@@ -75,7 +76,7 @@ get_config_frame(PurplePlugin *plugin)
GtkWidget *ret = NULL, *hbox = NULL, *frame = NULL, *vbox = NULL;
GtkWidget *label = NULL, *spin = NULL, *check = NULL;
GtkWidget *optmenu = NULL;
- GtkObject *adj = NULL;
+ GtkAdjustment *adj = NULL;
GtkSizeGroup *sg = NULL;
PurpleAccount *account = NULL;
int i;
@@ -84,12 +85,12 @@ get_config_frame(PurplePlugin *plugin)
sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
- ret = gtk_vbox_new(FALSE, 18);
+ ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
gtk_container_set_border_width(GTK_CONTAINER(ret), 12);
frame = pidgin_make_frame(ret, _("Point values to use when..."));
- vbox = gtk_vbox_new(FALSE, 5);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_container_add(GTK_CONTAINER(frame), vbox);
/* Status Spinboxes */
@@ -97,7 +98,7 @@ get_config_frame(PurplePlugin *plugin)
{
char *pref = g_strconcat("/purple/status/scores/", statuses[i].id, NULL);
- hbox = gtk_hbox_new(FALSE, 5);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
label = gtk_label_new_with_mnemonic(_(statuses[i].description));
@@ -105,8 +106,9 @@ get_config_frame(PurplePlugin *plugin)
gtk_size_group_add_widget(sg, label);
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
- adj = gtk_adjustment_new(purple_prefs_get_int(pref), -500, 500, 1, 1, 1);
- spin = gtk_spin_button_new((GtkAdjustment *)adj, 1, 0);
+ adj = GTK_ADJUSTMENT(gtk_adjustment_new(purple_prefs_get_int(pref),
+ -500, 500, 1, 1, 1));
+ spin = gtk_spin_button_new(adj, 1, 0);
g_signal_connect(G_OBJECT(spin), "value-changed", G_CALLBACK(pref_update), pref);
gtk_box_pack_start(GTK_BOX(hbox), spin, FALSE, FALSE, 0);
@@ -119,7 +121,7 @@ get_config_frame(PurplePlugin *plugin)
gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
/* Last match */
- hbox = gtk_hbox_new(FALSE, 5);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
check = gtk_check_button_new_with_label(_("Use last buddy when scores are equal"));
@@ -129,17 +131,17 @@ get_config_frame(PurplePlugin *plugin)
frame = pidgin_make_frame(ret, _("Point values to use for account..."));
- vbox = gtk_vbox_new(FALSE, 5);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_container_add(GTK_CONTAINER(frame), vbox);
/* Account */
- hbox = gtk_hbox_new(FALSE, 5);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
/* make this here so I can use it in the option menu callback, we'll
* actually set it up later */
- adj = gtk_adjustment_new(0, -500, 500, 1, 1, 1);
- spin = gtk_spin_button_new((GtkAdjustment *)adj, 1, 0);
+ 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),
@@ -147,8 +149,7 @@ get_config_frame(PurplePlugin *plugin)
gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0);
/* this is where we set up the spin button we made above */
- account = g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(GTK_OPTION_MENU(optmenu))))),
- "account");
+ account = pidgin_account_option_menu_get_selected(optmenu);
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));
@@ -165,7 +166,6 @@ get_config_frame(PurplePlugin *plugin)
static PidginPluginUiInfo ui_info =
{
get_config_frame,
- 0, /* page_num (Reserved) */
/* Padding */
NULL,
NULL,
diff --git a/pidgin/plugins/convcolors.c b/pidgin/plugins/convcolors.c
deleted file mode 100644
index 7e85d2683f..0000000000
--- a/pidgin/plugins/convcolors.c
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * Conversation Colors
- * 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-convcolors"
-#define PLUGIN_NAME N_("Conversation Colors")
-#define PLUGIN_STATIC_NAME ConversationColors
-#define PLUGIN_SUMMARY N_("Customize colors in the conversation window")
-#define PLUGIN_DESCRIPTION N_("Customize colors in the conversation window")
-#define PLUGIN_AUTHOR "Sadrul H Chowdhury <sadrul@users.sourceforge.net>"
-
-/* System headers */
-#include <gdk/gdk.h>
-#include <glib.h>
-#include <gtk/gtk.h>
-
-/* Purple headers */
-#include <gtkplugin.h>
-#include <version.h>
-
-#include <conversation.h>
-#include <gtkconv.h>
-#include <gtkprefs.h>
-#include <gtkutils.h>
-
-#define PREF_PREFIX "/plugins/gtk/" PLUGIN_ID
-#define PREF_IGNORE PREF_PREFIX "/ignore_incoming"
-#define PREF_CHATS PREF_PREFIX "/chats"
-#define PREF_IMS PREF_PREFIX "/ims"
-
-#define PREF_SEND PREF_PREFIX "/send"
-#define PREF_SEND_C PREF_SEND "/color"
-#define PREF_SEND_F PREF_SEND "/format"
-#define PREF_SEND_E PREF_SEND "/enabled"
-
-#define PREF_RECV PREF_PREFIX "/recv"
-#define PREF_RECV_C PREF_RECV "/color"
-#define PREF_RECV_F PREF_RECV "/format"
-#define PREF_RECV_E PREF_RECV "/enabled"
-
-#define PREF_SYSTEM PREF_PREFIX "/system"
-#define PREF_SYSTEM_C PREF_SYSTEM "/color"
-#define PREF_SYSTEM_F PREF_SYSTEM "/format"
-#define PREF_SYSTEM_E PREF_SYSTEM "/enabled"
-
-#define PREF_ERROR PREF_PREFIX "/error"
-#define PREF_ERROR_C PREF_ERROR "/color"
-#define PREF_ERROR_F PREF_ERROR "/format"
-#define PREF_ERROR_E PREF_ERROR "/enabled"
-
-#define PREF_NICK PREF_PREFIX "/nick"
-#define PREF_NICK_C PREF_NICK "/color"
-#define PREF_NICK_F PREF_NICK "/format"
-#define PREF_NICK_E PREF_NICK "/enabled"
-
-enum
-{
- FONT_BOLD = 1 << 0,
- FONT_ITALIC = 1 << 1,
- FONT_UNDERLINE = 1 << 2
-};
-
-static struct
-{
- PurpleMessageFlags flag;
- char *prefix;
- const char *text;
-} formats[] =
-{
- {PURPLE_MESSAGE_ERROR, PREF_ERROR, N_("Error Messages")},
- {PURPLE_MESSAGE_NICK, PREF_NICK, N_("Highlighted Messages")},
- {PURPLE_MESSAGE_SYSTEM, PREF_SYSTEM, N_("System Messages")},
- {PURPLE_MESSAGE_SEND, PREF_SEND, N_("Sent Messages")},
- {PURPLE_MESSAGE_RECV, PREF_RECV, N_("Received Messages")},
- {0, NULL, NULL}
-};
-
-static gboolean
-displaying_msg(PurpleAccount *account, const char *who, char **displaying,
- PurpleConversation *conv, PurpleMessageFlags flags)
-{
- int i;
- char tmp[128], *t;
- gboolean bold, italic, underline;
- int f;
- const char *color;
- gboolean rtl = FALSE;
-
- for (i = 0; formats[i].prefix; i++)
- if (flags & formats[i].flag)
- break;
-
- if (!formats[i].prefix)
- return FALSE;
-
- g_snprintf(tmp, sizeof(tmp), "%s/enabled", formats[i].prefix);
-
- if (!purple_prefs_get_bool(tmp) ||
- (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM &&
- !purple_prefs_get_bool(PREF_IMS)) ||
- (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT &&
- !purple_prefs_get_bool(PREF_CHATS)))
- return FALSE;
-
- g_snprintf(tmp, sizeof(tmp), "%s/color", formats[i].prefix);
- color = purple_prefs_get_string(tmp);
-
- g_snprintf(tmp, sizeof(tmp), "%s/format", formats[i].prefix);
- f = purple_prefs_get_int(tmp);
- bold = (f & FONT_BOLD);
- italic = (f & FONT_ITALIC);
- underline = (f & FONT_UNDERLINE);
- rtl = purple_markup_is_rtl(*displaying);
-
- if (purple_prefs_get_bool(PREF_IGNORE))
- {
- /* This seems to be necessary, especially for received messages. */
- t = *displaying;
- *displaying = purple_strreplace(t, "\n", "<br>");
- g_free(t);
-
- t = *displaying;
- *displaying = purple_markup_strip_html(t);
- g_free(t);
-
- t = *displaying;
- *displaying = g_markup_escape_text(t, -1);
- g_free(t);
-
- /* Restore the links */
- t = *displaying;
- *displaying = purple_markup_linkify(t);
- g_free(t);
- }
-
- if (color && *color)
- {
- t = *displaying;
- *displaying = g_strdup_printf("<FONT COLOR=\"%s\">%s</FONT>", color, t);
- g_free(t);
- }
-
- t = *displaying;
- *displaying = g_strdup_printf("%s%s%s%s%s%s%s%s%s",
- bold ? "<B>" : "</B>",
- italic ? "<I>" : "</I>",
- underline ? "<U>" : "</U>",
- rtl ? "<SPAN style=\"direction:rtl;text-align:right;\">" : "",
- t,
- rtl ? "</SPAN>" : "",
- bold ? "</B>" : "<B>",
- italic ? "</I>" : "<I>",
- underline ? "</U>" : "<U>"
- );
- g_free(t);
-
- return FALSE;
-}
-
-static gboolean
-plugin_load(PurplePlugin *plugin)
-{
- purple_signal_connect(pidgin_conversations_get_handle(),
- "displaying-im-msg", plugin,
- PURPLE_CALLBACK(displaying_msg), NULL);
- purple_signal_connect(pidgin_conversations_get_handle(),
- "displaying-chat-msg", plugin,
- PURPLE_CALLBACK(displaying_msg), NULL);
- return TRUE;
-}
-
-static gboolean
-plugin_unload(PurplePlugin *plugin)
-{
- return TRUE;
-}
-
-/* Ripped from PurpleRC */
-static void
-color_response(GtkDialog *color_dialog, gint response, const char *data)
-{
- if (response == GTK_RESPONSE_OK)
- {
-#if GTK_CHECK_VERSION(2,14,0)
- GtkWidget *colorsel =
- gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(color_dialog));
-#else
- GtkWidget *colorsel = GTK_COLOR_SELECTION_DIALOG(color_dialog)->colorsel;
-#endif
- GdkColor color;
- char colorstr[8];
- char tmp[128];
-
- gtk_color_selection_get_current_color(GTK_COLOR_SELECTION(colorsel), &color);
-
- g_snprintf(colorstr, sizeof(colorstr), "#%02X%02X%02X",
- color.red/256, color.green/256, color.blue/256);
-
- g_snprintf(tmp, sizeof(tmp), "%s/color", data);
-
- purple_prefs_set_string(tmp, colorstr);
- }
-
- gtk_widget_destroy(GTK_WIDGET(color_dialog));
-}
-
-static void
-set_color(GtkWidget *widget, const char *data)
-{
- GtkWidget *color_dialog = NULL;
- GdkColor color;
- char title[128];
- char tmp[128];
-
- g_snprintf(title, sizeof(title), _("Select Color for %s"), _(data));
- color_dialog = gtk_color_selection_dialog_new(title);
- g_signal_connect(G_OBJECT(color_dialog), "response",
- G_CALLBACK(color_response), (gpointer)data);
-
- g_snprintf(tmp, sizeof(tmp), "%s/color", data);
- if (gdk_color_parse(purple_prefs_get_string(tmp), &color))
- {
-#if GTK_CHECK_VERSION(2,14,0)
- gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(
- gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(color_dialog))),
- &color);
-#else
- gtk_color_selection_set_current_color(
- GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(color_dialog)->colorsel),
- &color);
-#endif
- }
-
- gtk_window_present(GTK_WINDOW(color_dialog));
-}
-
-static void
-toggle_enabled(GtkWidget *widget, gpointer data)
-{
- const char *prefix = (char *)data;
- gboolean e;
- char tmp[128];
-
- g_snprintf(tmp, sizeof(tmp), "%s/enabled", prefix);
- e = purple_prefs_get_bool(tmp);
- purple_prefs_set_bool(tmp, !e);
-}
-
-static void
-toggle_something(const char *prefix, int format)
-{
- int f;
- char tmp[128];
-
- g_snprintf(tmp, sizeof(tmp), "%s/format", prefix);
- f = purple_prefs_get_int(tmp);
- f ^= format;
- purple_prefs_set_int(tmp, f);
-}
-
-static void
-toggle_bold(GtkWidget *widget, gpointer data)
-{
- toggle_something(data, FONT_BOLD);
-}
-
-static void
-toggle_italic(GtkWidget *widget, gpointer data)
-{
- toggle_something(data, FONT_ITALIC);
-}
-
-static void
-toggle_underline(GtkWidget *widget, gpointer data)
-{
- toggle_something(data, FONT_UNDERLINE);
-}
-
-static void
-enable_toggled(const char *name, PurplePrefType type, gconstpointer val, gpointer data)
-{
- GtkWidget *widget = (GtkWidget *)data;
-
- gtk_widget_set_sensitive(widget, GPOINTER_TO_INT(val));
-}
-
-static void
-disconnect_prefs_callbacks(GtkObject *object, gpointer data)
-{
- PurplePlugin *plugin = (PurplePlugin *)data;
-
- purple_prefs_disconnect_by_handle(plugin);
-}
-
-static GtkWidget *
-get_config_frame(PurplePlugin *plugin)
-{
- GtkWidget *ret;
- GtkWidget *frame;
- int i;
-
- ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
- gtk_container_set_border_width(GTK_CONTAINER(ret), PIDGIN_HIG_BORDER);
-
- for (i = 0; formats[i].prefix; i++)
- {
- char tmp[128];
- char tmp2[128];
- int f;
- gboolean e;
- GtkWidget *vbox, *hbox, *button;
-
- g_snprintf(tmp2, sizeof(tmp2), "%s/enabled", formats[i].prefix);
- e = purple_prefs_get_bool(tmp2);
-
- g_snprintf(tmp, sizeof(tmp), "%s/format", formats[i].prefix);
- f = purple_prefs_get_int(tmp);
-
- frame = pidgin_make_frame(ret, _(formats[i].text));
- vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
- gtk_box_pack_start(GTK_BOX(frame), vbox, FALSE, FALSE, 0);
-
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
- gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-
- button = gtk_check_button_new_with_label(_("Enabled"));
- gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
- if (e)
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
- g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(toggle_enabled),
- formats[i].prefix);
-
- button = pidgin_pixbuf_button_from_stock(" Color", GTK_STOCK_SELECT_COLOR,
- PIDGIN_BUTTON_HORIZONTAL);
- gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
- g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(set_color),
- formats[i].prefix);
- gtk_widget_set_sensitive(button, e);
- purple_prefs_connect_callback(plugin, tmp2, enable_toggled, button);
-
- button = gtk_check_button_new_with_label(_("Bold"));
- gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
- if (f & FONT_BOLD)
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
- g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(toggle_bold),
- formats[i].prefix);
- gtk_widget_set_sensitive(button, e);
- purple_prefs_connect_callback(plugin, tmp2, enable_toggled, button);
-
- button = gtk_check_button_new_with_label(_("Italic"));
- gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
- if (f & FONT_ITALIC)
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
- g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(toggle_italic),
- formats[i].prefix);
- gtk_widget_set_sensitive(button, e);
- purple_prefs_connect_callback(plugin, tmp2, enable_toggled, button);
-
- button = gtk_check_button_new_with_label(_("Underline"));
- gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
- if (f & FONT_UNDERLINE)
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
- g_signal_connect(G_OBJECT(button), "clicked",
- G_CALLBACK(toggle_underline), formats[i].prefix);
- gtk_widget_set_sensitive(button, e);
- purple_prefs_connect_callback(plugin, tmp2, enable_toggled, button);
- }
-
- g_signal_connect(GTK_OBJECT(ret), "destroy", G_CALLBACK(disconnect_prefs_callbacks), plugin);
- frame = pidgin_make_frame(ret, _("General"));
- pidgin_prefs_checkbox(_("Ignore incoming format"), PREF_IGNORE, frame);
- pidgin_prefs_checkbox(_("Apply in Chats"), PREF_CHATS, frame);
- pidgin_prefs_checkbox(_("Apply in IMs"), PREF_IMS, frame);
-
- gtk_widget_show_all(ret);
- return ret;
-}
-
-static PidginPluginUiInfo ui_info =
-{
- get_config_frame,
- 0,
-
- /* padding */
- NULL,
- NULL,
- NULL,
- NULL
-};
-
-static PurplePluginInfo info =
-{
- PURPLE_PLUGIN_MAGIC, /* Magic */
- PURPLE_MAJOR_VERSION, /* Purple Major Version */
- PURPLE_MINOR_VERSION, /* Purple Minor Version */
- PURPLE_PLUGIN_STANDARD, /* plugin type */
- PIDGIN_PLUGIN_TYPE, /* ui requirement */
- 0, /* flags */
- NULL, /* dependencies */
- PURPLE_PRIORITY_DEFAULT, /* priority */
-
- PLUGIN_ID, /* plugin id */
- PLUGIN_NAME, /* name */
- DISPLAY_VERSION, /* version */
- PLUGIN_SUMMARY, /* summary */
- PLUGIN_DESCRIPTION, /* description */
- PLUGIN_AUTHOR, /* author */
- PURPLE_WEBSITE, /* website */
-
- plugin_load, /* load */
- plugin_unload, /* unload */
- NULL, /* destroy */
-
- &ui_info, /* ui_info */
- NULL, /* extra_info */
- NULL, /* prefs_info */
- NULL, /* actions */
-
- /* padding */
- NULL,
- NULL,
- NULL,
- NULL
-};
-
-static void
-init_plugin(PurplePlugin *plugin)
-{
- purple_prefs_add_none(PREF_PREFIX);
-
- purple_prefs_add_bool(PREF_IGNORE, TRUE);
- purple_prefs_add_bool(PREF_CHATS, TRUE);
- purple_prefs_add_bool(PREF_IMS, TRUE);
-
- purple_prefs_add_none(PREF_SEND);
- purple_prefs_add_none(PREF_RECV);
- purple_prefs_add_none(PREF_SYSTEM);
- purple_prefs_add_none(PREF_ERROR);
- purple_prefs_add_none(PREF_NICK);
-
- purple_prefs_add_string(PREF_SEND_C, "#909090");
- purple_prefs_add_string(PREF_RECV_C, "#000000");
- purple_prefs_add_string(PREF_SYSTEM_C, "#50a050");
- purple_prefs_add_string(PREF_ERROR_C, "#ff0000");
- purple_prefs_add_string(PREF_NICK_C, "#0000dd");
-
- purple_prefs_add_int(PREF_SEND_F, 0);
- purple_prefs_add_int(PREF_RECV_F, 0);
- purple_prefs_add_int(PREF_SYSTEM_F, FONT_ITALIC);
- purple_prefs_add_int(PREF_ERROR_F, FONT_BOLD | FONT_UNDERLINE);
- purple_prefs_add_int(PREF_NICK_F, FONT_BOLD);
-
- purple_prefs_add_bool(PREF_SEND_E, TRUE);
- purple_prefs_add_bool(PREF_RECV_E, TRUE);
- purple_prefs_add_bool(PREF_SYSTEM_E, TRUE);
- purple_prefs_add_bool(PREF_ERROR_E, TRUE);
- purple_prefs_add_bool(PREF_NICK_E, TRUE);
-}
-
-PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info)
diff --git a/pidgin/plugins/crazychat/Makefile.am b/pidgin/plugins/crazychat/Makefile.am
index 339fbcaa5f..2b0a541822 100644
--- a/pidgin/plugins/crazychat/Makefile.am
+++ b/pidgin/plugins/crazychat/Makefile.am
@@ -1,4 +1,4 @@
-plugindir = $(libdir)/pidgin
+plugindir = @PIDGIN_PLUGINDIR@
if ENABLE_DEBUG
DEBUG_CPPFLAGS = -g -pg
@@ -17,7 +17,8 @@ else
MD_CPPFLAGS =
endif
-crazychat_la_LDFLAGS = -module -avoid-version $(GTK_LIBS) $(DEBUG_LFLAGS)
+crazychat_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+crazychat_la_LIBADD = @PIDGIN_LIBS@ $(GTKGLEXT_LIBS)
crazychat_la_SOURCES = cc_pidgin_plugin.c cc_gtk_gl.c cc_gtk_gl.h \
cc_interface.h crazychat.c crazychat.h util.h\
cc_network.c cc_network.h filter.c filter.h\
@@ -25,8 +26,6 @@ crazychat_la_SOURCES = cc_pidgin_plugin.c cc_gtk_gl.c cc_gtk_gl.h \
QT_CPPFLAGS = -D_DISABLE_QT_
-crazychat_la_LIBADD = $(GTKGLEXT_LIBS)
-
#AM_CFLAGS = $(GTK_CFLAGS) \
#$(GTKGLEXT_CFLAGS) \
#-I$(top_srcdir)/include
diff --git a/pidgin/plugins/crazychat/cc_network.c b/pidgin/plugins/crazychat/cc_network.c
index a80e580c18..45c30eab14 100644
--- a/pidgin/plugins/crazychat/cc_network.c
+++ b/pidgin/plugins/crazychat/cc_network.c
@@ -97,22 +97,22 @@ void cc_net_send_invite(struct crazychat *cc, char *name, PurpleAccount *account
{
struct cc_session *session;
PurpleConversation *conv;
- PurpleConvIm *im;
+ PurpleIMConversation *im;
char buf[BUFSIZ];
session = cc_find_session(cc, name);
if (session) return; /* already have a session with this guy */
session = cc_add_session(cc, name);
session->state = INVITE;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, name, account);
+ conv = purple_conversations_find_with_account(name, account);
if (!conv) {
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, name);
+ conv = purple_im_conversation_new(account, name);
}
im = purple_conversation_get_im_data(conv);
snprintf(buf, BUFSIZ, "%s%s!%d", CRAZYCHAT_INVITE_CODE,
purple_network_get_my_ip(-1), cc->tcp_port);
Debug("Sent invite to %s for port: %d\n", name, cc->tcp_port);
- purple_conv_im_send(im, buf);
+ purple_im_conversation_send(im, buf);
}
void cc_net_recv_invite(PurpleAccount *account, struct crazychat *cc, char *name,
@@ -131,7 +131,7 @@ void cc_net_recv_invite(PurpleAccount *account, struct crazychat *cc, char *name
session = cc_find_session(cc, name);
if (!session) {
Debug("Creating a CrazyChat session invite dialog box!\n");
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, name, account);
+ conv = purple_conversations_find_with_account(name, account);
if (conv) convwin = purple_conversation_get_window(conv);
else convwin = NULL;
/* pop gtk window asking if want to accept */
@@ -206,15 +206,15 @@ static void cc_net_send_ready(PurpleAccount *account, struct cc_session *session
/* socket created, send the ready message */
PurpleConversation *conv;
- PurpleConvIm *im;
+ PurpleIMConversation *im;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, session->name, account);
+ conv = purple_conversations_find_with_account(session->name, account);
if (!conv) {
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account,
+ conv = purple_im_conversation_new(account,
session->name);
}
im = purple_conversation_get_im_data(conv);
- purple_conv_im_send(im, CRAZYCHAT_READY_CODE);
+ purple_im_conversation_send(im, CRAZYCHAT_READY_CODE);
/* register timer callback for checking socket connection */
args = (struct sock_accept_args*)malloc(sizeof(*args));
@@ -260,7 +260,7 @@ static void invite_handler(GtkDialog *dialog, gint response, struct accept_args
struct cc_session *session;
char buf[BUFSIZ];
PurpleConversation *conv;
- PurpleConvIm *im;
+ PurpleIMConversation *im;
if (response == GTK_RESPONSE_ACCEPT) {
assert(args);
@@ -272,14 +272,14 @@ static void invite_handler(GtkDialog *dialog, gint response, struct accept_args
session->peer_port = args->peer_port;
snprintf(buf, BUFSIZ, "%s%s", CRAZYCHAT_ACCEPT_CODE,
purple_network_get_my_ip(-1));
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, args->name,
+ conv = purple_conversations_find_with_account(args->name,
args->account);
if (!conv) {
conv = purple_conversation_new(PURPLE_CONV_TYPE_IM,
args->account, args->name);
}
im = purple_conversation_get_im_data(conv);
- purple_conv_im_send(im, buf);
+ purple_im_conversation_send(im, buf);
}
free(args->name);
free(args);
diff --git a/pidgin/plugins/crazychat/cc_pidgin_plugin.c b/pidgin/plugins/crazychat/cc_pidgin_plugin.c
index 6219d5f615..b813975d2a 100644
--- a/pidgin/plugins/crazychat/cc_pidgin_plugin.c
+++ b/pidgin/plugins/crazychat/cc_pidgin_plugin.c
@@ -71,8 +71,7 @@ static gboolean receive_im_cb(PurpleAccount *account, char **sender,
* @param message the message we are displaying
* @param data user data
*/
-static gboolean display_im_cb(PurpleAccount *account, const char *who, char **message,
- PurpleConnection *conv, PurpleMessageFlags flags, void *data);
+static gboolean display_im_cb(PurpleConversation *conv, PurpleMessage *pmsg, gpointer data);
/**
* Callback for CrazyChat plugin configuration frame
@@ -247,8 +246,8 @@ static gboolean receive_im_cb(PurpleAccount *account, char **sender,
return FALSE;
}
-static gboolean display_im_cb(PurpleAccount *account, PurpleConversation *conv,
- char **message, void *data)
+static gboolean
+display_im_cb(PurpleConversation *conv, PurpleMessage *pmsg, gpointer data);
{
struct crazychat *cc;
@@ -413,7 +412,7 @@ static gboolean cc_signed_on(PurpleConnection *gc, void *plugin)
(purple_connections_get_handle(), "signed-on",
plugin, PURPLE_CALLBACK(cc_signed_on));
purple_signal_connect(PIDGIN_BLIST
- (purple_get_blist()),
+ (purple_blist_get_buddy_list()),
"drawing-menu", plugin,
PURPLE_CALLBACK(cc_buddy_menu), NULL);
conv_handle = purple_conversations_get_handle();
@@ -433,7 +432,7 @@ static gboolean plugin_load(PurplePlugin *plugin)
return FALSE;
cc_init(&cc_info);
- buddy_list = purple_get_blist();
+ buddy_list = purple_blist_get_buddy_list();
if (buddy_list) {
purple_signal_connect(PIDGIN_BLIST
(buddy_list),
@@ -464,7 +463,7 @@ static gboolean plugin_unload(PurplePlugin *plugin)
cc_destroy(extra);
conv_handle = purple_conversations_get_handle();
purple_signal_disconnect(PIDGIN_BLIST
- (purple_get_blist()),
+ (purple_blist_get_buddy_list()),
"drawing-menu", plugin,
PURPLE_CALLBACK(cc_buddy_menu));
purple_signal_disconnect(conv_handle, "received-im", plugin,
diff --git a/pidgin/plugins/disco/Makefile.am b/pidgin/plugins/disco/Makefile.am
index 36f6ba3a7a..7181a293eb 100644
--- a/pidgin/plugins/disco/Makefile.am
+++ b/pidgin/plugins/disco/Makefile.am
@@ -1,6 +1,6 @@
-plugindir = $(libdir)/pidgin
+plugindir = @PIDGIN_PLUGINDIR@
-xmppdisco_la_LDFLAGS = -module -avoid-version
+xmppdisco_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
EXTRA_DIST = \
Makefile.mingw
@@ -15,12 +15,11 @@ xmppdisco_la_SOURCES = \
xmppdisco.c \
xmppdisco.h
-xmppdisco_la_LIBADD = $(GTK_LIBS)
+xmppdisco_la_LIBADD = @PIDGIN_LIBS@
endif
AM_CPPFLAGS = \
- -DDATADIR=\"$(datadir)\" \
-I$(top_srcdir)/libpurple \
-I$(top_builddir)/libpurple \
-I$(top_srcdir)/pidgin \
diff --git a/pidgin/plugins/disco/Makefile.mingw b/pidgin/plugins/disco/Makefile.mingw
index c07993839f..de7751ce02 100644
--- a/pidgin/plugins/disco/Makefile.mingw
+++ b/pidgin/plugins/disco/Makefile.mingw
@@ -19,6 +19,7 @@ INCLUDE_PATHS += -I. \
-I$(GTK_TOP)/include/pango-1.0 \
-I$(GTK_TOP)/include/atk-1.0 \
-I$(GTK_TOP)/include/cairo \
+ -I$(GTK_TOP)/include/gdk-pixbuf-2.0 \
-I$(GTK_TOP)/lib/glib-2.0/include \
-I$(GTK_TOP)/lib/gtk-2.0/include \
-I$(PURPLE_TOP) \
diff --git a/pidgin/plugins/disco/gtkdisco.c b/pidgin/plugins/disco/gtkdisco.c
index 5cdcd5c757..89c29556d4 100644
--- a/pidgin/plugins/disco/gtkdisco.c
+++ b/pidgin/plugins/disco/gtkdisco.c
@@ -31,6 +31,7 @@
#include "request.h"
#include "pidgintooltip.h"
+#include "gtk3compat.h"
#include "gtkdisco.h"
#include "xmppdisco.h"
@@ -119,14 +120,18 @@ pidgin_disco_load_icon(XmppDiscoService *service, const char *size)
if (service->type == XMPP_DISCO_SERVICE_TYPE_GATEWAY && service->gateway_type) {
char *tmp = g_strconcat(service->gateway_type, ".png", NULL);
- filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", size, tmp, NULL);
+ filename = g_build_filename(PURPLE_DATADIR,
+ "pixmaps", "pidgin", "protocols", size, tmp, NULL);
g_free(tmp);
#if 0
} else if (service->type == XMPP_DISCO_SERVICE_TYPE_USER) {
- filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "status", size, "person.png", NULL);
+ filename = g_build_filename(PURPLE_DATADIR,
+ "pixmaps", "pidgin", "status", size, "person.png", NULL);
#endif
- } else if (service->type == XMPP_DISCO_SERVICE_TYPE_CHAT)
- filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "status", size, "chat.png", NULL);
+ } else if (service->type == XMPP_DISCO_SERVICE_TYPE_CHAT) {
+ filename = g_build_filename(PURPLE_DATADIR,
+ "pixmaps", "pidgin", "status", size, "chat.png", NULL);
+ }
if (filename) {
pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
@@ -175,7 +180,7 @@ static void discolist_ok_cb(PidginDiscoList *pdl, const char *server)
if (!server || !*server) {
purple_notify_error(my_plugin, _("Invalid Server"), _("Invalid Server"),
- NULL);
+ NULL, purple_request_cpar_from_connection(pdl->pc));
pidgin_disco_list_set_in_progress(pdl, FALSE);
pidgin_disco_list_unref(pdl);
@@ -244,7 +249,7 @@ static void browse_button_cb(GtkWidget *button, PidginDiscoDialog *dialog)
server, FALSE, FALSE, NULL,
_("Find Services"), PURPLE_CALLBACK(discolist_ok_cb),
_("Cancel"), PURPLE_CALLBACK(discolist_cancel_cb),
- purple_connection_get_account(pc), NULL, NULL, pdl);
+ purple_request_cpar_from_connection(pc), pdl);
g_free(server);
}
@@ -427,18 +432,20 @@ static gboolean account_filter_func(PurpleAccount *account)
}
static gboolean
-disco_paint_tooltip(GtkWidget *tipwindow, gpointer data)
+disco_paint_tooltip(GtkWidget *tipwindow, cairo_t *cr, gpointer data)
{
PangoLayout *layout = g_object_get_data(G_OBJECT(tipwindow), "tooltip-plugin");
-#if GTK_CHECK_VERSION(2,14,0)
- gtk_paint_layout(gtk_widget_get_style(tipwindow),
- gtk_widget_get_window(tipwindow),
- GTK_STATE_NORMAL, FALSE,
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context = gtk_widget_get_style_context(tipwindow);
+ gtk_style_context_add_class(context, GTK_STYLE_CLASS_TOOLTIP);
+ gtk_render_layout(context, cr, 6, 6, layout);
#else
- gtk_paint_layout(tipwindow->style, tipwindow->window, GTK_STATE_NORMAL, FALSE,
+ gtk_paint_layout(gtk_widget_get_style(tipwindow),
+ gtk_widget_get_window(tipwindow),
+ GTK_STATE_NORMAL, FALSE,
+ NULL, tipwindow, "tooltip",
+ 6, 6, layout);
#endif
- NULL, tipwindow, "tooltip",
- 6, 6, layout);
return TRUE;
}
@@ -642,7 +649,7 @@ PidginDiscoDialog *pidgin_disco_dialog_new(void)
/* Create the parent vbox for everything. */
vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(window), FALSE, PIDGIN_HIG_BORDER);
- vbox2 = gtk_vbox_new(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);
diff --git a/pidgin/plugins/disco/xmppdisco.c b/pidgin/plugins/disco/xmppdisco.c
index 07efa34fc4..300bf225de 100644
--- a/pidgin/plugins/disco/xmppdisco.c
+++ b/pidgin/plugins/disco/xmppdisco.c
@@ -44,7 +44,6 @@ disco#info:
#include "version.h"
#include "gtkconv.h"
-#include "gtkimhtml.h"
#include "gtkplugin.h"
#include "xmppdisco.h"
@@ -56,7 +55,7 @@ static GHashTable *iq_callbacks = NULL;
static gboolean iq_listening = FALSE;
typedef void (*XmppIqCallback)(PurpleConnection *pc, const char *type,
- const char *id, const char *from, xmlnode *iq,
+ const char *id, const char *from, PurpleXmlNode *iq,
gpointer data);
struct item_data {
@@ -116,7 +115,7 @@ remove_iq_callbacks_by_pc(gpointer key, gpointer value, gpointer user_data)
static gboolean
xmpp_iq_received(PurpleConnection *pc, const char *type, const char *id,
- const char *from, xmlnode *iq)
+ const char *from, PurpleXmlNode *iq)
{
struct xmpp_iq_cb_data *cb_data;
@@ -162,18 +161,18 @@ static void
xmpp_disco_info_do(PurpleConnection *pc, gpointer cbdata, const char *jid,
const char *node, XmppIqCallback cb)
{
- xmlnode *iq, *query;
+ PurpleXmlNode *iq, *query;
char *id = generate_next_id();
- iq = xmlnode_new("iq");
- xmlnode_set_attrib(iq, "type", "get");
- xmlnode_set_attrib(iq, "to", jid);
- xmlnode_set_attrib(iq, "id", id);
+ iq = purple_xmlnode_new("iq");
+ purple_xmlnode_set_attrib(iq, "type", "get");
+ purple_xmlnode_set_attrib(iq, "to", jid);
+ purple_xmlnode_set_attrib(iq, "id", id);
- query = xmlnode_new_child(iq, "query");
- xmlnode_set_namespace(query, NS_DISCO_INFO);
+ query = purple_xmlnode_new_child(iq, "query");
+ purple_xmlnode_set_namespace(query, NS_DISCO_INFO);
if (node)
- xmlnode_set_attrib(query, "node", node);
+ purple_xmlnode_set_attrib(query, "node", node);
/* Steals id */
xmpp_iq_register_callback(pc, id, cbdata, cb);
@@ -181,25 +180,25 @@ xmpp_disco_info_do(PurpleConnection *pc, gpointer cbdata, const char *jid,
purple_signal_emit(purple_connection_get_prpl(pc), "jabber-sending-xmlnode",
pc, &iq);
if (iq != NULL)
- xmlnode_free(iq);
+ purple_xmlnode_free(iq);
}
static void
xmpp_disco_items_do(PurpleConnection *pc, gpointer cbdata, const char *jid,
const char *node, XmppIqCallback cb)
{
- xmlnode *iq, *query;
+ PurpleXmlNode *iq, *query;
char *id = generate_next_id();
- iq = xmlnode_new("iq");
- xmlnode_set_attrib(iq, "type", "get");
- xmlnode_set_attrib(iq, "to", jid);
- xmlnode_set_attrib(iq, "id", id);
+ iq = purple_xmlnode_new("iq");
+ purple_xmlnode_set_attrib(iq, "type", "get");
+ purple_xmlnode_set_attrib(iq, "to", jid);
+ purple_xmlnode_set_attrib(iq, "id", id);
- query = xmlnode_new_child(iq, "query");
- xmlnode_set_namespace(query, NS_DISCO_ITEMS);
+ query = purple_xmlnode_new_child(iq, "query");
+ purple_xmlnode_set_namespace(query, NS_DISCO_ITEMS);
if (node)
- xmlnode_set_attrib(query, "node", node);
+ purple_xmlnode_set_attrib(query, "node", node);
/* Steals id */
xmpp_iq_register_callback(pc, id, cbdata, cb);
@@ -207,19 +206,19 @@ xmpp_disco_items_do(PurpleConnection *pc, gpointer cbdata, const char *jid,
purple_signal_emit(purple_connection_get_prpl(pc), "jabber-sending-xmlnode",
pc, &iq);
if (iq != NULL)
- xmlnode_free(iq);
+ purple_xmlnode_free(iq);
}
static XmppDiscoServiceType
-disco_service_type_from_identity(xmlnode *identity)
+disco_service_type_from_identity(PurpleXmlNode *identity)
{
const char *category, *type;
if (!identity)
return XMPP_DISCO_SERVICE_TYPE_OTHER;
- category = xmlnode_get_attrib(identity, "category");
- type = xmlnode_get_attrib(identity, "type");
+ category = purple_xmlnode_get_attrib(identity, "category");
+ type = purple_xmlnode_get_attrib(identity, "type");
if (!category)
return XMPP_DISCO_SERVICE_TYPE_OTHER;
@@ -252,7 +251,6 @@ static const struct {
} disco_type_mappings[] = {
{ "gadu-gadu", "gadu-gadu" }, /* the prpl is prpl-gg, but list_icon returns "gadu-gadu" */
{ "sametime", "meanwhile" },
- { "myspaceim", "myspace" },
{ "xmpp", "jabber" }, /* prpl-jabber (mentioned in case the prpl is renamed so this line will match) */
{ NULL, NULL }
};
@@ -265,7 +263,7 @@ disco_type_from_string(const gchar *str)
g_return_val_if_fail(str != NULL, "");
for ( ; disco_type_mappings[i].from; ++i) {
- if (!strcasecmp(str, disco_type_mappings[i].from))
+ if (!g_ascii_strcasecmp(str, disco_type_mappings[i].from))
return disco_type_mappings[i].to;
}
@@ -275,11 +273,11 @@ disco_type_from_string(const gchar *str)
static void
got_info_cb(PurpleConnection *pc, const char *type, const char *id,
- const char *from, xmlnode *iq, gpointer data)
+ const char *from, PurpleXmlNode *iq, gpointer data)
{
struct item_data *item_data = data;
PidginDiscoList *list = item_data->list;
- xmlnode *query;
+ PurpleXmlNode *query;
--list->fetch_count;
@@ -287,10 +285,10 @@ got_info_cb(PurpleConnection *pc, const char *type, const char *id,
goto out;
if (g_str_equal(type, "result") &&
- (query = xmlnode_get_child(iq, "query"))) {
- xmlnode *identity = xmlnode_get_child(query, "identity");
+ (query = purple_xmlnode_get_child(iq, "query"))) {
+ PurpleXmlNode *identity = purple_xmlnode_get_child(query, "identity");
XmppDiscoService *service;
- xmlnode *feature;
+ PurpleXmlNode *feature;
service = g_new0(XmppDiscoService, 1);
service->list = item_data->list;
@@ -322,15 +320,15 @@ got_info_cb(PurpleConnection *pc, const char *type, const char *id,
service->description = item_data->name;
item_data->name = NULL;
} else if (identity)
- service->description = g_strdup(xmlnode_get_attrib(identity, "name"));
+ service->description = g_strdup(purple_xmlnode_get_attrib(identity, "name"));
/* TODO: Overlap with service->name a bit */
service->jid = g_strdup(from);
- for (feature = xmlnode_get_child(query, "feature"); feature;
- feature = xmlnode_get_next_twin(feature)) {
+ for (feature = purple_xmlnode_get_child(query, "feature"); feature;
+ feature = purple_xmlnode_get_next_twin(feature)) {
const char *var;
- if (!(var = xmlnode_get_attrib(feature, "var")))
+ if (!(var = purple_xmlnode_get_attrib(feature, "var")))
continue;
if (g_str_equal(var, NS_REGISTER))
@@ -345,7 +343,7 @@ got_info_cb(PurpleConnection *pc, const char *type, const char *id,
if (service->type == XMPP_DISCO_SERVICE_TYPE_GATEWAY)
service->gateway_type = g_strdup(disco_type_from_string(
- xmlnode_get_attrib(identity, "type")));
+ purple_xmlnode_get_attrib(identity, "type")));
pidgin_disco_add_service(list, service, service->parent);
}
@@ -362,11 +360,11 @@ out:
static void
got_items_cb(PurpleConnection *pc, const char *type, const char *id,
- const char *from, xmlnode *iq, gpointer data)
+ const char *from, PurpleXmlNode *iq, gpointer data)
{
struct item_data *item_data = data;
PidginDiscoList *list = item_data->list;
- xmlnode *query;
+ PurpleXmlNode *query;
gboolean has_items = FALSE;
--list->fetch_count;
@@ -375,14 +373,14 @@ got_items_cb(PurpleConnection *pc, const char *type, const char *id,
goto out;
if (g_str_equal(type, "result") &&
- (query = xmlnode_get_child(iq, "query"))) {
- xmlnode *item;
+ (query = purple_xmlnode_get_child(iq, "query"))) {
+ PurpleXmlNode *item;
- for (item = xmlnode_get_child(query, "item"); item;
- item = xmlnode_get_next_twin(item)) {
- const char *jid = xmlnode_get_attrib(item, "jid");
- const char *name = xmlnode_get_attrib(item, "name");
- const char *node = xmlnode_get_attrib(item, "node");
+ for (item = purple_xmlnode_get_child(query, "item"); item;
+ item = purple_xmlnode_get_next_twin(item)) {
+ const char *jid = purple_xmlnode_get_attrib(item, "jid");
+ const char *name = purple_xmlnode_get_attrib(item, "name");
+ const char *node = purple_xmlnode_get_attrib(item, "node");
has_items = TRUE;
@@ -432,24 +430,24 @@ out:
static void
server_items_cb(PurpleConnection *pc, const char *type, const char *id,
- const char *from, xmlnode *iq, gpointer data)
+ const char *from, PurpleXmlNode *iq, gpointer data)
{
struct item_data *cb_data = data;
PidginDiscoList *list = cb_data->list;
- xmlnode *query;
+ PurpleXmlNode *query;
g_free(cb_data);
--list->fetch_count;
if (g_str_equal(type, "result") &&
- (query = xmlnode_get_child(iq, "query"))) {
- xmlnode *item;
-
- for (item = xmlnode_get_child(query, "item"); item;
- item = xmlnode_get_next_twin(item)) {
- const char *jid = xmlnode_get_attrib(item, "jid");
- const char *name = xmlnode_get_attrib(item, "name");
- const char *node = xmlnode_get_attrib(item, "node");
+ (query = purple_xmlnode_get_child(iq, "query"))) {
+ PurpleXmlNode *item;
+
+ for (item = purple_xmlnode_get_child(query, "item"); item;
+ item = purple_xmlnode_get_next_twin(item)) {
+ const char *jid = purple_xmlnode_get_attrib(item, "jid");
+ const char *name = purple_xmlnode_get_attrib(item, "name");
+ const char *node = purple_xmlnode_get_attrib(item, "node");
struct item_data *item_data;
if (!jid)
@@ -474,23 +472,23 @@ server_items_cb(PurpleConnection *pc, const char *type, const char *id,
static void
server_info_cb(PurpleConnection *pc, const char *type, const char *id,
- const char *from, xmlnode *iq, gpointer data)
+ const char *from, PurpleXmlNode *iq, gpointer data)
{
struct item_data *cb_data = data;
PidginDiscoList *list = cb_data->list;
- xmlnode *query;
- xmlnode *error;
+ PurpleXmlNode *query;
+ PurpleXmlNode *error;
gboolean items = FALSE;
--list->fetch_count;
if (g_str_equal(type, "result") &&
- (query = xmlnode_get_child(iq, "query"))) {
- xmlnode *feature;
+ (query = purple_xmlnode_get_child(iq, "query"))) {
+ PurpleXmlNode *feature;
- for (feature = xmlnode_get_child(query, "feature"); feature;
- feature = xmlnode_get_next_twin(feature)) {
- const char *var = xmlnode_get_attrib(feature, "var");
+ for (feature = purple_xmlnode_get_child(query, "feature"); feature;
+ feature = purple_xmlnode_get_next_twin(feature)) {
+ const char *var = purple_xmlnode_get_attrib(feature, "var");
if (purple_strequal(var, NS_DISCO_ITEMS)) {
items = TRUE;
break;
@@ -508,17 +506,17 @@ server_info_cb(PurpleConnection *pc, const char *type, const char *id,
}
}
else {
- error = xmlnode_get_child(iq, "error");
- if (xmlnode_get_child(error, "remote-server-not-found")
- || xmlnode_get_child(error, "jid-malformed")) {
+ error = purple_xmlnode_get_child(iq, "error");
+ if (purple_xmlnode_get_child(error, "remote-server-not-found")
+ || purple_xmlnode_get_child(error, "jid-malformed")) {
purple_notify_error(my_plugin, _("Error"),
_("Server does not exist"),
- NULL);
+ NULL, NULL);
}
else {
purple_notify_error(my_plugin, _("Error"),
_("Server does not support service discovery"),
- NULL);
+ NULL, NULL);
}
pidgin_disco_list_set_in_progress(list, FALSE);
g_free(cb_data);
@@ -567,21 +565,21 @@ void xmpp_disco_service_expand(XmppDiscoService *service)
void xmpp_disco_service_register(XmppDiscoService *service)
{
- xmlnode *iq, *query;
+ PurpleXmlNode *iq, *query;
char *id = generate_next_id();
- iq = xmlnode_new("iq");
- xmlnode_set_attrib(iq, "type", "get");
- xmlnode_set_attrib(iq, "to", service->jid);
- xmlnode_set_attrib(iq, "id", id);
+ iq = purple_xmlnode_new("iq");
+ purple_xmlnode_set_attrib(iq, "type", "get");
+ purple_xmlnode_set_attrib(iq, "to", service->jid);
+ purple_xmlnode_set_attrib(iq, "id", id);
- query = xmlnode_new_child(iq, "query");
- xmlnode_set_namespace(query, NS_REGISTER);
+ query = purple_xmlnode_new_child(iq, "query");
+ purple_xmlnode_set_namespace(query, NS_REGISTER);
purple_signal_emit(purple_connection_get_prpl(service->list->pc),
"jabber-sending-xmlnode", service->list->pc, &iq);
if (iq != NULL)
- xmlnode_free(iq);
+ purple_xmlnode_free(iq);
g_free(id);
}
diff --git a/pidgin/plugins/extplacement.c b/pidgin/plugins/extplacement.c
index 494e0849d2..dd5b9958fc 100644
--- a/pidgin/plugins/extplacement.c
+++ b/pidgin/plugins/extplacement.c
@@ -31,11 +31,12 @@
static void
conv_placement_by_number(PidginConversation *conv)
{
- PidginWindow *win = NULL;
+ PidginConvWindow *win = NULL;
GList *wins = NULL;
if (purple_prefs_get_bool("/plugins/gtk/extplacement/placement_number_separate"))
- win = pidgin_conv_window_last_with_type(purple_conversation_get_type(conv->active_conv));
+ win = PURPLE_IS_IM_CONVERSATION(conv->active_conv) ?
+ pidgin_conv_window_last_im() : pidgin_conv_window_last_chat();
else if ((wins = pidgin_conv_windows_get_list()) != NULL)
win = g_list_last(wins)->data;
@@ -56,8 +57,15 @@ conv_placement_by_number(PidginConversation *conv)
for (l = pidgin_conv_windows_get_list(); l != NULL; l = l->next) {
win = l->data;
+ if (!conv || !conv->active_conv ||
+ !G_TYPE_FROM_INSTANCE(conv->active_conv))
+ {
+ g_warn_if_reached();
+ continue;
+ }
+
if (purple_prefs_get_bool("/plugins/gtk/extplacement/placement_number_separate") &&
- purple_conversation_get_type(pidgin_conv_window_get_active_conversation(win)) != purple_conversation_get_type(conv->active_conv))
+ G_TYPE_FROM_INSTANCE(pidgin_conv_window_get_active_conversation(win)) != G_TYPE_FROM_INSTANCE(conv->active_conv))
continue;
count = pidgin_conv_window_get_gtkconv_count(win);
@@ -103,7 +111,7 @@ get_plugin_pref_frame(PurplePlugin *plugin) {
/* Translators: "New conversations" should match the text in the preferences dialog and "By conversation count" should be the same text used above */
ppref = purple_plugin_pref_new_with_label(_("Note: The preference for \"New conversations\" must be set to \"By conversation count\"."));
- purple_plugin_pref_set_type(ppref, PURPLE_PLUGIN_PREF_INFO);
+ purple_plugin_pref_set_pref_type(ppref, PURPLE_PLUGIN_PREF_INFO);
purple_plugin_pref_frame_add(frame, ppref);
ppref = purple_plugin_pref_new_with_name_and_label(
@@ -122,8 +130,7 @@ get_plugin_pref_frame(PurplePlugin *plugin) {
static PurplePluginUiInfo prefs_info = {
get_plugin_pref_frame,
- 0, /* page_num (Reserved) */
- NULL, /* frame (Reserved) */
+ NULL,
/* padding */
NULL,
diff --git a/pidgin/plugins/gestures/Makefile.am b/pidgin/plugins/gestures/Makefile.am
index faa3baf74c..b1994f59c2 100644
--- a/pidgin/plugins/gestures/Makefile.am
+++ b/pidgin/plugins/gestures/Makefile.am
@@ -1,6 +1,6 @@
-plugindir = $(libdir)/pidgin
+plugindir = @PIDGIN_PLUGINDIR@
-gestures_la_LDFLAGS = -module -avoid-version
+gestures_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
if PLUGINS
@@ -13,12 +13,11 @@ gestures_la_SOURCES = \
stroke.c \
stroke-draw.c
-gestures_la_LIBADD = $(GTK_LIBS)
+gestures_la_LIBADD = @PIDGIN_LIBS@
endif
AM_CPPFLAGS = \
- -DDATADIR=\"$(datadir)\" \
-I$(top_srcdir)/libpurple \
-I$(top_builddir)/libpurple \
-I$(top_srcdir)/pidgin \
diff --git a/pidgin/plugins/gestures/gestures.c b/pidgin/plugins/gestures/gestures.c
index 4848a844b1..e8aae0a41e 100644
--- a/pidgin/plugins/gestures/gestures.c
+++ b/pidgin/plugins/gestures/gestures.c
@@ -26,6 +26,7 @@
#include "signals.h"
#include "version.h"
+#include "gtk3compat.h"
#include "gtkconv.h"
#include "gtkplugin.h"
#include "gtkutils.h"
@@ -48,12 +49,12 @@ stroke_close(GtkWidget *widget, void *data)
gtkconv = PIDGIN_CONVERSATION(conv);
- gstroke_cleanup(gtkconv->imhtml);
- purple_conversation_destroy(conv);
+ gstroke_cleanup(gtkconv->webview);
+ g_object_unref(conv);
}
static void
-switch_page(PidginWindow *win, GtkDirectionType dir)
+switch_page(PidginConvWindow *win, GtkDirectionType dir)
{
int count, current;
@@ -78,7 +79,7 @@ stroke_prev_tab(GtkWidget *widget, void *data)
{
PurpleConversation *conv;
PidginConversation *gtkconv;
- PidginWindow *win;
+ PidginConvWindow *win;
conv = (PurpleConversation *)data;
gtkconv = PIDGIN_CONVERSATION(conv);
@@ -91,7 +92,7 @@ static void
stroke_next_tab(GtkWidget *widget, void *data)
{
PurpleConversation *conv;
- PidginWindow *win;
+ PidginConvWindow *win;
conv = (PurpleConversation *)data;
win = PIDGIN_CONVERSATION(conv)->win;
@@ -102,7 +103,7 @@ stroke_next_tab(GtkWidget *widget, void *data)
static void
stroke_new_win(GtkWidget *widget, void *data)
{
- PidginWindow *new_win, *old_win;
+ PidginConvWindow *new_win, *old_win;
PurpleConversation *conv;
conv = (PurpleConversation *)data;
@@ -126,15 +127,15 @@ attach_signals(PurpleConversation *conv)
gtkconv = PIDGIN_CONVERSATION(conv);
- gstroke_enable(gtkconv->imhtml);
- gstroke_signal_connect(gtkconv->imhtml, "14789", stroke_close, conv);
- gstroke_signal_connect(gtkconv->imhtml, "1456", stroke_close, conv);
- gstroke_signal_connect(gtkconv->imhtml, "1489", stroke_close, conv);
- gstroke_signal_connect(gtkconv->imhtml, "74123", stroke_next_tab, conv);
- gstroke_signal_connect(gtkconv->imhtml, "7456", stroke_next_tab, conv);
- gstroke_signal_connect(gtkconv->imhtml, "96321", stroke_prev_tab, conv);
- gstroke_signal_connect(gtkconv->imhtml, "9654", stroke_prev_tab, conv);
- gstroke_signal_connect(gtkconv->imhtml, "25852", stroke_new_win, conv);
+ gstroke_enable(gtkconv->webview);
+ gstroke_signal_connect(gtkconv->webview, "14789", stroke_close, conv);
+ gstroke_signal_connect(gtkconv->webview, "1456", stroke_close, conv);
+ gstroke_signal_connect(gtkconv->webview, "1489", stroke_close, conv);
+ gstroke_signal_connect(gtkconv->webview, "74123", stroke_next_tab, conv);
+ gstroke_signal_connect(gtkconv->webview, "7456", stroke_next_tab, conv);
+ gstroke_signal_connect(gtkconv->webview, "96321", stroke_prev_tab, conv);
+ gstroke_signal_connect(gtkconv->webview, "9654", stroke_prev_tab, conv);
+ gstroke_signal_connect(gtkconv->webview, "25852", stroke_new_win, conv);
}
static void
@@ -145,7 +146,6 @@ new_conv_cb(PurpleConversation *conv)
}
#if 0
-#if GTK_CHECK_VERSION(2,4,0)
static void
mouse_button_menu_cb(GtkComboBox *opt, gpointer data)
{
@@ -153,15 +153,6 @@ mouse_button_menu_cb(GtkComboBox *opt, gpointer data)
gstroke_set_mouse_button(button + 2);
}
-#else
-static void
-mouse_button_menu_cb(GtkMenuItem *item, gpointer data)
-{
- int button = (int)data;
-
- gstroke_set_mouse_button(button + 2);
-}
-#endif
#endif
static void
@@ -184,7 +175,7 @@ plugin_load(PurplePlugin *plugin)
PurpleConversation *conv;
GList *l;
- for (l = purple_get_conversations(); l != NULL; l = l->next) {
+ for (l = purple_conversations_get_all(); l != NULL; l = l->next) {
conv = (PurpleConversation *)l->data;
if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv))
@@ -207,7 +198,7 @@ plugin_unload(PurplePlugin *plugin)
PidginConversation *gtkconv;
GList *l;
- for (l = purple_get_conversations(); l != NULL; l = l->next) {
+ for (l = purple_conversations_get_all(); l != NULL; l = l->next) {
conv = (PurpleConversation *)l->data;
if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv))
@@ -215,8 +206,8 @@ plugin_unload(PurplePlugin *plugin)
gtkconv = PIDGIN_CONVERSATION(conv);
- gstroke_cleanup(gtkconv->imhtml);
- gstroke_disable(gtkconv->imhtml);
+ gstroke_cleanup(gtkconv->webview);
+ gstroke_disable(gtkconv->webview);
}
return TRUE;
@@ -230,20 +221,16 @@ get_config_frame(PurplePlugin *plugin)
GtkWidget *toggle;
#if 0
GtkWidget *opt;
-#if GTK_CHECK_VERSION(2,4,0)
- GtkWidget *menu, *item;
-#endif
#endif
/* Outside container */
- ret = gtk_vbox_new(FALSE, 18);
+ ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
gtk_container_set_border_width(GTK_CONTAINER(ret), 12);
/* Configuration frame */
vbox = pidgin_make_frame(ret, _("Mouse Gestures Configuration"));
#if 0
-#if GTK_CHECK_VERSION(2,4,0)
/* Mouse button drop-down menu */
opt = gtk_combo_box_new_text();
@@ -255,26 +242,6 @@ get_config_frame(PurplePlugin *plugin)
gtk_box_pack_start(GTK_BOX(vbox), opt, FALSE, FALSE, 0);
gtk_combo_box_set_active(GTK_COMBO_BOX(opt),
gstroke_get_mouse_button() - 2);
-#else
- /* Mouse button drop-down menu */
- menu = gtk_menu_new();
- opt = gtk_option_menu_new();
-
- item = gtk_menu_item_new_with_label(_("Middle mouse button"));
- g_signal_connect(G_OBJECT(item), "activate",
- G_CALLBACK(mouse_button_menu_cb), opt);
- gtk_menu_append(menu, item);
-
- item = gtk_menu_item_new_with_label(_("Right mouse button"));
- g_signal_connect(G_OBJECT(item), "activate",
- G_CALLBACK(mouse_button_menu_cb), opt);
- gtk_menu_append(menu, item);
-
- gtk_box_pack_start(GTK_BOX(vbox), opt, FALSE, FALSE, 0);
- gtk_option_menu_set_menu(GTK_OPTION_MENU(opt), menu);
- gtk_option_menu_set_history(GTK_OPTION_MENU(opt),
- gstroke_get_mouse_button() - 2);
-#endif
#endif
/* "Visual gesture display" checkbox */
@@ -293,7 +260,6 @@ get_config_frame(PurplePlugin *plugin)
static PidginPluginUiInfo ui_info =
{
get_config_frame,
- 0, /* page_num (Reserved) */
/* padding */
NULL,
@@ -353,8 +319,10 @@ init_plugin(PurplePlugin *plugin)
purple_prefs_add_none("/plugins/gtk/X11/gestures");
purple_prefs_add_bool("/plugins/gtk/X11/gestures/visual", FALSE);
- purple_prefs_connect_callback(plugin, "/plugins/gtk/X11/gestures/visual",
- visual_pref_cb, NULL);
+ purple_prefs_connect_callback(plugin,
+ "/plugins/gtk/X11/gestures/visual", visual_pref_cb, NULL);
+ gstroke_set_draw_strokes(purple_prefs_get_bool(
+ "/plugins/gtk/X11/gestures/visual"));
}
PURPLE_INIT_PLUGIN(gestures, init_plugin, info)
diff --git a/pidgin/plugins/gestures/stroke-draw.c b/pidgin/plugins/gestures/stroke-draw.c
index 023d0724ae..71da590d7f 100644
--- a/pidgin/plugins/gestures/stroke-draw.c
+++ b/pidgin/plugins/gestures/stroke-draw.c
@@ -19,9 +19,7 @@
#include <X11/Xlib.h>
#include <X11/Xutil.h>
-#if !GTK_CHECK_VERSION(2,14,0)
-#define gtk_widget_get_window(x) x->window
-#endif
+#include "gtk3compat.h"
static void gstroke_invisible_window_init (GtkWidget *widget);
/*FIXME: Maybe these should be put in a structure, and not static...*/
@@ -55,47 +53,53 @@ static guint timer_id;
static void gstroke_execute (GtkWidget *widget, const gchar *name);
static void
-record_stroke_segment (GtkWidget *widget)
+record_stroke_segment(GtkWidget *widget)
{
- gint x, y;
- struct gstroke_metrics *metrics;
+ gint x, y;
+ struct gstroke_metrics *metrics;
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkDeviceManager *devmgr;
+ GdkDevice *dev;
+#endif
- g_return_if_fail( widget != NULL );
+ g_return_if_fail(widget != NULL);
- gtk_widget_get_pointer (widget, &x, &y);
+#if GTK_CHECK_VERSION(3,0,0)
+ devmgr = gdk_display_get_device_manager(gtk_widget_get_display(widget));
+ dev = gdk_device_manager_get_client_pointer(devmgr);
+ gdk_window_get_device_position(gtk_widget_get_window(widget),
+ dev, &x, &y, NULL);
+#else
+ gtk_widget_get_pointer(widget, &x, &y);
+#endif
- if (last_mouse_position.invalid)
- last_mouse_position.invalid = FALSE;
- else if (gstroke_draw_strokes())
- {
+ if (last_mouse_position.invalid)
+ last_mouse_position.invalid = FALSE;
+ else if (gstroke_draw_strokes()) {
#if 1
- XDrawLine (gstroke_disp, gstroke_window, gstroke_gc,
- last_mouse_position.last_point.x,
- last_mouse_position.last_point.y,
- x, y);
- /* XFlush (gstroke_disp); */
+ XDrawLine(gstroke_disp, gstroke_window, gstroke_gc,
+ last_mouse_position.last_point.x,
+ last_mouse_position.last_point.y, x, y);
+ /* XFlush (gstroke_disp); */
#else
- /* FIXME: this does not work. It will only work if we create a
- corresponding GDK window for stroke_window and draw on
- that... */
- gdk_draw_line (gtk_widget_get_window(widget),
- widget->style->fg_gc[GTK_STATE_NORMAL],
- last_mouse_position.last_point.x,
- last_mouse_position.last_point.y,
- x,
- y);
+ /* FIXME: this does not work. It will only work if we create
+ * a corresponding GDK window for stroke_window and draw on
+ * that... */
+ gdk_draw_line(gtk_widget_get_window(widget),
+ widget->style->fg_gc[GTK_STATE_NORMAL],
+ last_mouse_position.last_point.x,
+ last_mouse_position.last_point.y, x, y);
#endif
- }
+ }
- if (last_mouse_position.last_point.x != x
- || last_mouse_position.last_point.y != y)
- {
- last_mouse_position.last_point.x = x;
- last_mouse_position.last_point.y = y;
- metrics = (struct gstroke_metrics *)g_object_get_data(G_OBJECT(widget),
- GSTROKE_METRICS);
- _gstroke_record (x, y, metrics);
- }
+ if (last_mouse_position.last_point.x != x ||
+ last_mouse_position.last_point.y != y)
+ {
+ last_mouse_position.last_point.x = x;
+ last_mouse_position.last_point.y = y;
+ metrics = g_object_get_data(G_OBJECT(widget), GSTROKE_METRICS);
+ _gstroke_record (x, y, metrics);
+ }
}
static gint
@@ -121,8 +125,11 @@ static void gstroke_cancel(GdkEvent *event)
timer_id = 0;
if( event != NULL )
+#if GTK_CHECK_VERSION(3,0,0)
+ gdk_device_ungrab(gdk_event_get_device(event), event->button.time);
+#else
gdk_pointer_ungrab (event->button.time);
-
+#endif
if (gstroke_draw_strokes() && gstroke_disp != NULL) {
/* get rid of the invisible stroke window */
@@ -160,9 +167,16 @@ process_event (GtkWidget *widget, GdkEvent *event, gpointer data G_GNUC_UNUSED)
if (cursor == NULL)
cursor = gdk_cursor_new(GDK_PENCIL);
+#if GTK_CHECK_VERSION(3,0,0)
+ 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);
+#else
gdk_pointer_grab (gtk_widget_get_window(widget), FALSE,
GDK_BUTTON_RELEASE_MASK, NULL, cursor,
event->button.time);
+#endif
timer_id = g_timeout_add (GSTROKE_TIMEOUT_DURATION,
gstroke_timeout, widget);
return TRUE;
@@ -181,7 +195,11 @@ process_event (GtkWidget *widget, GdkEvent *event, gpointer data G_GNUC_UNUSED)
last_mouse_position.invalid = TRUE;
original_widget = NULL;
g_source_remove (timer_id);
+#if GTK_CHECK_VERSION(3,0,0)
+ gdk_device_ungrab(gdk_event_get_device(event), event->button.time);
+#else
gdk_pointer_ungrab (event->button.time);
+#endif
timer_id = 0;
{
@@ -339,7 +357,7 @@ gstroke_invisible_window_init (GtkWidget *widget)
unsigned int border_width;
XSizeHints hints;
Display *disp = GDK_WINDOW_XDISPLAY(gtk_widget_get_window(widget));
- Window wind = GDK_WINDOW_XWINDOW (gtk_widget_get_window(widget));
+ Window wind = gdk_x11_window_get_xid(gtk_widget_get_window(widget));
int screen = DefaultScreen (disp);
if (!gstroke_draw_strokes())
diff --git a/pidgin/plugins/gevolution/Makefile.am b/pidgin/plugins/gevolution/Makefile.am
index 56c652633b..9134fd435c 100644
--- a/pidgin/plugins/gevolution/Makefile.am
+++ b/pidgin/plugins/gevolution/Makefile.am
@@ -1,6 +1,6 @@
-plugindir = $(libdir)/pidgin
+plugindir = @PIDGIN_PLUGINDIR@
-gevolution_la_LDFLAGS = -module -avoid-version
+gevolution_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
if PLUGINS
@@ -15,12 +15,11 @@ gevolution_la_SOURCES = \
new_person_dialog.c \
eds-utils.c
-gevolution_la_LIBADD = $(EVOLUTION_ADDRESSBOOK_LIBS) $(GTK_LIBS)
+gevolution_la_LIBADD = @PIDGIN_LIBS@ $(EVOLUTION_ADDRESSBOOK_LIBS)
endif
AM_CPPFLAGS = \
- -DDATADIR=\"$(datadir)\" \
-I$(top_srcdir)/libpurple \
-I$(top_builddir)/libpurple \
-I$(top_srcdir)/pidgin \
diff --git a/pidgin/plugins/gevolution/add_buddy_dialog.c b/pidgin/plugins/gevolution/add_buddy_dialog.c
index 4570726936..650cdbc1c4 100644
--- a/pidgin/plugins/gevolution/add_buddy_dialog.c
+++ b/pidgin/plugins/gevolution/add_buddy_dialog.c
@@ -21,6 +21,8 @@
#include "internal.h"
#include "gtkblist.h"
#include "pidgin.h"
+
+#include "gtk3compat.h"
#include "gtkutils.h"
#include "debug.h"
@@ -192,7 +194,7 @@ add_ims(GevoAddBuddyDialog *dialog, EContact *contact, const char *name,
if (account_name == NULL)
continue;
- if (purple_find_buddy(dialog->account, account_name) != NULL)
+ if (purple_blist_find_buddy(dialog->account, account_name) != NULL)
continue;
gtk_list_store_append(dialog->model, &iter);
@@ -227,7 +229,7 @@ add_ims(GevoAddBuddyDialog *dialog, EContact *contact, const char *name,
}
static void
-populate_treeview(GevoAddBuddyDialog *dialog, const gchar *uri)
+populate_treeview(GevoAddBuddyDialog *dialog, const gchar *uid)
{
EBookQuery *query;
EBook *book;
@@ -250,8 +252,7 @@ populate_treeview(GevoAddBuddyDialog *dialog, const gchar *uri)
gtk_list_store_clear(dialog->model);
- if (!gevo_load_addressbook(uri, &book, &err))
- {
+ if (!gevo_load_addressbook(uid, &book, &err)) {
purple_debug_error("evolution",
"Error retrieving default addressbook: %s\n", err->message);
g_error_free(err);
@@ -333,16 +334,15 @@ static void
addrbook_change_cb(GtkComboBox *combo, GevoAddBuddyDialog *dialog)
{
GtkTreeIter iter;
- const char *esource_uri;
+ const char *esource_uid;
if (!gtk_combo_box_get_active_iter(combo, &iter))
return;
gtk_tree_model_get(GTK_TREE_MODEL(dialog->addrbooks), &iter,
- ADDRBOOK_COLUMN_URI, &esource_uri,
- -1);
+ ADDRBOOK_COLUMN_UID, &esource_uid, -1);
- populate_treeview(dialog, esource_uri);
+ populate_treeview(dialog, esource_uid);
}
static void
@@ -455,7 +455,7 @@ gevo_add_buddy_dialog_show(PurpleAccount *account, const char *username,
G_CALLBACK(delete_win_cb), dialog);
/* Setup the vbox */
- vbox = gtk_vbox_new(FALSE, 12);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
gtk_container_add(GTK_CONTAINER(dialog->win), vbox);
gtk_widget_show(vbox);
@@ -468,7 +468,7 @@ gevo_add_buddy_dialog_show(PurpleAccount *account, const char *username,
gtk_widget_show(label);
/* Add the search hbox */
- hbox = gtk_hbox_new(FALSE, 6);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
gtk_widget_show(hbox);
@@ -551,12 +551,12 @@ gevo_add_buddy_dialog_show(PurpleAccount *account, const char *username,
/* Cool. Now we only have a little left... */
/* Separator. */
- sep = gtk_hseparator_new();
+ sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0);
gtk_widget_show(sep);
/* Button box */
- bbox = gtk_hbutton_box_new();
+ bbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
gtk_box_set_spacing(GTK_BOX(bbox), 6);
gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
diff --git a/pidgin/plugins/gevolution/assoc-buddy.c b/pidgin/plugins/gevolution/assoc-buddy.c
index cbe5f908f8..8b3dc18721 100644
--- a/pidgin/plugins/gevolution/assoc-buddy.c
+++ b/pidgin/plugins/gevolution/assoc-buddy.c
@@ -21,8 +21,9 @@
#include "internal.h"
#include "gtkblist.h"
#include "pidgin.h"
+
+#include "gtk3compat.h"
#include "gtkutils.h"
-#include "gtkimhtml.h"
#include "debug.h"
@@ -130,11 +131,10 @@ add_columns(GevoAssociateBuddyDialog *dialog)
}
static void
-populate_treeview(GevoAssociateBuddyDialog *dialog, const gchar *uri)
+populate_treeview(GevoAssociateBuddyDialog *dialog, const gchar *uid)
{
EBook *book;
EBookQuery *query;
- const char *prpl_id;
gboolean status;
GList *cards, *c;
GError *err = NULL;
@@ -154,8 +154,7 @@ populate_treeview(GevoAssociateBuddyDialog *dialog, const gchar *uri)
gtk_list_store_clear(dialog->model);
- if (!gevo_load_addressbook(uri, &book, &err))
- {
+ if (!gevo_load_addressbook(uid, &book, &err)) {
purple_debug_error("evolution",
"Error retrieving addressbook: %s\n", err->message);
g_error_free(err);
@@ -188,8 +187,6 @@ populate_treeview(GevoAssociateBuddyDialog *dialog, const gchar *uri)
return;
}
- prpl_id = purple_account_get_protocol_id(dialog->buddy->account);
-
for (c = cards; c != NULL; c = c->next)
{
EContact *contact = E_CONTACT(c->data);
@@ -207,8 +204,9 @@ populate_treeview(GevoAssociateBuddyDialog *dialog, const gchar *uri)
-1);
/* See if this user has the buddy in its list. */
- protocol_field = gevo_prpl_get_field(dialog->buddy->account,
- dialog->buddy);
+ protocol_field = gevo_prpl_get_field(
+ purple_buddy_get_account(dialog->buddy),
+ dialog->buddy);
if (protocol_field > 0)
{
@@ -218,7 +216,8 @@ populate_treeview(GevoAssociateBuddyDialog *dialog, const gchar *uri)
for (l = ims; l != NULL; l = l->next)
{
- if (!strcmp(l->data, dialog->buddy->name))
+ if (!strcmp(l->data,
+ purple_buddy_get_name(dialog->buddy)))
{
GtkTreeSelection *selection;
@@ -241,24 +240,24 @@ static void
addrbook_change_cb(GtkComboBox *combo, GevoAssociateBuddyDialog *dialog)
{
GtkTreeIter iter;
- const char *esource_uri;
+ const char *esource_uid;
if (!gtk_combo_box_get_active_iter(combo, &iter))
return;
gtk_tree_model_get(GTK_TREE_MODEL(dialog->addrbooks), &iter,
- ADDRBOOK_COLUMN_URI, &esource_uri,
- -1);
+ ADDRBOOK_COLUMN_UID, &esource_uid, -1);
- populate_treeview(dialog, esource_uri);
+ populate_treeview(dialog, esource_uid);
}
static void
new_person_cb(GtkWidget *w, GevoAssociateBuddyDialog *dialog)
{
- gevo_new_person_dialog_show(dialog->book, NULL, dialog->buddy->account,
- dialog->buddy->name, NULL, dialog->buddy,
- TRUE);
+ gevo_new_person_dialog_show(dialog->book, NULL,
+ purple_buddy_get_account(dialog->buddy),
+ purple_buddy_get_name(dialog->buddy),
+ NULL, dialog->buddy, TRUE);
delete_win_cb(NULL, NULL, dialog);
}
@@ -289,13 +288,15 @@ assoc_buddy_cb(GtkWidget *w, GevoAssociateBuddyDialog *dialog)
COLUMN_DATA, &contact,
-1);
- protocol_field = gevo_prpl_get_field(dialog->buddy->account, dialog->buddy);
+ protocol_field = gevo_prpl_get_field(
+ purple_buddy_get_account(dialog->buddy), dialog->buddy);
if (protocol_field == 0)
return; /* XXX */
list = e_contact_get(contact, protocol_field);
- list = g_list_append(list, g_strdup(dialog->buddy->name));
+ list = g_list_append(list,
+ g_strdup(purple_buddy_get_name(dialog->buddy)));
e_contact_set(contact, protocol_field, list);
@@ -319,7 +320,6 @@ gevo_associate_buddy_dialog_new(PurpleBuddy *buddy)
GtkWidget *hbox;
GtkWidget *bbox;
GtkWidget *sep;
- GtkWidget *expander;
GtkTreeSelection *selection;
GtkCellRenderer *cell;
@@ -335,7 +335,7 @@ gevo_associate_buddy_dialog_new(PurpleBuddy *buddy)
G_CALLBACK(delete_win_cb), dialog);
/* Setup the vbox */
- vbox = gtk_vbox_new(FALSE, 12);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
gtk_container_add(GTK_CONTAINER(dialog->win), vbox);
gtk_widget_show(vbox);
@@ -348,7 +348,7 @@ gevo_associate_buddy_dialog_new(PurpleBuddy *buddy)
gtk_widget_show(label);
/* Add the search hbox */
- hbox = gtk_hbox_new(FALSE, 6);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
gtk_widget_show(hbox);
@@ -419,28 +419,13 @@ gevo_associate_buddy_dialog_new(PurpleBuddy *buddy)
G_CALLBACK(addrbook_change_cb), dialog);
gtk_combo_box_set_active(GTK_COMBO_BOX(dialog->addrbooks_combo), 0);
- /* Add the expander */
- expander = gtk_expander_new_with_mnemonic(_("User _details"));
- gtk_box_pack_start(GTK_BOX(vbox), expander, FALSE, FALSE, 0);
- gtk_widget_show(expander);
-
- /*
- * User details
- */
-
- /* Textview */
- dialog->imhtml = gtk_imhtml_new(NULL, NULL);
- gtk_container_add(GTK_CONTAINER(expander),
- pidgin_make_scrollable(dialog->imhtml, GTK_POLICY_NEVER, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, -1));
- gtk_widget_show(dialog->imhtml);
-
/* Separator. */
- sep = gtk_hseparator_new();
+ sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0);
gtk_widget_show(sep);
/* Button box */
- bbox = gtk_hbutton_box_new();
+ bbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
gtk_box_set_spacing(GTK_BOX(bbox), 6);
gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
diff --git a/pidgin/plugins/gevolution/eds-utils.c b/pidgin/plugins/gevolution/eds-utils.c
index 0ecb286950..dfbdfd138d 100644
--- a/pidgin/plugins/gevolution/eds-utils.c
+++ b/pidgin/plugins/gevolution/eds-utils.c
@@ -23,7 +23,6 @@
#include "gtkblist.h"
#include "pidgin.h"
#include "gtkutils.h"
-#include "gtkimhtml.h"
#include "debug.h"
#include "gevolution.h"
@@ -52,9 +51,9 @@ gevo_addrbooks_model_unref(GtkTreeModel *model)
void
gevo_addrbooks_model_populate(GtkTreeModel *model)
{
- ESourceList *addressbooks;
+ ESourceRegistry *registry;
GError *err = NULL;
- GSList *groups, *g;
+ GList *sources, *s;
GtkTreeIter iter;
GtkListStore *list;
@@ -63,66 +62,55 @@ gevo_addrbooks_model_populate(GtkTreeModel *model)
list = GTK_LIST_STORE(model);
- if (!e_book_get_addressbooks(&addressbooks, &err))
- {
+ registry = e_source_registry_new_sync(NULL, &err);
+
+ if (!registry) {
purple_debug_error("evolution",
- "Unable to fetch list of address books.\n");
+ "Unable to fetch list of address books.");
gtk_list_store_append(list, &iter);
- gtk_list_store_set(list, &iter,
- ADDRBOOK_COLUMN_NAME, _("None"),
- ADDRBOOK_COLUMN_URI, NULL,
- -1);
+ gtk_list_store_set(list, &iter, ADDRBOOK_COLUMN_NAME, _("None"),
+ ADDRBOOK_COLUMN_UID, NULL, -1);
+ g_clear_error(&err);
return;
}
- groups = e_source_list_peek_groups(addressbooks);
+ sources = e_source_registry_list_sources(registry, E_SOURCE_EXTENSION_ADDRESS_BOOK);
- if (groups == NULL)
- {
+ if (sources == NULL) {
+ g_object_unref(registry);
gtk_list_store_append(list, &iter);
- gtk_list_store_set(list, &iter,
- ADDRBOOK_COLUMN_NAME, _("None"),
- ADDRBOOK_COLUMN_URI, NULL,
- -1);
+ gtk_list_store_set(list, &iter, ADDRBOOK_COLUMN_NAME, _("None"),
+ ADDRBOOK_COLUMN_UID, NULL, -1);
return;
}
- for (g = groups; g != NULL; g = g->next)
- {
- GSList *sources, *s;
-
- sources = e_source_group_peek_sources(g->data);
-
- for (s = sources; s != NULL; s = s->next)
- {
- ESource *source = E_SOURCE(s->data);
+ for (s = sources; s != NULL; s = s->next) {
+ ESource *source = E_SOURCE(s->data);
- g_object_ref(source);
+ g_object_ref(source);
- gtk_list_store_append(list, &iter);
- gtk_list_store_set(list, &iter,
- ADDRBOOK_COLUMN_NAME, e_source_peek_name(source),
- ADDRBOOK_COLUMN_URI, e_source_get_uri(source),
- -1);
- }
+ gtk_list_store_append(list, &iter);
+ gtk_list_store_set(list, &iter,
+ ADDRBOOK_COLUMN_NAME, e_source_get_display_name(source),
+ ADDRBOOK_COLUMN_UID, e_source_get_uid(source), -1);
}
- g_object_unref(addressbooks);
+ g_object_unref(registry);
+ g_list_free_full(sources, g_object_unref);
}
static EContact *
-gevo_run_query_in_uri(const gchar *uri, EBookQuery *query)
+gevo_run_query_in_source(ESource *source, EBookQuery *query)
{
EBook *book;
gboolean status;
GList *cards;
GError *err = NULL;
- if (!gevo_load_addressbook(uri, &book, &err))
- {
+ if (!gevo_load_addressbook_from_source(source, &book, &err)) {
purple_debug_error("evolution",
"Error retrieving addressbook: %s\n", err->message);
g_error_free(err);
@@ -172,12 +160,13 @@ gevo_run_query_in_uri(const gchar *uri, EBookQuery *query)
EContact *
gevo_search_buddy_in_contacts(PurpleBuddy *buddy, EBookQuery *query)
{
- ESourceList *addressbooks;
+ ESourceRegistry *registry;
GError *err = NULL;
EBookQuery *full_query;
- GSList *groups, *g;
+ GList *sources, *s;
EContact *result;
- EContactField protocol_field = gevo_prpl_get_field(buddy->account, buddy);
+ EContactField protocol_field =
+ gevo_prpl_get_field(purple_buddy_get_account(buddy), buddy);
if (protocol_field == 0)
return NULL;
@@ -187,7 +176,8 @@ gevo_search_buddy_in_contacts(PurpleBuddy *buddy, EBookQuery *query)
EBookQuery *queries[2];
queries[0] = query;
- queries[1] = e_book_query_field_test(protocol_field, E_BOOK_QUERY_IS, buddy->name);
+ queries[1] = e_book_query_field_test(protocol_field,
+ E_BOOK_QUERY_IS, purple_buddy_get_name(buddy));
if (queries[1] == NULL)
{
purple_debug_error("evolution", "Error in creating protocol query\n");
@@ -199,7 +189,8 @@ gevo_search_buddy_in_contacts(PurpleBuddy *buddy, EBookQuery *query)
}
else
{
- full_query = e_book_query_field_test(protocol_field, E_BOOK_QUERY_IS, buddy->name);
+ full_query = e_book_query_field_test(protocol_field,
+ E_BOOK_QUERY_IS, purple_buddy_get_name(buddy));
if (full_query == NULL)
{
purple_debug_error("evolution", "Error in creating protocol query\n");
@@ -207,8 +198,9 @@ gevo_search_buddy_in_contacts(PurpleBuddy *buddy, EBookQuery *query)
}
}
- if (!e_book_get_addressbooks(&addressbooks, &err))
- {
+ registry = e_source_registry_new_sync(NULL, &err);
+
+ if (!registry) {
purple_debug_error("evolution",
"Unable to fetch list of address books.\n");
e_book_query_unref(full_query);
@@ -217,30 +209,22 @@ gevo_search_buddy_in_contacts(PurpleBuddy *buddy, EBookQuery *query)
return NULL;
}
- groups = e_source_list_peek_groups(addressbooks);
- if (groups == NULL)
- {
- g_object_unref(addressbooks);
- e_book_query_unref(full_query);
- return NULL;
- }
-
- for (g = groups; g != NULL; g = g->next)
- {
- GSList *sources, *s;
- sources = e_source_group_peek_sources(g->data);
- for (s = sources; s != NULL; s = s->next)
- {
- result = gevo_run_query_in_uri(e_source_get_uri(E_SOURCE(s->data)), full_query);
- if (result != NULL) {
- g_object_unref(addressbooks);
- e_book_query_unref(full_query);
- return result;
- }
+ sources = e_source_registry_list_sources(registry,
+ E_SOURCE_EXTENSION_ADDRESS_BOOK);
+
+ for (s = sources; s != NULL; s = s->next) {
+ result = gevo_run_query_in_source(E_SOURCE(s->data),
+ full_query);
+ if (result != NULL) {
+ g_object_unref(registry);
+ g_list_free_full(sources, g_object_unref);
+ e_book_query_unref(full_query);
+ return result;
}
}
- g_object_unref(addressbooks);
+ g_object_unref(registry);
+ g_list_free_full(sources, g_object_unref);
e_book_query_unref(full_query);
return NULL;
}
diff --git a/pidgin/plugins/gevolution/gevo-util.c b/pidgin/plugins/gevolution/gevo-util.c
index 8f97a7df8f..68461180f6 100644
--- a/pidgin/plugins/gevolution/gevo-util.c
+++ b/pidgin/plugins/gevolution/gevo-util.c
@@ -29,32 +29,33 @@ void
gevo_add_buddy(PurpleAccount *account, const char *group_name,
const char *buddy_name, const char *alias)
{
- PurpleConversation *conv = NULL;
+ PurpleIMConversation *conv = NULL;
PurpleBuddy *buddy;
PurpleGroup *group;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy_name, account);
+ conv = purple_conversations_find_im_with_account(buddy_name, account);
- group = purple_find_group(group_name);
+ group = purple_blist_find_group(group_name);
if (group == NULL)
{
group = purple_group_new(group_name);
purple_blist_add_group(group, NULL);
}
- buddy = purple_find_buddy_in_group(account, buddy_name, group);
+ buddy = purple_blist_find_buddy_in_group(account, buddy_name, group);
if (buddy == NULL)
{
buddy = purple_buddy_new(account, buddy_name, alias);
purple_blist_add_buddy(buddy, NULL, group, NULL);
}
- purple_account_add_buddy(account, buddy);
+ purple_account_add_buddy(account, buddy, NULL);
if (conv != NULL)
{
- purple_buddy_icon_update(purple_conv_im_get_icon(PURPLE_CONV_IM(conv)));
- purple_conversation_update(conv, PURPLE_CONV_UPDATE_ADD);
+ purple_buddy_icon_update(purple_im_conversation_get_icon(conv));
+ purple_conversation_update(PURPLE_CONVERSATION(conv),
+ PURPLE_CONVERSATION_UPDATE_ADD);
}
}
@@ -68,20 +69,19 @@ gevo_get_groups(void)
g_list_free(list);
list = NULL;
- if (purple_get_blist()->root == NULL)
- {
- list = g_list_append(list, (gpointer)_("Buddies"));
- }
- else
- {
- for (gnode = purple_get_blist()->root;
+ if (purple_blist_get_buddy_list()->root == 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)
{
- if (PURPLE_BLIST_NODE_IS_GROUP(gnode))
+ if (PURPLE_IS_GROUP(gnode))
{
- g = (PurpleGroup *)gnode;
- list = g_list_append(list, g->name);
+ g = PURPLE_GROUP(gnode);
+ list = g_list_append(list,
+ (gpointer)purple_group_get_name(g));
}
}
}
@@ -124,16 +124,39 @@ gevo_prpl_is_supported(PurpleAccount *account, PurpleBuddy *buddy)
}
gboolean
-gevo_load_addressbook(const gchar* uri, EBook **book, GError **error)
+gevo_load_addressbook(const gchar* uid, EBook **book, GError **error)
{
gboolean result = FALSE;
+ ESourceRegistry *registry;
+ ESource *source;
g_return_val_if_fail(book != NULL, FALSE);
- if (uri == NULL)
- *book = e_book_new_system_addressbook(error);
+ registry = e_source_registry_new_sync(NULL, error);
+
+ if (!registry)
+ return FALSE;
+
+ if (uid == NULL)
+ source = e_source_registry_ref_default_address_book(registry);
else
- *book = e_book_new_from_uri(uri, error);
+ source = e_source_registry_ref_source(registry, uid);
+
+ g_object_unref(registry);
+
+ result = gevo_load_addressbook_from_source(source, book, error);
+
+ g_object_unref(source);
+
+ return result;
+}
+
+gboolean
+gevo_load_addressbook_from_source(ESource *source, EBook **book, GError **error)
+{
+ gboolean result = FALSE;
+
+ *book = e_book_new(source, error);
if (*book == NULL)
return FALSE;
diff --git a/pidgin/plugins/gevolution/gevolution.c b/pidgin/plugins/gevolution/gevolution.c
index 9398393b23..f4dda80a02 100644
--- a/pidgin/plugins/gevolution/gevolution.c
+++ b/pidgin/plugins/gevolution/gevolution.c
@@ -29,6 +29,7 @@
#include "util.h"
#include "version.h"
+#include "gtk3compat.h"
#include "gtkblist.h"
#include "gtkconv.h"
#include "gtkplugin.h"
@@ -99,11 +100,12 @@ update_ims_from_contact(EContact *contact, const char *name,
me = g_strdup(purple_normalize(account, purple_account_get_username(account)));
for (l2 = ims; l2 != NULL; l2 = l2->next)
{
- if (purple_find_buddy(account, l2->data) != NULL ||
+ if (purple_blist_find_buddy(account, l2->data) != NULL ||
!strcmp(me, purple_normalize(account, l2->data)))
continue;
- gevo_add_buddy(account, _("Buddies"), l2->data, name);
+ gevo_add_buddy(account, PURPLE_BLIST_DEFAULT_GROUP_NAME,
+ l2->data, name);
}
g_free(me);
}
@@ -220,14 +222,14 @@ signed_on_cb(PurpleConnection *gc)
static void
menu_item_activate_cb(PurpleBlistNode *node, gpointer user_data)
{
- PurpleBuddy *buddy = (PurpleBuddy *)node;
+ PurpleBuddy *buddy = PURPLE_BUDDY(node);
gevo_associate_buddy_dialog_new(buddy);
}
static void
menu_item_send_mail_activate_cb(PurpleBlistNode *node, gpointer user_data)
{
- PurpleBuddy *buddy = (PurpleBuddy *)node;
+ PurpleBuddy *buddy = PURPLE_BUDDY(node);
char *mail = NULL;
mail = gevo_get_email_for_buddy(buddy);
@@ -242,20 +244,24 @@ menu_item_send_mail_activate_cb(PurpleBlistNode *node, gpointer user_data)
g_free(app);
g_free(mail);
- g_spawn_command_line_async(command_line, NULL);
+ if (!g_spawn_command_line_async(command_line, NULL)) {
+ purple_debug_error("gevolution",
+ "Failed executing mailto command");
+ }
g_free(command_line);
g_free(quoted);
}
else
{
- purple_notify_error(NULL, NULL, _("Unable to send email"),
- _("The evolution executable was not found in the PATH."));
+ purple_notify_error(NULL, NULL, _("Unable to send "
+ "email"), _("The evolution executable was not "
+ "found in the PATH."), NULL);
}
}
else
{
purple_notify_error(NULL, NULL, _("Unable to send email"),
- _("An email address was not found for this buddy."));
+ _("An email address was not found for this buddy."), NULL);
}
}
@@ -268,10 +274,10 @@ blist_node_extended_menu_cb(PurpleBlistNode *node, GList **menu)
EContact *contact;
char *mail;
- if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
+ if (!PURPLE_IS_BUDDY(node))
return;
- buddy = (PurpleBuddy *)node;
+ buddy = PURPLE_BUDDY(node);
account = purple_buddy_get_account(buddy);
if (!gevo_prpl_is_supported(account, buddy))
@@ -425,7 +431,7 @@ get_config_frame(PurplePlugin *plugin)
GList *l;
/* Outside container */
- ret = gtk_vbox_new(FALSE, 18);
+ ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
gtk_container_set_border_width(GTK_CONTAINER(ret), 12);
/* Configuration frame */
@@ -515,7 +521,6 @@ get_config_frame(PurplePlugin *plugin)
static PidginPluginUiInfo ui_info =
{
get_config_frame, /**< get_config_frame */
- 0, /**< page_num */
/* Padding */
NULL,
NULL,
diff --git a/pidgin/plugins/gevolution/gevolution.h b/pidgin/plugins/gevolution/gevolution.h
index 1b92b2b940..a76b1ccbf0 100644
--- a/pidgin/plugins/gevolution/gevolution.h
+++ b/pidgin/plugins/gevolution/gevolution.h
@@ -25,12 +25,12 @@
# include "config.h"
#endif
-#include <libebook/e-book.h>
+#include <libebook/libebook.h>
enum
{
ADDRBOOK_COLUMN_NAME,
- ADDRBOOK_COLUMN_URI,
+ ADDRBOOK_COLUMN_UID,
NUM_ADDRBOOK_COLUMNS
};
@@ -97,7 +97,6 @@ typedef struct
GtkWidget *addrbooks_combo;
GtkWidget *search_field;
GtkWidget *assoc_button;
- GtkWidget *imhtml;
GtkListStore *model;
GtkTreeModel *addrbooks;
@@ -123,7 +122,9 @@ GList *gevo_get_groups(void);
EContactField gevo_prpl_get_field(PurpleAccount *account, PurpleBuddy *buddy);
gboolean gevo_prpl_is_supported(PurpleAccount *account, PurpleBuddy *buddy);
-gboolean gevo_load_addressbook(const gchar *uri, EBook **book, GError **error);
+gboolean gevo_load_addressbook(const gchar *uid, EBook **book, GError **error);
+gboolean gevo_load_addressbook_from_source(ESource *source, EBook **book,
+ GError **error);
char *gevo_get_email_for_buddy(PurpleBuddy *buddy);
GevoAssociateBuddyDialog *gevo_associate_buddy_dialog_new(PurpleBuddy *buddy);
diff --git a/pidgin/plugins/gevolution/new_person_dialog.c b/pidgin/plugins/gevolution/new_person_dialog.c
index 30682f0ce1..f600b678fa 100644
--- a/pidgin/plugins/gevolution/new_person_dialog.c
+++ b/pidgin/plugins/gevolution/new_person_dialog.c
@@ -20,6 +20,8 @@
*/
#include "internal.h"
#include "pidgin.h"
+
+#include "gtk3compat.h"
#include "gtkutils.h"
#include "debug.h"
@@ -33,7 +35,7 @@ add_pref_box(GtkSizeGroup *sg, GtkWidget *parent, const char *text,
GtkWidget *hbox;
GtkWidget *label;
- hbox = gtk_hbox_new(FALSE, 6);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 0);
gtk_widget_show(hbox);
@@ -96,7 +98,7 @@ add_cb(GtkWidget *w, GevoNewPersonDialog *dialog)
char *full_name = NULL;
if (dialog->person_only)
- username = dialog->buddy->name;
+ username = purple_buddy_get_name(dialog->buddy);
else
username = gtk_entry_get_text(GTK_ENTRY(dialog->username));
@@ -249,7 +251,7 @@ gevo_new_person_dialog_show(EBook *book, EContact *contact,
G_CALLBACK(delete_win_cb), dialog);
/* Setup the vbox */
- vbox = gtk_vbox_new(FALSE, 12);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
gtk_container_add(GTK_CONTAINER(dialog->win), vbox);
gtk_widget_show(vbox);
@@ -300,7 +302,7 @@ gevo_new_person_dialog_show(EBook *book, EContact *contact,
gtk_widget_show_all(dialog->group_combo);
/* Separator */
- sep = gtk_hseparator_new();
+ sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0);
gtk_widget_show(sep);
@@ -312,7 +314,7 @@ gevo_new_person_dialog_show(EBook *book, EContact *contact,
}
/* Create the parent hbox for this whole thing. */
- hbox = gtk_hbox_new(FALSE, 12);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
gtk_widget_show(hbox);
@@ -334,7 +336,7 @@ gevo_new_person_dialog_show(EBook *book, EContact *contact,
#endif
/* Now the right side. */
- vbox2 = gtk_vbox_new(FALSE, 12);
+ vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0);
gtk_widget_show(vbox2);
@@ -383,12 +385,12 @@ gevo_new_person_dialog_show(EBook *book, EContact *contact,
}
/* Separator */
- sep = gtk_hseparator_new();
+ sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0);
gtk_widget_show(sep);
/* Button box */
- bbox = gtk_hbutton_box_new();
+ bbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
gtk_box_set_spacing(GTK_BOX(bbox), 6);
gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
diff --git a/pidgin/plugins/gtk-signals-test.c b/pidgin/plugins/gtk-signals-test.c
index a1cf84bf25..121f7c2419 100644
--- a/pidgin/plugins/gtk-signals-test.c
+++ b/pidgin/plugins/gtk-signals-test.c
@@ -57,44 +57,44 @@ blist_drawing_tooltip_cb(PurpleBlistNode *node, GString *str, gboolean full, voi
* Conversation subsystem signal callbacks
**************************************************************************/
static void
-conversation_dragging_cb(PidginWindow *source, PidginWindow *destination) {
+conversation_dragging_cb(PidginConvWindow *source, PidginConvWindow *destination) {
purple_debug_info("gtk-signal-test", "conversation dragging cb\n");
}
static gboolean
-displaying_im_msg_cb(PurpleAccount *account, const char *who, char **buffer,
- PurpleConversation *conv, PurpleMessageFlags flags, void *data)
+displaying_im_msg_cb(PurpleConversation *conv, PurpleMessage *pmsg, gpointer data)
{
purple_debug_misc("gtk-signals test", "displaying-im-msg (%s, %s)\n",
- purple_conversation_get_name(conv), *buffer);
+ purple_conversation_get_name(conv),
+ purple_message_get_contents(pmsg));
return FALSE;
}
static void
-displayed_im_msg_cb(PurpleAccount *account, const char *who, const char *buffer,
- PurpleConversation *conv, PurpleMessageFlags flags, void *data)
+displayed_im_msg_cb(PurpleConversation *conv, PurpleMessage *msg, gpointer data)
{
purple_debug_misc("gtk-signals test", "displayed-im-msg (%s, %s)\n",
- purple_conversation_get_name(conv), buffer);
+ purple_conversation_get_name(conv),
+ purple_message_get_contents(msg));
}
static gboolean
-displaying_chat_msg_cb(PurpleAccount *account, const char *who, char **buffer,
- PurpleConversation *conv, PurpleMessageFlags flags, void *data)
+displaying_chat_msg_cb(PurpleConversation *conv, PurpleMessage *pmsg, gpointer data)
{
purple_debug_misc("gtk-signals test", "displaying-chat-msg (%s, %s)\n",
- purple_conversation_get_name(conv), *buffer);
+ purple_conversation_get_name(conv),
+ purple_message_get_contents(pmsg));
return FALSE;
}
static void
-displayed_chat_msg_cb(PurpleAccount *account, const char *who, const char *buffer,
- PurpleConversation *conv, PurpleMessageFlags flags, void *data)
+displayed_chat_msg_cb(PurpleConversation *conv, PurpleMessage *msg, gpointer data)
{
purple_debug_misc("gtk-signals test", "displayed-chat-msg (%s, %s)\n",
- purple_conversation_get_name(conv), buffer);
+ purple_conversation_get_name(conv),
+ purple_message_get_contents(msg));
}
static void
@@ -110,7 +110,7 @@ conversation_switched_cb(PurpleConversation *conv, void *data)
static gboolean
plugin_load(PurplePlugin *plugin)
{
- void *accounts_handle = pidgin_account_get_handle();
+ void *accounts_handle = pidgin_accounts_get_handle();
void *blist_handle = pidgin_blist_get_handle();
void *conv_handle = pidgin_conversations_get_handle();
diff --git a/pidgin/plugins/history.c b/pidgin/plugins/history.c
index 3d2507343b..9d20416c50 100644
--- a/pidgin/plugins/history.c
+++ b/pidgin/plugins/history.c
@@ -14,18 +14,18 @@
#include "version.h"
#include "gtkconv.h"
-#include "gtkimhtml.h"
#include "gtkplugin.h"
+#include "gtkwebview.h"
#define HISTORY_PLUGIN_ID "gtk-history"
#define HISTORY_SIZE (4 * 1024)
-static gboolean _scroll_imhtml_to_end(gpointer data)
+static gboolean _scroll_webview_to_end(gpointer data)
{
- GtkIMHtml *imhtml = data;
- gtk_imhtml_scroll_to_end(GTK_IMHTML(imhtml), FALSE);
- g_object_unref(G_OBJECT(imhtml));
+ PidginWebView *webview = data;
+ pidgin_webview_scroll_to_end(PIDGIN_WEBVIEW(webview), FALSE);
+ g_object_unref(G_OBJECT(webview));
return FALSE;
}
@@ -33,25 +33,29 @@ static void historize(PurpleConversation *c)
{
PurpleAccount *account = purple_conversation_get_account(c);
const char *name = purple_conversation_get_name(c);
- PurpleConversationType convtype;
GList *logs = NULL;
const char *alias = name;
guint flags;
char *history;
PidginConversation *gtkconv;
+#if 0
+ /* FIXME: WebView has no options */
GtkIMHtmlOptions options = GTK_IMHTML_NO_COLOURS;
+#endif
char *header;
+#if 0
+ /* FIXME: WebView has no protocol setting */
char *protocol;
+#endif
char *escaped_alias;
const char *header_date;
- convtype = purple_conversation_get_type(c);
gtkconv = PIDGIN_CONVERSATION(c);
g_return_if_fail(gtkconv != NULL);
/* An IM which is the first active conversation. */
g_return_if_fail(gtkconv->convs != NULL);
- if (convtype == PURPLE_CONV_TYPE_IM && !gtkconv->convs->next)
+ if (PURPLE_IS_IM_CONVERSATION(c) && !gtkconv->convs->next)
{
GSList *buddies;
GSList *cur;
@@ -62,11 +66,11 @@ static void historize(PurpleConversation *c)
return;
/* Find buddies for this conversation. */
- buddies = purple_find_buddies(account, name);
+ buddies = purple_blist_find_buddies(account, name);
/* If we found at least one buddy, save the first buddy's alias. */
if (buddies != NULL)
- alias = purple_buddy_get_contact_alias((PurpleBuddy *)buddies->data);
+ alias = purple_buddy_get_contact_alias(PURPLE_BUDDY(buddies->data));
for (cur = buddies; cur != NULL; cur = cur->next)
{
@@ -79,7 +83,7 @@ static void historize(PurpleConversation *c)
PurpleBlistNode *parent = purple_blist_node_get_parent(node);
PurpleBlistNode *child = purple_blist_node_get_first_child(parent);
- alias = purple_buddy_get_contact_alias((PurpleBuddy *)node);
+ alias = purple_buddy_get_contact_alias(PURPLE_BUDDY(node));
/* We've found a buddy that matches this conversation. It's part of a
* PurpleContact with more than one PurpleBuddy. Loop through the PurpleBuddies
@@ -87,8 +91,8 @@ static void historize(PurpleConversation *c)
for (node2 = child ; node2 != NULL ; node2 = purple_blist_node_get_sibling_next(node2))
{
logs = g_list_concat(purple_log_get_logs(PURPLE_LOG_IM,
- purple_buddy_get_name((PurpleBuddy *)node2),
- purple_buddy_get_account((PurpleBuddy *)node2)),
+ purple_buddy_get_name(PURPLE_BUDDY(node2)),
+ purple_buddy_get_account(PURPLE_BUDDY(node2))),
logs);
}
break;
@@ -101,7 +105,7 @@ static void historize(PurpleConversation *c)
else
logs = g_list_sort(logs, purple_log_compare);
}
- else if (convtype == PURPLE_CONV_TYPE_CHAT)
+ else if (PURPLE_IS_CHAT_CONVERSATION(c))
{
/* If we're not logging, don't show anything.
* Otherwise, we might show a very old log. */
@@ -116,15 +120,24 @@ static void historize(PurpleConversation *c)
history = purple_log_read((PurpleLog*)logs->data, &flags);
gtkconv = PIDGIN_CONVERSATION(c);
+#if 0
+ /* FIXME: WebView has no options */
if (flags & PURPLE_LOG_READ_NO_NEWLINE)
options |= GTK_IMHTML_NO_NEWLINE;
+#endif
+#if 0
+ /* FIXME: WebView has no protocol setting */
protocol = g_strdup(gtk_imhtml_get_protocol_name(GTK_IMHTML(gtkconv->imhtml)));
gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml),
purple_account_get_protocol_name(((PurpleLog*)logs->data)->account));
+#endif
- if (gtk_text_buffer_get_char_count(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml))))
- gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR>", options);
+#if 0
+ /* TODO WebKit: Do this properly... */
+ if (!pidgin_webview_is_empty(PIDGIN_WEBVIEW(gtkconv->webview)))
+ pidgin_webview_append_html(PIDGIN_WEBVIEW(gtkconv->webview), "<BR>");
+#endif
escaped_alias = g_markup_escape_text(alias, -1);
@@ -134,21 +147,24 @@ static void historize(PurpleConversation *c)
header_date = purple_date_format_full(localtime(&((PurpleLog *)logs->data)->time));
header = g_strdup_printf(_("<b>Conversation with %s on %s:</b><br>"), escaped_alias, header_date);
- gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), header, options);
+ pidgin_webview_append_html(PIDGIN_WEBVIEW(gtkconv->webview), header);
g_free(header);
g_free(escaped_alias);
g_strchomp(history);
- gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), history, options);
+ pidgin_webview_append_html(PIDGIN_WEBVIEW(gtkconv->webview), history);
g_free(history);
- gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<hr>", options);
+ pidgin_webview_append_html(PIDGIN_WEBVIEW(gtkconv->webview), "<hr>");
+#if 0
+ /* FIXME: WebView has no protocol setting */
gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml), protocol);
g_free(protocol);
+#endif
- g_object_ref(G_OBJECT(gtkconv->imhtml));
- g_idle_add(_scroll_imhtml_to_end, gtkconv->imhtml);
+ 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);
@@ -163,7 +179,7 @@ history_prefs_check(PurplePlugin *plugin)
purple_notify_warning(plugin, NULL, _("History Plugin Requires Logging"),
_("Logging can be enabled from Tools -> Preferences -> Logging.\n\n"
"Enabling logs for instant messages and/or chats will activate "
- "history for the same conversation type(s)."));
+ "history for the same conversation type(s)."), NULL);
}
}
diff --git a/pidgin/plugins/iconaway.c b/pidgin/plugins/iconaway.c
index e372181363..5fa395795b 100644
--- a/pidgin/plugins/iconaway.c
+++ b/pidgin/plugins/iconaway.c
@@ -34,7 +34,7 @@ static void
iconify_windows(PurpleAccount *account, PurpleStatus *old, PurpleStatus *newstatus)
{
PurplePresence *presence;
- PidginWindow *win;
+ PidginConvWindow *win;
GList *windows;
presence = purple_status_get_presence(newstatus);
@@ -48,7 +48,7 @@ iconify_windows(PurpleAccount *account, PurpleStatus *old, PurpleStatus *newstat
windows != NULL;
windows = windows->next) {
- win = (PidginWindow *)windows->data;
+ win = (PidginConvWindow *)windows->data;
gtk_window_iconify(GTK_WINDOW(win->window));
}
diff --git a/pidgin/plugins/imgupload.c b/pidgin/plugins/imgupload.c
new file mode 100644
index 0000000000..abd407e46c
--- /dev/null
+++ b/pidgin/plugins/imgupload.c
@@ -0,0 +1,483 @@
+/*
+ * Image Uploader - an inline images implementation for protocols without
+ * support for such feature.
+ *
+ * Copyright (C) 2014, Tomasz Wasilczyk <twasilczyk@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
+ */
+
+#include "internal.h"
+
+#include "debug.h"
+#include "glibcompat.h"
+#include "http.h"
+#include "version.h"
+
+#include "gtk3compat.h"
+#include "gtkconv.h"
+#include "gtkplugin.h"
+#include "gtkutils.h"
+#include "gtkwebviewtoolbar.h"
+#include "pidginstock.h"
+
+#include <json-glib/json-glib.h>
+
+#define IMGUP_IMGUR_CLIENT_ID "b6d33c6bb80e1b6"
+#define IMGUP_PREF_PREFIX "/plugins/gtk/imgupload/"
+
+static PurplePlugin *plugin_handle = NULL;
+
+static void
+imgup_upload_done(PidginWebView *webview, const gchar *url, const gchar *title);
+static void
+imgup_upload_failed(PidginWebView *webview);
+
+
+/******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+
+static gboolean
+imgup_conn_is_hooked(PurpleConnection *gc)
+{
+ return GPOINTER_TO_INT(g_object_get_data(G_OBJECT(gc), "imgupload-set"));
+}
+
+
+/******************************************************************************
+ * Imgur implementation
+ ******************************************************************************/
+
+static void
+imgup_imgur_uploaded(PurpleHttpConnection *hc, PurpleHttpResponse *resp,
+ gpointer _webview)
+{
+ JsonParser *parser;
+ JsonObject *result;
+ const gchar *data;
+ gsize data_len;
+ PidginWebView *webview = PIDGIN_WEBVIEW(_webview);
+ const gchar *url, *title;
+
+ if (!purple_http_response_is_successful(resp)) {
+ imgup_upload_failed(webview);
+ return;
+ }
+
+ data = purple_http_response_get_data(resp, &data_len);
+ parser = json_parser_new();
+ if (!json_parser_load_from_data(parser, data, data_len, NULL)) {
+ purple_debug_warning("imgupload", "Invalid json got from imgur");
+
+ imgup_upload_failed(webview);
+ return;
+ }
+
+ result = json_node_get_object(json_parser_get_root(parser));
+
+ if (!json_object_get_boolean_member(result, "success")) {
+ g_object_unref(parser);
+
+ purple_debug_warning("imgupload", "imgur - not a success");
+
+ imgup_upload_failed(webview);
+ return;
+ }
+
+ result = json_object_get_object_member(result, "data");
+ url = json_object_get_string_member(result, "link");
+
+ title = g_object_get_data(G_OBJECT(webview), "imgupload-imgur-name");
+
+ imgup_upload_done(webview, url, title);
+
+ g_object_unref(parser);
+ g_object_set_data(G_OBJECT(webview), "imgupload-imgur-name", NULL);
+}
+
+static PurpleHttpConnection *
+imgup_imgur_upload(PidginWebView *webview, PurpleImage *image)
+{
+ PurpleHttpRequest *req;
+ PurpleHttpConnection *hc;
+ gchar *req_data, *img_data, *img_data_e;
+
+ req = purple_http_request_new("https://api.imgur.com/3/image");
+ purple_http_request_set_method(req, "POST");
+ purple_http_request_header_set(req, "Authorization",
+ "Client-ID " IMGUP_IMGUR_CLIENT_ID);
+
+ /* TODO: make it a plain, multipart/form-data request */
+ img_data = purple_base64_encode(purple_image_get_data(image),
+ purple_image_get_size(image));
+ img_data_e = g_uri_escape_string(img_data, NULL, FALSE);
+ g_free(img_data);
+ req_data = g_strdup_printf("type=base64&image=%s", img_data_e);
+ g_free(img_data_e);
+
+ purple_http_request_header_set(req, "Content-Type",
+ "application/x-www-form-urlencoded");
+ purple_http_request_set_contents(req, req_data, -1);
+ g_free(req_data);
+
+ /* TODO: set it to hc, not webview (after gobjectifying it) */
+ g_object_set_data_full(G_OBJECT(webview), "imgupload-imgur-name",
+ g_strdup(purple_image_get_friendly_filename(image)), g_free);
+
+ hc = purple_http_request(NULL, req, imgup_imgur_uploaded, webview);
+ purple_http_request_unref(req);
+
+ return hc;
+}
+
+/******************************************************************************
+ * Image/link upload and insertion
+ ******************************************************************************/
+
+static void
+imgup_upload_finish(PidginWebView *webview)
+{
+ gpointer plswait;
+
+ g_object_steal_data(G_OBJECT(webview), "imgupload-hc");
+ plswait = g_object_get_data(G_OBJECT(webview), "imgupload-plswait");
+ g_object_set_data(G_OBJECT(webview), "imgupload-plswait", NULL);
+
+ if (plswait)
+ purple_request_close(PURPLE_REQUEST_WAIT, plswait);
+}
+
+static void
+imgup_upload_done(PidginWebView *webview, const gchar *url, const gchar *title)
+{
+ gboolean url_desc;
+
+ imgup_upload_finish(webview);
+
+ if (!purple_prefs_get_bool(IMGUP_PREF_PREFIX "use_url_desc"))
+ url_desc = FALSE;
+ else {
+ PidginWebViewButtons format;
+
+ format = pidgin_webview_get_format_functions(webview);
+ url_desc = format & PIDGIN_WEBVIEW_LINKDESC;
+ }
+
+ pidgin_webview_insert_link(webview, url, url_desc ? title : NULL);
+}
+
+static void
+imgup_upload_failed(PidginWebView *webview)
+{
+ gboolean is_cancelled;
+
+ imgup_upload_finish(webview);
+
+ is_cancelled = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(webview),
+ "imgupload-cancelled"));
+ g_object_set_data(G_OBJECT(webview), "imgupload-cancelled", NULL);
+
+ if (!is_cancelled)
+ purple_debug_error("imgupload", "Failed uploading image");
+}
+
+static void
+imgup_upload_cancel(gpointer _webview)
+{
+ PurpleHttpConnection *hc;
+ PidginWebView *webview = PIDGIN_WEBVIEW(_webview);
+
+ g_object_set_data(G_OBJECT(webview), "imgupload-plswait", NULL);
+ g_object_set_data(G_OBJECT(webview), "imgupload-cancelled",
+ GINT_TO_POINTER(TRUE));
+ hc = g_object_get_data(G_OBJECT(webview), "imgupload-hc");
+ if (hc)
+ purple_http_conn_cancel(hc);
+}
+
+static gboolean
+imgup_upload_start(PidginWebView *webview, PurpleImage *image, gpointer _gtkconv)
+{
+ PidginConversation *gtkconv = _gtkconv;
+ PurpleConversation *conv = gtkconv->active_conv;
+ PurpleHttpConnection *hc;
+ gpointer plswait;
+
+ if (!imgup_conn_is_hooked(purple_conversation_get_connection(conv)))
+ return FALSE;
+
+ hc = imgup_imgur_upload(webview, image);
+ g_object_set_data_full(G_OBJECT(webview), "imgupload-hc",
+ hc, (GDestroyNotify)purple_http_conn_cancel);
+
+ plswait = purple_request_wait(plugin_handle, _("Uploading image"),
+ _("Please wait for image URL being retrieved..."),
+ NULL, FALSE, imgup_upload_cancel,
+ purple_request_cpar_from_conversation(conv), webview);
+ g_object_set_data(G_OBJECT(webview), "imgupload-plswait", plswait);
+
+ return TRUE;
+}
+
+
+/******************************************************************************
+ * Setup/cleanup
+ ******************************************************************************/
+
+static void
+imgup_pidconv_init(PidginConversation *gtkconv)
+{
+ PidginWebView *webview;
+
+ webview = PIDGIN_WEBVIEW(gtkconv->entry);
+
+ g_signal_connect(G_OBJECT(webview), "insert-image",
+ G_CALLBACK(imgup_upload_start), gtkconv);
+}
+
+static void
+imgup_pidconv_uninit(PidginConversation *gtkconv)
+{
+ PidginWebView *webview;
+
+ webview = PIDGIN_WEBVIEW(gtkconv->entry);
+
+ g_signal_handlers_disconnect_by_func(G_OBJECT(webview),
+ G_CALLBACK(imgup_upload_start), gtkconv);
+}
+
+static void
+imgup_conv_init(PurpleConversation *conv)
+{
+ PurpleConnection *gc;
+
+ gc = purple_conversation_get_connection(conv);
+ if (!gc)
+ return;
+ if (!imgup_conn_is_hooked(gc))
+ return;
+
+ purple_conversation_set_features(conv,
+ purple_conversation_get_features(conv) &
+ ~PURPLE_CONNECTION_FLAG_NO_IMAGES);
+
+ g_object_set_data(G_OBJECT(conv), "imgupload-set", GINT_TO_POINTER(TRUE));
+}
+
+static void
+imgup_conv_uninit(PurpleConversation *conv)
+{
+ PurpleConnection *gc;
+
+ gc = purple_conversation_get_connection(conv);
+ if (gc) {
+ if (!imgup_conn_is_hooked(gc))
+ return;
+ } else {
+ if (!g_object_get_data(G_OBJECT(conv), "imgupload-set"))
+ return;
+ }
+
+ purple_conversation_set_features(conv,
+ purple_conversation_get_features(conv) |
+ PURPLE_CONNECTION_FLAG_NO_IMAGES);
+
+ g_object_set_data(G_OBJECT(conv), "imgupload-set", NULL);
+}
+
+static void
+imgup_conn_init(PurpleConnection *gc)
+{
+ PurpleConnectionFlags flags;
+
+ flags = purple_connection_get_flags(gc);
+
+ if (!(flags & PURPLE_CONNECTION_FLAG_NO_IMAGES))
+ return;
+
+ flags &= ~PURPLE_CONNECTION_FLAG_NO_IMAGES;
+ purple_connection_set_flags(gc, flags);
+
+ g_object_set_data(G_OBJECT(gc), "imgupload-set", GINT_TO_POINTER(TRUE));
+}
+
+static void
+imgup_conn_uninit(PurpleConnection *gc)
+{
+ if (!imgup_conn_is_hooked(gc))
+ return;
+
+ purple_connection_set_flags(gc, purple_connection_get_flags(gc) |
+ PURPLE_CONNECTION_FLAG_NO_IMAGES);
+
+ g_object_set_data(G_OBJECT(gc), "imgupload-set", NULL);
+}
+
+static gboolean
+imgup_plugin_load(PurplePlugin *plugin)
+{
+ GList *it;
+
+ plugin_handle = plugin;
+
+ it = purple_connections_get_all();
+ for (; it; it = g_list_next(it)) {
+ PurpleConnection *gc = it->data;
+ imgup_conn_init(gc);
+ }
+
+ it = purple_conversations_get_all();
+ for (; it; it = g_list_next(it)) {
+ PurpleConversation *conv = it->data;
+ imgup_conv_init(conv);
+ if (PIDGIN_IS_PIDGIN_CONVERSATION(conv))
+ imgup_pidconv_init(PIDGIN_CONVERSATION(conv));
+ }
+
+ purple_signal_connect(purple_connections_get_handle(),
+ "signed-on", plugin,
+ PURPLE_CALLBACK(imgup_conn_init), NULL);
+ purple_signal_connect(purple_connections_get_handle(),
+ "signing-off", plugin,
+ PURPLE_CALLBACK(imgup_conn_uninit), NULL);
+ purple_signal_connect(pidgin_conversations_get_handle(),
+ "conversation-displayed", plugin,
+ PURPLE_CALLBACK(imgup_pidconv_init), NULL);
+
+ return TRUE;
+}
+
+static gboolean
+imgup_plugin_unload(PurplePlugin *plugin)
+{
+ GList *it;
+
+ it = purple_conversations_get_all();
+ for (; it; it = g_list_next(it)) {
+ PurpleConversation *conv = it->data;
+ imgup_conv_uninit(conv);
+ if (PIDGIN_IS_PIDGIN_CONVERSATION(conv))
+ imgup_pidconv_uninit(PIDGIN_CONVERSATION(conv));
+ }
+
+ it = purple_connections_get_all();
+ for (; it; it = g_list_next(it)) {
+ PurpleConnection *gc = it->data;
+ imgup_conn_uninit(gc);
+ }
+
+ plugin_handle = NULL;
+
+ return TRUE;
+}
+
+/******************************************************************************
+ * Prefs
+ ******************************************************************************/
+
+static void
+imgup_prefs_ok(gpointer _unused, PurpleRequestFields *fields)
+{
+ gboolean use_url_desc;
+
+ use_url_desc = purple_request_fields_get_bool(fields, "use_url_desc");
+
+ purple_prefs_set_bool(IMGUP_PREF_PREFIX "use_url_desc", use_url_desc);
+}
+
+static gpointer
+imgup_prefs_get(PurplePlugin *plugin)
+{
+ PurpleRequestCommonParameters *cpar;
+ PurpleRequestFields *fields;
+ PurpleRequestFieldGroup *group;
+ PurpleRequestField *field;
+ gpointer handle;
+
+ fields = purple_request_fields_new();
+ group = purple_request_field_group_new(NULL);
+ purple_request_fields_add_group(fields, group);
+
+ field = purple_request_field_bool_new("use_url_desc",
+ _("Use image filename as link description"),
+ purple_prefs_get_bool(IMGUP_PREF_PREFIX "use_url_desc"));
+ purple_request_field_group_add_field(group, field);
+
+ cpar = purple_request_cpar_new();
+ purple_request_cpar_set_icon(cpar, PURPLE_REQUEST_ICON_DIALOG);
+
+ handle = purple_request_fields(plugin,
+ _("Image Uploader"), NULL, NULL, fields,
+ _("OK"), (GCallback)imgup_prefs_ok,
+ _("Cancel"), NULL,
+ cpar, NULL);
+
+ return handle;
+}
+
+/******************************************************************************
+ * Plugin stuff
+ ******************************************************************************/
+
+static PurplePluginUiInfo imgup_prefs_info = {
+ NULL,
+ imgup_prefs_get,
+
+ /* padding */
+ NULL, NULL, NULL, NULL
+};
+
+static PurplePluginInfo imgup_info =
+{
+ PURPLE_PLUGIN_MAGIC,
+ PURPLE_MAJOR_VERSION,
+ PURPLE_MINOR_VERSION,
+ PURPLE_PLUGIN_STANDARD,
+ PIDGIN_PLUGIN_TYPE,
+ 0,
+ NULL,
+ PURPLE_PRIORITY_DEFAULT,
+ "gtk-imgupload",
+ N_("Image Uploader"),
+ DISPLAY_VERSION,
+ N_("Inline images implementation for protocols without such feature."),
+ N_("Adds inline images support for protocols lacking this feature by "
+ "uploading them to the external service."),
+ "Tomasz Wasilczyk <twasilczyk@pidgin.im>",
+ PURPLE_WEBSITE,
+ imgup_plugin_load,
+ imgup_plugin_unload,
+ NULL,
+ NULL,
+ NULL,
+ &imgup_prefs_info,
+ NULL,
+
+ /* padding */
+ NULL, NULL, NULL, NULL
+};
+
+static void
+imgup_init_plugin(PurplePlugin *plugin)
+{
+ purple_prefs_add_none("/plugins");
+ purple_prefs_add_none("/plugins/gtk");
+ purple_prefs_add_none("/plugins/gtk/imgupload");
+
+ purple_prefs_add_bool(IMGUP_PREF_PREFIX "use_url_desc", TRUE);
+}
+
+PURPLE_INIT_PLUGIN(imgupload, imgup_init_plugin, imgup_info)
diff --git a/pidgin/plugins/mailchk.c b/pidgin/plugins/mailchk.c
index cd0b677034..c9bf0e9f55 100644
--- a/pidgin/plugins/mailchk.c
+++ b/pidgin/plugins/mailchk.c
@@ -22,21 +22,21 @@ check_mail()
static off_t oldsize = 0;
gchar *filename;
off_t newsize;
- struct stat s;
+ GStatBuf st;
gint ret = 0;
filename = g_strdup(g_getenv("MAIL"));
if (!filename)
filename = g_strconcat("/var/spool/mail/", g_get_user_name(), NULL);
- if (g_stat(filename, &s) < 0) {
+ if (g_stat(filename, &st) < 0) {
g_free(filename);
return -1;
}
- newsize = s.st_size;
+ newsize = st.st_size;
if (newsize) ret |= ANY_MAIL;
- if (s.st_mtime > s.st_atime && newsize) ret |= UNREAD_MAIL;
+ if (st.st_mtime > st.st_atime && newsize) ret |= UNREAD_MAIL;
if (newsize != oldsize && (ret & UNREAD_MAIL)) ret |= NEW_MAIL;
oldsize = newsize;
@@ -55,7 +55,7 @@ static gboolean
check_timeout(gpointer data)
{
gint count = check_mail();
- PurpleBuddyList *list = purple_get_blist();
+ PurpleBuddyList *list = purple_blist_get_buddy_list();
if (count == -1)
return FALSE;
@@ -90,7 +90,7 @@ check_timeout(gpointer data)
static void
signon_cb(PurpleConnection *gc)
{
- PurpleBuddyList *list = purple_get_blist();
+ PurpleBuddyList *list = purple_blist_get_buddy_list();
if (list && PURPLE_IS_GTK_BLIST(list) && !timer) {
check_timeout(NULL); /* we want the box to be drawn immediately */
timer = purple_timeout_add_seconds(2, check_timeout, NULL);
@@ -100,7 +100,7 @@ signon_cb(PurpleConnection *gc)
static void
signoff_cb(PurpleConnection *gc)
{
- PurpleBuddyList *list = purple_get_blist();
+ PurpleBuddyList *list = purple_blist_get_buddy_list();
if ((!list || !PURPLE_IS_GTK_BLIST(list) || !PIDGIN_BLIST(list)->vbox) && timer) {
purple_timeout_remove(timer);
timer = 0;
@@ -114,7 +114,7 @@ signoff_cb(PurpleConnection *gc)
static gboolean
plugin_load(PurplePlugin *plugin)
{
- PurpleBuddyList *list = purple_get_blist();
+ PurpleBuddyList *list = purple_blist_get_buddy_list();
void *conn_handle = purple_connections_get_handle();
if (!check_timeout(NULL)) {
diff --git a/pidgin/plugins/markerline.c b/pidgin/plugins/markerline.c
index 7db983f3f2..b19df4e88e 100644
--- a/pidgin/plugins/markerline.c
+++ b/pidgin/plugins/markerline.c
@@ -33,85 +33,40 @@
/* Purple headers */
#include <gtkconv.h>
-#include <gtkimhtml.h>
#include <gtkplugin.h>
+#include <gtkwebview.h>
#include <version.h>
#define PREF_PREFIX "/plugins/gtk/" PLUGIN_ID
#define PREF_IMS PREF_PREFIX "/ims"
#define PREF_CHATS PREF_PREFIX "/chats"
-static int
-imhtml_expose_cb(GtkWidget *widget, GdkEventExpose *event, PidginConversation *gtkconv)
-{
- int y, last_y, offset;
- GdkRectangle visible_rect;
- GtkTextIter iter;
- GdkRectangle buf;
- int pad;
- PurpleConversation *conv = gtkconv->active_conv;
- PurpleConversationType type = purple_conversation_get_type(conv);
-
- if ((type == PURPLE_CONV_TYPE_CHAT && !purple_prefs_get_bool(PREF_CHATS)) ||
- (type == PURPLE_CONV_TYPE_IM && !purple_prefs_get_bool(PREF_IMS)))
- return FALSE;
-
- gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(widget), &visible_rect);
-
- offset = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "markerline"));
- if (offset)
- {
- gtk_text_buffer_get_iter_at_offset(gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)),
- &iter, offset);
-
- gtk_text_view_get_iter_location(GTK_TEXT_VIEW(widget), &iter, &buf);
- last_y = buf.y + buf.height;
- pad = (gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(widget)) +
- gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(widget))) / 2;
- last_y += pad;
- }
- else
- last_y = 0;
-
- gtk_text_view_buffer_to_window_coords(GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_TEXT,
- 0, last_y, 0, &y);
-
- if (y >= event->area.y)
- {
- GdkColor red = {0, 0xffff, 0, 0};
- cairo_t *cr = gdk_cairo_create(GDK_DRAWABLE(event->window));
-
- gdk_cairo_set_source_color(cr, &red);
- cairo_move_to(cr, 0.0, y + 0.5);
- cairo_rel_line_to(cr, visible_rect.width, 0.0);
- cairo_set_line_width(cr, 1.0);
- cairo_stroke(cr);
- cairo_destroy(cr);
- }
- return FALSE;
-}
-
static void
update_marker_for_gtkconv(PidginConversation *gtkconv)
{
- GtkTextIter iter;
- GtkTextBuffer *buffer;
+ PurpleConversation *conv;
+
g_return_if_fail(gtkconv != NULL);
- buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml));
+ conv = gtkconv->active_conv;
- if (!gtk_text_buffer_get_char_count(buffer))
+ if ((PURPLE_IS_CHAT_CONVERSATION(conv) && !purple_prefs_get_bool(PREF_CHATS)) ||
+ (PURPLE_IS_IM_CONVERSATION(conv) && !purple_prefs_get_bool(PREF_IMS)))
return;
- gtk_text_buffer_get_end_iter(buffer, &iter);
-
- g_object_set_data(G_OBJECT(gtkconv->imhtml), "markerline",
- GINT_TO_POINTER(gtk_text_iter_get_offset(&iter)));
- gtk_widget_queue_draw(gtkconv->imhtml);
+ 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, PidginWindow *win)
+focus_removed(GtkWidget *widget, GdkEventVisibility *event, PidginConvWindow *win)
{
PurpleConversation *conv;
PidginConversation *gtkconv;
@@ -125,30 +80,8 @@ focus_removed(GtkWidget *widget, GdkEventVisibility *event, PidginWindow *win)
return FALSE;
}
-#if 0
-static gboolean
-window_resized(GtkWidget *w, GdkEventConfigure *event, PidginWindow *win)
-{
- GList *list;
-
- list = pidgin_conv_window_get_gtkconvs(win);
-
- for (; list; list = list->next)
- update_marker_for_gtkconv(list->data);
-
- return FALSE;
-}
-
-static gboolean
-imhtml_resize_cb(GtkWidget *w, GtkAllocation *allocation, PidginConversation *gtkconv)
-{
- gtk_widget_queue_draw(w);
- return FALSE;
-}
-#endif
-
static void
-page_switched(GtkWidget *widget, GtkWidget *page, gint num, PidginWindow *win)
+page_switched(GtkWidget *widget, GtkWidget *page, gint num, PidginConvWindow *win)
{
focus_removed(NULL, NULL, win);
}
@@ -156,29 +89,28 @@ page_switched(GtkWidget *widget, GtkWidget *page, gint num, PidginWindow *win)
static void
detach_from_gtkconv(PidginConversation *gtkconv, gpointer null)
{
- g_signal_handlers_disconnect_by_func(G_OBJECT(gtkconv->imhtml), imhtml_expose_cb, gtkconv);
+ 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(PidginWindow *win, gpointer null)
+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);
-
- gtk_widget_queue_draw(win->window);
}
static void
attach_to_gtkconv(PidginConversation *gtkconv, gpointer null)
{
detach_from_gtkconv(gtkconv, NULL);
- g_signal_connect(G_OBJECT(gtkconv->imhtml), "expose_event",
- G_CALLBACK(imhtml_expose_cb), gtkconv);
+ update_marker_for_gtkconv(gtkconv);
}
static void
-attach_to_pidgin_window(PidginWindow *win, gpointer null)
+attach_to_pidgin_window(PidginConvWindow *win, gpointer null)
{
g_list_foreach(pidgin_conv_window_get_gtkconvs(win), (GFunc)attach_to_gtkconv, NULL);
@@ -187,8 +119,6 @@ attach_to_pidgin_window(PidginWindow *win, gpointer null)
g_signal_connect(G_OBJECT(win->notebook), "switch_page",
G_CALLBACK(page_switched), win);
-
- gtk_widget_queue_draw(win->window);
}
static void
@@ -206,7 +136,7 @@ attach_to_all_windows(void)
static void
conv_created(PidginConversation *gtkconv, gpointer null)
{
- PidginWindow *win;
+ PidginConvWindow *win;
win = pidgin_conv_get_window(gtkconv);
if (!win)
@@ -220,23 +150,22 @@ static void
jump_to_markerline(PurpleConversation *conv, gpointer null)
{
PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
- int offset;
- GtkTextIter iter;
if (!gtkconv)
return;
- offset = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(gtkconv->imhtml), "markerline"));
- gtk_text_buffer_get_iter_at_offset(GTK_IMHTML(gtkconv->imhtml)->text_buffer, &iter, offset);
- gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(gtkconv->imhtml), &iter, 0, TRUE, 0, 0);
+ 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)
{
- PurpleConversationType type = purple_conversation_get_type(conv);
- gboolean enabled = ((type == PURPLE_CONV_TYPE_IM && purple_prefs_get_bool(PREF_IMS)) ||
- (type == PURPLE_CONV_TYPE_CHAT && purple_prefs_get_bool(PREF_CHATS)));
+ gboolean enabled = ((PURPLE_IS_IM_CONVERSATION(conv) && purple_prefs_get_bool(PREF_IMS)) ||
+ (PURPLE_IS_CHAT_CONVERSATION(conv) && purple_prefs_get_bool(PREF_CHATS)));
PurpleMenuAction *action = purple_menu_action_new(_("Jump to markerline"),
enabled ? PURPLE_CALLBACK(jump_to_markerline) : NULL, NULL, NULL);
*list = g_list_append(*list, action);
@@ -287,7 +216,6 @@ get_plugin_pref_frame(PurplePlugin *plugin)
static PurplePluginUiInfo prefs_info = {
get_plugin_pref_frame,
- 0,
NULL,
/* padding */
diff --git a/pidgin/plugins/musicmessaging/Makefile.am b/pidgin/plugins/musicmessaging/Makefile.am
index 9dea5b045f..6c847bca4b 100644
--- a/pidgin/plugins/musicmessaging/Makefile.am
+++ b/pidgin/plugins/musicmessaging/Makefile.am
@@ -1,9 +1,9 @@
EXTRA_DIST = \
music.png
-musicmessagingdir = $(libdir)/pidgin
+musicmessagingdir = @PIDGIN_PLUGINDIR@
-musicmessaging_la_LDFLAGS = -module -avoid-version
+musicmessaging_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
if PLUGINS
if ENABLE_DBUS
@@ -16,17 +16,17 @@ musicmessaging_LTLIBRARIES = musicmessaging.la
musicmessaging_la_SOURCES = \
musicmessaging.c
-musicmessaging_la_LIBADD = $(GTK_LIBS) $(DBUS_LIBS)
+musicmessaging_la_LIBADD = @PIDGIN_LIBS@ $(DBUS_LIBS)
CLEANFILES = music-messaging-bindings.c
.PHONY: always
$(top_builddir)/libpurple/dbus-types.h: always
- cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F)
+ $(AM_V_GEN)cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F)
music-messaging-bindings.c: $(top_srcdir)/libpurple/dbus-analyze-functions.py $(musicmessaging_la_SOURCES)
- cat $(srcdir)/$(musicmessaging_la_SOURCES) | \
+ $(AM_V_GEN)cat $(srcdir)/$(musicmessaging_la_SOURCES) | \
$(PYTHON) $(top_srcdir)/libpurple/dbus-analyze-functions.py --export-only > $@
$(musicmessaging_la_OBJECTS) musicmessaging.so: music-messaging-bindings.c $(top_builddir)/libpurple/dbus-types.h
@@ -35,7 +35,6 @@ endif
endif
AM_CPPFLAGS = \
- -DDATADIR=\"$(datadir)\" \
-I$(top_builddir)/libpurple \
-I$(top_srcdir)/libpurple \
-I$(top_srcdir)/pidgin \
diff --git a/pidgin/plugins/musicmessaging/musicmessaging.c b/pidgin/plugins/musicmessaging/musicmessaging.c
index 1a735d7c9a..37bfb3f9f5 100644
--- a/pidgin/plugins/musicmessaging/musicmessaging.c
+++ b/pidgin/plugins/musicmessaging/musicmessaging.c
@@ -24,6 +24,7 @@
#include "conversation.h"
+#include "gtk3compat.h"
#include "gtkconv.h"
#include "gtkplugin.h"
#include "gtkutils.h"
@@ -63,7 +64,7 @@ static void add_button (MMConversation *mmconv);
static void remove_widget (GtkWidget *button);
static void init_conversation (PurpleConversation *conv);
static void conv_destroyed(PurpleConversation *conv);
-static gboolean intercept_sent(PurpleAccount *account, const char *who, char **message, void* pData);
+static gboolean intercept_sent(PurpleAccount *account, PurpleMessage *msg, void* pData);
static gboolean intercept_received(PurpleAccount *account, char **sender, char **message, PurpleConversation *conv, int *flags);
static gboolean send_change_request (const int session, const char *id, const char *command, const char *parameters);
static gboolean send_change_confirmed (const int session, const char *command, const char *parameters);
@@ -84,10 +85,20 @@ DBusGProxy *proxy;
#define DBUS_INTERFACE_GSCORE "org.gscore.GScoreInterface"
/* Define the functions to export for use with DBus */
-DBUS_EXPORT void music_messaging_change_request (const int session, const char *command, const char *parameters);
-DBUS_EXPORT void music_messaging_change_confirmed (const int session, const char *command, const char *parameters);
-DBUS_EXPORT void music_messaging_change_failed (const int session, const char *id, const char *command, const char *parameters);
-DBUS_EXPORT void music_messaging_done_session (const int session);
+DBUS_EXPORT void
+music_messaging_change_request(const int session, const char *command,
+ const char *parameters);
+
+DBUS_EXPORT void
+music_messaging_change_confirmed(const int session, const char *command,
+ const char *parameters);
+
+DBUS_EXPORT void
+music_messaging_change_failed(const int session, const char *id,
+ const char *command, const char *parameters);
+
+DBUS_EXPORT void
+music_messaging_done_session(const int session);
/* This file has been generated by the #dbus-analize-functions.py
script. It contains dbus wrappers for the four functions declared
@@ -95,7 +106,9 @@ DBUS_EXPORT void music_messaging_done_session (const int session);
#include "music-messaging-bindings.c"
/* Exported functions */
-void music_messaging_change_request(const int session, const char *command, const char *parameters)
+DBUS_EXPORT void
+music_messaging_change_request(const int session, const char *command,
+ const char *parameters)
{
MMConversation *mmconv = (MMConversation *)g_list_nth_data(conversations, session);
@@ -104,14 +117,14 @@ void music_messaging_change_request(const int session, const char *command, cons
{
if (mmconv->originator)
{
- char *name = (mmconv->conv)->name;
+ const char *name = purple_conversation_get_name(mmconv->conv);
send_change_request (session, name, command, parameters);
} else
{
GString *to_send = g_string_new("");
g_string_append_printf(to_send, "##MM## request %s %s##MM##", command, parameters);
- purple_conv_im_send(PURPLE_CONV_IM(mmconv->conv), to_send->str);
+ purple_conversation_send(mmconv->conv, to_send->str);
purple_debug_misc("musicmessaging", "Sent request: %s\n", to_send->str);
}
@@ -119,7 +132,9 @@ void music_messaging_change_request(const int session, const char *command, cons
}
-void music_messaging_change_confirmed(const int session, const char *command, const char *parameters)
+DBUS_EXPORT void
+music_messaging_change_confirmed(const int session, const char *command,
+ const char *parameters)
{
MMConversation *mmconv = (MMConversation *)g_list_nth_data(conversations, session);
@@ -131,7 +146,7 @@ void music_messaging_change_confirmed(const int session, const char *command, co
GString *to_send = g_string_new("");
g_string_append_printf(to_send, "##MM## confirm %s %s##MM##", command, parameters);
- purple_conv_im_send(PURPLE_CONV_IM(mmconv->conv), to_send->str);
+ purple_conversation_send(mmconv->conv, to_send->str);
} else
{
/* Do nothing. If they aren't the originator, then they can't confirm. */
@@ -140,12 +155,14 @@ void music_messaging_change_confirmed(const int session, const char *command, co
}
-void music_messaging_change_failed(const int session, const char *id, const char *command, const char *parameters)
+DBUS_EXPORT void
+music_messaging_change_failed(const int session, const char *id,
+ const char *command, const char *parameters)
{
MMConversation *mmconv = (MMConversation *)g_list_nth_data(conversations, session);
purple_notify_message(plugin_pointer, PURPLE_NOTIFY_MSG_INFO, command,
- parameters, NULL, NULL, NULL);
+ parameters, NULL, NULL, NULL, NULL);
if (mmconv->started)
{
@@ -154,7 +171,7 @@ void music_messaging_change_failed(const int session, const char *id, const char
GString *to_send = g_string_new("");
g_string_append_printf(to_send, "##MM## failed %s %s %s##MM##", id, command, parameters);
- purple_conv_im_send(PURPLE_CONV_IM(mmconv->conv), to_send->str);
+ purple_conversation_send(mmconv->conv, to_send->str);
} else
{
/* Do nothing. If they aren't the originator, then they can't confirm. */
@@ -162,12 +179,13 @@ void music_messaging_change_failed(const int session, const char *id, const char
}
}
-void music_messaging_done_session(const int session)
+DBUS_EXPORT void
+music_messaging_done_session(const int session)
{
MMConversation *mmconv = (MMConversation *)g_list_nth_data(conversations, session);
purple_notify_message(plugin_pointer, PURPLE_NOTIFY_MSG_INFO, "Session",
- "Session Complete", NULL, NULL, NULL);
+ "Session Complete", NULL, NULL, NULL, NULL);
session_end(mmconv);
}
@@ -183,7 +201,7 @@ static gboolean send_change_request (const int session, const char *id, const ch
DBusMessage *message;
/* Create the signal we need */
- message = dbus_message_new_signal (DBUS_PATH_PURPLE, DBUS_INTERFACE_PURPLE, "GscoreChangeRequest");
+ message = dbus_message_new_signal (PURPLE_DBUS_PATH, PURPLE_DBUS_INTERFACE, "GscoreChangeRequest");
/* Append the string "Ping!" to the signal */
dbus_message_append_args (message,
@@ -210,7 +228,7 @@ static gboolean send_change_confirmed (const int session, const char *command, c
DBusMessage *message;
/* Create the signal we need */
- message = dbus_message_new_signal (DBUS_PATH_PURPLE, DBUS_INTERFACE_PURPLE, "GscoreChangeConfirmed");
+ message = dbus_message_new_signal (PURPLE_DBUS_PATH, PURPLE_DBUS_INTERFACE, "GscoreChangeConfirmed");
/* Append the string "Ping!" to the signal */
dbus_message_append_args (message,
@@ -261,6 +279,7 @@ mmconv_from_conv(PurpleConversation *conv)
static gboolean
plugin_load(PurplePlugin *plugin) {
void *conv_list_handle;
+ GList *l;
PURPLE_DBUS_RETURN_FALSE_IF_DISABLED(plugin);
@@ -273,7 +292,8 @@ plugin_load(PurplePlugin *plugin) {
plugin_pointer = plugin;
/* Add the button to all the current conversations */
- purple_conversation_foreach (init_conversation);
+ for (l = purple_conversations_get_all(); l != NULL; l = l->next)
+ init_conversation((PurpleConversation *)l->data);
/* Listen for any new conversations */
conv_list_handle = purple_conversations_get_handle();
@@ -309,33 +329,34 @@ plugin_unload(PurplePlugin *plugin) {
static gboolean
-intercept_sent(PurpleAccount *account, const char *who, char **message, void* pData)
+intercept_sent(PurpleAccount *account, PurpleMessage *msg, void* pData)
{
- if (message == NULL || *message == NULL || **message == '\0')
+ const gchar *cont = purple_message_get_contents(msg);
+
+ if (purple_message_is_empty(msg))
return FALSE;
- if (0 == strncmp(*message, MUSICMESSAGING_PREFIX, strlen(MUSICMESSAGING_PREFIX)))
+ if (0 == strncmp(cont, MUSICMESSAGING_PREFIX, strlen(MUSICMESSAGING_PREFIX)))
{
- purple_debug_misc("purple-musicmessaging", "Sent MM Message: %s\n", *message);
- message = 0;
+ purple_debug_misc("purple-musicmessaging", "Sent MM Message: %s\n", cont);
}
- else if (0 == strncmp(*message, MUSICMESSAGING_START_MSG, strlen(MUSICMESSAGING_START_MSG)))
+ else if (0 == strncmp(cont, MUSICMESSAGING_START_MSG, strlen(MUSICMESSAGING_START_MSG)))
{
purple_debug_misc("purple-musicmessaging", "Sent MM request.\n");
return FALSE;
}
- else if (0 == strncmp(*message, MUSICMESSAGING_CONFIRM_MSG, strlen(MUSICMESSAGING_CONFIRM_MSG)))
+ else if (0 == strncmp(cont, MUSICMESSAGING_CONFIRM_MSG, strlen(MUSICMESSAGING_CONFIRM_MSG)))
{
purple_debug_misc("purple-musicmessaging", "Sent MM confirm.\n");
return FALSE;
}
- else if (0 == strncmp(*message, "test1", strlen("test1")))
+ else if (0 == strncmp(cont, "test1", strlen("test1")))
{
purple_debug_misc("purple-musicmessaging", "\n\nTEST 1\n\n");
send_change_request(0, "test-id", "test-command", "test-parameters");
return FALSE;
}
- else if (0 == strncmp(*message, "test2", strlen("test2")))
+ else if (0 == strncmp(cont, "test2", strlen("test2")))
{
purple_debug_misc("purple-musicmessaging", "\n\nTEST 2\n\n");
send_change_confirmed(1, "test-command", "test-parameters");
@@ -376,7 +397,7 @@ intercept_received(PurpleAccount *account, char **sender, char **message, Purple
if (mmconv->originator)
{
int session = mmconv_from_conv_loc(conv);
- char *id = (mmconv->conv)->name;
+ const char *id = purple_conversation_get_name(mmconv->conv);
char *command;
char *parameters;
@@ -424,11 +445,12 @@ intercept_received(PurpleAccount *account, char **sender, char **message, Purple
command = strtok(NULL, " ");
/* char *parameters = strtok(NULL, "#"); DONT NEED PARAMETERS */
- if ((mmconv->conv)->name == id)
+ // TODO: Shouldn't this be strcmp() ?
+ if (purple_conversation_get_name(mmconv->conv) == id)
{
purple_notify_message(plugin_pointer, PURPLE_NOTIFY_MSG_ERROR,
_("Music Messaging"),
- _("There was a conflict in running the command:"), command, NULL, NULL);
+ _("There was a conflict in running the command:"), command, NULL, NULL, NULL);
}
}
}
@@ -465,16 +487,18 @@ intercept_received(PurpleAccount *account, char **sender, char **message, Purple
static void send_request(MMConversation *mmconv)
{
- PurpleConnection *connection = purple_conversation_get_gc(mmconv->conv);
+ PurpleConnection *connection = purple_conversation_get_connection(mmconv->conv);
const char *convName = purple_conversation_get_name(mmconv->conv);
- serv_send_im(connection, convName, MUSICMESSAGING_START_MSG, PURPLE_MESSAGE_SEND);
+ purple_serv_send_im(connection, purple_message_new_outgoing(
+ convName, MUSICMESSAGING_START_MSG, 0));
}
static void send_request_confirmed(MMConversation *mmconv)
{
- PurpleConnection *connection = purple_conversation_get_gc(mmconv->conv);
+ PurpleConnection *connection = purple_conversation_get_connection(mmconv->conv);
const char *convName = purple_conversation_get_name(mmconv->conv);
- serv_send_im(connection, convName, MUSICMESSAGING_CONFIRM_MSG, PURPLE_MESSAGE_SEND);
+ purple_serv_send_im(connection, purple_message_new_outgoing(
+ convName, MUSICMESSAGING_CONFIRM_MSG, 0));
}
@@ -537,7 +561,7 @@ static void run_editor (MMConversation *mmconv)
if (!(g_spawn_async (".", args, NULL, 4, NULL, NULL, &(mmconv->pid), &spawn_error)))
{
purple_notify_error(plugin_pointer, _("Error Running Editor"),
- _("The following error has occurred:"), spawn_error->message);
+ _("The following error has occurred:"), spawn_error->message, NULL);
mmconv->started = FALSE;
}
else
@@ -548,11 +572,16 @@ static void run_editor (MMConversation *mmconv)
static void kill_editor (MMConversation *mmconv)
{
+#ifdef HAVE_SIGNAL_H
if (mmconv->pid)
{
kill(mmconv->pid, SIGINT);
mmconv->pid = 0;
}
+#else
+ purple_debug_warning("musicmessaging",
+ "kill() is not supported on this platform");
+#endif
}
static void init_conversation (PurpleConversation *conv)
@@ -585,7 +614,9 @@ static void conv_destroyed (PurpleConversation *conv)
static void add_button (MMConversation *mmconv)
{
+#if 0
PurpleConversation *conv = mmconv->conv;
+#endif
GtkWidget *button, *image, *sep;
gchar *file_path;
@@ -595,14 +626,14 @@ static void add_button (MMConversation *mmconv)
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(music_button_toggled), mmconv);
- file_path = g_build_filename(DATADIR, "pixmaps", "purple", "buttons",
- "music.png", NULL);
+ file_path = g_build_filename(PURPLE_DATADIR,
+ "pixmaps", "purple", "buttons", "music.png", NULL);
image = gtk_image_new_from_file(file_path);
g_free(file_path);
gtk_container_add((GtkContainer *)button, image);
- sep = gtk_vseparator_new();
+ sep = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
mmconv->seperator = sep;
mmconv->button = button;
@@ -611,8 +642,10 @@ static void add_button (MMConversation *mmconv)
gtk_widget_show(image);
gtk_widget_show(button);
+#if 0
gtk_box_pack_start(GTK_BOX(PIDGIN_CONVERSATION(conv)->toolbar), sep, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(PIDGIN_CONVERSATION(conv)->toolbar), button, FALSE, FALSE, 0);
+#endif
}
static void remove_widget (GtkWidget *button)
@@ -632,7 +665,7 @@ get_config_frame(PurplePlugin *plugin)
GtkWidget *editor_path_button;
/* Outside container */
- ret = gtk_vbox_new(FALSE, 18);
+ ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
gtk_container_set_border_width(GTK_CONTAINER(ret), 10);
/* Configuration frame */
@@ -660,7 +693,6 @@ get_config_frame(PurplePlugin *plugin)
static PidginPluginUiInfo ui_info =
{
get_config_frame,
- 0, /* page_num (reserved) */
/* padding */
NULL,
diff --git a/pidgin/plugins/notify.c b/pidgin/plugins/notify.c
index a993a5fa70..83953787d7 100644
--- a/pidgin/plugins/notify.c
+++ b/pidgin/plugins/notify.c
@@ -93,6 +93,7 @@
#include "version.h"
#include "debug.h"
+#include "gtk3compat.h"
#include "gtkplugin.h"
#include "gtkutils.h"
@@ -106,7 +107,7 @@ static GdkAtom _PurpleUnseenCount = GDK_NONE;
/* notification set/unset */
static int notify(PurpleConversation *conv, gboolean increment);
-static void notify_win(PidginWindow *purplewin, PurpleConversation *conv);
+static void notify_win(PidginConvWindow *purplewin, PurpleConversation *conv);
static void unnotify(PurpleConversation *conv, gboolean reset);
static int unnotify_cb(GtkWidget *widget, gpointer data,
PurpleConversation *conv);
@@ -121,19 +122,19 @@ static void apply_method(void);
static void apply_notify(void);
/* string function */
-static void handle_string(PidginWindow *purplewin);
+static void handle_string(PidginConvWindow *purplewin);
/* count_title function */
-static void handle_count_title(PidginWindow *purplewin);
+static void handle_count_title(PidginConvWindow *purplewin);
/* count_xprop function */
-static void handle_count_xprop(PidginWindow *purplewin);
+static void handle_count_xprop(PidginConvWindow *purplewin);
/* urgent function */
-static void handle_urgent(PidginWindow *purplewin, gboolean set);
+static void handle_urgent(PidginConvWindow *purplewin, gboolean set);
/* raise function */
-static void handle_raise(PidginWindow *purplewin);
+static void handle_raise(PidginConvWindow *purplewin);
/* present function */
static void handle_present(PurpleConversation *conv);
@@ -142,7 +143,7 @@ static void handle_present(PurpleConversation *conv);
/* Begin doing stuff below this line... */
/****************************************/
static guint
-count_messages(PidginWindow *purplewin)
+count_messages(PidginConvWindow *purplewin)
{
guint count = 0;
GList *convs = NULL, *l;
@@ -150,7 +151,7 @@ count_messages(PidginWindow *purplewin)
for (convs = purplewin->gtkconvs; convs != NULL; convs = convs->next) {
PidginConversation *conv = convs->data;
for (l = conv->convs; l != NULL; l = l->next) {
- count += GPOINTER_TO_INT(purple_conversation_get_data(l->data, "notify-message-count"));
+ count += GPOINTER_TO_INT(g_object_get_data(G_OBJECT(l->data), "notify-message-count"));
}
}
@@ -162,7 +163,7 @@ notify(PurpleConversation *conv, gboolean increment)
{
gint count;
gboolean has_focus;
- PidginWindow *purplewin = NULL;
+ PidginConvWindow *purplewin = NULL;
if (conv == NULL || PIDGIN_CONVERSATION(conv) == NULL)
return 0;
@@ -173,9 +174,9 @@ notify(PurpleConversation *conv, gboolean increment)
purplewin = PIDGIN_CONVERSATION(conv)->win;
/* If we aren't doing notifications for this type of conversation, return */
- if (((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) &&
+ if ((PURPLE_IS_IM_CONVERSATION(conv) &&
!purple_prefs_get_bool("/plugins/gtk/X11/notify/type_im")) ||
- ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) &&
+ (PURPLE_IS_CHAT_CONVERSATION(conv) &&
!purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat")))
return 0;
@@ -185,9 +186,9 @@ notify(PurpleConversation *conv, gboolean increment)
if (purple_prefs_get_bool("/plugins/gtk/X11/notify/type_focused") ||
!has_focus) {
if (increment) {
- count = GPOINTER_TO_INT(purple_conversation_get_data(conv, "notify-message-count"));
+ count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "notify-message-count"));
count++;
- purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(count));
+ g_object_set_data(G_OBJECT(conv), "notify-message-count", GINT_TO_POINTER(count));
}
notify_win(purplewin, conv);
@@ -197,7 +198,7 @@ notify(PurpleConversation *conv, gboolean increment)
}
static void
-notify_win(PidginWindow *purplewin, PurpleConversation *conv)
+notify_win(PidginConvWindow *purplewin, PurpleConversation *conv)
{
if (count_messages(purplewin) <= 0)
return;
@@ -220,7 +221,7 @@ static void
unnotify(PurpleConversation *conv, gboolean reset)
{
PurpleConversation *active_conv = NULL;
- PidginWindow *purplewin = NULL;
+ PidginConvWindow *purplewin = NULL;
g_return_if_fail(conv != NULL);
if (PIDGIN_CONVERSATION(conv) == NULL)
@@ -237,7 +238,7 @@ unnotify(PurpleConversation *conv, gboolean reset)
* removing it just to have it readded in re-notify is an
* unnecessary couple extra RTs to the server */
handle_urgent(purplewin, FALSE);
- purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(0));
+ g_object_set_data(G_OBJECT(conv), "notify-message-count", GINT_TO_POINTER(0));
/* Same logic as for the urgent hint, xprops are also a RT.
* This needs to go here so that it gets the updated message
* count. */
@@ -250,17 +251,18 @@ unnotify(PurpleConversation *conv, gboolean reset)
static int
unnotify_cb(GtkWidget *widget, gpointer data, PurpleConversation *conv)
{
- if (GPOINTER_TO_INT(purple_conversation_get_data(conv, "notify-message-count")) != 0)
+ if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "notify-message-count")) != 0)
unnotify(conv, TRUE);
return 0;
}
static gboolean
-message_displayed_cb(PurpleAccount *account, const char *who, char *message,
- PurpleConversation *conv, PurpleMessageFlags flags)
+message_displayed_cb(PurpleConversation *conv, PurpleMessage *msg, gpointer _unused)
{
- if ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT &&
+ PurpleMessageFlags flags = purple_message_get_flags(msg);
+
+ if ((PURPLE_IS_CHAT_CONVERSATION(conv) &&
purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat_nick") &&
!(flags & PURPLE_MESSAGE_NICK)))
return FALSE;
@@ -272,24 +274,25 @@ message_displayed_cb(PurpleAccount *account, const char *who, char *message,
}
static void
-im_sent_im(PurpleAccount *account, const char *receiver, const char *message)
+im_sent_im(PurpleAccount *account, PurpleMessage *msg, gpointer _unused)
{
- PurpleConversation *conv = NULL;
+ PurpleIMConversation *im = NULL;
if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_send")) {
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, receiver, account);
- unnotify(conv, TRUE);
+ im = purple_conversations_find_im_with_account(
+ purple_message_get_recipient(msg), account);
+ unnotify(PURPLE_CONVERSATION(im), TRUE);
}
}
static void
-chat_sent_im(PurpleAccount *account, const char *message, int id)
+chat_sent_im(PurpleAccount *account, PurpleMessage *msg, int id)
{
- PurpleConversation *conv = NULL;
+ PurpleChatConversation *chat = NULL;
if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_send")) {
- conv = purple_find_chat(purple_account_get_connection(account), id);
- unnotify(conv, TRUE);
+ chat = purple_conversations_find_chat(purple_account_get_connection(account), id);
+ unnotify(PURPLE_CONVERSATION(chat), TRUE);
}
}
@@ -297,7 +300,7 @@ static int
attach_signals(PurpleConversation *conv)
{
PidginConversation *gtkconv = NULL;
- GSList *imhtml_ids = NULL, *entry_ids = NULL;
+ GSList *webview_ids = NULL, *entry_ids = NULL;
guint id;
gtkconv = PIDGIN_CONVERSATION(conv);
@@ -316,9 +319,9 @@ attach_signals(PurpleConversation *conv)
G_CALLBACK(unnotify_cb), conv);
entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id));
- id = g_signal_connect(G_OBJECT(gtkconv->imhtml), "focus-in-event",
+ id = g_signal_connect(G_OBJECT(gtkconv->webview), "focus-in-event",
G_CALLBACK(unnotify_cb), conv);
- imhtml_ids = g_slist_append(imhtml_ids, GUINT_TO_POINTER(id));
+ webview_ids = g_slist_append(webview_ids, GUINT_TO_POINTER(id));
}
if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_click")) {
@@ -328,9 +331,9 @@ attach_signals(PurpleConversation *conv)
G_CALLBACK(unnotify_cb), conv);
entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id));
- id = g_signal_connect(G_OBJECT(gtkconv->imhtml), "button-press-event",
+ id = g_signal_connect(G_OBJECT(gtkconv->webview), "button-press-event",
G_CALLBACK(unnotify_cb), conv);
- imhtml_ids = g_slist_append(imhtml_ids, GUINT_TO_POINTER(id));
+ webview_ids = g_slist_append(webview_ids, GUINT_TO_POINTER(id));
}
if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_type")) {
@@ -339,8 +342,8 @@ attach_signals(PurpleConversation *conv)
entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id));
}
- purple_conversation_set_data(conv, "notify-imhtml-signals", imhtml_ids);
- purple_conversation_set_data(conv, "notify-entry-signals", entry_ids);
+ g_object_set_data(G_OBJECT(conv), "notify-webview-signals", webview_ids);
+ g_object_set_data(G_OBJECT(conv), "notify-entry-signals", entry_ids);
return 0;
}
@@ -355,26 +358,26 @@ detach_signals(PurpleConversation *conv)
if (!gtkconv)
return;
- ids = purple_conversation_get_data(conv, "notify-imhtml-signals");
+ ids = g_object_get_data(G_OBJECT(conv), "notify-webview-signals");
for (l = ids; l != NULL; l = l->next)
- g_signal_handler_disconnect(gtkconv->imhtml, GPOINTER_TO_INT(l->data));
+ g_signal_handler_disconnect(gtkconv->webview, GPOINTER_TO_INT(l->data));
g_slist_free(ids);
- ids = purple_conversation_get_data(conv, "notify-entry-signals");
+ ids = g_object_get_data(G_OBJECT(conv), "notify-entry-signals");
for (l = ids; l != NULL; l = l->next)
g_signal_handler_disconnect(gtkconv->entry, GPOINTER_TO_INT(l->data));
g_slist_free(ids);
- purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(0));
+ g_object_set_data(G_OBJECT(conv), "notify-message-count", GINT_TO_POINTER(0));
- purple_conversation_set_data(conv, "notify-imhtml-signals", NULL);
- purple_conversation_set_data(conv, "notify-entry-signals", NULL);
+ g_object_set_data(G_OBJECT(conv), "notify-webview-signals", NULL);
+ g_object_set_data(G_OBJECT(conv), "notify-entry-signals", NULL);
}
static void
conv_created(PurpleConversation *conv)
{
- purple_conversation_set_data(conv, "notify-message-count",
+ g_object_set_data(G_OBJECT(conv), "notify-message-count",
GINT_TO_POINTER(0));
/* always attach the signals, notify() will take care of conversation
@@ -386,7 +389,7 @@ static void
conv_switched(PurpleConversation *conv)
{
#if 0
- PidginWindow *purplewin = purple_conversation_get_window(new_conv);
+ PidginConvWindow *purplewin = purple_conversation_get_window(new_conv);
#endif
/*
@@ -412,7 +415,7 @@ conv_switched(PurpleConversation *conv)
static void
deleting_conv(PurpleConversation *conv)
{
- PidginWindow *purplewin = NULL;
+ PidginConvWindow *purplewin = NULL;
PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
if (gtkconv == NULL)
@@ -423,7 +426,7 @@ deleting_conv(PurpleConversation *conv)
purplewin = gtkconv->win;
handle_urgent(purplewin, FALSE);
- purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(0));
+ g_object_set_data(G_OBJECT(conv), "notify-message-count", GINT_TO_POINTER(0));
return;
@@ -437,8 +440,8 @@ deleting_conv(PurpleConversation *conv)
#if 0
static void
conversation_dragging(PurpleConversation *active_conv,
- PidginWindow *old_purplewin,
- PidginWindow *new_purplewin)
+ PidginConvWindow *old_purplewin,
+ PidginConvWindow *new_purplewin)
{
if (old_purplewin != new_purplewin) {
if (old_purplewin == NULL) {
@@ -454,7 +457,7 @@ conversation_dragging(PurpleConversation *active_conv,
printf("if else count = %d\n", count_messages(old_purplewin));
/*
PurpleConversation *old_active_conv = NULL;
- old_active_conv = purple_conv_window_get_active_conversation(new_purplewin);
+ old_active_conv = purple_conversation_window_get_active_conversation(new_purplewin);
purple_conversation_autoset_title(old_active_conv);
handle_urgent(old_purplewin, FALSE);
@@ -484,7 +487,7 @@ conversation_dragging(PurpleConversation *active_conv,
#endif
static void
-handle_string(PidginWindow *purplewin)
+handle_string(PidginConvWindow *purplewin)
{
GtkWindow *window = NULL;
gchar newtitle[256];
@@ -501,7 +504,7 @@ handle_string(PidginWindow *purplewin)
}
static void
-handle_count_title(PidginWindow *purplewin)
+handle_count_title(PidginConvWindow *purplewin)
{
GtkWindow *window;
char newtitle[256];
@@ -517,7 +520,7 @@ handle_count_title(PidginWindow *purplewin)
}
static void
-handle_count_xprop(PidginWindow *purplewin)
+handle_count_xprop(PidginConvWindow *purplewin)
{
#ifdef HAVE_X11
guint count;
@@ -536,11 +539,7 @@ handle_count_xprop(PidginWindow *purplewin)
}
count = count_messages(purplewin);
-#if GTK_CHECK_VERSION(2,14,0)
gdkwin = gtk_widget_get_window(window);
-#else
- gdkwin = window->window;
-#endif
gdk_property_change(gdkwin, _PurpleUnseenCount, _Cardinal, 32,
GDK_PROP_MODE_REPLACE, (guchar *) &count, 1);
@@ -548,7 +547,7 @@ handle_count_xprop(PidginWindow *purplewin)
}
static void
-handle_urgent(PidginWindow *purplewin, gboolean set)
+handle_urgent(PidginConvWindow *purplewin, gboolean set)
{
g_return_if_fail(purplewin != NULL);
g_return_if_fail(purplewin->window != NULL);
@@ -557,7 +556,7 @@ handle_urgent(PidginWindow *purplewin, gboolean set)
}
static void
-handle_raise(PidginWindow *purplewin)
+handle_raise(PidginConvWindow *purplewin)
{
pidgin_conv_window_raise(purplewin);
}
@@ -640,14 +639,14 @@ apply_method()
{
GList *convs;
- for (convs = purple_get_conversations(); convs != NULL;
+ for (convs = purple_conversations_get_all(); convs != NULL;
convs = convs->next) {
PurpleConversation *conv = (PurpleConversation *)convs->data;
/* remove notifications */
unnotify(conv, FALSE);
- if (GPOINTER_TO_INT(purple_conversation_get_data(conv, "notify-message-count")) != 0)
+ if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "notify-message-count")) != 0)
/* reattach appropriate notifications */
notify(conv, FALSE);
}
@@ -656,7 +655,7 @@ apply_method()
static void
apply_notify()
{
- GList *convs = purple_get_conversations();
+ GList *convs = purple_conversations_get_all();
while (convs) {
PurpleConversation *conv = (PurpleConversation *)convs->data;
@@ -677,12 +676,12 @@ get_config_frame(PurplePlugin *plugin)
GtkWidget *vbox = NULL, *hbox = NULL;
GtkWidget *toggle = NULL, *entry = NULL, *ref;
- ret = gtk_vbox_new(FALSE, 18);
+ ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
gtk_container_set_border_width(GTK_CONTAINER (ret), 12);
/*---------- "Notify For" ----------*/
frame = pidgin_make_frame(ret, _("Notify For"));
- vbox = gtk_vbox_new(FALSE, 5);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_container_add(GTK_CONTAINER(frame), vbox);
toggle = gtk_check_button_new_with_mnemonic(_("_IM windows"));
@@ -719,11 +718,11 @@ get_config_frame(PurplePlugin *plugin)
/*---------- "Notification Methods" ----------*/
frame = pidgin_make_frame(ret, _("Notification Methods"));
- vbox = gtk_vbox_new(FALSE, 5);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_container_add(GTK_CONTAINER(frame), vbox);
/* String method button */
- hbox = gtk_hbox_new(FALSE, 18);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 18);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
toggle = gtk_check_button_new_with_mnemonic(_("Prepend _string into window title:"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
@@ -789,7 +788,7 @@ get_config_frame(PurplePlugin *plugin)
/*---------- "Notification Removals" ----------*/
frame = pidgin_make_frame(ret, _("Notification Removal"));
- vbox = gtk_vbox_new(FALSE, 5);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_container_add(GTK_CONTAINER(frame), vbox);
/* Remove on focus button */
@@ -840,7 +839,7 @@ get_config_frame(PurplePlugin *plugin)
static gboolean
plugin_load(PurplePlugin *plugin)
{
- GList *convs = purple_get_conversations();
+ GList *convs = purple_conversations_get_all();
void *conv_handle = purple_conversations_get_handle();
void *gtk_conv_handle = pidgin_conversations_get_handle();
@@ -880,7 +879,7 @@ plugin_load(PurplePlugin *plugin)
static gboolean
plugin_unload(PurplePlugin *plugin)
{
- GList *convs = purple_get_conversations();
+ GList *convs = purple_conversations_get_all();
while (convs) {
PurpleConversation *conv = (PurpleConversation *)convs->data;
@@ -897,7 +896,6 @@ plugin_unload(PurplePlugin *plugin)
static PidginPluginUiInfo ui_info =
{
get_config_frame,
- 0, /* page_num (Reserved) */
/* padding */
NULL,
diff --git a/pidgin/plugins/perl/Makefile.am b/pidgin/plugins/perl/Makefile.am
index ac433246f1..64a090cf27 100644
--- a/pidgin/plugins/perl/Makefile.am
+++ b/pidgin/plugins/perl/Makefile.am
@@ -9,8 +9,6 @@ common_sources = \
common/GtkDebug.xs \
common/GtkDialogs.xs \
common/GtkFt.xs \
- common/GtkIMHtml.xs \
- common/GtkIMHtmlToolbar.xs \
common/GtkLog.xs \
common/GtkMenuTray.xs \
common/GtkPlugin.xs \
@@ -23,7 +21,6 @@ common_sources = \
common/GtkSession.xs \
common/GtkSound.xs \
common/GtkStatusBox.xs \
- common/GtkThemes.xs \
common/GtkUtils.xs \
common/Makefile.PL.in \
common/Pidgin.pm \
@@ -37,14 +34,14 @@ EXTRA_DIST = \
$(common_sources)
common/Makefile: common/Makefile.PL
- @if test "x${top_srcdir}" != "x${top_builddir}"; then \
+ $(AM_V_GEN)if test "x${top_srcdir}" != "x${top_builddir}"; then \
for f in ${common_sources}; do \
srcloc=${srcdir}; \
case $$srcloc in /*) ;; *) srcloc=../${srcdir} ;; esac; \
${LN_S} -f $$srcloc/$$f $$f; \
done; \
fi
- @cd common && $(perlpath) Makefile.PL
+ $(AM_V_at)cd common && $(perlpath) Makefile.PL > /dev/null
common/Makefile.PL: common/Makefile.PL.in $(top_builddir)/config.status
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)
@@ -55,8 +52,8 @@ all-local: common/Makefile
if [ ! -f Makefile ]; then \
$(perlpath) Makefile.PL; \
fi && \
- ($(MAKE) CC="$(CC)" CCFLAGS="$(PERL_CFLAGS) $(CFLAGS)" $(PERL_EXTRA_OPTS) || \
- $(MAKE) CC="$(CC)" CCFLAGS="$(PERL_CFLAGS) $(CFLAGS)" $(PERL_EXTRA_OPTS)) && \
+ ($(MAKE) CC="@$(abs_top_srcdir)/libpurple/tag.sh CC $(CC)" LD="@$(abs_top_srcdir)/libpurple/tag.sh LD $(CC)" PERLRUN="@$(abs_top_srcdir)/libpurple/tag.sh PERL $(PERL)" CCFLAGS="$(PERL_CFLAGS) $(CFLAGS)" CP="@cp" RM_F="@rm -f" CHMOD="@chmod" $(PERL_EXTRA_OPTS) || \
+ $(MAKE) CC="@$(abs_top_srcdir)/libpurple/tag.sh CC $(CC)" LD="@$(abs_top_srcdir)/libpurple/tag.sh LD $(CC)" PERLRUN="@$(abs_top_srcdir)/libpurple/tag.sh PERL $(PERL)" CCFLAGS="$(PERL_CFLAGS) $(CFLAGS)" CP="@cp" RM_F="@rm -f" CHMOD="@chmod" $(PERL_EXTRA_OPTS)) && \
cd ..; \
done
diff --git a/pidgin/plugins/perl/Makefile.mingw b/pidgin/plugins/perl/Makefile.mingw
index d118836b45..1e51ea3d46 100644
--- a/pidgin/plugins/perl/Makefile.mingw
+++ b/pidgin/plugins/perl/Makefile.mingw
@@ -7,19 +7,22 @@
PIDGIN_TREE_TOP := ../../..
include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
+#we cannot include win32dep.h, but we need struct sockaddr_in6 definition
+CFLAGS += -include ws2tcpip.h
+
##
## TARGET DEFINITIONS
##
.PHONY: all install clean
all:
- $(MAKE) -C ./common -f $(MINGW_MAKEFILE)
+ $(MAKE_at) $(MAKE) -C ./common -f $(MINGW_MAKEFILE)
install: all $(PIDGIN_INSTALL_PLUGINS_DIR)
- $(MAKE) -C ./common -f $(MINGW_MAKEFILE) install
+ $(MAKE_at) $(MAKE) -C ./common -f $(MINGW_MAKEFILE) install
##
## CLEAN RULES
##
clean:
- $(MAKE) -C ./common -f $(MINGW_MAKEFILE) clean
+ $(MAKE_at) $(MAKE) -C ./common -f $(MINGW_MAKEFILE) clean
diff --git a/pidgin/plugins/perl/common/GtkAccount.xs b/pidgin/plugins/perl/common/GtkAccount.xs
index 9a8eb69c5b..8f1195384c 100644
--- a/pidgin/plugins/perl/common/GtkAccount.xs
+++ b/pidgin/plugins/perl/common/GtkAccount.xs
@@ -4,7 +4,7 @@ MODULE = Pidgin::Account PACKAGE = Pidgin::Account PREFIX = pidgin_account_
PROTOTYPES: ENABLE
Purple::Handle
-pidgin_account_get_handle()
+pidgin_accounts_get_handle()
MODULE = Pidgin::Account PACKAGE = Pidgin::Account::Dialog PREFIX = pidgin_account_dialog_
PROTOTYPES: ENABLE
diff --git a/pidgin/plugins/perl/common/GtkBlist.xs b/pidgin/plugins/perl/common/GtkBlist.xs
index 8dd74fe3d7..453868cd86 100644
--- a/pidgin/plugins/perl/common/GtkBlist.xs
+++ b/pidgin/plugins/perl/common/GtkBlist.xs
@@ -69,8 +69,3 @@ pidgin_blist_joinchat_is_showable()
void
pidgin_blist_joinchat_show()
-
-void
-pidgin_blist_update_account_error_state(account, message)
- Purple::Account account
- const char * message
diff --git a/pidgin/plugins/perl/common/GtkConv.xs b/pidgin/plugins/perl/common/GtkConv.xs
index 042a43c871..6ec68b6710 100644
--- a/pidgin/plugins/perl/common/GtkConv.xs
+++ b/pidgin/plugins/perl/common/GtkConv.xs
@@ -4,8 +4,8 @@ MODULE = Pidgin::Conversation PACKAGE = Pidgin::Conversation PREFIX = pidgin_c
PROTOTYPES: ENABLE
void
-pidgin_conv_update_buddy_icon(conv)
- Purple::Conversation conv
+pidgin_conv_update_buddy_icon(im)
+ Purple::IMConversation im
void
pidgin_conv_switch_active_conversation(conv)
@@ -46,8 +46,19 @@ MODULE = Pidgin::Conversation PACKAGE = Pidgin::Conversations PREFIX = pidgin_
PROTOTYPES: ENABLE
void
-pidgin_conversations_find_unseen_list(type, min_state, hidden_only, max_count)
- Purple::ConversationType type
+pidgin_conversations_get_unseen_all(min_state, hidden_only, max_count)
+ Pidgin::UnseenState min_state
+ gboolean hidden_only
+ guint max_count
+
+void
+pidgin_conversations_get_unseen_ims(min_state, hidden_only, max_count)
+ Pidgin::UnseenState min_state
+ gboolean hidden_only
+ guint max_count
+
+void
+pidgin_conversations_get_unseen_chats(min_state, hidden_only, max_count)
Pidgin::UnseenState min_state
gboolean hidden_only
guint max_count
diff --git a/pidgin/plugins/perl/common/GtkConvWin.xs b/pidgin/plugins/perl/common/GtkConvWin.xs
index 8e74f12059..948daf438f 100644
--- a/pidgin/plugins/perl/common/GtkConvWin.xs
+++ b/pidgin/plugins/perl/common/GtkConvWin.xs
@@ -59,11 +59,6 @@ gboolean
pidgin_conv_window_has_focus(win)
Pidgin::Conversation::Window win
-Pidgin::Conversation::Window
-pidgin_conv_window_get_at_xy(x, y)
- int x
- int y
-
void
pidgin_conv_window_get_gtkconvs(win)
Pidgin::Conversation::Window win
@@ -79,12 +74,16 @@ pidgin_conv_window_get_gtkconv_count(win)
Pidgin::Conversation::Window win
Pidgin::Conversation::Window
-pidgin_conv_window_first_with_type(type)
- Purple::ConversationType type
+pidgin_conv_window_first_im()
+
+Pidgin::Conversation::Window
+pidgin_conv_window_last_im()
+
+Pidgin::Conversation::Window
+pidgin_conv_window_first_chat()
Pidgin::Conversation::Window
-pidgin_conv_window_last_with_type(type)
- Purple::ConversationType type
+pidgin_conv_window_last_chat()
MODULE = Pidgin::Conversation::Window PACKAGE = Pidgin::Conversation::Placement PREFIX = pidgin_conv_placement_
PROTOTYPES: ENABLE
diff --git a/pidgin/plugins/perl/common/GtkDialogs.xs b/pidgin/plugins/perl/common/GtkDialogs.xs
index 01767b9cb1..0e30194f1c 100644
--- a/pidgin/plugins/perl/common/GtkDialogs.xs
+++ b/pidgin/plugins/perl/common/GtkDialogs.xs
@@ -24,10 +24,6 @@ void
pidgin_dialogs_log()
void
-pidgin_dialogs_alias_contact(contact)
- Purple::BuddyList::Contact contact
-
-void
pidgin_dialogs_alias_buddy(buddy)
Purple::BuddyList::Buddy buddy
diff --git a/pidgin/plugins/perl/common/GtkIMHtml.xs b/pidgin/plugins/perl/common/GtkIMHtml.xs
deleted file mode 100644
index 43a75fa211..0000000000
--- a/pidgin/plugins/perl/common/GtkIMHtml.xs
+++ /dev/null
@@ -1,368 +0,0 @@
-#include "gtkmodule.h"
-
-/* This can't work at the moment since I don't have a typemap for Gtk::Widget.
- * I thought about using the one from libgtk2-perl but wasn't sure how to go
- * about doing that.
-Gtk::Widget
-gtk_imhtml_new(a, b)
-*/
-
-/* This can't work at the moment since I don't have a typemap for
- * Gtk::TextIter.
- * I thought about using the one from libgtk2-perl but wasn't sure how to go
- * about doing that.
-void
-gtk_imhtml_insert_html_at_iter(imhtml, text, options, iter)
- Pidgin::IMHtml imhtml
- const gchar * text
- Pidgin::IMHtml::Options options
- Gtk::TextIter iter
-*/
-
-/* This can't work at the moment since I don't have a typemap for Gtk::Widget.
- * I thought about using the one from libgtk2-perl but wasn't sure how to go
- * about doing that.
-void
-gtk_imhtml_delete(imhtml, start, end)
- Pidgin::IMHtml imhtml
- Gtk::TextIter start
- Gtk::TextIter end
-*/
-
-/* This can't work at the moment since I don't have a typemap for Gtk::Widget.
- * I thought about using the one from libgtk2-perl but wasn't sure how to go
- * about doing that.
-void
-gtk_imhtml_insert_link(imhtml, mark, url, text)
- Pidgin::IMHtml imhtml
- Gtk::TextMark mark
- const char * url
- const char * text
-*/
-
-/* This can't work at the moment since I don't have a typemap for Gtk::Widget.
- * I thought about using the one from libgtk2-perl but wasn't sure how to go
- * about doing that.
-void
-gtk_imhtml_insert_smiley_at_iter(imhtml, sml, smiley, iter)
- Pidgin::IMHtml imhtml
- const char * sml
- char * smiley
- Gtk::TextIter iter
-
-void
-gtk_imhtml_insert_image_at_iter(imhtml, id, iter)
- Pidgin::IMHtml imhtml
- int id
- Gtk::TextIter iter
-*/
-
-/* This can't work at the moment since I don't have a typemap for Gtk::Widget.
- * I thought about using the one from libgtk2-perl but wasn't sure how to go
- * about doing that.
-gchar_own *
-gtk_imhtml_get_markup_range(imhtml, start, end)
- Pidgin::IMHtml imhtml
- Gtk::TextIter start
- Gtk::TextIter end
-*/
-
-/* This can't work at the moment since I don't have a typemap for Gtk::Widget.
- * I thought about using the one from libgtk2-perl but wasn't sure how to go
- * about doing that.
-gchar_own *
-gtk_imhtml_get_text(imhtml, start, end)
- Pidgin::IMHtml imhtml
- Gtk::TextIter start
- Gtk::TextIter end
-*/
-
-/* This can't work at the moment since I don't have a typemap for Gdk::Pixbuf.
- * I thought about using the one from libgtk2-perl but wasn't sure how to go
- * about doing that.
-Pidgin::IMHtml::Scalable
-gtk_imhtml_image_new(img, filename, id)
- Gdk::Pixbuf img
- const gchar * filename
- int id
-*/
-
-/* This can't work at the moment since I don't have a typemap for Gtk::Widget.
- * I thought about using the one from libgtk2-perl but wasn't sure how to go
- * about doing that.
-void
-gtk_imhtml_image_add_to(scale, imhtml, iter)
- Pidgin::IMHtml::Scalable scale
- Pidgin::IMHtml imhtml
- Gtk::TextIter iter
-*/
-
-/* This can't work at the moment since I don't have a typemap for Gtk::Widget.
- * I thought about using the one from libgtk2-perl but wasn't sure how to go
- * about doing that.
-void
-gtk_imhtml_hr_add_to(scale, imhtml, iter)
- Pidgin::IMHtml::Scalable scale
- Pidgin::IMHtml imhtml
- Gtk::TextIter iter
-*/
-
-/* This can't work at the moment since I don't have a typemap for gboolean *.
-void
-gtk_imhtml_get_current_format(imhtml, bold, italic, underline)
- Pidgin::IMHtml imhtml
- gboolean * bold
- gboolean * italic
- gboolean * underline
-*/
-
-MODULE = Pidgin::IMHtml PACKAGE = Pidgin::IMHtml PREFIX = gtk_imhtml_
-PROTOTYPES: ENABLE
-
-Pidgin::IMHtml::Smiley
-gtk_imhtml_smiley_get(imhtml, sml, text)
- Pidgin::IMHtml imhtml
- const gchar * sml
- const gchar * text
-
-void
-gtk_imhtml_associate_smiley(imhtml, sml, smiley)
- Pidgin::IMHtml imhtml
- const gchar * sml
- Pidgin::IMHtml::Smiley smiley
-
-void
-gtk_imhtml_remove_smileys(imhtml)
- Pidgin::IMHtml imhtml
-
-void
-gtk_imhtml_set_funcs(imhtml, f)
- Pidgin::IMHtml imhtml
- Pidgin::IMHtml::Funcs f
-
-void
-gtk_imhtml_show_comments(imhtml, show)
- Pidgin::IMHtml imhtml
- gboolean show
-
-const char *
-gtk_imhtml_get_protocol_name(imhtml)
- Pidgin::IMHtml imhtml
-
-void
-gtk_imhtml_set_protocol_name(imhtml, protocol_name)
- Pidgin::IMHtml imhtml
- const gchar * protocol_name
-
-void
-gtk_imhtml_append_text(imhtml, text, options)
- Pidgin::IMHtml imhtml
- const gchar * text
- Pidgin::IMHtml::Options options
-
-void
-gtk_imhtml_append_text_with_images(imhtml, text, options, unused = NULL)
- Pidgin::IMHtml imhtml
- const gchar * text
- Pidgin::IMHtml::Options options
- SV *unused
-PREINIT:
- GSList *t_GL;
- int i, t_len;
-PPCODE:
- t_GL = NULL;
- if (unused)
- t_len = av_len((AV *)SvRV(unused));
- else
- t_len = 0;
-
- for (i = 0; i <= t_len && unused; i++) {
- STRLEN t_sl;
- t_GL = g_slist_append(t_GL, SvPV(*av_fetch((AV *)SvRV(unused), i, 0), t_sl));
- }
- gtk_imhtml_append_text_with_images(imhtml, text, options, t_GL);
-
-void
-gtk_imhtml_scroll_to_end(imhtml, smooth)
- Pidgin::IMHtml imhtml
- gboolean smooth
-
-void
-gtk_imhtml_clear(imhtml)
- Pidgin::IMHtml imhtml
-
-void
-gtk_imhtml_page_up(imhtml)
- Pidgin::IMHtml imhtml
-
-void
-gtk_imhtml_page_down(imhtml)
- Pidgin::IMHtml imhtml
-
-void
-gtk_imhtml_set_editable(imhtml, editable)
- Pidgin::IMHtml imhtml
- gboolean editable
-
-void
-gtk_imhtml_set_whole_buffer_formatting_only(imhtml, wbo)
- Pidgin::IMHtml imhtml
- gboolean wbo
-
-void
-gtk_imhtml_set_format_functions(imhtml, buttons)
- Pidgin::IMHtml imhtml
- Pidgin::IMHtml::Buttons buttons
-
-Pidgin::IMHtml::Buttons
-gtk_imhtml_get_format_functions(imhtml)
- Pidgin::IMHtml imhtml
-
-gchar_own *
-gtk_imhtml_get_current_fontface(imhtml)
- Pidgin::IMHtml imhtml
-
-gchar_own *
-gtk_imhtml_get_current_forecolor(imhtml)
- Pidgin::IMHtml imhtml
-
-gchar_own *
-gtk_imhtml_get_current_backcolor(imhtml)
- Pidgin::IMHtml imhtml
-
-gchar_own *
-gtk_imhtml_get_current_background(imhtml)
- Pidgin::IMHtml imhtml
-
-gint
-gtk_imhtml_get_current_fontsize(imhtml)
- Pidgin::IMHtml imhtml
-
-gboolean
-gtk_imhtml_get_editable(imhtml)
- Pidgin::IMHtml imhtml
-
-void
-gtk_imhtml_clear_formatting(imhtml)
- Pidgin::IMHtml imhtml
-
-void
-gtk_imhtml_toggle_bold(imhtml)
- Pidgin::IMHtml imhtml
-
-void
-gtk_imhtml_toggle_italic(imhtml)
- Pidgin::IMHtml imhtml
-
-void
-gtk_imhtml_toggle_underline(imhtml)
- Pidgin::IMHtml imhtml
-
-void
-gtk_imhtml_toggle_strike(imhtml)
- Pidgin::IMHtml imhtml
-
-void
-gtk_imhtml_toggle_forecolor(imhtml, color)
- Pidgin::IMHtml imhtml
- const char * color
-
-void
-gtk_imhtml_toggle_backcolor(imhtml, color)
- Pidgin::IMHtml imhtml
- const char * color
-
-void
-gtk_imhtml_toggle_background(imhtml, color)
- Pidgin::IMHtml imhtml
- const char * color
-
-void
-gtk_imhtml_toggle_fontface(imhtml, face)
- Pidgin::IMHtml imhtml
- const char * face
-
-void
-gtk_imhtml_toggle_link(imhtml, url)
- Pidgin::IMHtml imhtml
- const char * url
-
-void
-gtk_imhtml_insert_smiley(imhtml, sml, smiley)
- Pidgin::IMHtml imhtml
- const char * sml
- char * smiley
-
-void
-gtk_imhtml_font_set_size(imhtml, size)
- Pidgin::IMHtml imhtml
- gint size
-
-void
-gtk_imhtml_font_shrink(imhtml)
- Pidgin::IMHtml imhtml
-
-void
-gtk_imhtml_font_grow(imhtml)
- Pidgin::IMHtml imhtml
-
-gchar_own *
-gtk_imhtml_get_markup(imhtml)
- Pidgin::IMHtml imhtml
-
-# /* ETAN Test this, and document well that it returns an arrayref */
-void
-gtk_imhtml_get_markup_lines(imhtml)
- Pidgin::IMHtml imhtml
-PREINIT:
- gint i;
- AV *lines;
- gchar **bufs;
-PPCODE:
- bufs = gtk_imhtml_get_markup_lines(imhtml);
- lines = newAV();
- for (i = 0; bufs[i] != NULL; i++) {
- av_push(lines, newSVpv(bufs[i], 0));
- }
- XPUSHs(sv_2mortal(newRV_noinc((SV *)lines)));
-
-MODULE = Pidgin::IMHtml PACKAGE = Pidgin::IMHtml::Scalable PREFIX = gtk_imhtml_image_
-PROTOTYPES: ENABLE
-
-void
-gtk_imhtml_image_free(scale)
- Pidgin::IMHtml::Scalable scale
-
-void
-gtk_imhtml_image_scale(scale, width, height)
- Pidgin::IMHtml::Scalable scale
- int width
- int height
-
-MODULE = Pidgin::IMHtml PACKAGE = Pidgin::IMHtml::Hr PREFIX = gtk_imhtml_hr_
-PROTOTYPES: ENABLE
-
-Pidgin::IMHtml::Scalable
-gtk_imhtml_hr_new()
-
-void
-gtk_imhtml_hr_free(scale)
- Pidgin::IMHtml::Scalable scale
-
-void
-gtk_imhtml_hr_scale(scale, width, height)
- Pidgin::IMHtml::Scalable scale
- int width
- int height
-
-MODULE = Pidgin::IMHtml PACKAGE = Pidgin::IMHtml::Search PREFIX = gtk_imhtml_search_
-PROTOTYPES: ENABLE
-
-gboolean
-gtk_imhtml_search_find(imhtml, text)
- Pidgin::IMHtml imhtml
- const gchar * text
-
-void
-gtk_imhtml_search_clear(imhtml)
- Pidgin::IMHtml imhtml
diff --git a/pidgin/plugins/perl/common/GtkIMHtmlToolbar.xs b/pidgin/plugins/perl/common/GtkIMHtmlToolbar.xs
deleted file mode 100644
index 71b7cfbab7..0000000000
--- a/pidgin/plugins/perl/common/GtkIMHtmlToolbar.xs
+++ /dev/null
@@ -1,22 +0,0 @@
-#include "gtkmodule.h"
-
-/* This can't work at the moment since I don't have a typemap for Gtk::Widget.
- * I thought about using the one from libgtk2-perl but wasn't sure how to go
- * about doing that.
-
-Gtk::Widget
-gtk_imhtmltoolbar_new()
-
-void
-gtk_imhtmltoolbar_attach(toolbar, imhtml)
- Pidgin::IMHtmlToolbar toolbar
- Gtk::Widget imhtml
-*/
-
-MODULE = Pidgin::IMHtmlToolbar PACKAGE = Pidgin::IMHtmlToolbar PREFIX = gtk_imhtmltoolbar_
-PROTOTYPES: ENABLE
-
-void
-gtk_imhtmltoolbar_associate_smileys(toolbar, proto_id)
- Pidgin::IMHtmlToolbar toolbar
- const char * proto_id
diff --git a/pidgin/plugins/perl/common/GtkStatusBox.xs b/pidgin/plugins/perl/common/GtkStatusBox.xs
index 2a51ed074e..54d4102145 100644
--- a/pidgin/plugins/perl/common/GtkStatusBox.xs
+++ b/pidgin/plugins/perl/common/GtkStatusBox.xs
@@ -36,11 +36,6 @@ void
pidgin_status_box_pulse_connecting(status_box)
Pidgin::StatusBox status_box
-void
-pidgin_status_box_set_buddy_icon(status_box, img)
- Pidgin::StatusBox status_box
- Purple::StoredImage img
-
gchar_own *
pidgin_status_box_get_message(status_box)
Pidgin::StatusBox status_box
diff --git a/pidgin/plugins/perl/common/GtkThemes.xs b/pidgin/plugins/perl/common/GtkThemes.xs
deleted file mode 100644
index 6fe140041c..0000000000
--- a/pidgin/plugins/perl/common/GtkThemes.xs
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "gtkmodule.h"
-
-MODULE = Pidgin::Themes PACKAGE = Pidgin::Themes PREFIX = pidgin_themes_
-PROTOTYPES: ENABLE
-
-void
-pidgin_themes_init()
-
-gboolean
-pidgin_themes_smileys_disabled()
-
-void
-pidgin_themes_smiley_theme_probe()
-
-void
-pidgin_themes_load_smiley_theme(file, load)
- const char * file
- gboolean load
-
-void
-pidgin_themes_get_proto_smileys(id)
- const char * id
-PREINIT:
- GSList *l;
-PPCODE:
- for (l = pidgin_themes_get_proto_smileys(id); l != NULL; l = l->next) {
- XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Pidgin::IMHtml::Smiley")));
- }
diff --git a/pidgin/plugins/perl/common/MANIFEST b/pidgin/plugins/perl/common/MANIFEST
index 8db88a494e..4cd7561e69 100644
--- a/pidgin/plugins/perl/common/MANIFEST
+++ b/pidgin/plugins/perl/common/MANIFEST
@@ -6,8 +6,6 @@ GtkConvWin.xs
GtkDebug.xs
GtkDialogs.xs
GtkFt.xs
-GtkIMHtml.xs
-GtkIMHtmlToolbar.xs
GtkLog.xs
GtkMenuTray.xs
GtkPlugin.xs
@@ -20,7 +18,6 @@ GtkSavedStatuses.xs
GtkSession.xs
GtkSound.xs
GtkStatusBox.xs
-GtkThemes.xs
GtkUtils.xs
MANIFEST
Pidgin.pm
diff --git a/pidgin/plugins/perl/common/Makefile.PL.in b/pidgin/plugins/perl/common/Makefile.PL.in
index 030bf49116..496d73a5a0 100644
--- a/pidgin/plugins/perl/common/Makefile.PL.in
+++ b/pidgin/plugins/perl/common/Makefile.PL.in
@@ -4,19 +4,19 @@ use ExtUtils::MakeMaker;
# of the Makefile that is written.
WriteMakefile(
'NAME' => 'Pidgin',
- 'VERSION_FROM' => '@srcdir@/Pidgin.pm', # finds $VERSION
+ 'VERSION' => '@VERSION@',
($] >= 5.005 ? ## Add these new keywords supported since 5.005
('ABSTRACT_FROM' => '@srcdir@/Pidgin.pm', # finds $ABSTRACT
- 'AUTHOR' => 'Pidgin <http://pidgin.im/>') : ()),
- 'DEFINE' => '@DEBUG_CFLAGS@',
+ 'AUTHOR' => 'Pidgin <https://pidgin.im/>') : ()),
+ 'DEFINE' => '@DEBUG_CFLAGS@ -Wno-float-equal',
'dynamic_lib' => { 'OTHERLDFLAGS' => '@LDFLAGS@' },
- 'INC' => '-I. -I@srcdir@ -I@top_srcdir@ -I@top_srcdir@/libpurple -I@top_srcdir@/pidgin @GTK_CFLAGS@',
+ 'INC' => '-I. -I@srcdir@ -I@top_srcdir@ -I@top_builddir@ -I@top_srcdir@/libpurple -I@top_srcdir@/pidgin @GTK_CFLAGS@ @WEBKIT_CFLAGS@ -DHAVE_CONFIG_H',
'OBJECT' => '$(O_FILES)', # link all the C files too
'TYPEMAPS' => ["@top_srcdir@/libpurple/plugins/perl/common/typemap"],
# 'OPTIMIZE' => '-g', # For debugging.
'INSTALLDIRS' => 'vendor',
'INSTALL_BASE' => '$(prefix)',
- 'INSTALLVENDORARCH' => '$(libdir)/pidgin/perl',
+ 'INSTALLVENDORARCH' => '$(libdir)/pidgin-$(PURPLE_MAJOR_VERSION)/perl',
'INSTALLVENDORMAN3DIR' => '$(mandir)/man3',
'macro' => {
'prefix' => '@prefix@',
diff --git a/pidgin/plugins/perl/common/Makefile.mingw b/pidgin/plugins/perl/common/Makefile.mingw
index 0de0f2ea97..6b59764c01 100644
--- a/pidgin/plugins/perl/common/Makefile.mingw
+++ b/pidgin/plugins/perl/common/Makefile.mingw
@@ -31,11 +31,15 @@ INCLUDE_PATHS = -I. \
-I$(GTK_TOP)/include/glib-2.0 \
-I$(GTK_TOP)/include/gtk-2.0 \
-I$(GTK_TOP)/include/pango-1.0 \
+ -I$(GTK_TOP)/include/gdk-pixbuf-2.0 \
-I$(GTK_TOP)/lib/glib-2.0/include \
-I$(GTK_TOP)/lib/gtk-2.0/include \
- -I$(PERL_LIB_TOP)/CORE
+ -I$(LIBSOUP_TOP)/include/libsoup-2.4 \
+ -I$(WEBKITGTK_TOP)/include/webkitgtk-1.0 \
+ -I$(PERL_LIB_TOP)/include
-LIB_PATHS += -L$(PERL_LIB_TOP) \
+LIB_PATHS += \
+ -L$(PERL_LIB_TOP)/lib \
-L$(PURPLE_TOP) \
-L$(PIDGIN_TOP) \
-L$(PURPLE_PERL_TOP) \
@@ -54,8 +58,6 @@ XS_FILES = \
GtkDebug.xs \
GtkDialogs.xs \
GtkFt.xs \
- GtkIMHtml.xs \
- GtkIMHtmlToolbar.xs \
GtkLog.xs \
GtkMenuTray.xs \
GtkPlugin.xs \
@@ -67,7 +69,6 @@ XS_FILES = \
GtkSavedStatuses.xs \
GtkSound.xs \
GtkStatusBox.xs \
- GtkThemes.xs \
GtkUtils.xs
diff --git a/pidgin/plugins/perl/common/Pidgin.pm b/pidgin/plugins/perl/common/Pidgin.pm
index cc3f6b10b9..5fe7bbe12b 100644
--- a/pidgin/plugins/perl/common/Pidgin.pm
+++ b/pidgin/plugins/perl/common/Pidgin.pm
@@ -5,8 +5,6 @@ use strict;
use warnings;
use Carp;
-our $VERSION = '0.01';
-
use Purple;
require XSLoader;
@@ -36,7 +34,7 @@ write perl scripts that can be loaded in Pidgin as plugins. The script can
interact with IMs, chats, accounts, the buddy list, pidgin signals, and more.
The API for the perl interface is very similar to that of the Pidgin C API,
-which can be viewed at http://developer.pidgin.im/doxygen/ or in the header files
+which can be viewed at https://developer.pidgin.im/doxygen/ or in the header files
in the Pidgin source tree.
=head1 FUNCTIONS
@@ -46,11 +44,11 @@ in the Pidgin source tree.
=back
=head1 SEE ALSO
-Pidgin C API documentation - http://developer.pidgin.im/doxygen/
+Pidgin C API documentation - https://developer.pidgin.im/doxygen/
The Pidgin perl module.
-Pidgin website - http://pidgin.im/
+Pidgin website - https://pidgin.im/
=head1 AUTHOR
diff --git a/pidgin/plugins/perl/common/Pidgin.xs b/pidgin/plugins/perl/common/Pidgin.xs
index d9e5afe4ca..95ff451d74 100644
--- a/pidgin/plugins/perl/common/Pidgin.xs
+++ b/pidgin/plugins/perl/common/Pidgin.xs
@@ -14,8 +14,6 @@ PIDGIN_PERL_BOOT_PROTO(Conversation);
PIDGIN_PERL_BOOT_PROTO(Conversation__Window);
PIDGIN_PERL_BOOT_PROTO(Debug);
PIDGIN_PERL_BOOT_PROTO(Dialogs);
-PIDGIN_PERL_BOOT_PROTO(IMHtml);
-PIDGIN_PERL_BOOT_PROTO(IMHtmlToolbar);
PIDGIN_PERL_BOOT_PROTO(Log);
PIDGIN_PERL_BOOT_PROTO(MenuTray);
PIDGIN_PERL_BOOT_PROTO(Plugin);
@@ -30,7 +28,6 @@ PIDGIN_PERL_BOOT_PROTO(Session);
#endif
PIDGIN_PERL_BOOT_PROTO(Sound);
PIDGIN_PERL_BOOT_PROTO(StatusBox);
-PIDGIN_PERL_BOOT_PROTO(Themes);
PIDGIN_PERL_BOOT_PROTO(Utils);
PIDGIN_PERL_BOOT_PROTO(Xfer);
@@ -45,8 +42,6 @@ BOOT:
PIDGIN_PERL_BOOT(Conversation__Window);
PIDGIN_PERL_BOOT(Debug);
PIDGIN_PERL_BOOT(Dialogs);
- PIDGIN_PERL_BOOT(IMHtml);
- PIDGIN_PERL_BOOT(IMHtmlToolbar);
PIDGIN_PERL_BOOT(Log);
PIDGIN_PERL_BOOT(MenuTray);
PIDGIN_PERL_BOOT(Plugin);
@@ -61,6 +56,5 @@ BOOT:
#endif
PIDGIN_PERL_BOOT(Sound);
PIDGIN_PERL_BOOT(StatusBox);
- PIDGIN_PERL_BOOT(Themes);
PIDGIN_PERL_BOOT(Utils);
PIDGIN_PERL_BOOT(Xfer);
diff --git a/pidgin/plugins/perl/common/gtkmodule.h b/pidgin/plugins/perl/common/gtkmodule.h
index 8ebdf8afb2..1bb054a142 100644
--- a/pidgin/plugins/perl/common/gtkmodule.h
+++ b/pidgin/plugins/perl/common/gtkmodule.h
@@ -30,9 +30,7 @@ typedef struct group *Pidgin__Group;
#include "gtkconvwin.h"
#include "gtkdebug.h"
#include "gtkdialogs.h"
-#include "gtkft.h"
-#include "gtkimhtml.h"
-#include "gtkimhtmltoolbar.h"
+#include "gtkxfer.h"
#include "gtklog.h"
#include "gtkmenutray.h"
#include "gtkplugin.h"
@@ -45,7 +43,6 @@ typedef struct group *Pidgin__Group;
#include "gtksession.h"
#include "gtksound.h"
#include "gtkstatusbox.h"
-#include "gtkthemes.h"
#include "gtkutils.h"
/* gtkaccount.h */
@@ -60,23 +57,12 @@ typedef PidginConversation * Pidgin__Conversation;
typedef PidginUnseenState Pidgin__UnseenState;
/* gtkconvwin.h */
-typedef PidginWindow * Pidgin__Conversation__Window;
+typedef PidginConvWindow * Pidgin__Conversation__Window;
typedef PidginConvPlacementFunc Pidgin__Conversation__PlacementFunc;
-/* gtkft.h */
+/* gtkxfer.h */
typedef PidginXferDialog * Pidgin__Xfer__Dialog;
-/* gtkimhtml.h */
-typedef GtkIMHtml * Pidgin__IMHtml;
-typedef GtkIMHtmlButtons Pidgin__IMHtml__Buttons;
-typedef GtkIMHtmlFuncs * Pidgin__IMHtml__Funcs;
-typedef GtkIMHtmlScalable * Pidgin__IMHtml__Scalable;
-typedef GtkIMHtmlSmiley * Pidgin__IMHtml__Smiley;
-typedef GtkIMHtmlOptions Pidgin__IMHtml__Options;
-
-/* gtkimhtmltoolbar.h */
-typedef GtkIMHtmlToolbar * Pidgin__IMHtmlToolbar;
-
/* gtkmenutray.h */
typedef PidginMenuTray * Pidgin__MenuTray;
diff --git a/pidgin/plugins/perl/common/typemap b/pidgin/plugins/perl/common/typemap
index 56a21e56f1..7531a7952a 100644
--- a/pidgin/plugins/perl/common/typemap
+++ b/pidgin/plugins/perl/common/typemap
@@ -7,13 +7,6 @@ Pidgin::Conversation T_PurpleObj
Pidgin::Conversation::PlacementFunc T_PurpleObj
Pidgin::Conversation::Window T_PurpleObj
Pidgin::Xfer::Dialog T_PurpleObj
-Pidgin::IMHtml T_PurpleObj
-Pidgin::IMHtml::Buttons T_IV
-Pidgin::IMHtml::Funcs T_PurpleObj
-Pidgin::IMHtml::Scalable T_PurpleObj
-Pidgin::IMHtml::Smiley T_PurpleObj
-Pidgin::IMHtml::Options T_IV
-Pidgin::IMHtmlToolbar T_PurpleObj
Pidgin::MenuTray T_PurpleObj
Pidgin::StatusBox T_PurpleObj
Pidgin::UnseenState T_IV
diff --git a/pidgin/plugins/pidgininc.c b/pidgin/plugins/pidgininc.c
index 7e03243371..e3507cafcb 100644
--- a/pidgin/plugins/pidgininc.c
+++ b/pidgin/plugins/pidgininc.c
@@ -1,10 +1,10 @@
+/* When writing a third-party plugin, do not include libpurple's internal.h
+ * included below. This file is for internal libpurple use only. We're including
+ * it here for our own convenience. */
#include "internal.h"
-#include "plugin.h"
-#include "account.h"
-#include "connection.h"
-#include "conversation.h"
-#include "version.h"
+/* This file defines PURPLE_PLUGINS and includes all the libpurple headers */
+#include <purple.h>
/* include UI for pidgin_dialogs_about() */
#include "gtkplugin.h"
@@ -49,9 +49,9 @@ static void
bud(PurpleBuddy *who)
{
PurpleAccount *acct = who->account;
- PurpleConversation *conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, who->name);
+ PurpleConversation *conv = purple_im_conversation_new(acct, who->name);
- purple_conv_im_send(PURPLE_CONV_IM(conv), "Hello!");
+ purple_im_conversation_send(PURPLE_CONV_IM(conv), "Hello!");
}
/*
diff --git a/pidgin/plugins/pidginrc.c b/pidgin/plugins/pidginrc.c
deleted file mode 100644
index 3d9bc9c9eb..0000000000
--- a/pidgin/plugins/pidginrc.c
+++ /dev/null
@@ -1,704 +0,0 @@
-/**
- * @file pidginrc.c Pidgin GTK+ resource control plugin.
- * @ingroup pidgin
- */
-
-/* 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 "gtkplugin.h"
-#include "gtkprefs.h"
-#include "gtkutils.h"
-#include "util.h"
-#include "version.h"
-
-static guint pref_callback;
-
-static const gchar *color_prefs[] = {
- "/plugins/gtk/purplerc/color/GtkIMHtml::hyperlink-color",
- "/plugins/gtk/purplerc/color/GtkIMHtml::hyperlink-visited-color",
- "/plugins/gtk/purplerc/color/GtkIMHtml::send-name-color",
- "/plugins/gtk/purplerc/color/GtkIMHtml::receive-name-color",
- "/plugins/gtk/purplerc/color/GtkIMHtml::highlight-name-color",
- "/plugins/gtk/purplerc/color/GtkIMHtml::action-name-color",
- "/plugins/gtk/purplerc/color/GtkIMHtml::typing-notification-color"
-};
-static const gchar *color_prefs_set[] = {
- "/plugins/gtk/purplerc/set/color/GtkIMHtml::hyperlink-color",
- "/plugins/gtk/purplerc/set/color/GtkIMHtml::hyperlink-visited-color",
- "/plugins/gtk/purplerc/set/color/GtkIMHtml::send-name-color",
- "/plugins/gtk/purplerc/set/color/GtkIMHtml::receive-name-color",
- "/plugins/gtk/purplerc/set/color/GtkIMHtml::highlight-name-color",
- "/plugins/gtk/purplerc/set/color/GtkIMHtml::action-name-color",
- "/plugins/gtk/purplerc/set/color/GtkIMHtml::typing-notification-color"
-};
-static const gchar *color_names[] = {
- N_("Hyperlink Color"),
- N_("Visited Hyperlink Color"),
- N_("Sent Message Name Color"),
- N_("Received Message Name Color"),
- N_("Highlighted Message Name Color"),
- N_("Action Message Name Color"),
- N_("Typing Notification Color")
-};
-static GtkWidget *color_widgets[G_N_ELEMENTS(color_prefs)];
-
-static const gchar *widget_size_prefs[] = {
- "/plugins/gtk/purplerc/size/GtkTreeView::horizontal_separator"
-};
-static const gchar *widget_size_prefs_set[] = {
- "/plugins/gtk/purplerc/set/size/GtkTreeView::horizontal_separator"
-};
-static const gchar *widget_size_names[] = {
- N_("GtkTreeView Horizontal Separation")
-};
-static GtkWidget *widget_size_widgets[G_N_ELEMENTS(widget_size_prefs)];
-
-static const gchar *font_prefs[] = {
- "/plugins/gtk/purplerc/font/*pidgin_conv_entry",
- "/plugins/gtk/purplerc/font/*pidgin_conv_imhtml",
- "/plugins/gtk/purplerc/font/*pidgin_request_imhtml",
- "/plugins/gtk/purplerc/font/*pidgin_notify_imhtml",
-};
-static const gchar *font_prefs_set[] = {
- "/plugins/gtk/purplerc/set/font/*pidgin_conv_entry",
- "/plugins/gtk/purplerc/set/font/*pidgin_conv_imhtml",
- "/plugins/gtk/purplerc/set/font/*pidgin_request_imhtml",
- "/plugins/gtk/purplerc/set/font/*pidgin_notify_imhtml",
-};
-static const gchar *font_names[] = {
- N_("Conversation Entry"),
- N_("Conversation History"),
- N_("Request Dialog"),
- N_("Notify Dialog")
-};
-static GtkWidget *font_widgets[G_N_ELEMENTS(font_prefs)];
-
-/*
-static const gchar *widget_bool_prefs[] = {
-};
-static const gchar *widget_bool_prefs_set[] = {
-};
-static const gchar *widget_bool_names[] = {
-};
-static GtkWidget *widget_bool_widgets[G_N_ELEMENTS(widget_bool_prefs)];
-*/
-
-static GString *
-make_gtkrc_string(void)
-{
- gint i;
- gchar *prefbase = NULL;
- GString *style_string = g_string_new("");
-
- if (purple_prefs_get_bool("/plugins/gtk/purplerc/set/gtk-font-name")) {
- const gchar *pref = purple_prefs_get_string("/plugins/gtk/purplerc/gtk-font-name");
-
- if (pref != NULL && strcmp(pref, "")) {
- g_string_append_printf(style_string,
- "gtk-font-name = \"%s\"\n",
- pref);
- }
- }
-
- if (purple_prefs_get_bool("/plugins/gtk/purplerc/set/gtk-key-theme-name")) {
- const gchar *pref = purple_prefs_get_string("/plugins/gtk/purplerc/gtk-key-theme-name");
-
- if (pref != NULL && strcmp(pref, "")) {
- g_string_append_printf(style_string,
- "gtk-key-theme-name = \"%s\"\n",
- pref);
- }
- }
-
- g_string_append(style_string, "style \"purplerc_style\"\n{");
-
- if(purple_prefs_get_bool("/plugins/gtk/purplerc/set/disable-typing-notification")) {
- g_string_append(style_string, "\tGtkIMHtml::typing-notification-enable = 0\n");
- }
-
- for (i = 0; i < G_N_ELEMENTS(color_prefs); i++) {
- if (purple_prefs_get_bool(color_prefs_set[i])) {
- const gchar *pref;
-
- pref = purple_prefs_get_string(color_prefs[i]);
- if (pref != NULL && strcmp(pref, "")) {
- prefbase = g_path_get_basename(color_prefs[i]);
- g_string_append_printf(style_string,
- "\n\t%s = \"%s\"",
- prefbase, pref);
- g_free(prefbase);
- }
- }
- }
-
- for (i = 0; i < G_N_ELEMENTS(widget_size_prefs); i++) {
- if (purple_prefs_get_bool(widget_size_prefs_set[i])) {
- prefbase = g_path_get_basename(widget_size_prefs[i]);
- g_string_append_printf(style_string,
- "\n\t%s = %d", prefbase,
- purple_prefs_get_int(widget_size_prefs[i]));
- g_free(prefbase);
- }
- }
-
- /*
- for (i = 0; i < G_N_ELEMENTS(widget_bool_prefs); i++) {
- if (purple_prefs_get_bool(widget_bool_prefs_set[i])) {
- prefbase = g_path_get_basename(widget_bool_prefs[i]);
- g_string_append_printf(style_string,
- "\t%s = %d\n", prefbase,
- purple_prefs_get_bool(widget_bool_prefs[i]));
- g_free(prefbase);
- }
- }
- */
-
- g_string_append(style_string, "\n}\nwidget_class \"*\" style \"purplerc_style\"\n");
-
- for (i = 0; i < G_N_ELEMENTS(font_prefs); i++) {
- if (purple_prefs_get_bool(font_prefs_set[i])) {
- const gchar *pref;
-
- pref = purple_prefs_get_string(font_prefs[i]);
- if (pref != NULL && strcmp(pref, "")) {
- prefbase = g_path_get_basename(font_prefs[i]);
- g_string_append_printf(style_string,
- "style \"%s_style\"\n{\n"
- "\tfont_name = \"%s\"\n}"
- "\nwidget \"%s\" "
- "style \"%s_style\"\n",
- prefbase, pref,
- prefbase, prefbase);
- g_free(prefbase);
- }
- }
- }
-
- return style_string;
-}
-
-static void
-purplerc_make_changes(void)
-{
- GString *str = make_gtkrc_string();
- GtkSettings *setting = NULL;
-
- gtk_rc_parse_string(str->str);
- g_string_free(str, TRUE);
-
- setting = gtk_settings_get_default();
- gtk_rc_reset_styles(setting);
-}
-
-static void
-purplerc_write(GtkWidget *widget, gpointer data)
-{
- GString *str = make_gtkrc_string();
- str = g_string_prepend(str, "# This file automatically written by the Pidgin GTK+ Theme Control plugin.\n# Any changes to this file will be overwritten by the plugin when told to\n# write the settings again.\n# The FAQ (http://developer.pidgin.im/wiki/FAQ) contains some further examples\n# of possible pidgin gtkrc settings.\n");
- purple_util_write_data_to_file("gtkrc-2.0", str->str, -1);
- g_string_free(str, TRUE);
-}
-
-static void
-purplerc_reread(GtkWidget *widget, gpointer data)
-{
- gtk_rc_reparse_all();
- /* I don't know if this is necessary but if not it shouldn't hurt. */
- purplerc_make_changes();
-}
-
-static void
-purplerc_pref_changed_cb(const char *name, PurplePrefType type,
- gconstpointer value, gpointer data)
-{
- purplerc_make_changes();
-}
-
-static void
-purplerc_color_response(GtkDialog *color_dialog, gint response, gpointer data)
-{
- gint subscript = GPOINTER_TO_INT(data);
-
- if (response == GTK_RESPONSE_OK) {
- GdkColor color;
- gchar colorstr[8];
-#if GTK_CHECK_VERSION(2,14,0)
- GtkWidget *colorsel =
- gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(color_dialog));
-#else
- GtkWidget *colorsel = GTK_COLOR_SELECTION_DIALOG(color_dialog)->colorsel;
-#endif
-
- gtk_color_selection_get_current_color(GTK_COLOR_SELECTION(colorsel), &color);
-
- g_snprintf(colorstr, sizeof(colorstr), "#%02X%02X%02X",
- color.red/256, color.green/256, color.blue/256);
-
- purple_prefs_set_string(color_prefs[subscript], colorstr);
- }
- gtk_widget_destroy(GTK_WIDGET(color_dialog));
-}
-
-static void
-purplerc_set_color(GtkWidget *widget, gpointer data)
-{
- GdkColor color;
- gchar title[128];
- const gchar *pref = NULL;
- GtkWidget *color_dialog = NULL;
- gint subscript = GPOINTER_TO_INT(data);
-
- g_snprintf(title, sizeof(title), _("Select Color for %s"),
- _(color_names[GPOINTER_TO_INT(data)]));
- color_dialog = gtk_color_selection_dialog_new(_("Select Color"));
- g_signal_connect(G_OBJECT(color_dialog), "response",
- G_CALLBACK(purplerc_color_response), data);
-
- pref = purple_prefs_get_string(color_prefs[subscript]);
-
- if (pref != NULL && strcmp(pref, "")) {
- if (gdk_color_parse(pref, &color)) {
-#if GTK_CHECK_VERSION(2,14,0)
- gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(
- gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(color_dialog))),
- &color);
-#else
- gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(color_dialog)->colorsel), &color);
-#endif
- }
- }
-
- gtk_window_present(GTK_WINDOW(color_dialog));
-}
-
-static void
-purplerc_font_response(GtkDialog *font_dialog, gint response, gpointer data)
-{
- const gchar *prefpath;
- gint subscript = GPOINTER_TO_INT(data);
-
- if (response == GTK_RESPONSE_OK) {
- gchar *fontname = NULL;
-
- if (subscript == -1) {
- prefpath = "/plugins/gtk/purplerc/gtk-font-name";
- } else {
- prefpath = font_prefs[subscript];
- }
-
- fontname = gtk_font_selection_dialog_get_font_name(GTK_FONT_SELECTION_DIALOG(font_dialog));
-
- purple_prefs_set_string(prefpath, fontname);
- g_free(fontname);
- }
- gtk_widget_destroy(GTK_WIDGET(font_dialog));
-}
-
-static void
-purplerc_set_font(GtkWidget *widget, gpointer data)
-{
- gchar title[128];
- GtkWidget *font_dialog = NULL;
- gint subscript = GPOINTER_TO_INT(data);
- const gchar *pref = NULL, *prefpath = NULL;
-
- if (subscript == -1) {
- g_snprintf(title, sizeof(title), _("Select Interface Font"));
- prefpath = "/plugins/gtk/purplerc/gtk-font-name";
- } else {
- g_snprintf(title, sizeof(title), _("Select Font for %s"),
- _(font_names[subscript]));
- prefpath = font_prefs[subscript];
- }
-
- font_dialog = gtk_font_selection_dialog_new(title);
- g_signal_connect(G_OBJECT(font_dialog), "response",
- G_CALLBACK(purplerc_font_response), data);
-
- pref = purple_prefs_get_string(prefpath);
-
- if (pref != NULL && strcmp(pref, "")) {
- gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(font_dialog), pref);
- }
-
- gtk_window_present(GTK_WINDOW(font_dialog));
-}
-
-static gboolean
-purplerc_plugin_load(PurplePlugin *plugin)
-{
- purplerc_make_changes();
-
- pref_callback = purple_prefs_connect_callback(plugin,
- "/plugins/gtk/purplerc",
- purplerc_pref_changed_cb,
- NULL);
-
- return TRUE;
-}
-
-static gboolean
-purplerc_plugin_unload(PurplePlugin *plugin)
-{
- purple_prefs_disconnect_callback(pref_callback);
-
- return TRUE;
-}
-
-static GtkWidget *
-purplerc_make_interface_vbox(void)
-{
- GtkWidget *vbox = NULL, *hbox = NULL, *check = NULL;
- GtkSizeGroup *labelsg = NULL;
- gint i;
-
- vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
- labelsg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
-
- gtk_container_set_border_width(GTK_CONTAINER(vbox), PIDGIN_HIG_BORDER);
-
- for (i = 0; i < G_N_ELEMENTS(color_prefs); i++) {
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
- gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-
- check = pidgin_prefs_checkbox(_(color_names[i]),
- color_prefs_set[i], hbox);
- gtk_size_group_add_widget(labelsg, check);
-
- color_widgets[i] = pidgin_pixbuf_button_from_stock("",
- GTK_STOCK_SELECT_COLOR, PIDGIN_BUTTON_HORIZONTAL);
- gtk_box_pack_start(GTK_BOX(hbox), color_widgets[i], FALSE,
- FALSE, 0);
- gtk_widget_set_sensitive(color_widgets[i],
- purple_prefs_get_bool(color_prefs_set[i]));
- g_signal_connect(G_OBJECT(check), "toggled",
- G_CALLBACK(pidgin_toggle_sensitive),
- color_widgets[i]);
- g_signal_connect(G_OBJECT(color_widgets[i]), "clicked",
- G_CALLBACK(purplerc_set_color),
- GINT_TO_POINTER(i));
- }
-
- g_object_unref(labelsg);
-
- return vbox;
-}
-
-static GtkWidget *
-purplerc_make_fonts_vbox(void)
-{
- GtkWidget *vbox = NULL, *hbox = NULL, *check = NULL, *widget = NULL;
- GtkSizeGroup *labelsg = NULL;
- int i;
-
- vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
- labelsg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
-
- gtk_container_set_border_width(GTK_CONTAINER(vbox), PIDGIN_HIG_BORDER);
-
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
- gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-
- check = pidgin_prefs_checkbox(_("GTK+ Interface Font"),
- "/plugins/gtk/purplerc/set/gtk-font-name",
- hbox);
- gtk_size_group_add_widget(labelsg, check);
-
- widget = pidgin_pixbuf_button_from_stock("", GTK_STOCK_SELECT_FONT,
- PIDGIN_BUTTON_HORIZONTAL);
- gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);
- gtk_widget_set_sensitive(widget,
- purple_prefs_get_bool("/plugins/gtk/purplerc/set/gtk-font-name"));
- g_signal_connect(G_OBJECT(check), "toggled",
- G_CALLBACK(pidgin_toggle_sensitive), widget);
- g_signal_connect(G_OBJECT(widget), "clicked",
- G_CALLBACK(purplerc_set_font), GINT_TO_POINTER(-1));
-
- for (i = 0; i < G_N_ELEMENTS(font_prefs); i++) {
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
- gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-
- check = pidgin_prefs_checkbox(_(font_names[i]),
- font_prefs_set[i], hbox);
- gtk_size_group_add_widget(labelsg, check);
-
- font_widgets[i] = pidgin_pixbuf_button_from_stock("",
- GTK_STOCK_SELECT_FONT, PIDGIN_BUTTON_HORIZONTAL);
- gtk_box_pack_start(GTK_BOX(hbox), font_widgets[i], FALSE,
- FALSE, 0);
- gtk_widget_set_sensitive(font_widgets[i],
- purple_prefs_get_bool(font_prefs_set[i]));
- g_signal_connect(G_OBJECT(check), "toggled",
- G_CALLBACK(pidgin_toggle_sensitive),
- font_widgets[i]);
- g_signal_connect(G_OBJECT(font_widgets[i]), "clicked",
- G_CALLBACK(purplerc_set_font),
- GINT_TO_POINTER(i));
- }
-
- g_object_unref(labelsg);
-
- return vbox;
-}
-
-static GtkWidget *
-purplerc_make_misc_vbox(void)
-{
- /* Note: Intentionally not using the size group argument to the
- * pidgin_prefs_labeled_* functions they only add the text label to
- * the size group not the whole thing, which isn't what I want. */
- GtkWidget *vbox = NULL, *hbox = NULL, *check = NULL, *widget = NULL;
- GtkSizeGroup *labelsg = NULL;
- int i;
-
- vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
- labelsg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
-
- gtk_container_set_border_width(GTK_CONTAINER(vbox), PIDGIN_HIG_BORDER);
-
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
- gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-
- check = pidgin_prefs_checkbox(_("GTK+ Text Shortcut Theme"),
- "/plugins/gtk/purplerc/set/gtk-key-theme-name",
- hbox);
- gtk_size_group_add_widget(labelsg, check);
-
- widget = pidgin_prefs_labeled_entry(hbox, "",
- "/plugins/gtk/purplerc/gtk-key-theme-name",
- NULL);
- gtk_widget_set_sensitive(widget,
- purple_prefs_get_bool("/plugins/gtk/purplerc/set/gtk-key-theme-name"));
- g_signal_connect(G_OBJECT(check), "toggled",
- G_CALLBACK(pidgin_toggle_sensitive), widget);
-
- for (i = 0; i < G_N_ELEMENTS(widget_size_prefs); i++) {
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
- gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-
- check = pidgin_prefs_checkbox(_(widget_size_names[i]),
- widget_size_prefs_set[i], hbox);
- gtk_size_group_add_widget(labelsg, check);
-
- widget_size_widgets[i] = pidgin_prefs_labeled_spin_button(hbox, "", widget_size_prefs[i], 0, 50, NULL);
- gtk_widget_set_sensitive(widget_size_widgets[i],
- purple_prefs_get_bool(widget_size_prefs_set[i]));
- g_signal_connect(G_OBJECT(check), "toggled",
- G_CALLBACK(pidgin_toggle_sensitive),
- widget_size_widgets[i]);
- }
-
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
- gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-
- pidgin_prefs_checkbox(_("Disable Typing Notification Text"),
- "/plugins/gtk/purplerc/set/disable-typing-notification", hbox);
-
- /* Widget boolean stuff */
- /*
- for (i = 0; i < G_N_ELEMENTS(widget_bool_prefs); i++) {
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
- gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-
- check = pidgin_prefs_checkbox(_(widget_bool_names[i]),
- widget_bool_prefs_set[i], hbox);
- gtk_size_group_add_widget(labelsg, check);
-
- widget_bool_widgets[i] = pidgin_prefs_checkbox("", widget_bool_prefs[i], hbox);
-
- gtk_widget_set_sensitive(widget_bool_widgets[i],
- purple_prefs_get_bool(widget_bool_prefs_set[i]));
- g_signal_connect(G_OBJECT(check), "toggled",
- G_CALLBACK(pidgin_toggle_sensitive),
- widget_bool_widgets[i]);
- }
- */
-
- g_object_unref(labelsg);
-
- return vbox;
-}
-
-static GtkWidget *
-purplerc_get_config_frame(PurplePlugin *plugin)
-{
- gchar *tmp;
- GtkWidget *check = NULL, *label = NULL;
- GtkWidget *ret = NULL, *hbox = NULL, *frame = NULL, *note = NULL;
-#ifndef _WIN32
- const gchar *homepath = "$HOME";
-#else
- const gchar *homepath = "\%APPDATA\%";
-#endif
-
- ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
- note = gtk_notebook_new();
- label = gtk_label_new(NULL);
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
-
- gtk_container_set_border_width(GTK_CONTAINER(ret), PIDGIN_HIG_BORDER);
-
- tmp = g_strdup_printf("<span weight=\"bold\">%s</span>", _("GTK+ Theme Control Settings"));
- gtk_label_set_markup(GTK_LABEL(label), tmp);
- g_free(tmp);
-
- gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
- gtk_box_pack_start(GTK_BOX(ret), hbox, FALSE, FALSE, 0);
- gtk_box_pack_start(GTK_BOX(ret), note, FALSE, FALSE, 0);
-
- label = gtk_label_new(_("Colors"));
- gtk_notebook_insert_page(GTK_NOTEBOOK(note), purplerc_make_interface_vbox(), label, -1);
-
- label = gtk_label_new(_("Fonts"));
- gtk_notebook_insert_page(GTK_NOTEBOOK(note), purplerc_make_fonts_vbox(), label, -1);
-
- label = gtk_label_new(_("Miscellaneous"));
- gtk_notebook_insert_page(GTK_NOTEBOOK(note), purplerc_make_misc_vbox(), label, -1);
-
- gtk_box_pack_start(GTK_BOX(ret), gtk_hseparator_new(), TRUE, TRUE, 0);
-
- frame = pidgin_make_frame(ret, _("Gtkrc File Tools"));
-
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
- gtk_box_pack_start(GTK_BOX(frame), hbox, FALSE, FALSE, 0);
-
- tmp = g_strdup_printf(_("Write settings to %s%sgtkrc-2.0"),
- homepath, G_DIR_SEPARATOR_S ".purple" G_DIR_SEPARATOR_S);
- check = gtk_button_new_with_label(tmp);
- g_free(tmp);
- gtk_box_pack_start(GTK_BOX(hbox), check, TRUE, TRUE, 0);
- g_signal_connect(G_OBJECT(check), "clicked",
- G_CALLBACK(purplerc_write), NULL);
-
- check = gtk_button_new_with_label(_("Re-read gtkrc files"));
- gtk_box_pack_start(GTK_BOX(hbox), check, TRUE, TRUE, 0);
- g_signal_connect(G_OBJECT(check), "clicked",
- G_CALLBACK(purplerc_reread), NULL);
-
- gtk_widget_show_all(ret);
-
-
- return ret;
-}
-
-static PidginPluginUiInfo purplerc_ui_info =
-{
- purplerc_get_config_frame,
- 0, /* page_num (Reserved) */
-
- /* padding */
- NULL,
- NULL,
- NULL,
- NULL
-};
-
-static PurplePluginInfo purplerc_info =
-{
- PURPLE_PLUGIN_MAGIC,
- PURPLE_MAJOR_VERSION,
- PURPLE_MINOR_VERSION,
- PURPLE_PLUGIN_STANDARD,
- PIDGIN_PLUGIN_TYPE,
- 0,
- NULL,
- PURPLE_PRIORITY_DEFAULT,
- "purplerc",
- N_("Pidgin GTK+ Theme Control"),
- DISPLAY_VERSION,
- N_("Provides access to commonly used gtkrc settings."),
- N_("Provides access to commonly used gtkrc settings."),
- "Etan Reisner <deryni@pidgin.im>",
- PURPLE_WEBSITE,
- purplerc_plugin_load,
- purplerc_plugin_unload,
- NULL,
- &purplerc_ui_info,
- NULL,
- NULL,
- NULL,
-
- /* padding */
- NULL,
- NULL,
- NULL,
- NULL
-};
-
-static void
-purplerc_init(PurplePlugin *plugin)
-{
- gint i;
-
- purple_prefs_add_none("/plugins");
- purple_prefs_add_none("/plugins/gtk");
- purple_prefs_add_none("/plugins/gtk/purplerc");
- purple_prefs_add_none("/plugins/gtk/purplerc/set");
-
- purple_prefs_add_string("/plugins/gtk/purplerc/gtk-font-name", "");
- purple_prefs_add_bool("/plugins/gtk/purplerc/set/gtk-font-name", FALSE);
-
- purple_prefs_add_string("/plugins/gtk/purplerc/gtk-key-theme-name", "");
- purple_prefs_add_bool("/plugins/gtk/purplerc/set/gtk-key-theme-name", FALSE);
-
- purple_prefs_add_none("/plugins/gtk/purplerc/color");
- purple_prefs_add_none("/plugins/gtk/purplerc/set/color");
- for (i = 0; i < G_N_ELEMENTS(color_prefs); i++) {
- purple_prefs_add_string(color_prefs[i], "");
- purple_prefs_add_bool(color_prefs_set[i], FALSE);
- }
-
- purple_prefs_add_none("/plugins/gtk/purplerc/size");
- purple_prefs_add_none("/plugins/gtk/purplerc/set/size");
- for (i = 0; i < G_N_ELEMENTS(widget_size_prefs); i++) {
- purple_prefs_add_int(widget_size_prefs[i], 0);
- purple_prefs_add_bool(widget_size_prefs_set[i], FALSE);
- }
-
- purple_prefs_add_none("/plugins/gtk/purplerc/font");
- purple_prefs_add_none("/plugins/gtk/purplerc/set/font");
- for (i = 0; i < G_N_ELEMENTS(font_prefs); i++) {
- purple_prefs_add_string(font_prefs[i], "");
- purple_prefs_add_bool(font_prefs_set[i], FALSE);
- }
-
- /*
- purple_prefs_add_none("/plugins/gtk/purplerc/bool");
- purple_prefs_add_none("/plugins/gtk/purplerc/set/bool");
- for (i = 0; i < G_N_ELEMENTS(widget_bool_prefs); i++) {
- purple_prefs_add_bool(widget_bool_prefs[i], TRUE);
- purple_prefs_add_bool(widget_bool_prefs_set[i], FALSE);
- }
- */
-
- purple_prefs_add_bool("/plugins/gtk/purplerc/disable-typing-notification", FALSE);
- purple_prefs_add_bool("/plugins/gtk/purplerc/set/disable-typing-notification", FALSE);
-
- /* remove old cursor color prefs */
- purple_prefs_remove("/plugins/gtk/purplerc/color/GtkWidget::cursor-color");
- purple_prefs_remove("/plugins/gtk/purplerc/color/GtkWidget::secondary-cursor-color");
- purple_prefs_remove("/plugins/gtk/purplerc/set/color/GtkWidget::cursor-color");
- purple_prefs_remove("/plugins/gtk/purplerc/set/color/GtkWidget::secondary-cursor-color");
-}
-
-PURPLE_INIT_PLUGIN(purplerc, purplerc_init, purplerc_info)
diff --git a/pidgin/plugins/raw.c b/pidgin/plugins/raw.c
index 2fafa7e095..f5c8ae8650 100644
--- a/pidgin/plugins/raw.c
+++ b/pidgin/plugins/raw.c
@@ -29,6 +29,7 @@
#include "prpl.h"
#include "version.h"
+#include "gtk3compat.h"
#include "gtkplugin.h"
#include "gtkutils.h"
@@ -126,7 +127,7 @@ plugin_load(PurplePlugin *plugin)
G_CALLBACK(window_closed_cb), NULL);
/* Main hbox */
- hbox = gtk_hbox_new(FALSE, 6);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
gtk_container_add(GTK_CONTAINER(window), hbox);
/* Account drop-down menu. */
diff --git a/pidgin/plugins/relnot.c b/pidgin/plugins/relnot.c
index 8d04aa3848..7bbb9cb6bc 100644
--- a/pidgin/plugins/relnot.c
+++ b/pidgin/plugins/relnot.c
@@ -36,6 +36,7 @@
#include "debug.h"
#include "gtkblist.h"
#include "gtkutils.h"
+#include "http.h"
#include "notify.h"
#include "pidginstock.h"
#include "prefs.h"
@@ -60,42 +61,20 @@ release_show()
purple_notify_uri(NULL, PURPLE_WEBSITE);
}
-static void
-version_fetch_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
- const gchar *response, size_t len, const gchar *error_message)
+static void version_fetch_cb(PurpleHttpConnection *hc,
+ PurpleHttpResponse *response, gpointer user_data)
{
gchar *cur_ver;
- const char *tmp, *changelog;
- char response_code[4];
+ const char *changelog;
GtkWidget *release_dialog;
GString *message;
int i = 0;
- if(error_message || !response || !len)
+ if(!purple_http_response_is_successful(response))
return;
- memset(response_code, '\0', sizeof(response_code));
- /* Parse the status code - the response should be in the form of "HTTP/?.? 200 ..." */
- if ((tmp = strstr(response, " ")) != NULL) {
- tmp++;
- /* Read the 3 digit status code */
- if (len - (tmp - response) > 3) {
- memcpy(response_code, tmp, 3);
- }
- }
-
- if (strcmp(response_code, "200") != 0) {
- purple_debug_error("relnot", "Didn't recieve a HTTP status code of 200.\n");
- return;
- }
-
- /* Go to the start of the data */
- if((changelog = strstr(response, "\r\n\r\n")) == NULL) {
- purple_debug_error("relnot", "Unable to find start of HTTP response data.\n");
- return;
- }
- changelog += 4;
+ changelog = purple_http_response_get_data(response, NULL);
while(changelog[i] && changelog[i] != '\n') i++;
@@ -131,7 +110,7 @@ do_check(void)
{
int last_check = purple_prefs_get_int("/plugins/gtk/relnot/last_check");
if(!last_check || time(NULL) - last_check > MIN_CHECK_INTERVAL) {
- gchar *url, *request;
+ gchar *url;
const char *host = "pidgin.im";
url = g_strdup_printf("https://%s/version.php?version=%s&build=%s",
@@ -144,18 +123,8 @@ do_check(void)
#endif
);
- request = g_strdup_printf(
- "GET %s HTTP/1.0\r\n"
- "Connection: close\r\n"
- "Accept: */*\r\n"
- "Host: %s\r\n\r\n",
- url,
- host);
-
- purple_util_fetch_url_request_len(url, TRUE, NULL, FALSE,
- request, TRUE, -1, version_fetch_cb, NULL);
+ purple_http_get(NULL, version_fetch_cb, NULL, url);
- g_free(request);
g_free(url);
purple_prefs_set_int("/plugins/gtk/relnot/last_check", time(NULL));
diff --git a/pidgin/plugins/screencap.c b/pidgin/plugins/screencap.c
new file mode 100644
index 0000000000..21d0766056
--- /dev/null
+++ b/pidgin/plugins/screencap.c
@@ -0,0 +1,1047 @@
+/*
+ * Screen Capture - a plugin that allows taking screenshots and sending them
+ * to your buddies as inline images.
+ *
+ * Copyright (C) 2014, Tomasz Wasilczyk <twasilczyk@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
+ */
+
+#include "internal.h"
+
+#include <gdk/gdkkeysyms.h>
+
+#include "debug.h"
+#include "glibcompat.h"
+#include "version.h"
+
+#include "gtk3compat.h"
+#include "gtkconv.h"
+#include "gtkplugin.h"
+#include "gtkutils.h"
+#include "gtkwebviewtoolbar.h"
+#include "pidginstock.h"
+
+#define SCRNCAP_SHOOTING_TIMEOUT 500
+#define SCRNCAP_DEFAULT_COLOR "#FFFF00000000"
+
+enum
+{
+ SCRNCAP_RESPONSE_COLOR
+};
+
+static gboolean is_shooting = FALSE;
+static guint shooting_timeout = 0;
+static GtkWidget *current_window = NULL;
+
+static gint crop_origin_x, crop_origin_y;
+static gboolean crop_active;
+static gint crop_x, crop_y, crop_w, crop_h;
+
+static gint draw_origin_x, draw_origin_y;
+static gboolean draw_active;
+
+static GdkColor brush_color = {0, 65535, 0, 0};
+static gint line_width = 2;
+
+/******************************************************************************
+ * libpidgin helper functions
+ ******************************************************************************/
+
+static inline void
+scrncap_conv_set_data(PidginConversation *gtkconv, const gchar *key,
+ gpointer value)
+{
+ g_return_if_fail(gtkconv != NULL);
+
+ g_object_set_data(G_OBJECT(gtkconv->tab_cont), key, value);
+}
+
+static inline gpointer
+scrncap_conv_get_data(PidginConversation *gtkconv, const gchar *key)
+{
+ g_return_val_if_fail(gtkconv != NULL, NULL);
+
+ return g_object_get_data(G_OBJECT(gtkconv->tab_cont), key);
+}
+
+/******************************************************************************
+ * GdkPixbuf helper functions
+ ******************************************************************************/
+
+static GdkPixbuf *
+scrncap_perform_screenshot(void)
+{
+ GdkWindow *root;
+ gint orig_x, orig_y;
+
+ root = gdk_get_default_root_window();
+ gdk_window_get_origin(root, &orig_x, &orig_y);
+
+ return gdk_pixbuf_get_from_window(root, 0, 0,
+ gdk_window_get_width(root), gdk_window_get_height(root));
+}
+
+static void
+scrncap_pixbuf_darken(GdkPixbuf *pixbuf)
+{
+ guchar *pixels;
+ int i, y, width, height, row_width, n_channels, rowstride, pad;
+
+ pixels = gdk_pixbuf_get_pixels(pixbuf);
+ width = gdk_pixbuf_get_width(pixbuf);
+ height = gdk_pixbuf_get_height(pixbuf);
+ n_channels = gdk_pixbuf_get_n_channels(pixbuf);
+ rowstride = gdk_pixbuf_get_rowstride(pixbuf);
+
+ row_width = width * n_channels;
+ pad = rowstride - row_width;
+ g_return_if_fail(pad >= 0);
+
+ for (y = 0; y < height; y++) {
+ for (i = 0; i < row_width; i++, pixels++)
+ *pixels /= 2;
+ pixels += pad;
+ }
+}
+
+static gboolean
+scrncap_pixbuf_to_image_cb(const gchar *buf, gsize count, GError **error,
+ gpointer _image)
+{
+ PurpleImage *image = PURPLE_IMAGE(_image);
+
+ purple_image_transfer_write(image, buf, count);
+
+ return TRUE;
+}
+
+static PurpleImage *
+scrncap_pixbuf_to_image(GdkPixbuf *pixbuf)
+{
+ PurpleImage *image;
+ GError *error = NULL;
+
+ image = purple_image_transfer_new();
+
+ gdk_pixbuf_save_to_callback(pixbuf, scrncap_pixbuf_to_image_cb, image,
+ "png", &error, NULL);
+
+ purple_image_transfer_close(image);
+
+ if (error != NULL) {
+ purple_debug_error("screencap", "Failed saving an image: %s",
+ error->message);
+ g_error_free(error);
+ g_object_unref(image);
+ return NULL;
+ }
+
+ if (purple_image_is_ready(image)) {
+ if (purple_image_get_extension(image) == NULL) {
+ purple_debug_error("screencap", "Invalid image format");
+ g_object_unref(image);
+ return NULL;
+ }
+ } else {
+ purple_debug_error("screencap", "Image is not ready");
+ g_object_unref(image);
+ return NULL;
+ }
+
+ return image;
+}
+
+/******************************************************************************
+ * Draw window
+ ******************************************************************************/
+
+static gboolean
+scrncap_drawing_area_btnpress(GtkWidget *draw_area, GdkEventButton *event,
+ gpointer _unused)
+{
+ if (draw_active)
+ return TRUE;
+
+ draw_origin_x = event->x;
+ draw_origin_y = event->y;
+ draw_active = TRUE;
+
+ return TRUE;
+}
+
+static gboolean
+scrncap_drawing_area_btnrelease(GtkWidget *draw_area, GdkEvent *event,
+ gpointer _unused)
+{
+ if (!draw_active)
+ return TRUE;
+
+ draw_active = FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+scrncap_drawing_area_motion(GtkWidget *draw_area, GdkEventButton *event,
+ gpointer _cr)
+{
+ cairo_t *cr = _cr;
+ int x, y;
+ int redraw_x, redraw_y, redraw_w, redraw_h;
+
+ x = event->x;
+ y = event->y;
+
+ if (!draw_active) {
+ draw_origin_x = x;
+ draw_origin_y = y;
+ draw_active = TRUE;
+ return FALSE;
+ }
+
+ cairo_move_to(cr, draw_origin_x, draw_origin_y);
+ cairo_line_to(cr, x, y);
+ cairo_set_line_width(cr, line_width);
+ cairo_stroke(cr);
+
+ redraw_x = MIN(draw_origin_x, x) - line_width - 1;
+ redraw_y = MIN(draw_origin_y, y) - line_width - 1;
+ redraw_w = MAX(draw_origin_x, x) - redraw_x + line_width + 1;
+ redraw_h = MAX(draw_origin_y, y) - redraw_y + line_width + 1;
+
+ draw_origin_x = x;
+ draw_origin_y = y;
+
+ gtk_widget_queue_draw_area(draw_area,
+ redraw_x, redraw_y, redraw_w, redraw_h);
+
+ return FALSE;
+}
+
+static gboolean
+scrncap_drawing_area_enter(GtkWidget *widget, GdkEvent *event,
+ GdkCursor *draw_cursor)
+{
+ GdkWindow *gdkwindow;
+
+ gdkwindow = gtk_widget_get_window(GTK_WIDGET(widget));
+ gdk_window_set_cursor(gdkwindow, draw_cursor);
+
+ return FALSE;
+}
+
+static gboolean
+scrncap_drawing_area_leave(GtkWidget *widget, GdkEvent *event,
+ GdkCursor *draw_cursor)
+{
+ GdkWindow *gdkwindow;
+
+ gdkwindow = gtk_widget_get_window(GTK_WIDGET(widget));
+ gdk_window_set_cursor(gdkwindow, NULL);
+
+ return FALSE;
+}
+
+static void
+scrncap_draw_window_close(GtkWidget *window, gpointer _unused)
+{
+ if (current_window != window)
+ return;
+
+ is_shooting = FALSE;
+ current_window = NULL;
+}
+
+static gboolean
+scrncap_draw_window_paint(GtkWidget *widget, cairo_t *cr, gpointer _surface)
+{
+ cairo_surface_t *surface = _surface;
+
+ cairo_set_source_surface(cr, surface, 0, 0);
+ cairo_paint(cr);
+
+ return FALSE;
+}
+
+#if !GTK_CHECK_VERSION(3,0,0)
+static gboolean
+scrncap_draw_window_expose(GtkWidget *widget, GdkEventExpose *event,
+ gpointer _surface)
+{
+ cairo_t *cr = gdk_cairo_create(GDK_DRAWABLE(widget->window));
+
+ scrncap_draw_window_paint(widget, cr, _surface);
+
+ cairo_destroy(cr);
+
+ return FALSE;
+}
+#endif
+
+static void
+scrncap_draw_window_response(GtkDialog *draw_window, gint response_id,
+ gpointer _webview)
+{
+ PidginWebView *webview = PIDGIN_WEBVIEW(_webview);
+ GdkPixbuf *result = NULL;
+ PurpleImage *image;
+ const gchar *fname_prefix;
+ gchar *fname;
+ static guint fname_no = 0;
+
+ if (response_id == SCRNCAP_RESPONSE_COLOR)
+ return;
+
+ if (response_id == GTK_RESPONSE_OK) {
+ cairo_surface_t *surface = g_object_get_data(
+ G_OBJECT(draw_window), "surface");
+ result = gdk_pixbuf_get_from_surface(surface, 0, 0,
+ cairo_image_surface_get_width(surface),
+ cairo_image_surface_get_height(surface));
+ }
+
+ gtk_widget_destroy(GTK_WIDGET(draw_window));
+
+ if (result == NULL)
+ return;
+
+ image = scrncap_pixbuf_to_image(result);
+
+ /* translators: this is the file name prefix,
+ * keep it lowercase and pure ASCII.
+ * Please avoid "_" character, use "-" instead. */
+ fname_prefix = _("screenshot-");
+ fname = g_strdup_printf("%s%u", fname_prefix, ++fname_no);
+ purple_image_set_friendly_filename(image, fname);
+ g_free(fname);
+
+ pidgin_webview_insert_image(webview, image);
+ g_object_unref(image);
+}
+
+static void
+scrncap_draw_color_selected(GtkColorButton *button, cairo_t *cr)
+{
+ gchar *color_str;
+
+ pidgin_color_chooser_get_rgb(GTK_COLOR_CHOOSER(button), &brush_color);
+
+ cairo_set_source_rgb(cr,
+ brush_color.red / 65535.0,
+ brush_color.green / 65535.0,
+ brush_color.blue / 65535.0);
+
+ color_str = gdk_color_to_string(&brush_color);
+ purple_prefs_set_string("/plugins/gtk/screencap/brush_color", color_str);
+ g_free(color_str);
+}
+
+static void
+scrncap_draw_window(PidginWebView *webview, GdkPixbuf *screen)
+{
+ GtkDialog *draw_window;
+ GtkWidget *drawing_area, *box;
+ GtkWidget *scroll_area;
+ GtkWidget *color_button;
+ int width, height;
+ cairo_t *cr;
+ cairo_surface_t *surface;
+ GdkCursor *draw_cursor;
+
+ is_shooting = TRUE;
+
+ current_window = pidgin_create_dialog(
+ _("Insert screenshot"), 0, "insert-screenshot", TRUE);
+ draw_window = GTK_DIALOG(current_window);
+ gtk_widget_set_size_request(GTK_WIDGET(draw_window), 400, 300);
+ gtk_window_set_position(GTK_WINDOW(draw_window), GTK_WIN_POS_CENTER);
+ g_signal_connect(G_OBJECT(draw_window), "destroy",
+ G_CALLBACK(scrncap_draw_window_close), NULL);
+
+ draw_cursor = gdk_cursor_new(GDK_PENCIL);
+#if GTK_CHECK_VERSION(3,0,0)
+ g_object_set_data_full(G_OBJECT(draw_window), "draw-cursor",
+ draw_cursor, g_object_unref);
+#else
+ g_object_set_data_full(G_OBJECT(draw_window), "draw-cursor",
+ draw_cursor, (GDestroyNotify)gdk_cursor_unref);
+#endif
+
+ width = gdk_pixbuf_get_width(screen);
+ height = gdk_pixbuf_get_height(screen);
+
+ surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
+ cr = cairo_create(surface);
+ g_signal_connect_swapped(G_OBJECT(draw_window), "destroy",
+ G_CALLBACK(cairo_destroy), cr);
+ g_object_set_data_full(G_OBJECT(draw_window), "surface",
+ surface, (GDestroyNotify)cairo_surface_destroy);
+
+ gdk_cairo_set_source_pixbuf(cr, screen, 0, 0);
+ cairo_rectangle(cr, 0, 0, width, height);
+ cairo_fill(cr);
+ g_object_unref(screen);
+
+ drawing_area = gtk_drawing_area_new();
+ gtk_widget_set_size_request(drawing_area, width, height);
+#if GTK_CHECK_VERSION(3,0,0)
+ g_signal_connect(G_OBJECT(drawing_area), "draw",
+ G_CALLBACK(scrncap_draw_window_paint), surface);
+#else
+ g_signal_connect(G_OBJECT(drawing_area), "expose_event",
+ G_CALLBACK(scrncap_draw_window_expose), surface);
+#endif
+ gtk_widget_add_events(drawing_area, GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_MOTION_MASK |
+ GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
+ g_signal_connect(G_OBJECT(drawing_area), "button-press-event",
+ G_CALLBACK(scrncap_drawing_area_btnpress), NULL);
+ g_signal_connect(G_OBJECT(drawing_area), "button-release-event",
+ G_CALLBACK(scrncap_drawing_area_btnrelease), NULL);
+ g_signal_connect(G_OBJECT(drawing_area), "motion-notify-event",
+ G_CALLBACK(scrncap_drawing_area_motion), cr);
+ g_signal_connect(G_OBJECT(drawing_area), "enter-notify-event",
+ G_CALLBACK(scrncap_drawing_area_enter), draw_cursor);
+ g_signal_connect(G_OBJECT(drawing_area), "leave-notify-event",
+ G_CALLBACK(scrncap_drawing_area_leave), draw_cursor);
+
+ box = gtk_alignment_new(0.5, 0.5, 0, 0);
+ gtk_container_add(GTK_CONTAINER(box), drawing_area);
+ scroll_area = pidgin_make_scrollable(box,
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC,
+ GTK_SHADOW_NONE, -1, -1);
+#if GTK_CHECK_VERSION(3,0,0)
+ g_object_set(G_OBJECT(scroll_area), "expand", TRUE, NULL);
+#endif
+ gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(
+ GTK_DIALOG(draw_window))), scroll_area);
+
+ color_button = gtk_color_button_new();
+ pidgin_color_chooser_set_rgb(GTK_COLOR_CHOOSER(color_button),
+ &brush_color);
+ g_signal_connect(G_OBJECT(color_button), "color-set",
+ G_CALLBACK(scrncap_draw_color_selected), cr);
+ scrncap_draw_color_selected(GTK_COLOR_BUTTON(color_button), cr);
+
+ gtk_dialog_add_action_widget(draw_window, color_button,
+ SCRNCAP_RESPONSE_COLOR);
+ gtk_dialog_add_button(draw_window, GTK_STOCK_ADD, GTK_RESPONSE_OK);
+ gtk_dialog_add_button(draw_window, GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL);
+ gtk_dialog_set_default_response(draw_window, GTK_RESPONSE_OK);
+ g_signal_connect(G_OBJECT(draw_window), "response",
+ G_CALLBACK(scrncap_draw_window_response), webview);
+
+ gtk_widget_show_all(GTK_WIDGET(draw_window));
+}
+
+/******************************************************************************
+ * Crop window
+ ******************************************************************************/
+
+static void
+scrncap_crop_window_close(GtkWidget *window, gpointer _unused)
+{
+ if (current_window != window)
+ return;
+
+ is_shooting = FALSE;
+ current_window = NULL;
+}
+
+static gboolean
+scrncap_crop_window_keypress(GtkWidget *crop_window, GdkEventKey *event,
+ gpointer _webview)
+{
+ PidginWebView *webview = PIDGIN_WEBVIEW(_webview);
+ guint key = event->keyval;
+
+ if (key == GDK_KEY_Escape) {
+ gtk_widget_destroy(crop_window);
+ return TRUE;
+ }
+ if (key == GDK_KEY_Return) {
+ GdkPixbuf *screenshot, *subscreen, *result;
+
+ screenshot = g_object_get_data(G_OBJECT(crop_window),
+ "screenshot");
+ subscreen = gdk_pixbuf_new_subpixbuf(screenshot,
+ crop_x, crop_y, crop_w, crop_h);
+ result = gdk_pixbuf_copy(subscreen);
+ g_object_unref(subscreen);
+
+ gtk_widget_destroy(crop_window);
+
+ scrncap_draw_window(webview, result);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+scrncap_crop_window_focusout(GtkWidget *window, GdkEventFocus *event,
+ gpointer _unused)
+{
+ gtk_widget_destroy(window);
+ return FALSE;
+}
+
+static gboolean
+scrncap_crop_window_btnpress(GtkWidget *window, GdkEventButton *event,
+ gpointer _unused)
+{
+ GtkWidget *hint_box;
+ GtkImage *selection;
+ GtkFixed *cont;
+
+ g_return_val_if_fail(!crop_active, TRUE);
+
+ hint_box = g_object_get_data(G_OBJECT(window), "hint-box");
+ if (hint_box) {
+ gtk_widget_destroy(hint_box);
+ g_object_set_data(G_OBJECT(window), "hint-box", NULL);
+ }
+
+ selection = g_object_get_data(G_OBJECT(window), "selection");
+ cont = g_object_get_data(G_OBJECT(window), "cont");
+
+ gtk_fixed_move(cont, GTK_WIDGET(selection), -10, -10);
+ gtk_image_set_from_pixbuf(selection, NULL);
+ gtk_widget_show(GTK_WIDGET(selection));
+
+ crop_origin_x = event->x_root;
+ crop_origin_y = event->y_root;
+ crop_active = TRUE;
+
+ return TRUE;
+}
+
+static gboolean
+scrncap_crop_window_btnrelease(GtkWidget *window, GdkEvent *event,
+ gpointer _unused)
+{
+ crop_active = FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+scrncap_crop_window_motion(GtkWidget *window, GdkEventButton *event,
+ gpointer _unused)
+{
+ GtkFixed *cont;
+ GtkImage *selection;
+ GdkPixbuf *crop, *screenshot;
+
+ g_return_val_if_fail(crop_active, FALSE);
+
+ selection = g_object_get_data(G_OBJECT(window), "selection");
+ cont = g_object_get_data(G_OBJECT(window), "cont");
+
+ crop_x = MIN(crop_origin_x, event->x_root);
+ crop_y = MIN(crop_origin_y, event->y_root);
+ crop_w = abs(crop_origin_x - event->x_root);
+ crop_h = abs(crop_origin_y - event->y_root);
+ crop_w = MAX(crop_w, 1);
+ crop_h = MAX(crop_h, 1);
+
+ gtk_fixed_move(cont, GTK_WIDGET(selection), crop_x, crop_y);
+
+ screenshot = g_object_get_data(G_OBJECT(window), "screenshot");
+ crop = gdk_pixbuf_new_subpixbuf(screenshot,
+ crop_x, crop_y, crop_w, crop_h);
+ gtk_image_set_from_pixbuf(GTK_IMAGE(selection), crop);
+ g_object_unref(crop);
+
+ return FALSE;
+}
+
+static void
+scrncap_crop_window_realize(GtkWidget *crop_window, gpointer _unused)
+{
+ GdkWindow *gdkwindow;
+ GdkCursor *cursor;
+
+ gdkwindow = gtk_widget_get_window(GTK_WIDGET(crop_window));
+
+ gdk_window_set_events(gdkwindow, gdk_window_get_events(gdkwindow) |
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+ GDK_BUTTON_MOTION_MASK);
+
+ cursor = gdk_cursor_new(GDK_CROSSHAIR);
+ gdk_window_set_cursor(gdkwindow, cursor);
+#if GTK_CHECK_VERSION(3,0,0)
+ g_object_unref(cursor);
+#else
+ gdk_cursor_unref(cursor);
+#endif
+}
+
+static gboolean
+scrncap_do_screenshot_cb(gpointer _webview)
+{
+ PidginWebView *webview = PIDGIN_WEBVIEW(_webview);
+ GtkWindow *crop_window;
+ GdkPixbuf *screenshot, *screenshot_d;
+ int width, height;
+ GtkFixed *cont;
+ GtkImage *image, *selection;
+ GtkWidget *hint;
+ gchar *hint_msg;
+ GtkRequisition hint_size;
+ GtkWidget *hint_box;
+
+ shooting_timeout = 0;
+ crop_active = FALSE;
+
+ (void)webview;
+
+ screenshot = scrncap_perform_screenshot();
+ g_return_val_if_fail(screenshot != NULL, G_SOURCE_REMOVE);
+ width = gdk_pixbuf_get_width(screenshot);
+ height = gdk_pixbuf_get_height(screenshot);
+
+ crop_x = crop_y = 0;
+ crop_w = width;
+ crop_h = height;
+
+ current_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ crop_window = GTK_WINDOW(current_window);
+ gtk_window_set_decorated(crop_window, FALSE);
+ gtk_window_set_resizable(crop_window, FALSE);
+ gtk_widget_set_size_request(GTK_WIDGET(crop_window), width, height);
+ gtk_window_fullscreen(crop_window);
+ gtk_window_set_keep_above(crop_window, TRUE);
+
+ g_signal_connect(G_OBJECT(crop_window), "realize",
+ G_CALLBACK(scrncap_crop_window_realize), NULL);
+ g_signal_connect(G_OBJECT(crop_window), "destroy",
+ G_CALLBACK(scrncap_crop_window_close), NULL);
+ g_signal_connect(G_OBJECT(crop_window), "key-press-event",
+ G_CALLBACK(scrncap_crop_window_keypress), webview);
+ g_signal_connect(G_OBJECT(crop_window), "focus-out-event",
+ G_CALLBACK(scrncap_crop_window_focusout), NULL);
+ g_signal_connect(G_OBJECT(crop_window), "button-press-event",
+ G_CALLBACK(scrncap_crop_window_btnpress), NULL);
+ g_signal_connect(G_OBJECT(crop_window), "button-release-event",
+ G_CALLBACK(scrncap_crop_window_btnrelease), NULL);
+ g_signal_connect(G_OBJECT(crop_window), "motion-notify-event",
+ G_CALLBACK(scrncap_crop_window_motion), NULL);
+ g_object_set_data_full(G_OBJECT(crop_window), "screenshot",
+ screenshot, g_object_unref);
+
+ cont = GTK_FIXED(gtk_fixed_new());
+ g_object_set_data(G_OBJECT(crop_window), "cont", cont);
+ gtk_container_add(GTK_CONTAINER(crop_window), GTK_WIDGET(cont));
+
+ screenshot_d = gdk_pixbuf_copy(screenshot);
+ scrncap_pixbuf_darken(screenshot_d);
+ image = GTK_IMAGE(gtk_image_new_from_pixbuf(screenshot_d));
+ g_object_unref(screenshot_d);
+ gtk_fixed_put(cont, GTK_WIDGET(image), 0, 0);
+
+ selection = GTK_IMAGE(gtk_image_new_from_pixbuf(NULL));
+ gtk_fixed_put(cont, GTK_WIDGET(selection), -10, -10);
+ g_object_set_data(G_OBJECT(crop_window), "selection", selection);
+
+ hint = gtk_label_new(NULL);
+ hint_msg = g_strdup_printf("<span size='x-large'>%s</span>",
+ _("Select the region to send and press Enter button to confirm "
+ "or press Escape button to cancel"));
+ gtk_label_set_markup(GTK_LABEL(hint), hint_msg);
+ g_free(hint_msg);
+ gtk_misc_set_padding(GTK_MISC(hint), 10, 7);
+ hint_box = gtk_event_box_new();
+ gtk_container_add(GTK_CONTAINER(hint_box), hint);
+ gtk_widget_get_preferred_size(hint, NULL, &hint_size);
+ gtk_fixed_put(cont, hint_box,
+ width / 2 - hint_size.width / 2 - 10,
+ height / 2 - hint_size.height / 2 - 7);
+ g_object_set_data(G_OBJECT(crop_window), "hint-box", hint_box);
+
+ gtk_widget_show_all(GTK_WIDGET(crop_window));
+ gtk_widget_hide(GTK_WIDGET(selection));
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+scrncap_do_screenshot(GtkAction *action, PidginWebView *webview)
+{
+ if (current_window) {
+ gtk_window_present(GTK_WINDOW(current_window));
+ return;
+ }
+ if (is_shooting)
+ return;
+ is_shooting = TRUE;
+
+ shooting_timeout = purple_timeout_add(SCRNCAP_SHOOTING_TIMEOUT,
+ scrncap_do_screenshot_cb, webview);
+}
+
+/******************************************************************************
+ * PidginConversation setup
+ ******************************************************************************/
+
+static void
+scrncap_convwin_switch(GtkNotebook *notebook, GtkWidget *page, gint page_num,
+ gpointer _win)
+{
+ PidginConvWindow *win = _win;
+ PidginConversation *gtkconv;
+ PidginWebView *webview;
+ gboolean images_supported;
+ GtkAction *action;
+
+ gtkconv = pidgin_conv_window_get_active_gtkconv(win);
+ if (gtkconv == NULL)
+ return;
+
+ webview = PIDGIN_WEBVIEW(gtkconv->entry);
+ action = g_object_get_data(G_OBJECT(win->menu->menubar),
+ "insert-screenshot-action");
+
+ g_return_if_fail(action != NULL);
+
+ images_supported = pidgin_webview_get_format_functions(webview) &
+ PIDGIN_WEBVIEW_IMAGE;
+
+ gtk_action_set_sensitive(action, images_supported);
+}
+
+static void
+scrncap_convwin_menu_cb(GtkAction *action, PidginConvWindow *win)
+{
+ PidginConversation *gtkconv;
+ PidginWebView *webview;
+
+ gtkconv = pidgin_conv_window_get_active_gtkconv(win);
+ webview = PIDGIN_WEBVIEW(gtkconv->entry);
+
+ scrncap_do_screenshot(action, webview);
+}
+
+static void
+scrncap_convwin_init(PidginConvWindow *win)
+{
+ PidginConvWindowMenu *menu = win->menu;
+ GtkAction *action;
+ GtkWidget *conv_submenu, *conv_insert_image;
+ GtkWidget *scrncap_btn_menu;
+ gint pos = -1, i;
+ GList *children, *it;
+
+ action = g_object_get_data(G_OBJECT(menu->menubar),
+ "insert-screenshot-action");
+ if (action != NULL)
+ return;
+
+ action = gtk_action_new("InsertScreenshot", _("Insert Screens_hot..."),
+ NULL, PIDGIN_STOCK_TOOLBAR_INSERT_SCREENSHOT);
+ gtk_action_set_is_important(action, TRUE);
+ g_object_set_data_full(G_OBJECT(menu->menubar),
+ "insert-screenshot-action", action, g_object_unref);
+ g_signal_connect(G_OBJECT(action), "activate",
+ G_CALLBACK(scrncap_convwin_menu_cb), win);
+
+ conv_insert_image = gtk_ui_manager_get_widget(menu->ui,
+ "/Conversation/ConversationMenu/InsertImage");
+ g_return_if_fail(conv_insert_image != NULL);
+ conv_submenu = gtk_widget_get_parent(conv_insert_image);
+
+ pos = -1;
+ children = gtk_container_get_children(GTK_CONTAINER(conv_submenu));
+ for (it = children, i = 0; it; it = g_list_next(it), i++) {
+ if (it->data == conv_insert_image) {
+ pos = i + 1;
+ break;
+ }
+ }
+ g_list_free(children);
+ g_warn_if_fail(pos >= 0);
+
+ scrncap_btn_menu = gtk_action_create_menu_item(action);
+ g_object_set_data(G_OBJECT(menu->menubar), "insert-screenshot-btn",
+ scrncap_btn_menu);
+ gtk_menu_shell_insert(GTK_MENU_SHELL(conv_submenu),
+ GTK_WIDGET(scrncap_btn_menu), pos);
+ gtk_widget_show(GTK_WIDGET(scrncap_btn_menu));
+
+ g_signal_connect_after(G_OBJECT(win->notebook), "switch-page",
+ G_CALLBACK(scrncap_convwin_switch), win);
+ scrncap_convwin_switch(GTK_NOTEBOOK(win->notebook), NULL, 0, win);
+}
+
+static void
+scrncap_convwin_uninit(PidginConvWindow *win)
+{
+ PidginConvWindowMenu *menu = win->menu;
+ GtkWidget *btn;
+
+ btn = g_object_get_data(G_OBJECT(menu->menubar),
+ "insert-screenshot-btn");
+ if (btn)
+ gtk_widget_destroy(btn);
+
+ g_object_set_data(G_OBJECT(menu->menubar),
+ "insert-screenshot-btn", NULL);
+ g_object_set_data(G_OBJECT(menu->menubar),
+ "insert-screenshot-action", NULL);
+
+ g_signal_handlers_disconnect_matched(win->notebook, G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL, scrncap_convwin_switch, NULL);
+}
+
+static void
+scrncap_conversation_update(PidginWebView *webview,
+ PidginWebViewButtons buttons, gpointer _action)
+{
+ GtkAction *action = GTK_ACTION(_action);
+
+ gtk_action_set_sensitive(action, buttons & PIDGIN_WEBVIEW_IMAGE);
+}
+
+static void
+scrncap_conversation_init(PidginConversation *gtkconv)
+{
+ PidginWebView *webview;
+ PidginWebViewToolbar *toolbar;
+ GtkAction *action;
+ GtkToolItem *scrncap_btn_wide;
+ GtkWidget *scrncap_btn_lean;
+ gint pos = -1, i;
+ GtkToolbar *wide_view, *lean_view;
+ GtkMenu *wide_menu = NULL;
+ GList *wide_children, *it;
+
+ if (scrncap_conv_get_data(gtkconv, "scrncap-btn-wide") != NULL)
+ return;
+
+ webview = PIDGIN_WEBVIEW(gtkconv->entry);
+ toolbar = PIDGIN_WEBVIEWTOOLBAR(pidgin_webview_get_toolbar(webview));
+ g_return_if_fail(toolbar != NULL);
+ wide_view = GTK_TOOLBAR(pidgin_webviewtoolbar_get_wide_view(toolbar));
+ g_return_if_fail(wide_view != NULL);
+ lean_view = GTK_TOOLBAR(pidgin_webviewtoolbar_get_lean_view(toolbar));
+ g_return_if_fail(lean_view != NULL);
+
+ action = gtk_action_new("InsertScreenshot", _("_Screenshot"),
+ _("Insert screenshot"), PIDGIN_STOCK_TOOLBAR_INSERT_SCREENSHOT);
+ gtk_action_set_is_important(action, TRUE);
+ g_signal_connect(G_OBJECT(action), "activate",
+ G_CALLBACK(scrncap_do_screenshot), webview);
+
+ scrncap_btn_wide = GTK_TOOL_ITEM(gtk_action_create_tool_item(action));
+ scrncap_conv_set_data(gtkconv, "scrncap-btn-wide", scrncap_btn_wide);
+ for (i = 0; i < gtk_toolbar_get_n_items(wide_view); i++) {
+ GtkToolItem *ref_item = gtk_toolbar_get_nth_item(wide_view, i);
+ GtkAction *action;
+
+ action = g_object_get_data(G_OBJECT(ref_item), "action");
+ if (action == NULL)
+ continue;
+
+ if (g_strcmp0(gtk_action_get_name(action), "InsertImage") == 0) {
+ pos = i + 1;
+ break;
+ }
+ }
+ gtk_toolbar_insert(wide_view, scrncap_btn_wide, pos);
+ gtk_widget_show(GTK_WIDGET(scrncap_btn_wide));
+
+ for (i = 0; i < gtk_toolbar_get_n_items(lean_view); i++) {
+ GtkToolItem *ref_item = gtk_toolbar_get_nth_item(lean_view, i);
+ const gchar *menu_name;
+
+ menu_name = g_object_get_data(G_OBJECT(ref_item), "menu-name");
+ if (g_strcmp0(menu_name, "insert") == 0) {
+ wide_menu = g_object_get_data(G_OBJECT(ref_item), "menu");
+ break;
+ }
+ }
+ g_return_if_fail(wide_menu);
+
+ pos = -1;
+ wide_children = gtk_container_get_children(GTK_CONTAINER(wide_menu));
+ for (it = wide_children, i = 0; it; it = g_list_next(it), i++) {
+ GtkWidget *child = it->data;
+ GtkAction *action;
+
+ action = g_object_get_data(G_OBJECT(child), "action");
+ if (action == NULL)
+ continue;
+
+ if (g_strcmp0(gtk_action_get_name(action), "InsertImage") == 0) {
+ pos = i + 1;
+ break;
+ }
+ }
+ g_list_free(wide_children);
+ if (pos < 0) {
+ g_warn_if_fail(pos >= 0);
+ pos = 0;
+ }
+
+ g_signal_connect_object(G_OBJECT(webview), "allowed-formats-updated",
+ G_CALLBACK(scrncap_conversation_update), action, 0);
+ scrncap_conversation_update(webview,
+ pidgin_webview_get_format_functions(webview), action);
+
+ scrncap_btn_lean = gtk_action_create_menu_item(action);
+ scrncap_conv_set_data(gtkconv, "scrncap-btn-lean", scrncap_btn_lean);
+ gtk_menu_shell_insert(GTK_MENU_SHELL(wide_menu),
+ GTK_WIDGET(scrncap_btn_lean), pos);
+ gtk_widget_show(GTK_WIDGET(scrncap_btn_lean));
+}
+
+static void
+scrncap_conversation_uninit(PidginConversation *gtkconv)
+{
+ GtkWidget *scrncap_btn_wide, *scrncap_btn_lean;
+
+ scrncap_btn_wide = scrncap_conv_get_data(gtkconv, "scrncap-btn-wide");
+ if (scrncap_btn_wide == NULL)
+ return;
+
+ scrncap_btn_lean = scrncap_conv_get_data(gtkconv, "scrncap-btn-lean");
+
+ gtk_widget_destroy(scrncap_btn_wide);
+ if (scrncap_btn_lean)
+ gtk_widget_destroy(scrncap_btn_lean);
+
+ scrncap_conv_set_data(gtkconv, "scrncap-btn-wide", NULL);
+ scrncap_conv_set_data(gtkconv, "scrncap-btn-lean", NULL);
+}
+
+/******************************************************************************
+ * Plugin setup
+ ******************************************************************************/
+
+static gboolean
+scrncap_plugin_load(PurplePlugin *plugin)
+{
+ GList *it;
+ const gchar *color_str;
+
+ color_str = purple_prefs_get_string("/plugins/gtk/screencap/brush_color");
+ if (color_str && color_str[0])
+ gdk_color_parse(color_str, &brush_color);
+
+ purple_signal_connect(pidgin_conversations_get_handle(),
+ "conversation-displayed", plugin,
+ PURPLE_CALLBACK(scrncap_conversation_init), NULL);
+ purple_signal_connect(pidgin_conversations_get_handle(),
+ "conversation-window-created", plugin,
+ PURPLE_CALLBACK(scrncap_convwin_init), NULL);
+
+ it = purple_conversations_get_all();
+ for (; it; it = g_list_next(it)) {
+ PurpleConversation *conv = it->data;
+
+ if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv))
+ continue;
+ scrncap_conversation_init(PIDGIN_CONVERSATION(conv));
+ }
+
+ it = pidgin_conv_windows_get_list();
+ for (; it; it = g_list_next(it)) {
+ PidginConvWindow *win = it->data;
+ scrncap_convwin_init(win);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+scrncap_plugin_unload(PurplePlugin *plugin)
+{
+ GList *it;
+
+ if (shooting_timeout > 0)
+ purple_timeout_remove(shooting_timeout);
+ if (current_window != NULL)
+ gtk_widget_destroy(GTK_WIDGET(current_window));
+
+ it = purple_conversations_get_all();
+ for (; it; it = g_list_next(it)) {
+ PurpleConversation *conv = it->data;
+
+ if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv))
+ continue;
+ scrncap_conversation_uninit(PIDGIN_CONVERSATION(conv));
+ }
+
+ it = pidgin_conv_windows_get_list();
+ for (; it; it = g_list_next(it)) {
+ PidginConvWindow *win = it->data;
+ scrncap_convwin_uninit(win);
+ }
+
+ return TRUE;
+}
+
+static PidginPluginUiInfo scrncap_ui_info =
+{
+ NULL, /* config */
+
+ /* padding */
+ NULL, NULL, NULL, NULL
+};
+
+static PurplePluginInfo scrncap_info =
+{
+ PURPLE_PLUGIN_MAGIC,
+ PURPLE_MAJOR_VERSION,
+ PURPLE_MINOR_VERSION,
+ PURPLE_PLUGIN_STANDARD,
+ PIDGIN_PLUGIN_TYPE,
+ 0,
+ NULL,
+ PURPLE_PRIORITY_DEFAULT,
+ "gtk-screencap",
+ N_("Screen Capture"),
+ DISPLAY_VERSION,
+ N_("Send screenshots to your buddies."),
+ N_("Adds an option to send a screenshot as an inline image. "
+ "It works only with protocols that supports inline images."),
+ "Tomasz Wasilczyk <twasilczyk@pidgin.im>",
+ PURPLE_WEBSITE,
+ scrncap_plugin_load,
+ scrncap_plugin_unload,
+ NULL,
+ &scrncap_ui_info,
+ NULL,
+ NULL,
+ NULL,
+
+ /* padding */
+ NULL, NULL, NULL, NULL
+};
+
+static void
+scrncap_init_plugin(PurplePlugin *plugin)
+{
+ purple_prefs_add_none("/plugins");
+ purple_prefs_add_none("/plugins/gtk");
+ purple_prefs_add_none("/plugins/gtk/screencap");
+ purple_prefs_add_string("/plugins/gtk/screencap/brush_color",
+ SCRNCAP_DEFAULT_COLOR);
+}
+
+PURPLE_INIT_PLUGIN(screencap, scrncap_init_plugin, scrncap_info)
diff --git a/pidgin/plugins/sendbutton.c b/pidgin/plugins/sendbutton.c
index 943ee0568a..8fdc4eb383 100644
--- a/pidgin/plugins/sendbutton.c
+++ b/pidgin/plugins/sendbutton.c
@@ -117,7 +117,7 @@ conversation_displayed_cb(PidginConversation *gtkconv)
static gboolean
plugin_load(PurplePlugin *plugin)
{
- GList *convs = purple_get_conversations();
+ GList *convs = purple_conversations_get_all();
void *gtk_conv_handle = pidgin_conversations_get_handle();
purple_signal_connect(gtk_conv_handle, "conversation-displayed", plugin,
@@ -145,7 +145,7 @@ plugin_load(PurplePlugin *plugin)
static gboolean
plugin_unload(PurplePlugin *plugin)
{
- GList *convs = purple_get_conversations();
+ GList *convs = purple_conversations_get_all();
while (convs) {
PurpleConversation *conv = (PurpleConversation *)convs->data;
diff --git a/pidgin/plugins/spellchk.c b/pidgin/plugins/spellchk.c
index 2e8b62422b..1afc2d29a1 100644
--- a/pidgin/plugins/spellchk.c
+++ b/pidgin/plugins/spellchk.c
@@ -39,6 +39,7 @@
#include "util.h"
#include "version.h"
+#include "gtk3compat.h"
#include "gtkplugin.h"
#include "gtkprefs.h"
#include "gtkutils.h"
@@ -46,7 +47,6 @@
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
-#include <sys/stat.h>
#define SPELLCHECK_PLUGIN_ID "gtk-spellcheck"
#define SPELLCHK_OBJECT_KEY "spellchk"
@@ -126,7 +126,8 @@ make_word_proper(const gchar *word)
gchar *ret;
bytes = g_unichar_to_utf8(g_unichar_toupper(g_utf8_get_char(word)), buf);
- buf[MIN(bytes, sizeof(buf) - 1)] = '\0';
+ g_assert(bytes >= 0);
+ buf[MIN((gsize)bytes, sizeof(buf) - 1)] = '\0';
ret = g_strconcat(buf, g_utf8_offset_to_pointer(lower, 1), NULL);
g_free(lower);
@@ -319,6 +320,10 @@ spellchk_inside_word(GtkTextIter *iter)
* Part 1 of 2: This marks . as being an inside-word character. */
if (c == '.')
return TRUE;
+ if (c == '+')
+ return TRUE;
+ if (c == '-')
+ return TRUE;
/* Avoid problems with \r, for example (SF #1289031). */
if (c == '\\')
@@ -675,10 +680,10 @@ spellchk_new_attach(PurpleConversation *conv)
return;
}
-static int buf_get_line(char *ibuf, char **buf, int *position, gsize len)
+static int buf_get_line(char *ibuf, char **buf, gsize *position, gsize len)
{
- int pos = *position;
- int spos = pos;
+ gsize pos = *position;
+ gsize spos = pos;
if (pos == len)
return 0;
@@ -1775,7 +1780,7 @@ static void load_conf(void)
GHashTable *hashes;
char bad[82] = "";
char good[256] = "";
- int pnt = 0;
+ gsize pnt = 0;
gsize size;
gboolean complete = TRUE;
gboolean case_sensitive = FALSE;
@@ -1969,7 +1974,7 @@ static void list_add_new(void)
purple_notify_error(NULL, _("Duplicate Correction"),
_("The specified word already exists in the correction list."),
- gtk_entry_get_text(GTK_ENTRY(bad_entry)));
+ gtk_entry_get_text(GTK_ENTRY(bad_entry)), NULL);
return;
}
@@ -2115,7 +2120,7 @@ plugin_load(PurplePlugin *plugin)
load_conf();
/* Attach to existing conversations */
- for (convs = purple_get_conversations(); convs != NULL; convs = convs->next)
+ for (convs = purple_conversations_get_all(); convs != NULL; convs = convs->next)
{
spellchk_new_attach((PurpleConversation *)convs->data);
}
@@ -2132,7 +2137,7 @@ plugin_unload(PurplePlugin *plugin)
GList *convs;
/* Detach from existing conversations */
- for (convs = purple_get_conversations(); convs != NULL; convs = convs->next)
+ for (convs = purple_conversations_get_all(); convs != NULL; convs = convs->next)
{
PidginConversation *gtkconv = PIDGIN_CONVERSATION((PurpleConversation *)convs->data);
spellchk *spell = g_object_get_data(G_OBJECT(gtkconv->entry), SPELLCHK_OBJECT_KEY);
@@ -2165,7 +2170,7 @@ get_config_frame(PurplePlugin *plugin)
GtkWidget *vbox2;
GtkWidget *vbox3;
- ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
+ 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, _("Text Replacements"));
@@ -2235,7 +2240,7 @@ get_config_frame(PurplePlugin *plugin)
TRUE, TRUE, 0);
gtk_widget_show(tree);
- hbox = gtk_hbutton_box_new();
+ hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show(hbox);
@@ -2252,10 +2257,10 @@ get_config_frame(PurplePlugin *plugin)
vbox = pidgin_make_frame(ret, _("Add a new text replacement"));
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
gtk_widget_show(hbox);
- vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+ vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0);
gtk_widget_show(vbox2);
@@ -2290,7 +2295,7 @@ get_config_frame(PurplePlugin *plugin)
button = gtk_button_new_from_stock(GTK_STOCK_ADD);
g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK(list_add_new), NULL);
- vbox3 = gtk_vbox_new(FALSE, 0);
+ vbox3 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_box_pack_start(GTK_BOX(hbox), vbox3, TRUE, FALSE, 0);
gtk_widget_show(vbox3);
gtk_box_pack_end(GTK_BOX(vbox3), button, FALSE, FALSE, 0);
@@ -2314,7 +2319,6 @@ get_config_frame(PurplePlugin *plugin)
static PidginPluginUiInfo ui_info =
{
get_config_frame,
- 0, /* page_num (Reserved) */
/* padding */
NULL,
diff --git a/pidgin/plugins/themeedit-icon.c b/pidgin/plugins/themeedit-icon.c
index 252423f7d4..0cb55b024f 100644
--- a/pidgin/plugins/themeedit-icon.c
+++ b/pidgin/plugins/themeedit-icon.c
@@ -25,6 +25,7 @@
#include "theme-manager.h"
+#include "gtk3compat.h"
#include "gtkblist.h"
#include "gtkblist-theme.h"
#include "gtkutils.h"
@@ -180,7 +181,7 @@ use_icon_theme(GtkWidget *w, GtkWidget *window)
PidginStatusIconTheme *theme = create_icon_theme(window);
pidgin_stock_load_status_icon_theme(PIDGIN_STATUS_ICON_THEME(theme));
pidgin_stock_load_stock_icon_theme((PidginStockIconTheme *)theme);
- pidgin_blist_refresh(purple_get_blist());
+ pidgin_blist_refresh(purple_blist_get_buddy_list());
g_object_unref(theme);
}
@@ -271,7 +272,7 @@ void pidgin_icon_theme_edit(PurplePluginAction *unused)
for (s = 0; sections[s].heading; s++) {
const char *heading = sections[s].heading;
- box = gtk_vbox_new(FALSE, 0);
+ box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), box, gtk_label_new(heading));
vbox = pidgin_make_frame(box, heading);
@@ -281,7 +282,7 @@ void pidgin_icon_theme_edit(PurplePluginAction *unused)
const char *id = sections[s].options[i].stockid;
const char *text = _(sections[s].options[i].text);
- GtkWidget *hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
+ GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_CAT_SPACE);
GtkWidget *label = gtk_label_new(text);
GtkWidget *image = gtk_image_new_from_stock(id,
gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
diff --git a/pidgin/plugins/themeedit.c b/pidgin/plugins/themeedit.c
index 0290d36075..070d3763b9 100644
--- a/pidgin/plugins/themeedit.c
+++ b/pidgin/plugins/themeedit.c
@@ -22,6 +22,8 @@
#include "pidgin.h"
#include "version.h"
+#include "gtk3compat.h"
+
#include "theme-manager.h"
#include "gtkblist.h"
@@ -58,57 +60,43 @@ close_blist_theme(GtkWidget *w, GtkWidget *window)
}
static void
-theme_color_selected(GtkDialog *dialog, gint response, const char *prop)
+theme_color_selected(GtkColorButton *button, const char *prop)
{
- if (response == GTK_RESPONSE_OK) {
- GtkWidget *colorsel;
- GdkColor color;
- PidginBlistTheme *theme;
-
-#if GTK_CHECK_VERSION(2,14,0)
- colorsel =
- gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(dialog));
-#else
- colorsel = GTK_COLOR_SELECTION_DIALOG(dialog)->colorsel;
-#endif
- gtk_color_selection_get_current_color(GTK_COLOR_SELECTION(colorsel), &color);
+ GdkColor color;
+ PidginBlistTheme *theme;
- theme = pidgin_blist_get_theme();
+ pidgin_color_chooser_get_rgb(GTK_COLOR_CHOOSER(button), &color);
- if (prop_type_is_color(theme, prop)) {
- g_object_set(G_OBJECT(theme), prop, &color, NULL);
+ theme = pidgin_blist_get_theme();
+
+ if (prop_type_is_color(theme, prop)) {
+ g_object_set(G_OBJECT(theme), prop, &color, NULL);
+ } else {
+ PidginThemeFont *font = NULL;
+ g_object_get(G_OBJECT(theme), prop, &font, NULL);
+ if (!font) {
+ font = pidgin_theme_font_new(NULL, &color);
+ g_object_set(G_OBJECT(theme), prop, font, NULL);
+ pidgin_theme_font_free(font);
} else {
- PidginThemeFont *font = NULL;
- g_object_get(G_OBJECT(theme), prop, &font, NULL);
- if (!font) {
- font = pidgin_theme_font_new(NULL, &color);
- g_object_set(G_OBJECT(theme), prop, font, NULL);
- pidgin_theme_font_free(font);
- } else {
- pidgin_theme_font_set_color(font, &color);
- }
+ pidgin_theme_font_set_color(font, &color);
}
- pidgin_blist_set_theme(theme);
}
-
- gtk_widget_destroy(GTK_WIDGET(dialog));
+ pidgin_blist_set_theme(theme);
}
static void
-theme_font_face_selected(GtkWidget *dialog, gint response, gpointer font)
+theme_font_face_selected(GtkFontButton *button, PidginThemeFont *font)
{
- if (response == GTK_RESPONSE_OK || response == GTK_RESPONSE_APPLY) {
- const char *fontname = gtk_font_selection_dialog_get_font_name(GTK_FONT_SELECTION_DIALOG(dialog));
- pidgin_theme_font_set_font_face(font, fontname);
- pidgin_blist_refresh(purple_get_blist());
- }
- gtk_widget_destroy(dialog);
+ const char *fontname = gtk_font_button_get_font_name(button);
+ pidgin_theme_font_set_font_face(font, fontname);
+ pidgin_blist_refresh(purple_blist_get_buddy_list());
}
-static void
-theme_font_select_face(GtkWidget *widget, gpointer prop)
+static GtkWidget *
+theme_font_select_face_widget(const char *prop)
{
- GtkWidget *dialog;
+ GtkWidget *widget;
PidginBlistTheme *theme;
PidginThemeFont *font = NULL;
const char *face;
@@ -124,19 +112,20 @@ theme_font_select_face(GtkWidget *widget, gpointer prop)
}
face = pidgin_theme_font_get_font_face(font);
- dialog = gtk_font_selection_dialog_new(_("Select Font"));
+ widget = gtk_font_button_new();
+ gtk_font_button_set_title(GTK_FONT_BUTTON(widget), _("Select Font"));
if (face && *face)
- gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(dialog),
- face);
- g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(theme_font_face_selected),
+ gtk_font_button_set_font_name(GTK_FONT_BUTTON(widget), face);
+ g_signal_connect(G_OBJECT(widget), "font-set", G_CALLBACK(theme_font_face_selected),
font);
- gtk_widget_show_all(dialog);
+
+ return widget;
}
-static void
-theme_color_select(GtkWidget *widget, gpointer prop)
+static GtkWidget *
+theme_color_select_widget(const char *prop)
{
- GtkWidget *dialog;
+ GtkWidget *widget;
PidginBlistTheme *theme;
const GdkColor *color = NULL;
@@ -151,21 +140,15 @@ theme_color_select(GtkWidget *widget, gpointer prop)
color = pidgin_theme_font_get_color(pair);
}
- dialog = gtk_color_selection_dialog_new(_("Select Color"));
-#if GTK_CHECK_VERSION(2,14,0)
- if (color)
- gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(
- gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(dialog))),
- color);
-#else
+ widget = gtk_color_button_new();
+ gtk_color_button_set_title(GTK_COLOR_BUTTON(widget), _("Select Color"));
+ gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(widget), FALSE);
if (color)
- gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(dialog)->colorsel),
- color);
-#endif
- g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(theme_color_selected),
- prop);
+ pidgin_color_chooser_set_rgb(GTK_COLOR_CHOOSER(widget), color);
+ g_signal_connect(G_OBJECT(widget), "color-set",
+ G_CALLBACK(theme_color_selected), (gpointer)prop);
- gtk_widget_show_all(dialog);
+ return widget;
}
static GtkWidget *
@@ -175,20 +158,15 @@ pidgin_theme_create_color_selector(const char *text, const char *blurb, const ch
GtkWidget *color;
GtkWidget *hbox, *label;
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_CAT_SPACE);
label = gtk_label_new(_(text));
gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
gtk_size_group_add_widget(sizegroup, label);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-#if GTK_CHECK_VERSION(2, 12, 0)
gtk_widget_set_tooltip_text(label, blurb);
-#endif
- color = pidgin_pixbuf_button_from_stock("", GTK_STOCK_SELECT_COLOR,
- PIDGIN_BUTTON_HORIZONTAL);
- g_signal_connect(G_OBJECT(color), "clicked", G_CALLBACK(theme_color_select),
- (gpointer)prop);
+ color = theme_color_select_widget(prop);
gtk_box_pack_start(GTK_BOX(hbox), color, FALSE, FALSE, 0);
return hbox;
@@ -201,26 +179,18 @@ pidgin_theme_create_font_selector(const char *text, const char *blurb, const cha
GtkWidget *color, *font;
GtkWidget *hbox, *label;
- hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_CAT_SPACE);
label = gtk_label_new(_(text));
gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
gtk_size_group_add_widget(sizegroup, label);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-#if GTK_CHECK_VERSION(2, 12, 0)
gtk_widget_set_tooltip_text(label, blurb);
-#endif
- font = pidgin_pixbuf_button_from_stock("", GTK_STOCK_SELECT_FONT,
- PIDGIN_BUTTON_HORIZONTAL);
- g_signal_connect(G_OBJECT(font), "clicked", G_CALLBACK(theme_font_select_face),
- (gpointer)prop);
+ font = theme_font_select_face_widget(prop);
gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 0);
- color = pidgin_pixbuf_button_from_stock("", GTK_STOCK_SELECT_COLOR,
- PIDGIN_BUTTON_HORIZONTAL);
- g_signal_connect(G_OBJECT(color), "clicked", G_CALLBACK(theme_color_select),
- (gpointer)prop);
+ color = theme_color_select_widget(prop);
gtk_box_pack_start(GTK_BOX(hbox), color, FALSE, FALSE, 0);
return hbox;
@@ -247,7 +217,7 @@ pidgin_blist_theme_edit(PurplePluginAction *unused)
"offline",
"idle",
"message",
- "message_nick_said",
+ "message-nick-said",
"status",
NULL
}
@@ -306,7 +276,6 @@ pidgin_blist_theme_edit(PurplePluginAction *unused)
}
}
- gtk_dialog_set_has_separator(GTK_DIALOG(dialog), TRUE);
#ifdef NOT_SADRUL
pidgin_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_SAVE, G_CALLBACK(save_blist_theme), dialog);
#endif
diff --git a/pidgin/plugins/ticker/Makefile.am b/pidgin/plugins/ticker/Makefile.am
index ad137b6c71..cc40e32557 100644
--- a/pidgin/plugins/ticker/Makefile.am
+++ b/pidgin/plugins/ticker/Makefile.am
@@ -1,9 +1,9 @@
EXTRA_DIST = \
Makefile.mingw
-plugindir = $(libdir)/pidgin
+plugindir = @PIDGIN_PLUGINDIR@
-ticker_la_LDFLAGS = -module -avoid-version
+ticker_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
if PLUGINS
@@ -14,12 +14,11 @@ ticker_la_SOURCES = \
gtkticker.h \
ticker.c
-ticker_la_LIBADD = $(GTK_LIBS)
+ticker_la_LIBADD = @PIDGIN_LIBS@
endif
AM_CPPFLAGS = \
- -DDATADIR=\"$(datadir)\" \
-I$(top_srcdir)/libpurple \
-I$(top_builddir)/libpurple \
-I$(top_srcdir)/pidgin \
diff --git a/pidgin/plugins/ticker/Makefile.mingw b/pidgin/plugins/ticker/Makefile.mingw
index 15bb4de793..2ef62f274b 100644
--- a/pidgin/plugins/ticker/Makefile.mingw
+++ b/pidgin/plugins/ticker/Makefile.mingw
@@ -19,6 +19,7 @@ INCLUDE_PATHS += -I. \
-I$(GTK_TOP)/include/pango-1.0 \
-I$(GTK_TOP)/include/atk-1.0 \
-I$(GTK_TOP)/include/cairo \
+ -I$(GTK_TOP)/include/gdk-pixbuf-2.0 \
-I$(GTK_TOP)/lib/glib-2.0/include \
-I$(GTK_TOP)/lib/gtk-2.0/include \
-I$(PURPLE_TOP) \
diff --git a/pidgin/plugins/ticker/gtkticker.c b/pidgin/plugins/ticker/gtkticker.c
index 38ce49ed6e..c5da484c1a 100644
--- a/pidgin/plugins/ticker/gtkticker.c
+++ b/pidgin/plugins/ticker/gtkticker.c
@@ -21,44 +21,27 @@
* GtkTicker Copyright 2000 Syd Logan
*/
-/* FIXME: GTK+ deprecated GTK_WIDGET_MAPPED/REALIZED, but don't provide
- accessor functions yet. */
-#undef GSEAL_ENABLE
-
#include "gtkticker.h"
#include <gtk/gtk.h>
-/* These don't seem to be in a release yet. See BZ #69872 */
-#define gtk_widget_is_mapped(x) GTK_WIDGET_MAPPED(x)
-#define gtk_widget_is_realized(x) GTK_WIDGET_REALIZED(x)
-#define gtk_widget_set_realized(x,y) do {\
- if (y) \
- GTK_WIDGET_SET_FLAGS(x, GTK_REALIZED); \
- else \
- GTK_WIDGET_UNSET_FLAGS(x, GTK_REALIZED); \
-} while(0)
-#define gtk_widget_set_mapped(x,y) do {\
- if (y) \
- GTK_WIDGET_SET_FLAGS(x, GTK_MAPPED); \
- else \
- GTK_WIDGET_UNSET_FLAGS(x, GTK_MAPPED); \
-} while(0)
-
-#if !GTK_CHECK_VERSION(2,18,0)
-#define gtk_widget_get_visible(x) GTK_WIDGET_VISIBLE(x)
-
-#if !GTK_CHECK_VERSION(2,14,0)
-#define gtk_widget_get_window(x) x->window
-#endif
-#endif
+#include "gtk3compat.h"
static void gtk_ticker_compute_offsets (GtkTicker *ticker);
static void gtk_ticker_class_init (GtkTickerClass *klass);
static void gtk_ticker_init (GtkTicker *ticker);
static void gtk_ticker_map (GtkWidget *widget);
static void gtk_ticker_realize (GtkWidget *widget);
+#if GTK_CHECK_VERSION(3,0,0)
+static void gtk_ticker_get_preferred_width (GtkWidget *widget,
+ gint *minimal_width,
+ gint *natural_width);
+static void gtk_ticker_get_preferred_height (GtkWidget *widget,
+ gint *minimal_height,
+ gint *natural_height);
+#else
static void gtk_ticker_size_request (GtkWidget *widget,
GtkRequisition *requisition);
+#endif
static void gtk_ticker_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static void gtk_ticker_add_real (GtkContainer *container,
@@ -131,7 +114,12 @@ static void gtk_ticker_class_init (GtkTickerClass *class)
widget_class->map = gtk_ticker_map;
widget_class->realize = gtk_ticker_realize;
+#if GTK_CHECK_VERSION(3,0,0)
+ widget_class->get_preferred_width = gtk_ticker_get_preferred_width;
+ widget_class->get_preferred_height = gtk_ticker_get_preferred_height;
+#else
widget_class->size_request = gtk_ticker_size_request;
+#endif
widget_class->size_allocate = gtk_ticker_size_allocate;
container_class->add = gtk_ticker_add_real;
@@ -147,11 +135,7 @@ static GType gtk_ticker_child_type (GtkContainer *container)
static void gtk_ticker_init (GtkTicker *ticker)
{
-#if GTK_CHECK_VERSION(2,18,0)
gtk_widget_set_has_window (GTK_WIDGET (ticker), TRUE);
-#else
- GTK_WIDGET_UNSET_FLAGS (ticker, GTK_NO_WINDOW);
-#endif
ticker->interval = (guint) 200;
ticker->scootch = (guint) 2;
@@ -181,13 +165,13 @@ static void gtk_ticker_put (GtkTicker *ticker, GtkWidget *widget)
ticker->children = g_list_append (ticker->children, child_info);
- if (gtk_widget_is_realized (ticker))
+ if (gtk_widget_get_realized (GTK_WIDGET (ticker)))
gtk_widget_realize (widget);
if (gtk_widget_get_visible (GTK_WIDGET (ticker)) &&
gtk_widget_get_visible (widget))
{
- if (gtk_widget_is_mapped (GTK_WIDGET (ticker)))
+ if (gtk_widget_get_mapped (GTK_WIDGET (ticker)))
gtk_widget_map (widget);
gtk_widget_queue_resize (GTK_WIDGET (ticker));
@@ -298,7 +282,7 @@ static void gtk_ticker_map (GtkWidget *widget)
children = children->next;
if (gtk_widget_get_visible (child->widget) &&
- !gtk_widget_is_mapped (child->widget))
+ !gtk_widget_get_mapped (child->widget))
gtk_widget_map (child->widget);
}
@@ -310,10 +294,12 @@ static void gtk_ticker_realize (GtkWidget *widget)
GdkWindowAttr attributes;
gint attributes_mask;
GdkWindow *window;
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context;
+#else
GtkStyle *style;
-#if GTK_CHECK_VERSION(2,18,0)
- GtkAllocation allocation;
#endif
+ GtkAllocation allocation;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_TICKER (widget));
@@ -321,45 +307,117 @@ static void gtk_ticker_realize (GtkWidget *widget)
gtk_widget_set_realized (widget, TRUE);
attributes.window_type = GDK_WINDOW_CHILD;
-#if GTK_CHECK_VERSION(2,18,0)
gtk_widget_get_allocation (widget, &allocation);
attributes.x = allocation.x;
attributes.y = allocation.y;
attributes.width = allocation.width;
attributes.height = allocation.height;
-#else
- attributes.x = widget->allocation.x;
- attributes.y = widget->allocation.y;
- attributes.width = widget->allocation.width;
- attributes.height = widget->allocation.height;
-#endif
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.visual = gtk_widget_get_visual (widget);
+#if !GTK_CHECK_VERSION(3,0,0)
attributes.colormap = gtk_widget_get_colormap (widget);
+#endif
attributes.event_mask = gtk_widget_get_events (widget);
attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK;
+#if GTK_CHECK_VERSION(3,0,0)
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
+#else
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+#endif
window = gdk_window_new (gtk_widget_get_parent_window (widget),
&attributes, attributes_mask);
-#if GTK_CHECK_VERSION(2,18,0)
gtk_widget_set_window (widget, window);
-#else
- widget->window = window;
-#endif
gdk_window_set_user_data (window, widget);
-#if GTK_CHECK_VERSION(2,14,0)
- style = gtk_widget_get_style (widget);
- style = gtk_style_attach (style, window);
- gtk_widget_set_style (widget, style);
+#if GTK_CHECK_VERSION(3,0,0)
+ context = gtk_widget_get_style_context(widget);
+ gtk_style_context_add_class(context, GTK_STYLE_CLASS_BACKGROUND);
+ gtk_style_context_set_state(context, GTK_STATE_FLAG_NORMAL);
+ gtk_style_context_set_background(context, window);
#else
- style = widget->style = gtk_style_attach (widget->style, window);
-#endif
+ style = gtk_style_attach (gtk_widget_get_style (widget), window);
+ gtk_widget_set_style (widget, style);
gtk_style_set_background (style, window, GTK_STATE_NORMAL);
+#endif
+}
+
+#if GTK_CHECK_VERSION(3,0,0)
+static void
+gtk_ticker_get_preferred_width (GtkWidget *widget,
+ gint *minimal_width,
+ gint *natural_width)
+{
+ GtkTicker *ticker;
+ GtkTickerChild *child;
+ GList *children;
+ gint child_min_width, child_nat_width;
+ gint width;
+ guint border_width;
+
+ ticker = GTK_TICKER (widget);
+ *minimal_width = width = 0;
+
+ children = ticker->children;
+ while (children)
+ {
+ child = children->data;
+ children = children->next;
+
+ if (gtk_widget_get_visible (child->widget))
+ {
+ gtk_widget_get_preferred_width (child->widget, &child_min_width, &child_nat_width);
+
+ width += child_nat_width + ticker->spacing;
+ *minimal_width = MAX(*minimal_width, child_min_width);
+ }
+ }
+ if ( width > ticker->spacing )
+ width -= ticker->spacing;
+
+ border_width = gtk_container_get_border_width (GTK_CONTAINER (ticker));
+ width += border_width * 2;
+
+ *natural_width = width;
+}
+
+static void
+gtk_ticker_get_preferred_height (GtkWidget *widget,
+ gint *minimal_height,
+ gint *natural_height)
+{
+ GtkTicker *ticker;
+ GtkTickerChild *child;
+ GList *children;
+ gint child_min_height, child_nat_height;
+ gint height;
+ guint border_width;
+
+ ticker = GTK_TICKER (widget);
+ height = 0;
+
+ children = ticker->children;
+ while (children)
+ {
+ child = children->data;
+ children = children->next;
+
+ if (gtk_widget_get_visible (child->widget))
+ {
+ gtk_widget_get_preferred_height (child->widget, &child_min_height, &child_nat_height);
+
+ height = MAX (height, child_nat_height);
+ }
+ }
+
+ border_width = gtk_container_get_border_width (GTK_CONTAINER (ticker));
+ height += border_width * 2;
+ *minimal_height = *natural_height = height;
}
+#else
+
static void gtk_ticker_size_request (GtkWidget *widget, GtkRequisition *requisition)
{
GtkTicker *ticker;
@@ -398,9 +456,11 @@ static void gtk_ticker_size_request (GtkWidget *widget, GtkRequisition *requisit
requisition->height += border_width * 2;
requisition->width += border_width * 2;
}
+#endif
static void gtk_ticker_compute_offsets (GtkTicker *ticker)
{
+ GtkAllocation allocation;
GtkTickerChild *child;
GtkRequisition child_requisition;
GList *children;
@@ -411,15 +471,8 @@ static void gtk_ticker_compute_offsets (GtkTicker *ticker)
border_width = gtk_container_get_border_width (GTK_CONTAINER (ticker));
-#if GTK_CHECK_VERSION(2,18,0)
- {
- GtkAllocation allocation;
- gtk_widget_get_allocation (GTK_WIDGET (ticker), &allocation);
- ticker->width = allocation.width;
- }
-#else
- ticker->width = GTK_WIDGET(ticker)->allocation.width;
-#endif
+ gtk_widget_get_allocation (GTK_WIDGET (ticker), &allocation);
+ ticker->width = allocation.width;
ticker->total = 0;
children = ticker->children;
while (children) {
@@ -427,7 +480,7 @@ static void gtk_ticker_compute_offsets (GtkTicker *ticker)
child->x = 0;
if (gtk_widget_get_visible (child->widget)) {
- gtk_widget_get_child_requisition (child->widget, &child_requisition);
+ gtk_widget_get_preferred_size (child->widget, NULL, &child_requisition);
child->offset = ticker->total;
ticker->total +=
child_requisition.width + border_width + ticker->spacing;
@@ -441,6 +494,7 @@ static void gtk_ticker_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
GtkTicker *ticker;
+ GtkAllocation a;
GtkTickerChild *child;
GtkAllocation child_allocation;
GtkRequisition child_requisition;
@@ -453,28 +507,16 @@ static void gtk_ticker_size_allocate (GtkWidget *widget,
ticker = GTK_TICKER (widget);
-#if GTK_CHECK_VERSION(2,18,0)
- {
- GtkAllocation a;
- gtk_widget_get_allocation (GTK_WIDGET (ticker), &a);
- if ( a.width != ticker->width )
- ticker->dirty = TRUE;
- }
-#else
- if ( GTK_WIDGET(ticker)->allocation.width != ticker->width )
+ gtk_widget_get_allocation (GTK_WIDGET (ticker), &a);
+ if ( a.width != ticker->width )
ticker->dirty = TRUE;
-#endif
if ( ticker->dirty == TRUE ) {
gtk_ticker_compute_offsets( ticker );
}
-#if GTK_CHECK_VERSION(2,18,0)
gtk_widget_set_allocation (widget, allocation);
-#else
- widget->allocation = *allocation;
-#endif
- if (gtk_widget_is_realized (widget))
+ if (gtk_widget_get_realized (widget))
gdk_window_move_resize (gtk_widget_get_window (widget),
allocation->x,
allocation->y,
@@ -490,7 +532,7 @@ static void gtk_ticker_size_allocate (GtkWidget *widget,
child->x -= ticker->scootch;
if (gtk_widget_get_visible (child->widget)) {
- gtk_widget_get_child_requisition (child->widget, &child_requisition);
+ gtk_widget_get_preferred_size (child->widget, NULL, &child_requisition);
child_allocation.width = child_requisition.width;
child_allocation.x = child->offset + border_width + child->x;
if ( ( child_allocation.x + child_allocation.width ) < allocation->x ) {
diff --git a/pidgin/plugins/ticker/ticker.c b/pidgin/plugins/ticker/ticker.c
index ea26d0fb88..1511d5786d 100644
--- a/pidgin/plugins/ticker/ticker.c
+++ b/pidgin/plugins/ticker/ticker.c
@@ -27,13 +27,14 @@
#include "internal.h"
#include "pidgin.h"
-#include "blist.h"
+#include "buddylist.h"
#include "conversation.h"
#include "debug.h"
#include "prpl.h"
#include "signals.h"
#include "version.h"
+#include "gtk3compat.h"
#include "gtkblist.h"
#include "gtkplugin.h"
#include "gtkutils.h"
@@ -92,10 +93,9 @@ static gboolean buddy_click_cb(GtkWidget *widget, GdkEventButton *event, gpointe
PurpleContact *contact = user_data;
PurpleBuddy *b = purple_contact_get_priority_buddy(contact);
- PurpleConversation *conv = purple_conversation_new(PURPLE_CONV_TYPE_IM,
- purple_buddy_get_account(b),
- purple_buddy_get_name(b));
- purple_conversation_present(conv);
+ PurpleIMConversation *im = purple_im_conversation_new(purple_buddy_get_account(b),
+ purple_buddy_get_name(b));
+ purple_conversation_present(PURPLE_CONVERSATION(im));
return TRUE;
}
@@ -167,7 +167,7 @@ static void buddy_ticker_add_buddy(PurpleBuddy *b) {
td->ebox = gtk_event_box_new();
gtk_ticker_add(GTK_TICKER(ticker), td->ebox);
- hbox = gtk_hbox_new(FALSE, 0);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_container_add(GTK_CONTAINER(td->ebox), hbox);
buddy_ticker_set_pixmap(contact);
gtk_box_pack_start(GTK_BOX(hbox), td->icon, FALSE, FALSE, 0);
@@ -235,21 +235,21 @@ static void buddy_ticker_show(void)
gnode;
gnode = purple_blist_node_get_sibling_next(gnode))
{
- if(!PURPLE_BLIST_NODE_IS_GROUP(gnode))
+ if(!PURPLE_IS_GROUP(gnode))
continue;
for(cnode = purple_blist_node_get_first_child(gnode);
cnode;
cnode = purple_blist_node_get_sibling_next(cnode))
{
- if(!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
+ if(!PURPLE_IS_CONTACT(cnode))
continue;
for(bnode = purple_blist_node_get_first_child(cnode);
bnode;
bnode = purple_blist_node_get_sibling_next(bnode))
{
- if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
+ if(!PURPLE_IS_BUDDY(bnode))
continue;
- b = (PurpleBuddy *)bnode;
+ b = PURPLE_BUDDY(bnode);
if(PURPLE_BUDDY_IS_ONLINE(b))
buddy_ticker_add_buddy(b);
}
diff --git a/pidgin/plugins/timestamp.c b/pidgin/plugins/timestamp.c
deleted file mode 100644
index 0f46f47221..0000000000
--- a/pidgin/plugins/timestamp.c
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Purple - iChat-style timestamps
- *
- * Copyright (C) 2002-2003, Sean Egan
- * Copyright (C) 2003, Chris J. Friesen <Darth_Sebulba04@yahoo.com>
- * Copyright (C) 2007, Andrew Gaul <andrew@gaul.org>
- *
- * 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 "conversation.h"
-#include "debug.h"
-#include "prefs.h"
-#include "signals.h"
-#include "version.h"
-
-#include "gtkimhtml.h"
-#include "gtkplugin.h"
-#include "gtkprefs.h"
-#include "gtkutils.h"
-
-#define TIMESTAMP_PLUGIN_ID "gtk-timestamp"
-
-/* minutes externally, seconds internally, and milliseconds in preferences */
-static int interval = 5 * 60;
-
-static void
-timestamp_display(PurpleConversation *conv, time_t then, time_t now)
-{
- PidginConversation *gtk_conv = PIDGIN_CONVERSATION(conv);
- GtkWidget *imhtml = gtk_conv->imhtml;
- GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(imhtml));
- GtkTextIter iter;
- const char *mdate;
- int y, height;
- GdkRectangle rect;
- gboolean scrolled = FALSE;
- GtkTextTag *tag;
-
- /* display timestamp */
- mdate = purple_utf8_strftime(then == 0 ? "%H:%M" : "\n%H:%M",
- localtime(&now));
- gtk_text_buffer_get_end_iter(buffer, &iter);
-
- /* is the view already scrolled? */
- gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect);
- gtk_text_view_get_line_yrange(GTK_TEXT_VIEW(imhtml), &iter, &y, &height);
- if (((y + height) - (rect.y + rect.height)) > height)
- scrolled = TRUE;
-
- if ((tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "TIMESTAMP")) == NULL)
- tag = gtk_text_buffer_create_tag(buffer, "TIMESTAMP",
- "foreground", "#888888", "justification", GTK_JUSTIFY_CENTER,
- "weight", PANGO_WEIGHT_BOLD, NULL);
-
- gtk_text_buffer_insert_with_tags(buffer, &iter, mdate,
- strlen(mdate), tag, NULL);
-
- /* scroll view if necessary */
- gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect);
- gtk_text_view_get_line_yrange(
- GTK_TEXT_VIEW(imhtml), &iter, &y, &height);
- if (!scrolled && ((y + height) - (rect.y + rect.height)) > height &&
- gtk_text_buffer_get_char_count(buffer)) {
- gboolean smooth = purple_prefs_get_bool(
- PIDGIN_PREFS_ROOT "/conversations/use_smooth_scrolling");
- gtk_imhtml_scroll_to_end(GTK_IMHTML(imhtml), smooth);
- }
-}
-
-static gboolean
-timestamp_displaying_conv_msg(PurpleAccount *account, const char *who,
- char **buffer, PurpleConversation *conv,
- PurpleMessageFlags flags, void *data)
-{
- time_t now = time(NULL) / interval * interval;
- time_t then;
-
- if (!g_list_find(purple_get_conversations(), conv))
- return FALSE;
-
- then = GPOINTER_TO_INT(purple_conversation_get_data(
- conv, "timestamp-last"));
-
- if (now - then >= interval) {
- timestamp_display(conv, then, now);
- purple_conversation_set_data(
- conv, "timestamp-last", GINT_TO_POINTER(now));
- }
-
- return FALSE;
-}
-
-static void
-timestamp_new_convo(PurpleConversation *conv)
-{
- if (!g_list_find(purple_get_conversations(), conv))
- return;
-
- purple_conversation_set_data(conv, "timestamp-last", GINT_TO_POINTER(0));
-}
-
-static void
-set_timestamp(GtkWidget *spinner, void *null)
-{
- int tm;
-
- tm = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spinner));
- purple_debug(PURPLE_DEBUG_MISC, "timestamp",
- "setting interval to %d minutes\n", tm);
-
- interval = tm * 60;
- purple_prefs_set_int("/plugins/gtk/timestamp/interval", interval * 1000);
-}
-
-static GtkWidget *
-get_config_frame(PurplePlugin *plugin)
-{
- GtkWidget *ret;
- GtkWidget *frame, *label;
- GtkWidget *vbox, *hbox;
- GtkObject *adj;
- GtkWidget *spinner;
-
- ret = gtk_vbox_new(FALSE, 18);
- gtk_container_set_border_width (GTK_CONTAINER (ret), 12);
-
- frame = pidgin_make_frame(ret, _("Display Timestamps Every"));
- vbox = gtk_vbox_new(FALSE, 5);
- gtk_container_add(GTK_CONTAINER(frame), vbox);
-
- hbox = gtk_hbox_new(FALSE, 5);
- gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
-
- /* XXX limit to divisors of 60? */
- adj = gtk_adjustment_new(interval / 60, 1, 60, 1, 0, 0);
- spinner = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 0, 0);
- gtk_box_pack_start(GTK_BOX(hbox), spinner, TRUE, TRUE, 0);
- g_signal_connect(G_OBJECT(spinner), "value-changed",
- G_CALLBACK(set_timestamp), NULL);
- label = gtk_label_new(_("minutes"));
- gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
-
- gtk_widget_show_all(ret);
- return ret;
-}
-
-static gboolean
-plugin_load(PurplePlugin *plugin)
-{
- void *conv_handle = purple_conversations_get_handle();
- void *gtkconv_handle = pidgin_conversations_get_handle();
-
- /* lower priority to display initial timestamp after logged messages */
- purple_signal_connect_priority(conv_handle, "conversation-created",
- plugin, PURPLE_CALLBACK(timestamp_new_convo), NULL,
- PURPLE_SIGNAL_PRIORITY_DEFAULT + 1);
-
- purple_signal_connect(gtkconv_handle, "displaying-chat-msg",
- plugin, PURPLE_CALLBACK(timestamp_displaying_conv_msg), NULL);
- purple_signal_connect(gtkconv_handle, "displaying-im-msg",
- plugin, PURPLE_CALLBACK(timestamp_displaying_conv_msg), NULL);
-
- interval = purple_prefs_get_int("/plugins/gtk/timestamp/interval") / 1000;
-
- return TRUE;
-}
-
-static PidginPluginUiInfo ui_info =
-{
- get_config_frame,
- 0, /* page_num (Reserved) */
-
- /* padding */
- NULL,
- NULL,
- NULL,
- NULL
-};
-
-static PurplePluginInfo info =
-{
- PURPLE_PLUGIN_MAGIC,
- PURPLE_MAJOR_VERSION,
- PURPLE_MINOR_VERSION,
- PURPLE_PLUGIN_STANDARD, /**< type */
- PIDGIN_PLUGIN_TYPE, /**< ui_requirement */
- 0, /**< flags */
- NULL, /**< dependencies */
- PURPLE_PRIORITY_DEFAULT, /**< priority */
-
- TIMESTAMP_PLUGIN_ID, /**< id */
- N_("Timestamp"), /**< name */
- DISPLAY_VERSION, /**< version */
- /** summary */
- N_("Display iChat-style timestamps"),
- /** description */
- N_("Display iChat-style timestamps every N minutes."),
- "Sean Egan <seanegan@gmail.com>", /**< author */
- PURPLE_WEBSITE, /**< homepage */
-
- plugin_load, /**< load */
- NULL, /**< unload */
- NULL, /**< destroy */
-
- &ui_info, /**< ui_info */
- NULL, /**< extra_info */
- NULL,
- NULL,
-
- /* padding */
- NULL,
- NULL,
- NULL,
- NULL
-};
-
-static void
-init_plugin(PurplePlugin *plugin)
-{
- purple_prefs_add_none("/plugins/gtk/timestamp");
- purple_prefs_add_int("/plugins/gtk/timestamp/interval", interval * 1000);
-}
-
-PURPLE_INIT_PLUGIN(timestamp, init_plugin, info)
diff --git a/pidgin/plugins/timestamp_format.c b/pidgin/plugins/timestamp_format.c
deleted file mode 100644
index 5016875cb4..0000000000
--- a/pidgin/plugins/timestamp_format.c
+++ /dev/null
@@ -1,322 +0,0 @@
-#include "internal.h"
-
-#include "debug.h"
-#include "log.h"
-#include "plugin.h"
-#include "util.h"
-#include "version.h"
-
-#include "gtkconv.h"
-#include "gtkplugin.h"
-#include "gtkimhtml.h"
-
-#include <time.h>
-
-static const char *format_12hour_hour(const struct tm *tm)
-{
- static char hr[3];
- int hour = tm->tm_hour % 12;
- if (hour == 0)
- hour = 12;
-
- g_snprintf(hr, sizeof(hr), "%d", hour);
- return hr;
-}
-
-static PurplePluginPrefFrame *
-get_plugin_pref_frame(PurplePlugin *plugin)
-{
- PurplePluginPrefFrame *frame;
- PurplePluginPref *ppref;
- char *tmp;
-
- frame = purple_plugin_pref_frame_new();
-
- ppref = purple_plugin_pref_new_with_label(_("Timestamp Format Options"));
- purple_plugin_pref_frame_add(frame, ppref);
-
- tmp = g_strdup(_("_Force timestamp format:"));
- ppref = purple_plugin_pref_new_with_name_and_label(
- "/plugins/gtk/timestamp_format/force",
- tmp);
- purple_plugin_pref_set_type(ppref, PURPLE_PLUGIN_PREF_CHOICE);
- purple_plugin_pref_add_choice(ppref, _("Use system default"), "default");
- purple_plugin_pref_add_choice(ppref, _("12 hour time format"), "force12");
- purple_plugin_pref_add_choice(ppref, _("24 hour time format"), "force24");
- purple_plugin_pref_frame_add(frame, ppref);
- g_free(tmp);
-
- ppref = purple_plugin_pref_new_with_label(_("Show dates in..."));
- purple_plugin_pref_frame_add(frame, ppref);
-
- ppref = purple_plugin_pref_new_with_name_and_label(
- "/plugins/gtk/timestamp_format/use_dates/conversation",
- _("Co_nversations:"));
- purple_plugin_pref_set_type(ppref, PURPLE_PLUGIN_PREF_CHOICE);
- purple_plugin_pref_add_choice(ppref, _("For delayed messages"), "automatic");
- purple_plugin_pref_add_choice(ppref, _("For delayed messages and in chats"), "chats");
- purple_plugin_pref_add_choice(ppref, _("Always"), "always");
- purple_plugin_pref_frame_add(frame, ppref);
-
- ppref = purple_plugin_pref_new_with_name_and_label(
- "/plugins/gtk/timestamp_format/use_dates/log",
- _("_Message Logs:"));
- purple_plugin_pref_set_type(ppref, PURPLE_PLUGIN_PREF_CHOICE);
- purple_plugin_pref_add_choice(ppref, _("For delayed messages"), "automatic");
- purple_plugin_pref_add_choice(ppref, _("For delayed messages and in chats"), "chats");
- purple_plugin_pref_add_choice(ppref, _("Always"), "always");
- purple_plugin_pref_frame_add(frame, ppref);
-
- return frame;
-}
-
-static char *timestamp_cb_common(PurpleConversation *conv,
- time_t t,
- gboolean show_date,
- const char *force,
- const char *dates,
- gboolean parens)
-{
- struct tm *tm;
-
- g_return_val_if_fail(dates != NULL, NULL);
-
- tm = localtime(&t);
-
- if (show_date ||
- !strcmp(dates, "always") ||
- (conv != NULL && purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT && !strcmp(dates, "chats")))
- {
- if (g_str_equal(force, "force24"))
- return g_strdup_printf("%s%s%s", parens ? "(" : "", purple_utf8_strftime("%Y-%m-%d %H:%M:%S", tm), parens ? ")" : "");
- else if (g_str_equal(force, "force12")) {
- char *date = g_strdup_printf("%s", purple_utf8_strftime("%Y-%m-%d ", tm));
- char *remtime = g_strdup_printf("%s", purple_utf8_strftime(":%M:%S %p", tm));
- const char *hour = format_12hour_hour(tm);
- char *output;
-
- output = g_strdup_printf("%s%s%s%s%s",
- parens ? "(" : "", date,
- hour, remtime, parens ? ")" : "");
-
- g_free(date);
- g_free(remtime);
-
- return output;
- } else
- return g_strdup_printf("%s%s%s", parens ? "(" : "", purple_date_format_long(tm), parens ? ")" : "");
- }
-
- if (g_str_equal(force, "force24"))
- return g_strdup_printf("%s%s%s", parens ? "(" : "", purple_utf8_strftime("%H:%M:%S", tm), parens ? ")" : "");
- else if (g_str_equal(force, "force12")) {
- const char *hour = format_12hour_hour(tm);
- char *remtime = g_strdup_printf("%s", purple_utf8_strftime(":%M:%S %p", tm));
- char *output = g_strdup_printf("%s%s%s%s", parens ? "(" : "", hour, remtime, parens ? ")" : "");
-
- g_free(remtime);
-
- return output;
- }
-
- return NULL;
-}
-
-static char *conversation_timestamp_cb(PurpleConversation *conv,
- time_t t, gboolean show_date, gpointer data)
-{
- const char *force = purple_prefs_get_string(
- "/plugins/gtk/timestamp_format/force");
- const char *dates = purple_prefs_get_string(
- "/plugins/gtk/timestamp_format/use_dates/conversation");
-
- g_return_val_if_fail(conv != NULL, NULL);
-
- return timestamp_cb_common(conv, t, show_date, force, dates, TRUE);
-}
-
-static char *log_timestamp_cb(PurpleLog *log, time_t t, gboolean show_date, gpointer data)
-{
- const char *force = purple_prefs_get_string(
- "/plugins/gtk/timestamp_format/force");
- const char *dates = purple_prefs_get_string(
- "/plugins/gtk/timestamp_format/use_dates/log");
-
- g_return_val_if_fail(log != NULL, NULL);
-
- return timestamp_cb_common(log->conv, t, show_date, force, dates, FALSE);
-}
-
-static void
-menu_cb(GtkWidget *item, gpointer data)
-{
- PurplePlugin *plugin = data;
- GtkWidget *frame = pidgin_plugin_get_config_frame(plugin), *dialog;
- if (!frame)
- return;
-
- dialog = gtk_dialog_new_with_buttons(PIDGIN_ALERT_TITLE, NULL,
- GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
- NULL);
- g_signal_connect_after(G_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), dialog);
-#if GTK_CHECK_VERSION(2,14,0)
- gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), frame);
-#else
- gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), frame);
-#endif
- gtk_window_set_role(GTK_WINDOW(dialog), "plugin_config");
- gtk_window_set_title(GTK_WINDOW(dialog), _(purple_plugin_get_name(plugin)));
- gtk_widget_show_all(dialog);
-}
-
-static gboolean
-textview_emission_hook(GSignalInvocationHint *hint, guint n_params,
- const GValue *pvalues, gpointer data)
-{
- GtkTextView *view = GTK_TEXT_VIEW(g_value_get_object(pvalues));
- GtkWidget *menu, *item;
- GtkTextBuffer *buffer;
- GtkTextIter cursor;
- int cx, cy, bx, by;
-
- if (!GTK_IS_IMHTML(view))
- return TRUE;
-
-#if GTK_CHECK_VERSION(2,14,0)
- if (!gdk_window_get_pointer(gtk_widget_get_window(GTK_WIDGET(view)), &cx, &cy, NULL))
- return TRUE;
-#else
- if (!gdk_window_get_pointer(GTK_WIDGET(view)->window, &cx, &cy, NULL))
- return TRUE;
-#endif
-
- buffer = gtk_text_view_get_buffer(view);
-
- gtk_text_view_window_to_buffer_coords(view, GTK_TEXT_WINDOW_TEXT, cx, cy, &bx, &by);
- gtk_text_view_get_iter_at_location(view, &cursor, bx, by);
- if (!gtk_text_iter_has_tag(&cursor,
- gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "comment")))
- return TRUE;
-
- menu = g_value_get_object(&pvalues[1]);
-
- item = gtk_menu_item_new_with_label(_("Timestamp Format Options"));
- gtk_widget_show_all(item);
- g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(menu_cb), data);
- gtk_menu_shell_insert(GTK_MENU_SHELL(menu), item, 0);
-
- item = gtk_separator_menu_item_new();
- gtk_widget_show(item);
- gtk_menu_shell_insert(GTK_MENU_SHELL(menu), item, 1);
-
- return TRUE;
-}
-
-static guint signal_id;
-static gulong hook_id;
-
-static gboolean
-plugin_load(PurplePlugin *plugin)
-{
- gpointer klass = NULL;
-
- purple_signal_connect(pidgin_conversations_get_handle(), "conversation-timestamp",
- plugin, PURPLE_CALLBACK(conversation_timestamp_cb), NULL);
- purple_signal_connect(purple_log_get_handle(), "log-timestamp",
- plugin, PURPLE_CALLBACK(log_timestamp_cb), NULL);
-
- klass = g_type_class_ref(GTK_TYPE_TEXT_VIEW);
-
- /* In 3.0.0, use purple_g_signal_connect_flags */
- g_signal_parse_name("populate_popup", GTK_TYPE_TEXT_VIEW, &signal_id, NULL, FALSE);
- hook_id = g_signal_add_emission_hook(signal_id, 0, textview_emission_hook,
- plugin, NULL);
-
- g_type_class_unref(klass);
-
- return TRUE;
-}
-
-static gboolean
-plugin_unload(PurplePlugin *plugin)
-{
- g_signal_remove_emission_hook(signal_id, hook_id);
- return TRUE;
-}
-
-static PurplePluginUiInfo prefs_info = {
- get_plugin_pref_frame,
- 0, /* page num (Reserved) */
- NULL,/* frame (Reserved) */
-
- /* padding */
- NULL,
- NULL,
- NULL,
- NULL
-};
-
-static PurplePluginInfo info =
-{
- PURPLE_PLUGIN_MAGIC,
- PURPLE_MAJOR_VERSION,
- PURPLE_MINOR_VERSION,
- PURPLE_PLUGIN_STANDARD, /**< type */
- PIDGIN_PLUGIN_TYPE, /**< ui_requirement */
- 0, /**< flags */
- NULL, /**< dependencies */
- PURPLE_PRIORITY_DEFAULT, /**< priority */
-
- "core-timestamp_format", /**< id */
- N_("Message Timestamp Formats"), /**< name */
- DISPLAY_VERSION, /**< version */
- /** summary */
- N_("Customizes the message timestamp formats."),
- /** description */
- N_("This plugin allows the user to customize "
- "conversation and logging message timestamp "
- "formats."),
- "Richard Laager <rlaager@pidgin.im>", /**< author */
- PURPLE_WEBSITE, /**< homepage */
-
- plugin_load, /**< load */
- plugin_unload, /**< unload */
- NULL, /**< destroy */
-
- NULL, /**< ui_info */
- NULL, /**< extra_info */
- &prefs_info, /**< prefs_info */
- NULL, /**< actions */
-
- /* padding */
- NULL,
- NULL,
- NULL,
- NULL
-};
-
-static void
-init_plugin(PurplePlugin *plugin)
-{
- purple_prefs_add_none("/plugins/gtk");
- purple_prefs_add_none("/plugins/gtk/timestamp_format");
-
- if (!purple_prefs_exists("/plugins/gtk/timestamp_format/force") &&
- purple_prefs_exists("/plugins/gtk/timestamp_format/force_24hr"))
- {
- if (purple_prefs_get_bool(
- "/plugins/gtk/timestamp_format/force_24hr"))
- purple_prefs_add_string("/plugins/gtk/timestamp_format/force", "force24");
- else
- purple_prefs_add_string("/plugins/gtk/timestamp_format/force", "default");
- }
- else
- purple_prefs_add_string("/plugins/gtk/timestamp_format/force", "default");
-
- purple_prefs_add_none("/plugins/gtk/timestamp_format/use_dates");
- purple_prefs_add_string("/plugins/gtk/timestamp_format/use_dates/conversation", "automatic");
- purple_prefs_add_string("/plugins/gtk/timestamp_format/use_dates/log", "automatic");
-}
-
-PURPLE_INIT_PLUGIN(timestamp_format, init_plugin, info)
diff --git a/pidgin/plugins/unity.c b/pidgin/plugins/unity.c
index b66a79b609..31e044ed1b 100644
--- a/pidgin/plugins/unity.c
+++ b/pidgin/plugins/unity.c
@@ -17,10 +17,11 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#include "internal.h"
-#include "version.h"
#include "account.h"
#include "savedstatuses.h"
+#include "version.h"
+#include "gtk3compat.h"
#include "gtkplugin.h"
#include "gtkconv.h"
#include "gtkutils.h"
@@ -61,10 +62,10 @@ update_launcher()
return;
if (launcher_count == LAUNCHER_COUNT_MESSAGES) {
- for (convs = purple_get_conversations(); convs != NULL; convs = convs->next) {
+ for (convs = purple_conversations_get_all(); convs != NULL; convs = convs->next) {
PurpleConversation *conv = convs->data;
- count += GPOINTER_TO_INT(purple_conversation_get_data(conv,
- "unity-message-count"));
+ count += GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv),
+ "unity-message-count"));
}
} else {
count = n_sources;
@@ -82,15 +83,13 @@ update_launcher()
static gchar *
conversation_id(PurpleConversation *conv)
{
- PurpleConversationType conv_type = purple_conversation_get_type(conv);
PurpleAccount *account = purple_conversation_get_account(conv);
- char type[2] = "0";
- type[0] += conv_type;
- return g_strconcat(type, ":",
- purple_conversation_get_name(conv), ":",
- purple_account_get_username(account), ":",
- purple_account_get_protocol_id(account), NULL);
+ return g_strconcat((PURPLE_IS_IM_CONVERSATION(conv) ? "im" :
+ PURPLE_IS_CHAT_CONVERSATION(conv) ? "chat" : "misc"), ":",
+ purple_conversation_get_name(conv), ":",
+ purple_account_get_username(account), ":",
+ purple_account_get_protocol_id(account), NULL);
}
static void
@@ -104,7 +103,7 @@ messaging_menu_add_conversation(PurpleConversation *conv, gint count)
icon data for IMs */
if (!messaging_menu_app_has_source(mmapp, id))
messaging_menu_app_append_source(mmapp, id, NULL,
- purple_conversation_get_title(conv));
+ purple_conversation_get_title(conv));
if (messaging_menu_text == MESSAGING_MENU_TIME)
messaging_menu_app_set_source_time(mmapp, id, g_get_real_time());
@@ -129,10 +128,11 @@ refill_messaging_menu()
{
GList *convs;
- for (convs = purple_get_conversations(); convs != NULL; convs = convs->next) {
+ for (convs = purple_conversations_get_all(); convs != NULL; convs = convs->next) {
PurpleConversation *conv = convs->data;
messaging_menu_add_conversation(conv,
- GPOINTER_TO_INT(purple_conversation_get_data(conv, "unity-message-count")));
+ GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv),
+ "unity-message-count")));
}
}
@@ -140,7 +140,7 @@ static int
alert(PurpleConversation *conv)
{
gint count;
- PidginWindow *purplewin = NULL;
+ PidginConvWindow *purplewin = NULL;
if (conv == NULL || PIDGIN_CONVERSATION(conv) == NULL)
return 0;
@@ -149,13 +149,13 @@ alert(PurpleConversation *conv)
if (!pidgin_conv_window_has_focus(purplewin) ||
!pidgin_conv_window_is_active_conversation(conv))
{
- count = GPOINTER_TO_INT(purple_conversation_get_data(conv,
- "unity-message-count"));
+ count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv),
+ "unity-message-count"));
if (!count++)
++n_sources;
- purple_conversation_set_data(conv, "unity-message-count",
- GINT_TO_POINTER(count));
+ g_object_set_data(G_OBJECT(conv), "unity-message-count",
+ GINT_TO_POINTER(count));
messaging_menu_add_conversation(conv, count);
update_launcher();
}
@@ -166,10 +166,11 @@ alert(PurpleConversation *conv)
static void
unalert(PurpleConversation *conv)
{
- if (GPOINTER_TO_INT(purple_conversation_get_data(conv, "unity-message-count")) > 0)
+ if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "unity-message-count")) > 0)
--n_sources;
- purple_conversation_set_data(conv, "unity-message-count",
- GINT_TO_POINTER(0));
+
+ g_object_set_data(G_OBJECT(conv), "unity-message-count",
+ GINT_TO_POINTER(0));
messaging_menu_remove_conversation(conv);
update_launcher();
}
@@ -182,11 +183,12 @@ unalert_cb(GtkWidget *widget, gpointer data, PurpleConversation *conv)
}
static gboolean
-message_displayed_cb(PurpleAccount *account, const char *who, char *message,
- PurpleConversation *conv, PurpleMessageFlags flags)
+message_displayed_cb(PurpleConversation *conv, PurpleMessage *msg, gpointer _unused)
{
- if ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT &&
- alert_chat_nick && !(flags & PURPLE_MESSAGE_NICK)))
+ PurpleMessageFlags flags = purple_message_get_flags(msg);
+
+ if ((PURPLE_IS_CHAT_CONVERSATION(conv) && alert_chat_nick &&
+ !(flags & PURPLE_MESSAGE_NICK)))
return FALSE;
if ((flags & PURPLE_MESSAGE_RECV) && !(flags & PURPLE_MESSAGE_DELAYED))
@@ -196,27 +198,27 @@ message_displayed_cb(PurpleAccount *account, const char *who, char *message,
}
static void
-im_sent_im(PurpleAccount *account, const char *receiver, const char *message)
+im_sent_im(PurpleAccount *account, PurpleMessage *msg, gpointer _unused)
{
- PurpleConversation *conv = NULL;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, receiver,
- account);
- unalert(conv);
+ PurpleIMConversation *im = NULL;
+ im = purple_conversations_find_im_with_account(
+ purple_message_get_recipient(msg), account);
+ unalert(PURPLE_CONVERSATION(im));
}
static void
-chat_sent_im(PurpleAccount *account, const char *message, int id)
+chat_sent_im(PurpleAccount *account, PurpleMessage *msg, int id)
{
- PurpleConversation *conv = NULL;
- conv = purple_find_chat(purple_account_get_connection(account), id);
- unalert(conv);
+ PurpleChatConversation *chat = NULL;
+ chat = purple_conversations_find_chat(purple_account_get_connection(account), id);
+ unalert(PURPLE_CONVERSATION(chat));
}
static void
conv_created(PurpleConversation *conv)
{
- purple_conversation_set_data(conv, "unity-message-count",
- GINT_TO_POINTER(0));
+ g_object_set_data(G_OBJECT(conv), "unity-message-count",
+ GINT_TO_POINTER(0));
attach_signals(conv);
}
@@ -229,22 +231,26 @@ deleting_conv(PurpleConversation *conv)
static void
message_source_activated(MessagingMenuApp *app, const gchar *id,
- gpointer user_data)
+ gpointer user_data)
{
gchar **sections = g_strsplit(id, ":", 0);
PurpleConversation *conv = NULL;
PurpleAccount *account;
- PidginWindow *purplewin = NULL;
- PurpleConversationType conv_type;
+ PidginConvWindow *purplewin = NULL;
char *type = sections[0];
char *cname = sections[1];
char *aname = sections[2];
char *protocol = sections[3];
- conv_type = type[0] - '0';
account = purple_accounts_find(aname, protocol);
- conv = purple_find_conversation_with_account(conv_type, cname, account);
+
+ if (g_strcmp0(type, "im") == 0)
+ conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(cname, account));
+ else if (g_strcmp0(type, "chat") == 0)
+ conv = PURPLE_CONVERSATION(purple_conversations_find_chat_with_account(cname, account));
+ else
+ conv = purple_conversations_find_with_account(cname, account);
if (conv) {
unalert(conv);
@@ -277,7 +283,7 @@ status_changed_cb(PurpleSavedStatus *saved_status)
{
MessagingMenuStatus status = MESSAGING_MENU_STATUS_AVAILABLE;
- switch (purple_savedstatus_get_type(saved_status)) {
+ switch (purple_savedstatus_get_primitive_type(saved_status)) {
case PURPLE_STATUS_AVAILABLE:
case PURPLE_STATUS_MOOD:
case PURPLE_STATUS_TUNE:
@@ -311,7 +317,7 @@ status_changed_cb(PurpleSavedStatus *saved_status)
static void
messaging_menu_status_changed(MessagingMenuApp *mmapp,
- MessagingMenuStatus mm_status, gpointer user_data)
+ MessagingMenuStatus mm_status, gpointer user_data)
{
PurpleSavedStatus *saved_status;
PurpleStatusPrimitive primitive = PURPLE_STATUS_UNSET;
@@ -393,12 +399,12 @@ attach_signals(PurpleConversation *conv)
return 0;
id = g_signal_connect(G_OBJECT(gtkconv->entry), "focus-in-event",
- G_CALLBACK(unalert_cb), conv);
- purple_conversation_set_data(conv, "unity-entry-signal", GUINT_TO_POINTER(id));
+ G_CALLBACK(unalert_cb), conv);
+ g_object_set_data(G_OBJECT(conv), "unity-entry-signal", GUINT_TO_POINTER(id));
- id = g_signal_connect(G_OBJECT(gtkconv->imhtml), "focus-in-event",
- G_CALLBACK(unalert_cb), conv);
- purple_conversation_set_data(conv, "unity-imhtml-signal", GUINT_TO_POINTER(id));
+ id = g_signal_connect(G_OBJECT(gtkconv->webview), "focus-in-event",
+ G_CALLBACK(unalert_cb), conv);
+ g_object_set_data(G_OBJECT(conv), "unity-webview-signal", GUINT_TO_POINTER(id));
return 0;
}
@@ -412,14 +418,14 @@ detach_signals(PurpleConversation *conv)
if (!gtkconv)
return;
- id = GPOINTER_TO_INT(purple_conversation_get_data(conv, "unity-imhtml-signal"));
- g_signal_handler_disconnect(gtkconv->imhtml, id);
+ id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "unity-webview-signal"));
+ g_signal_handler_disconnect(gtkconv->webview, id);
- id = GPOINTER_TO_INT(purple_conversation_get_data(conv, "unity-entry-signal"));
+ id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "unity-entry-signal"));
g_signal_handler_disconnect(gtkconv->entry, id);
- purple_conversation_set_data(conv, "unity-message-count",
- GINT_TO_POINTER(0));
+ g_object_set_data(G_OBJECT(conv), "unity-message-count",
+ GINT_TO_POINTER(0));
}
static GtkWidget *
@@ -428,26 +434,26 @@ get_config_frame(PurplePlugin *plugin)
GtkWidget *ret = NULL, *frame = NULL;
GtkWidget *vbox = NULL, *toggle = NULL;
- ret = gtk_vbox_new(FALSE, 18);
+ ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
gtk_container_set_border_width(GTK_CONTAINER (ret), 12);
/* Alerts */
frame = pidgin_make_frame(ret, _("Chatroom alerts"));
- vbox = gtk_vbox_new(FALSE, 5);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_container_add(GTK_CONTAINER(frame), vbox);
toggle = gtk_check_button_new_with_mnemonic(_("Chatroom message alerts _only where someone says your username"));
gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
- purple_prefs_get_bool("/plugins/gtk/unity/alert_chat_nick"));
+ purple_prefs_get_bool("/plugins/gtk/unity/alert_chat_nick"));
g_signal_connect(G_OBJECT(toggle), "toggled",
- G_CALLBACK(alert_config_cb), NULL);
+ G_CALLBACK(alert_config_cb), NULL);
/* Launcher integration */
frame = pidgin_make_frame(ret, _("Launcher Icon"));
- vbox = gtk_vbox_new(FALSE, 5);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_container_add(GTK_CONTAINER(frame), vbox);
toggle = gtk_radio_button_new_with_mnemonic(NULL, _("_Disable launcher integration"));
@@ -455,45 +461,45 @@ get_config_frame(PurplePlugin *plugin)
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_int("/plugins/gtk/unity/launcher_count") == LAUNCHER_COUNT_DISABLE);
g_signal_connect(G_OBJECT(toggle), "toggled",
- G_CALLBACK(launcher_config_cb), GUINT_TO_POINTER(LAUNCHER_COUNT_DISABLE));
+ G_CALLBACK(launcher_config_cb), GUINT_TO_POINTER(LAUNCHER_COUNT_DISABLE));
toggle = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(toggle),
- _("Show number of unread _messages on launcher icon"));
+ _("Show number of unread _messages on launcher icon"));
gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_int("/plugins/gtk/unity/launcher_count") == LAUNCHER_COUNT_MESSAGES);
g_signal_connect(G_OBJECT(toggle), "toggled",
- G_CALLBACK(launcher_config_cb), GUINT_TO_POINTER(LAUNCHER_COUNT_MESSAGES));
+ G_CALLBACK(launcher_config_cb), GUINT_TO_POINTER(LAUNCHER_COUNT_MESSAGES));
toggle = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(toggle),
- _("Show number of unread co_nversations on launcher icon"));
+ _("Show number of unread co_nversations on launcher icon"));
gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_int("/plugins/gtk/unity/launcher_count") == LAUNCHER_COUNT_SOURCES);
g_signal_connect(G_OBJECT(toggle), "toggled",
- G_CALLBACK(launcher_config_cb), GUINT_TO_POINTER(LAUNCHER_COUNT_SOURCES));
+ G_CALLBACK(launcher_config_cb), GUINT_TO_POINTER(LAUNCHER_COUNT_SOURCES));
/* Messaging menu integration */
frame = pidgin_make_frame(ret, _("Messaging Menu"));
- vbox = gtk_vbox_new(FALSE, 5);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_container_add(GTK_CONTAINER(frame), vbox);
toggle = gtk_radio_button_new_with_mnemonic(NULL,
- _("Show number of _unread messages for conversations in messaging menu"));
+ _("Show number of _unread messages for conversations in messaging menu"));
gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_int("/plugins/gtk/unity/messaging_menu_text") == MESSAGING_MENU_COUNT);
g_signal_connect(G_OBJECT(toggle), "toggled",
- G_CALLBACK(messaging_menu_config_cb), GUINT_TO_POINTER(MESSAGING_MENU_COUNT));
+ G_CALLBACK(messaging_menu_config_cb), GUINT_TO_POINTER(MESSAGING_MENU_COUNT));
toggle = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(toggle),
- _("Show _elapsed time for unread conversations in messaging menu"));
+ _("Show _elapsed time for unread conversations in messaging menu"));
gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_int("/plugins/gtk/unity/messaging_menu_text") == MESSAGING_MENU_TIME);
g_signal_connect(G_OBJECT(toggle), "toggled",
- G_CALLBACK(messaging_menu_config_cb), GUINT_TO_POINTER(MESSAGING_MENU_TIME));
+ G_CALLBACK(messaging_menu_config_cb), GUINT_TO_POINTER(MESSAGING_MENU_TIME));
gtk_widget_show_all(ret);
return ret;
@@ -502,7 +508,7 @@ get_config_frame(PurplePlugin *plugin)
static gboolean
plugin_load(PurplePlugin *plugin)
{
- GList *convs = purple_get_conversations();
+ GList *convs = purple_conversations_get_all();
PurpleSavedStatus *saved_status;
void *conv_handle = purple_conversations_get_handle();
void *gtk_conv_handle = pidgin_conversations_get_handle();
@@ -516,32 +522,32 @@ plugin_load(PurplePlugin *plugin)
messaging_menu_text = purple_prefs_get_int("/plugins/gtk/unity/messaging_menu_text");
g_signal_connect(mmapp, "activate-source",
- G_CALLBACK(message_source_activated), NULL);
+ G_CALLBACK(message_source_activated), NULL);
g_signal_connect(mmapp, "status-changed",
- G_CALLBACK(messaging_menu_status_changed), NULL);
+ G_CALLBACK(messaging_menu_status_changed), NULL);
saved_status = purple_savedstatus_get_current();
status_changed_cb(saved_status);
purple_signal_connect(savedstat_handle, "savedstatus-changed", plugin,
- PURPLE_CALLBACK(status_changed_cb), NULL);
+ PURPLE_CALLBACK(status_changed_cb), NULL);
launcher = unity_launcher_entry_get_for_desktop_id("pidgin.desktop");
g_object_ref(launcher);
launcher_count = purple_prefs_get_int("/plugins/gtk/unity/launcher_count");
purple_signal_connect(gtk_conv_handle, "displayed-im-msg", plugin,
- PURPLE_CALLBACK(message_displayed_cb), NULL);
+ PURPLE_CALLBACK(message_displayed_cb), NULL);
purple_signal_connect(gtk_conv_handle, "displayed-chat-msg", plugin,
- PURPLE_CALLBACK(message_displayed_cb), NULL);
+ PURPLE_CALLBACK(message_displayed_cb), NULL);
purple_signal_connect(conv_handle, "sent-im-msg", plugin,
- PURPLE_CALLBACK(im_sent_im), NULL);
+ PURPLE_CALLBACK(im_sent_im), NULL);
purple_signal_connect(conv_handle, "sent-chat-msg", plugin,
- PURPLE_CALLBACK(chat_sent_im), NULL);
+ PURPLE_CALLBACK(chat_sent_im), NULL);
purple_signal_connect(conv_handle, "conversation-created", plugin,
- PURPLE_CALLBACK(conv_created), NULL);
+ PURPLE_CALLBACK(conv_created), NULL);
purple_signal_connect(conv_handle, "deleting-conversation", plugin,
- PURPLE_CALLBACK(deleting_conv), NULL);
+ PURPLE_CALLBACK(deleting_conv), NULL);
while (convs) {
PurpleConversation *conv = (PurpleConversation *)convs->data;
@@ -555,14 +561,14 @@ plugin_load(PurplePlugin *plugin)
static gboolean
plugin_unload(PurplePlugin *plugin)
{
- GList *convs = purple_get_conversations();
+ GList *convs = purple_conversations_get_all();
while (convs) {
PurpleConversation *conv = (PurpleConversation *)convs->data;
unalert(conv);
detach_signals(conv);
convs = convs->next;
}
-
+
unity_launcher_entry_set_count_visible(launcher, FALSE);
messaging_menu_app_unregister(mmapp);
@@ -574,7 +580,6 @@ plugin_unload(PurplePlugin *plugin)
static PidginPluginUiInfo ui_info =
{
get_config_frame,
- 0, /* page_num (Reserved) */
/* padding */
NULL,
diff --git a/pidgin/plugins/vvconfig.c b/pidgin/plugins/vvconfig.c
deleted file mode 100644
index 71a3038127..0000000000
--- a/pidgin/plugins/vvconfig.c
+++ /dev/null
@@ -1,807 +0,0 @@
-/*
- * Configures microphones and webcams for voice and video
- * Copyright (C) 2009 Mike Ruprecht <cmaiku@gmail.com>
- *
- * 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 "mediamanager.h"
-#include "media-gst.h"
-#include "version.h"
-#include "gtkplugin.h"
-#include "gtkutils.h"
-#include "gtkprefs.h"
-
-#include <gst/interfaces/propertyprobe.h>
-
-/* container window for showing a stand-alone configurator */
-static GtkWidget *window = NULL;
-
-static PurpleMediaElementInfo *old_video_src = NULL, *old_video_sink = NULL,
- *old_audio_src = NULL, *old_audio_sink = NULL;
-
-static const gchar *AUDIO_SRC_PLUGINS[] = {
- "alsasrc", "ALSA",
- /* "esdmon", "ESD", ? */
- "osssrc", "OSS",
- "pulsesrc", "PulseAudio",
- "sndiosrc", "sndio",
- /* "audiotestsrc wave=silence", "Silence", */
- "audiotestsrc", "Test Sound",
- NULL
-};
-
-static const gchar *AUDIO_SINK_PLUGINS[] = {
- "alsasink", "ALSA",
- "artsdsink", "aRts",
- "esdsink", "ESD",
- "osssink", "OSS",
- "pulsesink", "PulseAudio",
- "sndiosink", "sndio",
- NULL
-};
-
-static const gchar *VIDEO_SRC_PLUGINS[] = {
- "videotestsrc", "Test Input",
- "dshowvideosrc","DirectDraw",
- "ksvideosrc", "KS Video",
- "qcamsrc", "Quickcam",
- "v4lsrc", "Video4Linux",
- "v4l2src", "Video4Linux2",
- "v4lmjpegsrc", "Video4Linux MJPEG",
- NULL
-};
-
-static const gchar *VIDEO_SINK_PLUGINS[] = {
- /* "aasink", "AALib", Didn't work for me */
- "directdrawsink","DirectDraw",
- "glimagesink", "OpenGL",
- "ximagesink", "X Window System",
- "xvimagesink", "X Window System (Xv)",
- NULL
-};
-
-static GList *
-get_element_devices(const gchar *element_name)
-{
- GList *ret = NULL;
- GstElement *element;
- GObjectClass *klass;
- GstPropertyProbe *probe;
- const GParamSpec *pspec;
-
- ret = g_list_prepend(ret, (gpointer)_("Default"));
- ret = g_list_prepend(ret, "");
-
- if (!strcmp(element_name, "<custom>") || (*element_name == '\0')) {
- return g_list_reverse(ret);
- }
-
- element = gst_element_factory_make(element_name, "test");
- if(!element) {
- purple_debug_info("vvconfig", "'%s' - unable to find element\n", element_name);
- return g_list_reverse(ret);
- }
-
- klass = G_OBJECT_GET_CLASS (element);
- if(!klass) {
- purple_debug_info("vvconfig", "'%s' - unable to find G_Object Class\n", element_name);
- return g_list_reverse(ret);
- }
-
- if (!g_object_class_find_property(klass, "device") ||
- !GST_IS_PROPERTY_PROBE(element) ||
- !(probe = GST_PROPERTY_PROBE(element)) ||
- !(pspec = gst_property_probe_get_property(probe, "device"))) {
- purple_debug_info("vvconfig", "'%s' - no device\n", element_name);
- } else {
- gint n;
- GValueArray *array;
-
- /* Set autoprobe[-fps] to FALSE to avoid delays when probing. */
- if (g_object_class_find_property (klass, "autoprobe")) {
- g_object_set (G_OBJECT (element), "autoprobe", FALSE, NULL);
- if (g_object_class_find_property (klass, "autoprobe-fps")) {
- g_object_set (G_OBJECT (element), "autoprobe-fps", FALSE, NULL);
- }
- }
-
- array = gst_property_probe_probe_and_get_values (probe, pspec);
- if (array == NULL) {
- purple_debug_info("vvconfig", "'%s' has no devices\n", element_name);
- return g_list_reverse(ret);
- }
-
- for (n=0; n < array->n_values; ++n) {
- GValue *device;
- const gchar *name;
- const gchar *device_name;
-
-G_GNUC_BEGIN_IGNORE_DEPRECATIONS
- /* GValueArray is in gstreamer-0.10 API */
- device = g_value_array_get_nth(array, n);
-G_GNUC_END_IGNORE_DEPRECATIONS
- g_object_set_property(G_OBJECT(element), "device", device);
- if (gst_element_set_state(element, GST_STATE_READY)
- != GST_STATE_CHANGE_SUCCESS) {
- purple_debug_warning("vvconfig",
- "Error changing state of %s\n",
- element_name);
- continue;
- }
-
- g_object_get(G_OBJECT(element), "device-name", &name, NULL);
- device_name = g_value_get_string(device);
- if (name == NULL)
- name = _("Unknown");
- purple_debug_info("vvconfig", "Found device %s : %s for %s\n",
- device_name, name, element_name);
- ret = g_list_prepend(ret, (gpointer)name);
- ret = g_list_prepend(ret, (gpointer)device_name);
- gst_element_set_state(element, GST_STATE_NULL);
- }
- }
- gst_object_unref(element);
-
- return g_list_reverse(ret);
-}
-
-static GList *
-get_element_plugins(const gchar **plugins)
-{
- GList *ret = NULL;
-
- ret = g_list_prepend(ret, "Default");
- ret = g_list_prepend(ret, "");
- for (; plugins[0] && plugins[1]; plugins += 2) {
- if (gst_default_registry_check_feature_version(
- plugins[0], 0, 0, 0)) {
- ret = g_list_prepend(ret, (gpointer)plugins[1]);
- ret = g_list_prepend(ret, (gpointer)plugins[0]);
- }
- }
- ret = g_list_reverse(ret);
- return ret;
-}
-
-static void
-device_changed_cb(const gchar *name, PurplePrefType type,
- gconstpointer value, gpointer data)
-{
- GtkSizeGroup *sg = data;
- GtkWidget *parent, *widget;
- GSList *widgets;
- GList *devices;
- GValue gvalue;
- gint position;
- gchar *label, *pref;
-
- widgets = gtk_size_group_get_widgets(GTK_SIZE_GROUP(sg));
- for (; widgets; widgets = g_slist_next(widgets)) {
- const gchar *widget_name =
- gtk_widget_get_name(GTK_WIDGET(widgets->data));
- if (!strcmp(widget_name, name)) {
- gchar *temp_str;
- gchar delimiters[3] = {0, 0, 0};
- const gchar *text;
- gint keyval, pos;
-
- widget = widgets->data;
- /* Get label with _ from the GtkLabel */
- text = gtk_label_get_text(GTK_LABEL(widget));
- keyval = gtk_label_get_mnemonic_keyval(GTK_LABEL(widget));
- delimiters[0] = g_ascii_tolower(keyval);
- delimiters[1] = g_ascii_toupper(keyval);
- pos = strcspn(text, delimiters);
- if (pos != -1) {
- temp_str = g_strndup(text, pos);
- label = g_strconcat(temp_str, "_",
- text + pos, NULL);
- g_free(temp_str);
- } else {
- label = g_strdup(text);
- }
- break;
- }
- }
-
- if (widgets == NULL)
- return;
-
- parent = gtk_widget_get_parent(widget);
- widget = parent;
- parent = gtk_widget_get_parent(GTK_WIDGET(widget));
- gvalue.g_type = 0;
- g_value_init(&gvalue, G_TYPE_INT);
- gtk_container_child_get_property(GTK_CONTAINER(parent),
- GTK_WIDGET(widget), "position", &gvalue);
- position = g_value_get_int(&gvalue);
- g_value_unset(&gvalue);
- gtk_widget_destroy(widget);
-
- pref = g_strdup(name);
- strcpy(pref + strlen(pref) - strlen("plugin"), "device");
- devices = get_element_devices(value);
- if (g_list_find_custom(devices, purple_prefs_get_string(pref),
- (GCompareFunc)strcmp) == NULL)
- purple_prefs_set_string(pref, g_list_next(devices)->data);
- widget = pidgin_prefs_dropdown_from_list(parent,
- label, PURPLE_PREF_STRING,
- pref, devices);
- g_list_free(devices);
- g_signal_connect_swapped(widget, "destroy",
- G_CALLBACK(g_free), pref);
- g_free(label);
- gtk_misc_set_alignment(GTK_MISC(widget), 0, 0.5);
- gtk_widget_set_name(widget, name);
- gtk_size_group_add_widget(sg, widget);
- gtk_box_reorder_child(GTK_BOX(parent),
- gtk_widget_get_parent(GTK_WIDGET(widget)), position);
-}
-
-static void
-get_plugin_frame(GtkWidget *parent, GtkSizeGroup *sg,
- const gchar *name, const gchar *plugin_label,
- const gchar **plugin_strs, const gchar *plugin_pref,
- const gchar *device_label, const gchar *device_pref)
-{
- GtkWidget *vbox, *widget;
- GList *plugins, *devices;
-
- vbox = pidgin_make_frame(parent, name);
-
- /* Setup plugin preference */
- plugins = get_element_plugins(plugin_strs);
- widget = pidgin_prefs_dropdown_from_list(vbox, plugin_label,
- PURPLE_PREF_STRING, plugin_pref, plugins);
- g_list_free(plugins);
- gtk_size_group_add_widget(sg, widget);
- gtk_misc_set_alignment(GTK_MISC(widget), 0, 0.5);
-
- /* Setup device preference */
- devices = get_element_devices(purple_prefs_get_string(plugin_pref));
- if (g_list_find_custom(devices, purple_prefs_get_string(device_pref),
- (GCompareFunc) strcmp) == NULL)
- purple_prefs_set_string(device_pref, g_list_next(devices)->data);
- widget = pidgin_prefs_dropdown_from_list(vbox, device_label,
- PURPLE_PREF_STRING, device_pref, devices);
- g_list_free(devices);
- gtk_widget_set_name(widget, plugin_pref);
- gtk_size_group_add_widget(sg, widget);
- gtk_misc_set_alignment(GTK_MISC(widget), 0, 0.5);
-
- purple_prefs_connect_callback(vbox, plugin_pref,
- device_changed_cb, sg);
- g_signal_connect_swapped(vbox, "destroy",
- G_CALLBACK(purple_prefs_disconnect_by_handle), vbox);
-}
-
-static GtkWidget *
-get_plugin_config_frame(PurplePlugin *plugin) {
- GtkWidget *notebook, *vbox_audio, *vbox_video;
- GtkSizeGroup *sg;
-
- notebook = gtk_notebook_new();
- gtk_container_set_border_width(GTK_CONTAINER(notebook),
- PIDGIN_HIG_BORDER);
- gtk_widget_show(notebook);
-
- vbox_audio = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
- vbox_video = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
- gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
- vbox_audio, gtk_label_new(_("Audio")));
- gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
- vbox_video, gtk_label_new(_("Video")));
- gtk_container_set_border_width(GTK_CONTAINER (vbox_audio),
- PIDGIN_HIG_BORDER);
- gtk_container_set_border_width(GTK_CONTAINER (vbox_video),
- PIDGIN_HIG_BORDER);
-
- gtk_widget_show(vbox_audio);
- gtk_widget_show(vbox_video);
-
- sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
-
- get_plugin_frame(vbox_audio, sg, _("Output"), _("_Plugin"), AUDIO_SINK_PLUGINS,
- "/plugins/core/vvconfig/audio/sink/plugin", _("_Device"),
- "/plugins/core/vvconfig/audio/sink/device");
- get_plugin_frame(vbox_audio, sg, _("Input"), _("P_lugin"), AUDIO_SRC_PLUGINS,
- "/plugins/core/vvconfig/audio/src/plugin", _("D_evice"),
- "/plugins/core/vvconfig/audio/src/device");
-
- get_plugin_frame(vbox_video, sg, _("Output"), _("_Plugin"), VIDEO_SINK_PLUGINS,
- "/plugins/gtk/vvconfig/video/sink/plugin", _("_Device"),
- "/plugins/gtk/vvconfig/video/sink/device");
- get_plugin_frame(vbox_video, sg, _("Input"), _("P_lugin"), VIDEO_SRC_PLUGINS,
- "/plugins/core/vvconfig/video/src/plugin", _("D_evice"),
- "/plugins/core/vvconfig/video/src/device");
-
- return notebook;
-}
-
-static GstElement *
-create_video_src(PurpleMedia *media,
- const gchar *session_id, const gchar *participant)
-{
- const gchar *plugin = purple_prefs_get_string(
- "/plugins/core/vvconfig/video/src/plugin");
- const gchar *device = purple_prefs_get_string(
- "/plugins/core/vvconfig/video/src/device");
- GstElement *ret;
-
- if (plugin[0] == '\0')
- return purple_media_element_info_call_create(old_video_src,
- media, session_id, participant);
-
- ret = gst_element_factory_make(plugin, "vvconfig-videosrc");
- if (device[0] != '\0')
- g_object_set(G_OBJECT(ret), "device", device, NULL);
- if (!strcmp(plugin, "videotestsrc"))
- g_object_set(G_OBJECT(ret), "is-live", 1, NULL);
- return ret;
-}
-
-static GstElement *
-create_video_sink(PurpleMedia *media,
- const gchar *session_id, const gchar *participant)
-{
- const gchar *plugin = purple_prefs_get_string(
- "/plugins/gtk/vvconfig/video/sink/plugin");
- const gchar *device = purple_prefs_get_string(
- "/plugins/gtk/vvconfig/video/sink/device");
- GstElement *ret;
-
- if (plugin[0] == '\0')
- return purple_media_element_info_call_create(old_video_sink,
- media, session_id, participant);
-
- ret = gst_element_factory_make(plugin, NULL);
- if (device[0] != '\0')
- g_object_set(G_OBJECT(ret), "device", device, NULL);
- return ret;
-}
-
-static GstElement *
-create_audio_src(PurpleMedia *media,
- const gchar *session_id, const gchar *participant)
-{
- const gchar *plugin = purple_prefs_get_string(
- "/plugins/core/vvconfig/audio/src/plugin");
- const gchar *device = purple_prefs_get_string(
- "/plugins/core/vvconfig/audio/src/device");
- GstElement *ret;
-
- if (plugin[0] == '\0')
- return purple_media_element_info_call_create(old_audio_src,
- media, session_id, participant);
-
- ret = gst_element_factory_make(plugin, NULL);
- if (device[0] != '\0')
- g_object_set(G_OBJECT(ret), "device", device, NULL);
- return ret;
-}
-
-static GstElement *
-create_audio_sink(PurpleMedia *media,
- const gchar *session_id, const gchar *participant)
-{
- const gchar *plugin = purple_prefs_get_string(
- "/plugins/core/vvconfig/audio/sink/plugin");
- const gchar *device = purple_prefs_get_string(
- "/plugins/core/vvconfig/audio/sink/device");
- GstElement *ret;
-
- if (plugin[0] == '\0')
- return purple_media_element_info_call_create(old_audio_sink,
- media, session_id, participant);
-
- ret = gst_element_factory_make(plugin, NULL);
- if (device[0] != '\0')
- g_object_set(G_OBJECT(ret), "device", device, NULL);
- return ret;
-}
-
-static void
-set_element_info_cond(PurpleMediaElementInfo *old_info,
- PurpleMediaElementInfo *new_info, const gchar *id)
-{
- gchar *element_id = purple_media_element_info_get_id(old_info);
- if (!strcmp(element_id, id))
- purple_media_manager_set_active_element(
- purple_media_manager_get(), new_info);
- g_free(element_id);
-}
-
-static gboolean
-plugin_load(PurplePlugin *plugin)
-{
- PurpleMediaManager *manager;
- PurpleMediaElementInfo *video_src, *video_sink,
- *audio_src, *audio_sink;
-
- /* Disable the plugin if the UI doesn't support VV */
- if (purple_media_manager_get_ui_caps(purple_media_manager_get()) ==
- PURPLE_MEDIA_CAPS_NONE)
- return FALSE;
-
- purple_prefs_add_none("/plugins/core/vvconfig");
- purple_prefs_add_none("/plugins/core/vvconfig/audio");
- purple_prefs_add_none("/plugins/core/vvconfig/audio/src");
- purple_prefs_add_string("/plugins/core/vvconfig/audio/src/plugin", "");
- purple_prefs_add_string("/plugins/core/vvconfig/audio/src/device", "");
- purple_prefs_add_none("/plugins/core/vvconfig/audio/sink");
- purple_prefs_add_string("/plugins/core/vvconfig/audio/sink/plugin", "");
- purple_prefs_add_string("/plugins/core/vvconfig/audio/sink/device", "");
- purple_prefs_add_none("/plugins/core/vvconfig/video");
- purple_prefs_add_none("/plugins/core/vvconfig/video/src");
- purple_prefs_add_string("/plugins/core/vvconfig/video/src/plugin", "");
- purple_prefs_add_string("/plugins/core/vvconfig/video/src/device", "");
- purple_prefs_add_none("/plugins/gtk/vvconfig");
- purple_prefs_add_none("/plugins/gtk/vvconfig/video");
- purple_prefs_add_none("/plugins/gtk/vvconfig/video/sink");
- purple_prefs_add_string("/plugins/gtk/vvconfig/video/sink/plugin", "");
- purple_prefs_add_string("/plugins/gtk/vvconfig/video/sink/device", "");
-
- video_src = g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO,
- "id", "vvconfig-videosrc",
- "name", "VV Conf Plugin Video Source",
- "type", PURPLE_MEDIA_ELEMENT_VIDEO
- | PURPLE_MEDIA_ELEMENT_SRC
- | PURPLE_MEDIA_ELEMENT_ONE_SRC
- | PURPLE_MEDIA_ELEMENT_UNIQUE,
- "create-cb", create_video_src, NULL);
- video_sink = g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO,
- "id", "vvconfig-videosink",
- "name", "VV Conf Plugin Video Sink",
- "type", PURPLE_MEDIA_ELEMENT_VIDEO
- | PURPLE_MEDIA_ELEMENT_SINK
- | PURPLE_MEDIA_ELEMENT_ONE_SINK,
- "create-cb", create_video_sink, NULL);
- audio_src = g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO,
- "id", "vvconfig-audiosrc",
- "name", "VV Conf Plugin Audio Source",
- "type", PURPLE_MEDIA_ELEMENT_AUDIO
- | PURPLE_MEDIA_ELEMENT_SRC
- | PURPLE_MEDIA_ELEMENT_ONE_SRC
- | PURPLE_MEDIA_ELEMENT_UNIQUE,
- "create-cb", create_audio_src, NULL);
- audio_sink = g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO,
- "id", "vvconfig-audiosink",
- "name", "VV Conf Plugin Audio Sink",
- "type", PURPLE_MEDIA_ELEMENT_AUDIO
- | PURPLE_MEDIA_ELEMENT_SINK
- | PURPLE_MEDIA_ELEMENT_ONE_SINK,
- "create-cb", create_audio_sink, NULL);
-
- purple_debug_info("gtkmedia", "Registering media element types\n");
- manager = purple_media_manager_get();
-
- old_video_src = purple_media_manager_get_active_element(manager,
- PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SRC);
- old_video_sink = purple_media_manager_get_active_element(manager,
- PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SINK);
- old_audio_src = purple_media_manager_get_active_element(manager,
- PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SRC);
- old_audio_sink = purple_media_manager_get_active_element(manager,
- PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SINK);
-
- set_element_info_cond(old_video_src, video_src, "pidgindefaultvideosrc");
- set_element_info_cond(old_video_sink, video_sink, "pidgindefaultvideosink");
- set_element_info_cond(old_audio_src, audio_src, "pidgindefaultaudiosrc");
- set_element_info_cond(old_audio_sink, audio_sink, "pidgindefaultaudiosink");
-
- return TRUE;
-}
-
-static void
-config_destroy(GtkObject *w, gpointer nul)
-{
- purple_debug_info("vvconfig", "closing vv configuration window\n");
- window = NULL;
-}
-
-static void
-config_close(GtkObject *w, gpointer nul)
-{
- gtk_widget_destroy(GTK_WIDGET(window));
-}
-
-typedef GtkWidget *(*FrameCreateCb)(PurplePlugin *plugin);
-
-static void
-show_config(PurplePluginAction *action)
-{
- if (!window) {
- FrameCreateCb create_frame = action->user_data;
- GtkWidget *vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
- GtkWidget *hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
- GtkWidget *config_frame = create_frame(NULL);
- GtkWidget *close = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-
- gtk_container_add(GTK_CONTAINER(vbox), config_frame);
- gtk_container_add(GTK_CONTAINER(vbox), hbox);
- window = pidgin_create_window(action->label,
- PIDGIN_HIG_BORDER, NULL, FALSE);
- g_signal_connect(G_OBJECT(window), "destroy",
- G_CALLBACK(config_destroy), NULL);
- g_signal_connect(G_OBJECT(close), "clicked",
- G_CALLBACK(config_close), NULL);
- gtk_box_pack_end(GTK_BOX(hbox), close, FALSE, FALSE, PIDGIN_HIG_BORDER);
- gtk_container_add(GTK_CONTAINER(window), vbox);
- gtk_widget_show(GTK_WIDGET(close));
- gtk_widget_show(GTK_WIDGET(vbox));
- gtk_widget_show(GTK_WIDGET(hbox));
- }
- gtk_window_present(GTK_WINDOW(window));
-}
-
-static GstElement *
-create_pipeline()
-{
- GstElement *pipeline = gst_pipeline_new("voicetest");
- GstElement *src = create_audio_src(NULL, NULL, NULL);
- GstElement *sink = create_audio_sink(NULL, NULL, NULL);
- GstElement *volume = gst_element_factory_make("volume", "volume");
- GstElement *level = gst_element_factory_make("level", "level");
- GstElement *valve = gst_element_factory_make("valve", "valve");
-
- gst_bin_add_many(GST_BIN(pipeline), src, volume, level, valve, sink, NULL);
- gst_element_link_many(src, volume, level, valve, sink, NULL);
-
- gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING);
-
- return pipeline;
-}
-
-static void
-on_volume_change_cb(GtkRange *range, GstBin *pipeline)
-{
- GstElement *volume;
-
- g_return_if_fail(pipeline != NULL);
-
- volume = gst_bin_get_by_name(pipeline, "volume");
- g_object_set(volume, "volume", gtk_range_get_value(range) / 10.0, NULL);
-}
-
-static gdouble
-gst_msg_db_to_percent(GstMessage *msg, gchar *value_name)
-{
- const GValue *list;
- const GValue *value;
- gdouble value_db;
- gdouble percent;
-
- list = gst_structure_get_value(
- gst_message_get_structure(msg), value_name);
- value = gst_value_list_get_value(list, 0);
- value_db = g_value_get_double(value);
- percent = pow(10, value_db / 20);
- return (percent > 1.0) ? 1.0 : percent;
-}
-
-typedef struct
-{
- GtkProgressBar *level;
- GtkRange *threshold;
-} BusCbCtx;
-
-static gboolean
-gst_bus_cb(GstBus *bus, GstMessage *msg, BusCbCtx *ctx)
-{
- if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ELEMENT &&
- gst_structure_has_name(msg->structure, "level")) {
-
- GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg));
- gchar *name = gst_element_get_name(src);
-
- if (!strcmp(name, "level")) {
- gdouble percent;
- gdouble threshold;
- GstElement *valve;
-
- percent = gst_msg_db_to_percent(msg, "rms");
- gtk_progress_bar_set_fraction(ctx->level, percent * 5);
-
- percent = gst_msg_db_to_percent(msg, "decay");
- threshold = gtk_range_get_value(ctx->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(ctx->level,
- "text", (percent < threshold) ? _("DROP") : " ", NULL);
- }
-
- g_free(name);
- }
-
- return TRUE;
-}
-
-static void
-voice_test_frame_destroy_cb(GtkObject *w, GstElement *pipeline)
-{
- g_return_if_fail(GST_IS_ELEMENT(pipeline));
-
- gst_element_set_state(pipeline, GST_STATE_NULL);
- gst_object_unref(pipeline);
-}
-
-static void
-volume_scale_destroy_cb(GtkRange *volume, gpointer nul)
-{
- purple_prefs_set_int("/purple/media/audio/volume/input",
- gtk_range_get_value(volume));
-}
-
-static gchar*
-threshold_value_format_cb(GtkScale *scale, gdouble value)
-{
- return g_strdup_printf ("%.*f%%", gtk_scale_get_digits(scale), value);
-}
-
-static void
-threshold_scale_destroy_cb(GtkRange *threshold, gpointer nul)
-{
- purple_prefs_set_int("/purple/media/audio/silence_threshold",
- gtk_range_get_value(threshold));
-}
-
-static GtkWidget *
-get_voice_test_frame(PurplePlugin *plugin)
-{
- GtkWidget *vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
- GtkWidget *level = gtk_progress_bar_new();
- GtkWidget *volume = gtk_hscale_new_with_range(0, 100, 1);
- GtkWidget *threshold = gtk_hscale_new_with_range(0, 100, 1);
- GtkWidget *label;
- GtkTable *table = GTK_TABLE(gtk_table_new(2, 2, FALSE));
-
- GstElement *pipeline;
- GstBus *bus;
- BusCbCtx *ctx;
-
- g_object_set(vbox, "width-request", 500, NULL);
-
- gtk_table_set_row_spacings(table, PIDGIN_HIG_BOX_SPACE);
- gtk_table_set_col_spacings(table, PIDGIN_HIG_BOX_SPACE);
-
- label = gtk_label_new(_("Volume:"));
- g_object_set(label, "xalign", 0.0, NULL);
- gtk_table_attach(table, label, 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
- gtk_table_attach_defaults(table, volume, 1, 2, 0, 1);
- label = gtk_label_new(_("Silence threshold:"));
- g_object_set(label, "xalign", 0.0, "yalign", 1.0, NULL);
- gtk_table_attach(table, label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
- gtk_table_attach_defaults(table, threshold, 1, 2, 1, 2);
-
- gtk_container_add(GTK_CONTAINER(vbox), level);
- gtk_container_add(GTK_CONTAINER(vbox), GTK_WIDGET(table));
- gtk_widget_show_all(vbox);
-
- pipeline = create_pipeline();
- bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
- gst_bus_add_signal_watch(bus);
- ctx = g_new(BusCbCtx, 1);
- ctx->level = GTK_PROGRESS_BAR(level);
- ctx->threshold = GTK_RANGE(threshold);
- g_signal_connect_data(bus, "message", G_CALLBACK(gst_bus_cb),
- ctx, (GClosureNotify)g_free, 0);
- gst_object_unref(bus);
-
- g_signal_connect(volume, "value-changed",
- (GCallback)on_volume_change_cb, pipeline);
-
- gtk_range_set_value(GTK_RANGE(volume),
- purple_prefs_get_int("/purple/media/audio/volume/input"));
- gtk_widget_set(volume, "draw-value", FALSE, NULL);
-
- gtk_range_set_value(GTK_RANGE(threshold),
- purple_prefs_get_int("/purple/media/audio/silence_threshold"));
-
- g_signal_connect(vbox, "destroy",
- G_CALLBACK(voice_test_frame_destroy_cb), pipeline);
- g_signal_connect(volume, "destroy",
- G_CALLBACK(volume_scale_destroy_cb), NULL);
- g_signal_connect(threshold, "format-value",
- G_CALLBACK(threshold_value_format_cb), NULL);
- g_signal_connect(threshold, "destroy",
- G_CALLBACK(threshold_scale_destroy_cb), NULL);
-
- return vbox;
-}
-
-static GList *
-actions(PurplePlugin *plugin, gpointer context)
-{
- GList *l = NULL;
- PurplePluginAction *act = NULL;
-
- act = purple_plugin_action_new(_("Input and Output Settings"),
- show_config);
- act->user_data = get_plugin_config_frame;
- l = g_list_append(l, act);
-
- act = purple_plugin_action_new(_("Microphone Test"),
- show_config);
- act->user_data = get_voice_test_frame;
- l = g_list_append(l, act);
-
- return l;
-}
-
-static gboolean
-plugin_unload(PurplePlugin *plugin)
-{
- PurpleMediaManager *manager = purple_media_manager_get();
- purple_media_manager_set_active_element(manager, old_video_src);
- purple_media_manager_set_active_element(manager, old_video_sink);
- purple_media_manager_set_active_element(manager, old_audio_src);
- purple_media_manager_set_active_element(manager, old_audio_sink);
- return TRUE;
-}
-
-static PidginPluginUiInfo ui_info = {
- get_plugin_config_frame,
- 0, /* page_num (Reserved) */
- /* Padding */
- NULL,
- NULL,
- NULL,
- NULL
-};
-
-static PurplePluginInfo info =
-{
- PURPLE_PLUGIN_MAGIC, /**< magic */
- PURPLE_MAJOR_VERSION, /**< major version */
- PURPLE_MINOR_VERSION, /**< minor version */
- PURPLE_PLUGIN_STANDARD, /**< type */
- PIDGIN_PLUGIN_TYPE, /**< ui_requirement */
- 0, /**< flags */
- NULL, /**< dependencies */
- PURPLE_PRIORITY_DEFAULT, /**< priority */
-
- "gtk-maiku-vvconfig", /**< id */
- N_("Voice/Video Settings"), /**< name */
- DISPLAY_VERSION, /**< version */
- N_("Configure your microphone and webcam."), /**< summary */
- N_("Configure microphone and webcam "
- "settings for voice/video calls."), /**< description */
- "Mike Ruprecht <cmaiku@gmail.com>", /**< author */
- PURPLE_WEBSITE, /**< homepage */
-
- plugin_load, /**< load */
- plugin_unload, /**< unload */
- NULL, /**< destroy */
-
- &ui_info, /**< ui_info */
- NULL, /**< extra_info */
- NULL, /**< prefs_info */
- actions, /**< actions */
-
- /* padding */
- NULL,
- NULL,
- NULL,
- NULL
-};
-
-static void
-init_plugin(PurplePlugin *plugin) {
-}
-
-PURPLE_INIT_PLUGIN(vvconfig, init_plugin, info)
diff --git a/pidgin/plugins/webkit.c b/pidgin/plugins/webkit.c
new file mode 100644
index 0000000000..45e814e729
--- /dev/null
+++ b/pidgin/plugins/webkit.c
@@ -0,0 +1,85 @@
+/*
+ * WebKit - Open the inspector on any WebKit views.
+ * Copyright (C) 2011 Elliott Sales de Andrade <qulogic@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.
+ */
+
+#include "internal.h"
+
+#include "version.h"
+
+#include "gtkplugin.h"
+
+static gboolean
+plugin_load(PurplePlugin *plugin)
+{
+ purple_prefs_add_none(PIDGIN_PREFS_ROOT "/webview");
+ purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/webview/inspector_enabled", FALSE);
+
+ purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/webview/inspector_enabled", TRUE);
+
+ return TRUE;
+}
+
+static gboolean
+plugin_unload(PurplePlugin *plugin)
+{
+ purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/webview/inspector_enabled", FALSE);
+
+ return TRUE;
+}
+
+static PurplePluginInfo info =
+{
+ PURPLE_PLUGIN_MAGIC,
+ PURPLE_MAJOR_VERSION, /**< major version */
+ PURPLE_MINOR_VERSION, /**< minor version */
+ PURPLE_PLUGIN_STANDARD, /**< type */
+ PIDGIN_PLUGIN_TYPE, /**< ui_requirement */
+ 0, /**< flags */
+ NULL, /**< dependencies */
+ PURPLE_PRIORITY_DEFAULT, /**< priority */
+
+ "gtkwebkit-inspect", /**< id */
+ N_("WebKit Development"), /**< name */
+ DISPLAY_VERSION, /**< version */
+ N_("Enables WebKit Inspector."), /**< summary */
+ N_("Enables WebKit's built-in inspector. This "
+ "may be viewed by right-clicking a WebKit "
+ "widget and selecting 'Inspect Element'."), /**< description */
+ "Elliott Sales de Andrade <qulogic@pidgin.im>", /**< author */
+ PURPLE_WEBSITE, /**< homepage */
+ plugin_load, /**< load */
+ plugin_unload, /**< unload */
+ NULL, /**< destroy */
+ NULL, /**< ui_info */
+ NULL, /**< extra_info */
+ NULL, /**< prefs_info */
+ NULL, /**< actions */
+
+ /* padding */
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static void
+init_plugin(PurplePlugin *plugin)
+{
+}
+
+PURPLE_INIT_PLUGIN(webkit-devel, init_plugin, info)
diff --git a/pidgin/plugins/win32/transparency/Makefile.am b/pidgin/plugins/win32/transparency/Makefile.am
new file mode 100644
index 0000000000..ad97d98504
--- /dev/null
+++ b/pidgin/plugins/win32/transparency/Makefile.am
@@ -0,0 +1,16 @@
+plugindir = @PIDGIN_PLUGINDIR@
+
+plugin_LTLIBRARIES = win2ktrans.la
+
+win2ktrans_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+win2ktrans_la_SOURCES = win2ktrans.c
+win2ktrans_la_LIBADD = @PIDGIN_LIBS@
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/libpurple \
+ -I$(top_builddir)/libpurple \
+ -I$(top_srcdir)/pidgin \
+ $(DEBUG_CPPFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(PLUGIN_CFLAGS)
diff --git a/pidgin/plugins/win32/transparency/Makefile.mingw b/pidgin/plugins/win32/transparency/Makefile.mingw
index 3b7b2e2cfe..02303fa9cc 100644
--- a/pidgin/plugins/win32/transparency/Makefile.mingw
+++ b/pidgin/plugins/win32/transparency/Makefile.mingw
@@ -20,6 +20,7 @@ INCLUDE_PATHS += -I. \
-I$(GTK_TOP)/include/pango-1.0 \
-I$(GTK_TOP)/include/atk-1.0 \
-I$(GTK_TOP)/include/cairo \
+ -I$(GTK_TOP)/include/gdk-pixbuf-2.0 \
-I$(GTK_TOP)/lib/glib-2.0/include \
-I$(GTK_TOP)/lib/gtk-2.0/include \
-I$(PURPLE_TOP) \
diff --git a/pidgin/plugins/win32/transparency/win2ktrans.c b/pidgin/plugins/win32/transparency/win2ktrans.c
index a4ae3b538d..8ec8f03601 100644
--- a/pidgin/plugins/win32/transparency/win2ktrans.c
+++ b/pidgin/plugins/win32/transparency/win2ktrans.c
@@ -21,16 +21,15 @@
* 02111-1301, USA.
*
*/
-#ifndef _WIN32_WINNT
-#define _WIN32_WINNT 0x0500
-#endif
-#include <gdk/gdkwin32.h>
+
#include "internal.h"
+#include <gdk/gdkwin32.h>
#include "core.h"
#include "prefs.h"
#include "debug.h"
+#include "gtk3compat.h"
#include "gtkconv.h"
#include "gtkplugin.h"
#include "gtkprefs.h"
@@ -44,9 +43,9 @@
*/
#define WINTRANS_PLUGIN_ID "gtk-win-trans"
-#define blist (purple_get_blist() \
- ? (PIDGIN_BLIST(purple_get_blist()) \
- ? ((PIDGIN_BLIST(purple_get_blist()))->window) \
+#define blist (purple_blist_get_buddy_list() \
+ ? (PIDGIN_BLIST(purple_blist_get_buddy_list()) \
+ ? ((PIDGIN_BLIST(purple_blist_get_buddy_list()))->window) \
: NULL) \
: NULL)
@@ -80,7 +79,7 @@ static GSList *window_list = NULL;
static void set_wintrans(GtkWidget *window, int alpha, gboolean enabled,
gboolean always_on_top) {
- HWND hWnd = GDK_WINDOW_HWND(window->window);
+ HWND hWnd = GDK_WINDOW_HWND(gtk_widget_get_window(window));
LONG style = GetWindowLong(hWnd, GWL_EXSTYLE);
if (enabled) {
style |= WS_EX_LAYERED;
@@ -167,7 +166,7 @@ static GtkWidget *wintrans_slider(GtkWidget *win) {
gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
gtk_widget_show(frame);
- hbox = gtk_hbox_new(FALSE, 5);
+ hbox = gtk_hbox_new(GTK_ORIENTATION_HORIZONTAL, 5);
gtk_container_add(GTK_CONTAINER(frame), hbox);
label = gtk_label_new(_("Opacity:"));
@@ -176,10 +175,10 @@ static GtkWidget *wintrans_slider(GtkWidget *win) {
slider = gtk_hscale_new_with_range(50, 255, 1);
gtk_range_set_value(GTK_RANGE(slider), imalpha);
- gtk_widget_set_usize(GTK_WIDGET(slider), 200, -1);
+ gtk_widget_set_size_request(GTK_WIDGET(slider), 200, -1);
/* On slider val change, update window's transparency level */
- g_signal_connect(GTK_OBJECT(slider), "value-changed",
+ g_signal_connect(G_OBJECT(slider), "value-changed",
G_CALLBACK(change_alpha), win);
gtk_box_pack_start(GTK_BOX(hbox), slider, FALSE, TRUE, 5);
@@ -205,7 +204,7 @@ static slider_win* find_slidwin(GtkWidget *win) {
}
/* Clean up transparency stuff for the conv window */
-static void cleanup_conv_window(PidginWindow *win) {
+static void cleanup_conv_window(PidginConvWindow *win) {
GtkWidget *window = win->window;
slider_win *slidwin = NULL;
@@ -225,7 +224,7 @@ static void cleanup_conv_window(PidginWindow *win) {
static void
conversation_delete_cb(PurpleConversation *conv) {
- PidginWindow *win = pidgin_conv_get_window(PIDGIN_CONVERSATION(conv));
+ PidginConvWindow *win = pidgin_conv_get_window(PIDGIN_CONVERSATION(conv));
/* If it is the last conversation in the window, cleanup */
if (win != NULL && pidgin_conv_window_get_gtkconv_count(win) == 1)
cleanup_conv_window(win);
@@ -257,7 +256,7 @@ static void add_slider(GtkWidget *win) {
GTK_CONTAINER(win));
wl != NULL;
wl = wl->next) {
- if (GTK_IS_VBOX(GTK_OBJECT(wl->data)))
+ if (GTK_IS_VBOX(G_OBJECT(wl->data)))
vbox = GTK_WIDGET(wl->data);
else {
purple_debug_error(WINTRANS_PLUGIN_ID,
@@ -323,7 +322,7 @@ static void remove_convs_wintrans(gboolean remove_signal) {
GList *wins;
for (wins = pidgin_conv_windows_get_list(); wins; wins = wins->next) {
- PidginWindow *win = wins->data;
+ PidginConvWindow *win = wins->data;
GtkWidget *window = win->window;
if (purple_prefs_get_bool(OPT_WINTRANS_IM_ENABLED))
@@ -338,7 +337,7 @@ static void remove_convs_wintrans(gboolean remove_signal) {
remove_sliders();
}
-static void set_conv_window_trans(PidginWindow *oldwin, PidginWindow *newwin) {
+static void set_conv_window_trans(PidginConvWindow *oldwin, PidginConvWindow *newwin) {
GtkWidget *win = newwin->window;
/* check prefs to see if we want trans */
@@ -375,7 +374,7 @@ static void update_convs_wintrans(GtkWidget *toggle_btn, const char *pref) {
GList *wins;
for (wins = pidgin_conv_windows_get_list(); wins; wins = wins->next) {
- PidginWindow *win = wins->data;
+ PidginConvWindow *win = wins->data;
set_conv_window_trans(NULL, win);
}
@@ -387,11 +386,11 @@ static void update_convs_wintrans(GtkWidget *toggle_btn, const char *pref) {
}
static void
-conv_updated_cb(PurpleConversation *conv, PurpleConvUpdateType type) {
+conv_updated_cb(PurpleConversation *conv, PurpleConversationUpdateType type) {
PidginConversation *pconv = PIDGIN_CONVERSATION(conv);
- PidginWindow *win = pidgin_conv_get_window(pconv);
+ PidginConvWindow *win = pidgin_conv_get_window(pconv);
- if (type == PURPLE_CONV_UPDATE_UNSEEN && !pidgin_conv_is_hidden(pconv)
+ if (type == PURPLE_CONVERSATION_UPDATE_UNSEEN && !pidgin_conv_is_hidden(pconv)
&& pconv->unseen_state == PIDGIN_UNSEEN_NONE
&& pidgin_conv_window_get_gtkconv_count(win) == 1) {
GtkWidget *window = win->window;
@@ -414,7 +413,7 @@ conv_updated_cb(PurpleConversation *conv, PurpleConvUpdateType type) {
static void
new_conversation_cb(PurpleConversation *conv) {
- PidginWindow *win = pidgin_conv_get_window(PIDGIN_CONVERSATION(conv));
+ PidginConvWindow *win = pidgin_conv_get_window(PIDGIN_CONVERSATION(conv));
/* If it is the first conversation in the window,
* add the sliders, and set transparency */
@@ -452,7 +451,7 @@ static void alpha_change(GtkWidget *w, gpointer data) {
int imalpha = gtk_range_get_value(GTK_RANGE(w));
for (wins = pidgin_conv_windows_get_list(); wins; wins = wins->next) {
- PidginWindow *win = wins->data;
+ PidginConvWindow *win = wins->data;
set_wintrans(win->window, imalpha, TRUE,
purple_prefs_get_bool(OPT_WINTRANS_IM_ONTOP));
}
@@ -473,7 +472,7 @@ static void update_existing_convs() {
GList *wins;
for (wins = pidgin_conv_windows_get_list(); wins; wins = wins->next) {
- PidginWindow *win = wins->data;
+ PidginConvWindow *win = wins->data;
GtkWidget *window = win->window;
set_conv_window_trans(NULL, win);
@@ -552,7 +551,7 @@ static GtkWidget *get_config_frame(PurplePlugin *plugin) {
imtransbox = pidgin_make_frame(ret, _("IM Conversation Windows"));
button = pidgin_prefs_checkbox(_("_IM window transparency"),
OPT_WINTRANS_IM_ENABLED, imtransbox);
- g_signal_connect(GTK_OBJECT(button), "clicked",
+ g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK(update_convs_wintrans),
(gpointer) OPT_WINTRANS_IM_ENABLED);
@@ -561,12 +560,12 @@ static GtkWidget *get_config_frame(PurplePlugin *plugin) {
gtk_widget_set_sensitive(GTK_WIDGET(trans_box), FALSE);
gtk_widget_show(trans_box);
- g_signal_connect(GTK_OBJECT(button), "clicked",
+ g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK(pidgin_toggle_sensitive), trans_box);
button = pidgin_prefs_checkbox(_("_Show slider bar in IM window"),
OPT_WINTRANS_IM_SLIDER, trans_box);
- g_signal_connect(GTK_OBJECT(button), "clicked",
+ g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK(update_convs_wintrans),
(gpointer) OPT_WINTRANS_IM_SLIDER);
@@ -576,14 +575,14 @@ static GtkWidget *get_config_frame(PurplePlugin *plugin) {
button = pidgin_prefs_checkbox(_("Always on top"), OPT_WINTRANS_IM_ONTOP,
trans_box);
- g_signal_connect(GTK_OBJECT(button), "clicked",
+ g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK(update_convs_wintrans),
(gpointer) OPT_WINTRANS_IM_ONTOP);
gtk_box_pack_start(GTK_BOX(imtransbox), trans_box, FALSE, FALSE, 5);
/* IM transparency slider */
- hbox = gtk_hbox_new(FALSE, 5);
+ hbox = gtk_hbox_new(GTK_ORIENTATION_HORIZONTAL, 5);
label = gtk_label_new(_("Opacity:"));
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
@@ -591,11 +590,11 @@ static GtkWidget *get_config_frame(PurplePlugin *plugin) {
slider = gtk_hscale_new_with_range(50, 255, 1);
gtk_range_set_value(GTK_RANGE(slider),
purple_prefs_get_int(OPT_WINTRANS_IM_ALPHA));
- gtk_widget_set_usize(GTK_WIDGET(slider), 200, -1);
+ gtk_widget_set_size_request(GTK_WIDGET(slider), 200, -1);
- g_signal_connect(GTK_OBJECT(slider), "value-changed",
+ g_signal_connect(G_OBJECT(slider), "value-changed",
G_CALLBACK(alpha_change), NULL);
- g_signal_connect(GTK_OBJECT(slider), "focus-out-event",
+ g_signal_connect(G_OBJECT(slider), "focus-out-event",
G_CALLBACK(alpha_pref_set_int),
(gpointer) OPT_WINTRANS_IM_ALPHA);
@@ -609,7 +608,7 @@ static GtkWidget *get_config_frame(PurplePlugin *plugin) {
bltransbox = pidgin_make_frame (ret, _("Buddy List Window"));
button = pidgin_prefs_checkbox(_("_Buddy List window transparency"),
OPT_WINTRANS_BL_ENABLED, bltransbox);
- g_signal_connect(GTK_OBJECT(button), "clicked",
+ g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK(set_blist_trans),
(gpointer) OPT_WINTRANS_BL_ENABLED);
@@ -617,20 +616,20 @@ static GtkWidget *get_config_frame(PurplePlugin *plugin) {
if (!purple_prefs_get_bool(OPT_WINTRANS_BL_ENABLED))
gtk_widget_set_sensitive(GTK_WIDGET(trans_box), FALSE);
gtk_widget_show(trans_box);
- g_signal_connect(GTK_OBJECT(button), "clicked",
+ g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK(pidgin_toggle_sensitive), trans_box);
button = pidgin_prefs_checkbox(
_("Remove Buddy List window transparency on focus"),
OPT_WINTRANS_BL_ONFOCUS, trans_box);
button = pidgin_prefs_checkbox(_("Always on top"), OPT_WINTRANS_BL_ONTOP,
trans_box);
- g_signal_connect(GTK_OBJECT(button), "clicked",
+ g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK(set_blist_trans),
(gpointer) OPT_WINTRANS_BL_ONTOP);
gtk_box_pack_start(GTK_BOX(bltransbox), trans_box, FALSE, FALSE, 5);
/* IM transparency slider */
- hbox = gtk_hbox_new(FALSE, 5);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
label = gtk_label_new(_("Opacity:"));
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
@@ -639,11 +638,11 @@ static GtkWidget *get_config_frame(PurplePlugin *plugin) {
gtk_range_set_value(GTK_RANGE(slider),
purple_prefs_get_int(OPT_WINTRANS_BL_ALPHA));
- gtk_widget_set_usize(GTK_WIDGET(slider), 200, -1);
+ gtk_widget_set_size_request(GTK_WIDGET(slider), 200, -1);
- g_signal_connect(GTK_OBJECT(slider), "value-changed",
+ g_signal_connect(G_OBJECT(slider), "value-changed",
G_CALLBACK(bl_alpha_change), NULL);
- g_signal_connect(GTK_OBJECT(slider), "focus-out-event",
+ g_signal_connect(G_OBJECT(slider), "focus-out-event",
G_CALLBACK(alpha_pref_set_int),
(gpointer) OPT_WINTRANS_BL_ALPHA);
@@ -660,7 +659,6 @@ static GtkWidget *get_config_frame(PurplePlugin *plugin) {
static PidginPluginUiInfo ui_info =
{
get_config_frame,
- 0, /* page_num (Reserved) */
/* padding */
NULL,
diff --git a/pidgin/plugins/win32/winprefs/Makefile.am b/pidgin/plugins/win32/winprefs/Makefile.am
new file mode 100644
index 0000000000..3e46d8541d
--- /dev/null
+++ b/pidgin/plugins/win32/winprefs/Makefile.am
@@ -0,0 +1,19 @@
+plugindir = @PIDGIN_PLUGINDIR@
+
+plugin_LTLIBRARIES = winprefs.la
+
+winprefs_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
+winprefs_la_SOURCES = \
+ gtkappbar.c \
+ gtkappbar.h \
+ winprefs.c
+winprefs_la_LIBADD = @PIDGIN_LIBS@
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/libpurple \
+ -I$(top_builddir)/libpurple \
+ -I$(top_srcdir)/pidgin \
+ $(DEBUG_CPPFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(PLUGIN_CFLAGS)
diff --git a/pidgin/plugins/win32/winprefs/Makefile.mingw b/pidgin/plugins/win32/winprefs/Makefile.mingw
index 9095458654..52bab8d138 100644
--- a/pidgin/plugins/win32/winprefs/Makefile.mingw
+++ b/pidgin/plugins/win32/winprefs/Makefile.mingw
@@ -21,6 +21,7 @@ INCLUDE_PATHS += -I. \
-I$(GTK_TOP)/include/pango-1.0 \
-I$(GTK_TOP)/include/atk-1.0 \
-I$(GTK_TOP)/include/cairo \
+ -I$(GTK_TOP)/include/gdk-pixbuf-2.0 \
-I$(GTK_TOP)/lib/glib-2.0/include \
-I$(GTK_TOP)/lib/gtk-2.0/include \
-I$(PURPLE_TOP) \
diff --git a/pidgin/plugins/win32/winprefs/gtkappbar.c b/pidgin/plugins/win32/winprefs/gtkappbar.c
index a63124abc9..b3a81c627b 100644
--- a/pidgin/plugins/win32/winprefs/gtkappbar.c
+++ b/pidgin/plugins/win32/winprefs/gtkappbar.c
@@ -43,6 +43,12 @@ typedef BOOL WINAPI purple_GetMonitorInfo(HMONITOR, LPMONITORINFO);
static void gtk_appbar_do_dock(GtkAppBar *ab, UINT side);
+static inline HWND
+appbar_get_handle(GtkAppBar *ab)
+{
+ return GDK_WINDOW_HWND(gtk_widget_get_window(ab->win));
+}
+
/* Retrieve the rectangular display area from the specified monitor
* Return TRUE if successful, otherwise FALSE
*/
@@ -358,7 +364,7 @@ static void show_hide(GtkAppBar *ab, gboolean hide) {
if (hide) {
purple_debug_info("gtkappbar", "hidden\n");
- gtk_appbar_unregister(ab, GDK_WINDOW_HWND(ab->win->window));
+ gtk_appbar_unregister(ab, appbar_get_handle(ab));
ab->docked = TRUE;
ab->iconized = TRUE;
} else {
@@ -457,7 +463,7 @@ static GdkFilterReturn wnd_size(GtkAppBar *ab, GdkXEvent *xevent) {
if(msg->wParam == SIZE_MINIMIZED) {
purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "Minimize\n");
if(ab->docked) {
- gtk_appbar_unregister(ab, GDK_WINDOW_HWND(ab->win->window));
+ gtk_appbar_unregister(ab, appbar_get_handle(ab));
ab->docked = TRUE;
}
}
@@ -600,21 +606,21 @@ static void gtk_appbar_do_dock(GtkAppBar *ab, UINT side) {
purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "gtk_appbar_do_dock\n");
- if(!ab || !IsWindow(GDK_WINDOW_HWND(ab->win->window)))
- return;
+ if (!ab || !IsWindow(appbar_get_handle(ab)))
+ return;
ab->side = side;
- get_window_normal_rc(GDK_WINDOW_HWND(ab->win->window), &(ab->docked_rect));
+ get_window_normal_rc(appbar_get_handle(ab), &(ab->docked_rect));
CopyRect(&orig, &(ab->docked_rect));
- get_rect_of_window(GDK_WINDOW_HWND(ab->win->window), &windowRect);
- gtk_appbar_querypos(ab, GDK_WINDOW_HWND(ab->win->window), windowRect);
+ get_rect_of_window(appbar_get_handle(ab), &windowRect);
+ gtk_appbar_querypos(ab, appbar_get_handle(ab), windowRect);
if(EqualRect(&orig, &(ab->docked_rect)) == 0)
- MoveWindow(GDK_WINDOW_HWND(ab->win->window),
+ MoveWindow(appbar_get_handle(ab),
ab->docked_rect.left,
ab->docked_rect.top,
ab->docked_rect.right - ab->docked_rect.left,
ab->docked_rect.bottom - ab->docked_rect.top, TRUE);
- gtk_appbar_setpos(ab, GDK_WINDOW_HWND(ab->win->window));
+ gtk_appbar_setpos(ab, appbar_get_handle(ab));
ab->docked = TRUE;
}
@@ -623,7 +629,7 @@ void gtk_appbar_dock(GtkAppBar *ab, UINT side) {
g_return_if_fail(ab != NULL);
- hwnd = GDK_WINDOW_HWND(ab->win->window);
+ hwnd = appbar_get_handle(ab);
g_return_if_fail(IsWindow(hwnd));
@@ -654,10 +660,10 @@ GtkAppBar *gtk_appbar_add(GtkWidget *win) {
ab->win = win;
/* init docking coords */
- get_window_normal_rc(GDK_WINDOW_HWND(win->window), &(ab->docked_rect));
+ get_window_normal_rc(appbar_get_handle(ab), &(ab->docked_rect));
/* Add main window filter */
- gdk_window_add_filter(win->window,
+ gdk_window_add_filter(gtk_widget_get_window(win),
gtk_appbar_event_filter,
ab);
return ab;
@@ -670,8 +676,8 @@ void gtk_appbar_remove(GtkAppBar *ab) {
if(!ab)
return;
- hwnd = GDK_WINDOW_HWND(ab->win->window);
- gdk_window_remove_filter(ab->win->window,
+ hwnd = appbar_get_handle(ab);
+ gdk_window_remove_filter(gtk_widget_get_window(ab->win),
gtk_appbar_event_filter,
ab);
if(ab->docked) {
diff --git a/pidgin/plugins/win32/winprefs/winprefs.c b/pidgin/plugins/win32/winprefs/winprefs.c
index 62564de38e..0e0df55fc5 100644
--- a/pidgin/plugins/win32/winprefs/winprefs.c
+++ b/pidgin/plugins/win32/winprefs/winprefs.c
@@ -20,12 +20,9 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
*/
-#include <gtk/gtk.h>
-#include <gdk/gdkwin32.h>
-
#include "internal.h"
-#include "gtkwin32dep.h"
+#include "pidgin.h"
#include "core.h"
#include "debug.h"
@@ -91,7 +88,7 @@ static void blist_set_ontop(gboolean val) {
if(!blist)
return;
- gtk_window_set_keep_above(GTK_WINDOW(PIDGIN_BLIST(purple_get_blist())->window), val);
+ gtk_window_set_keep_above(GTK_WINDOW(PIDGIN_BLIST(purple_blist_get_buddy_list())->window), val);
}
static void blist_dock_cb(gboolean val) {
@@ -168,7 +165,7 @@ static void blist_create_cb(PurpleBuddyList *purple_blist, void *data) {
blist_set_dockable(TRUE);
if(purple_prefs_get_bool(PREF_DBLIST_DOCKED)) {
blist_ab->undocked_height = purple_prefs_get_int(PREF_DBLIST_HEIGHT);
- if(!(gdk_window_get_state(blist->window)
+ if(!(gdk_window_get_state(gtk_widget_get_window(blist))
& GDK_WINDOW_STATE_WITHDRAWN)) {
gtk_appbar_dock(blist_ab,
purple_prefs_get_int(PREF_DBLIST_SIDE));
@@ -193,7 +190,7 @@ winprefs_set_autostart(GtkWidget *w) {
char *runval = NULL;
if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)))
- runval = g_strdup_printf("\"%s" G_DIR_SEPARATOR_S "pidgin.exe\"", wpurple_install_dir());
+ runval = g_strdup_printf("\"%s" G_DIR_SEPARATOR_S "pidgin.exe\"", wpurple_bin_dir());
if(!wpurple_write_reg_string(HKEY_CURRENT_USER, RUNKEY, "Pidgin", runval)
/* For Win98 */
@@ -236,9 +233,9 @@ static gboolean plugin_load(PurplePlugin *plugin) {
handle = plugin;
/* blist docking init */
- if(purple_get_blist() && PIDGIN_BLIST(purple_get_blist())
- && PIDGIN_BLIST(purple_get_blist())->window) {
- blist_create_cb(purple_get_blist(), NULL);
+ 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);
}
/* This really shouldn't happen anymore generally, but if for some strange
@@ -320,7 +317,6 @@ static GtkWidget* get_config_frame(PurplePlugin *plugin) {
static PidginPluginUiInfo ui_info =
{
get_config_frame,
- 0,
/* padding */
NULL,
diff --git a/pidgin/plugins/xmppconsole.c b/pidgin/plugins/xmppconsole.c
index 6fcd66ac91..250c0f2b52 100644
--- a/pidgin/plugins/xmppconsole.c
+++ b/pidgin/plugins/xmppconsole.c
@@ -25,15 +25,19 @@
#include "prpl.h"
#include "xmlnode.h"
-#include "gtkimhtml.h"
+#include "gtkwebview.h"
#include "gtkutils.h"
+#include <gdk/gdkkeysyms.h>
+
+#include "gtk3compat.h"
+
typedef struct {
PurpleConnection *gc;
GtkWidget *window;
GtkWidget *hbox;
GtkWidget *dropdown;
- GtkWidget *imhtml;
+ GtkWidget *webview;
GtkWidget *entry;
GtkWidget *sw;
int count;
@@ -43,31 +47,56 @@ typedef struct {
XmppConsole *console = NULL;
static void *xmpp_console_handle = NULL;
-#define BRACKET_COLOR "#940f8c"
-#define TAG_COLOR "#8b1dab"
-#define ATTR_NAME_COLOR "#a02961"
-#define ATTR_VALUE_COLOR "#324aa4"
-#define XMLNS_COLOR "#2cb12f"
+static const gchar *xmpp_prpls[] = {
+ "prpl-jabber", "prpl-gtalk", "prpl-facebook-xmpp", 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 (g_strcmp0(xmpp_prpls[i], prpl_name) == 0)
+ return TRUE;
+ i++;
+ }
+
+ return FALSE;
+}
static char *
-xmlnode_to_pretty_str(xmlnode *node, int *len, int depth)
+purple_xmlnode_to_pretty_str(PurpleXmlNode *node, int *len)
{
GString *text = g_string_new("");
- xmlnode *c;
- char *node_name, *esc, *esc2, *tab = NULL;
+ PurpleXmlNode *c;
+ char *node_name, *esc, *esc2;
gboolean need_end = FALSE, pretty = TRUE;
g_return_val_if_fail(node != NULL, NULL);
- if (pretty && depth) {
- tab = g_strnfill(depth, '\t');
- text = g_string_append(text, tab);
- }
-
node_name = g_markup_escape_text(node->name, -1);
g_string_append_printf(text,
- "<font color='" BRACKET_COLOR "'>&lt;</font>"
- "<font color='" TAG_COLOR "'><b>%s</b></font>",
+ "<span class=bracket>&lt;</span>"
+ "<span class=tag>%s</span>",
node_name);
if (node->xmlns) {
@@ -78,25 +107,25 @@ xmlnode_to_pretty_str(xmlnode *node, int *len, int depth)
{
char *xmlns = g_markup_escape_text(node->xmlns, -1);
g_string_append_printf(text,
- " <font color='" ATTR_NAME_COLOR "'><b>xmlns</b></font>="
- "'<font color='" XMLNS_COLOR "'><b>%s</b></font>'",
+ " <span class=attr>xmlns</span>="
+ "'<span class=xmlns>%s</span>'",
xmlns);
g_free(xmlns);
}
}
for (c = node->child; c; c = c->next)
{
- if (c->type == XMLNODE_TYPE_ATTRIB) {
+ if (c->type == PURPLE_XMLNODE_TYPE_ATTRIB) {
esc = g_markup_escape_text(c->name, -1);
esc2 = g_markup_escape_text(c->data, -1);
g_string_append_printf(text,
- " <font color='" ATTR_NAME_COLOR "'><b>%s</b></font>="
- "'<font color='" ATTR_VALUE_COLOR "'>%s</font>'",
+ " <span class=attr>%s</span>="
+ "'<span class=value>%s</span>'",
esc, esc2);
g_free(esc);
g_free(esc2);
- } else if (c->type == XMLNODE_TYPE_TAG || c->type == XMLNODE_TYPE_DATA) {
- if (c->type == XMLNODE_TYPE_DATA)
+ } 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;
}
@@ -104,119 +133,126 @@ xmlnode_to_pretty_str(xmlnode *node, int *len, int depth)
if (need_end) {
g_string_append_printf(text,
- "<font color='"BRACKET_COLOR"'>&gt;</font>%s",
+ "<span class=bracket>&gt;</span>%s",
pretty ? "<br>" : "");
+ need_end = FALSE;
for (c = node->child; c; c = c->next)
{
- if (c->type == XMLNODE_TYPE_TAG) {
+ if (c->type == PURPLE_XMLNODE_TYPE_TAG) {
int esc_len;
- esc = xmlnode_to_pretty_str(c, &esc_len, depth+1);
+ esc = purple_xmlnode_to_pretty_str(c, &esc_len);
+ if (!need_end) {
+ g_string_append(text, "<div class=tab>");
+ need_end = TRUE;
+ }
text = g_string_append_len(text, esc, esc_len);
g_free(esc);
- } else if (c->type == XMLNODE_TYPE_DATA && c->data_sz > 0) {
+ } else if (c->type == PURPLE_XMLNODE_TYPE_DATA && c->data_sz > 0) {
esc = g_markup_escape_text(c->data, c->data_sz);
text = g_string_append(text, esc);
g_free(esc);
}
}
- if(tab && pretty)
- text = g_string_append(text, tab);
+ if (need_end)
+ g_string_append(text, "</div>");
+
g_string_append_printf(text,
- "<font color='" BRACKET_COLOR "'>&lt;</font>/"
- "<font color='" TAG_COLOR "'><b>%s</b></font>"
- "<font color='" BRACKET_COLOR "'>&gt;</font><br>",
+ "<span class=bracket>&lt;</span>/"
+ "<span class=tag>%s</span>"
+ "<span class=bracket>&gt;</span><br>",
node_name);
} else {
g_string_append_printf(text,
- "/<font color='" BRACKET_COLOR "'>&gt;</font><br>");
+ "/<span class=bracket>&gt;</span><br>");
}
g_free(node_name);
- g_free(tab);
-
- if(len)
+ if (len)
*len = text->len;
return g_string_free(text, FALSE);
}
static void
-xmlnode_received_cb(PurpleConnection *gc, xmlnode **packet, gpointer null)
+purple_xmlnode_received_cb(PurpleConnection *gc, PurpleXmlNode **packet, gpointer null)
{
char *str, *formatted;
if (!console || console->gc != gc)
return;
- str = xmlnode_to_pretty_str(*packet, NULL, 0);
- formatted = g_strdup_printf("<body bgcolor='#ffcece'><pre>%s</pre></body>", str);
- gtk_imhtml_append_text(GTK_IMHTML(console->imhtml), formatted, 0);
+ str = purple_xmlnode_to_pretty_str(*packet, NULL);
+ formatted = g_strdup_printf("<div class=incoming>%s</div>", str);
+ pidgin_webview_append_html(PIDGIN_WEBVIEW(console->webview), formatted);
g_free(formatted);
g_free(str);
}
static void
-xmlnode_sent_cb(PurpleConnection *gc, char **packet, gpointer null)
+purple_xmlnode_sent_cb(PurpleConnection *gc, char **packet, gpointer null)
{
char *str;
char *formatted;
- xmlnode *node;
+ PurpleXmlNode *node;
if (!console || console->gc != gc)
return;
- node = xmlnode_from_str(*packet, -1);
+ node = purple_xmlnode_from_str(*packet, -1);
if (!node)
return;
- str = xmlnode_to_pretty_str(node, NULL, 0);
- formatted = g_strdup_printf("<body bgcolor='#dcecc4'><pre>%s</pre></body>", str);
- gtk_imhtml_append_text(GTK_IMHTML(console->imhtml), formatted, 0);
+ str = purple_xmlnode_to_pretty_str(node, NULL);
+ formatted = g_strdup_printf("<div class=outgoing>%s</div>", str);
+ pidgin_webview_append_html(PIDGIN_WEBVIEW(console->webview), formatted);
g_free(formatted);
g_free(str);
- xmlnode_free(node);
+ purple_xmlnode_free(node);
}
-static void message_send_cb(GtkWidget *widget, gpointer p)
+static gboolean
+message_send_cb(GtkWidget *widget, GdkEventKey *event, gpointer p)
{
- GtkTextIter start, end;
PurplePluginProtocolInfo *prpl_info = NULL;
PurpleConnection *gc;
- GtkTextBuffer *buffer;
- char *text;
+ gchar *text;
+
+ if (event->keyval != GDK_KEY_KP_Enter && event->keyval != GDK_KEY_Return)
+ return FALSE;
gc = console->gc;
if (gc)
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
-
- buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(console->entry));
- gtk_text_buffer_get_start_iter(buffer, &start);
- gtk_text_buffer_get_end_iter(buffer, &end);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
- text = gtk_imhtml_get_text(GTK_IMHTML(console->entry), &start, &end);
+ text = pidgin_webview_get_body_text(PIDGIN_WEBVIEW(widget));
if (prpl_info && prpl_info->send_raw != NULL)
prpl_info->send_raw(gc, text, strlen(text));
g_free(text);
- gtk_imhtml_clear(GTK_IMHTML(console->entry));
+ pidgin_webview_load_html_string(PIDGIN_WEBVIEW(console->entry), "");
+
+ return TRUE;
}
-static void entry_changed_cb(GtkTextBuffer *buffer, void *data)
+static void
+entry_changed_cb(GtkWidget *webview, void *data)
{
char *xmlstr, *str;
- GtkTextIter iter;
+#if 0
int wrapped_lines;
int lines;
GdkRectangle oneline;
int height;
int pad_top, pad_inside, pad_bottom;
- GtkTextIter start, end;
- xmlnode *node;
+#endif
+ PurpleXmlNode *node;
+#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);
@@ -237,32 +273,29 @@ static void entry_changed_cb(GtkTextBuffer *buffer, void *data)
height += (oneline.height + pad_inside) * (wrapped_lines - lines);
gtk_widget_set_size_request(console->sw, -1, height + 6);
+#endif
- gtk_text_buffer_get_start_iter(buffer, &start);
- gtk_text_buffer_get_end_iter(buffer, &end);
- str = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
+ str = pidgin_webview_get_body_text(PIDGIN_WEBVIEW(webview));
if (!str)
return;
xmlstr = g_strdup_printf("<xml>%s</xml>", str);
- node = xmlnode_from_str(xmlstr, -1);
+ node = purple_xmlnode_from_str(xmlstr, -1);
if (node) {
- gtk_imhtml_clear_formatting(GTK_IMHTML(console->entry));
+ pidgin_webview_clear_formatting(PIDGIN_WEBVIEW(console->entry));
} else {
- gtk_imhtml_toggle_background(GTK_IMHTML(console->entry), "#ffcece");
+ pidgin_webview_toggle_backcolor(PIDGIN_WEBVIEW(console->entry), "#ffcece");
}
g_free(str);
g_free(xmlstr);
if (node)
- xmlnode_free(node);
+ purple_xmlnode_free(node);
}
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);
- GtkTextIter iter;
- GtkTextBuffer *buffer;
- const char *to;
+ char *to;
int result;
char *stanza;
@@ -274,16 +307,14 @@ static void iq_clicked_cb(GtkWidget *w, gpointer nul)
GTK_STOCK_OK,
GTK_RESPONSE_ACCEPT,
NULL);
+#if !GTK_CHECK_VERSION(2,22,0)
gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
+#endif
gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
gtk_container_set_border_width(GTK_CONTAINER(dialog), 12);
-#if GTK_CHECK_VERSION(2,14,0)
vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
-#else
- vbox = GTK_DIALOG(dialog)->vbox;
-#endif
- hbox = gtk_hbox_new(FALSE, 3);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
label = gtk_label_new("To:");
@@ -295,18 +326,18 @@ static void iq_clicked_cb(GtkWidget *w, gpointer nul)
gtk_entry_set_activates_default (GTK_ENTRY (to_entry), TRUE);
gtk_box_pack_start(GTK_BOX(hbox), to_entry, FALSE, FALSE, 0);
- hbox = gtk_hbox_new(FALSE, 3);
+ 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_misc_set_alignment(GTK_MISC(label), 0, 0.5);
gtk_size_group_add_widget(sg, label);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
- type_combo = gtk_combo_box_new_text();
- gtk_combo_box_append_text(GTK_COMBO_BOX(type_combo), "get");
- gtk_combo_box_append_text(GTK_COMBO_BOX(type_combo), "set");
- gtk_combo_box_append_text(GTK_COMBO_BOX(type_combo), "result");
- gtk_combo_box_append_text(GTK_COMBO_BOX(type_combo), "error");
+ 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);
@@ -318,19 +349,20 @@ static void iq_clicked_cb(GtkWidget *w, gpointer nul)
return;
}
- to = gtk_entry_get_text(GTK_ENTRY(to_entry));
+ to = g_markup_escape_text(gtk_entry_get_text(GTK_ENTRY(to_entry)), -1);
- stanza = g_strdup_printf("<iq %s%s%s id='console%x' type='%s'></iq>",
+ stanza = g_strdup_printf("&lt;iq %s%s%s id='console%x' type='%s'&gt;"
+ "<a id=caret></a>"
+ "&lt;/iq&gt;",
to && *to ? "to='" : "",
to && *to ? to : "",
to && *to ? "'" : "",
g_random_int(),
- gtk_combo_box_get_active_text(GTK_COMBO_BOX(type_combo)));
+ gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(type_combo)));
- buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(console->entry));
- gtk_text_buffer_set_text(buffer, stanza, -1);
- gtk_text_buffer_get_iter_at_offset(buffer, &iter, strstr(stanza, "</iq>") - stanza);
- gtk_text_buffer_place_cursor(buffer, &iter);
+ pidgin_webview_load_html_string_with_selection(PIDGIN_WEBVIEW(console->entry), stanza);
+ gtk_widget_grab_focus(console->entry);
+ g_free(to);
g_free(stanza);
gtk_widget_destroy(dialog);
@@ -348,9 +380,8 @@ static void presence_clicked_cb(GtkWidget *w, gpointer nul)
GtkWidget *show_combo;
GtkWidget *type_combo;
GtkSizeGroup *sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
- GtkTextIter iter;
- GtkTextBuffer *buffer;
- const char *to, *type, *status, *show, *priority;
+ char *to, *status, *priority;
+ const char *type, *show;
int result;
char *stanza;
@@ -362,16 +393,14 @@ static void presence_clicked_cb(GtkWidget *w, gpointer nul)
GTK_STOCK_OK,
GTK_RESPONSE_ACCEPT,
NULL);
+#if !GTK_CHECK_VERSION(2,22,0)
gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
+#endif
gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
gtk_container_set_border_width(GTK_CONTAINER(dialog), 12);
-#if GTK_CHECK_VERSION(2,14,0)
vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
-#else
- vbox = GTK_DIALOG(dialog)->vbox;
-#endif
- hbox = gtk_hbox_new(FALSE, 3);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
label = gtk_label_new("To:");
@@ -383,41 +412,41 @@ static void presence_clicked_cb(GtkWidget *w, gpointer nul)
gtk_entry_set_activates_default (GTK_ENTRY (to_entry), TRUE);
gtk_box_pack_start(GTK_BOX(hbox), to_entry, FALSE, FALSE, 0);
- hbox = gtk_hbox_new(FALSE, 3);
+ 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_misc_set_alignment(GTK_MISC(label), 0, 0.5);
gtk_size_group_add_widget(sg, label);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
- type_combo = gtk_combo_box_new_text();
- gtk_combo_box_append_text(GTK_COMBO_BOX(type_combo), "default");
- gtk_combo_box_append_text(GTK_COMBO_BOX(type_combo), "unavailable");
- gtk_combo_box_append_text(GTK_COMBO_BOX(type_combo), "subscribe");
- gtk_combo_box_append_text(GTK_COMBO_BOX(type_combo), "unsubscribe");
- gtk_combo_box_append_text(GTK_COMBO_BOX(type_combo), "subscribed");
- gtk_combo_box_append_text(GTK_COMBO_BOX(type_combo), "unsubscribed");
- gtk_combo_box_append_text(GTK_COMBO_BOX(type_combo), "probe");
- gtk_combo_box_append_text(GTK_COMBO_BOX(type_combo), "error");
+ 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_hbox_new(FALSE, 3);
+ 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_misc_set_alignment(GTK_MISC(label), 0, 0.5);
gtk_size_group_add_widget(sg, label);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
- show_combo = gtk_combo_box_new_text();
- gtk_combo_box_append_text(GTK_COMBO_BOX(show_combo), "default");
- gtk_combo_box_append_text(GTK_COMBO_BOX(show_combo), "away");
- gtk_combo_box_append_text(GTK_COMBO_BOX(show_combo), "dnd");
- gtk_combo_box_append_text(GTK_COMBO_BOX(show_combo), "xa");
- gtk_combo_box_append_text(GTK_COMBO_BOX(show_combo), "chat");
+ 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_hbox_new(FALSE, 3);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
label = gtk_label_new("Status:");
@@ -429,7 +458,7 @@ static void presence_clicked_cb(GtkWidget *w, gpointer nul)
gtk_entry_set_activates_default (GTK_ENTRY (status_entry), TRUE);
gtk_box_pack_start(GTK_BOX(hbox), status_entry, FALSE, FALSE, 0);
- hbox = gtk_hbox_new(FALSE, 3);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
label = gtk_label_new("Priority:");
@@ -449,21 +478,22 @@ static void presence_clicked_cb(GtkWidget *w, gpointer nul)
return;
}
- to = gtk_entry_get_text(GTK_ENTRY(to_entry));
- type = gtk_combo_box_get_active_text(GTK_COMBO_BOX(type_combo));
+ to = g_markup_escape_text(gtk_entry_get_text(GTK_ENTRY(to_entry)), -1);
+ type = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(type_combo));
if (!strcmp(type, "default"))
type = "";
- show = gtk_combo_box_get_active_text(GTK_COMBO_BOX(show_combo));
+ show = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(show_combo));
if (!strcmp(show, "default"))
show = "";
- status = gtk_entry_get_text(GTK_ENTRY(status_entry));
- priority = gtk_entry_get_text(GTK_ENTRY(priority_entry));
+ status = g_markup_escape_text(gtk_entry_get_text(GTK_ENTRY(status_entry)), -1);
+ priority = g_markup_escape_text(gtk_entry_get_text(GTK_ENTRY(priority_entry)), -1);
if (!strcmp(priority, "0"))
- priority = "";
+ *priority = '\0';
- stanza = g_strdup_printf("<presence %s%s%s id='console%x' %s%s%s>"
+ stanza = g_strdup_printf("&lt;presence %s%s%s id='console%x' %s%s%s&gt;"
"%s%s%s%s%s%s%s%s%s"
- "</presence>",
+ "<a id=caret></a>"
+ "&lt;/presence&gt;",
*to ? "to='" : "",
*to ? to : "",
*to ? "'" : "",
@@ -473,23 +503,24 @@ static void presence_clicked_cb(GtkWidget *w, gpointer nul)
*type ? type : "",
*type ? "'" : "",
- *show ? "<show>" : "",
+ *show ? "&lt;show&gt;" : "",
*show ? show : "",
- *show ? "</show>" : "",
+ *show ? "&lt;/show&gt;" : "",
- *status ? "<status>" : "",
+ *status ? "&lt;status&gt;" : "",
*status ? status : "",
- *status ? "</status>" : "",
+ *status ? "&lt;/status&gt;" : "",
- *priority ? "<priority>" : "",
+ *priority ? "&lt;priority&gt;" : "",
*priority ? priority : "",
- *priority ? "</priority>" : "");
+ *priority ? "&lt;/priority&gt;" : "");
- buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(console->entry));
- gtk_text_buffer_set_text(buffer, stanza, -1);
- gtk_text_buffer_get_iter_at_offset(buffer, &iter, strstr(stanza, "</presence>") - stanza);
- gtk_text_buffer_place_cursor(buffer, &iter);
+ pidgin_webview_load_html_string_with_selection(PIDGIN_WEBVIEW(console->entry), stanza);
+ gtk_widget_grab_focus(console->entry);
g_free(stanza);
+ g_free(to);
+ g_free(status);
+ g_free(priority);
gtk_widget_destroy(dialog);
g_object_unref(sg);
@@ -506,9 +537,7 @@ static void message_clicked_cb(GtkWidget *w, gpointer nul)
GtkWidget *label;
GtkWidget *type_combo;
GtkSizeGroup *sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
- GtkTextIter iter;
- GtkTextBuffer *buffer;
- const char *to, *body, *thread, *subject;
+ char *to, *body, *thread, *subject;
char *stanza;
int result;
@@ -520,16 +549,14 @@ static void message_clicked_cb(GtkWidget *w, gpointer nul)
GTK_STOCK_OK,
GTK_RESPONSE_ACCEPT,
NULL);
+#if !GTK_CHECK_VERSION(2,22,0)
gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
+#endif
gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
gtk_container_set_border_width(GTK_CONTAINER(dialog), 12);
-#if GTK_CHECK_VERSION(2,14,0)
vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
-#else
- vbox = GTK_DIALOG(dialog)->vbox;
-#endif
- hbox = gtk_hbox_new(FALSE, 3);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
label = gtk_label_new("To:");
@@ -541,22 +568,22 @@ static void message_clicked_cb(GtkWidget *w, gpointer nul)
gtk_entry_set_activates_default (GTK_ENTRY (to_entry), TRUE);
gtk_box_pack_start(GTK_BOX(hbox), to_entry, FALSE, FALSE, 0);
- hbox = gtk_hbox_new(FALSE, 3);
+ 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_misc_set_alignment(GTK_MISC(label), 0, 0.5);
gtk_size_group_add_widget(sg, label);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
- type_combo = gtk_combo_box_new_text();
- gtk_combo_box_append_text(GTK_COMBO_BOX(type_combo), "chat");
- gtk_combo_box_append_text(GTK_COMBO_BOX(type_combo), "headline");
- gtk_combo_box_append_text(GTK_COMBO_BOX(type_combo), "groupchat");
- gtk_combo_box_append_text(GTK_COMBO_BOX(type_combo), "normal");
- gtk_combo_box_append_text(GTK_COMBO_BOX(type_combo), "error");
+ 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_hbox_new(FALSE, 3);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
label = gtk_label_new("Body:");
@@ -568,7 +595,7 @@ static void message_clicked_cb(GtkWidget *w, gpointer nul)
gtk_entry_set_activates_default (GTK_ENTRY (body_entry), TRUE);
gtk_box_pack_start(GTK_BOX(hbox), body_entry, FALSE, FALSE, 0);
- hbox = gtk_hbox_new(FALSE, 3);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
label = gtk_label_new("Subject:");
@@ -580,7 +607,7 @@ static void message_clicked_cb(GtkWidget *w, gpointer nul)
gtk_entry_set_activates_default (GTK_ENTRY (subject_entry), TRUE);
gtk_box_pack_start(GTK_BOX(hbox), subject_entry, FALSE, FALSE, 0);
- hbox = gtk_hbox_new(FALSE, 3);
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
label = gtk_label_new("Thread:");
@@ -600,38 +627,42 @@ static void message_clicked_cb(GtkWidget *w, gpointer nul)
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));
+ to = g_markup_escape_text(gtk_entry_get_text(GTK_ENTRY(to_entry)), -1);
+ body = g_markup_escape_text(gtk_entry_get_text(GTK_ENTRY(body_entry)), -1);
+ thread = g_markup_escape_text(gtk_entry_get_text(GTK_ENTRY(thread_entry)), -1);
+ subject = g_markup_escape_text(gtk_entry_get_text(GTK_ENTRY(subject_entry)), -1);
- stanza = g_strdup_printf("<message %s%s%s id='console%x' type='%s'>"
+ stanza = g_strdup_printf("&lt;message %s%s%s id='console%x' type='%s'&gt;"
"%s%s%s%s%s%s%s%s%s"
- "</message>",
+ "<a id=caret></a>"
+ "&lt;/message&gt;",
*to ? "to='" : "",
*to ? to : "",
*to ? "'" : "",
g_random_int(),
- gtk_combo_box_get_active_text(GTK_COMBO_BOX(type_combo)),
+ gtk_combo_box_text_get_active_text(
+ GTK_COMBO_BOX_TEXT(type_combo)),
- *body ? "<body>" : "",
+ *body ? "&lt;body&gt;" : "",
*body ? body : "",
- *body ? "</body>" : "",
+ *body ? "&lt;/body&gt;" : "",
- *subject ? "<subject>" : "",
+ *subject ? "&lt;subject&gt;" : "",
*subject ? subject : "",
- *subject ? "</subject>" : "",
+ *subject ? "&lt;/subject&gt;" : "",
- *thread ? "<thread>" : "",
+ *thread ? "&lt;thread&gt;" : "",
*thread ? thread : "",
- *thread ? "</thread>" : "");
+ *thread ? "&lt;/thread&gt;" : "");
- buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(console->entry));
- gtk_text_buffer_set_text(buffer, stanza, -1);
- gtk_text_buffer_get_iter_at_offset(buffer, &iter, strstr(stanza, "</message>") - stanza);
- gtk_text_buffer_place_cursor(buffer, &iter);
+ pidgin_webview_load_html_string_with_selection(PIDGIN_WEBVIEW(console->entry), stanza);
+ gtk_widget_grab_focus(console->entry);
g_free(stanza);
+ g_free(to);
+ g_free(body);
+ g_free(thread);
+ g_free(subject);
gtk_widget_destroy(dialog);
g_object_unref(sg);
@@ -640,16 +671,25 @@ static void message_clicked_cb(GtkWidget *w, gpointer nul)
static void
signing_on_cb(PurpleConnection *gc)
{
+ PurpleAccount *account;
+
if (!console)
return;
- gtk_combo_box_append_text(GTK_COMBO_BOX(console->dropdown), purple_account_get_username(gc->account));
+ 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)
+ if (console->count == 1) {
console->gc = gc;
- else
+ pidgin_webview_load_html_string(PIDGIN_WEBVIEW(console->webview), EMPTY_HTML);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(console->dropdown), 0);
+ } else
gtk_widget_show_all(console->hbox);
}
@@ -674,35 +714,53 @@ signed_off_cb(PurpleConnection *gc)
if (l == NULL)
return;
- gtk_combo_box_remove_text(GTK_COMBO_BOX(console->dropdown), i);
+ 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) {
+ char *tmp = g_strdup_printf("<div class=info>%s</div>",
+ _("Logged out."));
+ pidgin_webview_append_html(PIDGIN_WEBVIEW(console->webview), tmp);
+ g_free(tmp);
console->gc = NULL;
- gtk_imhtml_append_text(GTK_IMHTML(console->imhtml),
- _("<font color='#777777'>Logged out.</font>"), 0);
}
}
static gboolean
plugin_load(PurplePlugin *plugin)
{
- PurplePlugin *jabber;
+ int i;
+ gboolean any_registered = FALSE;
+
+ xmpp_console_handle = plugin;
- jabber = purple_find_prpl("prpl-jabber");
- if (!jabber)
+ i = 0;
+ while (xmpp_prpls[i] != NULL) {
+ PurplePlugin *xmpp;
+
+ xmpp = purple_find_prpl(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)
return FALSE;
- xmpp_console_handle = plugin;
- purple_signal_connect(jabber, "jabber-receiving-xmlnode", xmpp_console_handle,
- PURPLE_CALLBACK(xmlnode_received_cb), NULL);
- purple_signal_connect(jabber, "jabber-sending-text", xmpp_console_handle,
- PURPLE_CALLBACK(xmlnode_sent_cb), NULL);
purple_signal_connect(purple_connections_get_handle(), "signing-on",
- plugin, PURPLE_CALLBACK(signing_on_cb), NULL);
+ plugin, PURPLE_CALLBACK(signing_on_cb), NULL);
purple_signal_connect(purple_connections_get_handle(), "signed-off",
- plugin, PURPLE_CALLBACK(signed_off_cb), NULL);
+ plugin, PURPLE_CALLBACK(signed_off_cb), NULL);
return TRUE;
}
@@ -716,7 +774,7 @@ plugin_unload(PurplePlugin *plugin)
}
static void
-console_destroy(GtkObject *window, gpointer nul)
+console_destroy(GtkWidget *window, gpointer nul)
{
g_list_free(console->accounts);
g_free(console);
@@ -726,26 +784,18 @@ console_destroy(GtkObject *window, gpointer nul)
static void
dropdown_changed_cb(GtkComboBox *widget, gpointer nul)
{
- PurpleAccount *account;
-
if (!console)
return;
- account = purple_accounts_find(gtk_combo_box_get_active_text(GTK_COMBO_BOX(console->dropdown)),
- "prpl-jabber");
- if (!account || !account->gc)
- return;
-
- console->gc = account->gc;
- gtk_imhtml_clear(GTK_IMHTML(console->imhtml));
+ console->gc = g_list_nth_data(console->accounts, gtk_combo_box_get_active(GTK_COMBO_BOX(console->dropdown)));
+ pidgin_webview_load_html_string(PIDGIN_WEBVIEW(console->webview), EMPTY_HTML);
}
static void
create_console(PurplePluginAction *action)
{
- GtkWidget *vbox = gtk_vbox_new(FALSE, 6);
+ GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
GtkWidget *label;
- GtkTextBuffer *buffer;
GtkWidget *toolbar;
GList *connections;
GtkToolItem *button;
@@ -762,61 +812,66 @@ create_console(PurplePluginAction *action)
gtk_window_set_default_size(GTK_WINDOW(console->window), 580, 400);
gtk_container_add(GTK_CONTAINER(console->window), vbox);
- console->hbox = gtk_hbox_new(FALSE, 3);
+ 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_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
gtk_box_pack_start(GTK_BOX(console->hbox), label, FALSE, FALSE, 0);
- console->dropdown = gtk_combo_box_new_text();
+ console->dropdown = gtk_combo_box_text_new();
for (connections = purple_connections_get_all(); connections; connections = connections->next) {
PurpleConnection *gc = connections->data;
- if (!strcmp(purple_account_get_protocol_id(purple_connection_get_account(gc)), "prpl-jabber")) {
+ if (xmppconsole_is_xmpp_account(purple_connection_get_account(gc))) {
console->count++;
console->accounts = g_list_append(console->accounts, gc);
- gtk_combo_box_append_text(GTK_COMBO_BOX(console->dropdown),
+ 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_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->imhtml = gtk_imhtml_new(NULL, NULL);
- if (console->count == 0)
- gtk_imhtml_append_text(GTK_IMHTML(console->imhtml),
- _("<font color='#777777'>Not connected to XMPP</font>"), 0);
+ console->webview = pidgin_webview_new(FALSE);
+ pidgin_webview_load_html_string(PIDGIN_WEBVIEW(console->webview), EMPTY_HTML);
+ if (console->count == 0) {
+ char *tmp = g_strdup_printf("<div class=info>%s</div>",
+ _("Not connected to XMPP"));
+ pidgin_webview_append_html(PIDGIN_WEBVIEW(console->webview), tmp);
+ g_free(tmp);
+ }
gtk_box_pack_start(GTK_BOX(vbox),
- pidgin_make_scrollable(console->imhtml, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_ETCHED_IN, -1, -1),
+ pidgin_make_scrollable(console->webview, 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_imhtml_new(NULL, NULL);
- gtk_imhtml_set_whole_buffer_formatting_only(GTK_IMHTML(console->entry), TRUE);
- g_signal_connect(G_OBJECT(console->entry),"message_send", G_CALLBACK(message_send_cb), console);
+ console->entry = pidgin_webview_new(TRUE);
+ pidgin_webview_set_whole_buffer_formatting_only(PIDGIN_WEBVIEW(console->entry), TRUE);
+ 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);
- gtk_imhtml_set_editable(GTK_IMHTML(console->entry), TRUE);
- buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(console->entry));
- g_signal_connect(G_OBJECT(buffer), "changed", G_CALLBACK(entry_changed_cb), NULL);
+ g_signal_connect(G_OBJECT(console->entry), "changed", G_CALLBACK(entry_changed_cb), NULL);
- entry_changed_cb(buffer, NULL);
+ entry_changed_cb(console->entry, NULL);
gtk_widget_show_all(console->window);
if (console->count < 2)
diff --git a/pidgin/themes/Contents/Info.plist b/pidgin/themes/Contents/Info.plist
new file mode 100644
index 0000000000..f2891d3d11
--- /dev/null
+++ b/pidgin/themes/Contents/Info.plist
@@ -0,0 +1,16 @@
+<?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>
diff --git a/pidgin/themes/Contents/Resources/Content.html b/pidgin/themes/Contents/Resources/Content.html
new file mode 100644
index 0000000000..8b629fe7eb
--- /dev/null
+++ b/pidgin/themes/Contents/Resources/Content.html
@@ -0,0 +1,6 @@
+<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>
diff --git a/pidgin/themes/Contents/Resources/Incoming/Content.html b/pidgin/themes/Contents/Resources/Incoming/Content.html
new file mode 100644
index 0000000000..39a5bc4276
--- /dev/null
+++ b/pidgin/themes/Contents/Resources/Incoming/Content.html
@@ -0,0 +1,8 @@
+<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>
diff --git a/pidgin/themes/Contents/Resources/Status.html b/pidgin/themes/Contents/Resources/Status.html
new file mode 100644
index 0000000000..81e935bdd0
--- /dev/null
+++ b/pidgin/themes/Contents/Resources/Status.html
@@ -0,0 +1,7 @@
+<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>
diff --git a/pidgin/themes/Contents/Resources/Variants/Default.css b/pidgin/themes/Contents/Resources/Variants/Default.css
new file mode 100644
index 0000000000..d1c6352610
--- /dev/null
+++ b/pidgin/themes/Contents/Resources/Variants/Default.css
@@ -0,0 +1,2 @@
+@import ../main.css;
+
diff --git a/pidgin/themes/Contents/Resources/Variants/No-Timestamps.css b/pidgin/themes/Contents/Resources/Variants/No-Timestamps.css
new file mode 100644
index 0000000000..155fe8aa15
--- /dev/null
+++ b/pidgin/themes/Contents/Resources/Variants/No-Timestamps.css
@@ -0,0 +1,8 @@
+@import ../main.css;
+
+.x-container .x-time,
+.x-status_container .x-time
+{
+ display: none;
+}
+
diff --git a/pidgin/themes/Contents/Resources/main.css b/pidgin/themes/Contents/Resources/main.css
new file mode 100644
index 0000000000..6d4fbd0e82
--- /dev/null
+++ b/pidgin/themes/Contents/Resources/main.css
@@ -0,0 +1,95 @@
+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;
+}
diff --git a/pidgin/themes/Makefile.am b/pidgin/themes/Makefile.am
new file mode 100644
index 0000000000..d37485d68f
--- /dev/null
+++ b/pidgin/themes/Makefile.am
@@ -0,0 +1,22 @@
+
+themetemplatedir = $(datadir)/pidgin/theme
+themetemplate_DATA = Template.html
+themecontentsdir = $(themetemplatedir)/Contents
+themecontents_DATA = Contents/Info.plist
+themeresourcesdir = $(themecontentsdir)/Resources
+themeresources_DATA = Contents/Resources/Content.html \
+ Contents/Resources/Status.html \
+ Contents/Resources/main.css
+themeresourcesincomingdir = $(themeresourcesdir)/Incoming
+themeresourcesincoming_DATA = Contents/Resources/Incoming/Content.html
+themevariantsdir = $(themeresourcesdir)/Variants
+themevariants_DATA = Contents/Resources/Variants/Default.css \
+ Contents/Resources/Variants/No-Timestamps.css
+
+EXTRA_DIST = \
+ $(themetemplate_DATA) \
+ $(themecontents_DATA) \
+ $(themeresources_DATA) \
+ $(themeresourcesincoming_DATA) \
+ $(themevariants_DATA)
+
diff --git a/pidgin/themes/Template.html b/pidgin/themes/Template.html
new file mode 100644
index 0000000000..1c436fc4fa
--- /dev/null
+++ b/pidgin/themes/Template.html
@@ -0,0 +1,376 @@
+<!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>
diff --git a/pidgin/win32/MinimizeToTray.c b/pidgin/win32/MinimizeToTray.c
index 45dc2c823d..b4fd22c448 100644
--- a/pidgin/win32/MinimizeToTray.c
+++ b/pidgin/win32/MinimizeToTray.c
@@ -14,7 +14,7 @@
*
* Copyright 2000 Matthew Ellis <m.t.ellis@bigfoot.com>
*/
-#define _WIN32_WINNT 0x0500
+#include <config.h>
#include <windows.h>
#include "MinimizeToTray.h"
diff --git a/pidgin/win32/gtkdocklet-win32.c b/pidgin/win32/gtkdocklet-win32.c
index b02666078e..63916a427f 100644
--- a/pidgin/win32/gtkdocklet-win32.c
+++ b/pidgin/win32/gtkdocklet-win32.c
@@ -21,7 +21,9 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02111-1301, USA.
*/
-#define _WIN32_IE 0x0500
+
+#include <config.h>
+
#include <windows.h>
#include <gdk/gdkwin32.h>
#include <gdk/gdk.h>
@@ -46,7 +48,7 @@
*/
static HWND systray_hwnd = NULL;
/* additional two cached_icons entries for pending and connecting icons */
-static HICON cached_icons[PURPLE_STATUS_NUM_PRIMITIVES + 2];
+static HICON cached_icons[PURPLE_STATUS_NUM_PRIMITIVES + 3];
static GtkWidget *image = NULL;
/* This is used to trigger click events on so they appear to GTK+ as if they are triggered by input */
static GtkWidget *dummy_button = NULL;
@@ -497,14 +499,16 @@ static void systray_remove_nid(void) {
}
static void winpidgin_tray_update_icon(PurpleStatusPrimitive status,
- gboolean connecting, gboolean pending) {
+ PidginDockletFlag flags) {
int icon_index;
g_return_if_fail(image != NULL);
- if(connecting)
+ if(flags & PIDGIN_DOCKLET_CONNECTING)
icon_index = PURPLE_STATUS_NUM_PRIMITIVES;
- else if(pending)
+ else if(flags & PIDGIN_DOCKLET_EMAIL_PENDING)
+ icon_index = PURPLE_STATUS_NUM_PRIMITIVES+2;
+ else if(flags & PIDGIN_DOCKLET_CONV_PENDING)
icon_index = PURPLE_STATUS_NUM_PRIMITIVES+1;
else
icon_index = status;
@@ -535,9 +539,11 @@ static void winpidgin_tray_update_icon(PurpleStatusPrimitive status,
break;
}
- if (pending)
+ if (flags & PIDGIN_DOCKLET_EMAIL_PENDING)
+ icon_name = PIDGDIN_STOCK_TRAY_EMAIL;
+ else if (flags & PIDGIN_DOCKLET_CONV_PENDING)
icon_name = PIDGIN_STOCK_TRAY_PENDING;
- if (connecting)
+ else if (flags & PIDGIN_DOCKLET_CONNECTING)
icon_name = PIDGIN_STOCK_TRAY_CONNECT;
g_return_if_fail(icon_name != NULL);
diff --git a/pidgin/win32/gtkwin32dep.c b/pidgin/win32/gtkwin32dep.c
index be3008987c..90dc20d756 100644
--- a/pidgin/win32/gtkwin32dep.c
+++ b/pidgin/win32/gtkwin32dep.c
@@ -21,23 +21,19 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
*/
-#define _WIN32_IE 0x500
-#ifndef WINVER
-#define WINVER 0x0500 /* W2K */
-#endif
-#include <windows.h>
+#include "internal.h"
+
#include <io.h>
#include <stdlib.h>
#include <stdio.h>
#include <winuser.h>
+#include <shellapi.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include <gdk/gdkwin32.h>
-#include "internal.h"
-
#include "debug.h"
#include "notify.h"
#include "network.h"
@@ -47,13 +43,9 @@
#include "untar.h"
#include "gtkwin32dep.h"
-#include "win32dep.h"
#include "gtkconv.h"
#include "gtkconn.h"
#include "util.h"
-#ifdef USE_GTKSPELL
-#include "wspell.h"
-#endif
/*
* GLOBALS
@@ -74,6 +66,11 @@ HINSTANCE winpidgin_exe_hinstance(void) {
return exe_hInstance;
}
+void winpidgin_set_exe_hinstance(HINSTANCE hint)
+{
+ exe_hInstance = hint;
+}
+
HINSTANCE winpidgin_dll_hinstance(void) {
return dll_hInstance;
}
@@ -97,7 +94,7 @@ int winpidgin_gz_decompress(const char* in, const char* out) {
}
while((ret = gzread(fin, buf, 1024))) {
- if(fwrite(buf, 1, ret, fout) < ret) {
+ if ((int)fwrite(buf, 1, ret, fout) < ret) {
purple_debug_error("wpurple_gz_decompress", "Error writing %d bytes to file\n", ret);
gzclose(fin);
fclose(fout);
@@ -318,12 +315,12 @@ winpidgin_window_flash(GtkWindow *window, gboolean flash) {
g_return_if_fail(window != NULL);
- gdkwin = GTK_WIDGET(window)->window;
+ gdkwin = gtk_widget_get_window(GTK_WIDGET(window));
g_return_if_fail(GDK_IS_WINDOW(gdkwin));
- g_return_if_fail(GDK_WINDOW_TYPE(gdkwin) != GDK_WINDOW_CHILD);
+ g_return_if_fail(gdk_window_get_window_type(gdkwin) != GDK_WINDOW_CHILD);
- if(GDK_WINDOW_DESTROYED(gdkwin))
+ if (gdk_window_is_destroyed(gdkwin))
return;
memset(&info, 0, sizeof(FLASHWINFO));
@@ -343,14 +340,10 @@ winpidgin_window_flash(GtkWindow *window, gboolean flash) {
}
void
-winpidgin_conv_blink(PurpleConversation *conv, PurpleMessageFlags flags) {
- PidginWindow *win;
+winpidgin_conv_blink(PurpleConversation *conv) {
+ PidginConvWindow *win;
GtkWindow *window;
- /* Don't flash for our own messages or system messages */
- if(flags & PURPLE_MESSAGE_SEND || flags & PURPLE_MESSAGE_SYSTEM)
- return;
-
if(conv == NULL) {
purple_debug_info("winpidgin", "No conversation found to blink.\n");
return;
@@ -364,8 +357,11 @@ winpidgin_conv_blink(PurpleConversation *conv, PurpleMessageFlags flags) {
window = GTK_WINDOW(win->window);
/* Don't flash if the window is in the foreground */
- if (GetForegroundWindow() == GDK_WINDOW_HWND(GTK_WIDGET(window)->window))
+ if (GetForegroundWindow() ==
+ GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(window))))
+ {
return;
+ }
winpidgin_window_flash(window, TRUE);
/* Stop flashing when window receives focus */
@@ -377,24 +373,25 @@ winpidgin_conv_blink(PurpleConversation *conv, PurpleMessageFlags flags) {
}
static gboolean
-winpidgin_conv_im_blink(PurpleAccount *account, const char *who, char **message,
- PurpleConversation *conv, PurpleMessageFlags flags, void *data)
+winpidgin_conv_im_blink(PurpleConversation *conv, PurpleMessage *pmsg)
{
+ /* Don't flash for our own messages or system messages */
+ if (purple_message_get_flags(pmsg) & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_SYSTEM))
+ return FALSE;
if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/win32/blink_im"))
- winpidgin_conv_blink(conv, flags);
+ winpidgin_conv_blink(conv);
return FALSE;
}
-void winpidgin_init(HINSTANCE hint) {
+void winpidgin_init(void) {
typedef void (__cdecl* LPFNSETLOGFILE)(const LPCSTR);
LPFNSETLOGFILE MySetLogFile;
gchar *exchndl_dll_path;
- purple_debug_info("winpidgin", "winpidgin_init start\n");
-
- exe_hInstance = hint;
+ if (purple_debug_is_verbose())
+ purple_debug_misc("winpidgin", "winpidgin_init start\n");
- exchndl_dll_path = g_build_filename(wpurple_install_dir(), "exchndl.dll", NULL);
+ exchndl_dll_path = g_build_filename(wpurple_bin_dir(), "exchndl.dll", NULL);
MySetLogFile = (LPFNSETLOGFILE) wpurple_find_and_loadproc(exchndl_dll_path, "SetLogFile");
g_free(exchndl_dll_path);
exchndl_dll_path = NULL;
@@ -412,15 +409,13 @@ void winpidgin_init(HINSTANCE hint) {
g_free(locale_debug_dir);
}
-#ifdef USE_GTKSPELL
- winpidgin_spell_init();
-#endif
- purple_debug_info("winpidgin", "GTK+ :%u.%u.%u\n",
+ purple_debug_info("winpidgin", "GTK+: %u.%u.%u\n",
gtk_major_version, gtk_minor_version, gtk_micro_version);
messagewin_hwnd = winpidgin_message_window_init();
- purple_debug_info("winpidgin", "winpidgin_init end\n");
+ if (purple_debug_is_verbose())
+ purple_debug_misc("winpidgin", "winpidgin_init end\n");
}
void winpidgin_post_init(void) {
@@ -470,7 +465,7 @@ get_WorkingAreaRectForWindow(HWND hwnd, RECT *workingAreaRc) {
void winpidgin_ensure_onscreen(GtkWidget *win) {
RECT winR, wAR, intR;
- HWND hwnd = GDK_WINDOW_HWND(win->window);
+ HWND hwnd = GDK_WINDOW_HWND(gtk_widget_get_window(win));
g_return_if_fail(hwnd != NULL);
GetWindowRect(hwnd, &winR);
diff --git a/pidgin/win32/gtkwin32dep.h b/pidgin/win32/gtkwin32dep.h
index 3584334e0c..3b9076b874 100644
--- a/pidgin/win32/gtkwin32dep.h
+++ b/pidgin/win32/gtkwin32dep.h
@@ -22,12 +22,16 @@
*/
#ifndef _GTKWIN32DEP_H_
#define _GTKWIN32DEP_H_
+
+#include <config.h>
+
#include <windows.h>
#include <gtk/gtk.h>
#include "conversation.h"
HINSTANCE winpidgin_dll_hinstance(void);
HINSTANCE winpidgin_exe_hinstance(void);
+void winpidgin_set_exe_hinstance(HINSTANCE hint);
/* Utility */
int winpidgin_gz_decompress(const char* in, const char* out);
@@ -37,12 +41,12 @@ int winpidgin_gz_untar(const char* filename, const char* destdir);
void winpidgin_notify_uri(const char *uri);
void winpidgin_shell_execute(const char *target, const char *verb, const char *clazz);
void winpidgin_ensure_onscreen(GtkWidget *win);
-void winpidgin_conv_blink(PurpleConversation *conv, PurpleMessageFlags flags);
+void winpidgin_conv_blink(PurpleConversation *conv);
void winpidgin_window_flash(GtkWindow *window, gboolean flash);
DWORD winpidgin_get_lastactive(void);
/* init / cleanup */
-void winpidgin_init(HINSTANCE);
+void winpidgin_init(void);
void winpidgin_post_init(void);
void winpidgin_cleanup(void);
diff --git a/pidgin/win32/nsis/generate_gtk_zip.sh b/pidgin/win32/nsis/generate_gtk_zip.sh
index 078d398dd6..cfa49a4a2a 100755
--- a/pidgin/win32/nsis/generate_gtk_zip.sh
+++ b/pidgin/win32/nsis/generate_gtk_zip.sh
@@ -1,60 +1,283 @@
#!/bin/bash
# Script to generate zip file for GTK+ runtime to be included in Pidgin installer
-PIDGIN_BASE=$1
-GPG_SIGN=$2
+PIDGIN_BASE=`pwd`
+PIDGIN_BASE="$PIDGIN_BASE/../../.."
+GPG_SIGN=$1
if [ ! -e $PIDGIN_BASE/ChangeLog ]; then
- echo $(basename $0) must must have the pidgin base dir specified as a parameter.
+ echo "Pidgin base directory not found"
exit 1
fi
+if [ ! -e $PIDGIN_BASE/VERSION ]; then
+ cd ../../..
+ make -f Makefile.mingw VERSION
+ cd - > /dev/null
+fi
+
STAGE_DIR=`readlink -f $PIDGIN_BASE/pidgin/win32/nsis/gtk_runtime_stage`
+CERT_PATH=`readlink -f $PIDGIN_BASE/pidgin/win32/nsis`/cacert.pem
#Subdirectory of $STAGE_DIR
INSTALL_DIR=Gtk
CONTENTS_FILE=$INSTALL_DIR/CONTENTS
PIDGIN_VERSION=$( < $PIDGIN_BASE/VERSION )
#This needs to be changed every time there is any sort of change.
-BUNDLE_VERSION=2.16.6.2
-BUNDLE_SHA1SUM=e1b1ec8d2159fa98b2a9f516dbfe745bf7a22169
+BUNDLE_VERSION=2.24.18.0
+BUNDLE_SHA1SUM="5957b0bf3f5e520863cf8ba64db7592383e9dd42"
ZIP_FILE="$PIDGIN_BASE/pidgin/win32/nsis/gtk-runtime-$BUNDLE_VERSION.zip"
+#BUNDLE_URL="https://pidgin.im/win32/download_redir.php?version=$PIDGIN_VERSION&gtk_version=$BUNDLE_VERSION&dl_pkg=gtk"
+BUNDLE_URL="https://pidgin.im/~twasilczyk/win32/gtk-runtime-$BUNDLE_VERSION.zip"
+
+if [ "x`uname`" == "xLinux" ]; then
+ is_win32="no"
+else
+ is_win32="yes"
+fi
+
+function download() {
+ if [ -e "$2" ]; then
+ echo "File exists"
+ exit 1
+ fi
+ failed=0
+ wget -t 5 "$1" -O "$2" -o "wget.log" --retry-connrefused --waitretry=5 \
+ --ca-certificate="$CERT_PATH" \
+ || failed=1
+ if [ $failed != 0 ] ; then
+ if [ "$3" != "quiet" ] ; then
+ echo "Download failed"
+ cat "wget.log"
+ fi
+ rm "wget.log"
+ rm -f "$2"
+ return 1
+ fi
+ rm "wget.log"
+ return 0
+}
+
+cat $PIDGIN_BASE/share/ca-certs/*.pem > "$CERT_PATH"
#Download the existing file (so that we distribute the exact same file for all releases with the same bundle version)
FILE="$ZIP_FILE"
if [ ! -e "$FILE" ]; then
- wget "https://pidgin.im/win32/download_redir.php?version=$PIDGIN_VERSION&gtk_version=$BUNDLE_VERSION&dl_pkg=gtk" -O "$FILE"
+ echo "Downloading the existing file"
+ download "$BUNDLE_URL" "$FILE" "quiet"
+fi
+if [ -e "$FILE" ]; then
+ CHECK_SHA1SUM=`sha1sum $FILE`
+ CHECK_SHA1SUM=${CHECK_SHA1SUM%%\ *}
+else
+ CHECK_SHA1SUM=
fi
-CHECK_SHA1SUM=`sha1sum $FILE`
-CHECK_SHA1SUM=${CHECK_SHA1SUM%%\ *}
if [ "$CHECK_SHA1SUM" != "$BUNDLE_SHA1SUM" ]; then
- echo "sha1sum ($CHECK_SHA1SUM) for $FILE doesn't match expected value of $BUNDLE_SHA1SUM"
+ if [ "x$CHECK_SHA1SUM" != "x" ]; then
+ echo "sha1sum ($CHECK_SHA1SUM) for $FILE doesn't match expected value of $BUNDLE_SHA1SUM"
+ fi
# Allow "devel" versions to build their own bundles if the download doesn't succeed
if [[ "$PIDGIN_VERSION" == *"devel" ]]; then
echo "Continuing GTK+ Bundle creation for development version of Pidgin"
else
+ echo "Couldn't download GTK+ Bundle"
exit 1
fi
else
+ echo "GTK+ Bundle is up to date"
exit 0
fi
+# origin: http://download.opensuse.org/repositories/windows:/mingw:/win32/openSUSE_12.3/noarch/
+DOWNLOAD_HOST="https://pidgin.im/~twasilczyk/win32/runtime-deps/"
+
+ALL=""
+
+ARC_ATK="${DOWNLOAD_HOST}mingw32-atk-2.8.0-1.5.noarch.rpm ATK 2.8.0-1.5 sha1sum:0c682eadc299963aaa5d7998d655e46ead7d7515"
+ALL+="ARC_ATK "
+
+ARC_CAIRO2="${DOWNLOAD_HOST}mingw32-libcairo2-1.10.2-8.12.noarch.rpm Cairo 1.10.2-8.12 sha1sum:e33c58603678a8ba113bffe282bec810bd70172e"
+ALL+="ARC_CAIRO2 "
+
+ARC_DBUS="${DOWNLOAD_HOST}mingw32-dbus-1-1.8.0-1.19.noarch.rpm D-Bus 1.8.0-1.19 sha1sum:c951d935f58212abb70ba5edf1794390f8434832"
+ALL+="ARC_DBUS "
+
+ARC_DBUS_GLIB="${DOWNLOAD_HOST}mingw32-dbus-1-glib-0.100.2-1.2.noarch.rpm dbus-glib 0.100.2-1.2 sha1sum:72d27c0c01ce3e94e4dddc653c6ac0544c28362f"
+ALL+="ARC_DBUS_GLIB "
+
+ARC_ENCHANT="${DOWNLOAD_HOST}mingw32-enchant-1.6.0-3.9.noarch.rpm Enchant 1.6.0-3.9 sha1sum:a8569c8dcc77f69d065320388fca822ef5bf1fe5"
+ALL+="ARC_ENCHANT "
+
+ARC_FONTCONFIG="${DOWNLOAD_HOST}mingw32-fontconfig-2.10.92-1.2.noarch.rpm fontconfig 2.10.92-1.2 sha1sum:d8da351f5d4d9816f02ac5287dd7887c711902ed"
+ALL+="ARC_FONTCONFIG "
+
+ARC_FREETYPE="${DOWNLOAD_HOST}mingw32-freetype-2.4.12-3.10.noarch.rpm freetype 2.4.12-3.10 sha1sum:d0c8fc8ba3785f5445a429ae7b6a3fce6d1f0333"
+ALL+="ARC_FREETYPE "
+
+ARC_GDK_PIXBUF="${DOWNLOAD_HOST}mingw32-gdk-pixbuf-2.28.0-1.2.noarch.rpm gdk-pixbuf 2.28.0-1.2 sha1sum:8673e06c3a838e47a093043bf86bb62ea3627fe0"
+ALL+="ARC_GDK_PIXBUF "
+
+ARC_GEOCLUE="${DOWNLOAD_HOST}mingw32-libgeoclue-0.12.99-1.10.noarch.rpm Geoclue 0.12.99-1.10 sha1sum:84410ca9a6d2fac46217c51e22ebbc5ac3cae040"
+ALL+="ARC_GEOCLUE "
+
+ARC_GLIB="${DOWNLOAD_HOST}mingw32-glib2-2.38.0-1.4.noarch.rpm Glib 2.38.0-1.4 sha1sum:e71d8c2f105548e752df4a0b6ba5958ab4826707"
+ALL+="ARC_GLIB "
+
+ARC_GNUTLS="${DOWNLOAD_HOST}mingw32-libgnutls-3.1.22-1.3.noarch.rpm GnuTLS 3.1.22-1.3 sha1sum:e744435362f4124210a7b70d75f154b7ac0bf1fd"
+ALL+="ARC_GNUTLS "
+
+ARC_GNUTLS_GCRYPT="${DOWNLOAD_HOST}mingw32-libgcrypt-1.6.0-2.4.noarch.rpm libgcrypt 1.6.0-2.4 sha1sum:1d6ce973a866d93dc083888b19d64a7891ff224f"
+ALL+="ARC_GNUTLS_GCRYPT "
+
+ARC_GNUTLS_GPGERR="${DOWNLOAD_HOST}mingw32-libgpg-error-1.12-2.5.noarch.rpm gpg-error 1.12-2.5 sha1sum:af3a21a58fce483196e882ab1bf801203535f46e"
+ALL+="ARC_GNUTLS_GPGERR "
+
+ARC_GTK2="${DOWNLOAD_HOST}mingw32-gtk2-2.24.18-3.4.noarch.rpm GTK+ 2.24.18-3.4 sha1sum:31bc7f8aa2222517a3ec614924a07432983dc20d"
+ALL+="ARC_GTK2 "
+
+ARC_LIBFFI="${DOWNLOAD_HOST}mingw32-libffi-3.0.13-2.2.noarch.rpm libffi 3.0.13-2.2 sha1sum:0751dddb44eba3f553534c0a2a8ed438ed84a793"
+ALL+="ARC_LIBFFI "
+
+ARC_LIBGADU="${DOWNLOAD_HOST}mingw32-libgadu-1.12.0rc3-6.1.noarch.rpm libgadu 1.12.0rc3-6.1 sha1sum:ec2f3ccbc850c29bb26318a46ccba4db39b0a328"
+ALL+="ARC_LIBGADU "
+
+ARC_LIBGCC="${DOWNLOAD_HOST}mingw32-libgcc-4.8.2-3.7.noarch.rpm libgcc 4.8.2-3.7 sha1sum:91ab8f6881ce1004dda5279920742f78743e897b"
+ALL+="ARC_LIBGCC "
+
+ARC_LIBGMP="${DOWNLOAD_HOST}mingw32-libgmp-5.0.5-2.2.noarch.rpm libgmp 5.0.5-2.2 sha1sum:30c8c403d4d2dead7674e567d83c8c069b603e49"
+ALL+="ARC_LIBGMP "
+
+ARC_LIBGNURX="${DOWNLOAD_HOST}mingw32-libgnurx-2.5-4.6.noarch.rpm libgnurx 2.5-4.6 sha1sum:51571e6b1e5e9fb865c110cae04c582ff3c44cb7"
+ALL+="ARC_LIBGNURX "
+
+ARC_LIBHB="${DOWNLOAD_HOST}mingw32-libharfbuzz-0.9.19-3.5.noarch.rpm libharfbuzz 0.9.19-3.5 sha1sum:a31cd8ed4a7a8b75cf7b1252706f79b19e612d68"
+ALL+="ARC_LIBHB "
+
+ARC_LIBHOGWEED="${DOWNLOAD_HOST}mingw32-libhogweed-2.7-2.2.noarch.rpm libhogweed 2.7-2.2 sha1sum:c22ea84a8a5037be6021f9494b8252861dee63b5"
+ALL+="ARC_LIBHOGWEED "
+
+ARC_LIBJASPER="${DOWNLOAD_HOST}mingw32-libjasper-1.900.1-6.6.noarch.rpm JasPer 1.900.1-6.6 sha1sum:1a0f0072e0b0f73bd8d4e26aed93baa10d77e504"
+ALL+="ARC_LIBJASPER "
+
+ARC_LIBICU="${DOWNLOAD_HOST}mingw32-libicu-51.1-2.3.noarch.rpm ICU 51.1-2.3 sha1sum:c259c9d7f9f58934ebb49ecc80b15b7492e5a245"
+ALL+="ARC_LIBICU "
+
+ARC_LIBIDN="${DOWNLOAD_HOST}mingw32-libidn-1.22-3.8.noarch.rpm libidn 1.22-3.8 sha1sum:2052ea6fc2e789b2c252f621a7134ea4286cf5cc"
+ALL+="ARC_LIBIDN "
+
+ARC_LIBINTL="${DOWNLOAD_HOST}mingw32-libintl-0.18.1.1-15.11.noarch.rpm libintl 0.18.1.1-15.11 sha1sum:29812544f7362dde1378f71feb31fed4f9cb640e"
+ALL+="ARC_LIBINTL "
+
+ARC_LIBJPEG="${DOWNLOAD_HOST}mingw32-libjpeg-8d-3.6.noarch.rpm libjpeg 8d-3.6 sha1sum:db85723377243045388a5d3c873262cd83ffa7e2"
+ALL+="ARC_LIBJPEG "
+
+ARC_LIBJSON="${DOWNLOAD_HOST}mingw32-libjson-glib-0.14.2-2.1.noarch.rpm json-glib 0.14.2-2.1 sha1sum:366bf545855ced7fdfefc57b75ef7bbb5ebc249b"
+ALL+="ARC_LIBJSON "
+
+ARC_LIBLZMA="${DOWNLOAD_HOST}mingw32-liblzma-5.0.4-1.6.noarch.rpm liblzma 5.0.4-1.6 sha1sum:67bad5204ae09d163f799adec3286fff297e3bc8"
+ALL+="ARC_LIBLZMA "
+
+ARC_LIBNETTLE="${DOWNLOAD_HOST}mingw32-libnettle-2.7-2.2.noarch.rpm libnettle 2.7-2.2 sha1sum:45337e6ccb46c0752d2761b6c48a20e97dd09195"
+ALL+="ARC_LIBNETTLE "
+
+ARC_LIBP11="${DOWNLOAD_HOST}mingw32-libp11-kit0-0.20.1-4.3.noarch.rpm libp11 0.20.1-4.3 sha1sum:ee5b7a3e16b29f26ee1c275d8228ba0bb6a27190"
+ALL+="ARC_LIBP11 "
+
+ARC_LIBPNG="${DOWNLOAD_HOST}mingw32-libpng-1.5.17-2.11.noarch.rpm libpng 1.5.17-2.11 sha1sum:ff2c29197b5529d2cdc936f7b2b61af5c1175d29"
+ALL+="ARC_LIBPNG "
+
+ARC_LIBSILC="${DOWNLOAD_HOST}mingw32-libsilc-1.1.10-2.1.noarch.rpm libsilc 1.1.10-2.1 sha1sum:b7690eac1a91caf2b02b058483a3768705a6f3df"
+ALL+="ARC_LIBSILC "
+
+ARC_LIBSILCCL="${DOWNLOAD_HOST}mingw32-libsilcclient-1.1.10-2.1.noarch.rpm libsilcclient 1.1.10-2.1 sha1sum:88b84ff4c43643ce4b8ec1eb345e73c139cc164a"
+ALL+="ARC_LIBSILCCL "
-ATK="http://ftp.gnome.org/pub/gnome/binaries/win32/atk/1.32/atk_1.32.0-2_win32.zip ATK 1.32.0-2 sha1sum:3c31c9d6b19af840e2bd8ccbfef4072a6548dc4e"
-#Cairo 1.10.2 has a bug that can be seen when selecting text
-#CAIRO="http://ftp.gnome.org/pub/GNOME/binaries/win32/dependencies/cairo_1.10.2-2_win32.zip Cairo 1.10.2-2 sha1sum:d44cd66a9f4d7d29a8f2c28d1c1c5f9b0525ba44"
-CAIRO="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/cairo_1.8.10-1_win32.zip Cairo 1.8.10-1 sha1sum:a08476cccd807943958610977a138c4d6097c7b8"
-EXPAT="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/expat_2.1.0-1_win32.zip Expat 2.1.0-1 gpg:0x71D4DDE53F188CBE"
-FONTCONFIG="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/fontconfig_2.8.0-2_win32.zip Fontconfig 2.8.0-2 sha1sum:37a3117ea6cc50c8a88fba9b6018f35a04fa71ce"
-FREETYPE="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/freetype_2.4.10-1_win32.zip Freetype 2.4.10-1 gpg:0x71D4DDE53F188CBE"
-GETTEXT="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/gettext-runtime_0.18.1.1-2_win32.zip Gettext 0.18.1.1-2 sha1sum:a7cc1ce2b99b408d1bbea9a3b4520fcaf26783b3"
-GLIB="http://ftp.gnome.org/pub/gnome/binaries/win32/glib/2.28/glib_2.28.8-1_win32.zip Glib 2.28.8-1 sha1sum:5d158f4c77ca0b5508e1042955be573dd940b574"
-GTK="http://ftp.acc.umu.se/pub/gnome/binaries/win32/gtk+/2.16/gtk+_2.16.6-2_win32.zip GTK+ 2.16.6-2 sha1sum:012853e6de814ebda0cc4459f9eed8ae680e6d17"
-LIBPNG="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/libpng_1.4.12-1_win32.zip libpng 1.4.12-1 gpg:0x71D4DDE53F188CBE"
-PANGO="https://developer.pidgin.im/static/win32/pango_1.29.4-1daa_win32.zip Pango 1.29.4-1daa gpg:0x86723FEEDE890574"
-ZLIB="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/zlib_1.2.5-2_win32.zip zlib 1.2.5-2 sha1sum:568907188761df2d9309196e447d91bbc5555d2b"
+ARC_LIBSOUP="${DOWNLOAD_HOST}mingw32-libsoup-2.42.2-1.1.noarch.rpm libsoup 2.42.2-1.1 sha1sum:f0af29ceb420daaa549dd5dc470fbd62bc732252"
+ALL+="ARC_LIBSOUP "
-ALL="ATK CAIRO EXPAT FONTCONFIG FREETYPE GETTEXT GLIB GTK LIBPNG PANGO ZLIB"
+ARC_LIBSSP="${DOWNLOAD_HOST}mingw32-libssp-4.8.2-3.7.noarch.rpm LibSSP 4.8.2-3.7 sha1sum:bc42038985d5c75dd3b1511849db2c9f80dbbd7e"
+ALL+="ARC_LIBSSP "
+
+ARC_LIBSTDCPP="${DOWNLOAD_HOST}mingw32-libstdc++-4.8.2-3.7.noarch.rpm libstdc++ 4.8.2-3.7 sha1sum:1f9779c16afb0bbfab6dc79389cf6f6198292062"
+ALL+="ARC_LIBSTDCPP "
+
+ARC_LIBTASN="${DOWNLOAD_HOST}mingw32-libtasn1-3.3-3.2.noarch.rpm libtasn 3.3-3.2 sha1sum:3d5bb0df8eb7ed5e3f05b5378d3d61dbbbdbbd3f"
+ALL+="ARC_LIBTASN "
+
+ARC_LIBTIFF="${DOWNLOAD_HOST}mingw32-libtiff-4.0.2-1.6.noarch.rpm libtiff 4.0.2-1.6 sha1sum:3a082540386748ead608d388ce55a0c1dd28715d"
+ALL+="ARC_LIBTIFF "
+
+ARC_LIBXML="${DOWNLOAD_HOST}mingw32-libxml2-2.9.0-2.1.noarch.rpm libxml 2.9.0-2.1 sha1sum:de73090544effcd167f94fcfe8e2d1f005adbea7"
+ALL+="ARC_LIBXML "
+
+ARC_LIBXSLT="${DOWNLOAD_HOST}mingw32-libxslt-1.1.28-1.2.noarch.rpm libxslt 1.1.28-1.2 sha1sum:6ee150c6271edded95f92285f59d02c2896e459e"
+ALL+="ARC_LIBXSLT "
+
+ARC_MEANW="${DOWNLOAD_HOST}mingw32-meanwhile-1.0.2-3.2.noarch.rpm Meanwhile 1.0.2-3.2 sha1sum:6b0fd8d94205d80eba37ea3e3f19ded7a1297473"
+ALL+="ARC_MEANW "
+
+ARC_MOZNSS="${DOWNLOAD_HOST}mingw32-mozilla-nss-3.14.5-3.9.noarch.rpm NSS 3.14.5-3.9 sha1sum:434bcb5073bae6d16ab248280b2b033507d20453"
+ALL+="ARC_MOZNSS "
+
+ARC_MOZNSPR="${DOWNLOAD_HOST}mingw32-mozilla-nspr-4.10.2-2.8.noarch.rpm NSPR 4.10.2-2.8 sha1sum:ca61d4453042725e4f700a4b51859dc5f58110c4"
+ALL+="ARC_MOZNSPR "
+
+ARC_NCURSES="${DOWNLOAD_HOST}mingw32-ncurses-5.9-20140422.1.noarch.rpm ncurses 5.9-20140422.1 sha1sum:4873c22e5f67d0bc72bbb89b71a3967bce6067e0"
+ALL+="ARC_NCURSES "
+
+ARC_PANGO="${DOWNLOAD_HOST}mingw32-pango-1.34.0-2.3.noarch.rpm Pango 1.34.0-2.3 sha1sum:65b55b73c4f5c8107fdf48ef2e4f5c351189cd4f"
+ALL+="ARC_PANGO "
+
+ARC_PIXMAN="${DOWNLOAD_HOST}mingw32-pixman-0.30.0-3.10.noarch.rpm pixman 0.30.0-3.10 sha1sum:ed63261f29c356a58276435df013376e688a3a6b"
+ALL+="ARC_PIXMAN "
+
+ARC_PROTOBUFC="${DOWNLOAD_HOST}mingw32-protobuf-c-0.15-6.1.noarch.rpm protobuf-c 0.15-6.1 sha1sum:b58ef0aca3c99d956479ec1510e3ca62d79a443f"
+ALL+="ARC_PROTOBUFC "
+
+ARC_PTHREADS="${DOWNLOAD_HOST}mingw32-pthreads-2.8.0-14.6.noarch.rpm pthreads 2.8.0-14.6 sha1sum:e948ae221f82bbcb4fbfd991638e4170c150fe9f"
+ALL+="ARC_PTHREADS "
+
+ARC_SQLITE="${DOWNLOAD_HOST}mingw32-libsqlite3-0-3.8.4.1-1.4.noarch.rpm SQLite 3.8.4.1-1.4 sha1sum:1c42db1a48f616d824c3ae8e0a8eb0693ddac88f"
+ALL+="ARC_SQLITE "
+
+ARC_VV_FARST="${DOWNLOAD_HOST}mingw32-farstream-0.1.2-5.3.noarch.rpm farstream 0.1.2-5.3 sha1sum:0334213ece2f339cba38aff9290ef07238763c5c"
+ALL+="ARC_VV_FARST "
+
+ARC_VV_GST="${DOWNLOAD_HOST}mingw32-gstreamer-0.10.36-6.3.noarch.rpm GStreamer 0.10.36-6.3 sha1sum:3fd80dfc05c64f277d787c60799638701e0f058e"
+ALL+="ARC_VV_GST "
+
+ARC_VV_GST_LIB="${DOWNLOAD_HOST}mingw32-libgstreamer-0.10.36-6.3.noarch.rpm GStreamer-libgstreamer 0.10.36-6.3 sha1sum:eef44d1ff93f0c2ddffdbaecc65f08a5617b4724"
+ALL+="ARC_VV_GST_LIB "
+
+ARC_VV_GST_INT="${DOWNLOAD_HOST}mingw32-libgstinterfaces-0.10.36-5.4.noarch.rpm GStreamer-interfaces 0.10.36-5.4 sha1sum:d974b38c1da02191103c253e27a15ec7f160000f"
+ALL+="ARC_VV_GST_INT "
+
+ARC_VV_GST_PLBAD="${DOWNLOAD_HOST}mingw32-gst-plugins-bad-0.10.23-5.4.noarch.rpm GStreamer-plugins-bad 0.10.23-5.4 sha1sum:d2754a1358edab0c06b4038123274025f58af6ef"
+ALL+="ARC_VV_GST_PLBAD "
+
+ARC_VV_GST_PLBASE="${DOWNLOAD_HOST}mingw32-gst-plugins-base-0.10.36-5.4.noarch.rpm GStreamer-plugins-base 0.10.36-5.4 sha1sum:9e642d5a1e71dfeaa5b38b7ebf0ade4442ee763b"
+ALL+="ARC_VV_GST_PLBASE "
+
+ARC_VV_GST_PLGOOD="${DOWNLOAD_HOST}mingw32-gst-plugins-good-0.10.31-5.4.noarch.rpm GStreamer-plugins-good 0.10.31-5.4 sha1sum:3e0daa815e4d51749fc6d2e9353245d09ee9854d"
+ALL+="ARC_VV_GST_PLGOOD "
+
+ARC_VV_LIBNICE="${DOWNLOAD_HOST}mingw32-libnice-0.1.4-5.3.noarch.rpm libnice 0.1.4-5.3 sha1sum:abbabaa03d81202f2d78adca2b833d1072dfecf0"
+ALL+="ARC_VV_LIBNICE "
+
+ARC_VV_LIBOGG="${DOWNLOAD_HOST}mingw32-libogg-1.3.0-1.8.noarch.rpm libogg 1.3.0-1.8 sha1sum:1978cbd5148630fc95d4a6b1c5024f76f519fcd4"
+ALL+="ARC_VV_LIBOGG "
+
+ARC_VV_LIBTHEORA="${DOWNLOAD_HOST}mingw32-libtheora-1.1.1-5.8.noarch.rpm libtheora 1.1.1-5.8 sha1sum:9809978e4e7c0a620dd735218bb1bd317fe32149"
+ALL+="ARC_VV_LIBTHEORA "
+
+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 "
mkdir -p $STAGE_DIR
cd $STAGE_DIR
@@ -65,15 +288,65 @@ mkdir $INSTALL_DIR
#new CONTENTS file
echo Bundle Version $BUNDLE_VERSION > $CONTENTS_FILE
+if [ $is_win32 = "yes" ]; then
+CPIO_URL="https://pidgin.im/~twasilczyk/win32/devel-deps/cpio/bsdcpio-3.0.3-1.4.tar.gz"
+CPIO_SHA1SUM="0460c7a52f8c93d3c4822d6d1aaf9410f21bd4da"
+CPIO_DIR="bsdcpio"
+FILE="bsdcpio.tar.gz"
+if [ ! -e "$FILE" ]; then
+ echo "Downloading bsdcpio"
+ download "$CPIO_URL" "$FILE" || exit 1
+fi
+CHECK_SHA1SUM=`sha1sum $FILE`
+CHECK_SHA1SUM=${CHECK_SHA1SUM%%\ *}
+if [ "$CHECK_SHA1SUM" != "$CPIO_SHA1SUM" ]; then
+ echo "sha1sum ($CHECK_SHA1SUM) for $FILE doesn't match expected value of $CPIO_SHA1SUM"
+ rm $FILE
+ exit 1
+fi
+rm -rf "$CPIO_DIR"
+mkdir "$CPIO_DIR"
+tar xf "$FILE" --strip-components=1 --directory="$CPIO_DIR" || exit 1
+BSDCPIO=bsdcpio/bsdcpio.exe
+else
+BSDCPIO=`which bsdcpio`
+fi
+
+function rpm_install {
+ PKG_NAME=${NAME%%\ *}
+ if [ "$PKG_NAME" = "GStreamer-plugins-bad" ]; then
+ cp $MINGW_DIR/lib/gstreamer-0.10/libgstdirectdrawsink.dll $INSTALL_DIR/lib/gstreamer-0.10/
+ cp $MINGW_DIR/lib/gstreamer-0.10/libgstdirectsoundsrc.dll $INSTALL_DIR/lib/gstreamer-0.10/
+ cp $MINGW_DIR/lib/gstreamer-0.10/libgstliveadder.dll $INSTALL_DIR/lib/gstreamer-0.10/
+ cp $MINGW_DIR/lib/gstreamer-0.10/libgstrtpmux.dll $INSTALL_DIR/lib/gstreamer-0.10/
+ cp $MINGW_DIR/lib/gstreamer-0.10/libgstwinks.dll $INSTALL_DIR/lib/gstreamer-0.10/
+ elif [ "$PKG_NAME" = "GStreamer-plugins-good" ]; then
+ cp $MINGW_DIR/lib/gstreamer-0.10/libgstalaw.dll $INSTALL_DIR/lib/gstreamer-0.10/
+ cp $MINGW_DIR/lib/gstreamer-0.10/libgstdirectsoundsink.dll $INSTALL_DIR/lib/gstreamer-0.10/
+ cp $MINGW_DIR/lib/gstreamer-0.10/libgstlevel.dll $INSTALL_DIR/lib/gstreamer-0.10/
+ cp $MINGW_DIR/lib/gstreamer-0.10/libgstmulaw.dll $INSTALL_DIR/lib/gstreamer-0.10/
+ cp $MINGW_DIR/lib/gstreamer-0.10/libgstrtp.dll $INSTALL_DIR/lib/gstreamer-0.10/
+ cp $MINGW_DIR/lib/gstreamer-0.10/libgstrtpmanager.dll $INSTALL_DIR/lib/gstreamer-0.10/
+ cp $MINGW_DIR/lib/gstreamer-0.10/libgstrtsp.dll $INSTALL_DIR/lib/gstreamer-0.10/
+ cp $MINGW_DIR/lib/gstreamer-0.10/libgstwavparse.dll $INSTALL_DIR/lib/gstreamer-0.10/
+ else
+ cp -rf $MINGW_DIR/* $INSTALL_DIR
+ fi
+}
+
function download_and_extract {
URL=${1%%\ *}
VALIDATION=${1##*\ }
NAME=${1%\ *}
NAME=${NAME#*\ }
FILE=$(basename $URL)
+ MINGW_DIR="usr/i686-w64-mingw32/sys-root/mingw"
+ MINGW_DIR_TOP="usr"
if [ ! -e $FILE ]; then
- echo Downloading $NAME
- wget $URL || exit 1
+ echo "Downloading $NAME"
+ download "$URL" "$FILE" || exit 1
+ else
+ echo "Extracting $NAME"
fi
VALIDATION_TYPE=${VALIDATION%%:*}
VALIDATION_VALUE=${VALIDATION##*:}
@@ -87,7 +360,7 @@ function download_and_extract {
elif [ $VALIDATION_TYPE == 'gpg' ]; then
if [ ! -e "$FILE.asc" ]; then
echo Downloading GPG key for $NAME
- wget "$URL.asc" || exit 1
+ download "$URL.asc" "$FILE.asc" || exit 1
fi
#Use our own keyring to avoid adding stuff to the main keyring
#This doesn't use $GPG_SIGN because we don't this validation to be bypassed when people are skipping signing output
@@ -105,36 +378,52 @@ function download_and_extract {
EXTENSION=${FILE##*.}
#This is an OpenSuSE build service RPM
if [ $EXTENSION == 'rpm' ]; then
- echo "Generating zip from $FILE"
- FILE=$(../rpm2zip.sh $FILE)
+ rm -rf $MINGW_DIR_TOP
+ $BSDCPIO --quiet -f etc/fonts/conf.d -di < $FILE || exit 1
+ rpm_install
+ rm -rf $MINGW_DIR_TOP
+ else
+ unzip -q $FILE -d $INSTALL_DIR || exit 1
fi
- unzip -q $FILE -d $INSTALL_DIR || exit 1
echo "$NAME" >> $CONTENTS_FILE
}
+echo "Downloading and extracting components..."
for VAL in $ALL
do
VAR=${!VAL}
download_and_extract "$VAR"
done
+rm -rf $CPIO_DIR
+rm "$CERT_PATH"
-#Default GTK+ Theme to MS-Windows
-echo gtk-theme-name = \"MS-Windows\" > $INSTALL_DIR/etc/gtk-2.0/gtkrc
+#mv "${STAGE_DIR}/${INSTALL_DIR}/share/tcl8.5" "${STAGE_DIR}/${INSTALL_DIR}/lib/"
+rm "${STAGE_DIR}/${INSTALL_DIR}/lib/gstreamer-0.10/libfsmsnconference.dll"
+rm "${STAGE_DIR}/${INSTALL_DIR}/lib/gstreamer-0.10/libgstgnomevfs.dll"
-#Blow away translations that we don't have in Pidgin
-for LOCALE_DIR in $INSTALL_DIR/share/locale/*
-do
- LOCALE=$(basename $LOCALE_DIR)
- if [ ! -e $PIDGIN_BASE/po/$LOCALE.po ]; then
- echo Removing $LOCALE translation as it is missing from Pidgin
- rm -r $LOCALE_DIR
- fi
-done
+echo "All components ready"
+
+#Default GTK+ Theme to MS-Windows (already set)
+#echo gtk-theme-name = \"MS-Windows\" > $INSTALL_DIR/etc/gtk-2.0/gtkrc
+
+#Blow away translations that we don't have in Pidgin (temporarily not included)
+#for LOCALE_DIR in $INSTALL_DIR/share/locale/*
+#do
+# LOCALE=$(basename $LOCALE_DIR)
+# if [ ! -e $PIDGIN_BASE/po/$LOCALE.po ]; then
+# echo Removing $LOCALE translation as it is missing from Pidgin
+# rm -r $LOCALE_DIR
+# fi
+#done
#Generate zip file to be included in installer
rm -f $ZIP_FILE
zip -9 -r $ZIP_FILE Gtk
-($GPG_SIGN -ab $ZIP_FILE && $GPG_SIGN --verify $ZIP_FILE.asc) || exit 1
-exit 0
+if [ "`$GPG_SIGN -K 2> /dev/null`" != "" ]; then
+ ($GPG_SIGN -ab $ZIP_FILE && $GPG_SIGN --verify $ZIP_FILE.asc) || exit 1
+else
+ echo "Warning: cannot sign generated bundle"
+fi
+exit 0
diff --git a/pidgin/win32/nsis/nsis_translations.desktop.in b/pidgin/win32/nsis/nsis_translations.desktop.in
index ed0dc8284a..e732e7f120 100644
--- a/pidgin/win32/nsis/nsis_translations.desktop.in
+++ b/pidgin/win32/nsis/nsis_translations.desktop.in
@@ -47,15 +47,15 @@ _URIHANDLERSSECTIONTITLE=URI Handlers
#Installer Subsection Text
_PIDGINSPELLCHECKSECTIONTITLE=Spellchecking Support
# $R3 will display the URL that the Dictionary failed to download from
-_PIDGINSPELLCHECKERROR=Error Installing Spellchecking ($R3).$\rIf retrying fails, manual installation instructions are at: http://developer.pidgin.im/wiki/Installing%20Pidgin#manual_win32_spellcheck_installation
+_PIDGINSPELLCHECKERROR=Error Installing Spellchecking ($R3).$\rIf retrying fails, manual installation instructions are at: https://developer.pidgin.im/wiki/Installing%20Pidgin#manual_win32_spellcheck_installation
#Installer Subsection Detailed Description
_PIDGINSPELLCHECKSECTIONDESCRIPTION=Support for Spellchecking. (Internet connection required for installation)
# $R2 will display the URL that the Debug Symbols failed to download from
-_PIDGINDEBUGSYMBOLSERROR=Error Installing Debug Symbols ($R2).$\rIf retrying fails, you may need to use the 'Offline Installer' from http://pidgin.im/download/windows/ .
+_PIDGINDEBUGSYMBOLSERROR=Error Installing Debug Symbols ($R2).$\rIf retrying fails, you may need to use the 'Offline Installer' from https://pidgin.im/download/windows/ .
# $R2 will display the URL that the GTK+ Runtime failed to download from
-_PIDGINGTKDOWNLOADERROR=Error Downloading the GTK+ Runtime ($R2).$\rThis is required for Pidgin to function; if retrying fails, you may need to use the 'Offline Installer' from http://pidgin.im/download/windows/ .
+_PIDGINGTKDOWNLOADERROR=Error Downloading the GTK+ Runtime ($R2).$\rThis is required for Pidgin to function; if retrying fails, you may need to use the 'Offline Installer' from https://pidgin.im/download/windows/ .
_PIDGINUNINSTALLERROR1=The uninstaller could not find registry entries for Pidgin.$\rIt is likely that another user installed this application.
_PIDGINUNINSTALLERROR2=You do not have permission to uninstall this application.
diff --git a/pidgin/win32/nsis/pidgin-installer.nsi b/pidgin/win32/nsis/pidgin-installer.nsi
index 18d89a85fa..92eb5547de 100644
--- a/pidgin/win32/nsis/pidgin-installer.nsi
+++ b/pidgin/win32/nsis/pidgin-installer.nsi
@@ -121,7 +121,7 @@ ReserveFile "${NSISDIR}\Plugins\UserInfo.dll"
;!define MUI_FINISHPAGE_RUN "$INSTDIR\pidgin.exe"
;!define MUI_FINISHPAGE_RUN_NOTCHECKED
!define MUI_FINISHPAGE_LINK $(PIDGINFINISHVISITWEBSITE)
- !define MUI_FINISHPAGE_LINK_LOCATION "http://pidgin.im"
+ !define MUI_FINISHPAGE_LINK_LOCATION "https://pidgin.im"
;--------------------------------
;Pages
@@ -310,7 +310,7 @@ Section $(PIDGINSECTIONTITLE) SecPidgin
WriteRegStr HKLM "${PIDGIN_UNINSTALL_KEY}" "DisplayIcon" "$INSTDIR\pidgin.exe"
WriteRegStr HKLM "${PIDGIN_UNINSTALL_KEY}" "DisplayName" "Pidgin"
WriteRegStr HKLM "${PIDGIN_UNINSTALL_KEY}" "DisplayVersion" "${PIDGIN_VERSION}"
- WriteRegStr HKLM "${PIDGIN_UNINSTALL_KEY}" "HelpLink" "http://developer.pidgin.im/wiki/Using Pidgin"
+ WriteRegStr HKLM "${PIDGIN_UNINSTALL_KEY}" "HelpLink" "https://developer.pidgin.im/wiki/Using Pidgin"
WriteRegDWORD HKLM "${PIDGIN_UNINSTALL_KEY}" "NoModify" 1
WriteRegDWORD HKLM "${PIDGIN_UNINSTALL_KEY}" "NoRepair" 1
WriteRegStr HKLM "${PIDGIN_UNINSTALL_KEY}" "UninstallString" "$INSTDIR\${PIDGIN_UNINST_EXE}"
@@ -324,7 +324,7 @@ Section $(PIDGINSECTIONTITLE) SecPidgin
WriteRegStr HKCU "${PIDGIN_UNINSTALL_KEY}" "DisplayIcon" "$INSTDIR\pidgin.exe"
WriteRegStr HKCU "${PIDGIN_UNINSTALL_KEY}" "DisplayName" "Pidgin"
WriteRegStr HKCU "${PIDGIN_UNINSTALL_KEY}" "DisplayVersion" "${PIDGIN_VERSION}"
- WriteRegStr HKCU "${PIDGIN_UNINSTALL_KEY}" "HelpLink" "http://developer.pidgin.im/wiki/Using Pidgin"
+ WriteRegStr HKCU "${PIDGIN_UNINSTALL_KEY}" "HelpLink" "https://developer.pidgin.im/wiki/Using Pidgin"
WriteRegDWORD HKCU "${PIDGIN_UNINSTALL_KEY}" "NoModify" 1
WriteRegDWORD HKCU "${PIDGIN_UNINSTALL_KEY}" "NoRepair" 1
WriteRegStr HKCU "${PIDGIN_UNINSTALL_KEY}" "UninstallString" "$INSTDIR\${PIDGIN_UNINST_EXE}"
@@ -403,7 +403,6 @@ SectionGroupEnd
SectionGroup /e $(URIHANDLERSSECTIONTITLE) SecURIHandlers
!insertmacro URI_SECTION "aim"
!insertmacro URI_SECTION "msnim"
- !insertmacro URI_SECTION "myim"
!insertmacro URI_SECTION "ymsgr"
!insertmacro URI_SECTION "xmpp"
SectionGroupEnd
@@ -522,8 +521,6 @@ Section Uninstall
Call un.UnregisterURIHandler
Push "msnim"
Call un.UnregisterURIHandler
- Push "myim"
- Call un.UnregisterURIHandler
Push "ymsgr"
Call un.UnregisterURIHandler
Push "xmpp"
@@ -562,12 +559,19 @@ Section Uninstall
RMDir "$INSTDIR\ca-certs"
RMDir /r "$INSTDIR\locale"
RMDir /r "$INSTDIR\pixmaps"
+ RMDir /r "$INSTDIR\theme"
Delete "$INSTDIR\plugins\autoaccept.dll"
Delete "$INSTDIR\plugins\buddynote.dll"
Delete "$INSTDIR\plugins\convcolors.dll"
Delete "$INSTDIR\plugins\extplacement.dll"
Delete "$INSTDIR\plugins\gtkbuddynote.dll"
Delete "$INSTDIR\plugins\history.dll"
+ Delete "$INSTDIR\plugins\internalkeyring.dll"
+ Delete "$INSTDIR\plugins\libfacebook.dll"
+ Delete "$INSTDIR\plugins\libgtalk.dll"
+ Delete "$INSTDIR\plugins\ssl-gnutls.dll"
+ Delete "$INSTDIR\plugins\webkit.dll"
+ Delete "$INSTDIR\plugins\wincred.dll"
Delete "$INSTDIR\plugins\iconaway.dll"
Delete "$INSTDIR\plugins\idle.dll"
Delete "$INSTDIR\plugins\joinpart.dll"
@@ -578,13 +582,10 @@ Section Uninstall
Delete "$INSTDIR\plugins\libirc.dll"
Delete "$INSTDIR\plugins\libmsn.dll"
Delete "$INSTDIR\plugins\libmxit.dll"
- Delete "$INSTDIR\plugins\libmyspace.dll"
- Delete "$INSTDIR\plugins\libnapster.dll"
Delete "$INSTDIR\plugins\libnovell.dll"
Delete "$INSTDIR\plugins\libsametime.dll"
Delete "$INSTDIR\plugins\libsilc.dll"
Delete "$INSTDIR\plugins\libsimple.dll"
- Delete "$INSTDIR\plugins\libtoc.dll"
Delete "$INSTDIR\plugins\libyahoo.dll"
Delete "$INSTDIR\plugins\libyahoojp.dll"
Delete "$INSTDIR\plugins\libxmpp.dll"
@@ -622,7 +623,6 @@ Section Uninstall
RMDir "$INSTDIR\sounds\purple"
RMDir "$INSTDIR\sounds"
Delete "$INSTDIR\spellcheck\libenchant.dll"
- Delete "$INSTDIR\spellcheck\libgtkspell-0.dll"
Delete "$INSTDIR\spellcheck\lib\enchant\libenchant_aspell.dll"
Delete "$INSTDIR\spellcheck\lib\enchant\libenchant_ispell.dll"
Delete "$INSTDIR\spellcheck\lib\enchant\libenchant_myspell.dll"
diff --git a/pidgin/win32/nsis/rpm2zip.sh b/pidgin/win32/nsis/rpm2zip.sh
deleted file mode 100755
index 51c6adaf33..0000000000
--- a/pidgin/win32/nsis/rpm2zip.sh
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/bin/sh
-
-here=`pwd`
-for F in $*; do
- case $F in
- mingw32-*.noarch.rpm|mingw64-*.noarch.rpm|*/mingw32-*.noarch.rpm|*/mingw64-*.noarch.rpm)
- package=`rpm -qp $F 2>/dev/null`
- case $package in
- mingw32-*|mingw64-*)
- case $package in
- mingw32-*)
- cpu=i686
- bits=32
- ;;
- mingw64-*)
- cpu=x86_64
- bits=64
- ;;
- esac
- origname=`rpm -qp --queryformat='%{NAME}' $F 2>/dev/null`
- name=$origname
- case $name in
- *-devel)
- name=${name%el}
- ;;
- esac
- shortpackage="$name"_`rpm -qp --queryformat='%{VERSION}-%{RELEASE}'_win${bits} $F 2>/dev/null`
- shortpackage=${shortpackage#mingw32-}
- shortpackage=${shortpackage#mingw64-}
- shortname=$name
- shortname=${shortname#mingw32-}
- shortname=${shortname#mingw64-}
- tmp=`mktemp -d`
- #rpm2cpio $F | lzcat | (cd $tmp && cpio --quiet -id)
- rpm2cpio $F | (cd $tmp && cpio --quiet -id)
- (
- cd $tmp
- zipfile="$here/$shortpackage.zip"
- rm -f $zipfile
- (cd usr/${cpu}-pc-mingw32/sys-root/mingw && zip -q -r -D $zipfile .)
- if [ -d usr/share/doc/packages/$origname ] ; then
- mv usr/share/doc/packages/$origname usr/share/doc/packages/$shortname
- (cd usr && zip -q -r -D $zipfile share/doc/packages/$shortname)
- fi
- mkdir -p manifest
- unzip -l $zipfile >manifest/$shortpackage.mft
- zip -q $zipfile manifest/$shortpackage.mft
- N=`unzip -l $zipfile | wc -l | sed -e 's/^ *\([0-9]*\).*/\1/'`
- Nm1=`expr $N - 1`
- unzip -l $zipfile | sed -e "1,3 d" -e "$Nm1,$N d" | awk '{print $4}' | grep -v -E '/$' >manifest/$shortpackage.mft
- zip -q $zipfile manifest/$shortpackage.mft
- echo $zipfile
- )
- rm -rf $tmp
- ;;
- *)
- echo $F is not a mingw32/64 RPM package >&2
- ;;
- esac
- ;;
- *)
- echo $F is not a mingw32/64 RPM package >&2
- ;;
- esac
-done
diff --git a/pidgin/win32/pidgin_dll_rc.rc.in b/pidgin/win32/pidgin_dll_rc.rc.in
index b41144cb99..5b0bc6bae2 100644
--- a/pidgin/win32/pidgin_dll_rc.rc.in
+++ b/pidgin/win32/pidgin_dll_rc.rc.in
@@ -1,10 +1,9 @@
#include <winver.h>
-#include "version.h"
#include "resource.h"
VS_VERSION_INFO VERSIONINFO
- FILEVERSION PURPLE_MAJOR_VERSION,PURPLE_MINOR_VERSION,PURPLE_MICRO_VERSION,0
- PRODUCTVERSION PURPLE_MAJOR_VERSION,PURPLE_MINOR_VERSION,PURPLE_MICRO_VERSION,0
+ FILEVERSION @PURPLE_MAJOR_VERSION@,@PURPLE_MINOR_VERSION@,@PURPLE_MICRO_VERSION@,0
+ PRODUCTVERSION @PURPLE_MAJOR_VERSION@,@PURPLE_MINOR_VERSION@,@PURPLE_MICRO_VERSION@,0
FILEFLAGSMASK 0
FILEFLAGS 0
FILEOS VOS__WINDOWS32
@@ -16,13 +15,13 @@ VS_VERSION_INFO VERSIONINFO
BLOCK "040904B0"
BEGIN
VALUE "CompanyName", "The Pidgin developer community"
- VALUE "FileDescription", "GTK+ Pidgin Library"
- VALUE "FileVersion", "@PIDGIN_VERSION@"
+ VALUE "FileDescription", "Pidgin UI library"
+ VALUE "FileVersion", "@PURPLE_VERSION@"
VALUE "InternalName", "libpidgin"
- VALUE "LegalCopyright", "Copyright (C) 1998-2010 The Pidgin developer community (See the COPYRIGHT file in the source distribution)."
- VALUE "OriginalFilename", "pidgin.dll"
+ VALUE "LegalCopyright", "Copyright (C) 1998-2014 The Pidgin developer community (See the COPYRIGHT file in the source distribution)."
+ VALUE "OriginalFilename", "libpidgin-@PURPLE_API_VERSION@.dll"
VALUE "ProductName", "Pidgin"
- VALUE "ProductVersion", "@PIDGIN_VERSION@"
+ VALUE "ProductVersion", "@PURPLE_VERSION@"
END
END
BLOCK "VarFileInfo"
diff --git a/pidgin/win32/pidgin_exe_rc.rc.in b/pidgin/win32/pidgin_exe_rc.rc.in
index b7a2135841..d357496c72 100644
--- a/pidgin/win32/pidgin_exe_rc.rc.in
+++ b/pidgin/win32/pidgin_exe_rc.rc.in
@@ -1,10 +1,9 @@
#include <winver.h>
#include "resource.h"
-#include "version.h"
VS_VERSION_INFO VERSIONINFO
- FILEVERSION PURPLE_MAJOR_VERSION,PURPLE_MINOR_VERSION,PURPLE_MICRO_VERSION,0
- PRODUCTVERSION PURPLE_MAJOR_VERSION,PURPLE_MINOR_VERSION,PURPLE_MICRO_VERSION,0
+ FILEVERSION @PURPLE_MAJOR_VERSION@,@PURPLE_MINOR_VERSION@,@PURPLE_MICRO_VERSION@,0
+ PRODUCTVERSION @PURPLE_MAJOR_VERSION@,@PURPLE_MINOR_VERSION@,@PURPLE_MICRO_VERSION@,0
FILEFLAGSMASK 0
FILEFLAGS 0
FILEOS VOS__WINDOWS32
@@ -16,13 +15,13 @@ VS_VERSION_INFO VERSIONINFO
BLOCK "040904B0"
BEGIN
VALUE "CompanyName", "The Pidgin developer community"
- VALUE "FileDescription", "Pidgin"
- VALUE "FileVersion", "@PIDGIN_VERSION@"
+ VALUE "FileDescription", "Pidgin instant messenger"
+ VALUE "FileVersion", "@PURPLE_VERSION@"
VALUE "InternalName", "pidgin"
- VALUE "LegalCopyright", "Copyright (C) 1998-2010 The Pidgin developer community (See the COPYRIGHT file in the source distribution)."
- VALUE "OriginalFilename", "@ORIGINAL_FILENAME@"
+ VALUE "LegalCopyright", "Copyright (C) 1998-2014 The Pidgin developer community (See the COPYRIGHT file in the source distribution)."
+ VALUE "OriginalFilename", "pidgin.exe"
VALUE "ProductName", "Pidgin"
- VALUE "ProductVersion", "@PIDGIN_VERSION@"
+ VALUE "ProductVersion", "@PURPLE_VERSION@"
END
END
BLOCK "VarFileInfo"
diff --git a/pidgin/win32/prepare-workspace.sh b/pidgin/win32/prepare-workspace.sh
new file mode 100644
index 0000000000..cdc7b2392e
--- /dev/null
+++ b/pidgin/win32/prepare-workspace.sh
@@ -0,0 +1,445 @@
+#!/bin/bash
+# Script to prepare a workspace (devel dependencies, check system
+# configuration) for Pidgin compilation under win32
+#
+# Written by Tomek Wasilczyk <tomkiewicz@cpw.pidgin.im>, licensed under GNU GPL
+
+PLATFORM=`uname -m`
+
+# configuration
+
+BONJOUR_SDK_DIR="/cygdrive/c/Program Files/Bonjour SDK"
+ACTIVEPERL_GUID_PACKED="BC98F31FB8440B94CB3674649419766C 547A2C684F806164DB756F228DAB5840 5E7EC16051106BB43818746C209BC8D7"
+PERL_DIR_FALLBACK="/cygdrive/c/Perl/bin"
+if [ "$PLATFORM" == "x86_64" ]; then
+ NSIS_DIR_REGKEY="HKEY_LOCAL_MACHINE/SOFTWARE/Wow6432Node/NSIS/@"
+else
+ NSIS_DIR_REGKEY="HKEY_LOCAL_MACHINE/SOFTWARE/NSIS/@"
+fi
+
+
+DEBUG_SKIP_DOWNLOADING=0
+DEBUG_SKIP_INSTALL=0
+
+DOWNLOAD_HOST="https://pidgin.im/~twasilczyk/win32/devel-deps/"
+
+ARCHIVES=""
+OBS_SKIP="usr/i686-w64-mingw32/sys-root/mingw"
+
+# bsdcpio is used for extracting rpms
+ARC_CPI="${DOWNLOAD_HOST}cpio/bsdcpio-3.0.3-1.4.tar.gz;bsdcpio;3.0.3-1.4;0460c7a52f8c93d3c4822d6d1aaf9410f21bd4da;bsdcpio-3.0.3-1.4;bsdcpio"
+ARCHIVES+="ARC_CPI "
+
+ARC_CSA="${DOWNLOAD_HOST}cyrus-sasl-2.1.25.tar.gz;Cyrus SASL;2.1.25;b9d7f510c0c5daa71ee5225daacdd58e948a8d19;cyrus-sasl-2.1.25;cyrus-sasl-2.1"
+ARCHIVES+="ARC_CSA "
+
+ARC_NSS="${DOWNLOAD_HOST}mingw32-mozilla-nss-devel-3.14.3-2.2.noarch.rpm;NSS;3.14.3-2.2;fd394678ef2a8ef1dbbc20c25701678bc8678acf;${OBS_SKIP};nss-3.14"
+ARCHIVES+="ARC_NSS "
+ARC_NSP="${DOWNLOAD_HOST}mingw32-mozilla-nspr-devel-4.9.6-4.1.noarch.rpm;NSPR;4.9.6-4.1;b15aefbf99ade3042d0e4ed32f9368ff38064ecd;${OBS_SKIP};nss-3.14"
+ARCHIVES+="ARC_NSP "
+
+ARC_GTLS="${DOWNLOAD_HOST}mingw32-libgnutls-devel-3.1.16-2.2.noarch.rpm;GnuTLS;3.1.16-2.2;50406ff150ff81d1282a2f2a551699aa8a53e175;${OBS_SKIP};gnutls-3.1"
+ARCHIVES+="ARC_GTLS "
+
+ARC_PID="${DOWNLOAD_HOST}pidgin-inst-deps-20130214.tar.gz;inst-deps;20130214;372218ab472c4070cd45489dae175dea5638cf17;;"
+ARCHIVES+="ARC_PID "
+
+#mingw gcc and its dependencies
+ARC_MG1="${DOWNLOAD_HOST}mingw32-gcc-4.8.0-6.1.noarch.rpm;mingw: gcc;4.8.0-6.1;00591ba625cb4d3968f9907a76e7e3350e80c65b;${OBS_SKIP};mingw"
+ARCHIVES+="ARC_MG1 "
+ARC_MG2="${DOWNLOAD_HOST}mingw32-cpp-4.8.0-6.1.noarch.rpm;mingw: cpp;4.8.0-6.1;ea22584abf14cdf34217bb5eb24a30211a382882;${OBS_SKIP};mingw"
+ARCHIVES+="ARC_MG2 "
+ARC_MG3="${DOWNLOAD_HOST}mingw32-binutils-2.22.52-3.5.noarch.rpm;mingw: binutils;2.22.52-3.5;e6431d8dfa0dfe5a3488017c291cb68193999808;${OBS_SKIP};mingw"
+ARCHIVES+="ARC_MG3 "
+ARC_MG4="${DOWNLOAD_HOST}mingw32-libgmp-5.0.5-1.6.noarch.rpm;mingw: libgmp;5.0.5-1.6;58ff8155e870063a2cab999f413ffa1ec6ad2d16;${OBS_SKIP};mingw"
+ARCHIVES+="ARC_MG4 "
+ARC_MG5="${DOWNLOAD_HOST}mingw32-libmpc-1.0-1.6.noarch.rpm;mingw: libmpc;1.0-1.6;552dd1de81aef3dfdb7b3a87f13b79e6805d9940;${OBS_SKIP};mingw"
+ARCHIVES+="ARC_MG5 "
+ARC_MG6="${DOWNLOAD_HOST}mingw32-libmpfr-3.1.0-1.6.noarch.rpm;mingw: libmpfr;3.1.0-1.6;d86d12af65c442dc260d156528fff009d21dab9c;${OBS_SKIP};mingw"
+ARCHIVES+="ARC_MG6 "
+ARC_MG7="${DOWNLOAD_HOST}mingw32-runtime-20130216-2.3.noarch.rpm;mingw: runtime;20130216-2.3;9ff3810f8313d19ab18458d73565856608cf9188;${OBS_SKIP};mingw"
+ARCHIVES+="ARC_MG7 "
+ARC_MG8="${DOWNLOAD_HOST}mingw32-zlib-1.2.7-1.7.noarch.rpm;mingw: zlib;1.2.7-1.7;c34986df8520de706f9e8516f4353af90ba78f39;${OBS_SKIP};mingw"
+ARCHIVES+="ARC_MG8 "
+ARC_MG9="${DOWNLOAD_HOST}mingw32-headers-20130216-1.1.noarch.rpm;mingw: headers;20130216-1.1;313bdc131e15bbca1e4332395c536f2caa9e54b0;${OBS_SKIP}/include;mingw/lib/gcc/i686-w64-mingw32/4.8.0/include"
+ARCHIVES+="ARC_MG9 "
+ARC_MGA="${DOWNLOAD_HOST}mingw32-libgcc-4.8.0-6.1.noarch.rpm;mingw: libgcc;4.8.0-6.1;ab599bf07bf2d56367c57b442440598358c943af;${OBS_SKIP};mingw"
+ARCHIVES+="ARC_MGA "
+
+#gtk and friends
+GTK_DIR="gtk2-2.24"
+ARC_GT1="${DOWNLOAD_HOST}mingw32-glib2-devel-2.38.0-1.4.noarch.rpm;gtk: Glib;2.38.0-1.4;9299f85c4eb7fc84bc29d47edd9444cc2fdf3c7a;${OBS_SKIP};${GTK_DIR}"
+ARCHIVES+="ARC_GT1 "
+ARC_GT2="${DOWNLOAD_HOST}mingw32-gtk2-devel-2.24.14-2.7.noarch.rpm;gtk: GTK+2;2.24.14-2.7;4abd5fddf7ca2b6ee7ab35f4b549894bc146a005;${OBS_SKIP};${GTK_DIR}"
+ARCHIVES+="ARC_GT2 "
+ARC_GT3="${DOWNLOAD_HOST}mingw32-libintl-devel-0.18.1.1-13.6.noarch.rpm;gtk: libintl;0.18.1.1-13.6;49afd3059ecc7713debb29b801558958637114d1;${OBS_SKIP};${GTK_DIR}"
+ARCHIVES+="ARC_GT3 "
+ARC_GT4="${DOWNLOAD_HOST}mingw32-zlib-devel-1.2.7-1.7.noarch.rpm;gtk: zlib;1.2.7-1.7;e3fd07747fcd96bbf83d7a1a870feccc19c0e15e;${OBS_SKIP};${GTK_DIR}"
+ARCHIVES+="ARC_GT4 "
+ARC_GT5="${DOWNLOAD_HOST}mingw32-atk-devel-2.8.0-1.5.noarch.rpm;gtk: ATK;2.8.0-1.5;d6c54241ef3ce80b4a6722f23fe47eba88e0a9f0;${OBS_SKIP};${GTK_DIR}"
+ARCHIVES+="ARC_GT5 "
+ARC_GT6="${DOWNLOAD_HOST}mingw32-cairo-devel-1.10.2-8.12.noarch.rpm;gtk: Cairo;1.10.2-8.12;a9ea09988bc896226971dc544d9b499882d37ba6;${OBS_SKIP};${GTK_DIR}"
+ARCHIVES+="ARC_GT6 "
+ARC_GT7="${DOWNLOAD_HOST}mingw32-gdk-pixbuf-devel-2.28.0-1.2.noarch.rpm;gtk: GDK-PixBuf;2.28.0-1.2;d476228dd6e1ad43bbf0dd5d6e9e9bad394c9ec5;${OBS_SKIP};${GTK_DIR}"
+ARCHIVES+="ARC_GT7 "
+ARC_GT8="${DOWNLOAD_HOST}mingw32-pango-devel-1.34.0-2.3.noarch.rpm;gtk: Pango;1.34.0-2.3;c875ae60dacf05b642d7da5f289a3c58ff9b0e52;${OBS_SKIP};${GTK_DIR}"
+ARCHIVES+="ARC_GT8 "
+ARC_GT9="${DOWNLOAD_HOST}mingw32-glib2-2.36.1-1.1.noarch.rpm;gtk: Glib runtimes;2.36.1-1.1;ed468f064f61c5a12b716c83cba8ccbe05d22992;${OBS_SKIP};${GTK_DIR}"
+ARCHIVES+="ARC_GT9 "
+ARC_G10="${DOWNLOAD_HOST}mingw32-libintl-0.18.1.1-13.6.noarch.rpm;gtk: libintl;0.18.1.1-13.6;0e6fde8e86788874366f308e25634f95613e906a;${OBS_SKIP};${GTK_DIR}"
+ARCHIVES+="ARC_G10 "
+
+ARC_ENC="${DOWNLOAD_HOST}mingw32-enchant-devel-1.6.0-3.9.noarch.rpm;Enchant;1.6.0-3.9;1daadbb4fbeb06a6ad26bed916dc2a980d971c49;${OBS_SKIP};enchant-1.6"
+ARCHIVES+="ARC_ENC "
+
+# TODO: is it really necessary?
+ARC_INT="${DOWNLOAD_HOST}intltool-0.50.2-4.1.1.noarch.rpm;intltool;0.50.2-4.1.1;92c42de2b8a9827b6dca65090dd4b0e293397689;usr;intltool-0.50"
+ARCHIVES+="ARC_INT "
+
+ARC_MWH="${DOWNLOAD_HOST}mingw32-meanwhile-devel-1.0.2-3.2.noarch.rpm;meanwhile;1.0.2-3.2;2c92bbf6084cb930c923ec94c17b62b4b894c146;${OBS_SKIP};meanwhile-1.0"
+ARCHIVES+="ARC_MWH "
+ARC_MWHD="${DOWNLOAD_HOST}mingw32-meanwhile-debug-1.0.2-3.2.noarch.rpm;meanwhile debug symbols;1.0.2-3.2;7e3c02178d219426eeb8f4f34147763c7ea5be85;${OBS_SKIP};meanwhile-1.0"
+ARCHIVES+="ARC_MWHD "
+
+ARC_PRL="${DOWNLOAD_HOST}perl-5.10.0.tar.gz;Perl;5.10.0;46496029a80cabdfa119cbd70bc14d14bfde8071;perl-5.10.0;perl-5.10"
+ARCHIVES+="ARC_PRL "
+
+ARC_SIL="${DOWNLOAD_HOST}mingw32-silc-toolkit-devel-1.1.10-2.1.noarch.rpm;SILC Toolkit;1.1.10-2.1;cc92fc87c013a085bdd0664e8fba1acc5a2ccb18;${OBS_SKIP};silc-toolkit-1.1"
+ARCHIVES+="ARC_SIL "
+
+ARC_TCL="${DOWNLOAD_HOST}mingw32-tcl-devel-8.5.9-14.1.noarch.rpm;Tcl;8.5.9-14.1;22a64967654629e01a2f52226c3de431a43683f8;${OBS_SKIP};tcl-8.5;include/tcl-private/generic/(tcl|tclDecls|tclPlatDecls|tclTomMath|tclTomMathDecls)\\.h"
+ARCHIVES+="ARC_TCL "
+
+ARC_TK="${DOWNLOAD_HOST}mingw32-tk-devel-8.5.9-8.7.noarch.rpm;Tk;8.5.9-8.7;c469e5933cace0f2eed0fec9892843ca216c51ea;${OBS_SKIP};tcl-8.5;include/tk-private/generic/(tk|tkDecls|tkIntXlibDecls|tkPlatDecls)\\.h"
+ARCHIVES+="ARC_TK "
+
+ARC_JSG="${DOWNLOAD_HOST}mingw32-json-glib-devel-0.14.2-2.1.noarch.rpm;json-glib;0.14.2-2.1;27154ec4e4fa214b72f28658be2de7be4e0a9e3e;${OBS_SKIP};json-glib-0.14"
+ARCHIVES+="ARC_JSG "
+
+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 "
+
+ARC_GTT="${DOWNLOAD_HOST}mingw32-gettext-runtime-0.18.1.1-13.6.noarch.rpm;gettext;0.18.1.1-13.6;e3785e932427d63bf5cf27f258d1236e49437143;${OBS_SKIP};gettext-0.18"
+ARCHIVES+="ARC_GTT "
+ARC_GTL="${DOWNLOAD_HOST}mingw32-libintl-0.18.1.1-13.6.noarch.rpm;gettext: libintl;0.18.1.1-13.6;0e6fde8e86788874366f308e25634f95613e906a;${OBS_SKIP};gettext-0.18"
+ARCHIVES+="ARC_GTL "
+
+ARC_VV1="${DOWNLOAD_HOST}mingw32-gstreamer-devel-0.10.36-10.1.noarch.rpm;gstreamer;0.10.36-10.1;a54b53b31a47dd3d4243b8e772553e0b05430aaf;${OBS_SKIP};gstreamer-0.10"
+ARCHIVES+="ARC_VV1 "
+ARC_VV2="${DOWNLOAD_HOST}mingw32-gst-plugins-base-devel-0.10.36-15.1.noarch.rpm;gst-plugins-base;0.10.36-15.1;5bc0d94abdce4f2f2bafceda8046f01a5b29bd71;${OBS_SKIP};gstreamer-0.10"
+ARCHIVES+="ARC_VV2 "
+ARC_VV3="${DOWNLOAD_HOST}mingw32-farstream-devel-0.1.2-19.1.noarch.rpm;farstream;0.1.2-19.1;6c9f29de289b661d192c88998ed5bdf17de7bcec;${OBS_SKIP};gstreamer-0.10"
+ARCHIVES+="ARC_VV3 "
+
+ARC_GG="${DOWNLOAD_HOST}mingw32-libgadu-devel-1.12.0rc1-1.5.noarch.rpm;libgadu;1.12.0rc1-1.5;b88941625625628634081cf8aa03dcdc1be63d03;${OBS_SKIP};libgadu-1.12"
+ARCHIVES+="ARC_GG "
+
+
+# implementation
+
+if [ `uname -o` != "Cygwin" ]; then
+ echo
+ echo "WARNING: You are on a non-Cygwin platform! Your mileage may vary."
+ echo
+fi
+
+function path_win32_to_cygwin() {
+ path_ret=`echo "$1" | $SED 's|\\\\|/|g; s|\\(.\\):|/cygdrive/\\1|; s| |\\ |g'`
+}
+
+function path_real() {
+ if [ "$REALPATH" != "" ]; then
+ path_ret="`${REALPATH} "$1"`"
+ else
+ path_ret="$1"
+ fi
+}
+
+function reg_get_path() {
+ reg_ret=""
+ reg_key="/proc/registry/$1"
+ if [ ! -f $reg_key ] ; then
+ reg_key="/proc/registry64/$1"
+ fi
+ if [ -f $reg_key ] ; then
+ path_win32_to_cygwin "`cat ${reg_key}`"
+ reg_ret="${path_ret}"
+ return 0
+ fi
+ return 1
+}
+
+function reg_get_install_path() {
+ reg_ret=""
+ for guid_packed in $1 ; do
+ reg_get_path "HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows/CurrentVersion/Installer/UserData/S-1-5-18/Products/${guid_packed}/InstallProperties/InstallLocation" && return 0
+ done
+ return 1
+}
+
+function check_path() {
+ chk_cmd="$1"
+ expected="$2"
+
+ expected=`${REALPATH} -e "$expected"`
+ current=`which "${chk_cmd}" 2> /dev/null`
+ if [ "$expected" == "" ]; then
+ echo "Error while checking path"
+ exit 1
+ fi
+
+ if [ "$expected" != "$current" ]; then
+ dir=`dirname "${expected}"` || exit 1
+ echo "Adding $dir to PATH"
+ echo "" >> ~/.bashrc
+ echo "export PATH=\"$dir\":\$PATH" >> ~/.bashrc
+ return 1
+ fi
+ return 0
+}
+
+function download() {
+ if [ -e "$2" ]; then
+ echo "File exists"
+ exit 1
+ fi
+ failed=0
+ $WGET -t 3 "$1" -O "$2" -o "wget.log" --retry-connrefused --waitretry=2 --ca-certificate="$WIN32DEV_STORE/cacert.pem" || failed=1
+ if [ $failed != 0 ]; then
+ echo "Download failed"
+ cat "wget.log"
+ rm "wget.log"
+ rm -f "$2"
+ exit 1
+ fi
+ rm "wget.log"
+}
+
+function sha1sum_calc() {
+ sha1sum_ret=`$SHA1SUM "$1" | $SED 's| .*||'`
+}
+
+function download_archive() {
+ url=`echo "$1" | $CUT -d';' -f1`
+ name=`echo "$1" | $CUT -d';' -f2`
+ version=`echo "$1" | $CUT -d';' -f3`
+ sha1sum_orig=`echo "$1" | $CUT -d';' -f4`
+ file=`$BASENAME "$url"`
+
+ cd "$WIN32DEV_STORE"
+
+ if [ ! -e "$file" ]; then
+ echo "Downloading ${name} ${version}..."
+ download "$url" "$file"
+ fi
+
+ sha1sum_calc "$file"
+ sha1sum_file="$sha1sum_ret"
+
+ if [ "$sha1sum_file" != "$sha1sum_orig" ]; then
+ echo "sha1sum ($sha1sum_file) for $file doesn't match expected value of $sha1sum_orig"
+ exit 1
+ fi
+
+ cd - > /dev/null
+}
+
+function extract_archive() {
+ url=`echo "$1" | $CUT -d';' -f1`
+ name=`echo "$1" | $CUT -d';' -f2`
+ dir_skip=`echo "$1" | $CUT -d';' -f5`
+ dir_extract=`echo "$1" | $CUT -d';' -f6`
+ filter_output=`echo "$1" | $CUT -d';' -f7`
+ file=`$BASENAME "$url"`
+ ext=`echo "$file" | $SED 's|.*\.\(.*\)|\1|'`
+
+ old_dir=`pwd`
+ cd "$WIN32DEV_STORE"
+
+ if [ "${filter_output}" == "" ]; then
+ # don't match anything (only rpm)
+ filter_output="$$"
+ fi
+
+ if [ "$ext" == "patch" ]; then
+ echo "Applying ${name}..."
+
+ old_tmp="$TMP"
+ TMP="."
+
+ patch --strip=${dir_skip} --directory="${WIN32DEV_BASE}/${dir_extract}" --forward --quiet --input="$WIN32DEV_STORE/${file}" || exit 1
+
+ TMP="${old_tmp}"
+
+ cd "${old_dir}"
+ continue
+ fi
+
+ echo "Installing ${name}..."
+
+ rm -rf "tmp"
+ mkdir "tmp"
+ if [ "$ext" == "gz" ] || [ "$ext" == "bz2" ]; then
+ $TAR -xf "$file" -C "tmp" || exit 1
+ elif [ "$ext" == "zip" ]; then
+ $UNZIP -q "$file" -d "tmp" || exit 1
+ elif [ "$ext" == "rpm" ]; then
+ cd "tmp"
+ ( ${WIN32DEV_BASE}/bsdcpio/bsdcpio.exe --quiet -di < "../${file}" 2>&1 ) | grep -v -P "${filter_output}" 1>&2
+ cd ..
+ else
+ echo "Uknown extension: $ext"
+ rm -rf "tmp"
+ exit 1
+ fi
+
+ dst_dir="${WIN32DEV_BASE}/${dir_extract}"
+ src_dir="tmp/${dir_skip}"
+ mkdir -p $dst_dir
+ cp -rf "${src_dir}"/* "${dst_dir}"/ || exit 1
+ rm -rf "tmp"
+
+ cd "${old_dir}"
+}
+
+# required and optional system dependencies
+REALPATH=`which realpath`
+BASENAME=`which basename`
+SED=`which sed`
+CUT=`which cut`
+WGET=`which wget`
+SHA1SUM=`which sha1sum`
+TAR=`which tar`
+UNZIP=`which unzip`
+
+if [ "$SED" == "" ] || [ "$CUT" == "" ] || [ "$BASENAME" == "" ] ||
+ [ "$WGET" == "" ] || [ "$SHA1SUM" == "" ] || [ "$TAR" == "" ] ||
+ [ "$UNZIP" == "" ] || [ "$REALPATH" == "" ]; then
+ echo
+ echo ERROR: One or more required utilities were not found. Use Cygwin\'s setup.exe to
+ echo install all packages listed above.
+ exit 1
+fi
+
+# determining paths
+
+PIDGIN_BASE="`pwd`/../.."
+WIN32DEV_BASE="${PIDGIN_BASE}/../win32-dev"
+WIN32DEV_STORE="${PIDGIN_BASE}/../win32-dev-store"
+
+if [ ! -e "${PIDGIN_BASE}/ChangeLog" ]; then
+ echo "Pidgin base directory not found"
+ exit 1
+fi
+
+if [ -e "$WIN32DEV_BASE" ] && [ $DEBUG_SKIP_INSTALL == 0 ]; then
+ echo "win32-dev directory ($(readlink -f $WIN32DEV_BASE)) exists, please remove it before proceeding"
+ exit 1
+fi
+
+mkdir -p "$WIN32DEV_STORE" || exit 1
+path_real "$PIDGIN_BASE"
+PIDGIN_BASE="$path_ret"
+path_real "$WIN32DEV_STORE"
+WIN32DEV_STORE="$path_ret"
+
+cat "$PIDGIN_BASE/share/ca-certs"/*.pem > "$WIN32DEV_STORE/cacert.pem"
+
+# checking for Bonjour SDK
+
+# User may choose Bonjour SDK installation during the setup process and it may
+# be read from registry using one of the following GUIDs:
+# 5CA28B3B1DEA7654999C464610C010EB 2EA34582882FE334694F0BCD7D8DE336.
+# Despite this fact, the SDK is installed into hardcoded directory anyway:
+# "C:\Program Files\Bonjour SDK" (for 32bit and 64bit the path is the same).
+if [ ! -d "$BONJOUR_SDK_DIR" ]; then
+ echo "Bonjour SDK for Windows v3.0/v2.0.4 is not installed, please do it."
+ echo "You can download this SDK from https://developer.apple.com/bonjour/"
+ echo "(Apple ID may be required)."
+ exit 1;
+fi
+
+# checking for Perl
+
+reg_get_install_path "$ACTIVEPERL_GUID_PACKED"
+ACTIVEPERL_DIR=$reg_ret
+PERL_DIR=""
+if [ "$ACTIVEPERL_DIR" != "" ]; then
+ PERL_DIR="$ACTIVEPERL_DIR/bin"
+else
+ PERL_DIR="$PERL_DIR_FALLBACK"
+fi
+
+if ! ${REALPATH} -e "${PERL_DIR}/perl" &> /dev/null ; then
+ echo "Perl not found in \"${PERL_DIR}\", please install it."
+ exit 1
+fi
+
+# checking for NSIS
+
+reg_get_path "${NSIS_DIR_REGKEY}"
+NSIS_DIR=$reg_ret
+if [ "${NSIS_DIR}" == "" ]; then
+ echo "NSIS not found, please install it."
+ exit 1
+fi
+
+if ! ${REALPATH} -e "${NSIS_DIR}/Plugins/nsisunz.dll" &> /dev/null ; then
+ echo "NSIS plugin \"nsisunz.dll\" not found in \"${NSIS_DIR}/Plugins\", please install it."
+ exit 1
+fi
+
+if ! ${REALPATH} -e "${NSIS_DIR}/Plugins/SHA1Plugin.dll" &> /dev/null ; then
+ echo "NSIS plugin \"SHA1Plugin.dll\" not found in \"${NSIS_DIR}/Plugins\", please install it."
+ exit 1
+fi
+
+# downloading archives
+if [ $DEBUG_SKIP_DOWNLOADING == 0 ]; then
+echo "Downloading and verifying archives..."
+for ARCHIVE in $ARCHIVES ; do
+ ARCHIVE=${!ARCHIVE}
+ download_archive "$ARCHIVE"
+done
+fi
+
+echo "Composing workspace..."
+if [ $DEBUG_SKIP_INSTALL == 0 ]; then
+mkdir "$WIN32DEV_BASE" || exit 1
+fi
+path_real "$WIN32DEV_BASE"
+WIN32DEV_BASE="$path_ret"
+
+if [ $DEBUG_SKIP_INSTALL == 0 ]; then
+echo "Installing Bonjour SDK..."
+mkdir "$WIN32DEV_BASE/bonjour-sdk"
+cp -r "${BONJOUR_SDK_DIR}"/* "$WIN32DEV_BASE/bonjour-sdk/"
+
+for ARCHIVE in $ARCHIVES ; do
+ ARCHIVE=${!ARCHIVE}
+ extract_archive "$ARCHIVE"
+done
+fi
+
+echo "Removing bsdcpio..."
+rm -rf "${WIN32DEV_BASE}/bsdcpio"
+
+echo "Checking PATH..."
+path_changed=0
+check_path "gcc" "${WIN32DEV_BASE}/mingw/bin/gcc" || path_changed=1
+check_path "perl" "${PERL_DIR}/perl" || path_changed=1
+check_path "makensis" "${NSIS_DIR}/makensis" || path_changed=1
+if [ $path_changed == 1 ]; then
+ echo "PATH changed - executing sub-shell"
+ bash
+ echo "This session uses outdated PATH variable - please exit"
+ exit 1
+fi
+
+echo "Done."
diff --git a/pidgin/win32/untar.c b/pidgin/win32/untar.c
index db1fa1bd4e..e2a7978905 100644
--- a/pidgin/win32/untar.c
+++ b/pidgin/win32/untar.c
@@ -123,7 +123,7 @@ static FILE *infp = NULL; /* input byte stream */
static FILE *outfp = NULL; /* output stream, for file currently being extracted */
static Ulong_t outsize = 0; /* number of bytes remainin in file currently being extracted */
static char **only = NULL; /* array of filenames to extract/list */
-static int nonlys = 0; /* number of filenames in "only" array; 0=extract all */
+static guint nonlys = 0; /* number of filenames in "only" array; 0=extract all */
static int didabs = 0; /* were any filenames affected by the absence of -p? */
static untar_opt untarops = 0; /* Untar options */
@@ -257,7 +257,7 @@ static void cvtwrite(blk, size, fp)
Ulong_t size; /* number of characters to be written */
FILE *fp; /* file to write to */
{
- int i, j;
+ Ulong_t i, j;
static Uchar_t mod[TSIZE];
if (CONVERT)
@@ -327,7 +327,7 @@ static int untar_block(Uchar_t *blk) {
static char *name,*n2;/* prefix and name, combined */
static int first = 1;/* Boolean: first block of archive? */
long sum; /* checksum for this block */
- int i;
+ guint i;
tar_t tblk[1];
#ifdef _POSIX_SOURCE
diff --git a/pidgin/win32/untar.h b/pidgin/win32/untar.h
index 10e88976f1..54ad9ca007 100644
--- a/pidgin/win32/untar.h
+++ b/pidgin/win32/untar.h
@@ -12,7 +12,7 @@
extern "C" {
#endif /* __cplusplus */
-typedef enum _untar_opt {
+typedef enum {
UNTAR_LISTING = (1 << 0),
UNTAR_QUIET = (1 << 1),
UNTAR_VERBOSE = (1 << 2),
diff --git a/pidgin/win32/winpidgin.c b/pidgin/win32/winpidgin.c
index 4ad9ae9be6..71d6632ac5 100644
--- a/pidgin/win32/winpidgin.c
+++ b/pidgin/win32/winpidgin.c
@@ -25,20 +25,20 @@
*
*/
-/* This is for ATTACH_PARENT_PROCESS */
-#ifndef _WIN32_WINNT
-#define _WIN32_WINNT 0x501
-#endif
+#include "config.h"
+
#include <windows.h>
+#include <shellapi.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
-#include "config.h"
+#ifndef IS_WIN32_CROSS_COMPILED
typedef int (__cdecl* LPFNPIDGINMAIN)(HINSTANCE, int, char**);
+#endif
typedef void (WINAPI* LPFNSETDLLDIRECTORY)(LPCWSTR);
typedef BOOL (WINAPI* LPFNATTACHCONSOLE)(DWORD);
typedef BOOL (WINAPI* LPFNSETPROCESSDEPPOLICY)(DWORD);
@@ -48,7 +48,11 @@ static BOOL portable_mode = FALSE;
/*
* PROTOTYPES
*/
+#ifdef IS_WIN32_CROSS_COMPILED
+int __cdecl pidgin_main(HINSTANCE hint, int argc, char *argv[]);
+#else
static LPFNPIDGINMAIN pidgin_main = NULL;
+#endif
static LPFNSETDLLDIRECTORY MySetDllDirectory = NULL;
static const wchar_t *get_win32_error_message(DWORD err) {
@@ -63,6 +67,44 @@ static const wchar_t *get_win32_error_message(DWORD err) {
return err_msg;
}
+static BOOL reg_value_exists(HKEY key, wchar_t *sub_key, wchar_t *val_name) {
+ HKEY hkey;
+ LONG retv;
+ DWORD index;
+ wchar_t name_buffer[100];
+ BOOL exists = FALSE;
+
+ if (sub_key == NULL || val_name == NULL)
+ return FALSE;
+
+ retv = RegOpenKeyExW(key, sub_key, 0, KEY_ENUMERATE_SUB_KEYS, &hkey);
+ if (retv != ERROR_SUCCESS)
+ return FALSE;
+
+ if (val_name[0] == L'\0') {
+ RegCloseKey(hkey);
+ return TRUE;
+ }
+
+ index = 0;
+ while (TRUE)
+ {
+ DWORD name_size = sizeof(name_buffer);
+ retv = RegEnumValueW(hkey, index++, name_buffer, &name_size,
+ NULL, NULL, NULL, NULL);
+ if (retv != ERROR_SUCCESS)
+ break;
+ name_size /= sizeof(wchar_t);
+ if (wcsncmp(name_buffer, val_name, name_size) == 0) {
+ exists = TRUE;
+ break;
+ }
+ }
+
+ RegCloseKey(hkey);
+ return exists;
+}
+
static BOOL read_reg_string(HKEY key, wchar_t *sub_key, wchar_t *val_name, LPBYTE data, LPDWORD data_len) {
HKEY hkey;
BOOL ret = FALSE;
@@ -95,9 +137,7 @@ static BOOL read_reg_string(HKEY key, wchar_t *sub_key, wchar_t *val_name, LPBYT
return ret;
}
-static BOOL common_dll_prep(const wchar_t *path) {
- HMODULE hmod;
- HKEY hkey;
+static BOOL check_for_gtk(const wchar_t *path) {
struct _stat stat_buf;
wchar_t test_path[MAX_PATH + 1];
@@ -105,15 +145,79 @@ static BOOL common_dll_prep(const wchar_t *path) {
L"%s\\libgtk-win32-2.0-0.dll", path);
test_path[sizeof(test_path) / sizeof(wchar_t) - 1] = L'\0';
- if (_wstat(test_path, &stat_buf) != 0) {
- printf("Unable to determine GTK+ path. \n"
- "Assuming GTK+ is in the PATH.\n");
- return FALSE;
- }
+ return (_wstat(test_path, &stat_buf) == 0);
+}
+
+static void common_dll_prep(const wchar_t *path) {
+ HMODULE hmod;
+ HKEY hkey;
+ wchar_t alt_path_buff[MAX_PATH + 1];
+ wchar_t tmp_path[MAX_PATH + 1];
+ /* Hold strlen("FS_PLUGIN_PATH=" or "GST_PLUGIN_SYSTEM_PATH") +
+ * MAX_PATH + 1
+ */
+ wchar_t set_path[MAX_PATH + 24];
+ wchar_t *fslash, *bslash;
+
+ if (!check_for_gtk(path)) {
+ const wchar_t *winpath = _wgetenv(L"PATH");
+ wchar_t *delim;
+
+ if (winpath == NULL) {
+ printf("Unable to determine GTK+ path (and PATH is not set).\n");
+ exit(-1);
+ }
+
+ path = NULL;
+ do
+ {
+ wcsncpy(alt_path_buff, winpath, MAX_PATH);
+ alt_path_buff[MAX_PATH] = L'\0';
+ delim = wcschr(alt_path_buff, L';');
+ if (delim != NULL) {
+ delim[0] = L'\0';
+ winpath = wcschr(winpath, L';') + 1;
+ }
+ if (check_for_gtk(alt_path_buff)) {
+ path = alt_path_buff;
+ break;
+ }
+ }
+ while (delim != NULL);
+ if (path == NULL) {
+ printf("Unable to determine GTK+ path.\n");
+ exit(-1);
+ }
+ }
wprintf(L"GTK+ path found: %s\n", path);
+ wcsncpy(tmp_path, path, MAX_PATH);
+ tmp_path[MAX_PATH] = L'\0';
+ bslash = wcsrchr(tmp_path, L'\\');
+ fslash = wcsrchr(tmp_path, L'/');
+ if (bslash && bslash > fslash)
+ bslash[0] = L'\0';
+ else if (fslash && fslash > bslash)
+ fslash[0] = L'\0';
+ /* tmp_path now contains \path\to\Pidgin\Gtk */
+
+ _snwprintf(set_path, sizeof(set_path) / sizeof(wchar_t),
+ L"FS_PLUGIN_PATH=%s\\lib\\farstream-0.1", tmp_path);
+ set_path[sizeof(set_path) / sizeof(wchar_t) - 1] = L'\0';
+ _wputenv(set_path);
+
+ _snwprintf(set_path, sizeof(set_path) / sizeof(wchar_t),
+ L"GST_PLUGIN_SYSTEM_PATH=%s\\lib\\gstreamer-0.10", tmp_path);
+ set_path[sizeof(set_path) / sizeof(wchar_t) - 1] = L'\0';
+ _wputenv(set_path);
+
+ _snwprintf(set_path, sizeof(set_path) / sizeof(wchar_t),
+ L"GST_PLUGIN_PATH=%s\\lib\\gstreamer-0.10", tmp_path);
+ set_path[sizeof(set_path) / sizeof(wchar_t) - 1] = L'\0';
+ _wputenv(set_path);
+
if ((hmod = GetModuleHandleW(L"kernel32.dll"))) {
MySetDllDirectory = (LPFNSETDLLDIRECTORY) GetProcAddress(
hmod, "SetDllDirectoryW");
@@ -124,7 +228,6 @@ static BOOL common_dll_prep(const wchar_t *path) {
/* For Windows XP SP1+ / Server 2003 we use SetDllDirectory to avoid dll hell */
if (MySetDllDirectory) {
- printf("Using SetDllDirectory\n");
MySetDllDirectory(path);
}
@@ -182,11 +285,10 @@ static BOOL common_dll_prep(const wchar_t *path) {
printf("SafeDllSearchMode is set to 0\n");
}/*end else*/
}
-
- return TRUE;
}
-static BOOL dll_prep(const wchar_t *pidgin_dir) {
+#ifndef IS_WIN32_CROSS_COMPILED
+static void dll_prep(const wchar_t *pidgin_dir) {
wchar_t path[MAX_PATH + 1];
path[0] = L'\0';
@@ -195,8 +297,9 @@ static BOOL dll_prep(const wchar_t *pidgin_dir) {
path[sizeof(path) / sizeof(wchar_t) - 1] = L'\0';
}
- return common_dll_prep(path);
+ common_dll_prep(path);
}
+#endif
static void portable_mode_dll_prep(const wchar_t *pidgin_dir) {
/* need to be able to fit MAX_PATH + "PURPLEHOME=" in path2 */
@@ -217,8 +320,8 @@ static void portable_mode_dll_prep(const wchar_t *pidgin_dir) {
path[cnt] = L'\0';
} else {
printf("Unable to determine current executable path. \n"
- "This will prevent the settings dir from being set.\n"
- "Assuming GTK+ is in the PATH.\n");
+ "This will prevent the settings dir from being set.\n");
+ common_dll_prep(L'\0');
return;
}
@@ -232,7 +335,12 @@ static void portable_mode_dll_prep(const wchar_t *pidgin_dir) {
wprintf(L"Setting settings dir: %s\n", path2);
_wputenv(path2);
- if (!dll_prep(pidgin_dir)) {
+ _snwprintf(path2, sizeof(path2) / sizeof(wchar_t), L"%s\\Gtk\\bin",
+ pidgin_dir);
+ path2[sizeof(path2) / sizeof(wchar_t) - 1] = L'\0';
+ if (check_for_gtk(path2))
+ common_dll_prep(path2);
+ else {
/* set the GTK+ path to be \\path\to\GTK\bin */
wcscat(path, L"\\GTK\\bin");
common_dll_prep(path);
@@ -431,7 +539,8 @@ static void winpidgin_add_stuff_to_path() {
printf("%s", "Looking for Perl... ");
plen = sizeof(perl_path) / sizeof(wchar_t);
- if (read_reg_string(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Perl", L"",
+ if (reg_value_exists(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Perl", L"") &&
+ read_reg_string(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Perl", L"",
(LPBYTE) &perl_path, &plen)) {
/* We *could* check for perl510.dll, but it seems unnecessary. */
wprintf(L"found in '%s'.\n", perl_path);
@@ -447,7 +556,8 @@ static void winpidgin_add_stuff_to_path() {
printf("%s", "Looking for MIT Kerberos... ");
plen = sizeof(mit_kerberos_path) / sizeof(wchar_t);
- if (read_reg_string(HKEY_LOCAL_MACHINE, L"SOFTWARE\\MIT\\Kerberos", L"InstallDir",
+ if (reg_value_exists(HKEY_LOCAL_MACHINE, L"SOFTWARE\\MIT\\Kerberos", L"InstallDir") &&
+ read_reg_string(HKEY_LOCAL_MACHINE, L"SOFTWARE\\MIT\\Kerberos", L"InstallDir",
(LPBYTE) &mit_kerberos_path, &plen)) {
/* We *could* check for gssapi32.dll */
wprintf(L"found in '%s'.\n", mit_kerberos_path);
@@ -764,8 +874,10 @@ WinMain (struct HINSTANCE__ *hInstance, struct HINSTANCE__ *hPrevInstance,
if (portable_mode)
portable_mode_dll_prep(pidgin_dir);
+#ifndef IS_WIN32_CROSS_COMPILED
else if (!getenv("PIDGIN_NO_DLL_CHECK"))
dll_prep(pidgin_dir);
+#endif
winpidgin_set_locale();
@@ -776,15 +888,18 @@ WinMain (struct HINSTANCE__ *hInstance, struct HINSTANCE__ *hPrevInstance,
if (!winpidgin_set_running(getenv("PIDGIN_MULTI_INST") == NULL && !multiple))
return 0;
+#ifndef IS_WIN32_CROSS_COMPILED
/* Now we are ready for Pidgin .. */
wcscat(pidgin_dir, L"\\pidgin.dll");
if ((hmod = LoadLibraryW(pidgin_dir)))
pidgin_main = (LPFNPIDGINMAIN) GetProcAddress(hmod, "pidgin_main");
+#endif
/* Restore pidgin_dir to point to where the executable is */
if (pidgin_dir_start)
pidgin_dir_start[0] = L'\0';
+#ifndef IS_WIN32_CROSS_COMPILED
if (!pidgin_main) {
DWORD dw = GetLastError();
BOOL mod_not_found = (dw == ERROR_MOD_NOT_FOUND || dw == ERROR_DLL_NOT_FOUND);
@@ -799,6 +914,7 @@ WinMain (struct HINSTANCE__ *hInstance, struct HINSTANCE__ *hPrevInstance,
return 0;
}
+#endif
/* Convert argv to utf-8*/
szArglist = CommandLineToArgvW(cmdLine, &j);
diff --git a/pidgin/win32/wspell.c b/pidgin/win32/wspell.c
deleted file mode 100644
index 317c67eab7..0000000000
--- a/pidgin/win32/wspell.c
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * pidgin
- *
- * File: wspell.c
- * Date: March, 2003
- * Description: Windows Pidgin gtkspell interface.
- *
- * Copyright (C) 2002-2003, Herman Bloggs <hermanator12002@yahoo.com>
- *
- * 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
-#ifdef USE_GTKSPELL
-#include <windows.h>
-#include <string.h>
-#include <stdlib.h>
-#include <glib.h>
-#include <gtk/gtk.h>
-#include <gtkspell/gtkspell.h>
-#include "debug.h"
-#include "win32dep.h"
-#include "wspell.h"
-
-/* Intermediate function so that we can eat Enchant error popups when it doesn't find a DLL
- * This is fixed upstream, but not released */
-GtkSpell* (*wpidginspell_new_attach_proxy) (GtkTextView *,
- const gchar *,
- GError **) = NULL;
-
-/* GTKSPELL DUMMY FUNCS */
-static GtkSpell* wgtkspell_new_attach(GtkTextView *view, const gchar *lang, GError **error) {
- GtkSpell *ret = NULL;
- if (wpidginspell_new_attach_proxy) {
- UINT old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
- ret = wpidginspell_new_attach_proxy(view, lang, error);
- SetErrorMode(old_error_mode);
- }
- return ret;
-}
-static GtkSpell* wgtkspell_get_from_text_view(GtkTextView *view) {return NULL;}
-static void wgtkspell_detach(GtkSpell *spell) {}
-static gboolean wgtkspell_set_language(GtkSpell *spell, const gchar *lang, GError **error) {return FALSE;}
-static void wgtkspell_recheck_all(GtkSpell *spell) {}
-
-/* GTKSPELL PROTOS */
-GtkSpell* (*wpidginspell_new_attach) (GtkTextView *,
- const gchar *,
- GError **) = wgtkspell_new_attach;
-
-GtkSpell* (*wpidginspell_get_from_text_view) (GtkTextView*) = wgtkspell_get_from_text_view;
-
-void (*wpidginspell_detach) (GtkSpell*) = wgtkspell_detach;
-
-gboolean (*wpidginspell_set_language) (GtkSpell*,
- const gchar*,
- GError**) = wgtkspell_set_language;
-
-void (*wpidginspell_recheck_all) (GtkSpell*) = wgtkspell_recheck_all;
-
-#define GTKSPELL_DLL "libgtkspell-0.dll"
-
-static void load_gtkspell() {
- UINT old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
- gchar *tmp, *tmp2;
-
- const char *path = g_getenv("PATH");
- tmp = g_build_filename(wpurple_install_dir(), "spellcheck", NULL);
- tmp2 = g_strdup_printf("%s%s%s", tmp,
- (path ? G_SEARCHPATH_SEPARATOR_S : ""),
- (path ? path : ""));
- g_free(tmp);
- g_setenv("PATH", tmp2, TRUE);
- g_free(tmp2);
-
- tmp = g_build_filename(wpurple_install_dir(), "spellcheck", GTKSPELL_DLL, NULL);
- /* Suppress error popups */
- wpidginspell_new_attach_proxy = (void*) wpurple_find_and_loadproc(tmp, "gtkspell_new_attach" );
- if (wpidginspell_new_attach_proxy) {
- wpidginspell_get_from_text_view = (void*) wpurple_find_and_loadproc(tmp, "gtkspell_get_from_text_view");
- wpidginspell_detach = (void*) wpurple_find_and_loadproc(tmp, "gtkspell_detach");
- wpidginspell_set_language = (void*) wpurple_find_and_loadproc(tmp, "gtkspell_set_language");
- wpidginspell_recheck_all = (void*) wpurple_find_and_loadproc(tmp, "gtkspell_recheck_all");
- } else {
- purple_debug_warning("wspell", "Couldn't load gtkspell (%s) \n", tmp);
- /*wpidginspell_new_attach = wgtkspell_new_attach;*/
- }
- g_free(tmp);
- SetErrorMode(old_error_mode);
-}
-
-void winpidgin_spell_init() {
- load_gtkspell();
-}
-#endif
diff --git a/pidgin/win32/wspell.h b/pidgin/win32/wspell.h
deleted file mode 100644
index b69552934b..0000000000
--- a/pidgin/win32/wspell.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * pidgin
- *
- * File: wspell.h
- *
- * Copyright (C) 2002-2003, Herman Bloggs <hermanator12002@yahoo.com>
- *
- * 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 _WSPELL_H_
-#define _WSPELL_H_
-#include <gtkspell/gtkspell.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-void winpidgin_spell_init(void);
-
-extern GtkSpell* (*wpidginspell_new_attach)(GtkTextView*, const gchar*, GError**);
-#define gtkspell_new_attach( view, lang, error ) \
-wpidginspell_new_attach( view, lang, error )
-
-extern GtkSpell* (*wpidginspell_get_from_text_view)(GtkTextView*);
-#define gtkspell_get_from_text_view( view ) \
-wpidginspell_get_from_text_view( view )
-
-extern void (*wpidginspell_detach)(GtkSpell*);
-#define gtkspell_detach( spell ) \
-wpidginspell_detach( spell )
-
-extern gboolean (*wpidginspell_set_language)(GtkSpell*, const gchar*, GError**);
-#define gtkspell_set_language( spell, lang, error ) \
-wpidginspell_set_language( spell, lang, error )
-
-extern void (*wpidginspell_recheck_all)(GtkSpell*);
-#define gtkspell_recheck_all( spell ) \
-wpidginspell_recheck_all( spell )
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif /* _WSPELL_H_ */
diff --git a/po/Makefile.mingw b/po/Makefile.mingw
index 2ffada376b..ed67ec02b7 100644
--- a/po/Makefile.mingw
+++ b/po/Makefile.mingw
@@ -22,7 +22,8 @@ CATALOGS = $(patsubst %.po,%.gmo,$(wildcard *.po))
##
.po.gmo:
- rm -f $@ && $(GMSGFMT) --statistics -o $@ $<
+ @echo -e " GEN\t$@"
+ @rm -f $@ && $(GMSGFMT) --statistics -o $@ $<
##
## TARGETS
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 9bb9ef78a7..0d5875ad42 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -6,7 +6,6 @@ finch/gntcertmgr.c
finch/gntconn.c
finch/gntconv.c
finch/gntdebug.c
-finch/gntft.c
finch/gntlog.c
finch/gntmedia.c
finch/gntnotify.c
@@ -18,6 +17,8 @@ finch/gntroomlist.c
finch/gntsound.c
finch/gntstatus.c
finch/gntui.c
+finch/gntxfer.c
+finch/libfinch.c
finch/libgnt/gntbox.c
finch/libgnt/gntbutton.c
finch/libgnt/gntcheckbox.c
@@ -39,18 +40,22 @@ finch/plugins/gnttinyurl.c
finch/plugins/grouping.c
finch/plugins/lastlog.c
libpurple/account.c
-libpurple/blist.c
+libpurple/accounts.c
+libpurple/buddylist.c
libpurple/certificate.c
libpurple/connection.c
libpurple/conversation.c
+libpurple/conversationtypes.c
libpurple/dbus-server.c
libpurple/dbus-server.h
libpurple/desktopitem.c
libpurple/dnsquery.c
-libpurple/ft.c
libpurple/gconf/purple.schemas.in
+libpurple/http.c
+libpurple/keyring.c
libpurple/log.c
libpurple/media/backend-fs2.c
+libpurple/message.c
libpurple/plugin.c
libpurple/plugins/autoaccept.c
libpurple/plugins/buddynote.c
@@ -61,9 +66,13 @@ libpurple/plugins/idle.c
libpurple/plugins/ipc-test-client.c
libpurple/plugins/ipc-test-server.c
libpurple/plugins/joinpart.c
+libpurple/plugins/keyrings/gnomekeyring.c
+libpurple/plugins/keyrings/internalkeyring.c
+libpurple/plugins/keyrings/kwallet.cpp
+libpurple/plugins/keyrings/secretservice.c
+libpurple/plugins/keyrings/wincred.c
libpurple/plugins/log_reader.c
libpurple/plugins/mono/loader/mono.c
-libpurple/plugins/newline.c
libpurple/plugins/offlinemsg.c
libpurple/plugins/one_time_password.c
libpurple/plugins/perl/perl.c
@@ -75,11 +84,23 @@ libpurple/plugins/ssl/ssl-nss.c
libpurple/plugins/ssl/ssl.c
libpurple/plugins/statenotify.c
libpurple/plugins/tcl/tcl.c
+libpurple/presence.c
libpurple/protocols/bonjour/bonjour.c
libpurple/protocols/bonjour/bonjour.h
libpurple/protocols/bonjour/jabber.c
libpurple/protocols/bonjour/mdns_win32.c
+libpurple/protocols/gg/account.c
+libpurple/protocols/gg/chat.c
+libpurple/protocols/gg/deprecated.c
+libpurple/protocols/gg/edisc.c
libpurple/protocols/gg/gg.c
+libpurple/protocols/gg/image-prpl.c
+libpurple/protocols/gg/message-prpl.c
+libpurple/protocols/gg/multilogon.c
+libpurple/protocols/gg/pubdir-prpl.c
+libpurple/protocols/gg/purplew.c
+libpurple/protocols/gg/status.c
+libpurple/protocols/gg/validator.c
libpurple/protocols/irc/cmds.c
libpurple/protocols/irc/dcc_send.c
libpurple/protocols/irc/irc.c
@@ -97,6 +118,8 @@ libpurple/protocols/jabber/chat.c
libpurple/protocols/jabber/facebook_roster.c
libpurple/protocols/jabber/jabber.c
libpurple/protocols/jabber/jutil.c
+libpurple/protocols/jabber/libfacebook.c
+libpurple/protocols/jabber/libgtalk.c
libpurple/protocols/jabber/libxmpp.c
libpurple/protocols/jabber/message.c
libpurple/protocols/jabber/parser.c
@@ -123,7 +146,6 @@ libpurple/protocols/msn/switchboard.c
libpurple/protocols/msn/userlist.c
libpurple/protocols/mxit/actions.c
libpurple/protocols/mxit/filexfer.c
-libpurple/protocols/mxit/http.c
libpurple/protocols/mxit/login.c
libpurple/protocols/mxit/multimx.c
libpurple/protocols/mxit/mxit.c
@@ -131,15 +153,14 @@ libpurple/protocols/mxit/profile.c
libpurple/protocols/mxit/protocol.c
libpurple/protocols/mxit/roster.c
libpurple/protocols/mxit/splashscreen.c
-libpurple/protocols/myspace/myspace.c
-libpurple/protocols/myspace/user.c
-libpurple/protocols/myspace/zap.c
libpurple/protocols/novell/nmuser.c
libpurple/protocols/novell/novell.c
libpurple/protocols/oscar/authorization.c
libpurple/protocols/oscar/clientlogin.c
libpurple/protocols/oscar/encoding.c
+libpurple/protocols/oscar/family_auth.c
libpurple/protocols/oscar/family_chatnav.c
+libpurple/protocols/oscar/family_feedbag.c
libpurple/protocols/oscar/family_icbm.c
libpurple/protocols/oscar/family_locate.c
libpurple/protocols/oscar/flap_connection.c
@@ -161,14 +182,6 @@ libpurple/protocols/silc/pk.c
libpurple/protocols/silc/silc.c
libpurple/protocols/silc/util.c
libpurple/protocols/silc/wb.c
-libpurple/protocols/silc10/buddy.c
-libpurple/protocols/silc10/chat.c
-libpurple/protocols/silc10/ft.c
-libpurple/protocols/silc10/ops.c
-libpurple/protocols/silc10/pk.c
-libpurple/protocols/silc10/silc.c
-libpurple/protocols/silc10/util.c
-libpurple/protocols/silc10/wb.c
libpurple/protocols/simple/simple.c
libpurple/protocols/yahoo/libyahoo.c
libpurple/protocols/yahoo/libyahoojp.c
@@ -183,6 +196,8 @@ libpurple/protocols/yahoo/ycht.c
libpurple/protocols/zephyr/zephyr.c
libpurple/proxy.c
libpurple/prpl.c
+libpurple/purple-socket.c
+libpurple/request.c
libpurple/request.h
libpurple/savedstatuses.c
libpurple/server.c
@@ -191,6 +206,7 @@ libpurple/sslconn.c
libpurple/status.c
libpurple/util.c
libpurple/win32/libc_interface.c
+libpurple/xfer.c
libpurple/xmlnode.c
pidgin.desktop.in
pidgin/gtkaccount.c
@@ -202,11 +218,7 @@ pidgin/gtkconv.c
pidgin/gtkdebug.c
pidgin/gtkdialogs.c
pidgin/gtkdocklet.c
-pidgin/gtkft.c
-pidgin/gtkimhtml.c
-pidgin/gtkimhtmltoolbar.c
pidgin/gtklog.c
-pidgin/gtkmain.c
pidgin/gtkmedia.c
pidgin/gtknotify.c
pidgin/gtkplugin.c
@@ -216,20 +228,22 @@ pidgin/gtkprivacy.c
pidgin/gtkrequest.c
pidgin/gtkroomlist.c
pidgin/gtksavedstatuses.c
-pidgin/gtksmiley.c
+pidgin/gtksmiley-manager.c
pidgin/gtksound.c
pidgin/gtkstatusbox.c
pidgin/gtkutils.c
+pidgin/gtkwebview.c
+pidgin/gtkwebviewtoolbar.c
pidgin/gtkwhiteboard.c
+pidgin/gtkxfer.c
+pidgin/libpidgin.c
pidgin/pidgin.h
pidgin/pidginstock.c
pidgin/pidgintooltip.c
pidgin/pixmaps/emotes/default/24/default.theme.in
-pidgin/pixmaps/emotes/none/none.theme.in
pidgin/pixmaps/emotes/small/16/small.theme.in
pidgin/plugins/cap/cap.c
pidgin/plugins/contact_priority.c
-pidgin/plugins/convcolors.c
pidgin/plugins/disco/gtkdisco.c
pidgin/plugins/disco/xmppdisco.c
pidgin/plugins/extplacement.c
@@ -244,23 +258,22 @@ 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/pidginrc.c
pidgin/plugins/raw.c
pidgin/plugins/relnot.c
+pidgin/plugins/screencap.c
pidgin/plugins/sendbutton.c
pidgin/plugins/spellchk.c
pidgin/plugins/themeedit-icon.c
pidgin/plugins/themeedit.c
pidgin/plugins/ticker/ticker.c
-pidgin/plugins/timestamp.c
-pidgin/plugins/timestamp_format.c
pidgin/plugins/unity.c
-pidgin/plugins/vvconfig.c
+pidgin/plugins/webkit.c
pidgin/plugins/win32/transparency/win2ktrans.c
pidgin/plugins/win32/winprefs/winprefs.c
pidgin/plugins/xmppconsole.c
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index 2a9ea38f41..efbf7bbfca 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -11,7 +11,6 @@ libpurple/plugins/perl/common/Core.c
libpurple/plugins/perl/common/Debug.c
libpurple/plugins/perl/common/FT.c
libpurple/plugins/perl/common/Idle.c
-libpurple/plugins/perl/common/ImgStore.c
libpurple/plugins/perl/common/Log.c
libpurple/plugins/perl/common/Network.c
libpurple/plugins/perl/common/Notify.c
@@ -19,6 +18,7 @@ libpurple/plugins/perl/common/Plugin.c
libpurple/plugins/perl/common/PluginPref.c
libpurple/plugins/perl/common/Pounce.c
libpurple/plugins/perl/common/Prefs.c
+libpurple/plugins/perl/common/Presence.c
libpurple/plugins/perl/common/Privacy.c
libpurple/plugins/perl/common/Proxy.c
libpurple/plugins/perl/common/Prpl.c
@@ -35,6 +35,7 @@ libpurple/plugins/perl/common/Status.c
libpurple/plugins/perl/common/Stringref.c
libpurple/plugins/perl/common/Util.c
libpurple/plugins/perl/common/Whiteboard.c
+libpurple/plugins/perl/common/Xfer.c
libpurple/plugins/perl/common/XMLNode.c
libpurple/protocols/null/nullprpl.c
pidgin.desktop.in.in
diff --git a/po/da.po b/po/da.po
index 4fdd3042d2..c2a4421408 100644
--- a/po/da.po
+++ b/po/da.po
@@ -3,47 +3,48 @@
# This file is distributed under the same license as the Pidgin package.
# Morten Brix Pedersen <morten@wtf.dk>, 2003-2006
# Peter Bach <bach.peter@gmail.com>, 2007-2009
-# Nicky Thomassen <nicky@aptget.dk>, 2011-2013
# Ask Hjorth Larsen <asklarsen@gmail.com>, 2012
+# Nicky Thomassen <nicky@aptget.dk>, 2011-2014
#
# Konventioner:
# Buddy list, Contact list = venneliste
-# Buddy pounce = logind-praj
-# Away = fravær eller fraværende
-# Online = tilkoblet
-# Offline = (almindeligvis) afkoblet
+# Buddy pounce = logind-praj
+# Away = fravær eller fraværende
+# Online = tilkoblet
+# Offline = (almindeligvis) afkoblet
# Offline message = offline-besked
-# (Ikke særlig pænt, men f.eks. "afkoblet besked" lyder som om det er
-# beskeden, der er afkoblet, og er derfor svært at forstå)
-# Join = tilslutte
-# IM / IMs = besked / beskeder
-# Mouse Gesture = Musebevægelse
-# Friendly Name = synligt navn
-# Tray Icon = Statusikon
-# Ban = Udvis
-# Signature = underskrift
-# Link = henvisning
-# web = internet
-# token = nøgle (netværkstermologi)
-# webcam = webkamera
-# mood = humør
-# smiley = humørikon
-# memo = huskeseddel
-# chat = samtale
-# alias = alias
-# nickname/nick = alias
-# display name = kaldenavn
-# buzz = bip
-# nudge = puf
-# idle = inaktiv
-# pager = personsøger
+# (Ikke særlig pænt, men fx "afkoblet besked" lyder som om, det er
+# beskeden, der er afkoblet, og er derfor svær at forstå).
+# Join = tilslutte
+# IM / IMs = (lyn)besked / (lyn)beskeder (alt efter kontekst)
+# Instant Message = lynbesked
+# Mouse Gesture = Musebevægelse
+# Friendly Name = synligt navn
+# Tray Icon = Statusikon
+# Ban = Udvis
+# Signature = underskrift
+# Link = henvisning
+# web = internet
+# token = nøgle (netværkstermologi)
+# webcam = webkamera
+# mood = humør
+# smiley = humørikon
+# memo = huskeseddel
+# chat = samtale
+# alias = alias
+# nickname/nick = alias
+# display name = kaldenavn
+# buzz = bip
+# nudge = puf
+# idle = inaktiv
+# pager = personsøger
#
msgid ""
msgstr ""
"Project-Id-Version: Pidgin 2.5.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-02-02 10:38-0800\n"
-"PO-Revision-Date: 2013-09-20 10:57+0100\n"
+"PO-Revision-Date: 2014-02-10 20:18+0100\n"
"Last-Translator: Nicky Thomassen <nicky@aptget.dk>\n"
"Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
"Language: da\n"
@@ -83,14 +84,8 @@ msgstr ""
" -v, --version vis nuværende version og afslut\n"
#, c-format
-msgid ""
-"%s encountered errors migrating your settings from %s to %s. Please "
-"investigate and complete the migration by hand. Please report this error at "
-"http://developer.pidgin.im"
-msgstr ""
-"%s opdagede fejl ved migrering af dine indstillinger fra %s til %s. Undersøg "
-"og færdiggør migreringen manuelt. Rapportér venligst denne fejl på http://"
-"developer.pidgin.im"
+msgid "%s encountered errors migrating your settings from %s to %s. Please investigate and complete the migration by hand. Please report this error at http://developer.pidgin.im"
+msgstr "%s opdagede fejl ved migrering af dine indstillinger fra %s til %s. Undersøg og færdiggør migreringen manuelt. Rapportér venligst denne fejl på http://developer.pidgin.im"
#. the user did not fill in the captcha
msgid "Error"
@@ -105,15 +100,11 @@ msgstr "Konto blev ikke tilføjet"
msgid "Username of an account must be non-empty."
msgstr "Brugernavnet på en konto må ikke være tomt."
-msgid ""
-"The account's protocol cannot be changed while it is connected to the server."
-msgstr ""
-"Kontoens protokol kan ikke ændres, så længe den er forbundet til serveren."
+msgid "The account's protocol cannot be changed while it is connected to the server."
+msgstr "Kontoens protokol kan ikke ændres, så længe den er forbundet til serveren."
-msgid ""
-"The account's username cannot be changed while it is connected to the server."
-msgstr ""
-"Kontoens brugernavn kan ikke ændres, så længe den er forbundet til serveren."
+msgid "The account's username cannot be changed while it is connected to the server."
+msgstr "Kontoens brugernavn kan ikke ændres, så længe den er forbundet til serveren."
msgid "New mail notifications"
msgstr "Påmindelse om ny post"
@@ -249,7 +240,7 @@ msgid "Alias (optional)"
msgstr "Alias (valgfrit)"
msgid "Invite message (optional)"
-msgstr "Inviteringsbesked (valgfri)"
+msgstr "Invitationsbesked (valgfri)"
msgid "Add in group"
msgstr "Føj til gruppe"
@@ -344,12 +335,10 @@ msgid "Enter empty string to reset the name."
msgstr "Indtast tom streng for at nulstille navnet."
msgid "Removing this contact will also remove all the buddies in the contact"
-msgstr ""
-"Ved at fjerne denne kontakt vil du også fjerne alle vennerne i denne kontakt"
+msgstr "Ved at fjerne denne kontakt vil du også fjerne alle vennerne i denne kontakt"
msgid "Removing this group will also remove all the buddies in the group"
-msgstr ""
-"Ved at fjerne denne gruppe vil du også fjerne alle vennerne i denne gruppe"
+msgstr "Ved at fjerne denne gruppe vil du også fjerne alle vennerne i denne gruppe"
#, c-format
msgid "Are you sure you want to remove %s?"
@@ -405,9 +394,7 @@ msgstr "Blokér"
msgid "Unblock"
msgstr "Frigiv"
-msgid ""
-"Please enter the username or alias of the person you would like to Block/"
-"Unblock."
+msgid "Please enter the username or alias of the person you would like to Block/Unblock."
msgstr "Indtast brugernavnet eller alias på personen, du vil blokere/frigive."
#. Not multiline
@@ -434,12 +421,8 @@ msgstr "Indtast navnet på den samtale du vil tilslutte."
msgid "Join"
msgstr "Tilslut"
-msgid ""
-"Please enter the username or alias of the person whose log you would like to "
-"view."
-msgstr ""
-"Indtast brugernavnet eller alias på den person, hvis log du gerne vil "
-"gennemse."
+msgid "Please enter the username or alias of the person whose log you would like to view."
+msgstr "Indtast brugernavnet eller alias på den person, hvis log du gerne vil gennemse."
#. Create the "Options" frame.
msgid "Options"
@@ -584,13 +567,11 @@ msgstr "%s afbrudt."
msgid ""
"%s\n"
"\n"
-"Finch will not attempt to reconnect the account until you correct the error "
-"and re-enable the account."
+"Finch will not attempt to reconnect the account until you correct the error and re-enable the account."
msgstr ""
"%s\n"
" \n"
-"Finch vil ikke forsøge at genforbinde til kontoen før du retter fejlen og "
-"genaktiverer kontoen."
+"Finch vil ikke forsøge at genforbinde til kontoen før du retter fejlen og genaktiverer kontoen."
msgid "Re-enable Account"
msgstr "Slå konto til igen"
@@ -599,8 +580,7 @@ msgid "No such command."
msgstr "Ingen kommando ved det navn."
msgid "Syntax Error: You typed the wrong number of arguments to that command."
-msgstr ""
-"Syntaksfejl: Du indtastede et forkert antal argumenter til den kommando."
+msgstr "Syntaksfejl: Du indtastede et forkert antal argumenter til den kommando."
msgid "Your command failed for an unknown reason."
msgstr "Din kommando mislykkedes af ukendt årsag."
@@ -636,20 +616,14 @@ msgstr ""
msgid "You have left this chat."
msgstr "Du har forladt denne samtale."
-msgid ""
-"The account has disconnected and you are no longer in this chat. You will be "
-"automatically rejoined in the chat when the account reconnects."
-msgstr ""
-"Kontoen har afbrudt dig og du er ikke længere i denne samtale. Du vil "
-"automatisk blive tilsluttet til samtalen igen når kontoen forbinder igen."
+msgid "The account has disconnected and you are no longer in this chat. You will be automatically rejoined in the chat when the account reconnects."
+msgstr "Kontoen har afbrudt dig og du er ikke længere i denne samtale. Du vil automatisk blive tilsluttet til samtalen igen når kontoen forbinder igen."
msgid "Logging started. Future messages in this conversation will be logged."
msgstr "Logning startet. Fremtidige beskeder i denne samtale vil blive logget."
-msgid ""
-"Logging stopped. Future messages in this conversation will not be logged."
-msgstr ""
-"Logning stoppet. Fremtidige beskeder i denne samtale vil ikke blive logget."
+msgid "Logging stopped. Future messages in this conversation will not be logged."
+msgstr "Logning stoppet. Fremtidige beskeder i denne samtale vil ikke blive logget."
msgid "Send To"
msgstr "Send til"
@@ -701,34 +675,21 @@ msgstr ""
"De følgende kommandoer er tilgængelige i denne sammenhæng:\n"
#, c-format
-msgid ""
-"%s is not a valid message class. See '/help msgcolor' for valid message "
-"classes."
-msgstr ""
-"%s er ikke en gyldig beskedklasse. Se \"/help msgcolor\" for gyldige "
-"beskedklasser."
+msgid "%s is not a valid message class. See '/help msgcolor' for valid message classes."
+msgstr "%s er ikke en gyldig beskedklasse. Se \"/help msgcolor\" for gyldige beskedklasser."
#, c-format
msgid "%s is not a valid color. See '/help msgcolor' for valid colors."
msgstr "%s er ikke gyldig farve. Se \"/help msgcolor\" for gyldige farver."
-msgid ""
-"say &lt;message&gt;: Send a message normally as if you weren't using a "
-"command."
-msgstr ""
-"say &lt;besked&gt;: Send en besked på normal vis, som om du ikke brugte en "
-"kommando."
+msgid "say &lt;message&gt;: Send a message normally as if you weren't using a command."
+msgstr "say &lt;besked&gt;: Send en besked på normal vis, som om du ikke brugte en kommando."
msgid "me &lt;action&gt;: Send an IRC style action to a buddy or chat."
-msgstr ""
-"me &lt;handling&gt;: Send en IRC-lignende handling til en ven eller samtale."
+msgstr "me &lt;handling&gt;: Send en IRC-lignende handling til en ven eller samtale."
-msgid ""
-"debug &lt;option&gt;: Send various debug information to the current "
-"conversation."
-msgstr ""
-"debug &lt;tilvalg&gt;: Send forskellige fejlsøgningsoplysninger til den "
-"nuværende samtale."
+msgid "debug &lt;option&gt;: Send various debug information to the current conversation."
+msgstr "debug &lt;tilvalg&gt;: Send forskellige fejlsøgningsoplysninger til den nuværende samtale."
msgid "clear: Clears the conversation scrollback."
msgstr "clear: Rydder samtalevinduet for tekst."
@@ -757,18 +718,8 @@ msgstr "prefs: Vis indstillingsvinduet."
msgid "statuses: Show the savedstatuses window."
msgstr "statuses: Vis vinduet med gemte statusser."
-msgid ""
-"msgcolor &lt;class&gt; &lt;foreground&gt; &lt;background&gt;: Set the color "
-"for different classes of messages in the conversation window.<br> &lt;"
-"class&gt;: receive, send, highlight, action, timestamp<br> &lt;foreground/"
-"background&gt;: black, red, green, blue, white, gray, darkgray, magenta, "
-"cyan, default<br><br>EXAMPLE:<br> msgcolor send cyan default"
-msgstr ""
-"msgcolor &lt;klasse&gt; &lt;forgrund&gt; &lt;baggrund;: Sæt farven for de "
-"forskellige klasser af beskeder i samtalevinduet.<br> &lt;klasse&gt;: "
-"receive, send, highlight, action, timestamp<br> &lt;forgrund/"
-"baggrund&gt;: black, red, green, blue, white, gray, darkgray, magenta, cyan, "
-"default<br><br>EKSEMPEL:<br> msgcolor send cyan default"
+msgid "msgcolor &lt;class&gt; &lt;foreground&gt; &lt;background&gt;: Set the color for different classes of messages in the conversation window.<br> &lt;class&gt;: receive, send, highlight, action, timestamp<br> &lt;foreground/background&gt;: black, red, green, blue, white, gray, darkgray, magenta, cyan, default<br><br>EXAMPLE:<br> msgcolor send cyan default"
+msgstr "msgcolor &lt;klasse&gt; &lt;forgrund&gt; &lt;baggrund;: Sæt farven for de forskellige klasser af beskeder i samtalevinduet.<br> &lt;klasse&gt;: receive, send, highlight, action, timestamp<br> &lt;forgrund/baggrund&gt;: black, red, green, blue, white, gray, darkgray, magenta, cyan, default<br><br>EKSEMPEL:<br> msgcolor send cyan default"
msgid "Unable to open file."
msgstr "Kunne ikke åbne fil."
@@ -871,25 +822,14 @@ msgstr "Samtaler med %s på %s"
msgid "%B %Y"
msgstr "%B %Y"
-msgid ""
-"System events will only be logged if the \"Log all status changes to system "
-"log\" preference is enabled."
-msgstr ""
-"Systembegivenheder vil kun blive logget, hvis indstillingen \"Anfør alle "
-"statusændringer i systemlog\" er slået til."
+msgid "System events will only be logged if the \"Log all status changes to system log\" preference is enabled."
+msgstr "Systembegivenheder vil kun blive logget, hvis indstillingen \"Anfør alle statusændringer i systemlog\" er slået til."
-msgid ""
-"Instant messages will only be logged if the \"Log all instant messages\" "
-"preference is enabled."
-msgstr ""
-"Beskeder vil kun blive logget, hvis indstillingen \"Log alle beskeder\" er "
-"slået til."
+msgid "Instant messages will only be logged if the \"Log all instant messages\" preference is enabled."
+msgstr "Beskeder vil kun blive logget, hvis indstillingen \"Log alle beskeder\" er slået til."
-msgid ""
-"Chats will only be logged if the \"Log all chats\" preference is enabled."
-msgstr ""
-"Samtaler vil kun blive logget, hvis indstillingen \"Log alle samtaler\" er "
-"slået til."
+msgid "Chats will only be logged if the \"Log all chats\" preference is enabled."
+msgstr "Samtaler vil kun blive logget, hvis indstillingen \"Log alle samtaler\" er slået til."
msgid "No logs were found"
msgstr "Ingen logge blev fundet"
@@ -949,7 +889,7 @@ msgid "call: Make an audio call."
msgstr "opkald: Foretag et lydopkald."
msgid "Emails"
-msgstr "E-post"
+msgstr "E-mail"
msgid "You have mail!"
msgstr "Du har post!"
@@ -1032,8 +972,7 @@ msgstr "Fejl ved indlæsning af udvidelsesmodul"
msgid "The selected file is not a valid plugin."
msgstr "Den valgte fil er ikke et gyldigt udvidelsesmodul."
-msgid ""
-"Please open the debug window and try again to see the exact error message."
+msgid "Please open the debug window and try again to see the exact error message."
msgstr "Åbn fejlsøgningsvinduet og prøv igen for at se den præcise fejlbesked."
msgid "Select plugin to install"
@@ -1466,9 +1405,7 @@ msgid "Couldn't find window"
msgstr "Kan ikke finde vindue"
msgid "This plugin cannot be loaded because it was not built with X11 support."
-msgstr ""
-"Dette udvidelsesmodul kan ikke indlæses, fordi det ikke er bygget med X11-"
-"understøttelse."
+msgstr "Dette udvidelsesmodul kan ikke indlæses, fordi det ikke er bygget med X11-understøttelse."
msgid "GntClipboard"
msgstr "GntClipboard"
@@ -1476,12 +1413,8 @@ msgstr "GntClipboard"
msgid "Clipboard plugin"
msgstr "Udvidelsesmodul til udklipsholder"
-msgid ""
-"When the gnt clipboard contents change, the contents are made available to "
-"X, if possible."
-msgstr ""
-"Når gnt-udklipsholderens indhold ændrer sig, bliver indholdet gjort "
-"tilgængeligt for X, hvis det er muligt."
+msgid "When the gnt clipboard contents change, the contents are made available to X, if possible."
+msgstr "Når gnt-udklipsholderens indhold ændrer sig, bliver indholdet gjort tilgængeligt for X, hvis det er muligt."
#, c-format
msgid "%s just signed on"
@@ -1540,13 +1473,11 @@ msgstr "Historikmodul kræver logning"
msgid ""
"Logging can be enabled from Tools -> Preferences -> Logging.\n"
"\n"
-"Enabling logs for instant messages and/or chats will activate history for "
-"the same conversation type(s)."
+"Enabling logs for instant messages and/or chats will activate history for the same conversation type(s)."
msgstr ""
"Logning kan slås til i Værktøjer -> Indstillinger -> Logning.\n"
"\n"
-"Hvis logning slås til for beskeder og/eller samtaler, vil historikken for "
-"samme konversationstyper ligeledes blive aktiveret."
+"Hvis logning slås til for beskeder og/eller samtaler, vil historikken for samme konversationstyper ligeledes blive aktiveret."
msgid "GntHistory"
msgstr "GntHistory"
@@ -1554,12 +1485,8 @@ msgstr "GntHistory"
msgid "Shows recently logged conversations in new conversations."
msgstr "Viser tidligere loggede samtaler i nye samtaler."
-msgid ""
-"When a new conversation is opened this plugin will insert the last "
-"conversation into the current conversation."
-msgstr ""
-"Når en ny samtale bliver åbnet, vil dette modul indsætte den sidste samtale "
-"i den nye samtale."
+msgid "When a new conversation is opened this plugin will insert the last conversation into the current conversation."
+msgstr "Når en ny samtale bliver åbnet, vil dette modul indsætte den sidste samtale i den nye samtale."
#, c-format
msgid ""
@@ -1589,8 +1516,7 @@ msgid "TinyURL plugin"
msgstr "Udvidesesmodul til TinyURL"
msgid "When receiving a message with URL(s), use TinyURL for easier copying"
-msgstr ""
-"Brug TinyURL for at lette kopiering ved modtagelse af beskeder med URL'er"
+msgstr "Brug TinyURL for at lette kopiering ved modtagelse af beskeder med URL'er"
msgid "Online"
msgstr "Tilkoblet"
@@ -1707,37 +1633,21 @@ msgstr "venneliste"
msgid "The certificate is self-signed and cannot be automatically checked."
msgstr "Certifikatet er selvunderskrevet, og kan ikke kontrolleres automatisk."
-msgid ""
-"The certificate is not trusted because no certificate that can verify it is "
-"currently trusted."
-msgstr ""
-"Der stoles ikke på certifikatet, fordi ingen certifikater, der i øjeblikket "
-"stoles på, kan verificere det."
+msgid "The certificate is not trusted because no certificate that can verify it is currently trusted."
+msgstr "Der stoles ikke på certifikatet, fordi ingen certifikater, der i øjeblikket stoles på, kan verificere det."
-msgid ""
-"The certificate is not valid yet. Check that your computer's date and time "
-"are accurate."
-msgstr ""
-"Certifikatet er ikke gyldigt endnu. Tjek at din computers tid og dato er "
-"korrekt."
+msgid "The certificate is not valid yet. Check that your computer's date and time are accurate."
+msgstr "Certifikatet er ikke gyldigt endnu. Tjek at din computers tid og dato er korrekt."
-msgid ""
-"The certificate has expired and should not be considered valid. Check that "
-"your computer's date and time are accurate."
-msgstr ""
-"Certifikatet er udløbet og bør ikke anses som gyldigt. Tjek at din computers "
-"tid og dato er præcist."
+msgid "The certificate has expired and should not be considered valid. Check that your computer's date and time are accurate."
+msgstr "Certifikatet er udløbet og bør ikke anses som gyldigt. Tjek at din computers tid og dato er præcist."
#. Translators: "domain" refers to a DNS domain (e.g. talk.google.com)
msgid "The certificate presented is not issued to this domain."
msgstr "Det præsenterede certifikat er ikke udstedt til dette domæne."
-msgid ""
-"You have no database of root certificates, so this certificate cannot be "
-"validated."
-msgstr ""
-"Du har ingen database med rodcertifikater, så dette certifikat kan ikke "
-"kontrolleres."
+msgid "You have no database of root certificates, so this certificate cannot be validated."
+msgstr "Du har ingen database med rodcertifikater, så dette certifikat kan ikke kontrolleres."
msgid "The certificate chain presented is invalid."
msgstr "Den præsenterede certifikatkæde er ugyldig."
@@ -1802,12 +1712,8 @@ msgid "Unable to validate certificate"
msgstr "Gyldigheden af certifikatet kunne ikke fastslås"
#, c-format
-msgid ""
-"The certificate claims to be from \"%s\" instead. This could mean that you "
-"are not connecting to the service you believe you are."
-msgstr ""
-"Certifikatet siger at det stammer fra \"%s\" i stedet for. Dette kan betyde, "
-"at du ikke er ved at forbinde til den tjeneste, du tror."
+msgid "The certificate claims to be from \"%s\" instead. This could mean that you are not connecting to the service you believe you are."
+msgstr "Certifikatet siger at det stammer fra \"%s\" i stedet for. Dette kan betyde, at du ikke er ved at forbinde til den tjeneste, du tror."
# Ikke sikker på common name
#. Make messages
@@ -1898,12 +1804,8 @@ msgid "Invite to chat"
msgstr "Invitér til samtale"
#. Put our happy label in it.
-msgid ""
-"Please enter the name of the user you wish to invite, along with an optional "
-"invite message."
-msgstr ""
-"Indtast navnet på den bruger, du vil invitere, samt eventuelt en "
-"invitationsbesked."
+msgid "Please enter the name of the user you wish to invite, along with an optional invite message."
+msgstr "Indtast navnet på den bruger, du vil invitere, samt eventuelt en invitationsbesked."
#, c-format
msgid "Failed to get connection: %s"
@@ -1965,7 +1867,7 @@ msgid "Unknown reason"
msgstr "Ukendt grund"
msgid "Aborting DNS lookup in Tor Proxy mode."
-msgstr "Afbryder DNS-opslag i Tor proxy mode."
+msgstr "Afbryder DNS-opslag i Tor-proxy-tilstand."
#, c-format
msgid ""
@@ -2082,12 +1984,8 @@ msgstr "Filoverførsel fra %s mislykkedes."
msgid "Whether the specified command should handle \"aim\" URLs"
msgstr "Hvorledes den angivne kommando skal håndtere \"aim\"-adresser"
-msgid ""
-"True if the command specified in the \"command\" key should handle \"aim\" "
-"URLs."
-msgstr ""
-"Sand hvis kommandoen angivet i \"kommando\"-nøglen skal håndtere \"aim\"-"
-"adresser."
+msgid "True if the command specified in the \"command\" key should handle \"aim\" URLs."
+msgstr "Sand hvis kommandoen angivet i \"kommando\"-nøglen skal håndtere \"aim\"-adresser."
msgid "The handler for \"aim\" URLs"
msgstr "Håndteringen for \"aim\"-adresser"
@@ -2098,22 +1996,14 @@ msgstr "Kommandoen til at håndtere \"aim\"-adresser, hvis aktiveret."
msgid "Run the command in a terminal"
msgstr "Kør kommandoen i en terminal"
-msgid ""
-"True if the command used to handle this type of URL should be run in a "
-"terminal."
-msgstr ""
-"Sand hvis kommandoen til at håndtere denne type adresse skal køres i en "
-"terminal."
+msgid "True if the command used to handle this type of URL should be run in a terminal."
+msgstr "Sand hvis kommandoen til at håndtere denne type adresse skal køres i en terminal."
msgid "Whether the specified command should handle \"gg\" URLs"
msgstr "Hvorledes den angivne kommando skal håndtere \"gg\"-adresser"
-msgid ""
-"True if the command specified in the \"command\" key should handle \"gg\" "
-"URLs."
-msgstr ""
-"Sand hvis kommandoen angivet i \"kommando\"-nøglen skal håndtere \"gg\"-"
-"adresser."
+msgid "True if the command specified in the \"command\" key should handle \"gg\" URLs."
+msgstr "Sand hvis kommandoen angivet i \"kommando\"-nøglen skal håndtere \"gg\"-adresser."
msgid "The handler for \"gg\" URLs"
msgstr "Håndteringen for \"gg\"-adresser"
@@ -2124,12 +2014,8 @@ msgstr "Kommandoen til at håndtere \"gg\"-adresser, hvis aktiveret."
msgid "Whether the specified command should handle \"icq\" URLs"
msgstr "Hvorledes den angivne kommando skal håndtere \"icq\"-adresser"
-msgid ""
-"True if the command specified in the \"command\" key should handle \"icq\" "
-"URLs."
-msgstr ""
-"Sand hvis kommandoen angivet i \"kommando\"-nøglen skal håndtere \"icq\"-"
-"adresser."
+msgid "True if the command specified in the \"command\" key should handle \"icq\" URLs."
+msgstr "Sand hvis kommandoen angivet i \"kommando\"-nøglen skal håndtere \"icq\"-adresser."
msgid "The handler for \"icq\" URLs"
msgstr "Håndteringen for \"icq\"-adresser"
@@ -2140,12 +2026,8 @@ msgstr "Kommandoen til at håndtere \"icq\"-adresser, hvis aktiveret."
msgid "Whether the specified command should handle \"irc\" URLs"
msgstr "Hvorledes den angivne kommando skal håndtere \"irc\"-adresser"
-msgid ""
-"True if the command specified in the \"command\" key should handle \"irc\" "
-"URLs."
-msgstr ""
-"Sand hvis kommandoen angivet i \"kommando\"-nøglen skal håndtere \"irc\"-"
-"adresser."
+msgid "True if the command specified in the \"command\" key should handle \"irc\" URLs."
+msgstr "Sand hvis kommandoen angivet i \"kommando\"-nøglen skal håndtere \"irc\"-adresser."
msgid "The handler for \"irc\" URLs"
msgstr "Håndteringen for \"irc\"-adresser"
@@ -2156,12 +2038,8 @@ msgstr "Kommandoen til at håndtere \"irc\"-adresser, hvis aktiveret."
msgid "Whether the specified command should handle \"msnim\" URLs"
msgstr "Hvorledes den angivne kommando skal håndtere \"msnim\"-adresser"
-msgid ""
-"True if the command specified in the \"command\" key should handle \"msnim\" "
-"URLs."
-msgstr ""
-"Sand hvis kommandoen angivet i \"kommando\"-nøglen skal håndtere \"msnim\"-"
-"adresser."
+msgid "True if the command specified in the \"command\" key should handle \"msnim\" URLs."
+msgstr "Sand hvis kommandoen angivet i \"kommando\"-nøglen skal håndtere \"msnim\"-adresser."
msgid "The handler for \"msnim\" URLs"
msgstr "Håndteringen for \"msnim\"-adresser"
@@ -2172,12 +2050,8 @@ msgstr "Kommandoen til at håndtere \"msnim\"-adresser, hvis aktiveret."
msgid "Whether the specified command should handle \"sip\" URLs"
msgstr "Hvorledes den angivne kommando skal håndtere \"sip\"-adresser"
-msgid ""
-"True if the command specified in the \"command\" key should handle \"sip\" "
-"URLs."
-msgstr ""
-"Sand hvis kommandoen angivet i \"kommando\"-nøglen skal håndtere \"sip\"-"
-"adresser."
+msgid "True if the command specified in the \"command\" key should handle \"sip\" URLs."
+msgstr "Sand hvis kommandoen angivet i \"kommando\"-nøglen skal håndtere \"sip\"-adresser."
msgid "The handler for \"sip\" URLs"
msgstr "Håndteringen for \"sip\"-adresser"
@@ -2188,12 +2062,8 @@ msgstr "Kommandoen til at håndtere \"sip\"-adresser, hvis aktiveret."
msgid "Whether the specified command should handle \"xmpp\" URLs"
msgstr "Hvorledes den angivne kommando skal håndtere \"xmpp\"-adresser"
-msgid ""
-"True if the command specified in the \"command\" key should handle \"xmpp\" "
-"URLs."
-msgstr ""
-"Sand hvis kommandoen angivet i \"kommando\"-nøglen skal håndtere \"xmpp\"-"
-"adresser."
+msgid "True if the command specified in the \"command\" key should handle \"xmpp\" URLs."
+msgstr "Sand hvis kommandoen angivet i \"kommando\"-nøglen skal håndtere \"xmpp\"-adresser."
msgid "The handler for \"xmpp\" URLs"
msgstr "Håndteringen for \"xmpp\"-adresser"
@@ -2204,12 +2074,8 @@ msgstr "Kommandoen til at håndtere \"xmpp\"-adresser, hvis aktiveret."
msgid "Whether the specified command should handle \"ymsgr\" URLs"
msgstr "Hvorledes den angivne kommando skal håndtere \"ymsgr\"-adresser"
-msgid ""
-"True if the command specified in the \"command\" key should handle \"ymsgr\" "
-"URLs."
-msgstr ""
-"Sand hvis kommandoen angivet i \"kommando\"-nøglen skal håndtere \"ymsgr\"-"
-"adresser."
+msgid "True if the command specified in the \"command\" key should handle \"ymsgr\" URLs."
+msgstr "Sand hvis kommandoen angivet i \"kommando\"-nøglen skal håndtere \"ymsgr\"-adresser."
msgid "The handler for \"ymsgr\" URLs"
msgstr "Håndteringen for \"ymsgr\"-adresser"
@@ -2236,20 +2102,12 @@ msgid "XML"
msgstr "XML"
#, c-format
-msgid ""
-"<font color=\"#16569E\"><font size=\"2\">(%s)</font> <b>%s &lt;AUTO-"
-"REPLY&gt;:</b></font> %s<br/>\n"
-msgstr ""
-"<font color=\"#16569E\"><font size=\"2\">(%s)</font> <b>%s &lt;AUTO-SVAR&gt;:"
-"</b></font> %s<br/>\n"
+msgid "<font color=\"#16569E\"><font size=\"2\">(%s)</font> <b>%s &lt;AUTO-REPLY&gt;:</b></font> %s<br/>\n"
+msgstr "<font color=\"#16569E\"><font size=\"2\">(%s)</font> <b>%s &lt;AUTO-SVAR&gt;:</b></font> %s<br/>\n"
#, c-format
-msgid ""
-"<font color=\"#A82F2F\"><font size=\"2\">(%s)</font> <b>%s &lt;AUTO-"
-"REPLY&gt;:</b></font> %s<br/>\n"
-msgstr ""
-"<font color=\"#A82F2F\"><font size=\"2\">(%s)</font> <b>%s &lt;AUTO-SVAR&gt;:"
-"</b></font> %s<br/>\n"
+msgid "<font color=\"#A82F2F\"><font size=\"2\">(%s)</font> <b>%s &lt;AUTO-REPLY&gt;:</b></font> %s<br/>\n"
+msgstr "<font color=\"#A82F2F\"><font size=\"2\">(%s)</font> <b>%s &lt;AUTO-SVAR&gt;:</b></font> %s<br/>\n"
msgid "<font color=\"red\"><b>Unable to find log path!</b></font>"
msgstr "<font color=\"red\"><b>Kunne ikke finde logsti!</b></font>"
@@ -2262,18 +2120,11 @@ msgstr "<font color=\"red\"><b>Kunne ikke læse filen: %s</b></font>"
msgid "(%s) %s <AUTO-REPLY>: %s\n"
msgstr "(%s) %s <AUTO-SVAR>: %s\n"
-msgid ""
-"No codecs found. Install some GStreamer codecs found in GStreamer plugins "
-"packages."
-msgstr ""
-"Ingen codec fundet. Installér nogle GStreamer-codec fra GStreamer-"
-"udvidelsespakken."
+msgid "No codecs found. Install some GStreamer codecs found in GStreamer plugins packages."
+msgstr "Ingen codec fundet. Installér nogle GStreamer-codec fra GStreamer-udvidelsespakken."
-msgid ""
-"No codecs left. Your codec preferences in fs-codecs.conf are too strict."
-msgstr ""
-"Ingen codec tilbage. Dine codec-indstillinger i fs-codecs.conf er for "
-"strikse."
+msgid "No codecs left. Your codec preferences in fs-codecs.conf are too strict."
+msgstr "Ingen codec tilbage. Dine codec-indstillinger i fs-codecs.conf er for strikse."
msgid "A non-recoverable Farsight2 error has occurred."
msgstr "Der opstod en uoprettelig Farsight2-fejl."
@@ -2309,19 +2160,12 @@ msgstr "Uoverensstemmelse i magisk tal %d for udvidelsesmodul (kræver %d)"
msgid "ABI version mismatch %d.%d.x (need %d.%d.x)"
msgstr "Uoverensstemmelse i ABI-version %d.%d.x (kræver %d.%d.x)"
-msgid ""
-"Plugin does not implement all required functions (list_icon, login and close)"
-msgstr ""
-"Udvidelsesmodul implementerer ikke alle de krævede funktioner (list_icon, "
-"login og close)"
+msgid "Plugin does not implement all required functions (list_icon, login and close)"
+msgstr "Udvidelsesmodul implementerer ikke alle de krævede funktioner (list_icon, login og close)"
#, c-format
-msgid ""
-"The required plugin %s was not found. Please install this plugin and try "
-"again."
-msgstr ""
-"Det krævede udvidelsesmodul %s blev ikke fundet. Installér venligst modulet "
-"og prøv igen."
+msgid "The required plugin %s was not found. Please install this plugin and try again."
+msgstr "Det krævede udvidelsesmodul %s blev ikke fundet. Installér venligst modulet og prøv igen."
msgid "Unable to load the plugin"
msgstr "Kan ikke indlæse udvidelsesmodulet"
@@ -2345,8 +2189,7 @@ msgstr "Acceptér automatisk filoverførselsanmodning fra udvalgte brugere."
#, c-format
msgid "Autoaccepted file transfer of \"%s\" from \"%s\" completed."
-msgstr ""
-"En automatisk accepteret filoverførsel af \"%s\" fra \"%s\" blev fuldført."
+msgstr "En automatisk accepteret filoverførsel af \"%s\" fra \"%s\" blev fuldført."
msgid "Autoaccept complete"
msgstr "Automatisk accept fuldført"
@@ -2395,8 +2238,7 @@ msgid ""
"Notify with a popup when an autoaccepted file transfer is complete\n"
"(only when there's no conversation with the sender)"
msgstr ""
-"Påmind med en pop-op, når en automatisk accepteret filoverførsel er "
-"fuldført\n"
+"Påmind med en pop-op, når en automatisk accepteret filoverførsel er fuldført\n"
"(kun hvis der ikke er nogen konversation med senderen)"
msgid "Create a new directory for each user"
@@ -2530,12 +2372,8 @@ msgid "Test plugin IPC support, as a client."
msgstr "Test IPC-udvidelsesmodulunderstøttelse som en klient."
#. * description
-msgid ""
-"Test plugin IPC support, as a client. This locates the server plugin and "
-"calls the commands registered."
-msgstr ""
-"Test IPC-udvidelsesmodulunderstøttelse som en klient. Dette finder "
-"serverudvidelsesmodulet og kalder den registrerede kommando."
+msgid "Test plugin IPC support, as a client. This locates the server plugin and calls the commands registered."
+msgstr "Test IPC-udvidelsesmodulunderstøttelse som en klient. Dette finder serverudvidelsesmodulet og kalder den registrerede kommando."
#. *< type
#. *< ui_requirement
@@ -2554,9 +2392,7 @@ msgstr "Test IPC-udvidelsesmodulunderstøttelse som en server."
#. * description
msgid "Test plugin IPC support, as a server. This registers the IPC commands."
-msgstr ""
-"Test IPC-udvidelsesmodulunderstøttelse som en server. Dette registrerer IPC-"
-"kommandoerne."
+msgstr "Test IPC-udvidelsesmodulunderstøttelse som en server. Dette registrerer IPC-kommandoerne."
msgid "Hide Joins/Parts"
msgstr "Skjul når folk kommer/går"
@@ -2587,12 +2423,8 @@ msgid "Hides extraneous join/part messages."
msgstr "Skjuler overflødige beskeder om folk, der kommer/går."
#. * description
-msgid ""
-"This plugin hides join/part messages in large rooms, except for those users "
-"actively taking part in a conversation."
-msgstr ""
-"Dette udvidelsesmodul skjuler beskeder om folk, der kommer/går i store rum, "
-"på nær for de som aktivt tager del i en samtale."
+msgid "This plugin hides join/part messages in large rooms, except for those users actively taking part in a conversation."
+msgstr "Dette udvidelsesmodul skjuler beskeder om folk, der kommer/går i store rum, på nær for de som aktivt tager del i en samtale."
#. This is used in the place of a timezone abbreviation if the
#. * offset is way off. The user should never really see it, but
@@ -2617,12 +2449,8 @@ msgstr "En eller flere beskeder kunne måske ikke leveres."
msgid "You were disconnected from the server."
msgstr "Din forbindelse til serveren blev afbrudt."
-msgid ""
-"You are currently disconnected. Messages will not be received unless you are "
-"logged in."
-msgstr ""
-"Du er ikke forbunddet. Beskeder vil ikke blive modtaget medmindre du er "
-"logget ind."
+msgid "You are currently disconnected. Messages will not be received unless you are logged in."
+msgstr "Du er ikke forbunddet. Beskeder vil ikke blive modtaget medmindre du er logget ind."
msgid "Message could not be sent because the maximum length was exceeded."
msgstr "Besked kunne ikke sendes, fordi den maksimale længde blev overskredet."
@@ -2703,18 +2531,13 @@ msgstr "Inkludér andre klienters logge i logviseren."
#. * description
msgid ""
-"When viewing logs, this plugin will include logs from other IM clients. "
-"Currently, this includes Adium, MSN Messenger, aMSN, and Trillian.\n"
+"When viewing logs, this plugin will include logs from other IM clients. Currently, this includes Adium, MSN Messenger, aMSN, and Trillian.\n"
"\n"
-"WARNING: This plugin is still alpha code and may crash frequently. Use it "
-"at your own risk!"
+"WARNING: This plugin is still alpha code and may crash frequently. Use it at your own risk!"
msgstr ""
-"Når der vises logge, vil dette udvidelsesmodul inkludere logge fra andre "
-"beskedklienter. På nuværende tidspunkt er dette Adium, MSN Messenger, aMSN "
-"og Trillian.\n"
+"Når der vises logge, vil dette udvidelsesmodul inkludere logge fra andre beskedklienter. På nuværende tidspunkt er dette Adium, MSN Messenger, aMSN og Trillian.\n"
"\n"
-"ADVARSEL: Dette udvidelsesmodul er stadig alfa-kode og kan bryde ned "
-"jævnligt. Brug det på eget ansvar!"
+"ADVARSEL: Dette udvidelsesmodul er stadig alfa-kode og kan bryde ned jævnligt. Brug det på eget ansvar!"
msgid "Mono Plugin Loader"
msgstr "Indlæser til Mono-moduler"
@@ -2746,35 +2569,21 @@ msgid "Prepends a newline to displayed message."
msgstr "Indskyder en ny linje i viste besked."
#. *< summary
-msgid ""
-"Prepends a newline to messages so that the rest of the message appears below "
-"the username in the conversation window."
-msgstr ""
-"Indskyder en ny linje til beskederne så resten af beskederne optræder neden "
-"under brugernavnet i samtalevinduet."
+msgid "Prepends a newline to messages so that the rest of the message appears below the username in the conversation window."
+msgstr "Indskyder en ny linje til beskederne så resten af beskederne optræder neden under brugernavnet i samtalevinduet."
msgid "Offline Message Emulation"
msgstr "Emulering af offline-beskeder"
msgid "Save messages sent to an offline user as pounce."
-msgstr ""
-"Gem beskeder som sendes til en afkoblet bruger som automatisk praj ved "
-"indlogning."
+msgstr "Gem beskeder som sendes til en afkoblet bruger som automatisk praj ved indlogning."
-msgid ""
-"The rest of the messages will be saved as pounces. You can edit/delete the "
-"pounce from the `Buddy Pounce' dialog."
-msgstr ""
-"Resten af beskederne vil blive gemt som logind-praj. Du kan redigere/slette "
-"de gemte beskeder fra vinduet \"Logind-praj\"."
+msgid "The rest of the messages will be saved as pounces. You can edit/delete the pounce from the `Buddy Pounce' dialog."
+msgstr "Resten af beskederne vil blive gemt som logind-praj. Du kan redigere/slette de gemte beskeder fra vinduet \"Logind-praj\"."
#, c-format
-msgid ""
-"\"%s\" is currently offline. Do you want to save the rest of the messages in "
-"a pounce and automatically send them when \"%s\" logs back in?"
-msgstr ""
-"\"%s\" er afkoblet. Vil du gemme resten af beskeden i et automatisk praj ved "
-"indlogning og automatisk sende den, når \"%s\" logger på igen?"
+msgid "\"%s\" is currently offline. Do you want to save the rest of the messages in a pounce and automatically send them when \"%s\" logs back in?"
+msgstr "\"%s\" er afkoblet. Vil du gemme resten af beskeden i et automatisk praj ved indlogning og automatisk sende den, når \"%s\" logger på igen?"
msgid "Offline Message"
msgstr "Offline-besked"
@@ -2814,13 +2623,10 @@ msgstr "Gennemtving at adgangskode kun bliver brugt én gang."
#. * description
msgid ""
-"Allows you to enforce on a per-account basis that passwords not being saved "
-"are only used in a single successful connection.\n"
+"Allows you to enforce on a per-account basis that passwords not being saved are only used in a single successful connection.\n"
"Note: The account password must not be saved for this to work."
msgstr ""
-"Giver dig mulighed for at gennemtvinge for hver enkelt konto, at "
-"adgangskoder, der ikke bliver gemt, kun bliver brugt til én vellykket "
-"forbindelse.\n"
+"Giver dig mulighed for at gennemtvinge for hver enkelt konto, at adgangskoder, der ikke bliver gemt, kun bliver brugt til én vellykket forbindelse.\n"
"Bemærk: Kontoens adgangskode må ikke gemmes, hvis dette skal virke."
#. *< type
@@ -2844,12 +2650,8 @@ msgstr "Synsk tilstand"
msgid "Psychic mode for incoming conversation"
msgstr "Synsk tilstand for indgående samtaler"
-msgid ""
-"Causes conversation windows to appear as other users begin to message you. "
-"This works for AIM, ICQ, XMPP, Sametime, and Yahoo!"
-msgstr ""
-"Får samtalevinduerne til at dukke frem når andre brugere begynder at skrive "
-"til dig. Dette virker med AIM, ICQ, XMPP, Sametime og Yahoo!"
+msgid "Causes conversation windows to appear as other users begin to message you. This works for AIM, ICQ, XMPP, Sametime, and Yahoo!"
+msgstr "Får samtalevinduerne til at dukke frem når andre brugere begynder at skrive til dig. Dette virker med AIM, ICQ, XMPP, Sametime og Yahoo!"
msgid "You feel a disturbance in the force..."
msgstr "Du føler en forstyrrelse i kraften..."
@@ -2995,11 +2797,8 @@ msgstr "Påmindelse om vennestatus"
#. *< version
#. * summary
#. * description
-msgid ""
-"Notifies in a conversation window when a buddy goes or returns from away or "
-"idle."
-msgstr ""
-"Fortæller i en samtale når en ven bliver fraværende eller kommer tilbage."
+msgid "Notifies in a conversation window when a buddy goes or returns from away or idle."
+msgstr "Fortæller i en samtale når en ven bliver fraværende eller kommer tilbage."
msgid "Tcl Plugin Loader"
msgstr "Indlæser til Tcl-udvidelsesmoduler"
@@ -3007,25 +2806,16 @@ msgstr "Indlæser til Tcl-udvidelsesmoduler"
msgid "Provides support for loading Tcl plugins"
msgstr "Giver understøttelse af Tcl-udvidelsesmoduler"
-msgid ""
-"Unable to detect ActiveTCL installation. If you wish to use TCL plugins, "
-"install ActiveTCL from http://www.activestate.com\n"
-msgstr ""
-"Kan ikke finde ActiveTCL-installation. Hvis du ønsker at bruge TCL-"
-"udvidelsesmoduler, så installér ActiveTCL fra http://www.activestate.com\n"
+msgid "Unable to detect ActiveTCL installation. If you wish to use TCL plugins, install ActiveTCL from http://www.activestate.com\n"
+msgstr "Kan ikke finde ActiveTCL-installation. Hvis du ønsker at bruge TCL-udvidelsesmoduler, så installér ActiveTCL fra http://www.activestate.com\n"
-msgid ""
-"Unable to find Apple's \"Bonjour for Windows\" toolkit, see http://d.pidgin."
-"im/BonjourWindows for more information."
-msgstr ""
-"Apples Bonjour-værktøjssæt til Windows blev ikke fundet. Se http://d.pidgin."
-"im/BonjourWindows for at få mere information."
+msgid "Unable to find Apple's \"Bonjour for Windows\" toolkit, see http://d.pidgin.im/BonjourWindows for more information."
+msgstr "Apples Bonjour-værktøjssæt til Windows blev ikke fundet. Se http://d.pidgin.im/BonjourWindows for at få mere information."
msgid "Unable to listen for incoming IM connections"
msgstr "Kan ikke lytte efter indgående beskeder"
-msgid ""
-"Unable to establish connection with the local mDNS server. Is it running?"
+msgid "Unable to establish connection with the local mDNS server. Is it running?"
msgstr "Kunne ikke etablerede forbindelse til lokal mDNS-server. Kører den?"
msgid "First name"
@@ -3036,7 +2826,7 @@ msgstr "Efternavn"
#. email
msgid "Email"
-msgstr "E-post"
+msgstr "E-mail"
msgid "AIM Account"
msgstr "AIM-konto"
@@ -3080,11 +2870,8 @@ msgstr "Fejl under kommunikation med lokal mDNSResponder."
msgid "Invalid proxy settings"
msgstr "Ugyldige proxyindstillinger"
-msgid ""
-"Either the host name or port number specified for your given proxy type is "
-"invalid."
-msgstr ""
-"Enten værtsnavn eller port, der er angivet til din proxytype, er ugyldig."
+msgid "Either the host name or port number specified for your given proxy type is invalid."
+msgstr "Enten værtsnavn eller port, der er angivet til din proxytype, er ugyldig."
msgid "Save Buddylist..."
msgstr "Gem venneliste..."
@@ -3391,9 +3178,8 @@ msgstr "Kodninger"
msgid "Auto-detect incoming UTF-8"
msgstr "Genkend indkommende UTF-8"
-#, fuzzy
msgid "Ident name"
-msgstr "Skærmnavn"
+msgstr "ID-navn"
msgid "Real name"
msgstr "Rigtigt navn"
@@ -3445,11 +3231,9 @@ msgstr " <i>(identificeret)</i>"
msgid "Nick"
msgstr "Alias"
-#, fuzzy
msgid "Login name"
-msgstr "Intet navn"
+msgstr "Log-ind navn"
-#, fuzzy
msgid "Host name"
msgstr "Værtsnavn"
@@ -3482,7 +3266,7 @@ msgstr "Emne for %s er: %s"
#, c-format
msgid "Topic for %s set by %s at %s on %s"
-msgstr "Emne for %s sat af %s den %s på %s"
+msgstr "Emne for %s sat af %s %s på %s"
#, c-format
msgid "Unknown message '%s'"
@@ -3543,19 +3327,11 @@ msgstr "tilstand (%s %s) af %s"
msgid "Invalid nickname"
msgstr "Ugyldigt alias"
-msgid ""
-"Your selected nickname was rejected by the server. It probably contains "
-"invalid characters."
-msgstr ""
-"Dit valgte alias blev afvist af serveren. Det indeholder sikkert ugyldige "
-"tegn."
+msgid "Your selected nickname was rejected by the server. It probably contains invalid characters."
+msgstr "Dit valgte alias blev afvist af serveren. Det indeholder sikkert ugyldige tegn."
-msgid ""
-"Your selected account name was rejected by the server. It probably contains "
-"invalid characters."
-msgstr ""
-"Dit valgte kontonavn blev afvist af serveren. Det indenholder sikkert "
-"ugyldige tegn."
+msgid "Your selected account name was rejected by the server. It probably contains invalid characters."
+msgstr "Dit valgte kontonavn blev afvist af serveren. Det indenholder sikkert ugyldige tegn."
#. We only want to do the following dance if the connection
#. has not been successfully completed. If it has, just
@@ -3603,18 +3379,17 @@ msgid "Failed to initialize SASL authentication: %s"
msgstr "Kunne ikke initialisere SASL-godkendelse: %s"
msgid "SASL authentication failed: No worthy authentication mechanisms found."
-msgstr "SASL-godkendelse fejlede: Ingen værdige SASL-mekanismer fundet."
+msgstr "SASL-godkendelse mislykkedes: Ingen værdige SASL-mekanismer fundet."
#, c-format
msgid "SASL authentication failed: %s"
msgstr "SASL-godkendelse mislykkedes: %s"
-msgid ""
-"SASL authentication failed: Server does not support SASL authentication."
+msgid "SASL authentication failed: Server does not support SASL authentication."
msgstr "SASL-godkendelse fejlede: Serveren understøtter ikke SASL-godkendelse."
msgid "SASL authentication failed: Initializing SASL failed."
-msgstr "SASL-godkendelse mislykkedes: Initialisering af SASL fejlede."
+msgstr "SASL-godkendelse mislykkedes: Initialisering af SASL mislykkedes."
msgid "Incorrect Password"
msgstr "Forkert adgangskode"
@@ -3628,12 +3403,8 @@ msgstr "action &lt;handling at udføre&gt;: Udfør en handling."
msgid "authserv: Send a command to authserv"
msgstr "authserv: Send en kommando til authserv"
-msgid ""
-"away [message]: Set an away message, or use no message to return from being "
-"away."
-msgstr ""
-"away [besked]: Indstil en fraværsbesked. Hvis ingen besked angives, så "
-"returnes du fra fravær."
+msgid "away [message]: Set an away message, or use no message to return from being away."
+msgstr "away [besked]: Indstil en fraværsbesked. Hvis ingen besked angives, så returnes du fra fravær."
msgid "ctcp <nick> <msg>: sends ctcp msg to nick."
msgstr "ctcp <nick> <msg>: sender ctcp-besked til alias."
@@ -3641,56 +3412,26 @@ msgstr "ctcp <nick> <msg>: sender ctcp-besked til alias."
msgid "chanserv: Send a command to chanserv"
msgstr "chanserv: Send en kommando til chanserv"
-msgid ""
-"deop &lt;nick1&gt; [nick2] ...: Remove channel operator status from "
-"someone. You must be a channel operator to do this."
-msgstr ""
-"deop &lt;navn1&gt; [navn2] ...: Fjern kanaloperatørstatus fra en eller "
-"anden. Du skal være kanaloperatør for at gøre dette."
+msgid "deop &lt;nick1&gt; [nick2] ...: Remove channel operator status from someone. You must be a channel operator to do this."
+msgstr "deop &lt;navn1&gt; [navn2] ...: Fjern kanaloperatørstatus fra en eller anden. Du skal være kanaloperatør for at gøre dette."
-msgid ""
-"devoice &lt;nick1&gt; [nick2] ...: Remove channel voice status from "
-"someone, preventing them from speaking if the channel is moderated (+m). You "
-"must be a channel operator to do this."
-msgstr ""
-"devoice &lt;navn1&gt; [navn2] ...: Fjern \"voice\"-status i kanal fra en "
-"eller anden. Det forhindrer dem i at snakke hvis kanalen er modereret (+m). "
-"Du skal være kanaloperatør for at gøre dette."
+msgid "devoice &lt;nick1&gt; [nick2] ...: Remove channel voice status from someone, preventing them from speaking if the channel is moderated (+m). You must be a channel operator to do this."
+msgstr "devoice &lt;navn1&gt; [navn2] ...: Fjern \"voice\"-status i kanal fra en eller anden. Det forhindrer dem i at snakke hvis kanalen er modereret (+m). Du skal være kanaloperatør for at gøre dette."
-msgid ""
-"invite &lt;nick&gt; [room]: Invite someone to join you in the specified "
-"channel, or the current channel."
-msgstr ""
-"invite &lt;navn&gt; [rum]: Invitér nogen til den angivne kanal, eller den "
-"nuværende kanal."
+msgid "invite &lt;nick&gt; [room]: Invite someone to join you in the specified channel, or the current channel."
+msgstr "invite &lt;navn&gt; [rum]: Invitér nogen til den angivne kanal, eller den nuværende kanal."
-msgid ""
-"j &lt;room1&gt;[,room2][,...] [key1[,key2][,...]]: Enter one or more "
-"channels, optionally providing a channel key for each if needed."
-msgstr ""
-"j &lt;rum1&gt;[,rum2][,...] [nøgle1[,nøgle2][,...]]: Deltag i en eller "
-"flere kanaler. Du kan angive en adgangsnøgle til hver, hvis det kræves."
+msgid "j &lt;room1&gt;[,room2][,...] [key1[,key2][,...]]: Enter one or more channels, optionally providing a channel key for each if needed."
+msgstr "j &lt;rum1&gt;[,rum2][,...] [nøgle1[,nøgle2][,...]]: Deltag i en eller flere kanaler. Du kan angive en adgangsnøgle til hver, hvis det kræves."
-msgid ""
-"join &lt;room1&gt;[,room2][,...] [key1[,key2][,...]]: Enter one or more "
-"channels, optionally providing a channel key for each if needed."
-msgstr ""
-"join &lt;rum1&gt;[,rum2][,...] [nøgle1[,nøgle2][,...]]: Tilslut en eller "
-"flere kanaler. Du kan angive en adgangsnøgle til hver, hvis det kræves."
+msgid "join &lt;room1&gt;[,room2][,...] [key1[,key2][,...]]: Enter one or more channels, optionally providing a channel key for each if needed."
+msgstr "join &lt;rum1&gt;[,rum2][,...] [nøgle1[,nøgle2][,...]]: Tilslut en eller flere kanaler. Du kan angive en adgangsnøgle til hver, hvis det kræves."
-msgid ""
-"kick &lt;nick&gt; [message]: Remove someone from a channel. You must be a "
-"channel operator to do this."
-msgstr ""
-"kick &lt;nick&gt; [besked]: Fjern nogen fra kanalen. Du skal have "
-"kanaloperatørstatus for at gøre dette."
+msgid "kick &lt;nick&gt; [message]: Remove someone from a channel. You must be a channel operator to do this."
+msgstr "kick &lt;nick&gt; [besked]: Fjern nogen fra kanalen. Du skal have kanaloperatørstatus for at gøre dette."
-msgid ""
-"list: Display a list of chat rooms on the network. <i>Warning, some servers "
-"may disconnect you upon doing this.</i>"
-msgstr ""
-"list: Vis en liste over samtalerum på netværket. <i>Advarsel, nogle servere "
-"kan afbryde forbindelsen når du gør dette.</i>"
+msgid "list: Display a list of chat rooms on the network. <i>Warning, some servers may disconnect you upon doing this.</i>"
+msgstr "list: Vis en liste over samtalerum på netværket. <i>Advarsel, nogle servere kan afbryde forbindelsen når du gør dette.</i>"
msgid "me &lt;action to perform&gt;: Perform an action."
msgstr "me &lt;handling at udføre&gt;: Udfør en handling."
@@ -3698,19 +3439,11 @@ msgstr "me &lt;handling at udføre&gt;: Udfør en handling."
msgid "memoserv: Send a command to memoserv"
msgstr "memoserv: Send en kommando til memoserv"
-msgid ""
-"mode &lt;+|-&gt;&lt;A-Za-z&gt; &lt;nick|channel&gt;: Set or unset a channel "
-"or user mode."
-msgstr ""
-"mode &lt;+|-&gt;&lt;A-Za-Z&gt; &lt;nick|kanal&gt;: Sæt eller fjern en "
-"kanal- eller brugertilstand."
+msgid "mode &lt;+|-&gt;&lt;A-Za-z&gt; &lt;nick|channel&gt;: Set or unset a channel or user mode."
+msgstr "mode &lt;+|-&gt;&lt;A-Za-Z&gt; &lt;nick|kanal&gt;: Sæt eller fjern en kanal- eller brugertilstand."
-msgid ""
-"msg &lt;nick&gt; &lt;message&gt;: Send a private message to a user (as "
-"opposed to a channel)."
-msgstr ""
-"msg &lt;navn&gt; &lt;besked&gt;: Send en privat besked til en bruger (i "
-"modsætning til en kanal)."
+msgid "msg &lt;nick&gt; &lt;message&gt;: Send a private message to a user (as opposed to a channel)."
+msgstr "msg &lt;navn&gt; &lt;besked&gt;: Send en privat besked til en bruger (i modsætning til en kanal)."
msgid "names [channel]: List the users currently in a channel."
msgstr "names [kanal]: Vis brugerne der er på kanalen lige nu."
@@ -3724,43 +3457,23 @@ msgstr "nickserv: Send en kommando til nickserv"
msgid "notice &lt;target&lt;: Send a notice to a user or channel."
msgstr "notice &lt;targetl&lt;: Send en påmindelse til en bruger eller kanal."
-msgid ""
-"op &lt;nick1&gt; [nick2] ...: Grant channel operator status to someone. You "
-"must be a channel operator to do this."
-msgstr ""
-"op &lt;navn1&gt; [navn2] ...: Giv kanaloperatørstatus til en eller anden. "
-"Du skal være kanaloperatør for at gøre dette."
+msgid "op &lt;nick1&gt; [nick2] ...: Grant channel operator status to someone. You must be a channel operator to do this."
+msgstr "op &lt;navn1&gt; [navn2] ...: Giv kanaloperatørstatus til en eller anden. Du skal være kanaloperatør for at gøre dette."
-msgid ""
-"operwall &lt;message&gt;: If you don't know what this is, you probably "
-"can't use it."
-msgstr ""
-"operwall &lt;message&gt;: Hvis du ikke ved hvad dette er, så kan du sikkert "
-"ikke bruge det."
+msgid "operwall &lt;message&gt;: If you don't know what this is, you probably can't use it."
+msgstr "operwall &lt;message&gt;: Hvis du ikke ved hvad dette er, så kan du sikkert ikke bruge det."
msgid "operserv: Send a command to operserv"
msgstr "operserv: Send en kommando til operserv"
-msgid ""
-"part [room] [message]: Leave the current channel, or a specified channel, "
-"with an optional message."
-msgstr ""
-"part [rum] [besked]: Forlad den nuværende kanal, eller angiv en kanal med "
-"en eventuel afskedsmeddelelse."
+msgid "part [room] [message]: Leave the current channel, or a specified channel, with an optional message."
+msgstr "part [rum] [besked]: Forlad den nuværende kanal, eller angiv en kanal med en eventuel afskedsmeddelelse."
-msgid ""
-"ping [nick]: Asks how much lag a user (or the server if no user specified) "
-"has."
-msgstr ""
-"ping [navn]: Spørger hvor meget lag (tidsforsinkelse) en bruger (eller "
-"serveren hvis ingen bruger angivet) har."
+msgid "ping [nick]: Asks how much lag a user (or the server if no user specified) has."
+msgstr "ping [navn]: Spørger hvor meget lag (tidsforsinkelse) en bruger (eller serveren hvis ingen bruger angivet) har."
-msgid ""
-"query &lt;nick&gt; &lt;message&gt;: Send a private message to a user (as "
-"opposed to a channel)."
-msgstr ""
-"query &lt;navn&gt; &lt;besked&gt;: Send en privat besked til en bruger (i "
-"modsætning til en kanal)."
+msgid "query &lt;nick&gt; &lt;message&gt;: Send a private message to a user (as opposed to a channel)."
+msgstr "query &lt;navn&gt; &lt;besked&gt;: Send en privat besked til en bruger (i modsætning til en kanal)."
msgid "quit [message]: Disconnect from the server, with an optional message."
msgstr "quit [besked]: Luk forbindelse til server, med en valgfri besked."
@@ -3768,12 +3481,8 @@ msgstr "quit [besked]: Luk forbindelse til server, med en valgfri besked."
msgid "quote [...]: Send a raw command to the server."
msgstr "quote [...]: Sender en rå kommando til serveren."
-msgid ""
-"remove &lt;nick&gt; [message]: Remove someone from a room. You must be a "
-"channel operator to do this."
-msgstr ""
-"remove &lt;nick&gt; [besked]: Fjern en eller anden fra et rum. Du skal være "
-"kanaloperatør for at gøre dette."
+msgid "remove &lt;nick&gt; [message]: Remove someone from a room. You must be a channel operator to do this."
+msgstr "remove &lt;nick&gt; [besked]: Fjern en eller anden fra et rum. Du skal være kanaloperatør for at gøre dette."
msgid "time: Displays the current local time at the IRC server."
msgstr "time: Viser den lokale tid på IRC-serveren."
@@ -3787,19 +3496,11 @@ msgstr "umode &lt;+|-&gt;&lt;A-Za-z&gt;: Sæt eller fjern en brugertilstand."
msgid "version [nick]: send CTCP VERSION request to a user"
msgstr "version [nick]: send CTCP VERSION-forspørgelse til en bruger"
-msgid ""
-"voice &lt;nick1&gt; [nick2] ...: Grant channel voice status to someone. You "
-"must be a channel operator to do this."
-msgstr ""
-"voice &lt;navn1&gt; [navn2] ...: Giv \"voice\"-status i kanal til en eller "
-"anden. Du skal være kanaloperatør for at gøre dette."
+msgid "voice &lt;nick1&gt; [nick2] ...: Grant channel voice status to someone. You must be a channel operator to do this."
+msgstr "voice &lt;navn1&gt; [navn2] ...: Giv \"voice\"-status i kanal til en eller anden. Du skal være kanaloperatør for at gøre dette."
-msgid ""
-"wallops &lt;message&gt;: If you don't know what this is, you probably can't "
-"use it."
-msgstr ""
-"wallops &lt;besked&gt;: Hvis du ikke ved hvad dette er, så kan du sikkert "
-"ikke bruge det."
+msgid "wallops &lt;message&gt;: If you don't know what this is, you probably can't use it."
+msgstr "wallops &lt;besked&gt;: Hvis du ikke ved hvad dette er, så kan du sikkert ikke bruge det."
msgid "whois [server] &lt;nick&gt;: Get information on a user."
msgstr "whois [server] &lt;navn&gt;: Hent oplysninger om en bruger."
@@ -3843,19 +3544,14 @@ msgid "Server does not use any supported authentication method"
msgstr "Serveren bruger ikke nogen understøttede godkendelsesmetoder"
#, c-format
-msgid ""
-"%s requires plaintext authentication over an unencrypted connection. Allow "
-"this and continue authentication?"
-msgstr ""
-"%s kræver godkendelse i klartekst over en ukrypteret forbindelse. Tillad "
-"dette og fortsæt godkendelse?"
+msgid "%s requires plaintext authentication over an unencrypted connection. Allow this and continue authentication?"
+msgstr "%s kræver godkendelse i klartekst over en ukrypteret forbindelse. Tillad dette og fortsæt godkendelse?"
msgid "Plaintext Authentication"
msgstr "Godkendelse i klartekst"
msgid "You require encryption, but it is not available on this server."
-msgstr ""
-"Du kræver kryptering, men kryptering er ikke til rådighed på denne server."
+msgstr "Du kræver kryptering, men kryptering er ikke til rådighed på denne server."
msgid "Invalid challenge from server"
msgstr "Ugyldig udfording fra serveren"
@@ -3864,16 +3560,11 @@ msgid "Server thinks authentication is complete, but client does not"
msgstr "Serveren tror godkendelsen er færdig, men det gør klienten ikke"
msgid "Server may require plaintext authentication over an unencrypted stream"
-msgstr ""
-"Serveren kræver muligvis godkendelse i klartekst over en ukrypteret strøm"
+msgstr "Serveren kræver muligvis godkendelse i klartekst over en ukrypteret strøm"
#, c-format
-msgid ""
-"%s may require plaintext authentication over an unencrypted connection. "
-"Allow this and continue authentication?"
-msgstr ""
-"%s kræver muligvis godkendelse i klartekst over en ukrypteret forbindelse. "
-"Tillad dette og fortsæt godkendelse?"
+msgid "%s may require plaintext authentication over an unencrypted connection. Allow this and continue authentication?"
+msgstr "%s kræver muligvis godkendelse i klartekst over en ukrypteret forbindelse. Tillad dette og fortsæt godkendelse?"
msgid "SASL authentication failed"
msgstr "SASL-godkendelse mislykkedes"
@@ -3888,17 +3579,11 @@ msgstr "Ugyldig kodning"
msgid "Unsupported Extension"
msgstr "Ikke-understøttet udvidelse"
-msgid ""
-"Unexpected response from the server. This may indicate a possible MITM "
-"attack"
+msgid "Unexpected response from the server. This may indicate a possible MITM attack"
msgstr "Uventet svar fra serveren. Dette kan indikere et muligt MITM-angreb"
-msgid ""
-"The server does support channel binding, but did not appear to advertise "
-"it. This indicates a likely MITM attack"
-msgstr ""
-"Serveren understøtter kanalbinding, men meddelte det tilsyneladene ikke. "
-"Dette indikerer sandsynligvis et MITM-angreb"
+msgid "The server does support channel binding, but did not appear to advertise it. This indicates a likely MITM attack"
+msgstr "Serveren understøtter kanalbinding, men meddelte det tilsyneladene ikke. Dette indikerer sandsynligvis et MITM-angreb"
msgid "Server does not support channel binding"
msgstr "Serveren understøtter ikke kanalbinding"
@@ -4009,12 +3694,8 @@ msgstr "Beskrivelse"
msgid "Edit XMPP vCard"
msgstr "Redigér XMPP-vCard"
-msgid ""
-"All items below are optional. Enter only the information with which you feel "
-"comfortable."
-msgstr ""
-"Alle elementerne nedenunder er valgfrie. Skriv kun de ting, du ønsker andre "
-"skal vide om dig."
+msgid "All items below are optional. Enter only the information with which you feel comfortable."
+msgstr "Alle elementerne nedenunder er valgfrie. Skriv kun de ting, du ønsker andre skal vide om dig."
msgid "Client"
msgstr "Klient"
@@ -4057,11 +3738,8 @@ msgid "Logo"
msgstr "Logo"
#, c-format
-msgid ""
-"%s will no longer be able to see your status updates. Do you want to "
-"continue?"
-msgstr ""
-"%s vil ikke længere kunne se dine statusopdateringer. Vil du fortsætte?"
+msgid "%s will no longer be able to see your status updates. Do you want to continue?"
+msgstr "%s vil ikke længere kunne se dine statusopdateringer. Vil du fortsætte?"
msgid "Cancel Presence Notification"
msgstr "Annullér påmindelse om tilstedeværelse"
@@ -4073,7 +3751,7 @@ msgid "Temporarily Hide From"
msgstr "Skjul dig midlertidigt for"
msgid "(Re-)Request authorization"
-msgstr "Anmod om ny godkendelse"
+msgstr "(Gen)anmod om ny godkendelse"
#. shouldn't this just happen automatically when the buddy is
#. removed?
@@ -4100,12 +3778,8 @@ msgid "The following are the results of your search"
msgstr "Din søgning gav følgende resultater"
#. current comment from Jabber User Directory users.jabber.org
-msgid ""
-"Find a contact by entering the search criteria in the given fields. Note: "
-"Each field supports wild card searches (%)"
-msgstr ""
-"Find en kontakt ved at indtaste søgekriterierne i de givne felter. Bemærk: "
-"Hvert felt understøtter joker-søgninger (%)"
+msgid "Find a contact by entering the search criteria in the given fields. Note: Each field supports wild card searches (%)"
+msgstr "Find en kontakt ved at indtaste søgekriterierne i de givne felter. Bemærk: Hvert felt understøtter joker-søgninger (%)"
msgid "Directory Query Failed"
msgstr "Mappeforespørgsel mislykkedes"
@@ -4123,7 +3797,7 @@ msgid "Fill in one or more fields to search for any matching XMPP users."
msgstr "Udfyld et eller flere felter for at søge efter XMPP-brugere."
msgid "Email Address"
-msgstr "E-post-adresse"
+msgstr "E-mail-adresse"
msgid "Search for XMPP users"
msgstr "Søg efter XMPP-brugere"
@@ -4218,13 +3892,10 @@ msgid "Roles:"
msgstr "Roller:"
msgid "Server requires TLS/SSL, but no TLS/SSL support was found."
-msgstr ""
-"Serveren kræver TLS/SSL, men der blev ikke fundet nogen TLS/SSL-"
-"understøttelse."
+msgstr "Serveren kræver TLS/SSL, men der blev ikke fundet nogen TLS/SSL-understøttelse."
msgid "You require encryption, but no TLS/SSL support was found."
-msgstr ""
-"Du kræver kryptering, men der blev ikke fundet nogen TLS/SSL-understøttelse."
+msgstr "Du kræver kryptering, men der blev ikke fundet nogen TLS/SSL-understøttelse."
msgid "Ping timed out"
msgstr "Tidsudløb for ping"
@@ -4257,7 +3928,7 @@ msgstr "Registrering mislykkedes"
#, c-format
msgid "Registration from %s successfully removed"
-msgstr "%s-registreringen blev fjernet"
+msgstr "Registreringen fra %s blev fjernet med succes"
msgid "Unregistration Successful"
msgstr "Afregistering fuldført"
@@ -4286,8 +3957,7 @@ msgstr "Adgangskode"
msgid "Unregister"
msgstr "Afregistrér"
-msgid ""
-"Please fill out the information below to change your account registration."
+msgid "Please fill out the information below to change your account registration."
msgstr "Udfyld oplysninger nedenunder for at ændre din kontoregistrering."
msgid "Please fill out the information below to register your new account."
@@ -4617,12 +4287,8 @@ msgid "Unable to buzz, because %s might be offline."
msgstr "Kan ikke bippe, fordi %s måske er afkoblet."
#, c-format
-msgid ""
-"Unable to buzz, because %s does not support it or does not wish to receive "
-"buzzes now."
-msgstr ""
-"Kunne ikke bippe fordi bruger %s ikke understøtter det, eller ikke ønsker at "
-"modtage et bip nu."
+msgid "Unable to buzz, because %s does not support it or does not wish to receive buzzes now."
+msgstr "Kunne ikke bippe fordi bruger %s ikke understøtter det, eller ikke ønsker at modtage et bip nu."
#. Yahoo only supports one attention command: the 'buzz'.
#. This is index number YAHOO_BUZZ.
@@ -4647,23 +4313,18 @@ msgstr "Ikke i stand til at sende medie med %s: Brugeren er ikke tilkoblet"
#, c-format
msgid "Unable to initiate media with %s: resource is not online"
-msgstr "Kunne ikke Initialisere medie med %s: Ressourcen er ikke online"
+msgstr "Kunne ikke initialisere medie med %s: Ressourcen er ikke online"
#, c-format
msgid "Unable to initiate media with %s: not subscribed to user presence"
-msgstr ""
-"Ikke i stand til at sende medie med %s: Abonnerer ikke på "
-"brugertilstedeværelse"
+msgstr "Ikke i stand til at sende medie med %s: Abonnerer ikke på brugertilstedeværelse"
msgid "Media Initiation Failed"
msgstr "Klargøring af medie mislykkedes"
#, c-format
-msgid ""
-"Please select the resource of %s with which you would like to start a media "
-"session."
-msgstr ""
-"Vælg venligst ressourcen fra %s som du gerne vil starte en mediesession med."
+msgid "Please select the resource of %s with which you would like to start a media session."
+msgstr "Vælg venligst ressourcen fra %s som du gerne vil starte en mediesession med."
msgid "Select a Resource"
msgstr "Vælg en ressource"
@@ -4692,35 +4353,23 @@ msgstr "topic [nyt emne]: Vis eller ændr emnet."
msgid "ban &lt;user&gt; [reason]: Ban a user from the room."
msgstr "ban &lt;bruger&gt; [grund]: Udvis en bruger fra rummet."
-msgid ""
-"affiliate &lt;owner|admin|member|outcast|none&gt; [nick1] [nick2] ...: Get "
-"the users with an affiliation or set users' affiliation with the room."
-msgstr ""
-"affiliate &lt;owner|admin|member|outcast|none&gt; [navn1] [navn2] ...: Find "
-"brugere med en given tilknytning, eller indstil en brugers tilknytning til "
-"rummet."
+msgid "affiliate &lt;owner|admin|member|outcast|none&gt; [nick1] [nick2] ...: Get the users with an affiliation or set users' affiliation with the room."
+msgstr "affiliate &lt;owner|admin|member|outcast|none&gt; [navn1] [navn2] ...: Find brugere med en given tilknytning, eller indstil en brugers tilknytning til rummet."
-msgid ""
-"role &lt;moderator|participant|visitor|none&gt; [nick1] [nick2] ...: Get the "
-"users with a role or set users' role with the room."
-msgstr ""
-"role &lt;moderator|participant|visitor|none&gt; [navn1] [navn2] ...: Find "
-"brugere med en rolle eller indstil en brugers rolle i rummet."
+msgid "role &lt;moderator|participant|visitor|none&gt; [nick1] [nick2] ...: Get the users with a role or set users' role with the room."
+msgstr "role &lt;moderator|participant|visitor|none&gt; [navn1] [navn2] ...: Find brugere med en rolle eller indstil en brugers rolle i rummet."
msgid "invite &lt;user&gt; [message]: Invite a user to the room."
msgstr "invite &lt;bruger&gt; [besked]: Invitér en bruger til rummet."
msgid "join: &lt;room[@server]&gt; [password]: Join a chat."
-msgstr "tilslut: &lt;rum[@server]&gt; [kodeord]: Tilslut en chat."
+msgstr "tilslut: &lt;rum[@server]&gt; [adgangskode]: Deltag i en chat."
msgid "kick &lt;user&gt; [reason]: Kick a user from the room."
msgstr "kick &lt;bruger&gt; [grund]: Spark en bruger ud fra rummet."
-msgid ""
-"msg &lt;user&gt; &lt;message&gt;: Send a private message to another user."
-msgstr ""
-"msg &lt;bruger&gt; &lt;besked&gt;: Send en privat besked til en anden "
-"bruger."
+msgid "msg &lt;user&gt; &lt;message&gt;: Send a private message to another user."
+msgstr "msg &lt;bruger&gt; &lt;besked&gt;: Send en privat besked til en anden bruger."
msgid "ping &lt;jid&gt;:\tPing a user/component/server."
msgstr "ping &lt;jid&gt;:\tPing en bruger/komponent/server."
@@ -4830,12 +4479,8 @@ msgstr "Fejl i samtale %s"
msgid "Create New Room"
msgstr "Opret nyt rum"
-msgid ""
-"You are creating a new room. Would you like to configure it, or accept the "
-"default settings?"
-msgstr ""
-"Du opretter et nyt rum. Vil du konfigurere det, eller bruge "
-"standardindstillingerne?"
+msgid "You are creating a new room. Would you like to configure it, or accept the default settings?"
+msgstr "Du opretter et nyt rum. Vil du konfigurere det, eller bruge standardindstillingerne?"
msgid "_Configure Room"
msgstr "_Konfigurér rum"
@@ -5140,12 +4785,8 @@ msgstr "Indstil brugeralias"
msgid "Please specify a new nickname for you."
msgstr "Angiv venligst et nyt alias til dig."
-msgid ""
-"This information is visible to all contacts on your contact list, so choose "
-"something appropriate."
-msgstr ""
-"Denne information er synlig for alle kontakter på din kontaktliste, så vælg "
-"noget passende."
+msgid "This information is visible to all contacts on your contact list, so choose something appropriate."
+msgstr "Denne information er synlig for alle kontakter på din kontaktliste, så vælg noget passende."
msgid "Set"
msgstr "Indstil"
@@ -5179,7 +4820,7 @@ msgid "Syntax Error (probably a client bug)"
msgstr "Syntaksfejl (sikkert en klientfejl)"
msgid "Invalid email address"
-msgstr "Ugyldig e-post-adresse"
+msgstr "Ugyldig e-mail-adresse"
msgid "User does not exist"
msgstr "Bruger findes ikke"
@@ -5348,20 +4989,12 @@ msgid "Buddy list synchronization issue in %s (%s)"
msgstr "Problemer med synkronisering af venneliste i %s (%s)"
#, c-format
-msgid ""
-"%s on the local list is inside the group \"%s\" but not on the server list. "
-"Do you want this buddy to be added?"
-msgstr ""
-"%s på den lokale liste er i gruppen \"%s\", men ikke på serverlisten. Vil du "
-"have denne ven tilføjet?"
+msgid "%s on the local list is inside the group \"%s\" but not on the server list. Do you want this buddy to be added?"
+msgstr "%s på den lokale liste er i gruppen \"%s\", men ikke på serverlisten. Vil du have denne ven tilføjet?"
#, c-format
-msgid ""
-"%s is on the local list but not on the server list. Do you want this buddy "
-"to be added?"
-msgstr ""
-"%s er på den lokale liste, men ikke på serverlisten. Vil du have denne ven "
-"tilføjet?"
+msgid "%s is on the local list but not on the server list. Do you want this buddy to be added?"
+msgstr "%s er på den lokale liste, men ikke på serverlisten. Vil du have denne ven tilføjet?"
msgid "Other Contacts"
msgstr "Andre kontakter"
@@ -5372,8 +5005,7 @@ msgstr "Kontakter uden for beskedsystem"
# Hmmm, egentlig burde det jo være "at blinke med øjet". Nogen ideer?
#, c-format
msgid "%s sent a wink. <a href='msn-wink://%s'>Click here to play it</a>"
-msgstr ""
-"%s sendte et vink. <a href='msn-wink://%s'>Tryk her for at afspille det</a>"
+msgstr "%s sendte et vink. <a href='msn-wink://%s'>Tryk her for at afspille det</a>"
#, c-format
msgid "%s sent a wink, but it could not be saved"
@@ -5381,8 +5013,7 @@ msgstr "%s sendte et vink, men det kunne ikke gemmes"
#, c-format
msgid "%s sent a voice clip. <a href='audio://%s'>Click here to play it</a>"
-msgstr ""
-"%s sendte at stemmeklip. <a href='audio://%s'>Klik her for at spille det</a>"
+msgstr "%s sendte at stemmeklip. <a href='audio://%s'>Klik her for at spille det</a>"
#, c-format
msgid "%s sent a voice clip, but it could not be saved"
@@ -5390,8 +5021,7 @@ msgstr "%s har sendt et stemmeklip, men det kunne ikke gemmes"
#, c-format
msgid "%s sent you a voice chat invite, which is not yet supported."
-msgstr ""
-"%s har inviteret dig til stemmesamtale, hvilket endnu ikke understøttes."
+msgstr "%s har inviteret dig til stemmesamtale, hvilket endnu ikke understøttes."
msgid "Nudge"
msgstr "Puf"
@@ -5405,7 +5035,7 @@ msgid "Nudging %s..."
msgstr "Puffer %s..."
msgid "Email Address..."
-msgstr "E-post-adresse..."
+msgstr "E-mail-adresse..."
msgid "Your new MSN friendly name is too long."
msgstr "Dit nye synlige MSN-navn er for langt."
@@ -5444,9 +5074,7 @@ msgstr "Du er ikke logget ind fra andre placeringer."
msgid "Allow multiple logins?"
msgstr "Tillad flere samtidige indlogninger?"
-msgid ""
-"Do you want to allow or disallow connecting from multiple locations "
-"simultaneously?"
+msgid "Do you want to allow or disallow connecting from multiple locations simultaneously?"
msgstr "Vil du tillade eller nægte forbindelser fra flere steder samtidig?"
msgid "Allow"
@@ -5467,12 +5095,8 @@ msgstr "Sæt mobiltelefonnummer."
msgid "Allow MSN Mobile pages?"
msgstr "Tillad MSN Mobile-sider?"
-msgid ""
-"Do you want to allow or disallow people on your buddy list to send you MSN "
-"Mobile pages to your cell phone or other mobile device?"
-msgstr ""
-"Vil du tillade eller nægte folk på din venneliste at sende dig MSN Mobile-"
-"sider til din mobiltelefon eller andre mobilenheder?"
+msgid "Do you want to allow or disallow people on your buddy list to send you MSN Mobile pages to your cell phone or other mobile device?"
+msgstr "Vil du tillade eller nægte folk på din venneliste at sende dig MSN Mobile-sider til din mobiltelefon eller andre mobilenheder?"
#, c-format
msgid "Blocked Text for %s"
@@ -5482,12 +5106,11 @@ msgid "No text is blocked for this account."
msgstr "Ingen tekst er blokeret for denne konto."
#, c-format
-msgid ""
-"MSN servers are currently blocking the following regular expressions:<br/>%s"
+msgid "MSN servers are currently blocking the following regular expressions:<br/>%s"
msgstr "MSN-servere blokerer følgende regulære udtryk:<br/>%s"
msgid "This account does not have email enabled."
-msgstr "Denne konto har ikke e-post aktiveret."
+msgstr "Denne konto har ikke e-mail aktiveret."
msgid "Send a mobile message."
msgstr "Send en mobilbesked."
@@ -5566,17 +5189,11 @@ msgid "Send to Mobile"
msgstr "Send til Mobile"
msgid "SSL support is needed for MSN. Please install a supported SSL library."
-msgstr ""
-"MSN kræver SSL-understøttelse. Installér venligst et understøttet SSL-"
-"bibliotek."
+msgstr "MSN kræver SSL-understøttelse. Installér venligst et understøttet SSL-bibliotek."
#, c-format
-msgid ""
-"Unable to add the buddy %s because the username is invalid. Usernames must "
-"be valid email addresses."
-msgstr ""
-"Ude af stand til at tilføje ven %s fordi brugernavnet er ugyldigt. "
-"Brugernavne skal være gyldige e-post-adresser."
+msgid "Unable to add the buddy %s because the username is invalid. Usernames must be valid email addresses."
+msgstr "Ude af stand til at tilføje ven %s fordi brugernavnet er ugyldigt. Brugernavne skal være gyldige e-mail-adresser."
msgid "Unable to Add"
msgstr "Ikke i stand til at tilføje"
@@ -5658,7 +5275,7 @@ msgid "Home Fax"
msgstr "Hjemmefax"
msgid "Personal Email"
-msgstr "E-post - privat"
+msgstr "E-mail - privat"
msgid "Personal IM"
msgstr "Beskeder - privat"
@@ -5698,7 +5315,7 @@ msgid "Work Fax"
msgstr "Arbejdsfax"
msgid "Work Email"
-msgstr "E-post - arbejde"
+msgstr "E-mail - arbejde"
msgid "Work IM"
msgstr "Beskeder - arbejde"
@@ -5719,21 +5336,11 @@ msgstr "Hjemmeside"
msgid "The user has not created a public profile."
msgstr "Brugeren har ikke oprettet en offentlig profil."
-msgid ""
-"MSN reported not being able to find the user's profile. This either means "
-"that the user does not exist, or that the user exists but has not created a "
-"public profile."
-msgstr ""
-"MSN rapporterede tilbage, at den ikke kunne finde brugerens profil. Dette "
-"betyder enten at brugeren ikke findes, eller at brugeren ikke har oprettet "
-"en offentlig profil."
+msgid "MSN reported not being able to find the user's profile. This either means that the user does not exist, or that the user exists but has not created a public profile."
+msgstr "MSN rapporterede tilbage, at den ikke kunne finde brugerens profil. Dette betyder enten at brugeren ikke findes, eller at brugeren ikke har oprettet en offentlig profil."
-msgid ""
-"Could not find any information in the user's profile. The user most likely "
-"does not exist."
-msgstr ""
-"Kunne ikke finde nogen oplysninger i brugerens profil. Brugeren findes "
-"sandsynligvis ikke."
+msgid "Could not find any information in the user's profile. The user most likely does not exist."
+msgstr "Kunne ikke finde nogen oplysninger i brugerens profil. Brugeren findes sandsynligvis ikke."
msgid "View web profile"
msgstr "Se internetprofil"
@@ -5800,38 +5407,24 @@ msgstr "Mobilbesked blev ikke sendt, fordi der opstod en ukendt fejl."
#, c-format
msgid ""
-"The MSN server will shut down for maintenance in %d minute. You will "
-"automatically be signed out at that time. Please finish any conversations "
-"in progress.\n"
+"The MSN server will shut down for maintenance in %d minute. You will automatically be signed out at that time. Please finish any conversations in progress.\n"
"\n"
-"After the maintenance has been completed, you will be able to successfully "
-"sign in."
+"After the maintenance has been completed, you will be able to successfully sign in."
msgid_plural ""
-"The MSN server will shut down for maintenance in %d minutes. You will "
-"automatically be signed out at that time. Please finish any conversations "
-"in progress.\n"
+"The MSN server will shut down for maintenance in %d minutes. You will automatically be signed out at that time. Please finish any conversations in progress.\n"
"\n"
-"After the maintenance has been completed, you will be able to successfully "
-"sign in."
+"After the maintenance has been completed, you will be able to successfully sign in."
msgstr[0] ""
-"MSN-serveren vil blive lukket ned for vedligeholdelse om %d minut. Du vil "
-"automatisk blive logget af på det tidspunkt. Færdiggør alle igangværende "
-"samtaler.\n"
+"MSN-serveren vil blive lukket ned for vedligeholdelse om %d minut. Du vil automatisk blive logget af på det tidspunkt. Færdiggør alle igangværende samtaler.\n"
"\n"
"Efter vedligeholdelsen er blevet gennemført, kan du logge ind igen."
msgstr[1] ""
-"MSN-serveren vil blive lukket ned for vedligeholdelse om %d minutter. Du vil "
-"automatisk blive logget af på det tidspunkt. Færdiggør alle igangværende "
-"samtaler.\n"
+"MSN-serveren vil blive lukket ned for vedligeholdelse om %d minutter. Du vil automatisk blive logget af på det tidspunkt. Færdiggør alle igangværende samtaler.\n"
"\n"
"Efter vedligeholdelsen er blevet gennemført, kan du logge ind igen."
-msgid ""
-"Message was not sent because the system is unavailable. This normally "
-"happens when the user is blocked or does not exist."
-msgstr ""
-"Besked blev ikke sendt, fordi systemet ikke er tilgængeligt. Dette sker "
-"normalt når brugeren er blokeret eller ikke findes."
+msgid "Message was not sent because the system is unavailable. This normally happens when the user is blocked or does not exist."
+msgstr "Besked blev ikke sendt, fordi systemet ikke er tilgængeligt. Dette sker normalt når brugeren er blokeret eller ikke findes."
msgid "Message was not sent because messages are being sent too quickly."
msgstr "Besked blev ikke sendt, fordi beskederne sendes for hurtigt."
@@ -5843,28 +5436,16 @@ msgid "Message was not sent because an unknown error occurred."
msgstr "Besked blev ikke sendt, da der opstod en ukendt fejl."
#, c-format
-msgid ""
-"%s (There was an error receiving this message. Converting the encoding from "
-"%s to UTF-8 failed.)"
-msgstr ""
-"%s (Der opstod en fejl ved modtagelse af denne besked. Konvertering af "
-"karaktersættet fra %s til UTF-8 fejlede.)"
+msgid "%s (There was an error receiving this message. Converting the encoding from %s to UTF-8 failed.)"
+msgstr "%s (Der opstod en fejl ved modtagelse af denne besked. Konvertering af tegnsættet fra %s til UTF-8 mislykkedes.)"
#, c-format
-msgid ""
-"%s (There was an error receiving this message. The charset was %s, but it "
-"was not valid UTF-8.)"
-msgstr ""
-"%s (Der opstod en fejl ved modtagelse af denne besked. Karaktersættet var "
-"%s, men var ikke gyldig UTF-8.)"
+msgid "%s (There was an error receiving this message. The charset was %s, but it was not valid UTF-8.)"
+msgstr "%s (Der opstod en fejl ved modtagelse af denne besked. Tegnsættet var %s, men var ikke gyldig UTF-8.)"
#, c-format
-msgid ""
-"%s (There was an error receiving this message. The charset was missing, but "
-"it was not valid UTF-8.)"
-msgstr ""
-"%s (Der opstod en fejl ved modtagelse af denne besked. Karaktersættet "
-"manglede, men den var ikke gyldig UTF-8.)"
+msgid "%s (There was an error receiving this message. The charset was missing, but it was not valid UTF-8.)"
+msgstr "%s (Der opstod en fejl ved modtagelse af denne besked. Tegnsættet manglede, men den var ikke gyldig UTF-8.)"
msgid "Writing error"
msgstr "Skrivefejl"
@@ -5890,9 +5471,7 @@ msgid "You have signed on from another location"
msgstr "Du er logget ind fra en anden maskine"
msgid "The MSN servers are temporarily unavailable. Please wait and try again."
-msgstr ""
-"MSN-serverne er midlertidigt utilgængelige. Vent venligst og forsøg igen "
-"senere."
+msgstr "MSN-serverne er midlertidigt utilgængelige. Vent venligst og forsøg igen senere."
msgid "The MSN servers are going down temporarily"
msgstr "MSN-serverne lukkes ned midlertidigt"
@@ -5901,11 +5480,8 @@ msgstr "MSN-serverne lukkes ned midlertidigt"
msgid "Unable to authenticate: %s"
msgstr "Kan ikke godkende: %s"
-msgid ""
-"Your MSN buddy list is temporarily unavailable. Please wait and try again."
-msgstr ""
-"Din MSN-venneliste er midlertidigt utilgængelig. Vent venligst og forsøg "
-"igen senere."
+msgid "Your MSN buddy list is temporarily unavailable. Please wait and try again."
+msgstr "Din MSN-venneliste er midlertidigt utilgængelig. Vent venligst og forsøg igen senere."
msgid "Handshaking"
msgstr "Forhandler"
@@ -5931,9 +5507,7 @@ msgstr "%s beder om at se dit webcam, men dette er endnu ikke er understøttet."
#, c-format
msgid "%s invited you to view his/her webcam, but this is not yet supported."
-msgstr ""
-"%s har inviteret dig til at se hans/hendes webcam, men dette er endnu ikke "
-"understøttet."
+msgstr "%s har inviteret dig til at se hans/hendes webcam, men dette er endnu ikke understøttet."
msgid "Away From Computer"
msgstr "Væk fra computeren"
@@ -5959,16 +5533,10 @@ msgstr "Besked kunne ikke sendes; der opstod en forbindelsesfejl:"
msgid "Message could not be sent because we are sending too quickly:"
msgstr "Besked kunne ikke sendes; vi sender for hurtigt:"
-msgid ""
-"Message could not be sent because we were unable to establish a session with "
-"the server. This is likely a server problem, try again in a few minutes:"
-msgstr ""
-"Besked kunne ikke sendes; vi ikke kunne etablere en session med serveren. "
-"Dette er højst sandsynligt et problem med serveren. Prøv igen om nogle få "
-"minutter:"
+msgid "Message could not be sent because we were unable to establish a session with the server. This is likely a server problem, try again in a few minutes:"
+msgstr "Besked kunne ikke sendes; vi ikke kunne etablere en session med serveren. Dette er højst sandsynligt et problem med serveren. Prøv igen om nogle få minutter:"
-msgid ""
-"Message could not be sent because an error with the switchboard occurred:"
+msgid "Message could not be sent because an error with the switchboard occurred:"
msgstr "Besked kunne ikke sendes; fejl med switchboard:"
msgid "Message may have not been sent because an unknown error occurred:"
@@ -5986,10 +5554,8 @@ msgstr "Brugernavnet du angav er ugyldigt."
msgid "The Display Name you entered is invalid."
msgstr "Kaldenavnet, du indtastede, er ugyldigt."
-msgid ""
-"The birthday you entered is invalid. The correct format is: 'YYYY-MM-DD'."
-msgstr ""
-"Den indtastede fødselsdag er ugyldig. Det rigtige format er: \"ÅÅÅÅ-MM-DD\"."
+msgid "The birthday you entered is invalid. The correct format is: 'YYYY-MM-DD'."
+msgstr "Den indtastede fødselsdag er ugyldig. Det rigtige format er: \"ÅÅÅÅ-MM-DD\"."
#. show error to user
msgid "Profile Update Error"
@@ -6001,8 +5567,7 @@ msgid "Profile"
msgstr "Profil"
msgid "Your profile information is not yet retrieved. Please try again later."
-msgstr ""
-"Din profils informationer er endnu ikke hentet. Prøv venligst igen senere."
+msgstr "Din profils informationer er endnu ikke hentet. Prøv venligst igen senere."
#. display name
#. nick name (required)
@@ -6027,14 +5592,14 @@ msgstr "Mobiltelefonnummer"
#. is searchable
msgid "Can be searched"
-msgstr "Kan blive søgt"
+msgstr "Kan gennemsøges"
#. is suggestable
msgid "Can be suggested"
-msgstr "Kan blive forslået"
+msgstr "Kan blive foreslåede"
msgid "Update your MXit Profile"
-msgstr "Opdatér din MXit profil"
+msgstr "Opdatér din MXit-profil"
msgid "The PIN you entered is invalid."
msgstr "Den indtastede PIN-kode er ugyldig."
@@ -6066,7 +5631,7 @@ msgid "Change PIN"
msgstr "Ændr PIN"
msgid "Change MXit PIN"
-msgstr "Ændr MXit PIN"
+msgstr "Ændr MXitPIN"
msgid "View Splash"
msgstr "Vis opstartsskærm"
@@ -6081,7 +5646,7 @@ msgid "Search for user"
msgstr "Søg efter bruger"
msgid "Search for a MXit contact"
-msgstr "Søg efter en MXit kontakt"
+msgstr "Søg efter en MXit-kontakt"
msgid "Type search information"
msgstr "Indtast søgeinformationer"
@@ -6099,7 +5664,7 @@ msgstr "Ændr PIN..."
#. suggested friends
msgid "Suggested friends..."
-msgstr "Forslået venner..."
+msgstr "Forslåede venner..."
#. search for contacts
msgid "Search for contacts..."
@@ -6119,26 +5684,20 @@ msgstr "Filen, du prøver at sende, er for stor!"
#. file read error
msgid "Unable to access the local file"
-msgstr "Kunne ikke indlæse den lokale fil"
+msgstr "Kan ikke indlæse den lokale fil"
#. file write error
msgid "Unable to save the file"
-msgstr "Kunne ikke gemme filen"
+msgstr "Kan ikke gemme filen"
-msgid ""
-"Unable to connect to the MXit HTTP server. Please check your server settings."
-msgstr ""
-"Ude af stand til at forbinde til MXit-serveren. Tjek venligst dine "
-"serverindstillinger."
+msgid "Unable to connect to the MXit HTTP server. Please check your server settings."
+msgstr "Ude af stand til at forbinde til MXit-serveren. Tjek venligst dine serverindstillinger."
msgid "Logging In..."
msgstr "Logger ind..."
-msgid ""
-"Unable to connect to the MXit server. Please check your server settings."
-msgstr ""
-"Ude af stand til at forbinde til MXit-serveren. Tjek venligst dine "
-"serverindstillinger."
+msgid "Unable to connect to the MXit server. Please check your server settings."
+msgstr "Ude af stand til at forbinde til MXit-serveren. Tjek venligst dine serverindstillinger."
msgid "Connecting..."
msgstr "Forbinder..."
@@ -6166,11 +5725,8 @@ msgstr "Fejl ved forbindelse med MXit WAP-stedet. Prøv venligst igen senere."
#. wapserver error
#. server could not find the user
-msgid ""
-"MXit is currently unable to process the request. Please try again later."
-msgstr ""
-"MXit er i øjeblikket ude af stand til at afvikle forspørgelsen. Prøv "
-"venligst igen senere."
+msgid "MXit is currently unable to process the request. Please try again later."
+msgstr "MXit er i øjeblikket ude af stand til at afvikle forspørgelsen. Prøv venligst igen senere."
msgid "Wrong security code entered. Please try again later."
msgstr "Den indtastede sikkerhedskode er forkert. Prøv venligst igen senere."
@@ -6185,8 +5741,7 @@ msgid "The MXit ID you entered is not registered. Please register first."
msgstr "Det indtastede MXit-id er ikke registreret. Registrér venligst først."
msgid "The MXit ID you entered is already registered. Please choose another."
-msgstr ""
-"Det indtastede MXit-id er allerede registreret. Vælg venligst et andet."
+msgstr "Det indtastede MXit-id er allerede registreret. Vælg venligst et andet."
msgid "Internal error. Please try again later."
msgstr "Intern fejl. Prøv venligst igen senere."
@@ -6243,7 +5798,7 @@ msgid "No profile available"
msgstr "Ingen profil tilgængelig"
msgid "This contact does not have a profile."
-msgstr "Denne konto har ikke en profil."
+msgstr "Denne Kontakt har ikke nogen profil."
msgid "Your MXit ID..."
msgstr "Dit MXit-id..."
@@ -6312,8 +5867,8 @@ msgstr "Hvor jeg bor"
#, c-format
msgid "You have %i suggested friend."
msgid_plural "You have %i suggested friends."
-msgstr[0] "Du har %i forslået ven."
-msgstr[1] "Du har %i forslået venner."
+msgstr[0] "Du har %i foreslåede ven."
+msgstr[1] "Du har %i foreslåede venner."
#, c-format
msgid "We found %i contact that matches your search."
@@ -6339,18 +5894,15 @@ msgid "Successfully Logged In..."
msgstr "Loggede ind..."
#, c-format
-msgid ""
-"%s sent you an encrypted message, but it is not supported on this client."
-msgstr ""
-"%s har sendt dig en krypteret besked, men dette understøttes ikke på denne "
-"klient."
+msgid "%s sent you an encrypted message, but it is not supported on this client."
+msgstr "%s har sendt dig en krypteret besked, men dette understøttes ikke på denne klient."
msgid "Message Error"
msgstr "Beskedfejl"
#. could not be decrypted
msgid "An encrypted message was received which could not be decrypted."
-msgstr "En krypterede besked blev modtaget, som ikke kan dekrypteres."
+msgstr "Der blev modtaget en krypteret besked, som ikke kan dekrypteres."
msgid "Cannot perform redirect using the specified protocol"
msgstr "Kan ikke viderstille med den angivne protokol"
@@ -6364,7 +5916,7 @@ msgstr "Logind-fejl: %s (%i)"
#, c-format
msgid "Logout error: %s (%i)"
-msgstr "Log ud-fejl: %s (%i)"
+msgstr "Logud-fejl: %s (%i)"
msgid "Contact Error"
msgstr "Kontaktfejl"
@@ -6391,7 +5943,7 @@ msgid "Contact Update Error"
msgstr "Fejl ved opdatering af kontakt"
msgid "File Transfer Error"
-msgstr "Filoverførselsfejl"
+msgstr "Fejl ved overførelse af fil"
msgid "Cannot create MultiMx room"
msgstr "Kan ikke oprette MultiMx-rum"
@@ -6482,10 +6034,10 @@ msgstr "Mistede forbindelsen til server"
#. khc: then use N_() in the array initializer and use _() when they are
#. used
msgid "New mail messages"
-msgstr "Nye e-postbeskeder"
+msgstr "Nye e-mail-beskeder"
msgid "New blog comments"
-msgstr "Nye blog-kommentarer"
+msgstr "Nye blogkommentarer"
msgid "New profile comments"
msgstr "Nye profilkommentarer"
@@ -6503,18 +6055,10 @@ msgid "IM Friends"
msgstr "IM-venner"
#, c-format
-msgid ""
-"%d buddy was added or updated from the server (including buddies already on "
-"the server-side list)"
-msgid_plural ""
-"%d buddies were added or updated from the server (including buddies already "
-"on the server-side list)"
-msgstr[0] ""
-"%d ven blev tilføjet eller opdateret fra serveren (inklusive venner som "
-"allerede findes på serverlisten)"
-msgstr[1] ""
-"%d venner blev tilføjet eller opdateret fra serveren (inklusive venner som "
-"allerede findes på serverlisten)"
+msgid "%d buddy was added or updated from the server (including buddies already on the server-side list)"
+msgid_plural "%d buddies were added or updated from the server (including buddies already on the server-side list)"
+msgstr[0] "%d ven blev tilføjet eller opdateret fra serveren (inklusive venner som allerede findes på serverlisten)"
+msgstr[1] "%d venner blev tilføjet eller opdateret fra serveren (inklusive venner som allerede findes på serverlisten)"
msgid "Add contacts from server"
msgstr "Tilføj kontakter fra server"
@@ -6524,14 +6068,8 @@ msgid "Protocol error, code %d: %s"
msgstr "Protokolfejl, kode %d: %s"
#, c-format
-msgid ""
-"%s Your password is %zu characters, which is longer than the maximum length "
-"of %d. Please shorten your password at http://profileedit.myspace.com/index."
-"cfm?fuseaction=accountSettings.changePassword and try again."
-msgstr ""
-"%s Din adgangskode er %zu tegn og dermed længere end maksimum på %d. Forkort "
-"venligst din adgangskode på http://profileedit.myspace.com/index.cfm?"
-"fuseaction=accountSettings.changePassword og prøv igen."
+msgid "%s Your password is %zu characters, which is longer than the maximum length of %d. Please shorten your password at http://profileedit.myspace.com/index.cfm?fuseaction=accountSettings.changePassword and try again."
+msgstr "%s Din adgangskode er %zu tegn og dermed længere end maksimum på %d. Forkort venligst din adgangskode på http://profileedit.myspace.com/index.cfm?fuseaction=accountSettings.changePassword og prøv igen."
msgid "Incorrect username or password"
msgstr "Forkert brugernavn eller adgangskode"
@@ -6566,12 +6104,8 @@ msgstr "Manglende kode"
msgid "The RC4 cipher could not be found"
msgstr "RC4-kode kunne ikke findes"
-msgid ""
-"Upgrade to a libpurple with RC4 support (>= 2.0.1). MySpaceIM plugin will "
-"not be loaded."
-msgstr ""
-"Opgradér til en libpurple med RC4-understøttelse (>=2.0.1). MySpaceIM-"
-"udvidelsesmodul vil ikke blive indlæst."
+msgid "Upgrade to a libpurple with RC4 support (>= 2.0.1). MySpaceIM plugin will not be loaded."
+msgstr "Opgradér til en libpurple med RC4-understøttelse (>=2.0.1). MySpaceIM-udvidelsesmodul vil ikke blive indlæst."
msgid "Add friends from MySpace.com"
msgstr "Tilføj venner fra MySpace.com"
@@ -6590,9 +6124,7 @@ msgid "myim URL handler"
msgstr "myim-URL-håndtering"
msgid "No suitable MySpaceIM account could be found to open this myim URL."
-msgstr ""
-"Der kunne ikke findes nogen passende MySpaceIM-konto til at åbne denne myim-"
-"URL."
+msgstr "Der kunne ikke findes nogen passende MySpaceIM-konto til at åbne denne myim-URL."
msgid "Enable the proper MySpaceIM account and try again."
msgstr "Aktivér den korrekte MySpaceIM-konto og prøv igen."
@@ -6627,14 +6159,8 @@ msgstr "Venner i alt"
msgid "Client Version"
msgstr "Klientversion"
-msgid ""
-"An error occurred while trying to set the username. Please try again, or "
-"visit http://editprofile.myspace.com/index.cfm?fuseaction=profile.username "
-"to set your username."
-msgstr ""
-"Der opstod en fejl under angivelsen af brugernavnet. Prøv venligst igen, "
-"eller se http://editprofile.myspace.com/index.cfm?fuseaction=profile."
-"username for at angive dit brugernavn."
+msgid "An error occurred while trying to set the username. Please try again, or visit http://editprofile.myspace.com/index.cfm?fuseaction=profile.username to set your username."
+msgstr "Der opstod en fejl under angivelsen af brugernavnet. Prøv venligst igen, eller se http://editprofile.myspace.com/index.cfm?fuseaction=profile.username for at angive dit brugernavn."
msgid "MySpaceIM - Username Available"
msgstr "MySpaceIM - Brugernavn tilgængeligt"
@@ -6852,12 +6378,8 @@ msgstr "Hovedarkiv er konfigureret forkert"
msgid "Could not recognize the host of the username you entered"
msgstr "Kunne ikke genkende værten for det brugernavn du indtastede"
-msgid ""
-"Your account has been disabled because too many incorrect passwords were "
-"entered"
-msgstr ""
-"Din konto er blevet deaktiveret, fordi for mange ugyldige adgangskoder blev "
-"indtastet"
+msgid "Your account has been disabled because too many incorrect passwords were entered"
+msgstr "Din konto er blevet deaktiveret, fordi for mange ugyldige adgangskoder blev indtastet"
msgid "You cannot add the same person twice to a conversation"
msgstr "Du kan ikke tilføje den samme person to gange til en samtale"
@@ -6877,9 +6399,7 @@ msgstr "Inkompatibel protokolversion"
msgid "The user has blocked you"
msgstr "Brugeren har blokeret dig"
-msgid ""
-"This evaluation version does not allow more than ten users to log in at one "
-"time"
+msgid "This evaluation version does not allow more than ten users to log in at one time"
msgstr "Denne prøveversion tillader højst 10 brugere at logge ind ad gangen"
msgid "The user is either offline or you are blocked"
@@ -6919,20 +6439,12 @@ msgid "Unable to send message. Could not create the conference (%s)."
msgstr "Kan ikke sende besked. Kunne ikke oprette konferencen (%s)."
#, c-format
-msgid ""
-"Unable to move user %s to folder %s in the server side list. Error while "
-"creating folder (%s)."
-msgstr ""
-"Kan ikke flytte bruger %s til mappe %s i listen hos serveren. Der opstod en "
-"fejl ved mappeoprettelse (%s)."
+msgid "Unable to move user %s to folder %s in the server side list. Error while creating folder (%s)."
+msgstr "Kan ikke flytte bruger %s til mappe %s i listen hos serveren. Der opstod en fejl ved mappeoprettelse (%s)."
#, c-format
-msgid ""
-"Unable to add %s to your buddy list. Error creating folder in server side "
-"list (%s)."
-msgstr ""
-"Kan ikke tilføje %s til din venneliste. Der opstod en fejl ved oprettelse af "
-"mappe i listen hos serveren (%s)."
+msgid "Unable to add %s to your buddy list. Error creating folder in server side list (%s)."
+msgstr "Kan ikke tilføje %s til din venneliste. Der opstod en fejl ved oprettelse af mappe i listen hos serveren (%s)."
#, c-format
msgid "Could not get details for user %s (%s)."
@@ -7017,17 +6529,11 @@ msgid "Would you like to join the conversation?"
msgstr "Vil du deltage i samtalen?"
#, c-format
-msgid ""
-"%s appears to be offline and did not receive the message that you just sent."
-msgstr ""
-"%s ser ud til at være afkoblet, og har ikke modtaget beskeden du lige sendte."
+msgid "%s appears to be offline and did not receive the message that you just sent."
+msgstr "%s ser ud til at være afkoblet, og har ikke modtaget beskeden du lige sendte."
-msgid ""
-"Unable to connect to server. Please enter the address of the server to which "
-"you wish to connect."
-msgstr ""
-"Kan ikke forbinde til server. Indtast venligst adressen på den server, du "
-"vil forbinde til."
+msgid "Unable to connect to server. Please enter the address of the server to which you wish to connect."
+msgstr "Kan ikke forbinde til server. Indtast venligst adressen på den server, du vil forbinde til."
msgid "This conference has been closed. No more messages can be sent."
msgstr "Konferencen er blevet afsluttet. Der kan ikke sendes flere beskeder."
@@ -7074,19 +6580,11 @@ msgstr "Modtog uventet respons fra %s: %s"
msgid "Received unexpected response from %s"
msgstr "Modtog uventet respons fra %s"
-msgid ""
-"You have been connecting and disconnecting too frequently. Wait ten minutes "
-"and try again. If you continue to try, you will need to wait even longer."
-msgstr ""
-"Du er logget ind og ud for mange gange inden for et kort tidsrum. Vent ti "
-"minutter og prøv så igen, for ellers skal du bare vente endnu længere."
+msgid "You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."
+msgstr "Du er logget ind og ud for mange gange inden for et kort tidsrum. Vent ti minutter og prøv så igen, for ellers skal du bare vente endnu længere."
-msgid ""
-"You required encryption in your account settings, but one of the servers "
-"doesn't support it."
-msgstr ""
-"Du forlanger kryptering i kontoindstillingerne, men en eller flere af "
-"serverne understøtter det ikke."
+msgid "You required encryption in your account settings, but one of the servers doesn't support it."
+msgstr "Du forlanger kryptering i kontoindstillingerne, men en eller flere af serverne understøtter det ikke."
#. Note to translators: The first %s is a URL, the second is an
#. error message.
@@ -7097,34 +6595,18 @@ msgstr "Fejl ved forspørgelse %s: %s"
msgid "The server returned an empty response"
msgstr "Serveren retunerede et tomt svar"
-msgid ""
-"Server requested that you fill out a CAPTCHA in order to sign in, but this "
-"client does not currently support CAPTCHAs."
-msgstr ""
-"Serveren har bedt dig om at udfylde en CAPTCHA for at logge på, men denne "
-"klient understøtter i øjeblikket ikke CAPTCHA."
+msgid "Server requested that you fill out a CAPTCHA in order to sign in, but this client does not currently support CAPTCHAs."
+msgstr "Serveren har bedt dig om at udfylde en CAPTCHA for at logge på, men denne klient understøtter i øjeblikket ikke CAPTCHA."
msgid "AOL does not allow your screen name to authenticate here"
msgstr "AOL understøtter ikke at dit viste navn godkendes her"
-msgid ""
-"(There was an error receiving this message. The buddy you are speaking with "
-"is probably using a different encoding than expected. If you know what "
-"encoding he is using, you can specify it in the advanced account options for "
-"your AIM/ICQ account.)"
-msgstr ""
-"(Der opstod en fejl ved modtagelse af denne besked. Vennen du taler med "
-"bruger måske en anden indkodning end forventet. Hvis du kender indkodningen, "
-"som han bruger, kan du angive den i avancerede kontoindstillinger for din "
-"AIM/ICQ-konto.)"
+msgid "(There was an error receiving this message. The buddy you are speaking with is probably using a different encoding than expected. If you know what encoding he is using, you can specify it in the advanced account options for your AIM/ICQ account.)"
+msgstr "(Der opstod en fejl ved modtagelse af denne besked. Vennen du taler med bruger måske en anden indkodning end forventet. Hvis du kender indkodningen, som han bruger, kan du angive den i avancerede kontoindstillinger for din AIM/ICQ-konto.)"
#, c-format
-msgid ""
-"(There was an error receiving this message. Either you and %s have "
-"different encodings selected, or %s has a buggy client.)"
-msgstr ""
-"(Der opstod en fejl ved modtagelse af denne besked. Enten har du eller %s "
-"valgt forskellige indkodninger, eller også har %s en fejlramt klient.)"
+msgid "(There was an error receiving this message. Either you and %s have different encodings selected, or %s has a buggy client.)"
+msgstr "(Der opstod en fejl ved modtagelse af denne besked. Enten har du eller %s valgt forskellige indkodninger, eller også har %s en fejlramt klient.)"
msgid "Could not join chat room"
msgstr "Kunne ikke tilslutte samtalerum"
@@ -7328,12 +6810,8 @@ msgid "Direct IM established"
msgstr "Direkte samtale etableret"
#, c-format
-msgid ""
-"%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"
-msgstr ""
-"%s forsøgte at sende dig en %s fil, men vi tillader kun filer op til %s over "
-"direkte besked. Prøv at bruge filoverførelse i stedet for.\n"
+msgid "%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"
+msgstr "%s forsøgte at sende dig en %s fil, men vi tillader kun filer op til %s over direkte besked. Prøv at bruge filoverførelse i stedet for.\n"
#, c-format
msgid "File %s is %s, which is larger than the maximum size of %s."
@@ -7389,27 +6867,15 @@ msgid "Finalizing connection"
msgstr "Færdiggør forbindelse"
#, c-format
-msgid ""
-"Unable to sign on as %s because the username is invalid. Usernames must be "
-"a valid email address, or start with a letter and contain only letters, "
-"numbers and spaces, or contain only numbers."
-msgstr ""
-"Ude af stand til at logge på som %s fordi brugernavnet er ugyldigt. "
-"Brugernavne skal være en gyldig e-post-adresse, eller starte med et bogstav "
-"og kun indeholde bogstaver, tal eller mellemrum, eller kun indeholde tal."
+msgid "Unable to sign on as %s because the username is invalid. Usernames must be a valid email address, or start with a letter and contain only letters, numbers and spaces, or contain only numbers."
+msgstr "Ude af stand til at logge på som %s fordi brugernavnet er ugyldigt. Brugernavne skal være en gyldig e-mail-adresse, eller starte med et bogstav og kun indeholde bogstaver, tal eller mellemrum, eller kun indeholde tal."
-msgid ""
-"You required encryption in your account settings, but encryption is not "
-"supported by your system."
-msgstr ""
-"Du har forlangt kryptering i kontoindstillingerne, men kryptering er ikke "
-"understøttet af dit system."
+msgid "You required encryption in your account settings, but encryption is not supported by your system."
+msgstr "Du har forlangt kryptering i kontoindstillingerne, men kryptering er ikke understøttet af dit system."
#, c-format
msgid "You may be disconnected shortly. If so, check %s for updates."
-msgstr ""
-"Du vil måske blive frakoblet snart. Hvis dette er tilfældet, så kontrollér "
-"%s for opdateringer."
+msgstr "Du vil måske blive frakoblet snart. Hvis dette er tilfældet, så kontrollér %s for opdateringer."
msgid "Unable to get a valid AIM login hash."
msgstr "Kan ikke hente et gyldigt AIM-logindhash."
@@ -7431,17 +6897,11 @@ msgstr "Din konto er i øjeblikket suspenderet"
#. service temporarily unavailable
msgid "The AOL Instant Messenger service is temporarily unavailable."
-msgstr "AOLs kvikbeskeds Messenger-tjeneste er midlertidigt utilgængelig."
+msgstr "AOL's lynbeskeds Messenger-tjeneste er midlertidigt utilgængelig."
#. username connecting too frequently
-msgid ""
-"Your username has been connecting and disconnecting too frequently. Wait ten "
-"minutes and try again. If you continue to try, you will need to wait even "
-"longer."
-msgstr ""
-"Dit brugernavn er logget ind og ud for mange gange inden for et kort "
-"tidsrum. Vent ti minutter og prøv så igen, for ellers skal du bare vente "
-"endnu længere."
+msgid "Your username has been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."
+msgstr "Dit brugernavn er logget ind og ud for mange gange inden for et kort tidsrum. Vent ti minutter og prøv så igen, for ellers skal du bare vente endnu længere."
#. client too old
#, c-format
@@ -7449,14 +6909,8 @@ msgid "The client version you are using is too old. Please upgrade at %s"
msgstr "Din klient er for gammel. Opgradér venligst på %s"
#. IP address connecting too frequently
-msgid ""
-"Your IP address has been connecting and disconnecting too frequently. Wait a "
-"minute and try again. If you continue to try, you will need to wait even "
-"longer."
-msgstr ""
-"Din IP-adresse er logget ind og ud for mange gange inden for et kort "
-"tidsrum. Vent ti minutter og prøv så igen, for ellers skal du bare vente "
-"endnu længere."
+msgid "Your IP address has been connecting and disconnecting too frequently. Wait a minute and try again. If you continue to try, you will need to wait even longer."
+msgstr "Din IP-adresse er logget ind og ud for mange gange inden for et kort tidsrum. Vent ti minutter og prøv så igen, for ellers skal du bare vente endnu længere."
msgid "The SecurID key entered is invalid"
msgstr "Den indtastede SecurID-nøgle ugyldig"
@@ -7475,12 +6929,10 @@ msgstr "Kan ikke initialisere forbindelse"
#, c-format
msgid ""
-"The user %u has denied your request to add them to your buddy list for the "
-"following reason:\n"
+"The user %u has denied your request to add them to your buddy list for the following reason:\n"
"%s"
msgstr ""
-"Din anmodning om at føje brugeren %u til din venneliste er blevet afvist med "
-"følgende grund:\n"
+"Din anmodning om at føje brugeren %u til din venneliste er blevet afvist med følgende grund:\n"
"%s"
msgid "ICQ authorization denied."
@@ -7522,7 +6974,7 @@ msgid ""
"Message is:\n"
"%s"
msgstr ""
-"Du har modtaget ICQ-e-post fra %s [%s]\n"
+"Du har modtaget en ICQ-e-mail fra %s [%s]\n"
"\n"
"Beskeden er:\n"
"%s"
@@ -7553,43 +7005,27 @@ msgstr[0] "Du har ikke modtaget %hu besked fra %s, fordi den var for store."
msgstr[1] "Du har ikke modtaget %hu beskeder fra %s, fordi de var for store."
#, c-format
-msgid ""
-"You missed %hu message from %s because the rate limit has been exceeded."
-msgid_plural ""
-"You missed %hu messages from %s because the rate limit has been exceeded."
-msgstr[0] ""
-"Du har ikke modtaget %hu besked fra %s, fordi den blev sendt for hurtigt."
-msgstr[1] ""
-"Du har ikke modtaget %hu beskeder fra %s, fordi de blev sendt for hurtigt."
+msgid "You missed %hu message from %s because the rate limit has been exceeded."
+msgid_plural "You missed %hu messages from %s because the rate limit has been exceeded."
+msgstr[0] "Du har ikke modtaget %hu besked fra %s, fordi den blev sendt for hurtigt."
+msgstr[1] "Du har ikke modtaget %hu beskeder fra %s, fordi de blev sendt for hurtigt."
#, c-format
-msgid ""
-"You missed %hu message from %s because his/her warning level is too high."
-msgid_plural ""
-"You missed %hu messages from %s because his/her warning level is too high."
-msgstr[0] ""
-"Du har ikke modtaget %hu besked fra %s, fordi hans/hendes advarselsniveau er "
-"for højt."
-msgstr[1] ""
-"Du har ikke modtaget %hu beskeder fra %s, fordi hans/hendes advarselsniveau "
-"er for højt."
+msgid "You missed %hu message from %s because his/her warning level is too high."
+msgid_plural "You missed %hu messages from %s because his/her warning level is too high."
+msgstr[0] "Du har ikke modtaget %hu besked fra %s, fordi hans/hendes advarselsniveau er for højt."
+msgstr[1] "Du har ikke modtaget %hu beskeder fra %s, fordi hans/hendes advarselsniveau er for højt."
#, c-format
msgid "You missed %hu message from %s because your warning level is too high."
-msgid_plural ""
-"You missed %hu messages from %s because your warning level is too high."
-msgstr[0] ""
-"Du har ikke modtaget %hu afsendt besked fra %s, fordi dit advarselsniveau er "
-"for højt."
-msgstr[1] ""
-"Du har ikke modtaget %hu afsendte beskeder fra %s, fordi dit advarselsniveau "
-"er for højt."
+msgid_plural "You missed %hu messages from %s because your warning level is too high."
+msgstr[0] "Du har ikke modtaget %hu afsendt besked fra %s, fordi dit advarselsniveau er for højt."
+msgstr[1] "Du har ikke modtaget %hu afsendte beskeder fra %s, fordi dit advarselsniveau er for højt."
#, c-format
msgid "You missed %hu message from %s for an unknown reason."
msgid_plural "You missed %hu messages from %s for an unknown reason."
-msgstr[0] ""
-"Du har ikke modtaget %hu besked fra %s som følge af en ukendt årsag."
+msgstr[0] "Du har ikke modtaget %hu besked fra %s som følge af en ukendt årsag."
msgstr[1] "Du har ikke modtaget %hu afsendte beskeder fra %s af ukendt årsag."
msgid "Your AIM connection may be lost."
@@ -7603,8 +7039,7 @@ msgid "The new formatting is invalid."
msgstr "Den nye formatering er ugyldig."
msgid "Username formatting can change only capitalization and whitespace."
-msgstr ""
-"Formatering af brugernavn kan kun ændre store/små bogstaver og mellemrum."
+msgstr "Formatering af brugernavn kan kun ændre store/små bogstaver og mellemrum."
msgid "Pop-Up Message"
msgstr "Pop op-besked"
@@ -7617,7 +7052,7 @@ msgstr[1] "Følgende brugernavne er tilknyttet %s"
#, c-format
msgid "No results found for email address %s"
-msgstr "Ingen resultater fundet for e-post-adressen %s"
+msgstr "Ingen resultater fundet for e-mail-adressen %s"
#, c-format
msgid "You should receive an email asking to confirm %s."
@@ -7627,48 +7062,28 @@ msgid "Account Confirmation Requested"
msgstr "Der er anmodet om kontobekræftelse"
#, c-format
-msgid ""
-"Error 0x%04x: Unable to format username because the requested name differs "
-"from the original."
-msgstr ""
-"Fejl 0x%04x: Kan ikke formatere brugernavn, fordi det anmodede brugernavn er "
-"forskelligt fra det oprindelige."
+msgid "Error 0x%04x: Unable to format username because the requested name differs from the original."
+msgstr "Fejl 0x%04x: Kan ikke formatere brugernavn, fordi det anmodede brugernavn er forskelligt fra det oprindelige."
#, c-format
msgid "Error 0x%04x: Unable to format username because it is invalid."
msgstr "Fejl 0x%04x: Kan ikke formatere brugernavn, fordi navnet er ugyldigt."
#, c-format
-msgid ""
-"Error 0x%04x: Unable to format username because the requested name is too "
-"long."
-msgstr ""
-"Fejl 0x%04x: Kan ikke formatere brugernavn, fordi det anmodede navn er for "
-"langt."
+msgid "Error 0x%04x: Unable to format username because the requested name is too long."
+msgstr "Fejl 0x%04x: Kan ikke formatere brugernavn, fordi det anmodede navn er for langt."
#, c-format
-msgid ""
-"Error 0x%04x: Unable to change email address because there is already a "
-"request pending for this username."
-msgstr ""
-"Fejl 0x%04x: Kan ikke ændre e-post-adresse, fordi der allerede er en "
-"ventende anmodning for dette brugernavn."
+msgid "Error 0x%04x: Unable to change email address because there is already a request pending for this username."
+msgstr "Fejl 0x%04x: Kan ikke ændre e-mail-adresse, fordi der allerede er en ventende anmodning for dette brugernavn."
#, c-format
-msgid ""
-"Error 0x%04x: Unable to change email address because the given address has "
-"too many usernames associated with it."
-msgstr ""
-"Fejl 0x%04x: Kan ikke ændre e-post-adresse, fordi den angivne adresse har "
-"for mange brugernavne tilknyttet sig."
+msgid "Error 0x%04x: Unable to change email address because the given address has too many usernames associated with it."
+msgstr "Fejl 0x%04x: Kan ikke ændre e-mail-adresse, fordi den angivne adresse har for mange brugernavne tilknyttet sig."
#, c-format
-msgid ""
-"Error 0x%04x: Unable to change email address because the given address is "
-"invalid."
-msgstr ""
-"Fejl 0x%04x: Kan ikke ændre e-post-adresse, fordi den angivne adresse er "
-"ugyldig."
+msgid "Error 0x%04x: Unable to change email address because the given address is invalid."
+msgstr "Fejl 0x%04x: Kan ikke ændre e-mail-adresse, fordi den angivne adresse er ugyldig."
#, c-format
msgid "Error 0x%04x: Unknown error."
@@ -7679,94 +7094,54 @@ msgstr "Fejl ved ændring af kontooplysninger"
#, c-format
msgid "The email address for %s is %s"
-msgstr "%ss e-post-adresse er %s"
+msgstr "E-mail-adressen for %s er %s"
msgid "Account Info"
msgstr "Kontooplysninger"
-msgid ""
-"Your IM Image was not sent. You must be Direct Connected to send IM Images."
-msgstr ""
-"Dit billede blev ikke sendt. Du skal være direkte forbundet for at sende "
-"billeder."
+msgid "Your IM Image was not sent. You must be Direct Connected to send IM Images."
+msgstr "Dit billede blev ikke sendt. Du skal være direkte forbundet for at sende billeder."
msgid "Unable to set AIM profile."
msgstr "Kan ikke ændre AIM-profil."
-msgid ""
-"You have probably requested to set your profile before the login procedure "
-"completed. Your profile remains unset; try setting it again when you are "
-"fully connected."
-msgstr ""
-"Du har sikkert anmodet om at sætte din profil før logind-processen er blevet "
-"færdig. Din profil er ikke sat; prøv at sætte den igen når du er færdig med "
-"at forbinde til serveren."
+msgid "You have probably requested to set your profile before the login procedure completed. Your profile remains unset; try setting it again when you are fully connected."
+msgstr "Du har sikkert anmodet om at sætte din profil før logind-processen er blevet færdig. Din profil er ikke sat; prøv at sætte den igen når du er færdig med at forbinde til serveren."
#, c-format
-msgid ""
-"The maximum profile length of %d byte has been exceeded. It has been "
-"truncated for you."
-msgid_plural ""
-"The maximum profile length of %d bytes has been exceeded. It has been "
-"truncated for you."
-msgstr[0] ""
-"Profil-længdens maksimale størrelse på %d byte er blevet overskredet. Den "
-"er blevet forkortet for dig."
-msgstr[1] ""
-"Profil-længdens maksimale størrelse på %d byte er blevet overskredet. Den "
-"er blevet forkortet for dig."
+msgid "The maximum profile length of %d byte has been exceeded. It has been truncated for you."
+msgid_plural "The maximum profile length of %d bytes has been exceeded. It has been truncated for you."
+msgstr[0] "Profil-længdens maksimale størrelse på %d byte er blevet overskredet. Den er blevet forkortet for dig."
+msgstr[1] "Profil-længdens maksimale størrelse på %d byte er blevet overskredet. Den er blevet forkortet for dig."
msgid "Profile too long."
msgstr "Profil for lang."
#, c-format
-msgid ""
-"The maximum away message length of %d byte has been exceeded. It has been "
-"truncated for you."
-msgid_plural ""
-"The maximum away message length of %d bytes has been exceeded. It has been "
-"truncated for you."
-msgstr[0] ""
-"Fraværsbeskedens maksimale længde på %d byte er blevet overskredet. Den er "
-"blevet forkortet for dig."
-msgstr[1] ""
-"Fraværsbeskedens maksimale længde på %d byte er blevet overskredet. Den er "
-"blevet forkortet for dig."
+msgid "The maximum away message length of %d byte has been exceeded. It has been truncated for you."
+msgid_plural "The maximum away message length of %d bytes has been exceeded. It has been truncated for you."
+msgstr[0] "Fraværsbeskedens maksimale længde på %d byte er blevet overskredet. Den er blevet forkortet for dig."
+msgstr[1] "Fraværsbeskedens maksimale længde på %d byte er blevet overskredet. Den er blevet forkortet for dig."
msgid "Away message too long."
msgstr "Fraværsbesked for lang."
#, c-format
-msgid ""
-"Unable to add the buddy %s because the username is invalid. Usernames must "
-"be a valid email address, or start with a letter and contain only letters, "
-"numbers and spaces, or contain only numbers."
-msgstr ""
-"Ude af stand til at tilføje vennen %s, fordi brugernavnet er ugyldigt. "
-"Brugernavne skal være en gyldig e-post-adresse, eller starte med et bogstav "
-"og kun indeholde bogstaver, tal og mellemrum, eller kun indeholde tal."
+msgid "Unable to add the buddy %s because the username is invalid. Usernames must be a valid email address, or start with a letter and contain only letters, numbers and spaces, or contain only numbers."
+msgstr "Ude af stand til at tilføje vennen %s, fordi brugernavnet er ugyldigt. Brugernavne skal være en gyldig e-mail-adresse, eller starte med et bogstav og kun indeholde bogstaver, tal og mellemrum, eller kun indeholde tal."
msgid "Unable to Retrieve Buddy List"
msgstr "Kan ikke hente venneliste"
-msgid ""
-"The AIM servers were temporarily unable to send your buddy list. Your buddy "
-"list is not lost, and will probably become available in a few minutes."
-msgstr ""
-"AIM-serverne var midlertidigt ude af stand til at sende din venneliste. Din "
-"venneliste er ikke gået tabt, og vil sikkert blive tilgængelig inden for et "
-"par minutter."
+msgid "The AIM servers were temporarily unable to send your buddy list. Your buddy list is not lost, and will probably become available in a few minutes."
+msgstr "AIM-serverne var midlertidigt ude af stand til at sende din venneliste. Din venneliste er ikke gået tabt, og vil sikkert blive tilgængelig inden for et par minutter."
msgid "Orphans"
msgstr "Hjemløse"
#, c-format
-msgid ""
-"Unable to add the buddy %s because you have too many buddies in your buddy "
-"list. Please remove one and try again."
-msgstr ""
-"Ude af stand til at tilføje vennen %s, fordi du har for mange venner i din "
-"venneliste. Fjern venligst en og prøv igen."
+msgid "Unable to add the buddy %s because you have too many buddies in your buddy list. Please remove one and try again."
+msgstr "Ude af stand til at tilføje vennen %s, fordi du har for mange venner i din venneliste. Fjern venligst en og prøv igen."
msgid "(no name)"
msgstr "(intet navn)"
@@ -7776,12 +7151,8 @@ msgid "Unable to add the buddy %s for an unknown reason."
msgstr "Ude af stand til at tilføje vennen %s af en ukendt årsag."
#, c-format
-msgid ""
-"The user %s has given you permission to add him or her to your buddy list. "
-"Do you want to add this user?"
-msgstr ""
-"Brugeren %s har givet dig lov til at tilføje ham eller hende til din "
-"venneliste. Vil du tilføje brugeren?"
+msgid "The user %s has given you permission to add him or her to your buddy list. Do you want to add this user?"
+msgstr "Brugeren %s har givet dig lov til at tilføje ham eller hende til din venneliste. Vil du tilføje brugeren?"
msgid "Authorization Given"
msgstr "Godkendelse givet"
@@ -7790,8 +7161,7 @@ msgstr "Godkendelse givet"
#. Granted
#, c-format
msgid "The user %s has granted your request to add them to your buddy list."
-msgstr ""
-"Brugeren %s har givet dig lov til at tilføje vedkommende til din venneliste."
+msgstr "Brugeren %s har givet dig lov til at tilføje vedkommende til din venneliste."
msgid "Authorization Granted"
msgstr "Godkendelse givet"
@@ -7800,12 +7170,10 @@ msgstr "Godkendelse givet"
#. Denied
#, c-format
msgid ""
-"The user %s has denied your request to add them to your buddy list for the "
-"following reason:\n"
+"The user %s has denied your request to add them to your buddy list for the following reason:\n"
"%s"
msgstr ""
-"Brugeren %s har afvist din anmodning om at tilføje vedkommende til din "
-"venneliste af følgende grund:\n"
+"Brugeren %s har afvist din anmodning om at tilføje vedkommende til din venneliste af følgende grund:\n"
"%s"
msgid "Authorization Denied"
@@ -7815,8 +7183,7 @@ msgid "_Exchange:"
msgstr "_Udveksl:"
msgid "Your IM Image was not sent. You cannot send IM Images in AIM chats."
-msgstr ""
-"Dit billede blev ikke sendt. Du kan ikke sende billeder i AIM-samtaler."
+msgstr "Dit billede blev ikke sendt. Du kan ikke sende billeder i AIM-samtaler."
msgid "iTunes Music Store Link"
msgstr "Henvisning til iTunes Music Store"
@@ -7835,12 +7202,8 @@ msgstr "Vennekommentar:"
msgid "You have selected to open a Direct IM connection with %s."
msgstr "Du har valgt at åbne en direkte besked-forbindelse med %s."
-msgid ""
-"Because this reveals your IP address, it may be considered a security risk. "
-"Do you wish to continue?"
-msgstr ""
-"Fordi dette afslører din IP-adresse, ses det som en privatlivs-risiko. Vil "
-"du fortsætte?"
+msgid "Because this reveals your IP address, it may be considered a security risk. Do you wish to continue?"
+msgstr "Fordi dette afslører din IP-adresse, ses det som en privatlivs-risiko. Vil du fortsætte?"
msgid "C_onnect"
msgstr "_Tilslut"
@@ -7886,21 +7249,17 @@ msgstr "du venter ikke på godkendelse"
msgid "You are awaiting authorization from the following buddies"
msgstr "Du afventer godkendelse fra følgende venner"
-msgid ""
-"You can re-request authorization from these buddies by right-clicking on "
-"them and selecting \"Re-request Authorization.\""
-msgstr ""
-"Du kan anmode om ny godkendelse fra disse venner ved at højreklikke på dem "
-"og vælge \"Anmod om ny godkendelse\"."
+msgid "You can re-request authorization from these buddies by right-clicking on them and selecting \"Re-request Authorization.\""
+msgstr "Du kan anmode om en ny godkendelse fra disse venner, ved at højreklikke på dem og vælge \"Anmod om ny godkendelse.\""
msgid "Find Buddy by Email"
-msgstr "Find ven ud fra e-post-adresse"
+msgstr "Find ven ud fra e-mail-adresse"
msgid "Search for a buddy by email address"
-msgstr "Søg efter en ven ud fra e-post-adresse"
+msgstr "Søg efter en ven ud fra e-mail-adresse"
msgid "Type the email address of the buddy you are searching for."
-msgstr "Indtast e-post-adressen på vennen, du søger efter."
+msgstr "Indtast e-mail-adressen på den ven, som du søger efter."
msgid "Set User Info (web)..."
msgstr "Indstil brugeroplysninger (internet)..."
@@ -7927,16 +7286,16 @@ msgid "Confirm Account"
msgstr "Bekræft konto"
msgid "Display Currently Registered Email Address"
-msgstr "Vis nuværende registrede e-post-adresse"
+msgstr "Vis nuværende registrede e-mail-adresse"
msgid "Change Currently Registered Email Address..."
-msgstr "Ændr nuværende registrede e-post-adresse..."
+msgstr "Ændr nuværende registrede e-mail-adresse..."
msgid "Show Buddies Awaiting Authorization"
msgstr "Vis venner der afventer godkendelse"
msgid "Search for Buddy by Email Address..."
-msgstr "Søg efter ven ud fra e-post-adresse..."
+msgstr "Søg efter ven ud fra e-mail-adresse..."
# ?
msgid "Use clientLogin"
@@ -7948,7 +7307,7 @@ msgid ""
"but does not reveal your IP address)"
msgstr ""
"Brug altid AIM/ICQ-proxyserver til\n"
-"filoverførsler og kvikbesked (langsommere,\n"
+"filoverførsler og lynbesked (langsommere,\n"
"men afslører ikke din IP-adresse)"
msgid "Allow multiple simultaneous logins"
@@ -7969,14 +7328,8 @@ msgstr "Forsøger at forbinde via proxy-server."
msgid "%s has just asked to directly connect to %s"
msgstr "%s har lige spurgt om at forbinde direkte til %s"
-msgid ""
-"This requires a direct connection between the two computers and is necessary "
-"for IM Images. Because your IP address will be revealed, this may be "
-"considered a privacy risk."
-msgstr ""
-"Dette kræver en direkte forbindelse imellem to computere og er nødvendigt "
-"for billeder. Fordi din IP-adresse vil blive afsløret, kan det anses som en "
-"privatlivsrisiko."
+msgid "This requires a direct connection between the two computers and is necessary for IM Images. Because your IP address will be revealed, this may be considered a privacy risk."
+msgstr "Dette kræver en direkte forbindelse imellem to computere og er nødvendigt for billeder. Fordi din IP-adresse vil blive afsløret, kan det anses som en privatlivsrisiko."
#. Label
msgid "Buddy Icon"
@@ -8194,11 +7547,8 @@ msgid "you have no buddies on this list"
msgstr "du har ingen venner på denne liste"
#, c-format
-msgid ""
-"You can add a buddy to this list by right-clicking on them and selecting \"%s"
-"\""
-msgstr ""
-"Du kan føje venner til denne liste ved at højreklikke på dem og vælge \"%s\""
+msgid "You can add a buddy to this list by right-clicking on them and selecting \"%s\""
+msgstr "Du kan føje venner til denne liste ved at højreklikke på dem og vælge \"%s\""
msgid "Visible List"
msgstr "Synlige-listen"
@@ -8260,8 +7610,7 @@ msgid "Starting Services"
msgstr "Starter tjenester"
#, c-format
-msgid ""
-"A Sametime administrator has issued the following announcement on server %s"
+msgid "A Sametime administrator has issued the following announcement on server %s"
msgstr "En Sametime-administrator har sendt følgende annoncering på server %s"
msgid "Sametime Administrator Announcement"
@@ -8306,12 +7655,8 @@ msgid "Create conference with user"
msgstr "Opret konference med bruger"
#, c-format
-msgid ""
-"Please enter a topic for the new conference, and an invitation message to be "
-"sent to %s"
-msgstr ""
-"Indtast et emne for den nye konference, og en invitationsbesked, der skal "
-"sendes til %s"
+msgid "Please enter a topic for the new conference, and an invitation message to be sent to %s"
+msgstr "Indtast et emne for den nye konference, og en invitationsbesked, der skal sendes til %s"
msgid "New Conference"
msgstr "Ny konference"
@@ -8329,14 +7674,8 @@ msgid "Invite user to a conference"
msgstr "Invitér bruger til en konference"
#, c-format
-msgid ""
-"Select a conference from the list below to send an invite to user %s. Select "
-"\"Create New Conference\" if you'd like to create a new conference to invite "
-"this user to."
-msgstr ""
-"Vælg en konference fra listen nedenunder for at sende en invitation til "
-"brugeren %s. Vælg \"Opret ny konference\" hvis du vil oprette en ny "
-"konference at invitere denne bruger til."
+msgid "Select a conference from the list below to send an invite to user %s. Select \"Create New Conference\" if you'd like to create a new conference to invite this user to."
+msgstr "Vælg en konference fra listen nedenunder for at sende en invitation til brugeren %s. Vælg \"Opret ny konference\" hvis du vil oprette en ny konference at invitere denne bruger til."
msgid "Invite to Conference"
msgstr "Invitér til konference"
@@ -8351,7 +7690,7 @@ msgid "Topic:"
msgstr "Emne:"
msgid "A server is required to connect this account"
-msgstr "En server er nødvendig for at forbinde denne konto"
+msgstr "Der kræves en server, for at forbinde denne konto"
#, c-format
msgid "Unknown (0x%04x)<br>"
@@ -8370,13 +7709,8 @@ msgid "An ambiguous user ID was entered"
msgstr "Der blev indtastet et ugyldigt bruger-id"
#, c-format
-msgid ""
-"The identifier '%s' may possibly refer to any of the following users. Please "
-"select the correct user from the list below to add them to your buddy list."
-msgstr ""
-"Identificeringen \"%s\" kan henvise til hvilken som helst af de følgende "
-"brugere. Vælg den korrekte bruger fra listen nedenunder for at føje dem til "
-"din venneliste."
+msgid "The identifier '%s' may possibly refer to any of the following users. Please select the correct user from the list below to add them to your buddy list."
+msgstr "Identificeringen \"%s\" kan henvise til hvilken som helst af de følgende brugere. Vælg den korrekte bruger fra listen nedenunder for at føje dem til din venneliste."
msgid "Select User"
msgstr "Vælg bruger"
@@ -8385,12 +7719,8 @@ msgid "Unable to add user: user not found"
msgstr "Kan ikke tilføje bruger: bruger ikke fundet"
#, c-format
-msgid ""
-"The identifier '%s' did not match any users in your Sametime community. This "
-"entry has been removed from your buddy list."
-msgstr ""
-"Identificeringen \"%s\" matchede ikke nogen brugere i dit Sametime-"
-"fællesskab. Denne post er blevet fjernet fra din venneliste."
+msgid "The identifier '%s' did not match any users in your Sametime community. This entry has been removed from your buddy list."
+msgstr "Identificeringen \"%s\" matchede ikke nogen brugere i dit Sametime-fællesskab. Denne post er blevet fjernet fra din venneliste."
#, c-format
msgid ""
@@ -8443,14 +7773,8 @@ msgid "Notes Address Book group results"
msgstr "Grupperesultater for Notes-adressebog"
#, c-format
-msgid ""
-"The identifier '%s' may possibly refer to any of the following Notes Address "
-"Book groups. Please select the correct group from the list below to add it "
-"to your buddy list."
-msgstr ""
-"Identificeringen \"%s\" kan henvise til hvilken som helst af de følgende "
-"Notes-adressebogsgrupper. Vælg venligst den korrekte gruppe fra listen "
-"nedenunder for at tilføje den til din venneliste."
+msgid "The identifier '%s' may possibly refer to any of the following Notes Address Book groups. Please select the correct group from the list below to add it to your buddy list."
+msgstr "Identificeringen \"%s\" kan henvise til hvilken som helst af de følgende Notes-adressebogsgrupper. Vælg venligst den korrekte gruppe fra listen nedenunder for at tilføje den til din venneliste."
msgid "Select Notes Address Book"
msgstr "Vælg Notes-adressebog"
@@ -8459,36 +7783,22 @@ msgid "Unable to add group: group not found"
msgstr "Kan ikke tilføje gruppe: gruppen ikke fundet"
#, c-format
-msgid ""
-"The identifier '%s' did not match any Notes Address Book groups in your "
-"Sametime community."
-msgstr ""
-"Identificeringen \"%s\" matchede ikke nogen Notes-adressebogsgrupper i dit "
-"Sametime-fællesskab."
+msgid "The identifier '%s' did not match any Notes Address Book groups in your Sametime community."
+msgstr "Identificeringen \"%s\" matchede ikke nogen Notes-adressebogsgrupper i dit Sametime-fællesskab."
msgid "Notes Address Book Group"
msgstr "Notes-adressebogsgruppe"
-msgid ""
-"Enter the name of a Notes Address Book group in the field below to add the "
-"group and its members to your buddy list."
-msgstr ""
-"Indtast navnet på en Notes-adressebogsgruppe i feltet nedenunder for at føje "
-"gruppen og dens medlemmer til din venneliste."
+msgid "Enter the name of a Notes Address Book group in the field below to add the group and its members to your buddy list."
+msgstr "Indtast navnet på en Notes-adressebogsgruppe i feltet nedenunder for at føje gruppen og dens medlemmer til din venneliste."
#, c-format
msgid "Search results for '%s'"
msgstr "Søgeresultater for \"%s\""
#, c-format
-msgid ""
-"The identifier '%s' may possibly refer to any of the following users. You "
-"may add these users to your buddy list or send them messages with the action "
-"buttons below."
-msgstr ""
-"Identificeringen \"%s\" kan muligvis henvise til hvilken som helst af de "
-"følgende brugere. Du kan føje disse brugere til din venneliste eller sende "
-"dem beskeder med handlingsknapperne nedenunder."
+msgid "The identifier '%s' may possibly refer to any of the following users. You may add these users to your buddy list or send them messages with the action buttons below."
+msgstr "Identificeringen \"%s\" kan muligvis henvise til hvilken som helst af de følgende brugere. Du kan føje disse brugere til din venneliste eller sende dem beskeder med handlingsknapperne nedenunder."
msgid "Search Results"
msgstr "Søgeresultater"
@@ -8498,8 +7808,7 @@ msgstr "Ingen fundne"
#, c-format
msgid "The identifier '%s' did not match any users in your Sametime community."
-msgstr ""
-"Identificeringen \"%s\" matchede ingen brugere i dit Sametime-fællesskab."
+msgstr "Identificeringen \"%s\" matchede ingen brugere i dit Sametime-fællesskab."
msgid "No Matches"
msgstr "Ingen fundne"
@@ -8507,12 +7816,8 @@ msgstr "Ingen fundne"
msgid "Search for a user"
msgstr "Søg efter en bruger"
-msgid ""
-"Enter a name or partial ID in the field below to search for matching users "
-"in your Sametime community."
-msgstr ""
-"Indtast et navn eller delvist id i feltet nedenunder for at søge efter "
-"matchende brugere i dit Sametime-fællesskab."
+msgid "Enter a name or partial ID in the field below to search for matching users in your Sametime community."
+msgstr "Indtast et navn eller delvist id i feltet nedenunder for at søge efter matchende brugere i dit Sametime-fællesskab."
msgid "User Search"
msgstr "Brugersøgning"
@@ -8568,12 +7873,8 @@ msgid "The remote user is not present in the network any more"
msgstr "Fjernbrugren er ikke længere til stede på netværket"
#, c-format
-msgid ""
-"Key agreement request received from %s. Would you like to perform the key "
-"agreement?"
-msgstr ""
-"Anmodning om nøgleudveksling modtaget fra %s. Vil du udføre "
-"nøgleudvekslingen?"
+msgid "Key agreement request received from %s. Would you like to perform the key agreement?"
+msgstr "Anmodning om nøgleudveksling modtaget fra %s. Vil du udføre nøgleudvekslingen?"
#, c-format
msgid ""
@@ -8619,13 +7920,8 @@ msgstr "Kan ikke hente oplysninger"
msgid "The %s buddy is not trusted"
msgstr "Vennen %s er ikke betroet"
-msgid ""
-"You cannot receive buddy notifications until you import his/her public key. "
-"You can use the Get Public Key command to get the public key."
-msgstr ""
-"Du kan ikke modtage vennepåmindelser før du importerer hans/hendes "
-"offentlige nøgle. Du kan bruge kommandoen \"Hent offentlig nøgle\" for at "
-"hente den offentlige nøgle."
+msgid "You cannot receive buddy notifications until you import his/her public key. You can use the Get Public Key command to get the public key."
+msgstr "Du kan ikke modtage vennepåmindelser før du importerer hans/hendes offentlige nøgle. Du kan bruge kommandoen \"Hent offentlig nøgle\" for at hente den offentlige nøgle."
#. Open file selector to select the public key.
msgid "Open..."
@@ -8635,12 +7931,8 @@ msgstr "Åbn..."
msgid "The %s buddy is not present in the network"
msgstr "Vennen %s er ikke til stede på netværket"
-msgid ""
-"To add the buddy you must import his/her public key. Press Import to import "
-"a public key."
-msgstr ""
-"For at tilføje vennen, skal du importere hendes/hans offentlige nøgle. Tryk "
-"Importér for at importere en offentlig nøgle."
+msgid "To add the buddy you must import his/her public key. Press Import to import a public key."
+msgstr "For at tilføje vennen, skal du importere hendes/hans offentlige nøgle. Tryk Importér for at importere en offentlig nøgle."
msgid "_Import..."
msgstr "_Importér..."
@@ -8648,19 +7940,11 @@ msgstr "_Importér..."
msgid "Select correct user"
msgstr "Vælg korrekt bruger"
-msgid ""
-"More than one user was found with the same public key. Select the correct "
-"user from the list to add to the buddy list."
-msgstr ""
-"Der blev fundet mere end én bruger med den samme offentlige nøgle. Vælg den "
-"korrekte bruger fra listen, som du vil føje til din venneliste."
+msgid "More than one user was found with the same public key. Select the correct user from the list to add to the buddy list."
+msgstr "Der blev fundet mere end én bruger med den samme offentlige nøgle. Vælg den korrekte bruger fra listen, som du vil føje til din venneliste."
-msgid ""
-"More than one user was found with the same name. Select the correct user "
-"from the list to add to the buddy list."
-msgstr ""
-"Der blev fundet mere end én bruger med samme navn. Vælg den korrekte bruger "
-"fra listen, som du vil føje til din venneliste."
+msgid "More than one user was found with the same name. Select the correct user from the list to add to the buddy list."
+msgstr "Der blev fundet mere end én bruger med samme navn. Vælg den korrekte bruger fra listen, som du vil føje til din venneliste."
msgid "Detached"
msgstr "Ikke koblet på"
@@ -8777,18 +8061,8 @@ msgid "Channel Public Keys List"
msgstr "Kanalens liste med offentlige nøgler"
#, c-format
-msgid ""
-"Channel authentication is used to secure the channel from unauthorized "
-"access. The authentication may be based on passphrase and digital "
-"signatures. If passphrase is set, it is required to be able to join. If "
-"channel public keys are set then only users whose public keys are listed are "
-"able to join."
-msgstr ""
-"Kanalgodkendelse bruges til at sikre kanalen fra uønsket adgang. "
-"Godkendelsen kan være baseret på en adgangsfrase og digitale underskrifter. "
-"Hvis adgangsfrasen er angivet, er den påkrævet for at tilslutte. Hvis "
-"kanalens offentlige nøgler er angivet, så er det kun brugere hvis offentlige "
-"nøgler er nævnt, som kan deltage."
+msgid "Channel authentication is used to secure the channel from unauthorized access. The authentication may be based on passphrase and digital signatures. If passphrase is set, it is required to be able to join. If channel public keys are set then only users whose public keys are listed are able to join."
+msgstr "Kanalgodkendelse bruges til at sikre kanalen fra uønsket adgang. Godkendelsen kan være baseret på en adgangsfrase og digitale underskrifter. Hvis adgangsfrasen er angivet, er den påkrævet for at tilslutte. Hvis kanalens offentlige nøgler er angivet, så er det kun brugere hvis offentlige nøgler er nævnt, som kan deltage."
msgid "Channel Authentication"
msgstr "Kanalgodkendelse"
@@ -8852,10 +8126,8 @@ msgid "Set Secret Channel"
msgstr "Sæt hemmelig kanal"
#, c-format
-msgid ""
-"You have to join the %s channel before you are able to join the private group"
-msgstr ""
-"Du skal tilslutte kanalen %s før du kan slutte dig til den private gruppe"
+msgid "You have to join the %s channel before you are able to join the private group"
+msgstr "Du skal tilslutte kanalen %s før du kan slutte dig til den private gruppe"
msgid "Join Private Group"
msgstr "Tilslut privat gruppe"
@@ -9098,12 +8370,8 @@ msgid "Passphrase required"
msgstr "Adgangsfrase påkrævet"
#, c-format
-msgid ""
-"Received %s's public key. Your local copy does not match this key. Would you "
-"still like to accept this public key?"
-msgstr ""
-"Modtog %ss offentlige nøgle. Din lokale kopi passer ikke med denne nøgle. "
-"Vil du stadig acceptere den?"
+msgid "Received %s's public key. Your local copy does not match this key. Would you still like to accept this public key?"
+msgstr "Modtog %ss offentlige nøgle. Din lokale kopi passer ikke med denne nøgle. Vil du stadig acceptere den?"
#, c-format
msgid "Received %s's public key. Would you like to accept this public key?"
@@ -9139,11 +8407,8 @@ msgstr "Fejl ved forbindelse til SILC-serveren"
msgid "Key Exchange failed"
msgstr "Nøgleudveksling mislykkedes"
-msgid ""
-"Resuming detached session failed. Press Reconnect to create new connection."
-msgstr ""
-"Genoptagelse af tidligere session mislykkedes. Opret en ny forbindelse ved "
-"at trykke på Genforbind."
+msgid "Resuming detached session failed. Press Reconnect to create new connection."
+msgstr "Genoptagelse af tidligere session mislykkedes. Opret en ny forbindelse ved at trykke på Genforbind."
msgid "Performing key exchange"
msgstr "Udfører nøgleudveksling"
@@ -9212,14 +8477,8 @@ msgstr "Tidszone (UTC)"
msgid "User Online Status Attributes"
msgstr "Statusattributter for tilkoblet bruger"
-msgid ""
-"You can let other users see your online status information and your personal "
-"information. Please fill the information you would like other users to see "
-"about yourself."
-msgstr ""
-"Du kan lade andre brugere se dine onlinestatusoplysninger samt dine "
-"personlige oplysninger. Udfyld de oplysninger, du gerne vil have at folk kan "
-"se om dig."
+msgid "You can let other users see your online status information and your personal information. Please fill the information you would like other users to see about yourself."
+msgstr "Du kan lade andre brugere se dine onlinestatusoplysninger samt dine personlige oplysninger. Udfyld de oplysninger, du gerne vil have at folk kan se om dig."
msgid "Message of the Day"
msgstr "Dagens besked (MOTD)"
@@ -9303,8 +8562,7 @@ msgid "topic [&lt;new topic&gt;]: View or change the topic"
msgstr "topic [&lt;nyt emne&gt;]: Vis eller ændr emnet"
msgid "join &lt;channel&gt; [&lt;password&gt;]: Join a chat on this network"
-msgstr ""
-"join &lt;kanal&gt; [&lt;adgangskode&gt;]: Tilslut samtale på dette netværk"
+msgstr "join &lt;kanal&gt; [&lt;adgangskode&gt;]: Tilslut samtale på dette netværk"
msgid "list: List channels on this network"
msgstr "list: Vis kanaler på dette netværk"
@@ -9316,11 +8574,10 @@ msgid "msg &lt;nick&gt; &lt;message&gt;: Send a private message to a user"
msgstr "msg &lt;navn&gt; &lt;besked&gt;: Send en privat besked til en bruger"
msgid "query &lt;nick&gt; [&lt;message&gt;]: Send a private message to a user"
-msgstr ""
-"query &lt;navn&gt; [&lt;besked&gt;]: Send en privat besked til en bruger"
+msgstr "query &lt;navn&gt; [&lt;besked&gt;]: Send en privat besked til en bruger"
msgid "motd: View the server's Message Of The Day"
-msgstr "motd: Vis dagens besked (MOTD)"
+msgstr "motd: Vis dagens besked fra serveren (MOTD)"
msgid "detach: Detach this session"
msgstr "detach: Gå væk fra denne session"
@@ -9340,19 +8597,11 @@ msgstr "nick &lt;nyt navn&gt;: Ændr dit alias"
msgid "whowas &lt;nick&gt;: View nick's information"
msgstr "whowas &lt;navn&gt;: Vis oplysninger om alias"
-msgid ""
-"cmode &lt;channel&gt; [+|-&lt;modes&gt;] [arguments]: Change or display "
-"channel modes"
-msgstr ""
-"cmode &lt;kanal&gt; [+|-&lt;tilstande&gt;] [argumenter]: Ændr eller vis "
-"kanaltilstande"
+msgid "cmode &lt;channel&gt; [+|-&lt;modes&gt;] [arguments]: Change or display channel modes"
+msgstr "cmode &lt;kanal&gt; [+|-&lt;tilstande&gt;] [argumenter]: Ændr eller vis kanaltilstande"
-msgid ""
-"cumode &lt;channel&gt; +|-&lt;modes&gt; &lt;nick&gt;: Change nick's modes "
-"on channel"
-msgstr ""
-"cumode &lt;kanal&gt; +|-&lt;tilstande&gt; &lt;navn&gt;: Ændr aliasets "
-"tilstande på kanal"
+msgid "cumode &lt;channel&gt; +|-&lt;modes&gt; &lt;nick&gt;: Change nick's modes on channel"
+msgstr "cumode &lt;kanal&gt; +|-&lt;tilstande&gt; &lt;navn&gt;: Ændr aliasets tilstande på kanal"
msgid "umode &lt;usermodes&gt;: Set your modes in the network"
msgstr "umode &lt;brugertilstande&gt;: Angiv dine tilstande på netværket"
@@ -9360,16 +8609,11 @@ msgstr "umode &lt;brugertilstande&gt;: Angiv dine tilstande på netværket"
msgid "oper &lt;nick&gt; [-pubkey]: Get server operator privileges"
msgstr "oper &lt;navn&gt; [-pubkey]: Få operatørprivilegier for serveren"
-msgid ""
-"invite &lt;channel&gt; [-|+]&lt;nick&gt;: invite nick or add/remove from "
-"channel invite list"
-msgstr ""
-"invite &lt;kanal&gt; [-|+]&lt;navn&gt;: invitér navn eller tilføj/fjern fra "
-"kanalens invitationsliste"
+msgid "invite &lt;channel&gt; [-|+]&lt;nick&gt;: invite nick or add/remove from channel invite list"
+msgstr "invite &lt;kanal&gt; [-|+]&lt;navn&gt;: invitér navn eller tilføj/fjern fra kanalens invitationsliste"
msgid "kick &lt;channel&gt; &lt;nick&gt; [comment]: Kick client from channel"
-msgstr ""
-"kick &lt;kanal&gt; &lt;navn&gt; [kommentar]: Spark klient ud fra kanal"
+msgstr "kick &lt;kanal&gt; &lt;navn&gt; [kommentar]: Spark klient ud fra kanal"
msgid "info [server]: View server administrative details"
msgstr "info [server]: Vis serverens administrative detaljer"
@@ -9378,8 +8622,7 @@ msgid "ban [&lt;channel&gt; +|-&lt;nick&gt;]: Ban client from channel"
msgstr "ban [&lt;kanal&gt; +|-&lt;navn&gt;]: Udvis klient fra kanal"
msgid "getkey &lt;nick|server&gt;: Retrieve client's or server's public key"
-msgstr ""
-"getkey &lt;navn|server&gt;: Hent klientens eller serverens offentlige nøgle"
+msgstr "getkey &lt;navn|server&gt;: Hent klientens eller serverens offentlige nøgle"
msgid "stats: View server and network statistics"
msgstr "stats: Vis server- og netværksstatistik"
@@ -9390,12 +8633,8 @@ msgstr "ping: Send PING til den forbundne server"
msgid "users &lt;channel&gt;: List users in channel"
msgstr "users &lt;kanal&gt;: Vis brugere på kanalen"
-msgid ""
-"names [-count|-ops|-halfops|-voices|-normal] &lt;channel(s)&gt;: List "
-"specific users in channel(s)"
-msgstr ""
-"names [-count|-ops|-halfops|-voices|-normal] &lt;kanaler&gt;: Vis angivne "
-"brugere på kanaler"
+msgid "names [-count|-ops|-halfops|-voices|-normal] &lt;channel(s)&gt;: List specific users in channel(s)"
+msgstr "names [-count|-ops|-halfops|-voices|-normal] &lt;kanaler&gt;: Vis angivne brugere på kanaler"
#. *< type
#. *< ui_requirement
@@ -9465,7 +8704,7 @@ msgstr "Brugernavn: \t%s\n"
#, c-format
msgid "Email: \t\t%s\n"
-msgstr "E-post: \t\t%s\n"
+msgstr "E-mail: \t\t%s\n"
#, c-format
msgid "Host Name: \t%s\n"
@@ -9532,9 +8771,7 @@ msgid "%s sent message to whiteboard. Would you like to open the whiteboard?"
msgstr "%s sendte en besked til tavlen. Vil du åbne tavlen?"
#, c-format
-msgid ""
-"%s sent message to whiteboard on %s channel. Would you like to open the "
-"whiteboard?"
+msgid "%s sent message to whiteboard on %s channel. Would you like to open the whiteboard?"
msgstr "%s sendte besked til tavlen på kanalen %s. Vil du åbne tavlen?"
msgid "Whiteboard"
@@ -9552,28 +8789,27 @@ msgstr "Fejl: Versionsuoverensstemmelse, opgradér din klient"
#, c-format
msgid "Failure: Remote does not trust/support your public key"
-msgstr ""
-"Fejl: Fjernvært stoler ikke på, eller understøtter ikke, din offentlige nøgle"
+msgstr "Fejl: Fjernvært stoler ikke på, eller understøtter ikke, din offentlige nøgle"
#, c-format
msgid "Failure: Remote does not support proposed KE group"
-msgstr "Fejl: Fjernvært understøtter ikke foreslået \"KE group\""
+msgstr "Fejl: Fjernvært understøtter ikke foreslåede KE-gruppe"
#, c-format
msgid "Failure: Remote does not support proposed cipher"
-msgstr "Fejl: Fjernvært understøtter ikke foreslået ciffer"
+msgstr "Fejl: Fjernvært understøtter ikke foreslåede ciffer"
#, c-format
msgid "Failure: Remote does not support proposed PKCS"
-msgstr "Fejl: Fjernvært undersøttter ikke foreslået PKCS"
+msgstr "Fejl: Fjernvært undersøttter ikke foreslåede PKCS"
#, c-format
msgid "Failure: Remote does not support proposed hash function"
-msgstr "Fejl: Fjernvært understøtter ikke foreslået hash-funktion"
+msgstr "Fejl: Fjernvært understøtter ikke foreslåede hash-funktion"
#, c-format
msgid "Failure: Remote does not support proposed HMAC"
-msgstr "Fejl: Fjernvært understøtter ikke foreslået HMAC"
+msgstr "Fejl: Fjernvært understøtter ikke foreslåede HMAC"
#, c-format
msgid "Failure: Incorrect signature"
@@ -9712,8 +8948,7 @@ msgstr "Udvidelsesmodul til Yahoo! JAPAN-protokollen"
#, c-format
msgid "%s has sent you a webcam invite, which is not yet supported."
-msgstr ""
-"%s har sendt dig en webkamerainvitation, hvilket endnu ikke er understøttet."
+msgstr "%s har sendt dig en webkamerainvitation, hvilket endnu ikke er understøttet."
msgid "Your SMS was not delivered"
msgstr "Din SMS blev ikke afleveret"
@@ -9729,19 +8964,14 @@ msgid "Authorization denied message:"
msgstr "Kommentar til nægtet godkendelse:"
#, c-format
-msgid ""
-"%s has (retroactively) denied your request to add them to your list for the "
-"following reason: %s."
+msgid "%s has (retroactively) denied your request to add them to your list for the following reason: %s."
msgstr ""
-"%s har (tilbagevirkende) nægtet din anmodning om at føje vedkommende til din "
-"venneliste af følgende grund:\n"
+"%s har (tilbagevirkende) nægtet din anmodning om at føje vedkommende til din venneliste af følgende grund:\n"
"%s."
#, c-format
msgid "%s has (retroactively) denied your request to add them to your list."
-msgstr ""
-"%s har (tilbagevirkende) nægtet din anmodning om at føje vedkommende til din "
-"venneliste."
+msgstr "%s har (tilbagevirkende) nægtet din anmodning om at føje vedkommende til din venneliste."
msgid "Add buddy rejected"
msgstr "Vennetilføjelse nægtet"
@@ -9751,54 +8981,31 @@ msgid "Received invalid data"
msgstr "Modtog ugyldige data"
#. security lock from too many failed login attempts
-msgid ""
-"Account locked: Too many failed login attempts. Logging into the Yahoo! "
-"website may fix this."
-msgstr ""
-"Konto låst: For mange mislykkedes logind-forsøg. Dette kan muligvis løses "
-"ved at logge ind på Yahoo!'s hjemmeside."
+msgid "Account locked: Too many failed login attempts. Logging into the Yahoo! website may fix this."
+msgstr "Konto låst: For mange mislykkedes logind-forsøg. Dette kan muligvis løses ved at logge ind på Yahoo!'s hjemmeside."
#. indicates a lock of some description
-msgid ""
-"Account locked: Unknown reason. Logging into the Yahoo! website may fix "
-"this."
-msgstr ""
-"Konto låst: Ukendt årsag. Dette kan muligvis løses ved at logge ind på "
-"Yahoo!'s hjemmeside."
+msgid "Account locked: Unknown reason. Logging into the Yahoo! website may fix this."
+msgstr "Konto låst: Ukendt årsag. Dette kan muligvis løses ved at logge ind på Yahoo!'s hjemmeside."
#. indicates a lock due to logging in too frequently
-msgid ""
-"Account locked: You have been logging in too frequently. Wait a few minutes "
-"before trying to connect again. Logging into the Yahoo! website may help."
-msgstr ""
-"Konto låst: Du har logget ind for ofte. Vent nogle få minutter inden du "
-"prøver at forbinde igen. Dette kan muligvis løses ved at logge ind på "
-"Yahoo!'s hjemmeside."
+msgid "Account locked: You have been logging in too frequently. Wait a few minutes before trying to connect again. Logging into the Yahoo! website may help."
+msgstr "Konto låst: Du har logget ind for ofte. Vent nogle få minutter inden du prøver at forbinde igen. Dette kan muligvis løses ved at logge ind på Yahoo!'s hjemmeside."
#. username or password missing
msgid "Username or password missing"
msgstr "Brugernavn eller adgangskode mangler"
#, c-format
-msgid ""
-"The Yahoo server has requested the use of an unrecognized authentication "
-"method. You will probably not be able to successfully sign on to Yahoo. "
-"Check %s for updates."
-msgstr ""
-"Yahoo-serveren har anmodet om brugen af en ukendt godkendelsesmetode. Du vil "
-"højst sandsynligt ikke kunne logge problemfrit på Yahoo. Kontrollér %s for "
-"opdateringer."
+msgid "The Yahoo server has requested the use of an unrecognized authentication method. You will probably not be able to successfully sign on to Yahoo. Check %s for updates."
+msgstr "Yahoo-serveren har anmodet om brugen af en ukendt godkendelsesmetode. Du vil højst sandsynligt ikke kunne logge problemfrit på Yahoo. Kontrollér %s for opdateringer."
msgid "Failed Yahoo! Authentication"
msgstr "Yahoo!-godkendelse mislykkedes"
#, c-format
-msgid ""
-"You have tried to ignore %s, but the user is on your buddy list. Clicking "
-"\"Yes\" will remove and ignore the buddy."
-msgstr ""
-"Du har prøvet at ignorere %s, men brugeren er på din venneliste. Hvis du "
-"klikker på \"Ja\", vil det fjerne og ignorere vennen."
+msgid "You have tried to ignore %s, but the user is on your buddy list. Clicking \"Yes\" will remove and ignore the buddy."
+msgstr "Du har prøvet at ignorere %s, men brugeren er på din venneliste. Hvis du klikker på \"Ja\", vil det fjerne og ignorere vennen."
msgid "Ignore buddy?"
msgstr "Ignorér ven?"
@@ -9806,35 +9013,22 @@ msgstr "Ignorér ven?"
msgid "Invalid username or password"
msgstr "Forkert brugernavn eller adgangskode"
-msgid ""
-"Your account has been locked due to too many failed login attempts. Please "
-"try logging into the Yahoo! website."
-msgstr ""
-"Din konto er blevet låst som følge af for mange mislykkedes logind-forsøg. "
-"Prøv venligst at logge ind på Yahoo!'s hjemmeside."
+msgid "Your account has been locked due to too many failed login attempts. Please try logging into the Yahoo! website."
+msgstr "Din konto er blevet låst som følge af for mange mislykkedes logind-forsøg. Prøv venligst at logge ind på Yahoo!'s hjemmeside."
msgid "Unknown error 52. Reconnecting should fix this."
msgstr "Ukendt fejl 52. Dette bør kunne løses ved at genforbinde."
-msgid ""
-"Error 1013: The username you have entered is invalid. The most common cause "
-"of this error is entering your email address instead of your Yahoo! ID."
-msgstr ""
-"Fejl 1013: Brugernavnet, du har indtastet, er ugyldigt. Den mest "
-"almindelige årsag til denne fejl er, at du har indtastet din e-post-adresse "
-"i stedet for dit Yahoo!-id."
+msgid "Error 1013: The username you have entered is invalid. The most common cause of this error is entering your email address instead of your Yahoo! ID."
+msgstr "Fejl 1013: Brugernavnet du har indtastet, er ugyldigt. Den mest almindelige årsag til denne fejl, er at du har indtastet din e-mail-adresse, i stedet for dit Yahoo!-id."
#, c-format
msgid "Unknown error number %d. Logging into the Yahoo! website may fix this."
-msgstr ""
-"Ukendt fejl nummer %d. Prøv at logge ind på Yahoo!'s hjemmeside for at fikse "
-"dette."
+msgstr "Ukendt fejl nummer %d. Prøv at logge ind på Yahoo!'s hjemmeside for at fikse dette."
#, c-format
msgid "Unable to add buddy %s to group %s to the server list on account %s."
-msgstr ""
-"Ude af stand til at tilføje ven %s til gruppe %s til serverlisten på konto "
-"%s."
+msgstr "Ude af stand til at tilføje ven %s til gruppe %s til serverlisten på konto %s."
msgid "Unable to add buddy to server list"
msgstr "Kan ikke føje ven til serverliste"
@@ -9857,11 +9051,8 @@ msgstr "Ude af stand til at etablere en forbindelse til %s: %s"
msgid "Unable to connect: The server returned an empty response."
msgstr "Ude af stand til at forbinde: Serveren retunerede et tomt svar."
-msgid ""
-"Unable to connect: The server's response did not contain the necessary "
-"information"
-msgstr ""
-"Kan ikke forbinde: Serverens svar indenholdt ikke den nødvendige information"
+msgid "Unable to connect: The server's response did not contain the necessary information"
+msgstr "Kan ikke forbinde: Serverens svar indenholdt ikke den nødvendige information"
msgid "Not at Home"
msgstr "Ikke hjemme"
@@ -9951,19 +9142,11 @@ msgstr "Yahoo! Japan-profil"
msgid "Yahoo! Profile"
msgstr "Yahoo!-profil"
-msgid ""
-"Sorry, profiles marked as containing adult content are not supported at this "
-"time."
-msgstr ""
-"Desværre, profiler der er markeret som havende indhold, der ikke er egnet "
-"for børn, understøttes ikke."
+msgid "Sorry, profiles marked as containing adult content are not supported at this time."
+msgstr "Desværre, profiler der er markeret som havende indhold, der ikke er egnet for børn, understøttes ikke."
-msgid ""
-"If you wish to view this profile, you will need to visit this link in your "
-"web browser:"
-msgstr ""
-"Hvis du vil se denne profil, skal du besøge denne henvisning i din "
-"webbrowser:"
+msgid "If you wish to view this profile, you will need to visit this link in your web browser:"
+msgstr "Hvis du vil se denne profil, skal du besøge denne henvisning i din webbrowser:"
msgid "Yahoo! ID"
msgstr "Yahoo!-id"
@@ -9989,27 +9172,14 @@ msgstr "Sej henvisning 3"
msgid "Last Update"
msgstr "Sidst opdateret"
-msgid ""
-"This profile is in a language or format that is not supported at this time."
-msgstr ""
-"Denne profil er skrevet på et sprog eller i et format, som ikke er "
-"understøttet på nuværende tidspunkt."
+msgid "This profile is in a language or format that is not supported at this time."
+msgstr "Denne profil er skrevet på et sprog eller i et format, som ikke er understøttet på nuværende tidspunkt."
-msgid ""
-"Could not retrieve the user's profile. This most likely is a temporary "
-"server-side problem. Please try again later."
-msgstr ""
-"Kunne ikke modtage brugerens profil. Dette er højst sandsynligt et "
-"midlertidigt serverproblem. Prøv venligst igen senere."
+msgid "Could not retrieve the user's profile. This most likely is a temporary server-side problem. Please try again later."
+msgstr "Kunne ikke modtage brugerens profil. Dette er højst sandsynligt et midlertidigt serverproblem. Prøv venligst igen senere."
-msgid ""
-"Could not retrieve the user's profile. This most likely means that the user "
-"does not exist; however, Yahoo! sometimes does fail to find a user's "
-"profile. If you know that the user exists, please try again later."
-msgstr ""
-"Kunne ikke modtage brugerens profil. Dette betyder højst sandsynligt at "
-"brugeren ikke findes; men Yahoo! tager nogen gange fejl og kan ikke finde en "
-"brugers profil. Hvis du ved at brugeren findes, så prøv venligst igen senere."
+msgid "Could not retrieve the user's profile. This most likely means that the user does not exist; however, Yahoo! sometimes does fail to find a user's profile. If you know that the user exists, please try again later."
+msgstr "Kunne ikke modtage brugerens profil. Dette betyder højst sandsynligt at brugeren ikke findes; men Yahoo! tager nogen gange fejl og kan ikke finde en brugers profil. Hvis du ved at brugeren findes, så prøv venligst igen senere."
msgid "The user's profile is empty."
msgstr "Brugerens profil er tom."
@@ -10033,12 +9203,8 @@ msgstr "Måske er rummet fyldt"
msgid "Not available"
msgstr "Ikke tilgængelig"
-msgid ""
-"Unknown error. You may need to logout and wait five minutes before being "
-"able to rejoin a chatroom"
-msgstr ""
-"Ukendt fejl. Det kræver måske at du logger ud og venter fem minutter, før du "
-"kan tilslutte samtalerummet igen"
+msgid "Unknown error. You may need to logout and wait five minutes before being able to rejoin a chatroom"
+msgstr "Ukendt fejl. Det kræver måske at du logger ud og venter fem minutter, før du kan tilslutte samtalerummet igen"
#, c-format
msgid "You are now chatting in %s."
@@ -10071,12 +9237,8 @@ msgstr "Brugerrum"
msgid "Connection problem with the YCHT server"
msgstr "Forbindelsesfejl med YCHT-serveren"
-msgid ""
-"(There was an error converting this message.\t Check the 'Encoding' option "
-"in the Account Editor)"
-msgstr ""
-"(Der opstod en fejl, da denne besked skulle konverteres.\t Sæt kryds ved "
-"\"Kodning\"-indstillingen i Kontoredigeringen)"
+msgid "(There was an error converting this message.\t Check the 'Encoding' option in the Account Editor)"
+msgstr "(Der opstod en fejl, da denne besked skulle konverteres.\t Sæt kryds ved \"Kodning\"-indstillingen i Kontoredigeringen)"
#, c-format
msgid "Unable to send to chat %s,%s,%s"
@@ -10121,37 +9283,22 @@ msgid "topic &lt;instance&gt;: Set the instance to be used on this class"
msgstr "topic &lt;instans&gt;: Sæt instans, der skal bruges på denne klasse"
msgid "sub &lt;class&gt; &lt;instance&gt; &lt;recipient&gt;: Join a new chat"
-msgstr ""
-"sub &lt;class&gt; &lt;instans&gt; &lt;modtager&gt;: Tilslut en ny samtale"
+msgstr "sub &lt;class&gt; &lt;instans&gt; &lt;modtager&gt;: Tilslut en ny samtale"
-msgid ""
-"zi &lt;instance&gt;: Send a message to &lt;message,<i>instance</i>,*&gt;"
+msgid "zi &lt;instance&gt;: Send a message to &lt;message,<i>instance</i>,*&gt;"
msgstr "zi &lt;instans&gt;: Send en besked til &lt;besked,<i>instans</i>,*&gt;"
-msgid ""
-"zci &lt;class&gt; &lt;instance&gt;: Send a message to &lt;<i>class</i>,"
-"<i>instance</i>,*&gt;"
-msgstr ""
-"zci &lt;klasse&gt; &lt;instans&gt;: Send en besked til &lt;<i>klasse</i>,"
-"<i>instans</i>,*&gt;"
+msgid "zci &lt;class&gt; &lt;instance&gt;: Send a message to &lt;<i>class</i>,<i>instance</i>,*&gt;"
+msgstr "zci &lt;klasse&gt; &lt;instans&gt;: Send en besked til &lt;<i>klasse</i>,<i>instans</i>,*&gt;"
-msgid ""
-"zcir &lt;class&gt; &lt;instance&gt; &lt;recipient&gt;: Send a message to &lt;"
-"<i>class</i>,<i>instance</i>,<i>recipient</i>&gt;"
-msgstr ""
-"zcir &lt;klasse&gt; &lt;instans&gt; &lt;modtager&gt;: Send en besked til &lt;"
-"<i>klasse</i>,<i>instans</i>,<i>modtager</i>&gt;"
+msgid "zcir &lt;class&gt; &lt;instance&gt; &lt;recipient&gt;: Send a message to &lt;<i>class</i>,<i>instance</i>,<i>recipient</i>&gt;"
+msgstr "zcir &lt;klasse&gt; &lt;instans&gt; &lt;modtager&gt;: Send en besked til &lt;<i>klasse</i>,<i>instans</i>,<i>modtager</i>&gt;"
-msgid ""
-"zir &lt;instance&gt; &lt;recipient&gt;: Send a message to &lt;MESSAGE,"
-"<i>instance</i>,<i>recipient</i>&gt;"
-msgstr ""
-"zir &lt;instans&gt; &lt;modtager&gt;: Send en besked til &lt;BESKED,"
-"<i>instans</i>,<i>modtager</i>&gt;"
+msgid "zir &lt;instance&gt; &lt;recipient&gt;: Send a message to &lt;MESSAGE,<i>instance</i>,<i>recipient</i>&gt;"
+msgstr "zir &lt;instans&gt; &lt;modtager&gt;: Send en besked til &lt;BESKED,<i>instans</i>,<i>modtager</i>&gt;"
msgid "zc &lt;class&gt;: Send a message to &lt;<i>class</i>,PERSONAL,*&gt;"
-msgstr ""
-"zc &lt;klasse&gt;: Send en besked til &lt;<i>klasse</i>,PERSONLIGT,*&gt;"
+msgstr "zc &lt;klasse&gt;: Send en besked til &lt;<i>klasse</i>,PERSONLIGT,*&gt;"
msgid "Resubscribe"
msgstr "Abonnér igen"
@@ -10403,12 +9550,8 @@ msgid "Error reading from %s: response too long (%d bytes limit)"
msgstr "Fejl ved læsning fra %s: svar for langt (grænsen er %d byte)"
#, c-format
-msgid ""
-"Unable to allocate enough memory to hold the contents from %s. The web "
-"server may be trying something malicious."
-msgstr ""
-"Ikke i stand til at allokere nok hukommelse til indholdet fra %s. "
-"Internetserveren prøver måske noget ondskabsfuldt."
+msgid "Unable to allocate enough memory to hold the contents from %s. The web server may be trying something malicious."
+msgstr "Ikke i stand til at allokere nok hukommelse til indholdet fra %s. Internetserveren prøver måske noget ondskabsfuldt."
#, c-format
msgid "Error reading from %s: %s"
@@ -10455,12 +9598,8 @@ msgid "Error Reading %s"
msgstr "Fejl ved læsning af %s"
#, c-format
-msgid ""
-"An error was encountered reading your %s. The file has not been loaded, and "
-"the old file has been renamed to %s~."
-msgstr ""
-"Der opstod en fejl ved læsning af din %s. Filen er ikke blevet indlæst, og "
-"den gamle fil er blevet omdøbt til %s~."
+msgid "An error was encountered reading your %s. The file has not been loaded, and the old file has been renamed to %s~."
+msgstr "Der opstod en fejl ved læsning af din %s. Filen er ikke blevet indlæst, og den gamle fil er blevet omdøbt til %s~."
msgid "Pidgin Internet Messenger"
msgstr "Pidgin - Internetbeskeder"
@@ -10468,11 +9607,8 @@ msgstr "Pidgin - Internetbeskeder"
msgid "Internet Messenger"
msgstr "Internetbeskeder"
-msgid ""
-"Chat over IM. Supports AIM, Google Talk, Jabber/XMPP, MSN, Yahoo and more"
-msgstr ""
-"Chat over personlige beskeder. Understøtter AIM, Google Talk, Jabber/XMPP, "
-"MSN, Yahoo og flere"
+msgid "Chat over IM. Supports AIM, Google Talk, Jabber/XMPP, MSN, Yahoo and more"
+msgstr "Chat over personlige beskeder. Understøtter AIM, Google Talk, Jabber/XMPP, MSN, Yahoo og flere"
#. Build the login options frame.
msgid "Login Options"
@@ -10495,7 +9631,7 @@ msgid "_Local alias:"
msgstr "_Lokalt alias:"
msgid "New _mail notifications"
-msgstr "Påmindelse om ny _e-post"
+msgstr "Påmindelse om nye _e-mails"
#. Buddy icon
msgid "Use this buddy _icon for this account:"
@@ -10556,7 +9692,7 @@ msgid "Use _silence suppression"
msgstr "Brug _stilhedsundertrykkelse"
msgid "_Voice and Video"
-msgstr "_Stemme og video"
+msgstr "_Tale og video"
msgid "Unable to save new account"
msgstr "Kan ikke gemme ny konto"
@@ -10586,38 +9722,26 @@ msgstr "Protokol"
msgid ""
"<span size='larger' weight='bold'>Welcome to %s!</span>\n"
"\n"
-"You have no IM accounts configured. To start connecting with %s press the "
-"<b>Add...</b> button below and configure your first account. If you want %s "
-"to connect to multiple IM accounts, press <b>Add...</b> again to configure "
-"them all.\n"
+"You have no IM accounts configured. To start connecting with %s press the <b>Add...</b> button below and configure your first account. If you want %s to connect to multiple IM accounts, press <b>Add...</b> again to configure them all.\n"
"\n"
-"You can come back to this window to add, edit, or remove accounts from "
-"<b>Accounts->Manage Accounts</b> in the Buddy List window"
+"You can come back to this window to add, edit, or remove accounts from <b>Accounts->Manage Accounts</b> in the Buddy List window"
msgstr ""
"<span size='larger' weight='bold'>Velkommen til %s!</span>\n"
"\n"
-"Du har ikke sant nogen beskedkonti op. Tryk på <b>Tilføj...</b>-knappen "
-"nedenfor for at komme i gang med at forbinde med %s, og konfigurér din "
-"første konto. Hvis du vil have %s til at forbinde til flere beskedkonti, så "
-"tryk <b>Tilføj...</b> igen for at konfigurere dem alle.\n"
+"Du har ikke sant nogen beskedkonti op. Tryk på <b>Tilføj...</b>-knappen nedenfor for at komme i gang med at forbinde med %s, og konfigurér din første konto. Hvis du vil have %s til at forbinde til flere beskedkonti, så tryk <b>Tilføj...</b> igen for at konfigurere dem alle.\n"
"\n"
-"Du kan vende tilbage til dette vindue for at tilføje, redigere eller fjerne "
-"konti fra <b>Konti->Kontohåndtering</b> i venneliste-vinduet"
+"Du kan vende tilbage til dette vindue for at tilføje, redigere eller fjerne konti fra <b>Konti->Kontohåndtering</b> i venneliste-vinduet"
#, c-format
-msgid ""
-"<a href=\"viewinfo\">%s</a>%s%s%s wants to add you (%s) to his or her buddy "
-"list%s%s"
-msgstr ""
-"<a href=\"viewinfo\">%s</a>%s%s%s vil gerne tilføje dig (%s) til hans eller "
-"hendes venneliste%s%s"
+msgid "<a href=\"viewinfo\">%s</a>%s%s%s wants to add you (%s) to his or her buddy list%s%s"
+msgstr "<a href=\"viewinfo\">%s</a>%s%s%s vil gerne tilføje dig (%s) til hans eller hendes venneliste%s%s"
#, c-format
msgid "%s%s%s%s wants to add you (%s) to his or her buddy list%s%s"
msgstr "%s%s%s%s vil tilføje dig (%s) til hans eller hendes venneliste%s%s"
msgid "Send Instant Message"
-msgstr "Send kvikbesked"
+msgstr "Send lynbesked"
#. Buddy List
msgid "Background Color"
@@ -10727,31 +9851,20 @@ msgstr "Tekstinformationen til når en ven har en ulæst besked"
msgid "Message (Nick Said) Text"
msgstr "Tekst, besked (med dit navn)"
-msgid ""
-"The text information for when a chat has an unread message that mentions "
-"your nickname"
-msgstr ""
-"Tekstinformationen for når en samtale har en ulæst besked, der nævner dit "
-"alias"
+msgid "The text information for when a chat has an unread message that mentions your nickname"
+msgstr "Tekstinformationen for når en samtale har en ulæst besked, der nævner dit alias"
msgid "The text information for a buddy's status"
msgstr "Tekstinformationen for en vens status"
#, c-format
msgid "You have %d contact named %s. Would you like to merge them?"
-msgid_plural ""
-"You currently have %d contacts named %s. Would you like to merge them?"
+msgid_plural "You currently have %d contacts named %s. Would you like to merge them?"
msgstr[0] "Du har %d kontakt navngivet %s. Vil du slå dem sammen?"
msgstr[1] "Du har %d kontakter navngivet %s. Vil du slå dem sammen?"
-msgid ""
-"Merging these contacts will cause them to share a single entry on the buddy "
-"list and use a single conversation window. You can separate them again by "
-"choosing 'Expand' from the contact's context menu"
-msgstr ""
-"Slås disse kontakter sammen, vil de dele et enkelt element på vennelisten, "
-"og bruge et enkelt samtalevindue. Du kan adskille dem igen ved at vælge "
-"\"Udfold\" i kontekstmenuen for kontakten"
+msgid "Merging these contacts will cause them to share a single entry on the buddy list and use a single conversation window. You can separate them again by choosing 'Expand' from the contact's context menu"
+msgstr "Slås disse kontakter sammen, vil de dele et enkelt element på vennelisten, og bruge et enkelt samtalevindue. Du kan adskille dem igen ved at vælge \"Udfold\" i kontekstmenuen for kontakten"
msgid "Please update the necessary fields."
msgstr "Opdatér de nødvendige felter."
@@ -10759,11 +9872,8 @@ msgstr "Opdatér de nødvendige felter."
msgid "A_ccount"
msgstr "_Konto"
-msgid ""
-"Please enter the appropriate information about the chat you would like to "
-"join.\n"
-msgstr ""
-"Indtast den nødvendige information om den samtale, du gerne vil tilslutte.\n"
+msgid "Please enter the appropriate information about the chat you would like to join.\n"
+msgstr "Indtast den nødvendige information om den samtale, du gerne vil tilslutte.\n"
msgid "Room _List"
msgstr "Rum_liste"
@@ -10853,11 +9963,8 @@ msgstr "_Udfold"
msgid "/Tools/Mute Sounds"
msgstr "/Værktøjer/Slå lyde fra"
-msgid ""
-"You are not currently signed on with an account that can add that buddy."
-msgstr ""
-"Du er på nuværende tidspunkt ikke logget ind på nogen konti som kan tilføje "
-"denne ven."
+msgid "You are not currently signed on with an account that can add that buddy."
+msgstr "Du er på nuværende tidspunkt ikke logget ind på nogen konti som kan tilføje denne ven."
#. I don't believe this can happen currently, I think
#. * everything that calls this function checks for one of the
@@ -10974,22 +10081,22 @@ msgid "/_Help"
msgstr "/_Hjælp"
msgid "/Help/Online _Help"
-msgstr "/Hjælp/Online_hjælp"
+msgstr "/Hjælp/Få _hjælp online"
msgid "/Help/_Build Information"
-msgstr "/Hjælp/_Kompileringsinformation"
+msgstr "/Hjælp/Information om _kompilering"
msgid "/Help/_Debug Window"
msgstr "/Hjælp/Vis _fejlsøgningsvindue"
msgid "/Help/De_veloper Information"
-msgstr "/Hjælp/_Udviklerinformation"
+msgstr "/Hjælp/Information om _udvikling"
msgid "/Help/_Plugin Information"
-msgstr "/Hjælp/_Udvidelsesinformation"
+msgstr "/Hjælp/_Information om udvidelsesmodul"
msgid "/Help/_Translator Information"
-msgstr "/Hjælp/_Oversætterinformation"
+msgstr "/Hjælp/Information om _oversættere"
msgid "/Help/_About"
msgstr "/Hjælp/_Om"
@@ -11112,8 +10219,7 @@ msgstr "Velkommen tilbage!"
#, c-format
msgid "%d account was disabled because you signed on from another location:"
-msgid_plural ""
-"%d accounts were disabled because you signed on from another location:"
+msgid_plural "%d accounts were disabled because you signed on from another location:"
msgstr[0] "%d konto blev deaktiveret, fordi du loggede ind andetsteds:"
msgstr[1] "%d konti blev deaktiveret, fordi du loggede ind andetsteds:"
@@ -11134,15 +10240,11 @@ msgstr "/Konti"
msgid ""
"<span weight='bold' size='larger'>Welcome to %s!</span>\n"
"\n"
-"You have no accounts enabled. Enable your IM accounts from the <b>Accounts</"
-"b> window at <b>Accounts->Manage Accounts</b>. Once you enable accounts, "
-"you'll be able to sign on, set your status, and talk to your friends."
+"You have no accounts enabled. Enable your IM accounts from the <b>Accounts</b> window at <b>Accounts->Manage Accounts</b>. Once you enable accounts, you'll be able to sign on, set your status, and talk to your friends."
msgstr ""
"<span weight='bold' size='larger'>Velkommen til %s!</span>\n"
"\n"
-"Du har ingen aktive konti. Aktivér dine beskedkonti fra <b>Konti</b>-vinduet "
-"under <b>Konti->Kontohåndtering</b>. Når du har aktiveret nogle konti, vil "
-"du være i stand til at logge på, sætte din status og tale med dine venner."
+"Du har ingen aktive konti. Aktivér dine beskedkonti fra <b>Konti</b>-vinduet under <b>Konti->Kontohåndtering</b>. Når du har aktiveret nogle konti, vil du være i stand til at logge på, sætte din status og tale med dine venner."
#. set the Show Offline Buddies option. must be done
#. * after the treeview or faceprint gets mad. -Robot101
@@ -11181,20 +10283,12 @@ msgid "This protocol does not support chat rooms."
msgstr "Denne protokol understøtter ikke samtalerum."
# Her er det nok bedst at understrege, at det ikke f.eks. er videosamtale el. lign.
-msgid ""
-"You are not currently signed on with any protocols that have the ability to "
-"chat."
-msgstr ""
-"Du er på nuværende tidspunkt ikke logget på nogen protokoller, der "
-"understøtter chatsamtale."
+msgid "You are not currently signed on with any protocols that have the ability to chat."
+msgstr "Du er på nuværende tidspunkt ikke logget på nogen protokoller, der understøtter chatsamtale."
# hvordan kan en 'samtale' være på vennelisten? Måske menes der chatrum
-msgid ""
-"Please enter an alias, and the appropriate information about the chat you "
-"would like to add to your buddy list.\n"
-msgstr ""
-"Indtast et alias og den nødvendige information om den, samtale du vil "
-"tilføje til din venneliste.\n"
+msgid "Please enter an alias, and the appropriate information about the chat you would like to add to your buddy list.\n"
+msgstr "Indtast et alias og den nødvendige information om den, samtale du vil tilføje til din venneliste.\n"
msgid "A_lias:"
msgstr "A_lias:"
@@ -11251,11 +10345,8 @@ msgstr "Ukendt kommando."
msgid "That buddy is not on the same protocol as this chat."
msgstr "Vennen er ikke på den samme protokol som denne samtale."
-msgid ""
-"You are not currently signed on with an account that can invite that buddy."
-msgstr ""
-"Du er på nuværende tidspunkt ikke logget på nogen konti, som kan invitere "
-"den ven."
+msgid "You are not currently signed on with an account that can invite that buddy."
+msgstr "Du er på nuværende tidspunkt ikke logget på nogen konti, som kan invitere den ven."
msgid "Invite Buddy Into Chat Room"
msgstr "Invitér ven med i samtalerum"
@@ -11522,12 +10613,8 @@ msgstr "Ulæste beskeder"
msgid "New Event"
msgstr "Ny begivenhed"
-msgid ""
-"The account has disconnected and you are no longer in this chat. You will "
-"automatically rejoin the chat when the account reconnects."
-msgstr ""
-"Kontoen har afbrudt dig og du er ikke længere i denne samtale. Du vil "
-"automatisk blive tilsluttet til samtalen igen, når kontoen forbinder igen."
+msgid "The account has disconnected and you are no longer in this chat. You will automatically rejoin the chat when the account reconnects."
+msgstr "Kontoen er ikke længere tilsluttet, og du er ikke længere i samtalen. Du vil automatisk blive sluttet til samtalen igen, når kontoen får forbindelse."
msgid "clear: Clears all conversation scrollbacks."
msgstr "clear: Rydder samtalevinduet for gemt tekst."
@@ -11675,7 +10762,7 @@ msgid "Assamese"
msgstr "Assamesisk"
msgid "Asturian"
-msgstr ""
+msgstr "Asturiansk"
msgid "Belarusian Latin"
msgstr "Hviderussisk latinsk"
@@ -11882,9 +10969,8 @@ msgstr "Albansk"
msgid "Serbian"
msgstr "Serbisk"
-#, fuzzy
msgid "Serbian Latin"
-msgstr "Hviderussisk latinsk"
+msgstr "Serbisk latin"
msgid "Sinhala"
msgstr "Singalesisk"
@@ -11932,49 +11018,15 @@ msgid "T.M.Thanh and the Gnome-Vi Team"
msgstr "T.M.Thanh og Gnome-Vi-holdet"
#, c-format
-msgid ""
-"%s is a messaging client based on libpurple which is capable of connecting "
-"to multiple messaging services at once. %s is written in C using GTK+. %s "
-"is released, and may be modified and redistributed, under the terms of the "
-"GPL version 2 (or later). A copy of the GPL is distributed with %s. %s is "
-"copyrighted by its contributors, a list of whom is also distributed with "
-"%s. There is no warranty for %s.<BR><BR>"
-msgstr ""
-"%s er en beskedklient baseret på libpurple, som er i stand til at forbinde "
-"til flere beskedtjenester ad gangen. %s er skrevet i C med GTK+. %s udgives "
-"under, og kan ændres og redistrubueres under, betingelserne i GPL version 2 "
-"(eller senere). En kopi af GPL distruberes sammen med %s. %s udgives med "
-"ophavsret af programmørerne, som er anført på en liste, der distribueres "
-"sammen med %s. Der er ingen garanti på %s.<BR><BR>"
+msgid "%s is a messaging client based on libpurple which is capable of connecting to multiple messaging services at once. %s is written in C using GTK+. %s is released, and may be modified and redistributed, under the terms of the GPL version 2 (or later). A copy of the GPL is distributed with %s. %s is copyrighted by its contributors, a list of whom is also distributed with %s. There is no warranty for %s.<BR><BR>"
+msgstr "%s er en beskedklient baseret på libpurple, som er i stand til at forbinde til flere beskedtjenester ad gangen. %s er skrevet i C med GTK+. %s udgives under, og kan ændres og redistrubueres under, betingelserne i GPL version 2 (eller senere). En kopi af GPL distruberes sammen med %s. %s udgives med ophavsret af programmørerne, som er anført på en liste, der distribueres sammen med %s. Der er ingen garanti på %s.<BR><BR>"
#, c-format
-msgid ""
-"<FONT SIZE=\"4\"><B>Helpful Resources</B></FONT><BR>\t<A HREF=\"%s"
-"\">Website</A><BR>\t<A HREF=\"%s\">Frequently Asked Questions</A><BR>\tIRC "
-"Channel: #pidgin on irc.freenode.net<BR>\tXMPP MUC: devel@conference.pidgin."
-"im<BR><BR>"
-msgstr ""
-"<FONT SIZE=\"4\"><B>Nyttige ressourcer</B></FONT><BR>\t<A HREF=\"%s"
-"\">Hjemmeside</A><BR>\t<A HREF=\"%s\">Ofte stillede spørgsmål</A><BR>\tIRC-"
-"kanal: #pidgin på irc.freenode.net<BR>\tXMPP MUC: devel@conference.pidgin."
-"im<BR><BR>"
+msgid "<FONT SIZE=\"4\"><B>Helpful Resources</B></FONT><BR>\t<A HREF=\"%s\">Website</A><BR>\t<A HREF=\"%s\">Frequently Asked Questions</A><BR>\tIRC Channel: #pidgin on irc.freenode.net<BR>\tXMPP MUC: devel@conference.pidgin.im<BR><BR>"
+msgstr "<FONT SIZE=\"4\"><B>Nyttige ressourcer</B></FONT><BR>\t<A HREF=\"%s\">Hjemmeside</A><BR>\t<A HREF=\"%s\">Ofte stillede spørgsmål</A><BR>\tIRC-kanal: #pidgin på irc.freenode.net<BR>\tXMPP MUC: devel@conference.pidgin.im<BR><BR>"
-msgid ""
-"<font size=\"4\"><b>Help from other Pidgin users</b></font> is available by "
-"e-mailing <a href=\"mailto:support@pidgin.im\">support@pidgin.im</a><br/"
-">This is a <b>public</b> mailing list! (<a href=\"http://pidgin.im/pipermail/"
-"support/\">archive</a>)<br/>We can't help with third-party protocols or "
-"plugins!<br/>This list's primary language is <b>English</b>. You are "
-"welcome to post in another language, but the responses may be less helpful."
-"<br/>"
-msgstr ""
-"<font size=\"4\"><b>Hjælp fra andre Pidgin-brugere</b></font> er til "
-"rådighed på e-post-listen <a href=\"mailto:support@pidgin.im"
-"\">support@pidgin.im</a><br/>Dette er en <b>offentlig</b> e-post-liste! (<a "
-"href=\"http://pidgin.im/pipermail/support/\">arkiv</a>)<br/>Vi kan ikke "
-"hjælpe med tredjepartsprotokoller og udvidelser!<br/>Denne listes primære "
-"sprog er <b>engelsk</b>. Du er velkommen til at skrive på et andet sprog, "
-"men svarene kan være mindre værd.<br/>"
+msgid "<font size=\"4\"><b>Help from other Pidgin users</b></font> is available by e-mailing <a href=\"mailto:support@pidgin.im\">support@pidgin.im</a><br/>This is a <b>public</b> mailing list! (<a href=\"http://pidgin.im/pipermail/support/\">archive</a>)<br/>We can't help with third-party protocols or plugins!<br/>This list's primary language is <b>English</b>. You are welcome to post in another language, but the responses may be less helpful.<br/>"
+msgstr "<font size=\"4\"><b>Hjælp fra andre Pidgin-brugere</b></font> er til rådighed på e-mail-listen <a href=\"mailto:support@pidgin.im\">support@pidgin.im</a><br/>Dette er en <b>offentlig</b> e-mail-liste! (<a href=\"http://pidgin.im/pipermail/support/\">arkiv</a>)<br/>Vi kan ikke hjælpe med tredjepartsprotokoller og udvidelser.<br/>Denne listes primære sprog er <b>engelsk</b>. Du er velkommen til at skrive på et andet sprog, men svarene kan være mindre værd.<br/>"
#, c-format
msgid "About %s"
@@ -12012,14 +11064,14 @@ msgstr "Tidligere oversættere"
#, c-format
msgid "%s Translator Information"
-msgstr "Oversætterinformation om %s"
+msgstr "Information om oversættelse for %s"
#, c-format
msgid "%s Plugin Information"
-msgstr "%s udvidelsesinformation"
+msgstr "Information om udvidelsen %s"
msgid "Plugin Information"
-msgstr "Udvidelsesinformation"
+msgstr "Information om udvidelsen"
msgid "_Name"
msgstr "_Navn"
@@ -12030,11 +11082,8 @@ msgstr "_Konto"
msgid "Get User Info"
msgstr "Hent brugeroplysninger"
-msgid ""
-"Please enter the username or alias of the person whose info you would like "
-"to view."
-msgstr ""
-"Indtast brugernavn eller alias på personen, hvis oplysninger du gerne vil se."
+msgid "Please enter the username or alias of the person whose info you would like to view."
+msgstr "Indtast brugernavn eller alias på personen, hvis oplysninger du gerne vil se."
msgid "View User Log"
msgstr "Vis brugerlog"
@@ -12059,18 +11108,10 @@ msgid "Enter an alias for this chat."
msgstr "Indtast et alias for denne samtale."
#, c-format
-msgid ""
-"You are about to remove the contact containing %s and %d other buddy from "
-"your buddy list. Do you want to continue?"
-msgid_plural ""
-"You are about to remove the contact containing %s and %d other buddies from "
-"your buddy list. Do you want to continue?"
-msgstr[0] ""
-"Du er ved at fjerne kontakten, der indeholder %s og %d yderligere ven fra "
-"din venneliste. Vil du fortsætte?"
-msgstr[1] ""
-"Du er ved at fjerne kontakten, der indeholder %s og %d yderligere venner fra "
-"din venneliste. Vil du fortsætte?"
+msgid "You are about to remove the contact containing %s and %d other buddy from your buddy list. Do you want to continue?"
+msgid_plural "You are about to remove the contact containing %s and %d other buddies from your buddy list. Do you want to continue?"
+msgstr[0] "Du er ved at fjerne kontakten, der indeholder %s og %d yderligere ven fra din venneliste. Vil du fortsætte?"
+msgstr[1] "Du er ved at fjerne kontakten, der indeholder %s og %d yderligere venner fra din venneliste. Vil du fortsætte?"
msgid "Remove Contact"
msgstr "Fjern kontakt"
@@ -12079,9 +11120,7 @@ msgid "_Remove Contact"
msgstr "_Fjern kontakt"
#, c-format
-msgid ""
-"You are about to merge the group called %s into the group called %s. Do you "
-"want to continue?"
+msgid "You are about to merge the group called %s into the group called %s. Do you want to continue?"
msgstr "Du er ved at slå gruppen %s sammen med gruppen %s. Vil du fortsætte?"
msgid "Merge Groups"
@@ -12091,12 +11130,8 @@ msgid "_Merge Groups"
msgstr "_Slå grupper sammen"
#, c-format
-msgid ""
-"You are about to remove the group %s and all its members from your buddy "
-"list. Do you want to continue?"
-msgstr ""
-"Du er ved at fjerne gruppen %s og alle dens medlemmer fra din venneliste. "
-"Vil du fortsætte?"
+msgid "You are about to remove the group %s and all its members from your buddy list. Do you want to continue?"
+msgstr "Du er ved at fjerne gruppen %s og alle dens medlemmer fra din venneliste. Vil du fortsætte?"
msgid "Remove Group"
msgstr "Fjern gruppe"
@@ -12105,8 +11140,7 @@ msgid "_Remove Group"
msgstr "_Fjern gruppe"
#, c-format
-msgid ""
-"You are about to remove %s from your buddy list. Do you want to continue?"
+msgid "You are about to remove %s from your buddy list. Do you want to continue?"
msgstr "Du skal til at slette %s fra din venneliste. Vil du fortsætte?"
msgid "Remove Buddy"
@@ -12116,11 +11150,8 @@ msgid "_Remove Buddy"
msgstr "_Fjern ven"
#, c-format
-msgid ""
-"You are about to remove the chat %s from your buddy list. Do you want to "
-"continue?"
-msgstr ""
-"Du skal til at fjerne samtalen %s fra din venneliste. Vil du fortsætte?"
+msgid "You are about to remove the chat %s from your buddy list. Do you want to continue?"
+msgstr "Du skal til at fjerne samtalen %s fra din venneliste. Vil du fortsætte?"
msgid "Remove Chat"
msgstr "Fjern samtale"
@@ -12292,9 +11323,7 @@ msgid "Typing notification font"
msgstr "Skrifttype for indtastningspåmindelser"
msgid "The font to use for the typing notification"
-msgstr ""
-"Skriftypen, der bruges til påmindelsen om at en bruger er ved at skrive en "
-"besked"
+msgstr "Skriftypen, der bruges til påmindelsen om at en bruger er ved at skrive en besked"
msgid "Enable typing notification"
msgstr "Aktivér påmindelse om indtastning"
@@ -12342,12 +11371,8 @@ msgstr "_URL"
msgid "_Description"
msgstr "_Beskrivelse"
-msgid ""
-"Please enter the URL and description of the link that you want to insert. "
-"The description is optional."
-msgstr ""
-"Indtast venligst URL og beskrivelse på den henvisning, du vil indsætte. "
-"Beskrivelsen kan udelades."
+msgid "Please enter the URL and description of the link that you want to insert. The description is optional."
+msgstr "Indtast venligst URL og beskrivelse på den henvisning, du vil indsætte. Beskrivelsen kan udelades."
msgid "Please enter the URL of the link that you want to insert."
msgstr "Indtast venligst URL'en på den henvisning, du vil indsætte."
@@ -12370,8 +11395,7 @@ msgid ""
"This smiley is disabled because a custom smiley exists for this shortcut:\n"
" %s"
msgstr ""
-"Denne smiley er deaktiveret, fordi der findes en tilpasset smiley for denne "
-"genvej:\n"
+"Denne smiley er deaktiveret, fordi der findes en tilpasset smiley for denne genvej:\n"
"%s"
msgid "Smile!"
@@ -12484,28 +11508,16 @@ msgid "Check permissions and try again."
msgstr "Kontrollér rettigheder og prøv igen."
#, c-format
-msgid ""
-"Are you sure you want to permanently delete the log of the conversation with "
-"%s which started at %s?"
-msgstr ""
-"Er du sikker på at du permanent vil slette loggen over samtaler med %s, som "
-"startede %s?"
+msgid "Are you sure you want to permanently delete the log of the conversation with %s which started at %s?"
+msgstr "Er du sikker på at du permanent vil slette loggen over samtaler med %s, som startede %s?"
#, c-format
-msgid ""
-"Are you sure you want to permanently delete the log of the conversation in "
-"%s which started at %s?"
-msgstr ""
-"Er du sikker på at du permanent vil slette loggen over samtalen i %s, som er "
-"startet %s?"
+msgid "Are you sure you want to permanently delete the log of the conversation in %s which started at %s?"
+msgstr "Er du sikker på at du permanent vil slette loggen over samtalen i %s, som er startet %s?"
#, c-format
-msgid ""
-"Are you sure you want to permanently delete the system log which started at "
-"%s?"
-msgstr ""
-"Er du sikker på at du permanent vil slette systemloggen, som er startet ved "
-"%s?"
+msgid "Are you sure you want to permanently delete the system log which started at %s?"
+msgstr "Er du sikker på at du permanent vil slette systemloggen, som er startet ved %s?"
msgid "Delete Log?"
msgstr "Slet log?"
@@ -12568,8 +11580,7 @@ msgid ""
" Without this only the first account will be enabled)."
msgstr ""
"slå angivne konto/konti til (valgfrit tilvalg NAVN\n"
-" angiver hvilke konti der skal bruges, adskilt af "
-"komma.\n"
+" angiver hvilke konti der skal bruges, adskilt af komma.\n"
" Uden dette vil kun den første konto blive slået til)."
msgid "X display to use"
@@ -12659,15 +11670,11 @@ msgstr "Kunne ikke åbne URL"
msgid "Error launching \"%s\": %s"
msgstr "Fejl ved kørsel af \"%s\": %s"
-msgid ""
-"The 'Manual' browser command has been chosen, but no command has been set."
-msgstr ""
-"Browserkommandoen \"Manuel\" er blevet valgt, men ingen kommando er blevet "
-"angivet."
+msgid "The 'Manual' browser command has been chosen, but no command has been set."
+msgstr "Browserkommandoen \"Manuel\" er blevet valgt, men ingen kommando er blevet angivet."
-#, fuzzy
msgid "Unable to open URL: the 'Manual' browser command seems invalid."
-msgstr "Browserkommandoen \"%s\" er ugyldig."
+msgstr "URL'en kunne ikke åbnes: Den 'manuelle' browserkommando ser ugyldig ud."
msgid "No message"
msgstr "Ingen besked"
@@ -12706,12 +11713,8 @@ msgstr "Deaktivér udvidelsesmoduler"
msgid "Could not unload plugin"
msgstr "Kunne ikke deaktivere udvidelsesmodul"
-msgid ""
-"The plugin could not be unloaded now, but will be disabled at the next "
-"startup."
-msgstr ""
-"Udvidelsesmodulet kunne ikke deaktiveres nu, men vil blive slået fra ved den "
-"næste opstart."
+msgid "The plugin could not be unloaded now, but will be disabled at the next startup."
+msgstr "Udvidelsesmodulet kunne ikke deaktiveres nu, men vil blive slået fra ved den næste opstart."
#, c-format
msgid ""
@@ -12882,8 +11885,7 @@ msgstr "Temavalg"
#. Instructions
msgid ""
"Select a theme that you would like to use from the lists below.\n"
-"New themes can be installed by dragging and dropping them onto the theme "
-"list."
+"New themes can be installed by dragging and dropping them onto the theme list."
msgstr ""
"Vælg et tema, du gerne vil bruge, fra listen nedenunder.\n"
"Nye temaer kan installeres ved at trække og slippe dem over temalisten."
@@ -12988,7 +11990,7 @@ msgid "F_lash window when IMs are received"
msgstr "_Blink vindue når der modtages beskeder"
msgid "Resize incoming custom smileys"
-msgstr "Ændr størrelse på indkomne tilpassede smileyer"
+msgstr "Ændr størrelsen på de modtaget tilpassede smileyer"
msgid "Maximum size:"
msgstr "Maksimal størrelse:"
@@ -13008,12 +12010,8 @@ msgstr "Samtale_skrifttype:"
msgid "Default Formatting"
msgstr "Standardformatering"
-msgid ""
-"This is how your outgoing message text will appear when you use protocols "
-"that support formatting."
-msgstr ""
-"Sådan vil din udgående besked se ud når du bruger protokoller, der "
-"understøtter formatering."
+msgid "This is how your outgoing message text will appear when you use protocols that support formatting."
+msgstr "Sådan vil din udgående besked se ud når du bruger protokoller, der understøtter formatering."
msgid "Cannot start proxy configuration program."
msgstr "Kan ikke starte proxy-konfigurationsprogram."
@@ -13424,11 +12422,8 @@ msgid "Status for %s"
msgstr "Status for %s"
#, c-format
-msgid ""
-"A custom smiley for '%s' already exists. Please use a different shortcut."
-msgstr ""
-"Der findes allerede en tilpasset smiley for \"%s\". Brug venligst en anden "
-"genvej."
+msgid "A custom smiley for '%s' already exists. Please use a different shortcut."
+msgstr "Der findes allerede en tilpasset smiley for \"%s\". Brug venligst en anden genvej."
msgid "Custom Smiley"
msgstr "Tilpasset smiley"
@@ -13497,21 +12492,14 @@ msgid "Cannot send folder %s."
msgstr "Kan ikke sende mappe %s."
#, c-format
-msgid ""
-"%s cannot transfer a folder. You will need to send the files within "
-"individually."
-msgstr ""
-"%s kan ikke overføre en mappe. Du skal sende filerne i denne individuelt."
+msgid "%s cannot transfer a folder. You will need to send the files within individually."
+msgstr "%s kan ikke overføre en mappe. Du skal sende filerne i denne individuelt."
msgid "You have dragged an image"
msgstr "Du har trukket et billede"
-msgid ""
-"You can send this image as a file transfer, embed it into this message, or "
-"use it as the buddy icon for this user."
-msgstr ""
-"Du kan sende dette billede som en filoverførsel, vedlægge det til en besked, "
-"eller bruge det som venneikonet til denne bruger."
+msgid "You can send this image as a file transfer, embed it into this message, or use it as the buddy icon for this user."
+msgstr "Du kan sende dette billede som en filoverførsel, vedlægge det til en besked, eller bruge det som venneikonet til denne bruger."
msgid "Set as buddy icon"
msgstr "Sæt som venneikon"
@@ -13525,19 +12513,11 @@ msgstr "Indsæt i besked"
msgid "Would you like to set it as the buddy icon for this user?"
msgstr "Vil du sætte det som venneikon for denne bruger?"
-msgid ""
-"You can send this image as a file transfer, or use it as the buddy icon for "
-"this user."
-msgstr ""
-"Du kan sende dette billede som en filoverførsel, eller bruge det som "
-"venneikonet for denne bruger."
+msgid "You can send this image as a file transfer, or use it as the buddy icon for this user."
+msgstr "Du kan sende dette billede som en filoverførsel, eller bruge det som venneikonet for denne bruger."
-msgid ""
-"You can insert this image into this message, or use it as the buddy icon for "
-"this user"
-msgstr ""
-"Du kan indsætte dette billede i beskeden, eller bruge det som venneikon for "
-"denne bruger"
+msgid "You can insert this image into this message, or use it as the buddy icon for this user"
+msgstr "Du kan indsætte dette billede i beskeden, eller bruge det som venneikon for denne bruger"
#. I don't know if we really want to do anything here. Most of
#. * the desktop item types are crap like "MIME Type" (I have no
@@ -13550,12 +12530,8 @@ msgstr ""
msgid "Cannot send launcher"
msgstr "Kan ikke sende starter"
-msgid ""
-"You dragged a desktop launcher. Most likely you wanted to send the target of "
-"this launcher instead of this launcher itself."
-msgstr ""
-"Du har trukket en skrivebordsstarter. Sandsynligvis vil du sende hvad "
-"starteren peger på, og ikke starteren selv."
+msgid "You dragged a desktop launcher. Most likely you wanted to send the target of this launcher instead of this launcher itself."
+msgstr "Du har trukket en skrivebordsstarter. Sandsynligvis vil du sende hvad starteren peger på, og ikke starteren selv."
#, c-format
msgid ""
@@ -13584,7 +12560,7 @@ msgid "_Copy Link Location"
msgstr "_Kopiér henvisningsadresse"
msgid "_Copy Email Address"
-msgstr "_Kopiér e-post-adresse"
+msgstr "_Kopiér e-mail-adresse"
msgid "_Open File"
msgstr "_Åbn fil"
@@ -13631,7 +12607,7 @@ msgid "_Add..."
msgstr "_Tilføj..."
msgid "_Open Mail"
-msgstr "_Åbn e-post"
+msgstr "_Åbn e-mail"
msgid "_Edit"
msgstr "_Redigér"
@@ -13713,12 +12689,8 @@ msgstr "Ven er afkoblet"
msgid "Point values to use when..."
msgstr "Pointværdier der skal bruges når..."
-msgid ""
-"The buddy with the <i>largest score</i> is the buddy who will have priority "
-"in the contact.\n"
-msgstr ""
-"Vennen med det <i>højeste pointtal</i> er den ven, som vil have prioritet i "
-"kontakten.\n"
+msgid "The buddy with the <i>largest score</i> is the buddy who will have priority in the contact.\n"
+msgstr "Vennen med det <i>højeste pointtal</i> er den ven, som vil have prioritet i kontakten.\n"
msgid "Use last buddy when scores are equal"
msgstr "Brug den sidste ven når pointtal er lige"
@@ -13738,18 +12710,12 @@ msgstr "Kontaktprioritet"
#. *< name
#. *< version
#. *< summary
-msgid ""
-"Allows for controlling the values associated with different buddy states."
-msgstr ""
-"Giver mulighed for at styre værdierne tilknyttet forskellige vennestatusser."
+msgid "Allows for controlling the values associated with different buddy states."
+msgstr "Giver mulighed for at styre værdierne tilknyttet forskellige vennestatusser."
#. *< description
-msgid ""
-"Allows for changing the point values of idle/away/offline states for buddies "
-"in contact priority computations."
-msgstr ""
-"Giver mulighed for at ændre værdierne af inaktiv/fraværende/afkoblet status "
-"for venner i beregninger af kontaktprioritet."
+msgid "Allows for changing the point values of idle/away/offline states for buddies in contact priority computations."
+msgstr "Giver mulighed for at ændre værdierne af inaktiv/fraværende/afkoblet status for venner i beregninger af kontaktprioritet."
msgid "Conversation Colors"
msgstr "Samtalefarver"
@@ -13844,12 +12810,8 @@ msgstr "Opdagelse af XMPP-tjenester"
msgid "Allows browsing and registering services."
msgstr "Tillader browsing og registrering af tjenester."
-msgid ""
-"This plugin is useful for registering with legacy transports or other XMPP "
-"services."
-msgstr ""
-"Dette udvidelsesmodul er nyttigt til registrering af ældre transportlag og "
-"andre XMPP-tjenester."
+msgid "This plugin is useful for registering with legacy transports or other XMPP services."
+msgstr "Dette udvidelsesmodul er nyttigt til registrering af ældre transportlag og andre XMPP-tjenester."
msgid "By conversation count"
msgstr "Efter antallet af samtaler"
@@ -13858,12 +12820,8 @@ msgid "Conversation Placement"
msgstr "Samtaleplacering"
#. Translators: "New conversations" should match the text in the preferences dialog and "By conversation count" should be the same text used above
-msgid ""
-"Note: The preference for \"New conversations\" must be set to \"By "
-"conversation count\"."
-msgstr ""
-"Bemærk: Indstillingerne for \"Nye samtaler\" skal sættes til \"Efter "
-"antallet af samtaler\"."
+msgid "Note: The preference for \"New conversations\" must be set to \"By conversation count\"."
+msgstr "Bemærk: Indstillingerne for \"Nye samtaler\" skal sættes til \"Efter antallet af samtaler\"."
msgid "Number of conversations per window"
msgstr "Antal samtaler pr. vindue"
@@ -13887,12 +12845,8 @@ msgstr "Ekstra placeringsindstillinger for samtaler."
#. *< summary
#. * description
-msgid ""
-"Restrict the number of conversations per windows, optionally separating IMs "
-"and Chats"
-msgstr ""
-"Begræns antallet af samtaler pr. vindue, med mulighed for seperate "
-"chatsamtale- og beskedvinduer"
+msgid "Restrict the number of conversations per windows, optionally separating IMs and Chats"
+msgstr "Begræns antallet af samtaler pr. vindue, med mulighed for seperate chatsamtale- og beskedvinduer"
#. Configuration frame
msgid "Mouse Gestures Configuration"
@@ -13925,14 +12879,12 @@ msgstr "Giver understøttelse for musegestusser"
#. * description
msgid ""
-"Allows support for mouse gestures in conversation windows. Drag the middle "
-"mouse button to perform certain actions:\n"
+"Allows support for mouse gestures in conversation windows. Drag the middle mouse button to perform certain actions:\n"
" • Drag down and then to the right to close a conversation.\n"
" • Drag up and then to the left to switch to the previous conversation.\n"
" • Drag up and then to the right to switch to the next conversation."
msgstr ""
-"Giver understøttelse for musegestusser i samtalevinduer. Træk i den "
-"midterste knap for at udføre forskellige handlinger:\n"
+"Giver understøttelse for musegestusser i samtalevinduer. Træk i den midterste knap for at udføre forskellige handlinger:\n"
" • Træk ned og derefter til højre, for at afslutte en samtale.\n"
" • Træk op og derefter til venstre, for at skifte til den forrige samtale.\n"
" • Træk op og derefter til højre, for at skifte til den næste samtale."
@@ -13956,12 +12908,8 @@ msgid "Select Buddy"
msgstr "Vælg ven"
#. Add the label.
-msgid ""
-"Select a person from your address book to add this buddy to, or create a new "
-"person."
-msgstr ""
-"Vælg en person fra din adressebog at føje denne ven til, eller opret en ny "
-"person."
+msgid "Select a person from your address book to add this buddy to, or create a new person."
+msgstr "Vælg en person fra din adressebog at føje denne ven til, eller opret en ny person."
#. Add the expander
msgid "User _details"
@@ -13972,19 +12920,19 @@ msgid "_Associate Buddy"
msgstr "_Tilknyt ven"
msgid "Unable to send email"
-msgstr "Kunne ikke sende e-post"
+msgstr "Kunne ikke sende e-mail"
msgid "The evolution executable was not found in the PATH."
msgstr "Den kørbare Evolution-fil blev ikke fundet i PATH."
msgid "An email address was not found for this buddy."
-msgstr "Der blev ikke fundet nogen e-post-adresse for denne ven."
+msgstr "Der blev ikke fundet nogen e-mail-adresse for denne ven."
msgid "Add to Address Book"
msgstr "Føj til adressebog"
msgid "Send Email"
-msgstr "Send e-post"
+msgstr "Send e-mail"
#. Configuration frame
msgid "Evolution Integration Configuration"
@@ -14030,7 +12978,7 @@ msgid "Last name:"
msgstr "Efternavn:"
msgid "Email:"
-msgstr "E-post:"
+msgstr "E-mail:"
#. *< type
#. *< ui_requirement
@@ -14080,7 +13028,7 @@ msgid "Mail Checker"
msgstr "Post-tjekker"
msgid "Checks for new local mail."
-msgstr "Tjekker efter lokal e-post."
+msgstr "Tjekker efter lokal e-mail."
msgid "Adds a small box to the buddy list that shows if you have new mail."
msgstr "Tilføjer en lille boks til vennelisten, der viser om du har ny post."
@@ -14103,12 +13051,8 @@ msgstr "Besked-v_induer"
msgid "C_hat windows"
msgstr "Sa_mtale-vinduer"
-msgid ""
-"A music messaging session has been requested. Please click the MM icon to "
-"accept."
-msgstr ""
-"Der er blevet anmodet om en musikbeskedssession. Klik på MM-ikonet for at "
-"acceptere."
+msgid "A music messaging session has been requested. Please click the MM icon to accept."
+msgstr "Der er blevet anmodet om en musikbeskedssession. Klik på MM-ikonet for at acceptere."
msgid "Music messaging session confirmed."
msgstr "Musikbeskeds-session bekræftet."
@@ -14147,12 +13091,8 @@ msgid "Music Messaging Plugin for collaborative composition."
msgstr "Musikbeskeds-udvidelsesmodul til samarbejde om komposition."
#. * summary
-msgid ""
-"The Music Messaging Plugin allows a number of users to simultaneously work "
-"on a piece of music by editing a common score in real-time."
-msgstr ""
-"Udvidelsesmodulet musikbeskeder tillader at flere brugere ad gangen arbejder "
-"på et stykke musik ved fælles at redigere noder i realtid."
+msgid "The Music Messaging Plugin allows a number of users to simultaneously work on a piece of music by editing a common score in real-time."
+msgstr "Udvidelsesmodulet musikbeskeder tillader at flere brugere ad gangen arbejder på et stykke musik ved fælles at redigere noder i realtid."
#. ---------- "Notify For" ----------
msgid "Notify For"
@@ -14233,8 +13173,7 @@ msgstr "Påmindelser om beskeder"
#. * summary
#. * description
msgid "Provides a variety of ways of notifying you of unread messages."
-msgstr ""
-"Giver mulighed for at påminde dig om ulæste beskeder på forskellige måder."
+msgstr "Giver mulighed for at påminde dig om ulæste beskeder på forskellige måder."
#. *< type
#. *< ui_requirement
@@ -14249,8 +13188,7 @@ msgstr "Udvidelsesmodul til demonstration af Pidgin"
#. *< version
#. * summary
msgid "An example plugin that does stuff - see the description."
-msgstr ""
-"Et eksempel-udvidelsesmodul, der gør forskellige ting - se beskrivelsen."
+msgstr "Et eksempel-udvidelsesmodul, der gør forskellige ting - se beskrivelsen."
#. * description
msgid ""
@@ -14343,16 +13281,10 @@ msgid "Raw"
msgstr "Rå"
msgid "Lets you send raw input to text-based protocols."
-msgstr ""
-"Giver dig mulighed for at sende rå inddata til tekstbaserede protokoller."
+msgstr "Giver dig mulighed for at sende rå inddata til tekstbaserede protokoller."
-msgid ""
-"Lets you send raw input to text-based protocols (XMPP, MSN, IRC, TOC). Hit "
-"'Enter' in the entry box to send. Watch the debug window."
-msgstr ""
-"Giver dig mulighed for at sende rå inddata til tekstbaserede protokoller "
-"(XMPP, MSN, IRC, TOC). Tryk \"Enter\" i indtastningsfeltet for at sende. "
-"Hold øje med fejlsøgningsvinduet."
+msgid "Lets you send raw input to text-based protocols (XMPP, MSN, IRC, TOC). Hit 'Enter' in the entry box to send. Watch the debug window."
+msgstr "Giver dig mulighed for at sende rå inddata til tekstbaserede protokoller (XMPP, MSN, IRC, TOC). Tryk \"Enter\" i indtastningsfeltet for at sende. Hold øje med fejlsøgningsvinduet."
#, c-format
msgid "You can upgrade to %s %s today."
@@ -14383,12 +13315,8 @@ msgid "Checks periodically for new releases."
msgstr "Tjekker periodisk efter nye udgivelser."
#. * description
-msgid ""
-"Checks periodically for new releases and notifies the user with the "
-"ChangeLog."
-msgstr ""
-"Tjekker periodisk efter nye udgivelser og viser brugeren hvilke ændringer, "
-"der er i den nye udgivelse."
+msgid "Checks periodically for new releases and notifies the user with the ChangeLog."
+msgstr "Tjekker periodisk efter nye udgivelser og viser brugeren hvilke ændringer, der er i den nye udgivelse."
#. *< major version
#. *< minor version
@@ -14407,12 +13335,8 @@ msgid "Conversation Window Send Button."
msgstr "Samtalevinduets sendeknap."
#. *< summary
-msgid ""
-"Adds a Send button to the entry area of the conversation window. Intended "
-"for use when no physical keyboard is present."
-msgstr ""
-"Tilføjer en sendeknap til indtastningsområdet i samtalevinduet. Beregnet til "
-"når der ikke er noget fysisk tastatur tilstede."
+msgid "Adds a Send button to the entry area of the conversation window. Intended for use when no physical keyboard is present."
+msgstr "Tilføjer en sendeknap til indtastningsområdet i samtalevinduet. Beregnet til når der ikke er noget fysisk tastatur tilstede."
msgid "Duplicate Correction"
msgstr "Duplikér rettelse"
@@ -14446,8 +13370,7 @@ msgstr "Du _sender:"
#. Created here so it can be passed to whole_words_button_toggled.
msgid "_Exact case match (uncheck for automatic case handling)"
-msgstr ""
-"_Præcist match på store og små bogstaver (afmarkér for automatisk håndtering)"
+msgstr "_Præcist match på store og små bogstaver (afmarkér for automatisk håndtering)"
msgid "Only replace _whole words"
msgstr "Erstat kun _hele ord"
@@ -14505,7 +13428,7 @@ msgid "Information dialog"
msgstr "Informationsdialogvindue"
msgid "Mail dialog"
-msgstr "E-post-dialogvindue"
+msgstr "E-mail-dialogvindue"
msgid "Question dialog"
msgstr "Spørgsmålsdialogvindue"
@@ -14640,47 +13563,38 @@ msgid "Customizes the message timestamp formats."
msgstr "Tilpasser formatet på tidsstempler i beskeder."
#. * description
-msgid ""
-"This plugin allows the user to customize conversation and logging message "
-"timestamp formats."
-msgstr ""
-"Dette udvidelsesmodul tillader brugeren selv at tilpasse formatet på "
-"tidsstempler i samtaler og logge."
+msgid "This plugin allows the user to customize conversation and logging message timestamp formats."
+msgstr "Dette udvidelsesmodul tillader brugeren selv at tilpasse formatet på tidsstempler i samtaler og logge."
#. Alerts
-#, fuzzy
msgid "Chatroom alerts"
-msgstr "Samtalerumsemblemer"
+msgstr "Alarmer for samtalerum"
-#, fuzzy
msgid "Chatroom message alerts _only where someone says your username"
-msgstr "\t_Kun når nogen siger dit brugernavn"
+msgstr "Kun advarsler fra samtalerum når en person siger _dit brugernavn"
#. Launcher integration
-#, fuzzy
msgid "Launcher Icon"
-msgstr "Gem ikon"
+msgstr "Ikon for programstarter"
msgid "_Disable launcher integration"
-msgstr ""
+msgstr "_Deaktivér integration af programstarter"
msgid "Show number of unread _messages on launcher icon"
-msgstr ""
+msgstr "Vis antallet af ulæste _beskeder i programstarterens ikon"
-#, fuzzy
msgid "Show number of unread co_nversations on launcher icon"
-msgstr "Antal samtaler pr. vindue"
+msgstr "Vis antallet af ulæste _samtaler i programstarterens ikon"
#. Messaging menu integration
-#, fuzzy
msgid "Messaging Menu"
-msgstr "Besked sendt"
+msgstr "Beskedmenu"
msgid "Show number of _unread messages for conversations in messaging menu"
-msgstr ""
+msgstr "Vis antallet af _ulæste samtalebeskeder i beskedmenuen"
msgid "Show _elapsed time for unread conversations in messaging menu"
-msgstr ""
+msgstr "Vis _gået tid for ulæste samtaler i beskedmenuen"
#. *< type
#. *< ui_requirement
@@ -14688,21 +13602,18 @@ msgstr ""
#. *< dependencies
#. *< priority
#. *< id
-#, fuzzy
msgid "Unity Integration"
-msgstr "Evolution-integration"
+msgstr "Unity-integration"
#. *< name
#. *< version
#. * summary
-#, fuzzy
msgid "Provides integration with Unity."
-msgstr "Giver integration med Evolution."
+msgstr "Giver integration med Unity."
#. * description
-#, fuzzy
msgid "Provides integration with Unity's messaging menu and launcher."
-msgstr "Giver integration med Evolution."
+msgstr "Giver integration med Unity's besked-menu og programstarter."
msgid "Audio"
msgstr "Lyd"
@@ -14729,7 +13640,7 @@ msgid "D_evice"
msgstr "E_nhed"
msgid "DROP"
-msgstr "SMID"
+msgstr "DROP"
msgid "Volume:"
msgstr "Lydstyrke:"
@@ -14738,7 +13649,7 @@ msgid "Silence threshold:"
msgstr "Grænseværdi for stilhed:"
msgid "Input and Output Settings"
-msgstr "Indstillinger for Input og output"
+msgstr "Indstillinger for input og output"
msgid "Microphone Test"
msgstr "Mikrofontest"
@@ -14762,8 +13673,7 @@ msgstr "Konfigurér din mikrofon og dit webkamera."
#. *< summary
msgid "Configure microphone and webcam settings for voice/video calls."
-msgstr ""
-"Konfigurér mikrofon- og webkamera-indstillinger til stemme/video-opkald."
+msgstr "Konfigurér mikrofon- og webkamera-indstillinger til stemme/video-opkald."
msgid "Opacity:"
msgstr "Ugennemsigtighed:"
@@ -14811,13 +13721,11 @@ msgstr "Variabel gennemsigtighed for venneliste og samtaler."
#. * description
msgid ""
-"This plugin enables variable alpha transparency on conversation windows and "
-"the buddy list.\n"
+"This plugin enables variable alpha transparency on conversation windows and the buddy list.\n"
"\n"
"* Note: This plugin requires Win2000 or greater."
msgstr ""
-"Dette udvidelsesmodul lader dig bruge variabel alphagennemsigtighed i "
-"samtalevinduer og vennelisten.\n"
+"Dette udvidelsesmodul lader dig bruge variabel alphagennemsigtighed i samtalevinduer og vennelisten.\n"
"\n"
"* Bemærk: Dette modul kræver Win2000 eller nyere."
@@ -14849,11 +13757,8 @@ msgstr "Indstillinger for Pidgin til Windows"
msgid "Options specific to Pidgin for Windows."
msgstr "Specifikke indstillinger for Pidgin til Windows."
-msgid ""
-"Provides options specific to Pidgin for Windows, such as buddy list docking."
-msgstr ""
-"Giver indstillinger specifikt til Pidgin for Windows, såsom dokning af "
-"venneliste."
+msgid "Provides options specific to Pidgin for Windows, such as buddy list docking."
+msgstr "Giver indstillinger specifikt til Pidgin for Windows, såsom dokning af venneliste."
msgid "<font color='#777777'>Logged out.</font>"
msgstr "<font color='#777777'>Loggede ud.</font>"
@@ -14881,31 +13786,21 @@ msgstr "Send og modtag rå XMPP-stanza'er."
#. * description
msgid "This plugin is useful for debugging XMPP servers or clients."
-msgstr ""
-"Dette udvidelsesmodul er nyttigt til fejlsøgning af XMPP-servere eller -"
-"klienter."
+msgstr "Dette udvidelsesmodul er nyttigt til fejlsøgning af XMPP-servere eller -klienter."
msgid "The installer is already running."
msgstr "Installeringsprogrammet kører allerede."
-msgid ""
-"An instance of Pidgin is currently running. Please exit Pidgin and try "
-"again."
-msgstr ""
-"Der kører allerede en instans af Pidgin. Vær venlig at afslutte Pidgin og "
-"prøv igen."
+msgid "An instance of Pidgin is currently running. Please exit Pidgin and try again."
+msgstr "Der kører allerede en instans af Pidgin. Vær venlig at afslutte Pidgin og prøv igen."
#. "Next >" appears on a button on the License Page of the Installer
msgid "Next >"
msgstr "Næste >"
#. $(^Name) is the current Version name (e.g. Pidgin 2.7.0). $_CLICK will become a translated version of "Click Next to continue." DO NOT translate the CLICK in $_CLICK. It will break the installer.
-msgid ""
-"$(^Name) is released under the GNU General Public License (GPL). The license "
-"is provided here for information purposes only. $_CLICK"
-msgstr ""
-"$(^Name) er frigivet under GPL-licensen. Licensen er kun medtaget her til "
-"generel orientering. $_CLICK"
+msgid "$(^Name) is released under the GNU General Public License (GPL). The license is provided here for information purposes only. $_CLICK"
+msgstr "$(^Name) er frigivet under GPL-licensen. Licensen er kun medtaget her til generel orientering. $_CLICK"
#. Installer Subsection Text
msgid "Pidgin Instant Messaging Client (required)"
@@ -14949,9 +13844,7 @@ msgstr "Opret Pidgin i startmenuen"
#. Installer Subsection Detailed Description
msgid "A multi-platform GUI toolkit, used by Pidgin"
-msgstr ""
-"Et udviklingsværktøj til grafiske grænseflader på tværs af platforme, som "
-"bruges af Pidgin"
+msgstr "Et udviklingsværktøj til grafiske grænseflader på tværs af platforme, som bruges af Pidgin"
#. Installer Subsection Text
msgid "Debug Symbols (for reporting crashes)"
@@ -14961,21 +13854,11 @@ msgstr "Fejlsøgningssymboler (til at rapportere nedbrud)"
msgid "Visit the Pidgin Web Page"
msgstr "Besøg Pidgins hjemmeside"
-msgid ""
-"Unable to uninstall the currently installed version of Pidgin. The new "
-"version will be installed without removing the currently installed version."
-msgstr ""
-"Kan ikke afinstallere den nuværende version af Pidgin. Den nye version vil "
-"blive installeret uden at fjerne den nuværende version."
+msgid "Unable to uninstall the currently installed version of Pidgin. The new version will be installed without removing the currently installed version."
+msgstr "Kan ikke afinstallere den nuværende version af Pidgin. Den nye version vil blive installeret uden at fjerne den nuværende version."
-msgid ""
-"Pidgin requires a compatible GTK+ Runtime (which doesn't appear to be "
-"already present).$\\rAre you sure you want to skip installing the GTK+ "
-"Runtime?"
-msgstr ""
-"Pidgin har brug for et kompatibelt GTK+-kørselsmiljø (hvilket ikke ser ud "
-"til at være til stede).$\\rEr du sikker på at du vil spring installeringen "
-"af et GTK+-kørselsmiljø over?"
+msgid "Pidgin requires a compatible GTK+ Runtime (which doesn't appear to be already present).$\\rAre you sure you want to skip installing the GTK+ Runtime?"
+msgstr "Pidgin har brug for et kompatibelt GTK+-kørselsmiljø (hvilket ikke ser ud til at være til stede).$\\rEr du sikker på at du vil spring installeringen af et GTK+-kørselsmiljø over?"
#. Installer Subsection Text
msgid "URI Handlers"
@@ -14987,47 +13870,23 @@ msgstr "Understøttelse af stavekontrol"
#. $R3 will display the URL that the Dictionary failed to download from
#, no-c-format
-msgid ""
-"Error Installing Spellchecking ($R3).$\\rIf retrying fails, manual "
-"installation instructions are at: http://developer.pidgin.im/wiki/Installing"
-"%20Pidgin#manual_win32_spellcheck_installation"
-msgstr ""
-"Fejl ved installering af stavekontrol ($R3).$\\rHvis gentagne forsøg fejler, "
-"er der manuelle instruktioner her: http://developer.pidgin.im/wiki/Installing"
-"%20Pidgin#manual_win32_spellcheck_installation"
+msgid "Error Installing Spellchecking ($R3).$\\rIf retrying fails, manual installation instructions are at: http://developer.pidgin.im/wiki/Installing%20Pidgin#manual_win32_spellcheck_installation"
+msgstr "Fejl ved installering af stavekontrol ($R3).$\\rHvis gentagne forsøg fejler, er der manuelle instruktioner her: http://developer.pidgin.im/wiki/Installing%20Pidgin#manual_win32_spellcheck_installation"
#. Installer Subsection Detailed Description
-msgid ""
-"Support for Spellchecking. (Internet connection required for installation)"
-msgstr ""
-"Understøttelse af stavekontrol. (Der kræves internetforbindelse for at kunne "
-"installere)"
+msgid "Support for Spellchecking. (Internet connection required for installation)"
+msgstr "Understøttelse af stavekontrol. (Der kræves internetforbindelse for at kunne installere)"
#. $R2 will display the URL that the Debug Symbols failed to download from
-msgid ""
-"Error Installing Debug Symbols ($R2).$\\rIf retrying fails, you may need to "
-"use the 'Offline Installer' from http://pidgin.im/download/windows/ ."
-msgstr ""
-"Fejl ved installeringen af fejlsøgningssymbolerne ($R2).$\\rHvis gentagne "
-"forsøg fejler, har du muligvis brug for det 'offline installeringsprogram' "
-"fra http://pidgin.im/download/windows/ ."
+msgid "Error Installing Debug Symbols ($R2).$\\rIf retrying fails, you may need to use the 'Offline Installer' from http://pidgin.im/download/windows/ ."
+msgstr "Fejl ved installeringen af fejlsøgningssymbolerne ($R2).$\\rHvis gentagne forsøg fejler, har du muligvis brug for det 'offline installeringsprogram' fra http://pidgin.im/download/windows/ ."
#. $R2 will display the URL that the GTK+ Runtime failed to download from
-msgid ""
-"Error Downloading the GTK+ Runtime ($R2).$\\rThis is required for Pidgin to "
-"function; if retrying fails, you may need to use the 'Offline Installer' "
-"from http://pidgin.im/download/windows/ ."
-msgstr ""
-"Fejl ved download af GTK+-kørselsmiljø ($R2).$\\rDette er nødvendigt for at "
-"Pidgin kan fungere; hvis gentagne forsøg fejler, har du muligvis brug for "
-"det 'offline installeringsprogram' fra http://pidgin.im/download/windows/ ."
+msgid "Error Downloading the GTK+ Runtime ($R2).$\\rThis is required for Pidgin to function; if retrying fails, you may need to use the 'Offline Installer' from http://pidgin.im/download/windows/ ."
+msgstr "Fejl ved download af GTK+-kørselsmiljø ($R2).$\\rDette er nødvendigt for at Pidgin kan fungere; hvis gentagne forsøg fejler, har du muligvis brug for det 'offline installeringsprogram' fra http://pidgin.im/download/windows/ ."
-msgid ""
-"The uninstaller could not find registry entries for Pidgin.$\\rIt is likely "
-"that another user installed this application."
-msgstr ""
-"Afinstallationen kunne ikke finde Pidgin i registreringsdatabasen.$\\rEn "
-"anden bruger har sandsynligvis installeret dette program."
+msgid "The uninstaller could not find registry entries for Pidgin.$\\rIt is likely that another user installed this application."
+msgstr "Afinstallationen kunne ikke finde Pidgin i registreringsdatabasen.$\\rEn anden bruger har sandsynligvis installeret dette program."
msgid "You do not have permission to uninstall this application."
msgstr "Du har ikke tilladelse til at afinstallere dette program."
@@ -15315,461 +14174,312 @@ msgstr "Du har ikke tilladelse til at afinstallere dette program."
# ?
#~ msgid "Publish Mobile"
#~ msgstr "Offentliggør mobil"
-
#~ msgid "Publish Contact"
#~ msgstr "Offentliggør kontakt"
-
#~ msgid "College"
#~ msgstr "Universitet"
-
#~ msgid "Horoscope"
#~ msgstr "Horoskop"
-
#~ msgid "Zodiac"
#~ msgstr "Stjernetegn"
-
#~ msgid "Blood"
#~ msgstr "Blod"
-
#~ msgid "True"
#~ msgstr "Sand"
-
#~ msgid "False"
#~ msgstr "Falsk"
-
#~ msgid "Modify Contact"
#~ msgstr "Ændr kontakt"
-
#~ msgid "Modify Address"
#~ msgstr "Ændr adresse"
-
#~ msgid "Modify Extended Information"
#~ msgstr "Ændr udvidet information"
-
#~ msgid "Modify Information"
#~ msgstr "Ret information"
-
#~ msgid "Update"
#~ msgstr "Opdatér"
-
#~ msgid "Could not change buddy information."
#~ msgstr "Kunne ikke ændre venneinformation."
-
#~ msgid "Buddy Memo"
#~ msgstr "Venne-huskeseddel"
-
#~ msgid "_Modify"
#~ msgstr "_Ret"
-
#~ msgid "Memo Modify"
#~ msgstr "Ret huskeseddel"
-
#~ msgid "Server says:"
#~ msgstr "Serveren siger:"
-
#~ msgid "%u requires verification: %s"
#~ msgstr "%u kræver godkendelse: %s"
-
#~ msgid "Add buddy question"
#~ msgstr "Tilføj vennespørgsmål"
-
#~ msgid "Enter answer here"
#~ msgstr "Indtast svar her"
-
#~ msgid "Send"
#~ msgstr "Send"
-
#~ msgid "Invalid answer."
#~ msgstr "Ugyldigt svar."
-
#~ msgid "Sorry, you're not my style."
#~ msgstr "Beklager, du er ikke min type."
-
#~ msgid "%u needs authorization"
#~ msgstr "%u mangler godkendelse"
-
#~ msgid "Add buddy authorize"
#~ msgstr "Tilføj vennegodkendelse"
-
#~ msgid "Enter request here"
#~ msgstr "Indtast anmodning her"
-
#~ msgid "Would you be my friend?"
#~ msgstr "Vil du være min ven?"
-
#~ msgid "QQ Buddy"
#~ msgstr "QQ-ven"
-
#~ msgid "Add buddy"
#~ msgstr "Tilføj ven"
-
#~ msgid "Invalid QQ Number"
#~ msgstr "Ugyldigt QQ-nummer"
-
#~ msgid "Failed sending authorize"
#~ msgstr "Godkendelse blev ikke sendt"
-
#~ msgid "Failed removing buddy %u"
#~ msgstr "Kunne ikke fjerne vennen %u"
-
#~ msgid "Failed removing me from %d's buddy list"
#~ msgstr "Fejl ved fjernelse af mig fra %ds venneliste"
-
#~ msgid "No reason given"
#~ msgstr "Ingen grund givet"
-
#~ msgid "You have been added by %s"
#~ msgstr "Du er blevet tilføjet af %s"
-
#~ msgid "Would you like to add him?"
#~ msgstr "Vil du tilføje ham?"
-
#~ msgid "Rejected by %s"
#~ msgstr "Afvist af %s"
-
#~ msgid "Message: %s"
#~ msgstr "Besked: %s"
-
#~ msgid "ID: "
#~ msgstr "Id: "
-
#~ msgid "Group ID"
#~ msgstr "Gruppe-id"
-
#~ msgid "QQ Qun"
#~ msgstr "QQ Qun"
-
#~ msgid "Please enter Qun number"
#~ msgstr "Indtast dit Qun-nummer"
-
#~ msgid "You can only search for permanent Qun\n"
#~ msgstr "Du kan kun søge efter permanente Qun\n"
-
#~ msgid "(Invalid UTF-8 string)"
#~ msgstr "(Ugyldig UTF-8-streng)"
-
#~ msgid "Not member"
#~ msgstr "Ikke medlem"
-
#~ msgid "Member"
#~ msgstr "Medlem"
-
#~ msgid "Requesting"
#~ msgstr "Anmoder"
-
#~ msgid "Admin"
#~ msgstr "Admin"
-
# ?
#~ msgid "Room Title"
#~ msgstr "Emne"
-
#~ msgid "Notice"
#~ msgstr "Bemærk"
-
# jeg gætter på at 'detail' skal forstås som 'in detail', altså 'i detaljer', heraf flertal. -Ask
#~ msgid "Detail"
#~ msgstr "Detaljer"
-
#~ msgid "Creator"
#~ msgstr "Opretter"
-
#~ msgid "Category"
#~ msgstr "Kategori"
-
#~ msgid "The Qun does not allow others to join"
#~ msgstr "Qun'en tillader ikke andre at tilslutte"
-
#~ msgid "Join QQ Qun"
#~ msgstr "Tilslut QQ Qun"
-
#~ msgid "Input request here"
#~ msgstr "Indtast forespørgsel her"
-
#~ msgid "Successfully joined Qun %s (%u)"
#~ msgstr "Tilsluttet Qun %s (%u)"
-
#~ msgid "Successfully joined Qun"
#~ msgstr "Tilsluttet Qun"
-
#~ msgid "Qun %u denied from joining"
#~ msgstr "Qun %u nægtet tilslutning"
-
#~ msgid "QQ Qun Operation"
#~ msgstr "QQ Qun-operation"
-
#~ msgid "Failed:"
#~ msgstr "Mislykkedes:"
-
#~ msgid "Join Qun, Unknown Reply"
#~ msgstr "Tilslut Qun, ukendt svar"
-
#~ msgid "Quit Qun"
#~ msgstr "Afslut Qun"
-
#~ msgid ""
#~ "Note, if you are the creator, \n"
#~ "this operation will eventually remove this Qun."
#~ msgstr ""
#~ "Bemærk, hvis du er opretteren, \n"
#~ "vil denne operation til sidst fjerne denne Qun."
-
#~ msgid "Sorry, you are not our style"
#~ msgstr "Beklager, du er ikke vores type"
-
#~ msgid "Successfully changed Qun members"
#~ msgstr "Ændrede Qun-medlemmer problemfrit"
-
#~ msgid "Successfully changed Qun information"
#~ msgstr "Ændrede Qun-information problemfrit"
-
#~ msgid "You have successfully created a Qun"
#~ msgstr "Du har oprettet en Qun"
-
#~ msgid "Would you like to set up detailed information now?"
#~ msgstr "Vil du opsætte detaljeret information nu?"
-
#~ msgid "Setup"
#~ msgstr "Opsætning"
-
#~ msgid "%u requested to join Qun %u for %s"
#~ msgstr "%u anmoder om at tilslutte Qun %u for %s"
-
#~ msgid "%u request to join Qun %u"
#~ msgstr "%u anmoder om at tilslutte Qun %u"
-
#~ msgid "Failed to join Qun %u, operated by admin %u"
#~ msgstr "Kunne ikke tilslutte Qun %u, styret af admin %u"
-
#~ msgid "<b>Joining Qun %u is approved by admin %u for %s</b>"
#~ msgstr "<b>Tilslutning i Qun %u godkendt af admin %u for %s</b>"
-
#~ msgid "<b>Removed buddy %u.</b>"
#~ msgstr "<b>Fjernede vennen %u.</b>"
-
#~ msgid "<b>New buddy %u joined.</b>"
#~ msgstr "<b>Ny ven %u tilsluttet.</b>"
-
#~ msgid "Unknown-%d"
#~ msgstr "Ukendt-%d"
-
#~ msgid "Level"
#~ msgstr "Niveau"
-
#~ msgid " VIP"
#~ msgstr " VIP"
-
#~ msgid " TCP"
#~ msgstr " TCP"
-
#~ msgid " FromMobile"
#~ msgstr " FromMobile"
-
#~ msgid " BindMobile"
#~ msgstr " BindMobile"
-
#~ msgid " Video"
#~ msgstr " Video"
-
#~ msgid " Zone"
#~ msgstr " Zone"
-
#~ msgid "Flag"
#~ msgstr "Flag"
-
#~ msgid "Ver"
#~ msgstr "Ver"
-
#~ msgid "Invalid name"
#~ msgstr "Ugyldigt navn"
-
#~ msgid "Select icon..."
#~ msgstr "Vælg ikon..."
-
#~ msgid "<b>Login time</b>: %d-%d-%d, %d:%d:%d<br>\n"
#~ msgstr "<b>Logindtid</b>: %d-%d-%d, %d:%d:%d<br>\n"
-
#~ msgid "<b>Total Online Buddies</b>: %d<br>\n"
#~ msgstr "<b>Tilkoblede venner i alt</b>: %d<br>\n"
-
#~ msgid "<b>Last Refresh</b>: %d-%d-%d, %d:%d:%d<br>\n"
#~ msgstr "<b>Sidste opdatering</b>: %d-%d-%d, %d:%d:%d<br>\n"
-
#~ msgid "<b>Server</b>: %s<br>\n"
#~ msgstr "<b>Server</b>: %s<br>\n"
-
#~ msgid "<b>Client Tag</b>: %s<br>\n"
#~ msgstr "<b>Klientmærke</b>: %s<br>\n"
-
#~ msgid "<b>Connection Mode</b>: %s<br>\n"
#~ msgstr "<b>Forbindelsestilstand</b>: %s<br>\n"
-
#~ msgid "<b>My Internet IP</b>: %s:%d<br>\n"
#~ msgstr "<b>Min IP-adresse</b>: %s:%d<br>\n"
-
#~ msgid "<b>Sent</b>: %lu<br>\n"
#~ msgstr "<b>Sendt</b>: %lu<br>\n"
-
#~ msgid "<b>Resend</b>: %lu<br>\n"
#~ msgstr "<b>Gensend</b>: %lu<br>\n"
-
#~ msgid "<b>Lost</b>: %lu<br>\n"
#~ msgstr "<b>Tabt</b>: %lu<br>\n"
-
#~ msgid "<b>Received</b>: %lu<br>\n"
#~ msgstr "<b>Modtaget</b>: %lu<br>\n"
-
#~ msgid "<b>Received Duplicate</b>: %lu<br>\n"
#~ msgstr "<b>Modtaget duplikeret</b>: %lu<br>\n"
-
#~ msgid "<b>Time</b>: %d-%d-%d, %d:%d:%d<br>\n"
#~ msgstr "<b>Tid</b>: %d-%d-%d, %d:%d:%d<br>\n"
-
#~ msgid "<b>IP</b>: %s<br>\n"
#~ msgstr "<b>IP</b>: %s<br>\n"
-
#~ msgid "<p><b>Original Author</b>:<br>\n"
#~ msgstr "<p><b>Oprindelig forfatter</b>:<br>\n"
-
#~ msgid "<p><b>Code Contributors</b>:<br>\n"
#~ msgstr "<p><b>Bidragydere til koden</b>:<br>\n"
-
#~ msgid "<p><b>Lovely Patch Writers</b>:<br>\n"
#~ msgstr "<p><b>Elskede programrettelsesskrivere</b>:<br>\n"
-
#~ msgid "<p><b>Acknowledgement</b>:<br>\n"
#~ msgstr "<p><b>Anerkendelse</b>:<br>\n"
-
#~ msgid "<p><b>Scrupulous Testers</b>:<br>\n"
#~ msgstr "<p><b>Omhyggelige testere</b>:<br>\n"
-
#~ msgid "<p><i>And, all the boys in the backroom...</i><br>\n"
#~ msgstr "<p><i>Og alle drengene i baggrunden...</i><br>\n"
-
#~ msgid "<i>Feel free to join us!</i> :)"
#~ msgstr "<i>Vær velkommen til at tilslutte os!</i> :)"
-
#~ msgid "About OpenQ %s"
#~ msgstr "Om OpenQ %s"
-
#~ msgid "Change Password"
#~ msgstr "Ændr adgangskode"
-
#~ msgid "Account Information"
#~ msgstr "Kontoinformation"
-
#~ msgid "Update all QQ Quns"
#~ msgstr "Opdatér alle QQ Qun'er"
-
#~ msgid "About OpenQ"
#~ msgstr "Om OpenQ"
-
#~ msgid "Modify Buddy Memo"
#~ msgstr "Ret venne-huskeseddel"
-
#~ msgid "QQ Protocol Plugin"
#~ msgstr "QQ-protokoludvidelsesmodul"
-
#~ msgid "Auto"
#~ msgstr "Auto"
-
#~ msgid "Select Server"
#~ msgstr "Vælg server"
-
#~ msgid "QQ2005"
#~ msgstr "QQ2005"
-
#~ msgid "QQ2007"
#~ msgstr "QQ2007"
-
#~ msgid "QQ2008"
#~ msgstr "QQ2008"
-
#~ msgid "Connect by TCP"
#~ msgstr "Tilslut med TCP"
-
#~ msgid "Show server notice"
#~ msgstr "Vis serverbemærkning"
-
#~ msgid "Show server news"
#~ msgstr "Vis servernyheder"
-
#~ msgid "Show chat room when msg comes"
#~ msgstr "Vis samtalerum når besked kommer"
-
#~ msgid "Keep alive interval (seconds)"
#~ msgstr "Hold i live-interval (sekunder)"
-
#~ msgid "Update interval (seconds)"
#~ msgstr "Opdateringsinterval (sekunder)"
-
#~ msgid "Unable to decrypt server reply"
#~ msgstr "Ude af stand til at afkryptere serversvar"
-
#~ msgid "Failed requesting token, 0x%02X"
#~ msgstr "Fejl ved forespørgsel efter nøgle, 0x%02X"
-
#~ msgid "Invalid token len, %d"
#~ msgstr "Ugyldig nøglelængde, %d"
-
#~ msgid "Redirect_EX is not currently supported"
#~ msgstr "Redirect_EX er for øjeblikket ikke understøttet"
-
#~ msgid "Activation required"
#~ msgstr "Aktivering påkrævet"
-
#~ msgid "Unknown reply code when logging in (0x%02X)"
#~ msgstr "Ukendt svarkode ved indlogning i (0x%02X)"
-
#~ msgid "Requesting captcha"
#~ msgstr "Anmoder om captcha"
-
#~ msgid "Checking captcha"
#~ msgstr "Kontrollerer captcha"
-
#~ msgid "Failed captcha verification"
#~ msgstr "Captcha-godkendelse mislykkedes"
-
#~ msgid "Captcha Image"
#~ msgstr "Captcha-billede"
-
#~ msgid "Enter code"
#~ msgstr "Indtast kode"
-
#~ msgid "QQ Captcha Verification"
#~ msgstr "QQ-Captcha-godkendelse"
-
#~ msgid "Enter the text from the image"
#~ msgstr "Indtast teksten fra billedet"
-
#~ msgid "Unknown reply when checking password (0x%02X)"
#~ msgstr "Ukendt svar ved kontrol af adgangskode (0x%02X)"
-
#~ msgid ""
#~ "Unknown reply code when logging in (0x%02X):\n"
#~ "%s"
#~ msgstr ""
#~ "Ukendt svarkode ved indlogning i (0x%02X):\n"
#~ "%s"
-
#~ msgid "Socket error"
#~ msgstr "Sokkel-fejl"
-
#~ msgid "Getting server"
#~ msgstr "Henter server"
-
#~ msgid "Requesting token"
#~ msgstr "Anmoder om nøgle"
-
#~ msgid "Invalid server or port"
#~ msgstr "Ugyldig server eller port"
-
#~ msgid "Connecting to server"
#~ msgstr "Tilslutter server"
-
#~ msgid "QQ Error"
#~ msgstr "QQ-fejl"
-
#~ msgid ""
#~ "Server News:\n"
#~ "%s\n"
@@ -15780,54 +14490,40 @@ msgstr "Du har ikke tilladelse til at afinstallere dette program."
#~ "%s\n"
#~ "%s\n"
#~ "%s"
-
#~ msgid "%s:%s"
#~ msgstr "%s:%s"
-
#~ msgid "From %s:"
#~ msgstr "Fra %s:"
-
#~ msgid ""
#~ "Server notice From %s: \n"
#~ "%s"
#~ msgstr ""
#~ "Servernotits fra %s: \n"
#~ "%s"
-
#~ msgid "Unknown SERVER CMD"
#~ msgstr "Ukendt SERVER CMD"
-
#~ msgid ""
#~ "Error reply of %s(0x%02X)\n"
#~ "Room %u, reply 0x%02X"
#~ msgstr ""
#~ "Fejlsvar %s(0x%02X)\n"
#~ "Rum %u, svar 0x%02X"
-
#~ msgid "QQ Qun Command"
#~ msgstr "QQ Qun-kommando"
-
#~ msgid "Unable to decrypt login reply"
#~ msgstr "Ude af stand til at afkryptere logindsvar"
-
#~ msgid "Unknown LOGIN CMD"
#~ msgstr "Ukendt LOGIN CMD"
-
#~ msgid "Unknown CLIENT CMD"
#~ msgstr "Ukendt CLIENT CMD"
-
#~ msgid "%d has declined the file %s"
#~ msgstr "%d har afslået filen %s"
-
#~ msgid "File Send"
#~ msgstr "Send fil"
-
#~ msgid "%d cancelled the transfer of %s"
#~ msgstr "%d annullerede overførslen af %s"
-
#~ msgid "bug master"
#~ msgstr "fejlbestyrer"
-
#, fuzzy
#~ msgid "An error occurred on the in-band bytestream transfer\n"
#~ msgstr "Der opstod en fejl ved forsøg på at åbne filen."
@@ -16642,6 +15338,7 @@ msgstr "Du har ikke tilladelse til at afinstallere dette program."
#~ msgstr "Gem som..."
#~ msgid "%s requests %s to accept %d file: %s (%.2f %s)%s%s"
+
#~ msgid_plural "%s requests %s to accept %d files: %s (%.2f %s)%s%s"
#~ msgstr[0] "%s anmoder %s om at acceptere %d fil: %s (%.2f %s)%s%s"
#~ msgstr[1] "%s anmoder %s om at acceptere %d filer: %s (%.2f %s)%s%s"
@@ -17222,6 +15919,7 @@ msgstr "Du har ikke tilladelse til at afinstallere dette program."
#~ "%d buddy from group %s was not removed because it belongs to an account "
#~ "which is disabled or offline. This buddy and the group were not "
#~ "removed.\n"
+
#~ msgid_plural ""
#~ "%d buddies from group %s were not removed because they belong to accounts "
#~ "which are currently disabled or offline. These buddies and the group "
diff --git a/po/de.po b/po/de.po
index 2f919d8170..cbca6a678f 100644
--- a/po/de.po
+++ b/po/de.po
@@ -4,7 +4,7 @@
# Copyright (C) 2002, Karsten Weiss <knweiss@gmx.de>
# Copyright (C) 2002-2013, Björn Voigt <bjoernv@arcor.de>,
# Jochen Kemnade <jochenkemnade@web.de>
-# Copyright (C) 2013-2014, Björn Voigt <bjoernv@arcor.de>
+# Copyright (C) 2013, Björn Voigt <bjoernv@arcor.de>,
#
# This file is distributed under the same license as the Pidgin package.
#
@@ -51,17 +51,6 @@ msgstr ""
" -n, --nologin nicht automatisch anmelden\n"
" -v, --version zeigt aktuelle Version und beendet das Programm\n"
-#, c-format
-msgid ""
-"%s encountered errors migrating your settings from %s to %s. Please "
-"investigate and complete the migration by hand. Please report this error at "
-"http://developer.pidgin.im"
-msgstr ""
-"%s ist beim Übertragen Ihrer Einstellungen von %s nach %s auf Fehler "
-"gestoßen. Bitte untersuchen Sie das Problem und vervollständigen Sie die "
-"Migration per Hand. Bitte melden Sie diesen Fehler auf http://developer."
-"pidgin.im"
-
#. the user did not fill in the captcha
msgid "Error"
msgstr "Fehler"
@@ -214,6 +203,8 @@ msgstr "Das gewählte Konto ist nicht online."
msgid "Error adding buddy"
msgstr "Fehler beim Hinzufügen des Buddys"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "Username"
msgstr "Benutzer"
@@ -348,6 +339,8 @@ msgid "View Log"
msgstr "Mitschnitt anzeigen"
#. General
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "Nickname"
msgstr "Spitzname"
@@ -781,7 +774,6 @@ msgid_plural "File Transfers - %d%% of %d files"
msgstr[0] "Dateiübertragungen - %d%% von %d Datei"
msgstr[1] "Dateiübertragungen - %d%% von %d Dateien"
-#. Create the window.
msgid "File Transfers"
msgstr "Dateiübertragungen"
@@ -800,8 +792,11 @@ msgstr "Geschwindigkeit"
msgid "Remaining"
msgstr "Verbleibend"
-#. XXX: Use of ggp_str_to_uin() is an ugly hack!
+#. TODO: Check whether it's correct to call prepend_pair_html,
+#. or if we should be using prepend_pair_plaintext
#. presence
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "Status"
msgstr "Status"
@@ -907,7 +902,6 @@ msgstr "Anrufen..."
msgid "Hangup"
msgstr "Auflegen"
-#. Number of actions
msgid "Accept"
msgstr "Akzeptieren"
@@ -1037,11 +1031,6 @@ msgstr "Plugin installieren..."
msgid "Configure Plugin"
msgstr "Plugin konfigurieren"
-#. copy the preferences to tmp values...
-#. * I liked "take affect immediately" Oh well :-(
-#. (that should have been "effect," right?)
-#. Back to instant-apply! I win! BU-HAHAHA!
-#. Create the window
msgid "Preferences"
msgstr "Einstellungen"
@@ -1230,8 +1219,8 @@ msgstr "Unterhaltungen"
msgid "Logging"
msgstr "Mitschnitt"
-msgid "You must fill all the required fields."
-msgstr "Sie müssen alle erforderlichen Felder ausfüllen."
+msgid "You must properly fill all the required fields."
+msgstr "Sie müssen alle erforderlichen Felder richtig ausfüllen."
msgid "The required fields are underlined."
msgstr "Die erforderlichen Felder sind unterstrichen."
@@ -1254,7 +1243,6 @@ msgstr "Drücken Sie 'Enter', um mehr Räume dieser Kategorie zu finden."
msgid "Get"
msgstr "Abrufen"
-#. Create the window.
msgid "Room List"
msgstr "Raumliste"
@@ -1390,6 +1378,8 @@ msgstr "Titel"
msgid "Type"
msgstr "Typ"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
#. Statuses are almost all the same. Define a macro to reduce code repetition.
#. PurpleStatusPrimitive
#. id - use default
@@ -1398,6 +1388,8 @@ msgstr "Typ"
#. user_settable
#. not independent
#. Attributes - each status can have a message.
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "Message"
msgstr "Nachricht"
@@ -1739,25 +1731,26 @@ msgstr "Die präsentierte Zertifizierungskette ist ungültig."
msgid "The certificate has been revoked."
msgstr "Das Zertifikat wurde widerrufen."
+msgid "The certificate was rejected by the user."
+msgstr "Das Zertifikat wurde vom Benutzer abgelehnt."
+
msgid "An unknown certificate error occurred."
msgstr "Es ist ein unbekannter Zertifikatfehler aufgetreten."
+msgid "(MATCH)"
+msgstr "(stimmt überein)"
+
msgid "(DOES NOT MATCH)"
msgstr "(stimmt nicht überein)"
-#. Make messages
#, c-format
msgid "%s has presented the following certificate for just-this-once use:"
msgstr ""
"%s hat das folgende Zertifikat für den einmaligen Gebrauch präsentiert:"
#, c-format
-msgid ""
-"Common name: %s %s\n"
-"Fingerprint (SHA1): %s"
-msgstr ""
-"Allgemeiner Name (Common name:) %s %s\n"
-"Fingerabdruck (SHA1): %s"
+msgid "Common name: %s %s"
+msgstr "Allgemeiner Name (Common name): %s %s"
#. TODO: Find what the handle ought to be
msgid "Single-use Certificate Verification"
@@ -1773,7 +1766,6 @@ msgstr "Zertifizierungsstelle"
msgid "SSL Peers Cache"
msgstr "SSL-Peers-Zwischenspeicher"
-#. Make messages
#, c-format
msgid "Accept certificate for %s?"
msgstr "Akzeptieren Sie das Zertifikat für %s?"
@@ -1782,9 +1774,6 @@ msgstr "Akzeptieren Sie das Zertifikat für %s?"
msgid "SSL Certificate Verification"
msgstr "SSL-Zertifikatsüberprüfung"
-msgid "_View Certificate..."
-msgstr "Ze_rtifikat ansehen..."
-
#, c-format
msgid "The certificate for %s could not be validated."
msgstr "Das Zertifikat für %s konnte nicht validiert werden."
@@ -1805,27 +1794,6 @@ msgstr ""
"dass Sie tatsächlich nicht mit dem Dienst verbunden sind, mit dem Sie "
"glauben verbunden zu sein."
-#. Make messages
-#, c-format
-msgid ""
-"Common name: %s\n"
-"\n"
-"Fingerprint (SHA1): %s\n"
-"\n"
-"Activation date: %s\n"
-"Expiration date: %s\n"
-msgstr ""
-"Allgemeiner Name (Common name): %s\n"
-"\n"
-"Fingerabdruck (SHA1): %s\n"
-"\n"
-"Aktivierungsdatum: %s\n"
-"Ablaufdatum: %s\n"
-
-#. TODO: Find what the handle ought to be
-msgid "Certificate Information"
-msgstr "Zertifikat-Information"
-
#. show error to user
msgid "Registration Error"
msgstr "Registrierungsfehler"
@@ -2760,6 +2728,9 @@ msgstr "Offline-Nachrichten-Emulation"
msgid "Save messages sent to an offline user as pounce."
msgstr "Sichert Nachrichten an einen Offline-Benutzer als Alarm."
+msgid "Offline message"
+msgstr "Offline-Nachricht"
+
msgid ""
"The rest of the messages will be saved as pounces. You can edit/delete the "
"pounce from the `Buddy Pounce' dialog."
@@ -2897,6 +2868,23 @@ msgstr "Einfaches Plugin"
msgid "Tests to see that most things are working."
msgstr "Tests, um zu sehen, ob das meiste funktioniert."
+#. Make messages
+#, c-format
+msgid ""
+"Common name: %s\n"
+"\n"
+"Fingerprint (SHA1): %s\n"
+"\n"
+"Activation date: %s\n"
+"Expiration date: %s\n"
+msgstr ""
+"Allgemeiner Name (Common name): %s\n"
+"\n"
+"Fingerabdruck (SHA1): %s\n"
+"\n"
+"Aktivierungsdatum: %s\n"
+"Ablaufdatum: %s\n"
+
#. Scheme name
msgid "X.509 Certificates"
msgstr "X.509-Zertifikate"
@@ -3029,19 +3017,29 @@ msgid ""
msgstr "Keine Verbindung zum lokalen mDNS-Server. Ist er aktiviert?"
# old strings
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "First name"
msgstr "Vorname"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "Last name"
msgstr "Nachname"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
#. email
msgid "Email"
msgstr "E-Mail"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "AIM Account"
msgstr "AIM-Konto"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "XMPP Account"
msgstr "XMPP-Konto"
@@ -3080,14 +3078,6 @@ msgstr ""
msgid "Error communicating with local mDNSResponder."
msgstr "Fehler bei der Kommunikation mit lokalem mDNSResponder."
-msgid "Invalid proxy settings"
-msgstr "Falsche Proxy-Einstellungen"
-
-msgid ""
-"Either the host name or port number specified for your given proxy type is "
-"invalid."
-msgstr "Hostname oder Portnummer Ihres Proxys sind falsch angegeben."
-
msgid "Save Buddylist..."
msgstr "Buddy-Liste speichern..."
@@ -3116,50 +3106,6 @@ msgstr "Buddy-Liste speichern..."
msgid "Load buddylist from file..."
msgstr "Buddy-Liste aus Datei laden..."
-msgid "City"
-msgstr "Stadt"
-
-msgid "Year of birth"
-msgstr "Geburtsjahr"
-
-#. gender
-msgid "Gender"
-msgstr "Geschlecht"
-
-msgid "Male or female"
-msgstr "Männlich oder weiblich"
-
-#. 0
-msgid "Male"
-msgstr "Männlich"
-
-msgid "Female"
-msgstr "Weiblich"
-
-msgid "Only online"
-msgstr "Nur online"
-
-msgid "Find buddies"
-msgstr "Suche Buddys"
-
-msgid "Please, enter your search criteria below"
-msgstr "Bitte geben Sie Ihre Suchkriterien unten ein"
-
-msgid "Show status to:"
-msgstr "Zeige Status:"
-
-msgid "All people"
-msgstr "Allen Leuten"
-
-msgid "Only buddies"
-msgstr "Nur Buddys"
-
-msgid "Change status broadcasting"
-msgstr "Statusveröffentlichung ändern"
-
-msgid "Please, select who can see your status"
-msgstr "Bitte wählen Sie, wer Ihren Status sehen darf"
-
#, c-format
msgid "Select a chat for buddy: %s"
msgstr "Wählen Sie einen Chat für den Benutzer: %s"
@@ -3167,52 +3113,6 @@ msgstr "Wählen Sie einen Chat für den Benutzer: %s"
msgid "Add to chat..."
msgstr "Zum Chat hinzufügen..."
-#. 0
-#. Global
-msgid "Available"
-msgstr "Verfügbar"
-
-#. 2
-msgid "Chatty"
-msgstr "Gesprächig"
-
-#. 3
-msgid "Do Not Disturb"
-msgstr "Nicht stören"
-
-#. 1
-#. get_yahoo_status_from_purple_status() returns YAHOO_STATUS_CUSTOM for
-#. * the generic away state (YAHOO_STATUS_TYPE_AWAY) with no message
-#. Away stuff
-msgid "Away"
-msgstr "Abwesend"
-
-msgid "UIN"
-msgstr "UIN"
-
-#. first name
-#. optional information
-msgid "First Name"
-msgstr "Vorname"
-
-msgid "Birth Year"
-msgstr "Geburtsjahr"
-
-msgid "Unable to display the search results."
-msgstr "Kann Suchergebnisse nicht anzeigen."
-
-msgid "Gadu-Gadu Public Directory"
-msgstr "Öffentliches Gadu-Gadu-Verzeichnis"
-
-msgid "Search results"
-msgstr "Suchergebnisse"
-
-msgid "No matching users found"
-msgstr "Keine entsprechenden Benutzer gefunden"
-
-msgid "There are no users matching your search criteria."
-msgstr "Es gibt keine Benutzer, die Ihrer Anfrage entsprechen."
-
msgid "Unable to read from socket"
msgstr "Socket kann nicht gelesen werden"
@@ -3222,6 +3122,30 @@ msgstr "Socket kann nicht gelesen werden"
msgid "Connected"
msgstr "Verbunden"
+msgid "Unable to resolve hostname"
+msgstr "Hostname konnte nicht aufgelöst werden"
+
+msgid "Incorrect password"
+msgstr "Falsches Passwort"
+
+msgid "SSL Connection Failed"
+msgstr "SSL-Verbindung gescheitert"
+
+msgid ""
+"Your account has been disabled because too many incorrect passwords were "
+"entered"
+msgstr ""
+"Ihr Konto wurde wegen zu vielen falsch eingegebenen Passworten gesperrt"
+
+msgid "Service temporarily unavailable"
+msgstr "Dienst momentan nicht verfügbar"
+
+msgid "Error connecting to proxy server"
+msgstr "Fehler beim Verbinden mit dem Proxy-Server"
+
+msgid "Error connecting to master server"
+msgstr "Fehler beim Verbinden mit dem Master-Server"
+
msgid "Connection failed"
msgstr "Verbindung fehlgeschlagen"
@@ -3231,15 +3155,23 @@ msgstr "Zum Chat hinzufügen"
msgid "Chat _name:"
msgstr "Chat_name:"
-#, c-format
-msgid "Unable to resolve hostname '%s': %s"
-msgstr "Hostname '%s' kann nicht aufgelöst werden: %s"
+msgid "The username specified is invalid."
+msgstr "Der angegebene Benutzername ist ungültig."
+
+msgid "SSL support unavailable"
+msgstr "SSL-Unterstützung nicht verfügbar"
+
+msgid "Provided server IP address is not valid"
+msgstr "Die angegebene IP-Adresse ist nicht gültig"
#. 1. connect to server
#. connect to the server
msgid "Connecting"
msgstr "Verbindungsaufbau"
+msgid "Image is too large, please try smaller one."
+msgstr "Das Bild ist zu groß. Bitte versuchen Sie ein kleineres."
+
msgid "Chat error"
msgstr "Chatfehler"
@@ -3249,12 +3181,21 @@ msgstr "Dieser Chatname existiert bereits"
msgid "Not connected to the server"
msgstr "Nicht mit dem Server verbunden"
+msgid "Change password..."
+msgstr "Passwort ändern..."
+
+msgid "Show status only for buddies"
+msgstr "Zeige Status nur für Buddys"
+
msgid "Find buddies..."
msgstr "Finde Buddys..."
msgid "Save buddylist to file..."
msgstr "Buddy-Liste in Datei speichern..."
+msgid "GG number..."
+msgstr "GG-Nummer..."
+
#. magic
#. major_version
#. minor_version
@@ -3273,25 +3214,24 @@ msgstr "Gadu-Gadu-Protokoll-Plugin"
msgid "Polish popular IM"
msgstr "Beliebter polnischer IM-Dienst"
-msgid "Gadu-Gadu User"
-msgstr "Gadu-Gadu-Benutzer"
-
msgid "GG server"
msgstr "GG-Server"
-msgid "Don't use encryption"
-msgstr "Keine Verschlüsselung benutzen"
-
msgid "Use encryption if available"
msgstr "Verschlüsselung benutzen, wenn verfügbar"
-#. TODO
msgid "Require encryption"
msgstr "Verschlüsselung fordern"
+msgid "Don't use encryption"
+msgstr "Keine Verschlüsselung benutzen"
+
msgid "Connection security"
msgstr "Verbindungssicherheit"
+msgid "Show links from strangers"
+msgstr "Zeige Links von Fremden"
+
#, c-format
msgid "Unknown command: %s"
msgstr "Unbekanntes Kommando: %s"
@@ -3344,9 +3284,6 @@ msgstr "_Passwort:"
msgid "IRC nick and server may not contain whitespace"
msgstr "IRC-Server und -Spitzname dürfen keinen Leerraum enthalten"
-msgid "SSL support unavailable"
-msgstr "SSL-Unterstützung nicht verfügbar"
-
msgid "Unable to connect"
msgstr "Verbindung nicht möglich"
@@ -3355,6 +3292,13 @@ msgstr "Verbindung nicht möglich"
msgid "Unable to connect: %s"
msgstr "Verbinden nicht möglich: %s"
+#. 1
+#. get_yahoo_status_from_purple_status() returns YAHOO_STATUS_CUSTOM for
+#. * the generic away state (YAHOO_STATUS_TYPE_AWAY) with no message
+#. Away stuff
+msgid "Away"
+msgstr "Abwesend"
+
msgid "Server closed the connection"
msgstr "Der Server hat die Verbindung beendet"
@@ -3380,6 +3324,8 @@ msgid "The IRC Protocol Plugin that Sucks Less"
msgstr "Das IRC-Protokoll-Plugin mit weniger Problemen"
#. set up account ID as user:server
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "Server"
msgstr "Server"
@@ -3393,9 +3339,6 @@ msgstr "Kodierungen"
msgid "Auto-detect incoming UTF-8"
msgstr "Eingehendes UTF-8 automatisch erkennen"
-msgid "Ident name"
-msgstr "Benutzername"
-
msgid "Real name"
msgstr "Echter Name"
@@ -3446,12 +3389,6 @@ msgstr " <i>(identifiziert)</i>"
msgid "Nick"
msgstr "Spitzname"
-msgid "Login name"
-msgstr "Anmeldename"
-
-msgid "Host name"
-msgstr "Rechnername"
-
msgid "Currently on"
msgstr "Im Moment in"
@@ -3968,6 +3905,8 @@ msgstr "Die Verbindung mit dem Server konnte nicht hergestellt werden: %s"
msgid "Unable to establish SSL connection"
msgstr "Kann SSL-Verbindung nicht erstellen"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "Full Name"
msgstr "Vollständiger Name"
@@ -4038,9 +3977,13 @@ msgstr ""
"Die folgenden Punkte sind optional. Geben Sie nur die Informationen an, die "
"Sie angeben möchten."
+#. TODO: Check whether it's correct to call prepend_pair_html,
+#. or if we should be using prepend_pair_plaintext
msgid "Client"
msgstr "Client"
+#. TODO: Check whether it's correct to call prepend_pair_html,
+#. or if we should be using prepend_pair_plaintext
msgid "Operating System"
msgstr "Betriebssystem"
@@ -4050,6 +3993,8 @@ msgstr "Lokale Zeit"
msgid "Priority"
msgstr "Priorität"
+#. TODO: Check whether it's correct to call prepend_pair_html,
+#. or if we should be using prepend_pair_plaintext
msgid "Resource"
msgstr "Ressource"
@@ -4114,6 +4059,11 @@ msgstr "Abmelden"
msgid "JID"
msgstr "JID"
+#. first name
+#. optional information
+msgid "First Name"
+msgstr "Vorname"
+
#. last name
msgid "Last Name"
msgstr "Nachname"
@@ -4289,6 +4239,9 @@ msgstr "Aufheben der Registrierung erfolgreich"
msgid "Unregistration Failed"
msgstr "Aufheben der Registrierung gescheitert"
+msgid "City"
+msgstr "Stadt"
+
msgid "State"
msgstr "Provinz/Bundesland"
@@ -4362,6 +4315,8 @@ msgstr "Server unterstützt kein Blockieren"
msgid "Not Authorized"
msgstr "Nicht autorisiert"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "Mood"
msgstr "Stimmung"
@@ -4402,6 +4357,14 @@ msgstr "Name der Stimmung"
msgid "Mood Comment"
msgstr "Stimmungskommentar"
+#. 2
+msgid "Chatty"
+msgstr "Gesprächig"
+
+#. 3
+msgid "Do Not Disturb"
+msgstr "Nicht stören"
+
#. primitive
#. ID
#. name - use default
@@ -4704,6 +4667,9 @@ msgstr "Wählen Sie eine Ressource"
msgid "Initiate Media"
msgstr "Initiiere Medien"
+msgid "Failed to specify mood"
+msgstr "Setzen der Stimmung gescheitert"
+
msgid "Account does not support PEP, can't set mood"
msgstr "Konto unterstützt kein PEP, kann die Stimmung nicht setzen"
@@ -4763,8 +4729,13 @@ msgstr "ping &lt;jid&gt;:\tBenutzer/Komponente/Server anpingen."
msgid "buzz: Buzz a user to get their attention"
msgstr "buzz: Einen Kontakt anrufen, um seine Aufmerksamkeit zu erhalten"
-msgid "mood: Set current user mood"
-msgstr "mood: Setze die aktuelle Benutzerstimmung"
+msgid "mood &lt;mood&gt; [text]: Set current user mood"
+msgstr "mood &lt;mood&gt; [Text]: Setze die aktuelle Benutzerstimmung"
+
+#. 0
+#. Global
+msgid "Available"
+msgstr "Verfügbar"
msgid "Extended Away"
msgstr "Abwesend (erweitert)"
@@ -5290,9 +5261,6 @@ msgstr "Zu viele Treffer zu einem FND"
msgid "Not logged in"
msgstr "Nicht angemeldet"
-msgid "Service temporarily unavailable"
-msgstr "Dienst momentan nicht verfügbar"
-
msgid "Database server error"
msgstr "Fehler des Datenbank-Servers"
@@ -5637,9 +5605,17 @@ msgstr "Allgemein"
msgid "Age"
msgstr "Alter"
+#. gender
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
+msgid "Gender"
+msgstr "Geschlecht"
+
msgid "Occupation"
msgstr "Beruf"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "Location"
msgstr "Ort"
@@ -6047,9 +6023,6 @@ msgstr "Buddy aus dem Adressbuch löschen?"
msgid "Do you want to delete this buddy from your address book as well?"
msgstr "Möchten Sie diesen Buddy außerdem aus Ihrem Adressbuch löschen?"
-msgid "The username specified is invalid."
-msgstr "Der angegebene Benutzername ist ungültig."
-
msgid "The Display Name you entered is invalid."
msgstr "Der eingegebene Anzeigename ist ungültig."
@@ -6065,6 +6038,8 @@ msgstr "Profil-Aktualisierungsfehler"
#. no profile information yet, so we cannot update
#. (reference: "libpurple/request.h")
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "Profile"
msgstr "Profil"
@@ -6078,6 +6053,13 @@ msgstr ""
msgid "Display Name"
msgstr "Anzeigename"
+msgid "Female"
+msgstr "Weiblich"
+
+#. 0
+msgid "Male"
+msgstr "Männlich"
+
#. about me
msgid "About Me"
msgstr "Über mich"
@@ -6182,18 +6164,12 @@ msgstr "Startbildschirm anzeigen..."
msgid "About..."
msgstr "Über..."
+#. we are trying to send a file to MXit
+#. need to reserve some space for packet headers
#. the file is too big
msgid "The file you are trying to send is too large!"
msgstr "Die Datei, die Sie senden möchten ist zu groß!"
-#. file read error
-msgid "Unable to access the local file"
-msgstr "Kann nicht auf die lokale Datei zugreifen"
-
-#. file write error
-msgid "Unable to save the file"
-msgstr "Kann die Datei nicht speichern"
-
msgid ""
"Unable to connect to the MXit HTTP server. Please check your server settings."
msgstr ""
@@ -6310,6 +6286,8 @@ msgstr "Sie haben eingeladen"
msgid "Loading menu..."
msgstr "Lade das Menü..."
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "Status Message"
msgstr "Status-Nachricht"
@@ -6696,18 +6674,26 @@ msgstr "Bildschirmauflösung (dpi)"
msgid "Base font size (points)"
msgstr "Basis-Schriftgröße (Punkt)"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "User"
msgstr "Benutzer"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "Headline"
msgstr "Überschrift"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "Song"
msgstr "Lied"
msgid "Total Friends"
msgstr "Freunde insgesamt"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "Client Version"
msgstr "Client-Version"
@@ -6911,9 +6897,6 @@ msgstr "Nicht unterstützt"
msgid "Password has expired"
msgstr "Passwort ist abgelaufen"
-msgid "Incorrect password"
-msgstr "Falsches Passwort"
-
msgid "Account has been disabled"
msgstr "Konto wurde deaktiviert"
@@ -6940,12 +6923,6 @@ msgstr ""
"Konnte den Rechnernamen des Benutzers, den Sie eingegeben haben, nicht "
"erkennen"
-msgid ""
-"Your account has been disabled because too many incorrect passwords were "
-"entered"
-msgstr ""
-"Ihr Konto wurde wegen zu vielen falsch eingegebenen Passworten gesperrt"
-
msgid "You cannot add the same person twice to a conversation"
msgstr "Sie können die selbe Person nicht zweimal zu einem Gespräch hinzufügen"
@@ -7071,12 +7048,9 @@ msgstr "Hauspostcode"
msgid "User ID"
msgstr "Benutzer-ID"
-#. tag = _("DN");
-#. value = nm_user_record_get_dn(user_record);
-#. if (value) {
-#. purple_notify_user_info_add_pair(user_info, tag, value);
-#. }
-#.
+msgid "DN"
+msgstr "DN"
+
msgid "Full name"
msgstr "Vollständiger Name"
@@ -7155,15 +7129,6 @@ msgstr ""
msgid "No reason given."
msgstr "Kein Grund angegeben."
-msgid "Authorization Denied Message:"
-msgstr "Nachricht für die Ablehnung der Autorisierung:"
-
-#. *
-#. * A wrapper for purple_request_action() that uses @c OK and @c Cancel buttons.
-#.
-msgid "_OK"
-msgstr "_OK"
-
#, c-format
msgid "Received unexpected response from %s: %s"
msgstr "Unerwartete Antwort von %s erhalten: %s"
@@ -7571,8 +7536,11 @@ msgstr "SecurID-Eingabe"
msgid "Enter the 6 digit number from the digital display."
msgstr "Geben Sie die 6-stellige Nummer vom Digital-Display ein."
-msgid "Password sent"
-msgstr "Passwort gesendet"
+#. *
+#. * A wrapper for purple_request_action() that uses @c OK and @c Cancel buttons.
+#.
+msgid "_OK"
+msgstr "_OK"
msgid "Unable to initialize connection"
msgstr "Kann Verbindung nicht erstellen"
@@ -7702,6 +7670,9 @@ msgstr[0] ""
msgstr[1] ""
"Sie haben %hu Nachrichten von %s aus unbekannten Gründen nicht erhalten."
+msgid "UIN"
+msgstr "UIN"
+
msgid "Your AIM connection may be lost."
msgstr "Ihre AIM-Verbindung könnte unterbrochen sein."
@@ -7720,6 +7691,9 @@ msgstr ""
msgid "Pop-Up Message"
msgstr "Pop-Up Nachricht"
+msgid "Unable to display the search results."
+msgstr "Kann Suchergebnisse nicht anzeigen."
+
#, c-format
msgid "The following username is associated with %s"
msgid_plural "The following usernames are associated with %s"
@@ -7873,9 +7847,6 @@ msgstr ""
"senden. Ihre Buddy-Liste ist nicht verloren und wird wahrscheinlich in ein "
"paar Minuten wieder verfügbar sein."
-msgid "Orphans"
-msgstr "Waisen"
-
#, c-format
msgid ""
"Unable to add the buddy %s because you have too many buddies in your buddy "
@@ -8179,6 +8150,7 @@ msgstr "Benutzerinformation nicht verfügbar: %s"
msgid "Mobile Phone"
msgstr "Handynummer"
+#. TODO: Is it correct to pass info->email here...?
msgid "Personal Web Page"
msgstr "Persönliche Webseite"
@@ -8199,6 +8171,7 @@ msgstr "Abteilung"
msgid "Position"
msgstr "Position"
+#. TODO: Is it correct to pass info->email here...?
msgid "Web Page"
msgstr "Webseite"
@@ -8468,13 +8441,13 @@ msgstr "Thema:"
msgid "A server is required to connect this account"
msgstr "Es wird ein Server benötigt um dieses Konto zu verbinden"
+msgid "Last Known Client"
+msgstr "Letzter bekannter Client"
+
#, c-format
msgid "Unknown (0x%04x)<br>"
msgstr "Unbekannt (0x%04x)<br>"
-msgid "Last Known Client"
-msgstr "Letzter bekannter Client"
-
msgid "User Name"
msgstr "Benutzername"
@@ -8797,21 +8770,33 @@ msgstr "Hyperaktiv"
msgid "Robot"
msgstr "Robot"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "User Modes"
msgstr "Benutzermodi"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "Preferred Contact"
msgstr "Bevorzugter Kontakt"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "Preferred Language"
msgstr "Bevorzugte Sprache"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "Device"
msgstr "Gerät"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "Timezone"
msgstr "Zeitzone"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
msgid "Geolocation"
msgstr "Geographische Koordinaten"
@@ -9681,78 +9666,12 @@ msgstr ""
msgid "Whiteboard"
msgstr "Whiteboard"
-msgid "No server statistics available"
-msgstr "Keine Serverstatistik verfügbar"
-
-msgid "Error during connecting to SILC Server"
-msgstr "Fehler beim Verbinden mit dem SILC-Server"
-
-#, c-format
-msgid "Failure: Version mismatch, upgrade your client"
-msgstr "Fehler: Unterschiedliche Version, aktualisieren Sie Ihren Client"
-
-#, c-format
-msgid "Failure: Remote does not trust/support your public key"
-msgstr ""
-"Fehler: Die entfernte Seite vertraut Ihrem öffentlichen Schlüssel nicht"
-
-#, c-format
-msgid "Failure: Remote does not support proposed KE group"
-msgstr ""
-"Fehler: Entferntes Programm unterstützt nicht die vorgeschlagen KE-Gruppe"
-
-#, c-format
-msgid "Failure: Remote does not support proposed cipher"
-msgstr ""
-"Fehler: Entferntes Programm unterstützt die vorgeschlagene Chiffre nicht"
-
-#, c-format
-msgid "Failure: Remote does not support proposed PKCS"
-msgstr "Fehler: Entferntes Programm unterstützt die vorgeschlagene PKCS nicht"
-
-#, c-format
-msgid "Failure: Remote does not support proposed hash function"
-msgstr ""
-"Fehler: Entferntes Programm unterstützt die vorgeschlagen Hashfunktion nicht"
-
-#, c-format
-msgid "Failure: Remote does not support proposed HMAC"
-msgstr "Fehler: Entferntes Programm unterstützt das vorgeschlagene HMAC nicht"
-
-#, c-format
-msgid "Failure: Incorrect signature"
-msgstr "Fehler: Falsche Signatur"
-
-#, c-format
-msgid "Failure: Invalid cookie"
-msgstr "Fehler: Ungültiger Cookie"
-
-#, c-format
-msgid "Failure: Authentication failed"
-msgstr "Fehler: Authentifizierung fehlgeschlagen"
-
-msgid "Unable to initialize SILC Client connection"
-msgstr "SILC-Client-Verbindung konnte nicht hergestellt werden"
-
-msgid "John Noname"
-msgstr "Max Mustermann"
-
-#, c-format
-msgid "Unable to load SILC key pair: %s"
-msgstr "SILC-Schlüsselpaar konnte nicht geladen werden: %s"
-
-msgid "Unable to create connection"
-msgstr "Kann Verbindung nicht erstellen"
-
msgid "Unknown server response"
msgstr "Unbekannte Serverantwort"
msgid "Unable to create listen socket"
msgstr "Lauschender Socket konnte nicht erstellt werden"
-msgid "Unable to resolve hostname"
-msgstr "Hostname konnte nicht aufgelöst werden"
-
msgid "SIP usernames may not contain whitespaces or @ symbols"
msgstr "SIP-Benutzernamen dürfen keine Leerzeichen oder @-Symbole enthalten"
@@ -9869,9 +9788,6 @@ msgstr "Ihre Yahoo!-Nachricht wurde nicht verschickt."
msgid "Yahoo! system message for %s:"
msgstr "Yahoo!-Systemnachricht für %s:"
-msgid "Authorization denied message:"
-msgstr "Nachricht für die Ablehnung der Autorisierung:"
-
#, c-format
msgid ""
"%s has (retroactively) denied your request to add them to your list for the "
@@ -10235,6 +10151,8 @@ msgstr "Kann nicht an den Chat %s,%s,%s senden"
msgid "Hidden or not logged-in"
msgstr "Versteckt oder nicht angemeldet"
+#. TODO: Need to escape locs.host and locs.time?
+#. TODO: Need to escape the two strings that make up tmp?
#, c-format
msgid "<br>At %s since %s"
msgstr "<br>Bei %s seit %s"
@@ -10374,6 +10292,14 @@ msgstr ""
msgid "Error resolving %s"
msgstr "Fehler beim Auflösen von %s"
+msgid "Invalid proxy settings"
+msgstr "Falsche Proxy-Einstellungen"
+
+msgid ""
+"Either the host name or port number specified for your given proxy type is "
+"invalid."
+msgstr "Hostname oder Portnummer Ihres Proxys sind falsch angegeben."
+
#, c-format
msgid "Requesting %s's attention..."
msgstr "Bitte um %ss Aufmerksamkeit..."
@@ -10443,9 +10369,6 @@ msgstr "Gespeichertes Bild"
msgid "Stored Image. (that'll have to do for now)"
msgstr "Gespeichertes Bild. (Das muss erstmal reichen)"
-msgid "SSL Connection Failed"
-msgstr "SSL-Verbindung gescheitert"
-
msgid "SSL Handshake Failed"
msgstr "SSL-Verhandlung gescheitert"
@@ -10548,38 +10471,6 @@ msgstr[0] "%d Minute"
msgstr[1] "%d Minuten"
#, c-format
-msgid "Could not open %s: Redirected too many times"
-msgstr "Konnte %s nicht öffnen: Zu oft weitergeleitet"
-
-#, c-format
-msgid "Unable to connect to %s"
-msgstr "Verbindung zu %s nicht möglich"
-
-#, c-format
-msgid "Error reading from %s: response too long (%d bytes limit)"
-msgstr "Fehler beim Lesen von %s: Antwort zu lang (%d Bytes maximal)"
-
-#, c-format
-msgid ""
-"Unable to allocate enough memory to hold the contents from %s. The web "
-"server may be trying something malicious."
-msgstr ""
-"Nicht genug Speicher für die Inhalte von %s verfügbar. Eventuell versucht "
-"der Web-Server etwas böses zu tun."
-
-#, c-format
-msgid "Error reading from %s: %s"
-msgstr "Fehler beim Lesen von %s: %s"
-
-#, c-format
-msgid "Error writing to %s: %s"
-msgstr "Fehler beim Schreiben von %s: %s"
-
-#, c-format
-msgid "Unable to connect to %s: %s"
-msgstr "Verbindung zu %s nicht möglich: %s"
-
-#, c-format
msgid " - %s"
msgstr " - %s"
@@ -10765,6 +10656,12 @@ msgstr ""
"Listenfenster zu diesem Dialog zurückkehren und Konten hinzufügen, "
"bearbeiten oder löschen"
+msgid "Authorization acceptance message:"
+msgstr "Nachricht für die Annahme der Autorisierung:"
+
+msgid "Authorization denied message:"
+msgstr "Nachricht für die Ablehnung der Autorisierung:"
+
#, c-format
msgid ""
"<a href=\"viewinfo\">%s</a>%s%s%s wants to add you (%s) to his or her buddy "
@@ -10773,11 +10670,6 @@ msgstr ""
"<a href=\"viewinfo\">%s</a>%s%s%s möchte Sie (%s) zu seiner oder ihrer Buddy-"
"Liste hinzufügen%s%s"
-#, c-format
-msgid "%s%s%s%s wants to add you (%s) to his or her buddy list%s%s"
-msgstr ""
-"%s%s%s%s möchte Sie (%s) zu seiner oder ihrer Buddy-Liste hinzufügen%s%s"
-
msgid "Send Instant Message"
msgstr "Sofortnachricht senden"
@@ -11016,9 +10908,6 @@ msgstr "_Zusammenklappen"
msgid "_Expand"
msgstr "A_usklappen"
-msgid "/Tools/Mute Sounds"
-msgstr "/Werkzeuge/Stummschalten"
-
msgid ""
"You are not currently signed on with an account that can add that buddy."
msgstr ""
@@ -11043,122 +10932,121 @@ msgstr "Benutzerstimmung ändern"
#. NOTE: Do not set any accelerator to Control+O. It is mapped by
#. gtk_blist_key_press_cb to "Get User Info" on the selected buddy.
#. Buddies menu
-msgid "/_Buddies"
-msgstr "/_Buddys"
-
-msgid "/Buddies/New Instant _Message..."
-msgstr "/Buddys/_Neue Sofortnachricht..."
+msgid "_Buddies"
+msgstr "_Buddys"
-msgid "/Buddies/Join a _Chat..."
-msgstr "/Buddys/Einen _Chat betreten..."
+msgid "New Instant _Message..."
+msgstr "_Neue Sofortnachricht..."
-msgid "/Buddies/Get User _Info..."
-msgstr "/Buddys/Benu_tzer-Info abrufen..."
+msgid "Join a _Chat..."
+msgstr "_Chat betreten..."
-msgid "/Buddies/View User _Log..."
-msgstr "/Buddys/Benutzer-_Mitschnitt ansehen..."
+msgid "Get User _Info..."
+msgstr "Benutzer-_Info abrufen..."
-msgid "/Buddies/Sh_ow"
-msgstr "/Buddys/_Anzeigen"
+msgid "View User _Log..."
+msgstr "Benutzer-_Mitschnitt anzeigen..."
-msgid "/Buddies/Show/_Offline Buddies"
-msgstr "/Buddys/Anzeigen/_Offline-Buddys"
+msgid "Sh_ow"
+msgstr "_Anzeigen"
-msgid "/Buddies/Show/_Empty Groups"
-msgstr "/Buddys/Anzeigen/_Leere Gruppen"
+msgid "_Sort Buddies"
+msgstr "Buddys _sortieren"
-msgid "/Buddies/Show/Buddy _Details"
-msgstr "/Buddys/Anzeigen/Buddy-_Details"
+msgid "_Add Buddy..."
+msgstr "_Buddy hinzufügen..."
-msgid "/Buddies/Show/Idle _Times"
-msgstr "/Buddys/Anzeigen/Untätigkeitszei_ten"
+msgid "Add _Group..."
+msgstr "_Gruppe hinzufügen..."
-msgid "/Buddies/Show/_Protocol Icons"
-msgstr "/Buddys/Anzeigen/_Protokoll-Icons"
+msgid "_Quit"
+msgstr "_Beenden"
-msgid "/Buddies/_Sort Buddies"
-msgstr "/Buddys/Buddys _sortieren"
+#. Accounts menu
+msgid "_Accounts"
+msgstr "_Konten"
-msgid "/Buddies/_Add Buddy..."
-msgstr "/Buddys/B_uddy hinzufügen..."
+msgid "Manage Accounts"
+msgstr "Konten verwalten"
-msgid "/Buddies/Add C_hat..."
-msgstr "/Buddys/C_hat hinzufügen..."
+#. Tools
+msgid "_Tools"
+msgstr "_Werkzeuge"
-msgid "/Buddies/Add _Group..."
-msgstr "/Buddys/_Gruppe hinzufügen..."
+msgid "Buddy _Pounces"
+msgstr "Buddy-_Alarm"
-msgid "/Buddies/_Quit"
-msgstr "/Buddys/_Beenden"
+msgid "_Certificates"
+msgstr "_Zertifikate"
-#. Accounts menu
-msgid "/_Accounts"
-msgstr "/_Konten"
+msgid "Custom Smile_ys"
+msgstr "Benutzerdefinierte Smile_ys"
-msgid "/Accounts/Manage Accounts"
-msgstr "/Konten/Konten verwalten"
+msgid "Plu_gins"
+msgstr "_Plugins"
-#. Tools
-msgid "/_Tools"
-msgstr "/_Werkzeuge"
+msgid "Pr_eferences"
+msgstr "_Einstellungen"
-msgid "/Tools/Buddy _Pounces"
-msgstr "/Werkzeuge/Buddy-_Alarm"
+msgid "Pr_ivacy"
+msgstr "Pri_vatsphäre"
-msgid "/Tools/_Certificates"
-msgstr "/Werkzeuge/_Zertifikate"
+msgid "Set _Mood"
+msgstr "Setze Sti_mmung"
-msgid "/Tools/Custom Smile_ys"
-msgstr "/Werkzeuge/Benutzerdefinierte Smile_ys"
+msgid "_File Transfers"
+msgstr "_Dateiübertragungen"
-msgid "/Tools/Plu_gins"
-msgstr "/Werkzeuge/Plu_gins"
+msgid "R_oom List"
+msgstr "R_aumliste"
-msgid "/Tools/Pr_eferences"
-msgstr "/Werkzeuge/_Einstellungen"
+msgid "System _Log"
+msgstr "System_mitschnitt"
-msgid "/Tools/Pr_ivacy"
-msgstr "/Werkzeuge/Pri_vatsphäre"
+#. Help
+msgid "_Help"
+msgstr "_Hilfe"
-msgid "/Tools/Set _Mood"
-msgstr "/Werkzeuge/Setze Sti_mmung"
+msgid "Online _Help"
+msgstr "Online-_Hilfe"
-msgid "/Tools/_File Transfers"
-msgstr "/Werkzeuge/_Dateiübertragungen"
+msgid "_Build Information"
+msgstr "_Build-Informationen"
-msgid "/Tools/R_oom List"
-msgstr "/Werkzeuge/Chat_räume"
+msgid "_Debug Window"
+msgstr "_Debug-Fenster"
-msgid "/Tools/System _Log"
-msgstr "/Werkzeuge/_Systemmitschnitt"
+msgid "De_veloper Information"
+msgstr "_Entwickler-Informationen"
-msgid "/Tools/Mute _Sounds"
-msgstr "/Werkzeuge/S_tummschalten"
+msgid "_Plugin Information"
+msgstr "_Plugin-Informationen"
-#. Help
-msgid "/_Help"
-msgstr "/_Hilfe"
+msgid "_Translator Information"
+msgstr "Ü_bersetzer-Informationen"
-msgid "/Help/Online _Help"
-msgstr "/Hilfe/Online-_Hilfe"
+msgid "_About"
+msgstr "Übe_r"
-msgid "/Help/_Build Information"
-msgstr "/Hilfe/_Build-Informationen"
+#. Buddies->Show menu
+msgid "_Offline Buddies"
+msgstr "_Offline-Buddys"
-msgid "/Help/_Debug Window"
-msgstr "/Hilfe/_Debug-Fenster"
+msgid "_Empty Groups"
+msgstr "_Leere Gruppen"
-msgid "/Help/De_veloper Information"
-msgstr "/Hilfe/_Entwickler-Informationen"
+msgid "Buddy _Details"
+msgstr "Buddy-_Details"
-msgid "/Help/_Plugin Information"
-msgstr "/Hilfe/_Plugin-Informationen"
+msgid "Idle _Times"
+msgstr "Un_tätigkeitszeiten"
-msgid "/Help/_Translator Information"
-msgstr "/Hilfe/Über_setzer-Informationen"
+msgid "_Protocol Icons"
+msgstr "_Protokoll-Icons"
-msgid "/Help/_About"
-msgstr "/Hilfe/Übe_r"
+#. Tools menu
+msgid "Mute _Sounds"
+msgstr "Stu_mmschalten"
#, c-format
msgid "<b>Account:</b> %s"
@@ -11216,30 +11104,6 @@ msgstr "Untätig %dh %02dm"
msgid "Idle %dm"
msgstr "Untätig seit %dm"
-msgid "/Buddies/New Instant Message..."
-msgstr "/Buddys/Neue Sofortnachricht..."
-
-msgid "/Buddies/Join a Chat..."
-msgstr "/Buddys/Chat betreten..."
-
-msgid "/Buddies/Get User Info..."
-msgstr "/Buddys/Benutzer-Info abrufen..."
-
-msgid "/Buddies/Add Buddy..."
-msgstr "/Buddys/Buddy hinzufügen..."
-
-msgid "/Buddies/Add Chat..."
-msgstr "/Buddys/Chat hinzufügen..."
-
-msgid "/Buddies/Add Group..."
-msgstr "/Buddys/Gruppe hinzufügen..."
-
-msgid "/Tools/Privacy"
-msgstr "/Werkzeuge/Privatsphäre"
-
-msgid "/Tools/Room List"
-msgstr "/Werkzeuge/Chaträume"
-
#, c-format
msgid "%d unread message from %s\n"
msgid_plural "%d unread messages from %s\n"
@@ -11295,9 +11159,6 @@ msgstr "<b>Passwort:</b>"
msgid "_Login"
msgstr "_Anmelden"
-msgid "/Accounts"
-msgstr "/Konten"
-
#. Translators: Please maintain the use of -> and <- to refer to menu heirarchy
#, c-format
msgid ""
@@ -11314,24 +11175,6 @@ msgstr ""
"aktiviert haben, können Sie sich anmelden, Ihren Status setzen und mit Ihren "
"Freunden reden."
-#. set the Show Offline Buddies option. must be done
-#. * after the treeview or faceprint gets mad. -Robot101
-#.
-msgid "/Buddies/Show/Offline Buddies"
-msgstr "/Buddys/Anzeigen/Offline-Buddys"
-
-msgid "/Buddies/Show/Empty Groups"
-msgstr "/Buddys/Anzeigen/Leere Gruppen"
-
-msgid "/Buddies/Show/Buddy Details"
-msgstr "/Buddys/Anzeigen/Buddy-Details"
-
-msgid "/Buddies/Show/Idle Times"
-msgstr "/Buddys/Anzeigen/Untätigkeitszeiten"
-
-msgid "/Buddies/Show/Protocol Icons"
-msgstr "/Buddys/Anzeigen/Protokoll-Icons"
-
msgid "Add a buddy.\n"
msgstr "Einen Buddy hinzufügen.\n"
@@ -11380,12 +11223,6 @@ msgstr "Bitte geben Sie den Namen der Gruppe ein, die hinzugefügt werden soll."
msgid "Enable Account"
msgstr "Konten aktivieren"
-msgid "<PurpleMain>/Accounts/Enable Account"
-msgstr "<PurpleMain>/Konten/Konto aktivieren"
-
-msgid "<PurpleMain>/Accounts/"
-msgstr "<PurpleMain>/Konten/"
-
msgid "_Edit Account"
msgstr "Konto _bearbeiten"
@@ -11398,19 +11235,41 @@ msgstr "Keine Aktionen verfügbar"
msgid "_Disable"
msgstr "_Deaktivieren"
-msgid "/Tools"
-msgstr "/Werkzeuge"
-
-msgid "/Buddies/Sort Buddies"
-msgstr "/Buddys/Buddys sortieren"
-
msgid "Type the host name for this certificate."
msgstr "Geben Sie einen Hostnamen für dieses Zertifikat an."
+#. Fire the notification
+#, c-format
+msgid "Certificate Information for %s"
+msgstr "Zertifikat-Informationen für %s"
+
#. Widget creation function
msgid "SSL Servers"
msgstr "SSL-Server"
+msgid "Unsafe debugging is now disabled."
+msgstr "Unsicheres Debugging ist nun deaktiviert"
+
+msgid "Unsafe debugging is now enabled."
+msgstr "Unsicheres Debugging ist nun aktiviert"
+
+msgid "Verbose debugging is now disabled."
+msgstr "Ausführliches Debugging ist nun deaktiviert"
+
+msgid "Verbose debugging is now enabled."
+msgstr "Ausführliches Debugging ist nun aktiviert"
+
+msgid "Supported debug options are: plugins version unsafe verbose"
+msgstr "Unterstützte Debug-Optionen sind: plugins version unsafe verbose"
+
+msgid ""
+"Use \"/help &lt;command&gt;\" for help on a specific command.<br/>The "
+"following commands are available in this context:<br/>"
+msgstr ""
+"Benutzen Sie „/help &lt;kommando&gt;“, um Hilfe für ein bestimmtes Kommando "
+"zu erhalten.<br/>Die folgenden Kommandos sind in diesem Kontext verfügbar:"
+"<br/>"
+
msgid "Unknown command."
msgstr "Unbekanntes Kommando."
@@ -11445,9 +11304,6 @@ msgstr "Nicht Ignorieren"
msgid "Ignore"
msgstr "Ignorieren"
-msgid "Get Away Message"
-msgstr "Neue Abwesenheitsnachricht abholen"
-
msgid "Last Said"
msgstr "Zuletzt gesagt"
@@ -11476,170 +11332,75 @@ msgid "Show All"
msgstr "Alle anzeigen"
#. Conversation menu
-msgid "/_Conversation"
-msgstr "/_Unterhaltung"
-
-msgid "/Conversation/New Instant _Message..."
-msgstr "/Unterhaltung/_Neue Sofortnachricht..."
-
-msgid "/Conversation/Join a _Chat..."
-msgstr "/Unterhaltung/Einen Cha_t betreten..."
-
-msgid "/Conversation/_Find..."
-msgstr "/Unterhaltung/_Finden..."
+msgid "_Conversation"
+msgstr "_Unterhaltung"
-msgid "/Conversation/View _Log"
-msgstr "/Unterhaltung/_Mitschnitt anzeigen"
+msgid "_Find..."
+msgstr "_Suchen..."
-msgid "/Conversation/_Save As..."
-msgstr "/Unterhaltung/S_peichern als..."
+msgid "_Save As..."
+msgstr "S_peichern unter..."
-msgid "/Conversation/Clea_r Scrollback"
-msgstr "/Unterhaltung/_Leeren"
+msgid "Clea_r Scrollback"
+msgstr "Gesprächsfenster lee_ren"
-msgid "/Conversation/M_edia"
-msgstr "/Unterhaltung/M_edien"
-
-msgid "/Conversation/Media/_Audio Call"
-msgstr "/Unterhaltung/Medien/_Audio-Anruf"
-
-msgid "/Conversation/Media/_Video Call"
-msgstr "/Unterhaltung/Medien/_Video-Anruf"
-
-msgid "/Conversation/Media/Audio\\/Video _Call"
-msgstr "/Unterhaltung/Medien/A_udio-\\/Video-Anruf"
+msgid "M_edia"
+msgstr "_Medien"
-msgid "/Conversation/Se_nd File..."
-msgstr "/Unterhaltung/Datei _senden..."
+msgid "Audio/Video _Call"
+msgstr "Audio/Video-_Anruf"
-msgid "/Conversation/Get _Attention"
-msgstr "/Unterhaltung/_Aufmerksamkeit erregen"
+msgid "Se_nd File..."
+msgstr "_Datei versenden..."
-msgid "/Conversation/Add Buddy _Pounce..."
-msgstr "/Unterhaltung/_Buddy-Alarm hinzufügen..."
+msgid "Get _Attention"
+msgstr "_Aufmerksamkeit erregen"
-msgid "/Conversation/_Get Info"
-msgstr "/Unterhaltung/_Info abrufen"
+msgid "_Get Info"
+msgstr "_Info abrufen"
-msgid "/Conversation/In_vite..."
-msgstr "/Unterhaltung/_Einladen..."
+msgid "In_vite..."
+msgstr "Ei_nladen..."
-msgid "/Conversation/M_ore"
-msgstr "/Unterhaltung/Me_hr"
+msgid "M_ore"
+msgstr "M_ehr"
-msgid "/Conversation/Al_ias..."
-msgstr "/Unterhaltung/Al_ias..."
+msgid "Al_ias..."
+msgstr "_Alias..."
-msgid "/Conversation/_Block..."
-msgstr "/Unterhaltung/_Blockieren..."
+msgid "_Block..."
+msgstr "_Sperren..."
-msgid "/Conversation/_Unblock..."
-msgstr "/Unterhaltung/_Entsperren..."
+msgid "_Unblock..."
+msgstr "Sperrung a_ufheben..."
-msgid "/Conversation/_Add..."
-msgstr "/Unterhaltung/_Hinzufügen..."
+msgid "_Add..."
+msgstr "_Hinzufügen..."
-msgid "/Conversation/_Remove..."
-msgstr "/Unterhaltung/_Entfernen..."
+msgid "_Remove..."
+msgstr "E_ntfernen"
-msgid "/Conversation/Insert Lin_k..."
-msgstr "/Unterhaltung/Lin_k einfügen..."
+msgid "Insert Lin_k..."
+msgstr "Lin_k einfügen..."
-msgid "/Conversation/Insert Imag_e..."
-msgstr "/Unterhaltung/Bil_d einfügen..."
+msgid "Insert Imag_e..."
+msgstr "Bild einfü_gen..."
-msgid "/Conversation/_Close"
-msgstr "/Unterhaltung/S_chließen"
+msgid "_Close"
+msgstr "_Schließen"
#. Options
-msgid "/_Options"
-msgstr "/_Optionen"
-
-msgid "/Options/Enable _Logging"
-msgstr "/Optionen/Schalte _Mitschnitt ein"
-
-msgid "/Options/Enable _Sounds"
-msgstr "/Optionen/Schalte _Klänge ein"
+msgid "_Options"
+msgstr "_Optionen"
-msgid "/Options/Show Formatting _Toolbars"
-msgstr "/Optionen/Zeige _Werkzeugleisten für Formatierung"
+msgid "Enable _Logging"
+msgstr "_Mitschnitt aktivieren"
-msgid "/Options/Show Ti_mestamps"
-msgstr "/Optionen/Zeige _Zeitstempel"
+msgid "Enable _Sounds"
+msgstr "_Klänge aktivieren"
-msgid "/Conversation/More"
-msgstr "/Unterhaltung/Mehr"
-
-msgid "/Options"
-msgstr "/Optionen"
-
-#. The menubar has been deactivated. Make sure the 'More' submenu is regenerated next time
-#. * the 'Conversation' menu pops up.
-#. Make sure the 'Conversation -> More' menuitems are regenerated whenever
-#. * the 'Conversation' menu pops up because the entries can change after the
-#. * conversation is created.
-msgid "/Conversation"
-msgstr "/Unterhaltung"
-
-msgid "/Conversation/View Log"
-msgstr "/Unterhaltung/Mitschnitt anzeigen"
-
-msgid "/Conversation/Media/Audio Call"
-msgstr "/Unterhaltung/Medien/Audio-Anruf"
-
-msgid "/Conversation/Media/Video Call"
-msgstr "/Unterhaltung/Medien/Video-Anruf"
-
-msgid "/Conversation/Media/Audio\\/Video Call"
-msgstr "/Unterhaltung/Medien/Audio-\\/Video-Anruf"
-
-msgid "/Conversation/Send File..."
-msgstr "/Unterhaltung/Datei senden ..."
-
-msgid "/Conversation/Get Attention"
-msgstr "/Unterhaltung/Aufmerksamkeit erregen"
-
-msgid "/Conversation/Add Buddy Pounce..."
-msgstr "/Unterhaltung/Buddy-Alarm hinzufügen..."
-
-msgid "/Conversation/Get Info"
-msgstr "/Unterhaltung/Info abrufen"
-
-msgid "/Conversation/Invite..."
-msgstr "/Unterhaltung/Einladen ..."
-
-msgid "/Conversation/Alias..."
-msgstr "/Unterhaltung/Alias..."
-
-msgid "/Conversation/Block..."
-msgstr "/Unterhaltung/Blockieren..."
-
-msgid "/Conversation/Unblock..."
-msgstr "/Unterhaltung/Entsperren..."
-
-msgid "/Conversation/Add..."
-msgstr "/Unterhaltung/Hinzufügen..."
-
-msgid "/Conversation/Remove..."
-msgstr "/Unterhaltung/Entfernen..."
-
-msgid "/Conversation/Insert Link..."
-msgstr "/Unterhaltung/Link einfügen..."
-
-msgid "/Conversation/Insert Image..."
-msgstr "/Unterhaltung/Bild einfügen..."
-
-msgid "/Options/Enable Logging"
-msgstr "/Optionen/Schalte Mitschnitt ein"
-
-msgid "/Options/Enable Sounds"
-msgstr "/Optionen/Schalte Klänge ein"
-
-msgid "/Options/Show Formatting Toolbars"
-msgstr "/Optionen/Zeige Werkzeugleisten für Formatierung"
-
-msgid "/Options/Show Timestamps"
-msgstr "/Optionen/Zeige Zeitstempel"
+msgid "Show Formatting _Toolbars"
+msgstr "Zeige _Werkzeugleisten für Formatierung"
msgid "User is typing..."
msgstr "Benutzer tippt gerade..."
@@ -11734,12 +11495,6 @@ msgstr "Nach Gruppe"
msgid "By account"
msgstr "Nach Konto"
-msgid "Find"
-msgstr "Suchen"
-
-msgid "_Search for:"
-msgstr "_Suche nach:"
-
msgid "Save Debug Log"
msgstr "Debug-Mitschnitt speichern"
@@ -11800,8 +11555,7 @@ msgstr "Webmaster"
msgid "win32 port"
msgstr "Win32 Portierung"
-#. Translators: This is a person's name. For most languages we recommend
-#. not translating it.
+#. feel free to not translate this
msgid "Ka-Hing Cheung"
msgstr "Ka-Hing Cheung"
@@ -11839,9 +11593,6 @@ msgstr "Arabisch"
msgid "Assamese"
msgstr "Assamesisch"
-msgid "Asturian"
-msgstr "Asturisch"
-
msgid "Belarusian Latin"
msgstr "Weißrussisch (Latin)"
@@ -12105,56 +11856,50 @@ msgstr "T.M.Thanh und das Gnome-Vi Team"
#, c-format
msgid ""
-"%s is a messaging client based on libpurple which is capable of connecting "
-"to multiple messaging services at once. %s is written in C using GTK+. %s "
-"is released, and may be modified and redistributed, under the terms of the "
-"GPL version 2 (or later). A copy of the GPL is distributed with %s. %s is "
-"copyrighted by its contributors, a list of whom is also distributed with "
-"%s. There is no warranty for %s.<BR><BR>"
+"<p>%s is a messaging client based on libpurple which is capable of "
+"connecting to multiple messaging services at once. %s is written in C using "
+"GTK+. %s is released, and may be modified and redistributed, under the "
+"terms of the GPL version 2 (or later). A copy of the GPL is distributed "
+"with %s. %s is copyrighted by its contributors, a list of whom is also "
+"distributed with %s. There is no warranty for %s.</p>"
msgstr ""
-"%s ist ein Nachrichtendienst, basierend auf libpurple, der die Verbindung zu "
-"mehreren Nachrichtendiensten gleichzeitig unterstützt. %s wird in C "
+"<p>%s ist ein Nachrichtendienst, basierend auf libpurple, der die Verbindung "
+"zu mehreren Nachrichtendiensten gleichzeitig unterstützt. %s wird in C "
"programmiert und nutzt GTK+. %s ist nach den Bedingungen der GPL (Version 2 "
"oder später) freigegeben und darf gemäß dieser bearbeitet und weiter "
"verbreitet werden. Eine Kopie der GPL wird mit %s ausgeliefert. %s wird "
"von seinen Mitwirkenden urheberrechtlich geschützt. Eine komplette Liste "
"der Mitwirkenden wird mit %s ausgeliefert. Wir übernehmen keine Haftung für "
-"%s.<BR><BR>"
+"%s.</p>"
#, c-format
msgid ""
-"<FONT SIZE=\"4\"><B>Helpful Resources</B></FONT><BR>\t<A HREF=\"%s"
-"\">Website</A><BR>\t<A HREF=\"%s\">Frequently Asked Questions</A><BR>\tIRC "
-"Channel: #pidgin on irc.freenode.net<BR>\tXMPP MUC: devel@conference.pidgin."
-"im<BR><BR>"
+"<h3>Helpful Resources</h3><ul><li><a href=\"%s\" title=\"%s\">Website</a></"
+"li><li><a href=\"%s\" title=\"%s\">Frequently Asked Questions</a></"
+"li><li>IRC Channel: #pidgin on irc.freenode.net</li><li>XMPP MUC: "
+"devel@conference.pidgin.im</li></ul>"
msgstr ""
-"<FONT SIZE=\"4\"><B>Hilfreiche Quellen</B></FONT><BR>\t<A HREF=\"%s"
-"\">Webseite</A><BR>\t<A HREF=\"%s\">Häufig gestellte Fragen (FAQ)</A><BR>"
-"\tIRC-Channel: #pidgin auf irc.freenode.net<BR>\tXMPP-MUC: devel@conference."
-"pidgin.im<BR><BR>"
+"<h3>Hilfreiche Quellen</h3><ul><li><a href=\"%s\" title=\"%s\">Webseite</a></"
+"li><li><a href=\"%s\" title=\"%s\">Häufig gestellte Fragen (FAQ)</a></"
+"li><li>IRC-Channel: #pidgin auf irc.freenode.net</li><li>XMPP-MUC: "
+"devel@conference.pidgin.im</li></ul>"
+#, c-format
msgid ""
-"<font size=\"4\"><b>Help from other Pidgin users</b></font> is available by "
-"e-mailing <a href=\"mailto:support@pidgin.im\">support@pidgin.im</a><br/"
-">This is a <b>public</b> mailing list! (<a href=\"http://pidgin.im/pipermail/"
-"support/\">archive</a>)<br/>We can't help with third-party protocols or "
-"plugins!<br/>This list's primary language is <b>English</b>. You are "
-"welcome to post in another language, but the responses may be less helpful."
-"<br/>"
+"<p><strong>Help from other Pidgin users</strong> is available by e-mailing "
+"<a href=\"mailto:%s\">%s</a>.<br/>This is a <strong>public</strong> mailing "
+"list! (<a href=\"%s\" title=\"%s\">archive</a>)<br/>We can't help with third-"
+"party protocols or plugins!<br/>This list's primary language is English. "
+"You are welcome to post in another language, but the responses may be less "
+"helpful.</p>"
msgstr ""
-"<font size=\"4\"><b>Hilfe von anderen Pidgin-Benutzern</b></font> erhält man "
-"per E-Mail an <a href=\"mailto:support@pidgin.im\">support@pidgin.im</a><br/"
-">Dies ist eine <b>öffentliche</b> Mailing-Liste! (<a href=\"http://pidgin.im/"
-"pipermail/support/\">Archiv</a>)<br/>Wir können nicht bei Problemen mit "
-"Drittanbieter-Protokollen oder Plugins helfen!<br/>Die Hauptsprache dieser "
-"Liste ist <b>Englisch</b>. Sie können gern in einer anderen Sprache "
-"schreiben, aber die Antworten könnten weniger hilfreich sein.<br/"
-">Deutschsprachige Benutzer können auch das Portal <a href=\"http://www."
-"pidgin-im.de/\">Pidgin-IM.de</a> nutzen. Dort finden Sie aktuelle "
-"Informationen zu Pidgin, können mit anderen Benutzern im <a href=\"http://"
-"forum.pidgin-im.de/\">Forum</a> diskutieren und Hilfe zu Problemen finden. "
-"Beachten Sie, dass dieses Portal unabhängig vom offiziellen Pidgin-Projekt "
-"ist.<br/><br/>"
+"<p><strong>Hilfe von anderen Pidgin-Benutzern</strong> erhält man per E-Mail "
+"an <a href=\"mailto:%s\">%s</a>.<br/>Dies ist eine <strong>öffentliche</"
+"strong> Mailing-Liste! (<a href=\"%s\" title=\"%s\">Archiv</a>)<br/>Wir "
+"können nicht bei Problemen mit Drittanbieter-Protokollen oder Plugins helfen!"
+"<br/>Die Hauptsprache dieser Liste ist <b>Englisch</b>. Sie können gern in "
+"einer anderen Sprache schreiben, aber die Antworten könnten weniger "
+"hilfreich sein.</p>"
#, c-format
msgid "About %s"
@@ -12220,12 +11965,6 @@ msgstr ""
msgid "View User Log"
msgstr "Benutzer-Mitschnitt anzeigen"
-msgid "Alias Contact"
-msgstr "Kontakt-Alias"
-
-msgid "Enter an alias for this contact."
-msgstr "Geben Sie einen Alias für diesen Kontakt ein."
-
#, c-format
msgid "Enter an alias for %s."
msgstr "Geben Sie einen Alias %s ein."
@@ -12329,24 +12068,9 @@ msgstr "_Ungelesene Nachrichten"
msgid "New _Message..."
msgstr "_Neue Nachricht..."
-msgid "_Accounts"
-msgstr "_Konten"
-
-msgid "Plu_gins"
-msgstr "_Plugins"
-
-msgid "Pr_eferences"
-msgstr "_Einstellungen"
-
-msgid "Mute _Sounds"
-msgstr "Stu_mmschalten"
-
msgid "_Blink on New Message"
msgstr "Be_i neuen Nachrichten blinken"
-msgid "_Quit"
-msgstr "_Beenden"
-
msgid "Not started"
msgstr "Nicht gestartet"
@@ -12830,6 +12554,9 @@ msgstr "_Pause"
msgid "_Mute"
msgstr "Stu_mmschalten"
+msgid "Call in progress"
+msgstr "Verbindungsaufbau"
+
#, c-format
msgid "%s has %d new message."
msgid_plural "%s has %d new messages."
@@ -12858,10 +12585,6 @@ msgid ""
msgstr ""
"Das benutzerdefinierte Browserkommando wurde ausgewählt, aber nicht gesetzt."
-msgid "Unable to open URL: the 'Manual' browser command seems invalid."
-msgstr ""
-"Kann die URL nicht öffnen: der 'manuelle' Browser scheint ungültig zu sein."
-
msgid "No message"
msgstr "Keine Nachricht"
@@ -13056,6 +12779,9 @@ msgstr "Das Standard-Klangthema für Pidgin"
msgid "The default Pidgin buddy list theme"
msgstr "Das Standard-Buddy-Listen-Thema für Pidgin"
+msgid "The default Pidgin conversation theme"
+msgstr "Das Standard-Gesprächsthema für Pidgin"
+
msgid "The default Pidgin status icon theme"
msgstr "Das Standard-Status-Icon-Thema für Pidgin"
@@ -13083,6 +12809,12 @@ msgstr ""
msgid "Buddy List Theme:"
msgstr "Buddy-Listen-Thema:"
+msgid "Conversation Theme:"
+msgstr "Gesprächsthema:"
+
+msgid "\tVariant:"
+msgstr "\tVariante:"
+
msgid "Status Icon Theme:"
msgstr "Status-Icon-Thema:"
@@ -13154,6 +12886,18 @@ msgstr "Rechts vertikal"
msgid "N_ew conversations:"
msgstr "N_eue Unterhaltungen:"
+msgid "Chat notification:"
+msgstr "Chat-Benachrichtigung:"
+
+msgid "On unseen events"
+msgstr "Bei ungelesenen Ereignissen"
+
+msgid "On unseen text"
+msgstr "Bei ungelesenen Text"
+
+msgid "On unseen text and the nick was said"
+msgstr "Bei ungelesenen Text und wenn der Spitzname genannt wird"
+
msgid "Show _formatting on incoming messages"
msgstr "Zeige _Formatierung bei ankommenden Nachrichten"
@@ -13481,6 +13225,40 @@ msgstr "Status beim Neu_start wiederherstellen"
msgid "Status to a_pply at startup:"
msgstr "Beim Starten folgenden Status _benutzen:"
+msgid "_Device"
+msgstr "_Gerät"
+
+msgid "_Plugin"
+msgstr "_Plugin"
+
+msgid "DROP"
+msgstr "Stille, da unterhalb des Schwellwerts"
+
+#, c-format
+msgid "Silence threshold: %d%%"
+msgstr "Schwellwert für Stille: %d%%"
+
+msgid "Volume:"
+msgstr "Lautstärke:"
+
+msgid "Test Audio"
+msgstr "Teste Audio"
+
+msgid "Test Video"
+msgstr "Teste Video"
+
+msgid "Audio"
+msgstr "Audio"
+
+msgid "Input"
+msgstr "Eingang"
+
+msgid "Output"
+msgstr "Ausgang"
+
+msgid "Video"
+msgstr "Video"
+
msgid "Interface"
msgstr "Schnittstelle"
@@ -13493,6 +13271,9 @@ msgstr "Status / Untätig"
msgid "Themes"
msgstr "Themen"
+msgid "Voice/Video"
+msgstr "Sprache/Video"
+
msgid "Allow all users to contact me"
msgstr "Allen Benutzern erlauben, mich zu kontaktieren"
@@ -13679,12 +13460,6 @@ msgstr "Gespeicherter Status..."
msgid "Status Selector"
msgstr "Statusauswahl"
-msgid "Google Talk"
-msgstr "Google Talk"
-
-msgid "Facebook (XMPP)"
-msgstr "Facebook (XMPP)"
-
#, c-format
msgid "The following error has occurred loading %s: %s"
msgstr "Beim Laden von %s ist folgender Fehler aufgetreten: %s"
@@ -13819,18 +13594,12 @@ msgstr "Alia_s"
msgid "Close _tabs"
msgstr "_Reiter schließen"
-msgid "_Get Info"
-msgstr "_Info abrufen"
-
msgid "_Invite"
msgstr "_Einladen"
msgid "_Modify..."
msgstr "_Bearbeiten..."
-msgid "_Add..."
-msgstr "_Hinzufügen..."
-
msgid "_Open Mail"
msgstr "Mail ö_ffnen"
@@ -14858,89 +14627,12 @@ msgstr ""
"Dieses Plugin erlaubt es dem Benutzer die Zeitstempel in Unterhaltungen und "
"im Mitschnitt anzupassen."
-#. Alerts
-msgid "Chatroom alerts"
-msgstr "Chatraum-Alarme"
-
-msgid "Chatroom message alerts _only where someone says your username"
-msgstr "Chatraum-Alarme nur dann, wenn j_emand Ihren Namen sagt"
-
-#. Launcher integration
-msgid "Launcher Icon"
-msgstr "Starter-Icon"
-
-msgid "_Disable launcher integration"
-msgstr "_Starter-Integration deaktivieren"
-
-msgid "Show number of unread _messages on launcher icon"
-msgstr "Zeige Zahl der ungelesenen Nac_hrichten auf dem Starter-Icon"
-
-msgid "Show number of unread co_nversations on launcher icon"
-msgstr "Zeige Zahl der ungelesenen U_nterhaltungen auf dem Starter-Icon"
-
-#. Messaging menu integration
-msgid "Messaging Menu"
-msgstr "Benachrichtigungsfeld"
-
-msgid "Show number of _unread messages for conversations in messaging menu"
-msgstr "Zeige Zahl der _ungelesenen Nachrichten im Benachrichtigungsfeld"
-
-msgid "Show _elapsed time for unread conversations in messaging menu"
-msgstr ""
-"Zeige die _abgelaufene Zeit für ungelesene Nachrichten im "
-"Benachrichtigungsfeld"
-
-#. *< type
-#. *< ui_requirement
-#. *< flags
-#. *< dependencies
-#. *< priority
-#. *< id
-msgid "Unity Integration"
-msgstr "Unity-Integration"
-
-#. *< name
-#. *< version
-#. * summary
-msgid "Provides integration with Unity."
-msgstr "Erlaubt die Integration mit Unity."
-
-#. * description
-msgid "Provides integration with Unity's messaging menu and launcher."
-msgstr ""
-"Erlaubt die Integration mit Unity's Benachrichtigungsfeld und dem Unity-"
-"Starter."
-
-msgid "Audio"
-msgstr "Audio"
-
-msgid "Video"
-msgstr "Video"
-
-msgid "Output"
-msgstr "Ausgang"
-
-msgid "_Plugin"
-msgstr "_Plugin"
-
-msgid "_Device"
-msgstr "_Gerät"
-
-msgid "Input"
-msgstr "Eingang"
-
msgid "P_lugin"
msgstr "P_lugin"
msgid "D_evice"
msgstr "G_erät"
-msgid "DROP"
-msgstr "Stille, da unterhalb des Schwellwerts"
-
-msgid "Volume:"
-msgstr "Lautstärke:"
-
msgid "Silence threshold:"
msgstr "Schwellwert für Stille:"
@@ -15062,8 +14754,8 @@ msgid ""
msgstr ""
"Bietet spezielle Optionen für Windows-Pidgin, wie Buddy-Listen-Docking."
-msgid "<font color='#777777'>Logged out.</font>"
-msgstr "<font color='#777777'>Abgemeldet.</font>"
+msgid "Logged out."
+msgstr "Angemeldet."
#. *< type
#. *< ui_requirement
@@ -15077,8 +14769,8 @@ msgstr "XMPP-Konsole"
msgid "Account: "
msgstr "Konto: "
-msgid "<font color='#777777'>Not connected to XMPP</font>"
-msgstr "<font color='#777777'>Nicht mit XMPP verbunden</font>"
+msgid "Not connected to XMPP"
+msgstr "Nicht mit XMPP verbunden"
#. *< name
#. *< version
@@ -15239,118 +14931,3 @@ msgstr ""
msgid "You do not have permission to uninstall this application."
msgstr "Sie haben keine Berechtigung, diese Anwendung zu deinstallieren."
-
-#~ msgid "Token Error"
-#~ msgstr "Token-Fehler"
-
-#~ msgid "Unable to fetch the token.\n"
-#~ msgstr "Kann das Token nicht abholen.\n"
-
-#~ msgid "You must fill in all registration fields"
-#~ msgstr "Sie müssen alle Registrierungsfelder ausfüllen"
-
-#~ msgid "Passwords do not match"
-#~ msgstr "Passwörter stimmen nicht überein"
-
-#~ msgid "Unable to register new account. An unknown error occurred."
-#~ msgstr ""
-#~ "Kann neues Konto nicht anlegen. Es ist ein unbekannter Fehler aufgetreten."
-
-#~ msgid "New Gadu-Gadu Account Registered"
-#~ msgstr "Neues Gadu-Gadu-Konto angelegt"
-
-#~ msgid "Registration completed successfully!"
-#~ msgstr "Registrierung erfolgreich abgeschlossen!"
-
-#~ msgid "Enter captcha text"
-#~ msgstr "Captcha-Text eingeben"
-
-#~ msgid "Captcha"
-#~ msgstr "Captcha"
-
-#~ msgid "Register New Gadu-Gadu Account"
-#~ msgstr "Registrierung eines neuen Gadu-Gadu-Kontos"
-
-#~ msgid "Please, fill in the following fields"
-#~ msgstr "Bitte füllen Sie die folgenden Felder aus"
-
-#~ msgid "Fill in the fields."
-#~ msgstr "Füllen Sie die Felder aus."
-
-#~ msgid "Your current password is different from the one that you specified."
-#~ msgstr "Ihr aktuelles Passwort ist anders als das angegebene."
-
-#~ msgid "Unable to change password. Error occurred.\n"
-#~ msgstr "Konnte das Passwort nicht ändern. Es trat ein Fehler auf.\n"
-
-#~ msgid "Change password for the Gadu-Gadu account"
-#~ msgstr "Ändern des Passworts für dieses Gadu-Gadu-Konto"
-
-#~ msgid "Password was changed successfully!"
-#~ msgstr "Das Passwort wurde erfolgreich geändert!"
-
-#~ msgid "Current password"
-#~ msgstr "Aktuelles Passwort"
-
-#~ msgid "Password (retype)"
-#~ msgstr "Passwort (nochmal)"
-
-#~ msgid "Enter current token"
-#~ msgstr "Geben Sie das aktuelle Token ein"
-
-#~ msgid "Current token"
-#~ msgstr "Aktuelles Token"
-
-#~ msgid "Please, enter your current password and your new password for UIN: "
-#~ msgstr ""
-#~ "Bitte geben Sie Ihr aktuelles und Ihr neues Passwort für folgende UIN "
-#~ "ein: "
-
-#~ msgid "Change Gadu-Gadu Password"
-#~ msgstr "Gadu-Gadu Passwort ändern"
-
-#~ msgid "Change password..."
-#~ msgstr "Passwort ändern..."
-
-#~ msgid "Buddy list downloaded"
-#~ msgstr "Buddy-Liste heruntergeladen"
-
-#~ msgid "Your buddy list was downloaded from the server."
-#~ msgstr "Ihre Buddy-Liste wurde von Server geladen."
-
-#~ msgid "Buddy list uploaded"
-#~ msgstr "Buddy-Liste hochgeladen"
-
-#~ msgid "Your buddy list was stored on the server."
-#~ msgstr "Ihre Buddy-Liste wurde auf dem Server gespeichert."
-
-#~ msgid "Upload buddylist to Server"
-#~ msgstr "Buddy-Liste zum Server hochladen"
-
-#~ msgid "Download buddylist from Server"
-#~ msgstr "Buddy-Liste vom Server herunterladen"
-
-#~ msgid "Delete buddylist from Server"
-#~ msgstr "Löschen der Buddy-Liste vom Server"
-
-#~ msgid "Hidden Number"
-#~ msgstr "Versteckte Nummer"
-
-#~ msgid "No Sametime Community Server specified"
-#~ msgstr "Kein Sametime-Community Server angegeben"
-
-#~ msgid ""
-#~ "No host or IP address has been configured for the Meanwhile account %s. "
-#~ "Please enter one below to continue logging in."
-#~ msgstr ""
-#~ "Es wurde kein Rechner für das Meanwhile-Konto %s angegeben. Bitte geben "
-#~ "Sie einen Rechner an, um die Anmeldung fortzusetzen."
-
-#~ msgid "Meanwhile Connection Setup"
-#~ msgstr "Meanwhile-Verbindungseinstellungen"
-
-#~ msgid "No Sametime Community Server Specified"
-#~ msgstr "Kein Sametime-Community Server angegeben"
-
-#~ msgid "Connect"
-#~ msgstr "Verbinden"
diff --git a/po/mai.po b/po/mai.po
index e348ede3f7..2c2528f2b8 100644
--- a/po/mai.po
+++ b/po/mai.po
@@ -10,7 +10,7 @@ msgstr ""
"POT-Creation-Date: 2014-02-02 10:38-0800\n"
"PO-Revision-Date: 2010-09-02 18:48+0530\n"
"Last-Translator: sangeeta_0975@yahoo.com>\n"
-"Language-Team: Maithili <http://code.google.com/p/bhashaghar>\n"
+"Language-Team: Maithili <https://code.google.com/p/bhashaghar/>\n"
"Language: mai\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
diff --git a/po/pl.po b/po/pl.po
index bf1a064f69..bc3bd5bbe6 100644
--- a/po/pl.po
+++ b/po/pl.po
@@ -1,7 +1,7 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
-#
+#
# Translators:
# Copyright (C) Artur Flinta <aflinta@at.kernel.pl>, 2004
# Copyright (C) Krzysztof Foltman <kfoltman@onet.pl>, 2003
@@ -10,666 +10,1008 @@
# Copyright (C) Piotr Makowski <pmakowski@aviary.pl>, 2008
# Copyright (C) Przemysław Sułek <pbs@linux.net.pl>, 2001
# Copyright (C) Tomasz Sałaciński <tsalacinski@gmail.com>, 2007
-# Piotr Drąg <piotrdrag@gmail.com>, 2012-2014
-# tomkiewicz, 2013
+# Piotr Drąg <piotrdrag@gmail.com>, 2011-2014
+# Richard Laager <rlaager@wiktel.com>, 2011
+# tomkiewicz <twasilczyk@pidgin.im>, 2013-2014
msgid ""
msgstr ""
"Project-Id-Version: Pidgin\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-02-02 10:38-0800\n"
-"PO-Revision-Date: 2014-02-02 15:39+0000\n"
+"POT-Creation-Date: 2014-05-25 09:57-0500\n"
+"PO-Revision-Date: 2014-05-24 15:02+0000\n"
"Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n"
-"Language-Team: Polish (http://www.transifex.com/projects/p/pidgin/language/"
-"pl/)\n"
-"Language: pl\n"
+"Language-Team: Polish (http://www.transifex.com/projects/p/pidgin/language/pl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
-"|| n%100>=20) ? 1 : 2);\n"
+"Language: pl\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
#. Translators may want to transliterate the name.
#. It is not to be translated.
+#: ../finch/finch.c:44 ../finch/libfinch.c:67 ../finch/libfinch.c:329
msgid "Finch"
msgstr "Finch"
-#, c-format
-msgid "%s. Try `%s -h' for more information.\n"
-msgstr "%s. Należy wpisać \"%s -h\", aby uzyskać więcej informacji.\n"
-
-#, c-format
-msgid ""
-"%s\n"
-"Usage: %s [OPTION]...\n"
-"\n"
-" -c, --config=DIR use DIR for config files\n"
-" -d, --debug print debugging messages to stderr\n"
-" -h, --help display this help and exit\n"
-" -n, --nologin don't automatically login\n"
-" -v, --version display the current version and exit\n"
-msgstr ""
-"%s\n"
-"Użycie: %s [OPCJA]...\n"
-"\n"
-" -c, --config=KATALOG używa KATALOGU z plikami konfiguracji\n"
-" -d, --debug przekazuje komunikaty debugowania na standardowe\n"
-" wyjście błędów\n"
-" -h, --help wyświetla tę pomoc i kończy pracę\n"
-" -n, --nologin bez automatycznego logowania\n"
-" -v, --version wyświetla bieżącą wersję i kończy pracę\n"
-
-#, c-format
-msgid ""
-"%s encountered errors migrating your settings from %s to %s. Please "
-"investigate and complete the migration by hand. Please report this error at "
-"http://developer.pidgin.im"
-msgstr ""
-"Program %s napotkał błędy podczas migracji ustawień z %s do %s. Proszę "
-"sprawdzić błąd i ukończyć migrację ręcznie, a także zgłosić ten błąd na "
-"http://developer.pidgin.im"
-
#. the user did not fill in the captcha
+#: ../finch/gntaccount.c:123 ../finch/gntaccount.c:168
+#: ../finch/gntaccount.c:180 ../finch/gntaccount.c:568 ../finch/gntblist.c:638
+#: ../finch/gntblist.c:800 ../finch/gntplugin.c:244 ../finch/gntplugin.c:305
+#: ../finch/gntrequest.c:388 ../finch/gntstatus.c:297 ../finch/gntstatus.c:306
+#: ../finch/plugins/gntclipboard.c:116 ../finch/plugins/gntclipboard.c:122
+#: ../finch/plugins/gntclipboard.c:129
+#: ../libpurple/protocols/jabber/chat.c:824
+#: ../libpurple/protocols/jabber/chat.c:836
+#: ../libpurple/protocols/jabber/jabber.c:2343
+#: ../libpurple/protocols/jabber/jutil.c:716
+#: ../libpurple/protocols/mxit/login.c:532
+#: ../libpurple/protocols/mxit/protocol.c:2517
+#: ../libpurple/protocols/silc/ops.c:79 ../libpurple/protocols/silc/ops.c:1484
+#: ../pidgin/plugins/disco/xmppdisco.c:512
+#: ../pidgin/plugins/disco/xmppdisco.c:517
msgid "Error"
msgstr "Błąd"
+#: ../finch/gntaccount.c:124 ../finch/gntaccount.c:169
+#: ../finch/gntaccount.c:181
msgid "Account was not modified"
msgstr "Nie zmodyfikowano konta"
+#: ../finch/gntaccount.c:125
msgid "Account was not added"
msgstr "Nie dodano konta"
+#: ../finch/gntaccount.c:126
msgid "Username of an account must be non-empty."
msgstr "Nazwa użytkownika konta nie może być pusta."
+#: ../finch/gntaccount.c:170
msgid ""
-"The account's protocol cannot be changed while it is connected to the server."
-msgstr ""
-"Protokół konta nie może być zmieniany, kiedy jest połączone z serwerem."
+"The account's protocol cannot be changed while it is connected to the "
+"server."
+msgstr "Protokół konta nie może być zmieniany, kiedy jest połączone z serwerem."
+#: ../finch/gntaccount.c:182
msgid ""
-"The account's username cannot be changed while it is connected to the server."
-msgstr ""
-"Nazwa użytkownika konta nie może być zmieniana, kiedy jest połączone z "
-"serwerem."
+"The account's username cannot be changed while it is connected to the "
+"server."
+msgstr "Nazwa użytkownika konta nie może być zmieniana, kiedy jest połączone z serwerem."
+#: ../finch/gntaccount.c:519
msgid "New mail notifications"
msgstr "Powiadomienia o nowej poczcie"
+#: ../finch/gntaccount.c:529 ../libpurple/protocols/gg/account.c:234
msgid "Remember password"
msgstr "Zapamiętanie hasła"
+#: ../finch/gntaccount.c:569
msgid "There are no protocol plugins installed."
msgstr "Nie zainstalowano żadnych wtyczek protokołów."
+#: ../finch/gntaccount.c:570
msgid "(You probably forgot to 'make install'.)"
msgstr "(Prawdopodobnie zapomniano o wykonaniu polecenia \"make install\")"
+#: ../finch/gntaccount.c:581 ../finch/gntconn.c:132
+#: ../pidgin/gtkaccount.c:1787 ../pidgin/gtkblist.c:5335
msgid "Modify Account"
msgstr "Modyfikacja konta"
+#: ../finch/gntaccount.c:581
msgid "New Account"
msgstr "Nowe Konto"
+#: ../finch/gntaccount.c:607 ../pidgin/gtkxfer.c:653
msgid "Protocol:"
msgstr "Protokół:"
+#: ../finch/gntaccount.c:615
+#: ../pidgin/plugins/gevolution/new_person_dialog.c:290
msgid "Username:"
msgstr "Nazwa użytkownika:"
+#: ../finch/gntaccount.c:628
msgid "Password:"
msgstr "Hasło:"
+#: ../finch/gntaccount.c:638
msgid "Alias:"
msgstr "Alias:"
#. Register checkbox
+#: ../finch/gntaccount.c:649
msgid "Create this account on the server"
msgstr "Utworzenie tego konta na serwerze"
#. Cancel button
#. Cancel
+#: ../finch/gntaccount.c:665 ../finch/gntaccount.c:734
+#: ../finch/gntaccount.c:1031 ../finch/gntblist.c:692 ../finch/gntblist.c:790
+#: ../finch/gntblist.c:837 ../finch/gntblist.c:1149 ../finch/gntblist.c:1386
+#: ../finch/gntblist.c:1519 ../finch/gntblist.c:2693 ../finch/gntblist.c:2743
+#: ../finch/gntblist.c:2816 ../finch/gntblist.c:2877 ../finch/gntcertmgr.c:86
+#: ../finch/gntplugin.c:594 ../finch/gntpounce.c:468 ../finch/gntpounce.c:675
+#: ../finch/gntprefs.c:267 ../finch/gntprefs.c:305 ../finch/gntsound.c:1075
+#: ../finch/gntstatus.c:141 ../finch/gntstatus.c:481 ../finch/gntstatus.c:606
+#: ../finch/plugins/gnthistory.c:180 ../libpurple/account.c:302
+#: ../libpurple/account.c:687 ../libpurple/account.c:721
+#: ../libpurple/conversation.c:804 ../libpurple/conversationtypes.c:1279
+#: ../libpurple/plugins/buddynote.c:51
+#: ../libpurple/plugins/keyrings/internalkeyring.c:617
+#: ../libpurple/protocols/gg/account.c:259
+#: ../libpurple/protocols/gg/account.c:516
+#: ../libpurple/protocols/gg/pubdir-prpl.c:544
+#: ../libpurple/protocols/gg/pubdir-prpl.c:856
+#: ../libpurple/protocols/gg/purplew.c:71
+#: ../libpurple/protocols/gg/status.c:360
+#: ../libpurple/protocols/jabber/buddy.c:693
+#: ../libpurple/protocols/jabber/buddy.c:2243
+#: ../libpurple/protocols/jabber/buddy.c:2294
+#: ../libpurple/protocols/jabber/chat.c:938
+#: ../libpurple/protocols/jabber/jabber.c:1464
+#: ../libpurple/protocols/jabber/jabber.c:1475
+#: ../libpurple/protocols/jabber/jabber.c:2553
+#: ../libpurple/protocols/jabber/jabber.c:3417
+#: ../libpurple/protocols/jabber/si.c:1573
+#: ../libpurple/protocols/jabber/usernick.c:83
+#: ../libpurple/protocols/jabber/xdata.c:407
+#: ../libpurple/protocols/msn/msn.c:459 ../libpurple/protocols/msn/msn.c:582
+#: ../libpurple/protocols/msn/msn.c:644 ../libpurple/protocols/msn/msn.c:659
+#: ../libpurple/protocols/msn/msn.c:676 ../libpurple/protocols/msn/msn.c:693
+#: ../libpurple/protocols/msn/msn.c:714
+#: ../libpurple/protocols/mxit/actions.c:311
+#: ../libpurple/protocols/mxit/actions.c:405
+#: ../libpurple/protocols/mxit/login.c:372
+#: ../libpurple/protocols/mxit/login.c:709
+#: ../libpurple/protocols/oscar/oscar.c:5041
+#: ../libpurple/protocols/oscar/peer.c:1098
+#: ../libpurple/protocols/sametime/sametime.c:3425
+#: ../libpurple/protocols/sametime/sametime.c:3511
+#: ../libpurple/protocols/sametime/sametime.c:5382
+#: ../libpurple/protocols/sametime/sametime.c:5473
+#: ../libpurple/protocols/sametime/sametime.c:5599
+#: ../libpurple/protocols/silc/buddy.c:469
+#: ../libpurple/protocols/silc/buddy.c:1092
+#: ../libpurple/protocols/silc/buddy.c:1207
+#: ../libpurple/protocols/silc/chat.c:625
+#: ../libpurple/protocols/silc/chat.c:759
+#: ../libpurple/protocols/silc/ops.c:1855
+#: ../libpurple/protocols/silc/silc.c:1071
+#: ../libpurple/protocols/silc/silc.c:1282
+#: ../libpurple/protocols/yahoo/libymsg.c:4418
+#: ../libpurple/protocols/yahoo/libymsg.c:4428
+#: ../libpurple/protocols/yahoo/yahoo_aliases.c:564
+#: ../libpurple/protocols/yahoo/yahoo_aliases.c:577
+#: ../libpurple/request.c:2305 ../pidgin/gtkaccount.c:2169
+#: ../pidgin/gtkaccount.c:2708 ../pidgin/gtkaccount.c:2772
+#: ../pidgin/gtkaccount.c:2814 ../pidgin/gtkblist.c:701
+#: ../pidgin/gtkblist.c:3669 ../pidgin/gtkblist.c:7570
+#: ../pidgin/gtkcertmgr.c:191 ../pidgin/gtkcertmgr.c:329
+#: ../pidgin/gtkdialogs.c:948 ../pidgin/gtkdialogs.c:1087
+#: ../pidgin/gtkdialogs.c:1178 ../pidgin/gtkdialogs.c:1201
+#: ../pidgin/gtkdialogs.c:1227 ../pidgin/gtkdialogs.c:1278
+#: ../pidgin/gtkdialogs.c:1319 ../pidgin/gtkdialogs.c:1375
+#: ../pidgin/gtkdialogs.c:1414 ../pidgin/gtkdialogs.c:1440
+#: ../pidgin/gtklog.c:321 ../pidgin/gtkplugin.c:441 ../pidgin/gtkpounce.c:1139
+#: ../pidgin/gtkprivacy.c:495 ../pidgin/gtkprivacy.c:510
+#: ../pidgin/gtkprivacy.c:535 ../pidgin/gtkprivacy.c:548
+#: ../pidgin/gtkrequest.c:340 ../pidgin/gtkrequest.c:964
+#: ../pidgin/gtksavedstatuses.c:312 ../pidgin/gtkstatusbox.c:1604
+#: ../pidgin/gtkutils.c:1539 ../pidgin/gtkutils.c:1559
+#: ../pidgin/gtkwebviewtoolbar.c:590 ../pidgin/plugins/disco/gtkdisco.c:251
+#: ../pidgin/plugins/imgupload.c:425
msgid "Cancel"
msgstr "Anuluj"
#. Save button
#. Save
+#: ../finch/gntaccount.c:669 ../finch/gntcertmgr.c:304 ../finch/gntdebug.c:338
+#: ../finch/gntplugin.c:594 ../finch/gntpounce.c:474 ../finch/gntprefs.c:267
+#: ../finch/gntprefs.c:304 ../finch/gntsound.c:1072 ../finch/gntstatus.c:484
+#: ../finch/gntstatus.c:594 ../libpurple/account.c:720
+#: ../libpurple/plugins/buddynote.c:50
+#: ../libpurple/protocols/jabber/buddy.c:692 ../pidgin/gtkblist.c:701
+#: ../pidgin/gtkdebug.c:492 ../pidgin/gtkrequest.c:347
msgid "Save"
msgstr "Zapisz"
+#: ../finch/gntaccount.c:728 ../pidgin/gtkaccount.c:2162
+#: ../pidgin/gtksavedstatuses.c:300 ../pidgin/gtkstatusbox.c:1598
#, c-format
msgid "Are you sure you want to delete %s?"
msgstr "Na pewno usunąć %s?"
+#: ../finch/gntaccount.c:731
msgid "Delete Account"
msgstr "Usuń konto"
#. Delete button
+#: ../finch/gntaccount.c:734 ../finch/gntaccount.c:849
+#: ../finch/gntcertmgr.c:312 ../finch/gntpounce.c:674 ../finch/gntpounce.c:737
+#: ../finch/gntstatus.c:140 ../finch/gntstatus.c:206
+#: ../pidgin/gtkaccount.c:2169 ../pidgin/gtklog.c:320
+#: ../pidgin/gtkpounce.c:1138 ../pidgin/gtkrequest.c:344
+#: ../pidgin/gtksavedstatuses.c:311 ../pidgin/gtkstatusbox.c:1603
msgid "Delete"
msgstr "Usuń"
+#: ../finch/gntaccount.c:810 ../finch/gntblist.c:2559 ../finch/gntui.c:103
+#: ../pidgin/gtkaccount.c:2566 ../pidgin/gtkaccount.c:2568
msgid "Accounts"
msgstr "Konta"
+#: ../finch/gntaccount.c:816
msgid "You can enable/disable accounts from the following list."
msgstr "Można włączyć/wyłączyć konta z poniższej listy."
#. Add button
+#: ../finch/gntaccount.c:840 ../finch/gntaccount.c:1030
+#: ../finch/gntblist.c:691 ../finch/gntblist.c:790 ../finch/gntblist.c:837
+#: ../finch/gntblist.c:2996 ../finch/gntcertmgr.c:299 ../finch/gntnotify.c:464
+#: ../finch/gntpounce.c:721 ../finch/gntroomlist.c:272
+#: ../finch/gntstatus.c:195 ../libpurple/protocols/sametime/sametime.c:5472
+#: ../libpurple/protocols/silc/chat.c:624 ../pidgin/gtkaccount.c:2707
+#: ../pidgin/gtkblist.c:7569 ../pidgin/gtkconv.c:1719
+#: ../pidgin/gtkrequest.c:345
msgid "Add"
msgstr "Dodaj"
#. Modify button
+#: ../finch/gntaccount.c:845 ../finch/gntpounce.c:729
msgid "Modify"
msgstr "Zmodyfikuj"
+#: ../finch/gntaccount.c:953 ../pidgin/gtkaccount.c:2654
#, c-format
msgid "%s%s%s%s has made %s his or her buddy%s%s"
msgstr "Użytkownik %s%s%s%s dodał %s do swojej listy znajomych%s%s"
+#: ../finch/gntaccount.c:1027 ../pidgin/gtkaccount.c:2706
msgid "Add buddy to your list?"
msgstr "Dodać do listy znajomych?"
+#: ../finch/gntaccount.c:1086
#, c-format
msgid "%s%s%s%s wants to add %s to his or her buddy list%s%s"
msgstr "Użytkownik %s%s%s%s chce dodać %s do swojej listy znajomych%s%s"
+#: ../finch/gntaccount.c:1111 ../finch/gntaccount.c:1114
+#: ../finch/gntaccount.c:1141 ../pidgin/gtkaccount.c:2912
msgid "Authorize buddy?"
msgstr "Upoważnić znajomego?"
+#: ../finch/gntaccount.c:1118 ../finch/gntaccount.c:1145
+#: ../pidgin/gtkaccount.c:2913
msgid "Authorize"
msgstr "Upoważnij"
+#: ../finch/gntaccount.c:1119 ../finch/gntaccount.c:1146
+#: ../pidgin/gtkaccount.c:2914
msgid "Deny"
msgstr "Odrzuć"
+#: ../finch/gntblist.c:269
#, c-format
msgid ""
"Online: %d\n"
"Total: %d"
-msgstr ""
-"Online: %d\n"
-"Razem: %d"
+msgstr "Online: %d\nRazem: %d"
+#: ../finch/gntblist.c:278
#, c-format
msgid "Account: %s (%s)"
msgstr "Konto: %s (%s)"
+#: ../finch/gntblist.c:290
#, c-format
msgid ""
"\n"
"Last Seen: %s ago"
-msgstr ""
-"\n"
-"Ostatnio widziany: %s temu"
+msgstr "\nOstatnio widziany: %s temu"
+#: ../finch/gntblist.c:310 ../libpurple/protocols/gg/gg.c:1137
+#: ../pidgin/gtkprefs.c:641 ../pidgin/gtkprefs.c:775 ../pidgin/gtkprefs.c:783
+#: ../pidgin/gtkprefs.c:794 ../pidgin/gtkprefs.c:3487
+#: ../pidgin/gtkprefs.c:3618
+#: ../pidgin/pixmaps/emotes/default/24/default.theme.in.h:1
msgid "Default"
msgstr "Domyślny"
+#: ../finch/gntblist.c:627
msgid "You must provide a username for the buddy."
msgstr "Należy podać nazwę użytkownika dla znajomego."
+#: ../finch/gntblist.c:629
msgid "You must provide a group."
msgstr "Należy podać grupę."
+#: ../finch/gntblist.c:631
msgid "You must select an account."
msgstr "Należy wybrać konto."
+#: ../finch/gntblist.c:633
msgid "The selected account is not online."
msgstr "Wybrane konto nie jest w trybie online."
+#: ../finch/gntblist.c:638
msgid "Error adding buddy"
msgstr "Błąd podczas dodawania znajomego"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
+#: ../finch/gntblist.c:670 ../libpurple/protocols/jabber/jabber.c:1398
+#: ../libpurple/protocols/jabber/jabber.c:1400
+#: ../libpurple/protocols/oscar/oscar.c:2828
+#: ../libpurple/protocols/silc/buddy.c:1565
+#: ../libpurple/protocols/silc/ops.c:1206
+#: ../libpurple/protocols/silc/ops.c:1209
+#: ../libpurple/protocols/silc/ops.c:1352
+#: ../libpurple/protocols/silc/ops.c:1355
+#: ../libpurple/protocols/silc/silc.c:1256 ../pidgin/gtkaccount.c:2242
+#: ../pidgin/gtksavedstatuses.c:946
msgid "Username"
msgstr "Nazwa użytkownika"
+#: ../finch/gntblist.c:673
msgid "Alias (optional)"
msgstr "Alias (opcjonalny)"
+#: ../finch/gntblist.c:676
msgid "Invite message (optional)"
msgstr "Wiadomość zaproszenia (opcjonalna)"
+#: ../finch/gntblist.c:679
msgid "Add in group"
msgstr "Dodaj do grupy"
+#: ../finch/gntblist.c:683 ../finch/gntblist.c:769 ../finch/gntblist.c:1729
+#: ../finch/gntblist.c:2674 ../finch/gntblist.c:2729 ../finch/gntblist.c:2803
+#: ../finch/gntblist.c:2862 ../finch/gntnotify.c:192 ../finch/gntstatus.c:572
+#: ../libpurple/plugins/idle.c:150 ../libpurple/plugins/idle.c:186
+#: ../pidgin/gtkblist.c:3920 ../pidgin/gtknotify.c:767
+#: ../pidgin/gtkpounce.c:1290 ../pidgin/plugins/gevolution/gevolution.c:462
msgid "Account"
msgstr "Konto"
+#: ../finch/gntblist.c:689 ../finch/gntblist.c:1198
+#: ../libpurple/protocols/silc/buddy.c:741
+#: ../libpurple/protocols/silc/buddy.c:1040
+#: ../libpurple/protocols/silc/buddy.c:1088
+#: ../libpurple/protocols/silc/buddy.c:1198
+#: ../libpurple/protocols/yahoo/libymsg.c:4236 ../pidgin/gtkblist.c:7327
+#: ../pidgin/plugins/gevolution/add_buddy_dialog.c:451
msgid "Add Buddy"
msgstr "Dodanie znajomego"
+#: ../finch/gntblist.c:689
msgid "Please enter buddy information."
msgstr "Proszę podać informacje o znajomym."
+#: ../finch/gntblist.c:737 ../libpurple/buddylist.c:856
msgid "Chats"
msgstr "Konferencje"
#. Extract their Name and put it in
+#: ../finch/gntblist.c:775 ../finch/gntblist.c:2669 ../finch/gntblist.c:2724
+#: ../finch/gntblist.c:2857 ../finch/gntroomlist.c:296
+#: ../libpurple/protocols/gg/pubdir-prpl.c:524
+#: ../libpurple/protocols/gg/pubdir-prpl.c:692
+#: ../libpurple/protocols/jabber/jabber.c:1421
+#: ../libpurple/protocols/jabber/jabber.c:1425
+#: ../libpurple/protocols/msn/msn.c:536 ../libpurple/protocols/msn/msn.c:2403
+#: ../libpurple/protocols/msn/msn.c:2468 ../libpurple/protocols/msn/msn.c:2495
+#: ../pidgin/gtkplugin.c:872 ../pidgin/gtkroomlist.c:783
+#: ../pidgin/plugins/disco/gtkdisco.c:561
+#: ../pidgin/plugins/gevolution/add_buddy_dialog.c:133
+#: ../pidgin/plugins/gevolution/assoc-buddy.c:123
msgid "Name"
msgstr "Nazwa"
+#: ../finch/gntblist.c:778 ../finch/gntblist.c:1672
+#: ../libpurple/protocols/gg/gg.c:645
+#: ../libpurple/protocols/gg/pubdir-prpl.c:400
+#: ../libpurple/protocols/msn/msn.c:2226
+#: ../libpurple/protocols/mxit/profile.c:207
+#: ../libpurple/protocols/silc/chat.c:615
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:705
+#: ../libpurple/protocols/zephyr/zephyr.c:807
+#: ../libpurple/protocols/zephyr/zephyr.c:1193 ../pidgin/gtkdialogs.c:1200
+#: ../pidgin/gtkdialogs.c:1226 ../pidgin/gtkrequest.c:352
msgid "Alias"
msgstr "Alias"
+#: ../finch/gntblist.c:781 ../finch/gntblist.c:3012
+#: ../pidgin/plugins/themeedit.c:225
msgid "Group"
msgstr "Grupa"
+#: ../finch/gntblist.c:785 ../finch/gntblist.c:1164
msgid "Auto-join"
msgstr "Automatyczne dołączanie"
+#: ../finch/gntblist.c:788 ../finch/gntblist.c:1200 ../pidgin/gtkblist.c:7510
msgid "Add Chat"
msgstr "Dodanie konferencji"
+#: ../finch/gntblist.c:789
msgid "You can edit more information from the context menu later."
msgstr "Można później zmodyfikować więcej informacji z menu kontekstowego."
+#: ../finch/gntblist.c:800
msgid "Error adding group"
msgstr "Błąd podczas dodawania grupy"
+#: ../finch/gntblist.c:801
msgid "You must give a name for the group to add."
msgstr "Należy podać nazwę dodawanej grupy."
+#: ../finch/gntblist.c:835 ../finch/gntblist.c:1202
+#: ../libpurple/protocols/sametime/sametime.c:5381
+#: ../libpurple/protocols/sametime/sametime.c:5470 ../pidgin/gtkblist.c:7566
msgid "Add Group"
msgstr "Dodanie grupy"
+#: ../finch/gntblist.c:835
msgid "Enter the name of the group"
msgstr "Proszę podać nazwę dodawanej grupy"
+#: ../finch/gntblist.c:1148 ../pidgin/gtkblist.c:700
msgid "Edit Chat"
msgstr "Zmodyfikuj konferencję"
+#: ../finch/gntblist.c:1148
msgid "Please Update the necessary fields."
msgstr "Proszę zaktualizować wymagane pola."
+#: ../finch/gntblist.c:1149 ../finch/gntstatus.c:201
msgid "Edit"
msgstr "Edycja"
+#: ../finch/gntblist.c:1174
msgid "Edit Settings"
msgstr "Zmodyfikuj ustawienia"
+#: ../finch/gntblist.c:1210 ../pidgin/gtkutils.c:937
msgid "Information"
msgstr "Informacje"
+#: ../finch/gntblist.c:1210 ../pidgin/gtkutils.c:937
msgid "Retrieving..."
msgstr "Pobieranie..."
+#: ../finch/gntblist.c:1275 ../finch/gntconv.c:715
+#: ../libpurple/protocols/silc/chat.c:913
msgid "Get Info"
msgstr "Pobierz informacje"
+#: ../finch/gntblist.c:1279 ../pidgin/gtkpounce.c:546
msgid "Add Buddy Pounce"
msgstr "Dodanie przechwytywania zdarzeń"
+#: ../finch/gntblist.c:1286 ../finch/gntconv.c:727
+#: ../libpurple/protocols/jabber/si.c:1573
+#: ../libpurple/protocols/oscar/userinfo.c:58 ../pidgin/gtkconv.c:1668
msgid "Send File"
msgstr "Wyślij plik"
+#: ../finch/gntblist.c:1293 ../libpurple/protocols/gg/status.c:460
+#: ../libpurple/protocols/msn/msn.c:1131
msgid "Blocked"
msgstr "Zablokowano"
+#: ../finch/gntblist.c:1298
msgid "Show when offline"
msgstr "Wyświetlaj w trybie offline"
+#: ../finch/gntblist.c:1381
#, c-format
msgid "Please enter the new name for %s"
msgstr "Proszę podać nową nazwę dla %s"
+#: ../finch/gntblist.c:1383 ../finch/gntblist.c:1672
msgid "Rename"
msgstr "Zmień nazwę"
+#: ../finch/gntblist.c:1383
msgid "Set Alias"
msgstr "Ustaw alias"
+#: ../finch/gntblist.c:1384
msgid "Enter empty string to reset the name."
msgstr "Proszę podać pusty ciąg, aby przywrócić nazwę."
+#: ../finch/gntblist.c:1497
msgid "Removing this contact will also remove all the buddies in the contact"
-msgstr ""
-"Usunięcie tego kontaktu spowoduje usunięcie wszystkich znajomych w tym "
-"kontakcie"
+msgstr "Usunięcie tego kontaktu spowoduje usunięcie wszystkich znajomych w tym kontakcie"
+#: ../finch/gntblist.c:1505
msgid "Removing this group will also remove all the buddies in the group"
-msgstr ""
-"Usunięcie tej grupy spowoduje usunięcie wszystkich znajomych z tej grupy"
+msgstr "Usunięcie tej grupy spowoduje usunięcie wszystkich znajomych z tej grupy"
+#: ../finch/gntblist.c:1510
#, c-format
msgid "Are you sure you want to remove %s?"
msgstr "Na pewno usunąć %s?"
#. XXX: anything to do with the returned ui-handle?
+#: ../finch/gntblist.c:1513
msgid "Confirm Remove"
msgstr "Potwierdzenie usunięcia"
+#: ../finch/gntblist.c:1518 ../finch/gntblist.c:1674 ../finch/gntxfer.c:238
+#: ../pidgin/gtkconv.c:1716 ../pidgin/gtkrequest.c:346
+#: ../pidgin/gtkstatusbox.c:322
msgid "Remove"
msgstr "Usuń"
#. Buddy List
+#: ../finch/gntblist.c:1648 ../finch/gntblist.c:3053 ../finch/gntprefs.c:259
+#: ../finch/gntui.c:104 ../pidgin/gtkblist.c:5965
+#: ../pidgin/plugins/win32/winprefs/winprefs.c:300
msgid "Buddy List"
msgstr "Lista znajomych"
+#: ../finch/gntblist.c:1679
msgid "Place tagged"
msgstr "Nadano znaczniki miejscu"
+#: ../finch/gntblist.c:1684
msgid "Toggle Tag"
msgstr "Przełącz znacznik"
+#: ../finch/gntblist.c:1688 ../finch/gntblist.c:2871
msgid "View Log"
-msgstr "Wyświetl dziennik rozmów"
+msgstr "Wyświetl archiwum rozmów"
#. General
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
+#: ../finch/gntblist.c:1723 ../libpurple/protocols/gg/pubdir-prpl.c:415
+#: ../libpurple/protocols/jabber/buddy.c:353
+#: ../libpurple/protocols/jabber/buddy.c:1072
+#: ../libpurple/protocols/jabber/buddy.c:2032
+#: ../libpurple/protocols/jabber/buddy.c:2226
+#: ../libpurple/protocols/jabber/jabber.c:1317
+#: ../libpurple/protocols/jabber/jabber.c:2366
+#: ../libpurple/protocols/jabber/jabber.c:2390
+#: ../libpurple/protocols/jabber/jabber.c:2406
+#: ../libpurple/protocols/jabber/jabber.c:2422
+#: ../libpurple/protocols/jabber/jabber.c:2436
+#: ../libpurple/protocols/msn/msn.c:2233 ../libpurple/protocols/msn/msn.c:2406
+#: ../libpurple/protocols/silc/buddy.c:1561
+#: ../libpurple/protocols/silc/ops.c:1015
+#: ../libpurple/protocols/silc/ops.c:1199
+#: ../libpurple/protocols/silc/ops.c:1345
+#: ../libpurple/protocols/yahoo/yahoo_aliases.c:512
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1062 ../pidgin/gtkblist.c:3944
msgid "Nickname"
msgstr "Pseudonim"
#. Never know what those translations might end up like...
#. Idle stuff
+#: ../finch/gntblist.c:1744 ../finch/gntprefs.c:263
+#: ../libpurple/protocols/bonjour/bonjour.c:377
+#: ../libpurple/protocols/jabber/buddy.c:765
+#: ../libpurple/protocols/jabber/jabber.c:2228
+#: ../libpurple/protocols/msn/msn.c:1077 ../libpurple/protocols/msn/msn.c:1094
+#: ../libpurple/protocols/msn/msn.c:1101 ../libpurple/protocols/msn/state.c:38
+#: ../libpurple/protocols/novell/novell.c:2881
+#: ../libpurple/protocols/oscar/userinfo.c:504
+#: ../libpurple/protocols/yahoo/libymsg.c:3962
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:710 ../pidgin/gtkblist.c:3971
+#: ../pidgin/gtkblist.c:4433 ../pidgin/gtkprefs.c:3394
msgid "Idle"
msgstr "Bezczynny"
+#: ../finch/gntblist.c:1758
msgid "On Mobile"
msgstr "Używa telefonu komórkowego"
+#: ../finch/gntblist.c:2065 ../pidgin/gtkdocklet.c:619
msgid "New..."
msgstr "Nowy..."
+#: ../finch/gntblist.c:2072 ../pidgin/gtkdocklet.c:620
msgid "Saved..."
msgstr "Zapisane..."
+#: ../finch/gntblist.c:2527 ../finch/gntconv.c:757 ../finch/gntplugin.c:414
+#: ../finch/gntui.c:109 ../pidgin/gtkplugin.c:830
msgid "Plugins"
msgstr "Wtyczki"
+#: ../finch/gntblist.c:2682 ../finch/gntblist.c:2687
msgid "Block/Unblock"
msgstr "Zablokuj/odblokuj"
+#: ../finch/gntblist.c:2683
msgid "Block"
msgstr "Zablokuj"
+#: ../finch/gntblist.c:2684
msgid "Unblock"
msgstr "Odblokuj"
+#: ../finch/gntblist.c:2689
msgid ""
-"Please enter the username or alias of the person you would like to Block/"
-"Unblock."
-msgstr ""
-"Proszę podać nazwę użytkownika lub alias osoby do zablokowania/odblokowania."
+"Please enter the username or alias of the person you would like to "
+"Block/Unblock."
+msgstr "Proszę podać nazwę użytkownika lub alias osoby do zablokowania/odblokowania."
#. Not multiline
#. Not masked?
#. No hints?
+#: ../finch/gntblist.c:2692 ../finch/gntblist.c:2742 ../finch/gntblist.c:2876
+#: ../finch/gntcertmgr.c:85 ../finch/gntconn.c:131 ../finch/gntnotify.c:81
+#: ../finch/plugins/gnthistory.c:179 ../libpurple/account.c:301
+#: ../libpurple/account.c:686
+#: ../libpurple/plugins/keyrings/internalkeyring.c:616
+#: ../libpurple/protocols/gg/account.c:258
+#: ../libpurple/protocols/gg/account.c:515
+#: ../libpurple/protocols/gg/pubdir-prpl.c:543
+#: ../libpurple/protocols/gg/pubdir-prpl.c:855
+#: ../libpurple/protocols/gg/status.c:359
+#: ../libpurple/protocols/jabber/jabber.c:2552
+#: ../libpurple/protocols/jabber/xdata.c:406
+#: ../libpurple/protocols/msn/msn.c:458 ../libpurple/protocols/msn/msn.c:581
+#: ../libpurple/protocols/msn/msn.c:658 ../libpurple/protocols/msn/msn.c:675
+#: ../libpurple/protocols/msn/msn.c:692
+#: ../libpurple/protocols/mxit/login.c:371
+#: ../libpurple/protocols/oscar/oscar.c:5040
+#: ../libpurple/protocols/silc/buddy.c:468
+#: ../libpurple/protocols/silc/buddy.c:1206
+#: ../libpurple/protocols/silc/chat.c:454
+#: ../libpurple/protocols/silc/chat.c:491
+#: ../libpurple/protocols/silc/chat.c:758
+#: ../libpurple/protocols/silc/ops.c:1314
+#: ../libpurple/protocols/silc/ops.c:1854
+#: ../libpurple/protocols/silc/silc.c:1070
+#: ../libpurple/protocols/yahoo/libymsg.c:4417
+#: ../libpurple/protocols/yahoo/libymsg.c:4427
+#: ../libpurple/protocols/yahoo/yahoo_aliases.c:563
+#: ../libpurple/protocols/yahoo/yahoo_aliases.c:576
+#: ../pidgin/gtkaccount.c:2771 ../pidgin/gtkaccount.c:2813
+#: ../pidgin/gtkblist.c:3668 ../pidgin/gtkcertmgr.c:189
+#: ../pidgin/gtkcertmgr.c:328 ../pidgin/gtkdialogs.c:947
+#: ../pidgin/gtkdialogs.c:1086 ../pidgin/gtkdialogs.c:1177
+#: ../pidgin/gtkrequest.c:338 ../pidgin/gtkutils.c:1538
+#: ../pidgin/gtkutils.c:1558 ../pidgin/plugins/imgupload.c:424
msgid "OK"
msgstr "OK"
+#: ../finch/gntblist.c:2737 ../pidgin/gtkdialogs.c:942
msgid "New Instant Message"
msgstr "Nowa wiadomość"
+#: ../finch/gntblist.c:2739 ../pidgin/gtkdialogs.c:944
msgid "Please enter the username or alias of the person you would like to IM."
-msgstr ""
-"Proszę podać nazwę użytkownika lub alias osoby, do którego wysłać wiadomość."
+msgstr "Proszę podać nazwę użytkownika lub alias osoby, do którego wysłać wiadomość."
+#: ../finch/gntblist.c:2799
msgid "Channel"
msgstr "Kanał"
+#: ../finch/gntblist.c:2811 ../pidgin/gtkblist.c:1150
msgid "Join a Chat"
msgstr "Dołączenie do konferencji"
+#: ../finch/gntblist.c:2813
msgid "Please enter the name of the chat you want to join."
msgstr "Proszę podać nazwę konferencji, do której dołączyć."
+#: ../finch/gntblist.c:2815 ../finch/gntnotify.c:473
msgid "Join"
msgstr "Dołącz"
+#: ../finch/gntblist.c:2873 ../pidgin/gtkdialogs.c:1174
msgid ""
-"Please enter the username or alias of the person whose log you would like to "
-"view."
-msgstr ""
-"Proszę podać nazwę użytkownika lub alias osoby, której dziennik rozmów "
-"wyświetlić."
+"Please enter the username or alias of the person whose log you would like to"
+" view."
+msgstr "Proszę podać nazwę użytkownika lub alias osoby, której archiwum rozmów wyświetlić."
#. Create the "Options" frame.
+#: ../finch/gntblist.c:2926 ../finch/gntpounce.c:456 ../pidgin/gtkpounce.c:836
msgid "Options"
msgstr "Opcje"
+#: ../finch/gntblist.c:2932
msgid "Send IM..."
msgstr "Wyślij wiadomość..."
+#: ../finch/gntblist.c:2937
msgid "Block/Unblock..."
msgstr "Zablokuj/odblokuj..."
+#: ../finch/gntblist.c:2942 ../pidgin/gtkdocklet.c:747
msgid "Join Chat..."
msgstr "Dołącz do konferencji..."
+#: ../finch/gntblist.c:2947 ../finch/gntconv.c:741
msgid "View Log..."
-msgstr "Wyświetl dziennik rozmów..."
+msgstr "Wyświetl archiwum rozmów..."
+#: ../finch/gntblist.c:2952
msgid "View All Logs"
-msgstr "Wyświetl wszystkie dzienniki rozmów"
+msgstr "Wyświetl całe archiwum rozmów"
+#: ../finch/gntblist.c:2957
msgid "Show"
msgstr "Wyświetl"
+#: ../finch/gntblist.c:2962
msgid "Empty groups"
msgstr "Puste grupy"
+#: ../finch/gntblist.c:2969
msgid "Offline buddies"
msgstr "Znajomi w trybie offline"
+#: ../finch/gntblist.c:2976
msgid "Sort"
msgstr "Uporządkuj"
+#: ../finch/gntblist.c:2981
msgid "By Status"
msgstr "Według stanu"
+#: ../finch/gntblist.c:2986 ../pidgin/gtkblist.c:4988
msgid "Alphabetically"
msgstr "Alfabetycznie"
+#: ../finch/gntblist.c:2991
msgid "By Log Size"
-msgstr "Według rozmiaru dziennika"
+msgstr "Według rozmiaru archiwum"
+#: ../finch/gntblist.c:3002 ../libpurple/conversationtypes.c:1266
+#: ../pidgin/gtknotify.c:1693
msgid "Buddy"
msgstr "Znajomy"
+#: ../finch/gntblist.c:3007 ../libpurple/protocols/oscar/userinfo.c:52
+#: ../libpurple/protocols/silc/silc.c:1016
+#: ../libpurple/protocols/silc/util.c:518
+#: ../pidgin/plugins/disco/gtkdisco.c:489
msgid "Chat"
msgstr "Konferencja"
+#: ../finch/gntblist.c:3017 ../finch/plugins/grouping.c:387
msgid "Grouping"
msgstr "Grupowanie"
+#: ../finch/gntcertmgr.c:81 ../pidgin/gtkcertmgr.c:182
msgid "Certificate Import"
msgstr "Import certyfikatu"
+#: ../finch/gntcertmgr.c:82 ../pidgin/gtkcertmgr.c:183
msgid "Specify a hostname"
msgstr "Proszę podać nazwę komputera"
+#: ../finch/gntcertmgr.c:83
msgid "Type the host name this certificate is for."
msgstr "Proszę podać nazwę komputera, dla którego jest ten certyfikat."
+#: ../finch/gntcertmgr.c:91 ../pidgin/gtkcertmgr.c:204
#, c-format
msgid ""
"File %s could not be imported.\n"
"Make sure that the file is readable and in PEM format.\n"
-msgstr ""
-"Plik %s nie może zostać zaimportowany.\n"
-"Proszę się upewnić, czy można odczytać plik i czy jest on w formacie PEM.\n"
+msgstr "Plik %s nie może zostać zaimportowany.\nProszę się upewnić, czy można odczytać plik i czy jest on w formacie PEM.\n"
+#: ../finch/gntcertmgr.c:93 ../pidgin/gtkcertmgr.c:206
msgid "Certificate Import Error"
msgstr "Błąd podczas importowania certyfikatu"
+#: ../finch/gntcertmgr.c:94 ../pidgin/gtkcertmgr.c:207
msgid "X.509 certificate import failed"
msgstr "Zaimportowanie certyfikatu X.509 się nie powiodło"
+#: ../finch/gntcertmgr.c:104 ../pidgin/gtkcertmgr.c:218
msgid "Select a PEM certificate"
msgstr "Wybór certyfikatu PEM"
+#: ../finch/gntcertmgr.c:121 ../pidgin/gtkcertmgr.c:239
#, c-format
msgid ""
"Export to file %s failed.\n"
"Check that you have write permission to the target path\n"
-msgstr ""
-"Wyeksportowanie do pliku %s się nie powiodło.\n"
-"Proszę upewnić się, czy użytkownik posiada uprawnienie do zapisu do ścieżki "
-"docelowej\n"
+msgstr "Wyeksportowanie do pliku %s się nie powiodło.\nProszę upewnić się, czy użytkownik posiada uprawnienie do zapisu do ścieżki docelowej\n"
+#: ../finch/gntcertmgr.c:123 ../pidgin/gtkcertmgr.c:241
msgid "Certificate Export Error"
msgstr "Błąd podczas eksportowania certyfikatu"
+#: ../finch/gntcertmgr.c:124 ../pidgin/gtkcertmgr.c:242
msgid "X.509 certificate export failed"
msgstr "Wyeksportowanie certyfikatu X.509 się nie powiodło"
+#: ../finch/gntcertmgr.c:153 ../pidgin/gtkcertmgr.c:292
msgid "PEM X.509 Certificate Export"
msgstr "Eksport certyfikatu PEM X.509"
+#: ../finch/gntcertmgr.c:181
#, c-format
msgid "Certificate for %s"
msgstr "Certyfikat dla %s"
+#: ../finch/gntcertmgr.c:188
#, c-format
msgid ""
"Common name: %s\n"
"\n"
"SHA1 fingerprint:\n"
"%s"
-msgstr ""
-"Wspólna nazwa: %s\n"
-"\n"
-"Odcisk SHA1:\n"
-"%s"
+msgstr "Wspólna nazwa: %s\n\nOdcisk SHA1:\n%s"
+#: ../finch/gntcertmgr.c:191
msgid "SSL Host Certificate"
msgstr "Certyfikat SSL komputera"
+#: ../finch/gntcertmgr.c:226 ../pidgin/gtkcertmgr.c:376
#, c-format
msgid "Really delete certificate for %s?"
msgstr "Na pewno usunąć certyfikat dla %s?"
+#: ../finch/gntcertmgr.c:229 ../pidgin/gtkcertmgr.c:378
msgid "Confirm certificate delete"
msgstr "Potwierdzenie usunięcia certyfikatu"
+#: ../finch/gntcertmgr.c:286 ../pidgin/gtkcertmgr.c:610
msgid "Certificate Manager"
msgstr "Menadżer certyfikatów"
+#: ../finch/gntcertmgr.c:291 ../libpurple/protocols/silc/silc.c:1258
+#: ../pidgin/gtkcertmgr.c:439
msgid "Hostname"
msgstr "Nazwa komputera"
+#: ../finch/gntcertmgr.c:308 ../finch/gntnotify.c:467 ../pidgin/gtkconv.c:1705
+#: ../pidgin/gtkdebug.c:591
msgid "Info"
msgstr "Informacje"
#. Close button
+#: ../finch/gntcertmgr.c:317 ../finch/gntnotify.c:200 ../finch/gntplugin.c:271
+#: ../finch/gntplugin.c:475 ../finch/gntpounce.c:746
+#: ../finch/gntroomlist.c:273 ../finch/gntstatus.c:212 ../finch/gntxfer.c:248
+#: ../libpurple/protocols/gg/multilogon.c:246
+#: ../libpurple/protocols/msn/msn.c:799
+#: ../libpurple/protocols/mxit/splashscreen.c:211
+#: ../libpurple/protocols/mxit/splashscreen.c:215
+#: ../libpurple/protocols/silc/util.c:344 ../pidgin/gtkaccount.c:2681
+#: ../pidgin/gtkblist.c:6080 ../pidgin/gtkblist.c:6128
+#: ../pidgin/gtkrequest.c:343
msgid "Close"
msgstr "Zamknij"
+#: ../finch/gntconn.c:121
#, c-format
msgid "%s (%s)"
msgstr "%s (%s)"
+#: ../finch/gntconn.c:124
#, c-format
msgid "%s disconnected."
msgstr "%s został rozłączony."
+#: ../finch/gntconn.c:125
#, c-format
msgid ""
"%s\n"
"\n"
-"Finch will not attempt to reconnect the account until you correct the error "
-"and re-enable the account."
-msgstr ""
-"%s\n"
-"\n"
-"Do momentu naprawienia błędu i ponownego włączenia konta program Finch nie "
-"będzie podejmował prób połączenia."
+"Finch will not attempt to reconnect the account until you correct the error and re-enable the account."
+msgstr "%s\n\nDo momentu naprawienia błędu i ponownego włączenia konta program Finch nie będzie podejmował prób połączenia."
+#: ../finch/gntconn.c:133
msgid "Re-enable Account"
msgstr "Ponownie włącz konto"
+#: ../finch/gntconv.c:155
msgid "No such command."
msgstr "Nie ma takiego polecenia."
-msgid "Syntax Error: You typed the wrong number of arguments to that command."
+#: ../finch/gntconv.c:159 ../pidgin/gtkconv.c:574
+msgid ""
+"Syntax Error: You typed the wrong number of arguments to that command."
msgstr "Błąd składni: podano błędną liczbę parametrów dla tego polecenia."
+#: ../finch/gntconv.c:165 ../pidgin/gtkconv.c:581
msgid "Your command failed for an unknown reason."
msgstr "Polecenie nie powiodło się z nieznanego powodu."
+#: ../finch/gntconv.c:171 ../pidgin/gtkconv.c:589
msgid "That command only works in chats, not IMs."
-msgstr ""
-"Te polecenie działa tylko w konferencjach, nie można go używać w "
-"wiadomościach."
+msgstr "Te polecenie działa tylko w konferencjach, nie można go używać w wiadomościach."
+#: ../finch/gntconv.c:175 ../pidgin/gtkconv.c:593
msgid "That command only works in IMs, not chats."
-msgstr ""
-"Te polecenie jest dostępne tylko w wiadomościach, nie można go używać w "
-"konferencjach."
+msgstr "Te polecenie jest dostępne tylko w wiadomościach, nie można go używać w konferencjach."
+#: ../finch/gntconv.c:180 ../pidgin/gtkconv.c:599
msgid "That command doesn't work on this protocol."
msgstr "Te polecenie nie działa z tym protokołem."
+#: ../finch/gntconv.c:189
msgid "Message was not sent, because you are not signed on."
-msgstr ""
-"Wiadomość nie została wysłana, ponieważ użytkownik nie jest zalogowany."
+msgstr "Wiadomość nie została wysłana, ponieważ użytkownik nie jest zalogowany."
+#: ../finch/gntconv.c:259
#, c-format
-msgid "%s (%s -- %s)"
-msgstr "%s (%s -- %s)"
+msgid "%s (%s -- %s)%s%s%s%s"
+msgstr "%s (%s -- %s)%s%s%s%s"
+#: ../finch/gntconv.c:288
#, c-format
msgid "%s [%s]"
msgstr "%s [%s]"
+#: ../finch/gntconv.c:293 ../finch/gntconv.c:1074 ../pidgin/gtkconv.c:3856
#, c-format
msgid ""
"\n"
"%s is typing..."
-msgstr ""
-"\n"
-"%s pisze..."
+msgstr "\n%s pisze..."
+#: ../finch/gntconv.c:312
msgid "You have left this chat."
msgstr "Użytkownik opuścił tę konferencję."
+#: ../finch/gntconv.c:381
msgid ""
-"The account has disconnected and you are no longer in this chat. You will be "
-"automatically rejoined in the chat when the account reconnects."
-msgstr ""
-"Konto zostało rozłączone i użytkownik opuścił konferencję. Po ponownym "
-"połączeniu konta zostanie automatycznie podjęta próba dołączenia do tej "
-"konferencji."
+"The account has disconnected and you are no longer in this chat. You will be"
+" automatically rejoined in the chat when the account reconnects."
+msgstr "Konto zostało rozłączone i użytkownik opuścił konferencję. Po ponownym połączeniu konta zostanie automatycznie podjęta próba dołączenia do tej konferencji."
+#: ../finch/gntconv.c:507 ../pidgin/gtkconv.c:1418
msgid "Logging started. Future messages in this conversation will be logged."
-msgstr ""
-"Zapisywanie do dziennika zostało rozpoczęte. Od tej chwili wszystkie "
-"wiadomości w tej rozmowie zostaną zapisane."
+msgstr "Zapisywanie do archiwum rozmów zostało rozpoczęte. Od tej chwili wszystkie wiadomości w tej rozmowie zostaną zapisane."
+#: ../finch/gntconv.c:511 ../pidgin/gtkconv.c:1423
msgid ""
"Logging stopped. Future messages in this conversation will not be logged."
-msgstr ""
-"Zapisywanie do dziennika zostało zatrzymane. Od tej chwili żadne wiadomości "
-"w tej rozmowie nie będą zapisywane."
+msgstr "Zapisywanie do archiwum rozmów zostało zatrzymane. Od tej chwili żadne wiadomości w tej rozmowie nie będą zapisywane."
+#: ../finch/gntconv.c:598
msgid "Send To"
msgstr "Wyślij do"
+#: ../finch/gntconv.c:692
msgid "Conversation"
msgstr "Rozmowa"
+#: ../finch/gntconv.c:698
msgid "Clear Scrollback"
msgstr "Wyczyść okno"
+#: ../finch/gntconv.c:702 ../finch/gntprefs.c:182
msgid "Show Timestamps"
msgstr "Wyświetl datę i godzinę"
+#: ../finch/gntconv.c:720
msgid "Add Buddy Pounce..."
msgstr "Dodaj przechwytywanie zdarzeń..."
+#: ../finch/gntconv.c:734
msgid "Invite..."
msgstr "Zaproś..."
+#: ../finch/gntconv.c:745
msgid "Enable Logging"
-msgstr "Zapis do dziennika"
+msgstr "Zapis do archiwum rozmów"
+#: ../finch/gntconv.c:751
msgid "Enable Sounds"
msgstr "Włączenie dźwięków"
+#: ../finch/gntconv.c:774
msgid "You are not connected."
msgstr "Brak połączenia."
+#: ../finch/gntconv.c:1027
msgid "<AUTO-REPLY> "
msgstr "<ODPOWIEDŹ-AUTOMATYCZNA> "
+#: ../finch/gntconv.c:1136
#, c-format
msgid "List of %d user:\n"
msgid_plural "List of %d users:\n"
@@ -677,283 +1019,243 @@ msgstr[0] "Lista %d użytkownika:\n"
msgstr[1] "Lista %d użytkowników:\n"
msgstr[2] "Lista %d użytkowników:\n"
+#: ../finch/gntconv.c:1305
msgid "Supported debug options are: plugins version"
msgstr "Obsługiwane opcje debugowania: plugins version"
+#: ../finch/gntconv.c:1346 ../pidgin/gtkconv.c:476
msgid "No such command (in this context)."
msgstr "Nie ma takiego polecenia (w tym kontekście)."
+#: ../finch/gntconv.c:1349
msgid ""
"Use \"/help &lt;command&gt;\" for help on a specific command.\n"
"The following commands are available in this context:\n"
-msgstr ""
-"Należy użyć \"/help &lt;polecenie&gt;\", aby uzyskać pomoc dla konkretnego "
-"polecenia.\n"
-"W tym kontekście dostępne są następujące polecenia:\n"
+msgstr "Należy użyć \"/help &lt;polecenie&gt;\", aby uzyskać pomoc dla konkretnego polecenia.\nW tym kontekście dostępne są następujące polecenia:\n"
+#: ../finch/gntconv.c:1393
#, c-format
msgid ""
"%s is not a valid message class. See '/help msgcolor' for valid message "
"classes."
-msgstr ""
-"%s nie jest prawidłową klasą wiadomości. Proszę zobaczyć \"/help msgcolor\", "
-"aby uzyskać prawidłowe klasy wiadomości."
+msgstr "%s nie jest prawidłową klasą wiadomości. Proszę zobaczyć \"/help msgcolor\", aby uzyskać prawidłowe klasy wiadomości."
+#: ../finch/gntconv.c:1400 ../finch/gntconv.c:1407
#, c-format
msgid "%s is not a valid color. See '/help msgcolor' for valid colors."
-msgstr ""
-"%s nie jest prawidłowym kolorem. Proszę zobaczyć \"/help msgcolor\", aby "
-"uzyskać prawidłowe kolory."
+msgstr "%s nie jest prawidłowym kolorem. Proszę zobaczyć \"/help msgcolor\", aby uzyskać prawidłowe kolory."
+#: ../finch/gntconv.c:1463 ../pidgin/gtkconv.c:8824
msgid ""
"say &lt;message&gt;: Send a message normally as if you weren't using a "
"command."
-msgstr ""
-"say &lt;wiadomość&gt;: wysyła wiadomość w normalny sposób, taki sam jak bez "
-"używania poleceń."
+msgstr "say &lt;wiadomość&gt;: wysyła wiadomość w normalny sposób, taki sam jak bez używania poleceń."
+#: ../finch/gntconv.c:1466 ../pidgin/gtkconv.c:8827
msgid "me &lt;action&gt;: Send an IRC style action to a buddy or chat."
-msgstr ""
-"me &lt;akcja&gt;: wysyła czynność w stylu IRC do znajomego lub konferencji."
+msgstr "me &lt;akcja&gt;: wysyła czynność w stylu IRC do znajomego lub konferencji."
+#: ../finch/gntconv.c:1469 ../pidgin/gtkconv.c:8830
msgid ""
"debug &lt;option&gt;: Send various debug information to the current "
"conversation."
-msgstr ""
-"debug &lt;opcja&gt;: wysyła różne informacje debugowania do bieżącej rozmowy."
+msgstr "debug &lt;opcja&gt;: wysyła różne informacje debugowania do bieżącej rozmowy."
+#: ../finch/gntconv.c:1472 ../pidgin/gtkconv.c:8833
msgid "clear: Clears the conversation scrollback."
msgstr "clear: czyści okno rozmowy."
+#: ../finch/gntconv.c:1475 ../pidgin/gtkconv.c:8839
msgid "help &lt;command&gt;: Help on a specific command."
msgstr "help &lt;polecenie&gt;: wyświetla pomoc konkretnego polecenia."
+#: ../finch/gntconv.c:1478
msgid "users: Show the list of users in the chat."
msgstr "users: wyświetla listę użytkowników w konferencji."
+#: ../finch/gntconv.c:1483
msgid "plugins: Show the plugins window."
msgstr "plugins: wyświetla okno wtyczek."
+#: ../finch/gntconv.c:1486
msgid "buddylist: Show the buddylist."
msgstr "buddylist: wyświetla listę znajomych."
+#: ../finch/gntconv.c:1489
msgid "accounts: Show the accounts window."
msgstr "accounts: wyświetla konta."
+#: ../finch/gntconv.c:1492
msgid "debugwin: Show the debug window."
msgstr "debugwin: wyświetla okno debugowania."
+#: ../finch/gntconv.c:1495
msgid "prefs: Show the preference window."
msgstr "prefs: wyświetla okno preferencji."
+#: ../finch/gntconv.c:1498
msgid "statuses: Show the savedstatuses window."
msgstr "statuses: wyświetla okno zapisanych stanów."
+#: ../finch/gntconv.c:1503 ../finch/gntconv.c:1511
msgid ""
"msgcolor &lt;class&gt; &lt;foreground&gt; &lt;background&gt;: Set the color "
-"for different classes of messages in the conversation window.<br> &lt;"
-"class&gt;: receive, send, highlight, action, timestamp<br> &lt;foreground/"
-"background&gt;: black, red, green, blue, white, gray, darkgray, magenta, "
-"cyan, default<br><br>EXAMPLE:<br> msgcolor send cyan default"
-msgstr ""
-"msgcolor &lt;klasa&gt; &lt;czcionka&gt; &lt;tło&gt;: ustawia kolor różnych "
-"klas wiadomości w oknie rozmowy.<br> &lt;klasa&gt;: receive, send, "
-"highlight, action, timestamp<br> &lt;czcionka/tło&gt;: black, red, green, "
-"blue, white, gray, darkgray, magenta, cyan, default<br><br>PRZYKŁAD:<br> "
-"msgcolor send cyan default"
-
+"for different classes of messages in the conversation window.<br> "
+"&lt;class&gt;: receive, send, highlight, action, timestamp<br> "
+"&lt;foreground/background&gt;: black, red, green, blue, white, gray, "
+"darkgray, magenta, cyan, default<br><br>EXAMPLE:<br> msgcolor send cyan "
+"default"
+msgstr "msgcolor &lt;klasa&gt; &lt;czcionka&gt; &lt;tło&gt;: ustawia kolor różnych klas wiadomości w oknie rozmowy.<br> &lt;klasa&gt;: receive, send, highlight, action, timestamp<br> &lt;czcionka/tło&gt;: black, red, green, blue, white, gray, darkgray, magenta, cyan, default<br><br>PRZYKŁAD:<br> msgcolor send cyan default"
+
+#: ../finch/gntdebug.c:272 ../pidgin/gtkconv.c:1054 ../pidgin/gtkdebug.c:110
+#: ../pidgin/gtkutils.c:3347 ../pidgin/gtkxfer.c:521
msgid "Unable to open file."
msgstr "Nie można otworzyć pliku."
+#: ../finch/gntdebug.c:312 ../finch/gntui.c:107 ../pidgin/gtkdebug.c:457
msgid "Debug Window"
msgstr "Okno debugowania"
-#. XXX: Setting the GROW_Y for the following widgets don't make sense. But right now
-#. * it's necessary to make the width of the debug window resizable ... like I said,
+#. XXX: Setting the GROW_Y for the following widgets don't make sense. But
+#. right now
+#. * it's necessary to make the width of the debug window resizable ... like I
+#. said,
#. * it doesn't make sense. The bug is likely in the packing in gntbox.c.
-#.
+#: ../finch/gntdebug.c:333 ../pidgin/gtkdebug.c:499
msgid "Clear"
msgstr "Wyczyść"
+#: ../finch/gntdebug.c:344
msgid "Filter:"
msgstr "Filtr:"
+#: ../finch/gntdebug.c:350 ../pidgin/gtkdebug.c:509
msgid "Pause"
msgstr "Wstrzymaj"
-#, c-format
-msgid "File Transfers - %d%% of %d file"
-msgid_plural "File Transfers - %d%% of %d files"
-msgstr[0] "Przesyłanie pliku - %d%% z %d pliku"
-msgstr[1] "Przesyłanie plików - %d%% z %d plików"
-msgstr[2] "Przesyłanie plików - %d%% z %d plików"
-
-#. Create the window.
-msgid "File Transfers"
-msgstr "Przesyłanie plików"
-
-msgid "Progress"
-msgstr "Postęp"
-
-msgid "Filename"
-msgstr "Nazwa pliku"
-
-msgid "Size"
-msgstr "Rozmiar"
-
-msgid "Speed"
-msgstr "Prędkość"
-
-msgid "Remaining"
-msgstr "Pozostało"
-
-#. XXX: Use of ggp_str_to_uin() is an ugly hack!
-#. presence
-msgid "Status"
-msgstr "Stan"
-
-msgid "Close this window when all transfers finish"
-msgstr "Zamknięcie tego okna po ukończeniu wszystkich przesyłań"
-
-msgid "Clear finished transfers"
-msgstr "Wyczyść ukończone przesyłania"
-
-msgid "Stop"
-msgstr "Zatrzymaj"
-
-msgid "Waiting for transfer to begin"
-msgstr "Oczekiwanie na rozpoczęcie przesyłu"
-
-msgid "Cancelled"
-msgstr "Anulowano"
-
-msgid "Failed"
-msgstr "Nie powiodło się"
-
-#, c-format
-msgid "%.2f KiB/s"
-msgstr "%.2f KB/s"
-
-msgid "Sent"
-msgstr "Wysłano"
-
-msgid "Received"
-msgstr "Odebrano"
-
-msgid "Finished"
-msgstr "Ukończono"
-
-#, c-format
-msgid "The file was saved as %s."
-msgstr "Plik został zapisany jako %s."
-
-msgid "Sending"
-msgstr "Wysyłanie"
-
-msgid "Receiving"
-msgstr "Odbieranie"
-
+#: ../finch/gntlog.c:188
#, c-format
msgid "Conversation in %s on %s"
msgstr "Rozmowa w %s dnia %s"
+#: ../finch/gntlog.c:191
#, c-format
msgid "Conversation with %s on %s"
msgstr "Rozmowa z %s dnia %s"
+#: ../finch/gntlog.c:234 ../pidgin/gtklog.c:503
msgid "%B %Y"
msgstr "%B %Y"
+#: ../finch/gntlog.c:274 ../pidgin/gtklog.c:549
msgid ""
"System events will only be logged if the \"Log all status changes to system "
"log\" preference is enabled."
-msgstr ""
-"Zdarzenia systemowe będą zapisywane wyłącznie, jeśli opcja \"Zapis zmian "
-"stanu do dziennika systemowego\" jest włączona."
+msgstr "Zdarzenia systemowe będą zapisywane wyłącznie, jeśli opcja \"Zapis zmian stanu do dziennika systemowego\" jest włączona."
+#: ../finch/gntlog.c:278 ../pidgin/gtklog.c:553
msgid ""
"Instant messages will only be logged if the \"Log all instant messages\" "
"preference is enabled."
-msgstr ""
-"Wiadomości będą zapisywane wyłącznie, jeśli opcja \"Zapis wszystkich "
-"wiadomości\" jest włączona."
+msgstr "Wiadomości będą zapisywane wyłącznie, jeśli opcja \"Zapis wszystkich wiadomości\" jest włączona."
-msgid ""
-"Chats will only be logged if the \"Log all chats\" preference is enabled."
-msgstr ""
-"Konferencje będą zapisywane wyłącznie, jeśli opcja \"Zapis wszystkich "
-"konferencji\" jest włączona."
+#: ../finch/gntlog.c:281 ../pidgin/gtklog.c:556
+msgid "Chats will only be logged if the \"Log all chats\" preference is enabled."
+msgstr "Konferencje będą zapisywane wyłącznie, jeśli opcja \"Zapis wszystkich konferencji\" jest włączona."
+#: ../finch/gntlog.c:287 ../pidgin/gtklog.c:565
msgid "No logs were found"
-msgstr "Nie odnaleziono żadnych dzienników rozmów"
+msgstr "Archiwum rozmów jest puste"
+#: ../finch/gntlog.c:334 ../pidgin/gtklog.c:643
msgid "Total log size:"
-msgstr "Całkowity rozmiar dziennika rozmów:"
+msgstr "Całkowity rozmiar archiwum:"
#. Search box *********
+#: ../finch/gntlog.c:342
msgid "Scroll/Search: "
msgstr "Przewiń/wyszukaj: "
+#: ../finch/gntlog.c:400 ../pidgin/gtklog.c:714
#, c-format
msgid "Conversations in %s"
msgstr "Rozmowy w %s"
+#: ../finch/gntlog.c:408 ../finch/gntlog.c:485 ../pidgin/gtklog.c:722
+#: ../pidgin/gtklog.c:802
#, c-format
msgid "Conversations with %s"
msgstr "Rozmowy z %s"
+#: ../finch/gntlog.c:410
msgid "All Conversations"
msgstr "Wszystkie rozmowy"
+#: ../finch/gntlog.c:510 ../pidgin/gtklog.c:827
msgid "System Log"
msgstr "Dziennik systemowy"
+#: ../finch/gntmedia.c:154 ../pidgin/gtkmedia.c:341
msgid "Calling..."
msgstr "Dzwonienie..."
+#: ../finch/gntmedia.c:155
msgid "Hangup"
msgstr "Rozłącz się"
-#. Number of actions
+#: ../finch/gntmedia.c:156 ../libpurple/certificate.c:744
+#: ../libpurple/certificate.c:1367
msgid "Accept"
msgstr "Odbierz"
+#: ../finch/gntmedia.c:157 ../libpurple/certificate.c:745
+#: ../libpurple/certificate.c:1368
msgid "Reject"
msgstr "Odrzuć"
+#: ../finch/gntmedia.c:185 ../pidgin/gtkmedia.c:971
msgid "Call in progress."
msgstr "Trwa rozmowa."
+#: ../finch/gntmedia.c:237 ../pidgin/gtkmedia.c:950
msgid "The call has been terminated."
msgstr "Rozmowa została przerwana."
+#: ../finch/gntmedia.c:265 ../pidgin/gtkmedia.c:647
#, c-format
msgid "%s wishes to start an audio session with you."
msgstr "Użytkownik %s chce rozpocząć sesję dźwiękową."
+#: ../finch/gntmedia.c:269
#, c-format
msgid "%s is trying to start an unsupported media session type with you."
-msgstr ""
-"Użytkownik %s próbuje rozpocząć nieobsługiwany typ sesji multimedialnej."
+msgstr "Użytkownik %s próbuje rozpocząć nieobsługiwany typ sesji multimedialnej."
+#: ../finch/gntmedia.c:283 ../pidgin/gtkmedia.c:966
msgid "You have rejected the call."
msgstr "Odrzucono rozmowę."
+#: ../finch/gntmedia.c:476
msgid "call: Make an audio call."
msgstr "call: rozpoczyna rozmowę dźwiękową."
+#: ../finch/gntnotify.c:183
msgid "Emails"
msgstr "Wiadomości e-mail"
+#: ../finch/gntnotify.c:189 ../finch/gntnotify.c:253
msgid "You have mail!"
msgstr "Nowa wiadomość."
+#: ../finch/gntnotify.c:192 ../pidgin/gtknotify.c:774
msgid "Sender"
msgstr "Nadawca"
+#: ../finch/gntnotify.c:192 ../pidgin/gtknotify.c:781
msgid "Subject"
msgstr "Temat"
+#: ../finch/gntnotify.c:223
#, c-format
msgid "%s (%s) has %d new message."
msgid_plural "%s (%s) has %d new messages."
@@ -961,43 +1263,58 @@ msgstr[0] "%s (%s) ma %d nową wiadomość."
msgstr[1] "%s (%s) ma %d nowe wiadomości."
msgstr[2] "%s (%s) ma %d nowych wiadomości."
+#: ../finch/gntnotify.c:253 ../pidgin/gtknotify.c:1624
msgid "New Mail"
msgstr "Nowa wiadomość e-mail"
+#: ../finch/gntnotify.c:359 ../pidgin/gtknotify.c:1206
#, c-format
msgid "Info for %s"
msgstr "Informacje o %s"
+#: ../finch/gntnotify.c:360 ../pidgin/gtknotify.c:1207
msgid "Buddy Information"
msgstr "Informacje o znajomym"
+#: ../finch/gntnotify.c:461 ../libpurple/protocols/mxit/login.c:709
+#: ../libpurple/protocols/mxit/splashscreen.c:215
msgid "Continue"
msgstr "Kontynuuj"
+#: ../finch/gntnotify.c:470 ../pidgin/gtkconv.c:1655
+#: ../pidgin/gtknotify.c:1668
msgid "IM"
msgstr "Wiadomość"
+#: ../finch/gntnotify.c:476 ../libpurple/conversationtypes.c:1278
+#: ../libpurple/protocols/sametime/sametime.c:3510
msgid "Invite"
msgstr "Zaproś"
+#: ../finch/gntnotify.c:479
msgid "(none)"
msgstr "(brak)"
#. XXX: The following expects that finch_notify_message gets called. This
#. * may not always happen, e.g. when another plugin sets its own
#. * notify_message. So tread carefully.
+#: ../finch/gntnotify.c:508 ../finch/plugins/gnttinyurl.c:419
msgid "URI"
msgstr "Adres URI"
+#: ../finch/gntplugin.c:139 ../finch/gntplugin.c:146
msgid "ERROR"
msgstr "BŁĄD"
+#: ../finch/gntplugin.c:139
msgid "loading plugin failed"
msgstr "wczytanie wtyczki się nie powiodło"
+#: ../finch/gntplugin.c:146
msgid "unloading plugin failed"
msgstr "wyłączanie wtyczki się nie powiodło"
+#: ../finch/gntplugin.c:189
#, c-format
msgid ""
"Name: %s\n"
@@ -1006,1412 +1323,1958 @@ msgid ""
"Author: %s\n"
"Website: %s\n"
"Filename: %s\n"
-msgstr ""
-"Nazwa: %s\n"
-"Wersja: %s\n"
-"Opis: %s\n"
-"Autor: %s\n"
-"Strona WWW: %s\n"
-"Nazwa pliku: %s\n"
+msgstr "Nazwa: %s\nWersja: %s\nOpis: %s\nAutor: %s\nStrona WWW: %s\nNazwa pliku: %s\n"
+#: ../finch/gntplugin.c:245
msgid "Plugin need to be loaded before you can configure it."
msgstr "Wtyczka musi być wczytana przed jej skonfigurowaniem."
+#: ../finch/gntplugin.c:305
msgid "No configuration options for this plugin."
msgstr "Brak opcji konfiguracji dla tej wtyczki."
+#: ../finch/gntplugin.c:328
msgid "Error loading plugin"
msgstr "Błąd podczas wczytywania wtyczki"
+#: ../finch/gntplugin.c:329
msgid "The selected file is not a valid plugin."
msgstr "Wybrany plik nie jest prawidłową wtyczką."
+#: ../finch/gntplugin.c:330
msgid ""
"Please open the debug window and try again to see the exact error message."
-msgstr ""
-"Proszę otworzyć okno debugowania i spróbować ponownie, aby zobaczyć dokładny "
-"komunikat błędu."
+msgstr "Proszę otworzyć okno debugowania i spróbować ponownie, aby zobaczyć dokładny komunikat błędu."
+#: ../finch/gntplugin.c:393
msgid "Select plugin to install"
msgstr "Proszę wybrać wtyczkę do zainstalowania"
+#: ../finch/gntplugin.c:419
msgid "You can (un)load plugins from the following list."
msgstr "Można wczytać/wyłączyć wtyczki z poniższej listy."
+#: ../finch/gntplugin.c:470
msgid "Install Plugin..."
msgstr "Zainstaluj wtyczkę..."
+#: ../finch/gntplugin.c:480
msgid "Configure Plugin"
msgstr "Skonfiguruj wtyczkę"
-#. copy the preferences to tmp values...
-#. * I liked "take affect immediately" Oh well :-(
-#. (that should have been "effect," right?)
-#. Back to instant-apply! I win! BU-HAHAHA!
-#. Create the window
+#: ../finch/gntplugin.c:586 ../finch/gntplugin.c:593 ../finch/gntprefs.c:266
+#: ../finch/gntui.c:112 ../pidgin/gtkprefs.c:4200 ../pidgin/gtkprefs.c:4202
msgid "Preferences"
msgstr "Preferencje"
+#: ../finch/gntpounce.c:190 ../pidgin/gtkpounce.c:262
msgid "Please enter a buddy to pounce."
msgstr "Proszę podać znajomego do przechwytywania zdarzeń."
+#: ../finch/gntpounce.c:335
msgid "New Buddy Pounce"
msgstr "Nowe przechwytywanie zdarzeń"
+#: ../finch/gntpounce.c:335
msgid "Edit Buddy Pounce"
msgstr "Zmodyfikuj przechwytywane zdarzenie"
+#: ../finch/gntpounce.c:340
msgid "Pounce Who"
msgstr "Źródło zdarzeń"
#. Account:
+#: ../finch/gntpounce.c:343 ../finch/gntstatus.c:452
msgid "Account:"
msgstr "Konto:"
+#: ../finch/gntpounce.c:365
msgid "Buddy name:"
msgstr "Nazwa znajomego:"
#. Create the "Pounce When Buddy..." frame.
+#: ../finch/gntpounce.c:383 ../pidgin/gtkpounce.c:616
msgid "Pounce When Buddy..."
msgstr "Przechwytywane zdarzenia, kiedy znajomy..."
+#: ../finch/gntpounce.c:385
msgid "Signs on"
msgstr "Zaloguje się"
+#: ../finch/gntpounce.c:386
msgid "Signs off"
msgstr "Wylogowuje się"
+#: ../finch/gntpounce.c:387
msgid "Goes away"
msgstr "Zaraz wraca"
+#: ../finch/gntpounce.c:388
msgid "Returns from away"
msgstr "Wraca"
+#: ../finch/gntpounce.c:389
msgid "Becomes idle"
msgstr "Przechodzi w stan bezczynności"
+#: ../finch/gntpounce.c:390
msgid "Is no longer idle"
msgstr "Powraca ze stanu bezczynności"
+#: ../finch/gntpounce.c:391
msgid "Starts typing"
msgstr "Rozpoczyna pisanie"
+#: ../finch/gntpounce.c:392
msgid "Pauses while typing"
msgstr "Wstrzymuje pisanie"
+#: ../finch/gntpounce.c:393
msgid "Stops typing"
msgstr "Kończy pisanie"
+#: ../finch/gntpounce.c:394
msgid "Sends a message"
msgstr "Wysyła wiadomość"
#. Create the "Action" frame.
+#: ../finch/gntpounce.c:423 ../pidgin/gtkpounce.c:677
msgid "Action"
msgstr "Czynność"
+#: ../finch/gntpounce.c:425
msgid "Open an IM window"
msgstr "Otwarcie okna rozmowy"
+#: ../finch/gntpounce.c:426
msgid "Pop up a notification"
msgstr "Wyświetlenie powiadomienia"
+#: ../finch/gntpounce.c:427
msgid "Send a message"
msgstr "Wysłanie wiadomości"
+#: ../finch/gntpounce.c:428
msgid "Execute a command"
msgstr "Wykonanie polecenia"
+#: ../finch/gntpounce.c:429
msgid "Play a sound"
msgstr "Odtworzenie dźwięku"
+#: ../finch/gntpounce.c:457
msgid "Pounce only when my status is not Available"
msgstr "Przechwytywanie tylko, gdy stan nie jest dostępny"
+#: ../finch/gntpounce.c:459 ../pidgin/gtkpounce.c:1303
msgid "Recurring"
msgstr "Powtarzanie"
+#: ../finch/gntpounce.c:627
msgid "Cannot create pounce"
msgstr "Nie można utworzyć przechwytywania"
+#: ../finch/gntpounce.c:628
msgid "You do not have any accounts."
msgstr "Nie ma żadnych kont."
+#: ../finch/gntpounce.c:629
msgid "You must create an account first before you can create a pounce."
msgstr "Należy najpierw utworzyć konto, aby utworzyć przechwytywanie."
+#: ../finch/gntpounce.c:671 ../pidgin/gtkpounce.c:1135
#, c-format
msgid "Are you sure you want to delete the pounce on %s for %s?"
msgstr "Na pewno usunąć przechwytywane zdarzenie od %s z konta %s?"
+#: ../finch/gntpounce.c:704 ../finch/gntui.c:105 ../pidgin/gtkpounce.c:1347
+#: ../pidgin/gtkpounce.c:1349
msgid "Buddy Pounces"
msgstr "Przechwytywanie zdarzeń"
+#: ../finch/gntpounce.c:811
#, c-format
msgid "%s has started typing to you (%s)"
msgstr "Użytkownik %s rozpoczął pisanie (%s)"
+#: ../finch/gntpounce.c:812
#, c-format
msgid "%s has paused while typing to you (%s)"
msgstr "Użytkownik %s przestał pisać (%s)"
+#: ../finch/gntpounce.c:813
#, c-format
msgid "%s has signed on (%s)"
msgstr "Użytkownik %s zalogował się (%s)"
+#: ../finch/gntpounce.c:814
#, c-format
msgid "%s has returned from being idle (%s)"
msgstr "Użytkownik %s powrócił ze stanu bezczynności (%s)"
+#: ../finch/gntpounce.c:815
#, c-format
msgid "%s has returned from being away (%s)"
msgstr "Użytkownik %s wrócił (%s)"
+#: ../finch/gntpounce.c:816
#, c-format
msgid "%s has stopped typing to you (%s)"
msgstr "Użytkownik %s przestał pisać (%s)"
+#: ../finch/gntpounce.c:817
#, c-format
msgid "%s has signed off (%s)"
msgstr "Użytkownik %s rozłączył się (%s)"
+#: ../finch/gntpounce.c:818
#, c-format
msgid "%s has become idle (%s)"
msgstr "Użytkownik %s przeszedł w stan bezczynności (%s)"
+#: ../finch/gntpounce.c:819
#, c-format
msgid "%s has gone away. (%s)"
msgstr "Użytkownik %s zaraz wraca. (%s)"
+#: ../finch/gntpounce.c:820
#, c-format
msgid "%s has sent you a message. (%s)"
msgstr "Użytkownik %s wysłał wiadomość. (%s)"
+#: ../finch/gntpounce.c:839
msgid "Unknown pounce event. Please report this!"
msgstr "Przechwycono nieznane zdarzenie. Proszę to zgłosić."
+#: ../finch/gntprefs.c:81
msgid "Based on keyboard use"
msgstr "W oparciu o użycie klawiatury"
+#: ../finch/gntprefs.c:83 ../pidgin/gtkprefs.c:3399
msgid "From last sent message"
msgstr "Od ostatniej wysłanej wiadomości"
+#: ../finch/gntprefs.c:85 ../pidgin/gtkprefs.c:1743 ../pidgin/gtkprefs.c:1751
+#: ../pidgin/gtkprefs.c:3398 ../pidgin/gtkprefs.c:3434
+#: ../pidgin/plugins/win32/winprefs/winprefs.c:307
msgid "Never"
msgstr "Nigdy"
+#: ../finch/gntprefs.c:175
msgid "Show Idle Time"
msgstr "Wyświetlanie czasu bezczynności"
+#: ../finch/gntprefs.c:176
msgid "Show Offline Buddies"
msgstr "Wyświetlanie znajomych w trybie offline"
+#: ../finch/gntprefs.c:183
msgid "Notify buddies when you are typing"
msgstr "Powiadamianie znajomych o pisaniu"
+#: ../finch/gntprefs.c:189 ../finch/plugins/gnthistory.c:155
msgid "Log format"
-msgstr "Format dziennika"
+msgstr "Format archiwum"
+#: ../finch/gntprefs.c:190 ../finch/plugins/gnthistory.c:145
msgid "Log IMs"
msgstr "Zapis wiadomości"
+#: ../finch/gntprefs.c:191 ../finch/plugins/gnthistory.c:146
msgid "Log chats"
msgstr "Zapis konferencji"
+#: ../finch/gntprefs.c:192
msgid "Log status change events"
msgstr "Zapis zmian stanu"
+#: ../finch/gntprefs.c:198
+msgid "Active keyring"
+msgstr "Aktywna baza kluczy"
+
+#: ../finch/gntprefs.c:204
msgid "Report Idle time"
msgstr "Zgłaszanie czasu bezczynności"
+#: ../finch/gntprefs.c:205
msgid "Change status when idle"
msgstr "Zmiana stanu podczas bezczynności"
+#: ../finch/gntprefs.c:206
msgid "Minutes before changing status"
msgstr "Minuty przed zmianą stanu"
+#: ../finch/gntprefs.c:207
msgid "Change status to"
msgstr "Zmiana stanu na"
+#: ../finch/gntprefs.c:260 ../pidgin/gtkprefs.c:1854 ../pidgin/gtkprefs.c:4166
msgid "Conversations"
msgstr "Rozmowy"
+#: ../finch/gntprefs.c:261 ../pidgin/gtkprefs.c:2835 ../pidgin/gtkprefs.c:2861
+#: ../pidgin/gtkprefs.c:2914
+msgid "Keyring"
+msgstr "Baza kluczy"
+
+#: ../finch/gntprefs.c:262 ../finch/plugins/gnthistory.c:153
+#: ../pidgin/gtkprefs.c:2656 ../pidgin/gtkprefs.c:4167
msgid "Logging"
-msgstr "Dziennik rozmów"
+msgstr "Archiwum rozmów"
+
+#: ../finch/gntprefs.c:296 ../finch/gntprefs.c:303 ../finch/gntui.c:113
+msgid "Keyring settings"
+msgstr "Ustawienia bazy kluczy"
+
+#: ../finch/gntprefs.c:297
+msgid "Selected keyring doesn't allow any configuration"
+msgstr "Wybrana baza kluczy nie zezwala na żadną konfigurację"
-msgid "You must fill all the required fields."
-msgstr "Należy wypełnić wszystkie wymagane pola."
+#: ../finch/gntrequest.c:389
+msgid "You must properly fill all the required fields."
+msgstr "Należy poprawnie wypełnić wszystkie wymagane pola."
+#: ../finch/gntrequest.c:390
msgid "The required fields are underlined."
msgstr "Wymagane pola są podkreślone."
+#: ../finch/gntrequest.c:699
msgid "Not implemented yet."
msgstr "Jeszcze nie zaimplementowano."
+#: ../finch/gntrequest.c:729
+msgid "Help"
+msgstr "Pomoc"
+
+#: ../finch/gntrequest.c:825 ../pidgin/gtkrequest.c:2519
msgid "Save File..."
msgstr "Zapisz plik..."
+#: ../finch/gntrequest.c:825 ../pidgin/gtkrequest.c:2520
msgid "Open File..."
msgstr "Otwórz plik..."
+#: ../finch/gntrequest.c:842
msgid "Choose Location..."
msgstr "Wybierz położenie..."
+#: ../finch/gntroomlist.c:205
msgid "Hit 'Enter' to find more rooms of this category."
msgstr "Naciśnięcie klawisza \"Enter\" wyszuka więcej pokoi w tej kategorii."
+#: ../finch/gntroomlist.c:270 ../finch/gntxfer.c:243
+msgid "Stop"
+msgstr "Zatrzymaj"
+
+#: ../finch/gntroomlist.c:271
msgid "Get"
msgstr "Uzyskaj"
-#. Create the window.
+#: ../finch/gntroomlist.c:283 ../finch/gntui.c:110 ../pidgin/gtkblist.c:7516
+#: ../pidgin/gtkroomlist.c:571 ../pidgin/gtkroomlist.c:573
msgid "Room List"
msgstr "Lista pokoi"
+#: ../finch/gntsound.c:92 ../pidgin/gtksound.c:59
msgid "Buddy logs in"
msgstr "Znajomy loguje się"
+#: ../finch/gntsound.c:93 ../pidgin/gtksound.c:60
msgid "Buddy logs out"
msgstr "Znajomy wylogowuje się"
+#: ../finch/gntsound.c:94 ../pidgin/gtksound.c:61
msgid "Message received"
msgstr "Odebrano wiadomość"
+#: ../finch/gntsound.c:95 ../pidgin/gtksound.c:62
msgid "Message received begins conversation"
msgstr "Odebrana wiadomość rozpoczyna rozmowę"
+#: ../finch/gntsound.c:96 ../pidgin/gtksound.c:63
msgid "Message sent"
msgstr "Wysłano wiadomość"
+#: ../finch/gntsound.c:97 ../pidgin/gtksound.c:64
msgid "Person enters chat"
msgstr "Osoba wchodzi na konferencję"
+#: ../finch/gntsound.c:98 ../pidgin/gtksound.c:65
msgid "Person leaves chat"
msgstr "Osoba opuszcza konferencję"
+#: ../finch/gntsound.c:99 ../pidgin/gtksound.c:66
msgid "You talk in chat"
msgstr "Własna wypowiedź na konferencji"
+#: ../finch/gntsound.c:100 ../pidgin/gtksound.c:67
msgid "Others talk in chat"
msgstr "Wypowiedź innych na konferencji"
+#: ../finch/gntsound.c:102 ../pidgin/gtksound.c:70
msgid "Someone says your username in chat"
msgstr "Ktoś wymawia nazwę użytkownika na konferencji"
+#: ../finch/gntsound.c:103 ../pidgin/gtksound.c:71
msgid "Attention received"
msgstr "odebrano uwagę"
+#: ../finch/gntsound.c:374 ../pidgin/gtksound.c:320
msgid "GStreamer Failure"
msgstr "Niepowodzenie biblioteki GStreamer"
+#: ../finch/gntsound.c:375 ../pidgin/gtksound.c:321
msgid "GStreamer failed to initialize."
msgstr "Zainicjowanie biblioteki GStreamer się nie powiodło."
+#: ../finch/gntsound.c:733 ../finch/gntsound.c:819 ../pidgin/gtkpounce.c:174
+#: ../pidgin/gtkpounce.c:185 ../pidgin/gtkpounce.c:314
+#: ../pidgin/gtkpounce.c:701 ../pidgin/gtkpounce.c:963
+#: ../pidgin/gtkprefs.c:1223 ../pidgin/gtkprefs.c:3038
+#: ../pidgin/gtkprefs.c:3135 ../pidgin/gtkprefs.c:3341
msgid "(default)"
msgstr "(domyślnie)"
+#: ../finch/gntsound.c:746
msgid "Select Sound File ..."
msgstr "Wybierz plik dźwiękowy..."
+#: ../finch/gntsound.c:921
msgid "Sound Preferences"
msgstr "Preferencje dźwięku"
+#: ../finch/gntsound.c:932
msgid "Profiles"
msgstr "Profile"
+#: ../finch/gntsound.c:971 ../pidgin/gtkprefs.c:3196
msgid "Automatic"
msgstr "Automatycznie"
+#: ../finch/gntsound.c:974
msgid "Console Beep"
msgstr "Sygnał konsoli"
+#: ../finch/gntsound.c:975 ../pidgin/gtkprefs.c:3210
msgid "Command"
msgstr "Polecenie"
+#: ../finch/gntsound.c:976
msgid "No Sound"
msgstr "Bez dźwięku"
+#: ../finch/gntsound.c:978
msgid "Sound Method"
msgstr "Metoda dźwięku"
+#: ../finch/gntsound.c:983
msgid "Method: "
msgstr "Metoda: "
+#: ../finch/gntsound.c:990
#, c-format
msgid ""
"Sound Command\n"
"(%s for filename)"
-msgstr ""
-"Polecenie odtworzenia dźwięku\n"
-"(%s dla nazwy pliku)"
+msgstr "Polecenie odtworzenia dźwięku\n(%s dla nazwy pliku)"
#. Sound options
+#: ../finch/gntsound.c:998 ../pidgin/gtkprefs.c:3189
msgid "Sound Options"
msgstr "Opcje dźwięku"
+#: ../finch/gntsound.c:999
msgid "Sounds when conversation has focus"
msgstr "Dźwięki w aktywnym oknie rozmowy"
+#: ../finch/gntsound.c:1007 ../pidgin/gtkprefs.c:1741
+#: ../pidgin/gtkprefs.c:1753 ../pidgin/gtkprefs.c:3241
+#: ../pidgin/plugins/win32/winprefs/winprefs.c:308
msgid "Always"
msgstr "Zawsze"
+#: ../finch/gntsound.c:1008 ../pidgin/gtkprefs.c:3239
msgid "Only when available"
msgstr "Tylko podczas dostępności"
+#: ../finch/gntsound.c:1009 ../pidgin/gtkprefs.c:3240
msgid "Only when not available"
msgstr "Tylko podczas nieobecności"
+#: ../finch/gntsound.c:1016
msgid "Volume(0-100):"
msgstr "Głośność (0-100):"
#. Sound events
+#: ../finch/gntsound.c:1035 ../pidgin/gtkprefs.c:3267
msgid "Sound Events"
msgstr "Zdarzenia dźwiękowe"
+#: ../finch/gntsound.c:1037 ../pidgin/gtknotify.c:1705
+#: ../pidgin/gtkprefs.c:3324
msgid "Event"
msgstr "Zdarzenie"
+#: ../finch/gntsound.c:1037
msgid "File"
msgstr "Plik"
+#: ../finch/gntsound.c:1056
msgid "Test"
msgstr "Przetestuj"
+#: ../finch/gntsound.c:1059 ../pidgin/gtkpounce.c:705
msgid "Reset"
msgstr "Przywróć"
+#: ../finch/gntsound.c:1062
msgid "Choose..."
msgstr "Wybierz..."
+#: ../finch/gntstatus.c:135
#, c-format
msgid "Are you sure you want to delete \"%s\""
msgstr "Czy na pewno usunąć \"%s\""
+#: ../finch/gntstatus.c:138
msgid "Delete Status"
msgstr "Usuń stan"
+#: ../finch/gntstatus.c:172 ../pidgin/gtksavedstatuses.c:560
msgid "Saved Statuses"
msgstr "Zapisane stany"
#. title
+#: ../finch/gntstatus.c:179 ../finch/gntstatus.c:535
+#: ../libpurple/protocols/mxit/actions.c:287 ../pidgin/gtksavedstatuses.c:463
msgid "Title"
msgstr "Tytuł"
+#: ../finch/gntstatus.c:179 ../pidgin/gtksavedstatuses.c:476
msgid "Type"
msgstr "Typ"
-#. Statuses are almost all the same. Define a macro to reduce code repetition.
-#. PurpleStatusPrimitive
-#. id - use default
-#. name - use default
-#. saveable
-#. user_settable
-#. not independent
-#. Attributes - each status can have a message.
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
+#: ../finch/gntstatus.c:179 ../finch/gntstatus.c:560 ../finch/gntstatus.c:572
+#: ../libpurple/conversationtypes.c:1271
+#: ../libpurple/protocols/bonjour/bonjour.c:294
+#: ../libpurple/protocols/bonjour/bonjour.c:301
+#: ../libpurple/protocols/bonjour/bonjour.c:385
+#: ../libpurple/protocols/gg/gg.c:653
+#: ../libpurple/protocols/gg/pubdir-prpl.c:409
+#: ../libpurple/protocols/gg/status.c:90 ../libpurple/protocols/gg/status.c:95
+#: ../libpurple/protocols/gg/status.c:100
+#: ../libpurple/protocols/gg/status.c:105
+#: ../libpurple/protocols/gg/status.c:110
+#: ../libpurple/protocols/gg/status.c:115 ../libpurple/protocols/irc/irc.c:279
+#: ../libpurple/protocols/jabber/jabber.c:2363
+#: ../libpurple/protocols/jabber/jabber.c:2387
+#: ../libpurple/protocols/jabber/jabber.c:2403
+#: ../libpurple/protocols/jabber/jabber.c:2419
+#: ../libpurple/protocols/jabber/jabber.c:2433
+#: ../libpurple/protocols/jabber/jabber.c:2448
+#: ../libpurple/protocols/msn/msn.c:1159 ../libpurple/protocols/msn/msn.c:1165
+#: ../libpurple/protocols/msn/msn.c:1171 ../libpurple/protocols/msn/msn.c:1177
+#: ../libpurple/protocols/msn/msn.c:1182 ../libpurple/protocols/msn/msn.c:1187
+#: ../libpurple/protocols/mxit/roster.c:78
+#: ../libpurple/protocols/novell/novell.c:2896
+#: ../libpurple/protocols/novell/novell.c:3002
+#: ../libpurple/protocols/novell/novell.c:3008
+#: ../libpurple/protocols/novell/novell.c:3014
+#: ../libpurple/protocols/oscar/oscar.c:4584
+#: ../libpurple/protocols/oscar/oscar.c:4593
+#: ../libpurple/protocols/oscar/oscar.c:4601
+#: ../libpurple/protocols/oscar/oscar.c:4609
+#: ../libpurple/protocols/oscar/oscar.c:4617
+#: ../libpurple/protocols/oscar/oscar.c:4625
+#: ../libpurple/protocols/oscar/oscar.c:4634
+#: ../libpurple/protocols/oscar/oscar.c:4642
+#: ../libpurple/protocols/oscar/oscar.c:4649
+#: ../libpurple/protocols/oscar/oscar.c:4661
+#: ../libpurple/protocols/oscar/oscar.c:4668
+#: ../libpurple/protocols/oscar/oscar.c:4675
+#: ../libpurple/protocols/sametime/sametime.c:3323
+#: ../libpurple/protocols/sametime/sametime.c:3329
+#: ../libpurple/protocols/sametime/sametime.c:3335
+#: ../libpurple/protocols/sametime/sametime.c:3414
+#: ../libpurple/protocols/silc/buddy.c:1578
+#: ../libpurple/protocols/simple/simple.c:252
+#: ../libpurple/protocols/yahoo/libymsg.c:4924
+#: ../libpurple/protocols/yahoo/libymsg.c:4930
+#: ../libpurple/protocols/yahoo/libymsg.c:4939
+#: ../libpurple/protocols/zephyr/zephyr.c:2377 ../pidgin/gtknotify.c:1713
+#: ../pidgin/gtksavedstatuses.c:491 ../pidgin/gtksavedstatuses.c:980
msgid "Message"
msgstr "Wiadomość"
#. Use
+#: ../finch/gntstatus.c:190 ../finch/gntstatus.c:589
msgid "Use"
msgstr "Użycie"
+#: ../finch/gntstatus.c:297
msgid "Invalid title"
msgstr "Nieprawidłowy tytuł"
+#: ../finch/gntstatus.c:298
msgid "Please enter a non-empty title for the status."
msgstr "Proszę podać niepusty tytuł stanu."
+#: ../finch/gntstatus.c:306
msgid "Duplicate title"
msgstr "Podwójny tytuł"
+#: ../finch/gntstatus.c:307
msgid "Please enter a different title for the status."
msgstr "Proszę podać inny tytuł dla stanu."
+#: ../finch/gntstatus.c:448
msgid "Substatus"
msgstr "Podstan"
+#: ../finch/gntstatus.c:460 ../pidgin/gtkxfer.c:656
msgid "Status:"
msgstr "Stan:"
+#: ../finch/gntstatus.c:475
msgid "Message:"
msgstr "Wiadomość:"
+#: ../finch/gntstatus.c:524
msgid "Edit Status"
msgstr "Zmodyfikuj stan"
+#. TODO: Check whether it's correct to call prepend_pair_html,
+#. or if we should be using prepend_pair_plaintext
+#. presence
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
+#: ../finch/gntstatus.c:543 ../finch/gntstatus.c:572 ../finch/gntxfer.c:212
+#: ../libpurple/protocols/bonjour/bonjour.c:381
+#: ../libpurple/protocols/gg/chat.c:573 ../libpurple/protocols/gg/gg.c:650
+#: ../libpurple/protocols/gg/gg.c:658
+#: ../libpurple/protocols/gg/pubdir-prpl.c:405
+#: ../libpurple/protocols/jabber/buddy.c:787
+#: ../libpurple/protocols/jabber/buddy.c:795
+#: ../libpurple/protocols/jabber/buddy.c:871
+#: ../libpurple/protocols/jabber/jabber.c:2213
+#: ../libpurple/protocols/msn/msn.c:1087 ../libpurple/protocols/msn/msn.c:1096
+#: ../libpurple/protocols/msn/msn.c:1101 ../libpurple/protocols/msn/msn.c:1104
+#: ../libpurple/protocols/mxit/mxit.c:342
+#: ../libpurple/protocols/mxit/profile.c:238
+#: ../libpurple/protocols/novell/novell.c:2891
+#: ../libpurple/protocols/oscar/oscar.c:2092
+#: ../libpurple/protocols/oscar/oscar.c:2119
+#: ../libpurple/protocols/oscar/userinfo.c:301
+#: ../libpurple/protocols/sametime/sametime.c:3300
+#: ../libpurple/protocols/sametime/sametime.c:4143
+#: ../libpurple/protocols/yahoo/libymsg.c:4118 ../pidgin/gtkblist.c:4016
+#: ../pidgin/gtkblist.c:4030 ../pidgin/gtkblist.c:4032
+#: ../pidgin/gtksavedstatuses.c:965 ../pidgin/gtksavedstatuses.c:1114
+msgid "Status"
+msgstr "Stan"
+
+#: ../finch/gntstatus.c:566
msgid "Use different status for following accounts"
msgstr "Użycie innych stanów dla poszczególnych kont"
#. Save & Use
+#: ../finch/gntstatus.c:600
msgid "Save & Use"
msgstr "Zapisz i użyj"
+#: ../finch/gntui.c:106
msgid "Certificates"
msgstr "Certyfikaty"
+#: ../finch/gntui.c:108 ../finch/gntxfer.c:120 ../finch/gntxfer.c:207
+#: ../pidgin/gtkxfer.c:233 ../pidgin/gtkxfer.c:721 ../pidgin/gtkxfer.c:723
+msgid "File Transfers"
+msgstr "Przesyłanie plików"
+
+#: ../finch/gntui.c:111 ../pidgin/gtkprefs.c:4172
msgid "Sounds"
msgstr "Dźwięki"
+#: ../finch/gntui.c:114
msgid "Statuses"
msgstr "Stany"
+#: ../finch/gntxfer.c:113 ../pidgin/gtkxfer.c:226
+#, c-format
+msgid "File Transfers - %d%% of %d file"
+msgid_plural "File Transfers - %d%% of %d files"
+msgstr[0] "Przesyłanie pliku - %d%% z %d pliku"
+msgstr[1] "Przesyłanie plików - %d%% z %d plików"
+msgstr[2] "Przesyłanie plików - %d%% z %d plików"
+
+#: ../finch/gntxfer.c:212 ../pidgin/gtkxfer.c:603
+msgid "Progress"
+msgstr "Postęp"
+
+#: ../finch/gntxfer.c:212 ../pidgin/gtkxfer.c:610
+msgid "Filename"
+msgstr "Nazwa pliku"
+
+#: ../finch/gntxfer.c:212 ../pidgin/gtkxfer.c:617
+msgid "Size"
+msgstr "Rozmiar"
+
+#: ../finch/gntxfer.c:212
+msgid "Speed"
+msgstr "Prędkość"
+
+#: ../finch/gntxfer.c:212 ../pidgin/gtkxfer.c:624
+msgid "Remaining"
+msgstr "Pozostało"
+
+#: ../finch/gntxfer.c:222
+msgid "Close this window when all transfers finish"
+msgstr "Zamknięcie tego okna po ukończeniu wszystkich przesyłań"
+
+#: ../finch/gntxfer.c:229
+msgid "Clear finished transfers"
+msgstr "Wyczyść ukończone przesyłania"
+
+#: ../finch/gntxfer.c:316 ../pidgin/gtkxfer.c:166 ../pidgin/gtkxfer.c:897
+msgid "Waiting for transfer to begin"
+msgstr "Oczekiwanie na rozpoczęcie przesyłu"
+
+#: ../finch/gntxfer.c:381 ../libpurple/http.c:2315 ../pidgin/gtkxfer.c:160
+#: ../pidgin/gtkxfer.c:978
+msgid "Cancelled"
+msgstr "Anulowano"
+
+#: ../finch/gntxfer.c:383 ../pidgin/gtkxfer.c:980
+msgid "Failed"
+msgstr "Nie powiodło się"
+
+#: ../finch/gntxfer.c:428 ../pidgin/gtkxfer.c:131
+#, c-format
+msgid "%.2f KiB/s"
+msgstr "%.2f KB/s"
+
+#: ../finch/gntxfer.c:439
+msgid "Sent"
+msgstr "Wysłano"
+
+#: ../finch/gntxfer.c:439
+msgid "Received"
+msgstr "Odebrano"
+
+#: ../finch/gntxfer.c:440 ../pidgin/gtkxfer.c:157 ../pidgin/gtkxfer.c:1042
+msgid "Finished"
+msgstr "Ukończono"
+
+#: ../finch/gntxfer.c:442
+#, c-format
+msgid "The file was saved as %s."
+msgstr "Plik został zapisany jako %s."
+
+#: ../finch/gntxfer.c:449
+msgid "Sending"
+msgstr "Wysyłanie"
+
+#: ../finch/gntxfer.c:449
+msgid "Receiving"
+msgstr "Odbieranie"
+
+#: ../finch/libfinch.c:246
+#, c-format
+msgid "%s. Try `%s -h' for more information.\n"
+msgstr "%s. Należy wpisać \"%s -h\", aby uzyskać więcej informacji.\n"
+
+#: ../finch/libfinch.c:248
+#, c-format
+msgid ""
+"%s\n"
+"Usage: %s [OPTION]...\n"
+"\n"
+" -c, --config=DIR use DIR for config files\n"
+" -d, --debug print debugging messages to stderr\n"
+" -h, --help display this help and exit\n"
+" -n, --nologin don't automatically login\n"
+" -v, --version display the current version and exit\n"
+msgstr "%s\nUżycie: %s [OPCJA]...\n\n -c, --config=KATALOG używa KATALOGU z plikami konfiguracji\n -d, --debug przekazuje komunikaty debugowania na standardowe\n wyjście błędów\n -h, --help wyświetla tę pomoc i kończy pracę\n -n, --nologin bez automatycznego logowania\n -v, --version wyświetla bieżącą wersję i kończy pracę\n"
+
+#: ../finch/plugins/gntclipboard.c:116 ../finch/plugins/gntclipboard.c:122
+#: ../finch/plugins/gntclipboard.c:129
msgid "Error loading the plugin."
msgstr "Błąd podczas wczytywania wtyczki."
+#: ../finch/plugins/gntclipboard.c:117
msgid "Couldn't find X display"
msgstr "Nie można odnaleźć ekranu X"
+#: ../finch/plugins/gntclipboard.c:123
msgid "Couldn't find window"
msgstr "Nie można odnaleźć okna"
-msgid "This plugin cannot be loaded because it was not built with X11 support."
+#: ../finch/plugins/gntclipboard.c:130
+msgid ""
+"This plugin cannot be loaded because it was not built with X11 support."
msgstr "Nie można wczytać tej wtyczki, ponieważ nie obsługuje ona X11."
+#: ../finch/plugins/gntclipboard.c:159
msgid "GntClipboard"
msgstr "GntClipboard"
+#: ../finch/plugins/gntclipboard.c:161
msgid "Clipboard plugin"
msgstr "Wtyczka schowka"
+#: ../finch/plugins/gntclipboard.c:162
msgid ""
"When the gnt clipboard contents change, the contents are made available to "
"X, if possible."
-msgstr ""
-"Kiedy zmienia się zawartość schowka gnt, w miarę możliwości jest ona "
-"dostępna dla X."
+msgstr "Kiedy zmienia się zawartość schowka gnt, w miarę możliwości jest ona dostępna dla X."
+#: ../finch/plugins/gntgf.c:232
#, c-format
msgid "%s just signed on"
msgstr "Użytkownik %s właśnie się zalogował"
+#: ../finch/plugins/gntgf.c:239
#, c-format
msgid "%s just signed off"
msgstr "Użytkownik %s właśnie się rozłączył"
+#: ../finch/plugins/gntgf.c:247
#, c-format
msgid "%s sent you a message"
msgstr "Użytkownik %s wysłał wiadomość"
+#: ../finch/plugins/gntgf.c:264
#, c-format
msgid "%s said your nick in %s"
msgstr "Użytkownik %s wypowiedział pseudonim użytkownika w %s"
+#: ../finch/plugins/gntgf.c:266
#, c-format
msgid "%s sent a message in %s"
msgstr "Użytkownik %s wysłał wiadomość w %s"
+#: ../finch/plugins/gntgf.c:304
msgid "Buddy signs on/off"
msgstr "Znajomy loguje się/wylogowuje się"
+#: ../finch/plugins/gntgf.c:305
msgid "You receive an IM"
msgstr "Odebrano wiadomość"
+#: ../finch/plugins/gntgf.c:306
msgid "Someone speaks in a chat"
msgstr "Ktoś odzywa się na konferencji"
+#: ../finch/plugins/gntgf.c:307
msgid "Someone says your name in a chat"
msgstr "Ktoś wymawia imię użytkownika na konferencji"
+#: ../finch/plugins/gntgf.c:335
msgid "Notify with a toaster when"
msgstr "Powiadamianie za pomocą tostera, kiedy"
+#: ../finch/plugins/gntgf.c:350
msgid "Beep too!"
msgstr "Także sygnał dźwiękowy."
+#: ../finch/plugins/gntgf.c:356
msgid "Set URGENT for the terminal window."
msgstr "Ustawienie WAŻNE dla okna terminala."
+#: ../finch/plugins/gntgf.c:376
msgid "GntGf"
msgstr "GntGf"
+#: ../finch/plugins/gntgf.c:378 ../finch/plugins/gntgf.c:379
msgid "Toaster plugin"
msgstr "Wtyczka tostera"
+#: ../finch/plugins/gnthistory.c:116 ../pidgin/plugins/history.c:149
#, c-format
msgid "<b>Conversation with %s on %s:</b><br>"
msgstr "<b>Rozmowa z %s dnia %s:</b><br>"
+#: ../finch/plugins/gnthistory.c:174 ../pidgin/plugins/history.c:179
msgid "History Plugin Requires Logging"
-msgstr "Wtyczka historii wymaga zapisu rozmów do dziennika"
+msgstr "Wtyczka historii wymaga zapisu rozmów do archiwum"
+#: ../finch/plugins/gnthistory.c:175 ../pidgin/plugins/history.c:180
msgid ""
"Logging can be enabled from Tools -> Preferences -> Logging.\n"
"\n"
-"Enabling logs for instant messages and/or chats will activate history for "
-"the same conversation type(s)."
-msgstr ""
-"Zapis do dziennika rozmów można włączyć w Narzędzia -> Preferencje -> "
-"Dziennik rozmów\n"
-"\n"
-"Wtyczka historii będzie aktywna dla tych typów rozmów, dla których zostanie "
-"włączony zapis do dziennika rozmów."
+"Enabling logs for instant messages and/or chats will activate history for the same conversation type(s)."
+msgstr "Zapis do archiwum rozmów można włączyć w Narzędzia -> Preferencje -> Archiwum rozmów\n\nWtyczka historii będzie aktywna dla tych typów rozmów, dla których zostanie włączony zapis do archiwum rozmów."
+#: ../finch/plugins/gnthistory.c:219
msgid "GntHistory"
msgstr "GntHistory"
+#: ../finch/plugins/gnthistory.c:221 ../pidgin/plugins/history.c:223
msgid "Shows recently logged conversations in new conversations."
-msgstr "Wyświetla ostatnie zapisy z dziennika rozmów w nowych rozmowach."
+msgstr "Wyświetla ostatnie zapisy z archiwum rozmów w nowych rozmowach."
+#: ../finch/plugins/gnthistory.c:222 ../pidgin/plugins/history.c:224
msgid ""
"When a new conversation is opened this plugin will insert the last "
"conversation into the current conversation."
-msgstr ""
-"Kiedy nowa rozmowa zostanie rozpoczęta, ta wtyczka wstawi ostatnią rozmowę "
-"do okna bieżącej rozmowy."
+msgstr "Kiedy nowa rozmowa zostanie rozpoczęta, ta wtyczka wstawi ostatnią rozmowę do okna bieżącej rozmowy."
+
+#: ../finch/plugins/gnttinyurl.c:222
+msgid "Error while querying TinyURL"
+msgstr "Błąd podczas odpytywania TinyURL"
+#: ../finch/plugins/gnttinyurl.c:354
#, c-format
msgid ""
"\n"
"Fetching TinyURL..."
-msgstr ""
-"\n"
-"Przechwytywanie adresu TinyURL..."
+msgstr "\nPrzechwytywanie adresu TinyURL..."
+#: ../finch/plugins/gnttinyurl.c:380
#, c-format
msgid "TinyURL for above: %s"
msgstr "Adres TinyURL dla powyższego: %s"
+#: ../finch/plugins/gnttinyurl.c:420
msgid "Please wait while TinyURL fetches a shorter URL ..."
msgstr "Proszę czekać, aż TinyURL przechwyci skrócony adres URL..."
+#: ../finch/plugins/gnttinyurl.c:503
msgid "Only create TinyURL for URLs of this length or greater"
-msgstr ""
-"Tworzenie adresów TinyURL tylko dla adresów URL tej długości lub większych"
+msgstr "Tworzenie adresów TinyURL tylko dla adresów URL tej długości lub większych"
+#: ../finch/plugins/gnttinyurl.c:507
msgid "TinyURL (or other) address prefix"
msgstr "Przedrostek adresu TinyURL (lub innego)"
+#: ../finch/plugins/gnttinyurl.c:535
msgid "TinyURL"
msgstr "TinyURL"
+#: ../finch/plugins/gnttinyurl.c:537
msgid "TinyURL plugin"
msgstr "Wtyczka TinyURL"
+#: ../finch/plugins/gnttinyurl.c:538
msgid "When receiving a message with URL(s), use TinyURL for easier copying"
-msgstr ""
-"Po odebraniu wiadomości z adresami URL, używa TinyURL, aby ułatwić kopiowanie"
+msgstr "Po odebraniu wiadomości z adresami URL, używa TinyURL, aby ułatwić kopiowanie"
+#: ../finch/plugins/grouping.c:85 ../libpurple/protocols/oscar/oscar.c:174
msgid "Online"
msgstr "Online"
#. primitive, no, id, name
+#: ../finch/plugins/grouping.c:87 ../finch/plugins/grouping.c:170
+#: ../libpurple/protocols/jabber/buddy.c:866
+#: ../libpurple/protocols/jabber/jutil.c:710
+#: ../libpurple/protocols/mxit/roster.c:53
+#: ../libpurple/protocols/novell/novell.c:2884
+#: ../libpurple/protocols/oscar/oscar.c:4507
+#: ../libpurple/protocols/oscar/userinfo.c:277
+#: ../libpurple/protocols/yahoo/libymsg.c:3964 ../libpurple/status.c:137
+#: ../pidgin/gtkblist.c:4016 ../pidgin/gtkblist.c:4410
+#: ../pidgin/gtkdocklet.c:598 ../pidgin/gtkstatusbox.c:1055
+#: ../pidgin/plugins/themeedit-icon.c:68
msgid "Offline"
msgstr "Offline"
+#: ../finch/plugins/grouping.c:142 ../pidgin/gtkblist.c:4053
msgid "Online Buddies"
msgstr "Znajomi w trybie online"
+#: ../finch/plugins/grouping.c:142
msgid "Offline Buddies"
msgstr "Znajomi w trybie offline"
+#: ../finch/plugins/grouping.c:152
msgid "Online/Offline"
msgstr "Online/offline"
+#: ../finch/plugins/grouping.c:189
msgid "Meebo"
msgstr "Meebo"
+#: ../finch/plugins/grouping.c:235
msgid "No Grouping"
msgstr "Bez grupowania"
+#: ../finch/plugins/grouping.c:314
msgid "Nested Subgroup"
msgstr "Zagnieżdżone podgrupy"
+#: ../finch/plugins/grouping.c:346
msgid "Nested Grouping (experimental)"
msgstr "Zagnieżdżone grupowanie (eksperymentalne)"
+#: ../finch/plugins/grouping.c:389 ../finch/plugins/grouping.c:390
msgid "Provides alternate buddylist grouping options."
msgstr "Dostarcza alternatywne opcje grupowania listy znajomych."
+#: ../finch/plugins/lastlog.c:69
msgid "Lastlog"
-msgstr "Ostatni dziennik"
+msgstr "Wyszukiwanie w archiwum"
#. Translator Note: The "backlog" is the conversation buffer/history.
+#: ../finch/plugins/lastlog.c:100
msgid "lastlog: Searches for a substring in the backlog."
msgstr "lastlog: wyszukuje podciąg w zaległościach."
+#: ../finch/plugins/lastlog.c:122
msgid "GntLastlog"
msgstr "GntLastlog"
+#: ../finch/plugins/lastlog.c:124 ../finch/plugins/lastlog.c:125
msgid "Lastlog plugin."
-msgstr "Wtyczka ostatniego dziennika."
-
-msgid "accounts"
-msgstr "konta"
+msgstr "Wtyczka wyszukiwania w archiwum rozmów."
+#: ../libpurple/account.c:253 ../libpurple/protocols/jabber/auth.c:108
+#: ../libpurple/protocols/jabber/auth_cyrus.c:148
+#: ../libpurple/protocols/silc/silc.c:476
msgid "Password is required to sign on."
msgstr "Do zalogowania wymagane jest hasło."
+#: ../libpurple/account.c:286
#, c-format
msgid "Enter password for %s (%s)"
msgstr "Proszę podać hasło dla %s (%s)"
+#: ../libpurple/account.c:293
msgid "Enter Password"
msgstr "Proszę podać hasło"
+#: ../libpurple/account.c:298
msgid "Save password"
msgstr "Zapisz hasło"
+#: ../libpurple/account.c:346 ../libpurple/connection.c:936
+#: ../libpurple/connection.c:1004
#, c-format
msgid "Missing protocol plugin for %s"
msgstr "Brak wtyczki protokołu %s"
+#: ../libpurple/account.c:347 ../libpurple/connection.c:939
msgid "Connection Error"
msgstr "Błąd połączenia"
+#: ../libpurple/account.c:609 ../libpurple/protocols/jabber/jabber.c:2504
msgid "New passwords do not match."
msgstr "Nowe hasła nie zgadzają się."
+#: ../libpurple/account.c:623
msgid "Fill out all fields completely."
msgstr "Proszę całkowicie wypełnić wszystkie pola."
+#: ../libpurple/account.c:656
msgid "Original password"
msgstr "Poprzednie hasło"
+#: ../libpurple/account.c:664
msgid "New password"
msgstr "Nowe hasło"
+#: ../libpurple/account.c:672
msgid "New password (again)"
msgstr "Nowe hasło (ponownie)"
+#: ../libpurple/account.c:679 ../libpurple/protocols/gg/account.c:509
#, c-format
msgid "Change password for %s"
msgstr "Zmiana hasła dla %s"
+#: ../libpurple/account.c:685 ../libpurple/protocols/gg/account.c:513
msgid "Please enter your current password and your new password."
msgstr "Proszę podać bieżące i nowe hasło."
+#: ../libpurple/account.c:713
#, c-format
msgid "Change user information for %s"
msgstr "Zmiana informacji o użytkowniku %s"
+#: ../libpurple/account.c:716 ../libpurple/protocols/gg/gg.c:930
+#: ../libpurple/protocols/gg/pubdir-prpl.c:853
+#: ../libpurple/protocols/yahoo/yahoo_aliases.c:562
+#: ../libpurple/protocols/yahoo/yahoo_aliases.c:575
msgid "Set User Info"
msgstr "Ustaw informacje o użytkowniku"
+#: ../libpurple/account.c:1045
msgid "This protocol does not support setting a public alias."
msgstr "Ten protokół nie obsługuje ustawiania publicznego pseudonimu."
+#: ../libpurple/account.c:1087
msgid "This protocol does not support fetching the public alias."
msgstr "Ten protokół nie obsługuje pobierania publicznego pseudonimu."
+#: ../libpurple/account.c:1476 ../libpurple/protocols/jabber/buddy.c:795
+#: ../libpurple/protocols/jabber/jutil.c:727
+#: ../libpurple/protocols/msn/oim.c:731
+#: ../libpurple/protocols/mxit/profile.c:47
+#: ../libpurple/protocols/mxit/profile.c:242
+#: ../libpurple/protocols/novell/novell.c:2887 ../pidgin/gtkprefs.c:3596
+#: ../pidgin/gtkxfer.c:163 ../pidgin/plugins/disco/gtkdisco.c:477
msgid "Unknown"
msgstr "Nieznany"
-#. Changing this string? Look in add_purple_buddy_to_groups
+#: ../libpurple/accounts.c:583
+msgid "accounts"
+msgstr "konta"
+
+#: ../libpurple/buddylist.c:375 ../libpurple/buddylist.c:376
msgid "Buddies"
msgstr "Znajomi"
+#: ../libpurple/buddylist.c:638
msgid "buddy list"
msgstr "lista znajomych"
+#: ../libpurple/certificate.c:44
msgid "The certificate is self-signed and cannot be automatically checked."
-msgstr ""
-"Certyfikat jest podpisany przez siebie samego i nie może zostać "
-"automatycznie sprawdzony."
+msgstr "Certyfikat jest podpisany przez siebie samego i nie może zostać automatycznie sprawdzony."
+#: ../libpurple/certificate.c:48
msgid ""
"The certificate is not trusted because no certificate that can verify it is "
"currently trusted."
-msgstr ""
-"Certyfikat nie jest zaufany, ponieważ żaden certyfikat, który może go "
-"sprawdzić, nie jest obecnie zaufany."
+msgstr "Certyfikat nie jest zaufany, ponieważ żaden certyfikat, który może go sprawdzić, nie jest obecnie zaufany."
+#: ../libpurple/certificate.c:52
msgid ""
"The certificate is not valid yet. Check that your computer's date and time "
"are accurate."
-msgstr ""
-"Certyfikat nie jest jeszcze prawidłowy. Proszę sprawdzić, czy data i czas "
-"komputera są dokładne."
+msgstr "Certyfikat nie jest jeszcze prawidłowy. Proszę sprawdzić, czy data i czas komputera są dokładne."
+#: ../libpurple/certificate.c:56
msgid ""
"The certificate has expired and should not be considered valid. Check that "
"your computer's date and time are accurate."
-msgstr ""
-"Certyfikat wygasł i nie jest więc prawidłowy. Proszę sprawdzić, czy data i "
-"czas komputera są dokładne."
+msgstr "Certyfikat wygasł i nie jest więc prawidłowy. Proszę sprawdzić, czy data i czas komputera są dokładne."
#. Translators: "domain" refers to a DNS domain (e.g. talk.google.com)
+#: ../libpurple/certificate.c:62
msgid "The certificate presented is not issued to this domain."
msgstr "Prezentowany certyfikat nie został wydany dla tej domeny."
+#: ../libpurple/certificate.c:65
msgid ""
"You have no database of root certificates, so this certificate cannot be "
"validated."
-msgstr ""
-"Brak bazy danych głównych certyfikatów, więc ten certyfikat nie może zostać "
-"sprawdzony."
+msgstr "Brak bazy danych głównych certyfikatów, więc ten certyfikat nie może zostać sprawdzony."
+#: ../libpurple/certificate.c:69
msgid "The certificate chain presented is invalid."
msgstr "Prezentowany ciąg certyfikatu jest nieprawidłowy."
+#: ../libpurple/certificate.c:72
msgid "The certificate has been revoked."
msgstr "Certyfikat został odrzucony."
+#: ../libpurple/certificate.c:75
+msgid "The certificate was rejected by the user."
+msgstr "Certyfikat został odrzucony przez użytkownika."
+
+#: ../libpurple/certificate.c:79
msgid "An unknown certificate error occurred."
msgstr "Wystąpił nieznany błąd certyfikatu."
+#: ../libpurple/certificate.c:729
+msgid "(MATCH)"
+msgstr "(PASUJE)"
+
+#: ../libpurple/certificate.c:731
msgid "(DOES NOT MATCH)"
msgstr "(NIE ZGADZA SIĘ)"
-#. Make messages
+#: ../libpurple/certificate.c:734
#, c-format
msgid "%s has presented the following certificate for just-this-once use:"
msgstr "%s zaprezentowało następujący certyfikat jednorazowego użytku:"
+#: ../libpurple/certificate.c:735
#, c-format
-msgid ""
-"Common name: %s %s\n"
-"Fingerprint (SHA1): %s"
-msgstr ""
-"Wspólna nazwa: %s %s\n"
-"Odcisk (SHA1): %s"
+msgid "Common name: %s %s"
+msgstr "Wspólna nazwa: %s %s"
#. TODO: Find what the handle ought to be
+#: ../libpurple/certificate.c:740
msgid "Single-use Certificate Verification"
msgstr "Sprawdzanie jednorazowego certyfikatu"
#. Scheme name
#. Pool name
+#: ../libpurple/certificate.c:1120
msgid "Certificate Authorities"
msgstr "Władze certyfikatów"
#. Scheme name
#. Pool name
+#: ../libpurple/certificate.c:1292
msgid "SSL Peers Cache"
msgstr "Pamięć podręczna użytkowników SSL"
-#. Make messages
+#: ../libpurple/certificate.c:1358
#, c-format
msgid "Accept certificate for %s?"
msgstr "Zaakceptować certyfikat dla %s?"
#. TODO: Find what the handle ought to be
+#: ../libpurple/certificate.c:1363
msgid "SSL Certificate Verification"
msgstr "Sprawdzanie certyfikatu SSL"
-msgid "_View Certificate..."
-msgstr "_Wyświetl certyfikat..."
-
+#: ../libpurple/certificate.c:1397 ../libpurple/certificate.c:1419
#, c-format
msgid "The certificate for %s could not be validated."
msgstr "Nie udało się sprawdzić certyfikatu dla %s."
#. TODO: Probably wrong.
+#: ../libpurple/certificate.c:1406
msgid "SSL Certificate Error"
msgstr "Błąd certyfikatu SSL"
+#: ../libpurple/certificate.c:1407
msgid "Unable to validate certificate"
msgstr "Nie można sprawdzić certyfikatu"
+#: ../libpurple/certificate.c:1431
#, c-format
msgid ""
"The certificate claims to be from \"%s\" instead. This could mean that you "
"are not connecting to the service you believe you are."
-msgstr ""
-"Z danych certyfikatu wynika, że pochodzi on z \"%s\". Może to oznaczać, że "
-"użytkownik łączy się z usługą inną niż żądana."
-
-#. Make messages
-#, c-format
-msgid ""
-"Common name: %s\n"
-"\n"
-"Fingerprint (SHA1): %s\n"
-"\n"
-"Activation date: %s\n"
-"Expiration date: %s\n"
-msgstr ""
-"Wspólna nazwa: %s\n"
-"\n"
-"Odcisk (SHA1): %s\n"
-"\n"
-"Data aktywacji: %s\n"
-"Data wygaśnięcia: %s\n"
-
-#. TODO: Find what the handle ought to be
-msgid "Certificate Information"
-msgstr "Informacje o certyfikacie"
-
-#. show error to user
-msgid "Registration Error"
-msgstr "Błąd podczas rejestracji"
-
-msgid "Unregistration Error"
-msgstr "Błąd podczas wyrejestrowania"
+msgstr "Z danych certyfikatu wynika, że pochodzi on z \"%s\". Może to oznaczać, że użytkownik łączy się z usługą inną niż żądana."
+#: ../libpurple/connection.c:210
#, c-format
msgid "+++ %s signed on"
msgstr "+++ %s zalogował się"
+#: ../libpurple/connection.c:241
#, c-format
msgid "+++ %s signed off"
msgstr "+++ %s rozłączył się"
#. Undocumented
#. Unknown error
+#: ../libpurple/connection.c:488 ../libpurple/plugin.c:269
+#: ../libpurple/protocols/jabber/buddy.c:2166
+#: ../libpurple/protocols/msn/servconn.c:149
+#: ../libpurple/protocols/msn/session.c:402
+#: ../libpurple/protocols/msn/session.c:415
+#: ../libpurple/protocols/oscar/family_chatnav.c:67
+#: ../libpurple/protocols/oscar/family_icbm.c:60
+#: ../libpurple/protocols/oscar/family_icbm.c:61
+#: ../libpurple/protocols/yahoo/libymsg.c:1964
+#: ../libpurple/protocols/yahoo/libymsg.c:1975
+#: ../libpurple/protocols/yahoo/libymsg.c:2361
+#: ../libpurple/purple-socket.c:182
msgid "Unknown error"
msgstr "Nieznany błąd"
+#. show error to user
+#: ../libpurple/connection.c:938 ../libpurple/protocols/mxit/login.c:293
+msgid "Registration Error"
+msgstr "Błąd podczas rejestracji"
+
+#: ../libpurple/connection.c:1006
+msgid "Unregistration Error"
+msgstr "Błąd podczas wyrejestrowania"
+
+#: ../libpurple/conversation.c:177
msgid "Unable to send message: The message is too large."
msgstr "Nie można wysłać wiadomości, ponieważ jest za duża."
+#: ../libpurple/conversation.c:180 ../libpurple/conversation.c:195
#, c-format
msgid "Unable to send message to %s."
msgstr "Nie można wysłać wiadomości do %s."
+#: ../libpurple/conversation.c:182
msgid "The message is too large."
msgstr "Ta wiadomość jest za duża."
+#: ../libpurple/conversation.c:192 ../libpurple/protocols/bonjour/jabber.c:306
+#: ../libpurple/protocols/bonjour/jabber.c:352
msgid "Unable to send message."
msgstr "Nie można wysłać wiadomości."
+#: ../libpurple/conversation.c:800
msgid "Send Message"
msgstr "Wyślij wiadomość"
+#: ../libpurple/conversation.c:803
msgid "_Send Message"
msgstr "_Wyślij wiadomość"
+#: ../libpurple/conversationtypes.c:940
#, c-format
msgid "%s entered the room."
msgstr "Użytkownik %s wszedł do pokoju."
+#: ../libpurple/conversationtypes.c:943
#, c-format
msgid "%s [<I>%s</I>] entered the room."
msgstr "Użytkownik %s [<I>%s</I>] wszedł do pokoju."
+#: ../libpurple/conversationtypes.c:1054
#, c-format
msgid "You are now known as %s"
msgstr "Użytkownik nazywa się teraz %s"
+#: ../libpurple/conversationtypes.c:1074
#, c-format
msgid "%s is now known as %s"
msgstr "Użytkownik %s nazywa się teraz %s"
+#: ../libpurple/conversationtypes.c:1150
#, c-format
msgid "%s left the room."
msgstr "Użytkownik %s opuścił pokój."
+#: ../libpurple/conversationtypes.c:1153
#, c-format
msgid "%s left the room (%s)."
msgstr "Użytkownik %s opuścił pokój (%s)."
+#: ../libpurple/conversationtypes.c:1263 ../libpurple/conversationtypes.c:1274
msgid "Invite to chat"
msgstr "Zaproś do konferencji"
#. Put our happy label in it.
+#: ../libpurple/conversationtypes.c:1275 ../pidgin/gtkconv.c:956
msgid ""
-"Please enter the name of the user you wish to invite, along with an optional "
-"invite message."
-msgstr ""
-"Proszę podać nazwę zapraszanego użytkownika. Można również wpisać treść "
-"zaproszenia (opcjonalnie)."
+"Please enter the name of the user you wish to invite, along with an optional"
+" invite message."
+msgstr "Proszę podać nazwę zapraszanego użytkownika. Można również wpisać treść zaproszenia (opcjonalnie)."
+#: ../libpurple/dbus-server.c:598
#, c-format
msgid "Failed to get connection: %s"
msgstr "Uzyskanie połączenia się nie powiodło: %s"
+#: ../libpurple/dbus-server.c:610
#, c-format
msgid "Failed to get name: %s"
msgstr "Uzyskanie nazwy się nie powiodło: %s"
+#: ../libpurple/dbus-server.c:622
#, c-format
msgid "Failed to get serv name: %s"
msgstr "Uzyskanie nazwy serwera się nie powiodło: %s"
+#: ../libpurple/dbus-server.h:87
msgid "Purple's D-BUS server is not running for the reason listed below"
-msgstr ""
-"Serwer D-Bus biblioteki Purple nie jest uruchomiony z poniższego powodu"
+msgstr "Serwer D-Bus biblioteki Purple nie jest uruchomiony z poniższego powodu"
+#: ../libpurple/desktopitem.c:282 ../libpurple/desktopitem.c:877
msgid "No name"
msgstr "Bez nazwy"
+#: ../libpurple/dnsquery.c:617
msgid "Unable to create new resolver process\n"
msgstr "Nie można utworzyć nowego procesu rozwiązania\n"
+#: ../libpurple/dnsquery.c:622
msgid "Unable to send request to resolver process\n"
msgstr "Nie można wysłać żądania do procesu rozwiązania\n"
+#: ../libpurple/dnsquery.c:655 ../libpurple/dnsquery.c:788
#, c-format
msgid ""
"Error resolving %s:\n"
"%s"
-msgstr ""
-"Błąd podczas rozwiązywania %s:\n"
-"%s"
+msgstr "Błąd podczas rozwiązywania %s:\n%s"
+#: ../libpurple/dnsquery.c:658 ../libpurple/dnsquery.c:802
+#: ../libpurple/dnsquery.c:856 ../libpurple/dnsquery.c:867
#, c-format
msgid "Error resolving %s: %d"
msgstr "Błąd podczas rozwiązywania %s: %d"
+#: ../libpurple/dnsquery.c:682
#, c-format
msgid ""
"Error reading from resolver process:\n"
"%s"
-msgstr ""
-"Błąd podczas odczytywania z procesu rozwiązania:\n"
-"%s"
+msgstr "Błąd podczas odczytywania z procesu rozwiązania:\n%s"
+#: ../libpurple/dnsquery.c:686
#, c-format
msgid "Resolver process exited without answering our request"
msgstr "Proces rozwiązania zakończył pracę bez odpowiedzi na żądanie"
+#: ../libpurple/dnsquery.c:752
#, c-format
msgid "Error converting %s to punycode: %d"
msgstr "Błąd podczas konwertowania %s na punycode: %d"
+#: ../libpurple/dnsquery.c:827
#, c-format
msgid "Thread creation failure: %s"
msgstr "Utworzenie wątku się nie powiodło: %s"
+#: ../libpurple/dnsquery.c:828 ../libpurple/protocols/oscar/oscar.c:948
+#: ../libpurple/protocols/oscar/util.c:65
msgid "Unknown reason"
msgstr "Nieznany powód"
+#: ../libpurple/dnsquery.c:904
msgid "Aborting DNS lookup in Tor Proxy mode."
msgstr "Przerywanie wyszukiwania DNS w trybie pośrednika Tor."
-#, c-format
-msgid ""
-"Error reading %s: \n"
-"%s.\n"
-msgstr ""
-"Błąd podczas odczytywania %s: \n"
-"%s.\n"
-
-#, c-format
-msgid ""
-"Error writing %s: \n"
-"%s.\n"
-msgstr ""
-"Błąd podczas zapisywania %s: \n"
-"%s.\n"
-
-#, c-format
-msgid ""
-"Error accessing %s: \n"
-"%s.\n"
-msgstr ""
-"Błąd podczas uzyskiwania dostępu do %s: \n"
-"%s.\n"
-
-msgid "Directory is not writable."
-msgstr "Katalog nie jest udostępniony do zapisu."
-
-msgid "Cannot send a file of 0 bytes."
-msgstr "Nie można wysłać pliku o zerowej długości."
+#: ../libpurple/gconf/purple.schemas.in.h:1
+msgid "Run the command in a terminal"
+msgstr "Wykonanie polecenie w terminalu"
-msgid "Cannot send a directory."
-msgstr "Nie można wysłać katalogu."
+#: ../libpurple/gconf/purple.schemas.in.h:2
+msgid "The command used to handle \"aim\" URLs, if enabled."
+msgstr "Polecenie używane do obsługi adresów URL \"aim\", jeśli włączone."
-#, c-format
-msgid "%s is not a regular file. Cowardly refusing to overwrite it.\n"
-msgstr ""
-"%s nie jest zwykłym plikiem. Na wszelki wypadek nie zostanie on zastąpiony.\n"
+#: ../libpurple/gconf/purple.schemas.in.h:3
+msgid "The command used to handle \"gg\" URLs, if enabled."
+msgstr "Polecenie używane do obsługi adresów URL \"gg\", jeśli włączone."
-msgid "File is not readable."
-msgstr "Plik jest nieczytelny."
+#: ../libpurple/gconf/purple.schemas.in.h:4
+msgid "The command used to handle \"icq\" URLs, if enabled."
+msgstr "Polecenie używane do obsługi adresów URL \"icq\", jeśli włączone."
-#, c-format
-msgid "%s wants to send you %s (%s)"
-msgstr "Użytkownik %s chce wysłać %s (%s)"
+#: ../libpurple/gconf/purple.schemas.in.h:5
+msgid "The command used to handle \"irc\" URLs, if enabled."
+msgstr "Polecenie używane do obsługi adresów URL \"irc\", jeśli włączone."
-#, c-format
-msgid "%s wants to send you a file"
-msgstr "Użytkownik %s chce wysłać plik"
+#: ../libpurple/gconf/purple.schemas.in.h:6
+msgid "The command used to handle \"msnim\" URLs, if enabled."
+msgstr "Polecenie używane do obsługi adresów URL \"msnim\", jeśli włączone."
-#, c-format
-msgid "Accept file transfer request from %s?"
-msgstr "Zaakceptować prośbę o przesłanie pliku od %s?"
+#: ../libpurple/gconf/purple.schemas.in.h:7
+msgid "The command used to handle \"sip\" URLs, if enabled."
+msgstr "Polecenie używane do obsługi adresów URL \"sip\", jeśli włączone."
-#, c-format
-msgid ""
-"A file is available for download from:\n"
-"Remote host: %s\n"
-"Remote port: %d"
-msgstr ""
-"Plik jest dostępny do pobrania z:\n"
-"Zdalnego komputera: %s\n"
-"Portu zdalnego komputera: %d"
+#: ../libpurple/gconf/purple.schemas.in.h:8
+msgid "The command used to handle \"xmpp\" URLs, if enabled."
+msgstr "Polecenie używane do obsługi adresów URL \"xmpp\", jeśli włączone."
-#, c-format
-msgid "%s is offering to send file %s"
-msgstr "Użytkownik %s oferuje wysłanie pliku %s"
+#: ../libpurple/gconf/purple.schemas.in.h:9
+msgid "The command used to handle \"ymsgr\" URLs, if enabled."
+msgstr "Polecenie używane do obsługi adresów URL \"ymsgr\", jeśli włączone."
-#, c-format
-msgid "%s is not a valid filename.\n"
-msgstr "%s nie jest prawidłową nazwą pliku.\n"
+#: ../libpurple/gconf/purple.schemas.in.h:10
+msgid "The handler for \"aim\" URLs"
+msgstr "Obsługa adresów URL \"aim\""
-#, c-format
-msgid "Offering to send %s to %s"
-msgstr "Oferowanie wysłania %s do %s"
+#: ../libpurple/gconf/purple.schemas.in.h:11
+msgid "The handler for \"gg\" URLs"
+msgstr "Obsługa adresów URL \"gg\""
-#, c-format
-msgid "Starting transfer of %s from %s"
-msgstr "Rozpoczynanie przesyłania pliku %s od %s"
+#: ../libpurple/gconf/purple.schemas.in.h:12
+msgid "The handler for \"icq\" URLs"
+msgstr "Obsługa adresów URL \"icq\""
-#, c-format
-msgid "Transfer of file <A HREF=\"file://%s\">%s</A> complete"
-msgstr "Ukończono przesyłanie pliku <a href=\"file://%s\">%s</a>"
+#: ../libpurple/gconf/purple.schemas.in.h:13
+msgid "The handler for \"irc\" URLs"
+msgstr "Obsługa adresów URL \"irc\""
-#, c-format
-msgid "Transfer of file %s complete"
-msgstr "Ukończono przesyłanie pliku %s"
+#: ../libpurple/gconf/purple.schemas.in.h:14
+msgid "The handler for \"msnim\" URLs"
+msgstr "Obsługa adresów URL \"msnim\""
-msgid "File transfer complete"
-msgstr "Ukończono przesyłanie pliku"
+#: ../libpurple/gconf/purple.schemas.in.h:15
+msgid "The handler for \"sip\" URLs"
+msgstr "Obsługa adresów URL \"sip\""
-#, c-format
-msgid "You cancelled the transfer of %s"
-msgstr "Anulowano przesyłanie pliku %s"
+#: ../libpurple/gconf/purple.schemas.in.h:16
+msgid "The handler for \"xmpp\" URLs"
+msgstr "Obsługa adresów URL \"xmpp\""
-msgid "File transfer cancelled"
-msgstr "Anulowano przesyłanie pliku"
+#: ../libpurple/gconf/purple.schemas.in.h:17
+msgid "The handler for \"ymsgr\" URLs"
+msgstr "Obsługa adresów URL \"ymsgr\""
-#, c-format
-msgid "%s cancelled the transfer of %s"
-msgstr "Użytkownik %s anulował przesyłanie pliku %s"
+#: ../libpurple/gconf/purple.schemas.in.h:18
+msgid ""
+"True if the command specified in the \"command\" key should handle \"aim\" "
+"URLs."
+msgstr "Wartość \"prawda\", jeśli polecenie podane w kluczu \"command\" powinno obsługiwać adresy URL \"aim\"."
-#, c-format
-msgid "%s cancelled the file transfer"
-msgstr "Użytkownik %s anulował przesyłanie pliku"
+#: ../libpurple/gconf/purple.schemas.in.h:19
+msgid ""
+"True if the command specified in the \"command\" key should handle \"gg\" "
+"URLs."
+msgstr "Wartość \"prawda\", jeśli polecenie podane w kluczu \"command\" powinno obsługiwać adresy URL \"gg\"."
-#, c-format
-msgid "File transfer to %s failed."
-msgstr "Przesyłanie pliku do %s się nie powiodło."
+#: ../libpurple/gconf/purple.schemas.in.h:20
+msgid ""
+"True if the command specified in the \"command\" key should handle \"icq\" "
+"URLs."
+msgstr "Wartość \"prawda\", jeśli polecenie podane w kluczu \"command\" powinno obsługiwać adresy URL \"icq\"."
-#, c-format
-msgid "File transfer from %s failed."
-msgstr "Przesyłanie pliku od %s się nie powiodło."
+#: ../libpurple/gconf/purple.schemas.in.h:21
+msgid ""
+"True if the command specified in the \"command\" key should handle \"irc\" "
+"URLs."
+msgstr "Wartość \"prawda\", jeśli polecenie podane w kluczu \"command\" powinno obsługiwać adresy URL \"irc\"."
-msgid "Whether the specified command should handle \"aim\" URLs"
-msgstr "Czy podane polecenie powinno obsługiwać adresy URL \"aim\""
+#: ../libpurple/gconf/purple.schemas.in.h:22
+msgid ""
+"True if the command specified in the \"command\" key should handle \"msnim\""
+" URLs."
+msgstr "Wartość \"prawda\", jeśli polecenie podane w kluczu \"command\" powinno obsługiwać adresy URL \"msnim\"."
+#: ../libpurple/gconf/purple.schemas.in.h:23
msgid ""
-"True if the command specified in the \"command\" key should handle \"aim\" "
+"True if the command specified in the \"command\" key should handle \"sip\" "
"URLs."
-msgstr ""
-"Wartość \"prawda\", jeśli polecenie podane w kluczu \"command\" powinno "
-"obsługiwać adresy URL \"aim\"."
+msgstr "Wartość \"prawda\", jeśli polecenie podane w kluczu \"command\" powinno obsługiwać adresy URL \"sip\"."
-msgid "The handler for \"aim\" URLs"
-msgstr "Obsługa adresów URL \"aim\""
-
-msgid "The command used to handle \"aim\" URLs, if enabled."
-msgstr "Polecenie używane do obsługi adresów URL \"aim\", jeśli włączone."
+#: ../libpurple/gconf/purple.schemas.in.h:24
+msgid ""
+"True if the command specified in the \"command\" key should handle \"xmpp\" "
+"URLs."
+msgstr "Wartość \"prawda\", jeśli polecenie podane w kluczu \"command\" powinno obsługiwać adresy URL \"xmpp\"."
-msgid "Run the command in a terminal"
-msgstr "Wykonanie polecenie w terminalu"
+#: ../libpurple/gconf/purple.schemas.in.h:25
+msgid ""
+"True if the command specified in the \"command\" key should handle \"ymsgr\""
+" URLs."
+msgstr "Wartość \"prawda\", jeśli polecenie podane w kluczu \"command\" powinno obsługiwać adresy URL \"ymsgr\"."
+#: ../libpurple/gconf/purple.schemas.in.h:26
msgid ""
"True if the command used to handle this type of URL should be run in a "
"terminal."
-msgstr ""
-"Wartość \"prawda\", jeśli polecenie użyte do obsługi tego typu adresów URL "
-"powinno być wykonywane w terminalu."
+msgstr "Wartość \"prawda\", jeśli polecenie użyte do obsługi tego typu adresów URL powinno być wykonywane w terminalu."
+
+#: ../libpurple/gconf/purple.schemas.in.h:27
+msgid "Whether the specified command should handle \"aim\" URLs"
+msgstr "Czy podane polecenie powinno obsługiwać adresy URL \"aim\""
+#: ../libpurple/gconf/purple.schemas.in.h:28
msgid "Whether the specified command should handle \"gg\" URLs"
msgstr "Czy podane polecenie powinno obsługiwać adresy URL \"gg\""
-msgid ""
-"True if the command specified in the \"command\" key should handle \"gg\" "
-"URLs."
-msgstr ""
-"Wartość \"prawda\", jeśli polecenie podane w kluczu \"command\" powinno "
-"obsługiwać adresy URL \"gg\"."
+#: ../libpurple/gconf/purple.schemas.in.h:29
+msgid "Whether the specified command should handle \"icq\" URLs"
+msgstr "Czy podane polecenie powinno obsługiwać adresy URL \"icq\""
-msgid "The handler for \"gg\" URLs"
-msgstr "Obsługa adresów URL \"gg\""
+#: ../libpurple/gconf/purple.schemas.in.h:30
+msgid "Whether the specified command should handle \"irc\" URLs"
+msgstr "Czy podane polecenie powinno obsługiwać adresy URL \"irc\""
-msgid "The command used to handle \"gg\" URLs, if enabled."
-msgstr "Polecenie używane do obsługi adresów URL \"gg\", jeśli włączone."
+#: ../libpurple/gconf/purple.schemas.in.h:31
+msgid "Whether the specified command should handle \"msnim\" URLs"
+msgstr "Czy podane polecenie powinno obsługiwać adresy URL \"msnim\""
-msgid "Whether the specified command should handle \"icq\" URLs"
-msgstr "Czy podane polecenie powinno obsługiwać adresy URL \"icq\""
+#: ../libpurple/gconf/purple.schemas.in.h:32
+msgid "Whether the specified command should handle \"sip\" URLs"
+msgstr "Czy podane polecenie powinno obsługiwać adresy URL \"sip\""
-msgid ""
-"True if the command specified in the \"command\" key should handle \"icq\" "
-"URLs."
-msgstr ""
-"Wartość \"prawda\", jeśli polecenie podane w kluczu \"command\" powinno "
-"obsługiwać adresy URL \"icq\"."
+#: ../libpurple/gconf/purple.schemas.in.h:33
+msgid "Whether the specified command should handle \"xmpp\" URLs"
+msgstr "Czy podane polecenie powinno obsługiwać adresy URL \"xmpp\""
-msgid "The handler for \"icq\" URLs"
-msgstr "Obsługa adresów URL \"icq\""
+#: ../libpurple/gconf/purple.schemas.in.h:34
+msgid "Whether the specified command should handle \"ymsgr\" URLs"
+msgstr "Czy podane polecenie powinno obsługiwać adresy URL \"ymsgr\""
-msgid "The command used to handle \"icq\" URLs, if enabled."
-msgstr "Polecenie używane do obsługi adresów URL \"icq\", jeśli włączone."
+#: ../libpurple/http.c:838 ../libpurple/http.c:846 ../libpurple/http.c:886
+#: ../libpurple/http.c:902 ../libpurple/http.c:1001 ../libpurple/http.c:1034
+#: ../libpurple/http.c:1049 ../libpurple/http.c:1110 ../libpurple/http.c:1135
+#: ../libpurple/http.c:1201 ../libpurple/http.c:1249
+#: ../libpurple/protocols/msn/session.c:379
+msgid "Error parsing HTTP"
+msgstr "Błąd podczas przetwarzania HTTP"
-msgid "Whether the specified command should handle \"irc\" URLs"
-msgstr "Czy podane polecenie powinno obsługiwać adresy URL \"irc\""
+#: ../libpurple/http.c:936
+msgid "Error while decompressing data"
+msgstr "Błąd podczas dekompresowania danych"
-msgid ""
-"True if the command specified in the \"command\" key should handle \"irc\" "
-"URLs."
-msgstr ""
-"Wartość \"prawda\", jeśli polecenie podane w kluczu \"command\" powinno "
-"obsługiwać adresy URL \"irc\"."
+#: ../libpurple/http.c:970
+msgid "Error handling retrieved data"
+msgstr "Błąd podczas obsługiwania pobranych danych"
-msgid "The handler for \"irc\" URLs"
-msgstr "Obsługa adresów URL \"irc\""
+#: ../libpurple/http.c:1092
+#, c-format
+msgid "Error reading from %s: %s"
+msgstr "Błąd podczas odczytywania z %s: %s"
-msgid "The command used to handle \"irc\" URLs, if enabled."
-msgstr "Polecenie używane do obsługi adresów URL \"irc\", jeśli włączone."
+#: ../libpurple/http.c:1228
+msgid "Invalid proxy credentials"
+msgstr "Nieprawidłowe dane uwierzytelniające pośrednika"
-msgid "Whether the specified command should handle \"msnim\" URLs"
-msgstr "Czy podane polecenie powinno obsługiwać adresy URL \"msnim\""
+#: ../libpurple/http.c:1282
+msgid "Error requesting data to write"
+msgstr "Błąd podczas żądania danych do zapisu"
-msgid ""
-"True if the command specified in the \"command\" key should handle \"msnim\" "
-"URLs."
-msgstr ""
-"Wartość \"prawda\", jeśli polecenie podane w kluczu \"command\" powinno "
-"obsługiwać adresy URL \"msnim\"."
+#: ../libpurple/http.c:1370
+#, c-format
+msgid "Error writing to %s: %s"
+msgstr "Błąd podczas zapisywania do %s: %s"
-msgid "The handler for \"msnim\" URLs"
-msgstr "Obsługa adresów URL \"msnim\""
+#: ../libpurple/http.c:1435 ../libpurple/http.c:1477
+#, c-format
+msgid "Unable to connect to %s: %s"
+msgstr "Nie można połączyć z %s: %s"
-msgid "The command used to handle \"msnim\" URLs, if enabled."
-msgstr "Polecenie używane do obsługi adresów URL \"msnim\", jeśli włączone."
+#: ../libpurple/http.c:1471
+#, c-format
+msgid "Unsupported protocol: %s"
+msgstr "Nieobsługiwany protokół: %s"
-msgid "Whether the specified command should handle \"sip\" URLs"
-msgstr "Czy podane polecenie powinno obsługiwać adresy URL \"sip\""
+#: ../libpurple/http.c:1478 ../libpurple/protocols/jabber/jabber.c:251
+msgid "Server requires TLS/SSL, but no TLS/SSL support was found."
+msgstr "Serwer wymaga TLS/SSL, ale ich obsługa nie została odnaleziona."
-msgid ""
-"True if the command specified in the \"command\" key should handle \"sip\" "
-"URLs."
-msgstr ""
-"Wartość \"prawda\", jeśli polecenie podane w kluczu \"command\" powinno "
-"obsługiwać adresy URL \"sip\"."
+#: ../libpurple/http.c:1493
+#, c-format
+msgid "Unable to connect to %s"
+msgstr "Nie można połączyć z %s"
-msgid "The handler for \"sip\" URLs"
-msgstr "Obsługa adresów URL \"sip\""
+#: ../libpurple/http.c:2794
+#, c-format
+msgid "Unknown HTTP error"
+msgstr "Nieznany błąd HTTP"
-msgid "The command used to handle \"sip\" URLs, if enabled."
-msgstr "Polecenie używane do obsługi adresów URL \"sip\", jeśli włączone."
+#: ../libpurple/http.c:2797
+#, c-format
+msgid "Invalid HTTP response code (%d)"
+msgstr "Nieprawidłowy kod odpowiedzi HTTP (%d)"
-msgid "Whether the specified command should handle \"xmpp\" URLs"
-msgstr "Czy podane polecenie powinno obsługiwać adresy URL \"xmpp\""
+#: ../libpurple/keyring.c:245
+#, c-format
+msgid "An unknown error has occured."
+msgstr "Wystąpił nieznany błąd."
-msgid ""
-"True if the command specified in the \"command\" key should handle \"xmpp\" "
-"URLs."
-msgstr ""
-"Wartość \"prawda\", jeśli polecenie podane w kluczu \"command\" powinno "
-"obsługiwać adresy URL \"xmpp\"."
+#: ../libpurple/keyring.c:384
+#, c-format
+msgid "There is a password migration session already running."
+msgstr "Sesja migracji haseł jest już uruchomiona."
-msgid "The handler for \"xmpp\" URLs"
-msgstr "Obsługa adresów URL \"xmpp\""
+#: ../libpurple/keyring.c:547
+#, c-format
+msgid "%s (disabled)"
+msgstr "%s (wyłączone)"
-msgid "The command used to handle \"xmpp\" URLs, if enabled."
-msgstr "Polecenie używane do obsługi adresów URL \"xmpp\", jeśli włączone."
+#: ../libpurple/keyring.c:684
+#, c-format
+msgid "Specified keyring is not registered."
+msgstr "Podana baza kluczy nie jest zarejestrowana."
-msgid "Whether the specified command should handle \"ymsgr\" URLs"
-msgstr "Czy podane polecenie powinno obsługiwać adresy URL \"ymsgr\""
+#: ../libpurple/keyring.c:698
+#, c-format
+msgid "No keyring loaded, cannot import password info."
+msgstr "Żadna baza kluczy nie jest wczytana, nie można zaimportować informacji o haśle."
-msgid ""
-"True if the command specified in the \"command\" key should handle \"ymsgr\" "
-"URLs."
-msgstr ""
-"Wartość \"prawda\", jeśli polecenie podane w kluczu \"command\" powinno "
-"obsługiwać adresy URL \"ymsgr\"."
+#: ../libpurple/keyring.c:718
+#, c-format
+msgid "Specified keyring id does not match the loaded one."
+msgstr "Podany identyfikator bazy kluczy nie pasuje do wczytanego."
-msgid "The handler for \"ymsgr\" URLs"
-msgstr "Obsługa adresów URL \"ymsgr\""
+#: ../libpurple/keyring.c:764
+#, c-format
+msgid "No keyring configured, cannot export password info."
+msgstr "Żadna baza kluczy nie jest skonfigurowana, nie można wyeksportować informacji o haśle."
-msgid "The command used to handle \"ymsgr\" URLs, if enabled."
-msgstr "Polecenie używane do obsługi adresów URL \"ymsgr\", jeśli włączone."
+#: ../libpurple/keyring.c:825
+#, c-format
+msgid "Cannot request a password while quitting."
+msgstr "Nie można zażądać hasła podczas kończenia działania."
+
+#: ../libpurple/keyring.c:840 ../libpurple/keyring.c:927
+#, c-format
+msgid "No keyring configured."
+msgstr "Żadna baza kluczy nie jest skonfigurowana."
+
+#: ../libpurple/keyring.c:874 ../libpurple/keyring.c:1209
+msgid "Keyrings"
+msgstr "Bazy kluczy"
+
+#: ../libpurple/keyring.c:875
+msgid "Failed to save a password in keyring."
+msgstr "Zapisanie hasła w bazie kluczy się nie powiodło."
+
+#: ../libpurple/keyring.c:902
+#, c-format
+msgid "Cannot save a password while quitting."
+msgstr "Nie można zapisać hasła podczas kończenia działania."
+#: ../libpurple/keyring.c:915
+#, c-format
+msgid "Cannot save a password during password migration."
+msgstr "Nie można zapisać hasła podczas migracji haseł."
+
+#: ../libpurple/keyring.c:1210
+msgid "Failed to load selected keyring."
+msgstr "Wczytanie wybranej bazy kluczy się nie powiodło."
+
+#: ../libpurple/keyring.c:1211
+msgid ""
+"Check your system configuration or select another one in Preferences dialog."
+msgstr "Proszę sprawdzić konfigurację systemu lub wybrać inną w oknie Preferencji."
+
+#: ../libpurple/log.c:193
msgid "<b><font color=\"red\">The logger has no read function</font></b>"
-msgstr "<b><font color=\"red\">Dziennik nie ma funkcji odczytu</font></b>"
+msgstr "<b><font color=\"red\">Wtyczka archiwum nie ma funkcji odczytu</font></b>"
+#: ../libpurple/log.c:656
msgid "HTML"
msgstr "HTML"
+#: ../libpurple/log.c:670
msgid "Plain text"
msgstr "Zwykły tekst"
+#: ../libpurple/log.c:684
msgid "Old flat format"
msgstr "Poprzedni płaski format"
+#: ../libpurple/log.c:946
msgid "Logging of this conversation failed."
-msgstr "Zapisanie tej rozmowy do dziennika się nie powiodło."
+msgstr "Zapisanie tej rozmowy do archiwum się nie powiodło."
+#: ../libpurple/log.c:1389
msgid "XML"
msgstr "XML"
+#: ../libpurple/log.c:1473
#, c-format
msgid ""
"<font color=\"#16569E\"><font size=\"2\">(%s)</font> <b>%s &lt;AUTO-"
"REPLY&gt;:</b></font> %s<br/>\n"
-msgstr ""
-"<font color=\"#16569E\"><font size=\"2\">(%s)</font> <b>%s &lt;ODPOWIEDŹ-"
-"AUTOMATYCZNA&gt;:</b></font> %s<br/>\n"
+msgstr "<font color=\"#16569E\"><font size=\"2\">(%s)</font> <b>%s &lt;ODPOWIEDŹ-AUTOMATYCZNA&gt;:</b></font> %s<br/>\n"
+#: ../libpurple/log.c:1475
#, c-format
msgid ""
"<font color=\"#A82F2F\"><font size=\"2\">(%s)</font> <b>%s &lt;AUTO-"
"REPLY&gt;:</b></font> %s<br/>\n"
-msgstr ""
-"<font color=\"#A82F2F\"><font size=\"2\">(%s)</font> <b>%s &lt;ODPOWIEDŹ-"
-"AUTOMATYCZNA&gt;:</b></font> %s<br/>\n"
+msgstr "<font color=\"#A82F2F\"><font size=\"2\">(%s)</font> <b>%s &lt;ODPOWIEDŹ-AUTOMATYCZNA&gt;:</b></font> %s<br/>\n"
+#: ../libpurple/log.c:1534 ../libpurple/log.c:1665
msgid "<font color=\"red\"><b>Unable to find log path!</b></font>"
-msgstr ""
-"<font color=\"red\"><b>Nie można odnaleźć ścieżki dziennika.</b></font>"
+msgstr "<font color=\"red\"><b>Nie można odnaleźć ścieżki archiwum.</b></font>"
+#: ../libpurple/log.c:1546 ../libpurple/log.c:1674
#, c-format
msgid "<font color=\"red\"><b>Could not read file: %s</b></font>"
msgstr "<font color=\"red\"><b>Nie można odczytać pliku: %s</b></font>"
+#: ../libpurple/log.c:1608
#, c-format
msgid "(%s) %s <AUTO-REPLY>: %s\n"
msgstr "(%s) %s <ODPOWIEDŹ-AUTOMATYCZNA>: %s\n"
+#: ../libpurple/media/backend-fs2.c:905
+msgid ""
+"\n"
+"\n"
+"Message from Farsight: "
+msgstr "\n\nKomunikat z biblioteki Farsight: "
+
+#: ../libpurple/media/backend-fs2.c:988
+msgid ""
+"Error initializing the call. This probably denotes problem in installation "
+"of GStreamer or Farsight."
+msgstr "Błąd podczas inicjowania rozmowy. Prawdopodobnie oznacza to błąd w instalacji biblioteki GStreamer lub Farsight."
+
+#: ../libpurple/media/backend-fs2.c:999
+msgid "Network error."
+msgstr "Błąd sieci."
+
+#: ../libpurple/media/backend-fs2.c:1006
+msgid ""
+"Codec negotiation failed. This problem might be resolved by installingmore "
+"GStreamer codecs."
+msgstr "Negocjacja kodeka się nie powiodła. Ten problem może zostać rozwiązany przez zainstalowanie większej liczby kodeków biblioteki GStreamer."
+
+#: ../libpurple/media/backend-fs2.c:1015
msgid ""
-"No codecs found. Install some GStreamer codecs found in GStreamer plugins "
+"No codecs found. Install some GStreamer codecs found in GStreamer plugins "
"packages."
-msgstr ""
-"Nie odnaleziono kodeków. Proszę zainstalować kodeki biblioteki GStreamer z "
-"pakietów wtyczek tej biblioteki."
+msgstr "Nie odnaleziono kodeków. Proszę zainstalować kodeki biblioteki GStreamer z jej pakietów wtyczek."
+#: ../libpurple/media/backend-fs2.c:1024
msgid ""
"No codecs left. Your codec preferences in fs-codecs.conf are too strict."
-msgstr ""
-"Nie pozostały żadne kodeki. Preferencje kodeków w pliku fs-codecs.conf są za "
-"ścisłe."
+msgstr "Nie pozostały żadne kodeki. Preferencje kodeków w pliku fs-codecs.conf są za ścisłe."
+
+#: ../libpurple/media/backend-fs2.c:1031
+msgid "Could not connect to the remote party"
+msgstr "Nie można połączyć się ze zdalną częścią"
+#: ../libpurple/media/backend-fs2.c:1058
msgid "A non-recoverable Farsight2 error has occurred."
msgstr "Wystąpił nieodwracalny błąd biblioteki Farsight2."
+#: ../libpurple/media/backend-fs2.c:1061
msgid "A non-recoverable Farstream error has occurred."
msgstr "Wystąpił błąd systemu Farstream, z którego nie można przywrócić."
+#: ../libpurple/media/backend-fs2.c:1327
msgid "Error with your microphone"
msgstr "Błąd mikrofonu"
+#: ../libpurple/media/backend-fs2.c:1330
msgid "Error with your webcam"
msgstr "Błąd kamery internetowej"
+#: ../libpurple/media/backend-fs2.c:1337
msgid "Conference error"
msgstr "Błąd konferencji"
+#: ../libpurple/media/backend-fs2.c:1753
#, c-format
msgid "Error creating session: %s"
msgstr "Błąd podczas tworzenia sesji: %s"
+#: ../libpurple/message.c:76
+msgid "Me"
+msgstr "Ja"
+
+#: ../libpurple/plugin.c:348
#, c-format
msgid "You are using %s, but this plugin requires %s."
msgstr "Używane jest %s, ale ta wtyczka wymaga %s."
+#: ../libpurple/plugin.c:363
msgid "This plugin has not defined an ID."
msgstr "Ta wtyczka nie określiła identyfikatora."
+#: ../libpurple/plugin.c:431
#, c-format
msgid "Plugin magic mismatch %d (need %d)"
msgstr "Magic wtyczki nie zgadza się %d (wymagane %d)"
+#: ../libpurple/plugin.c:448
#, c-format
msgid "ABI version mismatch %d.%d.x (need %d.%d.x)"
msgstr "Wersji ABI nie zgadza się %d.%d.x (wymagane %d.%d.x)"
+#: ../libpurple/plugin.c:465
msgid ""
-"Plugin does not implement all required functions (list_icon, login and close)"
-msgstr ""
-"Wtyczka nie implementuje wszystkich wymaganych funkcji (list_icon, login i "
+"Plugin does not implement all required functions (list_icon, login and "
"close)"
+msgstr "Wtyczka nie implementuje wszystkich wymaganych funkcji (list_icon, login i close)"
+#: ../libpurple/plugin.c:530
#, c-format
msgid ""
"The required plugin %s was not found. Please install this plugin and try "
"again."
-msgstr ""
-"Wymagana wtyczka %s nie została odnaleziona. Proszę ją zainstalować i "
-"spróbować ponownie."
+msgstr "Wymagana wtyczka %s nie została odnaleziona. Proszę ją zainstalować i spróbować ponownie."
+#: ../libpurple/plugin.c:535
msgid "Unable to load the plugin"
msgstr "Nie można wczytać wtyczki"
+#: ../libpurple/plugin.c:557
#, c-format
msgid "The required plugin %s was unable to load."
msgstr "Nie można wczytać wymaganej wtyczki %s."
+#: ../libpurple/plugin.c:561
msgid "Unable to load your plugin."
msgstr "Nie można wczytać wtyczki."
+#: ../libpurple/plugin.c:645
#, c-format
msgid "%s requires %s, but it failed to unload."
msgstr "%s wymaga %s, ale jej wyłączenie się nie powiodło."
+#: ../libpurple/plugins/autoaccept.c:23
msgid "Autoaccept"
msgstr "Automatyczne akceptowanie"
+#: ../libpurple/plugins/autoaccept.c:25 ../libpurple/plugins/autoaccept.c:26
msgid "Auto-accept file transfer requests from selected users."
-msgstr ""
-"Automatycznie akceptuje prośby o przesyłanie plików od wybranych "
-"użytkowników."
+msgstr "Automatycznie akceptuje prośby o przesyłanie plików od wybranych użytkowników."
+#: ../libpurple/plugins/autoaccept.c:78
#, c-format
msgid "Autoaccepted file transfer of \"%s\" from \"%s\" completed."
-msgstr ""
-"Ukończono automatycznie zaakceptowane przesyłanie pliku \"%s\" od \"%s\"."
+msgstr "Ukończono automatycznie zaakceptowane przesyłanie pliku \"%s\" od \"%s\"."
+#: ../libpurple/plugins/autoaccept.c:80
msgid "Autoaccept complete"
msgstr "Ukończono automatyczne akceptowanie"
+#: ../libpurple/plugins/autoaccept.c:202
#, c-format
msgid "When a file-transfer request arrives from %s"
msgstr "Pliki przysyłane przez %s"
+#: ../libpurple/plugins/autoaccept.c:204
msgid "Set Autoaccept Setting"
msgstr "Ustawienia automatycznego akceptowania"
+#: ../libpurple/plugins/autoaccept.c:206
msgid "_Save"
msgstr "Zapi_sz"
+#: ../libpurple/plugins/autoaccept.c:207 ../libpurple/plugins/idle.c:167
+#: ../libpurple/plugins/idle.c:200 ../libpurple/plugins/idle.c:225
+#: ../libpurple/protocols/mxit/actions.c:495
+#: ../libpurple/protocols/oscar/oscar.c:1048
+#: ../libpurple/protocols/oscar/oscar.c:4758
+#: ../libpurple/protocols/oscar/oscar.c:4815
+#: ../libpurple/protocols/oscar/oscar.c:5098
+#: ../libpurple/protocols/oscar/oscar.c:5155 ../libpurple/request.h:2180
+#: ../libpurple/request.h:2191 ../pidgin/gtkrequest.c:341
msgid "_Cancel"
msgstr "_Anuluj"
+#: ../libpurple/plugins/autoaccept.c:209 ../libpurple/plugins/autoaccept.c:278
msgid "Ask"
msgstr "Zapytaj"
+#: ../libpurple/plugins/autoaccept.c:210 ../libpurple/plugins/autoaccept.c:279
msgid "Auto Accept"
msgstr "Automatycznie zaakceptuj"
+#: ../libpurple/plugins/autoaccept.c:211 ../libpurple/plugins/autoaccept.c:280
msgid "Auto Reject"
msgstr "Automatycznie odrzuć"
+#: ../libpurple/plugins/autoaccept.c:225
msgid "Autoaccept File Transfers..."
msgstr "Automatycznie akceptowane przesyłania plików..."
#. XXX: Is there a better way than this? There really should be.
+#: ../libpurple/plugins/autoaccept.c:270
msgid ""
"Path to save the files in\n"
"(Please provide the full path)"
-msgstr ""
-"Ścieżka do zapisywania plików w\n"
-"(proszę podać pełną ścieżkę)"
+msgstr "Ścieżka do zapisywania plików w\n(proszę podać pełną ścieżkę)"
+#: ../libpurple/plugins/autoaccept.c:275
msgid ""
"When a file-transfer request arrives from a user who is\n"
"*not* on your buddy list:"
-msgstr ""
-"Kiedy żądanie przesłania pliku zostaje odebrane od\n"
-"użytkownika, który *nie* jest na liście znajomych:"
+msgstr "Kiedy żądanie przesłania pliku zostaje odebrane od\nużytkownika, który *nie* jest na liście znajomych:"
+#: ../libpurple/plugins/autoaccept.c:284
msgid ""
"Notify with a popup when an autoaccepted file transfer is complete\n"
"(only when there's no conversation with the sender)"
-msgstr ""
-"Powiadomienie, kiedy przesłanie automatycznie zaakceptowanego pliku\n"
-"zostanie ukończone (tylko wtedy, kiedy nie jest prowadzona rozmowa z nadawcą)"
+msgstr "Powiadomienie, kiedy przesłanie automatycznie zaakceptowanego pliku\nzostanie ukończone (tylko wtedy, kiedy nie jest prowadzona rozmowa z nadawcą)"
+#: ../libpurple/plugins/autoaccept.c:289
msgid "Create a new directory for each user"
msgstr "Utworzenie nowego katalogu dla każdego użytkownika"
+#: ../libpurple/plugins/autoaccept.c:293
msgid "Escape the filenames"
msgstr "Sekwencja sterująca przed nazwami plików"
+#: ../libpurple/plugins/buddynote.c:46 ../libpurple/protocols/msn/msn.c:2479
+#: ../libpurple/protocols/msn/msn.c:2509
msgid "Notes"
msgstr "Notatki"
+#: ../libpurple/plugins/buddynote.c:47
msgid "Enter your notes below..."
msgstr "Poniżej można wpisywać notatki..."
+#: ../libpurple/plugins/buddynote.c:64
msgid "Edit Notes..."
msgstr "Zmodyfikuj notatki..."
@@ -2423,15 +3286,18 @@ msgstr "Zmodyfikuj notatki..."
#. *< dependencies
#. *< priority
#. *< id
+#: ../libpurple/plugins/buddynote.c:89 ../pidgin/plugins/gtkbuddynote.c:76
msgid "Buddy Notes"
msgstr "Notatki o znajomych"
#. *< name
#. *< version
+#: ../libpurple/plugins/buddynote.c:91 ../pidgin/plugins/gtkbuddynote.c:78
msgid "Store notes on particular buddies."
msgstr "Przechowywanie notatek o poszczególnych znajomych."
#. *< summary
+#: ../libpurple/plugins/buddynote.c:92 ../pidgin/plugins/gtkbuddynote.c:79
msgid "Adds the option to store notes for buddies on your buddy list."
msgstr "Dodaje opcję przechowywania notatek o znajomych z listy."
@@ -2441,6 +3307,7 @@ msgstr "Dodaje opcję przechowywania notatek o znajomych z listy."
#. *< dependencies
#. *< priority
#. *< id
+#: ../libpurple/plugins/ciphertest.c:606
msgid "Cipher Test"
msgstr "Test szyfru"
@@ -2448,6 +3315,7 @@ msgstr "Test szyfru"
#. *< version
#. * summary
#. * description
+#: ../libpurple/plugins/ciphertest.c:609 ../libpurple/plugins/ciphertest.c:611
msgid "Tests the ciphers that ship with libpurple."
msgstr "Testuje szyfry dostarczane z biblioteką libpurple."
@@ -2457,6 +3325,7 @@ msgstr "Testuje szyfry dostarczane z biblioteką libpurple."
#. *< dependencies
#. *< priority
#. *< id
+#: ../libpurple/plugins/dbus-example.c:156
msgid "DBus Example"
msgstr "Przykład D-Bus"
@@ -2464,6 +3333,8 @@ msgstr "Przykład D-Bus"
#. *< version
#. * summary
#. * description
+#: ../libpurple/plugins/dbus-example.c:159
+#: ../libpurple/plugins/dbus-example.c:161
msgid "DBus Plugin Example"
msgstr "Przykładowa wtyczka D-Bus"
@@ -2473,6 +3344,7 @@ msgstr "Przykładowa wtyczka D-Bus"
#. *< dependencies
#. *< priority
#. *< id
+#: ../libpurple/plugins/filectl.c:248
msgid "File Control"
msgstr "Kontrola plikiem"
@@ -2480,38 +3352,51 @@ msgstr "Kontrola plikiem"
#. *< version
#. * summary
#. * description
+#: ../libpurple/plugins/filectl.c:251 ../libpurple/plugins/filectl.c:253
msgid "Allows control by entering commands in a file."
msgstr "Umożliwia kontrolowanie programu przez podawanie poleceń do pliku."
+#: ../libpurple/plugins/idle.c:155 ../libpurple/plugins/idle.c:213
msgid "Minutes"
msgstr "Minuty"
#. This is a cultural reference. Dy'er Mak'er is a song by Led Zeppelin.
-#. If that doesn't translate well into your language, drop the 's before translating.
+#. If that doesn't translate well into your language, drop the 's before
+#. translating.
+#: ../libpurple/plugins/idle.c:162 ../libpurple/plugins/idle.c:195
+#: ../libpurple/plugins/idle.c:220 ../libpurple/plugins/idle.c:312
msgid "I'dle Mak'er"
msgstr "Udawanie bezczynności"
+#: ../libpurple/plugins/idle.c:163 ../libpurple/plugins/idle.c:252
msgid "Set Account Idle Time"
msgstr "Ustawianie czasu bezczynności konta"
+#: ../libpurple/plugins/idle.c:166 ../libpurple/plugins/idle.c:224
msgid "_Set"
msgstr "U_staw"
+#: ../libpurple/plugins/idle.c:180
msgid "None of your accounts are idle."
msgstr "Żadne konto nie jest w stanie bezczynności."
+#: ../libpurple/plugins/idle.c:196 ../libpurple/plugins/idle.c:256
msgid "Unset Account Idle Time"
msgstr "Usuwanie ustawienia czasu bezczynności konta"
+#: ../libpurple/plugins/idle.c:199
msgid "_Unset"
msgstr "_Usuń ustawienie"
+#: ../libpurple/plugins/idle.c:221 ../libpurple/plugins/idle.c:260
msgid "Set Idle Time for All Accounts"
msgstr "Ustawienie czasu bezczynności wszystkich kont"
+#: ../libpurple/plugins/idle.c:265
msgid "Unset Idle Time for All Idled Accounts"
msgstr "Usunięcie ustawienia czasu bezczynności wszystkich kont"
+#: ../libpurple/plugins/idle.c:314 ../libpurple/plugins/idle.c:315
msgid "Allows you to hand-configure how long you've been idle"
msgstr "Umożliwia ręczną konfigurację czasu bezczynności"
@@ -2521,22 +3406,23 @@ msgstr "Umożliwia ręczną konfigurację czasu bezczynności"
#. *< dependencies
#. *< priority
#. *< id
+#: ../libpurple/plugins/ipc-test-client.c:87
msgid "IPC Test Client"
msgstr "Klient testowy IPC"
#. *< name
#. *< version
#. * summary
+#: ../libpurple/plugins/ipc-test-client.c:90
msgid "Test plugin IPC support, as a client."
msgstr "Testowanie obsługi wtyczki IPC jako klient."
#. * description
+#: ../libpurple/plugins/ipc-test-client.c:92
msgid ""
"Test plugin IPC support, as a client. This locates the server plugin and "
"calls the commands registered."
-msgstr ""
-"Testowanie obsługi wtyczki IPC jako klient. Ustala położenie wtyczki serwera "
-"i wywołuje zarejestrowane polecenia."
+msgstr "Testowanie obsługi wtyczki IPC jako klient. Ustala położenie wtyczki serwera i wywołuje zarejestrowane polecenia."
#. *< type
#. *< ui_requirement
@@ -2544,29 +3430,36 @@ msgstr ""
#. *< dependencies
#. *< priority
#. *< id
+#: ../libpurple/plugins/ipc-test-server.c:74
msgid "IPC Test Server"
msgstr "Serwer testowy IPC"
#. *< name
#. *< version
#. * summary
+#: ../libpurple/plugins/ipc-test-server.c:77
msgid "Test plugin IPC support, as a server."
msgstr "Testowanie obsługi wtyczki IPC jako serwer."
#. * description
+#: ../libpurple/plugins/ipc-test-server.c:79
msgid "Test plugin IPC support, as a server. This registers the IPC commands."
msgstr "Testowanie obsługi wtyczki IPC jako serwer. Rejestruje polecenia IPC."
+#: ../libpurple/plugins/joinpart.c:232
msgid "Hide Joins/Parts"
msgstr "Ukrywanie wejść/wyjść"
#. Translators: Followed by an input request a number of people
+#: ../libpurple/plugins/joinpart.c:237
msgid "For rooms with more than this many people"
msgstr "Dla pokoi z więcej, niż podaną, liczbą osób"
+#: ../libpurple/plugins/joinpart.c:242
msgid "If user has not spoken in this many minutes"
msgstr "Jeśli użytkownik nie odzywał się w ciągu tylu minut"
+#: ../libpurple/plugins/joinpart.c:247
msgid "Apply hiding rules to buddies"
msgstr "Zastosowanie reguł ukrywania do znajomych"
@@ -2576,116 +3469,372 @@ msgstr "Zastosowanie reguł ukrywania do znajomych"
#. *< dependencies
#. *< priority
#. *< id
+#: ../libpurple/plugins/joinpart.c:276
msgid "Join/Part Hiding"
msgstr "Ukrywanie wejść/wyjść"
#. *< name
#. *< version
#. * summary
+#: ../libpurple/plugins/joinpart.c:279
msgid "Hides extraneous join/part messages."
msgstr "Ukrywa dodatkowe wiadomości wejść/wyjść."
#. * description
+#: ../libpurple/plugins/joinpart.c:281
msgid ""
"This plugin hides join/part messages in large rooms, except for those users "
"actively taking part in a conversation."
-msgstr ""
-"Ta wtyczka umożliwia ukrywanie wiadomości wejść/wyjść w dużych pokojach, "
-"poza użytkownikami aktywnie uczestniczącymi w rozmowie."
+msgstr "Ta wtyczka umożliwia ukrywanie wiadomości wejść/wyjść w dużych pokojach, poza użytkownikami aktywnie uczestniczącymi w rozmowie."
+
+#: ../libpurple/plugins/keyrings/gnomekeyring.c:38
+msgid "GNOME Keyring"
+msgstr "Baza kluczy GNOME"
+
+#: ../libpurple/plugins/keyrings/gnomekeyring.c:39
+msgid "This plugin will store passwords in GNOME Keyring."
+msgstr "Ta wtyczka przechowuje hasła w bazie kluczy środowiska GNOME."
+
+#: ../libpurple/plugins/keyrings/gnomekeyring.c:103
+#, c-format
+msgid "No password found for account."
+msgstr "Nie odnaleziono hasła dla konta."
+
+#: ../libpurple/plugins/keyrings/gnomekeyring.c:109
+#: ../libpurple/plugins/keyrings/gnomekeyring.c:185
+#, c-format
+msgid "Access denied."
+msgstr "Brak dostępu."
+
+#: ../libpurple/plugins/keyrings/gnomekeyring.c:116
+#: ../libpurple/plugins/keyrings/gnomekeyring.c:192
+#, c-format
+msgid "Communication with GNOME Keyring failed."
+msgstr "Komunikacja z bazą kluczy GNOME się nie powiodła."
+
+#: ../libpurple/plugins/keyrings/gnomekeyring.c:120
+#: ../libpurple/plugins/keyrings/gnomekeyring.c:196
+#, c-format
+msgid "Unknown error (code: %d)."
+msgstr "Nieznany błąd (kod: %d)"
+
+#: ../libpurple/plugins/keyrings/gnomekeyring.c:126
+#, c-format
+msgid "Unknown error (password empty)."
+msgstr "Nieznany błąd (puste hasło)."
+
+#: ../libpurple/plugins/keyrings/gnomekeyring.c:247
+#: ../libpurple/plugins/keyrings/internalkeyring.c:436
+#, c-format
+msgid "Operation cancelled."
+msgstr "Anulowano działanie."
+
+#: ../libpurple/plugins/keyrings/gnomekeyring.c:313
+#: ../libpurple/plugins/keyrings/secretservice.c:232
+#, c-format
+msgid "Pidgin IM password for account %s"
+msgstr "Hasło komunikatora Pidgin dla konta %s"
+
+#: ../libpurple/plugins/keyrings/internalkeyring.c:38
+msgid "Internal keyring"
+msgstr "Wewnętrzna baza kluczy"
+
+#: ../libpurple/plugins/keyrings/internalkeyring.c:39
+msgid ""
+"This plugin provides the default password storage behaviour for libpurple."
+msgstr "Ta wtyczka dostarcza domyślną metodę przechowywania haseł dla biblioteki libpurple."
+
+#: ../libpurple/plugins/keyrings/internalkeyring.c:532
+#: ../libpurple/plugins/keyrings/internalkeyring.c:614
+msgid "Unlocking internal keyring"
+msgstr "Odblokowywanie wewnętrznej bazy kluczy"
+
+#: ../libpurple/plugins/keyrings/internalkeyring.c:533
+msgid "Selected encryption method is not supported."
+msgstr "Wybrana metoda szyfrowania jest nieobsługiwana."
+
+#: ../libpurple/plugins/keyrings/internalkeyring.c:534
+msgid ""
+"Most probably, your passwords were encrypted with newer Pidgin/libpurple "
+"version, please update."
+msgstr "Najprawdopodobniej hasła zostały zaszyfrowane za pomocą nowszej wersji programu Pidgin/libpurple. Proszę wykonać aktualizację."
+
+#: ../libpurple/plugins/keyrings/internalkeyring.c:543
+msgid "No password entered."
+msgstr "Nie podano żadnego hasła."
+
+#: ../libpurple/plugins/keyrings/internalkeyring.c:558
+msgid "Invalid master password entered, try again."
+msgstr "Podano nieprawidłowe główne hasło. Proszę spróbować ponownie."
+
+#: ../libpurple/plugins/keyrings/internalkeyring.c:603
+#: ../libpurple/plugins/keyrings/internalkeyring.c:868
+msgid "Master password"
+msgstr "Główne hasło"
+
+#: ../libpurple/plugins/keyrings/internalkeyring.c:607
+msgid "Please, enter master password"
+msgstr "Proszę podać główne hasło"
+
+#: ../libpurple/plugins/keyrings/internalkeyring.c:681
+#: ../libpurple/plugins/keyrings/wincred.c:94
+#, c-format
+msgid "Password not found."
+msgstr "Nie odnaleziono hasła."
+
+#: ../libpurple/plugins/keyrings/internalkeyring.c:802
+#, c-format
+msgid "Invalid password storage mode."
+msgstr "Nieprawidłowy tryb przechowywania haseł."
+
+#: ../libpurple/plugins/keyrings/internalkeyring.c:864
+msgid "Encrypt passwords"
+msgstr "Szyfrowanie haseł"
+
+#: ../libpurple/plugins/keyrings/internalkeyring.c:872
+msgid "New passphrase:"
+msgstr "Nowe hasło:"
+
+#: ../libpurple/plugins/keyrings/internalkeyring.c:877
+msgid "New passphrase (again):"
+msgstr "Nowe hasło (ponownie):"
+
+#: ../libpurple/plugins/keyrings/internalkeyring.c:881
+msgid "Advanced settings"
+msgstr "Zaawansowane ustawienia"
+
+#: ../libpurple/plugins/keyrings/internalkeyring.c:885
+msgid "Number of PBKDF2 iterations:"
+msgstr "Liczba powtórzeń PBKDF2:"
+
+#: ../libpurple/plugins/keyrings/internalkeyring.c:900
+msgid "You have to unlock the keyring first."
+msgstr "Należy najpierw odblokować bazę kluczy."
+
+#: ../libpurple/plugins/keyrings/internalkeyring.c:913
+#: ../libpurple/plugins/keyrings/internalkeyring.c:922
+#: ../libpurple/plugins/keyrings/internalkeyring.c:932
+msgid "Internal keyring settings"
+msgstr "Ustawienia wewnętrznej bazy kluczy"
+
+#: ../libpurple/plugins/keyrings/internalkeyring.c:914
+#: ../libpurple/protocols/silc/silc.c:1160
+msgid "Passphrases do not match"
+msgstr "Hasła nie pasują"
+
+#: ../libpurple/plugins/keyrings/internalkeyring.c:923
+msgid "You have to set up a Master password, if you want to enable encryption"
+msgstr "Należy ustawić główne hasło, aby włączyć szyfrowanie"
+
+#: ../libpurple/plugins/keyrings/internalkeyring.c:933
+msgid ""
+"You don't need any master password, if you won't enable passwords encryption"
+msgstr "Główne hasło nie jest potrzebne, jeśli szyfrowanie haseł nie zostanie włączone"
+
+#: ../libpurple/plugins/keyrings/kwallet.cpp:39
+msgid "KWallet"
+msgstr "KWallet"
+
+#: ../libpurple/plugins/keyrings/kwallet.cpp:40
+msgid "This plugin will store passwords in KWallet."
+msgstr "Ta wtyczka przechowuje hasła w usłudze KWallet."
+
+#: ../libpurple/plugins/keyrings/kwallet.cpp:344
+#, c-format
+msgid "Failed to save password."
+msgstr "Zapisanie hasła się nie powiodło."
+
+#: ../libpurple/plugins/keyrings/kwallet.cpp:357
+#, c-format
+msgid "Failed to read password."
+msgstr "Odczytanie hasła się nie powiodło."
+
+#: ../libpurple/plugins/keyrings/secretservice.c:46
+msgid "Secret Service"
+msgstr "Secret Service"
+
+#. magic
+#. major_version
+#. minor_version
+#. type
+#. ui_requirement
+#. flags
+#. dependencies
+#. priority
+#. id
+#. name
+#. version
+#. summary
+#: ../libpurple/plugins/keyrings/secretservice.c:318
+msgid "This plugin will store passwords in Secret Service."
+msgstr "Ta wtyczka przechowuje hasła w usłudze \"Secret Service\"."
+
+#: ../libpurple/plugins/keyrings/wincred.c:35
+msgid "Windows credentials"
+msgstr "Dane uwierzytelniające systemu Windows"
+
+#: ../libpurple/plugins/keyrings/wincred.c:36
+msgid "Store passwords using Windows credentials"
+msgstr "Przechowywanie haseł używając danych uwierzytelniających systemu Windows"
+
+#: ../libpurple/plugins/keyrings/wincred.c:37
+msgid "This plugin stores passwords using Windows credentials."
+msgstr "Ta wtyczka przechowuje hasła używając danych uwierzytelniających systemu Windows."
+
+#: ../libpurple/plugins/keyrings/wincred.c:101
+#, c-format
+msgid "Cannot read password, no valid logon session."
+msgstr "Nie można odczytać hasła, brak prawidłowej sesji logowania."
+
+#: ../libpurple/plugins/keyrings/wincred.c:109
+#, c-format
+msgid "Cannot read password (error %lx)."
+msgstr "Nie można odczytać hasła (błąd %lx)."
+
+#: ../libpurple/plugins/keyrings/wincred.c:130
+#, c-format
+msgid "Cannot read password (unicode error)."
+msgstr "Nie można odczytać hasła (błąd unikodu)."
+
+#: ../libpurple/plugins/keyrings/wincred.c:133
+#, c-format
+msgid "Got password for account %s.\n"
+msgstr "Pobrano hasło dla konta %s.\n"
+
+#: ../libpurple/plugins/keyrings/wincred.c:181
+#: ../libpurple/plugins/keyrings/wincred.c:231
+#, c-format
+msgid "Cannot remove password, no valid logon session."
+msgstr "Nie można usunąć hasła, brak prawidłowej sesji logowania."
+
+#: ../libpurple/plugins/keyrings/wincred.c:189
+#, c-format
+msgid "Cannot remove password (error %lx)."
+msgstr "Nie można usunąć hasła (błąd %lx)."
+
+#: ../libpurple/plugins/keyrings/wincred.c:239
+#, c-format
+msgid "Cannot store password (error %lx)."
+msgstr "Nie można przechować hasła (błąd %lx)."
#. This is used in the place of a timezone abbreviation if the
#. * offset is way off. The user should never really see it, but
#. * it's here just in case. The parens are to make it clear it's
#. * not a real timezone.
+#: ../libpurple/plugins/log_reader.c:495
msgid "(UTC)"
msgstr "(UTC)"
+#: ../libpurple/plugins/log_reader.c:1564
msgid "User is offline."
msgstr "Użytkownik jest w trybie offline."
+#: ../libpurple/plugins/log_reader.c:1570
msgid "Auto-response sent:"
msgstr "Wysłano automatyczną odpowiedź:"
+#: ../libpurple/plugins/log_reader.c:1585
+#: ../libpurple/plugins/log_reader.c:1588
+#: ../libpurple/plugins/statenotify.c:92
#, c-format
msgid "%s has signed off."
msgstr "Użytkownik %s rozłączył się."
+#: ../libpurple/plugins/log_reader.c:1609
msgid "One or more messages may have been undeliverable."
msgstr "Jedna lub więcej wiadomości mogła nie zostać dostarczona."
+#: ../libpurple/plugins/log_reader.c:1619
msgid "You were disconnected from the server."
msgstr "Rozłączono z serwera."
+#: ../libpurple/plugins/log_reader.c:1627
msgid ""
-"You are currently disconnected. Messages will not be received unless you are "
-"logged in."
-msgstr ""
-"Użytkownik jest teraz rozłączony. Wiadomości nie zostaną odbierane, dopóki "
-"użytkownik się nie zaloguje."
+"You are currently disconnected. Messages will not be received unless you are"
+" logged in."
+msgstr "Użytkownik jest teraz rozłączony. Wiadomości nie zostaną odbierane, dopóki użytkownik się nie zaloguje."
+#: ../libpurple/plugins/log_reader.c:1642
msgid "Message could not be sent because the maximum length was exceeded."
-msgstr ""
-"Nie można było wysłać wiadomości, ponieważ maksymalna długość została "
-"przekroczona."
+msgstr "Nie można było wysłać wiadomości, ponieważ maksymalna długość została przekroczona."
+#: ../libpurple/plugins/log_reader.c:1647
msgid "Message could not be sent."
msgstr "Nie można było wysłać wiadomości."
#. The names of IM clients are marked for translation at the request of
#. translators who wanted to transliterate them. Many translators
#. choose to leave them alone. Choose what's best for your language.
+#: ../libpurple/plugins/log_reader.c:2758
+#: ../libpurple/plugins/log_reader.c:2914
msgid "Adium"
msgstr "Adium"
#. The names of IM clients are marked for translation at the request of
#. translators who wanted to transliterate them. Many translators
#. choose to leave them alone. Choose what's best for your language.
+#: ../libpurple/plugins/log_reader.c:2771
+#: ../libpurple/plugins/log_reader.c:2919
msgid "Fire"
msgstr "Fire"
#. The names of IM clients are marked for translation at the request of
#. translators who wanted to transliterate them. Many translators
#. choose to leave them alone. Choose what's best for your language.
+#: ../libpurple/plugins/log_reader.c:2783
+#: ../libpurple/plugins/log_reader.c:2923
msgid "Messenger Plus!"
msgstr "Messenger Plus!"
#. The names of IM clients are marked for translation at the request of
#. translators who wanted to transliterate them. Many translators
#. choose to leave them alone. Choose what's best for your language.
+#: ../libpurple/plugins/log_reader.c:2797
+#: ../libpurple/plugins/log_reader.c:2928
msgid "QIP"
msgstr "QIP"
#. The names of IM clients are marked for translation at the request of
#. translators who wanted to transliterate them. Many translators
#. choose to leave them alone. Choose what's best for your language.
+#: ../libpurple/plugins/log_reader.c:2809
+#: ../libpurple/plugins/log_reader.c:2932
msgid "MSN Messenger"
msgstr "MSN Messenger"
#. The names of IM clients are marked for translation at the request of
#. translators who wanted to transliterate them. Many translators
#. choose to leave them alone. Choose what's best for your language.
+#: ../libpurple/plugins/log_reader.c:2821
+#: ../libpurple/plugins/log_reader.c:2936
msgid "Trillian"
msgstr "Trillian"
#. The names of IM clients are marked for translation at the request of
#. translators who wanted to transliterate them. Many translators
#. choose to leave them alone. Choose what's best for your language.
+#: ../libpurple/plugins/log_reader.c:2833
+#: ../libpurple/plugins/log_reader.c:2940
msgid "aMSN"
msgstr "aMSN"
#. Add general preferences.
+#: ../libpurple/plugins/log_reader.c:2896
msgid "General Log Reading Configuration"
-msgstr "Ogólna konfiguracja czytnika dzienników rozmów"
+msgstr "Ogólna konfiguracja przeglądarki archiwum rozmów"
+#: ../libpurple/plugins/log_reader.c:2900
msgid "Fast size calculations"
msgstr "Szybkie obliczanie rozmiaru"
+#: ../libpurple/plugins/log_reader.c:2904
msgid "Use name heuristics"
msgstr "Użycie heurystyki nazw"
#. Add Log Directory preferences.
+#: ../libpurple/plugins/log_reader.c:2910
msgid "Log Directory"
-msgstr "Katalog dziennika"
+msgstr "Katalog archiwum rozmów"
#. *< type
#. *< ui_requirement
@@ -2693,111 +3842,96 @@ msgstr "Katalog dziennika"
#. *< dependencies
#. *< priority
#. *< id
+#: ../libpurple/plugins/log_reader.c:2968
msgid "Log Reader"
-msgstr "Czytnik dzienników rozmów"
+msgstr "Czytnik archiwum rozmów"
#. *< name
#. *< version
#. * summary
+#: ../libpurple/plugins/log_reader.c:2972
msgid "Includes other IM clients' logs in the log viewer."
-msgstr "Dołącza dzienniki innych komunikatorów do przeglądarki dzienników."
+msgstr "Dołącza archiwa innych komunikatorów do przeglądarki archiwum rozmów."
#. * description
+#: ../libpurple/plugins/log_reader.c:2976
msgid ""
-"When viewing logs, this plugin will include logs from other IM clients. "
-"Currently, this includes Adium, MSN Messenger, aMSN, and Trillian.\n"
-"\n"
-"WARNING: This plugin is still alpha code and may crash frequently. Use it "
-"at your own risk!"
-msgstr ""
-"Podczas przeglądania dzienników rozmów, ta wtyczka automatycznie dołączy "
-"dzienniki z innych komunikatorów. Obsługiwane aktualnie komunikatory to "
-"Adium, MSN Messenger, aMSN i Trillian.\n"
+"When viewing logs, this plugin will include logs from other IM clients. Currently, this includes Adium, MSN Messenger, aMSN, and Trillian.\n"
"\n"
-"UWAGA: ta wtyczka nie jest jeszcze ukończona i może często wywoływać awarie. "
-"Należy jej używać wyłącznie na własne ryzyko."
+"WARNING: This plugin is still alpha code and may crash frequently. Use it at your own risk!"
+msgstr "Podczas przeglądania archiwum rozmów, ta wtyczka automatycznie dołączy archiwa z innych komunikatorów. Obsługiwane obecnie komunikatory to Adium, MSN Messenger, aMSN i Trillian.\n\nUWAGA: ta wtyczka nie jest jeszcze ukończona i może często wywoływać awarie. Należy jej używać wyłącznie na własne ryzyko."
+#: ../libpurple/plugins/mono/loader/mono.c:224
msgid "Mono Plugin Loader"
msgstr "Wczytywanie wtyczek Mono"
+#: ../libpurple/plugins/mono/loader/mono.c:226
+#: ../libpurple/plugins/mono/loader/mono.c:227
msgid "Loads .NET plugins with Mono."
msgstr "Wczytuje wtyczki języka .NET za pomocą Mono."
-msgid "Add new line in IMs"
-msgstr "Dodaje nowy wiersz w wiadomościach"
-
-msgid "Add new line in Chats"
-msgstr "Dodaje nowy wiersz w konferencjach"
-
-#. *< magic
-#. *< major version
-#. *< minor version
-#. *< type
-#. *< ui_requirement
-#. *< flags
-#. *< dependencies
-#. *< priority
-#. *< id
-msgid "New Line"
-msgstr "Nowy wiersz"
-
-#. *< name
-#. *< version
-msgid "Prepends a newline to displayed message."
-msgstr "Poprzedza wyświetlaną wiadomość nowym wierszem."
-
-#. *< summary
-msgid ""
-"Prepends a newline to messages so that the rest of the message appears below "
-"the username in the conversation window."
-msgstr ""
-"Poprzedza wiadomości nowym wierszem w ten sposób, że pozostała część "
-"wiadomości pojawia się poniżej nazwy użytkownika w oknie rozmowy."
-
+#: ../libpurple/plugins/offlinemsg.c:23
msgid "Offline Message Emulation"
msgstr "Emulacja wiadomości offline"
+#: ../libpurple/plugins/offlinemsg.c:25 ../libpurple/plugins/offlinemsg.c:26
msgid "Save messages sent to an offline user as pounce."
-msgstr ""
-"Zapis wiadomości wysłanych do użytkownika w trybie offline jako "
-"przechwytywane zdarzenie."
+msgstr "Zapis wiadomości wysłanych do użytkownika w trybie offline jako przechwytywane zdarzenie."
+#: ../libpurple/plugins/offlinemsg.c:93
+msgid "Offline message"
+msgstr "Wiadomość w trybie offline"
+
+#: ../libpurple/plugins/offlinemsg.c:102
msgid ""
"The rest of the messages will be saved as pounces. You can edit/delete the "
"pounce from the `Buddy Pounce' dialog."
-msgstr ""
-"Reszta wiadomości zostanie zapisana jako przechwytywane zdarzenie. Można "
-"zmodyfikować/usunąć zdarzenie z okna dialogowego \"Przechwytywanie zdarzeń\"."
+msgstr "Reszta wiadomości zostanie zapisana jako przechwytywane zdarzenie. Można zmodyfikować/usunąć zdarzenie z okna dialogowego \"Przechwytywanie zdarzeń\"."
+#: ../libpurple/plugins/offlinemsg.c:163
#, c-format
msgid ""
-"\"%s\" is currently offline. Do you want to save the rest of the messages in "
-"a pounce and automatically send them when \"%s\" logs back in?"
-msgstr ""
-"Użytkownik \"%s\" jest obecnie w trybie offline. Zapisać resztę wiadomości "
-"jako przechwytywane zdarzenie i automatycznie wysłać je, kiedy użytkownik "
-"\"%s\" zaloguje się ponownie?"
+"\"%s\" is currently offline. Do you want to save the rest of the messages in"
+" a pounce and automatically send them when \"%s\" logs back in?"
+msgstr "Użytkownik \"%s\" jest obecnie w trybie offline. Zapisać resztę wiadomości jako przechwytywane zdarzenie i automatycznie wysłać je, kiedy użytkownik \"%s\" zaloguje się ponownie?"
+#: ../libpurple/plugins/offlinemsg.c:167
msgid "Offline Message"
msgstr "Wiadomość w trybie offline"
+#: ../libpurple/plugins/offlinemsg.c:168
msgid "You can edit/delete the pounce from the `Buddy Pounces' dialog"
-msgstr ""
-"Można zmodyfikować/usunąć zdarzenie z okna dialogowego \"Przechwytywanie "
-"zdarzeń\""
-
+msgstr "Można zmodyfikować/usunąć zdarzenie z okna dialogowego \"Przechwytywanie zdarzeń\""
+
+#: ../libpurple/plugins/offlinemsg.c:171
+#: ../libpurple/protocols/msn/error.c:382
+#: ../libpurple/protocols/msn/msn.c:1129 ../libpurple/protocols/msn/msn.c:1132
+#: ../libpurple/protocols/novell/novell.c:1961
+#: ../libpurple/protocols/silc/buddy.c:328
+#: ../libpurple/protocols/silc/pk.c:117 ../libpurple/protocols/silc/wb.c:310
+#: ../pidgin/gtkrequest.c:334
msgid "Yes"
msgstr "Tak"
+#: ../libpurple/plugins/offlinemsg.c:172
+#: ../libpurple/protocols/msn/error.c:383
+#: ../libpurple/protocols/msn/msn.c:1129 ../libpurple/protocols/msn/msn.c:1132
+#: ../libpurple/protocols/novell/novell.c:1962
+#: ../libpurple/protocols/silc/buddy.c:329
+#: ../libpurple/protocols/silc/pk.c:118 ../libpurple/protocols/silc/wb.c:311
+#: ../pidgin/gtkrequest.c:336
msgid "No"
msgstr "Nie"
+#: ../libpurple/plugins/offlinemsg.c:199
msgid "Save offline messages in pounce"
msgstr "Zapisywanie wiadomości offline jako przechwytywane zdarzenie"
+#: ../libpurple/plugins/offlinemsg.c:203
msgid "Do not ask. Always save in pounce."
msgstr "Bez pytania. Zawsze zapisywane jako zdarzenie."
+#: ../libpurple/plugins/one_time_password.c:68
msgid "One Time Password"
msgstr "Jednorazowe hasło"
@@ -2807,24 +3941,23 @@ msgstr "Jednorazowe hasło"
#. *< dependencies
#. *< priority
#. *< id
+#: ../libpurple/plugins/one_time_password.c:123
msgid "One Time Password Support"
msgstr "Obsługa jednorazowych haseł"
#. *< name
#. *< version
#. * summary
+#: ../libpurple/plugins/one_time_password.c:126
msgid "Enforce that passwords are used only once."
msgstr "Wymusza użycie haseł jednorazowych."
#. * description
+#: ../libpurple/plugins/one_time_password.c:128
msgid ""
-"Allows you to enforce on a per-account basis that passwords not being saved "
-"are only used in a single successful connection.\n"
+"Allows you to enforce on a per-account basis that passwords not being saved are only used in a single successful connection.\n"
"Note: The account password must not be saved for this to work."
-msgstr ""
-"Umożliwia wymuszanie użycia niezapisanego hasła tylko w pojedynczym "
-"pomyślnym połączeniu dla każdego konta.\n"
-"Uwaga: hasło konta nie może być zapisywane."
+msgstr "Umożliwia wymuszanie użycia niezapisanego hasła tylko w pojedynczym pomyślnym połączeniu dla każdego konta.\nUwaga: hasło konta nie może być zapisywane."
#. *< type
#. *< ui_requirement
@@ -2832,41 +3965,48 @@ msgstr ""
#. *< dependencies
#. *< priority
#. *< id
+#: ../libpurple/plugins/perl/perl.c:696
msgid "Perl Plugin Loader"
msgstr "Wczytywanie wtyczek języka Perl"
#. *< name
#. *< version
#. *< summary
+#: ../libpurple/plugins/perl/perl.c:698 ../libpurple/plugins/perl/perl.c:699
msgid "Provides support for loading perl plugins."
msgstr "Dostarcza obsługę wczytywania wtyczek języka Perl."
+#: ../libpurple/plugins/psychic.c:19
msgid "Psychic Mode"
msgstr "Tryb jasnowidza"
+#: ../libpurple/plugins/psychic.c:20
msgid "Psychic mode for incoming conversation"
msgstr "Tryb jasnowidza dla nadchodzących rozmów"
+#: ../libpurple/plugins/psychic.c:21
msgid ""
"Causes conversation windows to appear as other users begin to message you. "
"This works for AIM, ICQ, XMPP, Sametime, and Yahoo!"
-msgstr ""
-"Włączenie tej wtyczki powoduje, że okna rozmów będą się pojawiały, kiedy "
-"inny użytkownik rozpocznie pisanie wiadomości. Wtyczka działa z AIM, ICQ, "
-"XMPP, Sametime i Yahoo!"
+msgstr "Włączenie tej wtyczki powoduje, że okna rozmów będą się pojawiały, kiedy inny użytkownik rozpocznie pisanie wiadomości. Wtyczka działa z AIM, ICQ, XMPP, Sametime i Yahoo!"
+#: ../libpurple/plugins/psychic.c:71
msgid "You feel a disturbance in the force..."
msgstr "Wyczuwasz zaburzenia mocy..."
+#: ../libpurple/plugins/psychic.c:90
msgid "Only enable for users on the buddy list"
msgstr "Tylko dla użytkowników z listy znajomych"
+#: ../libpurple/plugins/psychic.c:95
msgid "Disable when away"
msgstr "Wyłączone podczas nieobecności"
+#: ../libpurple/plugins/psychic.c:99
msgid "Display notification message in conversations"
msgstr "Treść powiadomienia w rozmowach"
+#: ../libpurple/plugins/psychic.c:104
msgid "Raise psychic conversations"
msgstr "Automatycznie otwieranie okien rozmów"
@@ -2876,6 +4016,7 @@ msgstr "Automatycznie otwieranie okien rozmów"
#. *< dependencies
#. *< priority
#. *< id
+#: ../libpurple/plugins/signals-test.c:905
msgid "Signals Test"
msgstr "Test sygnałów"
@@ -2883,6 +4024,8 @@ msgstr "Test sygnałów"
#. *< version
#. * summary
#. * description
+#: ../libpurple/plugins/signals-test.c:908
+#: ../libpurple/plugins/signals-test.c:910
msgid "Test to see that all signals are working properly."
msgstr "Test sprawdzający, czy wszystkie sygnały poprawnie działają."
@@ -2892,6 +4035,7 @@ msgstr "Test sprawdzający, czy wszystkie sygnały poprawnie działają."
#. *< dependencies
#. *< priority
#. *< id
+#: ../libpurple/plugins/simple.c:37
msgid "Simple Plugin"
msgstr "Prosta wtyczka"
@@ -2899,10 +4043,26 @@ msgstr "Prosta wtyczka"
#. *< version
#. * summary
#. * description
+#: ../libpurple/plugins/simple.c:40 ../libpurple/plugins/simple.c:42
msgid "Tests to see that most things are working."
msgstr "Testy sprawdzające, czy większość rzeczy działa."
+#. Make messages
+#: ../libpurple/plugins/ssl/ssl-gnutls.c:1227
+#: ../libpurple/plugins/ssl/ssl-nss.c:992
+#, c-format
+msgid ""
+"Common name: %s\n"
+"\n"
+"Fingerprint (SHA1): %s\n"
+"\n"
+"Activation date: %s\n"
+"Expiration date: %s\n"
+msgstr "Wspólna nazwa: %s\n\nOdcisk (SHA1): %s\n\nData aktywacji: %s\nData wygaśnięcia: %s\n"
+
#. Scheme name
+#: ../libpurple/plugins/ssl/ssl-gnutls.c:1249
+#: ../libpurple/plugins/ssl/ssl-nss.c:1013
msgid "X.509 Certificates"
msgstr "Certyfikaty X.509"
@@ -2912,6 +4072,7 @@ msgstr "Certyfikaty X.509"
#. *< dependencies
#. *< priority
#. *< id
+#: ../libpurple/plugins/ssl/ssl-gnutls.c:1326
msgid "GNUTLS"
msgstr "GnuTLS"
@@ -2919,6 +4080,8 @@ msgstr "GnuTLS"
#. *< version
#. * summary
#. * description
+#: ../libpurple/plugins/ssl/ssl-gnutls.c:1329
+#: ../libpurple/plugins/ssl/ssl-gnutls.c:1331
msgid "Provides SSL support through GNUTLS."
msgstr "Dostarcza obsługę SSL przez bibliotekę GnuTLS."
@@ -2928,6 +4091,7 @@ msgstr "Dostarcza obsługę SSL przez bibliotekę GnuTLS."
#. *< dependencies
#. *< priority
#. *< id
+#: ../libpurple/plugins/ssl/ssl-nss.c:1091
msgid "NSS"
msgstr "NSS"
@@ -2935,6 +4099,8 @@ msgstr "NSS"
#. *< version
#. * summary
#. * description
+#: ../libpurple/plugins/ssl/ssl-nss.c:1094
+#: ../libpurple/plugins/ssl/ssl-nss.c:1096
msgid "Provides SSL support through Mozilla NSS."
msgstr "Dostarcza obsługę SSL przez bibliotekę NSS Mozilli."
@@ -2944,6 +4110,7 @@ msgstr "Dostarcza obsługę SSL przez bibliotekę NSS Mozilli."
#. *< dependencies
#. *< priority
#. *< id
+#: ../libpurple/plugins/ssl/ssl.c:94
msgid "SSL"
msgstr "SSL"
@@ -2951,38 +4118,48 @@ msgstr "SSL"
#. *< version
#. * summary
#. * description
+#: ../libpurple/plugins/ssl/ssl.c:97 ../libpurple/plugins/ssl/ssl.c:99
msgid "Provides a wrapper around SSL support libraries."
msgstr "Dostarcza funkcję obudowującą biblioteki obsługi SSL."
+#: ../libpurple/plugins/statenotify.c:62
#, c-format
msgid "%s is no longer away."
msgstr "Użytkownik %s nie jest już nieobecny."
+#: ../libpurple/plugins/statenotify.c:64
#, c-format
msgid "%s has gone away."
msgstr "Użytkownik %s zaraz wraca."
+#: ../libpurple/plugins/statenotify.c:74
#, c-format
msgid "%s has become idle."
msgstr "Użytkownik %s jest bezczynny."
+#: ../libpurple/plugins/statenotify.c:76
#, c-format
msgid "%s is no longer idle."
msgstr "Użytkownik %s nie jest już bezczynny."
+#: ../libpurple/plugins/statenotify.c:85
#, c-format
msgid "%s has signed on."
msgstr "Użytkownik %s zalogował się."
+#: ../libpurple/plugins/statenotify.c:103
msgid "Notify When"
msgstr "Powiadamianie, kiedy"
+#: ../libpurple/plugins/statenotify.c:106
msgid "Buddy Goes _Away"
msgstr "Znajomy z_araz wraca"
+#: ../libpurple/plugins/statenotify.c:109
msgid "Buddy Goes _Idle"
msgstr "Znajomy jest _bezczynny"
+#: ../libpurple/plugins/statenotify.c:112
msgid "Buddy _Signs On/Off"
msgstr "Znajomy loguje _się/wylogowuje się"
@@ -2992,6 +4169,7 @@ msgstr "Znajomy loguje _się/wylogowuje się"
#. *< dependencies
#. *< priority
#. *< id
+#: ../libpurple/plugins/statenotify.c:159
msgid "Buddy State Notification"
msgstr "Powiadamianie o stanie znajomych"
@@ -2999,56 +4177,107 @@ msgstr "Powiadamianie o stanie znajomych"
#. *< version
#. * summary
#. * description
+#: ../libpurple/plugins/statenotify.c:162
+#: ../libpurple/plugins/statenotify.c:165
msgid ""
"Notifies in a conversation window when a buddy goes or returns from away or "
"idle."
-msgstr ""
-"Powiadamia w oknie rozmowy, kiedy użytkownik zaraz wraca lub jest bezczynny."
+msgstr "Powiadamia w oknie rozmowy, kiedy użytkownik zaraz wraca lub jest bezczynny."
+#: ../libpurple/plugins/tcl/tcl.c:426
msgid "Tcl Plugin Loader"
msgstr "Wczytywanie wtyczek języka Tcl"
+#: ../libpurple/plugins/tcl/tcl.c:428 ../libpurple/plugins/tcl/tcl.c:429
msgid "Provides support for loading Tcl plugins"
msgstr "Dostarcza obsługę wczytywania wtyczek języka Tcl"
-msgid ""
-"Unable to detect ActiveTCL installation. If you wish to use TCL plugins, "
-"install ActiveTCL from http://www.activestate.com\n"
-msgstr ""
-"Nie można wykryć instalacji biblioteki ActiveTcl. Aby używać wtyczek języka "
-"Tcl, należy zainstalować bibliotekę ActiveTcl ze strony http://www."
-"activestate.com\n"
+#: ../libpurple/presence.c:578
+#, c-format
+msgid "+++ %s became idle"
+msgstr "+++ %s jest bezczynny"
+
+#: ../libpurple/presence.c:580
+#, c-format
+msgid "+++ %s became unidle"
+msgstr "+++ %s nie jest już bezczynny"
+
+#: ../libpurple/presence.c:827
+#, c-format
+msgid "%s became idle"
+msgstr "Użytkownik %s jest bezczynny"
+
+#: ../libpurple/presence.c:847
+#, c-format
+msgid "%s became unidle"
+msgstr "Użytkownik %s nie jest już bezczynny"
+#: ../libpurple/protocols/bonjour/bonjour.c:99
msgid ""
-"Unable to find Apple's \"Bonjour for Windows\" toolkit, see http://d.pidgin."
-"im/BonjourWindows for more information."
-msgstr ""
-"Nie można odnaleźć zestawu narzędzi \"Bonjour dla systemu Windows\" firmy "
-"Apple. Aby uzyskać więcej informacji, proszę odwiedzić stronę http://d."
-"pidgin.im/BonjourWindows."
+"Unable to find Apple's \"Bonjour for Windows\" toolkit, see "
+"https://developer.pidgin.im/BonjourWindows for more information."
+msgstr "Nie można odnaleźć zestawu narzędzie \"Bonjour for Windows\" firmy Apple. Więcej informacji znajduje się na stronie https://developer.pidgin.im/BonjourWindows ."
+#: ../libpurple/protocols/bonjour/bonjour.c:122
msgid "Unable to listen for incoming IM connections"
msgstr "Nie można nasłuchiwać przychodzących połączeń komunikatora"
+#: ../libpurple/protocols/bonjour/bonjour.c:149
msgid ""
"Unable to establish connection with the local mDNS server. Is it running?"
-msgstr ""
-"Nie można nawiązać połączenia z lokalnym serwerem mDNS. Proszę sprawdzić, "
-"czy jest uruchomiony."
-
+msgstr "Nie można nawiązać połączenia z lokalnym serwerem mDNS. Proszę sprawdzić, czy jest uruchomiony."
+
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
+#: ../libpurple/protocols/bonjour/bonjour.c:398
+#: ../libpurple/protocols/bonjour/bonjour.c:768
+#: ../libpurple/protocols/gg/pubdir-prpl.c:419
+#: ../libpurple/protocols/gg/pubdir-prpl.c:814
+#: ../libpurple/protocols/jabber/jabber.c:1318
msgid "First name"
msgstr "Imię"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
+#: ../libpurple/protocols/bonjour/bonjour.c:403
+#: ../libpurple/protocols/bonjour/bonjour.c:771
+#: ../libpurple/protocols/gg/pubdir-prpl.c:423
+#: ../libpurple/protocols/gg/pubdir-prpl.c:818
+#: ../libpurple/protocols/jabber/jabber.c:1319
msgid "Last name"
msgstr "Nazwisko"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
#. email
+#: ../libpurple/protocols/bonjour/bonjour.c:410
+#: ../libpurple/protocols/bonjour/bonjour.c:774
+#: ../libpurple/protocols/gg/account.c:207
+#: ../libpurple/protocols/jabber/buddy.c:362
+#: ../libpurple/protocols/jabber/buddy.c:1149
+#: ../libpurple/protocols/jabber/buddy.c:1162
+#: ../libpurple/protocols/jabber/buddy.c:2034
+#: ../libpurple/protocols/jabber/jabber.c:1316
+#: ../libpurple/protocols/mxit/actions.c:291
+#: ../libpurple/protocols/silc/ops.c:1054
+#: ../libpurple/protocols/silc/silc.c:1018
+#: ../libpurple/protocols/silc/silc.c:1262
+#: ../libpurple/protocols/silc/util.c:520
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1057
msgid "Email"
msgstr "Adres e-mail"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
+#: ../libpurple/protocols/bonjour/bonjour.c:416
+#: ../libpurple/protocols/bonjour/bonjour.c:777
msgid "AIM Account"
msgstr "Konto AIM"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
+#: ../libpurple/protocols/bonjour/bonjour.c:422
+#: ../libpurple/protocols/bonjour/bonjour.c:780
msgid "XMPP Account"
msgstr "Konto XMPP"
@@ -3062,206 +4291,394 @@ msgstr "Konto XMPP"
#. *< version
#. * summary
#. * description
+#: ../libpurple/protocols/bonjour/bonjour.c:583
+#: ../libpurple/protocols/bonjour/bonjour.c:585
msgid "Bonjour Protocol Plugin"
msgstr "Wtyczka protokołu Bonjour"
+#: ../libpurple/protocols/bonjour/bonjour.c:733
msgid "Purple Person"
msgstr "Osoba biblioteki Purple"
#. Creating the options for the protocol
+#: ../libpurple/protocols/bonjour/bonjour.c:765
msgid "Local Port"
msgstr "Lokalny port"
+#: ../libpurple/protocols/bonjour/bonjour.h:33
msgid "Bonjour"
msgstr "Bonjour"
+#: ../libpurple/protocols/bonjour/jabber.c:401
#, c-format
msgid "%s has closed the conversation."
msgstr "Użytkownik %s zamknął okno rozmowy."
+#: ../libpurple/protocols/bonjour/jabber.c:491
+#: ../libpurple/protocols/bonjour/jabber.c:556
+#: ../libpurple/protocols/bonjour/jabber.c:601
+#: ../libpurple/protocols/bonjour/jabber.c:876
+#: ../libpurple/protocols/bonjour/jabber.c:897
msgid "Unable to send the message, the conversation couldn't be started."
msgstr "Nie można wysłać wiadomości. Rozmowa nie może zostać rozpoczęta."
+#: ../libpurple/protocols/bonjour/mdns_win32.c:110
msgid "Error communicating with local mDNSResponder."
msgstr "Błąd podczas komunikowania się z lokalnym programem mDNSResponder."
-msgid "Invalid proxy settings"
-msgstr "Nieprawidłowe ustawienia pośrednika"
+#: ../libpurple/protocols/gg/account.c:122
+msgid "Token Error"
+msgstr "Błąd tokenu"
+
+#: ../libpurple/protocols/gg/account.c:123
+msgid "Unable to fetch the token."
+msgstr "Nie można przechwycić tokenu."
+
+#: ../libpurple/protocols/gg/account.c:169
+msgid "Register New Gadu-Gadu Account"
+msgstr "Rejestracja nowego konta Gadu-Gadu"
+
+#: ../libpurple/protocols/gg/account.c:214
+#: ../libpurple/protocols/gg/account.c:217
+#: ../libpurple/protocols/gg/account.c:468
+#: ../libpurple/protocols/gg/account.c:478
+#: ../libpurple/protocols/jabber/jabber.c:1407
+#: ../libpurple/protocols/jabber/jabber.c:1411
+#: ../libpurple/protocols/jabber/jabber.c:2538
+msgid "Password"
+msgstr "Hasło"
-msgid ""
-"Either the host name or port number specified for your given proxy type is "
-"invalid."
-msgstr ""
-"Podano nieprawidłową nazwę komputera lub numer portu dla podanego typu "
-"pośrednika."
+#: ../libpurple/protocols/gg/account.c:226
+#: ../libpurple/protocols/jabber/jabber.c:2544
+msgid "Password (again)"
+msgstr "Hasło (ponownie)"
-msgid "Save Buddylist..."
-msgstr "Zapisz listę znajomych..."
+#: ../libpurple/protocols/gg/account.c:237
+#: ../libpurple/protocols/gg/account.c:248
+#: ../libpurple/protocols/gg/account.c:493
+#: ../libpurple/protocols/gg/account.c:503
+msgid "Captcha"
+msgstr "CAPTCHA"
-msgid "Your buddylist is empty, nothing was written to the file."
-msgstr "Lista znajomych jest pusta, nic nie zostało zapisane do pliku."
+#: ../libpurple/protocols/gg/account.c:241
+#: ../libpurple/protocols/gg/account.c:497
+msgid "Enter text from image below"
+msgstr "Proszę podać tekst z poniższego obrazu"
-msgid "Buddylist saved successfully!"
-msgstr "Pomyślnie zapisano listę znajomych."
+#: ../libpurple/protocols/gg/account.c:257
+msgid "Please, fill in the following fields"
+msgstr "Proszę wypełnić poniższe pola"
+
+#: ../libpurple/protocols/gg/account.c:342
+msgid "Unable to register new account. An unknown error occurred."
+msgstr "Nie można zarejestrować nowego konta. Wystąpił nieznany błąd."
+#: ../libpurple/protocols/gg/account.c:359
#, c-format
-msgid "Couldn't write buddy list for %s to %s"
-msgstr "Nie można zapisać listy znajomych dla %s do %s"
+msgid "Your new GG number: %u."
+msgstr "Nowy numer GG: %u."
-msgid "Couldn't load buddylist"
-msgstr "Nie można wczytać listy znajomych"
+#: ../libpurple/protocols/gg/account.c:361
+msgid "Registration completed successfully!"
+msgstr "Pomyślnie ukończono rejestrację."
-msgid "Load Buddylist..."
-msgstr "Wczytaj listę znajomych..."
+#: ../libpurple/protocols/gg/account.c:414
+msgid "Password change"
+msgstr "Zmiana hasła"
-msgid "Buddylist loaded successfully!"
-msgstr "Pomyślnie wczytano listę znajomych."
+#: ../libpurple/protocols/gg/account.c:462
+msgid "New email address"
+msgstr "Nowy adres e-mail"
-msgid "Save buddylist..."
-msgstr "Zapisz listę znajomych..."
+#: ../libpurple/protocols/gg/account.c:472
+msgid "Current password"
+msgstr "Bieżące hasło"
-msgid "Load buddylist from file..."
-msgstr "Wczytaj listę znajomych z pliku..."
+#: ../libpurple/protocols/gg/account.c:486
+msgid "Password (retype)"
+msgstr "Hasło (ponownie)"
-msgid "City"
-msgstr "Miasto"
+#: ../libpurple/protocols/gg/account.c:557
+msgid "Your current password is different from the one that you specified."
+msgstr "Bieżące hasło jest inne niż podane."
-msgid "Year of birth"
-msgstr "Rok urodzenia"
+#: ../libpurple/protocols/gg/account.c:567
+msgid "New password have to be different from the current one."
+msgstr "Nowe hasło musi być różne niż podane."
-#. gender
-msgid "Gender"
-msgstr "Płeć"
+#: ../libpurple/protocols/gg/account.c:624
+msgid "Unable to change password. An unknown error occurred."
+msgstr "Nie można zmienić hasła. Wystąpił nieznany błąd."
-msgid "Male or female"
-msgstr "Mężczyzna lub kobieta"
+#: ../libpurple/protocols/gg/account.c:638
+#: ../libpurple/protocols/jabber/jabber.c:2476
+msgid "Your password has been changed."
+msgstr "Hasło zostało zmienione."
-#. 0
-msgid "Male"
-msgstr "Mężczyzna"
+#: ../libpurple/protocols/gg/chat.c:153
+msgid "You have re-joined the chat"
+msgstr "Ponownie dołączono do konferencji"
-msgid "Female"
-msgstr "Kobieta"
+#: ../libpurple/protocols/gg/chat.c:288
+msgid "You have left the chat"
+msgstr "Opuszczono konferencję"
-msgid "Only online"
-msgstr "Tylko znajomi w trybie online"
+#: ../libpurple/protocols/gg/chat.c:302
+msgid "_Conference identifier:"
+msgstr "Identyfikator _konferencji:"
-msgid "Find buddies"
-msgstr "Wyszukiwanie znajomych"
+#: ../libpurple/protocols/gg/chat.c:376 ../libpurple/protocols/gg/chat.c:407
+#, c-format
+msgid "%s is not a valid room identifier"
+msgstr "%s nie jest prawidłowym identyfikatorem pokoju"
-msgid "Please, enter your search criteria below"
-msgstr "Proszę poniżej podać kryteria wyszukiwania"
+#: ../libpurple/protocols/gg/chat.c:377 ../libpurple/protocols/gg/chat.c:378
+#: ../libpurple/protocols/gg/chat.c:409 ../libpurple/protocols/gg/chat.c:410
+msgid "Invalid Room Identifier"
+msgstr "Nieprawidłowy identyfikator pokoju"
-msgid "Show status to:"
-msgstr "Wyświetlanie stanu:"
+#. if (chat->left)
+#: ../libpurple/protocols/gg/chat.c:413 ../libpurple/protocols/gg/chat.c:414
+#: ../libpurple/protocols/oscar/family_chatnav.c:65
+msgid "Could not join chat room"
+msgstr "Nie można dołączyć do pokoju konferencji"
-msgid "All people"
-msgstr "Wszystkim"
+#: ../libpurple/protocols/gg/chat.c:415
+msgid "You have to ask for invitation from another chat participant"
+msgstr "Należy poprosić o zaproszenie od innego uczestnika konferencji"
-msgid "Only buddies"
-msgstr "Tylko znajomym"
+#: ../libpurple/protocols/gg/chat.c:561
+msgid "Conference identifier"
+msgstr "Identyfikator konferencji"
-msgid "Change status broadcasting"
-msgstr "Zmień rozgłaszanie stanu"
+#: ../libpurple/protocols/gg/chat.c:565 ../libpurple/protocols/msn/msn.c:2508
+msgid "Start Date"
+msgstr "Data rozpoczęcia"
-msgid "Please, select who can see your status"
-msgstr "Proszę wybrać, kto może widzieć stan"
+#: ../libpurple/protocols/gg/chat.c:569
+msgid "User Count"
+msgstr "Liczba użytkowników"
-#, c-format
-msgid "Select a chat for buddy: %s"
-msgstr "Wybierz konferencję dla znajomego: %s"
+#: ../libpurple/protocols/gg/chat.c:589
+msgid "Joined"
+msgstr "Dołączono"
-msgid "Add to chat..."
-msgstr "Dodaj do konferencji..."
+#: ../libpurple/protocols/gg/chat.c:591
+msgid "Chat left"
+msgstr "Opuszczono konferencję"
-#. 0
-#. Global
-msgid "Available"
-msgstr "Dostępny"
+#: ../libpurple/protocols/gg/chat.c:593
+msgid "Can join chat"
+msgstr "Można dołączyć do konferencji"
-#. 2
-msgid "Chatty"
-msgstr "Chętny do rozmowy"
+#: ../libpurple/protocols/gg/deprecated.c:46 ../libpurple/proxy.c:2326
+#: ../libpurple/proxy.c:2397
+msgid "Invalid proxy settings"
+msgstr "Nieprawidłowe ustawienia pośrednika"
-#. 3
-msgid "Do Not Disturb"
-msgstr "Nie przeszkadzać"
+#: ../libpurple/protocols/gg/deprecated.c:47 ../libpurple/proxy.c:2327
+#: ../libpurple/proxy.c:2398
+msgid ""
+"Either the host name or port number specified for your given proxy type is "
+"invalid."
+msgstr "Podano nieprawidłową nazwę komputera lub numer portu dla podanego typu pośrednika."
-#. 1
-#. get_yahoo_status_from_purple_status() returns YAHOO_STATUS_CUSTOM for
-#. * the generic away state (YAHOO_STATUS_TYPE_AWAY) with no message
-#. Away stuff
-msgid "Away"
-msgstr "Zaraz wracam"
+#: ../libpurple/protocols/gg/edisc.c:291
+msgid "File transfer expired."
+msgstr "Przesłanie pliku wygasło."
-msgid "UIN"
-msgstr "Identyfikator użytkownika"
+#: ../libpurple/protocols/gg/edisc.c:429 ../libpurple/protocols/gg/gg.c:94
+#: ../libpurple/protocols/msn/error.c:228
+#: ../libpurple/protocols/silc/silc.c:341 ../libpurple/proxy.c:1777
+msgid "Authentication failed"
+msgstr "Uwierzytelnienie się nie powiodło"
-#. first name
-#. optional information
-msgid "First Name"
-msgstr "Imię"
+#: ../libpurple/protocols/gg/edisc.c:483
+msgid "Recipient not logged in"
+msgstr "Odbiorca nie jest zalogowany"
-msgid "Birth Year"
-msgstr "Rok urodzenia"
+#. bad sender recipient relation
+#: ../libpurple/protocols/gg/edisc.c:485
+msgid "Recipient didn't added you to his buddy list"
+msgstr "Odbiorca nie dodał użytkownika do swojej listy znajomych"
-msgid "Unable to display the search results."
-msgstr "Nie można wyświetlić wyników wyszukiwania."
+#: ../libpurple/protocols/gg/edisc.c:489
+msgid "Cannot offer sending a file"
+msgstr "Nie można zaoferować wysłania pliku"
-msgid "Gadu-Gadu Public Directory"
-msgstr "Katalog publiczny Gadu-Gadu"
+#: ../libpurple/protocols/gg/edisc.c:570
+msgid "Error while reading a file"
+msgstr "Błąd podczas odczytywania pliku"
-msgid "Search results"
-msgstr "Wyniki wyszukiwania"
+#: ../libpurple/protocols/gg/edisc.c:648 ../libpurple/protocols/gg/edisc.c:664
+msgid "Error while sending a file"
+msgstr "Błąd podczas wysyłania pliku"
-msgid "No matching users found"
-msgstr "Nie odnaleziono pasujących użytkowników"
+#: ../libpurple/protocols/gg/edisc.c:954
+msgid "Cannot confirm file transfer."
+msgstr "Nie można potwierdzić przesłania pliku."
-msgid "There are no users matching your search criteria."
-msgstr "Brak użytkowników spełniających kryteria wyszukiwania."
+#: ../libpurple/protocols/gg/edisc.c:1061
+#: ../libpurple/protocols/gg/edisc.c:1072
+msgid "Error while receiving a file"
+msgstr "Błąd podczas pobierania pliku"
+#: ../libpurple/protocols/gg/gg.c:95
+msgid "IMToken value has not been received."
+msgstr "Nie otrzymano wartości IMToken."
+
+#: ../libpurple/protocols/gg/gg.c:96
+msgid "Some features will be disabled. You may try again after a while."
+msgstr "Niektóre funkcje będą wyłączone. Można spróbować ponownie za jakiś czas."
+
+#: ../libpurple/protocols/gg/gg.c:121 ../libpurple/protocols/gg/gg.c:128
+#: ../libpurple/protocols/gg/gg.c:135
+msgid "Save Buddylist..."
+msgstr "Zapisz listę znajomych..."
+
+#: ../libpurple/protocols/gg/gg.c:121
+msgid "Your buddylist is empty, nothing was written to the file."
+msgstr "Lista znajomych jest pusta, nic nie zostało zapisane do pliku."
+
+#: ../libpurple/protocols/gg/gg.c:129
+msgid "Buddylist saved successfully!"
+msgstr "Pomyślnie zapisano listę znajomych."
+
+#: ../libpurple/protocols/gg/gg.c:133
+#, c-format
+msgid "Couldn't write buddy list for %s to %s"
+msgstr "Nie można zapisać listy znajomych dla %s do %s"
+
+#: ../libpurple/protocols/gg/gg.c:153 ../libpurple/protocols/gg/gg.c:154
+msgid "Couldn't load buddylist"
+msgstr "Nie można wczytać listy znajomych"
+
+#: ../libpurple/protocols/gg/gg.c:169
+msgid "Load Buddylist..."
+msgstr "Wczytaj listę znajomych..."
+
+#: ../libpurple/protocols/gg/gg.c:170
+msgid "Buddylist loaded successfully!"
+msgstr "Pomyślnie wczytano listę znajomych."
+
+#: ../libpurple/protocols/gg/gg.c:182
+msgid "Save buddylist..."
+msgstr "Zapisz listę znajomych..."
+
+#: ../libpurple/protocols/gg/gg.c:191 ../libpurple/protocols/gg/gg.c:940
+msgid "Load buddylist from file..."
+msgstr "Wczytaj listę znajomych z pliku..."
+
+#: ../libpurple/protocols/gg/gg.c:316 ../libpurple/protocols/gg/gg.c:476
msgid "Unable to read from socket"
msgstr "Nie można odczytać z gniazda"
-#. The session is now set up, ready to be connected. This emits the
-#. * signedOn signal, so clients can now do anything with msimprpl, and
-#. * we're ready for it (session key, userid, username all setup).
+#: ../libpurple/protocols/gg/gg.c:339
+msgid "Server disconnected"
+msgstr "Serwer się rozłączył"
+
+#: ../libpurple/protocols/gg/gg.c:512
+#: ../libpurple/protocols/sametime/sametime.c:1575
msgid "Connected"
msgstr "Połączono"
+#: ../libpurple/protocols/gg/gg.c:530
+#: ../libpurple/protocols/simple/simple.c:1811
+#: ../libpurple/protocols/simple/simple.c:1913 ../libpurple/proxy.c:2212
+msgid "Unable to resolve hostname"
+msgstr "Nie można uzyskać nazwy komputera"
+
+#: ../libpurple/protocols/gg/gg.c:536
+#: ../libpurple/protocols/novell/nmuser.c:1893
+#: ../libpurple/protocols/oscar/clientlogin.c:491
+#: ../libpurple/protocols/oscar/oscar.c:920
+#: ../libpurple/protocols/simple/simple.c:1132
+#: ../libpurple/protocols/yahoo/libymsg.c:2052
+msgid "Incorrect password"
+msgstr "Niepoprawne hasło"
+
+#: ../libpurple/protocols/gg/gg.c:541 ../libpurple/sslconn.c:182
+msgid "SSL Connection Failed"
+msgstr "Połączenie SSL się nie powiodło"
+
+#: ../libpurple/protocols/gg/gg.c:546
+#: ../libpurple/protocols/novell/nmuser.c:1927
+msgid ""
+"Your account has been disabled because too many incorrect passwords were "
+"entered"
+msgstr "Konto zostało wyłączone, ponieważ podano niepoprawne hasło za dużą ilość razy"
+
+#: ../libpurple/protocols/gg/gg.c:554 ../libpurple/protocols/msn/error.c:137
+msgid "Service temporarily unavailable"
+msgstr "Usługa jest tymczasowo niedostępna"
+
+#: ../libpurple/protocols/gg/gg.c:560
+msgid "Error connecting to proxy server"
+msgstr "Błąd podczas łączenia z serwerem pośrednika"
+
+#: ../libpurple/protocols/gg/gg.c:566
+msgid "Error connecting to master server"
+msgstr "Błąd podczas łączenia z głównym serwerem"
+
+#: ../libpurple/protocols/gg/gg.c:573
+msgid "Internal error"
+msgstr "Wewnętrzny błąd"
+
+#: ../libpurple/protocols/gg/gg.c:579 ../libpurple/protocols/gg/gg.c:781
+#: ../libpurple/protocols/silc/silc.c:376
+#: ../libpurple/protocols/silc/silc.c:423
msgid "Connection failed"
msgstr "Połączenie się nie powiodło"
-msgid "Add to chat"
-msgstr "Dodaj do konferencji"
-
-msgid "Chat _name:"
-msgstr "Nazwa ko_nferencji:"
+#: ../libpurple/protocols/gg/gg.c:707
+#: ../libpurple/protocols/msn/userlist.c:528
+msgid "The username specified is invalid."
+msgstr "Podana nazwa użytkownika jest nieprawidłowa."
-#, c-format
-msgid "Unable to resolve hostname '%s': %s"
-msgstr "Nie można uzyskać nazwy komputera \"%s\": %s"
+#: ../libpurple/protocols/gg/gg.c:738 ../libpurple/protocols/irc/irc.c:378
+#: ../libpurple/protocols/jabber/jabber.c:1056
+#: ../libpurple/protocols/novell/novell.c:2245
+#: ../libpurple/protocols/yahoo/libymsg.c:2132
+msgid "SSL support unavailable"
+msgstr "Obsługa SSL jest niedostępna"
-#. 1. connect to server
#. connect to the server
+#: ../libpurple/protocols/gg/gg.c:777 ../libpurple/protocols/irc/irc.c:368
+#: ../libpurple/protocols/jabber/jabber.c:1727
+#: ../libpurple/protocols/msn/session.c:430
+#: ../libpurple/protocols/novell/novell.c:2230
+#: ../libpurple/protocols/oscar/oscar.c:814
+#: ../libpurple/protocols/sametime/sametime.c:3723
+#: ../libpurple/protocols/simple/simple.c:1965
+#: ../libpurple/protocols/yahoo/libymsg.c:3773
+#: ../libpurple/protocols/zephyr/zephyr.c:1601 ../pidgin/gtkstatusbox.c:671
msgid "Connecting"
msgstr "Łączenie"
-msgid "Chat error"
-msgstr "Błąd konferencji"
-
-msgid "This chat name is already in use"
-msgstr "Nazwa konferencji jest już używana"
-
+#: ../libpurple/protocols/gg/gg.c:887
msgid "Not connected to the server"
msgstr "Nie połączono z serwerem"
+#: ../libpurple/protocols/gg/gg.c:916
+msgid "Show other sessions"
+msgstr "Wyświetl inne sesje"
+
+#: ../libpurple/protocols/gg/gg.c:920 ../libpurple/protocols/gg/status.c:350
+msgid "Show status only for buddies"
+msgstr "Wyświetlanie stanu tylko dla znajomych"
+
+#: ../libpurple/protocols/gg/gg.c:926
msgid "Find buddies..."
msgstr "Znajdź znajomych..."
+#: ../libpurple/protocols/gg/gg.c:936
msgid "Save buddylist to file..."
msgstr "Zapisz listę znajomych do pliku..."
+#: ../libpurple/protocols/gg/gg.c:968
+msgid "GG number..."
+msgstr "Numer GG..."
+
#. magic
#. major_version
#. minor_version
@@ -3273,101 +4690,422 @@ msgstr "Zapisz listę znajomych do pliku..."
#. id
#. name
#. version
+#: ../libpurple/protocols/gg/gg.c:1083
msgid "Gadu-Gadu Protocol Plugin"
msgstr "Wtyczka protokołu Gadu-Gadu"
#. summary
+#: ../libpurple/protocols/gg/gg.c:1084
msgid "Polish popular IM"
msgstr "Popularny w Polsce komunikator"
-msgid "Gadu-Gadu User"
-msgstr "Użytkownik Gadu-Gadu"
-
+#: ../libpurple/protocols/gg/gg.c:1114
msgid "GG server"
msgstr "Serwer Gadu-Gadu"
-msgid "Don't use encryption"
-msgstr "Bez użycia szyfrowania"
-
+#: ../libpurple/protocols/gg/gg.c:1127
+#: ../libpurple/protocols/jabber/libfacebook.c:232
+#: ../libpurple/protocols/jabber/libgtalk.c:279
+#: ../libpurple/protocols/jabber/libxmpp.c:273
+#: ../libpurple/protocols/oscar/oscar.c:5567
msgid "Use encryption if available"
msgstr "Użycie szyfrowania, jeśli jest dostępne"
-#. TODO
+#: ../libpurple/protocols/gg/gg.c:1129
+#: ../libpurple/protocols/jabber/libfacebook.c:233
+#: ../libpurple/protocols/jabber/libgtalk.c:278
+#: ../libpurple/protocols/jabber/libxmpp.c:272
+#: ../libpurple/protocols/oscar/oscar.c:5568
msgid "Require encryption"
msgstr "Wymaganie szyfrowania"
+#: ../libpurple/protocols/gg/gg.c:1130
+#: ../libpurple/protocols/oscar/oscar.c:5569
+msgid "Don't use encryption"
+msgstr "Bez użycia szyfrowania"
+
+#: ../libpurple/protocols/gg/gg.c:1132
+#: ../libpurple/protocols/jabber/libfacebook.c:242
+#: ../libpurple/protocols/jabber/libgtalk.c:288
+#: ../libpurple/protocols/jabber/libxmpp.c:282
+#: ../libpurple/protocols/oscar/oscar.c:5593
msgid "Connection security"
msgstr "Bezpieczeństwo połączenia"
+#: ../libpurple/protocols/gg/gg.c:1141
+msgid "Protocol version"
+msgstr "Wersja protokołu"
+
+#: ../libpurple/protocols/gg/gg.c:1146
+msgid "Show links from strangers"
+msgstr "Wyświetlanie odnośników od nieznanych użytkowników"
+
+#: ../libpurple/protocols/gg/image-prpl.c:215
+#, c-format
+msgid "Image delivered to %u."
+msgstr "Dostarczono obraz do %u."
+
+#. TODO: stock broken image?
+#: ../libpurple/protocols/gg/message-prpl.c:274
+#: ../libpurple/protocols/gg/message-prpl.c:282
+msgid "broken image"
+msgstr "uszkodzony obraz"
+
+#: ../libpurple/protocols/gg/message-prpl.c:498
+msgid "Image is too large, please try smaller one."
+msgstr "Obraz jest za duży, proszę spróbować mniejszy."
+
+#: ../libpurple/protocols/gg/message-prpl.c:502
+msgid "Image cannot be sent."
+msgstr "Nie można wysłać obrazu."
+
+#: ../libpurple/protocols/gg/multilogon.c:225
+msgid "IP"
+msgstr "Adres IP"
+
+#: ../libpurple/protocols/gg/multilogon.c:227
+msgid "Logon time"
+msgstr "Czas zalogowania"
+
+#: ../libpurple/protocols/gg/multilogon.c:229
+msgid "Session"
+msgstr "Sesja"
+
+#: ../libpurple/protocols/gg/multilogon.c:232
+msgid "Disconnect"
+msgstr "Rozłącz"
+
+#: ../libpurple/protocols/gg/multilogon.c:245
+msgid "Other Gadu-Gadu sessions"
+msgstr "Inne sesje Gadu-Gadu"
+
+#. Searching for buddies.
+#: ../libpurple/protocols/gg/pubdir-prpl.c:79
+msgid "Gadu-Gadu Public Directory"
+msgstr "Publiczny katalog Gadu-Gadu"
+
+#: ../libpurple/protocols/gg/pubdir-prpl.c:135
+msgid "Not specified"
+msgstr "Nie podano"
+
+#: ../libpurple/protocols/gg/pubdir-prpl.c:379
+#: ../libpurple/protocols/silc/buddy.c:717
+#: ../libpurple/protocols/silc/ops.c:1182
+#: ../libpurple/protocols/silc/ops.c:1331
+msgid "Cannot get user information"
+msgstr "Nie można pobrać informacji o użytkowniku"
+
+#. gender
+#: ../libpurple/protocols/gg/pubdir-prpl.c:426
+#: ../libpurple/protocols/gg/pubdir-prpl.c:532
+#: ../libpurple/protocols/gg/pubdir-prpl.c:696
+#: ../libpurple/protocols/gg/pubdir-prpl.c:822
+#: ../libpurple/protocols/msn/msn.c:2408
+#: ../libpurple/protocols/mxit/actions.c:242
+#: ../libpurple/protocols/mxit/login.c:353
+#: ../libpurple/protocols/mxit/profile.c:218
+#: ../libpurple/protocols/mxit/profile.c:341
+#: ../libpurple/protocols/oscar/userinfo.c:426
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1087
+msgid "Gender"
+msgstr "Płeć"
+
+#: ../libpurple/protocols/gg/pubdir-prpl.c:428
+#: ../libpurple/protocols/gg/pubdir-prpl.c:537
+#: ../libpurple/protocols/gg/pubdir-prpl.c:828
+#: ../libpurple/protocols/mxit/actions.c:243
+#: ../libpurple/protocols/mxit/login.c:354
+#: ../libpurple/protocols/mxit/profile.c:218
+#: ../libpurple/protocols/oscar/userinfo.c:426
+msgid "Female"
+msgstr "Kobieta"
+
+#: ../libpurple/protocols/gg/pubdir-prpl.c:428
+#: ../libpurple/protocols/gg/pubdir-prpl.c:535
+#: ../libpurple/protocols/gg/pubdir-prpl.c:826
+#: ../libpurple/protocols/mxit/actions.c:244
+#: ../libpurple/protocols/mxit/login.c:355
+#: ../libpurple/protocols/mxit/profile.c:218
+#: ../libpurple/protocols/oscar/userinfo.c:426
+msgid "Male"
+msgstr "Mężczyzna"
+
+#: ../libpurple/protocols/gg/pubdir-prpl.c:431
+#: ../libpurple/protocols/gg/pubdir-prpl.c:528
+#: ../libpurple/protocols/gg/pubdir-prpl.c:694
+#: ../libpurple/protocols/gg/pubdir-prpl.c:838
+#: ../libpurple/protocols/jabber/jabber.c:1321
+#: ../libpurple/protocols/oscar/userinfo.c:466
+#: ../libpurple/protocols/oscar/userinfo.c:474
+msgid "City"
+msgstr "Miasto"
+
+#. birthday
+#. birthday (required)
+#: ../libpurple/protocols/gg/pubdir-prpl.c:435
+#: ../libpurple/protocols/jabber/buddy.c:367
+#: ../libpurple/protocols/jabber/buddy.c:1075
+#: ../libpurple/protocols/msn/msn.c:2477
+#: ../libpurple/protocols/mxit/actions.c:236
+#: ../libpurple/protocols/mxit/login.c:347
+#: ../libpurple/protocols/mxit/profile.c:215
+#: ../libpurple/protocols/oscar/userinfo.c:447
+msgid "Birthday"
+msgstr "Data urodzenia"
+
+#: ../libpurple/protocols/gg/pubdir-prpl.c:439
+#: ../libpurple/protocols/gg/pubdir-prpl.c:698
+#: ../libpurple/protocols/msn/msn.c:2407
+#: ../libpurple/protocols/mxit/profile.c:343
+#: ../libpurple/protocols/oscar/userinfo.c:452
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1077
+msgid "Age"
+msgstr "Wiek"
+
+#: ../libpurple/protocols/gg/pubdir-prpl.c:534
+msgid "Male or female"
+msgstr "Mężczyzna lub kobieta"
+
+#: ../libpurple/protocols/gg/pubdir-prpl.c:541
+msgid "Find buddies"
+msgstr "Wyszukiwanie znajomych"
+
+#: ../libpurple/protocols/gg/pubdir-prpl.c:542
+msgid "Please, enter your search criteria below"
+msgstr "Proszę poniżej podać kryteria wyszukiwania"
+
+#: ../libpurple/protocols/gg/pubdir-prpl.c:670
+msgid "Error while searching for buddies"
+msgstr "Błąd podczas wyszukiwania znajomych"
+
+#: ../libpurple/protocols/gg/pubdir-prpl.c:678
+msgid "No matching users found"
+msgstr "Nie odnaleziono pasujących użytkowników"
+
+#: ../libpurple/protocols/gg/pubdir-prpl.c:679
+msgid "There are no users matching your search criteria."
+msgstr "Brak użytkowników pasujących do kryteriów wyszukiwania."
+
+#: ../libpurple/protocols/gg/pubdir-prpl.c:690
+msgid "GG Number"
+msgstr "Numer GG"
+
+#: ../libpurple/protocols/gg/pubdir-prpl.c:727
+msgid "New search"
+msgstr "Nowe wyszukiwanie"
+
+#: ../libpurple/protocols/gg/pubdir-prpl.c:736
+msgid "Search results"
+msgstr "Wyniki wyszukiwania"
+
+#: ../libpurple/protocols/gg/pubdir-prpl.c:832
+#: ../libpurple/protocols/silc/ops.c:1019
+msgid "Birth Day"
+msgstr "Data urodzenia"
+
+#: ../libpurple/protocols/gg/pubdir-prpl.c:842
+msgid "Voivodeship"
+msgstr "Województwo"
+
+#: ../libpurple/protocols/gg/purplew.c:67
+#: ../libpurple/protocols/gg/purplew.c:68 ../libpurple/request.c:2275
+msgid "Please wait..."
+msgstr "Proszę czekać..."
+
+#. 2
+#: ../libpurple/protocols/gg/status.c:94
+#: ../libpurple/protocols/gg/status.c:198
+#: ../libpurple/protocols/jabber/jabber.c:2385
+#: ../libpurple/protocols/jabber/jutil.c:712
+#: ../libpurple/protocols/mxit/roster.c:56
+msgid "Chatty"
+msgstr "Chętny do rozmowy"
+
+#: ../libpurple/protocols/gg/status.c:355
+msgid "Change status broadcasting"
+msgstr "Zmiana rozgłaszania stanu"
+
+#: ../libpurple/protocols/gg/status.c:356
+msgid "Please, select who can see your status"
+msgstr "Proszę wybrać, kto może widzieć stan"
+
+#: ../libpurple/protocols/gg/status.c:462
+msgid "Not a buddy"
+msgstr "Nie jest znajomym"
+
+#: ../libpurple/protocols/gg/validator.c:52
+msgid "Captcha validation failed"
+msgstr "Sprawdzenie CAPTCHA się nie powiodło"
+
+#: ../libpurple/protocols/gg/validator.c:72
+msgid "Password can contain 6-15 alphanumeric characters"
+msgstr "Hasło musi zawierać 6-15 znaków alfanumerycznych"
+
+#: ../libpurple/protocols/gg/validator.c:96
+msgid "Passwords do not match"
+msgstr "Hasła się nie zgadzają"
+
+#: ../libpurple/protocols/irc/cmds.c:43
+#: ../libpurple/protocols/silc/silc.c:1917
#, c-format
msgid "Unknown command: %s"
msgstr "Nieznane polecenie: %s"
+#: ../libpurple/protocols/irc/cmds.c:580
+#: ../libpurple/protocols/jabber/jabber.c:2857
+#: ../libpurple/protocols/silc/silc.c:1605
#, c-format
msgid "current topic is: %s"
msgstr "obecny temat to: %s"
+#: ../libpurple/protocols/irc/cmds.c:584
+#: ../libpurple/protocols/jabber/jabber.c:2861
+#: ../libpurple/protocols/silc/silc.c:1609
msgid "No topic is set"
msgstr "Nie ustawiono tematu"
+#: ../libpurple/protocols/irc/dcc_send.c:303
+#: ../libpurple/protocols/irc/dcc_send.c:345
+#: ../libpurple/protocols/yahoo/yahoo_filexfer.c:943
+#: ../libpurple/protocols/yahoo/yahoo_filexfer.c:1037
msgid "File Transfer Failed"
msgstr "Przesyłanie plików się nie powiodło"
+#: ../libpurple/protocols/irc/dcc_send.c:304
+#: ../libpurple/protocols/irc/dcc_send.c:346
msgid "Unable to open a listening port."
msgstr "Nie można otworzyć portu nasłuchiwania."
+#: ../libpurple/protocols/irc/irc.c:77
msgid "Error displaying MOTD"
msgstr "Błąd podczas wyświetlania wiadomości dnia (MOTD)"
+#: ../libpurple/protocols/irc/irc.c:78
msgid "No MOTD available"
msgstr "Brak dostępnej wiadomości dnia (MOTD)"
+#: ../libpurple/protocols/irc/irc.c:79
msgid "There is no MOTD associated with this connection."
msgstr "Brak wiadomości dnia (MOTD) powiązanej z tym połączeniem."
+#: ../libpurple/protocols/irc/irc.c:83
#, c-format
msgid "MOTD for %s"
msgstr "Wiadomość dnia (MOTD) dla %s"
-#.
-#. * TODO: Handle this better. Probably requires a PurpleBOSHConnection
-#. * buffer that stores what is "being sent" until the
-#. * PurpleHTTPConnection reports it is fully sent.
-#.
#. TODO: do we really want to disconnect on a failure to write?
+#: ../libpurple/protocols/irc/irc.c:134 ../libpurple/protocols/irc/irc.c:179
+#: ../libpurple/protocols/irc/irc.c:704 ../libpurple/protocols/irc/irc.c:735
+#: ../libpurple/protocols/jabber/jabber.c:410
+#: ../libpurple/protocols/jabber/jabber.c:446
+#: ../libpurple/protocols/jabber/jabber.c:671
+#: ../libpurple/protocols/jabber/jabber.c:729
+#: ../libpurple/protocols/oscar/flap_connection.c:470
+#: ../libpurple/protocols/sametime/sametime.c:412
+#: ../libpurple/protocols/sametime/sametime.c:1786
+#: ../libpurple/protocols/simple/simple.c:441
+#: ../libpurple/protocols/yahoo/libymsg.c:3326
+#: ../libpurple/protocols/yahoo/ycht.c:477 ../libpurple/proxy.c:939
+#: ../libpurple/proxy.c:1522 ../libpurple/proxy.c:1636
+#: ../libpurple/proxy.c:1874 ../libpurple/proxy.c:1950
#, c-format
msgid "Lost connection with server: %s"
msgstr "Utracono połączenie z serwerem: %s"
+#: ../libpurple/protocols/irc/irc.c:294
msgid "View MOTD"
msgstr "Wyświetl wiadomość dnia (MOTD)"
+#: ../libpurple/protocols/irc/irc.c:306 ../libpurple/protocols/silc/chat.c:36
msgid "_Channel:"
msgstr "_Kanał:"
+#: ../libpurple/protocols/irc/irc.c:312
+#: ../libpurple/protocols/jabber/chat.c:62 ../pidgin/gtkaccount.c:759
msgid "_Password:"
msgstr "_Hasło:"
+#: ../libpurple/protocols/irc/irc.c:346
msgid "IRC nick and server may not contain whitespace"
msgstr "Pseudonimy i serwery IRC nie mogą zawierać spacji"
-msgid "SSL support unavailable"
-msgstr "Obsługa SSL jest niedostępna"
-
+#: ../libpurple/protocols/irc/irc.c:391
+#: ../libpurple/protocols/jabber/bosh.c:198
+#: ../libpurple/protocols/jabber/jabber.c:788
+#: ../libpurple/protocols/jabber/jabber.c:872
+#: ../libpurple/protocols/msn/msn.c:1394
+#: ../libpurple/protocols/msn/servconn.c:143
+#: ../libpurple/protocols/novell/novell.c:1776
+#: ../libpurple/protocols/oscar/oscar.c:809
+#: ../libpurple/protocols/oscar/oscar.c:875
+#: ../libpurple/protocols/oscar/oscar.c:996
+#: ../libpurple/protocols/sametime/sametime.c:3727
+#: ../libpurple/protocols/silc/silc.c:452
+#: ../libpurple/protocols/simple/simple.c:490
+#: ../libpurple/protocols/simple/simple.c:1862
+#: ../libpurple/protocols/yahoo/libymsg.c:3477
+#: ../libpurple/protocols/yahoo/libymsg.c:3506
+#: ../libpurple/protocols/yahoo/libymsg.c:3716
+#: ../libpurple/protocols/yahoo/libymsg.c:3739
+#: ../libpurple/protocols/yahoo/libymsg.c:3752
+#: ../libpurple/protocols/yahoo/yahoochat.c:1460
+#: ../libpurple/protocols/yahoo/ycht.c:550
+#: ../libpurple/protocols/yahoo/ycht.c:586
msgid "Unable to connect"
msgstr "Nie można się połączyć"
#. this is a regular connect, error out
+#: ../libpurple/protocols/irc/irc.c:482
+#: ../libpurple/protocols/oscar/flap_connection.c:475
+#: ../libpurple/protocols/sametime/sametime.c:1810
+#: ../libpurple/protocols/simple/simple.c:460
+#: ../libpurple/protocols/simple/simple.c:1742
+#: ../libpurple/protocols/yahoo/libymsg.c:3415
+#: ../libpurple/protocols/yahoo/libymsg.c:3442
#, c-format
msgid "Unable to connect: %s"
msgstr "Nie można połączyć: %s"
+#. 1
+#. get_yahoo_status_from_purple_status() returns YAHOO_STATUS_CUSTOM for
+#. * the generic away state (YAHOO_STATUS_TYPE_AWAY) with no message
+#. Away stuff
+#: ../libpurple/protocols/irc/irc.c:605 ../libpurple/protocols/irc/msgs.c:419
+#: ../libpurple/protocols/jabber/jutil.c:713
+#: ../libpurple/protocols/mxit/roster.c:55
+#: ../libpurple/protocols/novell/novell.c:2875
+#: ../libpurple/protocols/oscar/oscar.c:158
+#: ../libpurple/protocols/silc/buddy.c:1499
+#: ../libpurple/protocols/yahoo/libymsg.c:4811
+#: ../libpurple/protocols/yahoo/libymsg.c:4897 ../libpurple/status.c:141
+#: ../pidgin/gtkdocklet.c:586 ../pidgin/gtkprefs.c:3430
+#: ../pidgin/gtkstatusbox.c:1052 ../pidgin/plugins/themeedit-icon.c:65
+msgid "Away"
+msgstr "Zaraz wracam"
+
+#: ../libpurple/protocols/irc/irc.c:713 ../libpurple/protocols/irc/irc.c:744
+#: ../libpurple/protocols/jabber/jabber.c:669
+#: ../libpurple/protocols/jabber/jabber.c:727
+#: ../libpurple/protocols/oscar/flap_connection.c:468
+#: ../libpurple/protocols/sametime/sametime.c:1778
+#: ../libpurple/protocols/yahoo/libymsg.c:3333
+#: ../libpurple/protocols/yahoo/ycht.c:483 ../libpurple/proxy.c:928
+#: ../libpurple/proxy.c:1510 ../libpurple/proxy.c:1624
+#: ../libpurple/proxy.c:1863 ../libpurple/proxy.c:1938
msgid "Server closed the connection"
msgstr "Połączenie zostało zamknięte przez serwer"
+#: ../libpurple/protocols/irc/irc.c:884
+#: ../libpurple/protocols/silc/chat.c:1412
+#: ../libpurple/protocols/yahoo/yahoochat.c:1543
msgid "Users"
msgstr "Użytkownicy"
+#: ../libpurple/protocols/irc/irc.c:887
+#: ../libpurple/protocols/sametime/sametime.c:3411
+#: ../libpurple/protocols/silc/chat.c:1415
+#: ../libpurple/protocols/silc/ops.c:1412
+#: ../libpurple/protocols/yahoo/yahoochat.c:1552
msgid "Topic"
msgstr "Temat"
@@ -3379,1391 +5117,1897 @@ msgstr "Temat"
#. *< id
#. *< name
#. *< version
+#: ../libpurple/protocols/irc/irc.c:1037
msgid "IRC Protocol Plugin"
msgstr "Wtyczka protokołu IRC"
#. * summary
+#: ../libpurple/protocols/irc/irc.c:1038
msgid "The IRC Protocol Plugin that Sucks Less"
msgstr "Mniej beznadziejna wtyczka protokołu IRC"
#. set up account ID as user:server
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
+#: ../libpurple/protocols/irc/irc.c:1063 ../libpurple/protocols/irc/msgs.c:440
+#: ../libpurple/protocols/msn/msn.c:2998
+#: ../libpurple/protocols/oscar/oscar.c:5581
+#: ../libpurple/protocols/sametime/sametime.c:5711
+#: ../libpurple/protocols/silc/ops.c:1271
+#: ../libpurple/protocols/silc/ops.c:1360
+#: ../libpurple/protocols/simple/simple.c:2166
msgid "Server"
msgstr "Serwer"
#. port to connect to
+#: ../libpurple/protocols/irc/irc.c:1066 ../libpurple/protocols/msn/msn.c:3003
+#: ../libpurple/protocols/oscar/oscar.c:5584
+#: ../libpurple/protocols/sametime/sametime.c:5720
+#: ../libpurple/protocols/silc/silc.c:2208
msgid "Port"
msgstr "Port"
+#: ../libpurple/protocols/irc/irc.c:1069
msgid "Encodings"
msgstr "Kodowania"
+#: ../libpurple/protocols/irc/irc.c:1072
msgid "Auto-detect incoming UTF-8"
msgstr "Automatyczne wykrywanie przychodzącego UTF-8"
+#: ../libpurple/protocols/irc/irc.c:1075 ../libpurple/protocols/irc/msgs.c:431
msgid "Ident name"
msgstr "Nazwa protokołu Ident"
+#: ../libpurple/protocols/irc/irc.c:1078 ../libpurple/protocols/irc/msgs.c:423
+#: ../libpurple/protocols/silc/silc.c:1260
msgid "Real name"
msgstr "Imię i nazwisko"
-#.
-#. option = purple_account_option_string_new(_("Quit message"), "quitmsg", IRC_DEFAULT_QUIT);
-#. prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-#.
+#. option = purple_account_option_string_new(_("Quit message"), "quitmsg",
+#. IRC_DEFAULT_QUIT);
+#. prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
+#. option);
+#: ../libpurple/protocols/irc/irc.c:1086
msgid "Use SSL"
msgstr "Użycie SSL"
+#: ../libpurple/protocols/irc/irc.c:1090
msgid "Authenticate with SASL"
msgstr "Uwierzytelnienie za pomocą SASL"
+#: ../libpurple/protocols/irc/irc.c:1094
msgid "Allow plaintext SASL auth over unencrypted connection"
-msgstr ""
-"Zezwolenie na uwierzytelnianie zwykłym tekstem SASL przez niezaszyfrowane "
-"połączenie"
+msgstr "Zezwolenie na uwierzytelnianie zwykłym tekstem SASL przez niezaszyfrowane połączenie"
+#: ../libpurple/protocols/irc/msgs.c:258
msgid "Bad mode"
msgstr "Błędny tryb"
+#: ../libpurple/protocols/irc/msgs.c:276
#, c-format
msgid "Ban on %s by %s, set %s ago"
msgstr "Zablokowany na %s przez %s, ustawiono %s temu"
+#: ../libpurple/protocols/irc/msgs.c:280
#, c-format
msgid "Ban on %s"
msgstr "Zablokowany na %s"
+#: ../libpurple/protocols/irc/msgs.c:294
msgid "End of ban list"
msgstr "Koniec listy zablokowanych"
+#: ../libpurple/protocols/irc/msgs.c:305
#, c-format
msgid "You are banned from %s."
msgstr "Użytkownik został zablokowany na %s."
+#: ../libpurple/protocols/irc/msgs.c:306
msgid "Banned"
msgstr "Zablokowany"
+#: ../libpurple/protocols/irc/msgs.c:321
#, c-format
msgid "Cannot ban %s: banlist is full"
msgstr "Nie można zablokować %s: lista zablokowanych jest pełna"
+#: ../libpurple/protocols/irc/msgs.c:412
msgid " <i>(ircop)</i>"
msgstr " <i>(operator IRC)</i>"
+#: ../libpurple/protocols/irc/msgs.c:413
msgid " <i>(identified)</i>"
msgstr " <i>(zidentyfikowany)</i>"
+#: ../libpurple/protocols/irc/msgs.c:414
+#: ../libpurple/protocols/oscar/userinfo.c:404
+#: ../libpurple/protocols/silc/ops.c:1442
msgid "Nick"
msgstr "Pseudonim"
+#: ../libpurple/protocols/irc/msgs.c:427
msgid "Login name"
msgstr "Login"
+#: ../libpurple/protocols/irc/msgs.c:435
msgid "Host name"
msgstr "Nazwa komputera"
+#: ../libpurple/protocols/irc/msgs.c:446
+#: ../libpurple/protocols/silc/ops.c:1290
msgid "Currently on"
msgstr "Obecnie na"
+#: ../libpurple/protocols/irc/msgs.c:451
msgid "Idle for"
msgstr "Bezczynny od"
+#: ../libpurple/protocols/irc/msgs.c:454
msgid "Online since"
msgstr "W trybie online od"
+#: ../libpurple/protocols/irc/msgs.c:458
msgid "<b>Defining adjective:</b>"
msgstr "<b>Określenie przymiotnika:</b>"
+#: ../libpurple/protocols/irc/msgs.c:458
msgid "Glorious"
msgstr "Wspaniały"
+#: ../libpurple/protocols/irc/msgs.c:590
#, c-format
msgid "%s has changed the topic to: %s"
msgstr "Użytkownik %s zmienił temat na: %s"
+#: ../libpurple/protocols/irc/msgs.c:592
#, c-format
msgid "%s has cleared the topic."
msgstr "Użytkownik %s wyczyścił temat."
+#: ../libpurple/protocols/irc/msgs.c:601
#, c-format
msgid "The topic for %s is: %s"
msgstr "Temat dla %s to: %s"
+#: ../libpurple/protocols/irc/msgs.c:633
#, c-format
msgid "Topic for %s set by %s at %s on %s"
msgstr "Temat dla %s ustawiony przez użytkownika %s o godzinie %s w dniu %s"
+#: ../libpurple/protocols/irc/msgs.c:648
#, c-format
msgid "Unknown message '%s'"
msgstr "Nieznana wiadomość \"%s\""
+#: ../libpurple/protocols/irc/msgs.c:649
msgid "Unknown message"
msgstr "Nieznana wiadomość"
+#: ../libpurple/protocols/irc/msgs.c:649
msgid "The IRC server received a message it did not understand."
msgstr "Serwer IRC odebrał wiadomość, której nie mógł zinterpretować."
+#: ../libpurple/protocols/irc/msgs.c:672
#, c-format
msgid "Users on %s: %s"
msgstr "Użytkownicy na %s: %s"
+#: ../libpurple/protocols/irc/msgs.c:779
msgid "Time Response"
msgstr "Czas odpowiedzi"
+#: ../libpurple/protocols/irc/msgs.c:780
msgid "The IRC server's local time is:"
msgstr "Lokalny czas serwera IRC:"
+#: ../libpurple/protocols/irc/msgs.c:790
msgid "No such channel"
msgstr "Nie ma takiego kanału"
-#. does this happen?
-msgid "no such channel"
-msgstr "nie ma takiego kanału"
-
+#: ../libpurple/protocols/irc/msgs.c:802
msgid "User is not logged in"
msgstr "Użytkownik nie jest zalogowany"
+#: ../libpurple/protocols/irc/msgs.c:802
+msgid "no such channel"
+msgstr "nie ma takiego kanału"
+
+#: ../libpurple/protocols/irc/msgs.c:808
msgid "No such nick or channel"
msgstr "Brak takiego pseudonimu lub kanału"
+#: ../libpurple/protocols/irc/msgs.c:830
msgid "Could not send"
msgstr "Nie można wysłać"
+#: ../libpurple/protocols/irc/msgs.c:883
#, c-format
msgid "Joining %s requires an invitation."
msgstr "Dołączenie do %s wymaga zaproszenia."
+#: ../libpurple/protocols/irc/msgs.c:884
msgid "Invitation only"
msgstr "Tylko dla zaproszonych"
+#: ../libpurple/protocols/irc/msgs.c:1009
+#: ../libpurple/protocols/jabber/presence.c:742
#, c-format
msgid "You have been kicked by %s: (%s)"
msgstr "Użytkownik został wyrzucony przez %s: (%s)"
#. Remove user from channel
+#: ../libpurple/protocols/irc/msgs.c:1014
+#: ../libpurple/protocols/jabber/presence.c:749
+#: ../libpurple/protocols/silc/ops.c:725
#, c-format
msgid "Kicked by %s (%s)"
msgstr "Wyrzucony przez %s (%s)"
+#: ../libpurple/protocols/irc/msgs.c:1037
#, c-format
msgid "mode (%s %s) by %s"
msgstr "tryb (%s %s) przez %s"
+#: ../libpurple/protocols/irc/msgs.c:1129
+#: ../libpurple/protocols/jabber/jabber.c:2878
msgid "Invalid nickname"
msgstr "Nieprawidłowy pseudonim"
+#: ../libpurple/protocols/irc/msgs.c:1130
msgid ""
"Your selected nickname was rejected by the server. It probably contains "
"invalid characters."
-msgstr ""
-"Wybrany pseudonim został odrzucony przez serwer. Prawdopodobnie zawiera "
-"nieprawidłowe znaki."
+msgstr "Wybrany pseudonim został odrzucony przez serwer. Prawdopodobnie zawiera nieprawidłowe znaki."
+#: ../libpurple/protocols/irc/msgs.c:1137
msgid ""
-"Your selected account name was rejected by the server. It probably contains "
-"invalid characters."
-msgstr ""
-"Wybrana nazwa konta została odrzucona przez serwer. Prawdopodobnie zawiera "
-"nieprawidłowe znaki."
+"Your selected account name was rejected by the server. It probably contains"
+" invalid characters."
+msgstr "Wybrana nazwa konta została odrzucona przez serwer. Prawdopodobnie zawiera nieprawidłowe znaki."
#. We only want to do the following dance if the connection
#. has not been successfully completed. If it has, just
#. notify the user that their /nick command didn't go.
+#: ../libpurple/protocols/irc/msgs.c:1150
#, c-format
msgid "The nickname \"%s\" is already being used."
msgstr "Pseudonim \"%s\" jest już używany."
+#: ../libpurple/protocols/irc/msgs.c:1152
msgid "Nickname in use"
msgstr "Używany pseudonim"
+#: ../libpurple/protocols/irc/msgs.c:1193
msgid "Cannot change nick"
msgstr "Nie można zmienić pseudonimu"
+#: ../libpurple/protocols/irc/msgs.c:1194
msgid "Could not change nick"
msgstr "Nie można zmienić pseudonimu"
+#: ../libpurple/protocols/irc/msgs.c:1219
#, c-format
msgid "You have parted the channel%s%s"
msgstr "Opuszczono kanał%s%s"
+#: ../libpurple/protocols/irc/msgs.c:1258
msgid "Error: invalid PONG from server"
msgstr "Błąd: nieprawidłowe PONG od serwera"
+#: ../libpurple/protocols/irc/msgs.c:1260
#, c-format
msgid "PING reply -- Lag: %lu seconds"
msgstr "Odpowiedź PING - opóźnienie: %lu sekund"
+#: ../libpurple/protocols/irc/msgs.c:1344
#, c-format
msgid "Cannot join %s: Registration is required."
msgstr "Nie można dołączyć do %s: wymagana jest rejestracja."
+#: ../libpurple/protocols/irc/msgs.c:1345
+#: ../libpurple/protocols/silc/ops.c:1105
msgid "Cannot join channel"
msgstr "Nie można dołączyć do kanału"
+#: ../libpurple/protocols/irc/msgs.c:1377
msgid "Nick or channel is temporarily unavailable."
msgstr "Pseudonim lub kanał jest chwilowo niedostępny."
+#: ../libpurple/protocols/irc/msgs.c:1390
#, c-format
msgid "Wallops from %s"
msgstr "Zbiorowe nadawanie praw operatora od %s"
+#: ../libpurple/protocols/irc/msgs.c:1494
+#: ../libpurple/protocols/irc/msgs.c:1617
#, c-format
msgid "Failed to initialize SASL authentication: %s"
msgstr "Zainicjowanie uwierzytelniania SASL się nie powiodło: %s"
+#: ../libpurple/protocols/irc/msgs.c:1515
msgid "SASL authentication failed: No worthy authentication mechanisms found."
-msgstr ""
-"Uwierzytelnienie SASL się nie powiodło: nie odnaleziono wartościowych "
-"mechanizmów uwierzytelniania."
+msgstr "Uwierzytelnienie SASL się nie powiodło: nie odnaleziono wartościowych mechanizmów uwierzytelniania."
+#: ../libpurple/protocols/irc/msgs.c:1521
+#: ../libpurple/protocols/irc/msgs.c:1657
#, c-format
msgid "SASL authentication failed: %s"
msgstr "Uwierzytelnienie SASL się nie powiodło: %s"
+#: ../libpurple/protocols/irc/msgs.c:1568
msgid ""
"SASL authentication failed: Server does not support SASL authentication."
-msgstr ""
-"Uwierzytelnienie SASL się nie powiodło: serwer nie obsługuje "
-"uwierzytelniania SASL."
+msgstr "Uwierzytelnienie SASL się nie powiodło: serwer nie obsługuje uwierzytelniania SASL."
+#: ../libpurple/protocols/irc/msgs.c:1577
msgid "SASL authentication failed: Initializing SASL failed."
-msgstr ""
-"Uwierzytelnienie SASL się nie powiodło: zainicjowanie SASL się nie powiodło."
+msgstr "Uwierzytelnienie SASL się nie powiodło: zainicjowanie SASL się nie powiodło."
+#: ../libpurple/protocols/irc/msgs.c:1707
+#: ../libpurple/protocols/irc/msgs.c:1750
msgid "Incorrect Password"
msgstr "Niepoprawne hasło"
+#: ../libpurple/protocols/irc/msgs.c:1734
msgid "SASL authentication failed: No worthy mechanisms found"
-msgstr ""
-"Uwierzytelnienie SASL się nie powiodło: nie odnaleziono wartościowych "
-"mechanizmów"
+msgstr "Uwierzytelnienie SASL się nie powiodło: nie odnaleziono wartościowych mechanizmów"
+#: ../libpurple/protocols/irc/parse.c:141
msgid "action &lt;action to perform&gt;: Perform an action."
msgstr "action &lt;czynność do wykonania&gt;: wykonuje czynność."
+#: ../libpurple/protocols/irc/parse.c:142
msgid "authserv: Send a command to authserv"
msgstr "authserv: wysyła polecenie do authserv"
+#: ../libpurple/protocols/irc/parse.c:143
msgid ""
-"away [message]: Set an away message, or use no message to return from being "
-"away."
-msgstr ""
-"away [wiadomość]: ustawia wiadomość nieobecności lub powraca ze stanu "
-"nieobecności, jeśli nie podano wiadomości."
+"away [message]: Set an away message, or use no message to return from being"
+" away."
+msgstr "away [wiadomość]: ustawia wiadomość nieobecności lub powraca ze stanu nieobecności, jeśli nie podano wiadomości."
+#: ../libpurple/protocols/irc/parse.c:144
msgid "ctcp <nick> <msg>: sends ctcp msg to nick."
msgstr "ctcp <pseudonim> <wiadomość>: wysyła wiadomość ctcp do pseudonimu."
+#: ../libpurple/protocols/irc/parse.c:145
msgid "chanserv: Send a command to chanserv"
msgstr "chanserv: wysyła polecenie do chanserv"
+#: ../libpurple/protocols/irc/parse.c:146
msgid ""
"deop &lt;nick1&gt; [nick2] ...: Remove channel operator status from "
"someone. You must be a channel operator to do this."
-msgstr ""
-"deop &lt;pseudonim1&gt; [pseudonim2]...: usuwa stan operatora kanału z "
-"podanych osób. Aby użyć tego polecenia, należy być operatorem kanału."
+msgstr "deop &lt;pseudonim1&gt; [pseudonim2]...: usuwa stan operatora kanału z podanych osób. Aby użyć tego polecenia, należy być operatorem kanału."
+#: ../libpurple/protocols/irc/parse.c:147
msgid ""
"devoice &lt;nick1&gt; [nick2] ...: Remove channel voice status from "
-"someone, preventing them from speaking if the channel is moderated (+m). You "
-"must be a channel operator to do this."
-msgstr ""
-"devoice &lt;pseudonim1&gt; [pseudonim2]...: usuwa prawo głosu z podanej "
-"osoby, uniemożliwiając jej rozmowę, jeśli kanał jest moderowany (+m). Aby "
-"użyć tego polecenia, należy być operatorem kanału."
+"someone, preventing them from speaking if the channel is moderated (+m). You"
+" must be a channel operator to do this."
+msgstr "devoice &lt;pseudonim1&gt; [pseudonim2]...: usuwa prawo głosu z podanej osoby, uniemożliwiając jej rozmowę, jeśli kanał jest moderowany (+m). Aby użyć tego polecenia, należy być operatorem kanału."
+#: ../libpurple/protocols/irc/parse.c:148
msgid ""
"invite &lt;nick&gt; [room]: Invite someone to join you in the specified "
"channel, or the current channel."
-msgstr ""
-"invite &lt;pseudonim&gt; [pokój]: zaprasza podaną osobę na wybrany lub "
-"bieżący kanał."
+msgstr "invite &lt;pseudonim&gt; [pokój]: zaprasza podaną osobę na wybrany lub bieżący kanał."
+#: ../libpurple/protocols/irc/parse.c:149
msgid ""
"j &lt;room1&gt;[,room2][,...] [key1[,key2][,...]]: Enter one or more "
"channels, optionally providing a channel key for each if needed."
-msgstr ""
-"j &lt;pokój1&gt;[,pokój2][,...] [klucz1[,klucz2][,...]]: dołącza do jednego "
-"lub więcej kanałów, opcjonalnie dostarczając klucz kanałom, które tego "
-"wymagają."
+msgstr "j &lt;pokój1&gt;[,pokój2][,...] [klucz1[,klucz2][,...]]: dołącza do jednego lub więcej kanałów, opcjonalnie dostarczając klucz kanałom, które tego wymagają."
+#: ../libpurple/protocols/irc/parse.c:150
msgid ""
"join &lt;room1&gt;[,room2][,...] [key1[,key2][,...]]: Enter one or more "
"channels, optionally providing a channel key for each if needed."
-msgstr ""
-"join &lt;pokój1&gt;[,pokój2][,...] [klucz1[,klucz2][,...]]: dołącza do "
-"jednego lub więcej kanałów, opcjonalnie dostarczając klucz kanałom, które "
-"tego wymagają."
+msgstr "join &lt;pokój1&gt;[,pokój2][,...] [klucz1[,klucz2][,...]]: dołącza do jednego lub więcej kanałów, opcjonalnie dostarczając klucz kanałom, które tego wymagają."
+#: ../libpurple/protocols/irc/parse.c:151
msgid ""
"kick &lt;nick&gt; [message]: Remove someone from a channel. You must be a "
"channel operator to do this."
-msgstr ""
-"kick &lt;pseudonim&gt; [wiadomość]: usuwa podaną osobę z kanału. Aby użyć "
-"tego polecenia, należy być operatorem kanału."
+msgstr "kick &lt;pseudonim&gt; [wiadomość]: usuwa podaną osobę z kanału. Aby użyć tego polecenia, należy być operatorem kanału."
+#: ../libpurple/protocols/irc/parse.c:152
msgid ""
-"list: Display a list of chat rooms on the network. <i>Warning, some servers "
-"may disconnect you upon doing this.</i>"
-msgstr ""
-"list: wyświetla listę pokoi konferencji w sieci. <i>Ostrzeżenie: niektóre "
-"serwery mogą rozłączyć po wykonaniu tego polecenia.</i>"
+"list: Display a list of chat rooms on the network. <i>Warning, some servers"
+" may disconnect you upon doing this.</i>"
+msgstr "list: wyświetla listę pokoi konferencji w sieci. <i>Ostrzeżenie: niektóre serwery mogą rozłączyć po wykonaniu tego polecenia.</i>"
+#: ../libpurple/protocols/irc/parse.c:153
msgid "me &lt;action to perform&gt;: Perform an action."
msgstr "me &lt;czynność do wykonania&gt;: wykonuje czynność."
+#: ../libpurple/protocols/irc/parse.c:154
msgid "memoserv: Send a command to memoserv"
msgstr "memoserv: wysyła polecenie do memoserv"
+#: ../libpurple/protocols/irc/parse.c:155
msgid ""
-"mode &lt;+|-&gt;&lt;A-Za-z&gt; &lt;nick|channel&gt;: Set or unset a channel "
-"or user mode."
-msgstr ""
-"mode &lt;+|-&gt;&lt;A-Za-z&gt; &lt;pseudonim|kanał&gt;: ustawia lub usuwa "
-"ustawienie trybu kanału lub użytkownika."
+"mode &lt;+|-&gt;&lt;A-Za-z&gt; &lt;nick|channel&gt;: Set or unset a channel"
+" or user mode."
+msgstr "mode &lt;+|-&gt;&lt;A-Za-z&gt; &lt;pseudonim|kanał&gt;: ustawia lub usuwa ustawienie trybu kanału lub użytkownika."
+#: ../libpurple/protocols/irc/parse.c:156
msgid ""
"msg &lt;nick&gt; &lt;message&gt;: Send a private message to a user (as "
"opposed to a channel)."
-msgstr ""
-"msg &lt;pseudonim&gt; &lt;wiadomość&gt;: wysyła prywatną wiadomość do "
-"użytkownika (nie zostanie wyświetlona na kanale)."
+msgstr "msg &lt;pseudonim&gt; &lt;wiadomość&gt;: wysyła prywatną wiadomość do użytkownika (nie zostanie wyświetlona na kanale)."
+#: ../libpurple/protocols/irc/parse.c:157
msgid "names [channel]: List the users currently in a channel."
msgstr "names [kanał]: wyświetla listę użytkowników na kanale."
+#: ../libpurple/protocols/irc/parse.c:158
+#: ../libpurple/protocols/jabber/jabber.c:3631
msgid "nick &lt;new nickname&gt;: Change your nickname."
msgstr "nick &lt;nowy pseudonim&gt;: zmienia pseudonim."
+#: ../libpurple/protocols/irc/parse.c:159
msgid "nickserv: Send a command to nickserv"
msgstr "nickserv: wysyła polecenie do nickserv"
+#: ../libpurple/protocols/irc/parse.c:160
msgid "notice &lt;target&lt;: Send a notice to a user or channel."
msgstr "notice &lt;cel&lt;: wysyła uwagę do użytkownika lub kanału."
+#: ../libpurple/protocols/irc/parse.c:161
msgid ""
-"op &lt;nick1&gt; [nick2] ...: Grant channel operator status to someone. You "
-"must be a channel operator to do this."
-msgstr ""
-"op &lt;pseudonim1&gt; [pseudonim2]...: nadaje podanej osobie stan operatora "
-"kanału. Aby użyć tego polecenia, należy być operatorem kanału."
+"op &lt;nick1&gt; [nick2] ...: Grant channel operator status to someone. You"
+" must be a channel operator to do this."
+msgstr "op &lt;pseudonim1&gt; [pseudonim2]...: nadaje podanej osobie stan operatora kanału. Aby użyć tego polecenia, należy być operatorem kanału."
+#: ../libpurple/protocols/irc/parse.c:162
msgid ""
"operwall &lt;message&gt;: If you don't know what this is, you probably "
"can't use it."
-msgstr ""
-"operwall &lt;wiadomość&gt;: jeżeli nie wiadomo, co to jest, prawdopodobnie "
-"nie można tego użyć."
+msgstr "operwall &lt;wiadomość&gt;: jeżeli nie wiadomo, co to jest, prawdopodobnie nie można tego użyć."
+#: ../libpurple/protocols/irc/parse.c:163
msgid "operserv: Send a command to operserv"
msgstr "operserv: wysyła polecenie do operserv"
+#: ../libpurple/protocols/irc/parse.c:164
msgid ""
"part [room] [message]: Leave the current channel, or a specified channel, "
"with an optional message."
-msgstr ""
-"part [kanał] [wiadomość]: opuszcza bieżący lub wybrany kanał, opcjonalnie z "
-"wiadomością pożegnalną."
+msgstr "part [kanał] [wiadomość]: opuszcza bieżący lub wybrany kanał, opcjonalnie z wiadomością pożegnalną."
+#: ../libpurple/protocols/irc/parse.c:165
msgid ""
"ping [nick]: Asks how much lag a user (or the server if no user specified) "
"has."
-msgstr ""
-"ping [pseudonim]: sprawdza czas opóźnienia między użytkownikiem (lub "
-"serwerem, jeśli nie podano użytkownika)."
+msgstr "ping [pseudonim]: sprawdza czas opóźnienia między użytkownikiem (lub serwerem, jeśli nie podano użytkownika)."
+#: ../libpurple/protocols/irc/parse.c:166
msgid ""
"query &lt;nick&gt; &lt;message&gt;: Send a private message to a user (as "
"opposed to a channel)."
-msgstr ""
-"query &lt;pseudonim&gt; &lt;wiadomość&gt;: wysyła prywatną wiadomość do "
-"użytkownika (nie zostanie wyświetlona na kanale)."
+msgstr "query &lt;pseudonim&gt; &lt;wiadomość&gt;: wysyła prywatną wiadomość do użytkownika (nie zostanie wyświetlona na kanale)."
+#: ../libpurple/protocols/irc/parse.c:167
msgid "quit [message]: Disconnect from the server, with an optional message."
-msgstr ""
-"quit [wiadomość]: rozłącza z serwera, opcjonalnie z wiadomością pożegnalną."
+msgstr "quit [wiadomość]: rozłącza z serwera, opcjonalnie z wiadomością pożegnalną."
+#: ../libpurple/protocols/irc/parse.c:168
msgid "quote [...]: Send a raw command to the server."
msgstr "quote [...]: wysyła surowe polecenie do serwera."
+#: ../libpurple/protocols/irc/parse.c:169
msgid ""
"remove &lt;nick&gt; [message]: Remove someone from a room. You must be a "
"channel operator to do this."
-msgstr ""
-"remove &lt;pseudonim&gt; [wiadomość]: usuwa podaną osobę z pokoju. Aby użyć "
-"tego polecenia, należy być operatorem kanału."
+msgstr "remove &lt;pseudonim&gt; [wiadomość]: usuwa podaną osobę z pokoju. Aby użyć tego polecenia, należy być operatorem kanału."
+#: ../libpurple/protocols/irc/parse.c:170
msgid "time: Displays the current local time at the IRC server."
msgstr "time: wyświetla bieżący lokalny czas serwera IRC."
+#: ../libpurple/protocols/irc/parse.c:171
msgid "topic [new topic]: View or change the channel topic."
msgstr "topic [nowy temat]: wyświetla lub zmienia temat kanału."
+#: ../libpurple/protocols/irc/parse.c:172
msgid "umode &lt;+|-&gt;&lt;A-Za-z&gt;: Set or unset a user mode."
-msgstr ""
-"umode &lt;+|-&gt;&lt;A-Za-z&gt;: ustawia lub usuwa ustawienie trybu "
-"użytkownika."
+msgstr "umode &lt;+|-&gt;&lt;A-Za-z&gt;: ustawia lub usuwa ustawienie trybu użytkownika."
+#: ../libpurple/protocols/irc/parse.c:173
msgid "version [nick]: send CTCP VERSION request to a user"
msgstr "version [pseudonim]: wysyła żądanie CTCP VERSION do użytkownika"
+#: ../libpurple/protocols/irc/parse.c:174
msgid ""
-"voice &lt;nick1&gt; [nick2] ...: Grant channel voice status to someone. You "
-"must be a channel operator to do this."
-msgstr ""
-"voice &lt;pseudonim1&gt; [pseudonim2]...: nadaje prawo głosu podanej osobie. "
-"Aby użyć tego polecenia, należy być operatorem kanału."
+"voice &lt;nick1&gt; [nick2] ...: Grant channel voice status to someone. You"
+" must be a channel operator to do this."
+msgstr "voice &lt;pseudonim1&gt; [pseudonim2]...: nadaje prawo głosu podanej osobie. Aby użyć tego polecenia, należy być operatorem kanału."
+#: ../libpurple/protocols/irc/parse.c:175
msgid ""
-"wallops &lt;message&gt;: If you don't know what this is, you probably can't "
-"use it."
-msgstr ""
-"wallops &lt;wiadomość&gt;: jeżeli nie wiadomo, co to jest, prawdopodobnie "
-"nie można tego użyć."
+"wallops &lt;message&gt;: If you don't know what this is, you probably can't"
+" use it."
+msgstr "wallops &lt;wiadomość&gt;: jeżeli nie wiadomo, co to jest, prawdopodobnie nie można tego użyć."
+#: ../libpurple/protocols/irc/parse.c:176
msgid "whois [server] &lt;nick&gt;: Get information on a user."
msgstr "whois [serwer] &lt;pseudonim&gt;: uzyskuje informacje o użytkowniku."
+#: ../libpurple/protocols/irc/parse.c:177
msgid "whowas &lt;nick&gt;: Get information on a user that has logged off."
-msgstr ""
-"whowas &lt;pseudonim&gt;: uzyskuje informacje o użytkowniku, który właśnie "
-"się wylogował."
+msgstr "whowas &lt;pseudonim&gt;: uzyskuje informacje o użytkowniku, który właśnie się wylogował."
+#: ../libpurple/protocols/irc/parse.c:573
#, c-format
msgid "Reply time from %s: %lu seconds"
msgstr "Czas odpowiedzi od %s: %lu sekund"
+#: ../libpurple/protocols/irc/parse.c:574
msgid "PONG"
msgstr "PONG"
+#: ../libpurple/protocols/irc/parse.c:575
msgid "CTCP PING reply"
msgstr "Odpowiedź CTCP PING"
+#: ../libpurple/protocols/irc/parse.c:692
+#: ../libpurple/protocols/irc/parse.c:699
msgid "Disconnected."
msgstr "Rozłączono."
+#: ../libpurple/protocols/jabber/adhoccommands.c:161
+#: ../libpurple/protocols/jabber/auth.c:201
+#: ../libpurple/protocols/jabber/auth_scram.c:74
+#: ../libpurple/protocols/jabber/jabber.c:1156
+#: ../libpurple/protocols/jabber/jabber.c:1192
+#: ../libpurple/protocols/jabber/jabber.c:2717
msgid "Unknown Error"
msgstr "Nieznany błąd"
+#: ../libpurple/protocols/jabber/adhoccommands.c:163
+#: ../libpurple/protocols/jabber/adhoccommands.c:164
msgid "Ad-Hoc Command Failed"
msgstr "Polecenie ad-hoc się nie powiodło"
+#: ../libpurple/protocols/jabber/adhoccommands.c:200
msgid "execute"
msgstr "wykonaj"
+#: ../libpurple/protocols/jabber/auth.c:84
+#: ../libpurple/protocols/jabber/auth_plain.c:80
msgid "Server requires plaintext authentication over an unencrypted stream"
-msgstr ""
-"Serwer wymaga uwierzytelnienia w zwykłym tekście za pośrednictwem "
-"niezaszyfrowanego strumienia"
+msgstr "Serwer wymaga uwierzytelnienia w zwykłym tekście za pośrednictwem niezaszyfrowanego strumienia"
#. This happens when the server sends back jibberish
#. * in the "additional data with success" case.
#. * Seen with Wildfire 3.0.1.
-#.
+#: ../libpurple/protocols/jabber/auth.c:152
+#: ../libpurple/protocols/jabber/auth.c:406
+#: ../libpurple/protocols/jabber/auth.c:435
+#: ../libpurple/protocols/jabber/auth.c:446
+#: ../libpurple/protocols/jabber/auth.c:491
+#: ../libpurple/protocols/jabber/auth_cyrus.c:534
+#: ../libpurple/protocols/jabber/auth_digest_md5.c:178
+#: ../libpurple/protocols/jabber/jabber.c:157
msgid "Invalid response from server"
msgstr "Nieprawidłowa odpowiedź z serwera"
+#: ../libpurple/protocols/jabber/auth.c:193
+#: ../libpurple/protocols/jabber/auth.c:333
+#: ../libpurple/protocols/jabber/auth_cyrus.c:445
msgid "Server does not use any supported authentication method"
msgstr "Serwer nie używa obsługiwanej metody uwierzytelniania"
+#: ../libpurple/protocols/jabber/auth.c:317
+#: ../libpurple/protocols/jabber/auth_plain.c:94
#, c-format
msgid ""
"%s requires plaintext authentication over an unencrypted connection. Allow "
"this and continue authentication?"
-msgstr ""
-"%s wymaga uwierzytelnienia w zwykłym tekście za pośrednictwem "
-"niezaszyfrowanego strumienia. Pozwolić i kontynuować uwierzytelnianie?"
-
+msgstr "%s wymaga uwierzytelnienia w zwykłym tekście za pośrednictwem niezaszyfrowanego strumienia. Pozwolić i kontynuować uwierzytelnianie?"
+
+#: ../libpurple/protocols/jabber/auth.c:319
+#: ../libpurple/protocols/jabber/auth.c:320
+#: ../libpurple/protocols/jabber/auth_cyrus.c:262
+#: ../libpurple/protocols/jabber/auth_cyrus.c:263
+#: ../libpurple/protocols/jabber/auth_plain.c:96
+#: ../libpurple/protocols/jabber/auth_plain.c:97
msgid "Plaintext Authentication"
msgstr "Uwierzytelnianie w zwykłym tekście"
+#: ../libpurple/protocols/jabber/auth.c:357
+#: ../libpurple/protocols/jabber/jabber.c:279
msgid "You require encryption, but it is not available on this server."
msgstr "Wymagane jest szyfrowanie, ale nie jest ono dostępne na tym serwerze."
+#: ../libpurple/protocols/jabber/auth.c:417
+#: ../libpurple/protocols/jabber/auth_digest_md5.c:198
+#: ../libpurple/protocols/jabber/auth_digest_md5.c:223
+#: ../libpurple/protocols/jabber/auth_scram.c:469
+#: ../libpurple/protocols/jabber/auth_scram.c:489
+#: ../libpurple/protocols/jabber/auth_scram.c:526
+#: ../libpurple/protocols/jabber/auth_scram.c:560
msgid "Invalid challenge from server"
msgstr "Nieprawidłowe wyzwanie od serwera"
+#: ../libpurple/protocols/jabber/auth.c:451
msgid "Server thinks authentication is complete, but client does not"
msgstr "Serwer uważa, że uwierzytelnienie zostało ukończone, a klient nie"
+#: ../libpurple/protocols/jabber/auth_cyrus.c:39
msgid "Server may require plaintext authentication over an unencrypted stream"
-msgstr ""
-"Serwer może wymagać uwierzytelnienia w zwykłym tekście przez niezaszyfrowany "
-"strumień"
+msgstr "Serwer może wymagać uwierzytelnienia w zwykłym tekście przez niezaszyfrowany strumień"
+#: ../libpurple/protocols/jabber/auth_cyrus.c:260
#, c-format
msgid ""
"%s may require plaintext authentication over an unencrypted connection. "
"Allow this and continue authentication?"
-msgstr ""
-"%s może wymagać uwierzytelnienia w zwykłym tekście przez niezaszyfrowany "
-"strumień. Pozwolić i kontynuować uwierzytelnianie?"
+msgstr "%s może wymagać uwierzytelnienia w zwykłym tekście przez niezaszyfrowany strumień. Pozwolić i kontynuować uwierzytelnianie?"
+#: ../libpurple/protocols/jabber/auth_cyrus.c:297
msgid "SASL authentication failed"
msgstr "Uwierzytelnienie SASL się nie powiodło"
+#: ../libpurple/protocols/jabber/auth_cyrus.c:468
+#: ../libpurple/protocols/jabber/jabber.c:546
+#: ../libpurple/protocols/jabber/jabber.c:700
#, c-format
msgid "SASL error: %s"
msgstr "Błąd SASL: %s"
+#: ../libpurple/protocols/jabber/auth_scram.c:56
msgid "Invalid Encoding"
msgstr "Nieprawidłowe kodowanie"
+#: ../libpurple/protocols/jabber/auth_scram.c:58
msgid "Unsupported Extension"
msgstr "Nieobsługiwane rozszerzenie"
+#: ../libpurple/protocols/jabber/auth_scram.c:60
msgid ""
"Unexpected response from the server. This may indicate a possible MITM "
"attack"
-msgstr ""
-"Nieoczekiwana odpowiedź z serwera. Może to wskazywać na możliwy atak MITM"
+msgstr "Nieoczekiwana odpowiedź z serwera. Może to wskazywać na możliwy atak MITM"
+#: ../libpurple/protocols/jabber/auth_scram.c:62
msgid ""
-"The server does support channel binding, but did not appear to advertise "
-"it. This indicates a likely MITM attack"
-msgstr ""
-"Serwer obsługuje dowiązywanie kanału, ale nie ogłasza tego. Wskazuje to na "
-"prawdopodobny atak MITM"
+"The server does support channel binding, but did not appear to advertise it."
+" This indicates a likely MITM attack"
+msgstr "Serwer obsługuje dowiązywanie kanału, ale nie ogłasza tego. Wskazuje to na prawdopodobny atak MITM"
+#: ../libpurple/protocols/jabber/auth_scram.c:64
msgid "Server does not support channel binding"
msgstr "Serwer nie obsługuje dowiązywania kanału"
+#: ../libpurple/protocols/jabber/auth_scram.c:66
msgid "Unsupported channel binding method"
msgstr "Nieobsługiwana metoda dowiązywania kanału"
+#: ../libpurple/protocols/jabber/auth_scram.c:68
+#: ../libpurple/protocols/novell/nmuser.c:1896
msgid "User not found"
msgstr "Nie odnaleziono użytkownika"
+#: ../libpurple/protocols/jabber/auth_scram.c:70
msgid "Invalid Username Encoding"
msgstr "Nieprawidłowe kodowanie nazwy użytkownika"
+#: ../libpurple/protocols/jabber/auth_scram.c:72
+#: ../libpurple/protocols/jabber/jabber.c:2782
msgid "Resource Constraint"
msgstr "Ograniczenie zasobów"
+#: ../libpurple/protocols/jabber/auth_scram.c:403
msgid "Unable to canonicalize username"
msgstr "Nie można ustawić kanonicznej nazwy użytkownika"
+#: ../libpurple/protocols/jabber/auth_scram.c:414
msgid "Unable to canonicalize password"
msgstr "Nie można ustawić kanonicznego hasła"
+#: ../libpurple/protocols/jabber/auth_scram.c:479
+#: ../libpurple/protocols/jabber/auth_scram.c:551
msgid "Malicious challenge from server"
msgstr "Złośliwe wyzwanie od serwera"
+#: ../libpurple/protocols/jabber/auth_scram.c:541
msgid "Unexpected response from server"
msgstr "Nieoczekiwana odpowiedź z serwera"
+#: ../libpurple/protocols/jabber/bosh.c:208
msgid "The BOSH connection manager terminated your session."
msgstr "Menedżer połączeń BOSH zakończył sesję."
-msgid "No session ID given"
-msgstr "Nie podano identyfikatora sesji"
+#: ../libpurple/protocols/jabber/bosh.c:407
+msgid "No BOSH session ID given"
+msgstr "Nie podano identyfikatora sesji BOSH"
+#: ../libpurple/protocols/jabber/bosh.c:419
msgid "Unsupported version of BOSH protocol"
msgstr "Nieobsługiwana wersja protokołu BOSH"
-msgid "Unable to establish a connection with the server"
-msgstr "Nie można nawiązać połączenia z serwerem"
-
-#, c-format
-msgid "Unable to establish a connection with the server: %s"
-msgstr "Nie można nawiązać połączenia z serwerem: %s"
-
-msgid "Unable to establish SSL connection"
-msgstr "Nie można nawiązać połączenia SSL"
-
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
+#: ../libpurple/protocols/jabber/buddy.c:350
+#: ../libpurple/protocols/jabber/buddy.c:1046
+#: ../libpurple/protocols/sametime/sametime.c:4120
+#: ../libpurple/protocols/silc/ops.c:999
msgid "Full Name"
msgstr "Imię i nazwisko"
+#: ../libpurple/protocols/jabber/buddy.c:351
+#: ../libpurple/protocols/jabber/buddy.c:1057
+#: ../libpurple/protocols/silc/ops.c:1011
msgid "Family Name"
msgstr "Nazwisko"
+#: ../libpurple/protocols/jabber/buddy.c:352
+#: ../libpurple/protocols/jabber/buddy.c:1059
msgid "Given Name"
msgstr "Imię"
+#: ../libpurple/protocols/jabber/buddy.c:354
+#: ../libpurple/protocols/jabber/jabber.c:1325
msgid "URL"
msgstr "Adres URL"
+#: ../libpurple/protocols/jabber/buddy.c:355
+#: ../libpurple/protocols/jabber/buddy.c:1108
msgid "Street Address"
msgstr "Ulica"
-#.
#. * EXTADD is correct, EXTADR is generated by other
#. * clients. The next time someone reads this, remove
#. * EXTADR.
-#.
+#: ../libpurple/protocols/jabber/buddy.c:356
+#: ../libpurple/protocols/jabber/buddy.c:1106
msgid "Extended Address"
msgstr "Rozszerzony adres"
+#: ../libpurple/protocols/jabber/buddy.c:357
+#: ../libpurple/protocols/jabber/buddy.c:1110
msgid "Locality"
msgstr "Położenie"
+#: ../libpurple/protocols/jabber/buddy.c:358
+#: ../libpurple/protocols/jabber/buddy.c:1112
msgid "Region"
msgstr "Region"
+#: ../libpurple/protocols/jabber/buddy.c:359
+#: ../libpurple/protocols/jabber/buddy.c:1114
msgid "Postal Code"
msgstr "Kod pocztowy"
+#: ../libpurple/protocols/jabber/buddy.c:360
+#: ../libpurple/protocols/jabber/buddy.c:1117
+#: ../libpurple/protocols/mxit/profile.c:224
+#: ../libpurple/protocols/silc/silc.c:1266
msgid "Country"
msgstr "Kraj"
#. lots of clients (including purple) do this, but it's
#. * out of spec
+#: ../libpurple/protocols/jabber/buddy.c:361
+#: ../libpurple/protocols/jabber/buddy.c:1131
+#: ../libpurple/protocols/jabber/buddy.c:1137
msgid "Telephone"
msgstr "Telefon"
+#: ../libpurple/protocols/jabber/buddy.c:363
+#: ../libpurple/protocols/jabber/buddy.c:1178
msgid "Organization Name"
msgstr "Nazwa organizacji"
+#: ../libpurple/protocols/jabber/buddy.c:364
+#: ../libpurple/protocols/jabber/buddy.c:1180
msgid "Organization Unit"
msgstr "Jednostka organizacyjna"
+#: ../libpurple/protocols/jabber/buddy.c:365
+#: ../libpurple/protocols/jabber/buddy.c:1185
+#: ../libpurple/protocols/msn/msn.c:2496
+#: ../libpurple/protocols/novell/novell.c:1515
+#: ../libpurple/protocols/silc/ops.c:1023
msgid "Job Title"
msgstr "Stanowisko"
+#: ../libpurple/protocols/jabber/buddy.c:366
+#: ../libpurple/protocols/jabber/buddy.c:1187
msgid "Role"
msgstr "Rola"
-#. birthday
-#. birthday (required)
-msgid "Birthday"
-msgstr "Data urodzenia"
-
+#: ../libpurple/protocols/jabber/buddy.c:368
+#: ../libpurple/protocols/jabber/buddy.c:1189
+#: ../libpurple/protocols/jabber/chat.c:927 ../pidgin/gtkblist.c:4028
+#: ../pidgin/gtkplugin.c:699 ../pidgin/plugins/disco/gtkdisco.c:578
msgid "Description"
msgstr "Opis"
+#: ../libpurple/protocols/jabber/buddy.c:687
+#: ../libpurple/protocols/jabber/buddy.c:688
msgid "Edit XMPP vCard"
msgstr "Modyfikacja vCard protokołu XMPP"
+#: ../libpurple/protocols/jabber/buddy.c:689
msgid ""
-"All items below are optional. Enter only the information with which you feel "
-"comfortable."
-msgstr ""
-"Wypełnienie poniższych pól nie jest obowiązkowe. Proszę podać tylko te "
-"informacje, które mają zostać udostępnione."
+"All items below are optional. Enter only the information with which you feel"
+" comfortable."
+msgstr "Wypełnienie poniższych pól nie jest obowiązkowe. Proszę podać tylko te informacje, które mają zostać udostępnione."
+#. TODO: Check whether it's correct to call prepend_pair_html,
+#. or if we should be using prepend_pair_plaintext
+#: ../libpurple/protocols/jabber/buddy.c:736
msgid "Client"
msgstr "Klient"
+#. TODO: Check whether it's correct to call prepend_pair_html,
+#. or if we should be using prepend_pair_plaintext
+#: ../libpurple/protocols/jabber/buddy.c:742
msgid "Operating System"
msgstr "System operacyjny"
+#: ../libpurple/protocols/jabber/buddy.c:759
msgid "Local Time"
msgstr "Czas lokalny"
+#: ../libpurple/protocols/jabber/buddy.c:790
+#: ../libpurple/protocols/jabber/jabber.c:2362
+#: ../libpurple/protocols/jabber/jabber.c:2386
+#: ../libpurple/protocols/jabber/jabber.c:2402
+#: ../libpurple/protocols/jabber/jabber.c:2418
+#: ../libpurple/protocols/jabber/jabber.c:2432
msgid "Priority"
msgstr "Priorytet"
+#. TODO: Check whether it's correct to call prepend_pair_html,
+#. or if we should be using prepend_pair_plaintext
+#: ../libpurple/protocols/jabber/buddy.c:838
+#: ../libpurple/protocols/jabber/jabber.c:3353
+#: ../libpurple/protocols/jabber/libfacebook.c:220
+#: ../libpurple/protocols/jabber/libgtalk.c:267
+#: ../libpurple/protocols/jabber/libxmpp.c:261
+#: ../libpurple/protocols/jabber/si.c:1558
msgid "Resource"
msgstr "Zasób"
+#: ../libpurple/protocols/jabber/buddy.c:852
msgid "Uptime"
msgstr "Czas działania"
+#: ../libpurple/protocols/jabber/buddy.c:856
msgid "Logged Off"
msgstr "Wylogowano"
+#: ../libpurple/protocols/jabber/buddy.c:857
#, c-format
msgid "%s ago"
msgstr "%s temu"
+#: ../libpurple/protocols/jabber/buddy.c:1061
+#: ../libpurple/protocols/silc/ops.c:1007
+#: ../libpurple/protocols/yahoo/yahoo_aliases.c:513
msgid "Middle Name"
msgstr "Drugie imię"
+#: ../libpurple/protocols/jabber/buddy.c:1094
+#: ../libpurple/protocols/jabber/jabber.c:1320
+#: ../libpurple/protocols/oscar/userinfo.c:465
+#: ../libpurple/protocols/oscar/userinfo.c:473
+#: ../libpurple/protocols/silc/ops.c:1043
msgid "Address"
msgstr "Adres"
+#: ../libpurple/protocols/jabber/buddy.c:1099
msgid "P.O. Box"
msgstr "Skrytka pocztowa"
+#: ../libpurple/protocols/jabber/buddy.c:1215
+#: ../libpurple/protocols/mxit/profile.c:277
msgid "Photo"
msgstr "Zdjęcie"
+#: ../libpurple/protocols/jabber/buddy.c:1215
msgid "Logo"
msgstr "Logo"
+#: ../libpurple/protocols/jabber/buddy.c:1760
#, c-format
msgid ""
"%s will no longer be able to see your status updates. Do you want to "
"continue?"
-msgstr ""
-"Użytkownik %s nie będzie już mógł widzieć aktualizacji stanu. Kontynuować?"
+msgstr "Użytkownik %s nie będzie już mógł widzieć aktualizacji stanu. Kontynuować?"
+#: ../libpurple/protocols/jabber/buddy.c:1762
+#: ../libpurple/protocols/jabber/buddy.c:1871
msgid "Cancel Presence Notification"
msgstr "Anuluj powiadamianie o obecności"
+#: ../libpurple/protocols/jabber/buddy.c:1859
msgid "Un-hide From"
msgstr "Wyłącz ukrywanie"
+#: ../libpurple/protocols/jabber/buddy.c:1863
msgid "Temporarily Hide From"
msgstr "Tymczasowo ukryj z"
+#: ../libpurple/protocols/jabber/buddy.c:1878
msgid "(Re-)Request authorization"
msgstr "Ponów prośbę o upoważnienie"
#. shouldn't this just happen automatically when the buddy is
#. removed?
+#: ../libpurple/protocols/jabber/buddy.c:1887
msgid "Unsubscribe"
msgstr "Usuń subskrypcję"
+#: ../libpurple/protocols/jabber/buddy.c:1894
+#: ../libpurple/protocols/msn/msn.c:1309
+#: ../libpurple/protocols/novell/novell.c:3471
msgid "Initiate _Chat"
msgstr "Rozpocznij _konferencję"
+#: ../libpurple/protocols/jabber/buddy.c:1911
msgid "Log In"
msgstr "Zaloguj się"
+#: ../libpurple/protocols/jabber/buddy.c:1915
msgid "Log Out"
msgstr "Wyloguj się"
+#: ../libpurple/protocols/jabber/buddy.c:2026
msgid "JID"
msgstr "Identyfikator Jabbera"
+#. first name
+#. optional information
+#: ../libpurple/protocols/jabber/buddy.c:2028
+#: ../libpurple/protocols/jabber/buddy.c:2216
+#: ../libpurple/protocols/mxit/actions.c:248
+#: ../libpurple/protocols/mxit/profile.c:221
+#: ../libpurple/protocols/mxit/profile.c:337
+#: ../libpurple/protocols/oscar/userinfo.c:414
+#: ../libpurple/protocols/silc/ops.c:1003
+#: ../libpurple/protocols/yahoo/yahoo_aliases.c:510
+msgid "First Name"
+msgstr "Imię"
+
#. last name
+#: ../libpurple/protocols/jabber/buddy.c:2030
+#: ../libpurple/protocols/jabber/buddy.c:2221
+#: ../libpurple/protocols/mxit/actions.c:252
+#: ../libpurple/protocols/mxit/profile.c:222
+#: ../libpurple/protocols/mxit/profile.c:339
+#: ../libpurple/protocols/oscar/userinfo.c:415
+#: ../libpurple/protocols/yahoo/yahoo_aliases.c:511
msgid "Last Name"
msgstr "Nazwisko"
+#: ../libpurple/protocols/jabber/buddy.c:2062
msgid "The following are the results of your search"
msgstr "Poniżej znajdują się wyniki wyszukiwania"
#. current comment from Jabber User Directory users.jabber.org
+#: ../libpurple/protocols/jabber/buddy.c:2147
msgid ""
"Find a contact by entering the search criteria in the given fields. Note: "
"Each field supports wild card searches (%)"
-msgstr ""
-"Można znaleźć kontakt podając kryteria w pola poniżej. Uwaga: można używać "
-"wzorców (%)"
+msgstr "Można znaleźć kontakt podając kryteria w pola poniżej. Uwaga: można używać wzorców (%)"
+#: ../libpurple/protocols/jabber/buddy.c:2168
msgid "Directory Query Failed"
msgstr "Odpytanie katalogu się nie powiodło"
+#: ../libpurple/protocols/jabber/buddy.c:2169
msgid "Could not query the directory server."
msgstr "Nie można odpytać serwera katalogu."
#. Try to translate the message (see static message
#. list in jabber_user_dir_comments[])
+#: ../libpurple/protocols/jabber/buddy.c:2204
#, c-format
msgid "Server Instructions: %s"
msgstr "Instrukcje serwera: %s"
+#: ../libpurple/protocols/jabber/buddy.c:2211
msgid "Fill in one or more fields to search for any matching XMPP users."
msgstr "Proszę wypełnić jedno lub więcej pól, aby wyszukać użytkowników XMPP."
+#: ../libpurple/protocols/jabber/buddy.c:2231
+#: ../libpurple/protocols/novell/novell.c:1519
+#: ../libpurple/protocols/oscar/userinfo.c:416
+#: ../libpurple/protocols/oscar/userinfo.c:420
msgid "Email Address"
msgstr "Adres e-mail"
+#: ../libpurple/protocols/jabber/buddy.c:2240
+#: ../libpurple/protocols/jabber/buddy.c:2241
msgid "Search for XMPP users"
msgstr "Wyszukiwanie użytkowników XMPP"
#. "Search"
+#: ../libpurple/protocols/jabber/buddy.c:2242
+#: ../libpurple/protocols/sametime/sametime.c:5598
+#: ../pidgin/plugins/gevolution/add_buddy_dialog.c:476
+#: ../pidgin/plugins/gevolution/assoc-buddy.c:356
msgid "Search"
msgstr "Wyszukaj"
+#: ../libpurple/protocols/jabber/buddy.c:2257
+#: ../libpurple/protocols/jabber/buddy.c:2258
msgid "Invalid Directory"
msgstr "Nieprawidłowy katalog"
+#: ../libpurple/protocols/jabber/buddy.c:2289
msgid "Enter a User Directory"
msgstr "Katalog użytkowników"
+#: ../libpurple/protocols/jabber/buddy.c:2290
msgid "Select a user directory to search"
msgstr "Proszę wybrać katalog użytkowników do przeszukania"
+#: ../libpurple/protocols/jabber/buddy.c:2293
msgid "Search Directory"
msgstr "Przeszukaj katalog"
+#: ../libpurple/protocols/jabber/chat.c:44
+#: ../libpurple/protocols/oscar/oscar.c:4225
+#: ../libpurple/protocols/yahoo/yahoochat.c:1157
msgid "_Room:"
msgstr "_Pokój:"
+#: ../libpurple/protocols/jabber/chat.c:50
msgid "_Server:"
msgstr "_Serwer:"
+#: ../libpurple/protocols/jabber/chat.c:56
msgid "_Handle:"
msgstr "_Uchwyt:"
+#: ../libpurple/protocols/jabber/chat.c:378
#, c-format
msgid "%s is not a valid room name"
msgstr "%s nie jest prawidłową nazwą pokoju"
+#: ../libpurple/protocols/jabber/chat.c:379
msgid "Invalid Room Name"
msgstr "Nieprawidłowa nazwa pokoju"
+#: ../libpurple/protocols/jabber/chat.c:385
#, c-format
msgid "%s is not a valid server name"
msgstr "%s nie jest prawidłową nazwą serwera"
+#: ../libpurple/protocols/jabber/chat.c:386
+#: ../libpurple/protocols/jabber/chat.c:387
msgid "Invalid Server Name"
msgstr "Nieprawidłowa nazwa serwera"
+#: ../libpurple/protocols/jabber/chat.c:393
#, c-format
msgid "%s is not a valid room handle"
msgstr "%s nie jest prawidłowym uchwytem pokoju"
+#: ../libpurple/protocols/jabber/chat.c:394
+#: ../libpurple/protocols/jabber/chat.c:395
msgid "Invalid Room Handle"
msgstr "Nieprawidłowy uchwyt pokoju"
+#: ../libpurple/protocols/jabber/chat.c:539
+#: ../libpurple/protocols/jabber/chat.c:540
msgid "Configuration error"
msgstr "Błąd konfiguracji"
+#: ../libpurple/protocols/jabber/chat.c:550
+#: ../libpurple/protocols/jabber/chat.c:551
+#: ../libpurple/protocols/jabber/chat.c:702
msgid "Unable to configure"
msgstr "Nie można skonfigurować"
+#: ../libpurple/protocols/jabber/chat.c:567
+#: ../libpurple/protocols/jabber/chat.c:568
msgid "Room Configuration Error"
msgstr "Błąd konfiguracji pokoju"
+#: ../libpurple/protocols/jabber/chat.c:569
msgid "This room is not capable of being configured"
msgstr "Ten pokój nie może być konfigurowany"
+#: ../libpurple/protocols/jabber/chat.c:620
+#: ../libpurple/protocols/jabber/chat.c:621
+#: ../libpurple/protocols/jabber/chat.c:691
+#: ../libpurple/protocols/jabber/chat.c:692
msgid "Registration error"
msgstr "Błąd rejestracji"
+#: ../libpurple/protocols/jabber/chat.c:771
msgid "Nick changing not supported in non-MUC chatrooms"
-msgstr ""
-"Zmienianie pseudonimu nie jest obsługiwane w konferencjach nie będących MUC"
+msgstr "Zmienianie pseudonimu nie jest obsługiwane w konferencjach nie będących MUC"
+#: ../libpurple/protocols/jabber/chat.c:825
+#: ../libpurple/protocols/jabber/chat.c:837
+#: ../libpurple/protocols/silc/ops.c:1484
msgid "Error retrieving room list"
msgstr "Błąd podczas pobierania listy pokoi"
+#: ../libpurple/protocols/jabber/chat.c:886
+#: ../libpurple/protocols/jabber/chat.c:887
+#: ../pidgin/plugins/disco/gtkdisco.c:182
msgid "Invalid Server"
msgstr "Nieprawidłowy serwer"
+#: ../libpurple/protocols/jabber/chat.c:933
msgid "Enter a Conference Server"
msgstr "Proszę podać serwer konferencji"
+#: ../libpurple/protocols/jabber/chat.c:934
msgid "Select a conference server to query"
msgstr "Proszę wybrać serwer konferencji do odpytania"
+#: ../libpurple/protocols/jabber/chat.c:937
msgid "Find Rooms"
msgstr "Znajdź pokoje"
+#: ../libpurple/protocols/jabber/chat.c:1077
msgid "Affiliations:"
msgstr "Przynależności:"
+#: ../libpurple/protocols/jabber/chat.c:1089
+#: ../libpurple/protocols/jabber/chat.c:1184
msgid "No users found"
msgstr "Nie odnaleziono użytkowników"
+#: ../libpurple/protocols/jabber/chat.c:1172
msgid "Roles:"
msgstr "Role:"
-msgid "Server requires TLS/SSL, but no TLS/SSL support was found."
-msgstr "Serwer wymaga TLS/SSL, ale ich obsługa nie została odnaleziona."
-
+#: ../libpurple/protocols/jabber/jabber.c:258
msgid "You require encryption, but no TLS/SSL support was found."
-msgstr ""
-"Wymagana jest szyfrowanie, ale obsługa TLS/SSL nie została odnaleziona."
+msgstr "Wymagana jest szyfrowanie, ale obsługa TLS/SSL nie została odnaleziona."
+#: ../libpurple/protocols/jabber/jabber.c:625
msgid "Ping timed out"
msgstr "Przekroczono czas oczekiwania na ping"
+#: ../libpurple/protocols/jabber/jabber.c:940
+#: ../libpurple/protocols/jabber/jabber.c:978
+#: ../libpurple/protocols/jabber/jabber.c:3024
msgid "Invalid XMPP ID"
msgstr "Nieprawidłowy identyfikator XMPP"
+#: ../libpurple/protocols/jabber/jabber.c:949
msgid "Invalid XMPP ID. Username portion must be set."
-msgstr ""
-"Nieprawidłowy identyfikator XMPP. Należy ustawić część nazwy użytkownika."
+msgstr "Nieprawidłowy identyfikator XMPP. Należy ustawić część nazwy użytkownika."
+#: ../libpurple/protocols/jabber/jabber.c:958
msgid "Invalid XMPP ID. Domain must be set."
msgstr "Nieprawidłowy identyfikator XMPP. Należy ustawić domenę."
+#: ../libpurple/protocols/jabber/jabber.c:1034
msgid "Malformed BOSH URL"
msgstr "Błędnie sformatowany adresu URL protokołu BOSH"
+#: ../libpurple/protocols/jabber/jabber.c:1051
+msgid "Unable to establish SSL connection"
+msgstr "Nie można nawiązać połączenia SSL"
+
+#: ../libpurple/protocols/jabber/jabber.c:1140
#, c-format
msgid "Registration of %s@%s successful"
msgstr "Pomyślnie zarejestrowano nowe konto %s@%s"
+#: ../libpurple/protocols/jabber/jabber.c:1145
#, c-format
msgid "Registration to %s successful"
msgstr "Pomyślnie zarejestrowano nowe konto do %s"
+#: ../libpurple/protocols/jabber/jabber.c:1148
+#: ../libpurple/protocols/jabber/jabber.c:1149
msgid "Registration Successful"
msgstr "Pomyślna rejestracja"
+#: ../libpurple/protocols/jabber/jabber.c:1158
+#: ../libpurple/protocols/jabber/jabber.c:1159
msgid "Registration Failed"
msgstr "Rejestracja się nie powiodła"
+#: ../libpurple/protocols/jabber/jabber.c:1182
#, c-format
msgid "Registration from %s successfully removed"
msgstr "Pomyślnie usunięto rejestrację z %s"
+#: ../libpurple/protocols/jabber/jabber.c:1184
+#: ../libpurple/protocols/jabber/jabber.c:1185
msgid "Unregistration Successful"
msgstr "Pomyślnie wyrejestrowano"
+#: ../libpurple/protocols/jabber/jabber.c:1194
+#: ../libpurple/protocols/jabber/jabber.c:1195
msgid "Unregistration Failed"
msgstr "Wyrejestrowanie się nie powiodło"
+#: ../libpurple/protocols/jabber/jabber.c:1322
+#: ../libpurple/protocols/oscar/userinfo.c:467
+#: ../libpurple/protocols/oscar/userinfo.c:475
msgid "State"
msgstr "Stan/województwo"
+#: ../libpurple/protocols/jabber/jabber.c:1323
msgid "Postal code"
msgstr "Kod pocztowy"
+#: ../libpurple/protocols/jabber/jabber.c:1324
+#: ../libpurple/protocols/silc/ops.c:1048
+#: ../libpurple/protocols/silc/silc.c:1020
+#: ../libpurple/protocols/silc/util.c:522
msgid "Phone"
msgstr "Telefon"
+#: ../libpurple/protocols/jabber/jabber.c:1326 ../pidgin/gtknotify.c:1721
msgid "Date"
msgstr "Data"
+#: ../libpurple/protocols/jabber/jabber.c:1355
+#: ../libpurple/protocols/jabber/jabber.c:1356
msgid "Already Registered"
msgstr "Już zarejestrowano"
-msgid "Password"
-msgstr "Hasło"
+#: ../libpurple/protocols/jabber/jabber.c:1381
+msgid "Registration completed successfully. Please reconnect to continue."
+msgstr "Pomyślnie ukończono rejestrację. Proszę połączyć ponownie, aby kontynuować."
+#: ../libpurple/protocols/jabber/jabber.c:1443
msgid "Unregister"
msgstr "Wyrejestruj"
+#: ../libpurple/protocols/jabber/jabber.c:1450
msgid ""
"Please fill out the information below to change your account registration."
msgstr "Aby zmienić rejestrację konta, proszę wypełnić poniższe informacje."
+#: ../libpurple/protocols/jabber/jabber.c:1453
msgid "Please fill out the information below to register your new account."
msgstr "Aby zarejestrować nowe konto, proszę wypełnić poniższe informacje."
+#: ../libpurple/protocols/jabber/jabber.c:1461
+#: ../libpurple/protocols/jabber/jabber.c:1462
msgid "Register New XMPP Account"
msgstr "Zarejestruj nowe konto XMPP"
+#: ../libpurple/protocols/jabber/jabber.c:1463
+#: ../libpurple/protocols/jabber/jabber.c:1474
+#: ../pidgin/plugins/disco/gtkdisco.c:312
+#: ../pidgin/plugins/disco/gtkdisco.c:695
msgid "Register"
msgstr "Zarejestruj"
+#: ../libpurple/protocols/jabber/jabber.c:1470
#, c-format
msgid "Change Account Registration at %s"
msgstr "Zmień rejestrację konta na %s"
+#: ../libpurple/protocols/jabber/jabber.c:1471
#, c-format
msgid "Register New Account at %s"
msgstr "Zarejestruj nowe konto na %s"
+#: ../libpurple/protocols/jabber/jabber.c:1473
msgid "Change Registration"
msgstr "Zmień rejestrację"
+#: ../libpurple/protocols/jabber/jabber.c:1521
+#: ../libpurple/protocols/jabber/jabber.c:1522
msgid "Error unregistering account"
msgstr "Błąd podczas wyrejestrowywania konta"
+#: ../libpurple/protocols/jabber/jabber.c:1528
+#: ../libpurple/protocols/jabber/jabber.c:1529
msgid "Account successfully unregistered"
msgstr "Pomyślnie wyrejestrowano konto"
+#: ../libpurple/protocols/jabber/jabber.c:1731
msgid "Initializing Stream"
msgstr "Inicjowanie strumienia"
+#: ../libpurple/protocols/jabber/jabber.c:1736
msgid "Initializing SSL/TLS"
msgstr "Inicjowanie SSL/TLS"
+#: ../libpurple/protocols/jabber/jabber.c:1740
+#: ../libpurple/protocols/msn/session.c:436
msgid "Authenticating"
msgstr "Uwierzytelnianie"
+#: ../libpurple/protocols/jabber/jabber.c:1744
msgid "Re-initializing Stream"
msgstr "Ponowne inicjowanie strumienia"
+#: ../libpurple/protocols/jabber/jabber.c:1914
+#: ../libpurple/protocols/jabber/jabber.c:1915
msgid "Server doesn't support blocking"
msgstr "Serwer nie obsługuje blokowania"
+#: ../libpurple/protocols/jabber/jabber.c:2169
+#: ../libpurple/protocols/jabber/jabber.c:2695
+#: ../libpurple/protocols/jabber/jabber.c:2738
+#: ../libpurple/protocols/jabber/jabber.c:2776
+#: ../libpurple/protocols/oscar/oscar.c:4505
+#: ../libpurple/protocols/oscar/userinfo.c:270
msgid "Not Authorized"
msgstr "Nie upoważniono"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
+#: ../libpurple/protocols/jabber/jabber.c:2304
+#: ../libpurple/protocols/jabber/jabber.c:2307
+#: ../libpurple/protocols/jabber/jabber.c:2364
+#: ../libpurple/protocols/jabber/jabber.c:2388
+#: ../libpurple/protocols/jabber/jabber.c:2404
+#: ../libpurple/protocols/jabber/jabber.c:2420
+#: ../libpurple/protocols/jabber/jabber.c:2434
+#: ../libpurple/protocols/mxit/mxit.c:353
+#: ../libpurple/protocols/mxit/profile.c:246
+#: ../libpurple/protocols/mxit/profile.c:248
+#: ../libpurple/protocols/oscar/userinfo.c:296
+#: ../libpurple/protocols/silc/buddy.c:1586
+#: ../libpurple/protocols/silc/ops.c:1224
msgid "Mood"
msgstr "Nastrój"
+#: ../libpurple/protocols/jabber/jabber.c:2317
+#: ../libpurple/protocols/msn/msn.c:1052
msgid "Now Listening"
msgstr "Słucha teraz"
+#: ../libpurple/protocols/jabber/jabber.c:2324
+#: ../libpurple/protocols/mxit/roster.c:252
msgid "Both"
msgstr "Obustronna"
+#: ../libpurple/protocols/jabber/jabber.c:2326
msgid "From (To pending)"
msgstr "Od (do oczekuje na upoważnienie)"
+#: ../libpurple/protocols/jabber/jabber.c:2328
msgid "From"
msgstr "Od"
+#: ../libpurple/protocols/jabber/jabber.c:2331
msgid "To"
msgstr "Do"
+#: ../libpurple/protocols/jabber/jabber.c:2333
msgid "None (To pending)"
msgstr "Brak (do oczekuje na upoważnienie)"
+#: ../libpurple/protocols/jabber/jabber.c:2335
+#: ../libpurple/protocols/mxit/profile.c:248
+#: ../libpurple/protocols/mxit/roster.c:262 ../pidgin/gtkblist.c:3629
+#: ../pidgin/gtkblist.c:3631 ../pidgin/plugins/gevolution/eds-utils.c:72
+#: ../pidgin/plugins/gevolution/eds-utils.c:84
msgid "None"
msgstr "Brak"
#. subscription type
+#: ../libpurple/protocols/jabber/jabber.c:2338
+#: ../libpurple/protocols/mxit/mxit.c:357
+#: ../libpurple/protocols/mxit/profile.c:258
msgid "Subscription"
msgstr "Subskrypcja"
+#: ../libpurple/protocols/jabber/jabber.c:2365
+#: ../libpurple/protocols/jabber/jabber.c:2389
+#: ../libpurple/protocols/jabber/jabber.c:2405
+#: ../libpurple/protocols/jabber/jabber.c:2421
+#: ../libpurple/protocols/jabber/jabber.c:2435
msgid "Mood Text"
msgstr "Tekst nastroju"
+#: ../libpurple/protocols/jabber/jabber.c:2367
+#: ../libpurple/protocols/jabber/jabber.c:2391
+#: ../libpurple/protocols/jabber/jabber.c:2407
+#: ../libpurple/protocols/jabber/jabber.c:2423
msgid "Allow Buzz"
msgstr "Pozwolenie na szeptanie"
+#: ../libpurple/protocols/jabber/jabber.c:2374
+#: ../libpurple/protocols/mxit/roster.c:86
+#: ../libpurple/protocols/oscar/oscar.c:4686
msgid "Mood Name"
msgstr "Nazwa nastroju"
+#: ../libpurple/protocols/jabber/jabber.c:2375
+#: ../libpurple/protocols/oscar/oscar.c:4687
msgid "Mood Comment"
msgstr "Komentarz nastroju"
-#. primitive
-#. ID
-#. name - use default
-#. saveable
-#. should be user_settable some day
-#. independent
+#. 3
+#: ../libpurple/protocols/jabber/jabber.c:2431
+#: ../libpurple/protocols/jabber/jutil.c:715
+#: ../libpurple/protocols/mxit/roster.c:57
+#: ../libpurple/protocols/oscar/oscar.c:152
+#: ../libpurple/protocols/oscar/oscar.c:4667
+#: ../libpurple/protocols/sametime/sametime.c:3334
+msgid "Do Not Disturb"
+msgstr "Nie przeszkadzać"
+
+#: ../libpurple/protocols/jabber/jabber.c:2454
+#: ../libpurple/protocols/msn/msn.c:1205
msgid "Tune Artist"
msgstr "Wykonawca utworu"
+#: ../libpurple/protocols/jabber/jabber.c:2455
+#: ../libpurple/protocols/msn/msn.c:1207
msgid "Tune Title"
msgstr "Tytuł utworu"
+#: ../libpurple/protocols/jabber/jabber.c:2456
+#: ../libpurple/protocols/msn/msn.c:1206
msgid "Tune Album"
msgstr "Album utworu"
+#: ../libpurple/protocols/jabber/jabber.c:2457
msgid "Tune Genre"
msgstr "Gatunek utworu"
+#: ../libpurple/protocols/jabber/jabber.c:2458
msgid "Tune Comment"
msgstr "Komentarz utworu"
+#: ../libpurple/protocols/jabber/jabber.c:2459
msgid "Tune Track"
msgstr "Ścieżka utworu"
+#: ../libpurple/protocols/jabber/jabber.c:2460
msgid "Tune Time"
msgstr "Czas utworu"
+#: ../libpurple/protocols/jabber/jabber.c:2461
msgid "Tune Year"
msgstr "Rok utworu"
+#: ../libpurple/protocols/jabber/jabber.c:2462
msgid "Tune URL"
msgstr "Adres URL utworu"
+#: ../libpurple/protocols/jabber/jabber.c:2475
msgid "Password Changed"
msgstr "Zmieniono hasło"
-msgid "Your password has been changed."
-msgstr "Hasło zostało zmienione."
-
+#: ../libpurple/protocols/jabber/jabber.c:2483
+#: ../libpurple/protocols/jabber/jabber.c:2484
msgid "Error changing password"
msgstr "Błąd podczas zmieniania hasła"
-msgid "Password (again)"
-msgstr "Hasło (ponownie)"
-
+#: ../libpurple/protocols/jabber/jabber.c:2550
+#: ../libpurple/protocols/jabber/jabber.c:2551
msgid "Change XMPP Password"
msgstr "Zmiana hasła XMPP"
+#: ../libpurple/protocols/jabber/jabber.c:2551
msgid "Please enter your new password"
msgstr "Proszę podać nowe hasło"
+#. XXX: it doesn't seems to work
+#: ../libpurple/protocols/jabber/jabber.c:2564
+#: ../libpurple/protocols/oscar/oscar.c:5305
+#: ../libpurple/protocols/silc/silc.c:1344
+#: ../libpurple/protocols/yahoo/libymsg.c:4294
+#: ../libpurple/protocols/yahoo/libymsg.c:4439
msgid "Set User Info..."
msgstr "Ustaw informacje o użytkowniku..."
#. if (js->protocol_options & CHANGE_PASSWORD) {
+#: ../libpurple/protocols/jabber/jabber.c:2569
+#: ../libpurple/protocols/oscar/oscar.c:5316
+#: ../libpurple/protocols/silc/silc.c:1340
msgid "Change Password..."
msgstr "Zmień hasło..."
#. }
+#: ../libpurple/protocols/jabber/jabber.c:2574
msgid "Search for Users..."
msgstr "Wyszukaj użytkowników..."
+#: ../libpurple/protocols/jabber/jabber.c:2674
msgid "Bad Request"
msgstr "Błędne żądanie"
+#: ../libpurple/protocols/jabber/jabber.c:2677
msgid "Conflict"
msgstr "Konflikt"
+#: ../libpurple/protocols/jabber/jabber.c:2679
msgid "Feature Not Implemented"
msgstr "Funkcja nie jest jeszcze zaimplementowana"
+#: ../libpurple/protocols/jabber/jabber.c:2681
msgid "Forbidden"
msgstr "Zabronione"
+#: ../libpurple/protocols/jabber/jabber.c:2683
msgid "Gone"
msgstr "Rozłączono"
+#: ../libpurple/protocols/jabber/jabber.c:2685
+#: ../libpurple/protocols/jabber/jabber.c:2766
msgid "Internal Server Error"
msgstr "Wewnętrzny błąd serwera"
+#: ../libpurple/protocols/jabber/jabber.c:2687
msgid "Item Not Found"
msgstr "Nie odnaleziono elementu"
+#: ../libpurple/protocols/jabber/jabber.c:2689
msgid "Malformed XMPP ID"
msgstr "Błędnie sformatowany identyfikator XMPP"
+#: ../libpurple/protocols/jabber/jabber.c:2691
msgid "Not Acceptable"
msgstr "Nieakceptowalne"
+#: ../libpurple/protocols/jabber/jabber.c:2693
msgid "Not Allowed"
msgstr "Zabronione"
+#: ../libpurple/protocols/jabber/jabber.c:2697
msgid "Payment Required"
msgstr "Opłata jest wymagana"
+#: ../libpurple/protocols/jabber/jabber.c:2699
msgid "Recipient Unavailable"
msgstr "Odbiorca jest niedostępny"
+#: ../libpurple/protocols/jabber/jabber.c:2703
msgid "Registration Required"
msgstr "Wymagana rejestracja"
+#: ../libpurple/protocols/jabber/jabber.c:2705
msgid "Remote Server Not Found"
msgstr "Nie odnaleziono zdalnego serwera"
+#: ../libpurple/protocols/jabber/jabber.c:2707
msgid "Remote Server Timeout"
msgstr "Przekroczono czas oczekiwania zdalnego serwera"
+#: ../libpurple/protocols/jabber/jabber.c:2709
msgid "Server Overloaded"
msgstr "Przeciążony serwer"
+#: ../libpurple/protocols/jabber/jabber.c:2711
msgid "Service Unavailable"
msgstr "Usługa jest niedostępna"
+#: ../libpurple/protocols/jabber/jabber.c:2713
msgid "Subscription Required"
msgstr "Subskrypcja jest wymagana"
+#: ../libpurple/protocols/jabber/jabber.c:2715
msgid "Unexpected Request"
msgstr "Nieoczekiwane żądanie"
+#: ../libpurple/protocols/jabber/jabber.c:2723
msgid "Authorization Aborted"
msgstr "Przerwano upoważnienie"
+#: ../libpurple/protocols/jabber/jabber.c:2725
msgid "Incorrect encoding in authorization"
msgstr "Niepoprawne kodowanie upoważnienia"
+#: ../libpurple/protocols/jabber/jabber.c:2727
msgid "Invalid authzid"
msgstr "Nieprawidłowy identyfikator upoważnienia"
+#: ../libpurple/protocols/jabber/jabber.c:2729
msgid "Invalid Authorization Mechanism"
msgstr "Nieprawidłowy mechanizm upoważnienia"
+#: ../libpurple/protocols/jabber/jabber.c:2732
msgid "Authorization mechanism too weak"
msgstr "Mechanizm upoważnienia jest za słaby"
+#: ../libpurple/protocols/jabber/jabber.c:2740
msgid "Temporary Authentication Failure"
msgstr "Tymczasowe uwierzytelnienie się nie powiodło"
+#: ../libpurple/protocols/jabber/jabber.c:2743
msgid "Authentication Failure"
msgstr "Uwierzytelnienie się nie powiodło"
+#: ../libpurple/protocols/jabber/jabber.c:2751
msgid "Bad Format"
msgstr "Błędny format"
+#: ../libpurple/protocols/jabber/jabber.c:2753
msgid "Bad Namespace Prefix"
msgstr "Błędny przedrostek przestrzeni nazw"
+#: ../libpurple/protocols/jabber/jabber.c:2756
msgid "Resource Conflict"
msgstr "Konflikt zasobów"
+#: ../libpurple/protocols/jabber/jabber.c:2758
+#: ../libpurple/protocols/msn/msn.c:237
msgid "Connection Timeout"
msgstr "Przekroczono czas oczekiwania na połączenie"
+#: ../libpurple/protocols/jabber/jabber.c:2760
msgid "Host Gone"
msgstr "Komputer został rozłączony"
+#: ../libpurple/protocols/jabber/jabber.c:2762
msgid "Host Unknown"
msgstr "Nieznany komputer"
+#: ../libpurple/protocols/jabber/jabber.c:2764
msgid "Improper Addressing"
msgstr "Niepoprawne adresowanie"
+#: ../libpurple/protocols/jabber/jabber.c:2768
msgid "Invalid ID"
msgstr "Nieprawidłowy identyfikator"
+#: ../libpurple/protocols/jabber/jabber.c:2770
msgid "Invalid Namespace"
msgstr "Nieprawidłowa przestrzeń nazw"
+#: ../libpurple/protocols/jabber/jabber.c:2772
msgid "Invalid XML"
msgstr "Nieprawidłowy XML"
+#: ../libpurple/protocols/jabber/jabber.c:2774
msgid "Non-matching Hosts"
msgstr "Niepasujące komputery"
+#: ../libpurple/protocols/jabber/jabber.c:2778
msgid "Policy Violation"
msgstr "Naruszenie polityki"
+#: ../libpurple/protocols/jabber/jabber.c:2780
msgid "Remote Connection Failed"
msgstr "Zdalne połączenie się nie powiodło"
+#: ../libpurple/protocols/jabber/jabber.c:2784
msgid "Restricted XML"
msgstr "Zastrzeżony XML"
+#: ../libpurple/protocols/jabber/jabber.c:2786
msgid "See Other Host"
msgstr "Zobacz inne komputery"
+#: ../libpurple/protocols/jabber/jabber.c:2788
msgid "System Shutdown"
msgstr "Zamknięcie systemu"
+#: ../libpurple/protocols/jabber/jabber.c:2790
msgid "Undefined Condition"
msgstr "Nieokreślony warunek"
+#: ../libpurple/protocols/jabber/jabber.c:2792
msgid "Unsupported Encoding"
msgstr "Nieobsługiwane kodowanie"
+#: ../libpurple/protocols/jabber/jabber.c:2794
msgid "Unsupported Stanza Type"
msgstr "Nieobsługiwany typ stanzy"
+#: ../libpurple/protocols/jabber/jabber.c:2796
msgid "Unsupported Version"
msgstr "Nieobsługiwana wersja"
+#: ../libpurple/protocols/jabber/jabber.c:2798
msgid "XML Not Well Formed"
msgstr "Niepoprawnie sformatowany XML"
+#: ../libpurple/protocols/jabber/jabber.c:2800
msgid "Stream Error"
msgstr "Błąd strumienia"
+#: ../libpurple/protocols/jabber/jabber.c:2909
#, c-format
msgid "Unable to ban user %s"
msgstr "Nie można zablokować użytkownika %s"
+#: ../libpurple/protocols/jabber/jabber.c:2929
#, c-format
msgid "Unknown affiliation: \"%s\""
msgstr "Nieznana przynależność: \"%s\""
+#: ../libpurple/protocols/jabber/jabber.c:2939
#, c-format
msgid "Unable to affiliate user %s as \"%s\""
msgstr "Nie można zmienić przynależności użytkownika %s jako \"%s\""
+#: ../libpurple/protocols/jabber/jabber.c:2964
#, c-format
msgid "Unknown role: \"%s\""
msgstr "Nieznana rola: \"%s\""
+#: ../libpurple/protocols/jabber/jabber.c:2974
#, c-format
msgid "Unable to set role \"%s\" for user: %s"
msgstr "Nie można ustawić roli \"%s\" dla użytkownika: %s"
+#: ../libpurple/protocols/jabber/jabber.c:3056
#, c-format
msgid "Unable to kick user %s"
msgstr "Nie można wyrzucić użytkownika %s"
+#: ../libpurple/protocols/jabber/jabber.c:3094
#, c-format
msgid "Unable to ping user %s"
msgstr "Nie można wysłać ping użytkownikowi %s"
+#: ../libpurple/protocols/jabber/jabber.c:3116
#, c-format
msgid "Unable to buzz, because there is nothing known about %s."
msgstr "Nie można szeptać, ponieważ nic nie wiadomo o użytkowniku %s."
+#: ../libpurple/protocols/jabber/jabber.c:3123
#, c-format
msgid "Unable to buzz, because %s might be offline."
msgstr "Nie można szeptać, ponieważ użytkownik %s może być w trybie offline."
+#: ../libpurple/protocols/jabber/jabber.c:3147
#, c-format
msgid ""
"Unable to buzz, because %s does not support it or does not wish to receive "
"buzzes now."
-msgstr ""
-"Nie można szeptać, ponieważ użytkownik %s nie obsługuje lub nie chce teraz "
-"otrzymywać szeptów."
+msgstr "Nie można szeptać, ponieważ użytkownik %s nie obsługuje lub nie chce teraz otrzymywać szeptów."
#. Yahoo only supports one attention command: the 'buzz'.
#. This is index number YAHOO_BUZZ.
+#: ../libpurple/protocols/jabber/jabber.c:3195
+#: ../libpurple/protocols/yahoo/libymsg.c:5342
msgid "Buzz"
msgstr "Szepcz"
+#: ../libpurple/protocols/jabber/jabber.c:3196
+#: ../libpurple/protocols/yahoo/libymsg.c:5343
#, c-format
msgid "%s has buzzed you!"
msgstr "Użytkownik %s zaszeptał."
+#: ../libpurple/protocols/jabber/jabber.c:3196
+#: ../libpurple/protocols/yahoo/libymsg.c:5343
#, c-format
msgid "Buzzing %s..."
msgstr "Szeptanie do użytkownika %s..."
+#: ../libpurple/protocols/jabber/jabber.c:3308
#, c-format
msgid "Unable to initiate media with %s: invalid JID"
-msgstr ""
-"Nie można zainicjować multimediów z użytkownikiem %s: nieprawidłowy "
-"identyfikator Jabbera"
+msgstr "Nie można zainicjować multimediów z użytkownikiem %s: nieprawidłowy identyfikator Jabbera"
+#: ../libpurple/protocols/jabber/jabber.c:3310
#, c-format
msgid "Unable to initiate media with %s: user is not online"
-msgstr ""
-"Nie można zainicjować multimediów z użytkownikiem %s: użytkownik nie jest w "
-"trybie online"
+msgstr "Nie można zainicjować multimediów z użytkownikiem %s: użytkownik nie jest w trybie online"
+#: ../libpurple/protocols/jabber/jabber.c:3312
#, c-format
msgid "Unable to initiate media with %s: resource is not online"
-msgstr ""
-"Nie można zainicjować multimediów z użytkownikiem %s: zasób nie jest w "
-"trybie online"
+msgstr "Nie można zainicjować multimediów z użytkownikiem %s: zasób nie jest w trybie online"
+#: ../libpurple/protocols/jabber/jabber.c:3314
#, c-format
msgid "Unable to initiate media with %s: not subscribed to user presence"
-msgstr ""
-"Nie można zainicjować multimediów z użytkownikiem %s: nie subskrybowano "
-"obecności użytkownika"
+msgstr "Nie można zainicjować multimediów z użytkownikiem %s: nie subskrybowano obecności użytkownika"
+#: ../libpurple/protocols/jabber/jabber.c:3317
+#: ../libpurple/protocols/jabber/jabber.c:3318
msgid "Media Initiation Failed"
msgstr "Zainicjowanie multimediów się nie powiodło"
+#: ../libpurple/protocols/jabber/jabber.c:3405
#, c-format
msgid ""
"Please select the resource of %s with which you would like to start a media "
"session."
-msgstr ""
-"Proszę wybrać zasób użytkownika %s, z którym uruchomić sesję multimediów."
+msgstr "Proszę wybrać zasób użytkownika %s, z którym uruchomić sesję multimediów."
+#: ../libpurple/protocols/jabber/jabber.c:3415
+#: ../libpurple/protocols/jabber/si.c:1572
msgid "Select a Resource"
msgstr "Wybór Zasobu"
+#: ../libpurple/protocols/jabber/jabber.c:3416
msgid "Initiate Media"
msgstr "Zainicjuj multimedia"
+#: ../libpurple/protocols/jabber/jabber.c:3597
+msgid "Failed to specify mood"
+msgstr "Ustawienie nastroju się nie powiodło"
+
+#: ../libpurple/protocols/jabber/jabber.c:3604
msgid "Account does not support PEP, can't set mood"
msgstr "Konto nie obsługuje PEP, nie można ustawić nastroju"
+#: ../libpurple/protocols/jabber/jabber.c:3619
msgid "config: Configure a chat room."
msgstr "config: konfiguruje pokój konferencji."
+#: ../libpurple/protocols/jabber/jabber.c:3625
msgid "configure: Configure a chat room."
msgstr "configure: konfiguruje pokój konferencji."
+#: ../libpurple/protocols/jabber/jabber.c:3638
msgid "part [message]: Leave the room."
msgstr "part [wiadomość]: opuszcza pokój."
+#: ../libpurple/protocols/jabber/jabber.c:3644
msgid "register: Register with a chat room."
msgstr "register: rejestruje z pokojem konferencji."
+#: ../libpurple/protocols/jabber/jabber.c:3651
msgid "topic [new topic]: View or change the topic."
msgstr "topic [nowy temat]: wyświetla lub zmienia temat."
+#: ../libpurple/protocols/jabber/jabber.c:3657
msgid "ban &lt;user&gt; [reason]: Ban a user from the room."
msgstr "ban &lt;użytkownik&gt; [powód]: blokuje użytkownika z pokoju."
+#: ../libpurple/protocols/jabber/jabber.c:3664
msgid ""
"affiliate &lt;owner|admin|member|outcast|none&gt; [nick1] [nick2] ...: Get "
"the users with an affiliation or set users' affiliation with the room."
-msgstr ""
-"affiliate &lt;owner|admin|member|outcast|none&gt; [pseudonim1] "
-"[pseudonim2]...: uzyskuje użytkowników z przynależnością lub ustawia "
-"przynależność użytkowników z pokojem."
+msgstr "affiliate &lt;owner|admin|member|outcast|none&gt; [pseudonim1] [pseudonim2]...: uzyskuje użytkowników z przynależnością lub ustawia przynależność użytkowników z pokojem."
+#: ../libpurple/protocols/jabber/jabber.c:3673
msgid ""
-"role &lt;moderator|participant|visitor|none&gt; [nick1] [nick2] ...: Get the "
-"users with a role or set users' role with the room."
-msgstr ""
-"role &lt;moderator|participant|visitor|none&gt; [pseudonim1] "
-"[pseudonim2]...: uzyskuje użytkowników z rolami lub ustawia role "
-"użytkowników w pokoju."
+"role &lt;moderator|participant|visitor|none&gt; [nick1] [nick2] ...: Get the"
+" users with a role or set users' role with the room."
+msgstr "role &lt;moderator|participant|visitor|none&gt; [pseudonim1] [pseudonim2]...: uzyskuje użytkowników z rolami lub ustawia role użytkowników w pokoju."
+#: ../libpurple/protocols/jabber/jabber.c:3681
msgid "invite &lt;user&gt; [message]: Invite a user to the room."
msgstr "invite &lt;użytkownik&gt; [wiadomość]: zaprasza użytkownika do pokoju."
+#: ../libpurple/protocols/jabber/jabber.c:3688
msgid "join: &lt;room[@server]&gt; [password]: Join a chat."
msgstr "join: &lt;pokój[@serwer]&gt; [hasło]: dołącza do konferencji."
+#: ../libpurple/protocols/jabber/jabber.c:3695
msgid "kick &lt;user&gt; [reason]: Kick a user from the room."
msgstr "kick &lt;użytkownik&gt; [powód]: wyrzuca użytkownika z pokoju."
+#: ../libpurple/protocols/jabber/jabber.c:3701
msgid ""
"msg &lt;user&gt; &lt;message&gt;: Send a private message to another user."
-msgstr ""
-"msg &lt;użytkownik&gt; &lt;wiadomość&gt;: wysyła prywatną wiadomość do "
-"innego użytkownika."
+msgstr "msg &lt;użytkownik&gt; &lt;wiadomość&gt;: wysyła prywatną wiadomość do innego użytkownika."
-msgid "ping &lt;jid&gt;:\tPing a user/component/server."
-msgstr ""
-"ping &lt;identyfikator_Jabbera&gt;:\twysyła ping do użytkownika/składnika/"
-"serwera."
+#: ../libpurple/protocols/jabber/jabber.c:3708
+msgid "ping &lt;jid&gt;: Ping a user/component/server."
+msgstr "ping &lt;identyfikator_Jabbera&gt;: Wysyła ping do użytkownika/składnika/serwera."
+#: ../libpurple/protocols/jabber/jabber.c:3714
+#: ../libpurple/protocols/yahoo/libyahoo.c:53
+#: ../libpurple/protocols/yahoo/libyahoojp.c:50
msgid "buzz: Buzz a user to get their attention"
msgstr "buzz: szepcze do użytkownika, aby uzyskać jego uwagę"
-msgid "mood: Set current user mood"
-msgstr "mood: ustawia bieżący nastrój użytkownika"
+#: ../libpurple/protocols/jabber/jabber.c:3721
+msgid "mood &lt;mood&gt; [text]: Set current user mood"
+msgstr "mood &lt;mood&gt; [tekst]: ustawia obecny nastrój użytkownika"
+
+#. 0
+#. Global
+#: ../libpurple/protocols/jabber/jutil.c:711
+#: ../libpurple/protocols/msn/state.c:35 ../libpurple/protocols/msn/state.c:36
+#: ../libpurple/protocols/msn/state.c:43 ../libpurple/protocols/msn/state.c:44
+#: ../libpurple/protocols/mxit/roster.c:54
+#: ../libpurple/protocols/novell/novell.c:2872
+#: ../libpurple/protocols/yahoo/libymsg.c:3966 ../libpurple/status.c:138
+#: ../pidgin/gtkdocklet.c:582 ../pidgin/gtkstatusbox.c:1051
+#: ../pidgin/plugins/themeedit-icon.c:64
+msgid "Available"
+msgstr "Dostępny"
+#: ../libpurple/protocols/jabber/jutil.c:714
+#: ../pidgin/plugins/themeedit-icon.c:66
msgid "Extended Away"
msgstr "Wrócę później"
@@ -4777,696 +7021,1006 @@ msgstr "Wrócę później"
#. *< version
#. * summary
#. * description
-msgid "XMPP Protocol Plugin"
-msgstr "Wtyczka protokołu XMPP"
-
-#. Translators: 'domain' is used here in the context of Internet domains, e.g. pidgin.im
+#: ../libpurple/protocols/jabber/libfacebook.c:185
+#: ../libpurple/protocols/jabber/libfacebook.c:187
+msgid "Facebook XMPP Protocol Plugin"
+msgstr "Wtyczka protokołu XMPP serwisu Facebook"
+
+#. Translators: 'domain' is used here in the context of Internet domains, e.g.
+#. pidgin.im
+#: ../libpurple/protocols/jabber/libfacebook.c:215
+#: ../libpurple/protocols/jabber/libgtalk.c:263
+#: ../libpurple/protocols/jabber/libxmpp.c:257
msgid "Domain"
msgstr "Domena"
+#: ../libpurple/protocols/jabber/libfacebook.c:234
+#: ../libpurple/protocols/jabber/libgtalk.c:280
+#: ../libpurple/protocols/jabber/libxmpp.c:274
msgid "Use old-style SSL"
msgstr "Użycie starego stylu SSL"
+#: ../libpurple/protocols/jabber/libfacebook.c:246
+#: ../libpurple/protocols/jabber/libgtalk.c:315
+#: ../libpurple/protocols/jabber/libxmpp.c:309
+msgid "BOSH URL"
+msgstr "Adres URL protokołu BOSH"
+
+#. *< type
+#. *< ui_requirement
+#. *< flags
+#. *< dependencies
+#. *< priority
+#. *< id
+#. *< name
+#. *< version
+#. * summary
+#. * description
+#: ../libpurple/protocols/jabber/libgtalk.c:168
+#: ../libpurple/protocols/jabber/libgtalk.c:170
+msgid "Google Talk Protocol Plugin"
+msgstr "Wtyczka protokołu Google Talk"
+
+#: ../libpurple/protocols/jabber/libgtalk.c:293
+#: ../libpurple/protocols/jabber/libxmpp.c:287
msgid "Allow plaintext auth over unencrypted streams"
-msgstr ""
-"Pozwolenie na upoważnianie w zwykłym tekście przez niezaszyfrowane strumienie"
+msgstr "Pozwolenie na upoważnianie w zwykłym tekście przez niezaszyfrowane strumienie"
+#: ../libpurple/protocols/jabber/libgtalk.c:298
+#: ../libpurple/protocols/jabber/libxmpp.c:292
+#: ../libpurple/protocols/simple/simple.c:2172
msgid "Connect port"
msgstr "Port połączenia"
-#. TODO: default to automatically try different ports. Make the user be
-#. * able to set the first port to try (like LastConnectedPort in Windows client).
#. Account options
+#: ../libpurple/protocols/jabber/libgtalk.c:302
+#: ../libpurple/protocols/jabber/libxmpp.c:296
+#: ../libpurple/protocols/silc/silc.c:2204
msgid "Connect server"
msgstr "Serwer połączenia"
+#: ../libpurple/protocols/jabber/libgtalk.c:307
+#: ../libpurple/protocols/jabber/libxmpp.c:301
msgid "File transfer proxies"
msgstr "Pośredniki przesyłania plików"
-msgid "BOSH URL"
-msgstr "Adres URL protokołu BOSH"
-
#. this should probably be part of global smiley theme settings later on,
#. shared with MSN
+#: ../libpurple/protocols/jabber/libgtalk.c:322
+#: ../libpurple/protocols/jabber/libxmpp.c:316
msgid "Show Custom Smileys"
msgstr "Wyświetlanie własnych emotikon"
+#. *< type
+#. *< ui_requirement
+#. *< flags
+#. *< dependencies
+#. *< priority
+#. *< id
+#. *< name
+#. *< version
+#. * summary
+#. * description
+#: ../libpurple/protocols/jabber/libxmpp.c:162
+#: ../libpurple/protocols/jabber/libxmpp.c:164
+msgid "XMPP Protocol Plugin"
+msgstr "Wtyczka protokołu XMPP"
+
+#: ../libpurple/protocols/jabber/message.c:104
#, c-format
msgid "%s has left the conversation."
msgstr "Użytkownik %s opuścił rozmowę."
+#: ../libpurple/protocols/jabber/message.c:176
#, c-format
msgid "Message from %s"
msgstr "Wiadomość od użytkownika %s"
+#: ../libpurple/protocols/jabber/message.c:242
#, c-format
msgid "%s has set the topic to: %s"
msgstr "Użytkownik %s ustawił temat na: %s"
+#: ../libpurple/protocols/jabber/message.c:244
#, c-format
msgid "The topic is: %s"
msgstr "Temat: %s"
+#: ../libpurple/protocols/jabber/message.c:293
#, c-format
msgid "Message delivery to %s failed: %s"
msgstr "Dostarczenie wiadomości do użytkownika %s się nie powiodło: %s"
+#: ../libpurple/protocols/jabber/message.c:296
msgid "XMPP Message Error"
msgstr "Błąd wiadomości XMPP"
+#: ../libpurple/protocols/jabber/message.c:612
#, c-format
msgid "(Code %s)"
msgstr "(Kod %s)"
+#: ../libpurple/protocols/jabber/message.c:945
msgid "A custom smiley in the message is too large to send."
msgstr "Własna emotikona w wiadomości jest za duża do wysłania."
+#: ../libpurple/protocols/jabber/parser.c:57
msgid "XMPP stream header missing"
msgstr "Brak nagłówka strumienia XMPP"
+#: ../libpurple/protocols/jabber/parser.c:78
msgid "XMPP Version Mismatch"
msgstr "Wersja XMPP się nie zgadza"
+#: ../libpurple/protocols/jabber/parser.c:103
msgid "XMPP stream missing ID"
msgstr "Brak identyfikatora strumienia XMPP"
+#: ../libpurple/protocols/jabber/parser.c:305
msgid "XML Parse error"
msgstr "Błąd podczas przetwarzania XML"
+#: ../libpurple/protocols/jabber/presence.c:556
#, c-format
msgid "Error joining chat %s"
msgstr "Błąd podczas dołączania do konferencji %s"
+#: ../libpurple/protocols/jabber/presence.c:559
#, c-format
msgid "Error in chat %s"
msgstr "Błąd konferencji %s"
+#: ../libpurple/protocols/jabber/presence.c:604
+#: ../libpurple/protocols/jabber/presence.c:605
msgid "Create New Room"
msgstr "Utworzenie nowego pokoju"
+#: ../libpurple/protocols/jabber/presence.c:606
msgid ""
"You are creating a new room. Would you like to configure it, or accept the "
"default settings?"
-msgstr ""
-"Tworzony jest nowy pokój. Skonfigurować go, czy zastosować domyślne "
-"ustawienia?"
+msgstr "Tworzony jest nowy pokój. Skonfigurować go, czy zastosować domyślne ustawienia?"
+#: ../libpurple/protocols/jabber/presence.c:612
msgid "_Configure Room"
msgstr "S_konfiguruj pokój"
+#: ../libpurple/protocols/jabber/presence.c:613
msgid "_Accept Defaults"
msgstr "Z_aakceptuj domyślne"
+#: ../libpurple/protocols/jabber/presence.c:738
msgid "No reason"
msgstr "Bez powodu"
+#: ../libpurple/protocols/jabber/presence.c:745
#, c-format
msgid "You have been kicked: (%s)"
msgstr "Użytkownik został wyrzucony: (%s)"
+#: ../libpurple/protocols/jabber/presence.c:752
#, c-format
msgid "Kicked (%s)"
msgstr "Wyrzucony (%s)"
+#: ../libpurple/protocols/jabber/presence.c:959
msgid "Unknown Error in presence"
msgstr "Nieznany błąd obecności"
+#: ../libpurple/protocols/jabber/si.c:1430
+#: ../libpurple/protocols/jabber/si.c:1472
#, c-format
msgid "Unable to send file to %s, user does not support file transfers"
-msgstr ""
-"Nie można wysłać pliku do użytkownika %s, nie obsługuje on przesyłania plików"
+msgstr "Nie można wysłać pliku do użytkownika %s, nie obsługuje on przesyłania plików"
#. not success
+#: ../libpurple/protocols/jabber/si.c:1431
+#: ../libpurple/protocols/jabber/si.c:1432
+#: ../libpurple/protocols/jabber/si.c:1473
+#: ../libpurple/protocols/jabber/si.c:1474
+#: ../libpurple/protocols/jabber/si.c:1544
+#: ../libpurple/protocols/jabber/si.c:1545
+#: ../libpurple/protocols/mxit/protocol.c:2225
msgid "File Send Failed"
msgstr "Przesyłanie plików się nie powiodło"
+#: ../libpurple/protocols/jabber/si.c:1537
#, c-format
msgid "Unable to send file to %s, invalid JID"
-msgstr ""
-"Nie można wysłać pliku do użytkownika %s, nieprawidłowy identyfikator Jabbera"
+msgstr "Nie można wysłać pliku do użytkownika %s, nieprawidłowy identyfikator Jabbera"
+#: ../libpurple/protocols/jabber/si.c:1539
#, c-format
msgid "Unable to send file to %s, user is not online"
msgstr "Nie można wysłać pliku do użytkownika %s, nie jest on w trybie online"
+#: ../libpurple/protocols/jabber/si.c:1541
#, c-format
msgid "Unable to send file to %s, not subscribed to user presence"
-msgstr ""
-"Nie można wysłać pliku do użytkownika %s, nie subskrybowano jego obecności"
+msgstr "Nie można wysłać pliku do użytkownika %s, nie subskrybowano jego obecności"
+#: ../libpurple/protocols/jabber/si.c:1556
#, c-format
-msgid "Please select the resource of %s to which you would like to send a file"
+msgid ""
+"Please select the resource of %s to which you would like to send a file"
msgstr "Proszę wybrać zasób użytkownika %s, do którego wysłać plik"
+#: ../libpurple/protocols/jabber/usermood.c:34
msgid "Afraid"
msgstr "Przestraszony"
+#: ../libpurple/protocols/jabber/usermood.c:35
msgid "Amazed"
msgstr "Zdumiony"
+#: ../libpurple/protocols/jabber/usermood.c:36
msgid "Amorous"
msgstr "Miłosny"
+#: ../libpurple/protocols/jabber/usermood.c:37
+#: ../libpurple/protocols/mxit/roster.c:138
+#: ../libpurple/protocols/mxit/roster.c:202
+#: ../libpurple/protocols/oscar/family_locate.c:412
+#: ../libpurple/protocols/silc/buddy.c:1520
+#: ../libpurple/protocols/silc/silc.c:995
+#: ../libpurple/protocols/silc/util.c:485
msgid "Angry"
msgstr "Rozgniewany"
+#: ../libpurple/protocols/jabber/usermood.c:38
msgid "Annoyed"
msgstr "Zirytowany"
+#: ../libpurple/protocols/jabber/usermood.c:39
+#: ../libpurple/protocols/silc/buddy.c:1536
+#: ../libpurple/protocols/silc/silc.c:1011
+#: ../libpurple/protocols/silc/util.c:501
msgid "Anxious"
msgstr "Zaniepokojony"
+#: ../libpurple/protocols/jabber/usermood.c:40
msgid "Aroused"
msgstr "Rozbudzony"
+#: ../libpurple/protocols/jabber/usermood.c:41
+#: ../libpurple/protocols/silc/buddy.c:1524
+#: ../libpurple/protocols/silc/silc.c:999
+#: ../libpurple/protocols/silc/util.c:489
msgid "Ashamed"
msgstr "Zawstydzony"
+#: ../libpurple/protocols/jabber/usermood.c:42
+#: ../libpurple/protocols/mxit/roster.c:148
+#: ../libpurple/protocols/mxit/roster.c:222
+#: ../libpurple/protocols/silc/buddy.c:1532
+#: ../libpurple/protocols/silc/silc.c:1007
+#: ../libpurple/protocols/silc/util.c:497
msgid "Bored"
msgstr "Znudzony"
+#: ../libpurple/protocols/jabber/usermood.c:43
msgid "Brave"
msgstr "Dzielny"
+#: ../libpurple/protocols/jabber/usermood.c:44
msgid "Calm"
msgstr "Spokojny"
+#: ../libpurple/protocols/jabber/usermood.c:45
msgid "Cautious"
msgstr "Ostrożny"
+#: ../libpurple/protocols/jabber/usermood.c:46
+#: ../libpurple/protocols/mxit/roster.c:149
+#: ../libpurple/protocols/mxit/roster.c:224
msgid "Cold"
msgstr "Zimny"
+#: ../libpurple/protocols/jabber/usermood.c:47
msgid "Confident"
msgstr "Pewny siebie"
+#: ../libpurple/protocols/jabber/usermood.c:48
+#: ../libpurple/protocols/mxit/roster.c:150
+#: ../libpurple/protocols/mxit/roster.c:226
msgid "Confused"
msgstr "Zdezorientowany"
+#: ../libpurple/protocols/jabber/usermood.c:49
msgid "Contemplative"
msgstr "Kontemplacyjny"
+#: ../libpurple/protocols/jabber/usermood.c:50
msgid "Contented"
msgstr "Zadowolony"
+#: ../libpurple/protocols/jabber/usermood.c:51
msgid "Cranky"
msgstr "Marudny"
+#: ../libpurple/protocols/jabber/usermood.c:52
msgid "Crazy"
msgstr "Szalony"
+#: ../libpurple/protocols/jabber/usermood.c:53
msgid "Creative"
msgstr "Twórczy"
+#: ../libpurple/protocols/jabber/usermood.c:54
msgid "Curious"
msgstr "Ciekawy"
+#: ../libpurple/protocols/jabber/usermood.c:55
msgid "Dejected"
msgstr "Przygnębiony"
+#: ../libpurple/protocols/jabber/usermood.c:56
msgid "Depressed"
msgstr "Przybity"
+#: ../libpurple/protocols/jabber/usermood.c:57
msgid "Disappointed"
msgstr "Rozczarowany"
+#: ../libpurple/protocols/jabber/usermood.c:58
msgid "Disgusted"
msgstr "Zniesmaczony"
+#: ../libpurple/protocols/jabber/usermood.c:59
msgid "Dismayed"
msgstr "Skonsternowany"
+#: ../libpurple/protocols/jabber/usermood.c:60
msgid "Distracted"
msgstr "Rozkojarzony"
+#: ../libpurple/protocols/jabber/usermood.c:61
msgid "Embarrassed"
msgstr "Zawstydzony"
+#: ../libpurple/protocols/jabber/usermood.c:62
msgid "Envious"
msgstr "Zazdrosny"
+#: ../libpurple/protocols/jabber/usermood.c:63
+#: ../libpurple/protocols/mxit/roster.c:139
+#: ../libpurple/protocols/mxit/roster.c:204
+#: ../libpurple/protocols/silc/buddy.c:1534
+#: ../libpurple/protocols/silc/silc.c:1009
+#: ../libpurple/protocols/silc/util.c:499
msgid "Excited"
msgstr "Podekscytowany"
+#: ../libpurple/protocols/jabber/usermood.c:64
msgid "Flirtatious"
msgstr "Zalotny"
+#: ../libpurple/protocols/jabber/usermood.c:65
msgid "Frustrated"
msgstr "Sfrustrowany"
+#: ../libpurple/protocols/jabber/usermood.c:66
msgid "Grateful"
msgstr "Wdzięczny"
+#: ../libpurple/protocols/jabber/usermood.c:67
msgid "Grieving"
msgstr "Opłakujący"
+#: ../libpurple/protocols/jabber/usermood.c:68
+#: ../libpurple/protocols/mxit/roster.c:140
+#: ../libpurple/protocols/mxit/roster.c:206
msgid "Grumpy"
msgstr "Zrzędliwy"
+#: ../libpurple/protocols/jabber/usermood.c:69
msgid "Guilty"
msgstr "Żałuje"
+#: ../libpurple/protocols/jabber/usermood.c:70
+#: ../libpurple/protocols/mxit/roster.c:141
+#: ../libpurple/protocols/mxit/roster.c:208
+#: ../libpurple/protocols/silc/buddy.c:1516
+#: ../libpurple/protocols/silc/silc.c:991
+#: ../libpurple/protocols/silc/util.c:481
msgid "Happy"
msgstr "Wesoły"
+#: ../libpurple/protocols/jabber/usermood.c:71
msgid "Hopeful"
msgstr "Optymistyczny"
+#: ../libpurple/protocols/jabber/usermood.c:72
+#: ../libpurple/protocols/mxit/roster.c:145
+#: ../libpurple/protocols/mxit/roster.c:216
msgid "Hot"
msgstr "Sexy"
+#: ../libpurple/protocols/jabber/usermood.c:73
msgid "Humbled"
msgstr "Skromny"
+#: ../libpurple/protocols/jabber/usermood.c:74
msgid "Humiliated"
msgstr "Upokorzony"
+#: ../libpurple/protocols/jabber/usermood.c:75
+#: ../libpurple/protocols/mxit/roster.c:151
+#: ../libpurple/protocols/mxit/roster.c:228
msgid "Hungry"
msgstr "Głodny"
+#: ../libpurple/protocols/jabber/usermood.c:76
msgid "Hurt"
msgstr "Zraniony"
+#: ../libpurple/protocols/jabber/usermood.c:77
msgid "Impressed"
msgstr "Zaimponowany"
+#: ../libpurple/protocols/jabber/usermood.c:78
msgid "In awe"
msgstr "Zachwycony"
+#: ../libpurple/protocols/jabber/usermood.c:79
+#: ../libpurple/protocols/mxit/roster.c:142
+#: ../libpurple/protocols/oscar/family_locate.c:423
+#: ../libpurple/protocols/silc/silc.c:1003
msgid "In love"
msgstr "Zakochany"
+#: ../libpurple/protocols/jabber/usermood.c:80
msgid "Indignant"
msgstr "Obrażony"
+#: ../libpurple/protocols/jabber/usermood.c:81
msgid "Interested"
msgstr "Zainteresowany"
+#: ../libpurple/protocols/jabber/usermood.c:82
msgid "Intoxicated"
msgstr "Pijany"
+#: ../libpurple/protocols/jabber/usermood.c:83
+#: ../libpurple/protocols/mxit/roster.c:143
+#: ../libpurple/protocols/mxit/roster.c:212
+#: ../libpurple/protocols/silc/buddy.c:1526
+#: ../libpurple/protocols/silc/silc.c:1001
+#: ../libpurple/protocols/silc/util.c:491
msgid "Invincible"
msgstr "Niezwyciężony"
+#: ../libpurple/protocols/jabber/usermood.c:84
+#: ../libpurple/protocols/silc/buddy.c:1522
+#: ../libpurple/protocols/silc/silc.c:997
+#: ../libpurple/protocols/silc/util.c:487
msgid "Jealous"
msgstr "Zazdrosny"
+#: ../libpurple/protocols/jabber/usermood.c:85
msgid "Lonely"
msgstr "Samotny"
+#: ../libpurple/protocols/jabber/usermood.c:86
msgid "Lost"
msgstr "Zagubiony"
+#: ../libpurple/protocols/jabber/usermood.c:87
msgid "Lucky"
msgstr "Ma szczęście"
+#: ../libpurple/protocols/jabber/usermood.c:88
msgid "Mean"
msgstr "Podły"
+#: ../libpurple/protocols/jabber/usermood.c:89
msgid "Moody"
msgstr "Ponury"
+#: ../libpurple/protocols/jabber/usermood.c:90
msgid "Nervous"
msgstr "Nerwowy"
+#: ../libpurple/protocols/jabber/usermood.c:91
msgid "Neutral"
msgstr "Neutralny"
+#: ../libpurple/protocols/jabber/usermood.c:92
msgid "Offended"
msgstr "Urażony"
+#: ../libpurple/protocols/jabber/usermood.c:93
msgid "Outraged"
msgstr "Oburzony"
+#: ../libpurple/protocols/jabber/usermood.c:94
msgid "Playful"
msgstr "Figlarny"
+#: ../libpurple/protocols/jabber/usermood.c:95
msgid "Proud"
msgstr "Dumny"
+#: ../libpurple/protocols/jabber/usermood.c:96
msgid "Relaxed"
msgstr "Rozluźniony"
+#: ../libpurple/protocols/jabber/usermood.c:97
msgid "Relieved"
msgstr "Czuje ulgę"
+#: ../libpurple/protocols/jabber/usermood.c:98
msgid "Remorseful"
msgstr "Skruszony"
+#: ../libpurple/protocols/jabber/usermood.c:99
msgid "Restless"
msgstr "Niespokojny"
+#: ../libpurple/protocols/jabber/usermood.c:100
+#: ../libpurple/protocols/mxit/roster.c:144
+#: ../libpurple/protocols/mxit/roster.c:214
+#: ../libpurple/protocols/silc/buddy.c:1518
+#: ../libpurple/protocols/silc/silc.c:993
+#: ../libpurple/protocols/silc/util.c:483
msgid "Sad"
msgstr "Smutny"
+#: ../libpurple/protocols/jabber/usermood.c:101
msgid "Sarcastic"
msgstr "Sarkastyczny"
+#: ../libpurple/protocols/jabber/usermood.c:102
msgid "Satisfied"
msgstr "Zadowolony"
+#: ../libpurple/protocols/jabber/usermood.c:103
msgid "Serious"
msgstr "Poważny"
+#: ../libpurple/protocols/jabber/usermood.c:104
msgid "Shocked"
msgstr "Zszokowany"
+#: ../libpurple/protocols/jabber/usermood.c:105
msgid "Shy"
msgstr "Nieśmiały"
+#: ../libpurple/protocols/jabber/usermood.c:106
+#: ../libpurple/protocols/mxit/roster.c:146
+#: ../libpurple/protocols/mxit/roster.c:218
+#: ../libpurple/protocols/oscar/family_locate.c:415
msgid "Sick"
msgstr "Chory"
#. Sleepy / Tired
+#: ../libpurple/protocols/jabber/usermood.c:107
+#: ../libpurple/protocols/mxit/roster.c:147
+#: ../libpurple/protocols/mxit/roster.c:220
+#: ../libpurple/protocols/oscar/family_locate.c:425
+#: ../libpurple/protocols/silc/buddy.c:1530
+#: ../libpurple/protocols/silc/silc.c:1005
+#: ../libpurple/protocols/silc/util.c:495
msgid "Sleepy"
msgstr "Śenny"
+#: ../libpurple/protocols/jabber/usermood.c:108
msgid "Spontaneous"
msgstr "Spontaniczny"
+#: ../libpurple/protocols/jabber/usermood.c:109
+#: ../libpurple/protocols/mxit/roster.c:152
+#: ../libpurple/protocols/mxit/roster.c:230
msgid "Stressed"
msgstr "Zestresowany"
+#: ../libpurple/protocols/jabber/usermood.c:110
msgid "Strong"
msgstr "Silny"
+#: ../libpurple/protocols/jabber/usermood.c:111
msgid "Surprised"
msgstr "Zaskoczony"
+#: ../libpurple/protocols/jabber/usermood.c:112
msgid "Thankful"
msgstr "Wdzięczny"
+#: ../libpurple/protocols/jabber/usermood.c:113
msgid "Thirsty"
msgstr "Spragniony"
+#: ../libpurple/protocols/jabber/usermood.c:114
msgid "Tired"
msgstr "Zmęczony"
+#: ../libpurple/protocols/jabber/usermood.c:115
msgid "Undefined"
msgstr "Nieokreślony"
+#: ../libpurple/protocols/jabber/usermood.c:116
msgid "Weak"
msgstr "Słaby"
+#: ../libpurple/protocols/jabber/usermood.c:117
msgid "Worried"
msgstr "Zmartwiony"
+#: ../libpurple/protocols/jabber/usernick.c:81
msgid "Set User Nickname"
msgstr "Ustaw pseudonim użytkownika"
+#: ../libpurple/protocols/jabber/usernick.c:81
msgid "Please specify a new nickname for you."
msgstr "Proszę podać nowy pseudonim."
+#: ../libpurple/protocols/jabber/usernick.c:82
msgid ""
"This information is visible to all contacts on your contact list, so choose "
"something appropriate."
-msgstr ""
-"Ta informacja jest widoczna dla wszystkich kontaktów na liście, więc należy "
-"wybrać coś odpowiedniego."
+msgstr "Ta informacja jest widoczna dla wszystkich kontaktów na liście, więc należy wybrać coś odpowiedniego."
+#: ../libpurple/protocols/jabber/usernick.c:83
+#: ../libpurple/protocols/mxit/actions.c:310
+#: ../libpurple/protocols/mxit/actions.c:404
msgid "Set"
msgstr "Ustaw"
+#: ../libpurple/protocols/jabber/usernick.c:103
msgid "Set Nickname..."
msgstr "Ustaw pseudonim..."
+#: ../libpurple/protocols/jabber/xdata.c:384
msgid "Actions"
msgstr "Czynności"
+#: ../libpurple/protocols/jabber/xdata.c:386
msgid "Select an action"
msgstr "Wybór czynności"
#. only notify the user about problems adding to the friends list
#. * maybe we should do something else for other lists, but it probably
#. * won't cause too many problems if we just ignore it
+#: ../libpurple/protocols/msn/contact.c:1070
+#: ../libpurple/protocols/msn/contact.c:1169
+#: ../libpurple/protocols/msn/userlist.c:526
#, c-format
msgid "Unable to add \"%s\"."
msgstr "Nie można dodać \"%s\"."
+#: ../libpurple/protocols/msn/contact.c:1072
+#: ../libpurple/protocols/msn/contact.c:1170
msgid "Buddy Add error"
msgstr "Błąd podczas dodawania znajomego"
+#: ../libpurple/protocols/msn/contact.c:1073
+#: ../libpurple/protocols/msn/contact.c:1171
msgid "The username specified does not exist."
msgstr "Podana nazwa użytkownika nie istnieje."
+#: ../libpurple/protocols/msn/error.c:50
msgid "Unable to parse message"
msgstr "Nie można przetworzyć wiadomości"
+#: ../libpurple/protocols/msn/error.c:54
msgid "Syntax Error (probably a client bug)"
msgstr "Błąd składni (prawdopodobnie błąd klienta)"
+#: ../libpurple/protocols/msn/error.c:58 ../libpurple/request.c:2023
msgid "Invalid email address"
msgstr "Nieprawidłowy adres e-mail"
+#: ../libpurple/protocols/msn/error.c:61
msgid "User does not exist"
msgstr "Użytkownik nie istnieje"
+#: ../libpurple/protocols/msn/error.c:64
msgid "Fully qualified domain name missing"
msgstr "Brak pełnej nazwy domeny (FQDN)"
+#: ../libpurple/protocols/msn/error.c:67
msgid "Already logged in"
msgstr "Użytkownik jest już zalogowany"
+#: ../libpurple/protocols/msn/error.c:70 ../pidgin/gtkdialogs.c:911
msgid "Invalid username"
msgstr "Nieprawidłowa nazwa użytkownika"
+#: ../libpurple/protocols/msn/error.c:73
msgid "Invalid friendly name"
msgstr "Nieprawidłowy pseudonim"
+#: ../libpurple/protocols/msn/error.c:76
msgid "List full"
msgstr "Lista jest pełna"
+#: ../libpurple/protocols/msn/error.c:79
msgid "Already there"
msgstr "Już tu jest"
+#: ../libpurple/protocols/msn/error.c:83
msgid "Not on list"
msgstr "Nie ma na liście"
+#: ../libpurple/protocols/msn/error.c:86
+#: ../libpurple/protocols/zephyr/zephyr.c:763
msgid "User is offline"
msgstr "Użytkownik jest w trybie offline"
+#: ../libpurple/protocols/msn/error.c:89
msgid "Already in the mode"
msgstr "Jest już w tym trybie"
+#: ../libpurple/protocols/msn/error.c:93
msgid "Already in opposite list"
msgstr "Jest już na przeciwnej liście"
+#: ../libpurple/protocols/msn/error.c:97
msgid "Too many groups"
msgstr "Za dużo grup"
+#: ../libpurple/protocols/msn/error.c:100
msgid "Invalid group"
msgstr "Nieprawidłowa grupa"
+#: ../libpurple/protocols/msn/error.c:103
msgid "User not in group"
msgstr "Użytkownik nie jest w grupie"
+#: ../libpurple/protocols/msn/error.c:106
msgid "Group name too long"
msgstr "Nazwa grupy jest za długa"
+#: ../libpurple/protocols/msn/error.c:109
msgid "Cannot remove group zero"
msgstr "Nie można usunąć zera grup"
+#: ../libpurple/protocols/msn/error.c:113
msgid "Tried to add a user to a group that doesn't exist"
msgstr "Próbowano dodać użytkownika do grupy, która nie istnieje"
+#: ../libpurple/protocols/msn/error.c:116
msgid "Switchboard failed"
msgstr "Centrala się nie powiodła"
+#: ../libpurple/protocols/msn/error.c:120
msgid "Notify transfer failed"
msgstr "Powiadomienie o przesyłaniu się nie powiodło"
+#: ../libpurple/protocols/msn/error.c:125
msgid "Required fields missing"
msgstr "Brak wymaganych pól"
+#: ../libpurple/protocols/msn/error.c:129
msgid "Too many hits to a FND"
msgstr "Za dużo odwiedzin FND"
+#: ../libpurple/protocols/msn/error.c:133
+#: ../libpurple/protocols/oscar/family_icbm.c:55
+#: ../libpurple/protocols/oscar/util.c:39
msgid "Not logged in"
msgstr "Niezalogowany"
-msgid "Service temporarily unavailable"
-msgstr "Usługa jest tymczasowo niedostępna"
-
+#: ../libpurple/protocols/msn/error.c:140
msgid "Database server error"
msgstr "Błąd serwera bazy danych"
+#: ../libpurple/protocols/msn/error.c:144
msgid "Command disabled"
msgstr "Polecenie jest wyłączone"
+#: ../libpurple/protocols/msn/error.c:148
msgid "File operation error"
msgstr "Błąd działania na pliku"
+#: ../libpurple/protocols/msn/error.c:152
msgid "Memory allocation error"
msgstr "Błąd przydzielenia pamięci"
+#: ../libpurple/protocols/msn/error.c:156
msgid "Wrong CHL value sent to server"
msgstr "Wysłano błędną wartość CHL do serwera"
+#: ../libpurple/protocols/msn/error.c:161
msgid "Server busy"
msgstr "Serwer jest zajęty"
+#: ../libpurple/protocols/msn/error.c:164
+#: ../libpurple/protocols/msn/error.c:178
+#: ../libpurple/protocols/msn/error.c:236
msgid "Server unavailable"
msgstr "Serwer jest niedostępny"
+#: ../libpurple/protocols/msn/error.c:167
msgid "Peer notification server down"
msgstr "Serwer powiadamiania użytkowników nie działa"
+#: ../libpurple/protocols/msn/error.c:171
msgid "Database connect error"
msgstr "Błąd połączenia z bazą danych"
+#: ../libpurple/protocols/msn/error.c:175
msgid "Server is going down (abandon ship)"
msgstr "Serwer zostanie wyłączony (opuścić okręt)"
+#: ../libpurple/protocols/msn/error.c:182
msgid "Error creating connection"
msgstr "Błąd podczas tworzenia połączenia"
+#: ../libpurple/protocols/msn/error.c:186
msgid "CVR parameters are either unknown or not allowed"
msgstr "Parametry CVR są nieznane lub niedozwolone"
+#: ../libpurple/protocols/msn/error.c:190
msgid "Unable to write"
msgstr "Nie można zapisać"
+#: ../libpurple/protocols/msn/error.c:193
msgid "Session overload"
msgstr "Sesja jest przeciążona"
+#: ../libpurple/protocols/msn/error.c:197
msgid "User is too active"
msgstr "Użytkownik jest za bardzo aktywny"
+#: ../libpurple/protocols/msn/error.c:200
msgid "Too many sessions"
msgstr "Za dużo sesji"
+#: ../libpurple/protocols/msn/error.c:203
msgid "Passport not verified"
msgstr "Passport nie został sprawdzony"
+#: ../libpurple/protocols/msn/error.c:206
msgid "Bad friend file"
msgstr "Błędny plik przyjaciela"
+#: ../libpurple/protocols/msn/error.c:210
msgid "Not expected"
msgstr "Nieoczekiwano"
+#: ../libpurple/protocols/msn/error.c:215
msgid "Friendly name is changing too rapidly"
msgstr "Pseudonim jest za często zmieniany"
+#: ../libpurple/protocols/msn/error.c:224
msgid "Server too busy"
msgstr "Serwer jest zbyt zajęty"
-msgid "Authentication failed"
-msgstr "Uwierzytelnienie się nie powiodło"
-
+#: ../libpurple/protocols/msn/error.c:231
msgid "Not allowed when offline"
msgstr "Niedozwolone w trybie offline"
+#: ../libpurple/protocols/msn/error.c:239
msgid "Not accepting new users"
msgstr "Bez akceptowania nowych użytkowników"
+#: ../libpurple/protocols/msn/error.c:242
msgid "Kids Passport without parental consent"
msgstr "Passport dla dziecka bez zgody rodziców"
+#: ../libpurple/protocols/msn/error.c:245
msgid "Passport account not yet verified"
msgstr "Konto Passport nie zostało jeszcze sprawdzone"
+#: ../libpurple/protocols/msn/error.c:248
msgid "Passport account suspended"
msgstr "Konto Passport zostało zawieszone"
+#: ../libpurple/protocols/msn/error.c:251
msgid "Bad ticket"
msgstr "Błędny bilet"
+#: ../libpurple/protocols/msn/error.c:257
#, c-format
msgid "Unknown Error Code %d"
msgstr "Nieznany kod błędu %d"
+#: ../libpurple/protocols/msn/error.c:272
#, c-format
msgid "MSN Error: %s\n"
msgstr "Błąd MSN: %s\n"
+#: ../libpurple/protocols/msn/error.c:359
#, c-format
msgid "Buddy list synchronization issue in %s (%s)"
msgstr "Problem synchronizacji listy znajomych w %s (%s)"
+#: ../libpurple/protocols/msn/error.c:365
#, c-format
msgid ""
"%s on the local list is inside the group \"%s\" but not on the server list. "
"Do you want this buddy to be added?"
-msgstr ""
-"Użytkownik %s na lokalnej liście jest w grupie \"%s\", ale nie ma go na "
-"serwerze. Dodać tego znajomego?"
+msgstr "Użytkownik %s na lokalnej liście jest w grupie \"%s\", ale nie ma go na serwerze. Dodać tego znajomego?"
+#: ../libpurple/protocols/msn/error.c:373
#, c-format
msgid ""
"%s is on the local list but not on the server list. Do you want this buddy "
"to be added?"
-msgstr ""
-"Użytkownik %s znajduje się na lokalnej liście, ale nie ma go na serwerze. "
-"Dodać tego użytkownika?"
+msgstr "Użytkownik %s znajduje się na lokalnej liście, ale nie ma go na serwerze. Dodać tego użytkownika?"
+#: ../libpurple/protocols/msn/group.h:36
msgid "Other Contacts"
msgstr "Inne kontakty"
+#: ../libpurple/protocols/msn/group.h:39
msgid "Non-IM Contacts"
msgstr "Kontakty spoza komunikatora"
+#: ../libpurple/protocols/msn/msg.c:822
#, c-format
msgid "%s sent a wink. <a href='msn-wink://%s'>Click here to play it</a>"
-msgstr ""
-"Użytkownik %s wysłał mrugnięcie. <a href='msn-wink://%s'>Naciśnięcie tutaj "
-"odtworzy je</a>"
+msgstr "Użytkownik %s wysłał mrugnięcie. <a href='msn-wink://%s'>Naciśnięcie tutaj odtworzy je</a>"
+#: ../libpurple/protocols/msn/msg.c:828
#, c-format
msgid "%s sent a wink, but it could not be saved"
msgstr "Użytkownik %s wysłał mrugnięcie, ale nie można go zapisać"
+#: ../libpurple/protocols/msn/msg.c:848
#, c-format
msgid "%s sent a voice clip. <a href='audio://%s'>Click here to play it</a>"
-msgstr ""
-"Użytkownik %s wysłał nagranie dźwiękowe. <a href='audio://%s'>Naciśnięcie "
-"tutaj odtworzy je</a>"
+msgstr "Użytkownik %s wysłał nagranie dźwiękowe. <a href='audio://%s'>Naciśnięcie tutaj odtworzy je</a>"
+#: ../libpurple/protocols/msn/msg.c:854
#, c-format
msgid "%s sent a voice clip, but it could not be saved"
msgstr "Użytkownik %s wysłał nagranie dźwiękowe, ale nie można go zapisać"
+#: ../libpurple/protocols/msn/msg.c:1161
#, c-format
msgid "%s sent you a voice chat invite, which is not yet supported."
-msgstr ""
-"Użytkownik %s wysłał zaproszenie do konferencji głosowej, która nie jest "
-"jeszcze obsługiwana."
+msgstr "Użytkownik %s wysłał zaproszenie do konferencji głosowej, która nie jest jeszcze obsługiwana."
+#: ../libpurple/protocols/msn/msn.c:140
msgid "Nudge"
msgstr "Szturchnij"
+#: ../libpurple/protocols/msn/msn.c:141
#, c-format
msgid "%s has nudged you!"
msgstr "%s szturchnął użytkownika."
+#: ../libpurple/protocols/msn/msn.c:141
#, c-format
msgid "Nudging %s..."
msgstr "Szturchanie użytkownika %s..."
+#: ../libpurple/protocols/msn/msn.c:154
msgid "Email Address..."
msgstr "Adres e-mail..."
+#: ../libpurple/protocols/msn/msn.c:186 ../libpurple/protocols/msn/msn.c:265
msgid "Your new MSN friendly name is too long."
msgstr "Nowy pseudonim MSN jest za długi."
+#: ../libpurple/protocols/msn/msn.c:452
#, c-format
msgid "Set friendly name for %s."
msgstr "Ustawienie pseudonimu dla %s."
+#: ../libpurple/protocols/msn/msn.c:454
msgid "Set Friendly Name"
msgstr "Ustaw pseudonim"
+#: ../libpurple/protocols/msn/msn.c:455
msgid "This is the name that other MSN buddies will see you as."
msgstr "Ta nazwa będzie widoczna dla innych znajomych MSN."
+#: ../libpurple/protocols/msn/msn.c:531
msgid "This Location"
msgstr "To położenie"
+#: ../libpurple/protocols/msn/msn.c:533
msgid "This is the name that identifies this location"
msgstr "Ta nazwa identyfikuje to położenie."
+#: ../libpurple/protocols/msn/msn.c:542
msgid "Other Locations"
msgstr "Inne położenia"
+#: ../libpurple/protocols/msn/msn.c:557
msgid "You can sign out from other locations here"
msgstr "Tutaj można wylogować się z innych położeń"
@@ -5476,298 +8030,416 @@ msgstr "Tutaj można wylogować się z innych położeń"
#. a separate purple_request_field_label_new_without_colon function,
#. or by never automatically adding the colon and requiring that
#. callers add the colon themselves.
+#: ../libpurple/protocols/msn/msn.c:572
msgid "You are not signed in from any other locations."
msgstr "Nie zalogowano z innych położeń."
+#: ../libpurple/protocols/msn/msn.c:636
msgid "Allow multiple logins?"
msgstr "Zezwolić na wiele logowań?"
+#: ../libpurple/protocols/msn/msn.c:637
msgid ""
"Do you want to allow or disallow connecting from multiple locations "
"simultaneously?"
msgstr "Zezwolić czy zabronić połączenia z wielu położeń jednocześnie?"
+#: ../libpurple/protocols/msn/msn.c:642 ../libpurple/protocols/msn/msn.c:712
msgid "Allow"
msgstr "Zezwól"
+#: ../libpurple/protocols/msn/msn.c:643 ../libpurple/protocols/msn/msn.c:713
msgid "Disallow"
msgstr "Zabroń"
+#: ../libpurple/protocols/msn/msn.c:656
msgid "Set your home phone number."
msgstr "Ustawienie numeru telefonu domowego."
+#: ../libpurple/protocols/msn/msn.c:673
msgid "Set your work phone number."
msgstr "Ustawienie numeru telefonu służbowego."
+#: ../libpurple/protocols/msn/msn.c:690
msgid "Set your mobile phone number."
msgstr "Ustawienie numeru telefonu komórkowego."
+#: ../libpurple/protocols/msn/msn.c:705
msgid "Allow MSN Mobile pages?"
msgstr "Zezwolić na strony MSN Mobile?"
+#: ../libpurple/protocols/msn/msn.c:706
msgid ""
"Do you want to allow or disallow people on your buddy list to send you MSN "
"Mobile pages to your cell phone or other mobile device?"
-msgstr ""
-"Zezwolić czy zabronić osobom z listy znajomych na wysyłanie stron MSN Mobile "
-"do telefonu komórkowego lub innego przenośnego urządzenia?"
+msgstr "Zezwolić czy zabronić osobom z listy znajomych na wysyłanie stron MSN Mobile do telefonu komórkowego lub innego przenośnego urządzenia?"
+#: ../libpurple/protocols/msn/msn.c:728
#, c-format
msgid "Blocked Text for %s"
msgstr "Zablokowany tekst dla %s"
+#: ../libpurple/protocols/msn/msn.c:730
msgid "No text is blocked for this account."
msgstr "Żaden tekst nie jest zablokowany dla tego konta."
+#: ../libpurple/protocols/msn/msn.c:733
#, c-format
msgid ""
"MSN servers are currently blocking the following regular expressions:<br/>%s"
msgstr "Serwery MSN obecnie blokują następujące wyrażenia regularne:<br/>%s"
+#: ../libpurple/protocols/msn/msn.c:753
msgid "This account does not have email enabled."
msgstr "To konto nie ma włączonego adresu e-mail."
+#: ../libpurple/protocols/msn/msn.c:796
msgid "Send a mobile message."
msgstr "Wysłanie wiadomości SMS."
+#: ../libpurple/protocols/msn/msn.c:798
msgid "Page"
msgstr "Strona"
+#: ../libpurple/protocols/msn/msn.c:1055
msgid "Playing a game"
msgstr "Gra"
+#: ../libpurple/protocols/msn/msn.c:1058
+#: ../libpurple/protocols/oscar/family_locate.c:443
msgid "Working"
msgstr "Pracuje"
+#: ../libpurple/protocols/msn/msn.c:1128
msgid "Has you"
msgstr "Posiada użytkownika na swojej liście znajomych"
+#: ../libpurple/protocols/msn/msn.c:1136
+#: ../libpurple/protocols/yahoo/libymsg.c:4128
+#: ../libpurple/protocols/yahoo/yahoo_aliases.c:514
msgid "Home Phone Number"
msgstr "Numer telefonu domowego"
+#: ../libpurple/protocols/msn/msn.c:1141
+#: ../libpurple/protocols/yahoo/libymsg.c:4130
+#: ../libpurple/protocols/yahoo/yahoo_aliases.c:515
msgid "Work Phone Number"
msgstr "Numer telefonu służbowego"
+#: ../libpurple/protocols/msn/msn.c:1146
+#: ../libpurple/protocols/yahoo/libymsg.c:4132
+#: ../libpurple/protocols/yahoo/yahoo_aliases.c:516
msgid "Mobile Phone Number"
msgstr "Numer telefonu komórkowego"
+#: ../libpurple/protocols/msn/msn.c:1170 ../libpurple/protocols/msn/state.c:39
+#: ../libpurple/protocols/yahoo/libymsg.c:3942
+#: ../libpurple/protocols/yahoo/libymsg.c:4934
msgid "Be Right Back"
msgstr "Zaraz wracam"
+#: ../libpurple/protocols/msn/msn.c:1176 ../libpurple/protocols/msn/state.c:37
+#: ../libpurple/protocols/novell/novell.c:2878
+#: ../libpurple/protocols/novell/novell.c:3013
+#: ../libpurple/protocols/oscar/family_locate.c:406
+#: ../libpurple/protocols/silc/buddy.c:1503
+#: ../libpurple/protocols/silc/silc.c:59
+#: ../libpurple/protocols/yahoo/libymsg.c:3944
+#: ../libpurple/protocols/yahoo/libymsg.c:4938
+#: ../pidgin/plugins/themeedit-icon.c:67
msgid "Busy"
msgstr "Zajęty"
+#: ../libpurple/protocols/msn/msn.c:1181
+#: ../libpurple/protocols/yahoo/libymsg.c:3952
+#: ../libpurple/protocols/yahoo/libymsg.c:4952
msgid "On the Phone"
msgstr "Przy telefonie"
+#: ../libpurple/protocols/msn/msn.c:1186
+#: ../libpurple/protocols/yahoo/libymsg.c:3956
+#: ../libpurple/protocols/yahoo/libymsg.c:4958
msgid "Out to Lunch"
msgstr "Na obiedzie"
+#: ../libpurple/protocols/msn/msn.c:1208
msgid "Game Title"
msgstr "Nazwa gry"
+#: ../libpurple/protocols/msn/msn.c:1209
msgid "Office Title"
msgstr "Nazwa biura"
+#: ../libpurple/protocols/msn/msn.c:1227
msgid "Set Friendly Name..."
msgstr "Ustaw pseudonim..."
+#: ../libpurple/protocols/msn/msn.c:1234
msgid "View Locations..."
msgstr "Wyświetl położenia..."
+#: ../libpurple/protocols/msn/msn.c:1240
msgid "Set Home Phone Number..."
msgstr "Ustaw domowy numer telefonu..."
+#: ../libpurple/protocols/msn/msn.c:1244
msgid "Set Work Phone Number..."
msgstr "Ustaw numer telefonu służbowego..."
+#: ../libpurple/protocols/msn/msn.c:1248
msgid "Set Mobile Phone Number..."
msgstr "Ustaw numer telefonu komórkowego..."
+#: ../libpurple/protocols/msn/msn.c:1254
msgid "Enable/Disable Mobile Devices..."
msgstr "Włącz/wyłącz urządzenia przenośne..."
+#: ../libpurple/protocols/msn/msn.c:1259
msgid "Allow/Disallow Multiple Logins..."
msgstr "Zezwól/zabroń na wiele logowań..."
+#: ../libpurple/protocols/msn/msn.c:1263
msgid "Allow/Disallow Mobile Pages..."
msgstr "Zezwól/zabroń strony Mobile..."
+#: ../libpurple/protocols/msn/msn.c:1270
msgid "View Blocked Text..."
msgstr "Wyświetl zablokowany tekst..."
+#: ../libpurple/protocols/msn/msn.c:1276
msgid "Open Hotmail Inbox"
msgstr "Otwórz skrzynkę Hotmail"
+#: ../libpurple/protocols/msn/msn.c:1299
msgid "Send to Mobile"
msgstr "Wyślij do urządzenia przenośnego"
+#: ../libpurple/protocols/msn/msn.c:1347
msgid "SSL support is needed for MSN. Please install a supported SSL library."
-msgstr ""
-"MSN wymaga obsługi SSL. Proszę zainstalować obsługiwaną bibliotekę SSL."
+msgstr "MSN wymaga obsługi SSL. Proszę zainstalować obsługiwaną bibliotekę SSL."
+#: ../libpurple/protocols/msn/msn.c:1794
#, c-format
msgid ""
"Unable to add the buddy %s because the username is invalid. Usernames must "
"be valid email addresses."
-msgstr ""
-"Nie można dodać znajomego %s, ponieważ nazwa użytkownika jest nieprawidłowa. "
-"Nazwy użytkowników muszą być prawidłowymi adresami e-mail."
+msgstr "Nie można dodać znajomego %s, ponieważ nazwa użytkownika jest nieprawidłowa. Nazwy użytkowników muszą być prawidłowymi adresami e-mail."
+#: ../libpurple/protocols/msn/msn.c:1796
+#: ../libpurple/protocols/oscar/oscar.c:3519
+#: ../libpurple/protocols/oscar/oscar.c:3986
+#: ../libpurple/protocols/oscar/oscar.c:4001
msgid "Unable to Add"
msgstr "Nie można dodać"
+#: ../libpurple/protocols/msn/msn.c:2332 ../libpurple/protocols/msn/msn.c:2681
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:810
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1196
msgid "Error retrieving profile"
msgstr "Błąd podczas pobierania profilu"
+#: ../libpurple/protocols/msn/msn.c:2400
msgid "General"
msgstr "Ogólne"
-msgid "Age"
-msgstr "Wiek"
-
+#: ../libpurple/protocols/msn/msn.c:2409
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1092
msgid "Occupation"
msgstr "Zawód"
+#: ../libpurple/protocols/msn/msn.c:2410
+#: ../libpurple/protocols/novell/novell.c:1509
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1072
+#: ../libpurple/protocols/zephyr/zephyr.c:818
+#: ../libpurple/protocols/zephyr/zephyr.c:1202
msgid "Location"
msgstr "Położenie"
+#: ../libpurple/protocols/msn/msn.c:2415 ../libpurple/protocols/msn/msn.c:2610
+#: ../libpurple/protocols/msn/msn.c:2616 ../libpurple/protocols/msn/msn.c:2623
msgid "Hobbies and Interests"
msgstr "Zainteresowania i hobby"
+#: ../libpurple/protocols/msn/msn.c:2421 ../libpurple/protocols/msn/msn.c:2544
+#: ../libpurple/protocols/msn/msn.c:2550 ../libpurple/protocols/msn/msn.c:2557
+#: ../libpurple/protocols/msn/msn.c:2565 ../libpurple/protocols/msn/msn.c:2572
msgid "A Little About Me"
msgstr "Coś o mnie"
+#: ../libpurple/protocols/msn/msn.c:2438
msgid "Social"
msgstr "Społeczne"
+#: ../libpurple/protocols/msn/msn.c:2440
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1082
msgid "Marital Status"
msgstr "Stan cywilny"
+#: ../libpurple/protocols/msn/msn.c:2441
msgid "Interests"
msgstr "Zainteresowania"
+#: ../libpurple/protocols/msn/msn.c:2442
msgid "Pets"
msgstr "Zwierzęta"
+#: ../libpurple/protocols/msn/msn.c:2443
msgid "Hometown"
msgstr "Miasto rodzinne"
+#: ../libpurple/protocols/msn/msn.c:2444
msgid "Places Lived"
msgstr "Miejsca zamieszkania"
+#: ../libpurple/protocols/msn/msn.c:2445
msgid "Fashion"
msgstr "Moda"
+#: ../libpurple/protocols/msn/msn.c:2446
msgid "Humor"
msgstr "Humor"
+#: ../libpurple/protocols/msn/msn.c:2447
msgid "Music"
msgstr "Muzyka"
+#: ../libpurple/protocols/msn/msn.c:2448 ../libpurple/protocols/msn/msn.c:2632
+#: ../libpurple/protocols/msn/msn.c:2638
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1134
msgid "Favorite Quote"
msgstr "Ulubiony cytat"
+#: ../libpurple/protocols/msn/msn.c:2465
msgid "Contact Info"
msgstr "Informacje o kontakcie"
+#: ../libpurple/protocols/msn/msn.c:2466
msgid "Personal"
msgstr "Osobiste"
+#: ../libpurple/protocols/msn/msn.c:2469
msgid "Significant Other"
msgstr "Druga połówka"
+#: ../libpurple/protocols/msn/msn.c:2470
msgid "Home Phone"
msgstr "Telefon domowy"
+#: ../libpurple/protocols/msn/msn.c:2471
msgid "Home Phone 2"
msgstr "Drugi telefon domowy"
+#: ../libpurple/protocols/msn/msn.c:2472
+#: ../libpurple/protocols/oscar/userinfo.c:463
msgid "Home Address"
msgstr "Adres domowy"
+#: ../libpurple/protocols/msn/msn.c:2473
msgid "Personal Mobile"
msgstr "Osobisty telefon komórkowy"
+#: ../libpurple/protocols/msn/msn.c:2474
msgid "Home Fax"
msgstr "Faks domowy"
+#: ../libpurple/protocols/msn/msn.c:2475
msgid "Personal Email"
msgstr "Osobisty adres e-mail"
+#: ../libpurple/protocols/msn/msn.c:2476
msgid "Personal IM"
msgstr "Osobisty komunikator"
+#: ../libpurple/protocols/msn/msn.c:2478
msgid "Anniversary"
msgstr "Rocznica"
#. Business
+#: ../libpurple/protocols/msn/msn.c:2494
msgid "Work"
msgstr "Praca"
+#: ../libpurple/protocols/msn/msn.c:2497
+#: ../libpurple/protocols/oscar/userinfo.c:481
msgid "Company"
msgstr "Firma"
+#: ../libpurple/protocols/msn/msn.c:2498
+#: ../libpurple/protocols/novell/novell.c:1511
msgid "Department"
msgstr "Dział"
+#: ../libpurple/protocols/msn/msn.c:2499
msgid "Profession"
msgstr "Zawód"
+#: ../libpurple/protocols/msn/msn.c:2500
msgid "Work Phone"
msgstr "Telefon służbowy"
+#: ../libpurple/protocols/msn/msn.c:2501
msgid "Work Phone 2"
msgstr "Drugi telefon służbowy"
+#: ../libpurple/protocols/msn/msn.c:2502
+#: ../libpurple/protocols/oscar/userinfo.c:471
msgid "Work Address"
msgstr "Adres do pracy"
+#: ../libpurple/protocols/msn/msn.c:2503
msgid "Work Mobile"
msgstr "Służbowy telefon komórkowy"
+#: ../libpurple/protocols/msn/msn.c:2504
msgid "Work Pager"
msgstr "Pager służbowy"
+#: ../libpurple/protocols/msn/msn.c:2505
msgid "Work Fax"
msgstr "Faks służbowy"
+#: ../libpurple/protocols/msn/msn.c:2506
msgid "Work Email"
msgstr "Służbowy adres e-mail"
+#: ../libpurple/protocols/msn/msn.c:2507
msgid "Work IM"
msgstr "Służbowy komunikator"
-msgid "Start Date"
-msgstr "Data rozpoczęcia"
-
+#: ../libpurple/protocols/msn/msn.c:2581 ../libpurple/protocols/msn/msn.c:2587
+#: ../libpurple/protocols/msn/msn.c:2594 ../libpurple/protocols/msn/msn.c:2601
msgid "Favorite Things"
msgstr "Ulubione rzeczy"
+#: ../libpurple/protocols/msn/msn.c:2646
msgid "Last Updated"
msgstr "Ostatnia aktualizacja"
+#: ../libpurple/protocols/msn/msn.c:2657
+#: ../libpurple/protocols/silc/ops.c:1039
msgid "Homepage"
msgstr "Strona domowa"
+#: ../libpurple/protocols/msn/msn.c:2683
msgid "The user has not created a public profile."
msgstr "Użytkownik nie utworzył profilu publicznego."
+#: ../libpurple/protocols/msn/msn.c:2684
msgid ""
"MSN reported not being able to find the user's profile. This either means "
"that the user does not exist, or that the user exists but has not created a "
"public profile."
-msgstr ""
-"MSN nie mógł odnaleźć profilu użytkownika. Oznacza to, że użytkownik nie "
-"istnieje albo nie utworzył profilu publicznego."
+msgstr "MSN nie mógł odnaleźć profilu użytkownika. Oznacza to, że użytkownik nie istnieje albo nie utworzył profilu publicznego."
+#: ../libpurple/protocols/msn/msn.c:2688
msgid ""
"Could not find any information in the user's profile. The user most likely "
"does not exist."
-msgstr ""
-"Nie można odnaleźć informacji w profilu użytkownika. Użytkownik "
-"prawdopodobnie nie istnieje."
+msgstr "Nie można odnaleźć informacji w profilu użytkownika. Użytkownik prawdopodobnie nie istnieje."
+#: ../libpurple/protocols/msn/msn.c:2696
+#: ../libpurple/protocols/oscar/userinfo.c:545
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1234
msgid "View web profile"
msgstr "Wyświetl profil WWW"
@@ -5780,580 +8452,698 @@ msgstr "Wyświetl profil WWW"
#. *< name
#. *< version
#. *< summary
+#: ../libpurple/protocols/msn/msn.c:2972 ../libpurple/protocols/msn/msn.c:2973
msgid "Windows Live Messenger Protocol Plugin"
msgstr "Wtyczka protokołu Windows Live Messenger"
+#: ../libpurple/protocols/msn/msn.c:3007
msgid "Use HTTP Method"
msgstr "Użycie metody HTTP"
+#: ../libpurple/protocols/msn/msn.c:3012
msgid "HTTP Method Server"
msgstr "Serwer metody HTTP"
+#: ../libpurple/protocols/msn/msn.c:3017
msgid "Show custom smileys"
msgstr "Wyświetlanie własnych emotikon"
+#: ../libpurple/protocols/msn/msn.c:3022
msgid "Allow direct connections"
msgstr "Zezwalanie na bezpośrednie połączenia"
+#: ../libpurple/protocols/msn/msn.c:3027
msgid "Allow connecting from multiple locations"
msgstr "Zezwalanie na łączenie z wielu położeń"
+#: ../libpurple/protocols/msn/msn.c:3035
msgid "nudge: nudge a user to get their attention"
msgstr "nudge: szturcha użytkownika, aby uzyskać jego uwagę"
+#: ../libpurple/protocols/msn/nexus.c:359
msgid "Windows Live ID authentication:Unable to connect"
msgstr "Uwierzytelnianie identyfikatora Windows Live: nie można połączyć"
+#: ../libpurple/protocols/msn/nexus.c:366
msgid "Windows Live ID authentication:Invalid response"
msgstr "Uwierzytelnianie identyfikatora Windows Live: nieprawidłowa odpowiedź"
+#: ../libpurple/protocols/msn/notification.c:824
msgid "The following users are missing from your addressbook"
msgstr "Brak poniższych użytkowników w książce adresowej"
+#: ../libpurple/protocols/msn/notification.c:832
+#: ../libpurple/protocols/msn/notification.c:881
#, c-format
msgid "Unknown error (%d): %s"
msgstr "Nieznany błąd (%d): %s"
+#: ../libpurple/protocols/msn/notification.c:836
+#: ../libpurple/protocols/msn/notification.c:861
+#: ../libpurple/protocols/sametime/sametime.c:4416
msgid "Unable to add user"
msgstr "Nie można dodać użytkownika"
#. Unknown error!
+#: ../libpurple/protocols/msn/notification.c:860
+#: ../libpurple/protocols/msn/notification.c:908
+#: ../libpurple/protocols/yahoo/libymsg.c:2086
#, c-format
msgid "Unknown error (%d)"
msgstr "Nieznany błąd (%d)"
+#: ../libpurple/protocols/msn/notification.c:885
+#: ../libpurple/protocols/msn/notification.c:909
msgid "Unable to remove user"
msgstr "Nie można usunąć użytkownika"
+#: ../libpurple/protocols/msn/notification.c:1213
msgid "Mobile message was not sent because it was too long."
msgstr "Wiadomość Mobile nie została wysłana, ponieważ jest za długa."
+#: ../libpurple/protocols/msn/notification.c:1215
msgid "Mobile message was not sent because an unknown error occurred."
msgstr "Nie wysłano wiadomości Mobile, ponieważ wystąpił nieznany błąd."
+#: ../libpurple/protocols/msn/notification.c:2242
#, c-format
msgid ""
-"The MSN server will shut down for maintenance in %d minute. You will "
-"automatically be signed out at that time. Please finish any conversations "
-"in progress.\n"
+"The MSN server will shut down for maintenance in %d minute. You will automatically be signed out at that time. Please finish any conversations in progress.\n"
"\n"
-"After the maintenance has been completed, you will be able to successfully "
-"sign in."
+"After the maintenance has been completed, you will be able to successfully sign in."
msgid_plural ""
-"The MSN server will shut down for maintenance in %d minutes. You will "
-"automatically be signed out at that time. Please finish any conversations "
-"in progress.\n"
+"The MSN server will shut down for maintenance in %d minutes. You will automatically be signed out at that time. Please finish any conversations in progress.\n"
"\n"
-"After the maintenance has been completed, you will be able to successfully "
-"sign in."
-msgstr[0] ""
-"Serwer MSN zostanie wyłączony w celach konserwacyjnych za %d minutę. Po tym "
-"czasie nastąpi rozłączenie z MSN. Proszę zakończyć wszystkie rozmowy przed "
-"upłynięciem tego czasu.\n"
-"\n"
-"Po ukończeniu konserwacji będzie można zalogować się ponownie."
-msgstr[1] ""
-"Serwer MSN zostanie wyłączony w celach konserwacyjnych za %d minuty. Po tym "
-"czasie nastąpi rozłączenie z MSN. Proszę zakończyć wszystkie rozmowy przed "
-"upłynięciem tego czasu.\n"
-"\n"
-"Po ukończeniu konserwacji będzie można zalogować się ponownie."
-msgstr[2] ""
-"Serwer MSN zostanie wyłączony w celach konserwacyjnych za %d minut. Po tym "
-"czasie nastąpi rozłączenie z MSN. Proszę zakończyć wszystkie rozmowy przed "
-"upłynięciem tego czasu.\n"
-"\n"
-"Po ukończeniu konserwacji będzie można zalogować się ponownie."
+"After the maintenance has been completed, you will be able to successfully sign in."
+msgstr[0] "Serwer MSN zostanie wyłączony w celach konserwacyjnych za %d minutę. Po tym czasie nastąpi rozłączenie z MSN. Proszę zakończyć wszystkie rozmowy przed upłynięciem tego czasu.\n\nPo ukończeniu konserwacji będzie można zalogować się ponownie."
+msgstr[1] "Serwer MSN zostanie wyłączony w celach konserwacyjnych za %d minuty. Po tym czasie nastąpi rozłączenie z MSN. Proszę zakończyć wszystkie rozmowy przed upłynięciem tego czasu.\n\nPo ukończeniu konserwacji będzie można zalogować się ponownie."
+msgstr[2] "Serwer MSN zostanie wyłączony w celach konserwacyjnych za %d minut. Po tym czasie nastąpi rozłączenie z MSN. Proszę zakończyć wszystkie rozmowy przed upłynięciem tego czasu.\n\nPo ukończeniu konserwacji będzie można zalogować się ponownie."
+#: ../libpurple/protocols/msn/oim.c:406
msgid ""
"Message was not sent because the system is unavailable. This normally "
"happens when the user is blocked or does not exist."
-msgstr ""
-"Nie wysłano wiadomości, ponieważ system jest niedostępny. Zazwyczaj zdarza "
-"się to w sytuacji, kiedy użytkownik jet zablokowany lub nie istnieje."
+msgstr "Nie wysłano wiadomości, ponieważ system jest niedostępny. Zazwyczaj zdarza się to w sytuacji, kiedy użytkownik jet zablokowany lub nie istnieje."
+#: ../libpurple/protocols/msn/oim.c:410
msgid "Message was not sent because messages are being sent too quickly."
msgstr "Nie wysłano wiadomości, ponieważ wiadomości są za szybko wysyłane."
+#: ../libpurple/protocols/msn/oim.c:413
msgid "Message was not sent because an unknown encoding error occurred."
msgstr "Nie wysłano wiadomości, ponieważ wystąpił nieznany błąd kodowania."
+#: ../libpurple/protocols/msn/oim.c:419
msgid "Message was not sent because an unknown error occurred."
msgstr "Nie wysłano wiadomości, ponieważ wystąpił nieznany błąd."
+#: ../libpurple/protocols/msn/oim.c:671
#, c-format
msgid ""
"%s (There was an error receiving this message. Converting the encoding from "
"%s to UTF-8 failed.)"
-msgstr ""
-"%s (Wystąpił błąd podczas pobierania tej wiadomości. Konwertowanie kodowania "
-"z %s na UTF-8 się nie powiodło.)"
+msgstr "%s (Wystąpił błąd podczas odbierania tej wiadomości. Konwertowanie kodowania %s na UTF-8 się nie powiodło.)"
+#: ../libpurple/protocols/msn/oim.c:686
#, c-format
msgid ""
"%s (There was an error receiving this message. The charset was %s, but it "
"was not valid UTF-8.)"
-msgstr ""
-"%s (Wystąpił błąd podczas pobierania tej wiadomości. Kodowaniem było %s, ale "
-"to nie jest prawidłowe UTF-8.)"
+msgstr "%s (Wystąpił błąd podczas odbierania tej wiadomości. Zestawem znaków był %s, ale nie był on prawidłowym UTF-8.)"
+#: ../libpurple/protocols/msn/oim.c:690
#, c-format
msgid ""
"%s (There was an error receiving this message. The charset was missing, but "
"it was not valid UTF-8.)"
-msgstr ""
-"%s (Wystąpił błąd podczas pobierania tej wiadomości. Brak kodowania, ale to "
-"nie jest prawidłowe UTF-8.)"
+msgstr "%s (Wystąpił błąd podczas odbierania tej wiadomości. Brakuje zestawu znaków, i nie był on prawidłowym UTF-8.)"
+#: ../libpurple/protocols/msn/servconn.c:145
msgid "Writing error"
msgstr "Błąd zapisywania"
+#: ../libpurple/protocols/msn/servconn.c:147
msgid "Reading error"
msgstr "Błąd odczytywania"
+#: ../libpurple/protocols/msn/servconn.c:169
#, c-format
msgid ""
"Connection error from %s server:\n"
"%s"
-msgstr ""
-"Błąd połączenia od serwera %s:\n"
-"%s"
+msgstr "Błąd połączenia od serwera %s:\n%s"
+#: ../libpurple/protocols/msn/session.c:374
msgid "Our protocol is not supported by the server"
msgstr "Protokół nie jest obsługiwany przez serwer"
-msgid "Error parsing HTTP"
-msgstr "Błąd podczas przetwarzania HTTP"
-
+#: ../libpurple/protocols/msn/session.c:383
+#: ../libpurple/protocols/novell/novell.c:2060
+#: ../libpurple/protocols/oscar/flap_connection.c:464
+#: ../libpurple/protocols/yahoo/libymsg.c:174
msgid "You have signed on from another location"
msgstr "Zalogowano z innego położenia"
-msgid "The MSN servers are temporarily unavailable. Please wait and try again."
-msgstr ""
-"Serwery MSN są tymczasowo niedostępne. Proszę poczekać i spróbować ponownie."
+#: ../libpurple/protocols/msn/session.c:389
+msgid ""
+"The MSN servers are temporarily unavailable. Please wait and try again."
+msgstr "Serwery MSN są tymczasowo niedostępne. Proszę poczekać i spróbować ponownie."
+#: ../libpurple/protocols/msn/session.c:395
msgid "The MSN servers are going down temporarily"
msgstr "Serwery MSN są tymczasowo wyłączane"
+#: ../libpurple/protocols/msn/session.c:400
#, c-format
msgid "Unable to authenticate: %s"
msgstr "Nie można uwierzytelnić: %s"
+#: ../libpurple/protocols/msn/session.c:409
msgid ""
"Your MSN buddy list is temporarily unavailable. Please wait and try again."
-msgstr ""
-"Lista znajomych MSN jest tymczasowo niedostępna. Proszę poczekać i spróbować "
-"ponownie."
+msgstr "Lista znajomych MSN jest tymczasowo niedostępna. Proszę poczekać i spróbować ponownie."
+#: ../libpurple/protocols/msn/session.c:431
+#: ../libpurple/protocols/msn/session.c:433
msgid "Handshaking"
msgstr "Nawiązywanie połączenia"
+#: ../libpurple/protocols/msn/session.c:432
msgid "Transferring"
msgstr "Przesyłanie"
+#: ../libpurple/protocols/msn/session.c:434
msgid "Starting authentication"
msgstr "Rozpoczynanie uwierzytelniania"
+#: ../libpurple/protocols/msn/session.c:435
msgid "Getting cookie"
msgstr "Pobieranie ciasteczka"
+#: ../libpurple/protocols/msn/session.c:437
msgid "Sending cookie"
msgstr "Wysyłanie ciasteczka"
+#: ../libpurple/protocols/msn/session.c:438
msgid "Retrieving buddy list"
msgstr "Pobieranie listy znajomych"
+#: ../libpurple/protocols/msn/slpcall.c:585
#, c-format
-msgid "%s requests to view your webcam, but this request is not yet supported."
-msgstr ""
-"Użytkownik %s poprosił o wyświetlenie kamery internetowej, ale ta prośba nie "
-"jest jeszcze obsługiwana."
+msgid ""
+"%s requests to view your webcam, but this request is not yet supported."
+msgstr "Użytkownik %s poprosił o wyświetlenie kamery internetowej, ale ta prośba nie jest jeszcze obsługiwana."
+#: ../libpurple/protocols/msn/slpcall.c:604
#, c-format
msgid "%s invited you to view his/her webcam, but this is not yet supported."
-msgstr ""
-"Użytkownik %s zaprosił do obejrzenia swojej kamery internetowej, ale to nie "
-"jest jeszcze obsługiwane."
+msgstr "Użytkownik %s zaprosił do obejrzenia swojej kamery internetowej, ale to nie jest jeszcze obsługiwane."
+#: ../libpurple/protocols/msn/state.c:40
msgid "Away From Computer"
msgstr "Nie ma mnie przy komputerze"
+#: ../libpurple/protocols/msn/state.c:41
msgid "On The Phone"
msgstr "Rozmawia przez telefon"
+#: ../libpurple/protocols/msn/state.c:42
msgid "Out To Lunch"
msgstr "Na obiedzie"
+#: ../libpurple/protocols/msn/switchboard.c:465
msgid "Message may have not been sent because a timeout occurred:"
msgstr "Wiadomość nie została wysłana, ponieważ przekroczono czas oczekiwania:"
+#: ../libpurple/protocols/msn/switchboard.c:501
msgid "Message could not be sent, not allowed while invisible:"
-msgstr ""
-"Wiadomość nie została wysłana, nie można wysyłać wiadomości będąc w trybie "
-"niewidocznym:"
+msgstr "Wiadomość nie została wysłana, nie można wysyłać wiadomości będąc w trybie niewidocznym:"
+#: ../libpurple/protocols/msn/switchboard.c:505
msgid "Message could not be sent because the user is offline:"
-msgstr ""
-"Wiadomość nie została wysłana, ponieważ użytkownik jest w trybie offline:"
+msgstr "Wiadomość nie została wysłana, ponieważ użytkownik jest w trybie offline:"
+#: ../libpurple/protocols/msn/switchboard.c:509
msgid "Message could not be sent because a connection error occurred:"
msgstr "Wiadomość nie została wysłana, ponieważ wystąpił błąd połączenia:"
+#: ../libpurple/protocols/msn/switchboard.c:513
msgid "Message could not be sent because we are sending too quickly:"
-msgstr ""
-"Wiadomość nie została wysłana, ponieważ wiadomości są wysyłane za szybko:"
+msgstr "Wiadomość nie została wysłana, ponieważ wiadomości są wysyłane za szybko:"
+#: ../libpurple/protocols/msn/switchboard.c:517
msgid ""
-"Message could not be sent because we were unable to establish a session with "
-"the server. This is likely a server problem, try again in a few minutes:"
-msgstr ""
-"Wiadomość nie została wysłana, ponieważ nie można nawiązać sesji z serwerem. "
-"Prawdopodobnie jest to problem serwera, proszę spróbować ponownie za kilka "
-"minut:"
+"Message could not be sent because we were unable to establish a session with"
+" the server. This is likely a server problem, try again in a few minutes:"
+msgstr "Wiadomość nie została wysłana, ponieważ nie można nawiązać sesji z serwerem. Prawdopodobnie jest to problem serwera, proszę spróbować ponownie za kilka minut:"
+#: ../libpurple/protocols/msn/switchboard.c:524
msgid ""
"Message could not be sent because an error with the switchboard occurred:"
msgstr "Wiadomość nie została wysłana, ponieważ wystąpił błąd centrali:"
+#: ../libpurple/protocols/msn/switchboard.c:532
msgid "Message may have not been sent because an unknown error occurred:"
msgstr "Wiadomość nie została wysłana ponieważ wystąpił nieznany błąd:"
+#: ../libpurple/protocols/msn/userlist.c:473
msgid "Delete Buddy from Address Book?"
msgstr "Usunąć znajomego z książki adresowej?"
+#: ../libpurple/protocols/msn/userlist.c:474
msgid "Do you want to delete this buddy from your address book as well?"
msgstr "Usunąć tego znajomego także z książki adresowej?"
-msgid "The username specified is invalid."
-msgstr "Podana nazwa użytkownika jest nieprawidłowa."
-
+#: ../libpurple/protocols/mxit/actions.c:61
msgid "The Display Name you entered is invalid."
msgstr "Podana wyświetlana nazwa jest nieprawidłowa."
+#: ../libpurple/protocols/mxit/actions.c:68
+#: ../libpurple/protocols/mxit/login.c:254
msgid ""
"The birthday you entered is invalid. The correct format is: 'YYYY-MM-DD'."
-msgstr ""
-"Podana data urodzenia jest nieprawidłowa. Poprawna forma to: \"YYYY-MM-DD\"."
+msgstr "Podana data urodzenia jest nieprawidłowa. Poprawna forma to: \"YYYY-MM-DD\"."
#. show error to user
+#: ../libpurple/protocols/mxit/actions.c:197
msgid "Profile Update Error"
msgstr "Błąd podczas aktualizacji profilu"
#. no profile information yet, so we cannot update
#. (reference: "libpurple/request.h")
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
+#: ../libpurple/protocols/mxit/actions.c:221
+#: ../libpurple/protocols/mxit/actions.c:310
+#: ../libpurple/protocols/oscar/userinfo.c:536
msgid "Profile"
msgstr "Profil"
+#: ../libpurple/protocols/mxit/actions.c:221
msgid "Your profile information is not yet retrieved. Please try again later."
-msgstr ""
-"Nie pobrano jeszcze informacji o profilu. Proszę spróbować ponownie później."
+msgstr "Nie pobrano jeszcze informacji o profilu. Proszę spróbować ponownie później."
#. display name
#. nick name (required)
+#: ../libpurple/protocols/mxit/actions.c:232
+#: ../libpurple/protocols/mxit/login.c:342
+#: ../libpurple/protocols/mxit/profile.c:212
+#: ../libpurple/protocols/mxit/profile.c:335
msgid "Display Name"
msgstr "Wyświetl nazwę"
#. about me
+#: ../libpurple/protocols/mxit/actions.c:256
+#: ../libpurple/protocols/mxit/profile.c:227
msgid "About Me"
msgstr "O mnie"
#. where I live
+#: ../libpurple/protocols/mxit/actions.c:260
+#: ../libpurple/protocols/mxit/profile.c:230
msgid "Where I Live"
msgstr "Gdzie mieszkam"
#. relationship status
+#: ../libpurple/protocols/mxit/actions.c:264
+#: ../libpurple/protocols/mxit/profile.c:232
msgid "Relationship Status"
msgstr "Stan cywilny"
#. mobile number
+#: ../libpurple/protocols/mxit/actions.c:295
msgid "Mobile Number"
msgstr "Numer telefonu komórkowego"
#. is searchable
+#: ../libpurple/protocols/mxit/actions.c:299
msgid "Can be searched"
msgstr "Może być wyszukiwany"
#. is suggestable
+#: ../libpurple/protocols/mxit/actions.c:303
msgid "Can be suggested"
msgstr "Może być proponowany"
+#: ../libpurple/protocols/mxit/actions.c:310
msgid "Update your MXit Profile"
msgstr "Zaktualizuj profil MXit"
+#: ../libpurple/protocols/mxit/actions.c:335
+#: ../libpurple/protocols/mxit/login.c:265
msgid "The PIN you entered is invalid."
msgstr "Podany kod PIN jest nieprawidłowy."
+#: ../libpurple/protocols/mxit/actions.c:340
msgid "The PIN you entered has an invalid length [4-10]."
msgstr "Podany kod PIN ma nieprawidłową długość [4-10]."
+#: ../libpurple/protocols/mxit/actions.c:345
+#: ../libpurple/protocols/mxit/login.c:275
msgid "The PIN is invalid. It should only consist of digits [0-9]."
msgstr "Kod PIN jest nieprawidłowy. Powinien składać się tylko z cyfr [0-9]."
+#: ../libpurple/protocols/mxit/actions.c:351
+#: ../libpurple/protocols/mxit/login.c:281
msgid "The two PINs you entered do not match."
msgstr "Oba podane kody PIN nie zgadzają się."
#. show error to user
+#: ../libpurple/protocols/mxit/actions.c:369
msgid "PIN Update Error"
msgstr "Błąd podczas aktualizacji kodu PIN"
#. pin
#. pin (required)
+#: ../libpurple/protocols/mxit/actions.c:394
+#: ../libpurple/protocols/mxit/login.c:359
msgid "PIN"
msgstr "PIN"
#. verify pin
+#: ../libpurple/protocols/mxit/actions.c:399
+#: ../libpurple/protocols/mxit/login.c:363
msgid "Verify PIN"
msgstr "Sprawdź kod PIN"
#. (reference: "libpurple/request.h")
+#: ../libpurple/protocols/mxit/actions.c:404
msgid "Change PIN"
msgstr "Zmień PIN"
+#: ../libpurple/protocols/mxit/actions.c:404
msgid "Change MXit PIN"
msgstr "Zmień kod PIN MXit"
+#: ../libpurple/protocols/mxit/actions.c:422
msgid "View Splash"
msgstr "Wyświetl ekran powitalny"
+#: ../libpurple/protocols/mxit/actions.c:422
msgid "There is no splash-screen currently available"
msgstr "Żaden ekran powitalny nie jest obecnie dostępny"
+#: ../libpurple/protocols/mxit/actions.c:442
msgid "About"
msgstr "O wtyczce"
+#: ../libpurple/protocols/mxit/actions.c:490
msgid "Search for user"
msgstr "Wyszukaj użytkownika"
+#: ../libpurple/protocols/mxit/actions.c:491
msgid "Search for a MXit contact"
msgstr "Wyszukaj kontaktu MXit"
+#: ../libpurple/protocols/mxit/actions.c:492
msgid "Type search information"
msgstr "Tutaj można wpisać wyszukiwane informacje"
+#: ../libpurple/protocols/mxit/actions.c:494
+#: ../libpurple/protocols/oscar/oscar.c:5154
msgid "_Search"
msgstr "Wy_szukaj"
#. display / change profile
+#: ../libpurple/protocols/mxit/actions.c:514
msgid "Change Profile..."
msgstr "Zmień profil..."
#. change PIN
+#: ../libpurple/protocols/mxit/actions.c:518
msgid "Change PIN..."
msgstr "Zmień kod PIN..."
#. suggested friends
+#: ../libpurple/protocols/mxit/actions.c:522
msgid "Suggested friends..."
msgstr "Zaproponuj znajomych..."
#. search for contacts
+#: ../libpurple/protocols/mxit/actions.c:526
msgid "Search for contacts..."
msgstr "Wyszukaj kontakty..."
#. display splash-screen
+#: ../libpurple/protocols/mxit/actions.c:530
msgid "View Splash..."
msgstr "Wyświetl ekran powitalny..."
#. display plugin version
+#: ../libpurple/protocols/mxit/actions.c:534
msgid "About..."
msgstr "O wtyczce..."
#. the file is too big
+#: ../libpurple/protocols/mxit/filexfer.c:130
msgid "The file you are trying to send is too large!"
msgstr "Wysyłany plik jest za duży."
#. file read error
+#: ../libpurple/protocols/mxit/filexfer.c:177
msgid "Unable to access the local file"
msgstr "Nie można uzyskać dostępu do lokalnego pliku"
#. file write error
+#: ../libpurple/protocols/mxit/filexfer.c:455 ../pidgin/gtkwhiteboard.c:848
msgid "Unable to save the file"
msgstr "Nie można zapisać pliku"
-msgid ""
-"Unable to connect to the MXit HTTP server. Please check your server settings."
-msgstr ""
-"Nie można połączyć się z serwerem HTTP sieci MXit. Proszę sprawdzić "
-"ustawienia serwera."
-
+#: ../libpurple/protocols/mxit/login.c:113
msgid "Logging In..."
msgstr "Logowanie..."
+#: ../libpurple/protocols/mxit/login.c:175
+#: ../libpurple/protocols/mxit/login.c:212
msgid ""
"Unable to connect to the MXit server. Please check your server settings."
-msgstr ""
-"Nie można połączyć się z serwerem MXit. Proszę sprawdzić ustawienia serwera."
+msgstr "Nie można połączyć się z serwerem MXit. Proszę sprawdzić ustawienia serwera."
+#: ../libpurple/protocols/mxit/login.c:200
msgid "Connecting..."
msgstr "Łączenie..."
+#: ../libpurple/protocols/mxit/login.c:246
msgid "The Display Name you entered is too short."
msgstr "Podana wyświetlana nazwa jest za krótka."
+#: ../libpurple/protocols/mxit/login.c:270
msgid "The PIN you entered has an invalid length [7-10]."
msgstr "Podany kod PIN ma nieprawidłową długość [7-10]."
#. mxit login name
+#: ../libpurple/protocols/mxit/login.c:337
msgid "MXit ID"
msgstr "Identyfikator MXit"
#. show the form to the user to complete
+#: ../libpurple/protocols/mxit/login.c:369
+#: ../libpurple/protocols/mxit/login.c:370
msgid "Register New MXit Account"
msgstr "Zarejestruj nowe konto MXit"
+#: ../libpurple/protocols/mxit/login.c:371
msgid "Please fill in the following fields:"
msgstr "Proszę wypełnić następujące pola:"
#. no reply from the WAP site
+#: ../libpurple/protocols/mxit/login.c:399
+#: ../libpurple/protocols/mxit/login.c:633
msgid "Error contacting the MXit WAP site. Please try again later."
-msgstr ""
-"Błąd podczas łączenia się ze stroną WAP sieci MXit. Proszę spróbować "
-"ponownie później."
+msgstr "Błąd podczas łączenia się ze stroną WAP sieci MXit. Proszę spróbować ponownie później."
#. wapserver error
#. server could not find the user
+#: ../libpurple/protocols/mxit/login.c:413
+#: ../libpurple/protocols/mxit/login.c:642
msgid ""
"MXit is currently unable to process the request. Please try again later."
-msgstr ""
-"MXit obecnie nie może przetworzyć żądania. Proszę spróbować ponownie później."
+msgstr "MXit obecnie nie może przetworzyć żądania. Proszę spróbować ponownie później."
+#: ../libpurple/protocols/mxit/login.c:423
msgid "Wrong security code entered. Please try again later."
msgstr "Podano błędny kod bezpieczeństwa. Proszę spróbować ponownie później."
+#: ../libpurple/protocols/mxit/login.c:426
msgid "Your session has expired. Please try again later."
msgstr "Sesja wygasła. Proszę spróbować ponownie później."
+#: ../libpurple/protocols/mxit/login.c:429
msgid "Invalid country selected. Please try again."
msgstr "Wybrano nieprawidłowy kraj. Proszę spróbować ponownie."
+#: ../libpurple/protocols/mxit/login.c:432
msgid "The MXit ID you entered is not registered. Please register first."
-msgstr ""
-"Podany identyfikator MXit nie jest zarejestrowany. Proszę najpierw się "
-"zarejestrować."
+msgstr "Podany identyfikator MXit nie jest zarejestrowany. Proszę najpierw się zarejestrować."
+#: ../libpurple/protocols/mxit/login.c:435
msgid "The MXit ID you entered is already registered. Please choose another."
msgstr "Identyfikator MXit jest już zarejestrowany. Proszę wybrać inny."
+#: ../libpurple/protocols/mxit/login.c:442
msgid "Internal error. Please try again later."
msgstr "Wewnętrzny błąd. Proszę spróbować ponownie później."
+#: ../libpurple/protocols/mxit/login.c:532
msgid "You did not enter the security code"
msgstr "Nie podano kodu bezpieczeństwa"
+#: ../libpurple/protocols/mxit/login.c:660
msgid "Security Code"
msgstr "Kod bezpieczeństwa"
#. ask for input (required)
+#: ../libpurple/protocols/mxit/login.c:664
msgid "Enter Security Code"
msgstr "Proszę podać kod bezpieczeństwa"
+#: ../libpurple/protocols/mxit/login.c:670
msgid "Your Country"
msgstr "Kraj"
+#: ../libpurple/protocols/mxit/login.c:691
msgid "Your Language"
msgstr "Język"
#. display the form to the user and wait for his/her input
+#: ../libpurple/protocols/mxit/login.c:708
msgid "MXit Authorization"
msgstr "Upoważnienie MXit"
+#: ../libpurple/protocols/mxit/login.c:708
msgid "MXit account validation"
msgstr "Sprawdzenie konta MXit"
+#: ../libpurple/protocols/mxit/login.c:731
msgid "Retrieving User Information..."
msgstr "Pobieranie informacji o użytkowniku..."
+#: ../libpurple/protocols/mxit/multimx.c:214
msgid "was kicked"
msgstr "został wyrzucony"
+#: ../libpurple/protocols/mxit/multimx.c:230
msgid "You have been kicked from this MultiMX."
msgstr "Użytkownik został wyrzucony z tego MultiMX."
+#: ../libpurple/protocols/mxit/multimx.c:442
msgid "_Room Name:"
msgstr "_Nazwa pokoju:"
#. Display system message in chat window
+#: ../libpurple/protocols/mxit/multimx.c:577
msgid "You have invited"
msgstr "Zaproszono"
+#: ../libpurple/protocols/mxit/mxit.c:214
msgid "Loading menu..."
msgstr "Wczytywanie menu..."
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
+#: ../libpurple/protocols/mxit/mxit.c:348
+#: ../libpurple/protocols/mxit/profile.c:254
+#: ../libpurple/protocols/mxit/profile.c:284
msgid "Status Message"
msgstr "Wiadomość stanu"
+#: ../libpurple/protocols/mxit/mxit.c:361
msgid "Rejection Message"
msgstr "Wiadomość odrzucenia"
+#: ../libpurple/protocols/mxit/mxit.c:582
msgid "No profile available"
msgstr "Brak dostępnego profilu"
+#: ../libpurple/protocols/mxit/mxit.c:582
msgid "This contact does not have a profile."
msgstr "Ten kontakt nie posiada profilu."
+#: ../libpurple/protocols/mxit/mxit.c:601
msgid "Your MXit ID..."
msgstr "Identyfikator MXit użytkownika..."
#. contact is in Deleted, Rejected or None state
+#: ../libpurple/protocols/mxit/mxit.c:651
msgid "Re-Invite"
msgstr "Zaproś ponownie"
#. Configuration options
#. WAP server (reference: "libpurple/accountopt.h")
+#: ../libpurple/protocols/mxit/mxit.c:854
msgid "WAP Server"
msgstr "Serwer WAP"
+#: ../libpurple/protocols/mxit/mxit.c:857
msgid "Connect via HTTP"
msgstr "Połączenie przez HTTP"
+#: ../libpurple/protocols/mxit/mxit.c:860
msgid "Enable splash-screen popup"
msgstr "Włączenie wyskakującego ekranu powitalnego"
+#: ../libpurple/protocols/mxit/profile.c:49
msgid "Don't want to say"
msgstr "Nie chcę podać"
+#: ../libpurple/protocols/mxit/profile.c:51
msgid "Single"
msgstr "Singiel"
+#: ../libpurple/protocols/mxit/profile.c:53
msgid "In a relationship"
msgstr "W związku"
+#: ../libpurple/protocols/mxit/profile.c:55
msgid "Engaged"
msgstr "Zaręczony/zaręczona"
+#: ../libpurple/protocols/mxit/profile.c:57
msgid "Married"
msgstr "W związku małżeńskim"
+#: ../libpurple/protocols/mxit/profile.c:59
msgid "It's complicated"
msgstr "To skomplikowane"
+#: ../libpurple/protocols/mxit/profile.c:61
msgid "Widowed"
msgstr "Wdowa/wdowiec"
+#: ../libpurple/protocols/mxit/profile.c:63
msgid "Separated"
msgstr "W separacji"
+#: ../libpurple/protocols/mxit/profile.c:65
msgid "Divorced"
msgstr "Po rozwodzie"
+#: ../libpurple/protocols/mxit/profile.c:242
msgid "Last Online"
msgstr "Ostatnio online"
+#: ../libpurple/protocols/mxit/profile.c:267
msgid "Invite Message"
msgstr "Wiadomość zaproszenia"
+#: ../libpurple/protocols/mxit/profile.c:323
msgid "No results"
msgstr "Brak wyników"
+#: ../libpurple/protocols/mxit/profile.c:323
msgid "No contacts found."
msgstr "Nie odnaleziono kontaktów."
#. define columns
+#: ../libpurple/protocols/mxit/profile.c:332
msgid "UserId"
msgstr "Identyfikator użytkownika"
+#: ../libpurple/protocols/mxit/profile.c:345
msgid "Where I live"
msgstr "Gdzie mieszkam"
+#: ../libpurple/protocols/mxit/profile.c:372
#, c-format
msgid "You have %i suggested friend."
msgid_plural "You have %i suggested friends."
@@ -6361,6 +9151,7 @@ msgstr[0] "Zaproponowano %i znajomego."
msgstr[1] "Zaproponowano %i znajomych."
msgstr[2] "Zaproponowano %i znajomych."
+#: ../libpurple/protocols/mxit/profile.c:374
#, c-format
msgid "We found %i contact that matches your search."
msgid_plural "We found %i contacts that match your search."
@@ -6369,719 +9160,449 @@ msgstr[1] "Odnaleziono %i kontakty pasujące do wyszukiwania."
msgstr[2] "Odnaleziono %i kontaktów pasujących do wyszukiwania."
#. we must have lost the connection, so terminate it so that we can reconnect
+#: ../libpurple/protocols/mxit/protocol.c:388
msgid "We have lost the connection to MXit. Please reconnect."
msgstr "Utracono połączenie z MXit. Proszę połączyć się ponownie."
#. packet could not be queued for transmission
+#: ../libpurple/protocols/mxit/protocol.c:484
msgid "Message Send Error"
msgstr "Błąd podczas wysyłania wiadomości"
+#: ../libpurple/protocols/mxit/protocol.c:484
msgid "Unable to process your request at this time"
msgstr "Nie można przetworzyć żądania w tym momencie"
+#: ../libpurple/protocols/mxit/protocol.c:510
msgid "Timeout while waiting for a response from the MXit server."
msgstr "Przekroczono czas oczekiwania na odpowiedź od serwera MXit."
+#: ../libpurple/protocols/mxit/protocol.c:1446
msgid "Successfully Logged In..."
msgstr "Pomyślnie zalogowano..."
+#: ../libpurple/protocols/mxit/protocol.c:1543
#, c-format
msgid ""
"%s sent you an encrypted message, but it is not supported on this client."
-msgstr ""
-"Użytkownik %s wysłał zaszyfrowaną wiadomość, która nie jest obsługiwana w "
-"tym kliencie."
+msgstr "Użytkownik %s wysłał zaszyfrowaną wiadomość, która nie jest obsługiwana w tym kliencie."
+#: ../libpurple/protocols/mxit/protocol.c:1544
+#: ../libpurple/protocols/mxit/protocol.c:2464
msgid "Message Error"
msgstr "Błąd wiadomości"
-#. could not be decrypted
+#: ../libpurple/protocols/mxit/protocol.c:1551
msgid "An encrypted message was received which could not be decrypted."
msgstr "Otrzymano zaszyfrowaną wiadomość, której nie można odszyfrować."
+#: ../libpurple/protocols/mxit/protocol.c:2265
msgid "Cannot perform redirect using the specified protocol"
msgstr "Nie można wykonać przekierowania używając podanego protokołu"
+#: ../libpurple/protocols/mxit/protocol.c:2433
msgid "An internal MXit server error occurred."
msgstr "Wystąpił wewnętrzny błąd serwera MXit."
+#: ../libpurple/protocols/mxit/protocol.c:2452
#, c-format
msgid "Login error: %s (%i)"
msgstr "Błąd podczas logowania: %s (%i)"
+#: ../libpurple/protocols/mxit/protocol.c:2457
#, c-format
msgid "Logout error: %s (%i)"
msgstr "Błąd podczas wylogowywania: %s (%i)"
+#: ../libpurple/protocols/mxit/protocol.c:2461
msgid "Contact Error"
msgstr "Błąd kontaktu"
+#: ../libpurple/protocols/mxit/protocol.c:2467
msgid "Message Sending Error"
msgstr "Błąd podczas wysyłania wiadomości"
+#: ../libpurple/protocols/mxit/protocol.c:2470
msgid "Status Error"
msgstr "Błąd stanu"
+#: ../libpurple/protocols/mxit/protocol.c:2473
msgid "Mood Error"
msgstr "Błąd nastroju"
+#: ../libpurple/protocols/mxit/protocol.c:2484
msgid "Invitation Error"
msgstr "Błąd zaproszenia"
+#: ../libpurple/protocols/mxit/protocol.c:2487
msgid "Contact Removal Error"
msgstr "Błąd podczas usuwania kontaktu"
+#: ../libpurple/protocols/mxit/protocol.c:2491
msgid "Subscription Error"
msgstr "Błąd subskrypcji"
+#: ../libpurple/protocols/mxit/protocol.c:2494
msgid "Contact Update Error"
msgstr "Błąd podczas aktualizacji kontaktu"
+#: ../libpurple/protocols/mxit/protocol.c:2497
msgid "File Transfer Error"
msgstr "Błąd podczas przesyłania plików"
+#: ../libpurple/protocols/mxit/protocol.c:2500
msgid "Cannot create MultiMx room"
msgstr "Nie można utworzyć pokoju MultiMx"
+#: ../libpurple/protocols/mxit/protocol.c:2503
msgid "MultiMx Invitation Error"
msgstr "Błąd zaproszenia MultiMx"
+#: ../libpurple/protocols/mxit/protocol.c:2507
msgid "Profile Error"
msgstr "Błąd profilu"
#. bad packet
+#: ../libpurple/protocols/mxit/protocol.c:2721
msgid "Invalid packet received from MXit."
msgstr "Odebrano nieprawidłowy pakiet z MXit."
#. connection error
+#: ../libpurple/protocols/mxit/protocol.c:2786
msgid "A connection error occurred to MXit. (read stage 0x01)"
msgstr "Wystąpił błąd połączenia z MXit (poziom odczytu 0x01)."
#. connection closed
+#: ../libpurple/protocols/mxit/protocol.c:2791
msgid "A connection error occurred to MXit. (read stage 0x02)"
msgstr "Wystąpił błąd połączenia z MXit (poziom odczytu 0x02)."
+#: ../libpurple/protocols/mxit/protocol.c:2801
msgid "A connection error occurred to MXit. (read stage 0x03)"
msgstr "Wystąpił błąd połączenia z MXit (poziom odczytu 0x03)."
#. malformed packet length record (too long)
+#: ../libpurple/protocols/mxit/protocol.c:2812
msgid "A connection error occurred to MXit. (read stage 0x04)"
msgstr "Wystąpił błąd połączenia z MXit (poziom odczytu 0x04)."
#. connection error
+#: ../libpurple/protocols/mxit/protocol.c:2823
msgid "A connection error occurred to MXit. (read stage 0x05)"
msgstr "Wystąpił błąd połączenia z MXit (poziom odczytu 0x05)."
#. connection closed
+#: ../libpurple/protocols/mxit/protocol.c:2828
msgid "A connection error occurred to MXit. (read stage 0x06)"
msgstr "Wystąpił błąd połączenia z MXit (poziom odczytu 0x06)."
+#: ../libpurple/protocols/mxit/roster.c:210
+#: ../libpurple/protocols/silc/buddy.c:1528
+#: ../libpurple/protocols/silc/util.c:493
msgid "In Love"
msgstr "Zakochany"
+#: ../libpurple/protocols/mxit/roster.c:254
msgid "Pending"
msgstr "Oczekujący"
+#: ../libpurple/protocols/mxit/roster.c:256
msgid "Invited"
msgstr "Zaproszeni"
+#: ../libpurple/protocols/mxit/roster.c:258
msgid "Rejected"
msgstr "Odrzuceni"
+#: ../libpurple/protocols/mxit/roster.c:260
msgid "Deleted"
msgstr "Usunięci"
+#: ../libpurple/protocols/mxit/splashscreen.c:210
+#: ../libpurple/protocols/mxit/splashscreen.c:214
msgid "MXit Advertising"
msgstr "Reklamy MXit"
+#: ../libpurple/protocols/mxit/splashscreen.c:211
msgid "More Information"
msgstr "Więcej informacji"
-#, c-format
-msgid "No such user: %s"
-msgstr "Nie ma takiego użytkownika: %s"
-
-msgid "User lookup"
-msgstr "Wyszukanie użytkowników"
-
-msgid "Reading challenge"
-msgstr "Odczytywanie wyzwania"
-
-msgid "Unexpected challenge length from server"
-msgstr "Nieoczekiwana długość wyzwania od serwera"
-
-msgid "Logging in"
-msgstr "Logowanie"
-
-msgid "MySpaceIM - No Username Set"
-msgstr "MySpaceIM - nie ustawiono nazwy użytkownika"
-
-msgid "You appear to have no MySpace username."
-msgstr "Brak nazwy użytkownika MySpace."
-
-msgid "Would you like to set one now? (Note: THIS CANNOT BE CHANGED!)"
-msgstr "Ustawić ją teraz (uwaga: NIE MOŻE ZOSTAĆ POTEM ZMIENIONA)?"
-
-msgid "Lost connection with server"
-msgstr "Utracono połączenie z serwerem"
-
-#. Can't write _()'d strings in array initializers. Workaround.
-#. khc: then use N_() in the array initializer and use _() when they are
-#. used
-msgid "New mail messages"
-msgstr "Nowe wiadomości pocztowe"
-
-msgid "New blog comments"
-msgstr "Nowe komentarze na blogu"
-
-msgid "New profile comments"
-msgstr "Nowe komentarze na profilu"
-
-msgid "New friend requests!"
-msgstr "Nowe prośby o dodanie do listy przyjaciół"
-
-msgid "New picture comments"
-msgstr "Nowe komentarze do zdjęć"
-
-msgid "MySpace"
-msgstr "MySpace"
-
-msgid "IM Friends"
-msgstr "Znajomi"
-
-#, c-format
-msgid ""
-"%d buddy was added or updated from the server (including buddies already on "
-"the server-side list)"
-msgid_plural ""
-"%d buddies were added or updated from the server (including buddies already "
-"on the server-side list)"
-msgstr[0] ""
-"%d znajomy został dodany lub zaktualizowany z serwera (w tym znajomy już "
-"znajdujący się na liście na serwerze)"
-msgstr[1] ""
-"%d znajomych zostało dodanych lub zaktualizowanych z serwera (w tym znajomi "
-"już znajdujący się na liście na serwerze)"
-msgstr[2] ""
-"%d znajomych zostało dodanych lub zaktualizowanych z serwera (w tym znajomi "
-"już znajdujący się na liście na serwerze)"
-
-msgid "Add contacts from server"
-msgstr "Dodaj kontakty z serwera"
-
-#, c-format
-msgid "Protocol error, code %d: %s"
-msgstr "Błąd protokołu, kod %d: %s"
-
-#, c-format
-msgid ""
-"%s Your password is %zu characters, which is longer than the maximum length "
-"of %d. Please shorten your password at http://profileedit.myspace.com/index."
-"cfm?fuseaction=accountSettings.changePassword and try again."
-msgstr ""
-"%s Hasło ma %zu znaków, czyli więcej niż wynosi maksymalna długość %d. "
-"Proszę skrócić hasło na stronie http://profileedit.myspace.com/index.cfm?"
-"fuseaction=accountSettings.changePassword i spróbować ponownie."
-
-msgid "Incorrect username or password"
-msgstr "Nieprawidłowa nazwa użytkownika lub hasło"
-
-msgid "MySpaceIM Error"
-msgstr "Błąd MySpaceIM"
-
-msgid "Invalid input condition"
-msgstr "Nieprawidłowy warunek wejścia"
-
-msgid "Failed to add buddy"
-msgstr "Dodanie znajomego się nie powiodło"
-
-msgid "'addbuddy' command failed."
-msgstr "Polecenie \"addbuddy\" się nie powiodło."
-
-msgid "persist command failed"
-msgstr "Polecenie \"persist\" się nie powiodło"
-
-msgid "Failed to remove buddy"
-msgstr "Usunięcie znajomego się nie powiodło"
-
-msgid "'delbuddy' command failed"
-msgstr "Polecenie \"delbuddy\" się nie powiodło"
-
-msgid "blocklist command failed"
-msgstr "Polecenie \"blocklist\" się nie powiodło"
-
-msgid "Missing Cipher"
-msgstr "Brak szyfru"
-
-msgid "The RC4 cipher could not be found"
-msgstr "Nie można odnaleźć szyfru RC4"
-
-msgid ""
-"Upgrade to a libpurple with RC4 support (>= 2.0.1). MySpaceIM plugin will "
-"not be loaded."
-msgstr ""
-"Proszę zaktualizować do biblioteki libpurple z obsługą RC4 (>= 2.0.1). "
-"Wtyczka MySpaceIM nie zostanie wczytana."
-
-msgid "Add friends from MySpace.com"
-msgstr "Dodaj przyjaciół z serwisu MySpace.com"
-
-msgid "Importing friends failed"
-msgstr "Zaimportowanie przyjaciół się nie powiodło"
-
-#. TODO: find out how
-msgid "Find people..."
-msgstr "Znajdź osoby..."
-
-msgid "Change IM name..."
-msgstr "Zmień nazwę komunikatora..."
-
-msgid "myim URL handler"
-msgstr "obsługa adresów URL myim"
-
-msgid "No suitable MySpaceIM account could be found to open this myim URL."
-msgstr ""
-"Nie odnaleziono odpowiedniego konta MySpaceIM do otwarcia tego adresu URL "
-"myim."
-
-msgid "Enable the proper MySpaceIM account and try again."
-msgstr "Proszę włączyć odpowiednie konto MySpaceIM i spróbować ponowne."
-
-msgid "Show display name in status text"
-msgstr "Wyświetl nazwę ekranu w tekście stanu"
-
-msgid "Show headline in status text"
-msgstr "Wyświetl nagłówek w tekście stanu"
-
-msgid "Send emoticons"
-msgstr "Wyślij emotikony"
-
-msgid "Screen resolution (dots per inch)"
-msgstr "Rozdzielczość ekranu (punktów na cal)"
-
-msgid "Base font size (points)"
-msgstr "Podstawowy rozmiar czcionki (punkty)"
-
-msgid "User"
-msgstr "Użytkownik"
-
-msgid "Headline"
-msgstr "Nagłówek"
-
-msgid "Song"
-msgstr "Utwór"
-
-msgid "Total Friends"
-msgstr "Razem przyjaciół"
-
-msgid "Client Version"
-msgstr "Wersja klienta"
-
-msgid ""
-"An error occurred while trying to set the username. Please try again, or "
-"visit http://editprofile.myspace.com/index.cfm?fuseaction=profile.username "
-"to set your username."
-msgstr ""
-"Wystąpił błąd podczas próby ustawienia nazwy użytkownika. Proszę spróbować "
-"ponownie lub odwiedzić stronę http://editprofile.myspace.com/index.cfm?"
-"fuseaction=profile.username , aby ustawić nazwę użytkownika."
-
-msgid "MySpaceIM - Username Available"
-msgstr "MySpaceIM - nazwa użytkownika jest dostępna"
-
-msgid "This username is available. Would you like to set it?"
-msgstr "Ta nazwa użytkownika jest dostępna. Ustawić ją?"
-
-msgid "ONCE SET, THIS CANNOT BE CHANGED!"
-msgstr "PO USTAWIENIU NIE MOŻNA TEGO ZMIENIĆ"
-
-msgid "MySpaceIM - Please Set a Username"
-msgstr "MySpaceIM - proszę ustawić nazwę użytkownika"
-
-msgid "This username is unavailable."
-msgstr "Ta nazwa użytkownika jest niedostępna."
-
-msgid "Please try another username:"
-msgstr "Proszę spróbować inną nazwę użytkownika:"
-
-#. Protocol won't log in now without a username set.. Disconnect
-msgid "No username set"
-msgstr "Nie ustawiono nazwy użytkownika"
-
-msgid "Please enter a username to check its availability:"
-msgstr "Proszę podać nazwę użytkownika, aby sprawdzić jej dostępność:"
-
-#. TODO: icons for each zap
-#. Lots of comments for translators:
-#. Zap means "to strike suddenly and forcefully as if with a
-#. * projectile or weapon." This term often has an electrical
-#. * connotation, for example, "he was zapped by electricity when
-#. * he put a fork in the toaster."
-msgid "Zap"
-msgstr "Wstrząśnij"
-
-#, c-format
-msgid "%s has zapped you!"
-msgstr "Użytkownik %s wstrząsnął Tobą."
-
-#, c-format
-msgid "Zapping %s..."
-msgstr "Wstrząsanie użytkownikiem %s..."
-
-#. Whack means "to hit or strike someone with a sharp blow"
-msgid "Whack"
-msgstr "Uderz"
-
-#, c-format
-msgid "%s has whacked you!"
-msgstr "Użytkownik %s uderzył Cię."
-
-#, c-format
-msgid "Whacking %s..."
-msgstr "Uderzanie użytkownika %s..."
-
-#. Torch means "to set on fire." Don't worry, this doesn't
-#. * make a whole lot of sense in English, either. Feel free
-#. * to translate it literally.
-msgid "Torch"
-msgstr "Podpal"
-
-#, c-format
-msgid "%s has torched you!"
-msgstr "Użytkownik %s podpalił Cię."
-
-#, c-format
-msgid "Torching %s..."
-msgstr "Podpalanie użytkownika %s..."
-
-#. Smooch means "to kiss someone, often enthusiastically"
-msgid "Smooch"
-msgstr "Pocałuj"
-
-#, c-format
-msgid "%s has smooched you!"
-msgstr "Użytkownik %s pocałował Cię."
-
-#, c-format
-msgid "Smooching %s..."
-msgstr "Całowanie użytkownika %s..."
-
-#. A hug is a display of affection; wrapping your arms around someone
-msgid "Hug"
-msgstr "Przytul"
-
-#, c-format
-msgid "%s has hugged you!"
-msgstr "Użytkownik %s przytulił Cię."
-
-#, c-format
-msgid "Hugging %s..."
-msgstr "Przytulanie użytkownika %s..."
-
-#. Slap means "to hit someone with an open/flat hand"
-msgid "Slap"
-msgstr "Spoliczkuj"
-
-#, c-format
-msgid "%s has slapped you!"
-msgstr "Użytkownik %s spoliczkował Cię."
-
-#, c-format
-msgid "Slapping %s..."
-msgstr "Policzkowanie użytkownika %s..."
-
-#. Goose means "to pinch someone on their butt"
-msgid "Goose"
-msgstr "Kopnij"
-
-#, c-format
-msgid "%s has goosed you!"
-msgstr "Użytkownik %s kopnął Cię."
-
-#, c-format
-msgid "Goosing %s..."
-msgstr "Kopanie użytkownika %s..."
-
-#. A high-five is when two people's hands slap each other
-#. * in the air above their heads. It is done to celebrate
-#. * something, often a victory, or to congratulate someone.
-msgid "High-five"
-msgstr "Piątka"
-
-#, c-format
-msgid "%s has high-fived you!"
-msgstr "Użytkownik %s przybił piątkę."
-
-#, c-format
-msgid "High-fiving %s..."
-msgstr "Przybijanie piątki użytkownikowi %s..."
-
-#. We're not entirely sure what the MySpace people mean by
-#. * this... but we think it's the equivalent of "prank." Or, for
-#. * someone to perform a mischievous trick or practical joke.
-msgid "Punk"
-msgstr "Zrób żart"
-
-#, c-format
-msgid "%s has punk'd you!"
-msgstr "Użytkownik %s zażartował z Ciebie."
-
-#, c-format
-msgid "Punking %s..."
-msgstr "Żartowanie z użytkownika %s..."
-
-#. Raspberry is a slang term for the vibrating sound made
-#. * when you stick your tongue out of your mouth with your
-#. * lips closed and blow. It is typically done when
-#. * gloating or bragging. Nowadays it's a pretty silly
-#. * gesture, so it does not carry a harsh negative
-#. * connotation. It is generally used in a playful tone
-#. * with friends.
-msgid "Raspberry"
-msgstr "Pokaż język"
-
-#, c-format
-msgid "%s has raspberried you!"
-msgstr "Użytkownik %s pokazał ci język."
-
-#, c-format
-msgid "Raspberrying %s..."
-msgstr "Pokazywanie języka użytkownikowi %s..."
-
+#: ../libpurple/protocols/novell/nmuser.c:1863
msgid "Required parameters not passed in"
msgstr "Wymagane parametry nie zostały przekazane"
+#: ../libpurple/protocols/novell/nmuser.c:1866
msgid "Unable to write to network"
msgstr "Nie można zapisać do sieci"
+#: ../libpurple/protocols/novell/nmuser.c:1869
msgid "Unable to read from network"
msgstr "Nie można odczytać z sieci"
+#: ../libpurple/protocols/novell/nmuser.c:1872
msgid "Error communicating with server"
msgstr "Błąd podczas komunikacji z serwerem"
+#: ../libpurple/protocols/novell/nmuser.c:1876
msgid "Conference not found"
msgstr "Nie odnaleziono konferencji"
+#: ../libpurple/protocols/novell/nmuser.c:1879
msgid "Conference does not exist"
msgstr "Konferencja nie istnieje"
+#: ../libpurple/protocols/novell/nmuser.c:1883
msgid "A folder with that name already exists"
msgstr "Katalog o tej nazwie już istnieje"
+#: ../libpurple/protocols/novell/nmuser.c:1886
msgid "Not supported"
msgstr "Nieobsługiwane"
+#: ../libpurple/protocols/novell/nmuser.c:1890
msgid "Password has expired"
msgstr "Hasło wygasło"
-msgid "Incorrect password"
-msgstr "Niepoprawne hasło"
-
+#: ../libpurple/protocols/novell/nmuser.c:1899
msgid "Account has been disabled"
msgstr "Konto zostało wyłączone"
+#: ../libpurple/protocols/novell/nmuser.c:1902
msgid "The server could not access the directory"
msgstr "Serwer nie mógł uzyskać dostępu do katalogu"
+#: ../libpurple/protocols/novell/nmuser.c:1905
msgid "Your system administrator has disabled this operation"
msgstr "Administrator systemu wyłączył to działanie"
+#: ../libpurple/protocols/novell/nmuser.c:1908
msgid "The server is unavailable; try again later"
msgstr "Serwer jest niedostępny, proszę spróbować później"
+#: ../libpurple/protocols/novell/nmuser.c:1911
msgid "Cannot add a contact to the same folder twice"
msgstr "Nie można dodać kontaktu dwa razy do tego samego katalogu"
+#: ../libpurple/protocols/novell/nmuser.c:1914
msgid "Cannot add yourself"
msgstr "Nie można dodać siebie"
+#: ../libpurple/protocols/novell/nmuser.c:1917
msgid "Master archive is misconfigured"
msgstr "Główne archiwum jest błędnie skonfigurowane"
+#: ../libpurple/protocols/novell/nmuser.c:1921
+msgid "Incorrect username or password"
+msgstr "Nieprawidłowa nazwa użytkownika lub hasło"
+
+#: ../libpurple/protocols/novell/nmuser.c:1924
msgid "Could not recognize the host of the username you entered"
msgstr "Nie można rozpoznać komputera podanej nazwy użytkownika"
-msgid ""
-"Your account has been disabled because too many incorrect passwords were "
-"entered"
-msgstr ""
-"Konto zostało wyłączone, ponieważ podano niepoprawne hasło za dużą ilość razy"
-
+#: ../libpurple/protocols/novell/nmuser.c:1930
msgid "You cannot add the same person twice to a conversation"
msgstr "Nie można dodać tej samej osoby dwa razy do rozmowy"
+#: ../libpurple/protocols/novell/nmuser.c:1934
msgid "You have reached your limit for the number of contacts allowed"
msgstr "Osiągnięto maksymalną dozwoloną liczbę kontaktów"
+#: ../libpurple/protocols/novell/nmuser.c:1937
msgid "You have entered an incorrect username"
msgstr "Podano niepoprawną nazwę użytkownika"
+#: ../libpurple/protocols/novell/nmuser.c:1940
msgid "An error occurred while updating the directory"
msgstr "Wystąpił błąd podczas aktualizowania katalogu"
+#: ../libpurple/protocols/novell/nmuser.c:1943
msgid "Incompatible protocol version"
msgstr "Niezgodna wersja protokołu"
+#: ../libpurple/protocols/novell/nmuser.c:1946
msgid "The user has blocked you"
msgstr "Użytkownik zablokował użytkownika"
+#: ../libpurple/protocols/novell/nmuser.c:1949
msgid ""
"This evaluation version does not allow more than ten users to log in at one "
"time"
-msgstr ""
-"Wersja demonstracyjna nie pozwala na zalogowanie więcej niż dziesięciu "
-"użytkowników jednocześnie"
+msgstr "Wersja demonstracyjna nie pozwala na zalogowanie więcej niż dziesięciu użytkowników jednocześnie"
+#: ../libpurple/protocols/novell/nmuser.c:1952
msgid "The user is either offline or you are blocked"
msgstr "Użytkownik jest w trybie offline lub zablokował użytkownika"
+#: ../libpurple/protocols/novell/nmuser.c:1955
#, c-format
msgid "Unknown error: 0x%X"
msgstr "Nieznany błąd: 0x%X"
+#: ../libpurple/protocols/novell/novell.c:123
#, c-format
msgid "Unable to login: %s"
msgstr "Nie można się zalogować: %s"
+#: ../libpurple/protocols/novell/novell.c:253
#, c-format
msgid "Unable to send message. Could not get details for user (%s)."
-msgstr ""
-"Nie można wysłać wiadomości. Nie można uzyskać szczegółów użytkownika (%s)."
+msgstr "Nie można wysłać wiadomości. Nie można uzyskać szczegółów użytkownika (%s)."
+#: ../libpurple/protocols/novell/novell.c:403
#, c-format
msgid "Unable to add %s to your buddy list (%s)."
msgstr "Nie można dodać użytkownika %s do listy znajomych (%s)."
#. TODO: Improve this! message to who or for what conference?
+#: ../libpurple/protocols/novell/novell.c:430
#, c-format
msgid "Unable to send message (%s)."
msgstr "Nie można wysłać wiadomości (%s)."
+#: ../libpurple/protocols/novell/novell.c:502
+#: ../libpurple/protocols/novell/novell.c:1011
#, c-format
msgid "Unable to invite user (%s)."
msgstr "Nie można zaprosić użytkownika (%s)."
+#: ../libpurple/protocols/novell/novell.c:542
#, c-format
msgid "Unable to send message to %s. Could not create the conference (%s)."
-msgstr ""
-"Nie można wysłać wiadomości do %s. Nie można utworzyć konferencji (%s)."
+msgstr "Nie można wysłać wiadomości do %s. Nie można utworzyć konferencji (%s)."
+#: ../libpurple/protocols/novell/novell.c:547
#, c-format
msgid "Unable to send message. Could not create the conference (%s)."
msgstr "Nie można wysłać wiadomości. Nie można utworzyć konferencji (%s)."
+#: ../libpurple/protocols/novell/novell.c:595
#, c-format
msgid ""
"Unable to move user %s to folder %s in the server side list. Error while "
"creating folder (%s)."
-msgstr ""
-"Nie można przenieść użytkownika %s do katalogu %s na liście na serwerze. "
-"Błąd podczas tworzenia katalogu (%s)."
+msgstr "Nie można przenieść użytkownika %s do katalogu %s na liście na serwerze. Błąd podczas tworzenia katalogu (%s)."
+#: ../libpurple/protocols/novell/novell.c:644
#, c-format
msgid ""
"Unable to add %s to your buddy list. Error creating folder in server side "
"list (%s)."
-msgstr ""
-"Nie można dodać użytkownika %s do listy znajomych. Błąd podczas tworzenia "
-"katalogu na liście na serwerze (%s)."
+msgstr "Nie można dodać użytkownika %s do listy znajomych. Błąd podczas tworzenia katalogu na liście na serwerze (%s)."
+#: ../libpurple/protocols/novell/novell.c:718
#, c-format
msgid "Could not get details for user %s (%s)."
msgstr "Nie można uzyskać szczegółów użytkownika %s (%s)."
+#: ../libpurple/protocols/novell/novell.c:767
+#: ../libpurple/protocols/novell/novell.c:920
#, c-format
msgid "Unable to add user to privacy list (%s)."
msgstr "Nie można dodać użytkownika do prywatnej listy (%s)."
+#: ../libpurple/protocols/novell/novell.c:817
#, c-format
msgid "Unable to add %s to deny list (%s)."
msgstr "Nie można dodać użytkownika %s do listy odrzucanych (%s)."
+#: ../libpurple/protocols/novell/novell.c:873
#, c-format
msgid "Unable to add %s to permit list (%s)."
msgstr "Nie można dodać użytkownika %s do listy dopuszczonych (%s)."
+#: ../libpurple/protocols/novell/novell.c:943
#, c-format
msgid "Unable to remove %s from privacy list (%s)."
msgstr "Nie można usunąć użytkownika %s z listy prywatnej (%s)."
+#: ../libpurple/protocols/novell/novell.c:967
+#: ../libpurple/protocols/novell/novell.c:1688
#, c-format
msgid "Unable to change server side privacy settings (%s)."
msgstr "Nie można zmienić ustawień prywatności na serwerze (%s)."
+#: ../libpurple/protocols/novell/novell.c:1039
#, c-format
msgid "Unable to create conference (%s)."
msgstr "Nie można utworzyć konferencji (%s)."
+#: ../libpurple/protocols/novell/novell.c:1151
+#: ../libpurple/protocols/novell/novell.c:1735
msgid "Error communicating with server. Closing connection."
msgstr "Błąd podczas komunikowania się z serwerem. Zamykanie połączenia."
+#: ../libpurple/protocols/novell/novell.c:1507
msgid "Telephone Number"
msgstr "Numer telefonu"
+#: ../libpurple/protocols/novell/novell.c:1513
msgid "Personal Title"
msgstr "Tytuł osoby"
+#: ../libpurple/protocols/novell/novell.c:1517
msgid "Mailstop"
msgstr "Przegródka pocztowa"
+#: ../libpurple/protocols/novell/novell.c:1533
+#: ../libpurple/protocols/sametime/sametime.c:4112
msgid "User ID"
msgstr "Identyfikator użytkownika"
-#. tag = _("DN");
-#. value = nm_user_record_get_dn(user_record);
-#. if (value) {
-#. purple_notify_user_info_add_pair(user_info, tag, value);
-#. }
-#.
+#: ../libpurple/protocols/novell/novell.c:1542
+msgid "DN"
+msgstr "DN"
+
+#: ../libpurple/protocols/novell/novell.c:1551
msgid "Full name"
msgstr "Imię i nazwisko"
+#: ../libpurple/protocols/novell/novell.c:1678
#, c-format
msgid "GroupWise Conference %d"
msgstr "Konferencja GroupWise %d"
+#: ../libpurple/protocols/novell/novell.c:1762
msgid "Authenticating..."
msgstr "Uwierzytelnianie..."
+#: ../libpurple/protocols/novell/novell.c:1779
msgid "Waiting for response..."
msgstr "Oczekiwanie na odpowiedź..."
+#: ../libpurple/protocols/novell/novell.c:1914
#, c-format
msgid "%s has been invited to this conversation."
msgstr "Użytkownik %s został zaproszony do tej rozmowy."
+#: ../libpurple/protocols/novell/novell.c:1941
msgid "Invitation to Conversation"
msgstr "Zaproszenie do rozmowy"
+#: ../libpurple/protocols/novell/novell.c:1942
#, c-format
msgid ""
"Invitation from: %s\n"
"\n"
"Sent: %s"
-msgstr ""
-"Zaproszenie od: %s\n"
-"\n"
-"Wysłane: %s"
+msgstr "Zaproszenie od: %s\n\nWysłane: %s"
+#: ../libpurple/protocols/novell/novell.c:1944
msgid "Would you like to join the conversation?"
msgstr "Dołączyć do rozmowy?"
+#: ../libpurple/protocols/novell/novell.c:2115
#, c-format
msgid ""
"%s appears to be offline and did not receive the message that you just sent."
-msgstr ""
-"%s jest w trybie offline i nie odebrał wiadomości, którą właśnie wysłano."
+msgstr "%s jest w trybie offline i nie odebrał wiadomości, którą właśnie wysłano."
+#: ../libpurple/protocols/novell/novell.c:2216
msgid ""
-"Unable to connect to server. Please enter the address of the server to which "
-"you wish to connect."
-msgstr ""
-"Nie można połączyć się z serwerem. Proszę podać adres serwera, z którym się "
-"połączyć."
+"Unable to connect to server. Please enter the address of the server to which"
+" you wish to connect."
+msgstr "Nie można połączyć się z serwerem. Proszę podać adres serwera, z którym się połączyć."
+#: ../libpurple/protocols/novell/novell.c:2554
msgid "This conference has been closed. No more messages can be sent."
msgstr "Ta konferencja została zamknięta. Nie można wysyłać na nią wiadomości."
@@ -7095,238 +9616,290 @@ msgstr "Ta konferencja została zamknięta. Nie można wysyłać na nią wiadomo
#. *< version
#. * summary
#. * description
+#: ../libpurple/protocols/novell/novell.c:3591
+#: ../libpurple/protocols/novell/novell.c:3593
msgid "Novell GroupWise Messenger Protocol Plugin"
msgstr "Wtyczka protokołu Novell GroupWise Messenger"
+#: ../libpurple/protocols/novell/novell.c:3618
msgid "Server address"
msgstr "Adres serwera"
+#: ../libpurple/protocols/novell/novell.c:3622
msgid "Server port"
msgstr "Port serwera"
+#: ../libpurple/protocols/oscar/authorization.c:51
msgid "Please authorize me so I can add you to my buddy list."
msgstr "Proszę o upoważnienie, aby dodać użytkownika do listy znajomych."
+#: ../libpurple/protocols/oscar/authorization.c:88
+#: ../libpurple/protocols/oscar/oscar.c:1756
+#: ../libpurple/protocols/oscar/oscar.c:4191 ../pidgin/gtkaccount.c:2770
+#: ../pidgin/gtkaccount.c:2812
msgid "No reason given."
msgstr "Nie podano powodu."
-msgid "Authorization Denied Message:"
-msgstr "Wiadomość odmowy upoważnienia:"
-
-#. *
-#. * A wrapper for purple_request_action() that uses @c OK and @c Cancel buttons.
-#.
-msgid "_OK"
-msgstr "_OK"
-
+#: ../libpurple/protocols/oscar/clientlogin.c:117
#, c-format
msgid "Received unexpected response from %s: %s"
msgstr "Odebrano nieoczekiwaną odpowiedź z %s: %s"
+#: ../libpurple/protocols/oscar/clientlogin.c:119
#, c-format
msgid "Received unexpected response from %s"
msgstr "Odebrano nieoczekiwaną odpowiedź z adresu %s"
+#: ../libpurple/protocols/oscar/clientlogin.c:240
msgid ""
"You have been connecting and disconnecting too frequently. Wait ten minutes "
"and try again. If you continue to try, you will need to wait even longer."
-msgstr ""
-"Za częste próby połączenia i rozłączenia. Proszę poczekać dziesięć minut i "
-"spróbować ponownie. Jeśli próba zostanie podjęta teraz, czas oczekiwania "
-"jeszcze się wydłuży."
+msgstr "Za częste próby połączenia i rozłączenia. Proszę poczekać dziesięć minut i spróbować ponownie. Jeśli próba zostanie podjęta teraz, czas oczekiwania jeszcze się wydłuży."
+#: ../libpurple/protocols/oscar/clientlogin.c:286
+#: ../libpurple/protocols/oscar/oscar.c:1090
msgid ""
"You required encryption in your account settings, but one of the servers "
"doesn't support it."
-msgstr ""
-"Szyfrowanie jest wymagane w ustawieniach konta, ale jeden z serwerów go nie "
-"obsługuje."
+msgstr "Szyfrowanie jest wymagane w ustawieniach konta, ale jeden z serwerów go nie obsługuje."
#. Note to translators: The first %s is a URL, the second is an
#. error message.
+#: ../libpurple/protocols/oscar/clientlogin.c:343
+#: ../libpurple/protocols/oscar/clientlogin.c:579
#, c-format
msgid "Error requesting %s: %s"
msgstr "Błąd podczas żądania adresu %s: %s"
-msgid "The server returned an empty response"
-msgstr "Serwer zwrócił pustą odpowiedź"
-
+#: ../libpurple/protocols/oscar/clientlogin.c:495
msgid ""
"Server requested that you fill out a CAPTCHA in order to sign in, but this "
"client does not currently support CAPTCHAs."
-msgstr ""
-"Serwer zażądał wypełnienia pola CAPTCHA przed zalogowaniem, ale ten klient "
-"obecnie ich nie obsługuje."
+msgstr "Serwer zażądał wypełnienia pola CAPTCHA przed zalogowaniem, ale ten klient obecnie ich nie obsługuje."
+#: ../libpurple/protocols/oscar/clientlogin.c:500
msgid "AOL does not allow your screen name to authenticate here"
msgstr "AOL nie umożliwia uwierzytelnienia nazwy użytkownika w tym miejscu"
+#: ../libpurple/protocols/oscar/encoding.c:133
msgid ""
-"(There was an error receiving this message. The buddy you are speaking with "
-"is probably using a different encoding than expected. If you know what "
-"encoding he is using, you can specify it in the advanced account options for "
-"your AIM/ICQ account.)"
-msgstr ""
-"(Prawdopodobnie wystąpił błąd podczas pobierania tej wiadomości. Znajomy "
-"prawdopodobnie używa innego kodowania, niż jest oczekiwane. Jeśli wiadomo, "
-"jakiego kodowania używa, można je podać w zaawansowanych opcjach konta AIM/"
-"ICQ)."
+"(There was an error receiving this message. The buddy you are speaking with"
+" is probably using a different encoding than expected. If you know what "
+"encoding he is using, you can specify it in the advanced account options for"
+" your AIM/ICQ account.)"
+msgstr "(Prawdopodobnie wystąpił błąd podczas pobierania tej wiadomości. Znajomy prawdopodobnie używa innego kodowania, niż jest oczekiwane. Jeśli wiadomo, jakiego kodowania używa, można je podać w zaawansowanych opcjach konta AIM/ICQ)."
+#: ../libpurple/protocols/oscar/encoding.c:249
#, c-format
msgid ""
"(There was an error receiving this message. Either you and %s have "
"different encodings selected, or %s has a buggy client.)"
-msgstr ""
-"(Wystąpił błąd podczas pobierania tej wiadomości. Użytkownik i znajomy %s "
-"posiadają ustawione inne kodowania lub klient znajomego %s posiada błędy)."
+msgstr "(Wystąpił błąd podczas pobierania tej wiadomości. Użytkownik i znajomy %s posiadają ustawione inne kodowania lub klient znajomego %s posiada błędy)."
-msgid "Could not join chat room"
-msgstr "Nie można dołączyć do pokoju konferencji"
+#: ../libpurple/protocols/oscar/family_auth.c:524
+msgid "Received unexpected response from server"
+msgstr "Otrzymano nieoczekiwaną odpowiedź od serwera"
+#: ../libpurple/protocols/oscar/family_auth.c:548
+msgid "Password sent"
+msgstr "Wysłano hasło"
+
+#: ../libpurple/protocols/oscar/family_chatnav.c:66
msgid "Invalid chat room name"
msgstr "Nieprawidłowa nazwa pokoju konferencji"
+#: ../libpurple/protocols/oscar/family_icbm.c:54
+#: ../libpurple/protocols/oscar/util.c:35
msgid "Invalid error"
msgstr "Nieprawidłowy błąd"
+#: ../libpurple/protocols/oscar/family_icbm.c:56
msgid "Cannot receive IM due to parental controls"
msgstr "Nie można odebrać wiadomości z powodu kontroli rodzicielskiej"
+#: ../libpurple/protocols/oscar/family_icbm.c:57
msgid "Cannot send SMS without accepting terms"
msgstr "Nie można wysłać wiadomości SMS bez zaakceptowania warunków"
+#: ../libpurple/protocols/oscar/family_icbm.c:58
msgid "Cannot send SMS"
msgstr "Nie można wysłać wiadomości SMS"
#. SMS_WITHOUT_DISCLAIMER is weird
+#: ../libpurple/protocols/oscar/family_icbm.c:59
msgid "Cannot send SMS to this country"
msgstr "Nie można wysłać wiadomości SMS do tego kraju"
#. Undocumented
+#: ../libpurple/protocols/oscar/family_icbm.c:62
msgid "Cannot send SMS to unknown country"
msgstr "Nie można wysłać wiadomości SMS do nieznanego kraju"
+#: ../libpurple/protocols/oscar/family_icbm.c:63
msgid "Bot accounts cannot initiate IMs"
msgstr "Konta botów nie mogą inicjować wiadomości"
+#: ../libpurple/protocols/oscar/family_icbm.c:64
msgid "Bot account cannot IM this user"
msgstr "Konto bota nie może wysłać wiadomości do tego użytkownika"
+#: ../libpurple/protocols/oscar/family_icbm.c:65
msgid "Bot account reached IM limit"
msgstr "Konto bota osiągnęło ograniczenie liczby wiadomości"
+#: ../libpurple/protocols/oscar/family_icbm.c:66
msgid "Bot account reached daily IM limit"
msgstr "Konto bota osiągnęło dzienne ograniczenie liczby wiadomości"
+#: ../libpurple/protocols/oscar/family_icbm.c:67
msgid "Bot account reached monthly IM limit"
msgstr "Konto bota osiągnęło miesięczne ograniczenie liczby wiadomości"
+#: ../libpurple/protocols/oscar/family_icbm.c:68
msgid "Unable to receive offline messages"
msgstr "Nie można odebrać wiadomości w trybie offline"
+#: ../libpurple/protocols/oscar/family_icbm.c:69
msgid "Offline message store full"
msgstr "Przechowalnia wiadomości w trybie offline jest pełna"
+#: ../libpurple/protocols/oscar/family_icbm.c:168
#, c-format
msgid "Unable to send message: %s (%s)"
msgstr "Nie można wysłać wiadomości: %s (%s)."
+#: ../libpurple/protocols/oscar/family_icbm.c:171
#, c-format
msgid "Unable to send message: %s"
msgstr "Nie można wysłać wiadomości: %s"
+#: ../libpurple/protocols/oscar/family_icbm.c:176
#, c-format
msgid "Unable to send message to %s: %s (%s)"
msgstr "Nie można wysłać wiadomości do %s: %s (%s)"
+#: ../libpurple/protocols/oscar/family_icbm.c:180
#, c-format
msgid "Unable to send message to %s: %s"
msgstr "Nie można wysłać wiadomości do %s: %s"
+#: ../libpurple/protocols/oscar/family_locate.c:405
msgid "Thinking"
msgstr "Myśli"
+#: ../libpurple/protocols/oscar/family_locate.c:407
msgid "Shopping"
msgstr "Robi zakupy"
+#: ../libpurple/protocols/oscar/family_locate.c:411
msgid "Questioning"
msgstr "Badawczy"
+#: ../libpurple/protocols/oscar/family_locate.c:413
msgid "Eating"
msgstr "Je"
+#: ../libpurple/protocols/oscar/family_locate.c:414
msgid "Watching a movie"
msgstr "Ogląda film"
+#: ../libpurple/protocols/oscar/family_locate.c:416 ../pidgin/gtkconv.c:7588
+#: ../pidgin/gtkstatusbox.c:669
msgid "Typing"
msgstr "Pisze"
+#: ../libpurple/protocols/oscar/family_locate.c:417
msgid "At the office"
msgstr "W biurze"
+#: ../libpurple/protocols/oscar/family_locate.c:418
msgid "Taking a bath"
msgstr "Bierze kąpiel"
+#: ../libpurple/protocols/oscar/family_locate.c:419
msgid "Watching TV"
msgstr "Ogląda telewizję"
+#: ../libpurple/protocols/oscar/family_locate.c:420
msgid "Having fun"
msgstr "Bawi się"
+#: ../libpurple/protocols/oscar/family_locate.c:421
msgid "Sleeping"
msgstr "Śpi"
+#: ../libpurple/protocols/oscar/family_locate.c:422
msgid "Using a PDA"
msgstr "Używa palmtopa"
+#: ../libpurple/protocols/oscar/family_locate.c:426
msgid "Meeting friends"
msgstr "Spotyka się z przyjaciółmi"
+#: ../libpurple/protocols/oscar/family_locate.c:427
msgid "On the phone"
msgstr "Przy telefonie"
+#: ../libpurple/protocols/oscar/family_locate.c:428
msgid "Surfing"
msgstr "Serfuje"
#. "I am mobile." / "John is mobile."
+#: ../libpurple/protocols/oscar/family_locate.c:430 ../libpurple/status.c:143
msgid "Mobile"
msgstr "Ruchliwy"
+#: ../libpurple/protocols/oscar/family_locate.c:431
msgid "Searching the web"
msgstr "Szuka w sieci"
+#: ../libpurple/protocols/oscar/family_locate.c:432
msgid "At a party"
msgstr "Na imprezie"
+#: ../libpurple/protocols/oscar/family_locate.c:433
msgid "Having Coffee"
msgstr "Na kawie"
#. Playing video games
+#: ../libpurple/protocols/oscar/family_locate.c:435
msgid "Gaming"
msgstr "Gra"
+#: ../libpurple/protocols/oscar/family_locate.c:436
msgid "Browsing the web"
msgstr "Przegląda sieć"
+#: ../libpurple/protocols/oscar/family_locate.c:437
msgid "Smoking"
msgstr "Pali"
+#: ../libpurple/protocols/oscar/family_locate.c:438
msgid "Writing"
msgstr "Pisze"
#. Drinking [Alcohol]
+#: ../libpurple/protocols/oscar/family_locate.c:440
msgid "Drinking"
msgstr "Pije"
+#: ../libpurple/protocols/oscar/family_locate.c:441 ../libpurple/status.c:144
msgid "Listening to music"
msgstr "Słucha muzyki"
+#: ../libpurple/protocols/oscar/family_locate.c:442
msgid "Studying"
msgstr "Uczy się"
+#: ../libpurple/protocols/oscar/family_locate.c:444
msgid "In the restroom"
msgstr "W łazience"
+#: ../libpurple/protocols/oscar/flap_connection.c:473
+#: ../libpurple/proxy.c:1539 ../libpurple/proxy.c:1569
+#: ../libpurple/proxy.c:1649 ../libpurple/proxy.c:1718
+#: ../libpurple/proxy.c:1728 ../libpurple/proxy.c:1824
+#: ../libpurple/proxy.c:1963
msgid "Received invalid data on connection with server"
msgstr "Odebrano nieprawidłowe dane na połączeniu z serwerem"
@@ -7340,9 +9913,12 @@ msgstr "Odebrano nieprawidłowe dane na połączeniu z serwerem"
#. *< version
#. * summary
#. * description
+#: ../libpurple/protocols/oscar/libaim.c:120
+#: ../libpurple/protocols/oscar/libaim.c:122
msgid "AIM Protocol Plugin"
msgstr "Wtyczka protokołu AIM"
+#: ../libpurple/protocols/oscar/libicq.c:35
msgid "ICQ UIN..."
msgstr "Nazwa użytkownika ICQ..."
@@ -7356,255 +9932,285 @@ msgstr "Nazwa użytkownika ICQ..."
#. *< version
#. * summary
#. * description
+#: ../libpurple/protocols/oscar/libicq.c:136
+#: ../libpurple/protocols/oscar/libicq.c:138
msgid "ICQ Protocol Plugin"
msgstr "Wtyczka protokołu ICQ"
+#: ../libpurple/protocols/oscar/libicq.c:165
+#: ../libpurple/protocols/yahoo/libyahoo.c:318
+#: ../libpurple/protocols/yahoo/libyahoojp.c:216
+#: ../libpurple/protocols/zephyr/zephyr.c:3030
msgid "Encoding"
msgstr "Kodowanie"
+#: ../libpurple/protocols/oscar/odc.c:43
msgid "The remote user has closed the connection."
msgstr "Zdalny użytkownik zamknął połączenie."
+#: ../libpurple/protocols/oscar/odc.c:45
msgid "The remote user has declined your request."
msgstr "Zdalny użytkownik odrzucił prośbę."
+#: ../libpurple/protocols/oscar/odc.c:47
#, c-format
msgid "Lost connection with the remote user:<br>%s"
msgstr "Utracono połączenie ze zdalnym użytkownikiem:<br>%s"
+#: ../libpurple/protocols/oscar/odc.c:50
msgid "Received invalid data on connection with remote user."
msgstr "Odebrano nieprawidłowe dane na połączeniu ze zdalnym użytkownikiem."
+#: ../libpurple/protocols/oscar/odc.c:52
msgid "Unable to establish a connection with the remote user."
msgstr "Nie można nawiązać połączenia ze zdalnym użytkownikiem."
+#: ../libpurple/protocols/oscar/odc.c:562
msgid "Direct IM established"
msgstr "Nawiązano bezpośrednie połączenie"
+#: ../libpurple/protocols/oscar/odc.c:600
#, c-format
msgid ""
-"%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"
-msgstr ""
-"Użytkownik %s próbował wysłać plik %s, ale tylko pliki do %s są dozwolone "
-"przez bezpośrednie połączenie. Proszę spróbować przesyłania plików.\n"
+"%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"
+msgstr "Użytkownik %s próbował wysłać plik %s, ale tylko pliki do %s są dozwolone przez bezpośrednie połączenie. Proszę spróbować przesyłania plików.\n"
+#: ../libpurple/protocols/oscar/oft.c:666
#, c-format
msgid "File %s is %s, which is larger than the maximum size of %s."
msgstr "Plik %s ma %s, czyli więcej niż maksymalny rozmiar %s."
+#: ../libpurple/protocols/oscar/oscar.c:150
+#: ../libpurple/protocols/oscar/oscar.c:4592
msgid "Free For Chat"
msgstr "Chce pogadać"
+#: ../libpurple/protocols/oscar/oscar.c:154
+#: ../libpurple/protocols/oscar/oscar.c:4674
msgid "Not Available"
msgstr "Niedostępny"
+#: ../libpurple/protocols/oscar/oscar.c:156
+#: ../libpurple/protocols/oscar/oscar.c:4660
msgid "Occupied"
msgstr "Zajęty"
+#: ../libpurple/protocols/oscar/oscar.c:160
msgid "Web Aware"
msgstr "Na stronie WWW"
+#: ../libpurple/protocols/oscar/oscar.c:162
+#: ../libpurple/protocols/yahoo/libymsg.c:3960 ../libpurple/status.c:140
+#: ../pidgin/gtkdocklet.c:594 ../pidgin/gtkstatusbox.c:1054
msgid "Invisible"
msgstr "Niewidoczny"
+#: ../libpurple/protocols/oscar/oscar.c:164
+#: ../libpurple/protocols/oscar/oscar.c:4600
msgid "Evil"
msgstr "Zły"
+#: ../libpurple/protocols/oscar/oscar.c:166
+#: ../libpurple/protocols/oscar/oscar.c:4608
msgid "Depression"
msgstr "Depresja"
+#: ../libpurple/protocols/oscar/oscar.c:168
+#: ../libpurple/protocols/oscar/oscar.c:4616
msgid "At home"
msgstr "W domu"
+#: ../libpurple/protocols/oscar/oscar.c:170
+#: ../libpurple/protocols/oscar/oscar.c:4624
msgid "At work"
msgstr "W pracy"
+#: ../libpurple/protocols/oscar/oscar.c:172
msgid "At lunch"
msgstr "Na obiedzie"
+#: ../libpurple/protocols/oscar/oscar.c:302
#, c-format
msgid "Unable to connect to authentication server: %s"
msgstr "Nie można połączyć się z serwerem uwierzytelniania: %s"
+#: ../libpurple/protocols/oscar/oscar.c:310
#, c-format
msgid "Unable to connect to BOS server: %s"
msgstr "Nie można połączyć się z serwerem BOS: %s"
+#: ../libpurple/protocols/oscar/oscar.c:369
msgid "Username sent"
msgstr "Wysłano nazwę użytkownika"
+#: ../libpurple/protocols/oscar/oscar.c:373
msgid "Connection established, cookie sent"
msgstr "Nawiązano połączenie, wysłano ciasteczko"
#. TODO: Don't call this with ssi
+#: ../libpurple/protocols/oscar/oscar.c:454
msgid "Finalizing connection"
msgstr "Dokańczanie połączenia"
+#: ../libpurple/protocols/oscar/oscar.c:715
#, c-format
msgid ""
"Unable to sign on as %s because the username is invalid. Usernames must be "
"a valid email address, or start with a letter and contain only letters, "
"numbers and spaces, or contain only numbers."
-msgstr ""
-"Nie można zalogować jako %s, ponieważ nazwa użytkownika jest nieprawidłowa. "
-"Nazwy użytkowników muszą być prawidłowymi adresami e-mail lub zaczynać się "
-"od litery i zawierać tylko litery, liczby i spacje lub zawierać tylko liczby."
+msgstr "Nie można zalogować jako %s, ponieważ nazwa użytkownika jest nieprawidłowa. Nazwy użytkowników muszą być prawidłowymi adresami e-mail lub zaczynać się od litery i zawierać tylko litery, liczby i spacje lub zawierać tylko liczby."
+#: ../libpurple/protocols/oscar/oscar.c:742
msgid ""
"You required encryption in your account settings, but encryption is not "
"supported by your system."
-msgstr ""
-"Wymagane jest szyfrowanie w ustawieniach konta, ale nie jest ono obsługiwane "
-"przez system."
-
-#, c-format
-msgid "You may be disconnected shortly. If so, check %s for updates."
-msgstr ""
-"Połączenie może zostać wkrótce zerwane. Jeśli tak się stanie, proszę "
-"odwiedzić %s, aby poznać najnowsze informacje na ten temat."
-
-msgid "Unable to get a valid AIM login hash."
-msgstr "Nie można uzyskać prawidłowej funkcji mieszającej logowania AIM."
-
-msgid "Unable to get a valid login hash."
-msgstr "Nie można uzyskać prawidłowej funkcji mieszającej logowania."
+msgstr "Wymagane jest szyfrowanie w ustawieniach konta, ale nie jest ono obsługiwane przez system."
+#: ../libpurple/protocols/oscar/oscar.c:881
+#: ../libpurple/protocols/oscar/oscar.c:1000
msgid "Received authorization"
msgstr "Otrzymano upoważnienie"
#. Unregistered username
#. the username does not exist
+#: ../libpurple/protocols/oscar/oscar.c:914
+#: ../libpurple/protocols/yahoo/libymsg.c:2063
+#: ../libpurple/protocols/yahoo/libymsg.c:2365
msgid "Username does not exist"
msgstr "Nazwa użytkownika nie istnieje"
#. Suspended account
+#: ../libpurple/protocols/oscar/oscar.c:924
msgid "Your account is currently suspended"
msgstr "Konto jest obecnie zawieszone"
#. service temporarily unavailable
+#: ../libpurple/protocols/oscar/oscar.c:929
msgid "The AOL Instant Messenger service is temporarily unavailable."
msgstr "Usługa AOL Instant Messenger jest tymczasowo niedostępna."
#. username connecting too frequently
+#: ../libpurple/protocols/oscar/oscar.c:933
msgid ""
-"Your username has been connecting and disconnecting too frequently. Wait ten "
-"minutes and try again. If you continue to try, you will need to wait even "
+"Your username has been connecting and disconnecting too frequently. Wait ten"
+" minutes and try again. If you continue to try, you will need to wait even "
"longer."
-msgstr ""
-"Za częste próby połączenia i rozłączenia za pomocą nazwy użytkownika. Proszę "
-"poczekać dziesięć minut i spróbować ponownie. Jeśli próba zostanie podjęta "
-"teraz, czas oczekiwania jeszcze się wydłuży."
+msgstr "Za częste próby połączenia i rozłączenia za pomocą nazwy użytkownika. Proszę poczekać dziesięć minut i spróbować ponownie. Jeśli próba zostanie podjęta teraz, czas oczekiwania jeszcze się wydłuży."
#. client too old
+#: ../libpurple/protocols/oscar/oscar.c:938
#, c-format
msgid "The client version you are using is too old. Please upgrade at %s"
-msgstr ""
-"Używana wersja klienta jest za stara. Proszę zaktualizować do nowej wersji "
-"na stronie %s"
+msgstr "Używana wersja klienta jest za stara. Proszę zaktualizować do nowej wersji na stronie %s"
#. IP address connecting too frequently
+#: ../libpurple/protocols/oscar/oscar.c:945
msgid ""
-"Your IP address has been connecting and disconnecting too frequently. Wait a "
-"minute and try again. If you continue to try, you will need to wait even "
+"Your IP address has been connecting and disconnecting too frequently. Wait a"
+" minute and try again. If you continue to try, you will need to wait even "
"longer."
-msgstr ""
-"Za częste próby połączenia i rozłączenia za pomocą adresu IP. Proszę "
-"poczekać minutę i spróbować ponownie. Jeśli próba zostanie podjęta teraz, "
-"czas oczekiwania jeszcze się wydłuży."
+msgstr "Za częste próby połączenia i rozłączenia za pomocą adresu IP. Proszę poczekać minutę i spróbować ponownie. Jeśli próba zostanie podjęta teraz, czas oczekiwania jeszcze się wydłuży."
+#: ../libpurple/protocols/oscar/oscar.c:1028
msgid "The SecurID key entered is invalid"
msgstr "Podany klucz SecurID jest nieprawidłowy"
+#: ../libpurple/protocols/oscar/oscar.c:1044
msgid "Enter SecurID"
msgstr "Proszę podać klucz SecurID"
+#: ../libpurple/protocols/oscar/oscar.c:1045
msgid "Enter the 6 digit number from the digital display."
msgstr "Proszę podać sześciocyfrowy numer z ekranu cyfrowego."
-msgid "Password sent"
-msgstr "Wysłano hasło"
+#. *
+#. * purple_request_ok_cancel:
+#. *
+#. * A wrapper for purple_request_action() that uses <literal>OK</literal> and
+#. * <literal>Cancel</literal> buttons.
+#: ../libpurple/protocols/oscar/oscar.c:1047
+#: ../libpurple/protocols/oscar/oscar.c:4757
+#: ../libpurple/protocols/oscar/oscar.c:5097 ../libpurple/request.h:2179
+#: ../pidgin/gtkrequest.c:339
+msgid "_OK"
+msgstr "_OK"
+#: ../libpurple/protocols/oscar/oscar.c:1142
msgid "Unable to initialize connection"
msgstr "Nie można zainicjować połączenia"
+#: ../libpurple/protocols/oscar/oscar.c:1756
#, c-format
msgid ""
-"The user %u has denied your request to add them to your buddy list for the "
-"following reason:\n"
-"%s"
-msgstr ""
-"Użytkownik %u odrzucił prośbę o pozwolenie na dodanie go do listy znajomych "
-"z następującego powodu:\n"
+"The user %u has denied your request to add them to your buddy list for the following reason:\n"
"%s"
+msgstr "Użytkownik %u odrzucił prośbę o pozwolenie na dodanie go do listy znajomych z następującego powodu:\n%s"
+#: ../libpurple/protocols/oscar/oscar.c:1757
msgid "ICQ authorization denied."
msgstr "Odmowa upoważnienia ICQ."
#. Someone has granted you authorization
+#: ../libpurple/protocols/oscar/oscar.c:1763
#, c-format
msgid "The user %u has granted your request to add them to your buddy list."
msgstr "Użytkownik %u wyraził zgodę na dodanie go do listy znajomych."
+#: ../libpurple/protocols/oscar/oscar.c:1770
#, c-format
msgid ""
"You have received a special message\n"
"\n"
"From: %s [%s]\n"
"%s"
-msgstr ""
-"Odebrano wiadomość specjalną\n"
-"\n"
-"Od: %s [%s]\n"
-"%s"
+msgstr "Odebrano wiadomość specjalną\n\nOd: %s [%s]\n%s"
+#: ../libpurple/protocols/oscar/oscar.c:1778
#, c-format
msgid ""
"You have received an ICQ page\n"
"\n"
"From: %s [%s]\n"
"%s"
-msgstr ""
-"Odebrano stronę ICQ\n"
-"\n"
-"Od: %s [%s]\n"
-"%s"
+msgstr "Odebrano stronę ICQ\n\nOd: %s [%s]\n%s"
+#: ../libpurple/protocols/oscar/oscar.c:1786
#, c-format
msgid ""
"You have received an ICQ email from %s [%s]\n"
"\n"
"Message is:\n"
"%s"
-msgstr ""
-"Odebrano wiadomość e-mail ICQ od %s [%s]\n"
-"\n"
-"Treść wiadomości:\n"
-"%s"
+msgstr "Odebrano wiadomość e-mail ICQ od %s [%s]\n\nTreść wiadomości:\n%s"
+#: ../libpurple/protocols/oscar/oscar.c:1820
#, c-format
msgid "ICQ user %u has sent you a buddy: %s (%s)"
msgstr "Użytkownik ICQ %u wysłał znajomego: %s (%s)"
+#: ../libpurple/protocols/oscar/oscar.c:1828
msgid "Do you want to add this buddy to your buddy list?"
msgstr "Dodać tego znajomego do listy?"
+#: ../libpurple/protocols/oscar/oscar.c:1833 ../pidgin/gtkroomlist.c:322
+#: ../pidgin/plugins/disco/gtkdisco.c:701
msgid "_Add"
msgstr "Dod_aj"
+#: ../libpurple/protocols/oscar/oscar.c:1834
msgid "_Decline"
msgstr "O_drzuć"
+#: ../libpurple/protocols/oscar/oscar.c:1982
#, c-format
msgid "You missed %hu message from %s because it was invalid."
msgid_plural "You missed %hu messages from %s because they were invalid."
-msgstr[0] ""
-"Pominięto %hu wiadomość od użytkownika %s, ponieważ była nieprawidłowa."
-msgstr[1] ""
-"Pominięto %hu wiadomości od użytkownika %s, ponieważ były nieprawidłowe."
-msgstr[2] ""
-"Pominięto %hu wiadomości od użytkownika %s, ponieważ były nieprawidłowe."
+msgstr[0] "Pominięto %hu wiadomość od użytkownika %s, ponieważ była nieprawidłowa."
+msgstr[1] "Pominięto %hu wiadomości od użytkownika %s, ponieważ były nieprawidłowe."
+msgstr[2] "Pominięto %hu wiadomości od użytkownika %s, ponieważ były nieprawidłowe."
+#: ../libpurple/protocols/oscar/oscar.c:1991
#, c-format
msgid "You missed %hu message from %s because it was too large."
msgid_plural "You missed %hu messages from %s because they were too large."
@@ -7612,50 +10218,36 @@ msgstr[0] "Pominięto %hu wiadomość od użytkownika %s, ponieważ była za du
msgstr[1] "Pominięto %hu wiadomości od użytkownika %s, ponieważ były za duże."
msgstr[2] "Pominięto %hu wiadomości od użytkownika %s, ponieważ były za duże."
+#: ../libpurple/protocols/oscar/oscar.c:2000
#, c-format
msgid ""
"You missed %hu message from %s because the rate limit has been exceeded."
msgid_plural ""
"You missed %hu messages from %s because the rate limit has been exceeded."
-msgstr[0] ""
-"Pominięto %hu wiadomość od użytkownika %s, ponieważ przekroczono "
-"ograniczenie prędkości."
-msgstr[1] ""
-"Pominięto %hu wiadomości od użytkownika %s, ponieważ przekroczono "
-"ograniczenie prędkości."
-msgstr[2] ""
-"Pominięto %hu wiadomości od użytkownika %s, ponieważ przekroczono "
-"ograniczenie prędkości."
+msgstr[0] "Pominięto %hu wiadomość od użytkownika %s, ponieważ przekroczono ograniczenie prędkości."
+msgstr[1] "Pominięto %hu wiadomości od użytkownika %s, ponieważ przekroczono ograniczenie prędkości."
+msgstr[2] "Pominięto %hu wiadomości od użytkownika %s, ponieważ przekroczono ograniczenie prędkości."
+#: ../libpurple/protocols/oscar/oscar.c:2009
#, c-format
msgid ""
"You missed %hu message from %s because his/her warning level is too high."
msgid_plural ""
"You missed %hu messages from %s because his/her warning level is too high."
-msgstr[0] ""
-"Pominięto %hu wiadomość od użytkownika %s, ponieważ ma on za wysoki poziom "
-"ostrzeżenia."
-msgstr[1] ""
-"Pominięto %hu wiadomości od użytkownika %s, ponieważ mają one za wysoki "
-"poziom ostrzeżenia."
-msgstr[2] ""
-"Pominięto %hu wiadomości od użytkownika %s, ponieważ mają one za wysoki "
-"poziom ostrzeżenia."
+msgstr[0] "Pominięto %hu wiadomość od użytkownika %s, ponieważ ma on za wysoki poziom ostrzeżenia."
+msgstr[1] "Pominięto %hu wiadomości od użytkownika %s, ponieważ mają one za wysoki poziom ostrzeżenia."
+msgstr[2] "Pominięto %hu wiadomości od użytkownika %s, ponieważ mają one za wysoki poziom ostrzeżenia."
+#: ../libpurple/protocols/oscar/oscar.c:2018
#, c-format
msgid "You missed %hu message from %s because your warning level is too high."
msgid_plural ""
"You missed %hu messages from %s because your warning level is too high."
-msgstr[0] ""
-"Pominięto %hu wiadomość od użytkownika %s z powodu za wysokiego poziomu "
-"ostrzeżenia."
-msgstr[1] ""
-"Pominięto %hu wiadomości od użytkownika %s z powodu za wysokiego poziomu "
-"ostrzeżenia."
-msgstr[2] ""
-"Pominięto %hu wiadomości od użytkownika %s z powodu za wysokiego poziomu "
-"ostrzeżenia."
+msgstr[0] "Pominięto %hu wiadomość od użytkownika %s z powodu za wysokiego poziomu ostrzeżenia."
+msgstr[1] "Pominięto %hu wiadomości od użytkownika %s z powodu za wysokiego poziomu ostrzeżenia."
+msgstr[2] "Pominięto %hu wiadomości od użytkownika %s z powodu za wysokiego poziomu ostrzeżenia."
+#: ../libpurple/protocols/oscar/oscar.c:2027
#, c-format
msgid "You missed %hu message from %s for an unknown reason."
msgid_plural "You missed %hu messages from %s for an unknown reason."
@@ -7663,24 +10255,38 @@ msgstr[0] "Pominięto %hu wiadomość od użytkownika %s z nieznanego powodu."
msgstr[1] "Pominięto %hu wiadomości od użytkownika %s z nieznanego powodu."
msgstr[2] "Pominięto %hu wiadomości od użytkownika %s z nieznanego powodu."
+#: ../libpurple/protocols/oscar/oscar.c:2089
+#: ../libpurple/protocols/oscar/oscar.c:2116
+#: ../libpurple/protocols/oscar/userinfo.c:403
+msgid "UIN"
+msgstr "Identyfikator użytkownika"
+
+#: ../libpurple/protocols/oscar/oscar.c:2237
msgid "Your AIM connection may be lost."
msgstr "Połączenie AIM może zostać utracone."
+#: ../libpurple/protocols/oscar/oscar.c:2576
#, c-format
msgid "You have been disconnected from chat room %s."
msgstr "Rozłączono z pokoju konferencji %s."
+#: ../libpurple/protocols/oscar/oscar.c:2648
msgid "The new formatting is invalid."
msgstr "Nowe formatowanie jest nieprawidłowe."
+#: ../libpurple/protocols/oscar/oscar.c:2649
msgid "Username formatting can change only capitalization and whitespace."
-msgstr ""
-"Formatowanie nazwy użytkownika może zmienić wyłącznie wielkość liter i "
-"spacje."
+msgstr "Formatowanie nazwy użytkownika może zmienić wyłącznie wielkość liter i spacje."
+#: ../libpurple/protocols/oscar/oscar.c:2783
msgid "Pop-Up Message"
msgstr "Wiadomość wyskakująca"
+#: ../libpurple/protocols/oscar/oscar.c:2816
+msgid "Unable to display the search results."
+msgstr "Nie można wyświetlić wyników wyszukiwania."
+
+#: ../libpurple/protocols/oscar/oscar.c:2823
#, c-format
msgid "The following username is associated with %s"
msgid_plural "The following usernames are associated with %s"
@@ -7688,96 +10294,95 @@ msgstr[0] "Następująca nazwa użytkownika jest powiązana z %s"
msgstr[1] "Następujące nazwy użytkowników są powiązane z %s"
msgstr[2] "Następujące nazwy użytkowników są powiązane z %s"
+#: ../libpurple/protocols/oscar/oscar.c:2854
#, c-format
msgid "No results found for email address %s"
msgstr "Nie odnaleziono żadnych wyników dla adresu e-mail %s"
+#: ../libpurple/protocols/oscar/oscar.c:2876
#, c-format
msgid "You should receive an email asking to confirm %s."
-msgstr ""
-"Powinna zostać dostarczona wiadomość e-mail z prośbą o potwierdzenie %s."
+msgstr "Powinna zostać dostarczona wiadomość e-mail z prośbą o potwierdzenie %s."
+#: ../libpurple/protocols/oscar/oscar.c:2878
msgid "Account Confirmation Requested"
msgstr "Zażądano potwierdzenia konta"
+#: ../libpurple/protocols/oscar/oscar.c:2912
#, c-format
msgid ""
"Error 0x%04x: Unable to format username because the requested name differs "
"from the original."
-msgstr ""
-"Błąd 0x%04x: nie można sformatować nazwy użytkownika, ponieważ żądana nazwa "
-"różni się od oryginalnej."
+msgstr "Błąd 0x%04x: nie można sformatować nazwy użytkownika, ponieważ żądana nazwa różni się od oryginalnej."
+#: ../libpurple/protocols/oscar/oscar.c:2914
#, c-format
msgid "Error 0x%04x: Unable to format username because it is invalid."
-msgstr ""
-"Błąd 0x%04x: nie można sformatować nazwy użytkownika, ponieważ jest "
-"nieprawidłowa."
+msgstr "Błąd 0x%04x: nie można sformatować nazwy użytkownika, ponieważ jest nieprawidłowa."
+#: ../libpurple/protocols/oscar/oscar.c:2916
#, c-format
msgid ""
"Error 0x%04x: Unable to format username because the requested name is too "
"long."
-msgstr ""
-"Błąd 0x%04x: nie można sformatować nazwy użytkownika, ponieważ żądana nazwa "
-"jest za długa."
+msgstr "Błąd 0x%04x: nie można sformatować nazwy użytkownika, ponieważ żądana nazwa jest za długa."
+#: ../libpurple/protocols/oscar/oscar.c:2918
#, c-format
msgid ""
"Error 0x%04x: Unable to change email address because there is already a "
"request pending for this username."
-msgstr ""
-"Błąd 0x%04x: nie można zmienić adresu e-mail, ponieważ jest już oczekujące "
-"żądanie dla tej nazwy użytkownika."
+msgstr "Błąd 0x%04x: nie można zmienić adresu e-mail, ponieważ jest już oczekujące żądanie dla tej nazwy użytkownika."
+#: ../libpurple/protocols/oscar/oscar.c:2920
#, c-format
msgid ""
"Error 0x%04x: Unable to change email address because the given address has "
"too many usernames associated with it."
-msgstr ""
-"Błąd 0x%04x: nie można zmienić adresu e-mail, ponieważ podany adres jest "
-"skojarzony ze zbyt dużą ilością użytkowników."
+msgstr "Błąd 0x%04x: nie można zmienić adresu e-mail, ponieważ podany adres jest skojarzony ze zbyt dużą ilością użytkowników."
+#: ../libpurple/protocols/oscar/oscar.c:2922
#, c-format
msgid ""
"Error 0x%04x: Unable to change email address because the given address is "
"invalid."
-msgstr ""
-"Błąd 0x%04x: nie można zmienić adresu e-mail, ponieważ podany adres jest "
-"nieprawidłowy."
+msgstr "Błąd 0x%04x: nie można zmienić adresu e-mail, ponieważ podany adres jest nieprawidłowy."
+#: ../libpurple/protocols/oscar/oscar.c:2924
#, c-format
msgid "Error 0x%04x: Unknown error."
msgstr "Błąd 0x%04x: nieznany błąd."
+#: ../libpurple/protocols/oscar/oscar.c:2925
msgid "Error Changing Account Info"
msgstr "Błąd podczas zmieniania informacji o koncie"
+#: ../libpurple/protocols/oscar/oscar.c:2932
#, c-format
msgid "The email address for %s is %s"
msgstr "Adres e-mail dla %s to %s"
+#: ../libpurple/protocols/oscar/oscar.c:2934
msgid "Account Info"
msgstr "Informacje o koncie"
+#: ../libpurple/protocols/oscar/oscar.c:3132
msgid ""
"Your IM Image was not sent. You must be Direct Connected to send IM Images."
-msgstr ""
-"Obraz komunikatora nie został wysłany. Obrazy mogą być wysyłane tylko za "
-"pomocą bezpośredniego połączenia."
+msgstr "Obraz komunikatora nie został wysłany. Obrazy mogą być wysyłane tylko za pomocą bezpośredniego połączenia."
+#: ../libpurple/protocols/oscar/oscar.c:3359
msgid "Unable to set AIM profile."
msgstr "Nie można ustawić profilu AIM."
+#: ../libpurple/protocols/oscar/oscar.c:3360
msgid ""
"You have probably requested to set your profile before the login procedure "
"completed. Your profile remains unset; try setting it again when you are "
"fully connected."
-msgstr ""
-"Prawdopodobnie zażądano ustawienia profilu przed ukończeniem procedury "
-"logowania. Profil pozostanie nieustawiony; proszę spróbować ustawić go po "
-"ustanowieniu połączenia."
+msgstr "Prawdopodobnie zażądano ustawienia profilu przed ukończeniem procedury logowania. Profil pozostanie nieustawiony; proszę spróbować ustawić go po ustanowieniu połączenia."
+#: ../libpurple/protocols/oscar/oscar.c:3375
#, c-format
msgid ""
"The maximum profile length of %d byte has been exceeded. It has been "
@@ -7785,19 +10390,15 @@ msgid ""
msgid_plural ""
"The maximum profile length of %d bytes has been exceeded. It has been "
"truncated for you."
-msgstr[0] ""
-"Maksymalna długość profilu (%d bajt) została przekroczona. Profil został "
-"skrócony."
-msgstr[1] ""
-"Maksymalna długość profilu (%d bajty) została przekroczona. Profil został "
-"skrócony."
-msgstr[2] ""
-"Maksymalna długość profilu (%d bajtów) została przekroczona. Profil został "
-"skrócony."
+msgstr[0] "Maksymalna długość profilu (%d bajt) została przekroczona. Profil został skrócony."
+msgstr[1] "Maksymalna długość profilu (%d bajty) została przekroczona. Profil został skrócony."
+msgstr[2] "Maksymalna długość profilu (%d bajtów) została przekroczona. Profil został skrócony."
+#: ../libpurple/protocols/oscar/oscar.c:3380
msgid "Profile too long."
msgstr "Profil jest za długi."
+#: ../libpurple/protocols/oscar/oscar.c:3410
#, c-format
msgid ""
"The maximum away message length of %d byte has been exceeded. It has been "
@@ -7805,459 +10406,560 @@ msgid ""
msgid_plural ""
"The maximum away message length of %d bytes has been exceeded. It has been "
"truncated for you."
-msgstr[0] ""
-"Maksymalna długość wiadomości nieobecności (%d bajt) została przekroczona. "
-"Wiadomość została skrócona."
-msgstr[1] ""
-"Maksymalna długość wiadomości nieobecności (%d bajty) została przekroczona. "
-"Wiadomość została skrócona."
-msgstr[2] ""
-"Maksymalna długość wiadomości nieobecności (%d bajtów) została przekroczona. "
-"Wiadomość została skrócona."
+msgstr[0] "Maksymalna długość wiadomości nieobecności (%d bajt) została przekroczona. Wiadomość została skrócona."
+msgstr[1] "Maksymalna długość wiadomości nieobecności (%d bajty) została przekroczona. Wiadomość została skrócona."
+msgstr[2] "Maksymalna długość wiadomości nieobecności (%d bajtów) została przekroczona. Wiadomość została skrócona."
+#: ../libpurple/protocols/oscar/oscar.c:3416
msgid "Away message too long."
msgstr "Za długa wiadomość nieobecności."
+#: ../libpurple/protocols/oscar/oscar.c:3517
#, c-format
msgid ""
"Unable to add the buddy %s because the username is invalid. Usernames must "
"be a valid email address, or start with a letter and contain only letters, "
"numbers and spaces, or contain only numbers."
-msgstr ""
-"Nie można dodać znajomego %s, ponieważ nazwa użytkownika jest nieprawidłowa. "
-"Nazwy użytkowników muszą być prawidłowymi adresami e-mail lub zaczynać się "
-"od litery i zawierać tylko litery, liczby i spacje lub zawierać tylko liczby."
+msgstr "Nie można dodać znajomego %s, ponieważ nazwa użytkownika jest nieprawidłowa. Nazwy użytkowników muszą być prawidłowymi adresami e-mail lub zaczynać się od litery i zawierać tylko litery, liczby i spacje lub zawierać tylko liczby."
+#: ../libpurple/protocols/oscar/oscar.c:3653
msgid "Unable to Retrieve Buddy List"
msgstr "Nie można pobrać listy znajomych"
+#: ../libpurple/protocols/oscar/oscar.c:3654
msgid ""
-"The AIM servers were temporarily unable to send your buddy list. Your buddy "
-"list is not lost, and will probably become available in a few minutes."
-msgstr ""
-"Serwery AIM tymczasowo nie mogły wysłać listy znajomych. Nie została ona "
-"utracona i prawdopodobnie będzie dostępna za kilka minut."
-
-msgid "Orphans"
-msgstr "Osieroceni"
+"The AIM servers were temporarily unable to send your buddy list. Your buddy"
+" list is not lost, and will probably become available in a few minutes."
+msgstr "Serwery AIM tymczasowo nie mogły wysłać listy znajomych. Nie została ona utracona i prawdopodobnie będzie dostępna za kilka minut."
+#: ../libpurple/protocols/oscar/oscar.c:3984
#, c-format
msgid ""
"Unable to add the buddy %s because you have too many buddies in your buddy "
"list. Please remove one and try again."
-msgstr ""
-"Nie można dodać znajomego %s, ponieważ jest za dużo znajomych na liście. "
-"Proszę usunąć kogoś i spróbować ponownie."
+msgstr "Nie można dodać znajomego %s, ponieważ jest za dużo znajomych na liście. Proszę usunąć kogoś i spróbować ponownie."
+#: ../libpurple/protocols/oscar/oscar.c:3984
+#: ../libpurple/protocols/oscar/oscar.c:3999
msgid "(no name)"
msgstr "(bez nazwy)"
+#: ../libpurple/protocols/oscar/oscar.c:3998
#, c-format
msgid "Unable to add the buddy %s for an unknown reason."
msgstr "Nie można dodać znajomego %s z nieznanego powodu."
+#: ../libpurple/protocols/oscar/oscar.c:4115
#, c-format
msgid ""
"The user %s has given you permission to add him or her to your buddy list. "
"Do you want to add this user?"
-msgstr ""
-"Użytkownik %s udzielił pozwolenia na dodanie go do listy znajomych. Dodać go?"
+msgstr "Użytkownik %s udzielił pozwolenia na dodanie go do listy znajomych. Dodać go?"
+#: ../libpurple/protocols/oscar/oscar.c:4123
msgid "Authorization Given"
msgstr "Upoważniono"
#. Granted
+#: ../libpurple/protocols/oscar/oscar.c:4186
#, c-format
msgid "The user %s has granted your request to add them to your buddy list."
msgstr "Użytkownik %s wyraził zgodę na dodanie go do listy znajomych."
+#: ../libpurple/protocols/oscar/oscar.c:4187
msgid "Authorization Granted"
msgstr "Wyrażono zgodę"
#. Denied
+#: ../libpurple/protocols/oscar/oscar.c:4191
#, c-format
msgid ""
-"The user %s has denied your request to add them to your buddy list for the "
-"following reason:\n"
-"%s"
-msgstr ""
-"Użytkownik %s odrzucił prośbę o pozwolenie na dodanie go do listy "
-"użytkowników z następującego powodu:\n"
+"The user %s has denied your request to add them to your buddy list for the following reason:\n"
"%s"
+msgstr "Użytkownik %s odrzucił prośbę o pozwolenie na dodanie go do listy użytkowników z następującego powodu:\n%s"
+#: ../libpurple/protocols/oscar/oscar.c:4192
msgid "Authorization Denied"
msgstr "Odrzucono prośbę o pozwolenie"
+#: ../libpurple/protocols/oscar/oscar.c:4231
msgid "_Exchange:"
msgstr "_Wymiana:"
+#: ../libpurple/protocols/oscar/oscar.c:4348
msgid "Your IM Image was not sent. You cannot send IM Images in AIM chats."
-msgstr ""
-"Obraz komunikatora nie został wysłany. Nie można wysłać obrazów komunikatora "
-"w konferencjach AIM."
+msgstr "Obraz komunikatora nie został wysłany. Nie można wysłać obrazów komunikatora w konferencjach AIM."
+#: ../libpurple/protocols/oscar/oscar.c:4586
msgid "iTunes Music Store Link"
msgstr "Odnośnik do sklepu muzycznego iTunes"
+#: ../libpurple/protocols/oscar/oscar.c:4633
msgid "Lunch"
msgstr "Obiad"
+#: ../libpurple/protocols/oscar/oscar.c:4754
#, c-format
msgid "Buddy Comment for %s"
msgstr "Komentarz o znajomym %s"
+#: ../libpurple/protocols/oscar/oscar.c:4755
msgid "Buddy Comment:"
msgstr "Komentarz o znajomym:"
+#: ../libpurple/protocols/oscar/oscar.c:4804
#, c-format
msgid "You have selected to open a Direct IM connection with %s."
msgstr "Wybrano otwarcie bezpośredniego połączenia z %s."
+#: ../libpurple/protocols/oscar/oscar.c:4808
msgid ""
-"Because this reveals your IP address, it may be considered a security risk. "
-"Do you wish to continue?"
-msgstr ""
-"Ta funkcja ujawnia adres IP, może to stanowić pewne zagrożenie "
-"bezpieczeństwa. Kontynuować?"
+"Because this reveals your IP address, it may be considered a security risk."
+" Do you wish to continue?"
+msgstr "Ta funkcja ujawnia adres IP, może to stanowić pewne zagrożenie bezpieczeństwa. Kontynuować?"
+#: ../libpurple/protocols/oscar/oscar.c:4814
+#: ../libpurple/protocols/oscar/peer.c:1097
msgid "C_onnect"
msgstr "P_ołącz"
+#: ../libpurple/protocols/oscar/oscar.c:4853
msgid "You closed the connection."
msgstr "Zamknięto połączenie."
+#: ../libpurple/protocols/oscar/oscar.c:4912
msgid "Get AIM Info"
msgstr "Pobierz informacje AIM"
#. We only do this if the user is in our buddy list
+#: ../libpurple/protocols/oscar/oscar.c:4921
msgid "Edit Buddy Comment"
msgstr "Zmodyfikuj komentarz o znajomym"
+#: ../libpurple/protocols/oscar/oscar.c:4929
msgid "Get X-Status Msg"
msgstr "Pobierz wiadomość X-Status"
+#: ../libpurple/protocols/oscar/oscar.c:4947
msgid "End Direct IM Session"
msgstr "Zakończ sesję bezpośredniego połączenia"
+#: ../libpurple/protocols/oscar/oscar.c:4953
msgid "Direct IM"
msgstr "Połączenie bezpośrednie"
+#: ../libpurple/protocols/oscar/oscar.c:4971
msgid "Re-request Authorization"
msgstr "Ponownie poproś o upoważnienie"
+#: ../libpurple/protocols/oscar/oscar.c:5030
msgid "Require authorization"
msgstr "Wymagaj upoważnienia"
+#: ../libpurple/protocols/oscar/oscar.c:5033
msgid "Web aware (enabling this will cause you to receive SPAM!)"
msgstr "W sieci WWW (włączenie spowoduje otrzymywanie niechcianych wiadomości)"
+#: ../libpurple/protocols/oscar/oscar.c:5038
msgid "ICQ Privacy Options"
msgstr "Opcje prywatności ICQ"
+#: ../libpurple/protocols/oscar/oscar.c:5095
msgid "Change Address To:"
msgstr "Zmiana adresu na:"
+#: ../libpurple/protocols/oscar/oscar.c:5128
msgid "you are not waiting for authorization"
msgstr "upoważnienie nie jest oczekiwane"
+#: ../libpurple/protocols/oscar/oscar.c:5131
msgid "You are awaiting authorization from the following buddies"
msgstr "Oczekiwane jest upoważnienie od następujących znajomych"
+#: ../libpurple/protocols/oscar/oscar.c:5132
msgid ""
"You can re-request authorization from these buddies by right-clicking on "
"them and selecting \"Re-request Authorization.\""
-msgstr ""
-"Można ponownie poprosić o upoważnienia od tych znajomych, naciskają na nich "
-"prawym przyciskiem myszy i wybierając \"Ponownie poproś o upoważnienie\"."
+msgstr "Można ponownie poprosić o upoważnienia od tych znajomych, naciskają na nich prawym przyciskiem myszy i wybierając \"Ponownie poproś o upoważnienie\"."
+#: ../libpurple/protocols/oscar/oscar.c:5149
msgid "Find Buddy by Email"
msgstr "Znajdź użytkownika według adresu e-mail"
+#: ../libpurple/protocols/oscar/oscar.c:5150
msgid "Search for a buddy by email address"
msgstr "Wyszukaj znajomego według adresu e-mail"
+#: ../libpurple/protocols/oscar/oscar.c:5151
msgid "Type the email address of the buddy you are searching for."
msgstr "Proszę podać adres e-mail wyszukiwanego znajomego."
+#: ../libpurple/protocols/oscar/oscar.c:5311
msgid "Set User Info (web)..."
msgstr "Ustaw informacje o użytkowniku (WWW)..."
#. This only happens when connecting with the old-style BUCP login
+#: ../libpurple/protocols/oscar/oscar.c:5323
msgid "Change Password (web)"
msgstr "Zmień hasło (WWW)"
+#: ../libpurple/protocols/oscar/oscar.c:5330
msgid "Configure IM Forwarding (web)"
msgstr "Skonfiguruj przekierowanie komunikatora (WWW)"
#. ICQ actions
+#: ../libpurple/protocols/oscar/oscar.c:5340
msgid "Set Privacy Options..."
msgstr "Ustaw opcje prywatności..."
+#: ../libpurple/protocols/oscar/oscar.c:5344
msgid "Show Visible List"
msgstr "Wyświetl lista widocznych"
+#: ../libpurple/protocols/oscar/oscar.c:5347
msgid "Show Invisible List"
msgstr "Wyświetl listę niewidocznych"
#. AIM actions
+#: ../libpurple/protocols/oscar/oscar.c:5353
msgid "Confirm Account"
msgstr "Potwierdź konto"
+#: ../libpurple/protocols/oscar/oscar.c:5357
msgid "Display Currently Registered Email Address"
msgstr "Wyświetl aktualnie zarejestrowany adres e-mail"
+#: ../libpurple/protocols/oscar/oscar.c:5361
msgid "Change Currently Registered Email Address..."
msgstr "Zmień aktualnie zarejestrowany adres e-mail..."
+#: ../libpurple/protocols/oscar/oscar.c:5368
msgid "Show Buddies Awaiting Authorization"
msgstr "Wyświetl znajomych oczekujących na upoważnienie"
+#: ../libpurple/protocols/oscar/oscar.c:5374
msgid "Search for Buddy by Email Address..."
msgstr "Wyszukaj znajomych według adresu e-mail..."
+#: ../libpurple/protocols/oscar/oscar.c:5596
msgid "Use clientLogin"
msgstr "Użycie logowania klienta"
+#: ../libpurple/protocols/oscar/oscar.c:5601
msgid ""
"Always use AIM/ICQ proxy server for\n"
"file transfers and direct IM (slower,\n"
"but does not reveal your IP address)"
-msgstr ""
-"Używanie wyłącznie serwera pośrednika AIM/ICQ\n"
-"do przesyłania plików i bezpośrednich połączeń\n"
-"(wolniejsze, ale nie ujawnia adresu IP)"
+msgstr "Używanie wyłącznie serwera pośrednika AIM/ICQ\ndo przesyłania plików i bezpośrednich połączeń\n(wolniejsze, ale nie ujawnia adresu IP)"
+#: ../libpurple/protocols/oscar/oscar.c:5606
msgid "Allow multiple simultaneous logins"
msgstr "Zezwolenie na wiele jednoczesnych logowań"
+#: ../libpurple/protocols/oscar/peer.c:747
#, c-format
msgid "Asking %s to connect to us at %s:%hu for Direct IM."
-msgstr ""
-"Proszenie %s o połączenie z komputerem na %s:%hu, aby użyć bezpośredniego "
-"połączenia."
+msgstr "Proszenie %s o połączenie z komputerem na %s:%hu, aby użyć bezpośredniego połączenia."
+#: ../libpurple/protocols/oscar/peer.c:833
#, c-format
msgid "Attempting to connect to %s:%hu."
msgstr "Próba połączenia z %s:%hu."
+#: ../libpurple/protocols/oscar/peer.c:907
msgid "Attempting to connect via proxy server."
msgstr "Próba połączenia przez serwer pośrednika."
+#: ../libpurple/protocols/oscar/peer.c:1085
#, c-format
msgid "%s has just asked to directly connect to %s"
msgstr "Użytkownik %s poprosił o bezpośrednie połączenie z %s"
+#: ../libpurple/protocols/oscar/peer.c:1089
msgid ""
-"This requires a direct connection between the two computers and is necessary "
-"for IM Images. Because your IP address will be revealed, this may be "
+"This requires a direct connection between the two computers and is necessary"
+" for IM Images. Because your IP address will be revealed, this may be "
"considered a privacy risk."
-msgstr ""
-"Wymagane jest bezpośrednie połączenie między dwoma komputerami i jest ono "
-"niezbędne dla obrazów komunikatora. Ponieważ adres IP zostanie ujawniony, "
-"może się to wiązać z zagrożeniem prywatności."
+msgstr "Wymagane jest bezpośrednie połączenie między dwoma komputerami i jest ono niezbędne dla obrazów komunikatora. Ponieważ adres IP zostanie ujawniony, może się to wiązać z zagrożeniem prywatności."
#. Label
+#: ../libpurple/protocols/oscar/userinfo.c:43 ../pidgin/gtkutils.c:2232
+#: ../pidgin/plugins/gevolution/new_person_dialog.c:333
msgid "Buddy Icon"
msgstr "Ikona znajomego"
+#: ../libpurple/protocols/oscar/userinfo.c:46
+#: ../pidgin/plugins/themeedit-icon.c:82
msgid "Voice"
msgstr "Głos"
+#: ../libpurple/protocols/oscar/userinfo.c:49
msgid "AIM Direct IM"
msgstr "Komunikator bezpośredni AIM"
+#: ../libpurple/protocols/oscar/userinfo.c:55
msgid "Get File"
msgstr "Pobierz plik"
+#: ../libpurple/protocols/oscar/userinfo.c:62
msgid "Games"
msgstr "Gry"
+#: ../libpurple/protocols/oscar/userinfo.c:66
msgid "ICQ Xtraz"
msgstr "ICQ Xtraz"
+#: ../libpurple/protocols/oscar/userinfo.c:69
msgid "Add-Ins"
msgstr "Dodatki"
+#: ../libpurple/protocols/oscar/userinfo.c:72
msgid "Send Buddy List"
msgstr "Wyślij listę znajomych"
+#: ../libpurple/protocols/oscar/userinfo.c:75
msgid "ICQ Direct Connect"
msgstr "Bezpośrednie połączenia ICQ"
+#: ../libpurple/protocols/oscar/userinfo.c:78
msgid "AP User"
msgstr "Użytkownik AP"
+#: ../libpurple/protocols/oscar/userinfo.c:81
msgid "ICQ RTF"
msgstr "ICQ RTF"
+#: ../libpurple/protocols/oscar/userinfo.c:84
msgid "Nihilist"
msgstr "Nihilista"
+#: ../libpurple/protocols/oscar/userinfo.c:87
msgid "ICQ Server Relay"
msgstr "Odpowiedź serwera ICQ"
+#: ../libpurple/protocols/oscar/userinfo.c:90
msgid "Old ICQ UTF8"
msgstr "Poprzednie kodowanie UTF-8 protokołu ICQ"
+#: ../libpurple/protocols/oscar/userinfo.c:93
msgid "Trillian Encryption"
msgstr "Szyfrowanie programu Trillian"
+#: ../libpurple/protocols/oscar/userinfo.c:96
msgid "ICQ UTF8"
msgstr "Kodowanie UTF-8 protokołu ICQ"
+#: ../libpurple/protocols/oscar/userinfo.c:99
msgid "Hiptop"
msgstr "Hiptop"
+#: ../libpurple/protocols/oscar/userinfo.c:102
msgid "Security Enabled"
msgstr "Zabezpieczenia są włączone"
+#: ../libpurple/protocols/oscar/userinfo.c:105
msgid "Video Chat"
msgstr "Rozmowa wideo"
+#: ../libpurple/protocols/oscar/userinfo.c:109
msgid "iChat AV"
msgstr "Konferencja iChat"
+#: ../libpurple/protocols/oscar/userinfo.c:112
msgid "Live Video"
msgstr "Wideo na żywo"
+#: ../libpurple/protocols/oscar/userinfo.c:115
msgid "Camera"
msgstr "Kamera"
+#: ../libpurple/protocols/oscar/userinfo.c:118
msgid "Screen Sharing"
msgstr "Współdzielenie ekranu"
+#: ../libpurple/protocols/oscar/userinfo.c:343
+#: ../libpurple/protocols/oscar/userinfo.c:412
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:721 ../pidgin/gtkprefs.c:2171
msgid "IP Address"
msgstr "Adres IP"
+#: ../libpurple/protocols/oscar/userinfo.c:349
msgid "Warning Level"
msgstr "Poziom ostrzeżenia"
+#: ../libpurple/protocols/oscar/userinfo.c:358
msgid "Buddy Comment"
msgstr "Komentarz o znajomym"
+#: ../libpurple/protocols/oscar/userinfo.c:368
#, c-format
msgid "User information not available: %s"
msgstr "Informacje o użytkownicy są niedostępne: %s"
+#: ../libpurple/protocols/oscar/userinfo.c:423
+#: ../libpurple/protocols/silc/util.c:550
msgid "Mobile Phone"
msgstr "Telefon komórkowy"
+#. TODO: Is it correct to pass info->email here...?
+#: ../libpurple/protocols/oscar/userinfo.c:455
msgid "Personal Web Page"
msgstr "Osobista strona WWW"
#. aim_userinfo_t
#. use_html_status
+#: ../libpurple/protocols/oscar/userinfo.c:459
msgid "Additional Information"
msgstr "Dodatkowe informacje"
+#: ../libpurple/protocols/oscar/userinfo.c:468
+#: ../libpurple/protocols/oscar/userinfo.c:476
msgid "Zip Code"
msgstr "Kod pocztowy"
+#: ../libpurple/protocols/oscar/userinfo.c:479
msgid "Work Information"
msgstr "Informacje o pracy"
+#: ../libpurple/protocols/oscar/userinfo.c:482
msgid "Division"
msgstr "Wydział"
+#: ../libpurple/protocols/oscar/userinfo.c:483
msgid "Position"
msgstr "Zawód"
+#. TODO: Is it correct to pass info->email here...?
+#: ../libpurple/protocols/oscar/userinfo.c:485
msgid "Web Page"
msgstr "Strona WWW"
+#: ../libpurple/protocols/oscar/userinfo.c:513
msgid "Online Since"
msgstr "W trybie online od"
+#: ../libpurple/protocols/oscar/userinfo.c:518
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1181
msgid "Member Since"
msgstr "Członek od"
+#: ../libpurple/protocols/oscar/userinfo.c:524
msgid "Capabilities"
msgstr "Możliwości"
+#: ../libpurple/protocols/oscar/util.c:36
msgid "Invalid SNAC"
msgstr "Nieprawidłowe SNAC"
+#: ../libpurple/protocols/oscar/util.c:37
msgid "Server rate limit exceeded"
msgstr "Przekroczono ograniczenie prędkości serwera"
+#: ../libpurple/protocols/oscar/util.c:38
msgid "Client rate limit exceeded"
msgstr "Przekroczono ograniczenie prędkości klienta"
+#: ../libpurple/protocols/oscar/util.c:40
msgid "Service unavailable"
msgstr "Usługa jest niedostępna"
+#: ../libpurple/protocols/oscar/util.c:41
msgid "Service not defined"
msgstr "Nie określono usługi"
+#: ../libpurple/protocols/oscar/util.c:42
msgid "Obsolete SNAC"
msgstr "Przestarzałe SNAC"
+#: ../libpurple/protocols/oscar/util.c:43
msgid "Not supported by host"
msgstr "Nieobsługiwane przez serwer"
+#: ../libpurple/protocols/oscar/util.c:44
msgid "Not supported by client"
msgstr "Nieobsługiwane przez klienta"
+#: ../libpurple/protocols/oscar/util.c:45
msgid "Refused by client"
msgstr "Odmowa klienta"
+#: ../libpurple/protocols/oscar/util.c:46
msgid "Reply too big"
msgstr "Odpowiedź jest za duża"
+#: ../libpurple/protocols/oscar/util.c:47
msgid "Responses lost"
msgstr "Utracono odpowiedzi"
+#: ../libpurple/protocols/oscar/util.c:48
msgid "Request denied"
msgstr "Odrzucono prośby"
+#: ../libpurple/protocols/oscar/util.c:49
msgid "Busted SNAC payload"
msgstr "Uszkodzone dane SNAC"
+#: ../libpurple/protocols/oscar/util.c:50
msgid "Insufficient rights"
msgstr "Niewystarczające uprawnienia"
+#: ../libpurple/protocols/oscar/util.c:51
msgid "In local permit/deny"
msgstr "W lokalnym pozwoleniu/odmowie"
+#: ../libpurple/protocols/oscar/util.c:52
msgid "Warning level too high (sender)"
msgstr "Poziom ostrzeżenia jest za wysoki (nadawca)"
+#: ../libpurple/protocols/oscar/util.c:53
msgid "Warning level too high (receiver)"
msgstr "Poziom ostrzeżenia jest za wysoki (odbiorca)"
+#: ../libpurple/protocols/oscar/util.c:54
msgid "User temporarily unavailable"
msgstr "Użytkownik jest tymczasowo niedostępny"
+#: ../libpurple/protocols/oscar/util.c:55
msgid "No match"
msgstr "Brak wyników"
+#: ../libpurple/protocols/oscar/util.c:56
msgid "List overflow"
msgstr "Lista jest pełna"
+#: ../libpurple/protocols/oscar/util.c:57
msgid "Request ambiguous"
msgstr "Niejednoznaczna prośba"
+#: ../libpurple/protocols/oscar/util.c:58
msgid "Queue full"
msgstr "Kolejka jest pełna"
+#: ../libpurple/protocols/oscar/util.c:59
msgid "Not while on AOL"
msgstr "Nie podczas używania AOL"
#. Translators: This string is a menu option that, if selected, will cause
#. you to appear online to the chosen user even when your status is set to
#. Invisible.
+#: ../libpurple/protocols/oscar/visibility.c:26
+#: ../libpurple/protocols/yahoo/libymsg.c:4104
+#: ../libpurple/protocols/yahoo/libymsg.c:4170
msgid "Appear Online"
msgstr "Wygląda na tryb online"
#. Translators: This string is a menu option that, if selected, will cause
#. you to appear offline to the chosen user when your status is set to
#. Invisible (this is the default).
+#: ../libpurple/protocols/oscar/visibility.c:31
msgid "Don't Appear Online"
msgstr "Nie wygląda na tryb online"
#. Translators: This string is a menu option that, if selected, will cause
#. you to always appear offline to the chosen user (even when your status
#. isn't Invisible).
+#: ../libpurple/protocols/oscar/visibility.c:36
+#: ../libpurple/protocols/yahoo/libymsg.c:4176
msgid "Appear Offline"
msgstr "Wygląda na tryb offline"
@@ -8265,852 +10967,1114 @@ msgstr "Wygląda na tryb offline"
#. you to appear offline to the chosen user if you are invisible, and
#. appear online to the chosen user if you are not invisible (this is the
#. default).
+#: ../libpurple/protocols/oscar/visibility.c:42
msgid "Don't Appear Offline"
msgstr "Nie wygląda na tryb offline"
+#: ../libpurple/protocols/oscar/visibility.c:113
msgid "you have no buddies on this list"
msgstr "brak znajomych na tej liście"
+#: ../libpurple/protocols/oscar/visibility.c:116
#, c-format
msgid ""
-"You can add a buddy to this list by right-clicking on them and selecting \"%s"
-"\""
-msgstr ""
-"Można dodać znajomych do tej listy naciskają ma nich prawym przyciskiem "
-"myszy i wybierając \"%s\""
+"You can add a buddy to this list by right-clicking on them and selecting "
+"\"%s\""
+msgstr "Można dodać znajomych do tej listy naciskają ma nich prawym przyciskiem myszy i wybierając \"%s\""
+#: ../libpurple/protocols/oscar/visibility.c:127
msgid "Visible List"
msgstr "Lista widocznych"
+#: ../libpurple/protocols/oscar/visibility.c:128
msgid "These buddies will see your status when you switch to \"Invisible\""
-msgstr ""
-"Znajomi na tej liście będą widzieli stan użytkownika po przełączeniu na "
-"\"Niewidoczny\""
+msgstr "Znajomi na tej liście będą widzieli stan użytkownika po przełączeniu na \"Niewidoczny\""
+#: ../libpurple/protocols/oscar/visibility.c:137
msgid "Invisible List"
msgstr "Lista niewidocznych"
+#: ../libpurple/protocols/oscar/visibility.c:138
msgid "These buddies will always see you as offline"
msgstr "Znajomi na tej liście będą widzieli stan użytkownika jako tryb offline"
+#: ../libpurple/protocols/sametime/sametime.c:1317
#, c-format
msgid "<b>Group Title:</b> %s<br>"
msgstr "<b>Nazwa grupy:</b> %s<br>"
+#: ../libpurple/protocols/sametime/sametime.c:1318
#, c-format
msgid "<b>Notes Group ID:</b> %s<br>"
msgstr "<b>Identyfikator grupy programu Notes:</b> %s<br>"
+#: ../libpurple/protocols/sametime/sametime.c:1320
#, c-format
msgid "Info for Group %s"
msgstr "Informacje o grupie %s"
+#: ../libpurple/protocols/sametime/sametime.c:1322
msgid "Notes Address Book Information"
msgstr "Informacje o książce adresowej programu Notes"
+#: ../libpurple/protocols/sametime/sametime.c:1353
msgid "Invite Group to Conference..."
msgstr "Zaproś grupę do konferencji..."
+#: ../libpurple/protocols/sametime/sametime.c:1363
msgid "Get Notes Address Book Info"
msgstr "Uzyskaj informacje o książce adresowej programu Notes"
+#: ../libpurple/protocols/sametime/sametime.c:1534
msgid "Sending Handshake"
msgstr "Wysyłanie powitania"
+#: ../libpurple/protocols/sametime/sametime.c:1539
msgid "Waiting for Handshake Acknowledgement"
msgstr "Oczekiwanie na potwierdzenie powitania"
+#: ../libpurple/protocols/sametime/sametime.c:1544
msgid "Handshake Acknowledged, Sending Login"
msgstr "Potwierdzono powitania, wysyłanie logowania"
+#: ../libpurple/protocols/sametime/sametime.c:1549
msgid "Waiting for Login Acknowledgement"
msgstr "Oczekiwanie na potwierdzenie zalogowania"
+#: ../libpurple/protocols/sametime/sametime.c:1554
msgid "Login Redirected"
msgstr "Logowanie zostało przekierowane"
+#: ../libpurple/protocols/sametime/sametime.c:1560
msgid "Forcing Login"
msgstr "Wymuszanie zalogowania"
+#: ../libpurple/protocols/sametime/sametime.c:1565
msgid "Login Acknowledged"
msgstr "Potwierdzono zalogowanie"
+#: ../libpurple/protocols/sametime/sametime.c:1570
msgid "Starting Services"
msgstr "Uruchamianie usług"
+#: ../libpurple/protocols/sametime/sametime.c:1716
#, c-format
msgid ""
"A Sametime administrator has issued the following announcement on server %s"
msgstr "Administrator Sametime zostawił następujące ogłoszenie na serwerze %s"
+#: ../libpurple/protocols/sametime/sametime.c:1721
msgid "Sametime Administrator Announcement"
msgstr "Ogłoszenie administratora Sametime"
+#: ../libpurple/protocols/sametime/sametime.c:1853
#, c-format
msgid "Announcement from %s"
msgstr "Ogłoszenie od %s"
+#: ../libpurple/protocols/sametime/sametime.c:2032
msgid "Conference Closed"
msgstr "Zamknięto konferencję"
+#: ../libpurple/protocols/sametime/sametime.c:2465
msgid "Unable to send message: "
msgstr "Nie można wysłać wiadomości: "
+#: ../libpurple/protocols/sametime/sametime.c:2472
#, c-format
msgid "Unable to send message to %s:"
msgstr "Nie można wysłać wiadomości do %s:"
+#: ../libpurple/protocols/sametime/sametime.c:3014
msgid "Place Closed"
msgstr "Zamknięto miejsce"
+#: ../libpurple/protocols/sametime/sametime.c:3268
msgid "Microphone"
msgstr "Mikrofon"
+#: ../libpurple/protocols/sametime/sametime.c:3269
msgid "Speakers"
msgstr "Głośniki"
+#: ../libpurple/protocols/sametime/sametime.c:3270
msgid "Video Camera"
msgstr "Kamera wideo"
+#: ../libpurple/protocols/sametime/sametime.c:3274
msgid "File Transfer"
msgstr "Przesyłanie plików"
+#: ../libpurple/protocols/sametime/sametime.c:3306
+#: ../libpurple/protocols/sametime/sametime.c:4138
msgid "Supports"
msgstr "Obsługuje"
+#: ../libpurple/protocols/sametime/sametime.c:3311
+#: ../libpurple/protocols/sametime/sametime.c:4109
msgid "External User"
msgstr "Zewnętrzny użytkownik"
+#: ../libpurple/protocols/sametime/sametime.c:3417
msgid "Create conference with user"
msgstr "Utwórz konferencję z użytkownikiem"
+#: ../libpurple/protocols/sametime/sametime.c:3418
#, c-format
msgid ""
-"Please enter a topic for the new conference, and an invitation message to be "
-"sent to %s"
-msgstr ""
-"Proszę podać temat nowej konferencji i treść zaproszenia, która zostanie "
-"wysłana do %s"
+"Please enter a topic for the new conference, and an invitation message to be"
+" sent to %s"
+msgstr "Proszę podać temat nowej konferencji i treść zaproszenia, która zostanie wysłana do %s"
+#: ../libpurple/protocols/sametime/sametime.c:3422
msgid "New Conference"
msgstr "Nowa konferencja"
+#: ../libpurple/protocols/sametime/sametime.c:3424
msgid "Create"
msgstr "Utwórz"
+#: ../libpurple/protocols/sametime/sametime.c:3489
msgid "Available Conferences"
msgstr "Dostępne konferencje"
+#: ../libpurple/protocols/sametime/sametime.c:3495
msgid "Create New Conference..."
msgstr "Utwórz nową konferencję..."
+#: ../libpurple/protocols/sametime/sametime.c:3502
msgid "Invite user to a conference"
msgstr "Zaproś użytkownika do konferencji"
+#: ../libpurple/protocols/sametime/sametime.c:3503
#, c-format
msgid ""
-"Select a conference from the list below to send an invite to user %s. Select "
-"\"Create New Conference\" if you'd like to create a new conference to invite "
-"this user to."
-msgstr ""
-"Proszę wybrać konferencję z poniższej listy, aby wysłać zaproszenie "
-"użytkownikowi %s. Aby utworzyć nową konferencję, należy wybrać \"Utwórz nową "
-"konferencję\"."
+"Select a conference from the list below to send an invite to user %s. Select"
+" \"Create New Conference\" if you'd like to create a new conference to "
+"invite this user to."
+msgstr "Proszę wybrać konferencję z poniższej listy, aby wysłać zaproszenie użytkownikowi %s. Aby utworzyć nową konferencję, należy wybrać \"Utwórz nową konferencję\"."
+#: ../libpurple/protocols/sametime/sametime.c:3508
msgid "Invite to Conference"
msgstr "Zaproś do konferencji"
+#: ../libpurple/protocols/sametime/sametime.c:3599
msgid "Invite to Conference..."
msgstr "Zaproś do konferencji..."
+#: ../libpurple/protocols/sametime/sametime.c:3604
msgid "Send TEST Announcement"
msgstr "Wyślij ogłoszenie TESTOWE"
+#: ../libpurple/protocols/sametime/sametime.c:3623 ../pidgin/gtkconv.c:5031
msgid "Topic:"
msgstr "Temat:"
+#: ../libpurple/protocols/sametime/sametime.c:3683
msgid "A server is required to connect this account"
-msgstr "Wymagany jest serwer, aby połączyć to konto"
+msgstr "Wymagany jest serwer do połączenia z tym kontem"
+#: ../libpurple/protocols/sametime/sametime.c:4127
+#: ../libpurple/protocols/sametime/sametime.c:4130
+msgid "Last Known Client"
+msgstr "Ostatni znany klient"
+
+#: ../libpurple/protocols/sametime/sametime.c:4129
#, c-format
msgid "Unknown (0x%04x)<br>"
msgstr "Nieznany (0x%04x)<br>"
-msgid "Last Known Client"
-msgstr "Ostatni znany klient"
-
+#: ../libpurple/protocols/sametime/sametime.c:4292
+#: ../libpurple/protocols/sametime/sametime.c:5492
msgid "User Name"
msgstr "Nazwa użytkownika"
+#: ../libpurple/protocols/sametime/sametime.c:4295
+#: ../libpurple/protocols/sametime/sametime.c:5495
msgid "Sametime ID"
msgstr "Identyfikator Sametime"
+#: ../libpurple/protocols/sametime/sametime.c:4319
msgid "An ambiguous user ID was entered"
msgstr "Podano niejednoznaczny identyfikator użytkownika"
+#: ../libpurple/protocols/sametime/sametime.c:4320
#, c-format
msgid ""
-"The identifier '%s' may possibly refer to any of the following users. Please "
-"select the correct user from the list below to add them to your buddy list."
-msgstr ""
-"Identyfikator \"%s\" może odnosić się do dowolnego z poniższych "
-"użytkowników. Proszę wybrać poprawnego użytkownika z poniższej listy, aby "
-"dodać go do listy znajomych."
+"The identifier '%s' may possibly refer to any of the following users. Please"
+" select the correct user from the list below to add them to your buddy list."
+msgstr "Identyfikator \"%s\" może odnosić się do dowolnego z poniższych użytkowników. Proszę wybrać poprawnego użytkownika z poniższej listy, aby dodać go do listy znajomych."
+#: ../libpurple/protocols/sametime/sametime.c:4325
msgid "Select User"
msgstr "Wybór użytkownika"
+#: ../libpurple/protocols/sametime/sametime.c:4409
msgid "Unable to add user: user not found"
msgstr "Nie można dodać użytkownika, ponieważ nie został on odnaleziony"
+#: ../libpurple/protocols/sametime/sametime.c:4411
#, c-format
msgid ""
-"The identifier '%s' did not match any users in your Sametime community. This "
-"entry has been removed from your buddy list."
-msgstr ""
-"Identyfikator \"%s\" nie pasuje do żadnego użytkownika w społeczności "
-"Sametime. Ten wpis został usunięty z listy znajomych."
+"The identifier '%s' did not match any users in your Sametime community. This"
+" entry has been removed from your buddy list."
+msgstr "Identyfikator \"%s\" nie pasuje do żadnego użytkownika w społeczności Sametime. Ten wpis został usunięty z listy znajomych."
+#: ../libpurple/protocols/sametime/sametime.c:4993
#, c-format
msgid ""
"Error reading file %s: \n"
"%s\n"
-msgstr ""
-"Błąd podczas odczytywania pliku %s: \n"
-"%s\n"
+msgstr "Błąd podczas odczytywania pliku %s: \n%s\n"
+#: ../libpurple/protocols/sametime/sametime.c:5141
msgid "Remotely Stored Buddy List"
msgstr "Zdalnie przechowywana lista znajomych"
+#: ../libpurple/protocols/sametime/sametime.c:5146
msgid "Buddy List Storage Mode"
msgstr "Tryb przechowywania listy znajomych"
+#: ../libpurple/protocols/sametime/sametime.c:5149
msgid "Local Buddy List Only"
msgstr "Tylko lokalna lista znajomych"
+#: ../libpurple/protocols/sametime/sametime.c:5151
msgid "Merge List from Server"
msgstr "Połącz listę z serwera"
+#: ../libpurple/protocols/sametime/sametime.c:5153
msgid "Merge and Save List to Server"
msgstr "Połącz i zapisz listę na serwerze"
+#: ../libpurple/protocols/sametime/sametime.c:5155
msgid "Synchronize List with Server"
msgstr "Zsynchronizuj listę z serwerem"
+#: ../libpurple/protocols/sametime/sametime.c:5199
#, c-format
msgid "Import Sametime List for Account %s"
msgstr "Zaimportuj listę Sametime dla konta %s"
+#: ../libpurple/protocols/sametime/sametime.c:5239
#, c-format
msgid "Export Sametime List for Account %s"
msgstr "Wyeksportuj listę Sametime dla konta %s"
+#: ../libpurple/protocols/sametime/sametime.c:5293
msgid "Unable to add group: group exists"
msgstr "Nie można dodać grupy: grupa już istnieje"
+#: ../libpurple/protocols/sametime/sametime.c:5294
#, c-format
msgid "A group named '%s' already exists in your buddy list."
msgstr "Grupa o nazwie \"%s\" już istnieje na liście znajomych."
+#: ../libpurple/protocols/sametime/sametime.c:5297
+#: ../libpurple/protocols/sametime/sametime.c:5428
msgid "Unable to add group"
msgstr "Nie można dodać grupy"
+#: ../libpurple/protocols/sametime/sametime.c:5357
msgid "Possible Matches"
msgstr "Możliwe wyniki"
+#: ../libpurple/protocols/sametime/sametime.c:5373
msgid "Notes Address Book group results"
msgstr "Wyniki grupy książki adresowej programu Notes"
+#: ../libpurple/protocols/sametime/sametime.c:5374
#, c-format
msgid ""
-"The identifier '%s' may possibly refer to any of the following Notes Address "
-"Book groups. Please select the correct group from the list below to add it "
+"The identifier '%s' may possibly refer to any of the following Notes Address"
+" Book groups. Please select the correct group from the list below to add it "
"to your buddy list."
-msgstr ""
-"Identyfikator \"%s\" może odnosić się do dowolnej grupy książki adresowej "
-"programu Notes. Proszę wybrać poprawną grupę z poniższej listy, aby dodać ją "
-"do listy znajomych."
+msgstr "Identyfikator \"%s\" może odnosić się do dowolnej grupy książki adresowej programu Notes. Proszę wybrać poprawną grupę z poniższej listy, aby dodać ją do listy znajomych."
+#: ../libpurple/protocols/sametime/sametime.c:5379
msgid "Select Notes Address Book"
msgstr "Wybór książki adresowej programu Notes"
+#: ../libpurple/protocols/sametime/sametime.c:5422
msgid "Unable to add group: group not found"
msgstr "Nie można dodać do grupy: nie odnaleziono grupy"
+#: ../libpurple/protocols/sametime/sametime.c:5424
#, c-format
msgid ""
"The identifier '%s' did not match any Notes Address Book groups in your "
"Sametime community."
-msgstr ""
-"Identyfikator \"%s\" nie pasuje do żadnej grupy książki adresowej programu "
-"Notes w społeczności Sametime."
+msgstr "Identyfikator \"%s\" nie pasuje do żadnej grupy książki adresowej programu Notes w społeczności Sametime."
+#: ../libpurple/protocols/sametime/sametime.c:5466
msgid "Notes Address Book Group"
msgstr "Grupa książki adresowej programu Notes"
+#: ../libpurple/protocols/sametime/sametime.c:5467
msgid ""
"Enter the name of a Notes Address Book group in the field below to add the "
"group and its members to your buddy list."
-msgstr ""
-"Proszę podać nazwę grupy książki adresowej w poniższym polu, aby dodać grupę "
-"i jej członków do listy znajomych."
+msgstr "Proszę podać nazwę grupy książki adresowej w poniższym polu, aby dodać grupę i jej członków do listy znajomych."
+#: ../libpurple/protocols/sametime/sametime.c:5516
#, c-format
msgid "Search results for '%s'"
msgstr "Wyniki wyszukiwania dla \"%s\""
+#: ../libpurple/protocols/sametime/sametime.c:5517
#, c-format
msgid ""
"The identifier '%s' may possibly refer to any of the following users. You "
-"may add these users to your buddy list or send them messages with the action "
-"buttons below."
-msgstr ""
-"Identyfikator \"%s\" może odnosić się do dowolnego z poniższych "
-"użytkowników. Można dodać tych użytkowników do listy znajomych lub wysłać im "
-"wiadomości za pomocą poniższych przycisków czynności."
+"may add these users to your buddy list or send them messages with the action"
+" buttons below."
+msgstr "Identyfikator \"%s\" może odnosić się do dowolnego z poniższych użytkowników. Można dodać tych użytkowników do listy znajomych lub wysłać im wiadomości za pomocą poniższych przycisków czynności."
+#: ../libpurple/protocols/sametime/sametime.c:5524 ../pidgin/gtknotify.c:1020
msgid "Search Results"
msgstr "Wyniki wyszukiwania"
+#: ../libpurple/protocols/sametime/sametime.c:5549
msgid "No matches"
msgstr "Brak wyników"
+#: ../libpurple/protocols/sametime/sametime.c:5550
#, c-format
-msgid "The identifier '%s' did not match any users in your Sametime community."
-msgstr ""
-"Identyfikator \"%s\" nie pasuje do żadnego użytkownika w społeczności "
-"Sametime."
+msgid ""
+"The identifier '%s' did not match any users in your Sametime community."
+msgstr "Identyfikator \"%s\" nie pasuje do żadnego użytkownika w społeczności Sametime."
+#: ../libpurple/protocols/sametime/sametime.c:5554
msgid "No Matches"
msgstr "Brak wyników"
+#: ../libpurple/protocols/sametime/sametime.c:5592
msgid "Search for a user"
msgstr "Wyszukaj użytkownika"
+#: ../libpurple/protocols/sametime/sametime.c:5593
msgid ""
"Enter a name or partial ID in the field below to search for matching users "
"in your Sametime community."
-msgstr ""
-"Proszę podać nazwę lub częściowy identyfikator w poniższym polu, aby "
-"wyszukać pasujących użytkowników w społeczności Sametime."
+msgstr "Proszę podać nazwę lub częściowy identyfikator w poniższym polu, aby wyszukać pasujących użytkowników w społeczności Sametime."
+#: ../libpurple/protocols/sametime/sametime.c:5596
msgid "User Search"
msgstr "Wyszukiwanie użytkownika"
+#: ../libpurple/protocols/sametime/sametime.c:5609
msgid "Import Sametime List..."
msgstr "Zaimportuj listę Sametime..."
+#: ../libpurple/protocols/sametime/sametime.c:5613
msgid "Export Sametime List..."
msgstr "Wyeksportuj listę Sametime..."
+#: ../libpurple/protocols/sametime/sametime.c:5617
msgid "Add Notes Address Book Group..."
msgstr "Dodaj grupę książki adresowej programu Notes..."
+#: ../libpurple/protocols/sametime/sametime.c:5621
msgid "User Search..."
msgstr "Wyszukiwanie użytkownika..."
+#: ../libpurple/protocols/sametime/sametime.c:5728
msgid "Force login (ignore server redirects)"
msgstr "Wymuszenie logowania (ignorowanie przekierowań serwera)"
#. pretend to be Sametime Connect
+#: ../libpurple/protocols/sametime/sametime.c:5738
msgid "Hide client identity"
msgstr "Ukrycie tożsamości klienta"
+#: ../libpurple/protocols/silc/buddy.c:56
+#: ../libpurple/protocols/silc/buddy.c:424
+#: ../libpurple/protocols/silc/buddy.c:552
+#: ../libpurple/protocols/silc/buddy.c:715
+#: ../libpurple/protocols/silc/ft.c:431
#, c-format
msgid "User %s is not present in the network"
msgstr "Użytkownik %s nie jest obecny w sieci"
+#: ../libpurple/protocols/silc/buddy.c:57
+#: ../libpurple/protocols/silc/buddy.c:115
+#: ../libpurple/protocols/silc/buddy.c:121
+#: ../libpurple/protocols/silc/buddy.c:127
+#: ../libpurple/protocols/silc/buddy.c:133
+#: ../libpurple/protocols/silc/buddy.c:139
+#: ../libpurple/protocols/silc/buddy.c:145
+#: ../libpurple/protocols/silc/buddy.c:265
msgid "Key Agreement"
msgstr "Akceptacja klucza"
+#: ../libpurple/protocols/silc/buddy.c:58
msgid "Cannot perform the key agreement"
msgstr "Nie można wykonać akceptacji klucza"
+#: ../libpurple/protocols/silc/buddy.c:116
msgid "Error occurred during key agreement"
msgstr "Wystąpił błąd podczas akceptacji klucza"
+#: ../libpurple/protocols/silc/buddy.c:122
msgid "Key Agreement failed"
msgstr "Akceptacja klucza się nie powiodła"
+#: ../libpurple/protocols/silc/buddy.c:128
msgid "Timeout during key agreement"
msgstr "Przekroczono czas oczekiwania podczas akceptacji klucza"
+#: ../libpurple/protocols/silc/buddy.c:134
msgid "Key agreement was aborted"
msgstr "Przerwano akceptację klucza"
+#: ../libpurple/protocols/silc/buddy.c:139
msgid "Key agreement is already started"
msgstr "Akceptacja klucza jest już rozpoczęta"
+#: ../libpurple/protocols/silc/buddy.c:145
msgid "Key agreement cannot be started with yourself"
msgstr "Nie można rozpocząć akceptacji klucza z samym sobą"
+#: ../libpurple/protocols/silc/buddy.c:266
+#: ../libpurple/protocols/silc/buddy.c:397
+#: ../libpurple/protocols/silc/buddy.c:511
+#: ../libpurple/protocols/silc/buddy.c:522
msgid "The remote user is not present in the network any more"
msgstr "Zdalny użytkownik nie jest już obecny na sieci"
+#: ../libpurple/protocols/silc/buddy.c:309
#, c-format
msgid ""
"Key agreement request received from %s. Would you like to perform the key "
"agreement?"
-msgstr ""
-"Odebrano prośbę o zaakceptowanie klucza od użytkownika %s. Wykonać "
-"akceptację klucza?"
+msgstr "Odebrano prośbę o zaakceptowanie klucza od użytkownika %s. Wykonać akceptację klucza?"
+#: ../libpurple/protocols/silc/buddy.c:313
#, c-format
msgid ""
"The remote user is waiting key agreement on:\n"
"Remote host: %s\n"
"Remote port: %d"
-msgstr ""
-"Zdalny użytkownik oczekuje na akceptację klucza na:\n"
-"Zdalnym komputerze: %s\n"
-"Zdalnym porcie: %d"
+msgstr "Zdalny użytkownik oczekuje na akceptację klucza na:\nZdalnym komputerze: %s\nZdalnym porcie: %d"
+#: ../libpurple/protocols/silc/buddy.c:326
msgid "Key Agreement Request"
msgstr "Prośba o akceptację klucza"
+#: ../libpurple/protocols/silc/buddy.c:396
+#: ../libpurple/protocols/silc/buddy.c:426
+#: ../libpurple/protocols/silc/buddy.c:466
msgid "IM With Password"
msgstr "Wiadomość z hasłem"
+#: ../libpurple/protocols/silc/buddy.c:427
msgid "Cannot set IM key"
msgstr "Nie można ustawić klucza wiadomości"
+#: ../libpurple/protocols/silc/buddy.c:467
msgid "Set IM Password"
msgstr "Ustaw hasło wiadomości"
+#: ../libpurple/protocols/silc/buddy.c:510
+#: ../libpurple/protocols/silc/buddy.c:521
+#: ../libpurple/protocols/silc/buddy.c:554
+#: ../libpurple/protocols/silc/ops.c:1529
+#: ../libpurple/protocols/silc/ops.c:1541
msgid "Get Public Key"
msgstr "Pobierz klucz publiczny"
+#: ../libpurple/protocols/silc/buddy.c:555
+#: ../libpurple/protocols/silc/ops.c:1530
+#: ../libpurple/protocols/silc/ops.c:1542
msgid "Cannot fetch the public key"
msgstr "Nie można pobrać klucza publicznego"
+#: ../libpurple/protocols/silc/buddy.c:635
+#: ../libpurple/protocols/silc/buddy.c:1695
msgid "Show Public Key"
msgstr "Wyświetl klucz publiczny"
+#: ../libpurple/protocols/silc/buddy.c:636
+#: ../libpurple/protocols/silc/buddy.c:1041
+#: ../libpurple/protocols/silc/chat.c:244
msgid "Could not load public key"
msgstr "Nie można wczytać klucza publicznego"
+#: ../libpurple/protocols/silc/buddy.c:716
+#: ../libpurple/protocols/silc/ops.c:1065
+#: ../libpurple/protocols/silc/ops.c:1181
+#: ../libpurple/protocols/silc/ops.c:1311
+#: ../libpurple/protocols/silc/ops.c:1312
+#: ../libpurple/protocols/silc/ops.c:1330
msgid "User Information"
msgstr "Informacje o użytkowniku"
-msgid "Cannot get user information"
-msgstr "Nie można pobrać informacji o użytkowniku"
-
+#: ../libpurple/protocols/silc/buddy.c:739
#, c-format
msgid "The %s buddy is not trusted"
msgstr "Użytkownik %s nie jest zaufany"
+#: ../libpurple/protocols/silc/buddy.c:742
msgid ""
-"You cannot receive buddy notifications until you import his/her public key. "
-"You can use the Get Public Key command to get the public key."
-msgstr ""
-"Nie można odbierać powiadomień znajomego, dopóki nie zostanie zaimportowany "
-"jego klucz publiczny. Można użyć polecenia Pobierz klucz publiczny, aby go "
-"uzyskać."
+"You cannot receive buddy notifications until you import his/her public key."
+" You can use the Get Public Key command to get the public key."
+msgstr "Nie można odbierać powiadomień znajomego, dopóki nie zostanie zaimportowany jego klucz publiczny. Można użyć polecenia Pobierz klucz publiczny, aby go uzyskać."
#. Open file selector to select the public key.
+#: ../libpurple/protocols/silc/buddy.c:1075
msgid "Open..."
msgstr "Otwórz..."
+#: ../libpurple/protocols/silc/buddy.c:1086
#, c-format
msgid "The %s buddy is not present in the network"
msgstr "Znajomy %s nie jest obecny w sieci"
+#: ../libpurple/protocols/silc/buddy.c:1089
msgid ""
"To add the buddy you must import his/her public key. Press Import to import "
"a public key."
-msgstr ""
-"Aby dodać znajomego należy najpierw zaimportować jego klucz publiczny. W tym "
-"celu należy nacisnąć przycisk Zaimportuj."
+msgstr "Aby dodać znajomego należy najpierw zaimportować jego klucz publiczny. W tym celu należy nacisnąć przycisk Zaimportuj."
+#: ../libpurple/protocols/silc/buddy.c:1093
msgid "_Import..."
msgstr "Zai_mportuj..."
+#: ../libpurple/protocols/silc/buddy.c:1199
msgid "Select correct user"
msgstr "Wybór właściwego użytkownika"
+#: ../libpurple/protocols/silc/buddy.c:1201
msgid ""
"More than one user was found with the same public key. Select the correct "
"user from the list to add to the buddy list."
-msgstr ""
-"Odnaleziono więcej niż jednego użytkownika z tym samym kluczem publicznym. "
-"Proszę wybrać właściwego użytkownika, który ma zostać dodany do listy "
-"znajomych."
+msgstr "Odnaleziono więcej niż jednego użytkownika z tym samym kluczem publicznym. Proszę wybrać właściwego użytkownika, który ma zostać dodany do listy znajomych."
+#: ../libpurple/protocols/silc/buddy.c:1203
msgid ""
"More than one user was found with the same name. Select the correct user "
"from the list to add to the buddy list."
-msgstr ""
-"Odnaleziono więcej niż jednego użytkownika o tej samej nazwie. Proszę wybrać "
-"właściwego użytkownika, który ma zostać dodany do listy znajomych."
+msgstr "Odnaleziono więcej niż jednego użytkownika o tej samej nazwie. Proszę wybrać właściwego użytkownika, który ma zostać dodany do listy znajomych."
+#: ../libpurple/protocols/silc/buddy.c:1497
msgid "Detached"
msgstr "Oderwany"
+#: ../libpurple/protocols/silc/buddy.c:1501
+#: ../libpurple/protocols/silc/silc.c:61
msgid "Indisposed"
msgstr "Niedysponowany"
+#: ../libpurple/protocols/silc/buddy.c:1505
+#: ../libpurple/protocols/silc/silc.c:63
msgid "Wake Me Up"
msgstr "Obudźcie mnie"
+#: ../libpurple/protocols/silc/buddy.c:1507
+#: ../libpurple/protocols/silc/silc.c:55
msgid "Hyper Active"
msgstr "Hiperaktywny"
+#: ../libpurple/protocols/silc/buddy.c:1509
msgid "Robot"
msgstr "Robot"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
+#: ../libpurple/protocols/silc/buddy.c:1570
+#: ../libpurple/protocols/silc/ops.c:1217
msgid "User Modes"
msgstr "Tryby użytkownika"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
+#: ../libpurple/protocols/silc/buddy.c:1593
+#: ../libpurple/protocols/silc/ops.c:1236
msgid "Preferred Contact"
msgstr "Preferowany kontakt"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
+#: ../libpurple/protocols/silc/buddy.c:1600
+#: ../libpurple/protocols/silc/ops.c:1243
msgid "Preferred Language"
msgstr "Preferowany język"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
+#: ../libpurple/protocols/silc/buddy.c:1607
+#: ../libpurple/protocols/silc/ops.c:1250
msgid "Device"
msgstr "Urządzenie"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
+#: ../libpurple/protocols/silc/buddy.c:1614
+#: ../libpurple/protocols/silc/ops.c:1257
msgid "Timezone"
msgstr "Strefa czasowa"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
+#: ../libpurple/protocols/silc/buddy.c:1621
+#: ../libpurple/protocols/silc/ops.c:1264
msgid "Geolocation"
msgstr "Położenie geograficzne"
+#: ../libpurple/protocols/silc/buddy.c:1678
msgid "Reset IM Key"
msgstr "Przywróć klucz wiadomości"
+#: ../libpurple/protocols/silc/buddy.c:1683
msgid "IM with Key Exchange"
msgstr "Wiadomość z wymianą kluczy"
+#: ../libpurple/protocols/silc/buddy.c:1688
msgid "IM with Password"
msgstr "Wiadomość z hasłem"
+#: ../libpurple/protocols/silc/buddy.c:1701
msgid "Get Public Key..."
msgstr "Pobierz klucz publiczny..."
+#: ../libpurple/protocols/silc/buddy.c:1708
+#: ../libpurple/protocols/silc/ops.c:1645
msgid "Kill User"
msgstr "Zabij użytkownika"
+#: ../libpurple/protocols/silc/buddy.c:1718
+#: ../libpurple/protocols/silc/chat.c:1007
msgid "Draw On Whiteboard"
msgstr "Rysuj na tablicy"
+#: ../libpurple/protocols/silc/chat.c:42
msgid "_Passphrase:"
msgstr "_Hasło:"
+#: ../libpurple/protocols/silc/chat.c:83
#, c-format
msgid "Channel %s does not exist in the network"
msgstr "Kanał %s nie istnieje w tej sieci"
+#: ../libpurple/protocols/silc/chat.c:84
+#: ../libpurple/protocols/silc/chat.c:180
msgid "Channel Information"
msgstr "Informacje o kanale"
+#: ../libpurple/protocols/silc/chat.c:85
msgid "Cannot get channel information"
msgstr "Nie można pobrać informacji o kanale"
+#: ../libpurple/protocols/silc/chat.c:123
#, c-format
msgid "<b>Channel Name:</b> %s"
msgstr "<b>Nazwa kanału:</b> %s"
+#: ../libpurple/protocols/silc/chat.c:126
#, c-format
msgid "<br><b>User Count:</b> %d"
msgstr "<br><b>Liczba użytkowników:</b> %d"
+#: ../libpurple/protocols/silc/chat.c:133
#, c-format
msgid "<br><b>Channel Founder:</b> %s"
msgstr "<br><b>Założyciel kanału:</b> %s"
+#: ../libpurple/protocols/silc/chat.c:142
#, c-format
msgid "<br><b>Channel Cipher:</b> %s"
msgstr "<br><b>Szyfr kanału:</b> %s"
#. Definition of HMAC: http://en.wikipedia.org/wiki/HMAC
+#: ../libpurple/protocols/silc/chat.c:147
#, c-format
msgid "<br><b>Channel HMAC:</b> %s"
msgstr "<br><b>HMAC kanału:</b> %s"
+#: ../libpurple/protocols/silc/chat.c:152
#, c-format
msgid "<br><b>Channel Topic:</b><br>%s"
msgstr "<br><b>Temat kanału:</b><br>%s"
-#, c-format
+#: ../libpurple/protocols/silc/chat.c:157
msgid "<br><b>Channel Modes:</b> "
msgstr "<br><b>Tryby kanału:</b> "
+#: ../libpurple/protocols/silc/chat.c:171
#, c-format
msgid "<br><b>Founder Key Fingerprint:</b><br>%s"
msgstr "<br><b>Odcisk klucza założyciela:</b><br>%s"
+#: ../libpurple/protocols/silc/chat.c:172
#, c-format
msgid "<br><b>Founder Key Babbleprint:</b><br>%s"
msgstr "<br><b>Czytelny odcisk klucza założyciela:</b><br>%s"
+#: ../libpurple/protocols/silc/chat.c:243
msgid "Add Channel Public Key"
msgstr "Dodaj klucz publiczny kanału"
#. Add new public key
+#: ../libpurple/protocols/silc/chat.c:311
msgid "Open Public Key..."
msgstr "Otwórz klucz publiczny..."
+#: ../libpurple/protocols/silc/chat.c:430
msgid "Channel Passphrase"
msgstr "Hasło kanału"
+#: ../libpurple/protocols/silc/chat.c:437
msgid "Channel Public Keys List"
msgstr "Lista kluczy publicznych kanału"
+#: ../libpurple/protocols/silc/chat.c:442
#, c-format
msgid ""
"Channel authentication is used to secure the channel from unauthorized "
"access. The authentication may be based on passphrase and digital "
"signatures. If passphrase is set, it is required to be able to join. If "
-"channel public keys are set then only users whose public keys are listed are "
-"able to join."
-msgstr ""
-"Uwierzytelnienie kanału jest używane do zabezpieczenia przed wstępem osób "
-"nieupoważnionych. Może być oparta o hasła lub podpisy cyfrowe. Jeżeli "
-"zostanie ustawione hasło, wymagane jest podanie go, aby móc dołączyć. Jeśli "
-"zostaną ustawione klucze publiczne, wstęp będą miały tylko osoby, których "
-"klucze znajdują się na liście."
-
+"channel public keys are set then only users whose public keys are listed are"
+" able to join."
+msgstr "Uwierzytelnienie kanału jest używane do zabezpieczenia przed wstępem osób nieupoważnionych. Może być oparta o hasła lub podpisy cyfrowe. Jeżeli zostanie ustawione hasło, wymagane jest podanie go, aby móc dołączyć. Jeśli zostaną ustawione klucze publiczne, wstęp będą miały tylko osoby, których klucze znajdują się na liście."
+
+#: ../libpurple/protocols/silc/chat.c:451
+#: ../libpurple/protocols/silc/chat.c:452
+#: ../libpurple/protocols/silc/chat.c:488
+#: ../libpurple/protocols/silc/chat.c:489
+#: ../libpurple/protocols/silc/chat.c:941
msgid "Channel Authentication"
msgstr "Uwierzytelnienie kanału"
+#: ../libpurple/protocols/silc/chat.c:453
+#: ../libpurple/protocols/silc/chat.c:490
msgid "Add / Remove"
msgstr "Dodaj/usuń"
+#: ../libpurple/protocols/silc/chat.c:606
msgid "Group Name"
msgstr "Nazwa grupy"
+#: ../libpurple/protocols/silc/chat.c:610
+#: ../libpurple/protocols/silc/ops.c:1852
+#: ../libpurple/protocols/silc/silc.c:1271
msgid "Passphrase"
msgstr "Hasło"
+#: ../libpurple/protocols/silc/chat.c:621
#, c-format
msgid "Please enter the %s channel private group name and passphrase."
msgstr "Proszę podać nazwę i hasło prywatnej grupy kanału %s."
+#: ../libpurple/protocols/silc/chat.c:623
msgid "Add Channel Private Group"
msgstr "Dodaj nową prywatną grupę kanału"
+#: ../libpurple/protocols/silc/chat.c:755
msgid "User Limit"
msgstr "Ograniczenie użytkowników"
+#: ../libpurple/protocols/silc/chat.c:756
msgid "Set user limit on channel. Set to zero to reset user limit."
-msgstr ""
-"Ustawia ograniczenie użytkowników na kanale. Należy ustawić zero, aby "
-"przywrócić ograniczenie."
+msgstr "Ustawia ograniczenie użytkowników na kanale. Należy ustawić zero, aby przywrócić ograniczenie."
+#: ../libpurple/protocols/silc/chat.c:921
msgid "Invite List"
msgstr "Lista zaproszonych"
+#: ../libpurple/protocols/silc/chat.c:926
msgid "Ban List"
msgstr "Lista zablokowanych"
+#: ../libpurple/protocols/silc/chat.c:934
msgid "Add Private Group"
msgstr "Dodaj prywatną grupę"
+#: ../libpurple/protocols/silc/chat.c:947
msgid "Reset Permanent"
msgstr "Przywróć trwałe"
+#: ../libpurple/protocols/silc/chat.c:952
msgid "Set Permanent"
msgstr "Ustaw trwałe"
+#: ../libpurple/protocols/silc/chat.c:960
msgid "Set User Limit"
msgstr "Ustaw ograniczenie użytkowników"
+#: ../libpurple/protocols/silc/chat.c:966
msgid "Reset Topic Restriction"
msgstr "Przywróć ograniczenie tematu"
+#: ../libpurple/protocols/silc/chat.c:971
msgid "Set Topic Restriction"
msgstr "Ustaw ograniczenie tematu"
+#: ../libpurple/protocols/silc/chat.c:978
msgid "Reset Private Channel"
msgstr "Przywróć kanał prywatny"
+#: ../libpurple/protocols/silc/chat.c:983
msgid "Set Private Channel"
msgstr "Ustaw kanał prywatny"
+#: ../libpurple/protocols/silc/chat.c:990
msgid "Reset Secret Channel"
msgstr "Przywróć tajny kanał"
+#: ../libpurple/protocols/silc/chat.c:995
msgid "Set Secret Channel"
msgstr "Ustaw tajny kanał"
+#: ../libpurple/protocols/silc/chat.c:1056
#, c-format
msgid ""
-"You have to join the %s channel before you are able to join the private group"
+"You have to join the %s channel before you are able to join the private "
+"group"
msgstr "Należy dołączyć do kanału %s przed dołączeniem do prywatnej grupy"
+#: ../libpurple/protocols/silc/chat.c:1058
msgid "Join Private Group"
msgstr "Dołącz do prywatnej grupy"
+#: ../libpurple/protocols/silc/chat.c:1059
msgid "Cannot join private group"
msgstr "Nie można dołączyć do prywatnej grupy"
+#: ../libpurple/protocols/silc/chat.c:1257
+#: ../libpurple/protocols/silc/silc.c:1477
msgid "Call Command"
msgstr "Wywołaj polecenie"
+#: ../libpurple/protocols/silc/chat.c:1258
+#: ../libpurple/protocols/silc/silc.c:1478
msgid "Cannot call command"
msgstr "Nie można wywołać polecenia"
+#: ../libpurple/protocols/silc/chat.c:1258
+#: ../libpurple/protocols/silc/silc.c:1479
msgid "Unknown command"
msgstr "Nieznane polecenie"
+#: ../libpurple/protocols/silc/ft.c:88 ../libpurple/protocols/silc/ft.c:104
+#: ../libpurple/protocols/silc/ft.c:108 ../libpurple/protocols/silc/ft.c:113
+#: ../libpurple/protocols/silc/ft.c:118 ../libpurple/protocols/silc/ft.c:123
+#: ../libpurple/protocols/silc/ft.c:128 ../libpurple/protocols/silc/ft.c:278
+#: ../libpurple/protocols/silc/ft.c:284 ../libpurple/protocols/silc/ft.c:290
+#: ../libpurple/protocols/silc/ft.c:296 ../libpurple/protocols/silc/ft.c:433
msgid "Secure File Transfer"
msgstr "Bezpieczne przesyłanie plików"
+#: ../libpurple/protocols/silc/ft.c:88 ../libpurple/protocols/silc/ft.c:105
+#: ../libpurple/protocols/silc/ft.c:109 ../libpurple/protocols/silc/ft.c:114
+#: ../libpurple/protocols/silc/ft.c:119 ../libpurple/protocols/silc/ft.c:124
+#: ../libpurple/protocols/silc/ft.c:129
msgid "Error during file transfer"
msgstr "Błąd podczas przesyłania pliku"
+#: ../libpurple/protocols/silc/ft.c:89
msgid "Remote disconnected"
msgstr "Rozłączono zdalny"
+#: ../libpurple/protocols/silc/ft.c:110
msgid "Permission denied"
msgstr "Brak dostępu"
+#: ../libpurple/protocols/silc/ft.c:115
msgid "Key agreement failed"
msgstr "Akceptacja klucza się nie powiodła"
+#: ../libpurple/protocols/silc/ft.c:120 ../libpurple/protocols/silc/silc.c:353
msgid "Connection timed out"
msgstr "Przekroczono czas oczekiwania na połączenie"
+#: ../libpurple/protocols/silc/ft.c:125
msgid "Creating connection failed"
msgstr "Utworzenie połączenia się nie powiodło"
+#: ../libpurple/protocols/silc/ft.c:130
msgid "File transfer session does not exist"
msgstr "Sesja przesyłania plików nie istnieje"
+#: ../libpurple/protocols/silc/ft.c:279
msgid "No file transfer session active"
msgstr "Brak aktywnej sesji przesyłania plików"
+#: ../libpurple/protocols/silc/ft.c:285
msgid "File transfer already started"
msgstr "Przesyłanie plików zostało już rozpoczęte"
+#: ../libpurple/protocols/silc/ft.c:291
msgid "Could not perform key agreement for file transfer"
msgstr "Nie można wykonać akceptacji kluczy do przesyłania plików"
+#: ../libpurple/protocols/silc/ft.c:297
msgid "Could not start the file transfer"
msgstr "Nie można rozpocząć przesyłania pliku"
+#: ../libpurple/protocols/silc/ft.c:434
msgid "Cannot send file"
msgstr "Nie można wysłać pliku"
+#: ../libpurple/protocols/silc/ops.c:79
msgid "Error occurred"
msgstr "Wystąpił błąd"
+#: ../libpurple/protocols/silc/ops.c:560 ../libpurple/protocols/silc/ops.c:569
+#: ../libpurple/protocols/silc/ops.c:578
#, c-format
msgid "%s has changed the topic of <I>%s</I> to: %s"
msgstr "Użytkownik %s zmienił temat w <I>%s</I> na: %s"
+#: ../libpurple/protocols/silc/ops.c:644
#, c-format
msgid "<I>%s</I> set channel <I>%s</I> modes to: %s"
msgstr "Użytkownik <I>%s</I> ustawił tryb kanału <I>%s</I> na: %s"
+#: ../libpurple/protocols/silc/ops.c:648
#, c-format
msgid "<I>%s</I> removed all channel <I>%s</I> modes"
msgstr "Użytkownik <I>%s</I> usunął wszystkie tryby kanału <I>%s</I>"
+#: ../libpurple/protocols/silc/ops.c:680
#, c-format
msgid "<I>%s</I> set <I>%s's</I> modes to: %s"
msgstr "Użytkownik <I>%s</I> ustawił tryb kanału <I>%s</I> na: %s"
+#: ../libpurple/protocols/silc/ops.c:688
#, c-format
msgid "<I>%s</I> removed all <I>%s's</I> modes"
msgstr "Użytkownik <I>%s</I> usunął wszystkie tryby kanału <I>%s</I>"
+#: ../libpurple/protocols/silc/ops.c:717
#, c-format
msgid "You have been kicked off <I>%s</I> by <I>%s</I> (%s)"
msgstr "Wyrzucono z kanału <I>%s</I> przez użytkownika <I>%s</I> (%s)"
+#: ../libpurple/protocols/silc/ops.c:744 ../libpurple/protocols/silc/ops.c:749
+#: ../libpurple/protocols/silc/ops.c:754
#, c-format
msgid "You have been killed by %s (%s)"
msgstr "Zabito przez użytkownika %s (%s)"
+#: ../libpurple/protocols/silc/ops.c:775 ../libpurple/protocols/silc/ops.c:780
+#: ../libpurple/protocols/silc/ops.c:785
#, c-format
msgid "Killed by %s (%s)"
msgstr "Zabito przez %s (%s)"
+#: ../libpurple/protocols/silc/ops.c:822
msgid "Server signoff"
msgstr "Rozłączono z serwerem"
+#: ../libpurple/protocols/silc/ops.c:996
msgid "Personal Information"
msgstr "Informacje osobiste"
-msgid "Birth Day"
-msgstr "Data urodzenia"
-
+#: ../libpurple/protocols/silc/ops.c:1027
msgid "Job Role"
msgstr "Stanowisko pracy"
+#: ../libpurple/protocols/silc/ops.c:1031
+#: ../libpurple/protocols/silc/silc.c:1264
msgid "Organization"
msgstr "Organizacja"
+#: ../libpurple/protocols/silc/ops.c:1035
msgid "Unit"
msgstr "Jednostka"
+#: ../libpurple/protocols/silc/ops.c:1059
msgid "Note"
msgstr "Notatka"
+#: ../libpurple/protocols/silc/ops.c:1105
msgid "Join Chat"
msgstr "Dołącz do konferencji"
+#: ../libpurple/protocols/silc/ops.c:1140
#, c-format
msgid "You are channel founder on <I>%s</I>"
msgstr "Użytkownik jest założycielem kanału <I>%s</I>"
+#: ../libpurple/protocols/silc/ops.c:1144
#, c-format
msgid "Channel founder on <I>%s</I> is <I>%s</I>"
msgstr "Założycielem kanału <I>%s</I> jest <I>%s</I>"
+#: ../libpurple/protocols/silc/ops.c:1201
+#: ../libpurple/protocols/silc/ops.c:1347
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1067
msgid "Real Name"
msgstr "Imię i nazwisko"
+#: ../libpurple/protocols/silc/ops.c:1229
msgid "Status Text"
msgstr "Tekst stanu"
+#: ../libpurple/protocols/silc/ops.c:1301
+#: ../libpurple/protocols/silc/ops.c:1372
msgid "Public Key Fingerprint"
msgstr "Odcisk klucza publicznego"
+#: ../libpurple/protocols/silc/ops.c:1302
+#: ../libpurple/protocols/silc/ops.c:1373
msgid "Public Key Babbleprint"
msgstr "Czytelny odcisk klucza publicznego"
+#: ../libpurple/protocols/silc/ops.c:1315
msgid "_More..."
msgstr "_Więcej..."
+#: ../libpurple/protocols/silc/ops.c:1391
+#: ../libpurple/protocols/silc/silc.c:1328
msgid "Detach From Server"
msgstr "Odłącz od serwera"
+#: ../libpurple/protocols/silc/ops.c:1391
msgid "Cannot detach"
msgstr "Nie można odłączyć"
+#: ../libpurple/protocols/silc/ops.c:1412
msgid "Cannot set topic"
msgstr "Nie można ustawić tematu"
+#: ../libpurple/protocols/silc/ops.c:1442
msgid "Failed to change nickname"
msgstr "Zmiana pseudonimu się nie powiodła"
+#: ../libpurple/protocols/silc/ops.c:1496
msgid "Roomlist"
msgstr "Lista pokoi"
+#: ../libpurple/protocols/silc/ops.c:1496
msgid "Cannot get room list"
msgstr "Nie można pobrać listy pokoi"
+#: ../libpurple/protocols/silc/ops.c:1497
msgid "Network is empty"
msgstr "Sieć jest pusta"
+#: ../libpurple/protocols/silc/ops.c:1543
msgid "No public key was received"
msgstr "Nie odebrano żadnego klucza publicznego"
+#: ../libpurple/protocols/silc/ops.c:1556
+#: ../libpurple/protocols/silc/ops.c:1570
msgid "Server Information"
msgstr "Informacje o serwerze"
+#: ../libpurple/protocols/silc/ops.c:1557
msgid "Cannot get server information"
msgstr "Nie można pobrać informacji o serwerze"
+#: ../libpurple/protocols/silc/ops.c:1582
msgid "Server Statistics"
msgstr "Statystyki serwera"
+#: ../libpurple/protocols/silc/ops.c:1583
msgid "Cannot get server statistics"
msgstr "Nie można pobrać statystyk serwera"
+#: ../libpurple/protocols/silc/ops.c:1591
#, c-format
msgid ""
"Local server start time: %s\n"
@@ -9128,359 +12092,421 @@ msgid ""
"Total routers: %d\n"
"Total server operators: %d\n"
"Total router operators: %d\n"
-msgstr ""
-"Czas uruchomienia lokalnego serwera: %s\n"
-"Czas działania lokalnego serwera: %s\n"
-"Klienci lokalnego serwera: %d\n"
-"Kanały lokalnego serwera: %d\n"
-"Operatorzy lokalnego serwera: %d\n"
-"Operatorzy lokalnego routera: %d\n"
-"Klienci lokalnej komórki: %d\n"
-"Kanały lokalnej komórki: %d\n"
-"Serwery lokalnej komórki: %d\n"
-"Razem klientów: %d\n"
-"Razem kanałów: %d\n"
-"Razem serwerów: %d\n"
-"Razem routerów: %d\n"
-"Razem operatorów serwerów: %d\n"
-"Razem operatorów routerów: %d\n"
+msgstr "Czas uruchomienia lokalnego serwera: %s\nCzas działania lokalnego serwera: %s\nKlienci lokalnego serwera: %d\nKanały lokalnego serwera: %d\nOperatorzy lokalnego serwera: %d\nOperatorzy lokalnego routera: %d\nKlienci lokalnej komórki: %d\nKanały lokalnej komórki: %d\nSerwery lokalnej komórki: %d\nRazem klientów: %d\nRazem kanałów: %d\nRazem serwerów: %d\nRazem routerów: %d\nRazem operatorów serwerów: %d\nRazem operatorów routerów: %d\n"
+#: ../libpurple/protocols/silc/ops.c:1623
msgid "Network Statistics"
msgstr "Statystyki sieci"
+#: ../libpurple/protocols/silc/ops.c:1632
+#: ../libpurple/protocols/silc/ops.c:1638
msgid "Ping"
msgstr "Ping"
+#: ../libpurple/protocols/silc/ops.c:1632
msgid "Ping failed"
msgstr "Polecenie ping się nie powiodło"
+#: ../libpurple/protocols/silc/ops.c:1638
msgid "Ping reply received from server"
msgstr "Odebrano odpowiedź ping od serwera"
+#: ../libpurple/protocols/silc/ops.c:1646
msgid "Could not kill user"
msgstr "Nie można zabić użytkownika"
+#: ../libpurple/protocols/silc/ops.c:1686
msgid "WATCH"
msgstr "OBSERWUJ"
+#: ../libpurple/protocols/silc/ops.c:1686
msgid "Cannot watch user"
msgstr "Nie można obserwować użytkownika"
+#: ../libpurple/protocols/silc/ops.c:1763
+#: ../libpurple/protocols/silc/ops.c:1814
+#: ../libpurple/protocols/silc/silc.c:397
msgid "Resuming session"
msgstr "Wznawianie sesji"
+#: ../libpurple/protocols/silc/ops.c:1765
msgid "Authenticating connection"
msgstr "Uwierzytelnianie połączenia"
+#: ../libpurple/protocols/silc/ops.c:1816
msgid "Verifying server public key"
msgstr "Sprawdzanie klucza publicznego serwera"
+#: ../libpurple/protocols/silc/ops.c:1853
msgid "Passphrase required"
msgstr "Hasło jest wymagane"
+#: ../libpurple/protocols/silc/pk.c:102
#, c-format
msgid ""
-"Received %s's public key. Your local copy does not match this key. Would you "
-"still like to accept this public key?"
-msgstr ""
-"Odebrano klucz publiczny użytkownika %s. Lokalna kopia nie pasuje od tego "
-"klucza. Zaakceptować ten klucz publiczny?"
+"Received %s's public key. Your local copy does not match this key. Would you"
+" still like to accept this public key?"
+msgstr "Odebrano klucz publiczny użytkownika %s. Lokalna kopia nie pasuje od tego klucza. Zaakceptować ten klucz publiczny?"
+#: ../libpurple/protocols/silc/pk.c:107
#, c-format
msgid "Received %s's public key. Would you like to accept this public key?"
msgstr "Odebrano klucz publiczny użytkownika %s. Zaakceptować go?"
+#: ../libpurple/protocols/silc/pk.c:111
#, c-format
msgid ""
"Fingerprint and babbleprint for the %s key are:\n"
"\n"
"%s\n"
"%s\n"
-msgstr ""
-"Odcisk i czytelny odcisk klucza %s:\n"
-"\n"
-"%s\n"
-"%s\n"
+msgstr "Odcisk i czytelny odcisk klucza %s:\n\n%s\n%s\n"
+#: ../libpurple/protocols/silc/pk.c:114 ../libpurple/protocols/silc/pk.c:143
msgid "Verify Public Key"
msgstr "Sprawdź klucz publiczny"
+#: ../libpurple/protocols/silc/pk.c:119
msgid "_View..."
msgstr "_Wyświetl..."
+#: ../libpurple/protocols/silc/pk.c:144
msgid "Unsupported public key type"
msgstr "Nieobsługiwany typ klucza publicznego"
+#: ../libpurple/protocols/silc/silc.c:322
msgid "Disconnected by server"
msgstr "Rozłączono przez serwer"
+#: ../libpurple/protocols/silc/silc.c:330
msgid "Error connecting to SILC Server"
msgstr "Błąd podczas łączenia z serwerem SILC"
+#: ../libpurple/protocols/silc/silc.c:336
msgid "Key Exchange failed"
msgstr "Wymiana kluczy się nie powiodła"
+#: ../libpurple/protocols/silc/silc.c:346
msgid ""
"Resuming detached session failed. Press Reconnect to create new connection."
-msgstr ""
-"Wznowienie odłączonej sesji się nie powiodło. Naciśnięcie przycisku Połącz "
-"ponownie utworzy nowe połączenie."
+msgstr "Wznowienie odłączonej sesji się nie powiodło. Naciśnięcie przycisku Połącz ponownie utworzy nowe połączenie."
+#: ../libpurple/protocols/silc/silc.c:400
msgid "Performing key exchange"
msgstr "Wykonywanie wymiany kluczy"
+#: ../libpurple/protocols/silc/silc.c:496
+#: ../libpurple/protocols/silc/silc.c:513
+#: ../libpurple/protocols/silc/silc.c:542
msgid "Unable to load SILC key pair"
msgstr "Nie można wczytać pary kluczy SILC"
#. Progress
+#: ../libpurple/protocols/silc/silc.c:527
msgid "Connecting to SILC Server"
msgstr "Łączenie z serwerem SILC"
+#: ../libpurple/protocols/silc/silc.c:573
msgid "Out of memory"
msgstr "Brak pamięci"
+#: ../libpurple/protocols/silc/silc.c:624
msgid "Unable to initialize SILC protocol"
msgstr "Nie można zainicjować protokołu SILC"
+#: ../libpurple/protocols/silc/silc.c:637
msgid "Error loading SILC key pair"
msgstr "Błąd podczas wczytywania pary kluczy SILC"
+#: ../libpurple/protocols/silc/silc.c:696
+#: ../libpurple/protocols/silc/silc.c:1890
#, c-format
msgid "Download %s: %s"
msgstr "Pobierz %s: %s"
+#: ../libpurple/protocols/silc/silc.c:987
msgid "Your Current Mood"
msgstr "Aktualny nastrój"
+#: ../libpurple/protocols/silc/silc.c:989 ../pidgin/gtkprefs.c:3097
+#: ../pidgin/gtkwebviewtoolbar.c:1433
#, c-format
msgid "Normal"
msgstr "Normalny"
+#: ../libpurple/protocols/silc/silc.c:1014
msgid ""
"\n"
"Your Preferred Contact Methods"
-msgstr ""
-"\n"
-"Preferowane metody kontaktu"
+msgstr "\nPreferowane metody kontaktu"
+#: ../libpurple/protocols/silc/silc.c:1022
+#: ../libpurple/protocols/silc/util.c:526
msgid "SMS"
msgstr "SMS"
+#: ../libpurple/protocols/silc/silc.c:1024
+#: ../libpurple/protocols/silc/util.c:528
msgid "MMS"
msgstr "MMS"
+#: ../libpurple/protocols/silc/silc.c:1026
msgid "Video conferencing"
msgstr "Konferencje wideo"
+#: ../libpurple/protocols/silc/silc.c:1031
msgid "Your Current Status"
msgstr "Aktualny stan"
+#: ../libpurple/protocols/silc/silc.c:1038
msgid "Online Services"
msgstr "Usługi online"
+#: ../libpurple/protocols/silc/silc.c:1041
msgid "Let others see what services you are using"
msgstr "Inni użytkownicy mogą widzieć używane usługi"
+#: ../libpurple/protocols/silc/silc.c:1047
msgid "Let others see what computer you are using"
msgstr "Inni użytkownicy mogą widzieć informacje o komputerze"
+#: ../libpurple/protocols/silc/silc.c:1054
msgid "Your VCard File"
msgstr "Plik vCard"
+#: ../libpurple/protocols/silc/silc.c:1060
msgid "Timezone (UTC)"
msgstr "Strefa czasowa (UTC)"
+#: ../libpurple/protocols/silc/silc.c:1064
+#: ../libpurple/protocols/silc/silc.c:1065
msgid "User Online Status Attributes"
msgstr "Atrybuty stanu użytkowników w trybie online"
+#: ../libpurple/protocols/silc/silc.c:1066
msgid ""
-"You can let other users see your online status information and your personal "
-"information. Please fill the information you would like other users to see "
+"You can let other users see your online status information and your personal"
+" information. Please fill the information you would like other users to see "
"about yourself."
-msgstr ""
-"Można pozwolić innym użytkownikom na podgląd informacji stanu i informacji "
-"osobistych. Proszę wypełnić informacje o sobie, które mają widzieć inni "
-"użytkownicy."
+msgstr "Można pozwolić innym użytkownikom na podgląd informacji stanu i informacji osobistych. Proszę wypełnić informacje o sobie, które mają widzieć inni użytkownicy."
+#: ../libpurple/protocols/silc/silc.c:1106
+#: ../libpurple/protocols/silc/silc.c:1114
+#: ../libpurple/protocols/silc/silc.c:1749
msgid "Message of the Day"
msgstr "Wiadomość dnia"
+#: ../libpurple/protocols/silc/silc.c:1106
msgid "No Message of the Day available"
msgstr "Brak wiadomości dnia"
+#: ../libpurple/protocols/silc/silc.c:1107
+#: ../libpurple/protocols/silc/silc.c:1744
msgid "There is no Message of the Day associated with this connection"
msgstr "Brak wiadomości dnia powiązanej z tym połączeniem"
+#: ../libpurple/protocols/silc/silc.c:1159
+#: ../libpurple/protocols/silc/silc.c:1206
+#: ../libpurple/protocols/silc/silc.c:1279
+#: ../libpurple/protocols/silc/silc.c:1280
msgid "Create New SILC Key Pair"
msgstr "Utwórz nową parę kluczy SILC"
-msgid "Passphrases do not match"
-msgstr "Hasła nie pasują"
-
+#: ../libpurple/protocols/silc/silc.c:1207
msgid "Key Pair Generation failed"
msgstr "Utworzenie pary kluczy się nie powiodło"
+#: ../libpurple/protocols/silc/silc.c:1247
msgid "Key length"
msgstr "Długość klucza"
+#: ../libpurple/protocols/silc/silc.c:1249
msgid "Public key file"
msgstr "Plik klucza publicznego"
+#: ../libpurple/protocols/silc/silc.c:1251
msgid "Private key file"
msgstr "Plik klucza prywatnego"
+#: ../libpurple/protocols/silc/silc.c:1274
msgid "Passphrase (retype)"
msgstr "Hasło (ponownie)"
+#: ../libpurple/protocols/silc/silc.c:1281
msgid "Generate Key Pair"
msgstr "Utwórz parę kluczy"
+#: ../libpurple/protocols/silc/silc.c:1324
msgid "Online Status"
msgstr "Stan trybu online"
+#: ../libpurple/protocols/silc/silc.c:1332
msgid "View Message of the Day"
msgstr "Wyświetl wiadomość dnia"
+#: ../libpurple/protocols/silc/silc.c:1336
msgid "Create SILC Key Pair..."
msgstr "Utwórz parę kluczy SILC..."
+#: ../libpurple/protocols/silc/silc.c:1431
#, c-format
msgid "User <I>%s</I> is not present in the network"
msgstr "Użytkownik <I>%s</I> nie jest obecny w sieci"
+#: ../libpurple/protocols/silc/silc.c:1617
msgid "Topic too long"
msgstr "Temat jest za długi"
+#: ../libpurple/protocols/silc/silc.c:1699
msgid "You must specify a nick"
msgstr "Należy podać pseudonim"
+#: ../libpurple/protocols/silc/silc.c:1803
#, c-format
msgid "channel %s not found"
msgstr "nie odnaleziono kanału %s"
+#: ../libpurple/protocols/silc/silc.c:1808
#, c-format
msgid "channel modes for %s: %s"
msgstr "tryby kanału %s: %s"
+#: ../libpurple/protocols/silc/silc.c:1810
#, c-format
msgid "no channel modes are set on %s"
msgstr "nie ustawiono trybów kanału %s"
+#: ../libpurple/protocols/silc/silc.c:1822
#, c-format
msgid "Failed to set cmodes for %s"
msgstr "Ustawienie cmodes dla %s się nie powiodło"
+#: ../libpurple/protocols/silc/silc.c:1852
#, c-format
msgid "Unknown command: %s, (may be a client bug)"
msgstr "Nieznane polecenie: %s (może być błędem klienta)"
+#: ../libpurple/protocols/silc/silc.c:1933
msgid "part [channel]: Leave the chat"
msgstr "part [kanał]: opuszcza konferencję"
+#: ../libpurple/protocols/silc/silc.c:1937
msgid "leave [channel]: Leave the chat"
msgstr "leave [kanał]: opuszcza konferencję"
+#: ../libpurple/protocols/silc/silc.c:1941
msgid "topic [&lt;new topic&gt;]: View or change the topic"
msgstr "topic [&lt;nowy temat&gt;]: wyświetla lub zmienia temat"
+#: ../libpurple/protocols/silc/silc.c:1946
msgid "join &lt;channel&gt; [&lt;password&gt;]: Join a chat on this network"
msgstr "join &lt;kanał&gt; [&lt;hasło&gt;]: dołącza do kanału w tej sieci"
+#: ../libpurple/protocols/silc/silc.c:1950
msgid "list: List channels on this network"
msgstr "list: wyświetla listę kanałów w tej sieci"
+#: ../libpurple/protocols/silc/silc.c:1954
msgid "whois &lt;nick&gt;: View nick's information"
msgstr "whois &lt;pseudonim&gt;: wyświetla informacje o pseudonimie"
+#: ../libpurple/protocols/silc/silc.c:1958
+#: ../libpurple/protocols/zephyr/zephyr.c:2738
msgid "msg &lt;nick&gt; &lt;message&gt;: Send a private message to a user"
-msgstr ""
-"msg &lt;pseudonim&gt; &lt;wiadomość&gt;: wysyła prywatną wiadomość do "
-"użytkownika"
+msgstr "msg &lt;pseudonim&gt; &lt;wiadomość&gt;: wysyła prywatną wiadomość do użytkownika"
-msgid "query &lt;nick&gt; [&lt;message&gt;]: Send a private message to a user"
-msgstr ""
-"query &lt;pseudonim&gt; [&lt;wiadomość&gt;]: wysyła prywatną wiadomość do "
-"użytkownika"
+#: ../libpurple/protocols/silc/silc.c:1962
+msgid ""
+"query &lt;nick&gt; [&lt;message&gt;]: Send a private message to a user"
+msgstr "query &lt;pseudonim&gt; [&lt;wiadomość&gt;]: wysyła prywatną wiadomość do użytkownika"
+#: ../libpurple/protocols/silc/silc.c:1966
msgid "motd: View the server's Message Of The Day"
msgstr "motd: wyświetla wiadomość dnia serwera"
+#: ../libpurple/protocols/silc/silc.c:1970
msgid "detach: Detach this session"
msgstr "detach: odłącza tę sesję"
+#: ../libpurple/protocols/silc/silc.c:1974
msgid "quit [message]: Disconnect from the server, with an optional message"
msgstr "quit [wiadomość]: rozłącza z serwerem, z opcjonalną wiadomością"
+#: ../libpurple/protocols/silc/silc.c:1978
msgid "call &lt;command&gt;: Call any silc client command"
msgstr "call &lt;polecenie&gt;: wywołuje dowolne polecenie klienta SILC"
+#: ../libpurple/protocols/silc/silc.c:1984
msgid "kill &lt;nick&gt; [-pubkey|&lt;reason&gt;]: Kill nick"
msgstr "kill &lt;pseudonim&gt; [-pubkey|&lt;powód&gt;]: zabija pseudonim"
+#: ../libpurple/protocols/silc/silc.c:1988
msgid "nick &lt;newnick&gt;: Change your nickname"
msgstr "nick &lt;nowy_pseudonim&gt;: zmienia pseudonim"
+#: ../libpurple/protocols/silc/silc.c:1992
msgid "whowas &lt;nick&gt;: View nick's information"
msgstr "whowas &lt;pseudonim&gt;: wyświetla informacje o pseudonimie"
+#: ../libpurple/protocols/silc/silc.c:1996
msgid ""
"cmode &lt;channel&gt; [+|-&lt;modes&gt;] [arguments]: Change or display "
"channel modes"
-msgstr ""
-"cmode &lt;kanał&gt; [+|-&lt;tryby&gt;] [parametry]: zmienia lub wyświetla "
-"tryby kanału"
+msgstr "cmode &lt;kanał&gt; [+|-&lt;tryby&gt;] [parametry]: zmienia lub wyświetla tryby kanału"
+#: ../libpurple/protocols/silc/silc.c:2000
msgid ""
"cumode &lt;channel&gt; +|-&lt;modes&gt; &lt;nick&gt;: Change nick's modes "
"on channel"
-msgstr ""
-"cmode &lt;kanał&gt; +|-&lt;tryby&gt; &lt;pseudonim&gt;: zmienia tryby "
-"pseudonimu na kanale"
+msgstr "cmode &lt;kanał&gt; +|-&lt;tryby&gt; &lt;pseudonim&gt;: zmienia tryby pseudonimu na kanale"
+#: ../libpurple/protocols/silc/silc.c:2004
msgid "umode &lt;usermodes&gt;: Set your modes in the network"
msgstr "umode &lt;tryby_użytkownika&gt;: ustawia tryby użytkownika w sieci"
+#: ../libpurple/protocols/silc/silc.c:2008
msgid "oper &lt;nick&gt; [-pubkey]: Get server operator privileges"
-msgstr ""
-"oper &lt;pseudonim&gt; [-pubkey]: uzyskuje uprawnienia operatora serwera"
+msgstr "oper &lt;pseudonim&gt; [-pubkey]: uzyskuje uprawnienia operatora serwera"
+#: ../libpurple/protocols/silc/silc.c:2012
msgid ""
"invite &lt;channel&gt; [-|+]&lt;nick&gt;: invite nick or add/remove from "
"channel invite list"
-msgstr ""
-"invite &lt;kanał&gt; [-|+]&lt;pseudonim&gt;: zaprasza pseudonim lub dodaje/"
-"usuwa go z listy osób zaproszonych na kanał"
+msgstr "invite &lt;kanał&gt; [-|+]&lt;pseudonim&gt;: zaprasza pseudonim lub dodaje/usuwa go z listy osób zaproszonych na kanał"
+#: ../libpurple/protocols/silc/silc.c:2016
msgid "kick &lt;channel&gt; &lt;nick&gt; [comment]: Kick client from channel"
-msgstr ""
-"kick &lt;kanał&gt; &lt;pseudonim&gt; [komentarz]: wyrzuca klienta z kanału"
+msgstr "kick &lt;kanał&gt; &lt;pseudonim&gt; [komentarz]: wyrzuca klienta z kanału"
+#: ../libpurple/protocols/silc/silc.c:2020
msgid "info [server]: View server administrative details"
-msgstr ""
-"info [serwer]: wyświetla szczegółowe informacje administracyjne serwera"
+msgstr "info [serwer]: wyświetla szczegółowe informacje administracyjne serwera"
+#: ../libpurple/protocols/silc/silc.c:2024
msgid "ban [&lt;channel&gt; +|-&lt;nick&gt;]: Ban client from channel"
msgstr "ban [&lt;kanał&gt; +|-&lt;pseudonim&gt;]: blokuje klienta w kanale"
+#: ../libpurple/protocols/silc/silc.c:2028
msgid "getkey &lt;nick|server&gt;: Retrieve client's or server's public key"
-msgstr ""
-"getkey &lt;pseudonim|serwer&gt;: pobiera klucz publiczny klienta lub serwera"
+msgstr "getkey &lt;pseudonim|serwer&gt;: pobiera klucz publiczny klienta lub serwera"
+#: ../libpurple/protocols/silc/silc.c:2032
msgid "stats: View server and network statistics"
msgstr "stats: wyświetla statystyki serwera i sieci"
+#: ../libpurple/protocols/silc/silc.c:2036
msgid "ping: Send PING to the connected server"
msgstr "ping: wysyła PING do połączonego serwera"
+#: ../libpurple/protocols/silc/silc.c:2041
msgid "users &lt;channel&gt;: List users in channel"
msgstr "users &lt;kanał&gt;: wyświetla użytkowników na kanale"
+#: ../libpurple/protocols/silc/silc.c:2045
msgid ""
"names [-count|-ops|-halfops|-voices|-normal] &lt;channel(s)&gt;: List "
"specific users in channel(s)"
-msgstr ""
-"names [-count|-ops|-halfops|-voices|-normal] &lt;kanały&gt;: wyświetla listę "
-"podanych użytkowników na kanałach"
+msgstr "names [-count|-ops|-halfops|-voices|-normal] &lt;kanały&gt;: wyświetla listę podanych użytkowników na kanałach"
#. *< type
#. *< ui_requirement
@@ -9491,211 +12517,189 @@ msgstr ""
#. *< name
#. *< version
#. * summary
+#: ../libpurple/protocols/silc/silc.c:2159
msgid "SILC Protocol Plugin"
msgstr "Wtyczka protokołu SILC"
#. * description
+#: ../libpurple/protocols/silc/silc.c:2161
msgid "Secure Internet Live Conferencing (SILC) Protocol"
msgstr "Protokół SILC (Secure Internet Live Conferencing)"
+#: ../libpurple/protocols/silc/silc.c:2200 ../pidgin/gtkprefs.c:4168
msgid "Network"
msgstr "Sieć"
+#: ../libpurple/protocols/silc/silc.c:2211
msgid "Public Key file"
msgstr "Plik klucza publicznego"
+#: ../libpurple/protocols/silc/silc.c:2215
msgid "Private Key file"
msgstr "Plik klucza prywatnego"
+#: ../libpurple/protocols/silc/silc.c:2225
msgid "Cipher"
msgstr "Szyfr"
+#: ../libpurple/protocols/silc/silc.c:2235
msgid "HMAC"
msgstr "HMAC"
+#: ../libpurple/protocols/silc/silc.c:2238
msgid "Use Perfect Forward Secrecy"
msgstr "Użycie doskonałej magii przekierowania"
+#: ../libpurple/protocols/silc/silc.c:2242
msgid "Public key authentication"
msgstr "Uwierzytelnianie kluczem publicznym"
+#: ../libpurple/protocols/silc/silc.c:2245
msgid "Block IMs without Key Exchange"
msgstr "Blokowanie wiadomości bez wymiany klucza"
+#: ../libpurple/protocols/silc/silc.c:2248
msgid "Block messages to whiteboard"
msgstr "Blokowanie wiadomości do tablicy"
+#: ../libpurple/protocols/silc/silc.c:2251
msgid "Automatically open whiteboard"
msgstr "Automatyczne otwieranie tablicy"
+#: ../libpurple/protocols/silc/silc.c:2254
msgid "Digitally sign and verify all messages"
msgstr "Cyfrowe podpisywanie i sprawdzanie wszystkich wiadomości"
+#: ../libpurple/protocols/silc/util.c:163
+#: ../libpurple/protocols/silc/util.c:205
msgid "Creating SILC key pair..."
msgstr "Tworzenie pary kluczy SILC..."
+#: ../libpurple/protocols/silc/util.c:171
+#: ../libpurple/protocols/silc/util.c:213
msgid "Unable to create SILC key pair"
msgstr "Nie można utworzyć pary kluczy SILC"
#. Hint for translators: Please check the tabulator width here and in
#. the next strings (short strings: 2 tabs, longer strings 1 tab,
#. sum: 3 tabs or 24 characters)
+#: ../libpurple/protocols/silc/util.c:322
#, c-format
msgid "Real Name: \t%s\n"
msgstr "Imię i nazwisko: \t%s\n"
+#: ../libpurple/protocols/silc/util.c:324
#, c-format
msgid "User Name: \t%s\n"
msgstr "Nazwa użytkownika: \t%s\n"
+#: ../libpurple/protocols/silc/util.c:326
#, c-format
msgid "Email: \t\t%s\n"
msgstr "Adres e-mail: \t\t%s\n"
+#: ../libpurple/protocols/silc/util.c:328
#, c-format
msgid "Host Name: \t%s\n"
msgstr "Nazwa komputera: \t%s\n"
+#: ../libpurple/protocols/silc/util.c:330
#, c-format
msgid "Organization: \t%s\n"
msgstr "Organizacja: \t%s\n"
+#: ../libpurple/protocols/silc/util.c:332
#, c-format
msgid "Country: \t%s\n"
msgstr "Kraj: \t%s\n"
+#: ../libpurple/protocols/silc/util.c:333
#, c-format
msgid "Algorithm: \t%s\n"
msgstr "Algorytm: \t%s\n"
+#: ../libpurple/protocols/silc/util.c:334
#, c-format
msgid "Key Length: \t%d bits\n"
msgstr "Długość klucza: \t%d bity\n"
+#: ../libpurple/protocols/silc/util.c:336
#, c-format
msgid "Version: \t%s\n"
msgstr "Wersja: \t%s\n"
+#: ../libpurple/protocols/silc/util.c:338
#, c-format
msgid ""
"Public Key Fingerprint:\n"
"%s\n"
"\n"
-msgstr ""
-"Odcisk klucza publicznego:\n"
-"%s\n"
-"\n"
+msgstr "Odcisk klucza publicznego:\n%s\n\n"
+#: ../libpurple/protocols/silc/util.c:339
#, c-format
msgid ""
"Public Key Babbleprint:\n"
"%s"
-msgstr ""
-"Czytelny odcisk klucza publicznego:\n"
-"%s"
+msgstr "Czytelny odcisk klucza publicznego:\n%s"
+#: ../libpurple/protocols/silc/util.c:341
+#: ../libpurple/protocols/silc/util.c:342
msgid "Public Key Information"
msgstr "Informacje o kluczu publicznym"
+#: ../libpurple/protocols/silc/util.c:524
msgid "Paging"
msgstr "Stronicowanie"
+#: ../libpurple/protocols/silc/util.c:530
msgid "Video Conferencing"
msgstr "Konferencje wideo"
+#: ../libpurple/protocols/silc/util.c:548
msgid "Computer"
msgstr "Komputer"
+#: ../libpurple/protocols/silc/util.c:552
msgid "PDA"
msgstr "PDA"
+#: ../libpurple/protocols/silc/util.c:554
msgid "Terminal"
msgstr "Terminal"
+#: ../libpurple/protocols/silc/wb.c:290
#, c-format
msgid "%s sent message to whiteboard. Would you like to open the whiteboard?"
msgstr "Użytkownik %s wysłał wiadomość do tablicy. Otworzyć ją?"
+#: ../libpurple/protocols/silc/wb.c:294
#, c-format
msgid ""
"%s sent message to whiteboard on %s channel. Would you like to open the "
"whiteboard?"
msgstr "Użytkownik %s wysłał wiadomość do tablicy kanału %s. Otworzyć ją?"
+#: ../libpurple/protocols/silc/wb.c:308 ../pidgin/gtkwhiteboard.c:847
msgid "Whiteboard"
msgstr "Tablica"
-msgid "No server statistics available"
-msgstr "Brak dostępnych statystyk serwera"
-
-msgid "Error during connecting to SILC Server"
-msgstr "Błąd podczas łączenia z serwerem SILC"
-
-#, c-format
-msgid "Failure: Version mismatch, upgrade your client"
-msgstr "Błąd: niezgodna wersja, należy zaktualizować klienta"
-
-#, c-format
-msgid "Failure: Remote does not trust/support your public key"
-msgstr "Błąd: zdalny nie ufa/nie obsługuje klucza publicznego"
-
-#, c-format
-msgid "Failure: Remote does not support proposed KE group"
-msgstr "Błąd: zdalny nie obsługuje zaproponowanej grupy KE"
-
-#, c-format
-msgid "Failure: Remote does not support proposed cipher"
-msgstr "Błąd: zdalny nie obsługuje zaproponowanego szyfru"
-
-#, c-format
-msgid "Failure: Remote does not support proposed PKCS"
-msgstr "Błąd: zdalny nie obsługuje zaproponowanego PKCS"
-
-#, c-format
-msgid "Failure: Remote does not support proposed hash function"
-msgstr "Błąd: zdalny nie obsługuje zaproponowanej funkcji mieszającej"
-
-#, c-format
-msgid "Failure: Remote does not support proposed HMAC"
-msgstr "Błąd: zdalny nie obsługuje zaproponowanego HMAC"
-
-#, c-format
-msgid "Failure: Incorrect signature"
-msgstr "Błąd: niepoprawny podpis"
-
-#, c-format
-msgid "Failure: Invalid cookie"
-msgstr "Błąd: nieprawidłowe ciasteczko"
-
-#, c-format
-msgid "Failure: Authentication failed"
-msgstr "Błąd: uwierzytelnienie się nie powiodło"
-
-msgid "Unable to initialize SILC Client connection"
-msgstr "Nie można zainicjować połączenia klienta SILC"
-
-msgid "John Noname"
-msgstr "Jan Bezimienny"
-
-#, c-format
-msgid "Unable to load SILC key pair: %s"
-msgstr "Nie można wczytać pary kluczy SILC: %s"
-
-msgid "Unable to create connection"
-msgstr "Nie można utworzyć połączenia"
-
+#: ../libpurple/protocols/simple/simple.c:1147
msgid "Unknown server response"
msgstr "Nieznana odpowiedź serwera"
+#: ../libpurple/protocols/simple/simple.c:1782
+#: ../libpurple/protocols/simple/simple.c:1832
+#: ../libpurple/protocols/simple/simple.c:1847
+#: ../libpurple/protocols/simple/simple.c:1902
msgid "Unable to create listen socket"
msgstr "Nie można utworzyć gniazda nasłuchiwania"
-msgid "Unable to resolve hostname"
-msgstr "Nie można uzyskać nazwy komputera"
-
+#: ../libpurple/protocols/simple/simple.c:1933
msgid "SIP usernames may not contain whitespaces or @ symbols"
msgstr "Nazwy użytkowników SIP nie mogą zawierać spacji ani symboli @"
+#: ../libpurple/protocols/simple/simple.c:1953
msgid "SIP connect server not specified"
msgstr "Nie podano serwera połączenia SIP"
@@ -9707,40 +12711,55 @@ msgstr "Nie podano serwera połączenia SIP"
#. *< id
#. *< name
#. *< version
+#: ../libpurple/protocols/simple/simple.c:2140
msgid "SIP/SIMPLE Protocol Plugin"
msgstr "Wtyczka protokołu SIP/SIMPLE"
#. * summary
+#: ../libpurple/protocols/simple/simple.c:2141
msgid "The SIP/SIMPLE Protocol Plugin"
msgstr "Wtyczka protokołu SIP/SIMPLE"
+#: ../libpurple/protocols/simple/simple.c:2169
msgid "Publish status (note: everyone may watch you)"
msgstr "Publikacja stanu (uwaga: każdy będzie mógł obserwować użytkownika)"
+#: ../libpurple/protocols/simple/simple.c:2175
msgid "Use UDP"
msgstr "Użycie UDP"
+#: ../libpurple/protocols/simple/simple.c:2177
msgid "Use proxy"
msgstr "Użycie pośrednika"
+#: ../libpurple/protocols/simple/simple.c:2179 ../pidgin/gtkprefs.c:4169
msgid "Proxy"
msgstr "Pośrednik"
+#: ../libpurple/protocols/simple/simple.c:2181
msgid "Auth User"
msgstr "Uwierzytelnienie użytkownika"
+#: ../libpurple/protocols/simple/simple.c:2183
msgid "Auth Domain"
msgstr "Domena uwierzytelnienia"
+#: ../libpurple/protocols/yahoo/libyahoo.c:44
+#: ../libpurple/protocols/yahoo/libyahoojp.c:41
msgid "join &lt;room&gt;: Join a chat room on the Yahoo network"
msgstr "join &lt;pokój&gt;: dołącza do pokoju konferencji w sieci Yahoo!"
+#: ../libpurple/protocols/yahoo/libyahoo.c:49
+#: ../libpurple/protocols/yahoo/libyahoojp.c:46
msgid "list: List rooms on the Yahoo network"
msgstr "list: wyświetla listę pokoi w sieci Yahoo!"
+#: ../libpurple/protocols/yahoo/libyahoo.c:57
+#: ../libpurple/protocols/yahoo/libyahoojp.c:54
msgid "doodle: Request user to start a Doodle session"
msgstr "doodle: prosi użytkownika o rozpoczęcia sesji Doodle"
+#: ../libpurple/protocols/yahoo/libyahoo.c:166
msgid "Yahoo ID..."
msgstr "Identyfikator Yahoo!..."
@@ -9754,30 +12773,42 @@ msgstr "Identyfikator Yahoo!..."
#. *< version
#. * summary
#. * description
+#: ../libpurple/protocols/yahoo/libyahoo.c:284
+#: ../libpurple/protocols/yahoo/libyahoo.c:286
msgid "Yahoo! Protocol Plugin"
msgstr "Wtyczka protokołu Yahoo!"
+#: ../libpurple/protocols/yahoo/libyahoo.c:309
+#: ../libpurple/protocols/yahoo/libyahoojp.c:207
msgid "Pager port"
msgstr "Port pagera"
+#: ../libpurple/protocols/yahoo/libyahoo.c:312
+#: ../libpurple/protocols/yahoo/libyahoojp.c:210
msgid "File transfer server"
msgstr "Serwer przesyłania plików"
-msgid "File transfer port"
-msgstr "Port przesyłania plików"
-
+#: ../libpurple/protocols/yahoo/libyahoo.c:315
+#: ../libpurple/protocols/yahoo/libyahoojp.c:213
msgid "Chat room locale"
msgstr "Lokalizacja pokoju konferencji"
+#: ../libpurple/protocols/yahoo/libyahoo.c:321
+#: ../libpurple/protocols/yahoo/libyahoojp.c:219
msgid "Ignore conference and chatroom invitations"
msgstr "Ignorowanie zaproszeń do konferencji"
+#: ../libpurple/protocols/yahoo/libyahoo.c:325
+#: ../libpurple/protocols/yahoo/libyahoojp.c:223
msgid "Use account proxy for HTTP and HTTPS connections"
msgstr "Użycie pośrednika konta dla połączeń HTTP i HTTPS"
+#: ../libpurple/protocols/yahoo/libyahoo.c:328
+#: ../libpurple/protocols/yahoo/libyahoojp.c:226
msgid "Chat room list URL"
msgstr "Adres URL listy pokoi konferencji"
+#: ../libpurple/protocols/yahoo/libyahoojp.c:62
msgid "Yahoo JAPAN ID..."
msgstr "Identyfikator Yahoo! Japan..."
@@ -9791,453 +12822,514 @@ msgstr "Identyfikator Yahoo! Japan..."
#. *< version
#. * summary
#. * description
+#: ../libpurple/protocols/yahoo/libyahoojp.c:182
+#: ../libpurple/protocols/yahoo/libyahoojp.c:184
msgid "Yahoo! JAPAN Protocol Plugin"
msgstr "Wtyczka protokołu Yahoo! Japan"
+#: ../libpurple/protocols/yahoo/libymsg.c:929
#, c-format
msgid "%s has sent you a webcam invite, which is not yet supported."
-msgstr ""
-"Użytkownik %s wysyłał zaproszenie kamery internetowej, które nie jest "
-"jeszcze obsługiwane."
+msgstr "Użytkownik %s wysyłał zaproszenie kamery internetowej, które nie jest jeszcze obsługiwane."
+#: ../libpurple/protocols/yahoo/libymsg.c:1008
msgid "Your SMS was not delivered"
msgstr "Wiadomość SMS nie została dostarczona"
+#: ../libpurple/protocols/yahoo/libymsg.c:1152
msgid "Your Yahoo! message did not get sent."
msgstr "Wiadomość Yahoo! nie została wysłana."
+#: ../libpurple/protocols/yahoo/libymsg.c:1268
#, c-format
msgid "Yahoo! system message for %s:"
msgstr "Wiadomość systemu Yahoo! dla %s:"
-msgid "Authorization denied message:"
-msgstr "Wiadomość odmowy upoważnienia:"
-
+#: ../libpurple/protocols/yahoo/libymsg.c:1371
#, c-format
msgid ""
"%s has (retroactively) denied your request to add them to your list for the "
"following reason: %s."
-msgstr ""
-"Użytkownik %s odrzucił prośbę dodanie go do listy z następującego powodu: %s."
+msgstr "Użytkownik %s odrzucił prośbę dodanie go do listy z następującego powodu: %s."
+#: ../libpurple/protocols/yahoo/libymsg.c:1374
#, c-format
msgid "%s has (retroactively) denied your request to add them to your list."
msgstr "Użytkownik %s odrzucił prośbę o dodanie go do listy."
+#: ../libpurple/protocols/yahoo/libymsg.c:1376
msgid "Add buddy rejected"
msgstr "Odrzucono dodanie znajomego"
#. Some error in the received stream
+#: ../libpurple/protocols/yahoo/libymsg.c:1959
+#: ../libpurple/protocols/yahoo/libymsg.c:2044
msgid "Received invalid data"
msgstr "Odebrano nieprawidłowe dane"
#. security lock from too many failed login attempts
+#: ../libpurple/protocols/yahoo/libymsg.c:2057
msgid ""
"Account locked: Too many failed login attempts. Logging into the Yahoo! "
"website may fix this."
-msgstr ""
-"Zablokowano konto: za dużo nieudanych prób zalogowania. Zalogowanie na "
-"stronę WWW Yahoo! może to naprawić."
+msgstr "Zablokowano konto: za dużo nieudanych prób zalogowania. Zalogowanie na stronę WWW Yahoo! może to naprawić."
#. indicates a lock of some description
+#: ../libpurple/protocols/yahoo/libymsg.c:2068
msgid ""
"Account locked: Unknown reason. Logging into the Yahoo! website may fix "
"this."
-msgstr ""
-"Zablokowano konto: nieznany powód. Zalogowanie na stronę WWW Yahoo! może to "
-"naprawić."
+msgstr "Zablokowano konto: nieznany powód. Zalogowanie na stronę WWW Yahoo! może to naprawić."
#. indicates a lock due to logging in too frequently
+#: ../libpurple/protocols/yahoo/libymsg.c:2074
msgid ""
-"Account locked: You have been logging in too frequently. Wait a few minutes "
-"before trying to connect again. Logging into the Yahoo! website may help."
-msgstr ""
-"Zablokowano konto: za dużo prób zalogowania. Proszę poczekać kilka minut "
-"przed ponowną próbą połączenia się. Zalogowanie na stronę WWW Yahoo! może "
-"pomóc."
+"Account locked: You have been logging in too frequently. Wait a few minutes"
+" before trying to connect again. Logging into the Yahoo! website may help."
+msgstr "Zablokowano konto: za dużo prób zalogowania. Proszę poczekać kilka minut przed ponowną próbą połączenia się. Zalogowanie na stronę WWW Yahoo! może pomóc."
#. username or password missing
+#: ../libpurple/protocols/yahoo/libymsg.c:2081
msgid "Username or password missing"
msgstr "Brak nazwy użytkownika lub hasła"
+#: ../libpurple/protocols/yahoo/libymsg.c:2191
#, c-format
msgid ""
"The Yahoo server has requested the use of an unrecognized authentication "
"method. You will probably not be able to successfully sign on to Yahoo. "
"Check %s for updates."
-msgstr ""
-"Serwer Yahoo! zażądał użycia nierozpoznanej metody uwierzytelniania. "
-"Prawdopodobnie nie będzie można pomyślnie zalogować się do Yahoo!. Proszę "
-"sprawdzić %s, aby zaktualizować."
+msgstr "Serwer Yahoo! zażądał użycia nierozpoznanej metody uwierzytelniania. Prawdopodobnie nie będzie można pomyślnie zalogować się do Yahoo!. Proszę sprawdzić %s, aby zaktualizować."
+#: ../libpurple/protocols/yahoo/libymsg.c:2196
msgid "Failed Yahoo! Authentication"
msgstr "Uwierzytelnianie Yahoo! się nie powiodło"
+#: ../libpurple/protocols/yahoo/libymsg.c:2279
#, c-format
msgid ""
"You have tried to ignore %s, but the user is on your buddy list. Clicking "
"\"Yes\" will remove and ignore the buddy."
-msgstr ""
-"Próbowano zignorować użytkownika %s, ale znajduje się on na liście "
-"znajomych. Naciśnięcie przycisku \"Tak\" usunie i zignoruje znajomego."
+msgstr "Próbowano zignorować użytkownika %s, ale znajduje się on na liście znajomych. Naciśnięcie przycisku \"Tak\" usunie i zignoruje znajomego."
+#: ../libpurple/protocols/yahoo/libymsg.c:2283
msgid "Ignore buddy?"
msgstr "Zignorować znajomego?"
+#: ../libpurple/protocols/yahoo/libymsg.c:2378
msgid "Invalid username or password"
msgstr "Nieprawidłowa nazwa użytkownika lub hasło"
+#: ../libpurple/protocols/yahoo/libymsg.c:2382
msgid ""
"Your account has been locked due to too many failed login attempts. Please "
"try logging into the Yahoo! website."
-msgstr ""
-"Konto zostało zablokowane z powodu za dużej ilości nieudanych prób "
-"zalogowania. Proszę spróbować zalogować się na stronie WWW Yahoo!."
+msgstr "Konto zostało zablokowane z powodu za dużej ilości nieudanych prób zalogowania. Proszę spróbować zalogować się na stronie WWW Yahoo!."
+#: ../libpurple/protocols/yahoo/libymsg.c:2389
msgid "Unknown error 52. Reconnecting should fix this."
msgstr "Nieznany błąd 52. Ponowne połączenie powinno to naprawić."
+#: ../libpurple/protocols/yahoo/libymsg.c:2393
msgid ""
-"Error 1013: The username you have entered is invalid. The most common cause "
-"of this error is entering your email address instead of your Yahoo! ID."
-msgstr ""
-"Błąd 1013: podana nazwa użytkownika jest nieprawidłowa. Najczęstszym powodem "
-"tego błędu jest podanie adresu e-mail zamiast identyfikatora Yahoo!."
+"Error 1013: The username you have entered is invalid. The most common cause"
+" of this error is entering your email address instead of your Yahoo! ID."
+msgstr "Błąd 1013: podana nazwa użytkownika jest nieprawidłowa. Najczęstszym powodem tego błędu jest podanie adresu e-mail zamiast identyfikatora Yahoo!."
+#: ../libpurple/protocols/yahoo/libymsg.c:2399
#, c-format
msgid "Unknown error number %d. Logging into the Yahoo! website may fix this."
-msgstr ""
-"Nieznany błąd numer %d. Zalogowanie na stronę WWW Yahoo! może to naprawić."
+msgstr "Nieznany błąd numer %d. Zalogowanie na stronę WWW Yahoo! może to naprawić."
+#: ../libpurple/protocols/yahoo/libymsg.c:2493
#, c-format
msgid "Unable to add buddy %s to group %s to the server list on account %s."
-msgstr ""
-"Nie można dodać znajomego %s do grupy %s na liście serwera na koncie %s."
+msgstr "Nie można dodać znajomego %s do grupy %s na liście serwera na koncie %s."
+#: ../libpurple/protocols/yahoo/libymsg.c:2496
msgid "Unable to add buddy to server list"
msgstr "Nie można dodać znajomego do listy serwera"
+#: ../libpurple/protocols/yahoo/libymsg.c:3156
#, c-format
msgid "[ Audible %s/%s/%s.swf ] %s"
msgstr "[Słyszalne%s/%s/%s.swf] %s"
-msgid "Received unexpected HTTP response from server"
-msgstr "Odebrano nieoczekiwaną odpowiedź z serwera HTTP"
-
-#, c-format
-msgid "Lost connection with %s: %s"
-msgstr "Utracono połączenie z %s: %s"
-
-#, c-format
-msgid "Unable to establish a connection with %s: %s"
-msgstr "Nie można nawiązać połączenia z %s: %s"
-
+#: ../libpurple/protocols/yahoo/libymsg.c:3711
msgid "Unable to connect: The server returned an empty response."
msgstr "Nie można się połączyć: serwer zwrócił pustą odpowiedź."
+#: ../libpurple/protocols/yahoo/libymsg.c:3746
msgid ""
"Unable to connect: The server's response did not contain the necessary "
"information"
-msgstr ""
-"Nie można się połączyć: odpowiedź serwera nie zawiera wymaganych informacji"
+msgstr "Nie można się połączyć: odpowiedź serwera nie zawiera wymaganych informacji"
+#: ../libpurple/protocols/yahoo/libymsg.c:3946
+#: ../libpurple/protocols/yahoo/libymsg.c:4943
msgid "Not at Home"
msgstr "Poza domem"
+#: ../libpurple/protocols/yahoo/libymsg.c:3948
+#: ../libpurple/protocols/yahoo/libymsg.c:4946
msgid "Not at Desk"
msgstr "Poza biurkiem"
+#: ../libpurple/protocols/yahoo/libymsg.c:3950
+#: ../libpurple/protocols/yahoo/libymsg.c:4949
msgid "Not in Office"
msgstr "Poza biurem"
+#: ../libpurple/protocols/yahoo/libymsg.c:3954
+#: ../libpurple/protocols/yahoo/libymsg.c:4955
msgid "On Vacation"
msgstr "Na wakacjach"
+#: ../libpurple/protocols/yahoo/libymsg.c:3958
+#: ../libpurple/protocols/yahoo/libymsg.c:4961
msgid "Stepped Out"
msgstr "Wyszedł na chwilę"
+#: ../libpurple/protocols/yahoo/libymsg.c:4056
+#: ../libpurple/protocols/yahoo/libymsg.c:4087
msgid "Not on server list"
msgstr "Brak na liście serwera"
+#: ../libpurple/protocols/yahoo/libymsg.c:4107
+#: ../libpurple/protocols/yahoo/libymsg.c:4191
msgid "Appear Permanently Offline"
msgstr "Wygląda na trwały tryb offline"
+#: ../libpurple/protocols/yahoo/libymsg.c:4123
msgid "Presence"
msgstr "Obecność"
+#: ../libpurple/protocols/yahoo/libymsg.c:4185
msgid "Don't Appear Permanently Offline"
msgstr "Nie wygląda na rwały tryb offline"
+#: ../libpurple/protocols/yahoo/libymsg.c:4247
msgid "Join in Chat"
msgstr "Dołącz do konferencji"
+#: ../libpurple/protocols/yahoo/libymsg.c:4253
msgid "Initiate Conference"
msgstr "Rozpocznij konferencję"
+#: ../libpurple/protocols/yahoo/libymsg.c:4281
msgid "Presence Settings"
msgstr "Ustawienia obecności"
+#: ../libpurple/protocols/yahoo/libymsg.c:4286
msgid "Start Doodling"
msgstr "Rozpocznij sesję Doodle"
+#: ../libpurple/protocols/yahoo/libymsg.c:4406
+msgid "Activate which ID?"
+msgstr "Który identyfikator aktywować?"
+
+#: ../libpurple/protocols/yahoo/libymsg.c:4415
msgid "Select the ID you want to activate"
msgstr "Wybierz identyfikator do aktywacji"
+#: ../libpurple/protocols/yahoo/libymsg.c:4425
msgid "Join whom in chat?"
msgstr "Kogo dołączyć do konferencji?"
+#: ../libpurple/protocols/yahoo/libymsg.c:4444
msgid "Activate ID..."
msgstr "Aktywuj identyfikator..."
+#: ../libpurple/protocols/yahoo/libymsg.c:4448
msgid "Join User in Chat..."
msgstr "Dołącz użytkownika do konferencji..."
+#: ../libpurple/protocols/yahoo/libymsg.c:4453
msgid "Open Inbox"
msgstr "Otwórz skrzynkę odbiorczą"
+#: ../libpurple/protocols/yahoo/libymsg.c:4479
msgid "Can't send SMS. Unable to obtain mobile carrier."
-msgstr ""
-"Nie można wysłać wiadomości SMS. Nie można uzyskać operatora sieci "
-"komórkowej."
+msgstr "Nie można wysłać wiadomości SMS. Nie można uzyskać operatora sieci komórkowej."
+#: ../libpurple/protocols/yahoo/libymsg.c:4509
+#: ../libpurple/protocols/yahoo/libymsg.c:4625
msgid "Can't send SMS. Unknown mobile carrier."
msgstr "Nie można wysłać wiadomości SMS. Nieznany operator sieci komórkowej."
+#: ../libpurple/protocols/yahoo/libymsg.c:4615
msgid "Getting mobile carrier to send the SMS."
msgstr "Uzyskiwanie operatora sieci komórkowej, aby wysłać wiadomość SMS."
#. Write a local message to this conversation showing that a request for a
#. * Doodle session has been made
-#.
+#: ../libpurple/protocols/yahoo/yahoo_doodle.c:96
msgid "Sent Doodle request."
msgstr "Wysłano prośbę o Doodle."
-msgid "Unable to connect."
-msgstr "Nie można się połączyć."
-
-msgid "Unable to establish file descriptor."
-msgstr "Nie można ustanowić deskryptora pliku."
-
+#: ../libpurple/protocols/yahoo/yahoo_filexfer.c:794
#, c-format
msgid "%s is trying to send you a group of %d files.\n"
msgstr "Użytkownik %s próbuje wysłać grupę %d plików.\n"
+#: ../libpurple/protocols/yahoo/yahoo_filexfer.c:944
+msgid "Unable to get file header."
+msgstr "Nie można uzyskać nagłówka pliku."
+
+#: ../libpurple/protocols/yahoo/yahoo_filexfer.c:1038
+msgid "Unsupported method"
+msgstr "Nieobsługiwana metoda"
+
+#: ../libpurple/protocols/yahoo/yahoo_packet.c:309
msgid "Write Error"
msgstr "Błąd zapisu"
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:799
msgid "Yahoo! Japan Profile"
msgstr "Profil Yahoo! Japan"
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:800
msgid "Yahoo! Profile"
msgstr "Profil Yahoo!"
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:842
msgid ""
-"Sorry, profiles marked as containing adult content are not supported at this "
-"time."
-msgstr ""
-"Profile oznaczone jako zawierające treści dla dorosłych nie są jeszcze "
-"obsługiwane."
+"Sorry, profiles marked as containing adult content are not supported at this"
+" time."
+msgstr "Profile oznaczone jako zawierające treści dla dorosłych nie są jeszcze obsługiwane."
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:844
msgid ""
"If you wish to view this profile, you will need to visit this link in your "
"web browser:"
-msgstr ""
-"Aby wyświetlić ten profil, należy odwiedzić ten odnośnik w przeglądarce WWW:"
+msgstr "Aby wyświetlić ten profil, należy odwiedzić ten odnośnik w przeglądarce WWW:"
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1027
msgid "Yahoo! ID"
msgstr "Identyfikator Yahoo!"
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1105
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1109
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1113
msgid "Hobbies"
msgstr "Hobby"
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1123
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1127
msgid "Latest News"
msgstr "Ostatnie aktualności"
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1148
msgid "Home Page"
msgstr "Strona domowa"
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1163
msgid "Cool Link 1"
msgstr "Fajny odnośnik 1"
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1168
msgid "Cool Link 2"
msgstr "Fajny odnośnik 2"
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1172
msgid "Cool Link 3"
msgstr "Fajny odnośnik 3"
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1186
msgid "Last Update"
msgstr "Ostatnia aktualizacja"
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1199
msgid ""
"This profile is in a language or format that is not supported at this time."
-msgstr ""
-"Ten profil jest w języku lub formatowaniu, które nie jest jeszcze "
-"obsługiwane."
+msgstr "Ten profil jest w języku lub formatowaniu, które nie jest jeszcze obsługiwane."
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1216
msgid ""
"Could not retrieve the user's profile. This most likely is a temporary "
"server-side problem. Please try again later."
-msgstr ""
-"Nie można pobrać profilu użytkownika. Najprawdopodobniej są to tymczasowe "
-"problemy ze strony serwera. Proszę spróbować później."
+msgstr "Nie można pobrać profilu użytkownika. Najprawdopodobniej są to tymczasowe problemy ze strony serwera. Proszę spróbować później."
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1219
msgid ""
"Could not retrieve the user's profile. This most likely means that the user "
"does not exist; however, Yahoo! sometimes does fail to find a user's "
"profile. If you know that the user exists, please try again later."
-msgstr ""
-"Nie można pobrać profilu użytkownika. Najprawdopodobniej oznacza to, że "
-"użytkownik nie istnieje. Czasami jednak Yahoo! ma problemy z odnalezieniem "
-"profilu użytkownika. Jeśli użytkownik na pewno istnieje, proszę spróbować "
-"później."
+msgstr "Nie można pobrać profilu użytkownika. Najprawdopodobniej oznacza to, że użytkownik nie istnieje. Czasami jednak Yahoo! ma problemy z odnalezieniem profilu użytkownika. Jeśli użytkownik na pewno istnieje, proszę spróbować później."
+#: ../libpurple/protocols/yahoo/yahoo_profile.c:1225
msgid "The user's profile is empty."
msgstr "Profil użytkownika jest pusty."
+#: ../libpurple/protocols/yahoo/yahoochat.c:270
#, c-format
msgid "%s has declined to join."
msgstr "Użytkownik %s odmówił dołączenia."
+#: ../libpurple/protocols/yahoo/yahoochat.c:508
msgid "Failed to join chat"
msgstr "Dołączenie do konferencji się nie powiodło"
-#. -6
+#: ../libpurple/protocols/yahoo/yahoochat.c:512
msgid "Unknown room"
msgstr "Nieznany pokój"
-#. -15
+#: ../libpurple/protocols/yahoo/yahoochat.c:517
msgid "Maybe the room is full"
msgstr "Możliwe, że pokój jest pełny"
-#. -35
+#: ../libpurple/protocols/yahoo/yahoochat.c:522
msgid "Not available"
msgstr "niedostępny"
+#: ../libpurple/protocols/yahoo/yahoochat.c:527
msgid ""
"Unknown error. You may need to logout and wait five minutes before being "
"able to rejoin a chatroom"
-msgstr ""
-"Nieznany błąd. Należy wylogować się i poczekać pięć minut przed możliwością "
-"ponownego dołączenia do konferencji"
+msgstr "Nieznany błąd. Należy wylogować się i poczekać pięć minut przed możliwością ponownego dołączenia do konferencji"
+#: ../libpurple/protocols/yahoo/yahoochat.c:614
#, c-format
msgid "You are now chatting in %s."
msgstr "Prowadzona jest rozmowa w %s."
+#: ../libpurple/protocols/yahoo/yahoochat.c:821
msgid "Failed to join buddy in chat"
msgstr "Dołączenie znajomego do konferencji się nie powiodło"
+#: ../libpurple/protocols/yahoo/yahoochat.c:822
msgid "Maybe they're not in a chat?"
msgstr "Może nie ma ich na konferencji?"
+#: ../libpurple/protocols/yahoo/yahoochat.c:1461
msgid "Fetching the room list failed."
msgstr "Pobranie listy pokoi się nie powiodło."
+#: ../libpurple/protocols/yahoo/yahoochat.c:1546
msgid "Voices"
msgstr "Rozmowy głosowe"
+#: ../libpurple/protocols/yahoo/yahoochat.c:1549
msgid "Webcams"
msgstr "Kamery internetowe"
-msgid "Connection problem"
-msgstr "Problem połączenia"
-
-msgid "Unable to fetch room list."
-msgstr "Nie można pobrać listy pokoi."
-
+#: ../libpurple/protocols/yahoo/yahoochat.c:1624
msgid "User Rooms"
msgstr "Pokoje użytkowników"
+#: ../libpurple/protocols/yahoo/ycht.c:457
msgid "Connection problem with the YCHT server"
msgstr "Problem połączenia z serwerem YCHT"
+#: ../libpurple/protocols/zephyr/zephyr.c:356
msgid ""
"(There was an error converting this message.\t Check the 'Encoding' option "
"in the Account Editor)"
-msgstr ""
-"(Wystąpił błąd podczas konwertowania tej wiadomości.\t Proszę sprawdzić "
-"opcję \"Kodowanie\" w edytorze kont)"
+msgstr "(Wystąpił błąd podczas konwertowania tej wiadomości.\t Proszę sprawdzić opcję \"Kodowanie\" w edytorze kont)"
+#: ../libpurple/protocols/zephyr/zephyr.c:755
#, c-format
msgid "Unable to send to chat %s,%s,%s"
msgstr "Nie można wysłać do konferencji %s,%s,%s"
+#. TODO: Check whether it's correct to call add_pair_html,
+#. or if we should be using add_pair_plaintext
+#: ../libpurple/protocols/zephyr/zephyr.c:804
+#: ../libpurple/protocols/zephyr/zephyr.c:1189
+msgid "User"
+msgstr "Użytkownik"
+
+#: ../libpurple/protocols/zephyr/zephyr.c:810
+#: ../libpurple/protocols/zephyr/zephyr.c:1196
msgid "Hidden or not logged-in"
msgstr "Ukryty lub niezalogowany"
+#. TODO: Need to escape locs.host and locs.time?
+#. TODO: Need to escape the two strings that make up tmp?
+#: ../libpurple/protocols/zephyr/zephyr.c:817
+#: ../libpurple/protocols/zephyr/zephyr.c:1199
#, c-format
msgid "<br>At %s since %s"
msgstr "<br>Przy %s od %s"
+#: ../libpurple/protocols/zephyr/zephyr.c:1523
+#: ../libpurple/protocols/zephyr/zephyr.c:1524
msgid "Anyone"
msgstr "Każdy"
+#: ../libpurple/protocols/zephyr/zephyr.c:2394
msgid "_Class:"
msgstr "_Klasa:"
+#: ../libpurple/protocols/zephyr/zephyr.c:2400
msgid "_Instance:"
msgstr "_Instancja:"
+#: ../libpurple/protocols/zephyr/zephyr.c:2406
msgid "_Recipient:"
msgstr "Odbio_rca:"
+#: ../libpurple/protocols/zephyr/zephyr.c:2417
#, c-format
msgid "Attempt to subscribe to %s,%s,%s failed"
msgstr "Próba subskrypcji %s,%s,%s się nie powiodła"
+#: ../libpurple/protocols/zephyr/zephyr.c:2743
msgid "zlocate &lt;nick&gt;: Locate user"
msgstr "zlocate &lt;pseudonim&gt;: ustala położenie użytkownika"
+#: ../libpurple/protocols/zephyr/zephyr.c:2748
msgid "zl &lt;nick&gt;: Locate user"
msgstr "zl &lt;pseudonim&gt;: ustala położenie użytkownika"
+#: ../libpurple/protocols/zephyr/zephyr.c:2753
msgid "instance &lt;instance&gt;: Set the instance to be used on this class"
msgstr "instance &lt;instancja&gt;: ustawia instancję używaną w tej klasie"
+#: ../libpurple/protocols/zephyr/zephyr.c:2758
msgid "inst &lt;instance&gt;: Set the instance to be used on this class"
msgstr "inst &lt;instancja&gt;: ustawia instancję używaną w tej klasie"
+#: ../libpurple/protocols/zephyr/zephyr.c:2763
msgid "topic &lt;instance&gt;: Set the instance to be used on this class"
msgstr "topic &lt;instancja&gt;: ustawia instancję używaną w tej klasie"
+#: ../libpurple/protocols/zephyr/zephyr.c:2769
msgid "sub &lt;class&gt; &lt;instance&gt; &lt;recipient&gt;: Join a new chat"
-msgstr ""
-"sub &lt;klasa&gt; &lt;instancja&gt; &lt;odbiorca&gt;: dołącza do nowej "
-"konferencji"
+msgstr "sub &lt;klasa&gt; &lt;instancja&gt; &lt;odbiorca&gt;: dołącza do nowej konferencji"
+#: ../libpurple/protocols/zephyr/zephyr.c:2774
msgid ""
"zi &lt;instance&gt;: Send a message to &lt;message,<i>instance</i>,*&gt;"
-msgstr ""
-"zi &lt;instancja&gt;: wysyła wiadomość do &lt;wiadomość,<i>instancja</i>,"
-"*&gt;"
+msgstr "zi &lt;instancja&gt;: wysyła wiadomość do &lt;wiadomość,<i>instancja</i>,*&gt;"
+#: ../libpurple/protocols/zephyr/zephyr.c:2780
msgid ""
-"zci &lt;class&gt; &lt;instance&gt;: Send a message to &lt;<i>class</i>,"
-"<i>instance</i>,*&gt;"
-msgstr ""
-"zci &lt;klasa&gt; &lt;instancja&gt;: wysyła wiadomość do &lt;<i>klasa</i>,"
-"<i>instancja</i>,*&gt;"
+"zci &lt;class&gt; &lt;instance&gt;: Send a message to "
+"&lt;<i>class</i>,<i>instance</i>,*&gt;"
+msgstr "zci &lt;klasa&gt; &lt;instancja&gt;: wysyła wiadomość do &lt;<i>klasa</i>,<i>instancja</i>,*&gt;"
+#: ../libpurple/protocols/zephyr/zephyr.c:2786
msgid ""
-"zcir &lt;class&gt; &lt;instance&gt; &lt;recipient&gt;: Send a message to &lt;"
-"<i>class</i>,<i>instance</i>,<i>recipient</i>&gt;"
-msgstr ""
-"zcir &lt;klasa&gt; &lt;instancja&gt; &lt;odbiorca&gt;: wysyła wiadomość do "
-"&lt;<i>klasa</i>,<i>instancja</i>,<i>odbiorca</i>&gt;"
+"zcir &lt;class&gt; &lt;instance&gt; &lt;recipient&gt;: Send a message to "
+"&lt;<i>class</i>,<i>instance</i>,<i>recipient</i>&gt;"
+msgstr "zcir &lt;klasa&gt; &lt;instancja&gt; &lt;odbiorca&gt;: wysyła wiadomość do &lt;<i>klasa</i>,<i>instancja</i>,<i>odbiorca</i>&gt;"
+#: ../libpurple/protocols/zephyr/zephyr.c:2792
msgid ""
-"zir &lt;instance&gt; &lt;recipient&gt;: Send a message to &lt;MESSAGE,"
-"<i>instance</i>,<i>recipient</i>&gt;"
-msgstr ""
-"zir &lt;instancja&gt; &lt;odbiorca&gt;: wysyła wiadomość do &lt;WIADOMOŚĆ,"
-"<i>instancja</i>,<i>odbiorca</i>&gt;"
+"zir &lt;instance&gt; &lt;recipient&gt;: Send a message to "
+"&lt;MESSAGE,<i>instance</i>,<i>recipient</i>&gt;"
+msgstr "zir &lt;instancja&gt; &lt;odbiorca&gt;: wysyła wiadomość do &lt;WIADOMOŚĆ,<i>instancja</i>,<i>odbiorca</i>&gt;"
+#: ../libpurple/protocols/zephyr/zephyr.c:2797
msgid "zc &lt;class&gt;: Send a message to &lt;<i>class</i>,PERSONAL,*&gt;"
msgstr "zc &lt;klasa&gt;: wysyła wiadomość do &lt;<i>klasa</i>,OSOBISTA,*&gt;"
+#: ../libpurple/protocols/zephyr/zephyr.c:2879
msgid "Resubscribe"
msgstr "Ponownie subskrybuj"
+#: ../libpurple/protocols/zephyr/zephyr.c:2882
msgid "Retrieve subscriptions from server"
msgstr "Pobierz subskrypcje z serwera"
@@ -10251,190 +13343,210 @@ msgstr "Pobierz subskrypcje z serwera"
#. *< version
#. * summary
#. * description
+#: ../libpurple/protocols/zephyr/zephyr.c:2979
+#: ../libpurple/protocols/zephyr/zephyr.c:2981
msgid "Zephyr Protocol Plugin"
msgstr "Wtyczka protokołu Zephyr"
+#: ../libpurple/protocols/zephyr/zephyr.c:3006
msgid "Use tzc"
msgstr "Użycie tzc"
+#: ../libpurple/protocols/zephyr/zephyr.c:3009
msgid "tzc command"
msgstr "Polecenie tzc"
+#: ../libpurple/protocols/zephyr/zephyr.c:3012
msgid "Export to .anyone"
msgstr "Eksport do .anyone"
+#: ../libpurple/protocols/zephyr/zephyr.c:3015
msgid "Export to .zephyr.subs"
msgstr "Eksport do .zephyr.subs"
+#: ../libpurple/protocols/zephyr/zephyr.c:3018
msgid "Import from .anyone"
msgstr "Import z .anyone"
+#: ../libpurple/protocols/zephyr/zephyr.c:3021
msgid "Import from .zephyr.subs"
msgstr "Import z .zephyr.subs"
+#: ../libpurple/protocols/zephyr/zephyr.c:3024
msgid "Realm"
msgstr "Obszar"
+#: ../libpurple/protocols/zephyr/zephyr.c:3027
msgid "Exposure"
msgstr "Ekspozycja"
+#: ../libpurple/proxy.c:760 ../libpurple/proxy.c:815 ../libpurple/proxy.c:1259
+#: ../libpurple/proxy.c:1446 ../libpurple/proxy.c:2101
#, c-format
msgid "Unable to create socket: %s"
msgstr "Nie można utworzyć gniazda: %s"
+#: ../libpurple/proxy.c:994 ../libpurple/proxy.c:1015
#, c-format
msgid "Unable to parse response from HTTP proxy: %s"
msgstr "Nie można przetworzyć odpowiedzi od pośrednika HTTP: %s"
+#: ../libpurple/proxy.c:1048 ../libpurple/proxy.c:1110
+#: ../libpurple/proxy.c:1139
#, c-format
msgid "HTTP proxy connection error %d"
msgstr "Błąd połączenia serwera pośrednika HTTP %d"
+#: ../libpurple/proxy.c:1135
#, c-format
msgid "Access denied: HTTP proxy server forbids port %d tunneling"
msgstr "Odmowa dostępu: serwer pośrednika HTTP zabronił tunelowania portu %d"
+#: ../libpurple/proxy.c:1328
#, c-format
msgid "Error resolving %s"
msgstr "Błąd podczas rozwiązywania %s"
+#: ../libpurple/prpl.c:462
#, c-format
msgid "Requesting %s's attention..."
msgstr "Proszenie o uwagę użytkownika %s..."
+#: ../libpurple/prpl.c:506
#, c-format
msgid "%s has requested your attention!"
msgstr "Użytkownik %s poprosił o uwagę."
+#: ../libpurple/purple-socket.c:176 ../libpurple/purple-socket.c:203
+msgid "Invalid socket state"
+msgstr "Nieprawidłowy stan gniazda"
+
+#: ../libpurple/purple-socket.c:211
+msgid "Invalid file descriptor"
+msgstr "Nieprawidłowy deskryptor pliku"
+
+#: ../libpurple/request.c:2064
+#, c-format
+msgid "Invalid character '%c'"
+msgstr "Nieprawidłowy znak \"%c\""
+
+#. *
+#. * purple_request_yes_no:
#. *
-#. * A wrapper for purple_request_action() that uses @c Yes and @c No buttons.
-#.
+#. * A wrapper for purple_request_action() that uses <literal>Yes</literal>
+#. and
+#. * <literal>No</literal> buttons.
+#: ../libpurple/request.h:2167 ../pidgin/gtkblist.c:568
+#: ../pidgin/gtkrequest.c:335
msgid "_Yes"
msgstr "_Tak"
+#: ../libpurple/request.h:2168 ../pidgin/gtkblist.c:568
+#: ../pidgin/gtkrequest.c:337
msgid "_No"
msgstr "_Nie"
#. *
-#. * A wrapper for purple_request_action() that uses Accept and Cancel buttons.
-#.
+#. * purple_request_accept_cancel:
#. *
-#. * A wrapper for purple_request_action_with_icon() that uses Accept and Cancel
-#. * buttons.
-#.
+#. * A wrapper for purple_request_action() that uses Accept and Cancel
+#. buttons.
+#: ../libpurple/request.h:2190
msgid "_Accept"
msgstr "Z_aakceptuj"
-#. *
#. * The default message to use when the user becomes auto-away.
-#.
+#: ../libpurple/savedstatuses.c:43
msgid "I'm not here right now"
msgstr "Nie ma mnie teraz przy komputerze"
+#: ../libpurple/savedstatuses.c:533
msgid "saved statuses"
msgstr "zapisane stany"
+#: ../libpurple/server.c:265
#, c-format
msgid "%s is now known as %s.\n"
msgstr "Użytkownik %s jest teraz znany jako %s.\n"
+#: ../libpurple/server.c:795
#, c-format
msgid ""
"%s has invited %s to the chat room %s:\n"
"%s"
-msgstr ""
-"Użytkownik %s zaprosił %s do pokoju konferencji %s:\n"
-"%s"
+msgstr "Użytkownik %s zaprosił %s do pokoju konferencji %s:\n%s"
+#: ../libpurple/server.c:800
#, c-format
msgid "%s has invited %s to the chat room %s\n"
msgstr "Użytkownik %s zaprosił %s do pokoju konferencji %s\n"
+#: ../libpurple/server.c:805
msgid "Accept chat invitation?"
msgstr "Zaakceptować zaproszenie do konferencji?"
-#. Shortcut
-msgid "Shortcut"
-msgstr "Skrót"
-
-msgid "The text-shortcut for the smiley"
-msgstr "Skrót tekstowy do emotikony"
-
-#. Stored Image
-msgid "Stored Image"
-msgstr "Zapisany obraz"
-
-msgid "Stored Image. (that'll have to do for now)"
-msgstr "Zapisany obraz (który trzeba będzie teraz zrobić)"
-
-msgid "SSL Connection Failed"
-msgstr "Połączenie SSL się nie powiodło"
-
+#: ../libpurple/sslconn.c:184
msgid "SSL Handshake Failed"
msgstr "Powitanie SSL się nie powiodło"
+#: ../libpurple/sslconn.c:186
msgid "SSL peer presented an invalid certificate"
msgstr "Użytkownik SSL zaprezentował nieprawidłowy certyfikat"
+#: ../libpurple/sslconn.c:189
msgid "Unknown SSL error"
msgstr "Nieznany błąd SSL"
+#: ../libpurple/status.c:136
msgid "Unset"
msgstr "Usuń ustawienie"
+#: ../libpurple/status.c:139 ../pidgin/gtkdocklet.c:590
+#: ../pidgin/gtkstatusbox.c:1053
msgid "Do not disturb"
msgstr "Nie przeszkadzać"
+#: ../libpurple/status.c:142
msgid "Extended away"
msgstr "Wrócę później"
+#: ../libpurple/status.c:145
msgid "Feeling"
msgstr "Odczuwa"
+#: ../libpurple/status.c:515
#, c-format
msgid "%s (%s) changed status from %s to %s"
msgstr "Użytkownik %s (%s) zmienił stan z %s na %s"
+#: ../libpurple/status.c:527
#, c-format
msgid "%s (%s) is now %s"
msgstr "Użytkownik %s (%s) jest teraz %s"
+#: ../libpurple/status.c:534
#, c-format
msgid "%s (%s) is no longer %s"
msgstr "Użytkownik %s (%s) nie jest już %s"
-#, c-format
-msgid "%s became idle"
-msgstr "Użytkownik %s jest bezczynny"
-
-#, c-format
-msgid "%s became unidle"
-msgstr "Użytkownik %s nie jest już bezczynny"
-
-#, c-format
-msgid "+++ %s became idle"
-msgstr "+++ %s jest bezczynny"
-
-#, c-format
-msgid "+++ %s became unidle"
-msgstr "+++ %s nie jest już bezczynny"
-
-#.
#. * This string determines how some dates are displayed. The default
#. * string "%x %X" shows the date then the time. Translators can
#. * change this to "%X %x" if they want the time to be shown first,
#. * followed by the date.
-#.
+#: ../libpurple/util.c:684
#, c-format
msgid "%x %X"
msgstr "%x %X"
+#: ../libpurple/util.c:3676
msgid "Calculating..."
msgstr "Obliczanie..."
+#: ../libpurple/util.c:3679
msgid "Unknown."
msgstr "Nieznany."
+#: ../libpurple/util.c:3705
#, c-format
msgid "%d second"
msgid_plural "%d seconds"
@@ -10442,6 +13554,7 @@ msgstr[0] "%d sekunda"
msgstr[1] "%d sekundy"
msgstr[2] "%d sekund"
+#: ../libpurple/util.c:3717
#, c-format
msgid "%d day"
msgid_plural "%d days"
@@ -10449,6 +13562,7 @@ msgstr[0] "%d dzień"
msgstr[1] "%d dni"
msgstr[2] "%d dni"
+#: ../libpurple/util.c:3725
#, c-format
msgid "%s, %d hour"
msgid_plural "%s, %d hours"
@@ -10456,6 +13570,7 @@ msgstr[0] "%s, %d godzina"
msgstr[1] "%s, %d godziny"
msgstr[2] "%s, %d godzin"
+#: ../libpurple/util.c:3731
#, c-format
msgid "%d hour"
msgid_plural "%d hours"
@@ -10463,6 +13578,7 @@ msgstr[0] "%d godzina"
msgstr[1] "%d godziny"
msgstr[2] "%d godzin"
+#: ../libpurple/util.c:3739
#, c-format
msgid "%s, %d minute"
msgid_plural "%s, %d minutes"
@@ -10470,6 +13586,7 @@ msgstr[0] "%s, %d minuta"
msgstr[1] "%s, %d minuty"
msgstr[2] "%s, %d minut"
+#: ../libpurple/util.c:3745
#, c-format
msgid "%d minute"
msgid_plural "%d minutes"
@@ -10477,143 +13594,263 @@ msgstr[0] "%d minuta"
msgstr[1] "%d minuty"
msgstr[2] "%d minut"
-#, c-format
-msgid "Could not open %s: Redirected too many times"
-msgstr "Nie można otworzyć %s: przekierowano za wiele razy"
-
-#, c-format
-msgid "Unable to connect to %s"
-msgstr "Nie można połączyć się z %s"
-
-#, c-format
-msgid "Error reading from %s: response too long (%d bytes limit)"
-msgstr ""
-"Błąd podczas odczytywania z %s: odpowiedź jest za długa (ograniczenie do %d "
-"bajtów)"
-
-#, c-format
-msgid ""
-"Unable to allocate enough memory to hold the contents from %s. The web "
-"server may be trying something malicious."
-msgstr ""
-"Nie można przydzielić wystarczającej ilości pamięci, aby przechować "
-"zawartość %s. Serwer WWW może próbować złośliwych działań."
-
-#, c-format
-msgid "Error reading from %s: %s"
-msgstr "Błąd podczas odczytywania z %s: %s"
-
-#, c-format
-msgid "Error writing to %s: %s"
-msgstr "Błąd podczas zapisywania do %s: %s"
-
-#, c-format
-msgid "Unable to connect to %s: %s"
-msgstr "Nie można połączyć się z %s: %s"
-
+#: ../libpurple/util.c:4728
#, c-format
msgid " - %s"
msgstr " - %s"
+#: ../libpurple/util.c:4734
#, c-format
msgid " (%s)"
msgstr " (%s)"
#. 10053
+#: ../libpurple/win32/libc_interface.c:366
msgid "Connection interrupted by other software on your computer."
msgstr "Inne oprogramowanie na komputerze przerwało połączenie."
#. 10054
+#: ../libpurple/win32/libc_interface.c:369
msgid "Remote host closed connection."
msgstr "Zdalny komputer zamknął połączenie."
#. 10060
+#: ../libpurple/win32/libc_interface.c:372
msgid "Connection timed out."
msgstr "Przekroczono czas oczekiwania na połączenie."
#. 10061
+#: ../libpurple/win32/libc_interface.c:375
msgid "Connection refused."
msgstr "Odrzucono połączenie."
#. 10048
+#: ../libpurple/win32/libc_interface.c:378
msgid "Address already in use."
msgstr "Adres jest już używany."
+#: ../libpurple/xfer.c:308
+#, c-format
+msgid ""
+"Error reading %s: \n"
+"%s.\n"
+msgstr "Błąd podczas odczytywania %s: \n%s.\n"
+
+#: ../libpurple/xfer.c:312
+#, c-format
+msgid ""
+"Error writing %s: \n"
+"%s.\n"
+msgstr "Błąd podczas zapisywania %s: \n%s.\n"
+
+#: ../libpurple/xfer.c:316
+#, c-format
+msgid ""
+"Error accessing %s: \n"
+"%s.\n"
+msgstr "Błąd podczas uzyskiwania dostępu do %s: \n%s.\n"
+
+#: ../libpurple/xfer.c:354
+msgid "Directory is not writable."
+msgstr "Katalog nie jest udostępniony do zapisu."
+
+#: ../libpurple/xfer.c:370
+msgid "Cannot send a file of 0 bytes."
+msgstr "Nie można wysłać pliku o zerowej długości."
+
+#. * XXX - Sending a directory should be valid for some protocols.
+#: ../libpurple/xfer.c:380
+msgid "Cannot send a directory."
+msgstr "Nie można wysłać katalogu."
+
+#: ../libpurple/xfer.c:390
+#, c-format
+msgid "%s is not a regular file. Cowardly refusing to overwrite it.\n"
+msgstr "%s nie jest zwykłym plikiem. Na wszelki wypadek nie zostanie on zastąpiony.\n"
+
+#: ../libpurple/xfer.c:411
+msgid "File is not readable."
+msgstr "Plik jest nieczytelny."
+
+#: ../libpurple/xfer.c:479
+#, c-format
+msgid "%s wants to send you %s (%s)"
+msgstr "Użytkownik %s chce wysłać %s (%s)"
+
+#: ../libpurple/xfer.c:486
+#, c-format
+msgid "%s wants to send you a file"
+msgstr "Użytkownik %s chce wysłać plik"
+
+#: ../libpurple/xfer.c:534
+#, c-format
+msgid "Accept file transfer request from %s?"
+msgstr "Zaakceptować prośbę o przesłanie pliku od %s?"
+
+#: ../libpurple/xfer.c:538
+#, c-format
+msgid ""
+"A file is available for download from:\n"
+"Remote host: %s\n"
+"Remote port: %d"
+msgstr "Plik jest dostępny do pobrania z:\nZdalnego komputera: %s\nPortu zdalnego komputera: %d"
+
+#: ../libpurple/xfer.c:574
+#, c-format
+msgid "%s is offering to send file %s"
+msgstr "Użytkownik %s oferuje wysłanie pliku %s"
+
+#: ../libpurple/xfer.c:636
+#, c-format
+msgid "%s is not a valid filename.\n"
+msgstr "%s nie jest prawidłową nazwą pliku.\n"
+
+#: ../libpurple/xfer.c:663
+#, c-format
+msgid "Offering to send %s to %s"
+msgstr "Oferowanie wysłania %s do %s"
+
+#: ../libpurple/xfer.c:674
+#, c-format
+msgid "Starting transfer of %s from %s"
+msgstr "Rozpoczynanie przesyłania pliku %s od %s"
+
+#: ../libpurple/xfer.c:946
+#, c-format
+msgid "Transfer of file <A HREF=\"file://%s\">%s</A> complete"
+msgstr "Ukończono przesyłanie pliku <a href=\"file://%s\">%s</a>"
+
+#: ../libpurple/xfer.c:951
+#, c-format
+msgid "Transfer of file %s complete"
+msgstr "Ukończono przesyłanie pliku %s"
+
+#: ../libpurple/xfer.c:956
+msgid "File transfer complete"
+msgstr "Ukończono przesyłanie pliku"
+
+#: ../libpurple/xfer.c:1730
+#, c-format
+msgid "You cancelled the transfer of %s"
+msgstr "Anulowano przesyłanie pliku %s"
+
+#: ../libpurple/xfer.c:1735
+msgid "File transfer cancelled"
+msgstr "Anulowano przesyłanie pliku"
+
+#: ../libpurple/xfer.c:1796
+#, c-format
+msgid "%s cancelled the transfer of %s"
+msgstr "Użytkownik %s anulował przesyłanie pliku %s"
+
+#: ../libpurple/xfer.c:1801
+#, c-format
+msgid "%s cancelled the file transfer"
+msgstr "Użytkownik %s anulował przesyłanie pliku"
+
+#: ../libpurple/xfer.c:1858
+#, c-format
+msgid "File transfer to %s failed."
+msgstr "Przesyłanie pliku do %s się nie powiodło."
+
+#: ../libpurple/xfer.c:1860
+#, c-format
+msgid "File transfer from %s failed."
+msgstr "Przesyłanie pliku od %s się nie powiodło."
+
+#: ../libpurple/xmlnode.c:881
#, c-format
msgid "Error Reading %s"
msgstr "Błąd podczas odczytywania %s"
+#: ../libpurple/xmlnode.c:882
#, c-format
msgid ""
-"An error was encountered reading your %s. The file has not been loaded, and "
-"the old file has been renamed to %s~."
-msgstr ""
-"Wystąpił błąd podczas odczytywania %s. Plik nie został wczytany, a nazwa "
-"poprzedniego została zmieniona na %s~."
+"An error was encountered reading your %s. The file has not been loaded, and"
+" the old file has been renamed to %s~."
+msgstr "Wystąpił błąd podczas odczytywania %s. Plik nie został wczytany, a nazwa poprzedniego została zmieniona na %s~."
-msgid "Pidgin Internet Messenger"
-msgstr "Komunikator internetowy Pidgin"
+#: ../pidgin.desktop.in.h:1
+msgid ""
+"Chat over IM. Supports AIM, Google Talk, Jabber/XMPP, MSN, Yahoo and more"
+msgstr "Rozmawianie przez komunikator. Obsługuje sieci AIM, Google Talk, Jabber/XMPP, MSN, Yahoo oraz więcej"
+#: ../pidgin.desktop.in.h:2
msgid "Internet Messenger"
msgstr "Komunikator internetowy"
-msgid ""
-"Chat over IM. Supports AIM, Google Talk, Jabber/XMPP, MSN, Yahoo and more"
-msgstr ""
-"Rozmawianie przez komunikator. Obsługuje sieci AIM, Google Talk, Jabber/"
-"XMPP, MSN, Yahoo oraz więcej"
+#: ../pidgin.desktop.in.h:3
+msgid "Pidgin Internet Messenger"
+msgstr "Komunikator internetowy Pidgin"
#. Build the login options frame.
+#: ../pidgin/gtkaccount.c:629
msgid "Login Options"
msgstr "Opcje logowania"
+#: ../pidgin/gtkaccount.c:650
msgid "Pro_tocol:"
msgstr "Pro_tokół:"
+#: ../pidgin/gtkaccount.c:659 ../pidgin/gtkaccount.c:1308
msgid "_Username:"
msgstr "_Nazwa użytkownika:"
+#: ../pidgin/gtkaccount.c:764
msgid "Remember pass_word"
msgstr "_Zapamiętanie hasła"
#. Build the user options frame.
+#: ../pidgin/gtkaccount.c:820
msgid "User Options"
msgstr "Opcje użytkownika"
+#: ../pidgin/gtkaccount.c:833
msgid "_Local alias:"
msgstr "_Lokalny alias:"
+#: ../pidgin/gtkaccount.c:837
msgid "New _mail notifications"
msgstr "Powiada_mianie o nowej poczcie"
#. Buddy icon
+#: ../pidgin/gtkaccount.c:842
msgid "Use this buddy _icon for this account:"
msgstr "Użycie tej _ikony użytkownika dla tego konta:"
+#: ../pidgin/gtkaccount.c:977
msgid "Ad_vanced"
msgstr "Zaa_wansowane"
+#: ../pidgin/gtkaccount.c:1172
msgid "Use GNOME Proxy Settings"
msgstr "Użycie ustawień pośrednika środowiska GNOME"
+#: ../pidgin/gtkaccount.c:1173
msgid "Use Global Proxy Settings"
msgstr "Użycie globalnych ustawień pośrednika"
+#: ../pidgin/gtkaccount.c:1179
msgid "No Proxy"
msgstr "Bez pośrednika"
+#: ../pidgin/gtkaccount.c:1185 ../pidgin/gtkprefs.c:2542
msgid "SOCKS 4"
msgstr "SOCKS 4"
+#: ../pidgin/gtkaccount.c:1191 ../pidgin/gtkprefs.c:2543
msgid "SOCKS 5"
msgstr "SOCKS 5"
+#: ../pidgin/gtkaccount.c:1197 ../pidgin/gtkprefs.c:2544
msgid "Tor/Privacy (SOCKS5)"
msgstr "Tor/prywatność (SOCKS5)"
+#: ../pidgin/gtkaccount.c:1203 ../pidgin/gtkprefs.c:2545
msgid "HTTP"
msgstr "HTTP"
+#: ../pidgin/gtkaccount.c:1209 ../pidgin/gtkprefs.c:2546
msgid "Use Environmental Settings"
msgstr "Użycie ustawień środowiskowych"
@@ -10622,212 +13859,101 @@ msgstr "Użycie ustawień środowiskowych"
#. A) your network is really slow and you have nothing better to do than
#. look at butterflies.
#. B)You are looking really closely at something that shouldn't matter.
+#: ../pidgin/gtkaccount.c:1254
msgid "If you look real closely"
msgstr "Jeśli spojrzysz naprawdę blisko,"
#. This is an easter egg. See the comment on the previous line in the source.
+#: ../pidgin/gtkaccount.c:1257
msgid "you can see the butterflies mating"
msgstr "ujrzysz łączące się w pary motyle"
+#: ../pidgin/gtkaccount.c:1287
msgid "Proxy _type:"
msgstr "_Typ pośrednika:"
+#: ../pidgin/gtkaccount.c:1296 ../pidgin/gtkprefs.c:2562
msgid "_Host:"
msgstr "_Komputer:"
+#: ../pidgin/gtkaccount.c:1300
msgid "_Port:"
msgstr "_Port:"
+#: ../pidgin/gtkaccount.c:1313 ../pidgin/gtkprefs.c:2619
msgid "Pa_ssword:"
msgstr "Ha_sło:"
+#: ../pidgin/gtkaccount.c:1382
msgid "Use _silence suppression"
msgstr "Użycie tłumienia ci_szy"
+#: ../pidgin/gtkaccount.c:1387
msgid "_Voice and Video"
msgstr "_Dźwięk i wideo"
+#: ../pidgin/gtkaccount.c:1503
msgid "Unable to save new account"
msgstr "Nie można zapisać nowego konta"
+#: ../pidgin/gtkaccount.c:1504
msgid "An account already exists with the specified criteria."
msgstr "Konto o podanych kryteriach już istnieje."
+#: ../pidgin/gtkaccount.c:1787
msgid "Add Account"
msgstr "Dodanie konta"
+#: ../pidgin/gtkaccount.c:1804
msgid "_Basic"
msgstr "_Podstawowe"
+#: ../pidgin/gtkaccount.c:1812
msgid "Create _this new account on the server"
msgstr "_Utworzenie nowego konta na serwerze"
+#: ../pidgin/gtkaccount.c:1830
msgid "P_roxy"
msgstr "Poś_rednik"
+#: ../pidgin/gtkaccount.c:2234 ../pidgin/gtkplugin.c:859
msgid "Enabled"
msgstr "Włączone"
+#: ../pidgin/gtkaccount.c:2262
msgid "Protocol"
msgstr "Protokół"
+#: ../pidgin/gtkaccount.c:2449
#, c-format
msgid ""
"<span size='larger' weight='bold'>Welcome to %s!</span>\n"
"\n"
-"You have no IM accounts configured. To start connecting with %s press the "
-"<b>Add...</b> button below and configure your first account. If you want %s "
-"to connect to multiple IM accounts, press <b>Add...</b> again to configure "
-"them all.\n"
+"You have no IM accounts configured. To start connecting with %s press the <b>Add...</b> button below and configure your first account. If you want %s to connect to multiple IM accounts, press <b>Add...</b> again to configure them all.\n"
"\n"
-"You can come back to this window to add, edit, or remove accounts from "
-"<b>Accounts->Manage Accounts</b> in the Buddy List window"
-msgstr ""
-"<span size='larger' weight='bold'>Witaj w programie %s!</span>\n"
-"\n"
-"Nie skonfigurowano jeszcze żadnych kont komunikatora. Aby rozpocząć łączenie "
-"się za pomocą programu %s, należy najpierw nacisnąć poniższy przycisk "
-"<b>Dodaj...</b> i skonfigurować konto. Aby program %s łączył się z wieloma "
-"kontami, należy nacisnąć przycisk <b>Dodaj...</b> ponownie, aby je "
-"skonfigurować.\n"
-"\n"
-"Można wrócić do tego okna, aby dodać, zmodyfikować lub usunąć konta z menu "
-"<b>Konta->Zarządzaj kontami</b> w oknie listy znajomych"
+"You can come back to this window to add, edit, or remove accounts from <b>Accounts->Manage Accounts</b> in the Buddy List window"
+msgstr "<span size='larger' weight='bold'>Witaj w programie %s!</span>\n\nNie skonfigurowano jeszcze żadnych kont komunikatora. Aby rozpocząć łączenie się za pomocą programu %s, należy najpierw nacisnąć poniższy przycisk <b>Dodaj...</b> i skonfigurować konto. Aby program %s łączył się z wieloma kontami, należy nacisnąć przycisk <b>Dodaj...</b> ponownie, aby je skonfigurować.\n\nMożna wrócić do tego okna, aby dodać, zmodyfikować lub usunąć konta z menu <b>Konta->Zarządzaj kontami</b> w oknie listy znajomych"
+#: ../pidgin/gtkaccount.c:2769
+msgid "Authorization acceptance message:"
+msgstr "Wiadomość przyjęcia upoważnienia:"
+
+#: ../pidgin/gtkaccount.c:2811
+msgid "Authorization denied message:"
+msgstr "Wiadomość odmowy upoważnienia:"
+
+#: ../pidgin/gtkaccount.c:2885
#, c-format
msgid ""
"<a href=\"viewinfo\">%s</a>%s%s%s wants to add you (%s) to his or her buddy "
"list%s%s"
-msgstr ""
-"Użytkownik <a href=\"viewinfo\">%s</a>%s%s%s chce dodać użytkownika (%s) do "
-"swojej listy znajomych%s%s"
-
-#, c-format
-msgid "%s%s%s%s wants to add you (%s) to his or her buddy list%s%s"
-msgstr ""
-"Użytkownik %s%s%s%s chce dodać użytkownika (%s) do swojej listy znajomych%s%s"
+msgstr "Użytkownik <a href=\"viewinfo\">%s</a>%s%s%s chce dodać użytkownika (%s) do swojej listy znajomych%s%s"
+#: ../pidgin/gtkaccount.c:2921
msgid "Send Instant Message"
msgstr "Wyślij wiadomość"
-#. Buddy List
-msgid "Background Color"
-msgstr "Kolor tła"
-
-msgid "The background color for the buddy list"
-msgstr "Kolor tła listy znajomych"
-
-msgid "Layout"
-msgstr "Układ"
-
-msgid "The layout of icons, name, and status of the buddy list"
-msgstr "Układ ikon, nazw i stanów listy znajomych"
-
-#. Group
-#. Note to translators: These two strings refer to the background color
-#. of a buddy list group when in its expanded state
-msgid "Expanded Background Color"
-msgstr "Kolor tła rozwinięcia"
-
-msgid "The background color of an expanded group"
-msgstr "Kolor tła rozwiniętej grupy"
-
-#. Note to translators: These two strings refer to the font and color
-#. of a buddy list group when in its expanded state
-msgid "Expanded Text"
-msgstr "Rozwinięty tekst"
-
-msgid "The text information for when a group is expanded"
-msgstr "Informacja tekstowa, kiedy grupa jest rozwinięta"
-
-#. Note to translators: These two strings refer to the background color
-#. of a buddy list group when in its collapsed state
-msgid "Collapsed Background Color"
-msgstr "Koloru tła zwinięcia"
-
-msgid "The background color of a collapsed group"
-msgstr "Kolor tła zwiniętej grupy"
-
-#. Note to translators: These two strings refer to the font and color
-#. of a buddy list group when in its collapsed state
-msgid "Collapsed Text"
-msgstr "Zwinięty tekst"
-
-msgid "The text information for when a group is collapsed"
-msgstr "Informacja tekstowa, kiedy grupa jest zwinięta"
-
-#. Buddy
-#. Note to translators: These two strings refer to the background color
-#. of a buddy list contact or chat room
-msgid "Contact/Chat Background Color"
-msgstr "Kolor tła kontaktu/rozmowy"
-
-msgid "The background color of a contact or chat"
-msgstr "Kolor tła kontaktu lub rozmowy"
-
-#. Note to translators: These two strings refer to the font and color
-#. of a buddy list contact when in its expanded state
-msgid "Contact Text"
-msgstr "Tekst kontaktu"
-
-msgid "The text information for when a contact is expanded"
-msgstr "Informacja tekstowa, kiedy kontakt jest rozwinięty"
-
-#. Note to translators: These two strings refer to the font and color
-#. of a buddy list buddy when it is online
-msgid "Online Text"
-msgstr "Tekst trybu online"
-
-msgid "The text information for when a buddy is online"
-msgstr "Informacja tekstowa, kiedy znajomy jest w trybie online"
-
-#. Note to translators: These two strings refer to the font and color
-#. of a buddy list buddy when it is away
-msgid "Away Text"
-msgstr "Tekst niobecności"
-
-msgid "The text information for when a buddy is away"
-msgstr "Informacja tekstowa, kiedy znajomy jest nieobecny"
-
-#. Note to translators: These two strings refer to the font and color
-#. of a buddy list buddy when it is offline
-msgid "Offline Text"
-msgstr "Tekst trybu offline"
-
-msgid "The text information for when a buddy is offline"
-msgstr "Informacja tekstowa, kiedy znajomy jest w trybie offline"
-
-#. Note to translators: These two strings refer to the font and color
-#. of a buddy list buddy when it is idle
-msgid "Idle Text"
-msgstr "Tekst bezczynności"
-
-msgid "The text information for when a buddy is idle"
-msgstr "Informacja tekstowa, kiedy znajomy jest bezczynny"
-
-#. Note to translators: These two strings refer to the font and color
-#. of a buddy list buddy when they have sent you a new message
-msgid "Message Text"
-msgstr "Tekst wiadomości"
-
-msgid "The text information for when a buddy has an unread message"
-msgstr "Informacja tekstowa, kiedy znajomy posiada nieprzeczytaną wiadomość"
-
-#. Note to translators: These two strings refer to the font and color
-#. of a buddy list buddy when they have sent you a new message
-msgid "Message (Nick Said) Text"
-msgstr "Tekst wiadomości (pseudonim powiedział)"
-
-msgid ""
-"The text information for when a chat has an unread message that mentions "
-"your nickname"
-msgstr ""
-"Informacja tekstowa, kiedy w rozmowie jest nieprzeczytana wiadomość "
-"zawierająca pseudonim użytkownika"
-
-msgid "The text information for a buddy's status"
-msgstr "Informacja tekstowa stanu znajomego"
-
+#: ../pidgin/gtkblist.c:565
#, c-format
msgid "You have %d contact named %s. Would you like to merge them?"
msgid_plural ""
@@ -10836,335 +13962,395 @@ msgstr[0] "%d kontakt ma nazwę %s. Połączyć go?"
msgstr[1] "%d kontakty mają nazwę %s. Połączyć ich?"
msgstr[2] "%d kontaktów ma nazwę %s. Połączyć ich?"
+#: ../pidgin/gtkblist.c:566
msgid ""
"Merging these contacts will cause them to share a single entry on the buddy "
"list and use a single conversation window. You can separate them again by "
"choosing 'Expand' from the contact's context menu"
-msgstr ""
-"Połączenie tych kontaktów spowoduje współdzielenie pojedynczego wpisu na "
-"liście znajomych i używanie pojedynczego okna rozmowy. Można oddzielić je "
-"ponownie przez wybranie \"Rozwiń\" z menu kontekstowego kontaktu"
+msgstr "Połączenie tych kontaktów spowoduje współdzielenie pojedynczego wpisu na liście znajomych i używanie pojedynczego okna rozmowy. Można oddzielić je ponownie przez wybranie \"Rozwiń\" z menu kontekstowego kontaktu"
+#: ../pidgin/gtkblist.c:700
msgid "Please update the necessary fields."
msgstr "Proszę zaktualizować wymagane pola."
+#: ../pidgin/gtkblist.c:1027
msgid "A_ccount"
msgstr "Ko_nto"
+#: ../pidgin/gtkblist.c:1151
msgid ""
"Please enter the appropriate information about the chat you would like to "
"join.\n"
-msgstr ""
-"Proszę podać odpowiednie informacje o konferencji, do której dołączyć.\n"
+msgstr "Proszę podać odpowiednie informacje o konferencji, do której dołączyć.\n"
+#: ../pidgin/gtkblist.c:1156
msgid "Room _List"
msgstr "_Lista pokoi"
+#: ../pidgin/gtkblist.c:1418 ../pidgin/gtkprivacy.c:534
+#: ../pidgin/gtkprivacy.c:547
msgid "_Block"
msgstr "Za_blokuj"
+#: ../pidgin/gtkblist.c:1418
msgid "Un_block"
msgstr "Od_blokuj"
+#: ../pidgin/gtkblist.c:1461
msgid "Move to"
msgstr "Przenieś do"
+#: ../pidgin/gtkblist.c:1505
msgid "Get _Info"
msgstr "Pobierz _informacje"
+#: ../pidgin/gtkblist.c:1508 ../pidgin/pidginstock.c:82
msgid "I_M"
msgstr "Wiado_mość"
+#: ../pidgin/gtkblist.c:1517 ../pidgin/gtkconv.c:3159
msgid "_Audio Call"
msgstr "Rozmow_a głosowa"
+#: ../pidgin/gtkblist.c:1522
msgid "Audio/_Video Call"
msgstr "Rozmowa głosowa/_wideo"
+#: ../pidgin/gtkblist.c:1526 ../pidgin/gtkconv.c:3160
msgid "_Video Call"
msgstr "Rozmowa _wideo"
+#: ../pidgin/gtkblist.c:1538
msgid "_Send File..."
msgstr "_Wyślij plik..."
+#: ../pidgin/gtkblist.c:1545 ../pidgin/gtkconv.c:3166
msgid "Add Buddy _Pounce..."
msgstr "Dodaj przechwytywanie _zdarzeń..."
+#: ../pidgin/gtkblist.c:1550 ../pidgin/gtkblist.c:1554
+#: ../pidgin/gtkblist.c:1767 ../pidgin/gtkblist.c:1794
+#: ../pidgin/gtkconv.c:3153
msgid "View _Log"
-msgstr "Wyświetl _dziennik rozmów"
+msgstr "Wyśw_ietl archiwum rozmów"
+#: ../pidgin/gtkblist.c:1560 ../pidgin/gtkblist.c:1740
msgid "Hide When Offline"
msgstr "Ukryj w trybie offline"
+#: ../pidgin/gtkblist.c:1560 ../pidgin/gtkblist.c:1740
msgid "Show When Offline"
msgstr "Wyświetl w trybie offline"
+#: ../pidgin/gtkblist.c:1574 ../pidgin/gtkblist.c:1583
+#: ../pidgin/gtkblist.c:1777 ../pidgin/gtkblist.c:1800
msgid "_Alias..."
msgstr "_Alias..."
+#: ../pidgin/gtkblist.c:1577 ../pidgin/gtkblist.c:1585
+#: ../pidgin/gtkblist.c:1779 ../pidgin/gtkblist.c:1802
msgid "_Remove"
msgstr "_Usuń"
+#: ../pidgin/gtkblist.c:1710
msgid "Set Custom Icon"
msgstr "Ustaw własną ikonę"
+#: ../pidgin/gtkblist.c:1714 ../pidgin/gtkconv.c:2989
msgid "Remove Custom Icon"
msgstr "Usuń własną ikonę"
+#: ../pidgin/gtkblist.c:1728
msgid "Add _Buddy..."
msgstr "Dodaj _znajomego..."
+#: ../pidgin/gtkblist.c:1731 ../pidgin/gtkblist.c:3697
msgid "Add C_hat..."
msgstr "Dodaj _konferencję..."
+#: ../pidgin/gtkblist.c:1734
msgid "_Delete Group"
msgstr "_Usuń grupę"
+#: ../pidgin/gtkblist.c:1736
msgid "_Rename"
msgstr "_Zmień nazwę"
#. join button
+#: ../pidgin/gtkblist.c:1761 ../pidgin/gtkroomlist.c:320
+#: ../pidgin/gtkroomlist.c:631 ../pidgin/pidginstock.c:80
msgid "_Join"
msgstr "_Dołącz"
+#: ../pidgin/gtkblist.c:1763
msgid "Auto-Join"
msgstr "Automatyczne dołączenie"
+#: ../pidgin/gtkblist.c:1765
msgid "Persistent"
msgstr "Trwałe"
+#: ../pidgin/gtkblist.c:1775
msgid "_Edit Settings..."
msgstr "Zmodyfikuj ustawi_enia..."
+#: ../pidgin/gtkblist.c:1809 ../pidgin/gtkblist.c:1834
msgid "_Collapse"
msgstr "_Zwiń"
+#: ../pidgin/gtkblist.c:1839
msgid "_Expand"
msgstr "_Rozwiń"
-msgid "/Tools/Mute Sounds"
-msgstr "/Narzędzia/Wyciszenie dźwięków"
-
+#: ../pidgin/gtkblist.c:2547 ../pidgin/gtkconv.c:5806
+#: ../pidgin/gtkpounce.c:459
msgid ""
"You are not currently signed on with an account that can add that buddy."
-msgstr ""
-"Nie zalogowano do żadnego konta, do którego można dodać tego znajomego."
+msgstr "Nie zalogowano do żadnego konta, do którego można dodać tego znajomego."
#. I don't believe this can happen currently, I think
#. * everything that calls this function checks for one of the
#. * above node types first.
+#: ../pidgin/gtkblist.c:2920
msgid "Unknown node type"
msgstr "Nieznany typ węzła"
+#: ../pidgin/gtkblist.c:3627
msgid "Please select your mood from the list"
msgstr "Proszę wybrać nastrój z listy"
+#: ../pidgin/gtkblist.c:3661
msgid "Message (optional)"
msgstr "Wiadomość (opcjonalna)"
+#: ../pidgin/gtkblist.c:3666
msgid "Edit User Mood"
msgstr "Zmiana nastroju użytkownika"
#. NOTE: Do not set any accelerator to Control+O. It is mapped by
#. gtk_blist_key_press_cb to "Get User Info" on the selected buddy.
#. Buddies menu
-msgid "/_Buddies"
-msgstr "/_Znajomi"
-
-msgid "/Buddies/New Instant _Message..."
-msgstr "/Znajomi/Nowa wiado_mość..."
+#: ../pidgin/gtkblist.c:3689
+msgid "_Buddies"
+msgstr "_Znajomi"
-msgid "/Buddies/Join a _Chat..."
-msgstr "/Znajomi/Dołącz do _konferencji..."
+#: ../pidgin/gtkblist.c:3690 ../pidgin/gtkconv.c:3150
+msgid "New Instant _Message..."
+msgstr "Nowa _wiadomość..."
-msgid "/Buddies/Get User _Info..."
-msgstr "/Znajomi/_Informacje o użytkowniku..."
+#: ../pidgin/gtkblist.c:3691 ../pidgin/gtkconv.c:3151
+msgid "Join a _Chat..."
+msgstr "Dołącz do _konferencji..."
-msgid "/Buddies/View User _Log..."
-msgstr "/Znajomi/_Dziennik rozmów użytkownika..."
+#: ../pidgin/gtkblist.c:3692
+msgid "Get User _Info..."
+msgstr "_Informacje o użytkowniku..."
-msgid "/Buddies/Sh_ow"
-msgstr "/Znajomi/Wyświ_etlanie"
+#: ../pidgin/gtkblist.c:3693
+msgid "View User _Log..."
+msgstr "Wyświetl _archiwum rozmów użytkownika..."
-msgid "/Buddies/Show/_Offline Buddies"
-msgstr "/Znajomi/Wyświetlanie/Znajomi w trybie _offline"
+#: ../pidgin/gtkblist.c:3694
+msgid "Sh_ow"
+msgstr "Wyświ_etlanie"
-msgid "/Buddies/Show/_Empty Groups"
-msgstr "/Znajomi/Wyświetlanie/Pust_e grupy"
+#: ../pidgin/gtkblist.c:3695
+msgid "_Sort Buddies"
+msgstr "_Porządkowanie znajomych"
-msgid "/Buddies/Show/Buddy _Details"
-msgstr "/Znajomi/Wyświetlanie/_Szczegóły znajomych"
+#: ../pidgin/gtkblist.c:3696
+msgid "_Add Buddy..."
+msgstr "Dodaj zn_ajomego..."
-msgid "/Buddies/Show/Idle _Times"
-msgstr "/Znajomi/Wyświetlanie/I_nformacje o bezczynności"
+#: ../pidgin/gtkblist.c:3698
+msgid "Add _Group..."
+msgstr "Dodaj g_rupę..."
-msgid "/Buddies/Show/_Protocol Icons"
-msgstr "/Znajomi/Wyświetlanie/Ikony p_rotokołów"
+#: ../pidgin/gtkblist.c:3699 ../pidgin/gtkdocklet.c:782
+msgid "_Quit"
+msgstr "Za_kończ"
-msgid "/Buddies/_Sort Buddies"
-msgstr "/Znajomi/_Porządkowanie znajomych"
+#. Accounts menu
+#: ../pidgin/gtkblist.c:3702 ../pidgin/gtkdocklet.c:757
+msgid "_Accounts"
+msgstr "Kont_a"
-msgid "/Buddies/_Add Buddy..."
-msgstr "/Znajomi/Dod_aj znajomego..."
+#: ../pidgin/gtkblist.c:3703
+msgid "Manage Accounts"
+msgstr "Zarządzaj kontami"
-msgid "/Buddies/Add C_hat..."
-msgstr "/Znajomi/Dodaj ko_nferencję..."
+#. Tools
+#: ../pidgin/gtkblist.c:3706
+msgid "_Tools"
+msgstr "_Narzędzia"
-msgid "/Buddies/Add _Group..."
-msgstr "/Znajomi/Dodaj g_rupę..."
+#: ../pidgin/gtkblist.c:3707
+msgid "Buddy _Pounces"
+msgstr "Przechwytywanie _zdarzeń"
-msgid "/Buddies/_Quit"
-msgstr "/Znajomi/Za_kończ"
+#: ../pidgin/gtkblist.c:3708
+msgid "_Certificates"
+msgstr "_Certyfikaty"
-#. Accounts menu
-msgid "/_Accounts"
-msgstr "/Kont_a"
+#: ../pidgin/gtkblist.c:3709
+msgid "Custom Smile_ys"
+msgstr "Własne _emotikony"
-msgid "/Accounts/Manage Accounts"
-msgstr "/Konta/Zarządzaj kontami"
+#: ../pidgin/gtkblist.c:3710 ../pidgin/gtkdocklet.c:758
+msgid "Plu_gins"
+msgstr "_Wtyczki"
-#. Tools
-msgid "/_Tools"
-msgstr "/_Narzędzia"
+#: ../pidgin/gtkblist.c:3711 ../pidgin/gtkdocklet.c:759
+msgid "Pr_eferences"
+msgstr "_Preferencje"
-msgid "/Tools/Buddy _Pounces"
-msgstr "/Narzędzia/Przechwytywanie _zdarzeń"
+#: ../pidgin/gtkblist.c:3712
+msgid "Pr_ivacy"
+msgstr "Prywa_tność"
-msgid "/Tools/_Certificates"
-msgstr "/Narzędzia/_Certyfikaty"
+#: ../pidgin/gtkblist.c:3713
+msgid "Set _Mood"
+msgstr "_Ustaw nastrój"
-msgid "/Tools/Custom Smile_ys"
-msgstr "/Narzędzia/Własne _emotikony"
+#: ../pidgin/gtkblist.c:3714
+msgid "_File Transfers"
+msgstr "Przesyłanie p_lików"
-msgid "/Tools/Plu_gins"
-msgstr "/Narzędzia/W_tyczki"
+#: ../pidgin/gtkblist.c:3715
+msgid "R_oom List"
+msgstr "Lista p_okoi"
-msgid "/Tools/Pr_eferences"
-msgstr "/Narzędzia/_Preferencje"
+#: ../pidgin/gtkblist.c:3716
+msgid "System _Log"
+msgstr "_Dziennik systemowy"
-msgid "/Tools/Pr_ivacy"
-msgstr "/Narzędzia/Prywa_tność"
+#. Help
+#: ../pidgin/gtkblist.c:3719
+msgid "_Help"
+msgstr "Pomo_c"
-msgid "/Tools/Set _Mood"
-msgstr "/Narzędzia/_Ustaw nastrój"
+#: ../pidgin/gtkblist.c:3720
+msgid "Online _Help"
+msgstr "Pomo_c online"
-msgid "/Tools/_File Transfers"
-msgstr "/Narzędzia/Przesyłanie p_lików"
+#: ../pidgin/gtkblist.c:3721
+msgid "_Build Information"
+msgstr "I_nformacje programistyczne"
-msgid "/Tools/R_oom List"
-msgstr "/Narzędzia/Lista p_okoi"
+#: ../pidgin/gtkblist.c:3722
+msgid "_Debug Window"
+msgstr "Okno _debugowania"
-msgid "/Tools/System _Log"
-msgstr "/Narzędzia/_Dziennik systemowy"
+#: ../pidgin/gtkblist.c:3723
+msgid "De_veloper Information"
+msgstr "Infor_macje o programistach"
-msgid "/Tools/Mute _Sounds"
-msgstr "/Narzędzia/Wyci_szenie dźwięków"
+#: ../pidgin/gtkblist.c:3724
+msgid "_Plugin Information"
+msgstr "Informacje o _wtyczkach"
-#. Help
-msgid "/_Help"
-msgstr "/Pomo_c"
+#: ../pidgin/gtkblist.c:3725
+msgid "_Translator Information"
+msgstr "Inform_acje o tłumaczach"
-msgid "/Help/Online _Help"
-msgstr "/Help/Pomo_c online"
+#: ../pidgin/gtkblist.c:3726
+msgid "_About"
+msgstr "_O programie"
-msgid "/Help/_Build Information"
-msgstr "/Pomoc/I_nformacje programistyczne"
+#. Buddies->Show menu
+#: ../pidgin/gtkblist.c:3732
+msgid "_Offline Buddies"
+msgstr "Znajomi w trybie _offline"
-msgid "/Help/_Debug Window"
-msgstr "/Pomoc/Okno _debugowania"
+#: ../pidgin/gtkblist.c:3733
+msgid "_Empty Groups"
+msgstr "Pust_e grupy"
-msgid "/Help/De_veloper Information"
-msgstr "/Pomoc/Infor_macje o programistach"
+#: ../pidgin/gtkblist.c:3734
+msgid "Buddy _Details"
+msgstr "_Szczegóły znajomych"
-msgid "/Help/_Plugin Information"
-msgstr "/Pomoc/Informacje o _wtyczkach"
+#: ../pidgin/gtkblist.c:3735
+msgid "Idle _Times"
+msgstr "I_nformacje o bezczynności"
-msgid "/Help/_Translator Information"
-msgstr "/Pomoc/Inform_acje o tłumaczach"
+#: ../pidgin/gtkblist.c:3736
+msgid "_Protocol Icons"
+msgstr "Ikony p_rotokołów"
-msgid "/Help/_About"
-msgstr "/Pomoc/_O programie"
+#. Tools menu
+#: ../pidgin/gtkblist.c:3739 ../pidgin/gtkdocklet.c:763
+msgid "Mute _Sounds"
+msgstr "Wyci_szenie dźwięków"
+#: ../pidgin/gtkblist.c:3828
#, c-format
msgid "<b>Account:</b> %s"
msgstr "<b>Konto:</b> %s"
+#: ../pidgin/gtkblist.c:3847
#, c-format
msgid ""
"\n"
"<b>Occupants:</b> %d"
-msgstr ""
-"\n"
-"<b>Użytkownicy:</b> %d"
+msgstr "\n<b>Użytkownicy:</b> %d"
+#: ../pidgin/gtkblist.c:3853
#, c-format
msgid ""
"\n"
"<b>Topic:</b> %s"
-msgstr ""
-"\n"
-"<b>Temat:</b> %s"
+msgstr "\n<b>Temat:</b> %s"
+#: ../pidgin/gtkblist.c:3853
msgid "(no topic set)"
msgstr "(nie ustawiono tematu)"
+#: ../pidgin/gtkblist.c:3933
msgid "Buddy Alias"
msgstr "Alias znajomego"
+#: ../pidgin/gtkblist.c:3960
msgid "Logged In"
msgstr "Zalogowany"
+#: ../pidgin/gtkblist.c:4006
msgid "Last Seen"
msgstr "Ostatnio widziany"
+#: ../pidgin/gtkblist.c:4028
msgid "Spooky"
msgstr "Niesamowite"
+#: ../pidgin/gtkblist.c:4030
msgid "Awesome"
msgstr "Fajne"
+#: ../pidgin/gtkblist.c:4032
msgid "Rockin'"
msgstr "Wstrząsające"
+#: ../pidgin/gtkblist.c:4062
msgid "Total Buddies"
msgstr "Razem znajomych"
+#: ../pidgin/gtkblist.c:4426
#, c-format
msgid "Idle %dd %dh %02dm"
msgstr "Bezczynny %dd %dh %02dm"
+#: ../pidgin/gtkblist.c:4428
#, c-format
msgid "Idle %dh %02dm"
msgstr "Bezczynny %dh %02dm"
+#: ../pidgin/gtkblist.c:4430
#, c-format
msgid "Idle %dm"
msgstr "Bezczynny %dm"
-msgid "/Buddies/New Instant Message..."
-msgstr "/Znajomi/Nowa wiadomość..."
-
-msgid "/Buddies/Join a Chat..."
-msgstr "/Znajomi/Dołącz do konferencji..."
-
-msgid "/Buddies/Get User Info..."
-msgstr "/Znajomi/Pobierz informacje o użytkowniku..."
-
-msgid "/Buddies/Add Buddy..."
-msgstr "/Znajomi/Dodaj znajomego..."
-
-msgid "/Buddies/Add Chat..."
-msgstr "/Znajomi/Dodaj konferencję..."
-
-msgid "/Buddies/Add Group..."
-msgstr "/Znajomi/Dodaj grupę..."
-
-msgid "/Tools/Privacy"
-msgstr "/Narzędzia/Prywatność"
-
-msgid "/Tools/Room List"
-msgstr "/Narzędzia/Lista pokoi"
-
+#: ../pidgin/gtkblist.c:4788 ../pidgin/gtkdocklet.c:197
+#: ../pidgin/gtkdocklet.c:202
#, c-format
msgid "%d unread message from %s\n"
msgid_plural "%d unread messages from %s\n"
@@ -11172,427 +14358,400 @@ msgstr[0] "%d nieprzeczytana wiadomość od użytkownika %s\n"
msgstr[1] "%d nieprzeczytane wiadomości od użytkownika %s\n"
msgstr[2] "%d nieprzeczytanych wiadomości od użytkownika %s\n"
+#: ../pidgin/gtkblist.c:4987
msgid "Manually"
msgstr "Ręcznie"
+#: ../pidgin/gtkblist.c:4989
msgid "By status"
msgstr "Według stanu"
+#: ../pidgin/gtkblist.c:4990
msgid "By recent log activity"
-msgstr "Według ostatniej aktywności w dzienniku"
+msgstr "Od ostatniej rozmowy"
+#: ../pidgin/gtkblist.c:5326
#, c-format
msgid "%s disconnected"
msgstr "Użytkownik %s został rozłączony"
+#: ../pidgin/gtkblist.c:5328
#, c-format
msgid "%s disabled"
msgstr "Użytkownik %s wyłączony"
+#: ../pidgin/gtkblist.c:5332
msgid "Reconnect"
msgstr "Połącz ponownie"
+#: ../pidgin/gtkblist.c:5332 ../pidgin/gtkblist.c:5434
msgid "Re-enable"
msgstr "Włącz ponownie"
+#: ../pidgin/gtkblist.c:5345
msgid "SSL FAQs"
msgstr "FAQ protokołu SSL"
+#: ../pidgin/gtkblist.c:5432
msgid "Welcome back!"
msgstr "Witaj z powrotem!"
+#: ../pidgin/gtkblist.c:5468
#, c-format
msgid "%d account was disabled because you signed on from another location:"
msgid_plural ""
"%d accounts were disabled because you signed on from another location:"
msgstr[0] "%d konto zostało wyłączone, ponieważ zalogowano z innego położenia:"
msgstr[1] "%d konta zostały wyłączone, ponieważ zalogowano z innego położenia:"
-msgstr[2] ""
-"%d kont zostało wyłączonych, ponieważ zalogowano z innego położenia:"
+msgstr[2] "%d kont zostało wyłączonych, ponieważ zalogowano z innego położenia:"
+#: ../pidgin/gtkblist.c:5721
msgid "<b>Username:</b>"
msgstr "<b>Nazwa użytkownika:</b>"
+#: ../pidgin/gtkblist.c:5728
msgid "<b>Password:</b>"
msgstr "<b>Hasło:</b>"
+#: ../pidgin/gtkblist.c:5739
msgid "_Login"
msgstr "_Login"
-msgid "/Accounts"
-msgstr "/Konta"
-
-#. Translators: Please maintain the use of -> and <- to refer to menu heirarchy
+#. Translators: Please maintain the use of -> and <- to refer to menu
+#. heirarchy
+#: ../pidgin/gtkblist.c:6038
#, c-format
msgid ""
"<span weight='bold' size='larger'>Welcome to %s!</span>\n"
"\n"
-"You have no accounts enabled. Enable your IM accounts from the <b>Accounts</"
-"b> window at <b>Accounts->Manage Accounts</b>. Once you enable accounts, "
-"you'll be able to sign on, set your status, and talk to your friends."
-msgstr ""
-"<span weight='bold' size='larger'>Witaj w programie %s!</span>\n"
-"\n"
-"Nie włączono żadnych kont. Proszę włączyć konta z okna <b>Konta</b> w "
-"<b>Konta->Zarządzaj kontami</b>. Po włączeniu kont można się zalogować, "
-"ustawić stan i rozmawiać z przyjaciółmi."
-
-#. set the Show Offline Buddies option. must be done
-#. * after the treeview or faceprint gets mad. -Robot101
-#.
-msgid "/Buddies/Show/Offline Buddies"
-msgstr "/Znajomi/Wyświetlanie/Znajomi w trybie offline"
-
-msgid "/Buddies/Show/Empty Groups"
-msgstr "/Znajomi/Wyświetlanie/Puste grupy"
-
-msgid "/Buddies/Show/Buddy Details"
-msgstr "/Znajomi/Wyświetlanie/Szczegóły znajomych"
-
-msgid "/Buddies/Show/Idle Times"
-msgstr "/Znajomi/Wyświetlanie/Czas bezczynności"
-
-msgid "/Buddies/Show/Protocol Icons"
-msgstr "/Znajomi/Wyświetlanie/Ikony protokołów"
+"You have no accounts enabled. Enable your IM accounts from the <b>Accounts</b> window at <b>Accounts->Manage Accounts</b>. Once you enable accounts, you'll be able to sign on, set your status, and talk to your friends."
+msgstr "<span weight='bold' size='larger'>Witaj w programie %s!</span>\n\nNie włączono żadnych kont. Proszę włączyć konta z okna <b>Konta</b> w <b>Konta->Zarządzaj kontami</b>. Po włączeniu kont można się zalogować, ustawić stan i rozmawiać z przyjaciółmi."
+#: ../pidgin/gtkblist.c:7328
msgid "Add a buddy.\n"
msgstr "Dodanie znajomego.\n"
+#: ../pidgin/gtkblist.c:7343
msgid "Buddy's _username:"
msgstr "Nazwa _użytkownika:"
+#: ../pidgin/gtkblist.c:7360
msgid "(Optional) A_lias:"
msgstr "(Opcjonalny) a_lias:"
+#: ../pidgin/gtkblist.c:7371
msgid "(Optional) _Invite message:"
msgstr "(Opcjonalna) w_iadomość zaproszenia:"
+#: ../pidgin/gtkblist.c:7376
msgid "Add buddy to _group:"
msgstr "Dodanie znajomego do g_rupy:"
+#: ../pidgin/gtkblist.c:7483
msgid "This protocol does not support chat rooms."
msgstr "Ten protokół nie obsługuje pokojów konferencji."
+#: ../pidgin/gtkblist.c:7502
msgid ""
"You are not currently signed on with any protocols that have the ability to "
"chat."
-msgstr ""
-"Obecnie nie zalogowano za pomocą protokołu, który posiada możliwość "
-"konferencji."
+msgstr "Obecnie nie zalogowano za pomocą protokołu, który posiada możliwość konferencji."
+#: ../pidgin/gtkblist.c:7511
msgid ""
"Please enter an alias, and the appropriate information about the chat you "
"would like to add to your buddy list.\n"
-msgstr ""
-"Proszę podać alias i odpowiednie informacje o konferencji, która ma zostać "
-"dodana do listy znajomych.\n"
+msgstr "Proszę podać alias i odpowiednie informacje o konferencji, która ma zostać dodana do listy znajomych.\n"
+#: ../pidgin/gtkblist.c:7532
msgid "A_lias:"
msgstr "A_lias:"
+#: ../pidgin/gtkblist.c:7539
msgid "_Group:"
msgstr "_Grupa:"
+#: ../pidgin/gtkblist.c:7543
msgid "Auto_join when account connects."
msgstr "Aut_omatyczne dołączanie podczas połączenia konta"
+#: ../pidgin/gtkblist.c:7544
msgid "_Remain in chat after window is closed."
msgstr "Pozostawanie w konfe_rencji po zamknięciu okna"
+#: ../pidgin/gtkblist.c:7567
msgid "Please enter the name of the group to be added."
msgstr "Proszę podać nazwę dodawanej grupy."
+#: ../pidgin/gtkblist.c:8266
msgid "Enable Account"
msgstr "Włącz konto"
-msgid "<PurpleMain>/Accounts/Enable Account"
-msgstr "<PurpleMain>/Konta/Włącz konto"
-
-msgid "<PurpleMain>/Accounts/"
-msgstr "<PurpleMain>/Konta/"
-
+#: ../pidgin/gtkblist.c:8348
msgid "_Edit Account"
msgstr "Zm_odyfikuj konto"
+#: ../pidgin/gtkblist.c:8366
msgid "Set _Mood..."
msgstr "Ustaw _nastrój..."
+#: ../pidgin/gtkblist.c:8405 ../pidgin/gtkconv.c:3482
+#: ../pidgin/gtkwebview.c:776
msgid "No actions available"
msgstr "Brak dostępnych czynności"
+#: ../pidgin/gtkblist.c:8412
msgid "_Disable"
msgstr "_Wyłącz"
-msgid "/Tools"
-msgstr "/Narzędzia"
-
-msgid "/Buddies/Sort Buddies"
-msgstr "/Znajomi/Porządkowanie znajomych"
-
+#: ../pidgin/gtkcertmgr.c:184
msgid "Type the host name for this certificate."
msgstr "Proszę podać nazwę komputera dla tego certyfikatu."
+#. Fire the notification
+#: ../pidgin/gtkcertmgr.c:326
+#, c-format
+msgid "Certificate Information for %s"
+msgstr "Informacje o certyfikacie dla %s"
+
#. Widget creation function
+#: ../pidgin/gtkcertmgr.c:538
msgid "SSL Servers"
msgstr "Serwery SSL"
+#: ../pidgin/gtkconv.c:389
+msgid "Unsafe debugging is now disabled."
+msgstr "Niebezpieczne debugowanie jest teraz wyłączone."
+
+#: ../pidgin/gtkconv.c:394
+msgid "Unsafe debugging is now enabled."
+msgstr "Niebezpieczne debugowanie jest teraz włączone."
+
+#: ../pidgin/gtkconv.c:403
+msgid "Verbose debugging is now disabled."
+msgstr "Rozszerzone debugowanie jest teraz wyłączone."
+
+#: ../pidgin/gtkconv.c:408
+msgid "Verbose debugging is now enabled."
+msgstr "Rozszerzone debugowanie jest teraz włączone."
+
+#: ../pidgin/gtkconv.c:415
+msgid "Supported debug options are: plugins version unsafe verbose"
+msgstr "Obsługiwane opcje debugowania: plugins version unsafe verbose"
+
+#: ../pidgin/gtkconv.c:479
+msgid ""
+"Use \"/help &lt;command&gt;\" for help on a specific command.<br/>The "
+"following commands are available in this context:<br/>"
+msgstr "Użycie \"/help &lt;polecenie&gt;\" wyświetli pomoc podanego polecenia.<br/>Następujące polecenia są dostępne w tym kontekście:<br/>"
+
+#: ../pidgin/gtkconv.c:566
msgid "Unknown command."
msgstr "Nieznane polecenie."
+#: ../pidgin/gtkconv.c:854 ../pidgin/gtkconv.c:884
msgid "That buddy is not on the same protocol as this chat."
msgstr "Ten znajomy nie jest połączony tym samym protokołem, co konferencja."
+#: ../pidgin/gtkconv.c:877
msgid ""
"You are not currently signed on with an account that can invite that buddy."
-msgstr ""
-"Obecnie nie zalogowano za pomocą konta, które może zapraszać znajomych."
+msgstr "Obecnie nie zalogowano za pomocą konta, które może zapraszać znajomych."
+#: ../pidgin/gtkconv.c:927
msgid "Invite Buddy Into Chat Room"
msgstr "Zaproszenie znajomego do pokoju konferencji"
+#: ../pidgin/gtkconv.c:977
msgid "_Buddy:"
msgstr "_Znajomy:"
+#: ../pidgin/gtkconv.c:990 ../pidgin/gtksavedstatuses.c:1146
+#: ../pidgin/gtksavedstatuses.c:1454
msgid "_Message:"
msgstr "_Wiadomość:"
+#: ../pidgin/gtkconv.c:1072
#, c-format
msgid "<h1>Conversation with %s</h1>\n"
msgstr "<h1>Rozmowa z użytkownikiem %s</h1>\n"
+#: ../pidgin/gtkconv.c:1108
msgid "Save Conversation"
msgstr "Zapisz rozmowę"
+#: ../pidgin/gtkconv.c:1692
msgid "Un-Ignore"
msgstr "Usuń zignorowanie"
+#: ../pidgin/gtkconv.c:1695
msgid "Ignore"
msgstr "Zignoruj"
-msgid "Get Away Message"
-msgstr "Uzyskaj wiadomość nieobecności"
-
+#: ../pidgin/gtkconv.c:1728
msgid "Last Said"
msgstr "Ostatnio powiedziane"
+#: ../pidgin/gtkconv.c:2808
msgid "Unable to save icon file to disk."
msgstr "Nie można zapisać pliku ikony na dysku."
+#: ../pidgin/gtkconv.c:2903
msgid "Save Icon"
msgstr "Zapisz ikonę"
+#: ../pidgin/gtkconv.c:2960
msgid "Animate"
msgstr "Animuj"
+#: ../pidgin/gtkconv.c:2965
msgid "Hide Icon"
msgstr "Ukryj ikonę"
+#: ../pidgin/gtkconv.c:2968
msgid "Save Icon As..."
msgstr "Zapisz ikonę jako..."
+#: ../pidgin/gtkconv.c:2972
msgid "Set Custom Icon..."
msgstr "Ustaw własną ikonkę..."
+#: ../pidgin/gtkconv.c:2976
msgid "Change Size"
msgstr "Zmień rozmiar"
+#: ../pidgin/gtkconv.c:3129
msgid "Show All"
msgstr "Wyświetl wszystko"
#. Conversation menu
-msgid "/_Conversation"
-msgstr "/_Rozmowa"
-
-msgid "/Conversation/New Instant _Message..."
-msgstr "/Rozmowa/Nowa _wiadomość..."
-
-msgid "/Conversation/Join a _Chat..."
-msgstr "/Rozmowa/_Dołącz do konferencji..."
-
-msgid "/Conversation/_Find..."
-msgstr "/Rozmowa/_Znajdź..."
-
-msgid "/Conversation/View _Log"
-msgstr "/Rozmowa/Wyświetl _dziennik rozmów"
+#: ../pidgin/gtkconv.c:3149
+msgid "_Conversation"
+msgstr "_Rozmowa"
-msgid "/Conversation/_Save As..."
-msgstr "/Rozmowa/Zapi_sz jako..."
+#: ../pidgin/gtkconv.c:3152
+msgid "_Find..."
+msgstr "_Znajdź..."
-msgid "/Conversation/Clea_r Scrollback"
-msgstr "/Rozmowa/Wy_czyść okno"
+#: ../pidgin/gtkconv.c:3154
+msgid "_Save As..."
+msgstr "Zapi_sz jako..."
-msgid "/Conversation/M_edia"
-msgstr "/Rozmowa/Multim_edia"
+#: ../pidgin/gtkconv.c:3155
+msgid "Clea_r Scrollback"
+msgstr "Wy_czyść okno"
-msgid "/Conversation/Media/_Audio Call"
-msgstr "/Rozmowa/Multimedia/Rozmow_a głosowa"
+#: ../pidgin/gtkconv.c:3158
+msgid "M_edia"
+msgstr "Multim_edia"
-msgid "/Conversation/Media/_Video Call"
-msgstr "/Rozmowa/Multimedia/Rozmowa _wideo"
+#: ../pidgin/gtkconv.c:3161
+msgid "Audio/Video _Call"
+msgstr "R_ozmowa głosowa/wideo"
-msgid "/Conversation/Media/Audio\\/Video _Call"
-msgstr "/Rozmowa/Multimedia/_Rozmowa głosowa\\/wideo"
+#: ../pidgin/gtkconv.c:3164
+msgid "Se_nd File..."
+msgstr "Wyślij p_lik..."
-msgid "/Conversation/Se_nd File..."
-msgstr "/Rozmowa/Wyślij p_lik..."
+#: ../pidgin/gtkconv.c:3165
+msgid "Get _Attention"
+msgstr "Zw_róć uwagę"
-msgid "/Conversation/Get _Attention"
-msgstr "/Rozmowa/Zw_róć uwagę"
-
-msgid "/Conversation/Add Buddy _Pounce..."
-msgstr "/Rozmowa/Dodaj przechwytywanie _zdarzeń..."
-
-msgid "/Conversation/_Get Info"
-msgstr "/Rozmowy/_Pobierz informacje"
+#: ../pidgin/gtkconv.c:3167 ../pidgin/pidginstock.c:83
+msgid "_Get Info"
+msgstr "_Pobierz informacje"
-msgid "/Conversation/In_vite..."
-msgstr "/Rozmowa/Z_aproś..."
+#: ../pidgin/gtkconv.c:3168
+msgid "In_vite..."
+msgstr "Z_aproś..."
-msgid "/Conversation/M_ore"
-msgstr "/Rozmowa/Więc_ej"
+#: ../pidgin/gtkconv.c:3169
+msgid "M_ore"
+msgstr "Więc_ej"
-msgid "/Conversation/Al_ias..."
-msgstr "/Rozmowy/_Alias..."
+#: ../pidgin/gtkconv.c:3170
+msgid "Al_ias..."
+msgstr "_Alias..."
-msgid "/Conversation/_Block..."
-msgstr "/Rozmowa/Za_blokuj..."
+#: ../pidgin/gtkconv.c:3171
+msgid "_Block..."
+msgstr "Za_blokuj..."
-msgid "/Conversation/_Unblock..."
-msgstr "/Rozmowa/Od_blokuj..."
+#: ../pidgin/gtkconv.c:3172
+msgid "_Unblock..."
+msgstr "Od_blokuj..."
-msgid "/Conversation/_Add..."
-msgstr "/Rozmowa/Dod_aj..."
+#: ../pidgin/gtkconv.c:3173 ../pidgin/pidginstock.c:86
+msgid "_Add..."
+msgstr "Dod_aj..."
-msgid "/Conversation/_Remove..."
-msgstr "/Rozmowa/_Usuń..."
+#: ../pidgin/gtkconv.c:3174
+msgid "_Remove..."
+msgstr "_Usuń..."
-msgid "/Conversation/Insert Lin_k..."
-msgstr "/Rozmowa/Wstaw odnośni_k..."
+#: ../pidgin/gtkconv.c:3175
+msgid "Insert Lin_k..."
+msgstr "Wstaw odnośni_k..."
-msgid "/Conversation/Insert Imag_e..."
-msgstr "/Rozmowa/Wstaw _obraz..."
+#: ../pidgin/gtkconv.c:3176
+msgid "Insert Imag_e..."
+msgstr "Wstaw _obraz..."
-msgid "/Conversation/_Close"
-msgstr "/Rozmowa/Za_mknij"
+#: ../pidgin/gtkconv.c:3177
+msgid "_Close"
+msgstr "Za_mknij"
#. Options
-msgid "/_Options"
-msgstr "/_Opcje"
-
-msgid "/Options/Enable _Logging"
-msgstr "/Opcje/Zapis do _dziennika"
-
-msgid "/Options/Enable _Sounds"
-msgstr "/Opcje/Włą_czenie dźwięków"
-
-msgid "/Options/Show Formatting _Toolbars"
-msgstr "/Opcje/Wyświetlanie pasków narzędziowych forma_towania"
-
-msgid "/Options/Show Ti_mestamps"
-msgstr "/Opcje/_Wyświetlanie dat"
-
-msgid "/Conversation/More"
-msgstr "/Rozmowa/Więcej"
-
-msgid "/Options"
-msgstr "/Opcje"
-
-#. The menubar has been deactivated. Make sure the 'More' submenu is regenerated next time
-#. * the 'Conversation' menu pops up.
-#. Make sure the 'Conversation -> More' menuitems are regenerated whenever
-#. * the 'Conversation' menu pops up because the entries can change after the
-#. * conversation is created.
-msgid "/Conversation"
-msgstr "/Rozmowa"
-
-msgid "/Conversation/View Log"
-msgstr "/Rozmowa/Wyświetl dziennik rozmów"
-
-msgid "/Conversation/Media/Audio Call"
-msgstr "/Rozmowa/Multimedia/Rozmowa głosowa"
-
-msgid "/Conversation/Media/Video Call"
-msgstr "/Rozmowa/Multimedia/Rozmowa wideo"
-
-msgid "/Conversation/Media/Audio\\/Video Call"
-msgstr "/Rozmowa/Multimedia/Rozmowa głosowa\\/wideo"
-
-msgid "/Conversation/Send File..."
-msgstr "/Rozmowa/Wyślij plik..."
-
-msgid "/Conversation/Get Attention"
-msgstr "/Rozmowa/Zwróć uwagę"
-
-msgid "/Conversation/Add Buddy Pounce..."
-msgstr "/Rozmowa/Dodaj przechwytywanie zdarzeń..."
-
-msgid "/Conversation/Get Info"
-msgstr "/Rozmowa/Pobierz informacje"
-
-msgid "/Conversation/Invite..."
-msgstr "/Rozmowa/Zaproś..."
-
-msgid "/Conversation/Alias..."
-msgstr "/Rozmowa/Alias..."
-
-msgid "/Conversation/Block..."
-msgstr "/Rozmowa/Zablokuj..."
-
-msgid "/Conversation/Unblock..."
-msgstr "/Rozmowa/Odblokuj..."
-
-msgid "/Conversation/Add..."
-msgstr "/Rozmowa/Dodaj..."
-
-msgid "/Conversation/Remove..."
-msgstr "/Rozmowa/Usuń..."
+#: ../pidgin/gtkconv.c:3180
+msgid "_Options"
+msgstr "_Opcje"
-msgid "/Conversation/Insert Link..."
-msgstr "/Rozmowa/Wstaw odnośnik..."
+#: ../pidgin/gtkconv.c:3185
+msgid "Enable _Logging"
+msgstr "Zapis do _archiwum rozmów"
-msgid "/Conversation/Insert Image..."
-msgstr "/Rozmowa/Wstaw obraz..."
+#: ../pidgin/gtkconv.c:3186
+msgid "Enable _Sounds"
+msgstr "Włą_czenie dźwięków"
-msgid "/Options/Enable Logging"
-msgstr "/Opcje/Zapis do dziennika"
-
-msgid "/Options/Enable Sounds"
-msgstr "/Opcje/Włączenie dźwięków"
-
-msgid "/Options/Show Formatting Toolbars"
-msgstr "/Opcje/Wyświetlanie pasków narzędziowych formatowania"
-
-msgid "/Options/Show Timestamps"
-msgstr "/Opcje/Wyświetlanie dat"
+#: ../pidgin/gtkconv.c:3187
+msgid "Show Formatting _Toolbars"
+msgstr "Wyświetlanie pasków narzędziowych forma_towania"
+#: ../pidgin/gtkconv.c:3786
msgid "User is typing..."
msgstr "Użytkownik coś pisze..."
+#: ../pidgin/gtkconv.c:3858
#, c-format
msgid ""
"\n"
"%s has stopped typing"
-msgstr ""
-"\n"
-"Użytkownik %s przestał pisać"
+msgstr "\nUżytkownik %s przestał pisać"
#. Build the Send To menu
+#: ../pidgin/gtkconv.c:4071 ../pidgin/gtkconv.c:9425
msgid "S_end To"
msgstr "_Wyślij do"
+#: ../pidgin/gtkconv.c:4911 ../pidgin/plugins/sendbutton.c:61
msgid "_Send"
msgstr "_Wyślij"
#. Setup the label telling how many people are in the room.
+#: ../pidgin/gtkconv.c:5098
msgid "0 people in room"
msgstr "Brak osób w pokoju"
+#: ../pidgin/gtkconv.c:5278
msgid "Close Find bar"
msgstr "Zamknij pasek wyszukiwania"
+#: ../pidgin/gtkconv.c:5280
msgid "Find:"
msgstr "Znajdź:"
+#: ../pidgin/gtkconv.c:7079 ../pidgin/gtkconv.c:7193
#, c-format
msgid "%d person in room"
msgid_plural "%d people in room"
@@ -11600,554 +14759,696 @@ msgstr[0] "%d osoba w pokoju"
msgstr[1] "%d osoby w pokoju"
msgstr[2] "%d osób w pokoju"
+#: ../pidgin/gtkconv.c:7592
msgid "Stopped Typing"
msgstr "Zatrzymanie pisania"
+#: ../pidgin/gtkconv.c:7595
msgid "Nick Said"
msgstr "Pseudonim powiedział"
+#: ../pidgin/gtkconv.c:7598
msgid "Unread Messages"
msgstr "Nieprzeczytane wiadomości"
+#: ../pidgin/gtkconv.c:7604
msgid "New Event"
msgstr "Nowe zdarzenie"
+#: ../pidgin/gtkconv.c:8346
msgid ""
"The account has disconnected and you are no longer in this chat. You will "
"automatically rejoin the chat when the account reconnects."
-msgstr ""
-"Konto zostało rozłączone i użytkownik nie jest już obecny w tej konferencji. "
-"Po nawiązaniu połączenia nastąpi ponowne dołączenie."
+msgstr "Konto zostało rozłączone i użytkownik nie jest już obecny w tej konferencji. Po nawiązaniu połączenia nastąpi ponowne dołączenie."
+#: ../pidgin/gtkconv.c:8836
msgid "clear: Clears all conversation scrollbacks."
msgstr "clear: czyści wszystkie okna rozmów."
+#: ../pidgin/gtkconv.c:9095
msgid "Confirm close"
msgstr "Potwierdzenie zamknięcia"
+#: ../pidgin/gtkconv.c:9125
msgid "You have unread messages. Are you sure you want to close the window?"
msgstr "W oknie są nieprzeczytane wiadomości. Na pewno je zamknąć?"
+#: ../pidgin/gtkconv.c:10208
msgid "Close other tabs"
msgstr "Zamknij inne karty"
+#: ../pidgin/gtkconv.c:10214
msgid "Close all tabs"
msgstr "Zamknij wszystkie karty"
+#: ../pidgin/gtkconv.c:10222
msgid "Detach this tab"
msgstr "Odłącz tę kartę"
+#: ../pidgin/gtkconv.c:10228
msgid "Close this tab"
msgstr "Zamknij tę kartę"
+#: ../pidgin/gtkconv.c:10431
msgid "Close conversation"
msgstr "Zamknij rozmowę"
+#: ../pidgin/gtkconv.c:11076
msgid "Last created window"
msgstr "Ostatnie utworzone okno"
+#: ../pidgin/gtkconv.c:11078
msgid "Separate IM and Chat windows"
msgstr "Oddzielne okno dla rozmów i konferencji"
+#: ../pidgin/gtkconv.c:11080 ../pidgin/gtkprefs.c:2444
msgid "New window"
msgstr "Nowe okno"
+#: ../pidgin/gtkconv.c:11082
msgid "By group"
msgstr "Według grup"
+#: ../pidgin/gtkconv.c:11084
msgid "By account"
msgstr "Według konta"
-msgid "Find"
-msgstr "Znajdź"
-
-msgid "_Search for:"
-msgstr "Wy_szukiwanie:"
-
+#: ../pidgin/gtkdebug.c:125
msgid "Save Debug Log"
msgstr "Zapis do dziennika debugowania"
+#: ../pidgin/gtkdebug.c:334
msgid "Invert"
msgstr "Odwróć"
+#: ../pidgin/gtkdebug.c:337
msgid "Highlight matches"
msgstr "Wyróżnianie wyników"
+#: ../pidgin/gtkdebug.c:403
msgid "_Icon Only"
msgstr "Tylko _ikona"
+#: ../pidgin/gtkdebug.c:404
msgid "_Text Only"
msgstr "Tylko _tekst"
+#: ../pidgin/gtkdebug.c:405
msgid "_Both Icon & Text"
msgstr "I_kona i tekst"
+#: ../pidgin/gtkdebug.c:521 ../pidgin/gtkdebug.c:522
msgid "Filter"
msgstr "Filtr"
+#: ../pidgin/gtkdebug.c:540
msgid "Right click for more options."
msgstr "Naciśnięcie prawym przyciskiem myszy wyświetli więcej opcji."
+#: ../pidgin/gtkdebug.c:580
msgid "Level "
msgstr "Poziom "
+#: ../pidgin/gtkdebug.c:585
msgid "Select the debug filter level."
msgstr "Proszę wybrać poziom filtru debugowania."
+#: ../pidgin/gtkdebug.c:589
msgid "All"
msgstr "Wszystko"
+#: ../pidgin/gtkdebug.c:590
msgid "Misc"
msgstr "Różne"
+#: ../pidgin/gtkdebug.c:592
msgid "Warning"
msgstr "Ostrzeżenie"
+#: ../pidgin/gtkdebug.c:593
msgid "Error "
msgstr "Błąd "
+#: ../pidgin/gtkdebug.c:594
msgid "Fatal Error"
msgstr "Błąd krytyczny"
+#: ../pidgin/gtkdialogs.c:78
msgid "artist"
msgstr "artysta"
+#: ../pidgin/gtkdialogs.c:87
msgid "voice and video"
msgstr "dźwięk i wideo"
+#: ../pidgin/gtkdialogs.c:89
msgid "support"
msgstr "wsparcie"
+#: ../pidgin/gtkdialogs.c:91 ../pidgin/gtkdialogs.c:120
msgid "webmaster"
msgstr "webmaster"
+#: ../pidgin/gtkdialogs.c:111
msgid "win32 port"
msgstr "port dla Win32"
#. Translators: This is a person's name. For most languages we recommend
#. not translating it.
+#: ../pidgin/gtkdialogs.c:115
msgid "Ka-Hing Cheung"
msgstr "Ka-Hing Cheung"
+#: ../pidgin/gtkdialogs.c:116 ../pidgin/gtkdialogs.c:118
msgid "maintainer"
msgstr "opiekun"
+#: ../pidgin/gtkdialogs.c:119
msgid "libfaim maintainer"
msgstr "opiekun biblioteki libfaim"
-#. If "lazy bum" translates literally into a serious insult, use something else or omit it.
+#. If "lazy bum" translates literally into a serious insult, use something
+#. else or omit it.
+#: ../pidgin/gtkdialogs.c:124
msgid "hacker and designated driver [lazy bum]"
msgstr "haker i wyznaczony kierowca"
+#: ../pidgin/gtkdialogs.c:128
msgid "support/QA"
msgstr "wsparcie/ocena jakości"
+#: ../pidgin/gtkdialogs.c:129
msgid "XMPP"
msgstr "XMPP"
+#: ../pidgin/gtkdialogs.c:130
msgid "original author"
msgstr "autor pierwszej wersji"
+#: ../pidgin/gtkdialogs.c:132
msgid "lead developer"
msgstr "główny programista"
+#: ../pidgin/gtkdialogs.c:143
msgid "Senior Contributor/QA"
msgstr "Starszy współtwórca/ocena jakości"
+#: ../pidgin/gtkdialogs.c:152
msgid "Afrikaans"
msgstr "afrykanerski"
+#: ../pidgin/gtkdialogs.c:154 ../pidgin/gtkdialogs.c:266
msgid "Arabic"
msgstr "arabski"
+#: ../pidgin/gtkdialogs.c:155
msgid "Assamese"
msgstr "asamski"
+#: ../pidgin/gtkdialogs.c:156
msgid "Asturian"
msgstr "asturyjski"
+#: ../pidgin/gtkdialogs.c:157
msgid "Belarusian Latin"
msgstr "białoruski (alfabet łaciński)"
+#: ../pidgin/gtkdialogs.c:158 ../pidgin/gtkdialogs.c:267
msgid "Bulgarian"
msgstr "bułgarski"
+#: ../pidgin/gtkdialogs.c:160 ../pidgin/gtkdialogs.c:268
msgid "Bengali"
msgstr "bengalski"
+#: ../pidgin/gtkdialogs.c:163
msgid "Bengali-India"
msgstr "bengalski (Indie)"
+#: ../pidgin/gtkdialogs.c:164
msgid "Bosnian"
msgstr "bośniacki"
+#: ../pidgin/gtkdialogs.c:165 ../pidgin/gtkdialogs.c:270
msgid "Catalan"
msgstr "kataloński"
+#: ../pidgin/gtkdialogs.c:166
msgid "Valencian-Catalan"
msgstr "walencki - kataloński"
+#: ../pidgin/gtkdialogs.c:168 ../pidgin/gtkdialogs.c:272
msgid "Czech"
msgstr "czeski"
+#: ../pidgin/gtkdialogs.c:169 ../pidgin/gtkdialogs.c:274
msgid "Danish"
msgstr "duński"
+#: ../pidgin/gtkdialogs.c:170 ../pidgin/gtkdialogs.c:276
msgid "German"
msgstr "niemiecki"
+#: ../pidgin/gtkdialogs.c:171
msgid "Dzongkha"
msgstr "dzongka"
+#: ../pidgin/gtkdialogs.c:174
msgid "Greek"
msgstr "grecki"
+#: ../pidgin/gtkdialogs.c:176 ../pidgin/gtkdialogs.c:279
msgid "Australian English"
msgstr "australijski angielski"
+#: ../pidgin/gtkdialogs.c:177 ../pidgin/gtkdialogs.c:280
msgid "British English"
msgstr "brytyjski angielski"
+#: ../pidgin/gtkdialogs.c:178
msgid "Canadian English"
msgstr "kanadyjski angielski"
+#: ../pidgin/gtkdialogs.c:179
msgid "Esperanto"
msgstr "esperanto"
+#: ../pidgin/gtkdialogs.c:180 ../pidgin/gtkdialogs.c:281
msgid "Spanish"
msgstr "hiszpański"
+#: ../pidgin/gtkdialogs.c:181
msgid "Estonian"
msgstr "estoński"
+#: ../pidgin/gtkdialogs.c:182 ../pidgin/gtkdialogs.c:285
msgid "Basque"
msgstr "baskijski"
+#: ../pidgin/gtkdialogs.c:183
msgid "Persian"
msgstr "perski"
+#: ../pidgin/gtkdialogs.c:186 ../pidgin/gtkdialogs.c:287
msgid "Finnish"
msgstr "fiński"
+#: ../pidgin/gtkdialogs.c:187 ../pidgin/gtkdialogs.c:289
msgid "French"
msgstr "francuski"
+#: ../pidgin/gtkdialogs.c:188 ../pidgin/gtkdialogs.c:189
msgid "Irish"
msgstr "irlandzki"
+#: ../pidgin/gtkdialogs.c:190 ../pidgin/gtkdialogs.c:293
msgid "Galician"
msgstr "galicyjski"
+#: ../pidgin/gtkdialogs.c:192
msgid "Gujarati"
msgstr "gudżaracki"
+#: ../pidgin/gtkdialogs.c:193
msgid "Gujarati Language Team"
msgstr "Gudżaracki zespół tłumaczenia"
+#: ../pidgin/gtkdialogs.c:194 ../pidgin/gtkdialogs.c:294
msgid "Hebrew"
msgstr "hebrajski"
+#: ../pidgin/gtkdialogs.c:195 ../pidgin/gtkdialogs.c:295
msgid "Hindi"
msgstr "hindi"
+#: ../pidgin/gtkdialogs.c:197
msgid "Croatian"
msgstr "chorwacki"
+#: ../pidgin/gtkdialogs.c:198 ../pidgin/gtkdialogs.c:296
msgid "Hungarian"
msgstr "węgierski"
-msgid "Armenian"
-msgstr "armeński"
-
+#: ../pidgin/gtkdialogs.c:199
msgid "Indonesian"
msgstr "indonezyjski"
+#: ../pidgin/gtkdialogs.c:200 ../pidgin/gtkdialogs.c:298
msgid "Italian"
msgstr "włoski"
+#: ../pidgin/gtkdialogs.c:201 ../pidgin/gtkdialogs.c:299
msgid "Japanese"
msgstr "japoński"
+#: ../pidgin/gtkdialogs.c:202 ../pidgin/gtkdialogs.c:303
msgid "Georgian"
msgstr "gruziński"
+#: ../pidgin/gtkdialogs.c:202
msgid "Ubuntu Georgian Translators"
msgstr "Tłumacze Ubuntu na język gruziński"
+#: ../pidgin/gtkdialogs.c:203
+msgid "Kazakh"
+msgstr "kazachski"
+
+#: ../pidgin/gtkdialogs.c:204
msgid "Khmer"
msgstr "khmerski"
+#: ../pidgin/gtkdialogs.c:205
msgid "Kannada"
msgstr "kannada"
+#: ../pidgin/gtkdialogs.c:205
msgid "Kannada Translation team"
msgstr "Zespół tłumaczenia na język kannada"
+#: ../pidgin/gtkdialogs.c:206 ../pidgin/gtkdialogs.c:304
msgid "Korean"
msgstr "koreański"
+#: ../pidgin/gtkdialogs.c:207
msgid "Kurdish"
msgstr "kurdyjski"
-msgid "Lao"
-msgstr "laotański"
-
+#: ../pidgin/gtkdialogs.c:210 ../pidgin/gtkdialogs.c:308
msgid "Lithuanian"
msgstr "litewski"
+#: ../pidgin/gtkdialogs.c:211
+msgid "Latvian"
+msgstr "łotewski"
+
+#: ../pidgin/gtkdialogs.c:212
msgid "Maithili"
msgstr "maithili"
+#: ../pidgin/gtkdialogs.c:214
msgid "Meadow Mari"
msgstr "maryjski"
+#: ../pidgin/gtkdialogs.c:215 ../pidgin/gtkdialogs.c:311
msgid "Macedonian"
msgstr "macedoński"
+#: ../pidgin/gtkdialogs.c:218
msgid "Malayalam"
msgstr "malajalam"
+#: ../pidgin/gtkdialogs.c:219
msgid "Mongolian"
msgstr "mongolski"
+#: ../pidgin/gtkdialogs.c:220
msgid "Marathi"
msgstr "marathi"
-msgid "Malay"
-msgstr "malajski"
-
+#: ../pidgin/gtkdialogs.c:221
msgid "Burmese"
msgstr "birmański"
+#: ../pidgin/gtkdialogs.c:222 ../pidgin/gtkdialogs.c:313
msgid "Bokmål Norwegian"
msgstr "norweski bokmål"
+#: ../pidgin/gtkdialogs.c:223
msgid "Nepali"
msgstr "nepalski"
+#: ../pidgin/gtkdialogs.c:224 ../pidgin/gtkdialogs.c:316
msgid "Dutch, Flemish"
msgstr "holenderski (flamandzki)"
+#: ../pidgin/gtkdialogs.c:225
msgid "Norwegian Nynorsk"
msgstr "norweski nynorsk"
+#: ../pidgin/gtkdialogs.c:226 ../pidgin/gtkdialogs.c:317
msgid "Occitan"
msgstr "oksytański"
+#: ../pidgin/gtkdialogs.c:227
msgid "Oriya"
msgstr "orija"
+#: ../pidgin/gtkdialogs.c:228
msgid "Punjabi"
msgstr "pendżabski"
+#: ../pidgin/gtkdialogs.c:229 ../pidgin/gtkdialogs.c:318
msgid "Polish"
msgstr "polski"
+#: ../pidgin/gtkdialogs.c:230 ../pidgin/gtkdialogs.c:323
msgid "Portuguese"
msgstr "portugalski"
+#: ../pidgin/gtkdialogs.c:231 ../pidgin/gtkdialogs.c:324
+#: ../pidgin/gtkdialogs.c:325
msgid "Portuguese-Brazil"
msgstr "portugalski (Brazylia)"
+#: ../pidgin/gtkdialogs.c:232
msgid "Pashto"
msgstr "pasztuński"
+#: ../pidgin/gtkdialogs.c:233
msgid "Romanian"
msgstr "rumuński"
+#: ../pidgin/gtkdialogs.c:235 ../pidgin/gtkdialogs.c:326
msgid "Russian"
msgstr "rosyjski"
+#: ../pidgin/gtkdialogs.c:236 ../pidgin/gtkdialogs.c:329
msgid "Slovak"
msgstr "słowacki"
+#: ../pidgin/gtkdialogs.c:238 ../pidgin/gtkdialogs.c:332
msgid "Slovenian"
msgstr "słoweński"
+#: ../pidgin/gtkdialogs.c:239
msgid "Albanian"
msgstr "albański"
+#: ../pidgin/gtkdialogs.c:240 ../pidgin/gtkdialogs.c:333
msgid "Serbian"
msgstr "serbski"
+#: ../pidgin/gtkdialogs.c:241
msgid "Serbian Latin"
msgstr "serbski (alfabet łaciński)"
+#: ../pidgin/gtkdialogs.c:242
msgid "Sinhala"
msgstr "sinhala"
+#: ../pidgin/gtkdialogs.c:244 ../pidgin/gtkdialogs.c:335
msgid "Swedish"
msgstr "szwedzki"
+#: ../pidgin/gtkdialogs.c:245
msgid "Swahili"
msgstr "suahili"
+#: ../pidgin/gtkdialogs.c:246
msgid "Tamil"
msgstr "tamilski"
+#: ../pidgin/gtkdialogs.c:248 ../pidgin/gtkdialogs.c:337
msgid "Telugu"
msgstr "telugu"
+#: ../pidgin/gtkdialogs.c:249
msgid "Thai"
msgstr "tajlandzki"
+#: ../pidgin/gtkdialogs.c:250
+msgid "Tatar"
+msgstr "tatarski"
+
+#: ../pidgin/gtkdialogs.c:251
msgid "Ukranian"
msgstr "ukraiński"
+#: ../pidgin/gtkdialogs.c:252
msgid "Urdu"
msgstr "urdu"
+#: ../pidgin/gtkdialogs.c:253 ../pidgin/gtkdialogs.c:340
msgid "Vietnamese"
msgstr "wietnamski"
+#: ../pidgin/gtkdialogs.c:254 ../pidgin/gtkdialogs.c:341
msgid "Simplified Chinese"
msgstr "chiński uproszczony"
+#: ../pidgin/gtkdialogs.c:255
msgid "Hong Kong Chinese"
msgstr "chiński (Hong Kong)"
+#: ../pidgin/gtkdialogs.c:258 ../pidgin/gtkdialogs.c:344
msgid "Traditional Chinese"
msgstr "chiński tradycyjny"
+#: ../pidgin/gtkdialogs.c:265
msgid "Amharic"
msgstr "amharski"
+#: ../pidgin/gtkdialogs.c:297
+msgid "Armenian"
+msgstr "armeński"
+
+#: ../pidgin/gtkdialogs.c:307
+msgid "Lao"
+msgstr "laotański"
+
+#: ../pidgin/gtkdialogs.c:312
+msgid "Malay"
+msgstr "malajski"
+
+#: ../pidgin/gtkdialogs.c:338
msgid "Turkish"
msgstr "turecki"
+#: ../pidgin/gtkdialogs.c:340
msgid "T.M.Thanh and the Gnome-Vi Team"
msgstr "T.M. Thanh i Zespół Gnome-Vi"
+#: ../pidgin/gtkdialogs.c:525
#, c-format
msgid ""
-"%s is a messaging client based on libpurple which is capable of connecting "
-"to multiple messaging services at once. %s is written in C using GTK+. %s "
-"is released, and may be modified and redistributed, under the terms of the "
-"GPL version 2 (or later). A copy of the GPL is distributed with %s. %s is "
-"copyrighted by its contributors, a list of whom is also distributed with "
-"%s. There is no warranty for %s.<BR><BR>"
-msgstr ""
-"Program %s jest komunikatorem opartym na bibliotece libpurple, mogący łączyć "
-"się z wieloma usługami za jednym razem. Program %s został napisany w języku "
-"C używając biblioteki GTK+. Program %s został wydany oraz może być "
-"modyfikowany i rozprowadzany na warunkach licencji GPL (wersji 2 lub "
-"późniejszej). Kopia licencji GPL jest rozprowadzanym razem z programem %s. "
-"Prawa autorskie do programu %s mają jego współtwórcy, których lista jest "
-"rozprowadzana razem z programem %s. Dla programu %s nie jest dostarczana "
-"żadna gwarancja.<BR><BR>"
+"<p>%s is a messaging client based on libpurple which is capable of "
+"connecting to multiple messaging services at once. %s is written in C using"
+" GTK+. %s is released, and may be modified and redistributed, under the "
+"terms of the GPL version 2 (or later). A copy of the GPL is distributed "
+"with %s. %s is copyrighted by its contributors, a list of whom is also "
+"distributed with %s. There is no warranty for %s.</p>"
+msgstr "<p>Program %s jest komunikatorem opartym na bibliotece libpurple, mogący łączyć się z wieloma usługami za jednym razem. Program %s został napisany w języku C używając biblioteki GTK+. Program %s został wydany oraz może być modyfikowany i rozprowadzany na warunkach licencji GPL (wersji 2 lub późniejszej). Kopia licencji GPL jest rozprowadzanym razem z programem %s. Prawa autorskie do programu %s mają jego współtwórcy, których lista jest rozprowadzana razem z programem %s. Dla programu %s nie jest dostarczana żadna gwarancja.</p>"
+#: ../pidgin/gtkdialogs.c:535
#, c-format
msgid ""
-"<FONT SIZE=\"4\"><B>Helpful Resources</B></FONT><BR>\t<A HREF=\"%s"
-"\">Website</A><BR>\t<A HREF=\"%s\">Frequently Asked Questions</A><BR>\tIRC "
-"Channel: #pidgin on irc.freenode.net<BR>\tXMPP MUC: devel@conference.pidgin."
-"im<BR><BR>"
-msgstr ""
-"<FONT SIZE=\"4\"><B>Przydatne zasoby</B></FONT><BR>\t<A HREF=\"%s\">Strona "
-"WWW</A><BR>\t<A HREF=\"%s\">Najczęściej zadawane pytania</A><BR>\tKanał IRC: "
-"#pidgin w sieci irc.freenode.net<BR>\tMUC w sieci XMPP: devel@conference."
-"pidgin.im<BR><BR>"
-
-msgid ""
-"<font size=\"4\"><b>Help from other Pidgin users</b></font> is available by "
-"e-mailing <a href=\"mailto:support@pidgin.im\">support@pidgin.im</a><br/"
-">This is a <b>public</b> mailing list! (<a href=\"http://pidgin.im/pipermail/"
-"support/\">archive</a>)<br/>We can't help with third-party protocols or "
-"plugins!<br/>This list's primary language is <b>English</b>. You are "
-"welcome to post in another language, but the responses may be less helpful."
-"<br/>"
-msgstr ""
-"<font size=\"4\"><b>Pomoc od innych użytkowników programu Pidgin</b></font>: "
-"<a href=\"mailto:support@pidgin.im\">support@pidgin.im</a><br/>To jest "
-"<b>publiczna</b> lista mailingowa (<a href=\"http://pidgin.im/pipermail/"
-"support/\">archiwum</a>).<br/>Nie jest dostarczana żadna pomoc dla "
-"protokołów lub wtyczek osób trzecich.<br/>Główny język tej listy to "
-"<b>angielski</b>. Można na nią pisać w innych językach, ale odpowiedzi mogą "
-"być mniej pomocne.<br/>"
+"<h3>Helpful Resources</h3><ul><li><a href=\"%s\" "
+"title=\"%s\">Website</a></li><li><a href=\"%s\" title=\"%s\">Frequently "
+"Asked Questions</a></li><li>IRC Channel: #pidgin on "
+"irc.freenode.net</li><li>XMPP MUC: devel@conference.pidgin.im</li></ul>"
+msgstr "<h3>Przydatne zasoby</h3><ul><li><a href=\"%s\" title=\"%s\">Strona WWW</a></li><li><a href=\"%s\" title=\"%s\">Najczęściej zadawane pytania</a></li><li>Kanał IRC: #pidgin w sieci irc.freenode.net</li><li>MUC w sieci XMPP: devel@conference.pidgin.im</li></ul>"
+
+#: ../pidgin/gtkdialogs.c:554
+#, c-format
+msgid ""
+"<p><strong>Help from other Pidgin users</strong> is available by e-mailing "
+"<a href=\"mailto:%s\">%s</a>.<br/>This is a <strong>public</strong> mailing "
+"list! (<a href=\"%s\" title=\"%s\">archive</a>)<br/>We can't help with "
+"third-party protocols or plugins!<br/>This list's primary language is "
+"English. You are welcome to post in another language, but the responses may"
+" be less helpful.</p>"
+msgstr "<p><strong>Pomoc od innych użytkowników programu Pidgin:</strong> <a href=\"mailto:%s\">%s</a>.<br/>To jest <strong>publiczna</strong> lista mailingowa (<a href=\"%s\" title=\"%s\">archiwum</a>).<br/>Nie jest dostarczana żadna pomoc dla protokołów lub wtyczek osób trzecich.<br/>Główny język tej listy to angielski. Można na nią pisać w innych językach, ale odpowiedzi mogą być mniej pomocne.</p>"
+#: ../pidgin/gtkdialogs.c:566
#, c-format
msgid "About %s"
msgstr "O programie %s"
+#: ../pidgin/gtkdialogs.c:590
msgid "Build Information"
msgstr "Informacje programistyczne"
#. End of not to be translated section
+#: ../pidgin/gtkdialogs.c:749
#, c-format
msgid "%s Build Information"
msgstr "Informacje programistyczne o programie %s"
+#: ../pidgin/gtkdialogs.c:770
msgid "Current Developers"
msgstr "Obecni programiści"
+#: ../pidgin/gtkdialogs.c:776
msgid "Crazy Patch Writers"
msgstr "Twórcy łat"
+#: ../pidgin/gtkdialogs.c:782
msgid "Retired Developers"
msgstr "Poprzedni programiści"
+#: ../pidgin/gtkdialogs.c:788
msgid "Retired Crazy Patch Writers"
msgstr "Poprzedni twórcy łat"
+#: ../pidgin/gtkdialogs.c:792
#, c-format
msgid "%s Developer Information"
msgstr "Informacje o programistach programu %s"
+#: ../pidgin/gtkdialogs.c:813
msgid "Current Translators"
msgstr "Obecni tłumacze"
+#: ../pidgin/gtkdialogs.c:819
msgid "Past Translators"
msgstr "Poprzedni tłumacze"
+#: ../pidgin/gtkdialogs.c:823
#, c-format
msgid "%s Translator Information"
msgstr "Informacje o tłumaczach programu %s"
+#: ../pidgin/gtkdialogs.c:834
#, c-format
msgid "%s Plugin Information"
msgstr "Informacje o wtyczce %s"
+#: ../pidgin/gtkdialogs.c:842
msgid "Plugin Information"
msgstr "Informacje o wtyczce"
+#: ../pidgin/gtkdialogs.c:928 ../pidgin/gtkdialogs.c:1068
+#: ../pidgin/gtkdialogs.c:1148
msgid "_Name"
msgstr "_Nazwa"
+#: ../pidgin/gtkdialogs.c:934 ../pidgin/gtkdialogs.c:1073
+#: ../pidgin/gtkdialogs.c:1153
msgid "_Account"
msgstr "_Konto"
+#: ../pidgin/gtkdialogs.c:1081
msgid "Get User Info"
msgstr "Pobranie informacji o użytkowniku"
+#: ../pidgin/gtkdialogs.c:1083
msgid ""
"Please enter the username or alias of the person whose info you would like "
"to view."
-msgstr ""
-"Proszę podać nazwę użytkownika lub alias osoby, której informacje mają "
-"zostać wyświetlone."
+msgstr "Proszę podać nazwę użytkownika lub alias osoby, której informacje mają zostać wyświetlone."
+#: ../pidgin/gtkdialogs.c:1172
msgid "View User Log"
-msgstr "Wyświetlenie dziennika rozmów użytkownika"
-
-msgid "Alias Contact"
-msgstr "Alias kontaktu"
-
-msgid "Enter an alias for this contact."
-msgstr "Proszę podać alias dla tego kontaktu."
+msgstr "Wyświetlenie archiwum rozmów użytkownika"
+#: ../pidgin/gtkdialogs.c:1196
#, c-format
msgid "Enter an alias for %s."
msgstr "Proszę podać nazwę dla użytkownika %s."
+#: ../pidgin/gtkdialogs.c:1198
msgid "Alias Buddy"
msgstr "Alias znajomego"
+#: ../pidgin/gtkdialogs.c:1223
msgid "Alias Chat"
msgstr "Alias konferencji"
+#: ../pidgin/gtkdialogs.c:1224
msgid "Enter an alias for this chat."
msgstr "Proszę podać alias dla tej konferencji."
+#: ../pidgin/gtkdialogs.c:1266
#, c-format
msgid ""
"You are about to remove the contact containing %s and %d other buddy from "
@@ -12155,578 +15456,196 @@ msgid ""
msgid_plural ""
"You are about to remove the contact containing %s and %d other buddies from "
"your buddy list. Do you want to continue?"
-msgstr[0] ""
-"Za chwilę zostanie usunięty kontakt zawierający %s i %d inny znajomy z "
-"listy. Kontynuować?"
-msgstr[1] ""
-"Za chwilę zostanie usunięty kontakt zawierający %s i %d innych znajomych z "
-"listy. Kontynuować?"
-msgstr[2] ""
-"Za chwilę zostanie usunięty kontakt zawierający %s i %d innych znajomych z "
-"listy. Kontynuować?"
+msgstr[0] "Za chwilę zostanie usunięty kontakt zawierający %s i %d inny znajomy z listy. Kontynuować?"
+msgstr[1] "Za chwilę zostanie usunięty kontakt zawierający %s i %d innych znajomych z listy. Kontynuować?"
+msgstr[2] "Za chwilę zostanie usunięty kontakt zawierający %s i %d innych znajomych z listy. Kontynuować?"
+#: ../pidgin/gtkdialogs.c:1274
msgid "Remove Contact"
msgstr "Usunięcie kontaktu"
+#: ../pidgin/gtkdialogs.c:1277
msgid "_Remove Contact"
msgstr "_Usuń kontakt"
+#: ../pidgin/gtkdialogs.c:1308
#, c-format
msgid ""
"You are about to merge the group called %s into the group called %s. Do you "
"want to continue?"
-msgstr ""
-"Za chwilę zostaną połączone grupy o nazwie %s z grupą o nazwie%s. "
-"Kontynuować?"
+msgstr "Za chwilę zostaną połączone grupy o nazwie %s z grupą o nazwie%s. Kontynuować?"
+#: ../pidgin/gtkdialogs.c:1315
msgid "Merge Groups"
msgstr "Połączenie grup"
+#: ../pidgin/gtkdialogs.c:1318
msgid "_Merge Groups"
msgstr "_Połącz grupy"
+#: ../pidgin/gtkdialogs.c:1368
#, c-format
msgid ""
"You are about to remove the group %s and all its members from your buddy "
"list. Do you want to continue?"
-msgstr ""
-"Za chwilę zostanie usunięta grupa %s i wszyscy jej członkowie z listy "
-"znajomych. Kontynuować?"
+msgstr "Za chwilę zostanie usunięta grupa %s i wszyscy jej członkowie z listy znajomych. Kontynuować?"
+#: ../pidgin/gtkdialogs.c:1371
msgid "Remove Group"
msgstr "Usunięcie grupy"
+#: ../pidgin/gtkdialogs.c:1374
msgid "_Remove Group"
msgstr "_Usuń grupę"
+#: ../pidgin/gtkdialogs.c:1407
#, c-format
msgid ""
"You are about to remove %s from your buddy list. Do you want to continue?"
msgstr "Za chwilę zostanie usunięty znajomy %s z listy. Kontynuować?"
+#: ../pidgin/gtkdialogs.c:1410
msgid "Remove Buddy"
msgstr "Usunięcie znajomego"
+#: ../pidgin/gtkdialogs.c:1413
msgid "_Remove Buddy"
msgstr "_Usuń znajomego"
+#: ../pidgin/gtkdialogs.c:1434
#, c-format
msgid ""
"You are about to remove the chat %s from your buddy list. Do you want to "
"continue?"
-msgstr ""
-"Za chwilę zostanie usunięta konferencja %s z listy znajomych. Kontynuować?"
+msgstr "Za chwilę zostanie usunięta konferencja %s z listy znajomych. Kontynuować?"
+#: ../pidgin/gtkdialogs.c:1437
msgid "Remove Chat"
msgstr "Usunięcie konferencji"
+#: ../pidgin/gtkdialogs.c:1439
msgid "_Remove Chat"
msgstr "Usuń konfe_rencję"
+#: ../pidgin/gtkdocklet.c:194
msgid "Right-click for more unread messages...\n"
-msgstr ""
-"Naciśnięcie prawym przyciskiem myszy wyświetli więcej nieprzeczytanych "
-"wiadomości...\n"
+msgstr "Naciśnięcie prawym przyciskiem myszy wyświetli więcej nieprzeczytanych wiadomości...\n"
+#: ../pidgin/gtkdocklet.c:570
msgid "_Change Status"
msgstr "_Zmień stan"
+#: ../pidgin/gtkdocklet.c:717
msgid "Show Buddy _List"
msgstr "Wyświetlanie _listy znajomych"
+#: ../pidgin/gtkdocklet.c:722
msgid "_Unread Messages"
msgstr "_Nieprzeczytane wiadomości"
+#: ../pidgin/gtkdocklet.c:743
msgid "New _Message..."
msgstr "Nowa wiado_mość..."
-msgid "_Accounts"
-msgstr "Kont_a"
-
-msgid "Plu_gins"
-msgstr "_Wtyczki"
-
-msgid "Pr_eferences"
-msgstr "_Preferencje"
-
-msgid "Mute _Sounds"
-msgstr "Wyci_szenie dźwięków"
-
+#: ../pidgin/gtkdocklet.c:771
msgid "_Blink on New Message"
msgstr "Migotanie p_rzy nowej wiadomości"
-msgid "_Quit"
-msgstr "Za_kończ"
-
-msgid "Not started"
-msgstr "Nie rozpoczęto"
-
-msgid "<b>Receiving As:</b>"
-msgstr "<b>Odbieranie jako:</b>"
-
-msgid "<b>Receiving From:</b>"
-msgstr "<b>Odbieranie od:</b>"
-
-msgid "<b>Sending To:</b>"
-msgstr "<b>Wysyłanie do:</b>"
-
-msgid "<b>Sending As:</b>"
-msgstr "<b>Wysyłanie jako:</b>"
-
-msgid "There is no application configured to open this type of file."
-msgstr "Żaden program nie jest skonfigurowana do otwierania tego typu plików."
-
-msgid "An error occurred while opening the file."
-msgstr "Wystąpił błąd podczas otwierania pliku."
-
-#, c-format
-msgid "Error launching %s: %s"
-msgstr "Błąd podczas uruchamiania %s: %s"
-
-#, c-format
-msgid "Error running %s"
-msgstr "Błąd podczas wykonywania %s"
-
-#, c-format
-msgid "Process returned error code %d"
-msgstr "Proces zwrócił kod błędu %d"
-
-msgid "Filename:"
-msgstr "Nazwa pliku:"
-
-msgid "Local File:"
-msgstr "Lokalny plik:"
-
-msgid "Speed:"
-msgstr "Prędkość:"
-
-msgid "Time Elapsed:"
-msgstr "Upłynęło czasu:"
-
-msgid "Time Remaining:"
-msgstr "Pozostało czasu:"
-
-msgid "Close this window when all transfers _finish"
-msgstr "Zamknięcie tego okna po u_kończeniu wszystkich przesyłań"
-
-msgid "C_lear finished transfers"
-msgstr "_Czyszczenie ukończonych przesyłań"
-
-#. "Download Details" arrow
-msgid "File transfer _details"
-msgstr "_Szczegóły przesyłania plików"
-
-msgid "Paste as Plain _Text"
-msgstr "Wklej jako zwykły _tekst"
-
-msgid "_Reset formatting"
-msgstr "P_rzywróć formatowanie"
-
-msgid "Disable _smileys in selected text"
-msgstr "Wyłącz _emotikony w zaznaczonym tekście"
-
-msgid "Hyperlink color"
-msgstr "Kolor odnośnika"
-
-msgid "Color to draw hyperlinks."
-msgstr "Kolor wyświetlania odnośników."
-
-msgid "Hyperlink visited color"
-msgstr "Kolor odwiedzonych odnośników"
-
-msgid "Color to draw hyperlink after it has been visited (or activated)."
-msgstr "Kolor wyświetlania odnośników po ich odwiedzeniu (lub aktywowaniu)."
-
-msgid "Hyperlink prelight color"
-msgstr "Kolor podświetlonego odnośnika"
-
-msgid "Color to draw hyperlinks when mouse is over them."
-msgstr "Kolor wyświetlania odnośników pod kursorem myszy."
-
-msgid "Sent Message Name Color"
-msgstr "Kolor nazwy wysyłanej wiadomości"
-
-msgid "Color to draw the name of a message you sent."
-msgstr "Kolor wyświetlania nazwy wysłanej wiadomości."
-
-msgid "Received Message Name Color"
-msgstr "Kolor nazwy odebranej wiadomości"
-
-msgid "Color to draw the name of a message you received."
-msgstr "Kolor wyświetlania nazwy odebranej wiadomości."
-
-msgid "\"Attention\" Name Color"
-msgstr "Kolor nazwy \"uwagi\""
-
-msgid "Color to draw the name of a message you received containing your name."
-msgstr ""
-"Kolor wyświetlania nazwy odebranej wiadomości zawierającej nazwę użytkownika."
-
-msgid "Action Message Name Color"
-msgstr "Kolor nazwy wiadomości czynności"
-
-msgid "Color to draw the name of an action message."
-msgstr "Kolor wyświetlania nazwy wiadomości czynności."
-
-msgid "Action Message Name Color for Whispered Message"
-msgstr "Kolor nazwy wiadomości czynności dla wyszeptanej wiadomości"
-
-msgid "Color to draw the name of a whispered action message."
-msgstr ""
-"Kolor wyświetlania nazwy wiadomości czynności dla wyszeptanej wiadomości."
-
-msgid "Whisper Message Name Color"
-msgstr "Kolor nazwy wyszeptanej wiadomości"
-
-msgid "Color to draw the name of a whispered message."
-msgstr "Kolor wyświetlania nazwy wyszeptanej wiadomości."
-
-msgid "Typing notification color"
-msgstr "Kolor powiadomienia o pisaniu"
-
-msgid "The color to use for the typing notification"
-msgstr "Kolor używany do powiadomień o pisaniu"
-
-msgid "Typing notification font"
-msgstr "Czcionka powiadomienia o pisaniu"
-
-msgid "The font to use for the typing notification"
-msgstr "Czcionka używana do powiadomień o pisaniu"
-
-msgid "Enable typing notification"
-msgstr "Włączenie powiadomień o pisaniu"
-
-msgid ""
-"<span size='larger' weight='bold'>Unrecognized file type</span>\n"
-"\n"
-"Defaulting to PNG."
-msgstr ""
-"<span size='larger' weight='bold'>Nierozpoznany typ pliku</span>\n"
-"\n"
-"Domyślnie przyjmowany jest format PNG."
-
-#, c-format
-msgid ""
-"<span size='larger' weight='bold'>Error saving image</span>\n"
-"\n"
-"%s"
-msgstr ""
-"<span size='larger' weight='bold'>Błąd podczas zapisywania obrazu</span>\n"
-"\n"
-"%s"
-
-msgid "Save Image"
-msgstr "Zapis obrazu"
-
-msgid "_Save Image..."
-msgstr "Zapi_sz obraz..."
-
-msgid "_Add Custom Smiley..."
-msgstr "Dod_aj własną emotikonę..."
-
-msgid "Select Font"
-msgstr "Wybór czcionki"
-
-msgid "Select Text Color"
-msgstr "Wybór koloru tekstu"
-
-msgid "Select Background Color"
-msgstr "Wybór koloru tła"
-
-msgid "_URL"
-msgstr "Adres _URL"
-
-msgid "_Description"
-msgstr "_Opis"
-
-msgid ""
-"Please enter the URL and description of the link that you want to insert. "
-"The description is optional."
-msgstr ""
-"Proszę podać adres URL i opis wstawianego odnośnika. Opis jest opcjonalny."
-
-msgid "Please enter the URL of the link that you want to insert."
-msgstr "Proszę podać adres URL wstawianego odnośnika."
-
-msgid "Insert Link"
-msgstr "Wstawienie odnośnika"
-
-msgid "_Insert"
-msgstr "_Wstaw"
-
-#, c-format
-msgid "Failed to store image: %s\n"
-msgstr "Przechowanie obrazu się nie powiodło: %s\n"
-
-msgid "Insert Image"
-msgstr "Wstawienie obrazu"
-
-#, c-format
-msgid ""
-"This smiley is disabled because a custom smiley exists for this shortcut:\n"
-" %s"
-msgstr ""
-"Ta emotikona jest wyłączona, ponieważ istnieje własna emotikona dla tego "
-"skrótu:\n"
-" %s"
-
-msgid "Smile!"
-msgstr "Uśmiech!"
-
-msgid "_Manage custom smileys"
-msgstr "_Zarządzaj własnymi emotikonami"
-
-msgid "This theme has no available smileys."
-msgstr "Ten motyw nie posiada emotikon."
-
-msgid "_Font"
-msgstr "_Czcionka"
-
-msgid "Group Items"
-msgstr "Grupowanie elementów"
-
-msgid "Ungroup Items"
-msgstr "Bez grupowania elementów"
-
-msgid "Bold"
-msgstr "Pogrubienie"
-
-msgid "Italic"
-msgstr "Pochylenie"
-
-msgid "Underline"
-msgstr "Podkreślenie"
-
-msgid "Strikethrough"
-msgstr "Przekreślenie"
-
-msgid "Increase Font Size"
-msgstr "Zwiększ rozmiar czcionki"
-
-msgid "Decrease Font Size"
-msgstr "Zmniejsz rozmiar czcionki"
-
-msgid "Font Face"
-msgstr "Krój czcionki"
-
-msgid "Foreground Color"
-msgstr "Kolor czcionki"
-
-msgid "Reset Formatting"
-msgstr "Przywróć formatowanie"
-
-msgid "Insert IM Image"
-msgstr "Wstaw obraz"
-
-msgid "Insert Smiley"
-msgstr "Wstaw emotikonę"
-
-msgid "Send Attention"
-msgstr "Wyślij uwagę"
-
-msgid "<b>_Bold</b>"
-msgstr "<b>Pogru_bienie</b>"
-
-msgid "<i>_Italic</i>"
-msgstr "<i>P_ochylenie</i>"
-
-msgid "<u>_Underline</u>"
-msgstr "<u>Po_dkreślenie</u>"
-
-msgid "<span strikethrough='true'>Strikethrough</span>"
-msgstr "<span strikethrough='true'>Przekreślenie</span>"
-
-msgid "<span size='larger'>_Larger</span>"
-msgstr "<span size='larger'>Powię_kszenie</span>"
-
-msgid "_Normal"
-msgstr "_Normalna"
-
-msgid "<span size='smaller'>_Smaller</span>"
-msgstr "<span size='smaller'>Pomniej_szenie</span>"
-
-#. If we want to show the formatting for the following items, we would
-#. * need to update them when formatting changes. The above items don't need
-#. * no updating nor nothin'
-msgid "_Font face"
-msgstr "_Krój czcionki"
-
-msgid "Foreground _color"
-msgstr "K_olor czcionki"
-
-msgid "Bac_kground color"
-msgstr "_Kolor tła"
-
-msgid "_Image"
-msgstr "_Obraz"
-
-msgid "_Link"
-msgstr "O_dnośnik"
-
-msgid "_Horizontal rule"
-msgstr "_Linia pozioma"
-
-msgid "_Smile!"
-msgstr "_Uśmiech!"
-
-msgid "_Attention!"
-msgstr "_Uwaga!"
-
+#: ../pidgin/gtklog.c:242
msgid "Log Deletion Failed"
-msgstr "Usunięcie dziennika rozmów się nie powiodło"
+msgstr "Usunięcie archiwum rozmów się nie powiodło"
+#: ../pidgin/gtklog.c:243
msgid "Check permissions and try again."
msgstr "Proszę sprawdzić uprawnienia i spróbować ponownie."
+#: ../pidgin/gtklog.c:286
#, c-format
msgid ""
-"Are you sure you want to permanently delete the log of the conversation with "
-"%s which started at %s?"
-msgstr ""
-"Na pewno trwale usunąć dziennik rozmowy z użytkownikiem %s, rozpoczętej dnia "
-"%s?"
+"Are you sure you want to permanently delete the log of the conversation with"
+" %s which started at %s?"
+msgstr "Na pewno trwale usunąć rozmowę z użytkownikiem %s, rozpoczętej dnia %s?"
+#: ../pidgin/gtklog.c:297
#, c-format
msgid ""
"Are you sure you want to permanently delete the log of the conversation in "
"%s which started at %s?"
-msgstr "Na pewno trwale usunąć dziennik rozmowy w %s, rozpoczętej dnia %s?"
+msgstr "Na pewno trwale usunąć treść konferencji w %s, rozpoczętej dnia %s?"
+#: ../pidgin/gtklog.c:302
#, c-format
msgid ""
"Are you sure you want to permanently delete the system log which started at "
"%s?"
msgstr "Na pewno trwale usunąć dziennik systemowy rozpoczęty dnia %s?"
+#: ../pidgin/gtklog.c:317
msgid "Delete Log?"
-msgstr "Usunąć dziennik?"
+msgstr "Usunąć wpis z archiwum?"
+#: ../pidgin/gtklog.c:328
msgid "Delete Log..."
-msgstr "Usuń dziennik..."
+msgstr "Usuń z archiwum..."
+#: ../pidgin/gtklog.c:449
#, c-format
msgid "<span size='larger' weight='bold'>Conversation in %s on %s</span>"
msgstr "<span size='larger' weight='bold'>Rozmowa w %s dnia %s</span>"
+#: ../pidgin/gtklog.c:452
#, c-format
msgid "<span size='larger' weight='bold'>Conversation with %s on %s</span>"
-msgstr ""
-"<span size='larger' weight='bold'>Rozmowa z użytkownikiem %s dnia %s</span>"
+msgstr "<span size='larger' weight='bold'>Rozmowa z użytkownikiem %s dnia %s</span>"
#. Steal the "HELP" response and use it to trigger browsing to the logs folder
+#: ../pidgin/gtklog.c:580
msgid "_Browse logs folder"
-msgstr "_Przeglądaj katalog dzienników rozmów"
-
-#, c-format
-msgid "%s %s. Try `%s -h' for more information.\n"
-msgstr "%s %s. Należy podać \"%s -h\", aby uzyskać więcej informacji.\n"
-
-#, c-format
-msgid ""
-"Usage: %s [OPTION]...\n"
-"\n"
-msgstr ""
-"Użycie: %s [OPCJA]...\n"
-"\n"
-
-msgid "DIR"
-msgstr "KATALOG"
-
-msgid "use DIR for config files"
-msgstr "używa KATALOGU dla plików konfiguracji"
-
-msgid "print debugging messages to stdout"
-msgstr "wyświetla komunikaty debugowania na standardowym wyjściu"
-
-msgid "force online, regardless of network status"
-msgstr "wymusza tryb online, niezależnie od stanu sieci"
-
-msgid "display this help and exit"
-msgstr "wyświetla tę pomoc i kończy pracę"
-
-msgid "allow multiple instances"
-msgstr "pozwala na wiele instancji"
-
-msgid "don't automatically login"
-msgstr "bez automatycznego logowania"
-
-msgid "NAME"
-msgstr "NAZWA"
-
-msgid ""
-"enable specified account(s) (optional argument NAME\n"
-" specifies account(s) to use, separated by commas.\n"
-" Without this only the first account will be enabled)."
-msgstr ""
-"włącza podane konta (opcjonalny parametr NAZWA\n"
-" podaje używane konta, oddzielone przecinkami.\n"
-" Bez tego tylko pierwsze konto zostanie włączone)."
-
-msgid "X display to use"
-msgstr "używany ekran X"
-
-msgid "display the current version and exit"
-msgstr "wyświetla bieżącą wersję i kończy pracę"
-
-#, c-format
-msgid ""
-"%s %s has segfaulted and attempted to dump a core file.\n"
-"This is a bug in the software and has happened through\n"
-"no fault of your own.\n"
-"\n"
-"If you can reproduce the crash, please notify the developers\n"
-"by reporting a bug at:\n"
-"%ssimpleticket/\n"
-"\n"
-"Please make sure to specify what you were doing at the time\n"
-"and post the backtrace from the core file. If you do not know\n"
-"how to get the backtrace, please read the instructions at\n"
-"%swiki/GetABacktrace\n"
-msgstr ""
-"Program %s %s miał awarię i próbował zrzucić plik core.\n"
-"To jest błąd w oprogramowaniu nie powstały z winy użytkownika.\n"
-"\n"
-"Jeśli można powtórzyć awarię, proszę powiadomić programistów\n"
-"zgłaszając błąd na stronie:\n"
-"%ssimpleticket/\n"
-"\n"
-"Proszę upewnić się, że podano czynność wykonywaną w trakcie\n"
-"awarii oraz wysłano wyjątek z pliku core. Jeśli nie wiadomo,\n"
-"jak uzyskać wyjątek, proszę przeczytać instrukcje na stronie\n"
-"%swiki/GetABacktrace\n"
-
-#, c-format
-msgid "Exiting because another libpurple client is already running.\n"
-msgstr ""
-"Kończenie pracy, ponieważ inny klient biblioteki libpurple jest już "
-"uruchomiony.\n"
+msgstr "_Przeglądaj katalog archiwum rozmów"
+#: ../pidgin/gtkmedia.c:273
msgid "_Media"
msgstr "_Multimedia"
+#: ../pidgin/gtkmedia.c:274
msgid "_Hangup"
msgstr "_Rozłącz się"
+#: ../pidgin/gtkmedia.c:600
+msgid "Media error"
+msgstr "Błąd multimediów"
+
+#: ../pidgin/gtkmedia.c:644
#, c-format
msgid "%s wishes to start an audio/video session with you."
msgstr "Użytkownik %s chce rozpocząć sesję głosową/wideo."
+#: ../pidgin/gtkmedia.c:650
#, c-format
msgid "%s wishes to start a video session with you."
msgstr "Użytkownik %s chce rozpocząć sesję wideo."
+#: ../pidgin/gtkmedia.c:656
msgid "Incoming Call"
msgstr "Rozmowa przychodząca"
+#: ../pidgin/gtkmedia.c:777
msgid "_Hold"
msgstr "_Wstrzymaj"
+#: ../pidgin/gtkmedia.c:864 ../pidgin/pidginstock.c:88
msgid "_Pause"
msgstr "_Wstrzymaj"
+#: ../pidgin/gtkmedia.c:882
msgid "_Mute"
msgstr "Wy_cisz"
+#: ../pidgin/gtkmedia.c:973
+msgid "Call in progress"
+msgstr "Trwa rozmowa"
+
+#: ../pidgin/gtknotify.c:808
#, c-format
msgid "%s has %d new message."
msgid_plural "%s has %d new messages."
@@ -12734,6 +15653,7 @@ msgstr[0] "Użytkownik %s ma %d nową wiadomość."
msgstr[1] "Użytkownik %s ma %d nowe wiadomości."
msgstr[2] "Użytkownik %s ma %d nowych wiadomości."
+#: ../pidgin/gtknotify.c:844
#, c-format
msgid "<b>%d new email.</b>"
msgid_plural "<b>%d new emails.</b>"
@@ -12741,36 +15661,43 @@ msgstr[0] "<b>%d nowa wiadomość e-mail.</b>"
msgstr[1] "<b>%d nowe wiadomości e-mail.</b>"
msgstr[2] "<b>%d nowych wiadomości e-mail.</b>"
+#: ../pidgin/gtknotify.c:1266
#, c-format
msgid "The browser command \"%s\" is invalid."
msgstr "Polecenie przeglądarki \"%s\" jest nieprawidłowe."
+#: ../pidgin/gtknotify.c:1268 ../pidgin/gtknotify.c:1304
+#: ../pidgin/gtknotify.c:1468
msgid "Unable to open URL"
msgstr "Nie można otworzyć adresu URL"
+#: ../pidgin/gtknotify.c:1301
#, c-format
msgid "Error launching \"%s\": %s"
msgstr "Błąd podczas uruchamiania \"%s\": %s"
+#: ../pidgin/gtknotify.c:1469
msgid ""
"The 'Manual' browser command has been chosen, but no command has been set."
-msgstr ""
-"Wybrano \"ręczne\" polecenie przeglądarki, ale nie ustawiono polecenia."
+msgstr "Wybrano \"ręczne\" polecenie przeglądarki, ale nie ustawiono polecenia."
+#: ../pidgin/gtknotify.c:1478
msgid "Unable to open URL: the 'Manual' browser command seems invalid."
-msgstr ""
-"Nie można otworzyć adresu URL: \"ręczne\" polecenie przeglądarki jest "
-"nieprawidłowe."
+msgstr "Nie można otworzyć adresu URL: \"ręczne\" polecenie przeglądarki jest nieprawidłowe."
+#: ../pidgin/gtknotify.c:1553
msgid "No message"
msgstr "Brak wiadomości"
+#: ../pidgin/gtknotify.c:1630
msgid "Open All Messages"
msgstr "Otwórz wszystkie wiadomości"
+#: ../pidgin/gtknotify.c:1662
msgid "<span weight=\"bold\" size=\"larger\">You have mail!</span>"
msgstr "<span weight=\"bold\" size=\"larger\">Nowa wiadomość e-mail.</span>"
+#: ../pidgin/gtknotify.c:1665
msgid "New Pounces"
msgstr "Nowe przechwytywanie zdarzeń"
@@ -12781,856 +15708,1214 @@ msgstr "Nowe przechwytywanie zdarzeń"
#. this pounce was triggered--remove it from this list." Translating
#. it as "Remove" is acceptable if you can't think of a more precise
#. word.
+#: ../pidgin/gtknotify.c:1684
msgid "Dismiss"
msgstr "Odrzuć"
+#: ../pidgin/gtknotify.c:1729
msgid "<span weight=\"bold\" size=\"larger\">You have pounced!</span>"
-msgstr ""
-"<span weight=\"bold\" size=\"larger\">Nowe przechwytywanie zdarzenia.</span>"
+msgstr "<span weight=\"bold\" size=\"larger\">Nowe przechwytywanie zdarzenia.</span>"
+#: ../pidgin/gtkplugin.c:418
msgid "The following plugins will be unloaded."
msgstr "Następujące wtyczki zostaną wyłączone."
+#: ../pidgin/gtkplugin.c:437
msgid "Multiple plugins will be unloaded."
msgstr "Kilka wtyczek zostanie wyłączonych."
+#: ../pidgin/gtkplugin.c:439
msgid "Unload Plugins"
msgstr "Wyłącz wtyczki"
+#: ../pidgin/gtkplugin.c:457
msgid "Could not unload plugin"
msgstr "Nie można wyłączyć wtyczki"
+#: ../pidgin/gtkplugin.c:458
msgid ""
"The plugin could not be unloaded now, but will be disabled at the next "
"startup."
-msgstr ""
-"Nie można teraz wyłączyć wtyczki, ale zostanie ona wyłączona podczas "
-"następnego uruchomienia."
+msgstr "Nie można teraz wyłączyć wtyczki, ale zostanie ona wyłączona podczas następnego uruchomienia."
+#: ../pidgin/gtkplugin.c:590
#, c-format
msgid ""
"<span foreground=\"red\" weight=\"bold\">Error: %s\n"
"Check the plugin website for an update.</span>"
-msgstr ""
-"<span foreground=\"red\" weight=\"bold\">Błąd: %s\n"
-"Proszę zobaczyć stronę WWW wtyczki, aby uzyskać aktualizację.</span>"
+msgstr "<span foreground=\"red\" weight=\"bold\">Błąd: %s\nProszę zobaczyć stronę WWW wtyczki, aby uzyskać aktualizację.</span>"
+#: ../pidgin/gtkplugin.c:700
msgid "Author"
msgstr "Autor"
+#: ../pidgin/gtkplugin.c:778
msgid "<b>Written by:</b>"
msgstr "<b>Napisana przez:</b>"
+#: ../pidgin/gtkplugin.c:798
msgid "<b>Web site:</b>"
msgstr "<b>Strona WWW:</b>"
+#: ../pidgin/gtkplugin.c:807
msgid "<b>Filename:</b>"
msgstr "<b>Nazwa pliku:</b>"
+#: ../pidgin/gtkplugin.c:832
msgid "Configure Pl_ugin"
msgstr "Skonfig_uruj wtyczkę"
+#: ../pidgin/gtkplugin.c:894
msgid "<b>Plugin Details</b>"
msgstr "<b>Szczegółowe informacje o wtyczce</b>"
+#: ../pidgin/gtkpounce.c:156
msgid "Select a file"
msgstr "Wybór pliku"
+#: ../pidgin/gtkpounce.c:546
msgid "Modify Buddy Pounce"
msgstr "Zmodyfikuj przechwytywanie zdarzenie"
#. Create the "Pounce on Whom" frame.
+#: ../pidgin/gtkpounce.c:563
msgid "Pounce on Whom"
msgstr "Źródło zdarzeń"
+#: ../pidgin/gtkpounce.c:570 ../pidgin/gtkroomlist.c:591
+#: ../pidgin/plugins/disco/gtkdisco.c:660
msgid "_Account:"
msgstr "_Konto:"
+#: ../pidgin/gtkpounce.c:590
msgid "_Buddy name:"
msgstr "Nazwa _znajomego:"
+#: ../pidgin/gtkpounce.c:624
msgid "Si_gns on"
msgstr "Z_alogowanie"
+#: ../pidgin/gtkpounce.c:626
msgid "Signs o_ff"
msgstr "_Wylogowanie"
+#: ../pidgin/gtkpounce.c:628
msgid "Goes a_way"
msgstr "Zmiana stanu na _zajęty"
+#: ../pidgin/gtkpounce.c:630
msgid "Ret_urns from away"
msgstr "Powrót ze stan_u nieobecności"
+#: ../pidgin/gtkpounce.c:632
msgid "Becomes _idle"
msgstr "Przejście w stan bezczynnośc_i"
+#: ../pidgin/gtkpounce.c:634
msgid "Is no longer i_dle"
msgstr "Powrót ze stanu bezczynnoś_ci"
+#: ../pidgin/gtkpounce.c:636
msgid "Starts _typing"
msgstr "Rozpoczęcie pi_sania"
+#: ../pidgin/gtkpounce.c:638
msgid "P_auses while typing"
msgstr "Wstrzym_anie pisania"
+#: ../pidgin/gtkpounce.c:640
msgid "Stops t_yping"
msgstr "Zatrzymanie pisa_nia"
+#: ../pidgin/gtkpounce.c:642
msgid "Sends a _message"
msgstr "Wysłanie wiado_mości"
+#: ../pidgin/gtkpounce.c:685
msgid "Ope_n an IM window"
msgstr "Otwarcie ok_na rozmowy"
+#: ../pidgin/gtkpounce.c:687
msgid "_Pop up a notification"
msgstr "_Wyświetlenie powiadomienia"
+#: ../pidgin/gtkpounce.c:689
msgid "Send a _message"
msgstr "Wysłanie wiado_mości"
+#: ../pidgin/gtkpounce.c:691
msgid "E_xecute a command"
msgstr "Wyko_nanie polecenia"
+#: ../pidgin/gtkpounce.c:693
msgid "P_lay a sound"
msgstr "O_dtworzenie dźwięku"
+#: ../pidgin/gtkpounce.c:699
msgid "Brows_e..."
msgstr "Prz_eglądaj..."
+#: ../pidgin/gtkpounce.c:703
msgid "Br_owse..."
msgstr "Przegląd_aj..."
+#: ../pidgin/gtkpounce.c:704 ../pidgin/gtkprefs.c:3349
msgid "Pre_view"
msgstr "Podg_ląd"
+#: ../pidgin/gtkpounce.c:844
msgid "P_ounce only when my status is not Available"
msgstr "Przechwytywanie tylko, kiedy stan nie jest d_ostępny"
+#: ../pidgin/gtkpounce.c:849
msgid "_Recurring"
msgstr "Powta_rzanie"
+#: ../pidgin/gtkpounce.c:1268
msgid "Pounce Target"
msgstr "Cel zdarzeń"
+#: ../pidgin/gtkpounce.c:1446
msgid "Started typing"
msgstr "Rozpoczęcie pisania"
+#: ../pidgin/gtkpounce.c:1448
msgid "Paused while typing"
msgstr "Wstrzymanie pisania"
+#: ../pidgin/gtkpounce.c:1450
msgid "Signed on"
msgstr "Zalogowanie"
+#: ../pidgin/gtkpounce.c:1452
msgid "Returned from being idle"
msgstr "Powrót ze stanu bezczynności"
+#: ../pidgin/gtkpounce.c:1454
msgid "Returned from being away"
msgstr "Powrót ze stanu nieobecności"
+#: ../pidgin/gtkpounce.c:1456
msgid "Stopped typing"
msgstr "Zatrzymanie pisania"
+#: ../pidgin/gtkpounce.c:1458
msgid "Signed off"
msgstr "Wylogowanie"
+#: ../pidgin/gtkpounce.c:1460
msgid "Became idle"
msgstr "Przejście w stan bezczynności"
+#: ../pidgin/gtkpounce.c:1462
msgid "Went away"
msgstr "Przejście do stanu nieobecności"
+#: ../pidgin/gtkpounce.c:1464
msgid "Sent a message"
msgstr "Wysłanie wiadomości"
+#: ../pidgin/gtkpounce.c:1465
msgid "Unknown.... Please report this!"
msgstr "Nieznane... Proszę to zgłosić."
+#. "audiotestsrc wave=silence", "Silence",
+#: ../pidgin/gtkprefs.c:144
+msgid "Test Sound"
+msgstr "Test dźwięku"
+
+#: ../pidgin/gtkprefs.c:161 ../pidgin/gtkprefs.c:2136
+msgid "Disabled"
+msgstr "Wyłączone"
+
+#: ../pidgin/gtkprefs.c:162
+msgid "Test Input"
+msgstr "Test wejścia"
+
+#: ../pidgin/gtkprefs.c:582
msgid "(Custom)"
msgstr "(Własne)"
+#: ../pidgin/gtkprefs.c:594
+msgid "none"
+msgstr "brak"
+
+#: ../pidgin/gtkprefs.c:594 ../pidgin/gtkprefs.c:642 ../pidgin/gtkprefs.c:775
+#: ../pidgin/gtkprefs.c:783 ../pidgin/gtkprefs.c:794
msgid "Penguin Pimps"
msgstr "Pingwinki"
+#: ../pidgin/gtkprefs.c:595
+msgid "Selecting this disables graphical emoticons."
+msgstr "Wybranie tego motywu wyłącza graficzne emotikony."
+
+#: ../pidgin/gtkprefs.c:643
msgid "The default Pidgin sound theme"
msgstr "Domyślny motyw dźwięków programu Pidgin"
+#: ../pidgin/gtkprefs.c:776
msgid "The default Pidgin buddy list theme"
msgstr "Domyślny motyw listy znajomych programu Pidgin"
+#: ../pidgin/gtkprefs.c:784
+msgid "The default Pidgin conversation theme"
+msgstr "Domyślny motyw okna rozmów programu Pidgin"
+
+#: ../pidgin/gtkprefs.c:795
msgid "The default Pidgin status icon theme"
msgstr "Domyślny motyw ikon stanu programu Pidgin"
+#: ../pidgin/gtkprefs.c:953 ../pidgin/gtkprefs.c:961
msgid "Theme failed to unpack."
msgstr "Rozpakowanie motywu się nie powiodło."
+#: ../pidgin/gtkprefs.c:1009 ../pidgin/gtkprefs.c:1057
msgid "Theme failed to load."
msgstr "Wczytanie motywu się nie powiodło."
+#: ../pidgin/gtkprefs.c:1060
msgid "Theme failed to copy."
msgstr "Skopiowanie motywu się nie powiodło."
+#: ../pidgin/gtkprefs.c:1479
msgid "Theme Selections"
msgstr "Wybór motywów"
#. Instructions
+#: ../pidgin/gtkprefs.c:1482
msgid ""
"Select a theme that you would like to use from the lists below.\n"
-"New themes can be installed by dragging and dropping them onto the theme "
-"list."
-msgstr ""
-"Proszę wybrać motyw do użycia z poniższej listy. Nowe motywy\n"
-"można zainstalować przeciągając je na listę motywów."
+"New themes can be installed by dragging and dropping them onto the theme list."
+msgstr "Proszę wybrać motyw do użycia z poniższej listy. Nowe motywy\nmożna zainstalować przeciągając je na listę motywów."
+#: ../pidgin/gtkprefs.c:1496
msgid "Buddy List Theme:"
msgstr "Motyw listy znajomych:"
+#: ../pidgin/gtkprefs.c:1502
+msgid "Conversation Theme:"
+msgstr "Motyw okna rozmowy:"
+
+#: ../pidgin/gtkprefs.c:1507
+msgid "\tVariant:"
+msgstr "\tWariant:"
+
+#: ../pidgin/gtkprefs.c:1516
msgid "Status Icon Theme:"
msgstr "Motyw ikony stanu:"
+#: ../pidgin/gtkprefs.c:1522
msgid "Sound Theme:"
msgstr "Motyw dźwięków:"
+#: ../pidgin/gtkprefs.c:1528
msgid "Smiley Theme:"
msgstr "Motyw emotikon:"
+#: ../pidgin/gtkprefs.c:1688
msgid "Keyboard Shortcuts"
msgstr "Skróty klawiszowe"
+#: ../pidgin/gtkprefs.c:1708
msgid "Cl_ose conversations with the Escape key"
msgstr "Zamykanie r_ozmów za pomocą klawisza Escape"
#. System Tray
+#: ../pidgin/gtkprefs.c:1738
msgid "System Tray Icon"
msgstr "Ikona obszaru powiadamiania"
+#: ../pidgin/gtkprefs.c:1739
msgid "_Show system tray icon:"
msgstr "Wyświetlanie ikony ob_szaru powiadamiania:"
+#: ../pidgin/gtkprefs.c:1742
msgid "On unread messages"
msgstr "Kiedy są nieprzeczytane wiadomości"
+#: ../pidgin/gtkprefs.c:1748
msgid "Conversation Window"
msgstr "Okno rozmów"
+#: ../pidgin/gtkprefs.c:1749
msgid "_Hide new IM conversations:"
msgstr "_Ukrywanie nowych rozmów:"
+#: ../pidgin/gtkprefs.c:1752 ../pidgin/gtkprefs.c:3435
msgid "When away"
msgstr "Podczas nieobecności"
+#: ../pidgin/gtkprefs.c:1759
msgid "Minimi_ze new conversation windows"
msgstr "Minimali_zowanie nowych okien rozmów"
#. All the tab options!
+#: ../pidgin/gtkprefs.c:1763
msgid "Tabs"
msgstr "Karty"
+#: ../pidgin/gtkprefs.c:1765
msgid "Show IMs and chats in _tabbed windows"
msgstr "Wyświe_tlanie wiadomości i konferencji w kartach"
+#: ../pidgin/gtkprefs.c:1779
msgid "Show close b_utton on tabs"
msgstr "Wyświetlanie przycisku _zamknięcia na kartach"
+#: ../pidgin/gtkprefs.c:1782
msgid "_Placement:"
msgstr "_Położenie:"
+#: ../pidgin/gtkprefs.c:1784
msgid "Top"
msgstr "Góra"
+#: ../pidgin/gtkprefs.c:1785
msgid "Bottom"
msgstr "Dół"
+#: ../pidgin/gtkprefs.c:1786
msgid "Left"
msgstr "Lewo"
+#: ../pidgin/gtkprefs.c:1787
msgid "Right"
msgstr "Prawo"
+#: ../pidgin/gtkprefs.c:1788
msgid "Left Vertical"
msgstr "Pionowo z lewej"
+#: ../pidgin/gtkprefs.c:1789
msgid "Right Vertical"
msgstr "Pionowo z prawej"
+#: ../pidgin/gtkprefs.c:1795
msgid "N_ew conversations:"
msgstr "Now_e rozmowy:"
+#: ../pidgin/gtkprefs.c:1856
+msgid "Chat notification:"
+msgstr "Powiadomienia rozmów:"
+
+#: ../pidgin/gtkprefs.c:1858
+msgid "On unseen events"
+msgstr "Niewyświetlone wydarzenia"
+
+#: ../pidgin/gtkprefs.c:1859
+msgid "On unseen text"
+msgstr "Niewyświetlony tekst"
+
+#: ../pidgin/gtkprefs.c:1860
+msgid "On unseen text and the nick was said"
+msgstr "Niewyświetlony tekst i wspomniany pseudonim"
+
+#: ../pidgin/gtkprefs.c:1863
msgid "Show _formatting on incoming messages"
msgstr "Wyświetlanie f_ormatowania wiadomości przychodzących"
+#: ../pidgin/gtkprefs.c:1865
msgid "Close IMs immediately when the tab is closed"
msgstr "Zamykanie wiadomości po zamknięciu karty"
+#: ../pidgin/gtkprefs.c:1868
msgid "Show _detailed information"
msgstr "Wyświetlanie _szczegółowych informacji"
+#: ../pidgin/gtkprefs.c:1870
msgid "Enable buddy ic_on animation"
msgstr "Animowane i_kony znajomych"
+#: ../pidgin/gtkprefs.c:1877
msgid "_Notify buddies that you are typing to them"
msgstr "Powiadamia_nie znajomych o pisaniu"
+#: ../pidgin/gtkprefs.c:1879
msgid "Highlight _misspelled words"
msgstr "Wyróżnianie _błędów pisowni"
+#: ../pidgin/gtkprefs.c:1882
msgid "Use smooth-scrolling"
msgstr "Płynne przewijanie"
+#: ../pidgin/gtkprefs.c:1885
msgid "F_lash window when IMs are received"
msgstr "_Migotanie okna po odebraniu wiadomości"
+#: ../pidgin/gtkprefs.c:1892
msgid "Resize incoming custom smileys"
msgstr "Zmiana rozmiaru przychodzących własnych emotikon"
+#: ../pidgin/gtkprefs.c:1896
msgid "Maximum size:"
msgstr "Maksymalny rozmiar:"
+#: ../pidgin/gtkprefs.c:1911
msgid "Minimum input area height in lines:"
msgstr "Minimalna wysokość pola wprowadzania tekstu w wierszach:"
+#: ../pidgin/gtkprefs.c:1919
msgid "Font"
msgstr "Czcionka"
+#: ../pidgin/gtkprefs.c:1921
msgid "Use font from _theme"
msgstr "Użycie czcionki z mo_tywu"
+#: ../pidgin/gtkprefs.c:1932
msgid "Conversation _font:"
msgstr "_Czcionka rozmowy:"
+#: ../pidgin/gtkprefs.c:1942
msgid "Default Formatting"
msgstr "Domyślne formatowanie"
+#: ../pidgin/gtkprefs.c:1961
msgid ""
"This is how your outgoing message text will appear when you use protocols "
"that support formatting."
-msgstr ""
-"Tak będzie wyglądał tekst wiadomości wychodzącej podczas używania protokołów "
-"obsługujących formatowanie."
+msgstr "Tak będzie wyglądał tekst wiadomości wychodzącej podczas używania protokołów obsługujących formatowanie."
+#: ../pidgin/gtkprefs.c:2088
msgid "Cannot start proxy configuration program."
msgstr "Nie można uruchomić programu konfiguracji pośrednika."
+#: ../pidgin/gtkprefs.c:2101
msgid "Cannot start browser configuration program."
msgstr "Nie można uruchomić programu konfiguracji przeglądarki."
-msgid "Disabled"
-msgstr "Wyłączone"
-
+#: ../pidgin/gtkprefs.c:2138
#, c-format
msgid "Use _automatically detected IP address: %s"
msgstr "_Automatyczne wykrywanie adresu IP: %s"
+#: ../pidgin/gtkprefs.c:2181
msgid "ST_UN server:"
msgstr "Serwer ST_UN:"
+#: ../pidgin/gtkprefs.c:2193
msgid "<span style=\"italic\">Example: stunserver.org</span>"
msgstr "<span style=\"italic\">Przykład: stunserver.org</span>"
+#: ../pidgin/gtkprefs.c:2217
msgid "Public _IP:"
msgstr "Publiczne _IP:"
+#: ../pidgin/gtkprefs.c:2229
msgid "Ports"
msgstr "Porty"
+#: ../pidgin/gtkprefs.c:2232
msgid "_Enable automatic router port forwarding"
msgstr "Automatyczn_e przekierowanie portów routera"
+#: ../pidgin/gtkprefs.c:2237
msgid "_Manually specify range of ports to listen on:"
msgstr "_Ręcznie podany zakres portów do nasłuchiwania:"
+#: ../pidgin/gtkprefs.c:2240
msgid "_Start:"
msgstr "_Początkowy:"
+#: ../pidgin/gtkprefs.c:2247
msgid "_End:"
msgstr "Koń_cowy:"
#. TURN server
+#: ../pidgin/gtkprefs.c:2259
msgid "Relay Server (TURN)"
msgstr "Serwer przekaźnika (TURN)"
+#: ../pidgin/gtkprefs.c:2269
msgid "_TURN server:"
msgstr "Serwer _TURN:"
+#: ../pidgin/gtkprefs.c:2272
msgid "_UDP Port:"
msgstr "Port _UDP:"
+#: ../pidgin/gtkprefs.c:2275
msgid "T_CP Port:"
msgstr "Port T_CP:"
+#: ../pidgin/gtkprefs.c:2278
msgid "Use_rname:"
msgstr "Na_zwa użytkownika:"
+#: ../pidgin/gtkprefs.c:2280
msgid "Pass_word:"
msgstr "_Hasło:"
+#: ../pidgin/gtkprefs.c:2311
msgid "Seamonkey"
msgstr "SeaMonkey"
+#: ../pidgin/gtkprefs.c:2312
msgid "Opera"
msgstr "Opera"
-msgid "Netscape"
-msgstr "Netscape"
-
+#: ../pidgin/gtkprefs.c:2313
msgid "Mozilla"
msgstr "Mozilla"
+#: ../pidgin/gtkprefs.c:2314
msgid "Konqueror"
msgstr "Konqueror"
+#: ../pidgin/gtkprefs.c:2315
msgid "Google Chrome"
msgstr "Google Chrome"
#. Do not move the line below. Code below expects gnome-open to be in
#. * this list immediately after xdg-open!
+#: ../pidgin/gtkprefs.c:2318
msgid "Desktop Default"
msgstr "Domyślna środowiska"
+#: ../pidgin/gtkprefs.c:2319
msgid "GNOME Default"
msgstr "Domyślna środowiska GNOME"
+#: ../pidgin/gtkprefs.c:2320
msgid "Galeon"
msgstr "Galeon"
+#: ../pidgin/gtkprefs.c:2321
msgid "Firefox"
msgstr "Firefox"
+#: ../pidgin/gtkprefs.c:2322
msgid "Firebird"
msgstr "Firebird"
+#: ../pidgin/gtkprefs.c:2323
msgid "Epiphany"
msgstr "Epiphany"
#. Translators: please do not translate "chromium-browser" here!
+#: ../pidgin/gtkprefs.c:2325
msgid "Chromium (chromium-browser)"
msgstr "Chromium (chromium-browser)"
#. Translators: please do not translate "chrome" here!
+#: ../pidgin/gtkprefs.c:2327
msgid "Chromium (chrome)"
msgstr "Chromium (chrome)"
+#: ../pidgin/gtkprefs.c:2336
msgid "Manual"
msgstr "Ręcznie"
+#: ../pidgin/gtkprefs.c:2392
msgid "Browser Selection"
msgstr "Wybór przeglądarki"
+#: ../pidgin/gtkprefs.c:2398
msgid "Browser preferences are configured in GNOME preferences"
-msgstr ""
-"Preferencje przeglądarki są konfigurowane w preferencjach środowiska GNOME"
+msgstr "Preferencje przeglądarki są konfigurowane w preferencjach środowiska GNOME"
+#: ../pidgin/gtkprefs.c:2417
msgid "<b>Browser configuration program was not found.</b>"
msgstr "<b>Program konfiguracji przeglądarki nie został odnaleziony.</b>"
+#: ../pidgin/gtkprefs.c:2420
msgid "Configure _Browser"
msgstr "Sk_onfiguruj przeglądarkę"
+#: ../pidgin/gtkprefs.c:2433
msgid "_Browser:"
msgstr "_Przeglądarka:"
+#: ../pidgin/gtkprefs.c:2441
msgid "_Open link in:"
msgstr "_Otwieranie odnośników w:"
+#: ../pidgin/gtkprefs.c:2443
msgid "Browser default"
msgstr "Domyślna przeglądarka"
-msgid "Existing window"
-msgstr "Istniejące okno"
-
+#: ../pidgin/gtkprefs.c:2445
msgid "New tab"
msgstr "Nowa karta"
+#: ../pidgin/gtkprefs.c:2462
#, c-format
msgid ""
"_Manual:\n"
"(%s for URL)"
-msgstr ""
-"_Ręcznie:\n"
-"(%s dla adresu URL)"
+msgstr "_Ręcznie:\n(%s dla adresu URL)"
+#: ../pidgin/gtkprefs.c:2487
msgid "Proxy Server"
msgstr "Serwer pośrednika"
+#: ../pidgin/gtkprefs.c:2494
msgid "Proxy preferences are configured in GNOME preferences"
-msgstr ""
-"Preferencje pośrednika są konfigurowane w preferencjach środowiska GNOME"
+msgstr "Preferencje pośrednika są konfigurowane w preferencjach środowiska GNOME"
+#: ../pidgin/gtkprefs.c:2516
msgid "<b>Proxy configuration program was not found.</b>"
msgstr "<b>Program konfiguracji pośrednika nie został odnaleziony.</b>"
+#: ../pidgin/gtkprefs.c:2520
msgid "Configure _Proxy"
msgstr "S_konfiguruj pośrednika"
#. This is a global option that affects SOCKS4 usage even with
#. * account-specific proxy settings
+#: ../pidgin/gtkprefs.c:2535
msgid "Use remote _DNS with SOCKS4 proxies"
msgstr "Użycie zdalnego _DNS za pomocą pośredników SOCKS 4"
+#: ../pidgin/gtkprefs.c:2539
msgid "Proxy t_ype:"
msgstr "_Typ pośrednika:"
+#: ../pidgin/gtkprefs.c:2541
msgid "No proxy"
msgstr "Bez pośrednika"
+#: ../pidgin/gtkprefs.c:2582
msgid "P_ort:"
msgstr "_Port:"
+#: ../pidgin/gtkprefs.c:2599
msgid "User_name:"
msgstr "_Nazwa użytkownika:"
+#: ../pidgin/gtkprefs.c:2659
msgid "Log _format:"
-msgstr "_Format dziennika:"
+msgstr "_Format archiwum:"
+#: ../pidgin/gtkprefs.c:2664
msgid "Log all _instant messages"
msgstr "Zapisywanie wszystkich _wiadomości"
+#: ../pidgin/gtkprefs.c:2666
msgid "Log all c_hats"
msgstr "Zapisywanie wszystkich _konferencji"
+#: ../pidgin/gtkprefs.c:2668
msgid "Log all _status changes to system log"
msgstr "Zapisywanie wszystkich zmian _stanu do dziennika systemowego"
+#: ../pidgin/gtkprefs.c:2810
+#: ../pidgin/plugins/musicmessaging/musicmessaging.c:677
+msgid "_Apply"
+msgstr "Z_astosuj"
+
+#: ../pidgin/gtkprefs.c:2836
+msgid "Failed to set new keyring"
+msgstr "Ustawienie nowej bazy kluczy się nie powiodło"
+
+#: ../pidgin/gtkprefs.c:2862
+msgid "Selected keyring is disabled"
+msgstr "Wybrana baza kluczy jest wyłączona"
+
+#: ../pidgin/gtkprefs.c:2919
+msgid "Keyring:"
+msgstr "Baza kluczy:"
+
+#: ../pidgin/gtkprefs.c:3081
msgid "Sound Selection"
msgstr "Wybór dźwięków"
+#: ../pidgin/gtkprefs.c:3091
#, c-format
msgid "Quietest"
msgstr "Najciszej"
+#: ../pidgin/gtkprefs.c:3093
#, c-format
msgid "Quieter"
msgstr "Ciszej"
+#: ../pidgin/gtkprefs.c:3095
#, c-format
msgid "Quiet"
msgstr "Cicho"
+#: ../pidgin/gtkprefs.c:3099
#, c-format
msgid "Loud"
msgstr "Głośno"
+#: ../pidgin/gtkprefs.c:3101
#, c-format
msgid "Louder"
msgstr "Głośniej"
+#: ../pidgin/gtkprefs.c:3103
#, c-format
msgid "Loudest"
msgstr "Najgłośniej"
+#: ../pidgin/gtkprefs.c:3194
msgid "_Method:"
msgstr "Metod_a:"
+#: ../pidgin/gtkprefs.c:3209
msgid "Console beep"
msgstr "Sygnał konsoli"
+#: ../pidgin/gtkprefs.c:3212
msgid "No sounds"
msgstr "Bez dźwięków"
+#: ../pidgin/gtkprefs.c:3225
#, c-format
msgid ""
"Sound c_ommand:\n"
"(%s for filename)"
-msgstr ""
-"P_olecenie odtworzenia dźwięku\n"
-"(%s dla nazwy pliku)"
+msgstr "P_olecenie odtworzenia dźwięku\n(%s dla nazwy pliku)"
+#: ../pidgin/gtkprefs.c:3232
msgid "M_ute sounds"
msgstr "Wyci_szenie dźwięków"
+#: ../pidgin/gtkprefs.c:3235
msgid "Sounds when conversation has _focus"
msgstr "_Dźwięki w aktywnym oknie rozmowy"
+#: ../pidgin/gtkprefs.c:3237
msgid "_Enable sounds:"
msgstr "Włącz_enie dźwięków:"
+#: ../pidgin/gtkprefs.c:3255
msgid "V_olume:"
msgstr "Gł_ośność:"
+#: ../pidgin/gtkprefs.c:3317
msgid "Play"
msgstr "Odtwórz"
+#: ../pidgin/gtkprefs.c:3345
msgid "_Browse..."
msgstr "_Przeglądaj..."
+#: ../pidgin/gtkprefs.c:3353
msgid "_Reset"
msgstr "P_rzywróć"
+#: ../pidgin/gtkprefs.c:3396
msgid "_Report idle time:"
msgstr "_Zgłaszanie czasu bezczynności:"
+#: ../pidgin/gtkprefs.c:3401
msgid "Based on keyboard or mouse use"
msgstr "Na podstawie ruchów klawiatury i myszy"
+#: ../pidgin/gtkprefs.c:3408
msgid "_Minutes before becoming idle:"
msgstr "_Minuty przed zmianą stanu:"
+#: ../pidgin/gtkprefs.c:3414
msgid "Change to this status when _idle:"
msgstr "Zm_iana na ten stan podczas bezczynności:"
+#: ../pidgin/gtkprefs.c:3432
msgid "_Auto-reply:"
msgstr "_Odpowiedź automatyczna:"
+#: ../pidgin/gtkprefs.c:3436
msgid "When both away and idle"
msgstr "Podczas nieobecności i bezczynności"
#. Signon status stuff
+#: ../pidgin/gtkprefs.c:3442
msgid "Status at Startup"
msgstr "Stan ustawiany podczas uruchomienia"
+#: ../pidgin/gtkprefs.c:3444
msgid "Use status from last _exit at startup"
msgstr "Użycie stanu z ostatniej s_esji programu"
+#: ../pidgin/gtkprefs.c:3453
msgid "Status to a_pply at startup:"
msgstr "Stan _ustawiany podczas uruchamiania:"
+#: ../pidgin/gtkprefs.c:3495
+msgid "Random noise"
+msgstr "Losowy szum"
+
+#: ../pidgin/gtkprefs.c:3741
+msgid "DROP"
+msgstr "Upuszczenie"
+
+#: ../pidgin/gtkprefs.c:3815 ../pidgin/gtkprefs.c:3847
+#, c-format
+msgid "Silence threshold: %d%%"
+msgstr "Próg ciszy: %d%%"
+
+#: ../pidgin/gtkprefs.c:3838
+msgid "Volume:"
+msgstr "Głośność:"
+
+#: ../pidgin/gtkprefs.c:3862
+msgid "Test Audio"
+msgstr "Testuj dźwięk"
+
+#: ../pidgin/gtkprefs.c:4017
+msgid "Test Video"
+msgstr "Testuj obraz"
+
+#: ../pidgin/gtkprefs.c:4048 ../pidgin/gtkprefs.c:4096
+msgid "_Device"
+msgstr "Urzą_dzenie"
+
+#: ../pidgin/gtkprefs.c:4080
+msgid "_Plugin"
+msgstr "_Wtyczka"
+
+#: ../pidgin/gtkprefs.c:4124
+msgid "Audio"
+msgstr "Dźwięk"
+
+#: ../pidgin/gtkprefs.c:4125 ../pidgin/gtkprefs.c:4134
+msgid "Input"
+msgstr "Wejście"
+
+#: ../pidgin/gtkprefs.c:4128 ../pidgin/gtkprefs.c:4137
+msgid "Output"
+msgstr "Wyjście"
+
+#: ../pidgin/gtkprefs.c:4133
+msgid "Video"
+msgstr "Obraz"
+
+#: ../pidgin/gtkprefs.c:4157
msgid "Interface"
msgstr "Interfejs"
+#: ../pidgin/gtkprefs.c:4163
msgid "Browser"
msgstr "Przeglądarka"
+#: ../pidgin/gtkprefs.c:4170
+msgid "Password Storage"
+msgstr "Przechowywanie haseł"
+
+#: ../pidgin/gtkprefs.c:4173
msgid "Status / Idle"
msgstr "Stan/bezczynność"
+#: ../pidgin/gtkprefs.c:4174
msgid "Themes"
msgstr "Motywy"
+#: ../pidgin/gtkprefs.c:4176
+msgid "Voice/Video"
+msgstr "Dźwięk/obraz"
+
+#: ../pidgin/gtkprivacy.c:78
msgid "Allow all users to contact me"
msgstr "Zezwalanie wszystkim użytkownikom na kontakt"
+#: ../pidgin/gtkprivacy.c:79
msgid "Allow only the users on my buddy list"
msgstr "Zezwalanie tylko użytkownikom z listy znajomych"
+#: ../pidgin/gtkprivacy.c:80
msgid "Allow only the users below"
msgstr "Zezwalanie tylko poniższym użytkownikom"
+#: ../pidgin/gtkprivacy.c:81
msgid "Block all users"
msgstr "Blokowanie wszystkich użytkowników"
+#: ../pidgin/gtkprivacy.c:82
msgid "Block only the users below"
msgstr "Blokowanie tylko poniższych użytkowników"
+#: ../pidgin/gtkprivacy.c:341
msgid "Privacy"
msgstr "Prywatność"
+#: ../pidgin/gtkprivacy.c:351
msgid "Changes to privacy settings take effect immediately."
msgstr "Zmiany ustawień prywatności są uwzględniane natychmiast."
+#: ../pidgin/gtkprivacy.c:360
msgid "Set privacy for:"
msgstr "Ustawienie prywatności dla:"
#. Remove All button
+#: ../pidgin/gtkprivacy.c:403
msgid "Remove Al_l"
msgstr "Usuń wszyst_ko"
+#: ../pidgin/gtkprivacy.c:489 ../pidgin/gtkprivacy.c:506
msgid "Permit User"
msgstr "Zezwolenie dla użytkownika"
+#: ../pidgin/gtkprivacy.c:490
msgid "Type a user you permit to contact you."
msgstr "Proszę podać użytkownika, który ma pozwolenie na kontakt."
+#: ../pidgin/gtkprivacy.c:491
msgid "Please enter the name of the user you wish to be able to contact you."
-msgstr ""
-"Proszę podać nazwę użytkownika, który ma mieć możliwość kontaktowania się."
+msgstr "Proszę podać nazwę użytkownika, który ma mieć możliwość kontaktowania się."
+#: ../pidgin/gtkprivacy.c:494 ../pidgin/gtkprivacy.c:509
msgid "_Permit"
msgstr "_Zezwolenie"
+#: ../pidgin/gtkprivacy.c:500
#, c-format
msgid "Allow %s to contact you?"
msgstr "Zezwolić użytkownikowi %s na kontaktowanie się?"
+#: ../pidgin/gtkprivacy.c:502
#, c-format
msgid "Are you sure you wish to allow %s to contact you?"
msgstr "Na pewno zezwolić użytkownikowi %s na kontaktowanie się?"
+#: ../pidgin/gtkprivacy.c:530 ../pidgin/gtkprivacy.c:544
msgid "Block User"
msgstr "Blokowanie użytkownika"
+#: ../pidgin/gtkprivacy.c:531
msgid "Type a user to block."
msgstr "Proszę podać użytkownika do zablokowania."
+#: ../pidgin/gtkprivacy.c:532
msgid "Please enter the name of the user you wish to block."
msgstr "Proszę podać nazwę użytkownika, który ma zostać zablokowany."
+#: ../pidgin/gtkprivacy.c:540
#, c-format
msgid "Block %s?"
msgstr "Zablokować użytkownika %s?"
+#: ../pidgin/gtkprivacy.c:542
#, c-format
msgid "Are you sure you want to block %s?"
msgstr "Na pewno zablokować użytkownika %s?"
+#: ../pidgin/gtkrequest.c:342
msgid "Apply"
msgstr "Zastosuj"
+#: ../pidgin/gtkrequest.c:348
+msgid "Next"
+msgstr "Dalej"
+
+#: ../pidgin/gtkrequest.c:349 ../pidgin/pidginstock.c:90
+msgid "_Next"
+msgstr "_Dalej"
+
+#: ../pidgin/gtkrequest.c:350
+msgid "Back"
+msgstr "Wstecz"
+
+#: ../pidgin/gtkrequest.c:351
+msgid "_Back"
+msgstr "_Wstecz"
+
+#: ../pidgin/gtkrequest.c:941
+msgid "Please wait"
+msgstr "Proszę czekać"
+
+#: ../pidgin/gtkrequest.c:2487
msgid "That file already exists"
msgstr "Ten plik już istnieje"
+#: ../pidgin/gtkrequest.c:2488
msgid "Would you like to overwrite it?"
msgstr "Zastąpić go?"
+#: ../pidgin/gtkrequest.c:2491
msgid "Overwrite"
msgstr "Zastąp"
+#: ../pidgin/gtkrequest.c:2492
msgid "Choose New Name"
msgstr "Wybór nowej nazwy"
+#: ../pidgin/gtkrequest.c:2593
msgid "Select Folder..."
msgstr "Wybierz katalog..."
#. list button
+#: ../pidgin/gtkroomlist.c:614
msgid "_Get List"
msgstr "_Pobierz listę"
#. add button
+#: ../pidgin/gtkroomlist.c:622
msgid "_Add Chat"
msgstr "Dod_aj konferencję"
+#: ../pidgin/gtksavedstatuses.c:304
msgid "Are you sure you want to delete the selected saved statuses?"
msgstr "Na pewno usunąć wybrane zapisane stany?"
#. Use button
+#: ../pidgin/gtksavedstatuses.c:579 ../pidgin/gtksavedstatuses.c:1204
msgid "_Use"
msgstr "_Użyj"
+#: ../pidgin/gtksavedstatuses.c:723
msgid "Title already in use. You must choose a unique title."
msgstr "Ten tytuł jest już używany. Należy wybrać unikalny tytuł."
+#: ../pidgin/gtksavedstatuses.c:936
msgid "Different"
msgstr "Różne"
+#: ../pidgin/gtksavedstatuses.c:1133
msgid "_Title:"
msgstr "_Tytuł:"
+#: ../pidgin/gtksavedstatuses.c:1141 ../pidgin/gtksavedstatuses.c:1420
msgid "_Status:"
msgstr "_Stan:"
#. Different status message expander
+#: ../pidgin/gtksavedstatuses.c:1157
msgid "Use a _different status for some accounts"
msgstr "Użycie _różnych stanów dla poszczególnych kont"
#. Save & Use button
+#: ../pidgin/gtksavedstatuses.c:1211
msgid "Sa_ve & Use"
msgstr "Z_apisz i użyj"
+#: ../pidgin/gtksavedstatuses.c:1404
#, c-format
msgid "Status for %s"
msgstr "Stan dla %s"
+#: ../pidgin/gtksmiley-manager.c:187 ../pidgin/gtksmiley-manager.c:232
+msgid "Custom Smiley"
+msgstr "Własna emotikona"
+
+#: ../pidgin/gtksmiley-manager.c:230
#, c-format
msgid ""
"A custom smiley for '%s' already exists. Please use a different shortcut."
msgstr "Własna emotikona dla \"%s\" już istnieje. Proszę wybrać inny skrót."
-msgid "Custom Smiley"
-msgstr "Własna emotikona"
-
+#: ../pidgin/gtksmiley-manager.c:233
msgid "Duplicate Shortcut"
msgstr "Duplikat skrótu"
+#: ../pidgin/gtksmiley-manager.c:326
msgid "Edit Smiley"
msgstr "Modyfikacja emotikony"
+#: ../pidgin/gtksmiley-manager.c:326
msgid "Add Smiley"
msgstr "Dodanie emotikony"
+#: ../pidgin/gtksmiley-manager.c:368
msgid "_Image:"
msgstr "_Obraz:"
#. Shortcut text
+#: ../pidgin/gtksmiley-manager.c:404
msgid "S_hortcut text:"
msgstr "_Tekst skrótu:"
+#: ../pidgin/gtksmiley-manager.c:707
msgid "Smiley"
msgstr "Emotikona"
+#: ../pidgin/gtksmiley-manager.c:716
msgid "Shortcut Text"
msgstr "Tekst skrótu"
+#: ../pidgin/gtksmiley-manager.c:803
msgid "Custom Smiley Manager"
msgstr "Menadżer własnych emotikon"
+#: ../pidgin/gtkstatusbox.c:319
msgid "Select Buddy Icon"
msgstr "Wybór ikony znajomego"
+#: ../pidgin/gtkstatusbox.c:403
msgid "Click to change your buddyicon for this account."
msgstr "Naciśnięcie zmieni ikonę znajomego dla tego konta."
+#: ../pidgin/gtkstatusbox.c:404
msgid "Click to change your buddyicon for all accounts."
msgstr "Naciśnięcie zmieni ikonę znajomego dla wszystkich kont."
+#: ../pidgin/gtkstatusbox.c:673
msgid "Waiting for network connection"
msgstr "Oczekiwanie na połączenie sieciowe"
+#: ../pidgin/gtkstatusbox.c:1061
msgid "New status..."
msgstr "Nowy stan..."
+#: ../pidgin/gtkstatusbox.c:1062
msgid "Saved statuses..."
msgstr "Zapisane stany..."
+#: ../pidgin/gtkstatusbox.c:1762
msgid "Status Selector"
msgstr "Wybór stanu"
-msgid "Google Talk"
-msgstr "Google Talk"
-
-msgid "Facebook (XMPP)"
-msgstr "Facebook (XMPP)"
-
+#: ../pidgin/gtkutils.c:1397 ../pidgin/gtkutils.c:1425
#, c-format
msgid "The following error has occurred loading %s: %s"
msgstr "Wystąpił następujący błąd podczas wczytywania %s: %s"
+#: ../pidgin/gtkutils.c:1400 ../pidgin/gtkutils.c:1427
msgid "Failed to load image"
msgstr "Wczytanie obrazu się nie powiodło"
+#: ../pidgin/gtkutils.c:1501
#, c-format
msgid "Cannot send folder %s."
msgstr "Nie można wysłać katalogu %s."
+#: ../pidgin/gtkutils.c:1502
#, c-format
msgid ""
"%s cannot transfer a folder. You will need to send the files within "
"individually."
-msgstr ""
-"Użytkownik %s nie mógł wysłać katalogu. Należy wysłać pliki pojedynczo."
+msgstr "Użytkownik %s nie mógł wysłać katalogu. Należy wysłać pliki pojedynczo."
+#: ../pidgin/gtkutils.c:1535 ../pidgin/gtkutils.c:1547
+#: ../pidgin/gtkutils.c:1554
msgid "You have dragged an image"
msgstr "Upuszczono obraz"
+#: ../pidgin/gtkutils.c:1536
msgid ""
"You can send this image as a file transfer, embed it into this message, or "
"use it as the buddy icon for this user."
-msgstr ""
-"Można wysłać ten obraz jako przesyłanie pliku, wstawić go do tej wiadomości "
-"lub użyć go jako ikony znajomego dla tego użytkownika."
+msgstr "Można wysłać ten obraz jako przesyłanie pliku, wstawić go do tej wiadomości lub użyć go jako ikony znajomego dla tego użytkownika."
+#: ../pidgin/gtkutils.c:1542 ../pidgin/gtkutils.c:1562
msgid "Set as buddy icon"
msgstr "Ustaw jako ikonę znajomego"
+#: ../pidgin/gtkutils.c:1543 ../pidgin/gtkutils.c:1563
msgid "Send image file"
msgstr "Wyślij plik obrazu"
+#: ../pidgin/gtkutils.c:1544 ../pidgin/gtkutils.c:1563
msgid "Insert in message"
msgstr "Wstaw do wiadomości"
+#: ../pidgin/gtkutils.c:1548
msgid "Would you like to set it as the buddy icon for this user?"
msgstr "Ustawić jako ikonę znajomego dla tego użytkownika?"
+#: ../pidgin/gtkutils.c:1555
msgid ""
"You can send this image as a file transfer, or use it as the buddy icon for "
"this user."
-msgstr ""
-"Można wysłać ten obraz jako przesyłanie pliku lub użyć go jako ikony "
-"znajomego dla tego użytkownika."
+msgstr "Można wysłać ten obraz jako przesyłanie pliku lub użyć go jako ikony znajomego dla tego użytkownika."
+#: ../pidgin/gtkutils.c:1556
msgid ""
-"You can insert this image into this message, or use it as the buddy icon for "
-"this user"
-msgstr ""
-"Można wstawić ten obraz do tej wiadomości lub użyć go jako ikonę znajomego "
-"dla tego użytkownika"
+"You can insert this image into this message, or use it as the buddy icon for"
+" this user"
+msgstr "Można wstawić ten obraz do tej wiadomości lub użyć go jako ikonę znajomego dla tego użytkownika"
#. I don't know if we really want to do anything here. Most of
#. * the desktop item types are crap like "MIME Type" (I have no
@@ -13639,132 +16924,594 @@ msgstr ""
#. * "Application," but do we really want to send a binary and
#. * nothing else? Probably not. I'll just give an error and
#. * return.
-#. The original patch sent the icon used by the launcher. That's probably wrong
+#. The original patch sent the icon used by the launcher. That's probably
+#. wrong
+#: ../pidgin/gtkutils.c:1613
msgid "Cannot send launcher"
msgstr "Nie można wysłać aktywatora"
+#: ../pidgin/gtkutils.c:1614
msgid ""
-"You dragged a desktop launcher. Most likely you wanted to send the target of "
-"this launcher instead of this launcher itself."
-msgstr ""
-"Upuszczono aktywator pulpitu. Najprawdopodobniej zamierzano wysłać cel tego "
-"aktywatora zamiast jego samego."
+"You dragged a desktop launcher. Most likely you wanted to send the target of"
+" this launcher instead of this launcher itself."
+msgstr "Upuszczono aktywator pulpitu. Najprawdopodobniej zamierzano wysłać cel tego aktywatora zamiast jego samego."
+#: ../pidgin/gtkutils.c:2205
#, c-format
msgid ""
"<b>File:</b> %s\n"
"<b>File size:</b> %s\n"
"<b>Image size:</b> %dx%d"
-msgstr ""
-"<b>Plik:</b> %s\n"
-"<b>Rozmiar pliku:</b> %s\n"
-"<b>Rozmiar obrazu:</b> %dx%d"
+msgstr "<b>Plik:</b> %s\n<b>Rozmiar pliku:</b> %s\n<b>Rozmiar obrazu:</b> %dx%d"
+#: ../pidgin/gtkutils.c:2452
#, c-format
msgid "The file '%s' is too large for %s. Please try a smaller image.\n"
msgstr "Plik \"%s\" jest za duży dla %s. Proszę spróbować mniejszego obrazu.\n"
+#: ../pidgin/gtkutils.c:2454
msgid "Icon Error"
msgstr "Błąd ikony"
+#: ../pidgin/gtkutils.c:2454
msgid "Could not set icon"
msgstr "Nie można ustawić ikony"
+#: ../pidgin/gtkutils.c:3230
msgid "_Open Link"
msgstr "_Otwórz odnośnik"
+#: ../pidgin/gtkutils.c:3238
msgid "_Copy Link Location"
msgstr "S_kopiuj adres odnośnika"
+#: ../pidgin/gtkutils.c:3265
msgid "_Copy Email Address"
msgstr "S_kopiuj adres e-mail"
+#: ../pidgin/gtkutils.c:3303 ../pidgin/gtkxfer.c:475
+msgid "There is no application configured to open this type of file."
+msgstr "Żaden program nie jest skonfigurowana do otwierania tego typu plików."
+
+#: ../pidgin/gtkutils.c:3309 ../pidgin/gtkxfer.c:481
+msgid "An error occurred while opening the file."
+msgstr "Wystąpił błąd podczas otwierania pliku."
+
+#: ../pidgin/gtkutils.c:3345 ../pidgin/gtkxfer.c:518
+#, c-format
+msgid "Error launching %s: %s"
+msgstr "Błąd podczas uruchamiania %s: %s"
+
+#: ../pidgin/gtkutils.c:3353 ../pidgin/gtkxfer.c:527
+#, c-format
+msgid "Error running %s"
+msgstr "Błąd podczas wykonywania %s"
+
+#: ../pidgin/gtkutils.c:3354 ../pidgin/gtkxfer.c:528
+#, c-format
+msgid "Process returned error code %d"
+msgstr "Proces zwrócił kod błędu %d"
+
+#: ../pidgin/gtkutils.c:3391
msgid "_Open File"
msgstr "_Otwórz plik"
+#: ../pidgin/gtkutils.c:3399
msgid "Open _Containing Directory"
msgstr "Otwórz _katalog"
+#: ../pidgin/gtkutils.c:3452 ../pidgin/gtkwhiteboard.c:817
msgid "Save File"
msgstr "Zapisz plik"
+#: ../pidgin/gtkutils.c:3472
msgid "_Play Sound"
msgstr "O_dtwórz dźwięk"
+#: ../pidgin/gtkutils.c:3480
msgid "_Save File"
msgstr "Zapi_sz plik"
+#: ../pidgin/gtkwebview.c:407
+msgid "WebKit inspector"
+msgstr "Inspektor WebKit"
+
+#: ../pidgin/gtkwebview.c:496
+msgid "Input _Methods"
+msgstr "_Metody wprowadzania"
+
+#: ../pidgin/gtkwebview.c:509
+msgid "LRM _Left-to-right mark"
+msgstr "LRM znacznik od l_ewej do prawej"
+
+#: ../pidgin/gtkwebview.c:510
+msgid "RLM _Right-to-left mark"
+msgstr "RLM znacznik od p_rawej do lewej"
+
+#: ../pidgin/gtkwebview.c:511
+msgid "LRE Left-to-right _embedding"
+msgstr "LRE _osadzanie od lewej do prawej"
+
+#: ../pidgin/gtkwebview.c:512
+msgid "RLE Right-to-left e_mbedding"
+msgstr "RLE o_sadzanie od prawej do lewej"
+
+#: ../pidgin/gtkwebview.c:513
+msgid "LRO Left-to-right _override"
+msgstr "LRO _zastąpienie od lewej do prawej"
+
+#: ../pidgin/gtkwebview.c:514
+msgid "RLO Right-to-left o_verride"
+msgstr "RLO z_astąpienie od prawej do lewej"
+
+#: ../pidgin/gtkwebview.c:515
+msgid "PDF _Pop directional formatting"
+msgstr "PDF formatowanie kierunkowe _Pop"
+
+#: ../pidgin/gtkwebview.c:516
+msgid "ZWS _Zero width space"
+msgstr "ZWS spacja o _zerowej szerokości"
+
+#: ../pidgin/gtkwebview.c:517
+msgid "ZWJ Zero width _joiner"
+msgstr "ZWJ łą_cznik o zerowej szerokości"
+
+#: ../pidgin/gtkwebview.c:518
+msgid "ZWNJ Zero width _non-joiner"
+msgstr "ZWNJ _bezłącznik o zerowej szerokości"
+
+#: ../pidgin/gtkwebview.c:555
+msgid "_Insert Unicode Control Character"
+msgstr "_Wstaw znak kontrolny unikodu"
+
+#: ../pidgin/gtkwebview.c:630
+msgid "_Language"
+msgstr "_Język"
+
+#: ../pidgin/gtkwebview.c:703
+msgid "Save Image"
+msgstr "Zapis obrazu"
+
+#: ../pidgin/gtkwebview.c:809
+msgid "_Save Image..."
+msgstr "Zapi_sz obraz..."
+
+#: ../pidgin/gtkwebview.c:820
+msgid "_Add Custom Smiley..."
+msgstr "Dod_aj własną emotikonę..."
+
+#. Using connect_swapped means we don't need any wrapper functions
+#: ../pidgin/gtkwebview.c:837
+msgid "Cu_t"
+msgstr "_Wytnij"
+
+#: ../pidgin/gtkwebview.c:843
+msgid "_Copy"
+msgstr "S_kopiuj"
+
+#: ../pidgin/gtkwebview.c:849
+msgid "_Paste"
+msgstr "Wk_lej"
+
+#: ../pidgin/gtkwebview.c:855
+msgid "_Delete"
+msgstr "_Usuń"
+
+#: ../pidgin/gtkwebview.c:863
+msgid "Select _All"
+msgstr "Z_aznacz wszystko"
+
+#: ../pidgin/gtkwebview.c:896
+msgid "Inspect _Element"
+msgstr "Zbadaj _element"
+
+#: ../pidgin/gtkwebviewtoolbar.c:310 ../pidgin/plugins/themeedit.c:116
+msgid "Select Font"
+msgstr "Wybór czcionki"
+
+#: ../pidgin/gtkwebviewtoolbar.c:395
+msgid "Select Text Color"
+msgstr "Wybór koloru tekstu"
+
+#: ../pidgin/gtkwebviewtoolbar.c:479
+msgid "Select Background Color"
+msgstr "Wybór koloru tła"
+
+#: ../pidgin/gtkwebviewtoolbar.c:570
+msgid "_URL"
+msgstr "Adres _URL"
+
+#: ../pidgin/gtkwebviewtoolbar.c:576
+msgid "_Description"
+msgstr "_Opis"
+
+#: ../pidgin/gtkwebviewtoolbar.c:579
+msgid ""
+"Please enter the URL and description of the link that you want to insert. "
+"The description is optional."
+msgstr "Proszę podać adres URL i opis wstawianego odnośnika. Opis jest opcjonalny."
+
+#: ../pidgin/gtkwebviewtoolbar.c:583
+msgid "Please enter the URL of the link that you want to insert."
+msgstr "Proszę podać adres URL wstawianego odnośnika."
+
+#: ../pidgin/gtkwebviewtoolbar.c:588 ../pidgin/gtkwebviewtoolbar.c:1441
+msgid "Insert Link"
+msgstr "Wstawienie odnośnika"
+
+#: ../pidgin/gtkwebviewtoolbar.c:589 ../pidgin/gtkwebviewtoolbar.c:1591
+msgid "_Insert"
+msgstr "_Wstaw"
+
+#: ../pidgin/gtkwebviewtoolbar.c:626
+#, c-format
+msgid "Failed to store image: %s"
+msgstr "Przechowanie obrazu się nie powiodło: %s"
+
+#: ../pidgin/gtkwebviewtoolbar.c:652
+msgid "Insert Image"
+msgstr "Wstawienie obrazu"
+
+#: ../pidgin/gtkwebviewtoolbar.c:841
+#, c-format
+msgid ""
+"This smiley is disabled because a custom smiley exists for this shortcut:\n"
+" %s"
+msgstr "Ta emotikona jest wyłączona, ponieważ istnieje własna emotikona dla tego skrótu:\n %s"
+
+#: ../pidgin/gtkwebviewtoolbar.c:921
+msgid "Smile!"
+msgstr "Uśmiech!"
+
+#: ../pidgin/gtkwebviewtoolbar.c:942
+msgid "_Manage custom smileys"
+msgstr "_Zarządzaj własnymi emotikonami"
+
+#: ../pidgin/gtkwebviewtoolbar.c:971
+msgid "This theme has no available smileys."
+msgstr "Ten motyw nie posiada emotikon."
+
+#: ../pidgin/gtkwebviewtoolbar.c:1131 ../pidgin/gtkwebviewtoolbar.c:1551
+msgid "_Font"
+msgstr "_Czcionka"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1313
+msgid "Group Items"
+msgstr "Grupowanie elementów"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1313
+msgid "Ungroup Items"
+msgstr "Bez grupowania elementów"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1427
+msgid "<b>_Bold</b>"
+msgstr "<b>Pogru_bienie</b>"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1427
+msgid "Bold"
+msgstr "Pogrubienie"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1428
+msgid "<i>_Italic</i>"
+msgstr "<i>P_ochylenie</i>"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1428
+msgid "Italic"
+msgstr "Pochylenie"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1429
+msgid "<u>_Underline</u>"
+msgstr "<u>Po_dkreślenie</u>"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1429
+msgid "Underline"
+msgstr "Podkreślenie"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1430
+msgid "<span strikethrough='true'>Strikethrough</span>"
+msgstr "<span strikethrough='true'>Przekreślenie</span>"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1430
+msgid "Strikethrough"
+msgstr "Przekreślenie"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1431
+msgid "<span size='larger'>Larger</span>"
+msgstr "<span size='larger'>Większy</span>"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1431
+msgid "Increase Font Size"
+msgstr "Zwiększ rozmiar czcionki"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1433
+msgid "Normal Font Size"
+msgstr "Zwykły rozmiar czcionki"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1435
+msgid "<span size='smaller'>Smaller</span>"
+msgstr "<span size='smaller'>Mniejszy</span>"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1435
+msgid "Decrease Font Size"
+msgstr "Zmniejsz rozmiar czcionki"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1436
+msgid "_Font face"
+msgstr "_Krój czcionki"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1436
+msgid "Font Face"
+msgstr "Krój czcionki"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1437
+msgid "Foreground _color"
+msgstr "K_olor czcionki"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1437
+msgid "Foreground Color"
+msgstr "Kolor czcionki"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1438
+msgid "Bac_kground color"
+msgstr "_Kolor tła"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1438
+msgid "Background Color"
+msgstr "Kolor tła"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1439
+msgid "_Reset formatting"
+msgstr "P_rzywróć formatowanie"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1439
+msgid "Reset Formatting"
+msgstr "Przywróć formatowanie"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1440
+msgid "_Image"
+msgstr "_Obraz"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1440
+msgid "Insert IM Image"
+msgstr "Wstaw obraz"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1441
+msgid "_Link"
+msgstr "O_dnośnik"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1442
+msgid "_Horizontal rule"
+msgstr "_Linia pozioma"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1442
+msgid "Insert Horizontal rule"
+msgstr "Wstaw poziomą linię"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1443
+msgid "_Smile!"
+msgstr "_Uśmiech!"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1443
+msgid "Insert Smiley"
+msgstr "Wstaw emotikonę"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1444
+msgid "_Attention!"
+msgstr "_Uwaga!"
+
+#: ../pidgin/gtkwebviewtoolbar.c:1444
+msgid "Get Attention"
+msgstr "Zwróć uwagę"
+
+#: ../pidgin/gtkwhiteboard.c:794
msgid "Do you really want to clear?"
msgstr "Na pewno wyczyścić?"
-msgid "Select color"
-msgstr "Wybór koloru"
+#: ../pidgin/gtkxfer.c:151
+msgid "Not started"
+msgstr "Nie rozpoczęto"
+
+#: ../pidgin/gtkxfer.c:273
+msgid "<b>Receiving As:</b>"
+msgstr "<b>Odbieranie jako:</b>"
+
+#: ../pidgin/gtkxfer.c:275
+msgid "<b>Receiving From:</b>"
+msgstr "<b>Odbieranie od:</b>"
+
+#: ../pidgin/gtkxfer.c:279
+msgid "<b>Sending To:</b>"
+msgstr "<b>Wysyłanie do:</b>"
+
+#: ../pidgin/gtkxfer.c:281
+msgid "<b>Sending As:</b>"
+msgstr "<b>Wysyłanie jako:</b>"
+
+#: ../pidgin/gtkxfer.c:654
+msgid "Filename:"
+msgstr "Nazwa pliku:"
+
+#: ../pidgin/gtkxfer.c:655
+msgid "Local File:"
+msgstr "Lokalny plik:"
+
+#: ../pidgin/gtkxfer.c:657
+msgid "Speed:"
+msgstr "Prędkość:"
+
+#: ../pidgin/gtkxfer.c:658
+msgid "Time Elapsed:"
+msgstr "Upłynęło czasu:"
+
+#: ../pidgin/gtkxfer.c:659
+msgid "Time Remaining:"
+msgstr "Pozostało czasu:"
+
+#: ../pidgin/gtkxfer.c:740
+msgid "Close this window when all transfers _finish"
+msgstr "Zamknięcie tego okna po u_kończeniu wszystkich przesyłań"
+
+#: ../pidgin/gtkxfer.c:750
+msgid "C_lear finished transfers"
+msgstr "_Czyszczenie ukończonych przesyłań"
+
+#. "Download Details" arrow
+#: ../pidgin/gtkxfer.c:759
+msgid "File transfer _details"
+msgstr "_Szczegóły przesyłania plików"
+
+#: ../pidgin/libpidgin.c:385
+#, c-format
+msgid "%s %s. Try `%s -h' for more information.\n"
+msgstr "%s %s. Należy podać \"%s -h\", aby uzyskać więcej informacji.\n"
+
+#: ../pidgin/libpidgin.c:389
+#, c-format
+msgid ""
+"Usage: %s [OPTION]...\n"
+"\n"
+msgstr "Użycie: %s [OPCJA]...\n\n"
+
+#: ../pidgin/libpidgin.c:391
+msgid "DIR"
+msgstr "KATALOG"
+
+#: ../pidgin/libpidgin.c:391
+msgid "use DIR for config files"
+msgstr "używa KATALOGU dla plików konfiguracji"
+
+#: ../pidgin/libpidgin.c:393
+msgid "print debugging messages to stdout"
+msgstr "wyświetla komunikaty debugowania na standardowym wyjściu"
+
+#: ../pidgin/libpidgin.c:395
+msgid "force online, regardless of network status"
+msgstr "wymusza tryb online, niezależnie od stanu sieci"
+
+#: ../pidgin/libpidgin.c:397
+msgid "display this help and exit"
+msgstr "wyświetla tę pomoc i kończy pracę"
+
+#: ../pidgin/libpidgin.c:399
+msgid "allow multiple instances"
+msgstr "pozwala na wiele instancji"
+
+#: ../pidgin/libpidgin.c:401
+msgid "don't automatically login"
+msgstr "bez automatycznego logowania"
+
+#: ../pidgin/libpidgin.c:403
+msgid "NAME"
+msgstr "NAZWA"
+
+#: ../pidgin/libpidgin.c:404
+msgid ""
+"enable specified account(s) (optional argument NAME\n"
+" specifies account(s) to use, separated by commas.\n"
+" Without this only the first account will be enabled)."
+msgstr "włącza podane konta (opcjonalny parametr NAZWA\n podaje używane konta, oddzielone przecinkami.\n Bez tego tylko pierwsze konto zostanie włączone)."
+
+#: ../pidgin/libpidgin.c:411
+msgid "X display to use"
+msgstr "używany ekran X"
+
+#: ../pidgin/libpidgin.c:414
+msgid "display the current version and exit"
+msgstr "wyświetla bieżącą wersję i kończy pracę"
+
+#: ../pidgin/libpidgin.c:496
+#, c-format
+msgid ""
+"%s %s has segfaulted and attempted to dump a core file.\n"
+"This is a bug in the software and has happened through\n"
+"no fault of your own.\n"
+"\n"
+"If you can reproduce the crash, please notify the developers\n"
+"by reporting a bug at:\n"
+"%ssimpleticket/\n"
+"\n"
+"Please make sure to specify what you were doing at the time\n"
+"and post the backtrace from the core file. If you do not know\n"
+"how to get the backtrace, please read the instructions at\n"
+"%swiki/GetABacktrace\n"
+msgstr "Program %s %s miał awarię i próbował zrzucić plik core.\nTo jest błąd w oprogramowaniu nie powstały z winy użytkownika.\n\nJeśli można powtórzyć awarię, proszę powiadomić programistów\nzgłaszając błąd na stronie:\n%ssimpleticket/\n\nProszę upewnić się, że podano czynność wykonywaną w trakcie\nawarii oraz wysłano wyjątek z pliku core. Jeśli nie wiadomo,\njak uzyskać wyjątek, proszę przeczytać instrukcje na stronie\n%swiki/GetABacktrace\n"
+
+#: ../pidgin/libpidgin.c:777
+#, c-format
+msgid "Exiting because another libpurple client is already running.\n"
+msgstr "Kończenie pracy, ponieważ inny klient biblioteki libpurple jest już uruchomiony.\n"
#. Translators may want to transliterate the name.
#. It is not to be translated.
+#: ../pidgin/pidgin.h:51
msgid "Pidgin"
msgstr "Pidgin"
+#: ../pidgin/pidginstock.c:79
msgid "_Alias"
msgstr "_Alias"
+#: ../pidgin/pidginstock.c:81
msgid "Close _tabs"
msgstr "Zamknij _karty"
-msgid "_Get Info"
-msgstr "_Pobierz informacje"
-
+#: ../pidgin/pidginstock.c:84
msgid "_Invite"
msgstr "_Zaproś"
+#: ../pidgin/pidginstock.c:85
msgid "_Modify..."
msgstr "Zm_odyfikuj..."
-msgid "_Add..."
-msgstr "Dod_aj..."
-
+#: ../pidgin/pidginstock.c:87
msgid "_Open Mail"
msgstr "_Otwórz pocztę"
+#: ../pidgin/pidginstock.c:89
msgid "_Edit"
msgstr "_Edycja"
+#: ../pidgin/pidgintooltip.c:133
msgid "Pidgin Tooltip"
msgstr "Podpowiedź programu Pidgin"
+#: ../pidgin/pixmaps/emotes/default/24/default.theme.in.h:2
msgid "Pidgin smileys"
msgstr "Emotikony programu Pidgin"
-msgid "none"
-msgstr "brak"
-
-msgid "Selecting this disables graphical emoticons."
-msgstr "Wybranie tego motywu wyłącza graficzne emotikony."
-
+#: ../pidgin/pixmaps/emotes/small/16/small.theme.in.h:1
msgid "Small"
msgstr "Małe"
+#: ../pidgin/pixmaps/emotes/small/16/small.theme.in.h:2
msgid "Smaller versions of the default smileys"
msgstr "Mniejsze wersje domyślnych emotikon"
+#: ../pidgin/plugins/cap/cap.c:442 ../pidgin/plugins/cap/cap.c:445
msgid "Response Probability:"
msgstr "Prawdopodobieństwo odpowiedzi:"
+#: ../pidgin/plugins/cap/cap.c:769
msgid "Statistics Configuration"
msgstr "Konfiguracja statystyk"
#. msg_difference spinner
+#: ../pidgin/plugins/cap/cap.c:772
msgid "Maximum response timeout:"
msgstr "Maksymalny czas oczekiwania na odpowiedź:"
+#: ../pidgin/plugins/cap/cap.c:775 ../pidgin/plugins/cap/cap.c:782
+#: ../pidgin/plugins/cap/cap.c:789
msgid "minutes"
msgstr "minuty"
#. last_seen spinner
+#: ../pidgin/plugins/cap/cap.c:779
msgid "Maximum last-seen difference:"
msgstr "Maksymalna różnica ostatnio-widziany:"
#. threshold spinner
+#: ../pidgin/plugins/cap/cap.c:786
msgid "Threshold:"
msgstr "Próg:"
@@ -13774,47 +17521,57 @@ msgstr "Próg:"
#. *< dependencies
#. *< priority
#. *< id
+#: ../pidgin/plugins/cap/cap.c:893
msgid "Contact Availability Prediction"
msgstr "Przewidywanie dostępności kontaktu"
#. *< name
#. *< version
+#: ../pidgin/plugins/cap/cap.c:895
msgid "Contact Availability Prediction plugin."
msgstr "Wtyczka przewidywania dostępności kontaktu."
#. * summary
+#: ../pidgin/plugins/cap/cap.c:896
msgid "Displays statistical information about your buddies' availability"
msgstr "Wyświetla informacje statystyczne o dostępności znajomych"
+#: ../pidgin/plugins/contact_priority.c:62
msgid "Buddy is idle"
msgstr "Znajomy jest bezczynny"
+#: ../pidgin/plugins/contact_priority.c:63
msgid "Buddy is away"
msgstr "Znajomy zaraz wraca"
+#: ../pidgin/plugins/contact_priority.c:64
msgid "Buddy is \"extended\" away"
msgstr "Użytkownik ma stan \"wrócę później\""
#. Not used yet.
+#: ../pidgin/plugins/contact_priority.c:67
msgid "Buddy is mobile"
msgstr "Znajomy używa telefonu komórkowego"
+#: ../pidgin/plugins/contact_priority.c:69
msgid "Buddy is offline"
msgstr "Użytkownik jest w trybie offline"
+#: ../pidgin/plugins/contact_priority.c:91
msgid "Point values to use when..."
msgstr "Wskazywanie używanych wartości, kiedy..."
+#: ../pidgin/plugins/contact_priority.c:120
msgid ""
"The buddy with the <i>largest score</i> is the buddy who will have priority "
"in the contact.\n"
-msgstr ""
-"Znajomy z <i>największą ilością punktów</i> będzie traktowany priorytetowo "
-"przy nawiązywaniu kontaktu.\n"
+msgstr "Znajomy z <i>największą ilością punktów</i> będzie traktowany priorytetowo przy nawiązywaniu kontaktu.\n"
+#: ../pidgin/plugins/contact_priority.c:127
msgid "Use last buddy when scores are equal"
msgstr "Użycie ostatniego znajomego w przypadku równej ilości punktów"
+#: ../pidgin/plugins/contact_priority.c:132
msgid "Point values to use for account..."
msgstr "Wskazywanie używanej wartości dla konta..."
@@ -13824,145 +17581,128 @@ msgstr "Wskazywanie używanej wartości dla konta..."
#. *< dependencies
#. *< priority
#. *< id
+#: ../pidgin/plugins/contact_priority.c:188
msgid "Contact Priority"
msgstr "Priorytet kontaktu"
#. *< name
#. *< version
#. *< summary
+#: ../pidgin/plugins/contact_priority.c:191
msgid ""
"Allows for controlling the values associated with different buddy states."
-msgstr ""
-"Umożliwia kontrolowanie wartości powiązanych z różnymi stanami znajomych."
+msgstr "Umożliwia kontrolowanie wartości powiązanych z różnymi stanami znajomych."
#. *< description
+#: ../pidgin/plugins/contact_priority.c:193
msgid ""
-"Allows for changing the point values of idle/away/offline states for buddies "
-"in contact priority computations."
-msgstr ""
-"Umożliwia zmienianie wartości punktowych stanów bezczynny/nieobecny/offline "
-"przy obliczeniach priorytetu kontaktu."
-
-msgid "Conversation Colors"
-msgstr "Kolory rozmów"
-
-msgid "Customize colors in the conversation window"
-msgstr "Dostosowuje kolory w oknach rozmów"
-
-msgid "Error Messages"
-msgstr "Komunikaty błędów"
-
-msgid "Highlighted Messages"
-msgstr "Wyróżnione wiadomości"
-
-msgid "System Messages"
-msgstr "Komunikaty systemowe"
-
-msgid "Sent Messages"
-msgstr "Wysłane wiadomości"
-
-msgid "Received Messages"
-msgstr "Odebrane wiadomości"
-
-#, c-format
-msgid "Select Color for %s"
-msgstr "Wybór koloru dla %s"
-
-msgid "Ignore incoming format"
-msgstr "Ignorowanie formatowania w wiadomościach przychodzących"
-
-msgid "Apply in Chats"
-msgstr "Zastosowywanie w konferencjach"
-
-msgid "Apply in IMs"
-msgstr "Zastosowywanie w wiadomościach"
+"Allows for changing the point values of idle/away/offline states for buddies"
+" in contact priority computations."
+msgstr "Umożliwia zmienianie wartości punktowych stanów bezczynny/nieobecny/offline przy obliczeniach priorytetu kontaktu."
#. Note to translators: The string "Enter an XMPP Server" is asking the
#. user to type the name of an XMPP server which will then be queried
+#: ../pidgin/plugins/disco/gtkdisco.c:247
msgid "Server name request"
msgstr "Żądanie adresu serwera"
+#: ../pidgin/plugins/disco/gtkdisco.c:247
msgid "Enter an XMPP Server"
msgstr "Proszę podać serwer XMPP"
+#: ../pidgin/plugins/disco/gtkdisco.c:248
msgid "Select an XMPP server to query"
msgstr "Wybór serwera XMPP do odpytania"
+#: ../pidgin/plugins/disco/gtkdisco.c:250
msgid "Find Services"
msgstr "Znajdź usługi"
+#: ../pidgin/plugins/disco/gtkdisco.c:307
msgid "Add to Buddy List"
msgstr "Dodaj do listy znajomych"
+#: ../pidgin/plugins/disco/gtkdisco.c:481
msgid "Gateway"
msgstr "Brama"
+#: ../pidgin/plugins/disco/gtkdisco.c:485
msgid "Directory"
msgstr "Katalog"
+#: ../pidgin/plugins/disco/gtkdisco.c:493
msgid "PubSub Collection"
msgstr "Zbiór PubSub"
+#: ../pidgin/plugins/disco/gtkdisco.c:497
msgid "PubSub Leaf"
msgstr "Karta PubSub"
+#: ../pidgin/plugins/disco/gtkdisco.c:501
msgid "Other"
msgstr "Inny"
+#: ../pidgin/plugins/disco/gtkdisco.c:509
msgid ""
"\n"
"<b>Description:</b> "
-msgstr ""
-"\n"
-"<b>Opis:</b> "
+msgstr "\n<b>Opis:</b> "
#. Create the window.
+#: ../pidgin/plugins/disco/gtkdisco.c:644
msgid "Service Discovery"
msgstr "Wykrywanie usług"
+#: ../pidgin/plugins/disco/gtkdisco.c:685
msgid "_Browse"
msgstr "_Przeglądaj"
+#: ../pidgin/plugins/disco/xmppdisco.c:513
msgid "Server does not exist"
msgstr "Serwer nie istnieje"
+#: ../pidgin/plugins/disco/xmppdisco.c:518
msgid "Server does not support service discovery"
msgstr "Serwer nie obsługuje wykrywania usług"
+#: ../pidgin/plugins/disco/xmppdisco.c:598
+#: ../pidgin/plugins/disco/xmppdisco.c:657
msgid "XMPP Service Discovery"
msgstr "Wykrywanie usług XMPP"
+#: ../pidgin/plugins/disco/xmppdisco.c:659
msgid "Allows browsing and registering services."
msgstr "Umożliwia przeglądanie i rejestrowanie usług."
+#: ../pidgin/plugins/disco/xmppdisco.c:660
msgid ""
"This plugin is useful for registering with legacy transports or other XMPP "
"services."
-msgstr ""
-"Ta wtyczka jest przydatna do rejestrowania przestarzałych transportów lub "
-"innych usług XMPP."
+msgstr "Ta wtyczka jest przydatna do rejestrowania przestarzałych transportów lub innych usług XMPP."
+#: ../pidgin/plugins/extplacement.c:88
msgid "By conversation count"
msgstr "Według liczby rozmów"
+#: ../pidgin/plugins/extplacement.c:109
msgid "Conversation Placement"
msgstr "Rozmieszczanie rozmów"
-#. Translators: "New conversations" should match the text in the preferences dialog and "By conversation count" should be the same text used above
+#. Translators: "New conversations" should match the text in the preferences
+#. dialog and "By conversation count" should be the same text used above
+#: ../pidgin/plugins/extplacement.c:113
msgid ""
"Note: The preference for \"New conversations\" must be set to \"By "
"conversation count\"."
-msgstr ""
-"Uwaga: preferencja dla \"Nowe rozmowy\" musi zostać ustawiona na \"Według "
-"liczby rozmów\"."
+msgstr "Uwaga: preferencja dla \"Nowe rozmowy\" musi zostać ustawiona na \"Według liczby rozmów\"."
+#: ../pidgin/plugins/extplacement.c:119
msgid "Number of conversations per window"
msgstr "Liczba rozmów w jednym oknie"
+#: ../pidgin/plugins/extplacement.c:125
msgid "Separate IM and Chat windows when placing by number"
-msgstr ""
-"Oddzielanie okien wiadomości i konferencji podczas rozmieszczania według "
-"liczby"
+msgstr "Oddzielanie okien wiadomości i konferencji podczas rozmieszczania według liczby"
#. *< type
#. *< ui_requirement
@@ -13970,34 +17710,39 @@ msgstr ""
#. *< dependencies
#. *< priority
#. *< id
+#: ../pidgin/plugins/extplacement.c:153
msgid "ExtPlacement"
msgstr "Rozszerzone rozmieszczanie"
#. *< name
#. *< version
+#: ../pidgin/plugins/extplacement.c:155
msgid "Extra conversation placement options."
msgstr "Dodatkowe opcje rozmieszczania rozmów."
#. *< summary
#. * description
+#: ../pidgin/plugins/extplacement.c:157
msgid ""
"Restrict the number of conversations per windows, optionally separating IMs "
"and Chats"
-msgstr ""
-"Ogranicza liczbę rozmów w jednym oknie, opcjonalnie oddzielając wiadomości "
-"od konferencji"
+msgstr "Ogranicza liczbę rozmów w jednym oknie, opcjonalnie oddzielając wiadomości od konferencji"
#. Configuration frame
+#: ../pidgin/plugins/gestures/gestures.c:231
msgid "Mouse Gestures Configuration"
msgstr "Konfiguracja gestów myszy"
+#: ../pidgin/plugins/gestures/gestures.c:237
msgid "Middle mouse button"
msgstr "Środkowy przycisk myszy"
+#: ../pidgin/plugins/gestures/gestures.c:238
msgid "Right mouse button"
msgstr "Prawy przycisk myszy"
#. "Visual gesture display" checkbox
+#: ../pidgin/plugins/gestures/gestures.c:248
msgid "_Visual gesture display"
msgstr "_Wyświetlanie gestów"
@@ -14007,88 +17752,94 @@ msgstr "_Wyświetlanie gestów"
#. *< dependencies
#. *< priority
#. *< id
+#: ../pidgin/plugins/gestures/gestures.c:283
msgid "Mouse Gestures"
msgstr "Gesty myszy"
#. *< name
#. *< version
#. * summary
+#: ../pidgin/plugins/gestures/gestures.c:286
msgid "Provides support for mouse gestures"
msgstr "Dostarcza obsługę gestów myszy"
#. * description
+#: ../pidgin/plugins/gestures/gestures.c:288
msgid ""
-"Allows support for mouse gestures in conversation windows. Drag the middle "
-"mouse button to perform certain actions:\n"
+"Allows support for mouse gestures in conversation windows. Drag the middle mouse button to perform certain actions:\n"
" • Drag down and then to the right to close a conversation.\n"
" • Drag up and then to the left to switch to the previous conversation.\n"
" • Drag up and then to the right to switch to the next conversation."
-msgstr ""
-"Umożliwia obsługę gestami myszy w oknach rozmów. Przesunięcie myszy z "
-"wciśniętym środkowym przyciskiem wykona następujące czynności:\n"
-" • W dół, a następnie w prawo zamyka rozmowę.\n"
-" • W górę, a następnie w lewo przełącza na poprzednią rozmowę.\n"
-" • W górę, a następnie w prawo przełącza na następną rozmowę."
+msgstr "Umożliwia obsługę gestami myszy w oknach rozmów. Przesunięcie myszy z wciśniętym środkowym przyciskiem wykona następujące czynności:\n • W dół, a następnie w prawo zamyka rozmowę.\n • W górę, a następnie w lewo przełącza na poprzednią rozmowę.\n • W górę, a następnie w prawo przełącza na następną rozmowę."
+#: ../pidgin/plugins/gevolution/add_buddy_dialog.c:144
msgid "Instant Messaging"
msgstr "Komunikator"
#. Add the label.
+#: ../pidgin/plugins/gevolution/add_buddy_dialog.c:463
msgid "Select a person from your address book below, or add a new person."
msgstr "Proszę wybrać osobę z poniższej książki adresowej lub dodać nową."
+#: ../pidgin/plugins/gevolution/add_buddy_dialog.c:547
+#: ../pidgin/plugins/gevolution/new_person_dialog.c:301
msgid "Group:"
msgstr "Grupa:"
#. "New Person" button
+#: ../pidgin/plugins/gevolution/add_buddy_dialog.c:566
+#: ../pidgin/plugins/gevolution/assoc-buddy.c:435
+#: ../pidgin/plugins/gevolution/new_person_dialog.c:248
msgid "New Person"
msgstr "Nowa osoba"
#. "Select Buddy" button
+#: ../pidgin/plugins/gevolution/add_buddy_dialog.c:583
msgid "Select Buddy"
msgstr "Wybierz znajomego"
#. Add the label.
+#: ../pidgin/plugins/gevolution/assoc-buddy.c:343
msgid ""
-"Select a person from your address book to add this buddy to, or create a new "
-"person."
-msgstr ""
-"Proszę wybrać osobę z książki adresowej, do której dodać tego znajomego lub "
-"utworzyć nową osobę."
-
-#. Add the expander
-msgid "User _details"
-msgstr "_Szczegóły użytkownika"
+"Select a person from your address book to add this buddy to, or create a new"
+" person."
+msgstr "Proszę wybrać osobę z książki adresowej, do której dodać tego znajomego lub utworzyć nową osobę."
#. "Associate Buddy" button
+#: ../pidgin/plugins/gevolution/assoc-buddy.c:452
msgid "_Associate Buddy"
msgstr "Powiąż zn_ajomego"
+#: ../pidgin/plugins/gevolution/gevolution.c:256
+#: ../pidgin/plugins/gevolution/gevolution.c:263
msgid "Unable to send email"
msgstr "Nie można wysłać wiadomości e-mail"
+#: ../pidgin/plugins/gevolution/gevolution.c:257
msgid "The evolution executable was not found in the PATH."
-msgstr ""
-"Plik wykonywalny programu Evolution nie został odnaleziony w zmiennej PATH."
+msgstr "Plik wykonywalny programu Evolution nie został odnaleziony w zmiennej PATH."
+#: ../pidgin/plugins/gevolution/gevolution.c:264
msgid "An email address was not found for this buddy."
msgstr "Nie odnaleziono adresu e-mail dla tego znajomego."
+#: ../pidgin/plugins/gevolution/gevolution.c:290
msgid "Add to Address Book"
msgstr "Dodaj do książki adresowej"
+#: ../pidgin/plugins/gevolution/gevolution.c:302
msgid "Send Email"
msgstr "Wyślij wiadomość e-mail"
#. Configuration frame
+#: ../pidgin/plugins/gevolution/gevolution.c:438
msgid "Evolution Integration Configuration"
msgstr "Konfiguracja integracji z programem Evolution"
#. Label
+#: ../pidgin/plugins/gevolution/gevolution.c:441
msgid "Select all accounts that buddies should be auto-added to."
-msgstr ""
-"Proszę wybrać wszystkie konta, do których znajomi powinni być automatycznie "
-"dodawani."
+msgstr "Proszę wybrać wszystkie konta, do których znajomi powinni być automatycznie dodawani."
#. *< type
#. *< ui_requirement
@@ -14096,6 +17847,7 @@ msgstr ""
#. *< dependencies
#. *< priority
#. *< id
+#: ../pidgin/plugins/gevolution/gevolution.c:543
msgid "Evolution Integration"
msgstr "Integracja z programem Evolution"
@@ -14103,28 +17855,37 @@ msgstr "Integracja z programem Evolution"
#. *< version
#. * summary
#. * description
+#: ../pidgin/plugins/gevolution/gevolution.c:546
+#: ../pidgin/plugins/gevolution/gevolution.c:548
msgid "Provides integration with Evolution."
msgstr "Dostarcza integrację z programem Evolution."
+#: ../pidgin/plugins/gevolution/new_person_dialog.c:262
msgid "Please enter the person's information below."
msgstr "Proszę poniżej podać informacje o osobie."
+#: ../pidgin/plugins/gevolution/new_person_dialog.c:266
msgid "Please enter the buddy's username and account type below."
msgstr "Proszę poniżej podać nazwę znajomego i typ konta."
+#: ../pidgin/plugins/gevolution/new_person_dialog.c:286
msgid "Account type:"
msgstr "Typ konta:"
#. Optional Information section
+#: ../pidgin/plugins/gevolution/new_person_dialog.c:310
msgid "Optional information:"
msgstr "Opcjonalne informacje:"
+#: ../pidgin/plugins/gevolution/new_person_dialog.c:345
msgid "First name:"
msgstr "Imię:"
+#: ../pidgin/plugins/gevolution/new_person_dialog.c:357
msgid "Last name:"
msgstr "Nazwisko:"
+#: ../pidgin/plugins/gevolution/new_person_dialog.c:377
msgid "Email:"
msgstr "Adres e-mail:"
@@ -14134,6 +17895,7 @@ msgstr "Adres e-mail:"
#. *< dependencies
#. *< priority
#. *< id
+#: ../pidgin/plugins/gtk-signals-test.c:161
msgid "GTK Signals Test"
msgstr "Test sygnałów GTK+"
@@ -14141,18 +17903,19 @@ msgstr "Test sygnałów GTK+"
#. *< version
#. * summary
#. * description
+#: ../pidgin/plugins/gtk-signals-test.c:164
+#: ../pidgin/plugins/gtk-signals-test.c:166
msgid "Test to see that all ui signals are working properly."
-msgstr ""
-"Test sprawdzający, czy wszystkie sygnały interfejsu działają poprawnie."
+msgstr "Test sprawdzający, czy wszystkie sygnały interfejsu działają poprawnie."
+#: ../pidgin/plugins/gtkbuddynote.c:38
#, c-format
msgid ""
"\n"
"<b>Buddy Note</b>: %s"
-msgstr ""
-"\n"
-"<b>Notatka o znajomym</b>: %s"
+msgstr "\n<b>Notatka o znajomym</b>: %s"
+#: ../pidgin/plugins/history.c:221
msgid "History"
msgstr "Historia"
@@ -14162,6 +17925,7 @@ msgstr "Historia"
#. *< dependencies
#. *< priority
#. *< id
+#: ../pidgin/plugins/iconaway.c:82
msgid "Iconify on Away"
msgstr "Minimalizacja do ikony podczas nieobecności"
@@ -14169,70 +17933,107 @@ msgstr "Minimalizacja do ikony podczas nieobecności"
#. *< version
#. * summary
#. * description
+#: ../pidgin/plugins/iconaway.c:85 ../pidgin/plugins/iconaway.c:87
msgid "Iconifies the buddy list and your conversations when you go away."
msgstr "Minimalizuje listę znajomych i rozmowy podczas nieobecności."
+#: ../pidgin/plugins/imgupload.c:226
+msgid "Uploading image"
+msgstr "Wysyłanie obrazu"
+
+#: ../pidgin/plugins/imgupload.c:227
+msgid "Please wait for image URL being retrieved..."
+msgstr "Proszę czekać na pobranie adresu URL obrazu..."
+
+#: ../pidgin/plugins/imgupload.c:415
+msgid "Use image filename as link description"
+msgstr "Użycie nazwy pliku obrazu jako opis odnośnika"
+
+#: ../pidgin/plugins/imgupload.c:423 ../pidgin/plugins/imgupload.c:454
+msgid "Image Uploader"
+msgstr "Wysyłanie obrazów"
+
+#: ../pidgin/plugins/imgupload.c:456
+msgid "Inline images implementation for protocols without such feature."
+msgstr "Implementacja obrazów w wiadomościach dla protokołów bez takiej funkcji."
+
+#: ../pidgin/plugins/imgupload.c:457
+msgid ""
+"Adds inline images support for protocols lacking this feature by uploading "
+"them to the external service."
+msgstr "Dodaje obsługę obrazów w wiadomościach dla protokołów bez takiej funkcji przez wysyłanie ich do zewnętrznej usługi."
+
+#: ../pidgin/plugins/mailchk.c:160
msgid "Mail Checker"
msgstr "Sprawdzanie poczty"
+#: ../pidgin/plugins/mailchk.c:162
msgid "Checks for new local mail."
msgstr "Sprawdza, czy jest nowa lokalna poczta."
+#: ../pidgin/plugins/mailchk.c:163
msgid "Adds a small box to the buddy list that shows if you have new mail."
-msgstr ""
-"Dodaje do listy znajomych mały wskaźnik wyświetlający informacje o nowej "
-"poczcie."
+msgstr "Dodaje do listy znajomych mały wskaźnik wyświetlający informacje o nowej poczcie."
+#: ../pidgin/plugins/markerline.c:23
msgid "Markerline"
msgstr "Odkreślenie"
+#: ../pidgin/plugins/markerline.c:25 ../pidgin/plugins/markerline.c:26
msgid "Draw a line to indicate new messages in a conversation."
msgstr "Rysuje linię odkreślającą nowe wiadomości w rozmowie."
+#: ../pidgin/plugins/markerline.c:169
msgid "Jump to markerline"
msgstr "Przejdź do odkreślenia"
+#: ../pidgin/plugins/markerline.c:203
msgid "Draw Markerline in "
msgstr "Rysowanie odkreślenia w "
+#: ../pidgin/plugins/markerline.c:207 ../pidgin/plugins/notify.c:686
msgid "_IM windows"
msgstr "Oknach w_iadomości"
+#: ../pidgin/plugins/markerline.c:211 ../pidgin/plugins/notify.c:693
msgid "C_hat windows"
msgstr "Oknach _konferencji"
+#: ../pidgin/plugins/musicmessaging/musicmessaging.c:45
msgid ""
"A music messaging session has been requested. Please click the MM icon to "
"accept."
-msgstr ""
-"Poproszono o sesję wiadomości muzycznych. Proszę nacisnąć ikonę MM, aby "
-"zaakceptować."
+msgstr "Poproszono o sesję wiadomości muzycznych. Proszę nacisnąć ikonę MM, aby zaakceptować."
+#: ../pidgin/plugins/musicmessaging/musicmessaging.c:46
msgid "Music messaging session confirmed."
msgstr "Potwierdzono sesję wiadomości muzycznych."
+#: ../pidgin/plugins/musicmessaging/musicmessaging.c:452
msgid "Music Messaging"
msgstr "Wiadomości muzyczne"
+#: ../pidgin/plugins/musicmessaging/musicmessaging.c:453
msgid "There was a conflict in running the command:"
msgstr "Wystąpił konflikt podczas wykonywania polecenia:"
+#: ../pidgin/plugins/musicmessaging/musicmessaging.c:563
msgid "Error Running Editor"
msgstr "Błąd podczas uruchamiania edytora"
+#: ../pidgin/plugins/musicmessaging/musicmessaging.c:564
msgid "The following error has occurred:"
msgstr "Wystąpił następujący błąd:"
#. Configuration frame
+#: ../pidgin/plugins/musicmessaging/musicmessaging.c:672
msgid "Music Messaging Configuration"
msgstr "Konfiguracja wiadomości muzycznych"
+#: ../pidgin/plugins/musicmessaging/musicmessaging.c:676
msgid "Score Editor Path"
msgstr "Ścieżka do edytora nut"
-msgid "_Apply"
-msgstr "Z_astosuj"
-
#. *< type
#. *< ui_requirement
#. *< flags
@@ -14241,78 +18042,95 @@ msgstr "Z_astosuj"
#. *< id
#. *< name
#. *< version
+#: ../pidgin/plugins/musicmessaging/musicmessaging.c:717
msgid "Music Messaging Plugin for collaborative composition."
msgstr "Wtyczka do grupowego komponowania utworów muzycznych."
#. * summary
+#: ../pidgin/plugins/musicmessaging/musicmessaging.c:719
msgid ""
"The Music Messaging Plugin allows a number of users to simultaneously work "
"on a piece of music by editing a common score in real-time."
-msgstr ""
-"Wtyczka wiadomości muzycznych umożliwia wielu użytkownikom na jednoczesną "
-"pracę nad utworem muzycznym przez modyfikowanie nut w czasie rzeczywistym."
+msgstr "Wtyczka wiadomości muzycznych umożliwia wielu użytkownikom na jednoczesną pracę nad utworem muzycznym przez modyfikowanie nut w czasie rzeczywistym."
#. ---------- "Notify For" ----------
+#: ../pidgin/plugins/notify.c:682
msgid "Notify For"
msgstr "Powiadamianie dla"
+#: ../pidgin/plugins/notify.c:701
msgid "\t_Only when someone says your username"
msgstr "\tTylk_o, kiedy ktoś wymawia nazwę użytkownika"
+#: ../pidgin/plugins/notify.c:711
msgid "_Focused windows"
msgstr "A_ktywnych oknach"
#. ---------- "Notification Methods" ----------
+#: ../pidgin/plugins/notify.c:719
msgid "Notification Methods"
msgstr "Metody powiadamiania"
+#: ../pidgin/plugins/notify.c:726
msgid "Prepend _string into window title:"
msgstr "Poprzedzanie tytułu okna napi_sem:"
#. Count method button
+#: ../pidgin/plugins/notify.c:745
msgid "Insert c_ount of new messages into window title"
msgstr "Wstawianie liczby nowych wiad_omości do tytułu okna"
#. Count xprop method button
+#: ../pidgin/plugins/notify.c:754
msgid "Insert count of new message into _X property"
msgstr "Wstawianie liczby nowych wiadomości do właściwości _X"
#. Urgent method button
+#: ../pidgin/plugins/notify.c:762
msgid "Set window manager \"_URGENT\" hint"
msgstr "_Ustawienie podpowiedzi \"PILNY\" menedżera okien"
+#: ../pidgin/plugins/notify.c:764
msgid "_Flash window"
msgstr "_Miganie okna"
#. Raise window method button
+#: ../pidgin/plugins/notify.c:773
msgid "R_aise conversation window"
msgstr "Przenoszenie okn_a rozmowy na wierzch"
#. Present conversation method button
+#: ../pidgin/plugins/notify.c:781
msgid "_Present conversation window"
msgstr "_Wyświetlanie okna rozmowy"
#. ---------- "Notification Removals" ----------
+#: ../pidgin/plugins/notify.c:789
msgid "Notification Removal"
msgstr "Usunięcie powiadomień"
#. Remove on focus button
+#: ../pidgin/plugins/notify.c:794
msgid "Remove when conversation window _gains focus"
msgstr "Usunięcie po a_ktywacji okna rozmowy"
#. Remove on click button
+#: ../pidgin/plugins/notify.c:801
msgid "Remove when conversation window _receives click"
msgstr "Usunięcie po _naciśnięciu okna rozmowy"
#. Remove on type button
+#: ../pidgin/plugins/notify.c:809
msgid "Remove when _typing in conversation window"
msgstr "Usunięcie po wpisaniu _tekstu w oknie rozmowy"
#. Remove on message send button
+#: ../pidgin/plugins/notify.c:817
msgid "Remove when a _message gets sent"
msgstr "Usunięcie po wysłaniu wiado_mości"
#. Remove on conversation switch button
+#: ../pidgin/plugins/notify.c:826
msgid "Remove on switch to conversation ta_b"
msgstr "Usunięcie po przełączeniu na _kartę rozmowy"
@@ -14322,6 +18140,7 @@ msgstr "Usunięcie po przełączeniu na _kartę rozmowy"
#. *< dependencies
#. *< priority
#. *< id
+#: ../pidgin/plugins/notify.c:918
msgid "Message Notification"
msgstr "Powiadamianie o wiadomościach"
@@ -14329,10 +18148,9 @@ msgstr "Powiadamianie o wiadomościach"
#. *< version
#. * summary
#. * description
+#: ../pidgin/plugins/notify.c:921 ../pidgin/plugins/notify.c:923
msgid "Provides a variety of ways of notifying you of unread messages."
-msgstr ""
-"Dostarcza wiele sposobów powiadomienia użytkownika o nieprzeczytanych "
-"wiadomościach."
+msgstr "Dostarcza wiele sposobów powiadomienia użytkownika o nieprzeczytanych wiadomościach."
#. *< type
#. *< ui_requirement
@@ -14340,126 +18158,54 @@ msgstr ""
#. *< dependencies
#. *< priority
#. *< id
+#: ../pidgin/plugins/pidgininc.c:91
msgid "Pidgin Demonstration Plugin"
msgstr "Wtyczka demonstracyjna programu Pidgin"
#. *< name
#. *< version
#. * summary
+#: ../pidgin/plugins/pidgininc.c:94
msgid "An example plugin that does stuff - see the description."
msgstr "Przykładowa wtyczka, która coś robi - proszę zobaczyć opis."
#. * description
+#: ../pidgin/plugins/pidgininc.c:96
msgid ""
"This is a really cool plugin that does a lot of stuff:\n"
"- It tells you who wrote the program when you log in\n"
"- It reverses all incoming text\n"
"- It sends a message to people on your list immediately when they sign on"
-msgstr ""
-"To jest naprawdę doskonała wtyczka, która wiele robi:\n"
-"- Oznajmia, kto napisał program podczas logowania\n"
-"- Przepisuje przychodzący tekst od tyłu\n"
-"- Wysyła wiadomość do osób na liście zaraz po ich zalogowaniu"
-
-msgid "Hyperlink Color"
-msgstr "Kolor odnośnika"
-
-msgid "Visited Hyperlink Color"
-msgstr "Kolor odwiedzonego odnośnika"
-
-msgid "Highlighted Message Name Color"
-msgstr "Kolor nazwy wyróżnionej wiadomości"
-
-msgid "Typing Notification Color"
-msgstr "Kolor powiadomienia o pisaniu"
-
-msgid "GtkTreeView Horizontal Separation"
-msgstr "Poziome odstępy w widgecie GtkTreeView"
-
-msgid "Conversation Entry"
-msgstr "Wpis rozmowy"
-
-msgid "Conversation History"
-msgstr "Historia rozmowy"
-
-msgid "Request Dialog"
-msgstr "Okno dialogowe żądania"
-
-msgid "Notify Dialog"
-msgstr "Okno dialogowe powiadomienia"
-
-msgid "Select Color"
-msgstr "Wybór koloru"
-
-#, c-format
-msgid "Select Interface Font"
-msgstr "Wybór czcionki interfejsu"
-
-#, c-format
-msgid "Select Font for %s"
-msgstr "Wybór czcionki dla %s"
-
-msgid "GTK+ Interface Font"
-msgstr "Czcionka interfejsu GTK+"
-
-msgid "GTK+ Text Shortcut Theme"
-msgstr "Motyw skrótów tekstu GTK+"
-
-msgid "Disable Typing Notification Text"
-msgstr "Wyłączenie tekstu powiadamiania o pisaniu"
-
-msgid "GTK+ Theme Control Settings"
-msgstr "Ustawienia kontroli motywów GTK+"
-
-msgid "Colors"
-msgstr "Kolory"
-
-msgid "Fonts"
-msgstr "Czcionki"
-
-msgid "Miscellaneous"
-msgstr "Różne"
-
-msgid "Gtkrc File Tools"
-msgstr "Narzędzia plików gtkrc"
-
-#, c-format
-msgid "Write settings to %s%sgtkrc-2.0"
-msgstr "Zapisz ustawienia do pliku %s%sgtkrc-2.0"
-
-msgid "Re-read gtkrc files"
-msgstr "Ponownie odczytaj pliki gtkrc"
-
-msgid "Pidgin GTK+ Theme Control"
-msgstr "Kontrola motywów GTK+ w programie Pidgin"
-
-msgid "Provides access to commonly used gtkrc settings."
-msgstr "Dostarcza dostęp do często używanych ustawień gtkrc."
+msgstr "To jest naprawdę doskonała wtyczka, która wiele robi:\n- Oznajmia, kto napisał program podczas logowania\n- Przepisuje przychodzący tekst od tyłu\n- Wysyła wiadomość do osób na liście zaraz po ich zalogowaniu"
+#: ../pidgin/plugins/raw.c:176
msgid "Raw"
msgstr "Surowy"
+#: ../pidgin/plugins/raw.c:178
msgid "Lets you send raw input to text-based protocols."
msgstr "Umożliwia wysyłanie surowego tekstu w protokołach tekstowych."
+#: ../pidgin/plugins/raw.c:179
msgid ""
"Lets you send raw input to text-based protocols (XMPP, MSN, IRC, TOC). Hit "
"'Enter' in the entry box to send. Watch the debug window."
-msgstr ""
-"Umożliwia wysyłanie surowego tekstu w protokołach tekstowych (XMPP, MSN, "
-"IRC, TOC). Naciśnięcie przycisku \"Enter\" w polu wprowadzania tekstu wysyła "
-"go. Należy obserwować okno debugowania."
+msgstr "Umożliwia wysyłanie surowego tekstu w protokołach tekstowych (XMPP, MSN, IRC, TOC). Naciśnięcie przycisku \"Enter\" w polu wprowadzania tekstu wysyła go. Należy obserwować okno debugowania."
+#: ../pidgin/plugins/relnot.c:90
#, c-format
msgid "You can upgrade to %s %s today."
msgstr "Można zaktualizować do %s %s."
+#: ../pidgin/plugins/relnot.c:95
msgid "New Version Available"
msgstr "Dostępna jest nowa wersja"
+#: ../pidgin/plugins/relnot.c:98
msgid "Later"
msgstr "Później"
+#: ../pidgin/plugins/relnot.c:99
msgid "Download Now"
msgstr "Pobierz teraz"
@@ -14469,22 +18215,62 @@ msgstr "Pobierz teraz"
#. *< dependencies
#. *< priority
#. *< id
+#: ../pidgin/plugins/relnot.c:168
msgid "Release Notification"
msgstr "Powiadomienie o wydaniu"
#. *< name
#. *< version
#. * summary
+#: ../pidgin/plugins/relnot.c:171
msgid "Checks periodically for new releases."
msgstr "Sprawdza okresowo nowe wydania."
#. * description
+#: ../pidgin/plugins/relnot.c:173
msgid ""
"Checks periodically for new releases and notifies the user with the "
"ChangeLog."
-msgstr ""
-"Sprawdza okresowo nowe wydania i powiadamia użytkownika wyświetlając listę "
-"zmian."
+msgstr "Sprawdza okresowo nowe wydania i powiadamia użytkownika wyświetlając listę zmian."
+
+#. translators: this is the file name prefix,
+#. * keep it lowercase and pure ASCII.
+#. * Please avoid "_" character, use "-" instead.
+#: ../pidgin/plugins/screencap.c:326
+msgid "screenshot-"
+msgstr "zrzut-ekranu-"
+
+#: ../pidgin/plugins/screencap.c:367 ../pidgin/plugins/screencap.c:845
+msgid "Insert screenshot"
+msgstr "Wstaw zrzut ekranu"
+
+#: ../pidgin/plugins/screencap.c:664
+msgid ""
+"Select the region to send and press Enter button to confirm or press Escape "
+"button to cancel"
+msgstr "Należy wybrać obszar do wysłania i nacisnąć klawisz Enter, aby potwierdzić lub klawisz Escape, aby anulować"
+
+#: ../pidgin/plugins/screencap.c:755
+msgid "Insert Screens_hot..."
+msgstr "Wstaw zrzut _ekranu..."
+
+#: ../pidgin/plugins/screencap.c:844
+msgid "_Screenshot"
+msgstr "_Zrzut ekranu"
+
+#: ../pidgin/plugins/screencap.c:1018
+msgid "Screen Capture"
+msgstr "Zrzuty ekranu"
+
+#: ../pidgin/plugins/screencap.c:1020
+msgid "Send screenshots to your buddies."
+msgstr "Wysyłanie zrzutów ekranu do znajomych."
+
+#: ../pidgin/plugins/screencap.c:1021
+msgid ""
+"Adds an option to send a screenshot as an inline image. It works only with "
+"protocols that supports inline images."
+msgstr "Dodaje opcję wysyłania zrzutu ekranu jako obraz w wiadomości. Działa tylko z protokołami obsługującymi przesyłanie obrazów."
#. *< major version
#. *< minor version
@@ -14494,148 +18280,186 @@ msgstr ""
#. *< dependencies
#. *< priority
#. *< id
+#: ../pidgin/plugins/sendbutton.c:176
msgid "Send Button"
msgstr "Przycisk Wyślij"
#. *< name
#. *< version
+#: ../pidgin/plugins/sendbutton.c:178
msgid "Conversation Window Send Button."
msgstr "Przycisk Wyślij w oknie rozmowy."
#. *< summary
+#: ../pidgin/plugins/sendbutton.c:179
msgid ""
"Adds a Send button to the entry area of the conversation window. Intended "
"for use when no physical keyboard is present."
-msgstr ""
-"Dodaje przycisk Wyślij do obszaru wprowadzania okna rozmowy. Przeznaczone do "
-"używania w przpadkach, kiedy nie ma fizycznej klawiatury."
+msgstr "Dodaje przycisk Wyślij do obszaru wprowadzania okna rozmowy. Przeznaczone do używania w przpadkach, kiedy nie ma fizycznej klawiatury."
+#: ../pidgin/plugins/spellchk.c:1975
msgid "Duplicate Correction"
msgstr "Poprawianie duplikatów"
+#: ../pidgin/plugins/spellchk.c:1976
msgid "The specified word already exists in the correction list."
msgstr "Podany wyraz już istnieje na liście poprawianych wyrazów."
+#: ../pidgin/plugins/spellchk.c:2176
msgid "Text Replacements"
msgstr "Zastępowanie tekstu"
+#: ../pidgin/plugins/spellchk.c:2190
msgid "You type"
msgstr "Wpisane"
+#: ../pidgin/plugins/spellchk.c:2204
msgid "You send"
msgstr "Wysłane"
+#: ../pidgin/plugins/spellchk.c:2218
msgid "Whole words only"
msgstr "Tylko całe wyrazy"
+#: ../pidgin/plugins/spellchk.c:2230
msgid "Case sensitive"
msgstr "Rozróżnianie małych i wielkich liter"
+#: ../pidgin/plugins/spellchk.c:2258
msgid "Add a new text replacement"
msgstr "Dodaj nowe zastąpienie tekstu"
+#: ../pidgin/plugins/spellchk.c:2274
msgid "You _type:"
msgstr "_Wpisane:"
+#: ../pidgin/plugins/spellchk.c:2278
msgid "You _send:"
msgstr "Wy_słane:"
#. Created here so it can be passed to whole_words_button_toggled.
+#: ../pidgin/plugins/spellchk.c:2281
msgid "_Exact case match (uncheck for automatic case handling)"
-msgstr ""
-"Dokładn_e rozróżnianie małych i wielkich liter (odznaczenie powoduje "
-"automatyczne obsługiwanie wielkości liter)"
+msgstr "Dokładn_e rozróżnianie małych i wielkich liter (odznaczenie powoduje automatyczne obsługiwanie wielkości liter)"
+#: ../pidgin/plugins/spellchk.c:2283
msgid "Only replace _whole words"
msgstr "Zastępowanie tylko całych _wyrazów"
+#: ../pidgin/plugins/spellchk.c:2308
msgid "General Text Replacement Options"
msgstr "Ogólne opcje zastępowania tekstu"
+#: ../pidgin/plugins/spellchk.c:2309
msgid "Enable replacement of last word on send"
msgstr "Włączenie zastępowania ostatniego wyrazu podczas wysyłania"
+#: ../pidgin/plugins/spellchk.c:2341
msgid "Text replacement"
msgstr "Zastępowanie tekstu"
+#: ../pidgin/plugins/spellchk.c:2343 ../pidgin/plugins/spellchk.c:2344
msgid "Replaces text in outgoing messages according to user-defined rules."
-msgstr ""
-"Zastępuje tekst w wychodzących wiadomościach zgodnie z regułami określonymi "
-"przez użytkownika."
+msgstr "Zastępuje tekst w wychodzących wiadomościach zgodnie z regułami określonymi przez użytkownika."
+#: ../pidgin/plugins/themeedit-icon.c:69
msgid "Just logged in"
msgstr "Właśnie zalogowano"
+#: ../pidgin/plugins/themeedit-icon.c:70
msgid "Just logged out"
msgstr "Właśnie wylogowano"
+#: ../pidgin/plugins/themeedit-icon.c:71
msgid ""
"Icon for Contact/\n"
"Icon for Unknown person"
-msgstr ""
-"Ikona kontaktu/\n"
-"Ikona nieznanej osoby"
+msgstr "Ikona kontaktu/\nIkona nieznanej osoby"
+#: ../pidgin/plugins/themeedit-icon.c:72
msgid "Icon for Chat"
msgstr "Ikona konferencji"
+#: ../pidgin/plugins/themeedit-icon.c:75
msgid "Ignored"
msgstr "Ignorowany"
+#: ../pidgin/plugins/themeedit-icon.c:76
msgid "Founder"
msgstr "Założyciel"
#. A user in a chat room who has special privileges.
+#: ../pidgin/plugins/themeedit-icon.c:78
msgid "Operator"
msgstr "Operator"
#. A half operator is someone who has a subset of the privileges
#. that an operator has.
+#: ../pidgin/plugins/themeedit-icon.c:81
msgid "Half Operator"
msgstr "Zastępca operatora"
+#: ../pidgin/plugins/themeedit-icon.c:85
msgid "Authorization dialog"
msgstr "Okno dialogowe upoważnienia"
+#: ../pidgin/plugins/themeedit-icon.c:86
msgid "Error dialog"
msgstr "Okno dialogowe błędu"
+#: ../pidgin/plugins/themeedit-icon.c:87
msgid "Information dialog"
msgstr "Okno dialogowe informacji"
+#: ../pidgin/plugins/themeedit-icon.c:88
msgid "Mail dialog"
msgstr "Okno dialogowe poczty"
+#: ../pidgin/plugins/themeedit-icon.c:89
msgid "Question dialog"
msgstr "Okno dialogowe pytania"
+#: ../pidgin/plugins/themeedit-icon.c:90
msgid "Warning dialog"
msgstr "Okno dialogowe ostrzeżenia"
+#: ../pidgin/plugins/themeedit-icon.c:92
msgid "What kind of dialog is this?"
msgstr "Jaki to rodzaj okna dialogowego?"
+#: ../pidgin/plugins/themeedit-icon.c:100
msgid "Status Icons"
msgstr "Ikony stanu"
+#: ../pidgin/plugins/themeedit-icon.c:101
msgid "Chatroom Emblems"
msgstr "Emblematy konferencji"
+#: ../pidgin/plugins/themeedit-icon.c:102
msgid "Dialog Icons"
msgstr "Ikony okna dialogowego"
+#: ../pidgin/plugins/themeedit-icon.c:265
msgid "Pidgin Icon Theme Editor"
msgstr "Edytor motywów ikon programu Pidgin"
+#: ../pidgin/plugins/themeedit.c:144
+msgid "Select Color"
+msgstr "Wybór koloru"
+
+#: ../pidgin/plugins/themeedit.c:212
msgid "Contact"
msgstr "Kontakt"
+#: ../pidgin/plugins/themeedit.c:236
msgid "Pidgin Buddylist Theme Editor"
msgstr "Edytor motywu listy znajomych programu Pidgin"
+#: ../pidgin/plugins/themeedit.c:301
msgid "Edit Buddylist Theme"
msgstr "Zmodyfikuj motyw listy znajomych"
+#: ../pidgin/plugins/themeedit.c:303
msgid "Edit Icon Theme"
msgstr "Zmodyfikuj motyw ikon"
@@ -14646,12 +18470,14 @@ msgstr "Zmodyfikuj motyw ikon"
#. *< priority
#. *< id
#. * description
+#: ../pidgin/plugins/themeedit.c:321 ../pidgin/plugins/themeedit.c:326
msgid "Pidgin Theme Editor"
msgstr "Edytor motywów programu Pidgin"
#. *< name
#. *< version
#. * summary
+#: ../pidgin/plugins/themeedit.c:324
msgid "Pidgin Theme Editor."
msgstr "Edytor motywów programu Pidgin."
@@ -14661,6 +18487,7 @@ msgstr "Edytor motywów programu Pidgin."
#. *< dependencies
#. *< priority
#. *< id
+#: ../pidgin/plugins/ticker/ticker.c:76 ../pidgin/plugins/ticker/ticker.c:372
msgid "Buddy Ticker"
msgstr "Przewijana lista znajomych"
@@ -14668,116 +18495,48 @@ msgstr "Przewijana lista znajomych"
#. *< version
#. * summary
#. * description
+#: ../pidgin/plugins/ticker/ticker.c:375 ../pidgin/plugins/ticker/ticker.c:377
msgid "A horizontal scrolling version of the buddy list."
msgstr "Lista znajomych przesuwająca się w poziomie."
-msgid "Display Timestamps Every"
-msgstr "Wyświetlanie daty co"
-
-#. *< type
-#. *< ui_requirement
-#. *< flags
-#. *< dependencies
-#. *< priority
-#. *< id
-msgid "Timestamp"
-msgstr "Datownik"
-
-#. *< name
-#. *< version
-#. * summary
-msgid "Display iChat-style timestamps"
-msgstr "Wyświetlenie daty w stylu programu iChat"
-
-#. * description
-msgid "Display iChat-style timestamps every N minutes."
-msgstr "Wyświetlenie daty w stylu programu iChat do rozmowy co N minut."
-
-msgid "Timestamp Format Options"
-msgstr "Opcje formatu daty"
-
-msgid "_Force timestamp format:"
-msgstr "_Wymuszenie formatu daty i czasu:"
-
-msgid "Use system default"
-msgstr "Użycie domyślnego systemu"
-
-msgid "12 hour time format"
-msgstr "12 godzinny format czasu"
-
-msgid "24 hour time format"
-msgstr "24 godzinny format czasu"
-
-msgid "Show dates in..."
-msgstr "Wyświetlanie dat w..."
-
-msgid "Co_nversations:"
-msgstr "_Rozmowach:"
-
-msgid "For delayed messages"
-msgstr "Dla opóźnionych wiadomości"
-
-msgid "For delayed messages and in chats"
-msgstr "Dla opóźnionych wiadomości i w konferencjach"
-
-msgid "_Message Logs:"
-msgstr "Dzienniki wiado_mości:"
-
-#. *< type
-#. *< ui_requirement
-#. *< flags
-#. *< dependencies
-#. *< priority
-#. *< id
-msgid "Message Timestamp Formats"
-msgstr "Formaty daty wiadomości"
-
-#. *< name
-#. *< version
-#. * summary
-msgid "Customizes the message timestamp formats."
-msgstr "Dostosowuje formaty daty wiadomości."
-
-#. * description
-msgid ""
-"This plugin allows the user to customize conversation and logging message "
-"timestamp formats."
-msgstr ""
-"Ta wtyczka umożliwia użytkownikowi dostosowanie formatów daty wiadomości i "
-"dziennika."
-
#. Alerts
+#: ../pidgin/plugins/unity.c:441
msgid "Chatroom alerts"
msgstr "Powiadomienia w konferencjach"
+#: ../pidgin/plugins/unity.c:445
msgid "Chatroom message alerts _only where someone says your username"
-msgstr ""
-"P_owiadamianie o wiadomościach w konferencjach tylko, kiedy ktoś wymawia "
-"nazwę użytkownika"
+msgstr "P_owiadamianie o wiadomościach w konferencjach tylko, kiedy ktoś wymawia nazwę użytkownika"
#. Launcher integration
+#: ../pidgin/plugins/unity.c:454
msgid "Launcher Icon"
msgstr "Ikona aktywatora"
+#: ../pidgin/plugins/unity.c:458
msgid "_Disable launcher integration"
msgstr "_Wyłączenie integracji z aktywatorem"
+#: ../pidgin/plugins/unity.c:466
msgid "Show number of unread _messages on launcher icon"
msgstr "Wyświetlanie liczby _nieprzeczytanych wiadomości na ikonie aktywatora"
+#: ../pidgin/plugins/unity.c:474
msgid "Show number of unread co_nversations on launcher icon"
msgstr "Wyświetlanie liczby _nieprzeczytanych rozmów na ikonie aktywatora"
#. Messaging menu integration
+#: ../pidgin/plugins/unity.c:483
msgid "Messaging Menu"
msgstr "Menu komunikatora"
+#: ../pidgin/plugins/unity.c:488
msgid "Show number of _unread messages for conversations in messaging menu"
msgstr "Wyświetlanie _liczby nieprzeczytanych wiadomości w menu komunikatora"
+#: ../pidgin/plugins/unity.c:496
msgid "Show _elapsed time for unread conversations in messaging menu"
-msgstr ""
-"Wyświetlanie _czasu trwania nieprzeczytanych rozmów w menu komunikatora"
+msgstr "Wyświetlanie _czasu trwania nieprzeczytanych rozmów w menu komunikatora"
#. *< type
#. *< ui_requirement
@@ -14785,60 +18544,22 @@ msgstr ""
#. *< dependencies
#. *< priority
#. *< id
+#: ../pidgin/plugins/unity.c:602
msgid "Unity Integration"
msgstr "Integracja ze środowiskiem Unity"
#. *< name
#. *< version
#. * summary
+#: ../pidgin/plugins/unity.c:605
msgid "Provides integration with Unity."
msgstr "Dostarcza integrację ze środowiskiem Unity."
#. * description
+#: ../pidgin/plugins/unity.c:607
msgid "Provides integration with Unity's messaging menu and launcher."
-msgstr ""
-"Dostarcza integrację z menu komunikatora i aktywatorem środowiska Unity."
-
-msgid "Audio"
-msgstr "Dźwięk"
-
-msgid "Video"
-msgstr "Obraz"
-
-msgid "Output"
-msgstr "Wyjście"
-
-msgid "_Plugin"
-msgstr "_Wtyczka"
-
-msgid "_Device"
-msgstr "Urzą_dzenie"
-
-msgid "Input"
-msgstr "Wejście"
-
-msgid "P_lugin"
-msgstr "W_tyczka"
-
-msgid "D_evice"
-msgstr "Urządz_enie"
-
-msgid "DROP"
-msgstr "Upuszczenie"
-
-msgid "Volume:"
-msgstr "Głośność:"
-
-msgid "Silence threshold:"
-msgstr "Próg ciszy:"
+msgstr "Dostarcza integrację z menu komunikatora i aktywatorem środowiska Unity."
-msgid "Input and Output Settings"
-msgstr "Ustawienia wejścia i wyjścia"
-
-msgid "Microphone Test"
-msgstr "Test mikrofonu"
-
-#. *< magic
#. *< major version
#. *< minor version
#. *< type
@@ -14847,46 +18568,61 @@ msgstr "Test mikrofonu"
#. *< dependencies
#. *< priority
#. *< id
-msgid "Voice/Video Settings"
-msgstr "Ustawienia dźwięku/wideo"
+#: ../pidgin/plugins/webkit.c:57
+msgid "WebKit Development"
+msgstr "Programowanie w WebKit"
#. *< name
#. *< version
-msgid "Configure your microphone and webcam."
-msgstr "Konfiguruje mikrofon i kamerę internetową."
+#: ../pidgin/plugins/webkit.c:59
+msgid "Enables WebKit Inspector."
+msgstr "Włącza Inspektora WebKit."
#. *< summary
-msgid "Configure microphone and webcam settings for voice/video calls."
-msgstr ""
-"Konfiguruje ustawienia mikrofonu i kamery internetowej dla rozmów głosowych/"
-"wideo."
+#: ../pidgin/plugins/webkit.c:60
+msgid ""
+"Enables WebKit's built-in inspector. This may be viewed by right-clicking a "
+"WebKit widget and selecting 'Inspect Element'."
+msgstr "Włącza wbudowanego Inspektora WebKit. Może zostać wyświetlony przez naciśnięcie prawym przyciskiem myszy i wybranie \"Zbadaj element\"."
+#: ../pidgin/plugins/win32/transparency/win2ktrans.c:172
+#: ../pidgin/plugins/win32/transparency/win2ktrans.c:587
+#: ../pidgin/plugins/win32/transparency/win2ktrans.c:634
msgid "Opacity:"
msgstr "Nieprzezroczystość:"
#. IM Convo trans options
+#: ../pidgin/plugins/win32/transparency/win2ktrans.c:551
msgid "IM Conversation Windows"
msgstr "Okna rozmów"
+#: ../pidgin/plugins/win32/transparency/win2ktrans.c:552
msgid "_IM window transparency"
msgstr "Przezroczystość okien w_iadomości"
+#: ../pidgin/plugins/win32/transparency/win2ktrans.c:566
msgid "_Show slider bar in IM window"
msgstr "Wyświetlanie pa_ska przezroczystości w oknie wiadomości"
+#: ../pidgin/plugins/win32/transparency/win2ktrans.c:573
msgid "Remove IM window transparency on focus"
msgstr "Usuń przezroczystość okna wiadomości po aktywacji"
+#: ../pidgin/plugins/win32/transparency/win2ktrans.c:576
+#: ../pidgin/plugins/win32/transparency/win2ktrans.c:624
msgid "Always on top"
msgstr "Zawsze na wierzchu"
#. Buddy List trans options
+#: ../pidgin/plugins/win32/transparency/win2ktrans.c:608
msgid "Buddy List Window"
msgstr "Okno listy znajomych"
+#: ../pidgin/plugins/win32/transparency/win2ktrans.c:609
msgid "_Buddy List window transparency"
msgstr "_Przezroczystość okna listy znajomych"
+#: ../pidgin/plugins/win32/transparency/win2ktrans.c:622
msgid "Remove Buddy List window transparency on focus"
msgstr "Usuń przezroczystość okna listy znajomych po aktywacji"
@@ -14896,63 +18632,69 @@ msgstr "Usuń przezroczystość okna listy znajomych po aktywacji"
#. *< dependencies
#. *< priority
#. *< id
+#: ../pidgin/plugins/win32/transparency/win2ktrans.c:681
msgid "Transparency"
msgstr "Przezroczystość"
#. *< name
#. *< version
#. * summary
+#: ../pidgin/plugins/win32/transparency/win2ktrans.c:684
msgid "Variable Transparency for the buddy list and conversations."
msgstr "Zmienna przezroczystość listy znajomych i rozmów."
#. * description
+#: ../pidgin/plugins/win32/transparency/win2ktrans.c:686
msgid ""
-"This plugin enables variable alpha transparency on conversation windows and "
-"the buddy list.\n"
+"This plugin enables variable alpha transparency on conversation windows and the buddy list.\n"
"\n"
"* Note: This plugin requires Win2000 or greater."
-msgstr ""
-"Wtyczka pozwalająca na regulację przezroczystością okien rozmów i listy "
-"znajomych.\n"
-"\n"
-"* Uwaga: ta wtyczka wymaga systemu Windows 2000 lub nowszego."
+msgstr "Wtyczka pozwalająca na regulację przezroczystością okien rozmów i listy znajomych.\n\n* Uwaga: ta wtyczka wymaga systemu Windows 2000 lub nowszego."
#. Autostart
+#: ../pidgin/plugins/win32/winprefs/winprefs.c:277
msgid "Startup"
msgstr "Uruchomienie"
+#: ../pidgin/plugins/win32/winprefs/winprefs.c:278
#, c-format
msgid "_Start %s on Windows startup"
msgstr "Uruchomienie programu %s podczas _startu systemu Windows"
+#: ../pidgin/plugins/win32/winprefs/winprefs.c:290
msgid "Allow multiple instances"
msgstr "Zezwolenie na wiele instancji"
+#: ../pidgin/plugins/win32/winprefs/winprefs.c:301
msgid "_Dockable Buddy List"
msgstr "_Zakotwiczenie listy znajomych"
#. Blist On Top
+#: ../pidgin/plugins/win32/winprefs/winprefs.c:305
msgid "_Keep Buddy List window on top:"
msgstr "O_kno listy znajomych zawsze na wierzchu:"
#. XXX: Did this ever work?
+#: ../pidgin/plugins/win32/winprefs/winprefs.c:310
msgid "Only when docked"
msgstr "Tylko podczas zadokowania"
+#: ../pidgin/plugins/win32/winprefs/winprefs.c:339
msgid "Windows Pidgin Options"
msgstr "Opcje programu Pidgin dla systemu Windows"
+#: ../pidgin/plugins/win32/winprefs/winprefs.c:341
msgid "Options specific to Pidgin for Windows."
msgstr "Opcje specyficzne dla programu Pidgin dla Windows."
+#: ../pidgin/plugins/win32/winprefs/winprefs.c:342
msgid ""
"Provides options specific to Pidgin for Windows, such as buddy list docking."
-msgstr ""
-"Dostarcza opcje specyficzne dla programu Pidgin dla Windows, takie jak "
-"dokowanie listy znajomych."
+msgstr "Dostarcza opcje specyficzne dla programu Pidgin dla Windows, takie jak dokowanie listy znajomych."
-msgid "<font color='#777777'>Logged out.</font>"
-msgstr "<font color='#777777'>Wylogowano.</font>"
+#: ../pidgin/plugins/xmppconsole.c:723
+msgid "Logged out."
+msgstr "Wylogowano."
#. *< type
#. *< ui_requirement
@@ -14960,170 +18702,179 @@ msgstr "<font color='#777777'>Wylogowano.</font>"
#. *< dependencies
#. *< priority
#. *< id
+#: ../pidgin/plugins/xmppconsole.c:810 ../pidgin/plugins/xmppconsole.c:887
+#: ../pidgin/plugins/xmppconsole.c:906
msgid "XMPP Console"
msgstr "Konsola XMPP"
+#: ../pidgin/plugins/xmppconsole.c:817
msgid "Account: "
msgstr "Konto: "
-msgid "<font color='#777777'>Not connected to XMPP</font>"
-msgstr "<font color='#777777'>Nie połączono z XMPP</font>"
+#: ../pidgin/plugins/xmppconsole.c:840
+msgid "Not connected to XMPP"
+msgstr "Nie połączono z XMPP"
#. *< name
#. *< version
#. * summary
+#: ../pidgin/plugins/xmppconsole.c:909
msgid "Send and receive raw XMPP stanzas."
msgstr "Wysyłanie i odbieranie surowych strof XMPP."
#. * description
+#: ../pidgin/plugins/xmppconsole.c:911
msgid "This plugin is useful for debugging XMPP servers or clients."
msgstr "Ta wtyczka jest przydatna do debugowania serwerów lub klientów XMPP."
-msgid "The installer is already running."
-msgstr "Instalator jest już uruchomiony."
+#. $(^Name) is the current Version name (e.g. Pidgin 2.7.0). $_CLICK will
+#. become a translated version of "Click Next to continue." DO NOT translate
+#. the CLICK in $_CLICK. It will break the installer.
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:2
+msgid ""
+"$(^Name) is released under the GNU General Public License (GPL). The license"
+" is provided here for information purposes only. $_CLICK"
+msgstr "Program $(^Name) jest rozpowszechniany na warunkach Powszechnej Licencji Publicznej GNU (GPL). Licencja jest tu podana wyłącznie w celach informacyjnych. $_CLICK"
+#. Installer Subsection Detailed Description
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:4
+msgid "A multi-platform GUI toolkit, used by Pidgin"
+msgstr "Wieloplatformowy zestaw narzędzi do tworzenia graficznych interfejsów użytkownika, używany przez program Pidgin"
+
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:5
msgid ""
"An instance of Pidgin is currently running. Please exit Pidgin and try "
"again."
-msgstr ""
-"Program Pidgin jest obecnie uruchomiony. Proszę zakończyć program Pidgin i "
-"spróbować ponownie."
-
-#. "Next >" appears on a button on the License Page of the Installer
-msgid "Next >"
-msgstr "Dalej >"
+msgstr "Program Pidgin jest obecnie uruchomiony. Proszę zakończyć program Pidgin i spróbować ponownie."
-#. $(^Name) is the current Version name (e.g. Pidgin 2.7.0). $_CLICK will become a translated version of "Click Next to continue." DO NOT translate the CLICK in $_CLICK. It will break the installer.
-msgid ""
-"$(^Name) is released under the GNU General Public License (GPL). The license "
-"is provided here for information purposes only. $_CLICK"
-msgstr ""
-"Program $(^Name) jest rozpowszechniany na warunkach Powszechnej Licencji "
-"Publicznej GNU (GPL). Licencja jest tu podana wyłącznie w celach "
-"informacyjnych. $_CLICK"
+#. Installer Subsection Detailed Description
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:7
+msgid "Core Pidgin files and dlls"
+msgstr "Główne pliki programu Pidgin i biblioteki DLL"
-#. Installer Subsection Text
-msgid "Pidgin Instant Messaging Client (required)"
-msgstr "Klient komunikatora Pidgin (wymagane)"
+#. Installer Subsection Detailed Description
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:9
+msgid "Create a Start Menu entry for Pidgin"
+msgstr "Utworzenie wpisu w menu Start dla programu Pidgin"
-#. Installer Subsection Text
-msgid "GTK+ Runtime (required if not present)"
-msgstr "Biblioteka GTK+ (wymagana, jeśli nie jest obecna)"
+#. Installer Subsection Detailed Description
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:11
+msgid "Create a shortcut to Pidgin on the Desktop"
+msgstr "Utworzenie skrótu do programu Pidgin na pulpicie"
#. Installer Subsection Text
-msgid "Shortcuts"
-msgstr "Skróty"
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:13
+msgid "Debug Symbols (for reporting crashes)"
+msgstr "Symbole debugowania (do zgłaszania awarii)"
#. Installer Subsection Text
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:15
msgid "Desktop"
msgstr "Pulpit"
+#. $R2 will display the URL that the GTK+ Runtime failed to download from
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:17
+msgid ""
+"Error Downloading the GTK+ Runtime ($R2).$\\rThis is required for Pidgin to "
+"function; if retrying fails, you may need to use the 'Offline Installer' "
+"from https://pidgin.im/download/windows/ ."
+msgstr "Błąd podczas pobierania biblioteki wykonawczej GTK+ ($R2).$\\rJest ona wymagana do działania programu Pidgin. Jeśli ponowienie się nie powiedzie, to należy użyć \"Instalatora w trybie offline\" z witryny https://pidgin.im/download/windows/ ."
+
+#. $R2 will display the URL that the Debug Symbols failed to download from
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:19
+msgid ""
+"Error Installing Debug Symbols ($R2).$\\rIf retrying fails, you may need to "
+"use the 'Offline Installer' from https://pidgin.im/download/windows/ ."
+msgstr "Błąd podczas instalowania symboli debugowania ($R2).$\\rJeśli ponowienie się nie powiedzie, to należy użyć \"Instalatora w trybie offline\" z witryny https://pidgin.im/download/windows/ ."
+
+#. $R3 will display the URL that the Dictionary failed to download from
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:22
+#, no-c-format
+msgid ""
+"Error Installing Spellchecking ($R3).$\\rIf retrying fails, manual "
+"installation instructions are at: "
+"https://developer.pidgin.im/wiki/Installing%20Pidgin#manual_win32_spellcheck_installation"
+msgstr "Błąd podczas instalowania sprawdzania pisowni ($R3).$\\rJeśli ponowienie się nie powiedzie, instrukcje ręcznej instalacji znajdują się na stronie: https://developer.pidgin.im/wiki/Installing%20Pidgin#manual_win32_spellcheck_installation"
+
#. Installer Subsection Text
-msgid "Start Menu"
-msgstr "Menu Start"
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:24
+msgid "GTK+ Runtime (required if not present)"
+msgstr "Biblioteka GTK+ (wymagana, jeśli nie jest obecna)"
#. Installer Subsection Text
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:26
msgid "Localizations"
msgstr "Tłumaczenia"
-#. Installer Subsection Detailed Description
-msgid "Core Pidgin files and dlls"
-msgstr "Główne pliki programu Pidgin i biblioteki DLL"
-
-#. Installer Subsection Detailed Description
-msgid "Shortcuts for starting Pidgin"
-msgstr "Skróty do uruchamiania programu Pidgin"
-
-#. Installer Subsection Detailed Description
-msgid "Create a shortcut to Pidgin on the Desktop"
-msgstr "Utworzenie skrótu do programu Pidgin na pulpicie"
-
-#. Installer Subsection Detailed Description
-msgid "Create a Start Menu entry for Pidgin"
-msgstr "Utworzenie wpisu w menu Start dla programu Pidgin"
-
-#. Installer Subsection Detailed Description
-msgid "A multi-platform GUI toolkit, used by Pidgin"
-msgstr ""
-"Wieloplatformowy zestaw narzędzi do tworzenia graficznych interfejsów "
-"użytkownika, używany przez program Pidgin"
+#. "Next >" appears on a button on the License Page of the Installer
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:28
+msgid "Next >"
+msgstr "Dalej >"
#. Installer Subsection Text
-msgid "Debug Symbols (for reporting crashes)"
-msgstr "Symbole debugowania (do zgłaszania awarii)"
-
-#. Text displayed on Installer Finish Page
-msgid "Visit the Pidgin Web Page"
-msgstr "Odwiedź stronę WWW programu Pidgin"
-
-msgid ""
-"Unable to uninstall the currently installed version of Pidgin. The new "
-"version will be installed without removing the currently installed version."
-msgstr ""
-"Nie można odinstalować obecnie zainstalowanej wersji programu Pidgin. Nowa "
-"wersja zostanie zainstalowana bez usuwania obecnej."
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:30
+msgid "Pidgin Instant Messaging Client (required)"
+msgstr "Klient komunikatora Pidgin (wymagane)"
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:31
msgid ""
"Pidgin requires a compatible GTK+ Runtime (which doesn't appear to be "
"already present).$\\rAre you sure you want to skip installing the GTK+ "
"Runtime?"
-msgstr ""
-"Program Pidgin wymaga zgodnej biblioteki GTK+ (która nie jest jeszcze "
-"obecna).$\\rNa pewno pominąć instalowanie biblioteki GTK+?"
+msgstr "Program Pidgin wymaga zgodnej biblioteki GTK+ (która nie jest jeszcze obecna).$\\rNa pewno pominąć instalowanie biblioteki GTK+?"
#. Installer Subsection Text
-msgid "URI Handlers"
-msgstr "Obsługa adresów URI"
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:33
+msgid "Shortcuts"
+msgstr "Skróty"
+
+#. Installer Subsection Detailed Description
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:35
+msgid "Shortcuts for starting Pidgin"
+msgstr "Skróty do uruchamiania programu Pidgin"
#. Installer Subsection Text
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:37
msgid "Spellchecking Support"
msgstr "Obsługa sprawdzania pisowni"
-#. $R3 will display the URL that the Dictionary failed to download from
-#, no-c-format
-msgid ""
-"Error Installing Spellchecking ($R3).$\\rIf retrying fails, manual "
-"installation instructions are at: http://developer.pidgin.im/wiki/Installing"
-"%20Pidgin#manual_win32_spellcheck_installation"
-msgstr ""
-"Błąd podczas instalowania sprawdzania pisowni ($R3).$\\rJeśli ponowienie się "
-"nie powiedzie, instrukcje ręcznej instalacji znajdują się na stronie: http://"
-"developer.pidgin.im/wiki/Installing"
-"%20Pidgin#manual_win32_spellcheck_installation"
+#. Installer Subsection Text
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:39
+msgid "Start Menu"
+msgstr "Menu Start"
#. Installer Subsection Detailed Description
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:41
msgid ""
"Support for Spellchecking. (Internet connection required for installation)"
-msgstr ""
-"Obsługa sprawdzania pisowni (do instalacji wymagane jest połączenie z "
-"Internetem)."
+msgstr "Obsługa sprawdzania pisowni (do instalacji wymagane jest połączenie z Internetem)."
-#. $R2 will display the URL that the Debug Symbols failed to download from
-msgid ""
-"Error Installing Debug Symbols ($R2).$\\rIf retrying fails, you may need to "
-"use the 'Offline Installer' from http://pidgin.im/download/windows/ ."
-msgstr ""
-"Błąd podczas pobierania symboli debugowania ($R2).$\\rJeśli ponowienie się "
-"nie powiedzie, być może należy użyć 'Instalatora w trybie offline' ze strony "
-"http://pidgin.im/download/windows/ ."
-
-#. $R2 will display the URL that the GTK+ Runtime failed to download from
-msgid ""
-"Error Downloading the GTK+ Runtime ($R2).$\\rThis is required for Pidgin to "
-"function; if retrying fails, you may need to use the 'Offline Installer' "
-"from http://pidgin.im/download/windows/ ."
-msgstr ""
-"Błąd podczas pobierania biblioteki GTK+ ($R2).$\\rJest ona wymagana do "
-"działania programu Pidgin. Jeśli ponowienie się nie powiedzie, być może "
-"należy użyć 'Instalatora w trybie offline' ze strony http://pidgin.im/"
-"download/windows/ ."
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:42
+msgid "The installer is already running."
+msgstr "Instalator jest już uruchomiony."
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:43
msgid ""
"The uninstaller could not find registry entries for Pidgin.$\\rIt is likely "
"that another user installed this application."
-msgstr ""
-"Instalator nie może odnaleźć wpisów w rejestrze dla programu Pidgin.$"
-"\\rMożliwe, że inny użytkownik zainstalował ten program."
+msgstr "Instalator nie może odnaleźć wpisów w rejestrze dla programu Pidgin.$\\rMożliwe, że inny użytkownik zainstalował ten program."
+
+#. Installer Subsection Text
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:45
+msgid "URI Handlers"
+msgstr "Obsługa adresów URI"
+
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:46
+msgid ""
+"Unable to uninstall the currently installed version of Pidgin. The new "
+"version will be installed without removing the currently installed version."
+msgstr "Nie można odinstalować obecnie zainstalowanej wersji programu Pidgin. Nowa wersja zostanie zainstalowana bez usuwania obecnej."
+
+#. Text displayed on Installer Finish Page
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:48
+msgid "Visit the Pidgin Web Page"
+msgstr "Odwiedź stronę WWW programu Pidgin"
+#: ../pidgin/win32/nsis/nsis_translations.desktop.in.h:49
msgid "You do not have permission to uninstall this application."
msgstr "Brak uprawnień do odinstalowania tego programu."
diff --git a/valgrind-suppressions b/valgrind-suppressions
index 2c1073aaec..7eba582a4e 100644
--- a/valgrind-suppressions
+++ b/valgrind-suppressions
@@ -151,4 +151,93 @@
fun:purple_plugins_load_saved
fun:main
}
-
+{
+ libfontconfig leaks
+ Memcheck:Leak
+ fun:*alloc
+ obj:/usr/lib/libfontconfig.so.*
+ ...
+}
+{
+ 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
+ ...
+}
+{
+ wcslen_sse2 optimization 2
+ Memcheck:Cond
+ fun:__wcslen_sse2
+ ...
+}
+{
+ idna bug
+ Memcheck:Addr4
+ fun:idna_to_ascii_4z
+ fun:idna_to_ascii_8z
+ ...
+}
+{
+ libcairo uninitialized values
+ Memcheck:Cond
+ obj:*libpixman-1.so.*
+ obj:*libpixman-1.so.*
+ obj:*libpixman-1.so.*
+ obj:*libpixman-1.so.*
+ fun:pixman_image_composite32
+ obj:*libcairo.so.2.*
+ obj:*libcairo.so.2.*
+ obj:*libcairo.so.2.*
+ obj:*libcairo.so.2.*
+ obj:*
+}
+{
+ librsvg uninitialized values
+ Memcheck:Cond
+ obj:*librsvg-2.so.2.*
+ fun:rsvg_handle_get_pixbuf_sub
+ fun:rsvg_handle_get_pixbuf
+ obj:*
+}
+{
+ TODO temporary: gg prpl bug, fixit
+ Memcheck:Addr4
+ fun:memcpy
+ fun:ggp_resolver_purple_cb
+ fun:purple_dnsquery_resolved
+ ...
+}